[
  {
    "path": ".editorconfig",
    "content": "[*.{cs,vb}]\n\n# IDE0032: Use auto property\ndotnet_style_prefer_auto_properties = false:silent\n\n# CA1416: Validate platform compatibility\ndotnet_diagnostic.CA1416.severity = none\n\n# CA1806: Do not ignore method results\ndotnet_diagnostic.CA1806.severity = silent\n\n# CA2014: Do not use stackalloc in loops\ndotnet_diagnostic.CA2014.severity = warning\n\n# CS1573: Parameter has no matching param tag in the XML comment (but other parameters do)\ndotnet_diagnostic.CS1573.severity = suggestion\n\n[*]\ncharset = utf-8\n\n[*.iss]\ncharset = utf-8-bom\n"
  },
  {
    "path": ".gitattributes",
    "content": "###############################################################################\n# Set default behavior to automatically normalize line endings.\n###############################################################################\n* text=auto\n###############################################################################\n# Set the merge driver for project and solution files\n#\n# Merging from the command prompt will add diff markers to the files if there\n# are conflicts (Merging from VS is not affected by the settings below, in VS\n# the diff markers are never inserted). Diff markers may cause the following \n# file extensions to fail to load in VS. An alternative would be to treat\n# these files as binary and thus will always conflict and require user\n# intervention with every merge. To do so, just uncomment the entries below\n###############################################################################\n#*.sln       merge=binary\n#*.csproj    merge=binary\n#*.vbproj    merge=binary\n#*.vcxproj   merge=binary\n#*.vcproj    merge=binary\n#*.dbproj    merge=binary\n#*.fsproj    merge=binary\n#*.lsproj    merge=binary\n#*.wixproj   merge=binary\n#*.modelproj merge=binary\n#*.sqlproj   merge=binary\n#*.wwaproj   merge=binary\n###############################################################################\n# behavior for image files\n#\n# image files are treated as binary by default.\n###############################################################################\n#*.jpg   binary\n#*.png   binary\n#*.gif   binary\n###############################################################################\n/Libraries/** linguist-vendored\n###############################################################################\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "github: qgindi\n"
  },
  {
    "path": ".gitignore",
    "content": "## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n\n# User-specific files\n*.suo\n*.user\n*.userosscache\n*.sln.docstates\n\n# User-specific files (MonoDevelop/Xamarin Studio)\n*.userprefs\n\n# Build results\n[Dd]ebug/\n[Dd]ebugPublic/\n[Rr]elease/\n[Rr]eleases/\nbuild/\nbld/\n[Bb]in/\n[Oo]bj/\n\n# Visual Studio 2015 cache/options directory\n.vs/\n\n# MSTest test Results\n[Tt]est[Rr]esult*/\n[Bb]uild[Ll]og.*\n\n# NUNIT\n*.VisualState.xml\nTestResult.xml\n\n# Build Results of an ATL Project\n[Dd]ebugPS/\n[Rr]eleasePS/\ndlldata.c\n\n# DNX\nproject.lock.json\nartifacts/\n\n*_i.c\n*_p.c\n*_i.h\n*.ilk\n*.meta\n*.obj\n*.pch\n*.pdb\n*.pgc\n*.pgd\n*.rsp\n*.sbr\n*.tlb\n*.tli\n*.tlh\n*.tmp\n*.tmp_proj\n*.log\n*.vspscc\n*.vssscc\n.builds\n*.pidb\n*.svclog\n*.scc\n\n# Chutzpah Test files\n_Chutzpah*\n\n# Visual C++ cache files\nipch/\n*.aps\n*.ncb\n*.opensdf\n*.sdf\n*.cachefile\n\n# Visual Studio profiler\n*.psess\n*.vsp\n*.vspx\n\n# TFS 2012 Local Workspace\n$tf/\n\n# Guidance Automation Toolkit\n*.gpState\n\n# ReSharper is a .NET coding add-in\n_ReSharper*/\n*.[Rr]e[Ss]harper\n*.DotSettings.user\n\n# JustCode is a .NET coding add-in\n.JustCode\n\n# TeamCity is a build add-in\n_TeamCity*\n\n# DotCover is a Code Coverage Tool\n*.dotCover\n\n# NCrunch\n_NCrunch_*\n.*crunch*.local.xml\n\n# MightyMoose\n*.mm.*\nAutoTest.Net/\n\n# Web workbench (sass)\n.sass-cache/\n\n# Installshield output folder\n[Ee]xpress/\n\n# DocProject is a documentation generator add-in\nDocProject/buildhelp/\nDocProject/Help/*.HxT\nDocProject/Help/*.HxC\nDocProject/Help/*.hhc\nDocProject/Help/*.hhk\nDocProject/Help/*.hhp\nDocProject/Help/Html2\nDocProject/Help/html\n\n# Click-Once directory\npublish/\n\n# Publish Web Output\n*.[Pp]ublish.xml\n*.azurePubxml\n## _TODO: Comment the next line if you want to checkin your\n## web deploy settings but do note that will include unencrypted\n## passwords\n#*.pubxml\n\n*.publishproj\n\n# NuGet Packages\n*.nupkg\n# The packages folder can be ignored because of Package Restore\n**/packages/*\n# except build/, which is used as an MSBuild target.\n!**/packages/build/\n# Uncomment if necessary however generally it will be regenerated when needed\n#!**/packages/repositories.config\n\n# Windows Azure Build Output\ncsx/\n*.build.csdef\n\n# Windows Store app package directory\nAppPackages/\n\n# Visual Studio cache files\n# files ending in .cache can be ignored\n*.[Cc]ache\n# but keep track of directories ending in .cache\n!*.[Cc]ache/\n\n# Others\nClientBin/\n[Ss]tyle[Cc]op.*\n~$*\n*~\n*.dbmdl\n*.dbproj.schemaview\n*.pfx\n*.snk\n*.publishsettings\nnode_modules/\norleans.codegen.cs\n\n# RIA/Silverlight projects\nGenerated_Code/\n\n# Backup & report files from converting an old project file\n# to a newer Visual Studio version. Backup files are not needed,\n# because we have git ;-)\n_UpgradeReport_Files/\nBackup*/\nUpgradeLog*.XML\nUpgradeLog*.htm\n\n# SQL Server files\n*.mdf\n*.ldf\n\n# Business Intelligence projects\n*.rdl.data\n*.bim.layout\n*.bim_*.settings\n\n# Microsoft Fakes\nFakesAssemblies/\n\n# Node.js Tools for Visual Studio\n.ntvs_analysis.dat\n\n# Visual Studio 6 build log\n*.plg\n\n# Visual Studio 6 workspace options file\n*.opt\n\n# LightSwitch generated files\nGeneratedArtifacts/\n_Pvt_Extensions/\nModelManifest.xml\n\n# Au\n/Other/Api/\n\n/Libraries/PCRE/excluded/\n\n/_/*\n!/_/Default/\n!/_/Templates/\n!/_/*.manifest\n!/_/dotnet_ref_*.txt\n!/_/gitBinaryRestore.csv\n\n!/_/32/\n/_/32/*\n!/_/32/7za.exe\n\n/_/Default/*\n!/_/Default/Workspace/\n\n/_/Default/Workspace/*\n!/_/Default/Workspace/files/\n!/_/Default/Workspace/files.xml\n!/_/Default/Workspace/state.db\n\n/_/Templates/*\n!/_/Templates/files/\n!/_/Templates/files.xml\n\n/Cookbook/*\n!/Cookbook/files/\n!/Cookbook/files.xml\n\n*.bak\n/Au/global.json\n.tools/\n/Other/DocFX/_doc/cookbook\n/Au.Editor/Panels/PanelRead-WB.cs\n"
  },
  {
    "path": "Au/Api/Api.cs",
    "content": "//[assembly: DefaultDllImportSearchPaths(DllImportSearchPath.System32|DllImportSearchPath.UserDirectories)]\n\n#pragma warning disable 649, 169 //field never assigned/used\n\nnamespace Au.Types;\n\n//[DebuggerStepThrough]\nstatic unsafe partial class Api {\n\t#region util\n\t\n\t/// <summary>\n\t/// Gets the native size of a struct variable.\n\t/// Returns <c>Marshal.SizeOf(typeof(T))</c>.\n\t/// Speed: the same (in Release config) as <c>Marshal.SizeOf(typeof(T))</c>, and 2 times faster than <c>Marshal.SizeOf(v)</c>.\n\t/// </summary>\n\tinternal static int SizeOf<T>(T v) => Marshal.SizeOf<T>();\n\t\n\t/// <summary>\n\t/// Gets the native size of a type.\n\t/// Returns <c>Marshal.SizeOf(typeof(T))</c>.\n\t/// </summary>\n\tinternal static int SizeOf<T>() => Marshal.SizeOf<T>();\n\t\n\t/// <summary>\n\t/// Gets dll module handle (<c>GetModuleHandle</c>) or loads dll (<c>NativeLibrary.TryLoad</c>), and returns unmanaged exported function address (<c>GetProcAddress</c>).\n\t/// See also: <c>GetDelegate</c>.\n\t/// </summary>\n\tinternal static IntPtr GetProcAddress(string dllName, string funcName) {\n\t\tIntPtr hmod = GetModuleHandle(dllName);\n\t\tif (hmod == default && !NativeLibrary.TryLoad(dllName, out hmod)) return default;\n\t\t\n\t\treturn GetProcAddress(hmod, funcName);\n\t}\n\t\n\t/// <summary>\n\t/// Calls <see cref=\"GetProcAddress(string, string)\"/> (loads dll or gets handle) and <see cref=\"Marshal.GetDelegateForFunctionPointer{TDelegate}(IntPtr)\"/>.\n\t/// </summary>\n\tinternal static bool GetDelegate<T>(out T deleg, string dllName, string funcName) where T : class {\n\t\tIntPtr fa = GetProcAddress(dllName, funcName); if (fa == default) { deleg = null; return false; }\n\t\tdeleg = Marshal.GetDelegateForFunctionPointer<T>(fa);\n\t\treturn deleg != null;\n\t}\n\t\n\t/// <summary>\n\t/// Calls API <see cref=\"GetProcAddress(IntPtr, string)\"/> and <see cref=\"Marshal.GetDelegateForFunctionPointer{TDelegate}(IntPtr)\"/>.\n\t/// </summary>\n\tinternal static bool GetDelegate<T>(out T deleg, IntPtr hModule, string funcName) where T : class {\n\t\tdeleg = null;\n\t\tIntPtr fa = GetProcAddress(hModule, funcName); if (fa == default) return false;\n\t\tdeleg = Marshal.GetDelegateForFunctionPointer<T>(fa);\n\t\treturn deleg != null;\n\t}\n\t\n\t/// <summary>\n\t/// If <i>o</i> is not <c>null</c>, calls <see cref=\"Marshal.ReleaseComObject\"/>.\n\t/// </summary>\n\tinternal static void ReleaseComObject<T>(T o) where T : class {\n\t\tif (o != null) Marshal.ReleaseComObject(o);\n\t}\n\t\n\t#endregion\n\t\n\t#region gdi32\n\t\n\t[DllImport(\"gdi32.dll\")] //this and many other GDI functions don't use SetLastError\n\tinternal static extern bool DeleteObject(IntPtr ho);\n\t\n\t[DllImport(\"gdi32.dll\")]\n\tinternal static extern IntPtr CreateRectRgn(int x1, int y1, int x2, int y2);\n\t\n\tinternal const int RGN_AND = 1;\n\tinternal const int RGN_OR = 2;\n\tinternal const int RGN_XOR = 3;\n\tinternal const int RGN_DIFF = 4;\n\tinternal const int RGN_COPY = 5;\n\t\n\t[DllImport(\"gdi32.dll\")]\n\tinternal static extern int CombineRgn(IntPtr hrgnDst, IntPtr hrgnSrc1, IntPtr hrgnSrc2, int iMode);\n\t\n\t[DllImport(\"gdi32.dll\")]\n\tinternal static extern bool SetRectRgn(IntPtr hrgn, int left, int top, int right, int bottom);\n\t\n\t[DllImport(\"gdi32.dll\")]\n\tinternal static extern IntPtr CreateRectRgnIndirect(in RECT lprect);\n\t\n\t[DllImport(\"gdi32.dll\")]\n\tinternal static extern bool PtInRegion(IntPtr hrgn, int x, int y);\n\t\n\t[DllImport(\"gdi32.dll\")]\n\tinternal static extern IntPtr CreateCompatibleDC(IntPtr hdc);\n\t\n\t[DllImport(\"gdi32.dll\")]\n\tinternal static extern bool DeleteDC(IntPtr hdc);\n\t\n\t[DllImport(\"gdi32.dll\")]\n\tinternal static extern IntPtr SelectObject(IntPtr hdc, IntPtr h);\n\t\n\t[DllImport(\"gdi32.dll\", EntryPoint = \"GetObjectW\")]\n\tinternal static extern int GetObject(IntPtr h, int c, void* pv);\n\t\n\t/// <param name=\"mode\">1 transparent, 2 opaque.</param>\n\t[DllImport(\"gdi32.dll\")]\n\tinternal static extern int SetBkMode(IntPtr hdc, int mode);\n\t\n\t[DllImport(\"gdi32.dll\")]\n\tinternal static extern int SetBkColor(IntPtr hdc, int color);\n\t\n\t[DllImport(\"gdi32.dll\", EntryPoint = \"TextOutW\")]\n\tinternal static extern bool TextOut(IntPtr hdc, int x, int y, string lpString, int c);\n\t\n\t[DllImport(\"gdi32.dll\", EntryPoint = \"TextOutW\")]\n\tinternal static extern bool TextOut(IntPtr hdc, int x, int y, char* lpString, int c);\n\t\n\t[DllImport(\"gdi32.dll\", EntryPoint = \"ExtTextOutW\")]\n\tinternal static extern bool ExtTextOut(IntPtr hdc, int x, int y, uint options, in RECT lprect, char* lpString, int c, int* lpDx = null);\n\tinternal const uint ETO_CLIPPED = 0x4;\n\t\n\t[DllImport(\"gdi32.dll\")]\n\tinternal static extern bool MoveToEx(IntPtr hdc, int x, int y, out POINT lppt);\n\t\n\t[DllImport(\"gdi32.dll\")]\n\tinternal static extern bool GetCurrentPositionEx(IntPtr hdc, out POINT lppt);\n\t\n\t[DllImport(\"gdi32.dll\")]\n\tinternal static extern uint SetTextAlign(IntPtr hdc, uint align);\n\t\n\t[DllImport(\"gdi32.dll\")]\n\tinternal static extern int SetTextColor(IntPtr hdc, int color);\n\t\n\t[DllImport(\"gdi32.dll\")]\n\tinternal static extern IntPtr CreatePen(int iStyle, int cWidth, int color);\n\t\n\t[DllImport(\"gdi32.dll\")]\n\tinternal static extern bool LineTo(IntPtr hdc, int x, int y);\n\t\n\t[DllImport(\"gdi32.dll\")] //tested: does not set last error\n\tinternal static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int cx, int cy);\n\t\n\t[DllImport(\"gdi32.dll\")]\n\tinternal static extern int GetDeviceCaps(IntPtr hdc, int index);\n\t\n\t[DllImport(\"gdi32.dll\", EntryPoint = \"GetTextExtentPoint32W\")]\n\tinternal static extern bool GetTextExtentPoint32(IntPtr hdc, string lpString, int c, out SIZE psizl);\n\t\n\t[DllImport(\"gdi32.dll\", EntryPoint = \"GetTextExtentPoint32W\")]\n\tinternal static extern bool GetTextExtentPoint32(IntPtr hdc, char* lpString, int c, out SIZE psizl);\n\t\n\t[DllImport(\"gdi32.dll\", EntryPoint = \"CreateFontW\")]\n\tinternal static extern IntPtr CreateFont(int cHeight, int cWidth = 0, int cEscapement = 0, int cOrientation = 0, int cWeight = 0, int bItalic = 0, int bUnderline = 0, int bStrikeOut = 0, int iCharSet = 0, int iOutPrecision = 0, int iClipPrecision = 0, int iQuality = 0, int iPitchAndFamily = 0, string pszFaceName = null);\n\t\n\t[DllImport(\"gdi32.dll\", EntryPoint = \"CreateFontIndirectW\")]\n\tinternal static extern IntPtr CreateFontIndirect(in LOGFONT lplf);\n\t\n\tinternal const uint SRCCOPY = 0xCC0020;\n\tinternal const uint CAPTUREBLT = 0x40000000;\n\t\n\t[DllImport(\"gdi32.dll\")] //tested: in some cases does not set last error even if returns false\n\tinternal static extern bool BitBlt(IntPtr hdc, int x, int y, int cx, int cy, IntPtr hdcSrc, int x1, int y1, uint rop);\n\t\n\t[DllImport(\"gdi32.dll\")]\n\tinternal static extern bool StretchBlt(IntPtr hdcDest, int xDest, int yDest, int wDest, int hDest, IntPtr hdcSrc, int xSrc, int ySrc, int wSrc, int hSrc, uint rop);\n\t\n\t[DllImport(\"gdi32.dll\")]\n\tinternal static extern IntPtr CreateDIBSection(IntPtr hdc, in Api.BITMAPINFO pbmi, uint usage, out uint* ppvBits, IntPtr hSection = default, uint offset = 0);\n\t\n\tinternal struct BITMAPINFOHEADER {\n\t\tpublic int biSize;\n\t\tpublic int biWidth;\n\t\tpublic int biHeight;\n\t\tpublic ushort biPlanes;\n\t\tpublic ushort biBitCount;\n\t\tpublic int biCompression;\n\t\tpublic int biSizeImage;\n\t\tpublic int biXPelsPerMeter;\n\t\tpublic int biYPelsPerMeter;\n\t\tpublic int biClrUsed;\n\t\tpublic int biClrImportant;\n\t}\n\t\n\t/// <summary>\n\t/// <c>BITMAPINFOHEADER</c> members and 3 uints for color table etc.\n\t/// </summary>\n\tinternal struct BITMAPINFO {\n\t\tpublic readonly int biSize;\n\t\tpublic int biWidth;\n\t\tpublic int biHeight;\n\t\tpublic ushort biPlanes;\n\t\tpublic ushort biBitCount;\n\t\tpublic int biCompression;\n\t\tpublic int biSizeImage;\n\t\tpublic int biXPelsPerMeter;\n\t\tpublic int biYPelsPerMeter;\n\t\tpublic int biClrUsed;\n\t\tpublic int biClrImportant;\n\t\tpublic fixed uint bmiColors[3]; //info: GetDIBits(DIB_RGB_COLORS) sets 0xFF0000, 0xFF00, 0xFF. Note: with 8-bit colors bitmaps need 256, but this library does not use it.\n\t\t\n\t\t/// <summary>\n\t\t/// Sets <c>biSize=sizeof(BITMAPINFOHEADER)</c>. Note: it is less than <c>sizeof(BITMAPINFO)</c>.\n\t\t/// </summary>\n\t\tpublic BITMAPINFO() { biSize = sizeof(BITMAPINFOHEADER); }\n\t\t\n\t\t/// <summary>\n\t\t/// Sets width/height/bitcount/planes fields. Sets <c>biSize=sizeof(BITMAPINFOHEADER)</c>. Note: it is less than <c>sizeof(BITMAPINFO)</c>.\n\t\t/// </summary>\n\t\tpublic BITMAPINFO(int width, int height, int bitCount = 32) {\n\t\t\tbiSize = sizeof(BITMAPINFOHEADER);\n\t\t\tbiWidth = width;\n\t\t\tbiHeight = height;\n\t\t\tbiBitCount = (ushort)bitCount;\n\t\t\tbiPlanes = 1;\n\t\t}\n\t\t\n\t\t//little tested\n\t\t///// <summary>\n\t\t///// Gets DIB bits of compatible bitmap. Uses API <ms>GetDIBits</ms>. Returns <c>null</c> if failed.\n\t\t///// </summary>\n\t\t///// <param name=\"hb\">Bitmap handle.</param>\n\t\t///// <param name=\"topDown\">Create top-down DIB.</param>\n\t\t///// <param name=\"dc\">Eg <see cref=\"ScreenDC_\"/>.</param>\n\t\t///// <param name=\"palColors\">Use DIB_PAL_COLORS.</param>\n\t\t//public byte[] GetBitmapBits(IntPtr hb, bool topDown, IntPtr dc, bool palColors = false) {\n\t\t//\tbiBitCount = 0; //biBitCount=(ushort)bitCount; //somehow fails if bitCount is 32 and bitmap is 32-bit\n\t\t//\tif (0 == GetDIBits(dc, hb, 0, 0, null, ref this, palColors ? 1 : 0)) return null;\n\t\t//\tvar r = new byte[biSizeImage];\n\t\t//\tfixed (byte* p = r) {\n\t\t//\t\tint hei = biHeight; if (topDown) biHeight = -hei;\n\t\t//\t\tvar k = GetDIBits(dc, hb, 0, hei, p, ref this, palColors ? 1 : 0);\n\t\t//\t\tbiHeight = hei;\n\t\t//\t\tif (k == 0) return null;\n\t\t//\t}\n\t\t//\treturn r;\n\t\t//}\n\t}\n\t\n\t[DllImport(\"gdi32.dll\")]\n\tinternal static extern int GetDIBits(IntPtr hdc, IntPtr hbm, int start, int cLines, void* lpvBits, ref BITMAPINFO lpbmi, int usage);\n\t\n\t/// <summary>\n\t/// lpbmi can be <c>BITMAPINFOHEADER</c>/<c>BITMAPV5HEADER</c> or <c>BITMAPCOREHEADER</c>.\n\t/// </summary>\n\t[DllImport(\"gdi32.dll\")]\n\tinternal static extern int SetDIBitsToDevice(IntPtr hdc, int xDest, int yDest, int w, int h, int xSrc, int ySrc, int StartScan, int cLines, void* lpvBits, void* lpbmi, uint ColorUse = 0); //DIB_RGB_COLORS\n\t\n\t//internal const int WHITE_BRUSH = 0;\n\t//internal const int LTGRAY_BRUSH = 1;\n\t//internal const int GRAY_BRUSH = 2;\n\t//internal const int DKGRAY_BRUSH = 3;\n\t//internal const int BLACK_BRUSH = 4;\n\t//internal const int NULL_BRUSH = 5;\n\t//internal const int HOLLOW_BRUSH = 5;\n\t//internal const int WHITE_PEN = 6;\n\t//internal const int BLACK_PEN = 7;\n\t//internal const int NULL_PEN = 8;\n\t//internal const int OEM_FIXED_FONT = 10;\n\t//internal const int ANSI_FIXED_FONT = 11;\n\t//internal const int ANSI_VAR_FONT = 12;\n\t//internal const int SYSTEM_FONT = 13;\n\t//internal const int DEVICE_DEFAULT_FONT = 14;\n\t//internal const int DEFAULT_PALETTE = 15;\n\t//internal const int SYSTEM_FIXED_FONT = 16;\n\t//internal const int DEFAULT_GUI_FONT = 17;\n\t//internal const int DC_BRUSH = 18;\n\t//internal const int DC_PEN = 19;\n\t\n\t[DllImport(\"gdi32.dll\")]\n\tinternal static extern IntPtr GetStockObject(int i);\n\t\n\t[DllImport(\"gdi32.dll\")]\n\tinternal static extern IntPtr CreateSolidBrush(int color);\n\t\n\t[DllImport(\"gdi32.dll\")]\n\tinternal static extern int IntersectClipRect(IntPtr hdc, int left, int top, int right, int bottom);\n\t\n\tinternal struct LOGFONT {\n\t\tpublic int lfHeight;\n\t\tpublic int lfWidth;\n\t\tpublic int lfEscapement;\n\t\tpublic int lfOrientation;\n\t\tpublic int lfWeight;\n\t\tpublic byte lfItalic;\n\t\tpublic byte lfUnderline;\n\t\tpublic byte lfStrikeOut;\n\t\tpublic byte lfCharSet;\n\t\tpublic byte lfOutPrecision;\n\t\tpublic byte lfClipPrecision;\n\t\tpublic byte lfQuality;\n\t\tpublic byte lfPitchAndFamily;\n\t\tpublic fixed char lfFaceName[32];\n\t}\n\t\n\tinternal struct NONCLIENTMETRICS {\n\t\tpublic int cbSize;\n\t\tpublic int iBorderWidth;\n\t\tpublic int iScrollWidth;\n\t\tpublic int iScrollHeight;\n\t\tpublic int iCaptionWidth;\n\t\tpublic int iCaptionHeight;\n\t\tpublic LOGFONT lfCaptionFont;\n\t\tpublic int iSmCaptionWidth;\n\t\tpublic int iSmCaptionHeight;\n\t\tpublic LOGFONT lfSmCaptionFont;\n\t\tpublic int iMenuWidth;\n\t\tpublic int iMenuHeight;\n\t\tpublic LOGFONT lfMenuFont;\n\t\tpublic LOGFONT lfStatusFont;\n\t\tpublic LOGFONT lfMessageFont;\n\t\tpublic int iPaddedBorderWidth;\n\t}\n\t\n\t[DllImport(\"gdi32.dll\")] //does not set last error when fails\n\tinternal static extern uint GetPixel(IntPtr hdc, int x, int y);\n\t\n\t#endregion\n\t\n\t#region advapi32\n\t\n\t[DllImport(\"advapi32.dll\")]\n\tinternal static extern int RegSetValueEx(IntPtr hKey, string lpValueName, int Reserved, Microsoft.Win32.RegistryValueKind dwType, void* lpData, int cbData);\n\t\n\t[DllImport(\"advapi32.dll\")]\n\tinternal static extern int RegQueryValueEx(IntPtr hKey, string lpValueName, IntPtr Reserved, out Microsoft.Win32.RegistryValueKind dwType, void* lpData, ref int cbData);\n\t\n\tinternal const uint TOKEN_WRITE = STANDARD_RIGHTS_WRITE | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT;\n\tinternal const uint TOKEN_SOURCE_LENGTH = 8;\n\tinternal const uint TOKEN_READ = STANDARD_RIGHTS_READ | TOKEN_QUERY;\n\tinternal const uint TOKEN_QUERY_SOURCE = 16;\n\tinternal const uint TOKEN_QUERY = 8;\n\tinternal const uint TOKEN_IMPERSONATE = 4;\n\tinternal const uint TOKEN_EXECUTE = STANDARD_RIGHTS_EXECUTE;\n\tinternal const uint TOKEN_DUPLICATE = 2;\n\tinternal const uint TOKEN_AUDIT_SUCCESS_INCLUDE = 1;\n\tinternal const uint TOKEN_AUDIT_SUCCESS_EXCLUDE = 2;\n\tinternal const uint TOKEN_AUDIT_FAILURE_INCLUDE = 4;\n\tinternal const uint TOKEN_AUDIT_FAILURE_EXCLUDE = 8;\n\tinternal const uint TOKEN_ASSIGN_PRIMARY = 1;\n\tinternal const uint TOKEN_ALL_ACCESS_P = STANDARD_RIGHTS_REQUIRED | TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_QUERY_SOURCE | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT;\n\tinternal const uint TOKEN_ALL_ACCESS = TOKEN_ALL_ACCESS_P | TOKEN_ADJUST_SESSIONID;\n\tinternal const uint TOKEN_ADJUST_SESSIONID = 256;\n\tinternal const uint TOKEN_ADJUST_PRIVILEGES = 32;\n\tinternal const uint TOKEN_ADJUST_GROUPS = 64;\n\tinternal const uint TOKEN_ADJUST_DEFAULT = 128;\n\t\n\t[DllImport(\"advapi32.dll\", SetLastError = true)]\n\tinternal static extern bool OpenProcessToken(IntPtr ProcessHandle, uint DesiredAccess, out Handle_ TokenHandle);\n\t\n\tinternal enum TOKEN_INFORMATION_CLASS {\n\t\tTokenUser = 1,\n\t\tTokenGroups,\n\t\tTokenPrivileges,\n\t\tTokenOwner,\n\t\tTokenPrimaryGroup,\n\t\tTokenDefaultDacl,\n\t\tTokenSource,\n\t\tTokenType,\n\t\tTokenImpersonationLevel,\n\t\tTokenStatistics,\n\t\tTokenRestrictedSids,\n\t\tTokenSessionId,\n\t\tTokenGroupsAndPrivileges,\n\t\tTokenSessionReference,\n\t\tTokenSandBoxInert,\n\t\tTokenAuditPolicy,\n\t\tTokenOrigin,\n\t\tTokenElevationType,\n\t\tTokenLinkedToken,\n\t\tTokenElevation,\n\t\tTokenHasRestrictions,\n\t\tTokenAccessInformation,\n\t\tTokenVirtualizationAllowed,\n\t\tTokenVirtualizationEnabled,\n\t\tTokenIntegrityLevel,\n\t\tTokenUIAccess,\n\t\tTokenMandatoryPolicy,\n\t\tTokenLogonSid,\n\t\t//Win8\n\t\tTokenIsAppContainer,\n\t\tTokenCapabilities,\n\t\tTokenAppContainerSid,\n\t\tTokenAppContainerNumber,\n\t\tTokenUserClaimAttributes,\n\t\tTokenDeviceClaimAttributes,\n\t\tTokenRestrictedUserClaimAttributes,\n\t\tTokenRestrictedDeviceClaimAttributes,\n\t\tTokenDeviceGroups,\n\t\tTokenRestrictedDeviceGroups,\n\t\tTokenSecurityAttributes,\n\t\tTokenIsRestricted,\n\t\tTokenProcessTrustLevel,\n\t\tTokenPrivateNameSpace,\n\t\tMaxTokenInfoClass  // MaxTokenInfoClass should always be the last enum\n\t}\n\t\n\t[DllImport(\"advapi32.dll\", SetLastError = true)]\n\tinternal static extern bool GetTokenInformation(HandleRef TokenHandle, TOKEN_INFORMATION_CLASS TokenInformationClass, void* TokenInformation, uint TokenInformationLength, out uint ReturnLength);\n\t\n\t[DllImport(\"advapi32.dll\")]\n\tinternal static extern byte* GetSidSubAuthorityCount(IntPtr pSid);\n\t\n\t[DllImport(\"advapi32.dll\")]\n\tinternal static extern uint* GetSidSubAuthority(IntPtr pSid, uint nSubAuthority);\n\t\n\tinternal enum SECURITY_IMPERSONATION_LEVEL {\n\t\tSecurityAnonymous,\n\t\tSecurityIdentification,\n\t\tSecurityImpersonation,\n\t\tSecurityDelegation\n\t}\n\t\n\tinternal enum TOKEN_TYPE {\n\t\tTokenPrimary = 1,\n\t\tTokenImpersonation\n\t}\n\t\n\t[DllImport(\"advapi32.dll\", SetLastError = true)]\n\tinternal static extern bool DuplicateTokenEx(IntPtr hExistingToken, uint dwDesiredAccess, SECURITY_ATTRIBUTES lpTokenAttributes, SECURITY_IMPERSONATION_LEVEL ImpersonationLevel, TOKEN_TYPE TokenType, out IntPtr phNewToken);\n\t\n\t[DllImport(\"advapi32.dll\", SetLastError = true)]\n\tinternal static extern bool CreateProcessWithTokenW(IntPtr hToken, uint dwLogonFlags, string lpApplicationName, char[] lpCommandLine, uint dwCreationFlags, string lpEnvironment, string lpCurrentDirectory, in STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);\n\t\n\tinternal struct LUID {\n\t\tpublic uint LowPart;\n\t\tpublic int HighPart;\n\t}\n\t\n\t[DllImport(\"advapi32.dll\", EntryPoint = \"LookupPrivilegeValueW\", SetLastError = true)]\n\tinternal static extern bool LookupPrivilegeValue(string lpSystemName, string lpName, out LUID lpLuid);\n\t\n\t[StructLayout(LayoutKind.Sequential, Pack = 4)]\n\tinternal struct LUID_AND_ATTRIBUTES {\n\t\tpublic LUID Luid;\n\t\tpublic uint Attributes;\n\t}\n\t\n\tinternal struct TOKEN_PRIVILEGES {\n\t\tpublic int PrivilegeCount;\n\t\tpublic LUID_AND_ATTRIBUTES Privileges; //[1]\n\t}\n\t\n\t[DllImport(\"advapi32.dll\", SetLastError = true)]\n\tinternal static extern bool AdjustTokenPrivileges(IntPtr TokenHandle, bool DisableAllPrivileges, in TOKEN_PRIVILEGES NewState, uint BufferLength, [Out] TOKEN_PRIVILEGES[] PreviousState, IntPtr ReturnLength);\n\t\n\t[StructLayout(LayoutKind.Sequential)]\n\tinternal sealed class SECURITY_ATTRIBUTES : IDisposable {\n\t\tpublic int nLength;\n\t\tpublic void* lpSecurityDescriptor;\n\t\tpublic int bInheritHandle;\n\t\t\n\t\t/// <summary>\n\t\t/// Creates <c>SECURITY_ATTRIBUTES</c> from string security descriptor.\n\t\t/// <i>securityDescriptor</i> can be <c>null</c>; then <c>lpSecurityDescriptor</c> will be <c>null</c>.\n\t\t/// </summary>\n\t\tpublic SECURITY_ATTRIBUTES(string securityDescriptor) {\n\t\t\tnLength = IntPtr.Size * 3;\n\t\t\tif (securityDescriptor != null && !ConvertStringSecurityDescriptorToSecurityDescriptor(securityDescriptor, 1, out lpSecurityDescriptor)) throw new AuException(0, \"SECURITY_ATTRIBUTES\");\n\t\t}\n\t\t\n\t\tpublic void Dispose() {\n\t\t\tif (lpSecurityDescriptor != null) {\n\t\t\t\tLocalFree(lpSecurityDescriptor);\n\t\t\t\tlpSecurityDescriptor = null;\n\t\t\t}\n\t\t}\n\t\t\n\t\t~SECURITY_ATTRIBUTES() => Dispose();\n\t\t\n\t\t/// <summary>\n\t\t/// Creates <c>SECURITY_ATTRIBUTES</c> that allows UAC low IL processes to open the kernel object.\n\t\t/// </summary>\n\t\tpublic static readonly SECURITY_ATTRIBUTES ForLowIL = new SECURITY_ATTRIBUTES(\"D:NO_ACCESS_CONTROLS:(ML;;NW;;;LW)\");\n\t\t\n\t\t/// <summary>\n\t\t/// Creates <c>SECURITY_ATTRIBUTES</c> that allows UAC medium IL processes to open the pipe.\n\t\t/// Like of <c>PipeSecurity</c> that allows <c>ReadWrite</c> for <c>AuthenticatedUserSid</c>.\n\t\t/// </summary>\n\t\tpublic static readonly SECURITY_ATTRIBUTES ForPipes = new SECURITY_ATTRIBUTES(\"D:(A;;0x12019b;;;AU)\");\n\t}\n\t\n\t[DllImport(\"advapi32.dll\", EntryPoint = \"ConvertStringSecurityDescriptorToSecurityDescriptorW\", SetLastError = true)]\n\tinternal static extern bool ConvertStringSecurityDescriptorToSecurityDescriptor(string StringSecurityDescriptor, uint StringSDRevision, out void* SecurityDescriptor, uint* SecurityDescriptorSize = null);\n\t\n\t//[DllImport(\"advapi32.dll\", EntryPoint = \"ConvertSecurityDescriptorToStringSecurityDescriptorW\")]\n\t//internal static extern bool ConvertSecurityDescriptorToStringSecurityDescriptor(void* SecurityDescriptor, uint RequestedStringSDRevision, uint SecurityInformation, out char* StringSecurityDescriptor, out uint StringSecurityDescriptorLen);\n\t\n\t[DllImport(\"advapi32.dll\", EntryPoint = \"InitiateSystemShutdownW\", SetLastError = true)]\n\tinternal static extern bool InitiateSystemShutdown(string lpMachineName, string lpMessage, int dwTimeout, bool bForceAppsClosed, bool bRebootAfterShutdown);\n\t\n\t\n\t\n\t\n\t#endregion\n\t\n\t#region shell32\n\t\n\t//[DllImport(\"shell32.dll\")]\n\t//internal static extern bool IsUserAnAdmin();\n\t\n\tinternal const uint SHGFI_ICON = 0x000000100;     // get icon;\n\tinternal const uint SHGFI_DISPLAYNAME = 0x000000200;     // get display name;\n\tinternal const uint SHGFI_TYPENAME = 0x000000400;     // get type name;\n\tinternal const uint SHGFI_ATTRIBUTES = 0x000000800;     // get attributes;\n\tinternal const uint SHGFI_ICONLOCATION = 0x000001000;     // get icon location;\n\tinternal const uint SHGFI_EXETYPE = 0x000002000;     // return exe type;\n\tinternal const uint SHGFI_SYSICONINDEX = 0x000004000;     // get system icon index;\n\tinternal const uint SHGFI_LINKOVERLAY = 0x000008000;     // put a link overlay on icon;\n\tinternal const uint SHGFI_SELECTED = 0x000010000;     // show icon in selected state;\n\tinternal const uint SHGFI_ATTR_SPECIFIED = 0x000020000;     // get only specified attributes;\n\tinternal const uint SHGFI_LARGEICON = 0x000000000;     // get large icon;\n\tinternal const uint SHGFI_SMALLICON = 0x000000001;     // get small icon;\n\tinternal const uint SHGFI_OPENICON = 0x000000002;     // get open icon;\n\tinternal const uint SHGFI_SHELLICONSIZE = 0x000000004;     // get shell size icon;\n\tinternal const uint SHGFI_PIDL = 0x000000008;     // pszPath is a pidl;\n\tinternal const uint SHGFI_USEFILEATTRIBUTES = 0x000000010;     // use passed dwFileAttribute;\n\tinternal const uint SHGFI_ADDOVERLAYS = 0x000000020;     // apply the appropriate overlays;\n\tinternal const uint SHGFI_OVERLAYINDEX = 0x000000040;     // Get the index of the overlay;\n\t\n\tinternal struct SHFILEINFO {\n\t\tpublic IntPtr hIcon;\n\t\tpublic int iIcon;\n\t\tpublic uint dwAttributes;\n\t\t[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]\n\t\tpublic string szDisplayName;\n\t\t[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]\n\t\tpublic string szTypeName;\n\t}\n\t\n\t//[DllImport(\"shell32.dll\", EntryPoint = \"SHGetFileInfoW\")]\n\t//internal static extern nint SHGetFileInfo(string pszPath, uint dwFileAttributes, out SHFILEINFO psfi, int cbFileInfo, uint uFlags);\n\t\n\t[DllImport(\"shell32.dll\", EntryPoint = \"SHGetFileInfoW\")]\n\tstatic extern nint _SHGetFileInfo(nint pidl, uint dwFileAttributes, out SHFILEINFO psfi, int cbFileInfo, uint uFlags);\n\t\n\tinternal static nint SHGetFileInfo(nint pidl, out SHFILEINFO psfi, uint uFlags, uint dwFileAttributes = 0) {\n\t\treturn _SHGetFileInfo(pidl, dwFileAttributes, out psfi, Api.SizeOf<Api.SHFILEINFO>(), uFlags);\n\t}\n\t\n\tinternal static nint SHGetFileInfo(string path, out SHFILEINFO psfi, uint uFlags, uint dwFileAttributes = 0) {\n\t\t//if (0 != (uFlags & SHGFI_PIDL)) { //not tested\n\t\t//\tvar pidl = Pidl.FromString_(path);\n\t\t//\tif (pidl == 0) { psfi = default; return 0; }\n\t\t//\ttry { return _SHGetFileInfo(pidl, dwFileAttributes, out psfi, Api.SizeOf<Api.SHFILEINFO>(), uFlags); }\n\t\t//\tfinally { Marshal.FreeCoTaskMem(pidl); }\n\t\t//} else {\n\t\tfixed (char* p = path) return _SHGetFileInfo((nint)p, dwFileAttributes, out psfi, Api.SizeOf<Api.SHFILEINFO>(), uFlags);\n\t\t//}\n\t}\n\t\n\t//[DllImport(\"shell32.dll\")]\n\t//internal static extern int SHGetDesktopFolder(out IShellFolder ppshf);\n\t\n\t[DllImport(\"shell32.dll\")]\n\tinternal static extern int SHParseDisplayName(string pszName, IntPtr pbc, out IntPtr pidl, uint sfgaoIn, uint* psfgaoOut);\n\t\n\t[DllImport(\"shell32.dll\")]\n\tinternal static extern int SHGetNameFromIDList(IntPtr pidl, SIGDN sigdnName, out string ppszName);\n\t\n\t[DllImport(\"shell32.dll\")]\n\tinternal static extern int SHCreateShellItem(IntPtr pidlParent, IShellFolder psfParent, IntPtr pidl, out IShellItem ppsi);\n\t//This classic API supports absolute PIDL and parent+relative PIDL.\n\t//There are 2 newer API - SHCreateItemFromIDList (absoulte) and SHCreateItemWithParent (parent+relative). They can get IShellItem2 too, which is currently not useful here. Same speed.\n\t\n\t//[DllImport(\"shell32.dll\")]\n\t//internal static extern int SHCreateItemFromIDList(IntPtr pidl, in Guid riid, out IShellItem ppv); //or IShellItem2\n\t\n\t[DllImport(\"shell32.dll\")]\n\tinternal static extern int SHBindToParent(IntPtr pidl, in Guid riid, out IShellFolder ppv, out IntPtr ppidlLast);\n\t\n\t[DllImport(\"shell32.dll\")]\n\tinternal static extern int SHGetPropertyStoreForWindow(wnd hwnd, in Guid riid, out IPropertyStore ppv);\n\t\n\tinternal static PROPERTYKEY PKEY_AppUserModel_ID = new PROPERTYKEY() { fmtid = new Guid(0x9F4C2855, 0x9F79, 0x4B39, 0xA8, 0xD0, 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3), pid = 5 };\n\t\n\t[DllImport(\"shell32.dll\")]\n\tinternal static extern char** CommandLineToArgvW(string lpCmdLine, out int pNumArgs);\n\t\n\t[DllImport(\"shell32.dll\", EntryPoint = \"Shell_NotifyIconW\", SetLastError = true)]\n\tinternal static extern bool Shell_NotifyIcon(int dwMessage, in NOTIFYICONDATA lpData);\n\t\n\tinternal const int NIM_ADD = 0x0;\n\tinternal const int NIM_MODIFY = 0x1;\n\tinternal const int NIM_DELETE = 0x2;\n\tinternal const int NIM_SETFOCUS = 0x3;\n\tinternal const int NIM_SETVERSION = 0x4;\n\t\n\tinternal const int NOTIFYICON_VERSION_4 = 4;\n\t\n\tinternal const uint NIF_MESSAGE = 0x1;\n\tinternal const uint NIF_ICON = 0x2;\n\tinternal const uint NIF_TIP = 0x4;\n\tinternal const uint NIF_STATE = 0x8;\n\tinternal const uint NIF_INFO = 0x10;\n\tinternal const uint NIF_GUID = 0x20;\n\tinternal const uint NIF_REALTIME = 0x40;\n\tinternal const uint NIF_SHOWTIP = 0x80;\n\t\n\tinternal const uint NIS_HIDDEN = 0x1;\n\tinternal const uint NIS_SHAREDICON = 0x2;\n\t\n\tinternal struct NOTIFYICONDATA {\n\t\t/// <summary>\n\t\t/// Sets <c>cbSize</c>, <c>hWnd</c> and <c>uFlags</c>.\n\t\t/// </summary>\n\t\t/// <param name=\"wNotify\"></param>\n\t\t/// <param name=\"nifFlags\"></param>\n\t\tpublic NOTIFYICONDATA(wnd wNotify, uint nifFlags = 0) {\n\t\t\tcbSize = Api.SizeOf<Api.NOTIFYICONDATA>();\n\t\t\thWnd = wNotify;\n\t\t\tuFlags = nifFlags;\n\t\t}\n\t\t\n\t\tpublic int cbSize;\n\t\tpublic wnd hWnd;\n\t\tpublic int uID;\n\t\tpublic uint uFlags;\n\t\tpublic int uCallbackMessage;\n\t\tpublic IntPtr hIcon;\n\t\t[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] public string szTip;\n\t\tpublic uint dwState;\n\t\tpublic uint dwStateMask;\n\t\t[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string szInfo;\n\t\tpublic int uVersion;\n\t\t[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)] public string szInfoTitle;\n\t\tpublic uint dwInfoFlags;\n\t\tpublic Guid guidItem;\n\t\tpublic IntPtr hBalloonIcon;\n\t}\n\t\n\tinternal const int NIN_SELECT = 0x400;\n\tinternal const int NIN_KEYSELECT = 0x401;\n\tinternal const int NIN_BALLOONSHOW = 0x402;\n\tinternal const int NIN_BALLOONHIDE = 0x403;\n\tinternal const int NIN_BALLOONTIMEOUT = 0x404;\n\tinternal const int NIN_BALLOONUSERCLICK = 0x405;\n\tinternal const int NIN_POPUPOPEN = 0x406;\n\tinternal const int NIN_POPUPCLOSE = 0x407;\n\t\n\t[DllImport(\"shell32.dll\")]\n\tinternal static extern int Shell_NotifyIconGetRect(in NOTIFYICONIDENTIFIER identifier, out RECT iconLocation);\n\tinternal struct NOTIFYICONIDENTIFIER {\n\t\tpublic int cbSize;\n\t\tpublic wnd hWnd;\n\t\tpublic int uID;\n\t\tpublic Guid guidItem;\n\t}\n\t\n\tinternal struct SHSTOCKICONINFO {\n\t\tpublic int cbSize;\n\t\tpublic IntPtr hIcon;\n\t\tpublic int iSysImageIndex;\n\t\tpublic int iIcon;\n\t\tpublic fixed char szPath[260];\n\t}\n\t\n\t[DllImport(\"shell32.dll\")]\n\tinternal static extern int SHGetStockIconInfo(StockIcon siid, uint uFlags, ref SHSTOCKICONINFO psii);\n\t\n\t[DllImport(\"shell32.dll\", EntryPoint = \"#6\")]\n\tinternal static extern int SHDefExtractIcon(string pszIconFile, int iIndex, uint uFlags, IntPtr* phiconLarge, IntPtr* phiconSmall, int nIconSize);\n\t\n\tinternal const int SHIL_LARGE = 0;\n\tinternal const int SHIL_SMALL = 1;\n\tinternal const int SHIL_EXTRALARGE = 2;\n\t//internal const int SHIL_SYSSMALL = 3;\n\tinternal const int SHIL_JUMBO = 4;\n\t\n\t//[DllImport(\"shell32.dll\", EntryPoint = \"#727\")]\n\t//internal static extern int SHGetImageList(int iImageList, in Guid riid, out IImageList ppvObj);\n\t[DllImport(\"shell32.dll\", EntryPoint = \"#727\")]\n\tinternal static extern int SHGetImageList(int iImageList, in Guid riid, out IntPtr ppvObj);\n\t\n\tinternal const uint SHCNE_RENAMEITEM = 0x1;\n\tinternal const uint SHCNE_CREATE = 0x2;\n\tinternal const uint SHCNE_DELETE = 0x4;\n\tinternal const uint SHCNE_MKDIR = 0x8;\n\tinternal const uint SHCNE_RMDIR = 0x10;\n\tinternal const uint SHCNE_MEDIAINSERTED = 0x20;\n\tinternal const uint SHCNE_MEDIAREMOVED = 0x40;\n\tinternal const uint SHCNE_DRIVEREMOVED = 0x80;\n\tinternal const uint SHCNE_DRIVEADD = 0x100;\n\tinternal const uint SHCNE_NETSHARE = 0x200;\n\tinternal const uint SHCNE_NETUNSHARE = 0x400;\n\tinternal const uint SHCNE_ATTRIBUTES = 0x800;\n\tinternal const uint SHCNE_UPDATEDIR = 0x1000;\n\tinternal const uint SHCNE_UPDATEITEM = 0x2000;\n\tinternal const uint SHCNE_SERVERDISCONNECT = 0x4000;\n\tinternal const uint SHCNE_UPDATEIMAGE = 0x8000;\n\tinternal const uint SHCNE_DRIVEADDGUI = 0x10000;\n\tinternal const uint SHCNE_RENAMEFOLDER = 0x20000;\n\tinternal const uint SHCNE_FREESPACE = 0x40000;\n\tinternal const uint SHCNE_EXTENDED_EVENT = 0x4000000;\n\tinternal const uint SHCNE_ASSOCCHANGED = 0x8000000;\n\tinternal const uint SHCNE_DISKEVENTS = 0x2381F;\n\tinternal const uint SHCNE_GLOBALEVENTS = 0xC0581E0;\n\tinternal const uint SHCNE_ALLEVENTS = 0x7FFFFFFF;\n\tinternal const uint SHCNE_INTERRUPT = 0x80000000;\n\t\n\tinternal const uint SHCNF_IDLIST = 0x0;\n\tinternal const uint SHCNF_DWORD = 0x3;\n\tinternal const uint SHCNF_PATH = 0x5;\n\tinternal const uint SHCNF_PRINTER = 0x6;\n\tinternal const uint SHCNF_FLUSH = 0x1000;\n\tinternal const uint SHCNF_FLUSHNOWAIT = 0x3000;\n\tinternal const uint SHCNF_NOTIFYRECURSIVE = 0x10000;\n\t\n\t[DllImport(\"shell32.dll\")]\n\tinternal static extern void SHChangeNotify(uint wEventId, uint uFlags, string dwItem1, string dwItem2);\n\t\n\tinternal const uint SEE_MASK_CONNECTNETDRV = 0x80;\n\tinternal const uint SEE_MASK_NOZONECHECKS = 0x800000;\n\tinternal const uint SEE_MASK_UNICODE = 0x4000;\n\tinternal const uint SEE_MASK_FLAG_NO_UI = 0x400;\n\tinternal const uint SEE_MASK_INVOKEIDLIST = 0xC;\n\tinternal const uint SEE_MASK_NOCLOSEPROCESS = 0x40;\n\tinternal const uint SEE_MASK_NOASYNC = 0x100;\n\tinternal const uint SEE_MASK_NO_CONSOLE = 0x8000;\n\t//internal const uint SEE_MASK_HMONITOR = 0x200000;\n\t//internal const uint SEE_MASK_WAITFORINPUTIDLE = 0x2000000;\n\tinternal const uint SEE_MASK_FLAG_LOG_USAGE = 0x4000000;\n\t\n\tinternal struct SHELLEXECUTEINFO {\n\t\tpublic int cbSize;\n\t\tpublic uint fMask;\n\t\tpublic wnd hwnd;\n\t\tpublic string lpVerb;\n\t\tpublic string lpFile;\n\t\tpublic string lpParameters;\n\t\tpublic string lpDirectory;\n\t\tpublic int nShow;\n\t\tpublic IntPtr hInstApp;\n\t\tpublic IntPtr lpIDList;\n\t\tpublic string lpClass;\n\t\tpublic IntPtr hkeyClass;\n\t\tpublic uint dwHotKey;\n\t\tpublic IntPtr hMonitor;\n\t\tpublic Handle_ hProcess;\n\t}\n\t\n\t[DllImport(\"shell32.dll\", EntryPoint = \"ShellExecuteExW\", SetLastError = true)]\n\tinternal static extern bool ShellExecuteEx(ref SHELLEXECUTEINFO pExecInfo);\n\t\n\t[DllImport(\"shell32.dll\")]\n\tinternal static extern int SHOpenFolderAndSelectItems(HandleRef pidlFolder, uint cidl, IntPtr[] apidl, uint dwFlags);\n\t\n\t[DllImport(\"shell32.dll\", EntryPoint = \"#152\")]\n\tinternal static extern int ILGetSize(IntPtr pidl);\n\t\n\t[DllImport(\"shell32.dll\", EntryPoint = \"#25\")]\n\tinternal static extern IntPtr ILCombine(IntPtr pidl1, IntPtr pidl2);\n\t\n\t[DllImport(\"shell32.dll\", EntryPoint = \"#21\")]\n\tinternal static extern bool ILIsEqual(IntPtr pidl1, IntPtr pidl2);\n\t\n\tinternal const uint FO_MOVE = 0x1;\n\tinternal const uint FO_COPY = 0x2;\n\tinternal const uint FO_DELETE = 0x3;\n\tinternal const uint FO_RENAME = 0x4;\n\t\n\tinternal const uint FOF_MULTIDESTFILES = 0x1;\n\tinternal const uint FOF_CONFIRMMOUSE = 0x2;\n\tinternal const uint FOF_SILENT = 0x4;\n\tinternal const uint FOF_RENAMEONCOLLISION = 0x8;\n\tinternal const uint FOF_NOCONFIRMATION = 0x10;\n\tinternal const uint FOF_WANTMAPPINGHANDLE = 0x20;\n\tinternal const uint FOF_ALLOWUNDO = 0x40;\n\tinternal const uint FOF_FILESONLY = 0x80;\n\tinternal const uint FOF_SIMPLEPROGRESS = 0x100;\n\tinternal const uint FOF_NOCONFIRMMKDIR = 0x200;\n\tinternal const uint FOF_NOERRORUI = 0x400;\n\tinternal const uint FOF_NOCOPYSECURITYATTRIBS = 0x800;\n\tinternal const uint FOF_NORECURSION = 0x1000;\n\tinternal const uint FOF_NO_CONNECTED_ELEMENTS = 0x2000;\n\tinternal const uint FOF_WANTNUKEWARNING = 0x4000;\n\tinternal const uint FOF_NORECURSEREPARSE = 0x8000;\n\tinternal const uint FOF_NO_UI = 0x614;\n\t\n\tinternal struct SHFILEOPSTRUCT {\n\t\tpublic wnd hwnd;\n\t\tpublic uint wFunc;\n\t\tpublic string pFrom;\n\t\tpublic string pTo;\n\t\tpublic ushort fFlags;\n\t\t\n\t\t//workaround for this problem: the 32-bit version of SHFILEOPSTRUCT uses Pack = 1, ie no 2-byte gap after fFlags.\n\t\t//\tI don't want to use two versions. Then also would need two versions of code that use this struct.\n\t\t//\tThe last two members are not useful, but we need fAnyOperationsAborted. This workaround gets it through a property function.\n\t\t//\tUpdate: we don't need fAnyOperationsAborted. We use FOF_SILENT therefore cannot be aborted. And it is unreliable.\n\t\t//\t\tBut the workaround is tested, on both platformas.\n\t\t\n#if use_fAnyOperationsAborted\n\t\t\t\t//public bool fAnyOperationsAborted; //BOOL\n\t\t\t\tushort _fAnyOperationsAborted_32, _fAnyOperationsAborted_common, _fAnyOperationsAborted_64;\n\t\t\t\tpublic bool fAnyOperationsAborted\n\t\t\t\t{\n\t\t\t\t\tget\n\t\t\t\t\t{\n\t\t\t\t\t\tif(_fAnyOperationsAborted_common != 0) return true;\n\t\t\t\t\t\tvar v = osVersion.is32BitProcess ? _fAnyOperationsAborted_32 : _fAnyOperationsAborted_64;\n\t\t\t\t\t\treturn v != 0;\n\t\t\t\t\t}\n\t\t\t\t}\n#else\n\t\tprivate int fAnyOperationsAborted;\n#endif\n\t\tprivate IntPtr hNameMappings;\n\t\tprivate string lpszProgressTitle;\n\t\t//these are private and not used, because would be at invalid offsets on 32-bit\n\t}\n\t\n\t//internal struct SHFILEOPSTRUCT\n\t//{\n\t//\tpublic wnd hwnd;\n\t//\tpublic uint wFunc;\n\t//\tpublic string pFrom;\n\t//\tpublic string pTo;\n\t//\tpublic ushort fFlags;\n\t//\tpublic bool fAnyOperationsAborted;\n\t//\tpublic IntPtr hNameMappings;\n\t//\tpublic string lpszProgressTitle;\n\t//}\n\t\n\t//[StructLayout(LayoutKind.Sequential, Pack = 1)]\n\t//internal struct SHFILEOPSTRUCT__32\n\t//{\n\t//\tpublic wnd hwnd;\n\t//\tpublic uint wFunc;\n\t//\tpublic string pFrom;\n\t//\tpublic string pTo;\n\t//\tpublic ushort fFlags;\n\t//\tpublic bool fAnyOperationsAborted;\n\t//\tpublic IntPtr hNameMappings;\n\t//\tpublic string lpszProgressTitle;\n\t//}\n\t\n\t[DllImport(\"shell32.dll\", EntryPoint = \"SHFileOperationW\")]\n\tinternal static extern int SHFileOperation(in SHFILEOPSTRUCT lpFileOp);\n\t\n\t[DllImport(\"shell32.dll\", EntryPoint = \"DragQueryFileW\")]\n\tinternal static extern int DragQueryFile(IntPtr hDrop, int iFile, char* lpszFile, int cch);\n\t\n\t[DllImport(\"shell32.dll\")]\n\tinternal static extern bool IsUserAnAdmin();\n\t\n\t[DllImport(\"shell32.dll\", EntryPoint = \"SHEmptyRecycleBinW\")]\n\tinternal static extern int SHEmptyRecycleBin(wnd hwnd, string pszRootPath, int dwFlags);\n\t\n\t\n\t#endregion\n\t\n\t#region shlwapi\n\t\n\t[DllImport(\"shlwapi.dll\")]\n\tstatic extern int IUnknown_QueryService([MarshalAs(UnmanagedType.IUnknown)] object punk, in Guid guidService, in Guid riid, [MarshalAs(UnmanagedType.IUnknown)] out object ppvOut);\n\t\n\tpublic static bool QueryService<T>(object from, in Guid guidService, out T result) where T : class {\n\t\tbool ok = 0 == IUnknown_QueryService(from, guidService, typeof(T).GUID, out var o);\n\t\tresult = ok ? (T)o : null;\n\t\treturn ok;\n\t}\n\t\n\t//[DllImport(\"shlwapi.dll\")]\n\t//internal static extern uint ColorAdjustLuma(uint clrRGB, int n, bool fScale);\n\t\n\t[DllImport(\"shlwapi.dll\")]\n\tinternal static extern void ColorRGBToHLS(int clrRGB, out ushort pwHue, out ushort pwLuminance, out ushort pwSaturation);\n\t\n\t[DllImport(\"shlwapi.dll\")]\n\tinternal static extern int ColorHLSToRGB(ushort wHue, ushort wLuminance, ushort wSaturation);\n\t\n\t[DllImport(\"shlwapi.dll\")]\n\tinternal static extern int PathCreateFromUrlAlloc(string pszIn, out string ppszOut, uint dwFlags = 0);\n\t\n\t//internal enum ASSOCSTR\n\t//{\n\t//\tASSOCSTR_COMMAND = 1,\n\t//\tASSOCSTR_EXECUTABLE,\n\t//\tASSOCSTR_FRIENDLYDOCNAME,\n\t//\tASSOCSTR_FRIENDLYAPPNAME,\n\t//\tASSOCSTR_NOOPEN,\n\t//\tASSOCSTR_SHELLNEWVALUE,\n\t//\tASSOCSTR_DDECOMMAND,\n\t//\tASSOCSTR_DDEIFEXEC,\n\t//\tASSOCSTR_DDEAPPLICATION,\n\t//\tASSOCSTR_DDETOPIC,\n\t//\tASSOCSTR_INFOTIP,\n\t//\tASSOCSTR_QUICKTIP,\n\t//\tASSOCSTR_TILEINFO,\n\t//\tASSOCSTR_CONTENTTYPE,\n\t//\tASSOCSTR_DEFAULTICON,\n\t//\tASSOCSTR_SHELLEXTENSION,\n\t//\tASSOCSTR_DROPTARGET,\n\t//\tASSOCSTR_DELEGATEEXECUTE,\n\t//\tASSOCSTR_SUPPORTED_URI_PROTOCOLS,\n\t//\tASSOCSTR_PROGID,\n\t//\tASSOCSTR_APPID,\n\t//\tASSOCSTR_APPPUBLISHER,\n\t//\tASSOCSTR_APPICONREFERENCE,\n\t//\tASSOCSTR_MAX\n\t//}\n\t\n\t//[DllImport(\"shlwapi.dll\", EntryPoint = \"AssocQueryStringW\")]\n\t//internal static extern int AssocQueryString(uint flags, /*ASSOCSTR*/ int str, string pszAssoc, string pszExtra, char[] pszOut, ref int pcchOut);\n\t\n\t///// <summary>\n\t///// Returns executable path of file type.\n\t///// </summary>\n\t///// <param name=\"dotExt\"></param>\n\t//internal static string AssocQueryString(string dotExt/*, ASSOCSTR what = ASSOCSTR.ASSOCSTR_EXECUTABLE*/)\n\t//{\n\t//\tvar b = ApiBuffer_.Char_(300, out var n);\n\t//\tint hr = AssocQueryString(0x20, 2, dotExt, null, b, ref n); //ASSOCF_NOTRUNCATE\n\t//\tif(hr == E_POINTER) hr = AssocQueryString(0x20, 2, dotExt, null, b = ApiBuffer_.Char_(n), ref n);\n\t//\treturn hr == 0 ? b.ToString(n) : null;\n\t//}\n\t\n\t\n\t#endregion\n\t\n\t#region comctl32\n\t\n\t[DllImport(\"comctl32.dll\")]\n\tinternal static extern IntPtr ImageList_GetIcon(IntPtr himl, int i, uint flags);\n\t\n\t[DllImport(\"comctl32.dll\")]\n\tinternal static extern bool ImageList_GetIconSize(IntPtr himl, out int cx, out int cy);\n\t\n\tinternal const uint TME_LEAVE = 0x2;\n\tinternal const uint TME_NONCLIENT = 0x10;\n\tinternal const uint TME_CANCEL = 0x80000000;\n\t\n\tinternal struct TRACKMOUSEEVENT {\n\t\tpublic int cbSize;\n\t\tpublic uint dwFlags;\n\t\tpublic wnd hwndTrack;\n\t\tpublic int dwHoverTime;\n\t\t\n\t\tpublic TRACKMOUSEEVENT(wnd w, uint flags, int hoverTime = 0) {\n\t\t\tcbSize = sizeof(TRACKMOUSEEVENT);\n\t\t\thwndTrack = w;\n\t\t\tdwFlags = flags;\n\t\t\tdwHoverTime = hoverTime;\n\t\t}\n\t}\n\t\n\t[DllImport(\"comctl32.dll\", EntryPoint = \"_TrackMouseEvent\")]\n\tinternal static extern bool TrackMouseEvent(ref TRACKMOUSEEVENT lpEventTrack);\n\t\n\t/// <summary>\n\t/// Calls <c>TrackMouseEvent</c> with <c>TME_LEAVE</c>.\n\t/// </summary>\n\t/// <param name=\"w\"></param>\n\t/// <param name=\"track\"><c>true</c> to start, <c>false</c> to cancel.</param>\n\tinternal static bool TrackMouseLeave(wnd w, bool track) {\n\t\tvar t = new TRACKMOUSEEVENT(w, track ? TME_LEAVE : TME_LEAVE | TME_CANCEL);\n\t\treturn TrackMouseEvent(ref t);\n\t}\n\t\n\t[DllImport(\"comctl32.dll\", EntryPoint = \"#380\")]\n\tinternal static extern int LoadIconMetric(IntPtr hinst, nint pszName, int lims, out IntPtr phico);\n\t\n\t[DllImport(\"comctl32.dll\", EntryPoint = \"#410\")]\n\tinternal static extern bool SetWindowSubclass(wnd hWnd, SUBCLASSPROC pfnSubclass, nint uIdSubclass, nint dwRefData = 0);\n\t\n\tinternal delegate nint SUBCLASSPROC(wnd hWnd, int uMsg, nint wParam, nint lParam, nint uIdSubclass, nint dwRefData);\n\t\n\t[DllImport(\"comctl32.dll\", EntryPoint = \"#413\")]\n\tinternal static extern nint DefSubclassProc(wnd hWnd, int uMsg, nint wParam, nint lParam);\n\t\n\t[DllImport(\"comctl32.dll\", EntryPoint = \"#412\")]\n\tinternal static extern bool RemoveWindowSubclass(wnd hWnd, SUBCLASSPROC pfnSubclass, nint uIdSubclass);\n\t\n\t\n\t\n\t\n\t#endregion\n\t\n\t#region oleaut32\n\t\n\t[DllImport(\"oleaut32.dll\", EntryPoint = \"#6\")]\n\tinternal static extern void SysFreeString(char* bstrString);\n\t\n\t[DllImport(\"oleaut32.dll\", EntryPoint = \"#7\")]\n\tinternal static extern int SysStringLen(char* pbstr);\n\t\n\t[DllImport(\"oleaut32.dll\", EntryPoint = \"#4\")]\n\tinternal static extern BSTR SysAllocStringLen(string strIn, int len);\n\t\n\t[DllImport(\"oleaut32.dll\", EntryPoint = \"#2\")]\n\tinternal static extern BSTR SysAllocString(char* psz);\n\t\n\t[DllImport(\"oleaut32.dll\", EntryPoint = \"#147\")]\n\tinternal static extern int VariantChangeTypeEx(ref VARIANT pvargDest, in VARIANT pvarSrc, uint lcid, ushort wFlags, VARENUM vt);\n\t\n\t[DllImport(\"oleaut32.dll\", EntryPoint = \"#9\")]\n\tinternal static extern int VariantClear(ref VARIANT pvarg);\n\t\n\t[DllImport(\"oleaut32.dll\", EntryPoint = \"#35\")]\n\tinternal static extern int GetActiveObject(in Guid rclsid, IntPtr pvReserved, [MarshalAs(UnmanagedType.IUnknown)] out object ppunk);\n\t\n\t\n\t\n\t\n\t#endregion\n\t\n\t#region ole32\n\t\n\t[DllImport(\"ole32.dll\")]\n\tinternal static extern int CoCreateInstance(in Guid rclsid, nint pUnkOuter, uint dwClsContext, in Guid riid, [MarshalAs(UnmanagedType.IUnknown)] out object ppv);\n\t\n\t[DllImport(\"ole32.dll\")]\n\tinternal static extern int PropVariantClear(ref PROPVARIANT pvar);\n\t\n\t//internal enum APTTYPE\n\t//{\n\t//\tAPTTYPE_CURRENT = -1,\n\t//\tAPTTYPE_STA,\n\t//\tAPTTYPE_MTA,\n\t//\tAPTTYPE_NA,\n\t//\tAPTTYPE_MAINSTA\n\t//}\n\t\n\t//[DllImport(\"ole32.dll\")]\n\t//internal static extern int CoGetApartmentType(out APTTYPE pAptType, out int pAptQualifier);\n\t\n\t[DllImport(\"ole32.dll\")]\n\tinternal static extern int OleInitialize(IntPtr pvReserved);\n\t\n\t[DllImport(\"ole32.dll\")]\n\tinternal static extern void OleUninitialize();\n\t\n\t[ComImport, Guid(\"00000122-0000-0000-C000-000000000046\"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\n\tinternal interface IDropTarget {\n\t\tvoid DragEnter(System.Runtime.InteropServices.ComTypes.IDataObject d, int grfKeyState, POINT pt, ref int effect);\n\t\tvoid DragOver(int grfKeyState, POINT pt, ref int effect);\n\t\tvoid DragLeave();\n\t\tvoid Drop(System.Runtime.InteropServices.ComTypes.IDataObject d, int grfKeyState, POINT pt, ref int effect);\n\t}\n\t\n\t[DllImport(\"ole32.dll\")]\n\tinternal static extern int RegisterDragDrop(wnd hwnd, IDropTarget pDropTarget);\n\t\n\t[DllImport(\"ole32.dll\")]\n\tinternal static extern int RevokeDragDrop(wnd hwnd);\n\t\n\t[DllImport(\"ole32.dll\")]\n\tinternal static extern void ReleaseStgMedium(ref System.Runtime.InteropServices.ComTypes.STGMEDIUM medium);\n\t\n\t[DllImport(\"ole32.dll\")]\n\tinternal static extern int CLSIDFromProgID(string lpszProgID, out Guid lpclsid);\n\t\n\t#endregion\n\t\n\t#region oleacc\n\t\n\t[DllImport(\"oleacc.dll\")]\n\tinternal static extern int AccessibleObjectFromWindow(wnd hwnd, EObjid dwId, in Guid riid, [MarshalAs(UnmanagedType.IUnknown)] out object o);\n\t\n\t[DllImport(\"oleacc.dll\")]\n\tinternal static extern int AccessibleObjectFromEvent(wnd hwnd, EObjid dwObjectId, int dwChildId, out IntPtr ppacc, out VARIANT pvarChild);\n\t\n\t[DllImport(\"oleacc.dll\")]\n\tinternal static extern Handle_ GetProcessHandleFromHwnd(wnd hwnd);\n\t\n\t[DllImport(\"oleacc.dll\")]\n\tinternal static extern int CreateStdAccessibleObject(wnd hwnd, EObjid idObject, in Guid riid, out IAccessible ppvObject);\n\t\n\t[ComImport, Guid(\"618736e0-3c3d-11cf-810c-00aa00389b71\"), InterfaceType(ComInterfaceType.InterfaceIsDual)]\n\tinternal interface IAccessible {\n\t\tIAccessible get_accParent();\n\t\tint get_accChildCount();\n\t\t[PreserveSig] int get_accChild(VarInt varChild, [MarshalAs(UnmanagedType.IDispatch)] out object ppdispChild);\n\t\tstring get_accName(VarInt varChild);\n\t\tstring get_accValue(VarInt varChild);\n\t\tstring get_accDescription(VarInt varChild);\n\t\tVarInt get_accRole(VarInt varChild);\n\t\tVarInt get_accState(VarInt varChild);\n\t\tstring get_accHelp(VarInt varChild);\n\t\tint get_accHelpTopic(out string pszHelpFile, VarInt varChild);\n\t\tstring get_accKeyboardShortcut(VarInt varChild);\n\t\tobject get_accFocus();\n\t\tobject get_accSelection();\n\t\tstring get_accDefaultAction(VarInt varChild);\n\t\tvoid accSelect(ESelect flagsSelect, VarInt varChild);\n\t\tvoid accLocation(out int pxLeft, out int pyTop, out int pcxWidth, out int pcyHeight, VarInt varChild);\n\t\tobject accNavigate(NAVDIR navDir, VarInt varStart);\n\t\tVarInt accHitTest(int xLeft, int yTop);\n\t\tvoid accDoDefaultAction(VarInt varChild);\n\t\tvoid put_accName(VarInt varChild, string szName);\n\t\tvoid put_accValue(VarInt varChild, string szValue);\n\t\t\n\t\t//NOTE: although some members are obsolete or useless, don't use default implementation, because then exception at run time.\n\t}\n\t\n#pragma warning disable 169\n\tinternal struct VarInt {\n\t\tushort _vt, _1, _2, _3;\n\t\tnint _int, _4;\n\t\tpublic static implicit operator VarInt(int i) => new VarInt { _vt = 3, _int = i + 1 };\n\t\tpublic static implicit operator int(VarInt v) {\n\t\t\tif (v._vt == 3) return (int)v._int - 1;\n\t\t\tDebug_.Print($\"VarInt vt={v._vt}, value={v._int}\");\n\t\t\tthrow new ArgumentException();\n\t\t}\n\t}\n#pragma warning restore 169\n\t\n\tinternal enum NAVDIR { UP = 1, DOWN, LEFT, RIGHT, NEXT, PREVIOUS, FIRSTCHILD, LASTCHILD }\n\t\n\t\n\t#endregion\n\t\n\t#region msvcrt\n\t\n\t//don't use, because for eg \"0xffffffff\" returns int.Max instead of -1.\n\t//[DllImport(\"msvcrt.dll\", EntryPoint = \"wcstol\", CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern int strtoi(char* s, char** endPtr = null, int radix = 0);\n\t\n\t//don't use the u API because they return 1 if the value is too big and the string contains '-'.\n\t//[DllImport(\"msvcrt.dll\", EntryPoint = \"wcstoul\", CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern uint strtoui(char* s, char** endPtr = null, int radix = 0);\n\t\n\t[DllImport(\"msvcrt.dll\", EntryPoint = \"_wcstoi64\", CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern long strtoi64(char* s, char** endPtr = null, int radix = 0);\n\t//info: ntdll also has wcstol, wcstoul, _wcstoui64, but not _wcstoi64.\n\t\n\t//[DllImport(\"msvcrt.dll\", EntryPoint = \"_wcstoui64\", CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern ulong strtoui64(char* s, char** endPtr = null, int radix = 0);\n\t\n\t[DllImport(\"msvcrt.dll\", EntryPoint = \"_strtoi64\", CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern long strtoi64(byte* s, byte** endPtr = null, int radix = 0);\n\t\n\t//not used\n\t//[DllImport(\"msvcrt.dll\", EntryPoint = \"_strtoui64\", CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern long strtoui64(byte* s, byte** endPtr = null, int radix = 0);\n\t\n\t//This is used when working with char*. With C# strings use ExtString.ToInt32 etc.\n\tinternal static int strtoi(char* s, char** endPtr = null, int radix = 0) {\n\t\treturn (int)strtoi64(s, endPtr, radix);\n\t}\n\t\n\t//This is used with UTF-8 text.\n\tinternal static int strtoi(byte* s, byte** endPtr = null, int radix = 0) {\n\t\treturn (int)strtoi64(s, endPtr, radix);\n\t}\n\t\n#if false //not used, because we have ExtString.ToInt32 etc, which has no overflow problems. But it supports only decimal and hex, not any radix.\n\t/// <summary>\n\t/// Converts part of string to <c>int</c>.\n\t/// Returns the <c>int</c> value.\n\t/// Returns 0 if the string is <c>null</c>, <c>\"\"</c> or does not begin with a number; then <i>numberEndIndex</i> will be = <i>startIndex</i>.\n\t/// </summary>\n\t/// <param name=\"s\">String.</param>\n\t/// <param name=\"startIndex\">Offset in string where to start parsing.</param>\n\t/// <param name=\"numberEndIndex\">Receives offset in string where the number part ends.</param>\n\t/// <param name=\"radix\">If 0, parses the string as hexadecimal number if starts with <c>\"0x\"</c>, as octal if starts with <c>\"0\"</c>, else as decimal. Else it can be 2 to 36. Examples: 10 - parse as decimal (don't support <c>\"0x\"</c> etc); 16 - as hexadecimal (eg returns 26 if string is <c>\"1A\"</c> or <c>\"0x1A\"</c>); 2 - as binary (eg returns 5 if string is <c>\"101\"</c>).</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\"><i>startIndex</i> is invalid.</exception>\n\tinternal static int strtoi(string s, int startIndex, out int numberEndIndex, int radix = 0) {\n\t\tint R = 0, len = s == null ? 0 : s.Length - startIndex;\n\t\tif(len < 0) throw new ArgumentOutOfRangeException(\"startIndex\");\n\t\tif(len != 0)\n\t\t\tfixed (char* p = s) {\n\t\t\t\tchar* t = p + startIndex, e = t;\n\t\t\t\tR = strtoi(t, &e, radix);\n\t\t\t\tlen = (int)(e - t);\n\t\t\t}\n\t\tnumberEndIndex = startIndex + len;\n\t\treturn R;\n\t}\n\n\t/// <summary>\n\t/// Converts part of string to <c>long</c>.\n\t/// Returns the <c>long</c> value.\n\t/// Returns 0 if the string is <c>null</c>, <c>\"\"</c> or does not begin with a number; then <i>numberEndIndex</i> will be = <i>startIndex</i>.\n\t/// </summary>\n\t/// <param name=\"s\">String.</param>\n\t/// <param name=\"startIndex\">Offset in string where to start parsing.</param>\n\t/// <param name=\"numberEndIndex\">Receives offset in string where the number part ends.</param>\n\t/// <param name=\"radix\">If 0, parses the string as hexadecimal number if starts with <c>\"0x\"</c>, as octal if starts with <c>\"0\"</c>, else as decimal. Else it can be 2 to 36. Examples: 10 - parse as decimal (don't support <c>\"0x\"</c> etc); 16 - as hexadecimal (eg returns 26 if string is <c>\"1A\"</c> or <c>\"0x1A\"</c>); 2 - as binary (eg returns 5 if string is <c>\"101\"</c>).</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\"><i>startIndex</i> is invalid.</exception>\n\tinternal static long strtoi64(string s, int startIndex, out int numberEndIndex, int radix = 0) {\n\t\tlong R = 0;\n\t\tint len = s == null ? 0 : s.Length - startIndex;\n\t\tif(len < 0) throw new ArgumentOutOfRangeException(\"startIndex\");\n\t\tif(len != 0)\n\t\t\tfixed (char* p = s) {\n\t\t\t\tchar* t = p + startIndex, e = t;\n\t\t\t\tR = strtoi64(t, &e, radix);\n\t\t\t\tlen = (int)(e - t);\n\t\t\t}\n\t\tnumberEndIndex = startIndex + len;\n\t\treturn R;\n\t}\n\n\tinternal static int strtoi(string s, int startIndex = 0, int radix = 0) {\n\t\treturn strtoi(s, startIndex, out _, radix);\n\t}\n\n\tinternal static long strtoi64(string s, int startIndex = 0, int radix = 0) {\n\t\treturn strtoi64(s, startIndex, out _, radix);\n\t}\n#endif\n\t\n\t[DllImport(\"msvcrt.dll\", CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern char* _ltoa(int value, byte* s, int radix);\n\t\n\t[DllImport(\"msvcrt.dll\", CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern void* memmove(void* to, void* from, nint n);\n\t\n\t//[DllImport(\"msvcrt.dll\", CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern void* memset(void* ptr, int ch, nint n);\n\t\n\t[DllImport(\"msvcrt.dll\", CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern int memcmp(void* p1, void* p2, nint count);\n\t\n\t\n\t\n\t#endregion\n\t\n\t#region winmm\n\t\n\t[DllImport(\"winmm.dll\")]\n\tinternal static extern uint timeBeginPeriod(uint uPeriod);\n\t\n\t[DllImport(\"winmm.dll\")]\n\tinternal static extern uint timeEndPeriod(uint uPeriod);\n\t\n\t[DllImport(\"winmm.dll\")]\n\tinternal static extern uint waveOutSetVolume(IntPtr hwo, uint dwVolume);\n\t\n\t[DllImport(\"winmm.dll\")]\n\tinternal static extern uint waveOutGetVolume(IntPtr hwo, out uint pdwVolume);\n\t\n\t[DllImport(\"winmm.dll\", EntryPoint = \"PlaySoundW\")]\n\tinternal static extern bool PlaySound(string pszSound, IntPtr hmod, uint fdwSound);\n\t\n\tinternal const uint SND_FILENAME = 0x20000;\n\tinternal const uint SND_NODEFAULT = 0x2;\n\tinternal const uint SND_SYSTEM = 0x200000;\n\tinternal const uint SND_ASYNC = 0x1;\n\tinternal const uint SND_ALIAS = 0x10000;\n\tinternal const uint SND_APPLICATION = 0x80;\n\t\n\t[DllImport(\"user32.dll\")]\n\tinternal static extern bool MessageBeep(uint uType);\n\t\n\t[DllImport(\"kernel32.dll\")]\n\tinternal static extern bool Beep(int dwFreq, int dwDuration);\n\t\n\t#endregion\n\t\n\t#region dwmapi\n\t\n\tinternal enum DWMWA {\n\t\tNCRENDERING_ENABLED = 1,\n\t\tNCRENDERING_POLICY,\n\t\tTRANSITIONS_FORCEDISABLED,\n\t\tALLOW_NCPAINT,\n\t\tCAPTION_BUTTON_BOUNDS,\n\t\tNONCLIENT_RTL_LAYOUT,\n\t\tFORCE_ICONIC_REPRESENTATION,\n\t\tFLIP3D_POLICY,\n\t\tEXTENDED_FRAME_BOUNDS,\n\t\tHAS_ICONIC_BITMAP,\n\t\tDISALLOW_PEEK,\n\t\tEXCLUDED_FROM_PEEK,\n\t\tCLOAK,\n\t\tCLOAKED,\n\t\tFREEZE_REPRESENTATION,\n\t\tPASSIVE_UPDATE_MODE,\n\t}\n\t\n\t[DllImport(\"dwmapi.dll\")]\n\tinternal static extern int DwmGetWindowAttribute(wnd hwnd, DWMWA dwAttribute, void* pvAttribute, int cbAttribute);\n\t\n\t//[DllImport(\"dwmapi.dll\")]\n\t//internal static extern int DwmSetWindowAttribute(wnd hwnd, DWMWA dwAttribute, void* pvAttribute, int cbAttribute);\n\t\n\t//[DllImport(\"dwmapi.dll\")]\n\t//internal static extern int DwmRegisterThumbnail(wnd hwndDestination, wnd hwndSource, out IntPtr phThumbnailId);\n\t\n\t//[DllImport(\"dwmapi.dll\")]\n\t//internal static extern int DwmUnregisterThumbnail(IntPtr hThumbnailId);\n\t\n\t//[DllImport(\"dwmapi.dll\")]\n\t//internal static extern int DwmQueryThumbnailSourceSize(IntPtr hThumbnail, out SIZE pSize);\n\t\n\t//[DllImport(\"dwmapi.dll\")]\n\t//internal static extern int DwmUpdateThumbnailProperties(IntPtr hThumbnailId, in DWM_THUMBNAIL_PROPERTIES ptnProperties);\n\t\n\t//[StructLayout(LayoutKind.Sequential, Pack = 1)]\n\t//internal struct DWM_THUMBNAIL_PROPERTIES {\n\t//\tpublic uint dwFlags;\n\t//\tpublic RECT rcDestination;\n\t//\tpublic RECT rcSource;\n\t//\tpublic byte opacity;\n\t//\tpublic bool fVisible;\n\t//\tpublic bool fSourceClientAreaOnly;\n\t//}\n\t\n\t//internal const uint DWM_TNP_SOURCECLIENTAREAONLY = 0x10;\n\t//internal const uint DWM_TNP_RECTDESTINATION = 0x1;\n\t//internal const uint DWM_TNP_VISIBLE = 0x8;\n\t//internal const uint DWM_TNP_RECTSOURCE = 0x2;\n\t\n\t\n\t\n\t\n\t#endregion\n\t\n\t#region uxtheme\n\t\n\t[DllImport(\"uxtheme.dll\")]\n\tstatic extern IntPtr OpenThemeData(wnd hwnd, string pszClassList);\n\t\n\t[DllImport(\"uxtheme.dll\")]\n\tstatic extern IntPtr OpenThemeDataForDpi(wnd hwnd, string pszClassList, int dpi);\n\t\n\tinternal static IntPtr OpenThemeData(wnd hwnd, string classList, int dpi) {\n\t\tif (osVersion.minWin10_1703) return OpenThemeDataForDpi(hwnd, classList, dpi);\n\t\treturn OpenThemeData(hwnd, classList); //never mind: bad on Win8.1 non-primary screen with different DPI than primary if hwnd==0\n\t}\n\t\n\t[DllImport(\"uxtheme.dll\")]\n\tinternal static extern int CloseThemeData(IntPtr hTheme);\n\t\n\t[DllImport(\"uxtheme.dll\")]\n\tinternal static extern int GetThemePartSize(IntPtr hTheme, IntPtr hdc, int iPartId, int iStateId, RECT* prc, THEMESIZE eSize, out SIZE psz);\n\tinternal enum THEMESIZE {\n\t\tTS_MIN,\n\t\tTS_TRUE,\n\t\tTS_DRAW\n\t}\n\t\n\t[DllImport(\"uxtheme.dll\")]\n\tinternal static extern int DrawThemeBackground(IntPtr hTheme, IntPtr hdc, int iPartId, int iStateId, in RECT pRect, RECT* pClipRect = null);\n\t\n\t//[DllImport(\"uxtheme.dll\")]\n\t//internal static extern int SetWindowTheme(wnd hwnd, string pszSubAppName, string pszSubIdList);\n\t\n\t[DllImport(\"uxtheme.dll\")]\n\tinternal static extern int GetThemeSysColor(IntPtr hTheme, int iColorId);\n\t\n\t[DllImport(\"uxtheme.dll\")]\n\tinternal static extern IntPtr GetThemeSysColorBrush(IntPtr hTheme, int iColorId);\n\t\n\t[DllImport(\"uxtheme.dll\")]\n\tinternal static extern int BufferedPaintInit();\n\t\n\t//[DllImport(\"uxtheme.dll\")]\n\t//internal static extern int BufferedPaintUnInit();\n\t\n\t[DllImport(\"uxtheme.dll\")]\n\tinternal static extern IntPtr BeginBufferedPaint(IntPtr hdcTarget, in RECT prcTarget, BP_BUFFERFORMAT dwFormat, ref BP_PAINTPARAMS pPaintParams, out IntPtr phdc);\n\t\n\t[DllImport(\"uxtheme.dll\")]\n\tinternal static extern int EndBufferedPaint(IntPtr hBufferedPaint, bool fUpdateTarget);\n\t\n\tinternal enum BP_BUFFERFORMAT {\n\t\tBPBF_COMPATIBLEBITMAP,\n\t\tBPBF_DIB,\n\t\tBPBF_TOPDOWNDIB,\n\t\tBPBF_TOPDOWNMONODIB\n\t}\n\tinternal struct BP_PAINTPARAMS {\n\t\tpublic int cbSize;\n\t\tpublic uint dwFlags;\n\t\tpublic RECT* prcExclude;\n\t\t//public BLENDFUNCTION* pBlendFunction;\n\t\tuint pBlendFunction;\n\t}\n\t//internal struct BLENDFUNCTION {\n\t//\tpublic byte BlendOp;\n\t//\tpublic byte BlendFlags;\n\t//\tpublic byte SourceConstantAlpha;\n\t//\tpublic byte AlphaFormat;\n\t//}\n\t\n\t\n\t#endregion\n\t\n\t#region wtsapi\n\t\n\t[DllImport(\"wtsapi32.dll\", SetLastError = true)]\n\tinternal static extern bool WTSTerminateProcess(IntPtr hServer, int ProcessId, int ExitCode);\n\t\n\t[DllImport(\"wtsapi32.dll\")]\n\tinternal static extern void WTSFreeMemory(void* pMemory);\n\t\n\t[DllImport(\"wtsapi32.dll\", EntryPoint = \"WTSQuerySessionInformationW\", SetLastError = true)]\n\tstatic extern bool _WTSQuerySessionInformation(nint hServer, int SessionId, WTS_INFO_CLASS WTSInfoClass, out char* ppBuffer, out int pBytesReturned);\n\t\n\tinternal enum WTS_INFO_CLASS {\n\t\tWTSInitialProgram,\n\t\tWTSApplicationName,\n\t\tWTSWorkingDirectory,\n\t\tWTSOEMId,\n\t\tWTSSessionId,\n\t\tWTSUserName,\n\t\tWTSWinStationName,\n\t\tWTSDomainName,\n\t\tWTSConnectState,\n\t\tWTSClientBuildNumber,\n\t\tWTSClientName,\n\t\tWTSClientDirectory,\n\t\tWTSClientProductId,\n\t\tWTSClientHardwareId,\n\t\tWTSClientAddress,\n\t\tWTSClientDisplay,\n\t\tWTSClientProtocolType,\n\t\tWTSIdleTime,\n\t\tWTSLogonTime,\n\t\tWTSIncomingBytes,\n\t\tWTSOutgoingBytes,\n\t\tWTSIncomingFrames,\n\t\tWTSOutgoingFrames,\n\t\tWTSClientInfo,\n\t\tWTSSessionInfo,\n\t\tWTSSessionInfoEx,\n\t\tWTSConfigInfo,\n\t\tWTSValidationInfo,\n\t\tWTSSessionAddressV4,\n\t\tWTSIsRemoteSession\n\t}\n\t\n\tinternal static unsafe bool WTSQuerySessionInformation(int sessionId, Api.WTS_INFO_CLASS what, out string r) {\n\t\tif (_WTSQuerySessionInformation(0, -1, what, out char* p, out int len)) {\n\t\t\tr = len > 2 ? new(p, 0, len / 2 - 1) : null;\n\t\t\tApi.WTSFreeMemory(p);\n\t\t\treturn r != null;\n\t\t}\n\t\tr = null;\n\t\treturn false;\n\t}\n\t\n\tinternal static unsafe bool WTSQuerySessionInformation<T>(int sessionId, Api.WTS_INFO_CLASS what, out T r) where T : unmanaged {\n\t\tif (_WTSQuerySessionInformation(0, -1, what, out char* p, out int len)) {\n\t\t\tDebug_.PrintIf(len != sizeof(T), len.ToS());\n\t\t\tr = len == sizeof(T) ? *(T*)p : default;\n\t\t\tApi.WTSFreeMemory(p);\n\t\t\treturn len == sizeof(T);\n\t\t}\n\t\tr = default;\n\t\treturn false;\n\t}\n\t\n\t[DllImport(\"wtsapi32.dll\")]\n\tinternal static extern bool WTSGetChildSessionId(out int pSessionId);\n\t\n\t\n\t#endregion\n\t\n\t#region ntdll\n\t\n\tinternal struct RTL_OSVERSIONINFOW {\n\t\tpublic int dwOSVersionInfoSize;\n\t\tpublic uint dwMajorVersion;\n\t\tpublic uint dwMinorVersion;\n\t\tpublic uint dwBuildNumber;\n\t\tpublic uint dwPlatformId;\n\t\tpublic fixed char szCSDVersion[128];\n\t}\n\t\n\t[DllImport(\"ntdll.dll\", ExactSpelling = true)]\n\tinternal static extern int RtlGetVersion(ref RTL_OSVERSIONINFOW lpVersionInformation);\n\t\n\t[DllImport(\"ntdll.dll\")]\n\tinternal static extern uint NtQueryTimerResolution(out uint maxi, out uint mini, out uint current);\n\t//info: NtSetTimerResolution can set min 0.5 ms resolution. timeBeginPeriod min 1.\n\t\n\t[DllImport(\"ntdll.dll\")]\n\tinternal static extern void MD5Init(out Hash.MD5Context context);\n\t\n\t[DllImport(\"ntdll.dll\")]\n\tinternal static extern void MD5Update(ref Hash.MD5Context context, void* data, int dataLen);\n\t\n\t[DllImport(\"ntdll.dll\")]\n\tinternal static extern void MD5Final(ref Hash.MD5Context context);\n\t\n#pragma warning disable 169\n\t[DllImport(\"ntdll.dll\")]\n\tinternal static extern int NtQueryInformationProcess(IntPtr ProcessHandle, int ProcessInformationClass, void* ProcessInformation, int ProcessInformationLength, out int ReturnLength);\n\t\n\t//all structs are same in 64-bit and 32-bit processes\n\t\n\tinternal record struct PROCESS_BASIC_INFORMATION {\n\t\tlong Reserved1;\n\t\tpublic long PebBaseAddress;\n\t\tlong Reserved2_0, Reserved2_1;\n\t\tpublic long UniqueProcessId;\n\t\tpublic long ParentProcessId;\n\t}\n\t\n\tinternal struct RTL_USER_PROCESS_PARAMETERS {\n\t\tfixed byte Reserved[96];\n\t\tpublic UNICODE_STRING ImagePathName;\n\t\tpublic UNICODE_STRING CommandLine;\n\t}\n\t\n\tinternal struct UNICODE_STRING {\n\t\tpublic ushort Length;\n\t\tpublic ushort MaximumLength;\n\t\tpublic char* Buffer;\n\t}\n\t\n\tinternal struct SYSTEM_PROCESS_INFORMATION {\n\t\tinternal uint NextEntryOffset;\n\t\tinternal uint NumberOfThreads;\n\t\tlong SpareLi1;\n\t\tlong SpareLi2;\n\t\tlong SpareLi3;\n\t\tinternal long CreateTime;\n\t\tinternal long UserTime;\n\t\tinternal long KernelTime;\n\t\t\n\t\tinternal ushort NameLength;   // UNICODE_STRING   \n\t\tinternal ushort MaximumNameLength;\n\t\tinternal IntPtr NamePtr;     // This will point into the data block returned by NtQuerySystemInformation\n\t\t\n\t\tinternal int BasePriority;\n\t\tinternal IntPtr UniqueProcessId;\n\t\tinternal IntPtr InheritedFromUniqueProcessId;\n\t\tinternal uint HandleCount;\n\t\tinternal uint SessionId;\n\t\t\n\t\t//unused members\n\t}\n#pragma warning restore 169\n\t\n\tinternal const int STATUS_INFO_LENGTH_MISMATCH = unchecked((int)0xC0000004);\n\t\n\t[DllImport(\"ntdll.dll\")]\n\tinternal static extern int NtQuerySystemInformation(int five, SYSTEM_PROCESS_INFORMATION* SystemInformation, int SystemInformationLength, out int ReturnLength);\n\t\n\t[DllImport(\"ntdll.dll\")]\n\tinternal static extern int NtSuspendProcess(IntPtr handle);\n\t\n\t[DllImport(\"ntdll.dll\")]\n\tinternal static extern int NtResumeProcess(IntPtr handle);\n\t\n\t\n\t\n\t\n\t#endregion\n\t\n\t#region other\n\t\n\t[DllImport(\"msi.dll\", EntryPoint = \"#217\")]\n\tinternal static extern int MsiGetShortcutTarget(string szShortcutPath, char* szProductCode, char* szFeatureId, char* szComponentCode);\n\t\n\t[DllImport(\"msi.dll\", EntryPoint = \"#173\")]\n\tinternal static extern int MsiGetComponentPath(char* szProduct, char* szComponent, char* lpPathBuf, ref int pcchBuf);\n\t\n\t[DllImport(\"powrprof.dll\")]\n\tinternal static extern byte SetSuspendState(byte bHibernate, byte bForce, byte bWakeupEventsDisabled);\n\t\n\t[DllImport(\"powrprof.dll\")]\n\tinternal static extern byte IsPwrSuspendAllowed();\n\t\n\t//[DllImport(\"urlmon.dll\")]\n\t//internal static extern int FindMimeFromData(IntPtr pBC, string pwzUrl, byte[] pBuffer, int cbSize, string pwzMimeProposed, uint dwMimeFlags, out string ppwzMimeOut, uint dwReserved);\n\t\n\t#endregion\n\t\n\t#region struct\n\t\n\tinternal struct NEWHEADER {\n\t\tpublic ushort wReserved;\n\t\tpublic ushort wResType;\n\t\tpublic ushort wResCount;\n\t};\n\t\n\t[StructLayout(LayoutKind.Sequential, Pack = 2)]\n\tinternal struct ICONDIRENTRY {\n\t\tpublic byte bWidth;\n\t\tpublic byte bHeight;\n\t\tpublic byte bColorCount;\n\t\tpublic byte bReserved;\n\t\tpublic ushort wPlanes;\n\t\tpublic ushort wBitCount;\n\t\tpublic int dwBytesInRes;\n\t\tpublic int dwImageOffset;\n\t};\n\t\n\t#endregion\n}\n"
  },
  {
    "path": "Au/Api/Api^kernel32.cs",
    "content": "namespace Au.Types;\n\nstatic unsafe partial class Api {\n\t[DllImport(\"kernel32.dll\", SetLastError = true)] //note: without `SetLastError = true` Marshal.GetLastWin32Error is unaware that we set the code to 0 etc and returns old captured error code\n\tinternal static extern void SetLastError(int errCode);\n\t\n\tinternal const uint FORMAT_MESSAGE_FROM_SYSTEM = 0x1000;\n\tinternal const uint FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x100;\n\tinternal const uint FORMAT_MESSAGE_IGNORE_INSERTS = 0x200;\n\t\n\t[DllImport(\"kernel32.dll\", EntryPoint = \"FormatMessageW\")]\n\tinternal static extern int FormatMessage(uint dwFlags, IntPtr lpSource, int code, uint dwLanguageId, char** lpBuffer, int nSize, IntPtr Arguments);\n\t\n\t[DllImport(\"kernel32.dll\", EntryPoint = \"SetDllDirectoryW\", SetLastError = true)]\n\tinternal static extern bool SetDllDirectory(string lpPathName);\n\t\n\t[SuppressGCTransition] //makes slightly faster. Not faster with [MethodImpl].\n\t[DllImport(\"kernel32.dll\")]\n\tinternal static extern bool QueryPerformanceCounter(out long lpPerformanceCount);\n\t\n\t[DllImport(\"kernel32.dll\")]\n\tinternal static extern bool QueryPerformanceFrequency(out long lpFrequency);\n\t\n\t[DllImport(\"kernel32.dll\")]\n\tinternal static extern bool QueryUnbiasedInterruptTime(out long UnbiasedTime);\n\t\n\t[DllImport(\"kernel32.dll\")]\n\tinternal static extern long GetTickCount64();\n\t\n\t[DllImport(\"kernel32.dll\")]\n\tinternal static extern int GetTickCount();\n\t\n\tinternal struct SYSTEMTIME {\n\t\tpublic ushort wYear;\n\t\tpublic ushort wMonth;\n\t\tpublic ushort wDayOfWeek;\n\t\tpublic ushort wDay;\n\t\tpublic ushort wHour;\n\t\tpublic ushort wMinute;\n\t\tpublic ushort wSecond;\n\t\tpublic ushort wMilliseconds;\n\t}\n\t\n\t[DllImport(\"kernel32.dll\")]\n\tinternal static extern void GetLocalTime(out SYSTEMTIME lpSystemTime);\n\t\n\t[DllImport(\"kernel32.dll\")]\n\tinternal static extern void GetSystemTimeAsFileTime(out long lpSystemTimeAsFileTime);\n\t\n\t//[DllImport(\"kernel32.dll\", SetLastError = true)]\n\t//internal static extern bool GetThreadTimes(IntPtr hThread, out long lpCreationTime, out long lpExitTime, out long lpKernelTime, out long lpUserTime);\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern bool GetProcessTimes(IntPtr hProcess, out long lpCreationTime, out long lpExitTime, out long lpKernelTime, out long lpUserTime);\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern bool GetThreadTimes(IntPtr hThread, out long lpCreationTime, out long lpExitTime, out long lpKernelTime, out long lpUserTime);\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern int GetThreadDescription(IntPtr hThread, out char* ppszThreadDescription);\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern bool QueryProcessCycleTime(nint ProcessHandle, out long CycleTime);\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern bool QueryThreadCycleTime(nint ThreadHandle, out long CycleTime);\n\t\n\t[DllImport(\"kernel32.dll\")]\n\tinternal static extern bool QueryIdleProcessorCycleTimeEx(ushort Group, ref int BufferLength, long* ProcessorIdleCycleTime);\n\t\n\t[DllImport(\"kernel32.dll\")]\n\tinternal static extern ushort GetActiveProcessorGroupCount();\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern int GetThreadPriority(nint hThread);\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern bool SetThreadPriority(nint hThread, int nPriority);\n\t\n\tinternal const int THREAD_PRIORITY_TIME_CRITICAL = 15;\n\t\n\t[DllImport(\"kernel32.dll\", EntryPoint = \"CreateEventW\", SetLastError = true)]\n\tinternal static extern IntPtr CreateEvent2(IntPtr lpEventAttributes, bool bManualReset, bool bInitialState, string lpName);\n\t\n\tinternal static Handle_ CreateEvent(bool bManualReset)\n\t\t=> new(CreateEvent2(default, bManualReset, false, null));\n\t\n\t[DllImport(\"kernel32.dll\", EntryPoint = \"OpenEventW\", SetLastError = true)]\n\tinternal static extern IntPtr OpenEvent(uint dwDesiredAccess, bool bInheritHandle, string lpName);\n\t\n\tinternal const uint EVENT_MODIFY_STATE = 0x2;\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern bool SetEvent(IntPtr hEvent);\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern bool ResetEvent(IntPtr hEvent);\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern int WaitForSingleObject(IntPtr hHandle, int dwMilliseconds);\n\t\n\t//[DllImport(\"kernel32.dll\")]\n\t//internal static extern int SignalObjectAndWait(IntPtr hObjectToSignal, IntPtr hObjectToWaitOn, int dwMilliseconds, bool bAlertable);\n\t//note: don't know why, this often is much slower than setevent/waitforsingleobject.\n\t\n\t[DllImport(\"kernel32.dll\")] //note: no SetLastError = true\n\tinternal static extern bool CloseHandle(IntPtr hObject);\n\t\n\t//currently not used\n\t//[DllImport(\"kernel32.dll\")] //note: no SetLastError = true\n\t//internal static extern bool CloseHandle(HandleRef hObject);\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern bool SetHandleInformation(IntPtr hObject, uint dwMask, uint dwFlags);\n\t\n\t[DllImport(\"kernel32.dll\", EntryPoint = \"CreateMutexW\", SetLastError = true)]\n\tinternal static extern nint CreateMutex(SECURITY_ATTRIBUTES lpMutexAttributes, bool bInitialOwner, string lpName);\n\t\n\t[DllImport(\"kernel32.dll\", EntryPoint = \"OpenMutexW\", SetLastError = true)]\n\tinternal static extern nint OpenMutex(uint dwDesiredAccess, bool bInheritHandle, string lpName);\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern bool ReleaseMutex(nint hMutex);\n\t\n\t/// <summary>\n\t/// Note: use only for private threads. Not everything works like with <c>Thread.Start</c>. For example .NET does not auto-release COM objects when thread ends.\n\t/// </summary>\n\t/// <param name=\"lpStartAddress\"><c>[UnmanagedCallersOnly]</c></param>\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern IntPtr CreateThread(nint lpThreadAttributes, nint dwStackSize, delegate* unmanaged<GCHandle, uint> lpStartAddress, GCHandle lpParameter, uint dwCreationFlags, out int lpThreadId);\n\t\n\t[DllImport(\"kernel32.dll\")]\n\tinternal static extern IntPtr GetCurrentThread();\n\t\n\t[SuppressGCTransition]\n\t[DllImport(\"kernel32.dll\")]\n\tinternal static extern int GetCurrentThreadId();\n\t\n\t[DllImport(\"kernel32.dll\")]\n\tinternal static extern IntPtr GetCurrentProcess();\n\t\n\t[SuppressGCTransition]\n\t[DllImport(\"kernel32.dll\")]\n\tinternal static extern int GetCurrentProcessId();\n\t\n\t[DllImport(\"kernel32.dll\", EntryPoint = \"QueryFullProcessImageNameW\", SetLastError = true)]\n\tinternal static extern bool QueryFullProcessImageName(IntPtr hProcess, bool nativeFormat, char* lpExeName, ref int lpdwSize);\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern bool TerminateProcess(IntPtr hProcess, int uExitCode);\n\t\n\t[DllImport(\"kernel32.dll\")]\n\tinternal static extern void ExitProcess(int uExitCode);\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern bool IsWow64Process(IntPtr hProcess, out bool Wow64Process);\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern Handle_ CreateFileMapping(IntPtr hFile, SECURITY_ATTRIBUTES lpFileMappingAttributes, uint flProtect, uint dwMaximumSizeHigh, uint dwMaximumSizeLow, string lpName);\n\t\n\t[DllImport(\"kernel32.dll\", EntryPoint = \"OpenFileMappingW\", SetLastError = true)]\n\tinternal static extern Handle_ OpenFileMapping(uint dwDesiredAccess, bool bInheritHandle, string lpName);\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern void* MapViewOfFile(IntPtr hFileMappingObject, uint dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow, nint dwNumberOfBytesToMap);\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern bool UnmapViewOfFile(void* lpBaseAddress);\n\t\n\t[DllImport(\"kernel32.dll\", EntryPoint = \"GetModuleHandleW\", SetLastError = true)]\n\tinternal static extern IntPtr GetModuleHandle(string name);\n\t\n\t//Better use NativeLibrary.TryLoad.\n\t//Dlls loaded by LoadLibrary don't find other used dlls from the same directory if it's not the app directory. Need LoadLibraryEx with LOAD_WITH_ALTERED_SEARCH_PATH, and probably NativeLibrary.TryLoad uses it.\n\t//[DllImport(\"kernel32.dll\", EntryPoint = \"LoadLibraryW\", SetLastError = true)]\n\t//internal static extern IntPtr LoadLibrary(string lpLibFileName);\n\t\n\tinternal const uint LOAD_LIBRARY_AS_DATAFILE = 0x2;\n\t\n\t[DllImport(\"kernel32.dll\", EntryPoint = \"LoadLibraryExW\", SetLastError = true)]\n\tinternal static extern IntPtr LoadLibraryEx(string lpLibFileName, IntPtr hFile, uint dwFlags);\n\t\n\t[DllImport(\"kernel32.dll\")]\n\tinternal static extern bool FreeLibrary(IntPtr hLibModule);\n\t\n\t[DllImport(\"kernel32.dll\", BestFitMapping = false, SetLastError = true)]\n\tinternal static extern IntPtr GetProcAddress(IntPtr hModule, [MarshalAs(UnmanagedType.LPStr)] string lpProcName);\n\t\n\tinternal const uint PROCESS_TERMINATE = 0x0001;\n\tinternal const uint PROCESS_CREATE_THREAD = 0x0002;\n\tinternal const uint PROCESS_SET_SESSIONID = 0x0004;\n\tinternal const uint PROCESS_VM_OPERATION = 0x0008;\n\tinternal const uint PROCESS_VM_READ = 0x0010;\n\tinternal const uint PROCESS_VM_WRITE = 0x0020;\n\tinternal const uint PROCESS_DUP_HANDLE = 0x0040;\n\tinternal const uint PROCESS_CREATE_PROCESS = 0x0080;\n\tinternal const uint PROCESS_SET_QUOTA = 0x0100;\n\tinternal const uint PROCESS_SET_INFORMATION = 0x0200;\n\tinternal const uint PROCESS_QUERY_INFORMATION = 0x0400;\n\tinternal const uint PROCESS_SUSPEND_RESUME = 0x0800;\n\tinternal const uint PROCESS_QUERY_LIMITED_INFORMATION = 0x1000;\n\tinternal const uint PROCESS_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFFF;\n\tinternal const uint DELETE = 0x00010000;\n\tinternal const uint READ_CONTROL = 0x00020000;\n\tinternal const uint WRITE_DAC = 0x00040000;\n\tinternal const uint WRITE_OWNER = 0x00080000;\n\tinternal const uint SYNCHRONIZE = 0x00100000;\n\tinternal const uint STANDARD_RIGHTS_REQUIRED = 0x000F0000;\n\tinternal const uint STANDARD_RIGHTS_READ = READ_CONTROL;\n\tinternal const uint STANDARD_RIGHTS_WRITE = READ_CONTROL;\n\tinternal const uint STANDARD_RIGHTS_EXECUTE = READ_CONTROL;\n\tinternal const uint STANDARD_RIGHTS_ALL = 0x001F0000;\n\tinternal const uint TIMER_MODIFY_STATE = 0x2;\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern Handle_ OpenProcess(uint dwDesiredAccess, bool bInheritHandle, int dwProcessId);\n\t\n\t[DllImport(\"kernel32.dll\", EntryPoint = \"GetFullPathNameW\", SetLastError = true)]\n\tstatic extern int _GetFullPathName(string lpFileName, int nBufferLength, char* lpBuffer, char** lpFilePart);\n\t\n\t/// <summary>\n\t/// Calls API <c>GetFullPathName</c>.\n\t/// Returns <c>false</c> if failed or result is same; then <i>r</i> is <i>s</i>.\n\t/// <i>r</i> can be same variable as <i>s</i>.\n\t/// </summary>\n\t[SkipLocalsInit]\n\tinternal static bool GetFullPathName(string s, out string r) {\n\t\tusing FastBuffer<char> b = new();\n\t\tfor (; ; ) if (b.GetString(_GetFullPathName(s, b.n, b.p, null), out r, 0, s)) return (object)r != s;\n\t}\n\t\n\t[DllImport(\"kernel32.dll\", EntryPoint = \"GetLongPathNameW\", SetLastError = true)]\n\tstatic extern int _GetLongPathName(string lpszShortPath, char* lpszLongPath, int cchBuffer);\n\t\n\t/// <summary>\n\t/// Calls API <c>GetFullPathName</c>.\n\t/// Returns <c>false</c> if failed or result is same; then <i>r</i> is <i>s</i>.\n\t/// <i>r</i> can be same variable as <i>s</i>.\n\t/// </summary>\n\t[SkipLocalsInit]\n\tinternal static bool GetLongPathName(string s, out string r) {\n\t\tusing FastBuffer<char> b = new();\n\t\tfor (; ; ) if (b.GetString(_GetLongPathName(s, b.p, b.n), out r, 0, s)) return (object)r != s;\n\t}\n\t\n\t[DllImport(\"kernel32.dll\", EntryPoint = \"GetFinalPathNameByHandleW\", SetLastError = true)]\n\tstatic extern int _GetFinalPathNameByHandle(IntPtr hFile, char* lpszFilePath, int cchFilePath, uint dwFlags);\n\t\n\t[SkipLocalsInit]\n\tinternal static bool GetFinalPathNameByHandle(IntPtr h, out string r, uint dwFlags = 0) {\n\t\tusing FastBuffer<char> b = new();\n\t\tfor (; ; ) {\n\t\t\tg1: if (b.GetString(_GetFinalPathNameByHandle(h, b.p, b.n, dwFlags), out r, 0)) {\n\t\t\t\tif (r != null) return r.Length > 0;\n\t\t\t\tif (0u != (dwFlags & 3u)) { dwFlags &= ~3u; goto g1; } //if VOLUME_NAME_GUID, fails if network path\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t}\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern bool ProcessIdToSessionId(int dwProcessId, out int pSessionId);\n\t\n\tinternal const uint PAGE_NOACCESS = 0x1;\n\tinternal const uint PAGE_READONLY = 0x2;\n\tinternal const uint PAGE_READWRITE = 0x4;\n\tinternal const uint PAGE_WRITECOPY = 0x8;\n\tinternal const uint PAGE_EXECUTE = 0x10;\n\tinternal const uint PAGE_EXECUTE_READ = 0x20;\n\tinternal const uint PAGE_EXECUTE_READWRITE = 0x40;\n\tinternal const uint PAGE_EXECUTE_WRITECOPY = 0x80;\n\tinternal const uint PAGE_GUARD = 0x100;\n\tinternal const uint PAGE_NOCACHE = 0x200;\n\tinternal const uint PAGE_WRITECOMBINE = 0x400;\n\t\n\tinternal const uint MEM_COMMIT = 0x1000;\n\tinternal const uint MEM_RESERVE = 0x2000;\n\tinternal const uint MEM_DECOMMIT = 0x4000;\n\tinternal const uint MEM_RELEASE = 0x8000;\n\tinternal const uint MEM_RESET = 0x80000;\n\tinternal const uint MEM_TOP_DOWN = 0x100000;\n\tinternal const uint MEM_WRITE_WATCH = 0x200000;\n\tinternal const uint MEM_PHYSICAL = 0x400000;\n\tinternal const uint MEM_RESET_UNDO = 0x1000000;\n\tinternal const uint MEM_LARGE_PAGES = 0x20000000;\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern void* VirtualAlloc(void* lpAddress, nint dwSize, uint flAllocationType = MEM_COMMIT | MEM_RESERVE, uint flProtect = PAGE_READWRITE);\n\t//note: with PAGE_EXECUTE_READWRITE writing to the memory first time is much slower.\n\t\n\t[DllImport(\"kernel32.dll\")]\n\tinternal static extern bool VirtualFree(void* lpAddress, nint dwSize = 0, uint dwFreeType = MEM_RELEASE);\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern IntPtr VirtualAllocEx(HandleRef hProcess, IntPtr lpAddress, nint dwSize, uint flAllocationType = MEM_COMMIT | MEM_RESERVE, uint flProtect = PAGE_EXECUTE_READWRITE);\n\t\n\t[DllImport(\"kernel32.dll\")]\n\tinternal static extern bool VirtualFreeEx(HandleRef hProcess, IntPtr lpAddress, nint dwSize = 0, uint dwFreeType = MEM_RELEASE);\n\t\n\t[DllImport(\"kernel32.dll\", EntryPoint = \"GetFileAttributesW\", SetLastError = true)]\n\tinternal static extern FileAttributes GetFileAttributes(string lpFileName);\n\t\n\t[DllImport(\"kernel32.dll\", EntryPoint = \"SetFileAttributesW\", SetLastError = true)]\n\tinternal static extern bool SetFileAttributes(string lpFileName, FileAttributes dwFileAttributes);\n\t\n\t[StructLayout(LayoutKind.Sequential, Pack = 4)]\n\tinternal struct WIN32_FILE_ATTRIBUTE_DATA {\n\t\tpublic FileAttributes dwFileAttributes;\n\t\tpublic FILETIME ftCreationTime;\n\t\tpublic FILETIME ftLastAccessTime;\n\t\tpublic FILETIME ftLastWriteTime;\n\t\tpublic uint nFileSizeHigh;\n\t\tpublic uint nFileSizeLow;\n\t\tpublic long Size => (long)nFileSizeHigh << 32 | nFileSizeLow;\n\t}\n\t\n\t[DllImport(\"kernel32.dll\", EntryPoint = \"GetFileAttributesExW\", SetLastError = true)]\n\tinternal static extern bool GetFileAttributesEx(string lpFileName, int zero, out WIN32_FILE_ATTRIBUTE_DATA lpFileInformation);\n\t\n\t[DllImport(\"kernel32.dll\", EntryPoint = \"SearchPathW\", SetLastError = true)]\n\tstatic extern int _SearchPath(string lpPath, string lpFileName, string lpExtension, int nBufferLength, char* lpBuffer, char** lpFilePart);\n\t\n\t/// <summary>\n\t/// Calls API <c>SearchPath</c>. Returns full path, or <c>null</c> if not found.\n\t/// </summary>\n\t/// <param name=\"lpPath\">Parent directory or <c>null</c>.</param>\n\t/// <param name=\"lpFileName\"></param>\n\t/// <param name=\"lpExtension\"><c>null</c> or extension like <c>\".ext\"</c> to add if <i>lpFileName</i> is without extension.</param>\n\t[SkipLocalsInit]\n\tinternal static string SearchPath(string lpPath, string lpFileName, string lpExtension = null) {\n\t\tusing FastBuffer<char> b = new();\n\t\tfor (; ; ) if (b.GetString(_SearchPath(lpPath, lpFileName, lpExtension, b.n, b.p, null), out var s)) return s;\n\t}\n\t\n\tinternal const uint BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE = 0x1;\n\tinternal const uint BASE_SEARCH_PATH_DISABLE_SAFE_SEARCHMODE = 0x10000;\n\tinternal const uint BASE_SEARCH_PATH_PERMANENT = 0x8000;\n\t\n\t[DllImport(\"kernel32.dll\")]\n\tinternal static extern bool SetSearchPathMode(uint Flags);\n\t\n\tinternal const uint SEM_FAILCRITICALERRORS = 0x1;\n\tinternal const uint SEM_NOGPFAULTERRORBOX = 0x2;\n\t\n\t[DllImport(\"kernel32.dll\")]\n\tinternal static extern uint SetErrorMode(uint uMode);\n\t\n\t//[DllImport(\"kernel32.dll\")]\n\t//internal static extern uint GetErrorMode();\n\t\n\t//[DllImport(\"kernel32.dll\", SetLastError = true)]\n\t//internal static extern IntPtr LocalAlloc(uint uFlags, nint uBytes);\n\t\n\t[DllImport(\"kernel32.dll\")]\n\tinternal static extern IntPtr LocalFree(void* hMem);\n\t\n\t[DllImport(\"kernel32.dll\", EntryPoint = \"lstrcpynW\")]\n\tinternal static extern char* lstrcpyn(char* sTo, string sFrom, int sToBufferLength);\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern bool Wow64DisableWow64FsRedirection(out IntPtr OldValue);\n\t\n\t[DllImport(\"kernel32.dll\")]\n\tinternal static extern bool Wow64RevertWow64FsRedirection(IntPtr OlValue);\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern bool GetExitCodeProcess(IntPtr hProcess, out int lpExitCode);\n\t\n\t[DllImport(\"kernel32.dll\")]\n\tinternal static extern IntPtr GetProcessHeap();\n\t[DllImport(\"kernel32.dll\")]\n\tinternal static extern void* HeapAlloc(IntPtr hHeap, uint dwFlags, nint dwBytes);\n\t[DllImport(\"kernel32.dll\")]\n\tinternal static extern void* HeapReAlloc(IntPtr hHeap, uint dwFlags, void* lpMem, nint dwBytes);\n\t[DllImport(\"kernel32.dll\")]\n\tinternal static extern bool HeapFree(IntPtr hHeap, uint dwFlags, void* lpMem);\n\t\n\tinternal const int CP_UTF8 = 65001;\n\tinternal const uint MB_ERR_INVALID_CHARS = 0x8;\n\tinternal const uint WC_ERR_INVALID_CHARS = 0x80;\n\t\n\t[DllImport(\"kernel32.dll\")]\n\tinternal static extern int MultiByteToWideChar(uint CodePage, uint dwFlags, byte* lpMultiByteStr, int cbMultiByte, char* lpWideCharStr, int cchWideChar);\n\t\n\t[DllImport(\"kernel32.dll\")]\n\tinternal static extern int WideCharToMultiByte(uint CodePage, uint dwFlags, char* lpWideCharStr, int cchWideChar, byte* lpMultiByteStr, int cbMultiByte, IntPtr lpDefaultChar = default, int* lpUsedDefaultChar = null);\n\t\n\t[Flags]\n\tinternal enum Access : uint { }\n\t\n\tinternal const Access FILE_READ_DATA = (Access)0x1;\n\tinternal const Access FILE_LIST_DIRECTORY = (Access)0x1;\n\tinternal const Access FILE_WRITE_DATA = (Access)0x2;\n\tinternal const Access FILE_ADD_FILE = (Access)0x2;\n\tinternal const Access FILE_APPEND_DATA = (Access)0x4;\n\tinternal const Access FILE_ADD_SUBDIRECTORY = (Access)0x4;\n\tinternal const Access FILE_CREATE_PIPE_INSTANCE = (Access)0x4;\n\tinternal const Access FILE_READ_EA = (Access)0x8;\n\tinternal const Access FILE_WRITE_EA = (Access)0x10;\n\tinternal const Access FILE_EXECUTE = (Access)0x20;\n\tinternal const Access FILE_TRAVERSE = (Access)0x20;\n\tinternal const Access FILE_DELETE_CHILD = (Access)0x40;\n\tinternal const Access FILE_READ_ATTRIBUTES = (Access)0x80;\n\tinternal const Access FILE_WRITE_ATTRIBUTES = (Access)0x100;\n\tinternal const Access FILE_ALL_ACCESS = (Access)0x1F01FF;\n\tinternal const Access FILE_GENERIC_READ = (Access)0x120089;\n\tinternal const Access FILE_GENERIC_WRITE = (Access)0x120116;\n\tinternal const Access FILE_GENERIC_EXECUTE = (Access)0x1200A0;\n\t\n\tinternal const Access GENERIC_READ = (Access)0x80000000;\n\tinternal const Access GENERIC_WRITE = (Access)0x40000000;\n\t\n\tinternal enum CfCreation { }\n\t\n\tinternal const CfCreation CREATE_NEW = (CfCreation)1;\n\tinternal const CfCreation CREATE_ALWAYS = (CfCreation)2;\n\tinternal const CfCreation OPEN_EXISTING = (CfCreation)3;\n\tinternal const CfCreation OPEN_ALWAYS = (CfCreation)4;\n\tinternal const CfCreation TRUNCATE_EXISTING = (CfCreation)5;\n\t\n\t[Flags]\n\tinternal enum CfShare : uint { }\n\t\n\tinternal const CfShare FILE_SHARE_READ = (CfShare)0x1;\n\tinternal const CfShare FILE_SHARE_WRITE = (CfShare)0x2;\n\tinternal const CfShare FILE_SHARE_DELETE = (CfShare)0x4;\n\tinternal const CfShare FILE_SHARE_ALL = (CfShare)0x7;\n\t\n\t//the commented out attributes are not documented for CreateFile\n\tinternal const uint FILE_ATTRIBUTE_READONLY = 0x1;\n\tinternal const uint FILE_ATTRIBUTE_HIDDEN = 0x2;\n\tinternal const uint FILE_ATTRIBUTE_SYSTEM = 0x4;\n\t//internal const uint FILE_ATTRIBUTE_DIRECTORY = 0x10;\n\tinternal const uint FILE_ATTRIBUTE_ARCHIVE = 0x20;\n\t//internal const uint FILE_ATTRIBUTE_DEVICE = 0x40;\n\tinternal const uint FILE_ATTRIBUTE_NORMAL = 0x80;\n\tinternal const uint FILE_ATTRIBUTE_TEMPORARY = 0x100;\n\t//internal const uint FILE_ATTRIBUTE_SPARSE_FILE = 0x200;\n\t//internal const uint FILE_ATTRIBUTE_REPARSE_POINT = 0x400;\n\t//internal const uint FILE_ATTRIBUTE_COMPRESSED = 0x800;\n\tinternal const uint FILE_ATTRIBUTE_OFFLINE = 0x1000;\n\t//internal const uint FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x2000;\n\tinternal const uint FILE_ATTRIBUTE_ENCRYPTED = 0x4000;\n\t//internal const uint FILE_ATTRIBUTE_INTEGRITY_STREAM = 0x8000;\n\t//internal const uint FILE_ATTRIBUTE_VIRTUAL = 0x10000;\n\t//internal const uint FILE_ATTRIBUTE_NO_SCRUB_DATA = 0x20000;\n\t\n\tinternal const uint FILE_FLAG_WRITE_THROUGH = 0x80000000;\n\tinternal const uint FILE_FLAG_OVERLAPPED = 0x40000000;\n\tinternal const uint FILE_FLAG_NO_BUFFERING = 0x20000000;\n\tinternal const uint FILE_FLAG_RANDOM_ACCESS = 0x10000000;\n\tinternal const uint FILE_FLAG_SEQUENTIAL_SCAN = 0x8000000;\n\tinternal const uint FILE_FLAG_DELETE_ON_CLOSE = 0x4000000;\n\tinternal const uint FILE_FLAG_BACKUP_SEMANTICS = 0x2000000;\n\tinternal const uint FILE_FLAG_POSIX_SEMANTICS = 0x1000000;\n\tinternal const uint FILE_FLAG_SESSION_AWARE = 0x800000;\n\tinternal const uint FILE_FLAG_OPEN_REPARSE_POINT = 0x200000;\n\tinternal const uint FILE_FLAG_OPEN_NO_RECALL = 0x100000;\n\tinternal const uint FILE_FLAG_FIRST_PIPE_INSTANCE = 0x80000;\n\tinternal const uint FILE_FLAG_OPEN_REQUIRING_OPLOCK = 0x40000;\n\t\n\t[DllImport(\"kernel32.dll\", EntryPoint = \"CreateFileW\", SetLastError = true)]\n\tstatic extern IntPtr _CreateFile(string lpFileName, Access dwDesiredAccess, CfShare dwShareMode, SECURITY_ATTRIBUTES lpSecurityAttributes, CfCreation creationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile);\n\t\n\tinternal static Handle_ CreateFile(string lpFileName, Access dwDesiredAccess, CfShare dwShareMode, CfCreation creationDisposition, uint dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL, IntPtr hTemplateFile = default, SECURITY_ATTRIBUTES lpSecurityAttributes = null)\n\t\t=> new Handle_(_CreateFile(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, creationDisposition, dwFlagsAndAttributes, hTemplateFile));\n\t//note: cannot return Handle_ directly from API because returns -1 if failed. The ctor then makes 0.\n\t\n\t//note: not using parameter types SECURITY_ATTRIBUTES and OVERLAPPED* because it makes JIT-compiling much slower in some time-critical places.\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern bool ReadFile(IntPtr hFile, void* lpBuffer, int nBytesToRead, out int nBytesRead, void* lpOverlapped = null);\n\t\n\tinternal static bool ReadFileArr(IntPtr hFile, byte[] a, out int nBytesRead, void* lpOverlapped = null) {\n\t\tfixed (byte* p = a) return ReadFile(hFile, p, a.Length, out nBytesRead, lpOverlapped);\n\t}\n\t\n\tinternal static bool ReadFileArr(IntPtr hFile, out byte[] a, int size, out int nBytesRead, void* lpOverlapped = null) {\n\t\ta = new byte[size];\n\t\treturn ReadFileArr(hFile, a, out nBytesRead, lpOverlapped);\n\t}\n\t\n\t//internal static byte[] ReadFileArr(string file) {\n\t//\tusing var h = CreateFile(file, Api.GENERIC_READ, FILE_SHARE_ALL, OPEN_EXISTING);\n\t//\tif (h.Is0 || !GetFileSizeEx(h, out long size) || !ReadFileArr(h, out var a, (int)size, out _)) throw new AuException(0);\n\t//\treturn a;\n\t//}\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern bool WriteFile(IntPtr hFile, void* lpBuffer, int nBytesToWrite, out int nBytesWritten, void* lpOverlapped = null);\n\t//note: lpNumberOfBytesWritten can be null only if lpOverlapped is not null.\n\t\n\t//note: don't use overloads, because we Jit_.Compile(\"WriteFile\").\n\tinternal static bool WriteFile2(IntPtr hFile, RByte a, out int nBytesWritten) {\n\t\tfixed (byte* p = a) return WriteFile(hFile, p, a.Length, out nBytesWritten);\n\t}\n\t\n\t//internal static bool WriteFile2(IntPtr hFile, RByte a, out int nBytesWritten, void* lpOverlapped)\n\t//{\n\t//\tfixed (byte* p = a) return WriteFile(hFile, p, a.Length, out nBytesWritten, lpOverlapped);\n\t//}\n\t\n\tinternal struct OVERLAPPED {\n\t\tnint _1, _2;\n\t\tint _3, _4;\n\t\tpublic IntPtr hEvent;\n\t}\n\t\n\t[StructLayout(LayoutKind.Sequential, Pack = 4)]\n\tinternal struct BY_HANDLE_FILE_INFORMATION {\n\t\tpublic uint dwFileAttributes;\n\t\tpublic long ftCreationTime;\n\t\tpublic long ftLastAccessTime;\n\t\tpublic long ftLastWriteTime;\n\t\tpublic uint dwVolumeSerialNumber;\n\t\tuint _nFileSizeHigh;\n\t\tuint _nFileSizeLow;\n\t\tpublic uint nNumberOfLinks;\n\t\tuint _nFileIndexHigh;\n\t\tuint _nFileIndexLow;\n\t\t\n\t\tpublic long FileSize => (long)((ulong)_nFileSizeHigh << 32 | _nFileSizeLow);\n\t\t\n\t\tpublic long FileIndex => (long)((ulong)_nFileIndexHigh << 32 | _nFileIndexLow);\n\t}\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern bool GetFileInformationByHandle(IntPtr hFile, out BY_HANDLE_FILE_INFORMATION lpFileInformation);\n\t\n\t//internal enum FILE_INFO_BY_HANDLE_CLASS\n\t//{\n\t//\tFileBasicInfo,\n\t//\tFileStandardInfo,\n\t//\tFileNameInfo,\n\t//\tFileRenameInfo,\n\t//\tFileDispositionInfo,\n\t//\tFileAllocationInfo,\n\t//\tFileEndOfFileInfo,\n\t//\tFileStreamInfo,\n\t//\tFileCompressionInfo,\n\t//\tFileAttributeTagInfo,\n\t//\tFileIdBothDirectoryInfo,\n\t//\tFileIdBothDirectoryRestartInfo,\n\t//\tFileIoPriorityHintInfo,\n\t//\tFileRemoteProtocolInfo,\n\t//\tFileFullDirectoryInfo,\n\t//\tFileFullDirectoryRestartInfo,\n\t//\tFileStorageInfo,\n\t//\tFileAlignmentInfo,\n\t//\tFileIdInfo,\n\t//\tFileIdExtdDirectoryInfo,\n\t//\tFileIdExtdDirectoryRestartInfo,\n\t//\tMaximumFileInfoByHandleClass\n\t//}\n\t\n\t//internal struct FILE_BASIC_INFO\n\t//{\n\t//\tpublic long CreationTime;\n\t//\tpublic long LastAccessTime;\n\t//\tpublic long LastWriteTime;\n\t//\tpublic long ChangeTime;\n\t//\tpublic uint FileAttributes;\n\t//}\n\t\n\t//[DllImport(\"kernel32.dll\", SetLastError = true)]\n\t//internal static extern bool GetFileInformationByHandleEx(IntPtr hFile, int FileInformationClass, void* lpFileInformation, int dwBufferSize);\n\t\n\t//[DllImport(\"kernel32.dll\", SetLastError = true)]\n\t//internal static extern bool SetFileInformationByHandle(IntPtr hFile, int FileInformationClass, void* lpFileInformation, int dwBufferSize);\n\t\n\tinternal const int FILE_BEGIN = 0;\n\tinternal const int FILE_CURRENT = 1;\n\tinternal const int FILE_END = 2;\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern bool SetFilePointerEx(IntPtr hFile, long liDistanceToMove, long* lpNewFilePointer, int dwMoveMethod);\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern bool SetEndOfFile(IntPtr hFile);\n\t\n\t[DllImport(\"kernel32.dll\", EntryPoint = \"CreateMailslotW\", SetLastError = true)]\n\tstatic extern IntPtr _CreateMailslot(string lpName, uint nMaxMessageSize, int lReadTimeout, SECURITY_ATTRIBUTES lpSecurityAttributes);\n\t\n\tinternal static Handle_ CreateMailslot(string lpName, uint nMaxMessageSize, int lReadTimeout, SECURITY_ATTRIBUTES lpSecurityAttributes)\n\t\t=> new Handle_(_CreateMailslot(lpName, nMaxMessageSize, lReadTimeout, lpSecurityAttributes));\n\t//note: cannot return Handle_ directly from API because returns -1 if failed. The ctor then makes 0.\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern bool GetMailslotInfo(IntPtr hMailslot, uint* lpMaxMessageSize, out int lpNextSize, out int lpMessageCount, int* lpReadTimeout = null);\n\t\n\t[DllImport(\"kernel32.dll\")]\n\tinternal static extern int GetApplicationUserModelId(IntPtr hProcess, ref int AppModelIDLength, char* sbAppUserModelID);\n\t\n\t[DllImport(\"kernel32.dll\", EntryPoint = \"GetEnvironmentVariableW\", SetLastError = true)]\n\tstatic extern int _GetEnvironmentVariable(string lpName, char* lpBuffer, int nSize);\n\t\n\t/// <summary>\n\t/// Calls API <c>GetEnvironmentVariable</c>.\n\t/// Returns <c>null</c> if variable not found.\n\t/// Does not support <c>folders.X</c>.\n\t/// </summary>\n\t/// <param name=\"name\">Case-insensitive name. Without <c>%</c>.</param>\n\t[SkipLocalsInit]\n\tinternal static string GetEnvironmentVariable(string name) {\n\t\tusing FastBuffer<char> b = new();\n\t\tfor (; ; ) if (b.GetString(_GetEnvironmentVariable(name, b.p, b.n), out var s)) return s;\n\t}\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if environment variable exists.\n\t/// </summary>\n\tinternal static bool EnvironmentVariableExists(string name) => 0 != _GetEnvironmentVariable(name, null, 0);\n\t\n\t[DllImport(\"kernel32.dll\", EntryPoint = \"SetEnvironmentVariableW\", SetLastError = true)]\n\tinternal static extern bool SetEnvironmentVariable(string lpName, string lpValue);\n\t\n\t[DllImport(\"kernel32.dll\", EntryPoint = \"ExpandEnvironmentStringsW\")]\n\tstatic extern int _ExpandEnvironmentStrings(string lpSrc, char* lpDst, int nSize);\n\t\n\t/// <summary>\n\t/// Calls API <c>ExpandEnvironmentStrings</c>.\n\t/// Returns <c>false</c> if failed or result is same; then <i>r</i> is <i>s</i>.\n\t/// <i>r</i> can be same variable as <i>s</i>.\n\t/// </summary>\n\t[SkipLocalsInit]\n\tinternal static bool ExpandEnvironmentStrings(string s, out string r) {\n\t\tusing FastBuffer<char> b = new();\n\t\tfor (; ; ) if (b.GetString(_ExpandEnvironmentStrings(s, b.p, b.n), out r, BSFlags.ReturnsLengthWith0, s)) return (object)r != s;\n\t}\n\t\n\t[DllImport(\"kernel32.dll\", EntryPoint = \"GetEnvironmentStringsW\")]\n\tinternal static extern char* GetEnvironmentStrings();\n\t\n\t[DllImport(\"kernel32.dll\", EntryPoint = \"FreeEnvironmentStringsW\")]\n\tinternal static extern bool FreeEnvironmentStrings(char* penv);\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern int GetProcessId(IntPtr Process);\n\t\n\tinternal struct FILETIME {\n\t\tpublic uint dwLowDateTime;\n\t\tpublic uint dwHighDateTime;\n\t\t\n\t\tpublic static implicit operator long(FILETIME ft) => (long)((ulong)ft.dwHighDateTime << 32 | ft.dwLowDateTime);\n\t\tpublic static implicit operator FILETIME(long ft) => new() { dwHighDateTime = (uint)(ft >>> 32), dwLowDateTime = (uint)ft };\n\t}\n\t\n\tinternal struct WIN32_FIND_DATA {\n\t\tpublic FileAttributes dwFileAttributes;\n\t\tpublic FILETIME ftCreationTime;\n\t\tpublic FILETIME ftLastAccessTime;\n\t\tpublic FILETIME ftLastWriteTime;\n\t\tpublic uint nFileSizeHigh;\n\t\tpublic uint nFileSizeLow;\n\t\tpublic uint dwReserved0;\n\t\tpublic uint dwReserved1;\n\t\tpublic fixed char cFileName[260];\n\t\tpublic fixed char cAlternateFileName[14];\n\t\t\n\t\t/// <summary>\n\t\t/// Returns <c>cFileName</c> as string, or null if it's <c>\"..\"</c> or <c>\".\"</c>.\n\t\t/// </summary>\n\t\tpublic unsafe string Name {\n\t\t\tget {\n\t\t\t\tfixed (char* p = cFileName) {\n\t\t\t\t\tif (p[0] == '.') {\n\t\t\t\t\t\tif (p[1] == '\\0') return null;\n\t\t\t\t\t\tif (p[1] == '.' && p[2] == '\\0') return null;\n\t\t\t\t\t}\n\t\t\t\t\treturn new string(p);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Returns nonzero if this is a NTFS link: 1 symlink, 2 mount, 3 other.\n\t\t/// </summary>\n\t\tpublic int IsNtfsLink\n\t\t\t=> dwFileAttributes.Has(FileAttributes.ReparsePoint) && 0 != (dwReserved0 & 0x20000000)\n\t\t\t? dwReserved0 switch { 0xA000000C => 1, 0xA0000003 => 2, _ => 3 }\n\t\t\t: 0;\n\t}\n\t\n\t[DllImport(\"kernel32.dll\", EntryPoint = \"FindFirstFileW\", SetLastError = true)]\n\tinternal static extern IntPtr FindFirstFile(string lpFileName, out WIN32_FIND_DATA lpFindFileData);\n\t\n\t[DllImport(\"kernel32.dll\", EntryPoint = \"FindNextFileW\", SetLastError = true)]\n\tinternal static extern bool FindNextFile(IntPtr hFindFile, out WIN32_FIND_DATA lpFindFileData);\n\t\n\t[DllImport(\"kernel32.dll\")]\n\tinternal static extern bool FindClose(IntPtr hFindFile);\n\t\n#if TEST_FINDFIRSTFILEEX\n\tinternal enum FINDEX_INFO_LEVELS\n\t{\n\t\tFindExInfoStandard,\n\t\tFindExInfoBasic,\n\t\tFindExInfoMaxInfoLevel\n\t}\n\n\tinternal const uint FIND_FIRST_EX_LARGE_FETCH = 0x2;\n\n\t[DllImport(\"kernel32.dll\", EntryPoint = \"FindFirstFileExW\")]\n\tinternal static extern IntPtr FindFirstFileEx(string lpFileName, FINDEX_INFO_LEVELS fInfoLevelId, out WIN32_FIND_DATA lpFindFileData, int fSearchOp, IntPtr lpSearchFilter, uint dwAdditionalFlags);\n#endif\n\t\n\tinternal const uint MOVEFILE_REPLACE_EXISTING = 0x1;\n\tinternal const uint MOVEFILE_COPY_ALLOWED = 0x2;\n\tinternal const uint MOVEFILE_DELAY_UNTIL_REBOOT = 0x4;\n\tinternal const uint MOVEFILE_WRITE_THROUGH = 0x8;\n\tinternal const uint MOVEFILE_CREATE_HARDLINK = 0x10;\n\tinternal const uint MOVEFILE_FAIL_IF_NOT_TRACKABLE = 0x20;\n\t\n\t[DllImport(\"kernel32.dll\", EntryPoint = \"MoveFileExW\", SetLastError = true)]\n\tinternal static extern bool MoveFileEx(string lpExistingFileName, string lpNewFileName, uint dwFlags);\n\t\n\t//[DllImport(\"kernel32.dll\", EntryPoint = \"CopyFileW\", SetLastError = true)]\n\t//internal static extern bool CopyFile(string lpExistingFileName, string lpNewFileName, bool bFailIfExists);\n\t\n\tinternal const uint COPY_FILE_FAIL_IF_EXISTS = 0x1;\n\tinternal const uint COPY_FILE_RESTARTABLE = 0x2;\n\tinternal const uint COPY_FILE_OPEN_SOURCE_FOR_WRITE = 0x4;\n\tinternal const uint COPY_FILE_ALLOW_DECRYPTED_DESTINATION = 0x8;\n\tinternal const uint COPY_FILE_COPY_SYMLINK = 0x800;\n\tinternal const uint COPY_FILE_NO_BUFFERING = 0x1000;\n\t\n\t[DllImport(\"kernel32.dll\", EntryPoint = \"CopyFileExW\", SetLastError = true)]\n\tstatic extern bool CopyFileEx(string lpExistingFileName, string lpNewFileName, nint lpProgressRoutine, IntPtr lpData, int* pbCancel, uint dwCopyFlags);\n\t\n\tinternal static bool CopyFileEx(string lpExistingFileName, string lpNewFileName, uint dwCopyFlags) {\n\t\tif (!CopyFileEx(lpExistingFileName, lpNewFileName, 0, 0, null, dwCopyFlags)) return false;\n\t\t\n\t\t//Workaround for: when copying in Vmware virtual PC from host path like @\"\\\\vmware-host\\Shared Folders\\...\", adds Readonly attribute.\n\t\t//\tAlso GetFileAttributes[Ex] for the source adds Readonly. But FindFirstFile[Ex] doesn't.\n\t\tif (GetFileAttributes(lpNewFileName) is var a1 && a1.Has(FileAttributes.ReadOnly) && a1 != (FileAttributes)(-1)) {\n\t\t\tvar h = FindFirstFile(lpExistingFileName, out var d);\n\t\t\tif (h != -1) {\n\t\t\t\tFindClose(h);\n\t\t\t\tif (!d.dwFileAttributes.Has(FileAttributes.ReadOnly)) {\n\t\t\t\t\tDebug_.Print(\"Readonly attribute\");\n\t\t\t\t\tSetFileAttributes(lpNewFileName, d.dwFileAttributes);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\treturn true;\n\t}\n\t\n\t[DllImport(\"kernel32.dll\", EntryPoint = \"DeleteFileW\", SetLastError = true)]\n\tinternal static extern bool DeleteFile(string lpFileName);\n\t\n\t[DllImport(\"kernel32.dll\", EntryPoint = \"RemoveDirectoryW\", SetLastError = true)]\n\tinternal static extern bool RemoveDirectory(string lpPathName);\n\t\n\t[DllImport(\"kernel32.dll\", EntryPoint = \"CreateDirectoryW\", SetLastError = true)]\n\tinternal static extern bool CreateDirectory(string lpPathName, IntPtr lpSecurityAttributes); //ref SECURITY_ATTRIBUTES\n\t\n\t[DllImport(\"kernel32.dll\", EntryPoint = \"CreateDirectoryExW\", SetLastError = true)]\n\tinternal static extern bool CreateDirectoryEx(string lpTemplateDirectory, string lpNewDirectory, IntPtr lpSecurityAttributes); //ref SECURITY_ATTRIBUTES\n\t\n\t[DllImport(\"kernel32.dll\", EntryPoint = \"ReplaceFileW\", SetLastError = true)]\n\tinternal static extern bool ReplaceFile(string lpReplacedFileName, string lpReplacementFileName, string lpBackupFileName, uint dwReplaceFlags, IntPtr lpExclude = default, IntPtr lpReserved = default);\n\t\n\t[DllImport(\"kernel32.dll\", EntryPoint = \"GlobalAddAtomW\")]\n\tinternal static extern ushort GlobalAddAtom(string lpString);\n\t\n\t[DllImport(\"kernel32.dll\")]\n\tinternal static extern ushort GlobalDeleteAtom(ushort nAtom);\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern bool ReadProcessMemory(HandleRef hProcess, IntPtr lpBaseAddress, void* lpBuffer, nint nSize, nint* lpNumberOfBytesRead);\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern bool WriteProcessMemory(HandleRef hProcess, IntPtr lpBaseAddress, void* lpBuffer, nint nSize, nint* lpNumberOfBytesWritten);\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal extern static IntPtr CreateActCtx(in ACTCTX actctx);\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal extern static bool ActivateActCtx(IntPtr hActCtx, out IntPtr lpCookie);\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal extern static bool DeactivateActCtx(int dwFlags, IntPtr lpCookie);\n\t\n\t[DllImport(\"kernel32.dll\")]\n\tinternal static extern void ReleaseActCtx(IntPtr hActCtx);\n\t\n\tinternal const int ACTCTX_FLAG_RESOURCE_NAME_VALID = 0x8;\n\tinternal const int ACTCTX_FLAG_HMODULE_VALID = 0x80;\n\t\n\tinternal struct ACTCTX {\n\t\tpublic int cbSize;\n\t\tpublic uint dwFlags;\n\t\tpublic string lpSource;\n\t\tpublic ushort wProcessorArchitecture;\n\t\tpublic ushort wLangId;\n\t\tpublic IntPtr lpAssemblyDirectory;\n\t\tpublic IntPtr lpResourceName;\n\t\tpublic IntPtr lpApplicationName;\n\t\tpublic IntPtr hModule;\n\t}\n\t\n\t\n\t//internal const uint THREAD_TERMINATE = 0x1;\n\tinternal const uint THREAD_SUSPEND_RESUME = 0x2;\n\tinternal const uint THREAD_SET_CONTEXT = 0x10;\n\tinternal const uint THREAD_QUERY_LIMITED_INFORMATION = 0x800;\n\tinternal const uint THREAD_ALL_ACCESS = 0x1FFFFF;\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern Handle_ OpenThread(uint dwDesiredAccess, bool bInheritHandle, int dwThreadId);\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern int SuspendThread(IntPtr hThread);\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern uint ResumeThread(IntPtr hThread);\n\t\n\t//[DllImport(\"kernel32.dll\", SetLastError = true)]\n\t//internal static extern bool TerminateThread(IntPtr hThread, int dwExitCode);\n\t\n\tinternal const uint GMEM_FIXED = 0x0;\n\tinternal const uint GMEM_MOVEABLE = 0x2;\n\tinternal const uint GMEM_ZEROINIT = 0x40;\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern IntPtr GlobalAlloc(uint uFlags, nint dwBytes);\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern IntPtr GlobalFree(IntPtr hMem);\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern IntPtr GlobalLock(IntPtr hMem);\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern bool GlobalUnlock(IntPtr hMem);\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern nint GlobalSize(IntPtr hMem);\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern bool GetFileSizeEx(IntPtr hFile, out long lpFileSize);\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern int WaitForMultipleObjectsEx(int nCount, IntPtr* pHandles, bool bWaitAll, int dwMilliseconds, bool bAlertable);\n\t\n\t[DllImport(\"kernel32.dll\")]\n\tinternal static extern int SleepEx(int dwMilliseconds, bool bAlertable);\n\t\n\t[DllImport(\"kernel32.dll\", EntryPoint = \"GetStartupInfoW\")]\n\tinternal static extern void GetStartupInfo(out STARTUPINFO lpStartupInfo);\n\t\n\tinternal struct STARTUPINFO {\n\t\tpublic int cb;\n\t\tpublic IntPtr lpReserved;\n\t\tpublic char* lpDesktop;\n\t\tpublic char* lpTitle;\n\t\tpublic int dwX;\n\t\tpublic int dwY;\n\t\tpublic int dwXSize;\n\t\tpublic int dwYSize;\n\t\tpublic int dwXCountChars;\n\t\tpublic int dwYCountChars;\n\t\tpublic uint dwFillAttribute;\n\t\tpublic uint dwFlags;\n\t\tpublic ushort wShowWindow;\n\t\tpublic ushort cbReserved2;\n\t\tpublic IntPtr lpReserved2;\n\t\tpublic IntPtr hStdInput;\n\t\tpublic IntPtr hStdOutput;\n\t\tpublic IntPtr hStdError;\n\t}\n\t\n\tinternal struct STARTUPINFOEX {\n\t\tpublic STARTUPINFO StartupInfo;\n\t\tpublic IntPtr lpAttributeList;\n\t}\n\t\n\tinternal struct PROCESS_INFORMATION : IDisposable {\n\t\tpublic Handle_ hProcess;\n\t\tpublic Handle_ hThread;\n\t\tpublic int dwProcessId;\n\t\tpublic int dwThreadId;\n\t\t\n\t\tpublic void Dispose() {\n\t\t\thThread.Dispose();\n\t\t\thProcess.Dispose();\n\t\t}\n\t}\n\t\n\t//CreateProcess flags\n\tinternal const uint CREATE_SUSPENDED = 0x4;\n\tinternal const uint CREATE_NEW_CONSOLE = 0x10;\n\tinternal const uint CREATE_UNICODE_ENVIRONMENT = 0x400;\n\tinternal const uint EXTENDED_STARTUPINFO_PRESENT = 0x80000;\n\t//STARTUPINFO flags\n\tinternal const uint STARTF_USESHOWWINDOW = 0x1;\n\tinternal const uint STARTF_FORCEOFFFEEDBACK = 0x80;\n\tinternal const uint STARTF_USESTDHANDLES = 0x100;\n\t\n\t[DllImport(\"kernel32.dll\", EntryPoint = \"CreateProcessW\", SetLastError = true)]\n\tinternal static extern bool CreateProcess(string lpApplicationName, char[] lpCommandLine, SECURITY_ATTRIBUTES lpProcessAttributes, SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandles, uint dwCreationFlags, string lpEnvironment, string lpCurrentDirectory, in STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);\n\t\n\t[DllImport(\"advapi32.dll\", EntryPoint = \"CreateProcessAsUserW\", SetLastError = true)]\n\tinternal static extern bool CreateProcessAsUser(IntPtr hToken, string lpApplicationName, char[] lpCommandLine, SECURITY_ATTRIBUTES lpProcessAttributes, SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandles, uint dwCreationFlags, string lpEnvironment, string lpCurrentDirectory, in STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);\n\t\n\t[DllImport(\"kernel32.dll\", EntryPoint = \"CreateWaitableTimerW\", SetLastError = true)]\n\tinternal static extern Handle_ CreateWaitableTimer(SECURITY_ATTRIBUTES lpTimerAttributes, bool bManualReset, string lpTimerName);\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern bool SetWaitableTimer(IntPtr hTimer, ref long lpDueTime, int lPeriod = 0, IntPtr pfnCompletionRoutine = default, IntPtr lpArgToCompletionRoutine = default, bool fResume = false);\n\t\n\t[DllImport(\"kernel32.dll\", EntryPoint = \"OpenWaitableTimerW\", SetLastError = true)]\n\tinternal static extern Handle_ OpenWaitableTimer(uint dwDesiredAccess, bool bInheritHandle, string lpTimerName);\n\t\n\t[DllImport(\"kernel32.dll\", EntryPoint = \"GetModuleFileNameW\", SetLastError = true)]\n\tinternal static extern int GetModuleFileName(IntPtr hModule, char* lpFilename, int nSize);\n\t\n\tinternal const uint PIPE_ACCESS_INBOUND = 0x1;\n\tinternal const uint PIPE_ACCESS_OUTBOUND = 0x2;\n\tinternal const uint PIPE_ACCESS_DUPLEX = 0x3;\n\tinternal const uint PIPE_TYPE_MESSAGE = 0x4;\n\tinternal const uint PIPE_READMODE_MESSAGE = 0x2;\n\tinternal const uint PIPE_REJECT_REMOTE_CLIENTS = 0x8;\n\t\n\t[DllImport(\"kernel32.dll\", EntryPoint = \"CreateNamedPipeW\", SetLastError = true)]\n\tstatic extern IntPtr _CreateNamedPipe(string lpName, uint dwOpenMode, uint dwPipeMode, uint nMaxInstances, uint nOutBufferSize, uint nInBufferSize, uint nDefaultTimeOut, SECURITY_ATTRIBUTES lpSecurityAttributes);\n\t\n\tinternal static Handle_ CreateNamedPipe(string lpName, uint dwOpenMode, uint dwPipeMode, uint nMaxInstances, uint nOutBufferSize, uint nInBufferSize, uint nDefaultTimeOut, SECURITY_ATTRIBUTES lpSecurityAttributes)\n\t\t=> new Handle_(_CreateNamedPipe(lpName, dwOpenMode, dwPipeMode, nMaxInstances, nOutBufferSize, nInBufferSize, nDefaultTimeOut, lpSecurityAttributes));\n\t//note: cannot return Handle_ directly from API because returns -1 if failed. The ctor then makes 0.\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern bool CreatePipe(out Handle_ hReadPipe, out Handle_ hWritePipe, SECURITY_ATTRIBUTES lpPipeAttributes, uint nSize);\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern bool ConnectNamedPipe(IntPtr hNamedPipe, OVERLAPPED* lpOverlapped);\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern bool DisconnectNamedPipe(IntPtr hNamedPipe);\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern bool GetOverlappedResult(IntPtr hFile, ref OVERLAPPED lpOverlapped, out int lpNumberOfBytesTransferred, bool bWait);\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern bool CancelIo(IntPtr hFile);\n\t\n\t[DllImport(\"kernel32.dll\", EntryPoint = \"WaitNamedPipeW\", SetLastError = true)]\n\tinternal static extern bool WaitNamedPipe(string lpNamedPipeName, int nTimeOut);\n\t\n\t//[DllImport(\"kernel32.dll\", EntryPoint = \"CallNamedPipeW\", SetLastError = true)]\n\t//internal static extern bool CallNamedPipe(string lpNamedPipeName, void* lpInBuffer, int nInBufferSize, out int lpOutBuffer, int nOutBufferSize, out int lpBytesRead, int nTimeOut);\n\t\n\t//[DllImport(\"kernel32.dll\", SetLastError = true)]\n\t//internal static extern bool GetNamedPipeClientProcessId(IntPtr Pipe, out int ClientProcessId);\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern bool PeekNamedPipe(IntPtr hNamedPipe, void* lpBuffer, int nBufferSize, out int lpBytesRead, out int lpTotalBytesAvail, IntPtr lpBytesLeftThisMessage = default);\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern bool AllocConsole();\n\t\n\t[DllImport(\"kernel32.dll\", EntryPoint = \"OutputDebugStringW\")]\n\tinternal static extern void OutputDebugString(string lpOutputString);\n\t\n\t[DllImport(\"kernel32.dll\")]\n\tinternal static extern bool SetProcessWorkingSetSize(IntPtr hProcess, nint dwMinimumWorkingSetSize, nint dwMaximumWorkingSetSize);\n\t\n\t//internal struct PROCESS_MEMORY_COUNTERS\n\t//{\n\t//\tpublic int cb;\n\t//\tpublic int PageFaultCount;\n\t//\tpublic nint PeakWorkingSetSize;\n\t//\tpublic nint WorkingSetSize;\n\t//\tpublic nint QuotaPeakPagedPoolUsage;\n\t//\tpublic nint QuotaPagedPoolUsage;\n\t//\tpublic nint QuotaPeakNonPagedPoolUsage;\n\t//\tpublic nint QuotaNonPagedPoolUsage;\n\t//\tpublic nint PagefileUsage;\n\t//\tpublic nint PeakPagefileUsage;\n\t//}\n\t\n\t//[DllImport(\"kernel32.dll\", EntryPoint = \"K32GetProcessMemoryInfo\")]\n\t//internal static extern bool GetProcessMemoryInfo(IntPtr Process, ref PROCESS_MEMORY_COUNTERS ppsmemCounters, int cb);\n\t\n\t[DllImport(\"kernel32.dll\", EntryPoint = \"FindResourceW\", SetLastError = true)]\n\tpublic static extern IntPtr FindResource(IntPtr hModule, nint lpName, nint lpType);\n\t\n\tinternal const int RT_GROUP_ICON = 14;\n\t\n\t//internal const int STD_INPUT_HANDLE = -10;\n\tinternal const int STD_OUTPUT_HANDLE = -11;\n\t\n\t[DllImport(\"kernel32.dll\")]\n\tinternal static extern nint GetStdHandle(int nStdHandle);\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern uint GetConsoleOutputCP();\n\t\n\t[DllImport(\"kernel32.dll\")]\n\tinternal static extern nint SetUnhandledExceptionFilter(nint _);\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern uint QueueUserAPC(delegate* unmanaged<GCHandle, void> pfnAPC, IntPtr hThread, GCHandle dwData);\n\t\n\tinternal delegate void PAPCFUNC(nint Parameter);\n\t\n\t[DllImport(\"kernel32.dll\", EntryPoint = \"GetDriveTypeW\")]\n\tinternal static extern int GetDriveType(string lpRootPathName);\n\t\n\t/// <summary>\n\t/// Use this API instead of <c>Directory.CreateSymbolicLink</c> which has a bug: does not throw exception when fails (eg non-admin).\n\t/// Note: the API fails if non-admin.\n\t///\t\tWith flag 2 does not fail if enabled developer mode.\n\t///\t\tIt seems can be enabled for non-admin in <c>gpedit.msc</c>; not tested; google for more info.\n\t///\t\tSomewhere found this info, but it's incorrect: \"Windows 11 doesn’t require administrative privileges to create symbolic links\".\n\t/// </summary>\n\t/// <param name=\"dwFlags\">1 - directory.</param>\n\t[DllImport(\"kernel32.dll\", EntryPoint = \"CreateSymbolicLinkW\", SetLastError = true)]\n\t[return: MarshalAs(UnmanagedType.U1)] //BOOLEAN\n\tinternal static extern bool CreateSymbolicLink(string lpSymlinkFileName, string lpTargetFileName, uint dwFlags);\n\t\n\t[DllImport(\"kernel32.dll\")]\n\tinternal static extern int GetACP();\n\t\n\t[DllImport(\"kernel32.dll\", SetLastError = true)]\n\tinternal static extern bool DeviceIoControl(IntPtr hDevice, int dwIoControlCode, void* lpInBuffer, int nInBufferSize, void* lpOutBuffer, int nOutBufferSize, out int lpBytesReturned, nint lpOverlapped = 0);\n\t\n\tinternal const int IOCTL_STORAGE_QUERY_PROPERTY = 0x2D1400;\n\t\n\tinternal struct STORAGE_PROPERTY_QUERY {\n\t\tpublic int PropertyId;\n\t\tpublic int QueryType;\n\t\tpublic byte AdditionalParameters;\n\t}\n\t\n\tinternal struct DEVICE_SEEK_PENALTY_DESCRIPTOR {\n\t\tpublic uint Version;\n\t\tpublic uint Size;\n\t\tpublic byte IncursSeekPenalty;\n\t}\n\t\n\t[DllImport(\"kernel32.dll\", EntryPoint = \"GetVolumePathNameW\", SetLastError = true)]\n\tinternal static extern bool GetVolumePathName(string lpszFileName, char* lpszVolumePathName, int cchBufferLength);\n\t\n\t[DllImport(\"kernel32.dll\", EntryPoint = \"GetVolumeNameForVolumeMountPointW\", SetLastError = true)]\n\tinternal static extern bool GetVolumeNameForVolumeMountPoint(string lpszVolumeMountPoint, char* lpszVolumeName, int cchBufferLength);\n\t\n\t[DllImport(\"kernel32.dll\", EntryPoint = \"GetCommandLineW\")]\n\tinternal static extern char* GetCommandLine();\n\t\n\t[DllImport(\"kernel32.dll\")]\n\tinternal static extern int WTSGetActiveConsoleSessionId();\n\t\n\t\n\t\n\t\n\t\n\t#region undocumented\n\t\n\tinternal delegate int CheckElevationEnabled(out int pResult);\n\t\n\t#endregion\n}\n"
  },
  {
    "path": "Au/Api/Api^user32.cs",
    "content": "namespace Au.Types;\n\n//#pragma warning disable 649 //field never assigned\n\nstatic unsafe partial class Api {\n\t[DllImport(\"user32.dll\", EntryPoint = \"SendMessageW\", SetLastError = true)]\n\tinternal static extern nint SendMessage(wnd hWnd, int msg, nint wParam, nint lParam);\n\t\n\t[DllImport(\"user32.dll\", EntryPoint = \"SendMessageTimeoutW\", SetLastError = true)]\n\tinternal static extern nint SendMessageTimeout(wnd hWnd, int Msg, nint wParam, nint lParam, SMTFlags flags, int uTimeout, out nint lpdwResult);\n\t\n\t[DllImport(\"user32.dll\", EntryPoint = \"SendNotifyMessageW\", SetLastError = true)]\n\tinternal static extern bool SendNotifyMessage(wnd hWnd, int Msg, nint wParam, nint lParam);\n\t\n\t[DllImport(\"user32.dll\", EntryPoint = \"PostMessageW\", SetLastError = true)]\n\tinternal static extern bool PostMessage(wnd hWnd, int Msg, nint wParam, nint lParam);\n\t\n\t[DllImport(\"user32.dll\", EntryPoint = \"PostThreadMessageW\", SetLastError = true)]\n\tinternal static extern bool PostThreadMessage(int idThread, int Msg, nint wParam, nint lParam);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tstatic extern nint GetWindowLongW(wnd hWnd, int nIndex);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tstatic extern nint GetWindowLongPtrW(wnd hWnd, int nIndex);\n\t\n\t//info: 32-bit user32.dll does not have GetWindowLongPtrW etc. In C++, in x86 config it is defined as GetWindowLongW etc.\n\tinternal static nint GetWindowLongPtr(wnd w, int nIndex)\n\t\t=> IntPtr.Size == 8 ? GetWindowLongPtrW(w, nIndex) : GetWindowLongW(w, nIndex);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tstatic extern nint SetWindowLongW(wnd hWnd, int nIndex, nint dwNewLong);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tstatic extern nint SetWindowLongPtrW(wnd hWnd, int nIndex, nint dwNewLong);\n\t\n\tinternal static nint SetWindowLongPtr(wnd w, int nIndex, nint dwNewLong)\n\t\t=> IntPtr.Size == 8 ? SetWindowLongPtrW(w, nIndex, dwNewLong) : SetWindowLongW(w, nIndex, dwNewLong);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tstatic extern nint GetClassLongW(wnd hWnd, int nIndex);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tstatic extern nint GetClassLongPtrW(wnd hWnd, int nIndex);\n\t\n\tinternal static nint GetClassLongPtr(wnd w, int nIndex)\n\t\t=> IntPtr.Size == 8 ? GetClassLongPtrW(w, nIndex) : GetClassLongW(w, nIndex);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tstatic extern nint SetClassLongW(wnd hWnd, int nIndex, nint dwNewLong);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tstatic extern nint SetClassLongPtrW(wnd hWnd, int nIndex, nint dwNewLong);\n\t\n\tinternal static nint SetClassLongPtr(wnd w, int nIndex, nint dwNewLong)\n\t\t=> IntPtr.Size == 8 ? SetClassLongPtrW(w, nIndex, dwNewLong) : SetClassLongW(w, nIndex, dwNewLong);\n\t\n\t[DllImport(\"user32.dll\", EntryPoint = \"GetClassNameW\", SetLastError = true)]\n\tinternal static extern int GetClassName(wnd hWnd, char* lpClassName, int nMaxCount);\n\t\n\t[DllImport(\"user32.dll\", EntryPoint = \"InternalGetWindowText\", SetLastError = true)]\n\tinternal static extern int InternalGetWindowText(wnd hWnd, char* pString, int cchMaxCount);\n\t\n\t[DllImport(\"user32.dll\")]\n\tinternal static extern bool IsWindow(wnd hWnd);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool IsWindowVisible(wnd hWnd);\n\t\n\tinternal const int SW_HIDE = 0;\n\tinternal const int SW_SHOWNORMAL = 1;\n\tinternal const int SW_SHOWMINIMIZED = 2;\n\tinternal const int SW_SHOWMAXIMIZED = 3;\n\tinternal const int SW_SHOWNOACTIVATE = 4; //restores min/max window\n\tinternal const int SW_SHOW = 5;\n\tinternal const int SW_MINIMIZE = 6;\n\tinternal const int SW_SHOWMINNOACTIVE = 7;\n\tinternal const int SW_SHOWNA = 8;\n\tinternal const int SW_RESTORE = 9;\n\tinternal const int SW_SHOWDEFAULT = 10;\n\tinternal const int SW_FORCEMINIMIZE = 11;\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern void ShowWindow(wnd hWnd, int SW_X);\n\t//note: the return value does not say succeeded/failed.\n\t//\tIt is non-zero if was visible, 0 if was hidden.\n\t//\tDeclared void to avoid programming errors.\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool IsWindowEnabled(wnd hWnd);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern void EnableWindow(wnd hWnd, bool bEnable);\n\t//note: the returns value does not say succeeded/failed.\n\t//\tIt is non-zero if was disabled, 0 if was enabled.\n\t//\tDeclared void to avoid programming errors.\n\t\n\t[DllImport(\"user32.dll\", EntryPoint = \"FindWindowExW\", SetLastError = true)]\n\tinternal static extern wnd _FindWindowEx(wnd hWndParent, wnd hWndChildAfter, string lpszClass, string lpszWindow);\n\t\n\tinternal static wnd FindWindowEx(wnd wParent = default, wnd wAfter = default, string cn = null, string name = null) {\n\t\t//Windows 11 bug: FindWindow[Ex] occasionally returns 0 for an existing window.\n\t\t//\tSecond or third call usually succeeds.\n\t\t//\tMore often fails when there is some activity, eg when Visual Studio compiles/launches an app, or when opening/closing Notepad++.\n\t\t//\tGetLastError 0.\n\t\t//\tTo reproduce, call every 2 ms, eg to find Notepad by classname.\n\t\t//\tEnumWindows does not fail.\n\t\t//\tOn Win10 never noticed and cannot reproduce.\n\t\t//\tMaybe FindWindowEx uses an unsafe loop like with GetWindow. When Z order changes, skips part of windows.\n\t\t//\tFUTURE: remove this workaround when API fixed. Also in wn::FindWndEx.\n\t\tif (osVersion.minWin11) {\n\t\t\tfor (int i = 5; --i >= 0;) {\n\t\t\t\tvar w = _FindWindowEx(wParent, wAfter, cn, name);\n\t\t\t\tif (!w.Is0) return w;\n\t\t\t}\n\t\t\treturn default;\n\t\t} else {\n\t\t\treturn _FindWindowEx(wParent, wAfter, cn, name);\n\t\t}\n\t}\n\t\n\tinternal struct WNDCLASSEX {\n\t\tpublic int cbSize;\n\t\tpublic uint style;\n\t\tpublic IntPtr lpfnWndProc; //not WNDPROC to avoid auto-marshaling where don't need. Use Marshal.GetFunctionPointerForDelegate/GetDelegateForFunctionPointer.\n\t\tpublic int cbClsExtra;\n\t\tpublic int cbWndExtra;\n\t\tpublic IntPtr hInstance;\n\t\tpublic IntPtr hIcon;\n\t\tpublic IntPtr hCursor;\n\t\tpublic nint hbrBackground;\n#pragma warning disable 169\n\t\tIntPtr lpszMenuName;\n#pragma warning restore 169\n\t\tpublic char* lpszClassName; //not string because CLR would call CoTaskMemFree\n\t\tpublic IntPtr hIconSm;\n\t\t\n\t\t/// <summary>\n\t\t/// If ex <c>null</c>, sets arrow cursor and style <c>CS_VREDRAW | CS_HREDRAW</c>.\n\t\t/// </summary>\n\t\tpublic WNDCLASSEX(RWCEtc ex) {\n\t\t\tcbSize = sizeof(WNDCLASSEX);\n\t\t\tif (ex == null) {\n\t\t\t\thCursor = LoadCursor(default, MCursor.Arrow);\n\t\t\t\tstyle = CS_VREDRAW | CS_HREDRAW;\n\t\t\t} else {\n\t\t\t\tstyle = ex.style;\n\t\t\t\tcbClsExtra = ex.cbClsExtra;\n\t\t\t\tcbWndExtra = ex.cbWndExtra;\n\t\t\t\t//hInstance = ex.hInstance;\n\t\t\t\thIcon = ex.hIcon;\n\t\t\t\tif (ex.hCursor != default) hCursor = ex.hCursor; else if (ex.mCursor != default) hCursor = LoadCursor(default, ex.mCursor);\n\t\t\t\thbrBackground = ex.hbrBackground;\n\t\t\t\thIconSm = ex.hIconSm;\n\t\t\t}\n\t\t}\n\t}\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern ushort RegisterClassEx(in WNDCLASSEX lpwcx);\n\t\n\t[DllImport(\"user32.dll\", EntryPoint = \"GetClassInfoExW\", SetLastError = true)]\n\tinternal static extern ushort GetClassInfoEx(IntPtr hInstance, string lpszClass, ref WNDCLASSEX lpwcx);\n\t\n\t[DllImport(\"user32.dll\", EntryPoint = \"UnregisterClassW\", SetLastError = true)]\n\tinternal static extern bool UnregisterClass(string lpClassName, IntPtr hInstance);\n\t\n\t[DllImport(\"user32.dll\", EntryPoint = \"UnregisterClassW\", SetLastError = true)]\n\tinternal static extern bool UnregisterClass(uint classAtom, IntPtr hInstance);\n\t\n\t[DllImport(\"user32.dll\", EntryPoint = \"CreateWindowExW\", SetLastError = true)]\n\tinternal static extern wnd CreateWindowEx(WSE dwExStyle, string lpClassName, string lpWindowName, WS dwStyle, int x, int y, int nWidth, int nHeight, wnd hWndParent = default, nint hMenu = 0, IntPtr hInstance = default, nint lpParam = 0);\n\t\n\tinternal const int CW_USEDEFAULT = 1 << 31;\n\t\n\t[DllImport(\"user32.dll\", EntryPoint = \"DefWindowProcW\")]\n\tinternal static extern nint DefWindowProc(wnd hWnd, int msg, nint wParam, nint lParam);\n\t\n\t[DllImport(\"user32.dll\", EntryPoint = \"CallWindowProcW\")]\n\tinternal static extern nint CallWindowProc(nint lpPrevWndFunc, wnd hWnd, int Msg, nint wParam, nint lParam);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool DestroyWindow(wnd hWnd);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern void PostQuitMessage(int nExitCode);\n\t\n\t[DllImport(\"user32.dll\", EntryPoint = \"GetMessage\", SetLastError = true)]\n\tinternal static extern int _GetMessage(out MSG lpMsg, wnd hWnd, int wMsgFilterMin, int wMsgFilterMax);\n\t\n\tinternal static bool GetMessage(out MSG m, wnd hWnd = default, int wMsgFilterMin = 0, int wMsgFilterMax = 0) {\n\t\tint r = _GetMessage(out m, hWnd, wMsgFilterMin, wMsgFilterMax);\n\t\tif (r == -1) throw new Win32Exception();\n\t\treturn r != 0;\n\t}\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool TranslateMessage(in MSG lpMsg);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern nint DispatchMessage(in MSG lpmsg);\n\t\n\tinternal const uint PM_NOREMOVE = 0x0;\n\tinternal const uint PM_REMOVE = 0x1;\n\tinternal const uint PM_NOYIELD = 0x2;\n\tinternal const uint PM_QS_SENDMESSAGE = 0x400000;\n\tinternal const uint PM_QS_POSTMESSAGE = 0x980000;\n\tinternal const uint PM_QS_PAINT = 0x200000;\n\tinternal const uint PM_QS_INPUT = 0x1C070000;\n\t\n\t[DllImport(\"user32.dll\", EntryPoint = \"PeekMessageW\", SetLastError = true)]\n\tinternal static extern bool PeekMessage(out MSG lpMsg, wnd hWnd = default, int wMsgFilterMin = 0, int wMsgFilterMax = 0, uint wRemoveMsg = PM_REMOVE);\n\t\n\t[DllImport(\"user32.dll\")]\n\tinternal static extern bool WaitMessage();\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool ReplyMessage(nint lResult);\n\t\n\tinternal const int GA_PARENT = 1;\n\tinternal const int GA_ROOT = 2;\n\tinternal const int GA_ROOTOWNER = 3;\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern wnd GetAncestor(wnd hwnd, uint GA_X);\n\t\n\t[DllImport(\"user32.dll\")]\n\tinternal static extern wnd GetForegroundWindow();\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool SetForegroundWindow(wnd hWnd);\n\t\n\tinternal const int ASFW_ANY = -1;\n\t\n#if true\n\t[DllImport(\"user32.dll\", EntryPoint = \"AllowSetForegroundWindow\", SetLastError = true)]\n\tstatic extern bool _AllowSetForegroundWindow(int dwProcessId);\n\t\n\tinternal static bool AllowSetForegroundWindow(int dwProcessId = ASFW_ANY) {\n\t\treturn _AllowSetForegroundWindow(ASFW_ANY);\n\t\t//Ignore dwProcessId, because AllowSetForegroundWindow doc says:\n\t\t//\"The process specified by the dwProcessId parameter loses the ability to set the foreground window the next time that either the user generates input, unless the input is directed at that process, or the next time a process calls AllowSetForegroundWindow, unless the same process is specified as in the previous call to AllowSetForegroundWindow.\"\n\t\t//If it's true (not tested), if we call AllowSetForegroundWindow with a process id, we disable setforegroundwindow in all other processes.\n\t}\n#else\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool AllowSetForegroundWindow(int dwProcessId = ASFW_ANY);\n#endif\n\t\n\tinternal const uint LSFW_LOCK = 1;\n\tinternal const uint LSFW_UNLOCK = 2;\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool LockSetForegroundWindow(uint LSFW_X);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern wnd SetFocus(wnd hWnd);\n\t\n\t[DllImport(\"user32.dll\")]\n\tinternal static extern wnd GetFocus();\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern wnd SetActiveWindow(wnd hWnd);\n\t\n\t[DllImport(\"user32.dll\")]\n\tinternal static extern wnd GetActiveWindow();\n\t\n\tinternal struct WINDOWPOS {\n\t\tpublic wnd hwnd;\n\t\tpublic wnd hwndInsertAfter;\n\t\tpublic int x;\n\t\tpublic int y;\n\t\tpublic int cx;\n\t\tpublic int cy;\n\t\tpublic SWPFlags flags;\n\t}\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool SetWindowPos(wnd hWnd, wnd hWndInsertAfter, int X, int Y, int cx, int cy, SWPFlags swpFlags);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern IntPtr BeginDeferWindowPos(int nNumWindows);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern IntPtr DeferWindowPos(IntPtr hWinPosInfo, wnd hWnd, wnd hWndInsertAfter, int x, int y, int cx, int cy, SWPFlags uFlags);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool EndDeferWindowPos(IntPtr hWinPosInfo);\n\t\n\tinternal struct FLASHWINFO {\n\t\tpublic int cbSize;\n\t\tpublic wnd hwnd;\n\t\tpublic uint dwFlags;\n\t\tpublic int uCount;\n\t\tpublic int dwTimeout;\n\t}\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool FlashWindowEx(ref FLASHWINFO pfwi);\n\t\n\tinternal const int GW_OWNER = 4;\n\tinternal const int GW_HWNDPREV = 3;\n\tinternal const int GW_HWNDNEXT = 2;\n\tinternal const int GW_HWNDLAST = 1;\n\tinternal const int GW_HWNDFIRST = 0;\n\tinternal const int GW_ENABLEDPOPUP = 6;\n\tinternal const int GW_CHILD = 5;\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern wnd GetWindow(wnd hWnd, int GW_X);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern wnd GetTopWindow(wnd hWnd);\n\t\n\t//rejected. Obsolete, confusing.\n\t//\tReturns owner for top-level windows with WS_POPUP style.\n\t//\tReturns 0 for child windows without WS_CHILD style, eg message-only windows and QM2 toolbar owners.\n\t//[DllImport(\"user32.dll\", SetLastError = true)]\n\t//internal static extern wnd GetParent(wnd hWnd);\n\t\n\t[DllImport(\"user32.dll\")]\n\tinternal static extern wnd GetDesktopWindow();\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern wnd GetShellWindow();\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern wnd GetLastActivePopup(wnd hWnd);\n\t\n\t[DllImport(\"user32.dll\")]\n\tinternal static extern bool IntersectRect(out RECT lprcDst, in RECT lprcSrc1, in RECT lprcSrc2);\n\t\n\t[DllImport(\"user32.dll\")]\n\tinternal static extern bool UnionRect(out RECT lprcDst, in RECT lprcSrc1, in RECT lprcSrc2);\n\t\n\t/// <summary>\n\t/// <c>GetPhysicalCursorPos</c>.\n\t/// </summary>\n\t/// <remarks>\n\t/// Gets DPI physical cursor pos, ie always in pixels.\n\t/// The classic <c>GetCursorPos</c> API behavior is undefined. Sometimes physical, sometimes logical.\n\t/// Make sure the process is fully DPI-aware.\n\t/// </remarks>\n\t[DllImport(\"user32.dll\", EntryPoint = \"GetPhysicalCursorPos\", SetLastError = true)]\n\tinternal static extern bool GetCursorPos(out POINT lpPoint);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool SetCursorPos(int X, int Y);\n\t\n\t[DllImport(\"user32.dll\", EntryPoint = \"LoadImageW\", SetLastError = true)]\n\tinternal static extern IntPtr LoadImage(IntPtr hInst, string name, int type, int cx, int cy, uint LR_X);\n\t[DllImport(\"user32.dll\", EntryPoint = \"LoadImageW\", SetLastError = true)]\n\tinternal static extern IntPtr LoadImage(IntPtr hInst, nint resId, int type, int cx, int cy, uint LR_X);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern IntPtr CopyImage(IntPtr h, int type, int cx, int cy, uint flags);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool DestroyIcon(IntPtr hIcon);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool GetWindowRect(wnd hWnd, out RECT lpRect);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool GetClientRect(wnd hWnd, out RECT lpRect);\n\t\n\tinternal const uint WPF_SETMINPOSITION = 0x1;\n\tinternal const uint WPF_RESTORETOMAXIMIZED = 0x2;\n\tinternal const uint WPF_ASYNCWINDOWPLACEMENT = 0x4;\n\t\n\tinternal struct WINDOWPLACEMENT {\n\t\tpublic int length;\n\t\t/// <summary><c>WPF_x</c></summary>\n\t\tpublic uint flags;\n\t\tpublic int showCmd;\n\t\tpublic POINT ptMinPosition;\n\t\tpublic POINT ptMaxPosition;\n\t\tpublic RECT rcNormalPosition;\n\t}\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool GetWindowPlacement(wnd hWnd, ref WINDOWPLACEMENT lpwndpl);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool SetWindowPlacement(wnd hWnd, in WINDOWPLACEMENT lpwndpl);\n\t\n\tinternal struct WINDOWINFO {\n\t\tpublic int cbSize;\n\t\tpublic RECT rcWindow;\n\t\tpublic RECT rcClient;\n\t\tpublic WS dwStyle;\n\t\tpublic WSE dwExStyle;\n\t\tpublic uint dwWindowStatus;\n\t\tpublic int cxWindowBorders;\n\t\tpublic int cyWindowBorders;\n\t\tpublic ushort atomWindowType;\n\t\tpublic ushort wCreatorVersion;\n\t}\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool GetWindowInfo(wnd hwnd, ref WINDOWINFO pwi);\n\t\n\t//[DllImport(\"user32.dll\", SetLastError = true)]\n\t//internal static extern bool GetTitleBarInfo(wnd hwnd, ref TITLEBARINFO pti);\n\t\n\t//internal struct TITLEBARINFO {\n\t//\tpublic int cbSize;\n\t//\tpublic RECT r;\n\t//\tpublic fixed uint rgstate[6];\n\t//}\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool IsZoomed(wnd hWnd);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool IsIconic(wnd hWnd);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern int GetWindowThreadProcessId(wnd hWnd, int* lpdwProcessId);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool IsWindowUnicode(wnd hWnd);\n\t\n\t[DllImport(\"user32.dll\", EntryPoint = \"GetPropW\", SetLastError = true)]\n\tinternal static extern nint GetProp(wnd hWnd, string lpString);\n\t\n\t[DllImport(\"user32.dll\", EntryPoint = \"GetPropW\", SetLastError = true)]\n\t//internal static extern nint GetProp(wnd hWnd, [MarshalAs(UnmanagedType.SysInt)] ushort atom); //exception, must be U2\n\tinternal static extern nint GetProp(wnd hWnd, nint atom);\n\t\n\t[DllImport(\"user32.dll\", EntryPoint = \"SetPropW\", SetLastError = true)]\n\tinternal static extern bool SetProp(wnd hWnd, string lpString, nint hData);\n\t\n\t[DllImport(\"user32.dll\", EntryPoint = \"SetPropW\", SetLastError = true)]\n\tinternal static extern bool SetProp(wnd hWnd, nint atom, nint hData);\n\t\n\t[DllImport(\"user32.dll\", EntryPoint = \"RemovePropW\", SetLastError = true)]\n\tinternal static extern nint RemoveProp(wnd hWnd, string lpString);\n\t\n\t[DllImport(\"user32.dll\", EntryPoint = \"RemovePropW\", SetLastError = true)]\n\tinternal static extern nint RemoveProp(wnd hWnd, nint atom);\n\t\n\tinternal delegate bool PROPENUMPROCEX(wnd hwnd, IntPtr lpszString, nint hData, nint dwData);\n\t\n\t[DllImport(\"user32.dll\", EntryPoint = \"EnumPropsExW\", SetLastError = true)]\n\tinternal static extern int EnumPropsEx(wnd hWnd, PROPENUMPROCEX lpEnumFunc, nint lParam);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern wnd GetDlgItem(wnd hDlg, int nIDDlgItem);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern int GetDlgCtrlID(wnd hWnd);\n\t\n\tinternal delegate int WNDENUMPROC(wnd w, void* p);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool EnumWindows(WNDENUMPROC lpEnumFunc, void* p = null);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool EnumThreadWindows(int dwThreadId, WNDENUMPROC lpEnumFunc, void* p = null);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool EnumChildWindows(wnd hWndParent, WNDENUMPROC lpEnumFunc, void* p = null);\n\t\n\t[DllImport(\"user32.dll\", EntryPoint = \"RegisterWindowMessageW\", SetLastError = true)]\n\tinternal static extern int RegisterWindowMessage(string lpString);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool IsChild(wnd hWndParent, wnd hWnd);\n\t\n\t//rejected. As slow as GetAncestor(GA_PARENT).\n\t//[DllImport(\"user32.dll\", SetLastError = true), Obsolete(\"Undocumented API\")]\n\t//internal static extern bool IsTopLevelWindow(wnd hWnd);\n\t\n\t[DllImport(\"user32.dll\")]\n\tinternal static extern IntPtr MonitorFromPoint(POINT pt, SODefault dwFlags);\n\t\n\t[DllImport(\"user32.dll\")]\n\tinternal static extern IntPtr MonitorFromRect(in RECT lprc, SODefault dwFlags);\n\t\n\t[DllImport(\"user32.dll\")]\n\tinternal static extern IntPtr MonitorFromWindow(wnd hwnd, SODefault dwFlags);\n\t\n\tinternal struct MONITORINFO {\n\t\tpublic int cbSize;\n\t\tpublic RECT rcMonitor;\n\t\tpublic RECT rcWork;\n\t\tpublic uint dwFlags;\n\t}\n\t\n\t[DllImport(\"user32.dll\", EntryPoint = \"GetMonitorInfoW\")]\n\tstatic extern bool _GetMonitorInfo(IntPtr hMonitor, ref MONITORINFO lpmi);\n\t\n\tinternal static bool GetMonitorInfo(IntPtr hMonitor, out MONITORINFO lpmi) {\n\t\tlpmi = new MONITORINFO { cbSize = sizeof(MONITORINFO) };\n\t\treturn _GetMonitorInfo(hMonitor, ref lpmi);\n\t}\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool EnumDisplayMonitors(nint hdc, RECT* lprcClip, delegate* unmanaged<nint, nint, RECT*, nint*, int> lpfnEnum, nint* a);\n\t\n\t#region GetSystemMetrics, SystemParametersInfo\n\t\n\tinternal const int SM_YVIRTUALSCREEN = 77;\n\tinternal const int SM_XVIRTUALSCREEN = 76;\n\tinternal const int SM_TABLETPC = 86;\n\tinternal const int SM_SWAPBUTTON = 23;\n\tinternal const int SM_STARTER = 88;\n\tinternal const int SM_SLOWMACHINE = 73;\n\tinternal const int SM_SHUTTINGDOWN = 8192;\n\tinternal const int SM_SHOWSOUNDS = 70;\n\tinternal const int SM_SERVERR2 = 89;\n\tinternal const int SM_SECURE = 44;\n\tinternal const int SM_SAMEDISPLAYFORMAT = 81;\n\tinternal const int SM_RESERVED4 = 27;\n\tinternal const int SM_RESERVED3 = 26;\n\tinternal const int SM_RESERVED2 = 25;\n\tinternal const int SM_RESERVED1 = 24;\n\tinternal const int SM_REMOTESESSION = 4096;\n\tinternal const int SM_REMOTECONTROL = 8193;\n\tinternal const int SM_PENWINDOWS = 41;\n\tinternal const int SM_NETWORK = 63;\n\tinternal const int SM_MOUSEWHEELPRESENT = 75;\n\tinternal const int SM_MOUSEPRESENT = 19;\n\tinternal const int SM_MIDEASTENABLED = 74;\n\tinternal const int SM_MENUDROPALIGNMENT = 40;\n\tinternal const int SM_MEDIACENTER = 87;\n\tinternal const int SM_IMMENABLED = 82;\n\tinternal const int SM_DEBUG = 22;\n\tinternal const int SM_DBCSENABLED = 42;\n\tinternal const int SM_CYVTHUMB = 9;\n\tinternal const int SM_CYVSCROLL = 20;\n\tinternal const int SM_CYVIRTUALSCREEN = 79;\n\tinternal const int SM_CYSMSIZE = 53;\n\tinternal const int SM_CYSMICON = 50;\n\tinternal const int SM_CYSMCAPTION = 51;\n\tinternal const int SM_CYSIZEFRAME = SM_CYFRAME;\n\tinternal const int SM_CYSIZE = 31;\n\tinternal const int SM_CYSCREEN = 1;\n\tinternal const int SM_CYMINTRACK = 35;\n\tinternal const int SM_CYMINSPACING = 48;\n\tinternal const int SM_CYMINIMIZED = 58;\n\tinternal const int SM_CYMIN = 29;\n\tinternal const int SM_CYMENUSIZE = 55;\n\tinternal const int SM_CYMENUCHECK = 72;\n\tinternal const int SM_CYMENU = 15;\n\tinternal const int SM_CYMAXTRACK = 60;\n\tinternal const int SM_CYMAXIMIZED = 62;\n\tinternal const int SM_CYKANJIWINDOW = 18;\n\tinternal const int SM_CYICONSPACING = 39;\n\tinternal const int SM_CYICON = 12;\n\tinternal const int SM_CYHSCROLL = 3;\n\tinternal const int SM_CYFULLSCREEN = 17;\n\tinternal const int SM_CYFRAME = 33;\n\tinternal const int SM_CYFOCUSBORDER = 84;\n\tinternal const int SM_CYFIXEDFRAME = SM_CYDLGFRAME;\n\tinternal const int SM_CYEDGE = 46;\n\tinternal const int SM_CYDRAG = 69;\n\tinternal const int SM_CYDOUBLECLK = 37;\n\tinternal const int SM_CYDLGFRAME = 8;\n\tinternal const int SM_CYCURSOR = 14;\n\tinternal const int SM_CYCAPTION = 4;\n\tinternal const int SM_CYBORDER = 6;\n\tinternal const int SM_CXVSCROLL = 2;\n\tinternal const int SM_CXVIRTUALSCREEN = 78;\n\tinternal const int SM_CXSMSIZE = 52;\n\tinternal const int SM_CXSMICON = 49;\n\tinternal const int SM_CXSIZEFRAME = SM_CXFRAME;\n\tinternal const int SM_CXSIZE = 30;\n\tinternal const int SM_CXSCREEN = 0;\n\tinternal const int SM_CXMINTRACK = 34;\n\tinternal const int SM_CXMINSPACING = 47;\n\tinternal const int SM_CXMINIMIZED = 57;\n\tinternal const int SM_CXMIN = 28;\n\tinternal const int SM_CXMENUSIZE = 54;\n\tinternal const int SM_CXMENUCHECK = 71;\n\tinternal const int SM_CXMAXTRACK = 59;\n\tinternal const int SM_CXMAXIMIZED = 61;\n\tinternal const int SM_CXICONSPACING = 38;\n\tinternal const int SM_CXICON = 11;\n\tinternal const int SM_CXHTHUMB = 10;\n\tinternal const int SM_CXHSCROLL = 21;\n\tinternal const int SM_CXFULLSCREEN = 16;\n\tinternal const int SM_CXFRAME = 32;\n\tinternal const int SM_CXFOCUSBORDER = 83;\n\tinternal const int SM_CXFIXEDFRAME = SM_CXDLGFRAME;\n\tinternal const int SM_CXEDGE = 45;\n\tinternal const int SM_CXDRAG = 68;\n\tinternal const int SM_CXDOUBLECLK = 36;\n\tinternal const int SM_CXDLGFRAME = 7;\n\tinternal const int SM_CXCURSOR = 13;\n\tinternal const int SM_CXBORDER = 5;\n\tinternal const int SM_CMOUSEBUTTONS = 43;\n\tinternal const int SM_CMONITORS = 80;\n\tinternal const int SM_CMETRICS = 90;\n\tinternal const int SM_CLEANBOOT = 67;\n\tinternal const int SM_CARETBLINKINGENABLED = 8194;\n\tinternal const int SM_ARRANGE = 56;\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern int GetSystemMetrics(int nIndex);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern int GetSystemMetricsForDpi(int nIndex, int dpi);\n\t\n\tinternal const int SPI_SETWORKAREA = 47;\n\tinternal const int SPI_SETWHEELSCROLLLINES = 105;\n\tinternal const int SPI_SETUIEFFECTS = 4159;\n\tinternal const int SPI_SETTOOLTIPFADE = 4121;\n\tinternal const int SPI_SETTOOLTIPANIMATION = 4119;\n\tinternal const int SPI_SETTOGGLEKEYS = 53;\n\tinternal const int SPI_SETSTICKYKEYS = 59;\n\tinternal const int SPI_SETSOUNDSENTRY = 65;\n\tinternal const int SPI_SETSNAPTODEFBUTTON = 96;\n\tinternal const int SPI_SETSHOWSOUNDS = 57;\n\tinternal const int SPI_SETSHOWIMEUI = 111;\n\tinternal const int SPI_SETSERIALKEYS = 63;\n\tinternal const int SPI_SETSELECTIONFADE = 4117;\n\tinternal const int SPI_SETSCREENSAVETIMEOUT = 15;\n\tinternal const int SPI_SETSCREENSAVERRUNNING = 97;\n\tinternal const int SPI_SETSCREENSAVEACTIVE = 17;\n\tinternal const int SPI_SETSCREENREADER = 71;\n\tinternal const int SPI_SETPOWEROFFTIMEOUT = 82;\n\tinternal const int SPI_SETPOWEROFFACTIVE = 86;\n\tinternal const int SPI_SETPENWINDOWS = 49;\n\tinternal const int SPI_SETNONCLIENTMETRICS = 42;\n\tinternal const int SPI_SETMOUSEVANISH = 4129;\n\tinternal const int SPI_SETMOUSETRAILS = 93;\n\tinternal const int SPI_SETMOUSESPEED = 113;\n\tinternal const int SPI_SETMOUSESONAR = 4125;\n\tinternal const int SPI_SETMOUSEKEYS = 55;\n\tinternal const int SPI_SETMOUSEHOVERWIDTH = 99;\n\tinternal const int SPI_SETMOUSEHOVERTIME = 103;\n\tinternal const int SPI_SETMOUSEHOVERHEIGHT = 101;\n\tinternal const int SPI_SETMOUSECLICKLOCKTIME = 8201;\n\tinternal const int SPI_SETMOUSECLICKLOCK = 4127;\n\tinternal const int SPI_SETMOUSEBUTTONSWAP = 33;\n\tinternal const int SPI_SETMOUSE = 4;\n\tinternal const int SPI_SETMINIMIZEDMETRICS = 44;\n\tinternal const int SPI_SETMENUUNDERLINES = SPI_SETKEYBOARDCUES;\n\tinternal const int SPI_SETMENUSHOWDELAY = 107;\n\tinternal const int SPI_SETMENUFADE = 4115;\n\tinternal const int SPI_SETMENUDROPALIGNMENT = 28;\n\tinternal const int SPI_SETMENUANIMATION = 4099;\n\tinternal const int SPI_SETLOWPOWERTIMEOUT = 81;\n\tinternal const int SPI_SETLOWPOWERACTIVE = 85;\n\tinternal const int SPI_SETLISTBOXSMOOTHSCROLLING = 4103;\n\tinternal const int SPI_SETLANGTOGGLE = 91;\n\tinternal const int SPI_SETKEYBOARDSPEED = 11;\n\tinternal const int SPI_SETKEYBOARDPREF = 69;\n\tinternal const int SPI_SETKEYBOARDDELAY = 23;\n\tinternal const int SPI_SETKEYBOARDCUES = 4107;\n\tinternal const int SPI_SETICONTITLEWRAP = 26;\n\tinternal const int SPI_SETICONTITLELOGFONT = 34;\n\tinternal const int SPI_SETICONS = 88;\n\tinternal const int SPI_SETICONMETRICS = 46;\n\tinternal const int SPI_SETHOTTRACKING = 4111;\n\tinternal const int SPI_SETHIGHCONTRAST = 67;\n\tinternal const int SPI_SETHANDHELD = 78;\n\tinternal const int SPI_SETGRIDGRANULARITY = 19;\n\tinternal const int SPI_SETGRADIENTCAPTIONS = 4105;\n\tinternal const int SPI_SETFOREGROUNDLOCKTIMEOUT = 8193;\n\tinternal const int SPI_SETFOREGROUNDFLASHCOUNT = 8197;\n\tinternal const int SPI_SETFONTSMOOTHINGTYPE = 8203;\n\tinternal const int SPI_SETFONTSMOOTHINGORIENTATION = 8211;\n\tinternal const int SPI_SETFONTSMOOTHINGCONTRAST = 8205;\n\tinternal const int SPI_SETFONTSMOOTHING = 75;\n\tinternal const int SPI_SETFOCUSBORDERWIDTH = 8207;\n\tinternal const int SPI_SETFOCUSBORDERHEIGHT = 8209;\n\tinternal const int SPI_SETFLATMENU = 4131;\n\tinternal const int SPI_SETFILTERKEYS = 51;\n\tinternal const int SPI_SETFASTTASKSWITCH = 36;\n\tinternal const int SPI_SETDROPSHADOW = 4133;\n\tinternal const int SPI_SETDRAGWIDTH = 76;\n\tinternal const int SPI_SETDRAGHEIGHT = 77;\n\tinternal const int SPI_SETDRAGFULLWINDOWS = 37;\n\tinternal const int SPI_SETDOUBLECLKWIDTH = 29;\n\tinternal const int SPI_SETDOUBLECLKHEIGHT = 30;\n\tinternal const int SPI_SETDOUBLECLICKTIME = 32;\n\tinternal const int SPI_SETDESKWALLPAPER = 20;\n\tinternal const int SPI_SETDESKPATTERN = 21;\n\tinternal const int SPI_SETDEFAULTINPUTLANG = 90;\n\tinternal const int SPI_SETCURSORSHADOW = 4123;\n\tinternal const int SPI_SETCURSORS = 87;\n\tinternal const int SPI_SETCOMBOBOXANIMATION = 4101;\n\tinternal const int SPI_SETCARETWIDTH = 8199;\n\tinternal const int SPI_SETBORDER = 6;\n\tinternal const int SPI_SETBLOCKSENDINPUTRESETS = 4135;\n\tinternal const int SPI_SETBEEP = 2;\n\tinternal const int SPI_SETANIMATION = 73;\n\tinternal const int SPI_SETACTIVEWNDTRKZORDER = 4109;\n\tinternal const int SPI_SETACTIVEWNDTRKTIMEOUT = 8195;\n\tinternal const int SPI_SETACTIVEWINDOWTRACKING = 4097;\n\tinternal const int SPI_SETACCESSTIMEOUT = 61;\n\tinternal const int SPI_LANGDRIVER = 12;\n\tinternal const int SPI_ICONVERTICALSPACING = 24;\n\tinternal const int SPI_ICONHORIZONTALSPACING = 13;\n\tinternal const int SPI_GETWORKAREA = 48;\n\tinternal const int SPI_GETWINDOWSEXTENSION = 92;\n\tinternal const int SPI_GETWHEELSCROLLLINES = 104;\n\tinternal const int SPI_GETUIEFFECTS = 4158;\n\tinternal const int SPI_GETTOOLTIPFADE = 4120;\n\tinternal const int SPI_GETTOOLTIPANIMATION = 4118;\n\tinternal const int SPI_GETTOGGLEKEYS = 52;\n\tinternal const int SPI_GETSTICKYKEYS = 58;\n\tinternal const int SPI_GETSOUNDSENTRY = 64;\n\tinternal const int SPI_GETSNAPTODEFBUTTON = 95;\n\tinternal const int SPI_GETSHOWSOUNDS = 56;\n\tinternal const int SPI_GETSHOWIMEUI = 110;\n\tinternal const int SPI_GETSERIALKEYS = 62;\n\tinternal const int SPI_GETSELECTIONFADE = 4116;\n\tinternal const int SPI_GETSCREENSAVETIMEOUT = 14;\n\tinternal const int SPI_GETSCREENSAVERRUNNING = 114;\n\tinternal const int SPI_GETSCREENSAVEACTIVE = 16;\n\tinternal const int SPI_GETSCREENREADER = 70;\n\tinternal const int SPI_GETPOWEROFFTIMEOUT = 80;\n\tinternal const int SPI_GETPOWEROFFACTIVE = 84;\n\tinternal const int SPI_GETNONCLIENTMETRICS = 41;\n\tinternal const int SPI_GETMOUSEVANISH = 4128;\n\tinternal const int SPI_GETMOUSETRAILS = 94;\n\tinternal const int SPI_GETMOUSESPEED = 112;\n\tinternal const int SPI_GETMOUSESONAR = 4124;\n\tinternal const int SPI_GETMOUSEKEYS = 54;\n\tinternal const int SPI_GETMOUSEHOVERWIDTH = 98;\n\tinternal const int SPI_GETMOUSEHOVERTIME = 102;\n\tinternal const int SPI_GETMOUSEHOVERHEIGHT = 100;\n\tinternal const int SPI_GETMOUSECLICKLOCKTIME = 8200;\n\tinternal const int SPI_GETMOUSECLICKLOCK = 4126;\n\tinternal const int SPI_GETMOUSE = 3;\n\tinternal const int SPI_GETMINIMIZEDMETRICS = 43;\n\tinternal const int SPI_GETMENUUNDERLINES = SPI_GETKEYBOARDCUES;\n\tinternal const int SPI_GETMENUSHOWDELAY = 106;\n\tinternal const int SPI_GETMENUFADE = 4114;\n\tinternal const int SPI_GETMENUDROPALIGNMENT = 27;\n\tinternal const int SPI_GETMENUANIMATION = 4098;\n\tinternal const int SPI_GETLOWPOWERTIMEOUT = 79;\n\tinternal const int SPI_GETLOWPOWERACTIVE = 83;\n\tinternal const int SPI_GETLISTBOXSMOOTHSCROLLING = 4102;\n\tinternal const int SPI_GETKEYBOARDSPEED = 10;\n\tinternal const int SPI_GETKEYBOARDPREF = 68;\n\tinternal const int SPI_GETKEYBOARDDELAY = 22;\n\tinternal const int SPI_GETKEYBOARDCUES = 4106;\n\tinternal const int SPI_GETICONTITLEWRAP = 25;\n\tinternal const int SPI_GETICONTITLELOGFONT = 31;\n\tinternal const int SPI_GETICONMETRICS = 45;\n\tinternal const int SPI_GETHOTTRACKING = 4110;\n\tinternal const int SPI_GETHIGHCONTRAST = 66;\n\tinternal const int SPI_GETGRIDGRANULARITY = 18;\n\tinternal const int SPI_GETGRADIENTCAPTIONS = 4104;\n\tinternal const int SPI_GETFOREGROUNDLOCKTIMEOUT = 8192;\n\tinternal const int SPI_GETFOREGROUNDFLASHCOUNT = 8196;\n\tinternal const int SPI_GETFONTSMOOTHINGTYPE = 8202;\n\tinternal const int SPI_GETFONTSMOOTHINGORIENTATION = 8210;\n\tinternal const int SPI_GETFONTSMOOTHINGCONTRAST = 8204;\n\tinternal const int SPI_GETFONTSMOOTHING = 74;\n\tinternal const int SPI_GETFOCUSBORDERWIDTH = 8206;\n\tinternal const int SPI_GETFOCUSBORDERHEIGHT = 8208;\n\tinternal const int SPI_GETFLATMENU = 4130;\n\tinternal const int SPI_GETFILTERKEYS = 50;\n\tinternal const int SPI_GETFASTTASKSWITCH = 35;\n\tinternal const int SPI_GETDROPSHADOW = 4132;\n\tinternal const int SPI_GETDRAGFULLWINDOWS = 38;\n\tinternal const int SPI_GETDESKWALLPAPER = 115;\n\tinternal const int SPI_GETDEFAULTINPUTLANG = 89;\n\tinternal const int SPI_GETCURSORSHADOW = 4122;\n\tinternal const int SPI_GETCOMBOBOXANIMATION = 4100;\n\tinternal const int SPI_GETCARETWIDTH = 8198;\n\tinternal const int SPI_GETBORDER = 5;\n\tinternal const int SPI_GETBLOCKSENDINPUTRESETS = 4134;\n\tinternal const int SPI_GETBEEP = 1;\n\tinternal const int SPI_GETANIMATION = 72;\n\tinternal const int SPI_GETACTIVEWNDTRKZORDER = 4108;\n\tinternal const int SPI_GETACTIVEWNDTRKTIMEOUT = 8194;\n\tinternal const int SPI_GETACTIVEWINDOWTRACKING = 4096;\n\tinternal const int SPI_GETACCESSTIMEOUT = 60;\n\tinternal const int SPI_GETMOUSEWHEELROUTING = 0x201C;\n\t\n\tinternal const uint SPIF_UPDATEINIFILE = 0x1;\n\tinternal const uint SPIF_SENDCHANGE = 0x2;\n\t\n\t/// <summary>\n\t/// Gets or sets any value. This is the direct API call.\n\t/// </summary>\n\t[DllImport(\"user32.dll\", EntryPoint = \"SystemParametersInfoW\", SetLastError = true)]\n\tinternal static extern bool SystemParametersInfo(uint uiAction, int uiParam, void* pvParam, uint fWinIni = 0);\n\t\n\t/// <summary>\n\t/// Gets 32-bit integer value. Returns <i>def</i> if failed.\n\t/// </summary>\n\tinternal static int SystemParametersInfo(uint uiAction, int def) {\n\t\tint r = 0;\n\t\treturn SystemParametersInfo(uiAction, 0, &r) ? r : def;\n\t}\n\t\n\t/// <summary>\n\t/// Gets <c>BOOL</c> value. Returns <c>false</c> if failed.\n\t/// </summary>\n\tinternal static bool SystemParametersInfo(uint uiAction) {\n\t\tint r = 0;\n\t\treturn SystemParametersInfo(uiAction, 0, &r) && r != 0;\n\t}\n\t\n\t//internal static bool SystemParametersInfo<T>(uint uiAction, ref T pvParam) where T : unmanaged {\n\t//\tfixed (T* p = &pvParam) return SystemParametersInfo(uiAction, sizeof(T), p, 0);\n\t//}\n\t\n\t/// <summary>\n\t/// Sets value.\n\t/// </summary>\n\tinternal static bool SystemParametersInfo(uint uiAction, int uiParam, void* pvParam, bool save, bool notify) {\n\t\tuint f = 0;\n\t\tif (save) f |= 1; //SPIF_UPDATEINIFILE\n\t\tif (notify) f |= 2; //SPIF_SENDCHANGE\n\t\treturn SystemParametersInfo(uiAction, uiParam, pvParam, f);\n\t}\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool SystemParametersInfoForDpi(uint uiAction, int uiParam, void* pvParam, uint fWinIni, int dpi);\n\t\n\t#endregion\n\t\n\t/// <summary>\n\t/// <c>WindowFromPhysicalPoint</c>. On Win8.1+ it is the same as <c>WindowFromPoint</c>.\n\t/// </summary>\n\t[DllImport(\"user32.dll\", EntryPoint = \"WindowFromPhysicalPoint\")]\n\tinternal static extern wnd WindowFromPoint(POINT pt);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool ScreenToClient(wnd hWnd, ref POINT lpPoint);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool ClientToScreen(wnd hWnd, ref POINT lpPoint);\n\t\n\t//internal static bool ClientToScreenIgnoreRtl(wnd w, ref POINT p)\n\t//{\n\t//\tif(!w.HasExStyle(WSE.LAYOUTRTL)) return ClientToScreen(w, ref p);\n\t//\tif(!GetClientRect(w, out var r) || !MapWindowPoints(w, default, ref r, out _)) return false;\n\t//\tp.Offset(r.left, r.top);\n\t//\treturn true;\n\t//}\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tstatic extern int MapWindowPoints(wnd hWndFrom, wnd hWndTo, void* lpPoints, int cPoints);\n\t\n\tinternal static bool MapWindowPoints(wnd wFrom, wnd wTo, void* points, int cPoints, out int ret) {\n\t\tlastError.clear();\n\t\tret = MapWindowPoints(wFrom, wTo, points, cPoints);\n\t\treturn ret != 0 ? true : lastError.code == 0;\n\t}\n\t\n\tinternal static bool MapWindowPoints(wnd wFrom, wnd wTo, ref RECT r, out int ret) {\n\t\tfixed (void* u = &r) return MapWindowPoints(wFrom, wTo, u, 2, out ret);\n\t}\n\t\n\tinternal static bool MapWindowPoints(wnd wFrom, wnd wTo, ref POINT p, out int ret) {\n\t\tfixed (void* u = &p) return MapWindowPoints(wFrom, wTo, u, 1, out ret);\n\t}\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool GetGUIThreadInfo(int idThread, ref GUITHREADINFO pgui);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool AttachThreadInput(int idAttach, int idAttachTo, bool fAttach);\n\t\n\tinternal const uint KEYEVENTF_EXTENDEDKEY = 0x1;\n\tinternal const uint KEYEVENTF_KEYUP = 0x2;\n\tinternal const uint KEYEVENTF_UNICODE = 0x4;\n\tinternal const uint KEYEVENTF_SCANCODE = 0x8;\n\t\n\tinternal struct INPUTK {\n\t\tnint _type;\n\t\tpublic ushort wVk;\n\t\tpublic ushort wScan;\n\t\tpublic uint dwFlags;\n\t\tpublic int time;\n\t\tpublic nint dwExtraInfo;\n#pragma warning disable 414 //never used\n\t\tint _u1, _u2; //need INPUT size\n#pragma warning restore 414\n\t\t\n\t\tpublic INPUTK(KKey vk, ushort sc, uint flags = 0) {\n\t\t\t_type = INPUT_KEYBOARD; dwExtraInfo = AuExtraInfo;\n\t\t\twVk = (ushort)vk; wScan = sc; dwFlags = flags;\n\t\t\ttime = 0; _u2 = _u1 = 0;\n\t\t\tDebug.Assert(sizeof(INPUTK) == sizeof(INPUTM));\n\t\t}\n\t\t\n\t\tpublic void Set(KKey vk, ushort sc, uint flags = 0) {\n\t\t\t_type = INPUT_KEYBOARD; dwExtraInfo = AuExtraInfo;\n\t\t\twVk = (ushort)vk; wScan = sc; dwFlags = flags;\n\t\t}\n\t\t\n\t\t//public void InitCommonFields()\n\t\t//{\n\t\t//\t_type = INPUT_KEYBOARD; dwExtraInfo = AuExtraInfo;\n\t\t//}\n\t\t\n\t\tconst int INPUT_KEYBOARD = 1;\n\t}\n\t\n\t[Flags]\n\tinternal enum IMFlags : uint {\n\t\tMove = 1,\n\t\tLeftDown = 2, LeftUp = 4,\n\t\tRightDown = 8, RightUp = 16,\n\t\tMiddleDown = 32, MiddleUp = 64,\n\t\tXDown = 0x80, XUp = 0x100,\n\t\tWheel = 0x0800, HWheel = 0x01000,\n\t\tNoCoalesce = 0x2000,\n\t\tVirtualdDesktop = 0x4000,\n\t\tAbsolute = 0x8000,\n\t\t//not API\n\t\tX1 = 0x1000000,\n\t\tX2 = 0x2000000,\n\t};\n\t\n\tinternal struct INPUTM {\n\t\tnint _type;\n\t\tpublic int dx;\n\t\tpublic int dy;\n\t\tpublic int mouseData;\n\t\tpublic IMFlags dwFlags;\n\t\tpublic int time;\n\t\tpublic nint dwExtraInfo;\n\t\t\n\t\tpublic INPUTM(IMFlags flags, int x = 0, int y = 0, int data = 0) {\n\t\t\t_type = INPUT_MOUSE;\n\t\t\tdx = x; dy = y; dwFlags = flags; mouseData = data;\n\t\t\ttime = 0; dwExtraInfo = AuExtraInfo;\n\t\t}\n\t\t\n\t\tconst int INPUT_MOUSE = 0;\n\t}\n\t\n\t/// <summary>\n\t/// Extra info value of key and mouse events sent by functions of this library.\n\t/// </summary>\n\tinternal const int AuExtraInfo = 0x71427fa5;\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tstatic extern int SendInput(int cInputs, void* pInputs, int cbSize);\n\t//tested: Returns 0 when: invalid argument; other desktop active.\n\t//\tDoes not return 0 when: UAC (documented); BlockInput; ClipCursor.\n\t\n\t/// <exception cref=\"InputDesktopException\"></exception>\n\tinternal static void SendInput(INPUTK* ip, int n = 1, bool dontThrow = false) {\n\t\tif (n != SendInput(n, ip, sizeof(INPUTK))) {\n\t\t\tif (!dontThrow) InputDesktopException.ThrowIfBadDesktop(\"*send keyboard input.\");\n\t\t\t//tested: if bad input desktop, GetLastError returns 'access denied'.\n\t\t\t\n\t\t\t//throw new AuException(\"*send keyboard input.\"); //rejected. Anyway in most cases cannot detect when fails (because API returns not 0).\n\t\t\tDebug_.Print($\"SendInput(key) failed. {lastError.message}\");\n\t\t}\n\t}\n\t\n\t/// <exception cref=\"InputDesktopException\"></exception>\n\tinternal static void SendInput(INPUTM* ip, int n = 1, bool dontThrow = false) {\n\t\tif (n != SendInput(n, ip, sizeof(INPUTM))) {\n\t\t\tif (!dontThrow) InputDesktopException.ThrowIfBadDesktop(\"*send mouse input.\");\n\t\t\t\n\t\t\t//throw new AuException(\"*send mouse input.\");\n\t\t\tDebug_.Print($\"SendInput(mouse) failed. {lastError.message}\");\n\t\t}\n\t}\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool IsHungAppWindow(wnd hwnd);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool SetLayeredWindowAttributes(wnd hwnd, uint crKey, byte bAlpha, uint dwFlags);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool GetLayeredWindowAttributes(wnd hwnd, out uint pcrKey, out byte pbAlpha, out uint pdwFlags);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern IntPtr CreateIcon(IntPtr hInstance, int nWidth, int nHeight, byte cPlanes, byte cBitsPixel, byte[] lpbANDbits, byte[] lpbXORbits);\n\t\n\t[DllImport(\"user32.dll\", EntryPoint = \"LoadCursorW\", SetLastError = true)]\n\tinternal static extern IntPtr LoadCursor(IntPtr hInstance, MCursor cursorId);\n\t\n\tinternal delegate void TIMERPROC(wnd param1, int param2, nint param3, uint param4);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern nint SetTimer(wnd hWnd, nint nIDEvent, int uElapse, TIMERPROC lpTimerFunc);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool KillTimer(wnd hWnd, nint uIDEvent);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern wnd SetParent(wnd hWndChild, wnd hWndNewParent);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool AdjustWindowRectEx(ref RECT lpRect, WS dwStyle, bool bMenu, WSE dwExStyle);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool AdjustWindowRectExForDpi(ref RECT lpRect, WS dwStyle, bool bMenu, WSE dwExStyle, int dpi);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool ChangeWindowMessageFilter(int message, uint dwFlag);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern short GetKeyState(int nVirtKey); //not KKey because it is :byte\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern short GetAsyncKeyState(int vKey); //not KKey because it is :byte\n\t\n\tinternal const uint MOD_ALT = 0x1;\n\tinternal const uint MOD_CONTROL = 0x2;\n\tinternal const uint MOD_SHIFT = 0x4;\n\tinternal const uint MOD_WIN = 0x8;\n\tinternal const uint MOD_NOREPEAT = 0x4000;\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool RegisterHotKey(wnd hWnd, int id, uint fsModifiers, KKey vk);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool UnregisterHotKey(wnd hWnd, int id);\n\t\n\tinternal const uint MWMO_WAITALL = 0x1;\n\tinternal const uint MWMO_ALERTABLE = 0x2;\n\tinternal const uint MWMO_INPUTAVAILABLE = 0x4;\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern int MsgWaitForMultipleObjectsEx(int nCount, IntPtr* pHandles, int dwMilliseconds, uint dwWakeMask, uint MWMO_Flags);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool InvalidateRect(wnd hWnd, RECT* lpRect, bool bErase);\n\tinternal static bool InvalidateRect(wnd hWnd, bool bErase = false) => InvalidateRect(hWnd, null, bErase);\n\tinternal static bool InvalidateRect(wnd hWnd, RECT r, bool bErase = false) => InvalidateRect(hWnd, &r, bErase);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool ValidateRect(wnd hWnd, RECT* lpRect);\n\tinternal static bool ValidateRect(wnd hWnd) => ValidateRect(hWnd, null);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool GetUpdateRect(wnd hWnd, out RECT lpRect, bool bErase);\n\t\n\tinternal const int ERROR = 0;\n\tinternal const int NULLREGION = 1;\n\tinternal const int SIMPLEREGION = 2;\n\tinternal const int COMPLEXREGION = 3;\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern int GetUpdateRgn(wnd hWnd, IntPtr hRgn, bool bErase);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool InvalidateRgn(wnd hWnd, IntPtr hRgn, bool bErase);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool DragDetect(wnd hwnd, POINT pt);\n\t\n\t[DllImport(\"user32.dll\")]\n\tinternal static extern IntPtr GetCursor();\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern IntPtr SetCursor(IntPtr hCursor);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern wnd SetCapture(wnd hWnd);\n\t\n\t[DllImport(\"user32.dll\")]\n\tinternal static extern wnd GetCapture();\n\t\n\t[DllImport(\"user32.dll\")]\n\tinternal static extern bool ReleaseCapture();\n\t\n\t//[DllImport(\"user32.dll\", EntryPoint = \"CharLowerBuffW\")]\n\t//internal static unsafe extern int CharLowerBuff(char* lpsz, int cchLength);\n\t\n\t//[DllImport(\"user32.dll\", CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern int wsprintfW(char* lpOut1024, string lpFmt, __arglist);\n\t//note: with __arglist always returns 0. Could instead use void*, but then much work to properly pack arguments.\n\t\n\t//tested speed (time %) of various formatting functions, with two int and one string arg, with converting to string:\n\t//\tStringBuilder.Append + int.ToString(CultureInfo.InvariantCulture): 85% - FASTEST\n\t//\tStringBuilder.Append: 100%\n\t//\tStringBuilder.AppendFormat + int.ToString() (avoid int boxing): 140%\n\t//\tStringBuilder.AppendFormat: 150%\n\t//\t$\"{var} string\": 160% (probably uses StringBuilder)\n\t//\twsprintfW (user32.dll): 150%\n\t//\t_snwprintf (msvcrt.dll): 500% - SLOWEST\n\t\n\t[DllImport(\"user32.dll\", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]\n\tinternal static extern int wsprintfA(byte* lpOut1024, string lpFmt, __arglist);\n\t\n\t[DllImport(\"user32.dll\", CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern int wsprintfW(char* lpOut1024, string lpFmt, __arglist);\n\t\n\tinternal struct PAINTSTRUCT {\n\t\tpublic IntPtr hdc;\n\t\tpublic bool fErase;\n\t\tpublic RECT rcPaint;\n\t\tpublic bool fRestore;\n\t\tpublic bool fIncUpdate;\n\t\tfixed byte rgbReserved[32];\n\t}\n\t\n\t[DllImport(\"user32.dll\")]\n\tinternal static extern IntPtr BeginPaint(wnd hWnd, out PAINTSTRUCT lpPaint);\n\t\n\t[DllImport(\"user32.dll\")]\n\tinternal static extern bool EndPaint(wnd hWnd, in PAINTSTRUCT lpPaint);\n\t\n\t[DllImport(\"user32.dll\")]\n\tinternal static extern bool UpdateWindow(wnd hWnd);\n\t\n\t[DllImport(\"user32.dll\")]\n\tinternal static extern nint GetKeyboardLayout(int idThread);\n\t\n\t[DllImport(\"user32.dll\", EntryPoint = \"MapVirtualKeyExW\")]\n\tinternal static extern uint MapVirtualKeyEx(uint uCode, uint uMapType, nint dwhkl);\n\t\n\t[DllImport(\"user32.dll\", EntryPoint = \"VkKeyScanExW\")]\n\tinternal static extern short VkKeyScanEx(char ch, nint dwhkl);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool OpenClipboard(wnd hWndNewOwner);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool CloseClipboard();\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool EmptyClipboard();\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern IntPtr SetClipboardData(int uFormat, IntPtr hMem);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern IntPtr GetClipboardData(int uFormat);\n\t\n\t//[DllImport(\"user32.dll\", SetLastError = true)]\n\t//internal static extern wnd SetClipboardViewer(wnd hWndNewViewer);\n\t\n\t//[DllImport(\"user32.dll\")]\n\t//internal static extern bool ChangeClipboardChain(wnd hWndRemove, wnd hWndNewNext);\n\t\n\t[DllImport(\"user32.dll\")]\n\tinternal static extern wnd GetOpenClipboardWindow();\n\t\n\t[DllImport(\"user32.dll\")]\n\tinternal static extern uint GetClipboardSequenceNumber();\n\t\n\t[DllImport(\"user32.dll\", EntryPoint = \"RegisterClipboardFormatW\")]\n\tinternal static extern int RegisterClipboardFormat(string lpszFormat);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool AddClipboardFormatListener(wnd hwnd);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool RemoveClipboardFormatListener(wnd hwnd);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern int EnumClipboardFormats(int format);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool IsClipboardFormatAvailable(int format);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern int GetPriorityClipboardFormat(int[] paFormatPriorityList, int cFormats);\n\t\n\t[DllImport(\"user32.dll\", EntryPoint = \"GetClipboardFormatNameW\", SetLastError = true)]\n\tinternal static unsafe extern int GetClipboardFormatName(int format, char* lpszFormatName, int cchMaxCount);\n\t\n\t[DllImport(\"user32.dll\")]\n\tinternal static extern int GetDoubleClickTime();\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool DrawIconEx(IntPtr hdc, int xLeft, int yTop, IntPtr hIcon, int cxWidth, int cyWidth, int istepIfAniCur = 0, IntPtr hbrFlickerFreeDraw = default, uint diFlags = 3); //DI_NORMAL\n\t\n\tinternal const uint CURSOR_SHOWING = 0x1;\n\t\n\tinternal struct CURSORINFO {\n\t\tpublic int cbSize;\n\t\tpublic uint flags;\n\t\tpublic IntPtr hCursor;\n\t\tpublic POINT ptScreenPos;\n\t}\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool GetCursorInfo(ref CURSORINFO pci);\n\t\n\tinternal struct ICONINFO : IDisposable {\n\t\tpublic bool fIcon;\n\t\tpublic int xHotspot;\n\t\tpublic int yHotspot;\n\t\tpublic IntPtr hbmMask;\n\t\tpublic IntPtr hbmColor;\n\t\t\n\t\tpublic ICONINFO(IntPtr hIcon) {\n\t\t\tGetIconInfo(hIcon, out this);\n\t\t\t//never mind if failed. Then hbm members are default, and caller can either check it or simply let other API fail.\n\t\t}\n\t\t\n\t\tpublic void Dispose() {\n\t\t\tif (hbmMask != default) DeleteObject(hbmMask);\n\t\t\tif (hbmColor != default) DeleteObject(hbmColor);\n\t\t}\n\t}\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool GetIconInfo(IntPtr hIcon, out ICONINFO piconinfo);\n\t//tested: GetIconInfoEx gets resource info only for icons loaded from a module loaded in this process.\n\t\n\tinternal struct BITMAP {\n\t\tpublic int bmType;\n\t\tpublic int bmWidth;\n\t\tpublic int bmHeight;\n\t\tpublic int bmWidthBytes;\n\t\tpublic ushort bmPlanes;\n\t\tpublic ushort bmBitsPixel;\n\t\tpublic IntPtr bmBits;\n\t}\n\t\n\tinternal const int WH_MSGFILTER = -1;\n\tinternal const int WH_KEYBOARD = 2;\n\tinternal const int WH_GETMESSAGE = 3;\n\tinternal const int WH_CALLWNDPROC = 4;\n\tinternal const int WH_CBT = 5;\n\t//internal const int WH_SYSMSGFILTER = 6; //hook proc must be in dll\n\tinternal const int WH_MOUSE = 7;\n\tinternal const int WH_DEBUG = 9;\n\tinternal const int WH_SHELL = 10;\n\tinternal const int WH_FOREGROUNDIDLE = 11;\n\tinternal const int WH_CALLWNDPROCRET = 12;\n\tinternal const int WH_KEYBOARD_LL = 13;\n\tinternal const int WH_MOUSE_LL = 14;\n\t\n\tinternal delegate nint HOOKPROC(int code, nint wParam, nint lParam);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern IntPtr SetWindowsHookEx(int WH_X, HOOKPROC lpfn, IntPtr hMod, int dwThreadId);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool UnhookWindowsHookEx(IntPtr hhk);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern nint CallNextHookEx(IntPtr hhk, int nCode, nint wParam, nint lParam);\n\t\n\tinternal const uint LLKHF_EXTENDED = 0x1;\n\tinternal const uint LLKHF_INJECTED = 0x10;\n\tinternal const uint LLKHF_ALTDOWN = 0x20;\n\tinternal const uint LLKHF_UP = 0x80;\n\t\n\tinternal struct KBDLLHOOKSTRUCT {\n\t\tpublic uint vkCode;\n\t\tpublic uint scanCode;\n\t\tpublic uint flags;\n\t\tpublic int time;\n\t\tpublic nint dwExtraInfo;\n\t\t\n\t\tpublic bool IsUp => 0 != (flags & LLKHF_UP);\n\t\t\n\t\t/// <summary>\n\t\t/// <c>true</c> if the event was generated by software.\n\t\t/// </summary>\n\t\tpublic bool IsInjected => 0 != (flags & LLKHF_INJECTED);\n\t\t\n\t\t/// <summary>\n\t\t/// <c>true</c> if the event was generated by functions of this library.\n\t\t/// </summary>\n\t\tpublic bool IsInjectedByAu => 0 != (flags & LLKHF_INJECTED) && dwExtraInfo == AuExtraInfo;\n\t\t//CONSIDER: also add IsInjectedByAuOrQM2. And let QM2 recognize the extra info too. Or let they use the same extra info; probably bad idea.\n\t\t\n\t\t/// <summary>\n\t\t/// The <c>set</c> function adds or removes flag 0x80000000.\n\t\t/// The <c>get</c> function returns <c>true</c> if flag 0x80000000 is set.\n\t\t/// </summary>\n\t\tpublic bool BlockEvent {\n\t\t\tget => 0 != (flags & 0x80000000);\n\t\t\tset { if (value) flags |= 0x80000000; else flags &= ~0x80000000; }\n\t\t}\n\t}\n\t\n\tinternal const uint LLMHF_INJECTED = 0x1;\n\t\n\tinternal struct MSLLHOOKSTRUCT {\n\t\tpublic POINT pt;\n\t\tpublic uint mouseData;\n\t\tpublic uint flags;\n\t\tpublic int time;\n\t\tpublic nint dwExtraInfo;\n\t\t\n\t\t/// <summary>\n\t\t/// <c>true</c> if the event was generated by software.\n\t\t/// </summary>\n\t\tpublic bool IsInjected => 0 != (flags & LLMHF_INJECTED);\n\t\t\n\t\t/// <summary>\n\t\t/// <c>true</c> if the event was generated by functions of this library.\n\t\t/// </summary>\n\t\tpublic bool IsInjectedByAu => 0 != (flags & LLMHF_INJECTED) && dwExtraInfo == AuExtraInfo;\n\t\t\n\t\t/// <summary>\n\t\t/// The <c>set</c> function adds or removes flag 0x80000000.\n\t\t/// The <c>get</c> function returns <c>true</c> if flag 0x80000000 is set.\n\t\t/// </summary>\n\t\tpublic bool BlockEvent {\n\t\t\tget => 0 != (flags & 0x80000000);\n\t\t\tset { if (value) flags |= 0x80000000; else flags &= ~0x80000000; }\n\t\t}\n\t}\n\t\n\tinternal const int HC_NOREMOVE = 3;\n\t\n\tinternal delegate void WINEVENTPROC(IntPtr hWinEventHook, EEvent event_, wnd hwnd, EObjid idObject, int idChild, int idEventThread, int dwmsEventTime);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern IntPtr SetWinEventHook(EEvent eventMin, EEvent eventMax, IntPtr hmodWinEventProc, WINEVENTPROC pfnWinEventProc, int idProcess, int idThread, EHookFlags dwFlags);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool UnhookWinEvent(IntPtr hWinEventHook);\n\t\n\t[Flags]\n\tinternal enum AnimationFlags : uint {\n\t\tRoll = 0x0000, // Uses a roll animation.\n\t\tHorizontalPositive = 0x00001, // Animates the window from left to right. This flag can be used with roll or slide animation.\n\t\tHorizontalNegative = 0x00002, // Animates the window from right to left. This flag can be used with roll or slide animation.\n\t\tVerticalPositive = 0x00004, // Animates the window from top to bottom. This flag can be used with roll or slide animation.\n\t\tVerticalNegative = 0x00008, // Animates the window from bottom to top. This flag can be used with roll or slide animation.\n\t\tCenter = 0x00010, // Makes the window appear to collapse inward if Hide is used or expand outward if the Hide is not used.\n\t\tHide = 0x10000, // Hides the window. By default, the window is shown.\n\t\tActivate = 0x20000, // Activates the window.\n\t\tSlide = 0x40000, // Uses a slide animation. By default, roll animation is used.\n\t\tBlend = 0x80000, // Uses a fade effect. This flag can be used only with a top-level window.\n\t\tMask = 0xfffff,\n\t}\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool AnimateWindow(wnd hWnd, int dwTime, AnimationFlags dwFlags);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool GetCaretPos(out POINT lpPoint);\n\t\n\tinternal static bool GetCaretPosInScreen_(out POINT p) => GetCaretPos(out p) && GetFocus().MapClientToScreen(ref p);\n\t\n\t[DllImport(\"user32.dll\")]\n\tinternal static extern int ToUnicodeEx(uint wVirtKey, uint wScanCode, byte* lpKeyState, char* pwszBuff, int cchBuff, uint wFlags, IntPtr dwhkl);\n\t\n\tinternal const uint PW_CLIENTONLY = 0x1;\n\tinternal const uint PW_RENDERFULLCONTENT = 0x2;\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool PrintWindow(wnd hwnd, IntPtr hdcBlt, uint nFlags);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern IntPtr GetDC(wnd hWnd);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern IntPtr GetWindowDC(wnd hWnd);\n\t\n\t[DllImport(\"user32.dll\")] //note: no SetLastError = true\n\tinternal static extern int ReleaseDC(wnd hWnd, IntPtr hDC);\n\t\n\t[DllImport(\"user32.dll\")]\n\tinternal static extern int FillRect(IntPtr hDC, in RECT lprc, nint hbr);\n\t\n\t[DllImport(\"user32.dll\")]\n\tinternal static extern int FrameRect(IntPtr hDC, in RECT lprc, IntPtr hbr);\n\t\n\tinternal const uint RDW_INVALIDATE = 0x1;\n\tinternal const uint RDW_ERASE = 0x4;\n\tinternal const uint RDW_ALLCHILDREN = 0x80;\n\tinternal const uint RDW_FRAME = 0x400;\n\t\n\t[DllImport(\"user32.dll\")]\n\tinternal static extern bool RedrawWindow(wnd hWnd, RECT* lprcUpdate = null, IntPtr hrgnUpdate = default, uint flags = 0);\n\t\n\t/// <param name=\"flags\"><c>Au.Controls.PopupAlignment</c></param>\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool CalculatePopupWindowPosition(in POINT anchorPoint, in SIZE windowSize, uint flags, in RECT excludeRect, out RECT popupWindowPosition);\n\t\n\t[DllImport(\"user32.dll\")]\n\tinternal static extern int MenuItemFromPoint(wnd hWnd, IntPtr hMenu, POINT ptScreen);\n\t\n\t[DllImport(\"user32.dll\")]\n\tinternal static extern int GetMenuItemID(IntPtr hMenu, int nPos);\n\t\n\tinternal struct MENUITEMINFO {\n\t\tpublic int cbSize;\n\t\tpublic uint fMask;\n\t\tpublic uint fType;\n\t\tpublic uint fState;\n\t\tpublic int wID;\n\t\tpublic IntPtr hSubMenu;\n\t\tpublic IntPtr hbmpChecked;\n\t\tpublic IntPtr hbmpUnchecked;\n\t\tpublic nint dwItemData;\n\t\tpublic char* dwTypeData;\n\t\tpublic int cch;\n\t\tpublic IntPtr hbmpItem;\n\t\t\n\t\tpublic MENUITEMINFO(uint miim) {\n\t\t\tcbSize = sizeof(MENUITEMINFO);\n\t\t\tfMask = miim;\n\t\t}\n\t}\n\t\n\t[DllImport(\"user32.dll\", EntryPoint = \"GetMenuItemInfoW\")]\n\tinternal static extern bool GetMenuItemInfo(IntPtr hmenu, int item, bool fByPosition, ref MENUITEMINFO lpmii);\n\t\n\tinternal const uint MIIM_STRING = 0x40;\n\t\n\t[DllImport(\"user32.dll\")]\n\tinternal static extern IntPtr GetSystemMenu(wnd hWnd, bool bRevert);\n\t\n\t[DllImport(\"user32.dll\")]\n\tinternal static extern bool EnableMenuItem(IntPtr hMenu, uint uIDEnableItem, uint uEnable);\n\t\n\tinternal const uint MF_GRAYED = 0x1;\n\t\n\tinternal const uint SIF_RANGE = 0x1;\n\tinternal const uint SIF_PAGE = 0x2;\n\tinternal const uint SIF_POS = 0x4;\n\tinternal const uint SIF_TRACKPOS = 0x10;\n\tinternal const int SB_LINEUP = 0;\n\t//internal const int SB_LINELEFT = 0;\n\tinternal const int SB_LINEDOWN = 1;\n\t//internal const int SB_LINERIGHT = 1;\n\tinternal const int SB_PAGEUP = 2;\n\t//internal const int SB_PAGELEFT = 2;\n\tinternal const int SB_PAGEDOWN = 3;\n\t//internal const int SB_PAGERIGHT = 3;\n\t//internal const int SB_THUMBPOSITION = 4;\n\tinternal const int SB_THUMBTRACK = 5;\n\tinternal const int SB_TOP = 6;\n\t//internal const int SB_LEFT = 6;\n\tinternal const int SB_BOTTOM = 7;\n\t//internal const int SB_RIGHT = 7;\n\t//internal const int SB_ENDSCROLL = 8;\n\tinternal const int SB_HORZ = 0;\n\tinternal const int SB_VERT = 1;\n\t//internal const int SB_CTL = 2;\n\tinternal const int SB_BOTH = 3;\n\t\n\tinternal struct SCROLLINFO {\n\t\tpublic int cbSize;\n\t\tpublic uint fMask;\n\t\tpublic int nMin;\n\t\tpublic int nMax;\n\t\tpublic int nPage;\n\t\tpublic int nPos;\n\t\tpublic int nTrackPos;\n\t\t\n\t\tpublic SCROLLINFO(uint mask) {\n\t\t\tcbSize = sizeof(SCROLLINFO);\n\t\t\tfMask = mask;\n\t\t}\n\t\t\n\t\tpublic int Set(wnd w, bool vertical, bool redraw = true)\n\t\t\t=> SetScrollInfo(w, vertical ? SB_VERT : SB_HORZ, this, redraw);\n\t\t\n\t\tpublic bool Get(wnd w, bool vertical)\n\t\t\t=> GetScrollInfo(w, vertical ? SB_VERT : SB_HORZ, ref this);\n\t\t\n\t\tpublic static SCROLLINFO Get(wnd w, bool vertical, uint mask) {\n\t\t\tSCROLLINFO v = new(mask);\n\t\t\tv.Get(w, vertical);\n\t\t\treturn v;\n\t\t}\n\t\t\n\t\tpublic static int GetTrackPos(wnd w, bool vertical) => Get(w, vertical, SIF_TRACKPOS).nTrackPos;\n\t\t\n\t\tpublic static void SetPos(wnd w, bool vertical, int pos, bool redraw = true) {\n\t\t\tnew SCROLLINFO(SIF_POS) { nPos = pos }.Set(w, vertical, redraw);\n\t\t}\n\t\t\n\t\tpublic static void SetRange(wnd w, bool vertical, int max, int page, bool redraw = true) {\n\t\t\tnew SCROLLINFO(SIF_RANGE | SIF_PAGE) { nMax = max, nPage = page }.Set(w, vertical, redraw);\n\t\t}\n\t}\n\t\n\t[DllImport(\"user32.dll\")]\n\tinternal static extern int SetScrollInfo(wnd hwnd, int nBar, in SCROLLINFO lpsi, bool redraw);\n\t\n\t[DllImport(\"user32.dll\")]\n\tinternal static extern bool GetScrollInfo(wnd hwnd, int nBar, ref SCROLLINFO lpsi);\n\t\n\t[DllImport(\"user32.dll\")]\n\tinternal static extern bool ShowScrollBar(wnd hWnd, int wBar, bool bShow);\n\t\n\t//[DllImport(\"user32.dll\", SetLastError = true)]\n\t//internal static extern bool GetScrollBarInfo(wnd hwnd, EObjid idObject, ref SCROLLBARINFO psbi);\n\t\n\t//internal struct SCROLLBARINFO\n\t//{\n\t//\tpublic int cbSize;\n\t//\tpublic RECT rcScrollBar;\n\t//\tpublic int dxyLineButton;\n\t//\tpublic int xyThumbTop;\n\t//\tpublic int xyThumbBottom;\n\t//\tpublic int reserved;\n\t//\t//public fixed uint rgstate[6];\n\t//\tpublic uint stateScrollbar, stateArrowTopRight, statePageUpRight, stateThumb, statePageDownLeft, stateArrowBottomLeft;\n\t//}\n\t//internal const uint STATE_SYSTEM_INVISIBLE = 0x8000;\n\t//internal const uint STATE_SYSTEM_OFFSCREEN = 0x10000;\n\t//internal const uint STATE_SYSTEM_PRESSED = 0x8;\n\t//internal const uint STATE_SYSTEM_UNAVAILABLE = 0x1;\n\t\n\t[DllImport(\"user32.dll\", EntryPoint = \"MessageBoxW\")]\n\tinternal static extern int MessageBox(wnd hWnd, string lpText, string lpCaption, uint uType);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern int GetWindowRgn(wnd hWnd, IntPtr hRgn);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern int GetDpiForWindow(wnd hWnd);\n\t\n\t[DllImport(\"user32.dll\")]\n\tinternal static extern IntPtr GetWindowDpiAwarenessContext(wnd hwnd);\n\t\n\t[DllImport(\"user32.dll\")]\n\tinternal static extern Dpi.Awareness GetAwarenessFromDpiAwarenessContext(IntPtr value);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern nint SetThreadDpiAwarenessContext(nint dpiContext);\n\t\n\t[DllImport(\"shcore.dll\")]\n\tinternal static extern int GetDpiForMonitor(IntPtr hmonitor, int dpiType, out int dpiX, out int dpiY);\n\t\n\t[DllImport(\"shcore.dll\")]\n\tinternal static extern int GetProcessDpiAwareness(IntPtr hprocess, out Dpi.Awareness value); //Dpi.Awareness is PROCESS_DPI_AWARENESS\n\t\n\t[DllImport(\"user32.dll\")]\n\tinternal static extern bool PhysicalToLogicalPointForPerMonitorDPI(wnd hWnd, ref POINT lpPoint);\n\t\n\t//[DllImport(\"user32.dll\")]\n\t//internal static extern bool PhysicalToLogicalPoint(wnd hWnd, ref POINT lpPoint);\n\t\n\t//internal static bool PhysicalToLogicalPoint_AnyOS(wnd w, ref POINT p) => osVersion.minWin8_1 ? PhysicalToLogicalPointForPerMonitorDPI(w, ref p) : PhysicalToLogicalPoint(w, ref p);\n\t\n\t//[DllImport(\"user32.dll\")]\n\t//internal static extern bool LogicalToPhysicalPointForPerMonitorDPI(wnd hWnd, ref POINT lpPoint);\n\t\n\t[DllImport(\"user32.dll\")]\n\tinternal static extern bool LogicalToPhysicalPoint(wnd hWnd, ref POINT lpPoint);\n\t\n\t//internal static bool LogicalToPhysicalPoint_AnyOS(wnd w, ref POINT p) => osVersion.minWin8_1 ? LogicalToPhysicalPointForPerMonitorDPI(w, ref p) : LogicalToPhysicalPoint(w, ref p);\n\t\n\t[DllImport(\"user32.dll\")]\n\tinternal static extern int GetSysColor(int nIndex);\n\t\n\t[DllImport(\"user32.dll\")]\n\tinternal static extern IntPtr GetSysColorBrush(int nIndex);\n\t\n\t//internal struct DRAWTEXTPARAMS\n\t//{\n\t//\tpublic int cbSize;\n\t//\tpublic int iTabLength;\n\t//\tpublic int iLeftMargin;\n\t//\tpublic int iRightMargin;\n\t//\tpublic int uiLengthDrawn;\n\t//}\n\t\n\t//[DllImport(\"user32.dll\", SetLastError = true)]\n\t//static extern int DrawTextExW(IntPtr hdc, char* lpchText, int cchText, ref RECT lprc, TFFlags format, DRAWTEXTPARAMS* lpdtp);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tstatic extern int DrawTextExW(IntPtr hdc, char* lpchText, int cchText, ref RECT lprc, TFFlags format, void* lpdtp);\n\t\n\tinternal static int DrawText(IntPtr hdc, RStr s, ref RECT lprc, TFFlags format) {\n\t\tif (format.Has(TFFlags.MODIFYSTRING)) throw new NotSupportedException(\"MODIFYSTRING\");\n\t\tfixed (char* p = s) return DrawTextExW(hdc, p, s.Length, ref lprc, format, null);\n\t\t\n\t\t//DRAWTEXTPARAMS doc incorrect. Left nad right margin fields are in pixels, not average char widths. Not tested tab width.\n\t}\n\t\n\tinternal const WS TTS_ALWAYSTIP = (WS)0x1;\n\tinternal const WS TTS_NOPREFIX = (WS)0x2;\n\tinternal const WS TTS_BALLOON = (WS)0x40;\n\tinternal const int TTM_ACTIVATE = 0x401;\n\tinternal const int TTM_SETMAXTIPWIDTH = 0x418;\n\tinternal const int TTM_ADDTOOL = 0x432;\n\tinternal const int TTM_DELTOOL = 0x433;\n\tinternal const int TTM_RELAYEVENT = 0x407;\n\t//internal const uint TTF_SUBCLASS = 0x10;\n\t\n\tinternal struct TTTOOLINFO {\n\t\tpublic int cbSize;\n\t\tpublic uint uFlags;\n\t\tpublic wnd hwnd;\n\t\tpublic nint uId;\n\t\tpublic RECT rect;\n\t\tpublic IntPtr hinst;\n\t\tpublic char* lpszText;\n\t\tpublic nint lParam;\n\t\tpublic void* lpReserved;\n\t}\n\t\n\t[DllImport(\"user32.dll\")]\n\tinternal static extern bool DrawEdge(IntPtr hdc, ref RECT qrc, uint edge, uint grfFlags);\n\t\n\tinternal const uint EDGE_ETCHED = 0x6;\n\tinternal const uint BF_LEFT = 0x1;\n\tinternal const uint BF_TOP = 0x2;\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern IntPtr GetThreadDesktop(int dwThreadId);\n\t\n\t[DllImport(\"user32.dll\", EntryPoint = \"GetUserObjectInformationW\", SetLastError = true)]\n\tinternal static extern bool GetUserObjectInformation(IntPtr hObj, int nIndex, void* pvInfo, int nLength, out int lpnLengthNeeded);\n\t\n\tinternal const int UOI_IO = 6;\n\t//internal const int UOI_NAME = 2;\n\t\n\t//[DllImport(\"user32.dll\", SetLastError = true)]\n\t//internal static extern IntPtr OpenInputDesktop(uint dwFlags, bool fInherit, uint dwDesiredAccess);\n\t\n\t//[DllImport(\"user32.dll\")]\n\t//internal static extern bool CloseDesktop(IntPtr hDesktop);\n\t\n\tinternal const uint ISMEX_SEND = 0x1;\n\tinternal const uint ISMEX_REPLIED = 0x8;\n\t\n\t[DllImport(\"user32.dll\")]\n\tinternal static extern uint InSendMessageEx(nint lpReserved = 0);\n\t\n\tpublic static bool InSendMessageBlocked => (InSendMessageEx() & (ISMEX_SEND | ISMEX_REPLIED)) == ISMEX_SEND;\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool ExitWindowsEx(int uFlags, uint dwReason);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool LockWorkStation();\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern nint RegisterSuspendResumeNotification(IntPtr hRecipient, uint Flags);\n\t\n\tinternal struct DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS {\n\t\tpublic delegate* unmanaged<void*, int, void*, int> Callback;\n\t\tpublic void* Context;\n\t}\n\t\n\t//internal const uint DEVICE_NOTIFY_WINDOW_HANDLE = 0x0;\n\t//internal const int DEVICE_NOTIFY_CALLBACK = 2;\n\t\n\t[DllImport(\"user32.dll\")]\n\tinternal static extern bool UnregisterSuspendResumeNotification(nint Handle);\n\t\n\t//internal struct DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS {\n\t//\tpublic IntPtr Callback; //DEVICE_NOTIFY_CALLBACK_ROUTINE\n\t//\tpublic nint Context;\n\t//}\n\t\n\t//internal delegate uint DEVICE_NOTIFY_CALLBACK_ROUTINE(nint context, uint type, nint setting);\n\t\n\t[DllImport(\"user32.dll\", SetLastError = true)]\n\tinternal static extern bool ClipCursor(RECT* lpRect);\n\t\n\t[DllImport(\"user32.dll\")]\n\tstatic extern bool GetLastInputInfo(ref LASTINPUTINFO plii);\n\t\n\tstruct LASTINPUTINFO {\n\t\tpublic int cbSize;\n\t\tpublic uint dwTime;\n\t}\n\t\n\t/// <summary>\n\t/// Calls API <see cref=\"GetLastInputInfo\"/> and makes the result 64-bit.\n\t/// </summary>\n\tinternal static long GetLastInputTime() {\n\t\tvar r = new LASTINPUTINFO { cbSize = 8 };\n\t\tif (!GetLastInputInfo(ref r)) return 0;\n\t\treturn TickCount32To64(r.dwTime);\n\t}\n\t\n\t/// <summary>\n\t/// Extends a 32-bit tick count (like from many Windows API) into the 64-bit tick count space.\n\t/// </summary>\n\t/// <param name=\"tick32\">32-bit time, like from API <c>GetTickCount</c>. Must not be in the future.</param>\n\tinternal static long TickCount32To64(uint tick32) {\n\t\tlong now64 = Environment.TickCount64;\n\t\tlong r = (now64 & ~0xffffffffL) | tick32;\n\t\tif (r > now64) r -= 0x1_0000_0000L;\n\t\treturn r;\n\t}\n}\n\n"
  },
  {
    "path": "Au/Api/Api_COM.cs",
    "content": "namespace Au.Types;\n\nstatic unsafe partial class Api {\n\tinternal struct STRRET {\n\t\tpublic uint uType;\n\n\t\t[StructLayout(LayoutKind.Explicit)]\n\t\tinternal struct TYPE_1 {\n\t\t\t[FieldOffset(0)]\n\t\t\tpublic char* pOleStr;\n\t\t\t[FieldOffset(0)]\n\t\t\tpublic uint uOffset;\n\t\t\t[FieldOffset(0)]\n\t\t\tpublic fixed sbyte cStr[260];\n\t\t}\n\t\tpublic TYPE_1 _2;\n\t}\n\n\t//internal static Guid IID_IShellFolder = new Guid(0x000214E6, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46);\n\n\t[ComImport, Guid(\"000214E6-0000-0000-C000-000000000046\"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\n\tinternal interface IShellFolder {\n\t\t//[PreserveSig] int ParseDisplayName(wnd hwnd, IntPtr pbc, [MarshalAs(UnmanagedType.LPWStr)] string pszDisplayName, uint* pchEaten, out IntPtr ppidl, uint* pdwAttributes);\n\t\t//[PreserveSig] int EnumObjects(wnd hwnd, uint grfFlags, out IEnumIDList ppenumIDList);\n\t\t//[PreserveSig] int BindToObject(IntPtr pidl, IntPtr pbc, in Guid riid, [MarshalAs(UnmanagedType.IUnknown)] out object ppv);\n\t\t//[PreserveSig] int BindToStorage(IntPtr pidl, IntPtr pbc, in Guid riid, [MarshalAs(UnmanagedType.IUnknown)] out object ppv);\n\t\t//[PreserveSig] int CompareIDs(nint lParam, IntPtr pidl1, IntPtr pidl2);\n\t\t//[PreserveSig] int CreateViewObject(wnd hwndOwner, in Guid riid, out IntPtr ppv);\n\t\t//[PreserveSig] int GetAttributesOf(uint cidl, [MarshalAs(UnmanagedType.LPArray)] IntPtr[] apidl, ref uint rgfInOut);\n\t\tvoid _0();\n\t\tvoid _1();\n\t\tvoid _2();\n\t\tvoid _3();\n\t\tvoid _4();\n\t\tvoid _5();\n\t\tvoid _6();\n\t\t[PreserveSig] int GetUIObjectOf(wnd hwndOwner, uint cidl, in IntPtr pidl, in Guid riid, nint rgfReserved, [MarshalAs(UnmanagedType.IUnknown)] out object ppv);\n\t\t//[PreserveSig] int GetDisplayNameOf(IntPtr pidl, uint uFlags, out STRRET pName);\n\t\t//[PreserveSig] int SetNameOf(wnd hwnd, IntPtr pidl, [MarshalAs(UnmanagedType.LPWStr)] string pszName, uint uFlags, out IntPtr ppidlOut);\n\t}\n\n\tinternal static bool GetUIObjectOf<T>(this IShellFolder t, IntPtr pidl, out T result) where T : class {\n\t\tresult = null;\n\t\tif (0 != t.GetUIObjectOf(default, 1, pidl, typeof(T).GUID, 0, out var o) || o is not T r) return false;\n\t\tresult = r;\n\t\treturn true;\n\t}\n\n\t//internal static Guid IID_IShellItem = new Guid(0x43826D1E, 0xE718, 0x42EE, 0xBC, 0x55, 0xA1, 0xE2, 0x61, 0xC3, 0x7B, 0xFE);\n\n\t[ComImport, Guid(\"43826d1e-e718-42ee-bc55-a1e261c37bfe\"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\n\tinternal interface IShellItem {\n\t\t//[PreserveSig] int BindToHandler(IntPtr pbc, in Guid bhid, in Guid riid, out IntPtr ppv); //IBindCtx\n\t\t//[PreserveSig] int GetParent(out IShellItem ppsi);\n\t\tvoid _0();\n\t\tvoid _1();\n\t\t[PreserveSig] int GetDisplayName(SIGDN sigdnName, [MarshalAs(UnmanagedType.LPWStr)] out string ppszName);\n\t\t[PreserveSig] int GetAttributes(uint sfgaoMask, out uint psfgaoAttribs);\n\t\t//[PreserveSig] int Compare(IShellItem psi, uint hint, out int piOrder);\n\t}\n\n\t//[ComImport, Guid(\"000214F2-0000-0000-C000-000000000046\"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\n\t//internal interface IEnumIDList {\n\t//\t[PreserveSig] int Next(int celt, [MarshalAs(UnmanagedType.LPArray)][Out] IntPtr[] rgelt, out int pceltFetched);\n\t//\t[PreserveSig] int Skip(int celt);\n\t//\t[PreserveSig] int Reset();\n\t//\t[PreserveSig] int Clone(out IEnumIDList ppenum);\n\t//}\n\n\t[ComImport, Guid(\"000214fa-0000-0000-c000-000000000046\"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\n\tinternal interface IExtractIcon {\n\t\t[PreserveSig] int GetIconLocation(uint uFlags, StringBuilder pszIconFile, int cchMax, out int piIndex, out uint pwFlags);\n\t\t//[PreserveSig] int Extract([MarshalAs(UnmanagedType.LPWStr)] string pszFile, int nIconIndex, out IntPtr phiconLarge, out IntPtr phiconSmall, uint nIconSize);\n\t}\n\n\t[ComImport, Guid(\"000214F9-0000-0000-C000-000000000046\"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\n\tinternal interface IShellLink {\n\t\t[PreserveSig] int GetPath(char* pszFile, int cch, IntPtr pfd = default, uint fFlags = 0);\n\t\t[PreserveSig] int GetIDList(out IntPtr ppidl);\n\t\t[PreserveSig] int SetIDList(IntPtr pidl);\n\t\t[PreserveSig] int GetDescription(char* pszName, int cch);\n\t\t[PreserveSig] int SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName);\n\t\t[PreserveSig] int GetWorkingDirectory(char* pszDir, int cch);\n\t\t[PreserveSig] int SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir);\n\t\t[PreserveSig] int GetArguments(char* pszArgs, int cch);\n\t\t[PreserveSig] int SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs);\n\t\t[PreserveSig] int GetHotkey(out ushort pwHotkey);\n\t\t[PreserveSig] int SetHotkey(ushort wHotkey);\n\t\t[PreserveSig] int GetShowCmd(out int piShowCmd);\n\t\t[PreserveSig] int SetShowCmd(int iShowCmd);\n\t\t[PreserveSig] int GetIconLocation(char* pszIconPath, int cch, out int piIcon);\n\t\t[PreserveSig] int SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon);\n\t\t[PreserveSig] int SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, uint dwReserved = 0);\n\t\t[PreserveSig] int Resolve(wnd hwnd, uint fFlags);\n\t\t[PreserveSig] int SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile);\n\n\t\t//info: default string marshaling in COM interfaces is BSTR, but in this interface strings are LPWSTR. Cannot use plain string and char[].\n\t\t//\tInstead of [MarshalAs(UnmanagedType.LPArray)] [Out] char[] can be just char*. Then also need fixed when calling.\n\t}\n\n\t[ComImport, Guid(\"00021401-0000-0000-C000-000000000046\"), ClassInterface(ClassInterfaceType.None)]\n\tinternal class ShellLink { }\n\n\t[ComImport, Guid(\"0000010b-0000-0000-C000-000000000046\"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\n\tinternal interface IPersistFile {\n\t\t// IPersist\n\t\t[PreserveSig] int GetClassID(out Guid pClassID);\n\t\t// IPersistFile\n\t\t[PreserveSig] int IsDirty();\n\t\t[PreserveSig] int Load([MarshalAs(UnmanagedType.LPWStr)] string pszFileName, uint dwMode);\n\t\t[PreserveSig] int Save([MarshalAs(UnmanagedType.LPWStr)] string pszFileName, [MarshalAs(UnmanagedType.Bool)] bool fRemember);\n\t\t//[PreserveSig] int SaveCompleted([MarshalAs(UnmanagedType.LPWStr)] string pszFileName);\n\t\t//[PreserveSig] int GetCurFile(out IntPtr ppszFileName);\n\t}\n\n\t//see also VARIANT in Struct.cs\n\tinternal struct PROPVARIANT : IDisposable {\n\t\tpublic VARENUM vt; //ushort\n\t\tushort _u1;\n\t\tuint _u2;\n\t\tpublic nint value;\n\t\tpublic nint value2;\n\n\t\t/// <summary>\n\t\t/// Calls <c>PropVariantClear</c>.\n\t\t/// </summary>\n\t\tpublic void Dispose() {\n\t\t\tPropVariantClear(ref this);\n\t\t}\n\t}\n\n\tinternal struct PROPERTYKEY {\n\t\tpublic Guid fmtid;\n\t\tpublic uint pid;\n\t}\n\n\tinternal static Guid IID_IPropertyStore = new Guid(0x886D8EEB, 0x8CF2, 0x4446, 0x8D, 0x02, 0xCD, 0xBA, 0x1D, 0xBD, 0xCF, 0x99);\n\n\t[ComImport, Guid(\"886d8eeb-8cf2-4446-8d02-cdba1dbdcf99\"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\n\tinternal interface IPropertyStore {\n\t\t[PreserveSig] int GetCount(out int cProps);\n\t\t[PreserveSig] int GetAt(int iProp, out PROPERTYKEY pkey);\n\t\t[PreserveSig] int GetValue(in PROPERTYKEY key, out PROPVARIANT pv);\n\t\t[PreserveSig] int SetValue(in PROPERTYKEY key, ref PROPVARIANT propvar);\n\t\t[PreserveSig] int Commit();\n\t}\n\n\t//note: this is used in the lib, even if IImageList isn't.\n\tinternal static Guid IID_IImageList = new Guid(0x46EB5926, 0x582E, 0x4017, 0x9F, 0xDF, 0xE8, 0x99, 0x8D, 0xAA, 0x09, 0x50);\n\n\t[ComImport, Guid(\"a5cd92ff-29be-454c-8d04-d82879fb3f1b\"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\n\tinternal interface IVirtualDesktopManager {\n\t\t[PreserveSig] int IsWindowOnCurrentVirtualDesktop(wnd topLevelWindow, [MarshalAs(UnmanagedType.Bool)] out bool onCurrentDesktop);\n\t\t[PreserveSig] int GetWindowDesktopId(wnd topLevelWindow, out Guid desktopId);\n\t\t[PreserveSig] int MoveWindowToDesktop(wnd topLevelWindow, in Guid desktopId);\n\t}\n\n\t[ComImport, Guid(\"aa509086-5ca9-4c25-8f95-589d3c07b48a\"), ClassInterface(ClassInterfaceType.None)]\n\tinternal class VirtualDesktopManager { }\n}\n"
  },
  {
    "path": "Au/Api/Api_UIA.cs",
    "content": "namespace Au.Types;\n\n/// <summary>\n/// Wraps some UI Automation API.\n/// </summary>\nstatic class UiaUtil {\n\tpublic static UiaApi.IUIAutomation Uia => _uia ??= osVersion.minWin8 ? new UiaApi.CUIAutomation8() as UiaApi.IUIAutomation : new UiaApi.CUIAutomation() as UiaApi.IUIAutomation;\n\t[ThreadStatic] static UiaApi.IUIAutomation _uia;\n\t\n\t/// <summary>\n\t/// Gets UI element from point.\n\t/// </summary>\n\t/// <param name=\"xy\">Screen coordinates.</param>\n\t/// <returns><c>null</c> if failed.</returns>\n\tpublic static UiaApi.IUIAutomationElement ElementFromPoint(POINT xy) {\n\t\treturn 0 == Uia.ElementFromPoint(xy, out var e) ? e : null;\n\t}\n\t\n\t/// <summary>\n\t/// Gets the focused element.\n\t/// </summary>\n\t/// <returns><c>null</c> if failed.</returns>\n\tpublic static UiaApi.IUIAutomationElement ElementFocused() {\n\t\treturn 0 == Uia.GetFocusedElement(out var e) ? e : null;\n\t}\n\t\n\t///// <summary>\n\t///// Gets the container control of this or nearest ancestor element that can retrieve it.\n\t///// </summary>\n\t//public static wnd Hwnd(this UiaApi.IUIAutomationElement t) {\n\t//\tif (0 == t.get_CurrentNativeWindowHandle(out var w) && !w.Is0) return w;\n\t\t\n\t//\tif (0 == Uia.get_RawViewWalker(out var walker)) {\n\t//\t\twhile (0 == walker.GetParentElement(t, out var p) && p != null && p != t) {\n\t//\t\t\tt = p;\n\t//\t\t\tif (0 == t.get_CurrentNativeWindowHandle(out w) && !w.Is0) return w;\n\t//\t\t}\n\t//\t}\n\t\t\n\t//\treturn default;\n\t//}\n\t\n\t/// <summary>\n\t/// Gets caret rectangle in screen from this focused element.\n\t/// </summary>\n\tpublic static bool GetCaretRect(this UiaApi.IUIAutomationElement t, out RECT r) {\n\t\tif (0 == t.GetCurrentPattern(UiaApi.UIA_TextPattern2Id, out var o) && o is UiaApi.IUIAutomationTextPattern2 p2) {\n\t\t\tif (0 == p2.GetCaretRange(out bool isActive, out var tr) && tr != null /*&& isActive*/) {\n\t\t\t\treturn tr.GetRect(out r, t);\n\t\t\t}\n\t\t}\n\t\tif (0 == t.GetCurrentPattern(UiaApi.UIA_TextPatternId, out o) && o is UiaApi.IUIAutomationTextPattern p) {\n\t\t\tif (0 == p.GetSelection(out var ranges) && 0 == ranges.GetElement(0, out var tr)) {\n\t\t\t\treturn tr.GetRect(out r, t, selectionToCaret: true);\n\t\t\t}\n\t\t}\n\t\tr = default;\n\t\treturn false;\n\t}\n\t\n\t/// <summary>\n\t/// Gets rectangle in screen.\n\t/// </summary>\n\tpublic static unsafe bool GetRect(this UiaApi.IUIAutomationTextRange t, out RECT r, UiaApi.IUIAutomationElement e, bool selectionToCaret = false) {\n\t\tif (_GetRect(t, out r)) {\n\t\t\tif (selectionToCaret) r.left = r.right - 1;\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\t//probably no selection\n\t\t\n\t\tif (0 == t.ExpandToEnclosingUnit(UiaApi.TextUnit.TextUnit_Character) && _GetRect(t, out r)) {\n\t\t\tr.right = r.left + 1;\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\t//probably caret at the end\n\t\t\n\t\tif (0 == t.MoveEndpointByUnit(UiaApi.TextPatternRangeEndpoint.TextPatternRangeEndpoint_Start, UiaApi.TextUnit.TextUnit_Character, -1, out int m) && m < 0 && _GetRect(t, out r)) {\n\t\t\t//moved to previous line?\n\t\t\tif (0 == t.GetText(2, out var s) && !s.NE() && s[0] is '\\r' or '\\n' && 0 == t.ExpandToEnclosingUnit(UiaApi.TextUnit.TextUnit_Line) && _GetRect(t, out var r2)) {\n\t\t\t\tr.Offset(0, r2.Height);\n\t\t\t\tr.right = (r.left = r2.left) + 1;\n\t\t\t} else {\n\t\t\t\tr.right = (r.left = r.right) + 1;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\t//probably no text\n\t\t\n\t\t//get the left edge of e rect\n\t\tif (0 == e.get_CurrentBoundingRectangle(out r)) {\n\t\t\tint dpi = Dpi.OfWindow(wnd.active);\n\t\t\tint h = (int)Dpi.Unscale(r.Height, dpi);\n\t\t\tif (h < 111) { //assume it's a single-line edit control\n\t\t\t\tif (h > 32) r.top = r.bottom - Dpi.Scale(32, dpi); //get the bottom max 32 logical pixels\n\t\t\t\tr.right = r.left + 1;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\t\n\t\treturn false;\n\t\t\n\t\tstatic unsafe bool _GetRect(UiaApi.IUIAutomationTextRange t, out RECT r) {\n\t\t\tr = default;\n\t\t\tif (0 != t.GetBoundingRectangles(out var sap) || sap == null) return false;\n\t\t\tuint n = sap->rgsabound.cElements / 4;\n\t\t\tif (n > 0) {\n\t\t\t\tvar p = (double*)sap->pvData + sap->rgsabound.cElements - 4;\n\t\t\t\tr = new(p[0].ToInt(), p[1].ToInt(), p[2].ToInt(), p[3].ToInt());\n\t\t\t}\n\t\t\tUiaApi.SafeArrayDestroy(sap);\n\t\t\treturn n > 0;\n\t\t}\n\t}\n\t\n\tpublic static bool GetCaretRectInPowerShell(out RECT r) {\n\t\t//GetGUIThreadInfo and MSAA don't work with PowerShell.\n\t\t//\tDoes not support IUIAutomationTextPattern2.\n\t\t//\tIUIAutomationTextPattern.GetSelection -> IUIAutomationTextRange.GetBoundingRectangles returns client coord, which may be fixed in the future.\n\t\t//\tWin+; doesn't work too. But PhraseExpress works. And IME (interesting: temporarily replaces the caret).\n\t\t//\tI would't care, but this was a user request.\n\t\t//Now instead using an undocumented PS feature.\n\t\t\n\t\tvar t = ElementFocused();\n\t\tif (t != null && 0 == t.get_CurrentControlType(out var ct) && ct == UiaApi.TypeId.Edit) {\n\t\t\tif (0 == Uia.get_RawViewWalker(out var walker)) {\n\t\t\t\tUiaApi.IUIAutomationElement e = null;\n\t\t\t\twhile (0 == (e == null ? walker.GetFirstChildElement(t, out e) : walker.GetNextSiblingElement(e, out e)) && e != null) {\n\t\t\t\t\t//if (0 == e.get_CurrentControlType(out ct)) print.it(ct);\n\t\t\t\t\t//if (0 == e.get_CurrentAutomationId(out var ai)) print.it(ai);\n\t\t\t\t\tif (0 == e.get_CurrentAutomationId(out var ai) && ai.Find(\"Caret\", true) >= 0 && 0 == e.get_CurrentControlType(out ct) && ct == UiaApi.TypeId.Custom) {\n\t\t\t\t\t\tif (0 == e.get_CurrentBoundingRectangle(out r)) {\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t//There are 3 child elements. The first is caret.\n\t\t\t\t//This is slow. Tested MSAA, but it gets only child element \"selection\", and cannot get rect when selection empty.\n\t\t\t}\n\t\t}\n\t\tr = default;\n\t\treturn false;\n\t}\n\t\n\t///// <summary>\n\t///// Gets text of <c>TextPattern</c> paragraph from point.\n\t///// </summary>\n\t///// <param name=\"t\"></param>\n\t///// <param name=\"xy\">Screen coordinates.</param>\n\t///// <returns><c>null</c> if the element does not support <c>TextPattern</c> or if failed.</returns>\n\t//public static string PatternTextFromPoint(this UiaApi.IUIAutomationElement t, POINT xy) {\n\t//\tif (0 == t.GetCurrentPattern(UiaApi.UIA_TextPatternId, out var o) && o is UiaApi.IUIAutomationTextPattern p) {\n\t//\t\tif (0 == p.RangeFromPoint(xy, out var tr) && 0 == tr.ExpandToEnclosingUnit(UiaApi.TextUnit.TextUnit_Paragraph)) {\n\t//\t\t\tif (0 == tr.GetText(5000, out var s)) return s;\n\t//\t\t}\n\t//\t}\n\t//\treturn null;\n\t//}\n\t\n\t///// <summary>\n\t///// Gets text of <c>ValuePattern</c>.\n\t///// </summary>\n\t///// <param name=\"t\"></param>\n\t///// <returns><c>null</c> if the element does not support <c>ValuePattern</c> or if failed.</returns>\n\t//public static string ValueText(this UiaApi.IUIAutomationElement t) {\n\t//\tif (0 == t.GetCurrentPattern(UiaApi.UIA_ValuePatternId, out var o) && o is UiaApi.IUIAutomationValuePattern p) {\n\t//\t\tif (0 == p.get_CurrentValue(out var s)) return s;\n\t//\t}\n\t//\treturn null;\n\t//}\n}\n\n#pragma warning disable 1591, 649, 169\n\n/// <summary>\n/// Declarations of some UI Automation API.\n/// </summary>\nunsafe class UiaApi : NativeApi {\n\t\n\t[ComImport, Guid(\"ff48dba4-60ef-4201-aa87-54103eef594e\"), ClassInterface(ClassInterfaceType.None)]\n\tinternal class CUIAutomation { }\n\n\t[ComImport, Guid(\"e22ad333-b25f-460c-83d0-0581107395c9\"), ClassInterface(ClassInterfaceType.None)]\n\tinternal class CUIAutomation8 { }\n\t\n\t[ComImport, Guid(\"30cbe57d-d9d0-452a-ab13-7ac5ac4825ee\"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\n\tinternal interface IUIAutomation {\n\t\t[PreserveSig] int CompareElements(IUIAutomationElement el1, IUIAutomationElement el2, [MarshalAs(UnmanagedType.Bool)] out bool areSame);\n\t\t[PreserveSig] int CompareRuntimeIds(SAFEARRAY* runtimeId1, SAFEARRAY* runtimeId2, [MarshalAs(UnmanagedType.Bool)] out bool areSame);\n\t\t[PreserveSig] int GetRootElement(out IUIAutomationElement root);\n\t\t[PreserveSig] int ElementFromHandle(void* hwnd, out IUIAutomationElement element);\n\t\t[PreserveSig] int ElementFromPoint(POINT pt, out IUIAutomationElement element);\n\t\t[PreserveSig] int GetFocusedElement(out IUIAutomationElement element);\n\t\t[PreserveSig] int GetRootElementBuildCache(IUIAutomationCacheRequest cacheRequest, out IUIAutomationElement root);\n\t\t[PreserveSig] int ElementFromHandleBuildCache(void* hwnd, IUIAutomationCacheRequest cacheRequest, out IUIAutomationElement element);\n\t\t[PreserveSig] int ElementFromPointBuildCache(POINT pt, IUIAutomationCacheRequest cacheRequest, out IUIAutomationElement element);\n\t\t[PreserveSig] int GetFocusedElementBuildCache(IUIAutomationCacheRequest cacheRequest, out IUIAutomationElement element);\n\t\t[PreserveSig] int CreateTreeWalker(IUIAutomationCondition pCondition, out IUIAutomationTreeWalker walker);\n\t\t[PreserveSig] int get_ControlViewWalker(out IUIAutomationTreeWalker walker);\n\t\t[PreserveSig] int get_ContentViewWalker(out IUIAutomationTreeWalker walker);\n\t\t[PreserveSig] int get_RawViewWalker(out IUIAutomationTreeWalker walker);\n\t\t[PreserveSig] int get_RawViewCondition(out IUIAutomationCondition condition);\n\t\t[PreserveSig] int get_ControlViewCondition(out IUIAutomationCondition condition);\n\t\t[PreserveSig] int get_ContentViewCondition(out IUIAutomationCondition condition);\n\t\t[PreserveSig] int CreateCacheRequest(out IUIAutomationCacheRequest cacheRequest);\n\t\t[PreserveSig] int CreateTrueCondition(out IUIAutomationCondition newCondition);\n\t\t[PreserveSig] int CreateFalseCondition(out IUIAutomationCondition newCondition);\n\t\t[PreserveSig] int CreatePropertyCondition(int propertyId, object value, out IUIAutomationCondition newCondition);\n\t\t[PreserveSig] int CreatePropertyConditionEx(int propertyId, object value, PropertyConditionFlags flags, out IUIAutomationCondition newCondition);\n\t\t[PreserveSig] int CreateAndCondition(IUIAutomationCondition condition1, IUIAutomationCondition condition2, out IUIAutomationCondition newCondition);\n\t\t[PreserveSig] int CreateAndConditionFromArray(SAFEARRAY* conditions, out IUIAutomationCondition newCondition);\n\t\t[PreserveSig] int CreateAndConditionFromNativeArray([MarshalAs(UnmanagedType.LPArray)][In] IUIAutomationCondition[] conditions, int conditionCount, out IUIAutomationCondition newCondition);\n\t\t[PreserveSig] int CreateOrCondition(IUIAutomationCondition condition1, IUIAutomationCondition condition2, out IUIAutomationCondition newCondition);\n\t\t[PreserveSig] int CreateOrConditionFromArray(SAFEARRAY* conditions, out IUIAutomationCondition newCondition);\n\t\t[PreserveSig] int CreateOrConditionFromNativeArray([MarshalAs(UnmanagedType.LPArray)][In] IUIAutomationCondition[] conditions, int conditionCount, out IUIAutomationCondition newCondition);\n\t\t[PreserveSig] int CreateNotCondition(IUIAutomationCondition condition, out IUIAutomationCondition newCondition);\n\t\t[PreserveSig] int AddAutomationEventHandler(int eventId, IUIAutomationElement element, TreeScope scope, IUIAutomationCacheRequest cacheRequest, IUIAutomationEventHandler handler);\n\t\t[PreserveSig] int RemoveAutomationEventHandler(int eventId, IUIAutomationElement element, IUIAutomationEventHandler handler);\n\t\t[PreserveSig] int AddPropertyChangedEventHandlerNativeArray(IUIAutomationElement element, TreeScope scope, IUIAutomationCacheRequest cacheRequest, IUIAutomationPropertyChangedEventHandler handler, [MarshalAs(UnmanagedType.LPArray)][In] int[] propertyArray, int propertyCount);\n\t\t[PreserveSig] int AddPropertyChangedEventHandler(IUIAutomationElement element, TreeScope scope, IUIAutomationCacheRequest cacheRequest, IUIAutomationPropertyChangedEventHandler handler, SAFEARRAY* propertyArray);\n\t\t[PreserveSig] int RemovePropertyChangedEventHandler(IUIAutomationElement element, IUIAutomationPropertyChangedEventHandler handler);\n\t\t[PreserveSig] int AddStructureChangedEventHandler(IUIAutomationElement element, TreeScope scope, IUIAutomationCacheRequest cacheRequest, IUIAutomationStructureChangedEventHandler handler);\n\t\t[PreserveSig] int RemoveStructureChangedEventHandler(IUIAutomationElement element, IUIAutomationStructureChangedEventHandler handler);\n\t\t[PreserveSig] int AddFocusChangedEventHandler(IUIAutomationCacheRequest cacheRequest, IUIAutomationFocusChangedEventHandler handler);\n\t\t[PreserveSig] int RemoveFocusChangedEventHandler(IUIAutomationFocusChangedEventHandler handler);\n\t\t[PreserveSig] int RemoveAllEventHandlers();\n\t\t[PreserveSig] int IntNativeArrayToSafeArray([MarshalAs(UnmanagedType.LPArray)][In] int[] array, int arrayCount, out SAFEARRAY* safeArray);\n\t\t[PreserveSig] int IntSafeArrayToNativeArray(SAFEARRAY* intArray, out int* array, out int arrayCount);\n\t\t[PreserveSig] int RectToVariant(RECT rc, out object var);\n\t\t[PreserveSig] int VariantToRect(object var, out RECT rc);\n\t\t[PreserveSig] int SafeArrayToRectNativeArray(SAFEARRAY* rects, out RECT* rectArray, out int rectArrayCount);\n\t\t[PreserveSig] int CreateProxyFactoryEntry();\n\t\t[PreserveSig] int get_ProxyFactoryMapping();\n\t\t[PreserveSig] int GetPropertyProgrammaticName(int property, out string name);\n\t\t[PreserveSig] int GetPatternProgrammaticName(int pattern, out string name);\n\t\t[PreserveSig] int PollForPotentialSupportedPatterns(IUIAutomationElement pElement, out SAFEARRAY* patternIds, out SAFEARRAY* patternNames);\n\t\t[PreserveSig] int PollForPotentialSupportedProperties(IUIAutomationElement pElement, out SAFEARRAY* propertyIds, out SAFEARRAY* propertyNames);\n\t\t[PreserveSig] int CheckNotSupported(object value, [MarshalAs(UnmanagedType.Bool)] out bool isNotSupported);\n\t\t[PreserveSig] int get_ReservedNotSupportedValue([MarshalAs(UnmanagedType.IUnknown)] out object notSupportedValue);\n\t\t[PreserveSig] int get_ReservedMixedAttributeValue([MarshalAs(UnmanagedType.IUnknown)] out object mixedAttributeValue);\n\t\t[PreserveSig] int ElementFromIAccessible();\n\t\t[PreserveSig] int ElementFromIAccessibleBuildCache();\n\t}\n\t\n\t[ComImport, Guid(\"c270f6b5-5c69-4290-9745-7a7f97169468\"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\n\tinternal interface IUIAutomationFocusChangedEventHandler {\n\t\t[PreserveSig] int HandleFocusChangedEvent(IUIAutomationElement sender);\n\t}\n\t\n\t[ComImport, Guid(\"e81d1b4e-11c5-42f8-9754-e7036c79f054\"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\n\tinternal interface IUIAutomationStructureChangedEventHandler {\n\t\t[PreserveSig] int HandleStructureChangedEvent(IUIAutomationElement sender, StructureChangeType changeType, SAFEARRAY* runtimeId);\n\t}\n\t\n\t[ComImport, Guid(\"40cd37d4-c756-4b0c-8c6f-bddfeeb13b50\"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\n\tinternal interface IUIAutomationPropertyChangedEventHandler {\n\t\t[PreserveSig] int HandlePropertyChangedEvent(IUIAutomationElement sender, int propertyId, object newValue);\n\t}\n\t\n\t[ComImport, Guid(\"146c3c17-f12e-4e22-8c27-f894b9b79c69\"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\n\tinternal interface IUIAutomationEventHandler {\n\t\t[PreserveSig] int HandleAutomationEvent(IUIAutomationElement sender, int eventId);\n\t}\n\t\n\t[Flags]\n\tinternal enum PropertyConditionFlags : uint {\n\t\tPropertyConditionFlags_None,\n\t\tPropertyConditionFlags_IgnoreCase,\n\t\tPropertyConditionFlags_MatchSubstring\n\t}\n\t\n\t[ComImport, Guid(\"4042c624-389c-4afc-a630-9df854a541fc\"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\n\tinternal interface IUIAutomationTreeWalker {\n\t\t[PreserveSig] int GetParentElement(IUIAutomationElement element, out IUIAutomationElement parent);\n\t\t[PreserveSig] int GetFirstChildElement(IUIAutomationElement element, out IUIAutomationElement first);\n\t\t[PreserveSig] int GetLastChildElement(IUIAutomationElement element, out IUIAutomationElement last);\n\t\t[PreserveSig] int GetNextSiblingElement(IUIAutomationElement element, out IUIAutomationElement next);\n\t\t[PreserveSig] int GetPreviousSiblingElement(IUIAutomationElement element, out IUIAutomationElement previous);\n\t\t[PreserveSig] int NormalizeElement(IUIAutomationElement element, out IUIAutomationElement normalized);\n\t\t[PreserveSig] int GetParentElementBuildCache(IUIAutomationElement element, IUIAutomationCacheRequest cacheRequest, out IUIAutomationElement parent);\n\t\t[PreserveSig] int GetFirstChildElementBuildCache(IUIAutomationElement element, IUIAutomationCacheRequest cacheRequest, out IUIAutomationElement first);\n\t\t[PreserveSig] int GetLastChildElementBuildCache(IUIAutomationElement element, IUIAutomationCacheRequest cacheRequest, out IUIAutomationElement last);\n\t\t[PreserveSig] int GetNextSiblingElementBuildCache(IUIAutomationElement element, IUIAutomationCacheRequest cacheRequest, out IUIAutomationElement next);\n\t\t[PreserveSig] int GetPreviousSiblingElementBuildCache(IUIAutomationElement element, IUIAutomationCacheRequest cacheRequest, out IUIAutomationElement previous);\n\t\t[PreserveSig] int NormalizeElementBuildCache(IUIAutomationElement element, IUIAutomationCacheRequest cacheRequest, out IUIAutomationElement normalized);\n\t\t[PreserveSig] int get_Condition(out IUIAutomationCondition condition);\n\t}\n\t\n\t[ComImport, Guid(\"d22108aa-8ac5-49a5-837b-37bbb3d7591e\"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\n\tinternal interface IUIAutomationElement {\n\t\t[PreserveSig] int SetFocus();\n\t\t[PreserveSig] int GetRuntimeId(out SAFEARRAY* runtimeId);\n\t\t[PreserveSig] int FindFirst(TreeScope scope, IUIAutomationCondition condition, out IUIAutomationElement found);\n\t\t[PreserveSig] int FindAll(TreeScope scope, IUIAutomationCondition condition, out IUIAutomationElementArray found);\n\t\t[PreserveSig] int FindFirstBuildCache(TreeScope scope, IUIAutomationCondition condition, IUIAutomationCacheRequest cacheRequest, out IUIAutomationElement found);\n\t\t[PreserveSig] int FindAllBuildCache(TreeScope scope, IUIAutomationCondition condition, IUIAutomationCacheRequest cacheRequest, out IUIAutomationElementArray found);\n\t\t[PreserveSig] int BuildUpdatedCache(IUIAutomationCacheRequest cacheRequest, out IUIAutomationElement updatedElement);\n\t\t[PreserveSig] int GetCurrentPropertyValue(int propertyId, out object retVal);\n\t\t[PreserveSig] int GetCurrentPropertyValueEx(int propertyId, [MarshalAs(UnmanagedType.Bool)] bool ignoreDefaultValue, out object retVal);\n\t\t[PreserveSig] int GetCachedPropertyValue(int propertyId, out object retVal);\n\t\t[PreserveSig] int GetCachedPropertyValueEx(int propertyId, [MarshalAs(UnmanagedType.Bool)] bool ignoreDefaultValue, out object retVal);\n\t\t[PreserveSig] int GetCurrentPatternAs(int patternId, in Guid riid, void** patternObject);\n\t\t[PreserveSig] int GetCachedPatternAs(int patternId, in Guid riid, void** patternObject);\n\t\t[PreserveSig] int GetCurrentPattern(int patternId, [MarshalAs(UnmanagedType.IUnknown)] out object patternObject);\n\t\t[PreserveSig] int GetCachedPattern(int patternId, [MarshalAs(UnmanagedType.IUnknown)] out object patternObject);\n\t\t[PreserveSig] int GetCachedParent(out IUIAutomationElement parent);\n\t\t[PreserveSig] int GetCachedChildren(out IUIAutomationElementArray children);\n\t\t[PreserveSig] int get_CurrentProcessId(out int retVal);\n\t\t[PreserveSig] int get_CurrentControlType(out TypeId retVal);\n\t\t[PreserveSig] int get_CurrentLocalizedControlType(out string retVal);\n\t\t[PreserveSig] int get_CurrentName(out string retVal);\n\t\t[PreserveSig] int get_CurrentAcceleratorKey(out string retVal);\n\t\t[PreserveSig] int get_CurrentAccessKey(out string retVal);\n\t\t[PreserveSig] int get_CurrentHasKeyboardFocus([MarshalAs(UnmanagedType.Bool)] out bool retVal);\n\t\t[PreserveSig] int get_CurrentIsKeyboardFocusable([MarshalAs(UnmanagedType.Bool)] out bool retVal);\n\t\t[PreserveSig] int get_CurrentIsEnabled([MarshalAs(UnmanagedType.Bool)] out bool retVal);\n\t\t[PreserveSig] int get_CurrentAutomationId(out string retVal);\n\t\t[PreserveSig] int get_CurrentClassName(out string retVal);\n\t\t[PreserveSig] int get_CurrentHelpText(out string retVal);\n\t\t[PreserveSig] int get_CurrentCulture(out int retVal);\n\t\t[PreserveSig] int get_CurrentIsControlElement([MarshalAs(UnmanagedType.Bool)] out bool retVal);\n\t\t[PreserveSig] int get_CurrentIsContentElement([MarshalAs(UnmanagedType.Bool)] out bool retVal);\n\t\t[PreserveSig] int get_CurrentIsPassword([MarshalAs(UnmanagedType.Bool)] out bool retVal);\n\t\t[PreserveSig] int get_CurrentNativeWindowHandle(out wnd retVal);\n\t\t[PreserveSig] int get_CurrentItemType(out string retVal);\n\t\t[PreserveSig] int get_CurrentIsOffscreen([MarshalAs(UnmanagedType.Bool)] out bool retVal);\n\t\t[PreserveSig] int get_CurrentOrientation(out OrientationType retVal);\n\t\t[PreserveSig] int get_CurrentFrameworkId(out string retVal);\n\t\t[PreserveSig] int get_CurrentIsRequiredForForm([MarshalAs(UnmanagedType.Bool)] out bool retVal);\n\t\t[PreserveSig] int get_CurrentItemStatus(out string retVal);\n\t\t[PreserveSig] int get_CurrentBoundingRectangle(out RECT retVal);\n\t\t[PreserveSig] int get_CurrentLabeledBy(out IUIAutomationElement retVal);\n\t\t[PreserveSig] int get_CurrentAriaRole(out string retVal);\n\t\t[PreserveSig] int get_CurrentAriaProperties(out string retVal);\n\t\t[PreserveSig] int get_CurrentIsDataValidForForm([MarshalAs(UnmanagedType.Bool)] out bool retVal);\n\t\t[PreserveSig] int get_CurrentControllerFor(out IUIAutomationElementArray retVal);\n\t\t[PreserveSig] int get_CurrentDescribedBy(out IUIAutomationElementArray retVal);\n\t\t[PreserveSig] int get_CurrentFlowsTo(out IUIAutomationElementArray retVal);\n\t\t[PreserveSig] int get_CurrentProviderDescription(out string retVal);\n\t\t[PreserveSig] int get_CachedProcessId(out int retVal);\n\t\t[PreserveSig] int get_CachedControlType(out int retVal);\n\t\t[PreserveSig] int get_CachedLocalizedControlType(out string retVal);\n\t\t[PreserveSig] int get_CachedName(out string retVal);\n\t\t[PreserveSig] int get_CachedAcceleratorKey(out string retVal);\n\t\t[PreserveSig] int get_CachedAccessKey(out string retVal);\n\t\t[PreserveSig] int get_CachedHasKeyboardFocus([MarshalAs(UnmanagedType.Bool)] out bool retVal);\n\t\t[PreserveSig] int get_CachedIsKeyboardFocusable([MarshalAs(UnmanagedType.Bool)] out bool retVal);\n\t\t[PreserveSig] int get_CachedIsEnabled([MarshalAs(UnmanagedType.Bool)] out bool retVal);\n\t\t[PreserveSig] int get_CachedAutomationId(out string retVal);\n\t\t[PreserveSig] int get_CachedClassName(out string retVal);\n\t\t[PreserveSig] int get_CachedHelpText(out string retVal);\n\t\t[PreserveSig] int get_CachedCulture(out int retVal);\n\t\t[PreserveSig] int get_CachedIsControlElement([MarshalAs(UnmanagedType.Bool)] out bool retVal);\n\t\t[PreserveSig] int get_CachedIsContentElement([MarshalAs(UnmanagedType.Bool)] out bool retVal);\n\t\t[PreserveSig] int get_CachedIsPassword([MarshalAs(UnmanagedType.Bool)] out bool retVal);\n\t\t[PreserveSig] int get_CachedNativeWindowHandle(void** retVal);\n\t\t[PreserveSig] int get_CachedItemType(out string retVal);\n\t\t[PreserveSig] int get_CachedIsOffscreen([MarshalAs(UnmanagedType.Bool)] out bool retVal);\n\t\t[PreserveSig] int get_CachedOrientation(out OrientationType retVal);\n\t\t[PreserveSig] int get_CachedFrameworkId(out string retVal);\n\t\t[PreserveSig] int get_CachedIsRequiredForForm([MarshalAs(UnmanagedType.Bool)] out bool retVal);\n\t\t[PreserveSig] int get_CachedItemStatus(out string retVal);\n\t\t[PreserveSig] int get_CachedBoundingRectangle(out RECT retVal);\n\t\t[PreserveSig] int get_CachedLabeledBy(out IUIAutomationElement retVal);\n\t\t[PreserveSig] int get_CachedAriaRole(out string retVal);\n\t\t[PreserveSig] int get_CachedAriaProperties(out string retVal);\n\t\t[PreserveSig] int get_CachedIsDataValidForForm([MarshalAs(UnmanagedType.Bool)] out bool retVal);\n\t\t[PreserveSig] int get_CachedControllerFor(out IUIAutomationElementArray retVal);\n\t\t[PreserveSig] int get_CachedDescribedBy(out IUIAutomationElementArray retVal);\n\t\t[PreserveSig] int get_CachedFlowsTo(out IUIAutomationElementArray retVal);\n\t\t[PreserveSig] int get_CachedProviderDescription(out string retVal);\n\t\t[PreserveSig] int GetClickablePoint(out POINT clickable, [MarshalAs(UnmanagedType.Bool)] out bool gotClickable);\n\t}\n\t\n\tinternal enum OrientationType {\n\t\tOrientationType_None,\n\t\tOrientationType_Horizontal,\n\t\tOrientationType_Vertical\n\t}\n\t\n\t[ComImport, Guid(\"b32a92b5-bc25-4078-9c08-d7ee95c48e03\"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\n\tinternal interface IUIAutomationCacheRequest {\n\t\t[PreserveSig] int AddProperty(int propertyId);\n\t\t[PreserveSig] int AddPattern(int patternId);\n\t\t[PreserveSig] int Clone(out IUIAutomationCacheRequest clonedRequest);\n\t\t[PreserveSig] int get_TreeScope(out TreeScope scope);\n\t\t[PreserveSig] int put_TreeScope(TreeScope scope);\n\t\t[PreserveSig] int get_TreeFilter(out IUIAutomationCondition filter);\n\t\t[PreserveSig] int put_TreeFilter(IUIAutomationCondition filter);\n\t\t[PreserveSig] int get_AutomationElementMode(out AutomationElementMode mode);\n\t\t[PreserveSig] int put_AutomationElementMode(AutomationElementMode mode);\n\t}\n\t\n\t[ComImport, Guid(\"14314595-b4bc-4055-95f2-58f2e42c9855\"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\n\tinternal interface IUIAutomationElementArray {\n\t\t[PreserveSig] int get_Length(out int length);\n\t\t[PreserveSig] int GetElement(int index, out IUIAutomationElement element);\n\t}\n\t\n\t[ComImport, Guid(\"352ffba8-0973-437c-a61f-f64cafd81df9\"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\n\tinternal interface IUIAutomationCondition { }\n\t\n\t[Flags]\n\tinternal enum TreeScope : uint {\n\t\tTreeScope_None,\n\t\tTreeScope_Element,\n\t\tTreeScope_Children,\n\t\tTreeScope_Descendants = 0x4,\n\t\tTreeScope_Parent = 0x8,\n\t\tTreeScope_Ancestors = 0x10,\n\t\tTreeScope_Subtree = 0x7\n\t}\n\t\n\tinternal struct SAFEARRAY {\n\t\tpublic ushort cDims;\n\t\tpublic ushort fFeatures;\n\t\tpublic uint cbElements;\n\t\tpublic uint cLocks;\n\t\tpublic void* pvData;\n\t\t/*[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]*/\n\t\tpublic SAFEARRAYBOUND rgsabound;\n\t}\n\t\n\tinternal struct SAFEARRAYBOUND {\n\t\tpublic uint cElements;\n\t\tpublic int lLbound;\n\t}\n\t\n\tinternal enum AutomationElementMode {\n\t\tAutomationElementMode_None,\n\t\tAutomationElementMode_Full\n\t}\n\t\n\tinternal enum StructureChangeType {\n\t\tStructureChangeType_ChildAdded,\n\t\tStructureChangeType_ChildRemoved,\n\t\tStructureChangeType_ChildrenInvalidated,\n\t\tStructureChangeType_ChildrenBulkAdded,\n\t\tStructureChangeType_ChildrenBulkRemoved,\n\t\tStructureChangeType_ChildrenReordered\n\t}\n\t\n\tinternal enum TypeId {\n\t\tButton = 50000,\n\t\tCalendar = 50001,\n\t\tCheckBox = 50002,\n\t\tComboBox = 50003,\n\t\tEdit = 50004,\n\t\tHyperlink = 50005,\n\t\tImage = 50006,\n\t\tListItem = 50007,\n\t\tList = 50008,\n\t\tMenu = 50009,\n\t\tMenuBar = 50010,\n\t\tMenuItem = 50011,\n\t\tProgressBar = 50012,\n\t\tRadioButton = 50013,\n\t\tScrollBar = 50014,\n\t\tSlider = 50015,\n\t\tSpinner = 50016,\n\t\tStatusBar = 50017,\n\t\tTab = 50018,\n\t\tTabItem = 50019,\n\t\tText = 50020,\n\t\tToolBar = 50021,\n\t\tToolTip = 50022,\n\t\tTree = 50023,\n\t\tTreeItem = 50024,\n\t\tCustom = 50025,\n\t\tGroup = 50026,\n\t\tThumb = 50027,\n\t\tDataGrid = 50028,\n\t\tDataItem = 50029,\n\t\tDocument = 50030,\n\t\tSplitButton = 50031,\n\t\tWindow = 50032,\n\t\tPane = 50033,\n\t\tHeader = 50034,\n\t\tHeaderItem = 50035,\n\t\tTable = 50036,\n\t\tTitleBar = 50037,\n\t\tSeparator = 50038,\n\t\tSemanticZoom = 50039,\n\t\tAppBar = 50040,\n\t\tCustomLandmark = 80000,\n\t\tFormLandmark = 80001,\n\t\tMainLandmark = 80002,\n\t\tNavigationLandmark = 80003,\n\t\tSearchLandmark = 80004,\n\t}\n\t\n\t[DllImport(\"oleaut32.dll\", EntryPoint = \"#16\", PreserveSig = true)]\n\tinternal static extern int SafeArrayDestroy(SAFEARRAY* psa);\n\t\n\tpublic const int UIA_TextPatternId = 10014;\n\tinternal const int UIA_TextPattern2Id = 10024;\n\t\n\t[ComImport, Guid(\"32eba289-3583-42c9-9c59-3b6d9a1e9b6a\"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\n\tpublic interface IUIAutomationTextPattern {\n\t\t[PreserveSig] int RangeFromPoint(POINT pt, out IUIAutomationTextRange range);\n\t\t[PreserveSig] int RangeFromChild(IUIAutomationElement child, out IUIAutomationTextRange range);\n\t\t[PreserveSig] int GetSelection(out IUIAutomationTextRangeArray ranges);\n\t\t[PreserveSig] int GetVisibleRanges(out IUIAutomationTextRangeArray ranges);\n\t\t[PreserveSig] int get_DocumentRange(out IUIAutomationTextRange range);\n\t\t[PreserveSig] int get_SupportedTextSelection(out SupportedTextSelection supportedTextSelection);\n\t}\n\t\n\tpublic enum SupportedTextSelection {\n\t\tSupportedTextSelection_None,\n\t\tSupportedTextSelection_Single,\n\t\tSupportedTextSelection_Multiple\n\t}\n\t\n\t[ComImport, Guid(\"ce4ae76a-e717-4c98-81ea-47371d028eb6\"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\n\tpublic interface IUIAutomationTextRangeArray {\n\t\t[PreserveSig] int get_Length(out int length);\n\t\t[PreserveSig] int GetElement(int index, out IUIAutomationTextRange element);\n\t}\n\t\n\t[ComImport, Guid(\"a543cc6a-f4ae-494b-8239-c814481187a8\"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\n\tpublic interface IUIAutomationTextRange {\n\t\t[PreserveSig] int Clone(out IUIAutomationTextRange clonedRange);\n\t\t[PreserveSig] int Compare(IUIAutomationTextRange range, [MarshalAs(UnmanagedType.Bool)] out bool areSame);\n\t\t[PreserveSig] int CompareEndpoints(TextPatternRangeEndpoint srcEndPoint, IUIAutomationTextRange range, TextPatternRangeEndpoint targetEndPoint, out int compValue);\n\t\t[PreserveSig] int ExpandToEnclosingUnit(TextUnit textUnit);\n\t\t[PreserveSig] int FindAttribute(int attr, object val, [MarshalAs(UnmanagedType.Bool)] bool backward, out IUIAutomationTextRange found);\n\t\t[PreserveSig] int FindText(string text, [MarshalAs(UnmanagedType.Bool)] bool backward, [MarshalAs(UnmanagedType.Bool)] bool ignoreCase, out IUIAutomationTextRange found);\n\t\t[PreserveSig] int GetAttributeValue(int attr, out object value);\n\t\t[PreserveSig] int GetBoundingRectangles(out SAFEARRAY* boundingRects);\n\t\t[PreserveSig] int GetEnclosingElement(out IUIAutomationElement enclosingElement);\n\t\t[PreserveSig] int GetText(int maxLength, out string text);\n\t\t[PreserveSig] int Move(TextUnit unit, int count, out int moved);\n\t\t[PreserveSig] int MoveEndpointByUnit(TextPatternRangeEndpoint endpoint, TextUnit unit, int count, out int moved);\n\t\t[PreserveSig] int MoveEndpointByRange(TextPatternRangeEndpoint srcEndPoint, IUIAutomationTextRange range, TextPatternRangeEndpoint targetEndPoint);\n\t\t[PreserveSig] int Select();\n\t\t[PreserveSig] int AddToSelection();\n\t\t[PreserveSig] int RemoveFromSelection();\n\t\t[PreserveSig] int ScrollIntoView([MarshalAs(UnmanagedType.Bool)] bool alignToTop);\n\t\t[PreserveSig] int GetChildren(out IUIAutomationElementArray children);\n\t}\n\t\n\t[ComImport, Guid(\"506a921a-fcc9-409f-b23b-37eb74106872\"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\n\tinternal interface IUIAutomationTextPattern2 : IUIAutomationTextPattern {\n\t\t// IUIAutomationTextPattern\n\t\t[PreserveSig] new int RangeFromPoint(POINT pt, out IUIAutomationTextRange range);\n\t\t[PreserveSig] new int RangeFromChild(IUIAutomationElement child, out IUIAutomationTextRange range);\n\t\t[PreserveSig] new int GetSelection(out IUIAutomationTextRangeArray ranges);\n\t\t[PreserveSig] new int GetVisibleRanges(out IUIAutomationTextRangeArray ranges);\n\t\t[PreserveSig] new int get_DocumentRange(out IUIAutomationTextRange range);\n\t\t[PreserveSig] new int get_SupportedTextSelection(out SupportedTextSelection supportedTextSelection);\n\t\t// IUIAutomationTextPattern2\n\t\t[PreserveSig] int RangeFromAnnotation(IUIAutomationElement annotation, out IUIAutomationTextRange range);\n\t\t[PreserveSig] int GetCaretRange([MarshalAs(UnmanagedType.Bool)] out bool isActive, out IUIAutomationTextRange range);\n\t}\n\t\n\tpublic enum TextUnit {\n\t\tTextUnit_Character,\n\t\tTextUnit_Format,\n\t\tTextUnit_Word,\n\t\tTextUnit_Line,\n\t\tTextUnit_Paragraph,\n\t\tTextUnit_Page,\n\t\tTextUnit_Document\n\t}\n\t\n\tpublic enum TextPatternRangeEndpoint {\n\t\tTextPatternRangeEndpoint_Start,\n\t\tTextPatternRangeEndpoint_End\n\t}\n}\n"
  },
  {
    "path": "Au/Api/Api_const.cs",
    "content": "//Windows API constants common to multiple API functions, such as WM_, WS_, errors.\n\nnamespace Au.Types;\n\nstatic unsafe partial class Api\n{\n\t#region Errors\n\n\tinternal const int S_OK = 0;\n\tinternal const int S_FALSE = 1;\n\tinternal const int ERROR_FILE_NOT_FOUND = 2;\n\tinternal const int ERROR_PATH_NOT_FOUND = 3;\n\tinternal const int ERROR_ACCESS_DENIED = 5;\n\tinternal const int ERROR_INVALID_HANDLE = 6;\n\tinternal const int ERROR_NOT_SAME_DEVICE = 17;\n\tinternal const int ERROR_NO_MORE_FILES = 18;\n\tinternal const int ERROR_NOT_READY = 21;\n\tinternal const int ERROR_SHARING_VIOLATION = 32;\n\tinternal const int ERROR_LOCK_VIOLATION = 33;\n\tinternal const int ERROR_HANDLE_EOF = 38;\n\tinternal const int ERROR_BAD_NETPATH = 53;\n\tinternal const int ERROR_BAD_NET_NAME = 67;\n\tinternal const int ERROR_FILE_EXISTS = 80;\n\tinternal const int ERROR_INVALID_PARAMETER = 87;\n\tinternal const int ERROR_BROKEN_PIPE = 109;\n\tinternal const int ERROR_SEM_TIMEOUT = 121;\n\tinternal const int ERROR_INSUFFICIENT_BUFFER = 122;\n\tinternal const int ERROR_INVALID_NAME = 123;\n\tinternal const int ERROR_DIR_NOT_EMPTY = 145;\n\tinternal const int ERROR_ALREADY_EXISTS = 183;\n\tinternal const int ERROR_MORE_DATA = 234;\n\tinternal const int ERROR_DIRECTORY = 267;\n\tinternal const int ERROR_PIPE_CONNECTED = 535;\n\tinternal const int ERROR_IO_PENDING = 997;\n\tinternal const int ERROR_UNABLE_TO_REMOVE_REPLACED = 1175;\n\tinternal const int ERROR_USER_MAPPED_FILE = 1224;\n\tinternal const int ERROR_PRIVILEGE_NOT_HELD = 1314;\n\tinternal const int ERROR_INVALID_WINDOW_HANDLE = 1400;\n\tinternal const int ERROR_TIMEOUT = 1460;\n\tinternal const int E_NOTIMPL = unchecked((int)0x80004001);\n\tinternal const int E_NOINTERFACE = unchecked((int)0x80004002);\n\tinternal const int E_FAIL = unchecked((int)0x80004005);\n\tinternal const int E_INVALIDARG = unchecked((int)0x80070057);\n\tinternal const int E_ACCESSDENIED = unchecked((int)0x80070005);\n\tinternal const int E_OUTOFMEMORY = unchecked((int)0x8007000E);\n\tinternal const int DISP_E_MEMBERNOTFOUND = unchecked((int)0x80020003);\n\tinternal const int REGDB_E_CLASSNOTREG = unchecked((int)0x80040154);\n\tinternal const int RPC_E_SERVER_CANTMARSHAL_DATA = unchecked((int)0x8001000D);\n\tinternal const int E_POINTER = unchecked((int)0x80004003);\n\n\t#endregion\n\n\t#region WM_\n\n\tinternal const int WM_NULL = 0;\n\tinternal const int WM_CREATE = 0x0001;\n\tinternal const int WM_DESTROY = 0x0002;\n\tinternal const int WM_MOVE = 0x0003;\n\tinternal const int WM_SIZE = 0x0005;\n\tinternal const int WM_ACTIVATE = 0x0006;\n\tinternal const int WM_SETFOCUS = 0x0007;\n\tinternal const int WM_KILLFOCUS = 0x0008;\n\tinternal const int WM_ENABLE = 0x000A;\n\tinternal const int WM_SETREDRAW = 0x000B;\n\tinternal const int WM_SETTEXT = 0x000C;\n\tinternal const int WM_GETTEXT = 0x000D;\n\tinternal const int WM_GETTEXTLENGTH = 0x000E;\n\tinternal const int WM_PAINT = 0x000F;\n\tinternal const int WM_CLOSE = 0x0010;\n\tinternal const int WM_QUERYENDSESSION = 0x0011;\n\tinternal const int WM_QUERYOPEN = 0x0013;\n\tinternal const int WM_ENDSESSION = 0x0016;\n\tinternal const int WM_QUIT = 0x0012;\n\tinternal const int WM_ERASEBKGND = 0x0014;\n\tinternal const int WM_SYSCOLORCHANGE = 0x0015;\n\tinternal const int WM_SHOWWINDOW = 0x0018;\n\tinternal const int WM_SETTINGCHANGE = 0x001A;\n\tinternal const int WM_DEVMODECHANGE = 0x001B;\n\tinternal const int WM_ACTIVATEAPP = 0x001C;\n\tinternal const int WM_FONTCHANGE = 0x001D;\n\tinternal const int WM_TIMECHANGE = 0x001E;\n\tinternal const int WM_CANCELMODE = 0x001F;\n\tinternal const int WM_SETCURSOR = 0x0020;\n\tinternal const int WM_MOUSEACTIVATE = 0x0021;\n\tinternal const int WM_CHILDACTIVATE = 0x0022;\n\tinternal const int WM_QUEUESYNC = 0x0023;\n\tinternal const int WM_GETMINMAXINFO = 0x0024;\n\tinternal const int WM_PAINTICON = 0x0026;\n\tinternal const int WM_ICONERASEBKGND = 0x0027;\n\tinternal const int WM_NEXTDLGCTL = 0x0028;\n\tinternal const int WM_SPOOLERSTATUS = 0x002A;\n\tinternal const int WM_DRAWITEM = 0x002B;\n\tinternal const int WM_MEASUREITEM = 0x002C;\n\tinternal const int WM_DELETEITEM = 0x002D;\n\tinternal const int WM_VKEYTOITEM = 0x002E;\n\tinternal const int WM_CHARTOITEM = 0x002F;\n\tinternal const int WM_SETFONT = 0x0030;\n\tinternal const int WM_GETFONT = 0x0031;\n\tinternal const int WM_SETHOTKEY = 0x0032;\n\tinternal const int WM_GETHOTKEY = 0x0033;\n\tinternal const int WM_QUERYDRAGICON = 0x0037;\n\tinternal const int WM_COMPAREITEM = 0x0039;\n\tinternal const int WM_GETOBJECT = 0x003D;\n\tinternal const int WM_COMPACTING = 0x0041;\n\tinternal const int WM_WINDOWPOSCHANGING = 0x0046;\n\tinternal const int WM_WINDOWPOSCHANGED = 0x0047;\n\tinternal const int WM_COPYDATA = 0x004A;\n\tinternal const int WM_CANCELJOURNAL = 0x004B;\n\tinternal const int WM_NOTIFY = 0x004E;\n\tinternal const int WM_INPUTLANGCHANGEREQUEST = 0x0050;\n\tinternal const int WM_INPUTLANGCHANGE = 0x0051;\n\tinternal const int WM_TCARD = 0x0052;\n\tinternal const int WM_HELP = 0x0053;\n\tinternal const int WM_USERCHANGED = 0x0054;\n\tinternal const int WM_NOTIFYFORMAT = 0x0055;\n\tinternal const int WM_CONTEXTMENU = 0x007B;\n\tinternal const int WM_STYLECHANGING = 0x007C;\n\tinternal const int WM_STYLECHANGED = 0x007D;\n\tinternal const int WM_DISPLAYCHANGE = 0x007E;\n\tinternal const int WM_GETICON = 0x007F;\n\tinternal const int WM_SETICON = 0x0080;\n\tinternal const int WM_NCCREATE = 0x0081;\n\tinternal const int WM_NCDESTROY = 0x0082;\n\tinternal const int WM_NCCALCSIZE = 0x0083;\n\tinternal const int WM_NCHITTEST = 0x0084;\n\tinternal const int WM_NCPAINT = 0x0085;\n\tinternal const int WM_NCACTIVATE = 0x0086;\n\tinternal const int WM_GETDLGCODE = 0x0087;\n\tinternal const int WM_SYNCPAINT = 0x0088;\n\tinternal const int WM_NCMOUSEMOVE = 0x00A0;\n\tinternal const int WM_NCLBUTTONDOWN = 0x00A1;\n\tinternal const int WM_NCLBUTTONUP = 0x00A2;\n\tinternal const int WM_NCLBUTTONDBLCLK = 0x00A3;\n\tinternal const int WM_NCRBUTTONDOWN = 0x00A4;\n\tinternal const int WM_NCRBUTTONUP = 0x00A5;\n\tinternal const int WM_NCRBUTTONDBLCLK = 0x00A6;\n\tinternal const int WM_NCMBUTTONDOWN = 0x00A7;\n\tinternal const int WM_NCMBUTTONUP = 0x00A8;\n\tinternal const int WM_NCMBUTTONDBLCLK = 0x00A9;\n\tinternal const int WM_NCXBUTTONDOWN = 0x00AB;\n\tinternal const int WM_NCXBUTTONUP = 0x00AC;\n\tinternal const int WM_NCXBUTTONDBLCLK = 0x00AD;\n\tinternal const int WM_INPUT_DEVICE_CHANGE = 0x00FE;\n\tinternal const int WM_INPUT = 0x00FF;\n\tinternal const int WM_KEYDOWN = 0x0100;\n\tinternal const int WM_KEYUP = 0x0101;\n\tinternal const int WM_CHAR = 0x0102;\n\tinternal const int WM_DEADCHAR = 0x0103;\n\tinternal const int WM_SYSKEYDOWN = 0x0104;\n\tinternal const int WM_SYSKEYUP = 0x0105;\n\tinternal const int WM_SYSCHAR = 0x0106;\n\tinternal const int WM_SYSDEADCHAR = 0x0107;\n\tinternal const int WM_UNICHAR = 0x0109;\n\tinternal const int WM_IME_STARTCOMPOSITION = 0x010D;\n\tinternal const int WM_IME_ENDCOMPOSITION = 0x010E;\n\tinternal const int WM_IME_COMPOSITION = 0x010F;\n\tinternal const int WM_INITDIALOG = 0x0110;\n\tinternal const int WM_COMMAND = 0x0111;\n\tinternal const int WM_SYSCOMMAND = 0x0112;\n\tinternal const int WM_TIMER = 0x0113;\n\tinternal const int WM_HSCROLL = 0x0114;\n\tinternal const int WM_VSCROLL = 0x0115;\n\tinternal const int WM_INITMENU = 0x0116;\n\tinternal const int WM_INITMENUPOPUP = 0x0117;\n\tinternal const int WM_MENUSELECT = 0x011F;\n\tinternal const int WM_MENUCHAR = 0x0120;\n\tinternal const int WM_ENTERIDLE = 0x0121;\n\tinternal const int WM_MENURBUTTONUP = 0x0122;\n\tinternal const int WM_MENUDRAG = 0x0123;\n\tinternal const int WM_MENUGETOBJECT = 0x0124;\n\tinternal const int WM_UNINITMENUPOPUP = 0x0125;\n\tinternal const int WM_MENUCOMMAND = 0x0126;\n\tinternal const int WM_CHANGEUISTATE = 0x0127;\n\tinternal const int WM_UPDATEUISTATE = 0x0128;\n\tinternal const int WM_QUERYUISTATE = 0x0129;\n\tinternal const int WM_CTLCOLORMSGBOX = 0x0132;\n\tinternal const int WM_CTLCOLOREDIT = 0x0133;\n\tinternal const int WM_CTLCOLORLISTBOX = 0x0134;\n\tinternal const int WM_CTLCOLORBTN = 0x0135;\n\tinternal const int WM_CTLCOLORDLG = 0x0136;\n\tinternal const int WM_CTLCOLORSCROLLBAR = 0x0137;\n\tinternal const int WM_CTLCOLORSTATIC = 0x0138;\n\tinternal const int WM_MOUSEFIRST = 0x0200;\n\tinternal const int WM_MOUSEMOVE = 0x0200;\n\tinternal const int WM_LBUTTONDOWN = 0x0201;\n\tinternal const int WM_LBUTTONUP = 0x0202;\n\tinternal const int WM_LBUTTONDBLCLK = 0x0203;\n\tinternal const int WM_RBUTTONDOWN = 0x0204;\n\tinternal const int WM_RBUTTONUP = 0x0205;\n\tinternal const int WM_RBUTTONDBLCLK = 0x0206;\n\tinternal const int WM_MBUTTONDOWN = 0x0207;\n\tinternal const int WM_MBUTTONUP = 0x0208;\n\tinternal const int WM_MBUTTONDBLCLK = 0x0209;\n\tinternal const int WM_MOUSEWHEEL = 0x020A;\n\tinternal const int WM_XBUTTONDOWN = 0x020B;\n\tinternal const int WM_XBUTTONUP = 0x020C;\n\tinternal const int WM_XBUTTONDBLCLK = 0x020D;\n\tinternal const int WM_MOUSEHWHEEL = 0x020E;\n\tinternal const int WM_MOUSELAST = 0x020E;\n\tinternal const int WM_PARENTNOTIFY = 0x0210;\n\tinternal const int WM_ENTERMENULOOP = 0x0211;\n\tinternal const int WM_EXITMENULOOP = 0x0212;\n\tinternal const int WM_NEXTMENU = 0x0213;\n\tinternal const int WM_SIZING = 0x0214;\n\tinternal const int WM_CAPTURECHANGED = 0x0215;\n\tinternal const int WM_MOVING = 0x0216;\n\tinternal const int WM_POWERBROADCAST = 0x0218;\n\tinternal const int WM_DEVICECHANGE = 0x0219;\n\tinternal const int WM_MDICREATE = 0x0220;\n\tinternal const int WM_MDIDESTROY = 0x0221;\n\tinternal const int WM_MDIACTIVATE = 0x0222;\n\tinternal const int WM_MDIRESTORE = 0x0223;\n\tinternal const int WM_MDINEXT = 0x0224;\n\tinternal const int WM_MDIMAXIMIZE = 0x0225;\n\tinternal const int WM_MDITILE = 0x0226;\n\tinternal const int WM_MDICASCADE = 0x0227;\n\tinternal const int WM_MDIICONARRANGE = 0x0228;\n\tinternal const int WM_MDIGETACTIVE = 0x0229;\n\tinternal const int WM_MDISETMENU = 0x0230;\n\tinternal const int WM_ENTERSIZEMOVE = 0x0231;\n\tinternal const int WM_EXITSIZEMOVE = 0x0232;\n\tinternal const int WM_DROPFILES = 0x0233;\n\tinternal const int WM_MDIREFRESHMENU = 0x0234;\n\tinternal const int WM_IME_SETCONTEXT = 0x0281;\n\tinternal const int WM_IME_NOTIFY = 0x0282;\n\tinternal const int WM_IME_CONTROL = 0x0283;\n\tinternal const int WM_IME_COMPOSITIONFULL = 0x0284;\n\tinternal const int WM_IME_SELECT = 0x0285;\n\tinternal const int WM_IME_CHAR = 0x0286;\n\tinternal const int WM_IME_REQUEST = 0x0288;\n\tinternal const int WM_IME_KEYDOWN = 0x0290;\n\tinternal const int WM_IME_KEYUP = 0x0291;\n\tinternal const int WM_MOUSEHOVER = 0x02A1;\n\tinternal const int WM_MOUSELEAVE = 0x02A3;\n\tinternal const int WM_NCMOUSEHOVER = 0x02A0;\n\tinternal const int WM_NCMOUSELEAVE = 0x02A2;\n\tinternal const int WM_WTSSESSION_CHANGE = 0x02B1;\n\tinternal const int WM_DPICHANGED = 0x2E0;\n\tinternal const int WM_DPICHANGED_BEFOREPARENT = 0x2E2;\n\tinternal const int WM_DPICHANGED_AFTERPARENT = 0x02E3;\n\tinternal const int WM_GETDPISCALEDSIZE = 0x02E4;\n\tinternal const int WM_CUT = 0x0300;\n\tinternal const int WM_COPY = 0x0301;\n\tinternal const int WM_PASTE = 0x0302;\n\tinternal const int WM_CLEAR = 0x0303;\n\tinternal const int WM_UNDO = 0x0304;\n\tinternal const int WM_RENDERFORMAT = 0x0305;\n\tinternal const int WM_RENDERALLFORMATS = 0x0306;\n\tinternal const int WM_DESTROYCLIPBOARD = 0x0307;\n\tinternal const int WM_DRAWCLIPBOARD = 0x0308;\n\tinternal const int WM_PAINTCLIPBOARD = 0x0309;\n\tinternal const int WM_VSCROLLCLIPBOARD = 0x030A;\n\tinternal const int WM_SIZECLIPBOARD = 0x030B;\n\tinternal const int WM_ASKCBFORMATNAME = 0x030C;\n\tinternal const int WM_CHANGECBCHAIN = 0x030D;\n\tinternal const int WM_HSCROLLCLIPBOARD = 0x030E;\n\tinternal const int WM_QUERYNEWPALETTE = 0x030F;\n\tinternal const int WM_PALETTEISCHANGING = 0x0310;\n\tinternal const int WM_PALETTECHANGED = 0x0311;\n\tinternal const int WM_HOTKEY = 0x0312;\n\tinternal const int WM_PRINT = 0x0317;\n\tinternal const int WM_PRINTCLIENT = 0x0318;\n\tinternal const int WM_APPCOMMAND = 0x0319;\n\tinternal const int WM_THEMECHANGED = 0x031A;\n\tinternal const int WM_CLIPBOARDUPDATE = 0x031D;\n\tinternal const int WM_DWMCOMPOSITIONCHANGED = 0x031E;\n\tinternal const int WM_DWMNCRENDERINGCHANGED = 0x031F;\n\tinternal const int WM_DWMCOLORIZATIONCOLORCHANGED = 0x0320;\n\tinternal const int WM_DWMWINDOWMAXIMIZEDCHANGE = 0x0321;\n\tinternal const int WM_GETTITLEBARINFOEX = 0x033F;\n\tinternal const int WM_APP = 0x8000;\n\tinternal const int WM_USER = 0x0400;\n\n\tinternal const int WM_CPL_LAUNCH = WM_USER + 0x1000;\n\tinternal const int WM_CPL_LAUNCHED = WM_USER + 0x1001;\n\tinternal const int WM_SYSTIMER = 0x118;\n\tinternal const int WM_HSHELL_ACCESSIBILITYSTATE = 11;\n\tinternal const int WM_HSHELL_ACTIVATESHELLWINDOW = 3;\n\tinternal const int WM_HSHELL_APPCOMMAND = 12;\n\tinternal const int WM_HSHELL_GETMINRECT = 5;\n\tinternal const int WM_HSHELL_LANGUAGE = 8;\n\tinternal const int WM_HSHELL_REDRAW = 6;\n\tinternal const int WM_HSHELL_TASKMAN = 7;\n\tinternal const int WM_HSHELL_WINDOWCREATED = 1;\n\tinternal const int WM_HSHELL_WINDOWDESTROYED = 2;\n\tinternal const int WM_HSHELL_WINDOWACTIVATED = 4;\n\tinternal const int WM_HSHELL_WINDOWREPLACED = 13;\n\n\tinternal const int WM_REFLECT = 0x2000;\n\n\t#endregion\n\n\t#region control styles, messages etc\n\n\t//ES_, EM_, EN_\n\tinternal const WS ES_MULTILINE = (WS)0x4;\n\tinternal const WS ES_PASSWORD = (WS)0x20;\n\tinternal const WS ES_AUTOVSCROLL = (WS)0x40;\n\tinternal const WS ES_AUTOHSCROLL = (WS)0x80;\n\tinternal const WS ES_WANTRETURN = (WS)0x1000;\n\tinternal const WS ES_NUMBER = (WS)0x2000;\n\n\tinternal const int EM_SETSEL = 0xB1;\n\tinternal const int EM_SETCUEBANNER = 0x1501;\n\n\t//CBS_, CB_, CBN_\n\tinternal const WS CBS_SIMPLE = (WS)1;\n\tinternal const WS CBS_DROPDOWN = (WS)2;\n\tinternal const WS CBS_DROPDOWNLIST = (WS)3;\n\tinternal const WS CBS_AUTOHSCROLL = (WS)0x40;\n\n\tinternal const int CB_INSERTSTRING = 330;\n\tinternal const int CB_SETCUEBANNER = 0x1703;\n\n\tinternal const int MN_GETHMENU = 0x1E1;\n\n\t#endregion\n\n\t#region CS_\n\n\tinternal const uint CS_VREDRAW = 0x1;\n\tinternal const uint CS_HREDRAW = 0x2;\n\tinternal const uint CS_DBLCLKS = 0x8;\n\tinternal const uint CS_OWNDC = 0x20;\n\tinternal const uint CS_CLASSDC = 0x40;\n\tinternal const uint CS_PARENTDC = 0x80;\n\tinternal const uint CS_NOCLOSE = 0x200;\n\tinternal const uint CS_SAVEBITS = 0x800;\n\tinternal const uint CS_BYTEALIGNCLIENT = 0x1000;\n\tinternal const uint CS_BYTEALIGNWINDOW = 0x2000;\n\tinternal const uint CS_GLOBALCLASS = 0x4000;\n\tinternal const uint CS_IME = 0x10000;\n\tinternal const uint CS_DROPSHADOW = 0x20000;\n\n\t#endregion\n\n\t#region HT (hit-test)\n\n\tinternal const int HTERROR = -2;\n\tinternal const int HTTRANSPARENT = -1;\n\tinternal const int HTNOWHERE = 0;\n\tinternal const int HTCLIENT = 1;\n\tinternal const int HTCAPTION = 2;\n\tinternal const int HTSYSMENU = 3;\n\tinternal const int HTSIZE = 4;\n\tinternal const int HTMENU = 5;\n\tinternal const int HTHSCROLL = 6;\n\tinternal const int HTVSCROLL = 7;\n\tinternal const int HTMINBUTTON = 8;\n\tinternal const int HTMAXBUTTON = 9;\n\tinternal const int HTLEFT = 10;\n\tinternal const int HTRIGHT = 11;\n\tinternal const int HTTOP = 12;\n\tinternal const int HTTOPLEFT = 13;\n\tinternal const int HTTOPRIGHT = 14;\n\tinternal const int HTBOTTOM = 15;\n\tinternal const int HTBOTTOMLEFT = 16;\n\tinternal const int HTBOTTOMRIGHT = 17;\n\tinternal const int HTBORDER = 18;\n\tinternal const int HTOBJECT = 19;\n\tinternal const int HTCLOSE = 20;\n\tinternal const int HTHELP = 21;\n\tinternal const int HTSIZEFIRST = HTLEFT;\n\tinternal const int HTSIZELAST = HTBOTTOMRIGHT;\n\n\t#endregion\n\n\t#region SC_\n\tinternal const int SC_SIZE = 0xF000;\n\tinternal const int SC_MOVE = 0xF010;\n\tinternal const int SC_MINIMIZE = 0xF020;\n\tinternal const int SC_MAXIMIZE = 0xF030;\n\tinternal const int SC_NEXTWINDOW = 0xF040;\n\tinternal const int SC_PREVWINDOW = 0xF050;\n\tinternal const int SC_CLOSE = 0xF060;\n\tinternal const int SC_VSCROLL = 0xF070;\n\tinternal const int SC_HSCROLL = 0xF080;\n\tinternal const int SC_MOUSEMENU = 0xF090;\n\tinternal const int SC_KEYMENU = 0xF100;\n\tinternal const int SC_ARRANGE = 0xF110;\n\tinternal const int SC_RESTORE = 0xF120;\n\tinternal const int SC_TASKLIST = 0xF130;\n\tinternal const int SC_SCREENSAVE = 0xF140;\n\tinternal const int SC_HOTKEY = 0xF150;\n\tinternal const int SC_DEFAULT = 0xF160;\n\tinternal const int SC_MONITORPOWER = 0xF170;\n\tinternal const int SC_CONTEXTHELP = 0xF180;\n\tinternal const int SC_SEPARATOR = 0xF00F;\n\t#endregion\n\n\t#region COLOR_\n\n\tinternal const int COLOR_SCROLLBAR = 0;\n\tinternal const int COLOR_BACKGROUND = 1;\n\tinternal const int COLOR_ACTIVECAPTION = 2;\n\tinternal const int COLOR_INACTIVECAPTION = 3;\n\tinternal const int COLOR_MENU = 4;\n\tinternal const int COLOR_WINDOW = 5;\n\tinternal const int COLOR_WINDOWFRAME = 6;\n\tinternal const int COLOR_MENUTEXT = 7;\n\tinternal const int COLOR_WINDOWTEXT = 8;\n\tinternal const int COLOR_CAPTIONTEXT = 9;\n\tinternal const int COLOR_ACTIVEBORDER = 10;\n\tinternal const int COLOR_INACTIVEBORDER = 11;\n\tinternal const int COLOR_APPWORKSPACE = 12;\n\tinternal const int COLOR_HIGHLIGHT = 13;\n\tinternal const int COLOR_HIGHLIGHTTEXT = 14;\n\tinternal const int COLOR_BTNFACE = 15;\n\tinternal const int COLOR_BTNSHADOW = 16;\n\tinternal const int COLOR_GRAYTEXT = 17;\n\tinternal const int COLOR_BTNTEXT = 18;\n\tinternal const int COLOR_INACTIVECAPTIONTEXT = 19;\n\tinternal const int COLOR_BTNHIGHLIGHT = 20;\n\tinternal const int COLOR_3DDKSHADOW = 21;\n\tinternal const int COLOR_3DLIGHT = 22;\n\tinternal const int COLOR_INFOTEXT = 23;\n\tinternal const int COLOR_INFOBK = 24;\n\tinternal const int COLOR_HOTLIGHT = 26;\n\tinternal const int COLOR_GRADIENTACTIVECAPTION = 27;\n\tinternal const int COLOR_GRADIENTINACTIVECAPTION = 28;\n\tinternal const int COLOR_MENUHILIGHT = 29;\n\tinternal const int COLOR_MENUBAR = 30;\n\tinternal const int COLOR_DESKTOP = 1;\n\tinternal const int COLOR_3DFACE = 15;\n\tinternal const int COLOR_3DSHADOW = 16;\n\tinternal const int COLOR_3DHIGHLIGHT = 20;\n\tinternal const int COLOR_3DHILIGHT = 20;\n\tinternal const int COLOR_BTNHILIGHT = 20;\n\n\t#endregion\n\n\t#region QS_\n\n\tinternal const uint QS_KEY = 0x1;\n\tinternal const uint QS_MOUSEMOVE = 0x2;\n\tinternal const uint QS_MOUSEBUTTON = 0x4;\n\tinternal const uint QS_POSTMESSAGE = 0x8;\n\tinternal const uint QS_TIMER = 0x10;\n\tinternal const uint QS_PAINT = 0x20;\n\tinternal const uint QS_SENDMESSAGE = 0x40;\n\tinternal const uint QS_HOTKEY = 0x80;\n\tinternal const uint QS_ALLPOSTMESSAGE = 0x100;\n\tinternal const uint QS_RAWINPUT = 0x400;\n\tinternal const uint QS_TOUCH = 0x800;\n\tinternal const uint QS_POINTER = 0x1000;\n\tinternal const uint QS_MOUSE = 0x6;\n\tinternal const uint QS_INPUT = 0x1C07;\n\tinternal const uint QS_ALLEVENTS = 0x1CBF;\n\tinternal const uint QS_ALLINPUT = 0x1CFF;\n\n\t#endregion\n\n\t#region WAIT_\n\n\tinternal const int WAIT_FAILED = -1;\n\tinternal const int WAIT_OBJECT_0 = 0x0;\n\tinternal const int WAIT_ABANDONED = 0x80;\n\tinternal const int WAIT_ABANDONED_0 = 0x80;\n\tinternal const int WAIT_IO_COMPLETION = 0xC0;\n\tinternal const int WAIT_TIMEOUT = 0x102;\n\n\t#endregion\n\n\t#region LR_, IMAGE_\n\n\tinternal const int IMAGE_BITMAP = 0;\n\tinternal const int IMAGE_ICON = 1;\n\tinternal const int IMAGE_CURSOR = 2;\n\tinternal const uint LR_MONOCHROME = 0x1;\n\tinternal const uint LR_COLOR = 0x2;\n\tinternal const uint LR_COPYRETURNORG = 0x4;\n\tinternal const uint LR_COPYDELETEORG = 0x8;\n\tinternal const uint LR_LOADFROMFILE = 0x10;\n\tinternal const uint LR_LOADTRANSPARENT = 0x20;\n\tinternal const uint LR_DEFAULTSIZE = 0x40;\n\tinternal const uint LR_VGACOLOR = 0x80;\n\tinternal const uint LR_LOADMAP3DCOLORS = 0x1000;\n\tinternal const uint LR_CREATEDIBSECTION = 0x2000;\n\tinternal const uint LR_COPYFROMRESOURCE = 0x4000;\n\tinternal const uint LR_SHARED = 0x8000;\n\n\t#endregion\n\n\t#region SFGAO_\n\n\tinternal const uint SFGAO_CANCOPY = 1;\n\tinternal const uint SFGAO_CANMOVE = 2;\n\tinternal const uint SFGAO_CANLINK = 4;\n\tinternal const uint SFGAO_STORAGE = 0x00000008;\n\tinternal const uint SFGAO_CANRENAME = 0x00000010;\n\tinternal const uint SFGAO_CANDELETE = 0x00000020;\n\tinternal const uint SFGAO_HASPROPSHEET = 0x00000040;\n\tinternal const uint SFGAO_DROPTARGET = 0x00000100;\n\tinternal const uint SFGAO_CAPABILITYMASK = 0x00000177;\n\tinternal const uint SFGAO_SYSTEM = 0x00001000;\n\tinternal const uint SFGAO_ENCRYPTED = 0x00002000;\n\tinternal const uint SFGAO_ISSLOW = 0x00004000;\n\tinternal const uint SFGAO_GHOSTED = 0x00008000;\n\tinternal const uint SFGAO_LINK = 0x00010000;\n\tinternal const uint SFGAO_SHARE = 0x00020000;\n\tinternal const uint SFGAO_READONLY = 0x00040000;\n\tinternal const uint SFGAO_HIDDEN = 0x00080000;\n\tinternal const uint SFGAO_DISPLAYATTRMASK = 0x000FC000;\n\tinternal const uint SFGAO_FILESYSANCESTOR = 0x10000000;\n\tinternal const uint SFGAO_FOLDER = 0x20000000;\n\tinternal const uint SFGAO_FILESYSTEM = 0x40000000;\n\tinternal const uint SFGAO_HASSUBFOLDER = 0x80000000;\n\tinternal const uint SFGAO_CONTENTSMASK = 0x80000000;\n\tinternal const uint SFGAO_VALIDATE = 0x01000000;\n\tinternal const uint SFGAO_REMOVABLE = 0x02000000;\n\tinternal const uint SFGAO_COMPRESSED = 0x04000000;\n\tinternal const uint SFGAO_BROWSABLE = 0x08000000;\n\tinternal const uint SFGAO_NONENUMERATED = 0x00100000;\n\tinternal const uint SFGAO_NEWCONTENT = 0x00200000;\n\tinternal const uint SFGAO_CANMONIKER = 0x00400000;\n\tinternal const uint SFGAO_HASSTORAGE = 0x00400000;\n\tinternal const uint SFGAO_STREAM = 0x00400000;\n\tinternal const uint SFGAO_STORAGEANCESTOR = 0x00800000;\n\tinternal const uint SFGAO_STORAGECAPMASK = 0x70C50008;\n\tinternal const uint SFGAO_PKEYSFGAOMASK = 0x81044000;\n\n\t#endregion\n\n\t#region STGM_\n\n\tinternal const uint STGM_DIRECT = 0x0;\n\tinternal const uint STGM_TRANSACTED = 0x10000;\n\tinternal const uint STGM_SIMPLE = 0x8000000;\n\tinternal const uint STGM_READ = 0x0;\n\tinternal const uint STGM_WRITE = 0x1;\n\tinternal const uint STGM_READWRITE = 0x2;\n\tinternal const uint STGM_SHARE_DENY_NONE = 0x40;\n\tinternal const uint STGM_SHARE_DENY_READ = 0x30;\n\tinternal const uint STGM_SHARE_DENY_WRITE = 0x20;\n\tinternal const uint STGM_SHARE_EXCLUSIVE = 0x10;\n\tinternal const uint STGM_PRIORITY = 0x40000;\n\tinternal const uint STGM_DELETEONRELEASE = 0x4000000;\n\tinternal const uint STGM_NOSCRATCH = 0x100000;\n\tinternal const uint STGM_CREATE = 0x1000;\n\tinternal const uint STGM_CONVERT = 0x20000;\n\tinternal const uint STGM_FAILIFTHERE = 0x0;\n\tinternal const uint STGM_NOSNAPSHOT = 0x200000;\n\tinternal const uint STGM_DIRECT_SWMR = 0x400000;\n\n\t#endregion\n\n\t#region MA_\n\tinternal const int MA_ACTIVATE = 1;\n\tinternal const int MA_ACTIVATEANDEAT = 2;\n\tinternal const int MA_NOACTIVATE = 3;\n\tinternal const int MA_NOACTIVATEANDEAT = 4;\n\t#endregion\n\n\t#region MK_\n\n\tinternal const int MK_LBUTTON = 0x1;\n\tinternal const int MK_RBUTTON = 0x2;\n\tinternal const int MK_SHIFT = 0x4;\n\tinternal const int MK_CONTROL = 0x8;\n\tinternal const int MK_MBUTTON = 0x10;\n\n\t#endregion\n\n\t#region CF_\n\n\tinternal const int CF_TEXT = 1;\n\tinternal const int CF_BITMAP = 2;\n\tinternal const int CF_METAFILEPICT = 3;\n\tinternal const int CF_SYLK = 4;\n\tinternal const int CF_DIF = 5;\n\tinternal const int CF_TIFF = 6;\n\tinternal const int CF_OEMTEXT = 7;\n\tinternal const int CF_DIB = 8;\n\tinternal const int CF_PALETTE = 9;\n\t//internal const int CF_PENDATA = 10; //obsolete\n\tinternal const int CF_RIFF = 11;\n\tinternal const int CF_WAVE = 12;\n\tinternal const int CF_UNICODETEXT = 13;\n\tinternal const int CF_ENHMETAFILE = 14;\n\tinternal const int CF_HDROP = 15;\n\tinternal const int CF_LOCALE = 16;\n\tinternal const int CF_DIBV5 = 17;\n\tinternal const int CF_MAX = 18;\n\t//internal const int CF_OWNERDISPLAY = 0x80; //these are rare and not supported by this library\n\t//internal const int CF_DSPTEXT = 0x81;\n\t//internal const int CF_DSPBITMAP = 0x82;\n\t//internal const int CF_DSPMETAFILEPICT = 0x83;\n\t//internal const int CF_DSPENHMETAFILE = 0x8E;\n\t//internal const int CF_PRIVATEFIRST = 0x200;\n\t//internal const int CF_PRIVATELAST = 0x2FF;\n\t//internal const int CF_GDIOBJFIRST = 0x300;\n\t//internal const int CF_GDIOBJLAST = 0x3FF;\n\n\t#endregion\n\n\t#region misc\n\n\tinternal const int IDI_APPLICATION = 32512;\n\tinternal const int PBT_APMSUSPEND = 0x4;\n\n\t#endregion\n\n\n\n\n\n\n\t#region ENUM\n\n\t[Flags]\n\tinternal enum VARENUM : ushort\n\t{\n\t\tVT_EMPTY,\n\t\tVT_NULL,\n\t\tVT_I2,\n\t\tVT_I4,\n\t\tVT_R4,\n\t\tVT_R8,\n\t\tVT_CY,\n\t\tVT_DATE,\n\t\tVT_BSTR,\n\t\tVT_DISPATCH,\n\t\tVT_ERROR,\n\t\tVT_BOOL,\n\t\tVT_VARIANT,\n\t\tVT_UNKNOWN,\n\t\tVT_DECIMAL,\n\t\tVT_I1 = 16,\n\t\tVT_UI1,\n\t\tVT_UI2,\n\t\tVT_UI4,\n\t\tVT_I8,\n\t\tVT_UI8,\n\t\tVT_INT,\n\t\tVT_UINT,\n\t\tVT_VOID,\n\t\tVT_HRESULT,\n\t\tVT_PTR,\n\t\tVT_SAFEARRAY,\n\t\tVT_CARRAY,\n\t\tVT_USERDEFINED,\n\t\tVT_LPSTR,\n\t\tVT_LPWSTR,\n\t\tVT_RECORD = 36,\n\t\tVT_INT_PTR,\n\t\tVT_UINT_PTR,\n\t\tVT_FILETIME = 64,\n\t\tVT_BLOB,\n\t\tVT_STREAM,\n\t\tVT_STORAGE,\n\t\tVT_STREAMED_OBJECT,\n\t\tVT_STORED_OBJECT,\n\t\tVT_BLOB_OBJECT,\n\t\tVT_CF,\n\t\tVT_CLSID,\n\t\tVT_VERSIONED_STREAM,\n\t\tVT_BSTR_BLOB = 0xFFF,\n\t\tVT_VECTOR,\n\t\tVT_ARRAY = 0x2000,\n\t\tVT_BYREF = 0x4000,\n\t\tVT_RESERVED = 0x8000,\n\t\tVT_ILLEGAL = 0xFFFF,\n\t\tVT_ILLEGALMASKED = 0xFFF,\n\t\tVT_TYPEMASK = 0xFFF\n\t}\n\n\t#endregion\n\n\t#region strings\n\n\tinternal const string string_IES = \"Internet Explorer_Server\";\n\n\n\t#endregion\n}\n"
  },
  {
    "path": "Au/Api/Api_public.cs",
    "content": "#pragma warning disable 649, 169 //field never assigned/used\n#pragma warning disable 1591 //missing XML documentation\n\nnamespace Au.Types;\n\n/// <summary>\n/// Window styles.\n/// </summary>\n/// <remarks>\n/// Reference: <ms>Window Styles</ms>.\n/// Here names are without prefix <c>WS_</c>. For example, instead of <c>WS_BORDER</c> use <c>WS.BORDER</c>. Not included constants that are 0 (eg <c>WS_TILED</c>) or are duplicate (eg <c>WS_SIZEBOX</c> is same as <c>WS_THICKFRAME</c>) or consist of multiple other constants (eg <c>WS_TILEDWINDOW</c>).\n/// </remarks>\n[Flags]\npublic enum WS : uint {\n\tPOPUP = 0x80000000,\n\tCHILD = 0x40000000,\n\tMINIMIZE = 0x20000000,\n\tVISIBLE = 0x10000000,\n\tDISABLED = 0x08000000,\n\tCLIPSIBLINGS = 0x04000000,\n\tCLIPCHILDREN = 0x02000000,\n\tMAXIMIZE = 0x01000000,\n\tBORDER = 0x00800000,\n\tDLGFRAME = 0x00400000,\n\tVSCROLL = 0x00200000,\n\tHSCROLL = 0x00100000,\n\tSYSMENU = 0x00080000,\n\tTHICKFRAME = 0x00040000,\n\tMINIMIZEBOX = 0x00020000,\n\tGROUP = 0x00020000,\n\tMAXIMIZEBOX = 0x00010000,\n\tTABSTOP = 0x00010000,\n\tCAPTION = BORDER | DLGFRAME,\n\t//these can cause bugs and confusion because consist of several styles. Not useful in C#, because used only to create native windows.\n\t//OVERLAPPEDWINDOW = CAPTION | SYSMENU | THICKFRAME | MINIMIZEBOX | MAXIMIZEBOX,\n\t//POPUPWINDOW = POPUP | BORDER | SYSMENU,\n}\n\n/// <summary>\n/// Window extended styles.\n/// </summary>\n/// <remarks>\n/// Reference: <ms>Extended Window Styles</ms>.\n/// Here names are without prefix <c>WS_EX_</c>. For example, instead of <c>WS_EX_TOOLWINDOW</c> use <c>WSE.TOOLWINDOW</c>. Not included constants that are 0 (eg <c>WS_EX_LEFT</c>).\n/// </remarks>\n[Flags]\npublic enum WSE : uint {\n\tDLGMODALFRAME = 0x00000001,\n\tNOPARENTNOTIFY = 0x00000004,\n\tTOPMOST = 0x00000008,\n\tACCEPTFILES = 0x00000010,\n\tTRANSPARENT = 0x00000020,\n\tMDICHILD = 0x00000040,\n\tTOOLWINDOW = 0x00000080,\n\tWINDOWEDGE = 0x00000100,\n\tCLIENTEDGE = 0x00000200,\n\tCONTEXTHELP = 0x00000400,\n\t//LEFT = 0x00000000,\n\tRIGHT = 0x00001000,\n\t//LTRREADING = 0x00000000,\n\tRTLREADING = 0x00002000,\n\t//RIGHTSCROLLBAR = 0x00000000,\n\tLEFTSCROLLBAR = 0x00004000,\n\tCONTROLPARENT = 0x00010000,\n\tSTATICEDGE = 0x00020000,\n\tAPPWINDOW = 0x00040000,\n\tLAYERED = 0x00080000,\n\tNOINHERITLAYOUT = 0x00100000,\n\tNOREDIRECTIONBITMAP = 0x00200000,\n\tLAYOUTRTL = 0x00400000,\n\tCOMPOSITED = 0x02000000,\n\tNOACTIVATE = 0x08000000,\n}\n\n/// <summary>API <ms>MSG</ms></summary>\npublic struct MSG //WinMSG\n{\n\tpublic wnd hwnd;\n\tpublic int message;\n\tpublic nint wParam;\n\tpublic nint lParam;\n\tpublic int time;\n\tpublic POINT pt;\n\n\tpublic override string ToString() {\n\t\tWndUtil.PrintMsg(out string s, this, new() { Indent = false, Number = false });\n\t\treturn s;\n\t}\n\n\tpublic static implicit operator MSG(in System.Windows.Forms.Message m)\n\t\t=> new MSG { hwnd = (wnd)m.HWnd, message = m.Msg, wParam = m.WParam, lParam = m.LParam };\n\n\tpublic static implicit operator MSG(in System.Windows.Interop.MSG m)\n\t\t=> new MSG { hwnd = (wnd)m.hwnd, message = m.message, wParam = m.wParam, lParam = m.lParam, time = m.time, pt = (m.pt_x, m.pt_y) };\n}\n\n/// <summary><see cref=\"GUITHREADINFO\"/> flags.</summary>\n[Flags]\npublic enum GTIFlags {\n\tCARETBLINKING = 0x1,\n\tINMOVESIZE = 0x2,\n\tINMENUMODE = 0x4,\n\tSYSTEMMENUMODE = 0x8,\n\tPOPUPMENUMODE = 0x10,\n}\n\n/// <summary>API <ms>GUITHREADINFO</ms></summary>\npublic struct GUITHREADINFO {\n\tpublic int cbSize;\n\tpublic GTIFlags flags;\n\tpublic wnd hwndActive;\n\tpublic wnd hwndFocus;\n\tpublic wnd hwndCapture;\n\tpublic wnd hwndMenuOwner;\n\tpublic wnd hwndMoveSize;\n\tpublic wnd hwndCaret;\n\tpublic RECT rcCaret;\n}\n\n/// <summary>API <ms>CREATESTRUCT</ms></summary>\npublic unsafe struct CREATESTRUCT {\n\tpublic nint lpCreateParams;\n\tpublic IntPtr hInstance;\n\tpublic nint hMenu;\n\tpublic wnd hwndParent;\n\tpublic int cy;\n\tpublic int cx;\n\tpublic int y;\n\tpublic int x;\n\tpublic WS style;\n\tpublic char* lpszName;\n\tpublic char* lpszClass;\n\tpublic WSE dwExStyle;\n\n\tpublic RStr Name => lpszName == default ? default\n\t\t: new RStr(lpszName, Ptr_.Length(lpszName));\n\t//public string Name => lpszName == default ? null : new string(lpszName);\n\n\t/// <summary>\n\t/// If <c>lpszClass</c> is atom, returns string with <c>#</c> prefix and atom value, like <c>\"#32770\"</c>.\n\t/// </summary>\n\tpublic RStr ClassName => (nuint)lpszClass < 0x10000 ? \"#\" + ((int)lpszClass).ToS()\n\t\t: new RStr(lpszClass, Ptr_.Length(lpszClass));\n\t//public string ClassName => (nuint)lpszClass < 0x10000 ? \"#\" + ((int)lpszClass).ToS() : new string(lpszClass);\n\n\t//tested and documented: CBT hook can change only x y cx cy.\n}\n\n/// <summary>API <ms>SIGDN</ms></summary>\npublic enum SIGDN : uint {\n\tNORMALDISPLAY,\n\tPARENTRELATIVEPARSING = 0x80018001,\n\tDESKTOPABSOLUTEPARSING = 0x80028000,\n\tPARENTRELATIVEEDITING = 0x80031001,\n\tDESKTOPABSOLUTEEDITING = 0x8004C000,\n\tFILESYSPATH = 0x80058000,\n\tURL = 0x80068000,\n\tPARENTRELATIVEFORADDRESSBAR = 0x8007C001,\n\tPARENTRELATIVE = 0x80080001,\n\tPARENTRELATIVEFORUI = 0x80094001\n}\n\n/// <summary>API <ms>SetWindowPos</ms> flags. Can be used with <see cref=\"wnd.SetWindowPos\"/>.</summary>\n[Flags]\npublic enum SWPFlags : uint {\n\tNOSIZE = 0x1,\n\tNOMOVE = 0x2,\n\tNOZORDER = 0x4,\n\tNOREDRAW = 0x8,\n\tNOACTIVATE = 0x10,\n\tFRAMECHANGED = 0x20,\n\tSHOWWINDOW = 0x40,\n\tHIDEWINDOW = 0x80,\n\tNOCOPYBITS = 0x100,\n\tNOOWNERZORDER = 0x200,\n\tNOSENDCHANGING = 0x400,\n\t_NOCLIENTSIZE = 0x800,\n\t_NOCLIENTMOVE = 0x1000,\n\tDEFERERASE = 0x2000,\n\tASYNCWINDOWPOS = 0x4000,\n\t_STATECHANGED = 0x8000,\n\t//_10000000 = 0x10000000,\n\t_KNOWNFLAGS = 0xffff,\n\n\t//the undocumented flags would break ToString() if not defined\n}\n\n/// <summary>\n/// Special window handle values. Can be used with <see cref=\"wnd.SetWindowPos\"/>.\n/// See API <ms>SetWindowPos</ms>.\n/// </summary>\npublic enum SpecHWND {\n\tTOP = 0,\n\tBOTTOM = 1,\n\tTOPMOST = -1,\n\tNOTOPMOST = -2,\n\tMESSAGE = -3,\n\tBROADCAST = 0xffff,\n}\n\n/// <summary>\n/// Window long constants. Used with <see cref=\"wnd.GetWindowLong\"/> and <see cref=\"wnd.SetWindowLong\"/>.\n/// See API <ms>GetWindowLong</ms>. See also API <ms>SetWindowSubclass</ms>.\n/// </summary>\npublic static class GWL {\n\tpublic const int WNDPROC = -4;\n\tpublic const int USERDATA = -21;\n\tpublic const int STYLE = -16;\n\tpublic const int ID = -12;\n\tpublic const int HWNDPARENT = -8;\n\tpublic const int HINSTANCE = -6;\n\tpublic const int EXSTYLE = -20;\n\t//info: also there are GWLP_, but their values are the same.\n\n\tpublic static class DWL {\n\t\tpublic static readonly int MSGRESULT = 0;\n\t\tpublic static readonly int DLGPROC = IntPtr.Size;\n\t\tpublic static readonly int USER = IntPtr.Size * 2;\n\t}\n}\n\n/// <summary>\n/// Window class long constants. Used with <see cref=\"WndUtil.GetClassLong\"/>.\n/// See API <ms>WNDCLASSEX</ms>, <ms>GetClassLong</ms>.\n/// </summary>\npublic static class GCL {\n\tpublic const int ATOM = -32;\n\tpublic const int WNDPROC = -24;\n\tpublic const int STYLE = -26;\n\tpublic const int MENUNAME = -8;\n\tpublic const int HMODULE = -16;\n\tpublic const int HICONSM = -34;\n\tpublic const int HICON = -14;\n\tpublic const int HCURSOR = -12;\n\tpublic const int HBRBACKGROUND = -10;\n\tpublic const int CBWNDEXTRA = -18;\n\tpublic const int CBCLSEXTRA = -20;\n\t//info: also there are GCLP_, but their values are the same.\n}\n\n/// <summary>API <ms>WNDPROC</ms></summary>\npublic delegate nint WNDPROC(wnd w, int msg, nint wp, nint lp);\n\n/// <summary>API <ms>SendMessageTimeout</ms> flags. Used with <see cref=\"wnd.SendTimeout\"/>.</summary>\n[Flags]\npublic enum SMTFlags : uint {\n\tBLOCK = 0x0001,\n\tABORTIFHUNG = 0x0002,\n\tNOTIMEOUTIFNOTHUNG = 0x0008,\n\tERRORONEXIT = 0x0020,\n}\n\n/// <summary>API <ms>DrawTextEx</ms> format flags.</summary>\n[Flags]\npublic enum TFFlags {\n\tCENTER = 0x1,\n\tRIGHT = 0x2,\n\tVCENTER = 0x4,\n\tBOTTOM = 0x8,\n\tWORDBREAK = 0x10,\n\tSINGLELINE = 0x20,\n\tEXPANDTABS = 0x40,\n\tTABSTOP = 0x80,\n\tNOCLIP = 0x100,\n\tEXTERNALLEADING = 0x200,\n\tCALCRECT = 0x400,\n\tNOPREFIX = 0x800,\n\tINTERNAL = 0x1000,\n\tEDITCONTROL = 0x2000,\n\tPATH_ELLIPSIS = 0x4000,\n\tEND_ELLIPSIS = 0x8000,\n\tMODIFYSTRING = 0x10000,\n\tRTLREADING = 0x20000,\n\tWORD_ELLIPSIS = 0x40000,\n\tNOFULLWIDTHCHARBREAK = 0x80000,\n\tHIDEPREFIX = 0x100000,\n\tPREFIXONLY = 0x200000\n}\n"
  },
  {
    "path": "Au/Api/Cpp.cs",
    "content": "using static Au.Types.GWL;\n\nnamespace Au.Types;\n\n[DebuggerStepThrough]\nstatic unsafe partial class Cpp {\n\tstatic List<(string dll, nint h)> _dlls = [];\n\n\tstatic Cpp() {\n\t\tLoadAuNativeDll(\"AuCpp.dll\");\n\n\t\tCpp_SetHelperCallback(&_HelperCallback); //note: not in elm ctor. Eg by the elm tool not via elm. Fast.\n\t}\n\n\t/// <summary>\n\t/// Loads correct 64/32/ARM64 version of a private native dll. Then <c>[DllImport]</c> will use it.\n\t/// Used for Au dlls (AuCpp) and LA dlls (Scintilla).\n\t/// </summary>\n\t/// <param name=\"fileName\">Dll file name like <c>\"name.dll\"</c>.</param>\n\t/// <returns>Handle.</returns>\n\t/// <exception cref=\"DllNotFoundException\"></exception>\n\t/// <remarks>\n\t/// Searches in:\n\t/// - subfolder <c>64</c> or <c>32</c> or <c>64\\ARM</c> of the <c>Au.dll</c> folder.\n\t/// - calls <c>NativeLibrary.TryLoad</c>, which works like simple <c>[DllImport]</c>, eg may use info from <c>deps.json</c>.\n\t/// - subfolder <c>64</c> etc of folder specified in environment variable <c>Au.Path</c>. For example the dll is unavailable if used in an assembly (managed dll) loaded in a nonstandard environment, eg VS forms designer or VS C# Interactive (then <c>folders.ThisApp</c> is <c>\"C:\\Program Files (x86)\\Microsoft Visual Studio\\...\"</c>). Workaround: set environment variable <c>Au.Path</c> = the main Au directory and restart Windows.\n\t/// </remarks>\n\tpublic static nint LoadAuNativeDll(string fileName) {\n\t\t//Debug.Assert(default == Api.GetModuleHandle(fileName)); //no, asserts if cpp dll is injected by acc\n\t\t\n\t\tnint h = 0;\n\t\tstring rel = (RuntimeInformation.ProcessArchitecture switch { Architecture.X86 => @\"32\\\", Architecture.Arm64 => @\"64\\ARM\\\", _ => @\"64\\\" }) + fileName;\n\t\t//rejected: use standard NuGet \"runtimes\" folder instead. I did not find info whether it can be used.\n\n\t\t//Au.dll dir + rel\n\t\tvar asm = typeof(Cpp).Assembly;\n\t\tif (asm.Location is [_, ..] s1) {\n\t\t\ts1 = s1[..(s1.LastIndexOf('\\\\') + 1)] + rel;\n\t\t\tif (NativeLibrary.TryLoad(s1, out h)) return h;\n\t\t}\n\n\t\t//like [DllImport]. It uses NATIVE_DLL_SEARCH_DIRECTORIES, which was built at startup by our AppHost or from deps.json.\n\t\t//\tAlso finds in temp dir when <PublishSingleFile>+<IncludeNativeLibrariesForSelfExtract>.\n\t\tif (NativeLibrary.TryLoad(fileName, asm, null, out h)) return h;\n\n\t\t//environment variable + rel\n\t\tif (Environment.GetEnvironmentVariable(\"Au.Path\") is string s2)\n\t\t\tif (NativeLibrary.TryLoad(pathname.combine(s2, rel), out h)) return h;\n\n\t\tthrow new DllNotFoundException(fileName + \" not found\");\n\t}\n\n\tinternal struct Cpp_Acc {\n\t\tpublic IntPtr acc;\n\t\tpublic int elem;\n\t\tpublic elm.Misc_ misc;\n\n\t\tpublic Cpp_Acc(IntPtr iacc, int elem_) { acc = iacc; elem = elem_; misc = default; }\n\t\tpublic Cpp_Acc(elm e) { acc = e._iacc; elem = e._elem; misc = e._misc; }\n\t\tpublic static implicit operator Cpp_Acc(elm e) => new(e);\n\t}\n\n\tinternal delegate int Cpp_AccFindCallbackT(Cpp_Acc a, RECT* r);\n\n\tinternal struct Cpp_AccFindParams {\n\t\tstring _role, _name, _prop;\n\t\tint _roleLength, _nameLength, _propLength;\n\t\tpublic EFFlags flags;\n\t\tpublic int skip;\n\t\tchar _resultProp; //elmFinder.RProp\n\t\tint _flags2;\n\n\t\tpublic Cpp_AccFindParams(string role, string name, string prop, EFFlags flags, int skip, char resultProp) {\n\t\t\tif (role != null) { _role = role; _roleLength = role.Length; }\n\t\t\tif (name != null) { _name = name; _nameLength = name.Length; }\n\t\t\tif (prop != null) { _prop = prop; _propLength = prop.Length; }\n\t\t\tthis.flags = flags;\n\t\t\tthis.skip = skip;\n\t\t\t_resultProp = resultProp;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Parses role. Enables Chrome acc if need.\n\t\t/// </summary>\n\t\tpublic void RolePrefix(wnd w) {\n\t\t\tif (_roleLength < 4) return;\n\t\t\tint i = _role.IndexOf(':'); if (i < 3) return;\n\t\t\t_flags2 = Cpp_AccRolePrefix(_role, i, w);\n\t\t\tif (_flags2 != 0) {\n\t\t\t\t_roleLength -= ++i;\n\t\t\t\t_role = _roleLength > 0 ? _role[i..] : null;\n\t\t\t}\n\t\t}\n\t}\n\n\t[DllImport(\"AuCpp.dll\", CallingConvention = CallingConvention.Cdecl)]\n\tstatic extern int Cpp_AccRolePrefix(string s, int len, wnd w);\n\n\t[DllImport(\"AuCpp.dll\", CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern EError Cpp_AccFind(wnd w, Cpp_Acc* aParent, Cpp_AccFindParams ap, Cpp_AccFindCallbackT also, out Cpp_Acc aResult, [MarshalAs(UnmanagedType.BStr)] out string sResult, bool getRects = false);\n\n\tinternal enum EError {\n\t\tNotFound = 0x1001, //UI element not found. With FindAll - no errors. This is actually not an error.\n\t\tInvalidParameter = 0x1002, //invalid parameter, for example wildcard expression (or regular expression in it)\n\t\tWindowClosed = 0x1003, //the specified window handle is invalid or the window was destroyed while injecting\n\t}\n\n\tinternal static bool IsCppError(int hr) {\n\t\treturn hr >= (int)EError.NotFound && hr <= (int)EError.WindowClosed;\n\t}\n\n\t/// <summary>\n\t/// flags: 1 not inproc, 2 get only name.\n\t/// </summary>\n\t[DllImport(\"AuCpp.dll\", CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern int Cpp_AccFromWindow(int flags, wnd w, EObjid objId, out Cpp_Acc aResult, out BSTR sResult);\n\n\tinternal delegate EXYFlags Cpp_AccFromPointCallbackT(EXYFlags flags, wnd wFP, wnd wTL);\n\n\t[DllImport(\"AuCpp.dll\", CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern int Cpp_AccFromPoint(POINT p, EXYFlags flags, Cpp_AccFromPointCallbackT callback, out Cpp_Acc aResult);\n\n\t[DllImport(\"AuCpp.dll\", CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern int Cpp_AccGetFocused(wnd w, EFocusedFlags flags, out Cpp_Acc aResult);\n\n\t//These are called from elm class functions like Cpp.Cpp_Func(this, ...); GC.KeepAlive(this);.\n\t//We can use 'this' because Cpp_Acc has an implicit conversion from elm operator.\n\t//Need GC.KeepAlive(this) everywhere. Else GC can collect the elm (and release _iacc) while in the Cpp func.\n\t//Alternatively could make the Cpp parameter 'const Cpp_Acc&', and pass elm directly. But I don't like it.\n\n\t[DllImport(\"AuCpp.dll\", CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern int Cpp_AccNavigate(Cpp_Acc aFrom, string navig, out Cpp_Acc aResult);\n\n\t[DllImport(\"AuCpp.dll\", CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern int Cpp_AccGetStringProp(Cpp_Acc a, char prop, out BSTR sResult);\n\n\t[DllImport(\"AuCpp.dll\", CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern int Cpp_AccWeb(Cpp_Acc a, string what, out BSTR sResult);\n\n\t[DllImport(\"AuCpp.dll\", CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern int Cpp_AccGetRect(Cpp_Acc a, out RECT r);\n\n\t[DllImport(\"AuCpp.dll\", CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern int Cpp_AccGetRole(Cpp_Acc a, out ERole roleInt, out BSTR roleStr);\n\n\t[DllImport(\"AuCpp.dll\", CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern int Cpp_AccGetInt(Cpp_Acc a, char what, out int R);\n\n\t[DllImport(\"AuCpp.dll\", CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern int Cpp_AccAction(Cpp_Acc a, char action = 'a', [MarshalAs(UnmanagedType.BStr)] string param = null);\n\n\t[DllImport(\"AuCpp.dll\", CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern int Cpp_AccSelect(Cpp_Acc a, ESelect flagsSelect);\n\n\t[DllImport(\"AuCpp.dll\", CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern int Cpp_AccGetSelection(Cpp_Acc a, out BSTR sResult);\n\n\t[DllImport(\"AuCpp.dll\", CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern int Cpp_AccGetProps(Cpp_Acc a, string props, out BSTR sResult);\n\n\t/// <param name=\"flags\">1 - wait less.</param>\n\t[DllImport(\"AuCpp.dll\", CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern void Cpp_Unload(uint flags);\n\n#if DEBUG\n\tinternal static void DebugUnload() {\n\t\tGC.Collect();\n\t\tGC.WaitForPendingFinalizers();\n\t\tCpp_Unload(0);\n\t}\n\n//\t[DllImport(\"AuCpp.dll\", CallingConvention = CallingConvention.Cdecl)]\n//\tinternal static extern IInterface Cpp_GetInterface();\n\n//\t[ComImport, Guid(\"3AB5235E-2768-47A2-909A-B5852A9D1868\"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\n//\tinternal interface IInterface {\n//\t\t[PreserveSig] int Add(int a, int b);\n//\t\t//int Prop { set; get; }\n//\t\t//[return: MarshalAs(UnmanagedType.Bool)] bool Prop { set; get; }\n//\t\t//bool Prop { [return: MarshalAs(UnmanagedType.Bool)] set; [return: MarshalAs(UnmanagedType.Bool)] get; }\n//\t\t//[property: MarshalAs(UnmanagedType.Bool)] bool Prop { set; get; }\n\n//\t\tvoid put_Prop([MarshalAs(UnmanagedType.Bool)] bool r);\n//\t\t[return: MarshalAs(UnmanagedType.Bool)] bool get_Prop();\n\n//\t\t//void put_Prop2([MarshalAs(UnmanagedType.LPStr)] string r);\n//\t\t//[return: MarshalAs(UnmanagedType.LPStr)] string get_Prop2();\n//\t\t//[return: MarshalAs(UnmanagedType.IUnknown)] object Prop2 { get; set; }\n//\t\t//object Prop2 { [return: MarshalAs(UnmanagedType.IUnknown)] get; set; }\n\n//#if false\n//\t\tvoid put_Prop2([MarshalAs(UnmanagedType.IUnknown)] object r);\n//\t\t[return: MarshalAs(UnmanagedType.IUnknown)] object get_Prop2();\n//#else\n//\t\tvoid put_Prop2(IUnknown r);\n//\t\tIUnknown get_Prop2();\n\n//\t\t[ComImport, Guid(\"00000000-0000-0000-c000-000000000046\"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\n//\t\tinternal interface IUnknown { }\n//#endif\n//\t}\n#endif\n\n\t[DllImport(\"AuCpp.dll\", CallingConvention = CallingConvention.Cdecl)]\n\tstatic extern void Cpp_SetHelperCallback(delegate* unmanaged<int, wnd, int> callback);\n\n\t[UnmanagedCallersOnly]\n\tstatic int _HelperCallback(int action, wnd w) {\n\t\tif (action == 1) { //AccEnableChrome asks to detect whether the command line contains --force-renderer-accessibility. If yes, will not try to enable acc.\n\t\t\tif (process.getCommandLine(w.ProcessId, removeProgram: true) is string s) {\n\t\t\t\tif (s.Contains(\"--force-renderer-accessibility\")) return 1;\n\t\t\t\t//print.warning(\"To use UI elements in web pages, start browser with command line --force-renderer-accessibility. Without it the code may fail sometimes or stop working in the future.\");\n\t\t\t}\n\t\t} else if (action == 2) { //AccEnableChrome failed\n\t\t\tprint.warning(\"To use UI elements in web pages, start browser with command line --force-renderer-accessibility.\");\n\t\t}\n\t\treturn 0;\n\t}\n\n\t// OTHER\n\n\t[DllImport(\"AuCpp.dll\", CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern IntPtr Cpp_ModuleHandle();\n\n\t[DllImport(\"AuCpp.dll\", CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern char* Cpp_LowercaseTable();\n\n\t[DllImport(\"AuCpp.dll\", CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern IntPtr Cpp_Clipboard(IntPtr hh);\n\n\t[DllImport(\"AuCpp.dll\", CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern bool Cpp_ShellExec(in Api.SHELLEXECUTEINFO x, out int pid, out int injectError, out int execError);\n\n\t[DllImport(\"AuCpp.dll\", CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern nint Cpp_AccWorkaround(Api.IAccessible a, nint wParam, ref nint obj);\n\n\t[DllImport(\"AuCpp.dll\", CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern void Cpp_UEF(bool on);\n\n\t[DllImport(\"AuCpp.dll\", CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern void Cpp_InactiveWindowWorkaround(bool on);\n\n\t/// <returns>0 failed, 1 x86, 2 x64, 3 arm64</returns>\n\t[DllImport(\"AuCpp.dll\", CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern int Cpp_GetProcessArchitecture(int pid);\n\n\t// TEST\n\n#if DEBUG\n\n\t[DllImport(\"AuCpp.dll\", CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern void Cpp_Test();\n\n\t//[DllImport(\"AuCpp.dll\", CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern void Cpp_TestWildex(string s, string w);\n\n\t//[DllImport(\"AuCpp.dll\", CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern int Cpp_TestInt(int a);\n\n\t//[DllImport(\"AuCpp.dll\", CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern int Cpp_TestString(string a, int b, int c);\n\n\t//[ComImport, Guid(\"3426CF3C-F7C2-4322-A292-463DB8729B54\"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\n\t//internal interface ICppTest\n\t//{\n\t//\t[PreserveSig] int TestInt(int a, int b, int c);\n\t//\t[PreserveSig] int TestString([MarshalAs(UnmanagedType.LPWStr)] string a, int b, int c);\n\t//\t[PreserveSig] int TestBSTR(string a, int b, int c);\n\t//}\n\n\t//[DllImport(\"AuCpp.dll\", CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern ICppTest Cpp_Interface();\n\n\n\t//[ComImport, Guid(\"57017F56-E7CA-4A7B-A8F8-2AE36077F50D\"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\n\t//internal interface IThreadExitEvent\n\t//{\n\t//\t[PreserveSig] int Unsubscribe();\n\t//}\n\n\t//[DllImport(\"AuCpp.dll\", CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern IThreadExitEvent Cpp_ThreadExitEvent(IntPtr callback);\n\n\t//[DllImport(\"AuCpp.dll\", CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern void Cpp_ThreadExitEvent2(IntPtr callback);\n#endif\n}\n"
  },
  {
    "path": "Au/Api/WinRT.cs",
    "content": "//WinRT common API and utils. Others are in files where used.\n\n//This library does not use C#/WinRT (Microsoft.Windows.SDK.NET.dll and WinRT.Runtime.dll).\n//Would be easier, but it has problems:\n//\t- Adds 2 files, 22 MB.\n//\t- OCR: if once used in a STA thread, then does not work in MTA threads.\n//\t- OCR: slower.\n\nnamespace Au.Types;\n\n#pragma warning disable 649, 169 //field never assigned/used\nstatic unsafe partial class WinRT {\n\t[DllImport(\"combase.dll\", PreserveSig = true)]\n\tinternal static extern int WindowsCreateString(string s, int length, out IntPtr hstring);\n\t\n\t[DllImport(\"combase.dll\", PreserveSig = true)]\n\tinternal static extern int WindowsDeleteString(IntPtr hstring);\n\t\n\t[DllImport(\"combase.dll\")]\n\tinternal static extern char* WindowsGetStringRawBuffer(IntPtr hstring, out int length);\n\t\n\t//probably don't need. Always returns 1 (already initialized) when called with current apartment state.\n\t//[DllImport(\"combase.dll\", PreserveSig = true)]\n\t//internal static extern int RoInitialize(int initType);\n\t\n\t[DllImport(\"combase.dll\", PreserveSig = true)]\n\tinternal static extern int RoGetActivationFactory(IntPtr activatableClassId, in Guid iid, out IntPtr factory);\n\t\n\tinternal static T Create<T>(string progId) where T : struct {\n\t\tusing var hs = new _Hstring(progId);\n\t\tHR(RoGetActivationFactory(hs, typeof(T).GUID, out var r));\n\t\treturn Unsafe.As<IntPtr, T>(ref r);\n\t}\n\t//rejected: use static factories. Now fast.\n\t\n\tinternal static void HR(int hr) {\n\t\tif (hr < 0) throw Marshal.GetExceptionForHR(hr);\n\t}\n\t\n\tinternal static T As<T>(IntPtr ip) where T : unmanaged {\n\t\tDebug.Assert(sizeof(T) == sizeof(IntPtr));\n\t\treturn Unsafe.As<IntPtr, T>(ref ip);\n\t}\n\t\n\t/// <summary>\n\t/// A COM interface pointer.\n\t/// </summary>\n\tinternal struct IUnknown : IDisposable {\n\t\tIntPtr _u;\n\t\t\n\t\tIUnknown(IntPtr iunknown) { _u = iunknown; }\n\t\t\n\t\tpublic void Dispose() {\n\t\t\tif (_u != default) { Marshal.Release(_u); _u = default; }\n\t\t}\n\n\t\tpublic bool IsNull => _u == default;\n\t\t\n\t\tint _QI(Type type, out IntPtr r) {\n#if NET9_0_OR_GREATER //changed ref -> in\n\t\t\treturn Marshal.QueryInterface(_u, type.GUID, out r);\n#else\n\t\t\tvar guid = type.GUID;\n\t\t\treturn Marshal.QueryInterface(_u, ref guid, out r);\n#endif\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Calls <c>QueryInterface</c>. Throws exception if failed.\n\t\t/// </summary>\n\t\tpublic T QI<T>() where T : unmanaged {\n\t\t\tHR(_QI(typeof(T), out var r));\n\t\t\treturn As<T>(r);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Calls <c>QueryInterface</c>. Returns <c>false</c> if failed.\n\t\t/// </summary>\n\t\tpublic bool QI<T>(out T r) where T : unmanaged {\n\t\t\tbool ok = 0 == _QI(typeof(T), out var v);\n\t\t\tr = ok ? As<T>(v) : default;\n\t\t\treturn ok;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets COM interface function at index <i>i</i> in vtbl.\n\t\t/// </summary>\n\t\tpublic nint this[int i] => (*(nint**)_u)[i];\n\t\t\n\t\tpublic static implicit operator IUnknown(IntPtr p) => new(p);\n\t\t\n\t\tpublic static implicit operator IntPtr(IUnknown p) => p._u;\n\t\t\n\t\tpublic override string ToString() => _u.ToString();\n\t\t\n\t\t/// <summary>\n\t\t/// Calls a 0-param function that returns <c>HSTRING</c>.\n\t\t/// </summary>\n\t\t/// <param name=\"i\">Interface function index in vtbl.</param>\n\t\tpublic string GetString(int i) {\n\t\t\tusing var s1 = new _Hstring(_GetPtr(i));\n\t\t\treturn s1.ToString();\n\t\t}\n\t\t\n\t\tIntPtr _GetPtr(int i) {\n\t\t\tHR(((delegate* unmanaged[Stdcall]<IntPtr, out IntPtr, int>)this[i])(_u, out var r));\n\t\t\treturn r;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Calls a 0-param function with a pointer-size return type (COM pointer, etc).\n\t\t/// </summary>\n\t\t/// <param name=\"i\">Interface function index in vtbl.</param>\n\t\tpublic T GetPtr<T>(int i) where T : unmanaged => As<T>(_GetPtr(i));\n\t\t\n\t\t//when calling: System.BadImageFormatException: Bad element type in SizeOf.\n\t\t//public IntPtr GetPtr<T1>(int i, T1 p1) where T1 : unmanaged {\n\t\t//\tHR(((delegate* unmanaged[Stdcall]<IntPtr, T1, out IntPtr, int>)this[i])(_u, p1, out var r));\n\t\t//\treturn r;\n\t\t//}\n\t\t\n\t\t/// <summary>\n\t\t/// <c>QI(IClosable).Close()</c>\n\t\t/// </summary>\n\t\tpublic void Close() {\n\t\t\tusing var c = QI<IClosable>();\n\t\t\tc.Close();\n\t\t}\n\t}\n\t\n\tinternal interface IComPtr : IDisposable {  }\n\t\n\tinternal struct IVectorView<T> : IComPtr where T : unmanaged {\n\t\tIUnknown _u; public IUnknown U => _u;\n\t\tpublic void Dispose() => _u.Dispose();\n\t\t\n\t\tIntPtr _GetAt(int i) {\n\t\t\tHR(((delegate* unmanaged[Stdcall]<IntPtr, int, out IntPtr, int>)_u[6])(_u, i, out var r));\n\t\t\treturn r;\n\t\t}\n\t\t\n\t\tpublic T this[int i] => As<T>(_GetAt(i));\n\t\t\n\t\tpublic int Size {\n\t\t\tget {\n\t\t\t\tHR(((delegate* unmanaged[Stdcall]<IntPtr, out int, int>)_u[7])(_u, out int r));\n\t\t\t\treturn r;\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic IEnumerable<T> Items(bool disposeItems = true) {\n\t\t\tfor (int i = 0, n = Size; i < n; i++) {\n\t\t\t\tvar v = _GetAt(i);\n\t\t\t\tyield return As<T>(v);\n\t\t\t\tif (disposeItems) Marshal.Release(v);\n\t\t\t}\n\t\t}\n\t}\n\t\n#if true\n\tinternal struct IAsyncOperation : IComPtr {\n\t\tIUnknown _u; public IUnknown U => _u;\n\t\tpublic void Dispose() => _u.Dispose();\n\t\t\n\t\tpublic T Await<T>(bool dispose = true) where T : unmanaged {\n\t\t\ttry {\n\t\t\t\tusing (var ai = _u.QI<IAsyncInfo>()) {\n\t\t\t\t\tfor (int i = 4; ; i++) {\n\t\t\t\t\t\tvar status = ai.Status;\n\t\t\t\t\t\t//print.it(status, i / 4);\n\t\t\t\t\t\tif (status == AsyncStatus.Completed) break;\n\t\t\t\t\t\tif (status != AsyncStatus.Started) throw new AuException();\n\t\t\t\t\t\twait.ms(i / 4);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\treturn _u.GetPtr<T>(8); //GetResults\n\t\t\t}\n\t\t\tfinally { if (dispose) Dispose(); }\n\t\t}\n\t\t\n\t\t[Guid(\"00000036-0000-0000-C000-000000000046\")]\n\t\tstruct IAsyncInfo : IComPtr {\n\t\tIUnknown _u; public IUnknown U => _u;\n\t\tpublic void Dispose() => _u.Dispose();\n\t\t\t\n\t\t\tpublic AsyncStatus Status {\n\t\t\t\tget {\n\t\t\t\t\tHR(((delegate* unmanaged[Stdcall]<IntPtr, out AsyncStatus, int>)_u[7])(_u, out var r));\n\t\t\t\t\treturn r;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tenum AsyncStatus { Canceled = 2, Completed = 1, Error = 3, Started = 0 }\n\t}\n#else //tried to use Completed callback, unsuccessfully\n\t\tinternal struct IAsyncOperation : IComPtr {\n\t\t\tIUnknown _u; public IUnknown U => _u;\n\t\t\tpublic void Dispose() => _u.Dispose();\n\t\t\t\n\t\t\tpublic T Await<T>(bool dispose = true) where T : unmanaged {\n\t\t\t\ttry {\n\t\t\t\t\t//static void _Handler(IAsyncOperation ai, AsyncStatus status) { print.it(\"completed\"); }\n\t\t\t\t\t//delegate*<IntPtr, int, void> p1 = &_Handler;\n\t\t\t\t\t//HR(((delegate* unmanaged[Stdcall]<IntPtr, void*, int>)_u[6])(_u, p1)); //put_Completed\n\t\t\t\t\t\n\t\t\t\t\tvar del = new AsyncOperationCompletedHandler(static (IAsyncOperation ai, AsyncStatus status) => { print.it(\"completed\"); });\n\t\t\t\t\tHR(((delegate* unmanaged[Stdcall]<IntPtr, AsyncOperationCompletedHandler, int>)_u[6])(_u, del)); //put_Completed\n\t\t\t\t\t\n\t\t\t\t\tdialog.show(\"\");\n\t\t\t\t\tGC.KeepAlive(del);\n\t\t\t\t\t\n\t\t\t\t\treturn _u.GetPtr<T>(8); //GetResults\n\t\t\t\t}\n\t\t\t\tfinally { if (dispose) Dispose(); }\n\t\t\t}\n\t\t\t\n\t\t\tenum AsyncStatus { Canceled = 2, Completed = 1, Error = 3, Started = 0 }\n\t\t\t\n\t\t\tdelegate void AsyncOperationCompletedHandler(IAsyncOperation ao, AsyncStatus status);\n\t\t}\n#endif\n\t\n\t[Guid(\"30d5a829-7fa4-4026-83bb-d75bae4ea99e\")]\n\tinternal struct IClosable : IComPtr {\n\t\tIUnknown _u; public IUnknown U => _u;\n\t\tpublic void Dispose() => _u.Dispose();\n\t\t\n\t\tpublic void Close() {\n\t\t\tHR(((delegate* unmanaged[Stdcall]<IntPtr, int>)_u[6])(_u));\n\t\t}\n\t}\n\t\n\tunsafe class _Hstring : IDisposable {\n\t\tIntPtr _h;\n\t\t\n\t\tpublic _Hstring(IntPtr hstring) {\n\t\t\t_h = hstring;\n\t\t}\n\t\t\n\t\tpublic _Hstring(string s) {\n\t\t\tWindowsCreateString(s, s.Lenn(), out _h);\n\t\t}\n\t\t\n\t\tpublic void Dispose() {\n\t\t\tif (_h != default) {\n\t\t\t\tWindowsDeleteString(_h);\n\t\t\t\t_h = default;\n\t\t\t}\n\t\t\tGC.SuppressFinalize(this);\n\t\t}\n\t\t\n\t\t~_Hstring() {\n\t\t\tif (_h != default) WindowsDeleteString(_h);\n\t\t}\n\t\t\n\t\t//public static implicit operator _Hstring(string s) => new(s);\n\t\tpublic static implicit operator IntPtr(_Hstring s) => s._h;\n\t\tpublic static implicit operator string(_Hstring s) => s.ToString();\n\t\t\n\t\tpublic override string ToString() {\n\t\t\tif (_h == default) return null;\n\t\t\tchar* p = WindowsGetStringRawBuffer(_h, out int len);\n\t\t\treturn new(p, 0, len);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au/Au.More/AppSingleInstance.cs",
    "content": "namespace Au.More;\n\n/// <summary>\n/// Implements \"single instance process\" feature.\n/// </summary>\n/// <example>\n/// <code><![CDATA[\n/// /*/ role exeProgram; ifRunning run; /*/\n/// if (script.testing) args = [\"test\", \"args\"];\n/// \n/// if (AppSingleInstance.AlreadyRunning(\"unique-mutex-name\", args)) {\n/// \tprint.it(\"already running\");\n/// \treturn;\n/// }\n/// \n/// var b = new wpfBuilder(\"Window\").WinSize(400);\n/// b.R.AddOkCancel();\n/// b.End();\n/// AppSingleInstance.Notified += a => {\n/// \tprint.it(\"AppSingleInstance.Notified\", a);\n/// \tb.Window.Activate();\n/// };\n/// if (!b.ShowDialog()) return;\n/// ]]></code>\n/// </example>\n/// <summary>\n/// Implements \"single instance process\" feature.\n/// </summary>\n/// <seealso cref=\"script.single\"/>\npublic static class AppSingleInstance {\n\tstatic Mutex _mutex;\n\tstatic wnd _wNotify;\n\t\n\t/// <summary>\n\t/// Detects whether a process of this app is already running.\n\t/// </summary>\n\t/// <param name=\"mutex\">A unique string to use for mutex name (see <see cref=\"Mutex(bool, string, out bool)\"/>). If prefix <c>@\"Global\\\"</c> used, detects processes in all user sessions.</param>\n\t/// <param name=\"notifyArgs\">\n\t/// If not <c>null</c>:\n\t/// <br/>• If already running, sends it to that process, which receives it in <see cref=\"Notified\"/> event.\n\t/// <br/>• Else enables <see cref=\"Notified\"/> event in this process.\n\t/// </param>\n\t/// <param name=\"waitMS\">Milliseconds to wait until this process can run. No timeout if -1.</param>\n\t/// <returns>True if already running.</returns>\n\t/// <exception cref=\"InvalidOperationException\">This function already called.</exception>\n\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"Mutex(bool, string, out bool)\"/>.</exception>\n\tpublic static bool AlreadyRunning(string mutex, IEnumerable<string> notifyArgs = null, int waitMS = 0) {\n\t\tvar m = new Mutex(true, mutex, out bool createdNew);\n\t\tif (null != Interlocked.CompareExchange(ref _mutex, m, null)) { m.Dispose(); throw new InvalidOperationException(); }\n\t\t\n\t\tif (!createdNew && waitMS != 0) {\n\t\t\ttry { createdNew = m.WaitOne(waitMS); }\n\t\t\tcatch (AbandonedMutexException) { createdNew = true; }\n\t\t}\n\t\t\n\t\tif (notifyArgs != null) {\n\t\t\tif (createdNew) {\n\t\t\t\tWndUtil.RegisterWindowClass(mutex, _WndProc);\n\t\t\t\t_wNotify = WndUtil.CreateMessageOnlyWindow(mutex, \"AppSingleInstance\");\n\t\t\t\tWndCopyData.EnableReceivingWM_COPYDATA();\n\t\t\t} else {\n\t\t\t\tvar w = wnd.findFast(\"AppSingleInstance\", mutex, messageOnly: true);\n\t\t\t\tif (!w.Is0) WndCopyData.Send<char>(w, 1, string.Join('\\0', notifyArgs));\n\t\t\t}\n\t\t}\n\t\t\n\t\treturn !createdNew;\n\t}\n\t\n\tstatic nint _WndProc(wnd w, int msg, nint wp, nint lp) {\n\t\tif (msg == Api.WM_COPYDATA) {\n\t\t\tvar x = new WndCopyData(lp);\n\t\t\tif (x.DataId == 1) {\n\t\t\t\tNotified?.Invoke(x.GetString().Split('\\0'));\n\t\t\t}\n\t\t}\n\t\treturn Api.DefWindowProc(w, msg, wp, lp);\n\t}\n\t\n\t/// <summary>\n\t/// When <see cref=\"AlreadyRunning\"/> in new process detected that this process is running.\n\t/// Receives <i>notifyArgs</i> passed to it.\n\t/// </summary>\n\t/// <remarks>\n\t/// To enable this event, call <see cref=\"AlreadyRunning\"/> with non-<c>null</c> <i>notifyArgs</i>. The event handler runs in the same thread. The thread must dispatch Windows messages (for example show a window or dialog, or call <see cref=\"wait.doEvents(int)\"/>).\n\t/// </remarks>\n\tpublic static event Action<string[]> Notified;\n}\n"
  },
  {
    "path": "Au/Au.More/BufferedPaint.cs",
    "content": "namespace Au.More;\n\n/// <summary>\n/// Wraps buffered paint API <ms>BeginBufferedPaint</ms> etc.\n/// Must be disposed locally, like in the example.\n/// </summary>\n/// <example>\n/// <code><![CDATA[\n/// case WM_PAINT:\n/// \tusing (BufferedPaint bp = new(w, true)) { ... }\n/// \treturn default;\n/// ]]></code>\n/// </example>\npublic struct BufferedPaint : IDisposable {\n\t//rejected. Users often will forget it. Better init automatically.\n\t///// <summary>\n\t///// Calls API <ms>BufferedPaintInit</ms>.\n\t///// </summary>\n\t//public static void Init() { Api.BufferedPaintInit(); } //fast\n\t\n\t///// <summary>\n\t///// Calls API <ms>BufferedPaintUnInit</ms>.\n\t///// </summary>\n\t//public static void Uninit() { Api.BufferedPaintUnInit(); }\n\t\n\t[ThreadStatic] static bool s_inited;\n\t//never mind: should BufferedPaintUnInit before thread exits.\n\t//\tNot very important. Usually a process has single UI thread. Tested: 10000 threads without BufferedPaintUnInit don't leak much.\n\t//\tTo detect thread exit could use eg FlsAlloc(callback)+FlsSetValue, or unmanaged dll thread detach.\n\t//\t\tBut it can be dangerous (too late, eg C# thread variables are already cleared).\n\t\n\twnd _w;\n\tIntPtr _dcn, _dcb;\n\tbool _wmPaint;\n\tApi.PAINTSTRUCT _ps;\n\tIntPtr _hb;\n\tRECT _r;\n\t\n\t/// <summary>\n\t/// Gets non-buffered DC with API <ms>BeginPaint</ms> or <ms>GetDC</ms>. Then gets buffered DC with API <ms>BeginBufferedPaint</ms> for entire client area or rectangle <i>r</i>.\n\t/// </summary>\n\t/// <param name=\"w\"></param>\n\t/// <param name=\"wmPaint\">Use API <c>BeginPaint</c>/<c>EndPaint</c>. If <c>false</c>, uses <c>GetDC</c>/<c>ReleaseDC</c>.</param>\n\t/// <param name=\"r\">Part of client area.</param>\n\tpublic unsafe BufferedPaint(wnd w, bool wmPaint, RECT? r = null) {\n\t\tif (!s_inited) s_inited = 0 == Api.BufferedPaintInit();\n\t\t\n\t\t_w = w;\n\t\tif (_wmPaint = wmPaint) {\n\t\t\t_dcn = Api.BeginPaint(w, out _ps);\n\t\t} else {\n\t\t\t_dcn = Api.GetDC(_w);\n\t\t}\n\t\t\n\t\t_r = r ?? _w.ClientRect;\n\t\tApi.BP_PAINTPARAMS pp = new() { cbSize = sizeof(Api.BP_PAINTPARAMS) };\n\t\t//var ru = wmPaint ? _ps.rcPaint : _r; //the buffer bitmap is smaller when rcPaint smaller, but in most cases don't need to change painting code, although GetViewportOrgEx etc get 0 offsets of the buffer DC. However problem with brush alignment.\n\t\t_hb = Api.BeginBufferedPaint(_dcn, _r, Api.BP_BUFFERFORMAT.BPBF_TOPDOWNDIB, ref pp, out _dcb); //BPBF_COMPATIBLEBITMAP slower //tested: works with 16 and 8 bit colors too\n\t\tDebug_.PrintIf(_hb == default && !_r.NoArea, $\"BeginBufferedPaint, {_r}\");\n\t\tif (_hb == default) _dcb = _dcn;\n\t}\n\t\n\t/// <summary>\n\t/// Calls API <ms>EndBufferedPaint</ms> and <ms>EndPaint</ms> or <ms>ReleaseDC</ms>.\n\t/// </summary>\n\tpublic void Dispose() {\n\t\tif (_dcn == default) return;\n\t\tif (_hb != default) Api.EndBufferedPaint(_hb, true);\n\t\tif (_wmPaint) Api.EndPaint(_w, _ps); else Api.ReleaseDC(_w, _dcn);\n\t\t_dcn = default;\n\t}\n\t\n\t/// <summary>\n\t/// Gets window DC.\n\t/// </summary>\n\tpublic IntPtr NonBufferedDC => _dcn;\n\t\n\t/// <summary>\n\t/// Gets the buffered DC. Returns <see cref=\"NonBufferedDC\"/> if API <ms>BeginBufferedPaint</ms> failed.\n\t/// </summary>\n\tpublic IntPtr DC => _dcb;\n\t\n\t/// <summary>\n\t/// Gets client area rectangle or rectangle passed to constructor.\n\t/// </summary>\n\tpublic RECT Rect => _r;\n\t\n\t/// <summary>\n\t/// Gets bounding rectangle of the update region in client area rectangle.\n\t/// </summary>\n\tpublic RECT UpdateRect {\n\t\tget {\n\t\t\tif (_wmPaint) return _ps.rcPaint;\n\t\t\tApi.GetUpdateRect(_w, out var r, false);\n\t\t\treturn r;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au/Au.More/CheckListDialog.cs",
    "content": "using System.Windows;\nusing System.Windows.Controls;\nusing System.Collections;\n\nnamespace Au.More;\n\n/// <summary>\n/// Dialog with list of check boxes.\n/// </summary>\n/// <seealso cref=\"EnumUI{TEnum}\"/>\n/// <example>\n/// <code><![CDATA[\n/// string[] a = [\"one\", \"two\"];\n/// var d = new CheckListDialog(\"Info.\");\n/// d.Add(a);\n/// d.Add(\"three\", true);\n/// d.Add(\"four\");\n/// if (!d.ShowDialog()) return;\n/// print.it(d.ResultBits, d.ResultIndices, d.ResultItems);\n/// ]]></code>\n/// </example>\npublic class CheckListDialog {\n\twpfBuilder _b;\n\tTextBlock _text;\n\tListBox _lb;\n\tList<CheckBox> _ac = new();\n\t\n\t/// <summary>\n\t/// Creates <see cref=\"Builder\"/>, sets some window properties, optionally sets static text.\n\t/// </summary>\n\t/// <param name=\"text\">Text above the list. Can be <c>null</c>. See also <see cref=\"FormatText\"/>.</param>\n\t/// <param name=\"title\">Window name. If <c>null</c>, uses <see cref=\"dialog.options.defaultTitle\"/>.</param>\n\tpublic CheckListDialog(string text = null, string title = null) {\n\t\t_b = new wpfBuilder(title ?? dialog.options.defaultTitle)\n\t\t\t.WinProperties(resizeMode: ResizeMode.NoResize)\n\t\t\t.Width(250..600);\n\t\t_b.R.Add(out _text, text).Wrap(TextWrapping.Wrap);\n\t\tif (text == null) _text.Visibility = Visibility.Collapsed;\n\t}\n\t\n\t/// <summary>\n\t/// Adds a checkbox.\n\t/// </summary>\n\t/// <returns>The checkbox.</returns>\n\tpublic CheckBox Add(string text, bool check = false, string tooltip = null) {\n\t\tif (_lb == null) _b.R.Add(out _lb).Height(..550);\n\t\t\n\t\t//var content = UseAccessKey ? text : text?.Replace(\"_\", \"__\");\n\t\tvar content = text?.Replace(\"_\", \"__\");\n\t\tCheckBox c = new() { Content = content, Tag = text, IsChecked = check, ToolTip = tooltip };\n\t\t_ac.Add(c);\n\t\t_lb.Items.Add(c);\n\t\treturn c;\n\t}\n\t\n\t/// <summary>\n\t/// Adds multiple checkboxes.\n\t/// </summary>\n\t/// <param name=\"items\">Array, <c>List</c>, etc containing text strings for checkboxes.</param>\n\t/// <param name=\"check\">Whether to check all checkboxes.</param>\n\tpublic void Add(IEnumerable<string> items, bool check = false) {\n\t\tforeach (var v in items) Add(v, check);\n\t}\n\t\n\t///// <summary>\n\t///// Character <c>'_'</c> in checkbox text isn't displayed. Instead the next character toggles the checkbox when pressed with <c>Alt</c>. For literal <c>\"_\"</c> use <c>\"__\"</c>.\n\t///// </summary>\n\t//public bool UseAccessKey { get; set; }\n\t\n\t/// <summary>\n\t/// Sets formatted text, like <see cref=\"wpfBuilder.FormatText(wpfBuilder.InterpolatedString)\"/>.\n\t/// </summary>\n\t/// <inheritdoc cref=\"wpfBuilder.formatTextOf\" path=\"//param|//exception\"/>\n\tpublic void FormatText(wpfBuilder.InterpolatedString text) {\n\t\twpfBuilder.formatTextOf(_text, text);\n\t\t_text.Visibility = Visibility.Visible;\n\t}\n\t\n\t/// <summary>\n\t/// Changes text of <b>OK</b> and <b>Cancel</b> buttons.\n\t/// </summary>\n\tpublic void SetButtons(string ok = \"OK\", string cancel = \"Cancel\") {\n\t\t_ok = ok;\n\t\t_cancel = cancel;\n\t}\n\tstring _ok, _cancel;\n\t\n\t/// <summary>\n\t/// Gets the <see cref=\"wpfBuilder\"/> that builds the dialog.\n\t/// You can use it to add more controls, change window properties, etc; see example.\n\t/// </summary>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// using System.Windows.Controls;\n\t/// string[] a = [\"one\", \"two\"];\n\t/// var d = new CheckListDialog(\"Info.\");\n\t/// d.Add(a);\n\t/// d.Builder.R.Add(\"Input\", out TextBox tInput);\n\t/// if (!d.ShowDialog()) return;\n\t/// print.it(d.ResultBits, d.ResultIndices, d.ResultItems, tInput.Text);\n\t/// ]]></code>\n\t/// </example>\n\tpublic wpfBuilder Builder => _b;\n\t\n\t/// <summary>\n\t/// Adds <b>OK</b>/<b>Cancel</b> buttons, shows dialog, sets result properties.\n\t/// </summary>\n\t/// <param name=\"owner\">\n\t/// Owner window, or an element in it.\n\t/// If used, sets <see cref=\"Window.ShowInTaskbar\"/> = <c>false</c>. Else sets <see cref=\"Window.Topmost\"/> = <c>true</c>, unless <see cref=\"dialog.options.topmostIfNoOwnerWindow\"/> is <c>false</c> or the active window belongs to this process.\n\t/// </param>\n\t/// <returns><c>true</c> if pressed <b>OK</b>.</returns>\n\tpublic bool ShowDialog(DependencyObject owner = null) {\n\t\t_b.R.AddOkCancel(_ok ?? \"OK\", _cancel ?? \"Cancel\");\n\t\t_b.End();\n\t\t\n\t\tvar w = _b.Window;\n\t\tif (owner != null) w.ShowInTaskbar = false;\n\t\telse if (dialog.options.topmostIfNoOwnerWindow && !wnd.active.IsOfThisProcess) w.Topmost = true;\n\t\t\n\t\tif (!_b.ShowDialog(owner)) return false;\n\t\t\n\t\tBitArray ba = new(_ac.Count);\n\t\tint n = 0;\n\t\tfor (int i = 0; i < ba.Length; i++) if (ba[i] = _ac[i].IsChecked == true) n++;\n\t\tResultIndices = new int[n];\n\t\tfor (int i = 0, j = 0; i < ba.Length; i++) if (ba[i]) ResultIndices[j++] = i;\n\t\tResultItems = new string[n];\n\t\tfor (int i = 0, j = 0; i < ba.Length; i++) if (ba[i]) ResultItems[j++] = _ac[i].Tag as string;\n\t\tResultBits = ba;\n\t\t\n\t\treturn true;\n\t}\n\t\n\t/// <summary>\n\t/// Gets a bit array where elements represent checkbox states (<c>true</c> if checked).\n\t/// This property is set by <see cref=\"ShowDialog\"/>.\n\t/// </summary>\n\tpublic BitArray ResultBits { get; private set; }\n\t\n\t/// <summary>\n\t/// Gets 0-based indices of checked items.\n\t/// This property is set by <see cref=\"ShowDialog\"/>.\n\t/// </summary>\n\tpublic int[] ResultIndices { get; private set; }\n\t\n\t/// <summary>\n\t/// Gets strings of checked items.\n\t/// This property is set by <see cref=\"ShowDialog\"/>.\n\t/// </summary>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// List<string> a = [\"one\", \"two\"];\n\t/// var d = new CheckListDialog(\"Info.\");\n\t/// d.Add(a);\n\t/// if (!d.ShowDialog() || !d.ResultItems.Any()) return;\n\t/// a = d.ResultItems.ToList();\n\t/// ]]></code>\n\t/// </example>\n\tpublic string[] ResultItems { get; private set; }\n}\n"
  },
  {
    "path": "Au/Au.More/ComUtil.cs",
    "content": "namespace Au.More;\n\n/// <summary>\n/// COM utility functions.\n/// </summary>\npublic static class ComUtil {\n\t/// <summary>\n\t/// Gets a COM object existing in other process and registered in ROT.\n\t/// </summary>\n\t/// <param name=\"progID\">ProgID of the COM class. Example: <c>\"Excel.Application\"</c>.</param>\n\t/// <param name=\"dontThrow\">If fails, don't throw exception but return <c>null</c>.</param>\n\t/// <exception cref=\"COMException\">\n\t/// <br/>• <i>progID</i> not found in the registry. Probably it is incorrect, or the program isn't installed.\n\t/// <br/>• An object of this type currently is unavailable. Probably the program is not running, or running with a different UAC integrity level.\n\t/// </exception>\n\t/// <remarks>\n\t/// Calls API <ms>GetActiveObject</ms>.\n\t///\n\t/// This process must have the same [](xref:uac) integrity level as the target process (of the COM object). In script <b>Properties</b> select <b>uac user</b>.\n\t/// </remarks>\n\t/// <seealso cref=\"Marshal.BindToMoniker(string)\"/>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// /*/ uac user; com Outlook 9.6 #ed6988d3.dll; /*/\n\t/// using Outlook = Microsoft.Office.Interop.Outlook;\n\t/// var app = (Outlook.Application)ComUtil.GetActiveObject(\"Outlook.Application\");\n\t/// print.it(app.ActiveExplorer().CurrentFolder.Name);\n\t/// ]]></code>\n\t/// </example>\n\tpublic static object GetActiveObject(string progID, bool dontThrow = false) {\n\t\tint hr = Api.CLSIDFromProgID(progID, out var clsid);\n\t\tif (hr < 0) return dontThrow ? null : throw Marshal.GetExceptionForHR(hr);\n\t\treturn GetActiveObject(clsid, dontThrow);\n\t}\n\n\t/// <exception cref=\"COMException\">An object of this type currently is unavailable. Probably its program is not running, or running with a different UAC integrity level.</exception>\n\tpublic static object GetActiveObject(in Guid clsid, bool dontThrow = false) {\n\t\tint hr = Api.GetActiveObject(clsid, default, out var r);\n\t\tif (hr < 0) return dontThrow ? null : throw Marshal.GetExceptionForHR(hr);\n\t\treturn r;\n\t}\n\n\t/// <summary>\n\t/// Gets COM object from a window using API <ms>AccessibleObjectFromWindow</ms><c>(OBJID_NATIVEOM, IID_IDispatch</c>).\n\t/// </summary>\n\t/// <param name=\"w\">Window or control.</param>\n\t/// <param name=\"cnChild\">Child window class name. Format: [wildcard expression](xref:wildcard_expression). If used, gets COM object from the first found child or descendant window where it succeeds. If <c>null</c>, gets COM object from <i>w</i>.</param>\n\t/// <param name=\"dontThrow\">If fails to get COM object, don't throw exception but return <c>null</c>.</param>\n\t/// <exception cref=\"AuWndException\"><i>w</i> 0 or invalid.</exception>\n\t/// <exception cref=\"AuException\">Failed.</exception>\n\t/// <example>\n\t/// Microsoft Excel.\n\t/// <code><![CDATA[\n\t/// /*/ com Excel 1.9 #9fdf46bf.dll; /*/\n\t/// using Excel = Microsoft.Office.Interop.Excel;\n\t/// var w = wnd.find(0, null, \"XLMAIN\", \"EXCEL.EXE\");\n\t/// Excel.Workbook book = ((Excel.Window)ComUtil.GetWindowObject(w, \"EXCEL7\")).Parent;\n\t/// print.it(book.Name);\n\t/// ]]></code>\n\t/// Microsoft Word.\n\t/// <code><![CDATA[\n\t/// /*/ com Word 8.7 #6a6b0205.dll; /*/\n\t/// using Word = Microsoft.Office.Interop.Word;\n\t/// var w = wnd.find(0, null, \"OpusApp\", \"WINWORD.EXE\");\n\t/// Word.Document doc = ((Word.Window)ComUtil.GetWindowObject(w, \"_WwG\")).Parent;\n\t/// print.it(doc.Name);\n\t/// ]]></code>\n\t/// Microsoft PowerPoint.\n\t/// <code><![CDATA[\n\t/// /*/ com PowerPoint 2.12 #fdf81915.dll; /*/\n\t/// using PowerPoint = Microsoft.Office.Interop.PowerPoint;\n\t/// var w = wnd.find(0, \"*PowerPoint\", null, \"POWERPNT.EXE\");\n\t/// PowerPoint.Presentation doc = ((PowerPoint.DocumentWindow)ComUtil.GetWindowObject(w, \"**m mdiClass||paneClassDC\")).Parent;\n\t/// print.it(doc.Name);\n\t/// ]]></code>\n\t/// Microsoft Access.\n\t/// <code><![CDATA[\n\t/// /*/ com Access 9.0 #cdda93ea.dll; /*/\n\t/// using Access = Microsoft.Office.Interop.Access;\n\t/// var w = wnd.find(0, null, \"OMain\", \"MSACCESS.EXE\");\n\t/// Access.Application app = (Access.Application)ComUtil.GetWindowObject(w);\n\t/// print.it(app.CurrentProject.Name);\n\t/// ]]></code>\n\t/// To get COM object from Microsoft Outlook or Publisher, use <see cref=\"GetActiveObject(string, bool)\"/>.\n\t/// </example>\n\tpublic static object GetWindowObject(wnd w, string cnChild = null, bool dontThrow = false) {\n\t\tw.ThrowIfInvalid();\n\t\tif (cnChild != null) {\n\t\t\tforeach (var c in w.ChildAll(cn: cnChild)) {\n\t\t\t\tif (0 == Api.AccessibleObjectFromWindow(c, EObjid.NATIVEOM, IID_IDispatch, out var o)) return o;\n\t\t\t}\n\t\t} else {\n\t\t\tif (0 == Api.AccessibleObjectFromWindow(w, EObjid.NATIVEOM, IID_IDispatch, out var o)) return o;\n\t\t}\n\t\tif (!dontThrow) throw new AuException($\"*get COM object from window {w}\");\n\t\treturn null;\n\t}\n\tstatic readonly Guid IID_IDispatch = new(\"00020400-0000-0000-C000-000000000046\");\n\n\t/// <summary>\n\t/// Creates COM object using ProgID.\n\t/// </summary>\n\t/// <param name=\"progID\">The programmatic identifier (ProgID) of the COM type.</param>\n\t/// <exception cref=\"Exception\"></exception>\n\t/// <remarks>\n\t/// Use this function when you don't have the COM interface definition or the interop assembly. Else use code like <c>var x = new InterfaceType();</c> or <c>var x = new CoclassType() as InterfaceType</c>.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// dynamic app = ComUtil.CreateObject(\"Excel.Application\");\n\t/// app.Visible = true;\n\t/// 3.s();\n\t/// app.Quit();\n\t/// ]]></code>\n\t/// </example>\n\tpublic static object CreateObject(string progID) {\n\t\treturn Activator.CreateInstance(Type.GetTypeFromProgID(progID, throwOnError: true));\n\t}\n\n\t/// <summary>\n\t/// Creates COM object using CLSID.\n\t/// </summary>\n\t/// <exception cref=\"Exception\"></exception>\n\tpublic static object CreateObject(in Guid clsid) {\n\t\treturn Activator.CreateInstance(Type.GetTypeFromCLSID(clsid, throwOnError: true));\n\t}\n\n\t/// <summary>\n\t/// Default value for optional parameters of type <ms>VARIANT</ms> (<c>object</c> in C#) of COM functions.\n\t/// The same as <see cref=\"System.Reflection.Missing.Value\"/>.\n\t/// </summary>\n\tpublic static readonly System.Reflection.Missing Missing = System.Reflection.Missing.Value;\n}\n"
  },
  {
    "path": "Au/Au.More/Convert2.cs",
    "content": "using System.IO.Compression;\nusing System.Security.Cryptography;\n\nnamespace Au.More;\n\n/// <summary>\n/// Data conversion functions. Compression, encryption, hex encoding, etc.\n/// </summary>\npublic static unsafe class Convert2 {\n\t#region hex\n\n\t/// <summary>\n\t/// Converts data in <c>byte[]</c> or other memory to hex-encoded string.\n\t/// </summary>\n\t/// <param name=\"data\">Data. See also: <see cref=\"MemoryMarshal.AsBytes\"/>, <see cref=\"CollectionsMarshal.AsSpan\"/>.</param>\n\t/// <param name=\"upperCase\">Let the hex string contain A-F, not a-f.</param>\n\t/// <remarks>\n\t/// The result string length is 2 <c>*</c> data length.\n\t/// Often it's better to use <see cref=\"Convert.ToBase64String\"/>, then result is 4/3 of data length. But cannot use Base64 in file names and URLs because it is case-sensitive and may contain character <c>'/'</c>. Both functions are fast.\n\t/// </remarks>\n\tpublic static string HexEncode(RByte data, bool upperCase = false) {\n\t\tfixed (byte* p = data) {\n\t\t\treturn HexEncode(p, data.Length, upperCase);\n\t\t}\n\t}\n\n\t/// <summary>\n\t/// Converts binary data in any memory to hex-encoded string.\n\t/// </summary>\n\t/// <param name=\"data\">The data. Can be any valid memory of specified size, for example a struct address.</param>\n\t/// <param name=\"size\">data memory size (bytes).</param>\n\t/// <inheritdoc cref=\"HexEncode(ReadOnlySpan{byte}, bool)\"/>\n\tpublic static string HexEncode(void* data, int size, bool upperCase = false) {\n\t\tint u = (upperCase ? 'A' : 'a') - 10;\n\t\tvar bytes = (byte*)data;\n\t\tstring R = new('\\0', size * 2);\n\t\tfixed (char* cp = R) {\n\t\t\tint* p = (int*)cp;\n\t\t\tfor (int i = 0; i < size; i++) {\n\t\t\t\tint b = bytes[i];\n\t\t\t\tint t = b & 0xf; t += t < 10 ? '0' : u;\n\t\t\t\tb >>= 4; b += b < 10 ? '0' : u;\n\t\t\t\tp[i] = t << 16 | b;\n\t\t\t}\n\t\t}\n\t\treturn R;\n\n\t\t//tested: ~50% of time takes string allocation.\n\t\t//tested: with string.Create same speed.\n\t}\n\n\t/// <summary>\n\t/// Converts a struct variable to hex-encoded string.\n\t/// </summary>\n\t/// <param name=\"x\">Variable.</param>\n\t/// <inheritdoc cref=\"HexEncode(ReadOnlySpan{byte}, bool)\"/>\n\tpublic static string HexEncode<T>(T x, bool upperCase = false) where T : unmanaged\n\t\t=> HexEncode(&x, sizeof(T), upperCase);\n\n\t/// <summary>\n\t/// Converts hex-encoded string to binary data.\n\t/// </summary>\n\t/// <param name=\"encoded\">String or <c>char[]</c> or span of string/array/memory containing hex encoded data.</param>\n\t/// <remarks>\n\t/// Skips spaces and other non-hex-digit characters. Example: <c>\"01 23 46 67\"</c> is the same as <c>\"01234667\"</c>.\n\t/// The number of hex digit characters should be divisible by 2, else the last character is ignored.\n\t/// </remarks>\n\t[SkipLocalsInit]\n\tpublic static byte[] HexDecode(RStr encoded) {\n\t\tusing FastBuffer<byte> b = new(encoded.Length / 2);\n\t\tint n = HexDecode(encoded, b.p, b.n);\n\t\tvar r = new byte[n];\n\t\tMarshal.Copy((IntPtr)b.p, r, 0, n);\n\t\treturn r;\n\t}\n\n\t/// <summary>\n\t/// Converts hex-encoded string to binary data. Writes to caller's memory buffer.\n\t/// </summary>\n\t/// <returns>The number of bytes written in <i>decoded</i> memory. It is equal or less than <c>Math.Min(bufferSize, encoded.Length/2)</c>.</returns>\n\t/// <param name=\"decoded\">Memory buffer for result.</param>\n\t/// <param name=\"bufferSize\">Max number of bytes that can be written to the <i>decoded</i> memory buffer.</param>\n\t/// <inheritdoc cref=\"HexDecode(ReadOnlySpan{char})\"/>\n\tpublic static int HexDecode(RStr encoded, void* decoded, int bufferSize) {\n\t\tif (encoded.Length == 0) return 0;\n\t\tvar t = Tables_.Hex;\n\t\tbyte* r = (byte*)decoded, rTo = r + bufferSize;\n\t\tuint k = 1;\n\t\tfor (int i = 0; i < encoded.Length; i++) {\n\t\t\tuint c = (uint)(encoded[i] - '0'); if (c >= 55) continue;\n\t\t\tc = t[c]; if (c == 0xFF) continue;\n\t\t\tk <<= 4;\n\t\t\tk |= c;\n\t\t\tif (0 != (k & 0x100)) {\n\t\t\t\tif (r >= rTo) break;\n\t\t\t\t*r++ = (byte)k;\n\t\t\t\tk = 1;\n\t\t\t}\n\t\t}\n\n\t\treturn (int)(r - (byte*)decoded);\n\n\t\t//speed: slightly slower than Base64Decode for the same binary data size.\n\t}\n\n\t/// <summary>\n\t/// Converts hex-encoded string to a struct variable.\n\t/// </summary>\n\t/// <returns><c>false</c> if decoded size != <c>sizeof(T)</c>.</returns>\n\t/// <param name=\"decoded\">The result variable.</param>\n\t/// <inheritdoc cref=\"HexDecode(ReadOnlySpan{char})\"/>\n\tpublic static bool HexDecode<T>(RStr encoded, out T decoded) where T : unmanaged {\n\t\tT t;\n\t\tif (HexDecode(encoded, &t, sizeof(T)) != sizeof(T)) { decoded = default; return false; }\n\t\tdecoded = t;\n\t\treturn true;\n\t}\n\n\t#endregion\n\n\t#region compress\n\n\t/// <summary>\n\t/// Compresses data. Uses <see cref=\"DeflateStream\"/>.\n\t/// </summary>\n\t/// <param name=\"data\">Data. See also: <see cref=\"MemoryMarshal.AsBytes\"/>, <see cref=\"CollectionsMarshal.AsSpan\"/>.</param>\n\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"DeflateStream\"/>.</exception>\n\tpublic static byte[] DeflateCompress(RByte data) {\n\t\tusing var m = new MemoryStream();\n\t\tusing (var x = new DeflateStream(m, CompressionLevel.Optimal)) x.Write(data); //note: must dispose before ToArray\n\t\treturn m.ToArray();\n\t\t//tested: GZipStream same compression but adds 18 bytes header. DeflateStream does not add any header.\n\t\t//tested: bz2 and 7z compression isn't much better with single 15 kb bmp file.\n\t}\n\n\t/// <summary>\n\t/// Decompresses data. Uses <see cref=\"DeflateStream\"/>.\n\t/// </summary>\n\t/// <returns>Decompressed data.</returns>\n\t/// <param name=\"compressed\">Compressed data.</param>\n\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"DeflateStream\"/>.</exception>\n\tpublic static byte[] DeflateDecompress(RByte compressed) {\n\t\tusing var m = new MemoryStream();\n\t\tDeflateDecompress(compressed, m);\n\t\treturn m.ToArray();\n\t}\n\n\t/// <summary>\n\t/// Decompresses data to a caller-provided memory stream. Uses <see cref=\"DeflateStream\"/>.\n\t/// </summary>\n\t/// <param name=\"compressed\">Compressed data.</param>\n\t/// <param name=\"decompressed\">Stream for decompressed data.</param>\n\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"DeflateStream\"/>.</exception>\n\tpublic static void DeflateDecompress(RByte compressed, Stream decompressed) {\n\t\tfixed (byte* p = compressed) {\n\t\t\tusing var m = new UnmanagedMemoryStream(p, compressed.Length);\n\t\t\tusing var x = new DeflateStream(m, CompressionMode.Decompress);\n\t\t\tx.CopyTo(decompressed);\n\t\t}\n\n\t\t//note: cannot deflateStream.Read directly to array because its Length etc are not supported.\n\t\t//note: also cannot use decompressedStream.GetBuffer because it can be bigger.\n\t}\n\n\t/// <summary>\n\t/// Compresses data. Uses <see cref=\"GZipStream\"/>.\n\t/// </summary>\n\t/// <param name=\"data\">Data. See also: <see cref=\"MemoryMarshal.AsBytes\"/>, <see cref=\"CollectionsMarshal.AsSpan\"/>.</param>\n\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"GZipStream\"/>.</exception>\n\tpublic static byte[] GzipCompress(RByte data) {\n\t\tusing var m = new MemoryStream();\n\t\tusing (var x = new GZipStream(m, CompressionLevel.Optimal)) x.Write(data);\n\t\treturn m.ToArray();\n\t}\n\n\t/// <summary>\n\t/// Decompresses data. Uses <see cref=\"GZipStream\"/>.\n\t/// </summary>\n\t/// <returns>Decompressed data.</returns>\n\t/// <param name=\"compressed\">Compressed data.</param>\n\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"GZipStream\"/>.</exception>\n\tpublic static byte[] GzipDecompress(RByte compressed) {\n\t\tusing var m = new MemoryStream();\n\t\tGzipDecompress(compressed, m);\n\t\treturn m.ToArray();\n\t}\n\n\t/// <summary>\n\t/// Decompresses data to a caller-provided memory stream. Uses <see cref=\"GZipStream\"/>.\n\t/// </summary>\n\t/// <param name=\"compressed\">Compressed data.</param>\n\t/// <param name=\"decompressed\">Stream for decompressed data.</param>\n\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"GZipStream\"/>.</exception>\n\tpublic static void GzipDecompress(RByte compressed, Stream decompressed) {\n\t\tfixed (byte* p = compressed) {\n\t\t\tusing var m = new UnmanagedMemoryStream(p, compressed.Length);\n\t\t\tusing var x = new GZipStream(m, CompressionMode.Decompress);\n\t\t\tx.CopyTo(decompressed);\n\t\t}\n\t}\n\n\t/// <summary>\n\t/// Compresses data. Uses <see cref=\"BrotliEncoder\"/>.\n\t/// </summary>\n\t/// <param name=\"data\">Data. See also: <see cref=\"MemoryMarshal.AsBytes\"/>, <see cref=\"CollectionsMarshal.AsSpan\"/>.</param>\n\t/// <param name=\"level\">Compression level, 0 (no compression) to 11 (maximal compression). Default 6. Bigger levels don't make much smaller but can make much slower.</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Invalid <i>level</i>.</exception>\n\t/// <exception cref=\"OutOfMemoryException\"></exception>\n\t/// <exception cref=\"AuException\"><see cref=\"BrotliEncoder.TryCompress\"/> failed.</exception>\n\tpublic static unsafe byte[] BrotliCompress(RByte data, int level = 6) {\n\t\tint n = BrotliEncoder.GetMaxCompressedLength(data.Length)\n\t\t\t+ data.Length / 1000 + 1000; //GetMaxCompressedLength returns too small value, and TryCompress fails when data is already compressed\n\t\tvar b = MemoryUtil.Alloc(n);\n\t\ttry {\n\t\t\tif (!BrotliEncoder.TryCompress(data, new(b, n), out n, level, 22)) throw new AuException();\n\t\t\treturn new Span<byte>(b, n).ToArray();\n\t\t}\n\t\tfinally { MemoryUtil.Free(b); }\n\t}\n\n\t/// <summary>\n\t/// Decompresses data. Uses <see cref=\"BrotliDecoder\"/>.\n\t/// </summary>\n\t/// <returns>Decompressed data.</returns>\n\t/// <param name=\"compressed\">Compressed data.</param>\n\t/// <exception cref=\"ArgumentException\">Invalid data.</exception>\n\t/// <exception cref=\"OutOfMemoryException\"></exception>\n\tpublic static unsafe byte[] BrotliDecompress(RByte compressed) {\n\t\tint n = checked(compressed.Length * 4 + 8000);\n\t\tfor (int i = 0; i < 3; i++) if (n < 512_000) n *= 2;\n\t\t//print.it(compressed.Length, n, n/compressed.Length); //usually ~ 80 KB\n\t\tfor (; ; n = checked(n * 2)) {\n\t\t\tbyte* b = MemoryUtil.Alloc(n);\n\t\t\ttry {\n\t\t\t\tif (BrotliDecoder.TryDecompress(compressed, new(b, n), out int nw)) return new Span<byte>(b, nw).ToArray();\n\t\t\t\tif (nw == 0) throw new ArgumentException(\"cannot decompress this data\");\n\t\t\t\t//print.it(n);\n\t\t\t}\n\t\t\tfinally { MemoryUtil.Free(b); }\n\t\t}\n\t}\n\n\t#endregion\n\n\t#region encrypt\n\n\t/// <summary>\n\t/// AES-encrypts a <c>byte[]</c> or string. Returns <c>byte[]</c>.\n\t/// </summary>\n\t/// <param name=\"data\">Data to encrypt. Can be <c>byte[]</c> or string.</param>\n\t/// <param name=\"key\">Encryption key. Can be non-empty string (eg a password) or <c>byte[]</c> of length 16 (eg a password hash).</param>\n\t/// <returns>Encrypted data. The first 16 bytes is initialization vector (not secret).</returns>\n\t/// <exception cref=\"ArgumentException\"></exception>\n\t/// <exception cref=\"CryptographicException\"></exception>\n\tpublic static byte[] AesEncryptB(object data, object key) {\n\t\tNot_.Null(data, key);\n\t\tvar enc = AesEncryptB(data, key, out var iv);\n\t\tvar r = new byte[enc.Length + iv.Length];\n\t\tiv.CopyTo(r, 0);\n\t\tenc.CopyTo(r, iv.Length);\n\t\treturn r;\n\t}\n\n\t/// <summary>\n\t/// AES-encrypts a <c>byte[]</c> or string. Returns <c>byte[]</c>.\n\t/// </summary>\n\t/// <param name=\"data\">Data to encrypt. Can be <c>byte[]</c> or string.</param>\n\t/// <param name=\"key\">Encryption key. Can be non-empty string (eg a password) or <c>byte[]</c> of length 16 (eg a password hash).</param>\n\t/// <param name=\"IV\">Receives an initialization vector. The function generates a random value. Use it with decrypt functions. Don't need to keep it in secret.</param>\n\t/// <returns>Encrypted data.</returns>\n\t/// <exception cref=\"ArgumentException\"></exception>\n\t/// <exception cref=\"CryptographicException\"></exception>\n\tpublic static byte[] AesEncryptB(object data, object key, out byte[] IV) {\n\t\tNot_.Null(data, key);\n\t\tvar d = data switch { byte[] b => b, string s => Encoding.UTF8.GetBytes(s), _ => throw new ArgumentException(\"Expected byte[] or string\", \"data\") };\n\t\tusing var aes = Aes.Create();\n\t\tusing var x = aes.CreateEncryptor(_Key(key), IV = aes.IV);\n\t\treturn x.TransformFinalBlock(d, 0, d.Length);\n\t}\n\n\t/// <summary>\n\t/// AES-encrypts a <c>byte[]</c> or string.\n\t/// Calls <see cref=\"AesEncryptB(object, object)\"/> and converts the returned <c>byte[]</c> to Base64 string.\n\t/// </summary>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// var data = \"Encryption example.\";\n\t/// var key = \"password\";\n\t/// var enc = Convert2.AesEncryptS(data, key);\n\t/// print.it(enc);\n\t/// var dec = Convert2.AesDecryptS(enc, key);\n\t/// print.it(dec);\n\t/// ]]></code>\n\t/// </example>\n\t/// <inheritdoc cref=\"AesEncryptB(object, object)\"/>\n\tpublic static string AesEncryptS(object data, object key) {\n\t\tNot_.Null(data, key);\n\t\treturn Convert.ToBase64String(AesEncryptB(data, key));\n\t}\n\n\t/// <summary>\n\t/// AES-encrypts a <c>byte[]</c> or string.\n\t/// Calls <see cref=\"AesEncryptB(object, object, out byte[])\"/> and converts the returned <c>byte[]</c> to Base64 string.\n\t/// </summary>\n\t/// <inheritdoc cref=\"AesEncryptB(object, object, out byte[])\"/>\n\tpublic static string AesEncryptS(object data, object key, out byte[] IV) {\n\t\tNot_.Null(data, key);\n\t\treturn Convert.ToBase64String(AesEncryptB(data, key, out IV));\n\t}\n\n\t/// <summary>\n\t/// AES-decrypts data. Returns <c>byte[]</c>.\n\t/// </summary>\n\t/// <param name=\"data\">Encryped data as <c>byte[]</c> or Base64 string.</param>\n\t/// <param name=\"key\">Encryption key. Can be non-empty string (eg a password) or <c>byte[]</c> of length 16 (eg a password hash).</param>\n\t/// <param name=\"IV\">If used <c>AesEncryptX</c> that returns an initialization vector, pass it here.</param>\n\t/// <exception cref=\"ArgumentException\"></exception>\n\t/// <exception cref=\"CryptographicException\"></exception>\n\tpublic static byte[] AesDecryptB(object data, object key, byte[] IV = null) {\n\t\tNot_.Null(data, key);\n\t\tvar d = data switch { byte[] b => b, string s => Convert.FromBase64String(s), _ => throw new ArgumentException(\"Expected byte[] or string\", \"data\") };\n\t\tusing var aes = Aes.Create();\n\t\tusing var x = aes.CreateDecryptor(_Key(key), IV ?? d[..16]);\n\t\tvar (from, len) = IV != null ? (0, d.Length) : (16, d.Length - 16);\n\t\treturn x.TransformFinalBlock(d, from, len);\n\t}\n\n\t/// <summary>\n\t/// AES-decrypts data.\n\t/// Calls <see cref=\"AesDecryptB(object, object, byte[])\"/> and converts the returned <c>byte[]</c> to string.\n\t/// </summary>\n\t/// <inheritdoc cref=\"AesDecryptB(object, object, byte[])\"/>\n\tpublic static string AesDecryptS(object data, object key, byte[] IV = null) {\n\t\tNot_.Null(data, key);\n\t\treturn Encoding.UTF8.GetString(AesDecryptB(data, key, IV));\n\t}\n\n\tstatic byte[] _Key(object key) {\n\t\tswitch (key) {\n\t\tcase byte[] b:\n\t\t\tif (b.Length != 16) throw new ArgumentException(\"Expected length 16\", \"key\");\n\t\t\treturn b;\n\t\tcase string s:\n\t\t\tif (s.Length == 0) throw new ArgumentException(\"Empty string\", \"key\");\n\t\t\treturn Hash.MD5(s).ToArray();\n\t\t}\n\t\tthrow new ArgumentException(\"Expected byte[] or string\", \"key\");\n\t}\n\n\t#endregion\n\n\t#region utf8\n\n\t/// <summary>\n\t/// Converts string to UTF-8 <c>byte[]</c>. Can append <c>\"\\0\"</c> (default) or some other string.\n\t/// </summary>\n\t/// <param name=\"chars\">String or <c>char[]</c> or span of string/array/memory.</param>\n\t/// <param name=\"append\">A string to append, or <c>null</c>. For example <c>\"\\0\"</c> (default) or <c>\"\\r\\n\"</c>. Must contain only ASCII characters.</param>\n\t/// <exception cref=\"ArgumentException\"><i>append</i> contains non-ASCII characters.</exception>\n\tpublic static byte[] Utf8Encode(RStr chars, string append = \"\\0\") {\n\t\tint n = Encoding.UTF8.GetByteCount(chars);\n\t\tint nAppend = append.Lenn();\n\t\tvar r = new byte[n + nAppend];\n\t\tint nn = Encoding.UTF8.GetBytes(chars, r);\n\t\tDebug.Assert(nn == n);\n\t\tif (nAppend > 0 && !(nAppend == 1 && append[0] == '\\0')) {\n\t\t\tforeach (char c in append) {\n\t\t\t\tif (c > 127) throw new ArgumentException(\"append must be ASCII\");\n\t\t\t\tr[nn++] = (byte)c;\n\t\t\t}\n\t\t}\n\t\treturn r;\n\n\t\t//speed: faster than WideCharToMultiByte. Same as System.Text.Unicode.Utf8.FromUtf16.\n\t}\n\n\t/// <summary>\n\t/// Converts <c>'\\0'</c>-terminated UTF-8 string to C# string (UTF-16).\n\t/// </summary>\n\t/// <param name=\"utf8\">UTF-8 string. If <c>null</c>, returns <c>null</c>.</param>\n\t/// <remarks>\n\t/// Finds <c>'\\0'</c> and calls <see cref=\"Encoding.GetString\"/>. Don't use this function when UTF-8 string length is known; call <c>Encoding.UTF8.GetString</c> directly.\n\t/// </remarks>\n\tpublic static string Utf8Decode(byte* utf8) => utf8 == null ? null : Encoding.UTF8.GetString(utf8, Ptr_.Length(utf8));\n\n\t/// <summary>\n\t/// Converts string to UTF-8. If non-ASCII, gets UTF-8 character offsets.\n\t/// </summary>\n\t/// <returns>\n\t/// Tuple:\n\t/// <br/>• <c>text</c> - <c>Encoding.UTF8.GetBytes(s)</c>.\n\t/// <br/>• <c>offsets</c> - <c>null</c> if <i>s</i> is ASCII. Else UTF-8 character offsets for each <i>s</i> character plus at <c>s.Length</c>.\n\t/// </returns>\n\tinternal static (byte[] text, int[] offsets) Utf8EncodeAndGetOffsets_(RStr s, bool append0 = false) {\n\t\tvar s2 = Utf8Encode(s, append0 ? \"\\0\" : \"\"); //always creates valid UTF-8. Replaces invalid UTF-16 chars.\n\t\tint len2 = s2.Length; if (append0) len2--;\n\t\tif (len2 == s.Length) return (s2, null); //ASCII\n\n\t\tvar a = new int[s.Length + 1];\n\t\tint i8 = 0, i16 = 0;\n\t\twhile (i8 < len2) {\n\t\t\ta[i16++] = i8;\n\t\t\tint c = s2[i8];\n\t\t\tif (c < 0xC0) i8++;\n\t\t\telse if (c < 0xE0) i8 += 2;\n\t\t\telse if (c < 0xF0) i8 += 3;\n\t\t\telse { a[i16++] = i8; i8 += 4; }\n\t\t}\n\t\tDebug.Assert(i8 == len2);\n\t\tDebug.Assert(i16 == s.Length);\n\t\ta[i16] = i8;\n\t\treturn (s2, a);\n\t}\n\n\t#endregion\n\n\t#region base64 (rejected)\n\n\t///// <summary>\n\t///// Gets Base64 encoded string length for non-encoded length.\n\t///// It is <c>(length + 2) / 3 * 4</c>.\n\t///// </summary>\n\t//public static int Base64EncodeLength(int length) => checked((length + 2) / 3 * 4);\n\n\t///// <summary>\n\t///// Gets decoded data length from Base64 encoded string length, assuming there are no newlines and other whitespace characters.\n\t///// It is <c>(int)(len * 3L / 4)</c> minus the number of padding <c>'='</c> characters (max 2).\n\t///// </summary>\n\t//public static int Base64DecodeLength(RStr encoded) {\n\t//\tint len = encoded.Length;\n\t//\tif (len == 0) return 0;\n\t//\tint r = (int)(len * 3L / 4);\n\t//\tif (0 == (len & 3)) {\n\t//\t\tif (encoded[len - 1] == '=') {\n\t//\t\t\tr--;\n\t//\t\t\tif (encoded[len - 2] == '=') r--;\n\t//\t\t}\n\t//\t}\n\t//\treturn r;\n\t//}\n\n\t//currently not used in this lib. Not tested speed.\n\t///// <summary>\n\t///// Converts string span containing Base64 encoded data to <c>byte[]</c>.\n\t///// </summary>\n\t///// <param name=\"encoded\">String or <c>char[]</c> or span of string/array/memory containing Base64 encoded data.</param>\n\t///// <param name=\"result\"></param>\n\t///// <returns><c>false</c> if failed.</returns>\n\t///// <remarks>\n\t///// Uses <see cref=\"Convert.TryFromBase64Chars\"/>.\n\t///// </remarks>\n\t//public static bool TryBase64Decode(RStr encoded, out byte[] result) {\n\t//\tresult = null;\n\t//\tencoded = encoded.Trim();\n\t//\t//todo: calc length: skip whitespace. Maybe bool parameter. Or use FastBuffer and copy, like in HexDecode.\n\t//\tvar a = new byte[Base64DecodeLength(encoded)];\n\t//\tif (!Convert.TryFromBase64Chars(encoded, a, out int len)) return false;\n\t//\tif (len != a.Length) {\n\t//\t\tDebug_.Print($\"{a.Length} {len}\");\n\t//\t\ta = a.AsSpan(0, len).ToArray();\n\t//\t}\n\t//\tresult = a;\n\t//\treturn true;\n\t//}\n\n\t//rejected: URL-safe Base64.\n\t//\tNot used in this lib.\n\t//\tCannot be used in file names because case-sensitive.\n\t//\tIf somebody wants URL-safe Base64, it's easy/fast to replace unsafe characters. Nobody would find and use these functions.\n\n\t///// <summary>\n\t///// Converts <c>byte[]</c> or other memory to Base64 encoded string that can be used in URL.\n\t///// </summary>\n\t///// <param name=\"data\">Data to encode.</param>\n\t///// <remarks>Like <see cref=\"Convert.ToBase64String(byte[])\"/>, but instead of <c>'/'</c> and <c>'+'</c> uses <c>'_'</c> and <c>'-'</c>.</remarks>\n\t//public static string Base64UrlEncode(RByte data) {\n\t//\tfixed (byte* p = data) return Base64UrlEncode(p, data.Length);\n\n\t//\t//speed: same as Convert.ToBase64String\n\t//}\n\n\t///// <summary>\n\t///// Converts binary data stored in any memory to Base64 encoded string that can be used in URL.\n\t///// </summary>\n\t///// <param name=\"data\">Data to encode.</param>\n\t///// <param name=\"length\">Number of bytes to encode.</param>\n\t///// <remarks>Instead of <c>'/'</c> and <c>'+'</c> uses <c>'_'</c> and <c>'-'</c>.</remarks>\n\t//public static string Base64UrlEncode(void* data, int length) {\n\t//\tvar ip = (IntPtr)data;\n\t//\treturn string.Create(Base64EncodeLength(length), (ip, length), static (span, tu) => {\n\t//\t\tConvert.TryToBase64Chars(new RByte((byte*)tu.ip, tu.length), span, out _);\n\t//\t\tfor (int i = 0; i < span.Length; i++) {\n\t//\t\t\tswitch (span[i]) { case '/': span[i] = '_'; break; case '+': span[i] = '-'; break; }\n\t//\t\t}\n\t//\t});\n\t//}\n\n\t///// <summary>\n\t///// Converts a struct variable to Base64 encoded string that can be used in URL.\n\t///// </summary>\n\t///// <param name=\"x\">Variable.</param>\n\t///// <remarks>Instead of <c>'/'</c> and <c>'+'</c> uses <c>'_'</c> and <c>'-'</c>.</remarks>\n\t//public static string Base64UrlEncode<T>(T x) where T : unmanaged {\n\t//\treturn Base64UrlEncode(&x, sizeof(T));\n\t//}\n\n\t//static void _Base64_Replace(RStr encoded, char[] a) {\n\t//\tfor (int i = 0; i < encoded.Length; i++) {\n\t//\t\tchar c = encoded[i];\n\t//\t\ta[i] = c switch { '_' => '/', '-' => '+', _ => c, };\n\t//\t}\n\t//}\n\n\t///// <summary>\n\t///// Converts string containing Base64 encoded data to <c>byte[]</c>. Supports standard encoding and URL-safe encoding.\n\t///// </summary>\n\t///// <param name=\"encoded\">String or <c>char[]</c> or span of string/array/memory containing Base64 encoded data.</param>\n\t///// <remarks>Like <see cref=\"Convert.FromBase64String(string)\"/>, but the string can contain <c>'_'</c> and <c>'-'</c> instead of <c>'/'</c> and <c>'+'</c>.</remarks>\n\t///// <exception cref=\"Exception\">Exceptions of <see cref=\"Convert.FromBase64CharArray\"/>.</exception>\n\t//public static byte[] Base64UrlDecode(RStr encoded) {\n\t//\tchar[] a = ArrayPool<char>.Shared.Rent(encoded.Length);\n\t//\ttry {\n\t//\t\t_Base64_Replace(encoded, a);\n\t//\t\treturn Convert.FromBase64CharArray(a, 0, encoded.Length);\n\t//\t}\n\t//\tfinally { ArrayPool<char>.Shared.Return(a); }\n\t//\t//never mind: almost 2 times slower than Convert.FromBase64CharArray.\n\t//\t//\tNormally this func is used with short strings and not with many strings in loop.\n\t//\t//\tArrayPool isn't as fast as should be. And copying to new array takes time.\n\t//}\n\n\t///// <summary>\n\t///// Converts string containing Base64 encoded data to bytes and stores in memory of a <c>Span</c> variable. Supports standard encoding and URL-safe encoding.\n\t///// Returns <c>false</c> if the encoded string is invalid or the buffer is too small.\n\t///// </summary>\n\t///// <param name=\"encoded\">String or <c>char[]</c> or span of string/array/memory containing Base64 encoded data.</param>\n\t///// <param name=\"decoded\">Memory buffer for the result.</param>\n\t///// <param name=\"decodedLength\"></param>\n\t///// <remarks>The string can contain <c>'_'</c> and <c>'-'</c> instead of <c>'/'</c> and <c>'+'</c>.</remarks>\n\t//public static bool Base64UrlDecode(RStr encoded, Span<byte> decoded, out int decodedLength) {\n\t//\tchar[] a = ArrayPool<char>.Shared.Rent(encoded.Length);\n\t//\ttry {\n\t//\t\t_Base64_Replace(encoded, a);\n\t//\t\treturn Convert.TryFromBase64Chars(new RStr(a, 0, encoded.Length), decoded, out decodedLength);\n\t//\t}\n\t//\tfinally { ArrayPool<char>.Shared.Return(a); }\n\t//}\n\n\t///// <summary>\n\t///// Converts string containing Base64 encoded data to bytes and stores in any memory. Supports standard encoding and URL-safe encoding.\n\t///// Returns <c>false</c> if the encoded string is invalid or the buffer is too small.\n\t///// </summary>\n\t///// <param name=\"encoded\">String or <c>char[]</c> or span of string/array/memory containing Base64 encoded data.</param>\n\t///// <param name=\"decoded\">Memory buffer for the result.</param>\n\t///// <param name=\"bufferSize\">The max number of bytes that can be written to the <i>decoded</i> memory buffer.</param>\n\t///// <param name=\"decodedLength\">Receives the number of bytes written to the <i>decoded</i> memory buffer.</param>\n\t///// <remarks>The string can contain <c>'_'</c> and <c>'-'</c> instead of <c>'/'</c> and <c>'+'</c>.</remarks>\n\t//public static bool Base64UrlDecode(RStr encoded, void* decoded, int bufferSize, out int decodedLength) {\n\t//\treturn Base64UrlDecode(encoded, new Span<byte>(decoded, bufferSize), out decodedLength);\n\t//}\n\n\t///// <summary>\n\t///// Converts string containing Base64 encoded data to a struct variable. Supports standard encoding and URL-safe encoding.\n\t///// Returns <c>false</c> if the encoded string is invalid or decoded size != <c>sizeof(T)</c>.\n\t///// </summary>\n\t///// <param name=\"encoded\">String or <c>char[]</c> or span of string/array/memory containing Base64 encoded data.</param>\n\t///// <param name=\"decoded\">The result variable.</param>\n\t///// <remarks>The string can contain <c>'_'</c> and <c>'-'</c> instead of <c>'/'</c> and <c>'+'</c>.</remarks>\n\t//public static bool Base64UrlDecode<T>(RStr encoded, out T decoded) where T : unmanaged {\n\t//\tT t;\n\t//\tif (!Base64UrlDecode(encoded, &t, sizeof(T), out int n) || n != sizeof(T)) { decoded = default; return false; }\n\t//\tdecoded = t;\n\t//\treturn true;\n\t//}\n\n\t#endregion\n}\n"
  },
  {
    "path": "Au/Au.More/DebugTraceListener.cs",
    "content": "namespace Au.More;\n\n/// <summary>\n/// Replaces the default trace listener with a listener that shows a message box on a failed assertion.\n/// </summary>\n/// <remarks>\n/// The new trace listener overrides the <see cref=\"DefaultTraceListener.Fail(string?, string?)\"/> method.\n/// On failed assertion (<see cref=\"Debug.Assert\"/>, <see cref=\"Trace.Assert\"/>, <see cref=\"Debug.Fail\"/>, <see cref=\"Trace.Fail\"/>) it shows a message box with buttons <b>Exit</b> <b>Debug</b> <b>Ignore</b>, unless debugger is attached or <see cref=\"DefaultTraceListener.AssertUiEnabled\"/> is <c>false</c>.\n/// </remarks>\npublic class DebugTraceListener : DefaultTraceListener {\n\t/// <summary>\n\t/// Replaces default trace listener.\n\t/// </summary>\n\t/// <param name=\"usePrint\">Also set <see cref=\"print.redirectDebugOutput\"/> = <c>true</c>.</param>\n\t//[Conditional(\"DEBUG\"), Conditional(\"TRACE\")] //no, in most cases this is called by this library, not directly by the app\n\tpublic static void Setup(bool usePrint) {\n\t\tif (!s_setup) {\n\t\t\ts_setup = true;\n\t\t\tTrace.Listeners.Remove(\"Default\"); //remove DefaultTraceListener. It calls Environment.FailFast which shows message box \"Unknown hard error\".\n\t\t\tTrace.Listeners.Add(new DebugTraceListener());\n\t\t}\n\t\tprint.redirectDebugOutput = usePrint;\n\t}\n\tstatic bool s_setup;\n\n\t///\n\tpublic override void Fail(string message, string detailMessage) {\n\t\tvar s = message;\n\t\tif (s.NE()) s = detailMessage; else if (!detailMessage.NE()) s = message + \"\\r\\n\" + detailMessage;\n\t\tif (!s.NE()) s += \"\\r\\n\";\n\n\t\tstring st = new StackTrace(2, true).ToString(), st1 = null;\n\t\tif (st.RxMatch(@\"(?m)^\\s+at (?!System\\.Diagnostics\\.)\", 0, out RXGroup g)) {\n\t\t\tst = st[g.Start..];\n\t\t\tst1 = st.Lines(true)[0];\n\t\t}\n\n\t\tvar s2 = \"---- Debug assertion failed ----\\r\\n\" + s + st;\n\t\tTrace.WriteLine(s2);\n\t\tif (!(print.redirectDebugOutput && print.qm2.use)) print.qm2.write(s2);\n\n\t\tif (Debugger.IsAttached) {\n\t\t\tDebugger.Break();\n\t\t} else {\n\t\t\tif (!AssertUiEnabled) return; //like default listener\n\n\t\t\ts = $\"{s}{st1}\\n\\nProcess id: {process.thisProcessId}\";\n\t\t\t\n\t\t\t//int r = dialog.showWarning(\"Debug assertion failed\", s, \"1 Exit|2 Ignore|3 script.debug|4 Debugger.Launch\", expandedText: st); //no. Need to block all messages in this thread, to prevent reentering this or executing code somewhere else.\n\t\t\t//int r = Task.Run(() => dialog.showWarning(\"Debug assertion failed\", s, \"1 Exit|2 Ignore|3 script.debug|4 Debugger.Launch\", expandedText: st)).Result; //no. It seems .NET adds messages (eg WM_TIMER) to a queue, and finally reposts all.\n\t\t\tint r = 0;\n\t\t\tvar thread = run.thread(() => { r = dialog.showWarning(\"Debug assertion failed\", s, \"1 Exit|2 Ignore|3 script.debug|4 Debugger.Launch\", expandedText: st); });\n\t\t\tdo 100.ms(); while (thread.IsAlive);\n\t\t\t\n\t\t\tif (r == 1) Api.ExitProcess(-1);\n\t\t\tif (r == 2) return;\n\t\t\tif (r == 4) Debugger.Launch();\n\t\t\telse {\n\t\t\t\tscript.debug();\n\t\t\t\tDebugger.Break();\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au/Au.More/Dpi.cs",
    "content": "namespace Au.More {\n\t/// <summary>\n\t/// Functions for high-DPI screen support.\n\t/// </summary>\n\t/// <remarks>\n\t/// To find DPI % on Windows 10 and 11: <b>Settings > System > Display > Scale and layout</b>. If not 100%, it means high DPI.\n\t/// \n\t/// This program must be per-monitor-DPI-aware. Else results are undefined.\n\t/// </remarks>\n\tpublic static class Dpi {\n\t\t/// <summary>\n\t\t/// Gets DPI of the primary screen at the time this process started.\n\t\t/// </summary>\n\t\tpublic static int System {\n\t\t\tget {\n\t\t\t\tif (_systemDPI == 0) {\n\t\t\t\t\tusing var dcs = new ScreenDC_();\n\t\t\t\t\t_systemDPI = Api.GetDeviceCaps(dcs, 90); //LOGPIXELSY\n\n\t\t\t\t\t//could use GetDpiForSystem instead (Windows 10 1607).\n\t\t\t\t\t//\tNormally the same result (tested), but probably not if thread awareness context is Unaware (not tested).\n\t\t\t\t}\n\t\t\t\treturn _systemDPI;\n\t\t\t}\n\t\t}\n\t\tstatic int _systemDPI;\n\n\t\t/// <summary>\n\t\t/// Gets the DPI of a window, as used in the window's process. It never changes for that window instance.\n\t\t/// </summary>\n\t\t/// <returns>If failed, returns the system DPI (<see cref=\"System\"/>).</returns>\n\t\t/// <param name=\"w\">A top-level window or control. Can belong to any process.</param>\n\t\t/// <param name=\"supportWin81\">\n\t\t/// If <c>true</c>, works on Windows 8.1 and later; however on Windows 8.1 slower and less reliable.\n\t\t/// If <c>false</c> (default), works on Windows 10 1607 and later.\n\t\t/// </param>\n\t\t/// <remarks>\n\t\t/// The result depends on the DPI awareness of the window:\n\t\t/// - per-monitor-DPI-aware - usually DPI of the windows's screen.\n\t\t/// - system aware - system DPI (DPI of the primary screen).\n\t\t/// - unaware - 96.\n\t\t/// \n\t\t/// The result also depends on the Windows version:\n\t\t/// - Works best on Windows 10 1607 and later. Uses API <ms>GetDpiForWindow</ms>.\n\t\t/// - On Windows 8.1 works if <i>supportWin81</i> <c>true</c>. If <c>false</c> (default), returns <see cref=\"System\"/>.\n\t\t/// - On Windows 7 and 8.0 always returns <see cref=\"System\"/>, because there are no Windows API. Most apps are system-DPI-aware and the result is correct; for unaware apps the result is incorrect. These Windows versions don't support per-monitor DPI.\n\t\t/// </remarks>\n\t\tpublic static int OfWindow(wnd w, bool supportWin81 = false) {\n\t\t\tif (!osVersion.minWin8_1) return System;\n\t\t\tint R = 0;\n\t\t\tif (!w.Is0) {\n\t\t\t\tif (osVersion.minWin10_1607) R = Api.GetDpiForWindow(w);\n\t\t\t\telse if (supportWin81) {\n\t\t\t\t\tvar v = WindowDpiAwareness(w); //info: quickly returns Awareness.PerMonitor if w.IsOfThisProcess\n\t\t\t\t\tif (v == Awareness.Unaware) R = 96;\n\t\t\t\t\telse if (v == Awareness.PerMonitor)\n\t\t\t\t\t\tif (0 != Api.GetDpiForMonitor(Api.MonitorFromWindow(w.Window, SODefault.Nearest), 0, out R, out _)) R = 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn R != 0 ? R : System;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Returns <c>OfWindow(w.Hwnd())</c>.\n\t\t/// </summary>\n\t\t/// <param name=\"w\">A Winforms window or control.</param>\n\t\tpublic static int OfWindow(System.Windows.Forms.Control w) => OfWindow(w.Hwnd());\n\t\t//rejected: supportWin81\n\n\t\t/// <summary>\n\t\t/// Returns <c>OfWindow(w.Hwnd())</c>.\n\t\t/// </summary>\n\t\t/// <param name=\"w\">A WPF window or element.</param>\n\t\tpublic static int OfWindow(System.Windows.DependencyObject w) => OfWindow(w.Hwnd());\n\n\t\t/// <summary>\n\t\t/// Gets DPI of a screen.\n\t\t/// </summary>\n\t\t/// <returns><see cref=\"System\"/> if fails or if not supported on this Windows version.</returns>\n\t\t/// <param name=\"hMonitor\">Native screen handle (<c>HMONITOR</c>).</param>\n\t\t/// <param name=\"supportWin81\">Support Windows 8.1 and later. If <c>false</c> (default), supports Windows 10 1607 and later.</param>\n\t\t/// <remarks>\n\t\t/// Uses API <ms>GetDpiForMonitor</ms>.\n\t\t/// </remarks>\n\t\t/// <seealso cref=\"screen.Dpi\"/>\n\t\tpublic static int OfScreen(IntPtr hMonitor, bool supportWin81 = false) {\n\t\t\tbool os = supportWin81 ? osVersion.minWin8_1 : osVersion.minWin10_1607;\n\t\t\treturn os && 0 == Api.GetDpiForMonitor(hMonitor, 0, out int R, out _) ? R : System;\n\t\t}\n\n\t\t/////\n\t\t//public static int OfScreen(screen s, bool supportWin81 = false) => OfScreen(s.Now, supportWin81);\n\n\t\t/// <summary>\n\t\t/// Scales <c>int</c> if the specified DPI isn't 96 (100%).\n\t\t/// </summary>\n\t\tpublic static int Scale(int i, DpiOf dpiOf) => Math2.MulDiv(i, dpiOf, 96);\n\n\t\t//no. Eg also would be used for uint, long... Or name eg ScaleD. Or add double extension method.\n\t\t///// <summary>\n\t\t///// Scales <c>int</c> if the specified DPI isn't 96 (100%).\n\t\t///// </summary>\n\t\t//public static int Scale(double i, DpiOf dpiOf) => (i*(int)dpiOf/96).ToInt();\n\n\t\t/// <summary>\n\t\t/// Unscales <c>int</c> if the specified DPI isn't 96 (100%).\n\t\t/// </summary>\n\t\tpublic static double Unscale(int i, DpiOf dpiOf) => i * (96d / dpiOf);\n\t\t//Unscaling sometimes useful with WPF. Unscale to double, not int, else result often incorrect.\n\n\t\t/// <summary>\n\t\t/// Scales <see cref=\"SIZE\"/> if the specified DPI isn't 96 (100%).\n\t\t/// </summary>\n\t\tpublic static SIZE Scale(SIZE z, DpiOf dpiOf) {\n\t\t\tint dpi = dpiOf;\n\t\t\tz.width = Math2.MulDiv(z.width, dpi, 96);\n\t\t\tz.height = Math2.MulDiv(z.height, dpi, 96);\n\t\t\treturn z;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Scales <see cref=\"System.Windows.Size\"/> if the specified DPI isn't 96 (100%).\n\t\t/// </summary>\n\t\tpublic static SIZE Scale(System.Windows.Size z, DpiOf dpiOf) {\n\t\t\tdouble f = (int)dpiOf / 96d;\n\t\t\tz.Width *= f;\n\t\t\tz.Height *= f;\n\t\t\treturn new(z.Width.ToInt(), z.Height.ToInt());\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Unscales <see cref=\"SIZE\"/> if the specified DPI isn't 96 (100%).\n\t\t/// </summary>\n\t\tpublic static System.Windows.Size Unscale(SIZE z, DpiOf dpiOf) {\n\t\t\tdouble f = 96d / dpiOf;\n\t\t\treturn new(z.width * f, z.height * f);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Scales <see cref=\"RECT\"/> if the specified DPI isn't 96 (100%).\n\t\t/// </summary>\n\t\tpublic static RECT Scale(RECT r, DpiOf dpiOf) {\n\t\t\tint dpi = dpiOf;\n\t\t\tr.left = Math2.MulDiv(r.left, dpi, 96);\n\t\t\tr.top = Math2.MulDiv(r.top, dpi, 96);\n\t\t\tr.right = Math2.MulDiv(r.right, dpi, 96);\n\t\t\tr.bottom = Math2.MulDiv(r.bottom, dpi, 96);\n\t\t\treturn r;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Unscales <see cref=\"RECT\"/> if the specified DPI isn't 96 (100%).\n\t\t/// </summary>\n\t\tpublic static System.Windows.Rect Unscale(RECT r, DpiOf dpiOf) {\n\t\t\tdouble f = 96d / dpiOf;\n\t\t\treturn new(r.left * f, r.top * f, r.Width * f, r.Height * f);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Calls API <ms>GetSystemMetricsForDpi</ms> if available, else <ms>GetSystemMetrics</ms>.\n\t\t/// </summary>\n\t\tpublic static int GetSystemMetrics(int nIndex, DpiOf dpiOf)\n\t\t\t=> osVersion.minWin10_1607\n\t\t\t? Api.GetSystemMetricsForDpi(nIndex, dpiOf)\n\t\t\t: Api.GetSystemMetrics(nIndex);\n\n\t\t/// <summary>\n\t\t/// Calls API <ms>SystemParametersInfoForDpi</ms> if available, else <ms>SystemParametersInfo</ms>.\n\t\t/// Use only with <i>uiAction</i> = <ms>SPI_GETICONTITLELOGFONT</ms>, <ms>SPI_GETICONMETRICS</ms>, <ms>SPI_GETNONCLIENTMETRICS</ms>.\n\t\t/// </summary>\n\t\tpublic static unsafe bool SystemParametersInfo(uint uiAction, int uiParam, void* pvParam, DpiOf dpiOf)\n\t\t\t=> osVersion.minWin10_1607\n\t\t\t? Api.SystemParametersInfoForDpi(uiAction, uiParam, pvParam, 0, dpiOf)\n\t\t\t: Api.SystemParametersInfo(uiAction, uiParam, pvParam);\n\n\t\t/// <summary>\n\t\t/// Calls API <ms>AdjustWindowRectExForDpi</ms> if available, else <ms>AdjustWindowRectEx</ms>.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Also adds scrollbar width or/and height if need.\n\t\t/// </remarks>\n\t\tpublic static bool AdjustWindowRectEx(DpiOf dpiOf, ref RECT r, WS style, WSE exStyle, bool hasMenu = false) {\n\t\t\tint dpi = dpiOf;\n\t\t\tbool ok = osVersion.minWin10_1607\n\t\t\t\t? Api.AdjustWindowRectExForDpi(ref r, style, hasMenu, exStyle, dpi)\n\t\t\t\t: Api.AdjustWindowRectEx(ref r, style, hasMenu, exStyle);\n\t\t\tif (ok) {\n\t\t\t\tif (style.Has(WS.VSCROLL)) r.Width += ScrollbarV_(dpi);\n\t\t\t\tif (style.Has(WS.HSCROLL)) r.Width += ScrollbarH_(dpi);\n\t\t\t}\n\t\t\treturn ok;\n\t\t}\n\n\t\tinternal static int ScrollbarV_(int dpi) => GetSystemMetrics(Api.SM_CXVSCROLL, dpi);\n\t\tinternal static int ScrollbarH_(int dpi) => GetSystemMetrics(Api.SM_CYHSCROLL, dpi);\n\n\t\t/// <summary>\n\t\t/// DPI awareness of a window or process.\n\t\t/// </summary>\n\t\tpublic enum Awareness { //== DPI_AWARENESS == PROCESS_DPI_AWARENESS\n\t\t\t///\n\t\t\tInvalid = -1,\n\t\t\t///\n\t\t\tUnaware,\n\t\t\t///\n\t\t\tSystem,\n\t\t\t///\n\t\t\tPerMonitor\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets DPI awareness of a window.\n\t\t/// </summary>\n\t\t/// <returns><see cref=\"Awareness.Invalid\"/> if failed.</returns>\n\t\t/// <param name=\"w\">A top-level window or control. Can belong to any process.</param>\n\t\t/// <remarks>\n\t\t/// Works best on Windows 10 1607 and later; uses API <ms>GetWindowDpiAwarenessContext</ms>.\n\t\t/// On Windows 8.1 returns <see cref=\"Awareness.PerMonitor\"/> if <i>w</i> is of this process; else uses API <ms>GetProcessDpiAwareness</ms>, which is slower and less reliable.\n\t\t/// On Windows 7 and 8.0 always returns <see cref=\"Awareness.System\"/>, because there are no Windows API.\n\t\t/// </remarks>\n\t\tpublic static Awareness WindowDpiAwareness(wnd w) {\n\t\t\tif (osVersion.minWin10_1607) {\n\t\t\t\treturn Api.GetAwarenessFromDpiAwarenessContext(Api.GetWindowDpiAwarenessContext(w));\n\t\t\t} else if (osVersion.minWin8_1) {\n\t\t\t\tif (w.IsOfThisProcess) return Awareness.PerMonitor;\n\t\t\t\tusing var hp = Handle_.OpenProcess(w);\n\t\t\t\treturn (!hp.Is0 && 0 == Api.GetProcessDpiAwareness(hp, out var a)) ? a : Awareness.Invalid;\n\t\t\t} else {\n\t\t\t\treturn Awareness.System;\n\t\t\t\t//could use, IsWindowVirtualized (except if of this process), but slow and unreliable.\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Detects whether the window is DPI-scaled/virtualized.\n\t\t/// </summary>\n\t\t/// <returns><c>false</c> if not DPI-scaled/virtualized or if fails to detect or if invalid window handle.</returns>\n\t\t/// <param name=\"w\">A top-level window or control. Can belong to any process.</param>\n\t\t/// <remarks>\n\t\t/// Such windows are a little blurry (entire client area).\n\t\t/// \n\t\t/// OS scales a window when it is on a high-DPI screen, depending on the DPI awareness of the window:\n\t\t/// - Unaware - always.\n\t\t/// - System - if the screen DPI is not equal to the system DPI of that process (which usually is of the primary screen, but not always).\n\t\t/// \n\t\t/// Such windows have various problems for automation apps:\n\t\t/// - Often difficult or impossible to get correct rectangles of UI elements (and therefore cannot click etc) or get correct UI element from point. It depends on used API (UIA, MSAA, JAB), inproc/notinproc, OS version and application.\n\t\t/// - On Windows 7 and 8.0 cannot easily get correct rectangles of such windows and their controls. This library ignores it, because would be too much work to apply workarounds in so many places and just for legacy OS versions (it has been fixed in Windows 8.1).\n\t\t/// - If with <see cref=\"uiimage\"/> want to use window pixels, need to capture image from window pixels, not from screen pixels.\n\t\t/// \n\t\t/// This function is not completely reliable. And not very fast. This process must be per-monitor-DPI-aware.\n\t\t/// </remarks>\n\t\tpublic static bool IsWindowVirtualized(wnd w) {\n\t\t\tif (GetScalingInfo_(w, out bool scaled, out _, out _)) return scaled;\n\t\t\treturn IsWindowVirtualizedLegacy_(w);\n\n\t\t\t//note: child windows can have different DPI awareness (minWin10_1607). See GetWindowDpiHostingBehavior. Not tested, not seen.\n\n\t\t\t//Also tested detecting with GDI. GDI functions return logical (not DPI-scaled) offsets/rectangles/etc. Works, but much slower.\n\t\t}\n\n\t\t/// <summary>\n\t\t/// On Win10+ returns <see cref=\"IsWindowVirtualized\"/>. Else <c>false</c>.\n\t\t/// </summary>\n\t\tinternal static bool IsWindowVirtualizedWin10_(wnd w) => osVersion.minWin10_1607 && IsWindowVirtualized(w);\n\n\t\t/// <summary>\n\t\t/// If possible, gets whether the window is DPI-scaled/virtualized, and gets physical and logical rects if scaled.\n\t\t/// Returns <c>false</c> if <c>!osVersion.minWin10_1607</c> or if cannot get that info.\n\t\t/// Gets that info in a fast and reliable way.\n\t\t/// </summary>\n\t\tinternal static bool GetScalingInfo_(wnd w, out bool scaled, out RECT rPhysical, out RECT rLogical) {\n\t\t\tscaled = false; rPhysical = default; rLogical = default;\n\t\t\tif (!osVersion.minWin10_1607) return false;\n\t\t\tvar awareness = WindowDpiAwareness(w); //fast on Win10\n\t\t\tif (awareness is Awareness.System or Awareness.Unaware) { //tested: unaware-gdi-scaled same as unaware\n\t\t\t\tif (awareness == Awareness.System && Api.GetDpiForWindow(w) != System) { /*fast*/\n\t\t\t\t\t//Cannot get rLogical. It's rare and temporary, ie when the user recently changed DPI of the primary screen.\n\t\t\t\t\t//Even if this func isn't used to get rects, without this fast code could be unreliable.\n\t\t\t\t\tDebug_.Print(\"w System DPI != our System DPI\");\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tfor (; ; ) {\n\t\t\t\t\tRECT r1 = w.Rect, r2, r3; //note: with ClientRect 4 times faster, but unreliable if small rect. Now fast enough.\n\t\t\t\t\tbool rectWorkaround = false;\n\t\t\t\t\tusing (var u = new AwarenessContext(awareness == Awareness.System ? -2 : -1)) {\n\t\t\t\t\t\tif (Api.GetAwarenessFromDpiAwarenessContext(u.Previous_) != Awareness.PerMonitor) { /*fast*/\n\t\t\t\t\t\t\t//cannot get rPhysical. But let's set PM awareness and get it. Works even if this process is Unaware.\n\t\t\t\t\t\t\trectWorkaround = _GetRect(w, out r1);\n\t\t\t\t\t\t\tDebug_.Print(\"bad DPI awareness of this thread; workaround \" + (rectWorkaround ? \"OK\" : \"failed\"));\n\t\t\t\t\t\t\tif (!rectWorkaround) return false; //unlikely. Then the caller probably will call the legacy func, it works with any DPI awareness.\n\t\t\t\t\t\t}\n\t\t\t\t\t\tr2 = w.Rect;\n\t\t\t\t\t\tif (r2 == r1) break;\n\t\t\t\t\t}\n\t\t\t\t\tif (!rectWorkaround) r3 = w.Rect; else _GetRect(w, out r3);\n\t\t\t\t\tif (r3 != r1) continue; //moved, resized or closed between Rect and Rect\n\t\t\t\t\tscaled = true;\n\t\t\t\t\trPhysical = r1;\n\t\t\t\t\trLogical = r2;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tstatic bool _GetRect(wnd w, out RECT r) {\n\t\t\t\t\tusing (var u2 = new AwarenessContext(-4)) { //per-monitor-v2\n\t\t\t\t\t\tif (u2.Previous_ == 0) { r = default; return false; } //API failed. Unlikely. Works even if this process is Unaware.\n\t\t\t\t\t\tr = w.Rect;\n\t\t\t\t\t}\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\tinternal static bool IsWindowVirtualizedLegacy_(wnd w) {\n\t\t\t//less reliable if control.\n\t\t\t//\tLogicalToPhysicalPoint fails if not in top-level window rect.\n\t\t\t//\tIt seems PhysicalToLogicalPointForPerMonitorDPI doesn't fail, but it fails if not in that screen.\n\t\t\tw = w.Window;\n\n\t\t\tRECT rPrev = default;\n\t\t\tfor (int i = 0; i < 5; i++) {\n\t\t\t\tif (!Api.GetWindowRect(w, out var r)) break; //Win10 1 mcs hot, 20 cold, old OS fast\n\t\t\t\tif (r != rPrev) { i = 0; rPrev = r; } //moved or resized\n\t\t\t\tPOINT p = new(r.CenterX, r.CenterY); //p must be inside the window\n\t\t\t\tif (i == 1) p.y = r.bottom - 1; else if (i == 2) p.y = r.top; else if (i == 3) p.x = r.right - 1; else if (i == 4) p.x = r.left; //and p must be in correct screen, which is unknown and therefore we use this code to guess. Normally succeeds at i==0, but may fail when the window is more than in 1 screen.\n\t\t\t\tPOINT k = p;\n\t\t\t\tif (osVersion.minWin8_1 ? Api.PhysicalToLogicalPointForPerMonitorDPI(w, ref p) : Api.LogicalToPhysicalPoint(w, ref p)) { //Win10 3 mcs hot, old OS fast\n\n\t\t\t\t\t//API bug: may scale the point although the window isn't scaled. Never mind.\n\t\t\t\t\t//\tWhen window's center is between 2 screens and at the same time half of the window is offscreen. The area is several pixels wide.\n\t\t\t\t\t//if (p != k) print.it(k, p);\n\n\t\t\t\t\tif (p != k) return true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t\t//tested: the API works with hidden and off-screen top-level windows too. Minimized windows are off-screen.\n\t\t\t//tested: works even if this process is DPI System or Unaware. Tested on Win10.\n\t\t\t//speed on Win10 when not PM-aware: 4 mcs hot, 40 mcs cold. All API much faster on old OS (tested on Vmware).\n\t\t}\n\n\t\t//No. In some cases (window positions) screen.of(w) does not match scaling. Not much faster.\n\t\t//public static bool IsWindowVirtualized2(wnd w) {\n\t\t//\tif (osVersion.minWin10_1607) {\n\t\t//\t\tif (WindowDpiAwareness(w) is Awareness.PerMonitor or Awareness.Invalid) return false; //fast on Win10; slow on Win8.1, unavailable on Win7/8. Other API very slow on Win10, much faster on old OS (Vmware).\n\t\t//\t\tw = w.Window; if (w.Is0) return false; //0.6 mcs hot\n\t\t//\t\tvar m = screen.of(w);\n\t\t//\t\tint d1 = m.Dpi, d2 = OfWindow(w);\n\t\t//\t\t//if (d1 == d2) print.it(d1);\n\t\t//\t\tif (d1 == d2) return false;\n\t\t//\t\treturn w.IsAlive;\n\t\t//\t} else {\n\t\t//\t\t//same as now\n\t\t//\t\treturn false;\n\t\t//\t}\n\t\t//}\n\n\t\t/// <summary>\n\t\t/// Can be used to temporarily change thread's DPI awareness context with API <ms>SetThreadDpiAwarenessContext</ms>.\n\t\t/// Does nothing if the API is unavailable (added in Windows 10 version 1607).\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Programs that use this library should use manifest with <c>dpiAwareness = PerMonitorV2</c> and <c>dpiAware = True/PM</c>. Then default DPI awareness context is <ms>DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2</ms>.\n\t\t/// </remarks>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// using var dac = new Dpi.AwarenessContext(-1);\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic struct AwarenessContext : IDisposable {\n\t\t\tnint _dac;\n\n\t\t\t/// <summary>\n\t\t\t/// If <see cref=\"Available\"/>, calls API <ms>SetThreadDpiAwarenessContext</ms>.\n\t\t\t/// </summary>\n\t\t\t/// <param name=\"dpiContext\">One of <ms>DPI_AWARENESS_CONTEXT</ms> constants: -1 <c>unaware</c>, -2 <c>system</c>, -3 <c>per-monitor</c>, -4 <c>per-monitor-v2</c>, -5 <c>unaware-gdiscaled</c>. Or a <ms>DPI_AWARENESS_CONTEXT</ms> handle.</param>\n\t\t\tpublic AwarenessContext(nint dpiContext) {\n\t\t\t\t_dac = osVersion.minWin10_1607 ? Api.SetThreadDpiAwarenessContext(dpiContext) : default;\n\t\t\t}\n\t\t\t//rejected: enum dpiContext.\n\n\t\t\t/// <summary>\n\t\t\t/// If <see cref=\"Available\"/>, calls API <ms>GetWindowDpiAwarenessContext</ms> and <ms>SetThreadDpiAwarenessContext</ms>. Sets the awareness context of this thread equal to that of the window.\n\t\t\t/// </summary>\n\t\t\tpublic AwarenessContext(wnd w) : this(Available ? Api.GetWindowDpiAwarenessContext(w) : 0) { }\n\n\t\t\t/// <summary>\n\t\t\t/// Restores previous DPI awareness context.\n\t\t\t/// </summary>\n\t\t\tpublic void Dispose() {\n\t\t\t\tif (_dac == default) return;\n\t\t\t\tApi.SetThreadDpiAwarenessContext(_dac);\n\t\t\t\t_dac = default;\n\t\t\t}\n\n\t\t\t///\n\t\t\tinternal nint Previous_ => _dac;\n\n\t\t\t/// <summary>\n\t\t\t/// Returns <c>true</c> if thread DPI awareness contexts are available on this Windows version (Windows 10 1607 and later).\n\t\t\t/// </summary>\n\t\t\tpublic static bool Available => osVersion.minWin10_1607;\n\n\t\t\t/////\n\t\t\t//public const nint DPI_AWARENESS_CONTEXT_UNAWARE = -1;\n\t\t\t/////\n\t\t\t//public const nint DPI_AWARENESS_CONTEXT_SYSTEM_AWARE = -2;\n\t\t\t/////\n\t\t\t//public const nint DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE = -3;\n\t\t\t/////\n\t\t\t//public const nint DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = -4;\n\t\t\t/////\n\t\t\t//public const nint DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED = -5;\n\t\t}\n\t}\n}\n\nnamespace Au.Types {\n\t/// <summary>\n\t/// Used for <i>dpiOf</i> parameter of functions. Allows to pass either a DPI value or an object from which gets DPI (window, screen etc).\n\t/// </summary>\n\t/// <remarks>\n\t/// Has implicit conversions from:\n\t/// <br/>• <c>int</c> - DPI.\n\t/// <br/>• <see cref=\"wnd\"/> - gets DPI of the window.\n\t/// <br/>• <c>IntPtr</c> or <c>nint</c> - (screen handle) gets DPI of the screen.\n\t/// <br/>• <see cref=\"POINT\"/> - gets DPI of the screen that contains the point.\n\t/// <br/>• <see cref=\"RECT\"/> - gets DPI of screen that contains the rectangle.\n\t/// <br/>• <see cref=\"System.Windows.Forms.Control\"/> - gets DPI of the winforms control.\n\t/// <br/>• <see cref=\"System.Windows.DependencyObject\"/> - gets DPI of the WPF element.\n\t/// \n\t/// The conversion operators set the <see cref=\"Dpi\"/> property and the function can use it.\n\t/// </remarks>\n\tpublic struct DpiOf {\n\t\treadonly int _dpi;\n\n\t\t///\n\t\tpublic DpiOf(int dpi) { _dpi = dpi; }\n\n\t\t/// <exception cref=\"AuWndException\">Invalid window handle.</exception>\n\t\tpublic DpiOf(wnd w) {\n\t\t\tw.ThrowIfInvalid();\n\t\t\t_dpi = More.Dpi.OfWindow(w);\n\t\t}\n\t\t//rejected: parameter supportWin81. Rarely used. Can use Dpi functions when need.\n\n\t\t/// <exception cref=\"ArgumentNullException\"></exception>\n\t\t/// <exception cref=\"AuWndException\">Invalid window handle.</exception>\n\t\tpublic DpiOf(System.Windows.Forms.Control c) : this(Not_.NullRet(c).Hwnd()) { }\n\n\t\t/// <exception cref=\"ArgumentNullException\"></exception>\n\t\t/// <exception cref=\"AuWndException\">Invalid window handle.</exception>\n\t\tpublic DpiOf(System.Windows.DependencyObject c) : this(Not_.NullRet(c).Hwnd()) { }\n\n\t\t///\n\t\tpublic DpiOf(IntPtr hMonitor) { _dpi = More.Dpi.OfScreen(hMonitor); }\n\n\t\t///\n\t\tpublic DpiOf(POINT screenOf) : this(screen.of(screenOf).Handle) { }\n\n\t\t///\n\t\tpublic DpiOf(RECT screenOf) : this(screen.of(screenOf).Handle) { }\n\n\t\t///\n\t\tpublic static implicit operator DpiOf(int dpi) => new(dpi);\n\n\t\t/// <exception cref=\"AuWndException\">Invalid window handle.</exception>\n\t\tpublic static implicit operator DpiOf(wnd w) => new(w);\n\n\t\t/// <exception cref=\"ArgumentNullException\"></exception>\n\t\t/// <exception cref=\"AuWndException\">Invalid window handle.</exception>\n\t\tpublic static implicit operator DpiOf(System.Windows.Forms.Control c) => new(c);\n\n\t\t/// <exception cref=\"ArgumentNullException\"></exception>\n\t\t/// <exception cref=\"AuWndException\">Invalid window handle.</exception>\n\t\tpublic static implicit operator DpiOf(System.Windows.DependencyObject c) => new(c);\n\n\t\t///\n\t\tpublic static implicit operator DpiOf(IntPtr hMonitor) => new(hMonitor);\n\n\t\t///\n\t\tpublic static implicit operator DpiOf(POINT screenOf) => new(screenOf);\n\n\t\t///\n\t\tpublic static implicit operator DpiOf(RECT screenOf) => new(screenOf);\n\n\t\t///\n\t\tpublic int Dpi => _dpi;\n\n\t\t///\n\t\tpublic static implicit operator int(DpiOf d) => d._dpi;\n\t}\n}"
  },
  {
    "path": "Au/Au.More/FastBuffer.cs",
    "content": "namespace Au.More {\n\t/// <summary>\n\t/// Memory buffer on stack with ability to expand and use heap memory.\n\t/// Can be used for calling Windows API or building arrays.\n\t/// Must be used with <c>[SkipLocalsInit]</c> attribute; add it to the caller function or class.\n\t/// </summary>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// print.it(api.GetEnvironmentVariable(\"temp\"));\n\t/// \n\t/// unsafe class api : NativeApi {\n\t/// \t[DllImport(\"kernel32.dll\", EntryPoint = \"GetEnvironmentVariableW\", SetLastError = true)]\n\t/// \tstatic extern int _GetEnvironmentVariable(string lpName, char* lpBuffer, int nSize);\n\t/// \n\t/// \t[SkipLocalsInit]\n\t/// \tinternal static string GetEnvironmentVariable(string name) {\n\t/// \t\tusing FastBuffer<char> b = new();\n\t/// \t\tfor (; ; ) if (b.GetString(_GetEnvironmentVariable(name, b.p, b.n), out var s)) return s;\n\t/// \t}\n\t/// }\n\t/// ]]></code>\n\t/// </example>\n\t[StructLayout(LayoutKind.Sequential, Size = 16 + StackSize + 16)] //16 for other fields + 16 for safety\n\tpublic unsafe ref struct FastBuffer<T> where T : unmanaged {\n\t\tT* _p; //buffer pointer (on stack or native heap)\n\t\tint _n; //buffer length (count of T elements)\n\t\tbool _free; //if false, buffer is on stack in this variable (_p=&_stack). If true, buffer is allocated with MemoryUtil.Alloc.\n\t\tlong _stack; //start of buffer of StackSize size\n\n\t\t/// <summary>\n\t\t/// A <see cref=\"FastBuffer{T}\"/> variable contains a field of this size. It is a memory buffer on stack.\n\t\t/// It is a byte count and does not depend on <c>T</c>. To get count of <c>T</c> elements on stack: <c>StackSize/sizeof(T)</c>.\n\t\t/// </summary>\n\t\tpublic const int StackSize = 2048;\n\t\t//const int StackSize = 16; //test More() and GetString()\n\n\t\t//Also tested:\n\t\t//\t1. Struct of normal size, when caller passes stackalloc'ed Span<T>. Slow in Debug. And more caling code.\n\t\t//\t2. See the commented out Buffer_.\n\t\t//\t3. Callback. Good: easy to use, less calling code, don't need [SkipLocalsInit]. Bad: problem with captured variables (garbage, slow); slower in any case.\n\t\t//\t4. foreach. Nothing good.\n\n\t\t/// <summary>\n\t\t/// Memory buffer pointer.\n\t\t/// </summary>\n\t\tpublic T* p => _p;\n\n\t\t/// <summary>\n\t\t/// Returns memory buffer pointer (<see cref=\"p\"/>).\n\t\t/// </summary>\n\t\tpublic static implicit operator T*(in FastBuffer<T> b) => b._p;\n\n\t\t/// <summary>\n\t\t/// Gets reference to <c>p[i]</c>. Does not check bounds.\n\t\t/// </summary>\n\t\tpublic ref T this[int i] => ref _p[i];\n\n\t\t/// <summary>\n\t\t/// Memory buffer length as number of elements of type <c>T</c>.\n\t\t/// </summary>\n\t\tpublic int n => _n;\n\n\t\t/// <summary>\n\t\t/// Allocates first buffer of default size. It is on stack (in this variable), and its length is <c>StackSize/sizeof(T)</c> elements of type <c>T</c> (2048 bytes or 1024 chars or 512 ints...).\n\t\t/// </summary>\n\t\tpublic FastBuffer() {\n\t\t\t//With this overload slightly faster. Also, the int overload is confusing when need buffer of default size.\n\n\t\t\t_stack = 0;\n\t\t\tfixed (long* t = &_stack) { _p = (T*)t; }\n\t\t\t//_p = (T*)Unsafe.AsPointer(ref _stack); //slower in Debug, same speed in Release\n\t\t\t_n = StackSize / sizeof(T);\n\t\t\t_free = false;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Allocates first buffer of specified size.\n\t\t/// </summary>\n\t\t/// <param name=\"n\">\n\t\t/// Buffer length (number of elements of type <c>T</c>).\n\t\t/// If <c>&lt;= StackSize/sizeof(T)</c>, the buffer contains<c> StackSize/sizeof(T)</c> elements on stack (in this variable); it is 2048 bytes or 1024 chars or 512 ints... Else allocates native memory (much slower).\n\t\t/// </param>\n\t\tpublic FastBuffer(int n) {\n\t\t\t_stack = 0;\n\t\t\tint nStack = StackSize / sizeof(T);\n\t\t\tif (_free = n > nStack) {\n\t\t\t\t_p = MemoryUtil.Alloc<T>(n + 1); //+1 for safety\n\t\t\t\t_n = n;\n\t\t\t} else {\n\t\t\t\t_n = nStack;\n\t\t\t\tfixed (long* t = &_stack) { _p = (T*)t; }\n\t\t\t\t//_p = (T*)Unsafe.AsPointer(ref _stack);\n\t\t\t}\n\n\t\t\t//rejected: for medium-size buffers use ArrayPool.\n\t\t\t//\tIt is usually much faster than MemoryUtil, but getting pinned pointer from array is slow.\n\t\t\t//\tTo get pinned pointer, I know 3 ways.\n\t\t\t//\t\t1. fixed(...){...}. Here cannot be used.\n\t\t\t//\t\t2. GCHandle. Makes 3 times slower than just ArrayPool Rent/Return. Then MemoryUtil is only 50% slower. With this buffer size it does not matter.\n\t\t\t//\t\t3. Memory<T>/MemoryHandle. Same speed as GCHandle. MemoryHandle is bigger and managed.\n\t\t\t//\tTested: MemoryPool is slower than ArrayPool and creates garbage.\n\n\t\t\t//tested: heap memory allocation becomes much slower starting from 1 MB. Then virtual memory is several times faster (else much slower). But with this buffer size it does not matter.\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Allocates new bigger buffer of specified length. Frees old buffer if need.\n\t\t/// </summary>\n\t\t/// <param name=\"n\">Number of elements of type <c>T</c>.</param>\n\t\t/// <param name=\"preserve\">Copy previous buffer contents to the new buffer.</param>\n\t\t/// <exception cref=\"ArgumentException\"><i>n</i> &lt;= current buffer length.</exception>\n\t\tpublic void More(int n, bool preserve = false) {\n\t\t\tif (_n == 0) throw new ArgumentNullException(null, \"No buffer. Use another constructor.\"); //with many API would still work, but very slow\n\t\t\tif (n <= _n) throw new ArgumentException(\"n <= this.n\");\n\t\t\tif (!preserve) {\n\t\t\t\tDispose();\n\t\t\t\t_p = MemoryUtil.Alloc<T>(n + 1); //+1 for safety\n\t\t\t} else if (_free) {\n\t\t\t\tMemoryUtil.ReAlloc<T>(ref _p, n + 1);\n\t\t\t} else {\n\t\t\t\tvar p = MemoryUtil.Alloc<T>(n + 1);\n\t\t\t\tMemoryUtil.Copy(_p, p, _n * sizeof(T));\n\t\t\t\t_p = p;\n\t\t\t}\n\t\t\t_n = n;\n\t\t\t_free = true;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Allocates new bigger buffer of at least <c>n*2</c> length. Frees old buffer if need.\n\t\t/// </summary>\n\t\t/// <param name=\"preserve\">Copy previous buffer contents to the new buffer.</param>\n\t\tpublic void More(bool preserve = false) => More(Math.Max(checked(_n * 2), 0x4000 / sizeof(T)), preserve); //16 KB = StackSize * 8\n\n\t\t/// <summary>\n\t\t/// Frees allocated memory if need.\n\t\t/// </summary>\n\t\t[MethodImpl(MethodImplOptions.AggressiveInlining)]\n\t\tpublic void Dispose() {\n\t\t\tif (_free) { MemoryUtil.Free(_p); _p = null; _n = 0; }\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets API result as string, or allocates bigger buffer if old buffer was too small.\n\t\t/// This function can be used when <c>T</c> is <c>char</c>.\n\t\t/// </summary>\n\t\t/// <param name=\"r\">The return value of the called Windows API function, if it returns string length or required buffer length. Or you can call <see cref=\"FindStringLength\"/>.</param>\n\t\t/// <param name=\"s\">Receives the result string if succeeded, else <i>sDefault</i> (default <c>null</c>).</param>\n\t\t/// <param name=\"flags\">\n\t\t/// Use if the API function isn't like this:\n\t\t/// <br/>• If succeeds, returns string length without the terminating <c>'\\0'</c> character.\n\t\t/// <br/>• If buffer too small, returns required buffer length.\n\t\t/// <br/>• If fails, returns 0.\n\t\t/// </param>\n\t\t/// <param name=\"sDefault\">Set <i>s</i> = this string if buffer too small or <i>r</i> &lt; 1 or if the retrieved string == this string (avoid creating new string).</param>\n\t\t/// <returns>\n\t\t/// If <i>r</i> > <see cref=\"n\"/>, calls <c>More(r);</c> and returns <c>false</c>.\n\t\t/// Else creates new string of <i>r</i> length and returns <c>true</c>.\n\t\t/// </returns>\n\t\tpublic bool GetString(int r, out string s, BSFlags flags = 0, string sDefault = null) {\n\t\t\tif (sizeof(T) != 2) throw new InvalidOperationException(); //cannot use extension method that would be added only to FastBuffer<char>. See GetString2 comments below.\n\t\t\ts = sDefault;\n\n\t\t\tif (r >= _n - 1) {\n\t\t\t\tif (0 != (flags & BSFlags.Truncates)) {\n\t\t\t\t\tif (r >= (0 != (flags & BSFlags.ReturnsLengthWith0) ? _n : _n - 1)) {\n\t\t\t\t\t\tMore();\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t} else if (r > _n) {\n\t\t\t\t\tMore(r);\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (r > 0) {\n\t\t\t\tif (0 != (flags & BSFlags.ReturnsLengthWith0)) r--;\n\t\t\t\tif (sDefault == null || !new Span<char>(_p, r).SequenceEqual(sDefault)) s = new string((char*)_p, 0, r);\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Finds length of <c>'\\0'</c>-terminated UTF-16 string in buffer and converts to C# string.\n\t\t/// This function can be used when <c>T</c> is <c>char</c>. Use when length is unknown.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// If there is no <c>'\\0'</c> character, gets whole buffer, and the string probably is truncated.\n\t\t/// </remarks>\n\t\tpublic string GetStringFindLength() {\n\t\t\treturn new((char*)_p, 0, FindStringLength());\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Finds length of <c>'\\0'</c>-terminated <c>char</c> string in buffer.\n\t\t/// Returns <see cref=\"n\"/> if there is no <c>'\\0'</c> character.\n\t\t/// </summary>\n\t\tpublic int FindStringLength() {\n\t\t\tif (sizeof(T) != 2) throw new InvalidOperationException();\n\t\t\treturn Ptr_.Length((char*)_p, _n);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Finds length of <c>'\\0'</c>-terminated <c>byte</c> string in buffer.\n\t\t/// Returns <see cref=\"n\"/> if there is no <c>'\\0'</c> character.\n\t\t/// </summary>\n\t\tpublic int FindByteStringLength() {\n\t\t\tif (sizeof(T) != 1) throw new InvalidOperationException();\n\t\t\treturn Ptr_.Length((byte*)_p, _n);\n\t\t}\n\t}\n}\n\nnamespace Au.Types {\n\t//error CS1657: Cannot use 'b' as a ref or out value because it is a 'using variable'.\n\t//If in, compiles, but very slow. Probably copies t because calls More() which isn't readonly.\n\t//public static partial class ExtAu\n\t//{\n\t//\tpublic static unsafe bool GetString2(this ref FastBuffer<char> t, int r, out string s, BSFlags flags = 0, string sDefault = null) {\n\t//\t\t...\n\t//\t}\n\t//}\n\n\t/// <summary>\n\t/// Flags for <see cref=\"FastBuffer{T}.GetString\"/>.\n\t/// </summary>\n\t[Flags]\n\tpublic enum BSFlags {\n\t\t/// <summary>\n\t\t/// If buffer too small, the API gets part of string instead of returning required buffer length.\n\t\t/// </summary>\n\t\tTruncates = 1,\n\n\t\t/// <summary>\n\t\t/// The API returns string length including the terminating <c>'\\0'</c> character.\n\t\t/// </summary>\n\t\tReturnsLengthWith0 = 2,\n\t}\n}\n\n//rejected. Maybe 5% faster, but not so easy to use. Need more code, and easy to forget something. Also VS warning if: for(var p = stackalloc ...).\n///// <summary>\n///// Allocates and frees native memory buffers for calling Windows API and other functions.\n///// Used when need to retry when the primary stackalloc'ed buffer was too small.\n///// </summary>\n///// <example>\n///// <code><![CDATA[\n///// [SkipLocalsInit]\n///// unsafe static string CurDir() {\n///// \tusing Buffer_<char> b = new(); int n, r;\n///// \tfor (var p = stackalloc char[n = 1024]; ; p = b.Alloc(n = r)) {\n///// \t\tr = api.GetCurrentDirectory(n, p);\n///// \t\tif(r < n) return r > 0 ? new(p, 0, r) : null;\n///// \t}\n///// }\n///// \n///// [SkipLocalsInit]\n///// unsafe static string WinText(wnd w) {\n///// \tusing Buffer_<char> b = new(); int n, r;\n///// \tfor (var p = stackalloc char[n = 1024]; ; p = b.Alloc(checked(n *= 2))) {\n///// \t\tr = api.InternalGetWindowText(w, p, n);\n///// \t\tif (r < n - 1) return new string(p, 0, r);\n///// \t}\n///// }\n///// ]]></code>\n///// </example>\n//unsafe ref struct Buffer_<T> where T : unmanaged\n//{\n//\tT* _p;\n\n//\t/// <summary>\n//\t/// Allocates n elements of type T of native memory. Frees old memory.\n//\t/// </summary>\n//\tpublic T* Alloc(int n) {\n//\t\tif (_p != null) { var p = _p; _p = null; MemoryUtil.Free(p); }\n//\t\treturn _p = MemoryUtil.Alloc<T>(n);\n//\t}\n\n//\t/// <summary>\n//\t/// Frees allocated memory.\n//\t/// </summary>\n//\tpublic void Dispose() {\n//\t\tif (_p != null) { var p = _p; _p = null; MemoryUtil.Free(p); }\n//\t}\n//}\n"
  },
  {
    "path": "Au/Au.More/FileOpenSaveDialog.cs",
    "content": "namespace Au.More {\n\t/// <summary>\n\t/// Shows standard \"Open\", \"Save As\" or \"Select Folder\" dialog to select a file or folder.\n\t/// </summary>\n\t/// <remarks>\n\t/// This class exists because the similar .NET classes have these problems:\n\t/// - May disable the <see cref=\"AppDomain.UnhandledException\"/> event.\n\t/// - As owner window they support only <c>Window</c> or <c>Form</c>. This class also supports window handles.\n\t/// - They support only filesystem items.\n\t/// - There is no option to not add the selected file to recent files that are displayed in the context menu of the taskbar button etc.\n\t/// - The WPF class does not have a client GUID property.\n\t/// \n\t/// There are 2 main dialog types - open (<see cref=\"ShowOpen\"/>) and save (<see cref=\"ShowSave\"/>). All other functions of this class (properties, etc) are common to opening and saving.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// var d = new FileOpenSaveDialog { FileTypes = \"Text files|*.txt|All files|*.*\" };\n\t/// if(d.ShowOpen(out string s)) print.it(s);\n\t/// ]]></code>\n\t/// </example>\n\tpublic class FileOpenSaveDialog {\n\t\treadonly string _clientGuid;\n\t\treadonly bool _clearClientData;\n\n\t\t/// <param name=\"clientGuid\">A GUID used to save the recently used folder path and other data of this dialog. Optional. See <ms>IFileDialog.SetClientGuid</ms>.</param>\n\t\t/// <param name=\"clearClientData\">Clear the recently used folder path and other data of this dialog.</param>\n\t\tpublic FileOpenSaveDialog(string clientGuid = null, bool clearClientData = false) {\n\t\t\t_clientGuid = clientGuid;\n\t\t\t_clearClientData = clearClientData;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Options common to all dialog types. Rarely used.\n\t\t/// </summary>\n\t\tpublic FOSFlags CommonFlags { get; set; }\n\n\t\t/// <summary>\n\t\t/// List of file types, like with <see cref=\"Microsoft.Win32.FileDialog.Filter\"/>.\n\t\t/// Example: <c>\"Text files|*.txt|Office files|*.doc;*.xls|All files|*.*\"</c>\n\t\t/// </summary>\n\t\tpublic Strings FileTypes { get; set; }\n\n\t\t/// <summary>\n\t\t/// 1-based index of the selected file type (see <see cref=\"FileTypes\"/>).\n\t\t/// </summary>\n\t\tpublic int FileTypeIndex { get; set; }\n\n\t\t/// <summary>\n\t\t/// Default extension to add to file names.\n\t\t/// Must be like <c>\"txt\"</c>, not like <c>\".txt\"</c>.\n\t\t/// If <c>null</c> (default), gets from <see cref=\"FileTypes\"/>; set <c>\"\"</c> to prevent it.\n\t\t/// </summary>\n\t\tpublic string DefaultExt { get; set; }\n\n\t\t/// <summary>\n\t\t/// Initial folder for the first time. Later is used the recently used folder instead.\n\t\t/// </summary>\n\t\tpublic string InitFolderFirstTime { get; set; }\n\n\t\t/// <summary>\n\t\t/// Initial folder to use now.\n\t\t/// In most cases it's recommended to use <see cref=\"InitFolderFirstTime\"/> instead.\n\t\t/// </summary>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// d.InitFolderNow = @\"C:\\Test\";\n\t\t/// d.InitFolderNow = /* This PC */ \":: 14001f50e04fd020ea3a6910a2d808002b30309d\";\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic string InitFolderNow { get; set; }\n\n\t\t/// <summary>\n\t\t/// Sets the initial file name text in the edit box.\n\t\t/// </summary>\n\t\tpublic string FileNameText { private get; set; }\n\n\t\t/// <summary>\n\t\t/// Sets the file name edit box label.\n\t\t/// </summary>\n\t\tpublic string FileNameLabel { private get; set; }\n\n\t\t/// <summary>\n\t\t/// Sets the <b>OK</b> button label.\n\t\t/// </summary>\n\t\tpublic string OkButtonLabel { private get; set; }\n\n\t\t/// <summary>\n\t\t/// Sets the dialog window name.\n\t\t/// </summary>\n\t\tpublic string Title { private get; set; }\n\n\t\tobject _Show(bool save, _Api.FOS f, AnyWnd owner, string saveFile = null) {\n\t\t\tvar w = owner.Hwnd;\n\t\t\tif (w.Is0) {\n\t\t\t\tw = wnd.active;\n\t\t\t\tif (!w.IsOfThisThread) w = default;\n\t\t\t}\n\n\t\t\tvar d = (_Api.IFileDialog)Activator.CreateInstance(Type.GetTypeFromCLSID(save ? _Api.CLSID_FileSaveDialog : _Api.CLSID_FileOpenDialog));\n\n\t\t\tif (_clientGuid != null) d.SetClientGuid(new(_clientGuid));\n\t\t\tif (_clearClientData) d.ClearClientData();\n\n\t\t\tf |= (_Api.FOS)CommonFlags ^ (_Api.FOS.FOS_DONTADDTORECENT | _Api.FOS.FOS_PATHMUSTEXIST); //print.it(f);\n\t\t\td.SetOptions(f);\n\n\t\t\tvar defExt = DefaultExt;\n\t\t\tvar ft = FileTypes.Value == null ? null : FileTypes.ToArray();\n\t\t\tif (ft != null) {\n\t\t\t\tint n = ft.Length / 2, i = FileTypeIndex;\n\t\t\t\td.SetFileTypes(n, ft);\n\t\t\t\tif (i > 0) d.SetFileTypeIndex(i--);\n\t\t\t\tif (defExt == null && (uint)i < n) ft[i * 2 + 1].RxMatch(@\"^\\*\\.([^*?;]+)\", 1, out defExt);\n\t\t\t}\n\t\t\tif (!defExt.NE()) d.SetDefaultExtension(defExt);\n\t\t\tif (InitFolderFirstTime != null && _ShellItemFromString(InitFolderFirstTime, out var si1)) d.SetDefaultFolder(si1);\n\t\t\tif (InitFolderNow != null && _ShellItemFromString(InitFolderNow, out var si2)) d.SetFolder(si2);\n\t\t\tif (FileNameText != null) d.SetFileName(FileNameText);\n\t\t\tif (FileNameLabel != null) d.SetFileNameLabel(FileNameLabel);\n\t\t\tif (OkButtonLabel != null) d.SetOkButtonLabel(OkButtonLabel);\n\t\t\tif (Title != null) d.SetTitle(Title);\n\n\t\t\tif (saveFile != null && d is _Api.IFileSaveDialog sd && _ShellItemFromString(saveFile, out var si3)) sd.SetSaveAsItem(si3);\n\n\t\t\t//if (0!=d.Show(w)) return null;\n\n\t\t\t//API bug: after d.Show stops working AppDomain.UnhandledException event.\n\t\t\t//\tSame with .NET wrappers; same when the native API called in C++; same with API GetOpenFileName.\n\t\t\t//\tThe API removes the .NET's unhandled exception filter and sets null filter. Need to restore it.\n\t\t\t//\tAppModuleInit_ auto-restores, but in some apps it isn't called.\n\t\t\t//\tFixed in Win11.\n\t\t\tvar uef = Api.SetUnhandledExceptionFilter(0);\n\t\t\tApi.SetUnhandledExceptionFilter(uef);\n\t\t\ttry { if (0 != d.Show(w)) return null; }\n\t\t\tfinally { Api.SetUnhandledExceptionFilter(uef); }\n\n\t\t\tif (ft != null) FileTypeIndex = d.GetFileTypeIndex();\n\t\t\t//FileNameText=d.GetFileName(); //fails here. And not useful.\n\n\t\t\tif (!f.Has(_Api.FOS.FOS_ALLOWMULTISELECT)) return _ShellItemToString(d.GetResult());\n\t\t\tvar r = ((_Api.IFileOpenDialog)d).GetResults();\n\t\t\tvar a = new string[r.GetCount()];\n\t\t\tfor (int i = 0; i < a.Length; i++) a[i] = _ShellItemToString(r.GetItemAt(i));\n\t\t\treturn a;\n\t\t}\n\n\t\tobject _ShowOpen(AnyWnd owner = default, bool multiSelect = false, bool selectFolder = false, bool onlyFilesystem = true, bool fileMustExist = true, bool previewPane = false) {\n\t\t\t//default FOS_NOCHANGEDIR, FOS_PATHMUSTEXIST, FOS_FILEMUSTEXIST\n\t\t\tvar f = _Api.FOS.FOS_NOCHANGEDIR;\n\t\t\tif (multiSelect) f |= _Api.FOS.FOS_ALLOWMULTISELECT;\n\t\t\tif (selectFolder) f |= _Api.FOS.FOS_PICKFOLDERS;\n\t\t\tif (onlyFilesystem) f |= _Api.FOS.FOS_FORCEFILESYSTEM; else f |= _Api.FOS.FOS_ALLNONSTORAGEITEMS;\n\t\t\tif (fileMustExist) f |= _Api.FOS.FOS_FILEMUSTEXIST;\n\t\t\tif (previewPane) f |= _Api.FOS.FOS_FORCEPREVIEWPANEON;\n\n\t\t\treturn _Show(false, f, owner);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Shows \"Open\" or \"Select Folder\" dialog that allows to select single item.\n\t\t/// </summary>\n\t\t/// <param name=\"result\">Full path of the selected file.</param>\n\t\t/// <param name=\"owner\">Owner window. Optional.</param>\n\t\t/// <param name=\"selectFolder\">Select folders, not files.</param>\n\t\t/// <param name=\"onlyFilesystem\">The dialog allows to select only file system items (files, folders), not other shell items or URLs. Default <c>true</c>. If <c>false</c>, other shell items are returned like <c>\":: ITEMIDLIST\"</c>; see <see cref=\"Pidl\"/>.</param>\n\t\t/// <param name=\"fileMustExist\">The dialog can return only existing items. Default <c>true</c>.</param>\n\t\t/// <param name=\"previewPane\">Display the preview pane.</param>\n\t\t/// <returns><c>true</c> on <b>OK</b>, <c>false</c> on <b>Cancel</b> or error.</returns>\n\t\tpublic bool ShowOpen(out string result, AnyWnd owner = default, bool selectFolder = false, bool onlyFilesystem = true, bool fileMustExist = true, bool previewPane = false) {\n\t\t\tresult = _ShowOpen(owner, false, selectFolder, onlyFilesystem, fileMustExist, previewPane) as string;\n\t\t\treturn result != null;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Shows \"Open\" or \"Select Folder\" dialog that allows to select multiple items.\n\t\t/// </summary>\n\t\t/// <param name=\"result\">Full paths of the selected files.</param>\n\t\t/// <inheritdoc cref=\"ShowOpen(out string, AnyWnd, bool, bool, bool, bool)\"/>\n\t\tpublic bool ShowOpen(out string[] result, AnyWnd owner = default, bool selectFolder = false, bool onlyFilesystem = true, bool fileMustExist = true, bool previewPane = false) {\n\t\t\tresult = _ShowOpen(owner, true, selectFolder, onlyFilesystem, fileMustExist, previewPane) as string[];\n\t\t\treturn result != null;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Shows \"Save As\" dialog.\n\t\t/// </summary>\n\t\t/// <param name=\"result\">Full path of the selected file.</param>\n\t\t/// <param name=\"owner\">Owner window. Optional.</param>\n\t\t/// <param name=\"overwritePrompt\">If the selected file already exists, show a message box. Default <c>true</c>.</param>\n\t\t/// <param name=\"initFile\">The initially selected file. Its name is displayed in the file name edit box, and the containing folder is opened. This would generally be used when the application is saving a file that already exists. For new files use <see cref=\"FileNameText\"/>.</param>\n\t\t/// <returns><c>true</c> on <b>OK</b>, <c>false</c> on <b>Cancel</b> or error.</returns>\n\t\tpublic bool ShowSave(out string result, AnyWnd owner = default, bool overwritePrompt = true, string initFile = null) {\n\t\t\t//default FOS_OVERWRITEPROMPT, FOS_NOCHANGEDIR, FOS_PATHMUSTEXIST, FOS_NOREADONLYRETURN\n\t\t\tvar f = _Api.FOS.FOS_NOCHANGEDIR;\n\t\t\tif (overwritePrompt) f |= _Api.FOS.FOS_OVERWRITEPROMPT;\n\t\t\t/*if(!overwriteReadonly)*/\n\t\t\tf |= _Api.FOS.FOS_NOREADONLYRETURN; //always works like with this flag\n\n\t\t\t//if(createPrompt) f|=_Api.FOS.FOS_CREATEPROMPT; //does not work. The .NET wrapper shows messagebox explicitly.\n\t\t\t//if(!testCreate) f|=_Api.FOS.FOS_NOTESTFILECREATE; //not important, not tested\n\t\t\t//if(strictFileTypes) f|=_Api.FOS.FOS_STRICTFILETYPES; //does not work\n\n\t\t\tresult = _Show(true, f, owner, initFile) as string;\n\t\t\treturn result != null;\n\t\t}\n\n\t\tstatic string _ShellItemToString(_Api.IShellItem r) {\n\t\t\t//if(!f.Has(_Api.FOS.FOS_FORCEFILESYSTEM)) {\n\t\t\t//\tvar k=r.GetAttributes(0xffffffff);\n\t\t\t//\tif(0==(k&_Api.SFGAO_FILESYSTEM)) {\n\t\t\t//\t\tprint.it(k);\n\t\t\t//\t}\n\t\t\t//}\n\n\t\t\tvar s = r.GetDisplayName(SIGDN.FILESYSPATH | SIGDN.URL); //info: for a non-FS item, even with SIGDN.FILESYSPATH gets string like \"::{GUID}\"\n\t\t\treturn Pidl.ClsidToItemidlist_(s);\n\t\t}\n\n\t\tstatic bool _ShellItemFromString(string path, out _Api.IShellItem si) {\n\t\t\tif (path.Starts(\":: \")) {\n\t\t\t\tvar p = Pidl.FromString(path); if (p == null) { si = null; return false; }\n\t\t\t\treturn 0 == _Api.SHCreateItemFromIDList(p.UnsafePtr, typeof(_Api.IShellItem).GUID, out si);\n\t\t\t} else {\n\t\t\t\treturn 0 == _Api.SHCreateItemFromParsingName(path, default, typeof(_Api.IShellItem).GUID, out si);\n\t\t\t}\n\t\t}\n\n\t\tunsafe class _Api {\n\t\t\tinternal static Guid CLSID_FileOpenDialog = new(0xDC1C5A9C, 0xE88A, 0x4DDE, 0xA5, 0xA1, 0x60, 0xF8, 0x2A, 0x20, 0xAE, 0xF7);\n\n\t\t\tinternal static Guid CLSID_FileSaveDialog = new(0xC0B4E2F3, 0xBA21, 0x4773, 0x8D, 0xBA, 0x33, 0x5E, 0xC9, 0x46, 0xEB, 0x8B);\n\n\t\t\t[ComImport, Guid(\"d57c7288-d4ad-4768-be02-9d969532d960\"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\n\t\t\tinternal interface IFileOpenDialog : IFileDialog {\n\t\t\t\t//IModalWindow\n\t\t\t\t[PreserveSig] new int Show(wnd hwndOwner);\n\t\t\t\t//IFileDialog\n\t\t\t\tnew void SetFileTypes(int cFileTypes, [MarshalAs(UnmanagedType.LPArray)][In] string[] rgFilterSpec);\n\t\t\t\tnew void SetFileTypeIndex(int iFileType);\n\t\t\t\tnew int GetFileTypeIndex();\n\t\t\t\t[PreserveSig] new int Advise(/*IFileDialogEvents pfde, out uint pdwCookie*/);\n\t\t\t\t[PreserveSig] new int Unadvise(uint dwCookie);\n\t\t\t\tnew void SetOptions(_Api.FOS fos);\n\t\t\t\tnew _Api.FOS GetOptions();\n\t\t\t\tnew void SetDefaultFolder(IShellItem psi);\n\t\t\t\tnew void SetFolder(IShellItem psi);\n\t\t\t\tnew IShellItem GetFolder();\n\t\t\t\tnew IShellItem GetCurrentSelection();\n\t\t\t\tnew void SetFileName([MarshalAs(UnmanagedType.LPWStr)] string pszName);\n\t\t\t\t[return: MarshalAs(UnmanagedType.LPWStr)] new string GetFileName();\n\t\t\t\tnew void SetTitle([MarshalAs(UnmanagedType.LPWStr)] string pszTitle);\n\t\t\t\tnew void SetOkButtonLabel([MarshalAs(UnmanagedType.LPWStr)] string pszText);\n\t\t\t\tnew void SetFileNameLabel([MarshalAs(UnmanagedType.LPWStr)] string pszLabel);\n\t\t\t\tnew IShellItem GetResult();\n\t\t\t\t[PreserveSig] new int AddPlace(/*IShellItem psi, FDAP fdap*/);\n\t\t\t\tnew void SetDefaultExtension([MarshalAs(UnmanagedType.LPWStr)] string pszDefaultExtension);\n\t\t\t\t[PreserveSig] new int Close(int hr);\n\t\t\t\tnew void SetClientGuid(in Guid guid);\n\t\t\t\tnew void ClearClientData();\n\t\t\t\t[PreserveSig] new int SetFilter(/*IShellItemFilter pFilter*/);\n\t\t\t\t//IFileOpenDialog\n\t\t\t\tIShellItemArray GetResults();\n\t\t\t\tIShellItemArray GetSelectedItems();\n\t\t\t}\n\n\t\t\t[ComImport, Guid(\"84bccd23-5fde-4cdb-aea4-af64b83d78ab\"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\n\t\t\tinternal interface IFileSaveDialog : IFileDialog {\n\t\t\t\t//IModalWindow\n\t\t\t\t[PreserveSig] new int Show(wnd hwndOwner);\n\t\t\t\t//IFileDialog\n\t\t\t\tnew void SetFileTypes(int cFileTypes, [MarshalAs(UnmanagedType.LPArray)][In] string[] rgFilterSpec);\n\t\t\t\tnew void SetFileTypeIndex(int iFileType);\n\t\t\t\tnew int GetFileTypeIndex();\n\t\t\t\t[PreserveSig] new int Advise(/*IFileDialogEvents pfde, out uint pdwCookie*/);\n\t\t\t\t[PreserveSig] new int Unadvise(uint dwCookie);\n\t\t\t\tnew void SetOptions(_Api.FOS fos);\n\t\t\t\tnew _Api.FOS GetOptions();\n\t\t\t\tnew void SetDefaultFolder(IShellItem psi);\n\t\t\t\tnew void SetFolder(IShellItem psi);\n\t\t\t\tnew IShellItem GetFolder();\n\t\t\t\tnew IShellItem GetCurrentSelection();\n\t\t\t\tnew void SetFileName([MarshalAs(UnmanagedType.LPWStr)] string pszName);\n\t\t\t\t[return: MarshalAs(UnmanagedType.LPWStr)] new string GetFileName();\n\t\t\t\tnew void SetTitle([MarshalAs(UnmanagedType.LPWStr)] string pszTitle);\n\t\t\t\tnew void SetOkButtonLabel([MarshalAs(UnmanagedType.LPWStr)] string pszText);\n\t\t\t\tnew void SetFileNameLabel([MarshalAs(UnmanagedType.LPWStr)] string pszLabel);\n\t\t\t\tnew IShellItem GetResult();\n\t\t\t\t[PreserveSig] new int AddPlace(/*IShellItem psi, FDAP fdap*/);\n\t\t\t\tnew void SetDefaultExtension([MarshalAs(UnmanagedType.LPWStr)] string pszDefaultExtension);\n\t\t\t\t[PreserveSig] new int Close(int hr);\n\t\t\t\tnew void SetClientGuid(in Guid guid);\n\t\t\t\tnew void ClearClientData();\n\t\t\t\t[PreserveSig] new int SetFilter(/*IShellItemFilter pFilter*/);\n\t\t\t\t//IFileSaveDialog\n\t\t\t\tvoid SetSaveAsItem(IShellItem psi);\n\t\t\t\t[PreserveSig] int SetProperties(/*IPropertyStore pStore*/);\n\t\t\t\t[PreserveSig] int SetCollectedProperties(/*IPropertyDescriptionList pList, [MarshalAs(UnmanagedType.Bool)] bool fAppendDefault*/);\n\t\t\t\t[PreserveSig] int GetProperties(/*out IPropertyStore ppStore*/);\n\t\t\t\t[PreserveSig] int ApplyProperties(/*IShellItem psi, IPropertyStore pStore, wnd hwnd, IFileOperationProgressSink pSink*/);\n\t\t\t}\n\n\t\t\t[ComImport, Guid(\"42f85136-db7e-439c-85f1-e4075d135fc8\"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\n\t\t\tinternal interface IFileDialog {\n\t\t\t\t//IModalWindow\n\t\t\t\t[PreserveSig] int Show(wnd hwndOwner);\n\t\t\t\t//IFileDialog\n\t\t\t\tvoid SetFileTypes(int cFileTypes, [MarshalAs(UnmanagedType.LPArray)][In] string[] rgFilterSpec);\n\t\t\t\tvoid SetFileTypeIndex(int iFileType);\n\t\t\t\tint GetFileTypeIndex();\n\t\t\t\t[PreserveSig] int Advise(/*IFileDialogEvents pfde, out uint pdwCookie*/);\n\t\t\t\t[PreserveSig] int Unadvise(uint dwCookie);\n\t\t\t\tvoid SetOptions(_Api.FOS fos);\n\t\t\t\t_Api.FOS GetOptions();\n\t\t\t\tvoid SetDefaultFolder(IShellItem psi);\n\t\t\t\tvoid SetFolder(IShellItem psi);\n\t\t\t\tIShellItem GetFolder();\n\t\t\t\tIShellItem GetCurrentSelection();\n\t\t\t\tvoid SetFileName([MarshalAs(UnmanagedType.LPWStr)] string pszName);\n\t\t\t\t[return: MarshalAs(UnmanagedType.LPWStr)] string GetFileName();\n\t\t\t\tvoid SetTitle([MarshalAs(UnmanagedType.LPWStr)] string pszTitle);\n\t\t\t\tvoid SetOkButtonLabel([MarshalAs(UnmanagedType.LPWStr)] string pszText);\n\t\t\t\tvoid SetFileNameLabel([MarshalAs(UnmanagedType.LPWStr)] string pszLabel);\n\t\t\t\tIShellItem GetResult();\n\t\t\t\t[PreserveSig] int AddPlace(/*IShellItem psi, FDAP fdap*/);\n\t\t\t\tvoid SetDefaultExtension([MarshalAs(UnmanagedType.LPWStr)] string pszDefaultExtension);\n\t\t\t\t[PreserveSig] int Close(int hr);\n\t\t\t\tvoid SetClientGuid(in Guid guid);\n\t\t\t\tvoid ClearClientData();\n\t\t\t\t[PreserveSig] int SetFilter(/*IShellItemFilter pFilter*/);\n\t\t\t}\n\n\t\t\t[ComImport, Guid(\"43826d1e-e718-42ee-bc55-a1e261c37bfe\"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\n\t\t\tinternal interface IShellItem {\n\t\t\t\t[PreserveSig] int BindToHandler(IntPtr pbc, in Guid bhid, in Guid riid, void** ppv);\n\t\t\t\t[PreserveSig] int GetParent(out IShellItem ppsi);\n\t\t\t\t[return: MarshalAs(UnmanagedType.LPWStr)] string GetDisplayName(SIGDN sigdnName);\n\t\t\t\tuint GetAttributes(uint sfgaoMask);\n\t\t\t\t[PreserveSig] int Compare(IShellItem psi, uint hint, out int piOrder);\n\t\t\t}\n\n\t\t\t[ComImport, Guid(\"b63ea76d-1f85-456f-a19c-48159efa858b\"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\n\t\t\tinternal interface IShellItemArray {\n\t\t\t\t[PreserveSig] int BindToHandler(/*IntPtr pbc, in Guid bhid, in Guid riid, void** ppvOut*/);\n\t\t\t\t[PreserveSig] int GetPropertyStore(/*GETPROPERTYSTOREFLAGS flags, in Guid riid, void** ppv*/);\n\t\t\t\t[PreserveSig] int GetPropertyDescriptionList(/*in PROPERTYKEY keyType, in Guid riid, void** ppv*/);\n\t\t\t\t[PreserveSig] int GetAttributes(/*SIATTRIBFLAGS AttribFlags, uint sfgaoMask, out uint psfgaoAttribs*/);\n\t\t\t\tint GetCount();\n\t\t\t\tIShellItem GetItemAt(int dwIndex);\n\t\t\t\t[PreserveSig] int EnumItems(/*out IEnumShellItems ppenumShellItems*/);\n\t\t\t}\n\n\t\t\t[Flags]\n\t\t\tinternal enum FOS : uint {\n\t\t\t\tFOS_OVERWRITEPROMPT = 0x2,\n\t\t\t\tFOS_STRICTFILETYPES = 0x4,\n\t\t\t\tFOS_NOCHANGEDIR = 0x8,\n\t\t\t\tFOS_PICKFOLDERS = 0x20,\n\t\t\t\tFOS_FORCEFILESYSTEM = 0x40,\n\t\t\t\tFOS_ALLNONSTORAGEITEMS = 0x80,\n\t\t\t\tFOS_NOVALIDATE = 0x100,\n\t\t\t\tFOS_ALLOWMULTISELECT = 0x200,\n\t\t\t\tFOS_PATHMUSTEXIST = 0x800,\n\t\t\t\tFOS_FILEMUSTEXIST = 0x1000,\n\t\t\t\tFOS_CREATEPROMPT = 0x2000,\n\t\t\t\tFOS_SHAREAWARE = 0x4000,\n\t\t\t\tFOS_NOREADONLYRETURN = 0x8000,\n\t\t\t\tFOS_NOTESTFILECREATE = 0x10000,\n\t\t\t\tFOS_HIDEMRUPLACES = 0x20000,\n\t\t\t\tFOS_HIDEPINNEDPLACES = 0x40000,\n\t\t\t\tFOS_NODEREFERENCELINKS = 0x100000,\n\t\t\t\tFOS_OKBUTTONNEEDSINTERACTION = 0x200000,\n\t\t\t\tFOS_DONTADDTORECENT = 0x2000000,\n\t\t\t\tFOS_FORCESHOWHIDDEN = 0x10000000,\n\t\t\t\tFOS_DEFAULTNOMINIMODE = 0x20000000,\n\t\t\t\tFOS_FORCEPREVIEWPANEON = 0x40000000,\n\t\t\t\tFOS_SUPPORTSTREAMABLEITEMS = 0x80000000\n\t\t\t}\n\n\t\t\t[DllImport(\"shell32.dll\", PreserveSig = true)]\n\t\t\tinternal static extern int SHCreateItemFromParsingName(string pszPath, IntPtr pbc, in Guid riid, out IShellItem ppv);\n\n\t\t\t[DllImport(\"shell32.dll\", PreserveSig = true)]\n\t\t\tinternal static extern int SHCreateItemFromIDList(IntPtr pidl, in Guid riid, out IShellItem ppv);\n\t\t}\n\t}\n}\n\nnamespace Au.Types {\n\t/// <summary>\n\t/// <see cref=\"FileOpenSaveDialog.CommonFlags\"/>.\n\t/// </summary>\n\t[Flags]\n\tpublic enum FOSFlags : uint //the values are like in FOS, some xored\n\t{\n\t\t/// <summary>Add the selected file to recent documents. See API <ms>SHAddToRecentDocs</ms>.</summary>\n\t\tAddToRecent = 0x2000000,\n\n\t\t/// <summary>Do not check for situations that would prevent an application from opening the selected file, such as sharing violations or access denied errors.</summary>\n\t\tNoValidateAccess = 0x100,\n\n\t\t///// <summary>The user can enter a path that does not exist, like <c>C:\\does not exist\\file.txt</c>.</summary>\n\t\t//NoValidatePath = 0x800, //does not work. Always validates.\n\n\t\t/// <summary>Shortcuts should not be treated as their target items, allowing an application to open a <c>.lnk</c> file.</summary>\n\t\tNoDereferenceLinks = 0x100000,\n\n\t\t/// <summary>Show hidden and system items.</summary>\n\t\tShowHidden = 0x10000000,\n\t}\n}\n"
  },
  {
    "path": "Au/Au.More/GdiTextRenderer.cs",
    "content": "//FUTURE: support columns. Maybe use \\t of specified widths.\n\nnamespace Au.More;\n#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member\n\n/// <summary>\n/// Draws text using fastest GDI API such as <ms>TextOut</ms> and standard UI font.\n/// Can easily draw string parts with different colors/styles without measuring.\n/// Must be disposed.\n/// </summary>\npublic unsafe class GdiTextRenderer : IDisposable {\n\tIntPtr _dc, _oldFont;\n\tuint _oldAlign;\n\tint _color, _oldColor;\n\tint _dpi;\n\tbool _releaseDC;\n\t\n\t/// <summary>Object created with this ctor can draw and measure.</summary>\n\t/// <param name=\"hdc\">Device context handle. <c>Dispose</c> will not release it.</param>\n\t/// <param name=\"dpi\"></param>\n\tpublic GdiTextRenderer(IntPtr hdc, int dpi) {\n\t\t_dpi = dpi;\n\t\t_dc = hdc;\n\t\t_oldFont = Api.SelectObject(_dc, NativeFont_.RegularCached(_dpi));\n\t\t_oldAlign = 0xffffffff;\n\t\tApi.SetBkMode(_dc, 1);\n\t\t_oldColor = Api.SetTextColor(_dc, _color = 0);\n\t}\n\t\n\t/// <summary>Object created with this ctor can measure only. Uses screen DC.</summary>\n\tpublic GdiTextRenderer(int dpi) {\n\t\t_dpi = dpi;\n\t\t_releaseDC = true;\n\t\t_dc = Api.GetDC(default);\n\t\t_oldFont = Api.SelectObject(_dc, NativeFont_.RegularCached(_dpi));\n\t}\n\t\n\tpublic void Dispose() {\n\t\tif (_dc != default) {\n\t\t\tApi.SelectObject(_dc, _oldFont);\n\t\t\tif (_releaseDC) Api.ReleaseDC(default, _dc);\n\t\t\telse {\n\t\t\t\tif (_oldAlign != 0xffffffff) Api.SetTextAlign(_dc, _oldAlign);\n\t\t\t\tif (_oldColor != _color) Api.SetTextColor(_dc, _oldColor);\n\t\t\t}\n\t\t\t_dc = default;\n\t\t}\n\t\t//never mind: we don't restore the old current position. Nobody later would draw at current position without movetoex. As well as bkmode.\n\t}\n\t\n\t/// <summary>\n\t/// Sets the current drawing position of the DC.\n\t/// Returns previous position.\n\t/// </summary>\n\tpublic POINT MoveTo(int x, int y) {\n\t\tApi.MoveToEx(_dc, x, y, out var p);\n\t\treturn p;\n\t}\n\t\n\t/// <summary>\n\t/// Gets the current drawing position of the DC.\n\t/// </summary>\n\tpublic POINT GetCurrentPosition() {\n\t\tApi.GetCurrentPositionEx(_dc, out var p);\n\t\treturn p;\n\t}\n\t\n\t/// <summary>\n\t/// Sets non-bold font.\n\t/// </summary>\n\tpublic void FontNormal() => Api.SelectObject(_dc, NativeFont_.RegularCached(_dpi));\n\t\n\t/// <summary>\n\t/// Sets bold font.\n\t/// </summary>\n\tpublic void FontBold() => Api.SelectObject(_dc, NativeFont_.BoldCached(_dpi));\n\t\n\t//public void FontItalic() => Api.SelectObject(_dc, NativeFont_.ItalicCached(_dpi));\n\t\n\t//public void FontBoldItalic() => Api.SelectObject(_dc, NativeFont_.BoldItalicCached(_dpi));\n\t\n\t/// <summary>\n\t/// Draws text at the current drawing position of the DC, and updates it.\n\t/// </summary>\n\t/// <param name=\"color\">Text color <c>0xBBGGRR</c>.</param>\n\t/// <param name=\"backColor\">Background color <c>0xBBGGRR</c>. Transparent if <c>null</c>.</param>\n\tpublic void DrawText(string s, int color = 0, Range? range = null, int? backColor = null) {\n\t\tvar (from, len) = range.GetOffsetAndLength(s.Lenn()); if (len == 0) return;\n\t\tif (_oldAlign == 0xffffffff) _oldAlign = Api.SetTextAlign(_dc, 1); //TA_UPDATECP\n\t\t_DrawText(s, 0, 0, color, from, len, backColor);\n\t}\n\t\n\t/// <summary>\n\t/// Draws text at specified position. Does not use/update the current drawing position of the DC.\n\t/// </summary>\n\t/// <param name=\"color\">Text color <c>0xBBGGRR</c>.</param>\n\t/// <param name=\"backColor\">Background color <c>0xBBGGRR</c>. Transparent if <c>null</c>.</param>\n\tpublic void DrawText(string s, POINT p, int color = 0, Range? range = null, int? backColor = null) {\n\t\tvar (from, len) = range.GetOffsetAndLength(s.Lenn()); if (len == 0) return;\n\t\tif (_oldAlign != 0xffffffff) { Api.SetTextAlign(_dc, _oldAlign); _oldAlign = 0xffffffff; }\n\t\t_DrawText(s, p.x, p.y, color, from, len, backColor);\n\t}\n\t\n\t/// <summary>\n\t/// Draws text clipped in specified rectangle. Does not use/update the current drawing position of the DC.\n\t/// </summary>\n\t/// <param name=\"color\">Text color <c>0xBBGGRR</c>.</param>\n\t/// <param name=\"backColor\">Background color <c>0xBBGGRR</c>. Transparent if <c>null</c>.</param>\n\tpublic void DrawText(string s, in RECT r, int color = 0, Range? range = null, int? backColor = null) {\n\t\tvar (from, len) = range.GetOffsetAndLength(s.Lenn()); if (len == 0) return;\n\t\tif (_oldAlign != 0xffffffff) { Api.SetTextAlign(_dc, _oldAlign); _oldAlign = 0xffffffff; }\n\t\t_DrawText(s, r, color, from, len, backColor);\n\t}\n\t\n\t//[Ext]TextOut fails if >= 1024 * 64.\n\t//\tOn Win7 and 8.1 depends on text width (ushort.MaxValue?). Eg fails if > ~2900 'W'.\n\t//\tExtTextOut doc: \"This value may not exceed 8192.\".\n\tstatic int _LimitLength(int len) => Math.Min(len, osVersion.minWin10 ? 1024 * 64 - 1 : 4000);\n\t\n\tunsafe void _DrawText(string s, int x, int y, int color, int from, int len, int? backColor) {\n\t\tif (color != _color) Api.SetTextColor(_dc, _color = color);\n\t\tusing var bc = new _BackColor(_dc, backColor);\n\t\tfixed (char* p = s) Api.TextOut(_dc, x, y, p + from, _LimitLength(len));\n\t}\n\t\n\tunsafe void _DrawText(string s, in RECT r, int color, int from, int len, int? backColor) {\n\t\tif (color != _color) Api.SetTextColor(_dc, _color = color);\n\t\tusing var bc = new _BackColor(_dc, backColor);\n\t\tfixed (char* p = s) Api.ExtTextOut(_dc, r.left, r.top, Api.ETO_CLIPPED, r, p + from, _LimitLength(len));\n\t}\n\t\n\tpublic SIZE MeasureText(string s, Range? range = null) {\n\t\tvar (from, len) = range.GetOffsetAndLength(s.Lenn()); if (len == 0) return default;\n\t\tfixed (char* p = s) {\n\t\t\tApi.GetTextExtentPoint32(_dc, p + from, _LimitLength(len), out var z);\n\t\t\treturn z;\n\t\t}\n\t}\n\t\n\tref struct _BackColor {\n\t\tint _oldColor, _oldMode;\n\t\tIntPtr _dc;\n\t\t\n\t\tpublic _BackColor(IntPtr dc, int? color) {\n\t\t\tif (color != null) {\n\t\t\t\t_dc = dc;\n\t\t\t\t_oldMode = Api.SetBkMode(_dc, 2);\n\t\t\t\t_oldColor = Api.SetBkColor(_dc, color.Value);\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic void Dispose() {\n\t\t\tif (_dc != default) {\n\t\t\t\tApi.SetBkColor(_dc, _oldColor);\n\t\t\t\tApi.SetBkMode(_dc, _oldMode);\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au/Au.More/Hash.cs",
    "content": "using System.Security.Cryptography;\nusing System.Buffers.Text;\n\nnamespace Au.More;\n\n/// <summary>\n/// Data hash functions.\n/// </summary>\n[EditorBrowsable(EditorBrowsableState.Never)] //obsolete as public, but still used internally. Created before the easy and fast .NET hash classes existed.\npublic static unsafe class Hash {\n\t#region FNV1\n\t\n\t/// <summary>\n\t/// 32-bit FNV-1 hash.\n\t/// Useful for fast hash table and checksum use, not cryptography. Similar to CRC32; faster but creates more collisions.\n\t/// </summary>\n\tpublic static int Fnv1(RStr data) {\n\t\tif (data.IsNull()) return 0;\n\t\t\n\t\tuint hash = 2166136261;\n\t\tfor (int i = 0; i < data.Length; i++)\n\t\t\thash = (hash * 16777619) ^ data[i];\n\t\treturn (int)hash;\n\t}\n\t\n\t/// <inheritdoc cref=\"Fnv1(RStr)\"/>\n\tpublic static int Fnv1(char* data, int lengthChars) => Fnv1(new RStr(data, lengthChars));\n\t\n\t/// <param name=\"data\">Data. See also: <see cref=\"MemoryMarshal.AsBytes\"/>, <see cref=\"CollectionsMarshal.AsSpan\"/>.</param>\n\t/// <inheritdoc cref=\"Fnv1(RStr)\"/>\n\tpublic static int Fnv1(RByte data) {\n\t\tif (data.IsNull()) return 0;\n\t\t\n\t\tuint hash = 2166136261;\n\t\tfor (int i = 0; i < data.Length; i++)\n\t\t\thash = (hash * 16777619) ^ data[i];\n\t\treturn (int)hash;\n\t}\n\t\n\t/// <inheritdoc cref=\"Fnv1(RStr)\"/>\n\tpublic static int Fnv1(byte* data, int lengthBytes) => Fnv1(new RByte(data, lengthBytes));\n\t\n\t/// <inheritdoc cref=\"Fnv1(RStr)\"/>\n\tpublic static int Fnv1<T>(T data) where T : unmanaged\n\t\t\t=> Fnv1((byte*)&data, sizeof(T));\n\t\n\t/// <summary>\n\t/// 64-bit FNV-1 hash.\n\t/// </summary>\n\tpublic static long Fnv1Long(RStr data) {\n\t\tif (data.IsNull()) return 0;\n\t\t\n\t\tulong hash = 14695981039346656037;\n\t\tfor (int i = 0; i < data.Length; i++)\n\t\t\thash = (hash * 1099511628211) ^ data[i];\n\t\treturn (long)hash;\n\t}\n\t\n\t/// <summary>\n\t/// 64-bit FNV-1 hash.\n\t/// </summary>\n\tpublic static long Fnv1Long(char* data, int lengthChars) => Fnv1(new RStr(data, lengthChars));\n\t\n\t/// <summary>\n\t/// 64-bit FNV-1 hash.\n\t/// </summary>\n\t/// <param name=\"data\">Data. See also: <see cref=\"MemoryMarshal.AsBytes\"/>, <see cref=\"CollectionsMarshal.AsSpan\"/>.</param>\n\tpublic static long Fnv1Long(RByte data) {\n\t\tif (data.IsNull()) return 0;\n\t\t\n\t\tulong hash = 14695981039346656037;\n\t\tfor (int i = 0; i < data.Length; i++)\n\t\t\thash = (hash * 1099511628211) ^ data[i];\n\t\treturn (long)hash;\n\t}\n\t\n\t/// <summary>\n\t/// 64-bit FNV-1 hash.\n\t/// </summary>\n\tpublic static long Fnv1Long(byte* data, int lengthBytes) => Fnv1(new RByte(data, lengthBytes));\n\t\n\t/// <summary>\n\t/// 64-bit FNV-1 hash.\n\t/// </summary>\n\tpublic static long Fnv1Long<T>(T data) where T : unmanaged\n\t\t\t=> Fnv1Long((byte*)&data, sizeof(T));\n\t\n\t/// <summary>\n\t/// FNV-1 hash, modified to make faster with long strings (then takes every n-th character).\n\t/// </summary>\n\t[Obsolete] //unused\n\tpublic static int Fast(char* data, int lengthChars) {\n\t\tif (data == null) return 0;\n\t\t\n\t\t//Also we take the last 1-2 characters (in the second loop), because often there are several strings like Chrome_WidgetWin_0, Chrome_WidgetWin_1...\n\t\t//Also we hash uints, not chars, unless the string is very short.\n\t\t//Tested speed with 400 unique strings (window/control names/classnames/programnames). The time was 7 mcs. For single call 17 ns.\n\t\t\n\t\tuint hash = 2166136261;\n\t\tint i = 0;\n\t\t\n\t\tif (lengthChars > 8) {\n\t\t\tint lc = lengthChars--;\n\t\t\tlengthChars /= 2; //we'll has uints, not chars\n\t\t\tint every = lengthChars / 8 + 1;\n\t\t\t\n\t\t\tfor (; i < lengthChars; i += every)\n\t\t\t\thash = (hash * 16777619) ^ ((uint*)data)[i];\n\t\t\t\n\t\t\ti = lengthChars * 2;\n\t\t\tlengthChars = lc;\n\t\t}\n\t\t\n\t\tfor (; i < lengthChars; i++)\n\t\t\thash = (hash * 16777619) ^ data[i];\n\t\t\n\t\treturn (int)hash;\n\t}\n\t\n\t/// <summary>\n\t/// FNV-1 hash, modified to make faster with long strings (then takes every n-th character).\n\t/// </summary>\n\t/// <param name=\"s\">String. Can be <c>null</c>.</param>\n\t[Obsolete] //unused\n\tpublic static int Fast(RStr s) {\n\t\tfixed (char* p = s) return Fast(p, s.Length);\n\t}\n\t\n\t#endregion\n\t\n\t#region MD5\n\t\n\t/// <summary>\n\t/// Computes MD5 hash of data.\n\t/// Call an <see cref=\"Add\"/> method one or more times. Finally use <see cref=\"Hash\"/> to get result.\n\t/// </summary>\n\t[StructLayout(LayoutKind.Explicit)]\n\tpublic struct MD5Context { //MD5_CTX + _state\n\t\t[FieldOffset(88)] MD5Result _result;\n\t\t[FieldOffset(104)] long _state; //1 inited/added, 2 finalled\n\t\t\n\t\t/// <summary>\n\t\t/// <c>true</c> if no data was added.\n\t\t/// </summary>\n\t\tpublic bool IsEmpty => _state == 0;\n\t\t\n\t\t/// <summary>Adds data.</summary>\n\t\t/// <exception cref=\"ArgumentOutOfRangeException\"><i>size</i> &lt; 0.</exception>\n\t\t/// <exception cref=\"ArgumentNullException\"><i>data</i> is <c>null</c> and <i>size</i> > 0.</exception>\n\t\tpublic void Add(void* data, int size) {\n\t\t\tif (size < 0) throw new ArgumentOutOfRangeException();\n\t\t\tif (size > 0) Not_.Null(data); //allow null if size 0. Eg 'fixed' gets null pointer if the span or array is empty.\n\t\t\tif (_state != 1) { Api.MD5Init(out this); _state = 1; }\n\t\t\tif (size > 0) Api.MD5Update(ref this, data, size);\n\t\t}\n\t\t\n\t\t/// <summary>Adds data.</summary>\n\t\tpublic void Add<T>(T data) where T : unmanaged\n\t\t\t=> Add(&data, sizeof(T));\n\t\t\n\t\t/// <summary>Adds data.</summary>\n\t\t/// <param name=\"data\">Data. See also: <see cref=\"MemoryMarshal.AsBytes\"/>, <see cref=\"CollectionsMarshal.AsSpan\"/>.</param>\n\t\tpublic void Add(RByte data) {\n\t\t\tfixed (byte* p = data) Add(p, data.Length); //note: p null if data empty\n\t\t}\n\t\t\n\t\t/// <summary>Adds string converted to UTF-8.</summary>\n\t\t/// <exception cref=\"ArgumentNullException\"><i>data</i> is <c>null</c>.</exception>\n\t\tpublic void Add(string data) => Add(Encoding.UTF8.GetBytes(data));\n\t\t\n\t\t//CONSIDER: alloc on stack to avoid garbage. This func works, but not faster.\n\t\t//[SkipLocalsInit]\n\t\t//public void Add2(string data) {\n\t\t//\tif (data.Length < 3000) {\n\t\t//\t\tSpan<byte> p = stackalloc byte[data.Length * 3];\n\t\t//\t\tint n = Encoding.UTF8.GetBytes(data, p);\n\t\t//\t\t//print.it(n);\n\t\t//\t\tAdd(p[..n]);\n\t\t//\t} else {\n\t\t//\t\tAdd(Encoding.UTF8.GetBytes(data));\n\t\t//\t}\n\t\t//}\n\t\t\n\t\t//rejected. Better use unsafe address, then will not need to copy data.\n\t\t///// <summary>Adds data.</summary>\n\t\t//public void Add<T>(T data) where T: unmanaged\n\t\t//{\n\t\t//\tAdd(&data, sizeof(T));\n\t\t//}\n\t\t\n\t\t/// <summary>\n\t\t/// Computes final hash of datas added with an <see cref=\"Add\"/> method.\n\t\t/// </summary>\n\t\t/// <exception cref=\"InvalidOperationException\"><c>Add</c> was not called.</exception>\n\t\t/// <remarks>\n\t\t/// Resets state, so that if <c>Add</c> called again, it will start adding new datas.\n\t\t/// </remarks>\n\t\tpublic MD5Result Hash {\n\t\t\tget {\n\t\t\t\tif (_state != 2) {\n\t\t\t\t\tif (_state != 1) throw new InvalidOperationException();\n\t\t\t\t\tApi.MD5Final(ref this);\n\t\t\t\t\t_state = 2;\n\t\t\t\t}\n\t\t\t\treturn _result;\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Result of <see cref=\"MD5Context.Hash\"/>.\n\t/// It is 16 bytes stored in 2 <c>long</c> fields <c>r1</c> and <c>r2</c>.\n\t/// If need, can be converted to <c>byte[]</c> with <see cref=\"MD5Result.ToArray\"/> or to hex string with <see cref=\"MD5Result.ToString\"/>.\n\t/// </summary>\n\tpublic record struct MD5Result {\n#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member\n\t\tpublic readonly long r1, r2;\n\t\t\n\t\tpublic MD5Result(long r1, long r2) { this.r1 = r1; this.r2 = r2; }\n#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member\n\t\t\n\t\t/// <summary>\n\t\t/// Converts this to <c>byte[]</c> of length 16.\n\t\t/// </summary>\n\t\tpublic byte[] ToArray() {\n\t\t\tvar r = new byte[16];\n\t\t\tfixed (byte* p = r) {\n\t\t\t\t*(long*)p = r1;\n\t\t\t\t*(long*)(p + 8) = r2;\n\t\t\t}\n\t\t\treturn r;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Converts this to hex string.\n\t\t/// </summary>\n\t\tpublic override string ToString() => Convert2.HexEncode(this);\n\t\t\n\t\t/// <summary>\n\t\t/// Creates <see cref=\"MD5Result\"/> from hex string returned by <see cref=\"ToString\"/>.\n\t\t/// </summary>\n\t\t/// <returns><c>false</c> if <i>encoded</i> is invalid.</returns>\n\t\tpublic static bool FromString(RStr encoded, out MD5Result r) => Convert2.HexDecode(encoded, out r);\n\t\t\n#if NET9_0_OR_GREATER\n\t\t/// <summary>\n\t\t/// Converts this to Base64url string (URL-safe Base64).\n\t\t/// </summary>\n\t\tpublic string ToStringBase64Url() => Base64Url.EncodeToString(_AsSpan());\n\t\t\n\t\t/// <summary>\n\t\t/// Creates <see cref=\"MD5Result\"/> from string returned by <see cref=\"ToStringBase64Url\"/>.\n\t\t/// </summary>\n\t\tpublic static bool FromStringBase64Url(RStr encoded, out MD5Result r) {\n\t\t\tr = default;\n\t\t\treturn Base64Url.TryDecodeFromChars(encoded, r._AsSpan(), out _);\n\t\t}\n\t\t\n\t\tSpan<byte> _AsSpan() => MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref this, 1));\n#endif\n\t}\n\t\n\t/// <summary>\n\t/// Computes MD5 hash of data.\n\t/// Uses <see cref=\"MD5Context\"/>.\n\t/// </summary>\n\t/// <param name=\"data\">Data. See also: <see cref=\"MemoryMarshal.AsBytes\"/>, <see cref=\"CollectionsMarshal.AsSpan\"/>.</param>\n\tpublic static MD5Result MD5(RByte data) {\n\t\tMD5Context md = default;\n\t\tmd.Add(data);\n\t\treturn md.Hash;\n\t}\n\t\n\t//rejected. Problems with overload resolution and implicit conversion Span to ReadOnlySpan. Not so often used. Then would need the same everywhere. Instead added doc.\n\t///// <summary>\n\t///// Computes MD5 hash of data.\n\t///// Uses <see cref=\"MD5Context\"/>.\n\t///// </summary>\n\t//public static MD5Result MD5<T>(ReadOnlySpan<T> data) where T : unmanaged\n\t//\t=> MD5(MemoryMarshal.AsBytes(data));\n\t\n\t/// <summary>\n\t/// Computes MD5 hash of string converted to UTF-8.\n\t/// Uses <see cref=\"MD5Context\"/>.\n\t/// </summary>\n\tpublic static MD5Result MD5(string data) {\n\t\tMD5Context md = default;\n\t\tmd.Add(data);\n\t\treturn md.Hash;\n\t}\n\t\n\t/// <summary>\n\t/// Computes MD5 hash of data. Returns result as hex or base64 string.\n\t/// Uses <see cref=\"MD5Context\"/>.\n\t/// </summary>\n\t/// <param name=\"data\">Data. See also: <see cref=\"MemoryMarshal.AsBytes\"/>, <see cref=\"CollectionsMarshal.AsSpan\"/>.</param>\n\t/// <param name=\"base64\"></param>\n\tpublic static string MD5(RByte data, bool base64) {\n\t\tvar h = MD5(data);\n\t\treturn base64 ? Convert.ToBase64String(new RByte((byte*)&h, 16)) : h.ToString();\n\t}\n\t\n\t///// <summary>\n\t///// Computes MD5 hash of data. Returns result as hex or base64 string.\n\t///// Uses <see cref=\"MD5Context\"/>.\n\t///// </summary>\n\t//public static string MD5<T>(ReadOnlySpan<T> data, bool base64) where T : unmanaged\n\t//\t=> MD5(MemoryMarshal.AsBytes(data), base64);\n\t\n\t/// <summary>\n\t/// Computes MD5 hash of string converted to UTF-8. Returns result as hex or base64 string.\n\t/// Uses <see cref=\"MD5Context\"/>.\n\t/// </summary>\n\tpublic static string MD5(string data, bool base64) {\n\t\tvar h = MD5(data);\n\t\treturn base64 ? Convert.ToBase64String(new RByte((byte*)&h, 16)) : h.ToString();\n\t}\n\t\n\t#endregion\n\t\n\t#region other\n\t\n\t/// <summary>\n\t/// Computes data hash using the specified cryptographic algorithm.\n\t/// </summary>\n\t/// <param name=\"data\">Data. See also: <see cref=\"MemoryMarshal.AsBytes\"/>, <see cref=\"CollectionsMarshal.AsSpan\"/>.</param>\n\t/// <param name=\"algorithm\">Algorithm name, eg <c>\"SHA256\"</c>. See <see cref=\"CryptoConfig\"/>.</param>\n\tpublic static byte[] Crypto(RByte data, string algorithm) {\n\t\tusing var x = (HashAlgorithm)CryptoConfig.CreateFromName(algorithm);\n\t\tvar r = new byte[x.HashSize / 8];\n\t\tx.TryComputeHash(data, r, out _);\n\t\treturn r;\n\t}\n\t\n\t/// <summary>\n\t/// Computes hash of string converted to UTF-8, using the specified cryptographic algorithm.\n\t/// </summary>\n\t/// <param name=\"data\"></param>\n\t/// <param name=\"algorithm\">Algorithm name, eg <c>\"SHA256\"</c>. See <see cref=\"CryptoConfig\"/>.</param>\n\tpublic static byte[] Crypto(string data, string algorithm)\n\t\t=> Crypto(Encoding.UTF8.GetBytes(data), algorithm);\n\t\n\t/// <summary>\n\t/// Computes data hash using the specified cryptographic algorithm. Returns result as hex or base64 string.\n\t/// </summary>\n\t/// <param name=\"data\">Data. See also: <see cref=\"MemoryMarshal.AsBytes\"/>, <see cref=\"CollectionsMarshal.AsSpan\"/>.</param>\n\t/// <param name=\"algorithm\">Algorithm name, eg <c>\"SHA256\"</c>. See <see cref=\"CryptoConfig\"/>.</param>\n\t/// <param name=\"base64\"></param>\n\tpublic static string Crypto(RByte data, string algorithm, bool base64) {\n\t\tvar b = Crypto(data, algorithm);\n\t\treturn base64 ? Convert.ToBase64String(b) : Convert2.HexEncode(b);\n\t}\n\t\n\t/// <summary>\n\t/// Computes hash of string converted to UTF-8, using the specified cryptographic algorithm. Returns result as hex or base64 string.\n\t/// </summary>\n\t/// <param name=\"data\"></param>\n\t/// <param name=\"algorithm\">Algorithm name, eg <c>\"SHA256\"</c>. See <see cref=\"CryptoConfig\"/>.</param>\n\t/// <param name=\"base64\"></param>\n\tpublic static string Crypto(string data, string algorithm, bool base64) {\n\t\tvar b = Crypto(data, algorithm);\n\t\treturn base64 ? Convert.ToBase64String(b) : Convert2.HexEncode(b);\n\t}\n\t\n\t#endregion\n\t\n}\n"
  },
  {
    "path": "Au/Au.More/HelpUtil.cs",
    "content": "namespace Au.More;\n\n/// <summary>\n/// Static functions to open a help topic etc.\n/// </summary>\npublic static class HelpUtil {\n\t/// <summary>\n\t/// Opens a LibreAutomate help topic.\n\t/// </summary>\n\t/// <param name=\"topic\">Topic file name, like <c>\"wnd.find\"</c> or <c>\"Au.Types.RECT\"</c> or <c>\"articles/Wildcard expression\"</c>.</param>\n\tpublic static void AuHelp(string topic) {\n#pragma warning disable CS0612 //Type or member is obsolete\n\t\tvar url = AuHelpUrl(topic);\n#pragma warning restore CS0612 //Type or member is obsolete\n\t\tif (AuHelpEvent_ is { } e) {\n\t\t\tvar k = new AuHelpEventArgs_ { Url = url };\n\t\t\te(k);\n\t\t\tif (k.Cancel) return;\n\t\t\turl = k.Url;\n\t\t}\n\t\trun.itSafe(url);\n\t}\n\t\n\t/// <summary>\n\t/// Gets URL of a LibreAutomate help topic.\n\t/// </summary>\n\t/// <param name=\"topic\">Topic file name, like <c>\"wnd.find\"</c> or <c>\"Au.Types.RECT\"</c> or <c>\"articles/Wildcard expression\"</c>.</param>\n\tpublic static string AuHelpUrl(string topic) {\n\t\tstring fragment = null;\n\t\tint i = topic.IndexOf('#');\n\t\tif (i >= 0) {\n\t\t\tfragment = topic[i..];\n\t\t\ttopic = topic[..i];\n\t\t}\n\t\t\n\t\tif (topic.Ends(\".this[]\")) topic = topic.ReplaceAt(^7.., \".Item\");\n\t\telse if (topic.Ends(\".this\")) topic = topic.ReplaceAt(^5.., \".Item\");\n\t\telse if (topic.Ends(\"[]\")) topic = topic.ReplaceAt(^2.., \".Item\");\n\t\telse if (topic.Contains(\"timer\")) topic = topic.RxReplace(@\"\\btimer2?\\.(after|every)\\b\\K\", \"_1\"); //the filename has this suffix because of the instance method After/Every\n\t\t\n\t\tvar url = AuHelpBaseUrl;\n\t\tif (!url.Ends('/')) url += \"/\";\n\t\tif (!topic.NE()) url = url\n\t\t\t\t+ (topic.Contains('/') ? null : (topic.Starts(\"Au.\") ? \"api/\" : \"api/Au.\"))\n\t\t\t\t+ topic\n\t\t\t\t+ (topic.Ends(\".html\") || topic.Ends('/') ? null : \".html\")\n\t\t\t\t+ fragment;\n\t\treturn url;\n\t}\n\t\n\t/// <summary>\n\t/// <c>s.Starts(false, \"Au.\", \"articles/\", \"editor/\", \"cookbook/\", \"api/\") > 0</c>\n\t/// </summary>\n\tinternal static bool IsAuHelp_(string s) => s.Starts(false, \"Au.\", \"articles/\", \"editor/\", \"cookbook/\", \"api/\") > 0;\n\t\n\t/// <summary>\n\t/// URL of the LibreAutomate documentation website: <c>\"https://www.libreautomate.com/\"</c>.\n\t/// </summary>\n\tpublic static string AuHelpBaseUrl => \"https://www.libreautomate.com/\";\n\n\tinternal static event Action<AuHelpEventArgs_> AuHelpEvent_;\n\t\n\tinternal class AuHelpEventArgs_ : CancelEventArgs {\n\t\tpublic string Url { get; set; }\n\t}\n}\n"
  },
  {
    "path": "Au/Au.More/HttpServerSession.cs",
    "content": "using System.Net;\nusing System.Net.Sockets;\nusing System.Text.Json;\n\nnamespace Au.More {\n\t/// <summary>\n\t/// Simple HTTP 1.1 server. Can be used for communicating between two scripts or apps on local network, internet or same computer. Also can receive requests from web browsers etc.\n\t/// </summary>\n\t/// <remarks>\n\t/// To receive HTTP messages, create a class with base <c>HttpServerSession</c> and add function <see cref=\"MessageReceived\"/>. Example in <see cref=\"Listen\"/>.\n\t/// </remarks>\n\tpublic abstract class HttpServerSession {\n\t\t/// <summary>\n\t\t/// Simple HTTP 1.1 server.\n\t\t/// </summary>\n\t\t/// <param name=\"port\">TCP port. Default 4455. If 0, uses the first available port in the dynamic/private ports range (49152-65535); you can use the <i>started</i> callback to discover it.</param>\n\t\t/// <param name=\"ip\">A local IPv4 or IPv6 address, like <c>\"127.0.0.1\"</c> or <c>\"[::1]\"</c>. If <c>null</c> (default), uses <see cref=\"IPAddress.IPv6Any\"/> and dual mode (supports IPv6 and IPv4 connections).</param>\n\t\t/// <param name=\"started\">Called when the server started (after <see cref=\"TcpListener.Start()\"/>).</param>\n\t\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"TcpListener\"/> functions. Unlikely.</exception>\n\t\t/// <remarks>\n\t\t/// Runs all the time and listens for new TCP client connections. For each connected client starts new thread, creates new object of your <see cref=\"HttpServerSession\"/>-based type, and calls <see cref=\"Run\"/>, which calls <see cref=\"MessageReceived\"/>. Supports keep-alive. Multiple sessions can run simultaneously.\n\t\t///\n\t\t///\tUses <see cref=\"TcpListener\"/>, not <see cref=\"HttpListener\"/>, therefore don't need administrator privileges, <c>netsh</c> and opening ports in firewall. Just the standard firewall dialog first time.\n\t\t///\n\t\t/// The HTTP server is accessible from local network computers. Usually not accessible from the internet. To make accessible from the internet, you can use ngrok or similar software. This server does not support https (secure connections), but ngrok makes internet connections secure.\n\t\t/// \n\t\t/// The Windows Firewall shows a dialog when the app uses a HTTP server the first time, unless <i>ip</i> is <c>\"127.0.0.1\"</c>.\n\t\t/// </remarks>\n\t\t/// <example>\n\t\t/// HTTP server.\n\t\t/// <code><![CDATA[\n\t\t/// HttpServerSession.Listen<MyHttpSession>();\n\t\t/// \n\t\t/// class MyHttpSession : HttpServerSession {\n\t\t/// \t//protected override void Run() {\n\t\t/// \t//\tprint.it($\"Session started. Client IP: {ClientIP}\");\n\t\t/// \t//\tbase.Run();\n\t\t/// \t//\tprint.it(\"Session ended\");\n\t\t/// \t//}\n\t\t/// \t\n\t\t/// \tprotected override void MessageReceived(HSMessage m, HSResponse r) {\n\t\t/// \t\t//Auth((u, p) => p == \"password206474\");\n\t\t/// \t\t\n\t\t/// \t\tprint.it(m.Method, m.TargetPath, m.UrlParameters, m.Headers, m.ContentText);\n\t\t/// \t\t\n\t\t/// \t\tstring response = \"RESPONSE\";\n\t\t/// \t\tr.SetContent(response);\n\t\t/// \t\t//or\n\t\t/// \t\t//r.Content = response.ToUTF8();\n\t\t/// \t\t//r.Headers.Add(\"Content-Type\", \"text/plain; charset=utf-8\");\n\t\t/// \t}\n\t\t/// }\n\t\t/// ]]></code>\n\t\t/// HTTP client.\n\t\t/// <code><![CDATA[\n\t\t/// var res = internet.http.Get(\"http://127.0.0.1:4455/file.txt?a=1&b=2\"/*, auth: \"user:pw\"*/).Text(); //from same computer\n\t\t/// //var res = internet.http.Get(\"http://ComputerName:4455/file.txt?a=1&b=2\"/*, auth: \"user:pw\"*/).Text(); //from local network\n\t\t/// //var res = internet.http.Get(\"https://1111-11-111-11-111.ngrok-free.app/file.txt?a=1&b=2\"/*, auth: \"user:pw\"*/).Text(); //from internet through ngrok\n\t\t/// print.it(res);\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic static void Listen<TSession>(int port = 4455, string ip = null, Action<TcpListener> started = null) where TSession : HttpServerSession, new() {\n\t\t\tvar server = ip == null\n\t\t\t\t? TcpListener.Create(port) //IPAddress.IPv6Any, dual mode (supports IPv6 and IPv4 connections); shows Firewall popup.\n\t\t\t\t: new(IPAddress.Parse(ip), port); //no Firewall popup if \"127.0.0.1\" or \"[::1]\", but then no dual mode; `server.Server.DualMode=true` fails.\n\t\t\t\n\t\t\tserver.Start();\n\t\t\tstarted?.Invoke(server);\n\t\t\ttry {\n\t\t\t\tfor (; ; ) {\n\t\t\t\t\tvar client = server.AcceptTcpClient();\n\t\t\t\t\trun.thread(() => {\n\t\t\t\t\t\tnew TSession()._Run(client);\n\t\t\t\t\t\tInterlocked.Decrement(ref s_nThreads);\n\t\t\t\t\t}).Name = \"Au.HttpServerSession\";\n\t\t\t\t\tInterlocked.Increment(ref s_nThreads);\n\t\t\t\t\twhile (s_nThreads >= (osVersion.is32BitProcess ? 200 : 2000)) 200.ms();\n\t\t\t\t}\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tserver.Stop();\n\t\t\t}\n\t\t}\n\t\tstatic int s_nThreads;\n\t\t\n\t\tTcpClient _client;\n\t\tNetworkStream _ns;\n\t\tHSMessage _message; //current message\n\t\t\n\t\t/// <summary>\n\t\t/// Gets the <see cref=\"TcpClient\"/> object of this session.\n\t\t/// </summary>\n\t\tprotected TcpClient Client => _client;\n\t\t\n\t\t/// <summary>\n\t\t/// Gets the IP address of the client.\n\t\t/// </summary>\n\t\tprotected IPAddress ClientIP => ((System.Net.IPEndPoint)_client.Client.RemoteEndPoint).Address;\n\t\t\n\t\t/// <summary>\n\t\t/// Print warning when something fails. This is for debug.\n\t\t/// </summary>\n\t\tprotected bool Verbose { get; set; }\n\t\t\n\t\t/// <summary>\n\t\t/// Keep-alive timeout, in milliseconds. Default 10000.\n\t\t/// </summary>\n\t\tprotected int KeepAliveTimeout { get; set; } = 10_000;\n\t\t\n\t\t/// <summary>\n\t\t/// Called when the server receives a HTTP request message.\n\t\t/// </summary>\n\t\t/// <param name=\"m\">Contains request info and content.</param>\n\t\t/// <param name=\"r\">Allows to set response info and content.</param>\n\t\t/// <remarks>\n\t\t///\tNot called if failed to read the message.\n\t\t/// \n\t\t/// The server uses try/catch when calling this. Prints unhandled exceptions if <see cref=\"Verbose\"/> <c>true</c>. On unhandled exception sends error 500 (<c>InternalServerError</c>) and closes the connection.\n\t\t/// </remarks>\n\t\tprotected abstract void MessageReceived(HSMessage m, HSResponse r);\n\t\t\n\t\t/// <summary>\n\t\t/// Performs basic authentication. If fails (either the client did not use basic authentication or <i>auth</i> returned <c>false</c>), throws exception. The client will receive error 401 (Unauthorized) and can retry.\n\t\t/// </summary>\n\t\t/// <param name=\"auth\">Callback function. Receives the user name and password. Returns <c>true</c> to continue or <c>false</c> to fail.</param>\n\t\t/// <remarks>\n\t\t/// After successful authentication does not repeat it again when the client sends more messages in this session.\n\t\t/// </remarks>\n\t\t/// <example><see cref=\"HttpServerSession\"/></example>\n\t\tprotected void Auth(Func<string, string, bool> auth) {\n\t\t\tif (_auth != true) {\n\t\t\t\t_auth = _message.Headers.TryGetValue(\"Authorization\", out var s)\n\t\t\t\t\t&& s.Split2_(' ', out var s1, out var s2, 5, 1)\n\t\t\t\t\t&& s1.Eqi(\"Basic\")\n\t\t\t\t\t&& Convert.FromBase64String(s2).ToStringUTF8().Split2_(':', out s1, out s2, 0, 0)\n\t\t\t\t\t&& auth(s1, s2);\n\t\t\t\tif (_auth == false) throw new UnauthorizedAccessException();\n\t\t\t}\n\t\t}\n\t\tbool? _auth;\n\t\t\n\t\tvoid _Run(TcpClient client) {\n\t\t\t_client = client;\n\t\t\t_client.ReceiveTimeout = 30_000; //tested: throws when the socked receives 0 bytes in this time. It's not the total time of a Read.\n\t\t\t_client.SendTimeout = 30_000; //it seems this is how long the socket waits until previous chunk is sent. We send in max 64 KB chunks.\n\t\t\ttry { Run(); }\n\t\t\tcatch (Exception e1) {\n\t\t\t\tif (Verbose) print.warning(e1, \"HttpServerSession: \");\n\t\t\t}\n\t\t\t_client.Close();\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Executes the session: reads requests, calls your <see cref=\"MessageReceived\"/>, writes responses, implements keep-alive.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// The server uses try/catch when calling this. Prints unhandled exceptions if <see cref=\"Verbose\"/> <c>true</c>. On unhandled exception closes the connection.\n\t\t/// </remarks>\n\t\t[SkipLocalsInit]\n\t\tprotected virtual void Run() {\n\t\t\t_ns = _client.GetStream();\n\t\t\tg1:\n\t\t\tHttpStatusCode status;\n\t\t\ttry {\n\t\t\t\tstatus = _ReadRequest();\n\t\t\t}\n\t\t\tcatch (HttpReadException_ eh) {\n\t\t\t\tstatus = eh.status;\n\t\t\t}\n\t\t\tif (status == HttpReader_.Disconnected) return;\n\t\t\t\n\t\t\tHSResponse response = new() { Status = status };\n\t\t\t\n\t\t\tif (status == HttpStatusCode.OK) {\n\t\t\t\ttry { MessageReceived(_message, response); }\n\t\t\t\tcatch (UnauthorizedAccessException) when (_auth == false) {\n\t\t\t\t\t_auth = null;\n\t\t\t\t\tresponse.Status = HttpStatusCode.Unauthorized;\n\t\t\t\t\tresponse.Headers[\"WWW-Authenticate\"] = \"Basic\";\n\t\t\t\t}\n\t\t\t\tcatch (Exception e1) {\n\t\t\t\t\tif (Verbose) print.warning(e1, \"HttpServerSession: \");\n\t\t\t\t\tif (response.Status == HttpStatusCode.OK) response.Status = HttpStatusCode.InternalServerError;\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tbool keepAlive = KeepAliveTimeout > 0 && status == HttpStatusCode.OK && !(_message.Headers.TryGetValue(\"Connection\", out var v1) && v1.Eqi(\"close\"));\n\t\t\tif (!keepAlive) response.Headers[\"Connection\"] = \"close\";\n\t\t\t\n\t\t\t_WriteResponse(response);\n\t\t\t_message = null;\n\t\t\t\n\t\t\tif (keepAlive) {\n\t\t\t\tif (_ns.Socket.Poll(Math.Min(KeepAliveTimeout, int.MaxValue / 1000) * 1000, SelectMode.SelectRead) && _ns.DataAvailable) goto g1; //else timeout or closed or error\n\t\t\t} else if (status != HttpStatusCode.OK) {\n\t\t\t\t//Cannot close until the client writes all data and reads the response. Else instead of response it may receive error 'connection reset'.\n\t\t\t\t//\tOn error often not all request bytes are read. And the size of this message is unknown.\n\t\t\t\t//\tRead all remaining request bytes until the client closes connection.\n\t\t\t\t//\t\tClient should close when it reads the response, because: 1. It's an error. 2. We send 'Connection: close'.\n\t\t\t\t//\ttested: _ns.Socket.LingerState does not work.\n\t\t\t\t//\tnot tested (because this code works well): _ns.Close(timeout);.\n\t\t\t\t_client.ReceiveTimeout = 10_000; //if client never closes, we'll get timeout exception\n\t\t\t\tSpan<byte> b = stackalloc byte[0x4000];\n\t\t\t\twhile (_ns.Read(b) != 0) {  }\n\t\t\t}\n\t\t}\n\t\t\n\t\tvoid _WriteResponse(HSResponse r) {\n\t\t\tr.Headers.TryAdd(\"Date\", DateTime.UtcNow.ToString(\"R\"));\n\t\t\tr.Headers.TryAdd(\"Server\", \"Au\");\n\t\t\t\n\t\t\tbyte[] content = r.Content;\n\t\t\tbool hasContent = !content.NE_();\n\t\t\tif (hasContent) {\n\t\t\t\t//r.Headers.TryAdd(\"Content-Type\", \"Content-Type: text/plain; charset=utf-8\"); //no. Server cannot guess. The HTML default is application/octet-stream.\n\t\t\t\t\n\t\t\t\t//compress\n\t\t\t\tif (content.Length > 1000 && _message.Headers.TryGetValue(\"Accept-Encoding\", out var ae) && !r.Headers.ContainsKey(\"Content-Encoding\")) {\n\t\t\t\t\tvar h = ae.Lower().Split(new char[] { ',', ';' }, StringSplitOptions.TrimEntries); //use ';' to split items like \"br;q=1.0\", and ignore q\n\t\t\t\t\tvar (content2, s2) =\n\t\t\t\t\t\th.Contains(\"br\") ? (Convert2.BrotliCompress(content), \"br\")\n\t\t\t\t\t\t: h.Contains(\"gzip\") ? (Convert2.GzipCompress(content), \"gzip\")\n\t\t\t\t\t\t: h.Contains(\"deflate\") ? (Convert2.DeflateCompress(content), \"deflate\")\n\t\t\t\t\t\t: (content, null);\n\t\t\t\t\tif (content2.Length < content.Length) {\n\t\t\t\t\t\tcontent = content2;\n\t\t\t\t\t\tr.Headers[\"Content-Encoding\"] = s2;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tr.Headers[\"Content-Length\"] = content.Lenn_().ToS(); //if no content, set 0, else client may wait even if there is empty line\n\t\t\t\n\t\t\tint bs = 50 + r.Reason.Lenn() + r.Headers.Sum(o => o.Key.Length + o.Value.Length + 4);\n\t\t\tusing (var w = new StreamWriter(_ns, Encoding.Latin1, bs, leaveOpen: true)) {\n\t\t\t\tw.Write($\"HTTP/1.1 {(int)r.Status} {r.Status}\");\n\t\t\t\tif (!r.Reason.NE()) w.Write(\", \" + r.Reason);\n\t\t\t\tw.WriteLine();\n\t\t\t\t\n\t\t\t\tforeach (var (k, v) in r.Headers) w.WriteLine(k + \": \" + v);\n\t\t\t\tw.WriteLine();\n\t\t\t}\n\t\t\t\n\t\t\tif (hasContent) {\n\t\t\t\t//perf.first();\n\t\t\t\t//_ns.Write(content); //does not wait until client reads all data. Usually returns after ~1 ms regardless of size etc.\n\t\t\t\tfor (int i = 0; i < content.Length;) {\n\t\t\t\t\tint n = Math.Min(0x10000, content.Length - i);\n\t\t\t\t\t_ns.Write(content, i, n); //waits if previous buffer still not sent\n\t\t\t\t\ti += n;\n\t\t\t\t}\n\t\t\t\t//perf.nw();\n\t\t\t}\n\t\t}\n\t\t\n\t\t[SkipLocalsInit]\n\t\tunsafe HttpStatusCode _ReadRequest() {\n\t\t\t_message = new();\n\t\t\tHttpReader_ reader = new(_ns);\n\t\t\t\n\t\t\t//read the request line\n\t\t\t{\n\t\t\t\tstring s; do s = reader.ReadLine(); while (s.Length == 0);\n\t\t\t\tif (!s.RxMatch(@\"^(GET|HEAD|POST|PUT|DELETE|CONNECT|OPTIONS|TRACE|PATCH) (.+) HTTP/(\\d\\.\\d)$\", out RXMatch m)) return HttpStatusCode.BadRequest;\n\t\t\t\tif (m[3].Value is not (\"1.1\" or \"1.0\")) return HttpStatusCode.HttpVersionNotSupported;\n\t\t\t\t_message.Method = m[1].Value;\n\t\t\t\t_message.RawTarget = s = m[2].Value;\n\t\t\t\tint i = s.IndexOf('?');\n\t\t\t\tif (i >= 0) {\n\t\t\t\t\t_message.UrlParameters = HSMessage.ParseUrlParameters_(s.AsSpan(i + 1));\n\t\t\t\t\ts = s[..i];\n\t\t\t\t}\n\t\t\t\t_message.TargetPath = WebUtility.UrlDecode(s);\n\t\t\t}\n\t\t\t\n\t\t\t//read headers\n\t\t\tvar headers = _message.Headers;\n\t\t\treader.ReadHeaders(headers);\n\t\t\t\n\t\t\t//read content\n\t\t\t_message.Content = reader.ReadContent(headers);\n\t\t\t\n\t\t\t//print.it(\"Target\", message.RawTarget, message.TargetPath, message.UrlParameters);\n\t\t\t//print.it(\"Headers\", message.Headers);\n\t\t\t//print.it(\"Content\", message.ContentText);\n\t\t\t\n\t\t\treturn HttpStatusCode.OK;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Reads an HTTP request or response message.\n\t/// All functions may throw <see cref=\"HttpReadException_\"/> and exceptions of <c>NetworkStream.Read</c>.\n\t/// The class was designed to read request, therefore throws exceptions such as <c>BadRequest</c>, but can be used to read response too.\n\t/// Uses 16 KB of stack. Consider <c>[SkipLocalsInit]</c>.\n\t/// </summary>\n\tunsafe ref struct HttpReader_ {\n\t\treadonly NetworkStream _ns;\n\t\tSpan<byte> _b, _line;\n\t\tint _n; //current buffered bytes\n\t\tint _pos; //current position in _b (0.._n)\n\t\tconst int c_bsize = 0x4000; //16 K\n\t\tfixed byte __b[c_bsize];\n\t\t\n\t\tpublic const HttpStatusCode Disconnected = 0;\n\t\t\n\t\tpublic HttpReader_(NetworkStream ns) {\n\t\t\t_ns = ns;\n\t\t\tfixed (byte* p = __b) _b = new(p, c_bsize);\n\t\t}\n\t\t\n\t\tvoid _Buffer() {\n\t\t\tif (_n > 0) {\n\t\t\t\t_b = _b.Slice(_n);\n\t\t\t\tif (_b.IsEmpty) {\n\t\t\t\t\tif (_line.Length == c_bsize) throw new HttpReadException_(HttpStatusCode.RequestHeaderFieldsTooLarge);\n\t\t\t\t\tfixed (byte* p = __b) _b = new(p, c_bsize);\n\t\t\t\t\t_line.CopyTo(_b);\n\t\t\t\t\tint ll = _line.Length;\n\t\t\t\t\t_line = _b;\n\t\t\t\t\t_b = _b.Slice(ll);\n\t\t\t\t}\n\t\t\t\t_pos = 0;\n\t\t\t}\n\t\t\t_n = _ns.Read(_b);\n\t\t\tif (_n == 0) throw new HttpReadException_(Disconnected);\n\t\t}\n\t\t\n\t\t//note: would be simpler with _ns.ReadByte(), but it is slow. Therefore we _ns.Read() chunks into __b, and then read bytes from there.\n\t\tint _ReadByte() {\n\t\t\tif (_pos == _n) _Buffer();\n\t\t\treturn _b[_pos++];\n\t\t}\n\t\t\n\t\tpublic string ReadLine() {\n\t\t\t_line = _b.Slice(_pos);\n\t\t\tfor (int len = 0; ; len++) {\n\t\t\t\tint c = _ReadByte();\n\t\t\t\tif (c is 10 or 13) {\n\t\t\t\t\t_ReadRN(c);\n\t\t\t\t\treturn Encoding.Latin1.GetString(_line.Slice(0, len));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tvoid _ReadRN(int c) {\n\t\t\tif (c == 10) return;\n\t\t\tif (c == 13 && _ReadByte() == 10) return;\n\t\t\tthrow new HttpReadException_(HttpStatusCode.BadRequest);\n\t\t}\n\t\t\n\t\tpublic void ReadRN() {\n\t\t\t_line = _b.Slice(_pos);\n\t\t\t_ReadRN(_ReadByte());\n\t\t}\n\t\t\n\t\tpublic byte[] Read(int length) {\n\t\t\tvar a = GC.AllocateUninitializedArray<byte>(length);\n\t\t\tSpan<byte> span = a;\n\t\t\t\n\t\t\t//at first read from _b\n\t\t\tint n = _n - _pos;\n\t\t\tif (n > 0) {\n\t\t\t\tn = Math.Min(n, length);\n\t\t\t\t_b.Slice(_pos, n).CopyTo(a);\n\t\t\t\t_pos += n;\n\t\t\t\tspan = span.Slice(n);\n\t\t\t}\n\t\t\t\n\t\t\t//then read from _ns, if did not read all from _b\n\t\t\twhile (!span.IsEmpty) {\n\t\t\t\tn = _ns.Read(span);\n\t\t\t\tif (n == 0) throw new HttpReadException_(Disconnected);\n\t\t\t\tspan = span.Slice(n);\n\t\t\t}\n\t\t\t\n\t\t\treturn a;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Reads headers until empty line.\n\t\t/// </summary>\n\t\t/// <param name=\"headers\">The function adds headers here.</param>\n\t\tpublic void ReadHeaders(Dictionary<string, string> headers) {\n\t\t\tstring header = null;\n\t\t\tfor (; ; ) {\n\t\t\t\tvar s = ReadLine();\n\t\t\t\tif (s.Length == 0) break;\n\t\t\t\tif (s[0] is ' ' or '\\t') {\n\t\t\t\t\tif (header == null) throw new HttpReadException_(HttpStatusCode.BadRequest);\n\t\t\t\t\theader = string.Concat(header, \" \", s.AsSpan(1));\n\t\t\t\t} else {\n\t\t\t\t\t_Header();\n\t\t\t\t\theader = s;\n\t\t\t\t}\n\t\t\t}\n\t\t\t_Header();\n\t\t\t\n\t\t\tvoid _Header() {\n\t\t\t\tif (header != null) {\n\t\t\t\t\tint i = header.IndexOf(':'); if (i <= 0 || header[i - 1] <= ' ') throw new HttpReadException_(HttpStatusCode.BadRequest);\n\t\t\t\t\tvar k = header[..i];\n\t\t\t\t\tvar v = header.AsSpan(i + 1).Trim().ToString();\n\t\t\t\t\tif (!headers.TryAdd(k, v)) headers[k] += \", \" + v;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Reads content and trailing headers.\n\t\t/// </summary>\n\t\t/// <param name=\"headers\">The function gets content length etc from here. Also reads trailing headers here.</param>\n\t\t/// <returns><c>null</c> if <i>headers</i> don't contain content-length or transfer-encoding.</returns>\n\t\tpublic byte[] ReadContent(Dictionary<string, string> headers) {\n\t\t\tbyte[] content = null;\n\t\t\t\n\t\t\t//get content info etc from headers\n\t\t\tint contentLength = 0;\n\t\t\tbool chunked = false, trailer = false;\n\t\t\tforeach (var (k, v) in headers) {\n\t\t\t\tif (k.Eqi(\"Content-Length\")) contentLength = v.ToInt();\n\t\t\t\telse if (k.Eqi(\"Transfer-Encoding\")) {\n\t\t\t\t\tif (v.Eq(\"chunked\")) chunked = true;\n\t\t\t\t\telse throw new HttpReadException_(HttpStatusCode.NotImplemented, k);\n\t\t\t\t} else if (k.Eqi(\"Trailer\")) trailer = true;\n\t\t\t}\n\t\t\t\n\t\t\t//read content\n\t\t\tif (chunked || contentLength > 0) {\n\t\t\t\tif (chunked) {\n\t\t\t\t\tList<byte[]> chunks = new();\n\t\t\t\t\tfor (; ; ) {\n\t\t\t\t\t\tvar s = ReadLine();\n\t\t\t\t\t\tif (!s.ToInt(out int len, flags: STIFlags.IsHexWithout0x | STIFlags.DontSkipSpaces)) throw new HttpReadException_(HttpStatusCode.BadRequest);\n\t\t\t\t\t\tif (len == 0) break;\n\t\t\t\t\t\tchunks.Add(Read(len));\n\t\t\t\t\t\tReadRN();\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tif (chunks.Count > 0) {\n\t\t\t\t\t\tbyte[] ac = chunks[0];\n\t\t\t\t\t\tif (chunks.Count > 1) {\n\t\t\t\t\t\t\tac = new byte[chunks.Sum(o => o.Length)];\n\t\t\t\t\t\t\tint i = 0;\n\t\t\t\t\t\t\tforeach (var a in chunks) { a.CopyTo(ac, i); i += a.Length; }\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcontent = ac;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tif (trailer) { //headers after content\n\t\t\t\t\t\tReadHeaders(headers);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tReadRN();\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tcontent = Read(contentLength);\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\treturn content;\n\t\t}\n\t}\n\t\n\tclass HttpReadException_ : Exception {\n\t\tpublic HttpReadException_(HttpStatusCode status, string message = null) : base(message) { this.status = status; }\n\t\tpublic readonly HttpStatusCode status;\n\t}\n}\n\nnamespace Au.Types {\n\t/// <summary>\n\t/// See <see cref=\"HttpServerSession.MessageReceived\"/>.\n\t/// </summary>\n\tpublic class HSMessage {\n\t\t/// <summary>\n\t\t/// Method, like <c>\"GET\"</c> or <c>\"POST\"</c>.\n\t\t/// </summary>\n\t\tpublic string Method { get; internal set; }\n\t\t\n\t\t/// <summary>\n\t\t/// Target, like <c>\"/file.html\"</c> or <c>\"/file.html?a=1&amp;b=2\"</c> or <c>\"/\"</c>. May be URL-encoded.\n\t\t/// </summary>\n\t\tpublic string RawTarget { get; internal set; }\n\t\t\n\t\t/// <summary>\n\t\t/// Target without URL parameters, like <c>\"/file.html\"</c> or <c>\"/\"</c>. Not URL-encoded.\n\t\t/// </summary>\n\t\tpublic string TargetPath { get; internal set; }\n\t\t\n\t\t/// <summary>\n\t\t/// URL parameters (query string). Not URL-encoded.\n\t\t/// </summary>\n\t\t/// <value><c>null</c> if there are no URL parameters.</value>\n\t\tpublic Dictionary<string, string> UrlParameters { get; internal set; }\n\t\t\n\t\t/// <summary>\n\t\t/// Headers. Case-insensitive.\n\t\t/// </summary>\n\t\tpublic Dictionary<string, string> Headers { get; } = new(StringComparer.OrdinalIgnoreCase);\n\t\t\n\t\t/// <summary>\n\t\t/// Raw content. For example POST data as UTF-8 text or binary.\n\t\t/// </summary>\n\t\t/// <value><c>null</c> if the message is without content.</value>\n\t\tpublic byte[] Content { get; internal set; }\n\t\t\n\t\t/// <summary>\n\t\t/// <c>Content-Type</c> header info.\n\t\t/// </summary>\n\t\t/// <value><c>null</c> if <c>Content-Type</c> header is missing or invalid.</value>\n\t\tpublic HSContentType ContentType => _contentType ??= HSContentType.Create(Headers);\n\t\tHSContentType _contentType;\n\t\t\n\t\t/// <summary>\n\t\t/// <see cref=\"Content\"/> converted to text.\n\t\t/// </summary>\n\t\t/// <value><c>null</c> if there is no content.</value>\n\t\t/// <remarks>If text encoding unspecified, uses UTF-8; if specified invalid, uses ASCII.</remarks>\n\t\tpublic string Text => _contentText ??= Content == null ? null : (ContentType?.Encoding ?? Encoding.UTF8).GetString(Content);\n\t\tstring _contentText;\n\t\t\n\t\t/// <summary>\n\t\t/// JSON-deserializes <see cref=\"Content\"/> to object of type <c>T</c>.\n\t\t/// </summary>\n\t\t/// <returns><c>default(T)</c> if the request does not have body data.</returns>\n\t\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"JsonSerializer.Deserialize{TValue}(Stream, JsonSerializerOptions?)\"/>.</exception>\n\t\tpublic T Json<T>() => Content == null ? default : JsonSerializer.Deserialize<T>(Content, InternetUtil_.JsonSerializerOptions);\n\t\t\n\t\t/// <summary>\n\t\t/// JSON-deserializes <see cref=\"Content\"/> to object of specified type.\n\t\t/// </summary>\n\t\t/// <returns><c>null</c> if the request does not have body data.</returns>\n\t\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"JsonSerializer.Deserialize(Stream, Type, JsonSerializerOptions?)\"/>.</exception>\n\t\tpublic object Json(Type type) => Content == null ? default : JsonSerializer.Deserialize(Content, type, InternetUtil_.JsonSerializerOptions);\n\t\t\n\t\t/// <summary>\n\t\t/// Keys/values from POST content with <c>Content-Type: application/x-www-form-urlencoded</c>.\n\t\t/// </summary>\n\t\t/// <value><c>null</c> if the message has no content of this type.</value>\n\t\tpublic Dictionary<string, string> Urlencoded {\n\t\t\tget {\n\t\t\t\tif (_contentUrlParameters == null && Content != null && Headers.TryGetValue(\"Content-Type\", out var v) && v.Starts(\"application/x-www-form-urlencoded\", true)) {\n\t\t\t\t\t_contentUrlParameters = ParseUrlParameters_(Encoding.Latin1.GetString(Content));\n\t\t\t\t}\n\t\t\t\treturn _contentUrlParameters;\n\t\t\t}\n\t\t}\n\t\tDictionary<string, string> _contentUrlParameters;\n\t\t\n\t\tinternal static Dictionary<string, string> ParseUrlParameters_(RStr s) {\n\t\t\tDictionary<string, string> d = null;\n\t\t\tfor (int i = 0, j; i < s.Length; i = j + 1) {\n\t\t\t\tint q = -1;\n\t\t\t\tfor (j = i; j < s.Length && s[j] != '&'; j++) if (s[j] == '=' && q < 0) q = j;\n\t\t\t\tif (q > 0) (d ??= new())[WebUtility.UrlDecode(s.Slice(i, q - i).ToString())] = WebUtility.UrlDecode(s.Slice(++q, j - q).ToString());\n\t\t\t}\n\t\t\treturn d;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Parts of multipart content. For example of POST content with <c>Content-Type: multipart/form-data</c>.\n\t\t/// </summary>\n\t\t/// <value><c>null</c> if the message has no multipart content.</value>\n\t\tpublic Dictionary<string, HSContentPart> Multipart {\n\t\t\tget {\n\t\t\t\tif (_contentParts == null && Content != null && Headers.TryGetValue(\"Content-Type\", out var v) && v.Starts(\"multipart/\", true)) {\n\t\t\t\t\t_contentParts = _GetContentMultipart();\n\t\t\t\t}\n\t\t\t\treturn _contentParts;\n\t\t\t}\n\t\t}\n\t\tDictionary<string, HSContentPart> _contentParts;\n\t\t\n\t\tDictionary<string, HSContentPart> _GetContentMultipart() {\n\t\t\tif (Content == null || ContentType?.Boundary is not string sb || Content.Length < sb.Length * 2 + 8) return null;\n\t\t\t//print.it($\"'{Content.ToStringUTF8()}'\");\n\t\t\tDictionary<string, HSContentPart> a = null;\n\t\t\t//need to parse bytes, not string, because part bodies can be binary or use various encodings\n\t\t\tRByte k = Content, b = Encoding.Latin1.GetBytes(\"--\" + sb), b0 = b.Slice(2);\n\t\t\tif (!_FindBound(k, b, 0, out int startBound, out int endBound, out bool last)) return null;\n\t\t\tfor (int index = 0; !last;) {\n\t\t\t\tint startPart = endBound;\n\t\t\t\tif (!_FindBound(k, b, startPart, out startBound, out endBound, out last)) return null;\n\t\t\t\tvar part = k.Slice(startPart, startBound - startPart);\n\t\t\t\t//print.it($\"<<{part.ToStringUTF8()}>>\");\n\t\t\t\tint i = 0;\n\t\t\t\tif (!part.StartsWith(\"\\r\\n\"u8)) {\n\t\t\t\t\ti = part.IndexOf(\"\\r\\n\\r\\n\"u8) + 2; if (i < 2) continue;\n\t\t\t\t}\n\t\t\t\tvar dh = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);\n\t\t\t\tif (i != 0) {\n\t\t\t\t\tvar sh = Encoding.Latin1.GetString(part.Slice(0, i - 2)).RxReplace(@\"\\n\\h\", \" \");\n\t\t\t\t\tforeach (var v in sh.Lines(true)) {\n\t\t\t\t\t\tif (v.Split2_(':', out var s1, out var s2, 1, 0)) dh[s1] = s2;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tHSContentPart p = new(index++, dh, part.Slice(i + 2).ToArray());\n\t\t\t\t(a ??= new())[p.Name] = p;\n\t\t\t}\n\t\t\treturn a;\n\t\t\t\n\t\t\tstatic bool _FindBound(RByte k, RByte b, int i, out int start, out int end, out bool last) {\n\t\t\t\tstart = end = 0; last = false;\n\t\t\t\tfor (; i < k.Length; i = end) {\n\t\t\t\t\tint j = k.Slice(i).IndexOf(b);\n\t\t\t\t\tif (j < 0) break;\n\t\t\t\t\tstart = i + j;\n\t\t\t\t\tend = start + b.Length + 2;\n\t\t\t\t\tif (end > k.Length) break;\n\t\t\t\t\tif (start == 0 || (start >= 2 && k[start - 1] == '\\n' && k[start - 2] == '\\r')) {\n\t\t\t\t\t\tif (start >= 2) start -= 2;\n\t\t\t\t\t\tif (k[end - 1] == '\\n' && k[end - 2] == '\\r') return true;\n\t\t\t\t\t\tif (k[end - 1] == '-' && k[end - 2] == '-') { last = true; return true; }\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Contains a single part of a multipart POST data.\n\t/// </summary>\n\t/// <param name=\"Index\">0-based index of this part in the list of parts.</param>\n\t/// <param name=\"Headers\">Headers of this part.</param>\n\t/// <param name=\"Content\">Raw content of this part. For example UTF-8 text.</param>\n\tpublic record class HSContentPart(int Index, Dictionary<string, string> Headers, byte[] Content) {\n\t\t/// <inheritdoc cref=\"HSMessage.ContentType\"/>\n\t\tpublic HSContentType ContentType => _contentType ??= HSContentType.Create(Headers);\n\t\tHSContentType _contentType;\n\t\t\n\t\t/// <summary>\n\t\t/// <see cref=\"Content\"/> converted to text.\n\t\t/// </summary>\n\t\t/// <value><c>null</c> if there is no content.</value>\n\t\t/// <remarks>If text encoding unspecified, uses UTF-8; if specified invalid, uses ASCII.</remarks>\n\t\tpublic string Text => _contentText ??= Content == null ? null : (ContentType?.Encoding ?? Encoding.UTF8).GetString(Content);\n\t\tstring _contentText;\n\t\t\n\t\tSystem.Net.Mime.ContentDisposition _ContentDisposition() {\n\t\t\tif (_contentDisposition == null && Headers.TryGetValue(\"Content-Disposition\", out var s)) try { _contentDisposition = new(s); } catch {  }\n\t\t\treturn _contentDisposition;\n\t\t}\n\t\tSystem.Net.Mime.ContentDisposition _contentDisposition;\n\t\t\n\t\t/// <summary>\n\t\t/// Gets name from <c>Content-Disposition</c> header.\n\t\t/// </summary>\n\t\t/// <value>If <c>Content-Disposition</c> header or name is missing, returns <c>Index.ToS()</c>.</value>\n\t\t/// <remarks>\n\t\t///\tDecodes <c>\"=?utf-8?B?base64?=\"</c>.\n\t\t/// </remarks>\n\t\tpublic string Name => _name ??= _DecodeMime(_ContentDisposition()?.Parameters[\"name\"] ?? Index.ToS());\n\t\tstring _name;\n\t\t\n\t\t/// <summary>\n\t\t/// Gets filename from <c>Content-Disposition</c> header.\n\t\t/// </summary>\n\t\t/// <value><c>null</c> if <c>Content-Disposition</c> header or filename is missing.</value>\n\t\t/// <remarks>\n\t\t///\tDecodes <c>\"utf-8''urlencoded\"</c> or <c>\"=?utf-8?B?base64?=\"</c>.\n\t\t/// </remarks>\n\t\tpublic string FileName {\n\t\t\tget {\n\t\t\t\tif (_fileName == null && _ContentDisposition() is { } cd) {\n\t\t\t\t\tif (cd.Parameters[\"filename*\"] is string s && s.Starts(\"utf-8''\")) { //never mind: can be \"any-charset'language'\"\n\t\t\t\t\t\ttry { _fileName = WebUtility.UrlDecode(s[7..]); } catch {  }\n\t\t\t\t\t}\n\t\t\t\t\t_fileName ??= _DecodeMime(cd.Parameters[\"filename\"]);\n\t\t\t\t}\n\t\t\t\treturn _fileName;\n\t\t\t}\n\t\t}\n\t\tstring _fileName;\n\t\t\n\t\tstatic string _DecodeMime(string s) {\n\t\t\tif (s != null && s.Starts(\"=?utf-8?B?\", true) && s.Ends(\"?=\")) {\n\t\t\t\ttry { return Convert.FromBase64String(s[10..^2]).ToStringUTF8(); } catch {  }\n\t\t\t}\n\t\t\treturn s;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Contains properties of HTTP <c>Content-Type</c> header.\n\t/// See <see cref=\"HSMessage.ContentType\"/>.\n\t/// </summary>\n\tpublic class HSContentType {\n\t\t/// <summary>\n\t\t/// Creates from <c>Content-Type</c> header.\n\t\t/// </summary>\n\t\t/// <value><c>null</c> if <c>Content-Type</c> header is missing or invalid.</value>\n\t\tpublic static HSContentType Create(Dictionary<string, string> headers) {\n\t\t\tif (headers.TryGetValue(\"Content-Type\", out var s)) {\n\t\t\t\ttry { return new(new(s)); }\n\t\t\t\tcatch {  }\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\t\n\t\tHSContentType(System.Net.Mime.ContentType t) {\n\t\t\tMediaType = t.MediaType;\n\t\t\tBoundary = t.Boundary;\n\t\t\tCharset = t.CharSet;\n\t\t\tEncoding = _GetEncoding(t);\n\t\t}\n\t\t\n\t\t///\n\t\tpublic string MediaType { get; }\n\t\t\n\t\t/// <summary>\n\t\t/// Returns the boundary parameter without double quotes, or <c>null</c> if not specified.\n\t\t/// </summary>\n\t\tpublic string Boundary { get; }\n\t\t\n\t\t/// <summary>\n\t\t/// Returns the <c>charset</c> parameter, or <c>null</c> if not specified.\n\t\t/// </summary>\n\t\tpublic string Charset { get; }\n\t\t\n\t\t/// <summary>\n\t\t/// Gets text encoding.\n\t\t/// </summary>\n\t\t/// <value>Returns:\n\t\t/// <br/>• <c>null</c> if multipart content (<see cref=\"Boundary\"/> not <c>null</c>).\n\t\t/// <br/>• UTF-8 if <c>charset</c> is <c>utf-8</c> or not specified.\n\t\t/// <br/>• <c>Encoding</c> that matches <c>charset</c>.\n\t\t/// <br/>• ASCII if <c>charset</c> is invalid.\n\t\t/// </value>\n\t\tpublic Encoding Encoding { get; }\n\t\t\n\t\tEncoding _GetEncoding(System.Net.Mime.ContentType t) {\n\t\t\tvar s = Charset;\n\t\t\tif (s == null) return t.Boundary != null ? null : Encoding.UTF8;\n\t\t\tif (s.Eqi(\"utf-8\")) return Encoding.UTF8;\n\t\t\tif (s.Eqi(\"us-ascii\")) return Encoding.ASCII;\n\t\t\tif (s.Eqi(\"iso-8859-1\")) return Encoding.Latin1;\n\t\t\treturn StringUtil.GetEncoding(s) ?? Encoding.ASCII;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// See <see cref=\"HttpServerSession.MessageReceived\"/>.\n\t/// </summary>\n\tpublic class HSResponse {\n\t\t/// <summary>\n\t\t/// Response status code. Initially <b>OK</b>.\n\t\t/// </summary>\n\t\tpublic HttpStatusCode Status { get; set; }\n\t\t\n\t\t/// <summary>\n\t\t/// Response reason phrase. Initially <c>null</c>.\n\t\t/// </summary>\n\t\tpublic string Reason { get; set; }\n\t\t\n\t\t/// <summary>\n\t\t/// Response headers. Initially empty.\n\t\t/// The server later may add <c>Date</c>, <c>Server</c>, <c>Content-Encoding</c>, <c>Content-Length</c>.\n\t\t/// </summary>\n\t\tpublic Dictionary<string, string> Headers { get; } = new(StringComparer.OrdinalIgnoreCase);\n\t\t\n\t\t/// <summary>\n\t\t/// Raw response content.\n\t\t/// </summary>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// r.Content = text.ToUTF8();\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\t/// <remarks>\n\t\t/// The server may send this data compressed (it depends on headers etc).\n\t\t/// </remarks>\n\t\tpublic byte[] Content { get; set; }\n\t\t\n\t\t/// <summary>\n\t\t/// Sets response content text.\n\t\t/// </summary>\n\t\t/// <param name=\"content\">Sets <see cref=\"Content\"/>: <c>Content = content.ToUTF8();</c>.</param>\n\t\t/// <param name=\"contentType\">If not <c>null</c>, sets <c>Content-Type</c> header.</param>\n\t\tpublic void SetContentText(string content, string contentType = \"text/plain; charset=utf-8\") {\n\t\t\tContent = content?.ToUTF8();\n\t\t\tif (contentType != null) Headers[\"Content-Type\"] = contentType;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// JSON-serializes object of type <c>T</c>, and sets <see cref=\"Content\"/>. Also sets <c>Content-Type</c> header.\n\t\t/// </summary>\n\t\t/// <param name=\"obj\">Object of type <c>T</c>.</param>\n\t\t/// <param name=\"contentType\">If not <c>null</c>, sets <c>Content-Type</c> header.</param>\n\t\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"JsonSerializer.SerializeToUtf8Bytes{TValue}(TValue, JsonSerializerOptions?)\"/>.</exception>\n\t\tpublic void SetContentJson<T>(T obj, string contentType = \"application/json; charset=utf-8\") {\n\t\t\tContent = JsonSerializer.SerializeToUtf8Bytes(obj, InternetUtil_.JsonSerializerOptions);\n\t\t\tif (contentType != null) Headers[\"Content-Type\"] = contentType;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// JSON-serializes object of specified type, and sets <see cref=\"Content\"/>. Also sets <c>Content-Type</c> header.\n\t\t/// </summary>\n\t\t/// <param name=\"obj\">Object.</param>\n\t\t/// <param name=\"type\"><i>obj</i> type.</param>\n\t\t/// <param name=\"contentType\">If not <c>null</c>, sets <c>Content-Type</c> header.</param>\n\t\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"JsonSerializer.SerializeToUtf8Bytes{TValue}(TValue, JsonSerializerOptions?)\"/>.</exception>\n\t\tpublic void SetContentJson<T>(object obj, Type type, string contentType = \"application/json; charset=utf-8\") {\n\t\t\tContent = JsonSerializer.SerializeToUtf8Bytes(obj, type, InternetUtil_.JsonSerializerOptions);\n\t\t\tif (contentType != null) Headers[\"Content-Type\"] = contentType;\n\t\t}\n\t}\n\t\n\tstatic class InternetUtil_ {\n\t\tstatic readonly Lazy<JsonSerializerOptions> s_defaultSerializerOptions = new(() => new(JsonSerializerDefaults.Web));\n\t\t\n\t\tpublic static JsonSerializerOptions JsonSerializerOptions => s_defaultSerializerOptions.Value;\n\t}\n}\n"
  },
  {
    "path": "Au/Au.More/IconImageCache.cs",
    "content": "//TODO2: 2025-11: often the main floating toolbar expands slowly. 2025-12: fast again.\n\nusing System.Drawing;\nusing Microsoft.Win32;\n\nnamespace Au.More;\n\n/// <summary>\n/// Gets images as <see cref=\"Bitmap\"/> of same logical size to be displayed as icons. Can get file icons or load images from files etc.\n/// </summary>\n/// <remarks>\n/// Uses memory cache and optionally file cache to avoid loading same image multiple times. Getting images from cache is much faster.\n///\n/// <c>Bitmap</c> objects retrieved by this class are stored in memory cache. Don't dispose them before disposing the <c>IconImageCache</c> object. Usually don't need to dispose these <c>Bitmap</c> objects explicitly (GC will do it).\n/// \n/// Thread-safe.\n/// </remarks>\npublic sealed class IconImageCache : IDisposable {\n\trecord class _DpiImages {\n\t\treadonly IconImageCache _cache;\n\t\tpublic readonly int dpi;\n\t\tpublic readonly string indexFile;\n\t\tpublic Dictionary<string, Hash.MD5Result> dNameHash; //index file data\n\t\tpublic readonly Dictionary<string, Bitmap> dNameBitmap = new(); //memory cache\n\t\tpublic readonly Dictionary<Hash.MD5Result, Bitmap> dHashBitmap = new(); //let all identical images share single Bitmap object\n\t\t\n\t\t//info: dictionary string keys are case-sensitive. We have not only paths but also base64 MD5. For paths we call Lower.\n\t\t\n\t\tpublic _DpiImages(IconImageCache cache, int dpi) {\n\t\t\t_cache = cache;\n\t\t\tthis.dpi = dpi;\n\t\t\tif (_cache._dir is string s) indexFile = s + \"\\\\\" + dpi.ToS() + \".dpi\";\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// If the index file for this DPI still not loaded, loads it into <c>dNameHash</c>.\n\t\t/// </summary>\n\t\tpublic void LoadIndexFile() {\n\t\t\tif (dNameHash == null && filesystem.exists(indexFile, useRawPath: true)) {\n\t\t\t\ttry {\n\t\t\t\t\tbool save = false;\n\t\t\t\t\tDictionary<string, Hash.MD5Result> d = new(StringComparer.OrdinalIgnoreCase);\n\t\t\t\t\tfilesystem.waitIfLocked(() => {\n\t\t\t\t\t\tvar fs = File.OpenRead(indexFile);\n\t\t\t\t\t\tusing var br = new BinaryReader(fs);\n\t\t\t\t\t\tfor (var len = fs.Length; fs.Position < len;) {\n\t\t\t\t\t\t\tvar imageKey = br.ReadString();\n\t\t\t\t\t\t\tHash.MD5Result hash = new(br.ReadInt64(), br.ReadInt64());\n\t\t\t\t\t\t\tref var r = ref d.GetValueRefOrAddDefault_(imageKey, out bool exists);\n\t\t\t\t\t\t\tr = hash;\n\t\t\t\t\t\t\tif (exists) save = true; //save without duplicates. Keep the newest hash.\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t\tdNameHash = d;\n\t\t\t\t\tif (save) _SaveIndex();\n\t\t\t\t}\n\t\t\t\tcatch (Exception e1) { Debug_.Print(e1); }\n\t\t\t}\n\t\t\tdNameHash ??= new();\n\t\t}\n\t\t\n\t\tvoid _SaveIndex() {\n\t\t\t//print.it(\"<><c red>SaveIndex<>\");\n\t\t\tfilesystem.waitIfLocked(() => {\n\t\t\t\tvar fs = File.Create(indexFile);\n\t\t\t\tusing var bw = new BinaryWriter(fs);\n\t\t\t\tforeach (var v in dNameHash) {\n\t\t\t\t\tbw.Write(v.Key);\n\t\t\t\t\tbw.Write(v.Value.r1);\n\t\t\t\t\tbw.Write(v.Value.r2);\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t\t\n\t\tpublic void AppendToIndexFile(string imageKey, Hash.MD5Result hash) {\n\t\t\tfilesystem.waitIfLocked(() => {\n\t\t\t\tvar fs = File.Open(indexFile, FileMode.Append);\n\t\t\t\tusing var bw = new BinaryWriter(fs);\n\t\t\t\tbw.Write(imageKey);\n\t\t\t\tbw.Write(hash.r1);\n\t\t\t\tbw.Write(hash.r2);\n\t\t\t});\n\t\t}\n\t}\n\t\n\treadonly string _dir;\n\treadonly List<_DpiImages> _aDpi = new(); //for each used DPI\n\treadonly int _imageSize;\n\tbool _disposed, _onceUsedFiles;\n\treadonly HashSet<string> _extDynamicIcon = new();\n\t\n\tstatic List<WeakReference<IconImageCache>> s_caches = new();\n\t\n\t/// <param name=\"imageSize\">Width and height of images. Min 16, max 256.</param>\n\t/// <param name=\"directory\">Path of cache directory. If <c>null</c>, will be used only memory cache.</param>\n\t/// <exception cref=\"ArgumentException\">Not full path.</exception>\n\tpublic IconImageCache(int imageSize = 16, string directory = null) {\n\t\tif (imageSize < 16 || imageSize > 256) throw new ArgumentOutOfRangeException(nameof(imageSize));\n\t\t_imageSize = imageSize;\n\t\t//directory = null; //test memory-only cache\n\t\tif (directory != null) _dir = pathname.normalize(directory);\n\t\t\n\t\tlock (s_caches) s_caches.Add(new(this));\n\t\t\n\t\t//_InitNotifyWindow();\n\t\tscript.GetAuxThread_(); //ensure the aux thread is running. It calls ClearAll_ when receives message \"clear image caches\".\n\t}\n\t\n\t/// <summary>\n\t/// Width and height of images.\n\t/// </summary>\n\tpublic int ImageSize => _imageSize;\n\t\n\t//public string CacheDirectory => _dir;\n\t\n\t/// <summary>\n\t/// Common cache for icons of size 16. Used by menus, toolbars and editor.\n\t/// </summary>\n\t/// <remarks>\n\t/// Uses cache directory <c>folders.ThisAppDataLocal + \"iconCache\"</c>.\n\t/// </remarks>\n\tpublic static IconImageCache Common => CommonOfSize(16);\n\t\n\t/// <summary>\n\t/// Common cache for icons of given size. Used by menus and toolbars if custom <see cref=\"MTBase.ImageSize\"/>.\n\t/// </summary>\n\t/// <param name=\"imageSize\">Width and height of images. Min 16, max 256.</param>\n\tpublic static IconImageCache CommonOfSize(int imageSize) {\n\t\tif (imageSize < 16 || imageSize > 256) throw new ArgumentOutOfRangeException(nameof(imageSize));\n\t\treturn s_commonDict.GetOrAdd(imageSize, o => new(o, folders.ThisAppDataLocal + \"iconCache\" + (o == 16 ? \"\" : o.ToS())));\n\t}\n\tstatic ConcurrentDictionary<int, IconImageCache> s_commonDict = new();\n\t//rejected: if this is a portable app in a removable drive, use only memory cache (dir=null). Maybe only if folders.ThisAppDataLocal is in a fixed drive.\n\t//\tCannot reliably detect whether this app is portable. And whether the app (or user) wants to use the file cache.\n\t//\tEg this app may be in an external SSD drive, but external SSD drives are detected as fixed, and impossible to know whether it is used as a portable app.\n\t//\tInstead, if this is a portable app and does not want to write in other drives, let it set folders.ThisAppDataLocal. And portable LA does it (as well as script processes started from it).\n\t\n\t/// <summary>\n\t/// Gets image from cache or file etc.\n\t/// </summary>\n\t/// <param name=\"imageSource\">File path, or resource path that starts with <c>\"resources/\"</c> or has prefix <c>\"resource:\"</c>, or icon name like <c>\"*Pack.Icon color\"</c>, etc. See <i>isImage</i> parameter.</param>\n\t/// <param name=\"dpi\">DPI of window that will display the image. See <see cref=\"Dpi\"/>.</param>\n\t/// <param name=\"isImage\">\n\t/// <c>false</c> - get file/folder/filetype/url/etc icon with <see cref=\"icon.of\"/>. If <i>imageSource</i> is relative path of a <c>.cs</c> file, gets its custom icon as image; returns <c>null</c> if no custom icon or if editor isn't running.\n\t/// <c>true</c> - load image from xaml/png/etc file, database, resource or string with <see cref=\"ImageUtil.LoadGdipBitmap\"/> or <see cref=\"ImageUtil.LoadWpfImageElement\"/>. Can be icon name like <c>\"*Pack.Icon color\"</c> (see menu <b>Tools > Icons</b>).\n\t/// \n\t/// To detect whether a string is an image, call <see cref=\"ImageUtil.HasImageOrResourcePrefix\"/>; if it returns <c>true</c>, it is image.\n\t/// </param>\n\t/// <param name=\"onException\">Action to call when fails to load image. If <c>null</c>, then silently returns <c>null</c>. Parameters are image source string and exception.</param>\n\tpublic unsafe Bitmap Get(string imageSource, int dpi, bool isImage, Action<string, Exception> onException = null) {\n\t\t//print.it(imageSource, isImage);\n\t\tif (_disposed) throw new ObjectDisposedException(nameof(IconImageCache));\n\t\tbool isXaml = isImage && (imageSource.Starts('<') || imageSource.Ends(\".xaml\", true));\n\t\tbool isStore = !isImage && imageSource.Starts(@\"shell:AppsFolder\\\"); //compare case-sensitive. Then users can pass eg \"shell:appsFolder...\" to display white icons in blue background.\n\t\tbool ofWorkspaceFile = false;\n\t\tif (!isImage && !isStore && imageSource.Ends(\".cs\", true) && !pathname.isFullPath(imageSource, orEnvVar: true)) { //eg `script.run(@\"x.cs\");`\n\t\t\timageSource = ScriptEditor.GetIcon(imageSource, EGetIcon.PathToIconName);\n\t\t\tif (imageSource == null) return null;\n\t\t\tisImage = ofWorkspaceFile = true;\n\t\t\t//rejected: use Dictionary<imageSource, iconName> to avoid frequent GetIcon for same imageSource. In LA process fast, elsewhere not too slow.\n\t\t\t//rejected: Move this code to the caller that needs it (MTBase).\n\t\t}\n\t\tbool isIconName = isImage && !isXaml && imageSource.Starts('*');\n\t\tif (isIconName) {\n\t\t\tisXaml = true;\n\t\t\timageSource = IconString_.NormalizeColor(imageSource); //color can be \"normal|highContrast\"\n\t\t}\n\t\tif (!isXaml && !isStore) dpi = 96; //will scale when drawing, it's fast and not so bad. Tested scaling with Lanczos3 etc filters, but the result for icons isn't better.\n\t\tstring imageKey = imageSource;\n\t\tif (!isIconName) {\n\t\t\tif ((isXaml && imageKey.Starts('<')) || (isImage && ImageUtil.HasImageStringPrefix(imageKey))) imageKey = Hash.MD5(imageSource, base64: true);\n\t\t\telse imageKey = imageKey.Lower();\n\t\t}\n\t\tbool isIco = !isImage && !isStore && imageKey.Ends(\".ico\");\n\t\t\n\t\tlock (this) {\n\t\t\t//get _DpiImages for dpi\n\t\t\t_DpiImages dd;\n\t\t\tforeach (var v in _aDpi) if (v.dpi == dpi) { dd = v; goto g1; }\n\t\t\t_aDpi.Add(dd = new _DpiImages(this, dpi));\n\t\t\tg1:\n\t\t\t\n\t\t\tif (dd.dNameBitmap.TryGetValue(imageKey, out var b)) return b;\n\t\t\t\n\t\t\t//print.it(imageSource, isImage);\n\t\t\t//using var p1 = perf.local();\n\t\t\t\n\t\t\t//bool useHash = !isImage && !isIco;\n\t\t\tbool useHash = isImage ? isIconName : !isIco; //use file cache for *icon too\n\t\t\t\n\t\t\t//use file cache for *icon too.\n\t\t\t//\tBecause:\n\t\t\t//\t\tLoads XAML icon slowly first time (~100 ms; not measured after reboot), even in LA, and even later loads slower than from the cache file.\n\t\t\t//\t\tNon-WPF process uses much more memory (because loads WPF), eg 14 -> 28 MB.\n\t\t\t//\tBad: when trying to find icons, users try many icons, colors, sizes. Then the cache is full of garbage.\n\t\t\t//\t\tTODO3: remove from cache if not using anymore. Or add only if frequently using.\n\t\t\t//FUTURE: to make loading XAML icons faster etc, try Windows.UI.Xaml.Markup.XamlReader.Load. Use Microsoft.Windows.SDK.NET.dll, or directly COM if possible. When the library will not support Win7/8.\n\t\t\t\n\t\t\tbool useFile = _dir != null && useHash;\n\t\t\tif (useFile) {\n\t\t\t\ttry {\n\t\t\t\t\tif (!_onceUsedFiles) {\n\t\t\t\t\t\t_onceUsedFiles = true;\n\t\t\t\t\t\tvar fe = filesystem.exists(_dir);\n\t\t\t\t\t\tif (fe.File) filesystem.delete(_dir); //fbc (was .db file)\n\t\t\t\t\t\tif (!fe.Directory) filesystem.createDirectory(_dir);\n\t\t\t\t\t\t_InitFiles();\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tdd.LoadIndexFile();\n\t\t\t\t\t\n\t\t\t\t\tbool useExt = false; g2:\n\t\t\t\t\tif (dd.dNameHash.TryGetValue(imageKey, out var hash)) {\n\t\t\t\t\t\tif (dd.dHashBitmap.TryGetValue(hash, out var bb)) return bb;\n\t\t\t\t\t\tvar path = _dir + \"\\\\\" + hash.ToString() + \".png\";\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t//b = (Bitmap)Image.FromFile(path); //no, locks file\n\t\t\t\t\t\t\tusing (var stream = File.OpenRead(path)) b = (Bitmap)Image.FromStream(stream);\n\t\t\t\t\t\t\tdd.dNameBitmap[imageKey] = b;\n\t\t\t\t\t\t\tdd.dHashBitmap[hash] = b;\n\t\t\t\t\t\t\treturn b;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcatch (Exception e1) { Debug_.PrintIf(filesystem.exists(path), e1); }\n\t\t\t\t\t} else if (!useExt && !isImage && !isStore && !imageKey.Ends(\".exe\") && !imageKey.Ends(\".lnk\") && pathname.isFullPath(imageSource) && filesystem.exists(imageSource, useRawPath: true).File) {\n\t\t\t\t\t\tvar ext = pathname.getExtension(imageKey);\n\t\t\t\t\t\tbool noExt = ext.Length == 0;\n\t\t\t\t\t\tif (noExt) ext = \".#\"; //will get the icon of unknown file types\n\t\t\t\t\t\tif (dd.dNameBitmap.TryGetValue(ext, out var b1)) return b1;\n\t\t\t\t\t\tif (!_extDynamicIcon.Contains(ext)) {\n\t\t\t\t\t\t\tif (!noExt && _DynamicIcon(ext)) _extDynamicIcon.Add(ext); else { imageKey = imageSource = ext; useExt = true; goto g2; }\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (Exception e1) { print.warning(e1); useFile = false; } //failed to create _dir\n\t\t\t}\n\t\t\t//p1.Next('C');\n\t\t\t\n\t\t\ttry {\n\t\t\t\t//long t1 = perf.mcs;\n\t\t\t\tif (!isImage) {\n\t\t\t\t\tb = isStore ? icon.winStoreAppImage(imageSource, Dpi.Scale(_imageSize, dpi)) : null;\n\t\t\t\t\tb ??= icon.of(imageSource, _imageSize)?.ToGdipBitmap();\n\t\t\t\t} else {\n\t\t\t\t\tif (isIconName) {\n\t\t\t\t\t\timageSource = ScriptEditor.GetIcon_(imageSource, EGetIcon.IconNameToXaml, skipResources: ofWorkspaceFile);\n\t\t\t\t\t\t//p1.Next('X');\n\t\t\t\t\t\tif (imageSource == null) return null;\n\t\t\t\t\t}\n\t\t\t\t\tif (isXaml) b = ImageUtil.LoadGdipBitmapFromXaml(imageSource, dpi, (_imageSize, _imageSize));\n\t\t\t\t\telse b = ImageUtil.LoadGdipBitmap(imageSource);\n\t\t\t\t}\n\t\t\t\t//if (useFile) useFile = perf.mcs - t1 > 1000; //reduces the index file size in worst cases, but makes significantly slower later\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tif (onException != null) onException(imageSource, ex);\n\t\t\t\t//else print.warning(\"IconImageCache.Get() failed. \" + ex.ToStringWithoutStack()); //no. Often prints while editing text if editor shows images in text.\n\t\t\t}\n\t\t\t//p1.Next('L');\n\t\t\t\n\t\t\tif (b != null && (isImage ? useFile : !isIco)) {\n\t\t\t\ttry {\n\t\t\t\t\tvar ms = new MemoryStream();\n\t\t\t\t\tb.Save(ms, System.Drawing.Imaging.ImageFormat.Png); //~200 mcs. It's fast if compared with icon.of etc and saving.\n\t\t\t\t\tvar hash = Hash.MD5(ms.GetBuffer().AsSpan(0, (int)ms.Position));\n\t\t\t\t\t\n\t\t\t\t\tref var br = ref dd.dHashBitmap.GetValueRefOrAddDefault_(hash, out bool exists);\n\t\t\t\t\tif (!exists) br = b; else { b.Dispose(); b = br; }\n\t\t\t\t\t\n\t\t\t\t\tif (useFile) {\n\t\t\t\t\t\t//p1.Next();\n\t\t\t\t\t\tif (!exists) {\n\t\t\t\t\t\t\t//print.it(\"<><c green>save<>\");\n\t\t\t\t\t\t\tfilesystem.waitIfLocked(() => {\n\t\t\t\t\t\t\t\tusing var fs = File.Create($@\"{_dir}\\{hash.ToString()}.png\");\n\t\t\t\t\t\t\t\tfs.Write(ms.GetBuffer().AsSpan(0, (int)ms.Position));\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\tdd.dNameHash[imageKey] = hash;\n\t\t\t\t\t\t//p1.Next();\n\t\t\t\t\t\tdd.AppendToIndexFile(imageKey, hash);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (Exception e1) { Debug_.Print(e1); }\n\t\t\t}\n\t\t\t\n\t\t\tdd.dNameBitmap[imageKey] = b;\n\t\t\treturn b;\n\t\t}\n\t\t\n\t\t//rejected: Don't cache if non-literal (non-interned) string. Caller may generate many random strings, eg icon colors. Impossible to detect reliably.\n\t\t\n\t\tstatic bool _DynamicIcon(string ext) {\n\t\t\tif (Registry.GetValue(@\"HKEY_CLASSES_ROOT\\\" + ext, \"\", null) is string s1) {\n\t\t\t\t//if (Registry.GetValue(@\"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\\" + ext + @\"\\UserChoice\", \"ProgId\", null) is string s2) return _DI(s2); //it seems Windows ignores this when looking for icon handler or %1\n\t\t\t\tif (_DI(s1)) return true;\n\t\t\t}\n\t\t\treturn false;\n\t\t\t\n\t\t\tstatic bool _DI(string progid)\n\t\t\t\t=> Registry.GetValue(@\"HKEY_CLASSES_ROOT\\\" + progid + @\"\\ShellEx\\IconHandler\", \"\", null) is string\n\t\t\t\t|| Registry.GetValue(@\"HKEY_CLASSES_ROOT\\\" + progid + @\"\\DefaultIcon\", \"\", null) is \"\\\"%1\\\"\" or \"%1\"; //exe, ico, cur, ani\n\t\t}\n\t}\n\t\n\tvoid _InitFiles() {\n\t\tif (_dir == null) return;\n\t\t\n\t\t//delete cache files if changed OS/.NET/Au version\n\t\tvar verPath = _dir + @\"\\version.txt\";\n\t\ttry {\n\t\t\tvar sNew = osVersion.onaString;\n\t\t\tif (filesystem.exists(verPath, useRawPath: true)) {\n\t\t\t\tvar sOld = filesystem.loadText(verPath);\n\t\t\t\tif (sNew == sOld) return;\n\t\t\t\t_ClearFiles();\n\t\t\t}\n\t\t\tfilesystem.saveText(verPath, sNew);\n\t\t}\n\t\tcatch (Exception e1) { Debug_.Print(e1); }\n\t}\n\t\n\t/// <summary>\n\t/// Removes images from memory cache (but does not dispose) and makes this object unusable.\n\t/// Optional.\n\t/// </summary>\n\tpublic void Dispose() {\n\t\t_disposed = true;\n\t\t_aDpi.Clear();\n\t\t_Dispose();\n\t\tGC.SuppressFinalize(this);\n\t}\n\t\n\t///\n\t~IconImageCache() { /*print.it(\"~IconImageCache\");*/ _Dispose(); }\n\t\n\tvoid _Dispose() {\n\t\tlock (s_caches) {\n\t\t\tfor (int i = s_caches.Count; --i >= 0;) {\n\t\t\t\tif (!s_caches[i].TryGetTarget(out var v) || v == this) s_caches.RemoveAt(i);\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Clears the cache (removes images from memory cache and file cache).\n\t/// </summary>\n\t/// <param name=\"redrawWindows\">Redraw (asynchronously) all visible windows of this process.</param>\n\tpublic void Clear(bool redrawWindows = false) {\n\t\tif (_disposed) throw new ObjectDisposedException(nameof(IconImageCache));\n\t\tif (_Clear() && redrawWindows) _RedrawWindowsOfThisProcess();\n\t}\n\t\n\tbool _Clear() {\n\t\tlock (this) {\n\t\t\tif (_disposed) return false;\n\t\t\t_aDpi.Clear();\n\t\t\t_extDynamicIcon.Clear();\n\t\t}\n\t\t_ClearFiles();\n\t\tCleared?.Invoke();\n\t\treturn true;\n\t}\n\t\n\t/// <summary>\n\t/// When the cache cleared.\n\t/// </summary>\n\tpublic event Action Cleared;\n\t\n\tvoid _ClearFiles() => _ClearFiles(_dir);\n\t\n\tstatic void _ClearFiles(string dir) {\n\t\tif (dir == null || !filesystem.exists(dir, true).Directory) return;\n\t\ttry {\n\t\t\tbool deletedAll = true;\n\t\t\tforeach (var v in Directory.GetFiles(dir, \"*.dpi\")) deletedAll &= Api.DeleteFile(v);\n\t\t\tif (deletedAll) {\n\t\t\t\tforeach (var v in Directory.GetFiles(dir, \"*.png\")) deletedAll &= Api.DeleteFile(v);\n\t\t\t\tif (deletedAll && !dir.Ends(@\"\\iconCache\", true) && Api.DeleteFile(dir + @\"\\version.txt\")) Api.RemoveDirectory(dir);\n\t\t\t}\n\t\t}\n\t\tcatch (Exception e1) { Debug_.Print(e1); }\n\t}\n\t\n\tstatic unsafe void _RedrawWindowsOfThisProcess() {\n\t\tforeach (var w in wnd.findAll(of: WOwner.Process(process.thisProcessId), flags: WFlags.CloakedToo))\n\t\t\tApi.RedrawWindow(w, flags: Api.RDW_INVALIDATE | Api.RDW_ALLCHILDREN);\n\t}\n\t\n\t/// <summary>\n\t/// Clears caches of all <see cref=\"IconImageCache\"/> instances of this or all processes. Redraws (asynchronously) all visible windows of these processes.\n\t/// </summary>\n\t/// <param name=\"allProcesses\">Clear in all processes of this user session.</param>\n\tpublic static void ClearAll(bool allProcesses = true) {\n\t\tClearAll_();\n\t\tif (allProcesses) {\n\t\t\t//if called in LA process (eg menu Tools > Update icons), clear the cache dir of scripts.\n\t\t\t//\tWithout it would clear only if some script processes already used the cache.\n\t\t\tif (script.role == SRole.EditorExtension && Common._dir is string s1) {\n\t\t\t\tDebug.Assert(s1.Ends(@\"\\iconCache\"));\n\t\t\t\ts1 = s1.ReplaceAt(^9.., \"_script\");\n\t\t\t\t//clear caches of all image sizes\n\t\t\t\tvar a = filesystem.enumDirectories(s1, \"iconCache*\").ToArray();\n\t\t\t\tforeach (var f in a) {\n\t\t\t\t\t_ClearFiles(f.FullPath);\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tfor (var w = wnd.findFast(null, script.c_auxWndClassName, true); !w.Is0; w = wnd.findFast(null, script.c_auxWndClassName, true, w))\n\t\t\t\tif (!w.IsOfThisProcess) w.SendNotify(script.c_msg_IconImageCache_ClearAll);\n\t\t}\n\t}\n\t\n\tinternal static void ClearAll_() {\n\t\tList<IconImageCache> a = new();\n\t\tlock (s_caches) foreach (var c in s_caches) if (c.TryGetTarget(out var v)) a.Add(v);\n\t\tbool redrawWindows = false;\n\t\tforeach (var v in a) redrawWindows |= v._Clear();\n\t\tif (redrawWindows) _RedrawWindowsOfThisProcess();\n\t}\n\t\n\t//static int s_auxInited;\n\t\n\t//static void _InitNotifyWindow() {\n\t//\t//if (0 == Interlocked.Exchange(ref s_auxInited, 1)) {\n\t//\t//\tvar t = script.GetAuxThread_();\n\t//\t//\tt.QueueAPC(_InitAuxThread);\n\t//\t//}\n\t\n\t//\t//static void _InitAuxThread() {\n\t//\t//\t//rejected. Assoc may be changed frequently. The cache probably even does not contain icons of those file types.\n\t//\t//\t//\tAnyway cannot auto-clear if changed while cache not running.\n\t//\t//\t//\tAlso cannot auto-clear when icons changed not because of a changed assoc. Eg changed .exe icon, edited .ico, changed .lnk icon.\n\t//\t//\t//using var pidl = Pidl.FromString(\":: \");\n\t//\t//\t//var e = new api.SHChangeNotifyEntry { pidl = pidl.UnsafePtr, fRecursive = true };\n\t//\t//\t//Api.SHChangeNotifyRegister(script.AuxWnd_, api.SHCNRF_ShellLevel, api.SHCNE_ASSOCCHANGED, script.c_msg_IconImageCache_ClearAll, 1, e);\n\t//\t//\t////undocumented: is it important to call SHChangeNotifyDeregister when this process ends?\n\t//\t//}\n\t//}\n}\n"
  },
  {
    "path": "Au/Au.More/ImageUtil.cs",
    "content": "using System.Windows;\nusing System.Windows.Markup;\nusing System.Windows.Controls;\nusing System.Windows.Media;\nusing System.Windows.Media.Imaging;\n\nnamespace Au.More;\n\n/// <summary>\n/// Loads WPF and GDI+ images from file, resource or string.\n/// </summary>\n/// <seealso cref=\"ResourceUtil\"/>\npublic static partial class ImageUtil {\n\t/// <summary>\n\t/// Returns <c>true</c> if string starts with <c>\"image:\"</c>.\n\t/// </summary>\n\tpublic static bool HasImageStringPrefix(string s) => s.Starts(\"image:\");\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if string starts with <c>\"resource:\"</c>, <c>\"resources/\"</c>, <c>\"image:\"</c> (Base64 encoded image), <c>\"imagefile:\"</c> (file path), <c>\"*\"</c> (XAML icon name) or <c>\"&lt;\"</c> (possibly XAML image).\n\t/// </summary>\n\tpublic static bool HasImageOrResourcePrefix(string s) => s.Starts('*') || s.Starts('<') || s.Starts(\"image:\") || s.Starts(\"imagefile:\") || ResourceUtil.HasResourcePrefix(s);\n\t\n\t/// <summary>\n\t/// Loads image file data as stream from Base64 string.\n\t/// </summary>\n\t/// <param name=\"s\">Base64 encoded image string with prefix <c>\"image:\"</c>.</param>\n\t/// <exception cref=\"ArgumentException\">String does not start with <c>\"image:\"</c> or is invalid Base64.</exception>\n\t/// <exception cref=\"Exception\"><see cref=\"Convert2.BrotliDecompress\"/> exceptions (when compressed <c>.bmp</c>).</exception>\n\tpublic static MemoryStream LoadImageStreamFromString(string s) {\n\t\tif (!HasImageStringPrefix(s)) throw new ArgumentException(\"String must start with \\\"image:\\\".\");\n\t\tint start = 6; while (start < s.Length && s[start] <= ' ') start++; //can be eg \"image:\\r\\n...\"\n\t\tbool compressedBmp = s.Eq(start, \"WkJN\");\n\t\tif (compressedBmp) start += 4;\n\t\tint n = (int)((s.Length - start) * 3L / 4);\n\t\tvar b = new byte[n];\n\t\tif (!Convert.TryFromBase64Chars(s.AsSpan(start), b, out n)) throw new ArgumentException(\"Invalid Base64 string\");\n\t\tif (!compressedBmp) return new MemoryStream(b, 0, n, false);\n\t\treturn new MemoryStream(Convert2.BrotliDecompress(b.AsSpan(0, n)), false);\n\t}\n\t\n\t/// <summary>\n\t/// Loads GDI+ image from Base64 string.\n\t/// </summary>\n\t/// <param name=\"s\">Base64 encoded image string with prefix <c>\"image:\"</c>.</param>\n\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"LoadImageStreamFromString\"/> and <see cref=\"System.Drawing.Bitmap(Stream)\"/>.</exception>\n\tstatic System.Drawing.Bitmap _LoadGdipBitmapFromString(string s)\n\t\t=> new(LoadImageStreamFromString(s));\n\t\n\t/// <summary>\n\t/// Loads WPF image from Base64 string.\n\t/// </summary>\n\t/// <param name=\"s\">Base64 encoded image string with prefix <c>\"image:\"</c>.</param>\n\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"LoadImageStreamFromString\"/> and <see cref=\"BitmapFrame.Create(Stream)\"/>.</exception>\n\tstatic BitmapFrame _LoadWpfImageFromString(string s)\n\t\t=> BitmapFrame.Create(LoadImageStreamFromString(s));\n\t\n\t//not used in library\n\t///// <summary>\n\t///// Calls <see cref=\"LoadGdipBitmapFromString\"/> and handles exceptions. On exception returns <c>null</c> and optionally prints a warning.\n\t///// </summary>\n\t//public static System.Drawing.Bitmap TryLoadGdipBitmapFromString(string s, bool warning) {\n\t//\ttry { return LoadGdipBitmapFromString(s); }\n\t//\tcatch (Exception ex) { if (warning) print.warning(ex.ToStringWithoutStack()); }\n\t//\treturn null;\n\t//}\n\t\n\t///// <summary>\n\t///// Calls <see cref=\"LoadWpfImageFromString\"/> and handles exceptions. On exception returns <c>null</c> and optionally prints warning.\n\t///// </summary>\n\t//public static BitmapFrame TryLoadWpfImageFromString(string s, bool warning) {\n\t//\ttry { return LoadWpfImageFromString(s); }\n\t//\tcatch (Exception ex) { if (warning) print.warning(ex.ToStringWithoutStack()); }\n\t//\treturn null;\n\t//}\n\t\n\t/// <summary>\n\t/// Loads GDI+ image from file, resource or string.\n\t/// </summary>\n\t/// <param name=\"image\">\n\t/// Can be:\n\t/// <br/>• file path. Can have prefix <c>\"imagefile:\"</c>.\n\t/// <br/>• resource path that starts with <c>\"resources/\"</c> or has prefix <c>\"resource:\"</c> (<see cref=\"ResourceUtil.GetGdipBitmap\"/>)\n\t/// <br/>• Base64 encoded image string with prefix <c>\"image:\"</c>.\n\t/// </param>\n\t/// <param name=\"xaml\">If not <c>null</c>, supports XAML images. See <see cref=\"LoadGdipBitmapFromXaml\"/>.</param>\n\t/// <exception cref=\"Exception\">Depending on <i>image</i> string format, exceptions of <see cref=\"File.OpenRead(string)\"/>, <see cref=\"System.Drawing.Bitmap(Stream)\"/>, etc.</exception>\n\tpublic static System.Drawing.Bitmap LoadGdipBitmap(string image, (int dpi, SIZE? size)? xaml = null) {\n\t\tif (HasImageStringPrefix(image))\n\t\t\treturn _LoadGdipBitmapFromString(image);\n\t\tif (xaml != null && (image.Starts('<') || image.Ends(\".xaml\", true)))\n\t\t\treturn LoadGdipBitmapFromXaml(image, xaml.Value.dpi, xaml.Value.size);\n\t\tif (ResourceUtil.HasResourcePrefix(image))\n\t\t\treturn ResourceUtil.GetGdipBitmap(image);\n\t\tif (image.Starts(\"imagefile:\")) image = image[10..];\n\t\timage = pathname.normalize(image, folders.ThisAppImages);\n\t\t//return new(image); //no, the file remains locked until the Bitmap is disposed (documented, tested)\n\t\tusing var fs = File.OpenRead(image);\n\t\treturn new(fs);\n\t}\n\t\n\t/// <summary>\n\t/// Loads WPF image or icon from file, resource or string.\n\t/// </summary>\n\t/// <param name=\"image\">\n\t/// Can be:\n\t/// <br/>• file path. Can have prefix <c>\"imagefile:\"</c>.\n\t/// <br/>• resource path that starts with <c>\"resources/\"</c> or has prefix <c>\"resource:\"</c> (<see cref=\"ResourceUtil.GetWpfImage\"/>)\n\t/// <br/>• Base64 encoded image string with prefix <c>\"image:\"</c>.\n\t/// </param>\n\t/// <exception cref=\"Exception\"></exception>\n\tpublic static BitmapFrame LoadWpfImage(string image) {\n\t\tif (HasImageStringPrefix(image)) return _LoadWpfImageFromString(image);\n\t\tif (ResourceUtil.HasResourcePrefix(image)) return ResourceUtil.GetWpfImage(image);\n\t\tif (image.Starts(\"imagefile:\")) image = image[10..];\n\t\timage = pathname.normalize(image, folders.ThisAppImages, flags: PNFlags.CanBeUrlOrShell); //CanBeUrlOrShell: support \"pack:\"\n\t\treturn BitmapFrame.Create(new Uri(image));\n\t\t//rejected: support XAML and \"*iconName\". Possible but not easy. Probably would be blurred when autoscaled.\n\t}\n\t\n\t/// <summary>\n\t/// Loads WPF image element from file, resource or string. Supports xaml, png and other image formats supported by WPF.\n\t/// </summary>\n\t/// <param name=\"image\">\n\t/// Can be:\n\t/// <br/>• file path. Can be <c>.xaml</c>, <c>.png</c> etc. Supports environment variables etc, see <see cref=\"pathname.expand\"/>. Can have prefix <c>\"imagefile:\"</c>.\n\t/// <br/>• resource path that starts with <c>\"resources/\"</c> or has prefix <c>\"resource:\"</c>. This function calls <see cref=\"ResourceUtil.GetXamlObject\"/> if ends with <c>\".xaml\"</c>, else <see cref=\"ResourceUtil.GetWpfImage\"/>.\n\t/// <br/>• Base64 encoded image with prefix <c>\"image:\"</c>. See <see cref=\"LoadImageStreamFromString\"/>.\n\t/// <br/>• XAML string that starts with <c>\"&lt;\"</c>. For example from the <b>Icons</b> tool of LibreAutomate.\n\t/// <br/>• XAML icon name like <c>\"*Pack.Icon color\"</c> or <c>\"*Pack.Icon color @size\"</c> or <c>\"*Pack1.Icon1 color1; *Pack2.Icon2 color2 %8,8,,\"</c>. More info in Remarks.\n\t/// </param>\n\t/// <returns>\n\t/// If <i>image</i> is XAML icon name or starts with <c>\"&lt;\"</c> or ends with <c>\".xaml\"</c> (case-insensitive), returns new WPF element of type specified by the XAML root element (uses <see cref=\"XamlReader\"/>). Else returns <see cref=\"Image\"/> with <c>Source</c> = <see cref=\"BitmapFrame\"/> (uses <see cref=\"LoadWpfImage\"/>).\n\t/// </returns>\n\t/// <remarks>\n\t/// <i>image</i> can be an XAML icon name from the <b>Icons</b> tool of LibreAutomate (LA), like <c>\"*Pack.Icon color\"</c>. Full format: <c>\"[*&lt;library&gt;]*pack.name[ color][ @size][ %margin][;more icons]\"</c>. Here parts enclosed in <c>[]</c> are optional. The color, size and margin parts can be in any order.\n\t/// <br/>• color - <c>#RRGGBB</c> or color name (WPF). If 2 colors like <c>\"#008000|#00FF00\"</c>, the second color is for high contrast dark theme. If omitted, will use the system color of control text. Also can be like <c>\"#008000|\"</c> to use control text only for dark contrast theme, or <c>\"|#00FF00\"</c> for vice versa.\n\t/// <br/>• size - icon size 1 to 16, like <c>\"*Pack.Icon blue @12\"</c>. Can be used to make the displayed icon smaller or in some cases less blurry. It is the logical width and height of the icon rendered at the center of a box of logical size 16x16. To make icon bigger, instead set properties <c>Width</c> and <c>Height</c> of the returned element; or <see cref=\"MTBase.ImageSize\"/> for a toolbar or menu.\n\t/// <br/>• margin - icon margins inside a box of logical size 16x16. Format: <c>%left,top,right,bottom,stretch,snap</c>. All parts are optional. Examples: <c>\"*Pack.Icon blue %,,8,8\"</c>, <c>\"*Pack.Icon blue %8,8\"</c>, <c>\"*Pack.Icon blue %4,,4,,f\"</c>. The stretch part can be <c>f</c> (fill) or <c>m</c> (move); default is uniform. The snap part can be <c>p</c> (sets <c>SnapsToDevicePixels=True</c>). Can be used either margin or size, not both.\n\t/// <br/>• more icons - can be specified multiple icons separated by semicolon, like <c>\"*Pack1.Icon1 color1; *Pack2.Icon2 color2\"</c>. It allows to create multi-color icons (for example a \"filled\" icon of one color + an \"outline\" icon of another color) or to add a small overlay icon (eg to indicate disabled state) at a corner (use margin).\n\t/// <br/>• library - name of assembly containing the resource. If omitted, uses <see cref=\"Assembly.GetEntryAssembly\"/>.\n\t/// <br/>The LA compiler finds icon strings anywhere in code, gets their XAML from the database, and adds the XAML to the assembly as a string resource (see <b>Properties > Resource > Options</b>). This function gets the XAML from resources (<see cref=\"ResourceUtil.GetString\"/>). If fails, then tries to get XAML from database, and fails if LA isn't running. Uses <see cref=\"ScriptEditor.GetIcon\"/>.\n\t/// </remarks>\n\t/// <exception cref=\"Exception\"></exception>\n\tpublic static FrameworkElement LoadWpfImageElement(string image) {\n\t\tif (image.Starts('*')) {\n\t\t\timage = ScriptEditor.GetIcon(image, EGetIcon.IconNameToXaml) ?? throw new AuException(\"*get icon \" + image);\n\t\t}\n\t\tif (image.Starts('<')) return (FrameworkElement)XamlReader.Parse(image);\n\t\tif (image.Ends(\".xaml\", true)) {\n\t\t\tif (ResourceUtil.HasResourcePrefix(image)) return (FrameworkElement)ResourceUtil.GetXamlObject(image);\n\t\t\tif (image.Starts(\"imagefile:\")) image = image[10..];\n\t\t\tusing var stream = File.OpenRead(image);\n\t\t\treturn (FrameworkElement)XamlReader.Load(stream);\n\t\t} else {\n\t\t\tvar bf = LoadWpfImage(image);\n\t\t\treturn new Image { Source = bf };\n\t\t}\n\t\t//Could set UseLayoutRounding=true as a workaround for blurry images, but often it does not work and have to be set on parent element.\n\t\t//\tThen does not work even if wrapped eg in a Border with UseLayoutRounding.\n\t}\n\t\n\t/// <summary>\n\t/// Loads GDI+ image from WPF XAML file or string.\n\t/// </summary>\n\t/// <param name=\"image\">XAML file, resource or string. See <see cref=\"LoadWpfImageElement\"/>.</param>\n\t/// <param name=\"dpi\">DPI of window that will display the image.</param>\n\t/// <param name=\"size\">Final image size in logical pixels (not DPI-scaled). If <c>null</c>, uses element's <c>DesiredSize</c> property, max 1024x1024.</param>\n\t/// <returns>New <c>Bitmap</c>. Note: its pixel format is <c>Format32bppPArgb</c> (premultiplied ARGB).</returns>\n\t/// <exception cref=\"Exception\"></exception>\n\t/// <remarks>\n\t/// Calls <see cref=\"LoadWpfImageElement\"/> and <see cref=\"ConvertWpfImageElementToGdipBitmap\"/>.\n\t/// Don't use the <c>Tag</c> property of the bitmap. It keeps bitmap data.\n\t/// </remarks>\n\t[MethodImpl(MethodImplOptions.NoInlining)]\n\tpublic static System.Drawing.Bitmap LoadGdipBitmapFromXaml(string image, int dpi, SIZE? size = null) {\n\t\tvar e = LoadWpfImageElement(image);\n\t\t//s_cwt.Add(e, new());\n\t\treturn ConvertWpfImageElementToGdipBitmap(e, dpi, size);\n\t}\n\t\n\t/// <summary>\n\t/// Converts WPF image element to GDI+ image.\n\t/// </summary>\n\t/// <param name=\"e\">For example <see cref=\"Viewbox\"/>.</param>\n\t/// <param name=\"dpi\">DPI of window that will display the image.</param>\n\t/// <param name=\"size\">\n\t/// Final image size in logical pixels (not DPI-scaled).\n\t/// If <c>null</c>, uses element's <c>DesiredSize</c> property, max 1024x1024.\n\t/// If not <c>null</c>, sets element's <c>Width</c> and <c>Height</c>; the element should not be used in UI.\n\t/// </param>\n\t/// <returns>New <c>Bitmap</c>. Note: its pixel format is <c>Format32bppPArgb</c> (premultiplied ARGB).</returns>\n\tpublic static unsafe System.Drawing.Bitmap ConvertWpfImageElementToGdipBitmap(FrameworkElement e, int dpi, SIZE? size = null) {\n\t\tbool measured = e.IsMeasureValid;\n\t\tif (size != null) {\n\t\t\tmeasured = false;\n\t\t\te.Width = size.Value.width;\n\t\t\te.Height = size.Value.height;\n\t\t}\n\t\tif (!measured) e.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));\n\t\tbool arranged = measured && e.IsArrangeValid;\n\t\tif (!arranged) e.Arrange(new Rect(e.DesiredSize));\n\t\tif (!arranged) e.UpdateLayout(); //prevent memory leak\n\t\tif (size == null) {\n\t\t\tvar z = e.DesiredSize; //if using RenderSize or ActualX, if element height!=width, draws in wrong place, clipped\n\t\t\tsize = new(Math.Min(1024d, z.Width).ToInt(), Math.Min(1024d, z.Height).ToInt());\n\t\t}\n\t\tvar (wid, hei) = Dpi.Scale(size.Value, dpi);\n\t\tvar rtb = new RenderTargetBitmap(wid, hei, dpi, dpi, PixelFormats.Pbgra32);\n\t\t//var rtb = t_rtb ??= new RenderTargetBitmap(wid, hei, dpi, dpi, PixelFormats.Pbgra32); rtb.Clear(); //not better\n\t\t//note: if Bgra32, throws exception \"'Bgra32' PixelFormat is not supported for this operation\".\n\t\trtb.Render(e);\n\t\tint stride = wid * 4, msize = hei * stride;\n\t\tvar b = new System.Drawing.Bitmap(wid, hei, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);\n\t\tusing var d = b.Data(System.Drawing.Imaging.ImageLockMode.ReadWrite);\n\t\trtb.CopyPixels(new(0, 0, wid, hei), d.Scan0, msize, stride);\n\t\tb.SetResolution(dpi, dpi);\n\t\treturn b;\n\t\t//tested: GC OK. Don't need GC_.AddObjectMemoryPressure. WPF makes enough garbage to trigger GC when need.\n\t}\n\t\n\t/// <summary>\n\t/// Converts WPF image element to native icon file data.\n\t/// </summary>\n\t/// <param name=\"stream\">Stream to write icon file data. Writes from start.</param>\n\t/// <param name=\"e\">Image element. See <see cref=\"LoadWpfImageElement\"/>.</param>\n\t/// <param name=\"sizes\">Sizes of icon images to add. For example 16, 24, 32, 48, 64. Sizes can be 1 to 256 inclusive.</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">An invalid size.</exception>\n\t/// <exception cref=\"Exception\"></exception>\n\tinternal static unsafe void ConvertWpfImageElementToIcon_(Stream stream, FrameworkElement e, int[] sizes) {\n\t\tstream.Position = Math2.AlignUp(sizeof(Api.NEWHEADER) + sizeof(Api.ICONDIRENTRY) * sizes.Length, 4);\n\t\tvar a = stackalloc Api.ICONDIRENTRY[sizes.Length];\n\t\tfor (int i = 0; i < sizes.Length; i++) {\n\t\t\tint size = sizes[i];\n\t\t\tif (size < 1 || size > 256) throw new ArgumentOutOfRangeException();\n\t\t\tusing var b = ImageUtil.ConvertWpfImageElementToGdipBitmap(e, 96, (size, size));\n\t\t\tint pos = (int)stream.Position;\n\t\t\tb.Save(stream, System.Drawing.Imaging.ImageFormat.Png);\n\t\t\tbyte bsize = (byte)(size == 256 ? 0 : checked((byte)size));\n\t\t\ta[i] = new Api.ICONDIRENTRY { bWidth = bsize, bHeight = bsize, wBitCount = 32, dwBytesInRes = (int)stream.Position - pos, dwImageOffset = pos };\n\t\t}\n\t\tvar posEnd = stream.Position;\n\t\tstream.Position = 0;\n\t\tvar h = new Api.NEWHEADER { wResType = 1, wResCount = (ushort)sizes.Length };\n\t\tstream.Write(new(&h, sizeof(Api.NEWHEADER)));\n\t\tstream.Write(new(a, sizeof(Api.ICONDIRENTRY) * sizes.Length));\n\t\tstream.Position = posEnd;\n\t}\n\t\n\t/// <summary>\n\t/// Converts WPF image element to native icon file.\n\t/// </summary>\n\t/// <inheritdoc cref=\"ConvertWpfImageElementToIcon_(Stream, FrameworkElement, int[])\"/>\n\tinternal static void ConvertWpfImageElementToIcon_(string icoFile, FrameworkElement e, int[] sizes) {\n\t\ticoFile = pathname.NormalizeMinimally_(icoFile);\n\t\tusing var stream = File.OpenWrite(icoFile);\n\t\tConvertWpfImageElementToIcon_(stream, e, sizes);\n\t}\n\t\n\t/// <summary>\n\t/// Converts XAML icon to GDI+ icon.\n\t/// </summary>\n\t/// <param name=\"s\">Icon name or XAML etc. See <see cref=\"ImageUtil.LoadWpfImageElement\"/>.</param>\n\t/// <param name=\"size\"></param>\n\t/// <returns>If fails, prints warning and returns null.</returns>\n\tinternal static System.Drawing.Icon XamlIconToGdipIcon_(string s, int size) {\n\t\ttry {\n\t\t\tvar e = ImageUtil.LoadWpfImageElement(s);\n\t\t\tvar ms = new MemoryStream();\n\t\t\tImageUtil.ConvertWpfImageElementToIcon_(ms, e, [size]);\n\t\t\tms.Position = 0;\n\t\t\treturn new System.Drawing.Icon(ms);\n\t\t}\n\t\tcatch (Exception ex) { print.warning(ex); return null; }\n\t}\n}\n"
  },
  {
    "path": "Au/Au.More/KeyToTextConverter.cs",
    "content": "namespace Au.More;\n\n/// <summary>\n/// Converts virtual-key codes to text characters.\n/// </summary>\n/// <remarks>\n/// To record user input, can be used <see cref=\"WindowsHook.Keyboard(Action{HookData.Keyboard}, bool, bool)\"/>.\n/// When recording user input, use same <c>KeyToTextConverter</c> variable for all keys.\n/// </remarks>\npublic class KeyToTextConverter\n{\n\t(KKey vk, KMod mod, uint sc, nint hkl) _deadKey;\n\n\t/// <summary>\n\t/// Converts a virtual-key code to text.\n\t/// </summary>\n\t/// <param name=\"text\">Receives text. Can be 1 character <i>c</i>, or string <i>s</i> with 2 or more characters. Receives default if this function returns <c>false</c> or if the key is a dead key.</param>\n\t/// <param name=\"vk\">Virtual-key code.</param>\n\t/// <param name=\"sc\">Scan code.</param>\n\t/// <param name=\"mod\">Modifier keys (<c>Shift</c> etc). See <see cref=\"keys.getMod(KMod)\"/>.</param>\n\t/// <param name=\"threadId\">Thread id of the focused or active window. Need for keyboard layout. If 0, uses this thread.</param>\n\t/// <returns><c>true</c> if it's a text key or dead key.</returns>\n\tpublic unsafe bool Convert(out (char c, string s) text, KKey vk, uint sc, KMod mod, int threadId) {\n\t\ttext = default;\n\t\tif (vk == KKey.Packet) {\n\t\t\ttext.c = (char)sc;\n\t\t} else {\n\t\t\tif (!IsPossiblyChar_(mod, vk)) return false;\n\n\t\t\tvar hkl = Api.GetKeyboardLayout(threadId);\n\t\t\tvar ks = stackalloc byte[256];\n\t\t\t_SetKS(mod);\n\t\t\tvar c = stackalloc char[8];\n\t\t\tbool win10 = osVersion.minWin10_1607; //the API resets dead key etc, but on new OS flag 4 prevents it\n\t\t\tint n = Api.ToUnicodeEx((uint)vk, sc, ks, c, 8, win10 ? 4u : 0u, hkl);\n\t\t\tif (n == 1 && c[0] < ' ') {\n\t\t\t\tDebug_.Print($\"{(int)c[0]}, {c[0]}\");\n\t\t\t\tif (c[0] == '\\r') c[n++] = '\\n'; else if (c[0] is not ('\\t' or '\\n')) n = 0;\n\t\t\t}\n\t\t\tif (n == 1) text.c = c[0]; else if (n > 0) text.s = new(c, 0, n);\n\t\t\tif (!win10) { //if need, set dead key again\n\t\t\t\tif (_deadKey.vk != 0 && _deadKey.hkl == hkl) {\n\t\t\t\t\t_SetKS(_deadKey.mod);\n\t\t\t\t\tApi.ToUnicodeEx((uint)_deadKey.vk, _deadKey.sc, ks, c, 8, 0, hkl);\n\t\t\t\t\t_deadKey.vk = 0;\n\t\t\t\t} else if (n < 0) {\n\t\t\t\t\t_deadKey = (vk, mod, sc, hkl);\n\t\t\t\t\tApi.ToUnicodeEx((uint)vk, sc, ks, c, 8, 0, hkl);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (n == 0) return false; //non-char key\n\n\t\t\tvoid _SetKS(KMod m) {\n\t\t\t\tks[(int)KKey.Shift] = (byte)((0 != (m & KMod.Shift)) ? 0x80 : 0);\n\t\t\t\tks[(int)KKey.Ctrl] = (byte)((0 != (m & KMod.Ctrl)) ? 0x80 : 0);\n\t\t\t\tks[(int)KKey.Alt] = (byte)((0 != (m & KMod.Alt)) ? 0x80 : 0);\n\t\t\t\t//ks[(int)KKey.Win] = (byte)((0 != (m & KMod.Win)) ? 0x80 : 0);\n\t\t\t\tks[(int)KKey.CapsLock] = (byte)(keys.isCapsLock ? 1 : 0); //don't need this for num lock\n\t\t\t}\n\t\t}\n\t\treturn true;\n\n\t\t//Notes:\n\t\t//1. Does not work with eg Chinese input method.\n\t\t//2. Catches everything that would later be changed by the app, or by a next hook, etc.\n\t\t//3. Don't know how to get Alt+numpad characters. Ignore them.\n\t\t//\tOn Alt up could call tounicodeex with sc with flag 0x8000. It gets the char, but resets keyboard state, and the char is not typed.\n\t\t//4. In console windows does not work with Unicode characters.\n\n\t\t//if(MapVirtualKeyEx(vk, MAPVK_VK_TO_CHAR, hkl)&0x80000000) { print.it(\"DEAD\"); return -1; } //this cannot be used because resets dead key\n\t}\n\n\t/// <summary>\n\t/// Clears internal fields such as dead key state.\n\t/// </summary>\n\tpublic void Clear() {\n\t\t_deadKey = default;\n\t}\n\n\t/// <summary>\n\t/// Returns <c>true</c> if the key + modifiers could generate a character, including <c>Enter</c> and <c>Tab</c> but not other control characters.\n\t/// </summary>\n\tinternal static bool IsPossiblyChar_(KMod m, KKey k) {\n\t\tif (m.Has(KMod.Win) || (m & ~KMod.Shift) == KMod.Ctrl || m == (KMod.Alt | KMod.Shift)) return false;\n\t\tif (k is KKey.Back or KKey.Escape) return false;\n\t\treturn true;\n\t}\n}\n"
  },
  {
    "path": "Au/Au.More/Math2.cs",
    "content": "namespace Au.More;\n\n/// <summary>\n/// Simple calculation functions.\n/// </summary>\n//[DebuggerStepThrough]\npublic static class Math2 {\n\t/// <summary>\n\t/// Creates <c>uint</c> by placing <c>(ushort)loWord</c> in bits 1-16 and <c>(ushort)hiWord</c> in bits 17-32.\n\t/// Like C macro <c>MAKELONG</c>, <c>MAKEWPARAM</c>, <c>MAKELPARAM</c>, <c>MAKELRESULT</c>.\n\t/// </summary>\n\t/// <returns>The return value is of type <c>nint</c>. It can be used with Windows message API as <i>lParam</i> or <i>wParam</i> or return value.</returns>\n\tpublic static nint MakeLparam(int loWord, int hiWord) => MakeLparam((uint)loWord, (uint)hiWord);\n\t//Returns nint, because usually used as sendmessage etc parameter. If uint, would need to explicitly cast to nint. If somebody casts to int, the result may be incorrect, ie negative.\n\t\n\t//Why named MakeLparam, MakeWord, LoWord, HiWord:\n\t//\t1. Like C macros MAKELPARAM/MAKEWORD/LOWORD/HIWORD.\n\t//\t2. MakeLparam used mostly as lParam of sendmessage etc.\n\t\n\t/// <inheritdoc cref=\"MakeLparam(int, int)\"/>\n\tpublic static nint MakeLparam(uint loWord, uint hiWord) => (nint)(((hiWord & 0xffff) << 16) | (loWord & 0xffff));\n\t\n\t/// <summary>\n\t/// Creates <c>uint</c> by placing <c>(ushort)p.x</c> in bits 1-16 and <c>(ushort)p.y</c> in bits 17-32.\n\t/// Like C macro <c>MAKELONG</c>, <c>MAKEWPARAM</c>, <c>MAKELPARAM</c>, <c>MAKELRESULT</c>.\n\t/// </summary>\n\t/// <returns>The return value is of type <c>nint</c>. It can be used with Windows message API as <i>lParam</i> or <i>wParam</i> or return value.</returns>\n\tpublic static nint MakeLparam(POINT p) => MakeLparam((uint)p.x, (uint)p.y);\n\t\n\t/// <summary>\n\t/// Creates <c>ushort</c> by placing <c>(byte)loByte</c> in bits 1-8 and <c>(byte)hiByte</c> in bits 9-16.\n\t/// Like C macro <c>MAKEWORD</c>.\n\t/// </summary>\n\tpublic static ushort MakeWord(int loByte, int hiByte) => MakeWord((uint)loByte, (uint)hiByte);\n\t\n\t/// <inheritdoc cref=\"MakeWord(int, int)\"/>\n\tpublic static ushort MakeWord(uint loByte, uint hiByte) => (ushort)(((hiByte & 0xff) << 8) | (loByte & 0xff));\n\t\n\t/// <summary>\n\t/// Gets bits 1-16 as <c>ushort</c>.\n\t/// Like C macro <c>LOWORD</c>.\n\t/// </summary>\n\t/// <remarks>\n\t/// The parameter is interpreted as <c>uint</c>. The parameter type <c>nint</c> allows to avoid explicit cast from <c>int</c> and <c>IntPtr</c>.\n\t/// </remarks>\n\tpublic static ushort LoWord(nint x) => (ushort)((uint)x & 0xFFFF);\n\t\n\t/// <summary>\n\t/// Gets bits 17-32 as <c>ushort</c>.\n\t/// Like C macro <c>HIWORD</c>.\n\t/// </summary>\n\t/// <inheritdoc cref=\"LoWord(nint)\"/>\n\tpublic static ushort HiWord(nint x) => (ushort)((uint)x >> 16);\n\t\n\t/// <summary>\n\t/// Gets bits 1-16 as <c>short</c>.\n\t/// Like C macro <c>GET_X_LPARAM</c>.\n\t/// </summary>\n\t/// <inheritdoc cref=\"LoWord(nint)\"/>\n\tpublic static short LoShort(nint x) => (short)((uint)x & 0xFFFF);\n\t\n\t/// <summary>\n\t/// Gets bits 17-32 as <c>short</c>.\n\t/// Like C macro <c>GET_Y_LPARAM</c>.\n\t/// </summary>\n\t/// <inheritdoc cref=\"LoWord(nint)\"/>\n\tpublic static short HiShort(nint x) => (short)((uint)x >> 16);\n\t\n\t/// <summary>\n\t/// Gets bits 1-8 as <c>byte</c>.\n\t/// Like C macro <c>LOBYTE</c>.\n\t/// </summary>\n\tpublic static byte LoByte(ushort x) => (byte)((uint)x & 0xFF);\n\t\n\t/// <summary>\n\t/// Gets bits 9-16 as <c>byte</c>.\n\t/// Like C macro <c>HIBYTE</c>.\n\t/// </summary>\n\tpublic static byte HiByte(ushort x) => (byte)((uint)x >> 8);\n\t\n\t/// <summary>\n\t/// Converts <c>nint</c> containing x and y coordinates to <see cref=\"POINT\"/>.\n\t/// </summary>\n\tpublic static POINT NintToPOINT(nint xy) => new(LoShort(xy), HiShort(xy));\n\t\n\t/// <summary>\n\t/// Returns <c>number * multiply / divide</c>.\n\t/// Multiplies without overflow and rounds up or down to the nearest integer.\n\t/// </summary>\n\t/// <exception cref=\"OverflowException\"></exception>\n\t/// <exception cref=\"DivideByZeroException\"></exception>\n\tpublic static int MulDiv(int number, int multiply, int divide) {\n\t\tif (divide == multiply) return number;\n\t\tlong r = (long)number * multiply;\n\t\tint d = divide / 2; if (r < 0 == divide < 0) r += d; else r -= d; //round\n\t\treturn checked((int)(r / divide));\n\t\t\n\t\t//This code produces the same results as API MulDiv. Tested with millions of random and edge values. Faster.\n\t\t//The only difference, API does not support int.MinValue.\n\t}\n\t//public static int MulDiv(int number, int multiply, int divide) => Api.MulDiv(number, multiply, divide);\n\t\n\t/// <summary>\n\t/// Calculates how many % of <i>whole</i> is <i>part</i>: <c>100L * part / whole</c>.\n\t/// </summary>\n\t/// <param name=\"whole\"></param>\n\t/// <param name=\"part\"></param>\n\t/// <param name=\"canRoundUp\">Round down or up. If <c>false</c> (default), can only round down.</param>\n\t/// <exception cref=\"OverflowException\"></exception>\n\tpublic static int PercentFromValue(int whole, int part, bool canRoundUp = false)\n\t\t=> whole == default ? default : (canRoundUp ? MulDiv(100, part, whole) : checked((int)(100L * part / whole)));\n\t\n\t/// <summary>\n\t/// Calculates how many % of <i>whole</i> is <i>part</i>: <c>100 * part / whole</c>.\n\t/// </summary>\n\tpublic static double PercentFromValue(double whole, double part)\n\t\t=> whole == default ? default : (100.0 * part / whole);\n\t\n\t/// <summary>\n\t/// Returns <i>percent</i> % of <i>whole</i>: <c>(long)whole * percent / 100</c>.\n\t/// </summary>\n\t/// <param name=\"whole\"></param>\n\t/// <param name=\"percent\"></param>\n\t/// <param name=\"canRoundUp\">Use <see cref=\"MulDiv\"/>, which can round down or up. If <c>false</c> (default), can only round down.</param>\n\t/// <exception cref=\"OverflowException\"></exception>\n\tpublic static int PercentToValue(int whole, int percent, bool canRoundUp = false)\n\t\t=> canRoundUp ? MulDiv(whole, percent, 100) : checked((int)((long)whole * percent / 100L));\n\t\n\t/// <summary>\n\t/// Returns <i>percent</i> % of <i>whole</i>: <c>whole * percent / 100</c>.\n\t/// </summary>\n\tpublic static double PercentToValue(double whole, double percent)\n\t\t=> whole * percent / 100.0;\n\t\n\t/// <summary>\n\t/// If <i>value</i> is divisible by <i>alignment</i>, returns <i>value</i>. Else returns the nearest bigger number that is divisible by <i>alignment</i>.\n\t/// </summary>\n\t/// <param name=\"value\">An integer value.</param>\n\t/// <param name=\"alignment\">Alignment. Must be a power of two (2, 4, 8, 16...).</param>\n\t/// <remarks>\n\t/// For example if <i>alignment</i> is 4, returns 4 if <i>value</i> is 1-4, returns 8 if <i>value</i> is 5-8, returns 12 if <i>value</i> is 9-10, and so on.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// for (int i = 0; i <= 20; i++) print.it(i, Math2.AlignUp(i, 4));\n\t/// ]]></code>\n\t/// </example>\n\tpublic static int AlignUp(int value, uint alignment) => (int)AlignUp((uint)value, alignment);\n\t\n\t/// <inheritdoc cref=\"AlignUp(int, uint)\"/>\n\tpublic static uint AlignUp(uint value, uint alignment) => (value + (alignment - 1)) & ~(alignment - 1);\n\t//shorter: (value + --alignment) & ~alignment. But possibly less optimized. Now (alignment - 1) and ~(alignment - 1) usually are constants.\n\t\n\t/// <summary>\n\t/// Swaps values of two variables: <c>T t = a; a = b; b = t;</c>\n\t/// </summary>\n\tpublic static void Swap<T>(ref T a, ref T b) {\n\t\tT t = a; a = b; b = t;\n\t}\n\t\n\t/// <summary>\n\t/// Swaps two ranges of bits.\n\t/// </summary>\n\t/// <param name=\"value\"></param>\n\t/// <param name=\"i\">Position of first range of bits.</param>\n\t/// <param name=\"j\">Position of second range of bits.</param>\n\t/// <param name=\"n\">Number of bits in each range.</param>\n\tpublic static int SwapBits(int value, int i, int j, int n) => (int)SwapBits((uint)value, i, j, n);\n\t\n\t/// <summary>\n\t/// Swaps two ranges of bits.\n\t/// </summary>\n\t/// <param name=\"value\"></param>\n\t/// <param name=\"i\">Position of first range of bits.</param>\n\t/// <param name=\"j\">Position of second range of bits.</param>\n\t/// <param name=\"n\">Number of bits in each range.</param>\n\tpublic static uint SwapBits(uint value, int i, int j, int n) {\n\t\t// http://graphics.stanford.edu/~seander/bithacks.html#SwappingBitsXOR\n\t\tuint x = ((value >> i) ^ (value >> j)) & ((1U << n) - 1); // XOR temporary\n\t\treturn value ^ ((x << i) | (x << j));\n\t}\n\t\n\t//rejected. Too simple and does not save any code. Also would need generic, for enum too.\n\t///// <summary>\n\t///// Clears <i>oldFlags</i> bits specified in <i>mask</i> and adds <i>newFlags</i> bits specified in <i>mask</i>.\n\t///// </summary>\n\t//int SetFlagsMasked(int oldFlags, int newFlags, int mask) => (oldFlags&~mask) | (newFlags&mask);\n\t\n\t/// <summary>\n\t/// Calculates angle degrees from coordinates x and y.\n\t/// </summary>\n\tpublic static double AngleFromXY(int x, int y) => Math.Atan2(y, x) * (180 / Math.PI);\n\t\n\t/// <summary>\n\t/// Calculates distance between two points.\n\t/// </summary>\n\tpublic static double Distance(POINT p1, POINT p2) {\n\t\tif (p1.y == p2.y) return Math.Abs(p2.x - p1.x); //horizontal line\n\t\tif (p1.x == p2.x) return Math.Abs(p2.y - p1.y); //vertical line\n\t\t\n\t\tlong dx = p2.x - p1.x, dy = p2.y - p1.y;\n\t\treturn Math.Sqrt(dx * dx + dy * dy);\n\t}\n\t\n\t/// <summary>\n\t/// Calculates distance between rectangle and point.\n\t/// </summary>\n\t/// <returns>If the point is outside, returns the nearest distance, else 0.</returns>\n\tpublic static double Distance(RECT r, POINT p) {\n\t\tr.Normalize(swap: true);\n\t\tif (r.Contains(p)) return 0;\n\t\tint x = p.x < r.left ? r.left : (p.x > r.right ? r.right : p.x);\n\t\tint y = p.y < r.top ? r.top : (p.y > r.bottom ? r.bottom : p.y);\n\t\treturn Distance((x, y), p);\n\t}\n}\n"
  },
  {
    "path": "Au/Au.More/MemoryBitmap.cs",
    "content": "namespace Au.More;\n\n/// <summary>\n/// Creates and manages native bitmap handle and memory DC (GDI device context).\n/// The bitmap is selected in the DC.\n/// </summary>\npublic class MemoryBitmap : IDisposable {\n\tIntPtr _dc, _bm, _oldbm;\n\tbool _disposed;\n\n\t/// <summary>\n\t/// DC handle.\n\t/// </summary>\n\tpublic IntPtr Hdc => _dc;\n\n\t/// <summary>\n\t/// Bitmap handle.\n\t/// </summary>\n\tpublic IntPtr Hbitmap => _bm;\n\n\t/// <summary>\n\t/// Does nothing. Later you can call <see cref=\"Create\"/> or <see cref=\"Attach\"/>.\n\t/// </summary>\n\tpublic MemoryBitmap() { }\n\n\t/// <summary>\n\t/// Calls <see cref=\"Create\"/>.\n\t/// </summary>\n\t/// <exception cref=\"ArgumentException\"><i>width</i> or <i>height</i> is less than 1.</exception>\n\t/// <exception cref=\"AuException\">Failed. Probably there is not enough memory for bitmap of specified size (need <c>with*height*4</c> bytes).</exception>\n\tpublic MemoryBitmap(int width, int height) {\n\t\tif (width <= 0 || height <= 0) throw new ArgumentException();\n\t\tif (!Create(width, height)) throw new AuException(\"*create memory bitmap of specified size\");\n\t}\n\n\t//rejected: not obvious, whether it attaches or copies. Also, attaching is rarely used.\n\t///// <summary>\n\t///// Calls <see cref=\"Attach\"/>.\n\t///// </summary>\n\t//public MemoryBitmap(IntPtr hBitmap)\n\t//{\n\t//\tAttach(hBitmap);\n\t//}\n\n\t///\n\tprotected virtual void Dispose(bool disposing) {\n\t\tif (_disposed) return;\n\t\t_disposed = true;\n\t\tDelete();\n\t}\n\n\t/// <summary>\n\t/// Deletes the bitmap and DC.\n\t/// </summary>\n\tpublic void Dispose() {\n\t\tDispose(true);\n\t\tGC.SuppressFinalize(this);\n\t}\n\n\t///\n\t~MemoryBitmap() => Dispose(false);\n\t//Calls DeleteDC. MSDN says that ReleaseDC must be called from the same thread. But does not say it about DeleteDC and others.\n\t//\tTested: DeleteDC returns true in finalizer (other thread).\n\n\t/// <summary>\n\t/// Deletes the bitmap and DC.\n\t/// </summary>\n\tpublic void Delete() {\n\t\tif (_dc == default) return;\n\t\tif (_bm != default) {\n\t\t\tApi.SelectObject(_dc, _oldbm);\n\t\t\tApi.DeleteObject(_bm);\n\t\t\t_bm = default;\n\t\t}\n\t\tApi.DeleteDC(_dc);\n\t\t_dc = default;\n\t}\n\n\t/// <summary>\n\t/// Creates new memory DC and bitmap of specified size and selects it into the DC.\n\t/// </summary>\n\t/// <returns><c>false</c> if failed. In any case deletes previous bitmap and DC.</returns>\n\t/// <param name=\"width\">Width, pixels. Must be > 0.</param>\n\t/// <param name=\"height\">Height, pixels. Must be > 0.</param>\n\tpublic bool Create(int width, int height) {\n\t\tif (_disposed) throw new ObjectDisposedException(nameof(MemoryBitmap));\n\t\tusing var dcs = new ScreenDC_();\n\t\tAttach(Api.CreateCompatibleBitmap(dcs, width, height));\n\t\treturn _bm != default;\n\t}\n\n\t/// <summary>\n\t/// Sets this variable to manage an existing bitmap.\n\t/// Selects the bitmap into a memory DC.\n\t/// Deletes previous bitmap and DC.\n\t/// </summary>\n\t/// <param name=\"hBitmap\">Native bitmap handle.</param>\n\tpublic void Attach(IntPtr hBitmap) {\n\t\tif (_disposed) throw new ObjectDisposedException(nameof(MemoryBitmap));\n\t\tDelete();\n\t\tif (hBitmap != default) {\n\t\t\t_dc = Api.CreateCompatibleDC(default);\n\t\t\t_oldbm = Api.SelectObject(_dc, _bm = hBitmap);\n\t\t}\n\t}\n\n\t/// <summary>\n\t/// Deletes memory DC, clears this variable and returns its bitmap (native bitmap handle).\n\t/// The returned bitmap is not selected into a DC. Will need to delete it with API <ms>DeleteObject</ms>.\n\t/// </summary>\n\tpublic IntPtr Detach() {\n\t\tIntPtr bret = _bm;\n\t\tif (_bm != default) {\n\t\t\tApi.SelectObject(_dc, _oldbm);\n\t\t\tApi.DeleteDC(_dc);\n\t\t\t_dc = default; _bm = default;\n\t\t}\n\t\treturn bret;\n\t}\n}\n"
  },
  {
    "path": "Au/Au.More/MemoryUtil.cs",
    "content": "namespace Au.More;\n\n/// <summary>\n/// Allocates memory from native heap of this process using heap API.\n/// Also has more functions to work with memory: copy, move, virtual alloc.\n/// </summary>\n/// <remarks>\n/// Uses the common heap of this process, API <ms>GetProcessHeap</ms>.\n/// </remarks>\npublic static unsafe class MemoryUtil {\n\tstatic IntPtr _processHeap = Api.GetProcessHeap();\n\t\n\t/// <summary>\n\t/// Allocates new memory block and returns its address.\n\t/// </summary>\n\t/// <param name=\"size\">Byte count.</param>\n\t/// <param name=\"zeroInit\">Set all bytes = 0.</param>\n\t/// <exception cref=\"OutOfMemoryException\">Failed. Probably <i>size</i> is too big.</exception>\n\t/// <remarks>\n\t/// Calls API <ms>HeapAlloc</ms>.\n\t/// The memory is unmanaged and will not be freed automatically. Always call <see cref=\"Free\"/> when done. Call <see cref=\"ReAlloc\"/> or <see cref=\"FreeAlloc\"/> if need to resize.\n\t/// </remarks>\n\tpublic static byte* Alloc(nint size, bool zeroInit = false)\n\t\t=> _ReAllocBytes(null, size, zeroInit);\n\t\n\t/// <param name=\"count\">Count of elements of type <c>T</c>.</param>\n\t/// <inheritdoc cref=\"Alloc(nint, bool)\"/>\n\tpublic static T* Alloc<T>(nint count, bool zeroInit = false) where T : unmanaged\n\t\t=> (T*)_ReAllocBytes(null, count * sizeof(T), zeroInit);\n\t\n\t//Rejected. With the above overload the calling code is easier to read. Not so often used.\n\t//public static void Alloc<T>(out T* mem, nint count, bool zeroInit = false) where T : unmanaged\n\t//\t=> mem = (T*)_ReAllocBytes(null, count * sizeof(T), zeroInit);\n\t\n\tstatic byte* _ReAllocBytes(void* mem, nint size, bool zeroInit = false) {\n\t\tuint flag = zeroInit ? 8u : 0u;\n\t\tfor (int i = 0; i < 5; i++) {\n\t\t\tif (i > 0) {\n\t\t\t\tGC.Collect();\n\t\t\t\tGC.WaitForPendingFinalizers();\n\t\t\t\tThread.Sleep(i * 100);\n\t\t\t}\n\t\t\tvoid* r;\n\t\t\tif (mem == null) r = Api.HeapAlloc(_processHeap, flag, size);\n\t\t\telse r = Api.HeapReAlloc(_processHeap, flag, mem, size);\n\t\t\tif (r != null) return (byte*)r;\n\t\t}\n\t\tthrow new OutOfMemoryException();\n\t\t\n\t\t//note: don't need GC.AddMemoryPressure.\n\t\t//\tNative memory usually is used for temporary buffers etc and is soon released eg with try/finally.\n\t\t//\tMarshal.AllocHGlobal does not do it too.\n\t}\n\t\n\t/// <summary>\n\t/// Reallocates a memory block to make it bigger or smaller.\n\t/// </summary>\n\t/// <param name=\"mem\">Input: old memory address; if <c>null</c>, allocates new memory like <see cref=\"Alloc{T}\"/>. Output: new memory address. Unchanged if exception.</param>\n\t/// <param name=\"count\">New count of elements of type <c>T</c>.</param>\n\t/// <param name=\"zeroInit\">When size is growing, set all added bytes = 0.</param>\n\t/// <exception cref=\"OutOfMemoryException\">Failed. Probably <i>count</i> is too big.</exception>\n\t/// <remarks>\n\t/// Calls API <ms>HeapReAlloc</ms> or <ms>HeapAlloc</ms>.\n\t/// Preserves data in <c>Math.Min(oldCount, newCount)</c> elements of old memory (copies from old memory if need).\n\t/// The memory is unmanaged and will not be freed automatically. Always call <see cref=\"Free\"/> when done. Call <c>ReAlloc</c> or <see cref=\"FreeAlloc\"/> if need to resize.\n\t/// </remarks>\n\tpublic static void ReAlloc<T>(ref T* mem, nint count, bool zeroInit = false) where T : unmanaged\n\t\t=> mem = (T*)_ReAllocBytes(mem, count * sizeof(T), zeroInit);\n\t\n\t/// <summary>\n\t/// Frees a memory block.\n\t/// Does nothing if <i>mem</i> is <c>null</c>.\n\t/// </summary>\n\t/// <remarks>\n\t/// Calls API <ms>HeapFree</ms>.\n\t/// </remarks>\n\tpublic static void Free(void* mem) {\n\t\tif (mem != null) Api.HeapFree(_processHeap, 0, mem);\n\t}\n\t\n\t/// <summary>\n\t/// Frees a memory block (if not <c>null</c>) and allocates new.\n\t/// </summary>\n\t/// <param name=\"mem\">Input: old memory address or <c>null</c>. Output: new memory address; <c>null</c> if exception (it prevents freeing twice).</param>\n\t/// <param name=\"count\">New count of elements of type <c>T</c>.</param>\n\t/// <param name=\"zeroInit\">Set all bytes = 0.</param>\n\t/// <exception cref=\"OutOfMemoryException\">Failed. Probably <i>count</i> is too big.</exception>\n\t/// <remarks>\n\t/// At first sets <i>mem</i> = <c>null</c>, to avoid double <see cref=\"Free\"/> if this function throws exception. Then calls <see cref=\"Free\"/> and <see cref=\"Alloc\"/>.\n\t/// </remarks>\n\tpublic static void FreeAlloc<T>(ref T* mem, nint count, bool zeroInit = false) where T : unmanaged {\n\t\tvar m = mem; mem = null;\n\t\tFree(m);\n\t\tmem = Alloc<T>(count, zeroInit);\n\t}\n\t\n\t/// <summary>\n\t/// Allocates new virtual memory block with API <ms>VirtualAlloc</ms> and returns its address: <c>VirtualAlloc(default, size, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE)</c>.\n\t/// </summary>\n\t/// <param name=\"size\">Byte count.</param>\n\t/// <exception cref=\"OutOfMemoryException\">Failed. Probably <i>size</i> is too big.</exception>\n\t/// <remarks>\n\t/// Faster than managed and <see cref=\"Alloc\"/> when memory size is large, more than 1 MB; else slower.\n\t/// The memory is initialized to zero (all bytes 0).\n\t/// </remarks>\n\tpublic static byte* VirtualAlloc(nint size) {\n\t\tfor (int i = 0; i < 5; i++) {\n\t\t\tif (i > 0) {\n\t\t\t\tGC.Collect();\n\t\t\t\tGC.WaitForPendingFinalizers();\n\t\t\t\tThread.Sleep(i * 100);\n\t\t\t}\n\t\t\tvar r = (byte*)Api.VirtualAlloc(default, size, Api.MEM_COMMIT | Api.MEM_RESERVE, Api.PAGE_READWRITE);\n\t\t\tif (r != null) return r;\n\t\t}\n\t\tthrow new OutOfMemoryException();\n\t\t\n\t\t//note: don't need GC.AddMemoryPressure.\n\t\t//\tNative memory usually is used for temporary buffers etc and is soon released eg with try/finally.\n\t\t//\tMarshal.AllocHGlobal does not do it too.\n\t}\n\t\n\t/// <summary>\n\t/// Frees a memory block allocated with <see cref=\"VirtualAlloc\"/>.\n\t/// Does nothing if <i>mem</i> is <c>null</c>.\n\t/// </summary>\n\tpublic static void VirtualFree(void* mem) {\n\t\tif (mem != null) Api.VirtualFree(mem);\n\t}\n\t\n\t/// <summary>\n\t/// Copies memory with <see cref=\"Buffer.MemoryCopy\"/>.\n\t/// </summary>\n\t/// <remarks>\n\t/// If some part of memory blocks overlaps, this function is much slower than <see cref=\"Move\"/>. Else same speed or slightly faster.\n\t/// </remarks>\n\tpublic static void Copy(void* from, void* to, nint size) => Buffer.MemoryCopy(from, to, size, size);\n\t//speed Buffer.MemoryCopy vs memcpy, non-overlapped: same if small, slightly faster if big.\n\t//speed Span.CopyTo vs Buffer.MemoryCopy: same if non-overlapped, slower if overlapped.\n\t\n\t/// <summary>\n\t/// Copies memory with API <ms>memmove</ms>.\n\t/// </summary>\n\t/// <remarks>\n\t/// If some part of memory blocks overlaps, this function is much faster than <see cref=\"Copy\"/>. Else same speed or slightly slower.\n\t/// </remarks>\n\tpublic static void Move(void* from, void* to, nint size) => Api.memmove(to, from, size);\n}\n"
  },
  {
    "path": "Au/Au.More/MenuItemInfo.cs",
    "content": "namespace Au.More\n{\n\t/// <summary>\n\t/// Gets item id, text and other info of a classic menu.\n\t/// </summary>\n\tpublic class MenuItemInfo\n\t{\n\t\tIntPtr _hm;\n\t\tint _id;\n\t\tbool _isSystem;\n\t\twnd _ow;\n\n\t\tprivate MenuItemInfo() { }\n\n\t\t/// <summary>\n\t\t/// Gets info of a menu item from point.\n\t\t/// </summary>\n\t\t/// <returns><c>null</c> if failed, eg the point is not in the menu or the window is hung.</returns>\n\t\t/// <param name=\"pScreen\">Point in screen coordinates.</param>\n\t\t/// <param name=\"w\">Popup menu window, class name <c>\"#32768\"</c>.</param>\n\t\t/// <param name=\"msTimeout\">Timeout (ms) to use when the window is busy or hung.</param>\n\t\tpublic static MenuItemInfo FromXY(POINT pScreen, wnd w, int msTimeout = 5000) {\n\t\t\tif (!w.SendTimeout(msTimeout, out var hm, Api.MN_GETHMENU)) return null;\n\t\t\tint i = Api.MenuItemFromPoint(default, hm, pScreen); if (i == -1) return null;\n\t\t\ti = Api.GetMenuItemID(hm, i); if (i == -1 || i == 0) return null;\n\t\t\treturn new MenuItemInfo { _hm = hm, _id = i };\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets info of a menu item from mouse.\n\t\t/// </summary>\n\t\t/// <returns><c>null</c> if failed, eg the point is not in a menu or the window is hung.</returns>\n\t\t/// <param name=\"msTimeout\">Timeout (ms) to use when the window is busy or hung.</param>\n\t\tpublic static MenuItemInfo FromXY(int msTimeout = 5000) {\n\t\t\tvar p = mouse.xy;\n\t\t\tvar w = wnd.fromXY(p, WXYFlags.Raw);\n\t\t\tif (!w.ClassNameIs(\"#32768\")) return null;\n\t\t\treturn FromXY(p, w, msTimeout);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets the popup menu handle.\n\t\t/// </summary>\n\t\tpublic IntPtr MenuHandle => _hm;\n\n\t\t/// <summary>\n\t\t/// Gets menu item id.\n\t\t/// </summary>\n\t\tpublic int ItemId => _id;\n\n\t\t/// <summary>\n\t\t/// Gets the owner window of the popup menu.\n\t\t/// </summary>\n\t\tpublic wnd OwnerWindow => _OwnerSystem().ow;\n\n\t\t/// <summary>\n\t\t/// <c>true</c> if it is a system menu, eg when right-clicked the title bar of a window.\n\t\t/// </summary>\n\t\tpublic bool IsSystem => _OwnerSystem().sys;\n\n\t\t(wnd ow, bool sys) _OwnerSystem() {\n\t\t\tif (_ow.Is0 && miscInfo.getGUIThreadInfo(out var g)) {\n\t\t\t\t_ow = g.hwndMenuOwner;\n\t\t\t\t_isSystem = g.flags.Has(GTIFlags.SYSTEMMENUMODE);\n\t\t\t}\n\t\t\treturn (_ow, _isSystem);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets menu item text.\n\t\t/// </summary>\n\t\t/// <returns><c>null</c> if failed.</returns>\n\t\t/// <param name=\"removeHotkey\">If contains <c>'\\t'</c> character, get substring before it.</param>\n\t\t/// <param name=\"removeAmp\">Call <see cref=\"StringUtil.RemoveUnderlineChar\"/>.</param>\n\t\tpublic string GetText(bool removeHotkey, bool removeAmp) => GetText(_hm, _id, false, removeHotkey, removeAmp);\n\n\t\t/// <summary>\n\t\t/// Gets menu item text.\n\t\t/// </summary>\n\t\t/// <returns><c>null</c> if failed.</returns>\n\t\t/// <param name=\"menuHandle\"></param>\n\t\t/// <param name=\"id\"></param>\n\t\t/// <param name=\"byIndex\">id is 0-based index. For example you can use it to get text of a submenu-item, because such items usually don't have id.</param>\n\t\t/// <param name=\"removeHotkey\">If contains <c>'\\t'</c> character, get substring before it.</param>\n\t\t/// <param name=\"removeAmp\">Call <see cref=\"StringUtil.RemoveUnderlineChar\"/>.</param>\n\t\t[SkipLocalsInit]\n\t\tpublic static unsafe string GetText(IntPtr menuHandle, int id, bool byIndex, bool removeHotkey, bool removeAmp) {\n\t\t\tvar mi = new Api.MENUITEMINFO(Api.MIIM_STRING);\n\t\t\tif (!Api.GetMenuItemInfo(menuHandle, id, byIndex, ref mi)) return null; //get required buffer size\n\t\t\tif (mi.cch == 0) return \"\";\n\t\t\tusing FastBuffer<char> b = new(mi.cch + 1);\n\t\t\tmi.cch = b.n;\n\t\t\tmi.dwTypeData = b.p;\n\t\t\tif (!Api.GetMenuItemInfo(menuHandle, id, byIndex, ref mi)) return null;\n\t\t\tvar s = b.GetStringFindLength();\n\t\t\tif (removeHotkey) { int i = s.IndexOf('\\t'); if (i >= 0) s = s[..i]; }\n\t\t\tif (removeAmp) s = StringUtil.RemoveUnderlineChar(s);\n\t\t\treturn s;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au/Au.More/MouseCursor.cs",
    "content": "namespace Au.More;\n\n/// <summary>\n/// Helps to load cursors, etc. Contains native cursor handle.\n/// </summary>\n/// <remarks>\n/// To load cursors for winforms can be used <see cref=\"System.Windows.Forms.Cursor\"/> constructors, but they don't support colors, ani cursors and custom size.\n/// Don't use this class to load cursors for WPF. Its <c>Cursor</c> class loads cursors correctly.\n/// </remarks>\npublic class MouseCursor {\n\tIntPtr _handle;\n\t\n\t//rejected: use HandleCollector like with icon. Unlikely somebody will use many cursors or even find this class.\n\t\n\t/// <summary>\n\t/// Sets native cursor handle.\n\t/// The cursor will be destroyed when disposing this variable or when converting to object of other type.\n\t/// </summary>\n\tpublic MouseCursor(IntPtr hcursor) { _handle = hcursor; }\n\t\n\t/// <summary>\n\t/// Destroys native cursor handle.\n\t/// </summary>\n\tpublic void Dispose() {\n\t\tif (_handle != default) { Api.DestroyIcon(_handle); _handle = default; }\n\t}\n\t\n\t///\n\t~MouseCursor() => Dispose();\n\t\n\t/// <summary>\n\t/// Gets native cursor handle.\n\t/// </summary>\n\tpublic IntPtr Handle => _handle;\n\t\n\t///// <summary>\n\t///// Gets native cursor handle.\n\t///// </summary>\n\t//public static implicit operator IntPtr(MouseCursor cursor) => cursor._handle;\n\t\n\t/// <summary>\n\t/// Loads cursor from file.\n\t/// </summary>\n\t/// <returns><c>default(MouseCursor)</c> if failed.</returns>\n\t/// <param name=\"file\"><c>.cur</c> or <c>.ani</c> file. If not full path, uses <see cref=\"folders.ThisAppImages\"/>.</param>\n\t/// <param name=\"size\">Width and height. If 0, uses system default size, which depends on DPI.</param>\n\tpublic static MouseCursor Load(string file, int size = 0) {\n\t\tfile = pathname.normalize(file, folders.ThisAppImages);\n\t\tif (file == null) return null;\n\t\tuint fl = Api.LR_LOADFROMFILE; if (size == 0) fl |= Api.LR_DEFAULTSIZE;\n\t\treturn new MouseCursor(Api.LoadImage(default, file, Api.IMAGE_CURSOR, size, size, fl));\n\t}\n\t\n\t/// <summary>\n\t/// Creates cursor from cursor file data in memory, for example from a managed resource.\n\t/// </summary>\n\t/// <returns><c>default(MouseCursor)</c> if failed.</returns>\n\t/// <param name=\"cursorData\">Data of <c>.cur</c> or <c>.ani</c> file.</param>\n\t/// <param name=\"size\">Width and height. If 0, uses system default size, which depends on DPI.</param>\n\t/// <remarks>\n\t/// This function creates/deletes a temporary file, because there is no good API to load cursor from memory.\n\t/// </remarks>\n\tpublic static MouseCursor Load(byte[] cursorData, int size = 0) {\n\t\tusing var tf = new TempFile();\n\t\tFile.WriteAllBytes(tf, cursorData);\n\t\treturn Load(tf, size);\n\t\t\n\t\t//If want to avoid temp file, can use:\n\t\t//\t1. CreateIconFromResourceEx.\n\t\t//\t\tBut quite much unsafe code (at first need to find cursor offset (of size) and set hotspot), less reliable (may fail for some .ani files), in some cases works not as well (may get wrong-size cursor).\n\t\t//\t\tThe code moved to the Unused project.\n\t\t//\t2. CreateIconIndirect. Not tested. No ani. Need to find cursor offset (of size).\n\t\t//WPF Cursor ctor uses temp file too.\n\t}\n\t\n\t/// <summary>\n\t/// Creates <see cref=\"System.Windows.Forms.Cursor\"/> object that shares native cursor handle with this object.\n\t/// </summary>\n\t/// <returns><c>null</c> if <see cref=\"Handle\"/> is <c>default(IntPtr)</c>.</returns>\n\tpublic System.Windows.Forms.Cursor ToGdipCursor() {\n\t\tif (_handle == default) return null;\n\t\tvar R = new System.Windows.Forms.Cursor(_handle);\n\t\ts_cwt.Add(R, this);\n\t\treturn R;\n\t}\n\tstatic readonly ConditionalWeakTable<System.Windows.Forms.Cursor, MouseCursor> s_cwt = new();\n\t\n\t//rejected. Don't need. WPF can load from file or stream. Loads correctly.\n\t///// <summary>\n\t///// Creates WPF <c>Cursor</c> object from this native cursor.\n\t///// </summary>\n\t///// <returns><c>null</c> if <c>Handle</c> is <c>default(IntPtr)</c>.</returns>\n\t///// <param name=\"destroyCursor\">If <c>true</c> (default), the returned variable owns the unmanaged cursor and destroys it when disposing. If <c>false</c>, the returned variable just uses the cursor handle and will not destroy; later will need to dispose this variable.</param>\n\t//public System.Windows.Input.Cursor ToWpfCursor(bool destroyCursor = true) {\n\t//\tif (_handle == default) return null;\n\t//\tvar R = System.Windows.Interop.CursorInteropHelper.Create(new _CursorHandle(_handle, destroyCursor));\n\t//\tif (destroyCursor) _handle = default;\n\t//\treturn R;\n\t//}\n\t\n\t//class _CursorHandle : SafeHandleZeroOrMinusOneIsInvalid\n\t//{\n\t//\tpublic _CursorHandle(IntPtr handle, bool ownsHandle) : base(ownsHandle) { base.handle = handle; }\n\t\n\t//\tprotected override bool ReleaseHandle() => Api.DestroyCursor(handle);\n\t//}\n\t\n\t/// <summary>\n\t/// Calculates 64-bit FNV1 hash of cursor's mask bitmap.\n\t/// </summary>\n\t/// <returns>0 if failed.</returns>\n\tpublic static unsafe long Hash(IntPtr hCursor) {\n\t\tusing Api.ICONINFO ii = new(hCursor);\n\t\tvar hb = Api.CopyImage(ii.hbmMask, Api.IMAGE_BITMAP, 0, 0, Api.LR_COPYDELETEORG | Api.LR_CREATEDIBSECTION);\n\t\tlong R = 0; Api.BITMAP b;\n\t\tif (0 != Api.GetObject(hb, sizeof(Api.BITMAP), &b) && b.bmBits != default)\n\t\t\tR = More.Hash.Fnv1Long((byte*)b.bmBits, b.bmHeight * b.bmWidthBytes);\n\t\treturn R;\n\t}\n\t\n\t///// <summary>\n\t///// Calculates 64-bit FNV1 hash of cursor's mask bitmap.\n\t///// </summary>\n\t///// <returns>0 if failed.</returns>\n\t//public unsafe long Hash() => Hash(_handle);\n\t\n\t/// <summary>\n\t/// Gets current global mouse cursor.\n\t/// </summary>\n\t/// <returns><c>false</c> if cursor is hidden.</returns>\n\t/// <param name=\"cursor\">Receives native handle. Don't destroy.</param>\n\tpublic static bool GetCurrentVisibleCursor(out IntPtr cursor) {\n\t\tApi.CURSORINFO ci = default; ci.cbSize = Api.SizeOf(ci);\n\t\tif (Api.GetCursorInfo(ref ci) && ci.hCursor != default && 0 != (ci.flags & Api.CURSOR_SHOWING)) { cursor = ci.hCursor; return true; }\n\t\tcursor = default; return false;\n\t}\n\t\n\t/// <summary>\n\t/// Workaround for brief \"wait\" cursor when mouse enters a window of current thread first time.\n\t/// Reason: default thread's cursor is \"wait\". OS shows it before the first <c>WM_SETCURSOR</c> sets correct cursor.\n\t/// Call eg on <c>WM_CREATE</c>.\n\t/// </summary>\n\tinternal static void SetArrowCursor_() {\n\t\tvar h = Api.LoadCursor(default, MCursor.Arrow);\n\t\tif (Api.GetCursor() != h) Api.SetCursor(h);\n\t}\n}\n"
  },
  {
    "path": "Au/Au.More/RecordingUtil.cs",
    "content": "﻿namespace Au.More\n{\n\t/// <summary>\n\t/// Functions for keyboard/mouse/etc recorder tools.\n\t/// </summary>\n\tpublic static partial class RecordingUtil\n\t{\n\t\t/// <summary>\n\t\t/// Converts multiple recorded mouse movements to string for <see cref=\"mouse.moveBy(string, double)\"/>.\n\t\t/// </summary>\n\t\t/// <param name=\"recorded\">\n\t\t/// List of x y distances from previous.\n\t\t/// The first distance is from mouse position before the first movement; at run time it will be distance from <see cref=\"mouse.lastXY\"/>.\n\t\t/// To create <c>uint</c> value from distance <i>dx</i> <i>dy</i> use <see cref=\"Math2.MakeLparam\"/> and cast to <c>uint</c>.\n\t\t/// </param>\n\t\t/// <param name=\"withSleepTimes\">\n\t\t/// <i>recorded</i> also contains sleep times (milliseconds) alternating with distances.\n\t\t/// It must start with a sleep time. Example: <c>{time1, dist1, time2, dist2}</c>. Another example: <c>{time1, dist1, time2, dist2, time3}</c>. This is invalid: <c>{dist1, time1, dist2, time2}</c>.\n\t\t/// </param>\n\t\tpublic static string MouseToString(IEnumerable<uint> recorded, bool withSleepTimes) {\n\t\t\tvar a = new List<byte>();\n\n\t\t\tbyte flags = 0;\n\t\t\tif (withSleepTimes) flags |= 1;\n\t\t\ta.Add(flags);\n\n\t\t\tint pdx = 0, pdy = 0;\n\t\t\tbool isSleep = withSleepTimes;\n\t\t\tforeach (var u in recorded) {\n\t\t\t\tint v, nbytes = 4;\n\t\t\t\tif (isSleep) {\n\t\t\t\t\tv = (int)Math.Min(u, 0x3fffffff);\n\t\t\t\t\tif (v > 3) v--; //_SendMove usually takes 0.5-1.5 ms\n\t\t\t\t\tif (v <= 1 << 6) nbytes = 1;\n\t\t\t\t\telse if (v <= 1 << 14) nbytes = 2;\n\t\t\t\t\telse if (v <= 1 << 22) nbytes = 3;\n\n\t\t\t\t\t//print.it($\"nbytes={nbytes}    sleep={v}\");\n\t\t\t\t\t//never mind: ~90% is 7. Removing it would make almost 2 times smaller string. But need much more code. Or compress (see comment below).\n\t\t\t\t} else {\n\t\t\t\t\t//info: to make more compact, we write not distances (dx dy) but distance changes (x y).\n\t\t\t\t\tint dx = Math2.LoShort((nint)u), x = dx - pdx; pdx = dx;\n\t\t\t\t\tint dy = Math2.HiShort((nint)u), y = dy - pdy; pdy = dy;\n\n\t\t\t\t\tif (x >= -4 && x < 4 && y >= -4 && y < 4) nbytes = 1; //3+3+2=8 bits, 90%\n\t\t\t\t\telse if (x >= -64 && x < 64 && y >= -64 && y < 64) nbytes = 2; //7+7+2=16 bits, ~10%\n\t\t\t\t\telse if (x >= -1024 && x < 1024 && y >= -1024 && y < 1024) nbytes = 3; //11+11+2=24 bits, ~0%\n\n\t\t\t\t\tint shift = nbytes * 4 - 1, mask = (1 << shift) - 1;\n\t\t\t\t\tv = (x & mask) | ((y & mask) << shift);\n\n\t\t\t\t\t//print.it($\"dx={dx} dy={dy}    x={x} y={y}    nbytes={nbytes}    v=0x{v:X}\");\n\t\t\t\t}\n\t\t\t\tv <<= 2; v |= (nbytes - 1);\n\t\t\t\tfor (; nbytes != 0; nbytes--, v >>= 8) a.Add((byte)v);\n\t\t\t\tisSleep ^= withSleepTimes;\n\t\t\t}\n\n\t\t\t//rejected: by default compresses to ~80% (20% smaller). When withSleepTimes, to ~50%, but never mind, rarely used.\n\t\t\t//print.it(a.Count, Convert2.Compress(a.ToArray()).Length);\n\n\t\t\treturn Convert.ToBase64String(a.ToArray());\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au/Au.More/ResourceUtil.cs",
    "content": "using System.Resources;\nusing System.Windows.Media.Imaging;\nusing System.Windows.Markup;\nusing System.Windows;\nusing System.Windows.Controls;\n\nnamespace Au.More;\n\n/// <summary>\n/// Gets managed resources from a .NET assembly.\n/// </summary>\n/// <remarks>\n/// Internally uses <see cref=\"ResourceManager\"/>. Uses <see cref=\"CultureInfo.InvariantCulture\"/>.\n///\n/// Loads resources from managed resource <c>\"AssemblyName.g.resources\"</c>. To add such resource files in Visual Studio, set file build action = <c>Resource</c>. Don't use <c>.resx</c> files and the <b>Resources</b> page in <b>Project Properties</b>.\n///\n/// By default loads resources from the app entry assembly. In script with role <c>miniProgram</c> - from the script's assembly. To specify another loaded assembly, use prefix like <c>\"&lt;AssemblyName&gt;\"</c> or <c>\"*&lt;AssemblyName&gt;\"</c>.\n///\n/// The resource name argument can optionally start with <c>\"resource:\"</c>.\n///\n/// Does not use caching. Creates new object even when loading the resource not the first time.\n/// </remarks>\npublic static class ResourceUtil {\n\t/// <summary>\n\t/// Gets resource of any type.\n\t/// </summary>\n\t/// <param name=\"name\">Resource name, like <c>\"file.txt\"</c> or <c>\"sub/file.txt\"</c>. More info: <see cref=\"ResourceUtil\"/>.</param>\n\t/// <exception cref=\"FileNotFoundException\">Cannot find assembly or resource.</exception>\n\t/// <exception cref=\"InvalidOperationException\">The resource is of different type. This function does not convert.</exception>\n\t/// <exception cref=\"Exception\">Other exceptions that may be thrown by used .NET functions.</exception>\n\tpublic static T Get<T>(string name) {\n\t\tvar o = _GetObject(ref name);\n\t\tif (o is T r) return r;\n\t\tthrow new InvalidOperationException($\"Resource '{name}' is not {typeof(T).Name}; it is {o.GetType().Name}.\");\n\t}\n\t\n\t/// <summary>\n\t/// Gets stream.\n\t/// </summary>\n\t/// <param name=\"name\">Resource name, like <c>\"file.png\"</c> or <c>\"sub/file.png\"</c>. More info: <see cref=\"ResourceUtil\"/>.</param>\n\t/// <exception cref=\"FileNotFoundException\">Cannot find assembly or resource.</exception>\n\t/// <exception cref=\"InvalidOperationException\">The resource type is not stream.</exception>\n\t/// <exception cref=\"Exception\">Other exceptions that may be thrown by used .NET functions.</exception>\n\t/// <remarks>\n\t/// Don't need to dispose the stream.\n\t/// </remarks>\n\tpublic static UnmanagedMemoryStream GetStream(string name) {\n\t\t//if (name.Starts(\"pack:\")) return _Pack(name); //rejected\n\t\treturn Get<UnmanagedMemoryStream>(name);\n\t}\n\t\n\t/// <summary>\n\t/// Gets string.\n\t/// </summary>\n\t/// <param name=\"name\">Resource name, like <c>\"myString\"</c> or <c>\"file.txt\"</c> or <c>\"sub/file.txt\"</c>. More info: <see cref=\"ResourceUtil\"/>.</param>\n\t/// <exception cref=\"FileNotFoundException\">Cannot find assembly or resource.</exception>\n\t/// <exception cref=\"InvalidOperationException\">Unsupported resource type.</exception>\n\t/// <exception cref=\"Exception\">Other exceptions that may be thrown by used .NET functions.</exception>\n\t/// <remarks>\n\t/// Supports resources of type <c>string</c>, <c>byte[]</c> (UTF-8), stream (UTF-8).\n\t/// </remarks>\n\tpublic static string GetString(string name) {\n\t\tvar o = _GetObject(ref name);\n\t\tswitch (o) {\n\t\tcase string s: return s;\n\t\tcase byte[] a: return Encoding.UTF8.GetString(a);\n\t\tcase UnmanagedMemoryStream m: return new StreamReader(m, Encoding.UTF8).ReadToEnd();\n\t\t}\n\t\tthrow new InvalidOperationException($\"Resource '{name}' is not string, byte[] or stream; it is {o.GetType().Name}.\");\n\t}\n\t\n\tinternal static string TryGetString_(string name) => _TryGetObject(ref name) as string;\n\t\n\t/// <summary>\n\t/// Gets <c>byte[]</c>.\n\t/// </summary>\n\t/// <param name=\"name\">Resource name, like <c>\"file.txt\"</c> or <c>\"sub/file.txt\"</c>. More info: <see cref=\"ResourceUtil\"/>.</param>\n\t/// <exception cref=\"FileNotFoundException\">Cannot find assembly or resource.</exception>\n\t/// <exception cref=\"InvalidOperationException\">Unsupported resource type.</exception>\n\t/// <exception cref=\"Exception\">Other exceptions that may be thrown by used .NET functions.</exception>\n\t/// <remarks>\n\t/// Supports resources of type <c>byte[]</c>, <c>string</c> (gets UTF-8 bytes), stream.\n\t/// </remarks>\n\tpublic static byte[] GetBytes(string name) {\n\t\tvar o = _GetObject(ref name);\n\t\tswitch (o) {\n\t\tcase byte[] a: return a;\n\t\tcase string s: return Encoding.UTF8.GetBytes(s);\n\t\tcase UnmanagedMemoryStream m:\n\t\t\tvar b = new byte[m.Length];\n\t\t\tm.Read(b);\n\t\t\treturn b;\n\t\t}\n\t\tthrow new InvalidOperationException($\"Resource '{name}' is not byte[], string or stream; it is {o.GetType().Name}.\");\n\t}\n\t\n\t/// <summary>\n\t/// Gets GDI+ image.\n\t/// </summary>\n\t/// <param name=\"name\">Resource name, like <c>\"file.png\"</c> or <c>\"sub/file.png\"</c>. More info: <see cref=\"ResourceUtil\"/>.</param>\n\t/// <exception cref=\"FileNotFoundException\">Cannot find assembly or resource.</exception>\n\t/// <exception cref=\"InvalidOperationException\">The resource type is not stream.</exception>\n\t/// <exception cref=\"Exception\">Other exceptions that may be thrown by used .NET functions.</exception>\n\tpublic static System.Drawing.Bitmap GetGdipBitmap(string name) {\n\t\treturn new System.Drawing.Bitmap(GetStream(name));\n\t}\n\t\n\t//rejected. Too simple and rare.\n\t///// <summary>\n\t///// Gets GDI+ icon.\n\t///// </summary>\n\t///// <param name=\"name\">Resource name, like <c>\"file.ico\"</c> or <c>\"sub/file.ico\"</c>. More info: <see cref=\"ResourceUtil\"/>.</param>\n\t///// <exception cref=\"FileNotFoundException\">Cannot find assembly or resource.</exception>\n\t///// <exception cref=\"InvalidOperationException\">The resource type is not stream.</exception>\n\t///// <exception cref=\"Exception\">Other exceptions that may be thrown by used .NET functions.</exception>\n\t//public static System.Drawing.Icon GetGdipIcon(string name) {\n\t//\treturn new System.Drawing.Icon(GetStream(name));\n\t//}\n\t\n\t/// <summary>\n\t/// Gets WPF image or icon that can be used as <c>ImageSource</c>.\n\t/// </summary>\n\t/// <param name=\"name\">Resource name, like <c>\"file.png\"</c> or <c>\"sub/file.png\"</c>. More info: <see cref=\"ResourceUtil\"/>.</param>\n\t/// <exception cref=\"FileNotFoundException\">Cannot find assembly or resource.</exception>\n\t/// <exception cref=\"InvalidOperationException\">The resource type is not stream.</exception>\n\t/// <exception cref=\"Exception\">Other exceptions that may be thrown by used .NET functions.</exception>\n\tpublic static BitmapFrame GetWpfImage(string name) {\n\t\treturn BitmapFrame.Create(GetStream(name));\n\t}\n\t\n\t/// <summary>\n\t/// Gets WPF object from XAML resource, for example image.\n\t/// </summary>\n\t/// <returns>An object of type of the XAML root object, for example <see cref=\"Viewbox\"/> if <see cref=\"Image\"/>.</returns>\n\t/// <param name=\"name\">Resource name, like <c>\"file.xaml\"</c> or <c>\"sub/file.xaml\"</c>. More info: <see cref=\"ResourceUtil\"/>.</param>\n\t/// <exception cref=\"FileNotFoundException\">Cannot find assembly or resource.</exception>\n\t/// <exception cref=\"InvalidOperationException\">The resource type is not stream.</exception>\n\t/// <exception cref=\"Exception\">Other exceptions that may be thrown by used .NET functions.</exception>\n\tpublic static object GetXamlObject(string name) {\n\t\treturn XamlReader.Load(GetStream(name));\n\t}\n\t\n\t/// <summary>\n\t/// Gets WPF image element from XAML or other image resource.\n\t/// </summary>\n\t/// <param name=\"name\">Resource name, like <c>\"file.png\"</c> or <c>\"sub/file.xaml\"</c>. More info: <see cref=\"ResourceUtil\"/>.</param>\n\t/// <exception cref=\"FileNotFoundException\">Cannot find assembly or resource.</exception>\n\t/// <exception cref=\"InvalidOperationException\">The resource type is not stream.</exception>\n\t/// <exception cref=\"Exception\">Other exceptions that may be thrown by used .NET functions.</exception>\n\t/// <remarks>\n\t/// If <i>name</i> ends with <c>\".xaml\"</c> (case-insensitive), calls <see cref=\"GetXamlObject\"/>. Else returns <see cref=\"Image\"/> with <c>Source</c> = <see cref=\"GetWpfImage\"/>.\n\t/// </remarks>\n\tpublic static FrameworkElement GetWpfImageElement(string name) {\n\t\tif (name.Ends(\".xaml\", true)) return (FrameworkElement)GetXamlObject(name);\n\t\treturn new Image { Source = GetWpfImage(name) };\n\t}\n\t\n\t//probably not useful\n\t///// <summary>\n\t///// Gets WPF image as <c>BitmapImage</c>.\n\t///// </summary>\n\t///// <param name=\"name\">Resource name, like <c>\"file.png\"</c> or <c>\"sub/file.png\"</c>. More info: <see cref=\"ResourceUtil\"/>.</param>\n\t///// <exception cref=\"FileNotFoundException\">Cannot find assembly or resource.</exception>\n\t///// <exception cref=\"InvalidOperationException\">The resource type is not stream.</exception>\n\t///// <exception cref=\"Exception\">Other exceptions that may be thrown by used .NET functions.</exception>\n\t//public static BitmapImage GetWpfBitmapImage(string name) {\n\t//\tvar st = GetStream(name);\n\t//\tvar bi = new BitmapImage();\n\t//\tbi.BeginInit();\n\t//\tbi.CacheOption = BitmapCacheOption.OnLoad;\n\t//\tbi.StreamSource = st;\n\t//\tbi.EndInit();\n\t//\treturn bi;\n\t//}\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if string starts with <c>\"resource:\"</c> or <c>\"resources/\"</c>.\n\t/// </summary>\n\tpublic static bool HasResourcePrefix(string s) {\n\t\treturn s.Starts(\"resource:\") || s.Starts(\"resources/\")/* || s.Starts(\"pack:\")*/;\n\t}\n\t\n\t//[MethodImpl(MethodImplOptions.NoInlining)] //avoid loading WPF dlls if no \"pack:\"\n\t//static UnmanagedMemoryStream _Pack(string name) {\n\t//\tif (script.role == SRole.MiniProgram && !name.Contains(\";component/\") && name.Starts(\"pack://application:,,,/\")) name = name.Insert(23, script.name + \";component/\");\n\t//\tif (Application.Current == null) new Application();\n\t//\treturn Application.GetResourceStream(new Uri(name)).Stream as UnmanagedMemoryStream;\n\t//}\n\t\n\tstatic object _GetObject(ref string name)\n\t\t=> _TryGetObject(ref name) ?? throw new FileNotFoundException($\"Cannot find resource '{name}'.\");\n\t\n\tstatic object _TryGetObject(ref string name) {\n\t\tvar rs = _RS(ref name, true);\n\t\tif (rs == null) return null;\n\t\tvar r = rs.GetObject(name);\n\t\tif (r == null) r = rs.GetObject(name.Lower());\n\t\treturn r;\n\t}\n\t\n\tstatic ResourceSet _RS(ref string name, bool noThrow = false) {\n\t\tif (name.Starts(\"resource:\")) name = name[9..];\n\t\tstring asmName = \"\";\n\t\tif (name is ['<', ..] or ['*', '<', ..]) {\n\t\t\tint i = name[0] == '*' ? 2 : 1;\n\t\t\tint j = name.IndexOf('>', i);\n\t\t\tif (j >= i) {\n\t\t\t\tasmName = name[i..j];\n\t\t\t\tname = name[++j..];\n\t\t\t}\n\t\t}\n\t\t\n\t\tlock (s_dict) {\n\t\t\tif (!s_dict.TryGetValue(asmName, out var rs)) {\n\t\t\t\tvar asm = asmName.Length == 0 ? AssemblyUtil_.GetEntryAssembly() : _FindAssembly(asmName);\n\t\t\t\tif (asm == null) return noThrow ? null : throw new FileNotFoundException($\"Cannot find loaded resource assembly '{asmName}'.\");\n\t\t\t\tvar rm = new ResourceManager(asm.GetName().Name + \".g\", asm);\n\t\t\t\trs = rm.GetResourceSet(CultureInfo.InvariantCulture, true, false);\n\t\t\t\tif (rs == null) return noThrow ? null : throw new FileNotFoundException($\"Cannot find resources in assembly '{asmName}'.\");\n\t\t\t\ts_dict.Add(asmName, rs);\n\t\t\t}\n\t\t\treturn rs;\n\t\t}\n\t}\n\t\n\tstatic readonly Dictionary<string, ResourceSet> s_dict = new(StringComparer.OrdinalIgnoreCase);\n\t\n\tstatic Assembly _FindAssembly(string name) {\n\t\tforeach (var v in AppDomain.CurrentDomain.GetAssemblies()) if (v.GetName().Name.Eqi(name)) return v;\n\t\treturn null;\n\t}\n}\n"
  },
  {
    "path": "Au/Au.More/SecurityUtil.cs",
    "content": "namespace Au.More\n{\n\t/// <summary>\n\t/// Security-related functions, such as enabling privileges.\n\t/// </summary>\n\tpublic static class SecurityUtil\n\t{\n\t\t/// <summary>\n\t\t/// Enables or disables a privilege for this process.\n\t\t/// </summary>\n\t\t/// <returns><c>false</c> if failed. Supports <see cref=\"lastError\"/>.</returns>\n\t\tpublic static bool SetPrivilege(string privilegeName, bool enable, string computer = null) {\n\t\t\tbool ok = false;\n\t\t\tvar p = new Api.TOKEN_PRIVILEGES { PrivilegeCount = 1, Privileges = new Api.LUID_AND_ATTRIBUTES { Attributes = enable ? 2u : 0 } }; //SE_PRIVILEGE_ENABLED\n\t\t\tif (Api.LookupPrivilegeValue(computer, privilegeName, out p.Privileges.Luid)) {\n\t\t\t\tApi.OpenProcessToken(Api.GetCurrentProcess(), Api.TOKEN_ADJUST_PRIVILEGES, out Handle_ hToken);\n\t\t\t\tApi.AdjustTokenPrivileges(hToken, false, p, 0, null, default);\n\t\t\t\tok = 0 == lastError.code;\n\t\t\t\thToken.Dispose();\n\t\t\t}\n\t\t\treturn ok;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au/Au.More/WaitableTimer.cs",
    "content": "namespace Au.More;\n\n/// <summary>\n/// Wraps a waitable timer handle.\n/// </summary>\n/// <remarks>\n/// More info: API <ms>CreateWaitableTimer</ms>.\n/// </remarks>\npublic class WaitableTimer : WaitHandle {\n\tWaitableTimer(IntPtr h) => SafeWaitHandle = new Microsoft.Win32.SafeHandles.SafeWaitHandle(h, true);\n\n\t/// <summary>\n\t/// Calls API <ms>CreateWaitableTimer</ms> and creates a <see cref=\"WaitableTimer\"/> object that wraps the timer handle.\n\t/// </summary>\n\t/// <param name=\"manualReset\"></param>\n\t/// <param name=\"timerName\">Timer name. If a timer with this name already exists, opens it if possible. If <c>null</c>, creates unnamed timer.</param>\n\t/// <exception cref=\"AuException\">Failed. For example, a non-timer kernel object with this name already exists.</exception>\n\tpublic static WaitableTimer Create(bool manualReset = false, string timerName = null) {\n\t\tvar h = Api.CreateWaitableTimer(Api.SECURITY_ATTRIBUTES.ForLowIL, manualReset, timerName);\n\t\tif (h.Is0) throw new AuException(0, \"*create timer\");\n\t\treturn new WaitableTimer(h);\n\t}\n\n\t/// <summary>\n\t/// Calls API <ms>OpenWaitableTimer</ms> and creates a <see cref=\"WaitableTimer\"/> object that wraps the timer handle.\n\t/// </summary>\n\t/// <param name=\"timerName\">Timer name. Fails if it does not exist; to open-or-create use <see cref=\"Create\"/>.</param>\n\t/// <param name=\"access\">See <ms>Synchronization Object Security and Access Rights</ms>. The default value <c>TIMER_MODIFY_STATE|SYNCHRONIZE</c> allows to set and wait.</param>\n\t/// <param name=\"inheritHandle\"></param>\n\t/// <param name=\"noException\">If fails, return <c>null</c>, don't throw exception. Supports <see cref=\"lastError\"/>.</param>\n\t/// <exception cref=\"AuException\">Failed. For example, the timer does not exist.</exception>\n\tpublic static WaitableTimer Open(string timerName, uint access = Api.TIMER_MODIFY_STATE | Api.SYNCHRONIZE, bool inheritHandle = false, bool noException = false) {\n\t\tvar h = Api.OpenWaitableTimer(access, inheritHandle, timerName);\n\t\tif (h.Is0) {\n\t\t\tvar e = lastError.code;\n\t\t\tif (noException) {\n\t\t\t\tlastError.code = e;\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tthrow new AuException(e, \"*open timer\");\n\t\t}\n\t\treturn new WaitableTimer(h);\n\t}\n\n\t/// <summary>\n\t/// Calls API <ms>SetWaitableTimer</ms>.\n\t/// </summary>\n\t/// <returns><c>false</c> if failed. Supports <see cref=\"lastError\"/>.</returns>\n\t/// <param name=\"dueTime\">\n\t/// The time after which the state of the timer is to be set to signaled. It is relative time (from now).\n\t/// If positive, in milliseconds. If negative, in 100 nanosecond intervals (microseconds <c>*</c> 10), see <ms>FILETIME</ms>.\n\t/// Also can be 0, to set minimal time.</param>\n\t/// <param name=\"period\">The period of the timer, in milliseconds. If 0, the timer is signaled once. If greater than 0, the timer is periodic.</param>\n\t/// <exception cref=\"OverflowException\"><c>dueTime*10000</c> is greater than <see cref=\"long.MaxValue\"/>.</exception>\n\tpublic bool Set(long dueTime, int period = 0) {\n\t\tif (dueTime > 0) dueTime = -checked(dueTime * 10000);\n\t\treturn Api.SetWaitableTimer(this.SafeWaitHandle.DangerousGetHandle(), ref dueTime, period, default, default, false);\n\t}\n\n\t/// <summary>\n\t/// Calls API <ms>SetWaitableTimer</ms>.\n\t/// </summary>\n\t/// <returns><c>false</c> if failed. Supports <see cref=\"lastError\"/>.</returns>\n\t/// <param name=\"dueTime\">The UTC date/time at which the state of the timer is to be set to signaled.</param>\n\t/// <param name=\"period\">The period of the timer, in milliseconds. If 0, the timer is signaled once. If greater than 0, the timer is periodic.</param>\n\tpublic bool SetAbsolute(DateTime dueTime, int period = 0) {\n\t\tvar t = dueTime.ToFileTimeUtc();\n\t\treturn Api.SetWaitableTimer(this.SafeWaitHandle.DangerousGetHandle(), ref t, period, default, default, false);\n\t}\n}\n"
  },
  {
    "path": "Au/Au.More/WinEventHook.cs",
    "content": "namespace Au.More {\n\t/// <summary>\n\t/// Helps with UI element event hooks. See API <ms>SetWinEventHook</ms>.\n\t/// </summary>\n\t/// <remarks>\n\t/// The thread that uses hooks must process Windows messages. For example have a window/dialog/messagebox, or use a \"wait-for\" function that dispatches messages or has such option (see <see cref=\"Seconds.DoEvents\"/>).\n\t/// \n\t/// <note type=\"important\">The variable should be disposed when don't need, or at least unhooked, either explicitly (call <see cref=\"Dispose\"/> or <see cref=\"Unhook\"/> in same thread) or with <c>using</c>. Can do it in hook procedure.</note>\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// bool stop = false;\n\t/// using var hook = new WinEventHook(EEvent.SYSTEM_FOREGROUND, 0, x => {\n\t/// \tprint.it(x.event_, x.w);\n\t/// \tvar e = x.GetElm();\n\t/// \tprint.it(e);\n\t/// \tif(x.w.ClassNameIs(\"Shell_TrayWnd\")) stop = true;\n\t/// });\n\t/// dialog.show(\"hook\");\n\t/// //or\n\t/// //wait.doEventsUntil(-10, () => stop); //wait max 10 s for activated taskbar\n\t/// //print.it(\"the end\");\n\t/// ]]></code>\n\t/// </example>\n\t[DebuggerStepThrough]\n\tpublic sealed class WinEventHook : IDisposable {\n\t\tIntPtr[] _a;\n\t\tApi.WINEVENTPROC _proc1; //our intermediate hook proc that calls _proc2\n\t\tAction<HookData.WinEvent> _proc2; //caller's hook proc\n\t\t[ThreadStatic] static List<WinEventHook> t_antiGC;\n\t\t\n\t\t/// <summary>\n\t\t/// Sets a hook for an event or a range of events.\n\t\t/// </summary>\n\t\t/// <param name=\"eventMin\">The lowest event constant value in the range of events. Can be <see cref=\"EEvent.MIN\"/> to indicate the lowest possible event value. Events reference: <ms>SetWinEventHook</ms>. Value 0 is ignored.</param>\n\t\t/// <param name=\"eventMax\">The highest event constant value in the range of events. Can be <see cref=\"EEvent.MAX\"/> to indicate the highest possible event value. If 0, uses <i>eventMin</i>.</param>\n\t\t/// <param name=\"hookProc\">The hook procedure (function that handles hook events).</param>\n\t\t/// <param name=\"idProcess\">The id of the process from which the hook function receives events. If 0 - all processes on the current desktop.</param>\n\t\t/// <param name=\"idThread\">The native id of the thread from which the hook function receives events. If 0 - all threads.</param>\n\t\t/// <param name=\"flags\"></param>\n\t\t/// <exception cref=\"AuException\">Failed.</exception>\n\t\t/// <example>See <see cref=\"WinEventHook\"/>.</example>\n\t\tpublic WinEventHook(EEvent eventMin, EEvent eventMax, Action<HookData.WinEvent> hookProc, int idProcess = 0, int idThread = 0, EHookFlags flags = 0) {\n\t\t\tNot_.Null(hookProc);\n\t\t\t_proc1 = _HookProc;\n\t\t\tHook(eventMin, eventMax, idProcess, idThread, flags);\n\t\t\t_proc2 = hookProc;\n\t\t\t(t_antiGC ??= new()).Add(this);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Sets multiple hooks.\n\t\t/// </summary>\n\t\t/// <param name=\"events\">Events. Reference: API <ms>SetWinEventHook</ms>. Elements with value 0 are ignored.</param>\n\t\t/// <inheritdoc cref=\"WinEventHook(EEvent, EEvent, Action{HookData.WinEvent}, int, int, EHookFlags)\"/>\n\t\tpublic WinEventHook(EEvent[] events, Action<HookData.WinEvent> hookProc, int idProcess = 0, int idThread = 0, EHookFlags flags = 0) {\n\t\t\tNot_.Null(hookProc);\n\t\t\t_proc1 = _HookProc;\n\t\t\tHook(events, idProcess, idThread, flags);\n\t\t\t_proc2 = hookProc;\n\t\t\t(t_antiGC ??= new()).Add(this);\n\t\t}\n\t\t\n\t\t/// <exception cref=\"InvalidOperationException\">Hooks are already set and <see cref=\"Unhook\"/> not called.</exception>\n\t\t/// <inheritdoc cref=\"WinEventHook(EEvent, EEvent, Action{HookData.WinEvent}, int, int, EHookFlags)\"/>\n\t\tpublic void Hook(EEvent eventMin, EEvent eventMax = 0, int idProcess = 0, int idThread = 0, EHookFlags flags = 0) {\n\t\t\t_Throw1();\n\t\t\t_a = new IntPtr[1];\n\t\t\t_SetHook(0, eventMin, eventMax, idProcess, idThread, flags);\n\t\t}\n\t\t\n\t\t/// <exception cref=\"InvalidOperationException\">Hooks are already set and <see cref=\"Unhook\"/> not called.</exception>\n\t\t/// <inheritdoc cref=\"WinEventHook(EEvent[], Action{HookData.WinEvent}, int, int, EHookFlags)\"/>\n\t\tpublic void Hook(EEvent[] events, int idProcess = 0, int idThread = 0, EHookFlags flags = 0) {\n\t\t\t_Throw1();\n\t\t\t_a = new IntPtr[events.Length];\n\t\t\tfor (int i = 0; i < events.Length; i++) _SetHook(i, events[i], 0, idProcess, idThread, flags);\n\t\t}\n\t\t\n\t\tvoid _SetHook(int i, EEvent eMin, EEvent eMax, int idProcess, int idThread, EHookFlags flags) {\n\t\t\tif (eMin == 0) return;\n\t\t\tif (eMax == 0) eMax = eMin;\n\t\t\tvar hh = Api.SetWinEventHook(eMin, eMax, default, _proc1, idProcess, idThread, flags);\n\t\t\tif (hh == default) {\n\t\t\t\tvar ec = lastError.code;\n\t\t\t\tUnhook();\n\t\t\t\tthrow new AuException(ec, \"*set hook for \" + eMin.ToString());\n\t\t\t}\n\t\t\t_a[i] = hh;\n\t\t}\n\t\t\n\t\tvoid _Throw1() {\n\t\t\tif (_a != null) throw new InvalidOperationException();\n\t\t\tif (_proc1 == null) throw new ObjectDisposedException(nameof(WinEventHook));\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Adds a hook for an event or a range of events.\n\t\t/// </summary>\n\t\t/// <returns>An <c>int</c> value greater than 0 that can be used with <see cref=\"Remove\"/>.</returns>\n\t\t/// <remarks>\n\t\t/// Parameters are the same as of the constructor, but values can be different.\n\t\t/// \n\t\t/// This function together with <see cref=\"Remove\"/> can be used to temporarily add/remove one or more hooks while using the same <see cref=\"WinEventHook\"/> variable and hook procedure. Don't need to call <see cref=\"Unhook\"/> before.\n\t\t/// </remarks>\n\t\t/// <inheritdoc cref=\"WinEventHook(EEvent, EEvent, Action{HookData.WinEvent}, int, int, EHookFlags)\"/>\n\t\tpublic int Add(EEvent eventMin, EEvent eventMax = 0, int idProcess = 0, int idThread = 0, EHookFlags flags = 0) {\n\t\t\tif (_proc1 == null) throw new ObjectDisposedException(nameof(WinEventHook));\n\t\t\tint i = 0;\n\t\t\tif (_a == null) {\n\t\t\t\t_a = new IntPtr[1];\n\t\t\t} else {\n\t\t\t\tfor (; i < _a.Length; i++) if (_a[i] == default) goto g1;\n\t\t\t\tArray.Resize(ref _a, i + 1);\n\t\t\t}\n\t\t\tg1:\n\t\t\t_SetHook(i, eventMin, eventMax, idProcess, idThread, flags);\n\t\t\treturn i + 1;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Removes a hook added by <see cref=\"Add\"/>.\n\t\t/// </summary>\n\t\t/// <param name=\"addedId\">A return value of <see cref=\"Add\"/>.</param>\n\t\t/// <exception cref=\"ArgumentException\"></exception>\n\t\tpublic void Remove(int addedId) {\n\t\t\taddedId--;\n\t\t\tif (_a == null || (uint)addedId >= _a.Length || _a[addedId] == default) throw new ArgumentException();\n\t\t\tif (!Api.UnhookWinEvent(_a[addedId])) print.warning(\"Failed to unhook WinEventHook.\");\n\t\t\t_a[addedId] = default;\n\t\t}\n\t\t\n\t\t///// <summary>\n\t\t///// True if hooks are set.\n\t\t///// </summary>\n\t\t//public bool Installed => _a != null;\n\t\t\n\t\t/// <summary>\n\t\t/// Removes all hooks.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Does nothing if already removed or wasn't set.\n\t\t/// Must be called from the same thread that sets the hook.\n\t\t/// </remarks>\n\t\tpublic void Unhook() {\n\t\t\tif (_a != null) {\n\t\t\t\tforeach (var hh in _a) {\n\t\t\t\t\tif (hh == default) continue;\n\t\t\t\t\tif (!Api.UnhookWinEvent(hh)) print.warning(\"WinEventHook.Unhook() failed.\");\n\t\t\t\t}\n\t\t\t\t_a = null;\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Calls <see cref=\"Unhook\"/>.\n\t\t/// </summary>\n\t\tpublic void Dispose() {\n\t\t\tUnhook();\n\t\t\t_proc1 = null;\n\t\t\tt_antiGC.Remove(this);\n\t\t\tGC.SuppressFinalize(this);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Prints a warning if the variable is not disposed. Cannot dispose in finalizer.\n\t\t/// </summary>\n\t\t~WinEventHook() {\n\t\t\t//MSDN: UnhookWinEvent fails if called from a thread different from the call that corresponds to SetWinEventHook.\n\t\t\tif (_a != null) print.warning(\"Non-disposed WinEventHook variable.\");\n\t\t}\n\t\t\n\t\tvoid _HookProc(IntPtr hHook, EEvent ev, wnd w, EObjid idObject, int idChild, int thread, int time) {\n\t\t\ttry {\n\t\t\t\t_proc2(new HookData.WinEvent(this, ev, w, idObject, idChild, thread, time));\n\t\t\t}\n\t\t\tcatch (Exception ex) { WindowsHook.OnException_(ex); }\n\t\t}\n\t}\n}\n\nnamespace Au.Types {\n\tpublic static partial class HookData {\n\t\t/// <summary>\n\t\t/// Hook data for the hook procedure set by <see cref=\"WinEventHook\"/>.\n\t\t/// More info: API <ms>WinEventProc</ms>.\n\t\t/// </summary>\n\t\tpublic unsafe struct WinEvent {\n\t\t\t/// <summary>The caller object of your hook procedure. For example can be used to unhook.</summary>\n\t\t\tpublic readonly WinEventHook hook;\n\t\t\t\n\t\t\t/// <summary>API <ms>WinEventProc</ms></summary>\n\t\t\tpublic readonly EEvent event_;\n\t\t\t\n\t\t\t/// <summary>API <ms>WinEventProc</ms></summary>\n\t\t\tpublic readonly wnd w;\n\t\t\t\n\t\t\t/// <summary>API <ms>WinEventProc</ms></summary>\n\t\t\tpublic readonly EObjid idObject;\n\t\t\t\n\t\t\t/// <summary>API <ms>WinEventProc</ms></summary>\n\t\t\tpublic readonly int idChild;\n\t\t\t\n\t\t\t/// <summary>API <ms>WinEventProc</ms></summary>\n\t\t\tpublic readonly int thread;\n\t\t\t\n\t\t\t/// <summary>API <ms>WinEventProc</ms></summary>\n\t\t\tpublic readonly int time;\n\t\t\t\n\t\t\tinternal WinEvent(WinEventHook hook, EEvent event_, wnd hwnd, EObjid idObject, int idChild, int thread, int time) {\n\t\t\t\tthis.hook = hook;\n\t\t\t\tthis.event_ = event_;\n\t\t\t\tthis.w = hwnd;\n\t\t\t\tthis.idObject = idObject;\n\t\t\t\tthis.idChild = idChild;\n\t\t\t\tthis.thread = thread;\n\t\t\t\tthis.time = time;\n\t\t\t}\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// Calls <see cref=\"elm.fromEvent\"/>.\n\t\t\t/// </summary>\n\t\t\tpublic elm GetElm() {\n\t\t\t\treturn elm.fromEvent(w, idObject, idChild);\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au/Au.More/WindowsHook.cs",
    "content": "namespace Au.More {\n\t/// <summary>\n\t/// Wraps API <ms>SetWindowsHookEx</ms>.\n\t/// </summary>\n\t/// <remarks>\n\t/// Hooks are used to receive notifications about various system events. Keyboard and mouse input, window messages, various window events.\n\t/// \n\t/// Threads that use hooks must process Windows messages. For example have a window/dialog/messagebox, or use a \"wait-for\" function that dispatches messages or has such option (see <see cref=\"Seconds.DoEvents\"/>).\n\t/// \n\t/// <note type=\"important\">The variable should be disposed when don't need, or at least unhooked, either explicitly (call <see cref=\"Dispose\"/> or <see cref=\"Unhook\"/> in same thread) or with <c>using</c>. Can do it in hook procedure.</note>\n\t/// \n\t/// <note type=\"warning\">Avoid many hooks. Each low-level keyboard or mouse hook makes the computer slower, even if the hook procedure is fast. On each input event (key down, key up, mouse move, click, wheel) Windows sends a message to your thread.</note>\n\t/// \n\t/// To receive hook events is used a callback function, aka hook procedure. Hook procedures of some hook types can block some events (call <see cref=\"HookData.Keyboard.BlockEvent\"/> or return <c>true</c>). Blocked events are not sent to apps and older hooks.\n\t/// \n\t/// Delegates of hook procedures are protected from GC until called <see cref=\"Dispose\"/> or until the thread ends, even of unreferenced <c>WindowsHook</c> variables.\n\t/// \n\t/// UI element functions may fail in hook procedures of low-level keyboard and mouse hooks. Workarounds exist.\n\t/// \n\t/// Exists an alternative way to monitor keyboard or mouse events - raw input API. Good: less overhead; can detect from which device the input event came. Bad: cannot block events; incompatible with low-level keyboard hooks. This library does not have functions to make the API easier to use.\n\t/// </remarks>\n\t[DebuggerStepThrough]\n\tpublic sealed class WindowsHook : IDisposable {\n\t\tIntPtr _hh; //HHOOK\n\t\treadonly Api.HOOKPROC _proc1; //our intermediate dispatcher hook proc that calls _proc2\n\t\tDelegate _proc2; //caller's hook proc\n\t\treadonly string _hookTypeString; //\"Keyboard\" etc\n\t\treadonly int _hookType; //Api.WH_\n\t\treadonly bool _ignoreAuInjected;\n\t\t[ThreadStatic] static List<WindowsHook> t_antiGC;\n\n\t\t/// <summary>\n\t\t/// Sets a low-level keyboard hook (<ms>WH_KEYBOARD_LL</ms>).\n\t\t/// See API <ms>SetWindowsHookEx</ms>.\n\t\t/// </summary>\n\t\t/// <returns>New <see cref=\"WindowsHook\"/> object that manages the hook.</returns>\n\t\t/// <param name=\"hookProc\">\n\t\t/// The hook procedure (function that handles hook events).\n\t\t/// Must return as soon as possible. More info: <see cref=\"LowLevelHooksTimeout\"/>.\n\t\t/// If calls <see cref=\"HookData.Keyboard.BlockEvent\"/> or <see cref=\"HookData.ReplyMessage\"/><c>(true)</c>, the event is not sent to apps and other hooks.\n\t\t/// Event data cannot be modified.\n\t\t/// <para>NOTE: When the hook procedure returns, the parameter variable becomes invalid and unsafe to use. If you need the data for later use, copy its properties and not whole variable.</para>\n\t\t/// </param>\n\t\t/// <param name=\"ignoreAuInjected\">Don't call the hook procedure for events sent by functions of this library. Default <c>true</c>.</param>\n\t\t/// <param name=\"setNow\">Set hook now. Default <c>true</c>.</param>\n\t\t/// <exception cref=\"AuException\">Failed.</exception>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// var stop = false;\n\t\t/// using var hook = WindowsHook.Keyboard(x => {\n\t\t/// \tprint.it(x);\n\t\t/// \tif(x.vkCode == KKey.Escape) { stop = true; x.BlockEvent(); }\n\t\t/// });\n\t\t/// dialog.show(\"hook\");\n\t\t/// //or\n\t\t/// //wait.doEventsUntil(-10, () => stop); //wait max 10 s for Esc key\n\t\t/// //print.it(\"the end\");\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic static WindowsHook Keyboard(Action<HookData.Keyboard> hookProc, bool ignoreAuInjected = true, bool setNow = true)\n\t\t\t=> new(Api.WH_KEYBOARD_LL, hookProc, setNow, 0, ignoreAuInjected);\n\n\t\t/// <summary>\n\t\t/// Sets a low-level mouse hook (<ms>WH_MOUSE_LL</ms>).\n\t\t/// See API <ms>SetWindowsHookEx</ms>.\n\t\t/// </summary>\n\t\t/// <returns>New <see cref=\"WindowsHook\"/> object that manages the hook.</returns>\n\t\t/// <param name=\"hookProc\">\n\t\t/// The hook procedure (function that handles hook events).\n\t\t/// Must return as soon as possible. More info: <see cref=\"LowLevelHooksTimeout\"/>.\n\t\t/// If calls <see cref=\"HookData.Mouse.BlockEvent\"/> or <see cref=\"HookData.ReplyMessage\"/><c>(true)</c>, the event is not sent to apps and other hooks.\n\t\t/// Event data cannot be modified.\n\t\t/// <para>NOTE: When the hook procedure returns, the parameter variable becomes invalid and unsafe to use. If you need the data for later use, copy its properties and not whole variable.</para>\n\t\t/// </param>\n\t\t/// <param name=\"ignoreAuInjected\">Don't call the hook procedure for events sent by functions of this library. Default <c>true</c>.</param>\n\t\t/// <param name=\"setNow\">Set hook now. Default <c>true</c>.</param>\n\t\t/// <exception cref=\"AuException\">Failed.</exception>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// var stop = false;\n\t\t/// using var hook = WindowsHook.Mouse(x => {\n\t\t/// \tprint.it(x);\n\t\t/// \tif(x.Event == HookData.MouseEvent.RightButton) { stop = x.IsButtonUp; x.BlockEvent(); }\n\t\t/// });\n\t\t/// dialog.show(\"hook\");\n\t\t/// //or\n\t\t/// //wait.doEventsUntil(-10, () => stop); //wait max 10 s for right-click\n\t\t/// //print.it(\"the end\");\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic static WindowsHook Mouse(Action<HookData.Mouse> hookProc, bool ignoreAuInjected = true, bool setNow = true)\n\t\t\t=> new(Api.WH_MOUSE_LL, hookProc, setNow, 0, ignoreAuInjected);\n\n\t\tinternal static WindowsHook MouseRaw_(Func<nint, nint, bool> hookProc, bool ignoreAuInjected = true, bool setNow = true)\n\t\t\t=> new(Api.WH_MOUSE_LL, hookProc, setNow, 0, ignoreAuInjected, \"Mouse\");\n\n\t\t/// <summary>\n\t\t/// Sets a <ms>WH_CBT</ms> hook for a thread of this process.\n\t\t/// See API <ms>SetWindowsHookEx</ms>.\n\t\t/// </summary>\n\t\t/// <returns>New <see cref=\"WindowsHook\"/> object that manages the hook.</returns>\n\t\t/// <param name=\"hookProc\">\n\t\t/// Hook procedure (function that handles hook events).\n\t\t/// Must return as soon as possible.\n\t\t/// If returns <c>true</c>, the event is canceled. For some events you can modify some fields of event data.\n\t\t/// <para>NOTE: When the hook procedure returns, the parameter variable becomes invalid and unsafe to use. If you need the data for later use, copy its properties and not the variable.</para>\n\t\t/// </param>\n\t\t/// <param name=\"threadId\">Native thread id, or 0 for this thread. The thread must belong to this process.</param>\n\t\t/// <param name=\"setNow\">Set hook now. Default <c>true</c>.</param>\n\t\t/// <exception cref=\"AuException\">Failed.</exception>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// using var hook = WindowsHook.ThreadCbt(x => {\n\t\t/// \tprint.it(x.code);\n\t\t/// \tswitch(x.code) {\n\t\t/// \tcase HookData.CbtEvent.ACTIVATE:\n\t\t/// \t\tprint.it(x.Hwnd);\n\t\t/// \t\tbreak;\n\t\t/// \tcase HookData.CbtEvent.CREATEWND:\n\t\t/// \t\tvar c=x.CreationInfo->lpcs;\n\t\t/// \t\tprint.it(x.Hwnd, c->x, c->lpszName);\n\t\t/// \t\tc->x=500;\n\t\t/// \t\tbreak;\n\t\t/// \t}\n\t\t/// \treturn false;\n\t\t/// });\n\t\t/// dialog.showOkCancel(\"hook\");\n\t\t/// //new Form().ShowDialog(); //to test MINMAX\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic static WindowsHook ThreadCbt(Func<HookData.ThreadCbt, bool> hookProc, int threadId = 0, bool setNow = true)\n\t\t\t=> new(Api.WH_CBT, hookProc, setNow, threadId);\n\n\t\t/// <summary>\n\t\t/// Sets a <ms>WH_GETMESSAGE</ms> hook for a thread of this process.\n\t\t/// See API <ms>SetWindowsHookEx</ms>.\n\t\t/// </summary>\n\t\t/// <returns>New <see cref=\"WindowsHook\"/> object that manages the hook.</returns>\n\t\t/// <param name=\"hookProc\">\n\t\t/// The hook procedure (function that handles hook events).\n\t\t/// Must return as soon as possible.\n\t\t/// The event cannot be canceled. As a workaround, you can set <c>msg->message=0</c>. Also can modify other fields.\n\t\t/// <para>NOTE: When the hook procedure returns, the pointer field of the parameter variable becomes invalid and unsafe to use.</para>\n\t\t/// </param>\n\t\t/// <param name=\"threadId\">Native thread id, or 0 for this thread. The thread must belong to this process.</param>\n\t\t/// <param name=\"setNow\">Set hook now. Default <c>true</c>.</param>\n\t\t/// <exception cref=\"AuException\">Failed.</exception>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// using var hook = WindowsHook.ThreadGetMessage(x => {\n\t\t/// \tprint.it(x.msg->ToString(), x.PM_NOREMOVE);\n\t\t/// });\n\t\t/// dialog.show(\"hook\");\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic static WindowsHook ThreadGetMessage(Action<HookData.ThreadGetMessage> hookProc, int threadId = 0, bool setNow = true)\n\t\t\t=> new(Api.WH_GETMESSAGE, hookProc, setNow, threadId);\n\n\t\t/// <summary>\n\t\t/// Sets a <ms>WH_GETMESSAGE</ms> hook for a thread of this process.\n\t\t/// See API <ms>SetWindowsHookEx</ms>.\n\t\t/// </summary>\n\t\t/// <returns>New <see cref=\"WindowsHook\"/> object that manages the hook.</returns>\n\t\t/// <param name=\"hookProc\">\n\t\t/// The hook procedure (function that handles hook events).\n\t\t/// Must return as soon as possible.\n\t\t/// If returns <c>true</c>, the event is canceled.\n\t\t/// </param>\n\t\t/// <param name=\"threadId\">Native thread id, or 0 for this thread. The thread must belong to this process.</param>\n\t\t/// <param name=\"setNow\">Set hook now. Default <c>true</c>.</param>\n\t\t/// <exception cref=\"AuException\">Failed.</exception>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// using var hook = WindowsHook.ThreadKeyboard(x => {\n\t\t/// \tprint.it(x.key, 0 != (x.lParam & 0x80000000) ? \"up\" : \"\", x.lParam, x.PM_NOREMOVE);\n\t\t/// \treturn false;\n\t\t/// });\n\t\t/// dialog.show(\"hook\");\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic static WindowsHook ThreadKeyboard(Func<HookData.ThreadKeyboard, bool> hookProc, int threadId = 0, bool setNow = true)\n\t\t\t=> new(Api.WH_KEYBOARD, hookProc, setNow, threadId);\n\n\t\t/// <summary>\n\t\t/// Sets a <ms>WH_MOUSE</ms> hook for a thread of this process.\n\t\t/// See API <ms>SetWindowsHookEx</ms>.\n\t\t/// </summary>\n\t\t/// <returns>New <see cref=\"WindowsHook\"/> object that manages the hook.</returns>\n\t\t/// <param name=\"hookProc\">\n\t\t/// The hook procedure (function that handles hook events).\n\t\t/// Must return as soon as possible.\n\t\t/// If returns <c>true</c>, the event is canceled.\n\t\t/// <para>NOTE: When the hook procedure returns, the pointer field of the parameter variable becomes invalid and unsafe to use.</para>\n\t\t/// </param>\n\t\t/// <param name=\"threadId\">Native thread id, or 0 for this thread. The thread must belong to this process.</param>\n\t\t/// <param name=\"setNow\">Set hook now. Default <c>true</c>.</param>\n\t\t/// <exception cref=\"AuException\">Failed.</exception>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// using var hook = WindowsHook.ThreadMouse(x => {\n\t\t/// \tprint.it(x.message, x.m->pt, x.m->hwnd, x.PM_NOREMOVE);\n\t\t/// \treturn false;\n\t\t/// });\n\t\t/// dialog.show(\"hook\");\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic static WindowsHook ThreadMouse(Func<HookData.ThreadMouse, bool> hookProc, int threadId = 0, bool setNow = true)\n\t\t\t=> new(Api.WH_MOUSE, hookProc, setNow, threadId);\n\n\t\t/// <summary>\n\t\t/// Sets a <ms>WH_CALLWNDPROC</ms> hook for a thread of this process.\n\t\t/// See API <ms>SetWindowsHookEx</ms>.\n\t\t/// </summary>\n\t\t/// <returns>A new <see cref=\"WindowsHook\"/> object that manages the hook.</returns>\n\t\t/// <param name=\"hookProc\">\n\t\t/// The hook procedure (function that handles hook events).\n\t\t/// Must return as soon as possible.\n\t\t/// The event cannot be canceled or modified.\n\t\t/// <para>NOTE: When the hook procedure returns, the pointer field of the parameter variable becomes invalid and unsafe to use.</para>\n\t\t/// </param>\n\t\t/// <param name=\"threadId\">Native thread id, or 0 for this thread. The thread must belong to this process.</param>\n\t\t/// <param name=\"setNow\">Set hook now. Default <c>true</c>.</param>\n\t\t/// <exception cref=\"AuException\">Failed.</exception>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// using var hook = WindowsHook.ThreadCallWndProc(x => {\n\t\t/// \tref var m = ref *x.msg;\n\t\t/// \tWndUtil.PrintMsg(out var s, m.hwnd, m.message, m.wParam, m.lParam);\n\t\t/// \tprint.it(s, x.sentByOtherThread);\n\t\t/// });\n\t\t/// dialog.show(\"hook\");\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic static WindowsHook ThreadCallWndProc(Action<HookData.ThreadCallWndProc> hookProc, int threadId = 0, bool setNow = true)\n\t\t\t=> new(Api.WH_CALLWNDPROC, hookProc, setNow, threadId);\n\n\t\t/// <summary>\n\t\t/// Sets a <ms>WH_CALLWNDPROCRET</ms> hook for a thread of this process.\n\t\t/// See API <ms>SetWindowsHookEx</ms>.\n\t\t/// </summary>\n\t\t/// <inheritdoc cref=\"ThreadCallWndProc\"/>\n\t\tpublic static WindowsHook ThreadCallWndProcRet(Action<HookData.ThreadCallWndProcRet> hookProc, int threadId = 0, bool setNow = true)\n\t\t\t=> new(Api.WH_CALLWNDPROCRET, hookProc, setNow, threadId);\n\n\t\tWindowsHook(int hookType, Delegate hookProc, bool setNow, int tid, bool ignoreAuInjected = false, [CallerMemberName] string m_ = null) {\n\t\t\tNot_.Null(hookProc);\n\t\t\t_proc2 = hookProc;\n\t\t\t_hookType = hookType;\n\t\t\t_hookTypeString = m_;\n\t\t\t_ignoreAuInjected = ignoreAuInjected;\n\t\t\tif (hookType is Api.WH_KEYBOARD_LL or Api.WH_MOUSE_LL) {\n\t\t\t\t_proc1 = _HookProcLL;\n\t\t\t\t//JIT-compile our hook proc and some functions it may call. OS gives us only 300 ms by default.\n\t\t\t\tif (!s_jit1) {\n\t\t\t\t\ts_jit1 = true;\n\t\t\t\t\tJit_.Compile(typeof(WindowsHook), nameof(_HookProcLL));\n\t\t\t\t\t_ = perf.ms;\n\t\t\t\t\t_ = keys.KeyTypes_.IsMod(KKey.Shift) && _DontBlockMod;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t_proc1 = _HookProc;\n\t\t\t}\n\t\t\tif (setNow) Hook(tid);\n\t\t\t(t_antiGC ??= new()).Add(this);\n\t\t}\n\t\tstatic bool s_jit1;\n\n\t\t/// <summary>\n\t\t/// Sets the hook.\n\t\t/// </summary>\n\t\t/// <param name=\"threadId\">If the hook type is a thread hook - thread id, or 0 for current thread. Else not used and must be 0.</param>\n\t\t/// <exception cref=\"AuException\">Failed.</exception>\n\t\t/// <exception cref=\"InvalidOperationException\">The hook is already set.</exception>\n\t\t/// <exception cref=\"ArgumentException\"><i>threadId</i> not 0 and the hook type is not a thread hook.</exception>\n\t\t/// <remarks>\n\t\t/// Usually don't need to call this function, because the <c>WindowsHook</c> static methods that return a new <c>WindowsHook</c> object by default call it.\n\t\t/// </remarks>\n\t\tpublic void Hook(int threadId = 0) {\n\t\t\tif (_proc2 == null) throw new ObjectDisposedException(nameof(WindowsHook));\n\t\t\tif (_hh != default) throw new InvalidOperationException(\"The hook is already set.\");\n\t\t\tif (_hookType is Api.WH_KEYBOARD_LL or Api.WH_MOUSE_LL) {\n\t\t\t\tif (threadId != 0) throw new ArgumentException(\"threadId must be 0\");\n\t\t\t} else if (threadId == 0) {\n\t\t\t\tthreadId = Api.GetCurrentThreadId();\n\t\t\t}\n\t\t\t_hh = Api.SetWindowsHookEx(_hookType, _proc1, default, threadId);\n\t\t\tif (_hh == default) throw new AuException(0, \"*set hook\");\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Removes the hook.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Does nothing if already removed or wasn't set.\n\t\t/// Later you can call <see cref=\"Hook\"/> to set hook again.\n\t\t/// Note: call <see cref=\"Dispose\"/> instead if will not need to hook again.\n\t\t/// </remarks>\n\t\tpublic void Unhook() {\n\t\t\tif (_hh != default) {\n\t\t\t\t_Restore_UnhookOld();\n\t\t\t\tbool ok = Api.UnhookWindowsHookEx(_hh);\n\t\t\t\tif (!ok) print.warning($\"WindowsHook.Unhook() failed ({_hookTypeString}). {lastError.message}\");\n\t\t\t\t_hh = default;\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Rehooks this low-level keyboard or mouse hook.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Low level hooks may be occasionally disabled by the OS or other hooks. Workaround - call this function eg every 10 s in same thread. For example use <see cref=\"timer\"/>. Don't call too frequently, eg every 1 s.\n\t\t/// This function unhooks current hook and sets new hook. Ensures that no events are missed or duplicate during it.\n\t\t/// </remarks>\n\t\t/// <exception cref=\"InvalidOperationException\">The hook type isn't low-level keyboard or mouse.</exception>\n\t\tpublic void Restore() {\n\t\t\tif (_hookType is not (Api.WH_KEYBOARD_LL or Api.WH_MOUSE_LL)) throw new InvalidOperationException();\n\t\t\tif (_proc2 == null) throw new ObjectDisposedException(nameof(WindowsHook));\n\t\t\tif (_hookType is Api.WH_KEYBOARD_LL && DontRestoreKeyboardHooks_) return;\n\n\t\t\t//If we simply unhook/hook here, some events are missed.\n\t\t\t//\tRestoring usually takes 0.2 - 0.5 ms. And it seems the new hook starts working with a delay.\n\t\t\t//\tIf restoring every 10 s, could miss maybe 1/10000 triggers.\n\t\t\t//\tTested: when restoring every 15 ms, missed 3/50 triggers.\n\t\t\t//\tSolution: unhook the old hook after several ms. To avoid duplicate events, unhook it in the hook proc too.\n#if false\n\t\t\tif (_hh != default) Api.UnhookWindowsHookEx(_hh);\n\t\t\t_hh = Api.SetWindowsHookEx(_hookType, _proc1, default, 0);\n\t\t\tif (_hh == default) throw new AuException(0, \"*set hook\");\n#else\n\t\t\t_Restore_UnhookOld();\n\t\t\tvar hh = Api.SetWindowsHookEx(_hookType, _proc1, default, 0);\n\t\t\tif (hh != default) {\n\t\t\t\tif (_hh != default) timer.after(10, _ => _Restore_UnhookOld());\n\t\t\t\t_oldHook = _hh;\n\t\t\t\t_hh = hh;\n\t\t\t} else {\n\t\t\t\tDebug_.Print(\"failed\");\n\t\t\t}\n#endif\n\t\t}\n\t\tIntPtr _oldHook;\n\t\t\n\t\t/// <summary>\n\t\t/// Can be used to temporarily disable <see cref=\"Restore\"/> of all keyboard hooks in all processes.\n\t\t/// For example when a hotkey control is focused.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// <c>Restore</c> is disabled when the number of <c>=true</c> calls is greater than the number of <c>=false</c> calls. \n\t\t/// </remarks>\n\t\tinternal static unsafe bool DontRestoreKeyboardHooks_ {\n\t\t\tget => SharedMemory_.Ptr->winHook.dontRestoreKeyboardHooks > 0;\n\t\t\tset => Interlocked.Add(ref SharedMemory_.Ptr->winHook.dontRestoreKeyboardHooks, value ? 1 : -1);\n\t\t}\n\n\t\tvoid _Restore_UnhookOld() {\n\t\t\tif (_oldHook != default) {\n\t\t\t\tbool ok = Api.UnhookWindowsHookEx(_oldHook);\n\t\t\t\t_oldHook = default;\n\t\t\t\tDebug_.PrintIf(!ok, \"failed to unhook old\");\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if the hook is set.\n\t\t/// </summary>\n\t\tpublic bool IsSet => _hh != default;\n\n\t\t///// <summary>\n\t\t///// Disable warning \"Non-disposed WindowsHook variable\".\n\t\t///// </summary>\n\t\t//public bool NoWarningNondisposed { get; set; }\n\n\t\t/// <summary>\n\t\t/// Calls <see cref=\"Unhook\"/> and disposes this object.\n\t\t/// </summary>\n\t\tpublic void Dispose() {\n\t\t\tUnhook();\n\t\t\t_proc2 = null;\n\t\t\tt_antiGC.Remove(this);\n\t\t\tGC.SuppressFinalize(this);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Prints a warning if the variable is not disposed. Cannot dispose in finalizer.\n\t\t/// </summary>\n\t\t~WindowsHook() {\n\t\t\t//unhooking in finalizer thread makes no sense. Must unhook in same thread, else fails.\n\t\t\tif (_hh != default) print.warning($\"Non-disposed WindowsHook ({_hookTypeString}) variable.\");\n\t\t\t//ok if unhooked but not disposed. If we are here, the thread ended and therefore don't need to remove this from t_antiGC.\n\t\t}\n\n\t\tunsafe nint _HookProc(int code, nint wParam, nint lParam) {\n\t\t\tif (code >= 0) {\n\t\t\t\ttry {\n\t\t\t\t\tbool eat = false;\n\n\t\t\t\t\tswitch (_proc2) {\n\t\t\t\t\tcase Func<HookData.ThreadCbt, bool> p:\n\t\t\t\t\t\teat = p(new HookData.ThreadCbt(this, code, wParam, lParam));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase Action<HookData.ThreadGetMessage> p:\n\t\t\t\t\t\tp(new HookData.ThreadGetMessage(this, wParam, lParam));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase Func<HookData.ThreadKeyboard, bool> p:\n\t\t\t\t\t\teat = p(new HookData.ThreadKeyboard(this, code, wParam, lParam));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase Func<HookData.ThreadMouse, bool> p:\n\t\t\t\t\t\teat = p(new HookData.ThreadMouse(this, code, wParam, lParam));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase Action<HookData.ThreadCallWndProc> p:\n\t\t\t\t\t\tp(new HookData.ThreadCallWndProc(this, wParam, lParam));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase Action<HookData.ThreadCallWndProcRet> p:\n\t\t\t\t\t\tp(new HookData.ThreadCallWndProcRet(this, wParam, lParam));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (eat) return 1;\n\t\t\t\t}\n\t\t\t\tcatch (Exception ex) { OnException_(ex); }\n\t\t\t}\n\n\t\t\treturn Api.CallNextHookEx(default, code, wParam, lParam);\n\t\t}\n\n\t\tunsafe nint _HookProcLL(int code, nint wParam, nint lParam) {\n\t\t\t_Restore_UnhookOld();\n\t\t\tif (code >= 0) {\n\t\t\t\ttry {\n\t\t\t\t\t//using var p1 = perf.local();\n\t\t\t\t\tbool eat = false;\n\t\t\t\t\tlong t1 = 0;\n\t\t\t\t\tAction<HookData.Mouse> pm1;\n\t\t\t\t\tFunc<nint, nint, bool> pm2;\n\n\t\t\t\t\tswitch (_proc2) {\n\t\t\t\t\tcase Action<HookData.Keyboard> p:\n\t\t\t\t\t\tvar kll = (Api.KBDLLHOOKSTRUCT*)lParam;\n\t\t\t\t\t\tvar vk = (KKey)kll->vkCode;\n\t\t\t\t\t\tif (kll->IsInjected) {\n\t\t\t\t\t\t\tif (kll->IsInjectedByAu) {\n\t\t\t\t\t\t\t\tif (kll->vkCode == 0) goto gr; //used to enable activating windows\n\t\t\t\t\t\t\t\tif (!kll->IsUp) Triggers.AutotextTriggers.ResetEverywhere = true;\n\t\t\t\t\t\t\t\tif (_ignoreAuInjected) goto gr;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (vk == KKey.MouseX2 && kll->dwExtraInfo == 1354291109) goto gr; //QM2 sync code\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t//When keys.Internal_.ReleaseModAndCapsLock sends Shift to turn off CapsLock,\n\t\t\t\t\t\t\t//\thooks receive a non-injected LShift down, CapsLock down/up and injected LShift up.\n\t\t\t\t\t\t\t//\tOur triggers would recover, but cannot auto-repeat. Better don't call the hookproc.\n\t\t\t\t\t\t\tif ((vk == KKey.CapsLock || vk == KKey.LShift) && _ignoreAuInjected && _IgnoreLShiftCaps) goto gr;\n\n\t\t\t\t\t\t\t//Test how our triggers recover when a modifier down or up event is lost. Or when triggers started while a modifier is down.\n\t\t\t\t\t\t\t//if(keys.isScrollLock) {\n\t\t\t\t\t\t\t//\t//if(vk == KKey.LCtrl && !kll->IsUp) { print.it(\"lost Ctrl down\"); goto gr; }\n\t\t\t\t\t\t\t//\tif(vk == KKey.LCtrl && kll->IsUp) { print.it(\"lost Ctrl up\"); goto gr; }\n\t\t\t\t\t\t\t//}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t//if (keys.KeyTypes_.IsMod(vk) && _DontBlockMod) goto gr; //old version, creates problems\n\t\t\t\t\t\tt1 = perf.ms;\n\t\t\t\t\t\t//p1.Next();\n\t\t\t\t\t\tp(new HookData.Keyboard(this, lParam)); //info: wParam is message, but it is not useful, everything is in lParam\n\t\t\t\t\t\tif (eat = kll->BlockEvent) {\n\t\t\t\t\t\t\tkll->BlockEvent = false;\n\t\t\t\t\t\t\tif (keys.KeyTypes_.IsMod(vk) && _DontBlockMod && kll->IsUp) eat = false;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase Action<HookData.Mouse> p:\n\t\t\t\t\t\tpm1 = p; pm2 = null;\n\t\t\t\t\t\tgm1:\n\t\t\t\t\t\tvar mll = (Api.MSLLHOOKSTRUCT*)lParam;\n\t\t\t\t\t\tswitch ((int)wParam) {\n\t\t\t\t\t\tcase Api.WM_LBUTTONDOWN: case Api.WM_RBUTTONDOWN: Triggers.AutotextTriggers.ResetEverywhere = true; break;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (_ignoreAuInjected && mll->IsInjectedByAu) goto gr;\n\n\t\t\t\t\t\t//API bug workaround. In DPI-scaled windows on click mhsLL->pt is logical, although on move/wheel is physical. Must be always physical.\n\t\t\t\t\t\t//At first noticed only on Win10. But then noticed the same on Win7, although used to be correct. OK on Win8.1. Maybe depends on some other conditions, eg UAC IL, DPI, multimonitor.\n\t\t\t\t\t\t//Now it seems the bug is fixed on Win10. Found on SO: \"Microsoft fixed it in 10.0.14393\"; it is version 1607, August 2, 2016; but I cannot confirm it.\n\t\t\t\t\t\t//The wrong coords are the same as GetCursorPos. Only GetPhysicalCursorPos does not lie. Api.GetCursorPos is mapped to GetPhysicalCursorPos.\n\t\t\t\t\t\t//Note: on WM_MOUSEMOVE Get[Physical]CursorPos returns previous coords. On other messages same as hook.\n\t\t\t\t\t\tif (wParam != Api.WM_MOUSEMOVE /*&& osVersion.winVer < osVersion.win10*/) Api.GetCursorPos(out mll->pt);\n\n\t\t\t\t\t\tt1 = perf.ms;\n\t\t\t\t\t\tif (pm2 != null) {\n\t\t\t\t\t\t\teat = pm2(wParam, lParam);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tpm1(new HookData.Mouse(this, wParam, lParam));\n\t\t\t\t\t\t\tif (eat = mll->BlockEvent) mll->BlockEvent = false;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase Func<nint, nint, bool> p: //raw mouse\n\t\t\t\t\t\tpm2 = p; pm1 = null;\n\t\t\t\t\t\tgoto gm1;\n\t\t\t\t\t}\n\n\t\t\t\t\t//Prevent Windows disabling the low-level key/mouse hook.\n\t\t\t\t\t//\tHook proc must return in HKEY_CURRENT_USER\\Control Panel\\Desktop:LowLevelHooksTimeout ms.\n\t\t\t\t\t//\t\tDefault 300. On Win10 max 1000 (bigger registry value is ignored and used 1000).\n\t\t\t\t\t//\tOn timeout Windows:\n\t\t\t\t\t//\t\t1. Does not wait more. Passes the message to the next hook etc, and we cannot return 1 to block it.\n\t\t\t\t\t//\t\t2. Kills the hook after several such cases. Usually 6 keys or 11 mouse events.\n\t\t\t\t\t//\t\t3. Makes the hook useless: next times does not wait for it, and we cannot return 1 to block the event.\n\t\t\t\t\t//\tSomehow does not apply 2 and 3 to some apps, eg C# apps created by Visual Studio, although applies to those created not by VS. I did not find why.\n\t\t\t\t\tif (t1 != 0 && (t1 = perf.ms - t1) > 200 && !Debugger.IsAttached) {\n\t\t\t\t\t\tif (t1 > LowLevelHooksTimeout - 50) {\n\t\t\t\t\t\t\tvar s1 = _hookType == Api.WH_KEYBOARD_LL ? \"key\" : \"mouse\";\n\t\t\t\t\t\t\tvar s2 = eat ? $\" On timeout the {s1} message is passed to the active window, other hooks, etc.\" : null;\n\t\t\t\t\t\t\t//print.warning($\"Possible hook timeout. Hook procedure time: {t1} ms. LowLevelHooksTimeout: {LowLevelHooksTimeout} ms.{s2}\"); //too slow first time\n\t\t\t\t\t\t\t//print.it($\"Warning: Possible hook timeout. Hook procedure time: {t1} ms. LowLevelHooksTimeout: {LowLevelHooksTimeout} ms.{s2}\\r\\n{new StackTrace(0, false)}\"); //first Write() JIT 30 ms\n\t\t\t\t\t\t\tThreadPool.QueueUserWorkItem(s3 => print.it(s3), $\"Warning: Possible hook timeout. Hook procedure time: {t1} ms. LowLevelHooksTimeout: {LowLevelHooksTimeout} ms.{s2}\\r\\n{new StackTrace(0, false)}\"); //fast if with false. But async print can be confusing.\n\t\t\t\t\t\t}\n\t\t\t\t\t\t//FUTURE: print warning if t1 is >25 frequently. Unhook and don't rehook if >LowLevelHooksTimeout frequently.\n\n\t\t\t\t\t\tUnhook();\n\t\t\t\t\t\t_hh = Api.SetWindowsHookEx(_hookType, _proc1, default, 0);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (eat) return 1;\n\t\t\t\t}\n\t\t\t\tcatch (Exception ex) { OnException_(ex); }\n\t\t\t}\n\t\t\tgr:\n\t\t\treturn Api.CallNextHookEx(default, code, wParam, lParam);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets the max time in milliseconds allowed by Windows for low-level keyboard and mouse hook procedures.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Gets registry value <c>HKEY_CURRENT_USER\\Control Panel\\Desktop:LowLevelHooksTimeout</c>. If it is missing, returns 300; it is the default value used by Windows. If greater than 1000, returns 1000, because Windows 10 ignores bigger values.\n\t\t/// \n\t\t/// If a hook procedure takes more time, Windows does not wait. Then its return value is ignored, and the event is passed to other apps, hooks, etc. After several such cases Windows may fully or partially disable the hook. This class detects such cases; then restores the hook and prints a warning. If the warning is rare, you can ignore it. If frequent, it means your hook procedure is too slow.\n\t\t/// \n\t\t/// Callback functions of keyboard and mouse triggers are called in a hook procedure, therefore must be as fast as possible. More info: <see cref=\"Triggers.TriggerFuncs\"/>.\n\t\t/// \n\t\t/// More info: <ms>registry LowLevelHooksTimeout</ms>.\n\t\t/// \n\t\t/// Note: After changing the timeout in registry, it is not applied immediately. Need to log off/on.\n\t\t/// </remarks>\n\t\tpublic static int LowLevelHooksTimeout {\n\t\t\tget {\n\t\t\t\tif (s_lowLevelHooksTimeout == 0) {\n\t\t\t\t\t//default 300, tested on Win10 and 7\n\t\t\t\t\t//max 1000 on Win10. On Win7 more. Not tested on Win8. On Win7/8 may be changed by a Windows update.\n\t\t\t\t\ts_lowLevelHooksTimeout =\n\t\t\t\t\t\tMicrosoft.Win32.Registry.GetValue(@\"HKEY_CURRENT_USER\\Control Panel\\Desktop\", \"LowLevelHooksTimeout\", null) is int v\n\t\t\t\t\t\t? (int)Math.Min(1000u, (uint)v)\n\t\t\t\t\t\t: 300;\n\t\t\t\t}\n\t\t\t\treturn s_lowLevelHooksTimeout;\n\t\t\t}\n\t\t\tinternal set {\n\t\t\t\tint v = Math.Clamp(value, 0, 5000);\n\t\t\t\tMicrosoft.Win32.Registry.SetValue(@\"HKEY_CURRENT_USER\\Control Panel\\Desktop\", \"LowLevelHooksTimeout\", v);\n\t\t\t\ts_lowLevelHooksTimeout = v;\n\t\t\t}\n\t\t}\n\t\tstatic int s_lowLevelHooksTimeout;\n\n\t\tinternal static void OnException_(Exception e) {\n\t\t\tprint.warning(\"Unhandled exception in hook procedure. \" + e.ToString(), -1);\n\t\t}\n\n\t\t[StructLayout(LayoutKind.Sequential, Size = 32)] //note: this struct is in shared memory. Size must be same in all library versions.\n\t\tinternal struct SharedMemoryData_ {\n\t\t\tpublic long dontBlockModUntil, dontBlocLShiftCapsUntil;\n\t\t\tpublic int dontRestoreKeyboardHooks;\n\t\t\t//12 bytes reserved\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Let other hooks (in all processes) don't block modifier key up events for <i>timeMS</i> milliseconds. If 0 - restore.\n\t\t/// Used by mouse triggers waiting for mod keys released, to prevent inputblockers blocking mod up events, eg when sending keys/text.\n\t\t/// Returns the timeout time (<c>Environment.TickCount64 + timeMS</c>) or 0.\n\t\t/// </summary>\n\t\tinternal unsafe long DontBlockModInOtherHooks_(long timeMS) {\n\t\t\t_ignoreModExceptThisHook = timeMS > 0;\n\t\t\tvar r = _ignoreModExceptThisHook ? Environment.TickCount64 + timeMS : 0;\n\t\t\tSharedMemory_.Ptr->winHook.dontBlockModUntil = r;\n\t\t\treturn r;\n\t\t}\n\n\t\tunsafe bool _DontBlockMod => SharedMemory_.Ptr->winHook.dontBlockModUntil > Environment.TickCount64 && !_ignoreModExceptThisHook;\n\t\tbool _ignoreModExceptThisHook;\n\n\t\t/// <summary>\n\t\t/// Let all hooks (in all processes) ignore <c>LShift</c> and <c>CapsLock</c> for <i>timeMS</i> milliseconds. If 0 - restore.\n\t\t/// Returns the timeout time (<c>Environment.TickCount64 + timeMS</c>) or 0.\n\t\t/// Used when turning off <c>CapsLock</c> with <c>Shift</c>.\n\t\t/// </summary>\n\t\tinternal static unsafe long IgnoreLShiftCaps_(long timeMS) {\n\t\t\tvar r = timeMS > 0 ? Environment.TickCount64 + timeMS : 0;\n\t\t\tSharedMemory_.Ptr->winHook.dontBlocLShiftCapsUntil = r;\n\t\t\treturn r;\n\t\t}\n\n\t\tstatic unsafe bool _IgnoreLShiftCaps => SharedMemory_.Ptr->winHook.dontBlocLShiftCapsUntil > Environment.TickCount64;\n\t}\n}\n\nnamespace Au.Types {\n\t/// <summary>\n\t/// Contains types of hook data for hook procedures set by <see cref=\"WindowsHook\"/> and <see cref=\"WinEventHook\"/>.\n\t/// </summary>\n\tpublic static partial class HookData {\n\t\t/// <summary>\n\t\t/// Event data for the hook procedure set by <see cref=\"WindowsHook.Keyboard\"/>.\n\t\t/// More info: API <ms>LowLevelKeyboardProc</ms>.\n\t\t/// </summary>\n\t\tpublic unsafe struct Keyboard {\n\t\t\t/// <summary>The caller object of your hook procedure. For example can be used to unhook.</summary>\n\t\t\tpublic readonly WindowsHook hook;\n\n\t\t\treadonly Api.KBDLLHOOKSTRUCT* _x;\n\n\t\t\tinternal Keyboard(WindowsHook hook, nint lParam) {\n\t\t\t\tthis.hook = hook;\n\t\t\t\t_x = (Api.KBDLLHOOKSTRUCT*)lParam;\n\t\t\t}\n\n\t\t\t/// <summary>\n\t\t\t/// Call this function to steal this event from other hooks and apps.\n\t\t\t/// </summary>\n\t\t\tpublic void BlockEvent() => _x->BlockEvent = true;\n\n\t\t\t/// <summary>\n\t\t\t/// Is extended key.\n\t\t\t/// </summary>\n\t\t\tpublic bool IsExtended => 0 != (_x->flags & Api.LLKHF_EXTENDED);\n\n\t\t\t/// <summary>\n\t\t\t/// <c>true</c> if the event was generated by API such as <ms>SendInput</ms>.\n\t\t\t/// <c>false</c> if the event was generated by the keyboard.\n\t\t\t/// </summary>\n\t\t\tpublic bool IsInjected => 0 != (_x->flags & Api.LLKHF_INJECTED);\n\n\t\t\t/// <summary>\n\t\t\t/// <c>true</c> if the event was generated by functions of this library.\n\t\t\t/// </summary>\n\t\t\tpublic bool IsInjectedByAu => 0 != (_x->flags & Api.LLKHF_INJECTED) && _x->dwExtraInfo == Api.AuExtraInfo;\n\n\t\t\t/// <summary>\n\t\t\t/// Key <c>Alt</c> is pressed.\n\t\t\t/// </summary>\n\t\t\tpublic bool IsAlt => 0 != (_x->flags & Api.LLKHF_ALTDOWN);\n\n\t\t\t/// <summary>\n\t\t\t/// Is key-up event.\n\t\t\t/// </summary>\n\t\t\tpublic bool IsUp => 0 != (_x->flags & Api.LLKHF_UP);\n\n\t\t\t/// <summary>\n\t\t\t/// If the key is a modifier key (<c>Shift</c>, <c>Ctrl</c>, <c>Alt</c>, <c>Win</c>), returns the modifier flag. Else returns 0.\n\t\t\t/// </summary>\n\t\t\tpublic KMod Mod => keys.Internal_.KeyToMod((KKey)_x->vkCode);\n\n\t\t\t/// <summary>\n\t\t\t/// If <see cref=\"vkCode\"/> is a left or right modifier key code (<c>LShift</c>, <c>LCtrl</c>, <c>LAlt</c>, <c>RShift</c>, <c>RCtrl</c>, <c>RAlt</c>, <c>RWin</c>), returns the common modifier key code (<c>Shift</c>, <c>Ctrl</c>, <c>Alt</c>, <c>Win</c>). Else returns <see cref=\"vkCode\"/>.\n\t\t\t/// </summary>\n\t\t\tpublic KKey Key {\n\t\t\t\tget {\n\t\t\t\t\tvar vk = (KKey)_x->vkCode;\n\t\t\t\t\tswitch (vk) {\n\t\t\t\t\tcase KKey.LShift: case KKey.RShift: return KKey.Shift;\n\t\t\t\t\tcase KKey.LCtrl: case KKey.RCtrl: return KKey.Ctrl;\n\t\t\t\t\tcase KKey.LAlt: case KKey.RAlt: return KKey.Alt;\n\t\t\t\t\tcase KKey.RWin: return KKey.Win;\n\t\t\t\t\t}\n\t\t\t\t\treturn vk;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/// <summary>\n\t\t\t/// Returns <c>true</c> if <i>key</i> == <see cref=\"vkCode\"/> or <i>key</i> is <c>Shift</c>, <c>Ctrl</c>, <c>Alt</c> or <c>Win</c> and <see cref=\"vkCode\"/> is <c>LShift</c>/<c>RShift</c>, <c>LCtrl</c>/<c>RCtrl</c>, <c>LAlt</c>/<c>RAlt</c> or <c>RWin</c>.\n\t\t\t/// </summary>\n\t\t\tpublic bool IsKey(KKey key) {\n\t\t\t\tvar vk = (KKey)_x->vkCode;\n\t\t\t\tif (key == vk) return true;\n\t\t\t\tswitch (key) {\n\t\t\t\tcase KKey.Shift: return vk == KKey.LShift || vk == KKey.RShift;\n\t\t\t\tcase KKey.Ctrl: return vk == KKey.LCtrl || vk == KKey.RCtrl;\n\t\t\t\tcase KKey.Alt: return vk == KKey.LAlt || vk == KKey.RAlt;\n\t\t\t\tcase KKey.Win: return vk == KKey.RWin;\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t/// <summary>\n\t\t\t/// Converts flags to API <ms>SendInput</ms> flags <c>KEYEVENTF_KEYUP</c> and <c>KEYEVENTF_EXTENDEDKEY</c>.\n\t\t\t/// </summary>\n\t\t\tinternal byte SendInputFlags_ {\n\t\t\t\tget {\n\t\t\t\t\tuint f = 0;\n\t\t\t\t\tif (IsUp) f |= Api.KEYEVENTF_KEYUP;\n\t\t\t\t\tif (IsExtended) f |= Api.KEYEVENTF_EXTENDEDKEY;\n\t\t\t\t\treturn (byte)f;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t///\n\t\t\tpublic override string ToString() {\n\t\t\t\treturn $\"{vkCode.ToString()} {(IsUp ? \"up\" : \"\")}{(IsInjected ? \" (injected)\" : \"\")}\";\n\t\t\t}\n\n\t\t\t/// <summary>API <ms>KBDLLHOOKSTRUCT</ms></summary>\n\t\t\tpublic KKey vkCode => (KKey)_x->vkCode;\n\t\t\t/// <summary>API <ms>KBDLLHOOKSTRUCT</ms></summary>\n\t\t\tpublic uint scanCode => _x->scanCode;\n\t\t\t/// <summary>API <ms>KBDLLHOOKSTRUCT</ms></summary>\n\t\t\tpublic uint flags => _x->flags;\n\t\t\t/// <summary>API <ms>KBDLLHOOKSTRUCT</ms></summary>\n\t\t\tpublic int time => _x->time;\n\t\t\t/// <summary>API <ms>KBDLLHOOKSTRUCT</ms></summary>\n\t\t\tpublic nint dwExtraInfo => _x->dwExtraInfo;\n\n\t\t\tinternal Api.KBDLLHOOKSTRUCT* NativeStructPtr_ => _x;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Extra info value used by functions of this library that generate keyboard events. Low-level hooks receive it in <c>dwExtraInfo</c>.\n\t\t/// </summary>\n\t\tpublic const int AuExtraInfo = Api.AuExtraInfo;\n\n\t\t/// <summary>\n\t\t/// Hook data for the hook procedure set by <see cref=\"WindowsHook.Mouse\"/>.\n\t\t/// More info: API <ms>LowLevelMouseProc</ms>.\n\t\t/// </summary>\n\t\tpublic unsafe struct Mouse {\n\t\t\t/// <summary>The caller object of your hook procedure. For example can be used to unhook.</summary>\n\t\t\tpublic readonly WindowsHook hook;\n\n\t\t\treadonly Api.MSLLHOOKSTRUCT* _x;\n\t\t\treadonly MouseEvent _event;\n\n\t\t\tinternal Mouse(WindowsHook hook, nint wParam, nint lParam) {\n\t\t\t\tthis.hook = hook;\n\t\t\t\tvar p = (Api.MSLLHOOKSTRUCT*)lParam;\n\t\t\t\t_x = p;\n\t\t\t\tint e = (int)wParam;\n\t\t\t\tswitch (e) {\n\t\t\t\tcase Api.WM_MOUSEMOVE: IsMove = true; break;\n\t\t\t\tcase Api.WM_LBUTTONDOWN: case Api.WM_RBUTTONDOWN: case Api.WM_MBUTTONDOWN: IsButtonDown = true; break;\n\t\t\t\tcase Api.WM_LBUTTONUP: case Api.WM_RBUTTONUP: case Api.WM_MBUTTONUP: e--; IsButtonUp = true; break;\n\t\t\t\tcase Api.WM_XBUTTONUP: e--; IsButtonUp = true; goto g1;\n\t\t\t\tcase Api.WM_XBUTTONDOWN:\n\t\t\t\t\tIsButtonDown = true;\n\t\t\t\t\tg1:\n\t\t\t\t\tswitch (p->mouseData >> 16) { case 1: e |= 0x1000; break; case 2: e |= 0x2000; break; }\n\t\t\t\t\tbreak;\n\t\t\t\tcase Api.WM_MOUSEWHEEL:\n\t\t\t\tcase Api.WM_MOUSEHWHEEL:\n\t\t\t\t\tIsWheel = true;\n\t\t\t\t\tint wheel = (short)(p->mouseData >> 16);\n\t\t\t\t\tif (wheel > 0) e |= 0x1000; else if (wheel < 0) e |= 0x2000;\n\t\t\t\t\tWheelValue = wheel;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\t_event = (MouseEvent)e;\n\t\t\t}\n\n\t\t\t/// <summary>\n\t\t\t/// Call this function to steal this event from other hooks and apps.\n\t\t\t/// </summary>\n\t\t\tpublic void BlockEvent() => _x->BlockEvent = true;\n\n\t\t\t/// <summary>\n\t\t\t/// What event it is (button, move, wheel).\n\t\t\t/// </summary>\n\t\t\tpublic MouseEvent Event => _event;\n\n\t\t\t/// <summary>\n\t\t\t/// Is mouse-move event.\n\t\t\t/// </summary>\n\t\t\tpublic bool IsMove { get; }\n\n\t\t\t/// <summary>\n\t\t\t/// Is button-down event.\n\t\t\t/// </summary>\n\t\t\tpublic bool IsButtonDown { get; }\n\n\t\t\t/// <summary>\n\t\t\t/// Is button-up event.\n\t\t\t/// </summary>\n\t\t\tpublic bool IsButtonUp { get; }\n\n\t\t\t/// <summary>\n\t\t\t/// Is button event (down or up).\n\t\t\t/// </summary>\n\t\t\tpublic bool IsButton => IsButtonDown | IsButtonUp;\n\n\t\t\t/// <summary>\n\t\t\t/// Converts <see cref=\"Event\"/> to <see cref=\"MButton\"/>.\n\t\t\t/// </summary>\n\t\t\t/// <value><c>Left</c>, <c>Right</c>, <c>Middle</c>, <c>X1</c>, <c>X2</c> or 0. The down/up/double flags not used.</value>\n\t\t\tpublic MButton Button {\n\t\t\t\tget {\n\t\t\t\t\treturn _event switch {\n\t\t\t\t\t\tMouseEvent.LeftButton => MButton.Left,\n\t\t\t\t\t\tMouseEvent.RightButton => MButton.Right,\n\t\t\t\t\t\tMouseEvent.MiddleButton => MButton.Middle,\n\t\t\t\t\t\tMouseEvent.X1Button => MButton.X1,\n\t\t\t\t\t\tMouseEvent.X2Button => MButton.X2,\n\t\t\t\t\t\t_ => 0,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/// <summary>\n\t\t\t/// Is wheel event.\n\t\t\t/// </summary>\n\t\t\tpublic bool IsWheel { get; }\n\n\t\t\t/// <summary>\n\t\t\t/// <c>true</c> if the event was generated by API such as <ms>SendInput</ms>.\n\t\t\t/// <c>false</c> if the event was generated by the mouse.\n\t\t\t/// </summary>\n\t\t\tpublic bool IsInjected => 0 != (flags & Api.LLMHF_INJECTED);\n\n\t\t\t/// <summary>\n\t\t\t/// <c>true</c> if the event was generated by functions of this library.\n\t\t\t/// </summary>\n\t\t\tpublic bool IsInjectedByAu => IsInjected && dwExtraInfo == Api.AuExtraInfo;\n\n\t\t\t/// <summary>\n\t\t\t/// Wheel rotation amount, 120 for 1 full tick. Negative if backward.\n\t\t\t/// Usually 120 or -120, but some devices or software may produce smaller or bigger values.\n\t\t\t/// </summary>\n\t\t\tpublic int WheelValue { get; }\n\n\t\t\t///\n\t\t\tpublic override string ToString() {\n\t\t\t\tvar ud = \"\"; if (IsButtonDown) ud = \"down\"; else if (IsButtonUp) ud = \"up\";\n\t\t\t\treturn $\"{Event.ToString()} {ud} {pt.ToString()}{(IsInjected ? \" (injected)\" : \"\")}\";\n\t\t\t}\n\n\t\t\t/// <summary>API <ms>MSLLHOOKSTRUCT</ms></summary>\n\t\t\tpublic POINT pt => _x->pt;\n\t\t\t/// <summary>API <ms>MSLLHOOKSTRUCT</ms></summary>\n\t\t\tpublic uint mouseData => _x->mouseData;\n\t\t\t/// <summary>API <ms>MSLLHOOKSTRUCT</ms></summary>\n\t\t\tpublic uint flags => _x->flags;\n\t\t\t/// <summary>API <ms>MSLLHOOKSTRUCT</ms></summary>\n\t\t\tpublic int time => _x->time;\n\t\t\t/// <summary>API <ms>MSLLHOOKSTRUCT</ms></summary>\n\t\t\tpublic nint dwExtraInfo => _x->dwExtraInfo;\n\n\t\t\tinternal Api.MSLLHOOKSTRUCT* NativeStructPtr_ => _x;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Mouse hook event types. See <see cref=\"Mouse.Event\"/>.\n\t\t/// </summary>\n\t\tpublic enum MouseEvent {\n#pragma warning disable 1591 //no XML doc\n\t\t\tMove = 0x0200, //WM_MOUSEMOVE\n\t\t\tLeftButton = 0x0201, //WM_LBUTTONDOWN\n\t\t\tRightButton = 0x0204, //WM_RBUTTONDOWN\n\t\t\tMiddleButton = 0x0207, //WM_MBUTTONDOWN\n\t\t\tX1Button = 0x120B, //WM_XBUTTONDOWN | 0x1000\n\t\t\tX2Button = 0x220B, //WM_XBUTTONDOWN | 0x2000\n\t\t\tWheelForward = 0x120A, //WM_WHEEL | 0x1000\n\t\t\tWheelBackward = 0x220A, //WM_WHEEL | 0x2000\n\t\t\tWheelRight = 0x120E, //WM_HWHEEL | 0x1000\n\t\t\tWheelLeft = 0x220E, //WM_HWHEEL | 0x2000\n#pragma warning restore 1591\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Hook data for the hook procedure set by <see cref=\"WindowsHook.ThreadCbt\"/>.\n\t\t/// More info: API <ms>CBTProc</ms>.\n\t\t/// </summary>\n\t\tpublic struct ThreadCbt {\n\t\t\t/// <summary>The caller object of your hook procedure. For example can be used to unhook.</summary>\n\t\t\tpublic readonly WindowsHook hook;\n\n\t\t\t/// <summary>API <ms>CBTProc</ms></summary>\n\t\t\tpublic readonly CbtEvent code;\n\n\t\t\t/// <summary>API <ms>CBTProc</ms></summary>\n\t\t\tpublic readonly nint wParam;\n\n\t\t\t/// <summary>API <ms>CBTProc</ms></summary>\n\t\t\tpublic readonly nint lParam;\n\n\t\t\t/// <summary>Window handle.</summary>\n\t\t\tpublic wnd Hwnd => code switch { CbtEvent.ACTIVATE or CbtEvent.CREATEWND or CbtEvent.DESTROYWND or CbtEvent.MINMAX or CbtEvent.MOVESIZE or CbtEvent.SETFOCUS => (wnd)wParam, _ => default };\n\n\t\t\tinternal ThreadCbt(WindowsHook hook, int code, nint wParam, nint lParam) {\n\t\t\t\tthis.hook = hook;\n\t\t\t\tthis.code = (CbtEvent)code;\n\t\t\t\tthis.wParam = wParam;\n\t\t\t\tthis.lParam = lParam;\n\t\t\t}\n\n\t\t\t/// <summary>\n\t\t\t/// Gets <see cref=\"CbtEvent.ACTIVATE\"/> event info.\n\t\t\t/// </summary>\n\t\t\t/// <exception cref=\"InvalidOperationException\"><see cref=\"code\"/> is not <see cref=\"CbtEvent.ACTIVATE\"/>.</exception>\n\t\t\tpublic unsafe CBTACTIVATESTRUCT* ActivationInfo => code == CbtEvent.ACTIVATE ? (CBTACTIVATESTRUCT*)lParam : throw new InvalidOperationException();\n\n\t\t\t/// <summary>\n\t\t\t/// API <ms>CBTACTIVATESTRUCT</ms>.\n\t\t\t/// </summary>\n\t\t\tpublic struct CBTACTIVATESTRUCT {\n\t\t\t\t///\n\t\t\t\tpublic bool fMouse;\n\t\t\t\t///\n\t\t\t\tpublic wnd hWndActive;\n\t\t\t}\n\n\t\t\t/// <summary>\n\t\t\t/// Gets <see cref=\"CbtEvent.CREATEWND\"/> event info.\n\t\t\t/// You can modify <c>x</c>, <c>y</c>, <c>cx</c>, <c>cy</c>, and <c>hwndInsertAfter</c>.\n\t\t\t/// </summary>\n\t\t\t/// <exception cref=\"InvalidOperationException\"><see cref=\"code\"/> is not <see cref=\"CbtEvent.CREATEWND\"/>.</exception>\n\t\t\tpublic unsafe CBT_CREATEWND* CreationInfo => code == CbtEvent.CREATEWND ? (CBT_CREATEWND*)lParam : throw new InvalidOperationException();\n\n\t\t\t/// <summary>\n\t\t\t/// API <ms>CBT_CREATEWND</ms>.\n\t\t\t/// </summary>\n\t\t\tpublic unsafe struct CBT_CREATEWND {\n\t\t\t\t///\n\t\t\t\tpublic CREATESTRUCT* lpcs;\n\t\t\t\t///\n\t\t\t\tpublic wnd hwndInsertAfter;\n\t\t\t}\n\n\t\t\t//rejected. Rarely used or too simple.\n\t\t\t///// <summary>\n\t\t\t///// Gets <see cref=\"CbtEvent.CLICKSKIPPED\"/> event info. Returns the mouse message.\n\t\t\t///// </summary>\n\t\t\t///// <param name=\"m\"><ms>MOUSEHOOKSTRUCT</ms>.</param>\n\t\t\t///// <exception cref=\"InvalidOperationException\"><see cref=\"code\"/> is not <c>CbtEvent.CLICKSKIPPED</c>.</exception>\n\t\t\t//public unsafe uint MouseInfo(out MOUSEHOOKSTRUCT* m) {\n\t\t\t//\tif (code != CbtEvent.CLICKSKIPPED) throw new InvalidOperationException();\n\t\t\t//\tm = (MOUSEHOOKSTRUCT*)lParam;\n\t\t\t//\treturn (uint)wParam;\n\t\t\t//}\n\n\t\t\t///// <summary>\n\t\t\t///// Gets <see cref=\"CbtEvent.KEYSKIPPED\"/> event info. Returns the key code.\n\t\t\t///// </summary>\n\t\t\t///// <param name=\"lParam\"><i>lParam</i> of the key message. Specifies the repeat count, scan code, etc. See API <ms>WM_KEYDOWN</ms>.</param>\n\t\t\t///// <exception cref=\"InvalidOperationException\"><see cref=\"code\"/> is not <c>CbtEvent.KEYSKIPPED</c>.</exception>\n\t\t\t//public KKey KeyInfo(out uint lParam) {\n\t\t\t//\tif (code != CbtEvent.KEYSKIPPED) throw new InvalidOperationException();\n\t\t\t//\tlParam = (uint)this.lParam;\n\t\t\t//\treturn (KKey)(uint)wParam;\n\t\t\t//}\n\n\t\t\t///// <summary>\n\t\t\t///// Gets <see cref=\"CbtEvent.SETFOCUS\"/> event info. Returns the window handle.\n\t\t\t///// </summary>\n\t\t\t///// <param name=\"wLostFocus\">The previously focused window, or <c>default(wnd)</c>.</param>\n\t\t\t///// <exception cref=\"InvalidOperationException\"><see cref=\"code\"/> is not <c>CbtEvent.SETFOCUS</c>.</exception>\n\t\t\t//public wnd FocusInfo(out wnd wLostFocus) {\n\t\t\t//\tif (code != CbtEvent.SETFOCUS) throw new InvalidOperationException();\n\t\t\t//\twLostFocus = (wnd)lParam;\n\t\t\t//\treturn (wnd)wParam;\n\t\t\t//}\n\n\t\t\t///// <summary>\n\t\t\t///// Gets <see cref=\"CbtEvent.MOVESIZE\"/> event info.\n\t\t\t///// </summary>\n\t\t\t///// <exception cref=\"InvalidOperationException\"><see cref=\"code\"/> is not <c>CbtEvent.MOVESIZE</c>.</exception>\n\t\t\t//public unsafe RECT* MoveSizeInfo => code == CbtEvent.MOVESIZE ? (RECT*)lParam : throw new InvalidOperationException();\n\n\t\t\t///// <summary>\n\t\t\t///// Gets <see cref=\"CbtEvent.MINMAX\"/> event info.\n\t\t\t///// Returns the new show state. See API <ms>ShowWindow</ms>. Minimized 6, maximized 3, restored 9.\n\t\t\t///// </summary>\n\t\t\t///// <exception cref=\"InvalidOperationException\"><see cref=\"code\"/> is not <c>CbtEvent.MINMAX</c>.</exception>\n\t\t\t//public int MinMaxInfo => code == CbtEvent.MINMAX ? (int)lParam & 0xffff : throw new InvalidOperationException();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// CBT hook event types. Used with <see cref=\"ThreadCbt\"/>.\n\t\t/// More info: API <ms>CBTProc</ms>.\n\t\t/// </summary>\n\t\tpublic enum CbtEvent {\n#pragma warning disable 1591 //no XML doc\n\t\t\tMOVESIZE = 0,\n\t\t\tMINMAX = 1,\n\t\t\t//QS = 2,\n\t\t\tCREATEWND = 3,\n\t\t\tDESTROYWND = 4,\n\t\t\tACTIVATE = 5,\n\t\t\tCLICKSKIPPED = 6,\n\t\t\tKEYSKIPPED = 7,\n\t\t\tSYSCOMMAND = 8,\n\t\t\tSETFOCUS = 9,\n#pragma warning restore 1591\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Hook data for the hook procedure set by <see cref=\"WindowsHook.ThreadGetMessage\"/>.\n\t\t/// More info: API <ms>GetMsgProc</ms>.\n\t\t/// </summary>\n\t\tpublic unsafe struct ThreadGetMessage {\n\t\t\t/// <summary>The caller object of your hook procedure. For example can be used to unhook.</summary>\n\t\t\tpublic readonly WindowsHook hook;\n\n\t\t\t/// <summary>\n\t\t\t/// The message has not been removed from the queue, because called API <ms>PeekMessage</ms> with flag <c>PM_NOREMOVE</c>.\n\t\t\t/// </summary>\n\t\t\tpublic readonly bool PM_NOREMOVE;\n\n\t\t\t/// <summary>\n\t\t\t/// Message parameters.\n\t\t\t/// API <ms>MSG</ms>.\n\t\t\t/// </summary>\n\t\t\tpublic readonly MSG* msg;\n\n\t\t\tinternal ThreadGetMessage(WindowsHook hook, nint wParam, nint lParam) {\n\t\t\t\tthis.hook = hook;\n\t\t\t\tPM_NOREMOVE = (uint)wParam == Api.PM_NOREMOVE;\n\t\t\t\tmsg = (MSG*)lParam;\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Hook data for the hook procedure set by <see cref=\"WindowsHook.ThreadKeyboard\"/>.\n\t\t/// More info: API <ms>KeyboardProc</ms>.\n\t\t/// </summary>\n\t\tpublic struct ThreadKeyboard {\n\t\t\t/// <summary>The caller object of your hook procedure. For example can be used to unhook.</summary>\n\t\t\tpublic readonly WindowsHook hook;\n\n\t\t\t/// <summary>\n\t\t\t/// The message has not been removed from the queue, because called API <ms>PeekMessage</ms> with flag <c>PM_NOREMOVE</c>.\n\t\t\t/// </summary>\n\t\t\tpublic readonly bool PM_NOREMOVE;\n\n\t\t\t/// <summary>\n\t\t\t/// The key code.\n\t\t\t/// </summary>\n\t\t\tpublic readonly KKey key;\n\n\t\t\t/// <summary>\n\t\t\t/// <i>lParam</i> of the key message. Specifies the key state, scan code, etc. See API <ms>KeyboardProc</ms>.\n\t\t\t/// </summary>\n\t\t\tpublic readonly uint lParam;\n\n\t\t\t/// <summary>\n\t\t\t/// Is key-up event.\n\t\t\t/// </summary>\n\t\t\tpublic bool IsUp => 0 != (lParam & 0x80000000);\n\n\t\t\tinternal ThreadKeyboard(WindowsHook hook, int code, nint wParam, nint lParam) {\n\t\t\t\tthis.hook = hook;\n\t\t\t\tPM_NOREMOVE = code == Api.HC_NOREMOVE;\n\t\t\t\tkey = (KKey)(uint)wParam;\n\t\t\t\tthis.lParam = (uint)lParam;\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Hook data for the hook procedure set by <see cref=\"WindowsHook.ThreadMouse\"/>.\n\t\t/// More info: API <ms>MouseProc</ms>.\n\t\t/// </summary>\n\t\tpublic unsafe struct ThreadMouse {\n\t\t\t/// <summary>The caller object of your hook procedure. For example can be used to unhook.</summary>\n\t\t\tpublic readonly WindowsHook hook;\n\n\t\t\t/// <summary>\n\t\t\t/// The message has not been removed from the queue, because called API <ms>PeekMessage</ms> with flag <c>PM_NOREMOVE</c>.\n\t\t\t/// </summary>\n\t\t\tpublic readonly bool PM_NOREMOVE;\n\n\t\t\t/// <summary>\n\t\t\t/// The mouse message, for example <ms>WM_MOUSEMOVE</ms>.\n\t\t\t/// </summary>\n\t\t\tpublic readonly uint message;\n\n\t\t\t/// <summary>\n\t\t\t/// More info about the mouse message.\n\t\t\t/// API <ms>MOUSEHOOKSTRUCT</ms>.\n\t\t\t/// </summary>\n\t\t\tpublic readonly MOUSEHOOKSTRUCT* m;\n\n\t\t\tinternal ThreadMouse(WindowsHook hook, int code, nint wParam, nint lParam) {\n\t\t\t\tthis.hook = hook;\n\t\t\t\tPM_NOREMOVE = code == Api.HC_NOREMOVE;\n\t\t\t\tmessage = (uint)wParam;\n\t\t\t\tm = (MOUSEHOOKSTRUCT*)lParam;\n\t\t\t}\n\t\t}\n\n#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member\n\t\t/// <summary>API <ms>MOUSEHOOKSTRUCT</ms></summary>\n\t\tpublic struct MOUSEHOOKSTRUCT {\n\t\t\tpublic POINT pt;\n\t\t\tpublic wnd hwnd;\n\t\t\tpublic int wHitTestCode;\n\t\t\tpublic nint dwExtraInfo;\n\t\t}\n\n\t\t/// <summary>API <ms>CWPSTRUCT</ms></summary>\n\t\tpublic struct CWPSTRUCT {\n\t\t\tpublic nint lParam;\n\t\t\tpublic nint wParam;\n\t\t\tpublic int message;\n\t\t\tpublic wnd hwnd;\n\t\t}\n\n\t\t/// <summary>API <ms>CWPRETSTRUCT</ms></summary>\n\t\tpublic struct CWPRETSTRUCT {\n\t\t\tpublic nint lResult;\n\t\t\tpublic nint lParam;\n\t\t\tpublic nint wParam;\n\t\t\tpublic int message;\n\t\t\tpublic wnd hwnd;\n\t\t}\n#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member\n\n\t\t/// <summary>\n\t\t/// Hook data for the hook procedure set by <see cref=\"WindowsHook.ThreadCallWndProc\"/>.\n\t\t/// More info: API <ms>CallWndProc</ms>.\n\t\t/// </summary>\n\t\tpublic unsafe struct ThreadCallWndProc {\n\t\t\t/// <summary>The caller object of your hook procedure. For example can be used to unhook.</summary>\n\t\t\tpublic readonly WindowsHook hook;\n\n\t\t\t/// <summary>\n\t\t\t/// True if the message was sent by another thread.\n\t\t\t/// </summary>\n\t\t\tpublic readonly bool sentByOtherThread; //note: incorrect info in MSDN\n\n\t\t\t/// <summary>\n\t\t\t/// Message parameters.\n\t\t\t/// API <ms>CWPSTRUCT</ms>.\n\t\t\t/// </summary>\n\t\t\tpublic readonly CWPSTRUCT* msg;\n\n\t\t\tinternal ThreadCallWndProc(WindowsHook hook, nint wParam, nint lParam) {\n\t\t\t\tthis.hook = hook;\n\t\t\t\tsentByOtherThread = 0 != wParam;\n\t\t\t\tmsg = (CWPSTRUCT*)lParam;\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Hook data for the hook procedure set by <see cref=\"WindowsHook.ThreadCallWndProcRet\"/>.\n\t\t/// More info: API <ms>CallWndRetProc</ms>.\n\t\t/// </summary>\n\t\tpublic unsafe struct ThreadCallWndProcRet {\n\t\t\t/// <summary>The caller object of your hook procedure. For example can be used to unhook.</summary>\n\t\t\tpublic readonly WindowsHook hook;\n\n\t\t\t/// <summary>\n\t\t\t/// True if the message was sent by another thread.\n\t\t\t/// </summary>\n\t\t\tpublic readonly bool sentByOtherThread; //note: incorrect info in MSDN\n\n\t\t\t/// <summary>\n\t\t\t/// Message parameters and the return value.\n\t\t\t/// API <ms>CWPRETSTRUCT</ms>.\n\t\t\t/// </summary>\n\t\t\tpublic readonly CWPRETSTRUCT* msg;\n\n\t\t\tinternal ThreadCallWndProcRet(WindowsHook hook, nint wParam, nint lParam) {\n\t\t\t\tthis.hook = hook;\n\t\t\t\tsentByOtherThread = 0 != wParam;\n\t\t\t\tmsg = (CWPRETSTRUCT*)lParam;\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Calls API API <ms>ReplyMessage</ms>, which allows to use <see cref=\"elm\"/> and COM in the hook procedure.\n\t\t/// </summary>\n\t\t/// <param name=\"cancelEvent\">\n\t\t/// Don't notify the target window about the event, and don't call other hook procedures.\n\t\t/// This value is used instead of the return value of the hook procedure, which is ignored.\n\t\t/// </param>\n\t\t/// <remarks>\n\t\t/// It can be used as a workaround for this problem: in low-level hook procedure some functions don't work with some windows. For example cannot get a UI element or use a COM object. Error/exception \"An outgoing call cannot be made since the application is dispatching an input-synchronous call (0x8001010D)\".\n\t\t/// </remarks>\n\t\tpublic static void ReplyMessage(bool cancelEvent) => Api.ReplyMessage(cancelEvent ? 1 : 0);\n\t}\n}\n"
  },
  {
    "path": "Au/Au.More/WinformsControlNames.cs",
    "content": "namespace Au.More;\n\n/// <summary>\n/// Gets programming names of .NET Windows Forms controls.\n/// </summary>\n/// <remarks>\n/// Usually each control has a unique name. It's the <see cref=\"System.Windows.Forms.Control.Name\"/> property. Useful to identify controls without a classic name/text.\n/// The control id of these controls is not useful, it is not constant.\n/// </remarks>\npublic sealed class WinformsControlNames : IDisposable {\n\tProcessMemory _pm;\n\twnd _w;\n\t\n\t///\n\tpublic void Dispose() {\n\t\tif (_pm != null) { _pm.Dispose(); _pm = null; }\n\t\tGC.SuppressFinalize(this);\n\t}\n\t\n\tstatic readonly int WM_GETCONTROLNAME = Api.RegisterWindowMessage(\"WM_GETCONTROLNAME\");\n\t\n\t/// <summary>\n\t/// Prepares to get control names.\n\t/// </summary>\n\t/// <param name=\"w\">Any top-level or child window of that process.</param>\n\t/// <exception cref=\"AuWndException\"><i>w</i> invalid.</exception>\n\t/// <exception cref=\"AuException\">Failed to allocate process memory (see <see cref=\"ProcessMemory\"/>) needed to get control names, usually because of [](xref:uac).</exception>\n\tpublic WinformsControlNames(wnd w) {\n\t\t_pm = new ProcessMemory(w, 4096); //throws\n\t\t_w = w;\n\t}\n\t\n\t/// <summary>\n\t/// Gets control name.\n\t/// </summary>\n\t/// <returns><c>null</c> if failed or the name is empty.</returns>\n\t/// <param name=\"c\">The control. Can be a top-level window too. Must be of the same process as the window specified in the constructor.</param>\n\tpublic string GetControlName(wnd c) {\n\t\tif (_pm == null) return null;\n\t\tif (!IsWinformsControl(c)) return null;\n\t\tif (!c.SendTimeout(5000, out var R, WM_GETCONTROLNAME, 4096, _pm.Mem) || (int)R < 1) return null;\n\t\tint len = (int)R - 1;\n\t\tif (len == 0) return \"\";\n\t\treturn _pm.ReadCharString(len);\n\t}\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if window class name starts with <c>\"WindowsForms\"</c>.\n\t/// Usually it means that we can get Windows Forms control name of <i>w</i> and its child controls.\n\t/// </summary>\n\t/// <param name=\"w\">The window. Can be top-level or control.</param>\n\tpublic static bool IsWinformsControl(wnd w) {\n\t\treturn w.ClassNameIs(\"WindowsForms*\");\n\t}\n\t\n\t/// <summary>\n\t/// Gets the programming name of a Windows Forms control.\n\t/// </summary>\n\t/// <returns><c>null</c> if it is not a Windows Forms control or if failed.</returns>\n\t/// <param name=\"c\">The control. Can be top-level window too.</param>\n\t/// <remarks>\n\t/// This function is easy to use and does not throw exceptions. However, when you need names of multiple controls of a single window, better create a <see cref=\"WinformsControlNames\"/> instance (once) and for each control call its <c>GetControlName</c> method, it will be faster.</remarks>\n\tpublic static string GetSingleControlName(wnd c) {\n\t\tif (!IsWinformsControl(c)) return null;\n\t\ttry {\n\t\t\tusing (var x = new WinformsControlNames(c)) return x.GetControlName(c);\n\t\t}\n\t\tcatch { }\n\t\treturn null;\n\t}\n\t\n\t//Don't use this cached version, it does not make significantly faster. Also, keeping process handle in such a way is not good, would need to use other thread to close it after some time.\n\t///// <summary>\n\t///// Gets programming name of a Windows Forms control.\n\t///// Returns <c>null</c> if it is not a Windows Forms control or if failed.\n\t///// </summary>\n\t///// <param name=\"c\">The control. Can be top-level window too.</param>\n\t///// <remarks>When need to get control names repeatedly or quite often, this function can be faster than creating <see cref=\"WinformsControlNames\"/> instance each time and calling its <c>GetControlName</c> method, because this function remembers the last used process etc. Also it is easier to use and does not throw exceptions.</remarks>\n\t//public static string GetSingleControlName(wnd c)\n\t//{\n\t//\tif(!IsWinformsControl(c)) return null;\n\t//\tuint pid = c.ProcessId; if(pid == 0) return null;\n\t//\tlock (_prevLock) {\n\t//\t\tif(pid != _prevPID || perf.ms - _prevTime > 1000) {\n\t//\t\t\tif(_prev != null) { _prev.Dispose(); _prev = null; }\n\t//\t\t\ttry { _prev = new WinformsControlNames(c); } catch { }\n\t//\t\t\t//print.it(\"new\");\n\t//\t\t} //else print.it(\"cached\");\n\t//\t\t_prevPID = pid; _prevTime = perf.ms;\n\t//\t\tif(_prev == null) return null;\n\t//\t\treturn _prev.GetControlName(c);\n\t//\t}\n\t//}\n\t//static WinformsControlNames _prev; static uint _prevPID; static long _prevTime; static object _prevLock = new object(); //cache\n}\n"
  },
  {
    "path": "Au/Au.Types/ColorInt.cs",
    "content": "using System.Drawing;\nusing System.Text.Json.Serialization;\n\nnamespace Au.Types;\n\n//rejected: /// <completionlist cref=\"Color\"/>\n\n/// <summary>\n/// Color, as <c>int</c> in <c>0xAARRGGBB</c> format.\n/// Can convert from/to <see cref=\"Color\"/>, <see cref=\"System.Windows.Media.Color\"/>, <c>int</c> (<c>0xAARRGGBB</c>), Windows <c>COLORREF</c> (<c>0xBBGGRR</c>), string.\n/// </summary>\npublic record struct ColorInt {\n\t/// <summary>\n\t/// Color value in <c>0xAARRGGBB</c> format.\n\t/// </summary>\n\t[JsonInclude]\n\tpublic int argb;\n\t\n\t/// <param name=\"colorARGB\">Color value in <c>0xAARRGGBB</c> or <c>0xRRGGBB</c> format.</param>\n\t/// <param name=\"makeOpaque\">Set alpha = 0xFF. If <c>null</c> (default), sets alpha = 0xFF if it is 0 in <i>colorBGR</i>.</param>\n\tpublic ColorInt(int colorARGB, bool? makeOpaque = null) {\n\t\tif (makeOpaque == true || (makeOpaque == null && (colorARGB & ~0xFFFFFF) == 0)) colorARGB |= 0xFF << 24;\n\t\targb = colorARGB;\n\t}\n\t\n\t/// <param name=\"colorARGB\">Color value in <c>0xAARRGGBB</c> or <c>0xRRGGBB</c> format.</param>\n\t/// <param name=\"makeOpaque\">Set alpha = 0xFF. If <c>null</c> (default), sets alpha = 0xFF if it is 0 in <i>colorBGR</i>.</param>\n\tpublic ColorInt(uint colorARGB, bool? makeOpaque) : this((int)colorARGB, makeOpaque) { }\n\t\n\t/// <summary>\n\t/// Converts from an <c>int</c> color value in <c>0xRRGGBB</c> or <c>0xAARRGGBB</c> format.\n\t/// Sets alpha = 0xFF if it is 0 in <i>color</i>.\n\t/// </summary>\n\t//[Obsolete] //to find all references\n\tpublic static implicit operator ColorInt(int color) => new(color);\n\t\n\t/// <summary>\n\t/// Converts from an <c>uint</c> color value in <c>0xRRGGBB</c> or <c>0xAARRGGBB</c> format.\n\t/// Sets alpha = 0xFF if it is 0 in <i>color</i>.\n\t/// </summary>\n\t//[Obsolete] //to find all references\n\tpublic static implicit operator ColorInt(uint color) => new((int)color);\n\t\n\t/// <summary>\n\t/// Converts from <see cref=\"Color\"/>.\n\t/// </summary>\n\tpublic static implicit operator ColorInt(Color color) => new(color.ToArgb(), false);\n\t\n\t/// <summary>\n\t/// Converts from <see cref=\"System.Windows.Media.Color\"/>.\n\t/// </summary>\n\tpublic static implicit operator ColorInt(System.Windows.Media.Color color)\n\t\t=> new((color.A << 24) | (color.R << 16) | (color.G << 8) | color.B, false);\n\t\n\t/// <summary>\n\t/// Converts from a color name (<see cref=\"Color.FromName(string)\"/>) or string <c>\"0xRRGGBB\"</c> or <c>\"#RRGGBB\"</c>.\n\t/// </summary>\n\t/// <remarks>\n\t/// If <i>s</i> is a hex number that contains 6 or less hex digits, makes opaque (alpha 0xFF).\n\t/// If <i>s</i> is <c>null</c> or invalid, sets <c>c.argb = 0</c> and returns <c>false</c>.\n\t/// </remarks>\n\tpublic static bool FromString(string s, out ColorInt c) {\n\t\tc.argb = 0;\n\t\tif (s == null || s.Length < 2) return false;\n\t\tif (s[0] == '0' && s[1] == 'x') {\n\t\t\tc.argb = s.ToInt(0, out int end);\n\t\t\tif (end < 3) return false;\n\t\t\tif (end <= 8) c.argb |= unchecked((int)0xFF000000);\n\t\t} else if (s[0] == '#') {\n\t\t\tc.argb = s.ToInt(1, out int end, STIFlags.IsHexWithout0x);\n\t\t\tif (end < 2) return false;\n\t\t\tif (end <= 7) c.argb |= unchecked((int)0xFF000000);\n\t\t} else {\n\t\t\tc.argb = Color.FromName(s).ToArgb();\n\t\t\tif (c.argb == 0) return false; //invalid is 0, black is 0xFF000000\n\t\t}\n\t\treturn true;\n\t}\n\t\n\t/// <summary>\n\t/// Converts from Windows native <c>COLORREF</c> (<c>0xBBGGRR</c> to <c>0xAARRGGBB</c>).\n\t/// </summary>\n\t/// <param name=\"colorBGR\">Color in <c>0xBBGGRR</c> format.</param>\n\t/// <param name=\"makeOpaque\">Set alpha = 0xFF. If <c>null</c> (default), sets alpha = 0xFF if it is 0 in <i>colorBGR</i>.</param>\n\tpublic static ColorInt FromBGR(int colorBGR, bool? makeOpaque = null) => new(SwapRB(colorBGR), makeOpaque);\n\t\n\t/// <summary>\n\t/// Converts to Windows native <c>COLORREF</c> (<c>0xBBGGRR</c> from <c>0xAARRGGBB</c>).\n\t/// </summary>\n\t/// <returns>color in <c>COLORREF</c> format. Does not modify this variable.</returns>\n\t/// <param name=\"zeroAlpha\">Set the alpha byte = 0.</param>\n\tpublic int ToBGR(bool zeroAlpha = true) {\n\t\tvar r = SwapRB(argb);\n\t\tif (zeroAlpha) r &= 0xFFFFFF;\n\t\treturn r;\n\t}\n\t\n\t//rejected. Easy to create bugs when actually need BGR. Let use ToBGR() when need BGR, or argb field when need ARGB.\n\t///// <summary>Returns <c>c.argb</c>.</summary>\n\t//public static explicit operator int(ColorInt c) => c.argb;\n\t\n\t///// <summary>Returns <c>(uint)c.argb</c>.</summary>\n\t//public static explicit operator uint(ColorInt c) => (uint)c.argb;\n\t\n\t/// <summary>Converts to <see cref=\"Color\"/>.</summary>\n\tpublic static explicit operator Color(ColorInt c) => Color.FromArgb(c.argb);\n\t\n\t/// <summary>Converts to <see cref=\"System.Windows.Media.Color\"/>.</summary>\n\tpublic static explicit operator System.Windows.Media.Color(ColorInt c) {\n\t\tuint k = (uint)c.argb;\n\t\treturn System.Windows.Media.Color.FromArgb((byte)(k >> 24), (byte)(k >> 16), (byte)(k >> 8), (byte)k);\n\t}\n\t\n\tinternal static System.Windows.Media.Color WpfColor_(int rgb)\n\t\t=> System.Windows.Media.Color.FromRgb((byte)(rgb >> 16), (byte)(rgb >> 8), (byte)rgb);\n\t\n\tinternal static System.Windows.Media.SolidColorBrush WpfBrush_(int rgb)\n\t\t=> new(WpfColor_(rgb));\n\t\n\t///// <summary>\n\t///// <c>FromBGR(GetSysColor)</c>.\n\t///// </summary>\n\t//internal static ColorInt FromSysColor_(int colorIndex) => FromBGR(Api.GetSysColor(colorIndex), true);\n\t\n\t///\n\tpublic override string ToString() => \"#\" + argb.ToString(\"X8\");\n\t\n\t/// <summary>\n\t/// Converts color from ARGB (<c>0xAARRGGBB</c>) to ABGR (<c>0xAABBGGRR</c>) or vice versa (swaps the red and blue bytes).\n\t/// ARGB is used in .NET, GDI+ and HTML/CSS.\n\t/// ABGR is used by most Windows API; aka <c>COLORREF</c>.\n\t/// </summary>\n\tpublic static int SwapRB(int color) => (color & unchecked((int)0xff00ff00)) | (color << 16 & 0xff0000) | (color >> 16 & 0xff);\n\t\n\t/// <inheritdoc cref=\"SwapRB(int)\"/>\n\tpublic static uint SwapRB(uint color) => (color & 0xff00ff00) | (color << 16 & 0xff0000) | (color >> 16 & 0xff);\n\t\n\t//rejected. Unclear usage. Instead let users call ToHLS, change L how they want, and call FromHLS.\n\t///// <summary>\n\t///// Changes color's luminance (makes darker or brighter).\n\t///// Returns new color. Does not modify this variable.\n\t///// </summary>\n\t///// <param name=\"n\">The luminance in units of 0.1 percent of the range (which depends on <i>totalRange</i>). Can be from -1000 to 1000.</param>\n\t///// <param name=\"totalRange\">If <c>true</c>, <i>n</i> is in whole luminance range (from minimal to maximal possible). If <c>false</c>, <i>n</i> is in the range from current luminance of the color to the maximal (if n positive) or minimal (if n negative) luminance.</param>\n\t///// <remarks>\n\t///// Calls API <ms>ColorAdjustLuma</ms>.\n\t///// Does not change hue and saturation. Does not use alpha.\n\t///// </remarks>\n\t//internal ColorInt AdjustLuminance(int n, bool totalRange = false) {\n\t//\tuint u = (uint)argb;\n\t//\tu = Api.ColorAdjustLuma(u & 0xffffff, n, !totalRange) | (u & 0xFF000000);\n\t//\treturn new((int)u, false);\n\t//\t//tested: with SwapRB the same.\n\t//}\n\t\n\t/// <summary>\n\t/// Converts from hue-luminance-saturation (HLS).\n\t/// </summary>\n\t/// <param name=\"H\">Hue, 0 to 240.</param>\n\t/// <param name=\"L\">Luminance, 0 to 240.</param>\n\t/// <param name=\"S\">Saturation, 0 to 240.</param>\n\t/// <param name=\"bgr\">Return color in <c>0xBBGGRR</c> format. If <c>false</c>, <c>0xRRGGBB</c>.</param>\n\t/// <returns>Color in <c>0xRRGGBB</c> or <c>0xBBGGRR</c> format, depending on <i>bgr</i>. Alpha 0.</returns>\n\tpublic static int FromHLS(int H, int L, int S, bool bgr) {\n\t\tif (S == 0) { //ColorHLSToRGB bug: returns 0 if S 0\n\t\t\tint i = L * 255 / 240;\n\t\t\treturn i | (i << 8) | (i << 16);\n\t\t}\n\t\tint color = Api.ColorHLSToRGB((ushort)H, (ushort)L, (ushort)S);\n\t\tif (!bgr) color = SwapRB(color);\n\t\treturn color;\n\t}\n\t\n\t/// <summary>\n\t/// Converts to hue-luminance-saturation (HLS).\n\t/// </summary>\n\t/// <param name=\"color\">Color in <c>0xRRGGBB</c> or <c>0xBBGGRR</c> format, depending on <i>bgr</i>. Ignores alpha.</param>\n\t/// <param name=\"bgr\"><i>color</i> is in <c>0xBBGGRR</c> format. If <c>false</c>, <c>0xRRGGBB</c>.</param>\n\t/// <returns>Hue, luminance and saturation. All 0 to 240.</returns>\n\tpublic static (int H, int L, int S) ToHLS(int color, bool bgr) {\n\t\tif (!bgr) color = SwapRB(color);\n\t\tApi.ColorRGBToHLS(color, out var H, out var L, out var S);\n\t\treturn (H, L, S);\n\t}\n\t\n\t/// <summary>\n\t/// Calculates color's perceived brightness.\n\t/// </summary>\n\t/// <returns>0 to 1.</returns>\n\t/// <param name=\"color\">Color in <c>0xRRGGBB</c> or <c>0xBBGGRR</c> format, depending on <i>bgr</i>. Ignores alpha.</param>\n\t/// <param name=\"bgr\"><i>color</i> is in <c>0xBBGGRR</c> format. If <c>false</c>, <c>0xRRGGBB</c>.</param>\n\t/// <remarks>\n\t/// Unlike <see cref=\"ToHLS\"/> and <see cref=\"Color.GetBrightness\"/>, this function uses different weights for red, green and blue components.\n\t/// Ignores alpha.\n\t/// </remarks>\n\tpublic static float GetPerceivedBrightness(int color, bool bgr) {\n\t\tuint u = (uint)color;\n\t\tif (bgr) u = SwapRB(u);\n\t\tuint R = u >> 16 & 0xff, G = u >> 8 & 0xff, B = u & 0xff;\n\t\treturn (float)(Math.Sqrt(R * R * .299 + G * G * .587 + B * B * .114) / 255);\n\t}\n\t\n\t//same result as ColorAdjustLuma. Probably slower.\n\t//internal static int SetLuminance(int color, bool bgr, double percent, bool totalRange) {\n\t//\tvar (H, L, S) = ToHLS(color, bgr);\n\t//\tL = (int)Math2.PercentToValue(totalRange ? 240 : L, percent);\n\t//\treturn (int)((uint)FromHLS(H, Math.Clamp(L, 0, 240), S, bgr) | (color & 0xFF000000));\n\t//}\n}\n"
  },
  {
    "path": "Au/Au.Types/JSettings.cs",
    "content": "using System.Text.Json;\nusing System.Text.Json.Serialization;\n\nnamespace Au.Types;\n\n/// <summary>\n/// Base of record classes that contain various settings as public fields or properties. Loads and lazily auto-saves from/to a JSON file.\n/// </summary>\n/// <remarks>\n/// All functions are thread-safe.\n/// </remarks>\n/// <example>\n/// <code><![CDATA[\n/// MySettings sett = MySettings.Load(); //in a class you would use a static field or property, but this example uses a local variable for simplicity\n/// \n/// print.it(sett.i);\n/// sett.i++;\n/// \n/// if (dialog.showInput(out string s, \"example\", editText: sett.s)) {\n/// \tsett.s = s;\n/// \t\n/// \tprint.it(\"old array:\", sett.a);\n/// \tif ((!sett.a.Contains(s))) sett.a = sett.a.InsertAt(-1, s);\n/// \t\n/// \tprint.it(\"old dictionary:\", sett.d);\n/// \tsett.d[s] = DateTime.Now;\n/// }\n/// \n/// \n/// record class MySettings : JSettings {\n/// \tpublic static readonly string File = folders.ThisAppDocuments + @\"MySettings.json\";\n/// \n/// \tpublic static MySettings Load() => Load<MySettings>(File);\n/// \t\n/// \t// examples of settings\n/// \tpublic int i;\n/// \tpublic string s = \"default\";\n/// \tpublic string[] a = [];\n/// \tpublic Dictionary<string, DateTime> d = new();\n/// }\n/// ]]></code>\n/// </example>\npublic abstract record class JSettings : IDisposable {\n\tstring _file;\n\tbool _loadedFile;\n\tbyte[] _old;\n\tJsonSerializerOptions _jsOpt;\n\treadonly object _lock = new();\n\t\n\tstatic readonly List<JSettings> s_list = new();\n\tstatic int s_loadedOnce;\n\t\n\t/// <summary>\n\t/// Loads a JSON file and deserializes to an object of type <c>T</c>, or creates a new object of type <c>T</c>.\n\t/// </summary>\n\t/// <returns>Object of type <c>T</c>. Either deserialized from file or new object with default values.</returns>\n\t/// <param name=\"file\">Full path of <c>.json</c> file. If <c>null</c>, does not load and will not save.</param>\n\t/// <param name=\"useDefault\">Use default settings. Don't load from <i>file</i>. Delete <i>file</i> if exists.</param>\n\t/// <param name=\"useDefaultOnError\">What to do if failed to load or parse the file: <c>true</c> (default) - backup (rename) the file and use default settings; <c>false</c> - throw exception (for example <see cref=\"JsonException\"/> if invalid JSON); <c>null</c> - show dialog with options to exit or use default settings.</param>\n\t/// <param name=\"jsOpt\">Options for deserializing and serializing. If <c>null</c>, uses <see cref=\"SerializerOptions\"/>.</param>\n\t/// <exception cref=\"ArgumentException\">Not full path.</exception>\n\t/// <exception cref=\"NotSupportedException\">Field type not supported by <see cref=\"JsonSerializer\"/>.</exception>\n\tprotected static T Load<T>(string file, bool useDefault = false, bool? useDefaultOnError = true, JsonSerializerOptions jsOpt = null) where T : JSettings\n\t\t=> (T)_Load(file, typeof(T), useDefault, useDefaultOnError, jsOpt);\n\t\n\tstatic JSettings _Load(string file, Type type, bool useDefault, bool? useDefaultOnError, JsonSerializerOptions jsOpt) {\n\t\tif (file == null) return Activator.CreateInstance(type) as JSettings;\n\t\t\n\t\tjsOpt ??= SerializerOptions;\n\t\t\n\t\tJSettings R = null;\n\t\tfile = pathname.normalize(file);\n\t\tif (filesystem.exists(file, true)) {\n\t\t\ttry {\n\t\t\t\tif (useDefault) {\n\t\t\t\t\tfilesystem.delete(file);\n\t\t\t\t} else {\n\t\t\t\t\t//using var p1 = perf.local(); //first time ~40 ms (similar hot and cold)\n\t\t\t\t\tvar b = filesystem.loadBytes(file);\n\t\t\t\t\t//p1.Next('f');\n\t\t\t\t\tR = JsonSerializer.Deserialize(b, type, jsOpt) as JSettings;\n\t\t\t\t\t//p1.Next('d');\n\t\t\t\t\tR._loadedFile = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception ex) when (ex is not NotSupportedException) {\n\t\t\t\tif (!useDefault) {\n\t\t\t\t\tif (useDefaultOnError == false) throw;\n\t\t\t\t\tif (useDefaultOnError == true) {\n\t\t\t\t\t\tstring backup = file + \".backup\";\n\t\t\t\t\t\tfilesystem.move(file, backup, FIfExists.Delete); //note: don't try/catch\n\t\t\t\t\t\tprint.it($\"<>Failed to load settings from {file}. Will use default settings.\\r\\n\\t<\\a>{ex.ToStringWithoutStack()}</\\a>\\r\\n\\tBackup: <explore>{backup}<>\");\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (!Environment.UserInteractive) throw;\n\t\t\t\t\t\tint button = dialog.show(\"Failed to load settings\",\n\t\t\t\t\t\t\tnew($\"{ex.ToStringWithoutStack()}\\n\\n<a>{file}</a>\", o => { run.selectInExplorer(file); }),\n\t\t\t\t\t\t\t\"1 Exit|2 Backup (rename) the file and use default settings\",\n\t\t\t\t\t\t\tflags: DFlags.CommandLinks,\n\t\t\t\t\t\t\ticon: DIcon.Error);\n\t\t\t\t\t\tif (button == 1) Environment.Exit(1);\n\t\t\t\t\t\telse if (filesystem.exists(file)) filesystem.move(file, file + \".backup\", FIfExists.Delete);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tR ??= Activator.CreateInstance(type) as JSettings;\n\t\t\n\t\tR._Ctor2(file, jsOpt);\n\t\t\n\t\t//autosave\n\t\tif (Interlocked.Exchange(ref s_loadedOnce, 1) == 0) {\n\t\t\trun.thread(() => {\n\t\t\t\tfor (; ; ) {\n\t\t\t\t\tThread.Sleep(2000);\n\t\t\t\t\t//Debug_.MemorySetAnchor();\n\t\t\t\t\t_SaveAllIfNeed(true);\n\t\t\t\t\t//Debug_.MemoryPrint(); //editor ~6 KB\n\t\t\t\t}\n\t\t\t}, sta: false).Name = \"Au.JSettings\";\n\t\t\t\n\t\t\tprocess.thisProcessExit += _ => { //info: .NET does not call finalizers when process exits\n\t\t\t\tFileWatcher.DisposeAll_();\n\t\t\t\t_SaveAllIfNeed(false);\n\t\t\t\ts_list.Clear();\n\t\t\t};\n\t\t}\n\t\t\n\t\tlock (s_list) s_list.Add(R);\n\t\t\n\t\treturn R;\n\t\t\n\t\tstatic void _SaveAllIfNeed(bool timer) {\n\t\t\tJSettings[] a; //can't lock both s_list and _lock (possible deadlock)\n\t\t\tlock (s_list) { a = s_list.ToArray(); }\n\t\t\t\n\t\t\tforeach (var v in a) {\n\t\t\t\tif (v.NoAutoSave) continue;\n\t\t\t\tif (timer && v.NoAutoSaveTimer) continue;\n\t\t\t\tv.SaveIfNeed();\n\t\t\t}\n\t\t}\n\t}\n\t\n\tvoid _Ctor2(string file, JsonSerializerOptions jsOpt) {\n\t\t_file = file;\n\t\t_jsOpt = jsOpt;\n\t\t_old = JsonSerializer.SerializeToUtf8Bytes(this, GetType(), _jsOpt);\n\t}\n\t\n\t/// <summary>\n\t/// Saves now if need, and releases used resources. In the future will not save or reload.\n\t/// Don't need to call if the settings are used until process exit.\n\t/// </summary>\n\tpublic void Dispose() {\n\t\tDispose(true);\n\t\t//no finalizer. Users can call Dispose, but usually settings objects live until process exit.\n\t}\n\t\n\t///\n\tprotected virtual void Dispose(bool disposing) {\n\t\tlock (s_list) if (!s_list.Remove(this)) return; //note: not inside `lock (_lock)` (possible deadlock)\n\t\t\n\t\tlock (_lock) {\n\t\t\t_watcher?.Dispose();\n\t\t\t_watcher = null;\n\t\t\t_modifiedExternally = null;\n\t\t\tif (!NoAutoSave) SaveIfNeed();\n\t\t\t_file = null;\n\t\t}\n\t}\n\t\n\t//repeated serialization speed with same options ~50 times better, eg 15000 -> 300 mcs cold. It's documented. Can be shared by multiple types.\n\tstatic readonly Lazy<JsonSerializerOptions> s_jsOptions = new(() => new() {\n\t\tAllowTrailingCommas = true,\n\t\tDefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,\n\t\tEncoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping,\n\t\tIncludeFields = true,\n\t\tIgnoreReadOnlyFields = true,\n\t\tIgnoreReadOnlyProperties = true,\n\t\tWriteIndented = true,\n\t});\n\t\n\t/// <summary>\n\t/// Default deserialization and serialization options.\n\t/// </summary>\n\t/// <value>\n\t/// <code><![CDATA[\n\t/// \tAllowTrailingCommas = true,\n\t/// \tDefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,\n\t/// \tEncoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,\n\t/// \tIncludeFields = true,\n\t/// \tIgnoreReadOnlyFields = true,\n\t/// \tIgnoreReadOnlyProperties = true,\n\t/// \tWriteIndented = true,\n\t/// ]]></code>\n\t/// </value>\n\tpublic static JsonSerializerOptions SerializerOptions => s_jsOptions.Value;\n\t\n\t/// <summary>\n\t/// <c>true</c> if settings were loaded from file.\n\t/// </summary>\n\t/// <remarks>\n\t/// Returns <c>false</c> if <see cref=\"Load\"/> did not find the file (the settings were not saved) or failed to load/parse or parameter <i>useDefault</i> = <c>true</c> or parameter <i>file</i> = <c>null</c>.\n\t/// </remarks>\n\t[JsonIgnore]\n\tpublic bool LoadedFile => _loadedFile;\n\t\n\t/// <summary>\n\t/// Don't automatically call <see cref=\"SaveIfNeed\"/>.\n\t/// If <c>false</c> (default), calls every 2 s (unless <see cref=\"NoAutoSaveTimer\"/> <c>true</c>); also when disposing and when the process exits.\n\t/// </summary>\n\tprotected bool NoAutoSave { get; set; }\n\t\n\t/// <summary>\n\t/// Don't call <see cref=\"SaveIfNeed\"/> every 2 s.\n\t/// Default <c>false</c>.\n\t/// </summary>\n\tprotected bool NoAutoSaveTimer { get; set; }\n\t\n\t/// <summary>\n\t/// Saves now if need.\n\t/// Call this when you want to save changed settings immediately; else the changes are auto-saved after max 2 s. See also <see cref=\"NoAutoSave\"/> and <see cref=\"NoAutoSaveTimer\"/>.\n\t/// </summary>\n\tpublic void SaveIfNeed() {\n\t\tlock (_lock) {\n\t\t\tif (_file is null) return;\n\t\t\tbool save = false;\n\t\t\ttry {\n\t\t\t\tvar b = JsonSerializer.SerializeToUtf8Bytes(this, GetType(), _jsOpt);\n\t\t\t\tsave = !b.AsSpan().SequenceEqual(_old);\n\t\t\t\tif (save) {\n\t\t\t\t\t_watcher?.Paused = true;\n\t\t\t\t\tfilesystem.saveBytes(_file, b);\n\t\t\t\t\t_old = b;\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception ex) { print.it($\"Failed to save settings to '{_file}'. {ex}\"); }\n\t\t\tfinally { if (save) _watcher?.Paused = false; }\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// When detected that the settings file was externally modified or deleted (for example by another process of your program).\n\t/// </summary>\n\t/// <remarks>\n\t/// Your event handler can call <see cref=\"Reload\"/>.\n\t/// \n\t/// Runs in a thread pool thread.\n\t/// </remarks>\n\t/// <exception cref=\"NotSupportedException\">Multiple <see cref=\"JSettings\"/> objects in this process use the same file.</exception>\n\tpublic event Action ModifiedExternally {\n\t\tadd {\n\t\t\tlock (_lock) {\n\t\t\t\tif (_file is null) return;\n\t\t\t\t_modifiedExternally += value ?? throw null;\n\t\t\t\t_watcher ??= FileWatcher.Watch(_file, _modifiedExternally);\n\t\t\t}\n\t\t}\n\t\tremove {\n\t\t\tlock (_lock) {\n\t\t\t\t_modifiedExternally -= value;\n\t\t\t\tif (_modifiedExternally == null) { _watcher?.Dispose(); _watcher = null; }\n\t\t\t}\n\t\t}\n\t}\n\t\n\tevent Action _modifiedExternally;\n\tFileWatcher _watcher;\n\t\n\t/// <summary>\n\t/// Reloads settings from the file.\n\t/// </summary>\n\t/// <param name=\"newSettings\">\n\t/// Receives a new settings object that inherits the behavior of this object but contains updated values of fields defined in the derived class. If the file does not exist, the fields have default values.\n\t/// If failed, receives this object (unchanged).\n\t/// </param>\n\t/// <returns><c>false</c> if failed.</returns>\n\t/// <remarks>\n\t/// Disposes this object. Does not change field values in it. It will no longer be tracked (e.g., for auto-save); tracking continues with the new object.\n\t/// </remarks>\n\t/// <example>\n\t/// Run this 2 times to test how a process of your app auto-reloads settings changed by another process of your app.\n\t/// <code><![CDATA[\n\t/// /*/ ifRunning run; /*/\n\t/// \n\t/// var sett = Settings.Load();\n\t/// sett.ModifiedExternally += () => {\n\t/// \tif (!sett.Reload(out sett)) return;\n\t/// \tprint.it($\"ModifiedExternally. This process: {process.thisProcessId}. {sett}\");\n\t/// };\n\t/// \n\t/// var b = new wpfBuilder(\"JSettings example\").WinSize(300).Columns(-1, -1, -1);\n\t/// b.R.AddButton(\"Print\", _ => { print.it(sett); });\n\t/// b.AddButton(\"one++\", _ => { sett.one++; sett.SaveIfNeed(); });\n\t/// b.AddButton(\"two++\", _ => { sett.two++; sett.SaveIfNeed(); });\n\t/// b.Window.Topmost = true;\n\t/// b.ShowDialog();\n\t/// \n\t/// record Settings : JSettings {\n\t/// \tpublic static Settings Load() => Load<Settings>(@\"C:\\Test\\JSettings.json\");\n\t/// \t\n\t/// #pragma warning disable CS0649\n\t/// \tpublic int one;\n\t/// \tpublic int two = 100;\n\t/// }\n\t/// ]]></code>\n\t/// </example>\n\tpublic bool Reload<T>(out T newSettings) where T : JSettings {\n\t\tbool ok = _Reload(out var js);\n\t\tnewSettings = js as T;\n\t\treturn ok;\n\t}\n\t\n\tbool _Reload(out JSettings newSettings) {\n\t\tnewSettings = this;\n\t\tJSettings R;\n\t\t\n\t\tlock (_lock) {\n\t\t\tif (_file is null) {\n\t\t\t\tprint.warning($\"JSettings.Reload called for a disposed or momory-only settings object\");\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t\n\t\t\ttry {\n\t\t\t\tif (filesystem.exists(_file, true)) {\n\t\t\t\t\tvar b = filesystem.loadBytes(_file);\n\t\t\t\t\tR = JsonSerializer.Deserialize(b, GetType(), _jsOpt) as JSettings;\n\t\t\t\t\tR._loadedFile = true;\n\t\t\t\t} else {\n\t\t\t\t\tR = Activator.CreateInstance(GetType()) as JSettings;\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tprint.warning($\"Failed to reload settings from '{_file}'. {ex}\", -1);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t\n\t\t\tR._Ctor2(_file, _jsOpt);\n\t\t\tR.NoAutoSave = NoAutoSave;\n\t\t\tR.NoAutoSaveTimer = NoAutoSaveTimer;\n\t\t\tR._watcher = _watcher; _watcher = null;\n\t\t\tR._modifiedExternally = _modifiedExternally; _modifiedExternally = null;\n\t\t\t_file = null;\n\t\t}\n\t\t\n\t\tlock (s_list) { s_list[s_list.IndexOf(this)] = R; } //note: not inside `lock (_lock)` (possible deadlock)\n\t\tnewSettings = R;\n\t\treturn true;\n\t}\n}\n"
  },
  {
    "path": "Au/Au.Types/TreeBase.cs",
    "content": "using System.Xml;\n\nnamespace Au.Types;\n\n/// <summary>\n/// Base class for tree classes.\n/// The tree can be loaded/saved as XML.\n/// </summary>\n/// <remarks>\n/// Implemented in the same way as <see cref=\"System.Xml.Linq.XContainer\"/>.\n/// </remarks>\n/// <example>\n/// Shows how to declare a <c>TreeBase</c>-derived class, load tree of nodes from an XML file, find descendant nodes, save the tree to an XML file.\n/// <code><![CDATA[\n/// using System.Xml;\n/// \n/// class MyTree : TreeBase<MyTree> {\n/// \tpublic string Name { get; set; }\n/// \tpublic int Id { get; private set; }\n/// \tpublic bool IsFolder { get; private set; }\n/// \n/// \tpublic MyTree(string name, int id, bool isFolder) { Name = name; Id = id; IsFolder = isFolder; }\n/// \n/// \t//XML element -> MyTree object\n/// \tMyTree(XmlReader x, MyTree parent)\n/// \t{\n/// \t\tif(parent == null) { //the root XML element\n/// \t\t\tif(x.Name != \"example\") throw new ArgumentException(\"XML root element must be 'example'\");\n/// \t\t\tIsFolder = true;\n/// \t\t} else {\n/// \t\t\tswitch(x.Name) {\n/// \t\t\tcase \"e\": break;\n/// \t\t\tcase \"f\": IsFolder = true; break;\n/// \t\t\tdefault: throw new ArgumentException(\"XML element must be 'e' or 'f'\");\n/// \t\t\t}\n/// #if true //two ways of reading attributes\n/// \t\t\tName = x[\"name\"];\n/// \t\t\tId = x[\"id\"].ToInt();\n/// #else\n/// \t\t\twhile(x.MoveToNextAttribute()) {\n/// \t\t\t\tvar v = x.Value;\n/// \t\t\t\tswitch(x.Name) {\n/// \t\t\t\tcase \"name\": Name = v; break;\n/// \t\t\t\tcase \"id\": Id = v.ToInt(); break;\n/// \t\t\t\t}\n/// \t\t\t}\n/// #endif\n/// \t\t\tif(Name.NE()) throw new ArgumentException(\"no 'name' attribute in XML\");\n/// \t\t\tif(Id == 0) throw new ArgumentException(\"no 'id' attribute in XML\");\n/// \t\t}\n/// \t}\n/// \n/// \tpublic static MyTree Load(string file) => XmlLoad(file, (x, p) => new MyTree(x, p));\n/// \n/// \tpublic void Save(string file) => XmlSave(file, (x, n) => n._XmlWrite(x));\n/// \n/// \t//MyTree object -> XML element\n/// \tvoid _XmlWrite(XmlWriter x)\n/// \t{\n/// \t\tif(Parent == null) {\n/// \t\t\tx.WriteStartElement(\"example\");\n/// \t\t} else {\n/// \t\t\tx.WriteStartElement(IsFolder ? \"f\" : \"e\");\n/// \t\t\tx.WriteAttributeString(\"name\", Name);\n/// \t\t\tx.WriteAttributeString(\"id\", Id.ToString());\n/// \t\t}\n/// \t}\n/// \n/// \tpublic override string ToString() => $\"{new string(' ', Level)}{(IsFolder ? 'f' : 'e')} {Name} ({Id})\";\n/// }\n/// \n/// static void TNodeExample() {\n/// \t/*\n/// \t<example>\n/// \t  <e name=\"one\" id=\"1\" />\n/// \t  <f name=\"two\" id=\"112\">\n/// \t\t<e name=\"three\" id=\"113\" />\n/// \t\t<e name=\"four\" id=\"114\" />\n/// \t\t<f name=\"five\" id=\"120\">\n/// \t\t  <e name=\"six\" id=\"121\" />\n/// \t\t  <e name=\"seven\" id=\"122\" />\n/// \t\t</f>\n/// \t  </f>\n/// \t  <f name=\"eight\" id=\"217\" />\n/// \t  <e name=\"ten\" id=\"144\" />\n/// \t</example>\n/// \t*/\n/// \n/// \tvar x = MyTree.Load(@\"C:\\test\\example.xml\");\n/// \tforeach(MyTree n in x.Descendants(true)) print.it(n);\n/// \t//print.it(x.Descendants().FirstOrDefault(k => k.Name == \"seven\")); //find a descendant\n/// \t//print.it(x.Descendants().Where(k => k.Level > 2)); //find some descendants\n/// \tx.Save(@\"C:\\test\\example2.xml\");\n/// }\n/// ]]></code>\n/// </example>\npublic abstract class TreeBase<T> where T : TreeBase<T> {\n\tT _next;\n\tT _parent;\n\tT _lastChild;\n\n\t#region properties\n\n\t/// <summary>\n\t/// Returns the parent node. Can be <c>null</c>.\n\t/// </summary>\n\tpublic T Parent => _parent;\n\n\t/// <summary>\n\t/// Returns the root ancestor node. Its <see cref=\"Parent\"/> is <c>null</c>.\n\t/// Returns this node if its <c>Parent</c> is <c>null</c>.\n\t/// </summary>\n\tpublic T RootAncestor {\n\t\tget {\n\t\t\tvar p = this as T;\n\t\t\twhile (p._parent != null) p = p._parent;\n\t\t\treturn p;\n\t\t}\n\t}\n\n\t/// <summary>\n\t/// Gets the number of ancestors (parent, its parent and so on).\n\t/// </summary>\n\tpublic int Level {\n\t\tget {\n\t\t\tint R = 0;\n\t\t\tfor (var p = _parent; p != null; p = p._parent) R++;\n\t\t\treturn R;\n\t\t}\n\t}\n\n\t/// <summary>\n\t/// Returns <c>true</c> if this node is a descendant of node <i>n</i>.\n\t/// </summary>\n\t/// <param name=\"n\">Can be <c>null</c>.</param>\n\tpublic bool IsDescendantOf(T n) {\n\t\tfor (var p = _parent; p != null; p = p._parent) if (p == n) return true;\n\t\treturn false;\n\t}\n\n\t/// <summary>\n\t/// Returns <c>true</c> if this node is a descendant of nearest ancestor node for which <i>predicate</i> returns <c>true</c>.\n\t/// </summary>\n\tpublic bool IsDescendantOf(Func<T, bool> predicate) {\n\t\tfor (var p = _parent; p != null; p = p._parent) if (predicate(p)) return true;\n\t\treturn false;\n\t}\n\n\t/// <summary>\n\t/// Returns <c>true</c> if this node is an ancestor of node <i>n</i>.\n\t/// </summary>\n\t/// <param name=\"n\">Can be <c>null</c>.</param>\n\tpublic bool IsAncestorOf(T n) => n?.IsDescendantOf(this as T) ?? false;\n\n\t/// <summary>\n\t/// Returns <c>true</c> if <see cref=\"Parent\"/> is not <c>null</c>.\n\t/// </summary>\n\tpublic bool HasParent => _parent != null;\n\n\t/// <summary>\n\t/// Returns <c>true</c> if this node has child nodes.\n\t/// </summary>\n\tpublic bool HasChildren => _lastChild != null;\n\n\t/// <summary>\n\t/// Gets the last child node, or <c>null</c> if none.\n\t/// </summary>\n\tpublic T LastChild => _lastChild;\n\n\t/// <summary>\n\t/// Gets the first child node, or <c>null</c> if none.\n\t/// </summary>\n\tpublic T FirstChild => _lastChild?._next;\n\n\t/// <summary>\n\t/// Gets next sibling node, or <c>null</c> if none.\n\t/// </summary>\n\tpublic T Next => _parent == null || this == _parent._lastChild ? null : _next;\n\n\t/// <summary>\n\t/// Gets previous sibling node, or <c>null</c> if none.\n\t/// </summary>\n\t/// <remarks>\n\t/// Can be slow if there are many siblings. This class does not have a \"previous\" field and therefore has to walk the linked list of siblings.\n\t/// </remarks>\n\tpublic T Previous {\n\t\tget {\n\t\t\tif (_parent == null) return null;\n\t\t\tT n = _parent._lastChild._next;\n\t\t\tDebug.Assert(n != null);\n\t\t\tT p = null;\n\t\t\twhile (n != this) {\n\t\t\t\tp = n;\n\t\t\t\tn = n._next;\n\t\t\t}\n\t\t\treturn p;\n\t\t}\n\t}\n\n\t/// <summary>\n\t/// Returns 0-based index of this node in parent.\n\t/// Returns -1 if no parent.\n\t/// </summary>\n\t/// <remarks>\n\t/// Can be slow if there are many siblings. This class does not have an \"index\" field and therefore has to walk the linked list of siblings.\n\t/// </remarks>\n\tpublic int Index {\n\t\tget {\n\t\t\tvar p = _parent;\n\t\t\tif (p != null) {\n\t\t\t\tvar n = p._lastChild;\n\t\t\t\tfor (int i = 0; ; i++) {\n\t\t\t\t\tn = n._next;\n\t\t\t\t\tif (n == this) return i;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn -1;\n\t\t}\n\t}\n\n\t#endregion\n\n\t#region methods\n\n\tvoid _AddCommon(T n) {\n\t\tif (n == null || n._parent != null || n == RootAncestor) throw new ArgumentException();\n\t\tn._parent = this as T;\n\t}\n\n\t/// <summary>\n\t/// Adds node <i>n</i> to this node as a child.\n\t/// </summary>\n\t/// <param name=\"n\"></param>\n\t/// <param name=\"first\">Insert <i>n</i> as the first child node. If <c>false</c> (default), appends to the end.</param>\n\t/// <exception cref=\"ArgumentException\"><i>n</i> is <c>null</c>, or has parent (need to <see cref=\"Remove\"/> at first), or is this node, or an ancestor of this node.</exception>\n\tpublic void AddChild(T n, bool first = false) {\n\t\t_AddCommon(n);\n\t\tif (_lastChild == null) { //our first child!\n\t\t\tn._next = n; //n now is LastChild and FirstChild\n\t\t} else {\n\t\t\tn._next = _lastChild._next; //_next of _lastChild is FirstChild\n\t\t\t_lastChild._next = n;\n\t\t\tif (first) return;\n\t\t}\n\t\t_lastChild = n;\n\t}\n\n\t/// <summary>\n\t/// Inserts node <i>n</i> before or after this node as a sibling.\n\t/// </summary>\n\t/// <param name=\"n\"></param>\n\t/// <param name=\"after\">Insert <i>n</i> after this node. If <c>false</c> (default), inserts before this node.</param>\n\t/// <exception cref=\"ArgumentException\">See <see cref=\"AddChild\"/>.</exception>\n\t/// <exception cref=\"InvalidOperationException\">This node does not have parent (<see cref=\"Parent\"/> is <c>null</c>).</exception>\n\tpublic void AddSibling(T n, bool after) {\n\t\tif (_parent == null) throw new InvalidOperationException(\"no parent\");\n\t\t_parent._Insert(n, this as T, after);\n\t}\n\n\tvoid _Insert(T n, T anchor, bool after) {\n\t\tif (after && anchor == _lastChild) { //after last child\n\t\t\tAddChild(n);\n\t\t} else if (!after && anchor == _lastChild._next) { //before first child\n\t\t\tAddChild(n, true);\n\t\t} else {\n\t\t\t_AddCommon(n);\n\t\t\tT prev, next;\n\t\t\tif (after) { prev = anchor; next = anchor._next; } else { prev = anchor.Previous; next = anchor; }\n\t\t\tn._next = next;\n\t\t\tprev._next = n;\n\t\t}\n\t}\n\n\t/// <summary>\n\t/// Removes this node from its parent.\n\t/// </summary>\n\t/// <remarks>\n\t/// After removing, the <see cref=\"Parent\"/> property is <c>null</c>.\n\t/// Does nothing if <c>Parent</c> is <c>null</c>.\n\t/// </remarks>\n\tpublic void Remove() => _parent?._Remove(this as T);\n\n\tvoid _Remove(T n) {\n\t\tDebug.Assert(n?._parent == this);\n\n\t\tT p = _lastChild;\n\t\twhile (p._next != n) p = p._next;\n\t\tif (p == n) {\n\t\t\t_lastChild = null;\n\t\t} else {\n\t\t\tif (_lastChild == n) _lastChild = p;\n\t\t\tp._next = n._next;\n\t\t}\n\t\tn._parent = null;\n\t\tn._next = null;\n\t}\n\n\t/// <summary>\n\t/// Gets ancestor nodes. The order is from this node towards the root node.\n\t/// </summary>\n\t/// <param name=\"andSelf\">Include this node.</param>\n\t/// <param name=\"noRoot\">Don't include <see cref=\"RootAncestor\"/>.</param>\n\tpublic IEnumerable<T> Ancestors(bool andSelf = false, bool noRoot = false) {\n\t\tvar n = andSelf ? this as T : _parent;\n\t\twhile (n != null) {\n\t\t\tif (noRoot && n._parent == null) break;\n\t\t\tyield return n;\n\t\t\tn = n._parent;\n\t\t}\n\t}\n\n\t/// <summary>\n\t/// Gets ancestor nodes. The order is from the root node towards this node.\n\t/// </summary>\n\t/// <param name=\"andSelf\">Include this node. Default <c>false</c>.</param>\n\t/// <param name=\"noRoot\">Don't include <see cref=\"RootAncestor\"/>.</param>\n\tpublic T[] AncestorsFromRoot(bool andSelf = false, bool noRoot = false) {\n\t\tT nFrom = andSelf ? this as T : _parent;\n\t\t//count\n\t\tint len = 0;\n\t\tfor (var n = nFrom; n != null; n = n._parent) {\n\t\t\tif (noRoot && n._parent == null) break;\n\t\t\tlen++;\n\t\t}\n\t\t//array\n\t\tif (len == 0) return [];\n\t\tvar a = new T[len];\n\t\tfor (var n = nFrom; len > 0; n = n._parent) a[--len] = n;\n\t\treturn a;\n\n\t\t//info: can use LINQ Reverse, but this func makes less garbage.\n\t}\n\n\t/// <summary>\n\t/// Gets all direct child nodes.\n\t/// </summary>\n\t/// <param name=\"andSelf\">Include this node. Default <c>false</c>.</param>\n\tpublic IEnumerable<T> Children(bool andSelf = false) {\n\t\tif (andSelf) yield return this as T;\n\t\tif (_lastChild != null) {\n\t\t\tvar n = _lastChild;\n\t\t\tdo {\n\t\t\t\tn = n._next;\n\t\t\t\tyield return n;\n\t\t\t} while (n != _lastChild);\n\t\t}\n\t}\n\n\t/// <summary>\n\t/// Gets number of direct child nodes.\n\t/// </summary>\n\tpublic int Count {\n\t\tget {\n\t\t\tint r = 0;\n\t\t\tif (_lastChild != null) {\n\t\t\t\tvar n = _lastChild;\n\t\t\t\tdo { r++; } while ((n = n._next) != _lastChild);\n\t\t\t}\n\t\t\treturn r;\n\t\t}\n\t}\n\n\t/// <summary>\n\t/// Gets all descendant nodes (direct children, their children and so on).\n\t/// </summary>\n\t/// <param name=\"andSelf\">Include this node. Default <c>false</c>.</param>\n\t/// <param name=\"stepInto\">If not <c>null</c>, called for each descendant node that has children. Let it return <c>false</c> to skip descendants of that node.</param>\n\tpublic IEnumerable<T> Descendants(bool andSelf = false, Func<T, bool> stepInto = null) {\n\t\tvar n = this as T;\n\t\tif (andSelf) yield return n;\n\t\twhile (true) {\n\t\t\tT last = n._lastChild;\n\t\t\tif (last != null && !(stepInto is {  } si && n != this && !si(n))) {\n\t\t\t\tn = last._next;\n\t\t\t} else {\n\t\t\t\twhile (n != null && n != this && n == n._parent._lastChild) n = n._parent;\n\t\t\t\tif (n == null || n == this) break;\n\t\t\t\tn = n._next;\n\t\t\t}\n\t\t\tyield return n;\n\t\t}\n\t}\n\n\t#endregion\n\n\t#region XML\n\n\t/// <summary>\n\t/// Used with <see cref=\"XmlLoad\"/>\n\t/// </summary>\n\tprotected delegate T XmlNodeReader(XmlReader x, T parent);\n\n\t/// <summary>\n\t/// Used with <see cref=\"XmlSave\"/>\n\t/// </summary>\n\tprotected delegate void XmlNodeWriter(XmlWriter x, T node);\n\n\t/// <summary>\n\t/// Loads XML file and creates tree of nodes from it.\n\t/// </summary>\n\t/// <returns>the root node.</returns>\n\t/// <param name=\"file\">XML file. Must be full path. Can contain environment variables etc, see <see cref=\"pathname.expand\"/>.</param>\n\t/// <param name=\"nodeReader\">Callback function that reads current XML element and creates/returns new node. See example.</param>\n\t/// <exception cref=\"ArgumentException\">Not full path.</exception>\n\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"XmlReader.Create(string)\"/>.</exception>\n\t/// <exception cref=\"XmlException\">An error occurred while parsing the XML.</exception>\n\t/// <example><see cref=\"TreeBase{T}\"/></example>\n\tprotected static T XmlLoad(string file, XmlNodeReader nodeReader) {\n\t\tfile = pathname.NormalizeMinimally_(file);\n\t\tvar xs = new XmlReaderSettings() { IgnoreComments = true, IgnoreProcessingInstructions = true, IgnoreWhitespace = true };\n\t\tusing var r = filesystem.waitIfLocked(() => XmlReader.Create(file, xs));\n\t\treturn XmlLoad(r, nodeReader);\n\t}\n\n\t/// <summary>\n\t/// Reads XML and creates tree of nodes.\n\t/// </summary>\n\t/// <returns>the root node.</returns>\n\t/// <param name=\"x\"></param>\n\t/// <param name=\"nodeReader\">Callback function that reads current XML element and creates/returns new node.</param>\n\t/// <exception cref=\"XmlException\">An error occurred while parsing the XML.</exception>\n\t/// <example><see cref=\"TreeBase{T}\"/></example>\n\tprotected static T XmlLoad(XmlReader x, XmlNodeReader nodeReader) {\n\t\tNot_.Null(x, nodeReader);\n\t\tT root = null, parent = null;\n\t\twhile (x.Read()) {\n\t\t\tvar nodeType = x.NodeType;\n\t\t\tif (nodeType == XmlNodeType.Element) {\n\t\t\t\tvar n = nodeReader(x, parent);\n\t\t\t\tif (root == null) root = n;\n\t\t\t\telse parent.AddChild(n);\n\t\t\t\tx.MoveToElement();\n\t\t\t\tif (!x.IsEmptyElement) parent = n;\n\t\t\t} else if (nodeType == XmlNodeType.EndElement) {\n\t\t\t\tif (parent == null) break;\n\t\t\t\tif (parent == root) break;\n\t\t\t\tparent = parent._parent;\n\t\t\t}\n\t\t}\n\t\treturn root;\n\t}\n\n\t/// <summary>\n\t/// Saves tree of nodes (this and descendants) to an XML file.\n\t/// </summary>\n\t/// <param name=\"file\">XML file. Must be full path. Can contain environment variables etc, see <see cref=\"pathname.expand\"/>.</param>\n\t/// <param name=\"nodeWriter\">Callback function that writes node's XML start element (see <see cref=\"XmlWriter.WriteStartElement(string)\"/>) and attributes (see <see cref=\"XmlWriter.WriteAttributeString(string, string)\"/>). Must not write children and end element. Also should not write value, unless your reader knows how to read it.</param>\n\t/// <param name=\"sett\">XML formatting settings. Optional.</param>\n\t/// <param name=\"children\">If not <c>null</c>, writes these nodes as if they were children of this node.</param>\n\t/// <exception cref=\"ArgumentException\">Not full path.</exception>\n\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"XmlWriter.Create(string)\"/> and other <see cref=\"XmlWriter\"/> methods.</exception>\n\t/// <remarks>\n\t/// Uses <see cref=\"filesystem.save\"/>. It ensures that existing file data is not damaged on exception etc.\n\t/// </remarks>\n\t/// <example><see cref=\"TreeBase{T}\"/></example>\n\tprotected void XmlSave(string file, XmlNodeWriter nodeWriter, XmlWriterSettings sett = null, IEnumerable<T> children = null) {\n\t\tfile = pathname.NormalizeMinimally_(file);\n\t\tsett ??= new XmlWriterSettings() { OmitXmlDeclaration = true, Indent = true, IndentChars = \"  \" };\n\t\tfilesystem.save(file, temp => {\n\t\t\tusing var x = XmlWriter.Create(temp, sett);\n\t\t\tXmlSave(x, nodeWriter, children);\n\t\t});\n\t}\n\n\t/// <summary>\n\t/// Writes tree of nodes (this and descendants) to an <see cref=\"XmlWriter\"/>.\n\t/// </summary>\n\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"XmlWriter\"/> methods.</exception>\n\t/// <example><see cref=\"TreeBase{T}\"/></example>\n\t/// <inheritdoc cref=\"XmlSave(string, XmlNodeWriter, XmlWriterSettings, IEnumerable{T})\" path=\"/param\"/>\n\tprotected void XmlSave(XmlWriter x, XmlNodeWriter nodeWriter, IEnumerable<T> children = null) {\n\t\tNot_.Null(x, nodeWriter);\n\t\tx.WriteStartDocument();\n\t\tif (children == null) {\n\t\t\t_XmlWrite(x, nodeWriter);\n\t\t} else {\n\t\t\tnodeWriter(x, this as T);\n\t\t\tforeach (var n in children) n._XmlWrite(x, nodeWriter);\n\t\t\tx.WriteEndElement();\n\t\t}\n\t\tx.WriteEndDocument();\n\t}\n\n\tvoid _XmlWrite(XmlWriter x, XmlNodeWriter nodeWriter) {\n\t\tnodeWriter(x, this as T);\n\t\tif (_lastChild != null) {\n\t\t\tvar c = _lastChild;\n\t\t\tdo {\n\t\t\t\tc = c._next;\n\t\t\t\tc._XmlWrite(x, nodeWriter);\n\t\t\t} while (c != _lastChild);\n\t\t}\n\t\tx.WriteEndElement();\n\t}\n\n\t#endregion\n}\n"
  },
  {
    "path": "Au/Au.Types/common.cs",
    "content": "using System.Diagnostics.CodeAnalysis;\nusing static Au.More.Serializer_;\n\nnamespace Au.Types {\n\t/// <summary>\n\t/// In DocFX-generated help files removes documentation and auto-generated links in TOC and class pages. For it is used filter.yml.\n\t/// </summary>\n\t[EditorBrowsable(EditorBrowsableState.Never), AttributeUsage(AttributeTargets.All)]\n\tpublic sealed class NoDoc : Attribute { }\n\t//tested: the DocFX /// <exclude /> removes the member from the compilation, not just prevents creating the doc article. Then may be error; don't use it. See https://dotnet.github.io/docfx/docs/dotnet-api-docs.html\n\t\n\t/// <summary>\n\t/// If a class is derived from this class, editor adds undeclared Windows API to its completion list.\n\t/// </summary>\n\tpublic abstract class NativeApi {\n\t\t//Or for it could use an attribute. But this base class easily solves 2 problems:\n\t\t//\t1. In 'new' expression does not show completion list (with types from winapi DB) if the winapi class still does not have types inside. Because the completion service then returns null. Now it is solved, because this class has nested types.\n\t\t//\t2. If class with attributes is after top-level statements, code info often does not work when typing directly above it. Works better if without attributes.\n\t\t//FUTURE: Also add attribute. Then can specify an alternative DB or text file with declarations. Maybe also some settings.\n\t\t//\tBut use this class as base too, like now. Eg could add protected util functions. Could use this class as both (base and attribute), but Attribute has static members.\n\t\t\n#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member\n\t\t\n\t\t/// <summary>\n\t\t/// Can be used in structures as flexible array member (the last field, defined like <c>Type[1] name;</c> in C).\n\t\t/// </summary>\n\t\tpublic struct FlexibleArray<T> where T : unmanaged {\n\t\t\tT _0;\n\t\t\t\n\t\t\tpublic ref T this[int index] {\n\t\t\t\t[UnscopedRef]\n\t\t\t\t[MethodImpl(MethodImplOptions.AggressiveInlining)]\n\t\t\t\tget => ref Unsafe.Add(ref _0, index);\n\t\t\t}\n\t\t\t\n\t\t\t[UnscopedRef]\n\t\t\t[MethodImpl(MethodImplOptions.AggressiveInlining)]\n\t\t\tpublic Span<T> AsSpan(int length) {\n\t\t\t\treturn MemoryMarshal.CreateSpan(ref _0, length);\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Windows API <c>BOOL</c>, with implicit conversions to/from C# <c>bool</c>.\n\t\t/// </summary>\n\t\tpublic readonly record struct BOOL {\n\t\t\tpublic readonly int IntValue;\n\t\t\t\n\t\t\tpublic BOOL(bool b) { IntValue = b ? 1 : 0; }\n\t\t\tpublic static implicit operator bool(BOOL b) => b.IntValue != 0;\n\t\t\tpublic static implicit operator BOOL(bool b) => new(b);\n\t\t\tpublic override string ToString() => IntValue == 0 ? \"False\" : \"True\";\n\t\t}\n#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member\n\t}\n\t\n\t/// <summary>\n\t/// Invokes specified action (calls callback function) at the end of <c>using(...) { ... }</c>.\n\t/// Usually returned by functions. Example: <see cref=\"opt.scope.mouse\"/>.\n\t/// </summary>\n\tpublic struct UsingEndAction : IDisposable {\n\t\treadonly Action _a;\n\t\t\n\t\t/// <summary>Sets action to be invoked when disposing this variable.</summary>\n\t\tpublic UsingEndAction(Action a) => _a = a;\n\t\t\n\t\t/// <summary>Invokes the action if not <c>null</c>.</summary>\n\t\tpublic void Dispose() => _a?.Invoke();\n\t}\n\t\n\t/// <summary>\n\t/// Used with <see cref=\"ParamStringAttribute\"/> to specify string parameter format.\n\t/// </summary>\n\tpublic enum PSFormat {\n\t\t///\n\t\tNone,\n\t\t\n\t\t/// <summary>\n\t\t/// Keys. See <see cref=\"keys.send(KKeysEtc[])\"/>.\n\t\t/// </summary>\n\t\tKeys,\n\t\t\n\t\t/// <summary>\n\t\t/// [Wildcard expression](xref:wildcard_expression).\n\t\t/// </summary>\n\t\tWildex,\n\t\t\n\t\t/// <summary>\n\t\t/// PCRE regular expression.\n\t\t/// </summary>\n\t\tRegexp,\n\t\t\n\t\t/// <summary>\n\t\t/// PCRE regular expression replacement string.\n\t\t/// </summary>\n\t\tRegexpReplacement,\n\t\t\n\t\t/// <summary>\n\t\t/// .NET regular expression.\n\t\t/// </summary>\n\t\tNetRegex,\n\t\t\n\t\t/// <summary>\n\t\t/// Hotkey, except triggers.\n\t\t/// </summary>\n\t\tHotkey,\n\t\t\n\t\t/// <summary>\n\t\t/// Hotkey trigger.\n\t\t/// </summary>\n\t\tHotkeyTrigger,\n\t\t\n\t\t/// <summary>\n\t\t/// Trigger modifiers without key.\n\t\t/// </summary>\n\t\tTriggerMod,\n\t\t\n\t\t/// <summary>\n\t\t/// Name or path of a script or class file in current workspace.\n\t\t/// </summary>\n\t\tCodeFile,\n\t\t\n\t\t/// <summary>\n\t\t/// Name or path of any file or folder in current workspace.\n\t\t/// </summary>\n\t\tFileInWorkspace,\n\t}\n\t\n\t/// <summary>\n\t/// A function parameter with this attribute is a string of the specified format, for example regular expression.\n\t/// Code editors should help to create correct string arguments: provide tools or reference, show errors.\n\t/// </summary>\n\t[AttributeUsage(AttributeTargets.Parameter /*| AttributeTargets.Field | AttributeTargets.Property*/, AllowMultiple = false)]\n\tpublic sealed class ParamStringAttribute : Attribute {\n\t\t//info: now .NET has similar attribute StringSyntaxAttribute. It was added later.\n\t\t\n\t\t///\n\t\tpublic ParamStringAttribute(PSFormat format) => Format = format;\n\t\t\n\t\t///\n\t\tpublic PSFormat Format { get; set; }\n\t}\n\t\n\t///// <summary>\n\t///// Specifies whether to set, add or remove flags.\n\t///// </summary>\n\t//public enum SetAddRemove\n\t//{\n\t//\t/// <summary>Set flags = the specified value.</summary>\n\t//\tSet = 0,\n\t//\t/// <summary>Add the specified flags, don't change others.</summary>\n\t//\tAdd = 1,\n\t//\t/// <summary>Remove the specified flags, don't change others.</summary>\n\t//\tRemove = 2,\n\t//\t/// <summary>Toggle the specified flags, don't change others.</summary>\n\t//\tXor = 3,\n\t//}\n}\n\nnamespace System.Runtime.CompilerServices //the attribute must be in this namespace\n{\n\t///\n\t[NoDoc]\n\t[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]\n\tpublic class IgnoresAccessChecksToAttribute : Attribute {\n\t\t///\n\t\tpublic IgnoresAccessChecksToAttribute(string assemblyName) { AssemblyName = assemblyName; }\n\t\t///\n\t\tpublic string AssemblyName { get; }\n\t}\n}\n"
  },
  {
    "path": "Au/Au.Types/exceptions.cs",
    "content": "namespace Au.Types {\n\t/// <summary>\n\t/// The base exception class used in this library.\n\t/// Thrown when something fails and there is no better exception type for that failure.\n\t/// </summary>\n\t/// <remarks>\n\t/// Some constructors support Windows API error code. Then <see cref=\"Message\"/> will contain its error description.\n\t/// If the string passed to the constructor starts with <c>\"*\"</c>, replaces the <c>\"*\"</c> with <c>\"Failed to \"</c>. If does not end with <c>\".\"</c>, appends <c>\".\"</c>.\n\t/// </remarks>\n\tpublic class AuException : Exception {\n\t\t/// <summary>\n\t\t/// Sets <see cref=\"Message\"/> = <i>message</i> (default <c>\"Failed.\"</c>).\n\t\t/// Sets <see cref=\"NativeErrorCode\"/> = 0.\n\t\t/// </summary>\n\t\tpublic AuException(string message = \"Failed.\", Exception innerException = null) : base(message, innerException) { }\n\n\t\t/// <summary>\n\t\t/// Sets <see cref=\"NativeErrorCode\"/> = <c>(errorCode != 0) ? errorCode : lastError.code</c>.\n\t\t/// Sets <see cref=\"Message\"/> = <c>message + \" \" + lastError.messageFor(NativeErrorCode)</c>.\n\t\t/// </summary>\n\t\tpublic AuException(int errorCode, string message = \"Failed.\", Exception innerException = null) : base(message, innerException) {\n\t\t\tNativeErrorCode = (errorCode != 0) ? errorCode : lastError.code;\n\t\t}\n\n\t\t/// <summary>Gets the Windows API error code.</summary>\n\t\tpublic int NativeErrorCode { get; protected set; }\n\n\t\t/// <summary>Gets error message.</summary>\n\t\tpublic override string Message => FormattedMessage ?? FormatMessage();\n\n\t\t/// <summary>String created by <see cref=\"FormatMessage\"/>, which should be called by the <see cref=\"Message\"/> override if <c>null</c>. Initially <c>null</c>.</summary>\n\t\tprotected string FormattedMessage;\n\n\t\t/// <summary>\n\t\t/// Formats error message. Sets and returns <see cref=\"FormattedMessage\"/>.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// As base text, uses the text passed to the constructor (default <c>\"Failed.\"</c>).\n\t\t/// If it starts with <c>\"*\"</c>, replaces the <c>\"*\"</c> with <c>\"Failed to \"</c>.\n\t\t/// If it ends with <c>\"*\"</c>, replaces the <c>\"*\"</c> with <i>commonPostfix</i> if it is not empty.\n\t\t/// If then the message does not end with <c>\".\"</c>, appends <c>\".\"</c>.\n\t\t/// If <i>appendMessage</i> is <c>null</c>, uses <c>lastError.messageFor(NativeErrorCode)</c> if <see cref=\"NativeErrorCode\"/> not 0.\n\t\t/// If then <i>appendMessage</i> is not empty, appends <c>\" \" + appendMessage</c>.\n\t\t/// Also appends <c>InnerException.Message</c> in new tab-indented line if <see cref=\"Exception.InnerException\"/> is not <c>null</c>.\n\t\t/// </remarks>\n\t\tprotected string FormatMessage(string appendMessage = null, string commonPostfix = null) {\n\t\t\tvar m = base.Message;\n\n\t\t\tif (!m.NE()) {\n\t\t\t\tif (m[0] == '*') m = \"Failed to \" + m[1..];\n\t\t\t\tif (!commonPostfix.NE() && m[^1] == '*') m = m[..^1] + commonPostfix;\n\t\t\t\tif (!m.Ends('.')) m += \".\";\n\t\t\t}\n\n\t\t\tif (appendMessage == null && NativeErrorCode != 0) appendMessage = lastError.messageFor(NativeErrorCode);\n\n\t\t\tif (!appendMessage.NE()) m = m + \" \" + appendMessage;\n\n\t\t\tif (InnerException != null) m = m + \"\\r\\n\\t\" + InnerException.Message;\n\n\t\t\treturn FormattedMessage = m;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// If <i>errorCode</i> is not 0, throws <see cref=\"AuException\"/> that includes the code and its message.\n\t\t/// More info: <see cref=\"FormatMessage\"/>.\n\t\t/// </summary>\n\t\t/// <param name=\"errorCode\">Windows API error code or <c>HRESULT</c>.</param>\n\t\t/// <param name=\"message\">Main message. The message of the error code will be appended to it.</param>\n\t\tpublic static void ThrowIfHresultNot0(int errorCode, string message = null) {\n\t\t\tif (errorCode != 0) throw new AuException(errorCode, message);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// If <i>errorCode</i> is less than 0, throws <see cref=\"AuException\"/> that includes the code and its message.\n\t\t/// More info: <see cref=\"FormatMessage\"/>.\n\t\t/// </summary>\n\t\t/// <param name=\"errorCode\">Windows API error code or <c>HRESULT</c>.</param>\n\t\t/// <param name=\"message\">Main message. The message of the error code will be appended to it.</param>\n\t\tpublic static void ThrowIfHresultNegative(int errorCode, string message = null) {\n\t\t\tif (errorCode < 0) throw new AuException(errorCode, message);\n\t\t}\n\t}\n\n\t/// <summary>\n\t/// Exception thrown mostly by <see cref=\"wnd\"/> functions.\n\t/// </summary>\n\t/// <remarks>\n\t/// Some constructors support Windows API error code. Then <see cref=\"Message\"/> also will contain its error description.\n\t/// If error code <c>ERROR_INVALID_WINDOW_HANDLE</c>, <see cref=\"Message\"/> also depends on whether the window handle is 0.\n\t/// If parameter <i>errorCode</i> is 0 or not used: if the window handle is invalid, uses <c>ERROR_INVALID_WINDOW_HANDLE</c>.\n\t/// If the string passed to the constructor starts with <c>\"*\"</c>, replaces the <c>\"*\"</c> with <c>\"Failed to \"</c>. If ends with <c>\"*\"</c>, replaces the <c>\"*\"</c> with <c>\" window.\"</c>. If does not end with <c>\".\"</c>, appends <c>\".\"</c>.\n\t/// </remarks>\n\tpublic class AuWndException : AuException {\n\t\tconst string _errStr_0Handle = \"The window handle is 0. Usually it means 'window not found'.\";\n\t\tconst string _errStr_InvalidHandle = \"Invalid window handle. Usually it means 'the window was closed'.\";\n\n\t\t/// <summary>\n\t\t/// Sets <see cref=\"Message\"/> = <i>message</i> (default <c>\"Failed.\"</c>).\n\t\t/// Sets <see cref=\"AuException.NativeErrorCode\"/> = <c>w.IsAlive ? 0 : ERROR_INVALID_WINDOW_HANDLE</c>.\n\t\t/// </summary>\n\t\tpublic AuWndException(wnd w, string message = \"Failed.\", Exception innerException = null)\n\t\t\t: base(message, innerException) { Window = w; NativeErrorCode = _Code(0, w); }\n\n\t\t/// <summary>\n\t\t/// Sets <see cref=\"AuException.NativeErrorCode\"/> = <c>(errorCode != 0) ? errorCode : (w.IsAlive ? lastError.code : ERROR_INVALID_WINDOW_HANDLE)</c>.\n\t\t/// Sets <see cref=\"Message\"/> = <c>message + \" \" + lastError.messageFor(NativeErrorCode)</c>.\n\t\t/// </summary>\n\t\tpublic AuWndException(wnd w, int errorCode, string message = \"Failed.\", Exception innerException = null)\n\t\t\t: base(_Code(errorCode, w), message, innerException) { Window = w; }\n\n\t\tstatic int _Code(int code, wnd w) {\n\t\t\tif (code != 0 || w.IsAlive) return code;\n\t\t\treturn Api.ERROR_INVALID_WINDOW_HANDLE;\n\t\t}\n\n\t\t/// <summary>Gets the window passed to the constructor.</summary>\n\t\tpublic wnd Window { get; }\n\n\t\t/// <summary>Gets error message.</summary>\n\t\tpublic override string Message {\n\t\t\tget {\n\t\t\t\tif (FormattedMessage == null) {\n\t\t\t\t\tstring m;\n\t\t\t\t\tif (Window.Is0) m = _errStr_0Handle;\n\t\t\t\t\telse if (NativeErrorCode == Api.ERROR_INVALID_WINDOW_HANDLE) m = _errStr_InvalidHandle;\n\t\t\t\t\telse m = null; //will append lastError.messageFor(NativeErrorCode) if NativeErrorCode not 0, or InnerException.Message if it is not null.\n\t\t\t\t\tFormatMessage(m, \" window.\");\n\t\t\t\t}\n\t\t\t\treturn FormattedMessage;\n\t\t\t}\n\t\t}\n\t}\n\n\t/// <summary>\n\t/// Functions that search for an object can throw this exception when not found.\n\t/// </summary>\n\tpublic class NotFoundException : Exception {\n\t\t/// <summary>\n\t\t/// Sets <c>Message = \"Not found.\"</c>.\n\t\t/// </summary>\n\t\tpublic NotFoundException() : base(\"Not found.\") { }\n\n\t\t/// <summary>\n\t\t/// Sets <c>Message = message</c>.\n\t\t/// </summary>\n\t\tpublic NotFoundException(string message) : base(message) { }\n\t}\n\n\t///// <summary>\n\t///// Exception thrown when a callback function returns an invalid value.\n\t///// </summary>\n\t//public class BadCallbackException : Exception {\n\t//\t/// <summary>\n\t//\t/// Sets <c>Message = \"The callback function returned an invalid value.\"</c>.\n\t//\t/// </summary>\n\t//\tpublic BadCallbackException() : base(\"The callback function returned an invalid value.\") { }\n\n\t//\t/// <summary>\n\t//\t/// Sets <c>Message = message</c>.\n\t//\t/// </summary>\n\t//\tpublic BadCallbackException(string message) : base(message) { }\n\t//}\n\n\t/// <summary>\n\t/// This exception is thrown when current thread is not on the input desktop and therefore cannot use mouse, keyboard, clipboard and window functions. \n\t/// For example when PC locked (<c>Win+L</c>), screen saver, UAC consent, <c>Ctrl+Alt+Delete</c> or not in the active user session.\n\t/// </summary>\n\t/// <seealso cref=\"miscInfo.isInputDesktop\"/>\n\tpublic class InputDesktopException : AuException {\n\t\t/// <param name=\"message\">Message text before the standard message text of this exception.</param>\n\t\tpublic InputDesktopException(string message = null)\n\t\t\t: base(message.NE() ? c_message : message + (message.Ends('.') ? \" \" : \". \") + c_message) { }\n\n\t\tconst string c_message = \"The main input desktop is inactive; mouse, keyboard, clipboard and UI functions are disabled.\";\n\n\t\t/// <summary>\n\t\t/// Calls <see cref=\"miscInfo.isInputDesktop\"/>. If it returns <c>false</c>, throws <see cref=\"InputDesktopException\"/>.\n\t\t/// </summary>\n\t\t/// <param name=\"message\">Message text before the standard message text of this exception.</param>\n\t\t/// <exception cref=\"InputDesktopException\"></exception>\n\t\tpublic static void ThrowIfBadDesktop(string message = null, bool detectLocked = false) {\n\t\t\tif (!miscInfo.isInputDesktop(detectLocked)) throw new InputDesktopException(message);\n\t\t}\n\t}\n}\n\nnamespace Au.Types {\n\tstatic partial class ExtMisc {\n\t\t/// <summary>\n\t\t/// Returns string containing exception type name and message.\n\t\t/// </summary>\n\t\tpublic static string ToStringWithoutStack(this Exception t) {\n\t\t\treturn t.GetType().Name + \", \" + t.Message;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au/Au.Types/param types.cs",
    "content": "namespace Au.Types;\n\n#if false //test docfx preprocessing\n/// <summary>\n/// Sum.\n/// two\n/// </summary>\n/// <param name=\"A\">A.</param>\n/// <param name=\"B\">B.</param>\npublic record class PublicRecord(int A, int B);\n\nrecord class InternalRecord_ {\n\tpublic int A { get; set; }\n\tpublic int B { get; set; }\n}\n\nrecord class InternalRecord2_(int A, int B) {\n\tpublic int C { get; set; }\n}\n\nrecord class InternalRecord3_(int A, int B);\n\npublic class NormalClass {\n\n\trecord class _Record4(int A, int B) {\n\t\tpublic int C { get; set; }\n\t}\n\n\t/// <summary>\n\t/// Sum.\n\t/// </summary>\n\t/// <param name=\"i\">Param.</param>\n\tpublic void Meth(int i) { }\n}\n#endif\n\n\n\n/// <summary>\n/// Contains x or y coordinate in screen or some other rectangle that can be specified in various ways: normal, reverse, fraction, center, max.\n/// Used for parameters of functions like <see cref=\"mouse.move\"/>, <see cref=\"wnd.Move\"/>.\n/// </summary>\n/// <remarks>\n/// To specify a normal coordinate (the origin is the left or top edge), assign an <c>int</c> value (implicit conversion from <c>int</c> to <c>Coord</c>).\n/// To specify a reverse coordinate (the origin is the right or bottom edge), use <see cref=\"Reverse\"/> or a \"from end\" index like <c>^1</c>. It is towards the left or top edge, unless negative. Or use <see cref=\"Max\"/> or <see cref=\"MaxInside\"/>.\n/// To specify a \"fraction of the rectangle\" coordinate, use <see cref=\"Fraction\"/> or a value of type float like <c>.5f</c>. Or use <see cref=\"Center\"/>.\n/// The meaning of <c>default(Coord)</c> depends on function where used. Many functions interpret it as center (same as <c>Coord.Center</c> or <c>.5f</c>).\n/// Also there are functions to convert <c>Coord</c> to normal coordinates.\n/// </remarks>\n/// <example>\n/// <code><![CDATA[\n/// mouse.move(100, 100); //left edge + 100, top edge + 100\n/// mouse.move(Coord.Reverse(100), 100); //right edge - 100, top edge + 100\n/// mouse.move(100, ^100); //left edge + 100, bottom edge - 100\n/// mouse.move(Coord.Fraction(.33), .9f); //left edge + 1/3 of the screen rectangle, top edge + 9/10\n/// mouse.move(Coord.Center, Coord.MaxInside); //x in center (left edge + 1/2), y by the bottom edge inside (Coord.Max would be outside)\n/// mouse.move(Coord.Reverse(-100), 1.1f); //right edge + 100, bottom edge + 0.1 of the rectangle\n/// \n/// var w = wnd.find(1, \"Untitled - Notepad\", \"Notepad\");\n/// w.Move(.5f, 100, .5f, ^200); //x = center, y = 100, width = half of screen, height = screen height - 200\n/// ]]></code>\n/// </example>\npublic record struct Coord {\n\t//Use single long field that packs int and CoordType.\n\t//If 2 fields (int and CoordType), 64-bit compiler creates huge calling code.\n\t//This version is good in 32-bit, very good in 64-bit. Even better than packing in single int (30 bits value and 2 bits type).\n\t//Don't use struct or union with both int and float fields. It creates slow and huge calling code.\n\treadonly long _v;\n\t\n\t/// <summary>\n\t/// Value type.\n\t/// </summary>\n\tpublic CoordType Type => (CoordType)(_v >> 32);\n\t\n\t/// <summary>\n\t/// Non-fraction value.\n\t/// </summary>\n\tpublic int Value => (int)_v;\n\t\n\t/// <summary>\n\t/// Fraction value.\n\t/// </summary>\n\tpublic unsafe float FractionValue => BitConverter.Int32BitsToSingle((int)_v);\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if <c>Type == CoordType.None</c> (no value assigned).\n\t/// </summary>\n\tpublic bool IsEmpty => Type == CoordType.None;\n\t\n\tCoord(CoordType type, int value) { _v = ((long)type << 32) | (uint)value; }\n\t//info: if type and value are constants, compiler knows the final value and does not use the << and | operators in the compiled code.\n\t\n\t/// <summary>\n\t/// Creates <see cref=\"Coord\"/> of <c>Normal</c> type.\n\t/// </summary>\n\t//[MethodImpl(MethodImplOptions.NoInlining)] //makes bigger/slower\n\tpublic static implicit operator Coord(int v) => new(CoordType.Normal, v);\n\t\n\t/// <summary>\n\t/// Creates <see cref=\"Coord\"/> of <c>Normal</c> or <c>Reverse</c> type. Reverse if the index is from end, like <c>^1</c>.\n\t/// </summary>\n\tpublic static implicit operator Coord(Index v) => new(v.IsFromEnd ? CoordType.Reverse : CoordType.Normal, v.Value);\n\t\n\t/// <summary>\n\t/// Creates <see cref=\"Coord\"/> of <c>Fraction</c> type.\n\t/// </summary>\n\tpublic static implicit operator Coord(float v) => new(CoordType.Fraction, BitConverter.SingleToInt32Bits(v));\n\t\n\t//these would create Fraction\n\t///\n\t[Obsolete(\"The value must be of type int or float.\", error: true), NoDoc]\n\tpublic static implicit operator Coord(uint f) => default;\n\t///\n\t[Obsolete(\"The value must be of type int or float.\", error: true), NoDoc]\n\tpublic static implicit operator Coord(long f) => default;\n\t///\n\t[Obsolete(\"The value must be of type int or float.\", error: true), NoDoc]\n\tpublic static implicit operator Coord(ulong f) => default;\n\t//tested: compiler does not allow to assign nint.\n\t\n\t/// <summary>\n\t/// Creates <see cref=\"Coord\"/> of <c>Reverse</c> type.\n\t/// Value 0 is at the right or bottom, and does not belong to the rectangle. Positive values are towards left or top.\n\t/// </summary>\n\t/// <remarks>\n\t/// Instead can be use \"from end\" index, for example argument <c>Coord.Reverse(1)</c> can be replaced with <c>^1</c>.\n\t/// </remarks>\n\tpublic static Coord Reverse(int v) => new(CoordType.Reverse, v);\n\t\n\t/// <summary>\n\t/// Creates <see cref=\"Coord\"/> of <c>Fraction</c> type.\n\t/// Value 0 is the left or top of the rectangle. Value 1.0 is the right or bottom of the rectangle. Values &lt;0 and >=1.0 are outside of the rectangle.\n\t/// </summary>\n\t/// <remarks>\n\t/// Instead can be used implicit conversion from <c>float</c>, for example argument <c>Coord.Fraction(.5)</c> can be replaced with <c>.5f</c>.\n\t/// </remarks>\n\tpublic static unsafe Coord Fraction(double v) => (float)v;\n\t\n\t/// <summary>\n\t/// Returns <c>Fraction(0.5)</c>.\n\t/// </summary>\n\t/// <seealso cref=\"Fraction\"/>\n\tpublic static Coord Center => .5f;\n\t\n\t/// <summary>\n\t/// Returns <c>Reverse(0)</c>. Same as <c>^0</c>.\n\t/// This point will be outside of the rectangle. See also <see cref=\"MaxInside\"/>.\n\t/// </summary>\n\t/// <seealso cref=\"Reverse\"/>\n\tpublic static Coord Max => Reverse(0);\n\t\n\t/// <summary>\n\t/// Returns <c>Reverse(1)</c>. Same as <c>^1</c>.\n\t/// This point will be inside of the rectangle, at the very right or bottom, assuming the rectangle is not empty.\n\t/// </summary>\n\t/// <seealso cref=\"Reverse\"/>\n\tpublic static Coord MaxInside => Reverse(1);\n\t\n\t//rejected: this could be used like Coord.Max + 1. Too limited usage.\n\t//public static Coord operator +(Coord c, int i) { return ...; }\n\t\n\tstatic bool _NeedRect(Coord x, Coord y) {\n\t\treturn (x.Type > CoordType.Normal) || (y.Type > CoordType.Normal);\n\t}\n\t\n\t/// <summary>\n\t/// Converts fractional/reverse coordinate to normal coordinate in a range.\n\t/// </summary>\n\t/// <param name=\"start\">Start of range.</param>\n\t/// <param name=\"end\">End of range.</param>\n\tpublic int NormalizeInRange(int start, int end) {\n\t\treturn Type switch {\n\t\t\tCoordType.Normal => start + Value,\n\t\t\tCoordType.Reverse => end - Value,\n\t\t\tCoordType.Fraction => start + (int)((end - start) * FractionValue),\n\t\t\t_ => 0,\n\t\t};\n\t}\n\t\n\t/// <summary>\n\t/// Converts fractional/reverse coordinates to normal coordinates in a rectangle.\n\t/// </summary>\n\t/// <param name=\"x\">X coordinate relative to <i>r</i>.</param>\n\t/// <param name=\"y\">Y coordinate relative to <i>r</i>.</param>\n\t/// <param name=\"r\">The rectangle.</param>\n\t/// <param name=\"widthHeight\">Use only width and height of <i>r</i>. If <c>false</c> (default), the function adds <i>r</i> offset (left and top).</param>\n\t/// <param name=\"centerIfEmpty\">If <i>x</i> or <i>y</i> is <c>default</c>, use <see cref=\"Coord.Center\"/>. Not used with <i>widthHeight</i>.</param>\n\tpublic static POINT NormalizeInRect(Coord x, Coord y, RECT r, bool widthHeight = false, bool centerIfEmpty = false) {\n\t\tif (widthHeight) r.Move(0, 0);\n\t\telse if (centerIfEmpty) {\n\t\t\tif (x.IsEmpty) x = Center;\n\t\t\tif (y.IsEmpty) y = Center;\n\t\t}\n\t\treturn (x.NormalizeInRange(r.left, r.right), y.NormalizeInRange(r.top, r.bottom));\n\t}\n\t\n\t/// <summary>\n\t/// Returns normal coordinates relative to the client area of a window. Converts fractional/reverse coordinates etc.\n\t/// </summary>\n\t/// <param name=\"x\">X coordinate relative to the client area of <i>w</i>.</param>\n\t/// <param name=\"y\">Y coordinate relative to the client area of <i>w</i>.</param>\n\t/// <param name=\"w\">The window.</param>\n\t/// <param name=\"nonClient\"><i>x</i> <i>y</i> are relative to the entire <i>w</i> rectangle, not to its client area.</param>\n\t/// <param name=\"centerIfEmpty\">If <i>x</i> or <i>y</i> is <c>default</c>, use <see cref=\"Coord.Center\"/>.</param>\n\tpublic static POINT NormalizeInWindow(Coord x, Coord y, wnd w, bool nonClient = false, bool centerIfEmpty = false) {\n\t\t//info: don't need widthHeight parameter because client area left/top are 0. With non-client don't need in this library and probably not useful. But if need, caller can explicitly offset the rect before calling this func.\n\t\t\n\t\tif (centerIfEmpty) {\n\t\t\tif (x.IsEmpty) x = Center;\n\t\t\tif (y.IsEmpty) y = Center;\n\t\t}\n\t\tPOINT p = default;\n\t\tif (!x.IsEmpty || !y.IsEmpty) {\n\t\t\tRECT r;\n\t\t\tif (nonClient) {\n\t\t\t\tw.GetRectIn(w, out r);\n\t\t\t} else if (_NeedRect(x, y)) {\n\t\t\t\tr = w.ClientRect;\n\t\t\t} else r = default;\n\t\t\tp.x = x.NormalizeInRange(r.left, r.right);\n\t\t\tp.y = y.NormalizeInRange(r.top, r.bottom);\n\t\t}\n\t\treturn p;\n\t}\n\t\n\t/// <summary>\n\t/// Returns normal coordinates relative to the primary screen. Converts fractional/reverse coordinates etc.\n\t/// </summary>\n\t/// <param name=\"x\">X coordinate relative to the specified screen (<c>default</c> - primary).</param>\n\t/// <param name=\"y\">Y coordinate relative to the specified screen (<c>default</c> - primary).</param>\n\t/// <param name=\"workArea\"><i>x</i> <i>y</i> are relative to the work area.</param>\n\t/// <param name=\"screen\">If used, <i>x</i> <i>y</i> are relative to this screen. Default - primary screen. Example: <c>screen.index(1)</c>.</param>\n\t/// <param name=\"widthHeight\">Use only width and height of the screen rectangle. If <c>false</c>, the function adds its offset (left and top, which can be nonzero if using the work area or a non-primary screen).</param>\n\t/// <param name=\"centerIfEmpty\">If <i>x</i> or <i>y</i> is <c>default</c>, use <see cref=\"Coord.Center\"/>.</param>\n\tpublic static POINT Normalize(Coord x, Coord y, bool workArea = false, screen screen = default, bool widthHeight = false, bool centerIfEmpty = false) {\n\t\tif (centerIfEmpty) {\n\t\t\tif (x.IsEmpty) x = Center;\n\t\t\tif (y.IsEmpty) y = Center;\n\t\t}\n\t\tPOINT p = default;\n\t\tif (!x.IsEmpty || !y.IsEmpty) {\n\t\t\tRECT r;\n\t\t\tif (workArea || !screen.IsEmpty || _NeedRect(x, y)) {\n\t\t\t\tr = screen.GetRect(workArea);\n\t\t\t\tif (widthHeight) r.Move(0, 0);\n\t\t\t} else r = default;\n\t\t\tp.x = x.NormalizeInRange(r.left, r.right);\n\t\t\tp.y = y.NormalizeInRange(r.top, r.bottom);\n\t\t}\n\t\treturn p;\n\t}\n\t\n\t///\n\tpublic override string ToString() {\n\t\tswitch (Type) {\n\t\tcase CoordType.Normal: return Value.ToString() + \", Normal\";\n\t\tcase CoordType.Reverse: return Value.ToString() + \", Reverse\";\n\t\tcase CoordType.Fraction: return FractionValue.ToS() + \", Fraction\";\n\t\tdefault: return \"default\";\n\t\t}\n\t}\n}\n\n/// <summary>\n/// <see cref=\"Coord\"/> variable value type.\n/// </summary>\npublic enum CoordType {\n\t/// <summary>\n\t/// No value. The variable is <c>default(Coord)</c>.\n\t/// </summary>\n\tNone,\n\t\n\t/// <summary>\n\t/// <see cref=\"Coord.Value\"/> is pixel offset from left or top of a rectangle.\n\t/// </summary>\n\tNormal,\n\t\n\t/// <summary>\n\t/// <see cref=\"Coord.Value\"/> is pixel offset from right or bottom of a rectangle, towards left or top.\n\t/// </summary>\n\tReverse,\n\t\n\t/// <summary>\n\t/// <see cref=\"Coord.FractionValue\"/> is fraction of a rectangle, where 0.0 is left or top, and 1.0 is right or bottom (outside of the rectangle).\n\t/// </summary>\n\tFraction,\n}\n\n/// <summary>\n/// Can be used to specify coordinates for various popup windows, like <c>new PopupXY(x, y)</c>, <c>(x, y)</c>, <c>PopupXY.In(rectangle)</c>, <c>PopupXY.Mouse</c>.\n/// </summary>\npublic class PopupXY {\n#pragma warning disable 1591 //XML doc\n\tpublic Coord x, y;\n\tpublic screen screen;\n\tpublic bool workArea;\n\tpublic bool inRect;\n\tpublic RECT rect;\n#pragma warning restore 1591 //XML doc\n\t\n\t/// <summary>\n\t/// Sets position and/or screen.\n\t/// </summary>\n\t/// <param name=\"x\">X relative to the screen or work area. Default - center.</param>\n\t/// <param name=\"y\">X relative to the screen or work area. Default - center.</param>\n\t/// <param name=\"workArea\"><i>x y</i> are relative to the work area of the screen.</param>\n\t/// <param name=\"screen\">Can be used to specify a screen. Default - primary. Example: <c>screen.index(1)</c>.</param>\n\t/// <remarks>\n\t/// Also there is are implicit conversions from tuple (x, y) and <see cref=\"POINT\"/>. Instead of <c>new PopupXY(x, y)</c> you can use <c>(x, y)</c>. Instead of <c>new PopupXY(p.x, p.y, false)</c> you can use <c>p</c> or <c>(POINT)p</c> .\n\t/// </remarks>\n\tpublic PopupXY(Coord x = default, Coord y = default, bool workArea = true, screen screen = default) {\n\t\tthis.x = x; this.y = y; this.workArea = workArea; this.screen = screen;\n\t}\n\t\n\t/// <summary>\n\t/// Creates new <see cref=\"PopupXY\"/> that specifies position in a rectangle. For example of the owner window.\n\t/// </summary>\n\t/// <param name=\"r\">Rectangle relative to the primary screen.</param>\n\t/// <param name=\"x\">X relative to the rectangle. Default - center.</param>\n\t/// <param name=\"y\">Y relative to the rectangle. Default - center.</param>\n\tpublic static PopupXY In(RECT r, Coord x = default, Coord y = default) => new(x, y) { inRect = true, rect = r };\n\t\n\t/// <summary>\n\t/// Creates new <see cref=\"PopupXY\"/> that specifies position relative to the work area of the primary screen.\n\t/// </summary>\n\tpublic static implicit operator PopupXY((Coord x, Coord y) p) => new(p.x, p.y, true);\n\t\n\t/// <summary>Creates new <see cref=\"PopupXY\"/> that specifies position relative to the primary screen (not to the work area).</summary>\n\tpublic static implicit operator PopupXY(POINT p) => new(p.x, p.y, false);\n\t//info: this conversion can be used with PopupXY.Mouse.\n\t\n\t//public bool IsRawXY => !inRect && screen.IsNull && workArea == false && x.Type == Coord.CoordType.Normal && y.Type == Coord.CoordType.Normal;\n\t\n\t/// <summary>\n\t/// Gets point coordinates below mouse cursor, for showing a tooltip-like popup.\n\t/// </summary>\n\tpublic static POINT Mouse {\n\t\tget {\n\t\t\tvar p = mouse.xy;\n\t\t\t\n\t\t\tvar scr = screen.of(p);\n\t\t\tvar rs = scr.Rect;\n\t\t\tint dy = Dpi.Scale(100, scr.Dpi);\n\t\t\tif (rs.bottom - p.y < dy) return (p.x, p.y - dy);\n\t\t\t\n\t\t\tint cy = Dpi.GetSystemMetrics(Api.SM_CYCURSOR, p);\n\t\t\tif (MouseCursor.GetCurrentVisibleCursor(out var c) && Api.GetIconInfo(c, out var u)) {\n\t\t\t\tif (u.hbmColor != default) Api.DeleteObject(u.hbmColor);\n\t\t\t\tApi.DeleteObject(u.hbmMask);\n\t\t\t\t\n\t\t\t\t//print.it(u.xHotspot, u.yHotspot);\n\t\t\t\tp.y += cy - u.yHotspot - 1; //not perfect, but better than just to add SM_CYCURSOR or some constant value.\n\t\t\t\treturn p;\n\t\t\t}\n\t\t\treturn (p.x, p.y + cy - 5);\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Gets <see cref=\"screen.Now\"/> if not empty, else screen that contains the specified point.\n\t/// </summary>\n\tpublic screen GetScreen() {\n\t\tif (!screen.IsEmpty) return screen.Now;\n\t\tPOINT p = inRect ? Coord.NormalizeInRect(x, y, rect, centerIfEmpty: true) : Coord.Normalize(x, y, workArea);\n\t\treturn screen.of(p);\n\t}\n}\n\n/// <summary>\n/// Used for parameters of functions that need a window handle as <see cref=\"wnd\"/> but also accept a WPF window/element, winforms form/control and <c>IntPtr</c>.\n/// </summary>\n/// <remarks>\n/// Has implicit conversions from:\n/// <br/>• <see cref=\"System.Windows.DependencyObject\"/> - WPF window or element.\n/// <br/>• <see cref=\"System.Windows.Forms.Control\"/> - <c>Form</c> or control.\n/// <br/>• <c>IntPtr</c> or <c>nint</c> - window handle.\n/// </remarks>\npublic struct AnyWnd {\n\treadonly object _o;\n\tAnyWnd(object o) { _o = o; }\n\t\n\t/// <summary>Assignment of a value of type <see cref=\"wnd\"/>.</summary>\n\tpublic static implicit operator AnyWnd(wnd w) => new(w);\n\t\n\t/// <summary>Assignment of a window handle as <c>IntPtr</c>.</summary>\n\tpublic static implicit operator AnyWnd(IntPtr hwnd) => new((wnd)hwnd);\n\t\n\t/// <summary>Assignment of a value of type <see cref=\"System.Windows.Forms.Control\"/> (<c>Form</c> or any control class).</summary>\n\tpublic static implicit operator AnyWnd(System.Windows.Forms.Control c) => new(c);\n\t\n\t/// <summary>Assignment of a value of type <c>System.Windows.DependencyObject</c> (WPF window or control).</summary>\n\tpublic static implicit operator AnyWnd(System.Windows.DependencyObject c) => c != null ? new AnyWnd(new object[] { c }) : default;\n\t\n\t/// <summary>\n\t/// Gets the window or control handle as <see cref=\"wnd\"/>.\n\t/// </summary>\n\t/// <value><c>default(wnd)</c> if not assigned.</value>\n\tpublic wnd Hwnd => wnd.Internal_.FromObject(_o);\n\t\n\t/// <summary>\n\t/// <c>true</c> if this is <c>default(AnyWnd)</c>.\n\t/// </summary>\n\tpublic bool IsEmpty => _o == null;\n}\n\n/// <summary>\n/// Used for function parameters to specify multiple strings.\n/// Contains a string like <c>\"One|Two|Three\"</c> or <c>string[]</c> or <c>List&lt;string&gt;</c>. Has implicit conversions from these types. Can be assigned collection initializer like <c>[\"a\", \"b\"]</c>.\n/// </summary>\n[CollectionBuilder(typeof(Strings), \"Create\")]\npublic struct Strings : IEnumerable<string> {\n\treadonly object _o;\n\tStrings(object o) { _o = o; }\n\t\n\t///\n\tpublic Strings(params string[] a) { _o = a; }\n\t\n\t///\n\tpublic static implicit operator Strings(string s) => new((object)s);\n\t\n\t///\n\tpublic static implicit operator Strings(string[] e) => new(e);\n\t\n\t///\n\tpublic static implicit operator Strings(List<string> e) => new(e);\n\t\n\t/// <summary>\n\t/// The raw value.\n\t/// </summary>\n\tpublic object Value => _o;\n\t\n\t/// <summary>\n\t/// Converts the value to <c>string[]</c>.\n\t/// Note: don't modify array elements. If the caller passed an array, this function returns it, not a copy.\n\t/// </summary>\n\tpublic string[] ToArray() {\n\t\treturn _o switch {\n\t\t\tstring[] a => a,\n\t\t\tstring s => s.Split('|'),\n\t\t\tList<string> a => a.ToArray(),\n\t\t\t_ => [], //null\n\t\t};\n\t}\n\t\n#region support collection expression\n\t\n\t//IEnumerable<string>\n\t\n\tIEnumerator<string> IEnumerable<string>.GetEnumerator() => ToArray().AsEnumerable().GetEnumerator();\n\t\n\t//IEnumerable\n\t\n\tIEnumerator IEnumerable.GetEnumerator() => ToArray().GetEnumerator();\n\t\n\t/// <summary>\n\t/// Returns <c>new(span.ToArray())</c>.\n\t/// </summary>\n\tpublic static Strings Create(ReadOnlySpan<string> span) => new(span.ToArray());\n\t\n#endregion\n}\n\n/// <summary>\n/// Font name, size and style.\n/// If <c>Name</c> not set, will be used standard GUI font; then <c>Size</c> can be 0 to use size of standard GUI font.\n/// On high-DPI screen the font size will be scaled.\n/// </summary>\npublic record class FontNSS(int Size = 0, string Name = null, bool Bold = false, bool Italic = false) {\n\t/// <summary>\n\t/// Creates font.\n\t/// </summary>\n\t/// <param name=\"dpi\">DPI for scaling.</param>\n\tinternal NativeFont_ CreateFont(DpiOf dpi) {\n\t\tif (Name == null) return new(dpi, Bold, Italic, Size);\n\t\treturn new(dpi, Name, Size, Bold, Italic);\n\t}\n}\n"
  },
  {
    "path": "Au/Au.Types/structs.cs",
    "content": "using System.Drawing;\nusing System.Text.Json.Serialization;\n\nnamespace Au.Types {\n\t/// <summary>\n\t/// Point coordinates x y.\n\t/// </summary>\n\tpublic record struct POINT {\n#pragma warning disable 1591, 3008 //XML doc, CLS-compliant\n\t\t[JsonInclude]\n\t\tpublic int x, y;\n\t\t\n\t\tpublic POINT(int x, int y) { this.x = x; this.y = y; }\n\t\t\n\t\tpublic static implicit operator POINT((int x, int y) t) => new(t.x, t.y);\n\t\t\n\t\tpublic static implicit operator POINT(Point p) => new(p.X, p.Y);\n\t\t\n\t\tpublic static implicit operator Point(POINT p) => new(p.x, p.y);\n\t\t\n\t\tpublic static implicit operator PointF(POINT p) => new(p.x, p.y);\n\t\t\n\t\tpublic static implicit operator System.Windows.Point(POINT p) => new(p.x, p.y);\n\t\t\n\t\t/// <param name=\"p\"></param>\n\t\t/// <param name=\"round\">Can round up, for example 1.7 to 2.</param>\n\t\t/// <exception cref=\"OverflowException\"></exception>\n\t\tpublic static POINT From(PointF p, bool round)\n\t\t\t=> new(round ? p.X.ToInt() : checked((int)p.X), round ? p.Y.ToInt() : checked((int)p.Y));\n\t\t\n\t\t/// <param name=\"p\"></param>\n\t\t/// <param name=\"round\">Can round up, for example 1.7 to 2.</param>\n\t\t/// <exception cref=\"OverflowException\"></exception>\n\t\tpublic static POINT From(System.Windows.Point p, bool round)\n\t\t\t=> new(round ? p.X.ToInt() : checked((int)p.X), round ? p.Y.ToInt() : checked((int)p.Y));\n\t\t\n\t\t//rejected\n\t\t///// <summary>Specifies position relative to the primary screen or its work area. Calls <see cref=\"Coord.Normalize\"/> with <i>centerIfEmpty</i> <c>true</c>.</summary>\n\t\t//public static implicit operator POINT((Coord x, Coord y, bool workArea) t) => _Coord(t.x, t.y, t.workArea, default);\n\t\t///// <summary>Specifies position relative to the specified screen or its work area. Calls <see cref=\"Coord.Normalize\"/> with <i>centerIfEmpty</i> <c>true</c>.</summary>\n\t\t//public static implicit operator POINT((Coord x, Coord y, screen screen, bool workArea) t) => _Coord(t.x, t.y, t.workArea, t.screen);\n\t\t///// <summary>Specifies position in the specified rectangle which is relative to the primary screen. Calls <see cref=\"Coord.NormalizeInRect\"/> with <i>centerIfEmpty</i> <c>true</c>.</summary>\n\t\t//public static implicit operator POINT((RECT r, Coord x, Coord y) t) => Coord.NormalizeInRect(t.x, t.y, t.r, centerIfEmpty: true);\n\t\t//static POINT _Coord(Coord x, Coord y, bool workArea, screen screen) => Coord.Normalize(x, y, workArea, screen, centerIfEmpty: true);\n\t\t\n\t\t//maybe in the future\n\t\t///// <summary>\n\t\t///// Converts <see cref=\"Coord\"/> coordinates into real coordinates.\n\t\t///// Calls <see cref=\"Coord.Normalize\"/> with <i>centerIfEmpty</i> <c>true</c>.\n\t\t///// </summary>\n\t\t//public static POINT Normalize(Coord x, Coord y, bool workArea = false, screen screen = default)\n\t\t//\t=> Coord.Normalize(x, y, workArea, screen, centerIfEmpty: true);\n\t\t\n\t\t//public static POINT NormalizeIn(RECT r, Coord x = default, Coord y = default)\n\t\t//\t=> Coord.NormalizeInRect(x, y, r, centerIfEmpty: true);\n\t\t\n\t\t/// <summary><c>this.x += x; this.y += y;</c></summary>\n\t\tpublic void Offset(int x, int y) { this.x += x; this.y += y; }\n\t\t\n\t\t/// <summary>Returns <c>new POINT(p.x + d.x, p.y + d.y)</c>.</summary>\n\t\tpublic static POINT operator +(POINT p, (int x, int y) d) => new(p.x + d.x, p.y + d.y);\n\t\t\n\t\tpublic void Deconstruct(out int x, out int y) { x = this.x; y = this.y; }\n\t\t\n\t\tpublic override string ToString() => $\"{{{x.ToS()}, {y.ToS()}}}\";\n#pragma warning restore 1591 //XML doc\n\t}\n\t\n\t/// <summary>\n\t/// Width and height.\n\t/// </summary>\n\tpublic record struct SIZE {\n#pragma warning disable 1591, 3008 //XML doc, CLS-compliant\n\t\t[JsonInclude]\n\t\tpublic int width, height;\n\t\t\n\t\tpublic SIZE(int width, int height) { this.width = width; this.height = height; }\n\t\t\n\t\tpublic static implicit operator SIZE((int width, int height) t) => new(t.width, t.height);\n\t\t\n\t\tpublic static implicit operator SIZE(Size z) => new(z.Width, z.Height);\n\t\t\n\t\tpublic static implicit operator Size(SIZE z) => new(z.width, z.height);\n\t\t\n\t\tpublic static implicit operator SizeF(SIZE z) => new(z.width, z.height);\n\t\t\n\t\tpublic static implicit operator System.Windows.Size(SIZE z) => new(z.width, z.height);\n\t\t\n\t\t/// <param name=\"z\"></param>\n\t\t/// <param name=\"round\">Can round up, for example 1.7 to 2.</param>\n\t\t/// <exception cref=\"OverflowException\"></exception>\n\t\tpublic static SIZE From(SizeF z, bool round)\n\t\t\t=> new(round ? z.Width.ToInt() : checked((int)z.Width), round ? z.Height.ToInt() : checked((int)z.Height));\n\t\t\n\t\t/// <param name=\"z\"></param>\n\t\t/// <param name=\"round\">Can round up, for example 1.7 to 2.</param>\n\t\t/// <exception cref=\"OverflowException\"></exception>\n\t\tpublic static SIZE From(System.Windows.Size z, bool round)\n\t\t\t=> new(round ? z.Width.ToInt() : checked((int)z.Width), round ? z.Height.ToInt() : checked((int)z.Height));\n\t\t\n\t\t/// <summary>Returns <c>new SIZE(z.width + d.x, z.height + d.y)</c>.</summary>\n\t\tpublic static SIZE operator +(SIZE z, (int x, int y) d) => new(z.width + d.x, z.height + d.y);\n\t\t\n\t\tpublic void Deconstruct(out int width, out int height) { width = this.width; height = this.height; }\n\t\t\n\t\tpublic override string ToString() => $\"{{{width.ToS()}, {height.ToS()}}}\";\n#pragma warning restore 1591 //XML doc\n\t}\n\t\n\t/// <summary>\n\t/// Rectangle coordinates left top right bottom.\n\t/// </summary>\n\t/// <remarks>\n\t/// This type can be used with Windows API functions. The .NET <c>Rectangle</c> etc can't, because their fields are different.\n\t/// Has conversions from/to <see cref=\"Rectangle\"/>.\n\t/// </remarks>\n\tpublic record struct RECT {\n#pragma warning disable 1591, 3008 //XML doc, CLS-compliant\n\t\t[JsonInclude]\n\t\tpublic int left, top;\n\t\tpublic int right, bottom;\n\t\t\n\t\t/// <summary>\n\t\t/// Sets all fields.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Sets <c>right = left + width; bottom = top + height;</c>. To specify right/bottom instead of width/height, use <see cref=\"FromLTRB\"/> instead.\n\t\t/// </remarks>\n\t\t[JsonConstructor] //without it JSON deserializer sets incorrect Width/Height because does it before setting left/right\n\t\tpublic RECT(int left, int top, int width, int height) {\n\t\t\tthis.left = left; this.top = top;\n\t\t\tright = left + width; bottom = top + height;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Creates <see cref=\"RECT\"/> with specified <c>left</c>, <c>top</c>, <c>right</c> and <c>bottom</c>.\n\t\t/// </summary>\n\t\tpublic static RECT FromLTRB(int left, int top, int right, int bottom)\n\t\t\t=> new() { left = left, top = top, right = right, bottom = bottom };\n\t\t\n\t\t/// <summary>\n\t\t/// Converts from tuple (left, top, width, height).\n\t\t/// </summary>\n\t\tpublic static implicit operator RECT((int L, int T, int W, int H) t) => new(t.L, t.T, t.W, t.H);\n\t\t\n\t\tpublic static implicit operator RECT(Rectangle r) => new(r.Left, r.Top, r.Width, r.Height);\n\t\t\n\t\tpublic static implicit operator Rectangle(RECT r) => new(r.left, r.top, r.Width, r.Height);\n\t\t\n\t\tpublic static implicit operator RectangleF(RECT r) => new(r.left, r.top, r.Width, r.Height);\n\t\t\n\t\tpublic static implicit operator System.Windows.Rect(RECT r) => new(r.left, r.top, r.Width, r.Height);\n\t\t\n\t\t/// <param name=\"r\"></param>\n\t\t/// <param name=\"round\">Can round up, for example 1.7 to 2.</param>\n\t\t/// <exception cref=\"OverflowException\"></exception>\n\t\tpublic static RECT From(RectangleF r, bool round) {\n\t\t\tif (round) return new(r.Left.ToInt(), r.Top.ToInt(), r.Width.ToInt(), r.Height.ToInt());\n\t\t\tchecked { return new((int)r.Left, (int)r.Top, (int)r.Width, (int)r.Height); }\n\t\t}\n\t\t\n\t\t/// <param name=\"r\"></param>\n\t\t/// <param name=\"round\">Can round up, for example 1.7 to 2.</param>\n\t\t/// <exception cref=\"OverflowException\"></exception>\n\t\tpublic static RECT From(System.Windows.Rect r, bool round) {\n\t\t\tif (round) return new(r.Left.ToInt(), r.Top.ToInt(), r.Width.ToInt(), r.Height.ToInt());\n\t\t\tchecked { return new((int)r.Left, (int)r.Top, (int)r.Width, (int)r.Height); }\n\t\t}\n\t\t\n\t\t//rejected. Rare.\n\t\t///// <summary>\n\t\t///// Sets fields like constructor <see cref=\"RECT(int,int,int,int)\"/>.\n\t\t///// </summary>\n\t\t//public void Set(int left, int top, int rightOrWidth, int bottomOrHeight, bool useWidthHeight = true) {\n\t\t//\tthis.left = left; this.top = top;\n\t\t//\tright = rightOrWidth; bottom = bottomOrHeight;\n\t\t//\tif (useWidthHeight) { right += left; bottom += top; }\n\t\t//}\n\t\t\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if all fields == 0.\n\t\t/// </summary>\n\t\tpublic bool Is0 => left == 0 && top == 0 && right == 0 && bottom == 0;\n\t\t\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if the rectangle area is empty or invalid: <c>right&lt;=left || bottom&lt;=top;</c>\n\t\t/// </summary>\n\t\tpublic bool NoArea => right <= left || bottom <= top;\n\t\t\n\t\t/// <summary>\n\t\t/// Gets or sets width.\n\t\t/// </summary>\n\t\tpublic int Width { get => right - left; set { right = left + value; } }\n\t\t\n\t\t/// <summary>\n\t\t/// Gets or sets height.\n\t\t/// </summary>\n\t\tpublic int Height { get => bottom - top; set { bottom = top + value; } }\n\t\t\n\t\t/// <summary>\n\t\t/// Returns <c>new POINT(left, top)</c>.\n\t\t/// </summary>\n\t\tpublic POINT XY => new(left, top);\n\t\t\n\t\t/// <summary>\n\t\t/// Returns <c>new SIZE(Width, Height)</c>.\n\t\t/// </summary>\n\t\tpublic SIZE Size => new(Width, Height);\n\t\t\n\t\t/// <summary>\n\t\t/// Gets horizontal center.\n\t\t/// </summary>\n\t\tpublic int CenterX => left + (right - left) / 2;\n\t\t//public int CenterX => (int)(((long)left + right) / 2);\n\t\t\n\t\t/// <summary>\n\t\t/// Gets vertical center.\n\t\t/// </summary>\n\t\tpublic int CenterY => top + (bottom - top) / 2;\n\t\t//public int CenterY => (int)(((long)top + bottom) / 2);\n\n\t\t/// <summary>\n\t\t/// Returns width multiplied by height: <c>Math.Abs((long)Width * Height)</c>.\n\t\t/// </summary>\n\t\tinternal long Area_ => Math.Abs((long)Width * Height);\n\t\t\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if this rectangle contains the specified point.\n\t\t/// </summary>\n\t\tpublic bool Contains(int x, int y) => x >= left && x < right && y >= top && y < bottom;\n\t\t\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if this rectangle contains the specified point.\n\t\t/// </summary>\n\t\tpublic bool Contains(POINT p) => Contains(p.x, p.y);\n\t\t\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if this rectangle contains entire specified rectangle.\n\t\t/// </summary>\n\t\tpublic bool Contains(RECT r2) => r2.left >= left && r2.top >= top && r2.right <= right && r2.bottom <= bottom;\n\t\t\n\t\t/// <summary>\n\t\t/// Makes this rectangle bigger or smaller: <c>left-=dx; right+=dx; top-=dy; bottom+=dy;</c>\n\t\t/// Use negative <i>dx</i>/<i>dy</i> to make the rectangle smaller. Note: too big negative <i>dx</i>/<i>dy</i> can make it invalid (<c>right &lt; left</c> or <c>bottom &lt; top</c>).\n\t\t/// </summary>\n\t\tpublic void Inflate(int dx, int dy) { left -= dx; right += dx; top -= dy; bottom += dy; }\n\t\t\n\t\t/// <summary>\n\t\t/// Replaces this rectangle with the intersection of itself and the specified rectangle.\n\t\t/// If the rectangles don't intersect, makes this variable empty.\n\t\t/// </summary>\n\t\t/// <returns><c>true</c> if the rectangles intersect.</returns>\n\t\tpublic bool Intersect(RECT r2) => Api.IntersectRect(out this, this, r2);\n\t\t\n\t\t/// <summary>\n\t\t/// Returns the intersection rectangle of two rectangles.\n\t\t/// If they don't intersect, returns empty rectangle.\n\t\t/// </summary>\n\t\tpublic static RECT Intersect(RECT r1, RECT r2) { Api.IntersectRect(out RECT r, r1, r2); return r; }\n\t\t\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if this rectangle and another rectangle intersect.\n\t\t/// </summary>\n\t\tpublic bool IntersectsWith(RECT r2) => Api.IntersectRect(out _, this, r2);\n\t\t\n\t\t/// <summary>\n\t\t/// Moves this rectangle by the specified offsets: <c>left+=dx; right+=dx; top+=dy; bottom+=dy;</c>\n\t\t/// Negative <i>dx</i> moves to the left. Negative <i>dy</i> moves up.\n\t\t/// </summary>\n\t\tpublic void Offset(int dx, int dy) { left += dx; right += dx; top += dy; bottom += dy; }\n\t\t\n\t\t/// <summary>\n\t\t/// Moves this rectangle so that <c>left</c>=<i>x</i> and <c>right</c>=<i>y</i>. Does not change <see cref=\"Width\"/> and <see cref=\"Height\"/>.\n\t\t/// </summary>\n\t\tpublic void Move(int x, int y) => Offset(x - left, y - top);\n\t\t\n\t\t/// <summary>\n\t\t/// Replaces this rectangle with the union of itself and the specified rectangle.\n\t\t/// Union is the smallest rectangle that contains two full rectangles.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// If either rectangle is empty (<see cref=\"Width\"/> or <see cref=\"Height\"/> is &lt;=0), the result is another rectangle. If both empty - empty rectangle.\n\t\t/// </remarks>\n\t\t/// <returns><c>true</c> if finally this rectangle is not empty.</returns>\n\t\tpublic bool Union(RECT r2) => Api.UnionRect(out this, this, r2);\n\t\t\n\t\t/// <summary>\n\t\t/// Returns the union of two rectangles.\n\t\t/// Union is the smallest rectangle that contains two full rectangles.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// If either rectangle is empty (<see cref=\"Width\"/> or <see cref=\"Height\"/> is &lt;=0), the result is another rectangle. If both empty - empty rectangle.\n\t\t/// </remarks>\n\t\tpublic static RECT Union(RECT r1, RECT r2) { Api.UnionRect(out RECT r, r1, r2); return r; }\n\t\t\n\t\t/// <summary>\n\t\t/// If <c>width</c> or <c>height</c> are negative, modifies this rectangle so that they would not be negative.\n\t\t/// </summary>\n\t\t/// <param name=\"swap\"><c>true</c> - swap <c>right</c>/<c>left</c>, <c>bottom</c>/<c>top</c>; <c>false</c> - set <c>right</c> = <c>left</c>, <c>bottom</c> = <c>top</c>.</param>\n\t\tpublic void Normalize(bool swap) {\n\t\t\tif (right < left) { if (swap) Math2.Swap(ref left, ref right); else right = left; }\n\t\t\tif (bottom < top) { if (swap) Math2.Swap(ref top, ref bottom); else bottom = top; }\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Moves this rectangle to the specified coordinates in the specified screen, and ensures that whole rectangle is in screen.\n\t\t/// Final rectangle coordinates are relative to the primary screen.\n\t\t/// </summary>\n\t\t/// <param name=\"x\">X coordinate in the specified screen. If <c>default</c> - center. Examples: <c>10</c>, <c>^10</c> (reverse), <c>.5f</c> (fraction).</param>\n\t\t/// <param name=\"y\">Y coordinate in the specified screen. If <c>default</c> - center.</param>\n\t\t/// <param name=\"screen\">Use this screen. If <c>default</c>, uses the primary screen. Example: <c>screen.index(1)</c>.</param>\n\t\t/// <param name=\"workArea\">Use the work area, not whole screen. Default <c>true</c>.</param>\n\t\t/// <param name=\"ensureInScreen\">If part of rectangle is not in screen, move and/or resize it so that entire rectangle would be in screen. Default <c>true</c>.</param>\n\t\t/// <remarks>\n\t\t/// This function can be used to calculate new window location before creating it. If window already exists, use <see cref=\"wnd.MoveInScreen\"/>.\n\t\t/// </remarks>\n\t\tpublic void MoveInScreen(Coord x, Coord y, screen screen = default, bool workArea = true, bool ensureInScreen = true) {\n\t\t\twnd.Internal_.MoveRectInScreen(false, ref this, x, y, screen, workArea, ensureInScreen);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Moves this rectangle to the specified coordinates in another rectangle <i>r</i>.\n\t\t/// </summary>\n\t\t/// <param name=\"r\">Another rectangle.</param>\n\t\t/// <param name=\"x\">X coordinate relative to <i>r</i>. Default - center. Examples: <c>10</c>, <c>^10</c> (reverse), <c>.5f</c> (fraction).</param>\n\t\t/// <param name=\"y\">Y coordinate relative to <i>r</i>. Default - center.</param>\n\t\t/// <param name=\"ensureInRect\">If part of rectangle is not in <i>r</i>, move and/or resize it so that entire rectangle would be in <i>r</i>.</param>\n\t\tpublic void MoveInRect(RECT r, Coord x = default, Coord y = default, bool ensureInRect = false) {\n\t\t\twnd.Internal_.MoveRectInRect(ref this, x, y, r, ensureInRect);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Adjusts this rectangle to ensure that whole rectangle is in screen.\n\t\t/// Initial and final rectangle coordinates are relative to the primary screen.\n\t\t/// </summary>\n\t\t/// <param name=\"screen\">Use this screen (see <see cref=\"screen\"/>). If <c>default</c>, uses screen of the rectangle (or nearest).</param>\n\t\t/// <param name=\"workArea\">Use the work area, not whole screen. Default <c>true</c>.</param>\n\t\t/// <remarks>\n\t\t/// This function can be used to calculate new window location before creating it. If window already exists, use <see cref=\"wnd.EnsureInScreen\"/>.\n\t\t/// </remarks>\n\t\tpublic void EnsureInScreen(screen screen = default, bool workArea = true) {\n\t\t\twnd.Internal_.MoveRectInScreen(true, ref this, default, default, screen, workArea, true);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if all fields of <i>r1</i> and <i>r2</i> are equal or almost equal with max difference +- <i>maxDiff</i>.\n\t\t/// </summary>\n\t\tinternal static bool EqualFuzzy_(RECT r1, RECT r2, int maxDiff) {\n\t\t\tif (Math.Abs(r1.left - r2.left) > maxDiff) return false;\n\t\t\tif (Math.Abs(r1.top - r2.top) > maxDiff) return false;\n\t\t\tif (Math.Abs(r1.right - r2.right) > maxDiff) return false;\n\t\t\tif (Math.Abs(r1.bottom - r2.bottom) > maxDiff) return false;\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Converts to string <c>\"{L=left T=top W=width H=height}\"</c>.\n\t\t/// </summary>\n\t\t/// <seealso cref=\"TryParse\"/>\n\t\tpublic override string ToString() {\n\t\t\treturn $\"{{L={left.ToS()} T={top.ToS()} W={Width.ToS()} H={Height.ToS()}}}\";\n\t\t\t//note: don't change the format. Some functions parse it, eg TryParse and acc in C++.\n\t\t\t\n\t\t\t//don't need R B. Rarely useful, just makes more difficult to read W H.\n\t\t\t//return $\"{{L={left} T={top} R={right} B={bottom}  W={Width} H={Height}}}\";\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Converts to string <c>\"left top width height\"</c>.\n\t\t/// </summary>\n\t\t/// <seealso cref=\"TryParse\"/>\n\t\tpublic string ToStringSimple() {\n\t\t\treturn $\"{left.ToS()} {top.ToS()} {Width.ToS()} {Height.ToS()}\";\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Formats string from <see cref=\"RECT\"/> main fields and properties.\n\t\t/// </summary>\n\t\t/// <param name=\"format\">\n\t\t/// <see cref=\"StringBuilder.AppendFormat\"/> format string. Example: <c>\"({0}, {1}, {4}, {5})\"</c>.\n\t\t/// This function passes to <c>AppendFormat</c> 6 values in this order: <c>left</c>, <c>top</c>, <c>right</c>, <c>bottom</c>, <c>Width</c>, <c>Height</c>.\n\t\t/// </param>\n\t\tpublic string ToStringFormat(string format) {\n\t\t\tusing (new StringBuilder_(out var b)) {\n\t\t\t\tb.AppendFormat(format, left.ToS(), top.ToS(), right.ToS(), bottom.ToS(), Width.ToS(), Height.ToS());\n\t\t\t\treturn b.ToString();\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Converts string to <see cref=\"RECT\"/>.\n\t\t/// </summary>\n\t\t/// <returns><c>false</c> if invalid string format.</returns>\n\t\t/// <param name=\"s\">String in format <c>\"{L=left T=top W=width H=height}\"</c> (<see cref=\"ToString\"/>) or <c>\"left top width height\"</c> (<see cref=\"ToStringSimple\"/>).</param>\n\t\t/// <param name=\"r\"></param>\n\t\tpublic static bool TryParse(string s, out RECT r) {\n\t\t\tr = default;\n\t\t\tbool ok;\n\t\t\tif (s.Starts('{')) {\n\t\t\t\tok = s.Eq(1, \"L=\") && s.ToInt(out r.left, 3, out int e)\n\t\t\t\t\t&& s.Eq(e, \" T=\") && s.ToInt(out r.top, e + 3, out e)\n\t\t\t\t\t&& s.Eq(e, \" W=\") && s.ToInt(out r.right, e + 3, out e)\n\t\t\t\t\t&& s.Eq(e, \" H=\") && s.ToInt(out r.bottom, e + 3, out e)\n\t\t\t\t\t&& s.Length == e + 1 && s[e] == '}';\n\t\t\t\t//tested: regex @\"^\\{L=(-?\\d+) T=(-?\\d+) W=(-?\\d+) H=(-?\\d+)\\}$\" 9 times slower.\n\t\t\t} else {\n\t\t\t\tok = s.ToInt(out r.left, 0, out int e) && s.ToInt(out r.top, e, out e) && s.ToInt(out r.right, e, out e) && s.ToInt(out r.bottom, e);\n\t\t\t}\n\t\t\tif (ok) {\n\t\t\t\tr.right += r.left;\n\t\t\t\tr.bottom += r.top;\n\t\t\t}\n\t\t\treturn ok;\n\t\t}\n#pragma warning restore 1591 //XML doc\n\t}\n\n\t/// <summary>\n\t/// Struct with fields <c>int start</c> and <c>int end</c>.\n\t/// </summary>\n\tpublic record struct StartEnd {\n\t\t///\n\t\tpublic int start;\n\t\t///\n\t\tpublic int end;\n\n\t\t///\n\t\tpublic StartEnd(int start, int end) { this.start = start; this.end = end; }\n\n\t\t/// <summary>\n\t\t/// Returns <c>end - start</c>.\n\t\t/// </summary>\n\t\tpublic int Length => end - start;\n\n\t\t/// <summary>\n\t\t/// Converts this to <see cref=\"Range\"/>.\n\t\t/// Can be used to get substring, like <c>s[x.Range]</c> instead of <c>s[x.start..x.end]</c>.\n\t\t/// </summary>\n\t\tpublic Range Range => start..end;\n\n\t\t///\n\t\tpublic static implicit operator Range(StartEnd s) => s.start..s.end;\n\t\t\n\t\t/// <exception cref=\"ArgumentException\">The start or end of the range is from the end.</exception>\n\t\tpublic static implicit operator StartEnd(Range r) {\n\t\t\tif (r.Start.IsFromEnd || r.End.IsFromEnd) throw new ArgumentException();\n\t\t\treturn new(r.Start.Value, r.End.Value);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets string span.\n\t\t/// </summary>\n\t\t[Obsolete, EditorBrowsable(EditorBrowsableState.Never)] //rarely used; easy with string.AsSpan; precedes `start` in intellisense.\n\t\tpublic RStr Span(string s) => s.AsSpan(start, end - start);\n\n\t\t///\n\t\tpublic override string ToString() => $\"({start}, {end})\";\n\t}\n\t\n\tinternal unsafe struct VARIANT : IDisposable {\n\t\tpublic Api.VARENUM vt; //ushort\n\t\tushort _u1;\n\t\tuint _u2;\n\t\tpublic nint value;\n\t\tpublic nint value2;\n\t\t//note: cannot use FieldOffset because of different 32/64 bit size\n\t\t\n\t\tpublic VARIANT(int x) { vt = Api.VARENUM.VT_I4; value = x; }\n\t\tpublic VARIANT(string x) { vt = Api.VARENUM.VT_BSTR; value = Marshal.StringToBSTR(x); }\n\t\t\n\t\tpublic static implicit operator VARIANT(int x) => new(x);\n\t\tpublic static implicit operator VARIANT(string x) => new(x);\n\t\t\n\t\tpublic int ValueInt { get { Debug.Assert(vt == Api.VARENUM.VT_I4); return (int)value; } }\n\t\tpublic BSTR ValueBstr { get { Debug.Assert(vt == Api.VARENUM.VT_BSTR); return BSTR.AttachBSTR((char*)value); } }\n\t\t\n\t\t/// <summary>\n\t\t/// Calls <ms>VariantClear</ms>.\n\t\t/// </summary>\n\t\tpublic void Dispose() {\n\t\t\t_Clear();\n\t\t}\n\t\t\n\t\tvoid _Clear() {\n\t\t\tif (vt >= Api.VARENUM.VT_BSTR) Api.VariantClear(ref this);\n\t\t\telse vt = 0; //info: VariantClear just sets vt=0 and does not clear other members\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Converts to string.\n\t\t/// </summary>\n\t\tpublic override string ToString() {\n\t\t\treturn _ToString();\n\t\t}\n\t\t\n\t\tstring _ToString() {\n\t\t\tswitch (vt) {\n\t\t\tcase Api.VARENUM.VT_BSTR: return value == default ? null : ValueBstr.ToString();\n\t\t\tcase Api.VARENUM.VT_I4: return value.ToString();\n\t\t\tcase 0: case Api.VARENUM.VT_NULL: return null;\n\t\t\t}\n\t\t\tVARIANT v2 = default;\n\t\t\tuint lcid = 0x409; //invariant\n\t\t\tswitch (vt & (Api.VARENUM)0xff) { case Api.VARENUM.VT_DATE: case Api.VARENUM.VT_DISPATCH: lcid = 0x400; break; } //LOCALE_USER_DEFAULT\n\t\t\tif (0 != Api.VariantChangeTypeEx(ref v2, this, lcid, 2, Api.VARENUM.VT_BSTR)) return null; //2 VARIANT_ALPHABOOL\n\t\t\treturn v2.value == default ? null : v2.ValueBstr.ToStringAndDispose();\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Converts to string.\n\t\t/// Disposes this <c>VARIANT</c>.\n\t\t/// </summary>\n\t\tpublic string ToStringAndDispose() {\n\t\t\tvar r = _ToString();\n\t\t\tDispose();\n\t\t\treturn r;\n\t\t}\n\t}\n\t\n\tinternal unsafe struct BSTR : IDisposable {\n\t\tchar* _p;\n\t\t\n\t\tBSTR(char* p) => _p = p;\n\t\t\n\t\tpublic static explicit operator BSTR(string s) => new((char*)Marshal.StringToBSTR(s));\n\t\tpublic static explicit operator nint(BSTR b) => (nint)b._p;\n\t\t\n\t\tpublic static BSTR AttachBSTR(char* bstr) => new(bstr);\n\t\t\n\t\tpublic static BSTR CopyFrom(char* anyString) => anyString == null ? default : Api.SysAllocString(anyString);\n\t\t\n\t\tpublic static BSTR Alloc(int len) => Api.SysAllocStringLen(null, len);\n\t\t\n\t\tpublic char* Ptr => _p;\n\t\t\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if the string is <c>null</c>.\n\t\t/// </summary>\n\t\tpublic bool Is0 => _p == null;\n\t\t\n\t\tpublic int Length => _p == null ? 0 : Api.SysStringLen(_p);\n\t\t\n\t\t/// <summary>\n\t\t/// Unsafe.\n\t\t/// </summary>\n\t\tpublic char this[int i] => _p[i];\n\t\t\n\t\t/// <summary>\n\t\t/// Converts to string.\n\t\t/// Does not dispose.\n\t\t/// </summary>\n\t\tpublic override string ToString() {\n\t\t\tvar p = _p; if (p == null) return null;\n\t\t\treturn Marshal.PtrToStringBSTR((IntPtr)_p);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Converts to string and disposes.\n\t\t/// </summary>\n\t\tpublic string ToStringAndDispose() {\n\t\t\tvar p = _p; if (p == null) return null;\n\t\t\tint len = Api.SysStringLen(p);\n\t\t\t\n\t\t\t//rejected:\n\t\t\t//Some objects can return BSTR containing '\\0's. Then probably the rest of string is garbage. I never noticed this but saw comments. Better allow '\\0's, because in some cases it can be valid string. When invalid, it will not harm too much.\n\t\t\t//int len2 = Ptr_.Length(p, len); Debug_.PrintIf(len2 != len, \"BSTR with '\\\\0'\"); len = len2;\n\t\t\t\n\t\t\tstring r = len == 0 ? \"\" : new string(p, 0, len);\n\t\t\tDispose();\n\t\t\treturn r;\n\t\t}\n\t\t\n\t\tpublic void Dispose() {\n\t\t\tvar t = _p;\n\t\t\tif (t != null) {\n\t\t\t\t_p = null;\n\t\t\t\tApi.SysFreeString(t);\n\t\t\t}\n\t\t}\n\t}\n\t\n}\n"
  },
  {
    "path": "Au/Au.Types/unused/AuClassless.cs",
    "content": "﻿//rejected\n//namespace Au.Types\n//{\n//\t/// <summary>\n//\t/// Contains functions of this library that can be called without a class, like <c>Function()</c> instead of <c>Class.Function()</c>.\n//\t/// In file global.cs: <c>global using static Au.Types.AuClassless;</c>.\n//\t/// Currently empty. Reserved for the future.\n//\t/// </summary>\n//\tpublic static class AuClassless\n//\t{\n//\t\t//public static void TestClassless() { }\n//\t}\n//}\n"
  },
  {
    "path": "Au/Au.cs",
    "content": "/*/\nrole classLibrary;\ndefine IDE_LA,AU,NO_GLOBAL,NO_DEFAULT_CHARSET_UNICODE;\nnoWarnings 419,649;\npreBuild ..\\@Au.Editor\\_prePostBuild.cs;\noutputPath %folders.Workspace%\\dll;\nmiscFlags 1;\nnoRef *\\Au.dll;\nresource resources\\red_cross_cursor.cur /path;\n/*/\n\n//Using outputPath %folders.Workspace%\\dll; instead of outputPath %folders.Workspace%\\..\\Au.Editor; because the compiler would replace Au.dll with the older version.\n//\t_prePostBuild will copy Au.dll to Au.Editor.\n"
  },
  {
    "path": "Au/Au.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n\t<PropertyGroup>\n\t\t<TargetFramework>net10.0-windows</TargetFramework>\n\t\t<UseWindowsForms>true</UseWindowsForms>\n\t\t<UseWpf>true</UseWpf>\n\t\t<GenerateAssemblyInfo>false</GenerateAssemblyInfo>\n\t\t<AssemblyName>Au</AssemblyName>\n\t\t<RootNamespace>Au</RootNamespace>\n\t\t<SignAssembly>true</SignAssembly>\n\t\t<AssemblyOriginatorKeyFile>..\\Au.snk</AssemblyOriginatorKeyFile>\n\t\t<AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n\t\t<DocumentationFile>bin\\Au.xml</DocumentationFile>\n\t\t<NoWarn>419;8981</NoWarn>\n\t\t<LangVersion>preview</LangVersion>\n\t\t<NoDefaultLaunchSettingsFile>true</NoDefaultLaunchSettingsFile>\n\t\t<NoWin32Manifest>true</NoWin32Manifest>\n\t\t<ProduceReferenceAssembly>False</ProduceReferenceAssembly>\n\t\t<NuGetAudit>False</NuGetAudit>\n\t\t<Platforms>AnyCPU</Platforms>\n\t</PropertyGroup>\n\n\t<!-- NuGet package. -->\n\t<PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|AnyCPU'\">\n\t\t<Version>1.1.6</Version>\n\t\t<!--\n\t\tTo create NuGet package is used script \"Create NuGet package\".\n\t\tNotes if creating manually:\n\t\t\tUpdate <Version>.\n\t\t\tFor NuGet package need multiple target frameworks. But it makes compilation slow.\n\t\t\tWorkaround - temporarily use different TargetFrameworks when creating NuGet package.\n\t\t\tPossible problem: error when building after changing TargetFrameworks. Missing target framework in the assets json file.\n\t\t\t\tWorkaround: delete obj folder.\n\t\t\tPossible problem: VS may not build for multiple frameworks soon after editing this file.\n\t\t\t\tMake sure the output files have correct date. Build again if need.\n\t\t-->\n\t\t\n\t\t<PackageId>LibreAutomate</PackageId>\n\t\t<Title>LibreAutomate</Title>\n\t\t<Authors>didgeridoo</Authors>\n\t\t<Product>LibreAutomate</Product>\n\t\t<Description>LibreAutomate is an automation library for Windows. Mostly desktop and web UI automation. To get the most of it, install the LibreAutomate app.</Description>\n\t\t<Copyright>Copyright (c) Gintaras Didžgalvis 2025</Copyright>\n\t\t<PackageProjectUrl>https://www.libreautomate.com</PackageProjectUrl>\n\t\t<PackageIcon>Icon-128.png</PackageIcon>\n\t\t<PackageReadmeFile>NuGet.md</PackageReadmeFile>\n\t\t<RepositoryUrl>https://github.com/qgindi/LibreAutomate</RepositoryUrl>\n\t\t<RepositoryType>git</RepositoryType>\n\t\t<PackageTags>UI automation;automate;windows;desktop;web;UI;hotkey;autotext;trigger;toolbar;keys;mouse;keyboard;clipboard;send;task</PackageTags>\n\t\t<PackageLicenseExpression>MIT</PackageLicenseExpression>\n\t\t<GeneratePackageOnBuild>False</GeneratePackageOnBuild>\n\t</PropertyGroup>\n\t<PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|AnyCPU'\">\n\t  <DebugType>portable</DebugType>\n\t  <DefineConstants>$(DefineConstants);AU</DefineConstants>\n\t</PropertyGroup>\n\t<PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|AnyCPU'\">\n\t  <DebugType>portable</DebugType>\n\t  <DefineConstants>$(DefineConstants);AU</DefineConstants>\n\t</PropertyGroup>\n\n\t<ItemGroup>\n\t\t<None Remove=\"resources\\red_cross_cursor.cur\" />\n\t</ItemGroup>\n\n\t<ItemGroup>\n\t\t<Resource Include=\"resources\\red_cross_cursor.cur\" />\n\t</ItemGroup>\n\n\t<ItemGroup>\n\t\t<None Include=\"x\\Icon-128.png\" Pack=\"true\" PackagePath=\"\\\" />\n\t\t<None Include=\"x\\NuGet.md\" Pack=\"true\" PackagePath=\"\\\" />\n\t</ItemGroup>\n\n\t<ItemGroup>\n\t\t<Compile Remove=\"Au.cs\" />\n\t</ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "Au/Ext/Bitmap.Resize.cs",
    "content": "//The main code is from FreeImage sources.\n\nusing System.Drawing;\nusing System.Drawing.Imaging;\n\nnamespace Au.Types;\n\n/// <summary>\n/// Used with <see cref=\"ExtMisc.Resize\"/>\n/// </summary>\npublic enum BRFilter {\n\t/// <summary>Produces sharper image (less blurry) than <c>Graphics.DrawImage</c> with <c>InterpolationMode.HighQualityBicubic</c>.</summary>\n\tLanczos3,\n\n\t/// <summary>Produces slightly sharper image (less blurry) than <c>Graphics.DrawImage</c> with <c>InterpolationMode.HighQualityBicubic</c>.</summary>\n\tCatmullRom,\n\n\t/// <summary>Produces image similar to <c>Graphics.DrawImage</c> with <c>InterpolationMode.HighQualityBicubic</c>.</summary>\n\tBicubic\n}\n\npublic static partial class ExtMisc {\n\t/// <summary>\n\t/// Resizes this image.\n\t/// </summary>\n\t/// <returns>Resized image (new object). Returns this image if new width and height would be the same as of this image.</returns>\n\t/// <param name=\"b\"></param>\n\t/// <param name=\"width\">New width.</param>\n\t/// <param name=\"height\">New height. If <i>width</i> or <i>height</i> is 0, calculates it (preserves aspect ratio).</param>\n\t/// <param name=\"filter\"></param>\n\t/// <param name=\"dispose\">When resized, call <c>Dispose</c> for this object.</param>\n\t/// <param name=\"premultiplied\">\n\t/// Let the resized bitmap have <c>PixelFormat</c> = <c>Format32bppPArgb</c>. It prevents distortions at transparent-opaque boundaries.\n\t/// If <c>false</c>: if this bitmap has <c>Format32bppArgb</c> or <c>Format32bppPArgb</c>, does not change, else <c>PixelFormat.Format32bppArgb</c>.\n\t/// </param>\n\t/// <exception cref=\"ArgumentException\">Unsupported <c>PixelFormat</c>.</exception>\n\tpublic static unsafe Bitmap Resize(this Bitmap b, int width, int height, BRFilter filter, bool dispose, bool premultiplied = false) {\n\t\tint wid1 = b.Width, hei1 = b.Height;\n\t\tif (width < 1) width = Math.Max(1, Math2.MulDiv(wid1, height, hei1));\n\t\tif (height < 1) height = Math.Max(1, Math2.MulDiv(hei1, width, wid1));\n\t\tif (width == wid1 && height == hei1) return b;\n\n\t\tvar pf = b.PixelFormat;\n\t\tif (pf == PixelFormat.Format32bppPArgb) premultiplied = true;\n\t\telse if (premultiplied) pf = PixelFormat.Format32bppPArgb;\n\t\telse if(pf is not (PixelFormat.Format32bppArgb or PixelFormat.Format32bppRgb)) pf = PixelFormat.Format32bppArgb;\n\n\t\tvar r = new Bitmap(width, height, pf);\n\t\tvar d1 = b.LockBits(new(0, 0, wid1, hei1), ImageLockMode.ReadOnly, pf);\n\t\tvar d2 = r.LockBits(new(0, 0, width, height), ImageLockMode.ReadWrite, pf);\n\t\ttry {\n\t\t\t_FreeImage.Resize((byte*)d1.Scan0, wid1, hei1, (byte*)d2.Scan0, width, height, filter, premultiplied);\n\t\t}\n\t\tfinally {\n\t\t\tb.UnlockBits(d1);\n\t\t\tr.UnlockBits(d2);\n\t\t}\n\n\t\tif (dispose) b.Dispose();\n\t\treturn r;\n\t}\n\n\t/// <inheritdoc cref=\"Resize(Bitmap, int, int, BRFilter, bool, bool)\"/>\n\t/// <param name=\"factor\">Scaling factor. For example 2 to make 2 times bigger, or 0.5 to make 2 times smaller.</param>\n\tpublic static Bitmap Resize(this Bitmap b, double factor, BRFilter filter, bool dispose, bool premultiplied = false) {\n\t\tif (factor == 1) return b;\n\t\tvar z = b.Size;\n\t\tint wid = (z.Width * factor).ToInt(), hei = (z.Height * factor).ToInt();\n\t\tif (wid == z.Width && hei == z.Height) return b;\n\t\treturn Resize(b, wid, hei, filter, dispose, premultiplied);\n\t}\n\n\tstatic unsafe class _FreeImage {\n\t\tinternal static void Resize(byte* src, int srcWidth, int srcHeight, byte* dst, int dstWidth, int dstHeight, BRFilter filter, bool premultiplied) {\n\t\t\t_Filter filt = filter switch {\n\t\t\t\tBRFilter.Lanczos3 => new _FilterLanczos3(),\n\t\t\t\tBRFilter.CatmullRom => new _FilterCatmullRom(),\n\t\t\t\tBRFilter.Bicubic => new _FilterBicubic(),\n\t\t\t\t_ => null\n\t\t\t};\n\n\t\t\tbyte* tmp;\n\n\t\t\tif (dstWidth <= srcWidth) {\n\t\t\t\t// xy filtering\n\n\t\t\t\tif (srcWidth != dstWidth) {\n\t\t\t\t\t// source and destination widths are different so, we must\n\t\t\t\t\t// filter horizontally\n\t\t\t\t\tif (srcHeight != dstHeight) {\n\t\t\t\t\t\t// source and destination heights are also different so, we need\n\t\t\t\t\t\t// a temporary image\n\t\t\t\t\t\ttmp = MemoryUtil.Alloc(dstWidth * srcHeight * 4);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// source and destination heights are equal so, we can directly\n\t\t\t\t\t\t// factor into destination image (second filter method will not\n\t\t\t\t\t\t// be invoked)\n\t\t\t\t\t\ttmp = dst;\n\t\t\t\t\t}\n\n\t\t\t\t\t// factor source image horizontally into temporary (or destination) image\n\t\t\t\t\t_horizontalFilter(src, srcHeight, srcWidth, tmp, dstWidth, filt, premultiplied);\n\t\t\t\t} else {\n\t\t\t\t\t// source and destination widths are equal so, just copy the\n\t\t\t\t\t// image pointer\n\t\t\t\t\ttmp = src;\n\t\t\t\t}\n\n\t\t\t\tif (srcHeight != dstHeight) {\n\t\t\t\t\t// source and destination heights are different so, factor\n\t\t\t\t\t// temporary (or source) image vertically into destination image\n\t\t\t\t\t_verticalFilter(tmp, dstWidth, srcHeight, dst, dstHeight, filt, premultiplied);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// yx filtering\n\n\t\t\t\tif (srcHeight != dstHeight) {\n\t\t\t\t\t// source and destination heights are different so, we must\n\t\t\t\t\t// filter vertically\n\t\t\t\t\tif (srcWidth != dstWidth) {\n\t\t\t\t\t\t// source and destination widths are also different so, we need\n\t\t\t\t\t\t// a temporary image\n\t\t\t\t\t\ttmp = MemoryUtil.Alloc(srcWidth * dstHeight * 4);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// source and destination widths are equal so, we can directly\n\t\t\t\t\t\t// factor into destination image (second filter method will not\n\t\t\t\t\t\t// be invoked)\n\t\t\t\t\t\ttmp = dst;\n\t\t\t\t\t}\n\n\t\t\t\t\t// factor source image vertically into temporary (or destination) image\n\t\t\t\t\t_verticalFilter(src, srcWidth, srcHeight, tmp, dstHeight, filt, premultiplied);\n\t\t\t\t} else {\n\t\t\t\t\t// source and destination heights are equal so, just copy the\n\t\t\t\t\t// image pointer\n\t\t\t\t\ttmp = src;\n\t\t\t\t}\n\n\t\t\t\tif (srcWidth != dstWidth) {\n\t\t\t\t\t// source and destination heights are different so, factor\n\t\t\t\t\t// temporary (or source) image horizontally into destination image\n\t\t\t\t\t_horizontalFilter(tmp, dstHeight, srcWidth, dst, dstWidth, filt, premultiplied);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// free temporary image, if not pointing to either src or dst\n\t\t\tif (tmp != src && tmp != dst) MemoryUtil.Free(tmp);\n\t\t}\n\n\t\tconst int FI_RGBA_RED = 2, FI_RGBA_GREEN = 1, FI_RGBA_BLUE = 0, FI_RGBA_ALPHA = 3;\n\n\t\tstatic void _horizontalFilter(byte* src, int height, int srcWidth, byte* dst, int dstWidth, _Filter filter, bool premultiplied) {\n\t\t\t// allocate and calculate the contributions\n\t\t\t_WeightsTable weightsTable = new(filter, dstWidth, srcWidth);\n\n\t\t\t// step through rows\n\t\t\tfor (int y = 0; y < height; y++) {\n\t\t\t\t// factor each row\n\t\t\t\tbyte* src_bits = src + srcWidth * 4 * y;\n\t\t\t\tbyte* dst_bits = dst + dstWidth * 4 * y;\n\n\t\t\t\tfor (int x = 0; x < dstWidth; x++) {\n\t\t\t\t\t// loop through row\n\t\t\t\t\tint iLeft = weightsTable.getLeftBoundary(x);\n\t\t\t\t\tint iLimit = weightsTable.getRightBoundary(x) - iLeft;\n\t\t\t\t\tbyte* pixel = src_bits + iLeft * 4;\n\t\t\t\t\tdouble r = 0, g = 0, b = 0, a = 0;\n\n\t\t\t\t\tfor (int i = 0; i < iLimit; i++) {\n\t\t\t\t\t\t// scan between boundaries\n\t\t\t\t\t\t// accumulate weighted effect of each neighboring pixel\n\t\t\t\t\t\tdouble weight = weightsTable.getWeight(x, i);\n\t\t\t\t\t\tr += (weight * pixel[FI_RGBA_RED]);\n\t\t\t\t\t\tg += (weight * pixel[FI_RGBA_GREEN]);\n\t\t\t\t\t\tb += (weight * pixel[FI_RGBA_BLUE]);\n\t\t\t\t\t\ta += (weight * pixel[FI_RGBA_ALPHA]);\n\t\t\t\t\t\tpixel += 4;\n\t\t\t\t\t}\n\n\t\t\t\t\t// clamp and place result in destination pixel\n\t\t\t\t\tint ai = Math.Clamp((int)(a + 0.5), 0, 0xFF);\n\t\t\t\t\tdst_bits[FI_RGBA_ALPHA] = (byte)ai;\n\t\t\t\t\tif (!premultiplied) ai = 0xFF; //else colors cannot be > alpha\n\t\t\t\t\tdst_bits[FI_RGBA_RED] = (byte)Math.Clamp((int)(r + 0.5), 0, ai);\n\t\t\t\t\tdst_bits[FI_RGBA_GREEN] = (byte)Math.Clamp((int)(g + 0.5), 0, ai);\n\t\t\t\t\tdst_bits[FI_RGBA_BLUE] = (byte)Math.Clamp((int)(b + 0.5), 0, ai);\n\t\t\t\t\tdst_bits += 4;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tstatic void _verticalFilter(byte* src, int width, int srcHeight, byte* dst, int dstHeight, _Filter filter, bool premultiplied) {\n\t\t\t// allocate and calculate the contributions\n\t\t\t_WeightsTable weightsTable = new(filter, dstHeight, srcHeight);\n\n\t\t\t// step through columns\n\t\t\tbyte* src_base = src;\n\t\t\tbyte* dst_base = dst;\n\t\t\tint src_pitch = width * 4;\n\t\t\tint dst_pitch = src_pitch;\n\n\t\t\tfor (int x = 0; x < width; x++) {\n\t\t\t\t// work on column x in dst\n\t\t\t\tint index = x * 4;\n\t\t\t\tbyte* dst_bits = dst_base + index;\n\n\t\t\t\t// factor each column\n\t\t\t\tfor (int y = 0; y < dstHeight; y++) {\n\t\t\t\t\t// loop through column\n\t\t\t\t\tint iLeft = weightsTable.getLeftBoundary(y);\n\t\t\t\t\tint iLimit = weightsTable.getRightBoundary(y) - iLeft;\n\t\t\t\t\tbyte* src_bits = src_base + iLeft * src_pitch + index;\n\t\t\t\t\tdouble r = 0, g = 0, b = 0, a = 0;\n\n\t\t\t\t\tfor (int i = 0; i < iLimit; i++) {\n\t\t\t\t\t\t// scan between boundaries\n\t\t\t\t\t\t// accumulate weighted effect of each neighboring pixel\n\t\t\t\t\t\tdouble weight = weightsTable.getWeight(y, i);\n\t\t\t\t\t\tr += (weight * src_bits[FI_RGBA_RED]);\n\t\t\t\t\t\tg += (weight * src_bits[FI_RGBA_GREEN]);\n\t\t\t\t\t\tb += (weight * src_bits[FI_RGBA_BLUE]);\n\t\t\t\t\t\ta += (weight * src_bits[FI_RGBA_ALPHA]);\n\t\t\t\t\t\tsrc_bits += src_pitch;\n\t\t\t\t\t}\n\n\t\t\t\t\t// clamp and place result in destination pixel\n\t\t\t\t\tint ai = Math.Clamp((int)(a + 0.5), 0, 0xFF);\n\t\t\t\t\tdst_bits[FI_RGBA_ALPHA] = (byte)ai;\n\t\t\t\t\tif (!premultiplied) ai = 0xFF; //else colors cannot be > alpha\n\t\t\t\t\tdst_bits[FI_RGBA_RED] = (byte)Math.Clamp((int)(r + 0.5), 0, ai);\n\t\t\t\t\tdst_bits[FI_RGBA_GREEN] = (byte)Math.Clamp((int)(g + 0.5), 0, ai);\n\t\t\t\t\tdst_bits[FI_RGBA_BLUE] = (byte)Math.Clamp((int)(b + 0.5), 0, ai);\n\t\t\t\t\tdst_bits += dst_pitch;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tstruct _WeightsTable {\n\t\t\t_Contribution[] _table;\n\t\t\tint _windowSize;\n\t\t\tint _lineLength;\n\n\t\t\tpublic _WeightsTable(_Filter filter, int uDstSize, int uSrcSize) {\n\t\t\t\tdouble dWidth;\n\t\t\t\tdouble dFScale;\n\t\t\t\tdouble dFilterWidth = filter.width;\n\n\t\t\t\t// factor factor\n\t\t\t\tdouble dScale = (double)uDstSize / uSrcSize;\n\n\t\t\t\tif (dScale < 1.0) {\n\t\t\t\t\t// minification\n\t\t\t\t\tdWidth = dFilterWidth / dScale;\n\t\t\t\t\tdFScale = dScale;\n\t\t\t\t} else {\n\t\t\t\t\t// magnification\n\t\t\t\t\tdWidth = dFilterWidth;\n\t\t\t\t\tdFScale = 1.0;\n\t\t\t\t}\n\n\t\t\t\t// allocate a new line contributions structure\n\t\t\t\t//\n\t\t\t\t// window size is the number of sampled pixels\n\t\t\t\t_windowSize = 2 * (int)Math.Ceiling(dWidth) + 1;\n\t\t\t\t// length of dst line (no. of rows / cols) \n\t\t\t\t_lineLength = uDstSize;\n\n\t\t\t\t// allocate list of contributions \n\t\t\t\t_table = new _Contribution[_lineLength];\n\t\t\t\tfor (int u = 0; u < _lineLength; u++) {\n\t\t\t\t\t// allocate contributions for every pixel\n\t\t\t\t\t_table[u].Weights = new double[_windowSize];\n\t\t\t\t}\n\n\t\t\t\t// offset for discrete to continuous coordinate conversion\n\t\t\t\tdouble dOffset = 0.5 / dScale;\n\n\t\t\t\tfor (int u = 0; u < _lineLength; u++) {\n\t\t\t\t\t// scan through line of contributions\n\n\t\t\t\t\t// inverse mapping (discrete dst 'u' to continous src 'dCenter')\n\t\t\t\t\tdouble dCenter = u / dScale + dOffset;\n\n\t\t\t\t\t// find the significant edge points that affect the pixel\n\t\t\t\t\tint iLeft = Math.Max(0, (int)(dCenter - dWidth + 0.5));\n\t\t\t\t\tint iRight = Math.Min((int)(dCenter + dWidth + 0.5), uSrcSize);\n\n\t\t\t\t\t_table[u].Left = iLeft;\n\t\t\t\t\t_table[u].Right = iRight;\n\n\t\t\t\t\tdouble dTotalWeight = 0;  // sum of weights (initialized to zero)\n\t\t\t\t\tfor (int iSrc = iLeft; iSrc < iRight; iSrc++) {\n\t\t\t\t\t\t// calculate weights\n\t\t\t\t\t\tdouble weight = dFScale * filter.Filter(dFScale * (iSrc + 0.5 - dCenter));\n\t\t\t\t\t\t_table[u].Weights[iSrc - iLeft] = weight;\n\t\t\t\t\t\tdTotalWeight += weight;\n\t\t\t\t\t}\n\t\t\t\t\tif ((dTotalWeight > 0) && (dTotalWeight != 1)) {\n\t\t\t\t\t\t// normalize weight of neighbouring points\n\t\t\t\t\t\tfor (int iSrc = iLeft; iSrc < iRight; iSrc++) {\n\t\t\t\t\t\t\t// normalize point\n\t\t\t\t\t\t\t_table[u].Weights[iSrc - iLeft] /= dTotalWeight;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// simplify the filter, discarding null weights at the right\n\t\t\t\t\t{\n\t\t\t\t\t\tint iTrailing = iRight - iLeft - 1;\n\t\t\t\t\t\twhile (_table[u].Weights[iTrailing] == 0) {\n\t\t\t\t\t\t\t_table[u].Right--;\n\t\t\t\t\t\t\tiTrailing--;\n\t\t\t\t\t\t\tif (_table[u].Right == _table[u].Left) {\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t} // next dst pixel\n\t\t\t}\n\n\t\t\t[MethodImpl(MethodImplOptions.AggressiveInlining)]\n\t\t\tpublic double getWeight(int dst_pos, int src_pos) {\n\t\t\t\treturn _table[dst_pos].Weights[src_pos];\n\t\t\t}\n\n\t\t\t[MethodImpl(MethodImplOptions.AggressiveInlining)]\n\t\t\tpublic int getLeftBoundary(int dst_pos) {\n\t\t\t\treturn _table[dst_pos].Left;\n\t\t\t}\n\n\t\t\t[MethodImpl(MethodImplOptions.AggressiveInlining)]\n\t\t\tpublic int getRightBoundary(int dst_pos) {\n\t\t\t\treturn _table[dst_pos].Right;\n\t\t\t}\n\n\t\t\tstruct _Contribution {\n\t\t\t\tpublic double[] Weights;\n\t\t\t\tpublic int Left, Right;\n\t\t\t}\n\t\t}\n\n#if true\n\t\tabstract class _Filter {\n\t\t\tpublic abstract double width { get; }\n\n\t\t\tpublic abstract double Filter(double dVal);\n\t\t}\n\n\t\tclass _FilterLanczos3 : _Filter {\n\t\t\tpublic override double width => 3;\n\n\t\t\tpublic override double Filter(double dVal) {\n\t\t\t\tdVal = Math.Abs(dVal);\n\t\t\t\tif (dVal < width) {\n\t\t\t\t\treturn (_sinc(dVal) * _sinc(dVal / width));\n\t\t\t\t}\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\tstatic double _sinc(double value) {\n\t\t\t\tif (value != 0) {\n\t\t\t\t\tvalue *= Math.PI;\n\t\t\t\t\treturn (Math.Sin(value) / value);\n\t\t\t\t}\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t}\n\n\t\tclass _FilterCatmullRom : _Filter {\n\t\t\tpublic override double width => 2;\n\n\t\t\tpublic override double Filter(double dVal) {\n\t\t\t\tif (dVal < -2) return 0;\n\t\t\t\tif (dVal < -1) return (0.5 * (4 + dVal * (8 + dVal * (5 + dVal))));\n\t\t\t\tif (dVal < 0) return (0.5 * (2 + dVal * dVal * (-5 - 3 * dVal)));\n\t\t\t\tif (dVal < 1) return (0.5 * (2 + dVal * dVal * (-5 + 3 * dVal)));\n\t\t\t\tif (dVal < 2) return (0.5 * (4 + dVal * (-8 + dVal * (5 - dVal))));\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t}\n\n\t\tclass _FilterBicubic : _Filter {\n\t\t\treadonly double p0, p2, p3;\n\t\t\treadonly double q0, q1, q2, q3;\n\n\t\t\tpublic _FilterBicubic() {\n\t\t\t\tdouble b = 1 / 3d, c = b;\n\t\t\t\tp0 = (6 - 2 * b) / 6;\n\t\t\t\tp2 = (-18 + 12 * b + 6 * c) / 6;\n\t\t\t\tp3 = (12 - 9 * b - 6 * c) / 6;\n\t\t\t\tq0 = (8 * b + 24 * c) / 6;\n\t\t\t\tq1 = (-12 * b - 48 * c) / 6;\n\t\t\t\tq2 = (6 * b + 30 * c) / 6;\n\t\t\t\tq3 = (-b - 6 * c) / 6;\n\t\t\t}\n\n\t\t\tpublic override double width => 2;\n\n\t\t\tpublic override double Filter(double dVal) {\n\t\t\t\tdVal = Math.Abs(dVal);\n\t\t\t\tif (dVal < 1)\n\t\t\t\t\treturn (p0 + dVal * dVal * (p2 + dVal * p3));\n\t\t\t\tif (dVal < 2)\n\t\t\t\t\treturn (q0 + dVal * (q1 + dVal * (q2 + dVal * q3)));\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t}\n#else\n\t\tclass _Filter {\n\t\t\tpublic readonly double width = 3;\n\n\t\t\tpublic double Filter(double dVal) {\n\t\t\t\tdVal = Math.Abs(dVal);\n\t\t\t\tif (dVal < width) {\n\t\t\t\t\treturn (_sinc(dVal) * _sinc(dVal / width));\n\t\t\t\t}\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\tstatic double _sinc(double value) {\n\t\t\t\tif (value != 0) {\n\t\t\t\t\tvalue *= Math.PI;\n\t\t\t\t\treturn (Math.Sin(value) / value);\n\t\t\t\t}\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t}\n#endif\n\t}\n}\n"
  },
  {
    "path": "Au/Ext/ExtMisc.cs",
    "content": "//note: be careful when adding functions to this class. Eg something may load winforms dlls although it seems not used.\n\nusing System.Drawing;\nusing System.Drawing.Imaging;\nusing Microsoft.Win32;\n\nnamespace Au.Types;\n\n/// <summary>\n/// Adds extension methods for some .NET types.\n/// </summary>\n[DebuggerStepThrough]\npublic static unsafe partial class ExtMisc {\n\t#region value types\n\t\n\t/// <summary>\n\t/// Converts to <c>int</c> with rounding.\n\t/// Calls <see cref=\"Convert.ToInt32(double)\"/>.\n\t/// </summary>\n\t/// <exception cref=\"OverflowException\"></exception>\n\tpublic static int ToInt(this double t) => Convert.ToInt32(t);\n\t\n\t/// <summary>\n\t/// Converts to <c>int</c> with rounding.\n\t/// Calls <see cref=\"Convert.ToInt32(float)\"/>.\n\t/// </summary>\n\t/// <exception cref=\"OverflowException\"></exception>\n\tpublic static int ToInt(this float t) => Convert.ToInt32(t);\n\t\n\t/// <summary>\n\t/// Converts to <c>int</c> with rounding.\n\t/// Calls <see cref=\"Convert.ToInt32(decimal)\"/>.\n\t/// </summary>\n\t/// <exception cref=\"OverflowException\"></exception>\n\tpublic static int ToInt(this decimal t) => Convert.ToInt32(t);\n\t\n\t//rejected. Too simple, and nobody would find and use.\n\t///// <summary>\n\t///// Converts to <c>int</c>.\n\t///// Can be used like <c>0xff123456.ToInt()</c> instead of <c>unchecked((int)0xff123456)</c>.\n\t///// </summary>\n\t//public static int ToInt(this uint t) => unchecked((int)t);\n\t\n\t///// <summary>\n\t///// Converts to <c>Color</c>.\n\t///// Can be used like <c>0xff123456.ToColor_()</c> instead of <c>Color.FromArgb(unchecked((int)0xff123456))</c>.\n\t///// </summary>\n\t///// <param name=\"t\"></param>\n\t///// <param name=\"makeOpaque\">Add 0xff000000.</param>\n\t//internal static Color ToColor_(this uint t, bool makeOpaque = true)\n\t//\t=> Color.FromArgb(unchecked((int)(t | (makeOpaque ? 0xff000000 : 0))));\n\t\n\t/// <summary>\n\t/// Converts to <c>Color</c>. Makes opaque (alpha 0xff).\n\t/// Can be used like <c>0x123456.ToColor_()</c> instead of <c>Color.FromArgb(unchecked((int)0xff123456))</c>.\n\t/// </summary>\n\tinternal static Color ToColor_(this int t, bool bgr = false) {\n\t\tif (bgr) t = ColorInt.SwapRB(t);\n\t\treturn Color.FromArgb(unchecked(0xff << 24 | t));\n\t}\n\t\n\t/// <summary>\n\t/// Converts <c>double</c> to <c>string</c>.\n\t/// Uses invariant culture, therefore decimal point is always <c>'.'</c>, not <c>','</c> etc.\n\t/// Calls <see cref=\"double.ToString(string, IFormatProvider)\"/>.\n\t/// </summary>\n\tpublic static string ToS(this double t, string format = null) {\n\t\treturn t.ToString(format, NumberFormatInfo.InvariantInfo);\n\t}\n\t\n\t/// <summary>\n\t/// Converts <c>float</c> to <c>string</c>.\n\t/// Uses invariant culture, therefore decimal point is always <c>'.'</c>, not <c>','</c> etc.\n\t/// Calls <see cref=\"float.ToString(string, IFormatProvider)\"/>.\n\t/// </summary>\n\tpublic static string ToS(this float t, string format = null) {\n\t\treturn t.ToString(format, NumberFormatInfo.InvariantInfo);\n\t}\n\t\n\t/// <summary>\n\t/// Converts <c>decimal</c> to <c>string</c>.\n\t/// Uses invariant culture, therefore decimal point is always <c>'.'</c>, not <c>','</c> etc.\n\t/// Calls <see cref=\"decimal.ToString(string, IFormatProvider)\"/>.\n\t/// </summary>\n\tpublic static string ToS(this decimal t, string format = null) {\n\t\treturn t.ToString(format, NumberFormatInfo.InvariantInfo);\n\t}\n\t\n\t/// <summary>\n\t/// Converts <c>int</c> to <c>string</c>.\n\t/// Uses invariant culture, therefore minus sign is always ASCII <c>'-',</c> not <c>'−'</c> etc.\n\t/// Calls <see cref=\"int.ToString(string, IFormatProvider)\"/>.\n\t/// </summary>\n\tpublic static string ToS(this int t, string format = null) {\n\t\treturn t.ToString(format, NumberFormatInfo.InvariantInfo);\n\t}\n\t\n\t/// <summary>\n\t/// Converts <c>long</c> to <c>string</c>.\n\t/// Uses invariant culture, therefore minus sign is always ASCII <c>'-'</c>, not <c>'−'</c> etc.\n\t/// Calls <see cref=\"double.ToString(string, IFormatProvider)\"/>.\n\t/// </summary>\n\tpublic static string ToS(this long t, string format = null) {\n\t\treturn t.ToString(format, NumberFormatInfo.InvariantInfo);\n\t}\n\t\n\t/// <summary>\n\t/// Converts <c>nint</c> to <c>string</c>.\n\t/// Uses invariant culture, therefore minus sign is always ASCII <c>'-'</c>, not <c>'−'</c> etc.\n\t/// Calls <see cref=\"IntPtr.ToString(string, IFormatProvider)\"/>.\n\t/// </summary>\n\tpublic static string ToS(this nint t, string format = null) {\n\t\treturn t.ToString(format, NumberFormatInfo.InvariantInfo);\n\t}\n\t//cref not nint.ToString because DocFX does not support it.\n\t\n\t/// <summary>\n\t/// Returns <c><![CDATA[(this >= min && this <= max) ? this : defaultValue]]></c>.\n\t/// </summary>\n    internal static int EnsureValid_(this int t, int min, int max, int defaultValue = 0)\n        => (t >= min && t <= max) ? t : defaultValue;\n\t\n\t//rare\n\t///// <summary>\n\t///// Returns <c>true</c> if <c>t.Width &lt;= 0 || t.Height &lt;= 0</c>.\n\t///// Note: <c>Rectangle.IsEmpty</c> returns <c>true</c> only when all fields are 0.\n\t///// </summary>\n\t//[MethodImpl(MethodImplOptions.AggressiveInlining)]\n\t//public static bool NoArea(this Rectangle t) {\n\t//\treturn t.Width <= 0 || t.Height <= 0;\n\t//}\n\t\n\t/// <summary>\n\t/// Calls <see cref=\"Range.GetOffsetAndLength\"/> and returns start and end instead of start and length.\n\t/// </summary>\n\t/// <param name=\"t\"></param>\n\t/// <param name=\"length\"></param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\"></exception>\n\t[MethodImpl(MethodImplOptions.AggressiveInlining)]\n\tpublic static (int start, int end) GetStartEnd(this Range t, int length) {\n\t\tvar v = t.GetOffsetAndLength(length);\n\t\treturn (v.Offset, v.Offset + v.Length);\n\t}\n\t\n\t/// <summary>\n\t/// If this is <c>null</c>, returns <c>(0, length)</c>. Else calls <see cref=\"Range.GetOffsetAndLength\"/> and returns start and end instead of start and length.\n\t/// </summary>\n\t/// <param name=\"t\"></param>\n\t/// <param name=\"length\"></param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\"></exception>\n\t[MethodImpl(MethodImplOptions.AggressiveInlining)]\n\tpublic static (int start, int end) GetStartEnd(this Range? t, int length)\n\t\t=> t?.GetStartEnd(length) ?? (0, length);\n\t\n\t/// <summary>\n\t/// If this is <c>null</c>, returns <c>(0, length)</c>. Else calls <see cref=\"Range.GetOffsetAndLength\"/>.\n\t/// </summary>\n\t/// <param name=\"t\"></param>\n\t/// <param name=\"length\"></param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\"></exception>\n\t[MethodImpl(MethodImplOptions.AggressiveInlining)]\n\tpublic static (int Offset, int Length) GetOffsetAndLength(this Range? t, int length)\n\t\t=> t?.GetOffsetAndLength(length) ?? (0, length);\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if null pointer.\n\t/// </summary>\n\t[MethodImpl(MethodImplOptions.AggressiveInlining)]\n\tpublic static bool IsNull<T>(this ReadOnlySpan<T> t) => t == ReadOnlySpan<T>.Empty;\n\t\n\t//currently not used. Creates shorter string than ToString.\n\t///// <summary>\n\t///// Converts this <c>Guid</c> to Base64 string.\n\t///// </summary>\n\t//public static string ToBase64(this Guid t) => Convert.ToBase64String(new RByte((byte*)&t, sizeof(Guid)));\n\t\n\t//rejected: too simple. We have print.it(uint), also can use $\"0x{t:X}\" or \"0x\" + t.ToString(\"X\").\n\t///// <summary>\n\t///// Converts <c>int</c> to hexadecimal string like <c>\"0x3A\"</c>.\n\t///// </summary>\n\t//public static string ToHex(this int t)\n\t//{\n\t//\treturn \"0x\" + t.ToString(\"X\");\n\t//}\n\t\n\t#endregion\n\t\n\t#region enum\n\t\n\t[MethodImpl(MethodImplOptions.AggressiveInlining)]\n\tstatic long _ToLong<T>(T v) where T : unmanaged, Enum {\n\t\tif (sizeof(T) == 4) return *(int*)&v;\n\t\tif (sizeof(T) == 8) return *(long*)&v;\n\t\tif (sizeof(T) == 2) return *(short*)&v;\n\t\treturn *(byte*)&v;\n\t\t//Compiler removes the if(sizeof(T) == n) and code that is unused with that size, because sizeof(T) is const.\n\t\t//Faster than with switch(sizeof(T)). It seems the switch code is considered too big to be inlined.\n\t}\n\t\n\t//same. Was faster when tested in the past.\n\t//[MethodImpl(MethodImplOptions.AggressiveInlining)]\n\t//static long _ToLong2<T>(T v) where T : unmanaged, Enum\n\t//{\n\t//\tif(sizeof(T) == 4) return Unsafe.As<T, int>(ref v);\n\t//\tif(sizeof(T) == 8) return Unsafe.As<T, long>(ref v);\n\t//\tif(sizeof(T) == 2) return Unsafe.As<T, short>(ref v);\n\t//\treturn Unsafe.As<T, byte>(ref v);\n\t//}\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if this enum variable has all flag bits specified in <i>flag</i>.\n\t/// </summary>\n\t/// <param name=\"t\"></param>\n\t/// <param name=\"flag\">One or more flags.</param>\n\t/// <remarks>\n\t/// The same as code <c>(t &amp; flag) == flag</c> or <see cref=\"Enum.HasFlag\"/>.\n\t/// </remarks>\n\t[MethodImpl(MethodImplOptions.AggressiveInlining)]\n\tpublic static bool Has<T>(this T t, T flag) where T : unmanaged, Enum {\n#if false //Enum.HasFlag used to be slow, but now compiler for it creates the same code as with operator\n\t\t\treturn t.HasFlag(flag);\n\t\t\t//However cannot use this because of JIT compiler bug: in some cases Has returns true when no flag.\n\t\t\t//Noticed it in TriggerActionThreads.Run in finally{} of actionWrapper, code o.flags.Has(TOFlags.Single).\n\t\t\t//It was elusive, difficult to debug, only in Release, and only after some time/times, when tiered JIT fully optimizes.\n\t\t\t//When Has returned true, print.it showed that flags is 0.\n\t\t\t//No bug if HasFlag called directly, not in extension method.\n#elif true //slightly slower than Enum.HasFlag and code as with operator\n\t\tvar m = _ToLong(flag);\n\t\treturn (_ToLong(t) & m) == m;\n#else //slower\n\t\t\tswitch(sizeof(T)) {\n\t\t\tcase 4: {\n\t\t\t\tvar a = Unsafe.As<T, uint>(ref t);\n\t\t\t\tvar b = Unsafe.As<T, uint>(ref flag);\n\t\t\t\treturn (a & b) == b;\n\t\t\t}\n\t\t\tcase 8: {\n\t\t\t\tvar a = Unsafe.As<T, ulong>(ref t);\n\t\t\t\tvar b = Unsafe.As<T, ulong>(ref flag);\n\t\t\t\treturn (a & b) == b;\n\t\t\t}\n\t\t\tcase 2: {\n\t\t\t\tvar a = Unsafe.As<T, ushort>(ref t);\n\t\t\t\tvar b = Unsafe.As<T, ushort>(ref flag);\n\t\t\t\treturn (a & b) == b;\n\t\t\t}\n\t\t\tdefault: {\n\t\t\t\tvar a = Unsafe.As<T, byte>(ref t);\n\t\t\t\tvar b = Unsafe.As<T, byte>(ref flag);\n\t\t\t\treturn (a & b) == b;\n\t\t\t}\n\t\t\t}\n\t\t\t//compiler removes the switch/case, because sizeof(T) is const\n#endif\n\t}\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if this enum variable has one or more flag bits specified in <i>flags</i>.\n\t/// </summary>\n\t/// <param name=\"t\"></param>\n\t/// <param name=\"flags\">One or more flags.</param>\n\t[MethodImpl(MethodImplOptions.AggressiveInlining)]\n\tpublic static bool HasAny<T>(this T t, T flags) where T : unmanaged, Enum {\n\t\treturn (_ToLong(t) & _ToLong(flags)) != 0;\n\t}\n\t\n\t//slower\n\t//[MethodImpl(MethodImplOptions.AggressiveInlining)]\n\t//public static bool HasAny5<T>(this T t, T flags) where T : unmanaged, Enum\n\t//{\n\t//\tif(sizeof(T) == 4) return (*(int*)&t & *(int*)&flags) != 0;\n\t//\tif(sizeof(T) == 8) return (*(long*)&t & *(long*)&flags) != 0;\n\t//\tif(sizeof(T) == 2) return (*(short*)&t & *(short*)&flags) != 0;\n\t//\treturn (*(byte*)&t & *(byte*)&flags) != 0;\n\t//}\n\t\n\t/// <summary>\n\t/// Adds or removes a flag or flags.\n\t/// </summary>\n\t/// <param name=\"t\"></param>\n\t/// <param name=\"flag\">One or more flags to add or remove.</param>\n\t/// <param name=\"add\">If <c>true</c>, adds flag, else removes flag.</param>\n\t[MethodImpl(MethodImplOptions.AggressiveOptimization)]\n\tpublic static void SetFlag<T>(ref this T t, T flag, bool add) where T : unmanaged, Enum {\n\t\tlong a = _ToLong(t), b = _ToLong(flag);\n\t\tif (add) a |= b; else a &= ~b;\n\t\tt = *(T*)&a;\n\t}\n\t\n\t/// <summary>\n\t/// Adds or removes a flag or flags.\n\t/// </summary>\n\t/// <param name=\"flag\">Flag(s) to add or remove.</param>\n\t/// <param name=\"add\">If <c>true</c>, adds the flag(s) (<c>t |= flag</c>), else removes (<c>t &amp;= ~flag</c>).</param>\n\tinternal static void SetFlag_(this ref int t, int flag, bool add) {\n\t\tif (add) t |= flag; else t &= ~flag;\n\t}\n\t\n\t/// <inheritdoc cref=\"SetFlag_(ref int, int, bool)\"/>\n\tinternal static void SetFlag_(this ref uint t, uint flag, bool set) {\n\t\tif (set) t |= flag; else t &= ~flag;\n\t}\n\t\n\t/// <inheritdoc cref=\"SetFlag_(ref int, int, bool)\"/>\n\tinternal static void SetFlag_(this ref ushort t, ushort flag, bool set) {\n\t\tif (set) t |= flag; else t = (ushort)(t & ~flag);\n\t}\n\t\n\t/// <inheritdoc cref=\"SetFlag_(ref int, int, bool)\"/>\n\tinternal static void SetFlag_(this ref byte t, byte flag, bool set) {\n\t\tif (set) t |= flag; else t = (byte)(t & ~flag);\n\t}\n\t\n\t#endregion\n\t\n\t#region char\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if character is ASCII <c>'0'</c> to <c>'9'</c>.\n\t/// </summary>\n\tpublic static bool IsAsciiDigit(this char c) => char.IsAsciiDigit(c);\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if character is ASCII <c>'A'</c> to <c>'Z'</c> or <c>'a'</c> to <c>'z'</c>.\n\t/// </summary>\n\t//public static bool IsAsciiAlpha(this char c) => (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');\n\tpublic static bool IsAsciiAlpha(this char c) => char.IsAsciiLetter(c);\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if character is ASCII <c>'A'</c> to <c>'Z'</c> or <c>'a'</c> to <c>'z'</c> or <c>'0'</c> to <c>'9'</c>.\n\t/// </summary>\n\tpublic static bool IsAsciiAlphaDigit(this char c) => char.IsAsciiLetterOrDigit(c);\n\t\n\t#endregion\n\t\n\t#region array\n\t\n\t/// <summary>\n\t/// Creates a copy of this array with one or more removed elements.\n\t/// </summary>\n\t/// <typeparam name=\"T\"></typeparam>\n\t/// <param name=\"t\"></param>\n\t/// <param name=\"index\"></param>\n\t/// <param name=\"count\"></param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\"></exception>\n\tpublic static T[] RemoveAt<T>(this T[] t, int index, int count = 1) {\n\t\tif ((uint)index > t.Length || count < 0 || index + count > t.Length) throw new ArgumentOutOfRangeException();\n\t\tint n = t.Length - count;\n\t\tif (n == 0) return [];\n\t\tvar r = new T[n];\n\t\tfor (int i = 0; i < index; i++) r[i] = t[i];\n\t\tfor (int i = index; i < n; i++) r[i] = t[i + count];\n\t\treturn r;\n\t}\n\t\n\t/// <summary>\n\t/// Creates a copy of this array with one inserted element.\n\t/// </summary>\n\t/// <typeparam name=\"T\"></typeparam>\n\t/// <param name=\"t\"></param>\n\t/// <param name=\"index\">Where to insert. If -1, adds to the end.</param>\n\t/// <param name=\"value\"></param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\"></exception>\n\tpublic static T[] InsertAt<T>(this T[] t, int index, T value = default) {\n\t\tif (index == -1) index = t.Length; else if ((uint)index > t.Length) throw new ArgumentOutOfRangeException();\n\t\tvar r = new T[t.Length + 1];\n\t\tfor (int i = 0; i < index; i++) r[i] = t[i];\n\t\tfor (int i = index; i < t.Length; i++) r[i + 1] = t[i];\n\t\tr[index] = value;\n\t\treturn r;\n\t}\n\t\n\t/// <summary>\n\t/// Creates a copy of this array with several inserted elements.\n\t/// </summary>\n\t/// <typeparam name=\"T\"></typeparam>\n\t/// <param name=\"t\"></param>\n\t/// <param name=\"index\">Where to insert. If -1, adds to the end.</param>\n\t/// <param name=\"values\"></param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\"></exception>\n\tpublic static T[] InsertAt<T>(this T[] t, int index, params ReadOnlySpan<T> values) {\n\t\tif (index == -1) index = t.Length; else if ((uint)index > t.Length) throw new ArgumentOutOfRangeException();\n\t\tint n = values.Length; if (n == 0) return t;\n\t\t\n\t\tvar r = new T[t.Length + n];\n\t\tfor (int i = 0; i < index; i++) r[i] = t[i];\n\t\tfor (int i = index; i < t.Length; i++) r[i + n] = t[i];\n\t\tfor (int i = 0; i < n; i++) r[i + index] = values[i];\n\t\treturn r;\n\t}\n\t\n\t#endregion\n\t\n\t#region IEnumerable\n\t\n\t/// <summary>\n\t/// Removes items based on a predicate. For example, all items that have certain value.\n\t/// </summary>\n\t/// <typeparam name=\"TKey\"></typeparam>\n\t/// <typeparam name=\"TValue\"></typeparam>\n\t/// <param name=\"t\"></param>\n\t/// <param name=\"predicate\"></param>\n\tpublic static void RemoveWhere<TKey, TValue>(this Dictionary<TKey, TValue> t, Func<KeyValuePair<TKey, TValue>, bool> predicate) {\n\t\tforeach (var k in t.Where(predicate).Select(kv => kv.Key).ToArray()) { t.Remove(k); }\n\t}\n\t\n\t/// <summary>\n\t/// Gets a reference to a <c>TValue</c> in this dictionary, adding a new entry with a default value if the key does not exist.\n\t/// This extension method just calls <see cref=\"CollectionsMarshal.GetValueRefOrAddDefault\"/>.\n\t/// </summary>\n\t/// <inheritdoc cref=\"CollectionsMarshal.GetValueRefOrAddDefault\"/>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// var d = new Dictionary<string, int>();\n\t/// for (int i = 0; i < 3; i++) {\n\t/// \tref var r = ref d.GetValueRefOrAddDefault(\"a\", out bool exists);\n\t/// \tprint.it(exists);\n\t/// \tif(!exists) r = 100; else r++;\n\t/// }\n\t/// print.it(d);\n\t/// ]]></code>\n\t/// </example>\n\tinternal static ref TValue GetValueRefOrAddDefault_<TKey, TValue>(this Dictionary<TKey, TValue> t, TKey key, out bool exists) {\n#pragma warning disable 9088 //weird and undocumented: \"This returns a parameter by reference 'exists' but it is scoped to the current method\"\n\t\treturn ref CollectionsMarshal.GetValueRefOrAddDefault(t, key, out exists);\n\t}\n\t\n\t/// <summary>\n\t/// Gets a reference to a <c>TValue</c> in this dictionary. If the key does not exist, sets <i>exists</i> = <c>false</c> and returns a reference <c>null</c>.\n\t/// This extension method just calls <see cref=\"CollectionsMarshal.GetValueRefOrNullRef\"/> and <see cref=\"Unsafe.IsNullRef\"/>.\n\t/// </summary>\n\t/// <param name=\"exists\">Receives <c>true</c> if the key exists.</param>\n\t/// <inheritdoc cref=\"CollectionsMarshal.GetValueRefOrNullRef\"/>\n\tinternal static ref TValue GetValueRefOrNullRef_<TKey, TValue>(this Dictionary<TKey, TValue> t, TKey key, out bool exists) {\n\t\tref TValue r = ref CollectionsMarshal.GetValueRefOrNullRef(t, key);\n\t\texists = !Unsafe.IsNullRef(ref r);\n\t\treturn ref r;\n\t}\n\t\n\t/// <inheritdoc cref=\"CollectionsMarshal.AsSpan\"/>\n\tpublic static Span<T> AsSpan<T>(this List<T> t)\n\t\t=> CollectionsMarshal.AsSpan(t);\n\t\n\t/// <summary>\n\t/// Gets a reference to an item.\n\t/// List items must not be added or removed while it is in use.\n\t/// </summary>\n\t/// <typeparam name=\"T\"></typeparam>\n\t/// <param name=\"i\">Item index.</param>\n\tpublic static ref T Ref<T>(this List<T> t, int i)\n\t\t=> ref CollectionsMarshal.AsSpan(t)[i];\n\t\n\t/// <summary>\n\t/// Adds key/value to dictionary. If the key already exists, adds the value to the same key as <c>List</c> item and returns the <c>List</c>; else returns <c>null</c>.\n\t/// </summary>\n\t/// <exception cref=\"ArgumentException\">key/value already exists.</exception>\n\tinternal static List<TValue> MultiAdd_<TKey, TValue>(this Dictionary<TKey, object> t, TKey k, TValue v) where TValue : class {\n\t\tif (t.TryAdd(k, v)) return null;\n\t\tvar o = t[k];\n\t\tif (o is List<TValue> a) {\n\t\t\tif (!a.Contains(v)) { a.Add(v); return a; }\n\t\t} else {\n\t\t\tvar g = o as TValue;\n\t\t\tif (g == null && o != null) throw new ArgumentException(\"bad type\");\n\t\t\tif (v != g) { t[k] = a = new List<TValue> { g, v }; return a; }\n\t\t}\n\t\tthrow new ArgumentException(\"key/value already exists\");\n\t}\n\t\n\t/// <summary>\n\t/// If dictionary contains key <i>k</i> that contains value <i>v</i> (as single value or in <c>List</c>), removes the value (and key if it was single value) and returns <c>true</c>.\n\t/// </summary>\n\tinternal static bool MultiRemove_<TKey, TValue>(this Dictionary<TKey, object> t, TKey k, TValue v) where TValue : class {\n\t\tif (!t.TryGetValue(k, out var o)) return false;\n\t\tif (o is List<TValue> a) {\n\t\t\tif (!a.Remove(v)) return false;\n\t\t\tif (a.Count == 1) t[k] = a[0];\n\t\t} else {\n\t\t\tvar g = o as TValue;\n\t\t\tif (g == null && o != null) throw new ArgumentException(\"bad type\");\n\t\t\tif (v != g) return false;\n\t\t\tt.Remove(k);\n\t\t}\n\t\treturn true;\n\t}\n\t\n\t/// <summary>\n\t/// If dictionary contains key <i>k</i>, gets its value (<i>v</i>) or list of values (<i>a</i>) and returns <c>true</c>.\n\t/// </summary>\n\t/// <param name=\"t\"></param>\n\t/// <param name=\"k\"></param>\n\t/// <param name=\"v\">Receives single value, or <c>null</c> if the key has multiple values.</param>\n\t/// <param name=\"a\">Receives multiple values, or <c>null</c> if the key has single value.</param>\n\tinternal static bool MultiGet_<TKey, TValue>(this Dictionary<TKey, object> t, TKey k, out TValue v, out List<TValue> a) where TValue : class {\n\t\tbool r = t.TryGetValue(k, out var o);\n\t\tv = o as TValue;\n\t\ta = o as List<TValue>;\n\t\tif (v == null && a == null && o != null) throw new ArgumentException(\"bad type\");\n\t\treturn r;\n\t}\n\n#if true\n\t/// <summary>\n\t/// Returns <c>Length</c>, or 0 if <c>null</c>.\n\t/// </summary>\n\tinternal static int Lenn_<T>(this T[] t) => t?.Length ?? 0;\n\t//internal static int Lenn_(this System.Collections.ICollection t) => t?.Count ?? 0; //slower, as well as Array\n\t\n\t/// <summary>\n\t/// Returns <c>Count</c>, or 0 if <c>null</c>.\n\t/// </summary>\n\tinternal static int Lenn_<T>(this List<T> t) => t?.Count ?? 0;\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if <c>null</c> or <c>Length</c> == 0.\n\t/// </summary>\n\tinternal static bool NE_<T>(this T[] t) => (t?.Length ?? 0) == 0;\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if <c>null</c> or <c>Count</c> == 0.\n\t/// </summary>\n\tinternal static bool NE_<T>(this List<T> t) => (t?.Count ?? 0) == 0;\n#else //still too early to use `extension` in this library. Eg then the XML file contains duplicate names (then exception in _DocumentationProvider.Create). Probably DocFX would not get it too.\n\textension<T>(T[] t) { //with System.Collections.ICollection slower, as well as Array\n\t\t/// <summary>\n\t\t/// Returns <c>Length</c>, or 0 if <c>null</c>.\n\t\t/// </summary>\n\t\tinternal int Lenn_ => t?.Length ?? 0;\n\t\t\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if <c>null</c> or <c>Length</c> == 0.\n\t\t/// </summary>\n\t\tinternal bool NE_ => (t?.Length ?? 0) == 0;\n\t}\n\t\n\textension<T>(List<T> t) {\n\t\t/// <summary>\n\t\t/// Returns <c>Count</c>, or 0 if <c>null</c>.\n\t\t/// </summary>\n\t\tinternal int Lenn_ => t?.Count ?? 0;\n\t\t\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if <c>null</c> or <c>Count</c> == 0.\n\t\t/// </summary>\n\t\tinternal bool NE_ => (t?.Count ?? 0) == 0;\n\t}\n#endif\n\n\t/// <summary>\n\t/// Efficiently recursively gets descendants of this tree.\n\t/// <see href=\"https://stackoverflow.com/a/30441479/2547338\"/>\n\t/// </summary>\n\t/// <typeparam name=\"T\"></typeparam>\n\t/// <param name=\"t\"></param>\n\t/// <param name=\"childSelector\"></param>\n\tinternal static IEnumerable<T> Descendants_<T>(this IEnumerable<T> t, Func<T, IEnumerable<T>> childSelector) {\n\t\tvar stack = new Stack<IEnumerator<T>>();\n\t\tvar enumerator = t.GetEnumerator();\n\t\t\n\t\ttry {\n\t\t\twhile (true) {\n\t\t\t\tif (enumerator.MoveNext()) {\n\t\t\t\t\tT element = enumerator.Current;\n\t\t\t\t\tyield return element;\n\t\t\t\t\t\n\t\t\t\t\tvar e = childSelector(element)?.GetEnumerator();\n\t\t\t\t\tif (e != null) {\n\t\t\t\t\t\tstack.Push(enumerator);\n\t\t\t\t\t\tenumerator = e;\n\t\t\t\t\t}\n\t\t\t\t} else if (stack.Count > 0) {\n\t\t\t\t\tenumerator.Dispose();\n\t\t\t\t\tenumerator = stack.Pop();\n\t\t\t\t} else {\n\t\t\t\t\tyield break;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfinally {\n\t\t\tenumerator.Dispose();\n\t\t\t\n\t\t\twhile (stack.Count > 0) // Clean up in case of an exception.\n\t\t\t{\n\t\t\t\tenumerator = stack.Pop();\n\t\t\t\tenumerator.Dispose();\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Efficiently recursively gets descendants of this tree.\n\t/// <see href=\"https://stackoverflow.com/a/30441479/2547338\"/>\n\t/// </summary>\n\t/// <param name=\"t\"></param>\n\t/// <param name=\"childSelector\"></param>\n\tinternal static System.Collections.IEnumerable Descendants_(this System.Collections.IEnumerable t, Func<object, System.Collections.IEnumerable> childSelector) {\n\t\tvar stack = new Stack<System.Collections.IEnumerator>();\n\t\tvar enumerator = t.GetEnumerator();\n\t\t\n\t\twhile (true) {\n\t\t\tif (enumerator.MoveNext()) {\n\t\t\t\tobject element = enumerator.Current;\n\t\t\t\tyield return element;\n\t\t\t\t\n\t\t\t\tvar e = childSelector(element)?.GetEnumerator();\n\t\t\t\tif (e != null) {\n\t\t\t\t\tstack.Push(enumerator);\n\t\t\t\t\tenumerator = e;\n\t\t\t\t}\n\t\t\t} else if (stack.Count > 0) {\n\t\t\t\tenumerator = stack.Pop();\n\t\t\t} else {\n\t\t\t\tyield break;\n\t\t\t}\n\t\t}\n\t}\n\t\n#endregion\n\t\n\t#region StringBuilder\n\t\n\t/// <summary>\n\t/// Appends string as new correctly formatted sentence.\n\t/// </summary>\n\t/// <returns>this.</returns>\n\t/// <param name=\"t\"></param>\n\t/// <param name=\"s\"></param>\n\t/// <param name=\"noUcase\">Don't make the first character uppercase.</param>\n\t/// <remarks>\n\t/// If <i>s</i> is <c>null</c> or <c>\"\"</c>, does nothing.\n\t/// If this is not empty, appends space.\n\t/// If <i>s</i> starts with a lowercase character, makes it uppercase, unless this ends with a character other than <c>'.'</c>.\n\t/// Appends <c>'.'</c> if <i>s</i> does not end with <c>'.'</c>, <c>';'</c>, <c>':'</c>, <c>','</c>, <c>'!'</c> or <c>'?'</c>.\n\t/// </remarks>\n\tpublic static StringBuilder AppendSentence(this StringBuilder t, string s, bool noUcase = false) {\n\t\tif (!s.NE()) {\n\t\t\tbool makeUcase = !noUcase && Char.IsLower(s[0]);\n\t\t\tif (t.Length > 0) {\n\t\t\t\tif (makeUcase && t[^1] != '.') makeUcase = false;\n\t\t\t\tt.Append(' ');\n\t\t\t}\n\t\t\tif (makeUcase) { t.Append(Char.ToUpper(s[0])).Append(s, 1, s.Length - 1); } else t.Append(s);\n\t\t\tswitch (s[^1]) {\n\t\t\tcase '.': case ';': case ':': case ',': case '!': case '?': break;\n\t\t\tdefault: t.Append('.'); break;\n\t\t\t}\n\t\t}\n\t\treturn t;\n\t}\n\t\n\t#endregion\n\t\n\t#region winforms\n\t\n\t/// <summary>\n\t/// Gets window handle as <see cref=\"wnd\"/>.\n\t/// </summary>\n\t/// <param name=\"t\">A <c>Control</c> or <c>Form</c> etc. Cannot be <c>null</c>.</param>\n\t/// <param name=\"create\">\n\t/// Create handle if still not created. Default <c>false</c> (return <c>default(wnd)</c>).\n\t/// Unlike <see cref=\"System.Windows.Forms.Control.CreateControl\"/>, creates handle even if invisible. Does not create child control handles.\n\t/// </param>\n\t/// <remarks>\n\t/// Should be called in control's thread. Calls <see cref=\"System.Windows.Forms.Control.IsHandleCreated\"/> and <see cref=\"System.Windows.Forms.Control.Handle\"/>.\n\t/// </remarks>\n\tpublic static wnd Hwnd(this System.Windows.Forms.Control t, bool create = false)\n\t\t=> create || t.IsHandleCreated ? new wnd(t.Handle) : default;\n\t\n\t#endregion\n\t\n\t#region System.Drawing\n\t\n\t/// <summary>\n\t/// Draws inset or outset rectangle.\n\t/// </summary>\n\t/// <param name=\"t\"></param>\n\t/// <param name=\"pen\">Pen with integer width and default alignment.</param>\n\t/// <param name=\"r\"></param>\n\t/// <param name=\"outset\">Draw outset.</param>\n\t/// <remarks>\n\t/// Calls <see cref=\"Graphics.DrawRectangle\"/> with arguments corrected so that it draws inside or outside <i>r</i>. Does not use <see cref=\"System.Drawing.Drawing2D.PenAlignment\"/>, it is unreliable.\n\t/// </remarks>\n\tpublic static void DrawRectangleInset(this Graphics t, Pen pen, RECT r, bool outset = false) {\n\t\tif (r.NoArea) return;\n\t\t//pen.Alignment = PenAlignment.Inset; //no. Eg ignored if 1 pixel width.\n\t\t//\tMSDN: \"A Pen that has its alignment set to Inset will yield unreliable results, sometimes drawing in the inset position and sometimes in the centered position.\".\n\t\tvar r0 = r;\n\t\tint w = (int)pen.Width, d = w / 2;\n\t\tr.left += d; r.top += d;\n\t\tr.right -= d = w - d; r.bottom -= d;\n\t\tif (outset) r.Inflate(w, w);\n\t\tif (!r.NoArea) {\n\t\t\tt.DrawRectangle(pen, r);\n\t\t} else { //DrawRectangle does not draw if width or height 0, even if pen alignment is Outset\n\t\t\tt.FillRectangle(pen.Brush, r0); //never mind dash style etc\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Draws inset rectangle of specified pen color and width.\n\t/// </summary>\n\t/// <remarks>\n\t/// Creates pen and calls other overload.\n\t/// </remarks>\n\tpublic static void DrawRectangleInset(this Graphics t, Color penColor, int penWidth, RECT r, bool outset = false) {\n\t\tusing var pen = new Pen(penColor, penWidth);\n\t\tDrawRectangleInset(t, pen, r, outset);\n\t}\n\t\n\t/// <summary>\n\t/// Creates solid brush and calls <see cref=\"Graphics.FillRectangle\"/>.\n\t/// </summary>\n\tpublic static void FillRectangle(this Graphics t, Color color, RECT r) {\n\t\tusing var brush = new SolidBrush(color);\n\t\tt.FillRectangle(brush, r);\n\t}\n\t\n\t/// <summary>\n\t/// Calls <c>b.LockBits</c> in ctor and <c>b.UnlockBits</c> in <c>Dispose</c>.\n\t/// </summary>\n\tinternal struct BitmapData_ : IDisposable {\n\t\tBitmap _b;\n\t\tBitmapData _d;\n\t\t\n\t\tpublic BitmapData_(Bitmap b, ImageLockMode mode, PixelFormat? pf = null) {\n\t\t\t_b = b;\n\t\t\t_d = _b.LockBits(new(default, b.Size), mode, pf ?? _b.PixelFormat);\n\t\t}\n\t\t\n\t\tpublic BitmapData_(Bitmap b, Rectangle r, ImageLockMode mode, PixelFormat? pf = null) {\n\t\t\t_b = b;\n\t\t\t_d = _b.LockBits(r, mode, pf ?? _b.PixelFormat);\n\t\t}\n\t\t\n\t\tpublic void Dispose() {\n\t\t\t_b?.UnlockBits(_d);\n\t\t\t_b = null;\n\t\t\t_d = null;\n\t\t}\n\t\t\n\t\tpublic int Width => _d.Width;\n\t\tpublic int Height => _d.Height;\n\t\tpublic int Stride => _d.Stride;\n\t\tpublic PixelFormat PixelFormat => _d.PixelFormat;\n\t\tpublic IntPtr Scan0 => _d.Scan0;\n\t}\n\t\n\t/// <summary>\n\t/// Creates a <c>BitmapData_</c> object that calls <c>b.LockBits</c> in ctor and <c>b.UnlockBits</c> in <c>Dispose</c>.\n\t/// </summary>\n\t/// <param name=\"pf\">If <c>null</c>, uses <c>b.PixelFormat</c>.</param>\n\tinternal static BitmapData_ Data(this Bitmap b, ImageLockMode mode, PixelFormat? pf = null)\n\t\t=> new BitmapData_(b, mode, pf);\n\t\n\t/// <summary>\n\t/// Creates a <c>BitmapData_</c> object that calls <c>b.LockBits</c> in ctor and <c>b.UnlockBits</c> in <c>Dispose</c>.\n\t/// </summary>\n\t/// <param name=\"pf\">If <c>null</c>, uses <c>b.PixelFormat</c>.</param>\n\tinternal static BitmapData_ Data(this Bitmap b, Rectangle r, ImageLockMode mode, PixelFormat? pf = null)\n\t\t=> new BitmapData_(b, r, mode, pf);\n\t\n\t#endregion\n\t\n\t#region other\n\t\n\t/// <summary>\n\t/// Gets a value from a subkey of this registry key.\n\t/// </summary>\n\t/// <param name=\"t\"></param>\n\t/// <param name=\"subkey\">The name or relative path of the subkey.</param>\n\t/// <param name=\"name\">The name of the value to retrieve.</param>\n\t/// <param name=\"defaultValue\">The value to return if <i>subkey</i> or <i>name</i> does not exist.</param>\n\t/// <param name=\"options\"></param>\n\t/// <returns>The value associated with <i>name</i>, or <i>defaultValue</i> if <i>subkey</i> or <i>name</i> not found.</returns>\n\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"RegistryKey.OpenSubKey(string)\"/> and <see cref=\"RegistryKey.GetValue(string?, object?, RegistryValueOptions)\"/>.</exception>\n\t/// <remarks>\n\t/// Calls <see cref=\"RegistryKey.OpenSubKey(string)\"/> and <see cref=\"RegistryKey.GetValue(string?, object?, RegistryValueOptions)\"/>.\n\t/// </remarks>\n\tpublic static object GetValue2(this RegistryKey t, string subkey, string name, object defaultValue = null, RegistryValueOptions options = default) {\n\t\tusing var k = t.OpenSubKey(subkey);\n\t\tif (k == null) return defaultValue;\n\t\treturn k.GetValue(name, defaultValue, options);\n\t\t\n\t\t//tested: RegGetValue same speed.\n\t}\n\t\n\t/// <summary>\n\t/// <c>=> t.GetAwaiter().GetResult();</c>\n\t/// </summary>\n\tinternal static T Result_<T>(this Task<T> t) => t.GetAwaiter().GetResult();\n\t\n\t#endregion\n}\n"
  },
  {
    "path": "Au/Ext/ExtWpf.cs",
    "content": "using System.Windows;\nusing System.Windows.Controls.Primitives;\nusing System.Windows.Interop;\nusing System.Windows.Controls;\nusing System.Windows.Media;\nusing System.Windows.Threading;\nusing System.Windows.Data;\nusing System.Windows.Automation;\nusing System.Windows.Automation.Peers;\nusing System.Windows.Automation.Provider;\nusing System.Windows.Input;\n\nnamespace Au.Types;\n\n/// <summary>\n/// Adds extension methods for some WPF classes.\n/// </summary>\npublic static class ExtWpf {\n\t/// <summary>\n\t/// Gets native window handle of this <see cref=\"Window\"/> or <see cref=\"Popup\"/>, or container window handle of this child object.\n\t/// </summary>\n\t/// <returns><c>default(wnd)</c> if:\n\t/// <br/>• called before creating or after closing real window;\n\t/// <br/>• failed;\n\t/// <br/>• <i>t</i> is <c>null</c>.</returns>\n\t/// <param name=\"t\"></param>\n\tpublic static wnd Hwnd(this DependencyObject t) {\n\t\tbool isPopup = false;\n\t\tswitch (t) {\n\t\tcase null: return default;\n\t\tcase Window w: return (wnd)new WindowInteropHelper(w).Handle; //FromDependencyObject works too, but this is usually slightly faster\n\t\tcase Popup p: t = p.Child; if (t == null) return default; isPopup = true; break; //FromVisual(Popup) returns null, FromDependencyObject too\n\t\t}\n\t\tif (PresentationSource.FromDependencyObject(t) is HwndSource hs) return (wnd)hs.Handle;\n\t\tif (isPopup) return default;\n\t\treturn Hwnd(LogicalTreeHelper.GetParent(t));\n\t}\n\t//rejected: notPopup. Not useful.\n\t///// <summary>\n\t///// Gets window handle of this <c>Window</c>, <c>Popup</c> or container window handle of this child object.\n\t///// Returns <c>default(wnd)</c> if: called before creating real window; failed; <i>t</i> is <c>null</c>.\n\t///// </summary>\n\t///// <param name=\"t\"></param>\n\t///// <param name=\"notPopup\">If this is <c>Popup</c> or in a <c>Popup</c>, get handle of popup's owner <c>Window</c>.</param>\n\t//public static wnd Hwnd(this DependencyObject t, bool notPopup = false)\n\t//{\n\t//\tswitch(t) {\n\t//\tcase null: return default;\n\t//\tcase Window w: return (wnd)new WindowInteropHelper(w).Handle; //FromDependencyObject works too, but this is usually slightly faster\n\t//\tcase Popup p when !notPopup: t = p.Child; if(t == null) return default; break; //FromVisual(Popup) returns null; or maybe owner window, not tested.\n\t//\t}\n\t//\tif(notPopup) {\n\t//\t\tvar w = Window.GetWindow(t); if(w == null) return default; //if Popup or in Popup, gets owner WIndow\n\t//\t\treturn (wnd)new WindowInteropHelper(w).Handle;\n\t//\t}\n\t//\tif(PresentationSource.FromDependencyObject(t) is HwndSource hs) return (wnd)hs.Handle;\n\t//\treturn default;\n\t//}\n\t\n\t/// <summary>\n\t/// Gets <c>IWin32Window</c> of this window for <c>System.Windows.Forms</c> functions like <c>Form.ShowDialog</c> and <c>ColorDialog.ShowDialog</c>.\n\t/// </summary>\n\tpublic static System.Windows.Forms.IWin32Window FormOwner(this Window t) {\n\t\tvar nw = new System.Windows.Forms.NativeWindow();\n\t\tnw.AssignHandle(t.Hwnd().Handle);\n\t\treturn nw;\n\t}\n\t\n\t/// <summary>\n\t/// Enumerates visual descendant objects, including parts of composite controls, and calls callback function <i>f</i> for each.\n\t/// When <i>f</i> returns <c>true</c>, stops and returns that object. Returns <c>null</c> if <i>f</i> does not return <c>true</c>.\n\t/// </summary>\n\tpublic static DependencyObject FindVisualDescendant(this DependencyObject t, Func<DependencyObject, bool> f, bool orSelf = false) {\n\t\tif (orSelf && f(t)) return t;\n\t\tfor (int i = 0, n = VisualTreeHelper.GetChildrenCount(t); i < n; i++) {\n\t\t\tvar v = VisualTreeHelper.GetChild(t, i);\n\t\t\tif (f(v)) return v;\n\t\t\tv = FindVisualDescendant(v, f);\n\t\t\tif (v != null) return v;\n\t\t}\n\t\treturn null;\n\t}\n\t\n\t/// <summary>\n\t/// Enumerates visual descendant objects, including parts of composite controls.\n\t/// </summary>\n\tpublic static IEnumerable<DependencyObject> VisualDescendants(this DependencyObject t) {\n\t\tfor (int i = 0, n = VisualTreeHelper.GetChildrenCount(t); i < n; i++) {\n\t\t\tvar v = VisualTreeHelper.GetChild(t, i);\n\t\t\tyield return v;\n\t\t\tforeach (var k in VisualDescendants(v)) yield return k;\n\t\t\t//TODO3: now creates much garbage if tree is big.\n\t\t\t//\tSee https://stackoverflow.com/a/30441479/2547338.\n\t\t\t//\tSee ExtMisc.Descendants. But it cannot be used here because VisualTreeHelper does not give an IEnumerable.\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Enumerates logical descendant objects, including parts of composite controls.\n\t/// </summary>\n\tpublic static IEnumerable LogicalDescendants(this DependencyObject t) {\n\t\t//foreach (var v in LogicalTreeHelper.GetChildren(t)) {\n\t\t//\tyield return v;\n\t\t//\tif (v is DependencyObject d)\n\t\t//\t\tforeach (var k in LogicalDescendants(d)) yield return k;\n\t\t//}\n\t\treturn LogicalTreeHelper.GetChildren(t).Descendants_(o => o is DependencyObject d ? LogicalTreeHelper.GetChildren(d) : null);\n\t}\n\t\n\t/// <summary>\n\t/// Gets visual ancestors (<see cref=\"VisualTreeHelper.GetParent\"/>).\n\t/// </summary>\n\t/// <param name=\"t\"></param>\n\t/// <param name=\"andThis\">Include this object.</param>\n\t/// <param name=\"last\">Last ancestor to get.</param>\n\tpublic static IEnumerable<DependencyObject> VisualAncestors(this DependencyObject t, bool andThis = false, object last = null) {\n\t\tfor (var v = t; v != null; v = VisualTreeHelper.GetParent(v)) {\n\t\t\tif (!andThis) { andThis = true; continue; }\n\t\t\tyield return v;\n\t\t\tif (v == last) yield break;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Calls callback function <i>f</i> for each visual ancestor (<see cref=\"VisualTreeHelper.GetParent\"/>), and returns the ancestor for which <i>f</i> returns <c>true</c>.\n\t/// Also can return <i>last</i> or <c>null</c>.\n\t/// </summary>\n\t/// <param name=\"t\"></param>\n\t/// <param name=\"andThis\">Include this object.</param>\n\t/// <param name=\"f\"></param>\n\t/// <param name=\"last\">When found this ancestor, stop and return <i>last</i> if <i>andLast</i> <c>true</c> or <c>null</c> if <c>false</c>.</param>\n\t/// <param name=\"andLast\">If <i>last</i> found, return <i>last</i> instead of <c>null</c>.</param>\n\t/// <seealso cref=\"ItemsControl.ContainerFromElement\"/>\n\tpublic static DependencyObject FindVisualAncestor(this DependencyObject t, bool andThis, Func<DependencyObject, bool> f, object last, bool andLast) {\n\t\tfor (var v = t; v != null; v = VisualTreeHelper.GetParent(v)) {\n\t\t\tif (!andThis) { andThis = true; continue; }\n\t\t\tif (f(v)) return v;\n\t\t\tif (v == last) return andLast ? v : null;\n\t\t}\n\t\treturn null;\n\t}\n\t\n\t/// <summary>\n\t/// Returns the nearest visual ancestor (<see cref=\"VisualTreeHelper.GetParent\"/>) of type <i>T</i>.\n\t/// Also can return <i>last</i> or <c>null</c>.\n\t/// </summary>\n\t/// <param name=\"t\"></param>\n\t/// <param name=\"andThis\">Include this object.</param>\n\t/// <param name=\"last\">When found this ancestor, stop and return <i>last</i> if <i>andLast</i> <c>true</c> or <c>null</c> if <c>false</c>.</param>\n\t/// <param name=\"andLast\">If <i>last</i> found, return <i>last</i> instead of <c>null</c>.</param>\n\t/// <seealso cref=\"ItemsControl.ContainerFromElement\"/>\n\tpublic static DependencyObject FindVisualAncestor<T>(this DependencyObject t, bool andThis, object last, bool andLast) where T : DependencyObject {\n\t\tfor (var v = t; v != null; v = VisualTreeHelper.GetParent(v)) {\n\t\t\tif (!andThis) { andThis = true; continue; }\n\t\t\tif (v is T r1) return r1;\n\t\t\tif (v == last) return andLast ? v : null;\n\t\t}\n\t\treturn null;\n\t}\n\t\n\t/// <summary>\n\t/// Gets rectangle of this element in screen coordinates.\n\t/// </summary>\n\t/// <returns><c>default(RECT)</c> if this is an invisible element (but not <see cref=\"Window\"/>) or if fails.</returns>\n\tpublic static RECT RectInScreen(this FrameworkElement t) {\n\t\tif (t is Window w) return w.Hwnd().Rect; //else would be incorrect: x/y of client area, width/height of window\n\t\tif (t.IsVisible) {\n\t\t\ttry {\n\t\t\t\tPoint p1 = t.PointToScreen(default), p2 = t.PointToScreen(new Point(t.ActualWidth, t.ActualHeight));\n\t\t\t\treturn RECT.FromLTRB(p1.X.ToInt(), p1.Y.ToInt(), p2.X.ToInt(), p2.Y.ToInt());\n\t\t\t}\n\t\t\tcatch (Exception e1) { Debug_.Print(e1); }\n\t\t}\n\t\treturn default;\n\t}\n\n\t/// <summary>\n\t/// Sets <see cref=\"Visibility\"/> = <c>Hidden</c> or <c>Visible</c>.\n\t/// </summary>\n\tinternal static void Hide_(this UIElement t, bool hide) {\n\t\tt.Visibility = hide ? Visibility.Hidden : Visibility.Visible;\n\t}\n\n\t/// <summary>\n\t/// Sets <see cref=\"Visibility\"/> = <c>Collapsed</c> or <c>Visible</c>.\n\t/// </summary>\n\tinternal static void Collapse_(this UIElement t, bool collapse) {\n\t\tt.Visibility = collapse ? Visibility.Collapsed : Visibility.Visible;\n\t}\n\t\n\t/// <summary>\n\t/// Sets UI Automation name.\n\t/// </summary>\n\tpublic static void UiaSetName(this DependencyObject t, string name) {\n\t\tSystem.Windows.Automation.AutomationProperties.SetName(t, name);\n\t}\n\t\n\t//rejected, FBC. Looks not good. Better .IsChecked == true.\n\t/// <summary>\n\t/// Returns <c>true</c> if <c>IsChecked == true</c>.\n\t/// </summary>\n\t[EditorBrowsable(EditorBrowsableState.Never)]\n\t//[Obsolete(\"use code IsChecked == true\")]\n\tpublic static bool True(this CheckBox t) => t.IsChecked == true;\n\t\n#if true\n\t//TODO3: does not work if this is called in ctor and caller sets Title afterwards.\n\tstatic unsafe void _Move(Window t, int x, int y, in RECT r, bool andSize) {\n\t\tvar wstate = t.WindowState;\n\t\tif (t.IsLoaded) {\n\t\t\tvar w = t.Hwnd();\n\t\t\tif (w.Is0) throw new ObjectDisposedException(\"Window\");\n\t\t\tif (wstate != WindowState.Normal) t.WindowState = WindowState.Normal;\n\t\t\tif (andSize) w.MoveL(r); else w.MoveL(x, y);\n\t\t} else {\n\t\t\t//tested: don't need this for Popup. Its PlacementRectangle can use physical pixels.\n\t\t\t\n\t\t\tt.WindowStartupLocation = WindowStartupLocation.Manual;\n\t\t\tif (wstate == WindowState.Minimized) t.ShowActivated = false;\n\t\t\t\n\t\t\tbool maxInactive = wstate is WindowState.Maximized && !t.ShowActivated;\n\t\t\tif (maxInactive) t.WindowState = WindowState.Normal; //WPF would throw exception, although it's easy to create maximized inactive window with CreateWindowEx\n\t\t\t\n\t\t\tWindowsHook.ThreadCbt(k => {\n\t\t\t\tif (k.code == HookData.CbtEvent.CREATEWND) {\n\t\t\t\t\tvar c = k.CreationInfo->lpcs;\n\t\t\t\t\tif (!c->style.Has(WS.CHILD)) {\n\t\t\t\t\t\tvar name = c->Name;\n\t\t\t\t\t\tif (name.Length > 25 && name.StartsWith(\"m8KFOuCJOUmjziONcXEi3A \")) {\n\t\t\t\t\t\t\tk.hook.Dispose();\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tvar s = name[23..].ToString();\n\t\t\t\t\t\t\tif (name[^1] == ';') {\n\t\t\t\t\t\t\t\tc->x = s.ToInt(0, out int e); c->y = s.ToInt(e);\n\t\t\t\t\t\t\t} else if (RECT.TryParse(s, out var r)) {\n\t\t\t\t\t\t\t\tc->x = r.left; c->y = r.top; c->cx = r.Width; c->cy = r.Height;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else { //didn't detect the window? Because unhooks when detects.\n\t\t\t\t\tDebug_.Print($\"{k.code} {k.Hwnd}\");\n\t\t\t\t\t//Debug_.PrintIf(k.code != HookData.CbtEvent.SETFOCUS, $\"{k.code} {k.Hwnd}\"); //sometimes SETFOCUS before CREATEWND, and it is bad\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t});\n\t\t\t\n\t\t\tt.Left = double.NaN;\n\t\t\tt.Top = double.NaN;\n\t\t\tif (andSize) {\n\t\t\t\tt.Width = double.NaN;\n\t\t\t\tt.Height = double.NaN;\n\t\t\t}\n\t\t\t\n\t\t\t//temporarily change Title. I didn't find other ways to recognize the window in the hook proc. Also in title we can pass r or x y.\n\t\t\tstring title = t.Title, s;\n\t\t\tif (andSize) s = \"m8KFOuCJOUmjziONcXEi3A \" + r.ToStringSimple(); else s = $\"m8KFOuCJOUmjziONcXEi3A {x} {y};\";\n\t\t\tt.Title = s;\n\t\t\t//Need to restore Title ASAP.\n\t\t\t//\tIn CBT hook cannot change window name in any way.\n\t\t\t//\tThe first opportunity is WM_CREATE, it's before WPF events, but cannot set Title property there.\n\t\t\t//\tThe sequence of .NET events depends on Window properties etc:\n\t\t\t//\t\tDefault: IsVisibleChanged, HwndSource.AddSourceChangedHandler, SourceInitialized, Loaded. And several unreliable events inbetween.\n\t\t\t//\t\tSizeToContent: IsVisibleChanged, HwndSource.AddSourceChangedHandler, Loaded, SourceInitialized.\n\t\t\t//\t\tWindowInteropHelper(w).EnsureHandle(): SourceInitialized (before ShowX), IsVisibleChanged, HwndSource.AddSourceChangedHandler, Loaded.\n\t\t\t//\t\tWindow without controls: Initialized, ....\n\t\t\tSourceChangedEventHandler eh = null;\n\t\t\teh = (_, _) => {\n\t\t\t\tHwndSource.RemoveSourceChangedHandler(t, eh);\n\t\t\t\tt.Title = title;\n\t\t\t\t//if (wstate == WindowState.Normal && !t.ShowActivated) t.Hwnd().ZorderTop(); //it seems don't need it\n\t\t\t\t\n\t\t\t\tif (maxInactive) t.Loaded += (_, _) => _MaximizeNoActivate(t.Hwnd());\n\t\t\t};\n\t\t\tHwndSource.AddSourceChangedHandler(t, eh);\n\t\t\t\n\t\t\tstatic void _MaximizeNoActivate(wnd w) {\n\t\t\t\t//HACK\n\t\t\t\tw.GetWindowPlacement_(out var p, false); //without this, later restored rect is like max\n\t\t\t\tw.SetStyle(WS.MAXIMIZE, WSFlags.Add);\n\t\t\t\tvar r = screen.of(w).WorkArea;\n\t\t\t\tw.SetWindowPos(SWPFlags.FRAMECHANGED | SWPFlags.NOACTIVATE | SWPFlags.NOZORDER, r.left, r.top, r.Width, r.Height);\n\t\t\t\t//p.showCmd = 3; w.SetWindowPlacement_(ref p, false);\n#if DEBUG\n\t\t\t\tw.GetWindowPlacement_(out var p2, false);\n\t\t\t\tDebug_.PrintIf(p2.rcNormalPosition != p.rcNormalPosition || p2.showCmd != 3);\n#endif\n\t\t\t}\n\t\t}\n\t}\n#elif true //does not change Title, but I don't like creating window handle before showing window\n\tstatic void _Move(Window t, int x, int y, in RECT r, bool andSize) {\n\t\tvar wstate=t.WindowState;\n\t\tif(wstate!=WindowState.Normal) t.WindowState=WindowState.Normal;\n\t\tif(t.IsLoaded) {\n\t\t\tvar w=t.Hwnd();\n\t\t\tif(w.Is0) throw new ObjectDisposedException(\"Window\");\n\t\t\tif(andSize) w.MoveL(r); else w.MoveL(x, y);\n\t\t} else {\n\t\t\tvar scrn=screen.of(new POINT(x, y));\n\t\t\tvar si=scrn.GetInfo();\n\t\t\tvar rs=si.workArea;\n\t\t\n\t\t\tif(andSize) {\n\t\t\t\tx=r.left; y=r.top;\n\t\t\t\tvar stc=t.SizeToContent;\n\t\t\t\tif(stc!=SizeToContent.WidthAndHeight) {\n\t\t\t\t\tdouble f=96d/scrn.Dpi;\n\t\t\t\t\tif(!stc.Has(SizeToContent.Width)) t.Width=r.Width*f;\n\t\t\t\t\tif(!stc.Has(SizeToContent.Height)) t.Height=r.Height*f;\n\t\t\t\t}\n\t\t\t}\n\t\t\n\t\t\tt.WindowStartupLocation=WindowStartupLocation.Manual;\n\t\t\tt.Left=double.NaN; t.Top=double.NaN;\n\t\t\tt.Loaded+=(_,_)=> {\n\t\t\t\tvar w=t.Hwnd();\n\t\t\t\tvar rw=w.Rect;\n\t\t\t\tx=Math.Clamp(x, rs.left, Math.Max(rs.right-rw.Width, rs.left));\n\t\t\t\ty=Math.Clamp(y, rs.top, Math.Max(rs.bottom-rw.Height, rs.top));\n\t\t\t\tw.MoveL(x, y);\n\t\t\t\n\t\t\t\tif(wstate!=WindowState.Normal) {\n\t\t\t\t\tif(wstate==WindowState.Maximized) t.SizeToContent=SizeToContent.Manual;\n\t\t\t\t\tt.WindowState=wstate;\n\t\t\t\t} else if(!t.ShowActivated) {\n\t\t\t\t\tw.ZorderTop();\n\t\t\t\t}\n\t\t\t};\n\t\t\n\t\t\tif(!si.isPrimary) {\n\t\t\t\tusing var h=WindowsHook.ThreadCbt(d=> {\n\t\t\t\t\tif(d.code== HookData.CbtEvent.CREATEWND) unsafe {\n\t\t\t\t\t\tvar w=d.CreationInfo(out var c, out _);\n\t\t\t\t\t\tif(c->style!=0 && !c->style.Has(WS.CHILD)) { //note: this does not work if ShowInTaskbar = false. Then WPF creates a \"Hidden Window\" before, even if owner specified.\n\t\t\t\t\t\t\tprint.it(c->x, c->y, c->cx, c->cy, c->hwndParent, c->style, c->lpszClass, c->lpszName);\n\t\t\t//\t\t\t\td.hook.Unhook();\n\t\t\t\t\t\t\tc->x=rs.left; c->y=rs.top;\n\t\t\t\t\t\t\t//the hook receives 2 windows. At first the true window and then some other HwndWrapper* with 0 x y cx cy style parent. The second is never visibe.\n\t\t\t\t\t\t\t//We use the 'c->style!=0' to ignore it. The real window always has some styles. There is no 100% reliable and clean way to recognize the real window.\n\t\t\t\t\t\t\t//Don't unhook, because future .NET versions may create more windows, maybe some before the real window. Or in some conditions.\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn false;\n\t\t\t\t});\n\n\t\t\t\tnew WindowInteropHelper(t).EnsureHandle();\n\t\t\t}\n\t\t}\n\t}\n#else //does not work well when maximized, per-monitor DPI, etc\n\tstatic void _Move(Window t, int x, int y, in RECT r, bool andSize) {\n\t\tvar wstate = t.WindowState;\n\t\tif (wstate != WindowState.Normal) t.WindowState = WindowState.Normal;\n\t\tif (t.IsLoaded) {\n\t\t\tvar w = t.Hwnd();\n\t\t\tif (w.Is0) throw new ObjectDisposedException(\"Window\");\n\t\t\tif (andSize) w.MoveL(r); else w.MoveL(x, y);\n\t\t} else {\n\t\t\t//tested: don't need this for Popup. Its PlacementRectangle uses physical pixels.\n\n\t\t\tif (andSize) {\n\t\t\t\tx = r.left; y = r.top;\n\t\t\t\tvar stc = t.SizeToContent;\n\t\t\t\tif (stc != SizeToContent.WidthAndHeight) {\n\t\t\t\t\tdouble f = 96d / screen.of(x, y).Dpi;\n\t\t\t\t\tif (!stc.Has(SizeToContent.Width)) t.Width = r.Width * f;\n\t\t\t\t\tif (!stc.Has(SizeToContent.Height)) t.Height = r.Height * f;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tt.WindowStartupLocation = WindowStartupLocation.Manual;\n\t\t\tt.Left = double.NaN; t.Top = double.NaN; //default location, somewhere near top-left of primary screen or owner's screen\n\t\t\tif (wstate == WindowState.Minimized) t.ShowActivated = false;\n\n\t\t\tt.SourceInitialized += (_, _) => {\n\t\t\t\tvar w = t.Hwnd();\n\t\t\t\tvar v = screen.of(x, y).Info;\n\t\t\t\tvar rs = v.workArea;\n\t\t\t\tif (!v.isPrimary) {\n\t\t\t\t\tusing var h = WindowsHook.ThreadCbt(k => k.code == HookData.CbtEvent.ACTIVATE); //workaround for WPF bug: activates window when DPI changes\n\t\t\t\t\tw.MoveL(rs.left, rs.top); //let DPI-scale\n\t\t\t\t}\n\t\t\t\tvar rw = w.Rect;\n\t\t\t\tx = Math.Clamp(x, rs.left, Math.Max(rs.right - rw.Width, rs.left));\n\t\t\t\ty = Math.Clamp(y, rs.top, Math.Max(rs.bottom - rw.Height, rs.top));\n\t\t\t\tw.MoveL(x, y);\n\t\t\t\t//speed: when moving to a screen with different DPI, total time is same.\n\n\t\t\t\tif (wstate != WindowState.Normal) {\n\t\t\t\t\tif (wstate == WindowState.Maximized) t.SizeToContent = SizeToContent.Manual;\n\t\t\t\t\tt.WindowState = wstate;\n\t\t\t\t} else if (!t.ShowActivated) {\n\t\t\t\t\tw.ZorderTop();\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t}\n#endif\n\t\n\t/// <summary>\n\t/// Sets window startup location before showing it first time. Also can move already loaded window.\n\t/// </summary>\n\t/// <param name=\"t\"></param>\n\t/// <param name=\"x\">X coordinate in screen. Physical pixels.</param>\n\t/// <param name=\"y\">Y coordinate in screen. Physical pixels.</param>\n\t/// <remarks>\n\t/// The unit is physical pixels. WPF provides <c>Left</c> and <c>Top</c> properties, but the unit is logical pixels, therefore cannot set exact location on high DPI screens, especially if there are multiple screens with different DPI.\n\t/// \n\t/// If the window is already loaded, just ensures it is not maximized/minimized and calls <see cref=\"wnd.MoveL\"/>.\n\t/// \n\t/// Else sets window location for normal state (not minimized/maximized). Temporarily changes <c>Title</c>. Clears <c>WindowStartupLocation</c>, <c>Left</c>, <c>Top</c>. Clears <c>ShowActivated</c> if minimized. Does not change <c>SizeToContent</c>.\n\t/// </remarks>\n\tpublic static void SetXY(this Window t, int x, int y) => _Move(t, x, y, default, false);\n\t\n\t/// <summary>\n\t/// Sets window startup rectangle (location and size) before showing it first time. Also can move/resize already loaded window.\n\t/// </summary>\n\t/// <param name=\"t\"></param>\n\t/// <param name=\"r\">Rectangle in screen. Physical pixels.</param>\n\t/// <remarks>\n\t/// The unit is physical pixels. WPF provides <c>Left</c>, <c>Top</c>, <c>Width</c> and <c>Height</c> properties, but the unit is logical pixels, therefore cannot set exact rectangle on high DPI screens, especially if there are multiple screens with different DPI.\n\t/// \n\t/// If the window is already loaded, just ensures it is not maximized/minimized and calls <see cref=\"wnd.MoveL\"/>.\n\t/// \n\t/// Else sets window rectangle for normal state (not minimized/maximized). Temporarily changes <c>Title</c>. Clears <c>WindowStartupLocation</c>, <c>Left</c>, <c>Top</c>, <c>Width</c>, <c>Height</c>. Clears <c>ShowActivated</c> if minimized. Does not change <c>SizeToContent</c>.\n\t/// </remarks>\n\tpublic static void SetRect(this Window t, RECT r) => _Move(t, 0, 0, r, true);\n\t\n\t/// <summary>\n\t/// Inserts row and adjusts row indices of children that are in other rows.\n\t/// </summary>\n\tpublic static void InsertRow(this Grid t, int index, RowDefinition d) {\n\t\t_GridShift(t, true, index, 1);\n\t\tt.RowDefinitions.Insert(index, d);\n\t}\n\t\n\t/// <summary>\n\t/// Inserts column and adjusts column indices of children that are in other columns.\n\t/// </summary>\n\tpublic static void InsertColumn(this Grid t, int index, ColumnDefinition d) {\n\t\t_GridShift(t, false, index, 1);\n\t\tt.ColumnDefinitions.Insert(index, d);\n\t}\n\t\n\t/// <summary>\n\t/// Removes row and adjusts row indices of children that are in other rows.\n\t/// </summary>\n\t/// <param name=\"t\"></param>\n\t/// <param name=\"index\"></param>\n\t/// <param name=\"removeChildren\">Remove children that are in that row.</param>\n\tpublic static void RemoveRow(this Grid t, int index, bool removeChildren) {\n\t\tif (removeChildren) _GridRemoveRowColChildren(t, true, index);\n\t\tt.RowDefinitions.RemoveAt(index);\n\t\t_GridShift(t, true, index, -1);\n\t}\n\t\n\t/// <summary>\n\t/// Removes column and adjusts column indices of children that are in other columns.\n\t/// </summary>\n\t/// <param name=\"t\"></param>\n\t/// <param name=\"index\"></param>\n\t/// <param name=\"removeChildren\">Remove children that are in that column.</param>\n\tpublic static void RemoveColumn(this Grid t, int index, bool removeChildren) {\n\t\tif (removeChildren) _GridRemoveRowColChildren(t, false, index);\n\t\tt.ColumnDefinitions.RemoveAt(index);\n\t\t_GridShift(t, false, index, -1);\n\t}\n\t\n\t/// <summary>\n\t/// Removes a child element and its row from this grid. Adjusts row indices of children that are in other rows.\n\t/// </summary>\n\t/// <param name=\"t\"></param>\n\t/// <param name=\"e\"></param>\n\t/// <param name=\"removeOtherElements\">Also remove other elements that are in that row.</param>\n\tpublic static void RemoveRow(this Grid t, UIElement e, bool removeOtherElements) {\n\t\tint i = Grid.GetRow(e);\n\t\t_GridRemoveChild(t, e);\n\t\tRemoveRow(t, i, removeOtherElements);\n\t}\n\t\n\t/// <summary>\n\t/// Removes a child element and its column from this grid. Adjusts column indices of children that are in other columns.\n\t/// </summary>\n\t/// <param name=\"t\"></param>\n\t/// <param name=\"e\"></param>\n\t/// <param name=\"removeOtherElements\">Also remove other elements that are in that column.</param>\n\tpublic static void RemoveColumn(this Grid t, UIElement e, bool removeOtherElements) {\n\t\tint i = Grid.GetColumn(e);\n\t\t_GridRemoveChild(t, e);\n\t\tRemoveColumn(t, i, removeOtherElements);\n\t}\n\t\n\tstatic void _GridShift(Grid g, bool rows, int startIndex, int shift) {\n\t\tif (startIndex >= (rows ? g.RowDefinitions.Count : g.ColumnDefinitions.Count)) return;\n\t\tforeach (UIElement e in g.Children) {\n\t\t\tint k = rows ? Grid.GetRow(e) : Grid.GetColumn(e);\n\t\t\tif (k < startIndex) continue;\n\t\t\tk += shift;\n\t\t\tif (rows) Grid.SetRow(e, k); else Grid.SetColumn(e, k);\n\t\t}\n\t}\n\t\n\tstatic void _GridRemoveRowColChildren(Grid g, bool row, int index) {\n\t\tvar cc = g.Children;\n\t\tfor (int i = cc.Count; --i >= 0;) {\n\t\t\tvar e = cc[i];\n\t\t\tint rc = row ? Grid.GetRow(e) : Grid.GetColumn(e);\n\t\t\tif (rc == index) _GridRemoveChild(g, e);\n\t\t}\n\t}\n\t\n\tstatic void _GridRemoveChild(Grid g, UIElement e) {\n\t\tg.Children.Remove(e);\n\t\tGrid.SetRow(e, 0);\n\t\tGrid.SetColumn(e, 0);\n\t}\n\t\n\t/// <summary>\n\t/// Adds a child element in specified row/column.\n\t/// </summary>\n\tpublic static void AddChild(this Grid g, UIElement e, int row, int column, int rowSpan = 1, int columnSpan = 1) {\n\t\tGrid.SetRow(e, row);\n\t\tGrid.SetColumn(e, column);\n\t\tif (rowSpan > 1) Grid.SetRowSpan(e, rowSpan);\n\t\tif (columnSpan > 1) Grid.SetColumnSpan(e, columnSpan);\n\t\tg.Children.Add(e);\n\t}\n\t\n\t/// <summary>\n\t/// Adds one or more columns. Like <see cref=\"wpfBuilder.Columns\"/>, but does not clear existing columns.\n\t/// </summary>\n\t/// <inheritdoc cref=\"wpfBuilder.Columns\" path=\"/param\"/>\n\tpublic static void AddColumns(this Grid g, params WBGridLength[] widths) {\n\t\tforeach (var v in widths) g.ColumnDefinitions.Add(v.Column);\n\t}\n\t\n\t/// <summary>\n\t/// Adds one or more rows. Like <see cref=\"wpfBuilder.Row\"/>.\n\t/// </summary>\n\t/// <param name=\"heights\"><inheritdoc cref=\"wpfBuilder.Row\" path=\"/param[@name='height']/node()\"/></param>\n\tpublic static void AddRows(this Grid g, params WBGridLength[] heights) {\n\t\tforeach (var v in heights) g.RowDefinitions.Add(v.Row);\n\t}\n\t\n\t/// <summary>\n\t/// Gets the <c>Text</c> property. Returns <c>null</c> if it is <c>\"\"</c>.\n\t/// </summary>\n\tpublic static string TextOrNull(this TextBox t) => t.Text.NullIfEmpty_();\n\t\n\t/// <summary>\n\t/// Workaround for WPF bug: on DPI change tries to activate window.\n\t/// Call on <c>WM_DPICHANED</c> message or in <c>OnDpiChanged</c> override.\n\t/// </summary>\n\tpublic static void DpiChangedWorkaround(this Window t) => _DCW(t.Dispatcher, t.Hwnd());\n\t\n\t/// <summary>\n\t/// Workaround for WPF bug: on DPI change tries to activate window.\n\t/// Call on <c>WM_DPICHANED</c> message or in <c>OnDpiChanged</c> override. Only if top-level window.\n\t/// </summary>\n\tpublic static void DpiChangedWorkaround(this HwndSource t) => _DCW(t.Dispatcher, (wnd)t.Handle);\n\t\n\tstatic void _DCW(Dispatcher d, wnd w) {\n\t\tif (wnd.active != w) {\n\t\t\tbool wasVisible = w.IsVisible; //allow to activate when opening window in non-primary screen\n\t\t\tvar h = WindowsHook.ThreadCbt(k => k.code == HookData.CbtEvent.ACTIVATE && (wnd)k.wParam == w && (wasVisible || !w.IsVisible));\n\t\t\td.InvokeAsync(() => h.Dispose());\n\t\t}\n\t}\n\t\n\t/// <returns><c>true</c> if in <c>ShowDialog</c>, <c>false</c> if not, <c>null</c> if failed (uses reflection).</returns>\n\tinternal static bool? IsModal_(this Window t) {\n\t\ttry {\n\t\t\tvar f = typeof(Window).GetField(\"_showingAsDialog\", BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.NonPublic);\n\t\t\treturn (bool)f.GetValue(t);\n\t\t}\n\t\tcatch {\n\t\t\tDebug_.Print(\"_showingAsDialog\");\n\t\t\treturn null;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Hides the grip and/or overflow controls in this toolbar.\n\t/// Call before the toolbar is loaded.\n\t/// </summary>\n\t/// <param name=\"t\"></param>\n\t/// <param name=\"hideGrip\">Hide grip. Sets <c>SetIsLocked</c> <c>true</c>.</param>\n\t/// <param name=\"hideOverflow\">Hides the overflow button while it is disabled.</param>\n\t/// <exception cref=\"InvalidOperationException\">Loaded.</exception>\n\tpublic static void HideGripAndOverflow(this ToolBar t, bool hideGrip = true, bool hideOverflow = true) {\n\t\tif (hideGrip) ToolBarTray.SetIsLocked(t, true);\n\t\tif (hideOverflow) {\n\t\t\tif (t.IsLoaded) throw new InvalidOperationException(\"loaded\");\n\t\t\tSizeChangedEventHandler h = null;\n\t\t\th = (_, _) => {\n\t\t\t\tt.SizeChanged -= h;\n\t\t\t\tif (t.Template.FindName(\"OverflowButton\", t) is ButtonBase ob) {\n\t\t\t\t\tob.SetBinding(\n\t\t\t\t\t\tUIElement.VisibilityProperty,\n\t\t\t\t\t\tnew Binding(\"IsEnabled\") {\n\t\t\t\t\t\t\tRelativeSource = RelativeSource.Self,\n\t\t\t\t\t\t\tConverter = new BooleanToVisibilityConverter()\n\t\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t};\n\t\t\tt.SizeChanged += h;\n\t\t\t//note: in Loaded event handler randomly does not work. Somehow t still does not have the overflow button.\n\t\t}\n\t}\n\t\n\tinternal static wpfBuilder AddToolBar_(this wpfBuilder t, out ToolBarTray tt, out ToolBar tb, bool vertical = false, bool hideOverflow = false, bool controlBrush = false) {\n\t\ttt = new ToolBarTray { IsLocked = true };\n\t\tif (vertical) tt.Orientation = Orientation.Vertical;\n\t\ttb = new ToolBar();\n\t\tif (controlBrush) {\n\t\t\ttt.Background = SystemColors.ControlBrush;\n\t\t\ttb.Background = SystemColors.ControlBrush;\n\t\t}\n\t\tKeyboardNavigation.SetTabNavigation(tb, KeyboardNavigationMode.Once);\n\t\ttt.ToolBars.Add(tb);\n\t\tif (hideOverflow) tb.HideGripAndOverflow(false);\n\t\tt.Add(tt);\n\t\treturn t;\n\t}\n\t\n\t/// <summary>\n\t/// Calls <see cref=\"IInvokeProvider.Invoke\"/>, which sends a request to click the button.\n\t/// Note: it's async; more info in Remarks.\n\t/// </summary>\n\t/// <exception cref=\"ElementNotEnabledException\"></exception>\n\t/// <remarks>\n\t/// It is async (does not wait until finished). The button click event is raised after this function returns.\n\t/// \n\t/// Does not click if the button is disabled.\n\t/// \n\t/// Another way: <c>button.RaiseEvent(new RoutedEventArgs(Button.ClickEvent));</c>. It is sync, ignores disabled state, and ignores <see cref=\"ButtonBase.Command\"/>.\n\t/// </remarks>\n\tpublic static void UiaClick(this Button t) { //tested: does not work with CheckBox\n\t\tif (UIElementAutomationPeer.CreatePeerForElement(t)?.GetPattern(PatternInterface.Invoke) is IInvokeProvider ip) ip.Invoke();\n\t}\n\t\n\t/// <summary>\n\t/// Shows the window in [preview mode](xref:code_editor).\n\t/// </summary>\n\t/// <param name=\"t\"></param>\n\t/// <exception cref=\"InvalidOperationException\">Called not in preview mode.</exception>\n\t/// <remarks>\n\t/// Changes some window properties (owner window, location, activation, etc), terminates previous preview process, calls <see cref=\"Window.ShowDialog\"/>. If closed, calls <see cref=\"Environment.Exit\"/>.\n\t/// \n\t/// If called not in preview mode, calls <see cref=\"Environment.Exit\"/>.\n\t/// </remarks>\n\tpublic static void Preview(this Window t) {\n\t\twnd wMain = ScriptEditor.MainWindow(); if (wMain.Is0) Environment.Exit(0);\n\t\tif (!Environment.CommandLine.RxMatch(@\" WPF_PREVIEW (-?\\d+) (-?\\d+)$\", out var m)) Environment.Exit(0);\n\t\tint pid = m[1].Value.ToInt();\n\t\tm[2].Value.ToInt(out long time);\n\t\t\n\t\tt.Title = \"WPF preview\";\n\t\tt.ShowActivated = false;\n\t\tt.WindowStartupLocation = WindowStartupLocation.Manual;\n\t\tt.WindowState = WindowState.Normal;\n\t\tt.ShowInTaskbar = true;\n\t\tt.Topmost = true;\n\t\t\n\t\tt.Loaded += (_, _) => {\n\t\t\tvar w = t.Hwnd();\n\t\t\t//unsafe { int BOOL = 1; Api.DwmSetWindowAttribute(w, Api.DWMWINDOWATTRIBUTE.DWMWA_TRANSITIONS_FORCEDISABLED, &BOOL, 0); } //does not disable the inflate/deflate animation; and don't need, with it even better\n\t\t\t\n\t\t\t//move to App.Settings.wpfpreview_xy or to the right side of the primary screen\n\t\t\tif ((int)ScriptEditor.WndMsg_.Send(Api.WM_USER, 3) is int xy && xy != 0) {\n\t\t\t\tvar p = Math2.NintToPOINT(xy);\n\t\t\t\tw.MoveL(p.x, p.y);\n\t\t\t\tw.EnsureInScreen();\n\t\t\t} else {\n\t\t\t\tw.MoveInScreen(^1, .5f);\n\t\t\t}\n\t\t\t\n\t\t\t//_TerminatePrevious(); pid = 0; //async less flickering, especially when no animations, eg toolwindow\n\t\t\tt.Dispatcher.InvokeAsync(() => { _TerminatePrevious(); pid = 0; }, DispatcherPriority.ApplicationIdle);\n\t\t\t\n\t\t\t//rejected. See the commented out workaround below. Instead set Topmost = true and ShowInTaskbar = true.\n\t\t\t//\tThe workaround may not always work, eg for other windows.\n\t\t\t//\tAlso noticed other anomalies, eg in some cases OS activates wrong window when this window closed.\n\t\t\t//if (!WndUtil.SetOwnerWindow(w, wMain)) w.ZorderTopmost();\n\t\t\t\n\t\t\tvar hs = PresentationSource.FromVisual(t) as HwndSource;\n\t\t\ths.AddHook(_WndProc);\n\t\t\tnint _WndProc(nint hwnd, int msg, nint wp, nint lp, ref bool handled) {\n\t\t\t\tvar w = (wnd)hwnd;\n\t\t\t\tswitch (msg) {\n\t\t\t\t//case Api.WM_NCLBUTTONDOWN or Api.WM_NCRBUTTONDOWN: //the window is already active\n\t\t\t\t//case Api.WM_LBUTTONDOWN or Api.WM_RBUTTONDOWN or Api.WM_MBUTTONDOWN: //for checkboxes etc\n\t\t\t\t//\tTask.Run(() => { var p = mouse.xy; Api.SetCursorPos(p.x, p.y); });\n\t\t\t\t//\t//workaround for: if using SetOwnerWindow:\n\t\t\t\t//\t//\tWhen the window is inactive, on click nonclient (eg to close), the window hangs until mouse moved.\n\t\t\t\t//\t//\tSimilar happens after clicking a checkbox ~30 times. Maybe other controls too.\n\t\t\t\t//\tbreak;\n\t\t\t\tcase Api.WM_EXITSIZEMOVE:\n\t\t\t\t\t//save wpfpreview_xy\n\t\t\t\t\tif (!(w.IsMinimized || w.IsMaximized)) {\n\t\t\t\t\t\tvar r = w.Rect; if (r.left == 0 && r.top == 0) r.top++;\n\t\t\t\t\t\tScriptEditor.WndMsg_.Send(Api.WM_USER, 4, Math2.MakeLparam(r.left, r.top));\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t};\n\t\t\n\t\ttry {\n\t\t\tt.ShowDialog();\n\t\t}\n\t\tfinally {\n\t\t\t_TerminatePrevious();\n\t\t\tEnvironment.Exit(0);\n\t\t}\n\t\t\n\t\tvoid _TerminatePrevious() {\n\t\t\tif (pid == 0) return;\n\t\t\tif (process.getTimes(pid, out long tc, out _) && tc <= time) {\n\t\t\t\t//print.it(\"terminate\", pid, time, tc);\n\t\t\t\tprocess.terminate(pid);\n\t\t\t} else {\n\t\t\t\t//print.it(\"bad\");\n\t\t\t\t//if previous task failed before calling this func, this wasn't called and therefore an even older task may be running. Close its window.\n\t\t\t\tforeach (var w in wnd.findAll(\"WPF preview\", \"HwndWrapper[*\", \"Au.Task-*.exe\")) {\n\t\t\t\t\tif (!w.IsOfThisProcess) w.Close(noWait: true);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au/Ext/ExtXml.cs",
    "content": "\nusing System.Xml.Linq;\nusing System.Xml;\n\nnamespace Au.Types {\n\t/// <summary>\n\t/// Adds extension methods for <see cref=\"XElement\"/> and <see cref=\"XDocument\"/>.\n\t/// </summary>\n\tpublic static class ExtXml {\n\t\t/// <summary>\n\t\t/// Gets XML attribute value.\n\t\t/// If the attribute does not exist, returns <c>null</c>.\n\t\t/// If the attribute value is empty, returns <c>\"\"</c>.\n\t\t/// </summary>\n\t\tpublic static string Attr(this XElement t, XName name) {\n\t\t\treturn t.Attribute(name)?.Value;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets XML attribute value.\n\t\t/// If the attribute does not exist, returns <i>defaultValue</i>.\n\t\t/// If the attribute value is empty, returns <c>\"\"</c>.\n\t\t/// </summary>\n\t\tpublic static string Attr(this XElement t, XName name, string defaultValue) {\n\t\t\tvar x = t.Attribute(name);\n\t\t\treturn x != null ? x.Value : defaultValue;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets XML attribute value.\n\t\t/// If the attribute does not exist, sets <i>value</i> = <c>null</c> and returns <c>false</c>.\n\t\t/// </summary>\n\t\tpublic static bool Attr(this XElement t, out string value, XName name) {\n\t\t\tvalue = t.Attribute(name)?.Value;\n\t\t\treturn value != null;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets attribute value converted to <c>int</c> number.\n\t\t/// If the attribute does not exist, returns <i>defaultValue</i>.\n\t\t/// If the attribute value is empty or does not begin with a valid number, returns 0.\n\t\t/// </summary>\n\t\tpublic static int Attr(this XElement t, XName name, int defaultValue) {\n\t\t\tvar x = t.Attribute(name);\n\t\t\treturn x != null ? x.Value.ToInt() : defaultValue;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets attribute value converted to <c>int</c> number.\n\t\t/// If the attribute does not exist, sets <i>value</i> = 0 and returns <c>false</c>.\n\t\t/// If the attribute value is empty or does not begin with a valid number, sets <i>value</i> = 0 and returns <c>true</c>.\n\t\t/// </summary>\n\t\tpublic static bool Attr(this XElement t, out int value, XName name) {\n\t\t\tvar x = t.Attribute(name);\n\t\t\tif (x == null) { value = 0; return false; }\n\t\t\tvalue = x.Value.ToInt();\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets attribute value converted to <c>long</c> number.\n\t\t/// If the attribute does not exist, sets <i>value</i> = 0 and returns <c>false</c>.\n\t\t/// If the attribute value is empty or does not begin with a valid number, sets <i>value</i> = 0 and returns <c>true</c>.\n\t\t/// </summary>\n\t\tpublic static bool Attr(this XElement t, out long value, XName name) {\n\t\t\tvar x = t.Attribute(name);\n\t\t\tif (x == null) { value = 0; return false; }\n\t\t\tx.Value.ToInt(out value);\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets attribute value converted to double number.\n\t\t/// If the attribute does not exist, sets <i>value</i> = 0 and returns <c>false</c>.\n\t\t/// If the attribute value is empty or is not a valid number, sets <i>value</i> = 0 and returns <c>true</c>.\n\t\t/// </summary>\n\t\tpublic static bool Attr(this XElement t, out double value, XName name) {\n\t\t\tvar x = t.Attribute(name);\n\t\t\tif (x == null) { value = 0d; return false; }\n\t\t\tx.Value.ToNumber(out value);\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets attribute value converted to float number.\n\t\t/// If the attribute does not exist, sets <i>value</i> = 0 and returns <c>false</c>.\n\t\t/// If the attribute value is empty or is not a valid number, sets <i>value</i> = 0 and returns <c>true</c>.\n\t\t/// </summary>\n\t\tpublic static bool Attr(this XElement t, out float value, XName name) {\n\t\t\tvar x = t.Attribute(name);\n\t\t\tif (x == null) { value = 0f; return false; }\n\t\t\tx.Value.ToNumber(out value);\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets attribute value as enum type <c>T</c>.\n\t\t/// If the attribute does not exist, sets <i>value</i> = <c>default</c> and returns <c>false</c>.\n\t\t/// If the attribute value is not a valid enum member name, sets <i>value</i> = <i>default</i> and returns <c>true</c>.\n\t\t/// </summary>\n\t\tpublic static bool Attr<T>(this XElement t, out T value, XName name) where T : unmanaged, Enum {\n\t\t\tvar x = t.Attribute(name);\n\t\t\tif (x == null) { value = default; return false; }\n\t\t\tEnum.TryParse(x.Value, out value);\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if this element has the specified attribute.\n\t\t/// </summary>\n\t\tpublic static bool HasAttr(this XElement t, XName name) {\n\t\t\treturn t.Attribute(name) != null;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets the first found descendant element.\n\t\t/// </summary>\n\t\t/// <returns><c>null</c> if not found.</returns>\n\t\tpublic static XElement Desc(this XElement t, XName name) {\n\t\t\treturn t.Descendants(name).FirstOrDefault();\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Finds the first descendant element that has the specified attribute or value.\n\t\t/// </summary>\n\t\t/// <returns><c>null</c> if not found.</returns>\n\t\t/// <param name=\"t\"></param>\n\t\t/// <param name=\"name\">Element name. If <c>null</c>, can be any name.</param>\n\t\t/// <param name=\"attributeName\">Attribute name. If <c>null</c>, uses the <c>Value</c> property of the element.</param>\n\t\t/// <param name=\"attributeValue\">Attribute value (or <c>Value</c>). If <c>null</c>, can be any value.</param>\n\t\t/// <param name=\"ignoreCase\">Case-insensitive <i>attributeValue</i>.</param>\n\t\tpublic static XElement Desc(this XElement t, XName name, XName attributeName, string attributeValue = null, bool ignoreCase = false) {\n\t\t\tforeach (var el in (name != null) ? t.Descendants(name) : t.Descendants()) {\n\t\t\t\tif (_CmpAttrOrValue(el, attributeName, attributeValue, ignoreCase)) return el;\n\t\t\t}\n\t\t\treturn null;\n\t\t\t\n\t\t\t//speed: several times faster than XPathSelectElement\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Finds all descendant elements that have the specified attribute or value.\n\t\t/// </summary>\n\t\t/// <returns><c>null</c> if not found.</returns>\n\t\t/// <param name=\"t\"></param>\n\t\t/// <param name=\"name\">Element name. If <c>null</c>, can be any name.</param>\n\t\t/// <param name=\"attributeName\">Attribute name. If <c>null</c>, uses the <c>Value</c> property of the element.</param>\n\t\t/// <param name=\"attributeValue\">Attribute value (or <c>Value</c>). If <c>null</c>, can be any value.</param>\n\t\t/// <param name=\"ignoreCase\">Case-insensitive <i>attributeValue</i>.</param>\n\t\tpublic static IEnumerable<XElement> Descs(this XElement t, XName name, XName attributeName, string attributeValue = null, bool ignoreCase = false) {\n\t\t\tforeach (var el in (name != null) ? t.Descendants(name) : t.Descendants()) {\n\t\t\t\tif (_CmpAttrOrValue(el, attributeName, attributeValue, ignoreCase)) yield return el;\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets the first found direct child element that has the specified attribute or value.\n\t\t/// </summary>\n\t\t/// <returns><c>null</c> if not found.</returns>\n\t\t/// <param name=\"t\"></param>\n\t\t/// <param name=\"name\">Element name. If <c>null</c>, can be any name.</param>\n\t\t/// <param name=\"attributeName\">Attribute name. If <c>null</c>, uses the <c>Value</c> property of the element.</param>\n\t\t/// <param name=\"attributeValue\">Attribute value (or <c>Value</c>). If <c>null</c>, can be any value.</param>\n\t\t/// <param name=\"ignoreCase\">Case-insensitive <i>attributeValue</i>.</param>\n\t\tpublic static XElement Elem(this XElement t, XName name, XName attributeName, string attributeValue = null, bool ignoreCase = false) {\n\t\t\tforeach (var el in (name != null) ? t.Elements(name) : t.Elements()) {\n\t\t\t\tif (_CmpAttrOrValue(el, attributeName, attributeValue, ignoreCase)) return el;\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets all direct child elements that have the specified attribute or value.\n\t\t/// </summary>\n\t\t/// <returns><c>null</c> if not found.</returns>\n\t\t/// <param name=\"t\"></param>\n\t\t/// <param name=\"name\">Element name. If <c>null</c>, can be any name.</param>\n\t\t/// <param name=\"attributeName\">Attribute name. If <c>null</c>, uses the <c>Value</c> property of the element.</param>\n\t\t/// <param name=\"attributeValue\">Attribute value (or <c>Value</c>). If <c>null</c>, can be any value.</param>\n\t\t/// <param name=\"ignoreCase\">Case-insensitive <i>attributeValue</i>.</param>\n\t\tpublic static IEnumerable<XElement> Elems(this XElement t, XName name, XName attributeName, string attributeValue = null, bool ignoreCase = false) {\n\t\t\tforeach (var el in (name != null) ? t.Elements(name) : t.Elements()) {\n\t\t\t\tif (_CmpAttrOrValue(el, attributeName, attributeValue, ignoreCase)) yield return el;\n\t\t\t}\n\t\t}\n\t\t\n\t\tstatic bool _CmpAttrOrValue(XElement el, XName attributeName, string attributeValue = null, bool ignoreCase = false) {\n\t\t\tif (attributeName != null) {\n\t\t\t\tvar a = el.Attribute(attributeName); if (a == null) return false;\n\t\t\t\tif (attributeValue != null && !a.Value.Eq(attributeValue, ignoreCase)) return false;\n\t\t\t} else {\n\t\t\t\tif (attributeValue != null && !el.Value.Eq(attributeValue, ignoreCase)) return false;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets the first found direct child element. If not found, adds new empty child element.\n\t\t/// </summary>\n\t\t/// <returns>The found or added element.</returns>\n\t\tpublic static XElement ElemOrAdd(this XElement t, XName name) {\n\t\t\tvar e = t.Element(name);\n\t\t\tif (e == null) t.Add(e = new XElement(name));\n\t\t\treturn e;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets the first found direct child element that has the specified attribute. If not found, adds new child element with the attribute.\n\t\t/// More info: <see cref=\"Elem\"/>\n\t\t/// </summary>\n\t\t/// <returns>The found or added element.</returns>\n\t\tpublic static XElement ElemOrAdd(this XElement t, XName name, XName attributeName, string attributeValue = null, bool ignoreCase = false) {\n\t\t\tvar e = t.Elem(name, attributeName, attributeValue, ignoreCase);\n\t\t\tif (e == null) t.Add(e = new XElement(name, new XAttribute(attributeName, attributeValue)));\n\t\t\treturn e;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Returns previous sibling element or <c>null</c>.\n\t\t/// </summary>\n\t\tpublic static XElement PrevElem(this XElement t) {\n\t\t\tfor (XNode n = t.PreviousNode; n != null; n = n.PreviousNode) {\n\t\t\t\tif (n is XElement e) return e;\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Returns next sibling element or <c>null</c>.\n\t\t/// </summary>\n\t\tpublic static XElement NextElem(this XElement t) {\n\t\t\tfor (XNode n = t.NextNode; n != null; n = n.NextNode) {\n\t\t\t\tif (n is XElement e) return e;\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Saves XML to a file in a safer way.\n\t\t/// Uses <see cref=\"XElement.Save(string, SaveOptions)\"/> and <see cref=\"filesystem.save\"/>.\n\t\t/// </summary>\n\t\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"XElement.Save\"/> and <see cref=\"filesystem.save\"/>.</exception>\n\t\tpublic static void SaveElem(this XElement t, string file, bool backup = false, SaveOptions? options = default) {\n\t\t\tfilesystem.save(file, temp => {\n\t\t\t\tif (options.HasValue) t.Save(temp, options.Value); else t.Save(temp);\n\t\t\t}, backup);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Saves XML to a file in a safer way.\n\t\t/// Uses <see cref=\"XDocument.Save(string)\"/> and <see cref=\"filesystem.save\"/>\n\t\t/// </summary>\n\t\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"XDocument.Save\"/> and <see cref=\"filesystem.save\"/>.</exception>\n\t\tpublic static void SaveDoc(this XDocument t, string file, bool backup = false, SaveOptions? options = default) {\n\t\t\tfilesystem.save(file, temp => {\n\t\t\t\tif (options.HasValue) t.Save(temp, options.Value); else t.Save(temp);\n\t\t\t}, backup);\n\t\t}\n\t}\n}\n\nnamespace Au.More {\n\t/// <summary>\n\t/// Loads <see cref=\"XElement\"/> and <see cref=\"XDocument\"/> in a safer way.\n\t/// </summary>\n\tpublic static class XmlUtil {\n\t\t/// <summary>\n\t\t/// Loads XML file in a safer way.\n\t\t/// Uses <see cref=\"XElement.Load(XmlReader, LoadOptions)\"/> and <see cref=\"filesystem.waitIfLocked\"/>.\n\t\t/// </summary>\n\t\t/// <param name=\"file\">\n\t\t/// File. Must be full path. Can contain environment variables etc, see <see cref=\"pathname.expand\"/>.\n\t\t/// If starts with <c>'&lt;'</c>, loads from XML string instead.\n\t\t/// </param>\n\t\t/// <param name=\"options\"></param>\n\t\t/// <exception cref=\"ArgumentException\">Not full path.</exception>\n\t\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"XElement.Load\"/>.</exception>\n\t\t/// <remarks>\n\t\t/// Unlike <see cref=\"XElement.Load(string, LoadOptions)\"/>, does not replace <c>\\r\\n</c> with <c>\\n</c>.\n\t\t/// </remarks>\n\t\tpublic static XElement LoadElem(string file, LoadOptions options = default)\n\t\t\t=> _Load(file, options, false) as XElement;\n\t\t\n\t\t/// <summary>\n\t\t/// Loads XML file with a namespace.\n\t\t/// </summary>\n\t\t/// <param name=\"ns\">Receives the default namespace. Use it to get elements, like <c>var x2 = x1.Element(ns + \"name\")</c>.</param>\n\t\t/// <inheritdoc cref=\"LoadElem(string, LoadOptions)\"/>\n\t\tpublic static XElement LoadElem(out XNamespace ns, string file, LoadOptions options = default) {\n\t\t\tvar r = _Load(file, options, false) as XElement;\n\t\t\tns = r.Name.Namespace;\n\t\t\treturn r;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// If XML file exists, loads it (calls <see cref=\"LoadElem\"/>), else creates new element or returns <c>null</c>.\n\t\t/// </summary>\n\t\t/// <param name=\"file\"></param>\n\t\t/// <param name=\"elemName\">Element name to use if <i>file</i> does not exist. If <c>null</c>, returns <c>null</c> if <i>file</i> does not exist.</param>\n\t\t/// <param name=\"options\"></param>\n\t\t/// <exception cref=\"ArgumentException\">Not full path.</exception>\n\t\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"XElement.Load\"/>.</exception>\n\t\tpublic static XElement LoadElemIfExists(string file, string elemName = null, LoadOptions options = default) {\n\t\t\tif (!filesystem.exists(file)) return elemName == null ? null : new(elemName);\n\t\t\treturn _Load(file, options, false) as XElement;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Loads XML file in a safer way.\n\t\t/// Uses <see cref=\"XDocument.Load(XmlReader, LoadOptions)\"/> and <see cref=\"filesystem.waitIfLocked\"/>.\n\t\t/// </summary>\n\t\t/// <param name=\"file\">\n\t\t/// File. Must be full path. Can contain environment variables etc, see <see cref=\"pathname.expand\"/>.\n\t\t/// If starts with <c>'&lt;'</c>, loads from XML string instead.\n\t\t/// </param>\n\t\t/// <param name=\"options\"></param>\n\t\t/// <exception cref=\"ArgumentException\">Not full path.</exception>\n\t\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"XDocument.Load\"/>.</exception>\n\t\t/// <remarks>\n\t\t/// Unlike <see cref=\"XDocument.Load(string, LoadOptions)\"/>, does not replace <c>\\r\\n</c> with <c>\\n</c>.\n\t\t/// </remarks>\n\t\tpublic static XDocument LoadDoc(string file, LoadOptions options = default)\n\t\t\t=> _Load(file, options, true) as XDocument;\n\t\t\n\t\tstatic XContainer _Load(string file, LoadOptions options, bool doc) {\n\t\t\tif (file.Starts('<')) return _Load2(file, options, doc, true);\n\t\t\tfile = pathname.NormalizeMinimally_(file);\n\t\t\treturn filesystem.waitIfLocked(() => _Load2(file, options, doc, false));\n\t\t\t\n\t\t\tstatic XContainer _Load2(string file, LoadOptions options, bool doc, bool isString) {\n\t\t\t\tusing var r = isString ? new XmlTextReader(new StringReader(file)) : new XmlTextReader(file); //to preserve \\r\\n\n\t\t\t\tif (0 == (options & LoadOptions.PreserveWhitespace)) r.WhitespaceHandling = WhitespaceHandling.Significant; //to save correctly formatted. Default of XElement.Load(string).\n\t\t\t\treturn doc ? (XContainer)XDocument.Load(r, options) : XElement.Load(r, options);\n\t\t\t}\n\t\t}\n\t}\n}"
  },
  {
    "path": "Au/Files, data/ExplorerFolder.cs",
    "content": "namespace Au.More;\n\n/// <summary>\n/// File Explorer folder window functions.\n/// </summary>\npublic class ExplorerFolder {\n\tapi.IWebBrowser2 _b;\n\twnd _w, _cTab;\n\t\n\t/// <summary>\n\t/// Creates <see cref=\"ExplorerFolder\"/> for a folder window.\n\t/// </summary>\n\t/// <returns><c>null</c> if failed.</returns>\n\t/// <param name=\"w\">A folder window.</param>\n\t/// <param name=\"tab\">Tab name (wildcard expression). If null (default), uses the current tab.</param>\n\t/// <exception cref=\"AuWndException\"><i>w</i> invalid.</exception>\n\tpublic static ExplorerFolder Of(wnd w, string tab = null) {\n\t\tw.ThrowIfInvalid();\n\t\twnd cTab = w.Child(tab, \"ShellTabWindowClass\");\n\t\tif (tab != null && cTab.Is0) return null;\n\t\tvar b = _GetIWebBrowser(w, cTab);\n\t\treturn b == null ? null : new() { _b = b, _w = w, _cTab = cTab };\n\t}\n\t\n\tstatic api.IWebBrowser2 _GetIWebBrowser(wnd w, wnd cTab) {\n\t\tforeach (var b in _EnumShellWindows()) {\n\t\t\ttry {\n\t\t\t\tif ((wnd)b.HWND == w) {\n\t\t\t\t\tif (!cTab.Is0 && _GetIShellBrowser(b, out var sb)) {\n\t\t\t\t\t\tbool ok = 0 == sb.GetWindow(out var c) && c == cTab;\n\t\t\t\t\t\tMarshal.ReleaseComObject(sb);\n\t\t\t\t\t\tif (!ok) continue;\n\t\t\t\t\t}\n\t\t\t\t\treturn b;\n\t\t\t\t}\n\t\t\t\tMarshal.ReleaseComObject(b);\n\t\t\t}\n\t\t\tcatch (COMException) { /*print.warning(b.LocationURL);*/ } //about:blank\n\t\t}\n\t\treturn null;\n\t}\n\t\n\tstatic IEnumerable<api.IWebBrowser2> _EnumShellWindows() {\n\t\t//var sw = new _Api.ShellWindows() as _Api.IShellWindows; //4 times slower than in C++. CoCreateInstance 3 times slower.\n\t\tif (0 != Api.CoCreateInstance(new(\"9BA05972-F6A8-11CF-A442-00A0C90A8F39\"), 0, 4, typeof(api.IShellWindows).GUID, out var o)) throw new AuException();\n\t\tvar sw = o as api.IShellWindows;\n\t\tfor (int i = 0, n = sw.Count(); i < n; i++) {\n\t\t\tyield return sw.Item(i) as api.IWebBrowser2;\n\t\t}\n\t\tMarshal.ReleaseComObject(sw);\n\t}\n\t\n\tstatic bool _GetIShellBrowser(api.IWebBrowser2 b, out api.IShellBrowser sb) => Api.QueryService(b, api.SID_STopLevelBrowser, out sb);\n\t\n\t/// <summary>\n\t/// Creates <see cref=\"ExplorerFolder\"/> for all folder windows.\n\t/// </summary>\n\t/// <param name=\"onlyFilesystem\">Skip folders that don't have a filesystem path, such as Control Panel and Recycle Bin.</param>\n\t/// <param name=\"tabsOf\">Need only tabs of this window.</param>\n\tpublic static ExplorerFolder[] All(bool onlyFilesystem = false, wnd tabsOf = default) {\n\t\tvar a = new List<ExplorerFolder>();\n\t\tforeach (var b in _EnumShellWindows()) {\n\t\t\ttry {\n\t\t\t\tvar s = b.LocationURL;\n\t\t\t\tif (s.NE()) {\n\t\t\t\t\tif (onlyFilesystem) continue;\n\t\t\t\t} else {\n\t\t\t\t\tif (!s.Starts(\"file:///\")) continue; //skip IE etc\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\twnd w = (wnd)b.HWND, c = default;\n\t\t\t\tif (!tabsOf.Is0 && w != tabsOf) continue;\n\t\t\t\tif (_GetIShellBrowser(b, out var sb)) sb.GetWindow(out c);\n\t\t\t\t\n\t\t\t\ta.Add(new() { _b = b, _w = w, _cTab = c });\n\t\t\t}\n\t\t\tcatch (COMException) { /*print.warning(b.LocationURL);*/ } //about:blank\n\t\t}\n\t\treturn a.ToArray();\n\t}\n\t\n\t/// <summary>\n\t/// Calls <see cref=\"GetFolderPath\"/>.\n\t/// </summary>\n\tpublic override string ToString() => GetFolderPath();\n\n\t/// <summary>\n\t/// Gets <c>IWebBrowser2</c> interface pointer.\n\t/// </summary>\n\tpublic object ComObject => _b;\n\t\n\t/// <summary>\n\t/// Gets window handle.\n\t/// </summary>\n\tpublic wnd Hwnd => _w;\n\t\n\t/// <summary>\n\t/// Gets tab control handle.\n\t/// </summary>\n\tpublic wnd HwndTab => _cTab;\n\t\n\t/// <summary>\n\t/// Gets folder path.\n\t/// For non-filesystem folder gets string like <c>\":: ITEMIDLIST\"</c>; see <see cref=\"Pidl\"/>.\n\t/// </summary>\n\t/// <returns><c>null</c> if failed.</returns>\n\tpublic string GetFolderPath() {\n\t\tvar s = _b.LocationURL;\n\t\tif (!s.NE()) {\n\t\t\tif (!s.Starts(\"file:///\")) return null;\n\t\t\tif (0 != Api.PathCreateFromUrlAlloc(s, out var r)) return null; //note: .NET urldecoding functions produce invalid string if there are urlencoded non-ASCII characters\n\t\t\treturn r;\n\t\t} else { //non-filesystem?\n\t\t\tif (_GetIShellBrowser(_b, out var sb)) {\n\t\t\t\tif (0 == sb.QueryActiveShellView(out var v1) && v1 is api.IFolderView2 f) {\n\t\t\t\t\tvar p = new Pidl(f.GetFolder(typeof(api.IPersistFolder2).GUID).GetCurFolder());\n\t\t\t\t\treturn p.ToString();\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Gets paths of selected items.\n\t/// </summary>\n\t/// <returns>Array containing 0 or more items.</returns>\n\t/// <remarks>\n\t/// For non-file-system items gets <c>\":: ITEMIDLIST\"</c>; see <see cref=\"Pidl\"/>.\n\t/// </remarks>\n\tpublic string[] GetSelectedItems() {\n\t\tvar d = _b.Document as api.ShellFolderView;\n\t\tvar items = d?.SelectedItems();\n\t\tif (items == null) return [];\n\t\tint n = items.Count;\n\t\tvar a = new List<string>(n);\n\t\tfor (int i = 0; i < n; i++) {\n\t\t\ttry {\n\t\t\t\tvar s = items.Item(i)?.Path;\n\t\t\t\tif (!s.NE()) a.Add(Pidl.ClsidToItemidlist_(s));\n\t\t\t}\n\t\t\tcatch { }\n\t\t\t//once: no selection, but items.Count returned 1, and items.Item(i) returned null. Could not reproduce after select-unselect.\n\t\t}\n\t\treturn a.ToArray();\n\t}\n\t\n\t/// <summary>\n\t/// Opens a folder in this window/tab.\n\t/// </summary>\n\t/// <param name=\"folder\">Folder path or <c>\":: ITEMIDLIST\"</c>. Or <c>\":back\"</c>, <c>\":forward\"</c>, <c>\":up\"</c>.</param>\n\t/// <exception cref=\"Exception\">Failed.</exception>\n\tpublic void Open(string folder) {\n\t\tif (!_GetIShellBrowser(_b, out var sb)) throw new AuException();\n\t\tvar flag = folder switch { \":back\" => api.SBSP_NAVIGATEBACK, \":forward\" => api.SBSP_NAVIGATEFORWARD, \":up\" => api.SBSP_PARENT, _ => 0u };\n\t\tif (flag != 0) {\n\t\t\tsb.BrowseObject(0, flag | api.SBSP_SAMEBROWSER);\n\t\t} else {\n\t\t\tusing var pidl = Pidl.FromString(folder, throwIfFailed: true);\n\t\t\tsb.BrowseObject(pidl.UnsafePtr, api.SBSP_SAMEBROWSER);\n\t\t}\n\t\t30.ms();\n\t\twhile (_b.Busy != 0) 10.ms();\n\t}\n\t\n\t/// <summary>\n\t/// Adds new tab in a folder window.\n\t/// </summary>\n\t/// <param name=\"w\">A folder window.</param>\n\t/// <param name=\"folder\">If not null, calls <see cref=\"Open\"/>.</param>\n\t/// <returns><see cref=\"ExplorerFolder\"/> for the new tab.</returns>\n\t/// <exception cref=\"Exception\">Failed.</exception>\n\t/// <remarks>\n\t/// To create new tab, activates the window and sends keys <c>Ctrl+T</c>.\n\t/// </remarks>\n\tpublic static ExplorerFolder NewTab(wnd w, string folder = null) {\n\t\t_ThrowIfNoMultipleTabs();\n\t\tw.Activate();\n\t\tvar c1 = w.Child(cn: \"ShellTabWindowClass\");\n\t\tkeys.send(\"Ctrl+T\");\n\t\t30.ms();\n\t\twait.until(5, () => w.Child(cn: \"ShellTabWindowClass\") != c1);\n\t\t\n\t\tvar r = ExplorerFolder.Of(w);\n\t\tif (folder != null) r.Open(@\"C:\\Test\");\n\t\treturn r;\n\t\t\n\t\t//Alternative.\n\t\t//\tBad: undocumented, found only in https://stackoverflow.com/a/78502949/26641797\n\t\t//\tGood: don't need to activate window.\n\t\t//\tSame: async too. Need to wait.\n\t\t//c1.Send(api.WM_COMMAND, 0xA21B);\n\t}\n\t\n\t/// <summary>\n\t/// Makes this tab visible.\n\t/// </summary>\n\t/// <exception cref=\"Exception\">Failed.</exception>\n\tpublic void SwitchToTab() {\n\t\t_ThrowIfNoMultipleTabs();\n\t\tvar s = _cTab.Name;\n\t\tvar c = _w.ChildFast(\"\", \"Microsoft.UI.Content.DesktopChildSiteBridge\");\n\t\tDebug_.PrintIf(c.Is0);\n\t\tif (c.Is0) c = _w;\n\t\tvar a = c.Elm[\"PAGETAB\", s].FindAll();\n\t\tif (a.Length == 0) throw new AuException();\n\t\tif (a.Length == 1) {\n\t\t\ta[0].Invoke();\n\t\t\tDebug_.PrintIf(_w.ChildFast(null, \"ShellTabWindowClass\") != _cTab);\n\t\t} else {\n\t\t\tforeach (var e in a) {\n\t\t\t\te.Invoke();\n\t\t\t\tif (_w.ChildFast(null, \"ShellTabWindowClass\") == _cTab) break;\n\t\t\t}\n\t\t}\n\t}\n\t\n\tstatic void _ThrowIfNoMultipleTabs() {\n\t\tif (!osVersion.minWin11_22H2) throw new InvalidOperationException(\"Multiple tabs not supported in this Windows version.\");\n\t}\n\t\n\t//rejected: InvokeVerbOnSelection. Too limited. Verbs unknown. Many commands can be invoked with hotkeys.\n\t\n\tclass api : NativeApi {\n\t\t[ComImport, Guid(\"85CB6900-4D95-11CF-960C-0080C7F4EE85\")]\n\t\tinternal interface IShellWindows {\n\t\t\tint Count();\n\t\t\t[return: MarshalAs(UnmanagedType.IDispatch)] object Item(object index);\n\t\t}\n\t\t\n\t\t[ComImport, Guid(\"d30c1661-cdaf-11d0-8a3e-00c04fc9e26e\"), InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]\n\t\tinternal interface IWebBrowser2 {\n\t\t\tobject Document { get; }\n\t\t\t//string Path { get; } //always C:\\WINDOWS\\\n\t\t\tstring LocationURL { get; }\n\t\t\tlong HWND { get; }\n\t\t\tshort Busy { get; }\n\t\t\tshort Visible { get; }\n\t\t}\n\t\t\n\t\t[ComImport, Guid(\"29EC8E6C-46D3-411f-BAAA-611A6C9CAC66\"), InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]\n\t\tinternal interface ShellFolderView {\n\t\t\tFolderItems SelectedItems();\n\t\t}\n\t\t\n\t\t[ComImport, Guid(\"744129E0-CBE5-11CE-8350-444553540000\"), InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]\n\t\tinternal interface FolderItems {\n\t\t\tint Count { get; }\n\t\t\tFolderItem Item(object index);\n\t\t}\n\t\t\n\t\t[ComImport, Guid(\"FAC32C80-CBE4-11CE-8350-444553540000\"), InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]\n\t\tinternal interface FolderItem {\n\t\t\tstring Path { get; }\n\t\t}\n\t\t\n\t\tinternal static readonly Guid SID_STopLevelBrowser = new(0x4C96BE40, 0x915C, 0x11CF, 0x99, 0xD3, 0x00, 0xAA, 0x00, 0x4A, 0xE8, 0x37);\n\t\t\n\t\t[ComImport, Guid(\"000214E2-0000-0000-C000-000000000046\"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\n\t\tinternal interface IShellBrowser {\n\t\t\t[PreserveSig] int GetWindow(out wnd phwnd);\n\t\t\tvoid _2();\n\t\t\tvoid _3();\n\t\t\tvoid _4();\n\t\t\tvoid _5();\n\t\t\tvoid _6();\n\t\t\tvoid _7();\n\t\t\tvoid _8();\n\t\t\tvoid BrowseObject(nint pidl, uint wFlags);\n\t\t\tvoid _a();\n\t\t\tvoid _b();\n\t\t\tvoid _c();\n\t\t\t[PreserveSig] int QueryActiveShellView([MarshalAs(UnmanagedType.IUnknown)] out object ppshv);\n\t\t}\n\t\t\n\t\t[ComImport, Guid(\"1AC3D9F0-175C-11d1-95BE-00609797EA4F\"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\n\t\tinternal interface IPersistFolder2 {\n\t\t\tvoid _1();\n\t\t\tvoid _2();\n\t\t\tIntPtr GetCurFolder();\n\t\t}\n\t\t\n\t\t[ComImport, Guid(\"1af3a467-214f-4298-908e-06b03e0b39f9\"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\n\t\tinternal interface IFolderView2 {\n\t\t\tvoid _1();\n\t\t\tvoid _2();\n\t\t\tIPersistFolder2 GetFolder(in Guid riid);\n\t\t}\n\t\t\n\t\tinternal const uint SBSP_SAMEBROWSER = 0x1;\n\t\tinternal const uint SBSP_NAVIGATEBACK = 0x4000;\n\t\tinternal const uint SBSP_NAVIGATEFORWARD = 0x8000;\n\t\tinternal const uint SBSP_PARENT = 0x2000;\n\t}\n}\n"
  },
  {
    "path": "Au/Files, data/FileSystemRedirection.cs",
    "content": "namespace Au.More;\n\n/// <summary>\n/// File system redirection functions. Can temporarily disable redirection, to allow this 32-bit process access the 64-bit System32 directory.\n/// Example: <c>using (FileSystemRedirection r1 = new()) { r1.Disable(); ... }</c>.\n/// </summary>\npublic struct FileSystemRedirection : IDisposable {\n\tbool _redirected;\n\tIntPtr _redirValue;\n\n\t/// <summary>\n\t/// Calls <see cref=\"Revert\"/>.\n\t/// </summary>\n\tpublic void Dispose() {\n\t\tRevert();\n\t}\n\n\t/// <summary>\n\t/// If <see cref=\"osVersion.is32BitProcessAnd64BitOS\"/>, calls API <ms>Wow64DisableWow64FsRedirection</ms>, which disables file system redirection.\n\t/// </summary>\n\t/// <remarks>\n\t/// The caller can call this without checking OS and process bitness. This function checks it and it is fast.\n\t/// \n\t/// Always call <see cref=\"Revert\"/> or <see cref=\"Dispose\"/>, for example use <c>finally</c> or <c>using</c> statement. Not calling it is more dangerous than a memory leak. It is not called by GC.\n\t/// </remarks>\n\tpublic void Disable() {\n\t\tif (osVersion.is32BitProcessAnd64BitOS)\n\t\t\t_redirected = Api.Wow64DisableWow64FsRedirection(out _redirValue);\n\t}\n\n\t/// <summary>\n\t/// If redirected, calls API <ms>Wow64RevertWow64FsRedirection</ms>.\n\t/// </summary>\n\tpublic void Revert() {\n\t\tif (_redirected)\n\t\t\t_redirected = !Api.Wow64RevertWow64FsRedirection(_redirValue);\n\t}\n\n\t/// <summary>\n\t/// Returns <c>true</c> if <see cref=\"osVersion.is32BitProcessAnd64BitOS\"/> is <c>true</c> and path starts with <see cref=\"folders.System\"/>.\n\t/// Most such paths are redirected, therefore you may want to disable redirection with this class.\n\t/// </summary>\n\t/// <param name=\"path\">Normalized path. This function does not normalize. Also it is unaware of <c>@\"\\\\?\\\"</c>.</param>\n\tpublic static bool IsSystem64PathIn32BitProcess(string path) {\n\t\treturn 0 != _IsSystem64PathIn32BitProcess(path);\n\t}\n\n\tstatic int _IsSystem64PathIn32BitProcess(string path) {\n\t\tif (!osVersion.is32BitProcessAnd64BitOS) return 0;\n\t\tstring sysDir = folders.System;\n\t\tif (!path.Starts(sysDir, true)) return 0;\n\t\tint len = sysDir.Length;\n\t\tif (path.Length > len && !pathname.IsSepChar_(path[len])) return 0;\n\t\treturn len;\n\t}\n\n\t/// <summary>\n\t/// If <see cref=\"osVersion.is32BitProcessAnd64BitOS\"/> is <c>true</c> and <i>path</i> starts with <see cref=\"folders.System\"/>, replaces that path part with <see cref=\"folders.SystemX64\"/>.\n\t/// It disables redirection to <see cref=\"folders.SystemX86\"/> for that path.\n\t/// </summary>\n\t/// <param name=\"path\">Normalized path. This function does not normalize. Also it is unaware of <c>@\"\\\\?\\\"</c>.</param>\n\t/// <param name=\"ifExistsOnlyThere\">Don't replace <i>path</i> if the file or directory exists in the redirected folder or does not exist in the non-redirected folder.</param>\n\tpublic static string GetNonRedirectedSystemPath(string path, bool ifExistsOnlyThere = false) {\n\t\tint i = _IsSystem64PathIn32BitProcess(path);\n\t\tif (i == 0) return path;\n\t\tif (ifExistsOnlyThere && filesystem.exists(path)) return path;\n\t\tvar s = path.ReplaceAt(0, i, folders.SystemX64);\n\t\tif (ifExistsOnlyThere && !filesystem.exists(s)) return path;\n\t\treturn s;\n\t}\n}\n"
  },
  {
    "path": "Au/Files, data/FileTree.cs",
    "content": "namespace Au.More;\n\n/// <summary>\n/// Contains file infos of this and descendant folders and files retrieved by <see cref=\"filesystem.enumerate\"/>.\n/// Can print a formatted list of descendant sizes.\n/// </summary>\npublic class FileTree : TreeBase<FileTree> {\n\tFEFile _f;\n\t\n\tFileTree(FEFile f) {\n\t\tif (f != null) {\n\t\t\t_f = f;\n\t\t\tSize = f.Size;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Gets the info retrieved by <see cref=\"filesystem.enumerate\"/>.\n\t/// </summary>\n\tpublic FEFile Info => _f;\n\t\n\t/// <summary>\n\t/// Filename.\n\t/// </summary>\n\tpublic string Name => _f.Name;\n\t\n\t/// <summary>\n\t/// Full path.\n\t/// </summary>\n\tpublic string Path => _f.FullPath;\n\t\n\t/// <inheritdoc cref=\"FEFile.IsDirectory\"/>\n\tpublic bool IsDirectory => _f.IsDirectory;\n\t\n\t/// <summary>\n\t/// Gets the file size. If directory, it's the sum of all descendant file sizes.\n\t/// </summary>\n\tpublic long Size { get; private set; }\n\t\n\t/// <summary>\n\t/// Calls <see cref=\"filesystem.enumerate\"/> and creates a tree of descendants.\n\t/// </summary>\n\t/// <param name=\"path\">Folder path.</param>\n\t/// <param name=\"onlyDirectories\">Don't include files.</param>\n\t/// <param name=\"minSize\">Don't include smaller files and directories. The unit is bytes.</param>\n\t/// <param name=\"ignoreInaccessible\">If cannot access some descendant directories, ignore them and don't throw exception. Default <c>true</c>.</param>\n\t/// <param name=\"recurseNtfsLinks\">Enumerate target directories of NTFS links, such as symbolic links and mount points.</param>\n\t/// <param name=\"dirFilter\">Called for each descendant directory. If returns <c>false</c>, that directory with descendants is not included. But its size contributes to ancestor sizes anyway.</param>\n\t/// <param name=\"fileFilter\">Called for each descendant file. If returns <c>false</c>, that file is not included. But its size contributes to ancestor sizes anyway.</param>\n\t/// <returns>The root of the tree. You can use its descendants and <see cref=\"Size\"/>. Don't use <see cref=\"Info\"/>, <see cref=\"Name\"/>, <see cref=\"Path\"/> and <see cref=\"IsDirectory\"/>.</returns>\n\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"filesystem.enumerate\"/>.</exception>\n\tpublic static FileTree Create(string path, bool onlyDirectories = false, long minSize = 0, bool ignoreInaccessible = true, bool recurseNtfsLinks = false, Func<FEFile, bool> dirFilter = null, Func<FEFile, bool> fileFilter = null) {\n\t\tvar flags = ignoreInaccessible ? FEFlags.IgnoreInaccessible : 0;\n\t\tif (recurseNtfsLinks) flags |= FEFlags.RecurseNtfsLinks;\n\t\t\n\t\tFileTree root = new(null);\n\t\t_Dir(path, root, 0, false, flags);\n\t\treturn root;\n\t\t\n\t\tvoid _Dir(string path, FileTree x, int level, bool skipDescendants, FEFlags flags) {\n\t\t\tforeach (var f in filesystem.enumerate(path, flags)) {\n\t\t\t\tf.Level = level;\n\t\t\t\tif (f.IsDirectory) {\n\t\t\t\t\tbool skip = skipDescendants || (dirFilter != null && !dirFilter(f));\n\t\t\t\t\tvar y = new FileTree(f);\n\t\t\t\t\t_Dir(f.FullPath, y, level + 1, skip, flags | FEFlags.UseRawPath);\n\t\t\t\t\tx.Size += y.Size;\n\t\t\t\t\tif (!skip && y.Size >= minSize) x.AddChild(y);\n\t\t\t\t} else {\n\t\t\t\t\tx.Size += f.Size;\n\t\t\t\t\tbool skip = skipDescendants || onlyDirectories || f.Size < minSize || (fileFilter != null && !fileFilter(f));\n\t\t\t\t\tif (!skip) x.AddChild(new(f));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Appends to a <see cref=\"StringBuilder\"/> a list of sizes and names of descendants, formatted for <see cref=\"print.it(string)\"/>, without a header.\n\t/// </summary>\n\tpublic void PrintSizes(StringBuilder b) {\n\t\t_Dir(this, 0);\n\t\t\n\t\tvoid _Dir(FileTree x, int level) {\n\t\t\tforeach (var y in x.Children().OrderByDescending(o => o.Size)) {\n\t\t\t\tb.Append('\\t', level);\n\t\t\t\tvar k = y.Size / MB;\n\t\t\t\tb.AppendFormat(k == 0 ? \"0       \" : k < .001 ? \"<0.001  \" : k < .1 ? \"{0,-7:0.###} \" : k < 1 ? \"{0,-7:0.##} \" : k < 10 ? \"{0,-7:0.#} \" : \"{0,-7:0.} \", k);\n\t\t\t\tif (y.IsDirectory) {\n\t\t\t\t\tb.AppendFormat(\"<explore {0}>{1}<>\", y.Path, y.Name);\n\t\t\t\t\tif (y.HasChildren) {\n\t\t\t\t\t\tb.AppendLine(\"    <fold>\");\n\t\t\t\t\t\t_Dir(y, level + 1);\n\t\t\t\t\t\tb.Length -= 2;\n\t\t\t\t\t\tb.Append(\"</fold>\");\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tb.AppendFormat(\"<explore {0}>{1}<>  (file)\", y.Path, y.Name);\n\t\t\t\t}\n\t\t\t\tb.AppendLine();\n\t\t\t}\n\t\t}\n\t}\n\t\n\tconst double MB = 1048576;\n\t\n\t/// <summary>\n\t/// Formats and prints a list of sizes and names of folder's descendant folders and optionally files, with a header.\n\t/// </summary>\n\t/// <param name=\"minSizeMB\">Don't include smaller files and directories. The unit is MB.</param>\n\t/// <inheritdoc cref=\"Create\"/>\n\tpublic static void PrintSizes(string path, bool onlyDirectories = false, double minSizeMB = 0, bool ignoreInaccessible = true, bool recurseNtfsLinks = false, Func<FEFile, bool> dirFilter = null, Func<FEFile, bool> fileFilter = null) {\n\t\tvar t = Create(path, onlyDirectories, (long)(minSizeMB * MB), ignoreInaccessible, recurseNtfsLinks, dirFilter, fileFilter);\n\t\t\n\t\tvar b = new StringBuilder(\"<><lc #C0E0A0>Sizes (MB) of folders\");\n\t\tif (!onlyDirectories) b.Append(\" and files\");\n\t\tb.Append($\" in <link>{path}<>\");\n\t\tif (minSizeMB > 0) b.Append($\". Skipped sizes < {minSizeMB} MB.\");\n\t\tb.AppendLine(\"<>\");\n\t\t\n\t\tt.PrintSizes(b);\n\t\tprint.it(b);\n\t}\n}\n"
  },
  {
    "path": "Au/Files, data/FileWatcher.cs",
    "content": "namespace Au.More;\n\n/// <summary>\n/// Watches a file for external changes. An external change is when another process writes, creates, deletes, moves or renames the file.\n/// </summary>\n/// <remarks>\n/// All functions are thread-safe.\n/// </remarks>\n/// <example>\n/// <code><![CDATA[\n/// /*/ ifRunning run; /*/\n/// \n/// var c = new C();\n/// c.Load(@\"C:\\Test\\FileWatcher.xml\");\n/// c.ModifyAndSave();\n/// dialog.show(\"Testing FileWatcher\");\n/// \n/// class C {\n/// \tstring _file;\n/// \tXElement _x;\n/// \tFileWatcher _watcher;\n/// \t\n/// \tpublic void Load(string file) {\n/// \t\t_file = file;\n/// \t\tif (filesystem.exists(_file)) {\n/// \t\t\t_x = XElement.Load(_file);\n/// \t\t} else {\n/// \t\t\t_x = XElement.Parse(\"\"\"<a>example</a>\"\"\");\n/// \t\t}\n/// \t\t_watcher ??= FileWatcher.Watch(_file, _OnExternalChange);\n/// \t}\n/// \t\n/// \tvoid _OnExternalChange() {\n/// \t\t_x = XElement.Load(_file);\n/// \t\tprint.it(\"external change\", _x);\n/// \t}\n/// \t\n/// \tpublic void ModifyAndSave() {\n/// \t\t_x.Value = Random.Shared.Next().ToString();\n/// \t\t\n/// \t\t_watcher?.Paused = true;\n/// \t\ttry { _x.Save(_file); }\n/// \t\tfinally { _watcher?.Paused = false; }\n/// \t}\n/// }\n/// ]]></code>\n/// </example>\npublic sealed class FileWatcher : IDisposable {\n\treadonly object _dirWatcher;\n\treadonly string _file;\n\tAction _action;\n\tCancellationTokenSource _cts;\n\tlong _timestamp;\n\treadonly object _lock = new();\n\t\n\tstatic _Watchers s_watchers = new();\n\t\n\tFileWatcher(object dirWatcher, string file, Action action) {\n\t\t_dirWatcher = dirWatcher;\n\t\t_file = file;\n\t\t_action = action;\n\t\t_SetTimestamp();\n\t}\n\t\n\t/// <summary>\n\t/// Stops watching.\n\t/// </summary>\n\t/// <remarks>\n\t/// Don't need to ever call this, unless you want to stop watching before the process exits.\n\t/// </remarks>\n\tpublic void Dispose() {\n\t\tDisposeNoRemove_();\n\t\ts_watchers.Remove(this);\n\t}\n\t\n\tinternal void DisposeNoRemove_() {\n\t\t_action = null;\n\t\tlock (_lock) {\n\t\t\t_cts?.Dispose();\n\t\t\t_cts = null;\n\t\t}\n\t}\n\t\n\tvoid _SetTimestamp() {\n\t\tfilesystem.GetTime_(_file, out _timestamp);\n\t}\n\t\n\t/// <summary>\n\t/// When your app writes, creates, deletes, moves or renames the file, it must set this property = <c>true</c> during the file operation. It helps to distinguish internal and external changes. Example: <see cref=\"FileWatcher\"/>.\n\t/// </summary>\n\tpublic bool Paused {\n\t\tget => field;\n\t\tset {\n\t\t\tif (value == field) return;\n\t\t\tfield = value;\n\t\t\tif (!field && _action != null) _SetTimestamp();\n\t\t}\n\t}\n\t\n\tvoid _Event(FileSystemEventArgs e) {\n\t\t//print.it(e.ChangeType, e.Name);\n\t\tif (Paused || _action is null) return;\n\t\t\n\t\ttry {\n\t\t\tlock (_lock) {\n\t\t\t\t_cts?.Cancel();\n\t\t\t\t_cts?.Dispose();\n\t\t\t\t_cts = new();\n\t\t\t\t\n\t\t\t\t_ = Task.Delay(100, _cts.Token).ContinueWith(t => { //to cancel previous duplicate events\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (t.IsCanceled) return;\n\t\t\t\t\t\tbool exists = filesystem.GetProp_(_file, out var p);\n\t\t\t\t\t\tbool deleted = e.ChangeType == WatcherChangeTypes.Deleted && !exists;\n\t\t\t\t\t\tif (deleted) {\n\t\t\t\t\t\t\t_timestamp = 0;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tDebug_.PrintIf(!exists); if (!exists) return; //maybe temporarily deleted; we should get 'deleted' notification afterwards\n\t\t\t\t\t\t\tDebug_.PrintIf(p.size == 0); if (p.size == 0) return; //eg VSCode saves like this: opens, clears, closes; then opens writes closes. Normally the first notification is canceled, but.\n\t\t\t\t\t\t\tif (p.time == _timestamp) return;\n\t\t\t\t\t\t\t_timestamp = p.time;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t//print.it(e.ChangeType, e.Name);\n\t\t\t\t\t\t_action?.Invoke();\n\t\t\t\t\t}\n\t\t\t\t\tcatch (Exception ex) { Debug_.Print(ex); }\n\t\t\t\t}, TaskScheduler.Default);\n\t\t\t}\n\t\t}\n\t\tcatch (Exception ex) { Debug_.Print(ex); } //eg _cts disposed\n\t}\n\t\n\t/// <summary>\n\t/// Starts watching a file for external changes. An external change is when another process writes, creates, deletes, moves or renames the file.\n\t/// </summary>\n\t/// <param name=\"file\">Full path of a file, without environment variables.</param>\n\t/// <param name=\"onExternalChange\">\n\t/// Called when detected that the file was changed externally.\n\t/// Important: when your app writes, creates, deletes, moves or renames the file, it must set <see cref=\"Paused\"/> = <c>true</c> during the file operation.\n\t/// Runs in a thread pool thread.\n\t/// </param>\n\t/// <returns>\n\t/// A new <see cref=\"FileWatcher\"/> instance used to manage the watcher.\n\t/// Don't need to dispose it, unless you want to stop watching before the process exits.\n\t/// Returns <c>null</c> and prints a warning if the watcher could not be created (for example the directory does not exist).\n\t/// </returns>\n\t/// <exception cref=\"NotSupportedException\">\n\t/// Thrown if this method has already been called for the same file without a corresponding call to <see cref=\"Dispose\"/>.\n\t/// If multiple notification handlers are needed, combine them into the <i>onExternalChange</i> delegate.\n\t/// </exception>\n\t/// <example><see cref=\"FileWatcher\"/></example>\n\tpublic static FileWatcher Watch(string file, Action onExternalChange)\n\t\t=> s_watchers.Add(file ?? throw new ArgumentNullException(), onExternalChange ?? throw new ArgumentNullException());\n\t\n\tinternal static void DisposeAll_() { s_watchers.DisposeAll(); }\n\t\n\tclass _Watchers {\n\t\tclass _DirectoryWatcher : FileSystemWatcher {\n\t\t\tpublic readonly Dictionary<string, FileWatcher> dFiles = new(StringComparer.OrdinalIgnoreCase);\n\t\t\t\n\t\t\tpublic _DirectoryWatcher(string dir) : base(dir) { }\n\t\t}\n\t\t\n\t\treadonly List<_DirectoryWatcher> _a = [];\n\t\t\n\t\tpublic FileWatcher Add(string file, Action modifiedExternally) {\n\t\t\tlock (this) {\n\t\t\t\tvar dir = pathname.getDirectory(file);\n\t\t\t\t_DirectoryWatcher dw = null; foreach (var v in _a) if (v.Path.Eqi(dir)) { dw = v; break; }\n\t\t\t\tif (dw is null) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tdw = new(dir) { NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite };\n\t\t\t\t\t\tdw.Deleted += _Event;\n\t\t\t\t\t\tdw.Created += _Event;\n\t\t\t\t\t\tdw.Changed += _Event;\n\t\t\t\t\t\tdw.Renamed += _Event;\n\t\t\t\t\t\tdw.EnableRaisingEvents = true;\n\t\t\t\t\t\t_a.Add(dw);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (Exception ex) { //eg directory does not exist\n\t\t\t\t\t\tdw?.Dispose();\n\t\t\t\t\t\tprint.warning($\"Cannot watch '{file}'. {ex}\");\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tFileWatcher f = new(dw, file, modifiedExternally);\n\t\t\t\tif (!dw.dFiles.TryAdd(pathname.getName(file), f)) throw new NotSupportedException($\"A FileWatcher for '{file}' already exists\");\n\t\t\t\t\n\t\t\t\treturn f;\n\t\t\t}\n\t\t\t\n\t\t\tstatic void _Event(object sender, FileSystemEventArgs e) {\n\t\t\t\tvar dw = sender as _DirectoryWatcher;\n\t\t\t\tif (dw.dFiles.TryGetValue(e.Name, out var f)) f._Event(e);\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic void Remove(FileWatcher f) {\n\t\t\tlock (this) {\n\t\t\t\tvar dw = f._dirWatcher as _DirectoryWatcher;\n\t\t\t\tdw.dFiles.Remove(pathname.getName(f._file));\n\t\t\t\tif (dw.dFiles.Count == 0) {\n\t\t\t\t\t_a.Remove(dw);\n\t\t\t\t\tdw.Dispose();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic void DisposeAll() {\n\t\t\tlock (this) {\n\t\t\t\tforeach (var dw in _a) {\n\t\t\t\t\tdw.Dispose();\n\t\t\t\t\tforeach (var f in dw.dFiles.Values) f.DisposeNoRemove_();\n\t\t\t\t\tdw.dFiles.Clear();\n\t\t\t\t}\n\t\t\t\t_a.Clear();\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic _Watchers() {\n\t\t\t//It's better to dispose/stop everything on process exit as soon as possible, to avoid invoking ModifiedExternally while/after clients execute process.thisProcessExit etc handlers.\n\t\t\tSystem.Runtime.Loader.AssemblyLoadContext.Default.Unloading += c => DisposeAll(); //before thisProcessExit/ProcessExit. Not on unhandled exception.\n\t\t\tAppDomain.CurrentDomain.UnhandledException += (_, _) => DisposeAll(); //never mind: maybe not the first event handler. Unlikely something bad can happen. If using JSettings, its process.thisProcessExit calls DisposeAll before saving all.\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au/Files, data/Pidl.cs",
    "content": "namespace Au.Types;\n\n/// <summary>\n/// Manages an <ms>ITEMIDLIST</ms> structure that is used to identify files and other shell objects instead of a file-system path.\n/// </summary>\n/// <remarks>\n/// Wraps an <c>ITEMIDLIST*</c>, also known as <i>PIDL</i> or <c>LPITEMIDLIST</c>.\n/// \n/// When calling native shell API, virtual objects can be identified only by <c>ITEMIDLIST*</c>. Some API also support \"parsing name\", which may look like <c>\"::{CLSID-1}\\::{CLSID-2}\"</c>. File-system objects can be identified by path as well as by <c>ITEMIDLIST*</c>. URLs can be identified by URL as well as by <c>ITEMIDLIST*</c>.\n/// \n/// The <c>ITEMIDLIST</c> structure is in unmanaged memory. You can dispose <c>Pidl</c> variables, or GC will do it later. Always dispose if creating many.\n/// \n/// This class has only <c>ITEMIDLIST</c> functions that are used in this library. Look for other functions on the Internet. Many of them are named with IL prefix, like <c>ILClone</c>, <c>ILGetSize</c>, <c>ILFindLastID</c>.\n/// </remarks>\npublic unsafe class Pidl : IDisposable {\n\tIntPtr _pidl;\n\t\n\t/// <summary>\n\t/// Gets the <c>ITEMIDLIST*</c>.\n\t/// </summary>\n\t/// <remarks>\n\t/// The <c>ITEMIDLIST</c> memory is managed by this variable and will be freed when disposing or GC-collecting it. Use <see cref=\"GC.KeepAlive\"/> where need.\n\t/// </remarks>\n\tpublic IntPtr UnsafePtr => _pidl;\n\t\n\t/// <summary>\n\t/// Gets the <c>ITEMIDLIST*</c>.\n\t/// </summary>\n\t/// <remarks>\n\t/// Use to pass to API where the parameter type is <see cref=\"System.Runtime.InteropServices.HandleRef\"/>. It is safer than <see cref=\"UnsafePtr\"/> because ensures that this variable will not be GC-collected during API call even if not referenced after the call.\n\t/// </remarks>\n\tpublic HandleRef HandleRef => new HandleRef(this, _pidl);\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if the <c>ITEMIDLIST*</c> is <c>null</c>.\n\t/// </summary>\n\tpublic bool IsNull => _pidl == default;\n\t\n\t/// <summary>\n\t/// Assigns an <c>ITEMIDLIST</c> to this variable.\n\t/// </summary>\n\t/// <param name=\"pidl\">\n\t/// <c>ITEMIDLIST*</c>.\n\t/// It can be created by any API that creates <c>ITEMIDLIST</c>. They allocate the memory with API <ms>CoTaskMemAlloc</ms>. This variable will finally free it with <see cref=\"Marshal.FreeCoTaskMem\"/> which calls API <ms>CoTaskMemFree</ms>.\n\t/// </param>\n\tpublic Pidl(IntPtr pidl) => _pidl = pidl;\n\t\n\t/// <summary>\n\t/// Combines two <c>ITEMIDLIST</c> (parent and child) and assigns the result to this variable.\n\t/// </summary>\n\t/// <param name=\"pidlAbsolute\">Absolute <c>ITEMIDLIST*</c> (parent folder).</param>\n\t/// <param name=\"pidlRelative\">Relative <c>ITEMIDLIST*</c> (child object).</param>\n\t/// <remarks>\n\t/// Does not free <i>pidlAbsolute</i> and <i>pidlRelative</i>.\n\t/// </remarks>\n\tpublic Pidl(IntPtr pidlAbsolute, IntPtr pidlRelative) => _pidl = Api.ILCombine(pidlAbsolute, pidlRelative);\n\t\n\t/// <summary>\n\t/// Frees the <c>ITEMIDLIST</c> with <see cref=\"Marshal.FreeCoTaskMem\"/> and clears this variable.\n\t/// </summary>\n\tpublic void Dispose() {\n\t\tDispose(true);\n\t\tGC.SuppressFinalize(this);\n\t}\n\t\n\t///\n\tprotected virtual void Dispose(bool disposing) {\n\t\tif (_pidl != default) {\n\t\t\tMarshal.FreeCoTaskMem(_pidl);\n\t\t\t_pidl = default;\n\t\t}\n\t}\n\t\n\t///\n\t~Pidl() { Dispose(false); }\n\t\n\t/// <summary>\n\t/// Gets the <c>ITEMIDLIST</c> and clears this variable so that it cannot be used and will not free the <c>ITEMIDLIST</c> memory. To free it use <see cref=\"Marshal.FreeCoTaskMem\"/>.\n\t/// </summary>\n\tpublic IntPtr Detach() {\n\t\tvar R = _pidl;\n\t\t_pidl = default;\n\t\treturn R;\n\t}\n\t\n\t/// <summary>\n\t/// Converts string to <c>ITEMIDLIST</c> and creates new <see cref=\"Pidl\"/> variable that holds it.\n\t/// </summary>\n\t/// <returns><c>null</c> if failed.</returns>\n\t/// <param name=\"s\">A file-system path or URL or shell object parsing name (see <see cref=\"ToShellString\"/>) or <c>\":: ITEMIDLIST\"</c> (see <see cref=\"ToHexString\"/>). Supports environment variables (see <see cref=\"pathname.expand\"/>).</param>\n\t/// <param name=\"throwIfFailed\">Throw exception if failed.</param>\n\t/// <exception cref=\"AuException\">Failed, and <i>throwIfFailed</i> is <c>true</c>. Probably invalid <i>s</i>.</exception>\n\t/// <remarks>\n\t/// Calls <ms>SHParseDisplayName</ms>, except when string is <c>\":: ITEMIDLIST\"</c>.\n\t/// If <c>\":: ITEMIDLIST\"</c>, does not check whether the shell object exists.\n\t/// </remarks>\n\tpublic static Pidl FromString(string s, bool throwIfFailed = false) {\n\t\tIntPtr R = FromString_(s, throwIfFailed);\n\t\treturn (R == default) ? null : new Pidl(R);\n\t}\n\t\n\t/// <summary>\n\t/// The same as <see cref=\"FromString\"/>, but returns unmanaged <c>ITEMIDLIST*</c>.\n\t/// Later need to free it with <c>Marshal.FreeCoTaskMem</c>.\n\t/// </summary>\n\t/// <param name=\"s\"></param>\n\t/// <param name=\"throwIfFailed\">If failed: <c>true</c> - throw <see cref=\"AuException\"/>; <c>false</c> - return 0.</param>\n\tinternal static IntPtr FromString_(string s, bool throwIfFailed = false) {\n\t\tIntPtr R;\n\t\ts = _Normalize(s);\n\t\tif (s.Starts(\":: \")) {\n\t\t\tvar span = s.AsSpan(3);\n\t\t\tint n = span.Length / 2;\n\t\t\tR = Marshal.AllocCoTaskMem(n + 2);\n\t\t\tbyte* b = (byte*)R;\n\t\t\tn = Convert2.HexDecode(span, b, n);\n\t\t\tb[n] = b[n + 1] = 0;\n\t\t} else { //file-system path or URL or shell object parsing name\n\t\t\tvar hr = Api.SHParseDisplayName(s, default, out R, 0, null);\n\t\t\tif (hr != 0) {\n\t\t\t\tif (throwIfFailed) throw new AuException(hr);\n\t\t\t\treturn default;\n\t\t\t}\n\t\t}\n\t\treturn R;\n\t}\n\t\n\t/// <summary>\n\t/// The same as <see cref=\"pathname.normalize\"/><c>(CanBeUrlOrShell|DontPrefixLongPath)</c>, but ignores non-full path (returns <i>s</i>).\n\t/// </summary>\n\t/// <param name=\"s\">File-system path or URL or <c>\"::...\"</c>.</param>\n\tstatic string _Normalize(string s) {\n\t\ts = pathname.expand(s);\n\t\tif (!pathname.isFullPath(s)) return s; //note: not EEV. Need to expand to \":: \" etc, and EEV would not do it.\n\t\treturn pathname.Normalize_(s, PNFlags.DontPrefixLongPath, true);\n\t}\n\t\n\t/// <summary>\n\t/// Converts the <c>ITEMIDLIST</c> to file path or URL or shell object parsing name or display name, depending on <i>stringType</i>.\n\t/// </summary>\n\t/// <returns>Returns <c>null</c> if this variable does not have an <c>ITEMIDLIST</c> (eg disposed or detached). If failed, returns <c>null</c> or throws exception.</returns>\n\t/// <param name=\"stringType\">\n\t/// String format. API <ms>SIGDN</ms>.\n\t/// Often used:\n\t/// <br/>• <c>SIGDN.NORMALDISPLAY</c> - returns object name without path. It is best to display in UI but cannot be parsed to create <c>ITEMIDLIST</c> again.\n\t/// <br/>• <c>SIGDN.FILESYSPATH</c> - returns path if the <c>ITEMIDLIST</c> identifies a file system object (file or directory). Else returns <c>null</c>.\n\t/// <br/>• <c>SIGDN.URL</c> - if URL, returns URL. If file system object, returns its path like <c>\"file:///C:/a/b.txt\"</c>. Else returns <c>null</c>.\n\t/// <br/>• <c>SIGDN.DESKTOPABSOLUTEPARSING</c> - returns path (if file system object) or URL (if URL) or shell object parsing name (if virtual object eg Control Panel). Note: not all returned parsing names can actually be parsed to create <c>ITEMIDLIST</c> again, therefore usually it's better to use <see cref=\"ToString\"/> instead.\n\t/// </param>\n\t/// <param name=\"throwIfFailed\">If failed, throw <see cref=\"AuException\"/>.</param>\n\t/// <exception cref=\"AuException\">Failed, and <i>throwIfFailed</i> is <c>true</c>.</exception>\n\t/// <remarks>\n\t/// Calls <ms>SHGetNameFromIDList</ms>.\n\t/// </remarks>\n\tpublic string ToShellString(SIGDN stringType, bool throwIfFailed = false) {\n\t\tvar R = ToShellString(_pidl, stringType, throwIfFailed);\n\t\tGC.KeepAlive(this);\n\t\treturn R;\n\t}\n\t\n\t/// <summary>\n\t/// Converts an <c>ITEMIDLIST</c> to file path or URL or shell object parsing name or display name, depending on <i>stringType</i>.\n\t/// </summary>\n\t/// <returns>Returns <c>null</c> if <i>pidl</i> is <c>default(IntPtr)</c>. If failed, returns <c>null</c> or throws exception.</returns>\n\t/// <inheritdoc cref=\"ToShellString(SIGDN, bool)\"/>\n\tpublic static string ToShellString(IntPtr pidl, SIGDN stringType, bool throwIfFailed = false) {\n\t\tif (pidl == default) return null;\n\t\tvar hr = Api.SHGetNameFromIDList(pidl, stringType, out string R);\n\t\tif (hr == 0) return R;\n\t\tif (throwIfFailed) throw new AuException(hr);\n\t\treturn null;\n\t}\n\t\n\t/// <summary>\n\t/// Converts the <c>ITEMIDLIST</c> to string.\n\t/// If it identifies an existing file-system object (file or directory), returns path. If URL, returns URL. Else returns <c>\":: ITEMIDLIST\"</c> (see <see cref=\"ToHexString\"/>).\n\t/// </summary>\n\t/// <returns><c>null</c> if this variable does not have an <c>ITEMIDLIST</c> (eg disposed or detached).</returns>\n\tpublic override string ToString() {\n\t\tvar R = ToString(_pidl);\n\t\tGC.KeepAlive(this);\n\t\treturn R;\n\t}\n\t\n#if true\n\t/// <summary>\n\t/// This overload uses an <c>ITEMIDLIST*</c> that is not stored in a <see cref=\"Pidl\"/> variable.\n\t/// </summary>\n\tpublic static string ToString(IntPtr pidl) {\n\t\tif (pidl == default) return null;\n\t\tApi.IShellItem si = null;\n\t\ttry {\n\t\t\tif (0 == Api.SHCreateShellItem(default, null, pidl, out si)) {\n\t\t\t\t//if(0 == Api.SHCreateItemFromIDList(pidl, Api.IID_IShellItem, out si)) { //same speed\n\t\t\t\t//if(si.GetAttributes(0xffffffff, out uint attr)>=0) print.it(attr);\n\t\t\t\tif (si.GetAttributes(Api.SFGAO_BROWSABLE | Api.SFGAO_FILESYSTEM, out uint attr) >= 0 && attr != 0) {\n\t\t\t\t\tvar f = (0 != (attr & Api.SFGAO_FILESYSTEM)) ? SIGDN.FILESYSPATH : SIGDN.URL;\n\t\t\t\t\tif (0 == si.GetDisplayName(f, out var R)) return R;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfinally { Api.ReleaseComObject(si); }\n\t\treturn ToHexString(pidl);\n\t}\n\t//this version is 40% slower with non-virtual objects (why?), but with virtual objects same speed as SIGDN_DESKTOPABSOLUTEPARSING.\n\t//The fastest (update: actually not) version would be to call ToShellString_(SIGDN_DESKTOPABSOLUTEPARSING), and then call ToHexString if it returns not a path or URL. But it is unreliable, because can return string in any format, eg \"Microsoft.WindowsCalculator_8wekyb3d8bbwe!App\".\n#elif false\n\t\t\t//this version works, but with virtual objects 2 times slower than SIGDN_DESKTOPABSOLUTEPARSING (which already is very slow with virtual).\n\t\t\tpublic static string ToString(IntPtr pidl)\n\t\t\t{\n\t\t\t\tif(pidl == default) return null;\n\t\t\t\tvar R = ToShellString(pidl, SIGDN.FILESYSPATH);\n\t\t\t\tif(R == null) R = ToShellString(pidl, SIGDN.URL);\n\t\t\t\tif(R == null) R = ToHexString(pidl);\n\t\t\t\treturn R;\n\t\t\t}\n#elif true\n\t\t\t//this version works, but with virtual objects 30% slower. Also 30% slower for non-virtual objects (why?).\n\t\t\tpublic static string ToString(IntPtr pidl)\n\t\t\t{\n\t\t\t\tif(pidl == default) return null;\n\n\t\t\t\tApi.IShellItem si = null;\n\t\t\t\ttry {\n\t\t\t\t\tif(0 == Api.SHCreateShellItem(default, null, pidl, out si)) {\n\t\t\t\t\t\tstring R = null;\n\t\t\t\t\t\tif(0 == si.GetDisplayName(SIGDN.FILESYSPATH, out R)) return R;\n\t\t\t\t\t\tif(0 == si.GetDisplayName(SIGDN.URL, out R)) return R;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfinally { Api.ReleaseComObject(si); }\n\t\t\t\treturn ToHexString(pidl);\n\t\t\t}\n#else\n\t\t\t//SHGetPathFromIDList also slow.\n\t\t\t//SHBindToObject cannot get ishellitem.\n#endif\n\t\n\t/// <summary>\n\t/// Returns string <c>\":: ITEMIDLIST\"</c>.\n\t/// Returns <c>null</c> if this variable does not have an <c>ITEMIDLIST</c> (eg disposed or detached).\n\t/// </summary>\n\t/// <remarks>\n\t/// The string can be used with some functions of this library, mostly of classes <see cref=\"filesystem\"/>, <see cref=\"Pidl\"/> and <see cref=\"icon\"/>. Cannot be used with native and .NET functions.\n\t/// </remarks>\n\tpublic string ToHexString() {\n\t\tvar R = ToHexString(_pidl);\n\t\tGC.KeepAlive(this);\n\t\treturn R;\n\t}\n\t\n\t/// <summary>\n\t/// Returns string <c>\":: ITEMIDLIST\"</c>.\n\t/// This overload uses an <c>ITEMIDLIST*</c> that is not stored in a <see cref=\"Pidl\"/> variable.\n\t/// </summary>\n\tpublic static string ToHexString(IntPtr pidl) {\n\t\tif (pidl == default) return null;\n\t\tint n = Api.ILGetSize(pidl) - 2; //API gets size with the terminating '\\0' (2 bytes)\n\t\tif (n < 0) return null;\n\t\tif (n == 0) return \":: \"; //shell root - Desktop\n\t\treturn \":: \" + Convert2.HexEncode((void*)pidl, n);\n\t}\n\t//rejected: use base64 <c>ITEMIDLIST</c>. Shorter, but cannot easily split, for example in folders.UnexpandPath.\n\t\n\t/// <summary>\n\t/// If <i>s</i> starts with <c>\"::{\"</c>, converts to <c>\":: ITEMIDLIST\"</c>. Else returns <i>s</i>.\n\t/// </summary>\n\tinternal static string ClsidToItemidlist_(string s) {\n\t\tif (s != null && s.Starts(\"::{\")) {\n\t\t\tusing var pidl = FromString(s);\n\t\t\tif (pidl != null) return pidl.ToString();\n\t\t}\n\t\treturn s;\n\t}\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if <c>ITEMIDLIST</c> values are equal.\n\t/// </summary>\n\tpublic bool ValueEquals(IntPtr pidl) => Api.ILIsEqual(_pidl, pidl);\n}\n"
  },
  {
    "path": "Au/Files, data/TempFile.cs",
    "content": "namespace Au.More;\n\n/// <summary>\n/// Creates unique name for a temporary file and later auto-deletes the file.\n/// </summary>\n/// <remarks>\n/// Use code like in the example to auto-delete the temporary file if exists. Or call <see cref=\"Dispose\"/>. Else this class does not delete the file.\n/// </remarks>\n/// <example>\n/// <code><![CDATA[\n/// using (var f = new TempFile()) {\n/// \tprint.it(f);\n/// \tfilesystem.saveText(f, \"DATA\");\n/// \tprint.it(filesystem.loadText(f));\n/// } //now auto-deletes the file\n/// ]]></code>\n/// </example>\npublic sealed class TempFile : IDisposable {\n\treadonly string _file;\n\n\t/// <summary>\n\t/// Creates full path string with a unique filename (GUID) for a temporary file.\n\t/// </summary>\n\t/// <param name=\"ext\">Filename extension with dot. Default: <c>\".tmp\"</c>. Can be <c>null</c>.</param>\n\t/// <param name=\"directory\">Parent directory. If <c>null</c> (default), uses <see cref=\"folders.ThisAppTemp\"/>. The function creates the directory if does not exist.</param>\n\t/// <exception cref=\"ArgumentException\"><i>directory</i> not full path.</exception>\n\t/// <exception cref=\"AuException\">Failed to create directory.</exception>\n\tpublic TempFile(string ext = \".tmp\", string directory = null) {\n\t\tfilesystem.createDirectory(directory ??= folders.ThisAppTemp);\n\t\t_file = pathname.combine(directory, Guid.NewGuid().ToString()) + ext;\n\t}\n\n\t/// <summary>\n\t/// Deletes the file if exists.\n\t/// </summary>\n\t/// <remarks>\n\t/// Does not throw exception if fails to delete.\n\t/// </remarks>\n\tpublic void Dispose() { filesystem.delete(_file, FDFlags.CanFail); }\n\n\t/// <summary>\n\t/// Gets the file path.\n\t/// </summary>\n\tpublic string File => _file;\n\n\t/// <summary>\n\t/// Returns the file path.\n\t/// </summary>\n\tpublic static implicit operator string(TempFile f) => f._file;\n\n\t/// <summary>\n\t/// Returns the file path.\n\t/// </summary>\n\tpublic override string ToString() => _file;\n}\n"
  },
  {
    "path": "Au/Files, data/filesystem-types.cs",
    "content": "\nnamespace Au.Types;\n\n/// <summary>\n/// File system entry type - file, directory, NTFS link, whether it exists and is accessible.\n/// The enum value <c>NotFound</c> is 0; <c>AccessDenied</c> is negative <c>((int)0x80000000)</c>; other values are greater than 0.\n/// </summary>\ninternal enum FileIs_ {\n\t/// <summary>Does not exist.</summary>\n\tNotFound = 0,\n\n\t/// <summary>Is file, and not NTFS link.</summary>\n\tFile = 1,\n\n\t/// <summary>Is directory, and not NTFS link.</summary>\n\tDirectory = 2,\n\n\t/// <summary>Is NTFS link to file.</summary>\n\tNtfsLinkFile = 5,\n\n\t/// <summary>Is NTFS link to directory.</summary>\n\tNtfsLinkDirectory = 6,\n\n\t/// <summary>Exists but this process cannot access it and get attributes.</summary>\n\tAccessDenied = int.MinValue,\n}\n\n/// <summary>\n/// Contains file or directory attributes. Tells whether it exists, is directory, readonly, hidden, system, NTFS link.\n/// See <see cref=\"filesystem.exists\"/>.\n/// </summary>\npublic struct FAttr {\n\treadonly FileAttributes _a;\n\treadonly bool _exists, _unknown, _ntfsLink;\n\n\t/// <param name=\"attributes\">Attributes, or 0 if does not exist or can't get attributes.</param>\n\t/// <param name=\"exists\">True if exists and can get attributes. False if does not exist. <c>null</c> if exists but can't get attributes.</param>\n\t/// <param name=\"ntfsLink\">Is a NTFS link, such as symbolic link or mount point.</param>\n\tinternal FAttr(FileAttributes attributes, bool? exists, bool ntfsLink) {\n\t\t_a = attributes;\n\t\t_exists = exists == true;\n\t\t_unknown = exists == null;\n\t\t_ntfsLink = ntfsLink;\n\t}\n\n\t/// <summary>\n\t/// Returns file or directory attributes. Returns 0 if <see cref=\"Exists\"/> <c>false</c>.\n\t/// </summary>\n\tpublic FileAttributes Attributes => _a;\n\n\t/// <summary>\n\t/// Returns <see cref=\"Exists\"/>.\n\t/// </summary>\n\tpublic static implicit operator bool(FAttr fa) => fa.Exists;\n\n\t/// <summary>\n\t/// Returns 0 if !<see cref=\"Exists\"/>, 1 if <see cref=\"File\"/>, 2 if <see cref=\"Directory\"/>. Can be used with switch.\n\t/// </summary>\n\tpublic static implicit operator int(FAttr fa) => !fa.Exists ? 0 : (fa.Directory ? 2 : 1);\n\n\t/// <summary>\n\t/// Exists and is accessible (<see cref=\"Unknown\"/> <c>false</c>).\n\t/// See also <see cref=\"File\"/>, <see cref=\"Directory\"/>.\n\t/// </summary>\n\tpublic bool Exists => _exists;\n\n\t/// <summary>\n\t/// Exists but this process cannot access it and get attributes (error \"access denied\"). Then other <c>bool</c> properties return <c>false</c>.\n\t/// </summary>\n\tpublic bool Unknown => _unknown;\n\n\t/// <summary>\n\t/// Is file (not directory), or NTFS link to a file (if <see cref=\"IsNtfsLink\"/> <c>true</c>).\n\t/// </summary>\n\tpublic bool File => 0 == (_a & FileAttributes.Directory) && _exists;\n\n\t/// <summary>\n\t/// Is directory, or NTFS link to a directory (if <see cref=\"IsNtfsLink\"/> <c>true</c>).\n\t/// </summary>\n\tpublic bool Directory => 0 != (_a & FileAttributes.Directory);\n\n\t/// <summary>\n\t/// It is a NTFS link, such as symbolic link, junction or mount point. Don't confuse with shell links (shortcuts).\n\t/// If <see cref=\"File\"/> <c>true</c>, the target is a file. If <see cref=\"Directory\"/> <c>true</c>, the target is a directory.\n\t/// </summary>\n\tpublic bool IsNtfsLink => _ntfsLink;\n\n\t/// <summary>\n\t/// Has <see cref=\"FileAttributes.ReadOnly\"/>.\n\t/// </summary>\n\tpublic bool IsReadonly => 0 != (_a & FileAttributes.ReadOnly);\n\n\t/// <summary>\n\t/// Has <see cref=\"FileAttributes.Hidden\"/>.\n\t/// </summary>\n\tpublic bool IsHidden => 0 != (_a & FileAttributes.Hidden);\n\n\t/// <summary>\n\t/// Has <see cref=\"FileAttributes.System\"/>.\n\t/// </summary>\n\tpublic bool IsSystem => 0 != (_a & FileAttributes.System);\n\n\t///\n\tpublic override string ToString() {\n\t\treturn Unknown ? \"unknown\" : (Exists ? $\"{{ Directory={Directory}, IsNtfsLink={IsNtfsLink}, Attributes={Attributes} }}\" : \"doesn't exist\");\n\t}\n}\n\n/// <summary>\n/// Flags for <see cref=\"filesystem.getAttributes\"/> and some other functions.\n/// </summary>\n[Flags]\npublic enum FAFlags {\n\t///<summary>Pass path to the API as it is, without any normalizing and validating.</summary>\n\tUseRawPath = 1,\n\n\t///<summary>\n\t///If failed, return <c>false</c> and don't throw exception.\n\t///Then, if you need error info, you can use <see cref=\"lastError\"/>. If the file/directory does not exist, it will return <ms>ERROR_FILE_NOT_FOUND</ms> or <c>ERROR_PATH_NOT_FOUND</c> or <c>ERROR_NOT_READY</c>.\n\t///If failed and the native error code is <c>ERROR_ACCESS_DENIED</c> or <c>ERROR_SHARING_VIOLATION</c>, the returned attributes will be <c>(FileAttributes)(-1)</c>. The file probably exists but is protected so that this process cannot access and use it. Else attributes will be 0.\n\t///</summary>\n\tDontThrow = 2,\n}\n\n/// <summary>\n/// File or directory properties. Used with <see cref=\"filesystem.getProperties\"/>.\n/// </summary>\npublic record struct FileProperties {\n\t///\n\tpublic FileAttributes Attributes { get; set; }\n\n\t///<summary>File size. For directories it is usually 0.</summary>\n\tpublic long Size { get; set; }\n\n\t///\n\tpublic DateTime LastWriteTimeUtc { get; set; }\n\n\t///\n\tpublic DateTime CreationTimeUtc { get; set; }\n\n\t///<summary>Note: this is unreliable. The operating system may not record this time automatically.</summary>\n\tpublic DateTime LastAccessTimeUtc { get; set; }\n\n\t/// <summary>\n\t/// It is a NTFS link, such as symbolic link or mount point. Don't confuse with shell links (shortcuts).\n\t/// </summary>\n\tpublic bool IsNtfsLink { get; set; }\n}\n\n/// <summary>\n/// flags for <see cref=\"filesystem.enumerate\"/>.\n/// </summary>\n[Flags]\npublic enum FEFlags {\n\t/// <summary>\n\t/// Enumerate all descendants, not only direct children. Also known as \"recurse subdirectories\".\n\t/// </summary>\n\tAllDescendants = 1,\n\n\t/// <summary>\n\t/// Also enumerate target directories of NTFS links, such as symbolic links and mount points. Use with <c>AllDescendants</c>.\n\t/// </summary>\n\tRecurseNtfsLinks = 2,\n\n\t/// <summary>\n\t/// Skip files and subdirectories that have <c>Hidden</c> attribute.\n\t/// </summary>\n\tSkipHidden = 4,\n\n\t/// <summary>\n\t/// Skip files and subdirectories that have <c>Hidden</c> and <c>System</c> attributes (both).\n\t/// These files/directories usually are created and used only by the operating system. Drives usually have several such directories. Another example - thumbnail cache files.\n\t/// Without this flag the function skips only these hidden-system root directories when enumerating a drive: <c>$Recycle.Bin</c>, <c>System Volume Information</c>, <c>Recovery</c>. If you want to include them too, use network path of the drive, for example <c>@\"\\\\localhost\\D$\\\"</c> for <c>D</c> drive.\n\t/// </summary>\n\tSkipHiddenSystem = 8, //note: must match FCFlags\n\n\t/// <summary>\n\t/// If fails to get the contents of the directory or a subdirectory because of its security settings, assume that the [sub]directory is empty.\n\t/// Without this flag then throws exception or calls <i>errorHandler</i>.\n\t/// </summary>\n\tIgnoreInaccessible = 0x10, //note: must match FCFlags\n\n\t/// <summary>\n\t/// Get only files and not subdirectories.\n\t/// Note: the <i>dirFilter</i> callback function is called just to ask whether to include children.\n\t/// </summary>\n\tOnlyFiles = 0x20,\n\n\t/// <summary>\n\t/// Don't call <see cref=\"pathname.normalize\"/> and don't throw exception for non-full path.\n\t/// </summary>\n\tUseRawPath = 0x40,\n\n\t/// <summary>\n\t/// Let <see cref=\"FEFile.Name\"/> be path relative to the specified directory path. Like <c>@\"\\name.txt\"</c> or <c>@\"\\subdirectory\\name.txt\"</c> instead of <c>\"name.txt\"</c>.\n\t/// </summary>\n\tNeedRelativePaths = 0x80,\n\n\t//rejected. Rarely used. Can use FileSystemRedirection, it's public.\n\t///// <summary>\n\t///// Temporarily disable file system redirection in this thread of this 32-bit process running on 64-bit Windows.\n\t///// Then you can enumerate the 64-bit System32 folder in your 32-bit process.\n\t///// Uses API <ms>Wow64DisableWow64FsRedirection</ms>.\n\t///// For vice versa (in 64-bit process enumerate the 32-bit System folder), instead use path <c>folders.SystemX86</c>.\n\t///// </summary>\n\t//DisableRedirection = 0x100,\n}\n\n/// <summary>\n/// flags for <see cref=\"filesystem.copy\"/> and some other similar functions.\n/// Used only when copying directory.\n/// </summary>\n[Flags]\npublic enum FCFlags {\n\t//note: these values must match the corresponding FEFlags values.\n\n\t/// <summary>\n\t/// Skip descendant files and directories that have <c>Hidden</c> and <c>System</c> attributes (both).\n\t/// They usually are created and used only by the operating system. Drives usually have several such directories. Another example - thumbnail cache files.\n\t/// They often are protected and would fail to copy, ruining whole copy operation.\n\t/// Without this flag the function skips only these hidden-system root directories when enumerating a drive: <c>$Recycle.Bin</c>, <c>System Volume Information</c>, <c>Recovery</c>.\n\t/// </summary>\n\tSkipHiddenSystem = 8,\n\n\t/// <summary>\n\t/// If fails to get the contents of the directory or a subdirectory because of its security settings, don't throw exception but assume that the [sub]directory is empty.\n\t/// </summary>\n\tIgnoreInaccessible = 0x10,\n\n\t/// <summary>\n\t/// Don't create subdirectories that after applying all filters would be empty.\n\t/// </summary>\n\tNoEmptyDirectories = 0x10000,\n}\n\n/// <summary>\n/// flags for <see cref=\"filesystem.delete\"/>.\n/// </summary>\n[Flags]\npublic enum FDFlags {\n\t/// <summary>\n\t/// Send to the Recycle Bin. If not possible, delete anyway, unless used <c>CanFail</c>.\n\t/// Why could be not possible: 1. The file is in a removable drive (most removables don't have a recycle bin). 2. The file is too large. 3. The path is too long. 4. The Recycle Bin is not used on that drive (it can be set in the Recycle Bin Properties dialog). 5. This process is non-UI-interactive, eg a service. 6. Unknown reasons.\n\t/// Note: it is much slower. To delete multiple, use <see cref=\"filesystem.delete(IEnumerable{string}, FDFlags)\"/>.\n\t/// </summary>\n\tRecycleBin = 1,\n\n\t/// <summary>\n\t/// If fails to delete, don't wait/retry and don't throw exception.\n\t/// </summary>\n\tCanFail = 2,\n\n\t//rejected. Rarely useful. Maybe in the future.\n\t///// <summary>\n\t///// Fail if has read-only attribute.\n\t///// </summary>\n\t//ReadonlyFail = 4,\n}\n\n/// <summary>\n/// Contains name and other main properties of a file or subdirectory retrieved by <see cref=\"filesystem.enumerate\"/>.\n/// The values are not changed after creating the variable.\n/// </summary>\npublic class FEFile {\n\tinternal FEFile(string name, string fullPath, in Api.WIN32_FIND_DATA d, int level) {\n\t\tName = name; FullPath = fullPath;\n\t\tAttributes = d.dwFileAttributes;\n\t\tSize = (long)d.nFileSizeHigh << 32 | d.nFileSizeLow;\n\t\tLastWriteTimeUtc = DateTime.FromFileTimeUtc(d.ftLastWriteTime); //fast, sizeof 8\n\t\tCreationTimeUtc = DateTime.FromFileTimeUtc(d.ftCreationTime);\n\t\t_level = (short)level;\n\t\tReparseTag = d.dwReserved0;\n\t}\n\n\t/// <summary>\n\t/// Gets file name. Or relative path if used <see cref=\"FEFlags.NeedRelativePaths\"/>.\n\t/// </summary>\n\tpublic string Name { get; }\n\n\t/// <summary>\n\t/// Gets full path.\n\t/// </summary>\n\tpublic string FullPath { get; }\n\n\t/// <summary>\n\t/// Gets filename extension. Returns <c>\"\"</c> if directory.\n\t/// </summary>\n\tpublic string Extension => IsDirectory ? \"\" : pathname.getExtension(Name); //note: if null for directory, then OrderBy throws exception\n\n\t/// <summary>\n\t/// Returns file size. For directories it is usually 0.\n\t/// </summary>\n\tpublic long Size { get; }\n\n\t///\n\tpublic DateTime LastWriteTimeUtc { get; }\n\n\t///\n\tpublic DateTime CreationTimeUtc { get; }\n\n\t///\n\tpublic FileAttributes Attributes { get; }\n\n\t/// <summary>\n\t/// It is a directory. Or a NTFS link to a directory (see <see cref=\"IsNtfsLink\"/>).\n\t/// </summary>\n\tpublic bool IsDirectory { get { return (Attributes & FileAttributes.Directory) != 0; } }\n\n\t/// <summary>\n\t/// Descendant level.\n\t/// 0 if direct child of the directory (<i>directoryPath</i>), 1 if child of child, and so on.\n\t/// </summary>\n\tpublic int Level {\n\t\tget => _level;\n\t\tinternal set { _level = (short)value; }\n\t}\n\tshort _level;\n\n\t/// <summary>\n\t/// It is a NTFS link, such as symbolic link or mount point. Don't confuse with shell links (shortcuts).\n\t/// </summary>\n\tpublic bool IsNtfsLink => Attributes.Has(FileAttributes.ReparsePoint) && 0 != (ReparseTag & 0x20000000);\n\n\t/// <summary>\n\t/// <ms>WIN32_FIND_DATA</ms><c>.dwReserved0</c>.\n\t/// </summary>\n\tpublic uint ReparseTag { get; }\n\n\t/// <summary>\n\t/// Returns <see cref=\"FullPath\"/>.\n\t/// </summary>\n\tpublic override string ToString() => FullPath;\n\n\t//This could be more dangerous than useful.\n\t///// <summary>\n\t///// Returns <c>FullPath</c>.\n\t///// </summary>\n\t//public static implicit operator string(FEFile f) { return f?.FullPath; }\n}\n\n/// <summary>\n/// What to do if the destination directory contains a file or directory with the same name as the source file or directory when copying, moving or renaming.\n/// </summary>\n/// <remarks>\n/// Used with <see cref=\"filesystem.copy\"/>, <see cref=\"filesystem.move\"/> and similar functions.\n/// When renaming or moving, if the destination is the same as the source, these options are ignored and the destination is simply renamed. For example when renaming <c>\"file.txt\"</c> to <c>\"FILE.TXT\"</c>.\n/// </remarks>\npublic enum FIfExists {\n\t/// <summary>Throw exception. Default.</summary>\n\tFail,\n\n\t/// <summary>Delete destination.</summary>\n\tDelete,\n\n\t/// <summary>Rename (backup) destination.</summary>\n\tRenameExisting,\n\n\t/// <summary>\n\t/// If destination directory exists, merge the source directory into it, replacing existing files.\n\t/// If destination file exists, deletes it.\n\t/// If destination directory exists and source is file, fails.\n\t/// </summary>\n\tMergeDirectory,\n\n\t/// <summary>Copy/move with a different name.</summary>\n\tRenameNew,\n\n#if not_implemented\n\t/// <summary>Display a dialog asking the user what to do.</summary>\n\tAsk,\n#endif\n}\n\n/// <summary>\n/// Used with <see cref=\"filesystem.more.getFinalPath\"/>\n/// </summary>\npublic enum FPFormat {\n\t/// <summary>\n\t/// With long-path prefix (<c>\"\\\\?\\\"</c> or <c>\"\\\\?\\UNC\\\"</c>) if path length > <see cref=\"pathname.maxDirectoryPathLength\"/>. This is default.\n\t/// </summary>\n\tPrefixIfLong,\n\n\t/// <summary>\n\t/// Always with long-path prefix (<c>\"\\\\?\\\"</c> or <c>\"\\\\?\\UNC\\\"</c>).\n\t/// </summary>\n\tPrefixAlways,\n\n\t/// <summary>\n\t/// Without long-path prefix, even if the path is very long.\n\t/// </summary>\n\tPrefixNever,\n\n\t/// <summary>\n\t/// With volume GUID (API <ms>GetFinalPathNameByHandle</ms> flag <c>VOLUME_NAME_GUID</c>).\n\t/// If it fails (eg network path), gets path with prefix, like <c>PrefixAlways</c>.\n\t/// </summary>\n\tVolumeGuid\n}\n\n/// <summary>\n/// See <see cref=\"filesystem.more.comparePaths\"/>.\n/// </summary>\npublic enum CPResult {\n\t/// <summary>\n\t/// The paths are unrelated.\n\t/// Example: <c>pathA: @\"C:\\Dir1\\File1.txt\"</c>, <c>pathB: @\"C:\\Dir2\\File1.txt\"</c>.\n\t/// </summary>\n\tNone,\n\n\t/// <summary>\n\t/// Both paths are of the same file or directory.\n\t/// Example: <c>pathA: @\"C:\\Dir1\\File1.txt\"</c>, <c>pathB: @\"C:\\Dir2\\..\\Dir1\\File1.txt\"</c>.\n\t/// </summary>\n\tSame,\n\n\t/// <summary>\n\t/// <i>pathA</i> is of a directory that contains file or directory specified by <i>pathB</i>.\n\t/// Example: <c>pathA: @\"C:\\Dir1\"</c>, <c>pathB: @\"C:\\Dir1\\Dir2\\File1.txt\"</c>.\n\t/// </summary>\n\tAContainsB,\n\n\t/// <summary>\n\t/// <i>pathB</i> is of a directory that contains file or directory specified by <i>pathA</i>.\n\t/// Example: <c>pathA: @\"C:\\Dir1\\Dir2\\File1.txt\"</c>, <c>pathB: @\"C:\\Dir1\"</c>.\n\t/// </summary>\n\tBContainsA,\n\n\t/// <summary>\n\t/// Failed. Probably one (or both) of specified files does not exist.\n\t/// The function supports <see cref=\"lastError\"/>.\n\t/// </summary>\n\tFailed,\n}\n\n/// <summary>\n/// See <see cref=\"filesystem.more.getFileId\"/>.\n/// </summary>\npublic record struct FileId(int VolumeSerialNumber, long FileIndex);\n\n/// <summary>\n/// See <see cref=\"filesystem.more.createSymbolicLink\"/>\n/// </summary>\npublic enum CSLink {\n\t/// <summary>Symbolic link to file.</summary>\n\tFile,\n\n\t/// <summary>Symbolic link to directory.</summary>\n\tDirectory,\n\n\t/// <summary>\n\t/// Junction to directory.\n\t/// \n\t/// Usually junctions work like symbolic links, but there are differences when creating them:\n\t/// <br/>• Don't need admin privileges to create.\n\t/// <br/>• Target must be full path. Fails if relative path.\n\t/// <br/>• Target must be on this computer. Fails if on a network computer.\n\t/// <br/>• Target must be directory. Fails if file.\n\t///\n\t/// Some programs interpret junctions differently. For example git adds the target directory.\n\t/// </summary>\n\tJunction,\n\n\t/// <summary>\n\t/// Junction to local directory or symbolic link to network directory.\n\t/// </summary>\n\tJunctionOrSymlink,\n}"
  },
  {
    "path": "Au/Files, data/filesystem.cs",
    "content": "\n//#define TEST_FINDFIRSTFILEEX\n\nusing Microsoft.Win32;\n\nnamespace Au;\n\n/// <summary>\n/// File and directory functions. Copy, move, delete, find, get properties, enumerate, create directory, load/save, etc.\n/// </summary>\n/// <remarks>\n/// Also you can use .NET file system classes, such as <see cref=\"File\"/> and <see cref=\"Directory\"/> in <c>System.IO</c> namespace. In the past they were too limited and unsafe to use, for example no long paths, too many exceptions, difficult to recursively enumerate directories containing protected items. Later improved, but this class still has something they don't, for example environment variables in path, safe load/save. This class does not have low-level functions to open/read/write files.\n/// \n/// Most functions support only full path. Most of them throw <see cref=\"ArgumentException\"/> if passed a filename or relative path, ie in \"current directory\". Using current directory is unsafe.\n/// Most functions support extended-length paths (longer than 259). Such local paths should have <c>@\"\\\\?\\\"</c> prefix, like <c>@\"\\\\?\\C:\\...\"</c>. Such network path should be like <c>@\"\\\\?\\UNC\\server\\share\\...\"</c>. See <see cref=\"pathname.prefixLongPath\"/>, <see cref=\"pathname.prefixLongPathIfNeed\"/>. Many functions support long paths even without prefix.\n/// \n/// Disk drives like <c>@\"C:\\\"</c> or <c>\"C:\"</c> are directories too.\n/// </remarks>\npublic static partial class filesystem {\n\t#region exists, attributes, properties\n\t\n\t/// <summary>\n\t/// Contains the write/create times, size and attributes of a file or directory.\n\t/// The equality method and operators compare only <see cref=\"time\"/>, <see cref=\"size\"/> and attributes <c>Directory</c> and <c>ReparsePoint</c>; not <see cref=\"timeCreated\"/>.\n\t/// </summary>\n\t/// <param name=\"time\">The last write time UTC as FILETIME.</param>\n\t/// <param name=\"size\">0 if directory.</param>\n\t/// <param name=\"attr\"></param>\n\t/// <param name=\"timeCreated\">The creation time UTC as FILETIME.</param>\n\tinternal record struct Prop_(long time, long size, FileAttributes attr, long timeCreated) {\n\t\tpublic bool Equals(Prop_ p) => p.time == time && p.size == size && (p.attr & (FileAttributes.Directory | FileAttributes.ReparsePoint)) == (attr & (FileAttributes.Directory | FileAttributes.ReparsePoint));\n\t\t\n\t\tpublic override int GetHashCode() => time.GetHashCode();\n\t\t\n\t\tpublic DateTime TimeAsDateTime => DateTime.FromFileTimeUtc(time);\n\t}\n\t\n\t/// <summary>\n\t/// Gets the last write time, size and attributes of a file or directory.\n\t/// </summary>\n\t/// <param name=\"path\">Full path. The function just asserts full path and calls <see cref=\"pathname.prefixLongPathIfNeed\"/>.</param>\n\t/// <param name=\"prop\"></param>\n\t/// <returns><c>false</c> if does not exist or access denied. No exceptions.</returns>\n\tinternal static unsafe bool GetProp_(string path, out Prop_ prop) {\n\t\tDebug.Assert(pathname.isFullPath(path));\n\t\tprop = default;\n\t\tpath = pathname.prefixLongPathIfNeed(path);\n\t\tif (!Api.GetFileAttributesEx(path, 0, out var d)) {\n\t\t\tif (!_GetAttributesOnError(path, FAFlags.DontThrow, out _, out _, &d)) return false;\n\t\t}\n\t\tprop = new(d.ftLastWriteTime, d.Size, d.dwFileAttributes, d.ftCreationTime);\n\t\treturn true;\n\t}\n\t\n\t/// <summary>\n\t/// Gets the last write time of a file or directory.\n\t/// </summary>\n\t/// <param name=\"path\">Full path. The function just asserts full path and calls <see cref=\"pathname.prefixLongPathIfNeed\"/>.</param>\n\t/// <param name=\"time\">Time UTC as <c>FILETIME</c>.</param>\n\t/// <returns><c>false</c> if does not exist or access denied. No exceptions.</returns>\n\tinternal static bool GetTime_(string path, out long time) {\n\t\tbool ok = GetProp_(path, out var p);\n\t\ttime = p.time;\n\t\treturn ok;\n\t}\n\t\n\t/// <summary>\n\t/// Gets file or directory attributes, size and times.\n\t/// </summary>\n\t/// <returns><c>false</c> if the file/directory does not exist.</returns>\n\t/// <param name=\"path\">Full path. Supports <c>@\"\\..\"</c> etc. If flag <c>UseRawPath</c> not used, supports environment variables (see <see cref=\"pathname.expand\"/>).</param>\n\t/// <param name=\"properties\">Receives properties.</param>\n\t/// <param name=\"flags\"></param>\n\t/// <exception cref=\"ArgumentException\">Not full path (when not used flag <c>UseRawPath</c>).</exception>\n\t/// <exception cref=\"AuException\">The file/directory exists but failed to get its properties. Not thrown if used flag <c>DontThrow</c>.</exception>\n\t/// <remarks>\n\t/// Calls API <ms>GetFileAttributesEx</ms>. Supports <see cref=\"lastError\"/> (useful with flag <c>DontThrow</c>).\n\t/// For NTFS links, gets properties of the link, not of its target.\n\t/// You can also get most of these properties with <see cref=\"enumerate\"/>.\n\t/// </remarks>\n\tpublic static unsafe bool getProperties(string path, out FileProperties properties, FAFlags flags = 0) {\n\t\tproperties = new();\n\t\tif (0 == (flags & FAFlags.UseRawPath)) path = pathname.NormalizeMinimally_(path); //the API supports .. etc\n\t\tif (!Api.GetFileAttributesEx(path, 0, out var d)) {\n\t\t\tif (!_GetAttributesOnError(path, flags, out _, out _, &d)) return false;\n\t\t}\n\t\tproperties.Attributes = d.dwFileAttributes;\n\t\tproperties.Size = d.Size;\n\t\tproperties.LastWriteTimeUtc = DateTime.FromFileTimeUtc(d.ftLastWriteTime);\n\t\tproperties.CreationTimeUtc = DateTime.FromFileTimeUtc(d.ftCreationTime);\n\t\tproperties.LastAccessTimeUtc = DateTime.FromFileTimeUtc(d.ftLastAccessTime);\n\t\tif (d.dwFileAttributes.Has(FileAttributes.ReparsePoint)) properties.IsNtfsLink = 0 != IsNtfsLink_(path);\n\t\treturn true;\n\t}\n\t\n\t/// <summary>\n\t/// Gets file or directory attributes.\n\t/// </summary>\n\t/// <returns><c>false</c> if the file/directory does not exist.</returns>\n\t/// <param name=\"path\">Full path. Supports <c>@\"\\..\"</c> etc. If flag <c>UseRawPath</c> not used, supports environment variables (see <see cref=\"pathname.expand\"/>).</param>\n\t/// <param name=\"attributes\">Receives attributes, or 0 if failed.</param>\n\t/// <param name=\"flags\"></param>\n\t/// <exception cref=\"ArgumentException\">Not full path (when not used flag <c>UseRawPath</c>).</exception>\n\t/// <exception cref=\"AuException\">Failed. Not thrown if used flag <c>DontThrow</c>.</exception>\n\t/// <remarks>\n\t/// Calls API <ms>GetFileAttributes</ms>. Supports <see cref=\"lastError\"/> (useful with flag <c>DontThrow</c>).\n\t/// For NTFS links, gets attributes of the link, not of its target.\n\t/// </remarks>\n\tpublic static unsafe bool getAttributes(string path, out FileAttributes attributes, FAFlags flags = 0) {\n\t\tif (0 == (flags & FAFlags.UseRawPath)) path = pathname.NormalizeMinimally_(path); //the API supports .. etc\n\t\tvar a = Api.GetFileAttributes(path);\n\t\tif (a == (FileAttributes)(-1)) return _GetAttributesOnError(path, flags, out attributes, out _);\n\t\tattributes = a;\n\t\treturn true;\n\t}\n\t\n\tstatic unsafe bool _GetAttributesOnError(string path, FAFlags flags, out FileAttributes attr, out bool ntfsLink, Api.WIN32_FILE_ATTRIBUTE_DATA* p = null) {\n\t\tattr = 0; ntfsLink = false;\n\t\tvar ec = lastError.code;\n\t\tswitch (ec) {\n\t\tcase Api.ERROR_FILE_NOT_FOUND or Api.ERROR_PATH_NOT_FOUND or Api.ERROR_NOT_READY: //note: no ERROR_BAD_NETPATH. Let's print, it can help to debug caller code.\n\t\t\treturn false;\n\t\tcase Api.ERROR_SHARING_VIOLATION: //eg c:\\pagefile.sys. GetFileAttributes fails, but FindFirstFile succeeds.\n\t\tcase Api.ERROR_ACCESS_DENIED: //probably in a protected directory. Then FindFirstFile fails, but try anyway. Or a file marked for deletion.\n\t\t\tvar d = new Api.WIN32_FIND_DATA();\n\t\t\tvar hfind = Api.FindFirstFile(path, out d);\n\t\t\tif (hfind != -1) {\n\t\t\t\tApi.FindClose(hfind);\n\t\t\t\tattr = d.dwFileAttributes;\n\t\t\t\tif (p != null) {\n\t\t\t\t\tp->dwFileAttributes = d.dwFileAttributes;\n\t\t\t\t\tp->nFileSizeHigh = d.nFileSizeHigh;\n\t\t\t\t\tp->nFileSizeLow = d.nFileSizeLow;\n\t\t\t\t\tp->ftCreationTime = d.ftCreationTime;\n\t\t\t\t\tp->ftLastAccessTime = d.ftLastAccessTime;\n\t\t\t\t\tp->ftLastWriteTime = d.ftLastWriteTime;\n\t\t\t\t}\n\t\t\t\tntfsLink = 0 != d.IsNtfsLink;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tlastError.code = ec;\n\t\t\tattr = (FileAttributes)(-1);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tDebug_.Print(lastError.messageFor(ec));\n\t\t\tbreak;\n\t\t}\n\t\tif (0 != (flags & FAFlags.DontThrow)) return false;\n\t\tthrow new AuException(ec, $\"*get file attributes: '{path}'\");\n\t}\n\t\n\t/// <summary>\n\t/// Gets attributes.\n\t/// Returns <c>false</c> if INVALID_FILE_ATTRIBUTES or if relative path. No exceptions.\n\t/// </summary>\n\tstatic unsafe bool _GetAttributes(string path, out FileAttributes attr, out bool ntfsLink, bool useRawPath) {\n\t\tif (!useRawPath) path = pathname.NormalizeMinimally_(path, throwIfNotFullPath: false);\n\t\t//note: NormalizeMinimally_ does not remove \\ at the end. The API succeeds if \"C:\\x\\dir\\\" but fails if \"C:\\x\\file\\\" (it's good).\n\t\t\n\t\tattr = Api.GetFileAttributes(path);\n\t\tif (attr != (FileAttributes)(-1)) ntfsLink = attr.Has(FileAttributes.ReparsePoint) && 0 != IsNtfsLink_(path);\n\t\telse if (!_GetAttributesOnError(path, FAFlags.DontThrow, out attr, out ntfsLink)) return false;\n\t\t\n\t\tif (!useRawPath && !pathname.isFullPath(path)) { lastError.code = Api.ERROR_FILE_NOT_FOUND; return false; }\n\t\treturn true;\n\t}\n\t\n\t/// <summary>\n\t/// Sets file or directory attributes.\n\t/// </summary>\n\t/// <returns><c>false</c> if the file/directory does not exist.</returns>\n\t/// <param name=\"path\">Full path. Supports <c>@\"\\..\"</c> etc. If flag <c>UseRawPath</c> not used, supports environment variables (see <see cref=\"pathname.expand\"/>).</param>\n\t/// <param name=\"attributes\">Attributes to set, add or remove.</param>\n\t/// <param name=\"add\"><c>null</c> (default) - set; <c>true</c> - add; <c>false</c> - remove.</param>\n\t/// <param name=\"flags\"></param>\n\t/// <exception cref=\"ArgumentException\">Not full path (when not used flag <c>UseRawPath</c>).</exception>\n\t/// <exception cref=\"AuException\">Failed. Not thrown if used flag <c>DontThrow</c>.</exception>\n\t/// <remarks>\n\t/// Calls API <ms>SetFileAttributes</ms>.\n\t/// For NTFS links, sets attributes of the link, not of its target.\n\t/// </remarks>\n\tpublic static unsafe bool setAttributes(string path, FileAttributes attributes, bool? add = null, FAFlags flags = 0) {\n\t\tif (0 == (flags & FAFlags.UseRawPath)) path = pathname.NormalizeMinimally_(path); //the API supports .. etc\n\t\tvar a = attributes;\n\t\tif (add != null) {\n\t\t\tvar v = Api.GetFileAttributes(path);\n\t\t\tif (add.Value) a = v | attributes; else a = v & ~attributes;\n\t\t\tif (a == attributes) return true;\n\t\t}\n\t\tif (Api.SetFileAttributes(path, a)) return true;\n\t\tif (0 != (flags & FAFlags.DontThrow)) return false;\n\t\tthrow new AuException(0, $\"*set file attributes: '{path}'\");\n\t}\n\t\n\t/// <summary>\n\t/// Calls API <c>FindFirstFile</c> to determine whether <i>path</i> is a NTFS link, such as symbolic link or mount point.\n\t/// </summary>\n\t/// <param name=\"path\">Raw path (does not normalize).</param>\n\t/// <returns>-1 failed, 0 no, 1 symlink, 2 mount, 3 other.</returns>\n\tinternal static int IsNtfsLink_(string path) {\n\t\tvar hfind = Api.FindFirstFile(path, out var fd);\n\t\tif (hfind == -1) return -1;\n\t\tint R = fd.IsNtfsLink;\n\t\tApi.FindClose(hfind);\n\t\treturn R;\n\t}\n\t\n\t/// <summary>\n\t/// Gets file or directory attributes as <see cref=\"FAttr\"/> that tells whether it exists, is directory, readonly, hidden, system, NTFS link. See examples.\n\t/// </summary>\n\t/// <param name=\"path\">Full path. Supports <c>@\"\\..\"</c> etc. If <i>useRawPath</i> is <c>false</c> (default), supports environment variables (see <see cref=\"pathname.expand\"/>). Can be <c>null</c>.</param>\n\t/// <param name=\"useRawPath\">Pass <i>path</i> to the API as it is, without any normalizing and full-path checking.</param>\n\t/// <remarks>\n\t/// Supports <see cref=\"lastError\"/>. If you need exception when fails, instead call <see cref=\"getAttributes\"/>.\n\t/// Always use full path. If not full path: if <i>useRawPath</i> is <c>false</c> (default), can't find the file; if <i>useRawPath</i> is <c>true</c>, searches in \"current directory\".\n\t/// For NTFS links gets attributes of the link, not of the target; does not care whether its target exists.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// var path = @\"C:\\Test\\test.txt\";\n\t/// if (filesystem.exists(path)) print.it(\"exists\");\n\t/// if (filesystem.exists(path).File) print.it(\"exists as file\");\n\t/// if (filesystem.exists(path).Directory) print.it(\"exists as directory\");\n\t/// if (filesystem.exists(path) is FAttr { File: true, IsReadonly: false }) print.it(\"exists as file and isn't readonly\");\n\t/// switch (filesystem.exists(path)) {\n\t/// case 0: print.it(\"doesn't exist\"); break;\n\t/// case 1: print.it(\"file\"); break;\n\t/// case 2: print.it(\"directory\"); break;\n\t/// }\n\t/// ]]></code>\n\t/// </example>\n\tpublic static FAttr exists(string path, bool useRawPath = false) {\n\t\tif (_GetAttributes(path, out var a, out bool ntfsLink, useRawPath)) return new(a, true, ntfsLink);\n\t\treturn new(0, (a == (FileAttributes)(-1)) ? null : false, false);\n\t}\n\t\n\t/// <summary>\n\t/// Gets file system entry type - file, directory, NTFS link, whether it exists and is accessible.\n\t/// Returns <c>NotFound</c> (0) if does not exist. Returns <c>AccessDenied</c> (&lt; 0) if exists but this process cannot access it and get attributes.\n\t/// Calls API <ms>GetFileAttributes</ms>.\n\t/// </summary>\n\t/// <param name=\"path\">Full path. Supports <c>@\"\\..\"</c> etc. If <i>useRawPath</i> is <c>false</c> (default), supports environment variables (see <see cref=\"pathname.expand\"/>). Can be <c>null</c>.</param>\n\t/// <param name=\"useRawPath\">Pass path to the API as it is, without any normalizing and full-path checking.</param>\n\t/// <remarks>\n\t/// Supports <see cref=\"lastError\"/>. If you need exception when fails, instead call <see cref=\"getAttributes\"/>.\n\t/// Always use full path. If path is not full: if <i>useRawPath</i> is <c>false</c> (default) returns <c>NotFound</c>; if <i>useRawPath</i> is <c>true</c>, searches in \"current directory\".\n\t/// </remarks>\n\tinternal static unsafe FileIs_ ExistsAs_(string path, bool useRawPath = false) {\n\t\tif (!_GetAttributes(path, out var a, out bool ntfsLink, useRawPath)) {\n\t\t\treturn (a == (FileAttributes)(-1)) ? FileIs_.AccessDenied : FileIs_.NotFound;\n\t\t}\n\t\tvar R = a.Has(FileAttributes.Directory) ? FileIs_.Directory : FileIs_.File;\n\t\tif (ntfsLink) R |= (FileIs_)4;\n\t\treturn R;\n\t}\n\t\n\t#endregion\n\t\n\t#region enumerate, search\n\t\n\t/// <summary>\n\t/// Finds file or directory and returns full path.\n\t/// </summary>\n\t/// <returns><c>null</c> if not found.</returns>\n\t/// <remarks>\n\t/// If the <i>path</i> argument is full path, calls <see cref=\"exists\"/> and returns normalized path if exists, <c>null</c> if not.\n\t/// Else searches in these places:\n\t/// 1. <i>dirs</i>, if used.\n\t/// 2. <see cref=\"folders.ThisApp\"/>.\n\t/// 3. Calls API <ms>SearchPath</ms>, which searches in the process directory, Windows system directories, current directory, <c>PATH</c> environment variable. The search order depends on API <ms>SetSearchPathMode</ms> or registry settings.\n\t/// 4. If <i>path</i> ends with <c>\".exe\"</c>, tries to get path from registry \"App Paths\" keys.\n\t/// </remarks>\n\t/// <param name=\"path\">Full or relative path or just filename with extension. Supports network paths too.</param>\n\t/// <param name=\"dirs\">0 or more directories where to search.</param>\n\tpublic static unsafe string searchPath(string path, params string[] dirs) {\n\t\tif (path.NE()) return null;\n\t\t\n\t\tif (pathname.isFullPathExpand(path, out string s, strict: false)) {\n\t\t\tif (exists(s)) return pathname.Normalize_(s, noExpandEV: true);\n\t\t\treturn null;\n\t\t}\n\t\t\n\t\tif (dirs != null) {\n\t\t\tforeach (var d in dirs) {\n\t\t\t\tif (!pathname.isFullPathExpand(d, out s)) continue;\n\t\t\t\ts = pathname.combine(s, path);\n\t\t\t\tif (exists(s)) return pathname.Normalize_(s, noExpandEV: true);\n\t\t\t}\n\t\t}\n\t\t\n\t\ts = folders.ThisApp + path;\n\t\tif (exists(s)) return pathname.Normalize_(s, noExpandEV: true);\n\t\t\n\t\tif ((s = Api.SearchPath(null, path)) != null) return s;\n\t\t\n\t\tif (path.Ends(\".exe\", true) && path.FindAny(@\"\\/\") < 0) {\n\t\t\ttry {\n\t\t\t\ts = Registry.GetValue(@\"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\\" + path, \"\", null) as string\n\t\t\t\t\t?? Registry.GetValue(@\"HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\\" + path, \"\", null) as string;\n\t\t\t\tif (s != null) {\n\t\t\t\t\ts = _PreparePath(s.Trim('\"'));\n\t\t\t\t\tif (exists(s, true)) return s;\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception ex) { Debug_.Print(ex); }\n\t\t}\n\t\t\n\t\t//maybe PATH env var changed since this or parent process started\n\t\t//rejected. LA updates at startup an on WM_SETTINGCHANGE, and that's enough.\n\t\t//if (more.UpdatePathEnvVar_())\n\t\t//\tif ((s = Api.SearchPath(null, path)) != null) return s;\n\t\t\n\t\treturn null;\n\t}\n\t//TODO2: add overload with flags to ignore current directory etc.\n\t\n\t/// <summary>\n\t/// Gets names and other info of files and subdirectories in the specified directory.\n\t/// </summary>\n\t/// <returns>An enumerable collection of <see cref=\"FEFile\"/> objects.</returns>\n\t/// <param name=\"directoryPath\">Full path of the directory.</param>\n\t/// <param name=\"flags\"></param>\n\t/// <param name=\"fileFilter\">\n\t/// Callback function that is called for each file (but not subdirectory). Let it return <c>true</c> to include the file in results.\n\t/// Example: <c>f => f.Name.Ends(\".png\", true)</c>.\n\t/// </param>\n\t/// <param name=\"dirFilter\">\n\t/// Callback function that is called for each subdirectory. Let it return flags: 1 - include the directory in results; 2 - include its children in results.\n\t/// The return value overrides flags <see cref=\"FEFlags.OnlyFiles\"/> and <see cref=\"FEFlags.AllDescendants\"/>.\n\t/// Example: <c>d => d.Name.Eqi(\"Debug\") ? 0 : 3</c>.\n\t/// </param>\n\t/// <param name=\"errorHandler\">\n\t/// Callback function that is called when fails to get children of a subdirectory, when using flag <see cref=\"FEFlags.AllDescendants\"/>.\n\t/// Receives the subdirectory path. Can call <see cref=\"lastError.code\"/> and throw an exception. If does not throw, the enumeration continues as if the directory is empty.\n\t/// If <i>errorHandler</i> not used, then <see cref=\"enumerate\"/> throws exception. See also: flag <see cref=\"FEFlags.IgnoreInaccessible\"/>.\n\t/// </param>\n\t/// <exception cref=\"ArgumentException\"><i>directoryPath</i> is invalid path or not full path.</exception>\n\t/// <exception cref=\"DirectoryNotFoundException\"><i>directoryPath</i> directory does not exist.</exception>\n\t/// <exception cref=\"AuException\">Failed to get children of <i>directoryPath</i> or of a subdirectory.</exception>\n\t/// <remarks>\n\t/// Uses API <ms>FindFirstFile</ms>.\n\t/// \n\t/// By default gets only direct children. Use flag <see cref=\"FEFlags.AllDescendants\"/> to get all descendants.\n\t/// \n\t/// The paths that this function gets are normalized, ie may not start with exact <i>directoryPath</i> string. Expanded environment variables (see <see cref=\"pathname.expand\"/>), <c>\"..\"</c>, DOS path etc. Paths longer than <see cref=\"pathname.maxDirectoryPathLength\"/> have <c>@\"\\\\?\\\"</c> prefix (see <see cref=\"pathname.prefixLongPathIfNeed\"/>).\n\t/// \n\t/// For NTFS links (symbolic links, mount points) gets link info, not target info.\n\t/// \n\t/// These errors are ignored:\n\t/// 1. Missing target directory of a NTFS link.\n\t/// 2. If used flag <see cref=\"FEFlags.IgnoreInaccessible\"/> - access denied.\n\t/// \n\t/// When an error is ignored, the function works as if that [sub]directory is empty; does not throw exception and does not call <i>errorHandler</i>.\n\t/// \n\t/// Enumeration of a subdirectory starts immediately after the subdirectory itself is retrieved.\n\t/// </remarks>\n\tpublic static IEnumerable<FEFile> enumerate(string directoryPath, FEFlags flags = 0,\n\t\tFunc<FEFile, bool> fileFilter = null,\n\t\tFunc<FEFile, int> dirFilter = null,\n\t\tAction<string> errorHandler = null\n\t\t) {\n\t\t//tested 2021-04-30: much faster than Directory.EnumerateX in .NET 5. Faster JIT, and then > 2 times faster.\n\t\t//tested 2022-01-31: ~20% slower than Directory.EnumerateX in .NET 6. Not tested JIT. Never mind.\n\t\t//tested 2025-06-20: ~1% slower than Directory.EnumerateX in .NET 9.\n\t\t//\tIt seems .NET uses undocumented API NtQueryDirectoryFile.\n\t\t//rejected: in this func use .NET FileSystemEnumerable.\n\t\t//\tGood: faster; familiar types.\n\t\t//\tBad: something we need is missing or difficult to return or need a workaround. Eg easily detect NTFS links, get relative path, prevent recursion to NTFS link target.\n\t\t\n\t\t//TODO2: stuck at ~340000 files when recursively enumerating C:\\ProgramData\\Microsoft\\Windows\\Containers\\Layers.\n\t\t//\tEven can't end the task process normally (access denied error).\n\t\t//\tActually it just becomes very slow, and maybe would complete if I waited long enough. \n\t\t//\tDirectory.EnumerateFileSystemEntries stuck at ~61000 files.\n\t\t//\tThe TreeSize app at first it seems stuck too, but after long time completes and reports 752000 files.\n\t\t\n\t\tstring path = directoryPath;\n\t\tif (0 == (flags & FEFlags.UseRawPath)) path = _PreparePath(path);\n\t\tpath = path.RemoveSuffix('\\\\');\n\t\t\n\t\tvar d = new Api.WIN32_FIND_DATA();\n\t\tIntPtr hfind = default;\n\t\tvar stack = new Stack<_EDStackEntry>();\n\t\tbool isFirst = true;\n\t\tFileAttributes attr = 0;\n\t\tint basePathLength = path.Length;\n\t\t//var redir = new FileSystemRedirection();\n\t\t\n\t\ttry {\n\t\t\t//if (0 != (flags & FEFlags.DisableRedirection)) redir.Disable();\n\t\t\t\n\t\t\tfor (; ; ) {\n\t\t\t\tif (isFirst) {\n\t\t\t\t\tisFirst = false;\n\t\t\t\t\tvar path2 = ((path.Length <= pathname.maxDirectoryPathLength - 2) ? path : pathname.prefixLongPath(path)) + @\"\\*\";\n#if TEST_FINDFIRSTFILEEX\n\t\t\t\t\thfind = Api.FindFirstFileEx(path2, Api.FINDEX_INFO_LEVELS.FindExInfoBasic, out d, 0, default, 0);\n\t\t\t\t\t//speed: FindFirstFileEx 0-2 % slower. FindExInfoBasic makes 0-2% faster. FIND_FIRST_EX_LARGE_FETCH makes 1-50% slower.\n#else\n\t\t\t\t\thfind = Api.FindFirstFile(path2, out d);\n#endif\n\t\t\t\t\tif (hfind == -1) {\n\t\t\t\t\t\thfind = default;\n\t\t\t\t\t\tvar ec = lastError.code;\n\t\t\t\t\t\t//print.it(ec, lastError.messageFor(ec), path);\n\t\t\t\t\t\tbool itsOK = false;\n\t\t\t\t\t\tswitch (ec) {\n\t\t\t\t\t\tcase Api.ERROR_FILE_NOT_FOUND:\n\t\t\t\t\t\tcase Api.ERROR_NO_MORE_FILES:\n\t\t\t\t\t\tcase Api.ERROR_NOT_READY:\n\t\t\t\t\t\t\t//rare, because most directories have \".\" and \"..\".\n\t\t\t\t\t\t\t//FindFirstFileEx sets ERROR_NO_MORE_FILES for some.\n\t\t\t\t\t\t\t//FindFirstFile could set ERROR_FILE_NOT_FOUND (documented), but was never in my tests.\n\t\t\t\t\t\t\t//ERROR_NOT_READY when trying to access an ejected CD/DVD drive etc.\n\t\t\t\t\t\t\titsOK = true;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase Api.ERROR_ACCESS_DENIED:\n\t\t\t\t\t\t\titsOK = 0 != (flags & FEFlags.IgnoreInaccessible);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase Api.ERROR_PATH_NOT_FOUND: //the directory not found, or NTFS link target directory is missing\n\t\t\t\t\t\tcase Api.ERROR_DIRECTORY: //it is file, not directory. Error text is \"The directory name is invalid\".\n\t\t\t\t\t\tcase Api.ERROR_BAD_NETPATH: //eg \\\\COMPUTER\\MissingFolder\n\t\t\t\t\t\t\tif (stack.Count == 0 && !exists(path, true).Directory)\n\t\t\t\t\t\t\t\tthrow new DirectoryNotFoundException($\"Directory not found: '{path}'. {lastError.messageFor(ec)}\");\n\t\t\t\t\t\t\t//itsOK = (attr & Api.FILE_ATTRIBUTE_REPARSE_POINT) != 0;\n\t\t\t\t\t\t\titsOK = true; //or maybe the subdirectory was deleted after we retrieved it\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase Api.ERROR_INVALID_NAME: //eg contains invalid characters\n\t\t\t\t\t\tcase Api.ERROR_BAD_NET_NAME: //eg \\\\COMPUTER\n\t\t\t\t\t\t\tif (stack.Count == 0)\n\t\t\t\t\t\t\t\tthrow new ArgumentException(lastError.messageFor(ec));\n\t\t\t\t\t\t\titsOK = true;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!itsOK) {\n\t\t\t\t\t\t\tif (errorHandler == null || stack.Count == 0) throw new AuException(ec, $\"*enumerate directory '{path}'\");\n\t\t\t\t\t\t\tApi.SetLastError(ec); //the above code possibly changed it, although currently it doesn't\n\t\t\t\t\t\t\terrorHandler(path);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif (!Api.FindNextFile(hfind, out d)) {\n\t\t\t\t\t\tDebug.Assert(lastError.code == Api.ERROR_NO_MORE_FILES);\n\t\t\t\t\t\tApi.FindClose(hfind);\n\t\t\t\t\t\thfind = default;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (hfind == default) {\n\t\t\t\t\tif (stack.Count == 0) break;\n\t\t\t\t\tvar t = stack.Pop();\n\t\t\t\t\thfind = t.hfind; path = t.path;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvar name = d.Name;\n\t\t\t\tif (name == null) continue; //\".\", \"..\"\n\t\t\t\tattr = d.dwFileAttributes;\n\t\t\t\tbool isDir = (attr & FileAttributes.Directory) != 0;\n\t\t\t\t\n\t\t\t\tif ((flags & FEFlags.SkipHidden) != 0 && (attr & FileAttributes.Hidden) != 0) continue;\n\t\t\t\tconst FileAttributes hidSys = FileAttributes.Hidden | FileAttributes.System;\n\t\t\t\tif ((attr & hidSys) == hidSys) {\n\t\t\t\t\tif ((flags & FEFlags.SkipHiddenSystem) != 0) continue;\n\t\t\t\t\t//skip Recycle Bin etc. It is useless, prevents copying drives, etc.\n\t\t\t\t\tif (isDir && path.Ends(':')) {\n\t\t\t\t\t\tif (name.Eqi(\"$Recycle.Bin\")) continue;\n\t\t\t\t\t\tif (name.Eqi(\"System Volume Information\")) continue;\n\t\t\t\t\t\tif (name.Eqi(\"Recovery\")) continue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvar fullPath = path + @\"\\\" + name;\n\t\t\t\tif (0 != (flags & FEFlags.NeedRelativePaths)) name = fullPath[basePathLength..];\n\t\t\t\t\n\t\t\t\t//prepend @\"\\\\?\\\" etc if need. Don't change fullPath length, because then would be difficult to get relative path.\n\t\t\t\tvar fp2 = pathname.prefixLongPathIfNeed(fullPath);\n\t\t\t\t\n\t\t\t\tvar r = new FEFile(name, fp2, d, stack.Count); //never mind, don't need for dirs if no dirFilter and is flag OnlyFiles\n\t\t\t\t\n\t\t\t\tif (isDir) {\n\t\t\t\t\tint inc = dirFilter != null ? dirFilter(r) : (flags.Has(FEFlags.OnlyFiles) ? 0 : 1) | (flags.Has(FEFlags.AllDescendants) ? 2 : 0);\n\t\t\t\t\tif (0 != (1 & inc)) yield return r;\n\t\t\t\t\tif (0 == (2 & inc)) continue;\n\t\t\t\t\tif (!flags.Has(FEFlags.RecurseNtfsLinks) && 0 != d.IsNtfsLink) continue;\n\t\t\t\t\t\n\t\t\t\t\tstack.Push(new _EDStackEntry() { hfind = hfind, path = path });\n\t\t\t\t\thfind = default; path = fullPath;\n\t\t\t\t\tisFirst = true;\n\t\t\t\t} else {\n\t\t\t\t\tif (fileFilter == null || fileFilter(r)) yield return r;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfinally {\n\t\t\tif (hfind != default) Api.FindClose(hfind);\n\t\t\twhile (stack.Count > 0) Api.FindClose(stack.Pop().hfind);\n\t\t\t\n\t\t\t//redir.Revert();\n\t\t}\n\t}\n\t\n\tstruct _EDStackEntry { internal IntPtr hfind; internal string path; }\n\t\n\t/// <summary>\n\t/// Gets names and other info of matching files in the specified directory.\n\t/// </summary>\n\t/// <param name=\"directoryPath\">Full path of the directory.</param>\n\t/// <param name=\"pattern\">\n\t/// File name pattern. Format: [wildcard expression](xref:wildcard_expression). Used only for files, not for subdirectories. Can be <c>null</c>.\n\t/// Examples:\n\t/// <br/>• <c>\"*.png\"</c> (only png files),\n\t/// <br/>• <c>\"**m *.png||*.bmp\"</c> (only png and bmp files),\n\t/// <br/>• <c>\"**nm *.png||*.bmp\"</c> (all files except png and bmp),\n\t/// <br/>• <c>@\"**r \\.html?$\"</c> (regular expression that matches <c>.htm</c> and <c>.html</c> files).\n\t/// </param>\n\t/// <param name=\"flags\">Flags. The function also adds flag <c>OnlyFiles</c>.</param>\n\t/// <exception cref=\"ArgumentException\">\n\t/// <i>directoryPath</i> is invalid path or not full path.\n\t/// Invalid <i>pattern</i> (<c>\"**options \"</c> or regular expression).\n\t/// </exception>\n\t/// <exception cref=\"DirectoryNotFoundException\"><i>directoryPath</i> directory does not exist.</exception>\n\t/// <exception cref=\"AuException\">Failed to get children of <i>directoryPath</i> or of a subdirectory.</exception>\n\t/// <inheritdoc cref=\"enumerate(string, FEFlags, Func{FEFile, bool}, Func{FEFile, int}, Action{string})\"/>\n\tpublic static IEnumerable<FEFile> enumFiles(string directoryPath, string pattern = null, FEFlags flags = 0) {\n\t\tflags |= FEFlags.OnlyFiles;\n\t\tif (pattern == null) return enumerate(directoryPath, flags);\n\t\twildex w = pattern;\n\t\treturn enumerate(directoryPath, flags, f => w.Match(f.Name));\n\t}\n\t\n\t/// <summary>\n\t/// Gets names and other info of matching subdirectories in the specified directory.\n\t/// </summary>\n\t/// <param name=\"directoryPath\">Full path of the directory.</param>\n\t/// <param name=\"pattern\">\n\t/// Directory name pattern. Format: [wildcard expression](xref:wildcard_expression). Can be <c>null</c>.\n\t/// </param>\n\t/// <param name=\"flags\"></param>\n\t/// <exception cref=\"ArgumentException\">\n\t/// <i>directoryPath</i> is invalid path or not full path.\n\t/// Invalid <i>pattern</i> (<c>\"**options \"</c> or regular expression).\n\t/// </exception>\n\t/// <exception cref=\"DirectoryNotFoundException\"><i>directoryPath</i> directory does not exist.</exception>\n\t/// <exception cref=\"AuException\">Failed to get children of <i>directoryPath</i> or of a subdirectory.</exception>\n\t/// <inheritdoc cref=\"enumerate(string, FEFlags, Func{FEFile, bool}, Func{FEFile, int}, Action{string})\"/>\n\tpublic static IEnumerable<FEFile> enumDirectories(string directoryPath, string pattern = null, FEFlags flags = 0) {\n\t\tif (pattern == null) return filesystem.enumerate(directoryPath, flags, _ => false);\n\t\twildex w = pattern;\n\t\tint enumChildren = flags.Has(FEFlags.AllDescendants) ? 2 : 0;\n\t\treturn filesystem.enumerate(directoryPath, flags, _ => false, d => (w.Match(d.Name) ? 1 : 0) | enumChildren);\n\t}\n\t\n\t#endregion\n\t\n\t#region move, copy, rename, delete\n\t\n\tenum _FileOp { Rename, Move, Copy, }\n\t\n\tstatic unsafe void _FileOperation(_FileOp op, bool into, string path1, string path2, FIfExists ifExists,\n\t\tFCFlags copyFlags = 0, Func<FEFile, bool> copyFileFilter = null, Func<FEFile, int> copyDirFilter = null\n\t\t) {\n\t\tstring opName = (op == _FileOp.Rename) ? \"rename\" : ((op == _FileOp.Move) ? \"move\" : \"copy\");\n\t\tpath1 = _PreparePath(path1);\n\t\tvar type1 = ExistsAs_(path1, true);\n\t\tif (type1 <= 0) throw new FileNotFoundException($\"Failed to {opName}. File not found: '{path1}'\");\n\t\t\n\t\tif (op == _FileOp.Rename) {\n\t\t\top = _FileOp.Move;\n\t\t\tif (pathname.isInvalidName(path2)) throw new ArgumentException($\"Invalid filename: '{path2}'\");\n\t\t\tpath2 = pathname.Combine_(_RemoveFilename(path1), path2);\n\t\t} else {\n\t\t\tstring path2Parent;\n\t\t\tif (into) {\n\t\t\t\tpath2Parent = _PreparePath(path2);\n\t\t\t\tpath2 = pathname.Combine_(path2Parent, _GetFilename(path1));\n\t\t\t} else {\n\t\t\t\tpath2 = _PreparePath(path2);\n\t\t\t\tpath2Parent = _RemoveFilename(path2, true);\n\t\t\t}\n\t\t\tif (path2Parent != null) {\n\t\t\t\ttry { _CreateDirectory(path2Parent, pathIsPrepared: true); }\n\t\t\t\tcatch (Exception ex) { throw new AuException($\"*create directory: '{path2Parent}'\", ex); }\n\t\t\t}\n\t\t}\n\t\t\n\t\tbool ok = false, copy = op == _FileOp.Copy, deleteSource = false, mergeDirectory = false;\n\t\tvar del = new _SafeDeleteExistingDirectory();\n\t\ttry {\n\t\t\tif (ifExists == FIfExists.MergeDirectory && type1 != FileIs_.Directory) ifExists = FIfExists.Fail;\n\t\t\t\n\t\t\tif (ifExists == FIfExists.Fail) {\n\t\t\t\t//API will fail if exists. We don't use API flags 'replace existing'.\n\t\t\t} else {\n\t\t\t\t//Delete, RenameExisting, MergeDirectory\n\t\t\t\t//bool deleted = false;\n\t\t\t\tvar existsAs = ExistsAs_(path2, true);\n\t\t\t\tbool existsAsDir = existsAs is FileIs_.Directory or FileIs_.NtfsLinkDirectory;\n\t\t\t\tswitch (existsAs) {\n\t\t\t\tcase FileIs_.NotFound:\n\t\t\t\t\t//deleted = true;\n\t\t\t\t\tbreak;\n\t\t\t\tcase FileIs_.AccessDenied:\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tif (more.isSameFile(path1, path2, useSymlink: true)) {\n\t\t\t\t\t\t//eg renaming \"file.txt\" to \"FILE.txt\"\n\t\t\t\t\t\tDebug_.Print(\"same file\");\n\t\t\t\t\t\t//deleted = true;\n\t\t\t\t\t\t//copy will fail, move will succeed\n\t\t\t\t\t} else if (ifExists == FIfExists.RenameNew) {\n\t\t\t\t\t\tpath2 = pathname.makeUnique(path2, existsAsDir);\n\t\t\t\t\t} else if (ifExists == FIfExists.MergeDirectory && existsAsDir) {\n\t\t\t\t\t\tif (type1 is FileIs_.Directory or FileIs_.NtfsLinkDirectory) {\n\t\t\t\t\t\t\t//deleted = true;\n\t\t\t\t\t\t\tmergeDirectory = true;\n\t\t\t\t\t\t\tif (!copy) { copy = true; deleteSource = true; }\n\t\t\t\t\t\t} // else API will fail. We refuse to replace a directory with a file.\n\t\t\t\t\t} else if (ifExists == FIfExists.RenameExisting || existsAs == FileIs_.Directory) {\n\t\t\t\t\t\t//deleted = \n\t\t\t\t\t\tdel.Rename(path2, ifExists == FIfExists.RenameExisting);\n\t\t\t\t\t\t//Rename to a temp name. Finally delete if ok (if !RenameExisting), undo if failed.\n\t\t\t\t\t\t//It also solves this problem: if we delete the directory now, need to ensure that it does not delete the source directory, which is quite difficult.\n\t\t\t\t\t} else {\n\t\t\t\t\t\t//deleted = 0 ==\n\t\t\t\t\t\t_DeleteL(path2, existsAs == FileIs_.NtfsLinkDirectory);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\t//if(!deleted) throw new AuException(Api.ERROR_FILE_EXISTS, $\"*{opName}\"); //don't need, later API will fail\n\t\t\t}\n\t\t\t\n\t\t\tif (!copy) {\n\t\t\t\t//note: don't use MOVEFILE_COPY_ALLOWED, because then moving directory to another drive fails with ERROR_ACCESS_DENIED and we don't know that the reason is different drive\n\t\t\t\tif (ok = Api.MoveFileEx(path1, path2, 0)) return;\n\t\t\t\tif (lastError.code == Api.ERROR_NOT_SAME_DEVICE) {\n\t\t\t\t\tcopy = true;\n\t\t\t\t\tdeleteSource = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tif (copy) {\n\t\t\t\tif (type1 == FileIs_.Directory) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\t_CopyDirectory(path1, path2, mergeDirectory, copyFlags, copyFileFilter, copyDirFilter);\n\t\t\t\t\t\tok = true;\n\t\t\t\t\t}\n\t\t\t\t\tcatch (Exception ex) when (op != _FileOp.Copy) {\n\t\t\t\t\t\tthrow new AuException($\"*{opName} '{path1}' to '{path2}'\", ex);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif (type1 == FileIs_.NtfsLinkDirectory)\n\t\t\t\t\t\tok = Api.CreateDirectoryEx(path1, path2, default);\n\t\t\t\t\telse\n\t\t\t\t\t\tok = Api.CopyFileEx(path1, path2, Api.COPY_FILE_FAIL_IF_EXISTS | Api.COPY_FILE_COPY_SYMLINK);\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tif (!ok) throw new AuException(0, $\"*{opName} '{path1}' to '{path2}'\");\n\t\t\t\n\t\t\tif (deleteSource) {\n\t\t\t\ttry {\n\t\t\t\t\t_Delete(path1);\n\t\t\t\t}\n\t\t\t\tcatch (Exception ex) {\n\t\t\t\t\tif (!path1.Ends(':')) //moving drive contents. Deleted contents but cannot delete drive.\n\t\t\t\t\t\tprint.warning($\"Failed to delete '{path1}' after copying it to another drive. {ex.Message}\");\n\t\t\t\t\t//throw new AuException(\"*move\", ex); //don't. MoveFileEx also succeeds even if fails to delete source.\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfinally {\n\t\t\t//dialog.show();\n\t\t\tdel.Finally(ok);\n\t\t}\n\t}\n\t\n\t//note: if merge, the destination directory must exist\n\tstatic unsafe void _CopyDirectory(string path1, string path2, bool merge, FCFlags copyFlags,\n\t\tFunc<FEFile, bool> fileFilter, Func<FEFile, int> dirFilter\n\t\t) {\n\t\t//FUTURE: add progressInterface parameter. Create a default interface implementation class that supports progress dialog and/or progress in taskbar button. Or instead create a ShellCopy function.\n\t\t//FUTURE: maybe add errorHandler parameter. Call it here when fails to copy, and also pass to Enumerate which calls it when fails to enum.\n\t\t\n\t\t//use intermediate array, and get it before creating path2 directory. It requires more memory, but is safer. Without it, eg bad things happen when copying a directory into itself.\n\t\tvar edFlags = FEFlags.AllDescendants | FEFlags.NeedRelativePaths | FEFlags.UseRawPath | (FEFlags)copyFlags;\n\t\tvar a = enumerate(path1, edFlags, fileFilter, dirFilter).ToArray();\n\t\t\n\t\tif (copyFlags.Has(FCFlags.NoEmptyDirectories)) {\n\t\t\tfor (int i = a.Length; --i >= 0;) {\n\t\t\t\tvar f = a[i];\n\t\t\t\tif (!f.IsDirectory) continue;\n\t\t\t\tif (i < a.Length - 1 && a[i + 1] is FEFile ff) {\n\t\t\t\t\tif (ff.Name.Starts(f.Name) && ff.Name.Eq(f.Name.Length, '\\\\')) continue;\n\t\t\t\t}\n\t\t\t\ta[i] = null;\n\t\t\t}\n\t\t}\n\t\t\n\t\tbool ok = false;\n\t\tstring s1 = null, s2 = null;\n\t\tif (!merge) {\n\t\t\tif (!path1.Ends(@\":\\\")) ok = Api.CreateDirectoryEx(path1, path2, default);\n\t\t\tif (!ok) ok = Api.CreateDirectory(path2, default);\n\t\t\tif (!ok) goto ge;\n\t\t}\n\t\t\n\t\tstring prevParentDir = null;\n\t\tforeach (var f in a) {\n\t\t\tif (f == null) continue;\n\t\t\ts1 = f.FullPath; s2 = pathname.prefixLongPathIfNeed(path2 + f.Name);\n\t\t\t//print.it(s2);\n\t\t\t\n\t\t\t//create intermediate dirs if need, eg if dirFilter returned 2 (don't include the dir but include its children)\n\t\t\t//TODO3: CreateDirectoryEx\n\t\t\tif (f.Level > 0 && (fileFilter != null || dirFilter != null)) {\n\t\t\t\tint ifn = _FindFilename(s2) - 1;\n\t\t\t\tif (prevParentDir == null || !s2.AsSpan(0, ifn).Eq(prevParentDir)) { //optimize to avoid this code for each file\n\t\t\t\t\tprevParentDir = s2[..ifn];\n\t\t\t\t\t_CreateDirectory(prevParentDir, pathIsPrepared: true);\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tif (f.IsDirectory) {\n\t\t\t\tif (merge) switch (exists(s2, true)) {\n\t\t\t\t\tcase 2: continue; //never mind: check NTFS link mismatch\n\t\t\t\t\tcase 1: _DeleteL(s2, false); break;\n\t\t\t\t\t}\n\t\t\t\t\n\t\t\t\tok = Api.CreateDirectoryEx(s1, s2, default);\n\t\t\t\tif (!ok && !f.IsNtfsLink) ok = Api.CreateDirectory(s2, default);\n\t\t\t} else {\n\t\t\t\tif (merge && getAttributes(s2, out var attr, FAFlags.DontThrow | FAFlags.UseRawPath)) {\n\t\t\t\t\tconst FileAttributes badAttr = FileAttributes.ReadOnly | FileAttributes.Hidden;\n\t\t\t\t\tif (0 != (attr & FileAttributes.Directory)) _Delete(s2);\n\t\t\t\t\telse if (0 != (attr & badAttr)) Api.SetFileAttributes(s2, attr & ~badAttr);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tuint fl = Api.COPY_FILE_COPY_SYMLINK; if (!merge) fl |= Api.COPY_FILE_FAIL_IF_EXISTS;\n\t\t\t\tok = Api.CopyFileEx(s1, s2, fl);\n\t\t\t}\n\t\t\tif (!ok) {\n\t\t\t\tif (f.IsNtfsLink) {\n\t\t\t\t\t//To create or copy NTFS links, need SeCreateSymbolicLinkPrivilege privilege.\n\t\t\t\t\t//Admins have it, else this process cannot get it.\n\t\t\t\t\t//Debug_.Print($\"failed to copy NTFS link '{s1}'. It's OK, skipped it. Error: {lastError.messageFor()}\");\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (0 != (copyFlags & FCFlags.IgnoreInaccessible)) {\n\t\t\t\t\tif (lastError.code == Api.ERROR_ACCESS_DENIED) continue;\n\t\t\t\t}\n\t\t\t\tgoto ge;\n\t\t\t}\n\t\t}\n\t\treturn;\n\t\tge:\n\t\tstring se = $\"*copy directory '{path1}' to '{path2}'\";\n\t\tif (s1 != null) se += $\" ('{s1}' to '{s2}')\";\n\t\tthrow new AuException(0, se);\n\t\t//never mind: wrong API error code if path1 and path2 is the same directory.\n\t}\n\t\n\tstruct _SafeDeleteExistingDirectory {\n\t\tstring _oldPath, _tempPath;\n\t\tbool _dontDelete;\n\t\t\n\t\t/// <summary>\n\t\t/// note: path must be normalized.\n\t\t/// </summary>\n\t\tinternal bool Rename(string path, bool dontDelete = false) {\n\t\t\tif (path.Length > pathname.maxDirectoryPathLength - 10) path = pathname.prefixLongPath(path);\n\t\t\tstring tempPath = null;\n\t\t\tint iFN = _FindFilename(path);\n\t\t\tstring s1 = path[..iFN] + \"old\", s2 = \" \" + path[iFN..];\n\t\t\tfor (int i = 1; ; i++) {\n\t\t\t\ttempPath = s1 + i + s2;\n\t\t\t\tif (!exists(tempPath, true)) break;\n\t\t\t}\n\t\t\tif (!Api.MoveFileEx(path, tempPath, 0)) return false;\n\t\t\t_oldPath = path; _tempPath = tempPath; _dontDelete = dontDelete;\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\tinternal bool Finally(bool succeeded) {\n\t\t\tif (_tempPath == null) return true;\n\t\t\tif (!succeeded) {\n\t\t\t\tif (!Api.MoveFileEx(_tempPath, _oldPath, 0)) return false;\n\t\t\t} else if (!_dontDelete) {\n\t\t\t\ttry { _Delete(_tempPath); } catch { return false; }\n\t\t\t}\n\t\t\t_oldPath = _tempPath = null;\n\t\t\treturn true;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Renames file or directory.\n\t/// </summary>\n\t/// <param name=\"path\">Full path.</param>\n\t/// <param name=\"newName\">New name without path. Example: <c>\"name.txt\"</c>.</param>\n\t/// <param name=\"ifExists\"></param>\n\t/// <exception cref=\"ArgumentException\">\n\t/// - <i>path</i> is not full path (see <see cref=\"pathname.isFullPath\"/>).\n\t/// - <i>newName</i> is invalid filename.\n\t/// </exception>\n\t/// <exception cref=\"FileNotFoundException\"><i>path</i> not found.</exception>\n\t/// <exception cref=\"AuException\">Failed.</exception>\n\t/// <remarks>\n\t/// Uses API <ms>MoveFileEx</ms>.\n\t/// </remarks>\n\tpublic static void rename(string path, string newName, FIfExists ifExists = FIfExists.Fail) {\n\t\t_FileOperation(_FileOp.Rename, false, path, newName, ifExists);\n\t}\n\t\n\t/// <summary>\n\t/// Moves (changes path of) file or directory.\n\t/// </summary>\n\t/// <param name=\"path\">Full path.</param>\n\t/// <param name=\"newPath\">\n\t/// New full path.\n\t/// <para>NOTE: It is not the new parent directory. See <see cref=\"moveTo\"/>.</para>\n\t/// </param>\n\t/// <param name=\"ifExists\"></param>\n\t/// <exception cref=\"ArgumentException\"><i>path</i> or <i>newPath</i> is not full path (see <see cref=\"pathname.isFullPath\"/>).</exception>\n\t/// <exception cref=\"FileNotFoundException\"><i>path</i> not found.</exception>\n\t/// <exception cref=\"AuException\">Failed.</exception>\n\t/// <remarks>\n\t/// In most cases uses API <ms>MoveFileEx</ms>. It's fast, because don't need to copy files.\n\t/// In these cases copies/deletes: destination is on another drive; need to merge directories.\n\t/// When need to copy, does not copy security properties; sets default.\n\t/// Creates the destination directory if does not exist (see <see cref=\"createDirectory\"/>).\n\t/// If <i>path</i> and <i>newPath</i> share the same parent directory, just renames the file.\n\t/// </remarks>\n\tpublic static void move(string path, string newPath, FIfExists ifExists = FIfExists.Fail) {\n\t\t_FileOperation(_FileOp.Move, false, path, newPath, ifExists);\n\t}\n\t\n\t/// <summary>\n\t/// Moves file or directory into another directory.\n\t/// </summary>\n\t/// <param name=\"path\">Full path.</param>\n\t/// <param name=\"newDirectory\">Full path of the new parent directory.</param>\n\t/// <param name=\"ifExists\"></param>\n\t/// <exception cref=\"ArgumentException\">\n\t/// - <i>path</i> or <i>newDirectory</i> is not full path (see <see cref=\"pathname.isFullPath\"/>).\n\t/// - <i>path</i> is drive. To move drive content, use <see cref=\"move\"/>.\n\t/// </exception>\n\t/// <exception cref=\"FileNotFoundException\"><i>path</i> not found.</exception>\n\t/// <exception cref=\"AuException\">Failed.</exception>\n\t/// <remarks>\n\t/// In most cases uses API <ms>MoveFileEx</ms>. It's fast, because don't need to copy files.\n\t/// In these cases copies/deletes: destination is on another drive; need to merge directories.\n\t/// When need to copy, does not copy security properties; sets default.\n\t/// Creates the destination directory if does not exist (see <see cref=\"createDirectory\"/>).\n\t/// </remarks>\n\tpublic static void moveTo(string path, string newDirectory, FIfExists ifExists = FIfExists.Fail) {\n\t\t_FileOperation(_FileOp.Move, true, path, newDirectory, ifExists);\n\t}\n\t\n\t/// <summary>\n\t/// Copies file or directory.\n\t/// </summary>\n\t/// <param name=\"path\">Full path.</param>\n\t/// <param name=\"newPath\">\n\t/// Full path of the destination.\n\t/// <para>NOTE: It is not the new parent directory. See <see cref=\"copyTo\"/>.</para>\n\t/// </param>\n\t/// <param name=\"ifExists\"></param>\n\t/// <param name=\"copyFlags\">Options used when copying directory.</param>\n\t/// <param name=\"fileFilter\">Callback function that decides which files to copy when copying directory. See <see cref=\"enumerate(string, FEFlags, Func{FEFile, bool}, Func{FEFile, int}, Action{string})\"/>. Note: this function uses <see cref=\"FEFlags.NeedRelativePaths\"/>.</param>\n\t/// <param name=\"dirFilter\">Callback function that decides which subdirectories to copy when copying directory. See <see cref=\"enumerate(string, FEFlags, Func{FEFile, bool}, Func{FEFile, int}, Action{string})\"/>. Note: this function uses <see cref=\"FEFlags.NeedRelativePaths\"/>.</param>\n\t/// <exception cref=\"ArgumentException\"><i>path</i> or <i>newPath</i> is not full path (see <see cref=\"pathname.isFullPath\"/>).</exception>\n\t/// <exception cref=\"FileNotFoundException\"><i>path</i> not found.</exception>\n\t/// <exception cref=\"AuException\">Failed.</exception>\n\t/// <remarks>\n\t/// Uses API <ms>CopyFileEx</ms> and <ms>CreateDirectoryEx</ms>.\n\t/// On Windows 7 does not copy security properties; sets default.\n\t/// Does not copy symbolic links (silently skips, no exception) if this process is not running as administrator.\n\t/// Creates the destination directory if does not exist (see <see cref=\"createDirectory\"/>).\n\t/// </remarks>\n\tpublic static void copy(string path, string newPath, FIfExists ifExists = FIfExists.Fail,\n\t\tFCFlags copyFlags = 0, Func<FEFile, bool> fileFilter = null, Func<FEFile, int> dirFilter = null\n\t\t) {\n\t\t_FileOperation(_FileOp.Copy, false, path, newPath, ifExists, copyFlags, fileFilter, dirFilter);\n\t}\n\t\n\t/// <summary>\n\t/// Copies file or directory into another directory.\n\t/// </summary>\n\t/// <param name=\"newDirectory\">Full path of the new parent directory.</param>\n\t/// <exception cref=\"ArgumentException\">\n\t/// - <i>path</i> or <i>newDirectory</i> is not full path (see <see cref=\"pathname.isFullPath\"/>).\n\t/// - <i>path</i> is drive. To copy drive content, use <see cref=\"copy\"/>.\n\t/// </exception>\n\t/// <exception cref=\"FileNotFoundException\"><i>path</i> not found.</exception>\n\t/// <exception cref=\"AuException\">Failed.</exception>\n\t/// <inheritdoc cref=\"copy\"/>\n\tpublic static void copyTo(string path, string newDirectory, FIfExists ifExists = FIfExists.Fail,\n\t\tFCFlags copyFlags = 0, Func<FEFile, bool> fileFilter = null, Func<FEFile, int> dirFilter = null\n\t\t) {\n\t\t_FileOperation(_FileOp.Copy, true, path, newDirectory, ifExists, copyFlags, fileFilter, dirFilter);\n\t}\n\t\n\t/// <summary>\n\t/// Deletes file or directory if exists.\n\t/// </summary>\n\t/// <returns><c>true</c> if deleted, <c>false</c> if failed (with flag <c>CanFail</c>), <c>null</c> if did not exist.</returns>\n\t/// <param name=\"path\">Full path.</param>\n\t/// <param name=\"flags\"></param>\n\t/// <exception cref=\"ArgumentException\"><i>path</i> is not full path (see <see cref=\"pathname.isFullPath\"/>).</exception>\n\t/// <exception cref=\"AuException\">Failed.</exception>\n\t/// <remarks>\n\t/// Does nothing if it does not exist (no exception).\n\t/// If directory, also deletes all its files and subdirectories. If fails to delete some, tries to delete as many as possible.\n\t/// Deletes read-only files too.\n\t/// Does not show any message boxes etc (confirmation, error, UAC consent, progress).\n\t/// \n\t/// Some reasons why this function can fail:\n\t/// 1. The file is open (in any process). Or a file in the directory is open.\n\t/// 2. This process does not have security permissions to access or delete the file or directory or some of its descendants.\n\t/// 3. The directory is (or contains) the \"current directory\" (in any process).\n\t/// </remarks>\n\tpublic static bool? delete(string path, FDFlags flags = 0)\n\t\t=> _Delete(_PreparePath(path), flags);\n\t\n\t/// <summary>\n\t/// Deletes multiple files or/and directories.\n\t/// </summary>\n\t/// <returns><c>true</c> if deleted all, <c>false</c> if failed to delete all or some (with flag <c>CanFail</c>), <c>null</c> if none existed.</returns>\n\t/// <param name=\"paths\">string array, <c>List</c> or other collection. Full paths.</param>\n\t/// <param name=\"flags\"></param>\n\t/// <exception cref=\"ArgumentException\"><i>path</i> is not full path (see <see cref=\"pathname.isFullPath\"/>).</exception>\n\t/// <exception cref=\"AggregateException\">Failed to delete all or some items. The exception object contains one <see cref=\"AuException\"/> for each failed-to-delete item.</exception>\n\t/// <remarks>\n\t/// This overload is faster when using Recycle Bin.\n\t/// If fails to delete some items specified in the list, deletes as many as possible.\n\t/// </remarks>\n\tpublic static bool? delete(IEnumerable<string> paths, FDFlags flags = 0) {\n\t\tif (flags.Has(FDFlags.RecycleBin)) {\n\t\t\tvar a = new List<string>();\n\t\t\tforeach (var v in paths) {\n\t\t\t\tvar s = _PreparePath(v);\n\t\t\t\tif (exists(s, true)) a.Add(s);\n\t\t\t}\n\t\t\tif (a.Count == 0) return null;\n\t\t\tif (_DeleteShell(null, true, a)) return true;\n\t\t\tDebug_.Print(\"_DeleteShell failed\");\n\t\t\t//if (flags.Has(FDFlags.CanFail)) return false; //no, the shell API does not try to delete other items if fails to delete some\n\t\t\t//flags &= ~FDFlags.RecycleBin; //no\n\t\t}\n\t\t\n\t\tbool? R = null;\n\t\tif (flags.Has(FDFlags.CanFail)) {\n\t\t\tforeach (var v in paths) {\n\t\t\t\tswitch (delete(v, flags)) {\n\t\t\t\tcase true: R ??= true; break;\n\t\t\t\tcase false: R = false; break;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn R;\n\t\t} else {\n\t\t\tList<Exception> ae = null;\n\t\t\tforeach (var v in paths) {\n\t\t\t\ttry { if (delete(v, flags) == true) R = true; }\n\t\t\t\tcatch (AuException e1) { (ae ??= new()).Add(e1); }\n\t\t\t}\n\t\t\treturn ae == null ? R : throw new AggregateException(\"Failed to delete.\", ae);\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// note: path must be normalized.\n\t/// </summary>\n\tstatic bool? _Delete(string path, FDFlags flags = 0) {\n\t\tbool canFail = flags.Has(FDFlags.CanFail);\n\t\t\n\t\tvar type = ExistsAs_(path, true);\n\t\tif (type == FileIs_.NotFound) return null;\n\t\tif (type == FileIs_.AccessDenied) return canFail ? false : throw new AuException(0, $\"*delete '{path}'\");\n\t\t\n\t\tif (flags.Has(FDFlags.RecycleBin)) {\n\t\t\tif (_DeleteShell(path, true)) return true;\n\t\t\tif (canFail) return false;\n\t\t\tDebug_.Print(\"_DeleteShell failed\");\n\t\t}\n\t\t\n\t\tint ec = 0;\n\t\tif (type == FileIs_.Directory) {\n\t\t\tvar a = enumerate(path, FEFlags.AllDescendants | FEFlags.UseRawPath | FEFlags.IgnoreInaccessible).ToArray();\n\t\t\t//print.it(a);\n\t\t\tfor (int i = a.Length; --i >= 0;) { //directories always are before their files, and will be empty when deleting in reverse\n\t\t\t\tvar f = a[i];\n\t\t\t\tvar at = f.Attributes;\n\t\t\t\tif (at.Has(FileAttributes.ReadOnly)) Api.SetFileAttributes(path, at & ~FileAttributes.ReadOnly);\n\t\t\t\t_DeleteL(f.FullPath, f.IsDirectory, canFail); //delete as many as possible\n\t\t\t}\n\t\t\tec = _DeleteL(path, true, canFail);\n\t\t\tif (ec == 0) {\n\t\t\t\t//notify shell. Else, if it was open in Explorer, it shows an error message box.\n\t\t\t\t//Info: .NET does not notify; SHFileOperation does.\n\t\t\t\tShellNotify_(Api.SHCNE_RMDIR, path);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (!canFail) {\n\t\t\t\tDebug_.Print(\"Using _DeleteShell.\");\n\t\t\t\tif (_DeleteShell(path, false)) return true;\n\t\t\t}\n\t\t} else {\n\t\t\tec = _DeleteL(path, type == FileIs_.NtfsLinkDirectory, canFail);\n\t\t\tif (ec == 0) return true;\n\t\t}\n\t\t\n\t\tif (exists(path, true)) return canFail ? false : throw new AuException(ec, $\"*delete '{path}'\");\n\t\t\n\t\t//info:\n\t\t//RemoveDirectory fails if not empty.\n\t\t//Directory.Delete fails if a descendant is read-only, etc.\n\t\t//Also both fail if a [now deleted] subfolder containing files was open in Explorer. Workaround: sleep/retry.\n\t\t//_DeleteShell usually does not have these problems. But it is very slow.\n\t\t//But all fail if it is current directory in any process. If in current process, _DeleteShell succeeds; it makes current directory = its parent.\n\t\treturn true;\n\t}\n\t\n\tstatic int _DeleteL(string path, bool dir, bool canFail = false) {\n\t\t//print.it(dir, path);\n\t\tif (dir ? Api.RemoveDirectory(path) : Api.DeleteFile(path)) return 0;\n\t\tvar ec = lastError.code;\n\t\tif (ec == Api.ERROR_ACCESS_DENIED) {\n\t\t\tvar a = Api.GetFileAttributes(path);\n\t\t\tif (a != (FileAttributes)(-1) && 0 != (a & FileAttributes.ReadOnly)) {\n\t\t\t\tApi.SetFileAttributes(path, a & ~FileAttributes.ReadOnly);\n\t\t\t\tif (dir ? Api.RemoveDirectory(path) : Api.DeleteFile(path)) return 0;\n\t\t\t\tec = lastError.code;\n\t\t\t}\n\t\t}\n\t\tif (!canFail) {\n\t\t\tfor (int i = 1; (ec is Api.ERROR_SHARING_VIOLATION or Api.ERROR_LOCK_VIOLATION or Api.ERROR_DIR_NOT_EMPTY) && i <= 50; i++) {\n\t\t\t\t//ERROR_DIR_NOT_EMPTY: see comments above about Explorer. Also fails in other cases, eg when a file was opened in a web browser.\n\t\t\t\tDebug_.PrintIf(i > 1, (ec == Api.ERROR_DIR_NOT_EMPTY ? \"ERROR_DIR_NOT_EMPTY when empty. Retry \" : \"ERROR_SHARING_VIOLATION. Retry \") + i.ToString());\n\t\t\t\tThread.Sleep(15);\n\t\t\t\tif (dir ? Api.RemoveDirectory(path) : Api.DeleteFile(path)) return 0;\n\t\t\t\tec = lastError.code;\n\t\t\t}\n\t\t}\n\t\tif (ec is Api.ERROR_FILE_NOT_FOUND or Api.ERROR_PATH_NOT_FOUND) return 0;\n\t\tDebug_.Print(\"_DeleteL failed. \" + lastError.messageFor(ec) + \"  \" + path\n\t\t\t+ (dir ? (\"   Children: \" + string.Join(\" | \", enumerate(path).Select(f => f.Name))) : null));\n\t\treturn ec;\n\t\t\n\t\t//never mind: .NET also calls DeleteVolumeMountPoint if it is a mount point. Somehow only for recursed dirs.\n\t\t//\tBut I did not find in MSDN doc that need to do it before calling removedirectory. I think OS should unmount automatically.\n\t\t//\tTested on Win10, works without unmounting explicitly. Even Explorer updates its current folder without notification.\n\t}\n\t\n\tstatic bool _DeleteShell(string path, bool recycle, List<string> a = null) {\n\t\tif (a != null) path = string.Join(\"\\0\", a);\n\t\tif (wildex.hasWildcardChars(path)) throw new ArgumentException(\"*? not supported.\");\n\t\tvar x = new Api.SHFILEOPSTRUCT() { wFunc = Api.FO_DELETE };\n\t\tuint f = Api.FOF_NO_UI; //info: FOF_NO_UI includes 4 flags - noerrorui, silent, noconfirm, noconfirmmkdir\n\t\tif (recycle) f |= Api.FOF_ALLOWUNDO; else f |= Api.FOF_NO_CONNECTED_ELEMENTS;\n\t\tx.fFlags = (ushort)f;\n\t\tx.pFrom = path + \"\\0\";\n\t\tx.hwnd = wnd.getwnd.root;\n\t\t\n\t\t//Call SHFileOperation in another thread. Because it pumps messages, including WM_PAINT and WM_TIMER, which causes random bugs. Also it's better to always call it in a STA thread.\n\t\t//\tHowever .NET wait methods pump messages too, although less. To reproduce, try to delete eg 10 files.\n\t\t//int r = Api.SHFileOperation(x);\n\t\t//int r = Task.Run(() => Api.SHFileOperation(x)).Result_(); //slower\n\t\t//int r = Task.Factory.StartNew(() => Api.SHFileOperation(x), default, 0, StaTaskScheduler_.Default).Result_();\n\t\t//run.thread(() => { Api.SHFileOperation(x); }).Join();\n\t\t\n\t\tusing var wh = run.thread(out _, out _, () => { try { Api.SHFileOperation(x); } catch (Exception ex) { Debug_.Print(ex); } });\n\t\twait.forHandle(0, 0, wh.DangerousGetHandle());\n\t\t\n\t\t//if(r != 0 || x.fAnyOperationsAborted) return false; //do not use fAnyOperationsAborted, it can be true even if finished to delete. Also, I guess it cannot be aborted because there is no UI, because we use FOF_SILENT to avoid deactivating the active window even when the UI is not displayed.\n\t\t//if(r != 0) return false; //after all, I don't trust this too\n\t\t//in some cases API returns 0 but does not delete. For example when path too long.\n\t\tif (a == null) {\n\t\t\tif (exists(path, true)) return false;\n\t\t} else {\n\t\t\tforeach (var v in a) if (exists(v, true)) return false;\n\t\t}\n\t\treturn true;\n\t\t\n\t\t//Also tested IFileOperation, but it is even slower.\n\t\t\n\t\t//Unsuccessfully tried to add flag 'if cannot use Recycle Bin, show a Yes|No message box and delete or fail'.\n\t\t//\tFOF_WANTNUKEWARNING does not work as it should, eg is ignored when the file is in a flash drive.\n\t\t//\tIt works only without FOF_NOCONFIRMATION, but then always shows confirmation if not disabled in RB Properties.\n\t}\n\t\n\t/// <summary>\n\t/// Creates new directory if does not exists.\n\t/// If need, creates missing parent/ancestor directories.\n\t/// </summary>\n\t/// <returns><c>true</c> if created new directory, <c>false</c> if the directory already exists. Throws exception if failed.</returns>\n\t/// <param name=\"path\">Path of new directory.</param>\n\t/// <param name=\"templateDirectory\">Optional path of a template directory from which to copy some properties. See API <ms>CreateDirectoryEx</ms>.</param>\n\t/// <exception cref=\"ArgumentException\">Not full path.</exception>\n\t/// <exception cref=\"AuException\">Failed.</exception>\n\t/// <remarks>\n\t/// If the directory already exists, this function does nothing, and returns <c>false</c>.\n\t/// Else, at first it creates missing parent/ancestor directories, then creates the specified directory.\n\t/// To create the specified directory, calls API <ms>CreateDirectory</ms> or <ms>CreateDirectoryEx</ms> (if <i>templateDirectory</i> is not <c>null</c>).\n\t/// </remarks>\n\tpublic static bool createDirectory(string path, string templateDirectory = null) {\n\t\treturn _CreateDirectory(path, templateDirectory: templateDirectory);\n\t}\n\t\n\t/// <summary>\n\t/// Creates parent directory for a new file, if does not exist.\n\t/// The same as <see cref=\"createDirectory\"/>, just removes filename from <i>filePath</i>.\n\t/// </summary>\n\t/// <returns><c>true</c> if created new directory, <c>false</c> if the directory already exists. Throws exception if failed.</returns>\n\t/// <param name=\"filePath\">Path of new file.</param>\n\t/// <exception cref=\"ArgumentException\">Not full path. No filename.</exception>\n\t/// <exception cref=\"AuException\">Failed.</exception>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// string path = @\"D:\\Test\\new\\test.txt\";\n\t/// filesystem.createDirectoryFor(path);\n\t/// File.WriteAllText(path, \"text\"); //would fail if directory @\"D:\\Test\\new\" does not exist\n\t/// ]]></code>\n\t/// </example>\n\tpublic static bool createDirectoryFor(string filePath) {\n\t\tfilePath = _PreparePath(filePath);\n\t\tvar path = _RemoveFilename(filePath);\n\t\treturn _CreateDirectory(path, pathIsPrepared: true);\n\t}\n\t\n\t/// <summary>\n\t/// Same as <see cref=\"createDirectoryFor\"/>, but <i>filePath</i> must be prepared (<c>_PreparePath</c> or normalize).\n\t/// </summary>\n\tstatic bool _createDirectoryForPrepared(string filePath) {\n\t\tvar path = _RemoveFilename(filePath);\n\t\treturn _CreateDirectory(path, pathIsPrepared: true);\n\t}\n\t\n\tstatic bool _CreateDirectory(string path, bool pathIsPrepared = false, string templateDirectory = null) {\n\t\tif (exists(path, pathIsPrepared).Directory) return false;\n\t\tif (!pathIsPrepared) path = _PreparePath(path);\n\t\tif (templateDirectory != null) templateDirectory = _PreparePath(templateDirectory);\n\t\t\n\t\tvar stack = new Stack<string>();\n\t\tvar s = path;\n\t\tdo {\n\t\t\tstack.Push(s);\n\t\t\ts = _RemoveFilename(s, true);\n\t\t\tif (s == null) throw new AuException($@\"*create directory '{path}'. Drive not found.\");\n\t\t} while (!exists(s, true).Directory);\n\t\t\n\t\twhile (stack.Count > 0) {\n\t\t\ts = stack.Pop();\n\t\t\tint retry = 0;\n\t\t\tg1:\n\t\t\tbool ok = (templateDirectory == null || stack.Count > 0)\n\t\t\t\t? Api.CreateDirectory(s, default)\n\t\t\t\t: Api.CreateDirectoryEx(templateDirectory, s, default);\n\t\t\tif (!ok) {\n\t\t\t\tint ec = lastError.code;\n\t\t\t\tif (ec == Api.ERROR_ALREADY_EXISTS) continue;\n\t\t\t\tif (ec == Api.ERROR_ACCESS_DENIED && ++retry < 5) { Thread.Sleep(15); goto g1; } //sometimes access denied briefly, eg immediately after deleting the folder while its parent is open in Explorer. Now could not reproduce on Win10.\n\t\t\t\tthrow new AuException(0, $@\"*create directory '{path}'\");\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\t\n\tinternal static void ShellNotify_(uint @event, string path, string path2 = null) {\n\t\t//ThreadPool.QueueUserWorkItem(_ => Api.SHChangeNotify(@event, Api.SHCNF_PATH, path, path2)); //no, this process may end soon\n\t\tApi.SHChangeNotify(@event, Api.SHCNF_PATH, path, path2);\n\t\t//TODO3: test speed. If slow, use threadpool and the process exit event.\n\t}\n\t\n\t/// <summary>\n\t/// The same as <c>pathname.normalize(path)</c>.\n\t/// Expands environment variables, throws <c>ArgumentException</c> if not full path, normalizes, etc.\n\t/// </summary>\n\t/// <exception cref=\"ArgumentException\">Not full path.</exception>\n\tstatic string _PreparePath(string path) {\n\t\tif (!pathname.isFullPathExpand(ref path)) throw new ArgumentException($\"Not full path: '{path}'.\");\n\t\treturn pathname.Normalize_(path, noExpandEV: true);\n\t}\n\t\n\t/// <summary>\n\t/// Finds filename, eg <c>@\"b.txt\"</c> in <c>@\"c:\\a\\b.txt\"</c>.\n\t/// </summary>\n\t/// <exception cref=\"ArgumentException\"><c>'\\\\'</c> not found or is at the end. If <i>noException</i>, instead returns -1.</exception>\n\tstatic int _FindFilename(string path, bool noException = false) {\n\t\tint R = path.FindLastAny(@\"\\/\");\n\t\tif (R < 0 || R == path.Length - 1) {\n\t\t\tif (noException) return -1;\n\t\t\tthrow new ArgumentException($\"No filename in path: '{path}'.\");\n\t\t}\n\t\treturn R + 1;\n\t}\n\t\n\t/// <summary>\n\t/// Removes filename, eg <c>@\"c:\\a\\b.txt\"</c> -> <c>@\"c:\\a\"</c>.\n\t/// </summary>\n\t/// <exception cref=\"ArgumentException\"><c>'\\\\'</c> not found or is at the end. If <i>noException</i>, instead returns <c>null</c>.</exception>\n\tstatic string _RemoveFilename(string path, bool noException = false) {\n\t\tint i = _FindFilename(path, noException); if (i < 0) return null;\n\t\treturn path[..--i];\n\t}\n\t\n\t/// <summary>\n\t/// Gets filename, eg <c>@\"c:\\a\\b.txt\"</c> -> <c>@\"b.txt\"</c>.\n\t/// </summary>\n\t/// <exception cref=\"ArgumentException\"><c>'\\\\'</c> not found or is at the end. If <i>noException</i>, instead returns <c>null</c>.</exception>\n\tstatic string _GetFilename(string path, bool noException = false) {\n\t\tint i = _FindFilename(path, noException); if (i < 0) return null;\n\t\treturn path[i..];\n\t}\n\t#endregion\n\t\n\t#region load, save\n\t\n\t/// <summary>\n\t/// This function can be used to safely open a file that may be temporarily locked (used by another process or thread). Waits while the file is locked.\n\t/// </summary>\n\t/// <returns>The return value of the lambda <i>f</i>.</returns>\n\t/// <param name=\"f\">Lambda that calls a function that creates, opens or opens/reads/closes a file.</param>\n\t/// <param name=\"millisecondsTimeout\">Wait max this number of milliseconds. Can be <see cref=\"Timeout.Infinite\"/> (-1).</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\"><i>millisecondsTimeout</i> less than -1.</exception>\n\t/// <exception cref=\"Exception\">Exceptions thrown by the called function.</exception>\n\t/// <remarks>\n\t/// Calls the lambda and handles <see cref=\"IOException\"/>. If the exception indicates that the file is locked, waits and retries in loop.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// var s1 = File.ReadAllText(file); //unsafe. Exception if the file is locked.\n\t/// \n\t/// var s2 = filesystem.waitIfLocked(() => File.ReadAllText(file)); //safe. Waits while the file is locked.\n\t/// \n\t/// using(var f = filesystem.waitIfLocked(() => File.OpenText(file))) { //safe. Waits while the file is locked.\n\t/// \tvar s3 = f.ReadToEnd();\n\t/// }\n\t/// \n\t/// using(var f = filesystem.waitIfLocked(() => File.Create(file))) { //safe. Waits while the file is locked.\n\t/// \tf.WriteByte(1);\n\t/// }\n\t/// ]]></code>\n\t/// </example>\n\tpublic static T waitIfLocked<T>(Func<T> f, int millisecondsTimeout = 2000) {\n\t\tvar w = new _LockedWaiter(millisecondsTimeout);\n\t\tg1:\n\t\ttry { return f(); }\n\t\tcatch (IOException e) when (w.ExceptionFilter(e)) { w.Sleep(); goto g1; }\n\t}\n\t\n\t/// <returns></returns>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// File.WriteAllText(file, \"TEXT\"); //unsafe. Exception if the file is locked.\n\t/// \n\t/// filesystem.waitIfLocked(() => File.WriteAllText(file, \"TEXT\")); //safe. Waits while the file is locked.\n\t/// ]]></code>\n\t/// </example>\n\t/// <inheritdoc cref=\"waitIfLocked{T}(Func{T}, int)\"/>\n\tpublic static void waitIfLocked(Action f, int millisecondsTimeout = 2000) {\n\t\tvar w = new _LockedWaiter(millisecondsTimeout);\n\t\tg1:\n\t\ttry { f(); }\n\t\tcatch (IOException e) when (w.ExceptionFilter(e)) { w.Sleep(); goto g1; }\n\t}\n\t\n\tstruct _LockedWaiter {\n\t\tint _timeout;\n\t\tlong _t0;\n\t\t\n\t\t[MethodImpl(MethodImplOptions.AggressiveInlining)]\n\t\tpublic _LockedWaiter(int millisecondsTimeout) {\n\t\t\tif (millisecondsTimeout < -1) throw new ArgumentOutOfRangeException();\n\t\t\t_timeout = millisecondsTimeout;\n\t\t\t_t0 = perf.ms;\n\t\t}\n\t\t\n\t\tpublic bool ExceptionFilter(IOException e) => ExceptionFilter(e.HResult & 0xffff);\n\t\t\n\t\t[MethodImpl(MethodImplOptions.AggressiveInlining)]\n\t\tpublic bool ExceptionFilter(int ec) {\n\t\t\t//print.it((uint)ec);\n\t\t\tswitch (ec) {\n\t\t\tcase Api.ERROR_SHARING_VIOLATION:\n\t\t\tcase Api.ERROR_LOCK_VIOLATION:\n\t\t\tcase Api.ERROR_USER_MAPPED_FILE:\n\t\t\tcase Api.ERROR_UNABLE_TO_REMOVE_REPLACED: //ReplaceFile or File.Replace\n\t\t\t\treturn _timeout < 0 || perf.ms - _t0 < _timeout;\n\t\t\tdefault: return false;\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic void Sleep() => Thread.Sleep(15);\n\t}\n\t\n\t/// <summary>\n\t/// Loads text file in a safer way.\n\t/// Uses <see cref=\"File.ReadAllText\"/> and <see cref=\"waitIfLocked{T}(Func{T}, int)\"/>.\n\t/// </summary>\n\t/// <param name=\"file\">File. Must be full path. Can contain environment variables etc, see <see cref=\"pathname.expand\"/>.</param>\n\t/// <param name=\"encoding\">Text encoding in file (if there is no BOM). Default UTF-8.</param>\n\t/// <param name=\"lockedWaitMS\">If cannot open the file because it is opened by another process etc, wait max this number of milliseconds. Can be <see cref=\"Timeout.Infinite\"/> (-1).</param>\n\t/// <param name=\"missingWaitMS\">If the file initially does not exist, wait max this number of milliseconds until exists. Can be <see cref=\"Timeout.Infinite\"/> (-1).</param>\n\t/// <exception cref=\"ArgumentException\">Not full path.</exception>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">A timeout is less than -1.</exception>\n\t/// <exception cref=\"TimeoutException\"><i>missingWaitMS</i> is > 0 and the file still does not exist after waiting.</exception>\n\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"File.ReadAllText\"/>.</exception>\n\tpublic static string loadText(string file, Encoding encoding = null, int lockedWaitMS = 2000, int missingWaitMS = 0) {\n\t\tfile = _LoadIntro(file, missingWaitMS);\n\t\treturn waitIfLocked(() => encoding == null ? File.ReadAllText(file) : File.ReadAllText(file, encoding), lockedWaitMS);\n\t}\n\t\n\tstatic string _LoadIntro(string file, int ms) {\n\t\tfile = pathname.NormalizeMinimally_(file);\n\t\tif (ms != 0) {\n\t\t\tdouble t = ms > 0 ? ms / 1000d : ms == -1 ? 0d : throw new ArgumentOutOfRangeException();\n\t\t\twait.until(t, () => exists(file, useRawPath: true));\n\t\t}\n\t\treturn file;\n\t}\n\t\n\t/// <summary>\n\t/// Loads file in a safer way.\n\t/// Uses <see cref=\"File.ReadAllBytes(string)\"/> and <see cref=\"waitIfLocked{T}(Func{T}, int)\"/>.\n\t/// </summary>\n\t/// <param name=\"file\">File. Must be full path. Can contain environment variables etc, see <see cref=\"pathname.expand\"/>.</param>\n\t/// <param name=\"lockedWaitMS\">If cannot open the file because it is opened by another process etc, wait max this number of milliseconds. Can be <see cref=\"Timeout.Infinite\"/> (-1).</param>\n\t/// <param name=\"missingWaitMS\">If the file initially does not exist, wait max this number of milliseconds until exists. Can be <see cref=\"Timeout.Infinite\"/> (-1).</param>\n\t/// <exception cref=\"ArgumentException\">Not full path.</exception>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">A timeout is less than -1.</exception>\n\t/// <exception cref=\"TimeoutException\"><i>missingWaitMS</i> is > 0 and the file still does not exist after waiting.</exception>\n\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"File.ReadAllBytes(string)\"/>.</exception>\n\tpublic static byte[] loadBytes(string file, int lockedWaitMS = 2000, int missingWaitMS = 0) {\n\t\tfile = _LoadIntro(file, missingWaitMS);\n\t\treturn waitIfLocked(() => File.ReadAllBytes(file), lockedWaitMS);\n\t}\n\t\n\t/// <summary>\n\t/// Loads file in a safer way.\n\t/// Uses <see cref=\"File.OpenRead(string)\"/> and <see cref=\"waitIfLocked{T}(Func{T}, int)\"/>.\n\t/// </summary>\n\t/// <param name=\"file\">File. Must be full path. Can contain environment variables etc, see <see cref=\"pathname.expand\"/>.</param>\n\t/// <param name=\"lockedWaitMS\">If cannot open the file because it is opened by another process etc, wait max this number of milliseconds. Can be <see cref=\"Timeout.Infinite\"/> (-1).</param>\n\t/// <param name=\"missingWaitMS\">If the file initially does not exist, wait max this number of milliseconds until exists. Can be <see cref=\"Timeout.Infinite\"/> (-1).</param>\n\t/// <exception cref=\"ArgumentException\">Not full path.</exception>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">A timeout is less than -1.</exception>\n\t/// <exception cref=\"TimeoutException\"><i>missingWaitMS</i> is > 0 and the file still does not exist after waiting.</exception>\n\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"File.OpenRead(string)\"/>.</exception>\n\tpublic static FileStream loadStream(string file, int lockedWaitMS = 2000, int missingWaitMS = 0) {\n\t\tfile = _LoadIntro(file, missingWaitMS);\n\t\treturn waitIfLocked(() => File.OpenRead(file), lockedWaitMS);\n\t}\n\t\n\t/// <summary>\n\t/// Writes any data to a file in a safe way, using a callback function.\n\t/// </summary>\n\t/// <param name=\"file\">\n\t/// File. Must be full path. Can contain environment variables etc, see <see cref=\"pathname.expand\"/>.\n\t/// If the file exists, this function overwrites it. If the directory does not exist, this function creates it.\n\t/// </param>\n\t/// <param name=\"writer\">\n\t/// Callback function (lambda etc) that creates/writes/closes a temporary file. Its parameter is the full path of the temporary file, which normally does not exist.\n\t/// May be called multiple times, because this function retries if the file is locked or if the directory does not exist (if <i>writer</i> throws <see cref=\"DirectoryNotFoundException\"/> exception).\n\t/// </param>\n\t/// <param name=\"backup\">Create backup file named <i>file</i> + <c>\"~backup\"</c>.</param>\n\t/// <param name=\"tempDirectory\">\n\t/// Directory for backup file and temporary file. If <c>null</c> or <c>\"\"</c> - <i>file</i>'s directory. Can contain environment variables etc.\n\t/// Must be in the same drive as <i>file</i>. If the directory does not exist, this function creates it.</param>\n\t/// <param name=\"lockedWaitMS\">If cannot open the file because it is opened by another process etc, wait max this number of milliseconds. Can be <see cref=\"Timeout.Infinite\"/> (-1).</param>\n\t/// <exception cref=\"ArgumentException\">Invalid <i>file</i> (eg not full path) or <i>lockedWaitMS</i> (less than -1).</exception>\n\t/// <exception cref=\"IOException\">Failed to replace file.</exception>\n\t/// <exception cref=\"Exception\">Exceptions of the function that actually writes data.</exception>\n\t/// <remarks>\n\t/// The file-write functions provided by .NET and Windows API are less reliable, because:\n\t/// 1. Fails if the file is temporarily open by another process or thread without sharing.\n\t/// 2. Can corrupt file data. If this thread, process, PC or disk dies while writing, may write only part of data or just make empty file. Usually it happens when PC is turned off incorrectly.\n\t/// \n\t/// To protect from 1, this functions waits/retries if the file is temporarily open/locked, like <see cref=\"waitIfLocked\"/>.\n\t/// To protect from 2, this function writes to a temporary file and renames/replaces the specified file using API <ms>ReplaceFile</ms>. Although not completely atomic, it ensures that file data is not corrupt; if cannot write all data, does not change existing file data.\n\t/// Also this function auto-creates directory if does not exist.\n\t/// \n\t/// This function is slower. Speed can be important when saving many files.\n\t/// </remarks>\n\tpublic static void save(string file, Action<string> writer, bool backup = false, string tempDirectory = null, int lockedWaitMS = 2000) {\n\t\tNot_.Null(writer);\n\t\t_Save(file, writer, backup, tempDirectory, lockedWaitMS);\n\t}\n\t\n\t/// <summary>\n\t/// Writes text to a file in a safe way (like <see cref=\"save\"/>), using <see cref=\"File.WriteAllText\"/>.\n\t/// </summary>\n\t/// <param name=\"text\">Text to write.</param>\n\t/// <param name=\"encoding\">Text encoding in file. Default is UTF-8 without BOM.</param>\n\t/// <inheritdoc cref=\"save\"/>\n\tpublic static void saveText(string file, string text, bool backup = false, string tempDirectory = null, int lockedWaitMS = 2000, Encoding encoding = null) {\n\t\t_Save(file, text ?? \"\", backup, tempDirectory, lockedWaitMS, encoding);\n\t}\n\t\n\t/// <summary>\n\t/// Writes data to a file in a safe way (like <see cref=\"save\"/>), using <see cref=\"File.WriteAllBytes\"/>.\n\t/// </summary>\n\t/// <param name=\"bytes\">Data to write.</param>\n\t/// <inheritdoc cref=\"save\"/>\n\tpublic static void saveBytes(string file, byte[] bytes, bool backup = false, string tempDirectory = null, int lockedWaitMS = 2000) {\n\t\tNot_.Null(bytes);\n\t\t_Save(file, bytes, backup, tempDirectory, lockedWaitMS);\n\t}\n\t//TODO2: ReadOnlySpan.\n\t\n\tstatic void _Save(string file, object data, bool backup, string tempDirectory, int lockedWaitMS, Encoding encoding = null) {\n\t\tfile = _PreparePath(file);\n\t\t\n\t\tstring s1 = file;\n\t\tif (!tempDirectory.NE()) {\n\t\t\ts1 = _PreparePath(tempDirectory);\n\t\t\t_CreateDirectory(s1, pathIsPrepared: true);\n\t\t\ts1 = pathname.combine(s1, pathname.getName(file));\n\t\t}\n\t\t\n\t\t_createDirectoryForPrepared(file);\n\t\t\n\t\tstring temp = s1 + \"~temp~\";\n\t\tstring back = s1 + \"~backup~\"; //always use the backup parameter, then ERROR_UNABLE_TO_REMOVE_REPLACED is far not so frequent, etc\n\t\t\n\t\tvar w = new _LockedWaiter(lockedWaitMS);\n\t\tg1:\n\t\ttry {\n\t\t\tswitch (data) {\n\t\t\tcase string text:\n\t\t\t\t//File.WriteAllText(temp, text, encoding ?? Encoding.UTF8); //no, it saves with BOM\n\t\t\t\tif (encoding != null) File.WriteAllText(temp, text, encoding);\n\t\t\t\telse File.WriteAllText(temp, text);\n\t\t\t\tbreak;\n\t\t\tcase byte[] bytes:\n\t\t\t\tFile.WriteAllBytes(temp, bytes);\n\t\t\t\tbreak;\n\t\t\tcase Action<string> func:\n\t\t\t\tfunc(temp);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tcatch (IOException e) when (w.ExceptionFilter(e)) { w.Sleep(); goto g1; }\n\t\t\n\t\tw = new _LockedWaiter(lockedWaitMS);\n\t\tg2:\n\t\tstring es = null;\n\t\tif (exists(file, true).File) {\n\t\t\tif (!Api.ReplaceFile(file, temp, back, 6)) es = \"save\"; //random ERROR_UNABLE_TO_REMOVE_REPLACED; _LockedWaiter knows it\n\t\t\telse if (backup) ShellNotify_(Api.SHCNE_RENAMEITEM, temp, file); //without it Explorer shows 2 files with filename of temp\n\t\t\telse if (!Api.DeleteFile(back)) Debug_.PrintNativeError(); //maybe should wait/retry if failed, but never noticed\n\t\t} else {\n\t\t\tif (!Api.MoveFileEx(temp, file, Api.MOVEFILE_REPLACE_EXISTING)) es = \"create\";\n\t\t}\n\t\tif (es != null) {\n\t\t\tint ec = lastError.code;\n\t\t\tif (w.ExceptionFilter(ec)) { w.Sleep(); goto g2; }\n\t\t\tthrow new IOException($\"Failed to {es} file '{file}'. {lastError.messageFor(ec)}\", ec);\n\t\t}\n\t}\n\t\n\t#endregion\n}\n"
  },
  {
    "path": "Au/Files, data/filesystem.more.cs",
    "content": "namespace Au;\n\npartial class filesystem {\n\t/// <summary>\n\t/// Miscellaneous rarely used file/directory functions.\n\t/// </summary>\n\tpublic static partial class more {\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if two paths are of the same existing file, regardless of path format etc.\n\t\t/// </summary>\n\t\t/// <param name=\"path1\">Path of a file or directory. Supports environment variables (see <see cref=\"pathname.expand\"/>) and paths relative to current directory.</param>\n\t\t/// <param name=\"path2\">Path of a file or directory. Supports environment variables (see <see cref=\"pathname.expand\"/>) and paths relative to current directory.</param>\n\t\t/// <param name=\"useSymlink\">If a path is of a symbolic link, use the link. If <c>false</c>, uses its target; for example, returns <c>false</c> if the target doesn't exist.</param>\n\t\t/// <exception cref=\"ArgumentException\">Not full path.</exception>\n\t\t/// <seealso cref=\"comparePaths(string, string, bool, bool)\"/>\n\t\tpublic static bool isSameFile(string path1, string path2, bool useSymlink = false) {\n\t\t\tusing var h1 = _OpenFileHandleForFileInfo(path1, useSymlink); if (h1.Is0) return false;\n\t\t\tusing var h2 = _OpenFileHandleForFileInfo(path2, useSymlink); if (h2.Is0) return false;\n\t\t\treturn Api.GetFileInformationByHandle(h1, out var k1)\n\t\t\t\t&& Api.GetFileInformationByHandle(h2, out var k2)\n\t\t\t\t&& k1.FileIndex == k2.FileIndex && k1.dwVolumeSerialNumber == k2.dwVolumeSerialNumber;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets <see cref=\"FileId\"/> of a file or directory.\n\t\t/// </summary>\n\t\t/// <returns><c>false</c> if failed. Supports <see cref=\"lastError\"/>.</returns>\n\t\t/// <param name=\"path\">Path of a file or directory. Supports environment variables (see <see cref=\"pathname.expand\"/>) and paths relative to current directory.</param>\n\t\t/// <param name=\"fileId\"></param>\n\t\t/// <param name=\"ofSymlink\">If <i>path</i> is of a symbolic link, get <see cref=\"FileId\"/> of the link, not of its target.</param>\n\t\t/// <exception cref=\"ArgumentException\">Not full path.</exception>\n\t\t/// <remarks>\n\t\t/// Calls API <ms>GetFileInformationByHandle</ms>.\n\t\t/// \n\t\t/// A file id can be used to uniquely identify a file or directory regardless of path format.\n\t\t/// \n\t\t/// Note: later the function can get a different id for the same path. For example after deleting the file and then creating new file at the same path (some apps save files in this way). You may want to use <see cref=\"getFinalPath\"/> instead.\n\t\t/// </remarks>\n\t\tpublic static unsafe bool getFileId(string path, out FileId fileId, bool ofSymlink = false) {\n\t\t\tusing var h = _OpenFileHandleForFileInfo(path, ofSymlink);\n\t\t\tif (h.Is0 || !Api.GetFileInformationByHandle(h, out var k)) { fileId = default; return false; }\n\t\t\tfileId = new((int)k.dwVolumeSerialNumber, k.FileIndex);\n\t\t\treturn true;\n\t\t}\n\n\t\tstatic Handle_ _OpenFileHandleForFileInfo(string path, bool ofSymlink = false) {\n\t\t\tpath = pathname.NormalizeMinimally_(path, throwIfNotFullPath: false);\n\t\t\treturn Api.CreateFile(path, 0, Api.FILE_SHARE_ALL, Api.OPEN_EXISTING, ofSymlink ? Api.FILE_FLAG_BACKUP_SEMANTICS | Api.FILE_FLAG_OPEN_REPARSE_POINT : Api.FILE_FLAG_BACKUP_SEMANTICS);\n\t\t\t//info: need FILE_FLAG_BACKUP_SEMANTICS for directories. Ignored for files.\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets full normalized path of an existing file or directory or symbolic link target.\n\t\t/// </summary>\n\t\t/// <returns><c>false</c> if failed. For example if the path does not point to an existing file or directory. Supports <see cref=\"lastError\"/>.</returns>\n\t\t/// <param name=\"path\">Full or relative path. Supports environment variables (see <see cref=\"pathname.expand\"/>).</param>\n\t\t/// <param name=\"result\">Receives full path, or <c>null</c> if failed.</param>\n\t\t/// <param name=\"ofSymlink\">If <i>path</i> is of a symbolic link, get final path of the link, not of its target.</param>\n\t\t/// <param name=\"format\">Result format.</param>\n\t\t/// <exception cref=\"ArgumentException\">Not full path.</exception>\n\t\t/// <remarks>\n\t\t/// Calls API <ms>GetFinalPathNameByHandle</ms>.\n\t\t/// \n\t\t/// Unlike <see cref=\"pathname.normalize\"/>, this function works with existing files and directories, not any strings.\n\t\t/// </remarks>\n\t\t/// <seealso cref=\"shortcutFile.getTarget(string)\"/>\n\t\tpublic static bool getFinalPath(string path, out string result, bool ofSymlink = false, FPFormat format = FPFormat.PrefixIfLong) {\n\t\t\tresult = null;\n\t\t\tusing var h = _OpenFileHandleForFileInfo(path, ofSymlink);\n\t\t\tif (h.Is0 || !Api.GetFinalPathNameByHandle(h, out var s, format == FPFormat.VolumeGuid ? 1u : 0u)) return false;\n\t\t\tif (format == FPFormat.PrefixNever || (format == FPFormat.PrefixIfLong && s.Length <= pathname.maxDirectoryPathLength))\n\t\t\t\ts = pathname.unprefixLongPath(s);\n\t\t\tresult = s;\n\t\t\treturn true;\n\n\t\t\t//never mind: does not change the root if it is like @\"\\\\ThisComputer\\share\" or @\"\\\\ThisComputer\\C$\" or @\"\\\\127.0.0.1\\c$\" or @\"\\\\LOCALHOST\\c$\" and it is the same as \"C:\\\".\n\t\t\t//\tTested: getFileId returns the same value for all these.\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Compares final paths of two existing files or directories to determine equality or relationship.\n\t\t/// </summary>\n\t\t/// <param name=\"pathA\">Full or relative path of an existing file or directory, in any format. Supports environment variables (see <see cref=\"pathname.expand\"/>).</param>\n\t\t/// <param name=\"pathB\">Full or relative path of an existing file or directory, in any format. Supports environment variables (see <see cref=\"pathname.expand\"/>).</param>\n\t\t/// <param name=\"ofSymlinkA\">If <i>pathA</i> is of a symbolic link, get final path of the link, not of its target.</param>\n\t\t/// <param name=\"ofSymlinkB\">If <i>pathB</i> is of a symbolic link, get final path of the link, not of its target.</param>\n\t\t/// <exception cref=\"ArgumentException\">Not full path.</exception>\n\t\t/// <remarks>\n\t\t/// Before comparing, calls <see cref=\"getFinalPath\"/>, therefore paths can have any format.\n\t\t/// Example: <c>@\"C:\\Test\\\"</c> and <c>@\"C:\\A\\..\\Test\"</c> are equal.\n\t\t/// Example: <c>@\"C:\\Test\\file.txt\"</c> and <c>\"file.txt\"</c> are equal if the file is in <c>@\"C:\\Test</c> and <c>@\"C:\\Test</c> is current directory.\n\t\t/// Example: <c>@\"C:\\Temp\\file.txt\"</c> and <c>\"%TEMP%\\file.txt\"</c> are equal if <c>TEMP</c> is an environment variable = <c>@\"C:\\Temp</c>.\n\t\t/// </remarks>\n\t\t/// <seealso cref=\"isSameFile(string, string, bool)\"/>\n\t\tpublic static CPResult comparePaths(string pathA, string pathB, bool ofSymlinkA = false, bool ofSymlinkB = false)\n\t\t\t=> comparePaths(ref pathA, ref pathB, ofSymlinkA, ofSymlinkB);\n\n\t\t/// <summary>\n\t\t/// Compares final paths of two existing files or directories to determine equality or relationship.\n\t\t/// Also gets final paths (see <see cref=\"getFinalPath\"/>).\n\t\t/// </summary>\n\t\t/// <inheritdoc cref=\"comparePaths(string, string, bool, bool)\"/>\n\t\tpublic static CPResult comparePaths(ref string pathA, ref string pathB, bool ofSymlinkA = false, bool ofSymlinkB = false) {\n\t\t\tif (!getFinalPath(pathA, out pathA, ofSymlinkA, FPFormat.PrefixAlways)) return CPResult.Failed;\n\t\t\tif (!getFinalPath(pathB, out pathB, ofSymlinkB, FPFormat.PrefixAlways)) return CPResult.Failed;\n\t\t\t//print.it(pathA, pathB);\n\t\t\tif (pathA.Eqi(pathB)) return CPResult.Same;\n\t\t\tif (pathA.Length < pathB.Length && pathB.Starts(pathA, true) && (pathB[pathA.Length] == '\\\\' || pathA.Ends('\\\\'))) return CPResult.AContainsB;\n\t\t\tif (pathB.Length < pathA.Length && pathA.Starts(pathB, true) && (pathA[pathB.Length] == '\\\\' || pathB.Ends('\\\\'))) return CPResult.BContainsA;\n\t\t\treturn CPResult.None;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Calls <see cref=\"enumerate\"/> and returns the sum of all descendant file sizes.\n\t\t/// With default flags, it includes sizes of all descendant files, in this directory and all subdirectories except in inaccessible [sub]directories.\n\t\t/// </summary>\n\t\t/// <param name=\"path\">Full path.</param>\n\t\t/// <param name=\"flags\"><see cref=\"enumerate\"/> flags.</param>\n\t\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"enumerate\"/>. By default no exceptions if used full path and the directory exists.</exception>\n\t\t/// <remarks>\n\t\t/// This function is slow if the directory is large.\n\t\t/// Don't use this function for files (throws exception) and drives (instead use <see cref=\"DriveInfo\"/>, it's fast and includes sizes of Recycle Bin and other protected hidden system directories).\n\t\t/// </remarks>\n\t\tpublic static long calculateDirectorySize(string path, FEFlags flags = FEFlags.AllDescendants | FEFlags.IgnoreInaccessible) {\n\t\t\treturn enumerate(path, flags).Sum(f => f.Size);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Empties the Recycle Bin.\n\t\t/// </summary>\n\t\t/// <param name=\"drive\">If not <c>null</c>, empties the Recycle Bin on this drive only. Example: <c>\"D:\"</c>.</param>\n\t\t/// <param name=\"progressUI\">Show progress dialog if slow. Default <c>true</c>.</param>\n\t\tpublic static void emptyRecycleBin(string drive = null, bool progressUI = false) {\n\t\t\tApi.SHEmptyRecycleBin(default, drive, progressUI ? 1 : 7);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Creates a NTFS symbolic link or junction.\n\t\t/// </summary>\n\t\t/// <param name=\"linkPath\">Full path of the link. Supports environment variables etc.</param>\n\t\t/// <param name=\"targetPath\">If <i>type</i> is <c>Junction</c>, must be full path. Else can be either full path or path relative to the parent directory of the link. If starts with an environment variable, the function expands it before creating the link.</param>\n\t\t/// <param name=\"type\"></param>\n\t\t/// <param name=\"elevate\">If fails to create symbolic link because this process does not have admin rights, run <c>cmd.exe mklink</c> as administrator. Will show a dialog and UAC consent. Not used if type is <c>Junction</c>, because don't need admin rights to create junctions.</param>\n\t\t/// <param name=\"deleteOld\">If <i>linkPath</i> already exists as a symbolic link or junction or empty directory, replace it.</param>\n\t\t/// <remarks>\n\t\t/// Some reasons why this function can fail:\n\t\t/// - The link already exists. Solution: use <c>deleteOld: true</c>.\n\t\t/// - This process is running not as administrator. Solution: use <i>type</i> <c>Junction</c> or <c>elevate: true</c>. To create symbolic links without admin rights, in Windows Settings enable developer mode.\n\t\t/// - The file system format is not NTFS. For example FAT32 in USB drive.\n\t\t/// \n\t\t/// More info: <google>CreateSymbolicLink, mklink, NTFS symbolic links, junctions</google>.\n\t\t/// </remarks>\n\t\t/// <exception cref=\"ArgumentException\">Not full path.</exception>\n\t\t/// <exception cref=\"AuException\">Failed.</exception>\n\t\tpublic static void createSymbolicLink(string linkPath, string targetPath, CSLink type, bool elevate = false, bool deleteOld = false) {\n\t\t\tlinkPath = pathname.normalize(linkPath);\n\t\t\tif (type is CSLink.Junction) {\n\t\t\t\ttargetPath = pathname.normalize(targetPath); //junctions don't support relative path\n\t\t\t} else { //symlinks support relative path\n\t\t\t\ttargetPath = targetPath.Replace('/', '\\\\'); //rumors: the link may not work if with /\n\t\t\t}\n\n\t\t\tstring trueLinkPath = null;\n\t\t\ttry {\n\t\t\t\tif (exists(linkPath, useRawPath: true) is var e && e) {\n\t\t\t\t\tbool ok = deleteOld && (e.IsNtfsLink || (e.Directory && Api.RemoveDirectory(linkPath))); //info: remove empty dir because eg Explorer does not copy links, but istead creates empty dir\n\t\t\t\t\tif (!ok) throw new AuException(Api.ERROR_ALREADY_EXISTS, \"*to create symbolic link.\");\n\t\t\t\t\ttrueLinkPath = linkPath;\n\t\t\t\t\tlinkPath = pathname.makeUnique(linkPath, true);\n\t\t\t\t} else createDirectoryFor(linkPath);\n\n\t\t\t\tif (type is CSLink.Junction or CSLink.JunctionOrSymlink) {\n\t\t\t\t\tvar r = run.console(out string s, \"cmd.exe\", $\"\"\"/u /c \"mklink /d /j \"{linkPath}\" \"{targetPath}\" \"\"\", encoding: Encoding.Unicode); //tested: UTF-16 on Win11 and Win7\n\t\t\t\t\tif (r == 0) return;\n\t\t\t\t\tif (!(type == CSLink.JunctionOrSymlink && s.Starts(\"Local volumes are required\"))) throw new AuException(\"*to create junction. \" + s.Trim());\n\t\t\t\t}\n\n\t\t\t\tuint fl = type == CSLink.File ? 0u : 1u; //SYMBOLIC_LINK_FLAG_DIRECTORY\n\t\t\t\tif (osVersion.minWin10_1703) fl |= 2u; //SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE\n\t\t\t\tif (Api.CreateSymbolicLink(linkPath, targetPath, fl)) return;\n\n\t\t\t\tint ec = lastError.code;\n\t\t\t\tif (ec == Api.ERROR_PRIVILEGE_NOT_HELD && elevate && !uacInfo.isAdmin) {\n\t\t\t\t\tif (dialog.showOkCancel(\"Create symbolic link\", \"Administrator rights required.\\n\\nTo create without admin rights, in Windows Settings enable developer mode.\", icon: DIcon.Shield)) {\n\t\t\t\t\t\tusing var tf = new TempFile();\n\t\t\t\t\t\tvar d = type == CSLink.File ? null : \"/d \";\n\t\t\t\t\t\tvar cl = $\"\"\"/u /c \"mklink {d}\"{linkPath}\" \"{targetPath}\" 2>\"{tf}\" \"\"\"; //redirects stderr to temp file\n\t\t\t\t\t\tvar r = run.it(folders.System + \"cmd.exe\", cl, RFlags.Admin | RFlags.WaitForExit, new() { WindowState = ProcessWindowStyle.Hidden });\n\t\t\t\t\t\tif (r.ProcessExitCode == 0) return;\n\t\t\t\t\t\tstring s = null; try { s = loadText(tf, encoding: Encoding.Unicode).Trim(); } catch { }\n\t\t\t\t\t\tthrow new AuException(\"*to create symbolic link. \" + s);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tthrow new AuException(ec, \"*to create symbolic link.\");\n\t\t\t}\n\t\t\tfinally { if (trueLinkPath != null && filesystem.exists(linkPath)) move(linkPath, trueLinkPath, FIfExists.Delete); }\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Detects whether the path is on a SSD drive.\n\t\t/// </summary>\n\t\t/// <param name=\"path\">Any path. The function uses just the drive part. Can be just <c>\"C:\"</c>. Fails if a network path.</param>\n\t\t/// <returns><c>true</c> if succeeded and the path is on a SSD drive.</returns>\n\t\t/// <remarks>\n\t\t/// Unreliable. For SSD drives usually returns true. For other drives usually fails and returns false.\n\t\t/// </remarks>\n\t\tinternal unsafe static bool IsOnSSD_(string path) {\n\t\t\ttry {\n\t\t\t\tif (path.Starts(@\"\\\\?\\\")) path = path[4..];\n\t\t\t\tvar di = new DriveInfo(path);\n\t\t\t\tif (!(di.DriveType is DriveType.Fixed or DriveType.Removable) || !di.IsReady) return false;\n\n\t\t\t\tvar b = stackalloc char[300];\n\t\t\t\tif (!Api.GetVolumeNameForVolumeMountPoint(di.Name, b, 300)) return false;\n\t\t\t\tpath = new(Ptr_.ToRSpan(b).TrimEnd('\\\\'));\n\t\t\t}\n\t\t\tcatch { return false; }\n\n\t\t\tusing var h = Api.CreateFile(path, 0, Api.FILE_SHARE_READ | Api.FILE_SHARE_WRITE, Api.OPEN_EXISTING, Api.FILE_FLAG_BACKUP_SEMANTICS);\n\t\t\tif (h.Is0) return false;\n\t\t\tvar query = new Api.STORAGE_PROPERTY_QUERY { PropertyId = 7 };\n\t\t\tApi.DEVICE_SEEK_PENALTY_DESCRIPTOR spt = default;\n\t\t\tbool r = Api.DeviceIoControl(h, Api.IOCTL_STORAGE_QUERY_PROPERTY, &query, sizeof(Api.STORAGE_PROPERTY_QUERY), &spt, sizeof(Api.DEVICE_SEEK_PENALTY_DESCRIPTOR), out _);\n\t\t\treturn r && spt.IncursSeekPenalty == 0;\n\t\t}\n\n\t\t#region garbage\n\n#if false //currently not used\n\t\t/// <summary>\n\t\t/// Gets HKEY_CLASSES_ROOT registry key of file type or protocol.\n\t\t/// The key usually contains subkeys <c>shell</c>, <c>DefaultIcon</c>, sometimes <c>shellex</c> and more.\n\t\t/// For example, for <c>\".txt\"</c> can return <c>\"txtfile\"</c>, for <c>\".cs\"</c> - <c>\"VisualStudio.cs.14.0\"</c>.\n\t\t/// Looks in <c>HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts</c> and in <c>HKEY_CLASSES_ROOT</c>.\n\t\t/// Returns <c>null</c> if the type/protocol is not registered.\n\t\t/// Returns <c>null</c> if <i>fileType</i> does not end with <c>\".extension\"</c> and does not start with <c>\"protocol:\"</c>; also if starts with <c>\"shell:\"</c>.\n\t\t/// </summary>\n\t\t/// <param name=\"fileType\">\n\t\t/// File type extension like <c>\".txt\"</c> or protocol like <c>\"http:\"</c>.\n\t\t/// Can be full path or URL; the function gets extension or protocol from the string.\n\t\t/// Can start with <c>%environment variable%</c>.\n\t\t/// </param>\n\t\t/// <param name=\"isFileType\">Don't parse <i>fileType</i>, it does not contain full path or URL or environment variables. It is <c>\".ext\"</c> or <c>\"protocol:\"</c>.</param>\n\t\t/// <param name=\"isURL\"><i>fileType</i> is URL or protocol like <c>\"http:\"</c>. Used only if <c>isFileType == true</c>, ie it is protocol.</param>\n\t\tinternal static string getFileTypeOrProtocolRegistryKey(string fileType, bool isFileType, bool isURL)\n\t\t{\n\t\t\tif(!isFileType) fileType = GetExtensionOrProtocol(fileType, out isURL);\n\t\t\telse if(isURL) fileType = fileType.RemoveSuffix(1); //\"proto:\" -> \"proto\"\n\t\t\tif(fileType.NE()) return null;\n\n\t\t\tstring userChoiceKey = @\"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\\" + fileType + @\"\\UserChoice\";\n\t\t\tif(Registry.GetValue(userChoiceKey, \"ProgId\", null) is string s1) return s1;\n\t\t\tif(isURL) return fileType;\n\t\t\tif(Registry.ClassesRoot.GetValue(fileType, null) is string s2) return s2;\n\t\t\treturn null;\n\n\t\t\t//note: IQueryAssociations.GetKey is very slow.\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets file path extension like <c>\".txt\"</c> or URL protocol like <c>\"http\"</c>.\n\t\t/// Returns <c>null</c> if path does not end with <c>\".extension\"</c> and does not start with <c>\"protocol:\"</c>; also if starts with <c>\"shell:\"</c>.\n\t\t/// </summary>\n\t\t/// <param name=\"path\">File path or URL. Can be just extension like <c>\".txt\"</c> or protocol like <c>\"http:\"</c>.</param>\n\t\t/// <param name=\"isProtocol\">Receives <c>true</c> if URL or protocol.</param>\n\t\tinternal static string GetExtensionOrProtocol(string path, out bool isProtocol)\n\t\t{\n\t\t\tisProtocol = false;\n\t\t\tif(path.NE()) return null;\n\t\t\tif(!PathIsExtension(path)) {\n\t\t\t\tint i = path.IndexOf(':');\n\t\t\t\tif(i > 1) {\n\t\t\t\t\tpath = path[..i]; //protocol\n\t\t\t\t\tif(path == \"shell\") return null; //eg \"shell:AppsFolder\\Microsoft.WindowsCalculator_8wekyb3d8bbwe!App\"\n\t\t\t\t\tisProtocol = true;\n\t\t\t\t} else {\n\t\t\t\t\tpath = pathname.getExtension(path);\n\t\t\t\t\tif(path.NE()) return null;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn path;\n\t\t}\n#endif\n\n#if false\n\t//this is ~300 times slower than filesystem.move. SHFileOperation too. Use only for files or other shell items in virtual folders. Unfinished.\n\tpublic static void renameFileOrDirectory(string path, string newName)\n\t{\n\t\tperf.first();\n\t\tif(pathname.isInvalidName(newName)) throw new ArgumentException(\"Invalid filename.\", nameof(newName));\n\t\tpath = _PreparePath(path, nameof(path));\n\n\t\tperf.next();\n\t\tvar si = _ShellItem(path, \"*rename\");\n\t\tperf.next();\n\t\tvar fo = new api.FileOperation() as api.IFileOperation;\n\t\tperf.next();\n\t\ttry {\n\t\t\tfo.SetOperationFlags(4); //FOF_SILENT. Without it shows a hidden dialog that becomes the active window.\n\t\t\tAuException.ThrowIfFailed(fo.RenameItem(si, newName, null), \"*rename\");\n\t\t\tperf.next();\n\t\t\tAuException.ThrowIfFailed(fo.PerformOperations(), \"*rename\");\n\t\t\tperf.next();\n\t\t}\n\t\tfinally {\n\t\t\tApi.ReleaseComObject(fo);\n\t\t\tApi.ReleaseComObject(si);\n\t\t}\n\t\tperf.nw();\n\t}\n\n\tstatic api.IShellItem _ShellItem(string path, string errMsg)\n\t{\n\t\tvar pidl = More.PidlFromString(path, true);\n\t\ttry {\n\t\t\tvar guid = typeof(api.IShellItem).GUID;\n\t\t\tAuException.ThrowIfFailed(api.SHCreateItemFromIDList(pidl, guid, out var R), errMsg);\n\t\t\treturn R;\n\t\t}\n\t\tfinally { Marshal.FreeCoTaskMem(pidl); }\n\t}\n\n\tstatic class api\n\t{\n\t\t[DllImport(\"shell32.dll\", PreserveSig = true)]\n\t\tinternal static extern int SHCreateItemFromIDList(IntPtr pidl, in Guid riid, out IShellItem ppv);\n\n\t\t[ComImport, Guid(\"3ad05575-8857-4850-9277-11b85bdb8e09\"), ClassInterface(ClassInterfaceType.None)]\n\t\tinternal class FileOperation { }\n\n\t\t[ComImport, Guid(\"947aab5f-0a5c-4c13-b4d6-4bf7836fc9f8\"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\n\t\tinternal interface IFileOperation\n\t\t{\n\t\t\t[PreserveSig] int Advise(IFileOperationProgressSink pfops, out uint pdwCookie);\n\t\t\t[PreserveSig] int Unadvise(uint dwCookie);\n\t\t\t[PreserveSig] int SetOperationFlags(uint dwOperationFlags);\n\t\t\t[PreserveSig] int SetProgressMessage([MarshalAs(UnmanagedType.LPWStr)] string pszMessage);\n\t\t\t[PreserveSig] int SetProgressDialog(IOperationsProgressDialog popd);\n\t\t\t[PreserveSig] int SetProperties(IntPtr pproparray); //IPropertyChangeArray\n\t\t\t[PreserveSig] int SetOwnerWindow(wnd hwndOwner);\n\t\t\t[PreserveSig] int ApplyPropertiesToItem(IShellItem psiItem);\n\t\t\t[PreserveSig] int ApplyPropertiesToItems([MarshalAs(UnmanagedType.IUnknown)] Object punkItems);\n\t\t\t[PreserveSig] int RenameItem(IShellItem psiItem, [MarshalAs(UnmanagedType.LPWStr)] string pszNewName, IFileOperationProgressSink pfopsItem);\n\t\t\t[PreserveSig] int RenameItems([MarshalAs(UnmanagedType.IUnknown)] Object pUnkItems, [MarshalAs(UnmanagedType.LPWStr)] string pszNewName);\n\t\t\t[PreserveSig] int MoveItem(IShellItem psiItem, IShellItem psiDestinationFolder, [MarshalAs(UnmanagedType.LPWStr)] string pszNewName, IFileOperationProgressSink pfopsItem);\n\t\t\t[PreserveSig] int MoveItems([MarshalAs(UnmanagedType.IUnknown)] Object punkItems, IShellItem psiDestinationFolder);\n\t\t\t[PreserveSig] int CopyItem(IShellItem psiItem, IShellItem psiDestinationFolder, [MarshalAs(UnmanagedType.LPWStr)] string pszCopyName, IFileOperationProgressSink pfopsItem);\n\t\t\t[PreserveSig] int CopyItems([MarshalAs(UnmanagedType.IUnknown)] Object punkItems, IShellItem psiDestinationFolder);\n\t\t\t[PreserveSig] int DeleteItem(IShellItem psiItem, IFileOperationProgressSink pfopsItem);\n\t\t\t[PreserveSig] int DeleteItems([MarshalAs(UnmanagedType.IUnknown)] Object punkItems);\n\t\t\t[PreserveSig] int NewItem(IShellItem psiDestinationFolder, uint dwFileAttributes, [MarshalAs(UnmanagedType.LPWStr)] string pszName, [MarshalAs(UnmanagedType.LPWStr)] string pszTemplateName, IFileOperationProgressSink pfopsItem);\n\t\t\t[PreserveSig] int PerformOperations();\n\t\t\t[PreserveSig] int GetAnyOperationsAborted([MarshalAs(UnmanagedType.Bool)] out bool pfAnyOperationsAborted);\n\t\t}\n\n\t\t[ComImport, Guid(\"04b0f1a7-9490-44bc-96e1-4296a31252e2\"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\n\t\tinternal interface IFileOperationProgressSink\n\t\t{\n\t\t\t[PreserveSig] int StartOperations();\n\t\t\t[PreserveSig] int FinishOperations(int hrResult);\n\t\t\t[PreserveSig] int PreRenameItem(uint dwFlags, IShellItem psiItem, [MarshalAs(UnmanagedType.LPWStr)] string pszNewName);\n\t\t\t[PreserveSig] int PostRenameItem(uint dwFlags, IShellItem psiItem, [MarshalAs(UnmanagedType.LPWStr)] string pszNewName, int hrRename, IShellItem psiNewlyCreated);\n\t\t\t[PreserveSig] int PreMoveItem(uint dwFlags, IShellItem psiItem, IShellItem psiDestinationFolder, [MarshalAs(UnmanagedType.LPWStr)] string pszNewName);\n\t\t\t[PreserveSig] int PostMoveItem(uint dwFlags, IShellItem psiItem, IShellItem psiDestinationFolder, [MarshalAs(UnmanagedType.LPWStr)] string pszNewName, int hrMove, IShellItem psiNewlyCreated);\n\t\t\t[PreserveSig] int PreCopyItem(uint dwFlags, IShellItem psiItem, IShellItem psiDestinationFolder, [MarshalAs(UnmanagedType.LPWStr)] string pszNewName);\n\t\t\t[PreserveSig] int PostCopyItem(uint dwFlags, IShellItem psiItem, IShellItem psiDestinationFolder, [MarshalAs(UnmanagedType.LPWStr)] string pszNewName, int hrCopy, IShellItem psiNewlyCreated);\n\t\t\t[PreserveSig] int PreDeleteItem(uint dwFlags, IShellItem psiItem);\n\t\t\t[PreserveSig] int PostDeleteItem(uint dwFlags, IShellItem psiItem, int hrDelete, IShellItem psiNewlyCreated);\n\t\t\t[PreserveSig] int PreNewItem(uint dwFlags, IShellItem psiDestinationFolder, [MarshalAs(UnmanagedType.LPWStr)] string pszNewName);\n\t\t\t[PreserveSig] int PostNewItem(uint dwFlags, IShellItem psiDestinationFolder, [MarshalAs(UnmanagedType.LPWStr)] string pszNewName, [MarshalAs(UnmanagedType.LPWStr)] string pszTemplateName, uint dwFileAttributes, int hrNew, IShellItem psiNewItem);\n\t\t\t[PreserveSig] int UpdateProgress(uint iWorkTotal, uint iWorkSoFar);\n\t\t\t[PreserveSig] int ResetTimer();\n\t\t\t[PreserveSig] int PauseTimer();\n\t\t\t[PreserveSig] int ResumeTimer();\n\t\t}\n\n\t\t[ComImport, Guid(\"0C9FB851-E5C9-43EB-A370-F0677B13874C\"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\n\t\tinternal interface IOperationsProgressDialog\n\t\t{\n\t\t\t[PreserveSig] int StartProgressDialog(wnd hwndOwner, uint flags);\n\t\t\t[PreserveSig] int StopProgressDialog();\n\t\t\t[PreserveSig] int SetOperation(SPACTION action);\n\t\t\t[PreserveSig] int SetMode(uint mode);\n\t\t\t[PreserveSig] int UpdateProgress(ulong ullPointsCurrent, ulong ullPointsTotal, ulong ullSizeCurrent, ulong ullSizeTotal, ulong ullItemsCurrent, ulong ullItemsTotal);\n\t\t\t[PreserveSig] int UpdateLocations(IShellItem psiSource, IShellItem psiTarget, IShellItem psiItem);\n\t\t\t[PreserveSig] int ResetTimer();\n\t\t\t[PreserveSig] int PauseTimer();\n\t\t\t[PreserveSig] int ResumeTimer();\n\t\t\t[PreserveSig] int GetMilliseconds(out ulong pullElapsed, out ulong pullRemaining);\n\t\t\t[PreserveSig] int GetOperationStatus(out PDOPSTATUS popstatus);\n\t\t}\n\n\t\tinternal enum SPACTION\n\t\t{\n\t\t\tSPACTION_NONE,\n\t\t\tSPACTION_MOVING,\n\t\t\tSPACTION_COPYING,\n\t\t\tSPACTION_RECYCLING,\n\t\t\tSPACTION_APPLYINGATTRIBS,\n\t\t\tSPACTION_DOWNLOADING,\n\t\t\tSPACTION_SEARCHING_INTERNET,\n\t\t\tSPACTION_CALCULATING,\n\t\t\tSPACTION_UPLOADING,\n\t\t\tSPACTION_SEARCHING_FILES,\n\t\t\tSPACTION_DELETING,\n\t\t\tSPACTION_RENAMING,\n\t\t\tSPACTION_FORMATTING,\n\t\t\tSPACTION_COPY_MOVING\n\t\t}\n\n\t\tinternal enum PDOPSTATUS\n\t\t{\n\t\t\tPDOPS_RUNNING = 1,\n\t\t\tPDOPS_PAUSED,\n\t\t\tPDOPS_CANCELLED,\n\t\t\tPDOPS_STOPPED,\n\t\t\tPDOPS_ERRORS\n\t\t}\n\n\n\t}\n#endif\n\n\t\t//rejected: unreliable. Uses registry, where many mimes are incorrect and nonconstant.\n\t\t//\tUse System.Web.MimeMapping.GetMimeMapping. It uses a hardcoded list, although too small.\n\t\t///// <summary>\n\t\t///// Gets file's MIME content type, like <c>\"text/html\"</c> or <c>\"image/png\"</c>.\n\t\t///// Returns <c>false</c> if cannot detect it.\n\t\t///// </summary>\n\t\t///// <param name=\"file\">File name or path or URL or just extension like <c>\".txt\"</c>. If <i>canAnalyseData</i> is <c>true</c>, must be full path of a file, and the file must exist and can be opened to read; else the function uses just <c>.extension</c>, and the file may exist or not.</param>\n\t\t///// <param name=\"contentType\">Result.</param>\n\t\t///// <param name=\"canAnalyseData\">If cannot detect from file extension, try to detect from file data.</param>\n\t\t///// <exception cref=\"ArgumentException\">Not full path. Only if <i>canAnalyseData</i> is <c>true</c>.</exception>\n\t\t///// <exception cref=\"Exception\">Exceptions of <see cref=\"File.ReadAllBytes\"/>. Only if <i>canAnalyseData</i> is <c>true</c>.</exception>\n\t\t///// <remarks>\n\t\t///// Uses API <ms>FindMimeFromData</ms>.\n\t\t///// </remarks>\n\t\t//public static bool getMimeContentType(string file, out string contentType, bool canAnalyseData = false)\n\t\t//{\n\t\t//\tif(file.Ends(\".cur\", true)) { contentType = \"image/x-icon\"; return true; } //registered without MIME or with text/plain\n\n\t\t//\tint hr = Api.FindMimeFromData(default, file, null, 0, null, 0, out contentType, 0);\n\t\t//\tif(hr != 0 && canAnalyseData) {\n\t\t//\t\tfile = pathname.normalize(file);\n\t\t//\t\tusing(var stream = File.OpenRead(file)) {\n\t\t//\t\t\tvar data = new byte[256];\n\t\t//\t\t\tint n = stream.Read(data, 0, 256);\n\t\t//\t\t\thr = Api.FindMimeFromData(default, null, data, n, null, 0, out contentType, 0);\n\t\t//\t\t}\n\t\t//\t}\n\t\t//\treturn hr == 0;\n\t\t//\t//note: the returned unmanaged string is freed with CoTaskMemFree, which uses HeapFree(GetProcessHeap).\n\t\t//\t//\tIn MSDN it is documented incorrectly: \"should be freed with the operator delete function\".\n\t\t//\t//\tTo discover it, call HeapSize(GetProcessHeap) before and after CoTaskMemFree. It returns -1 when called after.\n\t\t//}\n\n\t\t#endregion\n\t}\n}\n"
  },
  {
    "path": "Au/Files, data/folders.cs",
    "content": "#pragma warning disable 1591 //missing XML documentation\n\nnamespace Au {\n\t/// <summary>\n\t/// Gets known/special folder paths (Desktop, Temp, etc).\n\t/// </summary>\n\t/// <remarks>\n\t/// Most functions return <see cref=\"FolderPath\"/>, not <c>string</c>. It is implicitly convertible to <c>string</c>. Its operator <c>+</c> appends a filename or relative path string, with <c>\\</c> separator if need. Example:\n\t/// \n\t/// <code><![CDATA[string s = folders.Desktop + \"file.txt\"; //C:\\Users\\Name\\Desktop\\file.txt]]></code>\n\t/// \n\t/// If a function cannot get folder path, the return value contains <c>null</c> string. Then the <c>+</c> operator would throw <see cref=\"ArgumentException\"/>.\n\t///\n\t/// Some folders are known only on newer Windows versions or only on some computers. Some functions have a suffix like <c>_Win8</c> which means that the folder is unavailable on older Windows.\n\t/// Some known folders, although supported and registered, may be still not created.\n\t/// \n\t/// Some folders are virtual, for example Control Panel. They don't have a file system path, but can be identified by a data structure <ms>ITEMIDLIST</ms>. Functions of the nested class <see cref=\"shell\"/> return it as <see cref=\"Pidl\"/> or string <c>\":: ITEMIDLIST\"</c> that can be used with some functions of this library (<see cref=\"run.it\"/>, <see cref=\"icon.of\"/>, <see cref=\"icon.ofPidl(Pidl, int)\"/>) but not with .NET functions.\n\t///\n\t/// Most functions use Windows \"Known Folders\" API, such as <ms>SHGetKnownFolderPath</ms>.\n\t/// The list of Windows predefined known folders: <ms>KNOWNFOLDERID</ms>.\n\t/// Names of folders specific to current process have prefix <c>This</c>, like <c>ThisApp</c>.\n\t/// \n\t/// Some paths depend on the bitness (32 or 64 bit) of the OS and this process.\n\t/// <table>\n\t/// <tr>\n\t/// <td>32-bit Windows</td>\n\t/// <td>\n\t/// <c>System</c>, <c>SystemX86</c>, <c>SystemX64</c>: <c>@\"C:\\WINDOWS\\system32\"</c>\n\t/// <br/><c>ProgramFiles</c>, <c>ProgramFilesX86</c>, <c>ProgramFilesX64</c>: <c>@\"C:\\Program Files\"</c>\n\t/// <br/><c>ProgramFilesCommon</c>, <c>ProgramFilesCommonX86</c>, <c>ProgramFilesCommonX64</c>: <c>@\"C:\\Program Files\\Common Files\"</c>\n\t/// </td>\n\t/// </tr>\n\t/// <tr>\n\t/// <td>64-bit Windows, 64-bit process</td>\n\t/// <td>\n\t/// <c>System</c>, <c>SystemX64</c>: <c>@\"C:\\WINDOWS\\system32\"</c>\n\t/// <br/><c>SystemX86</c>: <c>@\"C:\\WINDOWS\\SysWOW64\"</c>\n\t/// <br/><c>ProgramFiles</c>, <c>ProgramFilesX64</c>: <c>@\"C:\\Program Files\"</c>\n\t/// <br/><c>ProgramFilesX86</c>: <c>@\"C:\\Program Files (x86)\"</c>\n\t/// <br/><c>ProgramFilesCommon</c>, <c>ProgramFilesCommonX64</c>: <c>@\"C:\\Program Files\\Common Files\"</c>\n\t/// <br/><c>ProgramFilesCommonX86</c>: <c>@\"C:\\Program Files (x86)\\Common Files\"</c>\n\t/// </td>\n\t/// </tr>\n\t/// <tr>\n\t/// <td>64-bit Windows, 32-bit process</td>\n\t/// <td>\n\t/// <c>System</c>: <c>@\"C:\\WINDOWS\\system32\"</c>. However the OS in most cases redirects this path to <c>@\"C:\\WINDOWS\\SysWOW64\"</c>.\n\t/// <br/><c>SystemX86</c>: <c>@\"C:\\WINDOWS\\SysWOW64\"</c>\n\t/// <br/><c>SystemX64</c>: <c>@\"C:\\WINDOWS\\Sysnative\"</c>. The OS redirects it to the true <c>@\"C:\\WINDOWS\\system32\"</c>. It is a special path, not in Explorer.\n\t/// <br/><c>ProgramFiles</c>, <c>ProgramFilesX86</c>: <c>@\"C:\\Program Files (x86)\"</c>\n\t/// <br/><c>ProgramFilesX64</c>: <c>@\"C:\\Program Files\"</c>\n\t/// <br/><c>ProgramFilesCommon</c>, <c>ProgramFilesCommonX86</c>: <c>@\"C:\\Program Files (x86)\\Common Files\"</c>\n\t/// <br/><c>ProgramFilesCommonX64</c>: <c>@\"C:\\Program Files\\Common Files\"</c>\n\t/// </td>\n\t/// </tr>\n\t/// </table>\n\t/// </remarks>\n\tpublic static class folders {\n\t\t#region generated by macro \"Auto create special folders class from KNOWNFOLDERID\"\n\t\t\n\t\tpublic static FolderPath AccountPictures_Win8 => _Get(0x008ca0b1, 0x55b44c56, 0xb8a84de4, 0xb299d3be);\n\t\tpublic static FolderPath AdminTools => _Get(0x724EF170, 0xA42D4FEF, 0x9F26B60E, 0x846FBA4F);\n\t\tpublic static FolderPath ApplicationShortcuts_Win8 => _Get(0xA3918781, 0xE5F24890, 0xB3D9A7E5, 0x4332328C);\n\t\tpublic static FolderPath CameraRoll_Win81 => _Get(0xAB5FB87B, 0x7CE24F83, 0x915D5508, 0x46C9537B);\n\t\tpublic static FolderPath CDBurning => _Get(0x9E52AB10, 0xF80D49DF, 0xACB84330, 0xF5687855);\n\t\tpublic static FolderPath CommonAdminTools => _Get(0xD0384E7D, 0xBAC34797, 0x8F14CBA2, 0x29B392B5);\n\t\tpublic static FolderPath CommonOEMLinks => _Get(0xC1BAE2D0, 0x10DF4334, 0xBEDD7AA2, 0x0B227A9D);\n\t\tpublic static FolderPath CommonPrograms => _Get(0x0139D44E, 0x6AFE49F2, 0x86903DAF, 0xCAE6FFB8);\n\t\tpublic static FolderPath CommonStartMenu => _Get(0xA4115719, 0xD62E491D, 0xAA7CE74B, 0x8BE3B067);\n\t\tpublic static FolderPath CommonStartup => _Get(0x82A5EA35, 0xD9CD47C5, 0x9629E15D, 0x2F714E6E);\n\t\tpublic static FolderPath CommonTemplates => _Get(0xB94237E7, 0x57AC4347, 0x9151B08C, 0x6C32D1F7);\n\t\tpublic static FolderPath Contacts => _Get(0x56784854, 0xC6CB462b, 0x816988E3, 0x50ACB882);\n\t\tpublic static FolderPath Cookies => _Get(0x2B0F765D, 0xC0E94171, 0x908E08A6, 0x11B84FF6);\n\t\tpublic static FolderPath Desktop => _Get(0xB4BFCC3A, 0xDB2C424C, 0xB0297FE9, 0x9A87C641);\n\t\tpublic static FolderPath DeviceMetadataStore => _Get(0x5CE4A5E9, 0xE4EB479D, 0xB89F130C, 0x02886155);\n\t\tpublic static FolderPath Documents => _Get(0xFDD39AD0, 0x238F46AF, 0xADB46C85, 0x480369C7);\n\t\tpublic static FolderPath DocumentsLibrary => _Get(0x7B0DB17D, 0x9CD24A93, 0x973346CC, 0x89022E7C);\n\t\tpublic static FolderPath Downloads => _Get(0x374DE290, 0x123F4565, 0x916439C4, 0x925E467B);\n\t\tpublic static FolderPath Favorites => _Get(0x1777F761, 0x68AD4D8A, 0x87BD30B7, 0x59FA33DD);\n\t\tpublic static FolderPath Fonts => _Get(0xFD228CB7, 0xAE114AE3, 0x864C16F3, 0x910AB8FE);\n\t\tpublic static FolderPath GameTasks => _Get(0x054FAE61, 0x4DD84787, 0x80B60902, 0x20C4B700);\n\t\tpublic static FolderPath History => _Get(0xD9DC8A3B, 0xB784432E, 0xA7815A11, 0x30A75963);\n\t\tpublic static FolderPath ImplicitAppShortcuts => _Get(0xBCB5256F, 0x79F64CEE, 0xB725DC34, 0xE402FD46);\n\t\tpublic static FolderPath InternetCache => _Get(0x352481E8, 0x33BE4251, 0xBA856007, 0xCAEDCF9D);\n\t\tpublic static FolderPath Libraries => _Get(0x1B3EA5DC, 0xB5874786, 0xB4EFBD1D, 0xC332AEAE);\n\t\tpublic static FolderPath Links => _Get(0xbfb9d5e0, 0xc6a9404c, 0xb2b2ae6d, 0xb6af4968);\n\t\tpublic static FolderPath LocalAppData => _Get(0xF1B32785, 0x6FBA4FCF, 0x9D557B8E, 0x7F157091);\n\t\tpublic static FolderPath LocalAppDataLow => _Get(0xA520A1A4, 0x17804FF6, 0xBD181673, 0x43C5AF16);\n\t\tpublic static FolderPath LocalizedResourcesDir => _Get(0x2A00375E, 0x224C49DE, 0xB8D1440D, 0xF7EF3DDC);\n\t\tpublic static FolderPath Music => _Get(0x4BD8D571, 0x6D1948D3, 0xBE974222, 0x20080E43);\n\t\tpublic static FolderPath MusicLibrary => _Get(0x2112AB0A, 0xC86A4FFE, 0xA3680DE9, 0x6E47012E);\n\t\tpublic static FolderPath NetHood => _Get(0xC5ABBF53, 0xE17F4121, 0x89008662, 0x6FC2C973);\n\t\tpublic static FolderPath OriginalImages => _Get(0x2C36C0AA, 0x58124b87, 0xBFD04CD0, 0xDFB19B39);\n\t\tpublic static FolderPath PhotoAlbums => _Get(0x69D2CF90, 0xFC334FB7, 0x9A0CEBB0, 0xF0FCB43C);\n\t\tpublic static FolderPath PicturesLibrary => _Get(0xA990AE9F, 0xA03B4E80, 0x94BC9912, 0xD7504104);\n\t\tpublic static FolderPath Pictures => _Get(0x33E28130, 0x4E1E4676, 0x835A9839, 0x5C3BC3BB);\n\t\tpublic static FolderPath Playlists => _Get(0xDE92C1C7, 0x837F4F69, 0xA3BB86E6, 0x31204A23);\n\t\tpublic static FolderPath PrintHood => _Get(0x9274BD8D, 0xCFD141C3, 0xB35EB13F, 0x55A758F4);\n\t\tpublic static FolderPath Profile => _Get(0x5E6C858F, 0x0E224760, 0x9AFEEA33, 0x17B67173);\n\t\tpublic static FolderPath ProgramData => _Get(0x62AB5D82, 0xFDC14DC3, 0xA9DD070D, 0x1D495D97);\n\t\t/// <summary>More info in class help.</summary>\n\t\tpublic static FolderPath ProgramFiles => new(__ProgramFiles ??= _ProgramFiles);\n\t\tstatic string __ProgramFiles;\n\t\tstatic FolderPath _ProgramFiles => _Get(0x905e63b6, 0xc1bf494e, 0xb29c65b7, 0x32d3d21a);\n\t\t//broken static FolderPath ProgramFilesX64 => _Get(0x6D809377, 0x6AF0444b, 0x8957A377, 0x3F02200E);\n\t\t/// <summary>More info in class help.</summary>\n\t\tpublic static FolderPath ProgramFilesX86 => new(__ProgramFilesX86 ??= _ProgramFilesX86);\n\t\tstatic string __ProgramFilesX86;\n\t\tstatic FolderPath _ProgramFilesX86 => _Get(0x7C5A40EF, 0xA0FB4BFC, 0x874AC0F2, 0xE0B9FA8E);\n\t\t/// <summary>More info in class help.</summary>\n\t\tpublic static FolderPath ProgramFilesCommon => _Get(0xF7F1ED05, 0x9F6D47A2, 0xAAAE29D3, 0x17C6F066);\n\t\t//broken static FolderPath ProgramFilesCommonX64 => _Get(0x6365D5A7, 0x0F0D45E5, 0x87F60DA5, 0x6B6A4F7D);\n\t\t/// <summary>More info in class help.</summary>\n\t\tpublic static FolderPath ProgramFilesCommonX86 => _Get(0xDE974D24, 0xD9C64D3E, 0xBF91F445, 0x5120B917);\n\t\tpublic static FolderPath Programs => _Get(0xA77F5D77, 0x2E2B44C3, 0xA6A2ABA6, 0x01054A51);\n\t\tpublic static FolderPath Public => _Get(0xDFDF76A2, 0xC82A4D63, 0x906A5644, 0xAC457385);\n\t\tpublic static FolderPath PublicDesktop => _Get(0xC4AA340D, 0xF20F4863, 0xAFEFF87E, 0xF2E6BA25);\n\t\tpublic static FolderPath PublicDocuments => _Get(0xED4824AF, 0xDCE445A8, 0x81E2FC79, 0x65083634);\n\t\tpublic static FolderPath PublicDownloads => _Get(0x3D644C9B, 0x1FB84f30, 0x9B45F670, 0x235F79C0);\n\t\tpublic static FolderPath PublicGameTasks => _Get(0xDEBF2536, 0xE1A84c59, 0xB6A24145, 0x86476AEA);\n\t\tpublic static FolderPath PublicLibraries => _Get(0x48DAF80B, 0xE6CF4F4E, 0xB8000E69, 0xD84EE384);\n\t\tpublic static FolderPath PublicMusic => _Get(0x3214FAB5, 0x97574298, 0xBB6192A9, 0xDEAA44FF);\n\t\tpublic static FolderPath PublicPictures => _Get(0xB6EBFB86, 0x6907413C, 0x9AF74FC2, 0xABF07CC5);\n\t\tpublic static FolderPath PublicRingtones => _Get(0xE555AB60, 0x153B4D17, 0x9F04A5FE, 0x99FC15EC);\n\t\tpublic static FolderPath PublicUserTiles_Win8 => _Get(0x0482af6c, 0x08f14c34, 0x8c90e17e, 0xc98b1e17);\n\t\tpublic static FolderPath PublicVideos => _Get(0x2400183A, 0x618549FB, 0xA2D84A39, 0x2A602BA3);\n\t\tpublic static FolderPath QuickLaunch => _Get(0x52a4f021, 0x7b7548a9, 0x9f6b4b87, 0xa210bc8f);\n\t\tpublic static FolderPath Recent => _Get(0xAE50C081, 0xEBD2438A, 0x86558A09, 0x2E34987A);\n\t\tpublic static FolderPath RecordedTV => _Get(0x1A6FDBA2, 0xF42D4358, 0xA798B74D, 0x745926C5);\n\t\tpublic static FolderPath ResourceDir => _Get(0x8AD10C31, 0x2ADB4296, 0xA8F7E470, 0x1232C972);\n\t\tpublic static FolderPath Ringtones => _Get(0xC870044B, 0xF49E4126, 0xA9C3B52A, 0x1FF411E8);\n\t\tpublic static FolderPath RoamingAppData => _Get(0x3EB685DB, 0x65F94CF6, 0xA03AE3EF, 0x65729F3D);\n\t\tpublic static FolderPath RoamedTileImages_Win8 => _Get(0xAAA8D5A5, 0xF1D64259, 0xBAA878E7, 0xEF60835E);\n\t\tpublic static FolderPath RoamingTiles_Win8 => _Get(0x00BCFC5A, 0xED944e48, 0x96A13F62, 0x17F21990);\n\t\tpublic static FolderPath SampleMusic => _Get(0xB250C668, 0xF57D4EE1, 0xA63C290E, 0xE7D1AA1F);\n\t\tpublic static FolderPath SamplePictures => _Get(0xC4900540, 0x23794C75, 0x844B64E6, 0xFAF8716B);\n\t\tpublic static FolderPath SamplePlaylists => _Get(0x15CA69B3, 0x30EE49C1, 0xACE16B5E, 0xC372AFB5);\n\t\tpublic static FolderPath SampleVideos => _Get(0x859EAD94, 0x2E8548AD, 0xA71A0969, 0xCB56A6CD);\n\t\tpublic static FolderPath SavedGames => _Get(0x4C5C32FF, 0xBB9D43b0, 0xB5B42D72, 0xE54EAAA4);\n\t\tpublic static FolderPath SavedPictures => _Get(0x3B193882, 0xD3AD4eab, 0x965A6982, 0x9D1FB59F);\n\t\tpublic static FolderPath SavedPicturesLibrary => _Get(0xE25B5812, 0xBE884bd9, 0x94B02923, 0x3477B6C3);\n\t\tpublic static FolderPath SavedSearches => _Get(0x7d1d3a04, 0xdebb4115, 0x95cf2f29, 0xda2920da);\n\t\tpublic static FolderPath Screenshots_Win8 => _Get(0xb7bede81, 0xdf944682, 0xa7d857a5, 0x2620b86f);\n\t\tpublic static FolderPath SearchHistory_Win81 => _Get(0x0D4C3DB6, 0x03A3462F, 0xA0E60892, 0x4C41B5D4);\n\t\tpublic static FolderPath SearchTemplates_Win81 => _Get(0x7E636BFE, 0xDFA94D5E, 0xB456D7B3, 0x9851D8A9);\n\t\tpublic static FolderPath SendTo => _Get(0x8983036C, 0x27C0404B, 0x8F08102D, 0x10DCFD74);\n\t\tpublic static FolderPath SidebarDefaultParts => _Get(0x7B396E54, 0x9EC54300, 0xBE0A2482, 0xEBAE1A26);\n\t\tpublic static FolderPath SidebarParts => _Get(0xA75D362E, 0x50FC4fb7, 0xAC2CA8BE, 0xAA314493);\n\t\tpublic static FolderPath SkyDrive_Win81 => _Get(0xA52BBA46, 0xE9E1435f, 0xB3D928DA, 0xA648C0F6);\n\t\tpublic static FolderPath SkyDriveCameraRoll_Win81 => _Get(0x767E6811, 0x49CB4273, 0x87C220F3, 0x55E1085B);\n\t\tpublic static FolderPath SkyDriveDocuments_Win81 => _Get(0x24D89E24, 0x2F194534, 0x9DDE6A66, 0x71FBB8FE);\n\t\tpublic static FolderPath SkyDrivePictures_Win81 => _Get(0x339719B5, 0x8C474894, 0x94C2D8F7, 0x7ADD44A6);\n\t\tpublic static FolderPath StartMenu => _Get(0x625B53C3, 0xAB484EC1, 0xBA1FA1EF, 0x4146FC19);\n\t\tpublic static FolderPath Startup => _Get(0xB97D20BB, 0xF46A4C97, 0xBA105E36, 0x08430854);\n\t\t/// <summary>More info in class help.</summary>\n\t\tpublic static FolderPath System => new(__System ??= _System);\n\t\tstatic string __System;\n\t\tstatic FolderPath _System => _Get(0x1AC14E77, 0x02E74E5D, 0xB7442EB1, 0xAE5198B7);\n\t\t/// <summary>More info in class help.</summary>\n\t\tpublic static FolderPath SystemX86 => _Get(0xD65231B0, 0xB2F14857, 0xA4CEA8E7, 0xC6EA7D27);\n\t\tpublic static FolderPath Templates => _Get(0xA63293E8, 0x664E48DB, 0xA079DF75, 0x9E0509F7);\n\t\tpublic static FolderPath TreeProperties => _Get(0x9E3995AB, 0x1F9C4F13, 0xB82748B2, 0x4B6C7174);\n\t\tpublic static FolderPath UserProfiles => _Get(0x0762D272, 0xC50A4BB0, 0xA382697D, 0xCD729B80);\n\t\tpublic static FolderPath UserProgramFiles => _Get(0x5CD7AEE2, 0x22194A67, 0xB85D6C9C, 0xE15660CB);\n\t\tpublic static FolderPath UserProgramFilesCommon => _Get(0xBCBD3057, 0xCA5C4622, 0xB42DBC56, 0xDB0AE516);\n\t\tpublic static FolderPath Videos => _Get(0x18989B1D, 0x99B5455B, 0x841CAB7C, 0x74E4DDFC);\n\t\tpublic static FolderPath VideosLibrary => _Get(0x491E922F, 0x56434AF4, 0xA7EB4E7A, 0x138D8174);\n\t\tpublic static FolderPath Windows => new(__Windows ??= _Windows);\n\t\tstatic string __Windows;\n\t\tstatic FolderPath _Windows => _Get(0xF38BF404, 0x1D4342F2, 0x930567DE, 0x0B28FC23);\n\t\t\n\t\t/// <summary>\n\t\t/// Gets <ms>ITEMIDLIST</ms> of known/special virtual folders (eg Control Panel), as string like <c>\":: 12345678...\"</c> or as <see cref=\"Pidl\"/>.\n\t\t/// </summary>\n\t\tpublic static class shell {\n\t\t\tpublic static FolderPath AddNewPrograms => _GetV(0xde61d971, 0x5ebc4f02, 0xa3a96c82, 0x895e5c04);\n\t\t\tpublic static FolderPath Apps_Win8 => _GetV(0x1e87508d, 0x89c242f0, 0x8a7e645a, 0x0f50ca58);\n\t\t\tpublic static FolderPath AppUpdates => _GetV(0xa305ce99, 0xf527492b, 0x8b1a7e76, 0xfa98d6e4);\n\t\t\tpublic static FolderPath ChangeRemovePrograms => _GetV(0xdf7266ac, 0x92744867, 0x8d553bd6, 0x61de872d);\n\t\t\tpublic static FolderPath Computer => _GetV(0x0AC0837C, 0xBBF8452A, 0x850D79D0, 0x8E667CA7);\n\t\t\tpublic static FolderPath Conflict => _GetV(0x4bfefb45, 0x347d4006, 0xa5beac0c, 0xb0567192);\n\t\t\tpublic static FolderPath Connections => _GetV(0x6F0CD92B, 0x2E9745D1, 0x88FFB0D1, 0x86B8DEDD);\n\t\t\tpublic static FolderPath ControlPanel => _GetV(0x82A74AEB, 0xAEB4465C, 0xA014D097, 0xEE346D63);\n\t\t\tpublic static FolderPath Games => _GetV(0xCAC52C1A, 0xB53D4edc, 0x92D76B2E, 0x8AC19434);\n\t\t\tpublic static FolderPath HomeGroup => _GetV(0x52528A6B, 0xB9E34ADD, 0xB60D588C, 0x2DBA842D);\n\t\t\tpublic static FolderPath HomeGroupCurrentUser_Win8 => _GetV(0x9B74B6A3, 0x0DFD4f11, 0x9E785F78, 0x00F2E772);\n\t\t\tpublic static FolderPath Internet => _GetV(0x4D9F7874, 0x4E0C4904, 0x967B40B0, 0xD20C3E4B);\n\t\t\tpublic static FolderPath Network => _GetV(0xD20BEEC4, 0x5CA84905, 0xAE3BBF25, 0x1EA09B53);\n\t\t\tpublic static FolderPath Printers => _GetV(0x76FC4E2D, 0xD6AD4519, 0xA66337BD, 0x56068185);\n\t\t\tpublic static FolderPath RecycleBin => _GetV(0xB7534046, 0x3ECB4C18, 0xBE4E64CD, 0x4CB7D6AC);\n\t\t\tpublic static FolderPath SEARCH_CSC => _GetV(0xee32e446, 0x31ca4aba, 0x814fa5eb, 0xd2fd6d5e);\n\t\t\tpublic static FolderPath SearchHome => _GetV(0x190337d1, 0xb8ca4121, 0xa6396d47, 0x2d16972a);\n\t\t\tpublic static FolderPath SEARCH_MAPI => _GetV(0x98ec0e18, 0x20984d44, 0x86446697, 0x9315a281);\n\t\t\tpublic static FolderPath SyncManager => _GetV(0x43668BF8, 0xC14E49B2, 0x97C97477, 0x84D784B7);\n\t\t\tpublic static FolderPath SyncResults => _GetV(0x289a9a43, 0xbe444057, 0xa41b587a, 0x76d7e7f9);\n\t\t\tpublic static FolderPath SyncSetup => _GetV(0x0F214138, 0xB1D34a90, 0xBBA927CB, 0xC0C5389A);\n\t\t\tpublic static FolderPath UsersFiles => _GetV(0xf3ce0f7c, 0x49014acc, 0x8648d5d4, 0x4b04ef8f);\n\t\t\tpublic static FolderPath UsersLibraries => _GetV(0xA302545D, 0xDEFF464b, 0xABE861C8, 0x648D939B);\n\t\t\t\n\t\t\tpublic static Pidl pidlAddNewPrograms => _GetVI(0xde61d971, 0x5ebc4f02, 0xa3a96c82, 0x895e5c04);\n\t\t\tpublic static Pidl pidlApps_Win8 => _GetVI(0x1e87508d, 0x89c242f0, 0x8a7e645a, 0x0f50ca58);\n\t\t\tpublic static Pidl pidlAppUpdates => _GetVI(0xa305ce99, 0xf527492b, 0x8b1a7e76, 0xfa98d6e4);\n\t\t\tpublic static Pidl pidlChangeRemovePrograms => _GetVI(0xdf7266ac, 0x92744867, 0x8d553bd6, 0x61de872d);\n\t\t\tpublic static Pidl pidlComputer => _GetVI(0x0AC0837C, 0xBBF8452A, 0x850D79D0, 0x8E667CA7);\n\t\t\tpublic static Pidl pidlConflict => _GetVI(0x4bfefb45, 0x347d4006, 0xa5beac0c, 0xb0567192);\n\t\t\tpublic static Pidl pidlConnections => _GetVI(0x6F0CD92B, 0x2E9745D1, 0x88FFB0D1, 0x86B8DEDD);\n\t\t\tpublic static Pidl pidlControlPanel => _GetVI(0x82A74AEB, 0xAEB4465C, 0xA014D097, 0xEE346D63);\n\t\t\tpublic static Pidl pidlGames => _GetVI(0xCAC52C1A, 0xB53D4edc, 0x92D76B2E, 0x8AC19434);\n\t\t\tpublic static Pidl pidlHomeGroup => _GetVI(0x52528A6B, 0xB9E34ADD, 0xB60D588C, 0x2DBA842D);\n\t\t\tpublic static Pidl pidlHomeGroupCurrentUser_Win8 => _GetVI(0x9B74B6A3, 0x0DFD4f11, 0x9E785F78, 0x00F2E772);\n\t\t\tpublic static Pidl pidlInternet => _GetVI(0x4D9F7874, 0x4E0C4904, 0x967B40B0, 0xD20C3E4B);\n\t\t\tpublic static Pidl pidlNetwork => _GetVI(0xD20BEEC4, 0x5CA84905, 0xAE3BBF25, 0x1EA09B53);\n\t\t\tpublic static Pidl pidlPrinters => _GetVI(0x76FC4E2D, 0xD6AD4519, 0xA66337BD, 0x56068185);\n\t\t\tpublic static Pidl pidlRecycleBin => _GetVI(0xB7534046, 0x3ECB4C18, 0xBE4E64CD, 0x4CB7D6AC);\n\t\t\tpublic static Pidl pidlSEARCH_CSC => _GetVI(0xee32e446, 0x31ca4aba, 0x814fa5eb, 0xd2fd6d5e);\n\t\t\tpublic static Pidl pidlSearchHome => _GetVI(0x190337d1, 0xb8ca4121, 0xa6396d47, 0x2d16972a);\n\t\t\tpublic static Pidl pidlSEARCH_MAPI => _GetVI(0x98ec0e18, 0x20984d44, 0x86446697, 0x9315a281);\n\t\t\tpublic static Pidl pidlSyncManager => _GetVI(0x43668BF8, 0xC14E49B2, 0x97C97477, 0x84D784B7);\n\t\t\tpublic static Pidl pidlSyncResults => _GetVI(0x289a9a43, 0xbe444057, 0xa41b587a, 0x76d7e7f9);\n\t\t\tpublic static Pidl pidlSyncSetup => _GetVI(0x0F214138, 0xB1D34a90, 0xBBA927CB, 0xC0C5389A);\n\t\t\tpublic static Pidl pidlUsersFiles => _GetVI(0xf3ce0f7c, 0x49014acc, 0x8648d5d4, 0x4b04ef8f);\n\t\t\tpublic static Pidl pidlUsersLibraries => _GetVI(0xA302545D, 0xDEFF464b, 0xABE861C8, 0x648D939B);\n\t\t}\n\t\t\n\t\t#endregion\n\t\t\n\t\t#region other paths\n\t\t\n\t\t/// <summary>\n\t\t/// Temp folder (temporary files) of this user account.\n\t\t/// </summary>\n\t\tpublic static FolderPath Temp => new(__temp ??= Path.GetTempPath().TrimEnd('\\\\'));\n\t\tstatic string __temp;\n\t\t\n\t\t/// <summary>\n\t\t/// Folder containing assemblies of this app.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Uses <see cref=\"AppContext.BaseDirectory\"/>.\n\t\t/// </remarks>\n\t\t/// <seealso cref=\"process.thisExePath\"/>\n\t\tpublic static FolderPath ThisApp => new(__thisApp ??= ThisAppBS.TrimEnd('\\\\'));\n\t\tstatic string __thisApp;\n\t\t\n\t\t/// <summary>\n\t\t/// <see cref=\"ThisApp\"/> with appended backslash character.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Uses <see cref=\"AppContext.BaseDirectory\"/>.\n\t\t/// </remarks>\n\t\tpublic static string ThisAppBS => __thisAppBS ??= AppContext.BaseDirectory; //info: AppDomain.CurrentDomain.BaseDirectory calls it\n\t\tstatic string __thisAppBS;\n\t\t//Can change: AppDomain.CurrentDomain.SetData(\"APP_CONTEXT_BASE_DIRECTORY\", \"C:\\\\\");\n\t\t\n\t\t#region set auto/once\n\t\t\n\t\t/// <summary>\n\t\t/// Don't auto-create folders when accessing <see cref=\"ThisAppDocuments\"/>, <see cref=\"ThisAppTemp\"/>, <see cref=\"ThisAppDataLocal\"/> or <see cref=\"ThisAppDataRoaming\"/> in this thread.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Normally this is used temporarily, then restored (set = <c>false</c>).\n\t\t/// </remarks>\n\t\tpublic static bool noAutoCreate {\n\t\t\tget => _noAutoCreate;\n\t\t\tset { _noAutoCreate = value; }\n\t\t}\n\t\t[ThreadStatic] static bool _noAutoCreate;\n\t\t\n\t\tstatic readonly object _lock = new();\n\t\t\n\t\tstatic string _SetAuto(ref string propVar, string value, bool create) {\n\t\t\tlock (_lock) {\n\t\t\t\tif (propVar == null) {\n\t\t\t\t\tif (create && !_noAutoCreate) filesystem.createDirectory(value);\n\t\t\t\t\tpropVar = value;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn propVar;\n\t\t}\n\t\t\n\t\tstatic void _SetOnce(ref string propVar, string value, bool create, [CallerMemberName] string m_ = null) {\n\t\t\tlock (_lock) {\n\t\t\t\tif (propVar != null) {\n#if DEBUG\n\t\t\t\t\tif (!Debugger.IsAttached) //debugger may get the property. Then _SetAuto sets default value.\n#endif\n\t\t\t\t\t\tthrow new InvalidOperationException(\"folders.\" + m_ + \" is already set.\");\n\t\t\t\t}\n\t\t\t\tif (create && !_noAutoCreate) filesystem.createDirectory(value);\n\t\t\t\tpropVar = value;\n\t\t\t}\n\t\t}\n\t\t\n\t\tstatic string _DefThisApp(string baseDir, string portableSubdir, bool warning = false) {\n\t\t\tbool ee = script.role == SRole.EditorExtension;\n\t\t\t\n\t\t\tif (ScriptEditor.IsPortable) return PortableData_ + \"\\\\\" + portableSubdir + (ee ? null : @\"\\_script\");\n\t\t\t\n\t\t\treturn baseDir + (ee ? @\"\\LibreAutomate\" : @\"\\LibreAutomate\\_script\");\n\t\t}\n\t\t\n\t\tinternal static string PortableData_ => __portableData ??= GetPortableDataDir_(Editor.Path);\n\t\tstatic string __portableData;\n\t\t\n\t\tstatic internal string GetPortableDataDir_(string appPath) {\n\t\t\tvar s = appPath + @\"\\data\";\n\t\t\t//if (filesystem.exists(s, true).File) return pathname.normalize(filesystem.loadText(s), appPath);\n\t\t\treturn s;\n\t\t}\n\t\t\n\t\t#endregion\n\t\t\n\t\t/// <summary>\n\t\t/// Gets or sets path of folder \"temporary files of this application\".\n\t\t/// </summary>\n\t\t/// <exception cref=\"InvalidOperationException\">Thrown by the <c>set</c> function if this property is already set.</exception>\n\t\t/// <remarks>\n\t\t/// Default path depends on script role (<see cref=\"script.role\"/> and portable mode (<see cref=\"ScriptEditor.IsPortable\"/>):\n\t\t/// - <c>miniProgram</c> and <c>exeProgram</c> - <c>folders.Temp + @\"LibreAutomate\\_script\"</c>. Or <c>folders.Temp + \"Au\"</c>, if exists (for backward compatibility).\n\t\t/// - <c>editorExtension</c> - <c>folders.Temp + \"LibreAutomate\"</c>. Cannot be changed.\n\t\t/// - Portable <c>miniProgram</c> and <c>exeProgram</c> - <c>folders.ThisApp + @\"data\\temp\\_script\"</c>. Note: <c>exeProgram</c> launched not from editor isn't portable.\n\t\t/// - Portable <c>editorExtension</c> - <c>folders.ThisApp + @\"data\\temp\"</c>. Cannot be changed.\n\t\t/// \n\t\t/// The <c>set</c> function does not change system settings. It just remembers a string that will be later returned by the <c>get</c> function in this process.\n\t\t/// Creates the folder if does not exist when <c>set</c> or <c>get</c> function called first time in this process, unless <see cref=\"noAutoCreate\"/> <c>true</c>.\n\t\t/// </remarks>\n\t\tpublic static FolderPath ThisAppTemp {\n\t\t\tget => new(__thisAppTemp ?? _SetAuto(ref __thisAppTemp, _DefThisApp(Temp, \"temp\"), create: true));\n\t\t\tset => _SetOnce(ref __thisAppTemp, value, create: true);\n\t\t}\n\t\tstatic string __thisAppTemp;\n\t\t\n\t\t/// <summary>\n\t\t/// Gets or sets path of folder \"user document files of this application\".\n\t\t/// </summary>\n\t\t/// <exception cref=\"InvalidOperationException\">Thrown by the <c>set</c> function if this property is already set.</exception>\n\t\t/// <remarks>\n\t\t/// Default path depends on script role (<see cref=\"script.role\"/>) and portable mode (<see cref=\"ScriptEditor.IsPortable\"/>):\n\t\t/// - <c>miniProgram</c> and <c>exeProgram</c> - <c>folders.Documents + @\"LibreAutomate\\_script\"</c>. Or <c>folders.Documents + \"Au\"</c>, if exists (for backward compatibility).\n\t\t/// - <c>editorExtension</c> - <c>folders.Documents + \"LibreAutomate\"</c>. Cannot be changed.\n\t\t/// - Portable <c>miniProgram</c> and <c>exeProgram</c> - <c>folders.ThisApp + @\"data\\doc\\_script\"</c>. Note: <c>exeProgram</c> launched not from editor isn't portable.\n\t\t/// - Portable <c>editorExtension</c> - <c>folders.ThisApp + @\"data\\doc\"</c>. Cannot be changed.\n\t\t/// \n\t\t/// The <c>set</c> function does not change system settings. It just remembers a string that will be later returned by the <c>get</c> function in this process.\n\t\t/// Creates the folder if does not exist when <c>set</c> or <c>get</c> function called first time in this process, unless <see cref=\"noAutoCreate\"/> <c>true</c>.\n\t\t/// </remarks>\n\t\tpublic static FolderPath ThisAppDocuments {\n\t\t\tget => new(__thisAppDocuments ?? _SetAuto(ref __thisAppDocuments, _DefThisApp(Documents, \"doc\", warning: true), create: true));\n\t\t\tset => _SetOnce(ref __thisAppDocuments, value, create: true);\n\t\t}\n\t\tstatic string __thisAppDocuments;\n\t\t\n\t\t/// <summary>\n\t\t/// Gets or sets path of folder \"local (non-roaming) private files of this application of this user account\".\n\t\t/// </summary>\n\t\t/// <exception cref=\"InvalidOperationException\">Thrown by the <c>set</c> function if this property is already set.</exception>\n\t\t/// <remarks>\n\t\t/// Default path depends on script role (<see cref=\"script.role\"/> and portable mode (<see cref=\"ScriptEditor.IsPortable\"/>):\n\t\t/// - <c>miniProgram</c> and <c>exeProgram</c> - <c>folders.LocalAppData + @\"LibreAutomate\\_script\"</c>. Or <c>folders.LocalAppData + \"Au\"</c>, if exists (for backward compatibility).\n\t\t/// - <c>editorExtension</c> - <c>folders.LocalAppData + \"LibreAutomate\"</c>. Cannot be changed.\n\t\t/// - Portable <c>miniProgram</c> and <c>exeProgram</c> - <c>folders.ThisApp + @\"data\\appLocal\\_script\"</c>. Note: <c>exeProgram</c> launched not from editor isn't portable.\n\t\t/// - Portable <c>editorExtension</c> - <c>folders.ThisApp + @\"data\\appLocal\"</c>. Cannot be changed.\n\t\t/// \n\t\t/// The <c>set</c> function does not change system settings. It just remembers a string that will be later returned by the <c>get</c> function in this process.\n\t\t/// Creates the folder if does not exist when <c>set</c> or <c>get</c> function called first time in this process, unless <see cref=\"noAutoCreate\"/> <c>true</c>.\n\t\t/// </remarks>\n\t\tpublic static FolderPath ThisAppDataLocal {\n\t\t\tget => new(__thisAppDataLocal ?? _SetAuto(ref __thisAppDataLocal, _DefThisApp(LocalAppData, \"appLocal\", warning: true), create: true));\n\t\t\tset => _SetOnce(ref __thisAppDataLocal, value, create: true);\n\t\t}\n\t\tstatic string __thisAppDataLocal;\n\t\t\n\t\t/// <summary>\n\t\t/// Gets or sets path of folder \"roaming private files of this application of this user account\".\n\t\t/// </summary>\n\t\t/// <exception cref=\"InvalidOperationException\">Thrown by the <c>set</c> function if this property is already set.</exception>\n\t\t/// <remarks>\n\t\t/// Default path depends on script role (<see cref=\"script.role\"/> and portable mode (<see cref=\"ScriptEditor.IsPortable\"/>):\n\t\t/// - <c>miniProgram</c> and <c>exeProgram</c> - <c>folders.RoamingAppData + @\"LibreAutomate\\_script\"</c>. Or <c>folders.RoamingAppData + \"Au\"</c>, if exists (for backward compatibility).\n\t\t/// - <c>editorExtension</c> - <c>folders.RoamingAppData + \"LibreAutomate\"</c>. Cannot be changed.\n\t\t/// - Portable <c>miniProgram</c> and <c>exeProgram</c> - <c>folders.ThisApp + @\"data\\appRoaming\\_script\"</c>. Note: <c>exeProgram</c> launched not from editor isn't portable.\n\t\t/// - Portable <c>editorExtension</c> - <c>folders.ThisApp + @\"data\\appRoaming\"</c>. Cannot be changed.\n\t\t/// \n\t\t/// The <c>set</c> function does not change system settings. It just remembers a string that will be later returned by the <c>get</c> function in this process.\n\t\t/// Creates the folder if does not exist when <c>set</c> or <c>get</c> function called first time in this process, unless <see cref=\"noAutoCreate\"/> <c>true</c>.\n\t\t/// </remarks>\n\t\tpublic static FolderPath ThisAppDataRoaming {\n\t\t\tget => new(__thisAppDataRoaming ?? _SetAuto(ref __thisAppDataRoaming, _DefThisApp(RoamingAppData, \"appRoaming\"), create: true));\n\t\t\tset => _SetOnce(ref __thisAppDataRoaming, value, create: true);\n\t\t}\n\t\tstatic string __thisAppDataRoaming;\n\t\t\n\t\t[EditorBrowsable(EditorBrowsableState.Never)] //renamed\n\t\t[Obsolete(\"This is an alias for ThisAppDataRoaming.\")]\n\t\tpublic static FolderPath ThisAppData {\n\t\t\tget => ThisAppDataRoaming;\n\t\t\tset => ThisAppDataRoaming = value;\n\t\t} //info: unexpandPath ignores ThisAppX.\n\t\t\n\t\t/// <summary>\n\t\t/// Gets or sets path of folder \"common (all users) private files of this application\".\n\t\t/// </summary>\n\t\t/// <exception cref=\"InvalidOperationException\">Thrown by the <c>set</c> function if this property is already set.</exception>\n\t\t/// <remarks>\n\t\t/// Default path depends on script role (<see cref=\"script.role\"/> and portable mode (<see cref=\"ScriptEditor.IsPortable\"/>):\n\t\t/// - <c>miniProgram</c> and <c>exeProgram</c> - <c>folders.ProgramData + @\"LibreAutomate\\_script\"</c>. Or <c>folders.ProgramData + \"Au\"</c>, if exists (for backward compatibility).\n\t\t/// - <c>editorExtension</c> - <c>folders.ProgramData + \"LibreAutomate\"</c>. Cannot be changed.\n\t\t/// - Portable <c>miniProgram</c> and <c>exeProgram</c> - <c>folders.ThisApp + @\"data\\appCommon\\_script\"</c>. Note: <c>exeProgram</c> launched not from editor isn't portable.\n\t\t/// - Portable <c>editorExtension</c> - <c>folders.ThisApp + @\"data\\appCommon\"</c>. Cannot be changed.\n\t\t/// \n\t\t/// The <c>set</c> function does not change system settings. It just remembers a string that will be later returned by the <c>get</c> function in this process.\n\t\t/// This function does not auto-create the folder; usually it is created when installing the application; the script editor does not use and does not install it.\n\t\t/// Note: the <c>ProgramData</c> folder has special security permissions. Programs running not as administrator usually cannot write there, unless your installer changed folder security permissions.\n\t\t/// </remarks>\n\t\tpublic static FolderPath ThisAppDataCommon {\n\t\t\tget => new(__thisAppDataCommon ?? _SetAuto(ref __thisAppDataCommon, _DefThisApp(ProgramData, \"appCommon\"), create: false));\n\t\t\tset => _SetOnce(ref __thisAppDataCommon, value, create: false);\n\t\t}\n\t\tstatic string __thisAppDataCommon;\n\t\t\n\t\t/// <summary>\n\t\t/// Gets or sets path of folder \"images of this application\".\n\t\t/// </summary>\n\t\t/// <exception cref=\"InvalidOperationException\">Thrown by the <c>set</c> function if this property is already set.</exception>\n\t\t/// <remarks>\n\t\t/// Default is <c>ThisAppBS + \"Images\"</c>.\n\t\t/// \n\t\t/// Used by functions of these classes: <see cref=\"icon\"/>, <see cref=\"popupMenu\"/>, <see cref=\"toolbar\"/>, <see cref=\"uiimage\"/>, possibly some other.\n\t\t/// This function does not auto-create the folder; usually it is created when installing the application; the script editor does not use and does not install it.\n\t\t/// </remarks>\n\t\tpublic static FolderPath ThisAppImages {\n\t\t\tget => new(__thisAppImages ?? _SetAuto(ref __thisAppImages, ThisAppBS + \"Images\", create: false));\n\t\t\tset => _SetOnce(ref __thisAppImages, value, create: false);\n\t\t}\n\t\tstatic string __thisAppImages;\n\t\t\n\t\t/// <summary>\n\t\t/// Gets the root directory of this application, like <c>@\"C:\\\"</c> or <c>@\"\\\\server\\share\\\"</c>.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// See <see cref=\"Path.GetPathRoot\"/>.\n\t\t/// </remarks>\n\t\tpublic static string ThisAppDriveBS => __thisAppDrive ??= pathname.GetRootBS_(ThisAppBS);\n\t\tstatic string __thisAppDrive;\n\t\t//public static FolderPath ThisAppDrive => new(__thisAppDrive ??= Path.GetPathRoot(ThisAppBS));\n\t\t\n\t\t/// <summary>\n\t\t/// Gets folder of the script editor.\n\t\t/// Available in the script editor process and in scripts launched from it. Elsewhere <c>null</c>.\n\t\t/// </summary>\n\t\tpublic static FolderPath Editor { get; internal set; }\n\t\t\n\t\t/// <summary>\n\t\t/// Gets folder of current workspace.\n\t\t/// Available in the script editor process and in scripts launched from it. Elsewhere <c>null</c>.\n\t\t/// </summary>\n\t\tpublic static FolderPath Workspace {\n\t\t\tget => __workspace;\n\t\t\tinternal set { __workspace = value; WorkspaceDriveBS = pathname.GetRootBS_(value); }\n\t\t}\n\t\tstatic FolderPath __workspace;\n\t\t\n\t\t/// <summary>\n\t\t/// Gets the root directory of <see cref=\"Workspace\"/>, like <c>@\"C:\\\"</c> or <c>@\"\\\\server\\share\\\"</c>.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// See <see cref=\"Path.GetPathRoot\"/>.\n\t\t/// </remarks>\n\t\tpublic static string WorkspaceDriveBS { get; private set; }\n\t\t\n\t\t//CONSIDER:\n\t\t//public static FolderPath ThisLibrarySettings { get => field ??= ThisAppDataRoaming; set; }\n\t\t\n#if !DEBUG //fbc\n\t\t/// <summary>\n\t\t/// Gets drive type (fixed, removable, network, etc) of <see cref=\"ThisAppDriveBS\"/>.\n\t\t/// </summary>\n\t\t[EditorBrowsable(EditorBrowsableState.Never)] //more annoying than useful. Intellisense selects it when the user types \"thisApp\". And maybe this class isn't the best place for it. Probably users will not look for such function here or somewhere in this library; they'll use DriveInfo. In any case, this is not very useful because: 1. Detects external SSD as Fixed; 2. The removable drive (SSD or not) may be used either as portable or always with the same computer.\n\t\tpublic static DriveType thisAppDriveType => __driveTypeApp ??= __GetDriveType(ThisAppDriveBS);\n\t\tstatic DriveType? __driveTypeApp;\n\t\t\n\t\t/// <summary>\n\t\t/// Gets drive type (fixed, removable, network, etc) of <see cref=\"WorkspaceDriveBS\"/>.\n\t\t/// </summary>\n\t\t[EditorBrowsable(EditorBrowsableState.Never)]\n\t\tpublic static DriveType workspaceDriveType => __GetDriveType(WorkspaceDriveBS);\n\t\t\n\t\t//note: don't use this func with any paths. It is for the above 2 funcs only.\n\t\tstatic DriveType __GetDriveType(string path) {\n\t\t\tpath = pathname.unprefixLongPath(path);\n\t\t\tif (path.Starts(@\"\\\\\")) return DriveType.Network; //GetDriveType does not support it. DriveInfo throws exception.\n\t\t\treturn (DriveType)Api.GetDriveType(path);\n\t\t}\n#endif\n\t\t\n\t\t/// <summary>\n\t\t/// Gets folder path of the caller source code file.\n\t\t/// </summary>\n\t\t/// <param name=\"f_\">[](xref:caller_info)</param>\n\t\t/// <seealso cref=\"CallerFilePathAttribute\"/>\n\t\t/// <seealso cref=\"script.sourcePath(string)\"/>\n\t\tpublic static FolderPath sourceCode([CallerFilePath] string f_ = null) => new(pathname.getDirectory(f_));\n\t\t\n\t\t/// <summary>\n\t\t/// Gets folder path of the main source code file of this program or of a library.\n\t\t/// </summary>\n\t\t/// <param name=\"asm\">An assembly compiled by LibreAutomate. If <c>null</c>, uses <see cref=\"Assembly.GetEntryAssembly\"/>.</param>\n\t\t/// <seealso cref=\"script.sourcePath(bool, Assembly)\"/>\n\t\tpublic static FolderPath sourceCodeMain(Assembly asm = null) => new(pathname.getDirectory(script.sourcePath(false, asm)));\n\t\t\n\t\t/// <summary>\n\t\t/// Gets non-redirected path of the System32 folder.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// If this process is 32-bit and OS is 64-bit, when it uses the <see cref=\"System\"/> folder path (<c>@\"C:\\WINDOWS\\system32\"</c>), the OS in most cases redirects it to <c>@\"C:\\Windows\\SysWOW64\"</c>, which contains 32-bit versions of program files. Use SystemX64 when you want to avoid the redirection and access the true System32 folder which on 64-bit OS contains 64-bit program files.\n\t\t/// More info in class help.\n\t\t/// </remarks>\n\t\t/// <seealso cref=\"FileSystemRedirection\"/>\n\t\t/// <seealso cref=\"osVersion.is32BitProcessAnd64BitOS\"/>\n\t\tpublic static FolderPath SystemX64 => new(__SystemX64 ??= osVersion.is32BitProcessAnd64BitOS ? Windows + \"Sysnative\" : System);\n\t\tstatic string __SystemX64;\n\t\t\n\t\t/// <summary>More info in class help.</summary>\n\t\tpublic static FolderPath ProgramFilesX64 => new(__ProgramFilesX64 ??= osVersion.is32BitProcessAnd64BitOS ? envVar(\"ProgramW6432\") : ProgramFiles);\n\t\tstatic string __ProgramFilesX64;\n\t\t\n\t\t/// <summary>More info in class help.</summary>\n\t\tpublic static FolderPath ProgramFilesCommonX64 => new(__ProgramFilesCommonX64 ??= osVersion.is32BitProcessAnd64BitOS ? envVar(\"CommonProgramW6432\") : ProgramFilesCommon);\n\t\tstatic string __ProgramFilesCommonX64;\n\t\t//The normal retrieving method for these folders is broken. Fails even on 64-bit OS if process is 32-bit.\n\t\t\n\t\t/// <summary>\n\t\t/// Gets .NET runtime folder, like <c>C:\\Program Files\\dotnet\\shared\\Microsoft.NETCore.App\\8.0.6</c>.\n\t\t/// </summary>\n\t\tpublic static FolderPath NetRuntime => new(__netRuntime ??= NetRuntimeBS.TrimEnd('\\\\'));\n\t\tstatic string __netRuntime;\n\t\t\n\t\t/// <summary>\n\t\t/// Gets .NET runtime folder with <c>'\\\\'</c> at the end, like <c>C:\\Program Files\\dotnet\\shared\\Microsoft.NETCore.App\\8.0.6\\</c>.\n\t\t/// </summary>\n\t\tpublic static string NetRuntimeBS => __netRuntimeBS ??= RuntimeEnvironment.GetRuntimeDirectory();\n\t\tstatic string __netRuntimeBS;\n\t\t\n\t\t/// <summary>\n\t\t/// Gets .NET runtime desktop folder, like <c>C:\\Program Files\\dotnet\\shared\\Microsoft.WindowsDesktop.App\\8.0.6</c>.\n\t\t/// </summary>\n\t\tpublic static FolderPath NetRuntimeDesktop => new(__netRuntimeDesktop ??= NetRuntimeDesktopBS.TrimEnd('\\\\'));\n\t\tstatic string __netRuntimeDesktop;\n\t\t\n\t\t/// <summary>\n\t\t/// Gets .NET runtime desktop folder with <c>'\\\\'</c> at the end, like <c>C:\\Program Files\\dotnet\\shared\\Microsoft.WindowsDesktop.App\\8.0.6\\</c>.\n\t\t/// </summary>\n\t\tpublic static string NetRuntimeDesktopBS => __netRuntimeDesktopBS ??= _NetRuntimeDesktopBS();\n\t\tstatic string __netRuntimeDesktopBS;\n\t\t\n\t\tstatic string _NetRuntimeDesktopBS() {\n\t\t\tvar s = typeof(Accessibility.IAccessible).Assembly.Location;\n\t\t\tif (s.NE()) return NetRuntimeBS; //single-file app\n\t\t\ts = pathname.getDirectory(s, withSeparator: true);\n\t\t\tDebug.Assert(s != NetRuntimeBS || s.Starts(ThisAppBS));\n\t\t\treturn s;\n\t\t\t//note: cannot get NetRuntimeDesktopBS from NetRuntimeBS.\n\t\t\t//\tCan be different version, eg Microsoft.NETCore.App\\8.0.0-rc.1.23419.4 and Microsoft.WindowsDesktop.App\\8.0.0-rc.1.23420.5.\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets CD/DVD drive path, like <c>@\"D:\\\"</c>.\n\t\t/// </summary>\n\t\t/// <returns><c>null</c> if unavailable.</returns>\n\t\tpublic static FolderPath CdDvdDrive {\n\t\t\tget {\n\t\t\t\tforeach (var di in DriveInfo.GetDrives()) {\n\t\t\t\t\tif (di.DriveType == DriveType.CDRom) return new(di.Name);\n\t\t\t\t}\n\t\t\t\treturn default;\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>Calls <see cref=\"removableDrive(int)\"/>(0).</summary>\n\t\tpublic static FolderPath RemovableDrive0 => removableDrive(0);\n\t\t/// <summary>Calls <see cref=\"removableDrive(int)\"/>(1).</summary>\n\t\tpublic static FolderPath RemovableDrive1 => removableDrive(1);\n\t\t/// <summary>Calls <see cref=\"removableDrive(int)\"/>(2).</summary>\n\t\tpublic static FolderPath RemovableDrive2 => removableDrive(2);\n\t\t/// <summary>Calls <see cref=\"removableDrive(int)\"/>(3).</summary>\n\t\tpublic static FolderPath RemovableDrive3 => removableDrive(3);\n\t\t\n\t\t/// <summary>\n\t\t/// Gets removable/external/USB drive path, like <c>@\"F:\\\"</c>.\n\t\t/// </summary>\n\t\t/// <returns><c>null</c> if unavailable.</returns>\n\t\t/// <param name=\"driveIndex\">0-based removable drive index.</param>\n\t\t/// <remarks>\n\t\t/// Calls <see cref=\"DriveInfo.GetDrives\"/> and counts drives of type <see cref=\"DriveType.Removable\"/>.\n\t\t/// </remarks>\n\t\tpublic static FolderPath removableDrive(int driveIndex = 0) {\n\t\t\tforeach (DriveInfo di in DriveInfo.GetDrives()) {\n\t\t\t\tif (di.DriveType == DriveType.Removable && driveIndex-- == 0) return new(di.Name);\n\t\t\t}\n\t\t\treturn default;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets removable/external/USB drive name (like <c>@\"F:\\\"</c>) by its volume label.\n\t\t/// </summary>\n\t\t/// <returns><c>null</c> if unavailable.</returns>\n\t\t/// <param name=\"volumeLabel\">Volume label. You can see it in drive <b>Properties</b> dialog; it is not the drive name that is displayed in File Explorer.</param>\n\t\tpublic static FolderPath removableDrive(string volumeLabel) {\n\t\t\tforeach (DriveInfo di in DriveInfo.GetDrives()) {\n\t\t\t\tif (di.DriveType == DriveType.Removable) {\n\t\t\t\t\tstring v = null; try { v = di.VolumeLabel; } catch { continue; }\n\t\t\t\t\tif (!v.Eqi(volumeLabel)) continue;\n\t\t\t\t\treturn new(di.Name);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn default;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets the value of an environment variable in current process.\n\t\t/// </summary>\n\t\t/// <returns><c>null</c> if variable not found.</returns>\n\t\t/// <seealso cref=\"Environment.GetEnvironmentVariable\"/>\n\t\t/// <seealso cref=\"Environment.SetEnvironmentVariable\"/>\n\t\t/// <seealso cref=\"pathname.expand\"/>\n\t\tpublic static FolderPath envVar(string envVar) => new(Api.GetEnvironmentVariable(envVar));\n\t\t\n\t\t#endregion\n\t\t\n\t\t#region private functions\n\t\t\n\t\t//Gets non-virtual known folder path from KNOWNFOLDERID specified with 4 uints.\n\t\tstatic FolderPath _Get(uint a, uint b, uint c, uint d) {\n\t\t\t//info: we don't use caching. It seems the API use caching internally.\n\t\t\t//tested: with IKnownFolder much slower.\n\t\t\t\n\t\t\tvar guid = new _Api.KNOWNFOLDERID(a, b, c, d);\n\t\t\tif (0 != _Api.SHGetKnownFolderPath(guid, _Api.KNOWN_FOLDER_FLAG.KF_FLAG_DONT_VERIFY, default, out string R)) R = null;\n\t\t\treturn new(R);\n\t\t}\n\t\t\n\t\t//Gets virtual known folder ITEMIDLIST from KNOWNFOLDERID specified with 4 uints.\n\t\tstatic Pidl _GetVI(uint a, uint b, uint c, uint d) {\n\t\t\tvar guid = new _Api.KNOWNFOLDERID(a, b, c, d);\n\t\t\tif (0 != _Api.SHGetKnownFolderIDList(guid, _Api.KNOWN_FOLDER_FLAG.KF_FLAG_DONT_VERIFY, default, out IntPtr pidl)) return null;\n\t\t\treturn new Pidl(pidl);\n\t\t}\n\t\t\n\t\t//Gets virtual known folder ITEMIDLIST from KNOWNFOLDERID specified with 4 uints.\n\t\t//Returns string \":: ITEMIDLIST\".\n\t\tstatic FolderPath _GetV(uint a, uint b, uint c, uint d) {\n\t\t\tusing var pidl = _GetVI(a, b, c, d); //never mind: could do it without creating new Pidl\n\t\t\treturn new(pidl?.ToHexString());\n\t\t}\n\t\t\n\t\t#endregion\n\t\t\n\t\t#region API\n\t\t\n\t\tstatic class _Api {\n\t\t\t//GUID that can be inited with 4 uints.\n\t\t\tinternal struct KNOWNFOLDERID {\n\t\t\t\tuint _a; ushort _b, _c; byte _d, _e, _f, _g, _h, _i, _j, _k;\n\t\t\t\t\n\t\t\t\tpublic KNOWNFOLDERID(uint a, uint b, uint c, uint d) {\n\t\t\t\t\t_a = a;\n\t\t\t\t\t_b = (ushort)(b >> 16);\n\t\t\t\t\t_c = (ushort)b;\n\t\t\t\t\t_d = (byte)(c >> 24);\n\t\t\t\t\t_e = (byte)(c >> 16);\n\t\t\t\t\t_f = (byte)(c >> 8);\n\t\t\t\t\t_g = (byte)c;\n\t\t\t\t\t_h = (byte)(d >> 24);\n\t\t\t\t\t_i = (byte)(d >> 16);\n\t\t\t\t\t_j = (byte)(d >> 8);\n\t\t\t\t\t_k = (byte)d;\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t[DllImport(\"shell32.dll\")]\n\t\t\tinternal static extern int SHGetKnownFolderPath(in KNOWNFOLDERID rfid, KNOWN_FOLDER_FLAG dwFlags, IntPtr hToken, out string ppszPath);\n\t\t\t\n\t\t\t[DllImport(\"shell32.dll\")]\n\t\t\tinternal static extern int SHGetKnownFolderIDList(in KNOWNFOLDERID rfid, KNOWN_FOLDER_FLAG dwFlags, IntPtr hToken, out IntPtr ppidl);\n\t\t\t\n\t\t\t[Flags]\n\t\t\tinternal enum KNOWN_FOLDER_FLAG : uint {\n\t\t\t\tKF_FLAG_SIMPLE_IDLIST = 0x00000100,\n\t\t\t\tKF_FLAG_NOT_PARENT_RELATIVE = 0x00000200,\n\t\t\t\tKF_FLAG_DEFAULT_PATH = 0x00000400,\n\t\t\t\tKF_FLAG_INIT = 0x00000800,\n\t\t\t\tKF_FLAG_NO_ALIAS = 0x00001000,\n\t\t\t\tKF_FLAG_DONT_UNEXPAND = 0x00002000,\n\t\t\t\tKF_FLAG_DONT_VERIFY = 0x00004000,\n\t\t\t\tKF_FLAG_CREATE = 0x00008000,\n\t\t\t\tKF_FLAG_NO_APPCONTAINER_REDIRECTION = 0x00010000,\n\t\t\t\tKF_FLAG_ALIAS_ONLY = 0x80000000\n\t\t\t}\n\t\t\t\n\t\t\tinternal enum KF_DEFINITION_FLAGS {\n\t\t\t\tKFDF_LOCAL_REDIRECT_ONLY = 0x2,\n\t\t\t\tKFDF_ROAMABLE = 0x4,\n\t\t\t\tKFDF_PRECREATE = 0x8,\n\t\t\t\tKFDF_STREAM = 0x10,\n\t\t\t\tKFDF_PUBLISHEXPANDEDPATH = 0x20\n\t\t\t}\n\t\t\t\n#pragma warning disable CS0649 //field never assigned\n\t\t\tinternal struct KNOWNFOLDER_DEFINITION {\n\t\t\t\tpublic KF_CATEGORY category;\n\t\t\t\tpublic string pszName;\n\t\t\t\tpublic string pszDescription;\n\t\t\t\tpublic Guid fidParent;\n\t\t\t\tpublic string pszRelativePath;\n\t\t\t\tpublic string pszParsingName;\n\t\t\t\tpublic string pszToolTip;\n\t\t\t\tpublic string pszLocalizedName;\n\t\t\t\tpublic string pszIcon;\n\t\t\t\tpublic string pszSecurity;\n\t\t\t\tpublic uint dwAttributes;\n\t\t\t\tpublic KF_DEFINITION_FLAGS kfdFlags;\n\t\t\t\tpublic Guid ftidType;\n\t\t\t}\n#pragma warning restore CS0649 //field never assigned\n\t\t\t\n\t\t\tinternal enum FFFP_MODE {\n\t\t\t\tFFFP_EXACTMATCH,\n\t\t\t\tFFFP_NEARESTPARENTMATCH\n\t\t\t}\n\t\t\t\n\t\t\t[ComImport, Guid(\"8BE2D872-86AA-4d47-B776-32CCA40C7018\"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\n\t\t\tinternal unsafe interface IKnownFolderManager {\n\t\t\t\t[PreserveSig] int FolderIdFromCsidl(int nCsidl, out Guid pfid);\n\t\t\t\t[PreserveSig] int FolderIdToCsidl(in Guid rfid, out int pnCsidl);\n\t\t\t\t[PreserveSig] int GetFolderIds(out Guid* ppKFId, out int pCount);\n\t\t\t\t[PreserveSig] int GetFolder(in Guid rfid, out IKnownFolder ppkf);\n\t\t\t\t[PreserveSig] int GetFolderByName([In, MarshalAs(UnmanagedType.LPWStr)] string pszCanonicalName, out IKnownFolder ppkf);\n\t\t\t\t//[PreserveSig] int RegisterFolder(in Guid rfid, in KNOWNFOLDER_DEFINITION pKFD);\n\t\t\t\t//[PreserveSig] int UnregisterFolder(in Guid rfid);\n\t\t\t\t//[PreserveSig] int FindFolderFromPath([In, MarshalAs(UnmanagedType.LPWStr)] string pszPath, FFFP_MODE mode, out IKnownFolder ppkf);\n\t\t\t\t//[PreserveSig] int FindFolderFromIDList(IntPtr pidl, out IKnownFolder ppkf);\n\t\t\t\t//[PreserveSig] int Redirect(in Guid rfid, wnd hwnd, uint flags, [In, MarshalAs(UnmanagedType.LPWStr)] string pszTargetPath, uint cFolders, [MarshalAs(UnmanagedType.LPArray)] [In] Guid[] pExclusion, char** ppszError);\n\t\t\t}\n\t\t\t\n\t\t\t[ComImport, Guid(\"4df0c730-df9d-4ae3-9153-aa6b82e9795a\"), ClassInterface(ClassInterfaceType.None)]\n\t\t\tinternal class KnownFolderManager { }\n\t\t\t\n\t\t\tinternal enum KF_CATEGORY {\n\t\t\t\tKF_CATEGORY_VIRTUAL = 1,\n\t\t\t\tKF_CATEGORY_FIXED = 2,\n\t\t\t\tKF_CATEGORY_COMMON = 3,\n\t\t\t\tKF_CATEGORY_PERUSER = 4\n\t\t\t}\n\t\t\t\n\t\t\t[ComImport, Guid(\"3AA7AF7E-9B36-420c-A8E3-F77D4674A488\"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\n\t\t\tinternal unsafe interface IKnownFolder {\n\t\t\t\t[PreserveSig] int GetId(out Guid pkfid);\n\t\t\t\t[PreserveSig] int GetCategory(out KF_CATEGORY pCategory);\n\t\t\t\t[PreserveSig] int GetShellItem(uint dwFlags, in Guid riid, void** ppv);\n\t\t\t\t[PreserveSig] int GetPath(uint dwFlags, [MarshalAs(UnmanagedType.LPWStr)] out string ppszPath); //tested: .NET correctly calls CoTaskMemFree\n\t\t\t\t[PreserveSig] int SetPath(uint dwFlags, [In, MarshalAs(UnmanagedType.LPWStr)] string pszPath);\n\t\t\t\t[PreserveSig] int GetIDList(uint dwFlags, out IntPtr ppidl);\n\t\t\t\t[PreserveSig] int GetFolderType(out Guid pftid);\n\t\t\t\t[PreserveSig] int GetRedirectionCapabilities(out uint pCapabilities);\n\t\t\t\t[PreserveSig] int GetFolderDefinition(out KNOWNFOLDER_DEFINITION pKFD);\n\t\t\t}\n\t\t}\n\t\t\n\t\t#endregion\n\t\t\n\t\t#region public methods\n\t\t\n\t\t/// <summary>\n\t\t/// Gets canonical names and paths of all known folders, including custom known folders registered by applications.\n\t\t/// These names can be used with <see cref=\"getFolder\"/>.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Paths of virtual and unavailable folders are returned as <c><![CDATA[\"<virtual>\"]]></c> and <c><![CDATA[\"<unavailable>\"]]></c>.\n\t\t/// </remarks>\n\t\tpublic static unsafe Dictionary<string, string> getKnownFolders() {\n\t\t\tvar dict = new Dictionary<string, string>();\n\t\t\t\n\t\t\tvar man = new _Api.KnownFolderManager() as _Api.IKnownFolderManager;\n\t\t\tGuid* gp = null;\n\t\t\ttry {\n\t\t\t\tif (man.GetFolderIds(out gp, out int nIds) != 0) return null;\n\t\t\t\tfor (int i = 0; i < nIds; i++) {\n\t\t\t\t\t_Api.IKnownFolder kf = null;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (man.GetFolder(gp[i], out kf) != 0) continue;\n\t\t\t\t\t\tif (kf.GetFolderDefinition(out var fd) != 0) continue;\n\t\t\t\t\t\tstring path = null;\n\t\t\t\t\t\tif (fd.category == _Api.KF_CATEGORY.KF_CATEGORY_VIRTUAL) {\n\t\t\t\t\t\t\tpath = \"<virtual>\";\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tif (kf.GetPath(0, out path) != 0) path = \"<unavailable>\";\n\t\t\t\t\t\t}\n\t\t\t\t\t\tdict.Add(fd.pszName, path);\n\t\t\t\t\t\t//tested: .NET correctly frees struct strings. Don't need FreeKnownFolderDefinitionFields, which is an inline function that calls CoTaskMemFree.\n\t\t\t\t\t}\n\t\t\t\t\tcatch { }\n\t\t\t\t\tfinally { Api.ReleaseComObject(kf); }\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch {\n\t\t\t\tdict = null;\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tMarshal.FreeCoTaskMem((IntPtr)gp);\n\t\t\t\tApi.ReleaseComObject(man);\n\t\t\t}\n\t\t\treturn dict;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets path of a known folder by its name.\n\t\t/// </summary>\n\t\t/// <returns><c>null</c> if unavailable.</returns>\n\t\t/// <param name=\"folderName\">\n\t\t/// Can be:\n\t\t/// <br/>• name of a property of this class, like <c>\"Documents\"</c>, <c>\"Temp\"</c>, <c>\"ThisApp\"</c>. The property must return <see cref=\"FolderPath\"/> or <c>string</c>.\n\t\t/// <br/>• name of a property of the nested class <see cref=\"shell\"/>, like <c>\"shell.ControlPanel\"</c>. Gets <c>\":: ITEMIDLIST\"</c>.\n\t\t/// <br/>• known folder canonical name. See <see cref=\"getKnownFolders\"/>. If has prefix <c>\"shell.\"</c>, gets <c>\":: ITEMIDLIST\"</c>. Much slower, but allows to get paths of folders registered by applications.\n\t\t/// </param>\n\t\t/// <seealso cref=\"pathname.expand\"/>\n\t\tpublic static FolderPath getFolder(string folderName) {\n\t\t\tif (folderName.NE()) return default;\n\t\t\tbool isVirtual = folderName.Starts(\"shell.\");\n\t\t\tif (isVirtual) folderName = folderName[6..];\n\t\t\t\n\t\t\t//properties of this class\n\t\t\tType ty = isVirtual ? typeof(shell) : typeof(folders);\n\t\t\tswitch (ty.GetProperty(folderName)?.GetValue(null)) {\n\t\t\tcase FolderPath fp: return fp;\n\t\t\tcase string fp: return new(fp);\n\t\t\t}\n\t\t\t//Using reflection is not the fastest way, but simplest, cannot make bugs, and don't need maintenance. Fast enough.\n\t\t\t\n\t\t\t//default and custom registered known folders by canonical name\n\t\t\tstring R = null;\n\t\t\t_Api.IKnownFolderManager man = null; _Api.IKnownFolder kf = null;\n\t\t\ttry {\n\t\t\t\tman = (_Api.IKnownFolderManager)new _Api.KnownFolderManager();\n\t\t\t\tif (man.GetFolderByName(folderName, out kf) != 0) return default;\n\t\t\t\tif (isVirtual) {\n\t\t\t\t\tif (0 != kf.GetIDList(0, out IntPtr pidl)) return default;\n\t\t\t\t\tR = Pidl.ToHexString(pidl);\n\t\t\t\t\tMarshal.FreeCoTaskMem(pidl);\n\t\t\t\t} else {\n\t\t\t\t\tif (0 != kf.GetPath(0, out R)) return default;\n\t\t\t\t\tR = pathname.expand(R);\n\t\t\t\t}\n\t\t\t\t//tested: works in MTA apartment too. And all props.\n\t\t\t}\n\t\t\tcatch { }\n\t\t\tfinally {\n\t\t\t\tApi.ReleaseComObject(kf);\n\t\t\t\tApi.ReleaseComObject(man);\n\t\t\t}\n\t\t\t\n\t\t\treturn new(R);\n\t\t\t\n\t\t\t//speed:\n\t\t\t//\tThe get-property code is 2 times slower than calling properties directly.\n\t\t\t//\tThe IKnownFolderManager code is 90 times slower than the get-property code.\n\t\t}\n\t\t\n\t\t#endregion\n\t\t\n\t\t//DON'T: public static class VirtualNAME that returns parsing name, eg \"::{CLSID}\\...\".\n\t\t//\tGood: native/.NET shellexecute supports it.\n\t\t//\tBad: native/.NET shellexecute supports only some. Almost nothing works in a 32-bit process on 64-bit OS; then even cannot convert the string to ITEMIDLIST. Some parsing names have other formats and the API gets wrong parsing names.\n\t\t\n\t\t//DON'T: The + operator returns FolderPath. Then folders.Desktop + subfolder + file would return \"desktop\\subfolder\\file\". Probably not good.\n\t\t//CONSIDER: operator / instead of +. Then could do things like 'folders.x / \"subfolder\" / \"file\"' and 'f /= \"append\"'.\n\t\t\n\t\t/// <summary>\n\t\t/// If string starts with a known/special folder path, gets folder name + relative path and returns <c>true</c>.\n\t\t/// For example if string is <c>\"C:\\Windows\\System32\\notepad.exe\"</c>, gets <c>\"folders.System\"</c> and <c>\"notepad.exe\"</c>.\n\t\t/// </summary>\n\t\t/// <param name=\"path\">Any string. Can be <c>null</c>. Case-insensitive. Supports <c>\":: ITEMIDLIST\"</c> (see <see cref=\"Pidl.ToHexString\"/>).</param>\n\t\t/// <param name=\"folder\">Receives special folder string like <c>\"folders.System\"</c>.</param>\n\t\t/// <param name=\"name\">Receives filename or relative path in the folder.</param>\n\t\t/// <remarks>\n\t\t/// Quite slow first time in process, eg 50 ms, because gets all folder paths. Later uses cached paths.\n\t\t/// </remarks>\n\t\t/// <seealso cref=\"pathname.expand\"/>\n\t\tpublic static bool unexpandPath(string path, out string folder, out string name) {\n\t\t\tvar p = path; folder = name = null;\n\t\t\tif (!p.NE()) {\n\t\t\t\tp = p.Lower();\n\t\t\t\tif (p.Starts(\":: \")) {\n\t\t\t\t\tforeach (var v in _upv.Value) {\n\t\t\t\t\t\tint n = v.path.Length;\n\t\t\t\t\t\tif (p.Starts(v.path)) {\n\t\t\t\t\t\t\tfolder = \"folders.shell.\" + v.name;\n\t\t\t\t\t\t\tname = path[n..];\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tp = p.Replace('/', '\\\\');\n\t\t\t\t\tforeach (var v in _up.Value) {\n\t\t\t\t\t\tint n = v.path.Length;\n\t\t\t\t\t\tif (p.Starts(v.path) && (p.Length == n || p[n] == '\\\\')) {\n\t\t\t\t\t\t\tfolder = \"folders.\" + v.name;\n\t\t\t\t\t\t\tif (p.Length > n) n++;\n\t\t\t\t\t\t\tname = path[n..];\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tif (ScriptEditor.IsPortable && path.Starts(ThisAppDriveBS, true)) {\n\t\t\t\t\t\tfolder = \"folders.ThisAppDriveBS\";\n\t\t\t\t\t\tname = path[ThisAppDriveBS.Length..];\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// If string starts with a known/special folder path, replaces that part with <c>%folders.FolderName%</c>. Else returns unchanged string.\n\t\t/// For example if string is <c>\"C:\\Windows\\System32\\notepad.exe\"</c>, returns <c>\"%folders.System%\\notepad.exe\"</c>.\n\t\t/// </summary>\n\t\t/// <param name=\"path\">Any string. Can be <c>null</c>. Case-insensitive. Supports <c>\":: ITEMIDLIST\"</c> (see <see cref=\"Pidl.ToHexString\"/>).</param>\n\t\tpublic static string unexpandPath(string path) {\n\t\t\tif (unexpandPath(path, out var s1, out var s2)) path = s2.NE() ? $@\"%{s1}%\" : $@\"%{s1}%\\{s2}\";\n\t\t\treturn path;\n\t\t}\n\t\t\n\t\tstatic readonly Lazy<List<(string path, string name)>> _up = new(() => {\n\t\t\tvar a = new List<(string path, string name)>(120); //105\n\t\t\tlock (_lock) {\n\t\t\t\tvar nac = noAutoCreate;\n\t\t\t\tnoAutoCreate = true;\n\t\t\t\tforeach (var pi in typeof(folders).GetProperties()) {\n\t\t\t\t\tif (pi.PropertyType == typeof(FolderPath)\n\t\t\t\t\t\t&& !pi.Name.Starts(\"ThisApp\") //different in processes\n\t\t\t\t\t\t&& pi.GetValue(null) is FolderPath fp) {\n\t\t\t\t\t\tvar s = fp.Path;\n\t\t\t\t\t\tif (!s.NE()) a.Add((s.Lower(), pi.Name));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tnoAutoCreate = nac;\n\t\t\t}\n\t\t\ta.Sort((s1, s2) => {\n\t\t\t\tvar r = s2.path.Length - s1.path.Length; //prefer longer path, eg C:\\A\\B to C:\\A\n\t\t\t\tif (r == 0) { //prefer shorter name, eg System to SystemX64\n\t\t\t\t\tr = string.CompareOrdinal(s1.path, s2.path); //sorting does not work without this\n\t\t\t\t\tif (r == 0 && s1.path == s2.path) r = s1.name.Length - s2.name.Length;\n\t\t\t\t}\n\t\t\t\treturn r;\n\t\t\t});\n\t\t\treturn a;\n\t\t});\n\t\t\n\t\tstatic readonly Lazy<List<(string path, string name)>> _upv = new(() => {\n\t\t\tvar a = new List<(string path, string name)>(30); //22\n\t\t\tforeach (var pi in typeof(folders.shell).GetProperties()) {\n\t\t\t\tif (pi.PropertyType == typeof(FolderPath) && pi.GetValue(null) is FolderPath fp) {\n\t\t\t\t\tvar s = fp.Path;\n\t\t\t\t\t//print.it(pi.Name, s);\n\t\t\t\t\tif (!s.NE()) a.Add((s, pi.Name));\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn a.OrderByDescending(o => o.path.Length).ToList();\n\t\t});\n\t\t\n\t\t/// <summary>\n\t\t/// If <i>path</i> starts with one of specified folder paths, replaces that part with the replacements string. Else returns unchanged string.\n\t\t/// </summary>\n\t\t/// <param name=\"path\">Any string. Can be <c>null</c>. Case-insensitive.</param>\n\t\t/// <param name=\"list\">Folder paths and replacement strings.</param>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// var s = folders.Documents + \"file.txt\";\n\t\t/// //var s = folders.Temp + \"file.txt\";\n\t\t/// print.it(s);\n\t\t/// print.it(folders.unexpandPath(s, (folders.Temp, \"%temp%\"), (folders.Documents, \"%folders.Documents%\")));\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic static string unexpandPath(string path, params (string folder, string replacement)[] list) {\n\t\t\tpath = path.Replace('/', '\\\\');\n\t\t\tforeach (var (f_, repl) in list) {\n\t\t\t\tvar f = f_.Replace('/', '\\\\');\n\t\t\t\tif (path.Starts(f, true)) {\n\t\t\t\t\tint len = f.Length;\n\t\t\t\t\tif (path.Length == len) return repl;\n\t\t\t\t\tif (path[len] is not ('\\\\' or '/') && f[^1] is not ('\\\\' or '/')) continue;\n\t\t\t\t\treturn string.Concat(repl, path.AsSpan(len));\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn path;\n\t\t}\n\t}\n}\n\nnamespace Au.Types {\n\t/// <summary>\n\t/// Most functions of <see cref=\"folders\"/> class return a value of this type.\n\t/// Contains folder path (string) and has operator <c>+</c> to append a string with backslash if need. Has implicit conversions from/to string.\n\t/// </summary>\n\tpublic struct FolderPath {\n\t\treadonly string _path;\n\t\tpublic FolderPath(string path) { _path = path; }\n\t\t\n\t\tpublic static explicit operator FolderPath(string path) => new(path); //not implicit. Example: var s = \"STRING \" + folders.ThisApp; // converts \"STRING \" to FolderPath and result is @\"STRING \\C:\\path\"\n\t\tpublic static implicit operator string(FolderPath f) => f._path;\n\t\t\n\t\t/// <summary>\n\t\t/// Returns <see cref=\"Path\"/>.\n\t\t/// </summary>\n\t\tpublic override string ToString() => _path;\n\t\t\n\t\t/// <summary>\n\t\t/// Returns the path string. For some folders it can be <c>null</c>.\n\t\t/// </summary>\n\t\tpublic string Path => _path;\n\t\t\n\t\t/// <summary>\n\t\t/// Returns the path string. If it is <c>null</c>, throws <see cref=\"InvalidOperationException\"/>.\n\t\t/// </summary>\n\t\t/// <exception cref=\"InvalidOperationException\"></exception>\n\t\tpublic string PathOrThrow => _path ?? throw new InvalidOperationException(\"the special folder path is null\");\n\t\t\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if the path is <c>null</c>.\n\t\t/// </summary>\n\t\tpublic bool IsNull => _path == null;\n\t\t\n\t\t/// <summary>\n\t\t/// Calls <see cref=\"pathname.combine\"/>.\n\t\t/// Example: <c>string s = folders.Desktop + \"file.txt\";</c>\n\t\t/// </summary>\n\t\t/// <exception cref=\"ArgumentException\"><i>fp</i> is empty.</exception>\n\t\tpublic static string operator +(FolderPath fp, string append) {\n\t\t\tif (fp._path.NE()) throw new ArgumentException(\"No folder path.\");\n\t\t\treturn pathname.combine(fp._path, append);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au/Files, data/icon.cs",
    "content": "using System.Drawing;\nusing System.Reflection.Emit;\n\nnamespace Au {\n\t/// <summary>\n\t/// Gets icons from/of files etc. Contains native icon handle.\n\t/// </summary>\n\t/// <remarks>\n\t/// Native icons must be destroyed. An <c>icon</c> variable destroys its native icon when disposing. To dispose, call <see cref=\"Dispose\"/> or use <c>using</c> statement. Or use functions like <see cref=\"ToGdipBitmap\"/>, <see cref=\"ToWpfImage\"/>; by default they dispose the <see cref=\"icon\"/> variable. It's OK to not dispose if you use few icons; GC will do it.\n\t/// </remarks>\n\tpublic class icon : IDisposable { //rejected: base SafeHandle.\n\t\tIntPtr _handle;\n\t\t\n\t\t/// <summary>\n\t\t/// Sets native icon handle.\n\t\t/// The icon will be destroyed when disposing this variable or when converting to object of other type.\n\t\t/// </summary>\n\t\tpublic icon(IntPtr hicon) {\n\t\t\tDebug_.PrintIf(hicon == default, \"hicon == default\");\n\t\t\t\n\t\t\t//Don't allow to exceed the process handle limit when the program does not dispose them. Default limits are 10000, but min 200.\n\t\t\t//Icons are USER objects. They also usually create 3 GDI objects (bitmaps?). So a process can have max ~3300 icons by default.\n\t\t\tif (hicon != default) { GC_.UserHandleCollector.Add(); GC_.GdiHandleCollector.Add(); GC_.GdiHandleCollector.Add(); GC_.GdiHandleCollector.Add(); }\n\t\t\t//rejected: Add property for native icon ownership or methods for refcounting.\n\t\t\t//\tUsually a process uses few icons. If many, the programmer knows the importance of disposing icons; or HandleCollector helps.\n\t\t\t\n\t\t\t_handle = hicon;\n\t\t}\n\t\t\n\t\t//FUTURE: FromGdipIcon, FromStream.\n\t\t\n\t\tstatic icon _New(IntPtr hi) => hi != default ? new(hi) : null;\n\t\t\n\t\t/// <summary>\n\t\t/// Destroys native icon handle.\n\t\t/// </summary>\n\t\tpublic void Dispose() {\n\t\t\tDispose(true);\n\t\t\tGC.SuppressFinalize(this); //never mind: actually this should be in Detach, but then intellisense gives 2 notes\n\t\t}\n\t\t\n\t\t///\n\t\tprotected virtual void Dispose(bool disposing) {\n\t\t\tvar h = Detach();\n\t\t\tif (h != default) Api.DestroyIcon(h);\n\t\t}\n\t\t\n\t\t///\n\t\t~icon() { Dispose(false); }\n\t\t\n\t\t/// <summary>\n\t\t/// Clears this variable and returns its native icon handle.\n\t\t/// </summary>\n\t\tpublic IntPtr Detach() {\n\t\t\tvar h = _handle;\n\t\t\tif (_handle != default) {\n\t\t\t\t_handle = default;\n\t\t\t\tGC_.UserHandleCollector.Remove(); GC_.GdiHandleCollector.Remove(); GC_.GdiHandleCollector.Remove(); GC_.GdiHandleCollector.Remove();\n\t\t\t}\n\t\t\treturn h;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets native icon handle.\n\t\t/// </summary>\n\t\tpublic IntPtr Handle => _handle;\n\t\t\n\t\t/// <summary>\n\t\t/// Gets native icon handle.\n\t\t/// </summary>\n\t\tpublic static implicit operator IntPtr(icon i) => i?._handle ?? default;\n\t\t\n\t\t/// <summary>\n\t\t/// Gets icon that can be displayed for a file, folder, shell object, URL or file type.\n\t\t/// </summary>\n\t\t/// <returns><c>null</c> if failed.</returns>\n\t\t/// <param name=\"file\">\n\t\t/// Can be:\n\t\t/// <br/>• Path of any file or folder. Supports environment variables. If not full path, uses <see cref=\"folders.ThisAppImages\"/> and <see cref=\"filesystem.searchPath\"/>.\n\t\t/// <br/>• Any shell object, like <c>\":: ITEMIDLIST\"</c>, <c>@\"::{CLSID-1}\\::{CLSID-2}\"</c>, <c>@\"shell:AppsFolder\\WinStoreAppId\"</c>.\n\t\t/// <br/>• File type like <c>\".txt\"</c>, or protocol like <c>\"http:\"</c>. Use <c>\".\"</c> to get folder icon.\n\t\t/// <br/>• Path with icon resource index or negative id, like <c>\"c:\\file.dll,4\"</c>, <c>\"c:\\file.exe,-4\"</c>.\n\t\t/// <br/>• URL.\n\t\t/// </param>\n\t\t/// <param name=\"size\">Icon width and height. Default 16.</param>\n\t\t/// <param name=\"flags\"></param>\n\t\t/// <remarks>\n\t\t/// <c>ITEMIDLIST</c> can be of any file, folder, URL or a non-filesystem shell object. See <see cref=\"Pidl.ToHexString\"/>.\n\t\t/// </remarks>\n\t\tpublic static icon of(string file, int size = 16, IconGetFlags flags = 0) {\n\t\t\tusing var ds = new _DebugSpeed(file);\n\t\t\treturn _OfFile(file, _NormalizeIconSizeArgument(size), flags);\n\t\t}\n\t\t\n\t\tstatic icon _OfFile(string file, int size = 16, IconGetFlags flags = 0) {\n\t\t\tif (file.NE()) return null;\n\t\t\tfile = pathname.expand(file, strict: false);\n\t\t\treturn _GetFileIcon(file, size, flags);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets icon of a file or other shell object specified as <c>ITEMIDLIST</c>.\n\t\t/// </summary>\n\t\t/// <returns><c>null</c> if failed.</returns>\n\t\t/// <param name=\"pidl\"><c>ITEMIDLIST</c>.</param>\n\t\t/// <param name=\"size\">Icon width and height. Default 16.</param>\n\t\tpublic static icon ofPidl(Pidl pidl, int size = 16) {\n\t\t\tusing var ds = new _DebugSpeed(pidl);\n\t\t\treturn _OfPidl(pidl, _NormalizeIconSizeArgument(size));\n\t\t}\n\t\t\n\t\tstatic icon _OfPidl(Pidl pidl, int size) {\n\t\t\tif (pidl?.IsNull ?? true) return null;\n\t\t\treturn _GetShellIcon(true, null, pidl, size);\n\t\t}\n\t\t\n\t\tstatic icon _GetFileIcon(string file, int size, IconGetFlags flags) {\n\t\t\tint index = 0;\n\t\t\tbool extractFromFile = false, isFileType = false, isURL = false, isShellPath = false, isPath = true;\n\t\t\t//bool getDefaultIfFails = 0!=(flags&IconGetFlags.DefaultIfFails);\n\t\t\t\n\t\t\tbool searchPath = 0 == (flags & IconGetFlags.DontSearch);\n\t\t\t\n\t\t\tif (0 == (flags & IconGetFlags.LiteralPath)) {\n\t\t\t\t//is \".ext\" or \"protocol:\"?\n\t\t\t\tisFileType = pathname.IsExtension_(file) || (isURL = pathname.IsProtocol_(file));\n\t\t\t\tif (!isFileType) isURL = pathname.isUrl(file);\n\t\t\t\tif (isFileType || isURL || (isShellPath = file[0] == ':')) isPath = false;\n\t\t\t\tif (isPath) {\n\t\t\t\t\t//get icon index from \"path,index\" and remove \",index\"\n\t\t\t\t\textractFromFile = parsePathIndex(file, out file, out index);\n\t\t\t\t\t\n\t\t\t\t\tif (!searchPath) {\n\t\t\t\t\t\tif (!pathname.isFullPath(file)) file = folders.ThisAppImages + file;\n\t\t\t\t\t\tfile = pathname.Normalize_(file, PNFlags.DontPrefixLongPath, noExpandEV: true);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tif (isPath) {\n\t\t\t\tif (searchPath) {\n\t\t\t\t\tfile = filesystem.searchPath(file, folders.ThisAppImages);\n\t\t\t\t\tif (file == null) return null; //ignore getDefaultIfFails\n\t\t\t\t}\n\t\t\t\tfile = pathname.unprefixLongPath(file);\n\t\t\t}\n\t\t\t\n\t\t\tif (isPath /*&& (extractFromFile || 0==(flags&IconGetFlags.Shell))*/) {\n\t\t\t\tint ext = 0;\n\t\t\t\tif (!extractFromFile && file.Length > 4) ext = file.Ends(true, \".ico\", \".exe\", \".scr\"/*, \".cur\", \".ani\"*/);\n\t\t\t\tif (extractFromFile || ext > 0) {\n\t\t\t\t\tvar v = _Load(file, size, index);\n\t\t\t\t\tif (v != null || extractFromFile) return v;\n\t\t\t\t\tswitch (filesystem.exists(file, true)) {\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\treturn null;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\treturn stock(ext is 2 or 3 ? StockIcon.APPLICATION : StockIcon.DOCNOASSOC, size);\n\t\t\t\t\t\t//case 2: //folder name ends with .ico etc\n\t\t\t\t\t}\n\t\t\t\t} else if (file.Ends(\".lnk\", true)) {\n\t\t\t\t\tvar v = _GetLnkIcon(file, size);\n\t\t\t\t\tif (v != null) return v;\n\t\t\t\t\t//print.it(\"_GetLnkIcon failed\", file);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t//note: here we don't cache icons.\n\t\t\t\t//Fast enough for where we use this. OS file buffers remain in memory for some time.\n\t\t\t\t//Where need, should instead use imagelists or some external cache that saves eg full toolbar bitmap.\n\t\t\t\t//SHGetFileInfo has its own cache. In some cases it makes faster (except first time in process), but using it to get all icons is much slower.\n\t\t\t}\n\t\t\t\n\t\t\tbool isExt = isFileType && !isURL;\n\t\t\t\n\t\t\t//Can use this code to avoid slow shell API if possible.\n\t\t\t//In some test cases can make ~2 times faster (with thread pool), especially in MTA thread.\n\t\t\t//But now, after other optimizations applied, in real life makes faster just 10-20%.\n#if false\n\t\t\t//if(0==(flags&IconGetFlags.Shell)){\n\t\t\tstring progId = isShellPath ? null : filesystem.more.getFileTypeOrProtocolRegistryKey(file, isFileType, isURL);\n\n\t\t\tRegistryKey rk = (progId == null) ? null : ARegistry.Open(progId, Registry.ClassesRoot);\n\t\t\t//print.it(file, progId, isFileType, isURL, rk != null);\n\n\t\t\tif(rk == null) {\n\t\t\t\t//Unregistered file type/protocol, no extension, folder, ::{CLSID}, shell:AppsFolder\\WinStoreAppId, or no progId key in HKCR\n\t\t\t\t//print.it(@\"unregistered\", file, progId);\n\t\t\t\tif(progId != null) goto gr; //the file type is known, but no progid key in HKCR. Let shell API figure out. Rare.\n\t\t\t\tif(isExt || (isPath && filesystem.exists(file).File)) return Stock(StockIcon.DOCNOASSOC, size);\n\t\t\t\tgoto gr;\n\t\t\t}\n\n\t\t\t//Registered file type/protocol.\n\t\t\tusing(rk) {\n\t\t\t\tif(ARegistry.KeyExists(@\"ShellEx\\IconHandler\", rk)) {\n\t\t\t\t\t//print.it(@\"handler\", file);\n\t\t\t\t\tgoto gr;\n\t\t\t\t}\n\n\t\t\t\tstring si;\n\t\t\t\tif(ARegistry.GetString(out si, \"\", @\"DefaultIcon\", rk) && si.Length > 0) {\n\t\t\t\t\t//print.it(\"registry: DefaultIcon\", file, si);\n\t\t\t\t\tif(si[0] == '@') si = null; //eg @{Microsoft.Windows.Photos_16.622.13140.0_x64__8wekyb3d8bbwe?ms-resource://Microsoft.Windows.Photos/Files/Assets/PhotosLogoExtensions.png}\n\t\t\t\t\telse ParseIconLocation(ref si, out index);\n\t\t\t\t} else if(ARegistry.GetString(out si, \"\", @\"shell\\open\\command\", rk) && si.Length > 0) {\n\t\t\t\t\t//print.it(@\"registry: shell\\open\\command\", file, si);\n\t\t\t\t\tvar a = si.SegSplit((si[0] == '\"') ? \"\\\"\" : \" \", StringSplitOptions.RemoveEmptyEntries);\n\t\t\t\t\tsi = (a.Length == 0) ? null : a[0];\n\t\t\t\t\tif(si.Ends(\"rundll32.exe\", true)) si = null;\n\t\t\t\t} else {\n\t\t\t\t\tsi = null;\n\t\t\t\t\t//print.it(\"registry: no\", file);\n\t\t\t\t\t//Usually shell API somehow gets si.\n\t\t\t\t\t//For example also looks in .ext > PerceivedType > HKCR\\SystemFileAssociations.\n\t\t\t\t\t//We can use AssocQueryString(ASSOCSTR_DEFAULTICON), but it is slow and not always gets correct si.\n\t\t\t\t}\n\n\t\t\t\t//if(si != null) print.it(file, si);\n\n\t\t\t\tif(si == \"%1\") {\n\t\t\t\t\t//print.it(file);\n\t\t\t\t\tif(isPath) si = file;\n\t\t\t\t\telse si = null;\n\t\t\t\t}\n\n\t\t\t\tif(si != null) {\n\t\t\t\t\tsi = pathname.expand(si);\n\t\t\t\t\tif(!pathname.isFullPath(si)) si = folders.System + si;\n\t\t\t\t\tvat v = _Load(si, size, index);\n\t\t\t\t\tif(v != null) return v;\n\t\t\t\t}\n\t\t\t}\n\t\t\t//}\n\t\t\tgr:\n#endif\n\t\t\treturn _GetShellIcon(!isExt, file, null, size);\n\t\t}\n\t\t\n\t\t//usePidl - if pidl not null/IsNull, use pidl, else convert file to PIDL. If false, pidl must be null.\n\t\tstatic icon _GetShellIcon(bool usePidl, string file, Pidl pidl, int size, bool freePidl = false) {\n\t\t\t//info:\n\t\t\t//\tWe support everything that can have icon - path, URL, protocol (eg \"http:\"), file extension (eg \".txt\"), shell item parsing name (eg \"::{CLSID}\"), \"shell:AppsFolder\\WinStoreAppId\".\n\t\t\t//\tWe call PidlFromString here and pass it to SHGetFileInfo. It makes faster when using thread pool, because multiple threads can call PidlFromString (slow) simultaneously.\n\t\t\t//\tPidlFromString does not support file extension. SHGetFileInfo does not support URL and protocol, unless used PIDL.\n\t\t\t//\tSHGetFileInfo gets the most correct icons, but only of standard sizes, which also depends on DPI and don't know what.\n\t\t\t//\tIExtractIcon for some types fails or gets wrong icon. Even cannot use it to get correct-size icons, because for most file types it uses system imagelists, which are DPI-dependent.\n\t\t\t//\tSHMapPIDLToSystemImageListIndex+SHGetImageList also is not better.\n\t\t\t\n\t\t\t//FUTURE: make faster \"shell:...\". Now eg 100 ms for Settings first time (50 ms Pidl.FromString_ and 50 ms SHGetFileInfo).\n\t\t\t//\tAlternatives (not tested): https://stackoverflow.com/questions/32122679/getting-icon-of-modern-windows-app-from-a-desktop-application\n\t\t\t//\tNot a big problem when using caching.\n\t\t\t//\tAlso some icons have incorrect background, eg Calculator.\n\t\t\t\n\t\t\tvar pidl2 = pidl?.UnsafePtr ?? default;\n\t\t\tif (usePidl) {\n\t\t\t\tif (pidl2 == default) {\n\t\t\t\t\tpidl2 = Pidl.FromString_(file);\n\t\t\t\t\tif (pidl2 == default) usePidl = false; else freePidl = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tif (!usePidl) {\n\t\t\t\tDebug.Assert(pidl2 == default && file != null);\n\t\t\t\tpidl2 = Marshal.StringToCoTaskMemUni(file);\n\t\t\t\tfreePidl = true;\n\t\t\t}\n\t\t\t\n\t\t\tif (pidl2 == default) return null;\n\t\t\t\n\t\t\t//This is faster but fails with some files etc, randomly with others.\n\t\t\t//It means that shell API and/or extensions are not thread-safe, even if can run in MTA.\n\t\t\t//return _GetShellIcon2(pidl2, size, usePidl);\n\t\t\t\n\t\t\ticon R;\n\t\t\ttry {\n\t\t\t\tif (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA) {\n\t\t\t\t\tR = _GetShellIcon2(usePidl, pidl2, size);\n\t\t\t\t} else {\n\t\t\t\t\t//tested: switching thread does not make slower. The speed depends mostly on locking, because then thread pool threads must wait.\n#if true\n\t\t\t\t\tR = Task.Factory.StartNew(() => _GetShellIcon2(usePidl, pidl2, size), default, 0, StaTaskScheduler_.Default).Result_();\n#else //old code, uses ThreadPoolSTA_\n\t\t\t\t\tusing var work = ThreadPoolSTA_.CreateWork(null, o => { R = _GetShellIcon2(usePidl, pidl2, size); });\n\t\t\t\t\twork.Submit();\n\t\t\t\t\twork.Wait();\n#endif\n\t\t\t\t}\n\t\t\t}\n\t\t\tfinally { if (freePidl) Marshal.FreeCoTaskMem(pidl2); }\n\t\t\tGC.KeepAlive(pidl);\n\t\t\treturn R;\n\t\t}\n\t\t\n\t\tstatic icon _GetShellIcon2(bool isPidl, IntPtr pidl, int size) {\n\t\t\tIntPtr il = default; int index = -1, ilIndex, realSize;\n\t\t\t\n\t\t\tif (size < (realSize = 16) * 5 / 4) ilIndex = Api.SHIL_SMALL;\n\t\t\telse if (size < (realSize = 32) * 5 / 4) ilIndex = Api.SHIL_LARGE;\n\t\t\telse if (size < 256) {\n\t\t\t\tilIndex = Api.SHIL_EXTRALARGE; realSize = 48;\n\t\t\t\t//info: cannot resize from 256 because GetIcon(SHIL_JUMBO) gives 48 icon if 256 icon unavailable. Getting real icon size is either impossible or quite difficult and slow (not tested).\n\t\t\t} else { ilIndex = Api.SHIL_JUMBO; realSize = 256; }\n\t\t\t\n\t\t\tusing var dac = new Dpi.AwarenessContext(-1); //unaware\n\t\t\t\n\t\t\t//Need to lock this part, or randomly fails with some file types.\n\t\t\tlock (\"TK6Z4XiSxkGSfC14/or5Mw\") {\n\t\t\t\ttry {\n\t\t\t\t\tuint fl = Api.SHGFI_SYSICONINDEX | Api.SHGFI_SHELLICONSIZE;\n\t\t\t\t\tif (ilIndex == Api.SHIL_SMALL) fl |= Api.SHGFI_SMALLICON;\n\t\t\t\t\tif (isPidl) fl |= Api.SHGFI_PIDL; else fl |= Api.SHGFI_USEFILEATTRIBUTES;\n\t\t\t\t\til = Api.SHGetFileInfo(pidl, out var x, fl);\n\t\t\t\t\tif (il != default) index = x.iIcon;\n\t\t\t\t\t//Marshal.Release(il); //undocumented, but without it IImageList refcount grows. Probably it's ok, because it is static, never deleted until process exits.\n\t\t\t\t}\n\t\t\t\tcatch { Debug_.Print(\"exception\"); }\n\t\t\t\t//Shell extensions may throw.\n\t\t\t\t//By default .NET does not allow to handle eg access violation exceptions.\n\t\t\t\t//\tPreviously we would add [HandleProcessCorruptedStateExceptions], but Core ignores it.\n\t\t\t\t//\tNow our AppHost sets environment variable COMPlus_legacyCorruptedStateExceptionsPolicy=1 before loading runtime.\n\t\t\t\t//\tOr could move the API call to the C++ dll.\n\t\t\t}\n\t\t\tif (index < 0) return null;\n\t\t\t\n\t\t\t//note: Getting icon from imagelist must be in STA thread too, else fails with some file types.\n\t\t\t//tested: This part works without locking. Using another lock here makes slower.\n\t\t\t\n\t\t\ttry {\n\t\t\t\tif (ilIndex == Api.SHIL_SMALL || ilIndex == Api.SHIL_LARGE || _GetShellImageList(ilIndex, out il)) {\n\t\t\t\t\t//print.it(il, Debug_.GetComObjRefCount(il));\n\t\t\t\t\tvar hi = Api.ImageList_GetIcon(il, index, 0);\n\t\t\t\t\tif (hi != default) {\n\t\t\t\t\t\tif (size != realSize) {\n\t\t\t\t\t\t\t//print.it(size, realSize, index, file);\n\t\t\t\t\t\t\thi = Api.CopyImage(hi, Api.IMAGE_ICON, size, size, Api.LR_COPYDELETEORG | Api.LR_COPYRETURNORG);\n\t\t\t\t\t\t\t//never mind if fails, it's too rare.\n\t\t\t\t\t\t\t//TODO3: test LR_COPYFROMRESOURCE.\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn _New(hi);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception e) { Debug_.Print(e); }\n\t\t\t//finally { if(il != default) Marshal.Release(il); }\n\t\t\treturn null;\n\t\t\t\n\t\t\tstatic bool _GetShellImageList(int ilIndex, out IntPtr R) {\n\t\t\t\tlock (\"vK6Z4XiSxkGSfC14/or5Mw\") { //the API fails if called simultaneously by multiple threads\n\t\t\t\t\tif (0 == Api.SHGetImageList(ilIndex, Api.IID_IImageList, out R)) return true;\n\t\t\t\t}\n\t\t\t\tDebug.Assert(false);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\t\n\t\t//Gets shortcut (.lnk) icon.\n\t\t//Much faster than other shell API.\n\t\t//Also gets correct icon where iextracticon fails and/or shgetfileinfo gets blank document icon, don't know why.\n\t\t//Usually fails only when target does not exist. Then iextracticon also fails, and shgetfileinfo gets blank document icon.\n\t\t//If fails, returns null. No exceptions.\n\t\tstatic icon _GetLnkIcon(string file, int size) {\n\t\t\ttry {\n\t\t\t\tusing var x = shortcutFile.open(file);\n\t\t\t\tvar s = x.GetIconLocation(out int ii); if (s != null) return _Load(s, size, ii);\n\t\t\t\ts = x.TargetPathRawMSI; if (s != null) return _OfFile(s, size, IconGetFlags.DontSearch);\n\t\t\t\t//print.it(\"need IDList\", file);\n\t\t\t\tusing (var pidl = x.TargetPidl) return _OfPidl(pidl, size);\n\t\t\t}\n\t\t\tcatch { return null; }\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Extracts icon directly from file.\n\t\t/// </summary>\n\t\t/// <returns><c>null</c> if failed.</returns>\n\t\t/// <param name=\"file\"><c>.ico</c>, <c>.exe</c>, <c>.dll</c> or other file that contains one or more icons. Also supports cursor files - <c>.cur</c>, <c>.ani</c>. Must be full path, without icon index. Supports environment variables (see <see cref=\"pathname.expand\"/>).</param>\n\t\t/// <param name=\"size\">Icon width and height. Default 16.</param>\n\t\t/// <param name=\"index\">Icon index or negative icon resource id in the <c>.exe</c>/<c>.dll</c> file.</param>\n\t\tpublic static icon load(string file, int size = 16, int index = 0) {\n\t\t\tusing var ds = new _DebugSpeed(file);\n\t\t\treturn _Load(file, _NormalizeIconSizeArgument(size), index);\n\t\t}\n\t\t\n\t\tstatic unsafe icon _Load(string file, int size, int index) {\n\t\t\t//We use SHDefExtractIcon because of better quality resizing (although several times slower) which matches shell and imagelist resizing.\n\t\t\t//With .ico it matches LoadImage speed (without resizing). PrivateExtractIcons is slightly slower.\n\t\t\t\n\t\t\tif (file.NE()) return null;\n\t\t\tIntPtr hi = default;\n\t\t\tint hr = Api.SHDefExtractIcon(file, index, 0, &hi, null, size);\n\t\t\treturn hr == 0 ? _New(hi) : null;\n\t\t\t\n\t\t\t//if(Api.PrivateExtractIcons(file, index, size, size, out R, default, 1, 0) != 1) return null;\n\t\t\t\n\t\t\t//SHOULDOO: test LoadIconWithScaleDown. If .ico or negative index. But maybe SHDefExtractIcon uses it, that is why better quality.\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets a shell stock icon.\n\t\t/// </summary>\n\t\t/// <returns><c>null</c> if failed.</returns>\n\t\t/// <param name=\"id\">Shell stock icon id.</param>\n\t\t/// <param name=\"size\">Icon width and height. Default 16.</param>\n\t\tpublic static unsafe icon stock(StockIcon id, int size = 16) {\n\t\t\tif (!GetStockIconLocation_(id, out var path, out int index)) return null;\n\t\t\treturn load(path, size, index);\n\t\t\t//note: don't cache, because of the quota of handles a process can have. Maybe only exe and document icons; maybe also folder and open folder.\n\t\t\t\n\t\t\t//tested: always gets 32x32 icon: Api.LoadImage(default, 32516, Api.IMAGE_ICON, 16, 16, Api.LR_SHARED); //OIC_INFORMATION\n\t\t}\n\t\t\n\t\tinternal static unsafe bool GetStockIconLocation_(StockIcon id, out string path, out int index) {\n\t\t\tvar x = new Api.SHSTOCKICONINFO(); x.cbSize = Api.SizeOf(x);\n\t\t\tif (0 == Api.SHGetStockIconInfo(id, 0, ref x)) {\n\t\t\t\tpath = new string(x.szPath);\n\t\t\t\tindex = x.iIcon;\n\t\t\t\treturn true;\n\t\t\t} else {\n\t\t\t\tpath = null;\n\t\t\t\tindex = 0;\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets icon from unmanaged resources of this program.\n\t\t/// </summary>\n\t\t/// <returns><c>null</c> if not found.</returns>\n\t\t/// <param name=\"size\">Icon width and height. Default 16.</param>\n\t\t/// <param name=\"resourceId\">Native resource id. Default <ms>IDI_APPLICATION</ms> (C# compilers add app icon with this id).</param>\n\t\t/// <remarks>\n\t\t/// If role <c>miniProgram</c> (default), at first looks in main assembly (<c>.dll</c>); if not found there, looks in <c>.exe</c> file. Else only in <c>.exe</c> file.\n\t\t/// \n\t\t/// The icon is cached and protected from destroying. Don't need to destroy it, and not error to do it.\n\t\t/// </remarks>\n\t\tpublic static icon ofThisApp(int size = 16, int resourceId = Api.IDI_APPLICATION) {\n\t\t\tvar hm = GetAppIconModuleHandle_(resourceId);\n\t\t\treturn FromModuleHandle_(hm, size, resourceId);\n\t\t\t\n\t\t\t//This is not 100% reliable because the icon id 32512 (IDI_APPLICATION) is undocumented.\n\t\t\t//I could not find a .NET method to get icon directly from native resources of assembly.\n\t\t\t//Could use the resource emumeration API...\n\t\t\t//info: MSDN lies that with LR_SHARED gets a cached icon regardless of size argument. Caches each size separately. Tested on Win 10, 7, XP.\n\t\t\t\n\t\t\t//TEST: LoadIconWithScaleDown.\n\t\t}\n\t\t\n\t\tinternal static icon FromModuleHandle_(IntPtr hm, int size = 16, int resourceId = Api.IDI_APPLICATION) {\n\t\t\tif (hm == default) return null;\n\t\t\tsize = _NormalizeIconSizeArgument(size);\n\t\t\treturn _New(Api.LoadImage(hm, resourceId, Api.IMAGE_ICON, size, size, Api.LR_SHARED));\n\t\t}\n\t\t\n\t\t\n\t\t/// <summary>\n\t\t/// Gets icon of tray icon size from unmanaged resources of this program or system.\n\t\t/// </summary>\n\t\t/// <param name=\"resourceId\">Native resource id. Default <ms>IDI_APPLICATION</ms> (C# compilers add app icon with this id).</param>\n\t\t/// <remarks>\n\t\t/// Calls API <ms>LoadIconMetric</ms>.\n\t\t/// \n\t\t/// The icon can be in main assembly (if role <c>miniProgram</c>) or in the program file (<c>.exe</c>). If not found, loads standard icon, see API <ms>LoadIconMetric</ms>.\n\t\t/// </remarks>\n\t\tpublic static icon trayIcon(int resourceId = Api.IDI_APPLICATION/*, bool big = false*/) {\n#if true\n\t\t\tIntPtr hi = default; int hr = 1;\n\t\t\tif (script.role == SRole.MiniProgram) hr = Api.LoadIconMetric(Api.GetModuleHandle(AssemblyUtil_.GetEntryAssembly().Location), resourceId, 0, out hi);\n\t\t\tif (hr != 0) hr = Api.LoadIconMetric(Api.GetModuleHandle(null), resourceId, 0, out hi);\n\t\t\tif (hr != 0) hr = Api.LoadIconMetric(default, resourceId, 0, out hi);\n\t\t\treturn hr == 0 ? _New(hi) : null;\n#else //10% slower\n\t\t\tvar h = GetAppIconModuleHandle_();\n\t\t\treturn 0 == Api.LoadIconMetric(h, resourceId, /*big ? 1 :*/ 0, out var hi) ? new(hi) : null;\n#endif\n\t\t\t//can load big icon too, but not very useful.\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Loads icon of tray icon size from <c>.ico</c> file.\n\t\t/// </summary>\n\t\t/// <returns><c>null</c> if not found.</returns>\n\t\t/// <remarks>\n\t\t/// Calls API <ms>LoadIconMetric</ms>.\n\t\t/// </remarks>\n\t\tpublic static unsafe icon trayIcon(string icoFile) {\n\t\t\tfixed (char* p = icoFile) return 0 == Api.LoadIconMetric(default, (nint)p, 0, out var hi) ? _New(hi) : null;\n\t\t\t//LoadIconMetric bug: does not load large icon from ico file.\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets native module handle of exe or dll that contains specified icon. Returns default if no icon.\n\t\t/// If role <c>miniProgram</c>, at first looks in main assembly (<c>.dll</c>).\n\t\t/// </summary>\n\t\tinternal static IntPtr GetAppIconModuleHandle_(int resourceId) {\n\t\t\tif (script.role == SRole.MiniProgram) {\n\t\t\t\tvar h1 = Api.GetModuleHandle(AssemblyUtil_.GetEntryAssembly().Location);\n\t\t\t\tif (default != Api.FindResource(h1, resourceId, Api.RT_GROUP_ICON)) return h1;\n\t\t\t}\n\t\t\tvar h2 = Api.GetModuleHandle(null);\n\t\t\tif (default != Api.FindResource(h2, resourceId, Api.RT_GROUP_ICON)) return h2;\n\t\t\treturn default;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets icon that is displayed in window title bar and in taskbar button.\n\t\t/// </summary>\n\t\t/// <returns><c>null</c> if failed.</returns>\n\t\t/// <param name=\"w\">A top-level window of any process.</param>\n\t\t/// <param name=\"size\">Icon width and height. Default 16.</param>\n\t\tpublic static icon ofWindow(wnd w, int size = 16) {\n\t\t\t//int size = Api.GetSystemMetrics(big ? Api.SM_CXICON : Api.SM_CXSMICON);\n\t\t\t\n\t\t\t//support Windows Store apps\n\t\t\tvar appId = WndUtil.GetWindowsStoreAppId(w, prependShellAppsFolder: true);\n\t\t\tif (appId != null) {\n\t\t\t\tvar v = of(appId, size, IconGetFlags.DontSearch);\n\t\t\t\tif (v != null) return v;\n\t\t\t}\n\t\t\t\n\t\t\tbool big = size >= 24; //TODO3: make high-DPI-aware. How?\n\t\t\tbool ok = w.SendTimeout(2000, out nint R, Api.WM_GETICON, big ? 1 : 0);\n\t\t\tif (R == 0 && ok) w.SendTimeout(2000, out R, Api.WM_GETICON, big ? 0 : 1);\n\t\t\tif (R == 0) R = WndUtil.GetClassLong(w, big ? GCL.HICON : GCL.HICONSM);\n\t\t\tif (R == 0) R = WndUtil.GetClassLong(w, big ? GCL.HICONSM : GCL.HICON);\n\t\t\t//tested this code with DPI 125%. Small icon of most windows match DPI (20), some 16, some 24.\n\t\t\t//tested: undocumented API InternalGetWindowIcon does not get icon of winstore app.\n\t\t\t\n\t\t\t//Copy, because will DestroyIcon, also it resizes if need.\n\t\t\tif (R != 0) R = Api.CopyImage(R, Api.IMAGE_ICON, size, size, 0);\n\t\t\treturn _New(R);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Sends <ms>WM_SETICON</ms> message.\n\t\t/// </summary>\n\t\t/// <param name=\"w\"></param>\n\t\t/// <param name=\"big\"><c>ICON_BIG</c>.</param>\n\t\tpublic void SetWindowIcon(wnd w, bool big) {\n\t\t\tw.Send(Api.WM_SETICON, big ? 1 : 0, _handle);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Creates icon at run time.\n\t\t/// </summary>\n\t\t/// <param name=\"width\"></param>\n\t\t/// <param name=\"height\"></param>\n\t\t/// <param name=\"drawCallback\">Called to draw icon. If <c>null</c>, the icon will be completely transparent.</param>\n\t\tpublic static icon create(int width, int height, Action<Graphics> drawCallback = null) {\n\t\t\tIntPtr hi;\n\t\t\tif (drawCallback != null) {\n\t\t\t\tusing var b = new Bitmap(width, height);\n\t\t\t\tusing var g = Graphics.FromImage(b);\n\t\t\t\tg.Clear(Color.Transparent); //optional, new bitmaps are transparent, but it is undocumented, and eg .NET Bitmap.MakeTransparent does it\n\t\t\t\tdrawCallback(g);\n\t\t\t\thi = b.GetHicon();\n\t\t\t} else {\n\t\t\t\tint nb = Math2.AlignUp(width, 32) / 8 * height;\n\t\t\t\tvar aAnd = new byte[nb]; for (int i = 0; i < nb; i++) aAnd[i] = 0xff;\n\t\t\t\tvar aXor = new byte[nb];\n\t\t\t\thi = Api.CreateIcon(default, width, height, 1, 1, aAnd, aXor);\n\t\t\t\t\n\t\t\t\t//speed: ~20 mcs. About 10 times faster than above. Faster than CopyImage etc.\n\t\t\t}\n\t\t\treturn _New(hi);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Creates <see cref=\"System.Drawing.Icon\"/> object that shares native icon handle with this object.\n\t\t/// </summary>\n\t\t/// <returns><c>null</c> if <see cref=\"Handle\"/> is <c>default(IntPtr)</c>.</returns>\n\t\tpublic Icon ToGdipIcon() {\n\t\t\tif (_handle == default) return null;\n\t\t\tvar R = Icon.FromHandle(_handle);\n\t\t\ts_cwt.Add(R, this);\n\t\t\treturn R;\n\t\t}\n\t\tstatic readonly ConditionalWeakTable<Icon, icon> s_cwt = new();\n\t\t\n\t\t/// <summary>\n\t\t/// Converts native icon to GDI+ bitmap object.\n\t\t/// </summary>\n\t\t/// <returns><c>null</c> if <see cref=\"Handle\"/> is <c>default(IntPtr)</c> or if fails to convert.</returns>\n\t\t/// <param name=\"destroyIcon\">\n\t\t/// If <c>true</c> (default), destroys the native icon object; also clears this variable and don't need to dispose it.\n\t\t/// If <c>false</c>, later will need to dispose this variable.\n\t\t/// </param>\n\t\tpublic Bitmap ToGdipBitmap(bool destroyIcon = true) {\n\t\t\tif (_handle != default) {\n\t\t\t\tusing var ic = Icon.FromHandle(_handle);\n\t\t\t\ttry { return ic.ToBitmap(); }\n\t\t\t\tcatch (Exception e) { print.warning(\"ToGdipBitmap() failed. \" + e.ToString(), -1); }\n\t\t\t\tfinally { if (destroyIcon) Dispose(); }\n\t\t\t}\n\t\t\treturn null;\n\t\t\t//note: don't use Bitmap.FromHicon. It just calls GdipCreateBitmapFromHICON which does not support alpha etc.\n\t\t\t//FUTURE: look for a faster way.\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Converts native icon to WPF image object.\n\t\t/// </summary>\n\t\t/// <returns><c>null</c> if <see cref=\"Handle\"/> is <c>default(IntPtr)</c> or if fails to convert.</returns>\n\t\t/// <param name=\"destroyIcon\">\n\t\t/// If <c>true</c> (default), destroys the native icon object; also clears this variable and don't need to dispose it.\n\t\t/// If <c>false</c>, later will need to dispose this variable.\n\t\t/// </param>\n\t\t/// <remarks>\n\t\t/// The image is not suitable for WPF window icon. Instead use <see cref=\"SetWindowIcon\"/> or WPF image loading functions.\n\t\t/// </remarks>\n\t\tpublic System.Windows.Media.Imaging.BitmapSource ToWpfImage(bool destroyIcon = true) {\n\t\t\tif (_handle != default) {\n\t\t\t\ttry { return System.Windows.Interop.Imaging.CreateBitmapSourceFromHIcon(_handle, default, default); }\n\t\t\t\tcatch (Exception e) { print.warning(\"ToWpfImage() failed. \" + e.ToString(), -1); }\n\t\t\t\tfinally { if (destroyIcon) Dispose(); }\n\t\t\t}\n\t\t\treturn null;\n\t\t\t\n\t\t\t//why not suitable for WPF window icon:\n\t\t\t//1. Shadows in alpha area. As a workaround could get icon bits and call BitmapFrame.Create(), but\n\t\t\t//2. For window need 2 icons - small and big.\n\t\t\t//See script \"HICON to ImageSource\".\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets icon size.\n\t\t/// </summary>\n\t\t/// <returns><c>default(SIZE)</c> if failed.</returns>\n\t\tpublic unsafe SIZE Size {\n\t\t\tget {\n\t\t\t\tif (_handle != default) {\n\t\t\t\t\tusing Api.ICONINFO ii = new(_handle);\n\t\t\t\t\tApi.BITMAP b = default;\n\t\t\t\t\tbool hasColors = ii.hbmColor != default;\n\t\t\t\t\tApi.GetObject(hasColors ? ii.hbmColor : ii.hbmMask, sizeof(Api.BITMAP), &b);\n\t\t\t\t\treturn new(b.bmWidth, hasColors ? b.bmHeight : b.bmHeight / 2);\n\t\t\t\t}\n\t\t\t\treturn default;\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Parses icon location string.\n\t\t/// </summary>\n\t\t/// <returns><c>true</c> if it includes icon index or resource id.</returns>\n\t\t/// <param name=\"s\">\n\t\t/// Icon location. Can be <c>\"path,index\"</c> or <c>\"path,-id\"</c> or just path.\n\t\t/// Supports path enclosed in <c>\"\"</c> like <c>\"\\\"path\\\",index\"</c>, and spaces between comma and index like <c>\"path, index\"</c>.\n\t\t/// </param>\n\t\t/// <param name=\"path\">Receives path without index and without <c>\"\"</c>. Can be the same variable as <i>s</i>.</param>\n\t\t/// <param name=\"index\">Receives index/id or 0.</param>\n\t\tpublic static bool parsePathIndex(string s, out string path, out int index) {\n\t\t\tpath = s; index = 0;\n\t\t\tif (s.NE()) return false;\n\t\t\tif (s[0] == '\"') path = s = s.Replace(\"\\\"\", \"\"); //can be eg \"path\",index\n\t\t\tif (s.Length < 3) return false;\n\t\t\tif (!s[^1].IsAsciiDigit()) return false;\n\t\t\tint i = s.LastIndexOf(','); if (i < 1) return false;\n\t\t\tindex = s.ToInt(i + 1, out int e); if (e != s.Length) return false;\n\t\t\tpath = s[..i];\n\t\t\treturn true;\n\t\t\t\n\t\t\t//note: API PathParseIconLocation has bugs. Eg splits \"path,5moreText\". Or from \"path, 5\" removes \", 5\" and returns 0.\n\t\t}\n\t\t\n\t\tstatic int _NormalizeIconSizeArgument(int size) {\n\t\t\tif (size == 0) return 16;\n\t\t\treturn (uint)size <= 256 ? size : throw new ArgumentOutOfRangeException(\"size\", \"Must be 0 - 256\");\n\t\t}\n\t\t\n\t\t//rejected. Not per-monitor-DPI-aware, etc. Better use 16 etc and then auto-scale.\n\t\t///// <summary>\n\t\t///// Gets size of small icons displayed in UI.\n\t\t///// Depends on DPI; 16 when DPI 100%.\n\t\t///// </summary>\n\t\t//public static int sizeSmall => Dpi.OfThisProcess / 6; //eg 96/6=16\n\t\t\n\t\t///// <summary>\n\t\t///// Gets size of large icons displayed in UI.\n\t\t///// Depends on DPI; 32 when DPI 100%.\n\t\t///// </summary>\n\t\t//public static int sizeLarge => Dpi.OfThisProcess / 3;\n\t\t\n\t\t//static int _SizeExtraLarge => Dpi.OfThisProcess / 2;\n\t\t\n\t\t//tested: shell imagelist icon sizes match these.\n\t\t//note: don't use GetSystemMetrics(SM_CXSMICON/SM_CXICON). They are for other purposes, eg window title bar, tray icon. On Win7 they can be different because can be changed in Control Panel. Used by SystemInformation.SmallIconSize etc.\n\t\t\n\t\t/// <summary>\n\t\t/// If not 0, \"get icon\" functions of this class will print (in editor's output) their execution time in milliseconds when it >= this value.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Icons are mostly used in toolbars and menus. Getting icons of some files can be slow. For example if antivirus program scans the file. Toolbars and menus that use slow icons may start with a noticeable delay. Use this property to find too slow icons. Then you can replace them with fast icons, for example <c>.ico</c> files.\n\t\t/// </remarks>\n\t\tpublic static int debugSpeed { get; set; }\n\t\t\n\t\tstruct _DebugSpeed : IDisposable {\n\t\t\tlong _time;\n\t\t\tobject _file; //string or Pidl\n\t\t\t\n\t\t\tpublic _DebugSpeed(object file) {\n\t\t\t\tif (debugSpeed > 0) {\n\t\t\t\t\t_file = file;\n\t\t\t\t\t_time = perf.ms;\n\t\t\t\t} else {\n\t\t\t\t\t_time = 0;\n\t\t\t\t\t_file = null;\n\t\t\t\t}\n\t\t\t\t//TODO3: implement global icon cache here. File-based.\n\t\t\t}\n\t\t\t\n\t\t\tpublic void Dispose() {\n\t\t\t\tif (_time != 0) {\n\t\t\t\t\tlong t = perf.ms - _time;\n\t\t\t\t\tif (t >= debugSpeed) {\n\t\t\t\t\t\t//if (_file is Pidl p) _file = p.ToShellString(SIGDN.NORMALDISPLAY);\n\t\t\t\t\t\tprint.it($\"icon.debugSpeed: {t} ms, {_file}\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets icon path from code that contains string like <c>@\"c:\\windows\\system32\\notepad.exe\"</c> or <c>@\"%folders.System%\\notepad.exe\"</c> or URL/shell.\n\t\t/// Also supports code patterns like <c>folders.System + \"notepad.exe\"</c> or <c>folders.shell.RecycleBin</c>.\n\t\t/// </summary>\n\t\t/// <returns><c>null</c> if no such string/pattern.</returns>\n\t\t/// <param name=\"mi\"></param>\n\t\t/// <param name=\"cs\">The string is <c>.cs</c> filename or relative path, but not full path.</param>\n\t\tinternal static string ExtractIconPathFromCode_(MethodInfo mi, out bool cs) {\n\t\t\t//support code pattern like 'folders.System + \"notepad.exe\"'.\n\t\t\t//\tOpcodes: call(folders.System), ldstr(\"notepad.exe\"), FolderPath.op_Addition.\n\t\t\t//also code pattern like 'folders.System' or 'folders.shell.RecycleBin'.\n\t\t\t//\tOpcodes: call(folders.System), FolderPath.op_Implicit(FolderPath to string).\n\t\t\t//also code pattern like 'run.itSafe(\"notepad.exe\")'.\n\t\t\t//print.it(mi.Name);\n\t\t\t\n\t\t\tcs = false;\n\t\t\tvar il = mi.GetMethodBody().GetILAsByteArray();\n\t\t\tif (il.Length > 100) return null;\n\t\t\t\n\t\t\tint i = 0, patternStart = -1; MethodInfo f1 = null; string filename = null, filename2 = null;\n\t\t\ttry {\n\t\t\t\tvar reader = new ILReader(mi, il);\n\t\t\t\tforeach (var instruction in reader.Instructions) {\n\t\t\t\t\tif (++i > 100) break;\n\t\t\t\t\tvar op = instruction.Op;\n\t\t\t\t\t//print.it(op);\n\t\t\t\t\tif (op == OpCodes.Nop) {\n\t\t\t\t\t\ti--;\n\t\t\t\t\t} else if (op == OpCodes.Ldstr) {\n\t\t\t\t\t\tvar s = instruction.Data as string;\n\t\t\t\t\t\t//print.it(s);\n\t\t\t\t\t\t//print.it(i, patternStart);\n\t\t\t\t\t\tif (i == patternStart + 1) filename = s;\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tif (pathname.isFullPathExpand(ref s)) return s; //eg run.it(@\"%folders.System%\\notepad.exe\");\n\t\t\t\t\t\t\tif (pathname.IsShellPathOrUrl_(s)) return s;\n\t\t\t\t\t\t\tfilename = null; patternStart = -1;\n\t\t\t\t\t\t\tif (i == 1) filename2 = s;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (op == OpCodes.Call && instruction.Data is MethodInfo f && f.IsStatic) {\n\t\t\t\t\t\t//print.it(f, f.DeclaringType, f.Name, f.MemberType, f.ReturnType, f.GetParameters().Length);\n\t\t\t\t\t\tvar dt = f.DeclaringType;\n\t\t\t\t\t\tif (dt == typeof(folders) || dt == typeof(folders.shell)) {\n\t\t\t\t\t\t\tif (f.ReturnType == typeof(FolderPath) && f.GetParameters().Length == 0) {\n\t\t\t\t\t\t\t\t//print.it(1);\n\t\t\t\t\t\t\t\tf1 = f;\n\t\t\t\t\t\t\t\tpatternStart = i;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else if (dt == typeof(FolderPath)) {\n\t\t\t\t\t\t\tif (i == patternStart + 2 && f.Name == \"op_Addition\") {\n\t\t\t\t\t\t\t\t//print.it(2);\n\t\t\t\t\t\t\t\tvar fp = (FolderPath)f1.Invoke(null, null);\n\t\t\t\t\t\t\t\tif (fp.Path == null) return null;\n\t\t\t\t\t\t\t\treturn fp + filename;\n\t\t\t\t\t\t\t} else if (i == patternStart + 1 && f.Name == \"op_Implicit\" && f.ReturnType == typeof(string)) {\n\t\t\t\t\t\t\t\t//print.it(3);\n\t\t\t\t\t\t\t\treturn (FolderPath)f1.Invoke(null, null);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t//} else if (dt == typeof(script)) {\n\t\t\t\t\t\t\t//\tprint.it(filename);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (filename2 != null) {\n\t\t\t\t\tif (filename2.Ends(\".exe\", true)) return filesystem.searchPath(filename2);\n\t\t\t\t\tif (cs = filename2.Ends(\".cs\", true) && !pathname.isFullPath(filename2, orEnvVar: true)) return filename2;\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception ex) { Debug_.Print(ex); }\n\t\t\treturn null;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets image of a Windows Store App.\n\t\t/// </summary>\n\t\t/// <returns><c>Bitmap</c> object, or <c>null</c> if failed. Its size may be != <i>size</i>; let the caller scale it when drawing.</returns>\n\t\t/// <param name=\"shellString\">String like <c>@\"shell:AppsFolder\\Microsoft.WindowsCalculator_8wekyb3d8bbwe!App\"</c>.</param>\n\t\t/// <param name=\"size\">Desired width and height.</param>\n\t\tpublic static Bitmap winStoreAppImage(string shellString, int size = 16) {\n\t\t\tusing var idl = Pidl.FromString(shellString); if (idl == null) return null; //the slowest part, > 90%\n\t\t\treturn winStoreAppImage(idl, size);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets image of a Windows Store App. This overload accepts a <see cref=\"Pidl\"/> instead of a shell string.\n\t\t/// </summary>\n\t\t/// <returns><c>Bitmap</c> object, or <c>null</c> if failed. Its size may be != <i>size</i>; let the caller scale it when drawing.</returns>\n\t\tpublic static Bitmap winStoreAppImage(Pidl pidl, int size = 16) {\n\t\t\tvar path = _GetWinStoreAppImagePath(pidl, size); if (path == null) return null;\n\t\t\t//var r = Image.FromFile(path) as Bitmap; //no, locks file\n\t\t\tusing var stream = File.OpenRead(path);\n\t\t\tvar r = Image.FromStream(stream) as Bitmap;\n\t\t\tr?.SetResolution(96, 96);\n\t\t\treturn r;\n\t\t}\n\t\t\n\t\tstatic string _GetWinStoreAppImagePath(Pidl pidl, int size = 16) {\n\t\t\tif (0 != Api.SHBindToParent(pidl.UnsafePtr, typeof(Api.IShellFolder).GUID, out var isf, out var idItem)) return null;\n\t\t\tif (!isf.GetUIObjectOf(idItem, out Api.IExtractIcon extract)) return null;\n\t\t\tvar sb = new StringBuilder(1000);\n\t\t\tif (0 != extract.GetIconLocation(0, sb, sb.Capacity, out int index, out uint rflags)) return null;\n\t\t\tvar loc = sb.ToString();\n\t\t\t//print.it(loc);\n\t\t\tif (loc.Ends(\".png\")) return loc; //Win 8.0, 8.1\n\t\t\tDebug_.PrintIf(!loc.Ends(\".png-100\"), loc);\n\t\t\tint ipng = loc.LastIndexOf(\".png\", StringComparison.OrdinalIgnoreCase); if (ipng < 0) return null;\n\t\t\tloc = loc[..ipng];\n\t\t\tif (!_GetPngPathFromIconLoc(out string path)) return null;\n\t\t\treturn path;\n\t\t\t\n\t\t\tbool _GetPngPathFromIconLoc(out string path) {\n\t\t\t\tpath = null;\n\t\t\t\tvar dir = pathname.getDirectory(loc);\n\t\t\t\tif (!filesystem.exists(dir).Directory) return false;\n\t\t\t\tvar a1 = Directory.GetFiles(dir, pathname.getName(loc) + \"*.png\");\n\t\t\t\tint nTargetsize = 0, nScale = 0;\n\t\t\t\tfor (int i = 0; i < a1.Length; i++) {\n\t\t\t\t\tvar v = a1[i];\n\t\t\t\t\tif (v.Find(\"contrast-\", loc.Length, true) >= 0) a1[i] = null;\n\t\t\t\t\telse if (v.Find(\"targetsize-\", loc.Length, true) >= 0) nTargetsize++;\n\t\t\t\t\telse if (v.Find(\"scale-\", loc.Length, true) >= 0) nScale++;\n\t\t\t\t}\n\t\t\t\tif (nTargetsize == 0 && nScale == 0) {\n\t\t\t\t\tpath = a1.FirstOrDefault(o => o.Eqi(loc));\n\t\t\t\t\tloc += \".png\";\n\t\t\t\t\treturn path != null;\n\t\t\t\t}\n\t\t\t\tvar a = new (string path, string q, int size)[nTargetsize > 0 ? nTargetsize : nScale];\n\t\t\t\tint j = 0;\n\t\t\t\tforeach (var v in a1) {\n\t\t\t\t\tif (v == null) continue;\n\t\t\t\t\tint i = v.Find(nTargetsize > 0 ? \"targetsize-\" : \"scale-\", loc.Length, true); if (i < 0) continue;\n\t\t\t\t\tint z = v.ToInt(i + (nTargetsize > 0 ? 11 : 6));\n\t\t\t\t\tif (nTargetsize == 0) z = Math2.MulDiv(z, 16, 100); //assume eg scale-100 == targetsize-16\n\t\t\t\t\ta[j++] = (v, v[loc.Length..^4], z);\n\t\t\t\t}\n\t\t\t\t//foreach (var k in a) print.it(\"\\t\" + k.q, k.size);\n\t\t\t\tint bestSize = int.MaxValue, maxSmallerSize = 0;\n\t\t\t\tforeach (var v in a) if (v.size == size) { bestSize = size; break; } else if (v.size > size) bestSize = Math.Min(bestSize, v.size); else maxSmallerSize = Math.Max(maxSmallerSize, v.size);\n\t\t\t\tif (bestSize == int.MaxValue) bestSize = maxSmallerSize;\n\t\t\t\t//print.it(bestSize);\n\t\t\t\tforeach (var v in a) if (v.size == bestSize && v.q.Find(\"altform-lightunplated\", true) >= 0) { path = v.path; return true; }\n\t\t\t\t//foreach (var v in a) if(v.size==bestSize && v.q.Find(\"altform-unplated\", true)>=0 && v.q.Find(\"theme-light\", true)>=0) { path=v.path; return true; }\n\t\t\t\t//foreach (var v in a) if(v.size==bestSize && v.q.Find(\"theme-light\", true)>=0) { path=v.path; return true; }\n\t\t\t\tforeach (var v in a) if (v.size == bestSize && v.q.Find(\"altform-unplated\", true) >= 0) { path = v.path; return true; }\n\t\t\t\tforeach (var v in a) if (v.size == bestSize) { path = v.path; return true; }\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t}\n}\n\nnamespace Au.Types {\n\t/// <summary>\n\t/// Flags for <see cref=\"icon.of\"/> and similar functions.\n\t/// </summary>\n\t[Flags]\n\tpublic enum IconGetFlags {\n\t\t/// <summary>\n\t\t/// The <i>file</i> argument is literal full path. Don't parse <c>\"path,index\"</c>, don't support <c>\".ext\"</c> (file type icon), don't make fully-qualified, etc.\n\t\t/// </summary>\n\t\tLiteralPath = 1,\n\t\t\n\t\t/// <summary>\n\t\t/// Don't call <see cref=\"filesystem.searchPath\"/>.\n\t\t/// </summary>\n\t\tDontSearch = 2,\n\t\t\n#if false\n\t\t/// Use shell API for all file types, including exe and ico.\n\t\tShell=8, //rejected because SHGetFileInfo does not get exe icon with shield overlay\n\n\t\t/// <summary>\n\t\t/// If file does not exist or fails to get its icon, get common icon for that file type, or default document icon if cannot get common icon.\n\t\t/// </summary>\n\t\tDefaultIfFails = 16, //rejected. Now for exe/ico/etc is like with shell API: if file exists, gets default icon (exe or document), else returns <c>default(IntPtr)</c>.\n#endif\n\t}\n\t\n#pragma warning disable 1591 //missing XML documentation\n\t/// <summary>See <see cref=\"icon.stock\"/>, <ms>SHSTOCKICONID</ms>.</summary>\n\tpublic enum StockIcon {\n\t\tDOCNOASSOC,\n\t\tDOCASSOC,\n\t\tAPPLICATION,\n\t\tFOLDER,\n\t\tFOLDEROPEN,\n\t\tDRIVE525,\n\t\tDRIVE35,\n\t\tDRIVEREMOVE,\n\t\tDRIVEFIXED,\n\t\tDRIVENET,\n\t\tDRIVENETDISABLED,\n\t\tDRIVECD,\n\t\tDRIVERAM,\n\t\tWORLD,\n\t\tSERVER = 15,\n\t\tPRINTER,\n\t\tMYNETWORK,\n\t\tFIND = 22,\n\t\tHELP,\n\t\tSHARE = 28,\n\t\tLINK,\n\t\tSLOWFILE,\n\t\tRECYCLER,\n\t\tRECYCLERFULL,\n\t\tMEDIACDAUDIO = 40,\n\t\tLOCK = 47,\n\t\tAUTOLIST = 49,\n\t\tPRINTERNET,\n\t\tSERVERSHARE,\n\t\tPRINTERFAX,\n\t\tPRINTERFAXNET,\n\t\tPRINTERFILE,\n\t\tSTACK,\n\t\tMEDIASVCD,\n\t\tSTUFFEDFOLDER,\n\t\tDRIVEUNKNOWN,\n\t\tDRIVEDVD,\n\t\tMEDIADVD,\n\t\tMEDIADVDRAM,\n\t\tMEDIADVDRW,\n\t\tMEDIADVDR,\n\t\tMEDIADVDROM,\n\t\tMEDIACDAUDIOPLUS,\n\t\tMEDIACDRW,\n\t\tMEDIACDR,\n\t\tMEDIACDBURN,\n\t\tMEDIABLANKCD,\n\t\tMEDIACDROM,\n\t\tAUDIOFILES,\n\t\tIMAGEFILES,\n\t\tVIDEOFILES,\n\t\tMIXEDFILES,\n\t\tFOLDERBACK,\n\t\tFOLDERFRONT,\n\t\tSHIELD,\n\t\tWARNING,\n\t\tINFO,\n\t\tERROR,\n\t\tKEY,\n\t\tSOFTWARE,\n\t\tRENAME,\n\t\tDELETE,\n\t\tMEDIAAUDIODVD,\n\t\tMEDIAMOVIEDVD,\n\t\tMEDIAENHANCEDCD,\n\t\tMEDIAENHANCEDDVD,\n\t\tMEDIAHDDVD,\n\t\tMEDIABLURAY,\n\t\tMEDIAVCD,\n\t\tMEDIADVDPLUSR,\n\t\tMEDIADVDPLUSRW,\n\t\tDESKTOPPC,\n\t\tMOBILEPC,\n\t\tUSERS,\n\t\tMEDIASMARTMEDIA,\n\t\tMEDIACOMPACTFLASH,\n\t\tDEVICECELLPHONE,\n\t\tDEVICECAMERA,\n\t\tDEVICEVIDEOCAMERA,\n\t\tDEVICEAUDIOPLAYER,\n\t\tNETWORKCONNECT,\n\t\tINTERNET,\n\t\tZIPFILE,\n\t\tSETTINGS,\n\t\tDRIVEHDDVD = 132,\n\t\tDRIVEBD,\n\t\tMEDIAHDDVDROM,\n\t\tMEDIAHDDVDR,\n\t\tMEDIAHDDVDRAM,\n\t\tMEDIABDROM,\n\t\tMEDIABDR,\n\t\tMEDIABDRE,\n\t\tCLUSTEREDDRIVE,\n\t\tMAX_ICONS = 181\n\t}\n#pragma warning restore 1591\n}\n"
  },
  {
    "path": "Au/Files, data/pathname.cs",
    "content": "//tested: System.IO.Path functions improved in Core.\n//\tNo exceptions if path contains invalid characters. Although the exceptions are still documented in MSDN.\n//\tSupport long paths and file streams.\n//\tFaster, etc.\n\nnamespace Au {\n\t/// <summary>\n\t/// File path string functions. Parse, combine, make full, make unique, make valid, expand variables, etc.\n\t/// </summary>\n\t/// <remarks>\n\t/// Most functions of this class work with strings and don't access the file system. Several functions query file system info.\n\t/// \n\t/// Functions of this class don't throw exceptions when path is invalid (path format, invalid characters). Only <see cref=\"normalize\"/> throws exception if not full path.\n\t/// \n\t/// Also you can use .NET class <see cref=\"Path\"/>. In its documentation you'll find more info about paths.\n\t/// </remarks>\n\tpublic static unsafe class pathname { //BAD: why pathname? Better would be eg filepath.\n\t\t/// <summary>\n\t\t/// If <i>path</i> starts with <c>\"%\"</c> or <c>\"\\\"%\"</c>, expands environment variables enclosed in <c>%</c>, else just returns <i>path</i>.\n\t\t/// Also supports known folder names, like <c>\"%folders.Documents%\"</c>. More info in Remarks.\n\t\t/// </summary>\n\t\t/// <param name=\"path\">Any string. Can be <c>null</c>.</param>\n\t\t/// <param name=\"strict\">\n\t\t/// What to do if <i>path</i> looks like starts with and environment variable or known folder but the variable/folder does not exist:\n\t\t/// <br/>• <c>true</c> - throw <see cref=\"ArgumentException\"/>;\n\t\t/// <br/>• <c>false</c> - return unexpanded path;\n\t\t/// <br/>• <c>null</c> (default) - call <see cref=\"print.warning\"/> and return unexpanded path.\n\t\t/// </param>\n\t\t/// <remarks>\n\t\t/// Supports known folder names. See <see cref=\"folders\"/>.\n\t\t/// Example: <c>@\"%folders.Documents%\\file.txt\"</c>.\n\t\t/// Example: <c>@\"%folders.shell.ControlPanel%\" //gets \":: ITEMIDLIST\"</c>.\n\t\t/// Usually known folders are used like <c>string path = folders.Documents + \"file.txt\"</c>. However it cannot be used when you want to store paths in text files, registry, etc. Then this feature is useful.\n\t\t/// To get known folder path, this function calls <see cref=\"folders.getFolder\"/>.\n\t\t/// \n\t\t/// This function is called by many functions of classes <see cref=\"pathname\"/>, <see cref=\"filesystem\"/>, <see cref=\"icon\"/>, some others, therefore all they support environment variables and known folders in path string.\n\t\t/// </remarks>\n\t\t/// <seealso cref=\"Environment.ExpandEnvironmentVariables\"/>\n\t\t/// <seealso cref=\"Environment.GetEnvironmentVariable\"/>\n\t\t/// <seealso cref=\"Environment.SetEnvironmentVariable\"/>\n\t\tpublic static string expand(string path, bool? strict = null) {\n\t\t\tvar s = path;\n\t\t\tif (s.Lenn() < 3) return s;\n\t\t\tif (s[0] != '%') {\n\t\t\t\tif (s[0] == '\"' && s[1] == '%') return \"\\\"\" + expand(s[1..], strict);\n\t\t\t\treturn s;\n\t\t\t}\n\t\t\tint i = s.IndexOf('%', 2); if (i < 0) return s;\n\t\t\t//return Environment.ExpandEnvironmentVariables(s); //5 times slower\n\n\t\t\t//support known folders, like @\"%folders.Documents%\\...\".\n\t\t\t//\trejected: without \"folders\", like @\"%%.Documents%\\...\". If need really short, can set and use environment variables.\n\t\t\t//if ((i > 10 && s.Starts(\"%folders.\")) || (i > 4 && s.Starts(\"%%\"))) {\n\t\t\t//\tvar prop = s[(s[1] == '%' ? 2 : 10)..i];\n\t\t\tif (i > 10 && s.Starts(\"%folders.\")) {\n\t\t\t\tvar prop = s[9..i];\n\t\t\t\tvar k = folders.getFolder(prop);\n\t\t\t\tif (k != null) {\n\t\t\t\t\ts = s[++i..];\n\t\t\t\t\tstring ks = k.Path; if (ks.Starts(\":: \")) return ks + s; //don't need \\\n\t\t\t\t\treturn k + s; //add \\ if need\n\t\t\t\t}\n\t\t\t\t//throw new AuException(\"folders does not have property \" + prop);\n\t\t\t}\n\n\t\t\tif (!Api.ExpandEnvironmentStrings(s, out s)) {\n\t\t\t\tvar err = \"Failed to expand path: \" + s;\n\t\t\t\tif (strict == true) throw new ArgumentException(err);\n\t\t\t\tif (strict != false) print.warning(err);\n\t\t\t\treturn s;\n\t\t\t}\n\t\t\treturn expand(s, strict); //can be %envVar2% in envVar1 value\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if the string is full path, like <c>@\"C:\\a\\b.txt\"</c> or <c>@\"C:\"</c> or <c>@\"\\\\server\\share\\...\"</c>:\n\t\t/// </summary>\n\t\t/// <param name=\"path\">Any string. Can be <c>null</c>.</param>\n\t\t/// <param name=\"orEnvVar\">Also return <c>true</c> if starts with <c>\"%environmentVariable%\"</c> or <c>\"%folders.Folder%\"</c>. Note: this function does not check whether the variable/folder exists; for it use <see cref=\"isFullPathExpand\"/> instead.</param>\n\t\t/// <remarks>\n\t\t/// Returns <c>true</c> if <i>path</i> matches one of these wildcard patterns:\n\t\t/// - <c>@\"?:\\*\"</c> - local path, like <c>@\"C:\\a\\b.txt\"</c>. Here <c>?</c> is A-Z, a-z.\n\t\t/// - <c>@\"?:\"</c> - drive name, like <c>@\"C:\"</c>. Here <c>?</c> is A-Z, a-z.\n\t\t/// - <c>@\"\\\\*\"</c> - network path, like <c>@\"\\\\server\\share\\...\"</c>. Or has prefix <c>@\"\\\\?\\\"</c>.\n\t\t/// \n\t\t/// Supports <c>'/'</c> characters too.\n\t\t/// \n\t\t/// Supports only file-system paths. Returns <c>false</c> if <i>path</i> is URL (<see cref=\"isUrl\"/>) or starts with <c>\"::\"</c>.\n\t\t/// </remarks>\n\t\tpublic static bool isFullPath(RStr path, bool orEnvVar = false) {\n\t\t\tint len = path.Length;\n\t\t\tif (len >= 2) {\n\t\t\t\tif (path[1] == ':' && path[0].IsAsciiAlpha()) {\n\t\t\t\t\treturn len == 2 || IsSepChar_(path[2]);\n\t\t\t\t\t//info: returns false if eg \"c:abc\" which means \"abc\" in current directory of drive \"c:\"\n\t\t\t\t}\n\t\t\t\tswitch (path[0]) {\n\t\t\t\tcase '\\\\' or '/':\n\t\t\t\t\treturn IsSepChar_(path[1]);\n\t\t\t\tcase '%' when orEnvVar:\n\t\t\t\t\treturn path[1..].IndexOf('%') > 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Expands environment variables and calls/returns <see cref=\"isFullPath\"/>.\n\t\t/// </summary>\n\t\t/// <returns><c>true</c> if the string is full path, like <c>@\"C:\\a\\b.txt\"</c> or <c>@\"C:\"</c> or <c>@\"\\\\server\\share\\...\"</c>.</returns>\n\t\t/// <param name=\"path\">\n\t\t/// Any string. Can be <c>null</c>.\n\t\t/// If starts with <c>'%'</c> character, calls <see cref=\"expand\"/> and then <see cref=\"isFullPath\"/> with expanded environment variables. If it returns <c>true</c>, replaces the passed variable with the expanded path string.\n\t\t/// </param>\n\t\t/// <param name=\"strict\"><inheritdoc cref=\"expand(string, bool?)\" path=\"/param[@name='strict']/node()\"/></param>\n\t\t/// <remarks>\n\t\t/// Returns <c>true</c> if <i>path</i> matches one of these wildcard patterns:\n\t\t/// - <c>@\"?:\\*\"</c> - local path, like <c>@\"C:\\a\\b.txt\"</c>. Here <c>?</c> is A-Z, a-z.\n\t\t/// - <c>@\"?:\"</c> - drive name, like <c>@\"C:\"</c>. Here <c>?</c> is A-Z, a-z.\n\t\t/// - <c>@\"\\\\*\"</c> - network path, like <c>@\"\\\\server\\share\\...\"</c>. Or has prefix <c>@\"\\\\?\\\"</c>.\n\t\t/// \n\t\t/// Supports <c>'/'</c> characters too.\n\t\t/// Supports only file-system paths. Returns <c>false</c> if <i>path</i> is URL (<see cref=\"isUrl\"/>) or starts with <c>\"::\"</c>.\n\t\t/// </remarks>\n\t\tpublic static bool isFullPathExpand(ref string path, bool? strict = null) => isFullPathExpand(path, out path, strict);\n\t\t\n\t\t/// <param name=\"path\">\n\t\t/// Any string. Can be <c>null</c>.\n\t\t/// If starts with <c>'%'</c> character, calls <see cref=\"expand\"/> and then <see cref=\"isFullPath\"/> with expanded environment variables. If it returns <c>true</c>, sets <i>path2</i> = the expanded path string.\n\t\t/// </param>\n\t\t/// <param name=\"path2\">Receives the final path.</param>\n\t\t/// <inheritdoc cref=\"isFullPathExpand(ref string, bool?)\"/>\n\t\tpublic static bool isFullPathExpand(string path, out string path2, bool? strict = null) {\n\t\t\tvar s = path2 = path;\n\t\t\tif (s.Lenn() < 2) return false;\n\t\t\tif (s[0] != '%') return isFullPath(s);\n\t\t\ts = expand(s, strict);\n\t\t\tif (s[0] == '%') return false;\n\t\t\tif (!isFullPath(s)) return false;\n\t\t\tpath2 = s;\n\t\t\treturn true;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets the length of the drive or network folder part in <i>path</i>, like <c>@\"C:\\\"</c>, <c>@\"\\\\server\\share\\\"</c>, <c>@\"\\\\?\\C:\\\"</c>, <c>@\"\\\\?\\UNC\\server\\share\\\"</c>, etc.\n\t\t/// </summary>\n\t\t/// <param name=\"path\">Full path or any string. Can be <c>null</c>. Should not be <c>@\"%environmentVariable%\\...\"</c>.</param>\n\t\t/// <remarks>\n\t\t/// See <see cref=\"Path.GetPathRoot\"/>.\n\t\t/// </remarks>\n\t\tpublic static int getRootLength(RStr path) {\n\t\t\tint i = Path.GetPathRoot(path).Length; //Span, no alloc\n\t\t\tif (i > 0 && i < path.Length && !IsSepChar_(path[i - 1]) && IsSepChar_(path[i])) i++; //@\"\\\\server\\share\" -> @\"\\\\server\\share\\\"\n\t\t\treturn i;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Calls <c>Path.GetPathRoot</c>. If no <c>'\\\\'</c> or <c>'/'</c> at the end, appends <c>\"\\\\\"</c>.\n\t\t/// Tested: <c>Path.GetPathRoot</c> returns network path like <c>@\"\\\\server\\share\"</c>. API <c>PathSkipRoot</c> returns with <c>'\\\\'</c>.\n\t\t/// </summary>\n\t\tinternal static string GetRootBS_(string s) {\n\t\t\ts = Path.GetPathRoot(s);\n\t\t\tif (!Path.EndsInDirectorySeparator(s)) s += \"\\\\\";\n\t\t\treturn s;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets the length of the URL protocol name (also known as URI scheme) in string, including <c>':'</c>.\n\t\t/// If the string does not start with a protocol name, returns 0.\n\t\t/// </summary>\n\t\t/// <param name=\"s\">A URL or path or any string. Can be <c>null</c>.</param>\n\t\t/// <remarks>\n\t\t/// URL examples: <c>\"http:\"</c> (returns 5), <c>\"http://www.x.com\"</c> (returns 5), <c>\"file:///path\"</c> (returns 5), <c>\"shell:etc\"</c> (returns 6).\n\t\t/// \n\t\t/// The protocol can be unknown. The function just checks string format, which is an ASCII alpha character followed by one or more ASCII alpha-numeric, <c>'.'</c>, <c>'-'</c>, <c>'+'</c> characters, followed by <c>':'</c> character.\n\t\t/// </remarks>\n\t\tpublic static int getUrlProtocolLength(RStr s) {\n\t\t\tint len = s.Length;\n\t\t\tif (len > 2 && s[0].IsAsciiAlpha() && s[1] != ':') {\n\t\t\t\tfor (int i = 1; i < len; i++) {\n\t\t\t\t\tvar c = s[i];\n\t\t\t\t\tif (c == ':') return i + 1;\n\t\t\t\t\tif (!(c.IsAsciiAlphaDigit() || c == '.' || c == '-' || c == '+')) break;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn 0;\n\t\t\t//info: API PathIsURL lies, like most shlwapi.dll functions.\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if the string starts with a URL protocol name (existing or not) and <c>':'</c> character.\n\t\t/// Calls <see cref=\"getUrlProtocolLength\"/> and returns <c>true</c> if it's not 0.\n\t\t/// </summary>\n\t\t/// <param name=\"s\">A URL or path or any string. Can be <c>null</c>.</param>\n\t\t/// <remarks>\n\t\t/// URL examples: <c>\"http:\"</c>, <c>\"http://www.x.com\"</c>, <c>\"file:///path\"</c>, <c>\"shell:etc\"</c>.\n\t\t/// </remarks>\n\t\tpublic static bool isUrl(RStr s) {\n\t\t\treturn 0 != getUrlProtocolLength(s);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Combines two path parts using character <c>'\\\\'</c>. For example directory path and file name.\n\t\t/// </summary>\n\t\t/// <param name=\"s1\">First part. Usually a directory.</param>\n\t\t/// <param name=\"s2\">Second part. Usually a filename or relative path.</param>\n\t\t/// <param name=\"s2CanBeFullPath\"><i>s2</i> can be full path. If it is, ignore <i>s1</i> and return <i>s2</i> with expanded environment variables. If <c>false</c> (default), simply combines <i>s1</i> and <i>s2</i>.</param>\n\t\t/// <param name=\"prefixLongPath\">Call <see cref=\"prefixLongPathIfNeed\"/> which may prepend <c>@\"\\\\?\\\"</c> if the result path is very long. Default <c>true</c>.</param>\n\t\t/// <remarks>\n\t\t/// If <i>s1</i> and <i>s2</i> are <c>null</c> or <c>\"\"</c>, returns <c>\"\"</c>. Else if <i>s1</i> is <c>null</c> or <c>\"\"</c>, returns <i>s2</i>. Else if <i>s2</i> is <c>null</c> or <c>\"\"</c>, returns <i>s1</i>.\n\t\t/// Does not expand environment variables. For it use <see cref=\"expand\"/> before, or <see cref=\"normalize\"/> instead. Path that starts with an environment variable here is considered not full path.\n\t\t/// Similar to <see cref=\"Path.Combine\"/>. Main differences: has some options; supports <c>null</c> arguments.\n\t\t/// </remarks>\n\t\t/// <seealso cref=\"normalize\"/>\n\t\tpublic static string combine(string s1, string s2, bool s2CanBeFullPath = false, bool prefixLongPath = true) {\n\t\t\tstring r;\n\t\t\tif (s1.NE()) r = s2 ?? \"\";\n\t\t\telse if (s2.NE()) r = s1 ?? \"\";\n\t\t\telse if (s2CanBeFullPath && isFullPath(s2)) r = s2;\n\t\t\telse {\n\t\t\t\tint k = 0;\n\t\t\t\tif (IsSepChar_(s1[^1])) k |= 1;\n\t\t\t\tif (IsSepChar_(s2[0])) k |= 2;\n\t\t\t\tswitch (k) {\n\t\t\t\tcase 0: r = s1 + @\"\\\" + s2; break;\n\t\t\t\tcase 3: r = s1 + s2[1..]; break;\n\t\t\t\tdefault: r = s1 + s2; break;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (prefixLongPath) r = prefixLongPathIfNeed(r);\n\t\t\treturn r;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Combines two path parts.\n\t\t/// Unlike <see cref=\"combine\"/>, fails if some part is empty or <c>@\"\\\"</c> or if <i>s2</i> is <c>@\"\\\\\"</c>. Also does not check <i>s2</i> full path.\n\t\t/// If fails, throws exception or returns <c>null</c> (if <i>noException</i>).\n\t\t/// </summary>\n\t\t/// <exception cref=\"ArgumentException\"></exception>\n\t\tinternal static string Combine_(string s1, string s2, bool noException = false) {\n\t\t\tif (!s1.NE() && !s2.NE()) {\n\t\t\t\tint k = 0;\n\t\t\t\tif (IsSepChar_(s1[^1])) {\n\t\t\t\t\tif (s1.Length == 1) goto ge;\n\t\t\t\t\tk |= 1;\n\t\t\t\t}\n\t\t\t\tif (IsSepChar_(s2[0])) {\n\t\t\t\t\tif (s2.Length == 1 || IsSepChar_(s2[1])) goto ge;\n\t\t\t\t\tk |= 2;\n\t\t\t\t}\n\t\t\t\tvar r = k switch {\n\t\t\t\t\t0 => s1 + @\"\\\" + s2,\n\t\t\t\t\t3 => s1 + s2[1..],\n\t\t\t\t\t_ => s1 + s2,\n\t\t\t\t};\n\t\t\t\treturn prefixLongPathIfNeed(r);\n\t\t\t}\n\t\t\tge:\n\t\t\tif (noException) return null;\n\t\t\tthrow new ArgumentException(\"Empty filename or path.\");\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if character <c>c is '\\\\' or '/'</c>.\n\t\t/// </summary>\n\t\t[MethodImpl(MethodImplOptions.AggressiveInlining)]\n\t\tinternal static bool IsSepChar_(char c) { return c is '\\\\' or '/'; }\n\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if <i>s</i> starts with <c>\"\\\\\"</c>. Supports <c>'/'</c>.\n\t\t/// </summary>\n\t\t/// <param name=\"s\">Can be <c>null</c>.</param>\n\t\tinternal static bool StartsWithTwoSlash_(string s) => s.Lenn() >= 2 && IsSepChar_(s[0]) && IsSepChar_(s[1]);\n\n\t\t///// <summary>\n\t\t///// Returns <c>true</c> if ends with <c>'\\\\'</c> or <c>'/'</c>.\n\t\t///// </summary>\n\t\t//internal static bool EndsWithSep_(RStr s) { return s.Length > 0 && s[^1] is '\\\\' or '/'; }\n\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if ends with <c>':'</c> preceded by a drive letter, like <c>\"C:\"</c> or <c>\"more\\C:\"</c>, but not like <c>\"moreC:\"</c>.\n\t\t/// </summary>\n\t\tstatic bool _EndsWithDriveWithoutSep(RStr s) {\n\t\t\tint i = s.Length - 1;\n\t\t\tif (i < 1 || s[i] != ':') return false;\n\t\t\tif (!s[--i].IsAsciiAlpha()) return false;\n\t\t\tif (i > 0 && !IsSepChar_(s[i - 1])) return false;\n\t\t\treturn true;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// If <i>s</i> is like <c>\"C:\"</c>, returns <c>\"C:\\\"</c>, else returns <i>s</i>.\n\t\t/// </summary>\n\t\tstatic string _AddSepToDrive(string s) => _EndsWithDriveWithoutSep(s) ? s + \"\\\\\" : s;\n\n\t\t/// <summary>\n\t\t/// Makes normal full path from path that can contain special substrings etc.\n\t\t/// </summary>\n\t\t/// <param name=\"path\">Any path.</param>\n\t\t/// <param name=\"defaultParentDirectory\">If <i>path</i> is not full path, combine it with <i>defaultParentDirectory</i> to make full path.</param>\n\t\t/// <param name=\"flags\"></param>\n\t\t/// <exception cref=\"ArgumentException\"><i>path</i> is not full path, and <i>defaultParentDirectory</i> is not used or does not make it full path.</exception>\n\t\t/// <remarks>\n\t\t/// The sequence of actions:\n\t\t/// 1. If <i>path</i> starts with <c>'%'</c> character, expands environment variables and special folder names. See <see cref=\"expand\"/>.\n\t\t/// 2. If <i>path</i> is not full path but looks like URL, and used flag <c>CanBeUrl</c>, returns <i>path</i>.\n\t\t/// 3. If <i>path</i> is not full path, and <i>defaultParentDirectory</i> is not <c>null</c>/<c>\"\"</c>, combines <i>path</i> with <c>expand(defaultParentDirectory)</c>.\n\t\t/// 4. If <i>path</i> is not full path, throws exception.\n\t\t/// 5. If <i>path</i> is like <c>\"C:\"</c> makes like <c>\"C:\\\"</c>.\n\t\t/// 6. Calls API <ms>GetFullPathName</ms>. It replaces <c>'/'</c> with <c>'\\\\'</c>, replaces multiple <c>'\\\\'</c> with single (where need), processes <c>@\"\\..\"</c> etc, trims spaces, etc.\n\t\t/// 7. If no flag <c>DontExpandDosPath</c>, if <i>path</i> looks like a short DOS path version (contains <c>'~'</c> etc), calls API <ms>GetLongPathName</ms>. It converts short DOS path to normal path, if possible, for example <c>@\"c:\\progra~1\"</c> to <c>@\"c:\\program files\"</c>. It is slow. It converts path only if the file exists.\n\t\t/// 8. If no flag <c>DontRemoveEndSeparator</c>, and string ends with <c>'\\\\'</c> character, and length > 4, removes the <c>'\\\\'</c>, unless then it would be a path to an existing file (not directory).\n\t\t/// 9. If no flag <c>DontPrefixLongPath</c>, calls <see cref=\"prefixLongPathIfNeed\"/>, which adds <c>@\"\\\\?\\\"</c> etc prefix if path is very long.\n\t\t/// \n\t\t/// Similar to <see cref=\"Path.GetFullPath\"/>. Main differences: this function expands environment variables, does not support relative paths (unless used <i>defaultParentDirectory</i>), trims <c>'\\\\'</c> at the end if need.\n\t\t/// </remarks>\n\t\t/// <seealso cref=\"filesystem.more.getFinalPath\"/>\n\t\tpublic static string normalize(string path, string defaultParentDirectory = null, PNFlags flags = 0) {\n\t\t\tif (!isFullPathExpand(ref path)) {\n\t\t\t\tif (0 != (flags & PNFlags.CanBeUrlOrShell)) if (IsShellPathOrUrl_(path)) return path;\n\t\t\t\tif (defaultParentDirectory.NE()) goto ge;\n\t\t\t\tpath = Combine_(expand(defaultParentDirectory), path);\n\t\t\t\tif (!isFullPath(path)) goto ge;\n\t\t\t}\n\n\t\t\treturn Normalize_(path, flags, noExpandEV: true);\n\t\t\tge:\n\t\t\tthrow new ArgumentException($\"Not full path: '{path}'.\");\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Same as <see cref=\"normalize\"/>, but skips full-path checking.\n\t\t/// <i>s</i> should be full path. If not full and not <c>null</c>/<c>\"\"</c>, combines with current directory.\n\t\t/// </summary>\n\t\tinternal static string Normalize_(string s, PNFlags flags = 0, bool noExpandEV = false) {\n\t\t\tif (!s.NE()) {\n\t\t\t\tif (!noExpandEV) s = expand(s);\n\t\t\t\tDebug_.PrintIf(IsShellPathOrUrl_(s), s);\n\n\t\t\t\ts = _AddSepToDrive(s); //API would append current directory\n\n\t\t\t\t//note: although slower, call GetFullPathName always, not just when contains @\"..\\\" etc.\n\t\t\t\t//\tBecause it does many things (see Normalize doc), not all documented.\n\t\t\t\t//\tWe still ~2 times faster than Path.GetFullPath (tested before Core).\n\t\t\t\tApi.GetFullPathName(s, out s);\n\n\t\t\t\tif (0 == (flags & PNFlags.DontExpandDosPath) && IsPossiblyDos_(s)) s = ExpandDosPath_(s);\n\n\t\t\t\tif (0 == (flags & PNFlags.DontRemoveEndSeparator) && IsSepChar_(s[^1]) && s.Length > 4) {\n\t\t\t\t\tvar s2 = s[..^1];\n\t\t\t\t\tif (Api.GetFileAttributes(s2).Has(FileAttributes.Directory)) s = s2; //if does not exist as file\n\t\t\t\t}\n\n\t\t\t\tif (0 == (flags & PNFlags.DontPrefixLongPath)) s = prefixLongPathIfNeed(s);\n\t\t\t}\n\t\t\treturn s;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Prepares path for passing to API and .NET functions that support <c>\"..\"</c>, DOS path etc.\n\t\t/// Calls expand, <c>_AddSepToDrive</c>, <see cref=\"prefixLongPathIfNeed\"/>. By default throws exception if <c>!isFullPath(path)</c>.\n\t\t/// </summary>\n\t\t/// <exception cref=\"ArgumentException\">Not full path (only if <i>throwIfNotFullPath</i> is <c>true</c>).</exception>\n\t\tinternal static string NormalizeMinimally_(string path, bool throwIfNotFullPath = true) {\n\t\t\tvar s = expand(path);\n\t\t\tDebug_.PrintIf(IsShellPathOrUrl_(s), s);\n\t\t\tif (throwIfNotFullPath && !isFullPath(s)) throw new ArgumentException($\"Not full path: '{path}'.\");\n\t\t\ts = _AddSepToDrive(s);\n\t\t\ts = prefixLongPathIfNeed(s);\n\t\t\treturn s;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Calls API <c>GetLongPathName</c>.\n\t\t/// Does not check whether <i>s</i> contains <c>'~'</c> character etc. Note: the API is slow.\n\t\t/// </summary>\n\t\t/// <param name=\"s\">Can be <c>null</c>.</param>\n\t\tinternal static string ExpandDosPath_(string s) {\n\t\t\tif (!s.NE()) Api.GetLongPathName(s, out s);\n\t\t\treturn s;\n\t\t\t//CONSIDER: the API fails if the file does not exist.\n\t\t\t//\tWorkaround: if filename does not contain '~', pass only the part that contains.\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if <i>s</i> looks like a DOS filename or path.\n\t\t/// Examples: <c>\"abcde~12\"</c>, <c>\"abcde~12.txt\"</c>, <c>@\"c:\\path\\abcde~12.txt\"</c>, <c>\"c:\\abcde~12\\path\"</c>.\n\t\t/// </summary>\n\t\t/// <param name=\"s\">Can be <c>null</c>.</param>\n\t\tinternal static bool IsPossiblyDos_(string s) {\n\t\t\t//print.it(s);\n\t\t\tif (s != null && s.Length >= 8) {\n\t\t\t\tfor (int i = 0; (i = s.IndexOf('~', i + 1)) > 0;) {\n\t\t\t\t\tint j = i + 1, k = 0;\n\t\t\t\t\tfor (; k < 6 && j < s.Length; k++, j++) if (!s[j].IsAsciiDigit()) break;\n\t\t\t\t\tif (k == 0) continue;\n\t\t\t\t\tchar c = j < s.Length ? s[j] : '\\\\';\n\t\t\t\t\tif (c == '\\\\' || c == '/' || (c == '.' && j == s.Length - 4)) {\n\t\t\t\t\t\tfor (j = i; j > 0; j--) {\n\t\t\t\t\t\t\tc = s[j - 1]; if (c == '\\\\' || c == '/') break;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (j == i - (7 - k)) return true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if starts with <c>\"::\"</c>.\n\t\t/// </summary>\n\t\t/// <param name=\"s\">Can be <c>null</c>.</param>\n\t\tinternal static bool IsShellPath_(RStr s) {\n\t\t\treturn s.Length >= 2 && s[0] == ':' && s[1] == ':';\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if <c>IsShellPath_(s) || isUrl(s)</c>.\n\t\t/// </summary>\n\t\t/// <param name=\"s\">Can be <c>null</c>.</param>\n\t\tinternal static bool IsShellPathOrUrl_(RStr s) => IsShellPath_(s) || isUrl(s);\n\n\t\t/// <summary>\n\t\t/// If <i>path</i> is full path (see <see cref=\"isFullPath\"/>) and does not start with <c>@\"\\\\?\\\"</c>, prepends <c>@\"\\\\?\\\"</c>.\n\t\t/// If <i>path</i> is network path (like <c>@\"\\\\computer\\folder\\...\"</c>), makes like <c>@\"\\\\?\\UNC\\computer\\folder\\...\"</c>.\n\t\t/// </summary>\n\t\t/// <param name=\"path\">\n\t\t/// Path. Can be <c>null</c>.\n\t\t/// Must not start with <c>\"%environmentVariable%\"</c>. This function does not expand it. See <see cref=\"expand\"/>.\n\t\t/// </param>\n\t\t/// <remarks>\n\t\t/// Windows API kernel functions support extended-length paths, ie longer than 259 characters. But the path must have this prefix. Windows API shell functions don't support it.\n\t\t/// </remarks>\n\t\tpublic static string prefixLongPath(string path) {\n\t\t\tvar s = path;\n\t\t\tif (isFullPath(s) && 0 == _GetPrefixLength(s)) {\n\t\t\t\tif (s.Length >= 2 && IsSepChar_(s[0]) && IsSepChar_(s[1])) s = s.ReplaceAt(0, 2, @\"\\\\?\\UNC\\\");\n\t\t\t\telse s = @\"\\\\?\\\" + s;\n\t\t\t}\n\t\t\treturn s;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Calls <see cref=\"prefixLongPath\"/> if <i>path</i> is longer than <see cref=\"maxDirectoryPathLength\"/> (247).\n\t\t/// </summary>\n\t\t/// <param name=\"path\">\n\t\t/// Path. Can be <c>null</c>.\n\t\t/// Must not start with <c>\"%environmentVariable%\"</c>. This function does not expand it. See <see cref=\"expand\"/>.\n\t\t/// </param>\n\t\tpublic static string prefixLongPathIfNeed(string path) {\n\t\t\tif (path.Lenn() > maxDirectoryPathLength) path = prefixLongPath(path);\n\t\t\treturn path;\n\n\t\t\t//info: MaxDirectoryPathLength is max length supported by API CreateDirectory.\n\t\t}\n\n\t\t/// <summary>\n\t\t/// If <i>path</i> starts with <c>@\"\\\\?\\\"</c> prefix, removes it.\n\t\t/// If <i>path</i> starts with <c>@\"\\\\?\\UNC\\\"</c> prefix, removes <c>@\"?\\UNC\\\"</c>.\n\t\t/// </summary>\n\t\t/// <param name=\"path\">\n\t\t/// Path. Can be <c>null</c>.\n\t\t/// Must not start with <c>\"%environmentVariable%\"</c>. This function does not expand it. See <see cref=\"expand\"/>.\n\t\t/// </param>\n\t\tpublic static string unprefixLongPath(string path) {\n\t\t\tif (!path.NE()) {\n\t\t\t\tswitch (_GetPrefixLength(path)) {\n\t\t\t\tcase 4: return path[4..];\n\t\t\t\tcase 8: return path.Remove(2, 6);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn path;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// If <i>s</i> starts with <c>@\"\\\\?\\UNC\\\"</c>, returns 8.\n\t\t/// Else if starts with <c>@\"\\\\?\\\"</c>, returns 4.\n\t\t/// Else returns 0.\n\t\t/// </summary>\n\t\t/// <param name=\"s\">Can be <c>null</c>.</param>\n\t\tstatic int _GetPrefixLength(RStr s) {\n\t\t\tint len = s.Length;\n\t\t\tif (len >= 4 && s[2] == '?' && IsSepChar_(s[0]) && IsSepChar_(s[1]) && IsSepChar_(s[3])) {\n\t\t\t\tif (len >= 8 && IsSepChar_(s[7]) && s[4..7].Eqi(\"UNC\")) return 8;\n\t\t\t\treturn 4;\n\t\t\t}\n\t\t\treturn 0;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Maximal file (not directory) path length supported by all functions (native, .NET and this library).\n\t\t/// For longer paths need <c>@\"\\\\?\\\"</c> prefix. It is supported by most native kernel API (but not shell API) and most functions of this library and .NET.\n\t\t/// </summary>\n\t\tpublic const int maxFilePathLength = 259;\n\n\t\t/// <summary>\n\t\t/// Maximal directory path length supported by all functions (native, .NET and this library).\n\t\t/// For longer paths need <c>@\"\\\\?\\\"</c> prefix. It is supported by most native kernel API (but not shell API) and most functions of this library and .NET.\n\t\t/// </summary>\n\t\tpublic const int maxDirectoryPathLength = 247;\n\n\t\t/// <summary>\n\t\t/// Replaces characters that cannot be used in file names.\n\t\t/// </summary>\n\t\t/// <param name=\"name\">Initial filename.</param>\n\t\t/// <param name=\"invalidCharReplacement\">A string that will replace each invalid character. Default <c>\"-\"</c>.</param>\n\t\t/// <remarks>\n\t\t/// Also corrects other forms of invalid or problematic filename: trims spaces and other blank characters; replaces <c>\".\"</c> at the end; prepends <c>\"@\"</c> if a reserved name like <c>\"CON\"</c> or <c>\"CON.txt\"</c>; returns <c>\"-\"</c> if <i>name</i> is <c>null</c>/empty/whitespace.\n\t\t/// Usually returns valid filename, however it can be too long (itself or when combined with a directory path).\n\t\t/// </remarks>\n\t\tpublic static string correctName(string name, string invalidCharReplacement = \"-\") {\n\t\t\tif (name == null || (name = name.Trim()).Length == 0) return invalidCharReplacement;\n\t\t\tname = _rxInvalidFN1.Replace(name, invalidCharReplacement).Trim();\n\t\t\tif (_rxInvalidFN2.IsMatch(name)) name = \"@\" + name;\n\t\t\treturn name;\n\t\t}\n\n\t\tstatic regexp _rxInvalidFN1 = new(@\"\\.$|[\\\\/|<>?*:\"\"\\x00-\\x1f]\");\n\t\tstatic regexp _rxInvalidFN2 = new(@\"(?i)^(CON|PRN|AUX|NUL|COM\\d|LPT\\d)(\\.|$)\");\n\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if name cannot be used for a file name, eg contains <c>'\\\\'</c> etc characters or is empty.\n\t\t/// More info: <see cref=\"correctName\"/>.\n\t\t/// </summary>\n\t\t/// <param name=\"name\">Any string. Example: <c>\"name.txt\"</c>. Can be <c>null</c>.</param>\n\t\tpublic static bool isInvalidName(string name) {\n\t\t\tif (name == null || (name = name.Trim()).Length == 0) return true;\n\t\t\treturn _rxInvalidFN1.IsMatch(name) || _rxInvalidFN2.IsMatch(name);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if character <i>c</i> is invalid in file names (the filename part).\n\t\t/// </summary>\n\t\tpublic static bool isInvalidNameChar(char c)\n\t\t\t=> c is < ' ' or '\"' or '<' or '>' or '|' or '*' or '?' or ':' or '\\\\' or '/';\n\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if character <i>c</i> is invalid in file paths.\n\t\t/// </summary>\n\t\tpublic static bool isInvalidPathChar(char c)\n\t\t\t=> c is < ' ' or '\"' or '<' or '>' or '|' or '*' or '?';\n\n\t\t/// <summary>\n\t\t/// Gets filename from <i>path</i>. Does not remove extension.\n\t\t/// </summary>\n\t\t/// <returns>Returns <c>\"\"</c> if there is no filename. Returns <c>null</c> if <i>path</i> is <c>null</c>.</returns>\n\t\t/// <param name=\"path\">Path or filename. Can be <c>null</c>.</param>\n\t\t/// <remarks>\n\t\t/// Similar to <see cref=\"Path.GetFileName\"/>. Some differences: if ends with <c>'\\\\'</c> or <c>'/'</c>, gets part before it, eg <c>\"B\"</c> from <c>@\"C:\\A\\B\\\"</c>.\n\t\t/// \n\t\t/// Supports separators <c>'\\\\'</c> and <c>'/'</c>.\n\t\t/// Also supports URL and shell parsing names like <c>@\"::{CLSID-1}\\0\\::{CLSID-2}\"</c>.\n\t\t/// \n\t\t/// Examples:\n\t\t/// \n\t\t/// | <i>path</i> | result\n\t\t/// | -\n\t\t/// | <c>@\"C:\\A\\B\\file.txt\"</c> | <c>\"file.txt\"</c>\n\t\t/// | <c>\"file.txt\"</c> | <c>\"file.txt\"</c>\n\t\t/// | <c>\"file\"</c> | <c>\"file\"</c>\n\t\t/// | <c>@\"C:\\A\\B\"</c> | <c>\"B\"</c>\n\t\t/// | <c>@\"C:\\A\\B\\\"</c> | <c>\"B\"</c>\n\t\t/// | <c>@\"C:\\A\\/B\\/\"</c> | <c>\"B\"</c>\n\t\t/// | <c>@\"C:\\\"</c> | <c>\"\"</c>\n\t\t/// | <c>@\"C:\"</c> | <c>\"\"</c>\n\t\t/// | <c>@\"\\\\network\\share\"</c> | <c>\"share\"</c>\n\t\t/// | <c>@\"C:\\aa\\file.txt:alt.stream\"</c> | <c>\"file.txt:alt.stream\"</c>\n\t\t/// | <c>\"http://a.b.c\"</c> | <c>\"a.b.c\"</c>\n\t\t/// | <c>\"::{A}\\::{B}\"</c> | <c>\"::{B}\"</c>\n\t\t/// | <c>\"\"</c> | <c>\"\"</c>\n\t\t/// | <c>null</c> | <c>null</c>\n\t\t/// </remarks>\n\t\tpublic static string getName(string path) {\n\t\t\treturn _GetPathPart(path, _PathPart.NameWithExt);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets filename without extension.\n\t\t/// </summary>\n\t\t/// <returns>Returns <c>\"\"</c> if there is no filename. Returns <c>null</c> if <i>path</i> is <c>null</c>.</returns>\n\t\t/// <param name=\"path\">Path or filename (then just removes extension). Can be <c>null</c>.</param>\n\t\t/// <remarks>\n\t\t/// The same as <see cref=\"getName\"/>, just removes extension.\n\t\t/// Similar to <see cref=\"Path.GetFileNameWithoutExtension\"/>. Some differences: if ends with <c>'\\\\'</c> or <c>'/'</c>, gets part before it, eg <c>\"B\"</c> from <c>@\"C:\\A\\B\\\"</c>.\n\t\t/// \n\t\t/// Supports separators <c>'\\\\'</c> and <c>'/'</c>.\n\t\t/// Also supports URL and shell parsing names like <c>@\"::{CLSID-1}\\0\\::{CLSID-2}\"</c>.\n\t\t/// \n\t\t/// Examples:\n\t\t/// \n\t\t/// | <i>path</i> | result\n\t\t/// | -\n\t\t/// | <c>@\"C:\\A\\B\\file.txt\"</c> | <c>\"file\"</c>\n\t\t/// | <c>\"file.txt\"</c> | <c>\"file\"</c>\n\t\t/// | <c>\"file\"</c> | <c>\"file\"</c>\n\t\t/// | <c>@\"C:\\A\\B\"</c> | <c>\"B\"</c>\n\t\t/// | <c>@\"C:\\A\\B\\\"</c> | <c>\"B\"</c>\n\t\t/// | <c>@\"C:\\A\\B.B\\\"</c> | <c>\"B.B\"</c>\n\t\t/// | <c>@\"C:\\aa\\file.txt:alt.stream\"</c> | <c>\"file.txt:alt\"</c>\n\t\t/// | <c>\"http://a.b.c\"</c> | <c>\"a.b\"</c>\n\t\t/// </remarks>\n\t\tpublic static string getNameNoExt(string path) {\n\t\t\treturn _GetPathPart(path, _PathPart.NameWithoutExt);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets filename extension, like <c>\".txt\"</c>.\n\t\t/// </summary>\n\t\t/// <returns>Returns <c>\"\"</c> if there is no extension. Returns <c>null</c> if <i>path</i> is <c>null</c>.</returns>\n\t\t/// <param name=\"path\">Path or filename. Can be <c>null</c>.</param>\n\t\t/// <remarks>\n\t\t/// Supports separators <c>'\\\\'</c> and <c>'/'</c>.\n\t\t/// </remarks>\n\t\tpublic static string getExtension(string path) {\n\t\t\treturn _GetPathPart(path, _PathPart.Ext);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets filename extension and path part without the extension.\n\t\t/// More info: <see cref=\"getExtension(string)\"/>.\n\t\t/// </summary>\n\t\t/// <param name=\"path\">Path or filename. Can be <c>null</c>.</param>\n\t\t/// <param name=\"pathWithoutExtension\">Receives path part without the extension. Can be the same variable as <i>path</i>.</param>\n\t\tpublic static string getExtension(string path, out string pathWithoutExtension) {\n\t\t\tvar ext = getExtension(path);\n\t\t\tif (ext != null && ext.Length > 0) pathWithoutExtension = path[..^ext.Length];\n\t\t\telse pathWithoutExtension = path;\n\t\t\treturn ext;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Finds filename extension, like <c>\".txt\"</c>.\n\t\t/// </summary>\n\t\t/// <returns>Index of <c>'.'</c> character, or -1 if there is no extension.</returns>\n\t\t/// <param name=\"path\">Path or filename. Can be <c>null</c>.</param>\n\t\tpublic static int findExtension(RStr path) {\n\t\t\tint i;\n\t\t\tfor (i = path.Length; --i >= 0;) {\n\t\t\t\tswitch (path[i]) {\n\t\t\t\tcase '.': return i;\n\t\t\t\tcase '\\\\': case '/': /*case ':':*/ return -1;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn i;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Removes filename part from <i>path</i>.\n\t\t/// By default also removes separator (<c>'\\\\'</c> or <c>'/'</c>) if it is not after drive name (eg <c>\"C:\"</c>).\n\t\t/// </summary>\n\t\t/// <returns>Returns <c>\"\"</c> if the string is a filename. Returns <c>null</c> if the string is <c>null</c> or a root (like <c>@\"C:\\\"</c> or <c>\"C:\"</c> or <c>@\"\\\\server\\share\"</c> or <c>\"http:\"</c>).</returns>\n\t\t/// <param name=\"path\">Path or filename. Can be <c>null</c>.</param>\n\t\t/// <param name=\"withSeparator\">Don't remove separator character(s) (<c>'\\\\'</c> or <c>'/'</c>). See examples.</param>\n\t\t/// <remarks>\n\t\t/// Similar to <see cref=\"Path.GetDirectoryName\"/>. Some differences: skips <c>'\\\\'</c> or <c>'/'</c> at the end (eg from <c>@\"C:\\A\\B\\\"</c> gets <c>@\"C:\\A\"</c>, not <c>@\"C:\\A\\B\"</c>); does not replace <c>'/'</c> with <c>'\\\\'</c>.\n\t\t/// \n\t\t/// Parses raw string. You may want to <see cref=\"normalize\"/> it at first.\n\t\t/// \n\t\t/// Supports separators <c>'\\\\'</c> and <c>'/'</c>.\n\t\t/// Also supports URL and shell parsing names like <c>@\"::{CLSID-1}\\0\\::{CLSID-2}\"</c>.\n\t\t/// \n\t\t/// Examples:\n\t\t/// \n\t\t/// | <i>path</i> | result\n\t\t/// | -\n\t\t/// | <c>@\"C:\\A\\B\\file.txt\"</c> | <c>@\"C:\\A\\B\"</c>\n\t\t/// | <c>\"file.txt\"</c> | <c>\"\"</c>\n\t\t/// | <c>@\"C:\\A\\B\\\"</c> | <c>@\"C:\\A\"</c>\n\t\t/// | <c>@\"C:\\A\\/B\\/\"</c> | <c>@\"C:\\A\"</c>\n\t\t/// | <c>@\"C:\\\"</c> | <c>null</c>\n\t\t/// | <c>@\"\\\\network\\share\"</c> | <c>null</c>\n\t\t/// | <c>\"http:\"</c> | <c>null</c>\n\t\t/// | <c>@\"C:\\aa\\file.txt:alt.stream\"</c> | <c>\"C:\\aa\"</c>\n\t\t/// | <c>\"http://a.b.c\"</c> | <c>\"http:\"</c>\n\t\t/// | <c>\"::{A}\\::{B}\"</c> | <c>\"::{A}\"</c>\n\t\t/// | <c>\"\"</c> | <c>\"\"</c>\n\t\t/// | <c>null</c> | <c>null</c>\n\t\t/// \n\t\t/// Examples when <i>withSeparator</i> <c>true</c>:\n\t\t/// \n\t\t/// | <i>path</i> | result\n\t\t/// | -\n\t\t/// | <c>@\"C:\\A\\B\"</c> | <c>@\"C:\\A\\\"</c> (not <c>@\"C:\\A\"</c>)\n\t\t/// | <c>\"http://x.y\"</c> | <c>\"http://\"</c> (not <c>\"http:\"</c>)\n\t\t/// </remarks>\n\t\tpublic static string getDirectory(string path, bool withSeparator = false) {\n\t\t\treturn _GetPathPart(path, _PathPart.Dir, withSeparator);\n\t\t}\n\n\t\tenum _PathPart { Dir, NameWithExt, NameWithoutExt, Ext, };\n\n\t\tstatic string _GetPathPart(string s, _PathPart what, bool withSeparator = false) {\n\t\t\tif (s == null) return null;\n\t\t\tint len = s.Length, i, iExt = -1;\n\n\t\t\t//rtrim '\\\\' and '/' etc\n\t\t\tfor (i = len; i > 0 && IsSepChar_(s[i - 1]); i--) {\n\t\t\t\tif (what == _PathPart.Ext) return \"\";\n\t\t\t\tif (what == _PathPart.NameWithoutExt) what = _PathPart.NameWithExt;\n\t\t\t}\n\t\t\tlen = i;\n\n\t\t\t//if ends with \":\" or @\":\\\", it is either drive or URL root or invalid\n\t\t\tif (len > 0 && s[len - 1] == ':' && !IsShellPath_(s)) return (what == _PathPart.Dir) ? null : \"\";\n\n\t\t\t//find '\\\\' or '/'. Also '.' if need.\n\t\t\t//Note: we don't split at ':', which could be used for alt stream or URL port or in shell parsing name as non-separator. This library does not support paths like \"C:relative path\".\n\t\t\twhile (--i >= 0) {\n\t\t\t\tchar c = s[i];\n\t\t\t\tif (c == '.') {\n\t\t\t\t\tif (what < _PathPart.NameWithoutExt) continue;\n\t\t\t\t\tif (iExt < 0) iExt = i;\n\t\t\t\t\tif (what == _PathPart.Ext) break;\n\t\t\t\t} else if (c == '\\\\' || c == '/') {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (iExt >= 0 && iExt == len - 1) iExt = -1; //eg ends with \"..\"\n\t\t\tif (what == _PathPart.NameWithoutExt && iExt < 0) what = _PathPart.NameWithExt;\n\n\t\t\tswitch (what) {\n\t\t\tcase _PathPart.Ext:\n\t\t\t\tif (iExt >= 0) return s[iExt..];\n\t\t\t\tbreak;\n\t\t\tcase _PathPart.NameWithExt:\n\t\t\t\tlen -= ++i; if (len == 0) return \"\";\n\t\t\t\treturn s.Substring(i, len);\n\t\t\tcase _PathPart.NameWithoutExt:\n\t\t\t\ti++;\n\t\t\t\treturn s[i..iExt];\n\t\t\tcase _PathPart.Dir:\n\t\t\t\t//skip multiple separators\n\t\t\t\tif (!withSeparator && i > 0) {\n\t\t\t\t\tfor (; i > 0; i--) { var c = s[i - 1]; if (!(c == '\\\\' || c == '/')) break; }\n\t\t\t\t\tif (i == 0) return null;\n\t\t\t\t}\n\t\t\t\tif (i > 0) {\n\t\t\t\t\t//returns null if i is in root\n\t\t\t\t\tint j = getRootLength(s); if (j > 0 && IsSepChar_(s[j - 1])) j--;\n\t\t\t\t\tif (i < j) return null;\n\n\t\t\t\t\tif (withSeparator || _EndsWithDriveWithoutSep(s.AsSpan(0, i))) i++;\n\t\t\t\t\treturn s[..i];\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\treturn \"\";\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if <i>s</i> is like <c>\".ext\"</c> and the ext part does not contain characters <c>.\\\\/:</c> and does not start/end with whitespace.\n\t\t/// </summary>\n\t\t/// <param name=\"s\">Can be <c>null</c>.</param>\n\t\tinternal static bool IsExtension_(RStr s) {\n\t\t\tif (s.Length < 2 || s[0] != '.') return false;\n\t\t\tfor (int i = 1; i < s.Length; i++) {\n\t\t\t\tswitch (s[i]) {\n\t\t\t\tcase '.' or '\\\\' or '/' or ':': return false;\n\t\t\t\tcase <= ' ' when i == 1 || i == s.Length - 1: return false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if <i>s</i> is like <c>\"protocol:\"</c> and not like <c>\"c:\"</c> or <c>\"protocol:more\"</c>.\n\t\t/// </summary>\n\t\t/// <param name=\"s\">Can be <c>null</c>.</param>\n\t\tinternal static bool IsProtocol_(RStr s) {\n\t\t\treturn s.Length > 2 && s[^1] == ':' && getUrlProtocolLength(s) == s.Length;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Creates path with unique filename for a new file or directory. \n\t\t/// If the specified path is of an existing file or directory, returns path where the filename part is modified like <c>\"file 2.txt\"</c>, <c>\"file 3.txt\"</c> etc. Else returns unchanged <i>path</i>.\n\t\t/// </summary>\n\t\t/// <param name=\"path\">Suggested full path.</param>\n\t\t/// <param name=\"isDirectory\">The path is for a directory. The number is always appended at the very end, not before <c>.extension</c>.</param>\n\t\tpublic static string makeUnique(string path, bool isDirectory) {\n\t\t\tif (!filesystem.exists(path)) return path;\n\t\t\tstring ext = isDirectory ? null : getExtension(path, out path);\n\t\t\tfor (int i = 2; ; i++) {\n\t\t\t\tvar s = path + \" \" + i + ext;\n\t\t\t\tif (!filesystem.exists(s)) return s;\n\t\t\t}\n\t\t}\n\t}\n}\n\nnamespace Au.Types {\n\t/// <summary>\n\t/// Flags for <see cref=\"pathname.normalize\"/>.\n\t/// </summary>\n\t[Flags]\n\tpublic enum PNFlags {\n\t\t/// <summary>Don't call API <ms>GetLongPathName</ms>.</summary>\n\t\tDontExpandDosPath = 1,\n\n\t\t/// <summary>Don't call <see cref=\"pathname.prefixLongPathIfNeed\"/>.</summary>\n\t\tDontPrefixLongPath = 2,\n\n\t\t/// <summary>Don't remove <c>\\</c> character at the end.</summary>\n\t\tDontRemoveEndSeparator = 4,\n\n\t\t/// <summary>If path is not a file-system path but looks like URL (eg <c>\"http:...\"</c> or <c>\"file:...\"</c>) or starts with <c>\"::\"</c>, don't throw exception and don't process more (only expand environment variables).</summary>\n\t\tCanBeUrlOrShell = 8,\n\t}\n}\n"
  },
  {
    "path": "Au/Files, data/shortcutFile.cs",
    "content": "namespace Au {\n\t/// <summary>\n\t/// Creates shell shortcuts (<c>.lnk</c> files) and gets shortcut properties.\n\t/// </summary>\n\tpublic unsafe sealed class shortcutFile : IDisposable {\n\t\tApi.IShellLink _isl;\n\t\tApi.IPersistFile _ipf;\n\t\tstring _lnkPath;\n\t\tbool _isOpen;\n\t\tbool _changedHotkey;\n\n\t\t/// <summary>\n\t\t/// Releases internally used COM objects (<c>IShellLink</c>, <c>IPersistFile</c>).\n\t\t/// </summary>\n\t\tpublic void Dispose() {\n\t\t\tif (_isl != null) {\n\t\t\t\tApi.ReleaseComObject(_ipf); _ipf = null;\n\t\t\t\tApi.ReleaseComObject(_isl); _isl = null;\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Returns the internally used <c>IShellLink</c> COM interface.\n\t\t/// </summary>\n\t\tinternal Api.IShellLink IShellLink => _isl;\n\t\t//This could be public, but then need to make IShellLink public. It is defined in a non-standard way. Never mind, it is not important.\n\n\t\tshortcutFile(string lnkPath, uint mode) {\n\t\t\t_isl = new Api.ShellLink() as Api.IShellLink;\n\t\t\t_ipf = _isl as Api.IPersistFile;\n\t\t\t_lnkPath = pathname.normalize(lnkPath);\n\t\t\tif (mode != Api.STGM_WRITE && (mode == Api.STGM_READ || filesystem.exists(_lnkPath).File)) {\n\t\t\t\tAuException.ThrowIfHresultNot0(_ipf.Load(_lnkPath, mode), \"*open\");\n\t\t\t\t_isOpen = true;\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Opens a shortcut file (<c>.lnk</c>) for getting shortcut properties.\n\t\t/// </summary>\n\t\t/// <param name=\"lnkPath\">Shortcut file (<c>.lnk</c>) path.</param>\n\t\t/// <exception cref=\"ArgumentException\">Not full path.</exception>\n\t\t/// <exception cref=\"AuException\">Failed to open <c>.lnk</c> file.</exception>\n\t\tpublic static shortcutFile open(string lnkPath) {\n\t\t\treturn new shortcutFile(lnkPath, Api.STGM_READ);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Creates a new <see cref=\"shortcutFile\"/> instance that can be used to create or replace a shortcut file.\n\t\t/// </summary>\n\t\t/// <param name=\"lnkPath\">Shortcut file (<c>.lnk</c>) path.</param>\n\t\t/// <exception cref=\"ArgumentException\">Not full path.</exception>\n\t\t/// <remarks>\n\t\t/// You can set properties and finally call <see cref=\"Save\"/>.\n\t\t/// If the shortcut file already exists, <c>Save</c> replaces it.\n\t\t/// </remarks>\n\t\tpublic static shortcutFile create(string lnkPath) {\n\t\t\treturn new shortcutFile(lnkPath, Api.STGM_WRITE);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Creates a new <see cref=\"shortcutFile\"/> instance that can be used to create or modify a shortcut file.\n\t\t/// </summary>\n\t\t/// <param name=\"lnkPath\">Shortcut file (<c>.lnk</c>) path.</param>\n\t\t/// <exception cref=\"ArgumentException\">Not full path.</exception>\n\t\t/// <exception cref=\"AuException\">Failed to open existing <c>.lnk</c> file.</exception>\n\t\t/// <remarks>\n\t\t/// Exception if file exists but cannot open it for read-write access.\n\t\t/// You can get and set properties and finally call <see cref=\"Save\"/>.\n\t\t/// If the shortcut file already exists, <c>Save</c> updates it.\n\t\t/// </remarks>\n\t\tpublic static shortcutFile openOrCreate(string lnkPath) {\n\t\t\treturn new shortcutFile(lnkPath, Api.STGM_READWRITE);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Saves to the shortcut file (<c>.lnk</c>).\n\t\t/// </summary>\n\t\t/// <exception cref=\"AuException\">Failed to save.</exception>\n\t\t/// <remarks>\n\t\t/// Creates parent folder if need.\n\t\t/// </remarks>\n\t\tpublic void Save() {\n\t\t\tif (_changedHotkey && !_isOpen && filesystem.exists(_lnkPath).File) _UnregisterHotkey(_lnkPath);\n\n\t\t\tfilesystem.createDirectoryFor(_lnkPath);\n\t\t\tAuException.ThrowIfHresultNot0(_ipf.Save(_lnkPath, true), \"*save\");\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets or sets shortcut target path.\n\t\t/// </summary>\n\t\t/// <value><c>null</c> if target isn't a file system object, eg Control Panel or URL.</value>\n\t\t/// <remarks>The <c>get</c> function gets path with expanded environment variables. If possible, it corrects the target of MSI shortcuts and 64-bit Program Files shortcuts where <c>IShellLink.GetPath</c> lies.</remarks>\n\t\t/// <exception cref=\"AuException\">The <c>set</c> function failed.</exception>\n\t\t/// <exception cref=\"ArgumentException\">The <c>set</c> function allows max length 259.</exception>\n\t\tpublic string TargetPath {\n\t\t\tget => _CorrectPath(_GetString(_WhatString.Path), true);\n\t\t\tset { AuException.ThrowIfHresultNot0(_isl.SetPath(_Max259(value))); }\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets shortcut target path and does not correct wrong MSI shortcut target.\n\t\t/// </summary>\n\t\tpublic string TargetPathRawMSI {\n\t\t\tget => _CorrectPath(_GetString(_WhatString.Path));\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets or sets a non-file-system target (eg Control Panel) through its <ms>ITEMIDLIST</ms>.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Also can be used for any target type, but gets raw value, for example MSI shortcut target is incorrect.\n\t\t/// Most but not all shortcuts have this property; the <c>get</c> function returns <c>null</c> if the shortcut does not have it.\n\t\t/// </remarks>\n\t\t/// <exception cref=\"AuException\">The <c>set</c> function failed.</exception>\n\t\tpublic Pidl TargetPidl {\n\t\t\tget => (0 == _isl.GetIDList(out var pidl)) ? new Pidl(pidl) : null;\n\t\t\tset { AuException.ThrowIfHresultNot0(_isl.SetIDList(value?.UnsafePtr ?? default)); GC.KeepAlive(value); }\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets or sets a URL target.\n\t\t/// Note: it is a <c>.lnk</c> shortcut, not a <c>.url</c> shortcut.\n\t\t/// </summary>\n\t\t/// <value>The <c>get</c> function returns string <c>\"file:///...\"</c> if target is a file.</value>\n\t\t/// <exception cref=\"AuException\">The <c>set</c> function failed.</exception>\n\t\tpublic string TargetURL {\n\t\t\tget {\n\t\t\t\tif (0 != _isl.GetIDList(out var pidl)) return null;\n\t\t\t\ttry { return Pidl.ToShellString(pidl, SIGDN.URL); }\n\t\t\t\tfinally { Marshal.FreeCoTaskMem(pidl); }\n\t\t\t}\n\t\t\tset {\n\t\t\t\tTargetAnyType = value;\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets or sets target of any type - file/folder, URL, virtual shell object (see <see cref=\"Pidl\"/>).\n\t\t/// The string can be used with <see cref=\"run.it\"/>.\n\t\t/// </summary>\n\t\t/// <exception cref=\"AuException\">The <c>set</c> function failed.</exception>\n\t\tpublic string TargetAnyType {\n\t\t\tget {\n\t\t\t\tvar R = TargetPath; if (R != null) return R; //support MSI etc\n\t\t\t\tif (0 != _isl.GetIDList(out var pidl)) return null;\n\t\t\t\ttry { return Pidl.ToString(pidl); } finally { Marshal.FreeCoTaskMem(pidl); }\n\t\t\t}\n\t\t\tset {\n\t\t\t\tvar pidl = Pidl.FromString_(value, true);\n\t\t\t\ttry { AuException.ThrowIfHresultNot0(_isl.SetIDList(pidl)); } finally { Marshal.FreeCoTaskMem(pidl); }\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets custom icon file path and icon index.\n\t\t/// </summary>\n\t\t/// <returns><c>null</c> if the shortcut does not have a custom icon (then you see its target icon).</returns>\n\t\t/// <param name=\"iconIndex\">Receives 0 or icon index or negative icon resource id.</param>\n\t\t[SkipLocalsInit]\n\t\tpublic string GetIconLocation(out int iconIndex) {\n\t\t\tvar b = stackalloc char[1024];\n\t\t\tif (0 != _isl.GetIconLocation(b, 1024, out iconIndex)) return null;\n\t\t\treturn _CorrectPath(new(b));\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Sets icon file path and icon index.\n\t\t/// </summary>\n\t\t/// <param name=\"path\"></param>\n\t\t/// <param name=\"iconIndex\">0 or icon index or negative icon resource id.</param>\n\t\t/// <exception cref=\"AuException\"/>\n\t\t/// <exception cref=\"ArgumentException\">Max length 259.</exception>\n\t\tpublic void SetIconLocation(string path, int iconIndex = 0) {\n\t\t\tAuException.ThrowIfHresultNot0(_isl.SetIconLocation(_Max259(path), iconIndex));\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets or sets the working directory path.\n\t\t/// </summary>\n\t\t/// <exception cref=\"AuException\">The <c>set</c> function failed.</exception>\n\t\t/// <exception cref=\"ArgumentException\">The <c>set</c> function allows max length 259.</exception>\n\t\tpublic string WorkingDirectory {\n\t\t\tget => _CorrectPath(_GetString(_WhatString.WorkingDirectory));\n\t\t\tset { AuException.ThrowIfHresultNot0(_isl.SetWorkingDirectory(_Max259(value))); } //see Description comments\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Throws if <i>s</i> longer than 259.\n\t\t/// <c>SetPath</c> then would throw without error description. <c>SetIconLocation</c> would limit to 260. <c>SetWorkingDirectory</c> and <c>SetDescription</c> succeed but corrupt the <c>.lnk</c> file. <c>SetArguments</c> OK. Others not tested, rare, never mind.\n\t\t/// </summary>\n\t\tstatic string _Max259(string s) => s.Lenn() <= 259 ? s : throw new ArgumentException(\"max length 259\");\n\n\t\t/// <summary>\n\t\t/// Gets or sets the command-line arguments.\n\t\t/// </summary>\n\t\t/// <exception cref=\"AuException\">The <c>set</c> function failed.</exception>\n\t\tpublic string Arguments {\n\t\t\tget => _GetString(_WhatString.Arguments);\n\t\t\tset { AuException.ThrowIfHresultNot0(_isl.SetArguments(value)); }\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets or sets the description text (comment).\n\t\t/// </summary>\n\t\t/// <exception cref=\"AuException\">The <c>set</c> function failed.</exception>\n\t\t/// <exception cref=\"ArgumentException\">The <c>set</c> function allows max length 259.</exception>\n\t\tpublic string Description {\n\t\t\tget => _GetString(_WhatString.Description);\n\t\t\tset { AuException.ThrowIfHresultNot0(_isl.SetDescription(_Max259(value))); }\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets or sets hotkey.\n\t\t/// </summary>\n\t\t/// <exception cref=\"ArgumentException\">The value for the <c>set</c> function includes <c>Win</c>.</exception>\n\t\t/// <exception cref=\"AuException\">The <c>set</c> function failed.</exception>\n\t\tpublic (KMod, KKey) Hotkey {\n\t\t\tget {\n\t\t\t\tif (0 != _isl.GetHotkey(out ushort k2)) return default;\n\t\t\t\tuint k = k2;\n\t\t\t\treturn ((KMod)(k >> 8 & 7), (KKey)(k & 0xFF));\n\t\t\t}\n\t\t\tset {\n\t\t\t\tvar (m, k) = value;\n\t\t\t\tif (0 != (m & ~(KMod.Ctrl | KMod.Alt | KMod.Shift))) throw new ArgumentException(\"Win not supported\");\n\t\t\t\tAuException.ThrowIfHresultNot0(_isl.SetHotkey(Math2.MakeWord((uint)k, (uint)m)));\n\t\t\t\t_changedHotkey = true;\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets or sets the window show state.\n\t\t/// Most programs ignore it.\n\t\t/// </summary>\n\t\t/// <value>1 (normal, default), 2 (minimized) or 3 (maximized).</value>\n\t\t/// <exception cref=\"AuException\">The <c>set</c> function failed.</exception>\n\t\tpublic int ShowState {\n\t\t\tget => (0 == _isl.GetShowCmd(out var R)) ? R : Api.SW_SHOWNORMAL;\n\t\t\tset { AuException.ThrowIfHresultNot0(_isl.SetShowCmd(value)); }\n\t\t}\n\n\t\t//Not implemented wrappers for these IShellLink methods:\n\t\t//SetRelativePath, Resolve - not useful.\n\t\t//All are easy to call through the IShellLink property.\n\n\t\t#region public static\n\n\t\t/// <summary>\n\t\t/// Gets shortcut target path. It also can be a URL or a <ms>ITEMIDLIST</ms> string (see <see cref=\"Pidl\"/>) of a virtual shell object.\n\t\t/// Uses <see cref=\"open\"/> and <see cref=\"TargetAnyType\"/>.\n\t\t/// </summary>\n\t\t/// <param name=\"lnkPath\">Shortcut file (<c>.lnk</c>) path.</param>\n\t\t/// <exception cref=\"AuException\">Failed to open.</exception>\n\t\tpublic static string getTarget(string lnkPath) {\n\t\t\treturn open(lnkPath).TargetAnyType;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// If shortcut file exists, unregisters its hotkey and deletes it.\n\t\t/// </summary>\n\t\t/// <param name=\"lnkPath\"><c>.lnk</c> file path.</param>\n\t\t/// <exception cref=\"AuException\">Failed to unregister hotkey.</exception>\n\t\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"filesystem.delete(string, FDFlags)\"/>.</exception>\n\t\tpublic static void delete(string lnkPath) {\n\t\t\tif (!filesystem.exists(lnkPath).File) return;\n\t\t\t_UnregisterHotkey(lnkPath);\n\t\t\tfilesystem.delete(lnkPath);\n\t\t}\n\n\t\t#endregion\n\n\t\t#region private\n\n\t\t/// <exception cref=\"AuException\">Failed to open or save.</exception>\n\t\tstatic void _UnregisterHotkey(string lnkPath) {\n\t\t\tDebug.Assert(filesystem.exists(lnkPath).File);\n\t\t\tusing var x = openOrCreate(lnkPath);\n\t\t\tvar k = x.Hotkey;\n\t\t\tif (k != default) {\n\t\t\t\tx.Hotkey = default;\n\t\t\t\tx.Save();\n\t\t\t}\n\t\t}\n\n\t\tenum _WhatString { Path, Arguments, WorkingDirectory, Description }\n\n\t\t[SkipLocalsInit]\n\t\tstring _GetString(_WhatString what) {\n\t\t\tusing FastBuffer<char> b = new();\n\t\t\tfor (; ; ) {\n\t\t\t\tint hr = 1;\n\t\t\t\tswitch (what) {\n\t\t\t\tcase _WhatString.Path: hr = _isl.GetPath(b.p, b.n); break;\n\t\t\t\tcase _WhatString.Arguments: hr = _isl.GetArguments(b.p, b.n); break;\n\t\t\t\tcase _WhatString.WorkingDirectory: hr = _isl.GetWorkingDirectory(b.p, b.n); break;\n\t\t\t\tcase _WhatString.Description: hr = _isl.GetDescription(b.p, b.n); break;\n\t\t\t\t}\n\t\t\t\tif (hr != 0) return null;\n\t\t\t\tif (b.GetString(b.FindStringLength(), out var s, BSFlags.Truncates)) return s;\n\t\t\t}\n\t\t}\n\n\t\tstring _CorrectPath(string R, bool fixMSI = false) {\n\t\t\tif (R.NE()) return null;\n\n\t\t\tif (!fixMSI) {\n\t\t\t\tR = pathname.expand(R);\n\t\t\t} else if (R.Find(@\"\\Installer\\{\") > 0) {\n\t\t\t\t//For MSI shortcuts GetPath gets like \"C:\\WINDOWS\\Installer\\{90110409-6000-11D3-8CFE-0150048383C9}\\accicons.exe\".\n\t\t\t\tvar product = stackalloc char[40];\n\t\t\t\tvar component = stackalloc char[40];\n\t\t\t\tif (0 != Api.MsiGetShortcutTarget(_lnkPath, product, null, component)) return null;\n\t\t\t\t//note: for some shortcuts MsiGetShortcutTarget gets empty component. Then MsiGetComponentPath fails.\n\t\t\t\t//\tOn my PC was 1 such shortcut - Microsoft Office Excel Viewer.lnk in start menu.\n\t\t\t\t//\tCould not find a workaround.\n\n\t\t\t\tint na = 1024; var b = stackalloc char[na];\n\t\t\t\tint hr = Api.MsiGetComponentPath(product, component, b, ref na);\n\t\t\t\tif (hr < 0) return null; //eg not installed, just advertised\n\t\t\t\tif (na == 0) return null;\n\t\t\t\tR = new(b, 0, na);\n\t\t\t\t//note: can be a registry key instead of file path. No such shortcuts on my PC.\n\t\t\t}\n\n\t\t\t//GetPath problem: replaces \"c:\\program files\" with \"c:\\program files (x86)\".\n\t\t\t//These don't help: SLGP_RAWPATH, GetIDList, disabled redirection.\n\t\t\t//GetWorkingDirectory and GetIconLocation get raw path, and envronment variables such as %ProgramFiles% are expanded to (x86) in 32-bit process.\n\t\t\tif (osVersion.is32BitProcessAnd64BitOS) {\n\t\t\t\tif (_pf == null) { string s = folders.ProgramFilesX86; _pf = s + \"\\\\\"; }\n\t\t\t\tif (R.Starts(_pf, true) && !filesystem.exists(R)) {\n\t\t\t\t\tvar s2 = R.Remove(_pf.Length - 7, 6);\n\t\t\t\t\tif (filesystem.exists(s2)) R = s2;\n\t\t\t\t\t//info: \"C:\\\\Program Files (x86)\\\\\" in English, \"C:\\\\Programme (x86)\\\\\" in German etc.\n\t\t\t\t\t//never mind: System32 folder also has similar problem, because of redirection.\n\t\t\t\t\t//note: ShellExecuteEx also has this problem.\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn R;\n\t\t}\n\t\tstatic string _pf;\n\n\t\t#endregion\n\t}\n}\n"
  },
  {
    "path": "Au/Files, data/sqlite.cs",
    "content": "namespace Au {\n\t/// <summary>\n\t/// A SQLite database connection.\n\t/// Creates/opens/closes database file or in-memory database. Executes SQL, etc.\n\t/// </summary>\n\t/// <remarks>\n\t/// This class wraps a SQLite API object <c>sqlite3*</c> and related <c>sqlite3_x</c> functions. They are documented in the SQLite website.\n\t/// \n\t/// To correctly close the database file, at first need to dispose all child objects, such as <see cref=\"sqliteStatement\"/>, then dispose the <c>sqlite</c> object. To dispose a static <c>sqlite</c> variable, you may want to use <see cref=\"process.thisProcessExit\"/> event. Although this class has a finalizer that disposes the object (closes database), you should always dispose explicitly. Finalizers don't run on process exit.\n\t/// </remarks>\n\t/// <seealso cref=\"sqliteStatement\"/>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// //open database file\n\t/// using var db = new sqlite(@\"C:\\test\\sqlite.db\");\n\t/// //create table\n\t/// db.Execute(\"CREATE TABLE IF NOT EXISTS test(id INTEGER PRIMARY KEY, name TEXT, x INT, guid BLOB, array BLOB)\");\n\t/// \n\t/// //add 2 rows of data\n\t/// using(var trans = db.Transaction()) { //optional, but makes much faster when making multiple changes, and ensures that all or none of these changes are written to the database\n\t/// \tusing(var p = db.Statement(\"INSERT OR REPLACE INTO test VALUES(?, ?, :x, ?, ?)\")) {\n\t/// \t\t//assume we want to add values of these variables to the database table\n\t/// \t\tint id = 1; string name = \"TEXT\"; long x = -10; Guid guid = Guid.NewGuid(); int[] arr = new int[] { 1, 2, 3 };\n\t/// \t\t//add first row\n\t/// \t\tp.Bind(1, id);\n\t/// \t\tp.Bind(2, name).BindStruct(4, guid).Bind(5, arr);\n\t/// \t\tp.Bind(\":x\", x);\n\t/// \t\tp.Step();\n\t/// \t\t//add second row\n\t/// \t\tp.Reset().Bind(1, 2).Bind(\":x\", 123456789012345).Step(); //unbound columns are null\n\t/// \t}\n\t/// \t//update single row\n\t/// \tdb.Execute(\"UPDATE test SET name=?2 WHERE id=?1\", 2, \"two\");\n\t/// \t//write all this to database\n\t/// \ttrans.Commit();\n\t/// }\n\t/// \n\t/// //get data\n\t/// using(var p = db.Statement(\"SELECT * FROM test\")) {\n\t/// \twhile(p.Step()) { //for each row of results\n\t/// \t\tprint.it(p.GetInt(0), p.GetText(1), p.GetLong(2));\n\t/// \t\tprint.it(p.GetStruct<Guid>(\"guid\"));\n\t/// \t\tprint.it(p.GetArray<int>(\"array\"));\n\t/// \t\tprint.it(\"----\");\n\t/// \t}\n\t/// }\n\t/// //get single value\n\t/// if(db.Get(out string s1, \"SELECT name FROM test WHERE id=?\", 1)) print.it(s1); else print.it(\"not found\");\n\t/// if(db.Get(out int i1, \"PRAGMA page_size\")) print.it(i1);\n\t/// ]]></code>\n\t/// </example>\n\tpublic unsafe class sqlite : IDisposable {\n\t\tIntPtr _db;\n\t\t\n\t\t/// <summary>\n\t\t/// Opens or creates a database file.\n\t\t/// </summary>\n\t\t/// <param name=\"file\">\n\t\t/// Database file. Can be:\n\t\t/// <br/>• Full path. Supports environment variables etc, see <see cref=\"pathname.expand\"/>\n\t\t/// <br/>• <c>\":memory:\"</c> - create a private, temporary in-memory database.\n\t\t/// <br/>• <c>\"\"</c> - create a private, temporary on-disk database.\n\t\t/// <br/>• Starts with <c>\"file:\"</c> - see <google>sqlite3_open_v2</google>.\n\t\t/// </param>\n\t\t/// <param name=\"flags\"><google>sqlite3_open_v2</google> flags. Default: read-write, create file if does not exist (and parent directory).</param>\n\t\t/// <param name=\"sql\">\n\t\t/// SQL to execute. For example, one or more <c>;</c>-separated <c>PRAGMA</c> statements to configure the database connection. Or even <c>\"CREATE TABLE IF NOT EXISTS ...\"</c>.\n\t\t/// This function also always executes <c>\"PRAGMA foreign_keys=ON;PRAGMA secure_delete=ON;\"</c>.\n\t\t/// </param>\n\t\t/// <exception cref=\"ArgumentException\">Not full path.</exception>\n\t\t/// <exception cref=\"SLException\">Failed to open database or execute <i>sql</i>.</exception>\n\t\t/// <remarks>\n\t\t/// Calls <google>sqlite3_open_v2</google>.\n\t\t/// \n\t\t/// Sets busy timeout 2000 ms.\n\t\t/// \n\t\t/// <note>If a variable of this class is used by multiple threads, use <c>lock(variable) {  }</c> where need.</note>\n\t\t/// </remarks>\n\t\tpublic sqlite(string file, SLFlags flags = SLFlags.ReadWriteCreate, string sql = null) {\n\t\t\tDebug.Assert(Assembly.GetCallingAssembly() != typeof(sqlite).Assembly); //don't use sqlite in Au\n\t\t\t\n\t\t\tbool isSpec = file != null && (file.Length == 0 || file == \":memory:\" || file.Starts(\"file:\"));\n\t\t\tif (!isSpec) {\n\t\t\t\tfile = pathname.normalize(file);\n\t\t\t\tif (flags.Has(SLFlags.SQLITE_OPEN_CREATE) && !filesystem.exists(file, true).File) filesystem.createDirectoryFor(file);\n\t\t\t}\n\t\t\tvar r = SLApi.sqlite3_open_v2(Convert2.Utf8Encode(file), ref _db, flags, null);\n\t\t\tif (r != 0) {\n\t\t\t\tDispose();\n\t\t\t\tthrow new SLException(r, \"sqlite3_open \" + file);\n\t\t\t}\n\t\t\tSLApi.sqlite3_busy_timeout(_db, 2000);\n\t\t\tExecute(\"PRAGMA foreign_keys=ON;PRAGMA secure_delete=ON;\" + sql);\n\t\t}\n\t\t\n\t\t///\n\t\tprotected virtual void Dispose(bool disposing) {\n\t\t\tif (_db != default) {\n\t\t\t\tvar r = SLApi.sqlite3_close_v2(_db);\n\t\t\t\tDebug.Assert(r == 0); SLUtil_.Warn(r, \"sqlite3_close\");\n\t\t\t\t_db = default;\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Calls <sqlite>sqlite3_close_v2</sqlite>.\n\t\t/// If fails, prints a warning.\n\t\t/// </summary>\n\t\tpublic void Dispose() {\n\t\t\tDispose(true);\n\t\t\tGC.SuppressFinalize(this);\n\t\t}\n\t\t\n\t\t///\n\t\t~sqlite() => Dispose(false);\n\t\t\n\t\t/// <summary><c>sqlite3*</c></summary>\n\t\tpublic static implicit operator IntPtr(sqlite c) => c._db;\n\t\t\n\t\t/// <summary><c>sqlite3*</c></summary>\n\t\tpublic IntPtr Handle => _db;\n\t\t\n\t\t/// <summary>\n\t\t/// Calls <sqlite>sqlite3_exec</sqlite> to execute one or more SQL statements that don't return data.\n\t\t/// </summary>\n\t\t/// <param name=\"sql\">SQL statement, or several <c>;</c>-separated statements.</param>\n\t\t/// <exception cref=\"SLException\">Failed to execute <i>sql</i>.</exception>\n\t\tpublic void Execute(string sql) {\n\t\t\tvar b = Convert2.Utf8Encode(sql);\n\t\t\tbyte* es = null; //gets better error text than sqlite3_errstr; sqlite3_errmsg gets nothing after sqlite3_exec.\n\t\t\tvar r = SLApi.sqlite3_exec(_db, b, default, default, &es);\n\t\t\tif (r != 0) throw new SLException(r, \"sqlite3_exec\", SLUtil_.ToStringAndFree(es));\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Executes single SQL statement that does not return data. Binds values.\n\t\t/// </summary>\n\t\t/// <param name=\"sql\">Single SQL statement.</param>\n\t\t/// <param name=\"bind\">\n\t\t/// Values that will replace <c>?</c> characters in <i>sql</i>.\n\t\t/// Read about SQL parameters in SQLite website. Supported types: <see cref=\"sqliteStatement.BindAll\"/>. Example: <see cref=\"sqlite\"/>.\n\t\t/// </param>\n\t\t/// <exception cref=\"SLException\">Failed.</exception>\n\t\t/// <exception cref=\"NotSupportedException\"><i>sql</i> contains more than single SQL statement.</exception>\n\t\tpublic void Execute(string sql, params object[] bind) {\n\t\t\tusing var p = Statement(sql, bind);\n\t\t\tp.Step();\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Executes single SQL statement that does not return data. To bind values calls callback function.\n\t\t/// </summary>\n\t\t/// <param name=\"sql\">Single SQL statement.</param>\n\t\t/// <param name=\"bind\">\n\t\t/// Callback function that should bind (<see cref=\"sqliteStatement.Bind\"/>) values to <c>?</c> characters in <i>sql</i>.\n\t\t/// Read about SQL parameters in SQLite website.\n\t\t/// </param>\n\t\t/// <exception cref=\"SLException\">Failed.</exception>\n\t\t/// <exception cref=\"NotSupportedException\"><i>sql</i> contains more than single SQL statement.</exception>\n\t\tpublic void Execute(string sql, Action<sqliteStatement> bind) {\n\t\t\tusing var p = Statement(sql);\n\t\t\tbind(p);\n\t\t\tp.Step();\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Returns <c>new sqliteStatement(this, sql)</c>.\n\t\t/// </summary>\n\t\t/// <param name=\"sql\">Single SQL statement. This function does not execute it.</param>\n\t\t/// <seealso cref=\"sqliteStatement\"/>\n\t\t/// <inheritdoc cref=\"sqliteStatement(sqlite, string, bool)\"/>\n\t\tpublic sqliteStatement Statement(string sql) => new sqliteStatement(this, sql);\n\t\t\n\t\t/// <summary>\n\t\t/// Returns <c>new sqliteStatement(this, sql).BindAll(bind)</c>.\n\t\t/// </summary>\n\t\t/// <param name=\"sql\">Single SQL statement. This function does not execute it.</param>\n\t\t/// <param name=\"bind\">\n\t\t/// Values that will replace <c>?</c> characters in <i>sql</i>. Optional.\n\t\t/// Read about SQL parameters in SQLite website. Supported types: <see cref=\"sqliteStatement.BindAll\"/>. Example: <see cref=\"sqlite\"/>.\n\t\t/// </param>\n\t\t/// <exception cref=\"NotSupportedException\">\n\t\t/// - <i>sql</i> contains more than single SQL statement.\n\t\t/// - A <i>bind</i> value is of an unsupported type.\n\t\t/// </exception>\n\t\t/// <seealso cref=\"sqliteStatement\"/>\n\t\t/// <seealso cref=\"sqliteStatement.BindAll\"/>\n\t\t/// <inheritdoc cref=\"sqliteStatement(sqlite, string, bool)\"/>\n\t\t/// <inheritdoc cref=\"sqliteStatement.BindAll\" path=\"/param\"/>\n\t\tpublic sqliteStatement Statement(string sql, params object[] bind) => new sqliteStatement(this, sql).BindAll(bind);\n\t\t\n\t\t#region get\n\t\t\n\t\t/// <summary>\n\t\t/// Executes single SQL statement and gets single value.\n\t\t/// </summary>\n\t\t/// <returns><c>false</c> if the statement returned no data.</returns>\n\t\t/// <param name=\"value\">Receives data.</param>\n\t\t/// <exception cref=\"SLException\">Failed.</exception>\n\t\t/// <exception cref=\"NotSupportedException\"><i>sql</i> contains more than single SQL statement.</exception>\n\t\t/// <remarks>\n\t\t/// Also can be used to get <c>uint</c>, <c>short</c>, <c>ushort</c>, <c>byte</c>, <c>sbyte</c>, enum. Will need to cast from <c>int</c>.\n\t\t/// </remarks>\n\t\t/// <inheritdoc cref=\"Execute(string, object[])\" path=\"/param\"/>\n\t\tpublic bool Get(out int value, string sql, params object[] bind) {\n\t\t\tusing var p = Statement(sql, bind);\n\t\t\tbool R = p.Step();\n\t\t\tvalue = R ? p.GetInt(0) : default;\n\t\t\treturn R;\n\t\t}\n\t\t\n\t\t/// <remarks>\n\t\t/// Also can be used to get <c>ulong</c>, 64-bit enum, maybe <see cref=\"DateTime\"/>.\n\t\t/// </remarks>\n\t\t/// <inheritdoc cref=\"Get(out int, string, object[])\"/>\n\t\tpublic bool Get(out long value, string sql, params object[] bind) {\n\t\t\tusing var p = Statement(sql, bind);\n\t\t\tbool R = p.Step();\n\t\t\tvalue = R ? p.GetLong(0) : default;\n\t\t\treturn R;\n\t\t}\n\t\t\n\t\t///// <remarks></remarks>\n\t\t///// <inheritdoc cref=\"Get(out int, string, object[])\"/>\n\t\t//public bool Get(out DateTime value, bool convertToLocal, string sql, params object[] bind)\n\t\t//{\n\t\t//\tusing var p = Prepare(sql, bind);\n\t\t//\tbool R = p.Step();\n\t\t//\tvalue = R ? p.GetDateTime(0, convertToLocal) : default;\n\t\t//\treturn R;\n\t\t//}\n\t\t\n\t\t/// <remarks></remarks>\n\t\t/// <inheritdoc cref=\"Get(out int, string, object[])\"/>\n\t\tpublic bool Get(out bool value, string sql, params object[] bind) {\n\t\t\tusing var p = Statement(sql, bind);\n\t\t\tbool R = p.Step();\n\t\t\tvalue = R ? p.GetBool(0) : default;\n\t\t\treturn R;\n\t\t}\n\t\t\n\t\t/// <remarks>\n\t\t/// Also can be used to get <c>float</c>.\n\t\t/// </remarks>\n\t\t/// <inheritdoc cref=\"Get(out int, string, object[])\"/>\n\t\tpublic bool Get(out double value, string sql, params object[] bind) {\n\t\t\tusing var p = Statement(sql, bind);\n\t\t\tbool R = p.Step();\n\t\t\tvalue = R ? p.GetDouble(0) : default;\n\t\t\treturn R;\n\t\t}\n\t\t\n\t\t/// <remarks></remarks>\n\t\t/// <inheritdoc cref=\"Get(out int, string, object[])\"/>\n\t\tpublic bool Get(out string value, string sql, params object[] bind) {\n\t\t\tusing var p = Statement(sql, bind);\n\t\t\tbool R = p.Step();\n\t\t\tvalue = R ? p.GetText(0) : default;\n\t\t\treturn R;\n\t\t}\n\t\t\n\t\t/// <remarks></remarks>\n\t\t/// <inheritdoc cref=\"Get(out int, string, object[])\"/>\n\t\tpublic bool Get<T>(out T[] value, string sql, params object[] bind) where T : unmanaged {\n\t\t\tusing var p = Statement(sql, bind);\n\t\t\tbool R = p.Step();\n\t\t\tvalue = R ? p.GetArray<T>(0) : default;\n\t\t\treturn R;\n\t\t}\n\t\t\n\t\t/// <remarks></remarks>\n\t\t/// <inheritdoc cref=\"Get(out int, string, object[])\"/>\n\t\tpublic bool Get<T>(out List<T> value, string sql, params object[] bind) where T : unmanaged {\n\t\t\tusing var p = Statement(sql, bind);\n\t\t\tbool R = p.Step();\n\t\t\tvalue = R ? p.GetList<T>(0) : default;\n\t\t\treturn R;\n\t\t}\n\t\t\n\t\t//note: can't add overload to get ReadOnlySpan<T>, because it becomes invalid when the func returns.\n\t\t\n\t\t/// <remarks>\n\t\t/// Can be used to get various value types, for example <c>decimal</c>, <see cref=\"Guid\"/>, <see cref=\"RECT\"/>.\n\t\t/// </remarks>\n\t\t/// <inheritdoc cref=\"Get(out int, string, object[])\"/>\n\t\tpublic bool GetStruct<T>(out T value, string sql, params object[] bind) where T : unmanaged {\n\t\t\tusing var p = Statement(sql, bind);\n\t\t\tbool R = p.Step();\n\t\t\tvalue = R ? p.GetStruct<T>(0) : default;\n\t\t\treturn R;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Executes single SQL statement and returns <c>true</c> if it returns at least one row of data.\n\t\t/// </summary>\n\t\t/// <remarks>This function is similar to the <c>GetX</c> functions, but it does not retrieve the data.</remarks>\n\t\t/// <inheritdoc cref=\"Get(out int, string, object[])\" path=\"//param|//exception\"/>\n\t\tpublic bool Any(string sql, params object[] bind) {\n\t\t\tusing var p = Statement(sql, bind);\n\t\t\treturn p.Step();\n\t\t}\n\t\t\n\t\t#endregion\n\t\t\n\t\t/// <summary>\n\t\t/// Calls <sqlite>sqlite3_last_insert_rowid</sqlite>.\n\t\t/// </summary>\n\t\tpublic long LastInsertRowid => SLApi.sqlite3_last_insert_rowid(_db);\n\t\t\n\t\t/// <summary>\n\t\t/// Calls <sqlite>sqlite3_changes</sqlite>.\n\t\t/// </summary>\n\t\tpublic int Changes => SLApi.sqlite3_changes(_db);\n\t\t\n\t\t/// <summary>\n\t\t/// Calls <sqlite>sqlite3_get_autocommit</sqlite>.\n\t\t/// </summary>\n\t\tpublic bool IsInTransaction => 0 == SLApi.sqlite3_get_autocommit(_db);\n\t\t\n\t\t/// <summary>\n\t\t/// Returns <c>new SLTransaction(this, sql, sqlOfDispose)</c>.\n\t\t/// See <see cref=\"SLTransaction(sqlite, string, string)\"/>.\n\t\t/// </summary>\n\t\t/// <inheritdoc cref=\"SLTransaction(sqlite, string, string)\"/>\n\t\tpublic SLTransaction Transaction(string sql = \"BEGIN\", string sqlOfDispose = \"ROLLBACK\")\n\t\t\t=> new SLTransaction(this, sql, sqlOfDispose);\n\t\t\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if the table exists.\n\t\t/// </summary>\n\t\t/// <param name=\"table\">Table name.</param>\n\t\t/// <remarks>\n\t\t/// This function is slower than <c>\"CREATE TABLE IF NOT EXISTS...\"</c>.\n\t\t/// </remarks>\n\t\tpublic bool TableExists(string table) {\n\t\t\treturn Any(\"SELECT 1 FROM sqlite_master WHERE type='table' AND name=?\", table);\n\t\t}\n\t\t\n\t\t#region util\n\t\t\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if default database text encoding is not UTF-8.\n\t\t/// </summary>\n\t\tinternal bool IsUtf16 {\n\t\t\tget {\n\t\t\t\tif (__isUtf16 == 0) {\n\t\t\t\t\tvar t = _RawGetText(\"PRAGMA encoding\") ?? \"\";\n\t\t\t\t\t__isUtf16 = (byte)(t.Starts(\"UTF-16\") ? 2 : 1);\n\t\t\t\t}\n\t\t\t\treturn __isUtf16 == 2;\n\t\t\t}\n\t\t}\n\t\tbyte __isUtf16; //0 not queried, 1 utf8 or failed, 2 utf16\n\t\t\n\t\tstring _RawGetText(string sql) {\n\t\t\tstring r = null;\n\t\t\tfixed (char* p = sql) {\n\t\t\t\tIntPtr x = default;\n\t\t\t\tif (0 == SLApi.sqlite3_prepare16_v3(_db, p, -1, 0, ref x, null)) {\n\t\t\t\t\tvar e = SLApi.sqlite3_step(x);\n\t\t\t\t\tDebug.Assert(e == SLError.Row || e == SLError.Done);\n\t\t\t\t\tif (e == SLError.Row) r = Convert2.Utf8Decode(SLApi.sqlite3_column_text(x, 0));\n\t\t\t\t\tSLApi.sqlite3_finalize(x);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn r;\n\t\t}\n\t\t\n\t\t#endregion\n\t}\n\t\n\t/// <summary>\n\t/// Creates and executes a SQLite prepared statement.\n\t/// </summary>\n\t/// <remarks>\n\t/// This class wraps a SQLite API object <sqlite>sqlite3_stmt*</sqlite> and related <c>sqlite3_x</c> functions. They are documented perfectly in the SQLite website.\n\t/// More info and example: <see cref=\"sqlite\"/>.\n\t/// <note type=\"important\">A variable of this class can be used by multiple threads, but not simultaneously. Use <c>lock(database) {  }</c> where need.</note>\n\t/// </remarks>\n\tpublic unsafe class sqliteStatement : IDisposable {\n\t\tsqlite _db;\n\t\tIntPtr _st;\n\t\t\n\t\t/// <summary>\n\t\t/// Calls <sqlite>sqlite3_prepare16_v3</sqlite>.\n\t\t/// </summary>\n\t\t/// <param name=\"db\"></param>\n\t\t/// <param name=\"sql\">Single SQL statement.</param>\n\t\t/// <param name=\"persistent\">Use flag <c>SQLITE_PREPARE_PERSISTENT</c>.</param>\n\t\t/// <exception cref=\"SLException\">Failed.</exception>\n\t\t/// <exception cref=\"NotSupportedException\"><i>sql</i> contains more than single SQL statement.</exception>\n\t\tpublic sqliteStatement(sqlite db, string sql, bool persistent = false) {\n\t\t\tNot_.Null(db);\n\t\t\t_db = db;\n\t\t\tint flags = persistent ? 1 : 0; //SQLITE_PREPARE_PERSISTENT\n\t\t\tfixed (char* p = sql) {\n\t\t\t\tchar* tail = null;\n\t\t\t\t_Err(SLApi.sqlite3_prepare16_v3(db, p, sql.Length * 2, flags, ref _st, &tail), \"sqlite3_prepare\");\n\t\t\t\tif (tail != null && tail - p != sql.Length) throw new NotSupportedException(\"sql contains more than single SQL statement\");\n\t\t\t}\n\t\t}\n\t\t\n\t\t///\n\t\tprotected virtual void Dispose(bool disposing) {\n\t\t\tif (_st != default) {\n\t\t\t\t//note: don't throw, because sqlite3_finalize can return error of previous sqlite3_step etc\n\t\t\t\tSLApi.sqlite3_finalize(_st);\n\t\t\t\t_st = default;\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Calls <sqlite>sqlite3_finalize</sqlite>.\n\t\t/// </summary>\n\t\tpublic void Dispose() {\n\t\t\tDispose(true);\n\t\t\tGC.SuppressFinalize(this);\n\t\t}\n\t\t\n\t\t///\n\t\t~sqliteStatement() => Dispose(false);\n\t\t\n\t\t/// <summary><c>sqlite3_stmt*</c></summary>\n\t\tpublic static implicit operator IntPtr(sqliteStatement s) => s._st;\n\t\t\n\t\t/// <summary><c>sqlite3_stmt*</c></summary>\n\t\tpublic IntPtr Handle => _st;\n\t\t\n\t\t/// <summary>\n\t\t/// The database connection.\n\t\t/// </summary>\n\t\tpublic sqlite DB => _db;\n\t\t\n\t\t/// <summary>\n\t\t/// Calls <sqlite>sqlite3_step</sqlite>, and returns <c>true</c> if results data available (<sqlite>sqlite3_step</sqlite> returned <c>SQLITE_ROW</c>).\n\t\t/// </summary>\n\t\t/// <exception cref=\"SLException\">Failed.</exception>\n\t\tpublic bool Step() {\n\t\t\tvar r = SLApi.sqlite3_step(_st);\n\t\t\tif (r == SLError.Row) return true;\n\t\t\tif (r != SLError.Done) _Err(r, \"sqlite3_step\");\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Calls <sqlite>sqlite3_reset</sqlite> and/or <sqlite>sqlite3_clear_bindings</sqlite>.\n\t\t/// </summary>\n\t\t/// <returns>this.</returns>\n\t\t/// <param name=\"resetStatement\">Call <sqlite>sqlite3_reset</sqlite>. Default <c>true</c>.</param>\n\t\t/// <param name=\"clearBindings\">Call <sqlite>sqlite3_clear_bindings</sqlite>. Default <c>true</c>.</param>\n\t\tpublic sqliteStatement Reset(bool resetStatement = true, bool clearBindings = true) {\n\t\t\t//note: don't throw, because sqlite3_reset can return error of previous sqlite3_step\n\t\t\tif (resetStatement) SLApi.sqlite3_reset(_st);\n\t\t\tif (clearBindings) SLApi.sqlite3_clear_bindings(_st);\n\t\t\treturn this;\n\t\t}\n\t\t\n\t\t#region bind\n\t\t\n\t\tint _B(SLIndexOrName p) {\n\t\t\tif (p.name == null) return p.index;\n\t\t\tint r = SLApi.sqlite3_bind_parameter_index(_st, Convert2.Utf8Encode(p.name));\n\t\t\tif (r == 0) throw new SLException($\"Parameter '{p.name}' does not exist in the SQL statement.\");\n\t\t\treturn r;\n\t\t}\n\t\t\n\t\t/// <summary>Calls <sqlite>sqlite3_bind_int</sqlite>.</summary>\n\t\t/// <returns>this.</returns>\n\t\t/// <param name=\"sqlParam\">Parameter name or 1-based index.</param>\n\t\t/// <exception cref=\"SLException\">Failed.</exception>\n\t\tpublic sqliteStatement Bind(SLIndexOrName sqlParam, int value)\n\t\t\t=> _Err(SLApi.sqlite3_bind_int(_st, _B(sqlParam), value), \"sqlite3_bind_int\");\n\t\t\n\t\t/// <summary>Calls <sqlite>sqlite3_bind_int</sqlite>.</summary>\n\t\t/// <returns>this.</returns>\n\t\t/// <param name=\"sqlParam\">Parameter name or 1-based index.</param>\n\t\t/// <exception cref=\"SLException\">Failed.</exception>\n\t\tpublic sqliteStatement Bind(SLIndexOrName sqlParam, uint value)\n\t\t\t=> Bind(sqlParam, (int)value);\n\t\t\n\t\t/// <summary>Calls <sqlite>sqlite3_bind_int64</sqlite>.</summary>\n\t\t/// <returns>this.</returns>\n\t\t/// <param name=\"sqlParam\">Parameter name or 1-based index.</param>\n\t\t/// <exception cref=\"SLException\">Failed.</exception>\n\t\tpublic sqliteStatement Bind(SLIndexOrName sqlParam, long value)\n\t\t\t=> _Err(SLApi.sqlite3_bind_int64(_st, _B(sqlParam), value), \"sqlite3_bind_int64\");\n\t\t\n\t\t/// <summary>Calls <sqlite>sqlite3_bind_int64</sqlite>.</summary>\n\t\t/// <returns>this.</returns>\n\t\t/// <param name=\"sqlParam\">Parameter name or 1-based index.</param>\n\t\t/// <exception cref=\"SLException\">Failed.</exception>\n\t\tpublic sqliteStatement Bind(SLIndexOrName sqlParam, ulong value)\n\t\t\t=> Bind(sqlParam, (long)value);\n\t\t\n\t\t/// <summary>Calls <google>sqlite3_bind_int</google> (0 or 1).</summary>\n\t\t/// <returns>this.</returns>\n\t\t/// <param name=\"sqlParam\">Parameter name or 1-based index.</param>\n\t\t/// <exception cref=\"SLException\">Failed.</exception>\n\t\tpublic sqliteStatement Bind(SLIndexOrName sqlParam, bool value)\n\t\t\t=> Bind(sqlParam, value ? 1 : 0);\n\t\t\n\t\t/// <summary>Binds an enum value as <c>int</c> or <c>long</c>. Calls <sqlite>sqlite3_bind_int</sqlite> or <sqlite>sqlite3_bind_int64</sqlite>.</summary>\n\t\t/// <returns>this.</returns>\n\t\t/// <param name=\"sqlParam\">Parameter name or 1-based index.</param>\n\t\t/// <exception cref=\"SLException\">Failed.</exception>\n\t\t[MethodImpl(MethodImplOptions.NoInlining)] //ensure that value is copied to the parameter, because must not be smaller than int\n\t\tpublic sqliteStatement Bind<T>(SLIndexOrName sqlParam, T value) where T : unmanaged, Enum\n\t\t\t=> Bind(sqlParam, sizeof(T) == 8 ? *(long*)&value : *(int*)&value);\n\t\t\n\t\t/// <summary>Calls <sqlite>sqlite3_bind_double</sqlite>.</summary>\n\t\t/// <returns>this.</returns>\n\t\t/// <param name=\"sqlParam\">Parameter name or 1-based index.</param>\n\t\t/// <exception cref=\"SLException\">Failed.</exception>\n\t\tpublic sqliteStatement Bind(SLIndexOrName sqlParam, double value)\n\t\t\t=> _Err(SLApi.sqlite3_bind_double(_st, _B(sqlParam), value), \"sqlite3_bind_double\");\n\t\t\n\t\t/// <summary>Calls <sqlite>sqlite3_bind_text16</sqlite>.</summary>\n\t\t/// <returns>this.</returns>\n\t\t/// <param name=\"sqlParam\">Parameter name or 1-based index.</param>\n\t\t/// <exception cref=\"SLException\">Failed.</exception>\n\t\tpublic sqliteStatement Bind(SLIndexOrName sqlParam, string value)\n\t\t\t=> _Err(SLApi.sqlite3_bind_text16(_st, _B(sqlParam), value, (value?.Length ?? 0) * 2), \"sqlite3_bind_text16\");\n\t\t\n\t\t/// <summary>Calls <sqlite>sqlite3_bind_blob64</sqlite>.</summary>\n\t\t/// <returns>this.</returns>\n\t\t/// <param name=\"sqlParam\">Parameter name or 1-based index.</param>\n\t\t/// <exception cref=\"SLException\">Failed.</exception>\n\t\tpublic sqliteStatement Bind(SLIndexOrName sqlParam, void* blob, long nBytes)\n\t\t\t=> _Err(SLApi.sqlite3_bind_blob64(_st, _B(sqlParam), blob, nBytes), \"sqlite3_bind_blob64\");\n\t\t\n\t\tsqliteStatement _Bind(SLIndexOrName sqlParam, RByte blob) {\n\t\t\tfixed (byte* p = blob) return Bind(sqlParam, p, blob.Length);\n\t\t}\n\t\t\n\t\t/// <summary>Calls <sqlite>sqlite3_bind_blob64</sqlite>.</summary>\n\t\t/// <returns>this.</returns>\n\t\t/// <param name=\"sqlParam\">Parameter name or 1-based index.</param>\n\t\t/// <exception cref=\"SLException\">Failed.</exception>\n\t\tpublic sqliteStatement Bind<T>(SLIndexOrName sqlParam, ReadOnlySpan<T> blob) where T : unmanaged\n\t\t\t=> _Bind(sqlParam, MemoryMarshal.AsBytes(blob));\n\t\t\n\t\t/// <summary>Calls <sqlite>sqlite3_bind_blob64</sqlite>.</summary>\n\t\t/// <returns>this.</returns>\n\t\t/// <param name=\"sqlParam\">Parameter name or 1-based index.</param>\n\t\t/// <exception cref=\"SLException\">Failed.</exception>\n\t\tpublic sqliteStatement Bind<T>(SLIndexOrName sqlParam, Span<T> blob) where T : unmanaged\n\t\t\t=> _Bind(sqlParam, MemoryMarshal.AsBytes(blob));\n\t\t\n\t\t/// <summary>Calls <sqlite>sqlite3_bind_blob64</sqlite>.</summary>\n\t\t/// <returns>this.</returns>\n\t\t/// <param name=\"sqlParam\">Parameter name or 1-based index.</param>\n\t\t/// <exception cref=\"SLException\">Failed.</exception>\n\t\tpublic sqliteStatement Bind<T>(SLIndexOrName sqlParam, T[] array) where T : unmanaged\n\t\t\t=> Bind(sqlParam, array.AsSpan());\n\t\t\n\t\t/// <summary>Calls <sqlite>sqlite3_bind_blob64</sqlite>.</summary>\n\t\t/// <returns>this.</returns>\n\t\t/// <param name=\"sqlParam\">Parameter name or 1-based index.</param>\n\t\t/// <exception cref=\"SLException\">Failed.</exception>\n\t\tpublic sqliteStatement Bind<T>(SLIndexOrName sqlParam, List<T> list) where T : unmanaged\n\t\t\t=> Bind(sqlParam, CollectionsMarshal.AsSpan(list));\n\t\t\n\t\t/// <summary>Binds a value as blob. Calls <sqlite>sqlite3_bind_blob64</sqlite>.</summary>\n\t\t/// <returns>this.</returns>\n\t\t/// <param name=\"sqlParam\">Parameter name or 1-based index.</param>\n\t\t/// <exception cref=\"SLException\">Failed.</exception>\n\t\t/// <remarks>Can be any value type that does not contain fields of reference types. Examples: <see cref=\"Guid\"/>, <see cref=\"POINT\"/>, <c>int</c>, <c>decimal</c>.</remarks>\n\t\tpublic sqliteStatement BindStruct<T>(SLIndexOrName sqlParam, T value) where T : unmanaged\n\t\t\t=> Bind(sqlParam, &value, sizeof(T));\n\t\t\n\t\t/// <summary>Calls <sqlite>sqlite3_bind_null</sqlite>.</summary>\n\t\t/// <returns>this.</returns>\n\t\t/// <param name=\"sqlParam\">Parameter name or 1-based index.</param>\n\t\t/// <exception cref=\"SLException\">Failed.</exception>\n\t\t/// <remarks>Usually don't need to call this function. Unset parameter values are null. The <c>Bind</c> functions set null too if the value is null.</remarks>\n\t\tpublic sqliteStatement BindNull(SLIndexOrName sqlParam)\n\t\t\t=> _Err(SLApi.sqlite3_bind_null(_st, _B(sqlParam)), \"sqlite3_bind_null\");\n\t\t\n\t\t//rejected. 1. Currently we don't have a Blob class. 2. Can do in SQL: zeroblob(nBytes).\n\t\t///// <summary>Calls <sqlite>sqlite3_bind_zeroblob</sqlite>.</summary>\n\t\t///// <returns>this.</returns>\n\t\t///// <exception cref=\"SLException\">Failed.</exception>\n\t\t//public Statement BindZeroBlob(SLIndexOrName sqlParam, int nBytes)\n\t\t//\t=> _Err(SLApi.sqlite3_bind_zeroblob(_st, _B(sqlParam), nBytes), \"sqlite3_bind_zeroblob\");\n\t\t\n\t\t//rejected. DateTime can be stored in many ways. Let users decide how they want to store it, and explicitly convert to long, string, etc.\n\t\t///// <summary>Calls <c>sqlite3_bind_int64(value.ToBinary())</c>.</summary>\n\t\t///// <returns>this.</returns>\n\t\t///// <exception cref=\"SLException\">Failed.</exception>\n\t\t//public Statement Bind(SLIndexOrName sqlParam, DateTime value, bool convertToUtc = false)\n\t\t//\t=> Bind(sqlParam, (convertToUtc ? value.ToUniversalTime() : value).ToBinary());\n\t\t\n\t\t/// <summary>\n\t\t/// Used by <see cref=\"BindAll\"/>.\n\t\t/// </summary>\n\t\tinternal void BindObject(int i, object v) {\n\t\t\tint k;\n\t\t\tswitch (v) {\n\t\t\tcase null: BindNull(i); break;\n\t\t\tcase int x: k = x; goto gi;\n\t\t\tcase uint x: k = (int)x; goto gi;\n\t\t\tcase bool x: k = x ? 1 : 0; goto gi;\n\t\t\tcase long x: Bind(i, x); break;\n\t\t\tcase ulong x: Bind(i, x); break;\n\t\t\tcase double x: Bind(i, x); break;\n\t\t\tcase float x: Bind(i, x); break;\n\t\t\tcase string x: Bind(i, x); break;\n\t\t\tcase byte x: k = x; goto gi;\n\t\t\tcase sbyte x: k = x; goto gi;\n\t\t\tcase short x: k = x; goto gi;\n\t\t\tcase ushort x: k = x; goto gi;\n\t\t\tcase decimal x: Bind(i, &x, sizeof(decimal)); break;\n\t\t\tcase Guid x: Bind(i, &x, sizeof(Guid)); break;\n\t\t\tcase Enum x:\n\t\t\t\tswitch (x.GetTypeCode()) {\n\t\t\t\tcase TypeCode.Int64: case TypeCode.UInt64: Bind(i, Convert.ToInt64(v)); break;\n\t\t\t\tdefault: k = Convert.ToInt32(v); goto gi;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase Array a:\n\t\t\t\t//never mind: should throw if managed type. Same for List.\n\t\t\t\t//\tIt seems .NET does not have a function to check it.\n\t\t\t\t//\tSlow workarounds: https://stackoverflow.com/questions/53968920/how-do-i-check-if-a-type-fits-the-unmanaged-constraint-in-c\n\t\t\t\tfixed (byte* p = Unsafe.As<byte[]>(a)) Bind(i, p, Buffer.ByteLength(a));\n\t\t\t\tbreak;\n\t\t\t//case System.Collections.IList a:\n\t\t\t//\t//Can get data pointer and number of elements:\n\t\t\t//\t//\tvar span = CollectionsMarshal.AsSpan(Unsafe.As<List<byte>>(a)).\n\t\t\t//\t//But how to get element type size in a safe/fast/clean way?\n\t\t\t//\t//\tThis works, but unsafe etc: Marshal.SizeOf(a.GetType().GetGenericArguments()[0])\n\t\t\t//\t//\tThis does not work: MemoryMarshal.AsBytes(span).\n\t\t\t//\t//Or can get array through reflection, but slow and unsafe: var v=a.GetType().GetField(\"_items\", BindingFlags.NonPublic|BindingFlags.Instance).GetValue(i) as Array;\n\t\t\t//\t//Or can convert to List<T> with 'dynamic', but first time it adds 72 ms delay (hot CPU).\n\t\t\t//\tbreak;\n\t\t\tdefault:\n\t\t\t\t//never mind: this func does not support other types supported by other BindX functions. Quite difficult.\n\t\t\t\tvar t = v.GetType();\n\t\t\t\tthrow new NotSupportedException(t.Name);\n\t\t\t\t//case DateTime x: Bind(i, x); break;\n\t\t\t}\n\t\t\treturn;\n\t\t\tgi: Bind(i, k);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Binds multiple values of any supported types.\n\t\t/// </summary>\n\t\t/// <returns>this.</returns>\n\t\t/// <param name=\"values\">\n\t\t/// Values that will replace <c>?</c> characters in SQL.\n\t\t/// Read about SQL parameters in SQLite website. Example: <see cref=\"sqlite\"/>.\n\t\t/// Supported types:\n\t\t/// <br/>• <c>int</c>, <c>uint</c>, <c>byte</c>, <c>sbyte</c>, <c>short</c>, <c>ushort</c> - calls <sqlite>sqlite3_bind_int</sqlite>.\n\t\t/// <br/>• <c>bool</c> - calls <sqlite>sqlite3_bind_int</sqlite> (0 or 1).\n\t\t/// <br/>• <c>long</c>, <c>ulong</c> - calls <sqlite>sqlite3_bind_int64</sqlite>.\n\t\t/// <br/>• <c>double</c>, <c>float</c> - calls <sqlite>sqlite3_bind_double</sqlite>.\n\t\t/// <br/>• <c>string</c> - calls <sqlite>sqlite3_bind_text16</sqlite>.\n\t\t/// <br/>• <c>decimal</c> - calls <sqlite>sqlite3_bind_blob64</sqlite>.\n\t\t/// <br/>• <c>Guid</c> - calls <sqlite>sqlite3_bind_blob64</sqlite>.\n\t\t/// <br/>• <c>Array</c> - calls <sqlite>sqlite3_bind_blob64</sqlite>.\n\t\t/// <br/>• An <c>enum</c> type - calls <sqlite>sqlite3_bind_int</sqlite> or <sqlite>sqlite3_bind_int64</sqlite>.\n\t\t/// </param>\n\t\t/// <exception cref=\"NotSupportedException\">A value is of an unsupported type.</exception>\n\t\t/// <exception cref=\"SLException\">Failed.</exception>\n\t\t/// <remarks>\n\t\t/// For each parameter calls a <c>sqlite3_bind_x</c> function depending on type. Uses index 1, 2 and so on.\n\t\t/// This function is an alternative to calling <c>BindX</c> functions for each parameter. However it supports less types and adds boxing overhead.\n\t\t/// Does not call <sqlite>sqlite3_reset</sqlite> and <sqlite>sqlite3_clear_bindings</sqlite>. If need, call <see cref=\"Reset\"/> before.\n\t\t/// </remarks>\n\t\tpublic sqliteStatement BindAll(params object[] values) {\n\t\t\tif (values != null)\n\t\t\t\tfor (int i = 0; i < values.Length;) {\n\t\t\t\t\tobject v = values[i];\n\t\t\t\t\tBindObject(++i, v);\n\t\t\t\t}\n\t\t\treturn this;\n\t\t}\n\t\t\n\t\t#endregion\n\t\t\n\t\t#region get\n\t\t\n\t\tint _C(SLIndexOrName p) {\n\t\t\tif (p.name == null) return p.index;\n\t\t\tint r = ColumnIndex(p.name);\n\t\t\tif (r < 0) throw new SLException($\"Column '{p.name}' does not exist in query results.\");\n\t\t\treturn r;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Calls <google>sqlite3_column_int</google>.\n\t\t/// </summary>\n\t\t/// <param name=\"column\">Column name of 0-based index in results.</param>\n\t\t/// <exception cref=\"SLException\">The column does not exist in query results.</exception>\n\t\t/// <remarks>\n\t\t/// Use this function to get integer values of size 4, 2 or 1 bytes: <c>int</c>, <c>uint</c>, <c>short</c>, <c>ushort</c>, <c>byte</c>, <c>sbyte</c>, enum.\n\t\t/// </remarks>\n\t\tpublic int GetInt(SLIndexOrName column) {\n\t\t\tint r = SLApi.sqlite3_column_int(_st, _C(column));\n\t\t\tif (r == 0) _WarnGet();\n\t\t\treturn r;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Calls <google>sqlite3_column_int64</google>.\n\t\t/// </summary>\n\t\t/// <param name=\"column\">Column name of 0-based index in results.</param>\n\t\t/// <exception cref=\"SLException\">The column does not exist in query results.</exception>\n\t\t/// <remarks>\n\t\t/// Use this function to get integer values of size 8 bytes: <c>long</c>, <c>ulong</c>, 64-bit enum, maybe <see cref=\"DateTime\"/>.\n\t\t/// </remarks>\n\t\tpublic long GetLong(SLIndexOrName column) {\n\t\t\tlong r = SLApi.sqlite3_column_int64(_st, _C(column));\n\t\t\tif (r == 0) _WarnGet();\n\t\t\treturn r;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Calls <google>sqlite3_column_int64</google> and returns <c>true</c> if the value is not 0.\n\t\t/// </summary>\n\t\t/// <param name=\"column\">Column name of 0-based index in results.</param>\n\t\t/// <exception cref=\"SLException\">The column does not exist in query results.</exception>\n\t\tpublic bool GetBool(SLIndexOrName column) => GetLong(column) != 0;\n\t\t\n\t\t///// <summary>\n\t\t///// Calls <c>DateTime.FromBinary(sqlite3_column_int64(column))</c>.\n\t\t///// </summary>\n\t\t///// <param name=\"column\">Column name of 0-based index in results.</param>\n\t\t///// <param name=\"convertToLocal\">If the value in database is stored as UTC, convert to local.</param>\n\t\t///// <exception cref=\"ArgumentException\">The value in database is not in the valid DateTime range.</exception>\n\t\t///// <seealso cref=\"Bind(int, DateTime, bool)\"/>\n\t\t//public DateTime GetDateTime(SLIndexOrName column, bool convertToLocal = false)\n\t\t//{\n\t\t//\tvar r = DateTime.FromBinary(GetLong(column)); //info: it's OK if 0/null\n\t\t//\tif(convertToLocal && r.Kind == DateTimeKind.Utc) r = r.ToLocalTime();\n\t\t//\treturn r;\n\t\t//}\n\t\t\n\t\t/// <summary>\n\t\t/// Calls <google>sqlite3_column_double</google>.\n\t\t/// </summary>\n\t\t/// <param name=\"column\">Column name of 0-based index in results.</param>\n\t\t/// <exception cref=\"SLException\">The column does not exist in query results.</exception>\n\t\tpublic double GetDouble(SLIndexOrName column) {\n\t\t\tdouble r = SLApi.sqlite3_column_double(_st, _C(column));\n\t\t\tif (r == 0) _WarnGet();\n\t\t\treturn r;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Calls <google>sqlite3_column_text</google>.\n\t\t/// </summary>\n\t\t/// <param name=\"column\">Column name of 0-based index in results.</param>\n\t\t/// <exception cref=\"SLException\">The column does not exist in query results.</exception>\n\t\tpublic string GetText(SLIndexOrName column) {\n\t\t\tint icol = _C(column);\n\t\t\tif (_db.IsUtf16) { //both these codes would work, but with long strings can be significantly slower if SQLite has to convert text encoding\n\t\t\t\tchar* t = SLApi.sqlite3_column_text16(_st, icol);\n\t\t\t\tif (t != null) return new string(t, 0, SLApi.sqlite3_column_bytes16(_st, icol));\n\t\t\t} else {\n\t\t\t\tbyte* t = SLApi.sqlite3_column_text(_st, icol);\n\t\t\t\tif (t != null) return Encoding.UTF8.GetString(t, SLApi.sqlite3_column_bytes(_st, icol));\n\t\t\t}\n\t\t\t_WarnGet();\n\t\t\treturn null;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Calls <google>sqlite3_column_blob</google>.\n\t\t/// </summary>\n\t\t/// <returns>The returned memory is managed by SQLite and will become invalid when calling other SQLite functions afterwards. For zero-length BLOB returns <c>null</c>.</returns>\n\t\t/// <param name=\"column\">Column name of 0-based index in results.</param>\n\t\t/// <param name=\"nBytes\">Blob size.</param>\n\t\t/// <exception cref=\"SLException\">The column does not exist in query results.</exception>\n\t\tpublic byte* GetBlob(SLIndexOrName column, out int nBytes) {\n\t\t\tint icol = _C(column);\n\t\t\tvar r = (byte*)SLApi.sqlite3_column_blob(_st, icol);\n\t\t\tif (r == null) { nBytes = 0; _WarnGet(); } else nBytes = SLApi.sqlite3_column_bytes(_st, icol);\n\t\t\treturn r;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Calls <google>sqlite3_column_blob</google>.\n\t\t/// </summary>\n\t\t/// <returns>The returned memory is managed by SQLite and will become invalid when calling other SQLite functions afterwards.</returns>\n\t\t/// <param name=\"column\">Column name of 0-based index in results.</param>\n\t\t/// <exception cref=\"SLException\">The column does not exist in query results.</exception>\n\t\tpublic RByte GetBlob(SLIndexOrName column) {\n\t\t\tvar p = GetBlob(column, out int n);\n\t\t\treturn new(p, n);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Calls <google>sqlite3_column_blob</google>.\n\t\t/// </summary>\n\t\t/// <returns>The returned memory is managed by SQLite and will become invalid when calling other SQLite functions afterwards.</returns>\n\t\t/// <param name=\"column\">Column name of 0-based index in results.</param>\n\t\t/// <exception cref=\"SLException\">The column does not exist in query results.</exception>\n\t\tpublic ReadOnlySpan<T> GetBlob<T>(SLIndexOrName column) where T : unmanaged {\n\t\t\tvar p = GetBlob(column, out int n);\n\t\t\treturn new(p, n / sizeof(T));\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Calls <google>sqlite3_column_blob</google> and creates array.\n\t\t/// </summary>\n\t\t/// <param name=\"column\">Column name of 0-based index in results.</param>\n\t\t/// <exception cref=\"SLException\">The column does not exist in query results.</exception>\n\t\tpublic T[] GetArray<T>(SLIndexOrName column) where T : unmanaged {\n\t\t\tvar t = GetBlob(column, out int nb);\n\t\t\tif (t == null) return null;\n\t\t\tif (nb == 0) return [];\n\t\t\tint size = sizeof(T);\n\t\t\tint ne = nb / size; nb = ne * size;\n\t\t\tvar r = new T[ne];\n\t\t\tfixed (T* p = r) MemoryUtil.Copy(t, p, nb);\n\t\t\treturn r;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Calls <google>sqlite3_column_blob</google> and creates <c>List&lt;T&gt;</c>.\n\t\t/// </summary>\n\t\t/// <param name=\"column\">Column name of 0-based index in results.</param>\n\t\t/// <exception cref=\"SLException\">The column does not exist in query results.</exception>\n\t\tpublic List<T> GetList<T>(SLIndexOrName column) where T : unmanaged {\n\t\t\tvar t = (T*)GetBlob(column, out int nb);\n\t\t\tif (t == null) return null;\n\t\t\tint ne = nb / sizeof(T);\n\t\t\tvar r = new List<T>(ne);\n\t\t\tfor (int i = 0; i < ne; i++) r.Add(t[i]);\n\t\t\treturn r;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Calls <google>sqlite3_column_blob</google> and creates a variable of any value type that does not have fields of reference types.\n\t\t/// </summary>\n\t\t/// <param name=\"column\">Column name of 0-based index in results.</param>\n\t\t/// <exception cref=\"SLException\">The column does not exist in query results.</exception>\n\t\tpublic T GetStruct<T>(SLIndexOrName column) where T : unmanaged {\n\t\t\tvar t = (T*)GetBlob(column, out int nb);\n\t\t\tT r = default;\n\t\t\tif (t != null && nb == sizeof(T)) MemoryUtil.Copy(t, &r, nb);\n\t\t\treturn r;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Calls <sqlite>sqlite3_column_count</sqlite>.\n\t\t/// </summary>\n\t\tpublic int ColumnCount => SLApi.sqlite3_column_count(_st);\n\t\t\n\t\t/// <summary>\n\t\t/// Calls <sqlite>sqlite3_column_name</sqlite>.\n\t\t/// </summary>\n\t\tpublic string ColumnName(int index) => Convert2.Utf8Decode(SLApi.sqlite3_column_name(_st, index));\n\t\t\n\t\t/// <summary>\n\t\t/// Finds column by name in results.\n\t\t/// </summary>\n\t\t/// <returns>0-based index, or -1 if not found.</returns>\n\t\t/// <param name=\"name\">Column name in results, as returned by <sqlite>sqlite3_column_name</sqlite>. Case-sensitive.</param>\n\t\t[SkipLocalsInit]\n\t\tpublic int ColumnIndex(string name) {\n\t\t\tint n = ColumnCount;\n\t\t\tif (n > 0 && !name.NE() && !name.Contains('\\0')) {\n\t\t\t\tSpan<byte> sa = stackalloc byte[name.Length * 3];\n\t\t\t\tvar bname = sa[..Encoding.UTF8.GetBytes(name, sa)];\n\t\t\t\tfor (int i = 0; i < n; i++) {\n\t\t\t\t\tbyte* b = SLApi.sqlite3_column_name(_st, i);\n\t\t\t\t\tif (*b == bname[0] && MemoryMarshal.CreateReadOnlySpanFromNullTerminated(b).SequenceEqual(bname)) return i;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn -1;\n\t\t}\n\t\t\n\t\t#endregion\n\t\t\n\t\t#region util\n\t\t\n\t\t[DebuggerStepThrough]\n\t\tsqliteStatement _Err(SLError r, string func) {\n\t\t\tif (r != 0 && r != SLError.Row) throw new SLException(r, _db, func);\n\t\t\treturn this;\n\t\t}\n\t\t\n\t\t//rejected: not thread-safe. Other .NET wrappers ignore it too.\n\t\t//void _Err(string func)\n\t\t//{\n\t\t//\tvar r = SLApi.sqlite3_errcode(_db);\n\t\t//\tif(r != 0 && r != SLError.Row) throw new SLException(r, _db, func);\n\t\t//}\n\t\t\n\t\t/// <summary>\n\t\t/// Called by <c>GetX</c> functions when <c>sqlite3_column_x</c> returns <c>null</c>/0.\n\t\t/// Shows warning if <sqlite>sqlite3_errcode</sqlite> is not 0 or Row.\n\t\t/// Does not throw exception because it is not thread-safe.\n\t\t/// </summary>\n\t\tvoid _WarnGet([CallerMemberName] string m_ = null) {\n\t\t\tvar r = SLApi.sqlite3_errcode(_db);\n\t\t\tif (r != 0 && r != SLError.Row) SLUtil_.Warn(r, m_, \"Note: it may be a false positive if this database connection is used by multiple threads simultaneously without locking.\");\n\t\t}\n\t\t\n\t\t#endregion\n\t}\n}\n\nnamespace Au.Types {\n\t/// <summary>\n\t/// A SQLite transaction or savepoint. The main purpose is to automatically rollback if not explicitly committed.\n\t/// Usage: <c>using(var trans = new SLTransaction(db)) { ... trans.Commit(); }</c>\n\t/// </summary>\n\tpublic sealed class SLTransaction : IDisposable {\n\t\tsqlite _db;\n\t\t\n\t\t/// <summary>\n\t\t/// Begins a SQLite transaction and prepares for automatic rollback if not explicitly committed.\n\t\t/// Usage: <c>using(var trans = new SLTransaction(db)) { ... trans.Commit(); }</c>\n\t\t/// </summary>\n\t\t/// <param name=\"db\"></param>\n\t\t/// <param name=\"sql\">SQL to execute now. Default <c>\"BEGIN\"</c>. For nested transaction use <c>\"SAVEPOINT name\"</c>.</param>\n\t\t/// <param name=\"sqlOfDispose\">\n\t\t/// SQL to execute when disposing the <see cref=\"SLTransaction\"/> variable if not called <see cref=\"Commit\"/> or <see cref=\"Rollback\"/>.\n\t\t/// Default <c>\"ROLLBACK\"</c>. For nested transaction use <c>\"ROLLBACK TO name\"</c>.\n\t\t/// See also: <see cref=\"SqlOfDispose\"/>.\n\t\t/// </param>\n\t\t/// <exception cref=\"SLException\">Failed to execute <i>sql</i>.</exception>\n\t\tpublic SLTransaction(sqlite db, string sql = \"BEGIN\", string sqlOfDispose = \"ROLLBACK\") {\n\t\t\tNot_.Null(db);\n\t\t\tdb.Execute(sql);\n\t\t\t_db = db;\n\t\t\tSqlOfDispose = sqlOfDispose;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Calls <see cref=\"Rollback\"/> if not called <see cref=\"Commit\"/> or <see cref=\"Rollback\"/>.\n\t\t/// </summary>\n\t\t/// <exception cref=\"SLException\">Failed to execute <see cref=\"SqlOfDispose\"/>.</exception>\n\t\tpublic void Dispose() {\n\t\t\tif (_db != null) Rollback(SqlOfDispose);\n\t\t}\n\t\t\n\t\t///\n\t\t~SLTransaction() {\n\t\t\tif (_db != null) print.warning($\"Non-disposed SLTransaction variable.\");\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Executes a rollback SQL (if in transaction) and disables <see cref=\"Dispose\"/>.\n\t\t/// Usually don't need to call this function explicitly. It is implicitly called when disposing this variable if the transaction was not committed.\n\t\t/// </summary>\n\t\t/// <param name=\"sql\">SQL to execute. Default: <see cref=\"SqlOfDispose\"/>.</param>\n\t\t/// <exception cref=\"SLException\">Failed to execute <i>sql</i>.</exception>\n\t\tpublic void Rollback(string sql = null) {\n\t\t\tif (_db == null) throw new InvalidOperationException();\n\t\t\tif (sql == null) sql = SqlOfDispose;\n\t\t\t//if(ErrorMessage!=null) print.it(ErrorMessage);\n\t\t\tif (_db.IsInTransaction) //in some cases sqlite rolls back on error\n\t\t\t\t_db.Execute(sql);\n\t\t\t_db = null;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Executes a commit SQL and disables <see cref=\"Dispose\"/>.\n\t\t/// </summary>\n\t\t/// <param name=\"sql\">SQL to execute. Default <c>\"COMMIT\"</c>. For nested transaction use <c>\"RELEASE name\"</c>.</param>\n\t\t/// <exception cref=\"SLException\">Failed to execute <i>sql</i>.</exception>\n\t\tpublic void Commit(string sql = \"COMMIT\") {\n\t\t\tif (_db == null) throw new InvalidOperationException();\n\t\t\t_db.Execute(sql);\n\t\t\t_db = null;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets or sets SQL to execute when disposing this variable if not called <see cref=\"Commit\"/> or <see cref=\"Rollback\"/>.\n\t\t/// Initially = parameter <c>sqlOfDispose</c> of constructor.\n\t\t/// </summary>\n\t\tpublic string SqlOfDispose { get; set; }\n\t\t\n\t\t//public string ErrorMessage { get; set; }\n\t}\n\t\n\t/// <summary>\n\t/// Used for parameter types of some <see cref=\"sqliteStatement\"/> functions.\n\t/// Has implicit conversions from <c>int</c> and <c>string</c>. If <c>int</c>, the value is interpreted as index. If <c>string</c> - as name.\n\t/// </summary>\n\tpublic struct SLIndexOrName {\n#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member\n\t\tpublic int index;\n\t\tpublic string name;\n\t\tSLIndexOrName(int index) { this.index = index; this.name = null; }\n\t\tSLIndexOrName(string name) { this.index = 0; this.name = name; }\n\t\tpublic static implicit operator SLIndexOrName(int index) => new(index);\n\t\tpublic static implicit operator SLIndexOrName(string name) => new(name);\n#pragma warning restore CS1591\n\t}\n\t\n\t/// <summary>\n\t/// Exception thrown by <see cref=\"sqlite\"/>, <see cref=\"sqliteStatement\"/> and related types.\n\t/// </summary>\n\tpublic class SLException : Exception {\n#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member\n\t\tpublic SLException(string message) : base(message) { }\n\t\t\n\t\tinternal SLException(SLError r, string prefix = null, string suffix = null)\n\t\t\t: base(SLUtil_.Concat(prefix, SLApi.Errstr(r), suffix)) {\n\t\t\tDebug.Assert(r != 0 && r != SLError.Done && r != SLError.Row);\n\t\t\tErrorCode = r;\n\t\t}\n\t\t\n\t\tinternal SLException(SLError r, IntPtr db, string prefix = null)\n\t\t\t: base(SLUtil_.Concat(prefix, r, db)) {\n\t\t\tDebug.Assert(r != 0 && r != SLError.Done && r != SLError.Row);\n\t\t\tErrorCode = r;\n\t\t}\n#pragma warning restore CS1591\n\t\t\n\t\t/// <summary>\n\t\t/// The called SQLite API function returned this error code.\n\t\t/// </summary>\n\t\tpublic SLError ErrorCode { get; private set; }\n\t}\n}\n\nnamespace Au.More {\n\tinternal static unsafe class SLUtil_ {\n\t\tinternal static string ToStringAndFree(byte* utf8) {\n\t\t\tvar r = Convert2.Utf8Decode(utf8);\n\t\t\tSLApi.sqlite3_free(utf8);\n\t\t\treturn r;\n\t\t}\n\t\t\n\t\t//currently not used\n\t\t//internal static void Err(SLError r, sqlite db, string func)\n\t\t//{\n\t\t//\tif(r != 0) throw new SLException(r, db, func);\n\t\t//}\n\t\t\n\t\tinternal static void Warn(SLError r, string func, string suffix = null) {\n\t\t\tif (r != 0) print.warning(SLUtil_.Concat(func, SLApi.Errstr(r), suffix));\n\t\t}\n\t\t\n\t\tinternal static string Concat(string s1, string s2, string s3) {\n\t\t\tusing (new StringBuilder_(out var b)) {\n\t\t\t\t_Append(s1); _Append(s2); if (s3 != s2) _Append(s3);\n\t\t\t\treturn b.ToString();\n\t\t\t\t\n\t\t\t\tvoid _Append(string s) => b.AppendSentence(s, s?.Starts(\"sqlite3_\") ?? false); //avoid uppercase for function names\n\t\t\t}\n\t\t}\n\t\t\n\t\tinternal static string Concat(string s1, SLError r, IntPtr db)\n\t\t\t=> Concat(s1, SLApi.Errstr(r), SLApi.sqlite3_errcode(db) == 0 ? null : SLApi.Errmsg(db));\n\t}\n\t\n}"
  },
  {
    "path": "Au/Files, data/sqlite_api.cs",
    "content": "using System.IO.Compression;\n\nnamespace Au.Types;\n\n#region enum\n\n//Most of these declarations are from System.Data.SQLite library. Modified.\n\n/// <summary>\n/// Flags for <see cref=\"sqlite\"/> constructor.\n/// </summary>\n[Flags]\npublic enum SLFlags {\n\t/// <summary>Default flags. <sqlite>SQLITE_OPEN_READWRITE</sqlite> and <sqlite>SQLITE_OPEN_CREATE</sqlite>.</summary>\n\tReadWriteCreate = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,\n\t\n#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member\n\tSQLITE_OPEN_READONLY = 0x1,\n\tSQLITE_OPEN_READWRITE = 0x2,\n\tSQLITE_OPEN_CREATE = 0x4,\n\tSQLITE_OPEN_URI = 0x40,\n\tSQLITE_OPEN_NOMUTEX = 0x8000,\n\tSQLITE_OPEN_FULLMUTEX = 0x10000,\n\tSQLITE_OPEN_SHAREDCACHE = 0x20000,\n\tSQLITE_OPEN_PRIVATECACHE = 0x40000,\n#pragma warning restore CS1591\n}\n\n/// <summary>\n/// SQLite API error codes. Also two success codes - Row and Done.\n/// </summary>\npublic enum SLError {\n\t/// <summary>\n\t/// Successful result\n\t/// </summary>\n\tOk /* 0 */,\n\t/// <summary>\n\t/// SQL error or missing database\n\t/// </summary>\n\tError /* 1 */,\n\t/// <summary>\n\t/// Internal logic error in SQLite\n\t/// </summary>\n\tInternal /* 2 */,\n\t/// <summary>\n\t/// Access permission denied\n\t/// </summary>\n\tPerm /* 3 */,\n\t/// <summary>\n\t/// Callback routine requested an abort\n\t/// </summary>\n\tAbort /* 4 */,\n\t/// <summary>\n\t/// The database file is locked\n\t/// </summary>\n\tBusy /* 5 */,\n\t/// <summary>\n\t/// A table in the database is locked\n\t/// </summary>\n\tLocked /* 6 */,\n\t/// <summary>\n\t/// A malloc() failed\n\t/// </summary>\n\tNoMem /* 7 */,\n\t/// <summary>\n\t/// Attempt to write a readonly database\n\t/// </summary>\n\tReadOnly /* 8 */,\n\t/// <summary>\n\t/// Operation terminated by <c>sqlite3_interrupt</c>\n\t/// </summary>\n\tInterrupt /* 9 */,\n\t/// <summary>\n\t/// Some kind of disk I/O error occurred\n\t/// </summary>\n\tIoErr /* 10 */,\n\t/// <summary>\n\t/// The database disk image is malformed\n\t/// </summary>\n\tCorrupt /* 11 */,\n\t/// <summary>\n\t/// Unknown opcode in <c>sqlite3_file_control</c>\n\t/// </summary>\n\tNotFound /* 12 */,\n\t/// <summary>\n\t/// Insertion failed because database is full\n\t/// </summary>\n\tFull /* 13 */,\n\t/// <summary>\n\t/// Unable to open the database file\n\t/// </summary>\n\tCantOpen /* 14 */,\n\t/// <summary>\n\t/// Database lock protocol error\n\t/// </summary>\n\tProtocol /* 15 */,\n\t/// <summary>\n\t/// Database is empty\n\t/// </summary>\n\tEmpty /* 16 */,\n\t/// <summary>\n\t/// The database schema changed\n\t/// </summary>\n\tSchema /* 17 */,\n\t/// <summary>\n\t/// String or BLOB exceeds size limit\n\t/// </summary>\n\tTooBig /* 18 */,\n\t/// <summary>\n\t/// Abort due to constraint violation\n\t/// </summary>\n\tConstraint /* 19 */,\n\t/// <summary>\n\t/// Data type mismatch\n\t/// </summary>\n\tMismatch /* 20 */,\n\t/// <summary>\n\t/// Library used incorrectly\n\t/// </summary>\n\tMisuse /* 21 */,\n\t/// <summary>\n\t/// Uses OS features not supported on host\n\t/// </summary>\n\tNoLfs /* 22 */,\n\t/// <summary>\n\t/// Authorization denied\n\t/// </summary>\n\tAuth /* 23 */,\n\t/// <summary>\n\t/// Auxiliary database format error\n\t/// </summary>\n\tFormat /* 24 */,\n\t/// <summary>\n\t/// 2nd parameter to <c>sqlite3_bind</c> out of range\n\t/// </summary>\n\tRange /* 25 */,\n\t/// <summary>\n\t/// File opened that is not a database file\n\t/// </summary>\n\tNotADb /* 26 */,\n\t/// <summary>\n\t/// Notifications from <c>sqlite3_log</c>\n\t/// </summary>\n\tNotice /* 27 */,\n\t/// <summary>\n\t/// Warnings from <c>sqlite3_log</c>\n\t/// </summary>\n\tWarning /* 28 */,\n\t/// <summary>\n\t/// <c>sqlite3_step</c> has another row ready\n\t/// </summary>\n\tRow = 100,\n\t/// <summary>\n\t/// <c>sqlite3_step</c> has finished executing\n\t/// </summary>\n\tDone /* 101 */,\n\t///// <summary>\n\t///// Used to mask off extended result codes\n\t///// </summary>\n\t//NonExtendedMask = 0xFF,\n\t\n#if false\n\t/////////////////////////////////////////////////////////////////////////\n\t// BEGIN EXTENDED RESULT CODES\n\t/////////////////////////////////////////////////////////////////////////\n\n\t/// <summary>\n\t/// A collation sequence was referenced by a schema and it cannot be\n\t/// found.\n\t/// </summary>\n\tError_Missing_CollSeq = (Error | (1 << 8)),\n\t/// <summary>\n\t/// An internal operation failed and it may succeed if retried.\n\t/// </summary>\n\tError_Retry = (Error | (2 << 8)),\n\t/// <summary>\n\t/// A file read operation failed.\n\t/// </summary>\n\tIoErr_Read = (IoErr | (1 << 8)),\n\t/// <summary>\n\t/// A file read operation returned less data than requested.\n\t/// </summary>\n\tIoErr_Short_Read = (IoErr | (2 << 8)),\n\t/// <summary>\n\t/// A file write operation failed.\n\t/// </summary>\n\tIoErr_Write = (IoErr | (3 << 8)),\n\t/// <summary>\n\t/// A file synchronization operation failed.\n\t/// </summary>\n\tIoErr_Fsync = (IoErr | (4 << 8)),\n\t/// <summary>\n\t/// A directory synchronization operation failed.\n\t/// </summary>\n\tIoErr_Dir_Fsync = (IoErr | (5 << 8)),\n\t/// <summary>\n\t/// A file truncate operation failed.\n\t/// </summary>\n\tIoErr_Truncate = (IoErr | (6 << 8)),\n\t/// <summary>\n\t/// A file metadata operation failed.\n\t/// </summary>\n\tIoErr_Fstat = (IoErr | (7 << 8)),\n\t/// <summary>\n\t/// A file unlock operation failed.\n\t/// </summary>\n\tIoErr_Unlock = (IoErr | (8 << 8)),\n\t/// <summary>\n\t/// A file lock operation failed.\n\t/// </summary>\n\tIoErr_RdLock = (IoErr | (9 << 8)),\n\t/// <summary>\n\t/// A file delete operation failed.\n\t/// </summary>\n\tIoErr_Delete = (IoErr | (10 << 8)),\n\t/// <summary>\n\t/// Not currently used.\n\t/// </summary>\n\tIoErr_Blocked = (IoErr | (11 << 8)),\n\t/// <summary>\n\t/// Out-of-memory during a file operation.\n\t/// </summary>\n\tIoErr_NoMem = (IoErr | (12 << 8)),\n\t/// <summary>\n\t/// A file existence/status operation failed.\n\t/// </summary>\n\tIoErr_Access = (IoErr | (13 << 8)),\n\t/// <summary>\n\t/// A check for a reserved lock failed.\n\t/// </summary>\n\tIoErr_CheckReservedLock = (IoErr | (14 << 8)),\n\t/// <summary>\n\t/// A file lock operation failed.\n\t/// </summary>\n\tIoErr_Lock = (IoErr | (15 << 8)),\n\t/// <summary>\n\t/// A file close operation failed.\n\t/// </summary>\n\tIoErr_Close = (IoErr | (16 << 8)),\n\t/// <summary>\n\t/// A directory close operation failed.\n\t/// </summary>\n\tIoErr_Dir_Close = (IoErr | (17 << 8)),\n\t/// <summary>\n\t/// A shared memory open operation failed.\n\t/// </summary>\n\tIoErr_ShmOpen = (IoErr | (18 << 8)),\n\t/// <summary>\n\t/// A shared memory size operation failed.\n\t/// </summary>\n\tIoErr_ShmSize = (IoErr | (19 << 8)),\n\t/// <summary>\n\t/// A shared memory lock operation failed.\n\t/// </summary>\n\tIoErr_ShmLock = (IoErr | (20 << 8)),\n\t/// <summary>\n\t/// A shared memory map operation failed.\n\t/// </summary>\n\tIoErr_ShmMap = (IoErr | (21 << 8)),\n\t/// <summary>\n\t/// A file seek operation failed.\n\t/// </summary>\n\tIoErr_Seek = (IoErr | (22 << 8)),\n\t/// <summary>\n\t/// A file delete operation failed because it does not exist.\n\t/// </summary>\n\tIoErr_Delete_NoEnt = (IoErr | (23 << 8)),\n\t/// <summary>\n\t/// A file memory mapping operation failed.\n\t/// </summary>\n\tIoErr_Mmap = (IoErr | (24 << 8)),\n\t/// <summary>\n\t/// The temporary directory path could not be obtained.\n\t/// </summary>\n\tIoErr_GetTempPath = (IoErr | (25 << 8)),\n\t/// <summary>\n\t/// A path string conversion operation failed.\n\t/// </summary>\n\tIoErr_ConvPath = (IoErr | (26 << 8)),\n\t/// <summary>\n\t/// Reserved.\n\t/// </summary>\n\tIoErr_VNode = (IoErr | (27 << 8)),\n\t/// <summary>\n\t/// An attempt to authenticate failed.\n\t/// </summary>\n\tIoErr_Auth = (IoErr | (28 << 8)),\n\t/// <summary>\n\t/// An attempt to begin a file system transaction failed.\n\t/// </summary>\n\tIoErr_Begin_Atomic = (IoErr | (29 << 8)),\n\t/// <summary>\n\t/// An attempt to commit a file system transaction failed.\n\t/// </summary>\n\tIoErr_Commit_Atomic = (IoErr | (30 << 8)),\n\t/// <summary>\n\t/// An attempt to rollback a file system transaction failed.\n\t/// </summary>\n\tIoErr_Rollback_Atomic = (IoErr | (31 << 8)),\n\t/// <summary>\n\t/// A database table is locked in shared-cache mode.\n\t/// </summary>\n\tLocked_SharedCache = (Locked | (1 << 8)),\n\t/// <summary>\n\t/// A virtual table in the database is locked.\n\t/// </summary>\n\tLocked_Vtab = (Locked | (2 << 8)),\n\t/// <summary>\n\t/// A database file is locked due to a recovery operation.\n\t/// </summary>\n\tBusy_Recovery = (Busy | (1 << 8)),\n\t/// <summary>\n\t/// A database file is locked due to snapshot semantics.\n\t/// </summary>\n\tBusy_Snapshot = (Busy | (2 << 8)),\n\t/// <summary>\n\t/// A database file cannot be opened because no temporary directory is available.\n\t/// </summary>\n\tCantOpen_NoTempDir = (CantOpen | (1 << 8)),\n\t/// <summary>\n\t/// A database file cannot be opened because its path represents a directory.\n\t/// </summary>\n\tCantOpen_IsDir = (CantOpen | (2 << 8)),\n\t/// <summary>\n\t/// A database file cannot be opened because its full path could not be obtained.\n\t/// </summary>\n\tCantOpen_FullPath = (CantOpen | (3 << 8)),\n\t/// <summary>\n\t/// A database file cannot be opened because a path string conversion operation failed.\n\t/// </summary>\n\tCantOpen_ConvPath = (CantOpen | (4 << 8)),\n\t/// <summary>\n\t/// A virtual table is malformed.\n\t/// </summary>\n\tCorrupt_Vtab = (Corrupt | (1 << 8)),\n\t/// <summary>\n\t/// A required sequence table is missing or corrupt.\n\t/// </summary>\n\tCorrupt_Sequence = (Corrupt | (2 << 8)),\n\t/// <summary>\n\t/// A database file is read-only due to a recovery operation.\n\t/// </summary>\n\tReadOnly_Recovery = (ReadOnly | (1 << 8)),\n\t/// <summary>\n\t/// A database file is read-only because a lock could not be obtained.\n\t/// </summary>\n\tReadOnly_CantLock = (ReadOnly | (2 << 8)),\n\t/// <summary>\n\t/// A database file is read-only because it needs rollback processing.\n\t/// </summary>\n\tReadOnly_Rollback = (ReadOnly | (3 << 8)),\n\t/// <summary>\n\t/// A database file is read-only because it was moved while open.\n\t/// </summary>\n\tReadOnly_DbMoved = (ReadOnly | (4 << 8)),\n\t/// <summary>\n\t/// The shared-memory file is read-only and it should be read-write.\n\t/// </summary>\n\tReadOnly_CantInit = (ReadOnly | (5 << 8)),\n\t/// <summary>\n\t/// Unable to create journal file because the directory is read-only.\n\t/// </summary>\n\tReadOnly_Directory = (ReadOnly | (6 << 8)),\n\t/// <summary>\n\t/// An operation is being aborted due to rollback processing.\n\t/// </summary>\n\tAbort_Rollback = (Abort | (2 << 8)),\n\t/// <summary>\n\t/// A <c>CHECK</c> constraint failed.\n\t/// </summary>\n\tConstraint_Check = (Constraint | (1 << 8)),\n\t/// <summary>\n\t/// A commit hook produced a unsuccessful return code.\n\t/// </summary>\n\tConstraint_CommitHook = (Constraint | (2 << 8)),\n\t/// <summary>\n\t/// A <c>FOREIGN KEY</c> constraint failed.\n\t/// </summary>\n\tConstraint_ForeignKey = (Constraint | (3 << 8)),\n\t/// <summary>\n\t/// Not currently used.\n\t/// </summary>\n\tConstraint_Function = (Constraint | (4 << 8)),\n\t/// <summary>\n\t/// A <c>NOT NULL</c> constraint failed.\n\t/// </summary>\n\tConstraint_NotNull = (Constraint | (5 << 8)),\n\t/// <summary>\n\t/// A <c>PRIMARY KEY</c> constraint failed.\n\t/// </summary>\n\tConstraint_PrimaryKey = (Constraint | (6 << 8)),\n\t/// <summary>\n\t/// The <c>RAISE</c> function was used by a trigger-program.\n\t/// </summary>\n\tConstraint_Trigger = (Constraint | (7 << 8)),\n\t/// <summary>\n\t/// A <c>UNIQUE</c> constraint failed.\n\t/// </summary>\n\tConstraint_Unique = (Constraint | (8 << 8)),\n\t/// <summary>\n\t/// Not currently used.\n\t/// </summary>\n\tConstraint_Vtab = (Constraint | (9 << 8)),\n\t/// <summary>\n\t/// A <c>ROWID</c> constraint failed.\n\t/// </summary>\n\tConstraint_RowId = (Constraint | (10 << 8)),\n\t/// <summary>\n\t/// Frames were recovered from the WAL log file.\n\t/// </summary>\n\tNotice_Recover_Wal = (Notice | (1 << 8)),\n\t/// <summary>\n\t/// Pages were recovered from the journal file.\n\t/// </summary>\n\tNotice_Recover_Rollback = (Notice | (2 << 8)),\n\t/// <summary>\n\t/// An automatic index was created to process a query.\n\t/// </summary>\n\tWarning_AutoIndex = (Warning | (1 << 8)),\n\t/// <summary>\n\t/// User authentication failed.\n\t/// </summary>\n\tAuth_User = (Auth | (1 << 8)),\n\t/// <summary>\n\t/// Success.  Prevents the extension from unloading until the process\n\t/// terminates.\n\t/// </summary>\n\tOk_Load_Permanently = (Ok | (1 << 8))\n#endif\n}\n\n#if false\n\t// These are the options to the internal sqlite3_config call.\ninternal enum Config\n{\n\tSQLITE_CONFIG_NONE = 0, // nil\n\tSQLITE_CONFIG_SINGLETHREAD = 1, // nil\n\tSQLITE_CONFIG_MULTITHREAD = 2, // nil\n\tSQLITE_CONFIG_SERIALIZED = 3, // nil\n\tSQLITE_CONFIG_MALLOC = 4, // sqlite3_mem_methods*\n\tSQLITE_CONFIG_GETMALLOC = 5, // sqlite3_mem_methods*\n\tSQLITE_CONFIG_SCRATCH = 6, // void*, int sz, int N\n\tSQLITE_CONFIG_PAGECACHE = 7, // void*, int sz, int N\n\tSQLITE_CONFIG_HEAP = 8, // void*, int nByte, int min\n\tSQLITE_CONFIG_MEMSTATUS = 9, // boolean\n\tSQLITE_CONFIG_MUTEX = 10, // sqlite3_mutex_methods*\n\tSQLITE_CONFIG_GETMUTEX = 11, // sqlite3_mutex_methods*\n\t\t\t\t\t\t\t\t // previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused\n\tSQLITE_CONFIG_LOOKASIDE = 13, // int int\n\tSQLITE_CONFIG_PCACHE = 14, // sqlite3_pcache_methods*\n\tSQLITE_CONFIG_GETPCACHE = 15, // sqlite3_pcache_methods*\n\tSQLITE_CONFIG_LOG = 16, // xFunc, void*\n\tSQLITE_CONFIG_URI = 17, // int\n\tSQLITE_CONFIG_PCACHE2 = 18, // sqlite3_pcache_methods2*\n\tSQLITE_CONFIG_GETPCACHE2 = 19, // sqlite3_pcache_methods2*\n\tSQLITE_CONFIG_COVERING_INDEX_SCAN = 20, // int\n\tSQLITE_CONFIG_SQLLOG = 21, // xSqllog, void*\n\tSQLITE_CONFIG_MMAP_SIZE = 22, // sqlite3_int64, sqlite3_int64\n\tSQLITE_CONFIG_WIN32_HEAPSIZE = 23, // int nByte\n\tSQLITE_CONFIG_PCACHE_HDRSZ = 24, // int *psz\n\tSQLITE_CONFIG_PMASZ = 25 // unsigned int szPma\n}\n\ninternal enum ConfigDb\n{\n\t/// <summary>\n\t/// This value represents an unknown (or invalid) option, do not use it.\n\t/// </summary>\n\tSQLITE_DBCONFIG_NONE = 0, // nil\n\n\t/// <summary>\n\t/// This option is used to change the name of the \"main\" database\n\t/// schema.  The sole argument is a pointer to a constant UTF-8 string\n\t/// which will become the new schema name in place of \"main\".\n\t/// </summary>\n\tSQLITE_DBCONFIG_MAINDBNAME = 1000, // char*\n\n\t/// <summary>\n\t/// This option is used to configure the lookaside memory allocator.\n\t/// The value must be an array with three elements.  The second element\n\t/// must be an <see cref=\"Int32\" /> containing the size of each buffer\n\t/// slot.  The third element must be an <see cref=\"Int32\" /> containing\n\t/// the number of slots.  The first element must be an <see cref=\"IntPtr\" />\n\t/// that points to a native memory buffer of bytes equal to or greater\n\t/// than the product of the second and third element values.\n\t/// </summary>\n\tSQLITE_DBCONFIG_LOOKASIDE = 1001, // void* int int\n\n\t/// <summary>\n\t/// This option is used to enable or disable the enforcement of\n\t/// foreign key constraints.\n\t/// </summary>\n\tSQLITE_DBCONFIG_ENABLE_FKEY = 1002, // int int*\n\n\t/// <summary>\n\t/// This option is used to enable or disable triggers.\n\t/// </summary>\n\tSQLITE_DBCONFIG_ENABLE_TRIGGER = 1003, // int int*\n\n\t/// <summary>\n\t/// This option is used to enable or disable the two-argument version\n\t/// of the <c>fts3_tokenizer</c> function which is part of the FTS3 full-text\n\t/// search engine extension.\n\t/// </summary>\n\tSQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER = 1004, // int int*\n\n\t/// <summary>\n\t/// This option is used to enable or disable the loading of extensions.\n\t/// </summary>\n\tSQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION = 1005, // int int*\n\n\t/// <summary>\n\t/// This option is used to enable or disable the automatic checkpointing\n\t/// when a WAL database is closed.\n\t/// </summary>\n\tSQLITE_DBCONFIG_NO_CKPT_ON_CLOSE = 1006, // int int*\n\n\t/// <summary>\n\t/// This option is used to enable or disable the query planner stability\n\t/// guarantee (QPSG).\n\t/// </summary>\n\tSQLITE_DBCONFIG_ENABLE_QPSG = 1007, // int int*\n\n\t/// <summary>\n\t/// This option is used to enable or disable the extra <c>EXPLAIN QUERY PLAN</c>\n\t/// output for trigger programs.\n\t/// </summary>\n\tSQLITE_DBCONFIG_TRIGGER_EQP = 1008, // int int*\n\n\t/// <summary>\n\t/// This option is used as part of the process to reset a database back\n\t/// to an empty state.  Because resetting a database is destructive and\n\t/// irreversible, the process requires the use of this obscure flag and\n\t/// multiple steps to help ensure that it does not happen by accident.\n\t/// </summary>\n\tSQLITE_DBCONFIG_RESET_DATABASE = 1009 // int int*\n}\n\ninternal enum TypeAffinity\n{\n\t/// <summary>\n\t/// All integers in SQLite default to Int64\n\t/// </summary>\n\tInt64 = 1,\n\t/// <summary>\n\t/// All floating point numbers in SQLite default to double\n\t/// </summary>\n\tDouble = 2,\n\t/// <summary>\n\t/// The default data type of SQLite is text\n\t/// </summary>\n\tText = 3,\n\t/// <summary>\n\t/// Typically blob types are only seen when returned from a function\n\t/// </summary>\n\tBlob = 4,\n\t/// <summary>\n\t/// Null types can be returned from functions\n\t/// </summary>\n\tNull = 5,\n}\n#endif\n\n#endregion\n\n#region functions\n\ninternal static unsafe class SLApi {\n\tconst string SQLITE_DLL = \"winsqlite3.dll\";\n\t\n\tstatic SLApi() {\n\t\t//Windows 10 and 11 have winsqlite3.dll. On other OS download from www.sqlite.org.\n\t\tif (!NativeLibrary.TryLoad(SQLITE_DLL, out _)) _LoadDownloaded();\n\t\t\n\t\tstatic void _LoadDownloaded() {\n\t\t\tstring path = folders.ThisAppDataLocal; if (path.Ends(@\"\\_script\", true)) path = path[..^8];\n\t\t\tpath += $@\"\\download\\{SQLITE_DLL}\";\n\t\t\t//print.it(path, filesystem.exists(path));\n\t\t\tif (!filesystem.exists(path)) {\n\t\t\t\tgRetry:\n\t\t\t\ttry {\n\t\t\t\t\t_Download(path);\n\t\t\t\t}\n\t\t\t\tcatch (Exception ex) {\n\t\t\t\t\tint r = dialog.showError(\"Failed to download missing files\", $\"Your OS does not have this file: {SQLITE_DLL}.\\r\\nThis program tried to download it from www.sqlite.org, but failed.\", \"1 Retry|0 Cancel\", expandedText: ex.ToString());\n\t\t\t\t\tif (r == 1) goto gRetry;\n\t\t\t\t\tEnvironment.Exit(0);\n\t\t\t\t}\n\t\t\t}\n\t\t\tNativeLibrary.Load(path);\n\t\t\t\n\t\t\tstatic void _Download(string path) {\n\t\t\t\t//Get the download URL of the last sqlite version for this OS. This way is documented in https://www.sqlite.org/download.html.\n\t\t\t\tvar html = internet.http_.Get(\"https://www.sqlite.org/download.html\").Text();\n\t\t\t\t//print.it(html);\n\t\t\t\tif (!html.RxMatch(@\"(?s)<!-- Download product data for scripts to read\\s+(.+)\\s+-->\", 1, out string csv)) throw new AuException();\n\t\t\t\t//print.it(csv);\n\t\t\t\tvar x = csvTable.parse(csv);\n\t\t\t\tstring s = $\"sqlite-dll-win-{RuntimeInformation.ProcessArchitecture.ToString().Lower()}\";\n\t\t\t\tstring url = x.Rows.First(o => o[2].Contains(s, StringComparison.OrdinalIgnoreCase))[2];\n\t\t\t\turl = \"https://www.sqlite.org/\" + url;\n\t\t\t\t//print.it(url);\n\t\t\t\t\n\t\t\t\t//Download and extract.\n\t\t\t\tvar bytes = internet.http_.Get(url).Bytes();\n\t\t\t\tusing var za = new ZipArchive(new MemoryStream(bytes), ZipArchiveMode.Read);\n\t\t\t\tvar e = za.GetEntry(\"sqlite3.dll\");\n\t\t\t\tfilesystem.createDirectoryFor(path);\n\t\t\t\te.ExtractToFile(path, true);\n\t\t\t}\n\t\t}\n\t}\n\t\n\t[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern SLError sqlite3_close_v2(IntPtr db);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern SLError sqlite3_create_function(IntPtr db, byte[] strName, int nArgs, int nType, IntPtr pvUser, SQLiteCallback func, SQLiteCallback fstep, SQLiteFinalCallback ffinal);\n\t\n\t[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern SLError sqlite3_finalize(IntPtr stmt);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern SLError sqlite3_backup_finish(IntPtr backup);\n\t\n\t[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern SLError sqlite3_reset(IntPtr stmt);\n\t\n\t[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern byte* sqlite3_bind_parameter_name(IntPtr stmt, int index);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern byte* sqlite3_column_database_name(IntPtr stmt, int index);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern byte* sqlite3_column_decltype(IntPtr stmt, int index);\n\t\n\t[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern byte* sqlite3_column_name(IntPtr stmt, int index);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern byte* sqlite3_column_origin_name(IntPtr stmt, int index);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern byte* sqlite3_column_table_name(IntPtr stmt, int index);\n\t\n\t[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern byte* sqlite3_column_text(IntPtr stmt, int index);\n\t\n\t[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern char* sqlite3_column_text16(IntPtr stmt, int index);\n\t\n\t[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern byte* sqlite3_errmsg(IntPtr db);\n\t\n\tinternal static string Errmsg(IntPtr db) => Convert2.Utf8Decode(sqlite3_errmsg(db));\n\t\n\t[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern SLError sqlite3_prepare16_v3(IntPtr db, char* sql, int nBytes, int prepFlags, ref IntPtr stmt, char** ptrRemain);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern SLError sqlite3_table_column_metadata(IntPtr db, byte[] dbName, byte[] tblName, byte[] colName, byte** ptrDataType, byte** ptrCollSeq, ref int notNull, ref int primaryKey, ref int autoInc);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern byte* sqlite3_value_text(IntPtr p);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern byte* sqlite3_libversion();\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern int sqlite3_libversion_number();\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern byte* sqlite3_sourceid();\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern int sqlite3_compileoption_used(byte[] zOptName);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern byte* sqlite3_compileoption_get(int N);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern SLError sqlite3_enable_shared_cache(int enable);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern SLError sqlite3_enable_load_extension(IntPtr db, int enable);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern SLError sqlite3_load_extension(IntPtr db, byte[] fileName, byte[] procName, byte** pError);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern SLError sqlite3_overload_function(IntPtr db, byte[] zName, int nArgs);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern SLError sqlite3_win32_set_directory16(uint type, string value);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern SLError sqlite3_win32_reset_heap();\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern SLError sqlite3_win32_compact_heap(ref uint largest);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern void* sqlite3_malloc(int n);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern void* sqlite3_malloc64(ulong n);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern void* sqlite3_realloc(void* p, int n);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern void* sqlite3_realloc64(void* p, ulong n);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern ulong sqlite3_msize(void* p);\n\t\n\t[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern void sqlite3_free(void* p);\n\t\n\t[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern SLError sqlite3_open_v2(byte[] filename, ref IntPtr db, SLFlags flags, byte[] vfsName);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern void sqlite3_interrupt(IntPtr db);\n\t\n\t[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern long sqlite3_last_insert_rowid(IntPtr db);\n\t\n\t[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern int sqlite3_changes(IntPtr db);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern long sqlite3_memory_used();\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern long sqlite3_memory_highwater(int resetFlag);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern SLError sqlite3_shutdown();\n\t\n\t[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern SLError sqlite3_busy_timeout(IntPtr db, int ms);\n\t\n\t[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern SLError sqlite3_clear_bindings(IntPtr stmt);\n\t\n\t[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\tstatic extern SLError sqlite3_bind_blob64(IntPtr stmt, int index, void* value, long nSize, nint nTransient);\n\t\n\tinternal static SLError sqlite3_bind_blob64(IntPtr stmt, int index, void* value, long nSize)\n\t\t=> sqlite3_bind_blob64(stmt, index, value, nSize, -1); //SQLITE_TRANSIENT //FUTURE: somehow avoid copying if large data. Eg if array or List, can use callback and GCHandle.\n\t\n\t[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern SLError sqlite3_bind_double(IntPtr stmt, int index, double value);\n\t\n\t[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern SLError sqlite3_bind_int(IntPtr stmt, int index, int value);\n\t\n\t[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern SLError sqlite3_bind_int64(IntPtr stmt, int index, long value);\n\t\n\t[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern SLError sqlite3_bind_null(IntPtr stmt, int index);\n\t\n\t[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\tstatic extern SLError sqlite3_bind_text16(IntPtr stmt, int index, string value, int nlen, nint nTransient);\n\t\n\tinternal static SLError sqlite3_bind_text16(IntPtr stmt, int index, string value, int nlen)\n\t\t=> sqlite3_bind_text16(stmt, index, value, nlen, -1); //SQLITE_TRANSIENT\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern SLError sqlite3_bind_zeroblob(IntPtr stmt, int index, int n);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern int sqlite3_bind_parameter_count(IntPtr stmt);\n\t\n\t[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern int sqlite3_bind_parameter_index(IntPtr stmt, byte[] strName);\n\t\n\t[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern int sqlite3_column_count(IntPtr stmt);\n\t\n\t[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern SLError sqlite3_step(IntPtr stmt);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern int sqlite3_stmt_readonly(IntPtr stmt);\n\t\n\t[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern double sqlite3_column_double(IntPtr stmt, int index);\n\t\n\t[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern int sqlite3_column_int(IntPtr stmt, int index);\n\t\n\t[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern long sqlite3_column_int64(IntPtr stmt, int index);\n\t\n\t[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern void* sqlite3_column_blob(IntPtr stmt, int index);\n\t\n\t[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern int sqlite3_column_bytes(IntPtr stmt, int index);\n\t\n\t[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern int sqlite3_column_bytes16(IntPtr stmt, int index);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern TypeAffinity sqlite3_column_type(IntPtr stmt, int index);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern SLError sqlite3_create_collation(IntPtr db, byte[] strName, int nType, IntPtr pvUser, SQLiteCollation func);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern void* sqlite3_value_blob(IntPtr p);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern int sqlite3_value_bytes(IntPtr p);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern int sqlite3_value_bytes16(IntPtr p);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern double sqlite3_value_double(IntPtr p);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern int sqlite3_value_int(IntPtr p);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern long sqlite3_value_int64(IntPtr p);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern TypeAffinity sqlite3_value_type(IntPtr p);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern void sqlite3_result_blob(IntPtr context, byte[] value, int nSize, IntPtr pvReserved);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern void sqlite3_result_double(IntPtr context, double value);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern void sqlite3_result_error(IntPtr context, byte[] strErr, int nLen);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern void sqlite3_result_error_code(IntPtr context, SLError value);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern void sqlite3_result_error_toobig(IntPtr context);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern void sqlite3_result_error_nomem(IntPtr context);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern void sqlite3_result_value(IntPtr context, IntPtr value);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern void sqlite3_result_zeroblob(IntPtr context, int nLen);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern void sqlite3_result_int(IntPtr context, int value);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern void sqlite3_result_int64(IntPtr context, long value);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern void sqlite3_result_null(IntPtr context);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern IntPtr sqlite3_aggregate_context(IntPtr context, int nBytes);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern void sqlite3_result_error16(IntPtr context, string strName, int nLen);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern void sqlite3_result_text16(IntPtr context, string strName, int nLen, IntPtr pvReserved);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern void sqlite3_progress_handler(IntPtr db, int ops, SQLiteProgressCallback func, IntPtr pvUser);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern IntPtr sqlite3_set_authorizer(IntPtr db, SQLiteAuthorizerCallback func, IntPtr pvUser);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern IntPtr sqlite3_update_hook(IntPtr db, SQLiteUpdateCallback func, IntPtr pvUser);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern IntPtr sqlite3_commit_hook(IntPtr db, SQLiteCommitCallback func, IntPtr pvUser);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern IntPtr sqlite3_trace(IntPtr db, SQLiteTraceCallback func, IntPtr pvUser);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern IntPtr sqlite3_trace_v2(IntPtr db, SQLiteTraceFlags mask, SQLiteTraceCallback2 func, IntPtr pvUser);\n\t\n\t// Since sqlite3_config() takes a variable argument list, we have to overload declarations\n\t// for all possible calls that we want to use.\n\t//[DllImport(SQLITE_DLL, EntryPoint = \"sqlite3_config\", CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern SLError sqlite3_config_none(Config op);\n\t\n\t//[DllImport(SQLITE_DLL, EntryPoint = \"sqlite3_config\", CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern SLError sqlite3_config_int(Config op, int value);\n\t\n\t////[DllImport(SQLITE_DLL, EntryPoint = \"sqlite3_config\", CallingConvention = CallingConvention.Cdecl)]\n\t////internal static extern SLError sqlite3_config_log(Config op, SQLiteLogCallback func, IntPtr pvUser);\n\t\n\t//[DllImport(SQLITE_DLL, EntryPoint = \"sqlite3_db_config\", CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern SLError sqlite3_db_config_charptr(IntPtr db, ConfigDb op, IntPtr charPtr);\n\t\n\t//[DllImport(SQLITE_DLL, EntryPoint = \"sqlite3_db_config\", CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern SLError sqlite3_db_config_int_refint(IntPtr db, ConfigDb op, int value, ref int result);\n\t\n\t//[DllImport(SQLITE_DLL, EntryPoint = \"sqlite3_db_config\", CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern SLError sqlite3_db_config_intptr_two_ints(IntPtr db, ConfigDb op, IntPtr ptr, int int0, int int1);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern SLError sqlite3_db_status(IntPtr db, SQLiteStatusOpsEnum op, ref int current, ref int highwater, int resetFlag);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern IntPtr sqlite3_rollback_hook(IntPtr db, SQLiteRollbackCallback func, IntPtr pvUser);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern IntPtr sqlite3_db_handle(IntPtr stmt);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern SLError sqlite3_db_release_memory(IntPtr db);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern IntPtr sqlite3_db_filename(IntPtr db, byte[] dbName);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern int sqlite3_db_readonly(IntPtr db, IntPtr dbName);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern IntPtr sqlite3_next_stmt(IntPtr db, IntPtr stmt);\n\t\n\t[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern SLError sqlite3_exec(IntPtr db, byte[] strSql, IntPtr pvCallback, IntPtr pvParam, byte** errMsg);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern int sqlite3_release_memory(int nBytes);\n\t\n\t[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern int sqlite3_get_autocommit(IntPtr db);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern SLError sqlite3_extended_result_codes(IntPtr db, int onoff);\n\t\n\t[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern SLError sqlite3_errcode(IntPtr db);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern SLError sqlite3_extended_errcode(IntPtr db);\n\t\n\t[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern byte* sqlite3_errstr(SLError rc);\n\t\n\tinternal static string Errstr(SLError r) => Convert2.Utf8Decode(sqlite3_errstr(r));\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern void sqlite3_log(SLError iErrCode, byte[] zFormat);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern SLError sqlite3_file_control(IntPtr db, byte[] zDbName, int op, IntPtr pArg);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern IntPtr sqlite3_backup_init(IntPtr destDb, byte[] zDestName, IntPtr sourceDb, byte[] zSourceName);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern SLError sqlite3_backup_step(IntPtr backup, int nPage);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern int sqlite3_backup_remaining(IntPtr backup);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern int sqlite3_backup_pagecount(IntPtr backup);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern SLError sqlite3_blob_close(IntPtr blob);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern int sqlite3_blob_bytes(IntPtr blob);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern SLError sqlite3_blob_open(IntPtr db, byte[] dbName, byte[] tblName, byte[] colName, long rowId, int flags, ref IntPtr ptrBlob);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern SLError sqlite3_blob_read(IntPtr blob, [MarshalAs(UnmanagedType.LPArray)] byte[] buffer, int count, int offset);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern SLError sqlite3_blob_reopen(IntPtr blob, long rowId);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern SLError sqlite3_blob_write(IntPtr blob, [MarshalAs(UnmanagedType.LPArray)] byte[] buffer, int count, int offset);\n\t\n\t//[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\n\t//internal static extern SLError sqlite3_declare_vtab(IntPtr db, byte[] zSQL);\n\t\n}\n\n#endregion\n"
  },
  {
    "path": "Au/GUI/EnumUI.cs",
    "content": "using System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Controls.Primitives;\nnamespace Au.More;\n\n/// <summary>\n/// Add enum members to a <see cref=\"popupMenu\"/> menu or WPF control.\n/// </summary>\npublic class EnumUI<TEnum> where TEnum : unmanaged, Enum {\n\t(TEnum e, object c, string text, string tt)[] _a;\n\tSelector _cb;\n\tbool _isFlags;\n\t\n\t/// <summary>\n\t/// Adds enum members to a <see cref=\"popupMenu\"/> menu as checkbox-items (if it's a <c>[Flags]</c> enum) or radio-items.\n\t/// </summary>\n\t/// <param name=\"m\"></param>\n\t/// <param name=\"init\">Initial value.</param>\n\t/// <param name=\"items\">Enum members and their text/tooltip. Optional. Text can be: <c>null</c>, <c>\"text\"</c>, <c>\"text|tooltip\"</c>, <c>\"|tooltip\"</c>.</param>\n\t/// <param name=\"sort\">Sort by name.</param>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// var m = new popupMenu();\n\t/// var f = new EnumUI<KMod>(m, KMod.Ctrl|KMod.Alt); //a [Flags] enum\n\t/// m.Separator();\n\t/// var e = new EnumUI<DayOfWeek>(m, DateTime.Today.DayOfWeek); //a non-[Flags] enum\n\t/// m.Show();\n\t/// print.it(f.Result);\n\t/// print.it(e.Result);\n\t/// ]]></code>\n\t/// </example>\n\t/// <seealso cref=\"popupMenu.AddEnum{TEnum}\"/>\n\tpublic EnumUI(popupMenu m, TEnum init = default, (TEnum value, string text)[] items = null, bool sort = false) {\n\t\tNot_.Null(m);\n\t\t_isFlags = typeof(TEnum).IsDefined(typeof(FlagsAttribute), false);\n\t\tbool cns = m.CheckDontClose; if (_isFlags) m.CheckDontClose = true;\n\t\t_InitArray(items, sort);\n\t\tfor (int i = 0; i < _a.Length; i++) {\n\t\t\tvar (e, _, text, tt) = _a[i];\n\t\t\tvar mi = _isFlags\n\t\t\t\t? m.AddCheck(text, init.HasFlag(e))\n\t\t\t\t: m.AddRadio(text, init.Equals(e));\n\t\t\tmi.Tooltip = tt;\n\t\t\t_a[i].c = mi;\n\t\t}\n\t\tif (_isFlags) m.CheckDontClose = cns;\n\t}\n\t\n\tvoid _InitArray((TEnum value, string text)[] items, bool sort) {\n\t\tif (items != null) {\n\t\t\t_a = new (TEnum e, object c, string text, string tt)[items.Length];\n\t\t\tfor (int i = 0; i < _a.Length; i++) {\n\t\t\t\tvar (v, text) = items[i];\n\t\t\t\t_a[i].e = v;\n\t\t\t\tif (text != null) {\n\t\t\t\t\tint j = text.IndexOf('|');\n\t\t\t\t\tif (j >= 0) {\n\t\t\t\t\t\t_a[i].tt = text[(j + 1)..];\n\t\t\t\t\t\ttext = j == 0 ? null : text[..j];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t_a[i].text = text ?? v.ToString();\n\t\t\t\tif (sort) _a = _a.OrderBy(o => o.text).ToArray();\n\t\t\t}\n\t\t} else {\n\t\t\tIEnumerable<TEnum> e = Enum.GetValues<TEnum>();\n\t\t\tif (_isFlags) e = e.Where(o => !EqualityComparer<TEnum>.Default.Equals(o, default));\n\t\t\tvar ee = e.Select(o => (o, (object)null, text: o.ToString(), (string)null));\n\t\t\tif (sort) ee = ee.OrderBy(o => o.text);\n\t\t\t_a = ee.ToArray();\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Adds members of a <c>[Flags]</c> enum as checkboxes to a WPF panel.\n\t/// </summary>\n\t/// <param name=\"container\"><see cref=\"StackPanel\"/>, <see cref=\"WrapPanel\"/> or <see cref=\"Grid\"/>. If <c>Grid</c> without columns, adds 2 columns.</param>\n\t/// <param name=\"init\">Initial value.</param>\n\t/// <param name=\"items\">Enum members and their text/tooltip. Optional. Text can be: <c>null</c>, <c>\"text\"</c>, <c>\"text|tooltip\"</c>, <c>\"|tooltip\"</c>.</param>\n\t/// <param name=\"sort\">Sort by name.</param>\n\t/// <example>\n\t/// With <see cref=\"wpfBuilder\"/>.\n\t/// <code><![CDATA[\n\t/// b.R.StartStack(vertical: true);\n\t/// var e = new EnumUI<KMod>(b.Panel, KMod.Ctrl|KMod.Alt);\n\t/// b.End();\n\t/// ...\n\t/// print.it(e.Result);\n\t/// ]]></code>\n\t/// </example>\n\t/// <seealso cref=\"wpfBuilder.AddEnum\"/>\n\tpublic EnumUI(Panel container, TEnum init = default, (TEnum value, string text)[] items = null, bool sort = false) {\n\t\tNot_.Null(container);\n\t\t_isFlags = true;\n\t\t_InitArray(items, sort);\n\t\t\n\t\tint nCol = 0;\n\t\tif (container is Grid g1) {\n\t\t\tnCol = g1.ColumnDefinitions.Count;\n\t\t\tif (nCol == 0) {\n\t\t\t\tnCol = 2;\n\t\t\t\tg1.AddColumns(0, -1);\n\t\t\t}\n\t\t}\n\t\t\n\t\tThickness margin = container is StackPanel { Orientation: Orientation.Vertical } ? new(2) : new(2, 2, 12, 2);\n\t\tfor (int i = 0, row = -1; i < _a.Length; i++) {\n\t\t\tvar (e, _, text, tt) = _a[i];\n\t\t\tvar c = new CheckBox {\n\t\t\t\tContent = text,\n\t\t\t\tToolTip = tt,\n\t\t\t\tIsChecked = init.HasFlag(e),\n\t\t\t\tMargin = margin,\n\t\t\t\tHorizontalAlignment = HorizontalAlignment.Stretch\n\t\t\t};\n\t\t\t_a[i].c = c;\n\t\t\tcontainer.Children.Add(c);\n\t\t\t\n\t\t\tif (nCol > 0) {\n\t\t\t\tint col = i % nCol;\n\t\t\t\tif (col == 0) { row++; ((Grid)container).RowDefinitions.Add(new() { Height = new() }); }\n\t\t\t\tGrid.SetColumn(c, col);\n\t\t\t\tGrid.SetRow(c, row);\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Adds members of a non-<c>[Flags]</c> enum to a WPF <see cref=\"Selector\"/> control, for example <see cref=\"ComboBox\"/>.\n\t/// </summary>\n\t/// <param name=\"container\"></param>\n\t/// <param name=\"init\">Initial value.</param>\n\t/// <param name=\"items\">Enum members and their text/tooltip. Optional. Text can be: <c>null</c>, <c>\"text\"</c>, <c>\"text|tooltip\"</c>, <c>\"|tooltip\"</c>.</param>\n\t/// <param name=\"sort\">Sort by name.</param>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// b.R.Add(\"Dock\", out ComboBox cb1);\n\t/// var e = new EnumUI<Dock>(cb1);\n\t/// ...\n\t/// print.it(e.Result);\n\t/// ]]></code>\n\t/// </example>\n\tpublic EnumUI(Selector container, TEnum init = default, (TEnum value, string text)[] items = null, bool sort = false) {\n\t\tNot_.Null(container);\n\t\t_cb = container;\n\t\t_InitArray(items, sort);\n\t\tint iSel = -1;\n\t\tfor (int i = 0; i < _a.Length; i++) {\n\t\t\tvar (e, _, text, tt) = _a[i];\n\t\t\tListBoxItem k = container is ComboBox ? new ComboBoxItem() : new ListBoxItem();\n\t\t\tk.Content = text;\n\t\t\tk.ToolTip = tt;\n\t\t\t_cb.Items.Add(k);\n\t\t\tif (iSel < 0 && init.Equals(e)) iSel = i;\n\t\t}\n\t\tif (iSel >= 0) _cb.SelectedIndex = iSel;\n\t}\n\t\n\t/// <summary>\n\t/// Gets enum value from checked/selected menu items or WPF elements.\n\t/// </summary>\n\tpublic TEnum Result {\n\t\tget {\n\t\t\tTEnum r = default;\n\t\t\tif (_cb != null) {\n\t\t\t\tint i = _cb.SelectedIndex;\n\t\t\t\tif (i >= 0) r = _a[i].e;\n\t\t\t} else {\n\t\t\t\tfor (int i = 0; i < _a.Length; i++) {\n\t\t\t\t\tvar (e, c, _, _) = _a[i];\n\t\t\t\t\tswitch (c) {\n\t\t\t\t\tcase PMItem mi:\n\t\t\t\t\t\tif (!mi.IsChecked) continue;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase CheckBox ch:\n\t\t\t\t\t\tif (ch.IsChecked != true) continue;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tif (_isFlags) {\n\t\t\t\t\t\tExtMisc.SetFlag(ref r, e, true);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tr = e;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn r;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au/GUI/dialog-static.cs",
    "content": "namespace Au;\n\npublic partial class dialog {\n\t#region Show\n\t\n\t/// <summary>\n\t/// Shows dialog.\n\t/// </summary>\n\t/// <returns>Selected button id.</returns>\n\t/// <param name=\"text1\">Heading text.</param>\n\t/// <param name=\"text2\">Message text. Can be string, or string with links like <c><![CDATA[new(\"Text <a>link</a> text.\", e => { print.it(\"link\"); })]]></c>.</param>\n\t/// <param name=\"buttons\">\n\t/// List of button names or <c>\"id name\"</c>. Examples: <c>\"OK|Cancel\"</c>, <c>\"1 Yes|2 No\"</c>, <c><![CDATA[\"1 &Save|2 Do&n't Save|0 Cancel\"]]></c>, <c>[\"1 One\", \"2 Two\"]</c>.\n\t/// Can contain common buttons (named <b>OK</b>, <b>Yes</b>, <b>No</b>, <b>Retry</b>, <b>Cancel</b>, <b>Close</b>) and/or custom buttons (any other names).\n\t/// This first in the list button will be focused (aka <i>default button</i>).\n\t/// More info: <see cref=\"Buttons\"/>.\n\t/// </param>\n\t/// <param name=\"flags\"></param>\n\t/// <param name=\"icon\"></param>\n\t/// <param name=\"owner\">Owner window. See <see cref=\"OwnerWindow\"/>.</param>\n\t/// <param name=\"expandedText\">Text in expander control. Can be string, or string with links like <c><![CDATA[new(\"Text <a>link</a> text.\", e => { print.it(\"link\"); })]]></c>.</param>\n\t/// <param name=\"footer\">Text at the bottom of the dialog. Can be string, or string with links like <c><![CDATA[new(\"Text <a>link</a> text.\", e => { print.it(\"link\"); })]]></c>. Icon can be specified like <c>\"i|Text\"</c>, where <c>i</c> is: <c>x</c> error, <c>!</c> warning, <c>i</c> info, <c>v</c> shield, <c>a</c> app.</param>\n\t/// <param name=\"title\">Title bar text. If omitted, <c>null</c> or <c>\"\"</c>, uses <see cref=\"options.defaultTitle\"/>.</param>\n\t/// <param name=\"controls\">Can be used to add more controls and later get their values: checkbox, radio buttons, text input.</param>\n\t/// <param name=\"x\">X position in screen. Default - screen center. Examples: <c>10</c>, <c>^10</c> (reverse), <c>.5f</c> (fraction).</param>\n\t/// <param name=\"y\">Y position in screen. Default - screen center.</param>\n\t/// <param name=\"screen\">See <see cref=\"InScreen\"/>. Examples: <c>screen.ofMouse</c>, <c>screen.at.left()</c>.</param>\n\t/// <param name=\"secondsTimeout\">If not 0, after this time (seconds) auto-close the dialog and return <see cref=\"Timeout\"/>.</param>\n\t/// <remarks>\n\t/// Tip: Use named arguments. Example: <c>dialog.show(\"Text\", icon: DIcon.Info, title: \"Title\")</c> .\n\t/// \n\t/// This function allows you to use many dialog features, but not all. Alternatively you can create a <see cref=\"dialog\"/> class instance, set properties and call <see cref=\"ShowDialog\"/>. Example in <see cref=\"dialog\"/> class help.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// if(1 != dialog.show(\"Continue?\", null, \"1 OK|2 Cancel\", icon: DIcon.Info)) return;\n\t/// print.it(\"OK\");\n\t/// \n\t/// switch (dialog.show(\"Save changes?\", \"More info.\", \"1 Save|2 Don't Save|0 Cancel\")) {\n\t/// case 1: print.it(\"save\"); break;\n\t/// case 2: print.it(\"don't\"); break;\n\t/// default: print.it(\"cancel\"); break;\n\t/// }\n\t/// ]]></code>\n\t/// \n\t/// <code><![CDATA[\n\t/// var con = new DControls { Checkbox = \"Check\", RadioButtons = \"1 One|2 Two|3 Three\", EditType = DEdit.Combo, EditText = \"zero\", ComboItems = [\"one\", \"two\"] };\n\t/// var r = dialog.show(\"Main text\", \"More text.\", \"1 OK|2 Cancel\", expandedText: \"Expanded text\", controls: con, secondsTimeout: 30);\n\t/// print.it(r, con.IsChecked, con.RadioId, con.EditText);\n\t/// switch(r) {\n\t/// case 1: print.it(\"OK\"); break;\n\t/// case dialog.Timeout: print.it(\"timeout\"); break;\n\t/// default: print.it(\"Cancel\"); break;\n\t/// }\n\t/// ]]></code>\n\t/// </example>\n\t/// <exception cref=\"Win32Exception\">Failed to show dialog. Unlikely.</exception>\n\tpublic static int show(\n\t\tstring text1 = null, DText text2 = null, Strings buttons = default, DFlags flags = 0, DIcon icon = 0, AnyWnd owner = default,\n\t\tDText expandedText = null, DText footer = null, string title = null, DControls controls = null,\n\t\tCoord x = default, Coord y = default, screen screen = default, int secondsTimeout = 0\n\t\t) {\n\t\tvar d = new dialog(text1, text2, buttons, flags, icon, owner,\n\t\t\texpandedText, footer, title, controls,\n\t\t\tx, y, screen, secondsTimeout);\n\t\treturn d.ShowDialog();\n\t}\n\t\n\t/// <summary>\n\t/// Shows dialog with <see cref=\"DIcon.Info\"/> icon.\n\t/// </summary>\n\t/// <remarks>Calls <see cref=\"show\"/>.</remarks>\n\t/// <example></example>\n\t/// <inheritdoc cref=\"show\"/>\n\tpublic static int showInfo(string text1 = null, DText text2 = null, Strings buttons = default, DFlags flags = 0, AnyWnd owner = default,\n\t\tDText expandedText = null, string title = null, int secondsTimeout = 0)\n\t\t=> show(text1, text2, buttons, flags, DIcon.Info, owner, expandedText, title: title, secondsTimeout: secondsTimeout);\n\t\n\t/// <summary>\n\t/// Shows dialog with <see cref=\"DIcon.Warning\"/> icon.\n\t/// </summary>\n\t/// <remarks>Calls <see cref=\"show\"/>.</remarks>\n\t/// <example></example>\n\t/// <inheritdoc cref=\"show\"/>\n\tpublic static int showWarning(string text1 = null, DText text2 = null, Strings buttons = default, DFlags flags = 0, AnyWnd owner = default,\n\t\tDText expandedText = null, string title = null, int secondsTimeout = 0)\n\t\t=> show(text1, text2, buttons, flags, DIcon.Warning, owner, expandedText, title: title, secondsTimeout: secondsTimeout);\n\t\n\t/// <summary>\n\t/// Shows dialog with <see cref=\"DIcon.Error\"/> icon.\n\t/// </summary>\n\t/// <remarks>Calls <see cref=\"show\"/>.</remarks>\n\t/// <example></example>\n\t/// <inheritdoc cref=\"show\"/>\n\tpublic static int showError(string text1 = null, DText text2 = null, Strings buttons = default, DFlags flags = 0, AnyWnd owner = default,\n\t\tDText expandedText = null, string title = null, int secondsTimeout = 0)\n\t\t=> show(text1, text2, buttons, flags, DIcon.Error, owner, expandedText, title: title, secondsTimeout: secondsTimeout);\n\t\n\t/// <summary>\n\t/// Shows dialog with <b>OK</b> and <b>Cancel</b> buttons.\n\t/// </summary>\n\t/// <returns><c>true</c> if selected <b>OK</b>.</returns>\n\t/// <remarks>Calls <see cref=\"show\"/>.</remarks>\n\t/// <example></example>\n\t/// <inheritdoc cref=\"show\"/>\n\tpublic static bool showOkCancel(string text1 = null, DText text2 = null, DFlags flags = 0, DIcon icon = 0, AnyWnd owner = default,\n\t\tDText expandedText = null, string title = null, int secondsTimeout = 0)\n\t\t=> 1 == show(text1, text2, \"OK|Cancel\", flags, icon, owner, expandedText, title: title, secondsTimeout: secondsTimeout);\n\t\n\t/// <summary>\n\t/// Shows dialog with <b>Yes</b> and <b>No</b> buttons.\n\t/// </summary>\n\t/// <returns><c>true</c> if selected <b>Yes</b>.</returns>\n\t/// <remarks>Calls <see cref=\"show\"/>.</remarks>\n\t/// <example></example>\n\t/// <inheritdoc cref=\"show\"/>\n\tpublic static bool showYesNo(string text1 = null, DText text2 = null, DFlags flags = 0, DIcon icon = 0, AnyWnd owner = default,\n\t\tDText expandedText = null, string title = null, int secondsTimeout = 0)\n\t\t=> 1 == show(text1, text2, \"Yes|No\", flags, icon, owner, expandedText, title: title, secondsTimeout: secondsTimeout);\n\t\n\t#endregion Show\n\t\n\t#region ShowInput\n\t\n\t/// <summary>\n\t/// Shows dialog with a text edit field and gets that text.\n\t/// </summary>\n\t/// <returns><c>true</c> if selected <b>OK</b> (or a custom button with id 1).</returns>\n\t/// <param name=\"s\">Variable that receives the text.</param>\n\t/// <param name=\"text1\">Heading text.</param>\n\t/// <param name=\"text2\">Message test (above the edit field).</param>\n\t/// <param name=\"editType\">Edit field type. It can be simple text (default), multiline, number, password or combo box.</param>\n\t/// <param name=\"editText\">Initial edit field text.</param>\n\t/// <param name=\"comboItems\">Combo box items used when <i>editType</i> is <see cref=\"DEdit.Combo\"/>.</param>\n\t/// <param name=\"flags\"></param>\n\t/// <param name=\"owner\">Owner window. See <see cref=\"OwnerWindow\"/>.</param>\n\t/// <param name=\"expandedText\">Text in expander control.</param>\n\t/// <param name=\"footer\">Text at the bottom of the dialog. Icon can be specified like <c>\"i|Text\"</c>, where <c>i</c> is: <c>x</c> error, <c>!</c> warning, <c>i</c> info, <c>v</c> shield, <c>a</c> app.</param>\n\t/// <param name=\"title\">Title bar text. If omitted, <c>null</c> or <c>\"\"</c>, uses <see cref=\"options.defaultTitle\"/>.</param>\n\t/// <param name=\"controls\">Can be used to add more controls and later get their values: checkbox, radio buttons.</param>\n\t/// <param name=\"x\">X position in screen. Default - screen center. Examples: <c>10</c>, <c>^10</c> (reverse), <c>.5f</c> (fraction).</param>\n\t/// <param name=\"y\">Y position in screen. Default - screen center.</param>\n\t/// <param name=\"screen\">See <see cref=\"InScreen\"/>. Examples: <c>screen.ofMouse</c>, <c>screen.index(1)</c>.</param>\n\t/// <param name=\"secondsTimeout\">If not 0, after this time (seconds) auto-close the dialog and return <see cref=\"Timeout\"/>.</param>\n\t/// <param name=\"buttons\">\n\t/// Buttons. List of names or <c>\"id name\"</c>. Example: <c>\"1 OK|2 Cancel|10 Browse...\"</c>. See <see cref=\"Buttons\"/>.\n\t/// Note: this function returns <c>true</c> only when clicked button with id 1.\n\t/// Usually custom buttons are used with <i>onButtonClick</i> function, which for example can get button id or disable closing the dialog.\n\t/// </param>\n\t/// <param name=\"onButtonClick\">A button-clicked event handler function. See examples.</param>\n\t/// <remarks>\n\t/// This function allows you to use many dialog features, but not all. Alternatively you can create a <see cref=\"dialog\"/> class instance, call <see cref=\"Edit\"/> or use the <i>controls</i> parameter, set other properties and call <see cref=\"ShowDialog\"/>.\n\t/// </remarks>\n\t/// <example>\n\t/// Simple.\n\t/// <code><![CDATA[\n\t/// string s;\n\t/// if(!dialog.showInput(out s, \"Example\")) return;\n\t/// print.it(s);\n\t/// \n\t/// if(!dialog.showInput(out var s2, \"Example\")) return;\n\t/// print.it(s2);\n\t/// ]]></code>\n\t/// \n\t/// With checkbox.\n\t/// <code><![CDATA[\n\t/// var con = new DControls { Checkbox = \"Check\" };\n\t/// if(!dialog.showInput(out var s, \"Example\", \"Comments.\", controls: con)) return;\n\t/// print.it(s, con.IsChecked);\n\t/// ]]></code>\n\t/// \n\t/// With <i>onButtonClick</i> function.\n\t/// <code><![CDATA[\n\t/// int r = 0;\n\t/// dialog.showInput(out string s, \"Example\", buttons: \"OK|Cancel|Later\", onButtonClick: e => r = e.Button);\n\t/// print.it(r);\n\t/// \n\t/// if(!dialog.showInput(out string s, \"Example\", flags: DFlags.CommandLinks, buttons: \"OK|Cancel|10 Set text\", onButtonClick: e => {\n\t/// \tif(e.Button == 10) { e.EditText = \"text\"; e.DontCloseDialog = true; }\n\t/// })) return;\n\t/// \n\t/// if(!dialog.showInput(out string s2, \"Example\", \"Try to click OK while text is empty.\", onButtonClick: e => {\n\t/// \tif(e.Button == 1 && e.EditText.NE()) {\n\t/// \t\tdialog.show(\"Text cannot be empty.\", owner: e.hwnd);\n\t/// \t\te.d.EditControl.Focus();\n\t/// \t\te.DontCloseDialog = true;\n\t/// \t}\n\t/// })) return;\n\t/// ]]></code>\n\t/// </example>\n\t/// <exception cref=\"Win32Exception\">Failed to show dialog.</exception>\n\tpublic static bool showInput(out string s,\n\t\tstring text1 = null, DText text2 = null,\n\t\tDEdit editType = DEdit.Text, string editText = null, Strings comboItems = default,\n\t\tDFlags flags = 0, AnyWnd owner = default,\n\t\tDText expandedText = null, DText footer = null, string title = null, DControls controls = null,\n\t\tCoord x = default, Coord y = default, screen screen = default, int secondsTimeout = 0,\n\t\tstring buttons = \"1 OK|2 Cancel\", Action<DEventArgs> onButtonClick = null\n\t\t) {\n\t\tif (buttons.NE()) buttons = \"1 OK|2 Cancel\";\n\t\tvar d = new dialog(text1, text2, buttons, flags, 0, owner, expandedText, footer, title, controls, x, y, screen, secondsTimeout);\n\t\t\n\t\td.Edit(editType != 0 ? editType : DEdit.Text, editText, comboItems);\n\t\tif (onButtonClick != null) d.ButtonClicked += onButtonClick;\n\t\t\n\t\tbool r = 1 == d.ShowDialog();\n\t\ts = r ? d._controls.EditText : null;\n\t\treturn r;\n\t}\n\t\n\t/// <summary>\n\t/// Shows dialog with a number edit field and gets that number.\n\t/// </summary>\n\t/// <returns><c>true</c> if selected <b>OK</b>.</returns>\n\t/// <param name=\"i\">Variable that receives the number.</param>\n\t/// <param name=\"text1\">Heading text.</param>\n\t/// <param name=\"text2\">Message text (above the edit field).</param>\n\t/// <param name=\"editText\">Initial edit field text.</param>\n\t/// <param name=\"flags\"></param>\n\t/// <param name=\"owner\">Owner window. See <see cref=\"OwnerWindow\"/>.</param>\n\t/// <remarks>\n\t/// Calls <see cref=\"showInput\"/> and converts string to <c>int</c>.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// int i;\n\t/// if(!dialog.showInputNumber(out i, \"Example\")) return;\n\t/// print.it(i);\n\t/// ]]></code>\n\t/// </example>\n\t/// <exception cref=\"Win32Exception\">Failed to show dialog.</exception>\n\tpublic static bool showInputNumber(out int i,\n\t\tstring text1 = null, DText text2 = null, int? editText = null,\n\t\tDFlags flags = 0, AnyWnd owner = default\n\t\t) {\n\t\ti = 0;\n\t\tif (!showInput(out string s, text1, text2, DEdit.Number, editText?.ToString(), default, flags, owner)) return false;\n\t\ti = s.ToInt();\n\t\treturn true;\n\t}\n\t\n\t#endregion ShowInput\n\t\n\t#region ShowList\n\t\n\t/// <summary>\n\t/// Shows dialog with a list of command-link buttons, and returns 1-based button index or 0.\n\t/// </summary>\n\t/// <returns>1-based index of the selected button. Returns 0 if clicked the <b>X</b> (close window) button or pressed <c>Esc</c>.</returns>\n\t/// <param name=\"list\">List items (buttons). Can be like <c>\"One|Two|Three\"</c> or <c>new(\"One\", \"Two\", \"Three\")</c> or string array or <c>List</c>. See <see cref=\"Buttons\"/>.</param>\n\t/// <param name=\"text1\">Heading text.</param>\n\t/// <param name=\"text2\">Message text.</param>\n\t/// <param name=\"flags\"></param>\n\t/// <param name=\"owner\">Owner window. See <see cref=\"OwnerWindow\"/>.</param>\n\t/// <param name=\"expandedText\">Text in expander control.</param>\n\t/// <param name=\"footer\">Text at the bottom of the dialog. Icon can be specified like <c>\"i|Text\"</c>, where <c>i</c> is: <c>x</c> error, <c>!</c> warning, <c>i</c> info, <c>v</c> shield, <c>a</c> app.</param>\n\t/// <param name=\"title\">Title bar text. If omitted, <c>null</c> or <c>\"\"</c>, uses <see cref=\"options.defaultTitle\"/>.</param>\n\t/// <param name=\"controls\">Can be used to add more controls and later get their values: checkbox, radio buttons, text input.</param>\n\t/// <param name=\"x\">X position in screen. Default - screen center. Examples: <c>10</c>, <c>^10</c> (reverse), <c>.5f</c> (fraction).</param>\n\t/// <param name=\"y\">Y position in screen. Default - screen center.</param>\n\t/// <param name=\"screen\">See <see cref=\"InScreen\"/>. Examples: <c>screen.ofMouse</c>, <c>screen.index(1)</c>.</param>\n\t/// <param name=\"secondsTimeout\">If not 0, after this time (seconds) auto-close the dialog and return <see cref=\"Timeout\"/>.</param>\n\t/// <remarks>\n\t/// This function allows you to use most of the dialog features, but not all. Alternatively you can create a <see cref=\"dialog\"/> class instance, set properties and call <see cref=\"ShowDialog\"/>. Example in <see cref=\"dialog\"/> class help.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// int r = dialog.showList(\"One|Two|Three\", \"Example\", y: -1, secondsTimeout: 15);\n\t/// if(r <= 0) return; //X/Esc or timeout\n\t/// print.it(r);\n\t/// ]]></code>\n\t/// </example>\n\t/// <exception cref=\"Win32Exception\">Failed to show dialog.</exception>\n\t/// <seealso cref=\"popupMenu.showSimple\"/>\n\tpublic static int showList(\n\t\tStrings list, string text1 = null, DText text2 = null, DFlags flags = 0, AnyWnd owner = default,\n\t\tDText expandedText = null, DText footer = null, string title = null, DControls controls = null,\n\t\tCoord x = default, Coord y = default, screen screen = default, int secondsTimeout = 0\n\t\t) {\n\t\treturn new dialog(text1, text2, default, flags | DFlags.XCancel | DFlags.ExpandDown, 0, owner,\n\t\t\texpandedText, footer, title, controls,\n\t\t\tx, y, screen, secondsTimeout)\n\t\t\t.ButtonsList(list)\n\t\t\t.ShowDialog();\n\t}\n\t\n\t#endregion ShowList\n\t\n\t#region ShowProgress\n\t\n\t/// <summary>\n\t/// Shows dialog with progress bar.\n\t/// Creates dialog in new thread and returns without waiting until it is closed.\n\t/// </summary>\n\t/// <returns>Variable that can be used to communicate with the dialog using these methods and properties: <see cref=\"IsOpen\"/>, <see cref=\"ThreadWaitForClosed\"/>, <see cref=\"Result\"/> (when closed), <see cref=\"Controls\"/> (when closed), <see cref=\"DialogWindow\"/>, <see cref=\"Send\"/>; through the <c>Send</c> property you can set progress, modify controls and close the dialog (see example).</returns>\n\t/// <param name=\"marquee\">Let the progress bar animate without indicating a percent of work done.</param>\n\t/// <remarks>\n\t/// This function allows you to use most of the dialog features, but not all. Alternatively you can create a <see cref=\"dialog\"/> class instance, set properties and call <see cref=\"ShowDialogNoWait\"/>.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// var pd = dialog.showProgress(false, \"Working\", buttons: \"1 Stop\", y: -1);\n\t/// for(int i = 1; i <= 100; i++) {\n\t/// \tif(!pd.IsOpen) { print.it(pd.Result); break; } //if the user closed the dialog\n\t/// \tpd.Send.Progress(i); //don't need this if marquee\n\t/// \t50.ms(); //do something in the loop\n\t/// }\n\t/// pd.Send.Close();\n\t/// ]]></code>\n\t/// </example>\n\t/// <inheritdoc cref=\"show\"/>\n\tpublic static dialog showProgress(bool marquee,\n\t\tstring text1 = null, DText text2 = null, string buttons = \"0 Cancel\", DFlags flags = 0, AnyWnd owner = default,\n\t\tDText expandedText = null, DText footer = null, string title = null, DControls controls = null,\n\t\tCoord x = default, Coord y = default, screen screen = default, int secondsTimeout = 0\n\t) {\n\t\tif (buttons.NE()) buttons = \"0 Cancel\";\n\t\t\n\t\tvar d = new dialog(text1, text2, buttons, flags, 0, owner,\n\t\t\texpandedText, footer, title, controls,\n\t\t\tx, y, screen, secondsTimeout);\n\t\t\n\t\td.Progress(true, marquee);\n\t\t\n\t\td.ShowDialogNoWait();\n\t\t\n\t\treturn d;\n\t}\n\t\n\t#endregion ShowProgress\n\t\n\t#region ShowNoWait\n\t\n\t/// <summary>\n\t/// Shows dialog like <see cref=\"show\"/> but does not wait.\n\t/// Creates dialog in other thread and returns without waiting until it is closed.\n\t/// </summary>\n\t/// <returns>Variable that can be used to communicate with the dialog using these methods and properties: <see cref=\"IsOpen\"/>, <see cref=\"ThreadWaitForClosed\"/>, <see cref=\"Result\"/> (when closed), <see cref=\"Controls\"/> (when closed), <see cref=\"DialogWindow\"/>, <see cref=\"Send\"/>; through the <c>Send</c> property you can modify controls and close the dialog (see example).</returns>\n\t/// <remarks>\n\t/// This function allows you to use most of the dialog features, but not all. Alternatively you can create a <see cref=\"dialog\"/> class instance, set properties and call <see cref=\"ShowDialogNoWait\"/>.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// dialog.showNoWait(\"Simple example\");\n\t/// \n\t/// var d = dialog.showNoWait(\"Another example\", \"text\", \"1 OK|2 Cancel\", y: -1, secondsTimeout: 30);\n\t/// 2.s(); //do something while the dialog is open\n\t/// d.Send.ChangeText2(\"new text\", false);\n\t/// 2.s(); //do something while the dialog is open\n\t/// d.ThreadWaitForClosed(); print.it(d.Result); //wait until the dialog is closed and get result. Optional, just an example.\n\t/// ]]></code>\n\t/// </example>\n\t/// <inheritdoc cref=\"show\"/>\n\tpublic static dialog showNoWait(\n\t\tstring text1 = null, DText text2 = null, Strings buttons = default, DFlags flags = 0, DIcon icon = 0, AnyWnd owner = default,\n\t\tDText expandedText = null, DText footer = null, string title = null, DControls controls = null,\n\t\tCoord x = default, Coord y = default, screen screen = default, int secondsTimeout = 0\n\t\t) {\n\t\tvar d = new dialog(text1, text2, buttons, flags, icon, owner,\n\t\t\texpandedText, footer, title, controls,\n\t\t\tx, y, screen, secondsTimeout);\n\t\td.ShowDialogNoWait();\n\t\treturn d;\n\t}\n\t\n\t#endregion ShowNoWait\n}\n"
  },
  {
    "path": "Au/GUI/dialog-types.cs",
    "content": "#pragma warning disable 649 //unused fields in API structs\n\nnamespace Au.Types {\n\t/// <summary>\n\t/// Text for <see cref=\"dialog\"/> functions. Supports links. Has implicit conversion from string.\n\t/// </summary>\n\t/// <param name=\"text\">Text. Links can be specified like <c><![CDATA[\"Text <a>link</a> text.\"]]></c>.</param>\n\t/// <param name=\"links\">Link click handlers like <c>e => { print.it(\"link\"); }</c>.</param>\n\tpublic record class DText(string text, params Action<DEventArgs>[] links) {\n\t\t/// <summary>\n\t\t/// Implicit conversion from string.\n\t\t/// </summary>\n\t\t/// <returns><c>null</c> if the string is <c>null</c>.</returns>\n\t\tpublic static implicit operator DText(string s) => s == null ? null : new(s, null);\n\t}\n\t\n#pragma warning disable 1591 //missing XML documentation\n\t/// <summary>\n\t/// Standard icons for <see cref=\"dialog\"/> functions.\n\t/// </summary>\n\tpublic enum DIcon {\n\t\tWarning = 0xffff,\n\t\tError = 0xfffe,\n\t\tInfo = 0xfffd,\n\t\tShield = 0xfffc,\n\t\t\n\t\t//these are undocumented but used in .NET TaskDialogStandardIcon. But why need?\n\t\t//ShieldBlueBar = ushort.MaxValue - 4,\n\t\t//ShieldGrayBar = ushort.MaxValue - 8,\n\t\t//ShieldWarningYellowBar = ushort.MaxValue - 5,\n\t\t//ShieldErrorRedBar = ushort.MaxValue - 6,\n\t\t//ShieldSuccessGreenBar = ushort.MaxValue - 7,\n\t\t\n\t\t/// <summary>\n\t\t/// Use <ms>IDI_APPLICATION</ms> icon from unmanaged resources of this program file or main assembly.\n\t\t/// If there are no icons - default program icon.\n\t\t/// C# compilers add app icon with this id. The <see cref=\"DIcon.App\"/> value is = <ms>IDI_APPLICATION</ms> (32512).\n\t\t/// If this program file contains multiple native icons in range <c>DIcon.App</c> to 0xf000, you can specify them like <c>DIcon.App+1</c>.\n\t\t/// </summary>\n\t\tApp = Api.IDI_APPLICATION\n\t}\n\t\n\t/// <summary>\n\t/// Text edit field type for <see cref=\"dialog.showInput\"/>, <see cref=\"dialog.Edit\"/>, etc.\n\t/// </summary>\n\tpublic enum DEdit {\n\t\tNone, Text, Multiline, Password, Number, Combo\n\t}\n#pragma warning restore 1591 //missing XML documentation\n\t\n\t/// <summary>\n\t/// Flags for <see cref=\"dialog\"/> functions.\n\t/// </summary>\n\t[Flags]\n\tpublic enum DFlags {\n\t\t/// <summary>\n\t\t/// Display custom buttons as a column of command-links, not as a row of classic buttons.\n\t\t/// Command links can have multi-line text. The first line has bigger font.\n\t\t/// More info about custom buttons: <see cref=\"dialog.Buttons\"/>.\n\t\t/// </summary>\n\t\tCommandLinks = 1,\n\t\t\n\t\t/// <summary>\n\t\t/// Show expanded text in footer.\n\t\t/// </summary>\n\t\tExpandDown = 1 << 1,\n\t\t\n\t\t/// <summary>\n\t\t/// Call <see cref=\"dialog.Wider\"/> with <i>width</i> 700.\n\t\t/// </summary>\n\t\tWider = 1 << 2,\n\t\t\n\t\t/// <summary>\n\t\t/// Allow to cancel even if there is no <b>Cancel</b> button.\n\t\t/// It adds <b>X</b> (Close) button to the title bar, and also allows to close the dialog with the <c>Esc</c> key.\n\t\t/// When the dialog is closed with the <b>X</b> button or <c>Esc</c>, the returned result button id is 0 if there is no <c>Cancel</c> button; else the same as when clicked the <c>Cancel</c> button.\n\t\t/// </summary>\n\t\tXCancel = 1 << 3,\n\t\t\n\t\t/// <summary>\n\t\t/// Show the dialog in the center of the owner window.\n\t\t/// </summary>\n\t\tCenterOwner = 1 << 4,\n\t\t\n\t\t/// <summary>\n\t\t/// Show the dialog at the mouse position. \n\t\t/// </summary>\n\t\tCenterMouse = 1 << 5,\n\t\t\n\t\t/// <summary>\n\t\t/// x y are raw coordinates relative to the primary screen.\n\t\t/// More info: <see cref=\"dialog.XY\"/>, <see cref=\"dialog.InScreen\"/>. \n\t\t/// </summary>\n\t\tRawXY = 1 << 6,\n\t\t\n\t\t/// <summary>\n\t\t/// Add <b>Minimize</b> button to the title bar.\n\t\t/// This flag is ignored if owner window specified.\n\t\t/// </summary>\n\t\tMinimizeButton = 1 << 7,\n\t\t\n\t\t/// <summary>\n\t\t/// Make the dialog a topmost window (always on top of other windows), regardless of <see cref=\"dialog.options.topmostIfNoOwnerWindow\"/> etc.\n\t\t/// \n\t\t/// If neither <c>Topmost</c> nor <c>NoTopmost</c> are set, makes topmost if both these are true: no owner window, <see cref=\"dialog.options.topmostIfNoOwnerWindow\"/> is <c>true</c> (default).\n\t\t/// </summary>\n\t\tTopmost = 1 << 8,\n\t\t\n\t\t/// <summary>\n\t\t/// Don't make the dialog a topmost window, regardless of <see cref=\"dialog.options.topmostIfNoOwnerWindow\"/> etc.\n\t\t/// </summary>\n\t\tNoTopmost = 1 << 9,\n\t\t\n\t\t//NoTaskbarButton = , //not so useful\n\t\t//NeverActivate = , //don't know how to implement. TDF_NO_SET_FOREGROUND does not work. LockSetForegroundWindow does not work if we can activate windows. HCBT_ACTIVATE can prevent activating but does not prevent deactivating.\n\t\t//AlwaysActivate = , //Don't use. Always allow. Because after AllowActivate (which is also used by Activate etc) always activates dialogs regardless of anything. As well as in uiAccess process.\n\t}\n\t\n\t/// <summary>\n\t/// Used with <see cref=\"dialog.show\"/> and similar functions to add more controls and get their final values.\n\t/// </summary>\n\tpublic class DControls {\n\t\t/// <summary>\n\t\t/// If not <c>null</c>, adds checkbox with this text.\n\t\t/// </summary>\n\t\tpublic string Checkbox { get; set; }\n\t\t\n\t\t/// <summary>\n\t\t/// Sets initial and gets final checkbox value (<c>true</c> if checked).\n\t\t/// </summary>\n\t\tpublic bool IsChecked { get; set; }\n\t\t\n\t\t/// <summary>\n\t\t/// Adds radio buttons.\n\t\t/// A list of strings <c>\"id text\"</c> separated by <c>|</c>, like <c>\"1 One|2 Two|3 Three\"</c>.\n\t\t/// </summary>\n\t\tpublic Strings RadioButtons { get; set; }\n\t\t\n\t\t/// <summary>\n\t\t/// Sets initial and gets final checked radio button. It is button id (as specified in <see cref=\"RadioButtons\"/>), not index.\n\t\t/// See <see cref=\"dialog.RadioButtons\"/>.\n\t\t/// </summary>\n\t\tpublic int RadioId { get; set; }\n\t\t\n\t\t/// <summary>\n\t\t/// Adds a text edit control.\n\t\t/// Note: then the dialog cannot have a progress bar.\n\t\t/// </summary>\n\t\tpublic DEdit EditType { get; set; }\n\t\t\n\t\t/// <summary>\n\t\t/// Sets initial and gets final text edit control value.\n\t\t/// </summary>\n\t\tpublic string EditText { get; set; }\n\t\t\n\t\t/// <summary>\n\t\t/// Sets combo box list items used when <see cref=\"EditType\"/> is <see cref=\"DEdit.Combo\"/>.\n\t\t/// </summary>\n\t\tpublic Strings ComboItems { get; set; }\n\t}\n\t\n\t/// <summary>\n\t/// Arguments for <see cref=\"dialog\"/> event handlers.\n\t/// </summary>\n\t/// <param name=\"d\">The dialog.</param>\n\t/// <param name=\"hwnd\">The dialog window.</param>\n\t/// <param name=\"message\">See <ms>task dialog notifications</ms>.</param>\n\t/// <param name=\"wParam\">See <ms>task dialog notifications</ms>.</param>\n\t/// <param name=\"Button\">In a <see cref=\"dialog.ButtonClicked\"/> event handler - button id.</param>\n\t/// <param name=\"TimerTimeMS\">In a <see cref=\"dialog.Timer\"/> event handler - timer time in milliseconds. The handler can set <c>returnValue</c> = 1 to reset this.</param>\n\t/// <param name=\"LinkHref\">In an <see cref=\"dialog.HyperlinkClicked\"/> event handler - the <c>href</c> attribute.</param>\n\t/// <remarks>\n\t/// To return a non-zero value from the callback function, assign the value to the <c>returnValue</c> field.\n\t/// More info: <ms>TaskDialogCallbackProc</ms>.\n\t/// </remarks>\n\tpublic record class DEventArgs(dialog d, wnd hwnd, DNative.TDN message, nint wParam, int Button, int TimerTimeMS, string LinkHref) {\n\t\t///\n\t\tpublic int returnValue;\n\t\t\n\t\t/// <summary>\n\t\t/// Your <see cref=\"dialog.ButtonClicked\"/> event handler function can use this to prevent closing the dialog.\n\t\t/// </summary>\n\t\tpublic bool DontCloseDialog { set { returnValue = value ? 1 : 0; } }\n\t\t\n\t\t/// <summary>\n\t\t/// Gets or sets edit field text.\n\t\t/// </summary>\n\t\tpublic string EditText {\n\t\t\tget => d.EditControl.ControlText;\n\t\t\tset { d.EditControl.SetText(value); }\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Can be used through <see cref=\"dialog.Send\"/>, to interact with dialog while it is open.\n\t/// </summary>\n\t/// <remarks>\n\t/// Example (in an event handler): <c>e.d.Close();</c>\n\t/// </remarks>\n\tpublic class DSend {\n\t\tvolatile dialog _tdo;\n\t\t\n\t\tinternal DSend(dialog tdo) { _tdo = tdo; }\n\t\tinternal void Clear_() { _tdo = null; }\n\t\t\n\t\t/// <summary>\n\t\t/// Sends a message to the dialog.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Call this method while the dialog is open, eg in an event handler.\n\t\t/// Example (in an event handler): <c>e.d.Send.Message(DNative.TDM.CLICK_VERIFICATION, 1);</c>\n\t\t/// Also there are several other functions to send some messages: change text, close dialog, enable/disable buttons, update progress.\n\t\t/// Reference: <ms>task dialog messages</ms>.\n\t\t/// <c>NAVIGATE_PAGE</c> not supported.\n\t\t/// </remarks>\n\t\tpublic int Message(DNative.TDM message, nint wParam = 0, nint lParam = 0) {\n\t\t\treturn _tdo?.SendMessage_(message, wParam, lParam) ?? 0;\n\t\t}\n\t\t\n\t\tvoid _SetText(bool resizeDialog, DNative.TDE partId, string text) {\n\t\t\t_tdo?.SetText_(resizeDialog, partId, text);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Changes the main big-font text.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Call this method while the dialog is open, eg in an event handler.\n\t\t/// </remarks>\n\t\tpublic void ChangeText1(string text, bool resizeDialog) {\n\t\t\t_SetText(resizeDialog, DNative.TDE.MAIN_INSTRUCTION, text);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Changes the main small-font text.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Call this method while the dialog is open, eg in an event handler.\n\t\t/// </remarks>\n\t\tpublic void ChangeText2(string text, bool resizeDialog) {\n\t\t\t_SetText(resizeDialog, DNative.TDE.CONTENT, text);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Changes the footer text.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Call this method while the dialog is open, eg in an event handler.\n\t\t/// </remarks>\n\t\tpublic void ChangeFooterText(string text, bool resizeDialog) {\n\t\t\t_SetText(resizeDialog, DNative.TDE.FOOTER, text);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Changes the expanded area text.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Call this method while the dialog is open, eg in an event handler.\n\t\t/// </remarks>\n\t\tpublic void ChangeExpandedText(string text, bool resizeDialog) {\n\t\t\t_SetText(resizeDialog, DNative.TDE.EXPANDED_INFORMATION, text);\n\t\t}\n\t\t\n#if false //currently not implemented\n\t\t/// <summary>\n\t\t/// Applies new properties to the dialog while it is already open.\n\t\t/// Call this method while the dialog is open, eg in an event handler, after setting new properties.\n\t\t/// Sends message <c>DNative.TDM.NAVIGATE_PAGE</c>.\n\t\t/// </summary>\n\t\tpublic void Reconstruct()\n\t\t{\n\t\t\tvar td = _tdo; if(td == null) return;\n\t\t\t_ApiSendMessageTASKDIALOGCONFIG(_dlg, (uint)DNative.TDM.NAVIGATE_PAGE, 0, ref td._c);\n\t\t}\n\n\t\t[DllImport(\"user32.dll\", EntryPoint = \"SendMessageW\")]\n\t\tstatic extern nint _ApiSendMessageTASKDIALOGCONFIG(wnd hWnd, uint msg, nint wParam, in TASKDIALOGCONFIG c);\n#endif\n\t\t/// <summary>\n\t\t/// Clicks a button. Normally it closes the dialog.\n\t\t/// </summary>\n\t\t/// <param name=\"buttonId\">A button id or some other number that will be returned by <see cref=\"dialog.ShowDialog\"/>.</param>\n\t\t/// <remarks>\n\t\t/// Call this method while the dialog is open, eg in an event handler.\n\t\t/// Sends message <see cref=\"DNative.TDM.CLICK_BUTTON\"/>.\n\t\t/// </remarks>\n\t\tpublic bool Close(int buttonId = 0) {\n\t\t\treturn 0 != Message(DNative.TDM.CLICK_BUTTON, buttonId);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Enables or disables a button.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Call this method while the dialog is open, eg in an event handler.\n\t\t/// Example: <c>d.Created += e => { e.d.Send.EnableButton(4, false); };</c>\n\t\t/// Sends message <see cref=\"DNative.TDM.ENABLE_BUTTON\"/>.\n\t\t/// </remarks>\n\t\tpublic void EnableButton(int buttonId, bool enable) {\n\t\t\tMessage(DNative.TDM.ENABLE_BUTTON, buttonId, enable ? 1 : 0);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Sets progress bar value, 0 to 100.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Call this method while the dialog is open, eg in an event handler.\n\t\t/// Sends message <see cref=\"DNative.TDM.SET_PROGRESS_BAR_POS\"/>.\n\t\t/// </remarks>\n\t\tpublic int Progress(int percent) {\n\t\t\tif (percent < 100) Message(DNative.TDM.SET_PROGRESS_BAR_POS, percent + 1); //workaround for the progress bar control lag. https://stackoverflow.com/questions/5332616/disabling-net-progressbar-animation-when-changing-value\n\t\t\treturn Message(DNative.TDM.SET_PROGRESS_BAR_POS, percent);\n\t\t}\n\t}\n\t\n\t#region public WinAPI\n\t\n#pragma warning disable 1591 //missing XML documentation\n\t/// <summary>\n\t/// Rarely used constants for Windows API used by <see cref=\"dialog\"/>.\n\t/// </summary>\n\t/// <remarks>\n\t/// Constants are in enums. Enum name is constant prefix. Enum members are without prefix. For example for <c>TDM_CLICK_BUTTON</c> use <c>DNative.TDM.CLICK_BUTTON</c>.\n\t/// </remarks>\n\tpublic static class DNative {\n\t\t/// <summary>\n\t\t/// Messages that your <see cref=\"dialog\"/> event handler can send to the dialog.\n\t\t/// </summary>\n\t\tpublic enum TDM {\n\t\t\tNAVIGATE_PAGE = Api.WM_USER + 101,\n\t\t\tCLICK_BUTTON = Api.WM_USER + 102, // wParam = button id\n\t\t\tSET_MARQUEE_PROGRESS_BAR = Api.WM_USER + 103, // wParam = 0 (nonMarque) wParam != 0 (Marquee)\n\t\t\tSET_PROGRESS_BAR_STATE = Api.WM_USER + 104, // wParam = new progress state (0, 1 or 2)\n\t\t\tSET_PROGRESS_BAR_RANGE = Api.WM_USER + 105, // lParam = Math2.MakeLparam(min, max)\n\t\t\tSET_PROGRESS_BAR_POS = Api.WM_USER + 106, // wParam = new position\n\t\t\tSET_PROGRESS_BAR_MARQUEE = Api.WM_USER + 107, // wParam = 0 (stop marquee), wParam != 0 (start marquee), lParam = speed (milliseconds between repaints)\n\t\t\tSET_ELEMENT_TEXT = Api.WM_USER + 108, // wParam = element (enum DNative.TDE), lParam = new element text (string)\n\t\t\tCLICK_RADIO_BUTTON = Api.WM_USER + 110, // wParam = radio button id\n\t\t\tENABLE_BUTTON = Api.WM_USER + 111, // wParam = button id, lParam = 0 (disable), lParam != 0 (enable)\n\t\t\tENABLE_RADIO_BUTTON = Api.WM_USER + 112, // wParam = radio button id, lParam = 0 (disable), lParam != 0 (enable)\n\t\t\tCLICK_VERIFICATION = Api.WM_USER + 113, // wParam = 0 (unchecked), 1 (checked), lParam = 1 (set key focus)\n\t\t\tUPDATE_ELEMENT_TEXT = Api.WM_USER + 114, // wParam = element (enum DNative.TDE), lParam = new element text (string)\n\t\t\tSET_BUTTON_ELEVATION_REQUIRED_STATE = Api.WM_USER + 115, // wParam = button id, lParam = 0 (elevation not required), lParam != 0 (elevation required)\n\t\t\tUPDATE_ICON = Api.WM_USER + 116  // wParam = icon element (enum DNative.TDIE), lParam = new icon (icon handle or DIcon)\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Notification messages that your <see cref=\"dialog\"/> event handler receives.\n\t\t/// </summary>\n\t\tpublic enum TDN : uint {\n\t\t\tCREATED = 0,\n\t\t\tNAVIGATED = 1,\n\t\t\tBUTTON_CLICKED = 2,\n\t\t\tHYPERLINK_CLICKED = 3,\n\t\t\tTIMER = 4,\n\t\t\tDESTROYED = 5,\n\t\t\tRADIO_BUTTON_CLICKED = 6,\n\t\t\tDIALOG_CONSTRUCTED = 7,\n\t\t\tVERIFICATION_CLICKED = 8,\n\t\t\tHELP = 9,\n\t\t\tEXPANDO_BUTTON_CLICKED = 10\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Constants for <see cref=\"DNative.TDM.SET_ELEMENT_TEXT\"/> and <see cref=\"DNative.TDM.UPDATE_ELEMENT_TEXT\"/> messages used with <see cref=\"dialog\"/>.\n\t\t/// </summary>\n\t\tpublic enum TDE {\n\t\t\tCONTENT,\n\t\t\tEXPANDED_INFORMATION,\n\t\t\tFOOTER,\n\t\t\tMAIN_INSTRUCTION\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Constants for <see cref=\"DNative.TDM.UPDATE_ICON\"/> message used with <see cref=\"dialog\"/>.\n\t\t/// </summary>\n\t\tpublic enum TDIE {\n\t\t\tICON_MAIN,\n\t\t\tICON_FOOTER\n\t\t}\n\t}\n#pragma warning restore 1591 //missing XML documentation\n\t\n\t#endregion public WinAPI\n\t\n}\n\nnamespace Au {\n\tpublic partial class dialog {\n\t\t#region private WinAPI\n\t\t\n\t\tdelegate int _TaskDialogIndirectDelegate(in TASKDIALOGCONFIG c, out int pnButton, out int pnRadioButton, out int pChecked);\n\t\tstatic readonly _TaskDialogIndirectDelegate TaskDialogIndirect = _GetTaskDialogIndirect();\n\t\t\n\t\tstatic _TaskDialogIndirectDelegate _GetTaskDialogIndirect() {\n\t\t\t//Activate manifest that tells to use comctl32.dll version 6. The API is unavailable in version 5.\n\t\t\t//Need this if the host app does not have such manifest, eg if uses the default manifest added by Visual Studio.\n\t\t\tusing (ActCtx_.Activate()) {\n\t\t\t\t//don't use DllImport, because it uses v5 comctl32.dll if it is already loaded.\n\t\t\t\tApi.GetDelegate(out _TaskDialogIndirectDelegate R, \"comctl32.dll\", \"TaskDialogIndirect\");\n\t\t\t\treturn R;\n\t\t\t}\n\t\t}\n\t\t\n\t\t//TASKDIALOGCONFIG flags.\n\t\t[Flags]\n\t\tenum _TDF {\n\t\t\tENABLE_HYPERLINKS = 0x0001,\n\t\t\tUSE_HICON_MAIN = 0x0002,\n\t\t\tUSE_HICON_FOOTER = 0x0004,\n\t\t\tALLOW_DIALOG_CANCELLATION = 0x0008,\n\t\t\tUSE_COMMAND_LINKS = 0x0010,\n\t\t\tUSE_COMMAND_LINKS_NO_ICON = 0x0020,\n\t\t\tEXPAND_FOOTER_AREA = 0x0040,\n\t\t\tEXPANDED_BY_DEFAULT = 0x0080,\n\t\t\tVERIFICATION_FLAG_CHECKED = 0x0100,\n\t\t\tSHOW_PROGRESS_BAR = 0x0200,\n\t\t\tSHOW_MARQUEE_PROGRESS_BAR = 0x0400,\n\t\t\tCALLBACK_TIMER = 0x0800,\n\t\t\tPOSITION_RELATIVE_TO_WINDOW = 0x1000,\n\t\t\tRTL_LAYOUT = 0x2000,\n\t\t\tNO_DEFAULT_RADIO_BUTTON = 0x4000,\n\t\t\tCAN_BE_MINIMIZED = 0x8000,\n\t\t\t//NO_SET_FOREGROUND = 0x00010000, //Win8, does not work\n\t\t\tSIZE_TO_CONTENT = 0x1000000,\n\t\t}\n\t\t\n\t\t//TASKDIALOGCONFIG buttons.\n\t\t[Flags]\n\t\tenum _TDCBF {\n\t\t\tOK = 1, Yes = 2, No = 4, Cancel = 8, Retry = 0x10, Close = 0x20,\n\t\t}\n\t\t\n\t\t[StructLayout(LayoutKind.Sequential, Pack = 1)]\n\t\tunsafe struct TASKDIALOG_BUTTON {\n\t\t\tpublic int id;\n\t\t\tpublic char* text;\n\t\t}\n\t\t\n\t\t[StructLayout(LayoutKind.Sequential, Pack = 1)]\n\t\tunsafe struct TASKDIALOGCONFIG {\n\t\t\tpublic int cbSize;\n\t\t\tpublic wnd hwndParent;\n\t\t\tpublic IntPtr hInstance;\n\t\t\tpublic _TDF dwFlags;\n\t\t\tpublic _TDCBF dwCommonButtons;\n\t\t\tpublic string pszWindowTitle;\n\t\t\tpublic IntPtr hMainIcon;\n\t\t\tpublic string pszMainInstruction;\n\t\t\tpublic string pszContent;\n\t\t\tpublic int cButtons;\n\t\t\tpublic TASKDIALOG_BUTTON* pButtons;\n\t\t\tpublic int nDefaultButton;\n\t\t\tpublic int cRadioButtons;\n\t\t\tpublic TASKDIALOG_BUTTON* pRadioButtons;\n\t\t\tpublic int nDefaultRadioButton;\n\t\t\tpublic string pszVerificationText;\n\t\t\tpublic string pszExpandedInformation;\n\t\t\tpublic string pszExpandedControlText;\n\t\t\tpublic string pszCollapsedControlText;\n\t\t\tpublic IntPtr hFooterIcon;\n\t\t\tpublic string pszFooter;\n\t\t\tpublic TaskDialogCallbackProc pfCallback;\n\t\t\tpublic IntPtr lpCallbackData;\n\t\t\tpublic int cxWidth;\n\t\t}\n\t\t\n\t\tdelegate int TaskDialogCallbackProc(wnd hwnd, DNative.TDN notification, nint wParam, nint lParam, IntPtr data);\n\t\t\n\t\t#endregion private WinAPI\n\t}\n}\n"
  },
  {
    "path": "Au/GUI/dialog-x-obsolete.cs",
    "content": "//info: the \"x\" in filename is for DocFX to correctly resolve links (changes file processing order).\n\n#if !DEBUG && NET9_0_OR_GREATER\nnamespace Au;\n\npublic partial class dialog {\n\t/// <remarks>This overload is obsolete. For text with links now use <see cref=\"DText\"/> instead of string + <i>onLinkClick</i>.</remarks>\n\t/// <inheritdoc cref=\"show(string, DText, Strings, DFlags, DIcon, AnyWnd, DText, DText, string, DControls, Coord, Coord, screen, int)\" path=\"/param\"/>\n\t[EditorBrowsable(EditorBrowsableState.Never)]\n\t[OverloadResolutionPriority(-1)]\n\tpublic dialog(\n\t\tstring text1 = null, string text2 = null, Strings buttons = default, DFlags flags = 0, DIcon icon = 0, AnyWnd owner = default,\n\t\tstring expandedText = null, string footer = null, string title = null, DControls controls = null,\n\t\tint defaultButton = 0, Coord x = default, Coord y = default, screen screen = default, int secondsTimeout = 0, Action<DEventArgs> onLinkClick = null\n\t\t) : this(text1, text2, buttons, flags, icon, owner, expandedText, footer, title, controls, x, y, screen, secondsTimeout) {\n\t\tif (defaultButton != 0) Default(defaultButton);\n\t\tif (onLinkClick != null) HyperlinkClicked += onLinkClick;\n\t}\n\t\n\t/// <remarks>This overload is obsolete. For text with links now use <see cref=\"DText\"/> instead of string + <i>onLinkClick</i>.</remarks>\n\t/// <inheritdoc cref=\"show(string, DText, Strings, DFlags, DIcon, AnyWnd, DText, DText, string, DControls, Coord, Coord, screen, int)\"/>\n\t[EditorBrowsable(EditorBrowsableState.Never)]\n\t[OverloadResolutionPriority(-1)]\n\tpublic static int show(\n\t\tstring text1 = null, string text2 = null, Strings buttons = default, DFlags flags = 0, DIcon icon = 0, AnyWnd owner = default,\n\t\tstring expandedText = null, string footer = null, string title = null, DControls controls = null,\n\t\tint defaultButton = 0, Coord x = default, Coord y = default, screen screen = default, int secondsTimeout = 0, Action<DEventArgs> onLinkClick = null\n\t\t) {\n\t\tvar d = new dialog(text1, text2, buttons, flags, icon, owner,\n\t\t\texpandedText, footer, title, controls,\n\t\t\tdefaultButton, x, y, screen, secondsTimeout, onLinkClick);\n\t\treturn d.ShowDialog();\n\t}\n\t\n\t/// <remarks>This overload is obsolete. For text with links now use <see cref=\"DText\"/> instead of string + <i>onLinkClick</i>.</remarks>\n\t/// <inheritdoc cref=\"showInput(out string, string, DText, DEdit, string, Strings, DFlags, AnyWnd, DText, DText, string, DControls, Coord, Coord, screen, int, string, Action{DEventArgs})\"/>\n\t[EditorBrowsable(EditorBrowsableState.Never)]\n\t[OverloadResolutionPriority(-1)]\n\tpublic static bool showInput(out string s,\n\t\tstring text1 = null, string text2 = null,\n\t\tDEdit editType = DEdit.Text, string editText = null, Strings comboItems = default,\n\t\tDFlags flags = 0, AnyWnd owner = default,\n\t\tstring expandedText = null, string footer = null, string title = null, DControls controls = null,\n\t\tCoord x = default, Coord y = default, screen screen = default, int secondsTimeout = 0, Action<DEventArgs> onLinkClick = null,\n\t\tstring buttons = \"1 OK|2 Cancel\", Action<DEventArgs> onButtonClick = null\n\t\t) {\n\t\tif (buttons.NE()) buttons = \"1 OK|2 Cancel\";\n\t\tvar d = new dialog(text1, text2, buttons, flags, 0, owner, expandedText, footer, title, controls, 0, x, y, screen, secondsTimeout, onLinkClick);\n\t\t\n\t\td.Edit(editType != 0 ? editType : DEdit.Text, editText, comboItems);\n\t\tif (onButtonClick != null) d.ButtonClicked += onButtonClick;\n\t\t\n\t\tbool r = 1 == d.ShowDialog();\n\t\ts = r ? d._controls.EditText : null;\n\t\treturn r;\n\t}\n\t\n\t/// <remarks>This overload is obsolete. For text with links now use <see cref=\"DText\"/> instead of string + <i>onLinkClick</i>.</remarks>\n\t/// <inheritdoc cref=\"showList(Strings, string, DText, DFlags, AnyWnd, DText, DText, string, DControls, Coord, Coord, screen, int)\"/>\n\t[EditorBrowsable(EditorBrowsableState.Never)]\n\t[OverloadResolutionPriority(-1)]\n\tpublic static int showList(\n\t\tStrings list, string text1 = null, string text2 = null, DFlags flags = 0, AnyWnd owner = default,\n\t\tstring expandedText = null, string footer = null, string title = null, DControls controls = null,\n\t\tint defaultButton = 0, Coord x = default, Coord y = default, screen screen = default, int secondsTimeout = 0,\n\t\tAction<DEventArgs> onLinkClick = null\n\t\t) {\n\t\tvar d = new dialog(text1, text2, default, flags | DFlags.XCancel | DFlags.ExpandDown, 0, owner,\n\t\t\texpandedText, footer, title, controls,\n\t\t\tdefaultButton, x, y, screen, secondsTimeout, onLinkClick);\n\t\td.ButtonsList(list);\n\t\treturn d.ShowDialog();\n\t}\n\t\n\t/// <remarks>This overload is obsolete. For text with links now use <see cref=\"DText\"/> instead of string + <i>onLinkClick</i>.</remarks>\n\t/// <inheritdoc cref=\"showProgress(bool, string, DText, string, DFlags, AnyWnd, DText, DText, string, DControls, Coord, Coord, screen, int)\"/>\n\t[EditorBrowsable(EditorBrowsableState.Never)]\n\t[OverloadResolutionPriority(-1)]\n\tpublic static dialog showProgress(bool marquee,\n\t\tstring text1 = null, string text2 = null, string buttons = \"0 Cancel\", DFlags flags = 0, AnyWnd owner = default,\n\t\tstring expandedText = null, string footer = null, string title = null, DControls controls = null,\n\t\tCoord x = default, Coord y = default, screen screen = default, int secondsTimeout = 0, Action<DEventArgs> onLinkClick = null\n\t) {\n\t\tif (buttons.NE()) buttons = \"0 Cancel\";\n\t\t\n\t\tvar d = new dialog(text1, text2, buttons, flags, 0, owner,\n\t\t\texpandedText, footer, title, controls,\n\t\t\t0, x, y, screen, secondsTimeout, onLinkClick);\n\t\t\n\t\td.Progress(true, marquee);\n\t\t\n\t\td.ShowDialogNoWait();\n\t\t\n\t\treturn d;\n\t}\n\t\n\t/// <remarks>This overload is obsolete. For text with links now use <see cref=\"DText\"/> instead of string + <i>onLinkClick</i>.</remarks>\n\t/// <inheritdoc cref=\"showNoWait(string, DText, Strings, DFlags, DIcon, AnyWnd, DText, DText, string, DControls, Coord, Coord, screen, int)\"/>\n\t[EditorBrowsable(EditorBrowsableState.Never)]\n\t[OverloadResolutionPriority(-1)]\n\tpublic static dialog showNoWait(\n\t\tstring text1 = null, string text2 = null, Strings buttons = default, DFlags flags = 0, DIcon icon = 0, AnyWnd owner = default,\n\t\tstring expandedText = null, string footer = null, string title = null, DControls controls = null,\n\t\tint defaultButton = 0, Coord x = default, Coord y = default, screen screen = default, int secondsTimeout = 0, Action<DEventArgs> onLinkClick = null\n\t\t) {\n\t\tvar d = new dialog(text1, text2, buttons, flags, icon, owner,\n\t\t\texpandedText, footer, title, controls,\n\t\t\tdefaultButton, x, y, screen, secondsTimeout, onLinkClick);\n\t\td.ShowDialogNoWait();\n\t\treturn d;\n\t}\n\t\n\t/// <inheritdoc cref=\"Title\"/>\n\t[EditorBrowsable(EditorBrowsableState.Never)]\n\tpublic void SetTitleBarText(string title) { Title(title); }\n\t\n\t/// <summary>\n\t/// Sets text.\n\t/// </summary>\n\t/// <param name=\"text1\">Heading text.</param>\n\t/// <param name=\"text2\">Message text.</param>\n\t[EditorBrowsable(EditorBrowsableState.Never)]\n\tpublic void SetText(string text1 = null, string text2 = null) {\n\t\t_c.pszMainInstruction = text1;\n\t\t_c.pszContent = text2;\n\t}\n\t\n\t///<inheritdoc cref=\"Icon(DIcon)\"/>\n\t[EditorBrowsable(EditorBrowsableState.Never)]\n\tpublic void SetIcon(DIcon icon) => Icon(icon);\n\n\t///<inheritdoc cref=\"Icon(object)\"/>\n\t[EditorBrowsable(EditorBrowsableState.Never)]\n\tpublic void SetIcon(object icon) => Icon(icon);\n\t\n\t///<inheritdoc cref=\"Buttons\"/>\n\t[EditorBrowsable(EditorBrowsableState.Never)]\n\tpublic void SetButtons(Strings buttons, bool asCommandLinks = false, Strings customButtons = default) {\n\t\tButtons(buttons, asCommandLinks).ButtonsList(customButtons, asCommandLinks);\n\t}\n\t\n\t/// <summary>\n\t/// Specifies which button responds to the <c>Enter</c> key.\n\t/// If 0 or not set, auto-selects.\n\t/// </summary>\n\t/// <value>Button id.</value>\n\t[EditorBrowsable(EditorBrowsableState.Never)]\n\tpublic int DefaultButton { set { Default(value); } }\n\t\n\t///<inheritdoc cref=\"RadioButtons\"/>\n\t[EditorBrowsable(EditorBrowsableState.Never)]\n\tpublic void SetRadioButtons(Strings buttons, int defaultId = 0) => RadioButtons(buttons, defaultId);\n\t\n\t///<inheritdoc cref=\"Checkbox\"/>\n\t[EditorBrowsable(EditorBrowsableState.Never)]\n\tpublic void SetCheckbox(string text, bool check = false) => Checkbox(text, check);\n\t\n\t///<inheritdoc cref=\"ExpandedText\"/>\n\t[EditorBrowsable(EditorBrowsableState.Never)]\n\tpublic void SetExpandedText(string text, bool showInFooter = false) => ExpandedText(text, showInFooter);\n\t\n\t///<inheritdoc cref=\"Expander\"/>\n\t[EditorBrowsable(EditorBrowsableState.Never)]\n\tpublic void SetExpandControl(bool defaultExpanded, string collapsedText = null, string expandedText = null)\n\t\t=> Expander(defaultExpanded, collapsedText, expandedText);\n\t\n\t/// <summary>\n\t/// Adds text and common icon at the bottom of the dialog.\n\t/// </summary>\n\t/// <param name=\"text\">Text, optionally preceded by an icon character and <c>|</c>, like <c>\"i|Text\"</c>. Icons: <c>x</c> error, <c>!</c> warning, <c>i</c> info, <c>v</c> shield, <c>a</c> app.</param>\n\t[EditorBrowsable(EditorBrowsableState.Never)]\n\tpublic void SetFooter(string text) => _SetFooter(text);\n\t\n\t/// <summary>\n\t/// Adds text and common icon at the bottom of the dialog.\n\t/// </summary>\n\t/// <param name=\"text\">Text.</param>\n\t/// <param name=\"icon\"></param>\n\t[EditorBrowsable(EditorBrowsableState.Never)]\n\tpublic void SetFooter(string text, DIcon icon) => FooterText(text).FooterIcon(icon);\n\t\n\t/// <summary>\n\t/// Adds text and custom icon at the bottom of the dialog.\n\t/// </summary>\n\t/// <inheritdoc cref=\"Icon(object)\" path=\"/param\"/>\n\t[EditorBrowsable(EditorBrowsableState.Never)]\n\tpublic void SetFooter(string text, object icon) => FooterText(text).FooterIcon(icon);\n\t\n\t///<inheritdoc cref=\"EditControl\"/>\n\t[EditorBrowsable(EditorBrowsableState.Never)]\n\tpublic void SetEditControl(DEdit editType, string editText = null, Strings comboItems = default)\n\t\t=> Edit(editType, editText, comboItems);\n\t\n\t/// <summary>\n\t/// Makes the dialog wider.\n\t/// </summary>\n\t[EditorBrowsable(EditorBrowsableState.Never)]\n\tpublic int Width { set { _c.cxWidth = value / 2; } }\n\t\n\t///<inheritdoc cref=\"OwnerWindow\"/>\n\t[EditorBrowsable(EditorBrowsableState.Never)]\n\tpublic void SetOwnerWindow(AnyWnd owner, bool ownerCenter = false, bool dontDisable = false)\n\t\t=> OwnerWindow(owner, ownerCenter, dontDisable);\n\t\n\t///<inheritdoc cref=\"XY\"/>\n\t[EditorBrowsable(EditorBrowsableState.Never)]\n\tpublic void SetXY(Coord x, Coord y, bool rawXY = false) => XY(x, y, rawXY);\n\t\n\t/// <summary>\n\t/// Sets a timeout to close the dialog after <i>closeAfterS</i> seconds. Then <see cref=\"ShowDialog\"/> returns <see cref=\"Timeout\"/>.\n\t/// </summary>\n\t[EditorBrowsable(EditorBrowsableState.Never)]\n\tpublic void SetTimeout(int closeAfterS, string timeoutActionText = null, bool noInfo = false)\n\t\t=> CloseAfter(closeAfterS, timeoutActionText, noInfo);\n}\n#endif\n"
  },
  {
    "path": "Au/GUI/dialog.cs",
    "content": "//rejected: by default show dialog in screen of mouse, like with <c>dialog.options.defaultScreen = screen.ofMouse;</c>.\n//\tSome Windows etc dialogs do it, and for me it's probably better. Eg Explorer's Properties even is at mouse position (top-left corner).\n//rejected: dialog.showCheckboxes. See Cookbook > Dialog - enum check-list, select.\n\nnamespace Au;\n\n/// <summary>\n/// Standard dialogs to show information or get user input.\n/// </summary>\n/// <remarks>\n/// You can use static functions like <see cref=\"show\"/> (less code) or create class instances.\n/// \n/// Uses task dialog API <ms>TaskDialogIndirect</ms>.\n/// \n/// Cannot be used in services. Instead use <see cref=\"System.Windows.Forms.MessageBox.Show\"/> with option <c>ServiceNotification</c> or <c>DefaultDesktopOnly</c>, or API <ms>MessageBox</ms> with corresponding flags, or API <ms>WTSSendMessage</ms>.\n/// </remarks>\n/// <example>\n/// Simple examples.\n/// <code><![CDATA[\n/// dialog.show(\"Example\", \"Message.);\n/// \n/// if(!dialog.showYesNo(\"Continue?\", \"More info.\")) return;\n/// \n/// switch(dialog.show(\"Save?\", \"More info.\", \"1 Save|2 Don't save|0 Cancel\")) {\n/// case 1: print.it(\"save\"); break;\n/// case 2: print.it(\"don't\"); break;\n/// default: print.it(\"cancel\"); break;\n/// }\n/// \n/// if(!dialog.showInput(out string s, \"Example\")) return;\n/// print.it(s);\n/// ]]></code>\n/// \n/// This example creates a class instance, sets properties, shows dialog, uses events, uses result.\n/// <code><![CDATA[\n/// var d = new dialog(\"Example\", \"Message.\");\n/// d.Buttons(\"1 OK|2 Cancel|3 Custom|4 Custom2\", asCommandLinks: true)\n/// \t.Icon(DIcon.Warning)\n/// \t.ExpandedText(\"Expanded info.\", true)\n/// \t.Checkbox(\"Check\")\n/// \t.RadioButtons(\"1 r1|2 r2\")\n/// \t.CloseAfter(30, \"OK\");\n/// d.ButtonClicked += e => { print.it(e.Button); if(e.Button == 4) e.DontCloseDialog = true; };\n/// d.Progress();\n/// d.Timer += e => { e.d.Send.Progress(e.TimerTimeMS / 100); };\n/// var r = d.ShowDialog();\n/// print.it(r, d.Controls.IsChecked, d.Controls.RadioId);\n/// switch(r) { case 1: print.it(\"OK\"); break; case dialog.Timeout: print.it(\"timeout\"); break; }\n/// ]]></code>\n/// </example>\npublic partial class dialog {\n\t#region static options\n\t\n\t/// <summary>\n\t/// Default options used by <see cref=\"dialog\"/> class functions.\n\t/// </summary>\n\tpublic static class options {\n\t\t/// <summary>\n\t\t/// Default title bar text.\n\t\t/// Default value - <see cref=\"script.name\"/>. In exe it is exe file name like <c>\"Example.exe\"</c>.\n\t\t/// </summary>\n\t\tpublic static string defaultTitle {\n\t\t\tget => _defaultTitle ?? script.name;\n\t\t\tset { _defaultTitle = value; }\n\t\t}\n\t\tstatic string _defaultTitle;\n\t\t\n\t\t/// <summary>\n\t\t/// Right-to-left layout.\n\t\t/// </summary>\n\t\tpublic static bool rtlLayout { get; set; }\n\t\t\n\t\t/// <summary>\n\t\t/// If there is no owner window, let the dialog be always on top of most other windows.\n\t\t/// Default <c>true</c>.\n\t\t/// </summary>\n\t\t/// <seealso cref=\"DFlags\"/>\n\t\tpublic static bool topmostIfNoOwnerWindow { get; set; } = true;\n\t\t\n\t\t/// <summary>\n\t\t/// Show dialogs on this screen when screen is not explicitly specified (<see cref=\"InScreen\"/> or parameter <i>screen</i>) and there is no owner window.\n\t\t/// The <see cref=\"screen\"/> must be lazy or empty.\n\t\t/// </summary>\n\t\t/// <exception cref=\"ArgumentException\"><see cref=\"screen\"/> with <c>Handle</c>. Must be lazy or empty.</exception>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// dialog.options.defaultScreen = screen.ofActiveWindow;\n\t\t/// dialog.options.defaultScreen = screen.ofMouse;\n\t\t/// dialog.options.defaultScreen = screen.at.left(lazy: true);\n\t\t/// dialog.options.defaultScreen = screen.index(1, lazy: true);\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic static screen defaultScreen {\n\t\t\tget => _defaultScreen;\n\t\t\tset => _defaultScreen = value.ThrowIfWithHandle_;\n\t\t}\n\t\tstatic screen _defaultScreen;\n\t\t\n\t\t/// <summary>\n\t\t/// If icon not specified, use <see cref=\"DIcon.App\"/>.\n\t\t/// </summary>\n\t\tpublic static bool useAppIcon { get; set; }\n\t\t\n\t\t/// <summary>\n\t\t/// If owner window not specified (see <see cref=\"OwnerWindow\"/>), use the active or top window of current thread as owner window (disable it, etc).\n\t\t/// </summary>\n\t\tpublic static bool autoOwnerWindow { get; set; }\n\t\t\n\t\t/// <summary>\n\t\t/// Timeout text format string. See <see cref=\"CloseAfter(int, string, bool)\"/>.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Default: <c>\"{0} s until this dialog closes, unless clicked.\\nTimeout action: {1}.\"</c>.\n\t\t/// Use placeholder <c>{0}</c> for seconds (in the first line) and <c>{1}</c> for timeout action (in the second line). \n\t\t/// </remarks>\n\t\tpublic static string timeoutTextFormat { get; set; } = c_defaultTimeoutTextFormat;\n\t\t\n\t\tinternal const string c_defaultTimeoutTextFormat = \"{0} s until this dialog closes, unless clicked.\\nTimeout action: {1}.\";\n\t}\n\t\n\t#endregion static options\n\t\n\tTASKDIALOGCONFIG _c;\n\tDFlags _flags;\n\t\n\tdialog(DFlags flags) {\n\t\t_c.cbSize = Api.SizeOf(_c);\n\t\t_flags = flags;\n\t\tRtlLayout = options.rtlLayout;\n\t}\n\t\n\t/// <summary>\n\t/// Initializes a new <see cref=\"dialog\"/> instance and sets main properties.\n\t/// </summary>\n\t/// <inheritdoc cref=\"show\" path=\"/param\"/>\n\tpublic dialog(\n\t\tstring text1 = null, DText text2 = null, Strings buttons = default, DFlags flags = 0, DIcon icon = 0, AnyWnd owner = default,\n\t\tDText expandedText = null, DText footer = null, string title = null, DControls controls = null,\n\t\tCoord x = default, Coord y = default, screen screen = default, int secondsTimeout = 0\n\t\t) : this(flags) {\n\t\tText1(text1);\n\t\tText2(text2);\n\t\tIcon(icon);\n\t\tButtons(buttons, 0 != (flags & DFlags.CommandLinks));\n\t\tif (controls != null) {\n\t\t\t_controls = controls;\n\t\t\tif (controls.Checkbox != null) Checkbox(controls.Checkbox, controls.IsChecked);\n\t\t\tif (controls.RadioButtons.Value != null) RadioButtons(controls.RadioButtons, controls.RadioId);\n\t\t}\n\t\tOwnerWindow(owner, 0 != (flags & DFlags.CenterOwner));\n\t\tXY(x, y, 0 != (flags & DFlags.RawXY));\n\t\tInScreen(screen);\n\t\tCloseAfter(secondsTimeout);\n\t\tExpandedText(expandedText, 0 != (flags & DFlags.ExpandDown));\n\t\t_SetFooter(footer);\n\t\tTitle(title);\n\t\tif (flags.Has(DFlags.Wider)) Wider(700);\n\t\tCanBeMinimized = flags.Has(DFlags.MinimizeButton);\n\t\tif (flags.Has(DFlags.Topmost)) Topmost = true; else if (flags.Has(DFlags.NoTopmost)) Topmost = false;\n\t}\n\t\n\t#region set properties\n\t\n\tvoid _SetFlag(_TDF flag, bool on) {\n\t\tif (on) _c.dwFlags |= flag; else _c.dwFlags &= ~flag;\n\t}\n\t\n\tbool _HasFlag(_TDF flag) {\n\t\treturn (_c.dwFlags & flag) != 0;\n\t}\n\t\n\t/// <summary>\n\t/// Sets title bar text.\n\t/// If not set, will use <see cref=\"options.defaultTitle\"/>.\n\t/// </summary>\n\tpublic dialog Title(string title) {\n\t\t_c.pszWindowTitle = title.NE() ? options.defaultTitle : title; //info: if \"\", API uses \"ProcessName.exe\".\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Sets heading text.\n\t/// </summary>\n\t/// <param name=\"text\">Text. Can be <c>null</c>.</param>\n\tpublic dialog Text1(string text) {\n\t\t_c.pszMainInstruction = text;\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Sets message text.\n\t/// </summary>\n\t/// <param name=\"text\">Text. Can be string, or string with links like <c><![CDATA[new(\"Text <a>link</a> text.\", e => { print.it(\"link\"); })]]></c>, or <c>null</c>.</param>\n\tpublic dialog Text2(DText text) {\n\t\t_c.pszContent = _DTextGetText(text, 0);\n\t\treturn this;\n\t}\n\t\n\tstring _DTextGetText(DText t, int caller) {\n\t\tif (t is null || t.text.NE()) return null;\n\t\tif (t.links.NE_()) return t.text;\n\t\t\n\t\t(_links ??= [[], [], []])[caller] = t.links; //replace old items in _links\n\t\t\n\t\tint i = 0;\n\t\treturn t.text.RxReplace(@\"<a\\K(?=>.+?</a>)\", m => {\n\t\t\tif (i == t.links.Length) return \"\";\n\t\t\treturn $\" href=\\\"{c_guidLink};{caller};{i++}\\\"\";\n\t\t});\n\t}\n\tconst string c_guidLink = \"37de6377cf1c43a2a6eb9a7945fca3c0\";\n\tAction<DEventArgs>[][] _links;\n\t\n\tbool _DTextLinkClicked(string href) {\n\t\tif (_links != null && href is { Length: > 35 } s && s.Starts(c_guidLink) && s.ToInt(out int caller, 33) && (uint)caller < 3 && s.ToInt(out int i, 35) && (uint)i < _links[caller].Length) {\n\t\t\t_links[caller][i]?.Invoke(new(this, _dlg, DNative.TDN.HYPERLINK_CLICKED, 0, 0, 0, null));\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\t\n\t/// <summary>\n\t/// Sets common icon.\n\t/// </summary>\n\t/// <remarks>\n\t/// The value also can be a native icon group resource id (cast to <see cref=\"DIcon\"/>), in range 1 to 0xf000.\n\t/// </remarks>\n\tpublic dialog Icon(DIcon icon) {\n\t\t_c.hMainIcon = (nint)icon;\n\t\t_iconGC = null;\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Sets custom icon.\n\t/// </summary>\n\t/// <param name=\"icon\">Can be:\n\t/// <br/>• <see cref=\"icon\"/>.\n\t/// <br/>• <see cref=\"System.Drawing.Icon\"/>.\n\t/// <br/>• <c>IntPtr</c> - native icon handle.\n\t/// <br/>• <see cref=\"System.Drawing.Bitmap\"/>.\n\t/// <br/>• string - XAML image, eg copied from the <b>Icons</b> tool. See <see cref=\"ImageUtil.LoadGdipBitmapFromXaml\"/>.\n\t/// </param>\n\t/// <remarks>\n\t/// The icon should be of logical size 32 or 16.\n\t/// </remarks>\n\tpublic dialog Icon(object icon) {\n\t\t_iconGC = icon; //will set when showing dialog, because may need screen DPI\n\t\t_c.hMainIcon = 0;\n\t\treturn this;\n\t\t//tested: displays original-size 32 and 16 icons, but shrinks bigger icons to 32.\n\t}\n\tobject _iconGC; //GC\n\t\n\t#region buttons\n\t\n\tconst int c_idOK = 1;\n\tconst int c_idCancel = 2;\n\tconst int c_idRetry = 4;\n\tconst int c_idYes = 6;\n\tconst int c_idNo = 7;\n\tconst int c_idClose = 8;\n\tconst int c_idTimeout = int.MinValue;\n\t\n\t/// <summary>\n\t/// The return value of <c>ShowX</c> functions on timeout.\n\t/// </summary>\n\tpublic const int Timeout = int.MinValue;\n\t\n\t_Buttons _buttons;\n\t\n\tstruct _Buttons {\n\t\tList<(int id, string s)> _customButtons, _radioButtons;\n\t\t\n\t\tint _defaultButtonUserId;\n\t\tbool _isDefaultButtonSet;\n\t\tpublic int DefaultButtonUserId { get => _defaultButtonUserId; set { _defaultButtonUserId = value; _isDefaultButtonSet = true; } }\n\t\t\n\t\tbool _hasXButton;\n\t\t\n\t\tpublic _TDCBF SetButtons(Strings buttons, Strings customButtons) {\n\t\t\t_customButtons = null;\n\t\t\t_mapIdUserNative = null;\n\t\t\t_defaultButtonUserId = 0;\n\t\t\t_isDefaultButtonSet = false;\n\t\t\t\n\t\t\tswitch (customButtons.Value) {\n\t\t\tcase string s:\n\t\t\t\t_ParseButtons(s, true);\n\t\t\t\tbreak;\n\t\t\tcase IEnumerable<string> e:\n\t\t\t\tint id = 0;\n\t\t\t\tforeach (var v in e) {\n\t\t\t\t\tstring s = _ParseSingleString(v, ref id, true);\n\t\t\t\t\t(_customButtons ??= new()).Add((id, s));\n\t\t\t\t}\n\t\t\t\tDefaultButtonUserId = 1;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t\n\t\t\treturn _ParseButtons(buttons, false);\n\t\t}\n\t\t\n\t\t_TDCBF _ParseButtons(Strings buttons, bool onlyCustom) {\n\t\t\tvar ba = buttons.ToArray(); if (ba.NE_()) return 0;\n\t\t\t\n\t\t\t_TDCBF commonButtons = 0;\n\t\t\tint id = 0, nextNativeId = 100;\n\t\t\t\n\t\t\tforeach (var v in ba) {\n\t\t\t\tstring s = _ParseSingleString(v, ref id, onlyCustom);\n\t\t\t\t\n\t\t\t\tint nativeId = 0;\n\t\t\t\tif (!onlyCustom) {\n\t\t\t\t\tswitch (s) {\n\t\t\t\t\tcase \"OK\": commonButtons |= _TDCBF.OK; nativeId = c_idOK; break;\n\t\t\t\t\tcase \"Yes\": commonButtons |= _TDCBF.Yes; nativeId = c_idYes; break;\n\t\t\t\t\tcase \"No\": commonButtons |= _TDCBF.No; nativeId = c_idNo; break;\n\t\t\t\t\tcase \"Cancel\": commonButtons |= _TDCBF.Cancel; nativeId = c_idCancel; break;\n\t\t\t\t\tcase \"Retry\": commonButtons |= _TDCBF.Retry; nativeId = c_idRetry; break;\n\t\t\t\t\tcase \"Close\": commonButtons |= _TDCBF.Close; nativeId = c_idClose; break;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (nativeId == 0) { //custom button\n\t\t\t\t\t(_customButtons ??= new()).Add((id, s));\n\t\t\t\t\tif (id < 0) nativeId = nextNativeId++; //need to map, because native ids of positive user ids are minus user ids\n\t\t\t\t}\n\t\t\t\tif (nativeId != 0) (_mapIdUserNative ??= new()).Add((id, nativeId));\n\t\t\t\t\n\t\t\t\tif (!_isDefaultButtonSet) DefaultButtonUserId = id;\n\t\t\t}\n\t\t\t\n\t\t\treturn commonButtons;\n\t\t}\n\t\t\n\t\tstatic string _ParseSingleString(string s, ref int id, bool dontSplit) {\n\t\t\tif (!dontSplit && StringUtil.ParseIntAndString(s, out var i, out string r)) id = i; else { r = s; id++; }\n\t\t\tr = r.Trim(\"\\r\\n\"); //API does not like newline at start, etc\n\t\t\tif (r.Length == 0) r = \" \"; //else API exception\n\t\t\telse r = r.Replace(\"\\r\\n\", \"\\n\"); //API adds 2 newlines for \\r\\n. Only for custom buttons, not for other controls/parts.\n\t\t\treturn r;\n\t\t}\n\t\t\n\t\tpublic void SetRadioButtons(Strings buttons) {\n\t\t\t_radioButtons = null;\n\t\t\tvar ba = buttons.ToArray(); if (ba.NE_()) return;\n\t\t\t\n\t\t\t_radioButtons = new();\n\t\t\tint id = 0;\n\t\t\tforeach (var v in ba) {\n\t\t\t\tstring s = _ParseSingleString(v, ref id, false);\n\t\t\t\t_radioButtons.Add((id, s));\n\t\t\t}\n\t\t}\n\t\t\n\t\tList<(int userId, int nativeId)> _mapIdUserNative;\n\t\t\n\t\tpublic int MapIdUserToNative(int userId) {\n\t\t\tif (userId == c_idTimeout) return userId; //0x80000000\n\t\t\tif (_mapIdUserNative != null) { //common buttons, and custom buttons with negative user id\n\t\t\t\tforeach (var v in _mapIdUserNative) if (v.userId == userId) return v.nativeId;\n\t\t\t}\n\t\t\treturn -userId; //custom button with positive user id\n\t\t}\n\t\t\n\t\tpublic int MapIdNativeToUser(int nativeId) {\n\t\t\tif (nativeId == c_idTimeout) return nativeId; //0x80000000\n\t\t\tif (nativeId <= 0) return -nativeId; //custom button with positive user id\n\t\t\tif (_mapIdUserNative != null) { //common buttons, and custom buttons with negative user id\n\t\t\t\tforeach (var v in _mapIdUserNative) if (v.nativeId == nativeId) return v.userId;\n\t\t\t}\n\t\t\tif (nativeId == c_idOK) return nativeId; //single OK button auto-added when no buttons specified\n\t\t\tDebug.Assert(nativeId == c_idCancel && _hasXButton);\n\t\t\treturn 0;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Sets <c>c.pButtons</c>, <c>c.cButtons</c>, <c>c.pRadioButtons</c> and <c>c.cRadioButtons</c>.\n\t\t/// Later call <c>MarshalFreeButtons</c>.\n\t\t/// </summary>\n\t\tpublic unsafe void MarshalButtons(ref TASKDIALOGCONFIG c) {\n\t\t\tc.pButtons = _MarshalButtons(false, out c.cButtons);\n\t\t\tc.pRadioButtons = _MarshalButtons(true, out c.cRadioButtons);\n\t\t\t\n\t\t\t_hasXButton = ((c.dwFlags & _TDF.ALLOW_DIALOG_CANCELLATION) != 0);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Frees memory allocated by <c>MarshalButtons</c> and sets the <i>c</i> members to <c>null</c>/0.\n\t\t/// </summary>\n\t\tpublic unsafe void MarshalFreeButtons(ref TASKDIALOGCONFIG c) {\n\t\t\tMemoryUtil.Free(c.pButtons);\n\t\t\tMemoryUtil.Free(c.pRadioButtons);\n\t\t\tc.pButtons = null; c.pRadioButtons = null;\n\t\t\tc.cButtons = 0; c.cRadioButtons = 0;\n\t\t}\n\t\t\n\t\tunsafe TASKDIALOG_BUTTON* _MarshalButtons(bool radio, out int nButtons) {\n\t\t\tvar a = radio ? _radioButtons : _customButtons;\n\t\t\tint n = a == null ? 0 : a.Count;\n\t\t\tnButtons = n;\n\t\t\tif (n == 0) return null;\n\t\t\tint nba = n * sizeof(TASKDIALOG_BUTTON), nb = nba;\n\t\t\tforeach (var v in a) nb += (v.s.Length + 1) * 2;\n\t\t\tvar r = (TASKDIALOG_BUTTON*)MemoryUtil.Alloc(nb);\n\t\t\tchar* s = (char*)((byte*)r + nba);\n\t\t\tfor (int i = 0; i < n; i++) {\n\t\t\t\tvar v = a[i];\n\t\t\t\tr[i].id = radio ? v.id : MapIdUserToNative(v.id);\n\t\t\t\tint len = v.s.Length + 1;\n\t\t\t\tr[i].text = Api.lstrcpyn(s, v.s, len);\n\t\t\t\ts += len;\n\t\t\t}\n\t\t\treturn r;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Sets buttons.\n\t/// </summary>\n\t/// <param name=\"buttons\">\n\t/// List of button names or <c>\"id name\"</c>. Examples: <c>\"OK|Cancel\"</c>, <c>\"1 Yes|2 No\"</c>, <c><![CDATA[\"1 &Save|2 Do&n't Save|0 Cancel\"]]></c>, <c>[\"1 One\", \"2 Two\"]</c>.\n\t/// Can contain common buttons (named <b>OK</b>, <b>Yes</b>, <b>No</b>, <b>Retry</b>, <b>Cancel</b>, <b>Close</b>) and/or custom buttons (any other names).\n\t/// This first in the list button will be focused (aka <i>default button</i>).\n\t/// More info in Remarks.\n\t/// </param>\n\t/// <param name=\"asCommandLinks\">The style of custom buttons. If <c>false</c> - row of classic buttons. If <c>true</c> - column of command-link buttons that can have multiline text.</param>\n\t/// <remarks>\n\t/// If buttons not set, the dialog will have <b>OK</b> button, id 1.\n\t/// \n\t/// Missing ids are auto-generated, for example <c>\"OK|Cancel|100 Custom1|Custom2\"</c> is the same as <c>\"1 OK|2 Cancel|100 Custom1|101 Custom2\"</c>.\n\t/// \n\t/// The first in the list button is the default button, ie is focused and therefore responds to the <c>Enter</c> key. For example, <c>\"2 No|1 Yes\"</c> adds <b>Yes</b> and <b>No</b> buttons and makes <b>No</b> default.\n\t/// \n\t/// To create keyboard shortcuts, use <c>&amp;</c> character in custom button labels. Use <c>&amp;&amp;</c> for literal <c>&amp;</c>. Example: <c><![CDATA[\"1 &Tuesday[]2 T&hursday[]3 Saturday && Sunday\"]]></c>.\n\t/// \n\t/// Trims newlines around ids and labels. For example, <c>\"\\r\\n1 One\\r\\n|\\r\\n2\\r\\nTwo\\r\\n\\r\\n\"</c> is the same as <c>\"1 One|2 Two\"</c>.\n\t/// \n\t/// There are 6 <i>common buttons</i>: <b>OK</b>, <b>Yes</b>, <b>No</b>, <b>Retry</b>, <b>Cancel</b>, <b>Close</b>. Buttons that have other names are <i>custom buttons</i>.\n\t/// How common buttons are different:\n\t/// 1. The button style is not affected by <i>asCommandLinks</i> or <see cref=\"DFlags.CommandLinks\"/>.\n\t/// 2. They have keyboard shortcuts that cannot be changed. Inserting <c>&amp;</c> in a label makes it a custom button.\n\t/// 3. Button <b>Cancel</b> can be selected with the <c>Esc</c> key. It also adds <b>X</b> (Close) button in title bar, which selects <b>Cancel</b>.\n\t/// 4. Always displayed in standard order (eg <b>Yes</b> <b>No</b>, never <b>No</b> <b>Yes</b>). But you can for example use <c>\"2 No|1 Yes\"</c> to set default button = <b>No</b>.\n\t/// 5. The displayed button label is localized, ie different when the Windows UI language is not English.\n\t/// </remarks>\n\tpublic dialog Buttons(Strings buttons = default, bool asCommandLinks = false) {\n\t\t_btn.buttons = buttons;\n\t\t_btn.commandLinks = asCommandLinks;\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Sets custom buttons to be displayed as a list.\n\t/// </summary>\n\t/// <param name=\"buttons\">\n\t/// List of button names. Can be string like <c>\"One|Two|...\"</c> or <c>string[]</c> or <c>List&lt;string&gt;</c>.\n\t/// Button ids will be 1, 2, ... . Default button will be 1, unless changed with <see cref=\"Default\"/>.\n\t/// Unlike <see cref=\"Buttons\"/>, this function does not allow to specify button ids; also all specified buttons will be custom buttons, even if named like <c>\"OK\"</c>.\n\t/// </param>\n\t/// <param name=\"asCommandLinks\">The style of custom buttons. If <c>false</c> - row of classic buttons. If <c>true</c> - column of command-link buttons that can have multiline text.</param>\n\t/// <remarks>\n\t/// You can call <see cref=\"Buttons\"/> too, to add common buttons (like <b>OK</b>, <b>Cancel</b>); use negative button ids. Both functions set the style (classic or command link) of custom buttons; wins the one called last.\n\t/// </remarks>\n\tpublic dialog ButtonsList(Strings buttons = default, bool asCommandLinks = true) {\n\t\t_btn.list = buttons;\n\t\t_btn.commandLinks = asCommandLinks;\n\t\treturn this;\n\t}\n\t\n\t(Strings buttons, Strings list, bool commandLinks, int idDefault) _btn;\n\t\n\t/// <summary>\n\t/// Sets default button. It responds to the <c>Enter</c> key.\n\t/// </summary>\n\t/// <param name=\"id\">Button id. If 0 - the first button in the list.</param>\n\tpublic dialog Default(int id) {\n\t\t_btn.idDefault = id;\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Adds radio buttons.\n\t/// </summary>\n\t/// <param name=\"buttons\">A list of strings <c>\"id text\"</c> separated by <c>|</c>, like <c>\"1 One|2 Two|3 Three\"</c>.</param>\n\t/// <param name=\"idDefault\">Check the radio button that has this id. If omitted or 0, checks the first. If negative, does not check.</param>\n\t/// <remarks>\n\t/// To get selected radio button id after closing the dialog, use <see cref=\"Controls\"/>.\n\t/// </remarks>\n\tpublic dialog RadioButtons(Strings buttons, int idDefault = 0) {\n\t\t_controls ??= new();\n\t\t_controls.RadioButtons = buttons;\n\t\t_controls.RadioId = idDefault;\n\t\treturn this;\n\t}\n\t\n\t#endregion buttons\n\t\n\t/// <summary>\n\t/// Adds check box (if <i>text</i> is not <c>null</c>/empty).\n\t/// </summary>\n\t/// <remarks>\n\t/// To get check box state after closing the dialog, use <see cref=\"Controls\"/>.\n\t/// </remarks>\n\tpublic dialog Checkbox(string text, bool check = false) {\n\t\t_controls ??= new();\n\t\t_controls.Checkbox = text;\n\t\t_controls.IsChecked = check;\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Adds text in expander control.\n\t/// </summary>\n\t/// <param name=\"showInFooter\">Show the text at the bottom of the dialog.</param>\n\t/// <inheritdoc cref=\"Text2\" path=\"/param\"/>\n\tpublic dialog ExpandedText(DText text, bool showInFooter = false) {\n\t\tstring s = _DTextGetText(text, 1);\n\t\t_SetFlag(_TDF.EXPAND_FOOTER_AREA, showInFooter);\n\t\t_c.pszExpandedInformation = s;\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Set properties of the expander control that shows and hides text added by <see cref=\"ExpandedText\"/>.\n\t/// </summary>\n\t/// <param name=\"expand\"></param>\n\t/// <param name=\"collapsedText\"></param>\n\t/// <param name=\"expandedText\"></param>\n\tpublic dialog Expander(bool expand, string collapsedText = null, string expandedText = null) {\n\t\t_SetFlag(_TDF.EXPANDED_BY_DEFAULT, expand);\n\t\t_c.pszCollapsedControlText = collapsedText;\n\t\t_c.pszExpandedControlText = expandedText;\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Adds footer text.\n\t/// </summary>\n\t/// <inheritdoc cref=\"Text2\" path=\"/param\"/>\n\tpublic dialog FooterText(DText text) {\n\t\t_footerText = _DTextGetText(text, 2);\n\t\treturn this;\n\t}\n\tstring _footerText;\n\t\n\t/// <summary>\n\t/// Adds footer icon.\n\t/// </summary>\n\t/// <remarks>\n\t/// The value also can be a native icon group resource id (cast to <see cref=\"DIcon\"/>), in range 1 to 0xf000.\n\t/// </remarks>\n\tpublic dialog FooterIcon(DIcon icon) {\n\t\t_c.hFooterIcon = (nint)icon;\n\t\t_iconFooterGC = null;\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Sets footer icon.\n\t/// </summary>\n\t/// <inheritdoc cref=\"Icon(object)\" path=\"/param\"/>\n\t/// <remarks>\n\t/// The icon should be of logical size 16.\n\t/// </remarks>\n\tpublic dialog FooterIcon(object icon) {\n\t\t_iconFooterGC = icon; //will set when showing dialog, because may need screen DPI\n\t\t_c.hFooterIcon = 0;\n\t\treturn this;\n\t}\n\tobject _iconFooterGC; //GC\n\t\n\t//Used by ctor. Supports string with icon, like \"i|Info\".\n\tvoid _SetFooter(DText text) {\n\t\tvar s = _DTextGetText(text, 2);\n\t\tif (s?.Eq(1, '|') ?? false) {\n\t\t\tFooterIcon(s[0] switch { 'x' => DIcon.Error, '!' => DIcon.Warning, 'i' => DIcon.Info, 'v' => DIcon.Shield, 'a' => DIcon.App, _ => 0 });\n\t\t\ts = s[2..];\n\t\t}\n\t\t_footerText = s;\n\t}\n\t\n\t/// <summary>\n\t/// Adds Edit or ComboBox control.\n\t/// </summary>\n\t/// <param name=\"editType\">Control type/style.</param>\n\t/// <param name=\"editText\">Initial edit field text.</param>\n\t/// <param name=\"comboItems\">Combo box items used when <i>editType</i> is <see cref=\"DEdit.Combo\"/>.</param>\n\t/// <remarks>\n\t/// To get control text after closing the dialog, use <see cref=\"Controls\"/>.\n\t/// \n\t/// Dialogs with an input field cannot have a progress bar.\n\t/// </remarks>\n\tpublic dialog Edit(DEdit editType, string editText = null, Strings comboItems = default) {\n\t\t_controls ??= new();\n\t\t_controls.EditType = editType;\n\t\t_controls.EditText = editText;\n\t\t_controls.ComboItems = comboItems;\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Makes the dialog wider.\n\t/// </summary>\n\t/// <param name=\"width\">\n\t/// Width of the dialog's client area, in logical pixels. The actual width depends on the screen DPI.\n\t/// If the value is less than default width, will be used default width.\n\t/// </param>\n\t/// <seealso cref=\"DFlags.Wider\"/>\n\tpublic dialog Wider(int width) {\n\t\t_c.cxWidth = width / 2;\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Sets owner window.\n\t/// </summary>\n\t/// <param name=\"owner\">Owner window, or one of its child/descendant controls. Can be <see cref=\"wnd\"/>, WPF window or element, winforms window or control. Can be <c>null</c>.</param>\n\t/// <param name=\"ownerCenter\">Show the dialog in the center of the owner window.</param>\n\t/// <param name=\"dontDisable\">Don't disable the owner window. If <c>false</c>, disables if it belongs to this thread.</param>\n\t/// <remarks>\n\t/// The owner window will be disabled, and this dialog will be on top of it.\n\t/// This window will be in owner's screen, if screen was not explicitly specified (see <see cref=\"InScreen\"/>). <see cref=\"dialog.options.defaultScreen\"/> is ignored.\n\t/// </remarks>\n\t/// <seealso cref=\"options.autoOwnerWindow\"/>\n\tpublic dialog OwnerWindow(AnyWnd owner, bool ownerCenter = false, bool dontDisable = false) {\n\t\t_c.hwndParent = owner.IsEmpty ? default : owner.Hwnd.Window;\n\t\t_SetFlag(_TDF.POSITION_RELATIVE_TO_WINDOW, ownerCenter);\n\t\t_enableOwner = dontDisable;\n\t\treturn this;\n\t}\n\tbool _enableOwner;\n\t\n\t/// <summary>\n\t/// Sets dialog position in screen.\n\t/// </summary>\n\t/// <param name=\"x\">X position in screen. If <c>default</c> - screen center. Examples: <c>10</c>, <c>^10</c> (reverse), <c>.5f</c> (fraction).</param>\n\t/// <param name=\"y\">Y position in screen. If <c>default</c> - screen center.</param>\n\t/// <param name=\"rawXY\"><i>x y</i> are relative to the primary screen (ignore <see cref=\"InScreen\"/> etc).</param>\n\t/// <seealso cref=\"InScreen\"/>\n\tpublic dialog XY(Coord x, Coord y, bool rawXY = false) {\n\t\t_x = x; _y = y;\n\t\t_rawXY = rawXY && (!_x.IsEmpty || !_y.IsEmpty);\n\t\treturn this;\n\t}\n\tCoord _x, _y; bool _rawXY;\n\t\n\t/// <summary>\n\t/// Sets the screen (display monitor) where to show the dialog in multi-screen environment.\n\t/// </summary>\n\t/// <remarks>\n\t/// If not set, will be used owner window's screen or <see cref=\"options.defaultScreen\"/>.\n\t/// More info: <see cref=\"screen\"/>, <see cref=\"wnd.MoveInScreen\"/>.\n\t/// </remarks>\n\tpublic dialog InScreen(screen screen) {\n\t\tScreen = screen;\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Sets to automatically close the dialog after <i>timeoutS</i> seconds. Then <see cref=\"ShowDialog\"/> returns <see cref=\"Timeout\"/>.\n\t/// </summary>\n\t/// <param name=\"timeoutS\">Timeout in seconds.</param>\n\t/// <param name=\"timeoutAction\">Short text to display what will happen on timeout. For example button name. See <see cref=\"options.timeoutTextFormat\"/>.</param>\n\t/// <param name=\"noInfo\">Don't display the timeout information in the footer.</param>\n\tpublic dialog CloseAfter(int timeoutS, string timeoutAction = null, bool noInfo = false) {\n\t\t_timeoutS = timeoutS;\n\t\t_timeoutActionText = timeoutAction;\n\t\t_timeoutNoInfo = noInfo;\n\t\treturn this;\n\t}\n\tint _timeoutS; bool _timeoutActive, _timeoutNoInfo; string _timeoutActionText;\n\t\n\t/// <summary>\n\t/// Sets progress bar.\n\t/// </summary>\n\t/// <param name=\"show\">Whether to show progress bar.</param>\n\t/// <param name=\"marquee\">Show just an animation that does not indicate a progress.</param>\n\t/// <remarks>\n\t/// To start or stop the marquee animation, use code like this when the dialog is visible: <c>d.Send.Message(DNative.TDM.SET_PROGRESS_BAR_MARQUEE, 1);</c>.\n\t/// To set progress, use <see cref=\"DSend.Progress(int)\"/> when the dialog is visible. Example in <see cref=\"showProgress\"/>.\n\t/// </remarks>\n\tpublic dialog Progress(bool show, bool marquee = false) {\n\t\tProgressBar = show && !marquee;\n\t\tProgressBarMarquee = show && marquee;\n\t\treturn this;\n\t}\n\t\n\t#endregion set properties\n\t\n\t#region old hidden non-fluent set properties\n\t\n\t///<inheritdoc cref=\"InScreen\"/>\n\t[EditorBrowsable(EditorBrowsableState.Never)]\n\tpublic screen Screen { set; get; }\n\t\n\t/// <summary>\n\t/// Show progress bar.\n\t/// </summary>\n\t[EditorBrowsable(EditorBrowsableState.Never)]\n\tpublic bool ProgressBar { set; get; }\n\t\n\t/// <summary>\n\t/// Show progress bar that does not indicate which part of the work is already done.\n\t/// </summary>\n\t[EditorBrowsable(EditorBrowsableState.Never)]\n\tpublic bool ProgressBarMarquee { set; get; }\n\t\n\t/// <summary>\n\t/// Right-to left layout.\n\t/// Default = <see cref=\"dialog.options.rtlLayout\"/>.\n\t/// </summary>\n\t[EditorBrowsable(EditorBrowsableState.Never)] //use options.rtlLayout instead\n\tpublic bool RtlLayout { set; get; }\n\t\n\t/// <summary>\n\t/// Add <b>Minimize</b> button to the title bar.\n\t/// </summary>\n\t[EditorBrowsable(EditorBrowsableState.Never)] //use DFlags.MinimizeButton instead\n\tpublic bool CanBeMinimized { set; get; }\n\t\n\t/// <summary>\n\t/// Makes the dialog window topmost or non-topmost.\n\t/// </summary>\n\t/// <value>\n\t/// <c>true</c> - set topmost style when creating the dialog.\n\t/// <c>false</c> - don't set.\n\t/// <c>null</c> (default) - topmost if both these are true: no owner window, <see cref=\"dialog.options.topmostIfNoOwnerWindow\"/> is <c>true</c> (default).\n\t/// </value>\n\t[EditorBrowsable(EditorBrowsableState.Never)]\n\tpublic bool? Topmost { set; get; }\n\t\n\t#endregion\n\t\n\twnd _dlg;\n\tint _threadIdInShow;\n\tbool _locked;\n\t\n\t/// <summary>\n\t/// Shows the dialog.\n\t/// Call this method after setting text and other properties.\n\t/// </summary>\n\t/// <returns>Selected button id.</returns>\n\t/// <exception cref=\"Win32Exception\">Failed to show dialog. Unlikely.</exception>\n\tpublic unsafe int ShowDialog() {\n\t\t//info: named ShowDialog, not Show, to not confuse with the static Show() which is used almost everywhere in documentation.\n\t\t\n\t\t_result = 0;\n\t\t_isClosed = false;\n\t\t\n\t\tTitle(_c.pszWindowTitle); //if not set, sets default\n\t\t_EditControlInitBeforeShowDialog(); //don't reorder, must be before flags\n\t\t\n\t\tif (_c.hwndParent.Is0 && options.autoOwnerWindow) {\n\t\t\tvar wa = wnd.thisThread.active;\n\t\t\tif (wa.Is0) wa = wnd.getwnd.TopThreadWindow_(onlyVisible: true, nonPopup: true);\n\t\t\t_c.hwndParent = wa; //info: MessageBox.Show also does it, but it also disables all thread windows\n\t\t}\n\t\tif (_c.hwndParent.IsAlive) {\n\t\t\tif (!_enableOwner && !_c.hwndParent.IsOfThisThread) _enableOwner = true;\n\t\t\tif (_enableOwner && !_c.hwndParent.IsEnabled(false)) _enableOwner = false;\n\t\t}\n\t\t\n\t\t_SetPos(true); //get screen\n\t\t\n\t\t_SetFlag(_TDF.SIZE_TO_CONTENT, true); //can make max 50% wider\n\t\t_SetFlag(_TDF.ALLOW_DIALOG_CANCELLATION, _flags.Has(DFlags.XCancel));\n\t\t_SetFlag(_TDF.RTL_LAYOUT, RtlLayout);\n\t\t_SetFlag(_TDF.CAN_BE_MINIMIZED, CanBeMinimized);\n\t\t_SetFlag(_TDF.SHOW_PROGRESS_BAR, ProgressBar);\n\t\t_SetFlag(_TDF.SHOW_MARQUEE_PROGRESS_BAR, ProgressBarMarquee);\n\t\t_SetFlag(_TDF.ENABLE_HYPERLINKS, _links != null || HyperlinkClicked != null);\n\t\t_SetFlag(_TDF.CALLBACK_TIMER, (_timeoutS > 0 || Timer != null));\n\t\t\n\t\t_c.dwCommonButtons = _buttons.SetButtons(_btn.buttons, _btn.list);\n\t\t_SetFlag(_TDF.USE_COMMAND_LINKS, _btn.commandLinks);\n\t\t_c.nDefaultButton = _buttons.MapIdUserToNative(_btn.idDefault != 0 ? _btn.idDefault : _buttons.DefaultButtonUserId);\n\t\t\n\t\tif (_controls != null) {\n\t\t\t_c.pszVerificationText = _controls.Checkbox;\n\t\t\t_SetFlag(_TDF.VERIFICATION_FLAG_CHECKED, _controls.IsChecked);\n\t\t\t\n\t\t\t_buttons.SetRadioButtons(_controls.RadioButtons);\n\t\t\t_c.nDefaultRadioButton = _controls.RadioId;\n\t\t\t_SetFlag(_TDF.NO_DEFAULT_RADIO_BUTTON, _controls.RadioId < 0);\n\t\t}\n\t\t\n\t\t_timeoutActive = _timeoutS > 0;\n\t\t_c.pszFooter = _timeoutActive && !_timeoutNoInfo ? _TimeoutFooterText(_timeoutS) : _footerText;\n\t\t\n\t\tscreen screenForIcons = default;\n\t\tif (_iconGC != null) _c.hMainIcon = _IconHandle(_iconGC, false); else if (_c.hMainIcon == 0 && options.useAppIcon) _c.hMainIcon = (nint)DIcon.App;\n\t\tif (_iconFooterGC != null) _c.hFooterIcon = _IconHandle(_iconFooterGC, true);\n\t\t_SetFlag(_TDF.USE_HICON_MAIN, _c.hMainIcon != 0 && _iconGC != null);\n\t\t_SetFlag(_TDF.USE_HICON_FOOTER, _c.hFooterIcon != 0 && _iconFooterGC != null);\n\t\t\n\t\tIntPtr _IconHandle(object o, bool small) {\n\t\t\tif (o is string s) {\n\t\t\t\tint k = small ? 16 : 32;\n\t\t\t\tif (screenForIcons.IsEmpty) screenForIcons = _GetScreenBeforeShow();\n\t\t\t\tk = Dpi.Scale(k, screenForIcons.Dpi);\n\t\t\t\to = ImageUtil.XamlIconToGdipIcon_(s, k);\n\t\t\t}\n\t\t\treturn o switch {\n\t\t\t\ticon a => a.Handle,\n\t\t\t\tSystem.Drawing.Icon a => a.Handle,\n\t\t\t\tSystem.Drawing.Bitmap a => new icon(a.GetHicon()),\n\t\t\t\tIntPtr a => a,\n\t\t\t\t_ => 0\n\t\t\t};\n\t\t}\n\t\t\n\t\tif (_iconGC == null && (long)_c.hMainIcon is >= 1 and < 0xf000) _c.hInstance = icon.GetAppIconModuleHandle_((int)_c.hMainIcon);\n\t\telse if (_iconFooterGC == null && (long)_c.hFooterIcon is >= 1 and < 0xf000) _c.hInstance = icon.GetAppIconModuleHandle_((int)_c.hFooterIcon);\n\t\t//info: DIcon.App is IDI_APPLICATION (32512).\n\t\t//Although MSDN does not mention that IDI_APPLICATION can be used when hInstance is NULL, it works. Even works for many other undocumented system resource ids, eg 100.\n\t\t//For App icon we could instead use icon handle, but then the small icon for the title bar and taskbar button can be distorted because shrinked from the big icon. Now extracts small icon from resources.\n\t\t\n\t\t_c.pfCallback = _CallbackProc;\n\t\t\n\t\tint rNativeButton = 0, rRadioButton = 0, rIsChecked = 0, hr = 0;\n\t\tWindowsHook hook = null;\n\t\t\n\t\ttry {\n\t\t\t_threadIdInShow = Environment.CurrentManagedThreadId;\n\t\t\t\n\t\t\t_buttons.MarshalButtons(ref _c);\n\t\t\tif (_c.pButtons == null) _SetFlag(_TDF.USE_COMMAND_LINKS | _TDF.USE_COMMAND_LINKS_NO_ICON, false); //avoid exception\n\t\t\t\n\t\t\tif (_timeoutActive) { //Need mouse/key messages to stop countdown on click or key.\n\t\t\t\thook = WindowsHook.ThreadGetMessage(_HookProc);\n\t\t\t}\n\t\t\t\n\t\t\twnd.Internal_.EnableActivate(true);\n\t\t\t\n\t\t\t//TaskDialog[Indirect] API bug: If called simultaneously by 2 threads, often fails and returns an unknown error code 0x800403E9.\n\t\t\t//Known workarounds:\n\t\t\t//\t1. Lock. Unlock on first callback message. Now used.\n\t\t\t//\t2. Retry. Now used only for other unexpected errors, eg out-of-memory.\n\t\t\t_LockUnlock(true);\n\t\t\t\n\t\t\tfor (int i = 0; i < 10; i++) { //see the API bug workaround comment\n\t\t\t\thr = _CallTDI(out rNativeButton, out rRadioButton, out rIsChecked);\n\t\t\t\t\n\t\t\t\t//if(hr != 0) print.it(\"0x\" + hr.ToString(\"X\"), !_dlg.Is0);\n\t\t\t\tif (hr == 0 //succeeded\n\t\t\t\t\t|| hr == Api.E_INVALIDARG //will never succeed\n\t\t\t\t\t|| hr == unchecked((int)0x8007057A) //invalid cursor handle (custom icon disposed)\n\t\t\t\t\t|| !_dlg.Is0 //_dlg is set if our callback function was called; then don't retry, because the dialog was possibly shown, and only then error.\n\t\t\t\t\t) break;\n\t\t\t\tThread.Sleep(30);\n\t\t\t}\n\t\t\t\n\t\t\tif (hr == 0) {\n\t\t\t\t_result = _buttons.MapIdNativeToUser(rNativeButton);\n\t\t\t\tif (_controls != null) {\n\t\t\t\t\t_controls.IsChecked = rIsChecked != 0;\n\t\t\t\t\t_controls.RadioId = rRadioButton;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tWndUtil.WaitForAnActiveWindow(doEvents: true);\n\t\t\t}\n\t\t}\n\t\tfinally {\n\t\t\t_LockUnlock(false);\n\t\t\t\n\t\t\t//Normally the dialog now is destroyed and _dlg now is 0, because _SetClosed called on the destroy message.\n\t\t\t//But on exception it is not called and the dialog is still alive and visible.\n\t\t\t//Therefore Windows shows its annoying \"stopped working\" UI (cannot reproduce it now with Core).\n\t\t\t//To avoid it, destroy the dialog now. Also to avoid possible memory leaks etc.\n\t\t\tif (!_dlg.Is0) Api.DestroyWindow(_dlg);\n\t\t\t\n\t\t\t_SetClosed();\n\t\t\t_threadIdInShow = 0;\n\t\t\thook?.Dispose();\n\t\t\t_buttons.MarshalFreeButtons(ref _c);\n\t\t}\n\t\t\n\t\tif (hr != 0) throw new Win32Exception(hr);\n\t\t\n\t\treturn _result;\n\t}\n\t\n\tint _CallTDI(out int pnButton, out int pnRadioButton, out int pChecked) {\n\t\t//#if DEBUG\n\t\t//\t\t\t//Debug_.PrintIf(\"1\" != Environment.GetEnvironmentVariable(\"COMPlus_legacyCorruptedStateExceptionsPolicy\"), \"no env var COMPlus_legacyCorruptedStateExceptionsPolicy=1\");\n\t\t//\t\t\tpnButton = pnRadioButton = pChecked = 0;\n\t\t//\t\t\ttry {\n\t\t//#endif\n\t\treturn TaskDialogIndirect(in _c, out pnButton, out pnRadioButton, out pChecked);\n\t\t//#if DEBUG\n\t\t//\t\t\t}\n\t\t//\t\t\tcatch (Exception e) {\n\t\t//\t\t\t\tthrow new Win32Exception(\"_CallTDI: \" + e.ToStringWithoutStack()); //note: not just throw;, and don't add inner exception\n\t\t//\t\t\t}\n\t\t\n\t\t//\t\t\t//The API throws 'access violation' exception if some value is invalid (eg unknown flags in dwCommonButtons) or it does not like something.\n\t\t//\t\t\t//By default .NET does not allow to handle eg access violation exceptions.\n\t\t//\t\t\t//\tPreviously we would add [HandleProcessCorruptedStateExceptions], but Core ignores it.\n\t\t//\t\t\t//\tNow our AppHost sets environment variable COMPlus_legacyCorruptedStateExceptionsPolicy=1 before loading runtime.\n\t\t//\t\t\t//\tOr could move the API call to the C++ dll.\n\t\t//#endif\n\t}\n\t\n\tvoid _LockUnlock(bool on) {\n\t\tvar obj = \"/0p4oSiwoE+7Saqf30udQQ\";\n\t\tif (on) {\n\t\t\tDebug.Assert(!_locked);\n\t\t\t_locked = false;\n\t\t\tMonitor.Enter(obj, ref _locked);\n\t\t} else if (_locked) {\n\t\t\tMonitor.Exit(obj);\n\t\t\t_locked = false;\n\t\t}\n\t}\n\t\n\t//Called twice:\n\t//\t1. Before showing dialog, to get screen while the dialog still isn't the active window if need. On TDN.CREATED screen.ofActiveWindow would be bad.\n\t//\t2. On TDN.CREATED, to move dialog if need.\n\tvoid _SetPos(bool before) {\n\t\tif (before) _screenForMove = default;\n\t\tif (_HasFlag(_TDF.POSITION_RELATIVE_TO_WINDOW)) return;\n\t\tif (_flags.Has(DFlags.CenterMouse)) {\n\t\t\tif (!before) {\n\t\t\t\tvar p = mouse.xy;\n\t\t\t\tvar scrn = screen.of(p);\n\t\t\t\tif (screen.of(_dlg).Handle != scrn.Handle) _dlg.MoveL_(scrn.Rect.XY); //resize if different DPI\n\t\t\t\tvar r = _dlg.Rect;\n\t\t\t\tr.Move(p.x - r.Width / 2, p.y - 20);\n\t\t\t\tr.EnsureInScreen(scrn);\n\t\t\t\t_dlg.MoveL(r.left, r.top);\n\t\t\t}\n\t\t} else if (!_rawXY) {\n\t\t\tif (before) {\n\t\t\t\t_screenForMove = Screen;\n\t\t\t\tif (_screenForMove.IsEmpty && _c.hwndParent.Is0) _screenForMove = options.defaultScreen;\n\t\t\t\tif (_screenForMove.LazyFunc != null) _screenForMove = _screenForMove.Now;\n\t\t\t} else if (!_x.IsEmpty || !_y.IsEmpty || !_screenForMove.IsEmpty) {\n\t\t\t\t_dlg.MoveInScreen(_x, _y, _screenForMove);\n\t\t\t}\n\t\t} else if (!before) {\n\t\t\t_dlg.Move(_x, _y);\n\t\t\t_dlg.EnsureInScreen();\n\t\t}\n\t}\n\tscreen _screenForMove;\n\t\n\t//To get DPI for icons.\n\tscreen _GetScreenBeforeShow() {\n\t\tif (!_screenForMove.IsEmpty) return _screenForMove;\n\t\tif (Api.GetSystemMetrics(Api.SM_CMONITORS) > 1) {\n\t\t\tif (_HasFlag(_TDF.POSITION_RELATIVE_TO_WINDOW)) return screen.of(_c.hwndParent); //ownerCenter\n\t\t\tif (_flags.Has(DFlags.CenterMouse)) return screen.of(mouse.xy);\n\t\t\tif (_rawXY) return screen.of(Coord.Normalize(_x, _y, centerIfEmpty: true));\n\t\t\tif (!_c.hwndParent.Is0) return screen.of(_c.hwndParent);\n\t\t}\n\t\treturn screen.primary;\n\t}\n\t\n\tint _CallbackProc(wnd w, DNative.TDN message, nint wParam, nint lParam, IntPtr data) {\n\t\tAction<DEventArgs> e = null;\n\t\tint R = 0, button = 0, timerTime = 0;\n\t\tstring linkHref = null;\n\t\t\n\t\t//print.it(message);\n\t\tswitch (message) {\n\t\tcase DNative.TDN.DIALOG_CONSTRUCTED:\n\t\t\t_LockUnlock(false);\n\t\t\tSend = new DSend(this); //note: must be before setting _dlg, because another thread may call if(d.IsOpen) d.Send.Message(..).\n\t\t\t_dlg = w;\n\t\t\tbreak;\n\t\tcase DNative.TDN.DESTROYED:\n\t\t\t//print.it(w.IsAlive); //valid\n\t\t\te = Destroyed;\n\t\t\tbreak;\n\t\tcase DNative.TDN.CREATED:\n\t\t\tif (_enableOwner) _c.hwndParent.Enable(true);\n\t\t\t_SetPos(false);\n\t\t\t\n\t\t\tif (Topmost ?? (_c.hwndParent.Is0 && options.topmostIfNoOwnerWindow)) w.ZorderTopmost();\n\t\t\t\n\t\t\t//w.SetStyleAdd(WS.THICKFRAME); //does not work\n\t\t\t\n\t\t\tif (_IsEdit) _EditControlCreate();\n\t\t\telse if (ProgressBarMarquee) Send.Message(DNative.TDM.SET_PROGRESS_BAR_MARQUEE, 1);\n\t\t\t\n\t\t\t//if(FlagKeyboardShortcutsVisible) w.Post(Api.WM_UPDATEUISTATE, 0x30002); //rejected. Don't need too many rarely used features.\n\t\t\t\n\t\t\t//fix API bug: dialog window is hidden if process STARTUPINFO specifies hidden window\n\t\t\ttimer.after(1, _ => _dlg.ShowL(true)); //use timer because at this time still invisible always\n\t\t\t\n\t\t\te = Created;\n\t\t\tbreak;\n\t\tcase DNative.TDN.TIMER:\n\t\t\ttimerTime = (int)wParam;\n\t\t\tif (_timeoutActive) {\n\t\t\t\tint timeElapsed = timerTime / 1000;\n\t\t\t\tif (timeElapsed < _timeoutS) {\n\t\t\t\t\tif (!_timeoutNoInfo) Send.ChangeFooterText(_TimeoutFooterText(_timeoutS - timeElapsed - 1), false);\n\t\t\t\t} else {\n\t\t\t\t\t_timeoutActive = false;\n\t\t\t\t\tSend.Close(c_idTimeout);\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\te = Timer;\n\t\t\tbreak;\n\t\tcase DNative.TDN.BUTTON_CLICKED:\n\t\t\te = ButtonClicked;\n\t\t\tbutton = _buttons.MapIdNativeToUser((int)wParam);\n\t\t\tbreak;\n\t\tcase DNative.TDN.HYPERLINK_CLICKED:\n\t\t\tlinkHref = Marshal.PtrToStringUni(lParam);\n\t\t\tif (_DTextLinkClicked(linkHref)) return 0;\n\t\t\te = HyperlinkClicked;\n\t\t\tbreak;\n\t\tcase DNative.TDN.HELP:\n\t\t\te = HelpF1;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\te = OtherEvents;\n\t\t\tbreak;\n\t\t}\n\t\t\n\t\tif (_IsEdit) _EditControlOnMessage(message);\n\t\t\n\t\tif (e != null) {\n\t\t\tvar ed = new DEventArgs(this, _dlg, message, wParam, button, timerTime, linkHref);\n\t\t\te(ed);\n\t\t\tR = ed.returnValue;\n\t\t}\n\t\t\n\t\tif (message == DNative.TDN.DESTROYED) _SetClosed();\n\t\t\n\t\treturn R;\n\t}\n\t\n\t/// <summary>\n\t/// After the dialog has been created and before it is displayed.\n\t/// </summary>\n\tpublic event Action<DEventArgs> Created;\n\t\n\t/// <summary>\n\t/// When the dialog is closed and its window handle is no longer valid.\n\t/// </summary>\n\tpublic event Action<DEventArgs> Destroyed;\n\t\n\t/// <summary>\n\t/// Every 200 ms.\n\t/// </summary>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// var d = new dialog(\"test\");\n\t/// d.Timer += e => { print.it(e.TimerTimeMS); };\n\t/// d.ShowDialog();\n\t/// ]]></code>\n\t/// </example>\n\tpublic event Action<DEventArgs> Timer;\n\t\n\t/// <summary>\n\t/// When the user selects a button.\n\t/// </summary>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// var d = new dialog(\"test\", buttons: \"1 Can close|2 Can't close\");\n\t/// d.ButtonClicked += e => { print.it(e.Button); e.DontCloseDialog = e.Button == 2; };\n\t/// d.ShowDialog();\n\t/// ]]></code>\n\t/// </example>\n\tpublic event Action<DEventArgs> ButtonClicked;\n\t\n\t/// <summary>\n\t/// When the user clicks a hyperlink in the dialog text.\n\t/// </summary>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// var d = new dialog(\"test\", \"Text with <a href=\\\"link data\\\">links</a>.\");\n\t/// d.HyperlinkClicked += e => { print.it(e.LinkHref); };\n\t/// d.ShowDialog();\n\t/// ]]></code>\n\t/// </example>\n\tpublic event Action<DEventArgs> HyperlinkClicked;\n\t\n\t/// <summary>\n\t/// When the user presses <c>F1</c>.\n\t/// </summary>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// var d = new dialog(\"test\", \"Some info.\", footer: \"Press F1 for more info.\");\n\t/// d.HelpF1 += e => { run.it(\"https://www.google.com/search?q=more+info\"); };\n\t/// d.ShowDialog();\n\t/// ]]></code>\n\t/// </example>\n\tpublic event Action<DEventArgs> HelpF1;\n\t\n\t/// <summary>\n\t/// Events other than <see cref=\"Created\"/>, <see cref=\"Destroyed\"/>, <see cref=\"Timer\"/>, <see cref=\"ButtonClicked\"/>, <see cref=\"HyperlinkClicked\"/>, <see cref=\"HelpF1\"/>. See API <ms>TaskDialogCallbackProc</ms>.\n\t/// </summary>\n\tpublic event Action<DEventArgs> OtherEvents;\n\t\n\t#region async etc\n\t\n\t/// <summary>\n\t/// Shows the dialog in new thread and returns without waiting until it is closed.\n\t/// </summary>\n\t/// <remarks>\n\t/// Calls <see cref=\"ThreadWaitForOpen\"/>, therefore the dialog is already open when this function returns.\n\t/// More info: <see cref=\"showNoWait\"/>\n\t/// </remarks>\n\t/// <exception cref=\"Win32Exception\">Failed to show dialog. Unlikely.</exception>\n\tpublic void ShowDialogNoWait() {\n\t\tException ex = null;\n\t\tTask.Run(() => { try { ShowDialog(); } catch (Exception e) { ex = e; } });\n\t\tif (!ThreadWaitForOpen()) throw ex ?? new Win32Exception(0);\n\t}\n\t\n\t/// <summary>\n\t/// Selected button id. The same as the <see cref=\"ShowDialog\"/> return value.\n\t/// </summary>\n\t/// <remarks>\n\t/// If the result is still unavailable (the dialog still not closed):\n\t/// - If called from the same thread that called <see cref=\"ShowDialog\"/>, returns 0.\n\t/// - If called from another thread, waits until the dialog is closed.\n\t/// \n\t/// Note: <see cref=\"ShowDialogNoWait\"/> calls <see cref=\"ShowDialog\"/> in another thread.\n\t/// </remarks>\n\tpublic int Result {\n\t\tget {\n\t\t\tif (!_WaitWhileInShow()) return 0;\n\t\t\treturn _result;\n\t\t}\n\t}\n\tint _result;\n\t\n\t/// <summary>\n\t/// After closing the dialog contains values of checkbox, radio buttons and/or text edit control.\n\t/// <c>null</c> if no controls.\n\t/// </summary>\n\tpublic DControls Controls => _controls;\n\tDControls _controls;\n\t\n\tbool _WaitWhileInShow() {\n\t\tif (_threadIdInShow != 0) {\n\t\t\tif (_threadIdInShow == Environment.CurrentManagedThreadId) return false;\n\t\t\twhile (_threadIdInShow != 0) Thread.Sleep(15);\n\t\t}\n\t\treturn true;\n\t}\n\t\n\t/// <summary>\n\t/// Can be used by other threads to wait until the dialog is open.\n\t/// </summary>\n\t/// <returns>\n\t/// <br/>• <c>true</c> - the dialog is open and you can send messages to it.\n\t/// <br/>• <c>false</c> - the dialog is already closed or failed to show.\n\t/// </returns>\n\tpublic bool ThreadWaitForOpen() {\n\t\t_AssertIsOtherThread();\n\t\twhile (!IsOpen) {\n\t\t\tif (_isClosed) return false;\n\t\t\twait.doEvents(15); //need 2-3 loops if 15. Without doEvents hangs if a form is the dialog owner.\n\t\t}\n\t\treturn true;\n\t}\n\t\n\t/// <summary>\n\t/// Can be used by other threads to wait until the dialog is closed.\n\t/// </summary>\n\tpublic void ThreadWaitForClosed() {\n\t\t_AssertIsOtherThread();\n\t\twhile (!_isClosed) {\n\t\t\tThread.Sleep(30);\n\t\t}\n\t\t_WaitWhileInShow();\n\t}\n\t\n\tvoid _AssertIsOtherThread() {\n\t\tif (_threadIdInShow != 0 && _threadIdInShow == Environment.CurrentManagedThreadId)\n\t\t\tthrow new AuException(\"wrong thread\");\n\t}\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if the dialog is open and your code can send messages to it.\n\t/// </summary>\n\tpublic bool IsOpen => !_dlg.Is0;\n\t\n\tvoid _SetClosed() {\n\t\t_isClosed = true;\n\t\tif (_dlg.Is0) return;\n\t\t_dlg = default;\n\t\tSend.Clear_();\n\t}\n\tbool _isClosed;\n\t\n\t#endregion async etc\n\t\n\t#region send messages\n\t\n\t/// <summary>\n\t/// Gets dialog window handle as <see cref=\"wnd\"/>.\n\t/// </summary>\n\t/// <returns><c>default(wnd)</c> if the dialog is not open.</returns>\n\tpublic wnd DialogWindow => _dlg;\n\t\n\t/// <summary>\n\t/// Allows to modify dialog controls while it is open, and close the dialog.\n\t/// </summary>\n\t/// <remarks>\n\t/// Example: <c>d.Send.Close();</c> .\n\t/// Example: <c>d.Send.ChangeText2(\"new text\", false);</c> .\n\t/// Example: <c>d.Send.Message(DNative.TDM.CLICK_VERIFICATION, 1);</c> .\n\t/// \n\t/// Can be used only while the dialog is open. Before showing the dialog returns <c>null</c>. After closing the dialog the returned variable is deactivated; its method calls are ignored.\n\t/// Can be used in dialog event handlers. Also can be used in another thread, for example with <see cref=\"showNoWait\"/> and <see cref=\"showProgress\"/>.\n\t/// </remarks>\n\tpublic DSend Send { get; private set; }\n\t\n\t//called by DSend\n\tinternal int SendMessage_(DNative.TDM message, nint wParam = 0, nint lParam = 0) {\n\t\tswitch (message) {\n\t\tcase DNative.TDM.CLICK_BUTTON:\n\t\tcase DNative.TDM.ENABLE_BUTTON:\n\t\tcase DNative.TDM.SET_BUTTON_ELEVATION_REQUIRED_STATE:\n\t\t\twParam = _buttons.MapIdUserToNative((int)wParam);\n\t\t\tbreak;\n\t\t}\n\t\t\n\t\treturn (int)_dlg.Send((int)message, wParam, lParam);\n\t}\n\t\n\t//called by DSend\n\tinternal void SetText_(bool resizeDialog, DNative.TDE partId, string text) {\n\t\tif (partId == DNative.TDE.CONTENT && (_controls?.EditType ?? default) == DEdit.Multiline) {\n\t\t\ttext = _c.pszContent = text + c_multilineString;\n\t\t}\n\t\t\n\t\t_dlg.Send((int)(resizeDialog ? DNative.TDM.SET_ELEMENT_TEXT : DNative.TDM.UPDATE_ELEMENT_TEXT), (int)partId, text ?? \"\");\n\t\t//info: null does not change text.\n\t\t\n\t\tif (_IsEdit) _EditControlUpdateAsync(!resizeDialog);\n\t\t//info: sometimes even UPDATE_ELEMENT_TEXT sends our control to the bottom of the Z order.\n\t}\n\t\n\t#endregion send messages\n\t\n\t#region hookProc, timeoutText\n\t\n\t//Disables timeout on click or key.\n\tunsafe void _HookProc(HookData.ThreadGetMessage d) {\n\t\tswitch (d.msg->message) {\n\t\tcase Api.WM_LBUTTONDOWN:\n\t\tcase Api.WM_NCLBUTTONDOWN:\n\t\tcase Api.WM_RBUTTONDOWN:\n\t\tcase Api.WM_NCRBUTTONDOWN:\n\t\tcase Api.WM_KEYDOWN:\n\t\tcase Api.WM_SYSKEYDOWN:\n\t\t\tif (_timeoutActive && d.msg->hwnd.Window == _dlg) {\n\t\t\t\t_timeoutActive = false;\n\t\t\t\tSend.ChangeFooterText(_footerText, false);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n\t\n\tstring _TimeoutFooterText(int timeLeft) {\n\t\tvar format = options.timeoutTextFormat;\n\t\tif (format.NE()) return _footerText;\n\t\tif (_timeoutActionText.NE()) format = format.Lines()[0];\n\t\tusing (new StringBuilder_(out var b)) {\n\t\t\tb.AppendFormat(format, timeLeft, _timeoutActionText);\n\t\t\tif (!_footerText.NE()) b.Append('\\n').Append(_footerText);\n\t\t\treturn b.ToString();\n\t\t}\n\t}\n\t\n\t#endregion hookProc, timeoutText\n\t\n\t#region Edit control\n\t\n\t//never mind: our edit control disappears when moving the dialog to a screen with different DPI\n\t\n\tbool _IsEdit => _controls != null && _controls.EditType != DEdit.None;\n\t\n\tvoid _EditControlInitBeforeShowDialog() {\n\t\tif (!_IsEdit) return;\n\t\tProgressBarMarquee = true;\n\t\tProgressBar = false;\n\t\t_c.pszContent ??= \"\";\n\t\tif (_c.pszExpandedInformation != null && _controls.EditType == DEdit.Multiline) _SetFlag(_TDF.EXPAND_FOOTER_AREA, true);\n\t}\n\t\n\tvoid _EditControlUpdate(bool onlyZorder = false) {\n\t\tif (_editWnd.Is0) return;\n\t\tif (!onlyZorder) {\n\t\t\t_EditControlGetPlace(out RECT r);\n\t\t\t_editParent.MoveL(r);\n\t\t\t_editWnd.MoveL(0, 0, r.Width, r.Height);\n\t\t}\n\t\t_editParent.ZorderTopRaw_();\n\t}\n\t\n\tvoid _EditControlUpdateAsync(bool onlyZorder = false) {\n\t\t_editParent.Post(Api.WM_APP + 111, onlyZorder ? 1 : 0);\n\t}\n\t\n\t//to reserve space for multiline Edit control we append this to text2\n\tconst string c_multilineString = \"\\r\\n\\r\\n\\r\\n\\r\\n\\r\\n\\r\\n\\r\\n\\r\\n\\r\\n\\r\\n \";\n\t\n\twnd _EditControlGetPlace(out RECT r) {\n\t\twnd parent = _dlg; //don't use the DirectUIHWND control for it, it can create problems\n\t\t\n\t\t//create or get cached font and calculate control height\n\t\t_editFont = NativeFont_.RegularCached(Dpi.OfWindow(parent));\n\t\t//tested: on Win8.1 API isn't PM-DPI-aware.\n\t\t\n\t\t//We'll hide the progress bar control and create our Edit control in its place.\n\t\twnd prog = parent.Child(cn: \"msctls_progress32\", flags: WCFlags.HiddenToo);\n\t\tprog.GetRectIn(parent, out r);\n\t\t\n\t\tif (_controls.EditType == DEdit.Multiline) {\n\t\t\tint top = r.top;\n\t\t\tif (!_c.pszContent.Ends(c_multilineString)) {\n\t\t\t\t_c.pszContent += c_multilineString;\n\t\t\t\t_dlg.Send((int)DNative.TDM.SET_ELEMENT_TEXT, (int)DNative.TDE.CONTENT, _c.pszContent);\n\t\t\t\tprog.GetRectIn(parent, out r); //used to calculate Edit control height: after changing text, prog is moved down, and we know its previous location...\n\t\t\t}\n\t\t\tif (_editMultilineHeight == 0) { _editMultilineHeight = r.bottom - top; } else top = r.bottom - _editMultilineHeight;\n\t\t\tr.top = top;\n\t\t} else {\n\t\t\tr.top = r.bottom - (_editFont.HeightOnScreen + 8);\n\t\t}\n\t\t\n\t\tprog.ShowL(false);\n\t\treturn parent;\n\t}\n\tint _editMultilineHeight;\n\t\n\tvoid _EditControlCreate() {\n\t\twnd parent = _EditControlGetPlace(out RECT r);\n\t\t\n\t\t//Create an intermediate \"#32770\" to be direct parent of the Edit control.\n\t\t//It is safer (the dialog will not receive Edit notifications) and helps to solve Tab/Esc problems.\n\t\tvar pStyle = WS.CHILD | WS.VISIBLE | WS.CLIPCHILDREN | WS.CLIPSIBLINGS; //don't need WS_TABSTOP\n\t\tvar pExStyle = WSE.NOPARENTNOTIFY; //not WSE.CONTROLPARENT\n\t\t_editParent = WndUtil.CreateWindow(\"#32770\", null, pStyle, pExStyle, r.left, r.top, r.Width, r.Height, parent);\n\t\tApi.SetWindowLongPtr(_editParent, GWL.DWL.DLGPROC, Marshal.GetFunctionPointerForDelegate(_editControlParentProcHolder = _EditControlParentProc));\n\t\t\n\t\t//Create Edit or ComboBox control.\n\t\tstring cn = \"Edit\";\n\t\tvar style = WS.CHILD | WS.VISIBLE; //don't need WS_TABSTOP\n\t\tswitch (_controls.EditType) {\n\t\tcase DEdit.Text: style |= Api.ES_AUTOHSCROLL; break;\n\t\tcase DEdit.Password: style |= Api.ES_PASSWORD | Api.ES_AUTOHSCROLL; break;\n\t\tcase DEdit.Number: style |= Api.ES_NUMBER | Api.ES_AUTOHSCROLL; break;\n\t\tcase DEdit.Multiline: style |= Api.ES_MULTILINE | Api.ES_AUTOVSCROLL | Api.ES_WANTRETURN | WS.VSCROLL; break;\n\t\tcase DEdit.Combo: style |= Api.CBS_DROPDOWN | Api.CBS_AUTOHSCROLL | WS.VSCROLL; cn = \"ComboBox\"; break;\n\t\t}\n\t\t_editWnd = WndUtil.CreateWindow(cn, null, style, WSE.CLIENTEDGE, 0, 0, r.Width, r.Height, _editParent);\n\t\tWndUtil.SetFont(_editWnd, _editFont);\n\t\t\n\t\t//Init the control.\n\t\t_editWnd.SetText(_controls.EditText);\n\t\tif (_controls.EditType == DEdit.Combo) {\n\t\t\tif (_controls.ComboItems.Value != null) {\n\t\t\t\tforeach (var s in _controls.ComboItems.ToArray()) _editWnd.Send(Api.CB_INSERTSTRING, -1, s);\n\t\t\t}\n\t\t\tRECT cbr = _editWnd.Rect;\n\t\t\t_editParent.ResizeL(cbr.Width, cbr.Height); //because ComboBox resizes itself\n\t\t} else {\n\t\t\t_editWnd.Send(Api.EM_SETSEL, 0, -1);\n\t\t}\n\t\t_editParent.ZorderTopRaw_();\n\t\tApi.SetFocus(_editWnd);\n\t}\n\t\n\tvoid _EditControlOnMessage(DNative.TDN message) {\n\t\tswitch (message) {\n\t\tcase DNative.TDN.BUTTON_CLICKED:\n\t\t\t_controls.EditText = _editWnd.ControlText;\n\t\t\tbreak;\n\t\tcase DNative.TDN.EXPANDO_BUTTON_CLICKED:\n\t\tcase DNative.TDN.NAVIGATED:\n\t\t\t_EditControlUpdateAsync(); //when expando clicked, sync does not work even with doevents\n\t\t\tbreak;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Gets edit control handle as <see cref=\"wnd\"/>.\n\t/// </summary>\n\tpublic wnd EditControl => _editWnd;\n\twnd _editWnd, _editParent;\n\tNativeFont_ _editFont;\n\t\n\t//Dlgproc of our intermediate #32770 control, the parent of out Edit control.\n\tnint _EditControlParentProc(wnd w, int msg, nint wParam, nint lParam) {\n\t\tswitch (msg) {\n\t\tcase Api.WM_SETFOCUS: //enables Tab when in single-line Edit control\n\t\t\tApi.SetFocus(_dlg.ChildFast(null, \"DirectUIHWND\"));\n\t\t\treturn 1;\n\t\tcase Api.WM_NEXTDLGCTL: //enables Tab when in multi-line Edit control\n\t\t\tApi.SetFocus(_dlg.ChildFast(null, \"DirectUIHWND\"));\n\t\t\treturn 1;\n\t\tcase Api.WM_CLOSE: //enables Esc when in edit control\n\t\t\t_dlg.Send(msg);\n\t\t\treturn 1;\n\t\tcase Api.WM_APP + 111: //async update edit control pos\n\t\t\t_EditControlUpdate(wParam != 0);\n\t\t\treturn 1;\n\t\t}\n\t\treturn 0;\n\t\t//BAD: Alt+key doesn't work when the edit control is focused.\n\t\t//\thttps://github.com/qgindi/LibreAutomate/issues/28\n\t\t//\tAlso we receive 500 pairs of WM_GETDLGCODE + WM_GETTEXT.\n\t\t//\tTo fix this, probably would need a getmsg hook. It could detect the event and focus a button. Too expensive.\n\t}\n\tWNDPROC _editControlParentProcHolder;\n\t\n\t#endregion Edit control\n}\n"
  },
  {
    "path": "Au/GUI/osd.cs",
    "content": "using System.Drawing;\n\nnamespace Au.Types {\n\t/// <summary>\n\t/// Transparent window that can be used for on-screen display. Derived classes on it can draw non-transparent text, rectangle, image, anything.\n\t/// </summary>\n\tpublic abstract class OsdWindow : IDisposable {\n\t\twnd _w;\n\t\t\n\t\t/// <summary>Destroys the OSD window.</summary>\n\t\tprotected virtual void Dispose(bool disposing) {\n\t\t\tif (_w.Is0) return;\n\t\t\tApi.ShowWindow(_w, 0);\n\t\t\t_TopmostWorkaroundUndo();\n\t\t\tvar w = _w; _w = default;\n\t\t\tif (!Api.DestroyWindow(w)) w.Post(Api.WM_CLOSE);\n\t\t}\n\t\t\n\t\t/// <summary>Destroys the OSD window.</summary>\n\t\tpublic void Dispose() => Dispose(true);\n\t\t\n\t\t/// <summary>Destroys the OSD window.</summary>\n\t\tpublic void Close() => Dispose(true);\n\t\t\n\t\t/// <summary>OSD window handle or <c>default(wnd)</c>.</summary>\n\t\tpublic wnd Hwnd => _w;\n\t\t\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if the OSD window is created.\n\t\t/// </summary>\n\t\tprotected bool IsHandleCreated => !_w.Is0;\n\t\t\n\t\t/// <summary>\n\t\t/// Redraws the OSD window immediately.\n\t\t/// Does nothing it is not created or not visible.\n\t\t/// </summary>\n\t\tprotected void Redraw() {\n\t\t\tif (!Visible) return;\n\t\t\tApi.InvalidateRect(_w);\n\t\t\tApi.UpdateWindow(_w);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Sets to redraw the OSD window later.\n\t\t/// Does nothing it is not created or not visible.\n\t\t/// </summary>\n\t\tprotected void Invalidate() {\n\t\t\tif (!Visible) return;\n\t\t\tApi.InvalidateRect(_w);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets or sets the opacity of the OSD window, from 0 to 1.\n\t\t/// If 1, completely opaque. If 0, pixels of <see cref=\"TransparentColor\"/> are transparent, others opaque. If between 0 and 1, partially transparent.\n\t\t/// </summary>\n\t\t/// <value>Default is 1. Default in <see cref=\"osdRect\"/> is 0.</value>\n\t\t/// <remarks>\n\t\t/// This property can be changed after creating OSD window.\n\t\t/// </remarks>\n\t\tpublic double Opacity {\n\t\t\tget => _opacity;\n\t\t\tset {\n\t\t\t\tvar v = Math.Min(Math.Max(value, 0.0), 1.0);\n\t\t\t\tif (v == _opacity) return;\n\t\t\t\tbool was0 = _opacity == 0;\n\t\t\t\t_opacity = v;\n\t\t\t\tif (!IsHandleCreated) return;\n\t\t\t\t_SetOpacity();\n\t\t\t\tif ((v == 0) != was0) Redraw();\n\t\t\t}\n\t\t}\n\t\tdouble _opacity = 1d;\n\t\t\n\t\tvoid _SetOpacity() {\n\t\t\tif (_opacity > 0) Api.SetLayeredWindowAttributes(_w, 0, (byte)(uint)(_opacity * 255), 2);\n\t\t\telse Api.SetLayeredWindowAttributes(_w, (uint)TransparentColor.ToBGR(), 0, 1);\n\t\t\t\n\t\t\t//never mind: when resizing an alpha-transparent window by moving the top-left corner, the opposite corner shakes.\n\t\t\t//\tIt's a Windows problem, and I could not find a workaround.\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets or sets transparent color, default 0xF5F4F5. Pixels of this color will be transparent, unless <see cref=\"Opacity\"/> is not 0.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// This property cannot be changed after creating OSD window.\n\t\t/// Note: when used for transparent text, text edges are blended with this color, and it can become visible if the color is not chosen carefully.\n\t\t/// </remarks>\n\t\tpublic ColorInt TransparentColor { get; set; } = 0xF5F4F5;\n\t\t\n\t\t/// <summary>\n\t\t/// Gets or sets OSD window size and position in screen.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// This property can be changed after creating OSD window.\n\t\t/// </remarks>\n\t\tpublic virtual RECT Rect {\n\t\t\tget => _r;\n\t\t\tset {\n\t\t\t\tif (value == _r) return;\n\t\t\t\t_r = value;\n\t\t\t\tif (IsHandleCreated) {\n\t\t\t\t\t_w.SetWindowPos(SWPFlags.NOACTIVATE, _r.left, _r.top, _r.Width, _r.Height, SpecHWND.TOPMOST);\n\t\t\t\t\t_TopmostWorkaroundApply();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tRECT _r;\n\t\t\n\t\t/// <summary>\n\t\t/// Gets or sets whether the OSD window is visible.\n\t\t/// The <c>set</c> function calls <see cref=\"Show\"/> (it creates OSD window if need) or <see cref=\"Hide\"/> (it does not destroy the OSD window).\n\t\t/// </summary>\n\t\tpublic bool Visible {\n\t\t\tget => _w.IsVisible;\n\t\t\tset { if (value) Show(); else Hide(); } //note: if overridden, calls the override func\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Shows the OSD window. Creates if need.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// In any case, also moves the window to the top of the Z order.\n\t\t/// </remarks>\n\t\tpublic virtual void Show() {\n\t\t\tif (Visible) {\n\t\t\t\t_w.ZorderTopmost();\n\t\t\t\t_TopmostWorkaroundApply();\n\t\t\t} else {\n\t\t\t\tif (_w.Is0) _CreateWindow();\n\t\t\t\t_w.ShowL(true);\n\t\t\t\t_w.ZorderTopmost();\n\t\t\t\t_TopmostWorkaroundApply();\n\t\t\t\tApi.UpdateWindow(_w);\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Hides the OSD window. Does not destroy; use <see cref=\"Close\"/> or <see cref=\"Dispose\"/> for it.\n\t\t/// Does nothing if not created or not visible.\n\t\t/// </summary>\n\t\tpublic virtual void Hide() {\n\t\t\tif (!Visible) return;\n\t\t\t_w.ShowL(false);\n\t\t}\n\t\t\n\t\tvoid _CreateWindow() {\n\t\t\t//register window class if need. Need another class if shadow.\n\t\t\tstring cn; byte regMask;\n\t\t\tif (Shadow) { cn = \"Au.OSD2\"; regMask = 2; } else { cn = \"Au.OSD\"; regMask = 1; }\n\t\t\tif ((s_isWinClassRegistered & regMask) == 0) {\n\t\t\t\tvar ce = new RWCEtc() { style = Api.CS_HREDRAW | Api.CS_VREDRAW, mCursor = MCursor.Arrow };\n\t\t\t\tif (Shadow) ce.style |= Api.CS_DROPSHADOW;\n\t\t\t\tWndUtil.RegisterWindowClass(cn, null, ce);\n\t\t\t\ts_isWinClassRegistered |= regMask;\n\t\t\t}\n\t\t\t\n\t\t\tvar es = WSE.TOOLWINDOW | WSE.TOPMOST | WSE.LAYERED | WSE.TRANSPARENT | WSE.NOACTIVATE;\n\t\t\tif (ClickToClose) es &= ~WSE.TRANSPARENT;\n\t\t\t_w = WndUtil.CreateWindow(WndProc, true, cn, Name, WS.POPUP, es); //note: don't set rect here: can be painting problems when resizing\n\t\t\t_SetOpacity();\n\t\t\tif (!_r.Is0) _w.SetWindowPos(SWPFlags.NOACTIVATE, _r.left, _r.top, _r.Width, _r.Height, SpecHWND.TOPMOST);\n\t\t}\n\t\tstatic byte s_isWinClassRegistered;\n\t\t\n\t\t/// <summary>\n\t\t/// Called when the OSD window receives a message.\n\t\t/// If your derived class overrides this function, it must call <c>base.WndProc</c> and return its return value, except when don't need default processing.\n\t\t/// </summary>\n\t\tprotected virtual nint WndProc(wnd w, int message, nint wParam, nint lParam) {\n\t\t\tswitch (message) {\n\t\t\tcase Api.WM_NCDESTROY:\n\t\t\t\tApi.PostMessage(default, 0, 0, 0); //stop waiting for a message. Never mind: not always need it.\n\t\t\t\tClosed?.Invoke(this, EventArgs.Empty);\n\t\t\t\t_w = default;\n\t\t\t\tbreak;\n\t\t\tcase Api.WM_ERASEBKGND:\n\t\t\t\treturn 1;\n\t\t\tcase Api.WM_PAINT:\n\t\t\t\tusing (var bp = new BufferedPaint(w, true)) {\n\t\t\t\t\tvar dc = bp.DC;\n\t\t\t\t\tusing var g = Graphics.FromHdc(dc);\n\t\t\t\t\tif (_opacity == 0) g.Clear((Color)TransparentColor);\n\t\t\t\t\tOnPaint(dc, g, bp.Rect);\n\t\t\t\t}\n\t\t\t\treturn default;\n\t\t\tcase Api.WM_MOUSEACTIVATE:\n\t\t\t\treturn Api.MA_NOACTIVATE;\n\t\t\tcase Api.WM_LBUTTONUP or Api.WM_RBUTTONUP or Api.WM_MBUTTONUP:\n\t\t\t\tClicked?.Invoke(this, message switch { Api.WM_LBUTTONUP => MButton.Left, Api.WM_RBUTTONUP => MButton.Right, _ => MButton.Middle });\n\t\t\t\tif (ClickToClose) w.Post(Api.WM_CLOSE);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t\n\t\t\treturn Api.DefWindowProc(w, message, wParam, lParam);\n\t\t}\n\n\t\t///\n\t\tpublic event EventHandler Closed;\n\n\t\t///\n\t\tpublic event EventHandler<MButton> Clicked;\n\t\t\n\t\t/// <summary>\n\t\t/// Called when the OSD window must be drawn or redrawn.\n\t\t/// Derived classes should override this function and draw anything. Don't need to call <c>base.OnPaint</c> of <see cref=\"OsdWindow\"/>, it does nothing.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// If <see cref=\"Opacity\"/> is 0 (default), <i>g</i> is filled with <see cref=\"TransparentColor\"/>. Pixels of this color will be transparent. The base class draws only non-transparent areas.\n\t\t/// </remarks>\n\t\tprotected virtual void OnPaint(IntPtr dc, Graphics g, RECT r) {\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// If <c>true</c>, the OSD window will have shadow.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// This property cannot be changed after creating OSD window.\n\t\t/// </remarks>\n\t\tprotected bool Shadow { get; set; }\n\t\t\n\t\t/// <summary>\n\t\t/// If <c>true</c>, the OSD window receive mouse messages. Only completely transparent areas don't. The user can click to close the OSD (left, right or middle button).\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// This property cannot be changed after creating OSD window.\n\t\t/// </remarks>\n\t\tprotected bool ClickToClose { get; set; }\n\t\t\n\t\t/// <summary>\n\t\t/// OSD window name. Optional, default <c>null</c>.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// This text is invisible. Can be used to find OSD window. The class name is <c>\"Au.OSD\"</c>; if with shadow - <c>\"Au.OSD2\"</c>.\n\t\t/// This property cannot be changed after creating OSD window.\n\t\t/// </remarks>\n\t\tpublic string Name { get; set; }\n\t\t\n\t\t/// <summary>\n\t\t/// Closes all OSD windows of this process.\n\t\t/// </summary>\n\t\t/// <param name=\"name\">If not <c>null</c>, closes only OSD windows whose <see cref=\"Name\"/> matches this [wildcard expression](xref:wildcard_expression).</param>\n\t\tpublic static void closeAll([ParamString(PSFormat.Wildex)] string name = null) {\n\t\t\tforeach (var w in wnd.findAll(name, \"**m Au.OSD||Au.OSD2\", WOwner.Process(Api.GetCurrentProcessId()))) w.Post(Api.WM_CLOSE);\n\t\t}\n\t\t\n\t\t#region topmost workaround\n\t\t\n\t\t//Workaround for: our topmost window is below some other topmost windows (Win8+).\n\t\t//\tWindow examples:\n\t\t//\tA. Windows Start menu, search, TaskListThumbnailWnd.\n\t\t//\tB. uiAccess apps, eg on-screen keyboard, Inspect, NVDA, QM2 uiAccess.\n\t\t//\tC. Probably Win8 Store apps.\n\t\t//\tKnown workarounds:\n\t\t//\t1. Temporarily make the window non-topmost. But it fails for A and probably C. Fails always if this process is not admin.\n\t\t//\t2. Temporarily set partially transparent. But it is useful only for on-screen rect. Not useful for menus and toolbars. Fails if not admin. Fails with WPF windows.\n\t\t//\t3. Set window region. Too crazy. Tested, does not work with eg OSK, although works with eg Notepad.\n\t\t//\t4. Run tools in uiAccess processes. Too crazy and limited.\n\t\t\n\t\tinternal bool TopmostWorkaround_ { get; set; }\n\t\t\n\t\tvoid _TopmostWorkaroundApply() {\n\t\t\tif (!TopmostWorkaround_ || !osVersion.minWin10) return; //never mind: on Win8 not tested. Would be bad to make full-screen windows transparent.\n\t\t\tvar w = wnd.fromXY(new(_r.CenterX, _r.CenterY), WXYFlags.NeedWindow);\n\t\t\tbool apply = false;\n\t\t\tlock (s_twList) {\n\t\t\t\tList<OsdWindow> aosd = null;\n\t\t\t\tforeach (var v in s_twList) if (v.w == w) { if (v.a.Contains(this)) return; aosd = v.a; break; }\n\t\t\t\tif (w.HasExStyle(WSE.TOPMOST)) {\n\t\t\t\t\tRECT r = w.Rect; r.Inflate(10, 10);\n\t\t\t\t\tif (r.Width > _r.Width && r.Height > _r.Height) {\n\t\t\t\t\t\tif (!_w.ZorderIsAbove(w)) {\n\t\t\t\t\t\t\tif (apply = aosd == null) s_twList.Add((w, new() { this }));\n\t\t\t\t\t\t\telse aosd.Add(this);\n\t\t\t\t\t\t\t//print.it(apply, w);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (apply && s_twTimer == null) {\n\t\t\t\t\ts_twTimer = new(_ => {\n\t\t\t\t\t\tList<wnd> aw = null;\n\t\t\t\t\t\tlock (s_twList) {\n\t\t\t\t\t\t\tforeach (var (w, _) in s_twList) {\n\t\t\t\t\t\t\t\t//print.it(w.GetTransparency(out var op, out var col), op, col);\n\t\t\t\t\t\t\t\tif (!w.GetTransparency(out var op, out var col) || op == null || op.Value > 200) {\n\t\t\t\t\t\t\t\t\tif (w.IsAlive) (aw ??= new()).Add(w);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (aw != null) foreach (var w in aw) w.SetTransparency(true, 200, noException: true);\n\t\t\t\t\t});\n\t\t\t\t\ts_twTimer.Every(1000);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (apply) w.SetTransparency(true, 200, noException: true);\n\t\t}\n\t\t\n\t\tvoid _TopmostWorkaroundUndo() {\n\t\t\tif (!TopmostWorkaround_ || !osVersion.minWin10) return;\n\t\t\tList<wnd> aw = null;\n\t\t\tlock (s_twList) {\n\t\t\t\tfor (int i = s_twList.Count; --i >= 0;) {\n\t\t\t\t\tvar (w, a) = s_twList[i];\n\t\t\t\t\tif (a.Remove(this) && a.Count == 0) {\n\t\t\t\t\t\ts_twList.RemoveAt(i);\n\t\t\t\t\t\tif (w.IsAlive) (aw ??= new()).Add(w);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (s_twList.Count == 0 && s_twTimer != null) {\n\t\t\t\t\ts_twTimer.Stop();\n\t\t\t\t\ts_twTimer = null;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (aw != null) foreach (var w in aw) w.SetTransparency(!true, 255, noException: true);\n\t\t}\n\t\t\n\t\tstatic List<(wnd w, List<OsdWindow> a)> s_twList = new();\n\t\tstatic timer2 s_twTimer;\n\t\t\n\t\t#endregion\n\t}\n\t\n\t/// <summary>\n\t/// Used by <see cref=\"osdRect.SetRects\"/>.\n\t/// </summary>\n\t/// <param name=\"Placement\">Label placement relative to rectangle: <c>'L'</c> (left), <c>'R'</c> (right), <c>'T'</c> (top), <c>'B'</c> (bottom) or <c>'I'</c> (inside). Default: <c>'L'</c>.</param>\n\tpublic record class ORLabelOptions(char Placement, ColorInt? BackgroundColor = null, ColorInt? TextColor = null) {\n\t\t///\n\t\tpublic static implicit operator ORLabelOptions(char placement) => new(placement);\n\t}\n}\n\nnamespace Au {\n\t/// <summary>\n\t/// Shows a mouse-transparent rectangle on screen. Just visible outline, or filled but partially transparent.\n\t/// </summary>\n\t/// <remarks>\n\t/// Creates a temporary partially transparent window, and draws rectangle in it. Can draw multiple rectangles.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// using (var x = new osdRect()) {\n\t/// \tx.Rect = (300, 300, 100, 100);\n\t/// \tx.Color = System.Drawing.Color.SlateBlue;\n\t/// \tx.Thickness = 4;\n\t/// \tx.Show();\n\t/// \tfor (int i = 0; i < 5; i++) {\n\t/// \t\t250.ms();\n\t/// \t\tx.Visible = !x.Visible;\n\t/// \t}\n\t/// }\n\t/// ]]></code>\n\t/// </example>\n\tpublic class osdRect : OsdWindow {\n\t\t///\n\t\tpublic osdRect() {\n\t\t\tOpacity = 0d;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets or sets rectangle color.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// This property can be changed after creating OSD window.\n\t\t/// </remarks>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// x.Color = 0xFF0000; //red\n\t\t/// x.Color = System.Drawing.Color.Orange;\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic ColorInt Color {\n\t\t\tget => _color;\n\t\t\tset { if (value != _color) { _color = value; Redraw(); } }\n\t\t}\n\t\tColorInt _color = 0; //=0 adds alpha 0xFF\n\t\t\n\t\t/// <summary>\n\t\t/// Gets or sets rectangle frame width.\n\t\t/// Used only if <see cref=\"OsdWindow.Opacity\"/> is 0 (default).\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// This property can be changed after creating OSD window.\n\t\t/// </remarks>\n\t\tpublic int Thickness {\n\t\t\tget => _thickness;\n\t\t\tset { if (value != _thickness) { _thickness = value; Redraw(); } }\n\t\t}\n\t\tint _thickness = 3;\n\t\t\n\t\t/// <summary>\n\t\t/// Called when the OSD window must be drawn or redrawn. Draws rectangle. More info: <see cref=\"OsdWindow.OnPaint\"/>.\n\t\t/// </summary>\n\t\tprotected override void OnPaint(IntPtr dc, Graphics g, RECT ra) {\n\t\t\tif (_rects == null) {\n\t\t\t\tif (Opacity > 0) {\n\t\t\t\t\tg.Clear((Color)_color);\n\t\t\t\t} else {\n\t\t\t\t\tg.DrawRectangleInset((Color)_color, _thickness, ra);\n\t\t\t\t}\n\t\t\t} else if (_rects.Length >= 0) {\n\t\t\t\tif (Opacity > 0) g.Clear((Color)TransparentColor);\n\t\t\t\tusing var tr = _hasLabels ? new GdiTextRenderer(dc, Dpi.OfWindow(Hwnd)) : null;\n\t\t\t\tfor (int i = 0; i < _rects.Length; i++) {\n\t\t\t\t\tvar r = _rects[i].r; r.Offset(-Rect.left, -Rect.top);\n\t\t\t\t\tg.DrawRectangleInset((Color)Color, Thickness, r);\n\t\t\t\t\tif (_hasLabels) {\n\t\t\t\t\t\tvar s = _rects[i].s;\n\t\t\t\t\t\tvar z = tr.MeasureText(s);\n\t\t\t\t\t\tint pad = z.height / 2;\n\t\t\t\t\t\tz.width += pad;\n\t\t\t\t\t\tint x = _labelOptions.Placement switch { 'L' => r.left - z.width - 1, 'R' => r.right, 'I' => r.left + Thickness + 1, _ => r.left };\n\t\t\t\t\t\tint y = _labelOptions.Placement switch { 'T' => r.top - z.height - 1, 'B' => r.bottom, 'I' => r.top + Thickness + 1, _ => r.top };\n\t\t\t\t\t\tif (x < 0) { x = 0; if (z.width >= r.left) z.width = r.left - 1; }\n\t\t\t\t\t\tRECT rr = new(x, y, z.width, z.height);\n\t\t\t\t\t\tif (_labelOptions.BackgroundColor != null) g.FillRectangle((Color)_labelOptions.BackgroundColor.Value, rr); else g.FillRectangle(Brushes.PaleGoldenrod, rr);\n\t\t\t\t\t\tg.DrawRectangle(Pens.DarkGray, rr);\n\t\t\t\t\t\trr.Inflate(-pad / 2, 0);\n\t\t\t\t\t\ttr.DrawText(s, rr, _labelOptions.TextColor?.ToBGR() ?? 0x404040);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\t(RECT r, string s)[] _rects;\n\t\tbool _hasLabels;\n\t\tORLabelOptions _labelOptions;\n\t\t\n\t\t/// <summary>\n\t\t/// Sets to draw multiple rectangles.\n\t\t/// </summary>\n\t\t/// <param name=\"rects\">Rectangles.</param>\n\t\t/// <param name=\"indexLabels\">Draw labels. The label text is the rectangle index in <i>rects</i>.</param>\n\t\t/// <param name=\"labelOptions\">Label options. If <c>char</c> - label placement relative to rectangle: <c>'L'</c> (left), <c>'R'</c> (right), <c>'T'</c> (top), <c>'B'</c> (bottom) or <c>'I'</c> (inside). Default: <c>'L'</c>.</param>\n\t\t/// <remarks>\n\t\t/// If this function called, will draw multiple rectangles instead of single (unless <i>rects</i> is <c>null</c>). <c>Opacity</c> should be 0 (default).\n\t\t/// </remarks>\n\t\tpublic void SetRects(IEnumerable<RECT> rects, bool indexLabels = false, ORLabelOptions labelOptions = null) {\n\t\t\tSetRects(indexLabels ? rects?.Select((r, i) => (r, i.ToS())) : rects?.Select(r => (r, (string)null)), labelOptions);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Sets to draw multiple rectangles with labels.\n\t\t/// </summary>\n\t\t/// <param name=\"rects\">Rectangles with label text. The text should be short, single line, else may draw clipped.</param>\n\t\t/// <inheritdoc cref=\"SetRects(IEnumerable{RECT}, bool, ORLabelOptions)\"/>\n\t\tpublic void SetRects(IEnumerable<(RECT r, string s)> rects, ORLabelOptions labelOptions = null) {\n\t\t\tif (rects == null) {\n\t\t\t\t_rects = null;\n\t\t\t\tRect = default;\n\t\t\t} else {\n\t\t\t\t_rects = rects.ToArray();\n\t\t\t\tvar r = RECT.FromLTRB(_rects.Min(o => o.r.left), _rects.Min(o => o.r.top), _rects.Max(o => o.r.right), _rects.Max(o => o.r.bottom));\n\t\t\t\tif (_hasLabels = _rects.Any(o => o.s != null)) {\n\t\t\t\t\tint ii = screen.of(r).Dpi;\n\t\t\t\t\tr.Inflate(ii * 2, ii / 4);\n\t\t\t\t}\n\t\t\t\tRect = r;\n\t\t\t\t_labelOptions = labelOptions ?? new('L');\n\t\t\t\tRedraw();\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Shows text on screen, like a tooltip or with transparent background.\n\t/// </summary>\n\t/// <remarks>\n\t/// Creates a temporary partially transparent window, and draws text in it.\n\t/// Most properties cannot be changed after creating OSD window.\n\t/// </remarks>\n\tpublic class osdText : OsdWindow {\n\t\tNativeFont_ _font;\n\t\t\n\t\t///\n\t\tprotected override void Dispose(bool disposing) {\n\t\t\tbase.Dispose(disposing);\n\t\t\t_font?.Dispose(); _font = null;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Coordinates.\n\t\t/// Default: <c>null</c> (screen center).\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Not used if <see cref=\"Rect\"/> is set.\n\t\t/// This property can be changed after creating OSD window.\n\t\t/// </remarks>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// var m = new osdText { Text = \"Text\" };\n\t\t/// m.XY = new(Coord.Center, Coord.Max); //bottom-center of the work area of the primary screen\n\t\t/// m.Show();\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic PopupXY XY {\n\t\t\tget => _xy;\n\t\t\tset {\n\t\t\t\t_xy = value;\n\t\t\t\tif (value != null) _rectIsSet = false;\n\t\t\t\tif (IsHandleCreated) base.Rect = Measure();\n\t\t\t}\n\t\t}\n\t\tPopupXY _xy;\n\t\t\n\t\t/// <summary>\n\t\t/// Gets or sets OSD window size and position in screen.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Normally don't need to use this property. If not used, the OSD window size depends on text etc, and position on <see cref=\"XY\"/>.\n\t\t/// This property can be changed after creating OSD window.\n\t\t/// </remarks>\n\t\t/// <seealso cref=\"Measure\"/>\n\t\tpublic override RECT Rect {\n\t\t\tget => base.Rect;\n\t\t\tset { _rectIsSet = true; base.Rect = value; }\n\t\t}\n\t\tbool _rectIsSet;\n\t\t\n\t\tvoid _ResizeOrInvalidate() {\n\t\t\tif (ResizeWhenContentChanged) base.Rect = Measure(); //info: invalidates if resized, because of the CS_ styles\n\t\t\tInvalidate();\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// When changing text, resize/move the OSD window if need.\n\t\t/// Default: <c>false</c>.\n\t\t/// </summary>\n\t\tpublic bool ResizeWhenContentChanged { get; set; }\n\t\t\n\t\t/// <summary>\n\t\t/// Text in OSD window.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// This property can be changed after creating OSD window; then the window is not moved/resized, unless <see cref=\"ResizeWhenContentChanged\"/> is <c>true</c>.\n\t\t/// </remarks>\n\t\tpublic string Text {\n\t\t\tget => _text;\n\t\t\tset { if (value != _text) { _text = value; _ResizeOrInvalidate(); } }\n\t\t}\n\t\tstring _text;\n\t\t\n\t\t/// <summary>\n\t\t/// Font.\n\t\t/// Default: <see cref=\"defaultSmallFont\"/>.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// This property cannot be changed after creating OSD window.\n\t\t/// </remarks>\n\t\tpublic FontNSS Font { get; set; }\n\t\t\n\t\t/// <summary>\n\t\t/// Text color.\n\t\t/// Default: <see cref=\"defaultTextColor\"/>.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// This property can be changed after creating OSD window.\n\t\t/// </remarks>\n\t\tpublic ColorInt TextColor {\n\t\t\tget => _textColor;\n\t\t\tset { if (value != _textColor) { _textColor = value; Invalidate(); } }\n\t\t}\n\t\tColorInt _textColor;\n\t\t\n\t\t/// <summary>\n\t\t/// Background color.\n\t\t/// Default: <see cref=\"defaultBackColor\"/>.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// This property can be changed after creating OSD window.\n\t\t/// Not used for completely transparent OSD.\n\t\t/// </remarks>\n\t\tpublic ColorInt BackColor {\n\t\t\tget => _backColor;\n\t\t\tset { if (value != _backColor) { _backColor = value; Invalidate(); } }\n\t\t}\n\t\tColorInt _backColor;\n\t\t\n\t\t/// <summary>\n\t\t/// Border color.\n\t\t/// Default: <see cref=\"defaultBorderColor\"/>.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// This property can be changed after creating OSD window.\n\t\t/// No border if <see cref=\"OsdWindow.Opacity\"/>==0 or <c>BorderColor</c>==<see cref=\"BackColor\"/>.\n\t\t/// </remarks>\n\t\tpublic ColorInt BorderColor {\n\t\t\tget => _borderColor;\n\t\t\tset { if (value != _borderColor) { _borderColor = value; Invalidate(); } }\n\t\t}\n\t\tprivate ColorInt _borderColor;\n\t\t\n\t\t/// <summary>\n\t\t/// Background image.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// This property cannot be changed after creating OSD window.\n\t\t/// </remarks>\n\t\tpublic Image BackgroundImage { get; set; }\n\t\t\n\t\t//public ImageLayout BackgroundImageLayout { get; set; } //FUTURE\n\t\t\n\t\t/// <summary>\n\t\t/// When used <see cref=\"BackgroundImage\"/>, the OSD window has the same size as the image, plus borders.\n\t\t/// Else OSD window size is calculated from sizes of text and icon. Then image is displayed scaled or clipped if need.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// This property cannot be changed after creating OSD window.\n\t\t/// </remarks>\n\t\tpublic bool IsOfImageSize { get; set; }\n\t\t\n\t\t/// <summary>\n\t\t/// Maximal text width.\n\t\t/// Default: 0 - no limit (depends on screen width etc).\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// This property cannot be changed after creating OSD window.\n\t\t/// </remarks>\n\t\tpublic int WrapWidth { get; set; }\n\t\t\n\t\t/// <summary>\n\t\t/// Gets or sets text format flags.\n\t\t/// Default: <c>TFFlags.NOPREFIX | TFFlags.WORDBREAK | TFFlags.EXPANDTABS</c>.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// This property cannot be changed after creating OSD window.\n\t\t/// </remarks>\n\t\tpublic TFFlags TextFormatFlags { get; set; } = TFFlags.NOPREFIX | TFFlags.WORDBREAK | TFFlags.EXPANDTABS;\n\t\t\n\t\t/// <summary>\n\t\t/// Icon or image at the left. Any size.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// For example <c>System.Drawing.SystemIcons.Information</c> or <c>icon.stock(StockIcon.INFO)</c> or <c>ImageUtil.LoadGdipBitmapFromXaml(\"XAML copied from the Icons tool. You can edit, Width, Height and Fill (color).\", screen.primary.Dpi)</c>.\n\t\t/// \n\t\t/// This property cannot be changed after creating OSD window.\n\t\t/// </remarks>\n\t\t/// <value><see cref=\"icon\"/>, <see cref=\"System.Drawing.Icon\"/> or <see cref=\"System.Drawing.Image\"/>.</value>\n\t\tpublic object Icon { get; set; }\n\t\tSIZE _iconSize;\n\t\t\n\t\t/// <summary>\n\t\t/// If <c>true</c>, the OSD window will have shadow.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// This property cannot be changed after creating OSD window.\n\t\t/// Window shadows can be disabled. See <ms>SPI_SETDROPSHADOW</ms>.\n\t\t/// </remarks>\n\t\tpublic new bool Shadow { get => base.Shadow; set => base.Shadow = value; }\n\t\t\n\t\t/// <summary>\n\t\t/// If <c>true</c>, the OSD window receive mouse messages. Only completely transparent areas don't. The user can click to close the OSD (left, right or middle button).\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// This property cannot be changed after creating OSD window.\n\t\t/// </remarks>\n\t\tpublic new bool ClickToClose { get => base.ClickToClose; set => base.ClickToClose = value; }\n\t\t\n\t\t/// <summary>\n\t\t/// See <see cref=\"OsdMode\"/>.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// This property cannot be changed after creating OSD window.\n\t\t/// </remarks>\n\t\tpublic OsdMode ShowMode { get; set; }\n\t\t\n\t\t//CONSIDER: public AnyWnd Owner { get; set; }\n\t\t\n\t\t///\n\t\tpublic osdText() {\n\t\t\tFont = defaultSmallFont;\n\t\t\t_textColor = defaultTextColor;\n\t\t\t_backColor = defaultBackColor;\n\t\t\t_borderColor = defaultBorderColor;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Shows the OSD window. Creates if need.\n\t\t/// By default does not wait; the window will be closed after <see cref=\"SecondsTimeout\"/>.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Depending on <see cref=\"ShowMode\"/>, creates the OSD window in this or new thread.\n\t\t/// If the OSD window is already created, just shows it if hidden. Many properties can be changed only before creating OSD window; call <see cref=\"OsdWindow.Close\"/> if need.\n\t\t/// </remarks>\n\t\tpublic override void Show() {\n\t\t\tif (!Hwnd.Is0) {\n\t\t\t\tbase.Show();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tbool thisThread = false, wait = false;\n\t\t\tswitch (ShowMode) {\n\t\t\tcase OsdMode.Auto: thisThread = process.thisThreadHasMessageLoop(); break;\n\t\t\tcase OsdMode.ThisThread: thisThread = true; break;\n\t\t\tcase OsdMode.Wait: thisThread = wait = true; break;\n\t\t\t}\n\t\t\tif (thisThread) {\n\t\t\t\t_Show(wait);\n\t\t\t} else {\n\t\t\t\t//Task.Run(() => ShowWait()); //works too, but cannot use StrongThread\n\t\t\t\trun.thread(() => _Show(true), ShowMode == OsdMode.WeakThread).Name = \"Au.osdText\";\n\t\t\t\tAu.wait.until(30, () => IsHandleCreated);\n\t\t\t\t\n\t\t\t\t//CONSIDER: make smaller timeout when main thread ended if OsdShowMode.Auto\n\t\t\t}\n\t\t}\n\t\t\n\t\tvoid _Show(bool sync) {\n\t\t\tif (!_rectIsSet) base.Rect = Measure();\n\t\t\t\n\t\t\tbase.Show();\n\t\t\t_SetCloseTimer();\n\t\t\tif (sync) wait.doEventsUntil(0, () => !IsHandleCreated);\n\t\t}\n\t\t\n\t\tvoid _SetCloseTimer() {\n\t\t\tint t = SecondsTimeout;\n\t\t\tif (t == 0) t = Math.Min(Text.Lenn(), 1000) / 10 + 3; //calc time from text length\n\t\t\t\n\t\t\tif (t > 0) Api.SetTimer(Hwnd, 1, Math.Min(t, int.MaxValue / 1000) * 1000, null);\n\t\t\telse Api.KillTimer(Hwnd, 1);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Close the OSD window after this time, seconds.\n\t\t/// If 0 (default), depends on text length. Can be <see cref=\"Timeout.Infinite\"/> (-1).\n\t\t/// </summary>\n\t\tpublic int SecondsTimeout {\n\t\t\tget => _secondsTimeout;\n\t\t\tset {\n\t\t\t\t_secondsTimeout = value;\n\t\t\t\tif (IsHandleCreated) Hwnd.SendNotify(Api.WM_USER + 100);\n\t\t\t}\n\t\t}\n\t\tint _secondsTimeout;\n\t\t\n#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member\n\t\tprotected override nint WndProc(wnd w, int message, nint wParam, nint lParam) {\n\t\t\tswitch (message) {\n\t\t\tcase Api.WM_TIMER when wParam == 1:\n\t\t\t\tClose();\n\t\t\t\treturn 0;\n\t\t\tcase Api.WM_USER + 100:\n\t\t\t\t_SetCloseTimer();\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\t\n\t\t\treturn base.WndProc(w, message, wParam, lParam);\n\t\t}\n#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member\n\t\t\n\t\t/// <summary>\n\t\t/// Draws OSD text etc.\n\t\t/// </summary>\n\t\tprotected override void OnPaint(IntPtr dc, Graphics g, RECT r) {\n\t\t\t//print.it(Api.GetCurrentThreadId());\n\t\t\tif (Opacity != 0) {\n\t\t\t\tg.Clear((Color)BackColor); //else OsdWindow cleared with TransparentColor\n\t\t\t\t\n\t\t\t\tif (BorderColor != BackColor) { //border\n\t\t\t\t\tg.DrawRectangleInset((Color)BorderColor, 1, r);\n\t\t\t\t\tr.Inflate(-1, -1);\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tif (BackgroundImage != null) {\n\t\t\t\tg.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;\n\t\t\t\tg.DrawImageUnscaledAndClipped(BackgroundImage, r); //info: if image smaller - scales; if bigger - clips.\n\t\t\t}\n\t\t\t\n\t\t\t_EnsureFontAndMargin(Dpi.OfWindow(Hwnd)); //eg if Measure not called because Rect is set\n\t\t\t\n\t\t\tr.Inflate(-_margin, -_margin);\n\t\t\tif (Font.Italic) r.Width += _margin / 2; //avoid clipping part of last char\n\t\t\t\n\t\t\tif (_iconSize != default) {\n\t\t\t\tint x = r.left + c_iconPadding, y = r.top + c_iconPadding;\n\t\t\t\tif (Icon is Image im) {\n\t\t\t\t\tg.DrawImageUnscaled(im, x, y);\n\t\t\t\t} else {\n\t\t\t\t\tvar hi = Icon switch { icon k => k.Handle, Icon k => k.Handle, _ => default };\n\t\t\t\t\tApi.DrawIconEx(dc, x, y, hi, 0, 0);\n\t\t\t\t}\n\t\t\t\tr.left += _iconSize.width + c_iconPadding * 2;\n\t\t\t}\n\t\t\t\n\t\t\tif (!Text.NE()) {\n\t\t\t\tApi.SetTextColor(dc, TextColor.ToBGR());\n\t\t\t\tApi.SetBkMode(dc, 1);\n\t\t\t\tvar tff = TextFormatFlags; if (WrapWidth > 0) tff |= TFFlags.WORDBREAK;\n\t\t\t\tusing var soFont = new GdiSelectObject_(dc, _font);\n\t\t\t\tRECT rt = r;\n\t\t\t\tApi.DrawText(dc, Text, ref rt, tff);\n\t\t\t}\n\t\t}\n\t\t\n\t\tconst int c_iconPadding = 5;\n\t\tint _margin;\n\t\t\n\t\tvoid _EnsureFontAndMargin(int dpi) {\n\t\t\tif (Text.NE()) {\n\t\t\t\t_margin = 0;\n\t\t\t} else if (_font == null) {\n\t\t\t\tvar f = Font ?? defaultSmallFont;\n\t\t\t\t_font = f.CreateFont(dpi);\n\t\t\t\t_margin = Dpi.Scale(Math.Max(f.Size / 3, 3), dpi);\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Calculates OSD window size and position.\n\t\t/// Can be called before showing OSD.\n\t\t/// </summary>\n\t\tpublic RECT Measure() {\n\t\t\tSize z = default;\n\t\t\tif (IsOfImageSize && BackgroundImage != null) {\n\t\t\t\tz = BackgroundImage.Size;\n\t\t\t} else {\n\t\t\t\tSize zi = default;\n\t\t\t\tif (Icon != null) {\n\t\t\t\t\tswitch (Icon) {\n\t\t\t\t\tcase icon k: zi = k.Size; break;\n\t\t\t\t\tcase Icon k: zi = k.Size; break;\n\t\t\t\t\tcase Image k: zi = k.Size; break;\n\t\t\t\t\t}\n\t\t\t\t\t_iconSize = zi;\n\t\t\t\t\tif (zi != default) { zi.Width += c_iconPadding * 2; zi.Height += c_iconPadding * 2; }\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvar scrn = XY?.GetScreen() ?? defaultScreen.Now;\n\t\t\t\tint dpi = scrn.Dpi;\n\t\t\t\t_margin = 0;\n\t\t\t\tif (!Text.NE()) {\n\t\t\t\t\t_font?.Dispose(); _font = null;\n\t\t\t\t\t_EnsureFontAndMargin(dpi);\n\t\t\t\t\tvar tff = TextFormatFlags;\n\t\t\t\t\tint maxWidth = scrn.WorkArea.Width - zi.Width - 10;\n\t\t\t\t\tint ww = WrapWidth; if (ww > 0) { maxWidth = Math.Min(maxWidth, ww); tff |= TFFlags.WORDBREAK; }\n\t\t\t\t\tusing var dc = new FontDC_(_font);\n\t\t\t\t\tz = dc.MeasureDT(Text, tff, maxWidth);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tz.Width += zi.Width;\n\t\t\t\tz.Height = Math.Max(z.Height, zi.Height);\n\t\t\t\t\n\t\t\t\tz.Width += _margin * 2; z.Height += _margin * 2;\n\t\t\t}\n\t\t\t\n\t\t\tif (Opacity != 0 && BorderColor != BackColor) { //border\n\t\t\t\tz.Width += 2; z.Height += 2;\n\t\t\t}\n\t\t\t\n\t\t\tRECT r = new(0, 0, z.Width, z.Height);\n\t\t\t//print.it(r);\n\t\t\t\n\t\t\tif (XY != null) {\n\t\t\t\tif (XY.inRect) r.MoveInRect(XY.rect, XY.x, XY.y, ensureInRect: false);\n\t\t\t\telse r.MoveInScreen(XY.x, XY.y, XY.screen, XY.workArea, ensureInScreen: false);\n\t\t\t\tr.EnsureInScreen(workArea: XY.workArea);\n\t\t\t} else {\n\t\t\t\tr.MoveInScreen(Coord.Center, Coord.Center, defaultScreen, workArea: true, ensureInScreen: true);\n\t\t\t}\n\t\t\t\n\t\t\treturn r;\n\t\t}\n\t\t\n\t\t#region public static\n\t\t\n\t\t/// <summary>\n\t\t/// Shows a tooltip-like OSD window with text and optionally icon.\n\t\t/// </summary>\n\t\t/// <param name=\"text\">Text in OSD window.<br/>Sets <see cref=\"Text\"/>.</param>\n\t\t/// <param name=\"secondsTimeout\"><inheritdoc cref=\"SecondsTimeout\" path=\"/summary/node()\"/>Sets <see cref=\"SecondsTimeout\"/>.</param>\n\t\t/// <param name=\"xy\"><inheritdoc cref=\"XY\" path=\"/summary/node()\"/>Sets <see cref=\"XY\"/>.</param>\n\t\t/// <param name=\"icon\"><inheritdoc cref=\"Icon\" path=\"/summary/node()\"/>Sets <see cref=\"Icon\"/>.</param>\n\t\t/// <param name=\"textColor\"><inheritdoc cref=\"TextColor\" path=\"/summary/node()\"/>Sets <see cref=\"TextColor\"/>.</param>\n\t\t/// <param name=\"backColor\"><inheritdoc cref=\"BackColor\" path=\"/summary/node()\"/>Sets <see cref=\"BackColor\"/>.</param>\n\t\t/// <param name=\"font\"><inheritdoc cref=\"Font\" path=\"/summary/node()\"/>Sets <see cref=\"Font\"/>.</param>\n\t\t/// <param name=\"name\">OSD window name.<br/>Sets <see cref=\"OsdWindow.Name\"/>.</param>\n\t\t/// <param name=\"showMode\">Sets <see cref=\"ShowMode\"/>.</param>\n\t\t/// <param name=\"dontShow\">Don't call <see cref=\"Show\"/>. The caller can use the return value to set some other properties and call <c>Show</c>.</param>\n\t\t/// <returns>Returns an <see cref=\"osdText\"/> object that can be used to change properties or close the OSD window.</returns>\n\t\t/// <remarks>\n\t\t/// Also sets these properties: <see cref=\"ClickToClose\"/>=<c>true</c>, <see cref=\"Shadow\"/>=<c>true</c>.\n\t\t/// </remarks>\n\t\tpublic static osdText showText(string text,\n\t\t\tint secondsTimeout = 0, PopupXY xy = null,\n\t\t\tobject icon = null, ColorInt? textColor = null, ColorInt? backColor = null, FontNSS font = null,\n\t\t\tstring name = null, OsdMode showMode = default, bool dontShow = false) {\n\t\t\tvar o = new osdText {\n\t\t\t\t_text = text,\n\t\t\t\tSecondsTimeout = secondsTimeout,\n\t\t\t\t_xy = xy,\n\t\t\t\tIcon = icon,\n\t\t\t\t_textColor = textColor ?? defaultTextColor,\n\t\t\t\t_backColor = backColor ?? defaultBackColor,\n\t\t\t\tFont = font ?? defaultSmallFont,\n\t\t\t\tName = name,\n\t\t\t\tShowMode = showMode,\n\t\t\t\tClickToClose = true,\n\t\t\t\tShadow = true\n\t\t\t};\n\t\t\t\n\t\t\tif (!dontShow) o.Show();\n\t\t\treturn o;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Shows a big-font text with transparent background.\n\t\t/// </summary>\n\t\t/// <param name=\"color\">Sets <see cref=\"TextColor\"/>. Default: <see cref=\"defaultTransparentTextColor\"/>.</param>\n\t\t/// <param name=\"font\">Sets <see cref=\"Font\"/>. Default: <see cref=\"defaultBigFont\"/>.</param>\n\t\t/// <remarks>\n\t\t/// Also sets these properties: <see cref=\"OsdWindow.Opacity\"/>=0.\n\t\t/// </remarks>\n\t\t/// <inheritdoc cref=\"showText(string, int, PopupXY, object, ColorInt?, ColorInt?, FontNSS, string, OsdMode, bool)\"/>\n\t\tpublic static osdText showTransparentText(string text,\n\t\t\tint secondsTimeout = 0, PopupXY xy = null,\n\t\t\tColorInt? color = null, FontNSS font = null,\n\t\t\tstring name = null, OsdMode showMode = default, bool dontShow = false) {\n\t\t\tvar o = new osdText {\n\t\t\t\t_text = text,\n\t\t\t\tSecondsTimeout = secondsTimeout,\n\t\t\t\t_xy = xy,\n\t\t\t\t_textColor = color ?? defaultTransparentTextColor,\n\t\t\t\tFont = font ?? s_bigFont,\n\t\t\t\tName = name,\n\t\t\t\tShowMode = showMode,\n\t\t\t\tOpacity = 0d\n\t\t\t};\n\t\t\t\n\t\t\tif (!dontShow) o.Show();\n\t\t\treturn o;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Shows on-screen image.\n\t\t/// </summary>\n\t\t/// <param name=\"image\">Sets <see cref=\"BackgroundImage\"/>.</param>\n\t\t/// <remarks>\n\t\t/// Also sets these properties: <see cref=\"IsOfImageSize\"/>=<c>true</c>, <see cref=\"OsdWindow.Opacity\"/>=0, <see cref=\"ClickToClose\"/>=<c>true</c>.\n\t\t/// </remarks>\n\t\t/// <inheritdoc cref=\"showText(string, int, PopupXY, object, ColorInt?, ColorInt?, FontNSS, string, OsdMode, bool)\"/>\n\t\tpublic static osdText showImage(Image image,\n\t\t\tint secondsTimeout = 0, PopupXY xy = null,\n\t\t\tstring name = null, OsdMode showMode = default, bool dontShow = false) {\n\t\t\tvar o = new osdText {\n\t\t\t\tBackgroundImage = image,\n\t\t\t\tSecondsTimeout = secondsTimeout,\n\t\t\t\t_xy = xy,\n\t\t\t\tName = name,\n\t\t\t\tShowMode = showMode,\n\t\t\t\tIsOfImageSize = true,\n\t\t\t\tOpacity = 0d,\n\t\t\t\tClickToClose = true\n\t\t\t};\n\t\t\to._backColor = o.TransparentColor;\n\t\t\t\n\t\t\tif (!dontShow) o.Show();\n\t\t\treturn o;\n\t\t}\n\t\t\n\t\t/// <summary>Default font for <see cref=\"showText\"/> and <see cref=\"osdText\"/>. Default: standard GUI font (usually <c>Segoe UI</c>), size 12.</summary>\n\t\t/// <exception cref=\"ArgumentNullException\"></exception>\n\t\tpublic static FontNSS defaultSmallFont { get => s_smallFont; set => s_smallFont = value ?? throw new ArgumentNullException(); }\n\t\tstatic FontNSS s_smallFont = new(12);\n\t\t\n\t\t/// <summary>Default font for <see cref=\"showTransparentText\"/>. Default: standard GUI font (usually <c>Segoe UI</c>), size 24.</summary>\n\t\t/// <exception cref=\"ArgumentNullException\"></exception>\n\t\tpublic static FontNSS defaultBigFont { get => s_bigFont; set => s_bigFont = value ?? throw new ArgumentNullException(); }\n\t\tstatic FontNSS s_bigFont = new(24);\n\t\t\n\t\t/// <summary>Default text color for <see cref=\"showText\"/> and <see cref=\"osdText\"/>. Default: 0x(dark gray).</summary>\n\t\tpublic static ColorInt defaultTextColor { get; set; } = 0x404040;\n\t\t\n\t\t/// <summary>Default border color for <see cref=\"showText\"/> and <see cref=\"osdText\"/>. Default: 0x404040 (dark gray).</summary>\n\t\tpublic static ColorInt defaultBorderColor { get; set; } = 0x404040;\n\t\t\n\t\t/// <summary>Default background color for <see cref=\"showText\"/> and <see cref=\"osdText\"/>. Default: 0xFFFFF0 (light yellow).</summary>\n\t\tpublic static ColorInt defaultBackColor { get; set; } = 0xFFFFF0;\n\t\t\n\t\t/// <summary>Default text color for <see cref=\"showTransparentText\"/>. Default: <c>0x8A2BE2</c> (<c>Color.BlueViolet</c>).</summary>\n\t\tpublic static ColorInt defaultTransparentTextColor { get; set; } = 0x8A2BE2;\n\t\t\n\t\t/// <summary>\n\t\t/// Default screen when <see cref=\"XY\"/> is not set.\n\t\t/// The <see cref=\"screen\"/> must be lazy or empty.\n\t\t/// </summary>\n\t\t/// <exception cref=\"ArgumentException\"><see cref=\"screen\"/> with <c>Handle</c>. Must be lazy (with <see cref=\"screen.LazyFunc\"/>) or empty.</exception>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// osdText.defaultScreen = screen.index(1, lazy: true);\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic static screen defaultScreen {\n\t\t\tget => _defaultScreen;\n\t\t\tset => _defaultScreen = value.ThrowIfWithHandle_;\n\t\t}\n\t\tstatic screen _defaultScreen;\n\t\t\n\t\t#endregion\n\t}\n}\n\nnamespace Au.Types {\n\t/// <summary>\n\t/// Whether <see cref=\"osdText.Show\"/> waits or shows the OSD window in this or new thread.\n\t/// </summary>\n\t/// <remarks>\n\t/// If this thread has windows, any value can be used, but usually <c>Auto</c> (default) or <c>ThisThread</c> is the best.\n\t/// </remarks>\n\tpublic enum OsdMode {\n\t\t/// <summary>Depends on <see cref=\"process.thisThreadHasMessageLoop\"/>. If it is <c>true</c>, uses <c>ThisThread</c>, else <c>StrongThread</c>. Does not wait.</summary>\n\t\tAuto,\n\t\t\n\t\t/// <summary>\n\t\t/// Show the OSD window in this thread and don't wait.\n\t\t/// This thread must must be a UI thread (with windows etc).\n\t\t/// </summary>\n\t\tThisThread,\n\t\t\n\t\t/// <summary>Show the OSD window in new thread and don't wait. Set <see cref=\"Thread.IsBackground\"/>=<c>true</c>, so that the OSD is closed when other threads of this app end.</summary>\n\t\tWeakThread,\n\t\t\n\t\t/// <summary>Show the OSD window in new thread and don't wait. Set <see cref=\"Thread.IsBackground\"/>=<c>false</c>, so that the OSD is not closed when other threads of this app end.</summary>\n\t\tStrongThread,\n\t\t\n\t\t/// <summary>\n\t\t/// Show the OSD window in this thread and wait until it disappears.\n\t\t/// Waits <see cref=\"osdText.SecondsTimeout\"/> seconds. While waiting, dispatches messages etc; see <see cref=\"wait.doEvents(int)\"/>.\n\t\t/// </summary>\n\t\tWait,\n\t}\n}"
  },
  {
    "path": "Au/GUI/popupMenu/MTBase.cs",
    "content": "using System.Drawing;\n\nnamespace Au.Types;\n\n/// <summary>\n/// Base class of <see cref=\"popupMenu\"/> and <see cref=\"toolbar\"/>.\n/// </summary>\n/// <remarks>\n/// <i>image</i> argument of \"add item\" functions can be:\n/// - icon name, like <c>\"*Pack.Icon color\"</c> (you can get it from the <b>Icons</b> tool). See <see cref=\"ImageUtil.LoadWpfImageElement\"/>.\n/// - file/folder path (string) - the \"show\" function calls <see cref=\"icon.of\"/> to get its icon. It also supports file type icons like <c>\".txt\"</c>, etc.\n/// - file path with prefix <c>\"imagefile:\"</c> or resource path that starts with <c>\"resources/\"</c> or has prefix <c>\"resource:\"</c> - the \"show\" function loads <c>.png</c> or <c>.xaml</c> image file or resource.\n/// - string with prefix <c>\"image:\"</c> - Base64 encoded image file. Can be created with the <b>Find image</b> tool.\n/// - <see cref=\"FolderPath\"/> - same as folder path string.\n/// - <see cref=\"Image\"/> - image.\n/// - <see cref=\"icon\"/> - icon. The \"add item\" function disposes it.\n/// - <see cref=\"StockIcon\"/> - the \"show\" function calls <see cref=\"icon.stock\"/>.\n/// - <c>null</c> - if <see cref=\"ExtractIconPathFromCode\"/> <c>true</c>, the \"show\" function tries to extract a file path from action code; then calls <see cref=\"icon.of\"/>. Else no image.\n/// - string <c>\"\"</c> - no image, even if <see cref=\"ExtractIconPathFromCode\"/> <c>true</c>.\n/// \n/// Item images should be of size 16x16 (small icon size). If high DPI, will scale images automatically, which makes them slightly blurred. To avoid scaling, can be used XAML images, but then slower.\n/// \n/// Images are loaded on demand, when showing the menu or submenu etc. If fails to load, prints warning (<see cref=\"print.warning\"/>).\n/// \n/// For icon/image files use full path, unless they are in <see cref=\"folders.ThisAppImages\"/>\n/// \n/// To add an image resource in Visual Studio, use build action <b>Resource</b> for the image file.\n/// </remarks>\npublic abstract partial class MTBase {\n\tprivate protected readonly string _name;\n\tprivate protected readonly string _sourceFile;\n\tprivate protected readonly int _sourceLine;\n\tprivate protected readonly int _threadId;\n\tprivate protected wnd _w;\n\tprivate protected int _dpi;\n\t(wnd tt, MTItem item, RECT rect) _tt;\n\t\n\tprivate protected MTBase() {\n\t\t_threadId = Api.GetCurrentThreadId();\n\t}\n\t\n\tprivate protected MTBase(string name, string f_, int l_, string m_ = null) : this() {\n\t\tif (name == null && !m_.NE())\n\t\t\tif (m_[0] is not ('<' or '.')) name = m_; //<Main>$, .ctor\n\t\t_name = name;\n\t\t_sourceFile = f_;\n\t\t_sourceLine = l_;\n\t}\n\t\n\tprivate protected virtual void _WmNccreate(wnd w) {\n\t\t_w = w;\n\t\tMouseCursor.SetArrowCursor_(); //workaround for: briefly shows \"wait\" cursor when entering mouse first time in process\n\t}\n\t\n\tprivate protected virtual void _WmNcdestroy() {\n\t\t_w = default;\n\t\t_tt = default;\n\t\tif (_stdAO != null) {\n\t\t\tMarshal.ReleaseComObject(_stdAO);\n\t\t\t_stdAO = null;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Extract file path or script path from item action code (for example <see cref=\"run.it\"/> or <see cref=\"script.run\"/> argument) and use icon of that file or script.\n\t/// This property is applied to items added afterwards; submenus inherit it.\n\t/// </summary>\n\t/// <value>Default: <see cref=\"toolbar\"/> <c>true</c>, <see cref=\"popupMenu\"/> with <i>name</i> <c>true</c>, <see cref=\"popupMenu\"/> without <i>name</i> <c>false</c>.</value>\n\t/// <remarks>\n\t/// Gets path from code that contains a string like <c>@\"c:\\windows\\system32\\notepad.exe\"</c> or <c>@\"%folders.System%\\notepad.exe\"</c> or URL/shell or <c>@\"\\folder\\script.cs\"</c>.\n\t/// Also supports code patterns like <c>folders.System + \"notepad.exe\"</c>, <c>folders.shell.RecycleBin</c>.\n\t/// \n\t/// If extracts path, also in the context menu adds item <b>Find file</b> which selects the file in Explorer or <b>Open script</b> which opens the script in editor.\n\t/// </remarks>\n\tpublic bool ExtractIconPathFromCode { get; set; }\n\t\n\t/// <summary>\n\t/// Execute item actions asynchronously in new threads.\n\t/// This property is applied to items added afterwards; submenus inherit it.\n\t/// </summary>\n\t/// <value>Default: <see cref=\"toolbar\"/> <c>true</c>, <see cref=\"popupMenu\"/> <c>false</c>.</value>\n\t/// <remarks>\n\t/// If current thread is a UI thread (has windows etc) or has triggers or hooks, and item action functions execute some long automations etc in current thread, current thread probably is hung during that time. Set this property = <c>true</c> to avoid it.\n\t/// </remarks>\n\tpublic bool ActionThread { get; set; }\n\t\n\t/// <summary>\n\t/// Whether to handle exceptions in item action code. If <c>false</c> (default), handles exceptions and on exception calls <see cref=\"print.warning\"/>.\n\t/// This property is applied to items added afterwards; submenus inherit it.\n\t/// </summary>\n\t/// <value>Default: <see cref=\"toolbar\"/> <c>false</c>, <see cref=\"popupMenu\"/> <c>false</c>.</value>\n\tpublic bool ActionException { get; set; }\n\t\n\t/// <summary>\n\t/// If an item has file path, show it in tooltip.\n\t/// This property is applied to items added afterwards; submenus inherit it.\n\t/// </summary>\n\t/// <value>Default: <see cref=\"toolbar\"/> <c>false</c>, <see cref=\"popupMenu\"/> <c>false</c>.</value>\n\tpublic bool PathInTooltip { get; set; }\n\t\n\t/// <summary>\n\t/// Width and height of images. Default 16, valid 16-256.\n\t/// </summary>\n\tpublic int ImageSize { get; set; } = 16;\n\t\n\tprivate protected IconImageCache _ImageCache => field ??= IconImageCache.CommonOfSize(ImageSize);\n\t\n\tprivate protected void _CopyProps(MTBase m) {\n\t\tm.ImageSize = ImageSize;\n\t\tm.ActionException = ActionException;\n\t\tm.ActionThread = ActionThread;\n\t\tm.ExtractIconPathFromCode = ExtractIconPathFromCode;\n\t\tm.PathInTooltip = PathInTooltip;\n\t}\n\t\n\tprivate protected string _SourceLink(MTItem x, string text) => x.sourceFile == null ? null : $\"<open {x.sourceFile}|{x.sourceLine}>{text}<>\";\n\t\n\tprivate protected bool _IsOtherThread => _threadId != Api.GetCurrentThreadId();\n\t\n\tinternal void _ThreadTrap() {\n\t\tif (_threadId != Api.GetCurrentThreadId()) throw new InvalidOperationException(\"Wrong thread.\");\n\t}\n\t\n\t/// <summary>\n\t/// Converts <c>x.image</c> (object containing string, <c>Image</c>, etc or <c>null</c>) to <c>Image</c>. Extracts icon path from code if need. Returns default if will extract async.\n\t/// </summary>\n\tprivate protected (Image image, bool dispose) _GetImage(MTItem x) {\n\t\tImage im = null; bool dontDispose = false;\n\t\t\n\t\tif (x.extractIconPath == 1) { //extract path always, not only when x.image==null, or we would not have path for other purposes\n\t\t\tx.file = icon.ExtractIconPathFromCode_(x.clicked.Method, out bool cs);\n\t\t\tif (x.file != null) {\n\t\t\t\tx.image ??= x.file;\n\t\t\t\tx.extractIconPath = (byte)(cs ? 4 : 2);\n\t\t\t} else x.extractIconPath = 3;\n\t\t}\n\t\t\n\t\tswitch (x.image) {\n\t\tcase Image g:\n\t\t\tim = g;\n\t\t\tdontDispose = true;\n\t\t\tbreak;\n\t\tcase string s when s.Length > 0:\n\t\t\ttry {\n\t\t\t\tdontDispose = true;\n\t\t\t\tbool isImage = ImageUtil.HasImageOrResourcePrefix(s);\n\t\t\t\tim = _ImageCache.Get(s, _dpi, isImage, _OnException);\n\t\t\t\tif (im == null && isImage) _OnException(s, null);\n\t\t\t}\n\t\t\tcatch (Exception e1) { _OnException(null, e1); }\n\t\t\t\n\t\t\tvoid _OnException(string s, Exception e) {\n\t\t\t\tprint.it($\"<>Failed to load image. {e?.ToStringWithoutStack().TrimEnd('.') ?? s}. {_SourceLink(x, \"Edit\")}\");\n\t\t\t}\n\t\t\tbreak;\n\t\tcase StockIcon si:\n\t\t\tim = icon.stock(si)?.ToGdipBitmap();\n\t\t\tbreak;\n\t\t}\n\t\treturn (im, im != null && !dontDispose);\n\t}\n\t\n\tprivate protected string _GetFullTooltip(MTItem b) {\n\t\tvar s = b.Tooltip;\n\t\tif (this is toolbar tb) {\n\t\t\tvar v = b as TBItem;\n\t\t\tif (!(tb.DisplayText || v.textAlways)) {\n\t\t\t\tif (s.NE()) s = v.Text;\n\t\t\t\telse if (!v.Text.NE()) s = b.Text + \"\\n\" + s;\n\t\t\t}\n\t\t}\n\t\tif (b.pathInTooltip) {\n\t\t\tvar sf = b.File;\n\t\t\tif (!(sf.NE() || sf.Starts(\"::\") || sf.Starts(\"shell:\"))) s = s.NE() ? sf : s + \"\\n\" + sf;\n\t\t}\n\t\treturn s;\n\t}\n\t\n\tprivate protected unsafe void _SetTooltip(MTItem b, RECT r, nint lParam, int submenuDelay = -1) {\n\t\tstring s = _GetFullTooltip(b);\n\t\tbool setTT = !s.NE() && b != _tt.item;\n\t\tif (!setTT && (setTT = _tt.item != null && _tt.item.rect != _tt.rect)) b = _tt.item; //update tooltip tool rect\n\t\tif (setTT) {\n\t\t\t_tt.rect = r;\n\t\t\t_tt.item = b;\n\t\t\tif (!_tt.tt.IsAlive) {\n\t\t\t\t_tt.tt = Api.CreateWindowEx(WSE.TOPMOST | WSE.TRANSPARENT, \"tooltips_class32\", null, Api.TTS_ALWAYSTIP | Api.TTS_NOPREFIX, 0, 0, 0, 0, _w);\n\t\t\t\t_tt.tt.Send(Api.TTM_ACTIVATE, 1);\n\t\t\t\t_tt.tt.Send(Api.TTM_SETMAXTIPWIDTH, 0, screen.of(_w).WorkArea.Width / 3);\n\t\t\t}\n\t\t\t\n\t\t\tif (b is PMItem) { //ensure the tooltip is above submenu in Z order\n\t\t\t\t_tt.tt.ZorderTopRaw_();\n\t\t\t\tif (submenuDelay > 0) submenuDelay += 100;\n\t\t\t\t_tt.tt.Send(0x403, 3, submenuDelay > (int)_tt.tt.Send(0x415, 3) ? submenuDelay : -1); //TTM_SETDELAYTIME,TTM_GETDELAYTIME,TTDT_INITIAL\n\t\t\t}\n\t\t\t\n\t\t\tfixed (char* ps = s) {\n\t\t\t\tvar g = new Api.TTTOOLINFO { cbSize = sizeof(Api.TTTOOLINFO), hwnd = _w, uId = 1, lpszText = ps, rect = r };\n\t\t\t\t_tt.tt.Send(Api.TTM_DELTOOL, 0, &g);\n\t\t\t\t_tt.tt.Send(Api.TTM_ADDTOOL, 0, &g);\n\t\t\t}\n\t\t} else {\n\t\t\tif (b != _tt.item) _HideTooltip();\n\t\t}\n\t\t\n\t\tif (_tt.item != null) {\n\t\t\tvar v = new MSG { hwnd = _w, message = Api.WM_MOUSEMOVE, lParam = lParam };\n\t\t\t_tt.tt.Send(Api.TTM_RELAYEVENT, 0, &v);\n\t\t}\n\t}\n\t\n\tprivate protected unsafe void _HideTooltip() {\n\t\tif (_tt.item != null) {\n\t\t\t_tt.item = null;\n\t\t\tvar g = new Api.TTTOOLINFO { cbSize = sizeof(Api.TTTOOLINFO), hwnd = _w, uId = 1 };\n\t\t\t_tt.tt.Send(Api.TTM_DELTOOL, 0, &g);\n\t\t}\n\t}\n}\n\n/// <summary>\n/// Base of <see cref=\"PMItem\"/> etc.\n/// </summary>\npublic abstract class MTItem {\n\tinternal Delegate clicked;\n\tinternal object image;\n\t/// <summary>1 if need to extract, 2 if already extracted (the image field is the path), 3 if failed to extract, 4 if extracted <c>\"script.cs\"</c></summary>\n\tinternal byte extractIconPath; //from MTBase.ExtractIconPathFromCode\n\tinternal bool actionThread; //from MTBase.ActionThread\n\tinternal bool actionException; //from MTBase.ActionException\n\tinternal bool pathInTooltip; //from MTBase.PathInTooltip\n\tinternal int sourceLine;\n\tinternal string sourceFile;\n\tinternal string file;\n\tinternal RECT rect;\n\tinternal Image image2;\n\t\n\tinternal bool HasImage_ => image2 != null;\n\t\n\t/// <summary>\n\t/// Item text.\n\t/// </summary>\n\tpublic string Text { get; set; }\n\t\n\t/// <summary>\n\t/// Item tooltip.\n\t/// </summary>\n\tpublic string Tooltip { get; set; }\n\t\n\t/// <summary>\n\t/// Any value. Not used by this library.\n\t/// </summary>\n\tpublic object Tag { get; set; }\n\t\n\t///\n\tpublic ColorInt TextColor { get; set; }\n\t\n\t/// <summary>\n\t/// Gets file or script path extracted from item action code (see <see cref=\"MTBase.ExtractIconPathFromCode\"/>) or sets path as it would be extracted.\n\t/// </summary>\n\t/// <remarks>\n\t/// Can be used to set file or script path when it cannot be extracted from action code.\n\t/// When you set this property, the menu/toolbar item uses icon of the specified file, and its context menu contains <b>Find file</b> or <b>Open script</b>.\n\t/// </remarks>\n\tpublic string File {\n\t\tget => file;\n\t\tset {\n\t\t\tfile = value;\n\t\t\tif (file == null) {\n\t\t\t\textractIconPath = 3;\n\t\t\t} else {\n\t\t\t\timage ??= file;\n\t\t\t\tbool cs = file.Ends(\".cs\", true) && !pathname.isFullPath(file, orEnvVar: true);\n\t\t\t\textractIconPath = (byte)(cs ? 4 : 2);\n\t\t\t}\n\t\t}\n\t}\n\t\n\tinternal void GoToFile_() {\n\t\tif (file.NE()) return;\n\t\tif (extractIconPath == 2) run.selectInExplorer(file);\n\t\telse ScriptEditor.Open(file);\n\t}\n\t\n\tinternal static (bool edit, bool go, string goText) CanEditOrGoToFile_(string sourceFile, MTItem item) {\n\t\tif (sourceFile != null) {\n\t\t\tif (ScriptEditor.Available) {\n\t\t\t\tif (item?.file == null) return (true, false, null);\n\t\t\t\treturn (true, true, item.extractIconPath == 2 ? \"Find file\" : \"Open script\");\n\t\t\t} else if (item?.extractIconPath == 2) {\n\t\t\t\treturn (false, true, \"Find file\");\n\t\t\t}\n\t\t}\n\t\treturn default;\n\t}\n\t\n\t/// <summary>\n\t/// Call when adding menu/toolbar item.\n\t/// Sets text and tooltip (from text). Sets <c>clicked</c>, <c>image</c> and <c>sourceLine</c> fields.\n\t/// Sets <c>extractIconPath</c>, <c>actionThread</c> and <c>actionException</c> fields from <i>mt</i> properties.\n\t/// </summary>\n\tinternal void Set_(MTBase mt, string text, Delegate click, MTImage im, int l_, string f_) {\n\t\tif (!text.NE()) {\n\t\t\tvar mi = this as PMItem;\n\t\t\tbool rawText = mi?.rawText ?? false;\n\t\t\tint i = text.IndexOf('\\0');\n\t\t\tif (i < 0 && !rawText) i = text.IndexOf('|');\n\t\t\tif (i >= 0) {\n\t\t\t\tvar v = _Split(text, i);\n\t\t\t\ttext = v.Item1;\n\t\t\t\tTooltip = v.Item2;\n\t\t\t}\n\t\t\tint len = text.Lenn();\n\t\t\tif (len > 0 && text[^1] == '\\a') {\n\t\t\t\ttext = text[..^1]; //remove for menu too, because user may move items from toolbar to menu and forget to remove '\\a'\n\t\t\t\tlen--;\n\t\t\t\tif (this is TBItem ti) ti.textAlways = true; //note: textAlways of groups is already true\n\t\t\t}\n\t\t\tif (mi != null && !rawText && len > 1) {\n\t\t\t\ti = text.IndexOf('\\t', 1);\n\t\t\t\tif (i > 0) {\n\t\t\t\t\tvar v = _Split(text, i);\n\t\t\t\t\ttext = v.Item1;\n\t\t\t\t\tmi.Hotkey = v.Item2;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!text.NE()) Text = text;\n\t\t\t\n\t\t\tstatic (string, string) _Split(string s, int i) {\n\t\t\t\tint j = i + 1; if (s.Eq(j, ' ')) j++;\n\t\t\t\treturn (i > 0 ? s[..i] : null, j < s.Length ? s[j..] : null);\n\t\t\t}\n\t\t}\n\t\t\n\t\timage = im.Value;\n\t\tif (image is icon ic) { image = ic.ToGdipBitmap(); image ??= \"\"; } //DestroyIcon now; don't extract from code.\n\t\t\n\t\tclicked = click;\n\t\tsourceLine = l_;\n\t\tsourceFile = f_;\n\t\t\n\t\textractIconPath = (byte)((mt.ExtractIconPathFromCode && clicked is not (null or Action<popupMenu> or Func<popupMenu>)) ? 1 : 0);\n\t\tactionThread = mt.ActionThread;\n\t\tactionException = mt.ActionException;\n\t\tpathInTooltip = mt.PathInTooltip;\n\t}\n\t\n\t///\n\tpublic override string ToString() => Text;\n}\n\n/// <summary>\n/// Used for menu/toolbar function parameters to specify an image in different ways (file path, <see cref=\"Image\"/> object, etc).\n/// </summary>\n/// <remarks>\n/// Has implicit conversions from string, <see cref=\"Image\"/>, <see cref=\"icon\"/>, <see cref=\"StockIcon\"/>, <see cref=\"FolderPath\"/>.\n/// More info: <see cref=\"MTBase\"/>.\n/// </remarks>\npublic struct MTImage {\n\treadonly object _o;\n\tMTImage(object o) { _o = o; }\n\t\n\t///\n\tpublic static implicit operator MTImage(string pathEtc) => new(pathEtc);\n\t///\n\tpublic static implicit operator MTImage(Image image) => new(image);\n\t///\n\tpublic static implicit operator MTImage(icon icon) => new(icon);\n\t///\n\tpublic static implicit operator MTImage(StockIcon icon) => new(icon);\n\t///\n\tpublic static implicit operator MTImage(FolderPath path) => new((string)path);\n\t\n\t/// <summary>\n\t/// Gets the raw value stored in this variable. Can be <c>string</c>, <see cref=\"Image\"/>, <see cref=\"icon\"/>, <see cref=\"StockIcon\"/> or <c>null</c>.\n\t/// </summary>\n\tpublic object Value => _o;\n}\n"
  },
  {
    "path": "Au/GUI/popupMenu/pm acc.cs",
    "content": "\nusing IAccessible = Au.Types.Api.IAccessible;\nusing VarInt = Au.Types.Api.VarInt;\nusing NAVDIR = Au.Types.Api.NAVDIR;\n\nnamespace Au.Types {\n\t[ComVisible(true)]\n\tpartial class MTBase {\n\t\tprivate protected bool _WmGetobject(nint wParam, nint lParam, out nint result) {\n\t\t\tresult = default;\n\t\t\tvar oid = (EObjid)lParam;\n\t\t\tif (oid != EObjid.CLIENT) return false;\n\t\t\tresult = Cpp.Cpp_AccWorkaround((IAccessible)this, wParam, ref _accWorkaround);\n\t\t\treturn true;\n\t\t}\n\t\tnint _accWorkaround;\n\n\t\t///\n\t\t~MTBase() { if (_accWorkaround != 0) Cpp.Cpp_AccWorkaround(null, 0, ref _accWorkaround); }\n\n\t\tprivate protected IAccessible _StdAO {\n\t\t\tget {\n\t\t\t\tif (_stdAO == null) Api.CreateStdAccessibleObject(_w, EObjid.CLIENT, typeof(IAccessible).GUID, out _stdAO);\n\t\t\t\treturn _stdAO;\n\t\t\t}\n\t\t}\n\t\tIAccessible _stdAO;\n\t}\n}\n\nnamespace Au {\n\t[ComVisible(true)]\n\tpartial class popupMenu : IAccessible {\n\t\tIAccessible IAccessible.get_accParent() => _StdAO.get_accParent();\n\n\t\tint IAccessible.get_accChildCount() => _a.Count;\n\n\t\tint IAccessible.get_accChild(VarInt varChild, out object ppdispChild) { ppdispChild = null; return 1; }\n\n\t\tstring IAccessible.get_accName(VarInt varChild)\n\t\t\t=> !_B(varChild, out var b) ? _name : (b.rawText ? b.Text : StringUtil.RemoveUnderlineChar(b.Text));\n\n\t\tstring IAccessible.get_accValue(VarInt varChild) => null;\n\n\t\tstring IAccessible.get_accDescription(VarInt varChild) => _B(varChild, out _) ? null : \"Popup menu\";\n\n\t\tVarInt IAccessible.get_accRole(VarInt varChild) {\n\t\t\tvar r = !_B(varChild, out var b) ? ERole.MENUPOPUP : (b.IsSeparator ? ERole.SEPARATOR : ERole.MENUITEM);\n\t\t\treturn (int)r - 1;\n\t\t}\n\n\t\tVarInt IAccessible.get_accState(VarInt varChild) {\n\t\t\tEState r = 0;\n\t\t\tif (!_w.IsEnabled()) r |= EState.DISABLED;\n\t\t\tif (!_B(varChild, out var b)) {\n\t\t\t\tif (!_w.IsVisible) r |= EState.INVISIBLE;\n\t\t\t} else {\n\t\t\t\tif (b.IsDisabled) r |= EState.DISABLED;\n\t\t\t\tif (FocusedItem == b) r |= EState.FOCUSED | EState.HOTTRACKED;\n\t\t\t\tif (b.IsChecked) r |= EState.CHECKED;\n\t\t\t\tif (b.IsSubmenu) r |= EState.HASPOPUP;\n\t\t\t\t//TODO3: if offscreen, r |= EState.INVISIBLE | EState.OFFSCREEN;\n\t\t\t}\n\t\t\treturn (int)r - 1;\n\t\t}\n\n\t\tstring IAccessible.get_accHelp(VarInt varChild) => _B(varChild, out var b) ? _GetFullTooltip(b) : null;\n\n\t\tint IAccessible.get_accHelpTopic(out string pszHelpFile, VarInt varChild) => throw new NotImplementedException();\n\n\t\tstring IAccessible.get_accKeyboardShortcut(VarInt varChild) {\n\t\t\tif (_B(varChild, out var b) && !b.rawText) {\n\t\t\t\tvar s = b.Text;\n\t\t\t\tint i = StringUtil.FindUnderlineChar(s);\n\t\t\t\tif (i >= 0) return s[i].ToString();\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\tobject IAccessible.get_accFocus() => null;\n\n\t\tobject IAccessible.get_accSelection() => null;\n\n\t\tstring IAccessible.get_accDefaultAction(VarInt varChild)\n\t\t\t=> !_B(varChild, out var b) || b.IsDisabled ? null : b.IsSubmenu ? \"Open\" : \"Execute\";\n\n\t\tvoid IAccessible.accSelect(ESelect flagsSelect, VarInt varChild) => throw new NotImplementedException();\n\n\t\tvoid IAccessible.accLocation(out int pxLeft, out int pyTop, out int pcxWidth, out int pcyHeight, VarInt varChild) {\n\t\t\tif (!_B(varChild, out var b)) {\n\t\t\t\t_StdAO.accLocation(out pxLeft, out pyTop, out pcxWidth, out pcyHeight, varChild);\n\t\t\t} else {\n\t\t\t\tvar r = _ItemRect(b, inScreen: true);\n\t\t\t\tpxLeft = r.left; pyTop = r.top; pcxWidth = r.Width; pcyHeight = r.Height;\n\t\t\t}\n\t\t}\n\n\t\tobject IAccessible.accNavigate(NAVDIR navDir, VarInt varStart) {\n\t\t\tint i = varStart;\n\t\t\tif (navDir == NAVDIR.FIRSTCHILD || navDir == NAVDIR.LASTCHILD) {\n\t\t\t\tif (i == -1) return navDir == NAVDIR.FIRSTCHILD ? 1 : _a.Count;\n\t\t\t} else {\n\t\t\t\tif (i == -1) return _StdAO.accNavigate(navDir, varStart);\n\t\t\t\tswitch (navDir) {\n\t\t\t\tcase NAVDIR.PREVIOUS:\n\t\t\t\t\tif (i > 0) return i;\n\t\t\t\t\tbreak;\n\t\t\t\tcase NAVDIR.NEXT:\n\t\t\t\t\tif (++i < _a.Count) return i + 1;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\tVarInt IAccessible.accHitTest(int xLeft, int yTop) {\n\t\t\tPOINT p = new(xLeft, yTop); _w.MapScreenToClient(ref p);\n\t\t\tif (!_w.ClientRect.Contains(p)) return _StdAO.accHitTest(xLeft, yTop);\n\t\t\treturn _HitTest(p);\n\t\t}\n\n\t\tvoid IAccessible.accDoDefaultAction(VarInt varChild) {\n\t\t\tif (!_B(varChild, out var b) || b.IsDisabled) return;\n\t\t\t_w.Post(Api.WM_USER + 50, (int)varChild);\n\t\t}\n\n\t\tvoid IAccessible.put_accName(VarInt varChild, string szName) => throw new NotImplementedException();\n\n\t\tvoid IAccessible.put_accValue(VarInt varChild, string szValue) => throw new NotImplementedException();\n\n\t\tbool _B(VarInt varChild, out PMItem b) {\n\t\t\tint i = varChild;\n\t\t\tif (i == -1) { b = null; return false; }\n\t\t\tb = _a[i]; return true;\n\t\t}\n\t}\n}"
  },
  {
    "path": "Au/GUI/popupMenu/pm render.cs",
    "content": "using System.Drawing;\nusing System.Drawing.Drawing2D;\n\nnamespace Au;\n\npublic unsafe partial class popupMenu {\n\t/// <summary>\n\t/// Sets some metrics, for example item padding.\n\t/// </summary>\n\t/// <seealso cref=\"defaultMetrics\"/>\n\tpublic PMMetrics Metrics { get; set; }\n\t\n\t/// <summary>\n\t/// Sets or gets default metrics.\n\t/// </summary>\n\t/// <seealso cref=\"Metrics\"/>\n\tpublic static PMMetrics defaultMetrics {\n\t\tget => s_defaultMetrics ??= new();\n\t\tset { s_defaultMetrics = value; }\n\t}\n\tstatic PMMetrics s_defaultMetrics;\n\t\n\t/// <summary>\n\t/// Sets or gets font.\n\t/// </summary>\n\tpublic FontNSS Font { get; set; }\n\t\n\t/// <summary>\n\t/// Sets or gets default font.\n\t/// </summary>\n\tpublic static FontNSS defaultFont { get; set; }\n\t\n\tNativeFont_ _GetFont(bool bold = false) {\n\t\tvar font = Font ?? defaultFont;\n\t\tif (font == null) return bold ? NativeFont_.BoldCached(_dpi) : NativeFont_.RegularCached(_dpi);\n\t\tif (!bold) return _font ??= font.CreateFont(_dpi);\n\t\treturn _fontBold ??= (font with { Bold = true }).CreateFont(_dpi);\n\t}\n\tNativeFont_ _font, _fontBold; //disposing in _WmNcdestroy or ~NativeFont_\n\t\n\t_Metrics _met;\n\t\n\tclass _Metrics : IDisposable {\n\t\tpublic bool hasImages, hasSubmenus, hasSeparators, hasHotkeys, checkInImageColumn;\n\t\tpublic int border, paddingY, paddingLeft, paddingRight, textPaddingX, textPaddingY, image, check, check2, submenu, submenuMargin, separator, sepLine;\n\t\tpublic SIZE sizeCheck, sizeSubmenu;\n\t\tpublic IntPtr theme;\n\t\tpublic int xTextEnd, xHotkeyStart;\n\t\t\n\t\tpublic void Dispose() {\n\t\t\tif (theme != default) {\n\t\t\t\tApi.CloseThemeData(theme);\n\t\t\t\ttheme = default;\n\t\t\t}\n\t\t\tGC.SuppressFinalize(this);\n\t\t}\n\t\t\n\t\t~_Metrics() => Dispose();\n\t\t\n\t\tpublic _Metrics(popupMenu m) {\n\t\t\tbool hasCheck = false;\n\t\t\tforeach (var b in m._a) {\n\t\t\t\tif (b.IsSeparator) hasSeparators = true;\n\t\t\t\telse {\n\t\t\t\t\tif (b.HasImage_) hasImages = true;\n\t\t\t\t\tif (b.checkType > 0) hasCheck = true;\n\t\t\t\t\tif (b.IsSubmenu) hasSubmenus = true;\n\t\t\t\t\tif (b.Hotkey != null) hasHotkeys = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (hasCheck && hasImages) {\n\t\t\t\tcheckInImageColumn = true;\n\t\t\t\tforeach (var b in m._a) if (b.checkType > 0 && b.HasImage_) { checkInImageColumn = false; break; }\n\t\t\t}\n\t\t\t\n\t\t\tvar k = m.Metrics;\n\t\t\tif (k == null) {\n\t\t\t\tfor (var p = m._sub.parent; p != null; p = p._sub.parent) if ((k = p.Metrics) != null) break;\n\t\t\t}\n\t\t\tk ??= defaultMetrics;\n\t\t\t\n\t\t\tint dpi = m._dpi;\n\t\t\tborder = dpi / 96;\n\t\t\tpaddingY = Dpi.Scale(k.ItemPaddingY, dpi);\n\t\t\tpaddingLeft = Dpi.Scale(k.ItemPaddingLeft, dpi);\n\t\t\tpaddingRight = Dpi.Scale(k.ItemPaddingRight, dpi);\n\t\t\ttextPaddingX = Dpi.Scale(8, dpi);\n\t\t\ttextPaddingY = Dpi.Scale(1, dpi);\n\t\t\tif (hasImages) image = Dpi.Scale(m.ImageSize, dpi);\n\t\t\tif (hasCheck) check = Dpi.Scale(18, dpi);\n\t\t\tif (hasSubmenus) submenu = Dpi.Scale(16, dpi);\n\t\t\tseparator = Dpi.Scale(8, dpi);\n\t\t\tsepLine = 2;\n\t\t\t\n\t\t\ttheme = Api.OpenThemeData(m._w, \"Menu\", dpi);\n\t\t\tif (theme != default) {\n\t\t\t\tusing var dc = new ScreenDC_();\n\t\t\t\tif (hasSubmenus) {\n\t\t\t\t\tApi.GetThemePartSize(theme, dc, 16, 1, null, Api.THEMESIZE.TS_TRUE, out sizeSubmenu);\n\t\t\t\t\tsubmenu = Math.Max(submenu, sizeSubmenu.width + submenu / 4);\n\t\t\t\t}\n\t\t\t\tif (hasCheck) {\n\t\t\t\t\tApi.GetThemePartSize(theme, dc, 11, 1, null, Api.THEMESIZE.TS_TRUE, out sizeCheck);\n\t\t\t\t\tcheck = Math.Max(check, sizeCheck.width + 2);\n\t\t\t\t}\n\t\t\t\tif (hasSeparators && 0 == Api.GetThemePartSize(theme, dc, 15, 0, null, Api.THEMESIZE.TS_TRUE, out var z)) sepLine = z.height;\n\t\t\t}\n\t\t\t\n\t\t\tif (checkInImageColumn) { check2 = check; check = 0; }\n\t\t\t\n\t\t\tsubmenuMargin = hasHotkeys ? 0 : submenu;\n\t\t}\n\t}\n\t\n\tSIZE _Measure(int maxWidth) {\n\t\tSIZE R = default;\n\t\t\n\t\t_met?.Dispose();\n\t\t_met = new _Metrics(this);\n\t\t\n\t\tint buttonPlusX = (_met.border + _met.textPaddingX) * 2 + _met.check + _met.image + _met.submenu + _met.submenuMargin + _met.paddingLeft + _met.paddingRight;\n\t\tint maxTextWidth = maxWidth - buttonPlusX;\n\t\t\n\t\tvar font = _GetFont();\n\t\tusing var dc = new FontDC_(font);\n\t\tint textHeight = dc.MeasureEP(\" \").height;\n\t\t\n\t\tint maxHotkey = 0;\n\t\tif (_met.hasHotkeys) {\n\t\t\tforeach (var b in _a) {\n\t\t\t\tif (b.Hotkey == null) continue;\n\t\t\t\tint wid = dc.MeasureDT(b.Hotkey, c_tffHotkey).width;\n\t\t\t\tmaxHotkey = Math.Max(maxHotkey, Math.Min(wid, maxTextWidth / 2));\n\t\t\t}\n\t\t}\n\t\tint hotkeyPlus = textHeight * 3 / 2; //space between text and hotkey\n\t\tif (maxHotkey > 0) maxTextWidth -= maxHotkey += hotkeyPlus;\n\t\t\n\t\tint y = 0;\n\t\tfor (int i = 0; i < _a.Count; i++) {\n\t\t\t//note: to support multiline, wrap, underlines and tabs we have to use DrawText, not TextOut.\n\t\t\t//\tDrawText(DT_CALCRECT) is slow. Very slow compared with GetTextExtentPoint32. Eg 100-300 ms for 1000 items. Depends on text length.\n\t\t\tvar b = _a[i];\n\t\t\tSIZE z;\n\t\t\tif (b.IsSeparator) {\n\t\t\t\tz = new(0, _met.separator);\n\t\t\t} else {\n\t\t\t\tvar s = b.Text;\n\t\t\t\tif (!s.NE()) {\n\t\t\t\t\tif (b.FontBold) Api.SelectObject(dc, _GetFont(true));\n\t\t\t\t\tz = dc.MeasureDT(s, _TfFlags(b), maxTextWidth);\n\t\t\t\t\tz.width = Math.Min(z.width, maxTextWidth);\n\t\t\t\t\tif (b.FontBold) Api.SelectObject(dc, font);\n\t\t\t\t\t_met.xTextEnd = Math.Max(_met.xTextEnd, z.width);\n\t\t\t\t\tz.width += buttonPlusX;\n\t\t\t\t\tb.textHeight = z.height;\n\t\t\t\t} else z = new(0, textHeight);\n\t\t\t\tz.height = Math.Max(z.height + _met.textPaddingY * 2 + 1, _met.image + _met.image / 16 * 2) + (_met.border + _met.paddingY) * 2;\n\t\t\t}\n\t\t\tb.rect = new(0, y, z.width, z.height);\n\t\t\ty += z.height;\n\t\t\tR.width = Math.Max(R.width, z.width);\n\t\t\tR.height = Math.Max(R.height, y);\n\t\t}\n\t\t\n\t\tif (maxHotkey > 0) {\n\t\t\tR.width += maxHotkey;\n\t\t\t_met.xHotkeyStart = _met.xTextEnd + hotkeyPlus;\n\t\t}\n\t\tforeach (var b in _a) b.rect.right = R.width;\n\t\t\n\t\treturn R;\n\t}\n\t\n\tTFFlags _TfFlags(PMItem b) {\n\t\tvar f = c_tff; if (b.rawText) f |= TFFlags.NOPREFIX; else if (!_flags.Has(PMFlags.Underline)) f |= TFFlags.HIDEPREFIX;\n\t\treturn f;\n\t}\n\tconst TFFlags c_tff = TFFlags.EXPANDTABS | TFFlags.WORDBREAK /*| TFFlags.PATH_ELLIPSIS*/;\n\tconst TFFlags c_tffHotkey = TFFlags.NOPREFIX | TFFlags.SINGLELINE | TFFlags.VCENTER;\n\t\n\tvoid _Render(IntPtr dc, RECT rUpdate) {\n\t\tusing (var menuBrush = GdiObject_.SysColorBrush(_met.theme, Api.COLOR_BTNFACE)) menuBrush.BrushFill(dc, rUpdate);\n\t\t\n\t\tusing var g = _met.hasImages ? Graphics.FromHdc(dc) : null;\n\t\tif (g != null) {\n\t\t\tg.InterpolationMode = InterpolationMode.HighQualityBicubic;\n\t\t}\n\t\tvar font = _GetFont();\n\t\tusing var soFont = new GdiSelectObject_(dc, font);\n\t\tApi.SetBkMode(dc, 1);\n\t\tint textColor = Api.GetThemeSysColor(_met.theme, Api.COLOR_BTNTEXT),\n\t\t\ttextColorDisabled = Api.GetThemeSysColor(_met.theme, Api.COLOR_GRAYTEXT);\n\t\t\n\t\trUpdate.Offset(0, _scroll.Offset);\n\t\tfor (int i = _scroll.Pos; i < _a.Count; i++) {\n\t\t\tvar b = _a[i];\n\t\t\t\n\t\t\tif (b.rect.bottom <= rUpdate.top) continue;\n\t\t\tif (b.rect.top >= rUpdate.bottom) break;\n\t\t\t\n\t\t\tvar r = _ItemRect(b);\n\t\t\t\n\t\t\tif (b.IsSeparator) {\n\t\t\t\tr.Inflate(-_met.textPaddingX / 4, 0);\n\t\t\t\t//r.left+=_met.check+_met.image;\n\t\t\t\tr.top += (r.Height - _met.sepLine) / 2;\n\t\t\t\tr.Height = _met.sepLine;\n\t\t\t\tif (_met.theme != default) Api.DrawThemeBackground(_met.theme, dc, 15, 0, r);\n\t\t\t\telse Api.DrawEdge(dc, ref r, Api.EDGE_ETCHED, Api.BF_TOP);\n\t\t\t} else {\n\t\t\t\tif (b.BackgroundColor != default)\n\t\t\t\t\tusing (var brush = GdiObject_.ColorBrush(b.BackgroundColor)) brush.BrushFill(dc, r);\n\t\t\t\tif (i == _iHot) {\n\t\t\t\t\tif ((textColor == 0 || b.TextColor != default) && b.BackgroundColor == default)\n\t\t\t\t\t\tusing (var brush = GdiObject_.ColorBrush(0xC0DCF3)) brush.BrushFill(dc, r);\n\t\t\t\t\tusing (var brush = GdiObject_.ColorBrush(0x90C8F6)) brush.BrushRect(dc, r);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tr.Inflate(-_met.border, -_met.border - _met.paddingY);\n\t\t\t\tr.left += _met.paddingLeft; r.right -= _met.paddingRight;\n\t\t\t\tvar r2 = r;\n\t\t\t\t\n\t\t\t\tr.left += _met.check;\n\t\t\t\t\n\t\t\t\tif (b.HasImage_) {\n\t\t\t\t\ttry { g.DrawImage(b.image2, r.left + _met.textPaddingY, r.top + (r.Height - _met.image) / 2, _met.image, _met.image); }\n\t\t\t\t\tcatch (Exception ex) { Debug_.Print(ex); }\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tr.left += _met.image + _met.textPaddingX; r.right -= _met.textPaddingX + _met.submenu + _met.submenuMargin;\n\t\t\t\tr.top += _met.textPaddingY; r.bottom -= _met.textPaddingY;\n\t\t\t\t\n\t\t\t\tif (b.Hotkey != null) {\n\t\t\t\t\tApi.SetTextColor(dc, textColorDisabled);\n\t\t\t\t\tvar rr = r; rr.left += _met.xHotkeyStart;\n\t\t\t\t\tApi.DrawText(dc, b.Hotkey, ref rr, c_tffHotkey);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tApi.SetTextColor(dc, b.TextColor != default ? b.TextColor.ToBGR() : (b.IsDisabled ? textColorDisabled : textColor));\n\t\t\t\tif (!b.Text.NE()) {\n\t\t\t\t\tif (b.FontBold) Api.SelectObject(dc, _GetFont(true));\n\t\t\t\t\tr.Width = _met.xTextEnd;\n\t\t\t\t\tvar rr = r; rr.Offset(0, (r.Height - b.textHeight) / 2); //vcenter\n\t\t\t\t\tApi.DrawText(dc, b.Text, ref rr, _TfFlags(b));\n\t\t\t\t\tif (b.FontBold) Api.SelectObject(dc, font);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (b.IsSubmenu) {\n\t\t\t\t\t_DrawControl(_met.sizeSubmenu, 16, b.IsDisabled ? 2 : 1, \"➜\", r2.right - _met.submenu, r.top, _met.submenu, r.Height);\n\t\t\t\t}\n\t\t\t\tif (b.IsChecked) {\n\t\t\t\t\t_DrawControl(_met.sizeCheck, 11, b.checkType == 1 ? (b.IsDisabled ? 2 : 1) : (b.IsDisabled ? 4 : 3), b.checkType == 1 ? \"✔\" : \"●\", r2.left, r.top, Math.Max(_met.check, _met.check2), r.Height);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvoid _DrawControl(SIZE z, int part, int state, string c, int x, int y, int width, int height) {\n\t\t\t\t\tif (_met.theme != default && z != default) {\n\t\t\t\t\t\tRECT r = new(x, r2.top + (r2.Height - z.height) / 2, z.width, z.height); //vcenter\n\t\t\t\t\t\tApi.DrawThemeBackground(_met.theme, dc, part, state, r);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tRECT r = new(x, y, width, height);\n\t\t\t\t\t\tApi.DrawText(dc, c, ref r, TFFlags.CENTER | TFFlags.VCENTER | TFFlags.SINGLELINE | TFFlags.NOCLIP);\n\t\t\t\t\t\t//cannot use DrawFrameControl(DFC_MENU, DFCS_MENUARROW etc), it draws with white background and small when high DPI\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t\n\tvoid _WmNcpaint() {\n\t\tusing var dc = new WindowDC_(Api.GetWindowDC(_w), _w);\n\t\tRECT r = new(0, 0, _size.window.width, _size.window.height);\n\t\tusing var brushBorder = GdiObject_.SysColorBrush(_met.theme, Api.COLOR_BTNSHADOW);\n\t\tusing var brushPadding = GdiObject_.SysColorBrush(_met.theme, Api.COLOR_BTNFACE);\n\t\tfor (int i = 0; i < _size.border; i++) {\n\t\t\tApi.FrameRect(dc, r, i == 0 ? brushBorder : brushPadding);\n\t\t\tr.Inflate(-1, -1);\n\t\t}\n\t}\n\t\n\tinternal void Invalidate_(PMItem k = null) {\n\t\t_ThreadTrap();\n\t\tif (_w.Is0) return;\n\t\tif (k != null) Api.InvalidateRect(_w, _ItemRect(k));\n\t\telse Api.InvalidateRect(_w);\n\t}\n\t\n\tvoid _Invalidate(int i) => Invalidate_(_a[i]);\n\t\n\tvoid _Images() {\n\t\tforeach (var v in _a) {\n\t\t\tv.image2 = _GetImage(v).image;\n\t\t}\n\t}\n\t\n\t//not used\n\t//internal void ChangeImage_(PMItem ti, Bitmap b) {\n\t//\tti.image2 = b;\n\t//\t_Invalidate(ti);\n\t//}\n}\n"
  },
  {
    "path": "Au/GUI/popupMenu/pm types.cs",
    "content": "\nnamespace Au.Types;\n\n/// <summary>\n/// Represents a menu item in <see cref=\"popupMenu\"/>.\n/// </summary>\n/// <remarks>\n/// Most properties cannot be changed while the menu is open. Can be changed <see cref=\"MTItem.Tag\"/>, <see cref=\"MTItem.Tooltip\"/>, <see cref=\"IsChecked\"/> and <see cref=\"IsDisabled\"/>.\n/// </remarks>\npublic class PMItem : MTItem {\n\treadonly popupMenu _m;\n\tinternal byte checkType; //1 checkbox, 2 radio\n\tinternal bool checkDontClose;\n\tinternal bool rawText;\n\tinternal int textHeight;\n\t\n\tinternal PMItem(popupMenu m, bool isDisabled, bool isChecked = false) {\n\t\t_m = m;\n\t\t_isDisabled = isDisabled;\n\t\t_isChecked = isChecked;\n\t\tcheckDontClose = m.CheckDontClose;\n\t\trawText = m.RawText;\n\t}\n\t\n\t/// <summary>Gets item action.</summary>\n\tpublic Action<PMItem> Clicked => base.clicked as Action<PMItem>;\n\t\n\t/// <summary>Gets or sets menu item id.</summary>\n\tpublic int Id { get; set; }\n\t\n\t/// <summary><c>true</c> if is a submenu-item.</summary>\n\tpublic bool IsSubmenu { get; init; }\n\t\n\t/// <summary><c>true</c> if is a separator.</summary>\n\tpublic bool IsSeparator { get; init; }\n\t\n\t/// <summary>\n\t/// Gets or sets disabled state.\n\t/// </summary>\n\tpublic bool IsDisabled {\n\t\tget => _isDisabled || IsSeparator;\n\t\tset {\n\t\t\tif (value != _isDisabled && !IsSeparator) {\n\t\t\t\t_isDisabled = value;\n\t\t\t\t_m.Invalidate_(this);\n\t\t\t}\n\t\t}\n\t}\n\tbool _isDisabled;\n\t\n\t/// <summary>\n\t/// Gets or sets checked state.\n\t/// </summary>\n\t/// <exception cref=\"InvalidOperationException\">The <c>set</c> function throws this exception if the item isn't checkable. Use <see cref=\"popupMenu.AddCheck\"/> or <see cref=\"popupMenu.AddRadio\"/>.</exception>\n\tpublic bool IsChecked {\n\t\tget => _isChecked;\n\t\tset {\n\t\t\tif (checkType == 0) throw new InvalidOperationException();\n\t\t\tif (value != _isChecked) {\n\t\t\t\t_isChecked = value;\n\t\t\t\t_m.Invalidate_(this);\n\t\t\t}\n\t\t}\n\t}\n\tbool _isChecked;\n\t\n\t/// <summary>Gets or sets whether to use bold font.</summary>\n\tpublic bool FontBold { get; set; }\n\t\n\t/// <summary>Gets or sets background color.</summary>\n\tpublic ColorInt BackgroundColor { get; set; }\n\t\n\t/// <summary>\n\t/// Hotkey display text.\n\t/// </summary>\n\tpublic string Hotkey { get; set; }\n\t\n\t/// <summary>\n\t/// Invokes <see cref=\"Clicked\"/> if not <c>null</c>.\n\t/// Handles exceptions if need. Invokes in new thread if need.\n\t/// </summary>\n\tinternal void InvokeAction_() {\n\t\tif (clicked is Action<PMItem> action) {\n\t\t\tif (actionThread) run.thread(() => _Invoke(), background: false); else _Invoke();\n\t\t\tvoid _Invoke() {\n\t\t\t\ttry { action(this); }\n\t\t\t\tcatch (Exception ex) when (!this.actionException) { print.warning(ex); }\n\t\t\t}\n\t\t}\n\t}\n}\n\n/// <summary>\n/// Flags for <see cref=\"popupMenu\"/> <c>ShowX</c> methods.\n/// </summary>\n/// <remarks>\n/// The <c>AlignX</c> flags are for API <ms>TrackPopupMenuEx</ms>.\n/// </remarks>\n[Flags]\npublic enum PMFlags {\n\t/// <summary>Show by the caret (text cursor) position. If not possible, use flag <c>WindowCenter</c> or <c>ScreenCenter</c> or <i>xy</i> or mouse position.</summary>\n\tByCaret = 0x1000000,\n\t\n\t/// <summary>Show in the center of the screen that contains the mouse pointer.</summary>\n\tScreenCenter = 0x2000000,\n\t\n\t/// <summary>Show in the center of the active window.</summary>\n\tWindowCenter = 0x4000000,\n\t\n\t/// <summary>Underline characters preceded by <c>&amp;</c>, regardless of Windows settings. More info: <see cref=\"StringUtil.RemoveUnderlineChar\"/>.</summary>\n\tUnderline = 0x8000000,\n\t\n\t//TPM_ flags\n\t\n\t/// <summary>Horizontally align the menu so that the show position would be in its center.</summary>\n\tAlignCenterH = 0x4,\n\t\n\t/// <summary>Horizontally align the menu so that the show position would be at its right side.</summary>\n\tAlignRight = 0x8,\n\t\n\t/// <summary>Vertically align the menu so that the show position would be in its center.</summary>\n\tAlignCenterV = 0x10,\n\t\n\t/// <summary>Vertically align the menu so that the show position would be at its bottom.</summary>\n\tAlignBottom = 0x20,\n\t\n\t/// <summary>Show at the bottom or top of <i>excludeRect</i>, not at the right/left.</summary>\n\tAlignRectBottomTop = 0x40,\n}\n\n/// <summary>\n/// Used with <see cref=\"popupMenu.KeyboardHook\"/>.\n/// </summary>\npublic enum PMKHook {\n\t/// <summary>Process the key event as usually.</summary>\n\tDefault,\n\t\n\t/// <summary>Close the menu.</summary>\n\tClose,\n\t\n\t/// <summary>Do nothing.</summary>\n\tNone,\n\t\n\t/// <summary>Execute the focused item and close the menu.</summary>\n\tExecuteFocused,\n}\n\n/// <summary>\n/// Used with <see cref=\"popupMenu.Metrics\"/> and <see cref=\"popupMenu.defaultMetrics\"/>.\n/// </summary>\n/// <remarks>\n/// All values are in logical pixels (1 pixel when DPI is 100%).\n/// </remarks>\npublic record class PMMetrics(int ItemPaddingY = 0, int ItemPaddingLeft = 0, int ItemPaddingRight = 0);\n"
  },
  {
    "path": "Au/GUI/popupMenu/popupMenu.cs",
    "content": "//TODO3: winevents EEvent.SYSTEM_MENUSTART, EEvent.SYSTEM_MENUEND, EEvent.SYSTEM_MENUPOPUPSTART, EEvent.SYSTEM_MENUPOPUPEND\n\nnamespace Au;\n\n/// <summary>\n/// Popup menu.\n/// </summary>\n/// <remarks>\n/// Can be used everywhere: in automation scripts, WPF apps, other apps, etc.\n/// Also can be used as a popup list and supports many items with scrollbar.\n/// \n/// Menu item text can include hotkey after <c>'\\t'</c> character and/or tooltip after <c>'|'</c> or <c>'\\0'</c> character. Examples: <c>\"Text\\t Hotkey\"</c>, <c>\"Text|Tooltip\"</c>, <c>\"Text\\t Hotkey\\0 Tooltip\"</c>. Character with prefix <c>&amp;</c> (eg <c>'A'</c> in <c>\"Save &amp;As\"</c>) will be underlined (depends on Windows settings and <see cref=\"PMFlags\"/>) and can be used to select the item with keyboard.\n/// \n/// Keyboard, mouse:\n/// - <c>Enter</c>, <c>Tab</c>, <c>Space</c> - close the menu and execute the focused item. Or show the submenu.\n/// - <c>Esc</c> - close the menu or current submenu.\n/// - <c>Left</c> - close current submenu.\n/// - <c>Right</c> - open submenu.\n/// - <c>Down</c>, <c>Up</c>, <c>PageDown</c>, <c>PageUp</c>, <c>End</c>, <c>Home</c> - focus other item.\n/// - underlined menu item character - close the menu and execute the item. Or show the submenu. See <see cref=\"PMFlags.Underline\"/>.\n/// - <c>Alt</c>, <c>Win</c>, <c>F10</c>, <c>Apps</c>, <c>Back</c> - close menus.\n/// - click outside - close the menu.\n/// - middle click - close the menu.\n/// - right click - show context menu (if used constructor with parameters).\n/// \n/// While a menu is open, it captures many keyboard keys, even when its thread isn't the foreground thread.\n/// \n/// Not thread-safe. All functions must be called in same thread, unless documented otherwise.\n/// </remarks>\n/// <example>\n/// <code><![CDATA[\n/// var m = new popupMenu(\"example\");\n/// m[\"One\"] = o => print.it(o);\n/// m[\"Two\\0Tooltip\", image: icon.stock(StockIcon.DELETE)] = o => { print.it(o); dialog.show(o.ToString()); };\n/// m.Submenu(\"Submenu\", m => {\n/// \tm[\"Three\"] = o => print.it(o);\n/// \tm[\"Four\"] = o => print.it(o);\n/// });\n/// m[\"notepad\"] = o => run.itSafe(folders.System + \"notepad.exe\");\n/// m.Show();\n/// ]]></code>\n/// </example>\npublic unsafe partial class popupMenu : MTBase {\n\treadonly List<PMItem> _a = new();\n\tint _lastId; //to auto-generate item ids\n\tbool _addedNewItems;\n\t(SIZE window, SIZE client, int border) _size;\n\tNativeScrollbar_ _scroll;\n\t(popupMenu child, popupMenu parent, PMItem item, timer timer) _sub;\n\t(POINT p, bool track, bool left, bool right, bool middle) _mouse;\n\tint _iHot = -1;\n\tPMFlags _flags;\n\tPMItem _result;\n\t\n\tstatic popupMenu() {\n\t\tWndUtil.RegisterWindowClass(\"Au.popupMenu\", etc: new() { style = Api.CS_HREDRAW | Api.CS_VREDRAW | Api.CS_DROPSHADOW, mCursor = MCursor.Arrow });\n\t}\n\t\n\t/// <summary>\n\t/// Use this constructor for various context menus of your app.\n\t/// </summary>\n\t/// <remarks>\n\t/// Users cannot right-click a menu item and open/select it in editor.\n\t/// </remarks>\n\tpublic popupMenu() { }\n\t\n\t/// <summary>\n\t/// Use this constructor in scripts.\n\t/// </summary>\n\t/// <param name=\"name\">Menu name. Must be a unique valid filename. Currently not used. Can be <c>null</c>.</param>\n\t/// <param name=\"f_\">[](xref:caller_info)</param>\n\t/// <param name=\"l_\">[](xref:caller_info)</param>\n\t/// <remarks>\n\t/// This overload sets <see cref=\"MTBase.ExtractIconPathFromCode\"/> = <c>true</c>.\n\t/// \n\t/// Users can right-click an item to open/select it in editor, unless <i>f_</i> is explicitly set = <c>null</c>.\n\t/// </remarks>\n\tpublic popupMenu(string name, [CallerFilePath] string f_ = null, [CallerLineNumber] int l_ = 0) : base(name, f_, l_) {\n\t\tExtractIconPathFromCode = true;\n\t}\n\t\n\t#region add\n\t\n\tPMItem _Add(PMItem mi, string text, MTImage image, int l_, string f_, Delegate click = null) {\n\t\t_ThreadTrap();\n\t\t_OpenTrap(\"cannot add items while the menu is open. To add to submenu, use the submenu variable.\");\n\t\tif (!mi.IsSeparator) mi.Set_(this, text, click, image, l_, _sourceFile == null ? null : f_);\n\t\t_a.Add(mi);\n\t\t_addedNewItems = true;\n\t\tLast = mi;\n\t\treturn mi;\n\t}\n\t\n\t/// <summary>\n\t/// Adds menu item with explicitly specified id.\n\t/// </summary>\n\t/// <param name=\"id\">Item id that <see cref=\"Show\"/> will return if clicked this item.</param>\n\t/// <param name=\"text\">Item text. Can include hotkey, tooltip and underlined character, like <c>\"Te&amp;xt\\t Hotkey\\0 Tooltip\"</c>; more info: <see cref=\"popupMenu\"/>.</param>\n\t/// <param name=\"image\">Item image. Read here: <see cref=\"MTBase\"/>.</param>\n\t/// <param name=\"disable\">Disabled state.</param>\n\t/// <param name=\"l_\">[](xref:caller_info)</param>\n\t/// <param name=\"f_\">[](xref:caller_info)</param>\n\tpublic PMItem Add(int id, string text, MTImage image = default, bool disable = false, [CallerLineNumber] int l_ = 0, [CallerFilePath] string f_ = null)\n\t\t=> _Add(new PMItem(this, disable) { Id = _lastId = id }, text, image, l_, f_);\n\t\n\t/// <summary>\n\t/// Adds menu item with auto-generated id.\n\t/// </summary>\n\t/// <param name=\"text\">Item text. Can include hotkey, tooltip and underlined character, like <c>\"Te&amp;xt\\t Hotkey\\0 Tooltip\"</c>; more info: <see cref=\"popupMenu\"/>.</param>\n\t/// <param name=\"image\">Item image. Read here: <see cref=\"MTBase\"/>.</param>\n\t/// <param name=\"disable\">Disabled state.</param>\n\t/// <param name=\"l_\">[](xref:caller_info)</param>\n\t/// <param name=\"f_\">[](xref:caller_info)</param>\n\t/// <remarks>\n\t/// Assigns id = the last specified or auto-generated id + 1. If not using explicitly specified ids, auto-generated ids are 1, 2, 3... Submenu-items, separators and items with action don't auto-generate ids.\n\t/// </remarks>\n\tpublic PMItem Add(string text, MTImage image = default, bool disable = false, [CallerLineNumber] int l_ = 0, [CallerFilePath] string f_ = null)\n\t\t=> _Add(new PMItem(this, disable) { Id = ++_lastId }, text, image, l_, f_);\n\t\n\t/// <summary>\n\t/// Adds menu item with action (callback function) that is executed on click.\n\t/// </summary>\n\t/// <param name=\"text\">Item text. Can include hotkey, tooltip and underlined character, like <c>\"Te&amp;xt\\t Hotkey\\0 Tooltip\"</c>; more info: <see cref=\"popupMenu\"/>.</param>\n\t/// <param name=\"click\">Action executed on click.</param>\n\t/// <param name=\"image\">Item image. Read here: <see cref=\"MTBase\"/>.</param>\n\t/// <param name=\"disable\">Disabled state.</param>\n\t/// <param name=\"l_\">[](xref:caller_info)</param>\n\t/// <param name=\"f_\">[](xref:caller_info)</param>\n\t/// <remarks>\n\t/// This function is the same as the indexer. The difference is, <c>Add</c> returns <see cref=\"PMItem\"/> object of the added item. When using the indexer, to access the item use <see cref=\"Last\"/>. These codes are the same: <c>var v=m.Add(\"text\", o=>{});\"</c> and <c>m[\"text\"]=o=>{}; var v=m.Last;</c>.\n\t/// </remarks>\n\tpublic PMItem Add(string text, Action<PMItem> click, MTImage image = default, bool disable = false, [CallerLineNumber] int l_ = 0, [CallerFilePath] string f_ = null)\n\t\t=> _Add(new PMItem(this, disable), text, image, l_, f_, click);\n\t\n\t/// <summary>\n\t/// Adds menu item with action (callback function) that is executed on click.\n\t/// </summary>\n\t/// <param name=\"text\">Item text. Can include hotkey, tooltip and underlined character, like <c>\"Te&amp;xt\\t Hotkey\\0 Tooltip\"</c>; more info: <see cref=\"popupMenu\"/>.</param>\n\t/// <param name=\"image\">Item image. Read here: <see cref=\"MTBase\"/>.</param>\n\t/// <param name=\"disable\">Disabled state.</param>\n\t/// <param name=\"l_\">[](xref:caller_info)</param>\n\t/// <param name=\"f_\">[](xref:caller_info)</param>\n\t/// <value>Action executed on click. Can be <c>null</c>.</value>\n\t/// <remarks>\n\t/// This function is the same as <see cref=\"Add(string, Action{PMItem}, MTImage, bool, int, string)\"/>. The difference is, <c>Add</c> returns <see cref=\"PMItem\"/> object of the added item. When using the indexer, to access the item use <see cref=\"Last\"/>. These codes are the same: <c>var v=m.Add(\"text\", o=>{});\"</c> and <c>m[\"text\"]=o=>{}; var v=m.Last;</c>.\n\t/// </remarks>\n\tpublic Action<PMItem> this[string text, MTImage image = default, bool disable = false, [CallerLineNumber] int l_ = 0, [CallerFilePath] string f_ = null] {\n\t\tset { Add(text, value, image, disable, l_, f_); }\n\t}\n\t\n\t/// <summary>\n\t/// Adds menu item to be used as a checkbox.\n\t/// </summary>\n\t/// <param name=\"text\">Item text. Can include hotkey, tooltip and underlined character, like <c>\"Te&amp;xt\\t Hotkey\\0 Tooltip\"</c>; more info: <see cref=\"popupMenu\"/>.</param>\n\t/// <param name=\"check\">Checked state.</param>\n\t/// <param name=\"click\">Action executed on click.</param>\n\t/// <param name=\"disable\">Disabled state.</param>\n\t/// <param name=\"image\">Item image. Read here: <see cref=\"MTBase\"/>.</param>\n\t/// <param name=\"l_\">[](xref:caller_info)</param>\n\t/// <param name=\"f_\">[](xref:caller_info)</param>\n\t/// <remarks>\n\t/// When clicked, <see cref=\"PMItem.IsChecked\"/> state is changed.\n\t/// </remarks>\n\tpublic PMItem AddCheck(string text, bool check = false, Action<PMItem> click = null, bool disable = false, MTImage image = default, [CallerLineNumber] int l_ = 0, [CallerFilePath] string f_ = null)\n\t\t=> _Add(new PMItem(this, disable, check) { checkType = 1 }, text, image, l_, f_, click);\n\t\n\t/// <summary>\n\t/// Adds menu item to be used as a radio button in a group of such items.\n\t/// </summary>\n\t/// <param name=\"text\">Item text. Can include hotkey, tooltip and underlined character, like <c>\"Te&amp;xt\\t Hotkey\\0 Tooltip\"</c>; more info: <see cref=\"popupMenu\"/>.</param>\n\t/// <param name=\"check\">Checked state.</param>\n\t/// <param name=\"click\">Action executed on click.</param>\n\t/// <param name=\"disable\">Disabled state.</param>\n\t/// <param name=\"image\">Item image. Read here: <see cref=\"MTBase\"/>.</param>\n\t/// <param name=\"l_\">[](xref:caller_info)</param>\n\t/// <param name=\"f_\">[](xref:caller_info)</param>\n\t/// <remarks>\n\t/// When clicked an unchecked radio item, its <see cref=\"PMItem.IsChecked\"/> state becomes <c>true</c>; <c>IsChecked</c> of other group items become <c>false</c>.\n\t/// </remarks>\n\tpublic PMItem AddRadio(string text, bool check = false, Action<PMItem> click = null, bool disable = false, MTImage image = default, [CallerLineNumber] int l_ = 0, [CallerFilePath] string f_ = null)\n\t\t=> _Add(new PMItem(this, disable, check) { checkType = 2 }, text, image, l_, f_, click);\n\t\n\t/// <summary>\n\t/// Adds menu item that opens a submenu.\n\t/// Used like <c>m.Submenu(\"Example\", m => { /* add submenu items */ });</c>.\n\t/// </summary>\n\t/// <param name=\"text\">Item text. Can include hotkey, tooltip and underlined character, like <c>\"Te&amp;xt\\t Hotkey\\0 Tooltip\"</c>; more info: <see cref=\"popupMenu\"/>.</param>\n\t/// <param name=\"opening\">Action called whenever opening the submenu and should add items to it.</param>\n\t/// <param name=\"image\">Item image. Read here: <see cref=\"MTBase\"/>.</param>\n\t/// <param name=\"disable\">Disabled state.</param>\n\t/// <param name=\"l_\">[](xref:caller_info)</param>\n\t/// <param name=\"f_\">[](xref:caller_info)</param>\n\t/// <remarks>\n\t/// The submenu is other <see cref=\"popupMenu\"/> object. It inherits many properties of this menu; see property documentation.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// m.Submenu(\"Example\", m => {\n\t/// \tm[\"A\"] = o => { print.it(o); };\n\t/// \tm[\"B\"] = o => { print.it(o); };\n\t/// });\n\t/// ]]></code>\n\t/// This code shows dynamically created menu of files in a folder and subfolders. Subfolder files are retrieved when opening the submenu.\n\t/// <code><![CDATA[\n\t/// var m=new popupMenu();\n\t/// _Dir(m, new DirectoryInfo(@\"C:\\\"));\n\t/// m.Show();\n\t/// \n\t/// static void _Dir(popupMenu m, DirectoryInfo dir) {\n\t/// \tforeach (var v in dir.EnumerateFileSystemInfos()) {\n\t/// \t\tif(v.Attributes.Has(FileAttributes.System|FileAttributes.Hidden)) continue;\n\t/// \t\tif(v.Attributes.Has(FileAttributes.Directory)) {\n\t/// \t\t\tm.Submenu(v.Name, m=> _Dir(m, v as DirectoryInfo));\n\t/// \t\t} else {\n\t/// \t\t\tm[v.Name]=o=>print.it(v.FullName);\n\t/// \t\t}\n\t/// \t\tm.Last.File = v.FullName;\n\t/// \t}\n\t/// }\n\t/// ]]></code>\n\t/// </example>\n\tpublic PMItem Submenu(string text, Action<popupMenu> opening, MTImage image = default, bool disable = false, [CallerLineNumber] int l_ = 0, [CallerFilePath] string f_ = null)\n\t\t=> _Add(new PMItem(this, disable) { IsSubmenu = true }, text, image, l_, f_, opening);\n\t\n\t/// <summary>\n\t/// Adds menu item that opens a reusable submenu.\n\t/// </summary>\n\t/// <param name=\"text\">Item text. Can include hotkey, tooltip and underlined character, like <c>\"Te&amp;xt\\t Hotkey\\0 Tooltip\"</c>; more info: <see cref=\"popupMenu\"/>.</param>\n\t/// <param name=\"opening\">Func called whenever opening the submenu and should return the submenu object. Can return <c>null</c>.</param>\n\t/// <param name=\"image\">Item image. Read here: <see cref=\"MTBase\"/>.</param>\n\t/// <param name=\"disable\">Disabled state.</param>\n\t/// <param name=\"l_\">[](xref:caller_info)</param>\n\t/// <param name=\"f_\">[](xref:caller_info)</param>\n\t/// <remarks>\n\t/// The caller creates the submenu (creates the <see cref=\"popupMenu\"/> object and adds items) and can reuse it many times. Other overload does not allow to create <see cref=\"popupMenu\"/> and reuse same object.\n\t/// The submenu does not inherit properties of this menu.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// var m2 = new popupMenu(); m2.AddCheck(\"C1\"); m2.AddCheck(\"C2\");\n\t/// m.Submenu(\"Submenu\", () => m2);\n\t/// ]]></code>\n\t/// </example>\n\tpublic PMItem Submenu(string text, Func<popupMenu> opening, MTImage image = default, bool disable = false, [CallerLineNumber] int l_ = 0, [CallerFilePath] string f_ = null)\n\t\t=> _Add(new PMItem(this, disable) { IsSubmenu = true }, text, image, l_, f_, opening);\n\t\n\t/// <summary>\n\t/// Adds separator.\n\t/// </summary>\n\tpublic void Separator()\n\t\t=> _Add(new PMItem(this, isDisabled: true) { IsSeparator = true }, null, default, 0, null);\n\t\n\t/// <summary>\n\t/// Gets the last added menu item.\n\t/// </summary>\n\tpublic PMItem Last { get; private set; }\n\t\n\t/// <summary>\n\t/// Gets added items, except separators and items in submenus.\n\t/// </summary>\n\t/// <remarks>\n\t/// Allows to set properties of multiple items in single place instead of after each \"add item\" code line.\n\t/// \n\t/// Does not get items in submenus. Submenus are separate <see cref=\"popupMenu\"/> objects and you can use their <c>Items</c> property.\n\t/// </remarks>\n\tpublic IEnumerable<PMItem> Items {\n\t\tget {\n\t\t\t_ThreadTrap();\n\t\t\tforeach (var v in _a) {\n\t\t\t\tif (!v.IsSeparator) yield return v;\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Gets added items and separators, except items in submenus.\n\t/// </summary>\n\tpublic IReadOnlyList<PMItem> ItemsAndSeparators {\n\t\tget {\n\t\t\t_ThreadTrap();\n\t\t\treturn _a;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Don't use: <c>&amp;</c> character for keyboard shortcut; tab character for hotkey; <c>|</c> character for tooltip (but use <c>\\0</c>).\n\t/// This property is applied to items added afterwards; submenus inherit it.\n\t/// </summary>\n\tpublic bool RawText { get; set; }\n\t\n\t/// <summary>\n\t/// Adds enum members as checkbox-items (if it's a <c>[Flags]</c> enum) or radio-items.\n\t/// </summary>\n\t/// <returns>Object for getting result later. See <see cref=\"EnumUI{TEnum}.Result\"/>.</returns>\n\t/// <param name=\"init\">Initial value.</param>\n\t/// <param name=\"items\">Enum members and their text/tooltip. Optional. Text can be: <c>null</c>, <c>\"text\"</c>, <c>\"text|tooltip\"</c>, <c>\"|tooltip\"</c>.</param>\n\t[EditorBrowsable(EditorBrowsableState.Never)] //obsolete. Too simple. Added EnumUI examples in cookbook.\n\tpublic EnumUI<TEnum> AddEnum<TEnum>(TEnum init = default, (TEnum value, string text)[] items = null) where TEnum : unmanaged, Enum {\n\t\treturn new EnumUI<TEnum>(this, init, items);\n\t}\n\t\n\t#endregion\n\t\n\t#region show, close\n\t\n\t/// <summary>\n\t/// Shows the menu and waits until closed.\n\t/// </summary>\n\t/// <returns>\n\t/// id of the selected item, or 0 if canceled.\n\t/// See also: <see cref=\"Result\"/>.\n\t/// </returns>\n\t/// <param name=\"flags\"></param>\n\t/// <param name=\"xy\">Menu position in screen. If <c>null</c> (default), uses mouse position by default. It depends on <i>flags</i>.</param>\n\t/// <param name=\"excludeRect\">The menu should not overlap this rectangle in screen.</param>\n\t/// <param name=\"owner\">Owner window. The menu will be automatically closed when destroying its owner window.</param>\n\t/// <exception cref=\"InvalidOperationException\">The menu is open or is submenu.</exception>\n\tpublic int Show(PMFlags flags = 0, POINT? xy = null, RECT? excludeRect = null, AnyWnd owner = default) {\n\t\t_ThreadTrap();\n\t\t_OpenTrap(\"this menu is already open\");\n\t\tif (_sub.parent != null) throw new InvalidOperationException(\"this is a submenu\");\n\t\tif (_a.Count == 0) return 0;\n\t\t\n\t\tApi.ReleaseCapture(); //winforms still capturing on MouseClick etc, and menu would be like disabled\n\t\t\n\t\tif (!flags.Has(PMFlags.Underline)) if (0 != Api.SystemParametersInfo(Api.SPI_GETKEYBOARDCUES, 0)) flags |= PMFlags.Underline;\n\t\t\n\t\t_Show(flags, xy, excludeRect, owner.Hwnd);\n\t\t\n\t\tint R = 0;\n\t\t\n\t\tWindowsHook hKey = null, hMouse = null;\n\t\ttimer timer = null;\n\t\ttry {\n\t\t\tvar wFore = wnd.active;\n\t\t\tbool foreground = wFore.IsOfThisThread;\n\t\t\t\n\t\t\t//to close with mouse use timer. Mouse hook may not work because of UAC.\n\t\t\tint mouseState = _GetMouseState();\n\t\t\ttimer = new(t => { //close if mouse clicked a non-menu window or if activated another window\n\t\t\t\tint ms = _GetMouseState();\n\t\t\t\tbool clicked = ms != mouseState;\n\t\t\t\tmouseState = ms;\n\t\t\t\tif (clicked) _CloseIfClickedNotMenu(wnd.fromMouse(WXYFlags.Raw));\n\t\t\t\telse if (wnd.active != wFore) Close();\n\t\t\t});\n\t\t\ttimer.Every(30);\n\t\t\t\n\t\t\tstatic int _GetMouseState() =>\n\t\t\t\t(keys.gui.getKeyState(KKey.MouseLeft) & 0x1)\n\t\t\t\t| ((keys.gui.getKeyState(KKey.MouseRight) & 0x1) << 1)\n\t\t\t\t| ((keys.gui.getKeyState(KKey.MouseMiddle) & 0x1) << 2)\n\t\t\t\t;\n\t\t\t//note: use only toggled state. Pressed state may change to \"no\" when mouse is already in a non-menu window although was in a menu window at the time of the mouse event.\n\t\t\t//note: in some cases toggled state may not change when clicked. Eg when clicked a taskbar button that activates another window. Then helps if (wnd.active!=wFore) Close();.\n\t\t\t\n\t\t\tvoid _CloseIfClickedNotMenu(wnd w) {\n\t\t\t\t//if(!w.Get.Owners(andThisWindow: true).Contains(_w)) Close(); //no, user may want nested root menus, although it is rare\n\t\t\t\tif (!_IsMenuWindow(w)) Close();\n\t\t\t}\n\t\t\t\n\t\t\tbool _IsMenuWindow(wnd w) => w == _w || w.ClassNameIs(\"Au.popupMenu\");\n\t\t\t\n\t\t\tif (!foreground) {\n\t\t\t\t//never mind: hooks don't work if the active window has higher UAC IL. Then use timer and mouse/Esc toggle state.\n\t\t\t\thKey = WindowsHook.Keyboard(h => {\n\t\t\t\t\tvar k = h.Key;\n\t\t\t\t\tif (KeyboardHook != null && !h.IsUp) {\n\t\t\t\t\t\tswitch (KeyboardHook(this, h)) {\n\t\t\t\t\t\tcase PMKHook.None: return;\n\t\t\t\t\t\tcase PMKHook.Close:\n\t\t\t\t\t\t\t_w.Post(Api.WM_CLOSE);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\tcase PMKHook.ExecuteFocused when FocusedItem != null:\n\t\t\t\t\t\t\t_w.Post(Api.WM_USER + 50, _a.IndexOf(FocusedItem));\n\t\t\t\t\t\t\th.BlockEvent();\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n#if true\n\t\t\t\t\tif (!_IsCancelKey(k)) {\n\t\t\t\t\t\tif (_IsPassKey(k)) return;\n\t\t\t\t\t\th.BlockEvent();\n\t\t\t\t\t}\n\t\t\t\t\tif (!h.IsUp) _w.Post(Api.WM_KEYDOWN, (int)k, 0); //else _w.Post(Api.WM_KEYUP, (int)k, 0xC0000001);\n#else //unfinished. The idea was to call TranslateMessage, and if then PeekMessage gets wm_char...\n\t\t\t\t\tif(_IsCancelKey(k)) {\n\t\t\t\t\t\t_w.Post(Api.WM_CLOSE);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (!h.IsUp) {\n\t\t\t\t\t\tvar ok=Api.TranslateMessage(new() { hwnd = _w, message = Api.WM_KEYDOWN, wParam = (int)k });\n\t\t\t\t\t\t//print.it(ok);\n\t\t\t\t\t\tif(Api.PeekMessage(out var v, _w, Api.WM_CHAR, Api.WM_CHAR, Api.PM_NOREMOVE)) print.it(\"peek\", v);\n\t\t\t\t\t}\n#endif\n\t\t\t\t});\n\t\t\t\t\n\t\t\t\t//If the active app is showing a menu, it captures the mouse.\n\t\t\t\t//\tIf this menu is there, a click goes to that app instead of this menu.\n\t\t\t\t//\tWorkaround: mouse hook. We cannot SetCapture in this background thread.\n\t\t\t\tif (_IsCapturingMouse()) {\n\t\t\t\t\thMouse = WindowsHook.Mouse(h => {\n\t\t\t\t\t\tif (h.IsInjected) return;\n\t\t\t\t\t\tif (!_IsCapturingMouse()) {\n\t\t\t\t\t\t\th.hook.Unhook();\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t//tested: mouse move and wheel works without this.\n\t\t\t\t\t\tif (h.IsButton && h.Button is MButton.Left or MButton.Right or MButton.Middle) {\n\t\t\t\t\t\t\tvar p = mouse.xy;\n\t\t\t\t\t\t\tvar w = wnd.fromXY(p, WXYFlags.Raw);\n\t\t\t\t\t\t\tif (!_IsMenuWindow(w)) return;\n\t\t\t\t\t\t\th.BlockEvent();\n\t\t\t\t\t\t\tint m = h.Button switch { MButton.Left => Api.WM_LBUTTONDOWN, MButton.Right => Api.WM_RBUTTONDOWN, _ => Api.WM_MBUTTONDOWN };\n\t\t\t\t\t\t\tif (h.IsButtonUp) m++;\n\t\t\t\t\t\t\tw.MapScreenToClient(ref p);\n\t\t\t\t\t\t\tw.Post(m, 0, Math2.MakeLparam(p));\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tstatic bool _IsCapturingMouse() => miscInfo.getGUIThreadInfo(out var g) && !g.hwndCapture.Is0;\n\t\t\t}\n\t\t\t\n\t\t\t//var pmo = new PrintMsgOptions(Api.WM_TIMER, Api.WM_MOUSEMOVE, Api.WM_NCMOUSEMOVE, Api.WM_PAINT, 0x138a /*SC_WORK_IDLE*/, Api.WM_USER, int.MinValue) { WindowProperties = true };\n\t\t\t//print.it(\"in\");\n\t\t\t_MessageLoop();\n\t\t\t//print.it(\"out\");\n\t\t\tvoid _MessageLoop() {\n\t\t\t\tdo {\n\t\t\t\t\tfor (; ; ) {\n\t\t\t\t\t\tif (_w.Is0) return;\n\t\t\t\t\t\tif (!Api.PeekMessage(out var m, default, 0, 0, Api.PM_NOREMOVE)) break;\n\t\t\t\t\t\tif (m.message == Api.WM_QUIT) return; //let outer loop get the message (tested)\n\t\t\t\t\t\tbool handled = false;\n\t\t\t\t\t\tif (m.message is Api.WM_LBUTTONDOWN or Api.WM_RBUTTONDOWN or Api.WM_MBUTTONDOWN or Api.WM_NCLBUTTONDOWN or Api.WM_NCRBUTTONDOWN or Api.WM_NCMBUTTONDOWN) {\n\t\t\t\t\t\t\t_CloseIfClickedNotMenu(m.hwnd);\n\t\t\t\t\t\t\tif (_w.Is0) return; //let outer loop get the message\n\t\t\t\t\t\t} else if (m.message is Api.WM_KEYDOWN or Api.WM_SYSKEYDOWN) {\n\t\t\t\t\t\t\thandled = _WmKeydown(m);\n\t\t\t\t\t\t\tif (!handled && _w.Is0) return; //let outer loop get the message. Used for keys that close the menu but must be passed to the app, eg Alt. If Esc, handled is true.\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!Api.PeekMessage(out m)) break;\n\t\t\t\t\t\tif (handled) continue;\n\t\t\t\t\t\t//WndUtil.PrintMsg(m, pmo);\n\t\t\t\t\t\tif (m.message == Api.WM_CHAR) {\n\t\t\t\t\t\t\t_TopMenu()._WmChar((char)m.wParam);\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tApi.TranslateMessage(m);\n\t\t\t\t\t\tApi.DispatchMessage(m);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\twhile (Api.WaitMessage());\n\t\t\t\t//why this strange loop?\n\t\t\t\t//\tIf the click or keydown closed the menu and wants to pass the message to the app, before passing it need to exit the loop, else bad things may happen.\n\t\t\t\t//\tOr could use GetMessage + PostMessage, but it is probably more dirty; need to repost all queued messages, to avoid eg lbuttondown after lbuttonup.\n\t\t\t}\n\t\t}\n\t\tfinally {\n\t\t\thKey?.Dispose();\n\t\t\thMouse?.Dispose();\n\t\t\ttimer?.Stop();\n\t\t\tif (!_w.Is0) Api.DestroyWindow(_w);\n\t\t}\n\t\t\n\t\tvar b = _result;\n\t\tif (b != null) {\n\t\t\tb.InvokeAction_();\n\t\t\tR = b.Id;\n\t\t}\n\t\t\n\t\treturn R;\n\t}\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if the menu window is open.\n\t/// </summary>\n\tpublic bool IsOpen => !_w.Is0;\n\t\n\t/// <summary>\n\t/// After closing the menu gets the selected item, or <c>null</c> if canceled.\n\t/// </summary>\n\tpublic PMItem Result => _result;\n\t\n\tvoid _OpenTrap(string error = null) {\n\t\tif (IsOpen) throw new InvalidOperationException(error);\n\t}\n\t\n\tvoid _ShowSubmenu(PMItem b, bool focusFirst = false, popupMenu m = null) {\n\t\tif (b == _sub.item) return;\n\t\t_sub.child?.Close();\n\t\t\n\t\tbool contextMenu = m != null;\n\t\tif (!contextMenu) {\n\t\t\tif (b.clicked == null) return;\n\t\t\tif (b.clicked is Action<popupMenu> menu) {\n\t\t\t\tm = b.sourceFile == null ? new() : new popupMenu(null, b.sourceFile, b.sourceLine);\n\t\t\t\tbase._CopyProps(m);\n\t\t\t\tm.CheckDontClose = CheckDontClose;\n\t\t\t\tm.RawText = RawText;\n\t\t\t\tmenu(m);\n\t\t\t} else if (b.clicked is Func<popupMenu> func) {\n\t\t\t\tm = func();\n\t\t\t}\n\t\t\tif (m == null || m._a.Count == 0) return;\n\t\t}\n\t\t\n\t\t_sub.child = m;\n\t\t_sub.item = b;\n\t\tm._sub.parent = this;\n\t\tif (focusFirst && m._iHot < 0) m._iHot = 0;\n\t\t\n\t\tif (contextMenu) {\n\t\t\tm._Show(0, null, null, _w);\n\t\t} else {\n\t\t\tvar r = _ItemRect(b, inScreen: true);\n\t\t\tr.Inflate(-_size.border, _size.border);\n\t\t\t\n\t\t\tm._Show(_flags & ~(PMFlags)0xffffff, new(r.right, r.top), r, _w);\n\t\t}\n\t}\n\t\n\tvoid _Show(PMFlags flags, POINT? xy, RECT? excludeRect, wnd owner) {\n\t\tif (_a.Count == 0) return;\n\t\t\n\t\t_result = null;\n\t\t_flags = flags;\n\t\t\n\t\tRECT cr = default;\n\t\tbool byCaret = flags.Has(PMFlags.ByCaret);\n\t\tif (byCaret) {\n\t\t\tif (caretRectFunc is {  } crf) {\n\t\t\t\tvar r1 = crf();\n\t\t\t\tif (r1 is null) byCaret = false; else cr = r1.Value;\n\t\t\t} else byCaret = miscInfo.getTextCursorRect(out cr, out _);\n\t\t}\n\t\tPOINT p = byCaret ? new(cr.left, cr.bottom) : xy ?? mouse.xy;\n\t\t\n\t\tvar scrn = screen.of(p);\n\t\t_dpi = scrn.Dpi;\n\t\tvar rs = scrn.WorkArea;\n\t\trs.Inflate(-8, -8);\n\t\t\n\t\tif (byCaret) {\n\t\t\tif (excludeRect == null) {\n\t\t\t\tcr.Inflate(50, 1);\n\t\t\t\texcludeRect = cr;\n\t\t\t\tflags |= PMFlags.AlignRectBottomTop;\n\t\t\t}\n\t\t} else {\n\t\t\tif (flags.Has(PMFlags.WindowCenter) && wnd.active is wnd wa && wa.GetRect(out var rw)) {\n\t\t\t\tp = new(rw.CenterX, rw.CenterY);\n\t\t\t\tflags |= PMFlags.AlignCenterH | PMFlags.AlignCenterV;\n\t\t\t} else if (flags.Has(PMFlags.ScreenCenter)) {\n\t\t\t\tp = new(rs.CenterX, rs.CenterY);\n\t\t\t\tflags |= PMFlags.AlignCenterH | PMFlags.AlignCenterV;\n\t\t\t} else if (excludeRect == null && !flags.HasAny(PMFlags.AlignCenterH | PMFlags.AlignCenterV)) {\n\t\t\t\texcludeRect = new(p.x, p.y, 1, 1);\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (_addedNewItems) {\n\t\t\t_addedNewItems = false;\n\t\t\t_Images();\n\t\t}\n\t\t\n\t\t_scroll = new(true, i => _a[i].rect.top, i => _a[i].rect.bottom);\n\t\t\n\t\tWS style = WS.POPUP | WS.DLGFRAME; //3-pixel frame\n\t\tWSE estyle = WSE.TOOLWINDOW | WSE.NOACTIVATE | WSE.TOPMOST;\n\t\tSIZE z = _Measure(rs.Width * 19 / 20);\n\t\t\n\t\tbool needScroll = z.height > rs.Height;\n\t\tif (needScroll) {\n\t\t\tz.height = rs.Height;\n\t\t\tstyle |= WS.VSCROLL;\n\t\t}\n\t\t\n\t\tif (byCaret && !flags.HasAny(PMFlags.AlignRight | PMFlags.AlignCenterH))\n\t\t\tp.x = Math.Max(p.x - _met.image - _met.check - _met.paddingLeft - _met.textPaddingX - 3, rs.left);\n\t\t\n\t\tRECT r = new(0, 0, z.width, z.height);\n\t\tDpi.AdjustWindowRectEx(_dpi, ref r, style, estyle);\n\t\t_size.window = r.Size; _size.client = z; _size.border = -r.top;\n\t\tApi.CalculatePopupWindowPosition(p, r.Size, (uint)flags & 0xffffff, excludeRect.GetValueOrDefault(), out r);\n\t\t\n\t\tif (r.bottom > rs.bottom && r.top > rs.top - 4) r.Move(r.left, r.top - 4); //let the bottom edge not touch the bottom edge of the screen\n\t\t\n\t\t_w = WndUtil.CreateWindow(_WndProc, true, \"Au.popupMenu\", null, style, estyle, r.left, r.top, r.Width, r.Height, owner);\n\t\t_SetScrollbar(needScroll);\n\t\t\n\t\t_w.ShowL(true);\n\t\t\n\t\t_mouse.p = _w.MouseClientXY;\n\t}\n\t\n\t/// <summary>\n\t/// Closes the menu and its submenus.\n\t/// </summary>\n\t/// <param name=\"ancestorsToo\">If this is a submenu, close the root menu with all submenus.</param>\n\t/// <remarks>\n\t/// Can be called from any thread.\n\t/// Does nothing if not open.\n\t/// </remarks>\n\tpublic void Close(bool ancestorsToo = false) {\n\t\tif (!IsOpen) return;\n\t\tif (_IsOtherThread) {\n\t\t\t_w.Post(Api.WM_CLOSE, ancestorsToo ? 1 : 0);\n\t\t} else {\n\t\t\tvar w = _w;\n\t\t\tif (ancestorsToo) {\n\t\t\t\tfor (var pm = _sub.parent; pm != null; pm = pm._sub.parent) {\n\t\t\t\t\tw = pm._w;\n\t\t\t\t\tpm._result = _result;\n\t\t\t\t}\n\t\t\t}\n\t\t\tApi.DestroyWindow(w);\n\t\t}\n\t}\n\t\n\tprivate protected override void _WmNcdestroy() {\n\t\t//print.it(\"destroy\", _name);\n\t\t_sub.timer?.Stop();\n\t\tvar pa = _sub.parent;\n\t\tif (pa != null) {\n\t\t\tpa._sub.child = null;\n\t\t\tpa._sub.item = null;\n\t\t} else {\n\t\t\t_w.Post(0);\n\t\t}\n\t\t_met?.Dispose(); _met = null;\n\t\t_font?.Dispose(); _font = null;\n\t\t_fontBold?.Dispose(); _fontBold = null;\n\t\t_scroll = null;\n\t\t_sub = default;\n\t\t_size = default;\n\t\t_mouse = default;\n\t\t_iHot = -1;\n\t\tbase._WmNcdestroy();\n\t}\n\t\n\t#endregion\n\t\n\tnint _WndProc(wnd w, int msg, nint wParam, nint lParam) {\n\t\t//var pmo = new PrintMsgOptions(Api.WM_NCHITTEST, Api.WM_SETCURSOR, Api.WM_MOUSEMOVE, Api.WM_NCMOUSEMOVE, 0x10c1);\n\t\t//if (WndUtil.PrintMsg(out string s, w, msg, wParam, lParam, pmo)) print.it(\"<><c green>\" + s + \"<>\");\n\t\t//WndUtil.PrintMsg(w, msg, wParam, lParam);\n\t\t\n\t\tif (_scroll.WndProc(w, msg, wParam, lParam)) return default;\n\t\t\n\t\tswitch (msg) {\n\t\tcase Api.WM_NCCREATE:\n\t\t\t_WmNccreate(w);\n\t\t\tbreak;\n\t\tcase Api.WM_NCDESTROY:\n\t\t\t_WmNcdestroy();\n\t\t\tbreak;\n\t\tcase Api.WM_CLOSE:\n\t\t\tClose(ancestorsToo: 0 != (wParam & 1));\n\t\t\treturn default;\n\t\t//case Api.WM_THEMECHANGED: //don't need for a menu window\n\t\t//\t_z?.Dispose();\n\t\t//\t_z = new _Metrics(this);\n\t\t//\tApi.InvalidateRect(w);\n\t\t//\tbreak;\n\t\tcase Api.WM_ERASEBKGND:\n\t\t\treturn default;\n\t\tcase Api.WM_PAINT:\n\t\t\tusing (var bp = new BufferedPaint(w, true)) _Render(bp.DC, bp.UpdateRect);\n\t\t\treturn default;\n\t\tcase Api.WM_MOUSEACTIVATE:\n\t\t\treturn Api.MA_NOACTIVATE;\n\t\tcase Api.WM_MOUSEMOVE:\n\t\t\t_WmMousemove(lParam, fake: false);\n\t\t\treturn default;\n\t\tcase Api.WM_MOUSELEAVE:\n\t\t\t_WmMouseleave();\n\t\t\treturn default;\n\t\tcase >= Api.WM_LBUTTONDOWN and <= Api.WM_MBUTTONUP:\n\t\t\t_WmMousebutton(msg, lParam);\n\t\t\treturn default;\n\t\tcase Api.WM_GETOBJECT:\n\t\t\tif (_WmGetobject(wParam, lParam, out var r1)) return r1;\n\t\t\tbreak;\n\t\tcase Api.WM_USER + 50: //posted by acc dodefaultaction or PMKHook.ExecuteFocused\n\t\t\tif (IsOpen) {\n\t\t\t\tint i = (int)wParam;\n\t\t\t\tif ((uint)i < _a.Count) _Click(i);\n\t\t\t}\n\t\t\treturn default;\n\t\t}\n\t\t\n\t\tvar R = Api.DefWindowProc(w, msg, wParam, lParam);\n\t\t\n\t\tswitch (msg) {\n\t\tcase Api.WM_NCPAINT:\n\t\t\t_WmNcpaint();\n\t\t\tbreak;\n\t\t}\n\t\t\n\t\treturn R;\n\t}\n\t\n\tvoid _SetScrollbar(bool needScroll) {\n\t\tif (needScroll) {\n\t\t\t_scroll.SetRange(_a.Count);\n\t\t\t\n\t\t\t_scroll.PosChanged += (sb, part) => {\n\t\t\t\t_sub.child?.Close();\n\t\t\t\t\n\t\t\t\tint pos = _scroll.Pos;\n\t\t\t\tApi.InvalidateRect(_w);\n\t\t\t\t\n\t\t\t\tif (part <= -2) { //if mouse wheel, update hot item, submenu, tooltip\n\t\t\t\t\tvar p = _w.MouseClientXY;\n\t\t\t\t\tif (_w.ClientRect.Contains(p)) _WmMousemove(Math2.MakeLparam(p), fake: true);\n\t\t\t\t}\n\t\t\t};\n\t\t\t_scroll.Visible = true;\n\t\t} else {\n\t\t\t_scroll.NItems = _a.Count;\n\t\t}\n\t}\n\t\n\tint _HitTest(POINT p, bool failIfDisabled = false) {\n\t\tp.y += _scroll.Offset;\n\t\tfor (int i = 0; i < _a.Count; i++) {\n\t\t\tif (_a[i].rect.Contains(p)) return (failIfDisabled && _a[i].IsDisabled) ? -1 : i;\n\t\t}\n\t\treturn -1;\n\t}\n\t\n\tRECT _ItemRect(PMItem k, bool inScreen = false) {\n\t\tvar r = k.rect;\n\t\tr.Offset(0, -_scroll.Offset);\n\t\tif (inScreen) _w.MapClientToScreen(ref r);\n\t\treturn r;\n\t}\n\t\n\tvoid _WmMousemove(nint lParam, bool fake) {\n\t\tvar p = Math2.NintToPOINT(lParam);\n\t\t\n\t\t//prevent selecting item when mouse position does not change. It would interfere with keyboard navigation.\n\t\tif (!fake && p == _mouse.p) return; _mouse.p = p;\n\t\t\n\t\tint i = _HitTest(p, failIfDisabled: true);\n\t\tif (i != _iHot) {\n\t\t\t_SetHotItem(i);\n\t\t\tif (i >= 0) {\n\t\t\t\tvar b = _a[i];\n\t\t\t\tint submenuDelay = _SubmenuTimer(b.IsSubmenu ? b : null);\n\t\t\t\t_SetTooltip(b, _ItemRect(b), lParam, submenuDelay);\n\t\t\t} else {\n\t\t\t\t_HideTooltip();\n\t\t\t\t_SubmenuTimer();\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (_iHot >= 0 != _mouse.track) _mouse.track = Api.TrackMouseLeave(_w, _iHot >= 0) && _iHot >= 0;\n\t\t\n\t\t_sub.parent?._SubmenuMouseMove();\n\t}\n\t\n\tint _SubmenuTimer(PMItem item = null) {\n\t\tif (item == null && _sub.child == null) return 0;\n\t\t\n\t\t_sub.timer ??= new(t => {\n\t\t\tif (t.Tag is PMItem mi) {\n\t\t\t\tif (FocusedItem == mi) _ShowSubmenu(mi);\n\t\t\t} else if (_sub.child != null && !_sub.child._w.Rect.Contains(mouse.xy)) {\n\t\t\t\t_sub.child.Close();\n\t\t\t}\n\t\t});\n\t\tint R = Api.SystemParametersInfo(Api.SPI_GETMENUSHOWDELAY, 400);\n\t\t_sub.timer.Tag = item;\n\t\t_sub.timer.After(R);\n\t\treturn R;\n\t}\n\t\n\tvoid _SubmenuMouseMove() {\n\t\tif (FocusedItem != _sub.item) {\n\t\t\t_SetHotItem(_a.IndexOf(_sub.item));\n\t\t\t_sub.timer.Stop();\n\t\t}\n\t}\n\t\n\tvoid _WmMouseleave() {\n\t\t_mouse.track = false;\n\t\tif (_iHot < 0) return;\n\t\tif (_sub.child?._w.Rect.Contains(mouse.xy) ?? false) return;\n\t\t_SetHotItem(-1);\n\t\t_SubmenuTimer();\n\t}\n\t\n\tvoid _WmMousebutton(int msg, nint lParam) {\n\t\tswitch (msg) {\n\t\tcase Api.WM_LBUTTONDOWN: _mouse.left = true; return;\n\t\tcase Api.WM_LBUTTONUP: if (!_mouse.left) return; _mouse.left = false; break;\n\t\tcase Api.WM_RBUTTONDOWN: _mouse.right = true; return;\n\t\tcase Api.WM_RBUTTONUP: if (!_mouse.right) return; _mouse.right = false; break;\n\t\tcase Api.WM_MBUTTONDOWN: _mouse.middle = true; return;\n\t\tcase Api.WM_MBUTTONUP: if (_mouse.middle) Close(ancestorsToo: true); return;\n\t\tdefault: return;\n\t\t}\n\t\tvar p = Math2.NintToPOINT(lParam);\n\t\tint i = _HitTest(p);\n\t\tif (i < 0) return;\n\t\tif (msg == Api.WM_LBUTTONUP) _Click(i);\n\t\telse _ContextMenu(_a[i]);\n\t}\n\t\n\tvoid _Click(int i, bool keyboard = false) {\n\t\tvar b = _a[i];\n\t\tif (b.IsDisabled) return;\n\t\t\n\t\tif (b.checkType > 0) {\n\t\t\tif (b.checkType == 1) {\n\t\t\t\tb.IsChecked ^= true;\n\t\t\t} else if (!b.IsChecked) {\n\t\t\t\tfor (int j = i; --j >= 0 && _Uncheck(j);) { }\n\t\t\t\tfor (int j = i; ++j < _a.Count && _Uncheck(j);) { }\n\t\t\t\tb.IsChecked = true;\n\t\t\t\t\n\t\t\t\tbool _Uncheck(int j) {\n\t\t\t\t\tvar v = _a[j];\n\t\t\t\t\tif (v.checkType != 2) return false;\n\t\t\t\t\tv.IsChecked = false;\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (b.checkDontClose) {\n\t\t\t\tb.InvokeAction_();\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (b.IsSubmenu) {\n\t\t\tif (keyboard) _SetHotItem(i, ensureVisible: true);\n\t\t\t_ShowSubmenu(b, focusFirst: keyboard);\n\t\t} else {\n\t\t\t_result = b;\n\t\t\tClose(ancestorsToo: true);\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Don't close menu when clicked a checkbox or radio item.\n\t/// This property is applied to items added afterwards; submenus inherit it.\n\t/// </summary>\n\tpublic bool CheckDontClose { get; set; }\n\t\n\tvoid _ContextMenu(PMItem b) {\n\t\tif (b.IsSeparator) return;\n\t\tvar (canEdit, canGo, goText) = MTItem.CanEditOrGoToFile_(b.sourceFile, b);\n\t\tif (canEdit || canGo) {\n\t\t\tvar m = new popupMenu();\n\t\t\tif (canEdit) m[\"Edit menu item\"] = _ => ScriptEditor.Open(b.sourceFile, b.sourceLine);\n\t\t\tif (canGo) m[goText] = _ => b.GoToFile_();\n\t\t\t_ShowSubmenu(b, m: m);\n\t\t}\n\t}\n\t\n\tpopupMenu _TopMenu() {\n\t\tvar m = this; while (m._sub.child != null) m = m._sub.child;\n\t\treturn m;\n\t}\n\t\n\tbool _TopHot(out (popupMenu m, int i, PMItem b) r) {\n\t\tvar m = this; while (m._sub.child != null) m = m._sub.child;\n\t\tint i = m._iHot;\n\t\tr = (m, i, i < 0 ? null : m._a[i]);\n\t\treturn i >= 0;\n\t}\n\t\n\t/// <summary>\n\t/// Gets or sets the focused menu item.\n\t/// </summary>\n\t/// <remarks>\n\t/// The focused item visually shows the menu item that would be executed if clicked or pressed <c>Enter</c>, <c>Tab</c> or <c>Space</c> key. It changes when the user moves the mouse or presses navigation keys (arrows, <c>End</c>, <c>Home</c>, <c>PageDown</c>, <c>PageUp</c>).\n\t/// This property can be set before showing the menu or when it is open.\n\t/// </remarks>\n\tpublic PMItem FocusedItem {\n\t\tget => _iHot >= 0 ? _a[_iHot] : null;\n\t\tset {\n\t\t\t_ThreadTrap();\n\t\t\tint i = -1;\n\t\t\tif (value != null) {\n\t\t\t\ti = _a.IndexOf(value);\n\t\t\t\tif (i < 0) throw new ArgumentException();\n\t\t\t}\n\t\t\tif (_w.Is0) {\n\t\t\t\t_iHot = i;\n\t\t\t} else {\n\t\t\t\t_SetHotItem(i, ensureVisible: true);\n\t\t\t}\n\t\t}\n\t}\n\t\n\tvoid _SetHotItem(int i, bool ensureVisible = false) {\n\t\tif (i != _iHot) {\n\t\t\tif (_iHot >= 0) _Invalidate(_iHot);\n\t\t\tif ((_iHot = i) >= 0) _Invalidate(_iHot);\n\t\t}\n\t\tif (ensureVisible && i >= 0) {\n\t\t\tint pos = _scroll.Pos, y = _scroll.Offset, hei = _size.client.height;\n\t\t\tvar r = _a[i].rect;\n\t\t\tif (r.top < y) {\n\t\t\t\twhile (pos > 0 && r.top < y) y -= _a[--pos].rect.Height;\n\t\t\t} else if (r.bottom > y + hei) {\n\t\t\t\twhile (pos < _scroll.Max && r.bottom > y + hei) y += _a[++pos].rect.Height;\n\t\t\t} else return;\n\t\t\t_scroll.Pos = pos;\n\t\t}\n\t}\n\t\n\tbool _WmKeydown(in MSG msg) { //called for root menu\n\t\tKKey k = (KKey)(int)msg.wParam;\n\t\tif (_IsCancelKey(k)) {\n\t\t\tClose();\n\t\t\treturn false;\n\t\t} else if (_IsOkKey(k)) {\n\t\t\tif (_TopHot(out var v)) v.m._Click(v.i, keyboard: true);\n\t\t} else if (k == KKey.Escape) {\n\t\t\t_TopMenu().Close();\n\t\t} else if (k == KKey.Left) {\n\t\t\tvar m = _TopMenu();\n\t\t\tif (m != this) m.Close();\n\t\t} else if (k == KKey.Right) {\n\t\t\tif (_TopHot(out var v) && v.b.IsSubmenu && !v.b.IsDisabled) v.m._ShowSubmenu(v.b, focusFirst: true);\n\t\t} else if (k is KKey.Down or KKey.Up or KKey.PageDown or KKey.PageUp or KKey.End or KKey.Home) {\n\t\t\t_TopMenu()._KeyNavigate(k);\n\t\t} else if (_IsPassKey(k)) {\n\t\t\treturn false;\n\t\t} else { //eg a char key. Translate to get wm_char (we'll eat it), but eat this wm_keydown.\n\t\t\tApi.TranslateMessage(msg);\n\t\t}\n\t\treturn true;\n\t}\n\t\n\tvoid _KeyNavigate(KKey k) { //called for top menu\n\t\tint i = _scroll.KeyNavigate(_iHot, k); if (i == _iHot) return;\n\t\twhile ((uint)i < _a.Count && _a[i].IsSeparator) i += k is KKey.Home or KKey.Down or KKey.PageDown ? 1 : -1;\n\t\t_SetHotItem(Math.Clamp(i, 0, _a.Count - 1), ensureVisible: true);\n\t}\n\t\n\tvoid _WmChar(char c) { //called for top menu\n\t\tif (c <= ' ') return;\n\t\tchar cl = char.ToLowerInvariant(c), cu = char.ToUpperInvariant(c);\n\t\tint iUnderlined = -1; List<int> aUnderlined = null;\n\t\tfor (int i = 0; i < _a.Count; i++) {\n\t\t\tvar v = _a[i];\n\t\t\tif (v.rawText || v.IsDisabled) continue;\n\t\t\tstring s = v.Text;\n\t\t\tint j = StringUtil.FindUnderlineChar(s);\n\t\t\tif (j >= 0 && (s[j] == cu || s[j] == cl)) {\n\t\t\t\tif (iUnderlined < 0) iUnderlined = i;\n\t\t\t\telse (aUnderlined ??= new() { iUnderlined }).Add(i);\n\t\t\t}\n\t\t}\n\t\tif (aUnderlined != null) {\n\t\t\tint fi = 0;\n\t\t\tif (_iHot >= 0) {\n\t\t\t\tfor (int i = 0; i < aUnderlined.Count; i++) if (aUnderlined[i] > _iHot) { fi = i; break; }\n\t\t\t}\n\t\t\tFocusedItem = _a[aUnderlined[fi]];\n\t\t} else if (iUnderlined >= 0) {\n\t\t\t_Click(iUnderlined, keyboard: true);\n\t\t}\n\t}\n\t\n\tstatic bool _IsOkKey(KKey k) => k is KKey.Enter or KKey.Tab or KKey.Space;\n\tstatic bool _IsCancelKey(KKey k) => k is KKey.Alt or KKey.Win or KKey.RWin or KKey.F10 or KKey.Apps or KKey.Back;\n\tstatic bool _IsPassKey(KKey k)\n\t\t=> k is KKey.Ctrl or KKey.Shift or KKey.CapsLock or KKey.NumLock or KKey.ScrollLock\n\t\tor KKey.PrintScreen or KKey.Pause or KKey.Insert\n\t\tor (>= KKey.F1 and <= KKey.F24) || keys.isMod(KMod.Ctrl | KMod.Alt | KMod.Win);\n\t\n\t/// <summary>\n\t/// Creates and shows a simple popup menu. Without images, actions, submenus. Returns item id or 0.\n\t/// </summary>\n\t/// <returns>id of the selected item when closed, or 0 if canceled.</returns>\n\t/// <param name=\"items\">\n\t/// Menu items, like <c>\"One|Two|Three\"</c> or <c>new(\"One\", \"Two\", \"Three\")</c> or string array or <c>List</c>.\n\t/// Item id can be optionally specified like <c>\"1 One|2 Two|3 Three\"</c>, unless <i>rawText</i> <c>true</c>. If missing, uses id of previous non-separator item + 1. Example: <c>\"One|Two|100 Three Four\"</c> (1|2|100|101).\n\t/// For separators use <c>null</c> or empty strings: <c>\"One|Two||Three|Four\"</c>.\n\t/// </param>\n\t/// <param name=\"flags\"></param>\n\t/// <param name=\"xy\">Menu position in screen. If <c>null</c> (default), uses mouse position by default. It depends on <i>flags</i>.</param>\n\t/// <param name=\"excludeRect\">The menu should not overlap this rectangle in screen.</param>\n\t/// <param name=\"owner\">Owner window. The menu will be automatically closed when destroying its owner window.</param>\n\t/// <param name=\"rawText\">Don't parse id from text.</param>\n\t/// <remarks>\n\t/// Adds menu items and calls <see cref=\"Show\"/>. Returns when the menu closes. All parameters except <i>items</i> are the same as in <see cref=\"Show\"/>.\n\t/// </remarks>\n\t/// <seealso cref=\"dialog.showList\"/>\n\tpublic static int showSimple(Strings items, PMFlags flags = 0, POINT? xy = null, RECT? excludeRect = null, AnyWnd owner = default, bool rawText = false) {\n\t\tvar a = items.ToArray();\n\t\tvar m = new popupMenu();\n\t\tforeach (var v in a) {\n\t\t\tvar s = v;\n\t\t\tif (s.NE()) {\n\t\t\t\tm.Separator();\n\t\t\t} else {\n\t\t\t\tif (!rawText && s.ToInt(out int id, 0, out int end)) {\n\t\t\t\t\tif (s.Eq(end, ' ')) end++;\n\t\t\t\t\ts = s[end..];\n\t\t\t\t\tm.Add(id, s);\n\t\t\t\t} else {\n\t\t\t\t\tm.Add(s);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn m.Show(flags, xy, excludeRect, owner);\n\t}\n\t\n\t/// <summary>\n\t/// Gets or sets callback function that decides how to respond to pressed keys (default, close, ignore, block).\n\t/// </summary>\n\t/// <remarks>\n\t/// The function is called on each key down event while the menu is open. Only if current thread is not in the foreground.\n\t/// To block a key, call <see cref=\"HookData.Keyboard.BlockEvent\"/>.\n\t/// The function must be as fast as possible.\n\t/// </remarks>\n\tpublic Func<popupMenu, HookData.Keyboard, PMKHook> KeyboardHook { get; set; }\n\t\n\t/// <summary>\n\t/// Sets a user-defined \"get caret rectangle\" function.\n\t/// </summary>\n\t/// <value>Default: null; calls <see cref=\"miscInfo.getTextCursorRect\"/>.</value>\n\t/// <remarks>\n\t/// The callback function is called by <see cref=\"Show\"/> when <i>flags</i> includes <see cref=\"PMFlags.ByCaret\"/>. Also used by <see cref=\"Au.Triggers.AutotextTriggerArgs.Menu\"/>.\n\t/// Let it return a rectangle in screen, or null if failed. For example, it can call <see cref=\"miscInfo.getTextCursorRect\"/>; if it fails, use an alternative way to get caret rectangle or preferred menu location.\n\t/// </remarks>\n\tpublic static Func<RECT?> caretRectFunc { get; set; }\n}\n"
  },
  {
    "path": "Au/GUI/toolbar/tb acc.cs",
    "content": "using IAccessible = Au.Types.Api.IAccessible;\nusing VarInt = Au.Types.Api.VarInt;\nusing NAVDIR = Au.Types.Api.NAVDIR;\n\nnamespace Au;\n\n[ComVisible(true)]\npartial class toolbar : IAccessible {\n\tIAccessible IAccessible.get_accParent() => _StdAO.get_accParent();\n\n\tint IAccessible.get_accChildCount() => _a.Count;\n\n\tint IAccessible.get_accChild(VarInt varChild, out object ppdispChild) { ppdispChild = null; return 1; }\n\n\tstring IAccessible.get_accName(VarInt varChild) => _B(varChild, out var b) ? (b.Text ?? b.Tooltip) : _name;\n\n\tstring IAccessible.get_accValue(VarInt varChild) => null;\n\n\tstring IAccessible.get_accDescription(VarInt varChild) => _B(varChild, out _) ? null : \"Floating toolbar\";\n\n\tVarInt IAccessible.get_accRole(VarInt varChild) {\n\t\tDebug_.PrintIf(Api.GetCurrentThreadId() != _w.ThreadId, \"thread\");\n\t\tvar r = !_B(varChild, out var b)\n\t\t\t? ERole.TOOLBAR\n\t\t\t: b.ItemType switch {\n\t\t\t\tTBItemType.Separator => ERole.SEPARATOR,\n\t\t\t\tTBItemType.Group => ERole.GROUPING,\n\t\t\t\tTBItemType.Menu => ERole.BUTTONMENU,\n\t\t\t\t_ => ERole.BUTTON\n\t\t\t};\n\t\treturn (int)r - 1;\n\t}\n\n\tVarInt IAccessible.get_accState(VarInt varChild) {\n\t\tEState r = 0;\n\t\tif (!_w.IsEnabled()) r |= EState.DISABLED;\n\t\tif (!_B(varChild, out var b)) {\n\t\t\tif (!_w.IsVisible) r |= EState.INVISIBLE;\n\t\t} else {\n\t\t\tif (b.IsSeparatorOrGroup_) r |= EState.DISABLED;\n\t\t\tif (b.IsMenu_) r |= EState.HASPOPUP;\n\t\t\t//TODO3: if offscreen, r |= EState.INVISIBLE | EState.OFFSCREEN;\n\t\t\t//no: EState.HOTTRACKED;\n\t\t}\n\t\treturn (int)r - 1;\n\t}\n\n\tstring IAccessible.get_accHelp(VarInt varChild) => _B(varChild, out var b) ? _GetFullTooltip(b) : null;\n\n\tint IAccessible.get_accHelpTopic(out string pszHelpFile, VarInt varChild) => throw new NotImplementedException();\n\n\tstring IAccessible.get_accKeyboardShortcut(VarInt varChild) => null;\n\n\tobject IAccessible.get_accFocus() => null;\n\n\tobject IAccessible.get_accSelection() => null;\n\n\tstring IAccessible.get_accDefaultAction(VarInt varChild)\n\t\t=> !_B(varChild, out var b) || b.IsSeparatorOrGroup_ ? null : b.IsMenu_ ? \"Open\" : \"Execute\";\n\n\tvoid IAccessible.accSelect(ESelect flagsSelect, VarInt varChild) => throw new NotImplementedException();\n\n\tvoid IAccessible.accLocation(out int pxLeft, out int pyTop, out int pcxWidth, out int pcyHeight, VarInt varChild) {\n\t\tif (!_B(varChild, out var b)) {\n\t\t\t_StdAO.accLocation(out pxLeft, out pyTop, out pcxWidth, out pcyHeight, varChild);\n\t\t} else {\n\t\t\tvar r = b.rect; _w.MapClientToScreen(ref r);\n\t\t\tpxLeft = r.left; pyTop = r.top; pcxWidth = r.Width; pcyHeight = r.Height;\n\t\t}\n\t}\n\n\tobject IAccessible.accNavigate(NAVDIR navDir, VarInt varStart) {\n\t\tint i = varStart;\n\t\tvar a = _a;\n\t\tif (navDir == NAVDIR.FIRSTCHILD || navDir == NAVDIR.LASTCHILD) {\n\t\t\tif (i == -1) return navDir == NAVDIR.FIRSTCHILD ? 1 : a.Count;\n\t\t} else {\n\t\t\tif (i == -1) return _StdAO.accNavigate(navDir, varStart);\n\t\t\tswitch (navDir) {\n\t\t\tcase NAVDIR.PREVIOUS:\n\t\t\t\tif (i > 0) return i;\n\t\t\t\tbreak;\n\t\t\tcase NAVDIR.NEXT:\n\t\t\t\tif (++i < a.Count) return i + 1;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tVarInt IAccessible.accHitTest(int xLeft, int yTop) {\n\t\tPOINT p = new(xLeft, yTop); _w.MapScreenToClient(ref p);\n\t\tif (!_w.ClientRect.Contains(p)) return _StdAO.accHitTest(xLeft, yTop);\n\t\treturn _HitTest(p);\n\t}\n\n\tvoid IAccessible.accDoDefaultAction(VarInt varChild) {\n\t\tif (!_B(varChild, out var b) || b.IsSeparatorOrGroup_) return;\n\t\t_w.Post(Api.WM_USER + 50, (int)varChild);\n\t}\n\n\tvoid IAccessible.put_accName(VarInt varChild, string szName) { }\n\n\tvoid IAccessible.put_accValue(VarInt varChild, string szValue) { }\n\n\tbool _B(VarInt varChild, out TBItem b) {\n\t\tint i = varChild;\n\t\tif (i == -1) { b = null; return false; }\n\t\tb = _a[i]; return true;\n\t}\n}"
  },
  {
    "path": "Au/GUI/toolbar/tb dialog.cs",
    "content": "//TODO2: sometimes the first time shows the window with several s delay. Can't repro, even after reboot. Possibly more likely after hibernation. The slow code is `new Form();` (even without setting properties).\n\nusing System.Windows.Forms;\n\nnamespace Au;\n\npublic partial class toolbar {\n\t[ThreadStatic] static Form s_listWindow;\n\t\n\t/// <summary>\n\t/// Creates a window with a list of toolbars of this thread. Can be used to find lost toolbars.\n\t/// </summary>\n\t/// <param name=\"show\">Show the window now, non-modal. If a window shown by this function already exists in this thread - activate it.</param>\n\tpublic static Form toolbarsDialog(bool show = true) {\n\t\tif (show && s_listWindow != null) {\n\t\t\ts_listWindow.Hwnd().ActivateL(true);\n\t\t\treturn s_listWindow;\n\t\t}\n\t\t\n\t\tvar f = new Form {\n\t\t\tText = \"Active toolbars\",\n\t\t\tSize = new(330, 330),\n\t\t\tAutoScaleMode = AutoScaleMode.Dpi,\n\t\t\tStartPosition = FormStartPosition.CenterScreen,\n\t\t\tIcon = icon.ofThisApp()?.ToGdipIcon()\n\t\t};\n\t\tf.Load += (_, _) => { f.Hwnd().ActivateL(); };\n\t\t\n\t\tvar lv = new ListView {\n\t\t\tDock = DockStyle.Fill,\n\t\t\tView = View.List,\n\t\t\tBorderStyle = BorderStyle.None,\n\t\t\tMultiSelect = false,\n\t\t\tContextMenuStrip = new()\n\t\t};\n\t\tf.Controls.Add(lv);\n\t\t\n\t\tvar osdr = new osdRect { Color = 0xff0000, Thickness = 12 };\n\t\tosdText osdt = null;\n\t\tListViewItem _osdItem = null;\n\t\t\n\t\tvar atb = _Manager._atb;\n\t\t(toolbar tb, bool sat)[] patb = atb.Select(o => (o, o.Satellite?.IsOpen ?? false)).ToArray();\n\t\tvar timer1 = timer.every(250, _ => {\n\t\t\tbool changed = atb.Count != patb.Length;\n\t\t\tif (!changed) {\n\t\t\t\tfor (int i = 0; i < atb.Count; i++) {\n\t\t\t\t\tif (atb[i] != patb[i].tb || (atb[i].Satellite?.IsOpen ?? false) != patb[i].sat) { changed = true; break; }\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (changed) {\n\t\t\t\t_HideRect();\n\t\t\t\tpatb = atb.Select(o => (o, o.Satellite?.IsOpen ?? false)).ToArray();\n\t\t\t\t_FillList();\n\t\t\t}\n\t\t});\n\t\t\n\t\tf.FormClosed += (_, _) => {\n\t\t\tif (show) s_listWindow = null;\n\t\t\tosdr.Dispose();\n\t\t\ttimer1.Stop();\n\t\t};\n\t\t\n\t\t_FillList();\n\t\t\n\t\tvoid _FillList() {\n\t\t\tlv.Items.Clear();\n\t\t\tforeach (var tb in _Manager._atb) {\n\t\t\t\t_Add(tb);\n\t\t\t\tif (tb.Satellite is { } sat && sat.IsOpen) _Add(sat);\n\t\t\t}\n\t\t\tvoid _Add(toolbar tb) {\n\t\t\t\tlv.Items.Add(tb.ToString()).Tag = tb;\n\t\t\t}\n\t\t}\n\t\t\n\t\tlv.MouseMove += (_, _) => { //note: MouseHover not always works\n\t\t\tvar p = mouse.xy;\n\t\t\tlv.Hwnd().MapScreenToClient(ref p);\n\t\t\tvar v = lv.HitTest(p).Item;\n\t\t\tif (v == _osdItem) return;\n\t\t\tif (v != null) {\n\t\t\t\tvar tb = v.Tag as toolbar;\n\t\t\t\tif (tb.IsOpen) {\n\t\t\t\t\tvar w = tb._w;\n\t\t\t\t\tvar r = w.Rect;\n\t\t\t\t\tif (screen.isInAnyScreen(r)) {\n\t\t\t\t\t\tr.Inflate(10, 10);\n\t\t\t\t\t\tosdr.Rect = r;\n\t\t\t\t\t\tosdr.Show();\n\t\t\t\t\t} else {\n\t\t\t\t\t\tosdt = osdText.showText($\"The toolbar is offscreen. Right-click to move.\\nRectangle: {r}\", xy: PopupXY.Mouse);\n\t\t\t\t\t}\n\t\t\t\t\tv.Selected = true;\n\t\t\t\t\tv.Focused = true;\n\t\t\t\t\t_osdItem = v;\n\t\t\t\t} else {\n\t\t\t\t\t_HideRect();\n\t\t\t\t\t_FillList();\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t_HideRect();\n\t\t\t}\n\t\t};\n\t\tlv.MouseLeave += (_, _) => _HideRect();\n\t\t\n\t\tvoid _HideRect() {\n\t\t\tif (_osdItem != null) {\n\t\t\t\t_osdItem = null;\n\t\t\t\tosdr.Hide();\n\t\t\t\tosdt?.Dispose(); osdt = null;\n\t\t\t}\n\t\t}\n\t\t\n\t\tlv.ItemActivate += (_, _) => {\n\t\t\t_Edit(lv.FocusedItem.Tag as toolbar);\n\t\t};\n\t\t\n\t\tvoid _Edit(toolbar tb) {\n\t\t\tScriptEditor.Open(tb._sourceFile, tb._sourceLine);\n\t\t\ttimer.after(100, _ => f.Hwnd().ZorderTop());\n\t\t}\n\t\t\n\t\tlv.ContextMenuStrip.Opening += (_, e) => {\n\t\t\te.Cancel = true;\n\t\t\tif (lv.SelectedItems.Count == 0) return;\n\t\t\t_HideRect();\n\t\t\tvar tb = lv.FocusedItem.Tag as toolbar;\n\t\t\tvar w = f.Hwnd();\n\t\t\tvar m = new popupMenu();\n\t\t\tm[\"Edit\\tD-click\"] = o => _Edit(tb);\n\t\t\tm[\"Move here\"] = o => {\n\t\t\t\tif (!tb.IsOpen) return;\n\t\t\t\tvar w = tb._w;\n\t\t\t\tif (!w.IsVisible && !dialog.showOkCancel(\"Hidden\", \"Move this hidden toolbar?\", owner: w)) return;\n\t\t\t\tw.MoveL_(mouse.xy);\n\t\t\t\tif (!w.ZorderIsAbove(w)) w.ZorderAbove(w);\n\t\t\t};\n\t\t\tm.Show(owner: w);\n\t\t};\n\t\t\n\t\tif (show) f.Show();\n\t\treturn s_listWindow = f;\n\t}\n}\n"
  },
  {
    "path": "Au/GUI/toolbar/tb man.cs",
    "content": "namespace Au;\n\npublic partial class toolbar {\n\tclass _OwnerWindow {\n\t\tpublic readonly List<toolbar> a = [];\n\t\tpublic readonly wnd w;\n\t\tpublic bool visible;\n\t\tbool _updatedOnce;\n\t\tRECT _rect, _clientRect;\n\t\tSIZE _prevSize, _prevClientSize;\n\t\t//public readonly int thread;\n\t\t\n\t\tpublic _OwnerWindow(wnd w) {\n\t\t\tthis.w = w;\n\t\t\t//thread = w.ThreadId;\n\t\t}\n\t\t\n\t\tpublic void AddTB(toolbar tb) {\n\t\t\ta.Add(tb);\n\t\t\ttb._ow = this;\n\t\t}\n\t\t\n\t\tpublic (bool visible, bool dead) IsVisible() {\n\t\t\tlastError.clear();\n\t\t\tif (!w.IsVisible) return (false, lastError.code != 0);\n\t\t\treturn (!w.IsMinimized && !w.IsCloaked, false);\n\t\t\t//speed: IsCloaked now on Win10 quite fast, faster than GetRect\n\t\t}\n\t\t\n\t\tpublic bool UpdateRect(out bool changed) {\n\t\t\tchanged = false;\n\t\t\t\n\t\t\t//if a toolbar has MaximizedWindowTopPlus, calculate how many physical pixels to add to the top of the window rect or client rect\n\t\t\tint? mtpw = null, mtpc = null;\n\t\t\tforeach (var tb in a) {\n\t\t\t\tif (tb._oc != null || tb._os != null) continue;\n\t\t\t\tif (tb.MaximizedWindowTopPlus != 0 && w.IsMaximized) {\n\t\t\t\t\tref int? mtp = ref tb._followClientArea ? ref mtpc : ref mtpw;\n\t\t\t\t\tif (mtp != null) print.warning(\"When multiple toolbars attached to the same window, set MaximizedWindowTopPlus once (for any toolbar), not for all toolbars.\");\n\t\t\t\t\telse mtp = tb._Scale(tb.MaximizedWindowTopPlus, true);\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tint have = 0;\n\t\t\tforeach (var tb in a) {\n\t\t\t\tif (tb._oc != null || tb._os != null) continue;\n\t\t\t\tif (tb._followClientArea) {\n\t\t\t\t\tif (0 != (have & 2)) continue;\n\t\t\t\t\tif (!w.GetClientRect(out var r, inScreen: true)) return false;\n\t\t\t\t\tr.top += mtpc.GetValueOrDefault();\n\t\t\t\t\tif (r != _clientRect) {\n\t\t\t\t\t\t_prevClientSize = (_clientRect.Width, _clientRect.Height);\n\t\t\t\t\t\t_clientRect = r;\n\t\t\t\t\t\tchanged = true;\n\t\t\t\t\t}\n\t\t\t\t\thave |= 2;\n\t\t\t\t} else {\n\t\t\t\t\tif (0 != (have & 1)) continue;\n\t\t\t\t\tif (!w.GetRect(out var r)) return false;\n\t\t\t\t\tr.top += mtpw.GetValueOrDefault();\n\t\t\t\t\tif (r != _rect) {\n\t\t\t\t\t\t_prevSize = (_rect.Width, _rect.Height);\n\t\t\t\t\t\t_rect = r;\n\t\t\t\t\t\tchanged = true;\n\t\t\t\t\t}\n\t\t\t\t\thave |= 1;\n\t\t\t\t}\n\t\t\t\tif (have == 3) break;\n\t\t\t}\n\t\t\tif (!_updatedOnce) _updatedOnce = changed = true;\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\tpublic (RECT r, SIZE size) GetCachedRectAndPrevSize(toolbar tb)\n\t\t\t=> (tb._followClientArea ? _clientRect : _rect, tb._followClientArea ? _prevClientSize : _prevSize);\n\t}\n\t\n\tclass _OwnerControl {\n\t\tpublic readonly wnd c;\n\t\tpublic readonly ITBOwnerObject oo;\n\t\tpublic RECT cachedRect;\n\t\tpublic SIZE prevSize;\n\t\tbool _updatedOnce;\n\t\t\n\t\tpublic _OwnerControl(wnd control, ITBOwnerObject ioo) {\n\t\t\tc = control;\n\t\t\too = ioo;\n\t\t}\n\t\t\n\t\tpublic (bool visible, bool dead) IsVisible(bool parentVisible = true) {\n\t\t\tif (!c.Is0) {\n\t\t\t\tlastError.clear();\n\t\t\t\tif (!c.IsVisible) return (false, lastError.code != 0);\n\t\t\t\tif (!parentVisible || c.IsMinimized) return default; //never mind: ancestors controls may be minimized\n\t\t\t}\n\t\t\tif (oo != null) {\n\t\t\t\tif (!oo.IsAlive) return (false, true);\n\t\t\t\tif (!oo.IsVisible) return default;\n\t\t\t}\n\t\t\treturn (true, false);\n\t\t}\n\t\t\n\t\tpublic bool UpdateRect(out bool changed) {\n\t\t\tbool ok = oo != null ? oo.GetRect(out RECT r) : c.GetRect(out r);\n\t\t\tif (changed = ok && r != cachedRect) {\n\t\t\t\tprevSize = (cachedRect.Width, cachedRect.Height);\n\t\t\t\tcachedRect = r;\n\t\t\t}\n\t\t\tif (!_updatedOnce) _updatedOnce = changed = true;\n\t\t\treturn ok;\n\t\t}\n\t}\n\t\n\tclass _OwnerScreen {\n\t\tpublic _OwnerScreen(toolbar tb, screen scrn) {\n\t\t\t_tb = tb;\n\t\t\t_scrn = (_isAuto = scrn.IsEmpty) ? screen.of(_tb._sett.screenx, _tb._sett.screeny) : scrn.Now;\n\t\t\tUpdateRect(out _);\n\t\t}\n\t\t\n\t\ttoolbar _tb;\n\t\tscreen _scrn;\n\t\tbool _isAuto;\n\t\tpublic RECT cachedRect;\n\t\tpublic SIZE prevSize;\n\t\t\n#if DEBUG\n\t\tpublic screen Screen {\n\t\t\tget {\n\t\t\t\tDebug_.PrintIf(!_scrn.IsAlive, \"screen not alive\");\n\t\t\t\treturn _scrn;\n\t\t\t}\n\t\t}\n#else\n\t\tpublic screen Screen => _scrn;\n#endif\n\t\t\n\t\t//public bool IsAuto => _isAuto;\n\t\t\n\t\tpublic bool UpdateRect(out bool changed) {\n\t\t\tRECT r = _scrn.Rect;\n\t\t\tif (changed = r != cachedRect && !r.Is0) {\n\t\t\t\tprevSize = (cachedRect.Width, cachedRect.Height);\n\t\t\t\tcachedRect = r;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\t//called from _WmWindowPosChanged\n\t\tpublic void UpdateIfAutoScreen() {\n\t\t\tif (!_isAuto) return;\n\t\t\tvar r = screen.of(_tb._w).Rect;\n\t\t\tint x = 0, y = 0; if (r.left != 0 || r.top != 0) { x = r.CenterX; y = r.CenterY; }\n\t\t\tif (x != _tb._sett.screenx || y != _tb._sett.screeny) {\n\t\t\t\t_scrn = screen.of(_tb._sett.screenx = x, _tb._sett.screeny = y);\n\t\t\t\tUpdateRect(out _);\n\t\t\t}\n\t\t}\n\t\t\n\t\t//Called on WM_DISPLAYCHANGE. If screen detached, sets _scrn = 0. When reattached, sets _scrn = new screen handle.\n\t\tpublic bool IsScreenInvalid() {\n\t\t\tif (!_scrn.IsAlive) {\n\t\t\t\t//Debug_.Print($\"CloseIfScreenInvalid, {_tb.Name}, {screen.of(_tb._w, SODefault.Zero)}\");\n\t\t\t\t\n\t\t\t\t_scrn = screen.of(new POINT(cachedRect.CenterX, cachedRect.CenterY), SODefault.Zero);\n\t\t\t\treturn _scrn.Handle == default;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t}\n\t\n\t_OwnerWindow _ow; //not null if owned\n\t_OwnerControl _oc; //not null if owned by a control or other object (ITBOwnerObject)\n\t_OwnerScreen _os; //not null if not owned or if anchor has flag Screen\n\tbool _followClientArea;\n\t\n\t[ThreadStatic] static _TBManager t_man;\n\tstatic _TBManager _Manager => t_man ??= new();\n\t\n\tclass _TBManager {\n\t\tinternal readonly List<toolbar> _atb = [];\n\t\treadonly List<_OwnerWindow> _aow = [];\n\t\ttimer _timer;\n\t\tint _timerPeriod;\n\t\tWinEventHook _hook;\n\t\tint _tempHook;\n\t\tbool _inHook;\n\t\t\n\t\tpublic void Add(toolbar tb, wnd w, wnd c, ITBOwnerObject ioo) {\n\t\t\tbool isOwned = !w.Is0;\n\t\t\tif (isOwned) {\n\t\t\t\tif (!_FindOW(w, out var ow)) _aow.Add(ow = new _OwnerWindow(w));\n\t\t\t\tow.AddTB(tb);\n\t\t\t\tif (!c.Is0 || ioo != null) tb._oc = new _OwnerControl(c, ioo);\n\t\t\t}\n\t\t\t\n\t\t\t_atb.Add(tb);\n\t\t\t\n\t\t\tif (_hook == null) {\n\t\t\t\t_hook = new WinEventHook([\n\t\t\t\t\t0, EEvent.OBJECT_REORDER,\n\t\t\t\t\tEEvent.OBJECT_CLOAKED, EEvent.OBJECT_UNCLOAKED,\n\t\t\t\t\tEEvent.SYSTEM_MOVESIZESTART, EEvent.SYSTEM_MOVESIZEEND,\n\t\t\t\t\tEEvent.SYSTEM_MINIMIZESTART, EEvent.SYSTEM_MINIMIZEEND,\n\t\t\t\t\t],\n\t\t\t\t\t_Hook,\n\t\t\t\t\tflags: EHookFlags.SKIPOWNTHREAD);\n\t\t\t\t_timer = new timer(_Timer);\n\t\t\t}\n\t\t\t\n\t\t\ttb._hide = TBHide.Owner;\n\t\t\tif (isOwned) {\n\t\t\t\t_SetTimer(250);\n\t\t\t} else {\n\t\t\t\tif (!_timer.IsRunning) _SetTimer(250);\n\t\t\t\ttb._FollowRect();\n\t\t\t\t//tb._Zorder();\n\t\t\t\ttb._SetVisible(true, TBHide.Owner);\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic void Remove(toolbar tb) {\n\t\t\t_atb.Remove(tb);\n\t\t\tvar ow = tb._ow;\n\t\t\tif (ow != null) {\n\t\t\t\tow.a.Remove(tb);\n\t\t\t\tif (ow.a.Count == 0) _aow.Remove(ow);\n\t\t\t}\n\t\t}\n\t\t\n\t\tvoid _SetTimer(int period) {\n\t\t\t_timer.Every(_timerPeriod = period);\n\t\t}\n\t\t\n\t\tvoid _Timer(timer t) {\n\t\t\tif (_timerPeriod != 250) _SetTimer(250);\n\t\t\t\n\t\t\t//remove closed toolbars and their owners if need. Now don't need because toolbars call Remove when closing.\n\t\t\t//for(int i = _atb.Count; --i >= 0;) {\n\t\t\t//\tvar tb = _atb[i];\n\t\t\t//\tif(tb._closed) Remove(tb);\n\t\t\t//}\n\t\t\t\n\t\t\t//move/close/hide/show owned toolbars together with their owners\n\t\t\tfor (int i = _aow.Count; --i >= 0;) {\n\t\t\t\tvar ow = _aow[i];\n\t\t\t\tif (!_FollowOwner(ow)) {\n\t\t\t\t\tfor (int j = ow.a.Count; --j >= 0;) {\n\t\t\t\t\t\tvar tb = ow.a[j];\n\t\t\t\t\t\ttb.Close();\n\t\t\t\t\t\tbool rem1 = _atb.Remove(tb); Debug_.PrintIf(rem1, \"\");\n\t\t\t\t\t}\n\t\t\t\t\tbool rem2 = _aow.Remove(ow); Debug_.PrintIf(rem2, \"\");\n\t\t\t\t\t//actually don't need these two Remove, because tb.Close calls Remove. Just don't use RemoveAt and foreach.\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t//occasionally may fail to zorder a toolbar. Retry several times.\n\t\t\tfor (int i = _atb.Count; --i >= 0;) {\n\t\t\t\tvar v = _atb[i];\n\t\t\t\tif (v._zorderRetry > 0) v._ZorderOwned();\n\t\t\t}\n\t\t\t\n\t\t\t_ZorderTimer();\n\t\t\t\n\t\t\t_ManageFullScreen();\n\t\t}\n\t\t\n\t\t//long _reorderTime;\n\t\tvoid _Hook(HookData.WinEvent d) {\n\t\t\t//print.it(d.event_, d.idObject, d.idChild, d.thread, d.w);\n\t\t\tif (d.w.Is0 || d.idObject != (d.event_ == EEvent.OBJECT_REORDER ? EObjid.CLIENT : EObjid.WINDOW) || d.idChild != 0) return;\n\t\t\tswitch (d.event_) {\n\t\t\tcase EEvent.OBJECT_REORDER when d.w == wnd.getwnd.root: //the hook does not give the window, only its thread id\n\t\t\t\t_Zorder();\n\t\t\t\tbreak;\n\t\t\tcase EEvent.SYSTEM_MOVESIZESTART when _tempHook == 0:\n\t\t\t\tif (_FindOW(d.w, out _)) _tempHook = _hook.Add(EEvent.OBJECT_LOCATIONCHANGE, flags: EHookFlags.SKIPOWNTHREAD);\n\t\t\t\tbreak;\n\t\t\tcase EEvent.SYSTEM_MOVESIZEEND when _tempHook != 0:\n\t\t\t\t_hook.Remove(_tempHook);\n\t\t\t\t_tempHook = 0;\n\t\t\t\tbreak;\n\t\t\tcase EEvent.OBJECT_LOCATIONCHANGE:\n\t\t\tcase EEvent.OBJECT_CLOAKED:\n\t\t\tcase EEvent.OBJECT_UNCLOAKED:\n\t\t\tcase EEvent.SYSTEM_MINIMIZESTART:\n\t\t\t\t//Debug_.PrintIf(_inHook, \"_inHook\"); //it's ok\n\t\t\t\tif (!_inHook && _FindOW(d.w, out _OwnerWindow ow)) {\n\t\t\t\t\t//prevent reenter.\n\t\t\t\t\t//\tThe ITBOwnerObject may retrieve sent messages, eg when getting acc rect.\n\t\t\t\t\t//\tIt's ok if hook missed. We'll call it on timer or next OBJECT_LOCATIONCHANGE.\n\t\t\t\t\t_inHook = true;\n\t\t\t\t\ttry { _FollowOwner(ow); }\n\t\t\t\t\tfinally { _inHook = false; }\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase EEvent.SYSTEM_MINIMIZEEND:\n\t\t\t\tif (_FindOW(d.w, out _)) _SetTimer(150);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t\n\t\t\t//SYSTEM_MOVESIZESTART and SYSTEM_MOVESIZEEND temporarily add/remove OBJECT_LOCATIONCHANGE to move toolbars with the owner window.\n\t\t\t//\tCannot make OBJECT_LOCATIONCHANGE always active, because it is called frequently, on each cursor position change etc.\n\t\t\t//\tThere are no other not-in-process hooks to detect moved windows. For CBT hook need 2 processes - 64bit and 32bit.\n\t\t\t\n\t\t\t//OBJECT_REORDER keeps toolbars above their owner windows in the Z order.\n\t\t\t//\tEasier would be to make the owner natively owner. But then problems:\n\t\t\t//\t1. If this process is admin, the owner's process cannot receive drag&drop from other non-admin processes. Don't know why, probably it is a Windows bug.\n\t\t\t//\t2. Fails if owner's process is a Store app. Also probaby if higher UAC IL.\n\t\t\t//\t3. In some cases possible various anomalies, for example wrong Z order of windows after closing the owner window.\n\t\t\t//\t4. All unknown and future things like those.\n\t\t\t//\tIn QM some of these problems were solved by adding a child window to the owner window and making it the native owner of the toolbar. But then other problems, eg DPI-scaling.\n\t\t\t\n\t\t\t//PROBLEM: OBJECT_REORDER makes creating windows slower.\n\t\t\t//\tFor example, combobox controls send OBJECT_REORDER when adding items. Two for each item that would be visible in the drop-down list.\n\t\t\t//\tTested: standard dialog box with 12 comboboxes, each with 30 such items. We receive ~720 OBJECT_REORDER.\n\t\t\t//\t\tIf there are 4 processes with 1 OBJECT_REORDER hook, dialog startup time increases 50%, from 360 to 540 ms.\n\t\t\t//Other used hooks aren't called frequently. Except OBJECT_LOCATIONCHANGE, but it is temporary.\n\t\t}\n\t\t\n\t\tbool _FindOW(wnd owner, out _OwnerWindow ow) {\n\t\t\tforeach (var v in _aow) if (v.w == owner) { ow = v; return true; }\n\t\t\tow = null; return false;\n\t\t}\n\t\t\n\t\tbool _FollowOwner(_OwnerWindow ow) {\n\t\t\tvar (visibleW, dead) = ow.IsVisible();\n\t\t\tif (dead) return false;\n\t\t\t\n\t\t\tbool changedRectW = false;\n\t\t\tif (visibleW) visibleW = ow.UpdateRect(out changedRectW);\n\t\t\tow.visible = visibleW;\n\t\t\t\n\t\t\tfor (int i = ow.a.Count; --i >= 0;) {\n\t\t\t\tbool visible, changedRect;\n\t\t\t\tvar tb = ow.a[i];\n\t\t\t\tvar oc = tb._oc;\n\t\t\t\tif (oc == null) {\n\t\t\t\t\tvisible = visibleW;\n\t\t\t\t\tchangedRect = changedRectW;\n\t\t\t\t} else {\n\t\t\t\t\t(visible, dead) = oc.IsVisible(visibleW);\n\t\t\t\t\tif (dead) {\n\t\t\t\t\t\ttb.Close();\n\t\t\t\t\t\tow.a.RemoveAt(i);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (visible) visible = oc.UpdateRect(out changedRect); else changedRect = false;\n\t\t\t\t}\n\t\t\t\tbool changedVisible = visible == tb._hide.Has(TBHide.Owner);\n\t\t\t\tif (visible && (changedRect || changedVisible)) { // || changedVisible is for new toolbars, but it's ok to call for old too\n\t\t\t\t\ttb._FollowRect(true);\n\t\t\t\t}\n\t\t\t\tif (changedVisible) {\n\t\t\t\t\ttb._SetVisible(visible, TBHide.Owner);\n\t\t\t\t\tif (visible) tb._ZorderOwned();\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\tvoid _ManageFullScreen(toolbar tb = null) {\n\t\t\tif (tb?.MiscFlags.Has(TBFlags.HideWhenFullScreen) ?? _atb.Any(o => o.MiscFlags.Has(TBFlags.HideWhenFullScreen))) {\n\t\t\t\tvar w = wnd.active;\n\t\t\t\tbool isFS = w.IsFullScreen_(out var scrn);\n\t\t\t\tif (tb != null) tb._ManageFullScreen(isFS, w, scrn);\n\t\t\t\telse foreach (var v in _atb) if (v.MiscFlags.Has(TBFlags.HideWhenFullScreen)) v._ManageFullScreen(isFS, w, scrn);\n\t\t\t}\n\t\t}\n\t\t\n\t\t//EEvent.OBJECT_REORDER\n\t\tvoid _Zorder() {\n\t\t\tfor (int i = _atb.Count; --i >= 0;) {\n\t\t\t\tvar tb = _atb[i];\n\t\t\t\tif (tb.IsOwned) tb._ZorderOwned();\n\t\t\t\telse if (_zorderDelay < 1) _zorderDelay = 2;\n\t\t\t}\n#if false\n\t\t\t//This version of zordering owned toolbars is faster but unreliable.\n\t\t\t//\t1. For console windows getwindowthreadprocessid gives wrong thread id. Hook receives the correct id.\n\t\t\t//\t2. When clicked client area of a Store app, hook receives thread id of the child control. It is different than that of the main host window.\n\t\t\t//\t3. All unknown and future things like those.\n\t\t\tforeach(var v in _aow) {\n\t\t\t\tif(v.thread != d.idThread) continue;\n\t\t\t\tforeach(var tb in v.a) tb._ZorderOwned();\n\t\t\t}\n#endif\n\t\t}\n\t\tint _zorderDelay;\n\t\t\n\t\tvoid _ZorderTimer() {\n\t\t\t//250-500 ms after EEvent.OBJECT_REORDER\n\t\t\tif (_zorderDelay < 1 || --_zorderDelay > 0) return;\n\t\t\t\n\t\t\t//Ensure the toolbar is on top of the primary taskbar. Or ontop of the active window if the primary taskbar is behind it.\n\t\t\t//\tNot on top of all topmost windows. Would cover tooltips etc, fight with sibling toolbars, etc.\n\t\t\tvar taskbar = s_taskbar.FindFast(null, \"Shell_TrayWnd\", false); //of the primary screen\n\t\t\tvar active = wnd.active;\n\t\t\tvar w = !active.Is0 && active != taskbar && (taskbar.Is0 || !taskbar.ZorderIsAbove(active) && !active.IsOfThisProcess) ? active : taskbar;\n\t\t\tif (w.Is0) return;\n\t\t\tfor (int i = _atb.Count; --i >= 0;) {\n\t\t\t\tvar v = _atb[i];\n\t\t\t\tif (v.IsOwned) continue;\n\t\t\t\tvar tb = v.Hwnd;\n\t\t\t\tif (!tb.ZorderIsAbove(w) && w.IsVisible) {\n\t\t\t\t\tif (w.IsTopmost) {\n\t\t\t\t\t\ttb.ZorderL_(w, before: true);\n\t\t\t\t\t} else if (!tb.IsTopmost) {\n\t\t\t\t\t\ttb.ZorderL_(SpecHWND.TOPMOST);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t//Windows 11 bug: sometimes, when activated a non-topmost window, it becomes on top of topmost windows, eg of taskbars and unowned toolbars.\n\t\t\t\t\t\t//\tAfterwards activating other normal windows makes them on top of topmost windows too.\n\t\t\t\t\t\t//\tImpossible to reproduce, it happens randomly, once in several days or weeks.\n\t\t\t\t\t\tDebug_.Print($\"toolbar behind the active non-topmost window: {w}\");\n\t\t\t\t\t\t\n\t\t\t\t\t\t//workaround 1. Sometimes works. Fails if w is \"Caret Listener Shim Window\"; then 2 fails too.\n\t\t\t\t\t\ttb.ZorderL_(SpecHWND.NOTOPMOST);\n\t\t\t\t\t\ttb.ZorderL_(SpecHWND.TOPMOST);\n\t\t\t\t\t\tif (!tb.ZorderIsAbove(w)) {\n\t\t\t\t\t\t\tDebug_.Print(\"Workaround 1 failed.\");\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t//workaround 2. Not much tested here, but `w1.ZorderL_(w2); w2.ZorderL_(w1);` works elsewhere.\n\t\t\t\t\t\t\ttb.ZorderL_(w, before: true); //may not work, although returns true. ZorderL_ gets previous window, and it may be HWND_TOP (0). SWP(HWND_TOP) does nothing if tb is behind the active non-topmost window (w).\n\t\t\t\t\t\t\tif (!tb.ZorderIsAbove(w)) { //workaround\n\t\t\t\t\t\t\t\ttb.ZorderL_(w);\n\t\t\t\t\t\t\t\tw.ZorderL_(tb);\n\t\t\t\t\t\t\t\tDebug_.PrintIf(!tb.ZorderIsAbove(w), $\"Workaround 2 failed.\");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\ttb.ZorderL_(SpecHWND.TOPMOST);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tstatic wnd.Cached_ s_taskbar;\n\t}\n\t\n\tvoid _ZorderOwned() {\n\t\tDebug.Assert(IsOwned);\n\t\tif (IsOwned && _ow.visible) {\n\t\t\twnd wt = _w, wo = _ow.w;\n\t\t\t\n\t\t\t//Some windows, eg UiPath, have an owned \"shadow frame\" window that may cover toolbar parts that aren't entirely inside the owner window's rect.\n\t\t\t//\tToolbars should detect such windows and zorder above them. Can't just blindly zorder above the topmost owned.\n\t\t\t//\tBut detecting can be difficult/slow/unreliable. Need to get all owned windows or all thread windows, etc.\n\t\t\t//\tNever mind. Let users don't move toolbars to such places where they may be covered.\n\t\t\t//if (_zorderedOnce && wo.IsActive) {\n\t\t\t//\tvar w2 = wo.Get.EnabledOwned(); //may be not that window, eg a tooltip\n\t\t\t//\t//print.it(\"eo\", w2);\n\t\t\t//\tif (!w2.Is0 && w2.Rect.Contains(wo.Rect)) wo = w2;\n\t\t\t//}\n\t\t\t//print.it(wo);\n\t\t\t\n\t\t\tif (!_zordered || !wt.ZorderIsAbove(wo)) {\n\t\t\t\t_zordered = wt.ZorderAbove(wo) || !wo.IsAlive;\n\t\t\t\tif (!_zordered) {\n\t\t\t\t\tvar ec = lastError.code;\n\t\t\t\t\tif (wt.ZorderIsAbove(wo)) {\n\t\t\t\t\t\t_zordered = true;\n\t\t\t\t\t} else if (\n#if !DEBUG\n\t\t\t\t\t\t_zorderRetry == 1 //the last retry\n#else\n\t\t\t\t\t\t_zorderRetry > 0 //any retry. It's OK if sometimes fails first time, brobably then it's a bad time to zorder.\n#endif\n\t\t\t\t\t\t) {\n\t\t\t\t\t\tvar es = ec == Api.ERROR_ACCESS_DENIED && wo.UacAccessDenied ? \"This process should run as admin, or owner's process not as admin.\" : lastError.messageFor(ec);\n\t\t\t\t\t\tprint.warning($\"Failed to Z-order toolbar '{_name}' above owner window. {es}\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (wt.IsTopmost && !wo.IsTopmost) {\n\t\t\t\twt.ZorderAbove(wo);\n\t\t\t}\n\t\t\t\n\t\t\tif (_zordered) _zorderRetry = 0; else if (_zorderRetry == 0) _zorderRetry = 5; else _zorderRetry--;\n\t\t\t\n\t\t\t//never mind: when clicked owner's title bar, we receive 2 hook events and need to ZorderAbove 2 times. Speed is OK, but flickers more often.\n\t\t\t//\tWhen we ZorderAbove on mouse down, Windows also zorders the window on mouse up, and then we receive second event.\n\t\t\t//\tPossible workarounds:\n\t\t\t//\t1. Temporarily make wt nativaly owned by _ow.w. Restore after 500 ms. But fails with higher UAC IL windows and appstore windows.\n\t\t\t//\t2. Temporarily make wt topmost. Restore after 500 ms. But Windows makes it difficult and possibly unreliable.\n\t\t}\n\t}\n\tbool _zordered;\n\tbyte _zorderRetry;\n\t\n\tvoid _ManageFullScreen(bool isFS, wnd wFore, screen scrn) {\n\t\tif (_inMoveSize) return;\n\t\tbool hide;\n\t\tif (!isFS) hide = false;\n\t\telse if (IsOwned) hide = OwnerWindow == wFore;\n\t\telse hide = screen.of(_w, SODefault.Zero) == scrn;\n\t\t\n\t\t_SetVisible(!hide, TBHide.FullScreen);\n\t}\n\t\n\tvoid _FollowRect(bool onFollowOwner = false) {\n\t\tif (_inMoveSize) return;\n\t\tif (onFollowOwner && Anchor.OfScreen() && _followedOnce) return;\n\t\tif (!onFollowOwner && _hide.Has(TBHide.Owner) && IsOwned && OwnerWindow.IsMinimized) return;\n\t\t\n\t\tbool dpiChanged = _os == null && _SetDpi();\n\t\t//print.it(dpiChanged, OwnerWindow);\n\t\t\n\t\tvar (r, prevSize) = _GetCachedOwnerRect();\n\t\t//print.it(r, Anchor, _xy, Size);\n\t\t\n\t\tvar swp = SWPFlags.NOZORDER | SWPFlags.NOOWNERZORDER | SWPFlags.NOACTIVATE;\n\t\tvar bounds = _w.Rect;\n\t\tint x, y, cx = bounds.Width, cy = bounds.Height;\n\t\t\n\t\tif (Anchor.HasLeft()) {\n\t\t\tx = (Anchor.OppositeX() ? r.right : r.left) + _Scale(_offsets.Left, true);\n\t\t\tif (Anchor.HasRight() && (!_followedOnce || r.Width != prevSize.width)) {\n\t\t\t\tif (_preferSize) _offsets.Right = _Unscale(r.right - x - cx, true);\n\t\t\t\telse cx = Math.Max(r.right - _Scale(_offsets.Right, true) - x, 2); //_WmWindowPosChanging will limit min max if need\n\t\t\t}\n\t\t} else {\n\t\t\tDebug.Assert(Anchor.HasRight());\n\t\t\tx = (Anchor.OppositeX() ? r.left : r.right) - _Scale(_offsets.Right, true) - cx;\n\t\t}\n\t\tif (Anchor.HasTop()) {\n\t\t\ty = (Anchor.OppositeY() ? r.bottom : r.top) + _Scale(_offsets.Top, true);\n\t\t\tif (Anchor.HasBottom() && (!_followedOnce || r.Height != prevSize.height)) {\n\t\t\t\tif (_preferSize) _offsets.Bottom = _Unscale(r.bottom - y - cy, true);\n\t\t\t\telse cy = Math.Max(r.bottom - _Scale(_offsets.Bottom, true) - y, 2);\n\t\t\t}\n\t\t} else {\n\t\t\tDebug.Assert(Anchor.HasBottom());\n\t\t\ty = (Anchor.OppositeY() ? r.top : r.bottom) - _Scale(_offsets.Bottom, true) - cy;\n\t\t}\n\t\t\n\t\tif (_preferSize) {\n\t\t\t_preferSize = false;\n\t\t\t_sett.offsets = _offsets;\n\t\t}\n\t\t\n\t\tif (x == bounds.left && y == bounds.top) swp |= SWPFlags.NOMOVE;\n\t\tif (cx == bounds.Width && cy == bounds.Height) swp |= SWPFlags.NOSIZE;\n\t\tif (!swp.Has(SWPFlags.NOMOVE | SWPFlags.NOSIZE)) {\n\t\t\t_ignorePosChanged = (byte)(dpiChanged ? 2 : 1);\n\t\t\t_w.SetWindowPos(swp, x, y, cx, cy);\n\t\t\t_ignorePosChanged = 0;\n\t\t}\n\t\t\n\t\tbool followedOnce = _followedOnce;\n\t\t_followedOnce = true;\n\t\t\n\t\tif (dpiChanged) {\n\t\t\t_AutoSizeNowIfIsOpen(measureText: true);\n\t\t}\n\t\t\n\t\tif (!followedOnce && _os != null) {\n\t\t\tvar sc = screen.of(_w, SODefault.Zero);\n\t\t\tif (sc != _os.Screen) {\n\t\t\t\t_w.EnsureInScreen(_os.Screen, workArea: !_w.IsTopmost);\n\t\t\t}\n\t\t}\n\t}\n\tbool _followedOnce;\n\tbyte _ignorePosChanged;\n\tbool _preferSize;\n\t\n\tvoid _WmWindowPosChanging(ref Api.WINDOWPOS wp) {\n\t\t//uncomment if using properties MinimumSize and MaximumSize.\n\t\t\n\t\tif (!_created) return;\n\t\t////print.it(this, wp.flags);\n\t\t\n\t\t//if(!wp.flags.Has(SWPFlags.NOSIZE)) {\n\t\t//\tSIZE min = _GetMinSize();\n\t\t//\tif(wp.cx < min.width) wp.cx = min.width;\n\t\t//\tif(wp.cy < min.height) wp.cy = min.height;\n\t\t//\tSIZE max = _Scale(MaximumSize);\n\t\t//\tif(max.width > 0) wp.cx = Math.Min(wp.cx, Math.Max(max.width, min.width));\n\t\t//\tif(max.height > 0) wp.cy = Math.Min(wp.cy, Math.Max(max.height, min.height));\n\t\t//}\n\t\t//\n\t\t//SIZE _GetMinSize()\n\t\t//{\n\t\t//\tint k = Border < TBBorder.ThreeD ? 1 : WndUtil.BorderWidth(_w);//?\n\t\t//\tk *= 2;\n\t\t//\tvar ms = _Scale(MinimumSize);\n\t\t//\treturn (Math.Max(k, ms.Width), Math.Max(k, ms.Height));\n\t\t//}\n\t\t\n\t\t//don't allow to move the satellite away from the planet.\n\t\t//\tOnly when _inMoveSize. In other cases can create problems, eg when DPI changes.\n\t\tif (!wp.flags.Has(SWPFlags.NOMOVE) && _IsSatellite && _inMoveSize) {\n\t\t\tRECT r = _satPlanet._w.Rect, rs = _w.Rect;\n\t\t\tif (wp.cx == rs.Width && wp.cy == rs.Height) { //only when moving. When resizing, it could collapse the toolbar; will snap finally.\n\t\t\t\tif (wp.x > r.right) wp.x = r.right; else wp.x = Math.Max(wp.x, r.left - wp.cx);\n\t\t\t\tif (wp.y > r.bottom) wp.y = r.bottom; else wp.y = Math.Max(wp.y, r.top - wp.cy);\n\t\t\t}\n\t\t}\n\t}\n\t\n\tvoid _WmWindowPosChanged(in Api.WINDOWPOS wp) {\n\t\tif (!_created) return;\n\t\tif (!wp.flags.Has(SWPFlags.NOMOVE | SWPFlags.NOSIZE) && _ignorePosChanged < 2) {\n\t\t\tbool resized = !wp.flags.Has(SWPFlags.NOSIZE);\n\t\t\tif (_ignorePosChanged == 0) {\n\t\t\t\tif (_os != null) {\n\t\t\t\t\tif (_os.IsScreenInvalid()) return;\n\t\t\t\t\t_os.UpdateIfAutoScreen();\n\t\t\t\t}\n\t\t\t\t_UpdateOffsets(wp.x, wp.y, wp.cx, wp.cy); //tested: if SWP_NOMOVE or SWP_NOSIZE, wp contains current values\n\t\t\t} else {\n\t\t\t\tif (resized && !_inMoveSize) _sett.size = _Unscale(_w.ClientRect.Size);\n\t\t\t}\n\t\t\tif (resized) {\n\t\t\t\t/*SIZE z=*/\n\t\t\t\t_Measure(_w.ClientRect.Width); //rewrap buttons etc\n\t\t\t\t\n\t\t\t\t//if(AutoSize && _ignorePosChanged && z.height!=wp.cy && Anchor is TBAnchor.TopLR or TBAnchor.BottomLR && Layout==TBLayout.HorizontalWrap) _Resize(z);//rejected\n\t\t\t}\n\t\t\t_SatFollow();\n\t\t}\n\t\tif (wp.flags.Has(SWPFlags.HIDEWINDOW)) {\n\t\t\t_SatHide();\n\t\t}\n\t}\n\t\n\tvoid _UpdateOffsets(int x, int y, int cx, int cy) {\n\t\tvar (r, _) = _GetCachedOwnerRect();\n\t\t//print.it(x, y, cx, cy, r);\n\t\tif (Anchor.HasLeft()) _offsets.Left = _Unscale(x - (Anchor.OppositeX() ? r.right : r.left), true);\n\t\tif (Anchor.HasRight()) _offsets.Right = _Unscale((Anchor.OppositeX() ? r.left : r.right) - x - cx, true);\n\t\tif (Anchor.HasTop()) _offsets.Top = _Unscale(y - (Anchor.OppositeY() ? r.bottom : r.top), true);\n\t\tif (Anchor.HasBottom()) _offsets.Bottom = _Unscale((Anchor.OppositeY() ? r.top : r.bottom) - y - cy, true);\n\t\tif (!_inMoveSize) {\n\t\t\t_sett.offsets = _offsets;\n\t\t\t_sett.size = _Unscale(_w.ClientRect.Size);\n\t\t}\n\t}\n\t\n\tvoid _SetInMoveSize(bool start) {\n\t\t_inMoveSize = start;\n\t\tif (!start) {\n\t\t\t_sett.offsets = _offsets;\n\t\t\tvar z = _Unscale(_w.ClientRect.Size);\n\t\t\tif (z != _sett.size) {\n\t\t\t\t_sett.size = z;\n\t\t\t\t_sett.wrapWidth = z.Width - _BorderPadding(unscaled: true) * 2 + 2;\n\t\t\t\tif (AutoSize) _AutoSizeNowIfIsOpen();\n\t\t\t}\n\t\t}\n\t}\n\tbool _inMoveSize;\n\t\n\t/// <summary>\n\t/// Gets the cached rectangle of the owner window, screen, control, etc.\n\t/// If is owned and anchor has flag <c>Screen</c>, the rectangle is of toolbar's screen.\n\t/// Also gets previous size.\n\t/// The values are cached by <c>UpdateRect</c> of <c>_OwnerWindow</c> etc.\n\t/// </summary>\n\t(RECT r, SIZE prevSize) _GetCachedOwnerRect() {\n\t\tif (_os != null) return (_os.cachedRect, _os.prevSize);\n\t\tif (_oc != null) return (_oc.cachedRect, _oc.prevSize);\n\t\treturn _ow.GetCachedRectAndPrevSize(this);\n\t}\n\t\n\tunsafe nint _WmGetDpiScaledSize(nint wParam, nint lParam) {\n\t\t//a quick and not perfect workaround for: on DPI change sometimes incorrect wrap/autosize\n\t\tif (_os != null && AutoSize && Layout == TBLayout.HorizontalWrap && _inMoveSize) {\n\t\t\tref var z = ref *(SIZE*)lParam;\n\t\t\tint dpi = (int)wParam;\n\t\t\tz.width = Math2.MulDiv(z.width, dpi, _dpi);\n\t\t\tz.height = Math2.MulDiv(z.height, dpi, _dpi);\n\t\t\tz.width += dpi / 8;\n\t\t\treturn 1;\n\t\t}\n\t\treturn 0;\n\t}\n\t\n\tunsafe void _WmDpiChanged(nint wParam, nint lParam) {\n\t\tif (_os != null && Math2.LoShort(wParam) != _dpi) {\n\t\t\ttimer.after(1, _ => { //workaround for Win11 bug: when moving the toolbar to another screen, often WM_DPICHANGED received when the window is still in old screen\n\t\t\t\t_os.UpdateIfAutoScreen();\n\t\t\t\tif (!_SetDpi()) return;\n\t\t\t\t_Images(true);\n\t\t\t\t_MeasureText();\n\t\t\t\tif (_inMoveSize && lParam != 0) { //cannot autosize now, or something bad may happen, eg nested wm_dpichanged until stack overflow\n\t\t\t\t\t_w.MoveL(*(RECT*)lParam);\n\t\t\t\t} else {\n\t\t\t\t\tif (DpiScaling.offsets == true) _FollowRect(); //update offsets if script wants so\n\t\t\t\t\t_AutoSizeNowIfIsOpen();\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t}\n\t\n\tvoid _WmDisplayChange() {\n\t\tif (_os != null) {\n\t\t\tif (_os.IsScreenInvalid()) return;\n\t\t\t\n\t\t\ttimer.after(200, _ => {\n\t\t\t\tif (_os == null || _closed) return;\n\t\t\t\t\n\t\t\t\t//workaround for Win11 bug: WS_EX_TOOLWINDOW windows don't receive WM_DPICHANGED when screen DPI changes\n\t\t\t\tvar dpi = _os.Screen.Dpi;\n\t\t\t\tif (dpi != _dpi) _WmDpiChanged(dpi, 0);\n\t\t\t\t\n\t\t\t\t_os.UpdateRect(out bool changed);\n\t\t\t\tif (changed) _FollowRect();\n\t\t\t});\n\t\t} else if (osVersion.minWin8_1) {\n\t\t\t//If owner's screen DPI changed, update toolbar's DPI/size/offsets. Need it for pm-dpi-aware windows that don't change rect when DPI changed.\n\t\t\t\n\t\t\t//WM_DISPLAYCHANGE doc: \"when the display resolution has changed\". Also when changed DPI, although undocumented. Tested on Win8.1 too.\n\t\t\t//\twm_dpichanged isn't good for it. We need DPI of owner, not of toolbar.\n\t\t\t//\tAlso we receive wm_settingchanged(SPI_SETLOGICALDPIOVERRIDE), but can't trust it. SPI_SETLOGICALDPIOVERRIDE's documentation is \"Do not use.\". No on Win8.1.\n\t\t\t\n\t\t\t//Wait until owner's DPI updated. If owner is pm-dpi-aware, its DPI is updated at random times, maybe after several s.\n\t\t\tint i = 15, oldDpi = _dpi;\n\t\t\ttimer.every(1000, t => {\n\t\t\t\tif (--i > 0 && _dpi == oldDpi && !_closed && !_hide.Has(TBHide.Owner)) {\n\t\t\t\t\tif (screen.of(OwnerWindow).Dpi == oldDpi) return;\n\t\t\t\t\t//print.it(\"DPI changed\", _dpi, Dpi.OfWindow(OwnerWindow, true));\n\t\t\t\t\t_FollowRect();\n\t\t\t\t}\n\t\t\t\t//print.it(\"stop\");\n\t\t\t\tt.Stop();\n\t\t\t\t//tested: if pm-dpi-aware window is minimized, its dpi changes when restored. Not if hidden.\n\t\t\t});\n\t\t}\n\t}\n}\n\n//TODO3: now toolbars are lost too often.\n//\tEg after removing autohide.\n"
  },
  {
    "path": "Au/GUI/toolbar/tb render.cs",
    "content": "using System.Drawing;\nusing System.Drawing.Drawing2D;\n\nnamespace Au;\n\npublic partial class toolbar {\n\t/// <summary>\n\t/// Border style.\n\t/// Default <see cref=\"TBBorder.Width2\"/>.\n\t/// </summary>\n\t/// <remarks>\n\t/// This property is in the context menu and is saved.\n\t/// </remarks>\n\tpublic TBBorder Border {\n\t\tget => _sett.border;\n\t\tset {\n\t\t\t_ThreadTrap();\n\t\t\tif (value == _sett.border) return;\n\t\t\tif (IsOpen) {\n\t\t\t\tRECT r = _w.ClientRectInScreen;\n\t\t\t\tvar bp1 = _BorderPadding();\n\t\t\t\t_sett.border = value;\n\t\t\t\tvar bpDiff = _BorderPadding() - bp1;\n\t\t\t\tif (bpDiff != 0) { r.Inflate(bpDiff, bpDiff); r.Normalize(false); }\n\t\t\t\tconst WS mask = WS.CAPTION | WS.THICKFRAME | WS.SYSMENU;\n\t\t\t\tWS s1 = _w.Style, s2 = _BorderStyle(value);\n\t\t\t\tif (s2 != (s1 & mask)) _w.SetStyle(s1 = ((s1 & ~mask) | s2));\n\t\t\t\tDpi.AdjustWindowRectEx(r, ref r, s1, _w.ExStyle);\n\t\t\t\t_w.MoveL(r, SWPFlags.FRAMECHANGED | SWPFlags.HIDEWINDOW);\n\t\t\t\tif (bpDiff != 0) _Measure(); //update button rectangles\n\t\t\t\t_w.ShowL(true);\n\t\t\t} else {\n\t\t\t\t_sett.border = value;\n\t\t\t}\n\t\t\t\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Layout of buttons (horizontal, vertical).\n\t/// </summary>\n\t/// <remarks>\n\t/// This property is in the context menu and is saved.\n\t/// </remarks>\n\tpublic TBLayout Layout {\n\t\tget => _sett.layout;\n\t\tset {\n\t\t\t_ThreadTrap();\n\t\t\tif (value != _sett.layout) {\n\t\t\t\t_sett.layout = value;\n\t\t\t\t_AutoSizeNowIfIsOpen();\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Display button text. Default <c>true</c>. If <c>false</c>, displays text in tooltips, and only displays first 2 characters for buttons without image.\n\t/// </summary>\n\t/// <remarks>\n\t/// A button can override this property:\n\t/// - To never display text, let its text be empty, like <c>\"\"</c> or <c>\"|Tooltip\"</c>.\n\t/// - To always display text, append <c>\"\\a\"</c>, like <c>\"Text\\a\"</c> or <c>\"Text\\a|Tooltip\"</c>.\n\t/// </remarks>\n\tpublic bool DisplayText {\n\t\tget => _sett.dispText;\n\t\tset {\n\t\t\t_ThreadTrap();\n\t\t\tif (value != _sett.dispText) {\n\t\t\t\t_sett.dispText = value;\n\t\t\t\t_AutoSizeNowIfIsOpen(measureText: true);\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Font properties.\n\t/// </summary>\n\t/// <remarks>\n\t/// Cannot be changed after showing toolbar window.\n\t/// </remarks>\n\tpublic FontNSS Font {\n\t\tget => _font ??= new();\n\t\tset { _font = value; }\n\t}\n\tFontNSS _font;\n\t\n\t/// <summary>\n\t/// Text color.\n\t/// If <c>default</c>, uses system color.\n\t/// </summary>\n\tpublic ColorInt TextColor {\n\t\tget => _textColor;\n\t\tset {\n\t\t\t_textColor = value;\n\t\t\t_Invalidate();\n\t\t}\n\t}\n\tColorInt _textColor;\n\t\n\t/// <summary>\n\t/// Background color or brush.\n\t/// </summary>\n\t/// <value>Can be <see cref=\"Color\"/>, <see cref=\"ColorInt\"/>, <c>int</c> (color <c>0xRRGGBB</c>) or <see cref=\"Brush\"/>. If <c>null</c> (default), uses system color.</value>\n\tpublic object Background {\n\t\tget => _background;\n\t\tset {\n\t\t\tif (value is not (null or Brush or Color or ColorInt or int)) throw new ArgumentException();\n\t\t\t_background = value;\n\t\t\t_Invalidate();\n\t\t}\n\t}\n\tobject _background;\n\t\n\t/// <summary>\n\t/// Border color when <see cref=\"Border\"/> is <see cref=\"TBBorder.Width1\"/> ... <see cref=\"TBBorder.Width4\"/>.\n\t/// If <c>default</c>, uses system color.\n\t/// </summary>\n\tpublic ColorInt BorderColor {\n\t\tget => _borderColor;\n\t\tset {\n\t\t\t_borderColor = value;\n\t\t\t_Invalidate();\n\t\t}\n\t}\n\tColorInt _borderColor;\n\t\n\t/// <summary>\n\t/// Don't display the default image (dot or triangle).\n\t/// </summary>\n\tpublic bool NoDefaultImage { get; set; }\n\t\n\t/// <summary>\n\t/// Sets some metrics of this toolbar, for example button padding.\n\t/// </summary>\n\t/// <remarks>\n\t/// Cannot be changed after showing toolbar window.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// t.Metrics = new(4, 2);\n\t/// ]]></code>\n\t/// </example>\n\t/// <seealso cref=\"defaultMetrics\"/>\n\tpublic TBMetrics Metrics { get; set; }\n\t\n\t/// <summary>\n\t/// Sets default metrics of toolbars.\n\t/// </summary>\n\t/// <seealso cref=\"Metrics\"/>\n\tpublic static TBMetrics defaultMetrics {\n\t\tget => s_defaultMetrics ??= new();\n\t\tset { s_defaultMetrics = value; }\n\t}\n\tstatic TBMetrics s_defaultMetrics;\n\t\n\tvoid _Images(bool onDpiChanged) {\n\t\tforeach (var v in _a) {\n\t\t\tif (onDpiChanged) {\n\t\t\t\tif (v.image2 == null || v.image is not string) continue;\n\t\t\t\t//will either find/return same bitmap in cache, or create new bitmap from XAML, or return found old XAML bitmap created for other DPI.\n\t\t\t\t//note: will not reload non-XAML images, eg those created from native icons. They will be drawn DPI-scaled, slightly blurry.\n\t\t\t}\n\t\t\t//var old = v.image2;\n\t\t\tv.image2 = _GetImage(v).image;\n\t\t\t//if (onDpiChanged) print.it(old == v.image2);\n\t\t\tif (!_hasCachedImages && !onDpiChanged && v.image2 != null && v.image is string s && s.Length > 0) _hasCachedImages = true;\n\t\t}\n\t\tif (_hasCachedImages) _ImageCache.Cleared += _UpdateCachedImages;\n\t}\n\tbool _hasCachedImages;\n\t\n\tvoid _UpdateCachedImages() {\n\t\tforeach (var v in _a) {\n\t\t\tif (v.image2 != null && v.image is string s && s.Length > 0) {\n\t\t\t\tif (_GetImage(v).image is { } b) v.image2 = b;\n\t\t\t}\n\t\t}\n\t}\n\t\n\t//not used\n\t//internal void ChangeImage_(TBItem ti, Bitmap b) {\n\t//\tif (_closed) return;\n\t//\tti.image2 = b;\n\t//\t_Invalidate(ti);\n\t//}\n\t\n\tconst TFFlags c_tff = TFFlags.NOPREFIX | TFFlags.EXPANDTABS;\n\t\n\tvoid _MeasureText() {\n\t\tNativeFont_ font = null;\n\t\tFontDC_ dc = null;\n\t\ttry {\n\t\t\tforeach (var b in _a) {\n\t\t\t\tint len = _TextDispLen(b);\n\t\t\t\tif (len != 0) {\n\t\t\t\t\tdc ??= new FontDC_(font = Font.CreateFont(_dpi));\n\t\t\t\t\tb.textSize = dc.MeasureDT(b.Text.AsSpan(0, len), c_tff);\n\t\t\t\t\tb.textSize.height++;\n\t\t\t\t} else {\n\t\t\t\t\tb.textSize = default;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfinally {\n\t\t\tdc?.Dispose();\n\t\t\tfont?.Dispose();\n\t\t}\n\t}\n\t\n\tint _TextDispLen(TBItem b) {\n\t\tvar s = b.Text;\n\t\tif (s.NE()) return 0;\n\t\tif (DisplayText || b.textAlways) return s.Length;\n\t\tif (b.HasImage_) return 0;\n\t\treturn Math.Min(s.Length, 2); //info: 2 is ok for surrogate\n\t}\n\t\n\tstruct _Metrics {\n\t\tpublic int bBorder, bPaddingX, bPaddingY, tbBorder, tbPadding, textPaddingR, textPaddingY, image, dot, triangle, imagePaddingX;\n\t\t\n\t\tpublic _Metrics(toolbar tb) {\n\t\t\tvar k = tb.Metrics ?? defaultMetrics;\n\t\t\tint dpi = tb._dpi;\n\t\t\tbBorder = dpi / 96;\n\t\t\tbPaddingX = Dpi.Scale(k.ButtonPaddingX, dpi);\n\t\t\tbPaddingY = Dpi.Scale(k.ButtonPaddingY, dpi);\n\t\t\ttbPadding = tb._BorderPadding();\n\t\t\ttbBorder = tbPadding > 0 ? bBorder : 0;\n\t\t\ttextPaddingR = Dpi.Scale(4, dpi);\n\t\t\ttextPaddingY = Dpi.Scale(1, dpi);\n\t\t\timage = Dpi.Scale(tb.ImageSize, dpi);\n\t\t\tif (!tb.NoDefaultImage) {\n\t\t\t\tdot = tb.ImageSize / 3;\n\t\t\t\ttriangle = tb.ImageSize / 2;\n\t\t\t} else dot = triangle = 0;\n\t\t\timagePaddingX = Dpi.Scale(2, dpi);\n\t\t\t\n\t\t\t//tbBorder += 1; //test border thickness\n\t\t\t//bBorder += 1;\n\t\t}\n\t\t\n\t\tpublic int ImageEtc(TBItem b, bool vert) => b.HasImage_ ? image : (dot == 0 ? 0 : (vert ? image : (b.IsMenu_ ? triangle : dot)));\n\t}\n\t\n\t/// <summary>\n\t/// Measures toolbar size and sets button rectangles.\n\t/// Returns size of client area.\n\t/// </summary>\n\tSIZE _Measure(int? width = null) {\n\t\t//print.it(\"measure\");\n\t\tSIZE R = default;\n\t\tbool autoSize = AutoSize && _a.Count > 0;\n\t\tbool vert = Layout == TBLayout.Vertical;\n\t\tvar m = new _Metrics(this);\n\t\tint tbp = m.tbPadding,\n\t\t\tbuttonPlusX = (m.bBorder + m.bPaddingX + m.imagePaddingX) * 2,\n\t\t\tbuttonPlusY = (m.bBorder + m.bPaddingY + m.textPaddingY) * 2;\n\t\t\n\t\tint ww = int.MaxValue;\n\t\tif (width != null) ww = width.Value - tbp * 2;\n\t\telse if (!autoSize) ww = _Scale(_sett.size.Width, false) - tbp * 2;\n\t\telse if (AutoSizeWrapWidth > 0) ww = _Scale(AutoSizeWrapWidth, false);\n\t\t//ww = Math.Max(ww, m.image); //no, then can't have thin vertical screen edge toolbars\n\t\t\n\t\tfor (int i = 0, x = 0, y = 0; i < _a.Count; i++) {\n\t\t\tvar b = _a[i];\n\t\t\tSIZE z;\n\t\t\tif (b.IsGroup_ || (b.IsSeparator_ && vert)) {\n\t\t\t\t_NewRow();\n\t\t\t\tif (b.Text.NE()) z = new(2, 2 + m.imagePaddingX * 2);\n\t\t\t\telse z = new(b.textSize.width + m.image * 2, b.textSize.height + m.imagePaddingX * 2);\n\t\t\t} else {\n\t\t\t\tif (b.IsSeparator_) {\n\t\t\t\t\tz = new(2 + m.imagePaddingX * 2, _a[i - 1].rect.Height);\n\t\t\t\t} else {\n\t\t\t\t\tz = new(buttonPlusX + m.ImageEtc(b, vert),\n\t\t\t\t\t\tm.image + m.bPaddingY * 2 + m.bBorder * 4); //m.bBorder*4 for borders and image padding\n\t\t\t\t\tif (b.textSize.width > 0) {\n\t\t\t\t\t\tz.width += b.textSize.width + m.textPaddingR;\n\t\t\t\t\t\tz.height = Math.Max(z.height, b.textSize.height + buttonPlusY);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!vert) if (x + z.width > ww && x > 0) _NewRow();\n\t\t\t}\n\t\t\tb.rect = new(x, y, z.width, z.height);\n\t\t\tb.rect.right = Math.Min(b.rect.right, ww);\n\t\t\tx += z.width;\n\t\t\tR.width = Math.Max(R.width, x);\n\t\t\tR.height = Math.Max(R.height, b.rect.bottom);\n\t\t\tif (vert || b.IsGroup_) _NewRow();\n\t\t\t\n\t\t\tvoid _NewRow() { x = 0; y = R.height; }\n\t\t}\n\t\t\n\t\tif (autoSize) {\n\t\t\tR.width = Math.Min(R.width, ww) + tbp * 2;\n\t\t\tR.height += tbp * 2;\n\t\t} else {\n\t\t\tR = new(ww + tbp * 2, _Scale(_sett.size.Height, false));\n\t\t}\n\t\t//print.it(R);\n\t\t\n\t\tforeach (var b in _a) {\n\t\t\tb.rect.Offset(tbp, tbp);\n\t\t\tif (vert || b.IsGroup_) b.rect.right = R.width - tbp;\n\t\t}\n\t\t\n\t\treturn R;\n\t}\n\t\n\tvoid _WmPaint(IntPtr dc, Graphics g, RECT rClient, RECT rUpdate) {\n\t\tswitch (Background) {\n\t\tcase Brush bb: g.FillRectangle(bb, rUpdate); break;\n\t\tcase Color bc: g.Clear(bc); break;\n\t\tcase ColorInt bc: g.Clear((Color)bc); break;\n\t\tcase int bc: g.Clear(bc.ToColor_()); break;\n\t\tdefault: g.Clear(SystemColors.Control); break;\n\t\t}\n\t\t\n\t\tvar m = new _Metrics(this);\n\t\tbool vert = Layout == TBLayout.Vertical;\n\t\t\n\t\tBrush brushDot = null, brushTriangle = null;\n\t\tNativeFont_ font = null; IntPtr oldFont = default;\n\t\tint groupTextX = 0;\n\t\tint sysTextColor = Api.GetSysColor(Api.COLOR_BTNTEXT);\n\t\ttry {\n\t\t\tfor (int i = 0; i < _a.Count; i++) {\n\t\t\t\tvar b = _a[i];\n\t\t\t\tif (!b.rect.IntersectsWith(rUpdate)) continue;\n\t\t\t\t\n\t\t\t\t//g.DrawRectangleInset(Pens.BlueViolet, b.rect);\n\t\t\t\t\n\t\t\t\tint textColor = b.TextColor != default ? b.TextColor.ToBGR() : (TextColor != default ? TextColor.ToBGR() : sysTextColor);\n\t\t\t\t\n\t\t\t\tint xImage = b.rect.left + m.bBorder + m.bPaddingX + m.imagePaddingX;\n\t\t\t\t\n\t\t\t\tif (b.IsSeparator_ && !vert) {\n\t\t\t\t\tvar r = b.rect; r.Inflate(-m.imagePaddingX, -m.imagePaddingX - m.bBorder);\n\t\t\t\t\tApi.DrawEdge(dc, ref r, Api.EDGE_ETCHED, Api.BF_LEFT);\n\t\t\t\t} else if (b.IsSeparatorOrGroup_) {\n\t\t\t\t\tint pad = m.tbPadding + m.imagePaddingX;\n\t\t\t\t\tRECT r = new(pad, b.rect.CenterY - 1, rClient.Width - pad * 2, 2);\n\t\t\t\t\tif (b.Text.NE()) {\n\t\t\t\t\t\tApi.DrawEdge(dc, ref r, Api.EDGE_ETCHED, Api.BF_TOP);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tint len = (r.Width - b.textSize.width) / 2 - m.textPaddingR; //length of left and right (from text) parts\n\t\t\t\t\t\tif (len > 0) {\n\t\t\t\t\t\t\tr.left = r.right - len;\n\t\t\t\t\t\t\tApi.DrawEdge(dc, ref r, Api.EDGE_ETCHED, Api.BF_TOP); //right\n\t\t\t\t\t\t\tr.left = pad; r.right = pad + len;\n\t\t\t\t\t\t\tApi.DrawEdge(dc, ref r, Api.EDGE_ETCHED, Api.BF_TOP); //left\n\t\t\t\t\t\t\tgroupTextX = r.right + m.textPaddingR;\n\t\t\t\t\t\t} else groupTextX = pad;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif (i == _iHot || i == _iClick) {\n\t\t\t\t\t\tif (!_noHotClick && (textColor == 0 || TextColor != default || b.TextColor != default))\n\t\t\t\t\t\t\tg.FillRectangle((i == _iClick ? 0xC0DCF3 : 0xD8E6F2).ToColor_(), b.rect);\n\t\t\t\t\t\tg.DrawRectangleInset((i == _iClick ? 0x90C8F6 : 0xC0DCF3).ToColor_(), m.bBorder, b.rect);\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tint x = xImage, y = b.rect.top;\n\t\t\t\t\tif (!b.HasImage_) {\n\t\t\t\t\t\tif (m.dot > 0) {\n\t\t\t\t\t\t\tg.SmoothingMode = SmoothingMode.HighQuality;\n\t\t\t\t\t\t\ty += (b.rect.Height - m.dot) / 2;\n\t\t\t\t\t\t\tif (vert) x += m.image / 4;\n\t\t\t\t\t\t\tif (b.IsMenu_) {\n\t\t\t\t\t\t\t\ty += m.triangle / 6;\n\t\t\t\t\t\t\t\tif (vert) x -= m.triangle / 8;\n\t\t\t\t\t\t\t\tbrushTriangle ??= new SolidBrush(Color.YellowGreen);\n\t\t\t\t\t\t\t\tg.FillPolygon(brushTriangle, new Point[] { new(x, y), new(x + m.triangle, y), new(x + m.triangle / 2, y + m.triangle / 2) });\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tbrushDot ??= new SolidBrush(Color.SkyBlue);\n\t\t\t\t\t\t\t\tg.FillEllipse(brushDot, x, y, m.dot, m.dot);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tg.SmoothingMode = SmoothingMode.None;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (b.image2 != null) {\n\t\t\t\t\t\ttry { g.DrawImage(b.image2, x, y + (b.rect.Height - m.image) / 2, m.image, m.image); }\n\t\t\t\t\t\tcatch (Exception ex) { Debug_.Print(ex); }\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (b.textSize.width > 0) {\n\t\t\t\t\tif (font == null) {\n\t\t\t\t\t\tfont = Font.CreateFont(_dpi);\n\t\t\t\t\t\toldFont = Api.SelectObject(dc, font.Handle);\n\t\t\t\t\t\tApi.SetBkMode(dc, 1);\n\t\t\t\t\t}\n\t\t\t\t\tApi.SetTextColor(dc, textColor);\n\t\t\t\t\t\n\t\t\t\t\tRECT r = new(b.IsGroup_ ? groupTextX : xImage + m.ImageEtc(b, vert) + m.imagePaddingX, b.rect.top + (b.rect.Height - b.textSize.height) / 2, b.textSize.width, b.rect.Height);\n\t\t\t\t\tr.right = Math.Min(r.right, b.rect.right - m.bBorder * 2);\n\t\t\t\t\t\n\t\t\t\t\tApi.DrawText(dc, b.Text.AsSpan(0, _TextDispLen(b)), ref r, c_tff);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfinally {\n\t\t\tbrushDot?.Dispose();\n\t\t\tbrushTriangle?.Dispose();\n\t\t\tif (font != null) {\n\t\t\t\tApi.SelectObject(dc, oldFont);\n\t\t\t\tfont.Dispose();\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (m.tbBorder > 0) {\n\t\t\tg.DrawRectangleInset(BorderColor != default ? (Color)BorderColor : SystemColors.ControlDark, m.tbBorder, rClient);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au/GUI/toolbar/tb sat.cs",
    "content": "using Au.Triggers;\n\nnamespace Au;\n\npublic partial class toolbar {\n\ttoolbar _satellite;\n\ttoolbar _satPlanet;\n\tbool _satVisible;\n\tint _satAnimation;\n\ttimer _satTimer;\n\ttimer _satAnimationTimer;\n\tscreen _screenAHSE;\n\n\t/// <summary>\n\t/// A toolbar attached to this toolbar. Can be <c>null</c>.\n\t/// </summary>\n\t/// <exception cref=\"InvalidOperationException\">The <c>set</c> function throws this exception if the satellite toolbar was attached to another toolbar or was shown as non-satellite toolbar.</exception>\n\t/// <remarks>\n\t/// The satellite toolbar is shown when mouse enters its owner toolbar and hidden when mouse leaves it and its owner. Like an \"auto hide\" feature.\n\t/// A toolbar can have multiple satellite toolbars at different times. A satellite toolbar can be attached/detached multiple times to the same toolbar.\n\t/// </remarks>\n\tpublic toolbar Satellite {\n\t\tget => _satellite;\n\t\tset {\n\t\t\t_ThreadTrap();\n\t\t\tif (value != _satellite) {\n\t\t\t\tif (value == null) {\n\t\t\t\t\t_SatHide();\n\t\t\t\t\t_satellite = null;\n\t\t\t\t\t//and don't clear _satPlanet etc\n\t\t\t\t} else {\n\t\t\t\t\tif (_closed || value._closed) throw new ObjectDisposedException(nameof(toolbar));\n\t\t\t\t\tvar p = value._satPlanet; if (p != this) { if (p != null || value._created) throw new InvalidOperationException(); }\n\t\t\t\t\t_satellite = value;\n\t\t\t\t\t_satellite._satPlanet = this;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/// <summary>\n\t/// If this is a satellite toolbar (<see cref=\"Satellite\"/>), gets its owner toolbar. Else <c>null</c>.\n\t/// </summary>\n\tpublic toolbar SatelliteOwner => _satPlanet;\n\n\tbool _IsSatellite => _satPlanet != null;\n\n\ttoolbar _SatPlanetOrThis => _satPlanet ?? this;\n\n\tvoid _SatMouse() {\n\t\tif (_satellite == null || _satVisible) return;\n\t\t_satVisible = true;\n\n\t\t//print.it(\"show\");\n\t\tif (!_satellite._created) {\n\t\t\tvar owner = _w;\n\t\t\t_satellite._CreateWindow(true, owner, isSatelite: true);\n\t\t\t_satellite._ow = new _OwnerWindow(owner);\n\t\t\t_satellite._ow.a.Add(_satellite);\n\t\t}\n\t\t_SatFollow();\n\t\t_SatShowHide(true, animate: true);\n\n\t\t_satTimer ??= new timer(_SatTimer);\n\t\t_satTimer.Every(100);\n\t}\n\n\tvoid _SatTimer(timer _) {\n\t\tDebug.Assert(IsOpen);\n\t\tif (_inMoveSize || _satellite._inMoveSize) return;\n\n\t\tPOINT p = mouse.xy;\n\t\tint dist = Dpi.Scale(30, _dpi);\n\t\tvar wa = wnd.active;\n\n\t\tRECT ru = default;\n\t\tif (_MouseIsIn(this) || _MouseIsIn(_satellite)) return;\n\t\tif (ru.Contains(p)) return;\n\t\tif (miscInfo.getGUIThreadInfo(out var g, Api.GetCurrentThreadId()) && g.flags.Has(GTIFlags.INMENUMODE)) return;\n\n\t\tbool _MouseIsIn(toolbar tb) {\n\t\t\tvar w = tb._w;\n\t\t\tif (w == wa) return true;\n\t\t\tvar r = w.Rect;\n\t\t\tr.Inflate(dist, dist);\n\t\t\tif (r.Contains(p.x, p.y)) return true;\n\t\t\tru.Union(r);\n\t\t\treturn false;\n\t\t}\n\n\t\t_SatHide(animate: true);\n\t}\n\n\tvoid _SatDestroying() {\n\t\tif (_IsSatellite) _satPlanet.Satellite = null;\n\t\tDebug_.PrintIf(_satellite != null && _satellite.IsOpen, \"_satellite\");\n\t\t//When destroying planet, OS at first destroys satellites (owned windows).\n\t}\n\n\t//Hides _satellite and stops _satTimer.\n\tvoid _SatHide(bool animate = false/*, [CallerMemberName] string cmn=null*/) {\n\t\tif (_satellite == null) return;\n\t\t//print.it(\"hide\", cmn, _satVisible);\n\t\tif (_satVisible) {\n\t\t\t_satVisible = false;\n\t\t\t_satTimer.Stop();\n\t\t\t_SatShowHide(false, animate);\n\t\t} else if (!animate && (_satAnimationTimer?.IsRunning ?? false)) {\n\t\t\t_SatShowHide(false, false);\n\t\t}\n\t}\n\n\t//Shows or hides _satellite and manages animation.\n\tvoid _SatShowHide(bool show, bool animate) {\n\t\tif (!animate || _satellite._transparency != default) {\n\t\t\tvar w = _satellite._w;\n\t\t\tif (show != w.IsVisible) _satellite._SetVisibleL(show);\n\t\t\tif (_satellite._transparency == default) w.SetTransparency(false);\n\t\t\t_satAnimationTimer?.Stop();\n\t\t\t_satAnimation = 0;\n\t\t\treturn;\n\t\t}\n\n\t\t_satAnimationTimer ??= new timer(_ => {\n\t\t\t_satAnimation += _satVisible ? 64 : -32;\n\t\t\tbool stop; if (_satVisible) { if (stop = _satAnimation >= 255) _satAnimation = 255; } else { if (stop = _satAnimation <= 0) _satAnimation = 0; }\n\t\t\tif (stop) {\n\t\t\t\t_satAnimationTimer.Stop();\n\t\t\t\tif (_satAnimation == 0) _satellite._SetVisibleL(false);\n\t\t\t}\n\t\t\tif (_satellite._transparency == default) _satellite._w.SetTransparency(!stop, _satAnimation);\n\t\t});\n\t\t_satAnimationTimer.Now();\n\t\t_satAnimationTimer.Every(30);\n\n\t\tif (show) _satellite._SetVisibleL(true);\n\t}\n\n\tvoid _SatFollow() {\n\t\tif (!_satVisible) return;\n\t\tif (!_satellite._ow.UpdateRect(out bool changed) || !changed) return;\n\t\t_satellite._FollowRect(onFollowOwner: true);\n\t}\n\n\t#region auto-hide owner toolbars\n\n\t/// <summary>\n\t/// Creates a new toolbar and sets its <see cref=\"Satellite\"/> = this.\n\t/// </summary>\n\t/// <returns>The new toolbar.</returns>\n\t/// <param name=\"ctorFlags\"><see cref=\"toolbar\"/> constructor flags.</param>\n\t/// <param name=\"f_\">[](xref:caller_info)</param>\n\t/// <param name=\"l_\">[](xref:caller_info)</param>\n\t/// <exception cref=\"InvalidOperationException\">This toolbar was attached to another toolbar or was shown as non-satellite toolbar.</exception>\n\t/// <remarks>\n\t/// Sets toolbar name = <c>this.Name + \"^\"</c>.\n\t/// If this already is a satellite toolbar, just returns its owner.\n\t/// </remarks>\n\tpublic toolbar AutoHide(TBCtor ctorFlags = 0, [CallerFilePath] string f_ = null, [CallerLineNumber] int l_ = 0) {\n\t\t_ThreadTrap();\n\t\tif (_satPlanet == null) {\n\t\t\t_satPlanet = new toolbar(this.Name + \"^\", ctorFlags, f_, l_) { Satellite = this };\n\t\t\tif (_satPlanet.FirstTime) {\n\t\t\t\t_satPlanet.Size = new(20, 20);\n\t\t\t}\n\t\t\t_satPlanet.AutoSize = false;\n\t\t}\n\t\treturn _satPlanet;\n\t}\n\n\t/// <summary>\n\t/// Creates a new toolbar and sets its <see cref=\"Satellite\"/> = this. Sets properties for showing at a screen edge.\n\t/// </summary>\n\t/// <returns>The new toolbar.</returns>\n\t/// <param name=\"mta\">Mouse edge trigger arguments.</param>\n\t/// <param name=\"rangeStart\"><i>rangeStart</i> and <i>rangeEnd</i> can be used to specify a smaller range of the edge part. For example, you can create 2 toolbars there: one with 0, .5f, other with .5f, 1f.</param>\n\t/// <param name=\"rangeEnd\"></param>\n\t/// <param name=\"thickness\">The visible thickness. Pixels.</param>\n\t/// <param name=\"ctorFlags\"><see cref=\"toolbar\"/> constructor flags.</param>\n\t/// <param name=\"f_\">[](xref:caller_info)</param>\n\t/// <param name=\"l_\">[](xref:caller_info)</param>\n\tpublic toolbar AutoHideScreenEdge(MouseTriggerArgs mta, Coord rangeStart = default, Coord rangeEnd = default, int thickness = 1, TBCtor ctorFlags = 0, [CallerFilePath] string f_ = null, [CallerLineNumber] int l_ = 0) {\n\t\tNot_.Null(mta);\n\t\tif (mta.Trigger.Kind != TMKind.Edge) throw new ArgumentException(\"Not an edge trigger.\");\n\t\treturn AutoHideScreenEdge(mta.Trigger.Edge, mta.Trigger.Screen, rangeStart, rangeEnd, thickness, ctorFlags, f_, l_);\n\t}\n\n\t/// <param name=\"edge\">Screen edge/part.</param>\n\t/// <param name=\"scrn\">Screen. Default: primary.</param>\n\t/// <inheritdoc cref=\"AutoHideScreenEdge(MouseTriggerArgs, Coord, Coord, int, TBCtor, string, int)\"/>\n\tpublic toolbar AutoHideScreenEdge(TMEdge edge, screen scrn = default, Coord rangeStart = default, Coord rangeEnd = default, int thickness = 1, TBCtor ctorFlags = 0, [CallerFilePath] string f_ = null, [CallerLineNumber] int l_ = 0) {\n\t\t_ThreadTrap();\n\t\tvar sh = scrn.Now;\n\t\tvar rs = sh.Rect;\n\n\t\tvar se = edge.ToString(); char se0 = se[0];\n\t\tbool vertical = se0 == 'L' || se0 == 'R';\n\n\t\tTBAnchor anchor = TBAnchor.TopLeft;\n\t\tRECT k = default;\n\t\tif (thickness <= 0) thickness = 1;\n\t\tint offscreen = thickness == 1 ? 1 : 0;\n\t\tswitch (se0) {\n\t\tcase 'T': k.top = -offscreen; break;\n\t\tcase 'R': k.right = -offscreen; anchor = TBAnchor.TopRight; break;\n\t\tcase 'B': k.bottom = -offscreen; anchor = TBAnchor.BottomLeft; break;\n\t\tcase 'L': k.left = -offscreen; break;\n\t\t}\n\t\tint x25 = rs.Width / 4, y25 = rs.Height / 4;\n\t\tbool reverse = false;\n\t\tswitch (edge) {\n\t\tcase TMEdge.TopInCenter50: k.left = x25; break;\n\t\tcase TMEdge.TopInRight25: anchor = TBAnchor.TopRight; reverse = true; break;\n\t\tcase TMEdge.RightInCenter50: k.top = y25; break;\n\t\tcase TMEdge.RightInBottom25: anchor = TBAnchor.BottomRight; reverse = true; break;\n\t\tcase TMEdge.BottomInCenter50: k.left = x25; break;\n\t\tcase TMEdge.BottomInRight25: anchor = TBAnchor.BottomRight; reverse = true; break;\n\t\tcase TMEdge.LeftInCenter50: k.top = y25; break;\n\t\tcase TMEdge.LeftInBottom25: anchor = TBAnchor.BottomLeft; reverse = true; break;\n\t\t}\n\n\t\tint edgeLength = vertical ? rs.Height : rs.Width; if (se.Contains(\"25\")) edgeLength /= 4; else if (se.Contains(\"50\")) edgeLength /= 2;\n\t\tint move = rangeStart.IsEmpty ? 0 : rangeStart.NormalizeInRange(0, edgeLength);\n\t\tint length = rangeEnd.IsEmpty ? edgeLength : Math.Max(0, rangeEnd.NormalizeInRange(0, edgeLength) - move);\n\t\tif (vertical) {\n\t\t\tif (reverse) k.bottom = edgeLength - length - move; else k.top += move;\n\t\t} else {\n\t\t\tif (reverse) k.right = edgeLength - length - move; else k.left += move;\n\t\t}\n\n\t\tvar planet = AutoHide(ctorFlags, f_, l_);\n\t\tplanet._screenAHSE = sh;\n\t\tif (planet.FirstTime || vertical != (planet.Size.Height > planet.Size.Width)) {\n\t\t\tSIZE z1 = vertical ? new(thickness + offscreen, length) : new(length, thickness + offscreen);\n\t\t\t//planet.Size = Dpi.Unscale(z1, sh.Handle); //no, such toolbars aren't DPI-sceled by default\n\t\t\tplanet.Size = z1;\n\t\t}\n\t\tif (planet.FirstTime) {\n\t\t\tplanet.Sizable = false;\n\t\t}\n\t\tplanet.Anchor = anchor;\n\t\tplanet.Offsets = new(k.left, k.top, k.right, k.bottom);\n\t\tplanet.Border = TBBorder.Width1;\n\t\tplanet.NoContextMenu = TBNoMenu.Anchor | TBNoMenu.Border | TBNoMenu.Layout | TBNoMenu.AutoSize;\n\n\t\tthis.Anchor = anchor | (vertical ? TBAnchor.OppositeEdgeX : TBAnchor.OppositeEdgeY);\n\t\tthis.NoContextMenu = TBNoMenu.Anchor;\n\n\t\treturn planet;\n\t}\n\n\t#endregion\n\n}\n"
  },
  {
    "path": "Au/GUI/toolbar/tb types.cs",
    "content": "namespace Au.Types;\n\n/// <summary>\n/// Represents a button or separator in <see cref=\"toolbar\"/>.\n/// </summary>\n/// <remarks>\n/// Most properties cannot be changed while the toolbar is open. Can be changed <see cref=\"MTItem.Tag\"/>, <see cref=\"MTItem.Tooltip\"/>.\n/// </remarks>\npublic class TBItem : MTItem {\n\treadonly toolbar _tb;\n\tinternal SIZE textSize;\n\tinternal readonly TBItemType type;\n\tinternal bool textAlways;\n\tinternal popupMenu menu;\n\t\n\tinternal TBItem(toolbar tb, TBItemType type) {\n\t\t_tb = tb;\n\t\tthis.type = type;\n\t\ttextAlways = type == TBItemType.Group;\n\t}\n\t\n\tinternal bool IsSeparator_ => type == TBItemType.Separator;\n\tinternal bool IsGroup_ => type == TBItemType.Group;\n\tinternal bool IsMenu_ => type == TBItemType.Menu;\n\tinternal bool IsSeparatorOrGroup_ => type is TBItemType.Separator or TBItemType.Group;\n\t\n\t///\n\tpublic TBItemType ItemType => type;\n\t\n\t/// <summary>Gets button action.</summary>\n\tpublic Action<TBItem> Clicked => base.clicked as Action<TBItem>;\n\t\n\t/// <summary>\n\t/// Button text.\n\t/// </summary>\n\t/// <remarks>\n\t/// Changing button text at run time resizes the button and toolbar.\n\t/// </remarks>\n\tpublic new string Text {\n\t\tget => base.Text;\n\t\tset {\n\t\t\t_tb._ThreadTrap();\n\t\t\tif (value != base.Text) {\n\t\t\t\tbase.Text = value;\n\t\t\t\t_tb._AutoSizeNowIfIsOpen(measureText: true);\n\t\t\t}\n\t\t}\n\t}\n}\n\n/// <summary>\n/// Used with <see cref=\"TBItem.ItemType\"/>.\n/// </summary>\npublic enum TBItemType : byte {\n#pragma warning disable 1591 //doc\n\tButton,\n\tMenu,\n\tSeparator,\n\tGroup,\n#pragma warning restore\n}\n\n/// <summary>\n/// Used with <see cref=\"toolbar.MiscFlags\"/>.\n/// </summary>\n[Flags]\npublic enum TBFlags {\n\t/// <summary>\n\t/// Activate the owner window when the toolbar clicked. Default.\n\t/// </summary>\n\tActivateOwnerWindow = 1,\n\t\n\t/// <summary>\n\t/// Hide the toolbar when a full-screen window is active. Default.\n\t/// </summary>\n\tHideWhenFullScreen = 2,\n\t\n\t//rejected: use SHQueryUserNotificationState to detect also presentation mode etc. Too slow, eg 1300 mcs, which is 100-500 times slower than wnd.isFullScreen.\n\t//\tHideWhenFullScreenActive\n\t//\tHideWhenFullScreenRunning (QUNS_BUSY) (in primary screen only)\n\t//\tHideWhenPresentation (QUNS_PRESENTATION_MODE)\n\t//\tHideWhenD3DFullScreen (QUNS_RUNNING_D3D_FULL_SCREEN). I guess it is for games. Not tested.\n\t//\tBut problem: no QUNS_BUSY if fullscreen in non-primary screen.\n\t//\tAlso found this comment (not tested): \"It also only works when the Explorer shell is running.\"\n}\n\n/// <summary>\n/// Used with <see cref=\"toolbar.Border\"/>.\n/// </summary>\npublic enum TBBorder {\n\t//note: don't reorder.\n\t\n\t/// <summary>No border.</summary>\n\tNone,\n\t\n\t/// <summary>1 pixel border.</summary>\n\tWidth1,\n\t\n\t/// <summary>1 pixel border + 1 pixel padding.</summary>\n\tWidth2,\n\t\n\t/// <summary>1 pixel border + 2 pixels padding.</summary>\n\tWidth3,\n\t\n\t/// <summary>1 pixel border + 3 pixels padding.</summary>\n\tWidth4,\n\t\n\t/// <summary>3D border.</summary>\n\tThreeD,\n\t\n\t/// <summary>Standard window border.</summary>\n\tThick,\n\t\n\t/// <summary>Title bar and standard window border.</summary>\n\tCaption,\n\t\n\t/// <summary>Title bar, <b>X</b> button and standard window border.</summary>\n\tCaptionX,\n}\n\n/// <summary>\n/// Used with <see cref=\"toolbar.Anchor\"/>.\n/// </summary>\npublic enum TBAnchor {\n\t//top 1, bottom 2, left 4, right 8\n\t\n\t/// <summary>\n\t/// Anchors are top and left edges. Default.\n\t/// </summary>\n\tTopLeft = 1 | 4,\n\t\n\t/// <summary>\n\t/// Anchors are top and right edges.\n\t/// </summary>\n\tTopRight = 1 | 8,\n\t\n\t/// <summary>\n\t/// Anchors are bottom and left edges.\n\t/// </summary>\n\tBottomLeft = 2 | 4,\n\t\n\t/// <summary>\n\t/// Anchors are bottom and right edges.\n\t/// </summary>\n\tBottomRight = 2 | 8,\n\t\n\t/// <summary>\n\t/// Anchors are top, left and right edges. The toolbar is resized horizontally when resizing its owner.\n\t/// </summary>\n\tTopLR = 1 | 4 | 8,\n\t\n\t/// <summary>\n\t/// Anchors are bottom, left and right edges. The toolbar is resized horizontally when resizing its owner.\n\t/// </summary>\n\tBottomLR = 2 | 4 | 8,\n\t\n\t/// <summary>\n\t/// Anchors are left, top and bottom edges. The toolbar is resized vertically when resizing its owner.\n\t/// </summary>\n\tLeftTB = 4 | 1 | 2,\n\t\n\t/// <summary>\n\t/// Anchors are right, top and bottom edges. The toolbar is resized vertically when resizing its owner.\n\t/// </summary>\n\tRightTB = 8 | 1 | 2,\n\t\n\t/// <summary>\n\t/// Anchors are all edges. The toolbar is resized when resizing its owner.\n\t/// </summary>\n\tAll = 15,\n\t\n\t/// <summary>\n\t/// Use owner's opposite left/right edge than specified. In other words, attach toolbar's left edge to owner's right edge or vice versa.\n\t/// This flag is for toolbars that normally are outside of the owner rectangle (at the left or right).\n\t/// This flag cannot be used with <c>TopLR</c>, <c>BottomLR</c>, <c>All</c>.\n\t/// </summary>\n\tOppositeEdgeX = 32,\n\t\n\t/// <summary>\n\t/// Use owner's opposite top/bottom edge than specified. In other words, attach toolbar's top edge to owner's bottom edge or vice versa.\n\t/// This flag is for toolbars that normally are outside of the owner rectangle (above or below).\n\t/// This flag cannot be used with <c>LeftTB</c>, <c>RightTB</c>, <c>All</c>.\n\t/// </summary>\n\tOppositeEdgeY = 64,\n\t\n\t/// <summary>\n\t/// Anchor is screen, not owner window. Don't move the toolbar together with its owner window.\n\t/// </summary>\n\tScreen = 128,\n}\n\nstatic partial class TBExt_ {\n\tinternal static bool HasTop(this TBAnchor a) => 0 != ((int)a & 1);\n\tinternal static bool HasBottom(this TBAnchor a) => 0 != ((int)a & 2);\n\tinternal static bool HasLeft(this TBAnchor a) => 0 != ((int)a & 4);\n\tinternal static bool HasRight(this TBAnchor a) => 0 != ((int)a & 8);\n\tinternal static bool OppositeX(this TBAnchor a) => 0 != ((int)a & 32);\n\tinternal static bool OppositeY(this TBAnchor a) => 0 != ((int)a & 64);\n\tinternal static bool OfScreen(this TBAnchor a) => 0 != ((int)a & 128);\n\tinternal static TBAnchor WithoutFlags(this TBAnchor a) => a & TBAnchor.All;\n}\n\n//rejected. Instead use System.Windows.Size. It loads 1 assembly in 1.5 ms and does not add much process memory.\n//public record struct SizeD\n//{\n//\t[System.Text.Json.Serialization.JsonInclude]\n//\tpublic double width;\n//\t[System.Text.Json.Serialization.JsonInclude]\n//\tpublic double height;\n\n//\tpublic SizeD(double width, double height) { this.width = width; this.height = height; }\n//}\n\n/// <summary>\n/// Used with <see cref=\"toolbar.Offsets\"/>.\n/// </summary>\npublic record struct TBOffsets {\n\t/// <summary>\n\t/// Horizontal distance from the owner's left edge (right if <see cref=\"TBAnchor.OppositeEdgeX\"/>) to the toolbar's left edge.\n\t/// </summary>\n\tpublic double Left { get; set; }\n\t\n\t/// <summary>\n\t/// Vertical distance from the owner's top edge (bottom if <see cref=\"TBAnchor.OppositeEdgeY\"/>) to the toolbar's top edge.\n\t/// </summary>\n\tpublic double Top { get; set; }\n\t\n\t/// <summary>\n\t/// Horizontal distance from the toolbar's right edge to the owner's right edge (left if <see cref=\"TBAnchor.OppositeEdgeX\"/>).\n\t/// </summary>\n\tpublic double Right { get; set; }\n\t\n\t/// <summary>\n\t/// Vertical distance from the toolbar's bottom edge to the owner's bottom edge (top if <see cref=\"TBAnchor.OppositeEdgeY\"/>).\n\t/// </summary>\n\tpublic double Bottom { get; set; }\n\t\n\t/// <summary>\n\t/// Sets all properties.\n\t/// </summary>\n\tpublic TBOffsets(double left, double top, double right, double bottom) {\n\t\tLeft = left; Top = top; Right = right; Bottom = bottom;\n\t}\n}\n\n/// <summary>\n/// Reasons to hide a toolbar. Used with <see cref=\"toolbar.Hide\"/>.\n/// </summary>\n[Flags]\npublic enum TBHide {\n\t/// <summary>Owner window is hidden, minimized, etc.</summary>\n\tOwner = 1,\n\t\n\t/// <summary>A full-screen window is active. See flag <see cref=\"TBFlags.HideWhenFullScreen\"/>.</summary>\n\tFullScreen = 2,\n\t\n\t//Satellite = 128, //no, _SetVisible and this enum aren't used with satellites\n\t\n\t/// <summary>This and bigger flag values can be used by callers for any purpose. Value 0x10000.</summary>\n\tUser = 0x10000,\n}\n\n/// <summary>\n/// Used with <see cref=\"toolbar.Layout\"/>.\n/// </summary>\npublic enum TBLayout {\n\t/// <summary>Default layout. Buttons are in single row. Wrapped when exceeds maximal row width. More rows can be added with <see cref=\"toolbar.Group\"/>.</summary>\n\tHorizontalWrap,\n\t\n\t/// <summary>Buttons are in single column, like in a popup menu. Separators are horizontal.</summary>\n\tVertical, //TODO3: if some buttons don't fit, add overflow drop-down menu. Or scrollbar; or add VerticalScroll.\n\t\n\t//\t/// <summary>Buttons are in single row. When it exceeds maximal row width, buttons are moved to a drop-down menu. More rows can be added with <see cref=\"toolbar.Group\"/>.</summary>\n\t//\tHorizontal,//TODO3\n}\n\n/// <summary>\n/// Used with <see cref=\"toolbar.NoContextMenu\"/>.\n/// </summary>\n[Flags]\npublic enum TBNoMenu {\n#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member\n\tMenu = 1,\n\tEdit = 1 << 1,\n\tAnchor = 1 << 2,\n\tLayout = 1 << 3,\n\tBorder = 1 << 4,\n\tSizable = 1 << 5,\n\tAutoSize = 1 << 6,\n\tMiscFlags = 1 << 7,\n\tToolbars = 1 << 8,\n\tHelp = 1 << 9,\n\tClose = 1 << 10,\n\tFile = 1 << 11,\n\tText = 1 << 12,\n#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member\n}\n\n/// <summary>\n/// Flags for <see cref=\"toolbar\"/> constructor.\n/// </summary>\n[Flags]\npublic enum TBCtor {\n\t/// <summary>\n\t/// Don't load saved settings. Delete the settings file of the toolbar, if exists.\n\t/// </summary>\n\tResetSettings = 1,\n\t\n\t/// <summary>\n\t/// Don't load and save settings. No file will be created or opened.\n\t/// </summary>\n\tDontSaveSettings = 2,\n}\n\n/// <summary>\n/// Used with <see cref=\"toolbar.DpiScaling\"/>.\n/// </summary>\npublic struct TBScaling {\n\t///\n\tpublic TBScaling(bool? size, bool? offsets) { this.size = size; this.offsets = offsets; }\n\t\n\t/// <summary>\n\t/// Scale toolbar size and related properties.\n\t/// If default (<c>null</c>), scales size, except of empty toolbars created by <see cref=\"toolbar.AutoHideScreenEdge\"/>.\n\t/// </summary>\n\tpublic bool? size;\n\t\n\t/// <summary>\n\t/// Scale toolbar offsets. See <see cref=\"toolbar.Offsets\"/>.\n\t/// If default (<c>null</c>), scales offsets, except when anchor is screen (not window etc).\n\t/// </summary>\n\tpublic bool? offsets;\n}\n\n/// <summary>\n/// Used with <see cref=\"toolbar.Metrics\"/> and <see cref=\"toolbar.defaultMetrics\"/>.\n/// </summary>\n/// <param name=\"ButtonPaddingX\">Button padding left and right.</param>\n/// <param name=\"ButtonPaddingY\">Button padding top and bottom.</param>\n/// <remarks>\n/// All values are in logical pixels (1 pixel when DPI is 100%).\n/// </remarks>\npublic record class TBMetrics(int ButtonPaddingX = 0, int ButtonPaddingY = 0);\n\n/// <summary>\n/// Used with <see cref=\"toolbar.Show(wnd, ITBOwnerObject)\"/>.\n/// </summary>\n/// <remarks>\n/// Allows a toolbar to follow an object in the owner window, for example a UI element or image. Or to hide in certain conditions.\n/// Define a class that implements this interface. Create a variable of that class and pass it to <see cref=\"toolbar.Show(wnd, ITBOwnerObject)\"/>.\n/// The interface functions are called every 250 ms, sometimes more frequently. Not called when the owner window is invisible or cloaked or minimized.\n/// </remarks>\npublic interface ITBOwnerObject {\n\t/// <summary>\n\t/// Returns <c>false</c> to close the toolbar.\n\t/// </summary>\n\t/// <remarks>\n\t/// Not called if the owner window is invisible or cloaked or minimized.\n\t/// The default implementation returns <c>true</c>.\n\t/// </remarks>\n\tbool IsAlive => true;\n\t\n\t/// <summary>\n\t/// Returns <c>false</c> to hide the toolbar temporarily.\n\t/// </summary>\n\t/// <remarks>\n\t/// Not called if the owner window is invisible or cloaked or minimized.\n\t/// The default implementation returns <c>true</c>.\n\t/// </remarks>\n\tbool IsVisible => true;\n\t\n\t/// <summary>\n\t/// Gets object rectangle.\n\t/// </summary>\n\t/// <returns><c>false</c> if failed.</returns>\n\t/// <param name=\"r\">Rectangle in screen coordinates.</param>\n\t/// <remarks>\n\t/// Not called if the owner window is invisible or cloaked or minimized or if <see cref=\"IsVisible\"/> returned <c>false</c>.\n\t/// </remarks>\n\tbool GetRect(out RECT r);\n}"
  },
  {
    "path": "Au/GUI/toolbar/tb util.cs",
    "content": "namespace Au;\n\npublic partial class toolbar {\n#if DEBUG\n\t/// <summary>\n\t/// This is a debug-only function. Returns the caller method name. Use like <c>print.it(caller(), ...)</c>.\n\t/// </summary>\n\tstatic string caller([CallerMemberName] string m_ = null) => m_;\n#endif\n\n\tbool _SetDpi() {\n\t\tDebug.Assert(_os != null || !OwnerWindow.Is0);\n\t\treturn _SetDpi(_os != null ? _os.Screen.Dpi : screen.of(OwnerWindow).Dpi);\n\t}\n\t\n\tbool _SetDpi(int dpi) {\n\t\tif (dpi == _dpi) return false;\n\t\t_dpi = dpi;\n\t\t_dpiF = _dpi / 96d;\n\t\treturn true;\n\t}\n\n\tbool _NeedScaling(bool offsets) {\n\t\tif (_dpi == 96) return false;\n\t\tif (offsets) return DpiScaling.offsets ?? _os == null;\n\t\treturn DpiScaling.size ?? _screenAHSE.IsEmpty;\n\t}\n\n\tint _Scale(double d, bool offsets) {\n\t\tif (_NeedScaling(offsets)) d *= _dpiF;\n\t\treturn d.ToInt();\n\t}\n\n\tdouble _Unscale(int i, bool offsets) => _NeedScaling(offsets) ? i / _dpiF : i;\n\n\tSIZE _Scale(System.Windows.Size z) => _NeedScaling(false) ? Dpi.Scale(z, _dpi) : SIZE.From(z, true);\n\n\tSystem.Windows.Size _Unscale(SIZE z) => _NeedScaling(false) ? Dpi.Unscale(z, _dpi) : z;\n\n\t//System.Windows.Size _Unscale(int width, int height) => _Unscale(new SIZE(width, height));\n\n\tstatic double _Limit(double d) {\n\t\tif (double.IsNaN(d)) throw new ArgumentException();\n\t\tconst int c_max = 2_000_000; //for max *1024 DPI scaling\n\t\treturn Math.Clamp(d, -c_max, c_max);\n\t}\n\n\t/// <summary>\n\t/// Measures, resizes and invalidates the toolbar now if need.\n\t/// </summary>\n\tinternal void _AutoSizeNowIfIsOpen(bool measureText = false) {\n\t\tif (!IsOpen) return;\n\t\tif (measureText) _MeasureText();\n\t\t_Resize(_Measure());\n\t\tApi.InvalidateRect(_w);\n\t}\n\n\tvoid _Resize(SIZE clientSize/*, bool ignoreAnchor=false*/) {\n\t\tvar r = new RECT(0, 0, clientSize.width, clientSize.height);\n\t\tDpi.AdjustWindowRectEx(_dpi, ref r, _w.Style, _w.ExStyle);\n\t\tint cx = r.Width, cy = r.Height;\n\t\tvar a = Anchor.WithoutFlags();\n\t\tif (/*ignoreAnchor ||*/ a == TBAnchor.All) {\n\t\t\t_w.ResizeL(cx, cy);\n\t\t} else {\n\t\t\tvar rw = _w.Rect;\n\t\t\tint dx = cx - rw.Width, dy = cy - rw.Height;\n\t\t\tif (!a.HasLeft()) rw.left -= dx; else if (!a.HasRight()) rw.right += dx;\n\t\t\tif (!a.HasTop()) rw.top -= dy; else if (!a.HasBottom()) rw.bottom += dy;\n\t\t\t_w.MoveL(rw);\n\t\t}\n\t}\n\n\tvoid _Invalidate(TBItem ti = null) {\n\t\t_ThreadTrap();\n\t\tif (!IsOpen) return;\n\t\tif (ti != null) Api.InvalidateRect(_w, ti.rect);\n\t\telse Api.InvalidateRect(_w);\n\t}\n\n\tvoid _Invalidate(int i) => _Invalidate(_a[i]);\n\n\tstatic WS _BorderStyle(TBBorder b) => b switch {\n\t\tTBBorder.ThreeD => WS.DLGFRAME,\n\t\tTBBorder.Thick => WS.THICKFRAME,\n\t\tTBBorder.Caption => WS.CAPTION | WS.THICKFRAME,\n\t\tTBBorder.CaptionX => WS.CAPTION | WS.THICKFRAME | WS.SYSMENU,\n\t\t_ => 0\n\t};\n\n\t/// <summary>\n\t/// Returns DPI-scaled border thickness in client area. Returns 0 if <i>b</i> is not <c>TBBorder.Width1</c> ... <c>TBBorder.Width4</c>.\n\t/// </summary>\n\tstatic int _BorderPadding(TBBorder b, int dpi) => b >= TBBorder.Width1 && b <= TBBorder.Width4 ? Dpi.Scale((int)b, dpi) : 0;\n\n\t/// <summary>\n\t/// Returns DPI-scaled border thickness in client area. Returns 0 if <i>b</i> is not <c>TBBorder.Width1</c> ... <c>TBBorder.Width4</c>.\n\t/// </summary>\n\tint _BorderPadding(TBBorder? b = null, bool unscaled = false) => _BorderPadding(b ?? Border, unscaled ? 96 : _dpi);\n\n\tstatic TBAnchor _GetInvalidAnchorFlags(TBAnchor anchor) {\n\t\treturn anchor.WithoutFlags() switch {\n\t\t\tTBAnchor.TopLeft or TBAnchor.TopRight or TBAnchor.BottomLeft or TBAnchor.BottomRight => 0,\n\t\t\tTBAnchor.TopLR or TBAnchor.BottomLR => TBAnchor.OppositeEdgeX,\n\t\t\tTBAnchor.LeftTB or TBAnchor.RightTB => TBAnchor.OppositeEdgeY,\n\t\t\t_ => TBAnchor.OppositeEdgeX | TBAnchor.OppositeEdgeY,\n\t\t};\n\t}\n\n\tvoid _CreatedTrap(string error = null) {\n\t\tif (_created) throw new InvalidOperationException(error);\n\t}\n}"
  },
  {
    "path": "Au/GUI/toolbar/toolbar.cs",
    "content": "using Au.Triggers;\n\nnamespace Au;\n\n//rejected: warn if using common controls version 5. Eg tooltips don't work. The same with menus.\n//\tToo many problems: 1. How to warn? Message box? 2. Need to also warn if no supported OS, DPI awareness, etc. Load/parse manifest in module initializer? 3. Maybe the app does not want to use these features (eg high DPI). 4. Maybe not possible to use correct manifest, eg when used in a scripting app. 5. Etc.\n//\tNever mind. The required manifest is documented.\n\n/// <summary>\n/// Floating toolbar.\n/// Can be attached to windows of other programs.\n/// </summary>\n/// <remarks>\n/// To create toolbar code can be used menu <b>TT > New toolbar</b>.\n/// \n/// Not thread-safe. All functions must be called from the same thread that created the <see cref=\"toolbar\"/> object, except where documented otherwise. Note: item actions by default run in other threads; see <see cref=\"MTBase.ActionThread\"/>.\n/// </remarks>\npublic partial class toolbar : MTBase {\n\trecord class _Settings : JSettings {\n\t\tpublic static _Settings Load(string file, bool useDefault) => Load<_Settings>(file, useDefault);\n\t\t\n\t\tpublic TBAnchor anchor = TBAnchor.TopLeft;\n\t\tpublic TBLayout layout;\n\t\tpublic TBBorder border = TBBorder.Width2;\n\t\tpublic bool dispText = true, sizable = true, autoSize = true;\n\t\tpublic TBFlags miscFlags = TBFlags.HideWhenFullScreen | TBFlags.ActivateOwnerWindow;\n\t\tpublic System.Windows.Size size = new(150, 24);\n\t\tpublic double wrapWidth;\n\t\tpublic TBOffsets offsets; // = new(150, 5, 7, 7);\n\t\tpublic int screenx, screeny;\n\t}\n\t\n\treadonly _Settings _sett;\n\treadonly List<TBItem> _a = new();\n\tbool _created;\n\tbool _closed;\n\tbool _topmost; //no owner, or owner is topmost\n\tdouble _dpiF; //DPI scaling factor, eg 1.5 for 144 dpi\n\t\n\tstatic int s_treadId;\n\t\n\t/// <param name=\"name\">\n\t/// Toolbar name. Must be valid filename.\n\t/// Used for: toolbar window name, settings file name, <see cref=\"find\"/>, some other functions.\n\t/// If <c>null</c>, uses the caller function's name if available, else exception.\n\t/// </param>\n\t/// <param name=\"flags\"></param>\n\t/// <param name=\"f_\">[](xref:caller_info)</param>\n\t/// <param name=\"l_\">[](xref:caller_info)</param>\n\t/// <param name=\"m_\">[](xref:caller_info)</param>\n\t/// <param name=\"settingsFile\"><c>null</c> or full path of the settings file of this toolbar.</param>\n\t/// <exception cref=\"ArgumentException\">Invalid <i>name</i>.</exception>\n\t/// <remarks>\n\t/// Each toolbar has a settings file, where are saved its position, size and context menu settings. This function reads the file if exists, ie if settings changed in the past. See <see cref=\"getSettingsFilePath\"/>. If fails, prints a warning and uses default settings.\n\t/// \n\t/// Sets properties:\n\t/// - <see cref=\"MTBase.ActionThread\"/> = <c>true</c>.\n\t/// - <see cref=\"MTBase.ExtractIconPathFromCode\"/> = <c>true</c>.\n\t/// </remarks>\n\tpublic toolbar(string name = null, TBCtor flags = 0,\n\t\t[CallerFilePath] string f_ = null, [CallerLineNumber] int l_ = 0, [CallerMemberName] string m_ = null, string settingsFile = null)\n\t\t: base(name, f_, l_, m_) {\n\t\t\n\t\tif (s_treadId == 0) s_treadId = _threadId; else if (_threadId != s_treadId) print.warning(\"All toolbars should be in single thread. Multiple threads use more CPU. If using triggers, insert this code before adding toolbar triggers: <code>Triggers.Options.ThreadOfTriggers();</code> or <code>Triggers.Options.ThreadThis();</code>\");\n\t\t\n\t\tsettingsFile = flags.Has(TBCtor.DontSaveSettings) ? null : (settingsFile ?? getSettingsFilePath(_name));\n\t\t_sett = _Settings.Load(settingsFile, flags.Has(TBCtor.ResetSettings));\n\t\t\n\t\t_offsets = _sett.offsets; //TODO3: don't use saved offsets if this toolbar was free and now is owned or vice versa. Because the position is irrelevent and may be far/offscreen. It usually happens when testing.\n\t\t\n\t\tActionThread = true;\n\t\tExtractIconPathFromCode = true;\n\t}\n\t\n\t/// <summary>\n\t/// Gets the toolbar window.\n\t/// </summary>\n\tpublic wnd Hwnd => _w;\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if the toolbar is open. False if closed or <see cref=\"Show\"/> still not called.\n\t/// </summary>\n\tpublic bool IsOpen => _created && !_closed;\n\t\n\t/// <summary>\n\t/// Gets the name of the toolbar.\n\t/// </summary>\n\tpublic string Name => _name;\n\t\n\t///\n\tpublic override string ToString() => _IsSatellite ? \"    \" + Name : Name; //the indentation is for the list in the Active toolbars dialog\n\t\n\t/// <summary>\n\t/// True if this toolbar started with default settings. False if loaded saved settings from file.\n\t/// </summary>\n\t/// <seealso cref=\"getSettingsFilePath\"/>\n\t/// <seealso cref=\"TBCtor\"/>\n\tpublic bool FirstTime => !_sett.LoadedFile;\n\t\n\t#region static functions\n\t\n\t/// <summary>\n\t/// Gets full path of toolbar's settings file. The file may exist or not.\n\t/// </summary>\n\t/// <param name=\"toolbarName\">Toolbar name. If this string is a full path, returns this string.</param>\n\t/// <remarks>\n\t/// Path: <c>folders.Workspace + $@\"\\.toolbars\\{toolbarName}.json\"</c>. If <see cref=\"folders.Workspace\"/> is <c>null</c>, uses <see cref=\"folders.ThisAppDataRoaming\"/>.\n\t/// </remarks>\n\tpublic static string getSettingsFilePath(string toolbarName) {\n\t\tif (toolbarName.NE()) throw new ArgumentException(\"Empty name\");\n\t\tif (pathname.isFullPath(toolbarName)) return toolbarName;\n\t\tstring s = folders.Workspace.Path;\n\t\tif (s != null) return s + @\"\\.toolbars\\\" + toolbarName + \".json\";\n\t\ts = s_settingsDir;\n\t\tif (s == null) {\n\t\t\ts = folders.ThisAppDataRoaming + \".toolbars\";\n\t\t\tif (!filesystem.exists(s).Directory) { //fbc. Previously was folders.ThisAppDocuments, but a library should not create directories there.\n\t\t\t\tbool nac = folders.noAutoCreate;\n\t\t\t\tfolders.noAutoCreate = true;\n\t\t\t\tvar s2 = folders.ThisAppDocuments + \".toolbars\";\n\t\t\t\tfolders.noAutoCreate = nac;\n\t\t\t\tif (filesystem.exists(s2).Directory) s = s2;\n\t\t\t}\n\t\t\ts_settingsDir = s += @\"\\\";\n\t\t}\n\t\treturn s + toolbarName + \".json\";\n\t}\n\tstatic string s_settingsDir;\n\t\n\t/// <summary>\n\t/// Finds an open toolbar by <see cref=\"Name\"/>.\n\t/// </summary>\n\t/// <returns><c>null</c> if not found or closed or never shown (<see cref=\"Show\"/> not called).</returns>\n\t/// <remarks>\n\t/// Finds only toolbars created in the same script and thread.\n\t/// \n\t/// Does not find satellite toolbars. Use this code: <c>toolbar.find(\"owner toolbar\").Satellite</c>\n\t/// </remarks>\n\tpublic static toolbar find(string name) => _Manager._atb.Find(o => o.Name == name);\n\t\n\tinternal static void TriggerActionEndedInToolbarUnfriendlyThread_() {\n\t\tif (_Manager._atb.Count > 0)\n\t\t\tprint.warning(\"Toolbars in wrong thread. Insert this code before adding toolbar triggers: <code>Triggers.Options.ThreadOfTriggers();</code> or <code>Triggers.Options.ThreadThis();</code>\", -1);\n\t}\n\t\n\t#endregion\n\t\n\t#region add item\n\t\n\tvoid _Add(TBItem item, string text, Delegate click, MTImage image, int l_, string f_) {\n\t\t_ThreadTrap();\n\t\t_CreatedTrap(_closed ? null : \"cannot add items while the toolbar is open. To add to submenu, use the submenu variable.\");\n\t\titem.Set_(this, text, click, image, l_, _sourceFile == null ? null : f_);\n\t\t_a.Add(item);\n\t\tLast = item;\n\t}\n\t\n\t/// <summary>\n\t/// Adds button.\n\t/// Same as <see cref=\"this[string, MTImage, int, string]\"/>.\n\t/// </summary>\n\t/// <param name=\"text\">Text. Or <c>\"Text|Tooltip\"</c>, or <c>\"|Tooltip\"</c>, or <c>\"Text|\"</c>. Separator can be <c>\"|\"</c> or <c>\"\\0 \"</c> (then <c>\"|\"</c> isn't a separator). To always display text regardless of <see cref=\"DisplayText\"/>, append <c>\"\\a\"</c>, like <c>\"Text\\a\"</c> or <c>\"Text\\a|Tooltip\"</c>.</param>\n\t/// <param name=\"click\">Action called when the button clicked.</param>\n\t/// <param name=\"image\">Image. Read here: <see cref=\"MTBase\"/>.</param>\n\t/// <param name=\"l_\">[](xref:caller_info)</param>\n\t/// <param name=\"f_\">[](xref:caller_info)</param>\n\t/// <remarks>\n\t/// More properties can be specified later (set properties of the returned <see cref=\"TBItem\"/> or use <see cref=\"Items\"/>) or before (<see cref=\"MTBase.ActionThread\"/>, <see cref=\"MTBase.ActionException\"/>, <see cref=\"MTBase.ExtractIconPathFromCode\"/>, <see cref=\"MTBase.PathInTooltip\"/>).\n\t/// </remarks>\n\tpublic TBItem Add(string text, Action<TBItem> click, MTImage image = default, [CallerLineNumber] int l_ = 0, [CallerFilePath] string f_ = null) {\n\t\tvar item = new TBItem(this, TBItemType.Button);\n\t\t_Add(item, text, click, image, l_, f_);\n\t\treturn item;\n\t}\n\t\n\t/// <summary>\n\t/// Adds button.\n\t/// Same as <see cref=\"Add(string, Action{TBItem}, MTImage, int, string)\"/>.\n\t/// </summary>\n\t/// <value>Action called when the button clicked.</value>\n\t/// <remarks>\n\t/// More properties can be specified later (set properties of <see cref=\"Last\"/> <see cref=\"TBItem\"/> or use <see cref=\"Items\"/>) or before (<see cref=\"MTBase.ActionThread\"/>, <see cref=\"MTBase.ActionException\"/>, <see cref=\"MTBase.ExtractIconPathFromCode\"/>, <see cref=\"MTBase.PathInTooltip\"/>).\n\t/// </remarks>\n\t/// <example>\n\t/// These two are the same.\n\t/// <code><![CDATA[\n\t/// tb.Add(\"Example\", o => print.it(o));\n\t/// tb[\"Example\"] = o => print.it(o);\n\t/// ]]></code>\n\t/// These four are the same.\n\t/// <code><![CDATA[\n\t/// var b = tb.Add(\"Example\", o => print.it(o)); b.Tooltip=\"tt\";\n\t/// tb.Add(\"Example\", o => print.it(o)).Tooltip=\"tt\";\n\t/// tb[\"Example\"] = o => print.it(o); var b=tb.Last; b.Tooltip=\"tt\";\n\t/// tb[\"Example\"] = o => print.it(o); tb.Last.Tooltip=\"tt\";\n\t/// ]]></code>\n\t/// </example>\n\t/// <inheritdoc cref=\"Add(string, Action{TBItem}, MTImage, int, string)\"/>\n\tpublic Action<TBItem> this[string text, MTImage image = default, [CallerLineNumber] int l_ = 0, [CallerFilePath] string f_ = null] {\n\t\tset { Add(text, value, image, l_, f_); }\n\t}\n\t\n\t//CONSIDER: AddCheck, AddRadio.\n\t\n\t/// <summary>\n\t/// Adds button with drop-down menu.\n\t/// </summary>\n\t/// <param name=\"menu\">Action that adds menu items. Called whenever the button clicked.</param>\n\t/// <remarks>\n\t/// The submenu is a <see cref=\"popupMenu\"/> object. It inherits these properties of this toolbar: <see cref=\"MTBase.ExtractIconPathFromCode\"/>, <see cref=\"MTBase.ActionException\"/>, <see cref=\"MTBase.ActionThread\"/>, <see cref=\"MTBase.PathInTooltip\"/>.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// tb.Menu(\"Menu\", m => {\n\t/// \tm[\"M1\"]=o=>print.it(o);\n\t/// \tm[\"M2\"]=o=>print.it(o);\n\t/// });\n\t/// ]]></code>\n\t/// </example>\n\t/// <inheritdoc cref=\"Add(string, Action{TBItem}, MTImage, int, string)\"/>\n\tpublic TBItem Menu(string text, Action<popupMenu> menu, MTImage image = default, [CallerLineNumber] int l_ = 0, [CallerFilePath] string f_ = null) {\n\t\tvar item = new TBItem(this, TBItemType.Menu);\n\t\t_Add(item, text, menu, image, l_, f_);\n\t\treturn item;\n\t}\n\t\n\t/// <summary>\n\t/// Adds button with drop-down menu.\n\t/// </summary>\n\t/// <param name=\"menu\">Func that returns the menu. Called whenever the button clicked.</param>\n\t/// <remarks>\n\t/// The caller creates the menu (creates the <see cref=\"popupMenu\"/> object and adds items) and can reuse it many times. Other overload does not allow to create <see cref=\"popupMenu\"/> and reuse same object.\n\t/// The submenu does not inherit properties of this toolbar.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// var m = new popupMenu(); m.AddCheck(\"C1\"); m.AddCheck(\"C2\");\n\t/// t.Menu(\"Menu\", () => m);\n\t/// ]]></code>\n\t/// </example>\n\t/// <inheritdoc cref=\"Add(string, Action{TBItem}, MTImage, int, string)\"/>\n\tpublic TBItem Menu(string text, Func<popupMenu> menu, MTImage image = default, [CallerLineNumber] int l_ = 0, [CallerFilePath] string f_ = null) {\n\t\tvar item = new TBItem(this, TBItemType.Menu);\n\t\t_Add(item, text, menu, image, l_, f_);\n\t\treturn item;\n\t}\n\t\n\t/// <summary>\n\t/// Adds new vertical separator. Horizontal if vertical toolbar.\n\t/// </summary>\n\tpublic TBItem Separator() {\n\t\tint i = _a.Count - 1;\n\t\tif (i < 0 || _a[i].IsGroup_) throw new InvalidOperationException(\"first item is separator\");\n\t\tvar item = new TBItem(this, TBItemType.Separator);\n\t\t_Add(item, null, null, default, 0, null);\n\t\treturn item;\n\t}\n\t\n\t/// <summary>\n\t/// Adds new horizontal separator, optionally with text.\n\t/// </summary>\n\t/// <param name=\"text\">Text. Or <c>\"Text|Tooltip\"</c>, or <c>\"|Tooltip\"</c>, or <c>\"Text|\"</c>. Separator can be <c>\"|\"</c> or <c>\"\\0 \"</c> (then <c>\"|\"</c> isn't a separator).</param>\n\tpublic TBItem Group(string text = null) {\n\t\tvar item = new TBItem(this, TBItemType.Group);\n\t\t_Add(item, text, null, default, 0, null);\n\t\treturn item;\n\t}\n\t\n\t/// <summary>\n\t/// Gets the last added item.\n\t/// </summary>\n\tpublic TBItem Last { get; private set; }\n\t\n\t/// <summary>\n\t/// Gets added buttons.\n\t/// </summary>\n\t/// <remarks>\n\t/// Allows to set properties of multiple buttons in single place instead of after each \"add button\" code line.\n\t/// Skips separators and groups.\n\t/// </remarks>\n\tpublic IEnumerable<TBItem> Items {\n\t\tget {\n\t\t\t_ThreadTrap();\n\t\t\tforeach (var v in _a) {\n\t\t\t\tif (!v.IsSeparatorOrGroup_) yield return v;\n\t\t\t}\n\t\t}\n\t}\n\t\n\t#endregion\n\t\n\t#region show, close, owner\n\t\n\t/// <summary>\n\t/// Shows the toolbar.\n\t/// </summary>\n\t/// <param name=\"screen\">\n\t/// Attach to this screen. For example a screen index (0 the primary, 1 the first non-primary, and so on). Example: <c>screen.index(1)</c>.\n\t/// If not specified, the toolbar will be attached to the screen where it is now or where will be moved later.\n\t/// Don't use this parameter if this toolbar was created by <see cref=\"AutoHideScreenEdge\"/>, because then screen is already known.\n\t/// </param>\n\t/// <exception cref=\"ArgumentException\">The toolbar was created by <see cref=\"AutoHideScreenEdge\"/>, and now screen specified again.</exception>\n\t/// <exception cref=\"InvalidOperationException\"><c>Show</c> already called.</exception>\n\t/// <remarks>\n\t/// The toolbar will be moved when the screen moved or resized.\n\t/// </remarks>\n\tpublic void Show(screen screen = default) {\n\t\tif (!_screenAHSE.IsEmpty) { if (screen.IsEmpty) screen = _screenAHSE; else throw new ArgumentException(); }\n\t\t_Show(false, default, null, screen);\n\t}\n\t\n\t/// <summary>\n\t/// Shows the toolbar and attaches to a window.\n\t/// </summary>\n\t/// <param name=\"ownerWindow\">Window or control. Can belong to any process.</param>\n\t/// <param name=\"clientArea\">Let the toolbar position be relative to the client area of the window.</param>\n\t/// <exception cref=\"InvalidOperationException\"><c>Show</c> already called.</exception>\n\t/// <exception cref=\"ArgumentException\"><i>ownerWindow</i> is 0.</exception>\n\t/// <remarks>\n\t/// The toolbar will be above the window in the Z order; moved when the window moved or resized; hidden when the window hidden, cloaked or minimized; destroyed when the window destroyed.\n\t/// </remarks>\n\tpublic void Show(wnd ownerWindow, bool clientArea = false) {\n\t\t_followClientArea = clientArea;\n\t\t_Show(true, ownerWindow, null, _screenAHSE);\n\t}\n\t\n\t/// <summary>\n\t/// Shows the toolbar and attaches to an object in a window.\n\t/// </summary>\n\t/// <param name=\"ownerWindow\">Window that contains the object. Can be control. Can belong to any process.</param>\n\t/// <param name=\"oo\">A variable of a user-defined class that implements <see cref=\"ITBOwnerObject\"/> interface. It provides object location, visibility, etc.</param>\n\t/// <exception cref=\"InvalidOperationException\"><c>Show</c> already called.</exception>\n\t/// <exception cref=\"ArgumentException\"><i>ownerWindow</i> is 0.</exception>\n\t/// <remarks>\n\t/// The toolbar will be above the window in the Z order; moved when the object or window moved or resized; hidden when the object or window hidden, cloaked or minimized; destroyed when the object or window destroyed.\n\t/// </remarks>\n\tpublic void Show(wnd ownerWindow, ITBOwnerObject oo) => _Show(true, ownerWindow, oo, default);\n\t\n\t/// <summary>\n\t/// Shows the toolbar.\n\t/// If <i>ta</i> is <see cref=\"WindowTriggerArgs\"/>, attaches the toolbar to the trigger window.\n\t/// Else if <i>ta</i> != <c>null</c>, calls <see cref=\"TriggerArgs.DisableTriggerUntilClosed(toolbar)\"/>.\n\t/// </summary>\n\tpublic void Show(TriggerArgs ta) {\n\t\tif (ta is WindowTriggerArgs wta) {\n\t\t\tShow(wta.Window);\n\t\t} else {\n\t\t\tShow();\n\t\t\tta?.DisableTriggerUntilClosed(this);\n\t\t}\n\t}\n\t\n\t//used for normal toolbars, not for satellite toolbars\n\tvoid _Show(bool owned, wnd owner, ITBOwnerObject oo, screen screen) {\n\t\t_ThreadTrap();\n\t\t_CreatedTrap(\"this toolbar is already open\");\n\t\t\n\t\twnd c = default;\n\t\tif (owned) {\n\t\t\tif (owner.Is0) throw new ArgumentException();\n\t\t\tvar w = owner.Window; if (w.Is0) return;\n\t\t\tif (w != owner) { c = owner; owner = w; }\n\t\t\tif (!_screenAHSE.IsEmpty) _sett.anchor |= TBAnchor.Screen;\n\t\t}\n\t\t\n\t\t_CreateWindow(owned, owner, screen);\n\t\t_Manager.Add(this, owner, c, oo);\n\t}\n\t\n\t//used for normal and satellite toolbars\n\tvoid _CreateWindow(bool owned, wnd owner, screen screen = default, bool isSatelite = false) {\n\t\t_topmost = !owned || owner.IsTopmost;\n\t\tif (!owned || Anchor.OfScreen()) _os = new _OwnerScreen(this, screen); else screen = screen.of(owner);\n\t\t\n\t\t_RegisterWinclass();\n\t\tif (_os != null) _SetDpi(); else _SetDpi(screen.Dpi); //OwnerWindow still not set\n\t\t_Images(false);\n\t\t_MeasureText();\n\t\tvar size = _Measure();\n\t\tvar style = WS.POPUP | WS.CLIPCHILDREN | _BorderStyle(_sett.border);\n\t\tvar estyle = WSE.TOOLWINDOW | WSE.NOACTIVATE;\n\t\tif (_topmost) estyle |= WSE.TOPMOST;\n\t\tvar r = screen.Rect; //create in center of screen, to minimize possibility of DPI change when setting final position\n\t\tr = new(r.CenterX - size.width / 2, r.CenterY - size.height / 2, size.width, size.height);\n\t\tDpi.AdjustWindowRectEx(_dpi, ref r, style, estyle);\n\t\tWndUtil.CreateWindow(_WndProc, true, \"Au.toolbar\", _name, style, estyle, r.left, r.top, r.Width, r.Height, isSatelite ? owner : default);\n\t\t_created = true;\n\t}\n\t\n\t/// <summary>\n\t/// Destroys the toolbar window.\n\t/// </summary>\n\t/// <remarks>\n\t/// Can be called from any thread.\n\t/// Does nothing if not open.\n\t/// </remarks>\n\tpublic void Close() {\n\t\tif (_w.Is0) return;\n\t\tif (_IsOtherThread) _w.Post(Api.WM_CLOSE); else Api.DestroyWindow(_w);\n\t\tif (_hasCachedImages) _ImageCache.Cleared -= _UpdateCachedImages;\n\t}\n\t\n\t/// <summary>\n\t/// When the toolbar window destroyed.\n\t/// </summary>\n\tpublic event Action Closed;\n\t\n\tprivate protected override void _WmNcdestroy() {\n\t\t_closed = true;\n\t\t_Manager.Remove(this);\n\t\t_SatDestroying();\n\t\t_sett.Dispose();\n\t\tbase._WmNcdestroy();\n\t\tClosed?.Invoke();\n\t}\n\t\n\t/// <summary>\n\t/// Adds or removes a reason to temporarily hide the toolbar. The toolbar is hidden if at least one reason exists. See also <see cref=\"Close\"/>.\n\t/// </summary>\n\t/// <param name=\"hide\"><c>true</c> to hide (add <i>reason</i>), <c>false</c> to show (remove <i>reason</i>).</param>\n\t/// <param name=\"reason\">A user-defined reason to hide/unhide. Can be <see cref=\"TBHide.User\"/> or a bigger value, eg <c>(TBHide)0x20000</c>, <c>(TBHide)0x40000</c>.</param>\n\t/// <exception cref=\"InvalidOperationException\">\n\t/// - The toolbar was never shown (<see cref=\"Show\"/> not called).\n\t/// - It is a satellite toolbar.\n\t/// - Wrong thread. Must be called from the same thread that created the toolbar. See <see cref=\"MTBase.ActionThread\"/>.\n\t/// </exception>\n\t/// <exception cref=\"ArgumentOutOfRangeException\"><i>reason</i> is less than <see cref=\"TBHide.User\"/>.</exception>\n\t/// <remarks>\n\t/// Toolbars are automatically hidden when the owner window is hidden, minimized, etc. This function can be used to hide toolbars for other reasons.\n\t/// </remarks>\n\tpublic void Hide(bool hide, TBHide reason) {\n\t\t_ThreadTrap();\n\t\tif (!_created || _IsSatellite) throw new InvalidOperationException();\n\t\tif (0 != ((int)reason & 0xffff)) throw new ArgumentOutOfRangeException();\n\t\t_SetVisible(!hide, reason);\n\t}\n\t\n\t/// <summary>\n\t/// Gets current reasons why the toolbar is hidden. Returns 0 if not hidden.\n\t/// </summary>\n\t/// <remarks>\n\t/// Not used with satellite toolbars.\n\t/// </remarks>\n\tpublic TBHide Hidden => _hide;\n\t\n\tvoid _SetVisible(bool show, TBHide reason) {\n\t\t//print.it(show, reason);\n\t\tif (show) {\n\t\t\tif (_hide == 0) return;\n\t\t\t_hide &= ~reason;\n\t\t\tif (_hide != 0) return;\n\t\t} else {\n\t\t\tvar h = _hide;\n\t\t\t_hide |= reason;\n\t\t\tif (h != 0) return;\n\t\t}\n\t\t_SetVisibleL(show);\n\t}\n\tTBHide _hide;\n\t\n\tvoid _SetVisibleL(bool show) => _w.ShowL(show);\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if the toolbar is attached to a window or an object in a window.\n\t/// </summary>\n\tpublic bool IsOwned => _ow != null;\n\t\n\t/// <summary>\n\t/// Returns the owner top-level window.\n\t/// Returns <c>default(wnd)</c> if the toolbar is not owned. See <see cref=\"IsOwned\"/>.\n\t/// </summary>\n\tpublic wnd OwnerWindow => _ow?.w ?? default;\n\t\n\t#endregion\n\t\n\t#region wndproc, context menu\n\t\n\tstatic void _RegisterWinclass() {\n\t\tif (0 == Interlocked.Exchange(ref s_winclassRegistered, 1)) {\n\t\t\tWndUtil.RegisterWindowClass(\"Au.toolbar\"/*, etc: new() { style = Api.CS_HREDRAW | Api.CS_VREDRAW, mCursor = MCursor.Arrow }*/);\n\t\t}\n\t}\n\tstatic int s_winclassRegistered;\n\t\n\tunsafe nint _WndProc(wnd w, int msg, nint wParam, nint lParam) {\n\t\t//WndUtil.PrintMsg(w, msg, wParam, lParam);\n\t\tbool activatedOwner = false;\n\t\t\n\t\tswitch (msg) {\n\t\tcase Api.WM_LBUTTONDOWN or Api.WM_RBUTTONDOWN or Api.WM_MBUTTONDOWN:\n\t\t\tvar tb1 = _SatPlanetOrThis;\n\t\t\tif (tb1.IsOwned && tb1.MiscFlags.Has(TBFlags.ActivateOwnerWindow)) {\n\t\t\t\tvar wo = tb1.OwnerWindow;\n\t\t\t\tif (!wo.IsActive) activatedOwner = wo.ActivateL();\n\t\t\t}\n\t\t\tbreak;\n\t\tcase Api.WM_MOUSEMOVE or Api.WM_NCMOUSEMOVE:\n\t\t\t_SatMouse();\n\t\t\tbreak;\n\t\t}\n\t\t\n\t\tswitch (msg) {\n\t\tcase Api.WM_NCCREATE:\n\t\t\t_WmNccreate(w);\n\t\t\tif (_transparency != default) _w.SetTransparency(true, _transparency.opacity, _transparency.colorKey);\n\t\t\tbreak;\n\t\tcase Api.WM_NCDESTROY:\n\t\t\t_WmNcdestroy();\n\t\t\t//PROBLEM: not called if thread ends without closing the toolbar window.\n\t\t\t//\tThen saves settings only on process exit. Not if process terminated.\n\t\t\t//\tIn most cases it isn't a problem because saves settings every 2 s (IIRC).\n\t\t\tbreak;\n\t\tcase Api.WM_ERASEBKGND:\n\t\t\treturn 0;\n\t\tcase Api.WM_PAINT:\n\t\t\tusing (BufferedPaint bp = new(w, true)) {\n\t\t\t\tvar dc = bp.DC;\n\t\t\t\tusing var g = System.Drawing.Graphics.FromHdc(dc);\n\t\t\t\tg.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;\n\t\t\t\t_WmPaint(dc, g, bp.Rect, bp.UpdateRect);\n\t\t\t}\n\t\t\treturn 0;\n\t\tcase Api.WM_MOUSEACTIVATE:\n\t\t\treturn Api.MA_NOACTIVATE;\n\t\tcase Api.WM_MOUSEMOVE:\n\t\t\t_WmMousemove(lParam);\n\t\t\treturn 0;\n\t\tcase Api.WM_MOUSELEAVE:\n\t\t\t_WmMouseleave();\n\t\t\treturn 0;\n\t\tcase Api.WM_LBUTTONDOWN:\n\t\t\t_WmMouselbuttondown(lParam, activatedOwner);\n\t\t\treturn 0;\n\t\tcase Api.WM_CONTEXTMENU or Api.WM_NCRBUTTONUP:\n\t\t\t_WmContextmenu();\n\t\t\tbreak;\n\t\tcase Api.WM_NCHITTEST:\n\t\t\tif (_WmNchittest(lParam, out int hitTest)) return hitTest; //returns a hittest code to move or resize if need\n\t\t\tbreak;\n\t\tcase Api.WM_NCLBUTTONDOWN:\n\t\t\tint ht = (int)wParam;\n\t\t\tif (ht == Api.HTCAPTION || (ht >= Api.HTSIZEFIRST && ht <= Api.HTSIZELAST)) {\n\t\t\t\t//workaround for: Windows tries to activate this window when moving or sizing it, unless this process is not allowed to activate windows.\n\t\t\t\t//\tThis window then may not become the foreground window, but it receives wm_activateapp, wm_activate, wm_setfocus, and is moved to the top of Z order.\n\t\t\t\t//\ttested: LockSetForegroundWindow does not work.\n\t\t\t\t//\tThis code better would be under WM_SYSCOMMAND, but then works only when sizing. When moving, activates before WM_SYSCOMMAND.\n\t\t\t\tusing (WindowsHook.ThreadCbt(d => d.code == HookData.CbtEvent.ACTIVATE)) {\n\t\t\t\t\t//also let arrow keys move/resize by 1 pixel.\n\t\t\t\t\t//\tIn active window Ctrl+arrow work automatically, but toolbars aren't active.\n\t\t\t\t\t//\tNever mind: does not work if active window has higher UAC IL. Even with Ctrl.\n\t\t\t\t\tusing var k1 = new RegisteredHotkey(); k1.Register(100, KKey.Left, _w);\n\t\t\t\t\tusing var k2 = new RegisteredHotkey(); k2.Register(101, KKey.Right, _w);\n\t\t\t\t\tusing var k3 = new RegisteredHotkey(); k3.Register(102, KKey.Up, _w);\n\t\t\t\t\tusing var k4 = new RegisteredHotkey(); k4.Register(103, KKey.Down, _w);\n\t\t\t\t\t\n\t\t\t\t\tApi.DefWindowProc(w, msg, wParam, lParam);\n\t\t\t\t}\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase Api.WM_ENTERSIZEMOVE:\n\t\t\t_SetInMoveSize(true);\n\t\t\tbreak;\n\t\tcase Api.WM_EXITSIZEMOVE:\n\t\t\t_SetInMoveSize(false);\n\t\t\tbreak;\n\t\tcase Api.WM_WINDOWPOSCHANGING:\n\t\t\t_WmWindowPosChanging(ref *(Api.WINDOWPOS*)lParam);\n\t\t\tbreak;\n\t\tcase RegisteredHotkey.WM_HOTKEY:\n\t\t\tint hkid = (int)wParam - 100;\n\t\t\tif (hkid >= 0 && hkid <= 3) {\n\t\t\t\tPOINT p = mouse.xy;\n\t\t\t\tApi.SetCursorPos(p.x + hkid switch { 0 => -1, 1 => 1, _ => 0 }, p.y + hkid switch { 2 => -1, 3 => 1, _ => 0 });\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase Api.WM_GETOBJECT:\n\t\t\tif (_WmGetobject(wParam, lParam, out var r1)) return r1;\n\t\t\tbreak;\n\t\tcase Api.WM_USER + 50: //posted by acc dodefaultaction\n\t\t\tif (!_closed) {\n\t\t\t\tint i = (int)wParam;\n\t\t\t\tif ((uint)i < _a.Count) _Click(i, false);\n\t\t\t}\n\t\t\treturn 0;\n\t\tcase Api.WM_USER + 51:\n\t\t\t_toolbarsDialog();\n\t\t\t[MethodImpl(MethodImplOptions.NoInlining)] //don't load System.Windows.Forms assembly\n\t\t\tstatic void _toolbarsDialog() => toolbarsDialog();\n\t\t\treturn 0;\n\t\t}\n\t\t\n\t\tvar R = Api.DefWindowProc(w, msg, wParam, lParam);\n\t\t\n\t\tswitch (msg) {\n\t\tcase Api.WM_WINDOWPOSCHANGED:\n\t\t\t_WmWindowPosChanged(in *(Api.WINDOWPOS*)lParam);\n\t\t\tbreak;\n\t\tcase Api.WM_GETDPISCALEDSIZE:\n\t\t\treturn _WmGetDpiScaledSize(wParam, lParam);\n\t\tcase Api.WM_DPICHANGED:\n\t\t\t_WmDpiChanged(wParam, lParam);\n\t\t\tbreak;\n\t\tcase Api.WM_DISPLAYCHANGE:\n\t\t\t_WmDisplayChange();\n\t\t\tbreak;\n\t\t\t//case Api.WM_SETTINGCHANGE:\n\t\t\t//\t_WmSettingChange(wParam, lParam);\n\t\t\t//\tbreak;\n\t\t}\n\t\t\n\t\treturn R;\n\t}\n\t\n\t#endregion\n\t\n\t#region input, context menu\n\t\n\tint _HitTest(POINT p) {\n\t\tfor (int i = 0; i < _a.Count; i++) {\n\t\t\tif (_a[i].rect.Contains(p)) return i;\n\t\t}\n\t\treturn -1;\n\t}\n\t\n\tunsafe void _WmMousemove(nint lParam) {\n\t\tif (_iClick < 0) {\n\t\t\tvar p = Math2.NintToPOINT(lParam);\n\t\t\tint i = _HitTest(p);\n\t\t\tif (i != _iHot) {\n\t\t\t\tif (_iHot >= 0) _Invalidate(_iHot);\n\t\t\t\tif ((_iHot = i) >= 0) {\n\t\t\t\t\t_Invalidate(_iHot);\n\t\t\t\t\tvar b = _a[i];\n\t\t\t\t\t_SetTooltip(b, b.rect, lParam);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (_iHot >= 0 != _trackMouseEvent) _trackMouseEvent = Api.TrackMouseLeave(_w, _iHot >= 0) && _iHot >= 0;\n\t\t}\n\t}\n\tint _iHot = -1, _iClick = -1;\n\tbool _trackMouseEvent, _noHotClick;\n\t\n\tvoid _WmMouseleave() {\n\t\t_trackMouseEvent = false;\n\t\tif (_iHot >= 0) { _Invalidate(_iHot); _iHot = -1; }\n\t}\n\t\n\tvoid _WmMouselbuttondown(nint lParam, bool activatedOwner) {\n\t\tvar mod = keys.gui.getMod();\n\t\tif (mod == 0) { //click button\n\t\t\tvar p = Math2.NintToPOINT(lParam);\n\t\t\tint i = _HitTest(p);\n\t\t\tif (i < 0) return;\n\t\t\t\n\t\t\tif (activatedOwner && _a[i].IsMenu_) { //let manager process OBJECT_REORDER. And let owner process WM_ACTIVATE. Else the menu sometimes disappears.\n\t\t\t\ttimer.after(50, _ => { if (IsOpen) _Click(i, true); });\n\t\t\t} else {\n\t\t\t\t_Click(i, true);\n\t\t\t}\n\t\t\t\n\t\t\t//} else if(mod==KMod.Shift) { //move toolbar\n\t\t\t//\tvar p=mouse.xy;\n\t\t\t//\tDragDropUtil.SimpleDragDrop(_w, MButtons.Left, d => {\n\t\t\t//\t\tif (d.Msg.message != Api.WM_MOUSEMOVE) return;\n\t\t\t//\t\tvar v=mouse.xy; if(v==p) return;\n\t\t\t//\t\tint dx=v.x-p.x, dy=v.y-p.y;\n\t\t\t//\t\tp=v;\n\t\t\t//\t\tvar r=_w.Rect;\n\t\t\t//\t\t_w.MoveL(r.left+dx, r.top+dy);\n\t\t\t//\t});\n\t\t}\n\t}\n\tint _menuClosedIndex; long _menuClosedTime;\n\t\n\tvoid _Click(int i, bool real) {\n\t\tvar b = _a[i];\n\t\tif (b.clicked == null) return;\n\t\tif (b.IsMenu_) {\n\t\t\tif (i == _menuClosedIndex && perf.ms - _menuClosedTime < 100) return;\n\t\t\tpopupMenu m = null;\n\t\t\tif (b.clicked is Action<popupMenu> menu) {\n\t\t\t\tm = new popupMenu(null, NoContextMenu.Has(TBNoMenu.Edit) ? null : b.sourceFile, b.sourceLine);\n\t\t\t\t_CopyProps(m);\n\t\t\t\tmenu(m);\n\t\t\t} else if (b.clicked is Func<popupMenu> func) {\n\t\t\t\tm = func();\n\t\t\t}\n\t\t\tif (m == null) return;\n\t\t\t\n\t\t\tvar r = b.rect; _w.MapClientToScreen(ref r);\n\t\t\tm.Show(PMFlags.AlignRectBottomTop, new(r.left, r.bottom), r, owner: _w);\n\t\t\t_menuClosedIndex = i; _menuClosedTime = perf.ms;\n\t\t\t//info: before wm_lbuttondown the menu is already closed and its message loop ended. Previous Show returns before new Show.\n\t\t} else {\n\t\t\tif (real) {\n\t\t\t\tbool ok = false;\n\t\t\t\ttry {\n\t\t\t\t\t_Invalidate(_iClick = i);\n\t\t\t\t\tok = WndUtil.DragLoop(_w, MButtons.Left, d => {\n\t\t\t\t\t\tif (d.msg.message != Api.WM_MOUSEMOVE) return;\n\t\t\t\t\t\tint j = _HitTest(Math2.NintToPOINT(d.msg.lParam));\n\t\t\t\t\t\tif ((j == i) == _noHotClick) {\n\t\t\t\t\t\t\t_noHotClick ^= true;\n\t\t\t\t\t\t\t_Invalidate(i);\n\t\t\t\t\t\t}\n\t\t\t\t\t}) && !_noHotClick;\n\t\t\t\t}\n\t\t\t\tfinally {\n\t\t\t\t\t_iClick = -1; _noHotClick = false;\n\t\t\t\t\t_Invalidate(i);\n\t\t\t\t}\n\t\t\t\tif (!ok) return;\n\t\t\t}\n\t\t\t//print.it(\"click\", b);\n\t\t\tif (b.actionThread) run.thread(() => _ExecItem(), background: false); //thread start speed: 250 mcs\n\t\t\telse _ExecItem();\n\t\t\tvoid _ExecItem() {\n\t\t\t\tvar action = b.clicked as Action<TBItem>;\n\t\t\t\ttry { action(b); }\n\t\t\t\tcatch (Exception ex) when (!b.actionException) { print.warning(ex); }\n\t\t\t}\n\t\t}\n\t}\n\t\n\tvoid _WmContextmenu() {\n\t\tvar no = NoContextMenu;\n\t\tif (no.Has(TBNoMenu.Menu)) return;\n\t\tif (_a.Count == 0 && _satellite != null) no |= TBNoMenu.Layout | TBNoMenu.AutoSize | TBNoMenu.Text;\n\t\t\n\t\tvar p = _w.MouseClientXY;\n\t\tint i = _HitTest(p);\n\t\tvar item = i < 0 ? null : _a[i];\n\t\t\n\t\tvar m = new popupMenu();\n\t\t\n\t\tif (!no.Has(TBNoMenu.Edit | TBNoMenu.File)) {\n\t\t\tstring sf; int sl; if (item != null) { sf = item.sourceFile; sl = item.sourceLine; } else { sf = _sourceFile; sl = _sourceLine; }\n\t\t\tvar (canEdit, canGo, goText) = MTItem.CanEditOrGoToFile_(sf, item);\n\t\t\tif (!no.Has(TBNoMenu.Edit) && canEdit) m[\"Edit toolbar\"] = o => ScriptEditor.Open(sf, sl);\n\t\t\tif (!no.Has(TBNoMenu.File) && canGo) m[goText] = o => item.GoToFile_();\n\t\t}\n\t\tif (!no.Has(TBNoMenu.Close)) m.Add(\"Close\", o => _SatPlanetOrThis.Close());\n\t\tif (m.Last != null) m.Separator();\n\t\t\n\t\tif (!no.Has(TBNoMenu.Anchor)) {\n\t\t\tm.Submenu(\"Anchor\", m => {\n\t\t\t\t_AddAnchor(TBAnchor.TopLeft);\n\t\t\t\t_AddAnchor(TBAnchor.TopRight);\n\t\t\t\t_AddAnchor(TBAnchor.BottomLeft);\n\t\t\t\t_AddAnchor(TBAnchor.BottomRight);\n\t\t\t\t_AddAnchor(TBAnchor.TopLR);\n\t\t\t\t_AddAnchor(TBAnchor.BottomLR);\n\t\t\t\t_AddAnchor(TBAnchor.LeftTB);\n\t\t\t\t_AddAnchor(TBAnchor.RightTB);\n\t\t\t\t_AddAnchor(TBAnchor.All);\n\t\t\t\tm.Separator();\n\t\t\t\t_AddAnchor(TBAnchor.OppositeEdgeX);\n\t\t\t\t_AddAnchor(TBAnchor.OppositeEdgeY);\n\t\t\t\tif (IsOwned) _AddAnchor(TBAnchor.Screen);\n\t\t\t\t\n\t\t\t\tvoid _AddAnchor(TBAnchor an) {\n\t\t\t\t\tvar k = an <= TBAnchor.All\n\t\t\t\t\t\t? m.AddRadio(an.ToString(), Anchor.WithoutFlags() == an, _ => Anchor = (Anchor & ~TBAnchor.All) | an)\n\t\t\t\t\t\t: m.AddCheck(an.ToString(), Anchor.Has(an), _ => Anchor ^= an, disable: _GetInvalidAnchorFlags(Anchor).Has(an));\n\t\t\t\t\tif (_IsSatellite) k.Tooltip = \"Note: You may want to set anchor of the owner toolbar instead. Anchor of this auto-hide toolbar is relative to the owner toolbar.\";\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t\tif (!no.Has(TBNoMenu.Layout)) {\n\t\t\tm.Submenu(\"Layout\", m => {\n\t\t\t\t_AddLayout(TBLayout.HorizontalWrap);\n\t\t\t\t_AddLayout(TBLayout.Vertical);\n\t\t\t\t//_AddLayout(TBLayout.Horizontal);\n\t\t\t\t\n\t\t\t\tvoid _AddLayout(TBLayout tl) {\n\t\t\t\t\tm.AddRadio(tl.ToString(), tl == Layout, _ => Layout = tl);\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t\tif (!no.Has(TBNoMenu.Border)) {\n\t\t\tm.Submenu(\"Border\", m => {\n\t\t\t\t_AddBorder(TBBorder.None);\n\t\t\t\t_AddBorder(TBBorder.Width1);\n\t\t\t\t_AddBorder(TBBorder.Width2);\n\t\t\t\t_AddBorder(TBBorder.Width3);\n\t\t\t\t_AddBorder(TBBorder.Width4);\n\t\t\t\t_AddBorder(TBBorder.ThreeD);\n\t\t\t\t_AddBorder(TBBorder.Thick);\n\t\t\t\t_AddBorder(TBBorder.Caption);\n\t\t\t\t_AddBorder(TBBorder.CaptionX);\n\t\t\t\t\n\t\t\t\tvoid _AddBorder(TBBorder b) {\n\t\t\t\t\tm.AddRadio(b.ToString(), b == Border, _ => Border = b);\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t\tif (!no.Has(TBNoMenu.Sizable | TBNoMenu.AutoSize | TBNoMenu.Text | TBNoMenu.MiscFlags)) {\n\t\t\tm.Submenu(\"More\", m => {\n\t\t\t\tif (!no.Has(TBNoMenu.Sizable)) m.AddCheck(\"Sizable\", Sizable, _ => Sizable ^= true);\n\t\t\t\tif (!no.Has(TBNoMenu.AutoSize)) m.AddCheck(\"Auto-size\", AutoSize, _ => AutoSize ^= true);\n\t\t\t\tif (!no.Has(TBNoMenu.Text)) m.AddCheck(\"Display text\", DisplayText, _ => DisplayText ^= true);\n\t\t\t\tif (!no.Has(TBNoMenu.MiscFlags)) {\n\t\t\t\t\t_AddFlag(TBFlags.HideWhenFullScreen);\n\t\t\t\t\tif (_SatPlanetOrThis.IsOwned) _AddFlag(TBFlags.ActivateOwnerWindow);\n\t\t\t\t\t\n\t\t\t\t\tvoid _AddFlag(TBFlags f) {\n\t\t\t\t\t\tvar tb = _SatPlanetOrThis;\n\t\t\t\t\t\tm.AddCheck(_EnumToString(f), tb.MiscFlags.Has(f), _ => tb.MiscFlags ^= f);\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tstatic string _EnumToString(Enum e) {\n\t\t\t\t\t\tvar s = e.ToString().RxReplace(@\"(?<=[^A-Z])[A-Z]\", m => \" \" + m.Value.Lower());\n\t\t\t\t\t\t//s = s.Replace(\"Dont\", \"Don't\");\n\t\t\t\t\t\treturn s;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t\t\n\t\tif (!no.Has(TBNoMenu.Toolbars | TBNoMenu.Help) && m.Last != null && !m.Last.IsSeparator) m.Separator();\n\t\tif (!no.Has(TBNoMenu.Toolbars)) m.Add(\"Toolbars\", o => toolbarsDialog());\n\t\tif (!no.Has(TBNoMenu.Help)) m[\"How to\"] = _ => dialog.showInfo(\"How to\",\n@\"Move toolbar: Shift+drag.\n\nResize toolbar: drag border. Cannot resize if in context menu is unchecked or unavailable More > Sizable; or if checked Border > None.\n\nMove or resize precisely: start to move or resize but don't move the mouse. Instead release Shift and press arrow keys. Finally release the mouse button.\n\");\n\t\t\n\t\tif (m.Last != null) m.Show();\n\t}\n\t\n\tbool _WmNchittest(nint xy, out int ht) {\n\t\tht = 0;\n\t\tif (keys.gui.getMod() == KMod.Shift) { //move\n\t\t\tht = Api.HTCAPTION;\n\t\t} else { //resize?\n\t\t\tif (Border == TBBorder.None || (!Sizable && Border < TBBorder.Thick)) return false;\n\t\t\tvar (x, y) = Math2.NintToPOINT(xy);\n\t\t\tif (Sizable) {\n\t\t\t\t_w.GetWindowInfo_(out var k);\n\t\t\t\tRECT r = k.rcWindow;\n\t\t\t\tint b = Border >= TBBorder.ThreeD ? k.cxWindowBorders : (_a.Count > 0 ? _BorderPadding() : Dpi.Scale(6, _dpi)); //make bigger if no buttons. Eg if auto-hide-at-screen-edge, border 1 is difficult to resize.\n\t\t\t\tint bx = Math.Min(b, r.Width / 2), by = Math.Min(b, r.Height / 2);\n\t\t\t\tif (bx == 0) bx = 1;\n\t\t\t\tif (by == 0) by = 1;\n\t\t\t\t//print.it(bx, by);\n\t\t\t\tint x1 = r.left + bx, x2 = --r.right - bx, y1 = r.top + by, y2 = --r.bottom - by;\n\t\t\t\tif (r.Width > bx * 8 && r.Height > by * 8) { //if toolbar isn't small, in corners allow to resize both width and height at the same time\n\t\t\t\t\tif (x < x1) ht = y < y1 ? Api.HTTOPLEFT : (y > y2 ? Api.HTBOTTOMLEFT : Api.HTLEFT);\n\t\t\t\t\telse if (x > x2) ht = y < y1 ? Api.HTTOPRIGHT : (y > y2 ? Api.HTBOTTOMRIGHT : Api.HTRIGHT);\n\t\t\t\t\telse if (y < y1) ht = Api.HTTOP;\n\t\t\t\t\telse if (y > y2) ht = Api.HTBOTTOM;\n\t\t\t\t\telse return false;\n\t\t\t\t} else if (r.Width >= r.Height) { //in corners prefer width\n\t\t\t\t\tif (x < x1) ht = Api.HTLEFT;\n\t\t\t\t\telse if (x > x2) ht = Api.HTRIGHT;\n\t\t\t\t\telse if (y < y1) ht = Api.HTTOP;\n\t\t\t\t\telse if (y > y2) ht = Api.HTBOTTOM;\n\t\t\t\t\telse return false;\n\t\t\t\t} else { //in corners prefer height\n\t\t\t\t\tif (y < y1) ht = Api.HTTOP;\n\t\t\t\t\telse if (y > y2) ht = Api.HTBOTTOM;\n\t\t\t\t\telse if (x < x1) ht = Api.HTLEFT;\n\t\t\t\t\telse if (x > x2) ht = Api.HTRIGHT;\n\t\t\t\t\telse return false;\n\t\t\t\t}\n\t\t\t} else { //disable resizing if border is natively sizable\n\t\t\t\tif (Border < TBBorder.Thick) return false;\n\t\t\t\t_w.GetWindowInfo_(out var k);\n\t\t\t\tk.rcWindow.Inflate(-k.cxWindowBorders, -k.cyWindowBorders);\n\t\t\t\tif (k.rcWindow.Contains(x, y)) return false;\n\t\t\t\tht = Api.HTBORDER;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\t\n\t#endregion\n\t\n\t#region properties\n\t\n\t/// <summary>\n\t/// Whether to DPI-scale toolbar size and offsets.\n\t/// Default: scale size; scale offsets if anchor is not screen.\n\t/// </summary>\n\t/// <remarks>\n\t/// The unit of measurement of <see cref=\"Size\"/>, <see cref=\"Offsets\"/> and some other properties depends on whether scaling is used for that property. If scaling is used, the unit is logical pixels; it is 1/96 inch regardless of screen DPI. If scaling not used, the unit is physical pixels. Screen DPI can be changed in Windows Settings; when it is 100%, logical and physical pixels are equal.\n\t/// </remarks>\n\tpublic TBScaling DpiScaling { get; set; }\n\t\n\t/// <summary>\n\t/// Toolbar width and height without non-client area when <see cref=\"AutoSize\"/> <c>false</c>.\n\t/// </summary>\n\t/// <remarks>\n\t/// Non-client area is border and title bar when <see cref=\"Border\"/> is <c>ThreeD</c>, <c>Thick</c>, <c>Caption</c> or <c>CaptionX</c>.\n\t/// \n\t/// The unit of measurement depends on <see cref=\"DpiScaling\"/>.\n\t/// \n\t/// This property is updated when resizing the toolbar. It is saved.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// t.Size = new(300, 40);\n\t/// ]]></code>\n\t/// </example>\n\tpublic System.Windows.Size Size {\n\t\tget => _sett.size;\n\t\tset {\n\t\t\t_ThreadTrap();\n\t\t\tif (value != _sett.size) {\n\t\t\t\tvalue = new(_Limit(value.Width), _Limit(value.Height));\n\t\t\t\t_sett.size = value;\n\t\t\t\tif (IsOpen && !AutoSize) _Resize(_Scale(value));\n\t\t\t}\n\t\t\tif (!_followedOnce) _preferSize = true;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Whether the border can be used to resize the toolbar.\n\t/// Default <c>true</c>.\n\t/// </summary>\n\t/// <remarks>\n\t/// This property is in the context menu and is saved.\n\t/// </remarks>\n\tpublic bool Sizable {\n\t\tget => _sett.sizable;\n\t\tset => _sett.sizable = value;\n\t}\n\t\n\t/// <summary>\n\t/// Automatically resize the toolbar to make all buttons visible.\n\t/// Default <c>true</c>.\n\t/// </summary>\n\t/// <remarks>\n\t/// This property is in the context menu and is saved.\n\t/// </remarks>\n\tpublic bool AutoSize {\n\t\tget => _sett.autoSize;\n\t\tset {\n\t\t\t_ThreadTrap();\n\t\t\tif (value != _sett.autoSize) {\n\t\t\t\t_sett.autoSize = value;\n\t\t\t\t_AutoSizeNowIfIsOpen();\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// When <see cref=\"AutoSize\"/> is <c>true</c>, this is the preferred width at which buttons are moved to the next row. Unlimited if 0.\n\t/// </summary>\n\t/// <remarks>\n\t/// The unit of measurement depends on <see cref=\"DpiScaling\"/>.\n\t/// \n\t/// This property is updated when the user resizes the toolbar while <see cref=\"AutoSize\"/> is <c>true</c>. It is saved.\n\t/// \n\t/// If layout of this toolbar is vertical, just sets max width.\n\t/// </remarks>\n\tpublic double AutoSizeWrapWidth {\n\t\tget => _sett.wrapWidth;\n\t\tset {\n\t\t\t_ThreadTrap();\n\t\t\tvalue = value > 0 ? _Limit(value) : 0;\n\t\t\tif (value != _sett.wrapWidth) {\n\t\t\t\t_sett.wrapWidth = value;\n\t\t\t\t_AutoSizeNowIfIsOpen();\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Specifies to which owner's edges the toolbar keeps constant distance when moving or resizing the owner.\n\t/// </summary>\n\t/// <remarks>\n\t/// The owner can be a window, screen, control or other object. It is specified when calling <see cref=\"Show\"/>.\n\t/// This property is in the context menu and is saved.\n\t/// </remarks>\n\t/// <seealso cref=\"Offsets\"/>\n\tpublic TBAnchor Anchor {\n\t\tget => _sett.anchor;\n\t\tset {\n\t\t\t_ThreadTrap();\n\t\t\tvalue &= ~_GetInvalidAnchorFlags(value);\n\t\t\tif (value.WithoutFlags() == 0) value |= TBAnchor.TopLeft;\n\t\t\tif (value == _sett.anchor) return;\n\t\t\tvar prev = _sett.anchor;\n\t\t\t_sett.anchor = value;\n\t\t\tif (IsOwned) {\n\t\t\t\t_os = value.OfScreen() ? new _OwnerScreen(this, default) : null; //follow toolbar's screen\n\t\t\t\tif (prev.OfScreen() && _followedOnce) {\n\t\t\t\t\tif (_oc != null) _oc.UpdateRect(out _); else _ow.UpdateRect(out _);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (_followedOnce) {\n\t\t\t\tvar r = _w.Rect;\n\t\t\t\t_UpdateOffsets(r.left, r.top, r.Width, r.Height);\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Specifies distances between edges of the toolbar and edges of its owner, depending on <see cref=\"Anchor\"/>.\n\t/// </summary>\n\t/// <remarks>\n\t/// Owner is specified when calling <see cref=\"Show\"/>. It can be a window, screen, control or other object.\n\t/// \n\t/// The <see cref=\"TBOffsets\"/> type has 4 properties - <c>Top</c>, <c>Bottom</c>, <c>Left</c> and <c>Right</c>, but used are only those included in <see cref=\"Anchor\"/>. For example, if <c>Anchor</c> is <c>TopLeft</c>, used are only <c>Top</c> and <c>Left</c>.\n\t/// \n\t/// The unit of measurement depends on <see cref=\"DpiScaling\"/> and whether anchor is screen.\n\t/// \n\t/// This property is updated when moving or resizing the toolbar. It is saved.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// t.Offsets = new(150, 5, 0, 0);\n\t/// ]]></code>\n\t/// </example>\n\tpublic TBOffsets Offsets {\n\t\tget => _offsets;\n\t\tset {\n\t\t\t_ThreadTrap();\n\t\t\t_preferSize = false;\n\t\t\tif (value == _offsets) return;\n\t\t\t_sett.offsets = _offsets = value;\n\t\t\tif (_followedOnce) _FollowRect();\n\t\t}\n\t}\n\tTBOffsets _offsets;\n\t\n\t/// <summary>\n\t/// Number of pixels to add to the top of the retrieved rectangle of the owner window when it is maximized. That is, move this toolbar slightly down (if positive) or up (if negative).\n\t/// </summary>\n\t/// <remarks>\n\t/// When you create a toolbar attached to a window, and the anchor is at the top, test whether it is in visually the same vertical position (relative to UI elements of the window) when the window is maximized and when not maximized. On some windows it will be in a different position, and it is not what you want. Reason: some windows, when maximized, draw their UI elements in a different vertical position than when not maximized. It's impossible to reliably correct it automatically. To correct it, set this property before calling <see cref=\"toolbar.Show(wnd, bool)\"/> or <see cref=\"toolbar.Show(TriggerArgs)\"/>. With most such windows, the good value is 6.7 or 7.5. With some windows may need a negative value.\n\t/// \n\t/// By default the value is logical pixels; that is, will be scaled depending on DPI.\n\t/// \n\t/// If the owner window has multiple attached toolbars and all they run in the same thread, set this property for one of them, not for all. It will adjust the vertical position of all them.\n\t/// \n\t/// With some windows can be used another way to correct the vertical position: call <see cref=\"toolbar.Show(wnd, bool)\"/> with <c>clientArea: true</c>.\n\t/// \n\t/// If neither way works, use <see cref=\"toolbar.Show(wnd, ITBOwnerObject)\"/> (you'll need to create a class that implements <see cref=\"ITBOwnerObject\"/>). For example, to move the toolbar to a different position when the window is in full-screen mode.\n\t/// </remarks>\n\tpublic double MaximizedWindowTopPlus { get; set; }\n\t\n\t//rejected. Would be rarely used, unless default 0. Avoid default limitations like this. We have a dialog to find lost toolbars.\n\t//public int MaxDistanceFromOwner { get; set; } = int.MaxValue;\n\t\n\t//rejected\n\t//public bool HideTextIfSmall { get; set; } //like ribbon UI\n\t\n\t/// <summary>\n\t/// Miscellaneous options.\n\t/// </summary>\n\t/// <remarks>\n\t/// This property is in the context menu (submenu <b>More</b>) and is saved.\n\t/// </remarks>\n\tpublic TBFlags MiscFlags {\n\t\tget => _sett.miscFlags;\n\t\tset {\n\t\t\t_sett.miscFlags = value;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Opacity and transparent color.\n\t/// </summary>\n\t/// <seealso cref=\"wnd.SetTransparency(bool, int?, ColorInt?, bool)\"/>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// t.Transparency = (64, null);\n\t/// ]]></code>\n\t/// </example>\n\tpublic (int? opacity, ColorInt? colorKey) Transparency {\n\t\tget => _transparency;\n\t\tset {\n\t\t\tif (value != _transparency) {\n\t\t\t\t_transparency = value;\n\t\t\t\tif (_created) _w.SetTransparency(value != default, value.opacity, value.colorKey);\n\t\t\t}\n\t\t}\n\t}\n\t(int? opacity, ColorInt? colorKey) _transparency;\n\t\n\t/// <summary>\n\t/// Gets or sets flags to hide some context menu items or menu itself.\n\t/// </summary>\n\tpublic TBNoMenu NoContextMenu { get; set; }\n\t\n\t#endregion\n}\n"
  },
  {
    "path": "Au/GUI/trayIcon.cs",
    "content": "namespace Au {\n\t/// <summary>\n\t/// Shows tray icon.\n\t/// </summary>\n\t/// <remarks>\n\t/// Wraps API <ms>Shell_NotifyIconW</ms>, <ms>NOTIFYICONDATAW</ms>. More info there.\n\t/// \n\t/// This thread must dispatch messages.\n\t/// \n\t/// Can be used by multiple threads (eg one thread adds tray icon and other thread later changes its tooltip).\n\t/// \n\t/// Creates a hidden window that receives tray icon events (click etc).\n\t/// </remarks>\n\tpublic class trayIcon : IDisposable {\n\t\treadonly int _id;\n\t\treadonly bool _disposeOnExit;\n\t\twnd _w;\n\t\t\n\t\t//rejected. Various problems, eg the program file cannot be moved. Unclear documentation.\n\t\t//\tGuid _guid;\n\t\t\n\t\tstatic trayIcon() {\n\t\t\ts_msgTaskbarCreated = WndUtil.RegisterMessage(\"TaskbarCreated\", uacEnable: true);\n\t\t\tWndUtil.RegisterWindowClass(\"trayIcon\");\n\t\t}\n\t\tstatic int s_msgTaskbarCreated;\n\t\t\n\t\t/// <summary>\n\t\t/// Tray icon notification message.\n\t\t/// </summary>\n\t\tprotected const int MsgNotify = Api.WM_USER + 145;\n\t\t\n\t\t/// <param name=\"id\">An id that helps Windows to distinguish multiple tray icons added by same program. Use 0, 1, 2, ... or all 0.</param>\n\t\t/// <param name=\"disposeOnExit\">\n\t\t/// Remove tray icon when process exits (<see cref=\"process.thisProcessExit\"/>).\n\t\t/// Note: can't remove if the process terminated or called <see cref=\"Environment.FailFast\"/> or API <ms>ExitProcess</ms>.\n\t\t/// </param>\n\t\tpublic trayIcon(int id = 0, bool disposeOnExit = true) {\n\t\t\t_disposeOnExit = disposeOnExit;\n\t\t\t_id = Hash.Fnv1(script.name) + id;\n\t\t\t///// <param name=\"guid\">A GUID that identifies the tray icon.</param>\n\t\t\t//_guid=guid;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Removes tray icon and disposes other resources.\n\t\t/// </summary>\n\t\tpublic void Dispose() { Dispose(true); }\n\t\t\n\t\t///\n\t\tprotected virtual void Dispose(bool disposing) { _Delete(disposing: true); }\n\t\t\n\t\tvoid _Delete(bool disposing = false) {\n\t\t\tlock (this) {\n\t\t\t\tif (_visible) {\n\t\t\t\t\tApi.Shell_NotifyIcon(Api.NIM_DELETE, _NewND());\n\t\t\t\t\t_visible = false;\n\t\t\t\t}\n\t\t\t\tif (disposing && !_w.Is0) {\n\t\t\t\t\tif (!Api.DestroyWindow(_w)) _w.Post(Api.WM_CLOSE);\n\t\t\t\t\t_w = default;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets or sets whether the tray icon is visible.\n\t\t/// </summary>\n\t\t/// <exception cref=\"InvalidOperationException\">Icon not set.</exception>\n\t\tpublic bool Visible {\n\t\t\tget => _visible;\n\t\t\tset {\n\t\t\t\tlock (this) {\n\t\t\t\t\tif (value != _visible) {\n\t\t\t\t\t\tif (value && _icon == null) throw new InvalidOperationException(\"Icon not set\"); //but allow ShowNotification without icon. This is to prevent using slow code like new trayIcon() { Visible=true, Icon=... }.\n\t\t\t\t\t\tif (value) _Update();\n\t\t\t\t\t\telse _Delete();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tbool _visible;\n\t\t\n\t\t/// <summary>\n\t\t/// Gets or sets icon.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// To display nice icon at any DPI, the icon should be loaded with <see cref=\"icon.trayIcon\"/> or API <ms>LoadIconMetric</ms>, either from a native resource in your app or from an <c>.ico</c> file, which should contain icons of sizes 16, 32 and also recommended 20, 24.\n\t\t/// </remarks>\n\t\tpublic icon Icon {\n\t\t\tget => _icon;\n\t\t\tset {\n\t\t\t\tlock (this) {\n\t\t\t\t\tif (value != _icon) {\n\t\t\t\t\t\t_icon = value;\n\t\t\t\t\t\tif (_visible) _Update(icon: true);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\ticon _icon;\n\t\t\n\t\t/// <summary>\n\t\t/// Gets or sets tooltip text.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Max length of displayed tooltip text is 127.\n\t\t/// </remarks>\n\t\tpublic string Tooltip {\n\t\t\tget => _tooltip;\n\t\t\tset {\n\t\t\t\tlock (this) {\n\t\t\t\t\tif (value != _tooltip) {\n\t\t\t\t\t\t_tooltip = value;\n\t\t\t\t\t\tif (_visible) _Update(tooltip: true);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tstring _tooltip;\n\t\t\n\t\tApi.NOTIFYICONDATA _NewND(uint nifFlags = 0, bool setTT = false) {\n\t\t\tvar d = new Api.NOTIFYICONDATA(_w, nifFlags) { uID = _id, uCallbackMessage = MsgNotify, uVersion = Api.NOTIFYICON_VERSION_4 };\n\t\t\t//if (_guid!=default) { d.uFlags|=Api.NIF_GUID; d.guidItem=_guid; }\n\t\t\tif (!_customPopup) d.uFlags |= Api.NIF_SHOWTIP;\n\t\t\tif (setTT) {\n\t\t\t\td.uFlags |= Api.NIF_TIP;\n\t\t\t\td.szTip = _customPopup ? \" \" : _tooltip?.Limit(127);\n\t\t\t}\n\t\t\treturn d;\n\t\t}\n\t\t\n\t\tbool _Update(bool icon = false, bool tooltip = false, _Notification n = null, bool taskbarCreated = false) {\n\t\t\tif (script.Exiting_) return false;\n\t\t\tlock (this) {\n\t\t\t\tif (_w.Is0) {\n\t\t\t\t\tif (_disposeOnExit) process.thisProcessExit += _ => _Delete();\n\t\t\t\t\t_w = WndUtil.CreateWindow(WndProc, true, \"trayIcon\", script.name, WS.POPUP, WSE.NOACTIVATE);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (taskbarCreated) _visible = false;\n\t\t\t\t\n\t\t\t\tif (!_visible) { icon = _icon != null; tooltip = !_tooltip.NE(); }\n\t\t\t\tvar d = _NewND(Api.NIF_MESSAGE, setTT: tooltip || _customPopup);\n\t\t\t\tif (icon) { d.uFlags |= Api.NIF_ICON; d.hIcon = _icon; }\n\t\t\t\t\n\t\t\t\tif (n != null) {\n\t\t\t\t\td.uFlags |= Api.NIF_INFO;\n\t\t\t\t\tif (n.flags.Has(TINFlags.Realtime)) d.uFlags |= Api.NIF_REALTIME;\n\t\t\t\t\td.dwInfoFlags = (uint)(n.flags & ~TINFlags.Realtime);\n\t\t\t\t\tif (n.icon != null) { d.dwInfoFlags |= 0x24; d.hBalloonIcon = n.icon; } //user icon, large\n\t\t\t\t\td.szInfoTitle = n.title?.Limit(63);\n\t\t\t\t\td.szInfo = n.text?.Limit(255);\n\t\t\t\t\t//if(!_visible) if(_icon==null && _tooltip.NE()) { d.uFlags|=Api.NIF_STATE; d.dwState=d.dwStateMask=1; } //hidden icon. But then no notification.\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tint nim = _visible ? Api.NIM_MODIFY : Api.NIM_ADD;\n\t\t\t\tbool ok = Api.Shell_NotifyIcon(nim, d);\n\t\t\t\tif (!ok && 4 == (d.dwInfoFlags & 15)) {\n\t\t\t\t\t//fails if size of custom icon of notification != XM_CXSMICON (if no NIIF_LARGE_ICON) or < SM_CXICON (if NIIF_LARGE_ICON).\n\t\t\t\t\t//\ttested: Even if now taskbar DPI is different (changed since this process started), need icon of these sizes (they didn't change since this process started).\n\t\t\t\t\t//\tWorkaround: Remove the user icon flag and print warning.\n\t\t\t\t\t//tested: Win7 displays XM_CXSMICON or SM_CXICON depending on NIIF_LARGE_ICON. Win8.1 too. Win10 displays SM_CXICON*1.5 regardless of NIIF_LARGE_ICON.\n\t\t\t\t\td.dwInfoFlags &= ~15u;\n\t\t\t\t\tok = Api.Shell_NotifyIcon(nim, d);\n\t\t\t\t\tif (ok) print.warning(\"Custom icon size must be >= trayIcon.notificationIconSize\");\n\t\t\t\t}\n\t\t\t\tif (!_visible) {\n\t\t\t\t\tif (ok || taskbarCreated) ok = Api.Shell_NotifyIcon(Api.NIM_SETVERSION, d);\n\t\t\t\t\t_visible = true;\n\t\t\t\t\t//note: Shell_NotifyIcon(NIM_ADD) fails if already added, eg when \"TaskbarCreated\" message received when taskbar DPI changed.\n\t\t\t\t\t//note: Win10 sets last error, but Win7 doesn't.\n\t\t\t\t}\n\t\t\t\t//if(!ok) print.it(\"Shell_NotifyIcon: \", lastError.message);\n\t\t\t\treturn ok;\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Shows temporary notification window by the tray icon.\n\t\t/// </summary>\n\t\t/// <param name=\"title\">Title, max 63 characters.</param>\n\t\t/// <param name=\"text\">Text, max 255 characters.</param>\n\t\t/// <param name=\"flags\">Standard icon and other flags.</param>\n\t\t/// <param name=\"icon\">Custom icon. Important: use icon of size returned by <see cref=\"notificationIconSize\"/>.</param>\n\t\t/// <remarks>\n\t\t/// If the tray icon isn't visible, makes it visible.\n\t\t/// \n\t\t/// No more than one notification at a time can be displayed. If an application attempts to display a notification when one is already being displayed, the new notification is queued and displayed when the older notification goes away.\n\t\t/// \n\t\t/// Users may choose to not show notifications, depending on various conditions. Look in Windows <b>Settings > System > Notifications</b>.\n\t\t/// </remarks>\n\t\tpublic void ShowNotification(string title, string text, TINFlags flags = 0, icon icon = default) {\n\t\t\tif (!_Update(n: new(title, text, flags, icon))) print.warning(\"ShowNotification() failed. \" + lastError.message);\n\t\t}\n\t\t\n\t\trecord class _Notification(string title, string text, TINFlags flags, icon icon);\n\t\t\n\t\t/// <summary>\n\t\t/// Hides notification.\n\t\t/// </summary>\n\t\tpublic void HideNotification() {\n\t\t\tlock (this) {\n\t\t\t\tif (_visible) Api.Shell_NotifyIcon(Api.NIM_MODIFY, _NewND(Api.NIF_INFO));\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets icon size for <see cref=\"ShowNotification\"/>.\n\t\t/// </summary>\n\t\tpublic static int notificationIconSize {\n\t\t\tget {\n\t\t\t\tint r = Api.GetSystemMetrics(Api.SM_CXICON);\n\t\t\t\tif (osVersion.minWin10) r = r * 3 / 2;\n\t\t\t\treturn r;\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Activates taskbar and makes the tray icon focused for keyboard.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// If the tray icon is in hidden overflow area, makes the area button focused.\n\t\t/// </remarks>\n\t\tpublic void Focus() {\n\t\t\tlock (this) {\n\t\t\t\tApi.Shell_NotifyIcon(Api.NIM_SETFOCUS, _NewND());\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Together with <see cref=\"Hwnd\"/> identifies this tray icon.\n\t\t/// </summary>\n\t\tprotected int Id => _id;\n\t\t\n\t\t/// <summary>\n\t\t/// A hidden window automatically created for this tray icon to receive its notifications.\n\t\t/// </summary>\n\t\tprotected internal wnd Hwnd => _w;\n\t\t\n\t\t/// <summary>\n\t\t/// Window procedure of the hidden window that receives tray icon notifications (<see cref=\"MsgNotify\"/>) in version 4 format.\n\t\t/// If you override it, call the base function.\n\t\t/// </summary>\n\t\tprotected virtual nint WndProc(wnd w, int msg, nint wParam, nint lParam) {\n\t\t\tif (_visible) {\n\t\t\t\tif (msg == MsgNotify) {\n\t\t\t\t\tint m = Math2.LoWord(lParam); POINT p = Math2.NintToPOINT(wParam);\n\t\t\t\t\t//if(m!=Api.WM_MOUSEMOVE) print.it(m, p);\n\t\t\t\t\tMessage?.Invoke(new(m, p));\n\t\t\t\t\tswitch (m) {\n\t\t\t\t\tcase Api.WM_CONTEXTMENU:\n\t\t\t\t\t\tRightClick?.Invoke(new(m, p));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase Api.NIN_SELECT: //mouse click or double click. After WM_LBUTTONUP.\n\t\t\t\t\tcase Api.NIN_KEYSELECT: //Space, Enter (never mind bug: 2 NIN_KEYSELECT on Enter), DoDefaultAction\n\t\t\t\t\t\tClick?.Invoke(new(m, p));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase Api.WM_MBUTTONUP:\n\t\t\t\t\t\tMiddleClick?.Invoke(new(m, p));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase Api.NIN_BALLOONUSERCLICK:\n\t\t\t\t\t\tNotificationClick?.Invoke(new(m, p));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase Api.NIN_POPUPOPEN: //tested: on Win10 only if no NIF_SHOWTIP, but on Win7 always\n\t\t\t\t\t\tif (_customPopup) _popupOpen?.Invoke(new(m, p));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase Api.NIN_POPUPCLOSE:\n\t\t\t\t\t\tif (_customPopup) PopupClose?.Invoke(new(m, p));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t} else if (msg == s_msgTaskbarCreated) { //explorer restarted or taskbar DPI changed\n\t\t\t\t\t_Update(taskbarCreated: true);\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tvar R = Api.DefWindowProc(w, msg, wParam, lParam);\n\t\t\t\n\t\t\tif (msg == Api.WM_NCDESTROY) {\n\t\t\t\t_Delete();\n\t\t\t}\n\t\t\t\n\t\t\treturn R;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// When received any message from the tray icon.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Receives mouse messages, <c>NIN_</c> messages and some other. See <ms>Shell_NotifyIconW</ms>.\n\t\t/// </remarks>\n\t\tpublic event Action<TIEventArgs> Message;\n\t\t\n\t\t/// <summary>\n\t\t/// When default action should be invoked (on click, <c>Space</c>/<c>Enter</c>, automation/accessibility API).\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// If clicked, the parameter contains message <c>NIN_SELECT</c> (1024) and mouse coordinates. Else <c>NIN_KEYSELECT</c> (1025) and top-left of the tray icon.\n\t\t/// On double click there are two <c>Click</c> events. To distinguish click and double click events, use <see cref=\"Message\"/> instead.\n\t\t/// </remarks>\n\t\tpublic event Action<TIEventArgs> Click;\n\t\t\n\t\t/// <summary>\n\t\t/// When a context menu should be shown (on right click or <c>Apps</c> key).\n\t\t/// </summary>\n\t\tpublic event Action<TIEventArgs> RightClick;\n\t\t\n\t\t/// <summary>\n\t\t/// When the tray icon clicked with the middle button.\n\t\t/// </summary>\n\t\t[Obsolete(\"Does not work on Windows 11.\")]\n\t\tpublic event Action<TIEventArgs> MiddleClick;\n\t\t\n\t\t/// <summary>\n\t\t/// When clicked the notification window.\n\t\t/// </summary>\n\t\tpublic event Action<TIEventArgs> NotificationClick;\n\t\t\n\t\t/// <summary>\n\t\t/// When it's time to close custom tooltip etc shown on <see cref=\"PopupOpen\"/>.\n\t\t/// </summary>\n\t\tpublic event Action<TIEventArgs> PopupClose;\n\t\t\n\t\t/// <summary>\n\t\t/// When it's time to open custom tooltip or some temporary popup window.\n\t\t/// If this event is used, does not show standard tooltip.\n\t\t/// </summary>\n\t\tpublic event Action<TIEventArgs> PopupOpen {\n\t\t\tadd => _SetPopupOpen(true, value);\n\t\t\tremove => _SetPopupOpen(false, value);\n\t\t}\n\t\t\n\t\tevent Action<TIEventArgs> _popupOpen;\n\t\tbool _customPopup;\n\t\t\n\t\tvoid _SetPopupOpen(bool add, Action<TIEventArgs> a) {\n\t\t\tif (add) _popupOpen += a; else _popupOpen -= a;\n\t\t\tbool customPopup = _popupOpen != null;\n\t\t\tif (customPopup != _customPopup) {\n\t\t\t\t_customPopup = customPopup;\n\t\t\t\tif (_visible) Api.Shell_NotifyIcon(Api.NIM_MODIFY, _NewND(setTT: true));\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets tray icon rectangle in screen.\n\t\t/// </summary>\n\t\t/// <returns><c>false</c> if failed, for example if the icon is in hidden overflow area. Supports <see cref=\"lastError\"/>.</returns>\n\t\tpublic unsafe bool GetRect(out RECT r) {\n\t\t\tvar x = new Api.NOTIFYICONIDENTIFIER { cbSize = sizeof(Api.NOTIFYICONIDENTIFIER), hWnd = _w, uID = _id };\n\t\t\t//if (_guid!=default) x.guidItem=_guid;\n\t\t\tint hr = Api.Shell_NotifyIconGetRect(x, out r);\n\t\t\tif (hr == 0) return true;\n\t\t\tlastError.code = hr;\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\t//\tpublic bool GetPopupRect(int width, int height) {\n\t\t//\t\tif(!_visible) return false;\n\t\t//\t\t//what if hidden in overflow area? Then should not show popup. Then OS does not show notifications.\n\t\t//\t\t//CalculatePopupWindowPosition\n\t\t//\t}\n\t}\n}\n\nnamespace Au.Types {\n\t/// <summary>\n\t/// Flags for <see cref=\"trayIcon.ShowNotification\"/>. See <c>NIIF_</c> flags of API <ms>NOTIFYICONDATAW</ms>.\n\t/// </summary>\n\t[Flags]\n\tpublic enum TINFlags {\n#pragma warning disable 1591 //no doc\n\t\tInfoIcon = 1,\n\t\tWarningIcon,\n\t\tErrorIcon,\n\t\t//UserIcon,\n\t\tNoSound = 0x10,\n\t\t//LargeIcon=0x20,\n\t\t//RespectQuietTime=0x80,\n#pragma warning restore\n\t\t\n\t\t/// <summary>\n\t\t/// Flag <c>NIF_REALTIME</c>.\n\t\t/// </summary>\n\t\tRealtime = 0x10000000,\n\t}\n\t\n#pragma warning disable 1591\n\tpublic record class TIEventArgs(int Message, POINT XY);\n#pragma warning restore\n\t\n}"
  },
  {
    "path": "Au/GUI/wpf-types.cs",
    "content": "using System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Controls.Primitives;\nusing System.Windows.Input;\nusing System.Windows.Documents;\nusing System.Windows.Media;\n\nnamespace Au.Types {\n\t\n\t/// <summary>\n\t/// Used with <see cref=\"wpfBuilder\"/> constructor to specify the type of the root panel.\n\t/// </summary>\n\tpublic enum WBPanelType {\n\t\t///\n\t\tGrid,\n\t\t///\n\t\tCanvas,\n\t\t///\n\t\tDock,\n\t\t///\n\t\tVerticalStack,\n\t\t///\n\t\tHorizontalStack,\n\t}\n\t\n\t/// <summary>\n\t/// Flags for obsolete <see cref=\"wpfBuilder.Add\"/> overloads.\n\t/// </summary>\n\t[Flags]\n\t[EditorBrowsable(EditorBrowsableState.Never)]\n\tpublic enum WBAdd {\n\t\t/// <summary>\n\t\t/// Add as child of <see cref=\"wpfBuilder.Last\"/>, which can be of type (or base type):\n\t\t/// <br/>• <see cref=\"ContentControl\"/>. Adds as its <see cref=\"ContentControl.Content\"/> property. For example you can add a <c>CheckBox</c> in a <c>Button</c>.\n\t\t/// <br/>• <see cref=\"Decorator\"/>, for example <see cref=\"Border\"/>. Adds as its <see cref=\"Decorator.Child\"/> property.\n\t\t/// </summary>\n\t\tChildOfLast = 1,\n\t\t\n\t\t/// <summary>\n\t\t/// Don't adjust properties of the element, except margin. By default it adjusts padding, alignment, etc, and properties specified in <see cref=\"wpfBuilder.Options\"/>.\n\t\t/// </summary>\n\t\tDontSetProperties = 2,\n\t}\n\t\n\t/// <summary>\n\t/// Used with <see cref=\"wpfBuilder\"/> functions for width/height parameters. Allows to specify minimal and/or maximal values too.\n\t/// </summary>\n\t/// <remarks>\n\t/// Has implicit conversions from <c>double</c>, <see cref=\"Range\"/> and tuple <c>(double length, Range minMax)</c>.\n\t/// To specify width or height, pass an <c>int</c> or <c>double</c> value, like <c>100</c> or <c>15.25</c>.\n\t/// To specify minimal value, pass a range like <c>100..</c>.\n\t/// To specify maximal value, pass a range like <c>..100</c>.\n\t/// To specify minimal and maximal values, pass a range like <c>100..500</c>.\n\t/// To specify width or height and minimal or/and maximal values, pass a tuple like <c>(100, 50..)</c> or <c>(100, ..200)</c> or <c>(100, 50..200)</c>.\n\t/// </remarks>\n\tpublic struct WBLength {\n\t\tdouble _v;\n\t\tRange _r;\n\t\t\n\t\tWBLength(double v, Range r) {\n\t\t\tif (r.Start.IsFromEnd || (r.End.IsFromEnd && r.End.Value != 0)) throw new ArgumentException();\n\t\t\t_v = v; _r = r;\n\t\t}\n\t\t\n\t\t///\n\t\tpublic static implicit operator WBLength(double v) => new WBLength { _v = v, _r = .. };\n\t\t\n\t\t///\n\t\tpublic static implicit operator WBLength(Range v) => new WBLength(double.NaN, v);\n\t\t\n\t\t///\n\t\tpublic static implicit operator WBLength((double length, Range minMax) v) => new WBLength(v.length, v.minMax);\n\t\t\n\t\t/// <summary>\n\t\t/// Gets the width or height value. Returns <c>false</c> if not set.\n\t\t/// </summary>\n\t\tpublic bool GetLength(out double value) {\n\t\t\tvalue = _v;\n\t\t\treturn !double.IsNaN(_v);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets the minimal value. Returns <c>false</c> if not set.\n\t\t/// </summary>\n\t\tpublic bool GetMin(out int value) {\n\t\t\tvalue = _r.Start.Value;\n\t\t\treturn value > 0;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets the maximal value. Returns <c>false</c> if not set.\n\t\t/// </summary>\n\t\tpublic bool GetMax(out int value) {\n\t\t\tvalue = _r.End.Value;\n\t\t\treturn !_r.End.IsFromEnd;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Sets <c>Width</c> or <c>Height</c> or/and <c>MinWidth</c>/<c>MinHeight</c> or/and <c>MaxWidth</c>/<c>MaxHeight</c> of the <see cref=\"FrameworkElement\"/>.\n\t\t/// </summary>\n\t\t/// <param name=\"e\">Element.</param>\n\t\t/// <param name=\"height\">Set <c>Height</c>. If <c>false</c>, sets <c>Width</c>.</param>\n\t\tpublic void ApplyTo(FrameworkElement e, bool height) {\n\t\t\tif (GetLength(out double d)) { if (height) e.Height = d; else e.Width = d; }\n\t\t\tif (GetMin(out int i)) { if (height) e.MinHeight = i; else e.MinWidth = i; }\n\t\t\tif (GetMax(out i)) { if (height) e.MaxHeight = i; else e.MaxWidth = i; }\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Used with <see cref=\"wpfBuilder\"/> functions to specify width/height of columns and rows. Allows to specify minimal and/or maximal values too.\n\t/// </summary>\n\t/// <remarks>\n\t/// Like <see cref=\"WBLength\"/>, but has functions to create <see cref=\"ColumnDefinition\"/> and <see cref=\"RowDefinition\"/>. Also has implicit conversion from these types.\n\t/// </remarks>\n\tpublic struct WBGridLength {\n\t\tdouble _v;\n\t\tRange _r;\n\t\tDefinitionBase _def;\n\t\t\n\t\tWBGridLength(double v, Range r) {\n\t\t\tif (r.Start.IsFromEnd || (r.End.IsFromEnd && r.End.Value != 0)) throw new ArgumentException();\n\t\t\t_v = v; _r = r; _def = null;\n\t\t}\n\t\t\n\t\t///\n\t\tpublic static implicit operator WBGridLength(double v) => new WBGridLength { _v = v, _r = .. };\n\t\t\n\t\t///\n\t\tpublic static implicit operator WBGridLength((double length, Range minMax) v) => new WBGridLength(v.length, v.minMax);\n\t\t\n\t\t///\n\t\tpublic static implicit operator WBGridLength(Range v) => new WBGridLength(-1, v);\n\t\t\n\t\t///\n\t\tpublic static implicit operator WBGridLength(DefinitionBase v) => new WBGridLength { _def = v };\n\t\t\n\t\t/// <summary>\n\t\t/// Creates column definition object from assigned width or/and min/max width values. Or just returns the assigned or previously created object.\n\t\t/// </summary>\n\t\tpublic ColumnDefinition Column {\n\t\t\tget {\n\t\t\t\tif (_def is ColumnDefinition d) return d;\n\t\t\t\td = new ColumnDefinition { Width = _GridLength(_v) };\n\t\t\t\tif (_r.Start.Value > 0) d.MinWidth = _r.Start.Value;\n\t\t\t\tif (!_r.End.IsFromEnd) d.MaxWidth = _r.End.Value;\n\t\t\t\t_def = d;\n\t\t\t\treturn d;\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Creates row definition object from assigned height or/and min/max height values. Or just returns the assigned or previously created object.\n\t\t/// </summary>\n\t\tpublic RowDefinition Row {\n\t\t\tget {\n\t\t\t\tif (_def is RowDefinition d) return d;\n\t\t\t\td = new RowDefinition { Height = _GridLength(_v) };\n\t\t\t\tif (_r.Start.Value > 0) d.MinHeight = _r.Start.Value;\n\t\t\t\tif (!_r.End.IsFromEnd) d.MaxHeight = _r.End.Value;\n\t\t\t\t_def = d;\n\t\t\t\treturn d;\n\t\t\t}\n\t\t}\n\t\t\n\t\tGridLength _GridLength(double d) {\n\t\t\tif (d > 0) return new GridLength(d, GridUnitType.Pixel);\n\t\t\tif (d < 0) return new GridLength(-d, GridUnitType.Star);\n\t\t\treturn new GridLength();\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Arguments for <see cref=\"wpfBuilder.AlsoAll\"/> callback function.\n\t/// </summary>\n\tpublic class WBAlsoAllArgs {\n\t\t/// <summary>\n\t\t/// Gets 0-based column index of last added control, or -1 if not in grid.\n\t\t/// </summary>\n\t\tpublic int Column { get; internal set; }\n\t\t\n\t\t/// <summary>\n\t\t/// Gets 0-based row index of last added control, or -1 if not in grid.\n\t\t/// </summary>\n\t\tpublic int Row { get; internal set; }\n\t}\n\t\n\t/// <summary>\n\t/// Arguments for <see cref=\"wpfBuilder.AddButton\"/> callback function.\n\t/// </summary>\n\tpublic class WBButtonClickArgs : CancelEventArgs {\n\t\t/// <summary>\n\t\t/// Gets the button.\n\t\t/// </summary>\n\t\tpublic Button Button { get; internal set; }\n\t\t\n\t\t/// <summary>\n\t\t/// Gets the window.\n\t\t/// </summary>\n\t\tpublic Window Window { get; internal set; }\n\t}\n\t\n\t/// <summary>\n\t/// Flags for <see cref=\"wpfBuilder.AddButton\"/>.\n\t/// </summary>\n\t[Flags]\n\tpublic enum WBBFlags {\n\t\t/// <summary>It is <b>OK</b> button (<see cref=\"Button.IsDefault\"/>, closes window, validates, <see cref=\"wpfBuilder.OkApply\"/> event).</summary>\n\t\tOK = 1,\n\t\t\n\t\t/// <summary>It is <b>Cancel</b> button (<see cref=\"Button.IsCancel\"/>, closes window).</summary>\n\t\tCancel = 2,\n\t\t\n\t\t/// <summary>It is <b>Apply</b> button (size like <b>OK</b>/<b>Cancel</b>, validates, <see cref=\"wpfBuilder.OkApply\"/> event).</summary>\n\t\tApply = 4,\n\t\t\n\t\t/// <summary>Perform validation like <b>OK</b> and <b>Apply</b> buttons.</summary>\n\t\tValidate = 8,\n\t}\n\t\n\t/// <summary>\n\t/// Obsolete.\n\t/// Can be used with <see cref=\"wpfBuilder.Text\"/> to add a hyperlink.\n\t/// </summary>\n\t[EditorBrowsable(EditorBrowsableState.Never)] //obsolete\n\tpublic class WBLink {\n\t\t///\n\t\tpublic Hyperlink Hlink { get; }\n\t\t\n\t\tWBLink(string text, bool bold) {\n\t\t\tRun run = new(text);\n\t\t\tHlink = new(bold ? new Bold(run) : run);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Sets link text and action.\n\t\t/// </summary>\n\t\tpublic WBLink(string text, Action action, bool bold = false) : this(text, bold) {\n\t\t\tHlink.Click += (o, e) => action();\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Sets link text and action.\n\t\t/// </summary>\n\t\t/// <param name=\"text\">Link text.</param>\n\t\t/// <param name=\"action\">Action to execute on click.</param>\n\t\t/// <param name=\"bold\">Bold font.</param>\n\t\tpublic WBLink(string text, Action<Hyperlink> action, bool bold = false) : this(text, bold) {\n\t\t\tHlink.Click += (o, e) => action(o as Hyperlink);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Sets link text and target URL or file etc.\n\t\t/// On click will call <see cref=\"run.itSafe\"/>.\n\t\t/// </summary>\n\t\t/// <param name=\"text\">Link text.</param>\n\t\t/// <param name=\"urlOrPath\">URL or path for <see cref=\"run.itSafe\"/>. If <c>null</c>, uses <i>text</i>.</param>\n\t\t/// <param name=\"args\"><i>args</i> for <see cref=\"run.itSafe\"/>.</param>\n\t\t/// <param name=\"bold\">Bold font.</param>\n\t\tpublic WBLink(string text, string urlOrPath = null, string args = null, bool bold = false) : this(text, _ => run.itSafe(urlOrPath ?? text, args)) { }\n\t}\n}\n\nnamespace Au.More {\n\t//rejected. Unsafe etc. For example, when assigning to object, uses CheckBool whereas the user may expect bool.\n\t//\t/// <summary>\n\t//\t/// <see cref=\"CheckBox\"/> that can be used like bool.\n\t//\t/// For example instead of <c>if(c.IsChecked == true)</c> can be used <c>if(c)</c>.\n\t//\t/// </summary>\n\t//\tpublic class CheckBool : CheckBox\n\t//\t{\n\t//\t\t///\n\t//\t\tpublic CheckBool()\n\t//\t\t{\n\t//\t\t\tthis.SetResourceReference(StyleProperty, typeof(CheckBox));\n\t//\t\t}\n\t//\n\t//\t\t/// <summary>\n\t//\t\t/// Returns true if <see cref=\"ToggleButton.IsChecked\"/> == true.\n\t//\t\t/// </summary>\n\t//\t\tpublic static implicit operator bool(CheckBool c) => c.IsChecked.GetValueOrDefault();\n\t//\t}\n\t\n\t\n\t/// <summary>\n\t/// Grid splitter control. Based on <see cref=\"GridSplitter\"/>, changes its behavior.\n\t/// </summary>\n\t/// <remarks>\n\t/// Try this class when <see cref=\"GridSplitter\"/> does not work as you want.\n\t/// \n\t/// Limitations (bad or good):\n\t/// - Splitters must be on own rows/columns. Throws exception if <c>ResizeBehavior</c> is not <c>PreviousAndNext</c> (which is default).\n\t/// - Throws exception is there are star-sized splitter rows.\n\t/// - Does not resize auto-sized rows/columns. Only pixel-sized and star-sized.\n\t/// - With <c>UseLayoutRounding</c> may flicker when resizing, especially when high DPI.\n\t/// </remarks>\n\tpublic class GridSplitter2 : GridSplitter {\n\t\tstatic GridSplitter2() {\n\t\t\tEventManager.RegisterClassHandler(typeof(GridSplitter2), Thumb.DragStartedEvent, new DragStartedEventHandler(_OnDragStarted));\n\t\t\tEventManager.RegisterClassHandler(typeof(GridSplitter2), Thumb.DragCompletedEvent, new DragCompletedEventHandler(_OnDragCompleted));\n\t\t\tEventManager.RegisterClassHandler(typeof(GridSplitter2), Thumb.DragDeltaEvent, new DragDeltaEventHandler(_OnDragDelta));\n\t\t}\n\t\t\n\t\t///\n\t\tpublic GridSplitter2() {\n\t\t\tResizeBehavior = GridResizeBehavior.PreviousAndNext;\n\t\t\tSnapsToDevicePixels = true;\n\t\t\tFocusable = false;\n\t\t}\n\t\t\n\t\tstatic void _OnDragStarted(object sender, DragStartedEventArgs e) => (sender as GridSplitter2)._OnDragStarted(e);\n\t\t\n\t\tvoid _OnDragStarted(DragStartedEventArgs e) {\n\t\t\tif (!ShowsPreview) e.Handled = true;\n\t\t\tif (!_Init()) base.CancelDrag();\n\t\t}\n\t\t\n\t\tstatic void _OnDragCompleted(object sender, DragCompletedEventArgs e) => (sender as GridSplitter2)._OnDragCompleted(e);\n\t\t\n\t\tvoid _OnDragCompleted(DragCompletedEventArgs e) {\n\t\t\tif (!ShowsPreview) e.Handled = true; //else somehow GridSplitter does not resize, just removes the adorner\n\t\t\tif (_a == null) return; //two events if called CancelDrag\n\t\t\tif (!e.Canceled) _MoveSplitter();\n\t\t\t_a = null;\n\t\t}\n\t\t\n\t\tstatic void _OnDragDelta(object sender, DragDeltaEventArgs e) => (sender as GridSplitter2)._OnDragDelta(e);\n\t\t\n\t\tvoid _OnDragDelta(DragDeltaEventArgs e) {\n\t\t\t_delta = _isVertical ? e.HorizontalChange : e.VerticalChange;\n\t\t\tvar di = DragIncrement; _delta = Math.Round(_delta / di) * di;\n\t\t\tif (ShowsPreview) return;\n\t\t\te.Handled = true;\n\t\t\tif (_working) return; _working = true; //avoid too much CPU and delayed repainting of hwndhosts\n\t\t\tDispatcher.InvokeAsync(() => _working = false, System.Windows.Threading.DispatcherPriority.ApplicationIdle);\n\t\t\t_MoveSplitter();\n\t\t}\n\t\tbool _working;\n\t\t\n\t\t///\n\t\tprotected override void OnKeyDown(KeyEventArgs e) {\n\t\t\tif (e.Key == Key.Up || e.Key == Key.Down || e.Key == Key.Left || e.Key == Key.Right) {\n\t\t\t\te.Handled = true;\n\t\t\t\tif (!_Init()) return;\n\t\t\t\t_delta = KeyboardIncrement * ((e.Key == Key.Up || e.Key == Key.Right) ? -1 : 1);\n\t\t\t\tif (_isVertical && FlowDirection == FlowDirection.RightToLeft) _delta = -_delta;\n\t\t\t\t_MoveSplitter();\n\t\t\t\t_a = null;\n\t\t\t} else if (e.Key == Key.Escape && _a != null) {\n\t\t\t\te.Handled = true;\n\t\t\t\tCancelDrag();\n\t\t\t}\n\t\t}\n\t\t\n\t\tbool _Init(Key key = default) {\n\t\t\t_a = null;\n\t\t\t_isVertical = _IsVertical();\n\t\t\tif (key != default && _isVertical != (key == Key.Left || key == Key.Right)) return false;\n\t\t\t//_resizeBehavior=_GetResizeBehavior(_isVertical);\n\t\t\tif (_GetResizeBehavior(_isVertical) != GridResizeBehavior.PreviousAndNext) throw new NotSupportedException(\"ResizeBehavior must be PreviousAndNext.\");\n\t\t\t_delta = 0;\n\t\t\t_grid = Parent as Grid;\n\t\t\t_a = new List<_RowCol>();\n\t\t\t_index = 0;\n\t\t\tvar splitters = _grid.Children.OfType<GridSplitter2>()\n\t\t\t\t.Where(o => o._IsVertical() == _isVertical)\n\t\t\t\t.Select(o => _IndexInGrid(o)).ToArray();\n\t\t\tint index = _IndexInGrid(this);\n\t\t\tfor (int i = 0, n = (_isVertical ? _grid.ColumnDefinitions.Count : _grid.RowDefinitions.Count); i < n; i++) {\n\t\t\t\tvar v = new _RowCol(_isVertical ? (DefinitionBase)_grid.ColumnDefinitions[i] : _grid.RowDefinitions[i]);\n\t\t\t\tif (splitters.Contains(i)) {\n\t\t\t\t\tif (v.IsStar) throw new InvalidOperationException(\"Splitter row/column cannot be star-sized.\");\n\t\t\t\t\tif (i == index) _index = _a.Count;\n\t\t\t\t} else {\n\t\t\t\t\tif (v.Unit == GridUnitType.Auto) continue;\n\t\t\t\t\tif (v.IsStar) v.SetSize(v.ActualSize);\n\t\t\t\t\t_a.Add(v);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (_index == 0 || _index == _a.Count) { //no resizable items before or after\n\t\t\t\t_a = null;\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\tGrid _grid;\n\t\tbool _isVertical;\n\t\t//\tGridResizeBehavior _resizeBehavior;\n\t\tList<_RowCol> _a; //resizable rows/columns, ie those without splitters and not auto-sized\n\t\tint _index; //index of first _a item after this splitter\n\t\tdouble _delta;\n\t\t\n\t\tvoid _MoveSplitter() {\n\t\t\tif (_a == null || _delta == 0) return;\n\t\t\t\n\t\t\t_Side before = default, after = default;\n\t\t\t\n\t\t\t//resize multiple star-sized items at that side?\n\t\t\tif (ResizeNearest || Keyboard.Modifiers == ModifierKeys.Control) {\n\t\t\t\tbefore.single = after.single = true;\n\t\t\t} else {\n\t\t\t\tint stars = 0; //flags: 1 stars before, 2 stars after\n\t\t\t\tfor (int i = 0; i < _a.Count; i++) if (_a[i].IsStar) stars |= i < _index ? 1 : 2;\n\t\t\t\tbefore.single = _index == 1 || 0 == (stars & 1) || _a[_index - 1].ActualSize < 4 || (stars == 3 && !_a[_index - 1].IsStar); //without the last || subexpression would be impossible to resize fixed-sized items if there are star-sized items at both sides\n\t\t\t\tafter.single = _index == _a.Count - 1 || 0 == (stars & 2) || _a[_index].ActualSize < 4 || (stars == 3 && !_a[_index].IsStar);\n\t\t\t}\n\t\t\t\n\t\t\tfor (int i = 0; i < _a.Count; i++) {\n\t\t\t\tif (!_IsResizable(i)) continue;\n\t\t\t\tif (i < _index) before.Add(_a[i]); else after.Add(_a[i]);\n\t\t\t}\n\t\t\t\n\t\t\tdouble v1 = Math.Clamp(before.size + _delta, before.min, before.max), v2 = Math.Clamp(after.size - _delta, after.min, after.max);\n\t\t\t_delta = 0;\n\t\t\tif (v1 == before.min || v1 == before.max) v2 = before.size + after.size - v1; else if (v2 == after.min || v2 == after.max) v1 = before.size + after.size - v2;\n\t\t\t\n\t\t\t_ResizeSide(before, true, v1);\n\t\t\t_ResizeSide(after, false, v2);\n\t\t\t\n\t\t\tvoid _ResizeSide(_Side side, bool isBefore, double size) {\n\t\t\t\tif (side.single) {\n\t\t\t\t\t_a[_index - (isBefore ? 1 : 0)].SetSize(size);\n\t\t\t\t} else {\n\t\t\t\t\tfor (int i = isBefore ? 0 : _index, to = isBefore ? _index : _a.Count; i < to; i++) {\n\t\t\t\t\t\tif (!_IsResizable(i)) continue;\n\t\t\t\t\t\tvar v = _a[i];\n\t\t\t\t\t\tvar k = size * v.ActualSize; if (side.size > 0.1) k /= side.size; else k = 0.1;\n\t\t\t\t\t\tv.SetSize(k);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tbool _IsResizable(int index) {\n\t\t\t\tif (index < _index) return before.single ? index == _index - 1 : _a[index].IsStar;\n\t\t\t\treturn after.single ? index == _index : _a[index].IsStar;\n\t\t\t}\n\t\t}\n\t\t\n\t\tstruct _Side {\n\t\t\tpublic double size, min, max;\n\t\t\tpublic bool single;\n\t\t\tpublic int stars;\n\t\t\t\n\t\t\tpublic void Add(_RowCol v) {\n\t\t\t\tsize += v.ActualSize;\n\t\t\t\tmin += v.Min;\n\t\t\t\tdouble x = v.Max;\n\t\t\t\tif (max != double.PositiveInfinity) { if (x == double.PositiveInfinity) max = x; else max += x; }\n\t\t\t\tif (!single && v.IsStar) stars++;\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Always resize only the nearest resizable row/column at each side.\n\t\t/// If <c>false</c> (default), may resize multiple star-sized rows/columns, unless with <c>Ctrl</c> key.\n\t\t/// </summary>\n\t\tpublic bool ResizeNearest { get; set; }\n\t\t\n\t\t#region util\n\t\t\n\t\tbool _IsVertical() { //see code of GridSplitter.GetEffectiveResizeDirection. The algorithm is documented.\n\t\t\tvar dir = this.ResizeDirection;\n\t\t\tif (dir != GridResizeDirection.Auto) return dir == GridResizeDirection.Columns;\n\t\t\tif (this.HorizontalAlignment != HorizontalAlignment.Stretch) return true;\n\t\t\tif (this.VerticalAlignment != VerticalAlignment.Stretch) return false;\n\t\t\treturn this.ActualWidth <= this.ActualHeight;\n\t\t}\n\t\t\n\t\tGridResizeBehavior _GetResizeBehavior(bool vertical) { //see code of GridSplitter.GetEffectiveResizeBehavior\n\t\t\tvar r = ResizeBehavior;\n\t\t\tif (r == GridResizeBehavior.BasedOnAlignment) {\n\t\t\t\tif (vertical) r = HorizontalAlignment switch {\n\t\t\t\t\tHorizontalAlignment.Left => GridResizeBehavior.PreviousAndCurrent,\n\t\t\t\t\tHorizontalAlignment.Right => GridResizeBehavior.CurrentAndNext,\n\t\t\t\t\t_ => GridResizeBehavior.PreviousAndNext,\n\t\t\t\t};\n\t\t\t\telse r = VerticalAlignment switch {\n\t\t\t\t\tVerticalAlignment.Top => GridResizeBehavior.PreviousAndCurrent,\n\t\t\t\t\tVerticalAlignment.Bottom => GridResizeBehavior.CurrentAndNext,\n\t\t\t\t\t_ => GridResizeBehavior.PreviousAndNext,\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn r;\n\t\t}\n\t\t\n\t\tint _IndexInGrid(UIElement e) => _isVertical ? Grid.GetColumn(e) : Grid.GetRow(e);\n\t\t\n\t\tclass _RowCol {\n\t\t\tRowDefinition _row;\n\t\t\tColumnDefinition _col;\n\t\t\t\n\t\t\tpublic _RowCol(DefinitionBase def) {\n\t\t\t\t_row = def as RowDefinition;\n\t\t\t\t_col = def as ColumnDefinition;\n\t\t\t\tMin = _row?.MinHeight ?? _col.MinWidth;\n\t\t\t\tMax = _row?.MaxHeight ?? _col.MaxWidth;\n\t\t\t\tUnit = DefSizeGL.GridUnitType;\n\t\t\t}\n\t\t\t\n\t\t\tpublic double ActualSize => _row?.ActualHeight ?? _col.ActualWidth;\n\t\t\t\n\t\t\tpublic double DefSize {\n\t\t\t\tget => _row?.Height.Value ?? _col.Width.Value;\n\t\t\t\t//\t\t\tset { DefSizeGL = new GridLength(value, Unit); }\n\t\t\t}\n\t\t\t\n\t\t\tGridLength DefSizeGL {\n\t\t\t\tget => _row?.Height ?? _col.Width;\n\t\t\t\t//\t\t\tset { if(_row!=null) _row.Height=value; else _col.Width=value; }\n\t\t\t}\n\t\t\t\n\t\t\tpublic void SetSize(double size) {\n\t\t\t\tvar z = new GridLength(size, Unit);\n\t\t\t\tif (_row != null) _row.Height = z; else _col.Width = z;\n\t\t\t}\n\t\t\t\n\t\t\tpublic GridUnitType Unit { get; private set; }\n\t\t\t\n\t\t\tpublic bool IsStar => Unit == GridUnitType.Star;\n\t\t\t\n\t\t\tpublic double Min { get; private set; }\n\t\t\t\n\t\t\tpublic double Max { get; private set; }\n\t\t}\n\t\t\n\t\t#endregion\n\t}\n\t\n\t/// <summary>\n\t/// Adorner that draws watermark/hint/cue text over the adorned control (<see cref=\"TextBox\"/> etc).\n\t/// </summary>\n\tpublic class WatermarkAdorner : Adorner {\n\t\tstring _text;\n\t\tControl _c;\n\t\tTextBox _tCB;\n\t\t\n\t\t/// <summary>\n\t\t/// Initializes and adds this adorner to the <see cref=\"AdornerLayer\"/> found by <see cref=\"AdornerLayer.GetAdornerLayer\"/>.\n\t\t/// </summary>\n\t\t/// <param name=\"c\">The control.</param>\n\t\t/// <param name=\"text\">Watermark text.</param>\n\t\t/// <remarks>\n\t\t/// In some kinds of windows it may not work unless a parent or ancestor of the control is an <see cref=\"AdornerDecorator\"/>.\n\t\t/// </remarks>\n\t\tpublic WatermarkAdorner(Control c, string text) : base(c) {\n\t\t\t_c = c;\n\t\t\t_text = text;\n\t\t\tIsHitTestVisible = false;\n\t\t\tif (AdornerLayer.GetAdornerLayer(c) is { } layer) layer.Add(this);\n\t\t\telse c.Loaded += (_, _) => { AdornerLayer.GetAdornerLayer(c)?.Add(this); }; //the Window or some ancestor element usually has an AdornerLayer in its template, which now probably still not applied\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets or sets watermark text.\n\t\t/// </summary>\n\t\tpublic string Text {\n\t\t\tget => _text;\n\t\t\tset {\n\t\t\t\tif (value != _text) {\n\t\t\t\t\t_text = value;\n\t\t\t\t\tInvalidateVisual();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Sets events to show/hide the adorner depending on control text.\n\t\t/// The control must be <see cref=\"TextBox\"/>, <see cref=\"PasswordBox\"/> or editable <see cref=\"ComboBox\"/>.\n\t\t/// </summary>\n\t\tpublic void SetAdornerVisibility() {\n\t\t\tif (_c is TextBox t) {\n\t\t\t\t_VisibilityT(t);\n\t\t\t} else if (_c is PasswordBox p) {\n\t\t\t\t_VisibilityP(p);\n\t\t\t} else {\n\t\t\t\tif (_c.IsLoaded) _OfChildTB();\n\t\t\t\telse _c.Loaded += (_, _) => { _OfChildTB(); };\n\t\t\t\t\n\t\t\t\tvoid _OfChildTB() { if (_c.FindVisualDescendant(o => o is TextBox) is TextBox t2) _VisibilityT(_tCB = t2); }\n\t\t\t}\n\t\t\t\n\t\t\tvoid _VisibilityT(TextBox t) {\n\t\t\t\tVisibility = t.Text.NE() ? Visibility.Visible : Visibility.Hidden;\n\t\t\t\tt.TextChanged += (_, _) => { Visibility = t.Text.NE() ? Visibility.Visible : Visibility.Hidden; };\n\t\t\t}\n\t\t\t\n\t\t\tvoid _VisibilityP(PasswordBox t) {\n\t\t\t\tVisibility = t.Password.NE() ? Visibility.Visible : Visibility.Hidden;\n\t\t\t\tt.PasswordChanged += (_, _) => { Visibility = t.Password.NE() ? Visibility.Visible : Visibility.Hidden; };\n\t\t\t}\n\t\t}\n\t\t\n\t\t///\n\t\tprotected override void OnRender(DrawingContext dc) {\n\t\t\tif (_text.NE()) return;\n\t\t\tvar tf = new Typeface(_c.FontFamily, _c.FontStyle, _c.FontWeight, _c.FontStretch);\n\t\t\tvar ft = new FormattedText(_text, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, tf, _c.FontSize, Brushes.DarkGray, 96);\n\t\t\tThickness bt = _c.BorderThickness, pad = _c.Padding;\n\t\t\tPoint p = new(bt.Left + pad.Left + 2, bt.Top + pad.Top);\n\t\t\tvar z = _c.RenderSize;\n\t\t\tdouble w = z.Width - bt.Right - pad.Right - 2, h = z.Height - bt.Bottom - pad.Bottom;\n\t\t\tif (_tCB != null) w = _tCB.RenderSize.Width;\n\t\t\tif (w < 4 || h < 4) return;\n\t\t\tdc.PushClip(new RectangleGeometry(new(0, 0, w, h)));\n\t\t\tdc.DrawText(ft, p);\n\t\t\tdc.Pop();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au/GUI/wpfBuilder.cs",
    "content": "using System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Controls.Primitives;\nusing System.Windows.Documents;\nusing System.Windows.Media;\nusing System.Windows.Data;\nusing System.Windows.Input;\nusing System.Xml.Linq;\n//for <see>\nusing C = System.Windows.Controls;\nusing W = System.Windows;\nusing D = System.Windows.Documents;\n\n//never mind: on Win7 text of all WPF checkboxes too low by 1 pixel. Not only of wpfBuilder.\n\nnamespace Au;\n\n#pragma warning disable WPF0001 //enable ThemeMode\n\n/// <summary>\n/// With this class you can create windows with controls, for example for data input.\n/// </summary>\n/// <remarks>\n/// This class uses WPF (Windows Presentation Foundation). Creates window at run time. No designer. No WPF and XAML knowledge required, unless you want something advanced.\n/// \n/// To start, use snippet <c>wpfDialogSnippet</c> or menu <b>File > New > Dialogs</b>. Also look in Cookbook.\n/// \n/// Most functions return <c>this</c>, to enable method chaining, aka fluent interface, like with <see cref=\"StringBuilder\"/>. See example.\n/// \n/// A <c>wpfBuilder</c> object can be used to create whole window or some window part, for example a tab page.\n/// \n/// The size/position unit in WPF is about 1/96 inch, regardless of screen DPI. For example, if DPI is 96 (100%), 1 unit = 1 physical pixel; if 150% - 1.5 pixel; if 200% - 2 pixels. WPF windows are DPI-scaled automatically when need. Your program's manifest should contain <c>dpiAware=true/PM</c> and <c>dpiAwareness=PerMonitorV2</c>; it is default for scripts/programs created with the script editor of this library.\n/// \n/// Note: WPF starts slowly and uses much memory. It is normal if to show the first window in process takes 500-1000 ms and the process uses 30 MB of memory, whereas WinForms takes 250 ms / 10 MB and native takes 50 ms / 2 MB. However WinForms becomes slower than WPF if there are more than 100 controls in window. This library uses WPF because it is the most powerful and works well with high DPI screens.\n/// \n/// WPF has many control types, for example <see cref=\"Button\"/>, <see cref=\"CheckBox\"/>, <see cref=\"TextBox\"/>, <see cref=\"ComboBox\"/>, <see cref=\"Label\"/>. Most are in namespaces <c>System.Windows.Controls</c> and <c>System.Windows.Controls.Primitives</c>. Also on the internet you can find many libraries containing WPF controls and themes. For example, search for <i>github awesome dotnet C#</i>. Many libraries are open-source, and most can be found in GitHub (source, info and sometimes compiled files). Compiled files usually can be found in <see href=\"https://www.nuget.org/\"/> as packages. Use menu <b>Tools > NuGet</b>.\n/// \n/// By default don't need XAML. When need, you can load XAML strings and files with <see cref=\"System.Windows.Markup.XamlReader\"/>.\n/// </remarks>\n/// <example>\n/// Dialog window with several controls for data input.\n/// <code><![CDATA[\n/// var b = new wpfBuilder(\"Example\").WinSize(400); //create Window object with Grid control; set window width 400\n/// b.R.Add(\"Text\", out TextBox text1).Focus(); //add label and text box control in first row\n/// b.R.Add(\"Combo\", out ComboBox combo1).Items(\"One|Two|Three\"); //in second row add label and combo box control with items\n/// b.R.Add(out CheckBox c1, \"Check\"); //in third row add check box control\n/// b.R.AddOkCancel(); //finally add standard OK and Cancel buttons\n/// b.End();\n/// if (!b.ShowDialog()) return; //show the dialog and wait until closed; return if closed not with OK button\n/// print.it(text1.Text, combo1.SelectedIndex, c1.IsChecked == true); //get user input from control variables\n/// ]]></code>\n/// </example>\npublic class wpfBuilder {\n\t//readonly FrameworkElement _container; //now used only in ctor\n\treadonly Window _window; //= _container or null\n\t_PanelBase _p; //current grid/stack/dock/canvas panel, either root or nested\n\t\n\tclass _PanelBase {\n\t\tprotected readonly wpfBuilder _b;\n\t\tpublic readonly _PanelBase parent;\n\t\tpublic readonly Panel panel;\n\t\tFrameworkElement _lastAdded, _lastAdded2;\n\t\tpublic bool ended;\n\t\t\n\t\tpublic _PanelBase(wpfBuilder b, Panel p) {\n\t\t\t_b = b;\n\t\t\tparent = b._p;\n\t\t\t_lastAdded = panel = p;\n\t\t}\n\t\t\n\t\tpublic virtual void BeforeAdd(bool childOfLast) {\n\t\t\tif (ended) throw new InvalidOperationException(\"Cannot add after End()\");\n\t\t\tif (childOfLast && _lastAdded == panel) throw new ArgumentException(\"Cannot add as child. The last element is panel.\");\n\t\t}\n\t\t\n\t\tpublic virtual void Add(FrameworkElement c) {\n\t\t\tSetLastAdded(c);\n\t\t\tpanel.Children.Add(c);\n\t\t}\n\t\t\n\t\tpublic virtual void End() { ended = true; }\n\t\t\n\t\tpublic FrameworkElement LastAdded => _lastAdded;\n\t\t\n\t\tpublic FrameworkElement LastAdded2 => _lastAdded2;\n\t\t\n\t\tpublic void SetLastAdded(FrameworkElement e) {\n\t\t\tif (_lastAdded != panel) _lastAdded2 = _lastAdded;\n\t\t\t_lastAdded = e;\n\t\t}\n\t\t\n\t\tpublic FrameworkElement LastDirect {\n\t\t\tget {\n\t\t\t\tif (_lastAdded == panel) {\n\t\t\t\t\tDebug_.Print(\"lastAdded == panel\");\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\tfor (var c = _lastAdded; ;) {\n\t\t\t\t\tvar pa = c.Parent as FrameworkElement;\n\t\t\t\t\tif (pa == panel) return c;\n\t\t\t\t\tc = pa;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t\n\tclass _Canvas : _PanelBase {\n\t\tpublic _Canvas(wpfBuilder b, Canvas c = null) : base(b, c ?? new Canvas()) {\n\t\t\tpanel.HorizontalAlignment = HorizontalAlignment.Left;\n\t\t\tpanel.VerticalAlignment = VerticalAlignment.Top;\n\t\t}\n\t}\n\t\n\tclass _DockPanel : _PanelBase {\n\t\tpublic _DockPanel(wpfBuilder b) : base(b, new DockPanel()) {\n\t\t}\n\t}\n\t\n\tclass _StackPanel : _PanelBase {\n\t\tpublic _StackPanel(wpfBuilder b, bool vertical) : base(b, new StackPanel { Orientation = vertical ? Orientation.Vertical : Orientation.Horizontal }) {\n\t\t}\n\t}\n\t\n\tclass _Grid : _PanelBase {\n\t\treadonly Grid _grid; //same as panel, just to avoid casting everywhere\n\t\tint _row = -1, _col;\n\t\tbool _isSpan;\n\t\tdouble? _andWidth;\n\t\t\n\t\tpublic _Grid(wpfBuilder b, Grid g = null) : base(b, g ?? new Grid()) {\n\t\t\t_grid = panel as Grid;\n\t\t\tif (gridLines) _grid.ShowGridLines = true;\n\t\t}\n\t\t\n\t\tpublic void Row(WBGridLength height) {\n\t\t\tif (_andWidth != null) throw new InvalidOperationException(\"And().Row()\");\n\t\t\tif (_row >= 0) {\n\t\t\t\t_SetLastSpan();\n\t\t\t\t_col = 0;\n\t\t\t} else if (_grid.ColumnDefinitions.Count == 0) {\n\t\t\t\t_grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(0, GridUnitType.Auto) });\n\t\t\t\t_grid.ColumnDefinitions.Add(new ColumnDefinition());\n\t\t\t}\n\t\t\t_row++;\n\t\t\t_grid.RowDefinitions.Add(height.Row);\n\t\t}\n\t\t\n\t\tpublic override void BeforeAdd(bool childOfLast) {\n\t\t\tbase.BeforeAdd(childOfLast);\n\t\t\tif (childOfLast) return;\n\t\t\tif (_row < 0 || _col >= _grid.ColumnDefinitions.Count) Row(0);\n\t\t\t_isSpan = false;\n\t\t}\n\t\t\n\t\tpublic override void Add(FrameworkElement c) {\n\t\t\tif (_andWidth != null) {\n\t\t\t\tvar width = _andWidth.Value; _andWidth = null;\n\t\t\t\tif (width < 0) {\n\t\t\t\t\tvar m = c.Margin;\n\t\t\t\t\tm.Left += -width + 3;\n\t\t\t\t\tc.Margin = m;\n\t\t\t\t} else if (width > 0) {\n\t\t\t\t\tc.Width = width;\n\t\t\t\t\tc.HorizontalAlignment = HorizontalAlignment.Right;\n\t\t\t\t}\n\t\t\t\tvar last = LastDirect;\n\t\t\t\tGrid.SetColumn(c, Grid.GetColumn(last));\n\t\t\t\tGrid.SetColumnSpan(c, Grid.GetColumnSpan(last));\n\t\t\t\t_isSpan = true;\n\t\t\t} else {\n\t\t\t\tGrid.SetColumn(c, _col);\n\t\t\t}\n\t\t\t_col++;\n\t\t\tGrid.SetRow(c, _row);\n\t\t\tbase.Add(c);\n\t\t}\n\t\t\n\t\tpublic void And(double width) {\n\t\t\tif (_col == 0 || _andWidth != null || LastAdded == panel) throw new InvalidOperationException(\"And()\");\n\t\t\tvar c = LastDirect;\n\t\t\tif (width < 0) {\n\t\t\t\tc.Width = -width;\n\t\t\t\tc.HorizontalAlignment = HorizontalAlignment.Left;\n\t\t\t} else if (width > 0) {\n\t\t\t\tvar m = c.Margin;\n\t\t\t\tm.Right += width + 3;\n\t\t\t\tc.Margin = m;\n\t\t\t}\n\t\t\t_andWidth = width;\n\t\t\t_col--;\n\t\t}\n\t\t\n\t\tpublic void Span(int span) {\n\t\t\tif (_col == 0) throw new InvalidOperationException(\"Span() at row start\");\n\t\t\tint cc = _grid.ColumnDefinitions.Count;\n\t\t\t_col--;\n\t\t\tif (span != 0) { //if 0, will add 2 controls in 1 cell\n\t\t\t\tif (span < 0 || _col + span > cc) span = cc - _col;\n\t\t\t\tGrid.SetColumnSpan(LastDirect, span);\n\t\t\t\t_col += span;\n\t\t\t}\n\t\t\t_isSpan = true;\n\t\t}\n\t\t\n\t\t//If not all row cells filled, let the last control span all remaining cells, unless its span specified explicitly.\n\t\tvoid _SetLastSpan() {\n\t\t\tif (!_isSpan && _row >= 0 && _col > 0) {\n\t\t\t\tint n = _grid.ColumnDefinitions.Count - _col;\n\t\t\t\tif (n > 0) Grid.SetColumnSpan(LastDirect, n + 1);\n\t\t\t}\n\t\t\t_isSpan = false;\n\t\t}\n\t\t\n\t\tpublic void Skip(int span = 1) {\n\t\t\tBeforeAdd(false);\n\t\t\t_col += span;\n\t\t\t_isSpan = true;\n\t\t}\n\t\t\n\t\tpublic override void End() {\n\t\t\tbase.End();\n\t\t\t_SetLastSpan();\n\t\t}\n\t\t\n\t\tpublic (int column, int row) NextCell => (_col, _row);\n\t}\n\t\n\t#region current panel\n\t\n\t/// <summary>\n\t/// Ends adding controls etc to the window or nested panel (<see cref=\"StartGrid\"/> etc).\n\t/// </summary>\n\t/// <remarks>\n\t/// Always call this method to end a nested panel. For root panel it is optional if using <see cref=\"ShowDialog\"/>.\n\t/// </remarks>\n\tpublic wpfBuilder End() {\n\t\tif (!_p.ended) {\n\t\t\t_p.End();\n\t\t\tif (_p.parent != null) {\n\t\t\t\t_p = _p.parent;\n\t\t\t} else {\n#if NET9_0_OR_GREATER\n\t\t\t\tif (_unGray && _window is { } w && w.ThemeMode != ThemeMode.None && w.Background == SystemColors.ControlBrush) w.Background = null;\n#endif\n\t\t\t}\n\t\t}\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Sets column count and widths of current grid.\n\t/// </summary>\n\t/// <param name=\"widths\">\n\t/// Column widths.\n\t/// An argument can be:\n\t/// <br/>• <c>int</c> or <c>double</c> - <see cref=\"ColumnDefinition.Width\"/>. Value 0 means auto-size. Negative value is star-width, ie fraction of total width of star-sized columns. Examples: <c>50</c>, <c>-0.5</c>.\n\t/// <br/>• <see cref=\"Range\"/> - <see cref=\"ColumnDefinition.MinWidth\"/> and/or <see cref=\"ColumnDefinition.MaxWidth\"/>. Sets width value = -1 (star-sized). Examples: <c>50..150</c>, <c>50..</c> or <c>..150</c>.\n\t/// <br/>• tuple <c>(double value, Range minMax)</c> - width and min/max widths. Example: <c>(-2, 50..)</c>.\n\t/// <br/>• <see cref=\"ColumnDefinition\"/>.\n\t/// </param>\n\t/// <exception cref=\"InvalidOperationException\">Columns() in non-grid panel or after an <c>Add</c> function.</exception>\n\t/// <remarks>\n\t/// If this function not called, the table has 2 columns like <c>.Columns(0, -1)</c>.\n\t/// \n\t/// If there are star-sized columns, should be set width of the grid or of its container. Call <see cref=\"Width\"/> or <see cref=\"Size\"/> or <see cref=\"WinSize\"/>. But if the grid is in a cell of another grid, usually it's better to set column width of that grid to a non-zero value, ie let it be not auto-sized.\n\t/// </remarks>\n\tpublic wpfBuilder Columns(params WBGridLength[] widths) {\n\t\tvar g = Last as Grid ?? throw new InvalidOperationException(\"Columns() in wrong place\");\n\t\tg.ColumnDefinitions.Clear();\n\t\tforeach (var v in widths) g.ColumnDefinitions.Add(v.Column);\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Starts new row in current grid.\n\t/// </summary>\n\t/// <param name=\"height\">\n\t/// Row height. Can be:\n\t/// <br/>• <c>int</c> or <c>double</c> - <see cref=\"RowDefinition.Height\"/>. Value 0 means auto-size. Negative value is star-width, ie fraction of total height of star-sized rows. Examples: <c>50</c>, <c>-0.5</c>.\n\t/// <br/>• <see cref=\"Range\"/> - <see cref=\"RowDefinition.MinHeight\"/> and/or <see cref=\"RowDefinition.MaxHeight\"/>. Sets height value = -1 (star-sized). Examples: <c>50..150</c>, <c>50..</c> or <c>..150</c>.\n\t/// <br/>• tuple <c>(double value, Range minMax)</c> - height and min/max heights. Example: <c>(-2, 50..200)</c>.\n\t/// <br/>• <see cref=\"RowDefinition\"/>.\n\t/// </param>\n\t/// <exception cref=\"InvalidOperationException\">In non-grid panel.</exception>\n\t/// <remarks>\n\t/// Calling this function is optional, except when not all cells of previous row are explicitly filled.\n\t/// \n\t/// If there are star-sized rows, grid height should be defined. Call <see cref=\"Height\"/> or <see cref=\"Size\"/>. But if the grid is in a cell of another grid, usually it's better to set row height of that grid to a non-zero value, ie let it be not auto-sized.\n\t/// </remarks>\n\tpublic wpfBuilder Row(WBGridLength height) {\n\t\tif (_p.ended) throw new InvalidOperationException(\"Row() after End()\");\n\t\tvar g = _p as _Grid ?? throw new InvalidOperationException(\"Row() in non-grid panel\");\n\t\tg.Row(height);\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Starts new auto-sized row in current grid. The same as <c>Row(0)</c>. See <see cref=\"Row\"/>.\n\t/// </summary>\n\t/// <exception cref=\"InvalidOperationException\">In non-grid panel.</exception>\n\tpublic wpfBuilder R => Row(0);\n\t\n\t#endregion\n\t\n\t#region ctors, window\n\t\n\t//\tstatic readonly DependencyProperty _wpfBuilderProperty = DependencyProperty.RegisterAttached(\"_wpfBuilder\", typeof(wpfBuilder), typeof(Panel));\n\tstatic ConditionalWeakTable<Panel, wpfBuilder> s_cwt = new();\n\t//which is better? Both fast.\n\t\n\t/// <summary>\n\t/// This constructor creates <see cref=\"W.Window\"/> object with panel of specified type (default is <see cref=\"Grid\"/>).\n\t/// </summary>\n\t/// <param name=\"windowTitle\">Window title bar text.</param>\n\t/// <param name=\"panelType\">Panel type. Default is <see cref=\"Grid\"/>. Later you also can add nested panels of various types with <c>StartX</c> functions.</param>\n\tpublic wpfBuilder(string windowTitle, WBPanelType panelType = WBPanelType.Grid) {\n\t\t/*_container=*/\n\t\t_window = new Window() { Title = windowTitle };\n\t\t_AddRootPanel(_window, false, panelType, true);\n\t}\n\t\n\t/// <summary>\n\t/// This constructor creates panel of specified type (default is <see cref=\"Grid\"/>) and optionally adds to a container.\n\t/// </summary>\n\t/// <param name=\"container\">\n\t/// Window or some other element that will contain the panel. Should be empty, unless the type supports multiple direct child elements. Can be <c>null</c>.\n\t/// If the type (or base type) is <see cref=\"ContentControl\"/> (<see cref=\"W.Window\"/>, <see cref=\"TabItem\"/>, <see cref=\"ToolTip\"/>, etc), <see cref=\"Popup\"/> or <see cref=\"Decorator\"/> (eg <c>Border</c>), this function adds the panel to it. If <i>container</i> is <c>null</c> or an element of some other type, need to explicitly add the panel to it, like <c>container.Child = b.Panel;</c> or <c>container.Children.Add(b.Panel);</c> or <c>b.Tooltip(btt.Panel);</c> or <c>hwndSource.RootVisual = btt.Panel;</c> (the code depends on <i>container</i> type).\n\t/// </param>\n\t/// <param name=\"panelType\">Panel type. Default is <see cref=\"Grid\"/>. Later you also can add nested panels of various types with <c>StartX</c> functions.</param>\n\t/// <param name=\"setProperties\">\n\t/// Set some container's properties like other overload does. Default <c>true</c>. Currently sets these properties, and only if <i>container</i> is of type <c>Window</c>:\n\t/// <br/>• <see cref=\"Window.SizeToContent\"/>, except when <i>container</i> is <c>Canvas</c> or has properties <c>Width</c> and/or <c>Height</c> set.\n\t/// <br/>• <c>SnapsToDevicePixels</c> = <c>true</c>.\n\t/// <br/>• <c>WindowStartupLocation</c> = <c>Center</c>.\n\t/// <br/>• <c>Topmost</c> and <c>Background</c> depending on static properties <see cref=\"winTopmost\"/> and <see cref=\"winWhite\"/>.\n\t/// </param>\n\tpublic wpfBuilder(FrameworkElement container = null, WBPanelType panelType = WBPanelType.Grid, bool setProperties = true) {\n\t\t//_container=container; // ?? throw new ArgumentNullException(\"container\"); //can be null\n\t\t_window = container as Window;\n\t\t_AddRootPanel(container, true, panelType, setProperties);\n\t}\n\t\n\tvoid _AddRootPanel(FrameworkElement container, bool externalContainer, WBPanelType panelType, bool setProperties) {\n\t\t_p = panelType switch {\n\t\t\tWBPanelType.Grid => new _Grid(this),\n\t\t\tWBPanelType.Canvas => new _Canvas(this),\n\t\t\tWBPanelType.Dock => new _DockPanel(this),\n\t\t\tWBPanelType.HorizontalStack => new _StackPanel(this, false),\n\t\t\tWBPanelType.VerticalStack => new _StackPanel(this, true),\n\t\t\t_ => throw new InvalidEnumArgumentException()\n\t\t};\n\t\tif (_window != null) _p.panel.Margin = new(3);\n\t\tswitch (container) {\n\t\tcase ContentControl c: c.Content = _p.panel; break;\n\t\tcase Popup c: c.Child = _p.panel; break;\n\t\tcase Decorator c: c.Child = _p.panel; break;\n\t\t\t//rejected. Rare. Let users add explicitly, like container.Child = b.Panel.\n\t\t\t//\t\tcase Panel c: c.Children.Add(_p.panel); break;\n\t\t\t//\t\tcase ItemsControl c: c.Items.Add(_p.panel); break;\n\t\t\t//\t\tcase TextBlock c: c.Inlines.Add(_p.panel); break;\n\t\t\t//\t\tdefault: throw new NotSupportedException(\"Unsupported container type\");\n\t\t}\n\t\tif (setProperties) {\n\t\t\tif (_window != null) {\n\t\t\t\tif (panelType != WBPanelType.Canvas) {\n\t\t\t\t\tif (externalContainer) {\n\t\t\t\t\t\t_window.SizeToContent = (double.IsNaN(_window.Width) ? SizeToContent.Width : 0) | (double.IsNaN(_window.Height) ? SizeToContent.Height : 0);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t_window.SizeToContent = SizeToContent.WidthAndHeight;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t_window.SnapsToDevicePixels = true; //workaround for black line at bottom, for example when there is single CheckBox in Grid.\n\t\t\t\t//_window.UseLayoutRounding=true; //not here. Makes many controls bigger by 1 pixel when resizing window with grid, etc. Maybe OK if in _Add (for each non-panel element).\n\t\t\t\tif (_window.WindowStartupLocation == default) _window.WindowStartupLocation = WindowStartupLocation.CenterScreen;\n\t\t\t\tif (winTopmost) _window.Topmost = true;\n\t\t\t\tif (!winWhite) {\n\t\t\t\t\t_window.Background = SystemColors.ControlBrush;\n\t\t\t\t\t_unGray = true;\n\t\t\t\t\t\n\t\t\t\t\t//rejected: remove this background brush when _window.ThemeMode property changed. Slow. Instead remove in End(), if _unGray still true.\n\t\t\t\t\t//#if NET9_0_OR_GREATER\n\t\t\t\t\t//\t\t\t\t\tDependencyPropertyDescriptor.FromProperty(Window.OverridesDefaultStyleProperty, typeof(Window)).AddValueChanged(_window, static (o, _) => {\n\t\t\t\t\t//\t\t\t\t\t\tif (o is Window w && w.ThemeMode != ThemeMode.None && w.Background == SystemColors.ControlBrush) w.Background = null;\n\t\t\t\t\t//\t\t\t\t\t});\n\t\t\t\t\t//#endif\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\ts_cwt.Add(_p.panel, this);\n\t\t\n\t\tif (script.role == SRole.MiniProgram && _window != null) Loaded += () => { }; //set custom icon if need\n\t}\n#pragma warning disable CS0414 //when compiling for nuget .NET 8\n\tbool _unGray;\n#pragma warning restore CS0414\n\t\n\t/// <summary>\n\t/// Shows the window and waits until closed.\n\t/// </summary>\n\t/// <param name=\"owner\">Owner window or element. Sets <see cref=\"Window.Owner\"/>.</param>\n\t/// <exception cref=\"InvalidOperationException\">\n\t/// - Container is not of type <c>Window</c>.\n\t/// - Missing <see cref=\"End\"/> for a panel added with a <c>StartX</c> function.\n\t/// </exception>\n\t/// <remarks>\n\t/// Calls <see cref=\"End\"/>, sets <see cref=\"Window.Owner\"/> and calls <see cref=\"Window.ShowDialog\"/>.\n\t/// You can instead call these functions directly. Or call <see cref=\"Window.Show\"/> to show as non-modal window, ie don't wait. Or add <see cref=\"Panel\"/> to some container window or other element, etc.\n\t/// </remarks>\n\tpublic bool ShowDialog(DependencyObject owner = null) {\n\t\t_ThrowIfNotWindow();\n\t\tif (_IsNested) throw new InvalidOperationException(\"Missing End() for a StartX() panel\");\n\t\tEnd();\n\t\t//if (script.isWpfPreview) _window.Preview(); //no\n\t\t\n\t\t_window.Owner = owner == null ? null : owner as Window ?? Window.GetWindow(owner);\n\t\t//TODO3: try to support AnyWnd. Why WPF here supports only Window and not Popup or HwndSource?\n\t\t\n\t\treturn true == _window.ShowDialog();\n\t}\n\t\n\t/// <summary>\n\t/// Sets window width and/or height or/and min/max width/height.\n\t/// </summary>\n\t/// <param name=\"width\">Width or/and min/max width.</param>\n\t/// <param name=\"height\">Height or/and min/max height.</param>\n\t/// <exception cref=\"InvalidOperationException\">\n\t/// - Container is not of type <c>Window</c>.\n\t/// - Cannot be after the last <see cref=\"End\"/>.\n\t/// - Cannot be after <see cref=\"WinRect\"/> or <see cref=\"WinSaved\"/>.\n\t/// </exception>\n\t/// <remarks>\n\t/// Use WPF logical device-independent units, not physical pixels.\n\t/// </remarks>\n\t/// <seealso cref=\"WinRect\"/>\n\t/// <seealso cref=\"WinSaved\"/>\n\tpublic wpfBuilder WinSize(WBLength? width = null, WBLength? height = null) {\n\t\t_ThrowIfNotWindow();\n\t\t_ThrowIfWasWinRect();\n\t\tif (_IsWindowEnded) throw new InvalidOperationException(\"WinSize() cannot be after last End()\"); //although currently could be anywhere\n\t\tvar u = _window.SizeToContent;\n\t\tif (width != null) { var v = width.Value; v.ApplyTo(_window, false); u &= ~SizeToContent.Width; }\n\t\tif (height != null) { var v = height.Value; v.ApplyTo(_window, true); u &= ~SizeToContent.Height; }\n\t\t_window.SizeToContent = u;\n\t\treturn this;\n\t}\n\t\n\tvoid _ThrowIfWasWinRectXY([CallerMemberName] string m_ = null) {\n\t\tif (_wasWinXY != 0) throw new InvalidOperationException(m_ + \" cannot be after WinXY, WinRect or WinSaved.\");\n\t}\n\tvoid _ThrowIfWasWinRect([CallerMemberName] string m_ = null) {\n\t\tif (_wasWinXY == 2) throw new InvalidOperationException(m_ + \" cannot be after WinRect or WinSaved.\");\n\t}\n\tbyte _wasWinXY; //1 xy, 2 rect\n\t\n\t/// <summary>\n\t/// Sets window location.\n\t/// </summary>\n\t/// <param name=\"x\">X coordinate in screen. Physical pixels.</param>\n\t/// <param name=\"y\">Y coordinate in screen. Physical pixels.</param>\n\t/// <exception cref=\"InvalidOperationException\">\n\t/// - Container is not of type <c>Window</c>.\n\t/// - Cannot be after <see cref=\"WinXY\"/>, <see cref=\"WinRect\"/> or <see cref=\"WinSaved\"/>.\n\t/// </exception>\n\t/// <remarks>\n\t/// With this function use physical pixels, not WPF logical device-independent units.\n\t/// Call this function before showing the window. Don't change location/size-related window properties after that.\n\t/// Calls <see cref=\"ExtWpf.SetXY\"/>.\n\t/// </remarks>\n\t/// <seealso cref=\"WinSaved\"/>\n\tpublic wpfBuilder WinXY(int x, int y) {\n\t\t_ThrowIfNotWindow();\n\t\t_ThrowIfWasWinRectXY(); _wasWinXY = 1;\n\t\t_window.SetXY(x, y);\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Sets window rectangle (location and size).\n\t/// </summary>\n\t/// <param name=\"r\">Rectangle in screen. Physical pixels.</param>\n\t/// <exception cref=\"InvalidOperationException\">\n\t/// - Container is not of type <c>Window</c>.\n\t/// - Cannot be after <see cref=\"WinXY\"/>, <see cref=\"WinRect\"/> or <see cref=\"WinSaved\"/>.\n\t/// </exception>\n\t/// <remarks>\n\t/// With this function use physical pixels, not WPF logical device-independent units.\n\t/// Call this function before showing the window. Don't change location/size-related window properties after that.\n\t/// Calls <see cref=\"ExtWpf.SetRect\"/>.\n\t/// </remarks>\n\t/// <seealso cref=\"WinSaved\"/>\n\tpublic wpfBuilder WinRect(RECT r) {\n\t\t_ThrowIfNotWindow();\n\t\t_ThrowIfWasWinRectXY(); _wasWinXY = 2;\n\t\t_window.SetRect(r);\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Saves window xy/size/state when closing and restores when opening.\n\t/// </summary>\n\t/// <param name=\"saved\">String that the <i>save</i> action received previously. Can be <c>null</c> or <c>\"\"</c>, usually first time (still not saved).</param>\n\t/// <param name=\"save\">Called when closing the window. Receives string containing window xy/size/state. Can save it in registry, file, anywhere.</param>\n\t/// <exception cref=\"InvalidOperationException\">\n\t/// - Container is not of type <c>Window</c>.\n\t/// - Cannot be after <see cref=\"WinXY\"/>, <see cref=\"WinRect\"/> or <see cref=\"WinSaved\"/>.\n\t/// - Window is loaded.\n\t/// </exception>\n\t/// <remarks>\n\t/// Calls <see cref=\"WndSavedRect.Restore\"/>.\n\t/// Call this function before showing the window. Don't change location/size-related window properties after that.\n\t/// If you use <see cref=\"WinSize\"/>, call it before. It is used if size is still not saved. The same if you set window position or state.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// string rk = @\"HKEY_CURRENT_USER\\Software\\Au\\Test\", rv = \"winSR\";\n\t/// var b = new wpfBuilder(\"Window\").WinSize(300);\n\t/// b.Row(0).Add(\"Text\", out TextBox _);\n\t/// b.R.AddOkCancel();\n\t/// b.WinSaved(Microsoft.Win32.Registry.GetValue(rk, rv, null) as string, o => Microsoft.Win32.Registry.SetValue(rk, rv, o));\n\t/// b.End();\n\t/// ]]></code>\n\t/// </example>\n\tpublic wpfBuilder WinSaved(string saved, Action<string> save) {\n\t\t_ThrowIfNotWindow();\n\t\t_ThrowIfWasWinRectXY(); _wasWinXY = 2;\n\t\tWndSavedRect.Restore(_window, saved, save);\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Changes various window properties.\n\t/// </summary>\n\t/// <param name=\"startLocation\">Sets <see cref=\"WindowStartupLocation\"/>.</param>\n\t/// <param name=\"resizeMode\">Sets <see cref=\"Window.ResizeMode\"/>.</param>\n\t/// <param name=\"showActivated\">Sets <see cref=\"Window.ShowActivated\"/>.</param>\n\t/// <param name=\"showInTaskbar\">Sets <see cref=\"Window.ShowInTaskbar\"/>.</param>\n\t/// <param name=\"topmost\">Sets <see cref=\"Window.Topmost\"/>.</param>\n\t/// <param name=\"state\">Sets <see cref=\"Window.WindowState\"/>.</param>\n\t/// <param name=\"style\">Sets <see cref=\"Window.WindowStyle\"/>.</param>\n\t/// <param name=\"icon\">Sets <see cref=\"Window.Icon\"/>. Example: <c>.WinProperties(icon: BitmapFrame.Create(new Uri(@\"d:\\icons\\file.ico\")))</c>.</param>\n\t/// <param name=\"whiteBackground\">Set background color: <c>true</c> <see cref=\"SystemColors.WindowBrush\"/> (white), <c>false</c> <see cref=\"SystemColors.ControlBrush\"/> (gray). See also <see cref=\"winWhite\"/>, <see cref=\"Brush\"/>.</param>\n\t/// <exception cref=\"InvalidOperationException\">\n\t/// - Container is not of type <c>Window</c>.\n\t/// - <i>startLocation</i> or <i>state</i> used after <see cref=\"WinXY\"/>, <see cref=\"WinRect\"/> or <see cref=\"WinSaved\"/>.\n\t/// </exception>\n\t/// <remarks>\n\t/// The function uses only non-<c>null</c> parameters.\n\t/// Or you can change <see cref=\"Window\"/> properties directly, for example <c>b.Window.Topmost = true;</c>.\n\t/// </remarks>\n\tpublic wpfBuilder WinProperties(WindowStartupLocation? startLocation = null, ResizeMode? resizeMode = null, bool? showActivated = null, bool? showInTaskbar = null, bool? topmost = null, WindowState? state = null, WindowStyle? style = null, ImageSource icon = null, bool? whiteBackground = null) {\n\t\t_ThrowIfNotWindow();\n\t\tif (startLocation.HasValue) { _ThrowIfWasWinRectXY(\"WinProperties(startLocation)\"); _window.WindowStartupLocation = startLocation.Value; }\n\t\tif (resizeMode.HasValue) _window.ResizeMode = resizeMode.Value;\n\t\tif (showActivated.HasValue) _window.ShowActivated = showActivated.Value;\n\t\tif (showInTaskbar.HasValue) _window.ShowInTaskbar = showInTaskbar.Value;\n\t\tif (topmost.HasValue) _window.Topmost = topmost.Value;\n\t\tif (state.HasValue) { _ThrowIfWasWinRectXY(\"WinProperties(state)\"); _window.WindowState = state.Value; }\n\t\tif (style.HasValue) _window.WindowStyle = style.Value;\n\t\tif (whiteBackground.HasValue) { _window.Background = whiteBackground.Value ? SystemColors.WindowBrush : SystemColors.ControlBrush; _unGray = false; }\n\t\tif (icon != null) _window.Icon = icon;\n\t\treturn this;\n\t}\n\t\n\t#endregion\n\t\n\t#region properties, events\n\t\n\t/// <summary>\n\t/// Gets the top-level window.\n\t/// </summary>\n\t/// <returns><c>null</c> if container is not of type <c>Window</c>.</returns>\n\tpublic Window Window => _window;\n\t\n\t/// <summary>\n\t/// Gets current <see cref=\"Grid\"/> or <see cref=\"StackPanel\"/> or etc.\n\t/// </summary>\n\tpublic Panel Panel => _p.panel;\n\t\n\t/// <summary>\n\t/// Gets the last child or descendant element added in current panel. Before that returns current panel.\n\t/// </summary>\n\t/// <remarks>\n\t/// The \"set properties of last element\" functions set properties of this element.\n\t/// </remarks>\n\tpublic FrameworkElement Last => _p.LastAdded;\n\t\n\t/// <summary>\n\t/// Gets the child or descendant element added in current panel before adding <see cref=\"Last\"/>. Can be <c>null</c>.\n\t/// </summary>\n\t/// <remarks>\n\t/// For example, after calling an <c>Add</c> overload that adds 2 elements (the first is <see cref=\"Label\"/>), this property returns the <see cref=\"Label\"/>.\n\t/// </remarks>\n\tpublic FrameworkElement Last2 => _p.LastAdded2;\n\t\n\t//\tnot useful\n\t//\t/// <summary>\n\t//\t/// Gets the last direct child element added in current panel. Before that returns current panel or its parent <see cref=\"GroupBox\"/>.\n\t//\t/// </summary>\n\t//\tpublic FrameworkElement LastDirect => _p.LastDirect;\n\t\n\t/// <summary>\n\t/// When root panel loaded and visible. Once.\n\t/// </summary>\n\t/// <remarks>\n\t/// If the panel is in a <see cref=\"TabControl\"/>, this event is fired when the tab page is selected/loaded first time.\n\t/// When this event is fired, handles of visible <see cref=\"System.Windows.Interop.HwndHost\"/>-based controls are already created.\n\t/// </remarks>\n\tpublic event Action Loaded {\n\t\tadd {\n\t\t\tif (!_loadedEvent2) { _loadedEvent2 = true; Panel.Loaded += _Panel_Loaded; }\n\t\t\t_loadedEvent += value;\n\t\t}\n\t\tremove {\n\t\t\t_loadedEvent -= value;\n\t\t}\n\t}\n\tAction _loadedEvent;\n\tbool _loadedEvent2;\n\t\n\tprivate void _Panel_Loaded(object sender, RoutedEventArgs e) {\n\t\tvar p = sender as Panel;\n\t\tif (!p.IsVisible) return;\n\t\tp.Loaded -= _Panel_Loaded;\n\t\t\n\t\t//if role miniProgram, use assembly icon instead of apphost icon\n\t\tif (script.role == SRole.MiniProgram && _window != null && _window.Icon == null) {\n\t\t\tvar hm = Api.GetModuleHandle(Assembly.GetEntryAssembly().Location);\n\t\t\tif (default != Api.FindResource(hm, Api.IDI_APPLICATION, Api.RT_GROUP_ICON)) {\n\t\t\t\tvar w = _window.Hwnd();\n\t\t\t\ticon.FromModuleHandle_(hm, Dpi.Scale(16, w))?.SetWindowIcon(w, false);\n\t\t\t\ticon.FromModuleHandle_(hm, Dpi.Scale(32, w))?.SetWindowIcon(w, true);\n\t\t\t}\n\t\t}\n\t\t\n\t\t_loadedEvent?.Invoke();\n\t}\n\t\n\t/// <summary>\n\t/// When clicked <b>OK</b> or <b>Apply</b> button.\n\t/// </summary>\n\t/// <remarks>\n\t/// <see cref=\"Button.IsDefault\"/> is <c>true</c> if it is <b>OK</b> button.\n\t/// The parameter's property <b>Cancel</b> can be used to prevent closing the window.\n\t/// </remarks>\n\tpublic event Action<WBButtonClickArgs> OkApply;\n\t\n\t#endregion\n\t\n\t#region static\n\t\n\t/// <summary>\n\t/// <see cref=\"Grid.ShowGridLines\"/> of grid panels created afterwards.\n\t/// To be used at design time only.\n\t/// </summary>\n\tpublic static bool gridLines { get; set; }\n\t\n\t/// <summary>\n\t/// <see cref=\"Window.Topmost\"/> of windows created afterwards.\n\t/// Usually used at design time only, to make always on top of editor window.\n\t/// </summary>\n\tpublic static bool winTopmost { get; set; }\n\t\n\t/// <summary>\n\t/// If <c>true</c>, <c>wpfBuilder</c> constructors used afterwards will not change the window background color.\n\t/// </summary>\n\t/// <remarks>\n\t/// If <c>true</c>, <c>wpfBuilder</c> constructors don't change the background color; then the color depends on theme.\n\t/// If <c>false</c> constructors set standard color of dialog windows, usually light gray.\n\t/// Default value depends on application's theme and usually is <c>true</c> if using a custom theme.\n\t/// </remarks>\n\tpublic static bool winWhite { get => s_winWhite ?? (_GetThemed() != _Themed.None); set { s_winWhite = value; } }\n\tstatic bool? s_winWhite;\n\t\n\t//\t/// <summary>\n\t//\t/// Default modifyPadding option value. See <see cref=\"Options\"/>.\n\t//\t/// </summary>\n\t//\tpublic static bool modifyPadding { get; set; }\n\t\n\t#endregion\n\t\n\t#region add\n\t\n\t/// <summary>\n\t/// Changes some options for elements added afterwards.\n\t/// </summary>\n\t/// <param name=\"modifyPadding\">Let <c>Add</c> adjust the <c>Padding</c> property of some controls to align content better when using default theme. Default value of this option depends on application's theme.</param>\n\t/// <param name=\"rightAlignLabels\">Right-align <see cref=\"Label\"/> controls in grid cells.</param>\n\t/// <param name=\"margin\">Default margin of elements. If not set, default margin is 3 in all sides. Default margin of nested panels is 0; this option is not used.</param>\n\t/// <param name=\"showToolTipOnKeyboardFocus\">Show tooltips when the tooltip owner element receives the keyboard focus when using keys to focus controls or open the window. If <c>true</c>, it can be set separately for each tooltip or owner element with <see cref=\"ToolTip.ShowsToolTipOnKeyboardFocus\"/> or <see cref=\"ToolTipService.SetShowsToolTipOnKeyboardFocus(DependencyObject, bool?)\"/>.</param>\n\t/// <param name=\"bindLabelVisibility\">Let <see cref=\"LabeledBy\"/> and an <c>Add</c> overload that adds 2 elements (the first is <see cref=\"Label\"/>) bind the <c>Visibility</c> property of the label to that of the last added element, to automatically hide/show the label together with the element. Also binds <c>IsEnabled</c> if it is <see cref=\"Label\"/> or <see cref=\"TextBlock\"/>.</param>\n\tpublic wpfBuilder Options(bool? modifyPadding = null, bool? rightAlignLabels = null, Thickness? margin = null, bool? showToolTipOnKeyboardFocus = null, bool? bindLabelVisibility = null) {\n\t\tif (modifyPadding != null) _opt_modifyPadding = modifyPadding.Value;\n\t\tif (rightAlignLabels != null) _opt_rightAlignLabels = rightAlignLabels.Value;\n\t\tif (margin != null) _opt_margin = margin.Value;\n\t\tif (showToolTipOnKeyboardFocus != null) _opt_showToolTipOnKeyboardFocus = showToolTipOnKeyboardFocus.Value;\n\t\tif (bindLabelVisibility != null) _opt_bindLabelVisibility = bindLabelVisibility.Value;\n\t\treturn this;\n\t}\n\tbool? _opt_modifyPadding;\n\tbool _opt_rightAlignLabels;\n\tThickness _opt_margin = new(3);\n\t//string _opt_radioGroup; //rejected. Radio buttons have problems with high DPI and should not be used. Or can put groups in panels.\n\t//\tdouble _opt_checkMargin=3; //rejected\n\tbool _opt_showToolTipOnKeyboardFocus;\n\tbool _opt_bindLabelVisibility;\n\t\n\tbool? _ModifyPadding => _opt_modifyPadding == false ? false : _IsThemed switch { _Themed.Wpf => null, _Themed.Other => _opt_modifyPadding == true, _ => true };\n\t\n\tvoid _Add(FrameworkElement e, object text, bool raw = false) {\n\t\tbool childOfLast = _child; _child = false;\n\t\tif (!raw) {\n\t\t\tif (e is Control c) {\n\t\t\t\t//rejected: modify padding etc through XAML. Not better than this.\n\t\t\t\t//rejected: use _opt_modifyPadding only if font Segoe UI. Tested with several fonts.\n\t\t\t\tswitch (c) {\n\t\t\t\tcase Label:\n\t\t\t\t\tswitch (_ModifyPadding) {\n\t\t\t\t\tcase true: c.Padding = new(1, 2, 1, 1); break; //default 5\n\t\t\t\t\tcase null: c.Padding = new(1); c.VerticalAlignment = VerticalAlignment.Center; break;\n\t\t\t\t\t}\n\t\t\t\t\tif (_opt_rightAlignLabels) c.HorizontalAlignment = HorizontalAlignment.Right;\n\t\t\t\t\tbreak;\n\t\t\t\tcase TextBox:\n\t\t\t\tcase PasswordBox:\n\t\t\t\t\tif (_ModifyPadding == true) c.Padding = new(2, 1, 1, 2); //default padding 0, height 18\n\t\t\t\t\tbreak;\n\t\t\t\tcase Button:\n\t\t\t\t\tif (text is string && _ModifyPadding == true) c.Padding = new(5, 1, 5, 2); //default 1\n\t\t\t\t\tbreak;\n\t\t\t\tcase ToggleButton:\n\t\t\t\t\tc.HorizontalAlignment = HorizontalAlignment.Left; //default stretch\n\t\t\t\t\tc.VerticalAlignment = VerticalAlignment.Center; //default top. Note: VerticalContentAlignment bad on Win7.\n\t\t\t\t\t\n\t\t\t\t\t//partial workaround for squint CheckBox/RadioButton when High DPI.\n\t\t\t\t\t//\tWithout it, check mark size/alignment is different depending on control's xy.\n\t\t\t\t\t//\tWith it at least all controls are equal, either bad (eg DPI 125%) or good (DPI 150%).\n\t\t\t\t\t//\tWhen bad, normal CheckBox check mark now still looks good. Only third state and RadioButtons look bad, but it is better than when controls look differently.\n\t\t\t\t\t//\tBut now at 150% DPI draws thick border.\n\t\t\t\t\t//c.UseLayoutRounding=true;\n\t\t\t\t\t//c.SnapsToDevicePixels=true; //does not help\n\t\t\t\t\tbreak;\n\t\t\t\tcase ComboBox cb:\n\t\t\t\t\t//Change padding because default Windows font Segoe UI is badly centered vertically. Too big space above text, and too big control height.\n\t\t\t\t\t//Tested: changed padding isn't the reason of different control heights or/and arrows when high DPI.\n\t\t\t\t\tif (cb.IsEditable) {\n\t\t\t\t\t\tif (_ModifyPadding == true) c.Padding = new(2, 1, 2, 2); //default (2)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (_ModifyPadding == true) c.Padding = new(5, 2, 4, 3); //default (6,3,5,3)\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t} else if (e is Image) {\n\t\t\t\te.UseLayoutRounding = true; //workaround for blurred images\n\t\t\t}\n\t\t\t\n\t\t\t//workaround for:\n\t\t\t//\t1. Blurred images in some cases.\n\t\t\t//\t2. High DPI: Different height of controls of same class, eg TextBox, ComboBox.\n\t\t\t//\t3. High DPI: Different height/shape/alignment of control parts, eg CheckBox/RadioButton check mark and ComboBox arrow.\n\t\t\t//\tBad: on DPI 150% makes control borders 2-pixel.\n\t\t\t//\tRejected. Thick border is very noticeable, especially TabControl. Different control sizes, v check mark and v arrow aren't so noticeable. Radio buttons and null checkboxes rarely used. Most my tested WPF programs don't use this.\n\t\t\t//\t\t\te.UseLayoutRounding=true;\n\t\t\t//\t\t\te.SnapsToDevicePixels=true; //does not help\n\t\t\t\n\t\t\tif (text != null) {\n\t\t\t\tswitch (e) {\n\t\t\t\tcase HeaderedContentControl u: u.Header = text; break; //GroupBox, Expander\n\t\t\t\tcase HeaderedItemsControl u: u.Header = text; break;\n\t\t\t\tcase ContentControl u: u.Content = text; break; //Label, buttons, etc\n\t\t\t\tcase TextBox u: u.Text = text.ToString(); break;\n\t\t\t\tcase PasswordBox u: u.Password = text.ToString(); break;\n\t\t\t\tcase ComboBox u: u.Text = text.ToString(); break;\n\t\t\t\tcase TextBlock u: u.Text = text.ToString(); break;\n\t\t\t\tcase RichTextBox u: u.AppendText(text.ToString()); break;\n\t\t\t\t//default: throw new NotSupportedException($\"Add() cannot set text/content of {e.GetType().Name}.\");\n\t\t\t\tdefault:\n\t\t\t\t\tif (!_PropGetSet<string>.TryCreate(e, \"Text\", out var pgs)) throw new NotSupportedException($\"Add() cannot set text/content of {e.GetType().Name}.\");\n\t\t\t\t\tpgs.Set(text.ToString());\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (!(childOfLast || e is GridSplitter)) e.Margin = _opt_margin;\n\t\t\n\t\t_AddToParent(e, childOfLast);\n\t\tif (_alsoAll != null) {\n\t\t\t_alsoAllArgs ??= new WBAlsoAllArgs();\n\t\t\tif (_p is _Grid g) {\n\t\t\t\tvar v = g.NextCell;\n\t\t\t\t_alsoAllArgs.Column = v.column - 1;\n\t\t\t\t_alsoAllArgs.Row = v.row;\n\t\t\t} else {\n\t\t\t\t_alsoAllArgs.Column = _alsoAllArgs.Row = -1;\n\t\t\t}\n\t\t\t_alsoAll(this, _alsoAllArgs);\n\t\t}\n\t}\n\t\n\tvoid _AddToParent(FrameworkElement e, bool childOfLast) {\n\t\tif (childOfLast) { //info: BeforeAdd throws exception if Last is panel\n\t\t\tswitch (Last) {\n\t\t\tcase ContentControl d: d.Content = e; break;\n\t\t\tcase Decorator d: d.Child = e; break;\n\t\t\t//case Panel d: d.Children.Add(e); break; //no, cannot add multiple items because Last becomes the added child\n\t\t\tdefault: throw new NotSupportedException($\"Cannot add child to {Last.GetType().Name}.\");\n\t\t\t}\n\t\t\t_p.SetLastAdded(e);\n\t\t} else {\n\t\t\t_p.Add(e);\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Sets option for the next <c>Add</c> call to add the element as a child or content of the last added element (<see cref=\"Last\"/>), which must be a <see cref=\"Decorator\"/> (for example <see cref=\"C.Border\"/> or <see cref=\"AdornerDecorator\"/>) or <see cref=\"ContentControl\"/> (for example <see cref=\"Button\"/>). Also will not change the <c>Margin</c> property.\n\t/// </summary>\n\t/// <exception cref=\"NotSupportedException\">The last added element is not of a supported type.</exception>\n\t/// <remarks>\n\t/// Also applies to functions that call <c>Add</c>, for example <c>AddButton</c>. Does not affect the obsolete hidden <c>Add</c> overloads with parameter <i>flags</i>.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// b.R.Add<Border>().Border(Brushes.Green).Child().Add(out TextBlock t1, \"Text\").Margin(\"L3R3\");\n\t/// ]]></code>\n\t/// </example>\n\tpublic wpfBuilder Child() {\n\t\tif (!(Last is ContentControl or Decorator)) throw new NotSupportedException($\"Cannot add child to {Last.GetType().Name}.\");\n\t\t_child = true;\n\t\treturn this;\n\t}\n\tbool _child;\n\t\n\t/// <summary>\n\t/// Adds an existing element.\n\t/// </summary>\n\t/// <param name=\"element\"></param>\n\t/// <param name=\"raw\">Don't change element properties, except margin. If <c>false</c> (default), works like other <c>Add</c> overloads: for some element types sets padding, alignment, properties specified in <see cref=\"wpfBuilder.Options\"/>, etc.</param>\n\t/// <seealso cref=\"And\"/>\n\t/// <seealso cref=\"Child\"/>\n\t/// <seealso cref=\"Options\"/>\n\t/// <seealso cref=\"AlsoAll\"/>\n\tpublic wpfBuilder Add(FrameworkElement element, bool raw = false) {\n\t\t_p.BeforeAdd(_child);\n\t\t_Add(element, null, raw);\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Creates and adds element of type <i>T</i> (control etc of any type).\n\t/// </summary>\n\t/// <param name=\"variable\">\n\t/// Receives element's variable. The function creates element of variable's type. You can use the variable to set element's properties before showing window or/and to get value after.\n\t/// Examples: <c>.Add(out CheckBox c1, \"Text\")</c>, <c>.Add(out _textBox1)</c>. If don't need a variable: <c>.Add(out Label _, \"Text\")</c> or <c>.Add&lt;Label>(\"Text\")</c>.\n\t/// </param>\n\t/// <param name=\"text\">\n\t/// Text, header or other content. Supported element types (or base types):\n\t/// <br/>• <see cref=\"TextBox\"/> - sets <c>Text</c> property.\n\t/// <br/>• <see cref=\"ComboBox\"/> - sets <c>Text</c> property (see also <see cref=\"Items\"/>).\n\t/// <br/>• <see cref=\"PasswordBox\"/> - sets <c>Password</c> property.\n\t/// <br/>• <see cref=\"TextBlock\"/> - sets <c>Text</c> property (see also <see cref=\"FormatText\"/> and <see cref=\"formattedText\"/>).\n\t/// <br/>• <see cref=\"HeaderedContentControl\"/>, <see cref=\"HeaderedItemsControl\"/> - sets <c>Header</c> property (see also <see cref=\"FormatText\"/> and <see cref=\"formattedText\"/>).\n\t/// <br/>• <see cref=\"ContentControl\"/> - sets <c>Content</c> property (can be string, other element, etc) (see also <see cref=\"FormatText\"/> and <see cref=\"formattedText\"/>).\n\t/// <br/>• <see cref=\"RichTextBox\"/> - calls <c>AppendText</c> (see also <see cref=\"LoadFile\"/>).\n\t/// <br/>• Other element types that have <c>Text</c> property.\n\t/// </param>\n\t/// <exception cref=\"NotSupportedException\">The function does not support non-<c>null</c> <i>text</i> for this element type.</exception>\n\t/// <seealso cref=\"And\"/>\n\t/// <seealso cref=\"Child\"/>\n\t/// <seealso cref=\"Options\"/>\n\t/// <seealso cref=\"AlsoAll\"/>\n\tpublic wpfBuilder Add<T>(out T variable, object text = null) where T : FrameworkElement, new() {\n\t\t_p.BeforeAdd(_child);\n\t\tvariable = new T();\n\t\t_Add(variable, text);\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Creates and adds element of type <i>T</i> (any type). This overload can be used when don't need element's variable.\n\t/// </summary>\n\t/// <param name=\"text\">Text, header or other content. More info - see other overload.</param>\n\t/// <exception cref=\"NotSupportedException\">The function does not support non-<c>null</c> <i>text</i> for this element type.</exception>\n\t/// <seealso cref=\"And\"/>\n\t/// <seealso cref=\"Child\"/>\n\t/// <seealso cref=\"Options\"/>\n\t/// <seealso cref=\"AlsoAll\"/>\n\tpublic wpfBuilder Add<T>(object text = null) where T : FrameworkElement, new()\n\t\t=> Add(out T _, text);\n\t\n\t/// <summary>\n\t/// Adds 2 elements: <see cref=\"Label\"/> and element of type <i>T</i> (control etc of any type).\n\t/// </summary>\n\t/// <param name=\"label\">Label text. Usually string or <see cref=\"TextBlock\"/>. Example: <c>new TextBlock() { TextWrapping = TextWrapping.Wrap, Text = \"long text\" }</c>.</param>\n\t/// <param name=\"variable\">Variable of second element. More info - see other overload.</param>\n\t/// <param name=\"text\">Text, header or other content of second element. More info - see other overload.</param>\n\t/// <param name=\"row2\">If not <c>null</c>, after adding first element calls <see cref=\"Row\"/> with this argument.</param>\n\t/// <exception cref=\"NotSupportedException\">If the function does not support non-<c>null</c> <i>text</i> for this element type.</exception>\n\t/// <remarks>\n\t/// Finally calls <see cref=\"LabeledBy\"/>; it sets <see cref=\"Label.Target\"/>, calls <see cref=\"System.Windows.Automation.AutomationProperties.SetLabeledBy\"/> and applies the <i>bindLabelVisibility</i> option (see <see cref=\"Options\"/>).\n\t/// </remarks>\n\tpublic wpfBuilder Add<T>(object label, out T variable, object text = null, WBGridLength? row2 = null) where T : FrameworkElement, new() {\n\t\tif (_child) throw new InvalidOperationException();\n\t\tAdd(out Label var1, label);\n\t\tif (row2 != null) Row(row2.Value);\n\t\tAdd(out variable, text);\n\t\treturn this.LabeledBy(var1);\n\t}\n\t\n\t/// <summary>\n\t/// Adds 2 elements: <see cref=\"Label\"/> and an existing element (control etc of any type).\n\t/// </summary>\n\t/// <inheritdoc cref=\"Add{T}(object, out T, object, WBGridLength?)\"/>\n\tpublic wpfBuilder Add(object label, FrameworkElement element, WBGridLength? row2 = null) {\n\t\tif (_child) throw new InvalidOperationException();\n\t\tAdd(out Label var1, label);\n\t\tif (row2 != null) Row(row2.Value);\n\t\tAdd(element);\n\t\treturn this.LabeledBy(var1);\n\t}\n\t\n#if !DEBUG && NET9_0_OR_GREATER\n#pragma warning disable CS1591 //Missing XML comment for publicly visible type or member\n\t//[Obsolete(\"Instead of flags use: b.Child().Add(...)\")]\n\t[EditorBrowsable(EditorBrowsableState.Never)]\n\tpublic wpfBuilder Add(FrameworkElement element, WBAdd flags) {\n\t\t_p.BeforeAdd(_child = flags.Has(WBAdd.ChildOfLast));\n\t\t_Add(element, null, flags.Has(WBAdd.DontSetProperties));\n\t\treturn this;\n\t}\n\t\n\t//[Obsolete(\"Instead of flags use: b.Child().Add(...)\")]\n\t[EditorBrowsable(EditorBrowsableState.Never)]\n\t[OverloadResolutionPriority(-1)]\n\tpublic wpfBuilder Add<T>(out T variable, object text = null, WBAdd flags = 0) where T : FrameworkElement, new() {\n\t\t_p.BeforeAdd(_child = flags.Has(WBAdd.ChildOfLast));\n\t\tvariable = new T();\n\t\t_Add(variable, text, flags.Has(WBAdd.DontSetProperties));\n\t\treturn this;\n\t}\n\t\n\t//[Obsolete(\"Instead of flags use: b.Child().Add(...)\")]\n\t[EditorBrowsable(EditorBrowsableState.Never)]\n\t//[OverloadResolutionPriority(-1)] //no, then woul choose Add<T>(out T variable, object text = null)\n\tpublic wpfBuilder Add<T>(out T variable, WBAdd flags) where T : FrameworkElement, new()\n\t\t=> Add(out variable, null, flags);\n\t\n\t//[Obsolete(\"Instead of flags use: b.Child().Add(...)\")]\n\t[EditorBrowsable(EditorBrowsableState.Never)]\n\t[OverloadResolutionPriority(-1)]\n\tpublic wpfBuilder Add<T>(object text = null, WBAdd flags = 0) where T : FrameworkElement, new()\n\t\t=> Add(out T _, text, flags);\n\t\n\t[EditorBrowsable(EditorBrowsableState.Never)]\n\t[Obsolete] //Too many overloads, confusing. Instead can use Last2 or LabeledBy.\n\tpublic wpfBuilder Add<T1, T2>(out T1 var1, object text1, out T2 var2, object text2 = null, WBGridLength? row2 = null) where T1 : FrameworkElement, new() where T2 : FrameworkElement, new() {\n\t\tAdd(out var1, text1);\n\t\tif (row2 != null) Row(row2.Value);\n\t\tAdd(out var2, text2); //note: no flags\n\t\treturn this.LabeledBy(var1);\n\t}\n#pragma warning restore CS1591\n#endif\n\t\n\t/// <summary>\n\t/// Adds button with <see cref=\"ButtonBase.Click\"/> event handler.\n\t/// </summary>\n\t/// <param name=\"variable\">Receives button's variable.</param>\n\t/// <param name=\"text\">Text/content (<see cref=\"ContentControl.Content\"/>).</param>\n\t/// <param name=\"click\">Action to call when the button clicked. Its parameter's property <c>Cancel</c> can be used to prevent closing the window when clicked this <b>OK</b> button. Not called if validation fails.</param>\n\t/// <param name=\"flags\"></param>\n\t/// <remarks>\n\t/// If <i>flags</i> contains <c>OK</c> or <c>Apply</c> or <c>Validate</c> and this window contains elements for which was called <see cref=\"Validation\"/>, on click performs validation; if fails, does not call the <i>click</i> action and does not close the window.\n\t/// </remarks>\n\tpublic wpfBuilder AddButton(out Button variable, object text, Action<WBButtonClickArgs> click, WBBFlags flags = 0/*, Action<WBButtonClickArgs> clickSplit = null*/) {\n\t\tAdd(out variable, text);\n\t\tvar c = variable;\n\t\tif (flags.Has(WBBFlags.OK)) c.IsDefault = true;\n\t\tif (flags.Has(WBBFlags.Cancel)) c.IsCancel = true;\n\t\tif (flags.HasAny(WBBFlags.OK | WBBFlags.Cancel | WBBFlags.Apply)) { c.MinWidth = 70; c.MinHeight = 21; }\n\t\tif (click != null || flags.HasAny(WBBFlags.OK | WBBFlags.Cancel | WBBFlags.Apply | WBBFlags.Validate)) {\n\t\t\tc.Click += (_, _) => {\n\t\t\t\tvar w = _FindWindow(c);\n\t\t\t\tif (flags.HasAny(WBBFlags.OK | WBBFlags.Apply | WBBFlags.Validate) && !_Validate(w, c)) return;\n\t\t\t\tbool needEvent = flags.HasAny(WBBFlags.OK | WBBFlags.Apply) && OkApply != null;\n\t\t\t\tvar e = (needEvent || click != null) ? new WBButtonClickArgs { Button = c, Window = w } : null;\n\t\t\t\tif (needEvent) {\n\t\t\t\t\tOkApply(e);\n\t\t\t\t\tif (e.Cancel) return;\n\t\t\t\t}\n\t\t\t\tif (click != null) {\n\t\t\t\t\tclick(e);\n\t\t\t\t\tif (e.Cancel) return;\n\t\t\t\t}\n\t\t\t\tif (flags.Has(WBBFlags.OK)) {\n\t\t\t\t\tbool modal = w.IsModal_() != false;\n\t\t\t\t\tif (modal) {\n\t\t\t\t\t\ttry { w.DialogResult = true; }\n\t\t\t\t\t\tcatch (InvalidOperationException) { modal = false; } //failed to detect modal?\n\t\t\t\t\t}\n\t\t\t\t\tif (!modal) w.Close();\n\t\t\t\t} else if (flags.Has(WBBFlags.Cancel)) {\n\t\t\t\t\tw.Close(); //info: IsCancel ignored if nonmodal\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t\t//if(clickSplit!=null) c.ClickSplit+=clickSplit;\n\t\t//FUTURE: split-button.\n\t\treturn this;\n\t}\n\t//\t/// <param name=\"clickSplit\">\n\t//\t/// If not null, creates split-button. Action to call when the arrow part clicked. Example:\n\t//\t/// <br/><c>b => { int mi = popupMenu.showSimple(\"1 One|2 Two\", b, (0, b.Height)); }</c>\n\t//\t/// </param>\n\t\n\t/// <inheritdoc cref=\"AddButton(out Button, object, Action{WBButtonClickArgs}, WBBFlags)\"/>\n\tpublic wpfBuilder AddButton(object text, Action<WBButtonClickArgs> click, WBBFlags flags = 0/*, Action<WBButtonClickArgs> clickSplit = null*/) {\n\t\treturn AddButton(out _, text, click, flags);\n\t}\n\t\n\t/// <summary>\n\t/// Adds button that closes the window and sets <see cref=\"ResultButton\"/>.\n\t/// </summary>\n\t/// <param name=\"text\">Text/content (<see cref=\"ContentControl.Content\"/>).</param>\n\t/// <param name=\"result\"><see cref=\"ResultButton\"/> value when clicked this button.</param>\n\t/// <remarks>\n\t/// When clicked, sets <see cref=\"ResultButton\"/> = <i>result</i>, closes the window, and <see cref=\"ShowDialog\"/> returns <c>true</c>.\n\t/// </remarks>\n\tpublic wpfBuilder AddButton(object text, int result/*, Action<WBButtonClickArgs> clickSplit = null*/) {\n\t\tAdd(out Button c, text);\n\t\tc.Click += (_, _) => { _resultButton = result; _FindWindow(c).DialogResult = true; };\n\t\t//if(clickSplit!=null) c.ClickSplit+=clickSplit;\n\t\treturn this;\n\t}\n\t//\t/// <param name=\"clickSplit\">\n\t//\t/// If not null, creates split-button. Action to call when the arrow part clicked. Example:\n\t//\t/// <br/><c>b => { int mi = popupMenu.showSimple(\"1 One|2 Two\", b, (0, b.Height)); }</c>\n\t//\t/// </param>\n\t\n\t/// <summary>\n\t/// If the window closed with an <see cref=\"AddButton(object, int)\"/> button, returns its <i>result</i>. Else returns 0.\n\t/// Note: if the button is in a tab page, use the <see cref=\"wpfBuilder\"/> variable of that page.\n\t/// </summary>\n\tpublic int ResultButton => _resultButton;\n\tint _resultButton;\n\t\n\t/// <summary>\n\t/// Adds <b>OK</b> and/or <b>Cancel</b> and/or <b>Apply</b> buttons.\n\t/// </summary>\n\t/// <param name=\"ok\">Text of <b>OK</b> button. If <c>null</c>, does not add the button.</param>\n\t/// <param name=\"cancel\">Text of <b>Cancel</b> button. If <c>null</c>, does not add the button.</param>\n\t/// <param name=\"apply\">Text of <b>Apply</b> button. If <c>null</c>, does not add the button.</param>\n\t/// <param name=\"stackPanel\">Add a right-bottom aligned <see cref=\"StackPanel\"/> that contains the buttons. See <see cref=\"StartOkCancel\"/>. If <c>null</c> (default), adds if not already in a stack panel, except when there is 1 button.</param>\n\t/// <remarks>\n\t/// Sets properties of <b>OK</b>/<b>Cancel</b> buttons so that click and <c>Enter</c>/<c>Esc</c> close the window; then <see cref=\"ShowDialog\"/> returns <c>true</c> on <b>OK</b>, <c>false</c> on <b>Cancel</b>.\n\t/// See also event <see cref=\"OkApply\"/>.\n\t/// </remarks>\n\tpublic wpfBuilder AddOkCancel(string ok = \"OK\", string cancel = \"Cancel\", string apply = null, bool? stackPanel = null)\n\t\t=> AddOkCancel(out _, out _, out _, ok, cancel, apply, stackPanel);\n\t\n\t/// <param name=\"bOK\">Variable of <b>OK</b> button.</param>\n\t/// <param name=\"bCancel\">Variable of <b>Cancel</b> button.</param>\n\t/// <param name=\"bApply\">Variable of <b>Apply</b> button.</param>\n\t/// <inheritdoc cref=\"AddOkCancel(string, string, string, bool?)\"/>\n\tpublic wpfBuilder AddOkCancel(out Button bOK, out Button bCancel, out Button bApply, string ok = \"OK\", string cancel = \"Cancel\", string apply = null, bool? stackPanel = null) {\n\t\tint n = 0; if (ok != null) n++; if (cancel != null) n++;\n\t\tif (n == 0) throw new ArgumentNullException();\n\t\tbool stack = stackPanel ?? (n > 1 && !(_p is _StackPanel));\n\t\tif (stack) StartOkCancel();\n\t\tif (ok != null) AddButton(out bOK, ok, null, WBBFlags.OK); else bOK = null;\n\t\tif (cancel != null) AddButton(out bCancel, cancel, null, WBBFlags.Cancel); else bCancel = null;\n\t\tif (apply != null) AddButton(out bApply, apply, null, WBBFlags.Apply); else bApply = null;\n\t\tif (stack) End();\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Adds <see cref=\"Separator\"/> control.\n\t/// </summary>\n\t/// <param name=\"vertical\">If <c>true</c>, adds vertical separator. If <c>false</c>, horizontal. If <c>null</c> (default), adds vertical if in horizontal stack panel, else adds horizontal.</param>\n\t/// <remarks>\n\t/// In <see cref=\"Canvas\"/> panel separator's default size is 1x1. Need to set size, like <c>.AddSeparator().XY(0, 50, 100, 1)</c>.\n\t/// </remarks>\n\tpublic wpfBuilder AddSeparator(bool? vertical = null) {\n\t\tAdd(out Separator c);\n\t\tif (vertical ?? (_p.panel is StackPanel p && p.Orientation == Orientation.Horizontal)) {\n\t\t\tc.Style = _style_VertSep ??= c.FindResource(ToolBar.SeparatorStyleKey) as Style;\n\t\t}\n\t\tc.UseLayoutRounding = true; //workaround: separators of different thickness when high DPI\n\t\treturn this;\n\t}\n\tStyle _style_VertSep;\n\t\n\t/// <summary>\n\t/// Adds enum members as <see cref=\"StackPanel\"/> with checkboxes (if it's a <c>[Flags]</c> enum) or <see cref=\"ComboBox\"/> control.\n\t/// </summary>\n\t/// <param name=\"e\">Variable for getting result later. See <see cref=\"EnumUI{TEnum}.Result\"/>.</param>\n\t/// <param name=\"init\">Initial value.</param>\n\t/// <param name=\"items\">Enum members and their text/tooltip. Optional. Text can be: <c>null</c>, <c>\"text\"</c>, <c>\"text|tooltip\"</c>, <c>\"|tooltip\"</c>.</param>\n\t/// <param name=\"label\">If not <c>null</c>, adds a <see cref=\"GroupBox\"/> or <see cref=\"Label\"/> control with this label. If it's a <c>[Flags]</c> enum, adds <see cref=\"GroupBox\"/> as parent of checkboxes, else adds <see cref=\"Label\"/> before the <see cref=\"ComboBox\"/> (uses 2 grid cells).</param>\n\t/// <param name=\"vertical\">Vertical stack. Default <c>true</c>.</param>\n\t[EditorBrowsable(EditorBrowsableState.Never)] //obsolete. Not flexible enough (eg users may want Grid, not StackPanel), etc. Added EnumUI examples in cookbook.\n\tpublic wpfBuilder AddEnum<TEnum>(out EnumUI<TEnum> e, TEnum init = default, (TEnum value, string text)[] items = null, string label = null, bool vertical = true) where TEnum : unmanaged, Enum {\n\t\tif (typeof(TEnum).IsDefined(typeof(FlagsAttribute), false)) {\n\t\t\tif (label != null) StartStack<GroupBox>(label, vertical: vertical);\n\t\t\telse StartStack(vertical: vertical);\n\t\t\te = new EnumUI<TEnum>(Panel, init, items);\n\t\t\tEnd();\n\t\t} else {\n\t\t\tComboBox cb;\n\t\t\tif (label != null) Add(label, out cb);\n\t\t\telse Add(out cb);\n\t\t\te = new EnumUI<TEnum>(cb, init, items);\n\t\t}\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Adds one or more empty cells in current row of current grid.\n\t/// </summary>\n\t/// <param name=\"span\">Column count.</param>\n\t/// <exception cref=\"InvalidOperationException\">In non-grid panel.</exception>\n\t/// <remarks>\n\t/// Actually just changes column index where next element will be added.\n\t/// </remarks>\n\tpublic wpfBuilder Skip(int span = 1) {\n\t\tif (span < 0) throw new ArgumentException();\n\t\tvar g = _p as _Grid ?? throw new InvalidOperationException(\"Skip() in non-grid panel\");\n\t\tg.Skip(span);\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Sets to add next element in the same grid cell as previous element.\n\t/// </summary>\n\t/// <param name=\"width\">Width of next element. If negative - width of previous element. Also it adds to the corresponding margin of other element. If 0, simply adds in the same place as previous element.</param>\n\t/// <exception cref=\"InvalidOperationException\">In non-grid panel or in a wrong place.</exception>\n\t/// <remarks>\n\t/// Can be used to add 2 elements in 1 cell as a cheaper and more concise way than with a <c>StartX</c> function.\n\t/// Next element will inherit column index and span of previous element but won't inherit row span.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// b.Add(\"File\", out TextBox _).And(70).AddButton(\"Browse...\", null);\n\t/// ]]></code>\n\t/// </example>\n\tpublic wpfBuilder And(double width) {\n\t\tvar g = _p as _Grid ?? throw new InvalidOperationException(\"And() in non-grid panel\");\n\t\tg.And(width);\n\t\treturn this;\n\t}\n\t\n\t#endregion\n\t\n\t#region set common properties of last added element\n\t\n\t/// <summary>\n\t/// Sets column span of the last added element.\n\t/// </summary>\n\t/// <param name=\"columns\">Column count. If -1 or too many, will span all remaining columns in current row. If 0, will share 1 column with next element added in current row; to set element positions use <see cref=\"Margin\"/>, <see cref=\"Width\"/> and <see cref=\"Align\"/>; see also <see cref=\"And\"/>.</param>\n\t/// <exception cref=\"InvalidOperationException\">In non-grid panel.</exception>\n\tpublic wpfBuilder Span(int columns) {\n\t\t_ParentOfLastAsOrThrow<_Grid>().Span(columns);\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Sets row span of the last added element.\n\t/// </summary>\n\t/// <param name=\"rows\">Row count.</param>\n\t/// <exception cref=\"InvalidOperationException\">In non-grid panel.</exception>\n\t/// <remarks>\n\t/// In next row(s) use <see cref=\"Skip\"/> to skip cells occupied by this element.\n\t/// Often it's better to add a nested panel instead. See <see cref=\"StartGrid\"/>.\n\t/// </remarks>\n\tpublic wpfBuilder SpanRows(int rows) {\n\t\tvar c = _ParentOfLastAsOrThrow<_Grid>().LastDirect;\n\t\tGrid.SetRowSpan(c, rows);\n\t\treturn this;\n\t}\n\t\n\t//rejected\n\t///// <summary>\n\t///// Calls your callback function.\n\t///// </summary>\n\t///// <param name=\"action\"></param>\n\t//public wpfBuilder Also(Action<wpfBuilder> action) {\n\t//\taction(this);\n\t//\treturn this;\n\t//}\n\t\n\t/// <summary>\n\t/// Sets callback function to be called by <c>AddX</c> functions for each element added afterwards. Not called by <c>StartX</c> functions for panels.\n\t/// </summary>\n\t/// <param name=\"action\">Callback function or <c>null</c>.</param>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// b.AlsoAll((b, e) => {\n\t/// \tif(b.Last is CheckBox c) { c.IsChecked = true; b.Margin(\"t1 b1\"); }\n\t/// });\n\t/// ]]></code>\n\t/// </example>\n\tpublic wpfBuilder AlsoAll(Action<wpfBuilder, WBAlsoAllArgs> action) {\n\t\t_alsoAll = action;\n\t\treturn this;\n\t}\n\tAction<wpfBuilder, WBAlsoAllArgs> _alsoAll;\n\tWBAlsoAllArgs _alsoAllArgs;\n\t\n\t/// <summary>\n\t/// Sets width and height of the last added element. Optionally sets alignment.\n\t/// </summary>\n\t/// <param name=\"width\">Width or/and min/max width.</param>\n\t/// <param name=\"height\">Height or/and min/max height.</param>\n\t/// <param name=\"alignX\">Horizontal alignment. If not <c>null</c>, calls <see cref=\"Align(string, string)\"/>.</param>\n\t/// <param name=\"alignY\">Vertical alignment.</param>\n\t/// <exception cref=\"ArgumentException\">Invalid alignment string.</exception>\n\tpublic wpfBuilder Size(WBLength width, WBLength height, string alignX = null, string alignY = null) {\n\t\tvar c = Last;\n\t\twidth.ApplyTo(c, false);\n\t\theight.ApplyTo(c, true);\n\t\tif (alignX != null || alignY != null) Align(alignX, alignY);\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Sets width of the last added element. Optionally sets alignment.\n\t/// </summary>\n\t/// <param name=\"width\">Width or/and min/max width.</param>\n\t/// <param name=\"alignX\">Horizontal alignment. If not <c>null</c>, calls <see cref=\"Align(string, string)\"/>.</param>\n\t/// <exception cref=\"ArgumentException\">Invalid alignment string.</exception>\n\tpublic wpfBuilder Width(WBLength width, string alignX = null) {\n\t\twidth.ApplyTo(Last, false);\n\t\tif (alignX != null) Align(alignX);\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Sets height of the last added element. Optionally sets alignment.\n\t/// </summary>\n\t/// <param name=\"height\">Height or/and min/max height.</param>\n\t/// <param name=\"alignY\">Vertical alignment. If not <c>null</c>, calls <see cref=\"Align(string, string)\"/>.</param>\n\t/// <exception cref=\"ArgumentException\">Invalid alignment string.</exception>\n\tpublic wpfBuilder Height(WBLength height, string alignY = null) {\n\t\theight.ApplyTo(Last, true);\n\t\tif (alignY != null) Align(null, alignY);\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Sets position of the last added element in <see cref=\"Canvas\"/> panel. Optionally sets size.\n\t/// </summary>\n\t/// <param name=\"x\"></param>\n\t/// <param name=\"y\"></param>\n\t/// <param name=\"width\">Width or/and min/max width.</param>\n\t/// <param name=\"height\">Height or/and min/max height.</param>\n\t/// <exception cref=\"InvalidOperationException\">Current panel is not <see cref=\"Canvas\"/>.</exception>\n\t/// <remarks>\n\t/// Only in <see cref=\"Canvas\"/> panel you can set position explicitly. In other panel types it is set automatically and can be adjusted with <see cref=\"Margin\"/>, <see cref=\"Align\"/>, container's <see cref=\"AlignContent\"/>, etc.\n\t/// </remarks>\n\tpublic wpfBuilder XY(double x, double y, WBLength? width = null, WBLength? height = null) {\n\t\tvar c = _ParentOfLastAsOrThrow<_Canvas>().LastDirect;\n\t\tCanvas.SetLeft(c, x);\n\t\tCanvas.SetTop(c, y);\n\t\twidth?.ApplyTo(c, false);\n\t\theight?.ApplyTo(c, true);\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Docks the last added element in <see cref=\"DockPanel\"/>.\n\t/// </summary>\n\t/// <param name=\"dock\"></param>\n\t/// <exception cref=\"InvalidOperationException\">Current panel is not <see cref=\"DockPanel\"/>.</exception>\n\tpublic wpfBuilder Dock(Dock dock) {\n\t\tvar c = _ParentOfLastAsOrThrow<_DockPanel>().LastDirect;\n\t\tDockPanel.SetDock(c, dock);\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Sets horizontal and/or vertical alignment of the last added element.\n\t/// </summary>\n\t/// <param name=\"x\">Horizontal alignment.</param>\n\t/// <param name=\"y\">Vertical alignment.</param>\n\t/// <exception cref=\"InvalidOperationException\">Current panel is <see cref=\"Canvas\"/>.</exception>\n\tpublic wpfBuilder Align(HorizontalAlignment? x = null, VerticalAlignment? y = null) {\n\t\tvar c = Last;\n\t\tif (c.Parent is Canvas) throw new InvalidOperationException(\"Align() in Canvas panel.\");\n\t\tif (x != null) c.HorizontalAlignment = x.Value;\n\t\tif (y != null) c.VerticalAlignment = y.Value;\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Sets horizontal and/or vertical alignment of the last added element.\n\t/// </summary>\n\t/// <param name=\"x\">Horizontal alignment. String that starts with one of these letters, uppercase or lowercase: <c>L</c> (left), <c>R</c> (right), <c>C</c> (center), <c>S</c> (stretch).</param>\n\t/// <param name=\"y\">Vertical alignment. String that starts with one of these letters, uppercase or lowercase: <c>T</c> (top), <c>B</c> (bottom), <c>C</c> (center), <c>S</c> (stretch).</param>\n\t/// <exception cref=\"InvalidOperationException\">Current panel is <see cref=\"Canvas\"/>.</exception>\n\t/// <exception cref=\"ArgumentException\">Invalid alignment string.</exception>\n\tpublic wpfBuilder Align(string x = null, string y = null) => Align(_AlignmentFromStringX(x), _AlignmentFromStringY(y));\n\t\n\tHorizontalAlignment? _AlignmentFromStringX(string s, [CallerMemberName] string m_ = null)\n\t\t=> s.NE() ? default(HorizontalAlignment?) : (char.ToUpperInvariant(s[0]) switch { 'L' => HorizontalAlignment.Left, 'C' => HorizontalAlignment.Center, 'R' => HorizontalAlignment.Right, 'S' => HorizontalAlignment.Stretch, _ => throw new ArgumentException(m_ + \"(x)\") });\n\t\n\tVerticalAlignment? _AlignmentFromStringY(string s, [CallerMemberName] string m_ = null)\n\t\t=> s.NE() ? default(VerticalAlignment?) : (char.ToUpperInvariant(s[0]) switch { 'T' => VerticalAlignment.Top, 'C' => VerticalAlignment.Center, 'B' => VerticalAlignment.Bottom, 'S' => VerticalAlignment.Stretch, _ => throw new ArgumentException(m_ + \"(y)\") });\n\t\n\t/// <summary>\n\t/// Sets content alignment of the last added element.\n\t/// </summary>\n\t/// <param name=\"x\">Horizontal alignment.</param>\n\t/// <param name=\"y\">Vertical alignment.</param>\n\t/// <exception cref=\"InvalidOperationException\">The last added element is not <see cref=\"Control\"/>.</exception>\n\tpublic wpfBuilder AlignContent(HorizontalAlignment? x = null, VerticalAlignment? y = null) {\n\t\tvar c = _LastAsControlOrThrow();\n\t\tif (x != null) c.HorizontalContentAlignment = x.Value;\n\t\tif (y != null) c.VerticalContentAlignment = y.Value;\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Sets content alignment of the last added element.\n\t/// </summary>\n\t/// <param name=\"x\">Horizontal alignment. String like with <see cref=\"Align(string, string)\"/>.</param>\n\t/// <param name=\"y\">Vertical alignment.</param>\n\t/// <exception cref=\"InvalidOperationException\">The last added element is not <see cref=\"Control\"/>.</exception>\n\t/// <exception cref=\"ArgumentException\">Invalid alignment string.</exception>\n\tpublic wpfBuilder AlignContent(string x = null, string y = null) => AlignContent(_AlignmentFromStringX(x), _AlignmentFromStringY(y));\n\t\n\t/// <summary>\n\t/// Sets margin of the last added element.\n\t/// </summary>\n\tpublic wpfBuilder Margin(Thickness margin) {\n\t\tLast.Margin = margin;\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Sets margin of the last added element.\n\t/// </summary>\n\tpublic wpfBuilder Margin(double? left = null, double? top = null, double? right = null, double? bottom = null) {\n\t\tvar c = Last;\n\t\tvar p = c.Margin;\n\t\tleft ??= p.Left;\n\t\ttop ??= p.Top;\n\t\tright ??= p.Right;\n\t\tbottom ??= p.Bottom;\n\t\tc.Margin = new(left.Value, top.Value, right.Value, bottom.Value);\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Sets margin of the last added element.\n\t/// </summary>\n\t/// <param name=\"margin\">\n\t/// String containing uppercase or lowercase letters for margin sides (<c>L</c>, <c>T</c>, <c>R</c>, <c>B</c>) optionally followed by a number (default 0) and optionally separated by spaces. Or just single number, to set all sides equal.\n\t/// Examples: <c>\"tb\"</c> (top 0, bottom 0), <c>\"L5 R15\"</c> (left 5, right 15), <c>\"2\"</c> (all sides 2).\n\t/// </param>\n\t/// <exception cref=\"ArgumentException\">Invalid string.</exception>\n\tpublic wpfBuilder Margin(string margin) {\n\t\tvar c = Last;\n\t\tvar m = c.Margin;\n\t\t_ThicknessFromString(ref m, margin);\n\t\tc.Margin = m;\n\t\treturn this;\n\t}\n\t\n\tstatic void _ThicknessFromString(ref Thickness t, string s, [CallerMemberName] string m_ = null) {\n\t\tif (s.NE()) return;\n\t\tif (s.ToInt(out int v1, 0, out int e1) && e1 == s.Length) {\n\t\t\tt = new(v1);\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tfor (int i = 0; i < s.Length; i++) {\n\t\t\tvar c = s[i]; if (c == ' ') continue;\n\t\t\tint v = s.ToInt(i + 1, out int end); if (end > 0) i = end - 1; //never mind: should be double. Currently we don't have a function that can recognize and convert part of string to double.\n\t\t\tswitch (c) {\n\t\t\tcase 't': case 'T': t.Top = v; break;\n\t\t\tcase 'b': case 'B': t.Bottom = v; break;\n\t\t\tcase 'l': case 'L': t.Left = v; break;\n\t\t\tcase 'r': case 'R': t.Right = v; break;\n\t\t\tdefault: throw new ArgumentException(m_ + \"()\");\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Sets padding of the last added control.\n\t/// </summary>\n\t/// <exception cref=\"InvalidOperationException\">The last added element does not have <c>Padding</c> property.</exception>\n\tpublic wpfBuilder Padding(Thickness thickness) {\n\t\tnew _PropGetSet<Thickness>(Last, \"Padding\").Set(thickness);\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Sets padding of the last added control.\n\t/// </summary>\n\t/// <exception cref=\"InvalidOperationException\">The last added element does not have <c>Padding</c> property.</exception>\n\tpublic wpfBuilder Padding(double? left = null, double? top = null, double? right = null, double? bottom = null) {\n\t\tvar c = new _PropGetSet<Thickness>(Last, \"Padding\");\n\t\tvar p = c.Get;\n\t\tleft ??= p.Left;\n\t\ttop ??= p.Top;\n\t\tright ??= p.Right;\n\t\tbottom ??= p.Bottom;\n\t\tc.Set(new(left.Value, top.Value, right.Value, bottom.Value));\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Sets padding of the last added control.\n\t/// </summary>\n\t/// <param name=\"padding\">\n\t/// String containing uppercase or lowercase letters for padding sides (<c>L</c>, <c>T</c>, <c>R</c>, <c>B</c>) optionally followed by a number (default 0) and optionally separated by spaces. Or just single number, to set all sides equal.\n\t/// Examples: <c>\"tb\"</c> (top 0, bottom 0), <c>\"L5 R15\"</c> (left 5, right 15), <c>\"2\"</c> (all sides 2).\n\t/// </param>\n\t/// <exception cref=\"InvalidOperationException\">The last added element does not have <c>Padding</c> property.</exception>\n\t/// <exception cref=\"ArgumentException\">Invalid string.</exception>\n\tpublic wpfBuilder Padding(string padding) {\n\t\tvar c = new _PropGetSet<Thickness>(Last, \"Padding\");\n\t\tvar p = c.Get;\n\t\t_ThicknessFromString(ref p, padding);\n\t\tc.Set(p);\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Sets <see cref=\"UIElement.IsEnabled\"/> of the last added element.\n\t/// </summary>\n\t/// <param name=\"disabled\">If <c>true</c> (default), sets <c>IsEnabled</c> = <c>false</c>, else sets <c>IsEnabled</c> = true.</param>\n\tpublic wpfBuilder Disabled(bool disabled = true) {\n\t\tLast.IsEnabled = !disabled;\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Sets <see cref=\"UIElement.Visibility\"/> of the last added element.\n\t/// </summary>\n\t/// <param name=\"hidden\">If <c>true</c> (default), sets <see cref=\"Visibility\"/> <c>Hiden</c>; if <c>false</c> - <c>Visible</c>; if <c>null</c> - <c>Collapsed</c>.</param>\n\tpublic wpfBuilder Hidden(bool? hidden = true) {\n\t\tLast.Visibility = hidden switch { true => Visibility.Hidden, false => Visibility.Visible, _ => Visibility.Collapsed };\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Sets tooltip text/content/object of the last added element. See <see cref=\"FrameworkElement.ToolTip\"/>.\n\t/// </summary>\n\t/// <param name=\"tooltip\">Tooltip text (string), or tooltip content element, or <see cref=\"ToolTip\"/> object.</param>\n\t/// <example>\n\t/// Text box with simple tooltip.\n\t/// <code><![CDATA[\n\t/// b.R.Add(\"Example\", out TextBox _).Tooltip(\"Tooltip text\");\n\t/// ]]></code>\n\t/// Tooltip with content created by another <see cref=\"wpfBuilder\"/>.\n\t/// <code><![CDATA[\n\t/// //tooltip content\n\t/// var btt = new wpfBuilder()\n\t/// \t.R.Add<Image>().Image(icon.stock(StockIcon.INFO).ToWpfImage())\n\t/// \t.R.Add<TextBlock>().Text(\"Some \", \"<b>text\", \".\")\n\t/// \t.End();\n\t/// //dialog\n\t/// var b = new wpfBuilder(\"Window\").WinSize(300);\n\t/// b.R.AddButton(\"Example\", null).Tooltip(btt.Panel);\n\t/// b.R.AddOkCancel();\n\t/// b.End();\n\t/// if (!b.ShowDialog()) return;\n\t/// ]]></code>\n\t/// </example>\n\tpublic wpfBuilder Tooltip(object tooltip) {\n\t\tLast.ToolTip = tooltip;\n\t\tif (!_opt_showToolTipOnKeyboardFocus) ToolTipService.SetShowsToolTipOnKeyboardFocus(Last, false);\n\t\treturn this;\n\t}\n\t//FUTURE: make easier to create tooltip content, eg Inlines of TextBlock. Would be good to create on demand.\n\t//FUTURE: hyperlinks in tooltip. Now does not work because tooltip closes when mouse leaves the element.\n\t\n\t/// <summary>\n\t/// Sets UI Automation name of the last added element.\n\t/// </summary>\n\tpublic wpfBuilder UiaName(string name) {\n\t\tLast.UiaSetName(name);\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Sets background and/or foreground brush (color, gradient, etc) of the last added element.\n\t/// </summary>\n\t/// <param name=\"background\">Background brush. See <see cref=\"Brushes\"/>, <see cref=\"SystemColors\"/>. Descendants usually inherit this property.</param>\n\t/// <param name=\"foreground\">Foreground brush. Usually sets text color. Descendants usually override this property.</param>\n\t/// <exception cref=\"NotSupportedException\">Last added element must be <see cref=\"Control\"/>, <see cref=\"Panel\"/>, <see cref=\"Border\"/> or <see cref=\"TextBlock\"/>. With <i>foreground</i> only <see cref=\"Control\"/> or <see cref=\"TextBlock\"/>.</exception>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// b.R.Add<Label>(\"Example1\").Brush(Brushes.Cornsilk, Brushes.Green).Border(Brushes.BlueViolet, 1);\n\t/// b.R.Add<Label>(\"Example2\").Brush(new LinearGradientBrush(Colors.Chocolate, Colors.White, 0));\n\t/// ]]></code>\n\t/// </example>\n\tpublic wpfBuilder Brush(Brush background = null, Brush foreground = null) { //named not Colors because: 1. Can set other brush than color, eg gradient. 2. Rarely used and in autocompletion lists is above Columns.\n\t\tvar last = Last;\n\t\tif (foreground != null) {\n\t\t\tnew _PropGetSet<Brush>(Last, \"Foreground\").Set(foreground);\n\t\t}\n\t\tif (background != null) {\n\t\t\tif (last == _p.panel && !_IsNested && _window != null) last = _window;\n\t\t\tnew _PropGetSet<Brush>(Last, \"Background\").Set(background);\n\t\t}\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Sets background and/or foreground color of the last added element.\n\t/// </summary>\n\t/// <exception cref=\"NotSupportedException\">Last added element must be <see cref=\"Control\"/>, <see cref=\"Panel\"/>, <see cref=\"Border\"/> or <see cref=\"TextBlock\"/>. With <i>foreground</i> only <see cref=\"Control\"/> or <see cref=\"TextBlock\"/>.</exception>\n\tpublic wpfBuilder Brush(ColorInt? background = null, ColorInt? foreground = null)\n\t\t=> Brush(background == null ? null : new SolidColorBrush((Color)background.Value), foreground == null ? null : new SolidColorBrush((Color)foreground.Value));\n\t\n\t/// <summary>\n\t/// Sets border properties of the last added element, which can be <see cref=\"Border\"/> or a <see cref=\"Control\"/>-derived class.\n\t/// </summary>\n\t/// <param name=\"color\">Border color brush. If <c>null</c>, uses <see cref=\"SystemColors.ActiveBorderBrush\"/>.</param>\n\t/// <param name=\"thickness\">Border thickness. Ignored if <i>thickness2</i> not <c>null</c>.</param>\n\t/// <param name=\"padding\">Sets the <c>Padding</c> property.</param>\n\t/// <param name=\"cornerRadius\">Sets <see cref=\"Border.CornerRadius\"/>. If used, the last added element must be <see cref=\"C.Border\"/>.</param>\n\t/// <param name=\"thickness2\">Border thickness to use instead of <i>thickness</i>. Allows to set non-uniform thickness.</param>\n\t/// <exception cref=\"NotSupportedException\">Last added element must be <see cref=\"Control\"/> or <see cref=\"C.Border\"/>. With <i>cornerRadius</i> only <see cref=\"C.Border\"/>.</exception>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// b.R.Add<Label>(\"Example1\").Border(Brushes.BlueViolet, 1, new(5)).Brush(Brushes.Cornsilk, Brushes.Green);\n\t/// b.R.Add<Border>().Border(Brushes.Blue, 2, cornerRadius: 3).Child().Add<Label>(\"Example2\");\n\t/// ]]></code>\n\t/// </example>\n\tpublic wpfBuilder Border(Brush color = null, double thickness = 1d, Thickness? padding = null, double? cornerRadius = null, Thickness? thickness2 = null) {\n\t\tcolor ??= SystemColors.ActiveBorderBrush;\n\t\tswitch (Last) {\n\t\tcase Control c:\n\t\t\tif (cornerRadius != null) throw new NotSupportedException(\"Border(): Last added must be Border, or cornerRadius null\");\n\t\t\tc.BorderBrush = color;\n\t\t\tc.BorderThickness = thickness2 ?? new(thickness);\n\t\t\tif (padding != null) c.Padding = padding.Value;\n\t\t\tbreak;\n\t\tcase Border c:\n\t\t\tc.BorderBrush = color;\n\t\t\tc.BorderThickness = thickness2 ?? new(thickness);\n\t\t\tif (padding != null) c.Padding = padding.Value;\n\t\t\tif (cornerRadius != null) c.CornerRadius = new CornerRadius(cornerRadius.Value);\n\t\t\tbreak;\n\t\tdefault: throw new NotSupportedException(\"Border(): Last added must be Control or Border\");\n\t\t}\n\t\treturn this;\n\t\t//tested: there are no other useful types that have these properties.\n\t}\n\t\n\t/// <param name=\"color\">Border color.</param>\n\t/// <inheritdoc cref=\"Border(Brush, double, Thickness?, double?, Thickness?)\"/>\n\tpublic wpfBuilder Border(ColorInt color, double thickness = 1d, Thickness? padding = null, double? cornerRadius = null, Thickness? thickness2 = null)\n\t\t=> Border(new SolidColorBrush((Color)color), thickness, padding, cornerRadius, thickness2);\n\t\n\t/// <summary>\n\t/// Sets font properties of the last added element and its descendants.\n\t/// </summary>\n\t/// <param name=\"name\">If not <c>null</c>, sets font name. Can be multiple fonts separated by commas.</param>\n\t/// <param name=\"size\">If not <c>null</c>, sets font size.</param>\n\t/// <param name=\"bold\">If not <c>null</c>, sets font bold or not.</param>\n\t/// <param name=\"italic\">If not <c>null</c>, sets font italic or not.</param>\n\tpublic wpfBuilder Font(string name = null, double? size = null, bool? bold = null, bool? italic = null) {\n\t\tvar c = Last;\n\t\tif (name != null) TextElement.SetFontFamily(c, new FontFamily(name));\n\t\tif (size != null) TextElement.SetFontSize(c, size.Value);\n\t\tif (bold != null) TextElement.SetFontWeight(c, bold == true ? FontWeights.Bold : FontWeights.Normal);\n\t\tif (italic != null) TextElement.SetFontStyle(c, italic == true ? FontStyles.Italic : FontStyles.Normal);\n\t\treturn this;\n\t\t//rejected: FontStretch? stretch=null. Rarely used. Most fonts don't support.\n\t\t\n\t\t//not sure is this is OK or should set font properties for each supporting class separately.\n\t}\n\t\n\t/// <summary>\n\t/// Sets <c>TextWrapping</c> property of the last added element.\n\t/// Supports <see cref=\"TextBlock\"/>, <see cref=\"TextBox\"/> and <see cref=\"AccessText\"/>.\n\t/// </summary>\n\tpublic wpfBuilder Wrap(TextWrapping wrapping) {\n\t\tswitch (Last) {\n\t\tcase TextBlock t: t.TextWrapping = wrapping; break;\n\t\tcase TextBox t: t.TextWrapping = wrapping; break;\n\t\tcase AccessText t: t.TextWrapping = wrapping; break;\n\t\tdefault: throw new NotSupportedException(\"Wrap(): Last added must be TextBlock, TextBox or AccessText\");\n\t\t}\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Sets <c>TextWrapping</c> property of the last added element = <see cref=\"TextWrapping.Wrap\"/> if <c>true</c> (default), else or <see cref=\"TextWrapping.NoWrap\"/>.\n\t/// Supports <see cref=\"TextBlock\"/>, <see cref=\"TextBox\"/> and <see cref=\"AccessText\"/>.\n\t/// </summary>\n\tpublic wpfBuilder Wrap(bool wrap = true) => Wrap(wrap ? TextWrapping.Wrap : TextWrapping.NoWrap);\n\t\n\t/// <summary>\n\t/// Attempts to set focus to the last added element when it'll become visible.\n\t/// </summary>\n\tpublic wpfBuilder Focus() {\n\t\tLast.Focus();\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Sets <c>Tag</c> property of the last added element.\n\t/// </summary>\n\tpublic wpfBuilder Tag(object tag) {\n\t\tLast.Tag = tag;\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Sets <see cref=\"FrameworkElement.DataContext\"/> property of the last added element.\n\t/// Then with <see cref=\"Bind\"/> of this and descendant elements don't need to specify data source object because it is set by this function.\n\t/// </summary>\n\t/// <param name=\"source\">Data source object.</param>\n\tpublic wpfBuilder BindingContext(object source) {\n\t\tLast.DataContext = source;\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Calls <see cref=\"FrameworkElement.SetBinding(DependencyProperty, string)\"/> of the last added element.\n\t/// </summary>\n\t/// <param name=\"property\">Element's dependency property, for example <c>TextBox.TextProperty</c>.</param>\n\t/// <param name=\"path\">Source property name or path, for example <c>nameof(MyData.Property)</c>. Source object should be set with <see cref=\"BindingContext\"/>.</param>\n\tpublic wpfBuilder Bind(DependencyProperty property, string path) {\n\t\tLast.SetBinding(property, path);\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Calls <see cref=\"FrameworkElement.SetBinding(DependencyProperty, BindingBase)\"/> of the last added element.\n\t/// </summary>\n\t/// <param name=\"property\">Element's dependency property, for example <c>TextBox.TextProperty</c>.</param>\n\t/// <param name=\"binding\">A binding object, for example <c>new Binding(nameof(MyData.Property))</c> or <c>new Binding(nameof(MyData.Property)) { Source = dataObject }</c>. In the first case, source object should be set with <see cref=\"BindingContext\"/>.</param>\n\tpublic wpfBuilder Bind(DependencyProperty property, BindingBase binding) {\n\t\tLast.SetBinding(property, binding);\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Calls <see cref=\"FrameworkElement.SetBinding(DependencyProperty, BindingBase)\"/> of the last added element and gets its return value.\n\t/// </summary>\n\t/// <param name=\"property\">Element's dependency property, for example <c>TextBox.TextProperty</c>.</param>\n\t/// <param name=\"binding\">A binding object.</param>\n\t/// <param name=\"r\">The return value of <c>SetBinding</c>.</param>\n\tpublic wpfBuilder Bind(DependencyProperty property, BindingBase binding, out BindingExpressionBase r) {\n\t\tr = Last.SetBinding(property, binding);\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Calls <see cref=\"FrameworkElement.SetBinding(DependencyProperty, BindingBase)\"/> of the last added element. Creates <see cref=\"Binding\"/> that uses <i>source</i> and <i>path</i>.\n\t/// </summary>\n\t/// <param name=\"property\">Element's dependency property, for example <c>TextBox.TextProperty</c>.</param>\n\t/// <param name=\"source\">Data source object.</param>\n\t/// <param name=\"path\">Source property name or path, for example <c>nameof(MyData.Property)</c>.</param>\n\tpublic wpfBuilder Bind(DependencyProperty property, object source, string path) {\n\t\tvar binding = new Binding(path) { Source = source };\n\t\tLast.SetBinding(property, binding);\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Sets a validation callback function for the last added element.\n\t/// </summary>\n\t/// <param name=\"func\">Function that returns an error string if element's value is invalid, else returns <c>null</c>.</param>\n\t/// <param name=\"linkClick\">If not null, called on error link click. For example can be used to make the element visible.</param>\n\t/// <remarks>\n\t/// The callback function will be called when clicked button <b>OK</b> or <b>Apply</b> or a button added with flag <see cref=\"WBBFlags.Validate\"/>.\n\t/// If it returns a non-<c>null</c> string, the window stays open and button's <i>click</i> callback not called. The string is displayed in a tooltip.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// var b = new wpfBuilder(\"Window\").WinSize(300);\n\t/// b.R.Add(\"Name\", out TextBox tName)\n\t/// \t.Validation(o => string.IsNullOrWhiteSpace(tName.Text) ? \"Name cannot be empty\" : null);\n\t/// b.R.Add(\"Count\", out TextBox tCount)\n\t/// \t.Validation(o => int.TryParse(tCount.Text, out int i1) && i1 >= 0 && i1 <= 100 ? null : \"Count must be 0-100\");\n\t/// b.R.AddOkCancel();\n\t/// b.End();\n\t/// if (!b.ShowDialog()) return;\n\t/// print.it(tName.Text, tCount.Text.ToInt());\n\t/// ]]></code>\n\t/// </example>\n\tpublic wpfBuilder Validation(Func<FrameworkElement, string> func, Action<FrameworkElement> linkClick = null/*, DependencyProperty property=null*/)\n\t\t=> Validation(Last, func, linkClick);\n\t\n\t/// <summary>\n\t/// Sets a validation callback function for an element.\n\t/// </summary>\n\t/// <inheritdoc cref=\"Validation(Func{FrameworkElement, string}, Action{FrameworkElement})\"/>\n\t/// <example/>\n\tpublic wpfBuilder Validation(FrameworkElement e, Func<FrameworkElement, string> func, Action<FrameworkElement> linkClick = null/*, DependencyProperty property=null*/) {\n\t\t//validate on click of OK or some other button. Often eg text fields initially are empty and must be filled.\n\t\t(_validations ??= new List<_Validation>()).Add(new(e, func, linkClick));\n\t\treturn this;\n\t\t//rejected: also validate on lost focus or changed property value.\n\t}\n\t\n\trecord class _Validation(FrameworkElement e, Func<FrameworkElement, string> func, Action<FrameworkElement> linkClick);\n\t\n\tList<_Validation> _validations;\n\t\n\tbool _Validate(Window w, Button b) {\n\t\tToolTip tt = null;\n\t\tTextBlock tb = null;\n\t\tforeach (var gb in _GetAllWpfBuilders(w)) { //find all wpfBuilder used to build this window\n\t\t\tif (gb._validations == null) continue;\n\t\t\tforeach (var (e, func, linkClick) in gb._validations) {\n\t\t\t\tvar s = func(e); if (s == null) continue;\n\t\t\t\tif (tb == null) tb = new TextBlock(); else tb.Inlines.Add(new LineBreak());\n\t\t\t\tvar h = new Hyperlink(new Run(s));\n\t\t\t\th.Click += (o, y) => {\n\t\t\t\t\tif (!e.IsVisible && _FindAncestorTabItem(e, out var ti)) ti.IsSelected = true;\n\t\t\t\t\tlinkClick?.Invoke(e);\n\t\t\t\t\tif (e.IsVisible) {\n\t\t\t\t\t\ttimer.after(1, _ => { //else does not focus etc if was in hidden tab page\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\te.BringIntoView();\n\t\t\t\t\t\t\t\te.Focus();\n\t\t\t\t\t\t\t\t_RedBorderAdorner.AddTo(e, true);\n\t\t\t\t\t\t\t\ttt.Closed += (_, _) => { _RedBorderAdorner.AddTo(e, false); };\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcatch { }\n\t\t\t\t\t\t\t//catch(Exception e1) { print.it(e1); }\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t\ttb.Inlines.Add(h);\n\t\t\t}\n\t\t}\n\t\tif (tb == null) return true;\n\t\ttt = new ToolTip { Content = tb, StaysOpen = false, PlacementTarget = b, Placement = PlacementMode.Bottom };\n\t\t//var tt=new Popup { Child=tb, StaysOpen=false, PlacementTarget=b, Placement= PlacementMode.Bottom }; //works, but black etc\n\t\ttt.IsOpen = true;\n\t\t//never mind: could add eg red rectangle, like WPF does on binding validation error. Not easy.\n\t\treturn false;\n\t}\n\t\n\tstatic List<wpfBuilder> _GetAllWpfBuilders(DependencyObject root) {\n\t\tvar a = new List<wpfBuilder>();\n\t\t_Enum(root, 0);\n\t\tvoid _Enum(DependencyObject parent, int level) {\n\t\t\tforeach (var o in LogicalTreeHelper.GetChildren(parent).OfType<DependencyObject>()) {\n\t\t\t\t//print.it(new string(' ', level) + o);\n\t\t\t\tif (o is Panel p && s_cwt.TryGetValue(p, out var gb)) a.Add(gb);\n\t\t\t\t_Enum(o, level + 1);\n\t\t\t}\n\t\t}\n\t\treturn a;\n\t}\n\t\n\tstatic bool _FindAncestorTabItem(DependencyObject e, out TabItem ti) {\n\t\tti = null;\n\t\tfor (; ; )\n\t\t{\n\t\t\tswitch (e = LogicalTreeHelper.GetParent(e)) {\n\t\t\tcase null: return false;\n\t\t\tcase TabItem t: ti = t; return true;\n\t\t\t}\n\t\t}\n\t}\n\t\n\tclass _RedBorderAdorner : Adorner {\n\t\tpublic _RedBorderAdorner(UIElement adornedElement) : base(adornedElement) { }\n\t\t\n\t\tprotected override void OnRender(DrawingContext drawingContext) {\n\t\t\tvar adornedElementRect = new Rect(AdornedElement.RenderSize);\n\t\t\tvar pen = new Pen(Brushes.Red, 2);\n\t\t\tdrawingContext.DrawRectangle(null, pen, adornedElementRect);\n\t\t}\n\t\t\n\t\tpublic static void AddTo(FrameworkElement e, bool add) {\n\t\t\tif (AdornerLayer.GetAdornerLayer(e) is { } layer) {\n\t\t\t\tif (add) {\n\t\t\t\t\tlayer.Add(new _RedBorderAdorner(e));\n\t\t\t\t} else {\n\t\t\t\t\tif (layer.GetAdorners(e)?.OfType<_RedBorderAdorner>().FirstOrDefault() is { } k) layer.Remove(k);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Sets <see cref=\"FrameworkElement.Name\"/> of the last added element.\n\t/// </summary>\n\t/// <param name=\"name\">Name. Must start with a letter or <c>_</c>, and contain only letters, digits and <c>_</c>.</param>\n\t/// <param name=\"andUia\">Also set UI Automation name (<see cref=\"UiaName\"/>).</param>\n\t/// <exception cref=\"ArgumentException\">Invalid name.</exception>\n\t/// <remarks>\n\t/// The <c>Name</c> property can be used to identify the element in code. It also sets the UIA <c>AutomationId</c> (regardless of <i>andUia</i>). It isn't displayed in UI.\n\t/// </remarks>\n\tpublic wpfBuilder Name(string name, bool andUia = false) {\n\t\tLast.Name = name;\n\t\tif (andUia) UiaName(name);\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Makes an element behave as a label of the last added element (<see cref=\"Last\"/>).\n\t/// </summary>\n\t/// <param name=\"label\">The label element. Usually <see cref=\"Label\"/> or <see cref=\"TextBlock\"/>, but can be any element.</param>\n\t/// <param name=\"bindVisibility\">If <c>true</c>, binds <c>Visibility</c> of <i>label</i> to that of the labeled element. If <c>false</c>, does not bind. If <c>null</c> (default), binds if the <i>bindLabelVisibility</i> option is true (see <see cref=\"Options\"/>). If binds <c>Visibility</c>, also binds <c>IsEnabled</c> if <i>label</i> is <see cref=\"Label\"/> or <see cref=\"TextBlock\"/>.</param>\n\t/// <remarks>\n\t/// Sets <i>label</i>'s <see cref=\"Label.Target\"/> if it's <see cref=\"Label\"/>. Calls <see cref=\"System.Windows.Automation.AutomationProperties.SetLabeledBy\"/>.\n\t/// </remarks>\n\tpublic wpfBuilder LabeledBy(FrameworkElement label, bool? bindVisibility = null) {\n\t\tvar e = Last;\n\t\tSystem.Windows.Automation.AutomationProperties.SetLabeledBy(e, label);\n\t\tif (label is Label la) la.Target = e;\n\t\tif (bindVisibility ?? _opt_bindLabelVisibility) {\n\t\t\tlabel?.SetBinding(UIElement.VisibilityProperty, new Binding(\"Visibility\") { Source = e, Mode = BindingMode.OneWay });\n\t\t\tif (label is Label or TextBlock) label.SetBinding(UIElement.IsEnabledProperty, new Binding(\"IsEnabled\") { Source = e, Mode = BindingMode.OneWay });\n\t\t}\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Makes <see cref=\"Last2\"/> behave as a label of <see cref=\"Last\"/>.\n\t/// </summary>\n\t/// <inheritdoc cref=\"LabeledBy(FrameworkElement, bool?)\"/>\n\tpublic wpfBuilder LabeledBy(bool? bindVisibility = null) => LabeledBy(Last2, bindVisibility);\n\t\n\t/// <summary>\n\t/// Sets watermark/hint/cue text of the last added <see cref=\"TextBox\"/>, <see cref=\"PasswordBox\"/> or editable <see cref=\"ComboBox\"/> control.\n\t/// The text is visible only when the control text is empty.\n\t/// </summary>\n\t/// <param name=\"text\">Watermark text.</param>\n\t/// <remarks>\n\t/// In some kinds of windows it may not work unless a parent or ancestor of the control is an <see cref=\"AdornerDecorator\"/> (like in the example).\n\t/// </remarks>\n\t/// <exception cref=\"NotSupportedException\">The last added element isn't <see cref=\"TextBox\"/>, <see cref=\"PasswordBox\"/> or editable <see cref=\"ComboBox\"/> control.</exception>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// b.R.Add<AdornerDecorator>().Child().Add(out TextBox text1).Watermark(\"Water\");\n\t/// ]]></code>\n\t/// More examples in Cookbook.\n\t/// </example>\n\tpublic wpfBuilder Watermark(string text) => Watermark(out _, text);\n\t\n\t/// <param name=\"adorner\">Receives the adorner. It can be used to change watermark text later.</param>\n\t/// <inheritdoc cref=\"Watermark(string)\"/>\n\tpublic wpfBuilder Watermark(out WatermarkAdorner adorner, string text) {\n\t\tvar c = Last as Control;\n\t\tif (!(c is TextBox or PasswordBox or ComboBox { IsEditable: true })) throw new NotSupportedException(\"Watermark(): Last added must be TextBox, PasswordBox or editable ComboBox\");\n\t\tadorner = new WatermarkAdorner(c, text);\n\t\tadorner.SetAdornerVisibility();\n\t\treturn this;\n\t}\n\t\n\t#endregion\n\t\n\t#region set type-specific properties of last added element\n\t\n\t/// <summary>\n\t/// Sets <see cref=\"ToggleButton.IsChecked\"/> and <see cref=\"ToggleButton.IsThreeState\"/> of the last added check box or radio button.\n\t/// </summary>\n\t/// <param name=\"check\"></param>\n\t/// <param name=\"threeState\"></param>\n\t/// <exception cref=\"NotSupportedException\">The last added element is not <see cref=\"ToggleButton\"/>.</exception>\n\tpublic wpfBuilder Checked(bool? check = true, bool threeState = false) {\n\t\tvar c = Last as ToggleButton ?? throw new NotSupportedException(\"Checked(): Last added element must be CheckBox or RadioButton\");\n\t\tc.IsThreeState = threeState;\n\t\tc.IsChecked = check;\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Sets <see cref=\"ToggleButton.IsChecked\"/> of the specified <see cref=\"RadioButton\"/>.\n\t/// </summary>\n\t/// <param name=\"check\"></param>\n\t/// <param name=\"control\"></param>\n\t/// <remarks>\n\t/// Unlike other similar functions, does not use <see cref=\"Last\"/>.\n\t/// </remarks>\n\tpublic wpfBuilder Checked(bool check, RadioButton control) {\n\t\tcontrol.IsChecked = check;\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Sets <see cref=\"TextBoxBase.IsReadOnly\"/> or <see cref=\"ComboBox.IsReadOnly\"/> of the last added text box or editable combo box.\n\t/// </summary>\n\t/// <param name=\"readOnly\"></param>\n\t/// <param name=\"caretVisible\">Sets <see cref=\"TextBoxBase.IsReadOnlyCaretVisible\"/>. Not used with <see cref=\"ComboBox\"/>.</param>\n\t/// <exception cref=\"NotSupportedException\">The last added element is not <see cref=\"TextBoxBase\"/> or <see cref=\"ComboBox\"/>.</exception>\n\tpublic wpfBuilder Readonly(bool readOnly = true, bool caretVisible = false) { //rejected: , bool caretVisible=false. Not useful.\n\t\tswitch (Last) {\n\t\tcase TextBoxBase c:\n\t\t\tc.IsReadOnly = readOnly;\n\t\t\tc.IsReadOnlyCaretVisible = caretVisible;\n\t\t\tbreak;\n\t\tcase ComboBox c:\n\t\t\tc.IsReadOnly = readOnly;\n\t\t\tbreak;\n\t\tdefault: throw new NotSupportedException(\"Readonly(): Last added must be TextBox, RichTextBox or ComboBox\");\n\t\t}\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Makes the last added <see cref=\"TextBox\"/> multiline.\n\t/// </summary>\n\t/// <param name=\"height\">If not <c>null</c>, sets height or/and min/max height.</param>\n\t/// <param name=\"wrap\">Sets <see cref=\"TextBox.TextWrapping\"/>.</param>\n\t/// <exception cref=\"NotSupportedException\">The last added element is not <see cref=\"TextBox\"/>.</exception>\n\tpublic wpfBuilder Multiline(WBLength? height = null, TextWrapping wrap = TextWrapping.WrapWithOverflow) {\n\t\tvar c = Last as TextBox ?? throw new NotSupportedException(\"Multiline(): Last added must be TextBox\");\n\t\tc.AcceptsReturn = true;\n\t\tc.TextWrapping = wrap;\n\t\tc.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;\n\t\tc.HorizontalScrollBarVisibility = ScrollBarVisibility.Auto;\n\t\theight?.ApplyTo(c, true);\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Makes the last added <see cref=\"ComboBox\"/> editable.\n\t/// </summary>\n\t/// <exception cref=\"NotSupportedException\">The last added element is not <see cref=\"ComboBox\"/>.</exception>\n\tpublic wpfBuilder Editable() {\n\t\tvar c = Last as ComboBox ?? throw new NotSupportedException(\"Editable(): Last added must be ComboBox\");\n\t\tc.IsEditable = true;\n\t\tif (_ModifyPadding == true) c.Padding = new(2, 1, 2, 2); //default (2) or set by _Add() for non-editable\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Splits string and ads substrings as items to the last added <see cref=\"ItemsControl\"/> (<see cref=\"ComboBox\"/>, etc).\n\t/// </summary>\n\t/// <param name=\"items\">String like <c>\"One|Two|Three\"</c>.</param>\n\t/// <exception cref=\"NotSupportedException\">The last added element is not <see cref=\"ItemsControl\"/>.</exception>\n\t/// <remarks>\n\t/// If it is a non-editable <see cref=\"ComboBox\"/>, selects the first item. See also <see cref=\"Select\"/>.\n\t/// </remarks>\n\tpublic wpfBuilder Items(string items) => _Items(items.Split('|'), null);\n\t\n\t/// <summary>\n\t/// Adds items of any type to the last added <see cref=\"ItemsControl\"/> (<see cref=\"ComboBox\"/>, etc).\n\t/// </summary>\n\t/// <param name=\"items\">Items of any type (<c>string</c>, WPF element).</param>\n\t/// <exception cref=\"NotSupportedException\">The last added element is not <see cref=\"ItemsControl\"/>.</exception>\n\t/// <remarks>\n\t/// If it is a non-editable <see cref=\"ComboBox\"/>, selects the first item. See also <see cref=\"Select\"/>.\n\t/// </remarks>\n\tpublic wpfBuilder Items(params object[] items) => _Items(items, null);\n\t\n\twpfBuilder _Items(object[] a, IEnumerable e) {\n\t\tvar ic = Last as ItemsControl ?? throw new NotSupportedException(\"Items(): Last added must be ItemsControl, for example ComboBox\");\n\t\tic.ItemsSource = null;\n\t\tif (a != null) {\n\t\t\tic.Items.Clear();\n\t\t\tforeach (var v in a) ic.Items.Add(v);\n\t\t} else if (e != null) {\n\t\t\tic.ItemsSource = e;\n\t\t}\n\t\tif (Last is ComboBox cb && !cb.IsEditable && cb.HasItems) cb.SelectedIndex = 0;\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Adds items as <see cref=\"IEnumerable\"/> to the last added <see cref=\"ItemsControl\"/> (<see cref=\"ComboBox\"/>, etc), with \"lazy\" option.\n\t/// </summary>\n\t/// <param name=\"items\">An <see cref=\"IEnumerable\"/> that contains items (eg array, <see cref=\"List{T}\"/>) or generates items (eg returned from a yield-return function).</param>\n\t/// <param name=\"lazy\">Retrieve items when (if) showing the dropdown part of the <see cref=\"ComboBox\"/> first time.</param>\n\t/// <exception cref=\"NotSupportedException\">\n\t/// - The last added element is not <see cref=\"ItemsControl\"/>.\n\t/// - <i>lazy</i> is <c>true</c> and the last added element is not <see cref=\"ComboBox\"/>.\n\t/// </exception>\n\tpublic wpfBuilder Items(IEnumerable items, bool lazy = false) => lazy ? Items(true, o => o.ItemsSource = items) : _Items(null, items);\n\t\n\t/// <summary>\n\t/// Sets callback function that should add items to the last added <see cref=\"ComboBox\"/> later.\n\t/// </summary>\n\t/// <param name=\"once\">Call the function once. If <c>false</c>, calls on each drop down.</param>\n\t/// <param name=\"onDropDown\">Callback function that should add items. Called before showing the dropdown part of the <see cref=\"ComboBox\"/>. Don't need to clear old items.</param>\n\t/// <exception cref=\"NotSupportedException\">The last added element is not <see cref=\"ComboBox\"/>.</exception>\n\tpublic wpfBuilder Items(bool once, Action<ComboBox> onDropDown) {\n\t\tvar c = Last as ComboBox ?? throw new NotSupportedException(\"Items(): Last added must be ComboBox\");\n\t\tEventHandler d = null;\n\t\td = (_, _) => {\n\t\t\tif (once) c.DropDownOpened -= d;\n\t\t\tif (c.ItemsSource != null) c.ItemsSource = null; else c.Items.Clear();\n\t\t\tonDropDown(c);\n\t\t};\n\t\tc.DropDownOpened += d;\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Selects an item of the last added <see cref=\"Selector\"/> (<see cref=\"ComboBox\"/>, etc).\n\t/// </summary>\n\t/// <param name=\"index\">0-based item index</param>\n\t/// <exception cref=\"NotSupportedException\">The last added element is not <see cref=\"Selector\"/>.</exception>\n\t/// <seealso cref=\"Items\"/>\n\tpublic wpfBuilder Select(int index) {\n\t\tvar c = Last as Selector ?? throw new NotSupportedException(\"Items(): Last added must be Selector, for example ComboBox or ListBox\");\n\t\tc.SelectedIndex = index;\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Selects an item of the last added <see cref=\"Selector\"/> (<see cref=\"ComboBox\"/>, etc).\n\t/// </summary>\n\t/// <param name=\"item\">An added item.</param>\n\t/// <exception cref=\"NotSupportedException\">The last added element is not <see cref=\"Selector\"/>.</exception>\n\t/// <seealso cref=\"Items\"/>\n\tpublic wpfBuilder Select(object item) {\n\t\tvar c = Last as Selector ?? throw new NotSupportedException(\"Items(): Last added must be Selector, for example ComboBox or ListBox\");\n\t\tc.SelectedItem = item;\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Obsolete. Use <see cref=\"FormatText\"/>.\n\t/// Adds inlines to the last added <see cref=\"TextBlock\"/>.\n\t/// </summary>\n\t/// <param name=\"inlines\">\n\t/// Arguments of type:\n\t/// <br/>• string like <c>\"&lt;b>text\"</c>, <c>\"&lt;i>text\"</c> or <c>\"&lt;u>text\"</c> adds inline of type <c>Bold</c>, <c>Italic</c> or <c>Underline</c>.\n\t/// <br/>• string like <c>\"&lt;a>text\"</c> adds <see cref=\"Hyperlink\"/>. Next argument of type <c>Action</c> or <c>Action&lt;Hyperlink&gt;</c> sets its action.\n\t/// <br/>• other string - plain text.\n\t/// <br/>• <see cref=\"WBLink\"/> adds a hyperlink.\n\t/// <br/>• <see cref=\"Inline\"/> of any type, eg <c>Run</c>, <c>Bold</c>, <c>Hyperlink</c>.\n\t/// <br/>• <see cref=\"UIElement\"/>.\n\t/// </param>\n\t/// <exception cref=\"NotSupportedException\">The last added element is not <c>TextBlock</c>.</exception>\n\t/// <exception cref=\"ArgumentException\">Unsupported argument type.</exception>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// b.R.Add<TextBlock>().Text(\n\t/// \t\"Text \", \"<b>bold \", \"\\n\",\n\t/// \tnew WBLink(\"libreautomate\", \"https://www.libreautomate.com\"), \", \",\n\t/// \tnew WBLink(\"Action\", _ => print.it(\"click\"), bold: true), \"\\n\",\n\t/// \tnew Run(\"color\") { Foreground = Brushes.Blue, Background = Brushes.Cornsilk, FontSize = 20 }, \"\\n\",\n\t/// \t\"controls\", new TextBox() { MinWidth = 100, Height = 20, Margin = new(3) }, new CheckBox() { Content = \"Check\" }, \"\\n\",\n\t/// \t\"image\", ImageUtil.LoadWpfImageElement(\"*PixelartIcons.Notes #0060F0\")\n\t/// \t);\n\t/// ]]></code>\n\t/// </example>\n\t[EditorBrowsable(EditorBrowsableState.Never)] //obsolete\n\tpublic wpfBuilder Text(params object[] inlines) {\n\t\tvar c = Last as TextBlock ?? throw new NotSupportedException(\"Text(): Last added must be TextBlock\");\n\t\tvar k = c.Inlines;\n\t\tk.Clear();\n\t\tHyperlink link = null;\n\t\tforeach (var v in inlines) {\n\t\t\tInline n = null; int i;\n\t\t\tswitch (v) {\n\t\t\tcase WBLink x:\n\t\t\t\tn = x.Hlink;\n\t\t\t\tbreak;\n\t\t\tcase Hyperlink x:\n\t\t\t\tn = link = x;\n\t\t\t\tbreak;\n\t\t\tcase Inline x:\n\t\t\t\tn = x;\n\t\t\t\tbreak;\n\t\t\tcase Action x when link != null: //<a> fbc\n\t\t\t\tlink.Click += (o, e) => x();\n\t\t\t\tcontinue;\n\t\t\tcase Action<Hyperlink> x when link != null: //<a> fbc\n\t\t\t\tlink.Click += (o, e) => x(o as Hyperlink);\n\t\t\t\tcontinue;\n\t\t\tcase UIElement x:\n\t\t\t\tn = new InlineUIContainer(x) { BaselineAlignment = BaselineAlignment.Center };\n\t\t\t\tbreak;\n\t\t\tcase string x:\n\t\t\t\tif (x.Starts('<') && (i = x.Starts(false, \"<a>\", \"<b>\", \"<i>\", \"<u>\")) > 0) {\n\t\t\t\t\tvar run = new Run(x[3..]);\n\t\t\t\t\tswitch (i) {\n\t\t\t\t\tcase 1: n = link = new Hyperlink(run); break;\n\t\t\t\t\tcase 2: n = new Bold(run); break;\n\t\t\t\t\tcase 3: n = new Italic(run); break;\n\t\t\t\t\tcase 4: n = new Underline(run); break;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tk.Add(x);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tdefault: throw new ArgumentException(\"Text(): unsupported argument type\");\n\t\t\t}\n\t\t\tk.Add(n);\n\t\t}\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Adds inlines (text, formatted text, hyperlinks, images, etc) to the last added <see cref=\"TextBlock\"/> etc.\n\t/// </summary>\n\t/// <param name=\"text\">\n\t/// Interpolated string (like <c>$\"string\"</c>) with tags etc. The format is XML without root element.\n\t/// <para>\n\t/// These tags add inlines of these types:\n\t/// <br/>• <c>&lt;b>text&lt;/b></c> - <see cref=\"Bold\"/>.\n\t/// <br/>• <c>&lt;i>text&lt;/i></c> - <see cref=\"Italic\"/>.\n\t/// <br/>• <c>&lt;u>text&lt;/u></c> - <see cref=\"Underline\"/>.\n\t/// <br/>• <c>&lt;s>text&lt;/s></c> - <see cref=\"D.Span\"/>.\n\t/// <br/>• <c>&lt;s {Span}>text&lt;/s></c> - <see cref=\"D.Span\"/> or a <see cref=\"D.Span\"/>-based type. The function adds <c>text</c> to its <c>Inlines</c> collection.\n\t/// <br/>• <c>&lt;a {Action or Action&lt;Hyperlink>}>text&lt;/a></c> - <see cref=\"Hyperlink\"/> that calls the action.\n\t/// <br/>• <c>&lt;a href='URL or path etc'>text&lt;/a></c> - <see cref=\"Hyperlink\"/> that calls <see cref=\"run.itSafe\"/>.\n\t/// </para>\n\t/// <para>\n\t/// Tags can have these attributes, like <c>&lt;s c='red' FontSize = '20'>text&lt;/s></c>:\n\t/// <br/>• <c>c</c> or <c>Foreground</c> - text color, like <c>'red'</c> or <c>'#AARRGGBB'</c> or <c>'#RRGGBB'</c> or <c>'#ARGB'</c> or <c>'#RGB'</c>.\n\t/// <br/>• <c>b</c> or <c>Background</c> - background color.\n\t/// <br/>• <c>FontFamily</c> - font name, like <c>'Consolas'</c>.\n\t/// <br/>• <c>FontSize</c> - font size, like <c>'20'</c>.\n\t/// <br/>• <c>ToolTip</c> - tooltip text.\n\t/// </para>\n\t/// <para>\n\t/// WPF elements of these types can be inserted without tags:\n\t/// <br/>• <c>{Inline}</c> - any inline, eg <see cref=\"Run\"/>. See also <c>&lt;s {Span}>text&lt;/s></c>.\n\t/// <br/>• <c>{UIElement}</c> - a WPF element, eg <see cref=\"CheckBox\"/> or <see cref=\"C.Image\"/>.\n\t/// <br/>• <c>{ImageSource}</c> - adds <see cref=\"C.Image\"/>.\n\t/// <br/>• <c>{IEnumerable&lt;Inline>}</c> - adds multiple <see cref=\"Inline\"/>.\n\t/// </para>\n\t/// XML special characters must be escaped:\n\t/// <br/>• <c>&lt;</c> - <c>&amp;lt;</c>.\n\t/// <br/>• <c>&amp;</c> - <c>&amp;amp;</c>.\n\t/// <br/>• <c>'</c>, <c>\"</c> - <c>&amp;apos;</c> in <c>'attribute'</c> or <c>&amp;quot;</c> in <c>\"attribute\"</c>.\n\t/// <para>\n\t/// </para>\n\t/// The <c>text</c> in above examples can contain nested tags and elements.\n\t/// The <c>&lt;a></c> tag creates an image hyperlink if <c>text</c> is <c>{image}</c>, where image is a <see cref=\"UIElement\"/> with image (see <see cref=\"ImageUtil.LoadWpfImageElement\"/>) or <see cref=\"ImageSource\"/> (see <see cref=\"ImageUtil.LoadWpfImage\"/>, <see cref=\"icon.ToWpfImage\"/>).\n\t/// </param>\n\t/// <exception cref=\"NotSupportedException\">Unsupported type of the last added element. Or supported type but non-empty <c>Content</c> and <c>Header</c> (read Remarks).</exception>\n\t/// <exception cref=\"ArgumentException\">Unknown <c>&lt;tag></c> or unsupported <c>{object}</c> type.</exception>\n\t/// <exception cref=\"InvalidOperationException\">The same <c>{Span}</c> or <c>{Inline}</c> object in multiple places.</exception>\n\t/// <exception cref=\"FormatException\">Invalid color attribute.</exception>\n\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"XElement.Parse\"/>.</exception>\n\t/// <remarks>\n\t/// The last added element can be of type:\n\t/// <br/>• <see cref=\"TextBlock\"/> - the function adds inlines to its <c>Inlines</c> collection.\n\t/// <br/>• <see cref=\"ContentControl\"/> (eg <see cref=\"Label\"/> or <see cref=\"Button\"/>) - creates new <see cref=\"TextBlock\"/> with inlines and sets its <c>Content</c> property if it is <c>null</c>. If <see cref=\"HeaderedContentControl\"/> (eg <see cref=\"GroupBox\"/>) and its <c>Header</c> property is <c>null</c>, sets <c>Header</c> instead.\n\t/// <br/>• <see cref=\"Panel\"/> whose <c>Parent</c> is <see cref=\"HeaderedContentControl\"/> (eg <c>b.StartGrid&lt;GroupBox>(null).FormatText($\"...\")</c>) - uses the <see cref=\"HeaderedContentControl\"/> like the above.\n\t///\n\t/// For elements other than the last added use <see cref=\"formatTextOf(object, InterpolatedString)\"/> or <see cref=\"formattedText(InterpolatedString)\"/>.\n\t///\n\t/// To load images can be used <see cref=\"ImageUtil.LoadWpfImageElement\"/> and <see cref=\"ImageUtil.LoadWpfImage\"/>.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// b.R.Add<TextBlock>().FormatText($\"\"\"\n\t/// Text <b>bold</b> <i>italic <u>underline</u>.</i>\n\t/// <s c='GreenYellow' b='Black' FontFamily='Consolas' FontSize='20'>attributes</s>\n\t/// <s {new Span() { Foreground = Brushes.Red, Background = new LinearGradientBrush(Colors.GreenYellow, Colors.Transparent, 90) }}>Span object, <b>bold</b></s>\n\t/// <a href='https://www.example.com'>example.com</a> <b><a href='notepad.exe'>Notepad</a></b>\n\t/// <a {() => { print.it(\"click\"); }}>click</a> <a {(Hyperlink h) => { print.it(\"click once\"); h.IsEnabled = false; }}>click once</a>\n\t/// <a {() => { print.it(\"click\"); }} ToolTip='image hyperlink'>{ImageUtil.LoadWpfImageElement(\"*Entypo.HelpWithCircle #008EEE @14\")}</a>\n\t/// {new Run(\"Run object\") { Foreground = Brushes.Blue, Background = Brushes.Goldenrod, FontSize = 20 }}\n\t/// Image {ImageUtil.LoadWpfImageElement(\"*PixelartIcons.Notes #0060F0\")}<!-- or ImageUtil.LoadWpfImage(@\"C:\\Test\\image.png\") -->\n\t/// Controls {new TextBox() { MinWidth = 100, Height = 20, Margin = new(3) }} {new CheckBox() { Content = \"Check\" }}\n\t/// \"\"\");\n\t/// ]]></code>\n\t/// Build interpolated string at run time.\n\t/// <code><![CDATA[\n\t/// wpfBuilder.InterpolatedString s = new();\n\t/// s.AppendLiteral(\"Text <b>bold</b> <a \");\n\t/// s.AppendFormatted(() => { print.it(\"click\"); });\n\t/// s.AppendLiteral(\">link</a>.\");\n\t/// b.R.Add<TextBlock>().FormatText(s);\n\t/// ]]></code>\n\t/// </example>\n\tpublic wpfBuilder FormatText(InterpolatedString text) {\n\t\tvar s = text.GetFormattedText();\n\t\t_FormatText(Last, s, text.aObj);\n\t\treturn this;\n\t}\n\t\n\t//rejected: overload `FormatText(string text)` that supports only tags where don't need {object}.\n\t\n\t/// <summary>\n\t/// Adds inlines (text, formatted text, hyperlinks, images, etc) to the specified <see cref=\"TextBlock\"/> etc.\n\t/// </summary>\n\t/// <param name=\"obj\">Object of type <see cref=\"TextBlock\"/>, <see cref=\"ContentControl\"/> or <see cref=\"InlineCollection\"/>. More info in <see cref=\"FormatText(InterpolatedString)\"/> remarks.</param>\n\t/// <exception cref=\"NotSupportedException\">Unsupported <i>obj</i> type or non-empty <c>Content</c>/<c>Header</c>.</exception>\n\t/// <exception cref=\"ArgumentException\">Unknown <c>&lt;tag></c> or unsupported <c>{object}</c> type.</exception>\n\t/// <exception cref=\"InvalidOperationException\">The same <c>{Span}</c> or <c>{Inline}</c> object in multiple places.</exception>\n\t/// <exception cref=\"FormatException\">Invalid color attribute.</exception>\n\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"XElement.Parse\"/>.</exception>\n\t/// <inheritdoc cref=\"FormatText(InterpolatedString)\" path=\"/param\"/>\n\tpublic static void formatTextOf(object obj, InterpolatedString text) {\n\t\tvar s = text.GetFormattedText();\n\t\t_FormatText(obj, s, text.aObj);\n\t}\n\t\n\t/// <summary>\n\t/// Creates new <see cref=\"TextBlock\"/> and adds inlines like <see cref=\"FormatText(InterpolatedString)\"/>.\n\t/// </summary>\n\t/// <exception cref=\"ArgumentException\">Unknown <c>&lt;tag></c> or unsupported <c>{object}</c> type.</exception>\n\t/// <exception cref=\"InvalidOperationException\">The same <c>{Span}</c> or <c>{Inline}</c> object in multiple places.</exception>\n\t/// <exception cref=\"FormatException\">Invalid color attribute.</exception>\n\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"XElement.Parse\"/>.</exception>\n\t/// <inheritdoc cref=\"FormatText(InterpolatedString)\" path=\"/param\"/>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// b.R.Add(wpfBuilder.formattedText($\"<b>Label</b>\"), out TextBox _);\n\t/// b.R.AddButton(_TextWithIcon(\"Button\", \"*PixelartIcons.Notes #0060F0\"), _ => { print.it(\"Button clicked\"); });\n\t/// \n\t/// static TextBlock _TextWithIcon(string text, string icon) {\n\t/// \tvar e = ImageUtil.LoadWpfImageElement(icon);\n\t/// \te.Margin = new(0, 0, 4, 0);\n\t/// \treturn wpfBuilder.formattedText($\"{e}{text}\");\n\t/// }\n\t/// ]]></code>\n\t/// </example>\n\tpublic static TextBlock formattedText(InterpolatedString text) {\n\t\tvar e = new TextBlock();\n\t\tvar s = text.GetFormattedText();\n\t\t_FormatText(e, s, text.aObj);\n\t\treturn e;\n\t}\n\t\n\tstatic void _FormatText(object obj, string text, List<object> a) {\n\t\t//print.it(text, a);\n\t\tInlineCollection ic;\n\t\tg1:\n\t\tswitch (obj) {\n\t\tcase TextBlock k: ic = k.Inlines; break;\n\t\tcase HeaderedContentControl k: k.Header = obj = new TextBlock(); goto g1;\n\t\tcase ContentControl k: k.Content = obj = new TextBlock(); goto g1;\n\t\tcase Panel k when k.Parent is HeaderedContentControl p1: obj = p1; goto g1; //eg b.StartGrid<GroupBox>(null).FormatText($\"...\")\n\t\tcase InlineCollection k: ic = k; break;\n\t\tdefault: throw new NotSupportedException(\"Format(): unsupported element type\");\n\t\t}\n\t\tic.Clear();\n\t\t\n\t\tvar xr = XElement.Parse(\"<x>\" + text + \"</x>\", LoadOptions.PreserveWhitespace);\n\t\t_Enum(ic, xr);\n\t\t\n\t\tvoid _Enum(InlineCollection ic, XElement xp) {\n\t\t\tfor (var xn = xp.FirstNode; xn != null; xn = xn.NextNode) {\n\t\t\t\tif (xn is XElement xe) _Element(xe);\n\t\t\t\telse if (xn is XText xt) _Text(xt); //also XCData\n\t\t\t}\n\t\t\t\n\t\t\tvoid _Element(XElement x) {\n\t\t\t\tSpan r = null;\n\t\t\t\tHyperlink h = null;\n\t\t\t\tvar tag = x.Name.LocalName;\n\t\t\t\tswitch (tag) {\n\t\t\t\tcase \"b\": r = new Bold(); break;\n\t\t\t\tcase \"i\": r = new Italic(); break;\n\t\t\t\tcase \"u\": r = new Underline(); break;\n\t\t\t\tcase \"s\":\n\t\t\t\t\tif (_GetAttrObj() is object o) { //<s {Span}>...</s>\n\t\t\t\t\t\tr = o as Span ?? throw new ArgumentException(\"Expected <s {Span}>\");\n\t\t\t\t\t\tif (r.Parent != null) throw new InvalidOperationException(\"Reused {Span} object\");\n\t\t\t\t\t} else { //<s attributes}>...</s>\n\t\t\t\t\t\tr = new();\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"a\":\n\t\t\t\t\th = new Hyperlink();\n\t\t\t\t\tr = h;\n\t\t\t\t\tif (x.Attr(\"href\") is string href) { //<a href='...'>\n\t\t\t\t\t\th.Click += (_, _) => run.itSafe(href);\n\t\t\t\t\t} else { //<a {Action}>\n\t\t\t\t\t\tswitch (_GetAttrObj()) {\n\t\t\t\t\t\tcase Action g: h.Click += (_, _) => g(); break;\n\t\t\t\t\t\tcase Action<Hyperlink> g: h.Click += (_, _) => g(h); break;\n\t\t\t\t\t\tdefault: throw new ArgumentException(\"Expected <a {Action}> or <a href='...'>\");\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tdefault: throw new ArgumentException($\"Unknown tag <{tag}>\");\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tforeach (var at in x.Attributes()) {\n\t\t\t\t\tvar v = at.Value;\n\t\t\t\t\tswitch (at.Name.LocalName) {\n\t\t\t\t\tcase \"c\" or \"Foreground\": r.Foreground = _Brush(v); break;\n\t\t\t\t\tcase \"b\" or \"Background\": r.Background = _Brush(v); break;\n\t\t\t\t\tcase \"FontFamily\": r.FontFamily = new(v); break;\n\t\t\t\t\tcase \"FontSize\": if (v.ToNumber(out double fsize)) r.FontSize = fsize; break;\n\t\t\t\t\tcase \"ToolTip\": r.ToolTip = v; break;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tic.Add(r);\n\t\t\t\tif (h == null || !_HyperlinkImage(x, h))\n\t\t\t\t\t_Enum(r.Inlines, x);\n\t\t\t\t\n\t\t\t\tobject _GetAttrObj() => a != null && x.Attr(\"_a\") is string s && s.Starts(c_mark2) && s.ToInt(out int i1, c_mark2.Length) ? a[i1] : null;\n\t\t\t\tstatic Brush _Brush(string v) => new SolidColorBrush((Color)ColorConverter.ConvertFromString(v));\n\t\t\t}\n\t\t\t\n\t\t\tvoid _Text(XText x) {\n\t\t\t\tvar s = x.Value;\n\t\t\t\tif (a != null) {\n\t\t\t\t\tint from = 0;\n\t\t\t\t\tfor (; from < s.Length; from++) {\n\t\t\t\t\t\tint m = s.Find(c_mark, from);\n\t\t\t\t\t\tif (m < 0) break;\n\t\t\t\t\t\tif (m > from) ic.Add(s[from..m]);\n\t\t\t\t\t\tif (s.ToInt(out int i, m + c_mark.Length, out from)) {\n\t\t\t\t\t\t\tvar k = a[i];\n\t\t\t\t\t\t\tg2:\n\t\t\t\t\t\t\tswitch (k) {\n\t\t\t\t\t\t\tcase Inline e:\n\t\t\t\t\t\t\t\tif (e.Parent != null) throw new InvalidOperationException(\"Reused {Inline} object\");\n\t\t\t\t\t\t\t\tic.Add(e);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase UIElement e:\n\t\t\t\t\t\t\t\tic.Add(new InlineUIContainer(e) { BaselineAlignment = BaselineAlignment.Center });\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase ImageSource e:\n\t\t\t\t\t\t\t\tk = new Image { Source = e, Stretch = Stretch.None };\n\t\t\t\t\t\t\t\tgoto g2;\n\t\t\t\t\t\t\tcase IEnumerable<Inline> e:\n\t\t\t\t\t\t\t\tic.AddRange(e);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tdefault: throw new ArgumentException($\"Unexpected element type {a[i]}\");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (from < s.Length) ic.Add(s[from..]);\n\t\t\t\t} else ic.Add(s);\n\t\t\t}\n\t\t}\n\t\t\n\t\tbool _HyperlinkImage(XElement x, Hyperlink h) {\n\t\t\tif (x.FirstNode is XText xt && xt.NextNode == null && xt.Value is string s && s.Starts(c_mark) && s.ToInt(out int i, c_mark.Length, out int end) && end == s.Length - 1) {\n\t\t\t\tUIElement eImg = a[i] switch {\n\t\t\t\t\tUIElement e => e,\n\t\t\t\t\tImageSource e => new Image { Source = e, Stretch = Stretch.None },\n\t\t\t\t\t_ => null\n\t\t\t\t};\n\t\t\t\tif (eImg == null) return false;\n\t\t\t\t\n\t\t\t\tBorder border = new() { //workaround for: image's transparent areas are not hittest-sensitive. InlineUIContainer.Background does not fix it.\n\t\t\t\t\tChild = eImg,\n\t\t\t\t\tBackground = Brushes.Transparent,\n\t\t\t\t\tPadding = new(0, 2, 0, 0)\n\t\t\t\t};\n\t\t\t\tInlineUIContainer uc = new(border) { BaselineAlignment = BaselineAlignment.Center };\n\t\t\t\th.Inlines.Add(uc);\n\t\t\t\th.TextDecorations = null;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t}\n\t\n\tconst string c_mark = \"_a='≡∫∫≡\", c_mark2 = \"≡∫∫≡\"; //start of mark. Full mark is like \"_a='≡∫∫≡0'\", where 0 is aObj index. With this format, can be used as an XML attribute, as well as in XML text.\n\t\n#pragma warning disable 1591 //no XML doc\n\t[InterpolatedStringHandler, NoDoc]\n\tpublic ref struct InterpolatedString {\n\t\tDefaultInterpolatedStringHandler _f;\n\t\tinternal List<object> aObj;\n\t\t\n\t\tpublic InterpolatedString(int literalLength, int formattedCount) {\n\t\t\t_f = new(literalLength, formattedCount);\n\t\t}\n\t\t\n\t\tpublic InterpolatedString(int literalLength, int formattedCount, IFormatProvider provider) {\n\t\t\t_f = new(literalLength, formattedCount, provider);\n\t\t}\n\t\t\n\t\tpublic InterpolatedString(int literalLength, int formattedCount, IFormatProvider provider, Span<char> initialBuffer) {\n\t\t\t_f = new(literalLength, formattedCount, provider, initialBuffer);\n\t\t}\n\t\t\n\t\tpublic void AppendLiteral(string value)\n\t\t\t => _f.AppendLiteral(value);\n\t\t\n\t\tpublic void AppendFormatted(string value)\n\t\t\t=> _f.AppendFormatted(value);\n\t\t\n\t\tpublic void AppendFormatted<T>(T value) {\n\t\t\tif (value is Delegate or DependencyObject or IEnumerable<Inline>) {\n\t\t\t\taObj ??= new();\n\t\t\t\t_f.AppendLiteral(c_mark);\n\t\t\t\t_f.AppendLiteral(aObj.Count.ToS());\n\t\t\t\t_f.AppendLiteral(\"'\");\n\t\t\t\taObj.Add(value);\n\t\t\t} else _f.AppendFormatted(value);\n\t\t}\n\t\t\n\t\tpublic void AppendFormatted<T>(T value, int alignment)\n\t\t\t => _f.AppendFormatted(value, alignment);\n\t\t\n\t\tpublic void AppendFormatted<T>(T value, string format) {\n\t\t\t_f.AppendFormatted(value, format);\n\t\t}\n\t\t\n\t\tpublic void AppendFormatted<T>(T value, int alignment, string format) {\n\t\t\t_f.AppendFormatted(value, alignment, format);\n\t\t}\n\t\t\n\t\tpublic void AppendFormatted(RStr value)\n\t\t\t=> _f.AppendFormatted(value);\n\t\t\n\t\tpublic void AppendFormatted(RStr value, int alignment = 0, string format = null)\n\t\t\t=> _f.AppendFormatted(value, alignment, format);\n\t\t\n\t\tpublic void AppendFormatted(string value, int alignment = 0, string format = null)\n\t\t\t=> _f.AppendFormatted(value, alignment, format);\n\t\t\n\t\tpublic void AppendFormatted(object value, int alignment = 0, string format = null)\n\t\t\t=> _f.AppendFormatted(value, alignment, format);\n\t\t\n\t\tpublic string GetFormattedText() => _f.ToStringAndClear();\n\t}\n#pragma warning restore 1591\n\t\n\t/// <summary>\n\t/// Loads a web page or RTF text from a file or URL into the last added element.\n\t/// </summary>\n\t/// <param name=\"source\">File or URL to load. Supported element types and sources:\n\t/// <see cref=\"WebBrowser\"/>, <see cref=\"Frame\"/> - URL or file path.\n\t/// <see cref=\"RichTextBox\"/> - path of a local <c>.rtf</c> file.\n\t/// </param>\n\t/// <exception cref=\"NotSupportedException\">\n\t/// - Unsupported element type.\n\t/// - <see cref=\"RichTextBox\"/> <i>source</i> does not end with <c>\".rtf\"</c>.\n\t/// </exception>\n\t/// <remarks>\n\t/// If fails to load, prints warning. See <see cref=\"print.warning\"/>.\n\t/// </remarks>\n\tpublic wpfBuilder LoadFile(string source) {\n\t\tvar c = Last;\n\t\tbool bad = false;\n\t\ttry {\n\t\t\tsource = _UriNormalize(source);\n\t\t\tswitch (c) {\n\t\t\tcase WebBrowser u: u.Source = new Uri(source); break;\n\t\t\tcase Frame u: u.Source = new Uri(source); break;\n\t\t\tcase RichTextBox u when source.Ends(\".rtf\", true):\n\t\t\t\tusing (var fs = File.OpenRead(source)) { u.Selection.Load(fs, DataFormats.Rtf); }\n\t\t\t\t//also supports DataFormats.Text,Xaml,XamlPackage. If need HTML, download and try HtmlToXamlConverter. See https://www.codeproject.com/Articles/1097390/Displaying-HTML-in-a-WPF-RichTextBox\n\t\t\t\tbreak;\n\t\t\tdefault: bad = true; break;\n\t\t\t}\n\t\t}\n\t\tcatch (Exception ex) { print.warning(\"LoadFile() failed. \" + ex.ToString(), -1); }\n\t\tif (bad) throw new NotSupportedException(\"LoadFile(): Unsupported type of element or source.\");\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Loads image into the last added <see cref=\"C.Image\"/>.\n\t/// </summary>\n\t/// <param name=\"source\">Sets <see cref=\"Image.Source\"/>.</param>\n\t/// <param name=\"stretch\">Sets <see cref=\"Image.Stretch\"/>.</param>\n\t/// <param name=\"stretchDirection\">Sets <see cref=\"Image.StretchDirection\"/>.</param>\n\t/// <exception cref=\"NotSupportedException\">The last added element is not <see cref=\"C.Image\"/>.</exception>\n\t/// <remarks>\n\t/// To load vector images from XAML, don't use <see cref=\"C.Image\"/> control and this function. Instead create control from XAML, for example with <see cref=\"ImageUtil.LoadWpfImageElement\"/>, and add it with <see cref=\"Add(FrameworkElement, bool)\"/>.\n\t/// </remarks>\n\t/// <seealso cref=\"icon.ToWpfImage\"/>\n\t/// <seealso cref=\"ImageUtil\"/>\n\tpublic wpfBuilder Image(ImageSource source, Stretch stretch = Stretch.None, StretchDirection stretchDirection = StretchDirection.DownOnly)\n\t\t => _Image(source, null, stretch, stretchDirection);\n\t\n\twpfBuilder _Image(ImageSource source, string file, Stretch stretch, StretchDirection stretchDirection) {\n\t\tvar c = Last as Image ?? throw new NotSupportedException(\"Image(): Last added must be Image\");\n\t\tif (file != null) {\n\t\t\t//try { source = new BitmapImage(_Uri(file)); }\n\t\t\ttry { source = ImageUtil.LoadWpfImage(file); }\n\t\t\tcatch (Exception ex) { print.warning(\"Image() failed. \" + ex.ToString(), -1); }\n\t\t}\n\t\tc.Stretch = stretch; //default Uniform\n\t\tc.StretchDirection = stretchDirection; //default Both\n\t\tc.Source = source;\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// Loads image from a file or URL into the last added <see cref=\"C.Image\"/>.\n\t/// </summary>\n\t/// <param name=\"source\">File path etc. See <see cref=\"ImageUtil.LoadWpfImage\"/>. Sets <see cref=\"Image.Source\"/>.</param>\n\t/// <param name=\"stretch\">Sets <see cref=\"Image.Stretch\"/>.</param>\n\t/// <param name=\"stretchDirection\">Sets <see cref=\"Image.StretchDirection\"/>.</param>\n\t/// <exception cref=\"NotSupportedException\">The last added element is not <see cref=\"C.Image\"/>.</exception>\n\t/// <remarks>\n\t/// If fails to load, prints warning. See <see cref=\"print.warning\"/>.\n\t/// </remarks>\n\tpublic wpfBuilder Image(string source, Stretch stretch = Stretch.None, StretchDirection stretchDirection = StretchDirection.DownOnly)\n\t\t=> _Image(null, source, stretch, stretchDirection);\n\t\n\t/// <summary>\n\t/// Sets vertical or horizontal splitter properties of the last added <see cref=\"GridSplitter\"/>.\n\t/// </summary>\n\t/// <param name=\"vertical\">If <c>true</c>, resizes columns, else rows.</param>\n\t/// <param name=\"span\">How many rows spans vertical splitter, or how many columns spans horizontal splitter. Can be more than row/column count.</param>\n\t/// <param name=\"thickness\">Width of vertical splitter or height of horizontal. If <c>double.NaN</c>, sets alignment \"stretch\", else \"center\".</param>\n\t/// <exception cref=\"NotSupportedException\">The last added element is not <see cref=\"GridSplitter\"/>.</exception>\n\t/// <example>\n\t/// Vertical splitter.\n\t/// <code><![CDATA[\n\t/// var b = new wpfBuilder(\"Window\").WinSize(400)\n\t/// \t.Columns(30.., 0, -1) //the middle column is for splitter; the 30 is minimal width\n\t/// \t.R.Add(out TextBox _)\n\t/// \t.Add<GridSplitter>().Splitter(true, 2).Brush(Brushes.Orange) //add splitter in the middle column\n\t/// \t.Add(out TextBox _)\n\t/// \t.R.Add(out TextBox _).Skip().Add(out TextBox _) //skip the splitter's column\n\t/// \t.R.AddOkCancel()\n\t/// \t.End();\n\t/// if (!b.ShowDialog()) return;\n\t/// ]]></code>\n\t/// Horizontal splitter.\n\t/// <code><![CDATA[\n\t/// var b = new wpfBuilder(\"Window\").WinSize(300, 300)\n\t/// \t.Row(27..).Add(\"Row\", out TextBox _)\n\t/// \t.Add<GridSplitter>().Splitter(false, 2).Brush(Brushes.Orange)\n\t/// \t.Row(-1).Add(\"Row\", out TextBox _)\n\t/// \t.R.AddOkCancel()\n\t/// \t.End();\n\t/// if (!b.ShowDialog()) return;\n\t/// ]]></code>\n\t/// </example>\n\tpublic wpfBuilder Splitter(bool vertical, int span = 1, double thickness = 4) {\n\t\tvar g = _ParentOfLastAsOrThrow<_Grid>();\n\t\tvar c = Last as GridSplitter ?? throw new NotSupportedException(\"Splitter(): Last added must be GridSplitter\");\n\t\tif (vertical) {\n\t\t\tc.HorizontalAlignment = double.IsNaN(thickness) ? HorizontalAlignment.Stretch : HorizontalAlignment.Center;\n\t\t\tc.VerticalAlignment = VerticalAlignment.Stretch;\n\t\t\tc.ResizeDirection = GridResizeDirection.Columns;\n\t\t\tc.Width = thickness;\n\t\t\tif (span != 1) Grid.SetRowSpan(c, span);\n\t\t} else {\n\t\t\tc.HorizontalAlignment = HorizontalAlignment.Stretch;\n\t\t\tc.VerticalAlignment = double.IsNaN(thickness) ? VerticalAlignment.Stretch : VerticalAlignment.Center;\n\t\t\tc.ResizeDirection = GridResizeDirection.Rows;\n\t\t\tc.Height = thickness;\n\t\t\tif (span != 1) g.Span(span);\n\t\t}\n\t\tc.ResizeBehavior = GridResizeBehavior.PreviousAndNext;\n\t\treturn this;\n\t}\n\t\n\t//FUTURE: need a numeric input control. This code is for WinForms NumericUpDown.\n\t//\tpublic wpfBuilder Number(decimal? value = null, decimal? min = null, decimal? max=null, decimal? increment=null, int? decimalPlaces=null, bool? thousandsSeparator=null, bool? hex =null) {\n\t//\t\tvar c = Last as NumericUpDown ?? throw new NotSupportedException(\"Number(): Last added must be NumericUpDown\");\n\t//\t\tif(min!=null) c.Minimum=min.Value;\n\t//\t\tif(max!=null) c.Maximum=max.Value;\n\t//\t\tif(increment!=null) c.Increment=increment.Value;\n\t//\t\tif(decimalPlaces!=null) c.DecimalPlaces=decimalPlaces.Value;\n\t//\t\tif(thousandsSeparator!=null) c.ThousandsSeparator=thousandsSeparator.Value;\n\t//\t\tif(hex!=null) c.Hexadecimal=hex.Value;\n\t//\t\tif(value!=null) c.Value=value.Value; else c.Text=null;\n\t//\t\treturn this;\n\t//\t}\n\t\n\t#endregion\n\t\n\t#region nested panel\n\t\n\twpfBuilder _Start(_PanelBase p, bool childOfLast) {\n\t\t_p.BeforeAdd(childOfLast);\n\t\t_AddToParent(p.panel, childOfLast);\n\t\t_p = p;\n\t\treturn this;\n\t}\n\t\n\twpfBuilder _Start<T>(_PanelBase p, out T container, object header) where T : HeaderedContentControl, new() {\n\t\tAdd(out container, header);\n\t\tcontainer.Content = p.panel;\n\t\tif (container is GroupBox) p.panel.Margin = new(0, 2, 0, 0);\n\t\t_p = p;\n\t\treturn this;\n\t}\n\t\n\t\n\t/// <summary>\n\t/// Adds <see cref=\"Grid\"/> panel (table) that will contain elements added with <see cref=\"Add\"/> etc. Finally call <see cref=\"End\"/> to return to current panel.\n\t/// </summary>\n\t/// <param name=\"childOfLast\">Add as a child or content of the last added element (<see cref=\"Last\"/>), which must be a <see cref=\"Decorator\"/> (for example <see cref=\"C.Border\"/> or <see cref=\"AdornerDecorator\"/>) or <see cref=\"ContentControl\"/> (for example <see cref=\"Button\"/>).</param>\n\t/// <remarks>\n\t/// How <see cref=\"Last\"/> changes: after calling this function it is the grid (<see cref=\"Panel\"/>); after adding an element it is the element; finally, after calling <see cref=\"End\"/> it is the grid if <i>childOfLast</i> <c>false</c>, else its parent. The same with all <c>StartX</c> functions.\n\t/// </remarks>\n\tpublic wpfBuilder StartGrid(bool childOfLast = false)\n\t\t=> _Start(new _Grid(this), childOfLast);\n\t\n\t/// <summary>\n\t/// Adds a headered content control (<see cref=\"GroupBox\"/>, <see cref=\"Expander\"/>, etc) with child <see cref=\"Grid\"/> panel (table) that will contain elements added with <see cref=\"Add\"/> etc. Finally call <see cref=\"End\"/> to return to current panel.\n\t/// </summary>\n\t/// <param name=\"header\">Header text/content.</param>\n\t/// <remarks>\n\t/// How <see cref=\"Last\"/> changes: after calling this function it is the grid (<see cref=\"Panel\"/>); after adding an element it is the element; finally, after calling <see cref=\"End\"/> it is the content control (grid's parent). The same with all <c>StartX</c> functions.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// b.StartGrid<GroupBox>(\"Group\");\n\t/// ]]></code>\n\t/// </example>\n\tpublic wpfBuilder StartGrid<T>(object header) where T : HeaderedContentControl, new()\n\t\t=> _Start(new _Grid(this), out T _, header);\n\t\n\t/// <param name=\"container\">Receives the content control's variable. The function creates new control of the type.</param>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// b.StartGrid(out Expander g, \"Expander\"); g.IsExpanded=true;\n\t/// ]]></code>\n\t/// </example>\n\t/// <inheritdoc cref=\"StartGrid{T}(object)\"/>\n\tpublic wpfBuilder StartGrid<T>(out T container, object header) where T : HeaderedContentControl, new()\n\t\t=> _Start(new _Grid(this), out container, header);\n\t\n\t\n\t\n\t/// <summary>\n\t/// Adds <see cref=\"Canvas\"/> panel that will contain elements added with <see cref=\"Add\"/> etc. Finally call <see cref=\"End\"/> to return to current panel.\n\t/// </summary>\n\t/// <param name=\"childOfLast\"><inheritdoc cref=\"StartGrid(bool)\" path=\"/param[@name='childOfLast']/node()\"/></param>\n\t/// <remarks>\n\t/// For each added control call <see cref=\"XY\"/> or use indexer like <c>[x, y]</c> or <c>[x, y, width, height]</c>.\n\t/// </remarks>\n\tpublic wpfBuilder StartCanvas(bool childOfLast = false)\n\t\t=> _Start(new _Canvas(this), childOfLast);\n\t\n\t/// <summary>\n\t/// Adds a headered content control (<see cref=\"GroupBox\"/>, <see cref=\"Expander\"/>, etc) with child <see cref=\"Canvas\"/> panel that will contain elements added with <see cref=\"Add\"/> etc. Finally call <see cref=\"End\"/> to return to current panel.\n\t/// </summary>\n\t/// <param name=\"header\">Header text/content.</param>\n\tpublic wpfBuilder StartCanvas<T>(object header) where T : HeaderedContentControl, new()\n\t\t=> _Start(new _Canvas(this), out T _, header);\n\t\n\t/// <param name=\"container\">Receives the content control's variable. The function creates new control of the type.</param>\n\t/// <inheritdoc cref=\"StartCanvas{T}(object)\"/>\n\tpublic wpfBuilder StartCanvas<T>(out T container, object header) where T : HeaderedContentControl, new()\n\t\t=> _Start(new _Canvas(this), out container, header);\n\t\n\t\n\t/// <summary>\n\t/// Adds <see cref=\"DockPanel\"/> panel that will contain elements added with <see cref=\"Add\"/> etc. Finally call <see cref=\"End\"/> to return to current panel.\n\t/// </summary>\n\t/// <param name=\"childOfLast\"><inheritdoc cref=\"StartGrid(bool)\" path=\"/param[@name='childOfLast']/node()\"/></param>\n\t/// <remarks>\n\t/// For added elements call <see cref=\"Dock\"/>, maybe except for the last element that fills remaining space.\n\t/// </remarks>\n\tpublic wpfBuilder StartDock(bool childOfLast = false)\n\t\t=> _Start(new _DockPanel(this), childOfLast);\n\t\n\t/// <summary>\n\t/// Adds a headered content control (<see cref=\"GroupBox\"/>, <see cref=\"Expander\"/>, etc) with child <see cref=\"DockPanel\"/> panel that will contain elements added with <see cref=\"Add\"/> etc. Finally call <see cref=\"End\"/> to return to current panel.\n\t/// </summary>\n\t/// <param name=\"header\">Header text/content.</param>\n\tpublic wpfBuilder StartDock<T>(object header) where T : HeaderedContentControl, new()\n\t\t=> _Start(new _DockPanel(this), out T _, header);\n\t\n\t/// <param name=\"container\">Receives the content control's variable. The function creates new control of the type.</param>\n\t/// <inheritdoc cref=\"StartDock{T}(object)\"/>\n\tpublic wpfBuilder StartDock<T>(out T container, object header) where T : HeaderedContentControl, new()\n\t\t=> _Start(new _DockPanel(this), out container, header);\n\t\n\t\n\t/// <summary>\n\t/// Adds <see cref=\"StackPanel\"/> panel that will contain elements added with <see cref=\"Add\"/> etc. Finally call <see cref=\"End\"/> to return to current panel.\n\t/// </summary>\n\t/// <param name=\"vertical\"></param>\n\t/// <param name=\"childOfLast\"><inheritdoc cref=\"StartGrid(bool)\" path=\"/param[@name='childOfLast']/node()\"/></param>\n\tpublic wpfBuilder StartStack(bool vertical = false, bool childOfLast = false)\n\t\t=> _Start(new _StackPanel(this, vertical), childOfLast);\n\t\n\t/// <summary>\n\t/// Adds a headered content control (<see cref=\"GroupBox\"/>, <see cref=\"Expander\"/>, etc) with child <see cref=\"StackPanel\"/> panel that will contain elements added with <see cref=\"Add\"/> etc. Finally call <see cref=\"End\"/> to return to current panel.\n\t/// </summary>\n\t/// <param name=\"header\">Header text/content.</param>\n\t/// <param name=\"vertical\"></param>\n\tpublic wpfBuilder StartStack<T>(object header, bool vertical = false) where T : HeaderedContentControl, new()\n\t\t=> _Start(new _StackPanel(this, vertical), out T _, header);\n\t\n\t/// <param name=\"container\">Receives the content control's variable. The function creates new control of the type.</param>\n\t/// <inheritdoc cref=\"StartStack{T}(object, bool)\"/>\n\tpublic wpfBuilder StartStack<T>(out T container, object header, bool vertical = false) where T : HeaderedContentControl, new()\n\t\t=> _Start(new _StackPanel(this, vertical), out container, header);\n\t\n\t\n\t/// <summary>\n\t/// Adds panel of any type. It will contain elements added with <see cref=\"Add\"/> etc. Finally call <see cref=\"End\"/> to return to current panel.\n\t/// </summary>\n\t/// <param name=\"panel\">New panel of any type (<see cref=\"Grid\"/>, <see cref=\"WrapPanel\"/>, etc). For <see cref=\"Grid\"/> and <see cref=\"Canvas\"/> this function sets some panel properties to make everything work like with other <c>StartX</c> functions.</param>\n\t/// <param name=\"childOfLast\"><inheritdoc cref=\"StartGrid(bool)\" path=\"/param[@name='childOfLast']/node()\"/></param>\n\tpublic wpfBuilder StartPanel(Panel panel, bool childOfLast = false)\n\t\t=> _Start(_StartPanel(panel), childOfLast);\n\t\n\t/// <summary>\n\t/// Adds a headered content control (<see cref=\"GroupBox\"/>, <see cref=\"Expander\"/>, etc) with child panel of any type. The panel will contain elements added with <see cref=\"Add\"/> etc. Finally call <see cref=\"End\"/> to return to current panel.\n\t/// </summary>\n\t/// <param name=\"panel\">New panel of any type (<see cref=\"Grid\"/>, <see cref=\"WrapPanel\"/>, etc). For <see cref=\"Grid\"/> and <see cref=\"Canvas\"/> this function sets some panel properties to make everything work like with other <c>StartX</c> functions.</param>\n\t/// <typeparam name=\"TContainer\">Any <see cref=\"HeaderedContentControl\"/>-based type.</typeparam>\n\t/// <param name=\"header\">Header text/content.</param>\n\tpublic wpfBuilder StartPanel<TContainer>(Panel panel, object header)\n\t\twhere TContainer : HeaderedContentControl, new()\n\t\t=> _Start(_StartPanel(panel), out TContainer _, header);\n\t\n\t_PanelBase _StartPanel(Panel panel) {\n\t\treturn panel switch {\n\t\t\tnull => throw new ArgumentNullException(),\n\t\t\tGrid g => new _Grid(this, g),\n\t\t\tCanvas c => new _Canvas(this, c),\n\t\t\t_ => new _PanelBase(this, panel)\n\t\t};\n\t}\n\t\n\t\n\t/// <summary>\n\t/// Adds right-bottom-aligned horizontal stack panel (<see cref=\"StartStack\"/>) for adding <b>OK</b>, <b>Cancel</b> and more buttons.\n\t/// When don't need more buttons, use just <see cref=\"AddOkCancel\"/>.\n\t/// </summary>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// b.StartOkCancel().AddOkCancel().AddButton(\"Help\", _ => {  }).Width(70).End();\n\t/// ]]></code>\n\t/// </example>\n\tpublic wpfBuilder StartOkCancel() {\n\t\tvar pa = _p;\n\t\tStartStack();\n\t\tif (pa is not _Canvas) {\n\t\t\t_p.panel.HorizontalAlignment = HorizontalAlignment.Right;\n\t\t\t_p.panel.VerticalAlignment = VerticalAlignment.Bottom;\n\t\t\t_p.panel.Margin = new(0, 2, 0, 0);\n\t\t}\n\t\treturn this;\n\t}\n\t\n\t#endregion\n\t\n\t#region util\n\t\n\tbool _IsNested => _p.parent != null;\n\t\n\tbool _IsWindowEnded => _p.ended && _p.parent == null;\n\t\n\tWindow _FindWindow(DependencyObject c) => _window ?? Window.GetWindow(c); //CONSIDER: support top-level HwndSource window\n\t\n\tWindow _GetOrFindWindow() => _window ?? Window.GetWindow(_p.panel);\n\t\n\tvoid _ThrowIfNotWindow([CallerMemberName] string m_ = null) {\n\t\tif (_window == null) throw new InvalidOperationException(m_ + \"(): Container is not Window\");\n\t}\n\t\n\tControl _LastAsControlOrThrow([CallerMemberName] string m_ = null) => (Last as Control) ?? throw new InvalidOperationException(m_ + \"(): Last added element is not Control\");\n\t\n\t_PanelBase _ParentOfLast => ReferenceEquals(Last, _p.panel) ? _p.parent : _p;\n\t\n\tT _ParentOfLastAsOrThrow<T>([CallerMemberName] string m_ = null) where T : _PanelBase {\n\t\treturn _ParentOfLast is T t ? t : throw new InvalidOperationException($\"{m_}() not in {typeof(T).Name[1..]} panel.\");\n\t}\n\t\n\t//\tvoid _ThrowIfParentOfLastIs<TControl>([CallerMemberName] string caller = null) where TControl : Panel {\n\t//\t\tif(Last.Parent is TControl) throw new InvalidOperationException($\"{caller}() in {typeof(TControl).Name} panel.\");\n\t//\t}\n\t\n\tstatic string _UriNormalize(string source) => pathname.normalize(source, flags: PNFlags.CanBeUrlOrShell);\n\t\n\t//static Uri _Uri(string source) => new Uri(_UriNormalize(source));\n\t\n\t/// <summary>\n\t/// Detects whether the application or window is using a theme.\n\t/// </summary>\n\tstatic _Themed _GetThemed(wpfBuilder b = null) {\n\t\tvar a = Application.Current;\n#if NET9_0_OR_GREATER\n\t\tif (a != null && a.ThemeMode != ThemeMode.None) return _Themed.Wpf;\n\t\tif (b?._GetOrFindWindow() is { } w && w.ThemeMode != ThemeMode.None) return _Themed.Wpf;\n#endif\n\t\tif (a != null && a.Resources.MergedDictionaries.Count > 0) return _Themed.Other;\n\t\treturn 0;\n\t}\n\tenum _Themed : byte { None, Wpf, Other }\n\t\n\t_Themed _IsThemed => _isThemed ??= _GetThemed(this);\n\t_Themed? _isThemed;\n\t\n\t/// <summary>\n\t/// Gets or sets a property of an element of any type that has the property.\n\t/// </summary>\n\tstruct _PropGetSet<T> {\n\t\tFrameworkElement _e;\n\t\tPropertyInfo _p;\n\t\t\n\t\tpublic _PropGetSet(FrameworkElement e, string prop, [CallerMemberName] string m_ = null) {\n\t\t\t_e = e;\n\t\t\t_p = e.GetType().GetProperty(prop, typeof(T)) ?? throw new InvalidOperationException(m_ + $\"(): Last added element does not have {prop} property\");\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Like ctor but does not throw.\n\t\t/// </summary>\n\t\t/// <returns><c>false</c> if failed.</returns>\n\t\tpublic static bool TryCreate(FrameworkElement e, string prop, out _PropGetSet<T> r) {\n\t\t\tvar p = e.GetType().GetProperty(prop, typeof(T));\n\t\t\tif (p == null) { r = default; return false; }\n\t\t\tr = new() { _e = e, _p = p };\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\tpublic T Get => (T)_p.GetValue(_e);\n\t\t\n\t\tpublic void Set(T value) { _p.SetValue(_e, value); }\n\t}\n\t\n\t#endregion\n}\n"
  },
  {
    "path": "Au/Input/RegisteredHotkey.cs",
    "content": "namespace Au.More;\n\n/// <summary>\n/// Registers a hotkey using API <ms>RegisterHotKey</ms>. Unregisters when disposing.\n/// </summary>\n/// <remarks>\n/// Can be used as a lightweight alternative to hotkey triggers.\n/// \n/// The variable must be disposed, either explicitly (call <see cref=\"Dispose\"/> or <see cref=\"Unregister\"/>) or with <c>using</c>.\n/// </remarks>\n/// <example>\n/// <code><![CDATA[\n/// using System.Windows;\n/// using System.Windows.Interop;\n/// \n/// new DialogClass().ShowDialog();\n/// \n/// class DialogClass : Window {\n/// \tRegisteredHotkey _hk1, _hk2;\n/// \t\n/// \tpublic DialogClass() {\n/// \t\tTitle = \"Hotkeys\";\n/// \t\tvar b = new wpfBuilder(this).WinSize(250);\n/// \t\tb.R.AddOkCancel();\n/// \t\tb.End();\n/// \t}\n/// \n/// \tprotected override void OnSourceInitialized(EventArgs e) {\n/// \t\tbase.OnSourceInitialized(e);\n/// \t\tvar hs = PresentationSource.FromVisual(this) as HwndSource;\n/// \t\ths.AddHook(_WndProc);\n/// \t\tbool r1 = _hk1.Register(1, \"Ctrl+Alt+F10\", this);\n/// \t\tbool r2 = _hk2.Register(2, (KMod.Ctrl | KMod.Shift, KKey.D), this); //Ctrl+Shift+D\n/// \t\tprint.it(r1, r2);\n/// \t}\n/// \n/// \tprotected override void OnClosed(EventArgs e) {\n/// \t\tbase.OnClosed(e);\n/// \t\t_hk1.Unregister();\n/// \t\t_hk2.Unregister();\n/// \t}\n/// \n/// \tIntPtr _WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) {\n/// \t\tif (msg == RegisteredHotkey.WM_HOTKEY) print.it(wParam);\n/// \n/// \t\treturn default;\n/// \t}\n/// }\n/// ]]></code>\n/// </example>\n/// <seealso cref=\"keys.waitForHotkey(Seconds, KHotkey, bool)\"/>\npublic struct RegisteredHotkey : IDisposable {\n\twnd _w;\n\tint? _id;\n\t\n\t///// <summary>The hotkey.</summary>\n\t//public KHotkey Hotkey { get; private set; }\n\t\n\t/// <summary>\n\t/// Registers a hotkey using API <ms>RegisterHotKey</ms>.\n\t/// </summary>\n\t/// <returns><c>false</c> if failed. Supports <see cref=\"lastError\"/>.</returns>\n\t/// <param name=\"id\">Hotkey id. Must be 0 to 0xBFFF or value returned by API <ms>GlobalAddAtom</ms>. It will be <i>wParam</i> of the <ms>WM_HOTKEY</ms> message.</param>\n\t/// <param name=\"hotkey\">Hotkey. Can be: string like <c>\"Ctrl+Shift+Alt+Win+K\"</c>, tuple <c>(KMod, KKey)</c>, enum <see cref=\"KKey\"/>, enum <c>Keys</c>, struct <see cref=\"KHotkey\"/>.</param>\n\t/// <param name=\"window\">Window/form that will receive the <ms>WM_HOTKEY</ms> message. Must be of this thread. If default, the message must be retrieved in the message loop of this thread.</param>\n\t///\t<param name=\"noRepeat\">Add flag <ms>MOD_NOREPEAT</ms>.</param>\n\t/// <exception cref=\"ArgumentException\">Error in hotkey string.</exception>\n\t/// <exception cref=\"InvalidOperationException\">This variable already registered a hotkey.</exception>\n\t/// <remarks>\n\t/// Fails if the hotkey is currently registered by this or another application or used by Windows. Also if <c>F12</c>.\n\t/// <note>Most single-key and <c>Shift+key</c> hotkeys don't work when the active window has higher UAC integrity level (eg admin) than this process. Media keys may work.</note>\n\t/// A single variable cannot register multiple hotkeys simultaneously. Use multiple variables, for example array.\n\t/// </remarks>\n\t/// <seealso cref=\"keys.waitForHotkey\"/>\n\tpublic bool Register(int id, [ParamString(PSFormat.Hotkey)] KHotkey hotkey, AnyWnd window = default, bool noRepeat = false) {\n\t\tif (_id != null) throw new InvalidOperationException(\"This variable already registered a hotkey. Use multiple variables or call Unregister.\");\n\t\tvar w = window.Hwnd;\n\t\tvar (mod, key) = Normalize_(hotkey);\n\t\tif (noRepeat) mod |= Api.MOD_NOREPEAT;\n\t\tif (!Api.RegisterHotKey(w, id, mod, key)) return false;\n\t\t_w = w; _id = id;\n\t\t//Hotkey = hotkey;\n\t\treturn true;\n\t}\n\t\n\tinternal static (uint mod, KKey key) Normalize_(KHotkey hotkey) {\n\t\tvar (mod, key) = hotkey;\n\t\tif (key == KKey.Pause && mod.Has(KMod.Ctrl)) key = KKey.Break;\n\t\t//if(key == KKey.NumPad5 && mod.Has(KMod.Shift)) key = KKey.Clear; //Shift+numpad don't work\n\t\treturn (Math2.SwapBits((uint)mod, 0, 2, 1), key);\n\t}\n\t\n\t/// <summary>\n\t/// Unregisters the hotkey.\n\t/// </summary>\n\t/// <remarks>\n\t/// Called implicitly when disposing this variable.\n\t/// Must be called from the same thread as when registering, and the window must be still alive.\n\t/// If fails, calls <see cref=\"print.warning\"/>.\n\t/// </remarks>\n\tpublic void Unregister() {\n\t\tif (_id != null) {\n\t\t\tif (!Api.UnregisterHotKey(_w, _id.Value)) {\n\t\t\t\tvar es = lastError.message;\n\t\t\t\tprint.warning($\"Failed to unregister hotkey, id={_id.Value}. {es}\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t_id = null;\n\t\t\t_w = default;\n\t\t\t//Hotkey = default;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Calls <see cref=\"Unregister\"/>.\n\t/// </summary>\n\tpublic void Dispose() => Unregister();\n\t\n\t//~RegisteredHotkey() => Unregister(); //makes no sense. Called from wrong thread and when the window is already destroyed.\n\t\n\t/// <summary>\n\t/// This message is posted to the window or to the thread's message loop.\n\t/// More info: <ms>WM_HOTKEY</ms>.\n\t/// </summary>\n\tpublic const int WM_HOTKEY = Api.WM_HOTKEY;\n}\n"
  },
  {
    "path": "Au/Input/clipboard.cs",
    "content": "namespace Au;\n\n/// <summary>\n/// Clipboard functions. Copy, paste, get and set clipboard text and other data.\n/// </summary>\n/// <remarks>\n/// This class is similar to the .NET <see cref=\"System.Windows.Forms.Clipboard\"/> class, which uses OLE API, works only in STA threads and does not work well in automation scripts. This class uses non-OLE API and works well in automation scripts and any threads.\n/// \n/// To set/get clipboard data of non-text formats, use class <see cref=\"clipboardData\"/>; to paste, use it with <see cref=\"pasteData\"/>; to copy (get from the active app), use it with <see cref=\"copyData{T}(Func{T}, bool, OKey, KHotkey, int)\"/>.\n/// \n/// Don't copy/paste in windows of own thread. Call it from another thread. Example in <see cref=\"keys.send\"/>.\n/// </remarks>\npublic static class clipboard {\n\t/// <summary>\n\t/// Clears the clipboard.\n\t/// </summary>\n\t/// <exception cref=\"AuException\">Failed to open clipboard (after 10 s of wait/retry).</exception>\n\tpublic static void clear() {\n\t\tusing (new OpenClipboard_(false)) EmptyClipboard_();\n\t}\n\t\n\tinternal static void EmptyClipboard_() {\n\t\tif (!Api.EmptyClipboard()) Debug.Assert(false);\n\t}\n\t\n\t/// <summary>\n\t/// Gets or sets clipboard text.\n\t/// </summary>\n\t/// <value><c>null</c> if there is no text.</value>\n\t/// <exception cref=\"AuException\">Failed to open clipboard (after 10 s of wait/retry) or set clipboard data.</exception>\n\t/// <exception cref=\"OutOfMemoryException\">The <c>set</c> function failed to allocate memory.</exception>\n\t/// <remarks>\n\t/// The <c>get</c> function calls <see cref=\"clipboardData.getText\"/>.\n\t/// \n\t/// Gets/sets only data of text format. For other formats (files, HTML, image, etc) use <see cref=\"clipboardData\"/> class.\n\t/// </remarks>\n\tpublic static string text {\n\t\tget => clipboardData.getText();\n\t\tset {\n\t\t\tusing (new OpenClipboard_(true)) {\n\t\t\t\tEmptyClipboard_();\n\t\t\t\tif (value != null) clipboardData.SetText_(value);\n\t\t\t}\n\t\t}\n\t}\n\t\n\t//Sets text (string) or multi-format data (Data). Clipboard must be open.\n\tstatic void _SetClipboard(object data, bool renderLater) {\n\t\tswitch (data) {\n\t\tcase clipboardData d:\n\t\t\td.SetOpenClipboard(renderLater);\n\t\t\tbreak;\n\t\tcase string s:\n\t\t\tif (renderLater) Api.SetClipboardData(Api.CF_UNICODETEXT, default);\n\t\t\telse clipboardData.SetText_(s);\n\t\t\tbreak;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Calls API <c>SetClipboardData(\"Clipboard Viewer Ignore\")</c>. Clipboard must be open.\n\t/// Then clipboard manager/viewer/etc programs that are aware of this convention don't try to get our clipboard data while we are pasting.\n\t/// Tested apps that support it: Ditto, Clipdiary. Other 5 tested apps don't. Windows 10 Clipboard History doesn't.\n\t/// </summary>\n\tstatic void _SetClipboardData_ClipboardViewerIgnore() {\n\t\tApi.SetClipboardData(ClipFormats.ClipboardViewerIgnore, Api.GlobalAlloc(Api.GMEM_MOVEABLE | Api.GMEM_ZEROINIT, 1));\n\t\t//tested: hMem cannot be default(IntPtr) or 0 bytes.\n\t}\n\t\n\t/// <summary>\n\t/// Gets the selected text from the focused app using the clipboard.\n\t/// </summary>\n\t/// <param name=\"cut\">Use <c>Ctrl+X</c>.</param>\n\t/// <param name=\"options\">\n\t/// Options. If <c>null</c> (default), uses <see cref=\"opt.key\"/>.\n\t/// Uses <see cref=\"OKey.RestoreClipboard\"/>, <see cref=\"OKey.KeySpeedClipboard\"/>, <see cref=\"OKey.NoBlockInput\"/>. Does not use <see cref=\"OKey.Hook\"/>.\n\t/// </param>\n\t/// <param name=\"hotkey\">Keys to use instead of <c>Ctrl+C</c> or <c>Ctrl+X</c>. Example: <c>hotkey: \"Ctrl+Shift+C\"</c>. Overrides <i>cut</i>.</param>\n\t/// <param name=\"timeoutMS\">Max time to wait until the focused app sets clipboard data, in milliseconds. If 0 (default), the timeout is 3000 ms. The function waits up to 10 times longer if the window is hung.</param>\n\t/// <exception cref=\"AuException\">Failed. Fails if there is no focused window or if it does not set clipboard data.</exception>\n\t/// <exception cref=\"InputDesktopException\"></exception>\n\t/// <remarks>\n\t/// Also can get file paths, as multiline text.\n\t/// \n\t/// Sends keys <c>Ctrl+C</c>, waits until the focused app sets clipboard data, gets it, finally restores clipboard data.\n\t/// Fails if the focused app does not set clipboard text or file paths, for example if there is no selected text/files.\n\t/// Works with console windows too, even if they don't support <c>Ctrl+C</c>.\n\t/// </remarks>\n\tpublic static string copy(bool cut = false, OKey options = null, [ParamString(PSFormat.Hotkey)] KHotkey hotkey = default, int timeoutMS = 0) {\n\t\treturn _Copy(cut, hotkey, options, timeoutMS);\n\t\t//rejected: 'format' parameter. Not useful.\n\t}\n\t\n\t/// <summary>\n\t/// Calls <see cref=\"copy\"/> and handles exceptions.\n\t/// </summary>\n\t/// <returns>Returns <c>false</c> if failed.</returns>\n\t/// <param name=\"text\">Receives the copied text.</param>\n\t/// <param name=\"timeout\">Max time to wait until the focused app sets clipboard data, in milliseconds. The function waits up to 10 times longer if the window is hung.</param>\n\t/// <param name=\"warning\">Call <see cref=\"print.warning\"/>.</param>\n\t/// <param name=\"osd\">Call <see cref=\"osdText.showTransparentText\"/> with text <c>\"Failed to copy text\"</c>.</param>\n\t/// <inheritdoc cref=\"copy\" path=\"//param|//remarks\"/>\n\tpublic static bool tryCopy(out string text, int timeout, bool warning = false, bool osd = false, OKey options = null, [ParamString(PSFormat.Hotkey)] KHotkey hotkey = default) {\n\t\ttry {\n\t\t\ttext = _Copy(false, hotkey, options, Math.Max(timeout, 25));\n\t\t\treturn true;\n\t\t}\n\t\tcatch (Exception e1) {\n\t\t\tif (warning) print.warning(e1);\n\t\t\tif (osd) osdText.showTransparentText(\"Failed to copy text\");\n\t\t\ttext = null;\n\t\t\treturn false;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Calls <see cref=\"copy\"/> and handles exceptions.\n\t/// </summary>\n\t/// <returns>Returns <c>false</c> if failed.</returns>\n\t/// <param name=\"text\">Receives the copied text.</param>\n\t/// <param name=\"warning\">Call <see cref=\"print.warning\"/>. Default <c>true</c>.</param>\n\t/// <param name=\"osd\">Call <see cref=\"osdText.showTransparentText\"/> with text <c>\"Failed to copy text\"</c>. Default <c>true</c>.</param>\n\t/// <inheritdoc cref=\"copy\" path=\"//param|//remarks\"/>\n\t[EditorBrowsable(EditorBrowsableState.Never)] //obsolete. Not optimal parameters.\n\tpublic static bool tryCopy(out string text, bool cut = false, OKey options = null, bool warning = true, bool osd = true, [ParamString(PSFormat.Hotkey)] KHotkey hotkey = default, int timeoutMS = 0) {\n\t\ttry {\n\t\t\ttext = _Copy(cut, hotkey, options, timeoutMS);\n\t\t\treturn true;\n\t\t}\n\t\tcatch (Exception e1) {\n\t\t\tif (warning) print.warning(e1);\n\t\t\tif (osd) osdText.showTransparentText(\"Failed to copy text\");\n\t\t\ttext = null;\n\t\t\treturn false;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Gets data of any formats from the focused app using the clipboard and a callback function.\n\t/// </summary>\n\t/// <returns>The return value of <i>callback</i>.</returns>\n\t/// <param name=\"callback\">Callback function. It can get clipboard data of any formats. It can use any clipboard functions, for example <see cref=\"clipboardData\"/> or <see cref=\"System.Windows.Forms.Clipboard\"/>. Don't call copy/paste functions.</param>\n\t/// <remarks>\n\t/// Sends keys <c>Ctrl+C</c>, waits until the focused app sets clipboard data, calls the callback function that gets it, finally restores clipboard data.\n\t/// Fails if the focused app does not set clipboard data.\n\t/// Works with console windows too, even if they don't support <c>Ctrl+C</c>.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// var image = clipboard.copyData(() => clipboardData.getImage());\n\t/// \n\t/// var (text, files) = clipboard.copyData(() => (clipboardData.getText(), clipboardData.getFiles()));\n\t/// ]]></code>\n\t/// </example>\n\t/// <inheritdoc cref=\"copy\"/>\n\tpublic static T copyData<T>(Func<T> callback, bool cut = false, OKey options = null, [ParamString(PSFormat.Hotkey)] KHotkey hotkey = default, int timeoutMS = 0) {\n\t\tNot_.Null(callback);\n\t\tT r = default;\n\t\t_Copy(cut, hotkey, options, timeoutMS, () => { r = callback(); });\n\t\treturn r;\n\t}\n\t\n\t///\n\t[EditorBrowsable(EditorBrowsableState.Never)] //obsolete\n\tpublic static void copyData(Action callback, bool cut = false, OKey options = null, KHotkey hotkey = default) {\n\t\tNot_.Null(callback);\n\t\t_Copy(cut, hotkey, options, 0, callback);\n\t}\n\t\n\tstatic string _Copy(bool cut, KHotkey hotkey, OKey options, int timeoutMS, Action callback = null) {\n\t\tstring R = null;\n\t\tvar optk = options ?? opt.key;\n\t\tbool restore = optk.RestoreClipboard;\n\t\t_ClipboardListener listener = null;\n\t\t_DisableClipboardHistory disableCH = default;\n\t\tvar bi = new inputBlocker() { ResendBlockedKeys = true };\n\t\tvar oc = new OpenClipboard_(createOwner: true, noOpenNow: !restore);\n\t\ttry {\n\t\t\tif (!optk.NoBlockInput) bi.Start(BIEvents.Keys);\n\t\t\tkeys.Internal_.ReleaseModAndDisableModMenu();\n\t\t\t\n\t\t\tdisableCH.Disable(); //fast\n\t\t\t\n\t\t\tvar save = new _SaveRestore();\n\t\t\tif (restore) {\n\t\t\t\tsave.Save();\n\t\t\t\toc.Close(false); //close clipboard; don't destroy our clipboard owner window\n\t\t\t}\n\t\t\t\n\t\t\twnd wFocus = keys.Internal_.GetWndFocusedOrActive(requireFocus: true);\n\t\t\tlistener = new _ClipboardListener(false, null, oc.WndClipOwner, wFocus);\n\t\t\t\n\t\t\tif (!Api.AddClipboardFormatListener(oc.WndClipOwner)) throw new AuException();\n\t\t\tvar ctrlC = new keys.Internal_.SendCopyPaste();\n\t\t\ttry {\n\t\t\t\tif (wFocus.IsConsole) {\n\t\t\t\t\twFocus.Post(Api.WM_SYSCOMMAND, 65520);\n\t\t\t\t\t//system menu &Edit > &Copy; tested on all OS; Windows 10 supports Ctrl+C, but it may be disabled.\n\t\t\t\t} else {\n\t\t\t\t\tif (hotkey.Key == 0) hotkey = new(KMod.Ctrl, cut ? KKey.X : KKey.C);\n\t\t\t\t\tctrlC.Press(hotkey, optk, wFocus);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t//wait until the app sets clipboard text\n\t\t\t\tlistener.Wait(ref ctrlC, timeoutMS);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tctrlC.Release();\n\t\t\t\tApi.RemoveClipboardFormatListener(oc.WndClipOwner);\n\t\t\t}\n\t\t\t\n\t\t\twFocus.SendTimeout(500, out _, 0); //workaround: in SharpDevelop and ILSpy (both WPF), API GetClipboardData takes ~1 s. Need to sleep min 10 ms or send message.\n\t\t\t\n\t\t\tif (callback != null) {\n\t\t\t\tcallback();\n\t\t\t\tif (restore) oc.Reopen();\n\t\t\t} else {\n\t\t\t\toc.Reopen();\n\t\t\t\tR = clipboardData.GetText_(0);\n\t\t\t}\n\t\t\t\n\t\t\tif (restore) save.Restore();\n\t\t}\n\t\tfinally {\n\t\t\toc.Dispose();\n\t\t\tbi.Dispose();\n\t\t\tdisableCH.Restore();\n\t\t}\n\t\tGC.KeepAlive(listener);\n\t\tif (R == null && callback == null) throw new AuException(\"*copy text\"); //no text in the clipboard. Probably not a text control; if text control but empty selection, usually throws in Wait, not here, because the target app then does not use the clipboard.\n\t\treturn R;\n\t}\n\t\n\t/// <summary>\n\t/// Pastes text or HTML into the focused app using the clipboard.\n\t/// </summary>\n\t/// <param name=\"text\">Text. Can be <c>null</c> if <i>html</i> used.</param>\n\t/// <param name=\"html\">\n\t/// HTML. Can be full HTML or fragment. See <see cref=\"clipboardData.AddHtml\"/>. Can be <c>null</c>.\n\t/// Can be specified only <i>text</i> or only <i>html</i> or both. If both, will paste <i>html</i> in apps that support it, elsewhere <i>text</i>. If only <i>html</i>, in apps that don't support HTML will paste <i>html</i> as text.\n\t/// </param>\n\t/// <param name=\"options\">\n\t/// Options. If <c>null</c> (default), uses <see cref=\"opt.key\"/>.\n\t/// Uses <see cref=\"OKey.PasteSleep\"/>, <see cref=\"OKey.RestoreClipboard\"/>, <see cref=\"OKey.PasteWorkaround\"/>, <see cref=\"OKey.KeySpeedClipboard\"/>, <see cref=\"OKey.NoBlockInput\"/>, <see cref=\"OKey.Hook\"/>.\n\t/// </param>\n\t/// <param name=\"hotkey\">Keys to use instead of <c>Ctrl+V</c>. Example: <c>hotkey: \"Ctrl+Shift+V\"</c>.</param>\n\t/// <param name=\"timeoutMS\">Max time to wait until the focused app gets clipboard data, in milliseconds. If 0 (default), the timeout is 3000 ms. The function waits up to 10 times longer if the window is hung.</param>\n\t/// <exception cref=\"AuException\">Failed. Fails if there is no focused window or if it does not get clipboard data.</exception>\n\t/// <exception cref=\"InputDesktopException\"></exception>\n\t/// <remarks>\n\t/// Sets clipboard data, sends keys <c>Ctrl+V</c>, waits until the focused app gets clipboard data, finally restores clipboard data.\n\t/// Fails if nothing gets clipboard data in several seconds.\n\t/// Works with console windows too, even if they don't support <c>Ctrl+V</c>.\n\t/// \n\t/// A clipboard viewer/manager program can make this function slower and less reliable, unless it supports <see cref=\"ClipFormats.ClipboardViewerIgnore\"/> or gets clipboard data with a delay.\n\t/// Possible problems with some virtual PC programs. Either pasting does not work in their windows, or they use a hidden clipboard viewer that makes this function slower and less reliable.\n\t/// </remarks>\n\t/// <seealso cref=\"keys.sendt\"/>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// clipboard.paste(\"Example\\r\\n\");\n\t/// ]]></code>\n\t/// </example>\n\tpublic static void paste(string text, string html = null, OKey options = null, [ParamString(PSFormat.Hotkey)] KHotkey hotkey = default, int timeoutMS = 0) {\n\t\tif (text.NE() && html.NE()) return;\n\t\tobject data = text;\n\t\tif (html != null) data = new clipboardData().AddHtml(html).AddText(text ?? html);\n\t\t_Paste(data, options, hotkey, timeoutMS);\n\t}\n\t//problem: fails to paste in VMware player. Could add an option to not sync, but fails anyway because VMware gets clipboard with a big delay.\n\t//TODO2: add a tip here in doc or in cookbook: To create HTML you can use an online HTML editor, for example https://html-online.com/.\n\t\n\t/// <summary>\n\t/// Calls <see cref=\"paste\"/> and handles exceptions.\n\t/// </summary>\n\t/// <returns>Returns <c>false</c> if failed.</returns>\n\t/// <param name=\"timeout\">Max time to wait until the focused app gets clipboard data, in milliseconds. The function waits up to 10 times longer if the window is hung.</param>\n\t/// <param name=\"warning\">Call <see cref=\"print.warning\"/>.</param>\n\t/// <param name=\"osd\">Call <see cref=\"osdText.showTransparentText\"/> with text <c>\"Failed to paste text\"</c>.</param>\n\t/// <inheritdoc cref=\"paste\" path=\"//param|//remarks\"/>\n\tpublic static bool tryPaste(string text, int timeout, bool warning = false, bool osd = false, string html = null, OKey options = null, [ParamString(PSFormat.Hotkey)] KHotkey hotkey = default) {\n\t\ttry {\n\t\t\tpaste(text, html, options, hotkey, Math.Max(timeout, 25));\n\t\t\treturn true;\n\t\t}\n\t\tcatch (Exception e1) {\n\t\t\tif (warning) print.warning(e1);\n\t\t\tif (osd) osdText.showTransparentText(\"Failed to paste text\");\n\t\t\treturn false;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Calls <see cref=\"paste\"/> and handles exceptions.\n\t/// </summary>\n\t/// <returns>Returns <c>false</c> if failed.</returns>\n\t/// <param name=\"warning\">Call <see cref=\"print.warning\"/>. Default <c>true</c>.</param>\n\t/// <param name=\"osd\">Call <see cref=\"osdText.showTransparentText\"/> with text <c>\"Failed to paste text\"</c>. Default <c>true</c>.</param>\n\t/// <inheritdoc cref=\"paste\" path=\"//param|//remarks\"/>\n\t[EditorBrowsable(EditorBrowsableState.Never)] //obsolete. Not optimal parameters.\n\tpublic static bool tryPaste(string text, string html = null, OKey options = null, bool warning = true, bool osd = true, [ParamString(PSFormat.Hotkey)] KHotkey hotkey = default, int timeoutMS = 0) {\n\t\ttry {\n\t\t\tpaste(text, html, options, hotkey, timeoutMS);\n\t\t\treturn true;\n\t\t}\n\t\tcatch (Exception e1) {\n\t\t\tif (warning) print.warning(e1);\n\t\t\tif (osd) osdText.showTransparentText(\"Failed to paste text\");\n\t\t\treturn false;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Pastes data added to a <see cref=\"clipboardData\"/> variable into the focused app using the clipboard.\n\t/// </summary>\n\t/// <example>\n\t/// Paste data of two formats: HTML and text.\n\t/// <code><![CDATA[\n\t/// clipboard.pasteData(new clipboardData().AddHtml(\"<b>text</b>\").AddText(\"text\"));\n\t/// ]]></code>\n\t/// </example>\n\t/// <inheritdoc cref=\"paste\"/>\n\tpublic static void pasteData(clipboardData data, OKey options = null, [ParamString(PSFormat.Hotkey)] KHotkey hotkey = default, int timeoutMS = 0) {\n\t\tNot_.Null(data);\n\t\t_Paste(data, options, hotkey, timeoutMS);\n\t}\n\t\n\t//rejected. Should use some UI-created/saved data containing all three formats.\n\t//public static void pasteRichText(string text, string rtf, string html = null, OKey options = null)\n\t//{\n\t//\tvar a = new List<(int, object)>();\n\t//\tif(!text.NE()) a.Add((0, text));\n\t//\tif(!rtf.NE()) a.Add((Lib.RtfFormat, rtf));\n\t//\tif(!html.NE()) a.Add((Lib.HtmlFormat, html));\n\t//\tif(a.Count == 0) return;\n\t//\t_Paste(a, options);\n\t//}\n\t\n\tstatic void _Paste(object data, OKey options, KHotkey hotkey, int timeoutMS) {\n\t\tvar wFocus = keys.Internal_.GetWndFocusedOrActive(requireFocus: true);\n\t\tvar optk = options ?? opt.key;\n\t\tusing (var bi = new inputBlocker { ResendBlockedKeys = true }) {\n\t\t\tif (!optk.NoBlockInput) bi.Start(BIEvents.Keys);\n\t\t\tkeys.Internal_.ReleaseModAndDisableModMenu();\n\t\t\toptk = optk.GetHookOptionsOrThis_(wFocus);\n\t\t\tPaste_(data, optk, wFocus, hotkey, timeoutMS);\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Used by <see cref=\"clipboard\"/> and <see cref=\"keys\"/>.\n\t/// The caller should block user input (if need), release modifier keys, get <i>optk</i>/<i>wFocus</i>, sleep finally (if need).\n\t/// </summary>\n\t/// <param name=\"data\">string or <see cref=\"clipboardData\"/>.</param>\n\tinternal static void Paste_(object data, OKey optk, wnd wFocus, KHotkey hotkey = default, int timeoutMS = 0) {\n\t\tbool isConsole = wFocus.IsConsole;\n\t\tList<KKey> andKeys = null;\n\t\t\n\t\tif (optk.PasteWorkaround && data is string s && !isConsole) {\n\t\t\tvar s2 = s.TrimEnd(\"\\r\\n\\t \");\n\t\t\tif (s2 != s) {\n\t\t\t\tandKeys = new List<KKey>();\n\t\t\t\tfor (int i = s2.Length; i < s.Length; i++) {\n\t\t\t\t\tchar ch = s[i];\n\t\t\t\t\tif (ch == '\\n') { if (i > 0 && s[i - 1] == '\\r') continue; ch = '\\r'; }\n\t\t\t\t\tandKeys.Add((KKey)ch);\n\t\t\t\t}\n\t\t\t\tif (s2.Length == 0) {\n\t\t\t\t\tkeys.Internal_.SendCopyPaste.AndSendKeys(andKeys, optk);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tdata = s2;\n\t\t\t}\n\t\t}\n\t\t\n\t\tbool sync = true; //CONSIDER: option to turn off, depending on window.\n\t\t_ClipboardListener listener = null;\n\t\t_DisableClipboardHistory disableCH = default;\n\t\tvar oc = new OpenClipboard_(true);\n\t\tbool restore = optk.RestoreClipboard;\n\t\t_SaveRestore save = default;\n\t\ttry {\n\t\t\tdisableCH.Disable(); //fast\n\t\t\t\n\t\t\tif (restore) save.Save();\n\t\t\t\n\t\t\tEmptyClipboard_();\n\t\t\t_SetClipboardData_ClipboardViewerIgnore();\n\t\t\t_SetClipboard(data, renderLater: sync);\n\t\t\toc.Close(false); //close clipboard; don't destroy our clipboard owner window\n\t\t\tif (sync) listener = new _ClipboardListener(true, data, oc.WndClipOwner, wFocus);\n\t\t\t//info:\n\t\t\t//\toc ctor creates a temporary message-only clipboard owner window. Its wndproc initially is DefWindowProc.\n\t\t\t//\tlistener ctor subclasses it. Its wndproc receives WM_RENDERFORMAT which sets clipboard data etc.\n\t\t\t\n\t\t\t//PasteSync_?.Start(new(wFocus, optk, data));\n\t\t\t\n\t\t\tvar ctrlV = new keys.Internal_.SendCopyPaste();\n\t\t\ttry {\n\t\t\t\tif (isConsole) {\n\t\t\t\t\twFocus.Post(Api.WM_SYSCOMMAND, 65521);\n\t\t\t\t\t//system menu &Edit > &Paste; tested on all OS; Windows 10 supports Ctrl+V, but it can be disabled.\n\t\t\t\t} else {\n\t\t\t\t\tif (hotkey.Key == 0) hotkey = new(KMod.Ctrl, KKey.V);\n\t\t\t\t\tctrlV.Press(hotkey, optk, wFocus, andKeys: andKeys);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t//wait until the app gets clipboard data\n\t\t\t\tif (sync) {\n\t\t\t\t\tlistener.Wait(ref ctrlV, timeoutMS);\n\t\t\t\t\tif (listener.FailedToSetData != null) throw new AuException(listener.FailedToSetData.Message);\n\t\t\t\t\tif (listener.IsBadWindow) sync = false;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tkeys.Internal_.Sleep(optk.KeySpeedClipboard);\n\t\t\t\t//rejected: subtract the time already waited in listener.Wait.\n\t\t\t\t//never mind: if too long, in some apps may autorepeat, eg BlueStacks after 500 ms. Rare.\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tctrlV.Release();\n\t\t\t}\n\t\t\t\n\t\t\twFocus.SendTimeout(1000, out _, 0, flags: 0); //can help with some apps, but many apps eg browsers are async\n\t\t\t\n\t\t\tint sleep = optk.PasteSleep; if (!sync && sleep < 500) sleep = (sleep + 500) / 2;\n\t\t\tkeys.Internal_.Sleep(sleep);\n\t\t\t\n\t\t\t//PasteSync_?.End();\n\t\t}\n\t\tfinally {\n\t\t\tif (restore && save.IsSaved) {\n\t\t\t\tif (oc.Reopen(true)) save.Restore();\n\t\t\t}\n\t\t\toc.Dispose();\n\t\t\tdisableCH.Restore();\n\t\t}\n\t\tGC.KeepAlive(listener);\n\t}\n\t\n\t//internal static IPasteSync_ PasteSync_ { get; set; }\n\t//internal interface IPasteSync_ {\n\t//\tvoid Start(PasteSyncArgs_ p);\n\t//\tvoid End();\n\t//}\n\t//internal record class PasteSyncArgs_(wnd wFocus, OKey optk, object data);\n\t\n\t/// <summary>\n\t/// Waits until the target app gets (when pasting) or sets (when copying) clipboard text.\n\t/// For it subclasses our clipboard owner window and uses clipboard messages. Does not unsubclass.\n\t/// </summary>\n\tclass _ClipboardListener : WaitVariable_ {\n\t\tbool _paste; //true if used for paste, false if for copy\n\t\tobject _data; //string or Data. null if !_paste.\n\t\tWNDPROC _wndProc;\n\t\t//wnd _wPrevClipViewer;\n\t\twnd _wFocus;\n\t\t\n\t\t/// <summary>\n\t\t/// The clipboard message has been received. Probably the target window responded to the <c>Ctrl+C</c> or <c>Ctrl+V</c>.\n\t\t/// When pasting, it is unreliable because of clipboard viewers/managers/etc. The caller also must check <c>IsBadWindow</c>.\n\t\t/// </summary>\n\t\tpublic bool Success => waitVar;\n\t\t\n\t\t/// <summary>\n\t\t/// When pasting, <c>true</c> if probably not the target process retrieved clipboard data. Probably a clipboard viewer/manager/etc.\n\t\t/// Not used when copying.\n\t\t/// </summary>\n\t\tpublic bool IsBadWindow;\n\t\t\n\t\t/// <summary>\n\t\t/// Exception thrown/caught when failed to set clipboard data.\n\t\t/// </summary>\n\t\tpublic Exception FailedToSetData;\n\t\t\n\t\t/// <summary>\n\t\t/// Subclasses <i>clipOwner</i>.\n\t\t/// </summary>\n\t\t/// <param name=\"paste\"><c>true</c> if used for paste, <c>false</c> if for copy.</param>\n\t\t/// <param name=\"data\">If used for paste, can be string containing Unicode text or <c>int</c>/<c>string</c> dictionary containing clipboard format/data.</param>\n\t\t/// <param name=\"clipOwner\">Our clipboard owner window.</param>\n\t\t/// <param name=\"wFocus\">The target control or window.</param>\n\t\tpublic _ClipboardListener(bool paste, object data, wnd clipOwner, wnd wFocus) {\n\t\t\t_paste = paste;\n\t\t\t_data = data;\n\t\t\t_wndProc = _WndProc;\n\t\t\t_wFocus = wFocus;\n\t\t\tWndUtil.SubclassUnsafe_(clipOwner, _wndProc);\n\t\t\t\n\t\t\t//rejected: use SetClipboardViewer to block clipboard managers/viewers/etc. This was used in QM2.\n\t\t\t//\tNowadays most such programs don't use SetClipboardViewer. They use AddClipboardFormatListener+WM_CLIPBOARDUPDATE.\n\t\t\t//\tknown apps that have clipboard viewer installed with SetClipboardViewer:\n\t\t\t//\t\tOpenOffice, LibreOffice: tested Writer, Calc.\n\t\t\t//\t\tVLC: after first Paste.\n\t\t\t//_wPrevClipViewer = Api.SetClipboardViewer(clipOwner);\n\t\t\t//print.it(_wPrevClipViewer);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Waits until the target app gets (when pasting) or sets (when copying) clipboard text.\n\t\t/// Throws <see cref=\"AuException\"/> on timeout (default 3 s normally, 28 s if the target window is hung).\n\t\t/// </summary>\n\t\t/// <param name=\"ctrlKey\">The variable that was used to send <c>Ctrl+V</c> or <c>Ctrl+C</c>. This function may call <c>Release</c> to avoid too long <c>Ctrl</c> down.</param>\n\t\tpublic void Wait(ref keys.Internal_.SendCopyPaste ctrlKey, int timeoutMS = 0) {\n\t\t\t//print.it(Success); //on Paste often already true, because SendInput dispatches sent messages\n\t\t\tint n = 6, t = 500; //max 3 s (6*500 ms). If hung, max 28 s.\n\t\t\tif (timeoutMS > 0) { n = (timeoutMS + 500 - 1) / 500; t = timeoutMS / n; }\n\t\t\twhile (!Success) {\n\t\t\t\twait.Wait_(t, WHFlags.DoEvents, stopVar: this);\n\t\t\t\tif (Success) break;\n\t\t\t\tif (--n == 0) throw new AuException(_paste ? \"*paste\" : \"*copy\");\n\t\t\t\tctrlKey.Release();\n\t\t\t\t//is hung?\n\t\t\t\t_wFocus.SendTimeout(t * 10, out _, 0, flags: 0);\n\t\t\t}\n\t\t}\n\t\t\n\t\tnint _WndProc(wnd w, int message, nint wParam, nint lParam) {\n\t\t\t//WndUtil.PrintMsg(w, message, wParam, lParam);\n\t\t\t\n\t\t\tswitch (message) {\n\t\t\t//case Api.WM_DESTROY:\n\t\t\t//\tApi.ChangeClipboardChain(w, _wPrevClipViewer);\n\t\t\t//\tbreak;\n\t\t\tcase Api.WM_RENDERFORMAT:\n\t\t\t\tif (_paste && !Success) {\n\t\t\t\t\tIsBadWindow = !_IsTargetWindow();\n\t\t\t\t\t\n\t\t\t\t\t//note: need to set clipboard data even if bad window.\n\t\t\t\t\t//\tElse the clipboard program may retry in loop. Eg Ditto. Then often pasting fails.\n\t\t\t\t\t//\tIf IsBadWindow, we'll then sleep briefly.\n\t\t\t\t\t//\tGood clipboard programs get clipboard data with a delay. Therefore usually they don't interfere, unless the target app is very slow.\n\t\t\t\t\t//\t\tEg Windows Clipboard History 200 ms. Eg Ditto default is 100 ms and can be changed.\n\t\t\t\t\t//\tAlso, after setting clipboard data we cannot wait for good window, because we'll not receive second WM_RENDERFORMAT.\n\t\t\t\t\t\n\t\t\t\t\ttry { _SetClipboard(_data, false); }\n\t\t\t\t\tcatch (Exception ex) { FailedToSetData = ex; } //cannot throw in wndproc, will throw later\n\t\t\t\t\twaitVar = true;\n\t\t\t\t}\n\t\t\t\treturn 0;\n\t\t\tcase Api.WM_CLIPBOARDUPDATE:\n\t\t\t\t//posted, not sent. Once, not for each format. Added in WinVista. QM2 used SetClipboardViewer/WM_DRAWCLIPBOARD.\n\t\t\t\tif (!_paste) waitVar = true;\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\t\n\t\t\treturn Api.DefWindowProc(w, message, wParam, lParam);\n\t\t\t\n\t\t\t//Returns false if probably not the target app reads from the clipboard. Probably a clipboard viewer/manager/etc.\n\t\t\tbool _IsTargetWindow() {\n\t\t\t\twnd wOC = Api.GetOpenClipboardWindow();\n\t\t\t\t\n\t\t\t\t//int color = 0; if(wOC != _wFocus) color = wOC.ProcessId == _wFocus.ProcessId ? 0xFF0000 : 0xFF;\n\t\t\t\t//print.it($\"<><c {color}>{wOC}</c>\");\n\t\t\t\t\n\t\t\t\tif (wOC == _wFocus) return true;\n\t\t\t\tif (wOC.Is0) return true; //tested: none of tested apps calls OpenClipboard(0)\n\t\t\t\tif (wOC.ProcessId == _wFocus.ProcessId) return true; //often classnamed \"CLIPBRDWNDCLASS\". Some clipboard managers too, eg Ditto.\n\t\t\t\tif (osVersion.minWin10 && 0 != _wFocus.Window.IsUwpApp) {\n\t\t\t\t\tvar prog = wOC.ProgramName;\n\t\t\t\t\tif (prog.Eqi(\"svchost.exe\")) return true;\n\t\t\t\t\t//if (prog.Eqi(\"RuntimeBroker.exe\")) return true; //used to be Store apps\n\t\t\t\t\t//tested: no problems on Win8.1\n\t\t\t\t}\n\t\t\t\t//tested: WinUI3 (cn \"WinUIDesktopWin32WindowClass\"): wOC != _wFocus, but same process.\n\t\t\t\t\n\t\t\t\t//CONSIDER: option to return true for user-known windows, eg using a callback. Print warning that includes wOC info.\n\t\t\t\t\n\t\t\t\tDebug_.Print(wOC.ToString());\n\t\t\t\treturn false;\n\t\t\t\t\n\t\t\t\t//BlueStacks problems:\n\t\t\t\t//\tUses an aggressive viewer. Always debugprints while it is running, even when other apps are active.\n\t\t\t\t//\tSometimes pastes old text, usually after starting BlueStacks or after some time of not using it.\n\t\t\t\t//\t\tWith or without clipboard restoring.\n\t\t\t\t//\t\tThen starts to work correctly always. Difficult to debug.\n\t\t\t\t//\t\tKeySpeedClipboard=100 usually helps, but sometimes even 300 does not help.\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Opens and closes clipboard using API <c>OpenClipboard</c> and <c>CloseClipboard</c>.\n\t/// Constructor tries to open for 10 s, then throws <see cref=\"AuException\"/>.\n\t/// If the <i>createOwner</i> parameter is <c>true</c>, creates temporary message-only clipboard owner window.\n\t/// If the <i>noOpenNow</i> parameter is <c>true</c>, does not open, only creates owner if need.\n\t/// <c>Dispose</c> closes clipboard and destroys the owner window.\n\t/// </summary>\n\tinternal struct OpenClipboard_ : IDisposable {\n\t\tbool _isOpen;\n\t\twnd _w;\n\t\t\n\t\tpublic wnd WndClipOwner => _w;\n\t\t\n\t\tpublic OpenClipboard_(bool createOwner, bool noOpenNow = false) {\n\t\t\t_isOpen = false;\n\t\t\t_w = default;\n\t\t\tif (createOwner) {\n\t\t\t\t_w = WndUtil.CreateWindowDWP_(messageOnly: true);\n\t\t\t\t//MSDN says, SetClipboardData fails if OpenClipboard called with 0 hwnd. It doesn't, but better use hwnd.\n\t\t\t}\n\t\t\tif (!noOpenNow) Reopen();\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Opens again.\n\t\t/// Must be closed.\n\t\t/// Owner window should be not destroyed; does not create again.\n\t\t/// </summary>\n\t\t/// <param name=\"noThrow\">If fails, return <c>false</c>, no exception. Also then waits 1 s instead of 10 s.</param>\n\t\t/// <exception cref=\"AuException\">Failed to open.</exception>\n\t\tpublic bool Reopen(bool noThrow = false) {\n\t\t\tDebug.Assert(!_isOpen);\n\t\t\tvar loop = new WaitLoop(new(noThrow ? -1 : -10) { Period = 1 });\n\t\t\twhile (!Api.OpenClipboard(_w)) {\n\t\t\t\tint ec = lastError.code;\n\t\t\t\tif (!loop.Sleep()) {\n\t\t\t\t\tDispose();\n\t\t\t\t\tif (noThrow) return false;\n\t\t\t\t\tthrow new AuException(ec, \"*open clipboard\");\n\t\t\t\t}\n\t\t\t}\n\t\t\t_isOpen = true;\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\tpublic void Close(bool destroyOwnerWindow) {\n\t\t\tif (_isOpen) {\n\t\t\t\tApi.CloseClipboard();\n\t\t\t\t_isOpen = false;\n\t\t\t}\n\t\t\tif (destroyOwnerWindow && !_w.Is0) {\n\t\t\t\tApi.DestroyWindow(_w);\n\t\t\t\t_w = default;\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic void Dispose() => Close(true);\n\t}\n\t\n\t/// <summary>\n\t/// Saves and restores clipboard data.\n\t/// Clipboard must be open. Don't need to call <c>EmptyClipboard</c> before <c>Restore</c>.\n\t/// </summary>\n\tstruct _SaveRestore {\n\t\tDictionary<int, byte[]> _data;\n\t\t\n\t\tpublic void Save(bool debug = false) {\n\t\t\tvar p1 = new perf.Instance(); //will need if debug==true. Don't delete the perf statements, they are used by a public function.\n\t\t\tbool allFormats = OKey.RestoreClipboardAllFormats || debug;\n\t\t\tstring[] exceptFormats = OKey.RestoreClipboardExceptFormats;\n\t\t\t\n\t\t\tfor (int format = 0; 0 != (format = Api.EnumClipboardFormats(format));) {\n\t\t\t\tbool skip = false; string name = null;\n\t\t\t\tif (!allFormats) {\n\t\t\t\t\tskip = format != Api.CF_UNICODETEXT;\n\t\t\t\t} else {\n\t\t\t\t\t//standard, private\n\t\t\t\t\tif (format < Api.CF_MAX) { //standard\n\t\t\t\t\t\tswitch (format) {\n\t\t\t\t\t\tcase Api.CF_OEMTEXT: //synthesized from other text formats\n\t\t\t\t\t\tcase Api.CF_BITMAP: //synthesized from DIB formats\n\t\t\t\t\t\tcase Api.CF_PALETTE: //rare, never mind\n\t\t\t\t\t\t\tskip = true;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase Api.CF_METAFILEPICT:\n\t\t\t\t\t\tcase Api.CF_ENHMETAFILE:\n\t\t\t\t\t\t\tskip = true; //never mind, maybe in the future\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (format < 0xC000) { //CF_OWNERDISPLAY, DSP, GDI, private\n\t\t\t\t\t\tskip = true; //never mind. Not auto-freed, etc. Rare.\n\t\t\t\t\t} //else registered\n\t\t\t\t\t\n\t\t\t\t\tif (!skip && exceptFormats != null && exceptFormats.Length != 0) {\n\t\t\t\t\t\tname = ClipFormats.GetName(format);\n\t\t\t\t\t\tforeach (string s in exceptFormats) if (s.Eqi(name)) { skip = true; break; }\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (debug) {\n\t\t\t\t\tname ??= ClipFormats.GetName(format);\n\t\t\t\t\tif (skip) print.it($\"{name,-62}  restore=False\");\n\t\t\t\t\telse p1.First();\n\t\t\t\t\t//note: we don't call GetClipboardData for formats in exceptFormats, because the conditions must be like when really saving. Time of GetClipboardData(format2) may depend on whether called GetClipboardData(format1).\n\t\t\t\t}\n\t\t\t\tif (skip) continue;\n\t\t\t\t\n\t\t\t\tvar data = Api.GetClipboardData(format);\n\t\t\t\t\n\t\t\t\tint size = (data == default) ? 0 : (int)Api.GlobalSize(data);\n\t\t\t\tif (size == 0 || size > 10 * 1024 * 1024) skip = true;\n\t\t\t\t//If data == default, probably the target app did SetClipboardData(NULL) but did not render data on WM_RENDERFORMAT.\n\t\t\t\t//\tIf we try to save/restore, we'll receive WM_RENDERFORMAT too. It can be dangerous.\n\t\t\t\t\n\t\t\t\tif (debug) {\n\t\t\t\t\tp1.Next();\n\t\t\t\t\tprint.it($\"{name,-32}  time={p1.TimeTotal,-8}  size={size,-8}  restore={!skip}\");\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (skip) continue;\n\t\t\t\t\n\t\t\t\tvar b = Api.GlobalLock(data);\n\t\t\t\tDebug.Assert(b != default); if (b == default) continue;\n\t\t\t\ttry {\n\t\t\t\t\t_data ??= new Dictionary<int, byte[]>();\n\t\t\t\t\tvar a = new byte[size];\n\t\t\t\t\tMarshal.Copy(b, a, 0, size);\n\t\t\t\t\t_data.Add(format, a);\n\t\t\t\t}\n\t\t\t\tfinally { Api.GlobalUnlock(data); }\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic void Restore() {\n\t\t\tif (_data == null) return;\n\t\t\tEmptyClipboard_();\n\t\t\tforeach (var v in _data) {\n\t\t\t\tvar a = v.Value;\n\t\t\t\tvar h = Api.GlobalAlloc(Api.GMEM_MOVEABLE, a.Length);\n\t\t\t\tvar b = Api.GlobalLock(h);\n\t\t\t\tif (b != default) {\n\t\t\t\t\ttry { Marshal.Copy(a, 0, b, a.Length); } finally { Api.GlobalUnlock(h); }\n\t\t\t\t\tif (default == Api.SetClipboardData(v.Key, h)) b = default;\n\t\t\t\t}\n\t\t\t\tDebug.Assert(b != default);\n\t\t\t\tif (b == default) Api.GlobalFree(h);\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic bool IsSaved => _data != null;\n\t}\n\t\n\t/// <summary>\n\t/// Temporarily disables Windows 10 Clipboard History.\n\t/// Note: before disabling, we must open clipboard, else Clipboard History could be suspended while it has clipboard open.\n\t/// </summary>\n\tstruct _DisableClipboardHistory {\n\t\t//Pasting is unreliable with Windows 10 Clipboard History (CH).\n\t\t//Sometimes does not paste because OpenClipboard fails in the target app, because then CH has it open.\n\t\t//Then also _IsTargetWindow debugprints. Often just debugprints and waits briefly, but pasting works.\n\t\t//CH is enabled by default. Can be disabled in Settings > System > Clipboard.\n\t\t//If enabled, CH opens clipboard and gets text after 200 ms, and then repeats every several ms, total ~15 times and 50 ms.\n\t\t//\tWhen the target app fails to OpenClipboard, Paste_ waits briefly and the script continues. We receive WM_RENDERFORMAT because CH gets text.\n\t\t//If disabled, CH still opens clipboard after 200 ms, total 1-3 times.\n\t\t//\tWhen the target app fails to OpenClipboard, Paste_ waits and fails, because does not receive WM_RENDERFORMAT. It seems CH does not get text.\n\t\t//Possible workarounds, maybe untested or unreliable or too crazy:\n\t\t//\tHook posted messages (in C++ dll) and block WM_CLIPBOARDUPDATE.\n\t\t//\t\tNow using.\n\t\t//\t\tSimple and fast if using only 64-bit hook.\n\t\t//\t\tLess reliable because the message is posted async and can arrive after we remove hook. Never noticed, even with Task.Delay(1).\n\t\t//\t\tDoes not work if our process is 32-bit. Also does not block viewers of different bitness processes. Never mind, it's rare and not so important.\n\t\t//\t\tBlocks CH when this process isn't admin too.\n\t\t//\t\tBlocks most other 64-bit clipboard viewers too.\n\t\t//\tTemporarily SuspendThread. Tested, simple, fast, reliable.\n\t\t//\t\tFind all \"CLIPBRDWNDCLASS\" message-only windows and suspend their threads.\n\t\t//\t\tThe service process is in user session and not admin.\n\t\t//\t\tBut then cannot copy/paste in winstore apps, eg Calculator and Stickynotes.\n\t\t//\tTemporarily stop service \"Clipboard User Service_xxxxxxx\". Tested. Disables CH completely.\n\t\t//\t\tWould be simple and fast, but need to find service name, it is with random suffix.\n\t\t//\t\tBut need to run as admin.\n\t\t//\t\tWhen pasting, OS autotarts it again after 400-500 ms.\n\t\t//\t\tPausing fails, but can stop/start.\n\t\t//\t\tOS does not allow to set startup type \"Disabled\". And auto-starts when eg the Settings page opened.\n\t\t//\tInject a dll into the target process and hook OpenClipboard, let it wait until succeeds. Too crazy.\n\t\t//\tSend WM_CLOSE to the CH clipboard window (wOC). Tested, works, but too crazy.\n\t\t//\tTemporarily RemoveClipboardFormatListener. Does not work.\n\t\t//Plus there are other OS parts that use clipboard viewers.\n\t\t//\tEg the Settings app in the Clipboard page opens/gets text after 500 ms, usually 2 times.\n\t\t//\tNote: these workarounds may not help while the Clipboard page is open. Then eg pasting often fails because the target app cannot open clipboard.\n\t\t//Tested with Ditto too.\n\t\t//\tWithout workaround sometimes fails because the target app cannot open clipboard.\n\t\t//\tWith this workaround (hook) never fails.\n\t\t\n#if true\n\t\tIntPtr _hh;\n\t\t//static int s_nhooks; //test how many hooks when eg pasting in loop with small sleep\n\t\t\n\t\tpublic void Disable() {\n\t\t\t//if (!osVersion.minWin10_1809 || osVersion.Is32BitProcessAnd64BitOS) return; //no, let's block clipboard viewers on all OS\n\t\t\tif (osVersion.is32BitProcessAnd64BitOS) return;\n\t\t\t//if (keys.isScrollLock) return;\n\t\t\t_hh = Cpp.Cpp_Clipboard(default);\n\t\t\tDebug.Assert(_hh != default);\n\t\t\t//print.it(++s_nhooks); //max 8 when all delays removed in script. Max 4-5 with default options. Max 3-4 with 10.ms() in loop.\n\t\t}\n\t\t\n\t\tpublic void Restore() {\n\t\t\tif (_hh == default) return;\n\t\t\tvar hh = _hh; _hh = default;\n\t\t\t//remove hook later, when all posted WM_CLIPBOARDUPDATE probably are received.\n\t\t\t//\tIn my quick tests it always worked reliably, even with delay 1 ms. But I did not test with many clipboard viewers and in stress conditions.\n\t\t\tTask.Delay(100).ContinueWith(_ => { Cpp.Cpp_Clipboard(hh); /*s_nhooks--;*/ }); //info: if this process ends sooner, OS removes the hook\n\t\t}\n#else\n\t\tList<Handle_> _a;\n\n\t\tpublic void Disable() {\n\t\t\tif (!osVersion.minWin10_1809) return;\n\t\t\tfor (wnd w = default; ;) {\n\t\t\t\tw = wnd.findFast(null, \"CLIPBRDWNDCLASS\", true, w); if (w.Is0) break;\n\t\t\t\tint tid = w.GetThreadProcessId(out int pid); if (tid == 0) continue;\n\t\t\t\tif (!process.getName(pid, noSlowAPI: true).Eqi(\"svchost.exe\")) continue;\n\t\t\t\tvar ht = Api.OpenThread(Api.THREAD_SUSPEND_RESUME, false, tid); if (ht.Is0) continue;\n\t\t\t\tif (Api.SuspendThread(ht) < 0) { ht.Dispose(); continue; }\n\t\t\t\t_a ??= new List<Handle_>();\n\t\t\t\t_a.Add(ht);\n\t\t\t}\n\t\t\tDebug_.PrintIf(_a == null, \"no suspended threads\");\n\t\t}\n\n\t\tpublic void Restore() {\n\t\t\tif (_a == null) return;\n\t\t\tforeach (var ht in _a) {\n\t\t\t\tApi.ResumeThread(ht);\n\t\t\t\tht.Dispose();\n\t\t\t}\n\t\t}\n#endif\n\t}\n\t\n\tinternal static void PrintClipboard_() {\n\t\tprint.it(\"---- Clipboard ----\");\n\t\tusing var oc = new OpenClipboard_(true);\n\t\tApi.GetClipboardData(0); //JIT\n\t\tvar save = new _SaveRestore();\n\t\tsave.Save(true);\n\t}\n}\n"
  },
  {
    "path": "Au/Input/clipboardData.cs",
    "content": "\n//#define SUPPORT_RAW_HANDLE\n\nusing System.Drawing;\nusing System.Drawing.Imaging;\n\nnamespace Au {\n\t/// <summary>\n\t/// Sets or gets clipboard data in multiple formats.\n\t/// </summary>\n\t/// <remarks>\n\t/// The <c>AddX</c> functions add data to the variable (not to the clipboard). Then <see cref=\"SetClipboard\"/> copies the added data to the clipboard. Also you can use the variable with <see cref=\"clipboard.pasteData\"/>.\n\t/// The static <c>GetX</c> functions get data directly from the clipboard.\n\t/// </remarks>\n\t/// <example>\n\t/// Get bitmap image from clipboard.\n\t/// <code><![CDATA[\n\t/// var image = clipboardData.getImage();\n\t/// if(image == null) print.it(\"no image in clipboard\"); else print.it(image.Size);\n\t/// ]]></code>\n\t/// Set clipboard data in two formats: text and image.\n\t/// <code><![CDATA[\n\t/// new clipboardData().AddText(\"text\").AddImage(Image.FromFile(@\"C:\\file.png\")).SetClipboard();\n\t/// ]]></code>\n\t/// Paste data of two formats: HTML and text.\n\t/// <code><![CDATA[\n\t/// clipboard.pasteData(new clipboardData().AddHtml(\"<b>text</b>\").AddText(\"text\"));\n\t/// ]]></code>\n\t/// Copy data in two formats: HTML and text.\n\t/// <code><![CDATA[\n\t/// string html = null, text = null;\n\t/// clipboard.copyData(() => { html = clipboardData.getHtml(); text = clipboardData.getText(); });\n\t/// print.it(html); print.it(text);\n\t/// ]]></code>\n\t/// </example>\n\tpublic class clipboardData {\n\t\tstruct _Data { public object data; public int format; }\n\t\tList<_Data> _a = new();\n\n\t\t#region add\n\n\t\tstatic void _CheckFormat(int format, bool minimalCheckFormat = false) {\n\t\t\tbool badFormat = false;\n\t\t\tif (format <= 0 || format > 0xffff) badFormat = true;\n\t\t\telse if (format < 0xC000 && !minimalCheckFormat) {\n\t\t\t\tif (format >= Api.CF_MAX) badFormat = true; //rare. Most are either not GlobalAlloc'ed or not auto-freed.\n\t\t\t\telse badFormat = format == Api.CF_BITMAP || format == Api.CF_PALETTE || format == Api.CF_METAFILEPICT || format == Api.CF_ENHMETAFILE; //not GlobalAlloc'ed\n\t\t\t}\n\t\t\tif (badFormat) throw new ArgumentException(\"Invalid format id.\");\n\t\t}\n\n\t\tclipboardData _Add(object data, int format, bool minimalCheckFormat = false) {\n\t\t\tNot_.Null(data);\n\t\t\t_CheckFormat(format, minimalCheckFormat);\n\n\t\t\t_a.Add(new _Data() { data = data, format = format });\n\t\t\treturn this;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Adds text.\n\t\t/// </summary>\n\t\t/// <returns>this.</returns>\n\t\t/// <param name=\"text\">Text.</param>\n\t\t/// <param name=\"format\">\n\t\t/// Clipboard format id. Default: <see cref=\"ClipFormats.Text\"/> (<ms>CF_UNICODETEXT</ms>).\n\t\t/// Text encoding depends on <i>format</i>; default UTF-16. See <see cref=\"ClipFormats.Register\"/>.\n\t\t/// </param>\n\t\t/// <exception cref=\"ArgumentNullException\"></exception>\n\t\t/// <exception cref=\"ArgumentException\">Invalid <i>format</i>.</exception>\n\t\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"Encoding.GetBytes(string)\"/>, which is called if encoding is not UTF-16.</exception>\n\t\tpublic clipboardData AddText(string text, int format = ClipFormats.Text) {\n\t\t\tEncoding enc = ClipFormats.GetTextEncoding_(format, out _);\n\t\t\tif (enc == null) return _Add(text, format == 0 ? Api.CF_UNICODETEXT : format);\n\t\t\treturn _Add(enc.GetBytes(text).InsertAt(-1), format);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Adds data of any format as <c>byte[]</c>.\n\t\t/// </summary>\n\t\t/// <returns>this.</returns>\n\t\t/// <param name=\"data\"><c>byte[]</c> containing data.</param>\n\t\t/// <param name=\"format\">Clipboard format id. See <see cref=\"ClipFormats.Register\"/>.</param>\n\t\t/// <exception cref=\"ArgumentNullException\"></exception>\n\t\t/// <exception cref=\"ArgumentException\">Invalid <i>format</i>. Supported are all registered formats and standard formats <c>&lt;CF_MAX</c> except GDI handles.</exception>\n\t\tpublic clipboardData AddBinary(byte[] data, int format) {\n\t\t\treturn _Add(data, format);\n\t\t}\n\n\t\t//rejected: rarely used, difficult to use, creates problems. If somebody needs it, can use API.\n#if SUPPORT_RAW_HANDLE\n\t\t\t/// <summary>\n\t\t\t/// Adds data of any format as raw clipboard object handle.\n\t\t\t/// </summary>\n\t\t\t/// <returns>this.</returns>\n\t\t\t/// <param name=\"handle\">Any handle supported by API <ms>SetClipboardData</ms>. The type depends on format. For most formats, after setting clipboard data the handle is owned and freed by Windows.</param>\n\t\t\t/// <param name=\"format\">Clipboard format id. See <see cref=\"RegisterClipboardFormat\"/>.</param>\n\t\t\t/// <exception cref=\"ArgumentNullException\"></exception>\n\t\t\t/// <exception cref=\"ArgumentException\">Invalid format.</exception>\n\t\t\t/// <remarks>\n\t\t\t/// The same handle cannot be added to the clipboard twice. To avoid it, \"set clipboard\" functions remove handles from the variable.\n\t\t\t/// </remarks>\n\t\t\tpublic Data AddHandle(IntPtr handle, int format)\n\t\t\t{\n\t\t\t\treturn _Add(handle != default ? (object)handle : null, format, minimalCheckFormat: true);\n\t\t\t}\n#endif\n\n\t\t/// <summary>\n\t\t/// Adds image.\n\t\t/// Uses clipboard format <see cref=\"ClipFormats.Png\"/> and/or <see cref=\"ClipFormats.Image\"/> (<ms>CF_BITMAP</ms>).\n\t\t/// </summary>\n\t\t/// <returns>this.</returns>\n\t\t/// <param name=\"image\">Image. Must be <see cref=\"Bitmap\"/>, else exception.</param>\n\t\t/// <param name=\"png\">\n\t\t/// Use PNG format (it supports transparency):\n\t\t/// <br/>• <c>false</c> - no, only <c>CF_BITMAP</c>.\n\t\t/// <br/>• <c>true</c> - yes, only PNG.\n\t\t/// <br/>• <c>null</c> (default) - add PNG and <c>CF_BITMAP</c>.\n\t\t/// </param>\n\t\t/// <exception cref=\"ArgumentNullException\"></exception>\n\t\tpublic clipboardData AddImage(Image image, bool? png = null) {\n\t\t\tvar b = (Bitmap)image;\n\t\t\tif (png != false) {\n\t\t\t\tvar ms = new MemoryStream();\n\t\t\t\tb.Save(ms, ImageFormat.Png);\n\t\t\t\t_Add(ms.ToArray(), ClipFormats.Png, minimalCheckFormat: true);\n\t\t\t}\n\t\t\tif (png != true) {\n\t\t\t\t_Add(b, Api.CF_BITMAP, minimalCheckFormat: true);\n\t\t\t}\n\t\t\treturn this;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Adds HTML text. Uses clipboard format <see cref=\"ClipFormats.Html\"/> (<c>\"HTML Format\"</c>).\n\t\t/// </summary>\n\t\t/// <returns>this.</returns>\n\t\t/// <param name=\"html\">Full HTML or HTML fragment. If full HTML, a fragment in it can be optionally specified. See examples.</param>\n\t\t/// <exception cref=\"ArgumentNullException\"></exception>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// d.AddHtml(\"<i>italy</i>\");\n\t\t/// d.AddHtml(\"<html><body><i>italy</i></body></html>\");\n\t\t/// d.AddHtml(\"<html><body><!--StartFragment--><i>italy</i><!--EndFragment--></body></html>\");\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic clipboardData AddHtml(string html) {\n\t\t\treturn AddBinary(CreateHtmlFormatData_(html), ClipFormats.Html);\n\t\t\t//note: don't support UTF-16 string of HTML format (starts with \"Version:\"). UTF8 conversion problems.\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Adds list of files to copy/paste. Uses clipboard format <see cref=\"ClipFormats.Files\"/> (<ms>CF_HDROP</ms>).\n\t\t/// </summary>\n\t\t/// <returns>this.</returns>\n\t\t/// <param name=\"files\">One or more file paths.</param>\n\t\t/// <exception cref=\"ArgumentNullException\"></exception>\n\t\tpublic clipboardData AddFiles(params string[] files) {\n\t\t\tNot_.Null(files);\n\t\t\tvar b = new StringBuilder(\"\\x14\\0\\0\\0\\0\\0\\0\\0\\x1\\0\"); //struct DROPFILES\n\t\t\tforeach (var s in files) { b.Append(s); b.Append('\\0'); }\n\t\t\treturn _Add(b.ToString(), Api.CF_HDROP, false);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Copies the added data of all formats to the clipboard.\n\t\t/// </summary>\n\t\t/// <exception cref=\"AuException\">Failed to open clipboard (after 10 s of wait/retry) or set clipboard data.</exception>\n\t\t/// <exception cref=\"OutOfMemoryException\">Failed to allocate memory for clipboard data.</exception>\n\t\t/// <remarks>\n\t\t/// Calls API <ms>OpenClipboard</ms>, <ms>EmptyClipboard</ms>, <ms>SetClipboardData</ms> and <ms>CloseClipboard</ms>.\n\t\t/// </remarks>\n\t\tpublic void SetClipboard() {\n\t\t\tusing (new clipboard.OpenClipboard_(true)) {\n\t\t\t\tclipboard.EmptyClipboard_();\n\t\t\t\tSetOpenClipboard();\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Copies the added data of all formats to the clipboard which is open/owned by this thread.\n\t\t/// </summary>\n\t\t/// <param name=\"renderLater\">Call API <ms>SetClipboardData</ms>: <c>SetClipboardData(format, default)</c>. When/if some app will try to get clipboard data, the first time your clipboard owner window will receive <ms>WM_RENDERFORMAT</ms> message and should call <c>SetOpenClipboard(false);</c>.</param>\n\t\t/// <param name=\"format\">Copy data only of this format. If 0 (default), of all formats.</param>\n\t\t/// <exception cref=\"OutOfMemoryException\">Failed to allocate memory for clipboard data.</exception>\n\t\t/// <exception cref=\"AuException\">Failed to set clipboard data.</exception>\n\t\t/// <remarks>\n\t\t/// This function is similar to <see cref=\"SetClipboard\"/>. It calls API <ms>SetClipboardData</ms> and does not call <ms>OpenClipboard</ms>, <ms>EmptyClipboard</ms>, <ms>CloseClipboard</ms>. The clipboard must be open and owned by a window of this thread.\n\t\t/// </remarks>\n\t\tpublic void SetOpenClipboard(bool renderLater = false, int format = 0) {\n\t\t\tfor (int i = 0; i < _a.Count; i++) {\n\t\t\t\tvar v = _a[i];\n\t\t\t\tif (format != 0 && v.format != format) continue;\n\t\t\t\tif (renderLater) {\n\t\t\t\t\tlastError.clear();\n\t\t\t\t\tApi.SetClipboardData(v.format, default);\n\t\t\t\t\tint ec = lastError.code; if (ec != 0) throw new AuException(ec, \"*set clipboard data\");\n\t\t\t\t} else _SetClipboard(v.format, v.data);\n\t\t\t}\n#if SUPPORT_RAW_HANDLE\n\t\t\t\t//remove caller-added handles, to avoid using the same handle twice\n\t\t\t\tif(renderLater) return;\n\t\t\t\tfor(int i = _a.Count; --i >= 0;) {\n\t\t\t\t\tvar v = _a[i];\n\t\t\t\t\tif(format != 0 && v.format != format) continue;\n\t\t\t\t\tif(v.data is IntPtr) _a.RemoveAt(i);\n\t\t\t\t}\n#endif\n\t\t}\n\n\t\tstatic unsafe void _SetClipboard(int format, object data) {\n\t\t\tIntPtr h = default;\n\t\t\tswitch (data) {\n\t\t\tcase string s:\n\t\t\t\tfixed (char* p = s) h = _CopyToHmem(p, (s.Length + 1) * 2);\n\t\t\t\tbreak;\n\t\t\tcase byte[] b:\n\t\t\t\tfixed (byte* p = b) h = _CopyToHmem(p, b.Length);\n\t\t\t\tbreak;\n#if SUPPORT_RAW_HANDLE\n\t\t\t\tcase IntPtr ip:\n\t\t\t\t\th = ip;\n\t\t\t\t\tbreak;\n#endif\n\t\t\tcase Bitmap bmp:\n\t\t\t\th = bmp.GetHbitmap();\n\t\t\t\tvar h2 = Api.CopyImage(h, 0, 0, 0, Api.LR_COPYDELETEORG); //DIB to compatible bitmap\n\t\t\t\tif (h2 == default) goto ge;\n\t\t\t\th = h2;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tDebug.Assert(h != default);\n\t\t\tif (default != Api.SetClipboardData(format, h)) return;\n\t\t\tge:\n\t\t\tint ec = lastError.code;\n\t\t\tif (data is Bitmap) Api.DeleteObject(h); else Api.GlobalFree(h);\n\t\t\tthrow new AuException(ec, \"*set clipboard data\");\n\t\t}\n\n\t\tstatic unsafe IntPtr _CopyToHmem(void* p, int size) {\n\t\t\tvar h = Api.GlobalAlloc(Api.GMEM_MOVEABLE, size); if (h == default) goto ge;\n\t\t\tvar v = (byte*)Api.GlobalLock(h); if (v == null) { Api.GlobalFree(h); goto ge; }\n\t\t\ttry { MemoryUtil.Copy(p, v, size); } finally { Api.GlobalUnlock(h); }\n\t\t\treturn h;\n\t\t\tge: throw new OutOfMemoryException();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Copies Unicode text to the clipboard without open/empty/close.\n\t\t/// </summary>\n\t\tinternal static void SetText_(string text) {\n\t\t\tDebug.Assert(text != null);\n\t\t\t_SetClipboard(Api.CF_UNICODETEXT, text);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Converts HTML string to <c>byte[]</c> containing data in clipboard format <c>\"HTML Format\"</c>.\n\t\t/// </summary>\n\t\t/// <param name=\"html\">Full HTML or HTML fragment. If full HTML, a fragment in it can be optionally specified. See examples.</param>\n\t\t/// <exception cref=\"ArgumentNullException\"></exception>\n\t\t/// <example>\n\t\t/// HTML examples.\n\t\t/// <code><![CDATA[\n\t\t/// \"<i>italy</i>\"\n\t\t/// \"<html><body><i>italy</i></body></html>\"\n\t\t/// \"<html><body><!--StartFragment--><i>italy</i><!--EndFragment--></body></html>\"\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tinternal static unsafe byte[] CreateHtmlFormatData_(string html) {\n\t\t\tNot_.Null(html);\n\t\t\tvar b = new StringBuilder(c_headerTemplate);\n\t\t\t//find \"<body>...</body>\" and \"<!--StartFragment-->...<!--EndFragment-->\" in it\n\t\t\tint isb = -1, ieb = -1, isf = -1, ief = -1; //start/end of inner body and fragment\n\t\t\tif (html.RxMatch(@\"<body\\b.*?>\", 0, out RXGroup body) && (ieb = html.Find(\"</body>\", body.End)) >= 0) {\n\t\t\t\tisb = body.End;\n\t\t\t\tisf = html.Find(c_startFragment, isb..ieb, true);\n\t\t\t\tif (isf >= 0) {\n\t\t\t\t\tisf += c_startFragment.Length;\n\t\t\t\t\tief = html.Find(c_endFragment, isf..ieb, true);\n\t\t\t\t}\n\t\t\t}\n\t\t\t//print.it($\"{isb} {ieb}  {isf} {ief}\");\n\t\t\tif (ieb < 0) { //no \"<body>...</body>\"\n\t\t\t\tb.Append(\"<html><body>\").Append(c_startFragment).Append(html).Append(c_endFragment).Append(\"</body></html>\");\n\t\t\t\tisf = 12 + c_startFragment.Length;\n\t\t\t\tief = isf + Encoding.UTF8.GetByteCount(html);\n\t\t\t} else {\n\t\t\t\tif (ief < 0) { //\"...<body>...</body>...\"\n\t\t\t\t\tb.Append(html, 0, isb).Append(c_startFragment).Append(html, isb, ieb - isb)\n\t\t\t\t\t\t.Append(c_endFragment).Append(html, ieb, html.Length - ieb);\n\t\t\t\t\tisf = isb + c_startFragment.Length;\n\t\t\t\t\tief = ieb + c_startFragment.Length;\n\t\t\t\t} else { //\"...<body>...<!--StartFragment-->...<!--EndFragment-->...</body>...\"\n\t\t\t\t\tb.Append(html);\n\t\t\t\t\tisb = isf; ieb = ief; //reuse these vars to calc UTF8 lengths\n\t\t\t\t}\n\t\t\t\t//correct isf/ief if html part lengths are different in UTF8\n\t\t\t\tif (!html.IsAscii()) {\n\t\t\t\t\tfixed (char* p = html) {\n\t\t\t\t\t\tint lenDiff1 = Encoding.UTF8.GetByteCount(p, isb) - isb;\n\t\t\t\t\t\tint lenDiff2 = Encoding.UTF8.GetByteCount(p + isb, ieb - isb) - (ieb - isb);\n\t\t\t\t\t\tisf += lenDiff1;\n\t\t\t\t\t\tief += lenDiff1 + lenDiff2;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t//print.it($\"{isf} {ief}\");\n\t\t\tisf += c_headerTemplate.Length; ief += c_headerTemplate.Length;\n\n\t\t\tb.Append('\\0');\n\t\t\tvar a = Encoding.UTF8.GetBytes(b.ToString());\n\t\t\t_SetNum(a.Length - 1, 53);\n\t\t\t_SetNum(isf, 79);\n\t\t\t_SetNum(ief, 103);\n\n\t\t\t//print.it(Encoding.UTF8.GetString(a));\n\t\t\treturn a;\n\n\t\t\tvoid _SetNum(int num, int i) {\n\t\t\t\tfor (; num != 0; num /= 10) a[--i] = (byte)('0' + num % 10);\n\t\t\t}\n\t\t}\n\t\tconst string c_startFragment = \"<!--StartFragment-->\";\n\t\tconst string c_endFragment = \"<!--EndFragment-->\";\n\t\tconst string c_headerTemplate = @\"Version:0.9\nStartHTML:0000000105\nEndHTML:0000000000\nStartFragment:0000000000\nEndFragment:0000000000\n\";\n\n\t\t#endregion\n\n\t\t#region get\n\n\t\tstruct _GlobalLock : IDisposable {\n\t\t\tIntPtr _hmem;\n\n\t\t\tpublic _GlobalLock(IntPtr hmem, out IntPtr mem, out int size) {\n\t\t\t\tmem = Api.GlobalLock(hmem);\n\t\t\t\tif (mem == default) { _hmem = default; size = 0; return; }\n\t\t\t\tsize = (int)Api.GlobalSize(_hmem = hmem);\n\t\t\t}\n\n\t\t\tpublic void Dispose() {\n\t\t\t\tApi.GlobalUnlock(_hmem);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets clipboard text without open/close.\n\t\t/// If format is 0, tries <c>CF_UNICODETEXT</c> and <c>CF_HDROP</c>.\n\t\t/// </summary>\n\t\tinternal static unsafe string GetText_(int format) {\n\t\t\tIntPtr h = default;\n\t\t\tif (format == 0) {\n\t\t\t\th = Api.GetClipboardData(Api.CF_UNICODETEXT);\n\t\t\t\tif (h == default) format = Api.CF_HDROP;\n\t\t\t}\n\t\t\tif (format == 0) format = Api.CF_UNICODETEXT;\n\t\t\telse {\n\t\t\t\th = Api.GetClipboardData(format); if (h == default) return null;\n\t\t\t\tif (format == Api.CF_HDROP) return string.Join(\"\\r\\n\", HdropToFiles_(h));\n\t\t\t}\n\n\t\t\tusing (new _GlobalLock(h, out var mem, out int len)) {\n\t\t\t\tif (mem == default) return null;\n\t\t\t\tvar s = (char*)mem; var b = (byte*)s;\n\n\t\t\t\tEncoding enc = ClipFormats.GetTextEncoding_(format, out bool unknown);\n\t\t\t\tif (unknown) {\n\t\t\t\t\tif ((len & 1) != 0 || Ptr_.Length(b, len) > len - 2) enc = Encoding.Default; //autodetect  //never mind: it is UTF-8, not ANSI. Rarely used, especially with non-ASCII text.\n\t\t\t\t}\n\n\t\t\t\tif (enc == null) {\n\t\t\t\t\tlen /= 2; while (len > 0 && s[len - 1] == '\\0') len--;\n\t\t\t\t\treturn new string(s, 0, len);\n\t\t\t\t} else {\n\t\t\t\t\t//most apps add single '\\0' at the end. Some don't add. Some add many, eg Dreamweaver. Trim all.\n\t\t\t\t\tint charLen = enc.GetByteCount(\"\\0\");\n\t\t\t\t\tswitch (charLen) {\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\twhile (len > 0 && b[len - 1] == '\\0') len--;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\tfor (int k = len / 2; k > 0 && s[k - 1] == '\\0'; k--) len -= 2;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 4:\n\t\t\t\t\t\tvar ip = (int*)s; for (int k = len / 4; k > 0 && ip[k - 1] == '\\0'; k--) len -= 4;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\treturn enc.GetString(b, len);\n\t\t\t\t\t//note: don't parse HTML format here. Let caller use GetHtml or parse itself.\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets text from the clipboard.\n\t\t/// </summary>\n\t\t/// <returns><c>null</c> if there is no text.</returns>\n\t\t/// <param name=\"format\">\n\t\t/// Clipboard format id. Default: <see cref=\"ClipFormats.Text\"/> (<ms>CF_UNICODETEXT</ms>).\n\t\t/// If 0, tries to get text (<see cref=\"ClipFormats.Text\"/>) or file paths (<see cref=\"ClipFormats.Files\"/>; returns multiline text).\n\t\t/// Text encoding depends on <i>format</i>; default UTF-16. See <see cref=\"ClipFormats.Register\"/>.\n\t\t/// </param>\n\t\t/// <exception cref=\"AuException\">Failed to open clipboard (after 10 s of wait/retry).</exception>\n\t\tpublic static string getText(int format = ClipFormats.Text) {\n\t\t\tusing (new clipboard.OpenClipboard_(false)) {\n\t\t\t\treturn GetText_(format);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets clipboard data of any format as <c>byte[]</c>.\n\t\t/// </summary>\n\t\t/// <returns><c>null</c> if there is no data of this format.</returns>\n\t\t/// <exception cref=\"ArgumentException\">Invalid <i>format</i>. Supported are all registered formats and standard formats <c>&lt;CF_MAX</c> except GDI handles.</exception>\n\t\t/// <exception cref=\"AuException\">Failed to open clipboard (after 10 s of wait/retry).</exception>\n\t\tpublic static byte[] getBinary(int format) {\n\t\t\t_CheckFormat(format);\n\t\t\tusing (new clipboard.OpenClipboard_(false)) {\n\t\t\t\tvar h = Api.GetClipboardData(format); if (h == default) return null;\n\t\t\t\tusing (new _GlobalLock(h, out var mem, out int len)) {\n\t\t\t\t\tif (mem == default) return null;\n\t\t\t\t\tvar b = new byte[len];\n\t\t\t\t\tMarshal.Copy(mem, b, 0, len);\n\t\t\t\t\treturn b;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets clipboard data of any format without copying to array. Uses a callback function.\n\t\t/// </summary>\n\t\t/// <param name=\"get\">Callback function that receives data. The clipboard is open until it returns. The data is read-only.</param>\n\t\t/// <returns>The return value of the callback function. Returns <c>default(T)</c> if there is no data of this format.</returns>\n\t\t/// <inheritdoc cref=\"getBinary(int)\"/>\n\t\tpublic static T getBinary<T>(int format, Func<IntPtr, int, T> get) {\n\t\t\t_CheckFormat(format);\n\t\t\tusing (new clipboard.OpenClipboard_(false)) {\n\t\t\t\treturn _GetBinary(format, get);\n\t\t\t}\n\t\t}\n\n\t\tstatic T _GetBinary<T>(int format, Func<IntPtr, int, T> get) {\n\t\t\tvar h = Api.GetClipboardData(format);\n\t\t\tif (h != default) {\n\t\t\t\tusing (new _GlobalLock(h, out var mem, out int len)) {\n\t\t\t\t\tif (mem != default) return get(mem, len); //.NET does not allow Func<ReadOnlySpan<byte>, T>\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn default;\n\t\t}\n\n#if SUPPORT_RAW_HANDLE\n\t\t\tpublic static IntPtr GetHandle(int format)\n\t\t\t{\n\t\t\t\t_CheckFormat(format, minimalCheckFormat: true);\n\t\t\t\tusing(new OpenClipboard_(false)) {\n\t\t\t\t\treturn Api.GetClipboardData(format);\n\t\t\t\t}\n\t\t\t}\n#endif\n\n\t\t/// <summary>\n\t\t/// Gets image from the clipboard.\n\t\t/// Uses clipboard format <see cref=\"ClipFormats.Png\"/> or <see cref=\"ClipFormats.Image\"/> (<ms>CF_BITMAP</ms>).\n\t\t/// </summary>\n\t\t/// <returns><c>null</c> if there is no data of this format.</returns>\n\t\t/// <param name=\"png\">\n\t\t/// Use PNG format (it supports transparency):\n\t\t/// <br/>• <c>false</c> - no, only <c>CF_BITMAP</c>.\n\t\t/// <br/>• <c>true</c> - yes, only PNG.\n\t\t/// <br/>• <c>null</c> (default) - yes, but get <c>CF_BITMAP</c> if there is no PNG.\n\t\t/// </param>\n\t\t/// <exception cref=\"AuException\">Failed to open clipboard (after 10 s of wait/retry).</exception>\n\t\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"Image.FromHbitmap\"/> or <see cref=\"Image.FromStream\"/>.</exception>\n\t\tpublic static unsafe Bitmap getImage(bool? png = null) {\n\t\t\tusing (new clipboard.OpenClipboard_(false)) {\n\t\t\t\tif (png != false && _GetBinary(ClipFormats.Png, static (mem, len) => {\n\t\t\t\t\tusing var ms = new UnmanagedMemoryStream((byte*)mem, len);\n\t\t\t\t\treturn Image.FromStream(ms);\n\t\t\t\t}) is Bitmap b1) return b1;\n\n\t\t\t\tif (png != true) {\n\t\t\t\t\tvar h = Api.GetClipboardData(Api.CF_BITMAP);\n\t\t\t\t\tif (h != default) {\n\t\t\t\t\t\tusing var b = Image.FromHbitmap(h, Api.GetClipboardData(Api.CF_PALETTE)); //bottom-up 32Rgb (GDI)\n\t\t\t\t\t\treturn b?.Clone(new(default, b.Size), PixelFormat.Format32bppArgb) as Bitmap; //top-down 32Argb (GDI+)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets HTML text from the clipboard. Uses clipboard format <see cref=\"ClipFormats.Html\"/> (<c>\"HTML Format\"</c>).\n\t\t/// </summary>\n\t\t/// <returns><c>null</c> if there is no data of this format or if failed to parse it.</returns>\n\t\t/// <exception cref=\"AuException\">Failed to open clipboard (after 10 s of wait/retry).</exception>\n\t\tpublic static string getHtml() => getHtml(out _, out _, out _);\n\n\t\t/// <param name=\"fragmentStart\">Fragment start index in the returned string.</param>\n\t\t/// <param name=\"fragmentLength\">Fragment length.</param>\n\t\t/// <param name=\"sourceURL\">Source URL, or <c>null</c> if unavailable.</param>\n\t\t/// <inheritdoc cref=\"getHtml()\"/>\n\t\tpublic static string getHtml(out int fragmentStart, out int fragmentLength, out string sourceURL) {\n\t\t\treturn ParseHtmlFormatData_(getBinary(ClipFormats.Html), out fragmentStart, out fragmentLength, out sourceURL);\n\t\t}\n\n\t\tinternal static string ParseHtmlFormatData_(byte[] b, out int fragmentStart, out int fragmentLength, out string sourceURL) {\n\t\t\t//print.it(s);\n\t\t\tfragmentStart = fragmentLength = 0; sourceURL = null;\n\t\t\tif (b == null) return null;\n\t\t\tstring s = Encoding.UTF8.GetString(b);\n\n\t\t\tint ish = s.Find(\"StartHTML:\", true);\n\t\t\tint ieh = s.Find(\"EndHTML:\", true);\n\t\t\tint isf = s.Find(\"StartFragment:\", true);\n\t\t\tint ief = s.Find(\"EndFragment:\", true);\n\t\t\tif (ish < 0 || ieh < 0 || isf < 0 || ief < 0) return null;\n\t\t\tisf = s.ToInt(isf + 14); if (isf < 0) return null;\n\t\t\tief = s.ToInt(ief + 12); if (ief < isf) return null;\n\t\t\tish = s.ToInt(ish + 10); if (ish < 0) ish = isf; else if (ish > isf) return null;\n\t\t\tieh = s.ToInt(ieh + 8); if (ieh < 0) ieh = ief; else if (ieh < ief) return null;\n\n\t\t\tif (s.Length != b.Length) {\n\t\t\t\tif (ieh > b.Length) return null;\n\t\t\t\t_CorrectOffset(ref isf);\n\t\t\t\t_CorrectOffset(ref ief);\n\t\t\t\t_CorrectOffset(ref ish);\n\t\t\t\t_CorrectOffset(ref ieh);\n\t\t\t} else if (ieh > s.Length) return null;\n\t\t\t//print.it(ish, ieh, isf, ief);\n\n\t\t\tint isu = s.Find(\"SourceURL:\", true), ieu;\n\t\t\tif (isu >= 0 && (ieu = s.FindAny(\"\\r\\n\", (isu += 10)..)) >= 0) sourceURL = s[isu..ieu];\n\n\t\t\tfragmentStart = isf - ish; fragmentLength = ief - isf;\n\t\t\treturn s[ish..ieh];\n\n\t\t\tvoid _CorrectOffset(ref int i) {\n\t\t\t\ti = Encoding.UTF8.GetCharCount(b, 0, i);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets file paths from the clipboard. Uses clipboard format <see cref=\"ClipFormats.Files\"/> (<ms>CF_HDROP</ms>).\n\t\t/// </summary>\n\t\t/// <returns><c>null</c> if there is no data of this format.</returns>\n\t\t/// <exception cref=\"AuException\">Failed to open clipboard (after 10 s of wait/retry).</exception>\n\t\tpublic static string[] getFiles() {\n\t\t\tusing (new clipboard.OpenClipboard_(false)) {\n\t\t\t\tvar h = Api.GetClipboardData(Api.CF_HDROP); if (h == default) return null;\n\t\t\t\treturn HdropToFiles_(h);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets file paths from <c>HDROP</c>.\n\t\t/// </summary>\n\t\t/// <returns>Array of zero or more non-<c>null</c> elements.</returns>\n\t\tinternal static unsafe string[] HdropToFiles_(IntPtr hdrop) {\n\t\t\tint n = Api.DragQueryFile(hdrop, -1, null, 0);\n\t\t\tvar a = new string[n];\n\t\t\tvar b = stackalloc char[500];\n\t\t\tfor (int i = 0; i < n; i++) {\n\t\t\t\tint len = Api.DragQueryFile(hdrop, i, b, 500);\n\t\t\t\ta[i] = new string(b, 0, len);\n\t\t\t}\n\t\t\treturn a;\n\t\t}\n\n\t\t#endregion\n\n\t\t#region contains\n\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if the clipboard contains data of the specified format.\n\t\t/// </summary>\n\t\t/// <param name=\"format\">Clipboard format id. See <see cref=\"ClipFormats\"/>.</param>\n\t\t/// <remarks>Calls API <ms>IsClipboardFormatAvailable</ms>.</remarks>\n\t\tpublic static bool contains(int format) {\n\t\t\treturn Api.IsClipboardFormatAvailable(format);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Returns the first of the specified formats that is in the clipboard.\n\t\t/// Returns 0 if the clipboard is empty. Returns -1 if the clipboard contains data but not in any of the specified formats.\n\t\t/// </summary>\n\t\t/// <param name=\"formats\">Clipboard format ids. See <see cref=\"ClipFormats\"/>.</param>\n\t\t/// <remarks>Calls API <ms>GetPriorityClipboardFormat</ms>.</remarks>\n\t\tpublic static int contains(params int[] formats) {\n\t\t\treturn Api.GetPriorityClipboardFormat(formats, formats.Length);\n\t\t}\n\n\t\t#endregion\n\n\t\t//CONSIDER: EnumFormats, OnClipboardChanged\n\t}\n}\n\nnamespace Au.Types {\n\t/// <summary>\n\t/// Some clipboard format ids.\n\t/// These and other standard and registered format ids can be used with <see cref=\"clipboardData\"/> class functions.\n\t/// </summary>\n\tpublic static class ClipFormats {\n\t\t/// <summary>The text format. Standard, API constant <ms>CF_UNICODETEXT</ms>. The default format of <see cref=\"clipboardData\"/> add/get text functions.</summary>\n\t\tpublic const int Text = Api.CF_UNICODETEXT;\n\n\t\t/// <summary>The image format. Standard, API constant <ms>CF_BITMAP</ms>. Used by <see cref=\"clipboardData\"/> add/get image functions.</summary>\n\t\tpublic const int Image = Api.CF_BITMAP;\n\n\t\t/// <summary>The file-list format. Standard, API constant <ms>CF_HDROP</ms>. Used by <see cref=\"clipboardData\"/> add/get files functions.</summary>\n\t\tpublic const int Files = Api.CF_HDROP;\n\n\t\t/// <summary>The HTML format. Registered, name <c>\"HTML Format\"</c>. Used by <see cref=\"clipboardData\"/> add/get HTML functions.</summary>\n\t\tpublic static int Html { get; } = Api.RegisterClipboardFormat(\"HTML Format\");\n\n\t\t/// <summary>The PNG format. Registered, name <c>\"PNG\"</c>. Used by <see cref=\"clipboardData\"/> add/get image functions.</summary>\n\t\tpublic static int Png { get; } = Api.RegisterClipboardFormat(\"PNG\");\n\n\t\t/// <summary>Registered format <c>\"Shell IDList Array\"</c>.</summary>\n\t\tinternal static int ShellIDListArray_ { get; } = Api.RegisterClipboardFormat(\"Shell IDList Array\");\n\n\t\t/// <summary>Registered format <c>\"FileGroupDescriptorW\"</c>.</summary>\n\t\tinternal static int FileGroupDescriptorW_ { get; } = Api.RegisterClipboardFormat(\"FileGroupDescriptorW\");\n\n\t\t/// <summary>\n\t\t/// Registered format <c>\"Clipboard Viewer Ignore\"</c>.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Some clipboard viewer/manager programs don't try to get clipboard data if this format is present. For example Ditto, Clipdiary.\n\t\t/// The copy/paste functions of this library add this format to the clipboard to avoid displaying the temporary text/data in these programs, which also could make the paste function slower and less reliable.\n\t\t/// </remarks>\n\t\tpublic static int ClipboardViewerIgnore { get; } = Api.RegisterClipboardFormat(\"Clipboard Viewer Ignore\");\n\n\t\t/// <summary>\n\t\t/// Registers a clipboard format and returns its id. If already registered, just returns id.\n\t\t/// </summary>\n\t\t/// <param name=\"name\">Format name.</param>\n\t\t/// <param name=\"textEncoding\">Text encoding, if it's a text format. Used by <see cref=\"clipboardData.getText\"/>, <see cref=\"clipboardData.AddText\"/> and functions that call them. For example <see cref=\"Encoding.UTF8\"/>. If <c>null</c>, text of unknown formats is considered Unicode UTF-16 (no encoding/decoding needed).</param>\n\t\t/// <remarks>Calls API <ms>RegisterClipboardFormat</ms>.</remarks>\n\t\tpublic static int Register(string name, Encoding textEncoding = null) {\n\t\t\tvar R = Api.RegisterClipboardFormat(name);\n\t\t\tif (textEncoding != null && R != 0 && R != Html) s_textEncoding[R] = textEncoding;\n\t\t\treturn R;\n\t\t}\n\n\t\tstatic readonly ConcurrentDictionary<int, Encoding> s_textEncoding = new();\n\n\t\t/// <summary>\n\t\t/// Gets text encoding for format.\n\t\t/// Returns <c>null</c> if UTF-16 or if the format is unknown and not in <c>s_textEncoding</c>.\n\t\t/// </summary>\n\t\tinternal static Encoding GetTextEncoding_(int format, out bool unknown) {\n\t\t\tunknown = false;\n\t\t\tif (format is 0 or Api.CF_UNICODETEXT or Api.CF_HDROP) return null;\n\t\t\tif (format < Api.CF_MAX) return Encoding.Default; //never mind: it is UTF-8, not ANSI. Rarely used, especially with non-ASCII text.\n\t\t\tif (format == Html) return Encoding.UTF8;\n\t\t\tif (s_textEncoding.TryGetValue(format, out var enc)) return enc == Encoding.Unicode ? null : enc;\n\t\t\tunknown = true;\n\t\t\treturn null;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets clipboard format name.\n\t\t/// </summary>\n\t\t/// <param name=\"format\">A registered or standard clipboard format. If standard, returns string like <c>\"CF_BITMAP\"</c>.</param>\n\t\t/// <param name=\"orNull\">Return <c>null</c> if <i>format</i> is unknown. If <c>false</c>, returns <i>format</i> as string.</param>\n\t\t/// <remarks>\n\t\t/// Calls API <ms>GetClipboardFormatName</ms>. Although undocumented, it also can get other strings from the same system atom table, for example registered Windows message names and window class names.\n\t\t/// </remarks>\n\t\t[SkipLocalsInit]\n\t\tpublic static unsafe string GetName(int format, bool orNull = false) {\n\t\t\t//registered\n\t\t\tif (format >= 0xC000 && format <= 0xffff) {\n\t\t\t\tvar b = stackalloc char[300];\n\t\t\t\tint len = Api.GetClipboardFormatName(format, b, 300);\n\t\t\t\tif (len > 0) return new string(b, 0, len);\n\t\t\t}\n\t\t\t//standard\n\t\t\tvar s = format switch { Api.CF_TEXT => \"CF_TEXT\", Api.CF_BITMAP => \"CF_BITMAP\", Api.CF_METAFILEPICT => \"CF_METAFILEPICT\", Api.CF_SYLK => \"CF_SYLK\", Api.CF_DIF => \"CF_DIF\", Api.CF_TIFF => \"CF_TIFF\", Api.CF_OEMTEXT => \"CF_OEMTEXT\", Api.CF_DIB => \"CF_DIB\", Api.CF_PALETTE => \"CF_PALETTE\", Api.CF_RIFF => \"CF_RIFF\", Api.CF_WAVE => \"CF_WAVE\", Api.CF_UNICODETEXT => \"CF_UNICODETEXT\", Api.CF_ENHMETAFILE => \"CF_ENHMETAFILE\", Api.CF_HDROP => \"CF_HDROP\", Api.CF_LOCALE => \"CF_LOCALE\", Api.CF_DIBV5 => \"CF_DIBV5\", _ => null };\n\t\t\treturn s ?? (orNull ? null : format.ToS());\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets formats currently in the clipboard.\n\t\t/// </summary>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// foreach (var f in ClipFormats.EnumClipboard()) {\n\t\t/// \tprint.it(ClipFormats.GetName(f));\n\t\t/// }\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic static IEnumerable<int> EnumClipboard() {\n\t\t\tusing var oc = new clipboard.OpenClipboard_(true);\n\t\t\tfor (int format = 0; 0 != (format = Api.EnumClipboardFormats(format));) {\n\t\t\t\tyield return format;\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au/Input/inputBlocker.cs",
    "content": "namespace Au {\n\t/// <summary>\n\t/// Blocks keyboard and/or mouse input events from reaching applications.\n\t/// </summary>\n\t/// <remarks>\n\t/// Uses keyboard and/or mouse hooks. Does not use API <ms>BlockInput</ms>, it does not work on current Windows versions.\n\t/// Blocks hardware-generated events and software-generated events, except generated by functions of this library.\n\t/// Functions of this library that send keys or text use this class internally, to block user-pressed keys and resend them afterwards (see <see cref=\"ResendBlockedKeys\"/>).\n\t/// Does not block:\n\t/// - In windows of the same thread that started blocking. For example, if your script shows a message box, the user can click its buttons.\n\t/// - In windows of higher [](xref:uac) integrity level (IL) processes, unless this process has uiAccess IL.\n\t/// - In special desktops/screens, such as when you press <c>Ctrl+Alt+Delete</c> or launch an admin program that requires UAC elevation. See also <see cref=\"ResumeAfterCtrlAltDelete\"/>.\n\t/// - Some Windows hotkeys, such as <c>Ctrl+Alt+Delete</c> and <c>Win+L</c>.\n\t/// - Keyboard hooks don't work in windows of this process if this process uses direct input or raw input API.\n\t/// \n\t/// To stop blocking, can be used <c>using</c>, like in the example. Or <c>try</c> with <c>finally</c> code that calls <see cref=\"Dispose\"/> or <see cref=\"Stop\"/>. Also automatically stops when this thread ends. Users can stop with <c>Ctrl+Alt+Delete</c>.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// using(new inputBlocker(BIEvents.All)) {\n\t/// \tprint.it(\"blocked\");\n\t/// \t5.s();\n\t/// }\n\t/// print.it(\"not blocked\");\n\t/// ]]></code>\n\t/// </example>\n\tpublic sealed unsafe class inputBlocker : IDisposable {\n\t\tHandle_ _syncEvent, _stopEvent;\n\t\tHandle_ _threadHandle;\n\t\tkeys _blockedKeys;\n\t\tlong _startTime;\n\t\tBIEvents _block;\n\t\tint _threadId;\n\t\tbool _disposed;\n\t\tbool _discardBlockedKeys;\n\t\t\n\t\t//note: don't use API BlockInput because:\n\t\t//\tUAC. Fails if our process has Medium IL.\n\t\t//\tToo limited, eg cannot block only keys or only mouse.\n\t\t\n\t\t/// <summary>\n\t\t/// This constructor does nothing (does not call <see cref=\"Start\"/>).\n\t\t/// </summary>\n\t\tpublic inputBlocker() { }\n\t\t\n\t\t/// <summary>\n\t\t/// This constructor calls <see cref=\"Start\"/>.\n\t\t/// </summary>\n\t\t/// <exception cref=\"ArgumentException\"><i>what</i> is 0.</exception>\n\t\tpublic inputBlocker(BIEvents what) {\n\t\t\tStart(what);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Starts blocking.\n\t\t/// </summary>\n\t\t/// <exception cref=\"ArgumentException\"><i>what</i> is 0.</exception>\n\t\t/// <exception cref=\"InvalidOperationException\">Already started.</exception>\n\t\tpublic void Start(BIEvents what) {\n\t\t\tif (_disposed) throw new ObjectDisposedException(nameof(inputBlocker));\n\t\t\tif (_block != 0) throw new InvalidOperationException();\n\t\t\tif (!what.HasAny(BIEvents.All)) throw new ArgumentException();\n\t\t\t\n\t\t\t_block = what;\n\t\t\t_startTime = Environment.TickCount64;\n\t\t\t\n\t\t\t_syncEvent = Api.CreateEvent(false);\n\t\t\t_stopEvent = Api.CreateEvent(false);\n\t\t\t_threadHandle = Api.OpenThread(Api.SYNCHRONIZE, false, _threadId = Api.GetCurrentThreadId());\n\t\t\t\n\t\t\tThreadPool.QueueUserWorkItem(_this => (_this as inputBlocker)._ThreadProc(), this);\n\t\t\t//TODO3: what if thread pool is very busy? Eg if scripts use it incorrectly. Maybe better have own internal pool.\n\t\t\t\n\t\t\tApi.WaitForSingleObject(_syncEvent, Timeout.Infinite);\n\t\t\tGC.KeepAlive(this);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Calls <see cref=\"Stop\"/>.\n\t\t/// </summary>\n\t\tpublic void Dispose() {\n\t\t\tif (!_disposed) {\n\t\t\t\t_disposed = true;\n\t\t\t\tStop();\n\t\t\t\tGC.SuppressFinalize(this);\n\t\t\t}\n\t\t}\n\t\t\n\t\t///\n\t\t~inputBlocker() => _CloseHandles();\n\t\t\n\t\tvoid _CloseHandles() {\n\t\t\tif (!_syncEvent.Is0) {\n\t\t\t\t_syncEvent.Dispose();\n\t\t\t\t_stopEvent.Dispose();\n\t\t\t\t_threadHandle.Dispose();\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Stops blocking.\n\t\t/// Plays back blocked keys if need. See <see cref=\"ResendBlockedKeys\"/>.\n\t\t/// Does nothing if currently is not blocking.\n\t\t/// </summary>\n\t\t/// <param name=\"discardBlockedKeys\">Do not play back blocked key-down events recorded because of <see cref=\"ResendBlockedKeys\"/>.</param>\n\t\tpublic void Stop(bool discardBlockedKeys = false) {\n\t\t\tif (_block == 0) return;\n\t\t\t_block = 0;\n\t\t\t_discardBlockedKeys = discardBlockedKeys;\n\t\t\tApi.SetEvent(_stopEvent);\n\t\t\tApi.WaitForSingleObject(_syncEvent, Timeout.Infinite);\n\t\t\t_CloseHandles();\n\t\t}\n\t\t\n\t\tconst int c_maxResendTime = 10000;\n\t\t\n\t\tvoid _ThreadProc() {\n\t\t\tWindowsHook hk = null, hm = null; WinEventHook hwe = null;\n\t\t\ttry {\n\t\t\t\ttry {\n\t\t\t\t\tif (_block.Has(BIEvents.Keys))\n\t\t\t\t\t\thk = WindowsHook.Keyboard(_keyHookProc ??= _KeyHookProc);\n\t\t\t\t\tif (_block.HasAny(BIEvents.MouseClicks | BIEvents.MouseMoving))\n\t\t\t\t\t\thm = WindowsHook.Mouse(_mouseHookProc ??= _MouseHookProc);\n\t\t\t\t}\n\t\t\t\tcatch (AuException e1) { Debug_.Print(e1); _block = 0; return; } //failed to hook\n\t\t\t\t\n\t\t\t\t//This prevents occassional inserting a foreign key after the first our-script-pressed key.\n\t\t\t\t//To reproduce, let our script send small series of chars in loop, and simultaneously a foreign script send other chars.\n\t\t\t\twait.doEvents();\n\t\t\t\t\n\t\t\t\t//print.it(\"started\");\n\t\t\t\tApi.SetEvent(_syncEvent);\n\t\t\t\t\n\t\t\t\t//the hook detects Ctrl+Alt+Del, Win+L, UAC consent, etc. SystemEvents.SessionSwitch only Win+L.\n\t\t\t\ttry { hwe = new WinEventHook(EEvent.SYSTEM_DESKTOPSWITCH, 0, _winEventProc ??= _WinEventProc); }\n\t\t\t\tcatch (AuException e1) { Debug_.Print(e1); } //failed to hook\n\t\t\t\t\n\t\t\t\twait.Wait_(-1, WHFlags.DoEvents, new IntPtr[] { _stopEvent, _threadHandle });\n\t\t\t\t\n\t\t\t\tif (_blockedKeys != null) {\n\t\t\t\t\tbool onlyUp = _discardBlockedKeys || Environment.TickCount64 - _startTime > c_maxResendTime;\n\t\t\t\t\t_blockedKeys.SendBlocked_(onlyUp);\n\t\t\t\t}\n\t\t\t\t//print.it(\"ended\");\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\t_blockedKeys = null;\n\t\t\t\thk?.Dispose();\n\t\t\t\thm?.Dispose();\n\t\t\t\thwe?.Dispose();\n\t\t\t\tApi.SetEvent(_syncEvent);\n\t\t\t}\n\t\t\tGC.KeepAlive(this);\n\t\t}\n\t\t\n\t\tAction<HookData.Keyboard> _keyHookProc;\n\t\tAction<HookData.Mouse> _mouseHookProc;\n\t\tAction<HookData.WinEvent> _winEventProc;\n\t\t\n\t\tvoid _KeyHookProc(HookData.Keyboard x) {\n\t\t\tif (_DontBlock(x.IsInjected, x.dwExtraInfo, x.vkCode)) {\n\t\t\t\t//print.it(\"ok\", x.vkCode, !x.IsUp);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t//print.it(message, x.vkCode);\n\t\t\t\n\t\t\t//if(x.vkCode == KKey.Delete && !x.IsUp) {\n\t\t\t//\t//Could detect Ctrl+Alt+Del here. But SetWinEventHook(SYSTEM_DESKTOPSWITCH) is better.\n\t\t\t//}\n\t\t\t\n\t\t\tif (ResendBlockedKeys && Environment.TickCount64 - _startTime < c_maxResendTime) {\n\t\t\t\t//If Shift is set to turn off CapsLock, on Shift the hook receives LShift down and CapsLock down/up with no 'injected' flag, even if the Shift was pressed by a script.\n\t\t\t\t//If we resend them, the hook catches the resent keys, and the resent LShift creates an infinite loop (actually for 10 s, see c_maxResendTime).\n\t\t\t\t//Workaround: don't resend these keys if isCapsLock and the system setting is active.\n\t\t\t\t//Also SendBlocked_ prevents such infinite loop in any case.\n\t\t\t\tbool no = false;\n\t\t\t\tif (x.vkCode == KKey.CapsLock || (x.vkCode == KKey.LShift && !x.IsUp)) {\n\t\t\t\t\tno = keys.isCapsLock && keys.IsCapsLockShiftOff_();\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (!no) {\n\t\t\t\t\t_blockedKeys ??= new keys(null);\n\t\t\t\t\t//print.it(\"blocked\", x.vkCode, !x.IsUp, x.IsInjected);\n\t\t\t\t\t_blockedKeys.AddRaw_(x.vkCode, (ushort)x.scanCode, x.SendInputFlags_);\n\t\t\t\t}\n\t\t\t}\n\t\t\tx.BlockEvent();\n\t\t}\n\t\t\n\t\tvoid _MouseHookProc(HookData.Mouse x) {\n\t\t\tbool isMMove = x.Event == HookData.MouseEvent.Move;\n\t\t\tswitch (_block & (BIEvents.MouseClicks | BIEvents.MouseMoving)) {\n\t\t\tcase BIEvents.MouseClicks | BIEvents.MouseMoving: break;\n\t\t\tcase BIEvents.MouseClicks: if (isMMove) return; break;\n\t\t\tcase BIEvents.MouseMoving: if (!isMMove) return; break;\n\t\t\t}\n\t\t\tif (!_DontBlock(x.IsInjected, x.dwExtraInfo, 0, isMMove)) x.BlockEvent();\n\t\t}\n\t\t\n\t\tbool _DontBlock(bool isInjected, nint extraInfo, KKey vk = 0, bool isMMove = false) {\n\t\t\tif (_pause) return true;\n\t\t\tif (isInjected) {\n\t\t\t\t//if(DontBlockInjected || (extraInfo != default && extraInfo == DontBlockInjectedExtraInfo)) return true;\n\t\t\t\tif (DontBlockInjected) return true;\n\t\t\t}\n\t\t\twnd w;\n\t\t\tif (vk != 0) {\n\t\t\t\t//var a = DontBlockKeys;\n\t\t\t\t//if(a != null) foreach(var k in a) if(vk == k) return true;\n\t\t\t\tw = wnd.active;\n\t\t\t} else {\n\t\t\t\tw = isMMove ? wnd.active : wnd.fromMouse();\n\t\t\t\t//note: don't use hook's pt, because of a bug in some OS versions.\n\t\t\t\t//note: for wheel it's better to use FromMouse.\n\t\t\t}\n\t\t\tif (w.ThreadId == _threadId) return true;\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\tvoid _WinEventProc(HookData.WinEvent x) {\n\t\t\t//the hook is called before and after Ctrl+Alt+Del screen. Only idEventThread different.\n\t\t\t//\tGetForegroundWindow returns 0. WTSGetActiveConsoleSessionId returns main session.\n\t\t\t\n\t\t\t//print.it(\"desktop switch\"); //return;\n\t\t\t\n\t\t\t_startTime = 0; //don't resend Ctrl+Alt+Del and other blocked keys\n\t\t\tif (!ResumeAfterCtrlAltDelete)\n\t\t\t\tThreadPool.QueueUserWorkItem(_this => (_this as inputBlocker).Stop(), this);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Continue blocking when returned from a special screen where blocking is disabled: <c>Ctrl+Alt+Delete</c>, [](xref:uac) consent, etc.\n\t\t/// </summary>\n\t\tpublic bool ResumeAfterCtrlAltDelete { get; set; }\n\t\t\n\t\t/// <summary>\n\t\t/// Record blocked keys, and play back when stopped blocking.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Will not play back if: 1. The blocking time is > 10 seconds; then plays back only key-up events. 2. Detected <c>Ctrl+Alt+Delete</c>, [](xref:uac) consent or some other special screen. 3. Called <see cref=\"Pause\"/>.\n\t\t/// </remarks>\n\t\tpublic bool ResendBlockedKeys { get; set; }\n\t\t\n\t\t/// <summary>\n\t\t/// Don't block software-generated key/mouse events.\n\t\t/// If <c>false</c> (default), only events generated by functions of this library are not blocked.\n\t\t/// </summary>\n\t\tpublic bool DontBlockInjected { get; set; }\n\t\t\n\t\t//rejected. Will be added later if need. Maybe a callback instead.\n\t\t///// <summary>\n\t\t///// Don't block software-generated key/mouse events if this extra info value was set when calling API <ms>SendInput</ms>.\n\t\t///// </summary>\n\t\t///// <remarks>\n\t\t///// Regardless of the property value, events generated by functions of this library are never blocked.\n\t\t///// </remarks>\n\t\t//public nint DontBlockInjectedExtraInfo { get; set; }\n\t\t\n\t\t///// <summary>\n\t\t///// Don't block these keys.\n\t\t///// </summary>\n\t\t///// <remarks>\n\t\t///// For modifier keys use the left/right key code: <c>LCtrl</c>, <c>RCtrl</c>, <c>LShift</c>, <c>RShift</c>, <c>LAlt</c>, <c>RAlt</c>, <c>Win</c>, <c>RWin</c>.\n\t\t///// </remarks>\n\t\t//public static KKey[] DontBlockKeys { get; set; }\n\t\t\n\t\t/// <summary>\n\t\t/// Gets or sets whether the blocking is paused.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// The <c>set</c> function is much faster than <see cref=\"Stop\"/>/<see cref=\"Start\"/>. Does not remove hooks etc. Discards blocked keys.\n\t\t/// </remarks>\n\t\tpublic bool Pause {\n\t\t\tget => _pause;\n\t\t\tset {\n\t\t\t\t_pause = value;\n\t\t\t\t_startTime = 0; //don't resend blocked keys\n\t\t\t}\n\t\t}\n\t\tbool _pause;\n\t}\n}\n\nnamespace Au.Types {\n\t/// <summary>\n\t/// Used with <see cref=\"inputBlocker\"/> class to specify what user input types to block (keys, mouse).\n\t/// </summary>\n\t[Flags]\n\tpublic enum BIEvents {\n\t\t/// <summary>\n\t\t/// Do not block.\n\t\t/// </summary>\n\t\tNone,\n\t\t\n\t\t/// <summary>\n\t\t/// Block keys. Except if generated by functions of this library.\n\t\t/// </summary>\n\t\tKeys = 1,\n\t\t\n\t\t/// <summary>\n\t\t/// Block mouse clicks and wheel. Except if generated by functions of this library.\n\t\t/// </summary>\n\t\tMouseClicks = 2,\n\t\t\n\t\t/// <summary>\n\t\t/// Block mouse moving. Except if generated by functions of this library.\n\t\t/// </summary>\n\t\tMouseMoving = 4,\n\t\t\n\t\t/// <summary>\n\t\t/// Block keys, mouse clicks, wheel and mouse moving. Except if generated by functions of this library.\n\t\t/// This flag combines all other non-zero flags.\n\t\t/// </summary>\n\t\tAll = 7,\n\t}\n\t\n\t\n}\n"
  },
  {
    "path": "Au/Input/keys.cs",
    "content": "//TODO3: on exception release modifiers.\n\nnamespace Au;\n\n/// <summary>\n/// Keyboard functions. Send virtual keystrokes and text to the active window, get key state, wait for key.\n/// </summary>\n/// <remarks>\n/// The main function is <see cref=\"send\"/>. Most documentation is there. See also <see cref=\"sendt\"/>. These functions use <see cref=\"opt.key\"/>. Alternatively can be used <c>keys</c> variables, see <see cref=\"keys(OKey)\"/>.\n/// </remarks>\n/// <example>\n/// <code><![CDATA[\n/// keys.send(\"Ctrl+Shift+Left\"); //press Ctrl+Shift+Left\n/// \n/// opt.key.KeySpeed = 300; //set options for static functions\n/// keys.send(\"Ctrl+A Del Tab*3\", \"!text\", \"Enter\", 500); //press Ctrl+A, Del, Tab 3 times, send text, Enter, wait 500 ms\n/// \n/// keys.sendt(\"text\\r\\n\"); //send text that ends with newline\n/// ]]></code>\n/// </example>\npublic partial class keys {\n\t/// <param name=\"cloneOptions\">Options to be copied to <see cref=\"Options\"/> of this variable. Usually <c>opt.key</c> (current ambient options) or <c>null</c> (default options).</param>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// var k = new keys(null);\n\t/// k.Options.KeySpeed = 50;\n\t/// k.AddKeys(\"Tab // Space\").AddRepeat(3).AddText(\"text\").AddKey(KKey.Enter).AddSleep(500);\n\t/// k.SendNow(); //sends and clears the variable\n\t/// k.Add(\"Tab // Space*3\", \"!text\", KKey.Enter, 500); //the same as the above k.AddKeys... line\n\t/// for(int i = 0; i < 5; i++) k.SendNow(true); //does not clear the variable\n\t/// ]]></code>\n\t/// </example>\n\tpublic keys(OKey cloneOptions) { Options = new OKey(cloneOptions); }\n\n\t/// <summary>\n\t/// Options used by this variable.\n\t/// </summary>\n\tpublic OKey Options { get; }\n\n\t//KEYEVENTF_ flags for API SendInput.\n\t[Flags]\n\tenum _KFlags : byte {\n\t\tExtended = 1,\n\t\tUp = 2,\n\t\tUnicode = 4,\n\t\tScancode = 8,\n\t};\n\n\t//_KEvent type - key, text, sleep, etc.\n\tenum _KType : byte {\n\t\tKeyEvent, //send key down or up event, depending on _KFlags.Up. In _KEvent used vk and scan.\n\t\tKeyPair, //send key down and up events. In _KEvent used vk and scan.\n\t\tChar, //send character using keys. In _KEvent used ch.\n\t\tText, //send text. In _KEvent used data, it is _data or _data element index.\n\t\tCallback, //call callback function. In _KEvent used data, it is _data or _data element index.\n\t\tRepeat, //repeat previous key. In _KEvent used repeat.\n\t\tSleep, //sleep. In _KEvent used sleep.\n\t}\n\n\t[StructLayout(LayoutKind.Explicit)]\n\tstruct _KEvent {\n\t\t[FieldOffset(0)] internal KKey vk; //byte\n\t\t[FieldOffset(1)] byte _flags; //_KFlags in 0x0F and _KType in 0xF0\n\t\t[FieldOffset(2)] internal ushort scan; //scan code if IsKey\n\t\t[FieldOffset(2)] internal ushort data; //_data or _data index if IsText or IsCallback\n\t\t[FieldOffset(2)] internal ushort repeat; //repeat count if IsRepeat\n\t\t[FieldOffset(2)] internal ushort sleep; //milliseconds if IsSleep\n\t\t[FieldOffset(2)] internal ushort ch; //character if IsChar\n\n\t\t//Event type KeyEvent or KeyPair.\n\t\tinternal _KEvent(bool pair, KKey vk, _KFlags siFlags, ushort scan = 0) {\n\t\t\tthis.vk = vk;\n\t\t\tvar f = (byte)siFlags; if (pair) f |= 16; _flags = f;\n\t\t\tthis.scan = scan;\n\t\t}\n\n\t\t//Event of any type except KeyEvent and KeyPair.\n\t\tinternal _KEvent(_KType type, ushort data) {\n\t\t\tDebug.Assert(type > _KType.KeyPair);\n\t\t\t_flags = (byte)((byte)type << 4);\n\t\t\tthis.data = data;\n\t\t}\n\n\t\tinternal _KType Type => (_KType)(_flags >> 4);\n\t\tinternal bool IsPair => Type is _KType.KeyPair;\n\t\tinternal bool IsKey => Type <= _KType.KeyPair;\n\t\tinternal bool IsChar => Type == _KType.Char;\n\t\tinternal bool IsKeyOrChar => Type <= _KType.Char;\n\t\tinternal bool IsText => Type == _KType.Text;\n\t\tinternal bool IsCallback => Type == _KType.Callback;\n\t\tinternal bool IsRepeat => Type == _KType.Repeat;\n\t\tinternal bool IsSleep => Type == _KType.Sleep;\n\t\tinternal bool IsUp => 0 != (_flags & 2);\n\t\tinternal _KFlags SIFlags => (_KFlags)(_flags & 15);\n\t\tinternal void MakeDown() => _flags &= 9;\n\t\tinternal void MakeUp() => _flags = (byte)((_flags & 9) | 2);\n\n#if DEBUG\n\t\tpublic override string ToString() {\n\t\t\tif (IsText) { Debug.Assert(SIFlags == 0); return $\"text \" + data; }\n\t\t\tif (IsCallback) { Debug.Assert(SIFlags == 0); return $\"callback \" + data; }\n\t\t\tif (IsSleep) { Debug.Assert(SIFlags == 0); return \"sleep \" + sleep; }\n\t\t\tif (IsRepeat) { Debug.Assert(SIFlags == 0); return \"repeat \" + repeat; }\n\t\t\tif (IsChar) { Debug.Assert(SIFlags == 0); return \"char \" + ch; }\n\t\t\treturn $\"{vk,-12} scan={scan,-4} flags={_flags}\";\n\t\t}\n#endif\n\t}\n\n\t//This struct is used to separate parsing-only fields from other fields.\n\tstruct _KParsingState {\n\t\tpublic Stack<_KEvent> mod; //pushed on \"+\" or \"+(\". Then popped on key not preceded by +, and also in Send().\n\t\tpublic bool paren; //we are between \"+(\" and \")\"\n\t\tpublic bool plus; //we are between \"+\" and key or text\n\t}\n\n\t//This struct is used to separate sending-only fields from other fields.\n\tstruct _KSendingState {\n\t\tpublic wnd wFocus;\n\t\tpublic OKey options;\n\n\t\tpublic void Clear() {\n\t\t\twFocus = default;\n\t\t}\n\t}\n\n\treadonly List<_KEvent> _a = new(); //all key events and elements for each text/callback/repeat/sleep\n\tobject _data; //text and callback parts. If there is 1 such part, it is string or Action; else it is List<object>.\n\t_KParsingState _pstate; //parsing state\n\t_KSendingState _sstate; //sending state\n\tbool _sending; //while sending, don't allow to add or send\n\tbool? _antiCapsLock;\n\n\t/// <summary>\n\t/// Adds keystrokes to the internal collection. They will be sent by <see cref=\"SendNow\"/>.\n\t/// </summary>\n\t/// <returns>This.</returns>\n\t/// <param name=\"keys_\">\n\t/// [Key names and operators](xref:key_names), like with <see cref=\"send\"/>. Can be <c>null</c> or <c>\"\"</c>.\n\t/// Example: <c>\"Tab Ctrl+V Alt+(E P) Left*3 Space a , 5 #5\"</c>.\n\t/// If has prefix <c>\"!\"</c> or <c>\"%\"</c>, calls <see cref=\"AddText(string, string)\"/>; use <c>\"!\"</c> for text, <c>\"%\"</c> for HTML.\n\t/// </param>\n\t/// <exception cref=\"ArgumentException\">Error in <i>keys_</i> string, for example an unknown key name.</exception>\n\tpublic keys AddKeys([ParamString(PSFormat.Keys)] string keys_) {\n\t\t_ThrowIfSending();\n\t\tvar k = keys_;\n\t\tif (k.NE()) return this;\n\t\tif (k[0] == '!') return AddText(k[1..]);\n\t\tif (k[0] == '%') return AddText(null, k[1..]);\n\t\tint i = 0, len = 0;\n\t\tforeach (var g in _SplitKeysString(k)) {\n\t\t\t//print.it($\"<><c #C000>{g.Value}</c>\"); //continue;\n\t\t\ti = g.Start; len = g.Length;\n\t\t\tchar c = k[i]; _KEvent e;\n\t\t\tswitch (c) {\n\t\t\tcase '*':\n\t\t\t\tif (len == 1 || _a.Count == 0) goto ge;\n\t\t\t\te = _a[^1];\n\t\t\t\tchar cLast = k[i + len - 1];\n\t\t\t\tswitch (cLast) {\n\t\t\t\tcase 'n': //down\n\t\t\t\tcase 'p': //up\n\t\t\t\t\tif (e.IsPair) {\n\t\t\t\t\t\t//make the last key down-only or up-only\n\t\t\t\t\t\tif (cLast == 'p') e.MakeUp(); else e.MakeDown();\n\t\t\t\t\t\t_a[^1] = e;\n\t\t\t\t\t} else if (cLast == 'p' && _FindLastKey(out e)) {\n\t\t\t\t\t\t//allow eg Key(\"A*down*3*up\") or Key(\"A*down\", 500, \"*up\")\n\t\t\t\t\t\te.MakeUp();\n\t\t\t\t\t\t_a.Add(e);\n\t\t\t\t\t} else goto ge;\n\t\t\t\t\tbreak;\n\t\t\t\tdefault: //repeat\n\t\t\t\t\tif (!e.IsKeyOrChar) goto ge;\n\t\t\t\t\tAddRepeat(k.ToInt(i + 1));\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase '+':\n\t\t\t\tif (_pstate.paren || _a.Count == 0) goto ge;\n\t\t\t\te = _a[^1];\n\t\t\t\tif (!e.IsPair) goto ge;\n\t\t\t\te.MakeDown();\n\t\t\t\t_a[^1] = e;\n\t\t\t\te.MakeUp();\n\t\t\t\t_pstate.mod ??= new Stack<_KEvent>();\n\t\t\t\t_pstate.mod.Push(e);\n\t\t\t\tif (len > 1) _pstate.paren = true; //\"*(\"\n\t\t\t\telse _pstate.plus = true;\n\t\t\t\tbreak;\n\t\t\tcase ')':\n\t\t\t\tif (!_pstate.paren) goto ge;\n\t\t\t\t_pstate.paren = false;\n\t\t\t\t_AddModUp();\n\t\t\t\tbreak;\n\t\t\tcase '_' when len == 2:\n\t\t\t\tAddChar(k[i + 1]);\n\t\t\t\tbreak;\n\t\t\tcase '^':\n\t\t\t\tif (_pstate.paren) goto ge;\n\t\t\t\tif (++i == g.End) break;\n\t\t\t\tif (g.End - i == 1 || _pstate.plus) {\n\t\t\t\t\twhile (i < g.End) AddChar(k[i++]);\n\t\t\t\t} else { //avoid eg Shift up/down between AB\n\t\t\t\t\t_AddTextAndHow(k[i..], OKeyText.KeysOrChar, true);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t//case '!': //rejected. Too many rules. Better slightly longer code than 2 ways to do the same.\n\t\t\t//\tAddText(k[++i..]);\n\t\t\t//\tbreak;\n\t\t\tdefault:\n\t\t\t\t//rejected: if non-ASCII, use AddChar. Why to add yet another rule for something rarely used.\n\t\t\t\tvar vk = _KeynameToKey(k, i, len);\n\t\t\t\tif (vk == 0) goto ge;\n\t\t\t\tAddKey(vk);\n\t\t\t\t//print.it(vk);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn this;\n\t\tge: throw _ArgumentException_ErrorInKeysString(k, i, len);\n\n\t\tbool _FindLastKey(out _KEvent e) {\n\t\t\tfor (int j = _a.Count; --j >= 0;) {\n\t\t\t\tvar t = _a[j];\n\t\t\t\tif (t.IsKey) { e = t; return true; }\n\t\t\t}\n\t\t\te = default; return false;\n\t\t}\n\t}\n\n\t//Adds mod up events if need: if _parsing.mod is not empty and + is not active and ( is not active.\n\tvoid _AddModUp() {\n\t\tif (!_pstate.plus && !_pstate.paren && _pstate.mod != null) {\n\t\t\twhile (_pstate.mod.Count != 0) _a.Add(_pstate.mod.Pop());\n\t\t}\n\t}\n\n\t//Adds key or other event. Calls _ModUp(). Not used fo sleep and repeat.\n\tkeys _AddKEvent(_KEvent e) {\n\t\t_AddModUp();\n\t\t_pstate.plus = false;\n\t\t_a.Add(e);\n\t\treturn this;\n\t}\n\n\t/// <summary>\n\t/// Adds single key, specified as <see cref=\"KKey\"/>, to the internal collection. It will be sent by <see cref=\"SendNow\"/>.\n\t/// </summary>\n\t/// <returns>This.</returns>\n\t/// <param name=\"key\">Virtual-key code, like <c>KKey.Tab</c> or <c>(KKey)200</c>. Valid values are 1-255.</param>\n\t/// <param name=\"down\"><c>true</c> - key down; <c>false</c> - key up; <c>null</c> (default) - key down-up.</param>\n\t/// <exception cref=\"ArgumentException\">Invalid <i>key</i> (0).</exception>\n\tpublic keys AddKey(KKey key, bool? down = null) {\n\t\t_ThrowIfSending();\n\t\tif (key == 0) throw new ArgumentException(\"Invalid value.\", nameof(key));\n\n\t\t_KFlags f = 0;\n\t\tif (down == false) f |= _KFlags.Up;\n\t\tif (KeyTypes_.IsExtended(key)) f |= _KFlags.Extended;\n\n\t\treturn _AddKEvent(new _KEvent(down == null, key, f));\n\t}\n\n\t/// <summary>\n\t/// Adds single key to the internal collection. Allows to specify scan code and whether it is an extended key. It will be sent by <see cref=\"SendNow\"/>.\n\t/// </summary>\n\t/// <returns>This.</returns>\n\t/// <param name=\"key\">Virtual-key code, like <c>KKey.Tab</c> or <c>(KKey)200</c>. Valid values are 1-255. Can be 0.</param>\n\t/// <param name=\"scanCode\">Scan code of the physical key. Scan code values are 1-127, but this function allows 1-0xffff. Can be 0.</param>\n\t/// <param name=\"extendedKey\"><c>true</c> if the key is an extended key.</param>\n\t/// <param name=\"down\"><c>true</c> - key down; <c>false</c> - key up; <c>null</c> (default) - key down-up.</param>\n\t/// <exception cref=\"ArgumentException\">Invalid scan code.</exception>\n\tpublic keys AddKey(KKey key, ushort scanCode, bool extendedKey, bool? down = null) {\n\t\t_ThrowIfSending();\n\t\t_KFlags f = 0;\n\t\tif (key == 0) f = _KFlags.Scancode;\n\t\telse {\n\t\t\t//don't: if extendedKey false, set true if need. Don't do it because this func is 'raw'.\n\t\t}\n\n\t\tif (down == false) f |= _KFlags.Up;\n\t\tif (extendedKey) f |= _KFlags.Extended;\n\n\t\treturn _AddKEvent(new _KEvent(down == null, key, f, scanCode));\n\t}\n\n\t/// <summary>\n\t/// Adds single character to the internal collection. It will be sent like text with option <see cref=\"OKeyText.KeysOrChar\"/>.\n\t/// </summary>\n\t/// <returns>This.</returns>\n\tpublic keys AddChar(char c) {\n\t\t_ThrowIfSending();\n\t\treturn _AddKEvent(new _KEvent(_KType.Char, c));\n\t}\n\n\n\t/// <summary>\n\t/// Adds key down or up event.\n\t/// </summary>\n\t/// <param name=\"vk\"></param>\n\t/// <param name=\"scan\"></param>\n\t/// <param name=\"siFlags\"><c>SendInput</c> flags.</param>\n\tinternal keys AddRaw_(KKey vk, ushort scan, byte siFlags) {\n\t\t_ThrowIfSending();\n\t\treturn _AddKEvent(new _KEvent(false, vk, (_KFlags)(siFlags & 0xf), scan));\n\t}\n\n\t/// <summary>\n\t/// Sends key events added by <c>AddRaw_</c> (called by <see cref=\"inputBlocker\"/>).\n\t/// Simply calls <c>Api.SendInput</c>. No options, no sleep, etc. No exceptions.\n\t/// If new events added while sending, sends them too, until there are no new events added.\n\t/// </summary>\n\t/// <param name=\"onlyUp\">Send only \"up\" events.</param>\n\tinternal unsafe void SendBlocked_(bool onlyUp) {\n\t\tfor (int ii = 0; ii < 5; ii++) {\n\t\t\tint n = 0;\n\t\t\tvar a = new Api.INPUTK[_a.Count];\n\t\t\tfor (int i = 0; i < _a.Count; i++) {\n\t\t\t\tvar k = _a[i];\n\t\t\t\tif (onlyUp && !k.IsUp) continue;\n\t\t\t\ta[n++].Set(k.vk, k.scan, (uint)k.SIFlags);\n\t\t\t}\n\t\t\t_a.Clear();\n\t\t\tif (n == 0) return;\n\t\t\tfixed (Api.INPUTK* p = a) Api.SendInput(p, n, dontThrow: true);\n\t\t\t//wait.doEvents(); //sometimes catches one more event, but not necessary\n\n\t\t\tif (_a.Count == 0) break;\n\t\t\tDebug_.PrintIf(ii == 4, \"loop?\");\n\t\t\t//The hook proc is called while in SendInput. If we don't retry, new blocked keys are lost.\n\t\t\t//\tBut don't retry forever, because in some cases OS injects keys and the hook receives them not marked as injected, eg on Shift if it is set to turn off CapsLock.\n\t\t}\n\t}\n\n\t/// <summary>\n\t/// Adds text or HTML. It will be sent by <see cref=\"SendNow\"/>.\n\t/// </summary>\n\t/// <returns>This.</returns>\n\t/// <remarks>\n\t/// To send text can use keys, characters or clipboard, depending on <see cref=\"opt.key\"/> and text. If <i>html</i> not <c>null</c>, uses clipboard.\n\t/// </remarks>\n\t/// <inheritdoc cref=\"sendt\" path=\"/param\"/>\n\tpublic keys AddText(string text, string html = null) {\n\t\t_ThrowIfSending();\n\t\tif (!html.NE()) {\n\t\t\tvar data = new clipboardData().AddHtml(html).AddText(text ?? html);\n\t\t\tvar ke = new _KEvent(_KType.Text, _SetData(data));\n\t\t\t_AddKEvent(ke);\n\t\t} else if (!text.NE()) {\n\t\t\tvar ke = new _KEvent(_KType.Text, _SetData(text));\n\t\t\t_AddKEvent(ke);\n\t\t}\n\t\treturn this;\n\t}\n\n\t/// <summary>\n\t/// Adds text with explicitly specified sending method (keys, characters or paste).\n\t/// </summary>\n\t/// <returns>This.</returns>\n\t/// <param name=\"text\">Text. Can be <c>null</c>.</param>\n\t/// <param name=\"how\">Overrides <see cref=\"OKey.TextHow\"/>.</param>\n\tpublic keys AddText(string text, OKeyText how) {\n\t\t_ThrowIfSending();\n\t\t_AddTextAndHow(text, how, false);\n\t\treturn this;\n\t}\n\n\tvoid _AddTextAndHow(string text, OKeyText how, bool keysArg) {\n\t\tif (!text.NE()) {\n\t\t\tint flags = (int)how | 0x80; if (keysArg) flags |= 0x40;\n\t\t\tvar ke = new _KEvent(_KType.Text, _SetData(text)) { vk = (KKey)flags };\n\t\t\t_AddKEvent(ke);\n\t\t}\n\t}\n\n\t/// <summary>\n\t/// Adds clipboard data, for example several formats. It will be pasted by <see cref=\"SendNow\"/>.\n\t/// </summary>\n\t/// <returns>This.</returns>\n\t/// <param name=\"cd\">Clipboard data.</param>\n\tpublic keys AddClipboardData(clipboardData cd) {\n\t\tNot_.Null(cd);\n\t\t_ThrowIfSending();\n\t\t_AddKEvent(new _KEvent(_KType.Text, _SetData(cd)));\n\t\treturn this;\n\t}\n\n\t//Adds text (string) or clipboardData or callback (Action) to _data.\n\tushort _SetData(object x) {\n\t\tint i;\n\t\tif (_data == null) {\n\t\t\ti = 0;\n\t\t\t_data = x;\n\t\t} else if (_data is List<object> a) {\n\t\t\ti = a.Count;\n\t\t\ta.Add(x);\n\t\t} else {\n\t\t\ti = 1;\n\t\t\t_data = new List<object>() { _data, x };\n\t\t}\n\t\treturn checked((ushort)i);\n\t}\n\n\t//Gets text (string) or clipboardData or callback (Action) from _data.\n\tobject _GetData(ushort i) {\n\t\tif (_data is List<object> a) return a[i];\n\t\treturn _data;\n\t}\n\n\t/// <summary>\n\t/// Adds a callback function.\n\t/// </summary>\n\t/// <returns>This.</returns>\n\t/// <param name=\"a\"></param>\n\t/// <remarks>\n\t/// The callback function will be called by <see cref=\"SendNow\"/> and can do anything except sending keys and copy/paste.\n\t/// </remarks>\n\tpublic keys AddAction(Action a) {\n\t\tNot_.Null(a);\n\t\t_ThrowIfSending();\n\t\treturn _AddKEvent(new _KEvent(_KType.Callback, _SetData(a)));\n\t}\n\n\t/// <summary>\n\t/// Adds the repeat operator. Then <see cref=\"SendNow\"/> will send the last added key or character <i>count</i> times.\n\t/// </summary>\n\t/// <returns>This.</returns>\n\t/// <param name=\"count\">The repeat count.</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\"><i>count</i> >10000 or &lt;0.</exception>\n\t/// <exception cref=\"ArgumentException\">The last added item is not a key or single character.</exception>\n\tpublic keys AddRepeat(int count) {\n\t\t_ThrowIfSending();\n\t\tif ((uint)count > 10000) throw new ArgumentOutOfRangeException(nameof(count), \"Max repeat count is 10000.\");\n\t\tint i = _a.Count; if (i == 0 || !_a[i - 1].IsKeyOrChar) throw new ArgumentException(\"No key to repeat.\");\n\t\t_a.Add(new _KEvent(_KType.Repeat, (ushort)count));\n\t\treturn this;\n\t}\n\n\t/// <summary>\n\t/// Adds a short pause. Then <see cref=\"SendNow\"/> will sleep (wait).\n\t/// </summary>\n\t/// <returns>This.</returns>\n\t/// <param name=\"timeMS\">Time to sleep, milliseconds.</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\"><i>timeMS</i> >10000 (1 minute) or &lt;0.</exception>\n\tpublic keys AddSleep(int timeMS) {\n\t\t_ThrowIfSending();\n\t\tif ((uint)timeMS > 10000) throw new ArgumentOutOfRangeException(nameof(timeMS), \"Max sleep time is 10000.\");\n\t\t_a.Add(new _KEvent(_KType.Sleep, (ushort)timeMS));\n\t\treturn this;\n\t}\n\n\t/// <summary>\n\t/// Adds keystrokes, text, sleep and other events to the internal collection. They will be sent/executed by <see cref=\"SendNow\"/>.\n\t/// </summary>\n\t/// <returns>This.</returns>\n\t/// <inheritdoc cref=\"keys.send\" path=\"/param\"/>\n\tpublic keys Add([ParamString(PSFormat.Keys)] params KKeysEtc[] keysEtc) {\n\t\t_ThrowIfSending();\n\t\tif (keysEtc != null) {\n\t\t\tfor (int i = 0; i < keysEtc.Length; i++) {\n\t\t\t\tvar o = keysEtc[i].Value ?? \"\";\n\t\t\t\tswitch (o) {\n\t\t\t\tcase string s:\n\t\t\t\t\tAddKeys(s);\n\t\t\t\t\tbreak;\n\t\t\t\tcase clipboardData cd:\n\t\t\t\t\tAddClipboardData(cd);\n\t\t\t\t\tbreak;\n\t\t\t\tcase KKey k:\n\t\t\t\t\tAddKey(k);\n\t\t\t\t\tbreak;\n\t\t\t\tcase char c:\n\t\t\t\t\tAddChar(c);\n\t\t\t\t\tbreak;\n\t\t\t\tcase int ms:\n\t\t\t\t\tAddSleep(ms);\n\t\t\t\t\tbreak;\n\t\t\t\tcase Action g:\n\t\t\t\t\tAddAction(g);\n\t\t\t\t\tbreak;\n\t\t\t\tcase KKeyScan t:\n\t\t\t\t\tAddKey(t.vk, t.scanCode, t.extendedKey);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn this;\n\t}\n\n\t/// <summary>\n\t/// Sends keys, text and executes other events added with the <c>AddX</c> functions.\n\t/// </summary>\n\t/// <param name=\"canSendAgain\">Don't clear the internal collection. If <c>true</c>, this function then can be called again (eg in loop) to send/execute the same keys etc. If <c>false</c> (default), clears the added keys etc; then you can call <c>AddX</c> functions and <c>SendNow</c> again.</param>\n\t/// <exception cref=\"ArgumentException\"><i>canSendAgain</i> is <c>true</c> and <i>keys_</i> end with <c>+</c> or <c>(</c>.</exception>\n\t/// <exception cref=\"AuException\">Failed. For example there is no focused window when sending text.</exception>\n\t/// <exception cref=\"InputDesktopException\"></exception>\n\tpublic void SendNow(bool canSendAgain = false) {\n\t\t//note: the \"Now\" in the name is just to make it different from the static function send(). If named Send, problems with DocFX etc.\n\n\t\t_ThrowIfSending();\n\t\tif (_a.Count == 0) return;\n\t\tif (canSendAgain) {\n\t\t\tif (_pstate.paren || _pstate.plus) throw new ArgumentException(\"canSendAgain cannot be true if keys_ ends with + or (\");\n\t\t}\n\n\t\t//print.it(\"-- _parsing.mod --\");\n\t\t//print.it(_parsing.mod);\n\n\t\t_AddModUp(); //add mod-up events if need, eg Ctrl-up after \"Ctrl+A\"\n\n\t\t//print.it(\"-- _a --\");\n\t\t//print.it(_a);\n\n\t\t//perf.first();\n\t\tint sleepFinally = 0;\n\t\tvar bi = new inputBlocker() { ResendBlockedKeys = true };\n\t\ttry {\n\t\t\t_sending = true;\n\t\t\t_antiCapsLock = Options.NoCapsOff || !isCapsLock ? false : null;\n\t\t\t//print.it(\"{\");\n\t\t\tif (!Options.NoBlockInput) bi.Start(BIEvents.Keys);\n\t\t\tif (!Options.NoModOff) Internal_.ReleaseModAndDisableModMenu();\n\t\t\t//perf.next();\n\t\t\tfor (int i = 0; i < _a.Count; i++) {\n\t\t\t\tvar k = _a[i];\n\t\t\t\tswitch (k.Type) {\n\t\t\t\tcase _KType.Sleep:\n\t\t\t\t\tif (i == _a.Count - 1) sleepFinally = k.sleep;\n\t\t\t\t\telse Internal_.Sleep(k.sleep);\n\t\t\t\t\tbreak;\n\t\t\t\tcase _KType.Repeat:\n\t\t\t\t\tDebug.Assert(i > 0 && _a[i - 1].IsKeyOrChar);\n\t\t\t\t\tbreak;\n\t\t\t\tcase _KType.Callback:\n\t\t\t\t\t(_GetData(k.data) as Action)();\n\t\t\t\t\tbreak;\n\t\t\t\tcase _KType.Char:\n\t\t\t\t\t_SendChar(k, i);\n\t\t\t\t\tbreak;\n\t\t\t\tcase _KType.Text:\n\t\t\t\t\t_SendText(k);\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\t_SendKey(k, i);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\t//perf.next();\n\t\t\tsleepFinally += GetOptionsAndWndFocused_(getWndAlways: false).optk.SleepFinally;\n\t\t}\n\t\tfinally {\n\t\t\tif (_antiCapsLock == true && !isCapsLock) Internal_.SendKey(KKey.CapsLock);\n\t\t\t_antiCapsLock = null;\n\t\t\t_sending = false;\n\t\t\tbi.Dispose();\n\t\t\t//perf.nw();\n\t\t\t//print.it(\"}\");\n\n\t\t\t//if canSendAgain, can be used like: AddX(); for(...) Send();\n\t\t\t//else can be used like: AddX(); Send(); AddX(); Send();\n\t\t\tif (!canSendAgain) {\n\t\t\t\t_a.Clear();\n\t\t\t\t_data = null;\n\t\t\t\t_sstate.Clear();\n\t\t\t\t//and don't clear _pstate\n\t\t\t}\n\t\t}\n\n\t\tif (sleepFinally > 0) Internal_.Sleep(sleepFinally);\n\n\t\t//_SyncWait();\n\t\t//CONSIDER: instead of SleepFinally use TimeSyncFinally, default 100 ms. Eg send a sync key and wait max TimeSyncFinally ms.\n\t\t//\tDon't sync after each (or some) sent key. Usually it does not make sense. The final sync/sleep is useful if next statement is not an input function.\n\t\t//Sync problems:\n\t\t//\tTried many ways, nothing is good enough. The test code now is in the \"Unused\" project.\n\t\t//\tThe best would be non-LL keyboard hook that sets event when receives our sent special key-up. Especially when combined with 'get thread CPU usage' while waiting for the event. However these hooks don't work eg in Store apps.\n\t\t//Better add a Sync function (keys.sync) or/and special key name, let users do it explicitly where need.\n\t}\n\n#if !DEBUG\n\t/// <summary>\n\t/// Deprecated. Use <see cref=\"SendNow\"/>.\n\t/// </summary>\n\t[EditorBrowsable(EditorBrowsableState.Never)]\n\tpublic void Send(bool canSendAgain = false) => SendNow(canSendAgain);\n#endif\n\n\tunsafe void _SendKey(_KEvent k, int i) {\n\t\tbool needScanCode = k.scan == 0 && !k.SIFlags.HasAny(_KFlags.Scancode | _KFlags.Unicode);\n\t\tvar (optk, wFocus) = GetOptionsAndWndFocused_(getWndAlways: needScanCode);\n\t\tif (needScanCode) {\n\t\t\tvar hkl = Api.GetKeyboardLayout(wFocus.ThreadId); //most layouts have the same standard scancodes, but eg dvorak different\n\t\t\tk.scan = Internal_.VkToSc(k.vk, hkl);\n\t\t}\n\n\t\tif (_antiCapsLock == null\n\t\t\t&& !k.SIFlags.Has(_KFlags.Unicode)\n\t\t\t&& k.vk is (>= KKey.A and <= KKey.Z) or (>= KKey.D0 and <= KKey.D9) or (>= KKey.OemSemicolon and <= KKey.OemTilde) or (>= KKey.OemOpenBrackets and <= KKey.OemQuotes)\n\t\t\t//CONSIDER: not if with a modifier\n\t\t\t) _AntiCapsLock();\n\n\t\tbool isLast = i == _a.Count - 1;\n\t\t_SendKey2(k, isLast ? default : _a[i + 1], isLast, optk);\n\t}\n\n\t//Caller should set k.scan; this func doesn't.\n\tunsafe static void _SendKey2(_KEvent k, _KEvent kNext, bool isLast, OKey optk) {\n\t\tvar ki = new Api.INPUTK(k.vk, k.scan, (uint)k.SIFlags);\n\n\t\tint count = 1, sleep = optk.KeySpeed;\n\t\tif (isLast) {\n\t\t\tif (!k.IsPair) sleep = Internal_.LimitSleepTime(sleep) - optk.SleepFinally;\n\t\t} else {\n\t\t\tif (kNext.IsRepeat) count = kNext.repeat;\n\t\t\telse if (!k.IsPair) {\n\t\t\t\t//If this is pair, sleep between down and up, and don't sleep after up.\n\t\t\t\t//Else if repeat, sleep always.\n\t\t\t\t//Else in most cases don't need to sleep. In some cases need, but can limit the time.\n\t\t\t\t//\tFor example, in Ctrl+C normally would not need to sleep after Ctrl down and Ctrl up.\n\t\t\t\t//\tHowever some apps/controls then may not work. Maybe they process mod and nonmod keys somehow async.\n\t\t\t\t//\tFor example, Ctrl+C in IE address bar often does not work if there is no sleep after Ctrl down. Always works if 1 ms.\n\n\t\t\t\tsleep = Internal_.LimitSleepTime(sleep);\n\t\t\t\tif (kNext.IsKey) {\n\t\t\t\t\tbool thisMod = KeyTypes_.IsMod(k.vk), nextMod = KeyTypes_.IsMod(kNext.vk);\n\t\t\t\t\tif (!k.IsUp) {\n\t\t\t\t\t\tif (kNext.IsUp) sleep = optk.KeySpeed;\n\t\t\t\t\t\telse if (thisMod == nextMod) sleep = 0;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (!thisMod || nextMod) sleep = 0;\n\t\t\t\t\t}\n\t\t\t\t} else if (kNext.IsSleep) sleep -= kNext.sleep;\n\t\t\t}\n\t\t}\n\t\tif (sleep < 0) sleep = 0;\n\n\t\t//var s = (k.vk).ToString();\n\t\t//if (k.IsPair) print.it($\"{s}<{sleep}>\");\n\t\t//else { var ud = k.IsUp ? '-' : '+'; if (sleep > 0) print.it($\"{s}{ud} {sleep}\"); else print.it($\"{s}{ud}\"); }\n\n\t\tfor (int r = 0; r < count; r++) {\n\t\t\t//perf.first();\n\t\t\tApi.SendInput(&ki);\n\t\t\t//perf.next();\n\t\t\tif (sleep > 0) {\n\t\t\t\tInternal_.Sleep(sleep);\n\t\t\t}\n\t\t\tif (k.IsPair) {\n\t\t\t\tki.dwFlags |= Api.KEYEVENTF_KEYUP;\n\t\t\t\tApi.SendInput(&ki);\n\t\t\t\tki.dwFlags &= ~Api.KEYEVENTF_KEYUP;\n\t\t\t}\n\t\t\t//perf.nw();\n\t\t\t//speed: min 400 mcs for each event. Often > 1000. Does not depend on whether all events sent by single SendInput call.\n\t\t}\n\t}\n\n\tunsafe void _SendChar(_KEvent ke, int i) {\n\t\tvar (optk, wFocus) = GetOptionsAndWndFocused_(getWndAlways: true, requireFocus: true);\n\t\tnint hkl = Api.GetKeyboardLayout(wFocus.ThreadId);\n\t\tif (_antiCapsLock == null) _AntiCapsLock();\n\t\tint count = 1; if (i < _a.Count - 1 && _a[i + 1].IsRepeat) count = _a[i + 1].repeat;\n\t\tint speed = optk.KeySpeed; if (count > 4) speed = Math.Min(speed, optk.TextSpeed + 2);\n\t\tKMod prevMod = 0;\n\t\ttry {\n\t\t\t_SendChar2((char)ke.ch, OKeyText.KeysOrChar, speed, count, hkl, ref prevMod, false); //note: don't use optk.TextShiftEnter\n\t\t}\n\t\tfinally {\n\t\t\tInternal_.ModPressRelease(false, prevMod);\n\t\t}\n\t}\n\n\tstatic unsafe void _SendChar2(char c, OKeyText textHow, int sleep, int count, nint hkl, ref KMod prevMod, bool shiftEnter) {\n\t\tKKey vk = 0; KMod mod = 0;\n\t\tif (c is '\\n' or '\\r') { //many apps don't support these as VK_PACKET\n\t\t\tvk = KKey.Enter;\n\t\t\tif (shiftEnter) mod = KMod.Shift;\n\t\t} else if (c is ' ' or '\\t') { //some apps don't support these as VK_PACKET\n\t\t\tvk = (KKey)c;\n\t\t} else if (textHow != OKeyText.Characters) {\n\t\t\t(vk, mod) = _CharToKey(c, hkl);\n\t\t\t//print.it(c, vk, mod, (ushort)km);\n\t\t}\n\n\t\tif (vk == 0) { //use vk_packet\n\t\t\tif (prevMod != 0) { Internal_.ModPressRelease(false, prevMod); prevMod = 0; }\n\n\t\t\t//note: need key-up event for VK_PACKET too.\n\t\t\t//\tKnown controls that need it: Qt edit controls; Office 2003 'type question' field.\n\t\t} else if (mod != prevMod) {\n\t\t\tvar md = mod ^ prevMod;\n\t\t\tif (0 != (md & KMod.Ctrl)) Internal_.SendCtrl(0 != (mod & KMod.Ctrl));\n\t\t\tif (0 != (md & KMod.Alt)) Internal_.SendAlt(0 != (mod & KMod.Alt));\n\t\t\tif (0 != (md & KMod.Shift)) Internal_.SendShift(0 != (mod & KMod.Shift));\n\t\t\tprevMod = mod;\n\t\t\tif (sleep > 0) Internal_.Sleep(Internal_.LimitSleepTime(sleep)); //need for apps that process mod-nonmod keys async\n\t\t}\n\n\t\tvar ki = new _INPUTKEY2(vk, vk == 0 ? c : Internal_.VkToSc(vk, hkl), vk == 0 ? Api.KEYEVENTF_UNICODE : 0);\n\t\tfor (int r = 0; r < count; r++) {\n\t\t\tApi.SendInput(&ki.k0, sleep > 0 ? 1 : 2);\n\t\t\tif (sleep > 0) {\n\t\t\t\tInternal_.Sleep(sleep);\n\t\t\t\tApi.SendInput(&ki.k1, 1);\n\t\t\t}\n\t\t}\n\t\t//rejected: try to synchronize somehow. To work better with slow and badly synchronized apps.\n\t\t//1. SendTimeout(WM_NULL). Although makes slower, usually does not make sync.\n\t\t//2. Sleep if the process uses CPU eg >50% of time. Tooo slow, even with Notepad. Tried GetProcessTimes and QueryProcessCycleTime (precise).\n\t\t//Eg UWP input processing is so slow and chaotic, impossible to sync.\n\t\t//rejected: option to sleep 1 ms every n-th char (eg use float 0...1 or negative value). Nothing good.\n\t}\n\n\tstatic (KKey vk, KMod mod) _CharToKey(char c, nint hkl) {\n\t\tshort km = Api.VkKeyScanEx(c, hkl); //note: call for non-ASCII char too; depending on keyboard layout it can succeed\n\t\tif (0 != (km & 0xf800)) return default; //-1 if failed, mod flag 8 Hankaku key, 16/32 reserved for driver\n\t\treturn ((KKey)(km & 0xff), (KMod)(km >> 8));\n\t}\n\n\tunsafe void _SendText(_KEvent ke) {\n\t\tvar (optk, wFocus) = GetOptionsAndWndFocused_(getWndAlways: true, requireFocus: true);\n\t\tobject data = _GetData(ke.data); //string or clipboardData\n\t\tstring s = data as string;\n\n\t\tOKeyText textHow; int flags = (byte)ke.vk;\n\t\tbool textHowSpecified = 0 != (flags & 0x80); //\"^text\" or AddText(string, OKeyText)\n\t\tif (textHowSpecified) textHow = (OKeyText)(flags & 0xf);\n\t\telse if (s != null && s.Length < optk.PasteLength) textHow = optk.TextHow;\n\t\telse textHow = OKeyText.Paste;\n\n\t\tif (textHow != OKeyText.Paste) {\n\t\t\t//use paste if there are Unicode surrogate pairs, because some apps/controls/frameworks don't support surrogates with WM_PACKET.\n\t\t\t//known apps that support: standard Edit and RichEdit controls, Chrome, Firefox, IE, WPF, WinForms, new Scintilla, Dreamweaver, LibreOffice.\n\t\t\t//known apps that don't: Office 2003, OpenOffice, old Scintilla.\n\t\t\t//known apps that don't if 0 sleep: QT edit controls in VirtualBox.\n\t\t\t//known apps that don't support these chars even when pasting: Java (tested the old and new frameworks).\n\t\t\t//tested: the same if SendInput(arrayOfAllChars).\n\t\t\tfor (int i = 0; i < s.Length; i++) if ((s[i] & 0xf800) == 0xd800) { textHow = OKeyText.Paste; break; }\n\t\t}\n\n\t\tnint hkl = 0;\n\t\tif (textHow is OKeyText.KeysOrChar or OKeyText.KeysOrPaste) {\n\t\t\thkl = Api.GetKeyboardLayout(wFocus.ThreadId);\n\t\t\tif (textHow == OKeyText.KeysOrPaste) {\n\t\t\t\tforeach (char c in s) {\n\t\t\t\t\tif (c is '\\r' or '\\n') continue;\n\t\t\t\t\tif (_CharToKey(c, hkl).vk == default) { textHow = OKeyText.Paste; break; }\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t//print.it(optk.TextHow, textHow);\n\n\t\tif (textHow == OKeyText.Paste) {\n\t\t\tPasting?.Invoke(this, new PastingEventArgs { Text = s, Options = optk, WndFocus = wFocus });\n\t\t\tclipboard.Paste_(data, optk, wFocus);\n\t\t\treturn;\n\t\t}\n\n\t\tif (_antiCapsLock == null && textHow is OKeyText.KeysOrChar or OKeyText.KeysOrPaste) _AntiCapsLock();\n\n\t\tKMod prevMod = 0;\n\t\tint sleep = 0 != (flags & 0x40) ? optk.KeySpeed : optk.TextSpeed; //0x40 if ^text\n\n\t\ttry {\n\t\t\tfor (int i = 0; i < s.Length; i++) {\n\t\t\t\tchar c = s[i];\n\n\t\t\t\tif (c == '\\r' && s.Eq(i + 1, '\\n')) continue; //\\r\\n -> \\n -> key Enter\n\n\t\t\t\t_SendChar2(c, textHow, sleep, 1, hkl, ref prevMod, textHowSpecified ? false : optk.TextShiftEnter);\n\t\t\t}\n\t\t}\n\t\tfinally {\n\t\t\tInternal_.ModPressRelease(false, prevMod);\n\t\t}\n\n\t\t//rejected: throw if changed the focused window.\n\t\t//\tPossible false positives, because everything is async.\n\t}\n\n\tvoid _AntiCapsLock(/*OKey optk*/) {\n\t\t//if (/*&& !optk.NoCapsOff*/) {\n\t\tif (!isCapsLock) {\n\t\t\t_antiCapsLock = false;\n\t\t\treturn;\n\t\t}\n\t\tif (isPressed(KKey.CapsLock)) Internal_.SendKey(KKey.CapsLock, false); //never mind: in this case later may not restore CapsLock because of auto-repeat\n\t\tInternal_.SendKey(KKey.CapsLock, true);\n\t\tbool ok = isPressed(KKey.CapsLock); //the send can fail because of UAC or the Windows setting\n\t\tInternal_.SendKey(KKey.CapsLock, false);\n\t\t//note: don't call isCapsLock again here. It is unreliable because GetKeyState is sync.\n\t\t//\tEg in some cases ignores the new key state until this UI thread removes all messages from queue.\n\t\tif (!ok && IsCapsLockShiftOff_()) {\n\t\t\t//Shift is set to turn off CapsLock in Settings > Time & Language > Language > Keyboard > Input method > Hot keys.\n\t\t\tWindowsHook.IgnoreLShiftCaps_(2000);\n\t\t\tInternal_.SendKey(KKey.Shift);\n\t\t\tWindowsHook.IgnoreLShiftCaps_(0);\n\n\t\t\t//note: need IgnoreLShiftCaps_, because when we send Shift, the BlockInput hook receives these events:\n\t\t\t//Left Shift down, not injected //!!\n\t\t\t//Caps Lock down, not injected\n\t\t\t//Caps Lock up, not injected\n\t\t\t//Left Shift up, injected\n\n\t\t\t//speed: often ~15 ms. Without Shift max 5 ms.\n\t\t}\n\t\t_antiCapsLock = true;\n\t\t//}\n\n\t\t//note: don't make _restoreCapsLock false if still isCapsLock true, because isCapsLock unreliable.\n\t\t//\tIf SendKey(CapsLock) did not work now, it probably will not work afterwards.\n\n\t\t//CONSIDER: remove this feature, or set non-default.\n\t\t//\tInstead, when sending text as keys, if CapsLock, invert Shift. Eg PAD uses this.\n\t\t//\tBut what then should do when sending keys (not text)? Probably should ignore CapsLock.\n\t\t//\tProbably safer with CapsLock off. Eg some target apps may interpret text differently when with an unexpected Shift.\n\t}\n\n\t/// <summary>\n\t/// Returns <c>true</c> if <c>Shift</c> is set to turn off <c>CapsLock</c> (system setting).\n\t/// </summary>\n\tinternal static bool IsCapsLockShiftOff_() => s_isCapsLockShiftOff ??= Microsoft.Win32.Registry.GetValue(@\"HKEY_CURRENT_USER\\Keyboard Layout\", \"Attributes\", 0) is int r1 && 0 != (r1 & 0x10000);\n\tstatic bool? s_isCapsLockShiftOff;\n\n\t/// <summary>\n\t/// Before pasting text through clipboard.\n\t/// </summary>\n\tpublic event EventHandler<PastingEventArgs> Pasting;\n}\n"
  },
  {
    "path": "Au/Input/keys.more.cs",
    "content": "namespace Au;\n\npartial class keys {\n\t/// <summary>\n\t/// Miscellaneous rarely used keyboard-related functions.\n\t/// </summary>\n\tpublic static partial class more {\n\t\t/// <summary>\n\t\t/// Converts key name to <see cref=\"KKey\"/>.\n\t\t/// </summary>\n\t\t/// <returns>0 if unknown key name.</returns>\n\t\t/// <param name=\"keyName\">[Key name](xref:key_names).</param>\n\t\tpublic static KKey parseKeyName(string keyName) {\n\t\t\tkeyName ??= \"\";\n\t\t\treturn _KeynameToKey(keyName, 0, keyName.Length);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Calls <see cref=\"parseKeyName\"/> and throws <see cref=\"ArgumentException\"/> if invalid key string.\n\t\t/// </summary>\n\t\tinternal static KKey ParseKeyNameThrow_(string keyName) {\n\t\t\tvar k = parseKeyName(keyName);\n\t\t\tif (k == 0) throw new ArgumentException(\"Unknown key name or error in key string.\");\n\t\t\treturn k;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Converts key name to <see cref=\"KKey\"/>.\n\t\t/// </summary>\n\t\t/// <returns>0 if unknown key name.</returns>\n\t\t/// <param name=\"s\">String containing [key name](xref:key_names).</param>\n\t\t/// <param name=\"startIndex\">Key name start index in <i>s</i>.</param>\n\t\t/// <param name=\"length\">Key name length.</param>\n\t\t/// <exception cref=\"ArgumentOutOfRangeException\">Invalid <i>startIndex</i> or <i>length</i>.</exception>\n\t\tpublic static KKey parseKeyName(string s, int startIndex, int length) {\n\t\t\ts ??= \"\";\n\t\t\tif ((uint)startIndex > s.Length || (uint)length > s.Length - startIndex) throw new ArgumentOutOfRangeException();\n\t\t\treturn _KeynameToKey(s, startIndex, length);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Converts keys string to <see cref=\"KKey\"/> array.\n\t\t/// </summary>\n\t\t/// <param name=\"keys_\">String containing one or more [key names](xref:key_names). Operators are not supported.</param>\n\t\t/// <exception cref=\"ArgumentException\">Error in <i>keys_</i> string.</exception>\n\t\tpublic static KKey[] parseKeysString(string keys_) {\n\t\t\tvar a = new List<KKey>();\n\t\t\tforeach (var g in _SplitKeysString(keys_ ?? \"\")) {\n\t\t\t\tKKey k = _KeynameToKey(keys_, g.Start, g.Length);\n\t\t\t\tif (k == 0) throw _ArgumentException_ErrorInKeysString(keys_, g.Start, g.Length);\n\t\t\t\ta.Add(k);\n\t\t\t}\n\t\t\treturn a.ToArray();\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Converts hotkey string like <c>\"Ctrl+A\"</c> to <see cref=\"KKey\"/> and <see cref=\"KMod\"/>.\n\t\t/// </summary>\n\t\t/// <returns><c>false</c> if the string is invalid.</returns>\n\t\t/// <remarks>\n\t\t/// For example, if <i>s</i> is <c>\"Ctrl+Left\"</c>, sets <c>mod = KMod.Ctrl</c>, <c>key = KKey.Left</c>.\n\t\t/// \n\t\t/// [Key names](xref:key_names) are like with <see cref=\"keys.send\"/>.\n\t\t/// \n\t\t/// Must be single non-modifier key, preceded by zero or more of modifier keys <c>Ctrl</c>, <c>Shift</c>, <c>Alt</c>, <c>Win</c>, all joined with <c>+</c>.\n\t\t/// Valid hotkey examples: <c>\"A\"</c>, <c>\"a\"</c>, <c>\"7\"</c>, <c>\"F12\"</c>, <c>\".\"</c>, <c>\"End\"</c>, <c>\"Ctrl+D\"</c>, <c>\"Ctrl+Alt+Shift+Win+Left\"</c>, <c>\" Ctrl + U \"</c>.\n\t\t/// Invalid hotkey examples: <c>null</c>, <c>\"\"</c>, <c>\"A+B\"</c>, <c>\"Ctrl+A+K\"</c>, <c>\"A+Ctrl\"</c>, <c>\"Ctrl+Shift\"</c>, <c>\"Ctrl+\"</c>, <c>\"NoSuchKey\"</c>, <c>\"tab\"</c>.\n\t\t/// </remarks>\n\t\tpublic static bool parseHotkeyString(string s, out KMod mod, out KKey key) {\n\t\t\tkey = 0; mod = 0;\n\t\t\tif (s == null) return false;\n\t\t\tint i = 0;\n\t\t\tforeach (var g in _SplitKeysString(s)) {\n\t\t\t\tif (key != 0) return false;\n\t\t\t\tif ((i++ & 1) == 0) {\n\t\t\t\t\tKKey k = _KeynameToKey(s, g.Start, g.Length);\n\t\t\t\t\tif (k == 0) return false;\n\t\t\t\t\tvar m = Internal_.KeyToMod(k);\n\t\t\t\t\tif (m != 0) {\n\t\t\t\t\t\tif ((m & mod) != 0) return false;\n\t\t\t\t\t\tmod |= m;\n\t\t\t\t\t} else key = k;\n\t\t\t\t} else if (g.Length != 1 || s[g.Start] != '+') return false;\n\t\t\t}\n\t\t\treturn key != 0 && key != KKey.Packet;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Converts hotkey string like <c>\"Ctrl+A\"</c> to winforms <see cref=\"System.Windows.Forms.Keys\"/>.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// For example, if <i>s</i> is <c>\"Ctrl+Left\"</c>, sets <c>hotkey = Keys.Control | Keys.Left</c>.\n\t\t/// </remarks>\n\t\t/// <returns><c>false</c> if the string is invalid or contains <c>\"Win\"</c>.</returns>\n\t\tpublic static bool parseHotkeyString(string s, out System.Windows.Forms.Keys hotkey) {\n\t\t\tif (!parseHotkeyString(s, out var m, out var k)) { hotkey = 0; return false; }\n\t\t\thotkey = KModToWinforms(m) | (System.Windows.Forms.Keys)k;\n\t\t\tif (m.Has(KMod.Win)) return false;\n\t\t\treturn true;\n\t\t\t//return Enum.IsDefined(typeof(System.Windows.Forms.Keys), (System.Windows.Forms.Keys)k); //not too slow\n\t\t\t//tested: enum Keys has all KKey values + some extinct.\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Converts hotkey string like <c>\"Ctrl+A\"</c> to WPF <see cref=\"System.Windows.Input.ModifierKeys\"/> and <see cref=\"System.Windows.Input.Key\"/> or <see cref=\"System.Windows.Input.MouseAction\"/>.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// For example, if <i>s</i> is <c>\"Ctrl+Left\"</c>, sets <c>mod = ModifierKeys.Control</c> and <c>key = Key.Left</c>.\n\t\t/// \n\t\t/// Supported mouse button strings: <c>\"Click\"</c>, <c>\"D-click\"</c>, <c>\"R-click\"</c>, <c>\"M-click\"</c>, <c>\"Wheel\"</c>. Example: <c>\"Ctrl+R-click\"</c>. The first character of a mouse word is case-insensitive.\n\t\t/// </remarks>\n\t\t/// <returns><c>false</c> if the string is invalid or contains incorrectly specified mouse buttons.</returns>\n\t\tpublic static bool parseHotkeyString(string s, out System.Windows.Input.ModifierKeys mod, out System.Windows.Input.Key key, out System.Windows.Input.MouseAction mouse) {\n\t\t\tmod = 0; key = 0; mouse = 0;\n\t\t\tif (s.Ends(\"lick\") || s.Ends(\"heel\")) {\n\t\t\t\tint i = s.LastIndexOf('+') + 1;\n\t\t\t\tvar v = s.AsSpan(i); var co = StringComparison.OrdinalIgnoreCase;\n\t\t\t\tif (v.Equals(\"Click\", co)) mouse = System.Windows.Input.MouseAction.LeftClick;\n\t\t\t\telse if (v.Equals(\"D-click\", co)) mouse = System.Windows.Input.MouseAction.LeftDoubleClick;\n\t\t\t\telse if (v.Equals(\"R-click\", co)) mouse = System.Windows.Input.MouseAction.RightClick;\n\t\t\t\telse if (v.Equals(\"M-click\", co)) mouse = System.Windows.Input.MouseAction.MiddleClick;\n\t\t\t\telse if (v.Equals(\"Wheel\", co)) mouse = System.Windows.Input.MouseAction.WheelClick;\n\t\t\t\tif (mouse != default) {\n\t\t\t\t\tif (i == 0) return true;\n\t\t\t\t\ts = s.ReplaceAt(i.., \"A\"); //replace the mouse word with a key name, else can't parse\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!parseHotkeyString(s, out var m, out var k)) return false;\n\t\t\tmod = KModToWpf(m);\n\t\t\treturn mouse != default || (key = KKeyToWpf(k)) != default;\n\t\t\t//tested: enum Key has all KKey values except mouse buttons and packet.\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Parses hotkey trigger string or mouse trigger modifiers string.\n\t\t/// Like <see cref=\"parseHotkeyString\"/>, but supports \"any mod\" (like <c>\"Shift?+K\"</c> or <c>\"?+K\"</c>) and <i>noKey</i>.\n\t\t/// </summary>\n\t\t/// <param name=\"s\"></param>\n\t\t/// <param name=\"mod\"></param>\n\t\t/// <param name=\"modAny\"></param>\n\t\t/// <param name=\"key\"></param>\n\t\t/// <param name=\"noKey\">Modifiers only. If <c>true</c>, <i>s</i> must be <c>\"modifiers\"</c> or <c>null</c>/<c>\"\"</c>. If <c>false</c>, <i>s</i> must be <c>\"key\"</c> or <c>\"modifiers+key\"</c>.</param>\n\t\tpublic static bool parseTriggerString(string s, out KMod mod, out KMod modAny, out KKey key, bool noKey) {\n\t\t\tkey = 0; mod = 0; modAny = 0;\n\t\t\tif (s.NE()) return noKey;\n\t\t\tint i = 0; bool ignore = false;\n\t\t\tforeach (var g in _SplitKeysString(s)) {\n\t\t\t\tif (ignore) { ignore = false; continue; }\n\t\t\t\tif (key != 0) return false;\n\t\t\t\tif ((i++ & 1) == 0) {\n\t\t\t\t\tKKey k = _KeynameToKey(s, g.Start, g.Length);\n\t\t\t\t\tif (k == 0) return false;\n\t\t\t\t\tvar m = Internal_.KeyToMod(k);\n\t\t\t\t\tif (m != 0) {\n\t\t\t\t\t\tif ((m & (mod | modAny)) != 0) return false;\n\t\t\t\t\t\tif (ignore = g.End < s.Length && s[g.End] == '?') modAny |= m; //eg \"Shift?+K\"\n\t\t\t\t\t\telse mod |= m;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (i == 1 && g.Length == 1 && s[g.Start] == '?') modAny = (KMod)15; //eg \"?+K\"\n\t\t\t\t\t\telse key = k;\n\t\t\t\t\t}\n\t\t\t\t} else if (g.Length != 1 || s[g.Start] != '+') return false;\n\t\t\t}\n\t\t\tif (noKey) return (mod | modAny) != 0 && key == 0;\n\t\t\treturn key != 0;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Formats hotkey string like <c>\"Ctrl+Shift+K\"</c>.\n\t\t/// </summary>\n\t\t/// <param name=\"b\">Append to this <c>StringBuilder</c>.</param>\n\t\t/// <param name=\"mod\"></param>\n\t\t/// <param name=\"key\"></param>\n\t\tpublic static void hotkeyToString(StringBuilder b, KMod mod, KKey key) {\n\t\t\tif (mod.Has(KMod.Ctrl)) b.Append(\"Ctrl+\");\n\t\t\tif (mod.Has(KMod.Alt)) b.Append(\"Alt+\");\n\t\t\tif (mod.Has(KMod.Shift)) b.Append(\"Shift+\");\n\t\t\tif (mod.Has(KMod.Win)) b.Append(\"Win+\");\n\t\t\tb.Append(keyToString(key)).ToString();\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Formats hotkey string like <c>\"Ctrl+Shift+K\"</c>.\n\t\t/// </summary>\n\t\tpublic static string hotkeyToString(KMod mod, KKey key) {\n\t\t\tif (mod == 0) return keyToString(key);\n\t\t\tusing (new StringBuilder_(out var b)) {\n\t\t\t\thotkeyToString(b, mod, key);\n\t\t\t\treturn b.ToString();\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets [key name](xref:key_names) that can be used in keys strings with <see cref=\"keys.send\"/> etc.\n\t\t/// </summary>\n\t\tpublic static string keyToString(KKey key) => c_keyNames[(int)key] ?? (\"VK\" + (int)key);\n\t\t\n\t\tstatic readonly string[] c_keyNames = {\n\t\"\",\n\t\"MouseLeft\",\n\t\"MouseRight\",\n\t\"Break\",\n\t\"MouseMiddle\",\n\t\"MouseX1\",\n\t\"MouseX2\",\n\tnull,\n\t\"Back\",\n\t\"Tab\",\n\tnull,\n\tnull,\n\t\"Clear\",\n\t\"Enter\",\n\tnull,\n\tnull,\n\t\"Shift\",\n\t\"Ctrl\",\n\t\"Alt\",\n\t\"Pause\",\n\t\"CapsLock\",\n\t\"IMEKanaMode\",\n\tnull,\n\t\"IMEJunjaMode\",\n\t\"IMEFinalMode\",\n\t\"IMEKanjiMode\",\n\tnull,\n\t\"Esc\",\n\t\"IMEConvert\",\n\t\"IMENonconvert\",\n\t\"IMEAccept\",\n\t\"IMEModeChange\",\n\t\"Space\",\n\t\"PgUp\",\n\t\"PgDn\",\n\t\"End\",\n\t\"Home\",\n\t\"Left\",\n\t\"Up\",\n\t\"Right\",\n\t\"Down\",\n\tnull,\n\tnull,\n\tnull,\n\t\"PrtSc\",\n\t\"Ins\",\n\t\"Del\",\n\tnull,\n\t\"0\",\n\t\"1\",\n\t\"2\",\n\t\"3\",\n\t\"4\",\n\t\"5\",\n\t\"6\",\n\t\"7\",\n\t\"8\",\n\t\"9\",\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\t\"A\",\n\t\"B\",\n\t\"C\",\n\t\"D\",\n\t\"E\",\n\t\"F\",\n\t\"G\",\n\t\"H\",\n\t\"I\",\n\t\"J\",\n\t\"K\",\n\t\"L\",\n\t\"M\",\n\t\"N\",\n\t\"O\",\n\t\"P\",\n\t\"Q\",\n\t\"R\",\n\t\"S\",\n\t\"T\",\n\t\"U\",\n\t\"V\",\n\t\"W\",\n\t\"X\",\n\t\"Y\",\n\t\"Z\",\n\t\"Win\",\n\t\"RWin\",\n\t\"Apps\",\n\tnull,\n\t\"Sleep\",\n\t\"#0\",\n\t\"#1\",\n\t\"#2\",\n\t\"#3\",\n\t\"#4\",\n\t\"#5\",\n\t\"#6\",\n\t\"#7\",\n\t\"#8\",\n\t\"#9\",\n\t\"#*\",\n\t\"#+\",\n\tnull,\n\t\"#-\",\n\t\"#.\",\n\t\"#/\",\n\t\"F1\",\n\t\"F2\",\n\t\"F3\",\n\t\"F4\",\n\t\"F5\",\n\t\"F6\",\n\t\"F7\",\n\t\"F8\",\n\t\"F9\",\n\t\"F10\",\n\t\"F11\",\n\t\"F12\",\n\t\"F13\",\n\t\"F14\",\n\t\"F15\",\n\t\"F16\",\n\t\"F17\",\n\t\"F18\",\n\t\"F19\",\n\t\"F20\",\n\t\"F21\",\n\t\"F22\",\n\t\"F23\",\n\t\"F24\",\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\t\"NumLock\",\n\t\"ScrollLock\",\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\t\"LShift\",\n\t\"RShift\",\n\t\"LCtrl\",\n\t\"RCtrl\",\n\t\"LAlt\",\n\t\"RAlt\",\n\t\"BrowserBack\",\n\t\"BrowserForward\",\n\t\"BrowserRefresh\",\n\t\"BrowserStop\",\n\t\"BrowserSearch\",\n\t\"BrowserFavorites\",\n\t\"BrowserHome\",\n\t\"VolumeMute\",\n\t\"VolumeDown\",\n\t\"VolumeUp\",\n\t\"MediaNextTrack\",\n\t\"MediaPrevTrack\",\n\t\"MediaStop\",\n\t\"MediaPlayPause\",\n\t\"LaunchMail\",\n\t\"LaunchMediaSelect\",\n\t\"LaunchApp1\",\n\t\"LaunchApp2\",\n\tnull,\n\tnull,\n\t\";\",\n\t\"=\",\n\t\",\",\n\t\"-\",\n\t\".\",\n\t\"/\",\n\t\"`\",\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\t\"[\",\n\t\"|\",\n\t\"]\",\n\t\"'\",\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\t\"IMEProcessKey\",\n\tnull,\n\t\"Packet\",\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n\tnull,\n};\n\t\t//this script creates the array:\n\t\t/*\nprint.clear();\nvar b=new StringBuilder(\"static readonly string[] c_keyNames = {\");\nfor (int i = 0; i < 256; i++) {\nvar k=(KKey)i;\nvar s=k switch {\n\t0 => \"\",\n\t>= KKey.D0 and <= KKey.D9 => ((char)k).ToString(),\n\t>= KKey.NumPad0 and <= KKey.NumPad9 => \"#\" + ((char)(k-KKey.NumPad0+'0')).ToString(),\n\tKKey.OemMinus => \"-\",\n\tKKey.OemPlus => \"=\",\n\tKKey.OemTilde => \"`\",\n\tKKey.OemOpenBrackets => \"[\",\n\tKKey.OemCloseBrackets => \"]\",\n\tKKey.OemPipe => \"|\",\n\tKKey.OemSemicolon => \";\",\n\tKKey.OemQuotes => \"'\",\n\tKKey.OemComma => \",\",\n\tKKey.OemPeriod => \".\",\n\tKKey.OemQuestion => \"/\",\n\tKKey.Decimal => \"#.\",\n\tKKey.Add => \"#+\",\n\tKKey.Divide => \"#/\",\n\tKKey.Multiply => \"#*\",\n\tKKey.Subtract => \"#-\",\n\tKKey.Escape => \"Esc\",\n\tKKey.PageUp => \"PgUp\",\n\tKKey.PageDown => \"PgDn\",\n\tKKey.PrintScreen => \"PrtSc\",\n\tKKey.Insert => \"Ins\",\n\tKKey.Delete => \"Del\",\n\t_ => Enum.IsDefined<KKey>(k) ? k.ToString() : null\n};\n//\tprint.it(k, s);\nb.Append(\"\\r\\n\\t\");\nif(s==null) b.Append(\"null\"); else b.Append('\"').Append(s).Append('\"');\nb.Append(',');\n}\nb.Append(\"\\r\\n};\");\nprint.it(b.ToString());\n\t\t*/\n\t\t\n\t\t/// <summary>\n\t\t/// Converts modifier key flags from <c>KMod</c> to winforms <c>Keys</c>.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// For <c>Win</c> returns flag <c>(Keys)0x80000</c>.\n\t\t/// </remarks>\n\t\tpublic static System.Windows.Forms.Keys KModToWinforms(KMod mod) => (System.Windows.Forms.Keys)((int)mod << 16);\n\t\t\n\t\t/// <summary>\n\t\t/// Converts modifier key flags from winforms <c>Keys</c> to <c>KMod</c>.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// For <c>Win</c> can be used flag <c>(Keys)0x80000</c>.\n\t\t/// </remarks>\n\t\tpublic static KMod KModFromWinforms(System.Windows.Forms.Keys mod) => (KMod)((int)mod >> 16);\n\t\t\n\t\t/// <summary>\n\t\t/// Converts modifier key flags from <c>KMod</c> to WPF <c>ModifierKeys</c>.\n\t\t/// </summary>\n\t\tpublic static System.Windows.Input.ModifierKeys KModToWpf(KMod mod) => (System.Windows.Input.ModifierKeys)_SwapMod((int)mod);\n\t\t\n\t\t/// <summary>\n\t\t/// Converts modifier key flags from WPF <c>ModifierKeys</c> to <c>KMod</c>.\n\t\t/// </summary>\n\t\tpublic static KMod KModFromWpf(System.Windows.Input.ModifierKeys mod) => (KMod)_SwapMod((int)mod);\n\t\t\n\t\tstatic int _SwapMod(int m) => (m & 0b1010) | (m << 2 & 4) | (m >> 2 & 1);\n\t\t\n\t\t/// <summary>\n\t\t/// Converts key from <c>KKey</c> to WPF <c>Key</c>.\n\t\t/// </summary>\n\t\tpublic static System.Windows.Input.Key KKeyToWpf(KKey k) => System.Windows.Input.KeyInterop.KeyFromVirtualKey((int)k);\n\t\t\n\t\t/// <summary>\n\t\t/// Converts key from WPF <c>Key</c> to <c>KKey</c>.\n\t\t/// </summary>\n\t\tpublic static KKey KKeyFromWpf(System.Windows.Input.Key k) => (KKey)System.Windows.Input.KeyInterop.VirtualKeyFromKey(k);\n\t\t\n\t\t/// <summary>\n\t\t/// Sends single key.\n\t\t/// </summary>\n\t\t/// <param name=\"k\">Virtual-key code.</param>\n\t\t/// <param name=\"down\"><c>true</c> down, <c>false</c> up, <c>null</c> down and up.</param>\n\t\t/// <param name=\"hkl\">\n\t\t/// Keyboard layout handle for scan code. See API <ms>GetKeyboardLayout</ms>.\n\t\t/// If 0 (default), uses keyboard layout of this thread; don't use 0 for keys whose scancode depends on keyboard layout.\n\t\t/// If -1, uses keyboard layout of the focused or active window.\n\t\t/// </param>\n\t\t/// <param name=\"extra\">An \"extra info\" value that can be used for example by keyboard hooks to recognize the key sender. If <c>null</c> (default), uses the same value as other functions of this library.</param>\n\t\t/// <param name=\"dontThrow\">Don't throw exception.</param>\n\t\t/// <remarks>\n\t\t/// This is a low-level function. Does nothing more (sleep, block input, etc). Does not use <see cref=\"opt\"/> options. Just gets missing info (scan code etc) and calls API <ms>SendInput</ms>.\n\t\t/// </remarks>\n\t\t/// <exception cref=\"InputDesktopException\"></exception>\n\t\tpublic static void sendKey(KKey k, bool? down = null, nint hkl = 0, int? extra = null, bool dontThrow = false)\n\t\t\t=> Internal_.SendKey(k, down, hkl, extra, dontThrow);\n\t\t\n\t\t//FUTURE: RemapKeyboardKeys. See QM2.\n\t\t\n\t}\n}\n"
  },
  {
    "path": "Au/Input/keys_static.cs",
    "content": "namespace Au;\n\npublic partial class keys {\n\t#region get key state\n\n\t/// <summary>\n\t/// Gets key states for using in UI code (winforms, WPF, etc).\n\t/// </summary>\n\t/// <remarks>\n\t/// Use functions of this class in user interface code (winforms, WPF, etc). In other code (automation scripts, etc) usually it's better to use functions of <see cref=\"keys\"/> class.\n\t/// \n\t/// In Windows there are two API to get key state - <ms>GetKeyState</ms> and <ms>GetAsyncKeyState</ms>.\n\t/// \n\t/// API <ms>GetAsyncKeyState</ms> is used by class <see cref=\"keys\"/> and not by this class (<c>keys.gui</c>). When physical key state changes (pressed/released), <c>GetAsyncKeyState</c> sees the change immediately. It is good in automation scripts, but not good in UI code because the state is not synchronized with the message queue.\n\t/// \n\t/// This class (<c>keys.gui</c>) uses API <ms>GetKeyState</ms>. In the foreground thread (of the active window), it sees key state changes not immediately but after the thread reads key messages from its queue. It is good in UI threads. In background threads this API usually works like <c>GetAsyncKeyState</c>, but it depends on API <ms>AttachThreadInput</ms> and in some cases is less reliable, for example may be unaware of keys pressed before the thread started.\n\t/// \n\t/// The key state returned by these API is not always the same as of the physical keyboard. There is no API to get real physical state. Some cases when it is different:\n\t/// 1. The key is pressed or released by software, such as the <see cref=\"send\"/> function of this library.\n\t/// 2. The key is blocked by a low-level hook. For example, hotkey triggers of this library use hooks.\n\t/// 3. The foreground window belongs to a process with higher UAC integrity level.\n\t/// \n\t/// Also there is API <ms>GetKeyboardState</ms>. It gets states of all keys in single call. Works like <ms>GetKeyState</ms>.\n\t/// </remarks>\n\tpublic static class gui {\n\t\t//rejected: instead of class keys.gui add property keys.isUIThread. If true, let its functions work like now keys.gui.\n\n\t\t/// <summary>\n\t\t/// Calls API <ms>GetKeyState</ms> and returns its return value.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// If returns &lt; 0, the key is pressed. If the low-order bit is 1, the key is toggled; it works only with <c>CapsLock</c>, <c>NumLock</c>, <c>ScrollLock</c> and several other keys, as well as mouse buttons.\n\t\t/// Can be used for mouse buttons too, for example <c>keys.gui.getKeyState(KKey.MouseLeft)</c>. When mouse left and right buttons are swapped, gets logical state, not physical.\n\t\t/// </remarks>\n\t\tpublic static short getKeyState(KKey key) => Api.GetKeyState((int)key);\n\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if the specified key or mouse button is pressed.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Can be used for mouse buttons too. Example: <c>keys.gui.isPressed(KKey.MouseLeft)</c>. When mouse left and right buttons are swapped, gets logical state, not physical.\n\t\t/// </remarks>\n\t\tpublic static bool isPressed(KKey key) => getKeyState(key) < 0;\n\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if the specified key or mouse button is toggled.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Works only with <c>CapsLock</c>, <c>NumLock</c>, <c>ScrollLock</c> and several other keys, as well as mouse buttons.\n\t\t/// </remarks>\n\t\tpublic static bool isToggled(KKey key) => 0 != (getKeyState(key) & 1);\n\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if the <c>Alt</c> key is pressed.\n\t\t/// </summary>\n\t\tpublic static bool isAlt => isPressed(KKey.Alt);\n\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if the <c>Ctrl</c> key is pressed.\n\t\t/// </summary>\n\t\tpublic static bool isCtrl => isPressed(KKey.Ctrl);\n\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if the <c>Shift</c> key is pressed.\n\t\t/// </summary>\n\t\tpublic static bool isShift => isPressed(KKey.Shift);\n\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if the <c>Win</c> key is pressed.\n\t\t/// </summary>\n\t\tpublic static bool isWin => isPressed(KKey.Win) || isPressed(KKey.RWin);\n\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if some modifier keys are pressed.\n\t\t/// </summary>\n\t\t/// <param name=\"mod\">Return <c>true</c> if some of these keys are pressed. Default: <c>Ctrl</c>, <c>Shift</c> or <c>Alt</c>.</param>\n\t\t/// <remarks>\n\t\t/// By default does not check the <c>Win</c> key, as it is not used in UI, but you can include it in <i>mod</i> if need.\n\t\t/// </remarks>\n\t\tpublic static bool isMod(KMod mod = KMod.Ctrl | KMod.Shift | KMod.Alt) {\n\t\t\tif (0 != (mod & KMod.Ctrl) && isCtrl) return true;\n\t\t\tif (0 != (mod & KMod.Shift) && isShift) return true;\n\t\t\tif (0 != (mod & KMod.Alt) && isAlt) return true;\n\t\t\tif (0 != (mod & KMod.Win) && isWin) return true;\n\t\t\treturn false;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets flags indicating which modifier keys are pressed.\n\t\t/// </summary>\n\t\t/// <param name=\"mod\">Check only these keys. Default: <c>Ctrl</c>, <c>Shift</c>, <c>Alt</c>.</param>\n\t\t/// <remarks>\n\t\t/// By default does not check the <c>Win</c> key, as it is not used in UI, but you can include it in <i>mod</i> if need.\n\t\t/// </remarks>\n\t\tpublic static KMod getMod(KMod mod = KMod.Ctrl | KMod.Shift | KMod.Alt) {\n\t\t\tKMod R = 0;\n\t\t\tif (0 != (mod & KMod.Ctrl) && isCtrl) R |= KMod.Ctrl;\n\t\t\tif (0 != (mod & KMod.Shift) && isShift) R |= KMod.Shift;\n\t\t\tif (0 != (mod & KMod.Alt) && isAlt) R |= KMod.Alt;\n\t\t\tif (0 != (mod & KMod.Win) && isWin) R |= KMod.Win;\n\t\t\treturn R;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if the <c>CapsLock</c> key is toggled.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// The same as <see cref=\"keys.isCapsLock\"/>.\n\t\t/// </remarks>\n\t\tpublic static bool isCapsLock => isToggled(KKey.CapsLock);\n\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if the <c>NumLock</c> key is toggled.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// The same as <see cref=\"keys.isNumLock\"/>.\n\t\t/// </remarks>\n\t\tpublic static bool isNumLock => isToggled(KKey.NumLock);\n\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if the <c>ScrollLock</c> key is toggled.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// The same as <see cref=\"keys.isScrollLock\"/>.\n\t\t/// </remarks>\n\t\tpublic static bool isScrollLock => isToggled(KKey.ScrollLock);\n\t}\n\n\t/// <summary>\n\t/// Returns <c>true</c> if the specified key or mouse button is pressed.\n\t/// In UI code use <see cref=\"keys.gui\"/> instead.\n\t/// </summary>\n\t/// <remarks>\n\t/// Uses API <ms>GetAsyncKeyState</ms>.\n\t/// </remarks>\n\tpublic static bool isPressed(KKey key) {\n\t\tif ((key == KKey.MouseLeft || key == KKey.MouseRight) && 0 != Api.GetSystemMetrics(Api.SM_SWAPBUTTON)) key = (KKey)((int)key ^ 3); //makes this func 3 times slower, eg 2 -> 6 mcs when cold CPU. But much faster when called next time without a delay; for example mouse.isPressed(Left|Right) is not slower than mouse.isPressed(Left), although calls this func 2 times.\n\t\treturn Api.GetAsyncKeyState((int)key) < 0;\n\t}\n\n\t/// <summary>\n\t/// Returns <c>true</c> if the <c>Alt</c> key is pressed. Calls <see cref=\"isPressed\"/>.\n\t/// In UI code use <see cref=\"keys.gui\"/> instead.\n\t/// </summary>\n\tpublic static bool isAlt => isPressed(KKey.Alt);\n\n\t/// <summary>\n\t/// Returns <c>true</c> if the <c>Ctrl</c> key is pressed. Calls <see cref=\"isPressed\"/>.\n\t/// In UI code use <see cref=\"keys.gui\"/> instead.\n\t/// </summary>\n\tpublic static bool isCtrl => isPressed(KKey.Ctrl);\n\n\t/// <summary>\n\t/// Returns <c>true</c> if the <c>Shift</c> key is pressed. Calls <see cref=\"isPressed\"/>.\n\t/// In UI code use <see cref=\"keys.gui\"/> instead.\n\t/// </summary>\n\tpublic static bool isShift => isPressed(KKey.Shift);\n\n\t/// <summary>\n\t/// Returns <c>true</c> if the <c>Win</c> key is pressed. Calls <see cref=\"isPressed\"/>.\n\t/// In UI code use <see cref=\"keys.gui\"/> instead.\n\t/// </summary>\n\tpublic static bool isWin => isPressed(KKey.Win) || isPressed(KKey.RWin);\n\n\t/// <summary>\n\t/// Returns <c>true</c> if some modifier keys are pressed: <c>Ctrl</c>, <c>Shift</c>, <c>Alt</c>, <c>Win</c>. Calls <see cref=\"isPressed\"/>.\n\t/// In UI code use <see cref=\"keys.gui\"/> instead.\n\t/// </summary>\n\t/// <param name=\"mod\">Return <c>true</c> if some of these keys are pressed. Default - any.</param>\n\t/// <seealso cref=\"waitForNoModifierKeys\"/>\n\tpublic static bool isMod(KMod mod = KMod.Ctrl | KMod.Shift | KMod.Alt | KMod.Win) {\n\t\tif (0 != (mod & KMod.Ctrl) && isCtrl) return true;\n\t\tif (0 != (mod & KMod.Shift) && isShift) return true;\n\t\tif (0 != (mod & KMod.Alt) && isAlt) return true;\n\t\tif (0 != (mod & KMod.Win) && isWin) return true;\n\t\treturn false;\n\t}\n\n\t/// <summary>\n\t/// Gets flags indicating which modifier keys are pressed: <c>Ctrl</c>, <c>Shift</c>, <c>Alt</c>, <c>Win</c>. Calls <see cref=\"isPressed\"/>.\n\t/// In UI code use <see cref=\"keys.gui\"/> instead.\n\t/// </summary>\n\t/// <param name=\"mod\">Check only these keys. Default - all four.</param>\n\tpublic static KMod getMod(KMod mod = KMod.Ctrl | KMod.Shift | KMod.Alt | KMod.Win) {\n\t\tKMod R = 0;\n\t\tif (0 != (mod & KMod.Ctrl) && isCtrl) R |= KMod.Ctrl;\n\t\tif (0 != (mod & KMod.Shift) && isShift) R |= KMod.Shift;\n\t\tif (0 != (mod & KMod.Alt) && isAlt) R |= KMod.Alt;\n\t\tif (0 != (mod & KMod.Win) && isWin) R |= KMod.Win;\n\t\treturn R;\n\t}\n\n\t/// <summary>\n\t/// Returns <c>true</c> if the <c>CapsLock</c> key is toggled.\n\t/// </summary>\n\tpublic static bool isCapsLock => gui.isCapsLock;\n\n\t/// <summary>\n\t/// Returns <c>true</c> if the <c>NumLock</c> key is toggled.\n\t/// </summary>\n\tpublic static bool isNumLock => gui.isNumLock;\n\n\t/// <summary>\n\t/// Returns <c>true</c> if the <c>ScrollLock</c> key is toggled.\n\t/// </summary>\n\tpublic static bool isScrollLock => gui.isScrollLock;\n\n\t#endregion\n\n\t#region wait\n\n\t/// <summary>\n\t/// Waits while some modifier keys (<c>Ctrl</c>, <c>Shift</c>, <c>Alt</c>, <c>Win</c>) are pressed. See <see cref=\"isMod\"/>.\n\t/// </summary>\n\t/// <param name=\"timeout\">Timeout, seconds. Can be 0 (infinite), >0 (exception) or &lt;0 (no exception). More info: [](xref:wait_timeout).</param>\n\t/// <param name=\"mod\">Check only these keys. Default: all.</param>\n\t/// <returns>Returns <c>true</c>. On timeout returns <c>false</c> if <i>timeout</i> is negative; else exception.</returns>\n\t/// <exception cref=\"TimeoutException\"><i>timeout</i> time has expired (if > 0).</exception>\n\tpublic static bool waitForNoModifierKeys(Seconds timeout = default, KMod mod = KMod.Ctrl | KMod.Shift | KMod.Alt | KMod.Win) {\n\t\treturn waitForNoModifierKeysAndMouseButtons(timeout, mod, 0);\n\t}\n\n\t/// <summary>\n\t/// Waits while some modifier keys (<c>Ctrl</c>, <c>Shift</c>, <c>Alt</c>, <c>Win</c>) or mouse buttons are pressed.\n\t/// </summary>\n\t/// <param name=\"timeout\">Timeout, seconds. Can be 0 (infinite), >0 (exception) or &lt;0 (no exception). More info: [](xref:wait_timeout). Default 0.</param>\n\t/// <param name=\"mod\">Check only these keys. Default: all.</param>\n\t/// <param name=\"buttons\">Check only these buttons. Default: all.</param>\n\t/// <returns>Returns <c>true</c>. On timeout returns <c>false</c> if <i>timeout</i> is negative; else exception.</returns>\n\t/// <exception cref=\"TimeoutException\"><i>timeout</i> time has expired (if > 0).</exception>\n\t/// <seealso cref=\"isMod\"/>\n\t/// <seealso cref=\"mouse.isPressed\"/>\n\t/// <seealso cref=\"mouse.waitForNoButtonsPressed\"/>\n\tpublic static bool waitForNoModifierKeysAndMouseButtons(Seconds timeout = default, KMod mod = KMod.Ctrl | KMod.Shift | KMod.Alt | KMod.Win, MButtons buttons = MButtons.Left | MButtons.Right | MButtons.Middle | MButtons.X1 | MButtons.X2) {\n\t\tvar loop = new WaitLoop(timeout);\n\t\tfor (; ; ) {\n\t\t\tif (!isMod(mod) && !mouse.isPressed(buttons)) return true;\n\t\t\tif (!loop.Sleep()) return false;\n\t\t}\n\t}\n\n\t/// <summary>\n\t/// Waits while the specified keys or/and mouse buttons are pressed.\n\t/// </summary>\n\t/// <param name=\"timeout\">Timeout, seconds. Can be 0 (infinite), >0 (exception) or &lt;0 (no exception). More info: [](xref:wait_timeout).</param>\n\t/// <param name=\"keys_\">One or more keys or/and mouse buttons. Waits until all are released.</param>\n\t/// <returns>Returns <c>true</c>. On timeout returns <c>false</c> if <i>timeout</i> is negative; else exception.</returns>\n\t/// <exception cref=\"TimeoutException\"><i>timeout</i> time has expired (if > 0).</exception>\n\tpublic static bool waitForReleased(Seconds timeout, params KKey[] keys_) {\n\t\treturn wait.until(timeout, () => {\n\t\t\tforeach (var k in keys_) if (isPressed(k)) return false;\n\t\t\treturn true;\n\t\t});\n\t}\n\n\t/// <summary>\n\t/// Waits while the specified keys are pressed.\n\t/// </summary>\n\t/// <param name=\"timeout\">Timeout, seconds. Can be 0 (infinite), >0 (exception) or &lt;0 (no exception). More info: [](xref:wait_timeout).</param>\n\t/// <param name=\"keys_\">One or more [keys](xref:key_names) without operators. Waits until all are released.</param>\n\t/// <returns>Returns <c>true</c>. On timeout returns <c>false</c> if <i>timeout</i> is negative; else exception.</returns>\n\t/// <exception cref=\"ArgumentException\">Error in <i>keys_</i> string.</exception>\n\t/// <exception cref=\"TimeoutException\"><i>timeout</i> time has expired (if > 0).</exception>\n\tpublic static bool waitForReleased(Seconds timeout, string keys_) {\n\t\treturn waitForReleased(timeout, more.parseKeysString(keys_));\n\t}\n\n\t/// <summary>\n\t/// Waits for key-down or key-up event of the specified key.\n\t/// </summary>\n\t/// <returns>Returns <c>true</c>. On timeout returns <c>false</c> if <i>timeout</i> is negative; else exception.</returns>\n\t/// <param name=\"timeout\">Timeout, seconds. Can be 0 (infinite), >0 (exception) or &lt;0 (no exception). More info: [](xref:wait_timeout).</param>\n\t/// <param name=\"key\">Wait for this key.</param>\n\t/// <param name=\"up\">Wait for key-up event.</param>\n\t/// <param name=\"block\">Make the event invisible to other apps. If <i>up</i> is <c>true</c>, makes the down event invisible too, if it comes while waiting for the up event.</param>\n\t/// <exception cref=\"ArgumentException\"><i>key</i> is 0.</exception>\n\t/// <exception cref=\"TimeoutException\"><i>timeout</i> time has expired (if > 0).</exception>\n\t/// <remarks>\n\t/// Waits for key event, not for key state.\n\t/// Uses low-level keyboard hook. Can wait for any single key. See also <see cref=\"waitForHotkey\"/>.\n\t/// Ignores key events injected by functions of this library.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// keys.waitForKey(0, KKey.Ctrl, up: false, block: true);\n\t/// print.it(\"Ctrl\");\n\t/// ]]></code>\n\t/// </example>\n\tpublic static bool waitForKey(Seconds timeout, KKey key, bool up = false, bool block = false) {\n\t\tif (key == 0) throw new ArgumentException();\n\t\treturn 0 != _WaitForKey(timeout, key, up, block);\n\t}\n\n\t/// <param name=\"key\">Wait for this key. A single-key string. See [key names](xref:key_names).</param>\n\t/// <exception cref=\"ArgumentException\">Invalid <i>key</i> string.</exception>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// keys.waitForKey(0, \"Ctrl\", up: false, block: true);\n\t/// print.it(\"Ctrl\");\n\t/// ]]></code>\n\t/// </example>\n\t/// <inheritdoc cref=\"waitForKey(Seconds, KKey, bool, bool)\"/>\n\tpublic static bool waitForKey(Seconds timeout, string key, bool up = false, bool block = false) {\n\t\treturn 0 != _WaitForKey(timeout, more.ParseKeyNameThrow_(key), up, block);\n\t}\n\n\t/// <summary>\n\t/// Waits for key-down or key-up event of any key, and gets the key code.\n\t/// </summary>\n\t/// <returns>\n\t/// Returns the key code. On timeout returns 0 if <i>timeout</i> is negative; else exception.\n\t/// For modifier keys returns the left or right key code, for example <c>LCtrl</c>/<c>RCtrl</c>, not <c>Ctrl</c>.\n\t/// </returns>\n\t/// <exception cref=\"TimeoutException\"><i>timeout</i> time has expired (if > 0).</exception>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// var key = keys.waitForKey(0, up: true, block: true);\n\t/// print.it(key);\n\t/// ]]></code>\n\t/// </example>\n\t/// <inheritdoc cref=\"waitForKey(Seconds, KKey, bool, bool)\" path=\"/param\"/>\n\tpublic static KKey waitForKey(Seconds timeout, bool up = false, bool block = false) {\n\t\treturn _WaitForKey(timeout, 0, up, block);\n\t}\n\n\tstatic KKey _WaitForKey(Seconds timeout, KKey key, bool up, bool block) {\n\t\t//TODO3: if up and block: don't block if was down when starting to wait. Also in the Mouse func.\n\n\t\tKKey R = 0;\n\t\tusing (WindowsHook.Keyboard(x => {\n\t\t\tif (key != 0 && !x.IsKey(key)) return;\n\t\t\tif (x.IsUp != up) {\n\t\t\t\tif (up && block) { //key down when waiting for up. If block, now block down too.\n\t\t\t\t\tif (key == 0) key = x.vkCode;\n\t\t\t\t\tx.BlockEvent();\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tR = x.vkCode; //info: for mod keys returns left/right\n\t\t\tif (block) x.BlockEvent();\n\t\t})) wait.doEventsUntil(timeout, () => R != 0);\n\n\t\treturn R;\n\t}\n\n\t/// <summary>\n\t/// Waits for keyboard events using callback function.\n\t/// </summary>\n\t/// <returns>\n\t/// Returns the key code. On timeout returns 0 if <i>timeout</i> is negative; else exception.\n\t/// For modifier keys returns the left or right key code, for example <c>LCtrl</c>/<c>RCtrl</c>, not <c>Ctrl</c>.\n\t/// </returns>\n\t/// <param name=\"timeout\">Timeout, seconds. Can be 0 (infinite), >0 (exception) or &lt;0 (no exception). More info: [](xref:wait_timeout).</param>\n\t/// <param name=\"f\">Callback function that receives key down and up events. Let it return <c>true</c> to stop waiting.</param>\n\t/// <param name=\"block\">Make the key down event invisible to other apps (when the callback function returns <c>true</c>).</param>\n\t/// <remarks>\n\t/// Waits for key event, not for key state.\n\t/// Uses low-level keyboard hook.\n\t/// Ignores key events injected by functions of this library.\n\t/// </remarks>\n\t/// <example>\n\t/// Wait for <c>F3</c> or <c>Esc</c>.\n\t/// <code><![CDATA[\n\t/// var k = keys.waitForKeys(0, k => !k.IsUp && k.Key is KKey.F3 or KKey.Escape, block: true);\n\t/// print.it(k);\n\t/// ]]></code>\n\t/// </example>\n\tpublic static KKey waitForKeys(Seconds timeout, Func<HookData.Keyboard, bool> f, bool block = false) {\n\t\tKKey R = 0;\n\t\tusing (WindowsHook.Keyboard(x => {\n\t\t\tif (!f(x)) return;\n\t\t\tR = x.vkCode; //info: for mod keys returns left/right\n\t\t\tif (block && !x.IsUp) x.BlockEvent();\n\t\t})) wait.doEventsUntil(timeout, () => R != 0);\n\n\t\treturn R;\n\t}\n\t//CONSIDER: Same for mouse.\n\n\t/// <summary>\n\t/// Registers a temporary hotkey and waits for it.\n\t/// </summary>\n\t/// <param name=\"timeout\">Timeout, seconds. Can be 0 (infinite), >0 (exception) or &lt;0 (no exception). More info: [](xref:wait_timeout).</param>\n\t/// <param name=\"hotkey\">Hotkey. Can be: string like <c>\"Ctrl+Shift+Alt+Win+K\"</c>, tuple <c>(KMod, KKey)</c>, enum <see cref=\"KKey\"/>, enum <c>Keys</c>, struct <see cref=\"KHotkey\"/>.</param>\n\t/// <param name=\"waitModReleased\">Also wait until hotkey modifier keys released.</param>\n\t/// <returns>Returns <c>true</c>. On timeout returns <c>false</c> if <i>timeout</i> is negative; else exception.</returns>\n\t/// <exception cref=\"ArgumentException\">Error in hotkey string.</exception>\n\t/// <exception cref=\"AuException\">Failed to register hotkey.</exception>\n\t/// <exception cref=\"TimeoutException\"><i>timeout</i> time has expired (if > 0).</exception>\n\t/// <remarks>\n\t/// Uses <see cref=\"RegisteredHotkey\"/> (API <ms>RegisterHotKey</ms>).\n\t/// Fails if the hotkey is currently registered by this or another application or used by Windows.\n\t/// <note>Most single-key and <c>Shift+key</c> hotkeys don't work when the active window has higher UAC integrity level than this process. Media keys may work.</note>\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// keys.waitForHotkey(0, \"F11\");\n\t/// keys.waitForHotkey(0, KKey.F11);\n\t/// keys.waitForHotkey(0, \"Shift+A\", true);\n\t/// keys.waitForHotkey(0, (KMod.Ctrl | KMod.Shift, KKey.P)); //Ctrl+Shift+P\n\t/// keys.waitForHotkey(5, \"Ctrl+Win+K\"); //exception after 5 s\n\t/// if(!keys.waitForHotkey(-5, \"Left\")) print.it(\"timeout\"); //returns false after 5 s\n\t/// ]]></code>\n\t/// </example>\n\tpublic static bool waitForHotkey(Seconds timeout, [ParamString(PSFormat.Hotkey)] KHotkey hotkey, bool waitModReleased = false) {\n\t\tif (s_atomWFH == 0) s_atomWFH = Api.GlobalAddAtom(\"Au.WaitForHotkey\");\n\t\tusing (RegisteredHotkey rhk = default) {\n\t\t\tif (!rhk.Register(s_atomWFH, hotkey)) throw new AuException(0, \"*register hotkey\");\n\t\t\tif (!wait.forPostedMessage(timeout, (ref MSG m) => m.message == Api.WM_HOTKEY && m.wParam == s_atomWFH)) return false;\n\t\t}\n\t\t\n\t\tif (waitModReleased) waitForNoModifierKeys(0, hotkey.Mod);\n\t\t\n\t\treturn true;\n\t}\n\tstatic ushort s_atomWFH;\n\t\n\t/// <summary>\n\t/// Sets a temporary keyboard hook and waits for a hotkey.\n\t/// </summary>\n\t/// <param name=\"timeout\">Timeout, seconds. Can be 0 (infinite), >0 (exception) or &lt;0 (no exception). More info: [](xref:wait_timeout).</param>\n\t/// <param name=\"hotkeys\">One or more hotkeys. Examples: <c>[\"Ctrl+M\"]</c>, <c>[\"Win+M\", \"Ctrl+Shift+Left\"]</c>.</param>\n\t/// <param name=\"block\">Make the key down event of the non-modifier key invisible to other apps. Default <c>true</c>.</param>\n\t/// <param name=\"waitModReleased\">Also wait until hotkey modifier keys released.</param>\n\t/// <returns>1-based index of the element in the array of hotkeys. On timeout returns 0 (if <i>timeout</i> negative; else exception).</returns>\n\t/// <exception cref=\"TimeoutException\"></exception>\n\t/// <remarks>\n\t/// Uses <see cref=\"keys.waitForKeys\"/>.\n\t/// Works even if the hotkey is used by Windows (except <c>Win+L</c> and <c>Ctrl+Alt+Del</c>) or an app as a hotkey or trigger.\n\t/// <note>Does not work when the active window has higher UAC integrity level than this process.</note>\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// int i = keys.waitForHotkeys(-10, [\"Win+Left\", \"Win+Right\"]);\n\t/// print.it(i);\n\t/// ]]></code>\n\t/// </example>\n\tpublic static int waitForHotkeys(Seconds timeout, [ParamString(PSFormat.Hotkey)] KHotkey[] hotkeys, bool block = true, bool waitModReleased = false) {\n\t\tint R = 0;\n\t\tkeys.waitForKeys(timeout, k => {\n\t\t\tif (!k.IsUp && k.Mod == 0) {\n\t\t\t\tvar mod = keys.getMod();\n\t\t\t\tfor (int i = 0; i < hotkeys.Length; i++) {\n\t\t\t\t\tif (k.Key == hotkeys[i].Key && mod == hotkeys[i].Mod) {\n\t\t\t\t\t\tR = i + 1;\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}, block);\n\t\t\n\t\tif (waitModReleased && R > 0) keys.waitForNoModifierKeys(0, hotkeys[R - 1].Mod);\n\t\t\n\t\treturn R;\n\t}\n\n\t#endregion\n\n\t/// <summary>\n\t/// Generates virtual keystrokes (keys, text).\n\t/// </summary>\n\t/// <param name=\"keysEtc\">\n\t/// Arguments of these types:\n\t/// <br/>• string - [key names and operators](xref:key_names), like <c>\"Enter A Ctrl+A\"</c>.\\\n\t/// Tool: in <c>\"\"</c> string press <c>Ctrl+Space</c>.\n\t/// <br/>• string with prefix <c>\"!\"</c> - literal text.\\\n\t/// Example: <c>var p = \"pass\"; keys.send(\"!user\", \"Tab\", \"!\" + p, \"Enter\");</c>\n\t/// <br/>• string with prefix <c>\"%\"</c> - HTML to paste. Full or fragment.\n\t/// <br/>• <see cref=\"clipboardData\"/> - clipboard data to paste.\n\t/// <br/>• <see cref=\"KKey\"/> - a single key.\\\n\t/// Example: <c>keys.send(\"Shift+\", KKey.Left, \"*3\");</c> is the same as <c>keys.send(\"Shift+Left*3\");</c>\n\t/// <br/>• <c>int</c> - sleep milliseconds. Max 10000.\\\n\t/// Example: <c>keys.send(\"Left\", 500, \"Right\");</c>\n\t/// <br/>• <see cref=\"Action\"/> - callback function.\\\n\t/// Example: <c>Action click = () => mouse.click(); keys.send(\"Shift+\", click);</c>\n\t/// <br/>• <see cref=\"KKeyScan\"/> - a single key, specified using scan code and/or virtual-key code and extended-key flag.\\\n\t/// Example: <c>keys.send(new KKeyScan(0x3B, false)); //key F1</c>\\\n\t/// Example: <c>keys.send(new KKeyScan(KKey.Enter, true)); //numpad Enter</c>\n\t/// <br/>• <c>char</c> - a single character. Like text with <see cref=\"OKeyText.KeysOrChar\"/> or operator <c>^</c>.\n\t/// </param>\n\t/// <exception cref=\"ArgumentException\">An invalid value, for example an unknown key name.</exception>\n\t/// <exception cref=\"AuException\">Failed. When sending text, fails if there is no focused window.</exception>\n\t/// <exception cref=\"InputDesktopException\"></exception>\n\t/// <remarks>\n\t/// String syntax: [key names and operators](xref:key_names).\n\t/// \n\t/// Uses <see cref=\"opt.key\"/>:\n\t/// <table>\n\t/// <tr>\n\t/// <th>Option</th>\n\t/// <th>Default</th>\n\t/// <th>Changed</th>\n\t/// </tr>\n\t/// <tr>\n\t/// <td><see cref=\"OKey.NoBlockInput\"/></td>\n\t/// <td><c>false</c>.\n\t/// Blocks user-pressed keys. Sends them afterwards.\n\t/// <br/>If the last argument is \"sleep\", stops blocking before executing it; else stops blocking after executing all arguments.</td>\n\t/// <td><c>true</c>.\n\t/// Does not block user-pressed keys.</td>\n\t/// </tr>\n\t/// <tr>\n\t/// <td><see cref=\"OKey.NoCapsOff\"/></td>\n\t/// <td><c>false</c>.\n\t/// If the <c>CapsLock</c> key is toggled, untoggles it temporarily (presses it before and after).</td>\n\t/// <td><c>true</c>.\n\t/// Does not touch the <c>CapsLock</c> key.\n\t/// <br/>Alphabetic keys of \"keys\" arguments can depend on <c>CapsLock</c>. Text of \"text\" arguments doesn't depend on <c>CapsLock</c>, unless <see cref=\"OKey.TextHow\"/> is <c>KeysX</c>.</td>\n\t/// </tr>\n\t/// <tr>\n\t/// <td><see cref=\"OKey.NoModOff\"/></td>\n\t/// <td><c>false</c>.\n\t/// Releases modifier keys (<c>Alt</c>, <c>Ctrl</c>, <c>Shift</c>, <c>Win</c>).\n\t/// <br/>Does it only at the start; later they cannot interfere, unless <see cref=\"OKey.NoBlockInput\"/> is <c>true</c>.</td>\n\t/// <td><c>true</c>.\n\t/// Does not touch modifier keys.</td>\n\t/// </tr>\n\t/// <tr>\n\t/// <td><see cref=\"OKey.TextSpeed\"/></td>\n\t/// <td>0 ms.</td>\n\t/// <td>0 - 1000.\n\t/// Changes the speed for \"text\" arguments.</td>\n\t/// </tr>\n\t/// <tr>\n\t/// <td><see cref=\"OKey.KeySpeed\"/></td>\n\t/// <td>2 ms.</td>\n\t/// <td>0 - 1000.\n\t/// Changes the speed for \"keys\" arguments.</td>\n\t/// </tr>\n\t/// <tr>\n\t/// <td><see cref=\"OKey.KeySpeedClipboard\"/></td>\n\t/// <td>5 ms.</td>\n\t/// <td>0 - 1000.\n\t/// Changes the speed of <c>Ctrl+V</c> keys when pasting text or HTML using clipboard.</td>\n\t/// </tr>\n\t/// <tr>\n\t/// <td><see cref=\"OKey.SleepFinally\"/></td>\n\t/// <td>10 ms.</td>\n\t/// <td>0 - 10000.\n\t/// <br/>Tip: to sleep finally, also can be used code like this: <c>keys.send(\"keys\", 1000);</c>.</td>\n\t/// </tr>\n\t/// <tr>\n\t/// <td><see cref=\"OKey.TextHow\"/></td>\n\t/// <td><see cref=\"OKeyText.Characters\"/>.</td>\n\t/// <td><c>KeysOrChar</c>, <c>KeysOrPaste</c> or <c>Paste</c>.</td>\n\t/// </tr>\n\t/// <tr>\n\t/// <td><see cref=\"OKey.TextShiftEnter\"/></td>\n\t/// <td><c>false</c>.</td>\n\t/// <td><c>true</c>. When sending text, instead of <c>Enter</c> send <c>Shift+Enter</c>.</td>\n\t/// </tr>\n\t/// <tr>\n\t/// <td><see cref=\"OKey.PasteLength\"/></td>\n\t/// <td>200.\n\t/// <br/>This option is used for \"text\" arguments. If text length >= this value, uses clipboard.</td>\n\t/// <td>>=0.</td>\n\t/// </tr>\n\t/// <tr>\n\t/// <td><see cref=\"OKey.PasteWorkaround\"/></td>\n\t/// <td><c>false</c>.\n\t/// <br/>This option is used for \"text\" arguments when using clipboard.\n\t/// </td>\n\t/// <td><c>true</c>.</td>\n\t/// </tr>\n\t/// <tr>\n\t/// <td><see cref=\"OKey.RestoreClipboard\"/></td>\n\t/// <td><c>true</c>.\n\t/// Restore clipboard data (by default only text).\n\t/// <br/>This option is used for \"text\" and \"HTML\" arguments when using clipboard.</td>\n\t/// <td><c>false</c>.\n\t/// Don't restore clipboard data.</td>\n\t/// </tr>\n\t/// <tr>\n\t/// <td><see cref=\"OKey.Hook\"/></td>\n\t/// <td><c>null</c>.</td>\n\t/// <td>Callback function that can modify options depending on active window etc.</td>\n\t/// </tr>\n\t/// </table>\n\t/// \n\t/// This function does not wait until the target app receives and processes sent keystrokes and text; there is no reliable way to know it. It just adds small delays depending on options (<see cref=\"OKey.SleepFinally\"/> etc). If need, change options or add \"sleep\" arguments or wait after calling this function. Sending text through the clipboard normally does not have these problems.\n\t/// \n\t/// Don't use this function to automate windows of own thread. Call it from another thread. See example with async/await.\n\t/// \n\t/// Administrator and uiAccess processes don't receive keystrokes sent by standard user processes. See [](xref:uac).\n\t/// \n\t/// Mouse button codes/names (eg <see cref=\"KKey.MouseLeft\"/>) cannot be used to click. Instead use callback, like in the \"Ctrl+click\" example.\n\t/// \n\t/// You can use a <see cref=\"keys\"/> variable instead of this function. Example: <c>new keys(null).Add(\"keys\", \"!text\").SendNow();</c>. More examples in <see cref=\"keys(OKey)\"/> topic.\n\t/// \n\t/// This function calls <see cref=\"Add(KKeysEtc[])\"/>, which calls these functions depending on argument type: <see cref=\"AddKeys\"/>, <see cref=\"AddText\"/>, <see cref=\"AddChar\"/>, <see cref=\"AddClipboardData\"/>, <see cref=\"AddKey(KKey, bool?)\"/>, <see cref=\"AddKey(KKey, ushort, bool, bool?)\"/>, <see cref=\"AddSleep\"/>, <see cref=\"AddAction\"/>. Then calls <see cref=\"SendNow\"/>.\n\t/// \n\t/// Uses API <ms>SendInput</ms>.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// //Press key Enter.\n\t/// keys.send(\"Enter\");\n\t/// \n\t/// //Press keys Ctrl+A.\n\t/// keys.send(\"Ctrl+A\");\n\t/// \n\t/// //Ctrl+Alt+Shift+Win+A.\n\t/// keys.send(\"Ctrl+Alt+Shift+Win+A\");\n\t/// \n\t/// //Alt down, E, P, Alt up.\n\t/// keys.send(\"Alt+(E P)\");\n\t/// \n\t/// //Alt down, E, P, Alt up.\n\t/// keys.send(\"Alt*down E P Alt*up\");\n\t/// \n\t/// //Send text \"Example\".\n\t/// keys.send(\"!Example\");\n\t/// keys.sendt(\"Example\"); //same\n\t/// \n\t/// //Press key End, key Backspace 3 times, send text \"Text\".\n\t/// keys.send(\"End Back*3\", \"!Text\");\n\t/// \n\t/// //Press Tab n times, send text \"user\", press Tab, send text \"password\", press Enter.\n\t/// int n = 5; string pw = \"password\";\n\t/// keys.send($\"Tab*{n}\", \"!user\", \"Tab\", \"!\" + pw, \"Enter\");\n\t/// \n\t/// //Press Ctrl+V, wait 500 ms, press Enter.\n\t/// keys.send(\"Ctrl+V\", 500, \"Enter\");\n\t/// \n\t/// //F2, Ctrl+K, Left 3 times, Space, A, comma, 5, numpad 5, BrowserBack.\n\t/// keys.send(\"F2 Ctrl+K Left*3 Space a , 5 #5\", KKey.BrowserBack);\n\t/// \n\t/// //Shift down, A 3 times, Shift up.\n\t/// keys.send(\"Shift+A*3\");\n\t/// \n\t/// //Shift down, A 3 times, Shift up.\n\t/// keys.send(\"Shift+\", KKey.A, \"*3\");\n\t/// \n\t/// //Shift down, A, wait 500 ms, B, Shift up.\n\t/// keys.send(\"Shift+(\", KKey.A, 500, KKey.B, \")\");\n\t/// \n\t/// //Send keys and text slowly.\n\t/// opt.key.KeySpeed = opt.key.TextSpeed = 50;\n\t/// keys.send(\"keys Shift+: Space 123456789 Space 123456789 ,Space\", \"!text: 123456789 123456789\\n\");\n\t/// \n\t/// //Ctrl+click\n\t/// Action click = () => mouse.click();\n\t/// keys.send(\"Ctrl+\", click);\n\t/// \n\t/// //Ctrl+click\n\t/// keys.send(\"Ctrl+\", new Action(() => mouse.click()));\n\t/// ]]></code>\n\t/// Show window and send keys/text to it when button clicked.\n\t/// <code><![CDATA[\n\t/// var b = new wpfBuilder(\"Window\").WinSize(250);\n\t/// b.R.AddButton(\"Keys\", async _ => {\n\t/// \t//keys.send(\"Tab\", \"!text\", 2000, \"Esc\"); //no\n\t/// \tawait Task.Run(() => { keys.send(\"Tab\", \"!text\", 2000, \"Esc\"); }); //use other thread\n\t/// });\n\t/// b.R.Add(\"Text\", out TextBox text1);\n\t/// b.R.AddOkCancel();\n\t/// b.End();\n\t/// if (!b.ShowDialog()) return;\n\t/// ]]></code>\n\t/// </example>\n\tpublic static void send([ParamString(PSFormat.Keys)] params KKeysEtc[] keysEtc) {\n\t\tnew keys(opt.key).Add(keysEtc).SendNow();\n\t}\n\t//CONSIDER: move most of Remarks to Articles. Also make the param doc smaller, and move the big list to Remarks.\n\n\t/// <summary>\n\t/// Generates virtual keystrokes. Like <see cref=\"send\"/>, but without reliability features: delays, user input blocking, resetting modifiers/<c>CapsLock</c>.\n\t/// </summary>\n\t/// <remarks>\n\t/// Ignores <see cref=\"opt.key\"/> and instead uses default options with these changes:\n\t/// - <c>SleepFinally</c> = 0.\n\t/// - <c>KeySpeed</c> = 0.\n\t/// - <c>NoBlockInput</c> = <c>true</c>.\n\t/// - <c>NoCapsOff</c> = <c>true</c>.\n\t/// - <c>NoModOff</c> = <c>true</c>.\n\t/// </remarks>\n\t/// <seealso cref=\"more.sendKey\"/>\n\t/// <inheritdoc cref=\"keys.send\" path=\"//param|//exception\"/>\n\tpublic static void sendL([ParamString(PSFormat.Keys)] params KKeysEtc[] keysEtc) {\n\t\tvar o = new OKey() { KeySpeed = 0, NoBlockInput = true, NoCapsOff = true, NoModOff = true, SleepFinally = 0 };\n\t\tnew keys(o).Add(keysEtc).SendNow();\n\t}\n\n\t/// <summary>\n\t/// Sends text to the active window, using virtual keystrokes or clipboard.\n\t/// </summary>\n\t/// <param name=\"text\">Text. Can be <c>null</c>.</param>\n\t/// <param name=\"html\">\n\t/// HTML. Can be full HTML or fragment. See <see cref=\"clipboardData.AddHtml\"/>.\n\t/// Can be specified only <i>text</i> or only <i>html</i> or both. If both, will paste <i>html</i> in apps that support it, elsewhere <i>text</i>. If only <i>html</i>, in apps that don't support HTML will paste <i>html</i> as text.\n\t/// </param>\n\t/// <exception cref=\"AuException\">Failed. Fails if there is no focused window.</exception>\n\t/// <exception cref=\"InputDesktopException\"></exception>\n\t/// <remarks>\n\t/// Calls <see cref=\"AddText(string, string)\"/> and <see cref=\"SendNow\"/>.\n\t/// To send text can use keys, characters or clipboard, depending on <see cref=\"opt.key\"/> and text. If <i>html</i> not <c>null</c>, uses clipboard.\n\t/// </remarks>\n\t/// <seealso cref=\"clipboard.paste\"/>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// keys.sendt(\"Text.\\r\\n\");\n\t/// ]]></code>\n\t/// Or use function <see cref=\"send\"/> and prefix <c>\"!\"</c>. For HTML use prefix <c>\"%\"</c>.\n\t/// <code><![CDATA[\n\t/// keys.send(\"!Send this text and press key\", \"Enter\");\n\t/// keys.send(\"%<b>bold</b> <i>italic</i>\", \"Enter\");\n\t/// ]]></code>\n\t/// </example>\n\tpublic static void sendt(string text, string html = null) {\n\t\tnew keys(opt.key).AddText(text, html).SendNow();\n\t}\n}\n\n//FUTURE: instead of QM2 AutoPassword: FocusPasswordField(); keys.send(\"!password\", \"Shift+Tab\", \"user\", \"Enter\");\n//public static void FocusPasswordField()\n"
  },
  {
    "path": "Au/Input/keys_types.cs",
    "content": "namespace Au.Types;\n\n#pragma warning disable 1591 //missing doc\n\n/// <summary>\n/// Modifier keys as flags.\n/// </summary>\n/// <seealso cref=\"keys.more.KModToWinforms\"/>\n/// <seealso cref=\"keys.more.KModFromWinforms\"/>\n/// <seealso cref=\"KKey\"/>\n[Flags]\npublic enum KMod : byte {\n\tShift = 1,\n\tCtrl = 2,\n\tAlt = 4,\n\tWin = 8,\n}\n\n/// <summary>\n/// Virtual-key codes.\n/// </summary>\n/// <remarks>\n/// The values are the same as the native <c>VK_</c> constants. Also the same as in the <see cref=\"System.Windows.Forms.Keys\"/> enum, but not as in the WPF <c>Key</c> enum.\n/// Rare and obsolete keys are not included. You can use <c>Keys</c> like <c>(KKey)Keys.Attn</c> or <c>VK_</c> constant values like <c>(KKey)200</c>.\n/// </remarks>\n/// <seealso cref=\"KMod\"/>\npublic enum KKey : byte {\n\tMouseLeft = 0x01,\n\tMouseRight = 0x02,\n\t///<summary><c>Ctrl+Pause</c>.</summary>\n\tBreak = 0x03,\n\tMouseMiddle = 0x04,\n\tMouseX1 = 0x05,\n\tMouseX2 = 0x06,\n\tBack = 0x08,\n\tTab = 0x09,\n\t///<summary><c>Shift+NumPad5</c>, or <c>NumPad5</c> when <c>NumLock</c> off.</summary>\n\tClear = 0x0C,\n\tEnter = 0x0D,\n\tShift = 0x10,\n\tCtrl = 0x11,\n\tAlt = 0x12,\n\tPause = 0x13,\n\tCapsLock = 0x14,\n\tIMEKanaMode = 0x15,\n\tIMEHangulMode = 0x15,\n\tIMEJunjaMode = 0x17,\n\tIMEFinalMode = 0x18,\n\tIMEHanjaMode = 0x19,\n\tIMEKanjiMode = 0x19,\n\tEscape = 0x1B,\n\tIMEConvert = 0x1C,\n\tIMENonconvert = 0x1D,\n\tIMEAccept = 0x1E,\n\tIMEModeChange = 0x1F,\n\tSpace = 0x20,\n\tPageUp = 0x21,\n\tPageDown = 0x22,\n\tEnd = 0x23,\n\tHome = 0x24,\n\tLeft = 0x25,\n\tUp = 0x26,\n\tRight = 0x27,\n\tDown = 0x28,\n\t//Select = 0x29,\n\t//Print = 0x2A,\n\t//Execute= 0x2B,\n\tPrintScreen = 0x2C,\n\tInsert = 0x2D,\n\tDelete = 0x2E,\n\t//Help = 0x2F,\n\t///<summary>The 0 <c>)</c> key.</summary>\n\tD0 = 0x30,\n\t///<summary>The 1 <c>!</c> key.</summary>\n\tD1 = 0x31,\n\t///<summary>The 2 <c>@</c> key.</summary>\n\tD2 = 0x32,\n\t///<summary>The 3 <c>#</c> key.</summary>\n\tD3 = 0x33,\n\t///<summary>The 4 <c>$</c> key.</summary>\n\tD4 = 0x34,\n\t///<summary>The 5 <c>%</c> key.</summary>\n\tD5 = 0x35,\n\t///<summary>The 6 <c>^</c> key.</summary>\n\tD6 = 0x36,\n\t///<summary>The 7 <c>&amp;</c> key.</summary>\n\tD7 = 0x37,\n\t///<summary>The 8 <c>*</c> key.</summary>\n\tD8 = 0x38,\n\t///<summary>The 9 <c>(</c> key.</summary>\n\tD9 = 0x39,\n\tA = 0x41,\n\tB = 0x42,\n\tC = 0x43,\n\tD = 0x44,\n\tE = 0x45,\n\tF = 0x46,\n\tG = 0x47,\n\tH = 0x48,\n\tI = 0x49,\n\tJ = 0x4A,\n\tK = 0x4B,\n\tL = 0x4C,\n\tM = 0x4D,\n\tN = 0x4E,\n\tO = 0x4F,\n\tP = 0x50,\n\tQ = 0x51,\n\tR = 0x52,\n\tS = 0x53,\n\tT = 0x54,\n\tU = 0x55,\n\tV = 0x56,\n\tW = 0x57,\n\tX = 0x58,\n\tY = 0x59,\n\tZ = 0x5A,\n\t///<summary>The left <c>Win</c> key.</summary>\n\tWin = 0x5B,\n\t///<summary>The right <c>Win</c> key.</summary>\n\tRWin = 0x5C,\n\t///<summary>The <c>Application/Menu</c> key.</summary>\n\tApps = 0x5D,\n\tSleep = 0x5F,\n\tNumPad0 = 0x60,\n\tNumPad1 = 0x61,\n\tNumPad2 = 0x62,\n\tNumPad3 = 0x63,\n\tNumPad4 = 0x64,\n\tNumPad5 = 0x65,\n\tNumPad6 = 0x66,\n\tNumPad7 = 0x67,\n\tNumPad8 = 0x68,\n\tNumPad9 = 0x69,\n\t///<summary>The numpad <c>*</c> key.</summary>\n\tMultiply = 0x6A,\n\t///<summary>The numpad <c>+</c> key.</summary>\n\tAdd = 0x6B,\n\t//Separator = 0x6C,\n\t///<summary>The numpad <c>-</c> key.</summary>\n\tSubtract = 0x6D,\n\t///<summary>The numpad <c>.</c> key.</summary>\n\tDecimal = 0x6E,\n\t///<summary>The numpad <c>/</c> key.</summary>\n\tDivide = 0x6F,\n\tF1 = 0x70,\n\tF2 = 0x71,\n\tF3 = 0x72,\n\tF4 = 0x73,\n\tF5 = 0x74,\n\tF6 = 0x75,\n\tF7 = 0x76,\n\tF8 = 0x77,\n\tF9 = 0x78,\n\tF10 = 0x79,\n\tF11 = 0x7A,\n\tF12 = 0x7B,\n\tF13 = 0x7C,\n\tF14 = 0x7D,\n\tF15 = 0x7E,\n\tF16 = 0x7F,\n\tF17 = 0x80,\n\tF18 = 0x81,\n\tF19 = 0x82,\n\tF20 = 0x83,\n\tF21 = 0x84,\n\tF22 = 0x85,\n\tF23 = 0x86,\n\tF24 = 0x87,\n\t//VK_NAVIGATION_VIEW ... VK_NAVIGATION_CANCEL\n\tNumLock = 0x90,\n\tScrollLock = 0x91,\n\t//VK_OEM_NEC_EQUAL ... VK_OEM_FJ_ROYA\n\t///<summary>The left <c>Shift</c> key.</summary>\n\tLShift = 0xA0,\n\t///<summary>The right <c>Shift</c> key.</summary>\n\tRShift = 0xA1,\n\t///<summary>The left <c>Ctrl</c> key.</summary>\n\tLCtrl = 0xA2,\n\t///<summary>The right <c>Ctrl</c> key.</summary>\n\tRCtrl = 0xA3,\n\t///<summary>The left <c>Alt</c> key.</summary>\n\tLAlt = 0xA4,\n\t///<summary>The right <c>Alt</c> key.</summary>\n\tRAlt = 0xA5,\n\tBrowserBack = 0xA6,\n\tBrowserForward = 0xA7,\n\tBrowserRefresh = 0xA8,\n\tBrowserStop = 0xA9,\n\tBrowserSearch = 0xAA,\n\tBrowserFavorites = 0xAB,\n\tBrowserHome = 0xAC,\n\tVolumeMute = 0xAD,\n\tVolumeDown = 0xAE,\n\tVolumeUp = 0xAF,\n\tMediaNextTrack = 0xB0,\n\tMediaPrevTrack = 0xB1,\n\tMediaStop = 0xB2,\n\tMediaPlayPause = 0xB3,\n\tLaunchMail = 0xB4,\n\tLaunchMediaSelect = 0xB5,\n\tLaunchApp1 = 0xB6,\n\tLaunchApp2 = 0xB7,\n\tOemSemicolon = 0xBA,\n\tOemPlus = 0xBB,\n\tOemComma = 0xBC,\n\tOemMinus = 0xBD,\n\tOemPeriod = 0xBE,\n\tOemQuestion = 0xBF,\n\tOemTilde = 0xC0,\n\t//VK_GAMEPAD_A ... VK_GAMEPAD_RIGHT_THUMBSTICK_LEFT\n\tOemOpenBrackets = 0xDB,\n\tOemPipe = 0xDC,\n\tOemCloseBrackets = 0xDD,\n\tOemQuotes = 0xDE,\n\t//VK_OEM_8 ... VK_ICO_00\n\tIMEProcessKey = 0xE5,\n\t//VK_ICO_CLEAR\n\t///<summary><ms>VK_PACKET</ms>. Not a key.</summary>\n\tPacket = 0xE7,\n\t//VK_OEM_RESET ... VK_OEM_BACKTAB\n\t//Attn = 0xF6,\n\t//Crsel = 0xF7,\n\t//Exsel = 0xF8,\n\t//EraseEof = 0xF9,\n\t//Play = 0xFA,\n\t//Zoom = 0xFB,\n\t//NoName = 0xFC,\n\t//Pa1 = 0xFD,\n\t//OemClear = 0xFE,\n}\n\n/// <summary>\n/// Virtual-key code, scan code and extended-key flag for <see cref=\"keys.send\"/> and similar functions.\n/// </summary>\n/// <example>\n/// This script prints properties of pressed keys.\n/// <code><![CDATA[\n/// using var hook = WindowsHook.Keyboard(k=> {\n/// \tif(!k.IsUp) print.it(k.Key, k.vkCode, k.scanCode, k.IsExtended);\n/// }, ignoreAuInjected: false);\n/// dialog.show(\"Hook\");\n/// ]]></code>\n/// </example>\npublic struct KKeyScan {\n\tpublic KKey vk;\n\tpublic bool extendedKey;\n\tpublic ushort scanCode;\n\n\tpublic KKeyScan(KKey vk, ushort scanCode, bool extendedKey) { this.vk = vk; this.scanCode = scanCode; this.extendedKey = extendedKey; }\n\n\tpublic KKeyScan(ushort scanCode, bool extendedKey) { vk = 0; this.scanCode = scanCode; this.extendedKey = extendedKey; }\n\n\tpublic KKeyScan(KKey vk, bool extendedKey) { this.vk = vk; this.scanCode = 0; this.extendedKey = extendedKey; }\n}\n\n/// <summary>\n/// Parameter type of <see cref=\"keys.send\"/> and similar functions.\n/// Has implicit conversions from <c>string</c>, <see cref=\"clipboardData\"/>, <see cref=\"KKey\"/>, <see cref=\"KKeyScan\"/>, <c>char</c>, <c>int</c> (sleep time) and <see cref=\"Action\"/>.\n/// </summary>\npublic struct KKeysEtc {\n\treadonly object _o;\n\tKKeysEtc(object o) { _o = o; }\n\n\tpublic KKeysEtc(Action a) { _o = a; } //allows 'new(() => {})' instead of 'new Action(() => {})'\n\n\tpublic object Value => _o;\n\n\tpublic static implicit operator KKeysEtc(string s) => new(s);\n\tpublic static implicit operator KKeysEtc(clipboardData cd) => new(cd);\n\tpublic static implicit operator KKeysEtc(KKey k) => new(k);\n\tpublic static implicit operator KKeysEtc(KKeyScan t) => new(t);\n\tpublic static implicit operator KKeysEtc(char c) => new(c);\n\tpublic static implicit operator KKeysEtc(int ms) => new(ms);\n\tpublic static implicit operator KKeysEtc(Action a) => new(a);\n}\n\n/// <summary>\n/// <see cref=\"keys.Pasting\"/> event data.\n/// </summary>\npublic class PastingEventArgs : EventArgs {\n\t///\n\tpublic string Text { get; init; }\n\t///\n\tpublic OKey Options { get; init; }\n\t///\n\tpublic wnd WndFocus { get; init; }\n}\n\n#pragma warning restore 1591\n\n/// <summary>\n/// Defines a hotkey as <see cref=\"KMod\"/> and <see cref=\"KKey\"/>.\n/// Has implicit conversion operators from string like <c>\"Ctrl+Shift+K\"</c>, tuple <c>(KMod, KKey)</c>, enum <see cref=\"KKey\"/>, enum <c>Keys</c>.\n/// </summary>\npublic struct KHotkey {\n\t/// <summary>\n\t/// Modifier keys (flags).\n\t/// </summary>\n\tpublic KMod Mod { get; set; }\n\n\t/// <summary>\n\t/// Key without modifier keys.\n\t/// </summary>\n\tpublic KKey Key { get; set; }\n\n\t///\n\tpublic KHotkey(KMod mod, KKey key) { Mod = mod; Key = key; }\n\n\t/// <summary>Implicit conversion from string like <c>\"Ctrl+Shift+K\"</c>.</summary>\n\t/// <exception cref=\"ArgumentException\">Error in hotkey.</exception>\n\tpublic static implicit operator KHotkey(string hotkey) {\n\t\tif (!keys.more.parseHotkeyString(hotkey, out var mod, out var key)) throw new ArgumentException(\"Error in hotkey.\");\n\t\treturn new KHotkey(mod, key);\n\t}\n\n\t/// <summary>Implicit conversion from tuple <c>(KMod, KKey)</c>.</summary>\n\tpublic static implicit operator KHotkey((KMod, KKey) hotkey) => new KHotkey(hotkey.Item1, hotkey.Item2);\n\n\t/// <summary>Implicit conversion from <see cref=\"KKey\"/> (hotkey without modifiers).</summary>\n\tpublic static implicit operator KHotkey(KKey key) => new KHotkey(0, key);\n\n\t/// <summary>Implicit conversion from <see cref=\"System.Windows.Forms.Keys\"/> like <c>Keys.Ctrl|Keys.B</c>.</summary>\n\tpublic static implicit operator KHotkey(System.Windows.Forms.Keys hotkey) => new KHotkey(keys.more.KModFromWinforms(hotkey), (KKey)(byte)hotkey);\n\n\t/// <summary>Explicit conversion to <see cref=\"System.Windows.Forms.Keys\"/>.</summary>\n\tpublic static explicit operator System.Windows.Forms.Keys(KHotkey hk) => keys.more.KModToWinforms(hk.Mod) | (System.Windows.Forms.Keys)hk.Key;\n\n\t/// <summary>Allows to get properties of a <see cref=\"KHotkey\"/> variable like <c>var (mod, key) = hotkey;</c></summary>\n\tpublic void Deconstruct(out KMod mod, out KKey key) { mod = Mod; key = Key; }\n\n\t///\n\tpublic override string ToString() => keys.more.hotkeyToString(Mod, Key);\n}\n"
  },
  {
    "path": "Au/Input/keys_util.cs",
    "content": "namespace Au;\n\npublic partial class keys {\n\t/// <summary>\n\t/// Converts part of string to <see cref=\"KKey\"/>.\n\t/// The substring should contain single key name, eg <c>\"Esc\"</c>, <c>\"A\"</c>, <c>\"=\"</c>.\n\t/// Returns 0 if invalid key name.\n\t/// </summary>\n\tstatic unsafe KKey _KeynameToKey(string s, int i, int len) {\n\t\t//print.it(s, i, len);\n\t\tif (len < 1) return 0;\n\t\t\n\t\tchar c = s[i];\n\t\t\n\t\t//character keys, like K, 9, -\n\t\tif (len == 1) {\n\t\t\treturn c switch {\n\t\t\t\t>= 'a' and <= 'z' => (KKey)(c - 32),\n\t\t\t\t>= 'A' and <= 'Z' => (KKey)c,\n\t\t\t\t>= '0' and <= '9' => (KKey)c,\n\t\t\t\t'-' => KKey.OemMinus,\n\t\t\t\t'=' => KKey.OemPlus,\n\t\t\t\t'`' or '~' => KKey.OemTilde,\n\t\t\t\t'[' or '{' => KKey.OemOpenBrackets,\n\t\t\t\t']' or '}' => KKey.OemCloseBrackets,\n\t\t\t\t'\\\\' or '|' => KKey.OemPipe,\n\t\t\t\t';' or ':' => KKey.OemSemicolon,\n\t\t\t\t'\\'' or '\"' => KKey.OemQuotes,\n\t\t\t\t',' or '<' => KKey.OemComma,\n\t\t\t\t'.' or '>' => KKey.OemPeriod,\n\t\t\t\t'/' or '?' => KKey.OemQuestion,\n\t\t\t\t_ => 0,\n\t\t\t};\n\t\t\t\n\t\t\t//special\n\t\t\t//+ //eg Ctrl+A\n\t\t\t//* //*nTimes, *down, *up\n\t\t\t//( //eg Alt+(A F)\n\t\t\t//)\n\t\t\t//# //numpad keys, eg #5, #*\n\t\t\t//_ //character\n\t\t\t//^ //characters\n\t\t\t\n\t\t\t//reserved\n\t\t\t//! @ $ % &\n\t\t}\n\t\t\n\t\t//numpad keys\n\t\tif (c == '#') {\n\t\t\tif (len != 2) return 0;\n\t\t\tc = s[i + 1];\n\t\t\treturn c switch {\n\t\t\t\t>= '0' and <= '9' => (KKey)(c - '0' + (int)KKey.NumPad0),\n\t\t\t\t'.' => KKey.Decimal,\n\t\t\t\t'+' => KKey.Add,\n\t\t\t\t'/' => KKey.Divide,\n\t\t\t\t'*' => KKey.Multiply,\n\t\t\t\t'-' => KKey.Subtract,\n\t\t\t\t_ => 0,\n\t\t\t};\n\t\t}\n\t\t\n\t\t//F keys\n\t\tif (c == 'F' && s[i + 1].IsAsciiDigit()) {\n\t\t\tint n = s.ToInt(i + 1, out int e, STIFlags.NoHex);\n\t\t\tif (n > 0 && n <= 24 && e == i + len) return (KKey)(0x6F + n);\n\t\t}\n\t\t\n\t\t//named keys\n\t\t//names start with an uppercase letter and must have at least 2 other anycase letters, except: Up, AltG (RAl), PageU (PgU), PageD (PgD), some alternative names (PU, PD, PB, PS, HM, SL, CL, NL, BS).\n\t\tKKey k = 0;\n\t\tchar c1 = char.ToLowerInvariant(s[i + 1]), //note: Tables_.LowerCase would make startup slow\n\t\t\tc2 = len > 2 ? char.ToLowerInvariant(s[i + 2]) : ' ',\n\t\t\tc3 = len > 3 ? char.ToLowerInvariant(s[i + 3]) : ' ',\n\t\t\tc4 = len > 4 ? char.ToLowerInvariant(s[i + 4]) : ' ';\n\t\tuint u = (uint)c1 << 16 | c2;\n\t\tswitch (c) {\n\t\tcase 'A':\n\t\t\tif (_U('l', 't')) k = c3 == 'g' ? KKey.RAlt : KKey.Alt;\n\t\t\telse if (_U('p', 'p')) k = KKey.Apps;\n\t\t\tbreak;\n\t\tcase 'B':\n\t\t\tif (_U('a', 'c') || _U('s', ' ')) k = KKey.Back;\n\t\t\tbreak;\n\t\tcase 'C':\n\t\t\tif (_U('t', 'r')) k = KKey.Ctrl;\n\t\t\telse if (_U('a', 'p') || _U('l', ' ')) k = KKey.CapsLock;\n\t\t\tbreak;\n\t\tcase 'D':\n\t\t\tif (_U('e', 'l')) k = KKey.Delete;\n\t\t\telse if (_U('o', 'w')) k = KKey.Down;\n\t\t\tbreak;\n\t\tcase 'E':\n\t\t\tif (_U('n', 't')) k = KKey.Enter;\n\t\t\telse if (_U('n', 'd')) k = KKey.End;\n\t\t\telse if (_U('s', 'c')) k = KKey.Escape;\n\t\t\tbreak;\n\t\tcase 'H':\n\t\t\tif (_U('o', 'm') || _U('m', ' ')) k = KKey.Home;\n\t\t\tbreak;\n\t\tcase 'I':\n\t\t\tif (_U('n', 's')) k = KKey.Insert;\n\t\t\tbreak;\n\t\tcase 'L':\n\t\t\tif (_U('e', 'f')) k = KKey.Left;\n\t\t\t//don't need LShift etc\n\t\t\tbreak;\n\t\tcase 'M':\n\t\t\tif (_U('e', 'n')) k = KKey.Apps;\n\t\t\tbreak;\n\t\tcase 'N':\n\t\t\tif (_U('u', 'm') || _U('l', ' ')) k = KKey.NumLock;\n\t\t\t//for NumEnter use keys.send((KKey.Enter, 0, true))\n\t\t\tbreak;\n\t\tcase 'P':\n\t\t\tif (_U('a', 'g') && c3 == 'e') k = c4 == 'u' ? KKey.PageUp : (c4 == 'd' ? KKey.PageDown : 0);\n\t\t\telse if (_U('g', 'u') || _U('u', ' ')) k = KKey.PageUp;\n\t\t\telse if (_U('g', 'd') || _U('d', ' ')) k = KKey.PageDown;\n\t\t\telse if (_U('a', 'u') || _U('b', ' ')) k = KKey.Pause;\n\t\t\telse if (_U('r', 'i') || _U('r', 't') || _U('s', ' ')) k = KKey.PrintScreen;\n\t\t\tbreak;\n\t\tcase 'R':\n\t\t\tif (_U('i', 'g')) k = KKey.Right;\n\t\t\telse if (_U('a', 'l')) k = KKey.RAlt;\n\t\t\telse if (_U('c', 't')) k = KKey.RCtrl;\n\t\t\telse if (_U('s', 'h')) k = KKey.RShift;\n\t\t\telse if (_U('w', 'i')) k = KKey.RWin;\n\t\t\tbreak;\n\t\tcase 'S':\n\t\t\tif (_U('h', 'i')) k = KKey.Shift;\n\t\t\telse if (_U('p', 'a')) k = KKey.Space;\n\t\t\telse if (_U('c', 'r') || _U('l', ' ')) k = KKey.ScrollLock;\n\t\t\t//SysRq not used on Windows\n\t\t\tbreak;\n\t\tcase 'T':\n\t\t\tif (_U('a', 'b')) k = KKey.Tab;\n\t\t\tbreak;\n\t\tcase 'U':\n\t\t\tif (c1 == 'p') k = KKey.Up;\n\t\t\tbreak;\n\t\tcase 'V':\n\t\t\tif (c1 == 'k') {\n\t\t\t\tint v = s.ToInt(i + 2, out int end, STIFlags.DontSkipSpaces);\n\t\t\t\tif (end != i + len || (uint)v > 255) v = 0;\n\t\t\t\treturn (KKey)v;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'W':\n\t\t\tif (_U('i', 'n')) k = KKey.Win;\n\t\t\tbreak;\n\t\t}\n\t\tif (k != 0) {\n\t\t\tfor (int i2 = i + len; i < i2; i++) if (!s[i].IsAsciiAlpha()) return 0;\n\t\t\treturn k;\n\t\t}\n\t\t\n\t\tif (c >= 'A' && c <= 'Z') {\n\t\t\tvar s1 = s.Substring(i, len);\n#if false\n\t\t\tif(Enum.TryParse(s1, true, out KKey r1)) return r1;\n\t\t\t//if(Enum.TryParse(s1, true, out System.Windows.Forms.Keys r2) && (uint)r2 <= 0xff) return (KKey)r2;\n#else //20-50 times faster and less garbage. Good JIT speed.\n\t\t\treturn _FindKeyInEnums(s1);\n#endif\n\t\t}\n\t\treturn 0;\n\t\t\n\t\tbool _U(char cc1, char cc2) { return u == ((uint)cc1 << 16 | cc2); }\n\t}\n\t\n\tstatic IEnumerable<RXGroup> _SplitKeysString(string keys_) =>\n\t\t(s_rxKeys ??= new regexp(@\"(?s)[A-Z][[:alnum:]]*|#\\S|\\*\\s*(?:\\d+|down|up)\\b|\\+\\s*\\(|_.|\\^.+|\\S\"))\n\t\t.FindAllG(keys_ ?? \"\", 0);\n\t//KeyName | #n | *r | *down | *up | +( | _char | ^chars | nonspace char\n\tstatic regexp s_rxKeys;\n\t\n\tstatic System.Collections.Hashtable s_htEnum; //with Dictionary much slower JIT\n\t[MethodImpl(MethodImplOptions.NoInlining)]\n\tstatic KKey _FindKeyInEnums(string key) {\n\t\tif (s_htEnum == null) {\n\t\t\tvar t = new System.Collections.Hashtable(160 /*220*/, StringComparer.OrdinalIgnoreCase);\n\t\t\tvar a1 = typeof(KKey).GetFields();\n\t\t\tfor (int j = 1; j < a1.Length; j++) { //note: start from 1 to skip the default value__, it gives exception\n\t\t\t\tt.Add(a1[j].Name, a1[j].GetRawConstantValue());\n\t\t\t}\n\t\t\t\n\t\t\t//rejected. Better avoid loading Forms dll. All useful keys are in KKey. For others can use virtual-key codes.\n\t\t\t//var a2 = typeof(System.Windows.Forms.Keys).GetFields();\n\t\t\t//for(int j = 4; j < a2.Length; j++) { //skip value__, KeyCode, Modifiers, None\n\t\t\t//\tvar v = a2[j];\n\t\t\t//\t//print.it(v.Name);\n\t\t\t//\tif(t.ContainsKey(v.Name)) continue;\n\t\t\t//\tvar k = v.GetRawConstantValue();\n\t\t\t//\tif((uint)(int)k >= 0xff) continue;\n\t\t\t//\tprint.it(v.Name, j);\n\t\t\t//\tt.Add(v.Name, k);\n\t\t\t//}\n\t\t\t\n\t\t\t//print.it(a1.Length, /*a2.Length,*/ t.Count); //216 with Keys enum, 156 without\n\t\t\ts_htEnum = t;\n\t\t}\n\t\tvar r = s_htEnum[key];\n\t\tif (r == null) return 0;\n\t\tif (r is byte) return (KKey)(byte)r;\n\t\treturn (KKey)(int)r;\n\t\t//note: GetRawConstantValue gets byte for KKey, int for Keys. GetValue(null) gets of enum type.\n\t}\n\t\n\tstatic ArgumentException _ArgumentException_ErrorInKeysString(string keys_, int i, int len) {\n\t\tint end = i + len;\n\t\treturn new ArgumentException($\"Error in keys string: {keys_[..i]}■{keys_[i..end]}■{keys_[end..]}\");\n\t\t//tested: all fonts on Win7 have ■.\n\t}\n\t\n\t/// <summary>\n\t/// Internal static functions.\n\t/// </summary>\n\tinternal static class Internal_ {\n\t\t/// <summary>\n\t\t/// Calls <see cref=\"wait.doEvents(int)\"/>.\n\t\t/// </summary>\n\t\tinternal static void Sleep(int ms) {\n\t\t\tif (ms > 0) wait.doEventsPrecise_(ms);\n\t\t\t\n\t\t\t//see comments in mouse._Sleep.\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// If <c>t > 10</c>, returns <c>(t / 4 + 8)</c>.\n\t\t/// </summary>\n\t\tinternal static int LimitSleepTime(int t) => t <= 10 ? t : (t / 4 + 8);\n\t\t\n\t\t/// <summary>\n\t\t/// If <i>k</i> is <c>Shift</c>, <c>Ctrl</c>, <c>Alt</c> or <c>Win</c> or their left/right versions, returns it as modifier flag, eg <c>KMod.Shift</c>.\n\t\t/// Else returns 0.\n\t\t/// </summary>\n\t\tinternal static KMod KeyToMod(KKey k) {\n\t\t\treturn k switch {\n\t\t\t\tKKey.Shift or KKey.LShift or KKey.RShift => KMod.Shift,\n\t\t\t\tKKey.Ctrl or KKey.LCtrl or KKey.RCtrl => KMod.Ctrl,\n\t\t\t\tKKey.Alt or KKey.LAlt or KKey.RAlt => KMod.Alt,\n\t\t\t\tKKey.Win or KKey.RWin => KMod.Win,\n\t\t\t\t_ => 0,\n\t\t\t};\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets scan code from virtual-key code.\n\t\t/// </summary>\n\t\t/// <param name=\"vk\"></param>\n\t\t/// <param name=\"hkl\">Keyboard layout. If 0, uses of current thread. If -1, uses current focus/active thread.</param>\n\t\tinternal static ushort VkToSc(KKey vk, nint hkl = 0) {\n\t\t\tif (hkl == 0) hkl = Api.GetKeyboardLayout(0);\n\t\t\telse if (hkl == -1) {\n\t\t\t\tvar w = wnd.focused; if (w.Is0) w = wnd.active;\n\t\t\t\thkl = Api.GetKeyboardLayout(w.ThreadId);\n\t\t\t}\n\t\t\t\n\t\t\tuint sc = Api.MapVirtualKeyEx((uint)vk, 0, hkl); //MAPVK_VK_TO_VSC\n\t\t\t\n\t\t\t//fix Windows bugs\n\t\t\tif (vk == KKey.PrintScreen && sc == 0x54) sc = 0x37;\n\t\t\tif (vk == KKey.Pause && sc == 0) sc = 0x45;\n\t\t\t\n\t\t\treturn (ushort)sc;\n\t\t\t\n\t\t\t//tested: LCtrl, RCtrl etc are correctly sent, although MSDN does not mention that SendInput supports it.\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Sends one key event.\n\t\t/// Just calls API <c>SendInput</c> with raw parameters.\n\t\t/// </summary>\n\t\t/// <exception cref=\"InputDesktopException\"></exception>\n\t\tinternal static unsafe void SendKeyEventRaw(KKey vk, ushort scan, uint flags, int? extra = null, bool dontThrow = false) {\n\t\t\tvar ki = new Api.INPUTK(vk, scan, flags);\n\t\t\tif (extra != null) ki.dwExtraInfo = extra.Value;\n\t\t\tApi.SendInput(&ki, dontThrow: dontThrow);\n\t\t}\n\t\t\n\t\t/// <inheritdoc cref=\"more.sendKey\"/>\n\t\tinternal static void SendKey(KKey k, bool? down = null, nint hkl = 0, int? extra = null, bool dontThrow = false) {\n\t\t\tuint f = 0;\n\t\t\tif (KeyTypes_.IsExtended(k)) f |= Api.KEYEVENTF_EXTENDEDKEY;\n\t\t\tushort scan = VkToSc(k, hkl);\n\t\t\t\n\t\t\tif (down != false) SendKeyEventRaw(k, scan, f, extra, dontThrow);\n\t\t\tif (down != true) SendKeyEventRaw(k, scan, f | Api.KEYEVENTF_KEYUP, extra, dontThrow);\n\t\t}\n\t\t\n\t\t/// <inheritdoc cref=\"SendKeyEventRaw\"/>\n\t\tinternal static void SendCtrl(bool down) => SendKeyEventRaw(KKey.Ctrl, 0x1D, down ? 0 : Api.KEYEVENTF_KEYUP);\n\t\t\n\t\t/// <inheritdoc cref=\"SendKeyEventRaw\"/>\n\t\tinternal static void SendAlt(bool down) => SendKeyEventRaw(KKey.Alt, 0x38, down ? 0 : Api.KEYEVENTF_KEYUP);\n\t\t\n\t\t/// <inheritdoc cref=\"SendKeyEventRaw\"/>\n\t\tinternal static void SendShift(bool down) => SendKeyEventRaw(KKey.Shift, 0x2A, down ? 0 : Api.KEYEVENTF_KEYUP);\n\t\t\n\t\t/// <inheritdoc cref=\"SendKeyEventRaw\"/>\n\t\tinternal static void SendRCtrlUp() => SendKeyEventRaw(KKey.Ctrl, 0x1D, Api.KEYEVENTF_KEYUP | Api.KEYEVENTF_EXTENDEDKEY);\n\t\t\n\t\t/// <inheritdoc cref=\"SendKeyEventRaw\"/>\n\t\tinternal static void SendRAltUp() => SendKeyEventRaw(KKey.Alt, 0x38, Api.KEYEVENTF_KEYUP | Api.KEYEVENTF_EXTENDEDKEY);\n\t\t\n\t\t/// <inheritdoc cref=\"SendKeyEventRaw\"/>\n\t\tinternal static void SendRShiftUp() => SendKeyEventRaw(KKey.Shift, 0x36, Api.KEYEVENTF_KEYUP);\n\t\t\n\t\t/// <summary>\n\t\t/// Presses or releases one or more modifier keys.\n\t\t/// Sends in this order: <c>Ctrl</c>, <c>Alt</c>, <c>Shift</c>, <c>Win</c>.\n\t\t/// </summary>\n\t\t/// <param name=\"press\"></param>\n\t\t/// <param name=\"mod\">Modifier keys. Does nothing if 0.</param>\n\t\t/// <exception cref=\"InputDesktopException\"></exception>\n\t\tinternal static unsafe void ModPressRelease(bool press, KMod mod) {\n\t\t\tif (mod == 0) return;\n\t\t\tvar a = stackalloc Api.INPUTK[4];\n\t\t\tint n = 0; uint f = press ? 0 : Api.KEYEVENTF_KEYUP;\n\t\t\tif (0 != (mod & KMod.Ctrl)) a[n++].Set(KKey.Ctrl, 0x1D, f);\n\t\t\tif (0 != (mod & KMod.Alt)) a[n++].Set(KKey.Alt, 0x38, f);\n\t\t\tif (0 != (mod & KMod.Shift)) a[n++].Set(KKey.Shift, 0x2A, f);\n\t\t\tif (0 != (mod & KMod.Win)) a[n++].Set(KKey.Win, 0x5B, f);\n\t\t\tApi.SendInput(a, n);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Releases modifier keys if pressed.\n\t\t/// May also press-release some other keys to avoid menu mode etc.\n\t\t/// Does not use options, sleep, blockinput, etc.\n\t\t/// </summary>\n\t\t/// <exception cref=\"InputDesktopException\"></exception>\n\t\tinternal static void ReleaseModAndDisableModMenu(bool dontThrow = false) {\n\t\t\tif (dontThrow) {\n\t\t\t\ttry { ReleaseModAndDisableModMenu(); }\n\t\t\t\tcatch (Exception ex) { Debug_.Print(ex); }\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\tint m = 0;\n\t\t\tif (isPressed(KKey.LShift)) m |= 1;\n\t\t\tif (isPressed(KKey.RShift)) m |= 0x10;\n\t\t\tif (isPressed(KKey.LCtrl)) m |= 2;\n\t\t\tif (isPressed(KKey.RCtrl)) m |= 0x20;\n\t\t\tif (isPressed(KKey.LAlt)) m |= 4;\n\t\t\tif (isPressed(KKey.RAlt)) m |= 0x40;\n\t\t\tif (isPressed(KKey.Win)) m |= 8;\n\t\t\tif (isPressed(KKey.RWin)) m |= 0x80;\n\t\t\tif (m == 0) return;\n\t\t\t\n\t\t\t//if Alt or Win pressed without Ctrl, send Ctrl to avoid menu mode or Start menu.\n\t\t\t//\tFor Alt works Ctrl-up, but maybe not everywhere. For Win need Ctrl-down-up.\n\t\t\tif (0 != (m & 0xCC) && 0 == (m & 0x22)) {\n\t\t\t\tSendCtrl(true);\n\t\t\t\tm |= 2;\n\t\t\t}\n\t\t\t\n\t\t\t//prevent invoking something when pressed-released only modifier keys.\n\t\t\t//\tExamples: switch keyboard layout on Ctrl+Alt or Ctrl+Shift; invoke QTranslate on two Ctrl; MS Office ad on Ctrl+Alt+Shift+Win.\n\t\t\t//\tThe vk is unassigned. Tested: vk 0 does not work.\n\t\t\tSendKeyEventRaw((KKey)0xD8, 0, Api.KEYEVENTF_KEYUP);\n\t\t\t\n\t\t\tif (0 != (m & 2)) SendCtrl(false);\n\t\t\tif (0 != (m & 0x20)) SendRCtrlUp();\n\t\t\tif (0 != (m & 1)) SendShift(false);\n\t\t\tif (0 != (m & 0x10)) SendRShiftUp();\n\t\t\tif (0 != (m & 4)) SendAlt(false);\n\t\t\tif (0 != (m & 0x40)) SendRAltUp();\n\t\t\tif (0 != (m & 8)) SendKey(KKey.Win, false);\n\t\t\tif (0 != (m & 0x80)) SendKey(KKey.RWin, false);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Sends <c>Ctrl+V</c> or <c>Ctrl+C</c> or <c>Ctrl+X</c>, and/or optionally one or more keys.\n\t\t/// Caller gets <i>optk</i> and <i>wFocus</i> with <c>GetOptionsAndWndFocused_</c> (it may want to know some options too).\n\t\t/// Caller calls <c>Press</c>, waits until the target app gets clipboard data, then calls <c>Release</c>.\n\t\t/// </summary>\n\t\tinternal unsafe struct SendCopyPaste {\n\t\t\tKHotkey _hk;\n\t\t\tushort _scan;\n\t\t\tOKey _opt;\n\t\t\tList<KKey> _andKeys;\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// Presses <c>Ctrl+key</c>. Does not release.\n\t\t\t/// If <i>andKeys</i> used, <c>Release</c> will press/release them.\n\t\t\t/// </summary>\n\t\t\t/// <exception cref=\"InputDesktopException\"></exception>\n\t\t\tpublic void Press(KHotkey hk, OKey optk, wnd wFocus, List<KKey> andKeys = null) {\n\t\t\t\t_hk = hk;\n\t\t\t\t_scan = VkToSc(_hk.Key, Api.GetKeyboardLayout(wFocus.ThreadId));\n\t\t\t\t_opt = optk;\n\t\t\t\t_andKeys = andKeys;\n\t\t\t\t\n\t\t\t\tif (_hk.Mod.Has(KMod.Ctrl)) SendCtrl(true);\n\t\t\t\tif (_hk.Mod.Has(KMod.Alt)) SendAlt(true);\n\t\t\t\tif (_hk.Mod.Has(KMod.Shift)) SendShift(true);\n\t\t\t\tInternal_.Sleep(_opt.KeySpeedClipboard); //eg need 100 ms for BlueStacks\n\t\t\t\tSendKeyEventRaw(_hk.Key, _scan, 0);\n\t\t\t}\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// Releases keys.\n\t\t\t/// Does nothing if already released.\n\t\t\t/// </summary>\n\t\t\t/// <exception cref=\"InputDesktopException\"></exception>\n\t\t\tpublic void Release() {\n\t\t\t\tif (_hk.Key == 0) return;\n\t\t\t\tvar vk = _hk.Key; _hk.Key = 0;\n\t\t\t\t\n\t\t\t\tSendKeyEventRaw(vk, _scan, Api.KEYEVENTF_KEYUP);\n\t\t\t\tif (_hk.Mod.Has(KMod.Shift)) SendShift(false);\n\t\t\t\tif (_hk.Mod.Has(KMod.Alt)) SendAlt(false);\n\t\t\t\tif (_hk.Mod.Has(KMod.Ctrl)) SendCtrl(false);\n\t\t\t\tif (_andKeys != null) AndSendKeys(_andKeys, _opt);\n\t\t\t}\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// Sends one or more keys.\n\t\t\t/// Not used for keys whose scancode can depend on keyboard layout. To get scancode, uses keyboard layout of current thread.\n\t\t\t/// </summary>\n\t\t\t/// <exception cref=\"InputDesktopException\"></exception>\n\t\t\tpublic static void AndSendKeys(List<KKey> keys_, OKey optk) {\n\t\t\t\tforeach (var k in keys_) {\n\t\t\t\t\tvar f = KeyTypes_.IsExtended(k) ? _KFlags.Extended : default;\n\t\t\t\t\tvar e = new _KEvent(true, k, f, VkToSc(k));\n\t\t\t\t\t_SendKey2(e, default, true, optk);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets focused or active window. Waits for it max 20-40 ms (820 ms if <i>requireFocus</i>). On timeout returns default (throws exception if <i>requireFocus</i>).\n\t\t/// </summary>\n\t\t/// <param name=\"requireFocus\">Wait for focused (and not just active) window longer, and throw exception on timeout. Used for clipboard copy/paste and send text.</param>\n\t\t/// <exception cref=\"AuException\">No focused window when <i>requireFocus</i>.</exception>\n\t\t/// <exception cref=\"InputDesktopException\"></exception>\n\t\tinternal static wnd GetWndFocusedOrActive(bool requireFocus) {\n\t\t\tfor (int i = 0; i < (requireFocus ? 100 : 20); i++) {\n\t\t\t\tmiscInfo.getGUIThreadInfo(out var g);\n\t\t\t\t//print.it(i, g.hwndFocus, g.hwndActive);\n\t\t\t\tif (!g.hwndFocus.Is0) return g.hwndFocus;\n\t\t\t\tif (!requireFocus && !g.hwndActive.Is0) return g.hwndActive;\n\t\t\t\twait.ms(i < 20 ? 1 : 10);\n\t\t\t}\n\t\t\tInputDesktopException.ThrowIfBadDesktop();\n\t\t\tif (requireFocus) throw new AuException(\"There is no focused window\"); //TODO3: test various windows and data types, maybe somewhere could work without focus\n\t\t\treturn default;\n\t\t\t\n\t\t\t//note: the purpose of this wait is not synchronization. It just makes getting the focused/active window more reliable.\n\t\t\t//\tCannot wait for a focused window. Users must program it explicitly.\n\t\t\t//\tWhen creating or activating a window, often there is no focus for 200 ms or more. Eg when opening the Save As dialog.\n\t\t\t//\tAlso, focus is optional.\n\t\t\t//\tAnyway, waiting for focus would not make more reliable, because keys are processed asynchronously.\n\t\t\t//\tUsually the active window is OK. We use it to get keyboard layout and/or to avoid calling Hook too frequently.\n\t\t\t//\tThis func waits for active window max 20-40 ms. When switching apps, usually there is no active window for 1-5 ms.\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Returns:\n\t/// - <i>optk</i> - <c>OKey</c> of this variable or <c>OKey</c> cloned from this variable and possibly modified by <c>Hook</c>.\n\t/// - <i>wFocus</i> - the focused or active window.\n\t/// </summary>\n\t/// <param name=\"getWndAlways\">if <c>false</c>, the caller does not need <i>wFocus</i>. Then <i>wFocus</i> will be <c>default(wnd)</c> if <c>Hook</c> is <c>null</c>.</param>\n\t/// <param name=\"requireFocus\">Wait for focused (and not just active) window longer, and throw exception on timeout. Used for clipboard copy/paste and send text.</param>\n\t/// <exception cref=\"AuException\">No focused window when <i>requireFocus</i>.</exception>\n\t/// <exception cref=\"InputDesktopException\"></exception>\n\tinternal (OKey optk, wnd wFocus) GetOptionsAndWndFocused_(bool getWndAlways, bool requireFocus = false) {\n\t\tif (Options.Hook == null && !getWndAlways) return (Options, default);\n\t\tvar w = Internal_.GetWndFocusedOrActive(requireFocus);\n\t\treturn (GetOptions_(w), w);\n\t}\n\t\n\t/// <summary>\n\t/// Returns <c>OKey</c> of this variable or <c>OKey</c> cloned from this variable and possibly modified by <c>Hook</c>.\n\t/// </summary>\n\t/// <param name=\"wFocus\">the focused or active window. The function uses it to avoid frequent calling of <c>Hook</c>. If you don't have it, use <c>GetOptionsAndWndFocused_</c> instead.</param>\n\tinternal OKey GetOptions_(wnd wFocus) {\n\t\tvar call = Options.Hook;\n\t\tif (call == null || wFocus.Is0) return Options;\n\t\tif (wFocus != _sstate.wFocus) {\n\t\t\t_sstate.wFocus = wFocus;\n\t\t\tif (_sstate.options == null) _sstate.options = new OKey(Options); else _sstate.options.CopyOrDefault_(Options);\n\t\t\tcall(new OKeyHookData(_sstate.options, wFocus));\n\t\t}\n\t\treturn _sstate.options;\n\t}\n\t\n\tvoid _ThrowIfSending() {\n\t\tif (_sending) throw new InvalidOperationException();\n\t}\n\t\n\tinternal static class KeyTypes_ {\n\t\t[Flags]\n\t\tenum _KT : byte {\n\t\t\tMod = 1,\n\t\t\tExtended = 2,\n\t\t\tMouse = 4,\n\t\t\tGksReliable = 8,\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// <c>Ctrl</c>, <c>LCtrl</c>, etc.\n\t\t/// </summary>\n\t\tpublic static bool IsMod(KKey vk) => 0 != (_b[(byte)vk] & _KT.Mod);\n\t\t\n\t\tpublic static bool IsExtended(KKey vk) => 0 != (_b[(byte)vk] & _KT.Extended);\n\t\t\n\t\tpublic static bool IsMouse(KKey vk) => 0 != (_b[(byte)vk] & _KT.Mouse);\n\t\t\n\t\t/// <summary>\n\t\t/// API <c>GetKeyState</c> always works.\n\t\t/// For other keys returns 0 if pressed or toggled before starting current thread.\n\t\t/// Modifiers (left/right too), lock keys, mouse, some other.\n\t\t/// </summary>\n\t\tpublic static bool IsGetKeyStateReliable(KKey vk) => 0 != (_b[(byte)vk] & _KT.GksReliable);\n\t\t\n\t\t/// <summary>\n\t\t/// The same as <see cref=\"IsGetKeyStateReliable\"/>.\n\t\t/// </summary>\n\t\tpublic static bool IsToggleable(KKey vk) => 0 != (_b[(byte)vk] & _KT.GksReliable);\n\t\t\n\t\tstatic _KT[] _b;\n\t\t\n\t\tstatic KeyTypes_() {\n\t\t\t_b = new _KT[256];\n\t\t\t\n\t\t\t_b[1] = _b[2] = _b[4] = _b[5] = _b[6]\n\t\t\t\t= _KT.Mouse | _KT.GksReliable;\n\t\t\t\n\t\t\t_b[16] = _b[17] = _b[18] = _b[(int)KKey.LShift] = _b[(int)KKey.RShift] = _b[(int)KKey.LCtrl] = _b[(int)KKey.LAlt]\n\t\t\t\t= _KT.Mod | _KT.GksReliable;\n\t\t\t\n\t\t\t_b[(int)KKey.PageUp] = _b[(int)KKey.PageDown] = _b[(int)KKey.End] = _b[(int)KKey.Home]\n\t\t\t\t= _b[(int)KKey.Left] = _b[(int)KKey.Up] = _b[(int)KKey.Right] = _b[(int)KKey.Down]\n\t\t\t\t= _b[(int)KKey.PrintScreen] = _b[(int)KKey.Insert] = _b[(int)KKey.Delete]\n\t\t\t\t= _b[(int)KKey.Sleep] = _b[(int)KKey.Apps] = _b[(int)KKey.Divide] = _b[(int)KKey.Break]\n\t\t\t\t= _KT.Extended;\n\t\t\t//and more, but undocumented, and cannot test. There is no API to get extended keys. MapVirtualKeyEx can get only of 50% keys.\n\t\t\t\n\t\t\t_b[(int)KKey.CapsLock] = _b[(int)KKey.ScrollLock]\n\t\t\t\t= _b[(int)KKey.Back] = _b[(int)KKey.Tab] = _b[(int)KKey.Enter] = _b[(int)KKey.Escape]\n\t\t\t\t= _KT.GksReliable; //also Home and maybe more\n\t\t\t\n\t\t\t_b[(int)KKey.NumLock]\n\t\t\t\t= _KT.Extended | _KT.GksReliable;\n\t\t\t\n\t\t\t_b[(int)KKey.Win] = _b[(int)KKey.RWin] = _b[(int)KKey.RCtrl] = _b[(int)KKey.RAlt]\n\t\t\t\t= _KT.Mod | _KT.Extended | _KT.GksReliable;\n\t\t\t\n\t\t\tfor (int i = (int)KKey.BrowserBack; i <= (int)KKey.LaunchApp2; i++) _b[i] = _KT.Extended; //media/browser/launchapp keys\n\t\t\t\n\t\t\t//for(int i = 1; i < 256; i++) print.it((KKey)i, _b[i]);\n\t\t}\n\t}\n\t\n\tstruct _INPUTKEY2 {\n\t\tpublic Api.INPUTK k0, k1;\n\t\t\n\t\tpublic _INPUTKEY2(KKey vk, ushort sc, uint flags = 0) {\n\t\t\tk0 = new Api.INPUTK(vk, sc, flags);\n\t\t\tk1 = new Api.INPUTK(vk, sc, flags | Api.KEYEVENTF_KEYUP);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au/Input/miscInfo.cs",
    "content": "namespace Au;\n\n/// <summary>\n/// Contains functions to get miscellaneous info not found in other classes of this library and .NET.\n/// </summary>\n/// <seealso cref=\"osVersion\"/>\n/// <seealso cref=\"folders\"/>\n/// <seealso cref=\"process\"/>\n/// <seealso cref=\"screen\"/>\n/// <seealso cref=\"script\"/>\n/// <seealso cref=\"perf\"/>\n/// <seealso cref=\"uacInfo\"/>\n/// <seealso cref=\"Dpi\"/>\n/// <seealso cref=\"Environment\"/>\n/// <seealso cref=\"System.Windows.Forms.SystemInformation\"/>\n/// <seealso cref=\"System.Windows.SystemParameters\"/>\npublic static class miscInfo {\n\t/// <summary>\n\t/// Calls API <ms>GetGUIThreadInfo</ms>. It gets info about mouse capturing, menu mode, move/size mode, focus, caret, etc.\n\t/// </summary>\n\t/// <param name=\"g\">API <ms>GUITHREADINFO</ms>.</param>\n\t/// <param name=\"idThread\">Thread id. If 0 - the foreground (active window) thread. See <see cref=\"process.thisThreadId\"/>, <see cref=\"wnd.ThreadId\"/>.</param>\n\tpublic static unsafe bool getGUIThreadInfo(out GUITHREADINFO g, int idThread = 0) {\n\t\tg = new GUITHREADINFO { cbSize = sizeof(GUITHREADINFO) };\n\t\treturn Api.GetGUIThreadInfo(idThread, ref g);\n\t}\n\t\n\t/// <summary>\n\t/// Gets caret rectangle.\n\t/// </summary>\n\t/// <returns><c>false</c> if failed.</returns>\n\t/// <param name=\"r\">Receives the rectangle, in screen coordinates.</param>\n\t/// <param name=\"w\">Receives the caret owner control or the focused control.</param>\n\t/// <param name=\"orMouse\">If fails, get mouse pointer coordinates.</param>\n\t/// <remarks>\n\t/// Some apps use non-standard caret; then may fail.\n\t/// </remarks>\n\tpublic static bool getTextCursorRect(out RECT r, out wnd w, bool orMouse = false) {\n\t\t//rejected. Too few controls support it.\n\t\t///// <param name=\"preferSelection\">Get text selection rectangle if possible.</param>\n\t\t\n\t\tif (getGUIThreadInfo(out var g)) {\n\t\t\tif (!g.hwndCaret.Is0) {\n\t\t\t\tif (g.rcCaret.bottom <= g.rcCaret.top) g.rcCaret.bottom = g.rcCaret.top + 16;\n\t\t\t\tr = g.rcCaret;\n\t\t\t\tg.hwndCaret.MapClientToScreen(ref r);\n\t\t\t\tw = g.hwndCaret;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\t\n\t\t\tif (!g.hwndFocus.Is0) {\n\t\t\t\tw = g.hwndFocus;\n\t\t\t\ttry {\n\t\t\t\t\tvar e = elm.fromWindow(g.hwndFocus, EObjid.CARET, EWFlags.NoThrow | EWFlags.NotInProc);\n\t\t\t\t\tif (e?.GetRect(out r) == true) return true;\n\t\t\t\t\t\n\t\t\t\t\tif (g.hwndFocus.ClassNameIs(\"HwndWrapper[powershell_ise.exe;*\")) {\n\t\t\t\t\t\tif (UiaUtil.GetCaretRectInPowerShell(out r)) return true;\n\t\t\t\t\t} else if (UiaUtil.ElementFocused() is { } ef) {\n\t\t\t\t\t\tif (ef.GetCaretRect(out r)) return true;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\t//GetGUIThreadInfo and MSAA don't work with winstore, winui3, Windows Terminal.\n\t\t\t\t\t//Most winstore and winui3 apps support IUIAutomationTextPattern2, and it gives correct caret rect.\n\t\t\t\t\t//Terminal supports only IUIAutomationTextPattern, and it gives correct caret rect when there is no selection.\n\t\t\t\t\t//Bad: some apps give client coordinates (bug, eg PowerShell). We can convert to screen easily, but can't know the coordinate type in unknown apps.\n\t\t\t\t\t//Win+; works well with all tested winstore apps and terminal, although some apps don't support even IUIAutomationTextPattern. What API it uses?\n\t\t\t\t\t//IME works everywhere. What API it uses?\n\t\t\t\t\t//PhraseExpress doesn't work.\n\t\t\t\t}\n\t\t\t\tcatch (Exception e1) { Debug_.Print(e1); }\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (orMouse) {\n\t\t\tApi.GetCursorPos(out var p);\n\t\t\tr = new RECT(p.x, p.y, 0, 16);\n\t\t} else r = default;\n\t\t\n\t\tw = default;\n\t\treturn false;\n\t\t\n\t\t//note: in Word, after changing caret pos, GetGUIThreadInfo and MSAA get pos 0 0. After 0.5 s gets correct. After typing always correct.\n\t}\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if current thread is on the input desktop and therefore can use mouse, keyboard, clipboard and window functions.\n\t/// </summary>\n\t/// <param name=\"detectLocked\">Return <c>false</c> if the active window is a full-screen window of <c>LockApp.exe</c> on Windows 10+. For example when computer has been locked but still not displaying the password field. Slower.</param>\n\t/// <remarks>\n\t/// Usually this app is running on default desktop. Examples of other desktops: the <c>Ctrl+Alt+Delete</c> screen, the PC locked screen, screen saver, UAC consent, custom desktops. If one of these is active, this process cannot use many mouse, keyboard, clipboard and window functions. They either throw exception or do nothing.\n\t/// </remarks>\n\t/// <seealso cref=\"InputDesktopException\"/>\n\tpublic static unsafe bool isInputDesktop(bool detectLocked = false) {\n\t\tvar w = wnd.active;\n\t\tif (w.Is0) { //tested: last error code 0\n\t\t\tint i = 0;\n\t\t\tif (!Api.GetUserObjectInformation(Api.GetThreadDesktop(Api.GetCurrentThreadId()), Api.UOI_IO, &i, 4, out _)) return true; //slow\n\t\t\treturn i != 0;\n\t\t\t//also tested several default screensavers on Win10 and 7. Goes through this branch. When closed, works like when locked (goes through other branch until next input).\n\t\t} else {\n\t\t\tif (detectLocked && osVersion.minWin10) {\n\t\t\t\tvar rw = w.Rect;\n\t\t\t\tif (rw.left == 0 && rw.top == 0) {\n\t\t\t\t\tvar rs = screen.primary.Rect;\n\t\t\t\t\tif (rw == rs) {\n\t\t\t\t\t\tvar s = w.ProgramName;\n\t\t\t\t\t\tif (s.Eqi(\"LockApp.exe\")) return false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t\t//info: in lock screen SHQueryUserNotificationState returns QUNS_NOT_PRESENT.\n\t\t\t//\tAlso documented but not tested: screen saver.\n\t\t\t//\ttested: QUNS_ACCEPTS_NOTIFICATIONS (normal) when Ctrl+Alt+Delete.\n\t\t\t//\tHowever it is too slow, eg 1300 mcs.\n\t\t}\n\t}\n\t\n\t//public static unsafe string GetInputDesktopName() {\n\t//\tvar hd = Api.OpenInputDesktop(0, false, Api.GENERIC_READ); //error \"Access is denied\" when this process is admin. Need SYSTEM.\n\t//\t//if (hd == default) throw new AuException(0);\n\t//\tif (hd == default) return null;\n\t//\tstring s = null;\n\t//\tvar p = stackalloc char[300];\n\t//\tif (Api.GetUserObjectInformation(hd, Api.UOI_NAME, p, 600, out int len) && len >= 4) s = new(p, 0, len / 2 - 1);\n\t//\tApi.CloseDesktop(hd);\n\t//\treturn s;\n\t//}\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if this process is running in a child session (aka Picture-in-Picture).\n\t/// </summary>\n\tpublic static unsafe bool isChildSession {\n\t\tget {\n\t\t\tif (!s_isChildSession.HasValue) {\n\t\t\t\tbool yes = false;\n\t\t\t\tif (osVersion.minWin8) {\n\t\t\t\t\tvar psid = process.thisProcessSessionId;\n\t\t\t\t\tif (psid > 1) {\n\t\t\t\t\t\tint csid = Api.WTSGetActiveConsoleSessionId();\n\t\t\t\t\t\tif (csid != -1 && psid != csid) {\n\t\t\t\t\t\t\tif (Api.WTSQuerySessionInformation(-1, Api.WTS_INFO_CLASS.WTSIsRemoteSession, out bool isRemote) && isRemote) {\n\t\t\t\t\t\t\t\tif (Api.WTSQuerySessionInformation(-1, Api.WTS_INFO_CLASS.WTSWinStationName, out string s1)) {\n\t\t\t\t\t\t\t\t\tyes = !s1.Starts(\"RDP-\");\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\ts_isChildSession = yes;\n\t\t\t}\n\t\t\treturn s_isChildSession.Value;\n\t\t}\n\t}\n\tstatic bool? s_isChildSession;\n}\n"
  },
  {
    "path": "Au/Input/mouse.cs",
    "content": "//TODO3: test how mouse moves through non-screen area between screens A and C when screen B is in between.\n//\tQM2 has problems crossing non-screen corners at default speed. Au works well.\n\nnamespace Au;\n\n/// <summary>\n/// Mouse functions.\n/// </summary>\n/// <remarks>\n/// Should not be used to click windows of own thread. It may work or not. If need, use another thread. Example in <see cref=\"keys.send\"/>.\n/// </remarks>\npublic static class mouse {\n\t/// <summary>\n\t/// Gets cursor (mouse pointer) position.\n\t/// </summary>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// var p = mouse.xy;\n\t/// print.it(p.x, p.y);\n\t/// \n\t/// var (x, y) = mouse.xy;\n\t/// print.it(x, y);\n\t/// ]]></code>\n\t/// <code><![CDATA[\n\t/// POINT mousePos = mouse.xy;\n\t/// mouse.moveBy(20, 50);\n\t/// POINT mousePos2 = mouse.xy;\n\t/// \n\t/// double dist = Math2.Distance(mousePos2, mousePos);\n\t/// print.it(dist, (int)dist, dist.ToInt(), mousePos2.x - mousePos.x, mousePos2.y - mousePos.y);\n\t/// ]]></code>\n\t/// </example>\n\tpublic static POINT xy { get { Api.GetCursorPos(out var p); return p; } }\n\t\n\tstatic void _Move(POINT p, bool fast) {\n\t\tbool relaxed = opt.mouse.Relaxed, willFail = false;\n\t\t\n\t\tif (!screen.isInAnyScreen(p)) {\n\t\t\tif (!relaxed) throw new ArgumentOutOfRangeException(null, \"Cannot mouse-move. This x y is not in screen. \" + p.ToString());\n\t\t\twillFail = true;\n\t\t}\n\t\t\n\t\tif (!fast) _MoveSlowTo(p);\n\t\t\n\t\tPOINT p0 = xy;\n\t\t//bool retry = false; g1:\n\t\tbool ok = false;\n\t\tfor (int i = 0, n = relaxed ? 3 : 10; i < n; i++) {\n\t\t\t//perf.first();\n\t\t\t_SendMove(p);\n\t\t\t//info: now xy is still not updated in ~10% cases.\n\t\t\t//\tIn my tests was always updated after sleeping 0 or 1 ms.\n\t\t\t//\tBut the user etc also can move the mouse at the same time. Then the i loop always helps.\n\t\t\t//perf.next();\n\t\t\tint j = 0;\n\t\t\tfor (; ; j++) {\n\t\t\t\tvar pNow = xy;\n\t\t\t\tok = (pNow == p);\n\t\t\t\tif (ok || pNow != p0 || j > 3) break;\n\t\t\t\twait.ms(j); //0+1+2+3\n\t\t\t}\n\t\t\t//perf.nw();\n\t\t\t//print.it(j, i);\n\t\t\tif (ok || willFail) break;\n\t\t\t//note: don't put the _Sleep(7) here\n\t\t\t\n\t\t\t//Accidentally this is also a workaround for SendInput bug:\n\t\t\t//\tWhen moving to other screen, moves to a wrong place if new x or y is outside of rect of old screen.\n\t\t\t//\tTo reproduce:\n\t\t\t//\t\tLet now mouse is in second screen, and try to move to 0 0 (primary screen).\n\t\t\t//\t\tCase 1: Let second screen is at the bottom and its left is eg 300. Single SendInput will move to 300 0.\n\t\t\t//\t\tCase 2: Let second screen is at the right and its top is eg 300. Will move x to the right of the primary screen.\n\t\t\t//\tTested only on Win10.\n\t\t\t//\tWorkaround: call SendInput twice.\n\t\t}\n\t\tif (!ok && !relaxed) {\n\t\t\tvar es = $\"*mouse-move to this x y in screen. \" + p.ToString();\n\t\t\twnd.active.UacCheckAndThrow_(es + \". The active\"); //it's a mystery for users. API SendInput fails even if the point is not in the window.\n\t\t\t//rejected: wnd.getwnd.root.ActivateL()\n\t\t\tInputDesktopException.ThrowIfBadDesktop(es);\n\t\t\tthrow new AuException(es);\n\t\t\t//known reasons:\n\t\t\t//\tActive window of higher UAC IL.\n\t\t\t//\tBlockInput, hook, some script etc that blocks mouse movement or restores mouse position.\n\t\t\t//\tClipCursor.\n\t\t}\n\t\ts_prevMousePos.last = p;\n\t\t\n\t\t_Sleep(opt.mouse.MoveSleepFinally);\n\t}\n\t\n\tstatic void _MoveSlowTo(POINT p) {\n\t\tbool drag = t_pressedButtons != 0;\n\t\tint speed = opt.mouse.MoveSpeed;\n\t\tif (drag) speed++; else if (speed == 0) return; //need at least 1 intermediate point, else some apps don't drag or don't select text etc\n\t\tvar p1 = mouse.xy; //the start point; p is the end point\n\t\tint x2 = p.x - p1.x, y2 = p.y - p1.y; //x and y distances\n\t\tbool xNeg, yNeg; if (xNeg = x2 < 0) x2 = -x2; if (yNeg = y2 < 0) y2 = -y2; //make code easier\n\t\tdouble dist = Math.Sqrt(x2 * x2 + y2 * y2); if (dist < 4) return;\n\t\tdouble angle = Math.Atan2(y2, x2);\n\t\tvar (sin, cos) = Math.SinCos(angle);\n\t\tdouble speed2 = speed / 10.0;\n\t\t\n\t\tfor (double z = 0; ;) {\n\t\t\tbool startDragSlowly = drag && z < Math.Min(20, dist); //some apps refuse to drag if too fast\n\t\t\tdouble d = ((startDragSlowly ? z : dist - z) / 10 + 1) / speed2; //the speed depends on the distance, and is decreasing; increasing while startDragSlowly\n\t\t\tif (d > dist / 2) d = dist / 2; else if (d < 2) d = 2; //need at least 1 intermediate point; don't need too many points\n\t\t\tz += d;\n\t\t\tint x = (z * cos).ToInt(), y = (z * sin).ToInt();\n\t\t\t\n\t\t\t//make the line smoother. Converting double to int creates ugly bumps etc.\n\t\t\t//\tUsually don't need it, but in some cases it may be better.\n\t\t\t//\tTry to add 1 to x or/and y and use it if then the angle difference is smaller.\n\t\t\tint xPlus = 0, yPlus = 0; double anDiff = 4;\n\t\t\tfor (int i = 0; i < 2; i++) {\n\t\t\t\tfor (int j = 0; j < 2; j++) {\n\t\t\t\t\tdouble ad = Math.Abs(Math.Atan2(y + j, x + i) - angle);\n\t\t\t\t\tif (ad < anDiff) { anDiff = ad; xPlus = i; yPlus = j; }\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (xPlus > 0 || yPlus > 0) {\n\t\t\t\tx += xPlus; y += yPlus;\n\t\t\t\tz = Math.Sqrt(x * x + y * y);\n\t\t\t}\n\t\t\t\n\t\t\tif (dist - z < .5) break;\n\t\t\t\n\t\t\t_SendMove(new(p1.x + (xNeg ? -x : x), p1.y + (yNeg ? -y : y)));\n\t\t\t_Sleep(7 + (speed - 1) / 10); //7-8 is the natural max WM_MOUSEMOVE period, even when the system timer period is 15.625 (default).\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Moves the cursor (mouse pointer) to the position <i>x y</i> relative to window <i>w</i>.\n\t/// </summary>\n\t/// <returns>Cursor position in screen coordinates.</returns>\n\t/// <param name=\"w\">Window or control.</param>\n\t/// <param name=\"x\">X coordinate relative to the client area of <i>w</i>. Default - center. Examples: <c>10</c>, <c>^10</c> (reverse), <c>.5f</c> (fraction).</param>\n\t/// <param name=\"y\">Y coordinate relative to the client area of <i>w</i>. Default - center.</param>\n\t/// <param name=\"nonClient\"><i>x y</i> are relative to the window rectangle.</param>\n\t/// <exception cref=\"AuWndException\">\n\t/// - Invalid window.\n\t/// - The window is hidden. No exception if just cloaked, for example in another desktop; then on click will activate, which usually uncloaks. No exception if <i>w</i> is a control.\n\t/// - Other window-related failures.\n\t/// </exception>\n\t/// <inheritdoc cref=\"move(POINT)\" path=\"//exception|//remarks\"/>\n\tpublic static POINT move(wnd w, Coord x = default, Coord y = default, bool nonClient = false) {\n\t\tWaitForNoButtonsPressed_();\n\t\tw.ThrowIfInvalid();\n\t\tvar wTL = w.Window;\n\t\tif (!wTL.IsVisible) throw new AuWndException(wTL, \"Cannot mouse-move. The window is invisible\"); //should make visible? Probably not. If cloaked because in an inactive virtual desktop etc, Click activates and it usually uncloaks.\n\t\tif (wTL.IsMinimized) { wTL.ShowNotMinimized(1); _Sleep(500); } //never mind: if w is a control...\n\t\tvar p = Coord.NormalizeInWindow(x, y, w, nonClient, centerIfEmpty: true);\n\t\tif (!w.MapClientToScreen(ref p)) w.ThrowUseNative();\n\t\t_Move(p, fast: false);\n\t\treturn p;\n\t}\n\t\n\t/// <summary>\n\t/// Moves the cursor (mouse pointer) to the position <i>x y</i> relative to UI object <i>obj</i>.\n\t/// </summary>\n\t/// <param name=\"obj\">Can be <see cref=\"wnd\"/>, <see cref=\"elm\"/>, <see cref=\"uiimage\"/>, <see cref=\"screen\"/>, <see cref=\"RECT\"/> in screen, <see cref=\"RECT\"/> in window, <see cref=\"lastXY\"/> (<c>true</c>), <see cref=\"xy\"/> (<c>false</c>)</param>\n\t/// <param name=\"x\">X coordinate relative to <i>obj</i>. Default - center. Examples: <c>10</c>, <c>^10</c> (reverse), <c>.5f</c> (fraction).</param>\n\t/// <param name=\"y\">Y coordinate relative to <i>obj</i>. Default - center.</param>\n\t/// <inheritdoc cref=\"move(POINT)\" path=\"//exception|//remarks\"/>\n\t/// <exception cref=\"Exception\">Other exceptions. Depends on <i>obj</i> type.</exception>\n\tpublic static void move(MObject obj, Coord x = default, Coord y = default) {\n\t\tswitch (obj.Value) {\n\t\tcase wnd w:\n\t\t\tmove(w, x, y);\n\t\t\tbreak;\n\t\tcase elm e:\n\t\t\te.MouseMove(x, y);\n\t\t\tbreak;\n\t\tcase uiimage u:\n\t\t\tu.MouseMove(x, y);\n\t\t\tbreak;\n\t\tcase RECT r:\n\t\t\tmove(Coord.NormalizeInRect(x, y, r, centerIfEmpty: true));\n\t\t\tbreak;\n\t\tcase (wnd w, RECT r):\n\t\t\tvar p = Coord.NormalizeInRect(x, y, r, centerIfEmpty: true);\n\t\t\tmove(w, p.x, p.y);\n\t\t\tbreak;\n\t\tcase (wnd w, bool nonClient):\n\t\t\tmove(w, x, y, nonClient);\n\t\t\tbreak;\n\t\tcase (screen s, bool workArea):\n\t\t\tmove(Coord.Normalize(x, y, workArea, s, centerIfEmpty: true));\n\t\t\tbreak;\n\t\tcase bool useLastXY:\n\t\t\tmove(_CoordToRelativeXY(x, y, useLastXY));\n\t\t\tbreak;\n\t\tcase null: //default(MObject)\n\t\t\tmove(x, y);\n\t\t\tbreak;\n\t\t}\n\t}\n\t\n\tstatic POINT _CoordToRelativeXY(Coord x, Coord y, bool useLastXY) {\n\t\tvar p = useLastXY ? lastXY : xy;\n\t\tif (x.Type is CoordType.Normal or CoordType.None) p.x += x.Value; else throw new ArgumentException(null, \"x\");\n\t\tif (y.Type is CoordType.Normal or CoordType.None) p.y += y.Value; else throw new ArgumentException(null, \"y\");\n\t\treturn p;\n\t}\n\t\n\t/// <summary>\n\t/// Moves the cursor (mouse pointer) to the specified position in screen.\n\t/// </summary>\n\t/// <returns>Normalized cursor position.</returns>\n\t/// <param name=\"x\">X coordinate. Examples: <c>10</c>, <c>^10</c> (reverse), <c>.5f</c> (fraction).</param>\n\t/// <param name=\"y\">Y coordinate.</param>\n\t/// <inheritdoc cref=\"move(POINT)\" path=\"//exception|//remarks\"/>\n\tpublic static POINT move(Coord x, Coord y) {\n\t\tWaitForNoButtonsPressed_();\n\t\tvar p = Coord.Normalize(x, y);\n\t\t_Move(p, fast: false);\n\t\treturn p;\n\t}\n\t//rejected: parameters bool workArea = false, screen screen = default. Rarely used. Can use the POINT overload and Coord.Normalize.\n\t\n\t/// <summary>\n\t/// Moves the cursor (mouse pointer) to the specified position in screen.\n\t/// </summary>\n\t/// <param name=\"p\">\n\t/// Coordinates.\n\t/// Tip: To specify coordinates relative to the right, bottom, work area or a non-primary screen, use <see cref=\"Coord.Normalize\"/>, like in the example.\n\t/// </param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">The position is not in screen. No exception if option <c>Relaxed</c> is <c>true</c> (then moves to a screen edge).</exception>\n\t/// <exception cref=\"AuException\">\n\t/// Failed to move the cursor to that position. Some reasons:\n\t/// - The active window belongs to a process of higher [](xref:uac) integrity level.\n\t/// - Another thread blocks or modifies mouse input (API <ms>BlockInput</ms>, mouse hooks, frequent API <ms>SendInput</ms> etc).\n\t/// - Some application called API <ms>ClipCursor</ms>. No exception if option <c>Relaxed</c> is <c>true</c> (then final cursor position is undefined).\n\t/// </exception>\n\t/// <exception cref=\"InputDesktopException\"></exception>\n\t/// <remarks>\n\t/// Uses <see cref=\"opt.mouse\"/>: <see cref=\"OMouse.MoveSpeed\"/>, <see cref=\"OMouse.MoveSleepFinally\"/>, <see cref=\"OMouse.Relaxed\"/>.\n\t/// </remarks>\n\t/// <example>\n\t/// Save-restore mouse position.\n\t/// <code><![CDATA[\n\t/// var p = mouse.xy;\n\t/// //...\n\t/// mouse.move(p);\n\t/// ]]></code>\n\t/// Use coordinates in the first non-primary screen.\n\t/// <code><![CDATA[\n\t/// mouse.move(Coord.Normalize(10, ^10, screen: screen.index(1))); //10 from left, 10 from bottom\n\t/// ]]></code>\n\t/// </example>\n\tpublic static void move(POINT p) {\n\t\tWaitForNoButtonsPressed_();\n\t\t_Move(p, fast: false);\n\t}\n\t\n\t/// <summary>\n\t/// Remembers current mouse cursor position to be later restored with <see cref=\"restore\"/>.\n\t/// </summary>\n\tpublic static void save() {\n\t\tif (s_prevMousePos is { } v) v.first = xy; else s_prevMousePos = new();\n\t}\n\t\n\t/// <summary>\n\t/// Moves the mouse cursor where it was at the time of the last <see cref=\"save\"/>. If it was not called - of the first \"mouse move\" or \"mouse click\" function call. Does nothing if these functions were not called.\n\t/// </summary>\n\t/// <remarks>\n\t/// Uses <see cref=\"opt.mouse\"/>: <see cref=\"OMouse.MoveSleepFinally\"/>, <see cref=\"OMouse.Relaxed\"/>.\n\t/// </remarks>\n\t/// <inheritdoc cref=\"move(POINT)\" path=\"/exception\"/>\n\tpublic static void restore() {\n\t\tif (s_prevMousePos is { first: var p }) {\n\t\t\tWaitForNoButtonsPressed_();\n\t\t\t_Move(p, fast: true);\n\t\t}\n\t}\n\t\n\tclass _PrevMousePos {\n\t\tpublic POINT first, last;\n\t\tpublic _PrevMousePos() { first = last = xy; }\n\t\tpublic _PrevMousePos(POINT p) { first = last = p; }\n\t}\n\tstatic _PrevMousePos s_prevMousePos;\n\t\n\t/// <summary>\n\t/// Mouse cursor position of the most recent successful \"mouse move\" or \"mouse click\" function call.\n\t/// If such functions are still not called, returns <see cref=\"xy\"/>.\n\t/// </summary>\n\tpublic static POINT lastXY => s_prevMousePos?.last ?? xy;\n\t\n\t//rejected. MoveRelative usually is better. If need, can use code: Move(mouse.xy.x+dx, mouse.xy.y+dy).\n\t//public static void MoveFromCurrent(int dx, int dy)\n\t//{\n\t//\tvar p = XY;\n\t//\tp.Offset(dx, dy);\n\t//\tMove(p);\n\t//}\n\t\n\t/// <summary>\n\t/// Moves the cursor (mouse pointer) relative to <see cref=\"lastXY\"/> or <see cref=\"xy\"/>.\n\t/// </summary>\n\t/// <returns>Final cursor position in screen.</returns>\n\t/// <param name=\"dx\">X offset from <c>lastXY.x</c> or <c>xy.x</c>.</param>\n\t/// <param name=\"dy\">Y offset from <c>lastXY.y</c> or <c>xy.y</c>.</param>\n\t/// <param name=\"useLastXY\">If <c>true</c> (default), moves relative to <see cref=\"lastXY\"/>, else relative to <see cref=\"xy\"/>.</param>\n\t/// <inheritdoc cref=\"move(POINT)\" path=\"//exception|//remarks\"/>\n\tpublic static POINT moveBy(int dx, int dy, bool useLastXY = true) {\n\t\tWaitForNoButtonsPressed_();\n\t\tvar p = useLastXY ? lastXY : xy;\n\t\tp.x += dx; p.y += dy;\n\t\t_Move(p, fast: false);\n\t\treturn p;\n\t}\n\t\n\t/// <summary>\n\t/// Moves the cursor (mouse pointer) relative to <see cref=\"lastXY\"/>. Uses multiple x y offsets.\n\t/// </summary>\n\t/// <returns>Final cursor position in screen.</returns>\n\t/// <param name=\"offsets\">String containing multiple x y offsets. Created by a mouse recorder tool with <see cref=\"RecordingUtil.MouseToString\"/>.</param>\n\t/// <param name=\"speedFactor\">Speed factor. For example, 0.5 makes 2 times faster.</param>\n\t/// <exception cref=\"FormatException\">Invalid Base64 string.</exception>\n\t/// <exception cref=\"ArgumentException\">The string is not compatible with this library version (recorded with a newer version and has additional options).</exception>\n\t/// <inheritdoc cref=\"move(POINT)\" path=\"/exception\"/>\n\t/// <remarks>\n\t/// Uses <see cref=\"opt.mouse\"/>: <see cref=\"OMouse.Relaxed\"/> (only for the last movement; always relaxed in intermediate movements).\n\t/// </remarks>\n\tpublic static POINT moveBy(string offsets, double speedFactor = 1.0) {\n\t\tWaitForNoButtonsPressed_();\n\t\t\n\t\tvar a = Convert.FromBase64String(offsets);\n\t\t\n\t\tbyte flags = a[0];\n\t\tconst int knownFlags = 1; if ((flags & knownFlags) != flags) throw new ArgumentException(\"Unknown string version\");\n\t\tbool withSleepTimes = 0 != (flags & 1);\n\t\tbool isSleep = withSleepTimes;\n\t\t\n\t\tvar p = lastXY;\n\t\tint pdx = 0, pdy = 0;\n\t\t\n\t\tfor (int i = 1; i < a.Length;) {\n\t\t\tif (i > 1 && (isSleep || !withSleepTimes)) {\n\t\t\t\t_SendMove(p);\n\t\t\t\tif (!withSleepTimes) _Sleep((7 * speedFactor).ToInt());\n\t\t\t}\n\t\t\t\n\t\t\tint v = a[i++], nbytes = (v & 3) + 1;\n\t\t\tfor (int j = 1; j < nbytes; j++) v |= a[i++] << j * 8;\n\t\t\tv = (int)((uint)v >> 2);\n\t\t\tif (isSleep) {\n\t\t\t\t//print.it($\"nbytes={nbytes}    sleep={v}\");\n\t\t\t\t\n\t\t\t\t_Sleep((v * speedFactor).ToInt());\n\t\t\t} else {\n\t\t\t\tint shift = nbytes * 4 - 1, mask = (1 << shift) - 1;\n\t\t\t\tint x = v & mask, y = (v >> shift) & mask;\n\t\t\t\tshift = 32 - shift; x <<= shift; x >>= shift; y <<= shift; y >>= shift; //sign-extend\n\t\t\t\tint dx = pdx + x; pdx = dx;\n\t\t\t\tint dy = pdy + y; pdy = dy;\n\t\t\t\t\n\t\t\t\t//print.it($\"dx={dx} dy={dy}    x={x} y={y}    nbytes={nbytes}    v=0x{v:X}\");\n\t\t\t\t\n\t\t\t\tp.x += dx; p.y += dy;\n\t\t\t}\n\t\t\tisSleep ^= withSleepTimes;\n\t\t}\n\t\t_Move(p, fast: true);\n\t\treturn p;\n\t}\n\t\n\t/// <summary>\n\t/// Sends single mouse movement event.\n\t/// x y are normal absolute coordinates.\n\t/// </summary>\n\tstatic void _SendMove(POINT p) {\n\t\ts_prevMousePos ??= new(); //sets .first=.last=mouse.xy\n\t\t_SendRaw(Api.IMFlags.Move, p.x, p.y);\n\t}\n\t\n\t/// <summary>\n\t/// Sends single mouse button down or up event.\n\t/// Does not use the action flags of button.\n\t/// Applies <c>SM_SWAPBUTTON</c>.\n\t/// Also moves to <i>p</i> in the same API <c>SendInput</c> call.\n\t/// </summary>\n\tinternal static void SendButton_(MButton button, bool down, POINT p) {\n\t\t//CONSIDER: release user-pressed modifier keys, like keys class does.\n\t\t//CONSIDER: block user input, like keys class does.\n\t\t\n\t\tApi.IMFlags f; MButtons mb;\n\t\tswitch (button & (MButton.Left | MButton.Right | MButton.Middle | MButton.X1 | MButton.X2)) {\n\t\tcase 0: //allow 0 for left. Example: wnd.find(...).MouseClick(x, y, MButton.DoubleClick)\n\t\tcase MButton.Left: f = down ? Api.IMFlags.LeftDown : Api.IMFlags.LeftUp; mb = MButtons.Left; break;\n\t\tcase MButton.Right: f = down ? Api.IMFlags.RightDown : Api.IMFlags.RightUp; mb = MButtons.Right; break;\n\t\tcase MButton.Middle: f = down ? Api.IMFlags.MiddleDown : Api.IMFlags.MiddleUp; mb = MButtons.Middle; break;\n\t\tcase MButton.X1: f = down ? Api.IMFlags.XDown | Api.IMFlags.X1 : Api.IMFlags.XUp | Api.IMFlags.X1; mb = MButtons.X1; break;\n\t\tcase MButton.X2: f = down ? Api.IMFlags.XDown | Api.IMFlags.X2 : Api.IMFlags.XUp | Api.IMFlags.X2; mb = MButtons.X2; break;\n\t\tdefault: throw new ArgumentException(\"Several buttons specified\", nameof(button)); //rejected: InvalidEnumArgumentException. It's in System.ComponentModel namespace.\n\t\t}\n\t\t\n\t\t//maybe mouse left/right buttons are swapped\n\t\tif (0 != (button & (MButton.Left | MButton.Right)) && 0 != Api.GetSystemMetrics(Api.SM_SWAPBUTTON))\n\t\t\tf ^= down ? Api.IMFlags.LeftDown | Api.IMFlags.RightDown : Api.IMFlags.LeftUp | Api.IMFlags.RightUp;\n\t\t\n\t\t//If this is a Click(x y), the sequence of sent events is like: move, sleep, down, sleep, up. Even Click() sleeps between down and up.\n\t\t//During the sleep the user can move the mouse. Correct it now if need.\n\t\t//tested: if don't need to move, mouse messages are not sent. Hooks not tested. In some cases are sent one or more mouse messages but it depends on other things.\n\t\t//Alternatively could temporarily block user input, but it is not good. Need a hook (UAC disables Api.BlockInput), etc. Better let scripts do it explicitly. If script contains several mouse/keys statements, it's better to block input once for all.\n\t\tf |= Api.IMFlags.Move;\n\t\t\n\t\t//normally don't need this, but this is a workaround for the SendInput bug with multiple screens\n\t\tif (p != xy) _SendRaw(Api.IMFlags.Move, p.x, p.y);\n\t\t\n\t\t_SendRaw(f, p.x, p.y);\n\t\t\n\t\tif (down) t_pressedButtons |= mb; else t_pressedButtons &= ~mb;\n\t}\n\t\n\t/// <summary>\n\t/// Calls <c>Api.SendInput</c> to send single mouse movement or/and button down or up or wheel event.\n\t/// Converts <i>x</i>, <i>y</i> as need for <c>MOUSEINPUT</c>.\n\t/// For X buttons use <c>Api.IMFlag.XDown|Api.IMFlag.X1</c> etc.\n\t/// If <c>Api.IMFlag.Move</c>, adds <c>Api.IMFlag.Absolute</c>.\n\t/// </summary>\n\tstatic unsafe void _SendRaw(Api.IMFlags flags, int x = 0, int y = 0, int wheel = 0) {\n\t\tif (0 != (flags & Api.IMFlags.Move)) {\n\t\t\tflags |= Api.IMFlags.Absolute;\n\t\t\tvar psr = screen.primary.Rect;\n\t\t\tx = (int)((((long)x << 16) + (x >= 0 ? 0x8000 : -0x8000)) / psr.Width);\n\t\t\ty = (int)((((long)y << 16) + (y >= 0 ? 0x8000 : -0x8000)) / psr.Height);\n\t\t}\n\t\t\n\t\tint mouseData;\n\t\tif (0 != (flags & (Api.IMFlags.XDown | Api.IMFlags.XUp))) {\n\t\t\tmouseData = (int)((uint)flags >> 24);\n\t\t\tflags &= (Api.IMFlags)0xffffff;\n\t\t} else mouseData = wheel;\n\t\t\n\t\tvar k = new Api.INPUTM(flags, x, y, mouseData);\n\t\tApi.SendInput(&k);\n\t}\n\t\n\tstatic void _Sleep(int ms) {\n\t\twait.doEventsPrecise_(ms);\n\t\t\n\t\t//note: always doevents, even if window from point is not of our thread. Because:\n\t\t//\tCannot always reliably detect what window will receive the message and what then happens.\n\t\t//\tThere is not much sense to avoid doevents. If no message loop, it is fast and safe; else the script author should use another thread or expect anything.\n\t\t//\tAPI SendInput dispatches sent messages anyway.\n\t\t//\t_Click shows warning if window of this thread.\n\t\t\n\t\t//FUTURE: sync better, especially finally.\n\t}\n\t\n\t[ThreadStatic] static MButtons t_pressedButtons;\n\t\n\tstatic void _Click(MButton button, POINT p, wnd w = default) {\n\t\tif (w.Is0) w = Api.WindowFromPoint(p);\n\t\tbool windowOfThisThread = w.IsOfThisThread;\n\t\tif (windowOfThisThread) print.warning(\"Click(window of own thread) may not work. Use another thread.\");\n\t\t//Sending a click to a window of own thread often does not work.\n\t\t//Reason 1: often the window on down event enters a message loop that waits for up event. But then this func cannot send the up event because it is in the loop (if it does doevents).\n\t\t//\tKnown workarounds:\n\t\t//\t1 (applied). Don't sleepdoevents between sending down and up events.\n\t\t//\t2. Let this func send the click from another thread, and sleepdoevents until that thread finishes the click.\n\t\t//Reason 2: if this func called from a click handler, OS does not send more mouse events.\n\t\t//\tKnown workarounds:\n\t\t//\t1. (applied): show warning. Let the user modify the script: either don't click own windows or click from another thread.\n\t\t\n\t\tint sleep = opt.mouse.ClickSpeed;\n\t\t\n\t\tswitch (button & (MButton.Down | MButton.Up | MButton.DoubleClick)) {\n\t\tcase MButton.DoubleClick:\n\t\t\tsleep = Math.Min(sleep, Api.GetDoubleClickTime() / 4);\n\t\t\t//info: default double-click time is 500. Control Panel can set 200-900. API can set 1.\n\t\t\t//info: to detect double-click, some apps use time between down and down (that is why /4), others between up and down.\n\t\t\t\n\t\t\tSendButton_(button, true, p);\n\t\t\tif (!windowOfThisThread) _Sleep(sleep);\n\t\t\tSendButton_(button, false, p);\n\t\t\tif (!windowOfThisThread) _Sleep(sleep);\n\t\t\tgoto case 0;\n\t\tcase 0: //click\n\t\t\tSendButton_(button, true, p);\n\t\t\tif (!windowOfThisThread) _Sleep(sleep);\n\t\t\tSendButton_(button, false, p);\n\t\t\tbreak;\n\t\tcase MButton.Down:\n\t\t\tSendButton_(button, true, p);\n\t\t\tbreak;\n\t\tcase MButton.Up:\n\t\t\tSendButton_(button, false, p);\n\t\t\tbreak;\n\t\tdefault: throw new ArgumentException(\"Incompatible flags: Down, Up, DoubleClick\", nameof(button));\n\t\t}\n\t\t_Sleep(sleep + opt.mouse.ClickSleepFinally);\n\t\t\n\t\t//rejected: detect click failures (UAC, BlockInput, hooks).\n\t\t//\tDifficult. Cannot detect reliably. SendInput returns true.\n\t\t//\tEg when blocked by UAC, GetKeyState shows changed toggle state. Then probably hooks also called, did not test.\n\t}\n\t\n\t/// <summary>\n\t/// Clicks, double-clicks, presses or releases a mouse button at position <i>x y</i> relative to window <i>w</i>.\n\t/// </summary>\n\t/// <returns>The return value can be used to auto-release the pressed button. Example: <see cref=\"MRelease\"/>.</returns>\n\t/// <param name=\"button\">Button and action. Default: left click.</param>\n\t/// <exception cref=\"ArgumentException\">Invalid <i>button</i> flags (multiple buttons or actions specified).</exception>\n\t/// <inheritdoc cref=\"click(wnd, Coord, Coord, bool)\"/>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// mouse.clickEx(MButton.Middle, w1, 695, 110);\n\t/// mouse.clickEx(MButton.Right | MButton.Down, w1, 695, 110);\n\t/// mouse.clickEx(MButton.Right | MButton.Up, w1, 695, 110);\n\t/// ]]></code>\n\t/// </example>\n\tpublic static MRelease clickEx(MButton button, wnd w, Coord x = default, Coord y = default, bool nonClient = false) {\n\t\tPOINT p = move(w, x, y, nonClient);\n\t\t\n\t\t//Make sure will click w, not another window.\n\t\tvar action = button & (MButton.Down | MButton.Up | MButton.DoubleClick);\n\t\tif (action != MButton.Up && !opt.mouse.Relaxed) { //allow to release anywhere, eg it could be a drag-drop\n\t\t\tvar wTL = w.Window;\n\t\t\tbool bad = !wTL.Rect.Contains(p);\n\t\t\tif (!bad) {\n\t\t\t\tif (!_CheckWindowFromPoint()) {\n\t\t\t\t\t//Debug_.Print(\"need to activate\");\n\t\t\t\t\t//info: activating brings to the Z top and also uncloaks\n\t\t\t\t\tif (!wTL.IsEnabled(false)) bad = true; //probably an owned modal dialog disabled the window\n\t\t\t\t\telse if (wTL.ThreadId == wnd.getwnd.shellWindow.ThreadId) bad = true; //desktop\n\t\t\t\t\telse if (wTL.IsActive) wTL.ZorderTop(); //can be below another window in the same topmost/normal Z order, although it is rare.\n\t\t\t\t\telse bad = !wTL.Activate_(wnd.Internal_.ActivateFlags.NoThrowIfInvalid | wnd.Internal_.ActivateFlags.IgnoreIfNoActivateStyleEtc | wnd.Internal_.ActivateFlags.NoGetWindow);\n\t\t\t\t\t\n\t\t\t\t\t//rejected: if wTL is desktop, minimize windows. Scripts should not have a reason to click desktop. If need, they can minimize windows explicitly.\n\t\t\t\t\t//CONSIDER: activate always, because some controls don't respond when clicked while the window is inactive. But there is a risk to activate a window that does not want to be activated on click, even if we don't activate windows that have noactivate style. Probably better let the script author insert Activate before Click when need.\n\t\t\t\t\t//CONSIDER: what if the window is hung?\n\t\t\t\t\t\n\t\t\t\t\tif (!bad) bad = !_CheckWindowFromPoint();\n\t\t\t\t} else if (!wTL.IsActive && !wTL.IsNoActivateStyle_()) {\n\t\t\t\t\t//activate window, because some windows/controls have this nasty feature:\n\t\t\t\t\t//\tIf window inactive, the first click just activates the window but does not execute the click action.\n\t\t\t\t\t//\tExample: ribbon controls.\n\t\t\t\t\t//\tUsually on WM_MOUSEACTIVATE they return MA_ACTIVATEANDEAT. We could send the message to detect it, but it's dirty and dangerous, eg some windows try to activate or focus self.\n\t\t\t\t\t//\tIn any case, activating could make more reliable. In QM2 it worked well, don't remember any problems.\n\t\t\t\t\twTL.ActivateL();\n\t\t\t\t\twTL.MinimalSleepIfOtherThread_();\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (bad) throw new AuWndException(wTL, \"Cannot click. The point is not in the window\");\n\t\t\t\n\t\t\tbool _CheckWindowFromPoint() {\n\t\t\t\tvar wfp = wnd.fromXY(p, WXYFlags.NeedWindow);\n\t\t\t\tif (wfp == wTL) return true;\n\t\t\t\t//forgive if same thread and no title bar. Eg a tooltip that disappears and relays the click to its owner window. But not if wTL is disabled.\n\t\t\t\tif (wTL.IsEnabled(false) && wfp.ThreadId == wTL.ThreadId && !wfp.HasStyle(WS.CAPTION)) return true;\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\t\n\t\t_Click(button, p, w);\n\t\treturn button;\n\t}\n\t\n\t/// <summary>\n\t/// Clicks, double-clicks, presses or releases a mouse button at position <i>x y</i> relative to UI object <i>obj</i>.\n\t/// </summary>\n\t/// <param name=\"button\">Button and action. Default: left click.</param>\n\t/// <param name=\"obj\">Can be <see cref=\"wnd\"/>, <see cref=\"elm\"/> (<see cref=\"elm.MouseClick\"/>), <see cref=\"uiimage\"/> (<see cref=\"uiimage.MouseClick\"/>), <see cref=\"screen\"/>, <see cref=\"RECT\"/> in screen, <see cref=\"RECT\"/> in window, <see cref=\"lastXY\"/> (<c>true</c>), <see cref=\"xy\"/> (<c>false</c>).</param>\n\t/// <param name=\"x\">X coordinate relative to <i>obj</i>. Default - center. Examples: <c>10</c>, <c>^10</c> (reverse), <c>.5f</c> (fraction).</param>\n\t/// <param name=\"y\">Y coordinate relative to <i>obj</i>. Default - center.</param>\n\t/// <example></example>\n\t/// <inheritdoc cref=\"clickEx(MButton, wnd, Coord, Coord, bool)\"/>\n\t/// <exception cref=\"Exception\">Other exceptions. Depends on <i>obj</i> type.</exception>\n\tpublic static MRelease clickEx(MButton button, MObject obj, Coord x = default, Coord y = default) {\n\t\tswitch (obj.Value) {\n\t\tcase wnd w:\n\t\t\treturn clickEx(button, w, x, y);\n\t\tcase elm e:\n\t\t\treturn e.MouseClick(x, y, button);\n\t\tcase uiimage u:\n\t\t\treturn u.MouseClick(x, y, button);\n\t\tcase RECT r:\n\t\t\treturn mouse.clickEx(button, Coord.NormalizeInRect(x, y, r, centerIfEmpty: true));\n\t\tcase (wnd w, RECT r):\n\t\t\tvar p = Coord.NormalizeInRect(x, y, r, centerIfEmpty: true);\n\t\t\treturn mouse.clickEx(button, w, p.x, p.y);\n\t\tcase (wnd w, bool nonClient):\n\t\t\treturn mouse.clickEx(button, w, x, y, nonClient);\n\t\tcase (screen s, bool workArea):\n\t\t\treturn mouse.clickEx(button, Coord.Normalize(x, y, workArea, s, centerIfEmpty: true));\n\t\tcase bool useLastXY:\n\t\t\treturn mouse.clickEx(button, _CoordToRelativeXY(x, y, useLastXY));\n\t\tcase null: //default(MObject)\n\t\t\treturn clickEx(button, x, y);\n\t\t}\n\t\treturn default; //never\n\t}\n\t\n\t/// <summary>\n\t/// Clicks, double-clicks, presses or releases a mouse button at the specified position in screen.\n\t/// </summary>\n\t/// <returns>The return value can be used to auto-release the pressed button. Example: <see cref=\"MRelease\"/>.</returns>\n\t/// <param name=\"button\">Button and action. Default: left click.</param>\n\t/// <exception cref=\"ArgumentException\">Invalid <i>button</i> flags (multiple buttons or actions specified).</exception>\n\t/// <inheritdoc cref=\"click(Coord, Coord)\"/>\n\tpublic static MRelease clickEx(MButton button, Coord x, Coord y) {\n\t\tPOINT p = move(x, y);\n\t\t_Click(button, p);\n\t\treturn button;\n\t}\n\t\n\t/// <param name=\"p\">\n\t/// Coordinates.\n\t/// Tip: To specify coordinates relative to the right, bottom, work area or a non-primary screen, use <see cref=\"Coord.Normalize\"/>, like in the example.\n\t/// </param>\n\t/// <inheritdoc cref=\"clickEx(MButton, Coord, Coord)\"/>\n\t/// <example>\n\t/// Click at 100 200.\n\t/// <code><![CDATA[\n\t/// mouse.clickEx(MButton.Left, (100, 200));\n\t/// ]]></code>\n\t/// \n\t/// Right-click at 50 from left and 100 from bottom of the work area.\n\t/// <code><![CDATA[\n\t/// mouse.clickEx(MButton.Right, Coord.Normalize(50, ^100, workArea: true));\n\t/// ]]></code>\n\t/// </example>\n\tpublic static MRelease clickEx(MButton button, POINT p) {\n\t\tmove(p);\n\t\t_Click(button, p);\n\t\treturn button;\n\t}\n\t\n\t/// <summary>\n\t/// Clicks, double-clicks, presses or releases a mouse button.\n\t/// By default does not move the mouse cursor.\n\t/// </summary>\n\t/// <returns>The return value can be used to auto-release the pressed button. Example: <see cref=\"MRelease\"/>.</returns>\n\t/// <param name=\"button\">Button and action. Default: left click.</param>\n\t/// <param name=\"useLastXY\">\n\t/// Use <see cref=\"lastXY\"/>. It is the mouse cursor position set by the most recent \"mouse move\" or \"mouse click\" function. Use this option for reliability.\n\t/// Example: <c>mouse.move(100, 100); mouse.clickEx(..., true);</c>. The click is always at 100 100, even if somebody changes cursor position between <c>mouse.move</c> sets it and <c>mouse.clickEx</c> uses it. In such case this option atomically moves the cursor to <see cref=\"lastXY\"/>. This movement is instant and does not use <see cref=\"opt\"/>.\n\t/// If <c>false</c> (default), clicks at the current cursor position (does not move it).\n\t/// </param>\n\t/// <exception cref=\"ArgumentException\">Invalid <i>button</i> flags (multiple buttons or actions specified).</exception>\n\t/// <exception cref=\"Exception\">If <i>lastXY</i> <c>true</c> and need to move the cursor - exceptions of <see cref=\"move(POINT)\"/>.</exception>\n\t/// <exception cref=\"InputDesktopException\"></exception>\n\t/// <remarks>\n\t/// Uses <see cref=\"opt.mouse\"/>: <see cref=\"OMouse.ClickSpeed\"/>, <see cref=\"OMouse.ClickSleepFinally\"/> and maybe those used by <see cref=\"move(POINT)\"/>.\n\t/// </remarks>\n\tpublic static MRelease clickEx(MButton button = MButton.Left, bool useLastXY = false) {\n\t\tPOINT p;\n\t\tif (useLastXY) p = lastXY;\n\t\telse {\n\t\t\tp = xy;\n\t\t\tif (s_prevMousePos is { } v) v.last = p; else s_prevMousePos = new(p); //sets .first=.last=p\n\t\t}\n\t\t_Click(button, p);\n\t\treturn button;\n\t}\n\t\n\t/// <summary>\n\t/// Left button click at position <i>x y</i> relative to window <i>w</i>.\n\t/// </summary>\n\t/// <param name=\"w\">Window or control.</param>\n\t/// <param name=\"x\">X coordinate relative to the client area of <i>w</i>. Default - center. Examples: <c>10</c>, <c>^10</c> (reverse), <c>.5f</c> (fraction).</param>\n\t/// <param name=\"y\">Y coordinate relative to the client area of <i>w</i>. Default - center.</param>\n\t/// <param name=\"nonClient\">The specified position is relative to the window rectangle, not to its client area.</param>\n\t/// <exception cref=\"AuWndException\">\n\t/// - The specified position is not in the window (read more in Remarks).\n\t/// - Invalid window.\n\t/// - The window is hidden. No exception if just cloaked, for example in another desktop; then on click will activate, which usually uncloaks. No exception if <i>w</i> is a control.\n\t/// - Other window-related failures.\n\t/// </exception>\n\t/// <inheritdoc cref=\"move(POINT)\" path=\"/exception\"/>\n\t/// <remarks>\n\t/// To move the mouse cursor, calls <see cref=\"move(wnd, Coord, Coord, bool)\"/>.\n\t/// If after moving the cursor it is not in the window (or a window of its thread), activates the window (or its top-level parent window). Throws exception if then <i>x y</i> is still not in the window. Skips all this when just releasing button or if option <c>Relaxed</c> is <c>true</c>. If <i>w</i> is a control, <i>x y</i> can be somewhere else in its top-level parent window.\n\t/// \n\t/// Uses <see cref=\"opt.mouse\"/>: <see cref=\"OMouse.MoveSpeed\"/>, <see cref=\"OMouse.MoveSleepFinally\"/> (between moving and clicking), <see cref=\"OMouse.ClickSpeed\"/>, <see cref=\"OMouse.ClickSleepFinally\"/>, <see cref=\"OMouse.Relaxed\"/>.\n\t/// </remarks>\n\tpublic static void click(wnd w, Coord x = default, Coord y = default, bool nonClient = false) {\n\t\tclickEx(MButton.Left, w, x, y, nonClient);\n\t}\n\t\n\t/// <summary>\n\t/// Left button click at position <i>x y</i>.\n\t/// </summary>\n\t/// <param name=\"x\">X coordinate in the screen. Examples: <c>10</c>, <c>^10</c> (reverse), <c>.5f</c> (fraction).</param>\n\t/// <param name=\"y\">Y coordinate in the screen.</param>\n\t/// <inheritdoc cref=\"move(POINT)\" path=\"/exception\"/>\n\t/// <remarks>\n\t/// Uses <see cref=\"opt.mouse\"/>: <see cref=\"OMouse.ClickSpeed\"/>, <see cref=\"OMouse.ClickSleepFinally\"/> and those used by <see cref=\"move(POINT)\"/>.\n\t/// </remarks>\n\tpublic static void click(Coord x, Coord y) {\n\t\t//note: most Click functions don't have a workArea and screen parameter. It is rarely used. For reliability better use the overloads that use window coordinates.\n\t\t\n\t\tclickEx(MButton.Left, x, y);\n\t}\n\t\n\t/// <summary>\n\t/// Left button click.\n\t/// </summary>\n\t/// <param name=\"useLastXY\">Use <see cref=\"lastXY\"/>, not current cursor position. More info: <see cref=\"clickEx(MButton, bool)\"/>.</param>\n\t/// <exception cref=\"Exception\">If <i>lastXY</i> <c>true</c> and need to move the cursor - exceptions of <see cref=\"move(POINT)\"/>.</exception>\n\t/// <exception cref=\"InputDesktopException\"></exception>\n\t/// <remarks>\n\t/// Uses <see cref=\"opt.mouse\"/>: <see cref=\"OMouse.ClickSpeed\"/>, <see cref=\"OMouse.ClickSleepFinally\"/> and maybe those used by <see cref=\"move(POINT)\"/>.\n\t/// </remarks>\n\tpublic static void click(bool useLastXY = false) {\n\t\tclickEx(MButton.Left, useLastXY);\n\t}\n\t\n\t/// <summary>\n\t/// Right button click at position <i>x y</i> relative to window <i>w</i>.\n\t/// </summary>\n\t/// <inheritdoc cref=\"click(wnd, Coord, Coord, bool)\"/>\n\tpublic static void rightClick(wnd w, Coord x = default, Coord y = default, bool nonClient = false) {\n\t\tclickEx(MButton.Right, w, x, y, nonClient);\n\t}\n\t\n\t/// <summary>\n\t/// Right button click at position <i>x y</i>.\n\t/// </summary>\n\t/// <inheritdoc cref=\"click(Coord, Coord)\"/>\n\tpublic static void rightClick(Coord x, Coord y) {\n\t\tclickEx(MButton.Right, x, y);\n\t}\n\t\n\t/// <summary>\n\t/// Right button click.\n\t/// </summary>\n\t/// <inheritdoc cref=\"click(bool)\"/>\n\tpublic static void rightClick(bool useLastXY = false) {\n\t\tclickEx(MButton.Right, useLastXY);\n\t}\n\t\n\t/// <summary>\n\t/// Left button double click at position <i>x y</i> relative to window <i>w</i>.\n\t/// </summary>\n\t/// <inheritdoc cref=\"click(wnd, Coord, Coord, bool)\"/>\n\tpublic static void doubleClick(wnd w, Coord x = default, Coord y = default, bool nonClient = false) {\n\t\tclickEx(MButton.Left | MButton.DoubleClick, w, x, y, nonClient);\n\t}\n\t\n\t/// <summary>\n\t/// Left button double click at position <i>x y</i>.\n\t/// </summary>\n\t/// <inheritdoc cref=\"click(Coord, Coord)\"/>\n\tpublic static void doubleClick(Coord x, Coord y) {\n\t\tclickEx(MButton.Left | MButton.DoubleClick, x, y);\n\t}\n\t\n\t/// <summary>\n\t/// Left button double click.\n\t/// </summary>\n\t/// <inheritdoc cref=\"click(bool)\"/>\n\tpublic static void doubleClick(bool useLastXY = false) {\n\t\tclickEx(MButton.Left | MButton.DoubleClick, useLastXY);\n\t}\n\t\n\t/// <summary>\n\t/// Left down (press and don't release) at position <i>x y</i> relative to window <i>w</i>.\n\t/// </summary>\n\t/// <returns>The return value can be used to auto-release the pressed button. Example: <see cref=\"MRelease\"/>.</returns>\n\t/// <inheritdoc cref=\"click(wnd, Coord, Coord, bool)\"/>\n\tpublic static MRelease leftDown(wnd w, Coord x = default, Coord y = default, bool nonClient = false) {\n\t\treturn clickEx(MButton.Left | MButton.Down, w, x, y, nonClient);\n\t}\n\t\n\t/// <summary>\n\t/// Left button down (press and don't release) at position <i>x y</i>.\n\t/// </summary>\n\t/// <returns>The return value can be used to auto-release the pressed button. Example: <see cref=\"MRelease\"/>.</returns>\n\t/// <inheritdoc cref=\"click(Coord, Coord)\"/>\n\tpublic static MRelease leftDown(Coord x, Coord y) {\n\t\treturn clickEx(MButton.Left | MButton.Down, x, y);\n\t}\n\t\n\t/// <summary>\n\t/// Left button down (press and don't release).\n\t/// </summary>\n\t/// <returns>The return value can be used to auto-release the pressed button. Example: <see cref=\"MRelease\"/>.</returns>\n\t/// <inheritdoc cref=\"click(bool)\"/>\n\tpublic static MRelease leftDown(bool useLastXY = false) {\n\t\treturn clickEx(MButton.Left | MButton.Down, useLastXY);\n\t}\n\t\n\t/// <summary>\n\t/// Left button up (release pressed button) at position <i>x y</i> relative to window <i>w</i>.\n\t/// </summary>\n\t/// <inheritdoc cref=\"click(wnd, Coord, Coord, bool)\"/>\n\tpublic static void leftUp(wnd w, Coord x = default, Coord y = default, bool nonClient = false) {\n\t\tclickEx(MButton.Left | MButton.Up, w, x, y, nonClient);\n\t}\n\t\n\t/// <summary>\n\t/// Left button up (release pressed button) at position <i>x y</i>.\n\t/// </summary>\n\t/// <inheritdoc cref=\"click(Coord, Coord)\"/>\n\tpublic static void leftUp(Coord x, Coord y) {\n\t\tclickEx(MButton.Left | MButton.Up, x, y);\n\t}\n\t\n\t/// <summary>\n\t/// Left button up (release pressed button).\n\t/// </summary>\n\t/// <inheritdoc cref=\"click(bool)\"/>\n\tpublic static void leftUp(bool useLastXY = false) {\n\t\tclickEx(MButton.Left | MButton.Up, useLastXY);\n\t}\n\t\n\t/// <summary>\n\t/// Right button down (press and don't release) at position <i>x y</i> relative to window <i>w</i>.\n\t/// </summary>\n\t/// <returns>The return value can be used to auto-release the pressed button. Example: <see cref=\"MRelease\"/>.</returns>\n\t/// <inheritdoc cref=\"click(wnd, Coord, Coord, bool)\"/>\n\tpublic static MRelease rightDown(wnd w, Coord x = default, Coord y = default, bool nonClient = false) {\n\t\treturn clickEx(MButton.Right | MButton.Down, w, x, y, nonClient);\n\t}\n\t\n\t/// <summary>\n\t/// Right button down (press and don't release) at position <i>x y</i>.\n\t/// </summary>\n\t/// <returns>The return value can be used to auto-release the pressed button. Example: <see cref=\"MRelease\"/>.</returns>\n\t/// <inheritdoc cref=\"click(Coord, Coord)\"/>\n\tpublic static MRelease rightDown(Coord x, Coord y) {\n\t\treturn clickEx(MButton.Right | MButton.Down, x, y);\n\t}\n\t\n\t/// <summary>\n\t/// Right button down (press and don't release).\n\t/// </summary>\n\t/// <returns>The return value can be used to auto-release the pressed button. Example: <see cref=\"MRelease\"/>.</returns>\n\t/// <inheritdoc cref=\"click(bool)\"/>\n\tpublic static MRelease rightDown(bool useLastXY = false) {\n\t\treturn clickEx(MButton.Right | MButton.Down, useLastXY);\n\t}\n\t\n\t/// <summary>\n\t/// Right button up (release pressed button) at position <i>x y</i> relative to window <i>w</i>.\n\t/// </summary>\n\t/// <inheritdoc cref=\"click(wnd, Coord, Coord, bool)\"/>\n\tpublic static void rightUp(wnd w, Coord x = default, Coord y = default, bool nonClient = false) {\n\t\tclickEx(MButton.Right | MButton.Up, w, x, y, nonClient);\n\t}\n\t\n\t/// <summary>\n\t/// Right button up (release pressed button) at position <i>x y</i>.\n\t/// </summary>\n\t/// <inheritdoc cref=\"click(Coord, Coord)\"/>\n\tpublic static void rightUp(Coord x, Coord y) {\n\t\tclickEx(MButton.Right | MButton.Up, x, y);\n\t}\n\t\n\t/// <summary>\n\t/// Right button up (release pressed button).\n\t/// </summary>\n\t/// <inheritdoc cref=\"click(bool)\"/>\n\tpublic static void rightUp(bool useLastXY = false) {\n\t\tclickEx(MButton.Right | MButton.Up, useLastXY);\n\t}\n\t\n\t/// <summary>\n\t/// Mouse wheel forward or backward.\n\t/// </summary>\n\t/// <param name=\"ticks\">Number of wheel ticks forward (positive) or backward (negative).</param>\n\t/// <param name=\"horizontal\">Horizontal wheel.</param>\n\t/// <remarks>\n\t/// Uses <see cref=\"opt.mouse\"/>: <see cref=\"OMouse.ClickSleepFinally\"/>.\n\t/// </remarks>\n\t/// <exception cref=\"InputDesktopException\"></exception>\n\tpublic static void wheel(double ticks, bool horizontal = false) {\n\t\tbool neg = ticks < 0; if (neg) ticks = -ticks;\n\t\tticks *= 120;\n\t\twhile (ticks > 0) {\n\t\t\tshort t = (short)(ticks < 30000 ? Math.Ceiling(ticks) : 30000); //max 250 full ticks\n\t\t\t_SendRaw(horizontal ? Api.IMFlags.HWheel : Api.IMFlags.Wheel, 0, 0, neg ? -t : t);\n\t\t\tticks -= t;\n\t\t}\n\t\t_Sleep(opt.mouse.ClickSleepFinally);\n\t}\n\t\n\t//rejected. Not so often used. It's easy to move(); wheel().\n\t///// <summary>\n\t///// Mouse move and wheel.\n\t///// </summary>\n\t//public static void wheel(Coord x, Coord y, double ticks, bool horizontal = false) {\n\t//\tmove(x, y);\n\t//\twheel(ticks, horizontal);\n\t//}\n\t\n\t///// <summary>\n\t///// Mouse move and wheel.\n\t///// </summary>\n\t//public static void wheel(wnd w, Coord x, Coord y, double ticks, bool horizontal = false) {\n\t//\tmove(w, x, y);\n\t//\twheel(ticks, horizontal);\n\t//}\n\t\n\t/// <summary>\n\t/// Presses a mouse button in object <i>o1</i>, moves the mouse cursor to object <i>o2</i> and releases the button.\n\t/// </summary>\n\t/// <param name=\"o1\">UI object (window, UI element, etc) where to press the mouse button.</param>\n\t/// <param name=\"o2\">UI object where to release the mouse button.</param>\n\t/// <param name=\"x1\">X offset in <i>o1</i> rectangle. Default: center.</param>\n\t/// <param name=\"y1\">Y offset in <i>o1</i> rectangle. Default: center.</param>\n\t/// <param name=\"x2\">X offset in <i>o2</i> rectangle. Default: center.</param>\n\t/// <param name=\"y2\">Y offset in <i>o2</i> rectangle. Default: center.</param>\n\t/// <param name=\"button\">Mouse button. Default: left.</param>\n\t/// <param name=\"mod\">Modifier keys (<c>Ctrl</c> etc).</param>\n\t/// <param name=\"sleep\">Wait this number of milliseconds after pressing the mouse button.</param>\n\t/// <param name=\"speed\">The drag speed. See <see cref=\"OMouse.MoveSpeed\"/>.</param>\n\t/// <inheritdoc cref=\"clickEx(MButton, MObject, Coord, Coord)\" path=\"/exception\"/>\n\tpublic static void drag(MObject o1, MObject o2, Coord x1 = default, Coord y1 = default, Coord x2 = default, Coord y2 = default, MButton button = MButton.Left, KMod mod = 0, int sleep = 0, int speed = 5) {\n\t\tif (o2.Value is bool useLastXY) { //get mouse position before moving to o1\n\t\t\tvar p = _CoordToRelativeXY(x2, y2, useLastXY);\n\t\t\to2 = default; x2 = p.x; y2 = p.y;\n\t\t}\n\t\t_Drag(o1, x1, y1, button, mod, sleep, speed, () => move(o2, x2, y2));\n\t}\n\t\n\t/// <summary>\n\t/// Presses a mouse button in object <i>obj</i>, moves the mouse cursor by offset <i>dx</i> <i>dy</i> and releases the button.\n\t/// </summary>\n\t/// <param name=\"obj\">UI object (window, UI element, etc) where to press the mouse button.</param>\n\t/// <param name=\"x\">X offset in <i>obj</i> rectangle. Default: center.</param>\n\t/// <param name=\"y\">Y offset in <i>obj</i> rectangle. Default: center.</param>\n\t/// <param name=\"dx\">X offset from the start position.</param>\n\t/// <param name=\"dy\">Y offset from the start position.</param>\n\t/// <param name=\"button\">Mouse button. Default: left.</param>\n\t/// <param name=\"mod\">Modifier keys (<c>Ctrl</c> etc).</param>\n\t/// <param name=\"sleep\">Wait this number of milliseconds after pressing the mouse button.</param>\n\t/// <param name=\"speed\">The drag speed. See <see cref=\"OMouse.MoveSpeed\"/>.</param>\n\t/// <inheritdoc cref=\"clickEx(MButton, MObject, Coord, Coord)\" path=\"/exception\"/>\n\tpublic static void drag(MObject obj, Coord x, Coord y, int dx, int dy, MButton button = MButton.Left, KMod mod = 0, int sleep = 0, int speed = 5) {\n\t\t_Drag(obj, x, y, button, mod, sleep, speed, () => moveBy(dx, dy));\n\t}\n\t\n\t/// <summary>\n\t/// Presses a mouse button in object <i>obj</i>, moves the mouse cursor using multiple recorded offsets and releases the button.\n\t/// </summary>\n\t/// <param name=\"obj\">UI object (window, UI element, etc) where to press the mouse button.</param>\n\t/// <param name=\"x\">X offset in <i>obj</i> rectangle. Default: center.</param>\n\t/// <param name=\"y\">Y offset in <i>obj</i> rectangle. Default: center.</param>\n\t/// <param name=\"offsets\">String containing multiple x y offsets from the start position. See <see cref=\"moveBy(string, double)\"/>.</param>\n\t/// <param name=\"button\">Mouse button. Default: left.</param>\n\t/// <param name=\"mod\">Modifier keys (<c>Ctrl</c> etc).</param>\n\t/// <param name=\"sleep\">Wait this number of milliseconds after pressing the mouse button.</param>\n\t/// <inheritdoc cref=\"clickEx(MButton, MObject, Coord, Coord)\" path=\"/exception\"/>\n\tpublic static void drag(MObject obj, Coord x, Coord y, string offsets, MButton button = MButton.Left, KMod mod = 0, int sleep = 0) {\n\t\t_Drag(obj, x, y, button, mod, sleep, 0, () => moveBy(offsets));\n\t}\n\t\n\tstatic void _Drag(MObject from, Coord x1, Coord y1, MButton button, KMod mod, int sleep, int speed, Action action) {\n\t\tif (button == 0) button = MButton.Left; else if (button != (button & (MButton.Left | MButton.Right | MButton.Middle | MButton.X1 | MButton.X2))) throw new ArgumentException(null, nameof(button));\n\t\tif ((uint)sleep >= 10000) throw new ArgumentException(null, nameof(sleep));\n\t\tif ((uint)speed >= 10000) throw new ArgumentException(null, nameof(speed));\n\t\tint speed0 = opt.mouse.MoveSpeed;\n\t\tbool isMod = false, isButton = false;\n\t\ttry {\n\t\t\tclickEx(button | MButton.Down, from, x1, y1);\n\t\t\tisButton = true;\n\t\t\t\n\t\t\tif (sleep > 0) wait.ms(sleep);\n\t\t\t\n\t\t\t_SendMod(true); //note: after button down. If before, it could eg Ctrl+select multiple objects instead of one.\n\t\t\tisMod = true;\n\t\t\t\n\t\t\topt.mouse.MoveSpeed = speed;\n\t\t\t\n\t\t\taction();\n\t\t}\n\t\tfinally {\n\t\t\tif (isButton) clickEx(button | MButton.Up, useLastXY: true);\n\t\t\topt.mouse.MoveSpeed = speed0;\n\t\t\tif (isMod) _SendMod(false);\n\t\t}\n\t\t\n\t\tvoid _SendMod(bool down) {\n\t\t\tif (mod == 0) return;\n\t\t\tvar k = new keys(opt.key);\n\t\t\tif (mod.Has(KMod.Ctrl)) k.AddKey(KKey.Ctrl, down);\n\t\t\tif (mod.Has(KMod.Shift)) k.AddKey(KKey.Shift, down);\n\t\t\tif (mod.Has(KMod.Alt)) k.AddKey(KKey.Alt, down);\n\t\t\tif (mod.Has(KMod.Win)) k.AddKey(KKey.Win, down);\n\t\t\tk.SendNow(); //and sleeps opt.key.SleepFinally (default 10)\n\t\t}\n\t}\n\t\n\t//not used\n\t///// <summary>\n\t///// Releases mouse buttons pressed by this thread (<c>t_pressedButtons</c>).\n\t///// </summary>\n\t///// <param name=\"p\">If not <c>null</c>, and XY is different, moves to this point. Used for reliability.</param>\n\t//static void _ReleaseButtons(POINT? p = null)\n\t//{\n\t//\tvar b = t_pressedButtons;\n\t//\tif(0 != (b & MButtons.Left)) _Click(MButton.Left | MButton.Up, p);\n\t//\tif(0 != (b & MButtons.Right)) _Click(MButton.Right | MButton.Up, p);\n\t//\tif(0 != (b & MButtons.Middle)) _Click(MButton.Middle | MButton.Up, p);\n\t//\tif(0 != (b & MButtons.X1)) _Click(MButton.X1 | MButton.Up, p);\n\t//\tif(0 != (b & MButtons.X2)) _Click(MButton.X2 | MButton.Up, p);\n\t//}\n\t//rejected: finally release script-pressed buttons, especially on exception. Instead let use code: using(mouse.leftDown(...)), it auto-releases pressed button.\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if some mouse buttons are pressed.\n\t/// </summary>\n\t/// <param name=\"buttons\">Return <c>true</c> if some of these buttons are down. Default: any.</param>\n\t/// <remarks>\n\t/// Uses API <ms>GetAsyncKeyState</ms>.\n\t/// When processing user input in UI code (forms, WPF), instead use class <see cref=\"keys.gui\"/> or .NET functions. They use API <ms>GetKeyState</ms>.\n\t/// When mouse left and right buttons are swapped, gets logical state, not physical.\n\t/// </remarks>\n\t/// <seealso cref=\"waitForNoButtonsPressed\"/>\n\tpublic static bool isPressed(MButtons buttons = MButtons.Left | MButtons.Right | MButtons.Middle | MButtons.X1 | MButtons.X2) {\n\t\tif (0 != (buttons & MButtons.Left) && keys.isPressed(KKey.MouseLeft)) return true;\n\t\tif (0 != (buttons & MButtons.Right) && keys.isPressed(KKey.MouseRight)) return true;\n\t\tif (0 != (buttons & MButtons.Middle) && keys.isPressed(KKey.MouseMiddle)) return true;\n\t\tif (0 != (buttons & MButtons.X1) && keys.isPressed(KKey.MouseX1)) return true;\n\t\tif (0 != (buttons & MButtons.X2) && keys.isPressed(KKey.MouseX2)) return true;\n\t\treturn false;\n\t}\n\t\n\t//rejected: not useful.\n\t///// <summary>\n\t///// Returns a value indicating which mouse buttons are pressed.\n\t///// </summary>\n\t///// <param name=\"buttons\">Check only these buttons. Default: all.</param>\n\t///// <remarks>See <see cref=\"IsPressed\"/>.</remarks>\n\t//public static MButtons buttons(MButtons buttons = MButtons.Left | MButtons.Right | MButtons.Middle | MButtons.X1 | MButtons.X2)\n\t//{\n\t//\tMButtons R = 0;\n\t//\tif(0 != (buttons & MButtons.Left) && keys.isKey(KKey.MouseLeft)) R |= MButtons.Left;\n\t//\tif(0 != (buttons & MButtons.Right) && keys.isKey(KKey.MouseRight)) R |= MButtons.Right;\n\t//\tif(0 != (buttons & MButtons.Middle) && keys.isKey(KKey.MouseMiddle)) R |= MButtons.Middle;\n\t//\tif(0 != (buttons & MButtons.X1) && keys.isKey(KKey.MouseX1)) return R |= MButtons.X1;\n\t//\tif(0 != (buttons & MButtons.X2) && keys.isKey(KKey.MouseX2)) return R |= MButtons.X2;\n\t//\treturn R;\n\t//}\n\t\n\t//rejected: rarely used. Can use IsPressed.\n\t///// <summary>\n\t///// Returns <c>true</c> if the left mouse button is pressed.\n\t///// </summary>\n\t///// <remarks>See <see cref=\"IsPressed\"/>.</remarks>\n\t//public static bool isLeft => keys.isPressed(KKey.MouseLeft);\n\t\n\t///// <summary>\n\t///// Returns <c>true</c> if the right mouse button is pressed.\n\t///// </summary>\n\t///// <remarks>See <see cref=\"IsPressed\"/>.</remarks>\n\t//public static bool isRight => keys.isPressed(KKey.MouseRight);\n\t\n\t/// <summary>\n\t/// Waits while some mouse buttons are pressed. See <see cref=\"isPressed\"/>.\n\t/// </summary>\n\t/// <param name=\"timeout\">Timeout, seconds. Can be 0 (infinite), >0 (exception) or &lt;0 (no exception). More info: [](xref:wait_timeout). Default 0.</param>\n\t/// <param name=\"buttons\">Wait only for these buttons. Default - all.</param>\n\t/// <returns>Returns <c>true</c>. On timeout returns <c>false</c> if <i>timeout</i> is negative; else exception.</returns>\n\t/// <exception cref=\"TimeoutException\"><i>timeout</i> time has expired (if > 0).</exception>\n\t/// <seealso cref=\"keys.waitForNoModifierKeysAndMouseButtons\"/>\n\tpublic static bool waitForNoButtonsPressed(Seconds timeout = default, MButtons buttons = MButtons.Left | MButtons.Right | MButtons.Middle | MButtons.X1 | MButtons.X2) {\n\t\treturn keys.waitForNoModifierKeysAndMouseButtons(timeout, 0, buttons);\n\t}\n\t\n\t/// <summary>\n\t/// Waits while some buttons are pressed, except those pressed by a <see cref=\"mouse\"/> class function in this thread.\n\t/// Does nothing if option <c>Relaxed</c> is <c>true</c>.\n\t/// </summary>\n\tinternal static void WaitForNoButtonsPressed_() {\n\t\t//not public, because we have WaitForNoButtonsPressed, which is unaware about script-pressed buttons, and don't need this awareness because the script author knows what is pressed by that script\n\t\t\n\t\tif (opt.mouse.Relaxed) return;\n\t\tvar mb = (MButtons.Left | MButtons.Right | MButtons.Middle | MButtons.X1 | MButtons.X2) & ~t_pressedButtons;\n\t\tif (waitForNoButtonsPressed(-2, mb)) return; //initially was 5 s, but users don't wait so long, and then wonder why it does not work. It's documented in OMouse.Relaxed.\n\t\tprint.warning(\"The mouse-move function waits because a mouse button is pressed by the user. This is to prevent unintended drag-and-drop. To disable this in the script: <code>opt.mouse.Relaxed = true;</code>\");\n\t\twaitForNoButtonsPressed(0, mb);\n\t}\n\t\n\t/// <summary>\n\t/// Waits for button-down or button-up event of the specified mouse button or buttons.\n\t/// </summary>\n\t/// <returns>Returns <c>true</c>. On timeout returns <c>false</c> if <i>timeout</i> is negative; else exception.</returns>\n\t/// <param name=\"timeout\">Timeout, seconds. Can be 0 (infinite), >0 (exception) or &lt;0 (no exception). More info: [](xref:wait_timeout).</param>\n\t/// <param name=\"button\">Mouse button. If several buttons specified, waits for any of them.</param>\n\t/// <param name=\"up\">Wait for button-up event.</param>\n\t/// <param name=\"block\">Make the event invisible to other apps. If <i>up</i> is <c>true</c>, makes the down event invisible too, if it comes while waiting for the up event.</param>\n\t/// <exception cref=\"ArgumentException\"><i>button</i> is 0.</exception>\n\t/// <exception cref=\"TimeoutException\"><i>timeout</i> time has expired (if > 0).</exception>\n\t/// <remarks>\n\t/// Unlike <see cref=\"waitForNoButtonsPressed\"/>, waits for down or up event, not for button state.\n\t/// Uses low-level mouse hook.\n\t/// Ignores mouse events injected by functions of this library.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// mouse.waitForClick(0, MButtons.Left, up: true, block: false);\n\t/// print.it(\"click\");\n\t/// ]]></code>\n\t/// </example>\n\tpublic static bool waitForClick(Seconds timeout, MButtons button, bool up = false, bool block = false) {\n\t\tif (button == 0) throw new ArgumentException();\n\t\treturn 0 != _WaitForClick(timeout, button, up, block);\n\t}\n\t\n\t/// <summary>\n\t/// Waits for button-down or button-up event of any mouse button, and gets the button code.\n\t/// </summary>\n\t/// <returns>Returns the button code. On timeout returns 0 if <i>timeout</i> is negative; else exception.</returns>\n\t/// <exception cref=\"TimeoutException\"><i>timeout</i> time has expired (if > 0).</exception>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// var button = mouse.waitForClick(0, up: true, block: true);\n\t/// print.it(button);\n\t/// ]]></code>\n\t/// </example>\n\t/// <inheritdoc cref=\"waitForClick(Seconds, MButtons, bool, bool)\" path=\"/param\"/>\n\tpublic static MButtons waitForClick(Seconds timeout, bool up = false, bool block = false) {\n\t\treturn _WaitForClick(timeout, 0, up, block);\n\t}\n\t\n\tstatic MButtons _WaitForClick(Seconds timeout, MButtons button, bool up, bool block) {\n\t\t//info: this and related functions use similar code as keys._WaitForKey.\n\t\t\n\t\tMButtons R = 0;\n\t\tusing (WindowsHook.Mouse(x => {\n\t\t\tMButtons b = 0;\n\t\t\tswitch (x.Event) {\n\t\t\tcase HookData.MouseEvent.LeftButton: b = MButtons.Left; break;\n\t\t\tcase HookData.MouseEvent.RightButton: b = MButtons.Right; break;\n\t\t\tcase HookData.MouseEvent.MiddleButton: b = MButtons.Middle; break;\n\t\t\tcase HookData.MouseEvent.X1Button: b = MButtons.X1; break;\n\t\t\tcase HookData.MouseEvent.X2Button: b = MButtons.X2; break;\n\t\t\t}\n\t\t\tif (b == 0) return;\n\t\t\tif (button != 0 && !button.Has(b)) return;\n\t\t\tif (x.IsButtonUp != up) {\n\t\t\t\tif (up && block) { //button down when we are waiting for up. If block, now block down too.\n\t\t\t\t\tif (button == 0) button = b;\n\t\t\t\t\tx.BlockEvent();\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tR = b;\n\t\t\tif (block) x.BlockEvent();\n\t\t})) wait.doEventsUntil(timeout, () => R != 0);\n\t\t\n\t\treturn R;\n\t}\n\t//FUTURE:\n\t//\twaitForWheel(Seconds timeout, bool? forward, bool block = false)\n\t//\twaitForMouseMove, waitForMouseStop.\n\t//\tIn QM2 these functions were created because somebody asked, but I don't use.\n\t\n\t/// <summary>\n\t/// Waits for a standard mouse cursor (pointer) visible.\n\t/// </summary>\n\t/// <param name=\"timeout\">Timeout, seconds. Can be 0 (infinite), >0 (exception) or &lt;0 (no exception). More info: [](xref:wait_timeout).</param>\n\t/// <param name=\"cursor\">Id of a standard cursor.</param>\n\t/// <param name=\"not\">Wait until this cursor disappears.</param>\n\t/// <returns>Returns <c>true</c>. On timeout returns <c>false</c> if <i>timeout</i> is negative; else exception.</returns>\n\t/// <exception cref=\"TimeoutException\"><i>timeout</i> time has expired (if > 0).</exception>\n\tpublic static bool waitForCursor(Seconds timeout, MCursor cursor, bool not = false) {\n\t\tIntPtr hcur = Api.LoadCursor(default, cursor);\n\t\tif (hcur == default) throw new AuException(0, \"*load cursor\");\n\t\t\n\t\treturn wait.until(timeout, () => (MouseCursor.GetCurrentVisibleCursor(out var c) && c == hcur) ^ not);\n\t}\n\t\n\t/// <summary>\n\t/// Waits for a nonstandard mouse cursor (pointer) visible.\n\t/// </summary>\n\t/// <param name=\"timeout\">Timeout, seconds. Can be 0 (infinite), >0 (exception) or &lt;0 (no exception). More info: [](xref:wait_timeout).</param>\n\t/// <param name=\"cursorHash\">Cursor hash, as returned by <see cref=\"MouseCursor.Hash\"/>.</param>\n\t/// <param name=\"not\">Wait until this cursor disappears.</param>\n\t/// <returns>Returns <c>true</c>. On timeout returns <c>false</c> if <i>timeout</i> is negative; else exception.</returns>\n\t/// <exception cref=\"TimeoutException\"><i>timeout</i> time has expired (if > 0).</exception>\n\tpublic static bool waitForCursor(Seconds timeout, long cursorHash, bool not = false) {\n\t\tif (cursorHash == 0) throw new ArgumentException();\n\t\treturn wait.until(timeout, () => (MouseCursor.GetCurrentVisibleCursor(out var c) && MouseCursor.Hash(c) == cursorHash) ^ not);\n\t}\n\t//TODO2: example. Cookbook contains example, but no info how to get the hash (MouseCursor.GetCurrentVisibleCursor + MouseCursor.Hash). Maybe even need a tool.\n\t//TODO2: wait for any in list.\n\t\n\t/// <summary>\n\t/// Posts mouse-click messages to the window.\n\t/// </summary>\n\t/// <param name=\"w\">Window or control.</param>\n\t/// <param name=\"x\">X coordinate in <i>w</i> client area or <i>rect</i>. Default - center. Examples: <c>10</c>, <c>^10</c> (reverse), <c>.5f</c> (fraction).</param>\n\t/// <param name=\"y\">Y coordinate in <i>w</i> client area or <i>rect</i>. Default - center.</param>\n\t/// <param name=\"button\">Can specify the left (default), right or middle button. Also flag for double-click, press or release.</param>\n\t/// <param name=\"rect\">A rectangle in <i>w</i> client area. If <c>null</c> (default), <i>x y</i> are relative to the client area.</param>\n\t/// <exception cref=\"AuWndException\">Invalid window.</exception>\n\t/// <exception cref=\"ArgumentException\">Unsupported button specified.</exception>\n\t/// <remarks>\n\t/// Does not move the mouse.\n\t/// Does not wait until the target application finishes processing the message.\n\t/// Works not with all windows.\n\t/// </remarks>\n\tpublic static void postClick(wnd w, Coord x = default, Coord y = default, MButton button = MButton.Left, RECT? rect = null) {\n\t\tRECT r;\n\t\tif (rect != null) {\n\t\t\tr = rect.Value;\n\t\t\tw.ThrowIfInvalid();\n\t\t} else {\n\t\t\tif (!w.GetClientRect(out r)) w.ThrowUseNative();\n\t\t}\n\t\tPostClick_(w, r, x, y, button);\n\t}\n\t\n\tinternal static void PostClick_(wnd w, RECT r, Coord x = default, Coord y = default, MButton button = MButton.Left) {\n\t\tMButton mask = MButton.Down | MButton.Up | MButton.DoubleClick, b = button & ~mask, dud = button & mask;\n\t\tif (b == 0) b = MButton.Left;\n\t\tint m = b switch {\n\t\t\tMButton.Left => Api.WM_LBUTTONDOWN,\n\t\t\tMButton.Right => Api.WM_RBUTTONDOWN,\n\t\t\tMButton.Middle => Api.WM_MBUTTONDOWN,\n\t\t\t_ => throw new ArgumentException(\"supported buttons: left, right, middle\")\n\t\t};\n\t\tif (dud is not (0 or MButton.Down or MButton.Up or MButton.DoubleClick)) throw new ArgumentException();\n\t\t\n\t\tPOINT point = Coord.NormalizeInRect(x, y, r, centerIfEmpty: true);\n\t\t\n\t\t//if the control is mouse-transparent, use its ancestor. Example: the color selection controls in the classic color dialog.\n\t\tif (w.HasStyle(WS.CHILD)) {\n\t\t\tvar w2 = w; var ps = point; w.MapClientToScreen(ref ps);\n\t\t\twhile (!w2.Is0 && Api.HTTRANSPARENT == w2.Send(Api.WM_NCHITTEST, 0, Math2.MakeLparam(ps))) w2 = w2.Get.DirectParent;\n\t\t\tif (w2 != w && !w2.Is0) { w.MapClientToClientOf(w2, ref point); w = w2; }\n\t\t}\n\t\t\n\t\tusing var workaround = new ButtonPostClickWorkaround_(w);\n\t\t\n\t\tnint xy = Math2.MakeLparam(point);\n\t\tnint mk = 0; if (keys.isCtrl) mk |= Api.MK_CONTROL; if (keys.isShift) mk |= Api.MK_SHIFT;\n\t\tnint mk1 = mk; if (dud != MButton.Up) mk1 |= b switch { MButton.Left => Api.MK_LBUTTON, MButton.Right => Api.MK_RBUTTON, _ => Api.MK_MBUTTON };\n\t\tif (dud != MButton.Up) w.Post(m, mk1, xy);\n\t\tif (dud != MButton.Down) {\n\t\t\tw.Post(Api.WM_MOUSEMOVE, mk1, xy);\n\t\t\tw.Post(m + 1, mk, xy);\n\t\t}\n\t\tif (dud == MButton.DoubleClick) {\n\t\t\tw.Post(m + 2, mk1, xy);\n\t\t\tw.Post(m + 1, mk, xy);\n\t\t}\n\t\t//_MinimalSleep(); //don't need. Eg elm.Invoke() does not wait too.\n\t\t\n\t\t//never mind: support nonclient (WM_NCRBUTTONDOWN etc)\n\t}\n\t\n\t/// <summary>\n\t/// Workaround for the documented <c>BM_CLICK</c>/<c>WM_LBUTTONDOWN</c> bug of classic button controls: randomly fails if inactive window.\n\t/// If <i>c</i> is a button in a dialog box, posts <c>WM_ACTIVATE</c> messages to the dialog box.\n\t/// </summary>\n\tinternal ref struct ButtonPostClickWorkaround_ {\n\t\treadonly wnd _w;\n\t\t\n\t\tpublic ButtonPostClickWorkaround_(wnd c) {\n\t\t\t//this func is fast, but slower is elm.wndcontainer (to get c) and JIT\n\t\t\t_w = default;\n\t\t\tvar w = c.Window; //not c.DirectParent. Eg in taskdialog it is not #32770. The postclick/invoke worked without this workaround in all tested top-level non-#32770 windows, with or without an intermediate #32770.\n\t\t\tif (w != c && !w.IsActive && w.ClassNameIs(\"#32770\") && c.CommonControlType == WControlType.Button) {\n\t\t\t\tw.Post(Api.WM_ACTIVATE, 1);\n\t\t\t\t_w = w;\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic void Dispose() {\n\t\t\tif (!_w.Is0) _w.Post(Api.WM_ACTIVATE);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au/Input/mouse_types.cs",
    "content": "namespace Au.Types;\n\n/// <summary>\n/// <i>button</i> parameter type for <see cref=\"mouse.clickEx(MButton, bool)\"/> and similar functions.\n/// </summary>\n/// <remarks>\n/// There are two groups of values:\n/// 1. Button (<c>Left</c>, <c>Right</c>, <c>Middle</c>, <c>X1</c>, <c>X2</c>). Default or 0: <c>Left</c>.\n/// 2. Action (<c>Down</c>, <c>Up</c>, <c>DoubleClick</c>). Default: click.\n/// \n/// Multiple values from the same group cannot be combined. For example <c>Left|Right</c> is invalid.\n/// Values from different groups can be combined. For example <c>Right|Down</c>.\n/// </remarks>\n[Flags]\npublic enum MButton {\n\t/// <summary>The left button.</summary>\n\tLeft = 1,\n\t\n\t/// <summary>The right button.</summary>\n\tRight = 2,\n\t\n\t/// <summary>The middle button.</summary>\n\tMiddle = 4,\n\t\n\t/// <summary>The 4-th button.</summary>\n\tX1 = 8,\n\t\n\t/// <summary>The 5-th button.</summary>\n\tX2 = 16,\n\t\n\t//rejected: not necessary. Can be confusing.\n\t///// <summary>\n\t///// Click (press and release).\n\t///// This is default. Value 0.\n\t///// </summary>\n\t//Click = 0,\n\t\n\t/// <summary>(flag) Press and don't release.</summary>\n\tDown = 32,\n\t\n\t/// <summary>(flag) Don't press, only release.</summary>\n\tUp = 64,\n\t\n\t/// <summary>(flag) Double-click.</summary>\n\tDoubleClick = 128,\n}\n\n/// <summary>\n/// Flags for mouse buttons.\n/// Used with functions that check mouse button states (pressed or not).\n/// </summary>\n/// <remarks>\n/// The values are the same as <see cref=\"System.Windows.Forms.MouseButtons\"/>, therefore can be cast to/from.\n/// </remarks>\n[Flags]\npublic enum MButtons {\n\t/// <summary>The left button.</summary>\n\tLeft = 0x00100000,\n\t\n\t/// <summary>The right button.</summary>\n\tRight = 0x00200000,\n\t\n\t/// <summary>The middle button.</summary>\n\tMiddle = 0x00400000,\n\t\n\t/// <summary>The 4-th button.</summary>\n\tX1 = 0x00800000,\n\t\n\t/// <summary>The 5-th button.</summary>\n\tX2 = 0x01000000,\n}\n\n/// <summary>\n/// At the end of <c>using(...) { ... }</c> block releases mouse buttons pressed by the function that returned this variable. See example.\n/// </summary>\n/// <example>\n/// Drag and drop: start at x=8 y=8, move 20 pixels down, drop.\n/// <code><![CDATA[\n/// using(mouse.leftDown(w, 8, 8)) mouse.moveBy(0, 20); //the button is auto-released when the 'using' code block ends\n/// ]]></code>\n/// </example>\npublic struct MRelease : IDisposable {\n\tMButton _buttons;\n\t///\n\tpublic static implicit operator MRelease(MButton b) => new MRelease() { _buttons = b };\n\t\n\t/// <summary>\n\t/// Releases mouse buttons pressed by the function that returned this variable.\n\t/// </summary>\n\tpublic void Dispose() {\n\t\tif (0 == (_buttons & MButton.Down)) return;\n\t\tif (0 != (_buttons & MButton.Left)) mouse.clickEx(MButton.Left | MButton.Up, true);\n\t\tif (0 != (_buttons & MButton.Right)) mouse.clickEx(MButton.Right | MButton.Up, true);\n\t\tif (0 != (_buttons & MButton.Middle)) mouse.clickEx(MButton.Middle | MButton.Up, true);\n\t\tif (0 != (_buttons & MButton.X1)) mouse.clickEx(MButton.X1 | MButton.Up, true);\n\t\tif (0 != (_buttons & MButton.X2)) mouse.clickEx(MButton.X2 | MButton.Up, true);\n\t}\n}\n\n/// <summary>\n/// Standard cursor ids.\n/// Used with <see cref=\"mouse.waitForCursor(Seconds, MCursor, bool)\"/>.\n/// </summary>\npublic enum MCursor {\n\t/// <summary>Standard arrow.</summary>\n\tArrow = 32512,\n\t\n\t/// <summary>I-beam (text editing).</summary>\n\tIBeam = 32513,\n\t\n\t/// <summary>Hourglass.</summary>\n\tWait = 32514,\n\t\n\t/// <summary>Crosshair.</summary>\n\tCross = 32515,\n\t\n\t/// <summary>Vertical arrow.</summary>\n\tUpArrow = 32516,\n\t\n\t/// <summary>Double-pointed arrow pointing northwest and southeast.</summary>\n\tSizeNWSE = 32642,\n\t\n\t/// <summary>Double-pointed arrow pointing northeast and southwest.</summary>\n\tSizeNESW = 32643,\n\t\n\t/// <summary>Double-pointed arrow pointing west and east.</summary>\n\tSizeWE = 32644,\n\t\n\t/// <summary>Double-pointed arrow pointing north and south.</summary>\n\tSizeNS = 32645,\n\t\n\t/// <summary>Four-pointed arrow pointing north, south, east, and west.</summary>\n\tSizeAll = 32646,\n\t\n\t/// <summary>Slashed circle.</summary>\n\tNo = 32648,\n\t\n\t/// <summary>Hand.</summary>\n\tHand = 32649,\n\t\n\t/// <summary>Standard arrow and small hourglass.</summary>\n\tAppStarting = 32650,\n\t\n\t/// <summary>Arrow and question mark.</summary>\n\tHelp = 32651,\n}\n\n/// <summary>\n/// This type is used for parameters of <see cref=\"mouse\"/> functions that accept multiple types of UI objects (window, UI element, screen, etc).\n/// </summary>\n/// <remarks>\n/// Has implicit conversions from <see cref=\"wnd\"/>, <see cref=\"elm\"/>, <see cref=\"uiimage\"/>, <see cref=\"screen\"/>, <see cref=\"RECT\"/> and <c>bool</c> (relative coordinates).\n/// Also has static functions to specify more parameters.\n/// </remarks>\npublic struct MObject {\n\tobject _o;\n\tMObject(object o) => _o = o;\n\t\n\t///\n\tpublic object Value => _o;\n\t\n\t/// <summary>\n\t/// Allows to specify coordinates in the client area of a window or control.\n\t/// </summary>\n\t/// <exception cref=\"AuWndException\">The window handle is 0.</exception>\n\t/// <seealso cref=\"Window(wnd, bool)\"/>\n\tpublic static implicit operator MObject(wnd w) { w.ThrowIf0(); return new(w); }\n\t\n\t/// <summary>\n\t/// Allows to specify coordinates in the rectangle of a UI element.\n\t/// </summary>\n\t/// <exception cref=\"ArgumentNullException\"/>\n\tpublic static implicit operator MObject(elm e) => new(Not_.NullRet(e));\n\t\n\t/// <summary>\n\t/// Allows to specify coordinates in the rectangle of an image found in a window etc.\n\t/// </summary>\n\t/// <exception cref=\"ArgumentNullException\"/>\n\tpublic static implicit operator MObject(uiimage i) => new(Not_.NullRet(i));\n\t\n\t/// <summary>\n\t/// Allows to specify coordinates in a rectangle anywhere on screen.\n\t/// </summary>\n\t/// <seealso cref=\"RectInWindow(wnd, RECT)\"/>\n\tpublic static implicit operator MObject(RECT r) => new(r);\n\t\n\t/// <summary>\n\t/// Allows to specify coordinates in a screen.\n\t/// </summary>\n\t/// <seealso cref=\"Screen(screen, bool)\"/>\n\tpublic static implicit operator MObject(screen s) => new((s, false));\n\t\n\t/// <summary>\n\t/// Allows to specify coordinates relative to <see cref=\"mouse.xy\"/> or <see cref=\"mouse.lastXY\"/>.\n\t/// </summary>\n\tpublic static implicit operator MObject(bool useLastXY) => new(useLastXY);\n\t\n\t/// <summary>\n\t/// Allows to specify coordinates in a screen, either in the work area or in entire rectangle.\n\t/// </summary>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// mouse.move(MObject.Screen(screen.primary, true), ^10, ^10); //near the bottom-right corner of the work area of the primary screen\n\t/// ]]></code>\n\t/// </example>\n\tpublic static MObject Screen(screen s, bool workArea) => new((s, workArea));\n\t\n\t/// <summary>\n\t/// Allows to specify coordinates in a window or control, either in the client area or in entire rectangle.\n\t/// </summary>\n\t/// <exception cref=\"AuWndException\">The window handle is 0.</exception>\n\tpublic static MObject Window(wnd w, bool nonClient) { w.ThrowIf0(); return new((w, true)); }\n\t\n\t/// <summary>\n\t/// Allows to specify coordinates in a rectangle in the client area of a window or control.\n\t/// </summary>\n\t/// <exception cref=\"AuWndException\">The window handle is 0.</exception>\n\tpublic static MObject RectInWindow(wnd w, RECT r) { w.ThrowIf0(); return new((w, r)); }\n}\n"
  },
  {
    "path": "Au/Internal/ActCtx_.cs",
    "content": "﻿namespace Au.More;\n\n/// <summary>\n/// Activates our manifest which tells to use <c>comctl32.dll</c> version 6.\n/// The manifest is embedded in this dll, resource id 2.\n/// </summary>\ninternal sealed class ActCtx_ : IDisposable {\n\tIntPtr _cookie;\n\t\n\tpublic static ActCtx_ Activate() {\n\t\tif (s_actCtx.Handle == default || !Api.ActivateActCtx(s_actCtx.Handle, out var cookie)) return default;\n\t\treturn new ActCtx_() { _cookie = cookie };\n\t}\n\t\n\tpublic void Dispose() {\n\t\tif (_cookie != default) {\n\t\t\tApi.DeactivateActCtx(0, _cookie);\n\t\t\t_cookie = default;\n\t\t}\n\t}\n\t\n\tstatic _ActCtx s_actCtx = new _ActCtx();\n\t\n\tclass _ActCtx {\n\t\tpublic IntPtr Handle;\n\t\t\n\t\tpublic _ActCtx() {\n\t\t\tApi.ACTCTX a = default;\n\t\t\ta.cbSize = Api.SizeOf<Api.ACTCTX>();\n#if true //the manifest is in AuCpp.dll\n\t\t\ta.dwFlags = Api.ACTCTX_FLAG_RESOURCE_NAME_VALID | Api.ACTCTX_FLAG_HMODULE_VALID;\n\t\t\ta.hModule = Cpp.Cpp_ModuleHandle();\n#else //old code. The manifest was in Au.dll. If <PublishSingleFile>, Location returns \"\".\n\t\t\t\ta.dwFlags = Api.ACTCTX_FLAG_RESOURCE_NAME_VALID;\n\t\t\t\ta.lpSource = Assembly.GetExecutingAssembly().Location;\n#endif\n\t\t\ta.lpResourceName = (IntPtr)2;\n\t\t\t\n\t\t\tvar h = Api.CreateActCtx(a);\n\t\t\tif (h != (IntPtr)(-1)) Handle = h;\n\t\t}\n\t\t\n\t\t~_ActCtx() {\n\t\t\tif (Handle != default) {\n\t\t\t\tApi.ReleaseActCtx(Handle);\n\t\t\t\tHandle = default;\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au/Internal/ArrayBuilder_.cs",
    "content": "//CONSIDER: System.Buffers.ArrayPool<T>.\n\nnamespace Au.More;\n\n/// <summary>\n/// Like <c>List</c> or <c>StringBuilder</c>, used as a temporary variable-size array to create final fixed-size array.\n/// To avoid much garbage (and many reallocations when growing), uses native memory heap. See <see cref=\"MemoryUtil\"/>.\n/// Must be explicitly disposed to free the native memory. Does not have a finalizer because is struct (to avoid garbage).\n/// Does not support reference types. Does not call <c>T.Dispose</c>.\n/// </summary>\n//[DebuggerStepThrough]\ninternal unsafe struct ArrayBuilder_<T> : IDisposable where T : unmanaged {\n\tT* _p;\n\tint _len, _cap;\n\t\n\tstatic int s_minCap;\n\t\n\tstatic ArrayBuilder_() {\n\t\tvar r = 16384 / sizeof(T); //above 16384 the memory allocation API become >=2 times slower\n\t\tif (r > 500) r = 500; else if (r < 8) r = 8;\n\t\ts_minCap = r;\n\t\t\n\t\t//info: 500 is optimal for getting all top-level windows (and invisible) as ArrayBuilder_<wnd>.\n\t\t//\tNormally there are 200-400 windows on my PC, rarely > 500.\n\t}\n\t\n\tpublic void Dispose() => Free();\n\t\n\t/// <summary>\n\t/// Gets array memory address (address of element 0).\n\t/// </summary>\n\tpublic T* Ptr => _p;\n\t\n\t/// <summary>\n\t/// Gets the number of elements.\n\t/// </summary>\n\tpublic int Count => _len;\n\t\n\t/// <summary>\n\t/// Gets the number of bytes in the array (<c>Count*sizeof(T)</c>).\n\t/// </summary>\n\tpublic int ByteCount => _len * sizeof(T);\n\t\n\t/// <summary>\n\t/// Gets or sets the total number of elements (not bytes) the internal memory can hold without resizing.\n\t/// </summary>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">(the <c>set</c> function) value less than <c>Count</c>. Instead use <c>ReAlloc</c> or <c>Free</c>.</exception>\n\tpublic int Capacity {\n\t\tget => _cap;\n\t\tset {\n\t\t\tif (value != _cap) {\n\t\t\t\tif (value < _len) throw new ArgumentOutOfRangeException();\n\t\t\t\tMemoryUtil.ReAlloc(ref _p, value);\n\t\t\t\t_cap = value;\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Allocates count elements. Sets <c>Count=count</c>.\n\t/// Frees previously allocated memory.\n\t/// Returns memory address (address of element 0).\n\t/// </summary>\n\t/// <param name=\"count\">Element count.</param>\n\t/// <param name=\"zeroInit\">Set all bytes = 0. If <c>false</c>, the memory is uninitialized, ie random byte values. Default <c>true</c>. Slower when <c>true</c>.</param>\n\t/// <param name=\"noExtra\">Set <c>Capacity</c> = count. If <c>false</c>, allocates more if count is less than the minimal capacity for this type.</param>\n\tpublic T* Alloc(int count, bool zeroInit = true, bool noExtra = false) {\n\t\tif (_cap != 0) Free();\n\t\tint cap = count; if (cap < s_minCap && !noExtra) cap = s_minCap;\n\t\t_p = MemoryUtil.Alloc<T>(cap, zeroInit);\n\t\t_cap = cap; _len = count;\n\t\treturn _p;\n\t}\n\t\n\t/// <summary>\n\t/// Adds or removes elements at the end. Sets <c>Count=count</c>.\n\t/// Preserves <c>Math.Min(Count, count)</c> existing elements.\n\t/// Returns memory address (address of element 0).\n\t/// </summary>\n\t/// <param name=\"count\">New element count.</param>\n\t/// <param name=\"zeroInit\">Set all added bytes = 0. If <c>false</c>, the added memory is uninitialized, ie random byte values. Default <c>true</c>. Slower when <c>true</c>.</param>\n\t/// <param name=\"noExtra\">Set <c>Capacity = count</c>. If <c>false</c>, allocates more if count is less than the minimal capacity for this type.</param>\n\t/// <remarks>\n\t/// The new memory usually is at a new location. The preserved elements are copied there.\n\t/// Sets <c>Count=count</c>. To allocate more memory without changing <c>Count</c>, change <c>Capacity</c> instead.\n\t/// </remarks>\n\tpublic T* ReAlloc(int count, bool zeroInit = true, bool noExtra = false) {\n\t\tint cap = count; if (cap < s_minCap && !noExtra) cap = s_minCap;\n\t\tMemoryUtil.ReAlloc(ref _p, cap, zeroInit);\n\t\t_cap = cap; _len = count;\n\t\treturn _p;\n\t}\n\t\n\t/// <summary>\n\t/// Frees memory. Sets <c>Count</c> and <c>Capacity</c> = 0.\n\t/// </summary>\n\tpublic void Free() {\n\t\tif (_cap == 0) return;\n\t\t_len = _cap = 0;\n\t\tvar p = _p; _p = null;\n\t\tMemoryUtil.Free(p);\n\t}\n\t\n\t/// <summary>\n\t/// Adds one element.\n\t/// The same as <c>Add</c>, but uses <c>in</c>. Use to avoid copying values of big types.\n\t/// </summary>\n\tpublic void AddR(in T value) {\n\t\tif (_len == _cap) _EnsureCapacity();\n\t\t_p[_len++] = value;\n\t}\n\t\n\t/// <summary>\n\t/// Adds one element.\n\t/// </summary>\n\tpublic void Add(T value) {\n\t\tif (_len == _cap) _EnsureCapacity();\n\t\t_p[_len++] = value;\n\t}\n\t\n\t/// <summary>\n\t/// Adds one zero-inited element and returns its reference.\n\t/// </summary>\n\tpublic ref T Add() {\n\t\tif (_len == _cap) _EnsureCapacity();\n\t\tref T r = ref _p[_len];\n\t\tr = default;\n\t\t_len++;\n\t\treturn ref r;\n\t}\n\t\n\t/// <summary>\n\t/// <c>Capacity = Math.Max(_cap * 2, s_minCap)</c>.\n\t/// </summary>\n\tvoid _EnsureCapacity() {\n\t\tCapacity = Math.Max(_cap * 2, s_minCap);\n\t}\n\t\n\t/// <summary>\n\t/// Gets element reference.\n\t/// </summary>\n\t/// <param name=\"i\">Element index.</param>\n\t/// <exception cref=\"IndexOutOfRangeException\"></exception>\n\tpublic ref T this[int i] {\n\t\t[MethodImpl(MethodImplOptions.AggressiveInlining)]\n\t\tget {\n\t\t\tif ((uint)i >= (uint)_len) _ThrowBadIndex();\n\t\t\treturn ref _p[i];\n\t\t}\n\t}\n\t\n\t[MethodImpl(MethodImplOptions.NoInlining)]\n\tstatic void _ThrowBadIndex() {\n\t\tthrow new IndexOutOfRangeException();\n\t}\n\t\n\t/// <summary>\n\t/// Copies elements to a new managed array.\n\t/// </summary>\n\tpublic T[] ToArray() {\n\t\tif (_len == 0) return [];\n\t\tvar r = new T[_len];\n\t\tfor (int i = 0; i < r.Length; i++) r[i] = _p[i];\n\t\treturn r;\n\t}\n}\n"
  },
  {
    "path": "Au/Internal/AssemblyUtil_.cs",
    "content": "using System.Runtime.Loader;\n\nnamespace Au.More;\n\n/// <summary>\n/// Assembly functions.\n/// </summary>\ninternal static class AssemblyUtil_ {\n\tpublic static Assembly GetEntryAssembly() {\n\t\tvar r = Assembly.GetEntryAssembly();\n\t\tif (r == null) {\n\t\t\t//info: null in miniProgram on AssemblyLoadContext.Default.Resolving event when the script is like:\n\t\t\t//\t'class Program : SomeType { static Main() { } }' where SomeType is the assembly being resolved.\n\n\t\t\t//Debug_.Print(\"Assembly.GetEntryAssembly null\");\n\t\t\tif (script.role == SRole.MiniProgram) {\n\t\t\t\tvar s = \"~\" + script.name;\n\t\t\t\tforeach (var v in AssemblyLoadContext.Default.Assemblies) if (v.GetName().Name == s) return v;\n\t\t\t}\n\t\t}\n\t\treturn r;\n\t}\n\n\t/// <summary>\n\t/// Returns <c>true</c> if the build configuration of the assembly is Debug. Returns <c>false</c> if Release (optimized).\n\t/// </summary>\n\t/// <remarks>\n\t/// Returns <c>true</c> if the assembly has <see cref=\"DebuggableAttribute\"/> and its <c>IsJITTrackingEnabled</c> is <c>true</c>.\n\t/// </remarks>\n\tpublic static bool IsDebug(Assembly a) => a?.GetCustomAttribute<DebuggableAttribute>()?.IsJITTrackingEnabled ?? false;\n\t//IsJITTrackingEnabled depends on config, but not 100% reliable, eg may be changed explicitly in source code (maybe IsJITOptimizerDisabled too).\n\t//IsJITOptimizerDisabled depends on 'Optimize code' checkbox in project Properties, regardless of config.\n\t//note: GetEntryAssembly returns null in func called by host through coreclr_create_delegate.\n\n\t/// <summary>\n\t/// Returns flags for loaded assemblies: 1 <c>System.Windows.Forms</c>, 2 <c>WindowsBase</c> (WPF).\n\t/// </summary>\n\tinternal static int IsLoadedWinformsWpf() {\n\t\tif (s_isLoadedWinformsWpf == 0) {\n\t\t\tlock (\"zjm5R47f7UOmgyHUVZaf1w\") {\n\t\t\t\tif (s_isLoadedWinformsWpf == 0) {\n\t\t\t\t\tvar ad = AppDomain.CurrentDomain;\n\t\t\t\t\tvar a = ad.GetAssemblies();\n\t\t\t\t\tforeach (var v in a) {\n\t\t\t\t\t\t_FlagFromName(v);\n\t\t\t\t\t\tif (s_isLoadedWinformsWpf == 3) return 3;\n\t\t\t\t\t}\n\t\t\t\t\tad.AssemblyLoad += (_, x) => _FlagFromName(x.LoadedAssembly);\n\t\t\t\t\ts_isLoadedWinformsWpf |= 0x100;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn s_isLoadedWinformsWpf & 3;\n\n\t\tvoid _FlagFromName(Assembly a) {\n\t\t\tstring s = a.FullName; //fast, cached. GetName can be slow because not cached.\n\t\t\tif (0 == (s_isLoadedWinformsWpf & 1) && s.Starts(\"System.Windows.Forms,\")) s_isLoadedWinformsWpf |= 1;\n\t\t\telse if (0 == (s_isLoadedWinformsWpf & 2) && s.Starts(\"WindowsBase,\")) s_isLoadedWinformsWpf |= 2;\n\t\t}\n\t}\n\tstatic volatile int s_isLoadedWinformsWpf;\n}\n"
  },
  {
    "path": "Au/Internal/AttachThreadInput_.cs",
    "content": "namespace Au.More;\n\n/// <summary>\n/// Calls API <ms>AttachThreadInput</ms> to attach/detach thread input.\n/// Constructor attaches thread input of this thread to that of the specified thread. <c>Dispose</c> detaches.\n/// </summary>\ninternal struct AttachThreadInput_ : IDisposable {\n\tint _tidThis, _tidAttach;\n\t\n\t/// <summary>\n\t/// Attaches thread input of this thread to that of the specified thread.\n\t/// </summary>\n\tpublic AttachThreadInput_(int idThreadAttachTo, out bool succeeded) {\n\t\t_tidThis = Api.GetCurrentThreadId();\n\t\tsucceeded = Api.AttachThreadInput(_tidThis, idThreadAttachTo, true);\n\t\t_tidAttach = succeeded ? idThreadAttachTo : 0;\n\t}\n\t\n\t/// <summary>\n\t/// Detaches thread input.\n\t/// </summary>\n\tpublic void Dispose() {\n\t\tif (_tidAttach != 0) {\n\t\t\tApi.AttachThreadInput(_tidThis, _tidAttach, false);\n\t\t\t_tidAttach = 0;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if <c>AttachThreadInput</c> succeeded and this variable is not disposed.\n\t/// </summary>\n\tpublic bool IsAttached => _tidAttach != 0;\n}\n"
  },
  {
    "path": "Au/Internal/Debug_.cs",
    "content": "namespace Au.More;\n\n/// <summary>\n/// Functions useful to debug code.\n/// </summary>\n/// <remarks>\n/// The <c>Debug_.PrintX</c> functions write to the same output as <see cref=\"print.it\"/>, not to the trace listeners like <see cref=\"Debug.Print(string)\"/> etc do. Also they add caller's name, file and line number.\n/// Functions <c>Print</c>, <c>PrintIf</c>, <c>PrintFunc</c> and <c>Dialog</c> work only if <c>DEBUG</c> is defined, which normally is when the caller project is in Debug configuration. Else they are not called, and arguments not evaluated at run time. This is because they have <c>[ConditionalAttribute(\"DEBUG\")]</c>.\n/// Note: when used in a library, the above functions depend on <c>DEBUG</c> of the library project and not on <c>DEBUG</c> of the consumer project of the library. For example, the library may be in Release configuration even if its consumer project is in Debug configuration. If your library wants to show some info only if its consumer project is in Debug config, instead you can use code like <c>if(opt.warnings.Verbose) print.warning(\"text\");</c>; see <see cref=\"print.warning\"/>, <see cref=\"OWarnings.Verbose\"/>.\n/// </remarks>\ninternal static class Debug_ {\n\tstatic void _Print(object text, string f_, int l_, string m_) {\n\t\tstring s = print.util.toString(text), fname = pathname.getName(f_), st;\n\t\tint i = s.Find(\"\\r\\n   at \");\n\t\tif (i >= 0) { st = s[(i + 2)..]; s = s[..i]; } else st = new StackTrace(2, true).ToString();\n\t\ts = $\"<>Debug: {m_} ({fname}:{l_}):  {s} <fold>\\r\\n{st}</fold>\";\n\t\t_Print2(s);\n\t}\n\t\n\tstatic void _Print2(object o) {\n\t\tstring s = o?.ToString();\n\t\tif (UseQM2) print.qm2.write(s); else print.it(s);\n\t}\n\t\n\tinternal static bool UseQM2;\n\t\n\t/// <summary>\n\t/// Calls <see cref=\"print.it\"/> to show some debug info. Also shows current function name/file/line.\n\t/// Works only if <c>DEBUG</c> is defined. Read more in class help.\n\t/// The 3 optional arguments are not used explicitly.\n\t/// Text can contain output tags.\n\t/// </summary>\n\t[Conditional(\"DEBUG\")]\n\tpublic static void Print(object text, [CallerFilePath] string f_ = null, [CallerLineNumber] int l_ = 0, [CallerMemberName] string m_ = null)\n\t\t=> _Print(text, f_, l_, m_);\n\t\n\t/// <summary>\n\t/// If condition is <c>true</c>, calls <see cref=\"print.it\"/> to show some debug info. Also shows current function name/file/line.\n\t/// Works only if <c>DEBUG</c> is defined. Read more in class help.\n\t/// If <i>text</i> <c>null</c>, uses <c>[CallerArgumentExpression(\"condition\")]</c>. Other optional parameters are not used explicitly.\n\t/// If text starts with <c>\"&lt;&gt;\"</c>, it can contain output tags.\n\t/// </summary>\n\t[Conditional(\"DEBUG\")]\n\tpublic static void PrintIf(bool condition, object text = null, [CallerFilePath] string f_ = null, [CallerLineNumber] int l_ = 0, [CallerMemberName] string m_ = null, [CallerArgumentExpression(\"condition\")] string ae_ = null) {\n\t\tif (condition) _Print(text ?? ae_, f_, l_, m_);\n\t}\n\t\n\t/// <summary>\n\t/// Calls <see cref=\"print.it\"/> with current function name.\n\t/// Works only if <c>DEBUG</c> is defined. Read more in class help.\n\t/// The optional argument is not used explicitly.\n\t/// </summary>\n\t[Conditional(\"DEBUG\")]\n\tpublic static void PrintFunc([CallerMemberName] string m_ = null)\n\t\t=> _Print2(m_);\n\t\n\t/// <summary>\n\t/// If <c>DEBUG</c> defined, prints <c>lastError.message</c>. Only if condition <c>true</c> (default).\n\t/// </summary>\n\t[Conditional(\"DEBUG\")]\n\tpublic static void PrintNativeError(bool condition = true, [CallerFilePath] string f_ = null, [CallerLineNumber] int l_ = 0, [CallerMemberName] string m_ = null) {\n\t\tif (condition) _Print(lastError.message, f_, l_, m_);\n\t}\n\t\n\t/// <summary>\n\t/// If <c>DEBUG</c> defined, prints <c>lastError.messageFor(code)</c>.\n\t/// </summary>\n\t[Conditional(\"DEBUG\")]\n\tpublic static void PrintNativeError(int code, [CallerFilePath] string f_ = null, [CallerLineNumber] int l_ = 0, [CallerMemberName] string m_ = null)\n\t\t=> _Print(lastError.messageFor(code), f_, l_, m_);\n\t\n\t/// <summary>\n\t/// Calls <see cref=\"dialog.show\"/> to show some debug info.\n\t/// Works only if <c>DEBUG</c> is defined. Read more in class help.\n\t/// The 3 optional arguments are not used explicitly.\n\t/// </summary>\n\t[Conditional(\"DEBUG\")]\n\tpublic static void Dialog(object text, [CallerFilePath] string f_ = null, [CallerLineNumber] int l_ = 0, [CallerMemberName] string m_ = null) {\n\t\tstring s = print.util.toString(text);\n\t\tdialog.show(\"Debug\", s, flags: DFlags.ExpandDown, expandedText: $\"{m_} ({pathname.getName(f_)}:{l_})\");\n\t}\n\t\n\t//rejected: use if(opt.warnings.Verbose) dialog.showWarning(...). It adds stack trace.\n\t///// <summary>\n\t///// If <c>opt.warnings.Verbose</c>, calls <see cref=\"dialog.show\"/> with text and stack trace.\n\t///// Read more in class help.\n\t///// </summary>\n\t//[MethodImpl(MethodImplOptions.NoInlining)]\n\t//public static void DialogOpt(string text)\n\t//{\n\t//\tif(!opt.warnings.Verbose) return;\n\t//\tvar x = new StackTrace(1, true);\n\t//\tdialog.show(\"Debug\", text, flags: DFlags.ExpandDown | DFlags.Wider, expandedText: x.ToString());\n\t//}\n\t\n\t//rejected: Not used in this library. Not useful for debug because don't show the stack trace. Instead use print.warning; it supports prefix \"Debug: \", \"Note: \", \"Info :\"; it also supports disabling warnings etc.\n\t///// <summary>\n\t///// If <c>opt.warnings.Verbose</c>, calls <see cref=\"print.it(string)\"/>.\n\t///// Read more in class help.\n\t///// </summary>\n\t//public static void PrintOpt(string text)\n\t//{\n\t//\tif(opt.warnings.Verbose) _Print(\"Debug: \" + text);\n\t//}\n\t\n\t//rejected: Don't need multiple warning functions. Now print.warning does not show more than 1 warning/second if opt.warnings.Verbose is false.\n\t///// <summary>\n\t///// If <c>opt.warnings.Verbose</c>, calls <see cref=\"print.warning\"/>.\n\t///// Read more in class help.\n\t///// </summary>\n\t//[MethodImpl(MethodImplOptions.NoInlining)]\n\t//public static void WarningOpt(string text)\n\t//{\n\t//\tif(opt.warnings.Verbose) print.warning(text, 1);\n\t//}\n\t\n\t/// <summary>\n\t/// Checks flags and throws <see cref=\"ArgumentException\"/> if some flags are invalid. The error message includes valid flag names.\n\t/// </summary>\n\t/// <param name=\"flags\">Flags to check.</param>\n\t/// <param name=\"goodFlags\">Valid flags.</param>\n\t/// <remarks>\n\t/// Can be used in functions that have an enum flags parameter but not all passed flags are valid for that function or object state.\n\t/// Does nothing if <c>!opt.warnings.Verbose</c>.\n\t/// When flags are valid, this function is fast.\n\t/// </remarks>\n\tpublic static unsafe void CheckFlagsOpt<T>(T flags, T goodFlags) where T : unmanaged, Enum {\n\t\t//FUTURE: if this is really often useful, make it public. If not used - remove.\n\t\t\n\t\tDebug.Assert(sizeof(T) == 4);\n\t\tint a = *(int*)&flags;\n\t\tint b = *(int*)&goodFlags;\n\t\tif (a != (a & b)) _CheckFlagsOpt(typeof(T), b);\n\t}\n\t\n\t[MethodImpl(MethodImplOptions.NoInlining)]\n\tstatic void _CheckFlagsOpt(Type t, int goodFlags) {\n\t\tif (!opt.warnings.Verbose) return;\n\t\tif (!t.IsEnum) throw new ArgumentException(\"Bad type.\");\n\t\tvar s = new StringBuilder(\"Invalid flags. Only these flags can be used: \"); bool added = false;\n\t\tfor (int i = 1; i != 0; i <<= 1) {\n\t\t\tif (0 == (i & goodFlags)) continue;\n\t\t\tif (added) s.Append(\", \"); else added = true;\n\t\t\ts.Append(t.GetEnumName(i));\n\t\t}\n\t\ts.Append('.');\n\t\t//print.warning(s.ToString(), 1);\n\t\tthrow new ArgumentException(s.ToString());\n\t}\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if using Debug configuration of <c>Au.dll</c>.\n\t/// </summary>\n\tpublic static bool IsAuDebugConfiguration {\n\t\tget {\n#if DEBUG\n\t\t\treturn true;\n#else\n\t\t\treturn false;\n#endif\n\t\t}\n\t}\n\t\n\t//CONSIDER: move the MemoryX functions to perf as public.\n\t\n\t/// <summary>\n\t/// Calls <c>Marshal.AddRef(obj)</c>, then calls/returns <c>Marshal.Release(obj)</c>.\n\t/// </summary>\n\tpublic static int GetComObjRefCount(IntPtr obj) {\n\t\tMarshal.AddRef(obj);\n\t\treturn Marshal.Release(obj);\n\t}\n\t\n\t/// <summary>\n\t/// Returns managed memory size as formatted string. Uses <c>GC.GetTotalMemory</c>.\n\t/// </summary>\n\t/// <param name=\"fromAnchor\">Get the difference from previous call to <c>MemorySetAnchor_</c>.</param>\n\tpublic static string MemoryGet(bool fromAnchor = true) {\n\t\tvar mem = GC.GetTotalMemory(false);\n\t\t//if(s_mem0 == 0) s_mem0 = mem;\n\t\tif (fromAnchor) mem -= s_mem0;\n\t\treturn (mem / 1024d / 1024d).ToS(\"F3\");\n\t}\n\tstatic long s_mem0;\n\t\n\t/// <summary>\n\t/// Prints managed memory size. Uses <c>GC.GetTotalMemory</c>.\n\t/// </summary>\n\t/// <param name=\"fromAnchor\">Get the difference from previous call to <c>MemorySetAnchor_</c>.</param>\n\tpublic static void MemoryPrint(bool fromAnchor = true) => _Print2(MemoryGet(fromAnchor));\n\t\n\t/// <summary>\n\t/// Memorizes current managed memory size, so that next call to another <c>MemoryX</c> function with <i>fromAnchor</i>=<c>true</c> (default) will get memory size difference from current memory size.\n\t/// </summary>\n\tpublic static void MemorySetAnchor() { s_mem0 = GC.GetTotalMemory(false); }\n\t\n\t///// <summary>\n\t///// Temporarily suspends GC collections if possible. Restores in <c>Dispose</c>.\n\t///// </summary>\n\t//public struct NoGcRegion : IDisposable {\n\t//\tbool _restore, _print;\n\t\t\n\t//\t/// <summary>\n\t//\t/// Suspends GC collections if possible.\n\t//\t/// Does nothing in 32-bit process.\n\t//\t/// </summary>\n\t//\t/// <param name=\"memSize\">Recommended 100_000_000. Still works if 200_000_000, but fails if 300_000_000. Not tested in 32-bit process.</param>\n\t//\t/// <param name=\"print\">Let <c>Dispose</c> print size of managed memory added since ctor.</param>\n\t//\tpublic NoGcRegion(long memSize, bool print = true) {\n\t//\t\t_restore = false;\n\t//\t\t_print = print;\n\t//\t\tif (osVersion.is32BitProcess) return;\n\t//\t\tif (_print) Debug_.MemorySetAnchor_();\n\t//\t\t//print.it(System.Runtime.GCSettings.LatencyMode);\n\t//\t\ttry { _restore = GC.TryStartNoGCRegion(memSize); }\n\t//\t\tcatch (InvalidOperationException ex) { Debug_.Print(ex); }\n\t//\t}\n\t\t\n\t//\t/// <summary>\n\t//\t/// Restores suspended GC collections.\n\t//\t/// </summary>\n\t//\tpublic void Dispose() {\n\t//\t\tif (_restore) {\n\t//\t\t\t_restore = false;\n\t//\t\t\t//print.it(System.Runtime.GCSettings.LatencyMode == System.Runtime.GCLatencyMode.NoGCRegion);\n\t//\t\t\t//if(System.Runtime.GCSettings.LatencyMode == System.Runtime.GCLatencyMode.NoGCRegion) GC.EndNoGCRegion();\n\t//\t\t\ttry { GC.EndNoGCRegion(); } //note: need to call even if not in nogc region (then exception); else TryStartNoGCRegion will throw exception.\n\t//\t\t\tcatch (InvalidOperationException ex) { Debug_.Print(ex); }\n\t//\t\t\tif (_print) Debug_.MemoryPrint_();\n\t//\t\t\tThreadPool.QueueUserWorkItem(_ => GC.Collect());\n\t//\t\t}\n\t//\t}\n\t//}\n\t\n\t/// <summary>\n\t/// Prints assemblies already loaded or/and loaded in the future.\n\t/// </summary>\n\tpublic static void PrintLoadedAssemblies(bool now, bool future, bool stackTrace = false) {\n\t\tif (now) {\n\t\t\tvar a = AppDomain.CurrentDomain.GetAssemblies();\n\t\t\t_Print2(\"-- now --\");\n\t\t\tforeach (var v in a) _Print2(\"# \" + v.FullName);\n\t\t}\n\t\tif (future) {\n\t\t\tif (stackTrace) new StackTrace(1, true); //load assemblies used by stack trace\n\t\t\t_Print2(\"-- future --\");\n\t\t\tAppDomain.CurrentDomain.AssemblyLoad += (object sender, AssemblyLoadEventArgs e) => {\n\t\t\t\t_Print2(\"# \" + e.LoadedAssembly.FullName);\n\t\t\t\tif (stackTrace) _Print2(new StackTrace(1, true));\n\t\t\t};\n\t\t\t//var stack = new Stack<string>();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au/Internal/GC_.cs",
    "content": "﻿namespace Au.More {\n\t/// <summary>\n\t/// <see cref=\"GC\"/> extensions.\n\t/// </summary>\n\tstatic class GC_ {\n\t\tstatic readonly ConditionalWeakTable<object, _Remover> s_table = new();\n\n\t\t/// <summary>\n\t\t/// Calls <see cref=\"GC.AddMemoryPressure\"/>. Later, when object <i>obj</i> is garbage-collected, will call <see cref=\"GC.RemoveMemoryPressure\"/>.\n\t\t/// </summary>\n\t\t/// <param name=\"obj\">An object of any type.</param>\n\t\t/// <param name=\"size\">Unmanaged memory size. It is passed to <c>GC.AddMemoryPressure</c> and <c>GC.RemoveMemoryPressure</c>.</param>\n\t\tpublic static void AddObjectMemoryPressure(object obj, long size) {\n\t\t\tGC.AddMemoryPressure(size);\n\t\t\ts_table.Add(obj, new _Remover(size));\n\t\t}\n\n\t\tclass _Remover {\n\t\t\treadonly long _size;\n\n\t\t\tpublic _Remover(long size) {\n\t\t\t\t_size = size;\n\t\t\t}\n\n\t\t\t~_Remover() {\n\t\t\t\t//print.it(\"removed \" + _size);\n\t\t\t\tGC.RemoveMemoryPressure(_size);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Provides a static <see cref=\"HandleCollector\"/> for USER handles, created with this code: <c>new(\"Au.User\", 300, 3000);</c>.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// OS refuses to create more USER objects than the process quota. Normally it is 10000, but can be min 200. It is set in registry.\n\t\t/// </remarks>\n\t\tpublic static HandleCollector UserHandleCollector { get; } = new(\"Au.User\", 300, 3000);\n\n\t\t/// <summary>\n\t\t/// Provides a static <see cref=\"HandleCollector\"/> for GDI handles, created with this code: <c>new(\"Au.GDI\", 300, 3000);</c>.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// OS refuses to create more GDI objects than the process quota. Normally it is 10000, but can be min 200. It is set in registry.\n\t\t/// </remarks>\n\t\tpublic static HandleCollector GdiHandleCollector { get; } = new(\"Au.GDI\", 300, 3000);\n\n\t\t//The above HandleCollectors are used by icon.\n\t\t//Tested, works well. But if threshold 100 or 200, GC become too frequent sometimes.\n\t\t//Default limit for USER and GDI objects is 10000, min 200.\n\t\t//Alternatively could use GC.AddMemoryPressure/GC.RemoveMemoryPressure.\n\t\t//\tIf we add 40000 bytes/icon and GC starts working when pressure is 4 MB, then the number of icons is ~100 and GDI handles ~300.\n\t\t//\tProblem: the GC threshold for unmanaged memory pressure is unknown, undocumented and changing.\n\t\t//\t\tTest results: used to be ~100 KB (why so small?), but now in .NET5 4MB (3MB in 32-bit process).\n\t\t//\t\tFor managed memory it is 2 MB.\n\t\t//\t\tCould instead allocate managed array of this size and keep in a field.\n\t\t//\t\t\tInfo: System.Drawing.Icon objects loaded from file or stream keep icon memory in private readonly byte[]? _iconData.\n\t\t//\t\t\t\tThat is why it triggers GC quite soon. But eg Clone doesn't clone that memory and it can be dangerous.\n\t\t//Don't care about icon memory size.\n\t}\n}\n"
  },
  {
    "path": "Au/Internal/GDI misc.cs",
    "content": "namespace Au.More;\n\n/// <summary>\n/// Helps to get and release screen DC with <c>using</c>.\n/// Uses API GetDC and ReleaseDC.\n/// </summary>\nstruct ScreenDC_ : IDisposable {\n\tIntPtr _dc;\n\n\tpublic ScreenDC_() => _dc = Api.GetDC(default);\n\n\tpublic static implicit operator IntPtr(ScreenDC_ dc) => dc._dc;\n\n\tpublic void Dispose() {\n\t\tif (_dc != default) {\n\t\t\tApi.ReleaseDC(default, _dc);\n\t\t\t_dc = default;\n\t\t}\n\t}\n}\n\n/// <summary>\n/// Helps to get and release window DC with <c>using</c>.\n/// Uses API GetDC and ReleaseDC.\n/// </summary>\nstruct WindowDC_ : IDisposable {\n\tIntPtr _dc;\n\twnd _w;\n\n\tpublic WindowDC_(IntPtr dc, wnd w) { _dc = dc; _w = w; }\n\n\tpublic WindowDC_(wnd w) => _dc = Api.GetDC(_w = w);\n\n\tpublic static implicit operator IntPtr(WindowDC_ dc) => dc._dc;\n\n\tpublic bool Is0 => _dc == default;\n\n\tpublic void Dispose() {\n\t\tif (_dc != default) {\n\t\t\tApi.ReleaseDC(_w, _dc);\n\t\t\t_dc = default;\n\t\t}\n\t}\n}\n\n/// <summary>\n/// Helps to create and delete compatible DC (memory DC) with <c>using</c>.\n/// Uses API CreateCompatibleDC and DeleteDC.\n/// </summary>\nclass MemoryDC_ : IDisposable {\n\tprotected IntPtr _dc;\n\n\t/// <summary>\n\t/// Creates memory DC compatible with screen.\n\t/// </summary>\n\tpublic MemoryDC_() : this(default) { }\n\n\tpublic MemoryDC_(IntPtr dc) => _dc = Api.CreateCompatibleDC(dc);\n\n\tpublic static implicit operator IntPtr(MemoryDC_ dc) => dc._dc;\n\n\tpublic bool Is0 => _dc == default;\n\n\tpublic void Dispose() => Dispose(true);\n\n\tprotected virtual void Dispose(bool disposing) {\n\t\tif (_dc != default) {\n\t\t\tApi.DeleteDC(_dc);\n\t\t\t_dc = default;\n\t\t}\n\t}\n}\n\n/// <summary>\n/// Memory DC with selected font.\n/// Can be used for font measurement.\n/// </summary>\nsealed class FontDC_ : MemoryDC_ {\n\tIntPtr _oldFont;\n\n\t/// <summary>\n\t/// Selects specified font.\n\t/// The <c>Dispose</c> method will select it out but will not destroy it.\n\t/// </summary>\n\t/// <param name=\"font\"></param>\n\tpublic FontDC_(IntPtr font) {\n\t\t_oldFont = Api.SelectObject(_dc, font);\n\t}\n\n\t/// <summary>\n\t/// Selects standard UI font for specified DPI.\n\t/// </summary>\n\t/// <param name=\"dpi\"></param>\n\tpublic FontDC_(DpiOf dpi) : this(NativeFont_.RegularCached(dpi)) { }\n\n\tprotected override void Dispose(bool disposing) {\n\t\tif (_oldFont != default) {\n\t\t\tApi.SelectObject(_dc, _oldFont);\n\t\t\t_oldFont = default;\n\t\t}\n\t\tbase.Dispose(disposing);\n\t}\n\n\t/// <summary>\n\t/// Measures text with API <ms>GetTextExtentPoint32</ms>.\n\t/// Should be single line without tabs. For drawing with API <ms>TextOut</ms> or <ms>ExtTextOut</ms>.\n\t/// </summary>\n\tpublic SIZE MeasureEP(string s) {\n\t\tApi.GetTextExtentPoint32(_dc, s, s.Length, out var z);\n\t\treturn z;\n\t}\n\n\t/// <summary>\n\t/// Measures text with API <ms>DrawTextEx</ms>.\n\t/// Can be multiline. For drawing with API <ms>DrawTextEx</ms>.\n\t/// </summary>\n\tpublic SIZE MeasureDT(RStr s, TFFlags format, int wrapWidth = 0) {\n\t\tif (s.Length == 0) return default;\n\t\tRECT r = new(0, 0, wrapWidth, 0);\n\t\tApi.DrawText(_dc, s, ref r, format | TFFlags.CALCRECT);\n\t\treturn new(r.Width + 1, r.Height);\n\t\t//When drawing, may cut 1 pixel at the right, eg if text ends with T. Workaround: now add 1.\n\t}\n}\n\nstruct GdiObject_ : IDisposable {\n\tIntPtr _h;\n\n\tpublic IntPtr Handle => _h;\n\n\tpublic GdiObject_(IntPtr handle) {\n\t\t_h = handle;\n\t}\n\n\tpublic void Dispose() {\n\t\tApi.DeleteObject(_h);\n\t\t_h = default;\n\t}\n\n\tpublic static implicit operator IntPtr(GdiObject_ g) => g._h;\n\n\t/// <summary>\n\t/// Calls API <c>CreateSolidBrush</c>.\n\t/// </summary>\n\t/// <param name=\"color\"><c>0xRRGGBB</c>.</param>\n\tpublic static GdiObject_ ColorBrush(ColorInt color) {\n\t\treturn new(Api.CreateSolidBrush(color.ToBGR()));\n\t}\n\n\t/// <summary>\n\t/// Calls API <c>GetThemeSysColorBrush</c>.\n\t/// </summary>\n\t/// <param name=\"hTheme\">Theme handle. If default, gets non-themed color.</param>\n\t/// <param name=\"colorIndex\">API <c>COLOR_x</c>.</param>\n\tpublic static GdiObject_ SysColorBrush(IntPtr hTheme, int colorIndex) {\n\t\treturn new(Api.GetThemeSysColorBrush(hTheme, colorIndex));\n\t}\n\n\tpublic void BrushFill(IntPtr dc, RECT r) {\n\t\tApi.FillRect(dc, r, _h);\n\t}\n\n\tpublic void BrushRect(IntPtr dc, RECT r) {\n\t\tApi.FrameRect(dc, r, _h);\n\t}\n}\n\nstruct GdiPen_ : IDisposable {\n\tIntPtr _pen;\n\n\tpublic IntPtr Handle => _pen;\n\n\tpublic GdiPen_(int color, int width = 1, int style = 0) {\n\t\t_pen = Api.CreatePen(style, width, color);\n\t}\n\n\tpublic void Dispose() {\n\t\tApi.DeleteObject(_pen);\n\t\t_pen = default;\n\t}\n\n\t/// <summary>\n\t/// Draws line and returns previous \"current position\".\n\t/// Don't need to select pen into DC.\n\t/// </summary>\n\tpublic POINT DrawLine(IntPtr dc, POINT start, POINT end) {\n\t\tvar old = Api.SelectObject(dc, _pen); //fast\n\t\tApi.MoveToEx(dc, start.x, start.y, out var p); //fast\n\t\tApi.LineTo(dc, end.x, end.y);\n\t\tApi.SelectObject(dc, old); //fast\n\t\treturn p;\n\t}\n}\n\nstruct GdiSelectObject_ : IDisposable {\n\tIntPtr _dc, _old;\n\n\tpublic GdiSelectObject_(IntPtr dc, IntPtr obj) {\n\t\t_old = Api.SelectObject(_dc = dc, obj);\n\t}\n\n\tpublic void Dispose() {\n\t\tApi.SelectObject(_dc, _old);\n\t\t_dc = default;\n\t}\n}\n"
  },
  {
    "path": "Au/Internal/Handle_.cs",
    "content": "\nusing Microsoft.Win32.SafeHandles;\n\nnamespace Au.More;\n\n/// <summary>\n/// Manages a kernel handle.\n/// Must be disposed.\n/// Has static functions to open process handle.\n/// </summary>\ninternal struct Handle_ : IDisposable {\n\tIntPtr _h;\n\t\n\t/// <summary>\n\t/// Attaches a kernel handle to this new variable.\n\t/// No exception when handle is invalid.\n\t/// If handle == -1, sets 0.\n\t/// </summary>\n\t/// <param name=\"handle\"></param>\n\tpublic Handle_(nint handle) { _h = handle == -1 ? default : handle; }\n\t\n\t//public static explicit operator Handle_(IntPtr p) => new Handle_(p); //no\n\t\n\tpublic static implicit operator IntPtr(Handle_ p) => p._h;\n\t\n\t/// <summary>\n\t/// <c>_h == default</c>.\n\t/// Info: <c>_h</c> never is -1.\n\t/// </summary>\n\tpublic bool Is0 => _h == default;\n\t\n\t/// <summary>\n\t/// <c>if (!Is0) { Api.CloseHandle(_h); _h = default; }</c>\n\t/// </summary>\n\tpublic void Dispose() {\n\t\tif (!Is0) { Api.CloseHandle(_h); _h = default; }\n\t}\n\t\n\t/// <summary>\n\t/// Opens process handle.\n\t/// Calls API <c>OpenProcess</c>.\n\t/// </summary>\n\t/// <returns>default if failed. Supports <see cref=\"lastError\"/>.</returns>\n\t/// <param name=\"processId\">Process id.</param>\n\t/// <param name=\"desiredAccess\">Desired access (<c>Api.PROCESS_</c>), as in API <c>OpenProcess</c> documentation.</param>\n\tpublic static Handle_ OpenProcess(int processId, uint desiredAccess = Api.PROCESS_QUERY_LIMITED_INFORMATION) {\n\t\tif (processId == 0) { lastError.code = Api.ERROR_INVALID_PARAMETER; return default; }\n\t\treturn _OpenProcess(processId, desiredAccess);\n\t}\n\t\n\t/// <summary>\n\t/// Opens window's process handle.\n\t/// This overload is more powerful: if API <c>OpenProcess</c> fails, it tries API <c>GetProcessHandleFromHwnd</c>, which can open higher integrity level processes, but only if current process is uiAccess and <i>desiredAccess</i> includes only <c>PROCESS_DUP_HANDLE</c>, <c>PROCESS_VM_OPERATION</c>, <c>PROCESS_VM_READ</c>, <c>PROCESS_VM_WRITE</c>, <c>SYNCHRONIZE</c>.\n\t/// </summary>\n\t/// <returns>default if failed. Supports <see cref=\"lastError\"/>.</returns>\n\t/// <param name=\"w\"></param>\n\t/// <param name=\"desiredAccess\">Desired access (<c>Api.PROCESS_</c>), as in API <c>OpenProcess</c> documentation.</param>\n\tpublic static Handle_ OpenProcess(wnd w, uint desiredAccess = Api.PROCESS_QUERY_LIMITED_INFORMATION) {\n\t\tint pid = w.ProcessId; if (pid == 0) return default;\n\t\treturn _OpenProcess(pid, desiredAccess, w);\n\t}\n\t\n\tstatic Handle_ _OpenProcess(int processId, uint desiredAccess = Api.PROCESS_QUERY_LIMITED_INFORMATION, wnd processWindow = default) {\n\t\tHandle_ R = Api.OpenProcess(desiredAccess, false, processId);\n\t\tif (R.Is0 && !processWindow.Is0 && 0 == (desiredAccess & ~(Api.PROCESS_DUP_HANDLE | Api.PROCESS_VM_OPERATION | Api.PROCESS_VM_READ | Api.PROCESS_VM_WRITE | Api.SYNCHRONIZE))) {\n\t\t\tint e = lastError.code;\n\t\t\tif (uacInfo.ofThisProcess.IsUIAccess) R = Api.GetProcessHandleFromHwnd(processWindow);\n\t\t\tif (R.Is0) Api.SetLastError(e);\n\t\t}\n\t\treturn R;\n\t}\n}\n\n/// <summary>\n/// Kernel handle that is derived from <c>WaitHandle</c>.\n/// When don't need to wait, use <see cref=\"Handle_\"/>, it's more lightweight and has more creation methods.\n/// </summary>\ninternal class WaitHandle_ : WaitHandle {\n\tpublic WaitHandle_(IntPtr nativeHandle, bool ownsHandle) {\n\t\tbase.SafeWaitHandle = new SafeWaitHandle(nativeHandle, ownsHandle);\n\t}\n\t\n\t/// <summary>\n\t/// Opens process handle.\n\t/// Returns <c>null</c> if failed.\n\t/// </summary>\n\t/// <param name=\"pid\"></param>\n\t/// <param name=\"desiredAccess\"></param>\n\tpublic static WaitHandle_ FromProcessId(int pid, uint desiredAccess) {\n\t\ttry {\n\t\t\tvar hp = Handle_.OpenProcess(pid, desiredAccess);\n\t\t\tif (!hp.Is0) return new WaitHandle_(hp, true);\n\t\t}\n\t\tcatch (Exception ex) { Debug_.Print(ex); }\n\t\treturn null;\n\t}\n}\n"
  },
  {
    "path": "Au/Internal/ILReader.cs",
    "content": "\n//https://stackoverflow.com/questions/14243284/how-can-i-retrieve-string-literals-using-reflection\n\n//#define FULL\n\nusing System.Reflection.Emit;\n\nnamespace Au.More;\n\n[StructLayout(LayoutKind.Sequential)]\ninternal struct ILInstruction {\n\tprivate readonly OpCode operationCode; // 40.  56-64.  The entire structure is very big.  maybe do array lookup for opcode instead.\n\t\n\tprivate readonly byte[] instructionRawData;\n\t\n\tprivate readonly object instructionData;\n\t\n\tprivate readonly int instructionAddress;\n\t\n\tprivate readonly int index;\n\t\n\tinternal ILInstruction(OpCode code, byte[] instructionRawData, int instructionAddress, object instructionData, int index) {\n\t\tthis.operationCode = code;\n\t\tthis.instructionRawData = instructionRawData;\n\t\tthis.instructionAddress = instructionAddress;\n\t\tthis.instructionData = instructionData;\n\t\tthis.index = index;\n\t}\n\t\n\tpublic OpCode Op {\n\t\tget {\n\t\t\treturn this.operationCode;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Gets the raw data.\n\t/// </summary>\n\tpublic byte[] RawData {\n\t\tget {\n\t\t\treturn this.instructionRawData;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Gets the data.\n\t/// </summary>\n\tpublic object Data {\n\t\tget {\n\t\t\treturn this.instructionData;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Gets the address of the instruction.\n\t/// </summary>\n\tpublic int Address {\n\t\tget {\n\t\t\treturn this.instructionAddress;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Gets the index of the instruction.\n\t/// </summary>\n\t/// <value>\n\t/// The index of the instruction.\n\t/// </value>\n\tpublic int InstructionIndex {\n\t\tget {\n\t\t\treturn this.index;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Gets the value as integer\n\t/// </summary>\n\t/// <value>The data value.</value>\n\tpublic int DataValue {\n\t\tget {\n\t\t\tint value = 0;\n\t\t\tif (this.Data != null) {\n\t\t\t\tif (this.Data is byte) {\n\t\t\t\t\tvalue = (byte)this.Data;\n\t\t\t\t} else if (this.Data is short) {\n\t\t\t\t\tvalue = (short)this.Data;\n\t\t\t\t} else if (this.Data is int) {\n\t\t\t\t\tvalue = (int)this.Data;\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\treturn value;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Gets the length of the instructions and operands.\n\t/// </summary>\n\t/// <value>The length.</value>\n\tpublic int Length {\n\t\tget {\n\t\t\treturn this.Op.Size + (this.RawData == null ? 0 : this.RawData.Length);\n\t\t}\n\t}\n\t\n#if DEBUG\n\t/// <summary>\n\t/// Returns a <see cref=\"System.String\"/> that represents this instance.\n\t/// </summary>\n\t/// <returns>\n\t/// A <see cref=\"System.String\"/> that represents this instance.\n\t/// </returns>\n\tpublic override string ToString() {\n\t\tStringBuilder builder = new StringBuilder();\n\t\tbuilder.AppendFormat(\"0x{0:x4} {1,-10}\", this.Address, this.Op.Name);\n\t\t\n\t\tif (this.Data != null) {\n\t\t\tbuilder.Append(this.Data.ToString());\n\t\t}\n\t\t\n\t\tif (this.RawData != null && this.RawData.Length > 0) {\n\t\t\tbuilder.Append(\" [0x\");\n\t\t\tfor (int i = this.RawData.Length; --i >= 0;) {\n\t\t\t\tbuilder.Append(this.RawData[i].ToString(\"x2\", System.Globalization.CultureInfo.InvariantCulture));\n\t\t\t}\n\t\t\t\n\t\t\tbuilder.Append(']');\n\t\t}\n\t\t\n\t\treturn builder.ToString();\n\t}\n#endif\n}\n\n/// <summary>\n/// Reads IL instructions from a byte stream.\n/// </summary>\n/// <remarks>Allows generated code to be viewed without debugger or enabled debug assemblies.</remarks>\ninternal sealed class ILReader {\n\t/// <summary>\n\t/// The _instruction lookup.\n\t/// </summary>\n\tprivate static readonly Lazy<Dictionary<short, OpCode>> instructionLookup = new Lazy<Dictionary<short, OpCode>>(ILReader.GetLookupTable, System.Threading.LazyThreadSafetyMode.ExecutionAndPublication);\n\t\n\t/// <summary>\n\t/// The IL reader provider.\n\t/// </summary>\n\tprivate IILReaderProvider intermediateLanguageProvider;\n\t\n\t/// <summary>\n\t/// Initializes a new instance of the <see cref=\"ILReader\"/> class.\n\t/// </summary>\n\tpublic ILReader(MethodInfo method, byte[] il = null) {\n\t\t//au: il added because the caller itself gets IL array and does nothing if it's too big to disassemble. To avoid getting IL 2 times.\n\t\t\n\t\tif (method == null) {\n\t\t\tthrow new ArgumentNullException(\"method\");\n\t\t}\n\t\t\n\t\tthis.intermediateLanguageProvider = ILReader.CreateILReaderProvider(method, il);\n\t}\n\t\n\t/// <summary>\n\t/// Gets the instructions.\n\t/// </summary>\n\t/// <value>The instructions.</value>\n\tpublic IEnumerable<ILInstruction> Instructions {\n\t\tget {\n\t\t\tbyte[] instructionBytes = this.intermediateLanguageProvider.GetMethodBody();\n\t\t\tint instructionIndex = 0, startAddress;\n\t\t\tfor (int position = 0; position < instructionBytes.Length;) {\n\t\t\t\tstartAddress = position;\n\t\t\t\tshort operationData = instructionBytes[position];\n\t\t\t\tif (IsInstructionPrefix(operationData)) {\n\t\t\t\t\toperationData = (short)((operationData << 8) | instructionBytes[++position]);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tposition++;\n\t\t\t\t\n\t\t\t\tOpCode code;\n\t\t\t\tif (!instructionLookup.Value.TryGetValue(operationData, out code)) {\n\t\t\t\t\tthrow new InvalidProgramException(string.Format(\"0x{0:X2} is not a valid op code.\", operationData));\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tint dataSize = GetSize(code.OperandType);\n\t\t\t\tbyte[] data = new byte[dataSize];\n\t\t\t\tBuffer.BlockCopy(instructionBytes, position, data, 0, dataSize);\n\t\t\t\tobject objData = this.GetData(code, data);\n\t\t\t\tposition += dataSize;\n\t\t\t\t\n\t\t\t\tif (code.OperandType == OperandType.InlineSwitch) {\n\t\t\t\t\tdataSize = (int)objData;\n\t\t\t\t\tint[] labels = new int[dataSize];\n\t\t\t\t\tfor (int index = 0; index < labels.Length; index++) {\n\t\t\t\t\t\tlabels[index] = BitConverter.ToInt32(instructionBytes, position);\n\t\t\t\t\t\tposition += 4;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tobjData = labels;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tyield return new ILInstruction(code, data, startAddress, objData, instructionIndex);\n\t\t\t\tinstructionIndex++;\n\t\t\t}\n\t\t}\n\t}\n\t\n\t\n\tprivate static IILReaderProvider CreateILReaderProvider(MethodInfo methodInfo, byte[] il = null) {\n#if FULL\n\t\tIILReaderProvider reader = DynamicILReaderProvider.Create(methodInfo, il);\n\t\tif(reader != null) {\n\t\t\treturn reader;\n\t\t}\n#endif\n\t\treturn new ILReaderProvider(methodInfo, il);\n\t}\n\t\n\t/// <summary>\n\t/// Checks to see if the IL instruction is a prefix indicating the length of the instruction is two bytes long.\n\t/// </summary>\n\t/// <param name=\"value\">The IL instruction as a byte.</param>\n\t/// <remarks>IL instructions can either be 1 or 2 bytes.</remarks>\n\t/// <returns>True if this IL instruction is a prefix indicating the instruction is two bytes long.</returns>\n\tprivate static bool IsInstructionPrefix(short value) {\n\t\treturn ((value & OpCodes.Prefix1.Value) == OpCodes.Prefix1.Value) || ((value & OpCodes.Prefix2.Value) == OpCodes.Prefix2.Value)\n\t\t\t|| ((value & OpCodes.Prefix3.Value) == OpCodes.Prefix3.Value) || ((value & OpCodes.Prefix4.Value) == OpCodes.Prefix4.Value)\n\t\t\t|| ((value & OpCodes.Prefix5.Value) == OpCodes.Prefix5.Value) || ((value & OpCodes.Prefix6.Value) == OpCodes.Prefix6.Value)\n\t\t\t|| ((value & OpCodes.Prefix7.Value) == OpCodes.Prefix7.Value) || ((value & OpCodes.Prefixref.Value) == OpCodes.Prefixref.Value);\n\t}\n\t\n\t/// <summary>\n\t/// The get lookup table.\n\t/// </summary>\n\t/// <returns>\n\t/// A dictionary of IL instructions.\n\t/// </returns>\n\tprivate static Dictionary<short, OpCode> GetLookupTable() {\n\t\t// Might be better to do an array lookup.  Use a seperate arrary for instructions without a prefix and array for each prefix.\n\t\tDictionary<short, OpCode> lookupTable = new Dictionary<short, OpCode>();\n\t\tFieldInfo[] fields = typeof(OpCodes).GetFields(BindingFlags.Static | BindingFlags.Public);\n\t\tforeach (FieldInfo field in fields) {\n\t\t\tOpCode code = (OpCode)field.GetValue(null);\n\t\t\tlookupTable.Add(code.Value, code);\n\t\t}\n\t\t\n\t\treturn lookupTable;\n\t}\n\t\n\t/// <summary>\n\t/// Gets the size of a operand.\n\t/// </summary>\n\t/// <param name=\"operandType\">Defines the type of operand.</param>\n\t/// <returns>The size in bytes of the operand type.</returns>\n\tprivate static int GetSize(OperandType operandType) {\n\t\tswitch (operandType) {\n\t\tcase OperandType.InlineNone:\n\t\t\treturn 0;\n\t\tcase OperandType.ShortInlineBrTarget:\n\t\tcase OperandType.ShortInlineI:\n\t\tcase OperandType.ShortInlineVar:\n\t\t\treturn 1;\n\t\tcase OperandType.InlineVar:\n\t\t\treturn 2;\n\t\tcase OperandType.InlineBrTarget:\n\t\tcase OperandType.InlineField:\n\t\tcase OperandType.InlineI:\n\t\tcase OperandType.InlineMethod:\n\t\tcase OperandType.InlineSig:\n\t\tcase OperandType.InlineString:\n\t\tcase OperandType.InlineSwitch:\n\t\tcase OperandType.InlineTok:\n\t\tcase OperandType.InlineType:\n\t\tcase OperandType.ShortInlineR:\n\t\t\treturn 4;\n\t\tcase OperandType.InlineI8:\n\t\tcase OperandType.InlineR:\n\t\t\treturn 8;\n\t\tdefault:\n\t\t\treturn 0;\n\t\t}\n\t}\n\t\n\tprivate object GetData(OpCode code, byte[] rawData) {\n\t\tobject data = null;\n\t\tswitch (code.OperandType) {\n\t\tcase OperandType.InlineField:\n\t\t\tdata = this.intermediateLanguageProvider.ResolveField(BitConverter.ToInt32(rawData, 0));\n\t\t\tbreak;\n\t\tcase OperandType.InlineSwitch:\n\t\t\tdata = BitConverter.ToInt32(rawData, 0);\n\t\t\t\n\t\t\tbreak;\n\t\tcase OperandType.InlineBrTarget:\n\t\tcase OperandType.InlineI:\n\t\t\tdata = BitConverter.ToInt32(rawData, 0);\n\t\t\tbreak;\n\t\tcase OperandType.InlineI8:\n\t\t\tdata = BitConverter.ToInt64(rawData, 0);\n\t\t\tbreak;\n\t\tcase OperandType.InlineMethod:\n\t\t\tdata = this.intermediateLanguageProvider.ResolveMethod(BitConverter.ToInt32(rawData, 0));\n\t\t\tbreak;\n\t\tcase OperandType.InlineR:\n\t\t\tdata = BitConverter.ToDouble(rawData, 0);\n\t\t\tbreak;\n\t\tcase OperandType.InlineSig:\n\t\t\tdata = this.intermediateLanguageProvider.ResolveSignature(BitConverter.ToInt32(rawData, 0));\n\t\t\tbreak;\n\t\tcase OperandType.InlineString:\n\t\t\tdata = this.intermediateLanguageProvider.ResolveString(BitConverter.ToInt32(rawData, 0));\n\t\t\tbreak;\n\t\tcase OperandType.InlineTok:\n\t\tcase OperandType.InlineType:\n\t\t\tdata = this.intermediateLanguageProvider.ResolveType(BitConverter.ToInt32(rawData, 0));\n\t\t\tbreak;\n\t\tcase OperandType.InlineVar:\n\t\t\tdata = BitConverter.ToInt16(rawData, 0);\n\t\t\tbreak;\n\t\tcase OperandType.ShortInlineVar:\n\t\tcase OperandType.ShortInlineI:\n\t\tcase OperandType.ShortInlineBrTarget:\n\t\t\tdata = rawData[0];\n\t\t\tbreak;\n\t\tcase OperandType.ShortInlineR:\n\t\t\tdata = BitConverter.ToSingle(rawData, 0);\n\t\t\tbreak;\n\t\t}\n\t\t\n\t\treturn data;\n\t}\n\t\n\tinterface IILReaderProvider {\n\t\tbyte[] GetMethodBody();\n\t\t\n\t\tFieldInfo ResolveField(int metadataToken);\n\t\tMemberInfo ResolveMember(int metadataToken);\n\t\t\n\t\tMethodBase ResolveMethod(int metadataToken);\n\t\tbyte[] ResolveSignature(int metadataToken);\n\t\t\n\t\tstring ResolveString(int metadataToken);\n\t\tType ResolveType(int metadataToken);\n\t}\n\t\n\tclass ILReaderProvider : IILReaderProvider {\n\t\tbyte[] _il;\n\t\t\n\t\tpublic ILReaderProvider(MethodInfo method, byte[] il = null) {\n\t\t\t_il = il;\n\t\t\tthis.Method = method;\n\t\t\tthis.MethodBody = method.GetMethodBody();\n\t\t\tthis.MethodModule = method.Module;\n\t\t}\n\t\t\n\t\tpublic MethodInfo Method { get; private set; }\n\t\t\n\t\tpublic MethodBody MethodBody { get; private set; }\n\t\t\n\t\tpublic Module MethodModule { get; private set; }\n\t\t\n\t\tpublic byte[] GetMethodBody() {\n\t\t\treturn _il ??= this.MethodBody.GetILAsByteArray();\n\t\t}\n\t\t\n\t\tpublic FieldInfo ResolveField(int metadataToken) {\n\t\t\treturn this.MethodModule.ResolveField(metadataToken);\n\t\t}\n\t\t\n\t\tpublic MemberInfo ResolveMember(int metadataToken) {\n\t\t\treturn this.MethodModule.ResolveMember(metadataToken);\n\t\t}\n\t\t\n\t\tpublic MethodBase ResolveMethod(int metadataToken) {\n\t\t\treturn this.MethodModule.ResolveMethod(metadataToken);\n\t\t}\n\t\t\n\t\tpublic byte[] ResolveSignature(int metadataToken) {\n\t\t\treturn this.MethodModule.ResolveSignature(metadataToken);\n\t\t}\n\t\t\n\t\tpublic string ResolveString(int metadataToken) {\n\t\t\treturn this.MethodModule.ResolveString(metadataToken);\n\t\t}\n\t\t\n\t\tpublic Type ResolveType(int metadataToken) {\n\t\t\treturn this.MethodModule.ResolveType(metadataToken);\n\t\t}\n\t}\n\t\n#if FULL\n\tclass DynamicILReaderProvider :IILReaderProvider\n\t{\n\t\tpublic const int TypeRidPrefix = 0x02000000;\n\n\t\tpublic const int MethodRidPrefix = 0x06000000;\n\n\t\tpublic const int FieldRidPrefix = 0x04000000;\n\n\t\tpublic static readonly Type RuntimeDynamicMethodType;\n\n\t\tprivate static readonly FieldInfo fileLengthField = typeof(ILGenerator).GetField(\"m_length\", BindingFlags.NonPublic | BindingFlags.Instance);\n\n\t\tprivate static readonly FieldInfo IntermediateLanguageBytesField = typeof(ILGenerator).GetField(\"m_ILStream\", BindingFlags.NonPublic | BindingFlags.Instance);\n\n\t\tprivate static readonly MethodInfo bakeByteArrayMethod = typeof(ILGenerator).GetMethod(\"BakeByteArray\", BindingFlags.NonPublic | BindingFlags.Instance);\n\n\t\tprivate static readonly PropertyInfo dynamicScopeIndexor;\n\n\t\tprivate static readonly FieldInfo dynamicScopeField;\n\n\t\tprivate static readonly Type genericMethodInfoType;\n\n\t\tprivate static readonly FieldInfo genericMethodHandleField;\n\n\t\tprivate static readonly FieldInfo genericMethodContextField;\n\n\t\tprivate static readonly Type varArgMethodType;\n\n\t\tprivate static readonly FieldInfo varArgMethodMethod;\n\n\t\tprivate static readonly Type genericFieldInfoType;\n\n\t\tprivate static readonly FieldInfo genericFieldInfoHandle;\n\n\t\tprivate static readonly FieldInfo genericFieldInfoContext;\n\n\t\tprivate static readonly FieldInfo ownerField;\n\n\t\tprivate object dynamicScope;\n\n\t\tprivate ILGenerator generator;\n\n\t\tstatic DynamicILReaderProvider()\n\t\t{\n\t\t\tBindingFlags bindingFlags = BindingFlags.NonPublic | BindingFlags.Instance;\n\t\t\tdynamicScopeIndexor = Type.GetType(\"System.Reflection.Emit.DynamicScope\").GetProperty(\"Item\", bindingFlags);\n\t\t\tdynamicScopeField = Type.GetType(\"System.Reflection.Emit.DynamicILGenerator\").GetField(\"m_scope\", bindingFlags);\n\n\t\t\tvarArgMethodType = Type.GetType(\"System.Reflection.Emit.VarArgMethod\");\n\t\t\tvarArgMethodMethod = varArgMethodType.GetField(\"m_method\", bindingFlags);\n\n\t\t\tgenericMethodInfoType = Type.GetType(\"System.Reflection.Emit.GenericMethodInfo\");\n\t\t\tgenericMethodHandleField = genericMethodInfoType.GetField(\"m_methodHandle\", bindingFlags);\n\t\t\tgenericMethodContextField = genericMethodInfoType.GetField(\"m_context\", bindingFlags);\n\n\t\t\tgenericFieldInfoType = Type.GetType(\"System.Reflection.Emit.GenericFieldInfo\", false);\n\t\t\tif(genericFieldInfoType != null) {\n\t\t\t\tgenericFieldInfoHandle = genericFieldInfoType.GetField(\"m_fieldHandle\", bindingFlags);\n\t\t\t\tgenericFieldInfoContext = genericFieldInfoType.GetField(\"m_context\", bindingFlags);\n\t\t\t} else {\n\t\t\t\tgenericFieldInfoHandle = genericFieldInfoContext = null;\n\t\t\t}\n\n\t\t\tRuntimeDynamicMethodType = typeof(DynamicMethod).GetNestedType(\"RTDynamicMethod\", BindingFlags.NonPublic);\n\t\t\townerField = RuntimeDynamicMethodType.GetField(\"m_owner\", bindingFlags);\n\t\t}\n\n\t\tprivate DynamicILReaderProvider(DynamicMethod method)\n\t\t{\n\t\t\tthis.Method = method;\n\t\t\tthis.generator = method.GetILGenerator();\n\t\t\tthis.dynamicScope = dynamicScopeField.GetValue(this.generator);\n\t\t}\n\n\t\tpublic DynamicMethod Method { get; private set; }\n\n\t\tinternal object this[int token]\n\t\t{\n\t\t\tget\n\t\t\t{\n\t\t\t\treturn dynamicScopeIndexor.GetValue(this.dynamicScope, new object[] { token });\n\t\t\t}\n\t\t}\n\n\t\tpublic static DynamicILReaderProvider Create(MethodInfo method)\n\t\t{\n\t\t\tif(method == null) {\n\t\t\t\tthrow new ArgumentNullException(\"method\");\n\t\t\t}\n\n\t\t\tDynamicMethod dynamicMethod = method as DynamicMethod;\n\t\t\tif(dynamicMethod != null) {\n\t\t\t\treturn new DynamicILReaderProvider(dynamicMethod);\n\t\t\t}\n\n\t\t\tType methodType = method.GetType();\n\t\t\tif(RuntimeDynamicMethodType.IsAssignableFrom(methodType)) {\n\t\t\t\treturn new DynamicILReaderProvider(ownerField.GetValue(method) as DynamicMethod);\n\t\t\t}\n\n\t\t\treturn null;\n\t\t}\n\n\t\tpublic byte[] GetMethodBody()\n\t\t{\n\t\t\tbyte[] data = null;\n\t\t\tILGenerator ilgen = this.Method.GetILGenerator();\n\n\t\t\ttry {\n\t\t\t\tdata = (byte[])bakeByteArrayMethod.Invoke(ilgen, null) ?? new byte[0];\n\t\t\t}\n\t\t\tcatch(TargetInvocationException) {\n\t\t\t\tint length = (int)fileLengthField.GetValue(ilgen);\n\t\t\t\tdata = new byte[length];\n\t\t\t\tArray.Copy((byte[])IntermediateLanguageBytesField.GetValue(ilgen), data, length);\n\t\t\t}\n\n\t\t\treturn data;\n\t\t}\n\n\t\tpublic FieldInfo ResolveField(int metadataToken)\n\t\t{\n\t\t\tobject tokenValue = this[metadataToken];\n\t\t\tif(tokenValue is RuntimeFieldHandle) {\n\t\t\t\treturn FieldInfo.GetFieldFromHandle((RuntimeFieldHandle)tokenValue);\n\t\t\t}\n\n\t\t\tif(tokenValue.GetType() == DynamicILReaderProvider.genericFieldInfoType) {\n\t\t\t\treturn FieldInfo.GetFieldFromHandle(\n\t\t\t\t\t(RuntimeFieldHandle)genericFieldInfoHandle.GetValue(tokenValue),\n\t\t\t\t\t(RuntimeTypeHandle)genericFieldInfoContext.GetValue(tokenValue));\n\t\t\t}\n\n\t\t\treturn null;\n\t\t}\n\n\t\tpublic MemberInfo ResolveMember(int metadataToken)\n\t\t{\n\t\t\tif((metadataToken & TypeRidPrefix) != 0) {\n\t\t\t\treturn this.ResolveType(metadataToken);\n\t\t\t}\n\n\t\t\tif((metadataToken & MethodRidPrefix) != 0) {\n\t\t\t\treturn this.ResolveMethod(metadataToken);\n\t\t\t}\n\n\t\t\tif((metadataToken & FieldRidPrefix) != 0) {\n\t\t\t\treturn this.ResolveField(metadataToken);\n\t\t\t}\n\n\t\t\treturn null;\n\t\t}\n\n\t\tpublic MethodBase ResolveMethod(int metadataToken)\n\t\t{\n\t\t\tobject tokenValue = this[metadataToken];\n\t\t\tDynamicMethod dynamicMethod = tokenValue as DynamicMethod;\n\t\t\tif(dynamicMethod != null) {\n\t\t\t\treturn dynamicMethod;\n\t\t\t}\n\n\t\t\tif(tokenValue is RuntimeMethodHandle) {\n\t\t\t\treturn MethodBase.GetMethodFromHandle((RuntimeMethodHandle)this[metadataToken]);\n\t\t\t}\n\n\t\t\tif(tokenValue.GetType() == DynamicILReaderProvider.genericFieldInfoType) {\n\t\t\t\treturn MethodBase.GetMethodFromHandle(\n\t\t\t\t\t(RuntimeMethodHandle)genericMethodHandleField.GetValue(tokenValue),\n\t\t\t\t\t(RuntimeTypeHandle)genericMethodContextField.GetValue(tokenValue));\n\t\t\t}\n\n\t\t\tif(tokenValue.GetType() == DynamicILReaderProvider.varArgMethodType) {\n\t\t\t\treturn DynamicILReaderProvider.varArgMethodMethod.GetValue(tokenValue) as MethodInfo;\n\t\t\t}\n\n\t\t\treturn null;\n\t\t}\n\n\t\tpublic byte[] ResolveSignature(int metadataToken)\n\t\t{\n\t\t\treturn this[metadataToken] as byte[];\n\t\t}\n\t\tpublic string ResolveString(int metadataToken)\n\t\t{\n\t\t\treturn this[metadataToken] as string;\n\t\t}\n\n\t\tpublic Type ResolveType(int metadataToken)\n\t\t{\n\t\t\treturn Type.GetTypeFromHandle((RuntimeTypeHandle)this[metadataToken]);\n\t\t}\n\t}\n#endif\n}\n"
  },
  {
    "path": "Au/Internal/IconString_.cs",
    "content": "using System.Windows;\nusing System.Windows.Media;\n\nnamespace Au.More;\n\n/// <summary>\n/// Parses XAML icon strings like <c>\"*Pack.Name color\"</c>.\n/// </summary>\nrecord struct IconString_ {\n\tpublic readonly string pack, name, color;\n\tpublic readonly int size, end;\n\tpublic readonly Thickness margin;\n\tpublic readonly char stretch;\n\tpublic readonly bool snapPixels;\n\t\n\tpublic bool HasValue => name != null;\n\tpublic bool HasSize => size is > 0 and <= 16;\n\tpublic bool HasMargin => margin != default;\n\t\n\tIconString_(RStr s) {\n\t\tif (!Detect(s, out var d)) return;\n\t\tpack = s[d.pack.Range].ToString();\n\t\tname = s[d.name.Range].ToString();\n\t\t\n\t\tint k = d.name.end;\n\t\tend = s.IndexOf(k, ';'); if (end < 0) end = s.Length;\n\t\t\n\t\tif (s.Eq(k, ' ')) {\n\t\t\ts = s[++k..end];\n\t\t\tSpan<Range> a1 = stackalloc Range[3], a2 = stackalloc Range[6];\n\t\t\tforeach (var v in a1[..s.Split(a1, ' ')]) {\n\t\t\t\tint i = v.Start.Value, j = v.End.Value;\n\t\t\t\tif (j == i) continue;\n\t\t\t\tvar c = s[i++];\n\t\t\t\tif (c is '#' or '|' || c.IsAsciiAlpha()) color = s[v].ToString();\n\t\t\t\telse if (c == '@') size = s[i..].ToInt_();\n\t\t\t\telse if (c == '%') { //margin\n\t\t\t\t\tint n = 0;\n\t\t\t\t\tvar m = s[i..j];\n\t\t\t\t\tforeach (var u in a2[..m.Split(a2, ',')]) {\n\t\t\t\t\t\tn++;\n\t\t\t\t\t\tif (u.End.Value == u.Start.Value) continue;\n\t\t\t\t\t\tif (n == 6) {\n\t\t\t\t\t\t\tsnapPixels = m[u][0] == 'p';\n\t\t\t\t\t\t} else if (n == 5) {\n\t\t\t\t\t\t\tstretch = m[u][0]; if (!(stretch is 'f' or 'm')) stretch = default;\n\t\t\t\t\t\t} else if (double.TryParse(m[u], CultureInfo.InvariantCulture, out var g)) {\n\t\t\t\t\t\t\tswitch (n) { case 1: margin.Left = g; break; case 2: margin.Top = g; break; case 3: margin.Right = g; break; case 4: margin.Bottom = g; break; }\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t//note: don't use `MemoryExtensions.SpanSplitEnumerator<char> RStr.Split<char>(char separator)`. Easier to use, but: 1. Not in .NET 8. 2. 5-10 times slower.\n\t}\n\t\n\t/// <summary>\n\t/// Parses single-icon string. Format: <c>\"[*&lt;library&gt;]*pack.name[ color][ @size][ %margin]\"</c>.\n\t/// </summary>\n\t/// <returns>false if bad format.</returns>\n\tpublic static bool Parse(RStr s, out IconString_ r) {\n\t\tr = new(s);\n\t\treturn r.HasValue;\n\t}\n\t\n\t/// <summary>\n\t/// Parses single-or-multi-icon string. Format: <c>\"[*&lt;library&gt;]*pack.name[ color][ @size][ %margin][;more icons]\"</c>.\n\t/// </summary>\n\t/// <returns>null if bad format.</returns>\n\tpublic static IconString_[] ParseAll(string icon) {\n\t\tList<IconString_> a = null;\n\t\tfor (RStr s = icon; Parse(s, out var r); s = s[(r.end + 1)..].TrimStart()) {\n\t\t\t(a ??= new()).Add(r);\n\t\t\tif (r.end == s.Length || s[r.end] != ';') break;\n\t\t}\n\t\treturn a?.ToArray();\n\t}\n\t\n\t/// <summary>\n\t/// Returns true if <i>s</i> starts with <c>\"*pack.name\"</c> or <c>\"*&lt;library&gt;*pack.name\"</c>.\n\t/// </summary>\n\tpublic static bool Detect(RStr s, out (StartEnd pack, StartEnd name) r) {\n\t\tr = default;\n\t\tif (s.Length < 8 || s[0] != '*' || !s.Contains('.')) return false;\n\t\tSpan<StartEnd> a = stackalloc StartEnd[3];\n\t\tif (!s_rxDetect.Match(s, a)) return false;\n\t\tr = (a[1], a[2]);\n\t\treturn true;\n\t}\n\tstatic regexp s_rxDetect = new regexp(@\"^\\*(?:<.+?>\\*)?([[:alpha:]]\\w+)\\.(\\w\\w+)\");\n\t\n\t/// <summary>\n\t/// Our compiler (_CreateManagedResources) adds XAML of icons to resources from literal strings. This function gets the XAML.\n\t/// </summary>\n\t/// <param name=\"icon\"></param>\n\t/// <returns>null if failed.</returns>\n\tpublic static string GetXamlFromResources(string icon) {\n\t\t//print.it(\"\\r\\n----\", icon);\n\t\tif (DetectAndRemoveParametersForResources(icon, out string icon2) && ResourceUtil.TryGetString_(icon2) is string xaml) {\n\t\t\t//print.it(xaml);\n\t\t\tvar a = ParseAll(icon);\n\t\t\tif (!XamlSetColorSizeMargin(ref xaml, a)) return null;\n\t\t\t//print.it(xaml);\n\t\t\treturn xaml;\n\t\t} else {\n\t\t\tDebug_.Print(icon);\n\t\t\treturn null;\n\t\t}\n\t}\n\t\n\tstatic regexp s_rxPath = new regexp(@\"<Path (.+?)/?>\");\n\tstatic regexp s_rxAttr = new(@\" (?:Fill|Stroke|Width|Height|Margin|Stretch|SnapsToDevicePixels)=\"\"[^\"\"]*\");\n\t\n\t/// <summary>\n\t/// In XAML sets color, size and margin specified in parsed icons.\n\t/// </summary>\n\t/// <param name=\"xaml\">XAML with as many <i>Path</i> as <i>a</i> elements.</param>\n\t/// <param name=\"a\">Parsed icons.</param>\n\t/// <returns>false if found errors etc.</returns>\n\tpublic static bool XamlSetColorSizeMargin(ref string xaml, IconString_[] a) {\n\t\tusing var b_ = new StringBuilder_(out var b, xaml.Length / 10 * 11 + 100);\n\t\tint appendFrom = 0, i = 0;\n\t\tbool anyHasSizeOrMargin = a.Any(o => o.HasSize || o.HasMargin);\n\t\t\n\t\tforeach (var gp in s_rxPath.FindAllG(xaml, 1)) { //for each <Path ...>\n\t\t\tif (i == a.Length) return false;\n\t\t\tvar x = a[i++];\n\t\t\tbool hasSize = x.HasSize, hasMargin = x.HasMargin, xamlHasSizeOrMargin = false;\n\t\t\tforeach (var ga in s_rxAttr.FindAllG(xaml, 0, gp.Start..gp.End)) { //for each attribute\n\t\t\t\tint attr = ga.Start + 1;\n\t\t\t\tif (xaml[attr] is 'F' or 'S') {\n\t\t\t\t\tstring sVal;\n\t\t\t\t\tif (xaml[attr + 1] == 'n') { //SnapsToDevicePixels\n\t\t\t\t\t\tif (hasSize || x.snapPixels) sVal = \"True\"; else continue;\n\t\t\t\t\t} else if (xaml[attr + 3] == 'e') { //Stretch\n\t\t\t\t\t\tif (x.stretch is 'f' or 'm') sVal = x.stretch is 'f' ? \"Fill\" : \"UniformToFill\"; else continue;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsVal = NormalizeColor(x.color);\n\t\t\t\t\t}\n\t\t\t\t\tb.Append(xaml, appendFrom, xaml.IndexOf('\"', attr) + 1 - appendFrom).Append(sVal);\n\t\t\t\t\tappendFrom = ga.End;\n\t\t\t\t} else {\n\t\t\t\t\txamlHasSizeOrMargin = true;\n\t\t\t\t\tif (hasSize || hasMargin) {\n\t\t\t\t\t\tprint.warning($\"@size and %margin not supported for this icon: *{x.pack}.{x.name}\", -1);\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (hasSize || hasMargin || (anyHasSizeOrMargin && !xamlHasSizeOrMargin)) {\n\t\t\t\tb.Append(xaml, appendFrom, gp.End - appendFrom);\n\t\t\t\tappendFrom = gp.End;\n\t\t\t\tif (hasSize) {\n\t\t\t\t\tif (hasMargin) { print.warning($\"Error in icon *{x.pack}.{x.name}: use @size or %margin, not both.\", -1); return false; }\n\t\t\t\t\tb.AppendFormat(\" Width=\\\"{0}\\\" Height=\\\"{0}\\\" Margin=\\\"{1}\\\"\", x.size, ((16 - x.size) / 2.0).ToS());\n\t\t\t\t} else if (hasMargin) {\n\t\t\t\t\tdouble wid = 16 - x.margin.Left - x.margin.Right, hei = 16 - x.margin.Top - x.margin.Bottom;\n\t\t\t\t\tif (wid <= 0 || hei <= 0) { print.warning($\"Error in icon *{x.pack}.{x.name}: margins left+right and top+bottom must be < 16.\", -1); return false; }\n\t\t\t\t\tb.AppendFormat(\" Width=\\\"{0}\\\" Height=\\\"{1}\\\" Margin=\\\"{2}\\\"\", wid.ToS(), hei.ToS(), x.margin.ToString());\n\t\t\t\t} else {\n\t\t\t\t\t//if like \"icon1;icon2\", and some icons have specified size or margin, and some others don't, some icons may be rendered very small, depending on others.\n\t\t\t\t\t//\tDon't know why. Workaround: specify Width/Height for all.\n\t\t\t\t\tb.Append(\" Width=\\\"16\\\" Height=\\\"16\\\"\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tb.Append(xaml, appendFrom, xaml.Length - appendFrom);\n\t\txaml = b.ToString();\n\t\treturn true;\n\t}\n\t\n\t/// <summary>\n\t/// If <i>s</i> is or contains <c>\"color|color2\"</c>, removes <c>\"|color2\"</c> or <c>\"color|\"</c> depending on <see cref=\"WpfUtil_.IsHighContrastDark\"/>.\n\t/// If then (or initially) <i>s</i> is empty, returns <c>SystemColors.ControlTextColor</c>.\n\t/// </summary>\n\tpublic static string NormalizeColor(string s) {\n\t\tint i = s?.IndexOf('|') ?? -1;\n\t\tif (i >= 0) {\n\t\t\tbool dark = WpfUtil_.IsHighContrastDark;\n\t\t\tusing (new StringBuilder_(out var b)) {\n\t\t\t\tint appendFrom = 0;\n\t\t\t\tdo {\n\t\t\t\t\tint j = i + 1;\n\t\t\t\t\tif (dark) {\n\t\t\t\t\t\twhile (i > 0 && s[i - 1] != ' ') i--;\n\t\t\t\t\t} else {\n\t\t\t\t\t\twhile (j < s.Length && !(s[j] is ' ' or ';')) j++;\n\t\t\t\t\t}\n\t\t\t\t\tb.Append(s, appendFrom, i - appendFrom);\n\t\t\t\t\ti = s.IndexOf('|', j);\n\t\t\t\t\tappendFrom = j;\n\t\t\t\t} while (i > 0);\n\t\t\t\tb.Append(s, appendFrom, s.Length - appendFrom);\n\t\t\t\ts = b.ToString();\n\t\t\t}\n\t\t}\n\t\tif (s.NE()) s = SystemColors.ControlTextColor.ToString();\n\t\treturn s;\n\t}\n\t\n\t/// <summary>\n\t/// If <i>s</i> is an icon string, removes parameters and returns true.\n\t/// <br/>Example1: <c>\"*Pack.Name color\"</c> -> <c>\"*Pack.Name\"</c>.\n\t/// <br/>Example2: <c><![CDATA[\"*<assembly>*Pack.Name1 color; *Pack.Name2 color margin;\"]]></c> -> <c>\"*Pack.Name1;Pack.Name2\"</c>.\n\t/// </summary>\n\tpublic static bool DetectAndRemoveParametersForResources(RStr s, out string result) {\n\t\tStringBuilder b = null;\n\t\twhile (Detect(s, out var d)) {\n\t\t\tif (b == null) b = new(); else b.Append(';');\n\t\t\tb.Append('*').Append(s[d.pack.Range]).Append('.').Append(s[d.name.Range]);\n\t\t\tint semi = s.IndexOf(d.name.end, ';'); if (semi < 0) break;\n\t\t\ts = s[++semi..].TrimStart();\n\t\t}\n\t\treturn (result = b?.ToString()) != null;\n\t}\n}\n"
  },
  {
    "path": "Au/Internal/Jit_.cs",
    "content": "﻿namespace Au.More;\n\n/// <summary>\n/// JIT-compiles methods.\n/// </summary>\nstatic class Jit_ {\n\tconst BindingFlags c_bindingFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly;\n\t\n\t/// <summary>\n\t/// JIT-compiles method.\n\t/// Uses <see cref=\"RuntimeHelpers.PrepareMethod\"/>.\n\t/// </summary>\n\t/// <param name=\"type\">Type containing the method.</param>\n\t/// <param name=\"method\">Method name.</param>\n\t/// <exception cref=\"ArgumentException\">Method does not exist.</exception>\n\t/// <exception cref=\"AmbiguousMatchException\">Multiple overloads exist.</exception>\n\tpublic static void Compile(Type type, string method) {\n\t\tvar m = type.GetMethod(method, c_bindingFlags);\n\t\tif (m == null) throw new ArgumentException($\"Method {type.Name}.{method} does not exist.\");\n\t\tRuntimeHelpers.PrepareMethod(m.MethodHandle);\n\t\t//tested: maybe MethodHandle.GetFunctionPointer can be used to detect whether the method is jited and assembly ngened.\n\t\t//\tCall GetFunctionPointer before and after PrepareMethod. If was not jited, the second call returns a different value.\n\t\t//\tUndocumented, therefore unreliable.\n\t}\n\t\n\t//rejected. Don't JIT-compile overloads.\n\t///// <summary>\n\t///// JIT-compiles a method overload.\n\t///// Uses <c>RuntimeHelpers.PrepareMethod</c>.\n\t///// </summary>\n\t///// <param name=\"type\">Type containing the method.</param>\n\t///// <param name=\"method\">Method name.</param>\n\t///// <param name=\"paramTypes\">Types of parameters of this overload.</param>\n\t///// <exception cref=\"ArgumentException\">Method does not exist.</exception>\n\t///// <exception cref=\"AmbiguousMatchException\">Multiple overloads exist that match <i>paramTypes</i>.</exception>\n\t//public static void Compile(Type type, string method, params Type[] paramTypes)\n\t//{\n\t//\tvar m = type.GetMethod(method, c_bindingFlags, null, paramTypes, null);\n\t//\tif(m == null) throw new ArgumentException($\"Method {type.Name}.{method} does not exist.\");\n\t//\tRuntimeHelpers.PrepareMethod(m.MethodHandle);\n\t//\t//tested: MethodHandle.GetFunctionPointer cannot be used to detect whether the method is jited.\n\t//\t//\tTried to find a faster way to detect whether the assembly is ngened.\n\t//}\n\t\n\t/// <summary>\n\t/// JIT-compiles multiple methods of same type.\n\t/// Uses <see cref=\"RuntimeHelpers.PrepareMethod\"/>.\n\t/// </summary>\n\t/// <param name=\"type\">Type containing the methods.</param>\n\t/// <param name=\"methods\">Method names.</param>\n\t/// <exception cref=\"ArgumentException\">Method does not exist.</exception>\n\tpublic static void Compile(Type type, params string[] methods) {\n\t\tforeach (var v in methods) Compile(type, v);\n\t}\n}\n"
  },
  {
    "path": "Au/Internal/LaDebugger_.cs",
    "content": "namespace Au.More;\n\nclass LaDebugger_ {\n\t//Called by netcoredbg as a breakpoint condition when specified Message.\n\tstatic bool Logpoint(bool condition, string s, string link) {\n\t\tif (condition) print.it($\"<><lc #f8f8d0><open {link}><c #40B000>♦<><> {s}<>\");\n\t\treturn false;\n\t}\n\t\n\t//Same problems as with LaDebugger_<T>. Also fails if struct.\n\t//static string Print<T>(T t) {\n\t//\tvar s = print.util.toString(t).Limit(1_000_000);\n\t//\treturn Convert.ToBase64String(Encoding.UTF8.GetBytes(s)); \n\t//}\n}\n\n//Functions called by netcoredbg -var-create to get a value in the print.it format.\n//netcoredbg does not support cast to object etc. This is the only way that works.\n//Fails if array.\n//Fails if ref struct.\n//Garbage if object or dynamic.\nclass LaDebugger_<T> {\n\tstatic string Print(T t) => _Print(t, false);\n\tstatic string PrintCompact(T t) => _Print(t, true);\n\t\n\tstatic string _Print(T t, bool compact) {\n\t\tvar s = print.util.toString(t, compact).Limit(1_000_000);\n\t\treturn Convert.ToBase64String(Encoding.UTF8.GetBytes(s));\n\t}\n\t\n\t//static string PrintArray(T[] a) => \"OK\"; //fails too\n}\n"
  },
  {
    "path": "Au/Internal/LineWriter_.cs",
    "content": "﻿namespace Au.More;\n\n/// <summary>\n/// <see cref=\"TextWriter\"/> optimized for writing full lines.\n/// </summary>\n/// <remarks>\n/// Derived class must override <see cref=\"WriteLineNow\"/>. Don't need to override <c>Write</c>/<c>WriteLine</c>.\n/// If <c>Write</c> called with text that does not end with <c>'\\n'</c>, just accumulates the text in this variable.\n/// When called <c>WriteLine</c> or <c>Flush</c> or <c>Write</c> with text that ends with <c>'\\n'</c>, calls <see cref=\"WriteLineNow\"/> of the derived class.\n/// </remarks>\ninternal abstract class LineWriter_ : TextWriter {\n\tStringBuilder _b;\n\t\n\t/// <summary>\n\t/// Returns <c>Encoding.Unicode</c>.\n\t/// </summary>\n\tpublic override Encoding Encoding => Encoding.Unicode;\n\t\n\t/// <summary>\n\t/// If <i>value</i> is <c>'\\n'</c>, writes accumulated text as full line and clears accumulated text, else just appends <i>value</i> to the accumulated text.\n\t/// </summary>\n\tpublic override void Write(char value) {\n\t\t//qm2.write((int)value, value);\n\t\tif (value == '\\n') {\n\t\t\tWriteLine();\n\t\t} else {\n\t\t\t(_b ??= new StringBuilder()).Append(value);\n\t\t}\n\t\tbase.Write(value);\n\t}\n\t\n\t/// <summary>\n\t/// If <i>value</i> ends with <c>'\\n'</c>, writes line (accumulated text + <i>value</i>) and clears accumulated text, else just appends <i>value</i> to the accumulated text.\n\t/// </summary>\n\tpublic override void Write(string value) {\n\t\t//qm2.write($\"'{value}'\");\n\t\t//qm2.write(\"Write\", $\"'{value}'\", value.ToCharArray());\n\t\tif (value.NE()) return;\n\t\tif (value.Ends('\\n')) {\n\t\t\tWriteLine(value[..^(value.Ends(\"\\r\\n\") ? 2 : 1)]);\n\t\t} else {\n\t\t\t(_b ??= new StringBuilder()).Append(value);\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// If this variable contains accumulated text, writes it as full line and clears it. Else writes empty line.\n\t/// </summary>\n\tpublic override void WriteLine() {\n\t\tWriteLineNow(_PrependBuilder(null));\n\t}\n\t\n\t/// <summary>\n\t/// Writes line (accumulated text + <i>value</i>) and clears accumulated text.\n\t/// </summary>\n\tpublic override void WriteLine(string value) {\n\t\t//qm2.write(\"WriteLine\", $\"'{value}'\", value.ToCharArray());\n\t\tWriteLineNow(_PrependBuilder(value));\n\t}\n\t\n\tstring _PrependBuilder(string value) {\n\t\tif (_b != null && _b.Length > 0) {\n\t\t\tvalue = _b.ToString() + value;\n\t\t\t_b.Clear();\n\t\t}\n\t\treturn value;\n\t}\n\t\n\t/// <summary>\n\t/// If this variable contains accumulated text, writes it as full line and clears it.\n\t/// </summary>\n\tpublic override void Flush() {\n\t\tvar s = _PrependBuilder(null);\n\t\tif (!s.NE()) WriteLineNow(s);\n\t}\n\t\n\t/// <summary>\n\t/// Called to write full line.\n\t/// </summary>\n\t/// <param name=\"s\">Line text. Does not end with line break characters.</param>\n\tprotected abstract void WriteLineNow(string s);\n}\n"
  },
  {
    "path": "Au/Internal/MiniProgram_.cs",
    "content": "//PROBLEM: slow startup.\n//A minimal script starts in 70-100 ms cold, 40 hot (old PC, tested long time ago). Now on new PC starts in 37 ms.\n//Workaround for role miniProgram:\n//\tPreload task process. Let it wait for next task. While waiting, it also can JIT etc.\n//\tThen starts in 12/4 ms (cold/hot). With script.setup 15/5. That was an old test, now should be slower. Now on new PC 10/6 and 12/8.\n//\tExcept first time. Also not faster if several scripts are started without a delay. Never mind.\n//\tThis is implemented in this class and in Au.AppHost (just ~10 code lines added in 1 place).\n//Not using this workaround since v1.1, unless meta startFaster true (undocumented). Because:\n//\tSometimes something does not work well (see problems below).\n//\tSince this was invented, .NET process startup became faster (faster JIT etc). Also computers faster. The delay now is barely noticeable.\n//FUTURE: if this workaround is sometimes useful, make meta startFaster public. Else remove the preloading code.\n\n//PROBLEM when preloaded: miniProgram windows start inactive, behind one or more windows. Unless they activate self, like dialog.\n//\tIt does not depend on the foreground lock setting/API. The setting/API just enable SetForegroundWindow, but most windows don't call it.\n//\tWorkaround: use CBT hook. It receives HCBT_ACTIVATE even when the window does not become the foreground window.\n//\t\tOn HCBT_ACTIVATE, async-call SetForegroundWindow. Also, editor calls AllowSetForegroundWindow before starting task.\n\n//PROBLEM when preloaded: when miniProgram starts a console program, occasionally its window is inactive, although on top of other windows.\n//\tTo reproduce: run miniProgram script: `run.it(\"cmd.exe\", flags: RFlags.InheritAdmin)`. If starts active, wait at least 30 s and run again.\n//\tNever noticed it on Windows 10, only on Windows 11.\n//\tWorkaround: after `run.it` wait a while, eg `1.s();`, because it happens only if this process exits immediately.\n\n//PROBLEM when preloaded: inherits old environment variables.\n\n//PROBLEM when preloaded: the preloaded processes may be confusing, even for me sometimes.\n\n//PROBLEM: although Main() starts fast, but the process ends slowly, because of .NET.\n//\tEg if starting an empty script every <50 ms, sometimes cannot start.\n\n//Smaller problem: .NET creates many threads. No workaround.\n\n/*\n//To test task startup speed, use script \"task startup speed.cs\":\n\n300.ms(); //give time to preload new task process\nfor (int i = 0; i < 5; i++) {\n//\tperf.cpu();\n\tvar t=perf.ms.ToString();\n\tscript.run(@\"miniProgram.cs\", t);\n//\tscript.run(@\"exeProgram.cs\", t);\n\t600.ms(); //give time for the process to exit\n}\n\n//miniProgram.cs and exeProgram.cs:\n\nprint.it(perf.ms-Int64.Parse(args[0]));\n*/\n\nusing System.Runtime.Loader;\n\nnamespace Au.More;\n\n/// <summary>\n/// Prepares to quickly start and execute a script with role <c>miniProgram</c> in this preloaded task process. Or starts/executes in this non-preloaded process.\n/// </summary>\nstatic unsafe class MiniProgram_ {\n\tstruct _TaskInit {\n\t\tpublic IntPtr asmFile;\n\t\tpublic IntPtr* args;\n\t\tpublic int nArgs;\n\t}\n\t\n\t/// <summary>\n\t/// Called by apphost.\n\t/// </summary>\n\t[MethodImpl(MethodImplOptions.NoOptimization)]\n\tstatic void Init(nint pn, out _TaskInit r) {\n\t\tr = default;\n\t\tstring pipeName = new((char*)pn);\n\t\t\n\t\tscript.role = SRole.MiniProgram;\n\t\t\n\t\tprocess.ThisThreadSetComApartment_(ApartmentState.STA); //1.5 ms\n\t\t\n\t\tscript.AppModuleInit_(auCompiler: true); //3 ms\n\t\t\n\t\t//rejected. Now this is implemented in editor. To detect when failed uses process exit code. Never mind exception text, it is not very useful.\n\t\t//process.thisProcessExit += e => { //0.9 ms\n\t\t//\tif (s_started != 0) print.TaskEvent_(e == null ? \"TE\" : \"TF \" + e.ToStringWithoutStack(), s_started);\n\t\t//};\n\t\t\n#if true\n\t\tif (!Api.WaitNamedPipe(pipeName, -1)) return;\n#else\n//rejected: JIT some functions in other thread. Now everything much faster than with old .NET.\n//\tSpeed of p1: with this 3500, without 6000 (slow Deserialize JIT).\n\n\t\tfor (int i = 0; ; i++) {\n\t\t\tif (Api.WaitNamedPipe(pipeName, i == 1 ? -1 : 25)) break;\n\t\t\tif (Marshal.GetLastWin32Error() != Api.ERROR_SEM_TIMEOUT) return;\n\t\t\tif (i == 1) break;\n\n\t\t\t//rejected: ProfileOptimization. Now everything is JIT-ed and is as fast as can be.\n\n\t\t\trun.thread(() => {\n\t\t\t\t//using var p2 = perf.local();\n\n\t\t\t\t//JIT\n\t\t\t\tJit_.Compile(typeof(Serializer_), \"Deserialize\");\n\t\t\t\t//tested: now Api functions fast, don't JIT.\n\t\t\t\t//p2.Next();\n\t\t\t\tJit_.Compile(typeof(script), nameof(script.setup), \"_AuxThread\");\n\t\t\t\t//p2.Next();\n\n\t\t\t\t//Thread.Sleep(20);\n\t\t\t\t//p2.Next();\n\t\t\t\t//\"Au\".ToLowerInvariant(); //15-40 ms //now <1 ms\n\n\t\t\t\t//if need to preload some assemblies, use code like this. But now .NET loads assemblies fast, not like in old framework.\n\t\t\t\t//_ = typeof(TypeFromAssembly).Assembly;\n\t\t\t}, sta: false);\n\t\t}\n#endif\n\t\t\n\t\t//Debug_.PrintLoadedAssemblies(true, true);\n\t\t\n\t\t//using var p1 = perf.local();\n\t\tusing var pipe = Api.CreateFile(pipeName, Api.GENERIC_READ, 0, Api.OPEN_EXISTING, 0);\n\t\tif (pipe.Is0) { Debug_.PrintNativeError(); return; }\n\t\t//p1.Next();\n\t\tint size; if (!Api.ReadFile(pipe, &size, 4, out int nr, default) || nr != 4) return;\n\t\tif (!Api.ReadFileArr(pipe, out var b, size, out nr) || nr != size) return;\n\t\t//p1.Next();\n\t\tvar a = Serializer_.Deserialize(b);\n\t\t//p1.Next('d');\n\t\tvar flags = (MPFlags)(int)a[2];\n\t\t\n\t\tr.asmFile = Marshal.StringToCoTaskMemUTF8(a[1]);\n\t\t//p1.Next();\n\t\tstring[] args = a[3];\n\t\tif (!args.NE_()) {\n\t\t\tr.nArgs = args.Length;\n\t\t\tr.args = (IntPtr*)Marshal.AllocHGlobal(args.Length * sizeof(IntPtr));\n\t\t\tfor (int i = 0; i < args.Length; i++) r.args[i] = Marshal.StringToCoTaskMemUTF8(args[i]);\n\t\t}\n\t\t//p1.Next();\n\t\t\n\t\tscript.s_idMainFile = (uint)(int)a[6];\n\t\tscript.s_wndEditorMsg = (wnd)(int)a[8];\n\t\tscript.s_wrPipeName = a[4];\n\t\t\n\t\tif (0 != (flags & MPFlags.FromEditor)) script.testing = true;\n\t\tif (0 != (flags & MPFlags.IsPortable)) ScriptEditor.IsPortable = true;\n\t\t\n\t\tfolders.Editor = new(folders.ThisApp);\n\t\tfolders.Workspace = new(a[5]);\n\t\t\n\t\tif (0 != (flags & MPFlags.RefPaths))\n\t\t\tAssemblyLoadContext.Default.Resolving += (alc, an)\n\t\t\t\t=> ResolveAssemblyFromRefPathsAttribute_(alc, an, AssemblyUtil_.GetEntryAssembly());\n\t\t\n\t\tif (0 != (flags & MPFlags.NativePaths))\n\t\t\tAssemblyLoadContext.Default.ResolvingUnmanagedDll += (_, dll)\n\t\t\t\t=> ResolveUnmanagedDllFromNativePathsAttribute_(dll, AssemblyUtil_.GetEntryAssembly());\n\t\t\n\t\tif (0 != (flags & MPFlags.MTA))\n\t\t\tprocess.ThisThreadSetComApartment_(ApartmentState.MTA);\n\t\t\n\t\tif (0 != (flags & MPFlags.Console)) {\n\t\t\tApi.AllocConsole();\n\t\t} else {\n\t\t\tif (0 != (flags & MPFlags.RedirectConsole)) RedirectConsole_();\n\t\t\t//Compiler adds this flag if the script uses System.Console assembly.\n\t\t\t//Else new users would not know how to test code examples with Console.WriteLine found on the internet.\n\t\t}\n\t\t\n\t\t//p1.Next();\n\t\tscript.Starting_(a[0], a[7], preloaded: 0 != (flags & MPFlags.Preloaded));\n\t\t\n\t\t//Api.QueryPerformanceCounter(out s_started);\n\t\t//print.TaskEvent_(\"TS\", s_started);\n\t}\n\t\n\t//for assemblies used in miniProgram and editorExtension scripts\n\tinternal static Assembly ResolveAssemblyFromRefPathsAttribute_(AssemblyLoadContext alc, AssemblyName an, Assembly scriptAssembly) {\n\t\t//print.it(\"managed\", an);\n\t\t//note: don't cache GetCustomAttribute/split results. It's many times faster than LoadFromAssemblyPath and JIT.\n\t\tvar attr = scriptAssembly.GetCustomAttribute<RefPathsAttribute>();\n\t\tif (attr != null) {\n\t\t\tstring name = an.Name;\n\t\t\tforeach (var v in attr.Paths.Split('|')) {\n\t\t\t\t//print.it(v);\n\t\t\t\tint iName = v.Length - name.Length - 4;\n\t\t\t\tif (iName <= 0 || v[iName - 1] != '\\\\' || !v.Eq(iName, name, true)) continue;\n\t\t\t\tif (!filesystem.exists(v).File) continue;\n\t\t\t\treturn alc.LoadFromAssemblyPath_(v);\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\t\n\tinternal static Assembly LoadFromAssemblyPath_(this AssemblyLoadContext t, string path) {\n\t\ttry { return t.LoadFromAssemblyPath(path); }\n\t\tcatch { }\n\t\t//catch (FileLoadException e1) {\n\t\t//\tDebug_.Print(\"alc.LoadFromAssemblyPath failed. Will retry with s_alc. \" + e1);\n\t\t//}\n\t\t//If the assembly has the same name as one of TPA assemblies (probably it's a newer version),\n\t\t//\tthe above LoadFromAssemblyPath ignores the path and tries to load the TPA assembly, and fails.\n\t\t//\tWorkaround: Then try to load to another AssemblyLoadContext.\n\t\t//return Assembly.LoadFile(path); //works, but better use the same context for all\n\t\ts_alc ??= new(\"Resolving\");\n\t\treturn s_alc.LoadFromAssemblyPath(path);\n\t}\n\tstatic AssemblyLoadContext s_alc;\n\t\n\t//for assemblies used in miniProgram and editorExtension scripts\n\tinternal static IntPtr ResolveUnmanagedDllFromNativePathsAttribute_(string name, Assembly scriptAssembly) {\n\t\tvar attr = scriptAssembly.GetCustomAttribute<NativePathsAttribute>();\n\t\tif (attr != null) {\n\t\t\tif (!name.Ends(\".dll\", true)) name += \".dll\";\n\t\t\tforeach (var v in attr.Paths.Split('|')) {\n\t\t\t\t//print.it(v);\n\t\t\t\tif (!v.Ends(name, true) || !v.Eq(v.Length - name.Length - 1, '\\\\')) continue;\n\t\t\t\tif (NativeLibrary.TryLoad(v, out var h)) return h;\n\t\t\t}\n\t\t}\n\t\treturn default;\n\t}\n\t\n\t/// <summary>\n\t/// Used by <c>exeProgram</c>.\n\t/// </summary>\n\t/// <param name=\"rootDir\">Directory that may contain subdir <c>\"runtimes\"</c>.</param>\n\tinternal static void ResolveNugetRuntimes_(string rootDir) {\n\t\tvar runtimesDir = pathname.combine(rootDir, \"runtimes\");\n\t\tif (!filesystem.exists(runtimesDir).Directory) return;\n\t\t\n\t\t//This code is similar as in Compiler._GetDllPaths:_AddGroup. There we get paths from XML, here from filesystem.\n\t\t\n\t\tint verPC = osVersion.minWin10 ? 100 : osVersion.minWin8_1 ? 81 : osVersion.minWin8 ? 80 : 70; //don't need Win11\n\t\t\n\t\tvar flags = FEFlags.AllDescendants | FEFlags.IgnoreInaccessible | FEFlags.NeedRelativePaths | FEFlags.UseRawPath;\n\t\tList<(FEFile f, int ver)> aNet = [], aNative = [];\n\t\tforeach (var f in filesystem.enumFiles(runtimesDir, \"*.dll\", flags)) {\n\t\t\tvar s = f.Name;\n\t\t\tif (!s.Starts(@\"\\win\", true) || s.Length < 10) continue;\n\t\t\t\n\t\t\tint i = 4, verDll = 0;\n\t\t\tif (s[i] is >= '0' and <= '9') {\n\t\t\t\tverDll = s.ToInt(i, out i);\n\t\t\t\tif (verDll != 81) verDll *= 10;\n\t\t\t\tif (verDll > verPC) continue;\n\t\t\t}\n\t\t\t\n\t\t\tif (s[i] == '-') {\n\t\t\t\tstring arch = RuntimeInformation.ProcessArchitecture switch { Architecture.X86 => @\"-x86\\\", Architecture.Arm64 => @\"-arm64\\\", _ => @\"-x64\\\" };\n\t\t\t\tif (!s.Eq(i, arch, true)) continue;\n\t\t\t\ti += arch.Length;\n\t\t\t} else if (s[i++] != '\\\\') {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t\n\t\t\tvar a = s.Eq(i, @\"native\\\", true) ? aNative : aNet;\n\t\t\ta.Add((f, verDll));\n\t\t}\n\t\t\n\t\tvar dr = _Do(aNet);\n\t\tvar dn = _Do(aNative);\n\t\t\n\t\tstatic Dictionary<string, string> _Do(List<(FEFile f, int ver)> a) {\n\t\t\tif (a.Count == 0) return null;\n\t\t\tDictionary<string, string> d = null;\n\t\t\tforeach (var group in a.ToLookup(o => pathname.getNameNoExt(o.f.Name), StringComparer.OrdinalIgnoreCase)) {\n\t\t\t\t//print.it($\"<><c blue>{group.Key}<>\");\n\t\t\t\t\n\t\t\t\tint verBest = -1;\n\t\t\t\tstring sBest = null;\n\t\t\t\tforeach (var (f, verDll) in group) {\n\t\t\t\t\tif (verDll > verBest) {\n\t\t\t\t\t\tverBest = verDll;\n\t\t\t\t\t\tsBest = f.FullPath;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (sBest != null) {\n\t\t\t\t\t//print.it(sBest);\n\t\t\t\t\td ??= new(StringComparer.OrdinalIgnoreCase);\n\t\t\t\t\td[group.Key] = sBest;\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\treturn d;\n\t\t}\n\t\t\n\t\tif (dr != null) AssemblyLoadContext.Default.Resolving += (alc, an) => {\n\t\t\tif (!dr.TryGetValue(an.Name, out var path)) return null;\n\t\t\treturn alc.LoadFromAssemblyPath_(path);\n\t\t};\n\t\tif (dn != null) AssemblyLoadContext.Default.ResolvingUnmanagedDll += (_, name) => {\n\t\t\tif (name.Ends(\".dll\", true)) name = name[..^4];\n\t\t\tif (!dn.TryGetValue(name, out var path)) return default;\n\t\t\tif (!NativeLibrary.TryLoad(path, out var r)) return default;\n\t\t\treturn r;\n\t\t};\n\t}\n\t\n\t[Flags]\n\tpublic enum MPFlags {\n\t\t/// <summary>Has <c>[RefPaths]</c> attribute. It is when using meta <c>r</c> or <c>nuget</c>.</summary>\n\t\tRefPaths = 1,\n\t\t\n\t\t/// <summary><c>Main</c> with <c>[MTAThread]</c>.</summary>\n\t\tMTA = 2,\n\t\t\n\t\t/// <summary>Has meta <c>console</c> true.</summary>\n\t\tConsole = 4,\n\t\t\n\t\t/// <summary>Uses <c>System.Console</c> assembly.</summary>\n\t\tRedirectConsole = 8,\n\t\t\n\t\t/// <summary>Has <c>[NativePaths]</c> attribute. It is when using NuGet packages with native dlls.</summary>\n\t\tNativePaths = 16,\n\t\t\n\t\t/// <summary>Started from editor with the <b>Run</b> button or menu command. Used for <see cref=\"script.testing\"/>.</summary>\n\t\tFromEditor = 32,\n\t\t\n\t\t/// <summary>Started from portable editor.</summary>\n\t\tIsPortable = 64,\n\t\t\n\t\t/// <summary>Using a preloaded process.</summary>\n\t\tPreloaded = 128,\n\t\t\n\t\t//Config = 256, //meta hasConfig\n\t}\n\t\n\tclass _ConsoleReader : TextReader {\n\t\tpublic override string ReadLine() {\n\t\t\tif (!dialog.showInput(out string s, \"\", \"Console.ReadLine\", screen: screen.ofActiveWindow)) s = \"\";\n\t\t\treturn s;\n\t\t}\n\t\t\n\t\tpublic override int Read() {\n\t\t\tvar s = _read;\n\t\t\tif (s.NE()) {\n\t\t\t\tif (!dialog.showInput(out s, \"\", \"Console.Read\", screen: screen.ofActiveWindow) || s == \"\") return -1;\n\t\t\t}\n\t\t\tchar c = s[0];\n\t\t\t_read = s[1..];\n\t\t\treturn (int)c;\n\t\t}\n\t\t\n\t\tstring _read;\n\t}\n\t\n\tinternal static void RedirectConsole_() {\n\t\tprint.redirectConsoleOutput = true;\n\t\tConsole.SetIn(new _ConsoleReader());\n\t}\n}"
  },
  {
    "path": "Au/Internal/NamespaceDoc.cs",
    "content": "\n//These are not supported by DocFX, but used for code info.\n//rejected. Now code info for namespaces does not work.\n\n//namespace Au\n//{\n//\t/// <summary>\n//\t/// Main classes of the automation library, except triggers.\n//\t/// </summary>\n//\t[CompilerGenerated]\n//\tclass NamespaceDoc\n//\t{\n//\t}\n//}\n\n//namespace Au.Types\n//{\n//\t/// <summary>\n//\t/// Types of function parameters, exceptions, etc used in the automation library.\n//\t/// </summary>\n//\t[CompilerGenerated]\n//\tclass NamespaceDoc\n//\t{\n//\t}\n//}\n\n//namespace Au.Triggers\n//{\n//\t/// <summary>\n//\t/// Triggers: hotkeys, autotext, mouse, window.\n//\t/// </summary>\n//\t[CompilerGenerated]\n//\tclass NamespaceDoc\n//\t{\n//\t}\n//}\n\n//namespace Au.More\n//{\n//\t/// <summary>\n//\t/// Rarely used classes of the automation library.\n//\t/// </summary>\n//\t[CompilerGenerated]\n//\tclass NamespaceDoc\n//\t{\n//\t}\n//}\n"
  },
  {
    "path": "Au/Internal/NativeFont_.cs",
    "content": "﻿namespace Au.More;\n\n/// <summary>\n/// Creates and manages native font handle.\n/// </summary>\ninternal sealed class NativeFont_ : IDisposable {\n\tIntPtr _h;\n\tpublic IntPtr Handle => _h;\n\n\tpublic NativeFont_(IntPtr handle) { _h = handle; }\n\n\tpublic static implicit operator IntPtr(NativeFont_ f) => f?._h ?? default;\n\n\t~NativeFont_() { _Dispose(); }\n\tpublic void Dispose() { _Dispose(); GC.SuppressFinalize(this); }\n\tvoid _Dispose() {\n\t\tif (_h != default) { Api.DeleteObject(_h); _h = default; }\n\t}\n\n\tpublic NativeFont_(int dpi, string name, int height, bool bold = false, bool italic = false) {\n\t\t_h = Api.CreateFont(\n\t\t\t-Math2.MulDiv(height, dpi, 72),\n\t\t\tcWeight: bold ? 700 : 0, //FW_BOLD\n\t\t\tbItalic: italic ? 1 : 0,\n\t\t\t//bUnderline: underline ? 1 : 0,\n\t\t\t//bStrikeOut: strikeout ? 1 : 0,\n\t\t\tiCharSet: 1,\n\t\t\tpszFaceName: name);\n\t}\n\n\tpublic unsafe NativeFont_(int dpi, bool bold, bool italic, int height = 0) {\n\t\tApi.LOGFONT m = default;\n\t\tDpi.SystemParametersInfo(Api.SPI_GETICONTITLELOGFONT, sizeof(Api.LOGFONT), &m, dpi);\n\t\tif (bold) m.lfWeight = 700;\n\t\tif (italic) m.lfItalic = 1;\n\t\tif (height != 0) m.lfHeight = -Math2.MulDiv(height, dpi, 72);\n\t\t_h = Api.CreateFontIndirect(m);\n\t}\n\n\tpublic int HeightOnScreen {\n\t\tget {\n\t\t\tif (_heightOnScreen == 0) {\n\t\t\t\tusing var dc = new FontDC_(_h);\n\t\t\t\t_heightOnScreen = dc.MeasureEP(\"A\").height;\n\t\t\t}\n\t\t\treturn _heightOnScreen;\n\t\t}\n\t}\n\tint _heightOnScreen;\n\n\t//flags: loword dpi, 0x10000 bold, 0x20000 italic\n\tstatic unsafe NativeFont_ _CreateCached(int flags) {\n\t\tint dpi = flags & 0xffff;\n\t\tbool bold = 0 != (flags & 0x10000), italic = 0 != (flags & 0x20000);\n\t\t//if (0 != (flags & 0x100000)) return new NativeFont_(dpi, \"Verdana\", 9, bold, italic);\n\t\treturn new NativeFont_(dpi, bold, italic);\n\t}\n\n\tstatic readonly ConcurrentDictionary<int, NativeFont_> _d = new();\n\n\t/// <summary>\n\t/// Cached standard font used by most windows and controls.\n\t/// Usually it is <c>Segoe UI</c> 9.\n\t/// </summary>\n\tinternal static NativeFont_ RegularCached(int dpi) => _d.GetOrAdd(dpi, i => _CreateCached(i));\n\n\t/// <summary>\n\t/// Cached standard bold font used by most windows and controls.\n\t/// </summary>\n\tinternal static NativeFont_ BoldCached(int dpi) => _d.GetOrAdd(dpi | 0x10000, i => _CreateCached(i));\n}\n\n//currently not used\n///// <summary>\n///// Provides various versions of standard UI font.\n///// Per-monitor DPI-aware.\n///// </summary>\n///// <remarks>\n///// The properties return non-cached <c>Font</c> objects. It's safe to dispose them. It's OK to not dispose (GC will do; GDI+ fonts don't use much unmanaged memory).\n///// </remarks>\n//internal static class Fonts_\n//{\n//\t//info: we don't return cached Font objects, because we cannot protect them from disposing. The Font class is sealed.\n//\t//\tSystemFonts too, always creates new object.\n//\t//\tBut eg Brushes and SystemBrushes use cached object. It is not protected from disposing (would be exception later).\n\n//\t/// <summary>\n//\t/// Standard font used by most windows and controls.\n//\t/// Usually it is <c>Segoe UI</c> 9.\n//\t/// </summary>\n//\tpublic static Font Regular(int dpi) => Font.FromHfont(NativeFont_.RegularCached(dpi));\n\n//\t/// <summary>\n//\t/// Bold version of <see cref=\"Regular\"/> font.\n//\t/// </summary>\n//\tpublic static Font Bold(int dpi) => Font.FromHfont(NativeFont_.BoldCached(dpi));\n//}\n"
  },
  {
    "path": "Au/Internal/NativeScrollbar_.cs",
    "content": "﻿namespace Au.More;\n\n/// <summary>\n/// Manages native vertical or horizontal scrollbar of a window.\n/// </summary>\ninternal /*abstract*/ class NativeScrollbar_ {\n\treadonly bool _vertical;\n\treadonly Func<int, int> _itemStart, _itemEnd;\n\twnd _w;\n\tint _pos, _max, _nItems, _offset;\n\t\n\tpublic NativeScrollbar_(bool vertical, Func<int, int> itemStart, Func<int, int> itemEnd) {\n\t\t_vertical = vertical;\n\t\t_itemStart = itemStart;\n\t\t_itemEnd = itemEnd;\n\t}\n\t//public NativeScrollbar_(bool vertical) {\n\t//\t_vertical = vertical;\n\t//}\n\t\n\tpublic bool Visible {\n\t\tget => _visible;\n\t\tset => _Show(value, true);\n\t}\n\tbool _visible;\n\t\n\tvoid _Show(bool show, bool api) {\n\t\tif (_visible != show) {\n\t\t\t_visible = show;\n\t\t\tif (!show) {\n\t\t\t\tif (_pos != 0 && !_w.Is0) Api.SCROLLINFO.SetPos(_w, _vertical, 0, false);\n\t\t\t\t_pos = _max = _offset = 0;\n\t\t\t}\n\t\t\tif (api && !_w.Is0) Api.ShowScrollBar(_w, _vertical ? Api.SB_VERT : Api.SB_HORZ, show);\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// If need, shows or hides vertical and/or horizontal scrollbar of same window.\n\t/// When need to show or hide both, this function is 2 times faster than calling <see cref=\"Visible\"/> separately.\n\t/// </summary>\n\tpublic static void ShowVH(NativeScrollbar_ vert, bool showV, NativeScrollbar_ horz, bool showH) {\n\t\tDebug.Assert(vert._w == horz._w && vert._vertical && !horz._vertical);\n\t\tif (showV == vert.Visible && showH == horz.Visible) return;\n\t\tbool dif = showV != showH;\n\t\tvert._Show(showV, dif);\n\t\thorz._Show(showH, dif);\n\t\tif (!dif) Api.ShowScrollBar(vert._w, Api.SB_BOTH, showV); //2 times faster and 2 times less messages, except wm_paint\n\t}\n\t\n\t//public void SetRange(int max, int page) {\n\t//\t_max = max;\n\t//\t_pos = Math.Min(_pos, max);\n\t//\tApi.SCROLLINFO.SetRange(_w, _vertical, max + page - 1, page, true);\n\t//}\n\t\n\t//protected abstract int ItemStart(int i);\n\t//protected abstract int ItemEnd(int i);\n\t\n\tpublic void SetRange(int nItems) {\n\t\t_max = 0;\n\t\t_nItems = nItems;\n\t\tif (nItems > 1) {\n\t\t\tvar rc = _w.ClientRect;\n\t\t\tint to = _itemEnd(_nItems - 1) - (_vertical ? rc.Height : rc.Width);\n\t\t\tfor (int i = _nItems; --i >= 0;) { //how many items in the last page?\n\t\t\t\tif (_itemStart(i) < to) { _max = i + 1; break; }\n\t\t\t}\n\t\t}\n\t\t_SetPos(_pos);\n\t\tApi.SCROLLINFO.SetRange(_w, _vertical, _nItems - 1, _nItems - _max, true);\n\t}\n\t\n\tbool _SetPos(int pos) {\n\t\tpos = Math.Clamp(pos, 0, _max);\n\t\tif (pos == _pos) return false;\n\t\t_pos = pos;\n\t\t_offset = _pos == 0 ? 0 : _itemStart(_pos);\n\t\treturn true;\n\t}\n\t\n\tvoid _Scroll(int pos, int part) {\n\t\tif (!_SetPos(pos)) return;\n\t\tApi.SCROLLINFO.SetPos(_w, _vertical, _pos, true);\n\t\tPosChanged?.Invoke(this, part);\n\t}\n\t\n\t/// <summary>\n\t/// Gets current scroll position (index of top visible item).\n\t/// Setter sets scroll position, clamped 0-Max. Does not invalidate the control.\n\t/// </summary>\n\tpublic int Pos {\n\t\tget => _pos;\n\t\tset => _Scroll(value, -1);\n\t}\n\t\n\tpublic int Offset => _offset;\n\t\n\t/// <summary>\n\t/// Gets max scroll position. Returns 0 if no scrollbar.\n\t/// </summary>\n\tpublic int Max => _max;\n\t\n\t/// <summary>\n\t/// Gets or sets item count.\n\t/// Use setter to set item count when there is no scrollbar; asserts <c>!_visible</c>.\n\t/// </summary>\n\tpublic int NItems {\n\t\tget => _nItems;\n\t\tset {\n\t\t\tDebug.Assert(!_visible);\n\t\t\t_nItems = value;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// When scrollbar position changed.\n\t/// The <c>int</c> parameter is event source: if scrollbar, it is one of <c>Api.SB_</c> constants; if <see cref=\"Pos\"/>, it is -1; if wheel, it is -2 if down, -3 if up.\n\t/// </summary>\n\tpublic event Action<NativeScrollbar_, int> PosChanged;\n\t\n\tpublic bool WndProc(wnd w, int msg, nint wParam, nint lParam) {\n\t\t_w = w;\n\t\tswitch (msg) {\n\t\tcase Api.WM_NCDESTROY:\n\t\t\t_w = default;\n\t\t\tbreak;\n\t\tcase Api.WM_VSCROLL when _vertical:\n\t\tcase Api.WM_HSCROLL when !_vertical:\n\t\t\t_WmScroll(Math2.LoWord(wParam));\n\t\t\treturn true;\n\t\tcase Api.WM_MOUSEWHEEL:\n\t\t\t_WmScroll(Math2.HiShort(wParam) < 0 ? -2 : -3);\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\t\n\tvoid _WmScroll(int part) {\n\t\tif (!_visible) return;\n\t\tint pos = _pos;\n\t\tswitch (part) {\n\t\tcase Api.SB_THUMBTRACK:\n\t\t\tpos = Api.SCROLLINFO.GetTrackPos(_w, _vertical);\n\t\t\tbreak;\n\t\tcase Api.SB_LINEDOWN: pos++; break;\n\t\tcase Api.SB_LINEUP: pos--; break;\n\t\tcase Api.SB_PAGEDOWN or Api.SB_PAGEUP:\n\t\t\tvar rc = _w.ClientRect;\n\t\t\tint clientSize = _vertical ? rc.Height : rc.Width;\n\t\t\tif (part == Api.SB_PAGEDOWN) {\n\t\t\t\tfor (int to = _offset + clientSize; pos < _nItems && _itemEnd(pos) <= to;) pos++;\n\t\t\t} else {\n\t\t\t\tfor (int to = _offset - clientSize; pos >= 0 && _itemStart(pos) >= to;) pos--;\n\t\t\t\tpos++;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase Api.SB_TOP: pos = 0; break;\n\t\tcase Api.SB_BOTTOM: pos = _max; break;\n\t\tcase -2 or -3:\n\t\t\tint k = Api.SystemParametersInfo(Api.SPI_GETWHEELSCROLLLINES, 3);\n\t\t\tpos += part == -2 ? k : -k;\n\t\t\tbreak;\n\t\tdefault: return;\n\t\t}\n\t\t_Scroll(pos, part);\n\t}\n\t\n\t/// <summary>\n\t/// Calculates new focused item index when pressed key <c>Down</c>, <c>Up</c>, <c>PageDown</c>, <c>PageUp</c>, <c>End</c> or <c>Home</c>.\n\t/// </summary>\n\t/// <param name=\"i\">Current focused item index. Can be -1.</param>\n\t/// <param name=\"k\"></param>\n\t/// <remarks>\n\t/// This scrollbar must be vertical. Asserts.\n\t/// Returns unchanged <i>i</i> (even if -1) if <i>k</i> isn't a navigation key or if cannot change focused item.\n\t/// Works like standard list controls.\n\t/// </remarks>\n\tpublic int KeyNavigate(int i, KKey k) {\n\t\tDebug.Assert(_vertical);\n\t\tif (!_vertical || _nItems == 0) return i;\n\t\tswitch (k) {\n\t\tcase KKey.Home: i = 0; break;\n\t\tcase KKey.End: i = int.MaxValue; break;\n\t\tcase KKey.Down: i++; break;\n\t\tcase KKey.Up: if (i < 0) i = int.MaxValue; else i--; break;\n\t\tcase KKey.PageDown when i < _nItems - 1:\n\t\tcase KKey.PageUp when i > 0:\n\t\t\tint clientHeight = _w.ClientRect.Height;\n\t\t\tif (k == KKey.PageDown) {\n\t\t\t\tint to = _offset + clientHeight;\n\t\t\t\tif (_itemEnd(i + 1) > to) to = _itemStart(i + 1) + clientHeight;\n\t\t\t\twhile (i + 1 < _nItems && _itemEnd(i + 1) <= to) i++;\n\t\t\t} else {\n\t\t\t\tint to = _offset;\n\t\t\t\tif (i == _pos) to -= clientHeight;\n\t\t\t\twhile (i - 1 >= 0 && _itemStart(i - 1) >= to) i--;\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault: return i;\n\t\t}\n\t\t\n\t\treturn Math.Clamp(i, 0, _nItems - 1);\n\t}\n}\n"
  },
  {
    "path": "Au/Internal/NativeThread_.cs",
    "content": "namespace Au.More;\n\n/// <summary>\n/// Represents a thread created using API <c>CreateThread</c>.\n/// Use when need thread handle/id ASAP, or to call APC easier.\n/// </summary>\nunsafe class NativeThread_ {\n\tnint _handle;\n\tint _tid;\n\tbool _sta, _threadInited;\n\tAction _proc;\n\tnint _initEvent; //much faster than with ManualResetEvent or ManualResetEventSlim\n\n\t~NativeThread_() {\n\t\tApi.CloseHandle(_handle);\n\t\tif (_initEvent != 0) Api.CloseHandle(_initEvent);\n\t}\n\n\t/// <summary>\n\t/// Starts new background thread using API <c>CreateThread</c>.\n\t/// <para>Note: in thread proc use <see cref=\"OfThisThread\"/>, not a static field inited like <c>s_thread = new NativeThread_(...);</c>, because <i>s_thread</i> may be still null.</para>\n\t/// </summary>\n\t/// <param name=\"proc\">Thread procedure.</param>\n\t/// <param name=\"sta\">Set <c>ApartmentState.STA</c>.</param>\n\t/// <param name=\"waitInited\">Wait now until the thread procedure calls <see cref=\"ThreadInited\"/>.</param>\n\tpublic NativeThread_(Action proc, bool sta = true, bool waitInited = false) {\n\t\t_proc = proc;\n\t\t_sta = sta;\n\t\tif (waitInited) _initEvent = Api.CreateEvent2(default, true, false, null);\n\t\t_handle = Api.CreateThread(default, 0, &_Thread, GCHandle.Alloc(this), 0, out _tid);\n\t\tif (waitInited) {\n\t\t\tApi.WaitForSingleObject(_initEvent, -1);\n\t\t\tApi.CloseHandle(_initEvent);\n\t\t\t_initEvent = 0;\n\t\t}\n\t}\n\n\t/// <summary>\n\t/// Gets thread handle.\n\t/// The finalizer closes the handle. The object is not garbage-collected while the thread procedure is running.\n\t/// </summary>\n\tpublic nint Handle => _handle;\n\n\t/// <summary>\n\t/// Gets thread id.\n\t/// </summary>\n\tpublic nint Id => _tid;\n\n\tpublic static NativeThread_ OfThisThread => t_ofThisThread;\n\t[ThreadStatic] static NativeThread_ t_ofThisThread;\n\n\t[UnmanagedCallersOnly]\n\tstatic uint _Thread(GCHandle param) {\n\t\tvar t = param.Target as NativeThread_;\n\t\tparam.Free();\n\t\tt_ofThisThread = t;\n\t\tif (t._sta) Thread.CurrentThread.SetApartmentState(ApartmentState.STA);\n\t\tt._proc();\n\t\treturn 0;\n\t}\n\n\t/// <summary>\n\t/// The thread procedure must call this when finished thread initialization and going to run an alertable message loop.\n\t/// If constructor was called with <i>waitInited</i> <c>true</c>, it will return (stop waiting).\n\t/// If actions were queued, executes them now.\n\t/// Example: <c>NativeThread_.OfThisThread.ThreadInited();</c>\n\t/// Note: don't use code like <c>s_thread.ThreadInited();</c>, because <i>s_thread</i> (inited like <c>s_thread = new NativeThread_(...);</c>) may be still null.\n\t/// </summary>\n\tpublic void ThreadInited() {\n\t\tif (_initEvent != 0) Api.SetEvent(_initEvent);\n\t\t_threadInited = true;\n\t\tif (t_queue is { } q) {\n\t\t\tt_queue = null;\n\t\t\tq.Invoke();\n\t\t}\n\t}\n\n\tpublic void QueueAPC(Action a) {\n\t\tApi.QueueUserAPC(&_Apc, _handle, GCHandle.Alloc(a));\n\t}\n\t\n\t[ThreadStatic] static Action t_queue;\n\t\n\t[UnmanagedCallersOnly]\n\tstatic void _Apc(GCHandle param) {\n\t\tvar a = param.Target as Action;\n\t\tparam.Free();\n\t\tif (t_ofThisThread is {  } t && t._threadInited) a(); else t_queue += a;\n\t}\n\n\tpublic static void AlertableMessageLoop() {\n\t\tfor (; ; ) {\n\t\t\tvar k = Api.MsgWaitForMultipleObjectsEx(0, null, -1, Api.QS_ALLINPUT, Api.MWMO_ALERTABLE | Api.MWMO_INPUTAVAILABLE);\n\t\t\tif (k == 0) {\n\t\t\t\tif (!wait.doEvents()) break;\n\t\t\t} else if (k != Api.WAIT_IO_COMPLETION) {\n\t\t\t\tDebug_.Print(k);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au/Internal/PostToThisThread_.cs",
    "content": "﻿namespace Au.More;\n\n/// <summary>\n/// Executes actions in same UI thread as ctor.\n/// Use <see cref=\"OfThisThread\"/>. Cannot create more instances.\n/// </summary>\nclass PostToThisThread_ {\n\treadonly wnd _w;\n\treadonly Queue<Action> _q = new();\n\t\n\tPostToThisThread_() {\n\t\t_w = WndUtil.CreateWindowDWP_(messageOnly: true, t_wp = _WndProc);\n\t\tManagedThreadId = Environment.CurrentManagedThreadId;\n\t}\n\t\n\tpublic static PostToThisThread_ OfThisThread => t_default ??= new();\n\t[ThreadStatic] static PostToThisThread_ t_default;\n\t[ThreadStatic] static WNDPROC t_wp;\n\t\n\tpublic int ManagedThreadId { get; }\n\t\n\tpublic void Post(Action a) {\n\t\tbool post;\n\t\tlock (this) {\n\t\t\tpost = _q.Count == 0;\n\t\t\t_q.Enqueue(a);\n\t\t}\n\t\tif (post) _w.Post(Api.WM_USER);\n\t}\n\t\n\tnint _WndProc(wnd w, int message, nint wParam, nint lParam) {\n\t\tswitch (message) {\n\t\tcase Api.WM_USER:\n\t\t\t//print.it(_q.Count);\n\t\t\tobject o;\n\t\t\tlock (this) {\n\t\t\t\to = _q.Count switch { 0 => null, 1 => _q.Dequeue(), _ => _q.ToArray() };\n\t\t\t\t_q.Clear();\n\t\t\t}\n\t\t\tswitch (o) {\n\t\t\tcase Action a:\n\t\t\t\ta();\n\t\t\t\tbreak;\n\t\t\tcase Action[] a:\n\t\t\t\tforeach (var f in a) f();\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\treturn default;\n\t\t}\n\t\t\n\t\treturn Api.DefWindowProc(w, message, wParam, lParam);\n\t}\n}\n"
  },
  {
    "path": "Au/Internal/ProcessStarter_.cs",
    "content": "using Microsoft.Win32.SafeHandles;\n\nnamespace Au.More;\n\nunsafe struct ProcessStarter_ {\n\tpublic char[] cl;\n\tpublic Api.STARTUPINFO si;\n\tpublic uint flags;\n\tpublic string curDir;\n\tpublic string envVar;\n\tstring _exe; //for errors only\n\t\n\t/// <summary>\n\t/// Prepares parameters for API <ms>CreateProcess</ms> and similar.\n\t/// </summary>\n\t/// <param name=\"exe\">\n\t/// Full path of program file. If not full path, uses <see cref=\"folders.ThisApp\"/>. Uses <see cref=\"pathname.normalize\"/>.\n\t/// If <i>rawExe</i> <c>true</c>, does not use <c>Normalize</c>/<c>ThisApp</c>.\n\t/// </param>\n\t/// <param name=\"args\"><c>null</c> or command line arguments.</param>\n\t/// <param name=\"curDir\">\n\t/// Initial current directory of the new process.\n\t/// - If <c>null</c>, uses <c>Directory.GetCurrentDirectory()</c>.\n\t/// - Else if <i>rawCurDir</i>==<c>true</c>, uses raw <i>curDir</i> value.\n\t/// - Else if <c>\"\"</c>, calls <c>pathname.getDirectory(exe)</c>.\n\t/// - Else calls <see cref=\"pathname.expand\"/>.\n\t/// </param>\n\t/// <param name=\"envVar\"><c>null</c> or environment variables to pass to the new process together with variables of this process. Format: <c>\"var1=value1\\0var2=value2\\0\"</c>. If ends with <c>\"\\0\\0\"</c>, will pass only these variables.</param>\n\t/// <param name=\"rawExe\">Don't normalize <i>exe</i>.</param>\n\t/// <param name=\"rawCurDir\">Don't normalize <i>curDir</i>.</param>\n\tpublic ProcessStarter_(string exe, string args = null, string curDir = null, string envVar = null, bool rawExe = false, bool rawCurDir = false) {\n\t\tif (!rawExe) exe = pathname.normalize(exe, folders.ThisApp, PNFlags.DontExpandDosPath | PNFlags.DontPrefixLongPath);\n\t\t_exe = exe;\n\t\tcl = (args == null ? (\"\\\"\" + exe + \"\\\"\" + \"\\0\") : (\"\\\"\" + exe + \"\\\" \" + args + \"\\0\")).ToCharArray();\n\t\tif (curDir == null) this.curDir = Directory.GetCurrentDirectory(); //if null passed to CreateProcessWithTokenW, the new process does not inherit current directory of this process\n\t\telse this.curDir = rawCurDir ? curDir : (curDir.Length == 0 ? pathname.getDirectory(exe) : pathname.expand(curDir));\n\t\t\n\t\tsi.cb = Api.SizeOf<Api.STARTUPINFO>();\n\t\tsi.dwFlags = Api.STARTF_FORCEOFFFEEDBACK;\n\t\t\n\t\tflags = Api.CREATE_UNICODE_ENVIRONMENT;\n\t\t\n\t\tif (envVar != null && !envVar.Ends(\"\\0\\0\")) {\n\t\t\tvar es = Api.GetEnvironmentStrings();\n\t\t\tint len1; for (var k = es; ; k++) if (k[0] == 0 && k[1] == 0) { len1 = (int)(k - es) + 2; break; }\n\t\t\tint len2 = envVar.Length;\n\t\t\tvar t = new string('\\0', len1 + len2);\n\t\t\tfixed (char* p = t) {\n\t\t\t\tMemoryUtil.Copy(es, p, --len1 * 2);\n\t\t\t\tfor (int i = 0; i < envVar.Length; i++) p[len1 + i] = envVar[i];\n\t\t\t}\n\t\t\tthis.envVar = t;\n\t\t\tApi.FreeEnvironmentStrings(es);\n\t\t} else this.envVar = null;\n\t}\n\t\n\t/// <summary>\n\t/// Starts process using API <c>CreateProcess</c> or <c>CreateProcessAsUser</c>, without the feedback hourglass cursor.\n\t/// </summary>\n\t/// <param name=\"pi\">Receives <c>CreateProcessX</c> results. Will need to close handles in <i>pi</i>, eg <c>pi.Dispose</c>.</param>\n\t/// <param name=\"inheritUiaccess\">If this process has UAC integrity level uiAccess, let the new process inherit it.</param>\n\t/// <param name=\"inheritHandles\">API parameter <i>bInheritHandles</i>.</param>\n\tpublic bool StartL(out Api.PROCESS_INFORMATION pi, bool inheritUiaccess = false, bool inheritHandles = false) {\n\t\tif (inheritUiaccess && Api.OpenProcessToken(Api.GetCurrentProcess(), Api.TOKEN_QUERY | Api.TOKEN_DUPLICATE | Api.TOKEN_ASSIGN_PRIMARY, out Handle_ hToken)) {\n\t\t\tusing (hToken) return Api.CreateProcessAsUser(hToken, null, cl, null, null, inheritHandles, flags, envVar, curDir, si, out pi);\n\t\t} else {\n\t\t\treturn Api.CreateProcess(null, cl, null, null, inheritHandles, flags, envVar, curDir, si, out pi);\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Starts process using API <c>CreateProcess</c> or <c>CreateProcessAsUser</c>, without the feedback hourglass cursor.\n\t/// </summary>\n\t/// <param name=\"need\">Which field to set in <c>Result</c>.</param>\n\t/// <param name=\"inheritUiaccess\">If this process has UAC integrity level uiAccess, let the new process inherit it.</param>\n\t/// <exception cref=\"AuException\">Failed.</exception>\n\tpublic Result Start(Result.Need need = 0, bool inheritUiaccess = false) {\n\t\tbool suspended = need == Result.Need.NetProcess && !_NetProcessObject.IsFast, resetSuspendedFlag = false;\n\t\tif (suspended && 0 == (flags & Api.CREATE_SUSPENDED)) { flags |= Api.CREATE_SUSPENDED; resetSuspendedFlag = true; }\n\t\tbool ok = StartL(out var pi, inheritUiaccess);\n\t\tif (resetSuspendedFlag) flags &= ~Api.CREATE_SUSPENDED;\n\t\tif (!ok) throw new AuException(0, $\"*start process '{_exe}'\");\n\t\treturn new Result(pi, need, suspended);\n\t}\n\t\n\t/// <summary>\n\t/// Starts UAC Medium integrity level (IL) process from this admin process.\n\t/// </summary>\n\t/// <param name=\"need\">Which field to set in <c>Result</c>.</param>\n\t/// <exception cref=\"AuException\">Failed.</exception>\n\t/// <remarks>\n\t/// Actually the process will have the same IL and user session as the shell process (normally explorer).\n\t/// Fails if there is no shell process (API <c>GetShellWindow</c> fails) for more than 2 s from calling this func.\n\t/// Asserts and fails if this is not admin/system process. Caller should at first call <see cref=\"uacInfo.isAdmin\"/> or <see cref=\"uacInfo.IntegrityLevel\"/>.\n\t/// </remarks>\n\tpublic Result StartUserIL(Result.Need need = 0) {\n\t\tif (s_userToken == null) {\n\t\t\tDebug.Assert(uacInfo.isAdmin); //else cannot set privilege\n\t\t\tif (!SecurityUtil.SetPrivilege(\"SeIncreaseQuotaPrivilege\", true)) goto ge;\n\t\t\t\n\t\t\t//perf.first();\n#if false //works, but slow, eg 60 ms, even if we don't create task everytime\n\t\t\tvar s = $\"\\\"{folders.ThisAppBS}{(osVersion.is32BitProcess ? \"32\" : \"64\")}\\\\AuCpp.dll\\\",Cpp_RunDll\";\n\t\t\tAu.WinTaskScheduler.Scheduler.CreateTaskToRunProgramOnDemand(\"Au\", \"rundll32\", false, folders.System + \"rundll32.exe\", s);\n\t\t\t//Au.WinTaskScheduler.Scheduler.CreateTaskToRunProgramOnDemand(\"Au\", \"rundll32\", false, folders.System + \"notepad.exe\"); //slow too\n\t\t\t//perf.next();\n\t\t\tint pid = Au.WinTaskScheduler.Scheduler.RunTask(\"Au\", \"rundll32\");\n\t\t\t//perf.next();\n\t\t\t//print.it(pid);\n\t\t\tvar hUserProcess = Handle_.OpenProcess(pid);\n\t\t\t//print.it((IntPtr)hUserProcess);\n\t\t\tif(hUserProcess.Is0) goto ge;\n#else\n\t\t\tbool retry = false;\n\t\t\tg1:\n\t\t\tvar w = Api.GetShellWindow();\n\t\t\tif (w.Is0) { //if Explorer process killed or crashed, wait until it restarts\n\t\t\t\tif (!wait.until(2, () => !Api.GetShellWindow().Is0)) throw new AuException($\"*start process '{_exe}' as user. There is no shell process.\");\n\t\t\t\t500.ms();\n\t\t\t\tw = Api.GetShellWindow();\n\t\t\t}\n\t\t\t\n\t\t\tvar hUserProcess = Handle_.OpenProcess(w);\n\t\t\tif (hUserProcess.Is0) {\n\t\t\t\tif (retry) goto ge;\n\t\t\t\tretry = true; 500.ms(); goto g1;\n\t\t\t}\n\t\t\t\n\t\t\t//two other ways:\n\t\t\t//1. Enum processes and find one that has Medium IL. Unreliable, eg its token may be modified.\n\t\t\t//2. Start a service process. Let it start a Medium IL process like in QM2. Because LocalSystem can get token with WTSQueryUserToken.\n\t\t\t//tested: does not work with GetTokenInformation(TokenLinkedToken). Even if would work, in non-admin session it is wrong token.\n#endif\n\t\t\t//perf.nw();\n\t\t\t\n\t\t\tusing (hUserProcess) {\n\t\t\t\tif (Api.OpenProcessToken(hUserProcess, Api.TOKEN_DUPLICATE, out Handle_ hShellToken)) {\n\t\t\t\t\tusing (hShellToken) {\n\t\t\t\t\t\tconst uint access = Api.TOKEN_QUERY | Api.TOKEN_ASSIGN_PRIMARY | Api.TOKEN_DUPLICATE | Api.TOKEN_ADJUST_DEFAULT | Api.TOKEN_ADJUST_SESSIONID;\n\t\t\t\t\t\tif (Api.DuplicateTokenEx(hShellToken, access, null, Api.SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, Api.TOKEN_TYPE.TokenPrimary, out var userToken))\n\t\t\t\t\t\t\ts_userToken = new SafeAccessTokenHandle(userToken);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (s_userToken == null) goto ge;\n\t\t}\n\t\t\n\t\tbool suspended = need == Result.Need.NetProcess && !_NetProcessObject.IsFast, resetSuspendedFlag = false;\n\t\tif (suspended && 0 == (flags & Api.CREATE_SUSPENDED)) { flags |= Api.CREATE_SUSPENDED; resetSuspendedFlag = true; }\n\t\tbool ok = Api.CreateProcessWithTokenW(s_userToken.DangerousGetHandle(), 0, null, cl, flags, envVar, curDir, si, out var pi);\n\t\tif (resetSuspendedFlag) flags &= ~Api.CREATE_SUSPENDED;\n\t\tif (!ok) goto ge;\n\t\treturn new Result(pi, need, suspended);\n\t\t\n\t\tge: throw new AuException(0, $\"*start process '{_exe}' as user\");\n\t}\n\tstatic SafeAccessTokenHandle s_userToken;\n\t\n\t/// <summary>\n\t/// Results of <see cref=\"ProcessStarter_\"/> functions.\n\t/// </summary>\n\tpublic class Result {\n\t\t/// <summary>\n\t\t/// Which field to set.\n\t\t/// </summary>\n\t\tpublic enum Need {\n\t\t\tNone,\n\t\t\t//NativeHandle,\n\t\t\tWaitHandle,\n\t\t\tNetProcess,\n\t\t}\n\t\t\n\t\tpublic int pid;\n\t\tpublic WaitHandle waitHandle;\n\t\tpublic Process netProcess;\n\t\t\n\t\tinternal Result(in Api.PROCESS_INFORMATION pi, Need need, bool suspended) {\n\t\t\tpid = pi.dwProcessId;\n\t\t\tswitch (need) {\n\t\t\tcase Need.NetProcess:\n\t\t\t\tnetProcess = _NetProcessObject.Create(pi, suspended: suspended);\n\t\t\t\tbreak;\n\t\t\tcase Need.WaitHandle:\n\t\t\t\tpi.hThread.Dispose();\n\t\t\t\twaitHandle = new WaitHandle_(pi.hProcess, true);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tpi.Dispose();\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Creates new .NET Process object with attached handle and/or id.\n\t/// </summary>\n\tstatic class _NetProcessObject //FUTURE: remove if unused\n\t{\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if can create such object in a fast/reliable way. Else <see cref=\"Create\"/> will use <c>Process.GetProcessById</c>.\n\t\t/// It depends on .NET framework version, because uses private methods of Process class through reflection.\n\t\t/// </summary>\n\t\tpublic static bool IsFast { get; } = _CanSetHandleId();\n\t\t\n\t\tpublic static bool _CanSetHandleId() {\n\t\t\tconst BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod;\n\t\t\ts_mi1 = typeof(Process).GetMethod(\"SetProcessHandle\", flags);\n\t\t\ts_mi2 = typeof(Process).GetMethod(\"SetProcessId\", flags);\n\t\t\tif (s_mi1 != null && s_mi2 != null) return true;\n\t\t\tDebug.Assert(false);\n\t\t\treturn false;\n\t\t}\n\t\tstatic MethodInfo s_mi1, s_mi2;\n\t\t\n\t\t/// <summary>\n\t\t/// Creates new .NET Process object with attached handle and/or id.\n\t\t/// Can be specified both handle and id, or one of them (then .NET will open process or get id from handle when need).\n\t\t/// </summary>\n\t\tpublic static Process Create(IntPtr handle, int id) {\n\t\t\tif (!IsFast) {\n\t\t\t\tif (id == 0) id = process.processIdFromHandle(handle);\n\t\t\t\treturn Process.GetProcessById(id); //3 ms, much garbage, gets all processes, can throw\n\t\t\t}\n\t\t\t\n\t\t\tvar p = new Process();\n\t\t\tvar o = new object[1];\n\t\t\tif (handle != default) {\n\t\t\t\to[0] = new SafeProcessHandle(handle, true);\n\t\t\t\ts_mi1.Invoke(p, o);\n\t\t\t}\n\t\t\tif (id != 0) {\n\t\t\t\to[0] = id;\n\t\t\t\ts_mi2.Invoke(p, o);\n\t\t\t}\n\t\t\treturn p;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Creates new .NET Process object with attached handle and id.\n\t\t/// Closes thread handle. If suspended, resumes thread.\n\t\t/// </summary>\n\t\tpublic static Process Create(in Api.PROCESS_INFORMATION pi, bool suspended) {\n\t\t\ttry {\n\t\t\t\treturn Create(pi.hProcess, pi.dwProcessId);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tif (suspended) Api.ResumeThread(pi.hThread);\n\t\t\t\tpi.hThread.Dispose();\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au/Internal/Ptr_.cs",
    "content": "namespace Au.More;\n\n/// <summary>\n/// String functions for unmanaged <c>char*</c> or <c>byte*</c> strings.\n/// </summary>\ninternal static unsafe class Ptr_ {\n\t#region char*\n\t\n\t/// <summary>\n\t/// Gets span from start to <c>'\\0'</c>.\n\t/// </summary>\n\tpublic static RStr ToRSpan(char* p) => MemoryMarshal.CreateReadOnlySpanFromNullTerminated(p);\n\t\n\t/// <summary>\n\t/// Gets the number of characters in <i>p</i> until <c>'\\0'</c>.\n\t/// </summary>\n\t/// <param name=\"p\"><c>'\\0'</c>-terminated string. Can be <c>null</c>.</param>\n\tpublic static int Length(char* p) => MemoryMarshal.CreateReadOnlySpanFromNullTerminated(p).Length;\n\t\n\t/// <summary>\n\t/// Gets the number of characters in <i>p</i> until <c>'\\0'</c> or <i>max</i>.\n\t/// </summary>\n\t/// <param name=\"p\"><c>'\\0'</c>-terminated string. Can be null if <i>max</i> is 0.</param>\n\t/// <param name=\"max\">Max length to scan. Returns <i>max</i> if does not find <c>'\\0'</c>.</param>\n\tpublic static int Length(char* p, int max) {\n\t\tint i = new RStr(p, max).IndexOf('\\0');\n\t\treturn i < 0 ? max : i;\n\t}\n\t\n\t#endregion\n\t\n\t#region byte*\n\t\n\t/// <summary>\n\t/// Gets span from start to <c>'\\0'</c>.\n\t/// </summary>\n\tpublic static RByte ToRSpan(byte* p) => MemoryMarshal.CreateReadOnlySpanFromNullTerminated(p);\n\t\n\t/// <summary>\n\t/// Gets the number of bytes in <i>p</i> until <c>'\\0'</c>.\n\t/// </summary>\n\t/// <param name=\"p\"><c>'\\0'</c>-terminated string. Can be <c>null</c>.</param>\n\tpublic static int Length(byte* p) => MemoryMarshal.CreateReadOnlySpanFromNullTerminated(p).Length;\n\t\n\t/// <summary>\n\t/// Gets the number of bytes in <i>p</i> until <c>'\\0'</c> or <i>max</i>.\n\t/// </summary>\n\t/// <param name=\"p\"><c>'\\0'</c>-terminated string. Can be null if <i>max</i> is 0.</param>\n\t/// <param name=\"max\">Max length to scan. Returns <i>max</i> if does not find <c>'\\0'</c>.</param>\n\tpublic static int Length(byte* p, int max) {\n\t\tint i = new RByte(p, max).IndexOf((byte)0);\n\t\treturn i < 0 ? max : i;\n\t}\n\t\n\t#endregion\n}\n"
  },
  {
    "path": "Au/Internal/Serializer_.cs",
    "content": "﻿namespace Au.More;\n\n/// <summary>\n/// Binary-serializes and deserializes multiple values of types <c>int</c>, <c>string</c>, <c>string[]</c>, <c>byte[]</c> and <c>null</c>.\n/// Used mostly for sending parameters for IPC through pipe etc.\n/// Similar to <c>BinaryWriter</c>, but faster and less garbage. Much faster than <c>BinaryFormatter</c>, CSV, etc.\n/// Serializes all values into a <c>byte[]</c> in single call. If need to append, use <c>BinaryWriter</c> instead.\n/// </summary>\ninternal static unsafe class Serializer_ {\n\t/// <summary>\n\t/// Type of input and output values of <see cref=\"Serializer_\"/> functions.\n\t/// Has implicit conversions from/to <c>int</c> and <c>string</c>.\n\t/// </summary>\n\tpublic struct Value {\n\t\tpublic object Obj;\n\t\tpublic int Int;\n\t\tpublic VType Type;\n\t\t\n\t\tValue(int i) { Obj = null; Int = i; Type = VType.Int; }\n\t\tValue(object o, VType type) { Obj = o; Int = 0; Type = o != null ? type : VType.Null; }\n\t\t\n\t\tpublic static implicit operator Value(int i) => new Value(i);\n\t\tpublic static implicit operator Value(string s) => new Value(s, VType.String);\n\t\tpublic static implicit operator Value(string[] a) => new Value(a, VType.StringArray);\n\t\tpublic static implicit operator Value(byte[] a) => new Value(a, VType.ByteArray);\n\t\t\n\t\tpublic static implicit operator int(Value a) => a.Int;\n\t\tpublic static implicit operator string(Value a) => a.Obj as string;\n\t\tpublic static implicit operator string[](Value a) => a.Obj as string[];\n\t\tpublic static implicit operator byte[](Value a) => a.Obj as byte[];\n\t}\n\t\n\tpublic enum VType { Null, Int, String, StringArray, ByteArray }\n\t\n\t/// <summary>\n\t/// Serializes multiple values of types <c>int</c>, <c>string</c>, <c>string[]</c> and <c>null</c>.\n\t/// The returned array can be passed to <see cref=\"Deserialize\"/>.\n\t/// </summary>\n\tpublic static byte[] Serialize(params Value[] a) => _Serialize(false, a);\n\t\n\t/// <summary>\n\t/// Serializes multiple values of types <c>int</c>, <c>string</c>, <c>string[]</c> and <c>null</c>.\n\t/// Unlike <see cref=\"Serialize\"/>, in the first 4 bytes writes the size of data that follows.\n\t/// Can be used with pipes or other streams where data size is initially unknown: read 4 bytes as <c>int dataSize</c>; <c>var b=new byte[dataSize]</c>, read it, pass <c>b</c> to <see cref=\"Deserialize\"/>. \n\t/// </summary>\n\tpublic static byte[] SerializeWithSize(params Value[] a) => _Serialize(true, a);\n\t\n\tstatic byte[] _Serialize(bool withSize, Value[] a) {\n\t\tint size = 4;\n\t\tif (withSize) size += 4;\n\t\tfor (int i = 0; i < a.Length; i++) {\n\t\t\tsize++;\n\t\t\tswitch (a[i].Type) {\n\t\t\tcase VType.Int: size += 4; break;\n\t\t\tcase VType.String: size += 4 + (a[i].Obj as string).Length * 2; break;\n\t\t\tcase VType.StringArray:\n\t\t\t\tint z = 4;\n\t\t\t\tforeach (var v in a[i].Obj as string[]) z += 4 + v.Lenn() * 2;\n\t\t\t\tsize += z;\n\t\t\t\tbreak;\n\t\t\tcase VType.ByteArray: size += 4 + (a[i].Obj as byte[]).Length; break;\n\t\t\t}\n\t\t}\n\t\tvar ab = new byte[size];\n\t\tfixed (byte* b0 = ab) {\n\t\t\tbyte* b = b0;\n\t\t\tif (withSize) { *(int*)b = ab.Length - 4; b += 4; }\n\t\t\t*(int*)b = a.Length; b += 4;\n\t\t\tfor (int i = 0; i < a.Length; i++) {\n\t\t\t\tvar ty = a[i].Type;\n\t\t\t\t*b++ = (byte)ty;\n\t\t\t\tswitch (ty) {\n\t\t\t\tcase VType.Int:\n\t\t\t\t\t*(int*)b = a[i].Int;\n\t\t\t\t\tb += 4;\n\t\t\t\t\tbreak;\n\t\t\t\tcase VType.String:\n\t\t\t\t\tvar s = a[i].Obj as string;\n\t\t\t\t\t_AddString(s);\n\t\t\t\t\tbreak;\n\t\t\t\tcase VType.StringArray:\n\t\t\t\t\tvar k = a[i].Obj as string[];\n\t\t\t\t\t*(int*)b = k.Length; b += 4;\n\t\t\t\t\tforeach (var v in k) {\n\t\t\t\t\t\tif (v != null) _AddString(v);\n\t\t\t\t\t\telse { *(int*)b = -1; b += 4; }\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase VType.ByteArray:\n\t\t\t\t\tvar u = a[i].Obj as byte[];\n\t\t\t\t\t*(int*)b = u.Length; b += 4;\n\t\t\t\t\tu.CopyTo(ab, b - b0);\n\t\t\t\t\tb += u.Length;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tDebug.Assert((b - b0) == size);\n\t\t\t\n\t\t\tvoid _AddString(string s) {\n\t\t\t\t*(int*)b = s.Length; b += 4;\n\t\t\t\tvar c = (char*)b;\n\t\t\t\tfor (int j = 0; j < s.Length; j++) c[j] = s[j];\n\t\t\t\tb += s.Length * 2;\n\t\t\t}\n\t\t}\n\t\treturn ab;\n\t}\n\t\n\t/// <summary>\n\t/// Deserializes values serialized by <see cref=\"Serialize\"/>.\n\t/// Returns array of values passed to <c>Serialize</c>.\n\t/// </summary>\n\tpublic static Value[] Deserialize(RByte serialized) {\n\t\tfixed (byte* b0 = serialized) {\n\t\t\tbyte* b = b0;\n\t\t\tint n = *(int*)b; b += 4;\n\t\t\tvar a = new Value[n];\n\t\t\tfor (int i = 0; i < n; i++) {\n\t\t\t\tswitch ((VType)(*b++)) {\n\t\t\t\tcase VType.Null:\n\t\t\t\t\tbreak;\n\t\t\t\tcase VType.Int:\n\t\t\t\t\ta[i] = *(int*)b; b += 4;\n\t\t\t\t\tbreak;\n\t\t\t\tcase VType.String:\n\t\t\t\t\ta[i] = _GetString();\n\t\t\t\t\tbreak;\n\t\t\t\tcase VType.StringArray:\n\t\t\t\t\tvar k = new string[*(int*)b]; b += 4;\n\t\t\t\t\tfor (int j = 0; j < k.Length; j++) k[j] = _GetString();\n\t\t\t\t\ta[i] = k;\n\t\t\t\t\tbreak;\n\t\t\t\tcase VType.ByteArray:\n\t\t\t\t\tint len = *(int*)b; b += 4;\n\t\t\t\t\ta[i] = serialized.Slice((int)(b - b0), len).ToArray();\n\t\t\t\t\tb += len;\n\t\t\t\t\tbreak;\n\t\t\t\tdefault: throw new ArgumentException();\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn a;\n\t\t\t\n\t\t\t[MethodImpl(MethodImplOptions.AggressiveInlining)]\n\t\t\tstring _GetString() {\n\t\t\t\tint len = *(int*)b; b += 4;\n\t\t\t\tif (len == -1) return null;\n\t\t\t\tvar R = new string((char*)b, 0, len);\n\t\t\t\tb += len * 2;\n\t\t\t\treturn R;\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au/Internal/SharedMemory_.cs",
    "content": "namespace Au.More;\n\n/// <summary>\n/// Memory shared by all processes using this library.\n/// </summary>\n[StructLayout(LayoutKind.Sequential, Size = c_size)]\nunsafe struct SharedMemory_ {\n\t#region variables used by our library classes\n\t//Declare variables used by our library classes.\n\t//Be careful:\n\t//1. Some type sizes are different in 32 and 64 bit process.\n\t//\tSolution: Use long and cast to IntPtr etc. For wnd use int.\n\t//2. The memory may be used by processes that use different library versions.\n\t//\tSolution: In new library versions don't change struct sizes and old members.\n\t//\t\tMaybe reserve some space for future members. If need more, add new struct.\n\t//\t\tUse eg [StructLayout(LayoutKind.Sequential, Size = 16)].\n\t\n\t//reserve 16 for some header, eg shared memory version.\n\t[StructLayout(LayoutKind.Sequential, Size = 16)] struct _Header { }\n\t_Header _h;\n\t\n\tinternal PrintServer.SharedMemoryData_ outp;\n\tinternal Triggers.ActionTriggers.SharedMemoryData_ triggers;\n\tinternal WindowsHook.SharedMemoryData_ winHook;\n\tinternal perf.Instance perf;\n\tinternal script.SharedMemoryData_ script;\n\t//internal ScriptEditor.SharedMemoryData_ editor;\n\t\n\t//public const int TasksDataSize_ = 0x4000;\n\t//internal struct TasksData_ { public int size; public fixed byte data[TasksDataSize_]; }\n\t//internal TasksData_ tasks;\n\t\n\t#endregion\n\t\n\tconst int c_size = 0x200000; //2 MB\n\t\n\tstatic SharedMemory_() {\n\t\tPtr = (SharedMemory_*)Mapping.CreateOrOpen(\"Au-memory-lib\", c_size).Mem;\n\t}\n\t\n\t/// <summary>\n\t/// Pointer to the shared memory.\n\t/// </summary>\n\tpublic static readonly SharedMemory_* Ptr;\n\t\n\t/// <summary>\n\t/// Gets pointer to the shared memory \"return data\" buffer.\n\t/// Used by <see cref=\"WndCopyData.Return\"/>.\n\t/// </summary>\n\tpublic static byte* ReturnDataPtr => (byte*)Ptr + c_size / 2;\n\t\n\t/// <summary>\n\t/// Size of <see cref=\"ReturnDataPtr\"/> buffer, 1 MB.\n\t/// </summary>\n\tpublic const int ReturnDataSize = c_size / 2;\n\t\n\t/// <summary>\n\t/// Shared memory pointer and mapping handle.\n\t/// </summary>\n\tpublic struct Mapping : IDisposable {\n\t\tIntPtr _hMapping;\n\t\tvoid* _mem;\n\t\t\n\t\tpublic void* Mem => _mem;\n\t\t\n\t\t/// <summary>\n\t\t/// Created new memory. If <c>false</c> - opened existing.\n\t\t/// </summary>\n\t\tpublic bool Created { get; }\n\t\t\n\t\tinternal Mapping(IntPtr h, void* m, bool created) {\n\t\t\t_hMapping = h;\n\t\t\t_mem = m;\n\t\t\tCreated = created;\n\t\t}\n\t\t\n\t\tpublic void Dispose() {\n\t\t\tif (_mem != null) { Api.UnmapViewOfFile(_mem); _mem = null; }\n\t\t\tif (_hMapping != default) { Api.CloseHandle(_hMapping); _hMapping = default; }\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Creates named shared memory of specified size. Opens if already exists.\n\t\t/// Returns <c>Mapping</c> variable that contains shared memory address in this process.\n\t\t/// </summary>\n\t\t/// <param name=\"name\">Shared memory name. Case-insensitive.</param>\n\t\t/// <param name=\"size\">Shared memory size. Ignored if the shared memory already exists.</param>\n\t\t/// <exception cref=\"AuException\">The API failed.</exception>\n\t\t/// <remarks>\n\t\t/// Calls API <ms>CreateFileMapping</ms> and API <ms>MapViewOfFile</ms>.\n\t\t/// The shared memory is alive at least until this process ends or the returned <c>Mapping</c> variable disposed. Other processes can keep the memory alive even after that.\n\t\t/// </remarks>\n\t\tpublic static Mapping CreateOrOpen(string name, int size) {\n\t\t\t//CONSIDER: don't use Api.SECURITY_ATTRIBUTES.ForLowIL. Speed with it 2.7 ms, without 1.7 ms.\n\t\t\t//\tBut then cannot use this library in low IL processes. Probably never used anyway.\n\t\t\t//\tTry to move everything to the cpp dll.\n\t\t\t\n\t\t\tvar hm = Api.CreateFileMapping((IntPtr)~0, Api.SECURITY_ATTRIBUTES.ForLowIL, Api.PAGE_READWRITE, 0, (uint)size, name);\n\t\t\tif (!hm.Is0) {\n\t\t\t\tbool created = lastError.code != Api.ERROR_ALREADY_EXISTS;\n\t\t\t\tvar mem = Api.MapViewOfFile(hm, 0x000F001F, 0, 0, 0); //FILE_MAP_ALL_ACCESS\n\t\t\t\tif (mem != default) return new(hm, mem, created);\n\t\t\t\thm.Dispose();\n\t\t\t}\n\t\t\tthrow new AuException(0, \"*open shared memory\");\n\t\t}\n\t\t\n\t\tpublic static bool TryOpenExisting(string name, out Mapping r) {\n\t\t\tr = default;\n\t\t\tvar hm = Api.OpenFileMapping(0x2, false, name); //FILE_MAP_WRITE\n\t\t\tif (hm.Is0) return false;\n\t\t\tvar mem = Api.MapViewOfFile(hm, 0x000F001F, 0, 0, 0); //FILE_MAP_ALL_ACCESS\n\t\t\tif (mem == default) { hm.Dispose(); return false; }\n\t\t\tr = new(hm, mem, false);\n\t\t\treturn true;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au/Internal/StaTaskScheduler_.cs",
    "content": "//\n// Copyright (c) Microsoft. All rights reserved.\n// Licensed under the MIT license. See LICENSE.md file in the project root for full license information.\n//\n\nnamespace Au.More;\n\n/// <summary>Provides a scheduler that uses STA threads.</summary>\nsealed class StaTaskScheduler_ : TaskScheduler, IDisposable {\n\t/// <summary>\n\t/// Static auto-created <c>StaTaskScheduler_</c> instance with 4 threads.\n\t/// </summary>\n\tpublic static new StaTaskScheduler_ Default => _default.Value;\n\treadonly static Lazy<StaTaskScheduler_> _default = new Lazy<StaTaskScheduler_>(() => new StaTaskScheduler_(4)); //info: 3-4 is optimal for getting icons\n\t\n\t/// <summary>Stores the queued tasks to be executed by our pool of STA threads.</summary>\n\tprivate BlockingCollection<Task> _tasks;\n\t/// <summary>The STA threads used by the scheduler.</summary>\n\tprivate readonly List<Thread> _threads;\n\t\n\t/// <summary>Initializes a new instance of the <c>StaTaskScheduler</c> class with the specified concurrency level.</summary>\n\t/// <param name=\"numberOfThreads\">The number of threads that should be created and used by this scheduler.</param>\n\tpublic StaTaskScheduler_(int numberOfThreads) {\n\t\t// Validate arguments\n\t\tif (numberOfThreads < 1) throw new ArgumentOutOfRangeException(nameof(numberOfThreads));\n\t\t\n\t\t// Initialize the tasks collection\n\t\t_tasks = new BlockingCollection<Task>();\n\t\t\n\t\t// Create the threads to be used by this scheduler\n\t\tvar a = new List<Thread>(numberOfThreads);\n\t\tfor (int i = 0; i < numberOfThreads; i++) {\n\t\t\tvar thread = new Thread(() => {\n\t\t\t\t// Continually get the next task and try to execute it.\n\t\t\t\t// This will continue until the scheduler is disposed and no more tasks remain.\n\t\t\t\tforeach (var t in _tasks.GetConsumingEnumerable()) {\n\t\t\t\t\tTryExecuteTask(t);\n\t\t\t\t}\n\t\t\t}) { IsBackground = true };\n\t\t\tthread.SetApartmentState(ApartmentState.STA);\n\t\t\ta.Add(thread);\n\t\t}\n\t\t_threads = a;\n\t\t\n\t\t// Start all of the threads\n\t\t_threads.ForEach(t => t.Start());\n\t}\n\t\n\t/// <summary>Queues a Task to be executed by this scheduler.</summary>\n\t/// <param name=\"task\">The task to be executed.</param>\n\tprotected override void QueueTask(Task task) =>\n\t\t// Push it into the blocking collection of tasks\n\t\t_tasks.Add(task);\n\t\n\t/// <summary>Provides a list of the scheduled tasks for the debugger to consume.</summary>\n\t/// <returns>An enumerable of all tasks currently scheduled.</returns>\n\tprotected override IEnumerable<Task> GetScheduledTasks() =>\n\t\t// Serialize the contents of the blocking collection of tasks for the debugger\n\t\t_tasks.ToArray();\n\t\n\t/// <summary>Determines whether a Task may be inlined.</summary>\n\t/// <param name=\"task\">The task to be executed.</param>\n\t/// <param name=\"taskWasPreviouslyQueued\">Whether the task was previously queued.</param>\n\t/// <returns><c>true</c> if the task was successfully inlined; otherwise, <c>false</c>.</returns>\n\tprotected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) =>\n\t\t// Try to inline if the current thread is STA\n\t\t//Thread.CurrentThread.GetApartmentState() == ApartmentState.STA &&\n\t\t//\tTryExecuteTask(task);\n\t\tfalse; //important. Never run in thread that calls Task.Wait or Task.Result.\n\t\n\t/// <summary>Gets the maximum concurrency level supported by this scheduler.</summary>\n\tpublic override int MaximumConcurrencyLevel => _threads.Count;\n\t\n\t/// <summary>\n\t/// Cleans up the scheduler by indicating that no more tasks will be queued.\n\t/// This method blocks until all threads successfully shutdown.\n\t/// </summary>\n\tpublic void Dispose() {\n\t\tif (_tasks != null) {\n\t\t\t// Indicate that no new tasks will be coming in\n\t\t\t_tasks.CompleteAdding();\n\t\t\t\n\t\t\t// Wait for all threads to finish processing tasks\n\t\t\tforeach (var thread in _threads) thread.Join();\n\t\t\t\n\t\t\t// Cleanup\n\t\t\t_tasks.Dispose();\n\t\t\t_tasks = null;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au/Internal/StringBuilder_.cs",
    "content": "﻿namespace Au.More;\n\n/// <summary>\n/// Provides a cached reusable instance of <c>StringBuilder</c> per thread. It's an optimization that reduces the number of instances constructed and collected.\n/// Used like <c>using(new StringBuilder_(out var b)) { b.Append(\"example\"); var s = b.ToString(); }</c>.\n/// </summary>\n/// <remarks>\n/// This is a modified copy of the .NET internal <c>StringBuilderCache</c> class.\n/// </remarks>\ninternal struct StringBuilder_ : IDisposable {\n\tStringBuilder _sb;\n\t\n\t/// <summary>\n\t/// The cached <c>StringBuilder</c> has this <c>Capacity</c>. The cache is not used if <i>capacity</i> is bigger.\n\t/// </summary>\n\tpublic const int Capacity = 2000;\n\t\n\t[ThreadStatic] private static StringBuilder t_cached;\n\t\n\t/// <summary>\n\t/// Gets a new or cached/cleared <c>StringBuilder</c> of the specified or bigger capacity.\n\t/// </summary>\n\t/// <param name=\"capacity\">\n\t/// Min needed <c>StringBuilder.Capacity</c>. If less than <c>Capacity</c> (2000), uses <c>Capacity</c>. If more than <c>Capacity</c>, does not use the cache.\n\t/// Use this parameter only when the needed capacity is variable; else either use default if need default or smaller, or don't use <c>StringBuilder_</c> if need bigger.\n\t/// </param>\n\tpublic StringBuilder_(out StringBuilder sb, int capacity = Capacity) {\n\t\tif (capacity <= Capacity) {\n\t\t\tcapacity = Capacity;\n\t\t\tvar b = t_cached;\n\t\t\tif (b != null) {\n\t\t\t\tt_cached = null;\n\t\t\t\tb.Clear();\n\t\t\t\tsb = _sb = b;\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tsb = _sb = new StringBuilder(capacity);\n\t}\n\t\n\t/// <summary>\n\t/// Releases the <c>StringBuilder</c> to the cache.\n\t/// </summary>\n\tpublic void Dispose() {\n\t\tif (_sb.Capacity == Capacity) t_cached = _sb;\n\t\t_sb = null;\n\t}\n}\n"
  },
  {
    "path": "Au/Internal/Util_.cs",
    "content": "using System.Windows;\n\nnamespace Au.More;\n\nstatic unsafe class Not_ {\n\t//internal static void NullCheck<T>(this T t, string paramName = null) where T : class {\n\t//\tif (t is null) throw new ArgumentNullException(paramName);\n\t//}\n\t\n\t/// <summary>\n\t/// Same as <c>ArgumentNullException.ThrowIfNull</c>.\n\t/// It's pity, they removed operator <c>!!</c> from C# 11.\n\t/// </summary>\n\tinternal static void Null(object o,\n\t\t[CallerArgumentExpression(\"o\")] string paramName = null) {\n\t\tif (o is null) throw new ArgumentNullException(paramName);\n\t}\n\tinternal static void Null(object o1, object o2,\n\t\t[CallerArgumentExpression(\"o1\")] string paramName1 = null,\n\t\t[CallerArgumentExpression(\"o2\")] string paramName2 = null) {\n\t\tif (o1 is null) throw new ArgumentNullException(paramName1);\n\t\tif (o2 is null) throw new ArgumentNullException(paramName2);\n\t}\n\tinternal static void Null(object o1, object o2, object o3,\n\t\t[CallerArgumentExpression(\"o1\")] string paramName1 = null,\n\t\t[CallerArgumentExpression(\"o2\")] string paramName2 = null,\n\t\t[CallerArgumentExpression(\"o3\")] string paramName3 = null) {\n\t\tif (o1 is null) throw new ArgumentNullException(paramName1);\n\t\tif (o2 is null) throw new ArgumentNullException(paramName2);\n\t\tif (o3 is null) throw new ArgumentNullException(paramName3);\n\t}\n\tinternal static void Null(object o1, object o2, object o3, object o4,\n\t\t[CallerArgumentExpression(\"o1\")] string paramName1 = null,\n\t\t[CallerArgumentExpression(\"o2\")] string paramName2 = null,\n\t\t[CallerArgumentExpression(\"o3\")] string paramName3 = null,\n\t\t[CallerArgumentExpression(\"o4\")] string paramName4 = null) {\n\t\tif (o1 is null) throw new ArgumentNullException(paramName1);\n\t\tif (o2 is null) throw new ArgumentNullException(paramName2);\n\t\tif (o3 is null) throw new ArgumentNullException(paramName3);\n\t\tif (o4 is null) throw new ArgumentNullException(paramName4);\n\t}\n\tinternal static void Null(void* o,\n\t\t[CallerArgumentExpression(\"o\")] string paramName = null) {\n\t\tif (o is null) throw new ArgumentNullException(paramName);\n\t}\n\tinternal static T NullRet<T>(T o,\n\t\t[CallerArgumentExpression(\"o\")] string paramName = null) where T : class {\n\t\tif (o is null) throw new ArgumentNullException(paramName);\n\t\treturn o;\n\t}\n}\n\nstatic class WpfUtil_ {\n\t/// <summary>\n\t/// <c>true</c> if <c>SystemParameters.HighContrast</c> and <c>ColorInt.GetPerceivedBrightness(SystemColors.ControlColor)&lt;=0.5</c>.\n\t/// </summary>\n\tpublic static bool IsHighContrastDark {\n\t\tget {\n\t\t\tif (!SystemParameters.HighContrast) return false; //fast, cached\n\t\t\tvar col = (ColorInt)SystemColors.ControlColor; //fast, cached\n\t\t\tvar v = ColorInt.GetPerceivedBrightness(col.argb, false);\n\t\t\treturn v <= .5;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au/Internal/misc_.cs",
    "content": "namespace Au.More;\n\n/// <summary>\n/// Dictionary with case-insensitive string keys (uses <see cref=\"StringComparer.OrdinalIgnoreCase\"/>).\n/// </summary>\n/// <remarks>\n/// This class can be used instead of <see cref=\"Dictionary{TKey, TValue}\"/>.\n/// Unlike <c>Dictionary</c>, keys remain case-insensitive after deserialization (<see cref=\"System.Text.Json.JsonSerializer\"/>, <see cref=\"JSettings\"/> etc).\n/// Another way to deserialize case-insensitive dictionaries - <c>JsonObjectCreationHandling.Populate</c> in serialization options or attribute.\n/// </remarks>\nclass DictionaryI_<TValue> : Dictionary<string, TValue> {\n\t/// <summary>\n\t/// Calls base constructor with <see cref=\"StringComparer.OrdinalIgnoreCase\"/>.\n\t/// </summary>\n\tpublic DictionaryI_() : base(StringComparer.OrdinalIgnoreCase) { }\n\t\n\t/// <summary>\n\t/// Calls base constructor with <see cref=\"StringComparer.OrdinalIgnoreCase\"/>.\n\t/// </summary>\n\tpublic DictionaryI_(int capacity) : base(capacity, StringComparer.OrdinalIgnoreCase) { }\n}\n"
  },
  {
    "path": "Au/Internal/tables.cs",
    "content": "namespace Au.More\n{\n\t/// <summary>\n\t/// Lookup tables for various functions of this library.\n\t/// </summary>\n\tunsafe static class Tables_\n\t{\n\t\tstatic Tables_() {\n\t\t\tvar t = new byte[55];\n\t\t\tfor (int u = 0; u < t.Length; u++) {\n\t\t\t\tchar c = (char)(u + '0');\n\t\t\t\tif (c >= '0' && c <= '9') t[u] = (byte)u;\n\t\t\t\telse if (c >= 'A' && c <= 'F') t[u] = (byte)(c - ('A' - 10));\n\t\t\t\telse if (c >= 'a' && c <= 'f') t[u] = (byte)(c - ('a' - 10));\n\t\t\t\telse t[u] = 0xFF;\n\t\t\t}\n\t\t\tHex = t;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Table for <see cref=\"Convert2.HexDecode\"/> and co.\n\t\t/// </summary>\n\t\tpublic static readonly byte[] Hex;\n\n\t\t/// <summary>\n\t\t/// Native-memory <c>char[0x10000]</c> containing lower-case versions of the first 0x10000 characters.\n\t\t/// </summary>\n\t\tpublic static char* LowerCase {\n\t\t\tget { var v = _lcTable; if (v == null) _lcTable = v = Cpp.Cpp_LowercaseTable(); return v; } //why operator ??= cannot be used with pointers?\n\t\t}\n\t\tstatic char* _lcTable;\n\t\t//never mind: this library does not support ucase/lcase chars 0x10000-0x100000 (surrogate pairs).\n\t\t//\tTested with IsUpper/IsLower: about 600 such chars exist. ToUpper/ToLower can convert 40 of them. Equals/StartsWith/IndexOf/etc fail.\n\t}\n}\n"
  },
  {
    "path": "Au/Other/PrintServer.cs",
    "content": "//#define NEED_CALLER //rejected. Too slow and generates much garbage.\n\n/*\nSingle global server is supported in this user session.\nSingle local server is supported in this process.\n\nServer does not implement an output window etc. It just collects messages and notifies an output window. Asynchronously.\n\nHow global output server/client is implemented:\n\tSingle server and multiple clients.\n\tServer receives messages sent by clients.\n\tClients - processes that send text messages to the server. The server's process also can be client.\n\tFor IPC is used mailslot, waitable timer and shared memory (SM).\n\tServer:\n\t\tCreates mailslot and timer. Sets a bool about it in SM.\n\t\tWaits for timer and reads messages from mailslot.\n\t\t\tFor better reliability, also checks mailslot periodically.\n\t\t\tIf messages available, notifies an output window.\n\t\tOn exit clears the bool in SM.\n\tClient, when sending a text message:\n\t\tIf the SM bool is not set - discards the message, and closes mailslot if was open. Else:\n\t\tIf not still done, opens mailslot.\n\t\tWrites message to mailslot.\n\t\tSets timer if not set. Uses another bool in SM to indicate that the timer is set; server clears it.\n\nHow local output server/client is implemented:\n\tSimilar to global. Differences:\n\tSingle server and single client (the same process).\n\tUses waitable timer, but not mailslot/SM. Instead of mailslot, adds messages directly to _messages. Instead of SM, uses static variables.\n\n*/\n\nnamespace Au.More {\n\t/// <summary>\n\t/// Receives messages sent by <see cref=\"print.it\"/>.\n\t/// </summary>\n\t/// <remarks>\n\t/// If server is global, clients can be multiple processes, including this. Else only this process.\n\t/// Works asynchronously, to make writing messages faster.\n\t/// When a client writes a message, the message arrives to the server with some delay and is placed in a queue.\n\t/// You then can get/remove messages from the queue (call <see cref=\"GetMessage\"/>) and display them in a window (for example).\n\t/// You can be notified about new messages.\n\t/// \n\t/// Recommended setup (see example):\n\t/// 1. When your application starts, create a <c>PrintServer</c> instance and assign to a static variable. Call <see cref=\"Start\"/>.\n\t/// 2. When your application creates its output window, call <see cref=\"SetNotifications\"/> to set window/message for notifications.\n\t/// 3. In window procedure, when received the notification message, get/remove/display all new output messages.\n\t/// 4. Call <see cref=\"Stop\"/> when closing the window.\n\t/// </remarks>\n\t/// <example>\n\t/// Simple program with output window.\n\t/// <code><![CDATA[\n\t/// using System.Windows.Forms;\n\t/// \n\t/// class OutputFormExample : Form {\n\t/// \tTextBox _tb;\n\t/// \n\t/// \tpublic OutputFormExample()\n\t/// \t{\n\t/// \t\t_tb = new TextBox();\n\t/// \t\t_tb.ReadOnly = true;\n\t/// \t\t_tb.Multiline = true;\n\t/// \t\t_tb.ScrollBars = ScrollBars.Both;\n\t/// \t\t_tb.WordWrap = false;\n\t/// \t\t_tb.Dock = DockStyle.Fill;\n\t/// \t\t_tb.TabStop = false;\n\t/// \t\tthis.Controls.Add(_tb);\n\t/// \t}\n\t/// \n\t/// \tprotected override void OnHandleCreated(EventArgs e) {\n\t/// \t\t_os.SetNotifications(this.Hwnd(), WM_APP);\n\t/// \t\tbase.OnHandleCreated(e);\n\t/// \t}\n\t/// \n\t/// \tprotected override void WndProc(ref Message m) {\n\t/// \t\tif(m.Msg == WM_APP) _ProcessMessages();\n\t/// \t\tbase.WndProc(ref m);\n\t/// \t}\n\t/// \t\n\t/// \tinternal const int WM_APP = 0x8000;\n\t/// \n\t/// \tvoid _ProcessMessages()\n\t/// \t{\n\t/// \t\twhile(_os.GetMessage(out var m)) {\n\t/// \t\t\tswitch(m.Type) {\n\t/// \t\t\tcase PrintServerMessageType.Clear:\n\t/// \t\t\t\t_tb.Clear();\n\t/// \t\t\t\tbreak;\n\t/// \t\t\tcase PrintServerMessageType.Write:\n\t/// \t\t\t\t//_tb.AppendText(m.Text);\n\t/// \t\t\t\t_tb.AppendText($\"{DateTime.FromFileTimeUtc(m.TimeUtc).ToLocalTime()}  {m.Caller}  {m.Text}\");\n\t/// \t\t\t\tbreak;\n\t/// \t\t\t}\n\t/// \t\t}\n\t/// \t}\n\t/// \n\t/// \tstatic PrintServer _os = new(isGlobal: false);\n\t/// \n\t/// \t[STAThread]\n\t/// \tstatic void Main()\n\t/// \t{\n\t/// \t\t_os.Start();\n\t/// \n\t/// \t\t//test Write and Clear, before and after creating window\n\t/// \t\tprint.ignoreConsole = true;\n\t/// \t\tprint.it(\"test before setting notifications\");\n\t/// \t\tTask.Run(() => { 1.s(); print.it(\"test after\"); 1.s(); print.clear(); 1.s(); print.it(\"test after Clear\"); });\n\t/// \n\t/// \t\tApplication.Run(new OutputFormExample());\n\t/// \t\t_os.Stop();\n\t/// \t}\n\t/// }\n\t/// ]]></code>\n\t/// </example>\n\tpublic unsafe class PrintServer {\n\t\t//info:\n\t\t//Although global and local servers are implemented quite differently, the interface is almost the same. For this and other reasons I decided to use single class.\n\t\t//For local server, the thread and kernel timer would be not necessary. Instead could use just a user timer. But it has some limitations etc.\n\t\t\n\t\treadonly ConcurrentQueue<PrintServerMessage> _messages = new(); //all received and still not removed messages that were sent by clients when they call print.it etc\n\t\tHandle_ _mailslot; //used if global\n\t\tWaitableTimer _timer; //used always\n\t\twnd _notifWnd;\n\t\tint _notifMsg;\n\t\tbool _isStarted;\n\t\treadonly bool _isGlobal;\n\t\tbool _isLocalTimer;\n\t\t\n\t\t/// <param name=\"isGlobal\">\n\t\t/// If <c>true</c>, will receive output from all processes that don't have local server.\n\t\t/// </param>\n\t\tpublic PrintServer(bool isGlobal) => _isGlobal = isGlobal;\n\t\t\n\t\t/// <summary>\n\t\t/// Starts server.\n\t\t/// </summary>\n\t\t/// <returns><c>false</c> if server already exists (if global - in any process).</returns>\n\t\t/// <exception cref=\"AuException\">Failed.</exception>\n\t\tpublic bool Start() {\n\t\t\tlock (this) {\n\t\t\t\tif (print.s_localServer != null) return false;\n\t\t\t\tif (_isGlobal) {\n\t\t\t\t\tvar m = Api.CreateMailslot(MailslotName_, 0, 0, Api.SECURITY_ATTRIBUTES.ForLowIL);\n\t\t\t\t\tif (m.Is0) {\n\t\t\t\t\t\tvar e = lastError.code;\n\t\t\t\t\t\tif (e == Api.ERROR_ALREADY_EXISTS) return false; //called not first time, or exists in another process\n\t\t\t\t\t\tthrow new AuException(e, \"*create mailslot\");\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\t_mailslot = m;\n\t\t\t\t\t_CreateTimerAndThread();\n\t\t\t\t\tSM_->isServer = 1;\n\t\t\t\t} else {\n\t\t\t\t\t_CreateTimerAndThread();\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tprint.s_localServer = this; //if global server, will work as local when writes same process\n\t\t\t\t_isStarted = true;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\tvoid _CreateTimerAndThread() {\n\t\t\ttry {\n\t\t\t\tif (_isGlobal) _timer = WaitableTimer.Create(false, TimerName_);\n\t\t\t\telse _timer = WaitableTimer.Create();\n\t\t\t\t\n\t\t\t\trun.thread(_Thread, sta: false).Name = \"Au.PrintServer\";\n\t\t\t}\n\t\t\tcatch {\n\t\t\t\tif (_isGlobal) _mailslot.Dispose();\n\t\t\t\t_timer?.Close(); _timer = null;\n\t\t\t\tthrow;\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Stops server.\n\t\t/// </summary>\n\t\tpublic void Stop() {\n\t\t\tlock (this) {\n\t\t\t\tif (!_isStarted) return;\n\t\t\t\t_isStarted = false;\n\t\t\t\tif (_isGlobal) {\n\t\t\t\t\t_mailslot.Dispose();\n\t\t\t\t\tSM_->isServer = 0;\n\t\t\t\t}\n\t\t\t\tprint.s_localServer = null;\n\t\t\t\t_timer?.Set(0); //break thread loop; use minimal time. //info: the thread will dispose _timer and set=null\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Calls <see cref=\"Stop\"/>.\n\t\t/// </summary>\n\t\t~PrintServer() => Stop();\n\t\t\n\t\t/// <summary>\n\t\t/// Sets window/message to be notified about server events.\n\t\t/// </summary>\n\t\t/// <param name=\"w\">Your window that displays output, or any other window. Its window procedure on <i>message</i> should call <see cref=\"GetMessage\"/> until it returns <c>false</c>. See example in class help.</param>\n\t\t/// <param name=\"message\">Windows message to send to <i>w</i> when one or more output events are available. For example <ms>WM_USER</ms> or <ms>WM_APP</ms>.</param>\n\t\tpublic void SetNotifications(wnd w, int message) {\n\t\t\t_notifMsg = message;\n\t\t\t_notifWnd = w;\n\t\t\tif (!w.Is0) _timer?.Set(30);\n\t\t}\n\t\t\n\t\tvoid _Thread() {\n\t\t\ttry {\n\t\t\t\tfor (int period = 1000; ;) {\n\t\t\t\t\tbool isTimerEvent = _timer.WaitOne(period); //true if timer event, false if timeout\n\t\t\t\t\tif (isTimerEvent) {\n\t\t\t\t\t\tif (_isGlobal) SM_->isTimer = 0;\n\t\t\t\t\t\t_isLocalTimer = false;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tlock (this) {\n\t\t\t\t\t\tif (!_isStarted) {\n\t\t\t\t\t\t\t_timer.Dispose(); _timer = null;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\tif (_isGlobal) { //read messages from mailslot and add to _messages. Else messages are added directly to _messages.\n\t\t\t\t\t\t\twhile (Api.GetMailslotInfo(_mailslot, null, out int nextSize, out var msgCount) && msgCount > 0) {\n\t\t\t\t\t\t\t\t//note: GetMailslotInfo makes Systeminformer show constant 24 B/s I/O total rate. Does not depend on period.\n\t\t\t\t\t\t\t\t_ReadMailslotMessage(nextSize);\n\t\t\t\t\t\t\t\tif (msgCount == 1) break;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tif (!_notifWnd.Is0 && !_messages.IsEmpty) {\n\t\t\t\t\t\t\n\t\t\t\t\t\t//print.qm2.write($\"{_messages.Count}, {_ToMB(_memSize)}, {_ToMB(GC.GetTotalMemory(false))}\");\n\t\t\t\t\t\t\n\t\t\t\t\t\tif (!_notifWnd.IsAlive) break;\n\t\t\t\t\t\t_notifWnd.Send(_notifMsg);\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tif (isTimerEvent) period = 50; //check after 50 ms, to avoid 1000 ms delay in case a client did not set timer because SM_->isTimer was still 1 although the timer was already signaled\n\t\t\t\t\telse period = 1000; //check every 1000 ms, for full reliability\n\t\t\t\t\t\n\t\t\t\t\t//Console.WriteLine($\"{period}\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tDebug_.Dialog(ex);\n\t\t\t}\n\t\t}\n\t\t\n\t\t[SkipLocalsInit]\n\t\tvoid _ReadMailslotMessage(int size) {\n\t\t\tusing FastBuffer<byte> b = new(size + 4); //+4 for \"\\r\\n\"\n\t\t\tvar p = b.p;\n\t\t\tbool ok = Api.ReadFile(_mailslot, p, size, out var readSize) && readSize == size;\n\t\t\tif (ok) {\n\t\t\t\tlong time = 0; string s = null, caller = null;\n\t\t\t\tvar mtype = (PrintServerMessageType)(*p++);\n\t\t\t\tswitch (mtype) {\n\t\t\t\tcase PrintServerMessageType.Write or PrintServerMessageType.TaskEvent:\n\t\t\t\t\tif (size < 10) { ok = false; break; } //type, time(8), lenCaller\n\t\t\t\t\ttime = *(long*)p; p += 8;\n\t\t\t\t\tint lenCaller = *p++;\n\t\t\t\t\tif (lenCaller > 0) {\n\t\t\t\t\t\tif (10 + lenCaller * 2 > size) { ok = false; break; }\n\t\t\t\t\t\tcaller = new string((char*)p, 0, lenCaller);\n\t\t\t\t\t\tp += lenCaller * 2;\n\t\t\t\t\t}\n\t\t\t\t\tint len = (size - (int)(p - b.p)) / 2;\n\t\t\t\t\tif (!NoNewline && mtype == PrintServerMessageType.Write) {\n\t\t\t\t\t\tchar* r = (char*)(b.p + size);\n\t\t\t\t\t\tr[0] = '\\r'; r[1] = '\\n';\n\t\t\t\t\t\tlen += 2;\n\t\t\t\t\t}\n\t\t\t\t\ts = new string((char*)p, 0, len);\n\t\t\t\t\tbreak;\n\t\t\t\tcase PrintServerMessageType.Clear or PrintServerMessageType.ScrollToTop when size == 1:\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tok = false;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tDebug.Assert(ok);\n\t\t\t\tif (ok) _AddMessage(new PrintServerMessage(mtype, s, time, caller));\n\t\t\t}\n\t\t}\n\t\t\n\t\t//static string _ToMB(long n) => Math.Round(n / 1048576d, 3).ToS();\n\t\t\n\t\t/// <summary>\n\t\t/// Adds <i>s</i> directly to <c>_messages</c> and sets timer.\n\t\t/// If <i>s</i> is <c>null</c>, it is \"Clear\" command.\n\t\t/// Else if <c>!NoNewline</c>, appends <c>\"\\r\\n\"</c>.\n\t\t/// Used with local server; also with global server when writes the server's process.\n\t\t/// </summary>\n\t\tinternal void LocalWrite_(string s, long time = 0, string caller = null) {\n\t\t\t//Debug.Assert(!_isGlobal);\n\t\t\tif (!NoNewline && s != null) s += \"\\r\\n\";\n\t\t\tvar m = new PrintServerMessage(s == null ? PrintServerMessageType.Clear : PrintServerMessageType.Write, s, time, caller);\n\t\t\t_AddMessage(m, true);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Adds action directly to <c>_messages</c> and sets timer.\n\t\t/// Used with local server; also with global server when writes the server's process.\n\t\t/// </summary>\n\t\tinternal void LocalAction_(PrintServerMessageType action) {\n\t\t\t//Debug.Assert(!_isGlobal);\n\t\t\t_AddMessage(new(action), true);\n\t\t}\n\t\t\n\t\tvoid _AddMessage(PrintServerMessage m, bool setTimer = false) {\n\t\t\t//_memSize += _GetMessageMemorySize(m);\n\t\t\t_messages.Enqueue(m);\n\t\t\tif (setTimer && !_isLocalTimer) { _timer?.Set(10); _isLocalTimer = true; }\n\t\t}\n\t\t\n\t\t//static int _GetMessageMemorySize(PrintServerMessage m) => 50 + m.Text.Lenn() * 2;\n\t\t//int _memSize;\n\t\t\n\t\t/// <summary>\n\t\t/// Gets next message and removes from the queue.\n\t\t/// </summary>\n\t\t/// <returns><c>false</c> if there are no messages.</returns>\n\t\t/// <remarks>\n\t\t/// Messages are added to an internal queue when clients call <see cref=\"print.it\"/> etc. They contain the text, time, etc. This function gets the oldest message and removes it from the queue.\n\t\t/// </remarks>\n\t\tpublic bool GetMessage(out PrintServerMessage m) {\n\t\t\tif (!_messages.TryDequeue(out m)) return false;\n\t\t\t//_memSize -= _GetMessageMemorySize(m);\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets the count of messages in the queue.\n\t\t/// </summary>\n\t\tpublic int MessageCount => _messages.Count;\n\t\t\n\t\t/// <summary>\n\t\t/// Let messages don't end with <c>\"\\r\\n\"</c>.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// This can be used for performance, to avoid string copying when using local server. Does not affect performance of global server.\n\t\t/// </remarks>\n\t\tpublic bool NoNewline { get; set; }\n\t\t\n#if NEED_CALLER\n\t\t/// <summary>\n\t\t/// Let clients provide the caller method of \"print\" functions.\n\t\t/// Note: It makes these methods much slower, especially when thread stack is big. Also generates much garbage. To find caller method is used <see cref=\"StackTrace\"/> class.\n\t\t/// See also: <see cref=\"IntroduceWriterClass\"/>.\n\t\t/// </summary>\n\t\tpublic bool NeedCallerMethod\n\t\t{\n\t\t\tget => _isGlobal ? (SM_->needCaller != 0) : _localNeedCaller;\n\t\t\tset { if(_isGlobal) SM_->needCaller = (byte)(value ? 1 : 0); else _localNeedCaller = value; }\n\t\t}\n\t\tbool _localNeedCaller;\n\n\t\tinternal static bool NeedCallerMethod_\n\t\t{\n\t\t\tget { var t = s_localServer; return (t != null) ? t.NeedCallerMethod : SM_->needCaller != 0; }\n\t\t}\n#endif\n\t\t\n\t\t/// <summary>\n\t\t/// Gets mailslot name like <c>@\"\\\\.\\mailslot\\Au.print\\\" + sessionId</c>.\n\t\t/// </summary>\n\t\tinternal static string MailslotName_ {\n\t\t\tget {\n\t\t\t\tif (_mailslotName == null) {\n\t\t\t\t\t_mailslotName = @\"\\\\.\\mailslot\\Au.print\\\" + process.thisProcessSessionId.ToString();\n\t\t\t\t}\n\t\t\t\treturn _mailslotName;\n\t\t\t}\n\t\t}\n\t\tstatic string _mailslotName;\n\t\t\n\t\t/// <summary>\n\t\t/// Gets waitable timer name like <c>\"timer.Au.print\"</c>.\n\t\t/// </summary>\n\t\tinternal static string TimerName_ => \"timer.Au.print\";\n\t\t\n\t\t/// <summary>\n\t\t/// Shared memory variables. Used with global server only.\n\t\t/// </summary>\n\t\t[StructLayout(LayoutKind.Sequential, Size = 16)] //note: this struct is in shared memory. Size must be same in all library versions.\n\t\tinternal struct SharedMemoryData_ {\n\t\t\tpublic byte isServer, isTimer;\n#if NEED_CALLER\n\t\t\t\tpublic byte needCaller;\n#endif\n\t\t}\n\t\tinternal static SharedMemoryData_* SM_ => &SharedMemory_.Ptr->outp;\n\t}\n}\n\nnamespace Au {\n\tpublic static partial class print {\n\t\t\n\t\t[MethodImpl(MethodImplOptions.NoInlining)] //for stack trace\n\t\tstatic void _ServerWrite(string s) {\n\t\t\tDebug.Assert(s != null);\n\t\t\t\n\t\t\tApi.GetSystemTimeAsFileTime(out var time);\n\t\t\t\n\t\t\tstring caller = script.name;\n#if NEED_CALLER\n\t\t\tif(PrintServer.NeedCallerMethod_) {\n\t\t\t\t//info: this func always called from directly, which is usually called through Writer, Write. But it is public and can be called directly.\n\t\t\t\tvar k = new StackTrace(2); //skip this func and directly()\n\t\t\t\tlock(_writerTypes) {\n\t\t\t\t\tfor(int i = 0, n = k.FrameCount; i < n; i++) {\n\t\t\t\t\t\tvar m = k.GetFrame(i).GetMethod();\n\t\t\t\t\t\tvar t = m.DeclaringType;\n\t\t\t\t\t\tif(_writerTypes.Contains(t)) continue;\n\t\t\t\t\t\tcaller = caller + \":\" + t.Name + \".\" + m.Name;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t//speed: with 'new StackFrame(i)' usually slower, regardless of stack size. Faster only when 1 loop, maybe 2.\n\t\t\t\t//info: here we don't optimize caller strings like PrintServer does, because StackTrace creates much more garbage.\n\t\t\t}\n#endif\n\t\t\t\n\t\t\tvar loc = s_localServer;\n\t\t\tif (loc != null) loc.LocalWrite_(s, time, caller);\n\t\t\telse s_client.WriteLine(s, PrintServerMessageType.Write, caller, time);\n\t\t}\n\t\t\n\t\tstatic void _ServerAction(PrintServerMessageType action) {\n\t\t\tvar loc = s_localServer;\n\t\t\tif (loc == null) s_client.AddAction(action);\n\t\t\telse if (action == PrintServerMessageType.Clear) loc.LocalWrite_(null);\n\t\t\telse loc.LocalAction_(action);\n\t\t}\n\t\t\n\t\tstatic readonly _ClientOfGlobalServer s_client = new();\n\t\tinternal static PrintServer s_localServer; //null if we don't have a local server\n\t\t\n\t\t/// <summary>\n\t\t/// Logs start/end/fail events of <c>miniProgram</c> trigger actions.\n\t\t/// Editor displays it in the <b>Recent tasks</b> window, not in the output panel.\n\t\t/// Could also log other events. For example at first used for task start/end/fail events, but now it is implemented in editor.\n\t\t/// </summary>\n\t\tinternal static void TaskEvent_(string s, long id, string sourceFile = null, int sourceLine = 0) {\n\t\t\tDebug.Assert(script.role == SRole.MiniProgram);\n\t\t\t//if (s == null) s = \"\\0DNl08ISh30Kbt6ekJV3VvA\"; //JIT //now not used\n\t\t\t//else {\n\t\t\tif (sourceFile == null) sourceFile = script.s_idMainFile.ToString(); //task started/ended/failed\n\t\t\telse sourceFile = sourceFile + \"\\0\" + sourceLine.ToS(); //trigger action started/ended/failed\n\t\t\tsourceFile = id.ToS() + \"\\0\" + sourceFile;\n\t\t\t//}\n\t\t\ts_client.WriteLine(s, PrintServerMessageType.TaskEvent, sourceFile);\n\t\t}\n\t\t\n\t\tunsafe class _ClientOfGlobalServer {\n\t\t\t//info: the mailslot/timer are implicitly disposed when process ends.\n\t\t\t\n\t\t\tHandle_ _mailslot;\n\t\t\tWaitableTimer _timer;\n\t\t\tlong _sizeWritten;\n\t\t\t\n\t\t\t[SkipLocalsInit]\n\t\t\tpublic void WriteLine(string s, PrintServerMessageType mtype, string caller = null, long time = 0) {\n\t\t\t\tif (time == 0) Api.GetSystemTimeAsFileTime(out time);\n\t\t\t\t\n\t\t\t\tlock (_lockObj1) {\n\t\t\t\t\tif (!_Connect()) return;\n\t\t\t\t\t\n\t\t\t\t\tint lenS = s.Length, lenCaller = (caller != null) ? Math.Min(caller.Length, 255) : 0;\n\t\t\t\t\tint lenAll = 1 + 8 + 1 + lenCaller * 2 + lenS * 2; //type, time, lenCaller, caller, s\n\t\t\t\t\tusing FastBuffer<byte> b = new(lenAll);\n\t\t\t\t\tbyte* p = b.p;\n\t\t\t\t\t//type\n\t\t\t\t\t*p++ = (byte)mtype;\n\t\t\t\t\t//time\n\t\t\t\t\t*(long*)p = time; p += 8;\n\t\t\t\t\t//caller\n\t\t\t\t\t*p++ = (byte)lenCaller;\n\t\t\t\t\tif (lenCaller != 0) {\n\t\t\t\t\t\tfixed (char* k = caller) MemoryUtil.Copy(k, p, lenCaller * 2);\n\t\t\t\t\t\tp += lenCaller * 2;\n\t\t\t\t\t}\n\t\t\t\t\t//s\n\t\t\t\t\tif (lenS != 0) fixed (char* k = s) MemoryUtil.Copy(k, p, lenS * 2); //s\n\t\t\t\t\t\n\t\t\t\t\t//if (s == \"\\0DNl08ISh30Kbt6ekJV3VvA\") { //JIT //now not used\n\t\t\t\t\t//\t\t\t\t\t\t\t\t\t   //_SetTimer();\n\t\t\t\t\t//\tJit_.Compile(typeof(WaitableTimer), nameof(WaitableTimer.Set));\n\t\t\t\t\t//\tJit_.Compile(typeof(Api), nameof(Api.WriteFile), nameof(Api.SetWaitableTimer)); //slow JIT SetWaitableTimer\n\t\t\t\t\t//\treturn;\n\t\t\t\t\t//}\n\t\t\t\t\t\n\t\t\t\t\tg1:\n\t\t\t\t\tbool ok = Api.WriteFile(_mailslot, b.p, lenAll, out _);\n\t\t\t\t\tif (!ok && _ReopenMailslot()) goto g1;\n\t\t\t\t\t\n\t\t\t\t\tif (ok) {\n\t\t\t\t\t\t_SetTimer();\n\t\t\t\t\t\t\n\t\t\t\t\t\t//prevent overflow of mailslot and _messages\n\t\t\t\t\t\t_sizeWritten += lenAll;\n\t\t\t\t\t\tif (_sizeWritten > 1_000_000) {\n\t\t\t\t\t\t\twhile (Api.GetFileSizeEx(_mailslot, out _sizeWritten) && _sizeWritten > 300_000) Thread.Sleep(15);\n\t\t\t\t\t\t\t//note: these numbers are carefully adjusted for best performance etc\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tpublic void AddAction(PrintServerMessageType action) {\n\t\t\t\tlock (_lockObj1) {\n\t\t\t\t\tif (!_Connect()) return;\n\t\t\t\t\t\n\t\t\t\t\tg1:\n\t\t\t\t\tbyte b = (byte)action;\n\t\t\t\t\tbool ok = Api.WriteFile(_mailslot, &b, 1, out _);\n\t\t\t\t\tif (!ok && _ReopenMailslot()) goto g1;\n\t\t\t\t\tDebug.Assert(ok);\n\t\t\t\t\t\n\t\t\t\t\tif (ok) _SetTimer();\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t//If last error says that server's mailslot closed, closes client's mailsot/timer and tries to reopen. If reopened, returns true.\n\t\t\tbool _ReopenMailslot() {\n\t\t\t\tif (lastError.code == Api.ERROR_HANDLE_EOF) { //server's mailslot closed\n\t\t\t\t\t_Close();\n\t\t\t\t\tif (_Connect()) return true;\n\t\t\t\t} else {\n\t\t\t\t\tDebug.Assert(false);\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t\n\t\t\t[MethodImpl(MethodImplOptions.AggressiveInlining)]\n\t\t\tvoid _SetTimer() {\n\t\t\t\tif (PrintServer.SM_->isTimer == 0) {\n\t\t\t\t\tif (_timer.Set(10)) PrintServer.SM_->isTimer = 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tvoid _Close() {\n\t\t\t\tif (!_mailslot.Is0) {\n\t\t\t\t\t_mailslot.Dispose();\n\t\t\t\t\t_timer.Close(); _timer = null;\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tbool _Connect() {\n\t\t\t\tif (PrintServer.SM_->isServer == 0) {\n\t\t\t\t\t_Close();\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (_mailslot.Is0) {\n\t\t\t\t\t_mailslot = CreateFile_(PrintServer.MailslotName_, true);\n\t\t\t\t\tif (_mailslot.Is0) return false;\n\t\t\t\t\t\n\t\t\t\t\t_timer = WaitableTimer.Open(PrintServer.TimerName_, noException: true);\n\t\t\t\t\tif (_timer == null) {\n\t\t\t\t\t\t_mailslot.Dispose();\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\t\n#if NEED_CALLER\n\t\tstatic readonly List<Type> _writerTypes = new List<Type>() { typeof(print), typeof(_OutputWriter) };\n\n\t\t/// <summary>\n\t\t/// Introduces a class that contain methods designed to write to the output.\n\t\t/// Purpose - when server's <see cref=\"PrintServer.NeedCallerMethod\"/> is <c>true</c>, skip methods of this class when searching for the caller method in the call stack.\n\t\t/// For example, if you created class <c>PrintColored</c> that contains methods <c>PrintRed</c>, <c>PrintGreen</c> and <c>PrintBlue</c>, you should execute this code in its static constructor: <c>print.introduceWriterClass(typeof(PrintColored));</c>.\n\t\t/// Also use this if you redirect output using a writer class that calls directly().\n\t\t/// Not used when writing to console or log file.\n\t\t/// </summary>\n\t\tpublic static void introduceWriterClass(Type t)\n\t\t{\n\t\t\tlock(_writerTypes) {\n\t\t\t\tif(!_writerTypes.Contains(t)) _writerTypes.Add(t);\n\t\t\t}\n\t\t}\n#endif\n\t}\n}\n\nnamespace Au.Types {\n\t/// <summary>\n\t/// See <see cref=\"PrintServerMessage.Type\"/>.\n\t/// </summary>\n\tpublic enum PrintServerMessageType {\n\t\t/// <summary>\n\t\t/// Add line to the output window.\n\t\t/// All <see cref=\"PrintServerMessage\"/> members can be used.\n\t\t/// </summary>\n\t\tWrite,\n\t\t\n\t\t/// <summary>\n\t\t/// Clear the output window.\n\t\t/// Only <see cref=\"PrintServerMessage.Type\"/> is used.\n\t\t/// </summary>\n\t\tClear,\n\t\t\n\t\t/// <summary>\n\t\t/// Used internally to log events such as start/end of trigger actions.\n\t\t/// </summary>\n\t\tTaskEvent,\n\t\t\n\t\t/// <summary>\n\t\t/// Scroll the output window to the top.\n\t\t/// Only <see cref=\"PrintServerMessage.Type\"/> is used.\n\t\t/// </summary>\n\t\tScrollToTop,\n\t}\n\t\n\t/// <summary>\n\t/// Contains message text and/or related info.\n\t/// More info: <see cref=\"PrintServer\"/>, <see cref=\"PrintServer.GetMessage\"/>.\n\t/// </summary>\n\tpublic class PrintServerMessage {\n\t\t/// <summary>\n\t\t/// Message type (write, clear, etc).\n\t\t/// </summary>\n\t\tpublic PrintServerMessageType Type { get; }\n\t\t\n\t\t/// <summary>\n\t\t/// Message text.\n\t\t/// Used with <see cref=\"PrintServerMessageType.Write\"/>.\n\t\t/// </summary>\n\t\tpublic string Text { get; set; }\n\t\t\n\t\t/// <summary>\n\t\t/// Message time in <ms>FILETIME</ms> format, UTC.\n\t\t/// Used with <see cref=\"PrintServerMessageType.Write\"/>.\n\t\t/// To convert to string: <c>DateTime.FromFileTimeUtc(m.TimeUtc).ToLocalTime().ToString()</c>.\n\t\t/// </summary>\n\t\tpublic long TimeUtc { get; }\n\t\t\n#if NEED_CALLER\n\t\t/// <summary>\n\t\t/// The <see cref=\"script.name\"/> property value of the process that called <see cref=\"print.it\"/>.\n\t\t/// Used with <see cref=\"PrintServerMessageType.Write\"/>.\n\t\t/// If <see cref=\"NeedCallerMethod\"/> is <c>true</c>, also includes the caller method. Format: <c>\"scriptname:type.method\"</c>.\n\t\t/// </summary>\n\t\tpublic string Caller { get; }\n\n\t\tinternal PrintServerMessage(PrintServerMessageType type, string text, long time, string caller)\n\t\t{\n\t\t\tType = type;\n\t\t\tText = text;\n\t\t\tTimeUtc = time;\n\t\t\tCaller = caller;\n\t\t}\n#else\n\t\t/// <summary>\n\t\t/// The <see cref=\"script.name\"/> property value of the process that called <see cref=\"print.it\"/>.\n\t\t/// Used with <see cref=\"PrintServerMessageType.Write\"/>.\n\t\t/// </summary>\n\t\tpublic string Caller { get; }\n\t\t\n\t\tinternal PrintServerMessage(PrintServerMessageType type, string text = null, long time = 0, string caller = null) {\n\t\t\tType = type;\n\t\t\tText = text;\n\t\t\tTimeUtc = time;\n\t\t\tCaller = caller;\n\t\t}\n#endif\n\t\t\n\t\t///\n\t\tpublic override string ToString() {\n\t\t\t//in editor used for output history\n\t\t\t\n\t\t\tif (Type != PrintServerMessageType.Write) return \"\";\n\t\t\tvar k = DateTime.FromFileTimeUtc(TimeUtc).ToLocalTime();\n\t\t\treturn $\"{k.ToString()}  |  {Caller}\\r\\n{Text}\";\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au/Other/ScriptEditor.cs",
    "content": "namespace Au.More;\n\n/// <summary>\n/// Contains functions to interact with the script editor, if available.\n/// </summary>\n/// <remarks>\n/// Functions of this class work when editor process is running, even if current process wasn't started from it. To detect whether current process was started from editor, use <see cref=\"folders.Editor\"/> (it is <c>null</c> if not).\n/// </remarks>\npublic static class ScriptEditor {\n\t/// <summary>\n\t/// Finds editor's message-only window used with <c>WM_COPYDATA</c> etc.\n\t/// Uses <see cref=\"script.s_wndEditorMsg\"/> or <see cref=\"wnd.Cached_\"/>.\n\t/// </summary>\n\tinternal static wnd WndMsg_ {\n\t\tget {\n\t\t\tvar w = script.s_wndEditorMsg; if (!w.Is0) return w;\n\t\t\treturn s_wndMsg.FindFast(null, c_msgWndClassName, true);\n\t\t}\n\t}\n\tstatic wnd.Cached_ s_wndMsg, s_wndMain;\n\t\n\t/// <summary>\n\t/// Class name of <see cref=\"WndMsg_\"/> window.\n\t/// </summary>\n\tinternal const string c_msgWndClassName = \"Au.Editor.m3gVxcTJN02pDrHiQ00aSQ\";\n\t\n\tinternal static wnd WndMain_(bool show = false) {\n\t\tvar w = WndMsg_;\n\t\treturn w.Is0 ? default : s_wndMain.Get(() => (wnd)w.Send(Api.WM_USER, 0, show ? 1 : 0));\n\t}\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if editor is running.\n\t/// </summary>\n\tpublic static bool Available => !WndMsg_.Is0;\n\t\n\t/// <summary>\n\t/// The main editor window.\n\t/// </summary>\n\t/// <param name=\"show\">Show the window (if the editor program is running).</param>\n\t/// <returns><c>default(wnd)</c> if the editor program isn't running or its main window still wasn't visible.</returns>\n\tpublic static wnd MainWindow(bool show = false) => WndMain_(show);\n\t\n\t/// <summary>\n\t/// Shows or hides the main editor window.\n\t/// </summary>\n\t/// <param name=\"show\">\n\t/// <br/>• <c>true</c> - show, activate, restore if minimized.\n\t/// <br/>• <c>false</c> - hide. It does not hide the tray icon.\n\t/// <br/>• <c>null</c> - toggle.\n\t/// </param>\n\tpublic static void ShowMainWindow(bool? show) {\n\t\tvar w = WndMsg_;\n\t\tif (!w.Is0) w.Send(Api.WM_USER, 1, show switch { true => 1, false => 2, _ => 0 });\n\t}\n\t\n\t/// <summary>\n\t/// Invokes an editor's menu command.\n\t/// </summary>\n\t/// <param name=\"command\">Command name. If <c>\"\"</c> or invalid, prints all names.</param>\n\t/// <param name=\"check\">If it's a checkbox-item, <c>true</c> checks it, <c>false</c> unchecks, <c>null</c> toggles. Else must be <c>null</c> (default).</param>\n\t/// <param name=\"dontWait\">Don't wait until the command finishes executing. For example, if it shows a modal dialog, don't wait until it is closed.</param>\n\t/// <param name=\"activateWindow\">Activate the main window. Default <c>true</c>. Some commands may not work correctly if the window isn't active.</param>\n\t/// <remarks>\n\t/// Shows the main window, regardless of <i>activateWindow</i>. Waits while it is disabled or not finished loading, unless script role is <c>editorExtension</c> and it runs in the editor's main thread (then not invoke the command).\n\t/// Does not invoke the command if the menu item is disabled or if it's a submenu-item.\n\t/// </remarks>\n\tpublic static void InvokeCommand(string command, bool? check = null, bool dontWait = false, bool activateWindow = true) {\n\t\tvar w = WndMsg_; if (w.Is0) return;\n\t\tint flags = check switch { true => 1, false => 2, _ => 0 } | _EditorExtensionFlag;\n\t\tif (activateWindow) flags |= 4;\n\t\tif (dontWait) flags |= 8;\n\t\tg1:\n\t\tnint r = WndCopyData.Send<char>(w, 11, command, flags);\n\t\tif (r == -1) { _WaitWhileEditorDisabled(); goto g1; }\n\t}\n\t\n\tstatic int _EditorExtensionFlag => process.IsLaMainThread_ ? 16 : 0;\n\t\n\tstatic void _WaitWhileEditorDisabled() { MainWindow().WaitFor(0, o => o.IsEnabled()); }\n\t\n\t/// <summary>\n\t/// Gets the state of an editor's menu command (checked, disabled).\n\t/// </summary>\n\t/// <param name=\"command\">Command name. If <c>\"\"</c> or invalid, prints all names.</param>\n\t/// <remarks>\n\t/// Shows the main window. Waits while it is disabled or not finished loading, unless script role is <c>editorExtension</c> and it runs in the editor's main thread (then returns <c>Disabled</c>).\n\t/// </remarks>\n\tpublic static ECommandState GetCommandState(string command) {\n\t\tvar w = WndMsg_; if (w.Is0) return 0;\n\t\tg1:\n\t\tnint r = WndCopyData.Send<char>(w, 12, command, _EditorExtensionFlag);\n\t\tif (r == -1) { _WaitWhileEditorDisabled(); goto g1; }\n\t\treturn (ECommandState)r;\n\t}\n\t\n\t/// <summary>\n\t/// Opens a script or other file. Also can move the text cursor.\n\t/// Does nothing if editor isn't running.\n\t/// </summary>\n\t/// <param name=\"file\">A file in current workspace. Can be full path, or relative path in workspace, or file name with extension (<c>\".cs\"</c> etc). If folder, selects it.</param>\n\t/// <param name=\"line\">If not <c>null</c>, goes to this 1-based line index.</param>\n\t/// <param name=\"offset\">If not <c>null</c>, goes to this 0-based column index in line (if <i>line</i> not <c>null</c>) or to this 0-based position in text (if <i>line</i> <c>null</c>).</param>\n\tpublic static void Open([ParamString(PSFormat.FileInWorkspace)] string file, int? line = null, int? offset = null) {\n\t\tvar w = WndMsg_; if (w.Is0) return;\n\t\tApi.AllowSetForegroundWindow(w.ProcessId);\n\t\tWndCopyData.Send<char>(w, 4, $\"{file}|{line}|{offset}\");\n\t}\n\t\n\t///\n\t[Obsolete(\"use Open\"), EditorBrowsable(EditorBrowsableState.Never)]\n\tpublic static void OpenAndGoToLine([ParamString(PSFormat.FileInWorkspace)] string file, int line) => Open(file, line);\n\t\n\t/// <summary>\n\t/// Gets icon string in specified format.\n\t/// </summary>\n\t/// <returns>Returns <c>null</c> if editor isn't running or if the file does not exist. Read more in Remarks.</returns>\n\t/// <param name=\"file\">Script file/folder path etc, or icon name. See <see cref=\"EGetIcon\"/>, <see cref=\"ImageUtil.LoadWpfImageElement\"/>.</param>\n\t/// <param name=\"what\">The format of input and output strings.</param>\n\t/// <remarks>\n\t/// If <i>what</i> is <see cref=\"EGetIcon.IconNameToXaml\"/>, this function tries to get icon XAML from assembly resources (passes <i>file</i> to <see cref=\"ResourceUtil.GetString\"/>, with color removed); if not found - from editor. By default the LibreAutomate compiler finds literal icon-like strings in code and adds icon XAML to assembly resources; see <b>Properties > Resource > Options</b>.\n\t/// </remarks>\n\tpublic static string GetIcon(string file, EGetIcon what) => GetIcon_(file, what, false);\n\t\n\tinternal static string GetIcon_(string file, EGetIcon what, bool skipResources) {\n\t\tif (IconNameToXaml_ is { } intx) return intx(file, what);\n\t\t\n\t\tif (what == EGetIcon.IconNameToXaml && script.role != SRole.EditorExtension && !skipResources) {\n\t\t\tif (IconString_.GetXamlFromResources(file) is string xaml) return xaml;\n\t\t}\n\t\t\n\t\tvar w = WndMsg_; if (w.Is0) return null;\n\t\tWndCopyData.SendReceive<char>(w, (int)Math2.MakeLparam(10, (int)what), file, out string r);\n\t\treturn r;\n\t\t//rejected: add option to get serialized Bitmap instead. Now loads XAML in this process. It is 230 ms and +27 MB.\n\t\t//\tNothing good if the toolbar etc also uses XAML icons directly, eg for non-script items. And serializing is slow.\n\t\t//\tNow not actual because of cache.\n\t}\n\t\n\t/// <summary>\n\t/// Editor sets this. Library uses it to avoid sendmessage when role <c>editorExtension</c>.\n\t/// </summary>\n\tinternal static Func<string, EGetIcon, string> IconNameToXaml_;\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if the editor program is installed as [portable](xref:portable).\n\t/// </summary>\n\t/// <remarks>\n\t/// Available in the script editor process and in scripts launched from it. Elsewhere <c>false</c>.\n\t/// \n\t/// If portable, these paths are different:\n\t/// - <see cref=\"folders.ThisAppDocuments\"/>\n\t/// - <see cref=\"folders.ThisAppDataLocal\"/>\n\t/// - <see cref=\"folders.ThisAppTemp\"/>\n\t/// - <see cref=\"folders.Editor\"/>\n\t/// - <see cref=\"folders.Workspace\"/>\n\t/// </remarks>\n\tpublic static bool IsPortable { get; internal set; }\n\t\n\t/// <summary>\n\t/// Gets name, text and some info of the currently active file in editor.\n\t/// </summary>\n\t/// <param name=\"needText\">Need text too.</param>\n\t/// <returns><c>null</c> if there are no open files or if failed.</returns>\n\tpublic static EFileInfo GetFileInfo(bool needText) => GetFileInfo(null, needText);\n\t\n\t/// <summary>\n\t/// Gets name, text and some info of a file in the current workspace in editor.\n\t/// </summary>\n\t/// <param name=\"file\">A file in current workspace. Can be full path, or relative path in workspace, or file name with extension (<c>\".cs\"</c> etc), or <c>\":id\"</c>.</param>\n\t/// <param name=\"needText\">Need text too.</param>\n\t/// <returns><c>null</c> if the specified file not found or if failed.</returns>\n\tpublic static EFileInfo GetFileInfo(string file, bool needText) {\n\t\tvar w = WndMsg_;\n\t\tvar flags = needText ? \"1\" : \"0\";\n\t\tif (!w.Is0 && WndCopyData.SendReceive<char>(w, 14, file != null ? flags + \" \" + file : flags, out byte[] r)) {\n\t\t\tvar x = Serializer_.Deserialize(r);\n\t\t\tstring path = x[0];\n\t\t\treturn new EFileInfo(pathname.getName(path), path, x[1], (EFileKind)(int)x[2], (uint)(int)x[3], x[4], x[5]);\n\t\t}\n\t\treturn null;\n\t}\n\t\n\t/// <summary>\n\t/// If a specified class file is currently active in editor, calls a callback function (which can be or call a function in that file) and returns true.\n\t/// </summary>\n\t/// <param name=\"files\">\n\t/// List of tuples <c>(string file, Action action)</c>:\n\t/// <br/>• <c>file</c> - a class file from current project; must be filename without \".cs\" and path.\n\t/// <br/>• <c>action</c> - a callback function to call if that file is the active file in editor.\n\t/// </param>\n\t/// <remarks>\n\t/// A script project folder can contain one script file at the top, and any number of class files. When you click <b>Run</b>, the code execution starts from the script, even if a class file is currently active in editor. But sometimes you may want to execute just a function in the current class file, and skip script code. The example shows how to do it easily.\n\t/// </remarks>\n\t/// <example>\n\t/// Code at/near the start of the script file of a script project.\n\t/// <code><![CDATA[\n\t/// if (script.testing && ScriptEditor.TestCurrentFileInProject((\"TaskA\", TaskA.Func1), (\"TaskB\", () => TaskB.Func1(5)))) return;\n\t/// ]]></code>\n\t/// File TaskA.cs.\n\t/// <code><![CDATA[\n\t/// static class TaskA {\n\t/// \tpublic static void Func1() {\n\t/// \t\tdialog.show(null, \"TaskA\");\n\t/// \t}\n\t/// }\n\t/// ]]></code>\n\t/// File TaskB.cs.\n\t/// <code><![CDATA[\n\t/// static class TaskB {\n\t/// \tpublic static void Func1(int x) {\n\t/// \t\tdialog.show(null, $\"TaskB, x={x}\");\n\t/// \t}\n\t/// }\n\t/// ]]></code>\n\t/// </example>\n\tpublic static bool TestCurrentFileInProject(params (string file, Action action)[] files) {\n\t\tif (GetFileInfo(false) is { } f && f.kind is EFileKind.Class) {\n\t\t\tvar s = f.name[..^3];\n\t\t\tforeach (var (n, a) in files) {\n\t\t\t\tif (n.Eqi(s)) {\n\t\t\t\t\ta();\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\t\n\t\n\t//rejected. Use folders.Editor.\n\t///// <summary>\n\t///// Gets some special folders of editor process.\n\t///// </summary>\n\t///// <remarks>\n\t///// Default folders are:\n\t///// <c>ThisAppDocuments</c> - <c>folders.Documents + \"LibreAutomate\"</c>.\n\t///// <c>ThisAppDataLocal</c> - <c>folders.LocalAppData + \"LibreAutomate\"</c>.\n\t///// <c>ThisAppTemp</c> - <c>folders.Temp + \"LibreAutomate\"</c>.\n\t///// \n\t///// Here <i>path</i> is either full path (like <c>C:\\folder</c> or <c>%folders.Documents%\\folder</c>) or path relative to the program's folder (like <c>ChildFolder</c> or <c>..\\SiblingFolder</c>).\n\t///// </remarks>\n\t///// <seealso cref=\"folders.Workspace\"/>\n\t//public static class Folders {\n\t//\tstatic string _Get(int i) {\n\t//\t\tif (a == null) {\n\t//\t\t\tvar w = WndMsg_;\n\t//\t\t\tif (!w.Is0) {\n\t//\t\t\t\tWndCopyData.SendReceive<char>(w, 13, null, out string r);\n\t//\t\t\t\tif (!r.NE()) a = r.Split('|');\n\t//\t\t\t}\n\t//\t\t\ta ??= new string[] { null, null, null };\n\t//\t\t}\n\t//\t\treturn a[i];\n\t//\t}\n\t\n\t//\tstatic string[] a;\n\t\n\t//\t/// <summary>\n\t//\t/// Gets <see cref=\"folders.ThisAppDocuments\"/> of editor process.\n\t//\t/// </summary>\n\t//\t/// <value>null if failed.</value>\n\t//\tpublic static FolderPath ThisAppDocuments => new(_Get(0));\n\t\n\t//\t/// <summary>\n\t//\t/// Gets <see cref=\"folders.ThisAppDataLocal\"/> of editor process.\n\t//\t/// </summary>\n\t//\t/// <value>null if failed.</value>\n\t//\tpublic static FolderPath ThisAppDataLocal => new(_Get(1));\n\t\n\t//\t/// <summary>\n\t//\t/// Gets <see cref=\"folders.ThisAppTemp\"/> of editor process.\n\t//\t/// </summary>\n\t//\t/// <value>null if failed.</value>\n\t//\tpublic static FolderPath ThisAppTemp => new(_Get(2));\n\t//}\n\t\n\t//[StructLayout(LayoutKind.Sequential, Size = 64)] //note: this struct is in shared memory. Size must be same in all library versions.\n\t//internal struct SharedMemoryData_ {\n\t//\tint _wndEditorMsg, _wndEditorMain;\n\t\n\t//\tinternal wnd wndEditorMsg {\n\t//\t\tget {\n\t//\t\t\tif (_wndEditorMsg != 0) {\n\t//\t\t\t\tvar w = (wnd)_wndEditorMsg;\n\t//\t\t\t\tif (w.ClassNameIs(c_msgWndClassName)) return w;\n\t//\t\t\t\t//_wndEditorMsg = 0; //no, unsafe\n\t//\t\t\t}\n\t//\t\t\treturn default;\n\t//\t\t}\n\t//\t\tset { _wndEditorMsg = (int)value; }\n\t//\t}\n\t//\tinternal wnd wndEditorMain {\n\t//\t\tget => wndEditorMsg.Is0 ? default : (wnd)_wndEditorMain;\n\t//\t\tset { _wndEditorMain = (int)value; }\n\t//\t}\n\t\n\t//}\n}\n"
  },
  {
    "path": "Au/Other/internet.cs",
    "content": "using System.Net.NetworkInformation;\nusing System.Net.Http;\nusing System.Net.Http.Json;\nusing System.Net.Http.Headers;\nusing System.Text.Json;\nusing System.Text.Json.Nodes;\nusing System.Net;\nusing System.IO.Compression;\n\nnamespace Au {\n\t/// <summary>\n\t/// This class, together with <see cref=\"ExtInternet\"/> (extension methods), make easier to use <see cref=\"HttpClient\"/> and other .NET Internet classes for tasks like download, post, ping.\n\t/// </summary>\n\tpublic static class internet {\n\t\t/// <summary>\n\t\t/// Sends an ICMP echo message to the specified website and returns <c>true</c> if successful. Can be used to check Internet connectivity.\n\t\t/// </summary>\n\t\t/// <param name=\"hostNameOrAddress\">Domain name like <c>\"google.com\"</c> or IP like <c>\"123.45.67.89\"</c>.</param>\n\t\t/// <param name=\"timeout\">Timeout in milliseconds.</param>\n\t\t/// <remarks>\n\t\t/// Not all websites support it.\n\t\t/// \n\t\t/// Uses <see cref=\"Ping\"/>.\n\t\t/// </remarks>\n\t\tpublic static bool ping(string hostNameOrAddress = \"google.com\", int timeout = 5000) {\n\t\t\ttry {\n\t\t\t\tusing var ping = new Ping();\n\t\t\t\tvar reply = ping.Send(hostNameOrAddress, timeout);\n\t\t\t\treturn reply.Status == IPStatus.Success;\n\t\t\t}\n\t\t\tcatch { return false; }\n\t\t} //also tested http, but slow etc.\n\t\t\n\t\t/// <summary>\n\t\t/// Sends an ICMP echo message to the specified website and returns <c>true</c> if successful. Gets the roundtrip time.\n\t\t/// </summary>\n\t\t/// <param name=\"roundtripTime\"><see cref=\"PingReply.RoundtripTime\"/>.</param>\n\t\t/// <inheritdoc cref=\"ping(string, int)\"/>\n\t\tpublic static bool ping(out int roundtripTime, string hostNameOrAddress = \"google.com\", int timeout = 5000) {\n\t\t\troundtripTime = 0;\n\t\t\ttry {\n\t\t\t\tusing var ping = new Ping();\n\t\t\t\tvar reply = ping.Send(hostNameOrAddress, timeout);\n\t\t\t\troundtripTime = (int)Math.Min(reply.RoundtripTime, int.MaxValue);\n\t\t\t\treturn reply.Status == IPStatus.Success;\n\t\t\t}\n\t\t\tcatch { return false; }\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets a static <see cref=\"HttpClient\"/> instance that can be used in scripts to download web pages, post web form data, etc.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Creates <see cref=\"HttpClient\"/> only the first time; later just returns it.\n\t\t/// \n\t\t/// Sets these properties and default headers:\n\t\t/// - <see cref=\"SocketsHttpHandler.AutomaticDecompression\"/> = <c>All</c>.\n\t\t/// - <c>User-Agent: Au</c>.\n\t\t/// \n\t\t/// <c>internet.http</c> makes easier to discover and use internet get/post/etc functions when using this library. You can instead create an <see cref=\"HttpClient\"/> instance and use its functions in the same way. See the second example. Use the same <c>HttpClient</c> instance when making multiple get/post/etc requests.\n\t\t/// </remarks>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// string s = internet.http.Get(\"https://httpbin.org/anything\").Text();\n\t\t/// ]]></code>\n\t\t/// Without <c>internet.http</c>.\n\t\t/// <code><![CDATA[\n\t\t/// using var http = new HttpClient();\n\t\t/// http.DefaultRequestHeaders.Add(\"User-Agent\", \"Script/1.0\");\n\t\t/// string s = http.Get(\"https://httpbin.org/anything\").Text();\n\t\t/// ]]></code>\n\t\t/// Or.\n\t\t/// <code><![CDATA[\n\t\t/// using var http = new HttpClient() { BaseAddress = new(\"https://httpbin.org\") };\n\t\t/// http.DefaultRequestHeaders.Add(\"User-Agent\", \"Script/1.0\");\n\t\t/// string s = http.Get(\"anything\").Text();\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic static HttpClient http => _lazyHC.Value; //rejected: public setter\n\t\tstatic Lazy<HttpClient> _lazyHC = new(_CreateHttpClient);\n\t\t\n\t\tstatic HttpClient _CreateHttpClient() {\n\t\t\tvar h = new SocketsHttpHandler { AutomaticDecompression = DecompressionMethods.All };\n\t\t\tvar r = new HttpClient(h);\n\t\t\tr.DefaultRequestHeaders.Add(\"User-Agent\", \"Au\"); //without it some servers reject requests\n\t\t\tprocess.thisProcessExit += _ => r.Dispose();\n\t\t\treturn r;\n\t\t\t\n\t\t\t//HttpClient does not close the connection after sending a request. It's good. Most servers have keep-alive timeout 5 or 10 s.\n\t\t\t//\tCloses when disposing. And maybe when server closes; and maybe after h.PooledConnectionIdleTimeout etc; not tested.\n\t\t\t//\tDoes not close when sending request to another server. Supports multiple connections at the same time.\n\t\t\t\n\t\t\t//It seems HttpClient.Send etc are thread-safe.\n\t\t\t//\tTested: if several threads send a request to the same URL, are created several connections.\n\t\t\t//\tHowever threads cannot safely change base address, default headers, etc.\n\t\t\t\n\t\t\t//Another possible problem - DNS changes.\n\t\t\t//\tBy default, idle connections are closed after 1 minute.\n\t\t\t//\tFor active connections can set h.PooledConnectionLifetime. Never mind.\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets a static <see cref=\"HttpClient\"/> instance that can be used in this library.\n\t\t/// </summary>\n\t\tinternal static HttpClient http_ => _lazyHC_.Value;\n\t\tstatic Lazy<HttpClient> _lazyHC_ = new(_CreateHttpClient);\n\t\t\n\t\t/// <summary>\n\t\t/// Creates an <see cref=\"HttpContent\"/> for posting web form fields with functions like <see cref=\"HttpClient.PostAsync(string?, HttpContent?)\"/> and <see cref=\"ExtInternet.Post\"/>.\n\t\t/// </summary>\n\t\t/// <param name=\"fields\">One or more web form field names and values. See example.</param>\n\t\t/// <exception cref=\"ArgumentException\">An empty name.</exception>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// var content = internet.formContent((\"name1\", \"value1\"), (\"name2\", \"value2\")).AddFile(\"name3\", @\"C:\\Test\\file.png\");\n\t\t/// string s = internet.http.Post(\"https://httpbin.org/anything\", content).Text();\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic static MultipartFormDataContent formContent(params (string name, object value)[] fields) {\n\t\t\tvar m = new MultipartFormDataContent();\n\t\t\tforeach (var (n, v) in fields) m.Add(n, v?.ToString());\n\t\t\treturn m;\n\t\t}\n\t\t//rejected: overload with params string[] fields. Not intuitive, need to learn how to separate names/values. Easy with tuples.\n\t\t\n\t\t//rejected. Does not support files and does not have significant advantages, just slightly smaller data to send.\n\t\t//public static FormUrlEncodedContent formContent2(params (string name, string value)[] fields)\n\t\t//\t=> new(fields.Select(o=>new KeyValuePair<string, string>(o.Item1, o.Item2)));\n\t\t\n\t\t/// <summary>\n\t\t/// Creates <see cref=\"HttpContent\"/> for posting JSON. It can be used with functions like <see cref=\"HttpClient.PostAsync(string?, HttpContent?)\"/> and <see cref=\"ExtInternet.Post\"/>.\n\t\t/// </summary>\n\t\t/// <param name=\"x\">JSON string, or <see cref=\"JsonNode\"/> (or a derived type), or object of any type that can be serialized to JSON with <see cref=\"JsonSerializer\"/>.</param>\n\t\t/// <param name=\"options\"></param>\n\t\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"JsonContent.Create(object?, Type, MediaTypeHeaderValue?, JsonSerializerOptions?)\"/>.</exception>\n\t\t/// <returns>\n\t\t/// If <i>x</i> is string, creates/returns new <see cref=\"StringContent\"/>.\n\t\t/// Else if <i>x</i> is <see cref=\"JsonNode\"/> (or a derived type), calls <see cref=\"JsonNode.ToJsonString\"/> and creates/returns new <see cref=\"StringContent\"/>.\n\t\t/// Else if <i>x</i> is <see cref=\"HttpContent\"/> (or a derived type), returns <i>x</i>.\n\t\t/// Else calls/returns <see cref=\"JsonContent.Create(object?, Type, MediaTypeHeaderValue?, JsonSerializerOptions?)\"/>.\n\t\t/// </returns>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// var v = new { a = \"A\", b = true };\n\t\t/// string s = internet.http.Post(\"https://httpbin.org/anything\", internet.jsonContent(v)).Text();\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic static HttpContent jsonContent(object x, JsonSerializerOptions options = null) {\n\t\t\treturn x switch {\n\t\t\t\tHttpContent c => c,\n\t\t\t\tstring s => new StringContent(s, null, \"application/json\"),\n\t\t\t\tJsonNode n => new StringContent(n.ToJsonString(options), null, \"application/json\"),\n\t\t\t\t_ => JsonContent.Create(x, x.GetType(), null, options),\n\t\t\t};\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Creates <see cref=\"HttpRequestMessage\"/> for <see cref=\"HttpClient.Send\"/> or <see cref=\"HttpClient.SendAsync\"/> in the same way as the <see cref=\"HttpClient\"/> extension methods of this library (<b>ExtInternet.Get</b>, <b>ExtInternet.Post</b> etc).\n\t\t/// </summary>\n\t\t/// <param name=\"method\"><c>System.Net.Http.HttpMethod.Post</c> etc.</param>\n\t\t/// <inheritdoc cref=\"ExtInternet.Get(HttpClient, string, bool, IEnumerable{string}, string, Action{HttpRequestMessage})\" path=\"/param\"/>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// using System.Net.Http;\n\t\t/// var m = internet.message(HttpMethod.Get, \"https://www.example.com\");\n\t\t/// var r = internet.http.Send(m);\n\t\t/// print.it(r.Text());\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic static HttpRequestMessage message(HttpMethod method, string url, IEnumerable<string> headers = null, HttpContent content = null, string auth = null)\n\t\t\t=> ExtInternet.HttpMessage_(method, url, headers, auth, null, content);\n\t\t\n\t\t/// <summary>\n\t\t/// Joins a URL address and parameters. Urlencodes parameters.\n\t\t/// </summary>\n\t\t/// <param name=\"address\">URL part without parameters or with some parameters. The function does not modify it.</param>\n\t\t/// <param name=\"parameters\">URL parameters to append, like <c>\"name1=value1\", \"name2=value2\"</c>. The function urlencodes them (<see cref=\"WebUtility.UrlEncode\"/>).</param>\n\t\t/// <returns>String like <c>\"address?name1=value1&amp;name2=value2\"</c>.</returns>\n\t\t/// <exception cref=\"ArgumentException\">Incorrect format of a parameters string.</exception>\n\t\tpublic static string urlAppend(string address, params string[] parameters) {\n\t\t\tvar b = new StringBuilder(address);\n\t\t\tchar sep = '?'; if (address != null && address.Contains('?')) sep = '&';\n\t\t\tforeach (var s in parameters) {\n\t\t\t\tint i = s.IndexOf('='); if (i < 1) throw new ArgumentException(\"parameters must be like \\\"name1=value1\\\", \\\"name2=value2\\\"\");\n\t\t\t\tb.Append(sep).Append(WebUtility.UrlEncode(s[..i])).Append('=').Append(WebUtility.UrlEncode(s[++i..]));\n\t\t\t\tsep = '&';\n\t\t\t}\n\t\t\treturn b.ToString();\n\t\t}\n\t\t//rejected: overload with (string, string)[]. Longer code and no real advantages.\n\t\t//rejected: overload with Strings. Unsafe, limited, und unclear how to use correctly.\n\t\t//rejected: overload with FormattableString. The user code is less readable, may be even longer, unclear, easy to make bugs. Difficult to prevent encoding the address part. Dubious advantage - can use FormattableString directly for 'url' parameters of functions like 'Get'.\n\t}\n}\n\nnamespace Au.Types {\n\t\n\t/// <summary>\n\t/// Extension methods for .NET Internet functions.\n\t/// </summary>\n\tpublic static class ExtInternet {\n\t\t/// <summary>\n\t\t/// Adds a non-file field.\n\t\t/// Uses <see cref=\"MultipartFormDataContent.Add(HttpContent, string)\"/>.\n\t\t/// </summary>\n\t\t/// <returns>This.</returns>\n\t\t/// <exception cref=\"ArgumentException\">See <see cref=\"MultipartFormDataContent.Add(HttpContent, string)\"/>.</exception>\n\t\tpublic static MultipartFormDataContent Add(this MultipartFormDataContent t, string name, string value) {\n\t\t\tt.Add(new StringContent(value), name);\n\t\t\treturn t;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Adds a file field.\n\t\t/// Uses <see cref=\"MultipartFormDataContent.Add(HttpContent, string, string)\"/>.\n\t\t/// Please read remarks about disposing.\n\t\t/// </summary>\n\t\t/// <param name=\"t\"></param>\n\t\t/// <param name=\"name\">Field name.</param>\n\t\t/// <param name=\"file\">File path.</param>\n\t\t/// <param name=\"contentType\"><c>Content-Type</c> header, for example <c>\"image/png\"</c>.</param>\n\t\t/// <param name=\"fileName\">Filename. If <c>null</c>, gets from <i>file</i>.</param>\n\t\t/// <returns>This.</returns>\n\t\t/// <remarks>\n\t\t/// Opens the file and stores the stream in this <see cref=\"MultipartFormDataContent\"/> object. Won't auto-close it after uploading. To close files, dispose this <c>MultipartFormDataContent</c> object, for example with <c>using</c> like in the example. Else the file will remain opened/locked until this process exits or until next garbage collection.\n\t\t/// </remarks>\n\t\t/// <exception cref=\"ArgumentException\">See <see cref=\"MultipartFormDataContent.Add(HttpContent, string, string)\"/>.</exception>\n\t\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"filesystem.loadStream\"/>.</exception>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// using var content = internet.formContent((\"name1\", \"value1\"), (\"name2\", \"value2\")).AddFile(\"name3\", @\"C:\\Test\\file.png\");\n\t\t/// string s = internet.http.Post(\"https://httpbin.org/anything\", content).Text();\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic static MultipartFormDataContent AddFile(this MultipartFormDataContent t, string name, string file, string contentType = null, string fileName = null) {\n\t\t\tvar k = new StreamContent(filesystem.loadStream(file));\n\t\t\tif (contentType != null) k.Headers.ContentType = new MediaTypeHeaderValue(contentType);\n\t\t\tt.Add(k, name, fileName ?? pathname.getName(file));\n\t\t\treturn t;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Adds multiple HTTP request headers.\n\t\t/// Uses <see cref=\"HttpHeaders.Add(string, string?)\"/>.\n\t\t/// </summary>\n\t\t/// <param name=\"t\"></param>\n\t\t/// <param name=\"headers\">Headers like <c>\"name1: value1\", \"name2: value2\"</c>.</param>\n\t\t/// <exception cref=\"ArgumentException\">Incorrect format of a <i>headers</i> string.</exception>\n\t\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"HttpHeaders.Add(string, string?)\"/>.</exception>\n\t\tpublic static void AddMany(this HttpRequestHeaders t, params string[] headers) => AddMany(t, (IEnumerable<string>)headers);\n\t\t\n\t\t/// <inheritdoc cref=\"AddMany(HttpRequestHeaders, string[])\"/>\n\t\tpublic static void AddMany(this HttpRequestHeaders t, IEnumerable<string> headers) {\n\t\t\tforeach (var s in headers) {\n\t\t\t\tint i = s?.IndexOf(':') ?? -1; if (i < 1) throw new ArgumentException(\"headers must be like \\\"name1: value1\\\", \\\"name2: value2\\\"\");\n\t\t\t\tint j = i + 1; while (s.Eq(j, ' ')) j++;\n\t\t\t\tt.Add(s[..i], s[j..]);\n\t\t\t}\n\t\t}\n\t\t//rejected: overload with (string, string)[]. Longer code and no real advantages.\n\t\t//rejected: overload Strings. Shorter code if can safely uses string, but longer etc if need to use array because values may contain '|'.\n\t\t\n\t\t#region HttpClient\n\t\t\n\t\t/// <summary>\n\t\t/// Sends a GET request to the specified URL, and gets the response.\n\t\t/// </summary>\n\t\t/// <param name=\"t\"></param>\n\t\t/// <param name=\"url\">URL. To create URL with urlencoded parameters you can use <see cref=\"internet.urlAppend\"/>.</param>\n\t\t/// <param name=\"dontWait\">Use <see cref=\"HttpCompletionOption.ResponseHeadersRead\"/>.</param>\n\t\t/// <param name=\"headers\">\n\t\t/// <c>null</c> or request headers like <c>[\"name1: value1\", \"name2: value2\"]</c>.\n\t\t/// Also you can add headers to <see cref=\"HttpClient.DefaultRequestHeaders\"/>, like <c>internet.http.DefaultRequestHeaders.Add(\"User-Agent\", \"Script/1.0\");</c>.\n\t\t/// </param>\n\t\t/// <param name=\"auth\">String like <c>\"username:password\"</c> for basic authentication. It will be encoded and sent in the <c>Authorization</c> header. For other authentication schemes use the <i>headers</i> parameter instead, like <c>headers: [\"Authorization: Bearer token\"]</c>.</param>\n\t\t/// <param name=\"also\">Can set more properties for the request.</param>\n\t\t/// <returns>An <c>HttpResponseMessage</c> object that can be used to get response content (web page HTML, JSON, file, etc), headers etc. To get content use <see cref=\"Text\"/> etc.</returns>\n\t\t/// <exception cref=\"Exception\">\n\t\t/// Exceptions of <see cref=\"HttpClient.Send(HttpRequestMessage, HttpCompletionOption)\"/>.\n\t\t/// If <i>headers</i> used, exceptions of <see cref=\"AddMany\"/>.\n\t\t/// </exception>\n\t\t/// <exception cref=\"UriFormatException\">Invalid URL format.</exception>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// string s = internet.http.Get(\"https://httpbin.org/anything\").Text();\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic static HttpResponseMessage Get(this HttpClient t, string url, bool dontWait = false, IEnumerable<string> headers = null, string auth = null, Action<HttpRequestMessage> also = null) {\n\t\t\tusing var m = HttpMessage_(HttpMethod.Get, url, headers, auth, also);\n\t\t\treturn t.Send(m, dontWait ? HttpCompletionOption.ResponseHeadersRead : HttpCompletionOption.ResponseContentRead);\n\t\t}\n\t\t\n\t\t//rejected. It's easy to use .NET methods directly (for those familiar with await). For the same reason rejected CancellationToken parameter of Get etc.\n\t\t///// <inheritdoc cref=\"Get(HttpClient, string, bool, IEnumerable{string}, string, Action{HttpRequestMessage}, CancellationToken)\"/>\n\t\t//public static Task<HttpResponseMessage> GetAsync(this HttpClient t, string url, bool dontWait = false, IEnumerable<string> headers = null, string auth = null, Action<HttpRequestMessage> also = null, CancellationToken cancel = default) {\n\t\t//\tusing var m = _HttpMessage(HttpMethod.Get, url, headers, auth, also);\n\t\t//\treturn t.SendAsync(m, dontWait ? HttpCompletionOption.ResponseHeadersRead : HttpCompletionOption.ResponseContentRead, cancel);\n\t\t//}\n\t\t\n\t\tinternal static HttpRequestMessage HttpMessage_(HttpMethod method, string url, IEnumerable<string> headers, string auth, Action<HttpRequestMessage> also, HttpContent content = null) {\n\t\t\tNot_.Null(url);\n\t\t\tvar m = new HttpRequestMessage(method, url);\n\t\t\tif (headers != null) m.Headers.AddMany(headers);\n\t\t\tif (auth != null) m.Headers.Add(\"Authorization\", \"Basic \" + Convert.ToBase64String(auth.ToUTF8()));\n\t\t\tif (content != null) m.Content = content;\n\t\t\talso?.Invoke(m);\n\t\t\treturn m;\n\t\t}\n\t\t\n\t\t//rejected. This could be used with 'also' parameter, and then could remove 'auth' parameter. But this library is mostly for non-programmers, and should make user code as simple as possible.\n\t\t///// <summary>\n\t\t///// Adds <c>Authorization</c> header for basic authentication.\n\t\t///// </summary>\n\t\t//public static void Auth(this HttpRequestMessage t, string user, string password) {\n\t\t//\tt.Headers.Add(\"Authorization\", \"Basic \" + Convert.ToBase64String($\"{user}:{password}\".ToUTF8()));\n\t\t//}\n\t\t\n\t\t/// <summary>\n\t\t/// Sends a GET request to the specified URL, and gets the response. Handles HTTP errors and exceptions.\n\t\t/// </summary>\n\t\t/// <param name=\"t\"></param>\n\t\t/// <param name=\"r\">Receives <c>HttpResponseMessage</c> object that can be used to get response content (web page HTML, JSON, file, etc), headers etc. See example. Will be <c>null</c> if failed because of an exception.</param>\n\t\t/// <param name=\"url\">URL. To create URL with urlencoded parameters you can use <see cref=\"internet.urlAppend\"/>.</param>\n\t\t/// <param name=\"dontWait\">Use <see cref=\"HttpCompletionOption.ResponseHeadersRead\"/>.</param>\n\t\t/// <param name=\"printError\">If failed, call <see cref=\"print.warning\"/>.</param>\n\t\t/// <inheritdoc cref=\"Get(HttpClient, string, bool, IEnumerable{string}, string, Action{HttpRequestMessage})\" path=\"/param\"/>\n\t\t/// <returns><c>false</c> if failed.</returns>\n\t\t/// <exception cref=\"UriFormatException\">Invalid URL format.</exception>\n\t\t/// <exception cref=\"Exception\">If <i>headers</i> used, exceptions of <see cref=\"AddMany\"/>.</exception>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// if (!internet.http.TryGet(out var r, \"https://httpbin.org/anything\", printError: true)) return;\n\t\t/// print.it(r.Text());\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic static bool TryGet(this HttpClient t, out HttpResponseMessage r, string url, bool dontWait = false, IEnumerable<string> headers = null, bool printError = false, string auth = null, Action<HttpRequestMessage> also = null) {\n\t\t\tusing var m = HttpMessage_(HttpMethod.Get, url, headers, auth, also);\n\t\t\ttry {\n\t\t\t\tr = t.Send(m, dontWait ? HttpCompletionOption.ResponseHeadersRead : HttpCompletionOption.ResponseContentRead);\n\t\t\t\tif (r.IsSuccessStatusCode) return true;\n\t\t\t\tif (printError) {\n\t\t\t\t\tvar s1 = $\"HTTP GET failed. {(int)r.StatusCode} ({r.StatusCode}), {r.ReasonPhrase}\";\n\t\t\t\t\tif (!dontWait) try { if (r.Text(ignoreError: true) is [_, ..] s2) s1 = s1 + \", \" + s2; } catch { }\n\t\t\t\t\tprint.warning(s1);\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tr = null;\n\t\t\t\tif (printError) print.warning($\"HTTP GET failed. {e.ToStringWithoutStack()}\");\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Sends a GET request to the specified URL, and gets the response. Saves the response content (file, web page, etc) in a file.\n\t\t/// </summary>\n\t\t/// <param name=\"t\"></param>\n\t\t/// <param name=\"url\">URL. To create URL with urlencoded parameters you can use <see cref=\"internet.urlAppend\"/>.</param>\n\t\t/// <param name=\"resultFile\">File path. The function uses <see cref=\"pathname.normalize\"/>. Creates parent directory if need.</param>\n\t\t/// <inheritdoc cref=\"Get(HttpClient, string, bool, IEnumerable{string}, string, Action{HttpRequestMessage})\" path=\"/param\"/>\n\t\t/// <returns>An <c>HttpResponseMessage</c> object that contains response headers etc. Rarely used.</returns>\n\t\t/// <exception cref=\"Exception\">\n\t\t/// Exceptions of <see cref=\"HttpClient.Send(HttpRequestMessage, HttpCompletionOption)\"/> and <see cref=\"Save\"/>.\n\t\t/// If <i>headers</i> used, exceptions of <see cref=\"AddMany\"/>.\n\t\t/// </exception>\n\t\tpublic static HttpResponseMessage Get(this HttpClient t, string url, string resultFile, IEnumerable<string> headers = null, string auth = null, Action<HttpRequestMessage> also = null) {\n\t\t\tNot_.Null(resultFile);\n\t\t\tvar r = Get(t, url, true, headers, auth, also).Save(resultFile);\n\t\t\tr.Dispose();\n\t\t\treturn r;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Sends a POST request to the specified URL, and gets the response.\n\t\t/// </summary>\n\t\t/// <param name=\"t\"></param>\n\t\t/// <param name=\"url\">URL.</param>\n\t\t/// <param name=\"content\">Data to post. Usually web form data (see <see cref=\"internet.formContent\"/>) or JSON (see <see cref=\"internet.jsonContent\"/>). Can be <c>null</c>.</param>\n\t\t/// <param name=\"dontWait\">Use <see cref=\"HttpCompletionOption.ResponseHeadersRead\"/>.</param>\n\t\t/// <inheritdoc cref=\"Get(HttpClient, string, bool, IEnumerable{string}, string, Action{HttpRequestMessage})\" path=\"/param\"/>\n\t\t/// <returns>An <c>HttpResponseMessage</c> object that can be used to get response content (web page HTML, JSON, file, etc), headers etc. To get content use <see cref=\"Text\"/> etc.</returns>\n\t\t/// <exception cref=\"Exception\">\n\t\t/// Exceptions of <see cref=\"HttpClient.Send(HttpRequestMessage, HttpCompletionOption)\"/>.\n\t\t/// If <i>headers</i> used, exceptions of <see cref=\"AddMany\"/>.\n\t\t/// </exception>\n\t\t/// <example>\n\t\t/// Post form data.\n\t\t/// Note: the <c>using</c> will close the file stream. Don't need it when content does not contain files.\n\t\t/// <code><![CDATA[\n\t\t/// using var content = internet.formContent((\"name1\", \"value1\"), (\"name2\", \"value2\")).AddFile(\"name3\", @\"C:\\Test\\file.png\");\n\t\t/// string s = internet.http.Post(\"https://httpbin.org/anything\", content).Text();\n\t\t/// ]]></code>\n\t\t/// Post object as JSON.\n\t\t/// <code><![CDATA[\n\t\t/// var v = new { a = \"A\", b = true };\n\t\t/// string s = internet.http.Post(\"https://httpbin.org/anything\", internet.jsonContent(v)).Text();\n\t\t/// print.it(s);\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic static HttpResponseMessage Post(this HttpClient t, string url, HttpContent content, IEnumerable<string> headers = null, bool dontWait = false, string auth = null, Action<HttpRequestMessage> also = null) {\n\t\t\tusing var m = HttpMessage_(HttpMethod.Post, url, headers, auth, also, content);\n\t\t\treturn t.Send(m, dontWait ? HttpCompletionOption.ResponseHeadersRead : HttpCompletionOption.ResponseContentRead);\n\t\t}\n\t\t//rejected: bool disposeContent = true (auto-close MultipartFormDataContent streams etc).\n\t\t//\tUsers may want to retry etc. Usually this is used in scripts that will exit soon. Usually not so important to close files immediately.\n\t\t//rejected. string resultFile = null. It seems POST is rarely used to download files. Also don't need parameter dontWait; HttpClient.PostAsync does not have it too.\n\t\t\n\t\t/// <summary>\n\t\t/// Sends a POST request to the specified URL, and gets the response. Handles HTTP errors and exceptions.\n\t\t/// </summary>\n\t\t/// <param name=\"t\"></param>\n\t\t/// <param name=\"r\">Receives <c>HttpResponseMessage</c> object that can be used to get response content (web page HTML, JSON, file, etc), headers etc. See example. Will be <c>null</c> if failed because of an exception.</param>\n\t\t/// <param name=\"url\">URL.</param>\n\t\t/// <param name=\"content\">Data to post. Usually web form data (see <see cref=\"internet.formContent\"/>) or JSON (see <see cref=\"internet.jsonContent\"/>). Can be <c>null</c>.</param>\n\t\t/// <param name=\"printError\">If failed, call <see cref=\"print.warning\"/>.</param>\n\t\t/// <param name=\"dontWait\">Use <see cref=\"HttpCompletionOption.ResponseHeadersRead\"/>.</param>\n\t\t/// <inheritdoc cref=\"Get(HttpClient, string, bool, IEnumerable{string}, string, Action{HttpRequestMessage})\" path=\"/param\"/>\n\t\t/// <returns><c>false</c> if failed.</returns>\n\t\t/// <exception cref=\"UriFormatException\">Invalid URL format.</exception>\n\t\t/// <exception cref=\"Exception\">If <i>headers</i> used, exceptions of <see cref=\"AddMany\"/>.</exception>\n\t\t/// <example>\n\t\t/// Post form data.\n\t\t/// <code><![CDATA[\n\t\t/// var content = internet.formContent((\"name1\", \"value1\"), (\"name2\", \"value2\"));\n\t\t/// if (!internet.http.TryPost(out var r, \"https://httpbin.org/anything\", content, printError: true)) return;\n\t\t/// print.it(r.Text());\n\t\t/// ]]></code>\n\t\t/// Post object as JSON.\n\t\t/// <code><![CDATA[\n\t\t/// var v = new { a = \"A\", b = true };\n\t\t/// if (!internet.http.TryPost(out var r, \"https://httpbin.org/anything\", internet.jsonContent(v), printError: true)) return;\n\t\t/// print.it(r.Text());\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic static bool TryPost(this HttpClient t, out HttpResponseMessage r, string url, HttpContent content, IEnumerable<string> headers = null, bool printError = false, bool dontWait = false, string auth = null, Action<HttpRequestMessage> also = null) {\n\t\t\tusing var m = HttpMessage_(HttpMethod.Post, url, headers, auth, also, content);\n\t\t\ttry {\n\t\t\t\tr = t.Send(m, dontWait ? HttpCompletionOption.ResponseHeadersRead : HttpCompletionOption.ResponseContentRead);\n\t\t\t\tif (r.IsSuccessStatusCode) return true;\n\t\t\t\tif (printError) {\n\t\t\t\t\tvar s1 = $\"HTTP POST failed. {(int)r.StatusCode} ({r.StatusCode}), {r.ReasonPhrase}\";\n\t\t\t\t\tif (!dontWait) try { if (r.Text(ignoreError: true) is [_, ..] s2) s1 = s1 + \", \" + s2; } catch { }\n\t\t\t\t\tprint.warning(s1);\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tr = null;\n\t\t\t\tif (printError) print.warning($\"HTTP POST failed. {e.ToStringWithoutStack()}\");\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\t#endregion\n\t\t\n\t\t#region HttpResponseMessage\n\t\t\n\t\t/// <summary>\n\t\t/// Gets content text as string. Downloads it if need.\n\t\t/// </summary>\n\t\t/// <param name=\"t\"></param>\n\t\t/// <param name=\"ignoreError\">Don't call <see cref=\"HttpResponseMessage.EnsureSuccessStatusCode\"/>.</param>\n\t\t/// <exception cref=\"HttpRequestException\">Failed HTTP request. No exception if <i>ignoreError</i> <c>true</c>.</exception>\n\t\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"HttpContent.ReadAsStringAsync()\"/>.</exception>\n\t\tpublic static string Text(this HttpResponseMessage t, bool ignoreError = false) {\n\t\t\tif (!ignoreError) t.EnsureSuccessStatusCode();\n\t\t\treturn t.Content.ReadAsStringAsync().Result_();\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets content data as <c>byte[]</c>. Downloads it if need. If content is text, the array contains that text, usually UTF-8.\n\t\t/// </summary>\n\t\t/// <param name=\"t\"></param>\n\t\t/// <param name=\"ignoreError\">Don't call <see cref=\"HttpResponseMessage.EnsureSuccessStatusCode\"/>.</param>\n\t\t/// <exception cref=\"HttpRequestException\">Failed HTTP request. No exception if <i>ignoreError</i> <c>true</c>.</exception>\n\t\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"HttpContent.ReadAsByteArrayAsync()\"/>.</exception>\n\t\tpublic static byte[] Bytes(this HttpResponseMessage t, bool ignoreError = false) {\n\t\t\tif (!ignoreError) t.EnsureSuccessStatusCode();\n\t\t\treturn t.Content.ReadAsByteArrayAsync().Result_();\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Parses content, which must be JSON, and returns the root node. Then you can access JSON elements like <c>var y = (string)r[\"x\"][\"y\"];</c>. Downloads content if need.\n\t\t/// Uses <see cref=\"JsonNode.Parse(ReadOnlySpan{byte}, JsonNodeOptions?, JsonDocumentOptions)\"/>.\n\t\t/// </summary>\n\t\t/// <param name=\"t\"></param>\n\t\t/// <param name=\"ignoreError\">Don't call <see cref=\"HttpResponseMessage.EnsureSuccessStatusCode\"/>.</param>\n\t\t/// <exception cref=\"HttpRequestException\">Failed HTTP request. No exception if <i>ignoreError</i> <c>true</c>.</exception>\n\t\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"HttpContent.ReadAsByteArrayAsync()\"/>.</exception>\n\t\t/// <exception cref=\"JsonException\">Failed to parse JSON.</exception>\n\t\tpublic static JsonNode Json(this HttpResponseMessage t, bool ignoreError = false)\n\t\t\t=> JsonNode.Parse(Bytes(t, ignoreError));\n\t\t\n\t\t/// <summary>\n\t\t/// Parses content, which must be JSON. From it creates/returns an object of type <i>T</i>. Downloads content if need.\n\t\t/// Uses <see cref=\"HttpContentJsonExtensions.ReadFromJsonAsync{T}(HttpContent, JsonSerializerOptions?, CancellationToken)\"/>.\n\t\t/// </summary>\n\t\t/// <param name=\"t\"></param>\n\t\t/// <exception cref=\"HttpRequestException\">Failed HTTP request.</exception>\n\t\t/// <exception cref=\"Exception\">Exceptions of <c>ReadFromJsonAsync</c>.</exception>\n\t\tpublic static T Json<T>(this HttpResponseMessage t)\n\t\t\t=> t.EnsureSuccessStatusCode().Content.ReadFromJsonAsync<T>().Result_();\n\t\t\n\t\t/// <summary>\n\t\t/// Saves content in a file. Downloads if need.\n\t\t/// </summary>\n\t\t/// <param name=\"t\"></param>\n\t\t/// <param name=\"file\">File path. The function uses <see cref=\"pathname.normalize\"/>. Creates parent directory if need.</param>\n\t\t/// <returns>This.</returns>\n\t\t/// <exception cref=\"HttpRequestException\">Failed HTTP request.</exception>\n\t\t/// <exception cref=\"ArgumentException\">Not full path.</exception>\n\t\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"HttpContent.ReadAsStream()\"/>, <see cref=\"File.Create(string)\"/> and other used functions.</exception>\n\t\t/// <remarks>\n\t\t/// By default <see cref=\"HttpClient\"/> downloads content to a memory buffer before returning. To avoid it, use <i>completionOption</i> <see cref=\"HttpCompletionOption.ResponseHeadersRead\"/>, or <see cref=\"Get(HttpClient, string, bool, IEnumerable{string}, string, Action{HttpRequestMessage})\"/> with <i>dontWait</i> <c>true</c>. Then call this function (it will download the file), and finally dispose the <see cref=\"HttpResponseMessage\"/>.\n\t\t/// </remarks>\n\t\t/// <seealso cref=\"Get(HttpClient, string, string, IEnumerable{string}, string, Action{HttpRequestMessage})\"/>\n\t\tpublic static HttpResponseMessage Save(this HttpResponseMessage t, string file) {\n\t\t\tt.EnsureSuccessStatusCode();\n\t\t\tfilesystem.createDirectoryFor(file = pathname.normalize(file));\n\t\t\tusing var s1 = t.Content.ReadAsStream();\n\t\t\tusing var s2 = File.Create(file);\n\t\t\t_GetDecompressStream(t, s1).CopyTo(s2);\n\t\t\treturn t;\n\t\t\t//rejected: decompress in all funcs. Then cannot use ReadAsByteArrayAsync, ReadAsStringAsync, ReadFromJsonAsync<T>.\n\t\t}\n\t\t\n\t\tstatic Stream _GetDecompressStream(HttpResponseMessage t, Stream s) {\n\t\t\tvar ce = t.Content.Headers.ContentEncoding;\n\t\t\tif (ce.Count == 1) {\n\t\t\t\tswitch (ce.First().Lower()) {\n\t\t\t\tcase \"br\": return new BrotliStream(s, CompressionMode.Decompress);\n\t\t\t\tcase \"gzip\": return new GZipStream(s, CompressionMode.Decompress);\n\t\t\t\tcase \"deflate\": return new DeflateStream(s, CompressionMode.Decompress);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn s;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Downloads content to stream and provides the progress.\n\t\t/// </summary>\n\t\t/// <param name=\"t\"></param>\n\t\t/// <param name=\"stream\">Writes to this stream.</param>\n\t\t/// <param name=\"progress\">Calls this callback function to report the progress. If <c>null</c>, shows standard progress dialog.</param>\n\t\t/// <param name=\"cancel\">Can be used to cancel.</param>\n\t\t/// <param name=\"disposeStream\">Call <c>stream.Dispose();</c>.</param>\n\t\t/// <param name=\"progressText1\">Progress dialog heading text. Default: <c>\"Downloading\"</c>.</param>\n\t\t/// <returns><c>false</c> if canceled.</returns>\n\t\t/// <exception cref=\"HttpRequestException\">Failed HTTP request.</exception>\n\t\t/// <exception cref=\"Exception\">Other exceptions.</exception>\n\t\t/// <remarks>\n\t\t/// By default <see cref=\"HttpClient\"/> downloads content to a memory buffer before returning. To avoid it, use <i>completionOption</i> <see cref=\"HttpCompletionOption.ResponseHeadersRead\"/>, or <see cref=\"Get(HttpClient, string, bool, IEnumerable{string}, string, Action{HttpRequestMessage})\"/> with <i>dontWait</i> <c>true</c>. Then call this function (it will download the file), and finally dispose the <see cref=\"HttpResponseMessage\"/>.\n\t\t/// \n\t\t/// Cannot provide the progress percentage if the content length is unknown. Top reasons:\n\t\t/// - The HTTP server uses chunked transfer encoding.\n\t\t/// - The HTTP server uses content compression and the <see cref=\"HttpClient\"/> is configured to automatically decompress (for example <see cref=\"internet.http\"/>). Instead of <c>internet.http</c> create a <see cref=\"HttpClient\"/> and optionally set header <c>\"Accept-Encoding: br, gzip, deflate\"</c>. This function will decompress.\n\t\t/// </remarks>\n\t\tpublic static bool Download(this HttpResponseMessage t, Stream stream, Action<ProgressArgs> progress = null, CancellationToken cancel = default, bool disposeStream = false, string progressText1 = null) {\n\t\t\tdialog pd = null;\n\t\t\tStream decomp = null;\n\t\t\ttry {\n\t\t\t\tArgumentNullException.ThrowIfNull(stream, nameof(stream));\n\t\t\t\tt.EnsureSuccessStatusCode();\n\t\t\t\tif (cancel.IsCancellationRequested) return false;\n\t\t\t\tlong size = t.Content.Headers.ContentLength ?? 0;\n\t\t\t\t//print.it(size);\n\t\t\t\t//print.it(t);\n\t\t\t\t//BAD: content length is unknown for many files if the HttpClient uses AutomaticDecompression (eg internet.http).\n\t\t\t\t//\tThe header is removed, because impossible to know the decompressed length now.\n\t\t\t\t//BAD: content length is unknown if \"Transfer-Encoding: chunked\". Eg most webservers use chunked for html files.\n\t\t\t\t//GOOD: usually web servers don't use \"Content-Encoding\" and \"Transfer-Encoding\" for big files that are usually compressed, eg exe, zip, png.\n\t\t\t\t\n\t\t\t\tusing var s1 = t.Content.ReadAsStream();\n\t\t\t\t\n\t\t\t\tstring filename = null;\n\t\t\t\tstring _DialogText(long bytes) => $\"{filename}\\n{bytes / 1048576d:0.#} MB\";\n\t\t\t\tif (progress == null) {\n\t\t\t\t\tfilename = pathname.getName(t.RequestMessage.RequestUri.AbsolutePath);\n\t\t\t\t\tpd = dialog.showProgress(size == 0, progressText1 ?? \"Downloading\", _DialogText(size));\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tStream stream1 = s1, stream2 = stream;\n\t\t\t\tstring ce = t.Content.Headers.ContentEncoding.FirstOrDefault()?.Lower();\n\t\t\t\tif (ce != null) {\n\t\t\t\t\tif (ce is not (\"br\" or \"gzip\" or \"deflate\") || t.Content.Headers.ContentEncoding.Count != 1) throw new NotSupportedException(\"Content-Encoding\");\n\t\t\t\t\tif (size > 0 && size < 10_000_000) stream2 = new MemoryStream((int)size); //to display progress we need a temp stream; finally will decompress it to *stream*\n\t\t\t\t\telse stream1 = decomp = _DecompStream(ce, s1); //can't display progress. Will decompress directly when reading.\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tstatic Stream _DecompStream(string ce, Stream s)\n\t\t\t\t\t=> ce switch { \"br\" => new BrotliStream(s, CompressionMode.Decompress), \"gzip\" => new GZipStream(s, CompressionMode.Decompress), _ => new DeflateStream(s, CompressionMode.Decompress) };\n\t\t\t\t\n\t\t\t\tvar b = new byte[16384];\n\t\t\t\tlong have = 0; int n, ppercent = 0, ptime = Environment.TickCount;\n\t\t\t\twhile ((n = stream1.Read(b)) > 0) {\n\t\t\t\t\tstream2.Write(b, 0, n);\n\t\t\t\t\tif (cancel.IsCancellationRequested) return false;\n\t\t\t\t\thave += n;\n\t\t\t\t\tint percent = 0, time = 0; bool updateProgress;\n\t\t\t\t\tif (size > 0) {\n\t\t\t\t\t\tpercent = (int)(have * 100 / size);\n\t\t\t\t\t\tif (updateProgress = percent > ppercent) ppercent = percent;\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttime = Environment.TickCount;\n\t\t\t\t\t\tif (updateProgress = time - ptime > 200) ptime = time;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tif (pd != null) {\n\t\t\t\t\t\tif (!pd.IsOpen) { pd = null; return false; }\n\t\t\t\t\t\tif (updateProgress) {\n\t\t\t\t\t\t\tif (size > 0) pd.Send.Progress(percent);\n\t\t\t\t\t\t\telse pd.Send.ChangeText2(_DialogText(have), resizeDialog: false);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (updateProgress) {\n\t\t\t\t\t\t\tProgressArgs pa = new(size, have, percent);\n\t\t\t\t\t\t\tprogress(pa);\n\t\t\t\t\t\t\tif (pa.Cancel) return false;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (cancel.IsCancellationRequested) return false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (size == 0) progress?.Invoke(new(have, have, 100));\n\t\t\t\t\n\t\t\t\tif (stream2 != stream) {\n\t\t\t\t\tstream2.Position = 0;\n\t\t\t\t\tdecomp = _DecompStream(ce, stream2);\n\t\t\t\t\tdecomp.CopyTo(stream);\n\t\t\t\t}\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tdecomp?.Dispose();\n\t\t\t\tif (disposeStream) stream.Dispose();\n\t\t\t\tpd?.Send.Close();\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Downloads content to file and provides the progress.\n\t\t/// </summary>\n\t\t/// <param name=\"file\">File path. The function uses <see cref=\"pathname.normalize\"/>. Creates parent directory if need.</param>\n\t\t/// <inheritdoc cref=\"Download(HttpResponseMessage, Stream, Action{ProgressArgs}, CancellationToken, bool, string)\"/>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// if (!internet.http.Get(url, true).Download(zip)) return;\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic static bool Download(this HttpResponseMessage t, string file, Action<ProgressArgs> progress = null, CancellationToken cancel = default, string progressText1 = null) {\n\t\t\tt.EnsureSuccessStatusCode();\n\t\t\tfilesystem.createDirectoryFor(file = pathname.normalize(file));\n\t\t\treturn Download(t, File.Create(file), progress, cancel, disposeStream: true, progressText1);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// The async version of <see cref=\"Download(HttpResponseMessage, Stream, Action{ProgressArgs}, CancellationToken, bool, string)\"/>.\n\t\t/// </summary>\n\t\t/// <inheritdoc cref=\"Download(HttpResponseMessage, Stream, Action{ProgressArgs}, CancellationToken, bool, string)\"/>\n\t\tpublic static Task<bool> DownloadAsync(this HttpResponseMessage t, Stream stream, Action<ProgressArgs> progress = null, CancellationToken cancel = default, bool disposeStream = false, string progressText1 = null) {\n\t\t\tif (stream == null || !t.IsSuccessStatusCode) {\n\t\t\t\tif (disposeStream) stream.Dispose();\n\t\t\t\tArgumentNullException.ThrowIfNull(stream, nameof(stream));\n\t\t\t\tt.EnsureSuccessStatusCode();\n\t\t\t}\n\t\t\tif (progress != null && SynchronizationContext.Current is { } ct && ct.GetType() != typeof(SynchronizationContext)) { //call in this thread\n\t\t\t\tbool canceled = false;\n\t\t\t\tvar progress0 = progress;\n\t\t\t\tprogress = pa => {\n\t\t\t\t\t//runs sync in task thread\n\t\t\t\t\tif (canceled) { pa.Cancel = true; return; }\n\t\t\t\t\tct.Post(_ => {\n\t\t\t\t\t\t//runs async in caller's thread\n\t\t\t\t\t\tif (canceled) return;\n\t\t\t\t\t\tprogress0(pa);\n\t\t\t\t\t\tif (pa.Cancel) canceled = true;\n\t\t\t\t\t}, null);\n\t\t\t\t};\n\t\t\t\t\n\t\t\t\t//note: don't use Progress<T>. With default synccontext it calls in random thread pool thread.\n\t\t\t\t//\tThis code works like Progress<T> with other synccontexts.\n\t\t\t}\n\t\t\treturn Task.Run(() => Download(t, stream, progress, cancel, disposeStream, progressText1));\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// The async version of <see cref=\"Download(HttpResponseMessage, string, Action{ProgressArgs}, CancellationToken, string)\"/>.\n\t\t/// </summary>\n\t\t/// <inheritdoc cref=\"Download(HttpResponseMessage, string, Action{ProgressArgs}, CancellationToken, string)\"/>\n\t\tpublic static Task<bool> DownloadAsync(this HttpResponseMessage t, string file, Action<ProgressArgs> progress = null, CancellationToken cancel = default) {\n\t\t\tt.EnsureSuccessStatusCode();\n\t\t\tfilesystem.createDirectoryFor(file = pathname.normalize(file));\n\t\t\tvar stream = File.Create(file);\n\t\t\treturn DownloadAsync(t, stream, progress, cancel, disposeStream: true);\n\t\t}\n\t\t\n\t\t#endregion\n\t}\n\t\n\t/// <summary>\n\t/// Arguments for a progress callback function.\n\t/// </summary>\n\t/// <param name=\"Total\">The max expected value. Or 0 if unknown.</param>\n\t/// <param name=\"Current\">The current value.</param>\n\t/// <param name=\"Percent\"><c>Current * 100 / Total</c>. Or 0 if unknown.</param>\n\tpublic record class ProgressArgs(long Total, long Current, int Percent) {\n\t\t/// <summary>\n\t\t/// The callback function can use this to cancel the operation.\n\t\t/// </summary>\n\t\tpublic bool Cancel { get; set; }\n\t}\n}\n"
  },
  {
    "path": "Au/Other/lastError.cs",
    "content": "namespace Au {\n\t/// <summary>\n\t/// Gets, sets or clears the last error code of Windows API. Gets error text.\n\t/// </summary>\n\t/// <remarks>\n\t/// Many Windows API functions, when failed, set an error code. Code 0 means no error. It is stored in an internal thread-specific <c>int</c> variable. But only if the API declaration's <c>DllImport</c> attribute has <c>SetLastError = true</c>.\n\t/// \n\t/// Some functions of this library simply call these API functions and don't throw exception when API fail. For example, most <see cref=\"wnd\"/> property-get functions.\n\t/// When failed, they return <c>false</c>/0/<c>null</c>/empty. Then you can use <see cref=\"code\"/> to get the error code or <see cref=\"message\"/> to get error text.\n\t/// \n\t/// Most of functions set error code only when failed, and don't clear the old error code when succeeded. Therefore may need to call <see cref=\"clear\"/> before.\n\t/// \n\t/// Windows API error code definitions and documentation are not included in this library. You can look for them in API function documentation on the internet.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// wnd w = wnd.find(\"Notepag\");\n\t/// lastError.clear();\n\t/// bool enabled = w.IsEnabled; //returns true if enabled, false if disabled or failed\n\t/// if(!enabled && lastError.code != 0) { print.it(lastError.message); return; } //1400, Invalid window handle\n\t/// print.it(enabled);\n\t/// ]]></code>\n\t/// </example>\n\tpublic static class lastError {\n\t\t/// <summary>\n\t\t/// Calls API <ms>SetLastError</ms>(0), which clears the Windows API last error code of this thread.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Need it before calling some functions if you want to use <see cref=\"code\"/> or <see cref=\"message\"/>.\n\t\t/// The same as <c>lastError.code = 0;</c>.\n\t\t/// </remarks>\n\t\tpublic static void clear() => Api.SetLastError(0);\n\t\t\n\t\t/// <summary>\n\t\t/// Gets (<see cref=\"Marshal.GetLastWin32Error\"/>) or sets (API <ms>SetLastError</ms>) the Windows API last error code of this thread.\n\t\t/// </summary>\n\t\tpublic static int code {\n\t\t\tget => Marshal.GetLastWin32Error();\n\t\t\tset => Api.SetLastError(value);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets the text message of the Windows API last error code of this thread.\n\t\t/// </summary>\n\t\t/// <returns><c>null</c> if the code is 0.</returns>\n\t\t/// <remarks>\n\t\t/// The string always ends with <c>\".\"</c>.\n\t\t/// </remarks>\n\t\tpublic static string message => messageFor(code);\n\t\t\n\t\t/// <summary>\n\t\t/// Gets the text message of a Windows API error code.\n\t\t/// </summary>\n\t\t/// <returns><c>null</c> if <i>errorCode</i> is 0.</returns>\n\t\t/// <remarks>\n\t\t/// The string always ends with <c>\".\"</c>.\n\t\t/// </remarks>\n\t\tpublic static unsafe string messageFor(int errorCode) {\n\t\t\tif (errorCode == 0) return null;\n\t\t\tif (errorCode == 1) return \"The requested data or action is unavailable. (0x1).\"; //or ERROR_INVALID_FUNCTION, but it's rare\n\t\t\tstring s = \"Unknown exception\";\n\t\t\tchar* p = null;\n\t\t\tconst uint fl = Api.FORMAT_MESSAGE_FROM_SYSTEM | Api.FORMAT_MESSAGE_ALLOCATE_BUFFER | Api.FORMAT_MESSAGE_IGNORE_INSERTS;\n\t\t\tint r = Api.FormatMessage(fl, default, errorCode, 0, &p, 0, default);\n\t\t\tif (p != null) {\n\t\t\t\twhile (r > 0 && p[r - 1] <= ' ') r--;\n\t\t\t\tif (r > 0) {\n\t\t\t\t\tif (p[r - 1] == '.') r--;\n\t\t\t\t\ts = new string(p, 0, r);\n\t\t\t\t}\n\t\t\t\tApi.LocalFree(p);\n\t\t\t}\n\t\t\ts = (uint)errorCode <= 0xffff ? $\"{s} ({errorCode}).\" : $\"{s} (0x{errorCode:X}).\";\n\t\t\treturn s;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au/Other/opt.cs",
    "content": "namespace Au {\n\t/// <summary>\n\t/// Ambient options for some functions of this library.\n\t/// </summary>\n\t/// <remarks>\n\t/// Some frequently used functions of this library have some options (settings). For example <see cref=\"keys.send\"/> allows to change speed, text sending method, etc. Passing options as parameters in each call usually isn't what you want to do in automation scripts. Instead use this class. See examples.\n\t/// \n\t/// To store these options, internally is used <see cref=\"AsyncLocal{T}\"/>. It means that a new task or thread inherits a copy of options of the caller. It can't modify options of the caller or other tasks/threads.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// opt.key.KeySpeed = 50;\n\t/// ]]></code>\n\t/// Set options for trigger actions.\n\t/// <code><![CDATA[\n\t/// Triggers.Options.BeforeAction = o => { opt.key.KeySpeed = 50; };\n\t/// ]]></code>\n\t/// </example>\n\tpublic static class opt {\n\t\t/// <summary>\n\t\t/// Options for mouse functions (class <see cref=\"Au.mouse\"/> and functions that use it).\n\t\t/// </summary>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// opt.mouse.ClickSpeed = 100;\n\t\t/// mouse.click();\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic static OMouse mouse => OMouse.Ambient_;\n\t\t\n\t\t/// <summary>\n\t\t/// Options for keyboard and clipboard functions (classes <see cref=\"keys\"/>, <see cref=\"clipboard\"/> and functions that use them).\n\t\t/// </summary>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// opt.key.KeySpeed = 100;\n\t\t/// keys.send(\"Right*10 Ctrl+A\");\n\t\t/// ]]></code>\n\t\t/// Use a <see cref=\"keys\"/> instance.\n\t\t/// <code><![CDATA[\n\t\t/// var k = new keys(opt.key); //create new keys instance and copy options from opt.key to it\n\t\t/// k.Options.KeySpeed = 100; //changes option of k but not of opt.key\n\t\t/// k.Add(\"Right*10 Ctrl+A\").SendNow(); //uses options of k\n\t\t/// ]]></code>\n\t\t/// Set options for trigger actions.\n\t\t/// <code><![CDATA[\n\t\t/// Triggers.Options.BeforeAction = o => { opt.key.KeySpeed = 50; };\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic static OKey key => OKey.Ambient_;\n\t\t\n\t\t/// <summary>\n\t\t/// Options for showing run-time warnings and other info that can be useful to find problems in code at run time.\n\t\t/// </summary>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// opt.warnings.Verbose = false;\n\t\t/// print.warning(\"Example\");\n\t\t/// print.warning(\"Example\");\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic static OWarnings warnings => OWarnings.Ambient_;\n\t\t\n\t\t/// <summary>\n\t\t/// Obsolete. Use <see cref=\"Seconds\"/> instead.\n\t\t/// For backward compatibility, wait functions still use <c>opt.wait.DoEvents</c> if <c>Seconds.DoEvents</c> not specified.\n\t\t/// </summary>\n\t\t[EditorBrowsable(EditorBrowsableState.Never)]\n\t\tpublic static OWait wait => OWait.Ambient_;\n\t\t\n\t\t/// <summary>\n\t\t/// Obsolete.\n\t\t/// </summary>\n\t\t[Obsolete(\"Use opt instead. To set options for triggers can be used code like this: Triggers.Options.BeforeAction = o => { opt.key.TextSpeed = 5; opt.mouse.ClickSpeed = 30; };\"), EditorBrowsable(EditorBrowsableState.Never)]\n\t\tpublic static class init {\n\t\t\t/// <summary>\n\t\t\t/// Obsolete. Same as <see cref=\"opt.mouse\"/>.\n\t\t\t/// </summary>\n\t\t\tpublic static OMouse mouse => opt.mouse;\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// Obsolete. Same as <see cref=\"opt.key\"/>.\n\t\t\t/// </summary>\n\t\t\tpublic static OKey key => opt.key;\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// Obsolete. Same as <see cref=\"opt.warnings\"/>.\n\t\t\t/// </summary>\n\t\t\tpublic static OWarnings warnings => opt.warnings;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Creates temporary scopes for options.\n\t\t/// Example: <c>using(opt.scope.key()) { opt.key.KeySpeed=5; ... }</c>.\n\t\t/// </summary>\n\t\tpublic static class scope {\n\t\t\t/// <summary>\n\t\t\t/// Creates temporary scope for <see cref=\"opt.mouse\"/> options. See example.\n\t\t\t/// </summary>\n\t\t\t/// <param name=\"inherit\">If <c>true</c> (default), inherit current options. If <c>false</c>, uses default options.</param>\n\t\t\t/// <example>\n\t\t\t/// <code><![CDATA[\n\t\t\t/// print.it(opt.mouse.ClickSpeed);\n\t\t\t/// using(opt.scope.mouse()) {\n\t\t\t/// \topt.mouse.ClickSpeed = 100;\n\t\t\t/// \tprint.it(opt.mouse.ClickSpeed);\n\t\t\t/// } //here restored automatically\n\t\t\t/// print.it(opt.mouse.ClickSpeed);\n\t\t\t/// ]]></code>\n\t\t\t/// </example>\n\t\t\tpublic static UsingEndAction mouse(bool inherit = true) {\n\t\t\t\tvar old = OMouse.Scope_(inherit);\n\t\t\t\treturn new UsingEndAction(() => OMouse.Ambient_ = old);\n\t\t\t}\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// Creates temporary scope for <see cref=\"opt.key\"/> options. See example.\n\t\t\t/// </summary>\n\t\t\t/// <param name=\"inherit\">If <c>true</c> (default), inherit current options. If <c>false</c>, uses default options.</param>\n\t\t\t/// <example>\n\t\t\t/// <code><![CDATA[\n\t\t\t/// print.it(opt.key.KeySpeed);\n\t\t\t/// using(opt.scope.key()) {\n\t\t\t/// \topt.key.KeySpeed = 5;\n\t\t\t/// \tprint.it(opt.key.KeySpeed);\n\t\t\t/// } //here restored automatically\n\t\t\t/// print.it(opt.key.KeySpeed);\n\t\t\t/// ]]></code>\n\t\t\t/// </example>\n\t\t\tpublic static UsingEndAction key(bool inherit = true) {\n\t\t\t\tvar old = OKey.Scope_(inherit);\n\t\t\t\treturn new UsingEndAction(() => OKey.Ambient_ = old);\n\t\t\t}\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// Creates temporary scope for <see cref=\"opt.warnings\"/> options. See example.\n\t\t\t/// </summary>\n\t\t\t/// <param name=\"inherit\">If <c>true</c> (default), inherit current options. If <c>false</c>, uses default options.</param>\n\t\t\t/// <example>\n\t\t\t/// <code><![CDATA[\n\t\t\t/// opt.warnings.Verbose = false;\n\t\t\t/// print.it(opt.warnings.Verbose, opt.warnings.IsDisabled(\"Test*\"));\n\t\t\t/// using(opt.scope.warnings()) {\n\t\t\t/// \topt.warnings.Verbose = true;\n\t\t\t/// \topt.warnings.Disable(\"Test*\");\n\t\t\t/// \tprint.it(opt.warnings.Verbose, opt.warnings.IsDisabled(\"Test*\"));\n\t\t\t/// } //here restored automatically\n\t\t\t/// print.it(opt.warnings.Verbose, opt.warnings.IsDisabled(\"Test*\"));\n\t\t\t/// ]]></code>\n\t\t\t/// </example>\n\t\t\tpublic static UsingEndAction warnings(bool inherit = true) {\n\t\t\t\tvar old = OWarnings.Scope_(inherit);\n\t\t\t\treturn new UsingEndAction(() => OWarnings.Ambient_ = old);\n\t\t\t}\n\t\t\t\n\t\t\t///\n\t\t\t[EditorBrowsable(EditorBrowsableState.Never)] //obsolete\n\t\t\tpublic static UsingEndAction wait(bool inherit = true) {\n\t\t\t\tvar old = OWait.Scope_(inherit);\n\t\t\t\treturn new UsingEndAction(() => OWait.Ambient_ = old);\n\t\t\t}\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// Creates temporary scope for all options. See example.\n\t\t\t/// </summary>\n\t\t\t/// <param name=\"inherit\">If <c>true</c> (default), inherit current options. If <c>false</c>, uses default options.</param>\n\t\t\t/// <example>\n\t\t\t/// <code><![CDATA[\n\t\t\t/// print.it(opt.key.KeySpeed, opt.mouse.ClickSpeed);\n\t\t\t/// using(opt.scope.all()) {\n\t\t\t/// \topt.key.KeySpeed = 5;\n\t\t\t/// \topt.mouse.ClickSpeed = 50;\n\t\t\t/// \tprint.it(opt.key.KeySpeed, opt.mouse.ClickSpeed);\n\t\t\t/// } //here restored automatically\n\t\t\t/// print.it(opt.key.KeySpeed, opt.mouse.ClickSpeed);\n\t\t\t/// ]]></code>\n\t\t\t/// </example>\n\t\t\tpublic static UsingEndAction all(bool inherit = true/*, int? speed = null*/) {\n\t\t\t\tvar o1 = OMouse.Scope_(inherit);\n\t\t\t\tvar o2 = OKey.Scope_(inherit);\n\t\t\t\tvar o3 = OWarnings.Scope_(inherit);\n\t\t\t\tvar o4 = OWait.Scope_(inherit);\n\t\t\t\treturn new UsingEndAction(() => {\n\t\t\t\t\tOMouse.Ambient_ = o1;\n\t\t\t\t\tOKey.Ambient_ = o2;\n\t\t\t\t\tOWarnings.Ambient_ = o3;\n\t\t\t\t\tOWait.Ambient_ = o4;\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n}\n\nnamespace Au.Types {\n\t/// <summary>\n\t/// Options for functions of class <see cref=\"mouse\"/>.\n\t/// </summary>\n\t/// <remarks>\n\t/// Total <c>Click(x, y)</c> time is: mouse move + <see cref=\"MoveSleepFinally\"/> + button down + <see cref=\"ClickSpeed\"/> + button up + <see cref=\"ClickSpeed\"/> + <see cref=\"ClickSleepFinally\"/>.\n\t/// </remarks>\n\t/// <seealso cref=\"opt.mouse\"/>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// opt.mouse.MoveSpeed = 30;\n\t/// ]]></code>\n\t/// </example>\n\tpublic sealed class OMouse {\n\t\tint _threadId;\n\t\tstatic AsyncLocal<OMouse> s_ambient = new();\n\t\t\n\t\tinternal static OMouse Ambient_ {\n\t\t\tget {\n\t\t\t\tvar threadId = Environment.CurrentManagedThreadId;\n\t\t\t\tif (s_ambient.Value is { } v) {\n\t\t\t\t\tif (v._threadId != threadId) s_ambient.Value = v = new(v) { _threadId = threadId };\n\t\t\t\t} else {\n\t\t\t\t\ts_ambient.Value = v = new() { _threadId = threadId };\n\t\t\t\t}\n\t\t\t\treturn v;\n\t\t\t}\n\t\t\tset { Debug.Assert(value?._threadId != 0); s_ambient.Value = value; } //used only to restore scope\n\t\t}\n\t\t\n\t\tinternal static OMouse Scope_(bool inherit) {\n\t\t\tvar old = s_ambient.Value;\n\t\t\ts_ambient.Value = (old != null && inherit) ? new(old) { _threadId = Environment.CurrentManagedThreadId } : null; //lazy\n\t\t\treturn old;\n\t\t}\n\t\t\n\t\tstruct _Fields { //makes easier to init, reset or copy fields\n\t\t\tpublic _Fields() { }\n\t\t\tpublic int ClickSpeed = 20, MoveSpeed, ClickSleepFinally = 10, MoveSleepFinally = 10;\n\t\t\tpublic bool Relaxed;\n\t\t}\n\t\t_Fields _f;\n\t\t\n\t\t/// <summary>\n\t\t/// Initializes this instance with default values or values copied from another instance.\n\t\t/// </summary>\n\t\t/// <param name=\"other\">If not <c>null</c>, copies its options into this variable.</param>\n\t\tinternal OMouse(OMouse other = null) //don't need public like OKey\n\t\t{\n\t\t\tif (other != null) {\n\t\t\t\t_f = other._f;\n\t\t\t} else {\n\t\t\t\t_f = new();\n\t\t\t}\n\t\t}\n\t\t\n\t\tint _Set(int value, int max) {\n\t\t\tif ((uint)value > max) throw new ArgumentOutOfRangeException(null, \"Max \" + max);\n\t\t\treturn value;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// How long to wait (milliseconds) after sending each mouse button down or up event (2 events for click, 4 for double-click).\n\t\t/// Default: 20.\n\t\t/// </summary>\n\t\t/// <value>Valid values: 0 - 1000 (1 s).</value>\n\t\t/// <exception cref=\"ArgumentOutOfRangeException\"></exception>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// opt.mouse.ClickSpeed = 30;\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic int ClickSpeed {\n\t\t\tget => _f.ClickSpeed;\n\t\t\tset => _f.ClickSpeed = _Set(value, 1000);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// If not 0, makes mouse movements slower, not instant.\n\t\t/// Default: 0.\n\t\t/// </summary>\n\t\t/// <value>Valid values: 0 (instant) - 10000 (slowest).</value>\n\t\t/// <exception cref=\"ArgumentOutOfRangeException\"></exception>\n\t\t/// <remarks>\n\t\t/// Used by <see cref=\"mouse.move\"/>, <see cref=\"mouse.click\"/> and other functions that generate mouse movement events, except <see cref=\"mouse.moveBy(string, double)\"/>.\n\t\t/// It is not milliseconds or some other unit. It adds intermediate mouse movements and small delays when moving the mouse cursor to the specified point. The speed also depends on the distance.\n\t\t/// Value 0 (default) does not add intermediate mouse movements. Adds at least 1 if some mouse buttons are pressed. Value 1 adds at least 1 intermediate mouse movement. Values 10-50 are good for visually slow movements.\n\t\t/// </remarks>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// opt.mouse.MoveSpeed = 30;\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic int MoveSpeed {\n\t\t\tget => _f.MoveSpeed;\n\t\t\tset => _f.MoveSpeed = _Set(value, 10000);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// How long to wait (milliseconds) before a \"mouse click\" or \"mouse wheel\" function returns.\n\t\t/// Default: 10.\n\t\t/// </summary>\n\t\t/// <value>Valid values: 0 - 10000 (10 s).</value>\n\t\t/// <exception cref=\"ArgumentOutOfRangeException\"></exception>\n\t\t/// <remarks>\n\t\t/// The \"click\" functions also sleep <see cref=\"ClickSpeed\"/> ms after button down and up. Default <c>ClickSpeed</c> is 20, default <c>ClickSleepFinally</c> is 10, therefore default click time without mouse-move is 20+20+10=50.\n\t\t/// </remarks>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// opt.mouse.ClickSpeedFinally = 30;\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic int ClickSleepFinally {\n\t\t\tget => _f.ClickSleepFinally;\n\t\t\tset => _f.ClickSleepFinally = _Set(value, 10000);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// How long to wait (milliseconds) after moving the mouse cursor. Used in \"move+click\" functions too.\n\t\t/// Default: 10.\n\t\t/// </summary>\n\t\t/// <value>Valid values: 0 - 1000 (1 s).</value>\n\t\t/// <exception cref=\"ArgumentOutOfRangeException\"></exception>\n\t\t/// <remarks>\n\t\t/// Used by <see cref=\"mouse.move\"/> (finally), <see cref=\"mouse.click\"/> (between moving and clicking) and other functions that generate mouse movement events.\n\t\t/// </remarks>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// opt.mouse.MoveSpeedFinally = 30;\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic int MoveSleepFinally {\n\t\t\tget => _f.MoveSleepFinally;\n\t\t\tset => _f.MoveSleepFinally = _Set(value, 1000);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Make some functions less strict (throw less exceptions etc).\n\t\t/// Default: <c>false</c>.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// This option is used by these functions:\n\t\t/// - <see cref=\"mouse.move\"/>, <see cref=\"mouse.click\"/> and other functions that move the cursor (mouse pointer):\\\n\t\t///   <c>false</c> - throw exception if cannot move the cursor to the specified x y. For example if the x y is not in screen.\\\n\t\t///   <c>true</c> - try to move anyway. Don't throw exception, regardless of the final cursor position (which probably will be at a screen edge).\n\t\t/// - <see cref=\"mouse.move\"/>, <see cref=\"mouse.click\"/> and other functions that move the cursor (mouse pointer):\\\n\t\t///   <c>false</c> - before moving the cursor, wait while a mouse button is pressed by the user or another thread. It prevents an unintended drag-drop.\\\n\t\t///   <c>true</c> - do not wait.\n\t\t/// - <see cref=\"mouse.click\"/> and other functions that click or press a mouse button using window coordinates:\\\n\t\t///   <c>false</c> - don't allow to click in another window. If need, activate the specified window (or its top-level parent). If that does not help, throw exception. However if the window is a control, allow x y anywhere in its top-level parent window.\\\n\t\t///   <c>true</c> - allow to click in another window. Don't activate the window and don't throw exception.\n\t\t/// </remarks>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// opt.mouse.Relaxed = true;\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic bool Relaxed {\n\t\t\tget => _f.Relaxed;\n\t\t\tset => _f.Relaxed = value;\n\t\t}\n\t\t\n\t\t///\n\t\tpublic override string ToString() =>\n\t\t\t$\"{{{string.Join(\", \", GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance).Select(p => $\"{p.Name} = {p.GetValue(this)}\"))}}}\";\n\t}\n\t\n\t/// <summary>\n\t/// Options for functions of class <see cref=\"keys\"/>.\n\t/// Some options also are used with <see cref=\"clipboard\"/> functions that send keys (<c>Ctrl+V</c> etc).\n\t/// </summary>\n\t/// <seealso cref=\"opt.key\"/>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// opt.key.KeySpeed = 50;\n\t/// ]]></code>\n\t/// Set options for trigger actions.\n\t/// <code><![CDATA[\n\t/// Triggers.Options.BeforeAction = o => { opt.key.KeySpeed = 50; };\n\t/// ]]></code>\n\t/// </example>\n\tpublic sealed class OKey {\n\t\tint _threadId;\n\t\tstatic AsyncLocal<OKey> s_ambient = new();\n\t\t\n\t\tinternal static OKey Ambient_ {\n\t\t\tget {\n\t\t\t\tvar threadId = Environment.CurrentManagedThreadId;\n\t\t\t\tif (s_ambient.Value is { } v) {\n\t\t\t\t\tif (v._threadId != threadId) s_ambient.Value = v = new(v) { _threadId = threadId };\n\t\t\t\t} else {\n\t\t\t\t\ts_ambient.Value = v = new() { _threadId = threadId };\n\t\t\t\t}\n\t\t\t\treturn v;\n\t\t\t}\n\t\t\tset { Debug.Assert(value?._threadId != 0); s_ambient.Value = value; } //used only to restore scope\n\t\t}\n\t\t\n\t\tinternal static OKey Scope_(bool inherit) {\n\t\t\tvar old = s_ambient.Value;\n\t\t\ts_ambient.Value = (old != null && inherit) ? new(old) { _threadId = Environment.CurrentManagedThreadId } : null; //lazy\n\t\t\treturn old;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Initializes this instance with default values or values copied from another instance.\n\t\t/// </summary>\n\t\t/// <param name=\"cloneOptions\">If not <c>null</c>, copies its options into this variable.</param>\n\t\tpublic OKey(OKey cloneOptions = null) {\n\t\t\tCopyOrDefault_(cloneOptions);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Copies options from <i>o</i>, or sets default if <c>o==null</c>. Like ctor does.\n\t\t/// </summary>\n\t\tinternal void CopyOrDefault_(OKey o) { _f = o?._f ?? new(); }\n\t\t\n\t\tstruct _Fields { //makes easier to init, reset or copy fields\n\t\t\tpublic _Fields() { }\n\t\t\tpublic int TextSpeed, KeySpeed = 2, KeySpeedClipboard = 5, SleepFinally = 10, PasteLength = 200, PasteSleep = 100/*, PasteRestoreAfter*/;\n\t\t\tpublic OKeyText TextHow = OKeyText.Characters;\n\t\t\tpublic bool TextShiftEnter, PasteWorkaround, RestoreClipboard = true, NoModOff, NoCapsOff, NoBlockInput;\n\t\t\tpublic Action<OKeyHookData> Hook;\n\t\t}\n\t\t_Fields _f;\n\t\t\n\t\t/// <summary>\n\t\t/// Returns this variable, or <c>OKey</c> cloned from this variable and possibly modified by <c>Hook</c>.\n\t\t/// </summary>\n\t\t/// <param name=\"wFocus\">The focused or active window. Use <c>GetWndFocusedOrActive</c>.</param>\n\t\tinternal OKey GetHookOptionsOrThis_(wnd wFocus) {\n\t\t\tvar call = this.Hook;\n\t\t\tif (call == null || wFocus.Is0) return this;\n\t\t\tvar R = new OKey(this);\n\t\t\tcall(new OKeyHookData(R, wFocus));\n\t\t\treturn R;\n\t\t}\n\t\t\n\t\tint _Set(int value, int max) {\n\t\t\tif ((uint)value > max) throw new ArgumentOutOfRangeException(null, \"Max \" + max);\n\t\t\treturn value;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// How long to wait (milliseconds) between pressing and releasing each character key. Used by <see cref=\"keys.sendt\"/>. Also by <see cref=\"keys.send\"/> and similar functions for <c>\"!text\"</c> arguments.\n\t\t/// Default: 0.\n\t\t/// </summary>\n\t\t/// <value>Valid values: 0 - 1000 (1 second).</value>\n\t\t/// <exception cref=\"ArgumentOutOfRangeException\"></exception>\n\t\t/// <remarks>\n\t\t/// Used only for \"text\" arguments, not for \"keys\" arguments. See <see cref=\"KeySpeed\"/>.\n\t\t/// </remarks>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// opt.key.TextSpeed = 50;\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic int TextSpeed {\n\t\t\tget => _f.TextSpeed;\n\t\t\tset => _f.TextSpeed = _Set(value, 1000);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// How long to wait (milliseconds) between pressing and releasing each key. Used by <see cref=\"keys.send\"/> and similar functions, except for <c>\"!text\"</c> arguments.\n\t\t/// Default: 2.\n\t\t/// </summary>\n\t\t/// <value>Valid values: 0 - 1000 (1 second).</value>\n\t\t/// <exception cref=\"ArgumentOutOfRangeException\"></exception>\n\t\t/// <remarks>\n\t\t/// Used only for \"keys\" arguments, not for \"text\" arguments. See <see cref=\"TextSpeed\"/>.\n\t\t/// </remarks>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// opt.key.KeySpeed = 50;\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic int KeySpeed {\n\t\t\tget => _f.KeySpeed;\n\t\t\tset => _f.KeySpeed = _Set(value, 1000);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// How long to wait (milliseconds) between sending clipboard copy/paste keys. For example, when sending <c>Ctrl+V</c>, waits after <c>Ctrl</c>-down and after <c>V</c>-down.\n\t\t/// Default: 5.\n\t\t/// </summary>\n\t\t/// <value>Valid values: 0 - 1000 (1 second).</value>\n\t\t/// <exception cref=\"ArgumentOutOfRangeException\"></exception>\n\t\t/// <remarks>\n\t\t/// With most apps these delays are not necessary. But with some apps/controls <c>Ctrl+V</c> etc may not work without a delay after <c>Ctrl</c>-down or/and after <c>V</c>-down.\n\t\t/// </remarks>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// opt.key.KeySpeedClipboard = 50;\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic int KeySpeedClipboard {\n\t\t\tget => _f.KeySpeedClipboard;\n\t\t\tset => _f.KeySpeedClipboard = _Set(value, 1000);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// How long to wait (milliseconds) before a \"send keys or text\" function returns.\n\t\t/// Default: 10.\n\t\t/// </summary>\n\t\t/// <value>Valid values: 0 - 10000 (10 seconds).</value>\n\t\t/// <exception cref=\"ArgumentOutOfRangeException\"></exception>\n\t\t/// <remarks>\n\t\t/// Not used by <see cref=\"clipboard\"/> class functions.\n\t\t/// </remarks>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// opt.key.SleepFinally = 50;\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic int SleepFinally {\n\t\t\tget => _f.SleepFinally;\n\t\t\tset => _f.SleepFinally = _Set(value, 10000);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// How to send text to the active window (keys, characters or clipboard).\n\t\t/// Default: <see cref=\"OKeyText.Characters\"/>.\n\t\t/// </summary>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// opt.key.TextHow = OKeyText.Paste;\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic OKeyText TextHow {\n\t\t\tget => _f.TextHow;\n\t\t\tset => _f.TextHow = value;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// When sending text, instead of <c>Enter</c> send <c>Shift+Enter</c>.\n\t\t/// Default: false.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// This option is applied when sending text with <see cref=\"keys.sendt\"/> (like <c>keys.sendt(\"A\\nB\")</c>) or with operator <c>!</c> (like <c>keys.send(\"!A\\nB\")</c>) or with <see cref=\"keys.AddText(string, string)\"/>. Ignored when using operator <c>^</c>, <c>_</c>, <see cref=\"keys.AddText(string, OKeyText)\"/>, <see cref=\"keys.AddChar(char)\"/>.\n\t\t/// </remarks>\n\t\tpublic bool TextShiftEnter {\n\t\t\tget => _f.TextShiftEnter;\n\t\t\tset => _f.TextShiftEnter = value;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// To send text use clipboard (like with <see cref=\"OKeyText.Paste\"/>) if text length is >= this value.\n\t\t/// Default: 200.\n\t\t/// </summary>\n\t\t/// <exception cref=\"ArgumentOutOfRangeException\"></exception>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// opt.key.PasteLength = 50;\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic int PasteLength {\n\t\t\tget => _f.PasteLength;\n\t\t\tset => _f.PasteLength = _Set(value, int.MaxValue);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// When pasting text that ends with space, tab or/and newline characters, remove them and after pasting send them as keys.\n\t\t/// Default: <c>false</c>.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Some apps trim these characters when pasting.\n\t\t/// </remarks>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// opt.key.PasteWorkaround = true;\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic bool PasteWorkaround {\n\t\t\tget => _f.PasteWorkaround;\n\t\t\tset => _f.PasteWorkaround = value;\n\t\t}\n\t\t\n\t\t//rejected: rarely used. Eg can be useful for Python programmers. Let call clipboard.paste() explicitly or set the Paste option eg in hook.\n\t\t///// <summary>\n\t\t///// To send text use <see cref=\"OKeyText.Paste\"/> if text contains characters <c>'\\n'</c> followed by <c>'\\t'</c> (tab) or spaces.\n\t\t///// </summary>\n\t\t///// <remarks>\n\t\t///// Some apps auto-indent. This option is a workaround.\n\t\t///// </remarks>\n\t\t//public bool PasteMultilineIndented { get; set; }\n\t\t\n\t\t/// <summary>\n\t\t/// Whether to restore clipboard data when copying or pasting text.\n\t\t/// Default: <c>true</c>.\n\t\t/// By default restores only text. See also <see cref=\"RestoreClipboardAllFormats\"/>, <see cref=\"RestoreClipboardExceptFormats\"/>.\n\t\t/// </summary>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// opt.key.RestoreClipboard = true;\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// opt.key.RestoreClipboard = false;\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic bool RestoreClipboard {\n\t\t\tget => _f.RestoreClipboard;\n\t\t\tset => _f.RestoreClipboard = value;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// How long to wait (milliseconds) after pasting (before restoring clipboard data, if need).\n\t\t/// Default: 100.\n\t\t/// </summary>\n\t\t/// <value>Valid values: 0 - 10000 (10 seconds).</value>\n\t\t/// <exception cref=\"ArgumentOutOfRangeException\"></exception>\n\t\t/// <remarks>\n\t\t/// This is the minimal wait time. Actually may wait longer; it depends on how the active window responds.\n\t\t/// </remarks>\n\t\tpublic int PasteSleep {\n\t\t\tget => _f.PasteSleep;\n\t\t\tset => _f.PasteSleep = _Set(value, 10000);\n\t\t}\n\t\t\n\t\t//CONSIDER\n\t\t///// <summary>\n\t\t///// After pasting (<see cref=\"clipboard.paste\"/> etc), if <see cref=\"RestoreClipboard\"/> <c>true</c>, restore clipboard data later (asynchronously) after this delay (milliseconds).\n\t\t///// Default: 0.\n\t\t///// </summary>\n\t\t///// <value>Valid values: 0 - 10000 (10 seconds).</value>\n\t\t///// <exception cref=\"ArgumentOutOfRangeException\"></exception>\n\t\t//public int PasteRestoreAfter {\n\t\t//\tget => _f.PasteRestoreAfter;\n\t\t//\tset => _f.PasteRestoreAfter = _Set(value, 10000);\n\t\t//}\n\t\t\n\t\t#region static RestoreClipboard options\n\t\t\n\t\t/// <summary>\n\t\t/// When copying or pasting text, restore clipboard data of all formats that are possible to restore.\n\t\t/// Default: <c>false</c> - restore only text.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Restoring data of all formats set by some apps can be slow or cause problems. More info: <see cref=\"RestoreClipboardExceptFormats\"/>.\n\t\t/// \n\t\t/// This property is static, not thread-static. It should be set (if need) at the start of script and not changed later.\n\t\t/// </remarks>\n\t\t/// <seealso cref=\"RestoreClipboard\"/>\n\t\t/// <seealso cref=\"RestoreClipboardExceptFormats\"/>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// OKey.RestoreClipboardAllFormats = true;\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic static bool RestoreClipboardAllFormats { get; set; }\n\t\t\n\t\t/// <summary>\n\t\t/// When copying or pasting text, and <see cref=\"RestoreClipboardAllFormats\"/> is <c>true</c>, do not restore clipboard data of these formats.\n\t\t/// Default: <c>null</c>.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// To restore clipboard data, the copy/paste functions at first get clipboard data. Getting data of some formats set by some apps can be slow (100 ms or more) or cause problems (the app can change something in its window or even show a dialog).\n\t\t/// It also depends on whether this is the first time the data is being retrieved. The app can render data on demand, when some app is retrieving it from the clipboard first time; then can be slow etc.\n\t\t/// \n\t\t/// You can use function <see cref=\"PrintClipboard\"/> to see format names and get-data times.\n\t\t/// \n\t\t/// There are several kinds of clipboard formats - registered, standard, private and display. Only registered formats have string names. For standard formats use API constant names, like <c>\"CF_WAVE\"</c>. Private, display and metafile formats are never restored.\n\t\t/// These formats are never restored: <c>CF_METAFILEPICT</c>, <c>CF_ENHMETAFILE</c>, <c>CF_PALETTE</c>, <c>CF_OWNERDISPLAY</c>, <c>CF_DSPx</c> formats, <c>CF_GDIOBJx</c> formats, <c>CF_PRIVATEx</c> formats. Some other formats too, but they are automatically synthesized from other formats if need. Also does not restore if data size is 0 or > 10 MB.\n\t\t/// \n\t\t/// This property is static, not thread-static. It should be set (if need) at the start of script and not changed later.\n\t\t/// </remarks>\n\t\t/// <seealso cref=\"RestoreClipboard\"/>\n\t\t/// <seealso cref=\"PrintClipboard\"/>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// OKey.RestoreClipboardExceptFormats = [\"CF_UNICODETEXT\", \"HTML Format\"];\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic static string[] RestoreClipboardExceptFormats { get; set; }\n\t\t\n\t\t/// <summary>\n\t\t/// Writes to the output some info about current clipboard data.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Shows this info for each clipboard format: format name, time spent to get data (microseconds), data size (bytes), and whether this format would be restored (depends on <see cref=\"RestoreClipboardExceptFormats\"/>).\n\t\t/// <note>Copy something to the clipboard each time before calling this function. Don't use <see cref=\"clipboard.copy\"/> and don't call this function in loop. Else it shows small times.</note>\n\t\t/// The time depends on app, etc. More info: <see cref=\"RestoreClipboardExceptFormats\"/>.\n\t\t/// </remarks>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// OKey.PrintClipboard();\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic static void PrintClipboard() => clipboard.PrintClipboard_();\n\t\t\n\t\t#endregion\n\t\t\n\t\t/// <summary>\n\t\t/// When starting to send keys or text, don't release modifier keys.\n\t\t/// Default: <c>false</c>.\n\t\t/// </summary>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// opt.key.NoModOff = true;\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic bool NoModOff {\n\t\t\tget => _f.NoModOff;\n\t\t\tset => _f.NoModOff = value;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// When starting to send keys or text, don't turn off <c>CapsLock</c>.\n\t\t/// Default: <c>false</c>.\n\t\t/// </summary>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// opt.key.NoCapsOff = true;\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic bool NoCapsOff {\n\t\t\tget => _f.NoCapsOff;\n\t\t\tset => _f.NoCapsOff = value;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// While sending or pasting keys or text, don't block user-pressed keys.\n\t\t/// Default: <c>false</c>.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// If <c>false</c> (default), user-pressed keys are sent afterwards. If <c>true</c>, user-pressed keys can be mixed with script-pressed keys, which is particularly dangerous when modifier keys are mixed (and combined) with non-modifier keys.\n\t\t/// </remarks>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// opt.key.NoBlockInput = true;\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic bool NoBlockInput {\n\t\t\tget => _f.NoBlockInput;\n\t\t\tset => _f.NoBlockInput = value;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Callback function that can modify options of \"send keys or text\" functions depending on active window etc.\n\t\t/// Default: <c>null</c>.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// The callback function is called by <see cref=\"keys.send\"/>, <see cref=\"keys.sendt\"/>, <see cref=\"keys.SendNow\"/>, <see cref=\"clipboard.paste\"/> and similar functions. Not called by <see cref=\"clipboard.copy\"/>.\n\t\t/// </remarks>\n\t\t/// <seealso cref=\"OKeyHookData\"/>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// opt.key.Hook = k => {\n\t\t/// \tprint.it(k.w);\n\t\t/// \tvar w = k.w.Window; //if k.w is a control, get its top-level window\n\t\t/// \tvar name = w.Name;\n\t\t/// \tif (name.Like(\"* Slow App\")) {\n\t\t/// \t\tk.optk.KeySpeed = 50;\n\t\t/// \t\tk.optk.TextSpeed = 50;\n\t\t/// \t}\n\t\t/// };\n\t\t/// \n\t\t/// for (int i = 0; i < 10; i++) {\n\t\t/// \t1.s();\n\t\t/// \tkeys.send(\"Home\");\n\t\t/// }\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic Action<OKeyHookData> Hook {\n\t\t\tget => _f.Hook;\n\t\t\tset => _f.Hook = value;\n\t\t}\n\t\t\n\t\t///\n\t\tpublic override string ToString() =>\n\t\t\t$\"{{{string.Join(\", \", GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance).Select(p => $\"{p.Name} = {p.GetValue(this)}\"))}}}\";\n\t}\n\t\n\t/// <summary>\n\t/// Parameter type of the <see cref=\"OKey.Hook\"/> callback function.\n\t/// </summary>\n\tpublic struct OKeyHookData {\n\t\tinternal OKeyHookData(OKey optk, wnd w) { this.optk = optk; this.w = w; }\n\t\t\n\t\t/// <summary>\n\t\t/// Options used by the \"send keys or text\" function. The callback function can modify them, except <c>Hook</c>, <c>NoModOff</c>, <c>NoCapsOff</c>, <c>NoBlockInput</c>.\n\t\t/// </summary>\n\t\tpublic readonly OKey optk;\n\t\t\n\t\t/// <summary>\n\t\t/// The focused control. If there is no focused control - the active window. Use <c>w.Window</c> to get top-level window; if <c>w.Window == w</c>, <c>w</c> is the active window, else the focused control. The callback function is not called if there is no active window.\n\t\t/// </summary>\n\t\tpublic readonly wnd w;\n\t}\n\t\n\t/// <summary>\n\t/// How functions send text.\n\t/// See <see cref=\"OKey.TextHow\"/>.\n\t/// </summary>\n\t/// <remarks>\n\t/// There are three ways to send text to the active app using keys:\n\t/// - Characters (default) - use special key code <ms>VK_PACKET</ms>. Can send most characters.\n\t/// - Keys - use virtual-key codes, with <c>Shift</c> etc where need. Can send only characters that can be simply entered with the keyboard using current keyboard layout.\n\t/// - Paste - use the clipboard and <c>Ctrl+V</c>. Can send any text.\n\t/// \n\t/// Most but not all apps support all three ways.\n\t/// </remarks>\n\tpublic enum OKeyText {\n\t\t/// <summary>\n\t\t/// Send most text characters using special key code <ms>VK_PACKET</ms>.\n\t\t/// This option is default. Few apps don't support it.\n\t\t/// For newlines, tab and space sends keys (<c>Enter</c>, <c>Tab</c>, <c>Space</c>), because <c>VK_PACKET</c> often does not work well.\n\t\t/// If text contains Unicode characters with Unicode code above 0xffff, clipboard-pastes whole text, because many apps don't support Unicode surrogates sent as <c>WM_PACKET</c> pairs.\n\t\t/// </summary>\n\t\tCharacters,\n\t\t//Tested many apps/controls/frameworks. Works almost everywhere.\n\t\t//Does not work with Pidgin (GTK), but works eg with Inkscape (GTK too).\n\t\t//I guess does not work with many games.\n\t\t//In PhraseExpress this is default. Its alternative methods are SendKeys (does not send Unicode chars) and clipboard. It uses clipboard if text is long, default 100. Allows to choose different for specified apps. Does not add any delays between chars; for some apps too fast, eg VirtualBox edit fields when text contains Unicode surrogates.\n\t\t\n\t\t/// <summary>\n\t\t/// Send virtual-key codes, with <c>Shift</c> etc where need.\n\t\t/// All apps support it.\n\t\t/// If a character cannot be simply typed with the keyboard using current keyboard layout, sends it like with the <c>Characters</c> option.\n\t\t/// </summary>\n\t\tKeysOrChar,\n\t\t\n\t\t/// <summary>\n\t\t/// Send virtual-key codes, with <c>Shift</c> etc where need.\n\t\t/// All apps support it.\n\t\t/// If text contains characters that cannot be simply typed with the keyboard using current keyboard layout, clipboard-pastes whole text.\n\t\t/// </summary>\n\t\tKeysOrPaste,\n\t\t\n\t\t/// <summary>\n\t\t/// Paste text using the clipboard and <c>Ctrl+V</c>.\n\t\t/// Few apps don't support it.\n\t\t/// This option is recommended for long text, because other ways then are too slow.\n\t\t/// Other options are unreliable when text length is more than 4000 and the target app is too slow to process sent characters. Then <see cref=\"OKey.TextSpeed\"/> can help.\n\t\t/// Also, other options are unreliable when the target app modifies typed text, for example has such features as auto-complete, auto-indent or auto-correct. However some apps modify even pasted text, for example trim the last newline or space.\n\t\t/// When pasting text, previous clipboard data of some formats is lost. Text is restored by default.\n\t\t/// </summary>\n\t\tPaste,\n\t\t\n\t\t//rejected: WmPaste. Few windows support it.\n\t\t//rejected: WM_CHAR. It isn't sync with keyboard/mouse input. It has sense only if window specified (send to inactive window). Maybe will add a function in the future.\n\t}\n\t\n\t/// <summary>\n\t/// Options for run-time warnings (<see cref=\"print.warning\"/>).\n\t/// </summary>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// opt.warnings.Verbose = false;\n\t/// ]]></code>\n\t/// </example>\n\tpublic sealed class OWarnings {\n\t\tint _threadId;\n\t\tstatic AsyncLocal<OWarnings> s_ambient = new();\n\t\t\n\t\tinternal static OWarnings Ambient_ {\n\t\t\tget {\n\t\t\t\tvar threadId = Environment.CurrentManagedThreadId;\n\t\t\t\tif (s_ambient.Value is { } v) {\n\t\t\t\t\tif (v._threadId != threadId) s_ambient.Value = v = new(v) { _threadId = threadId };\n\t\t\t\t} else {\n\t\t\t\t\ts_ambient.Value = v = new() { _threadId = threadId };\n\t\t\t\t}\n\t\t\t\treturn v;\n\t\t\t}\n\t\t\tset { Debug.Assert(value?._threadId != 0); s_ambient.Value = value; } //used only to restore scope\n\t\t}\n\t\t\n\t\tinternal static OWarnings Scope_(bool inherit) {\n\t\t\tvar old = s_ambient.Value;\n\t\t\ts_ambient.Value = (old != null && inherit) ? new(old) { _threadId = Environment.CurrentManagedThreadId } : null; //lazy\n\t\t\treturn old;\n\t\t}\n\t\t\n\t\tbool? _verbose;\n\t\tList<string> _disabledWarnings;\n\t\t\n\t\t/// <summary>\n\t\t/// Initializes this instance with default values or values copied from another instance.\n\t\t/// </summary>\n\t\t/// <param name=\"other\">If not <c>null</c>, copies its options into this variable.</param>\n\t\tinternal OWarnings(OWarnings other = null) {\n\t\t\tif (other != null) {\n\t\t\t\t_verbose = other._verbose;\n\t\t\t\t_disabledWarnings = other._disabledWarnings == null ? null : new(other._disabledWarnings);\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// If <c>true</c>, some library functions may display more warnings and other info.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// If not explicitly set, the default value depends on the build configuration of the main assembly: <c>true</c> if Debug, <c>false</c> if Release (<c>optimize true</c>).\n\t\t/// </remarks>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// opt.warnings.Verbose = false;\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic bool Verbose {\n\t\t\tget => (_verbose ??= script.isDebug) == true;\n\t\t\tset => _verbose = value;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Disables one or more run-time warnings.\n\t\t/// </summary>\n\t\t/// <param name=\"warningsWild\">One or more warnings as case-insensitive wildcard strings. See <see cref=\"ExtString.Like(string, string, bool)\"/>.</param>\n\t\t/// <remarks>\n\t\t/// Adds the strings to an internal list. When <see cref=\"print.warning\"/> is called, it looks in the list. If finds the warning in the list, does not show the warning.\n\t\t/// It's easy to auto-restore warnings with <c>using</c>, like in the second example. Restoring is optional.\n\t\t/// </remarks>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// opt.warnings.Disable(\"*part of warning 1 text*\", \"*part of warning 2 text*\");\n\t\t/// ]]></code>\n\t\t/// Temporarily disable all warnings.\n\t\t/// <code><![CDATA[\n\t\t/// opt.warnings.Verbose = true;\n\t\t/// print.warning(\"one\");\n\t\t/// using(opt.warnings.Disable(\"*\")) {\n\t\t/// \tprint.warning(\"two\");\n\t\t/// }\n\t\t/// print.warning(\"three\");\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic UsingEndAction Disable(params string[] warningsWild) {\n\t\t\t_disabledWarnings ??= new List<string>();\n\t\t\tint restoreCount = _disabledWarnings.Count;\n\t\t\t_disabledWarnings.AddRange(warningsWild);\n\t\t\treturn new UsingEndAction(() => _disabledWarnings.RemoveRange(restoreCount, _disabledWarnings.Count - restoreCount));\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if the specified warning text matches a wildcard string added with <see cref=\"Disable\"/>.\n\t\t/// </summary>\n\t\t/// <param name=\"text\">Warning text. Case-insensitive.</param>\n\t\tpublic bool IsDisabled(string text) {\n\t\t\tstring s = text ?? \"\";\n\t\t\tif (_disabledWarnings is { } a) foreach (var k in a) if (s.Like(k, true)) return true;\n\t\t\treturn false;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Obsolete. Use <see cref=\"Seconds\"/> instead. Some wait functions may still use some <c>OWait</c> properties for backward compatibility.\n\t/// </summary>\n\t[EditorBrowsable(EditorBrowsableState.Never)]\n\tpublic sealed class OWait {\n\t\tint _threadId;\n\t\tstatic AsyncLocal<OWait> s_ambient = new();\n\t\t\n\t\tinternal static OWait Ambient_ {\n\t\t\tget {\n\t\t\t\tvar threadId = Environment.CurrentManagedThreadId;\n\t\t\t\tif (s_ambient.Value is { } v) {\n\t\t\t\t\tif (v._threadId != threadId) s_ambient.Value = v = new(v) { _threadId = threadId };\n\t\t\t\t} else {\n\t\t\t\t\ts_ambient.Value = v = new() { _threadId = threadId };\n\t\t\t\t}\n\t\t\t\treturn v;\n\t\t\t}\n\t\t\tset { Debug.Assert(value?._threadId != 0); s_ambient.Value = value; } //used only to restore scope\n\t\t}\n\t\t\n\t\tinternal static OWait Scope_(bool inherit) {\n\t\t\tvar old = s_ambient.Value;\n\t\t\ts_ambient.Value = (old != null && inherit) ? new(old) { _threadId = Environment.CurrentManagedThreadId } : null; //lazy\n\t\t\treturn old;\n\t\t}\n\t\t\n\t\tinternal OWait() { _period = 10; }\n\t\t\n\t\tinternal OWait(OWait other) { _doEvents = other._doEvents; _period = other._period; }\n\t\t\n\t\t/// <summary>\n\t\t/// Obsolete. Use <see cref=\"Seconds\"/> instead.\n\t\t/// </summary>\n\t\tpublic OWait(int? period = null, bool? doEvents = null) {\n\t\t\t_doEvents = doEvents ?? opt.wait._doEvents;\n\t\t\t_period = period ?? opt.wait._period;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Obsolete. Use <see cref=\"Seconds\"/> instead.\n\t\t/// </summary>\n\t\tpublic bool DoEvents {\n\t\t\tget => _doEvents;\n\t\t\tset => _doEvents = value;\n\t\t}\n\t\tbool _doEvents;\n\t\t\n\t\t/// <summary>\n\t\t/// Obsolete. Used only by obsolete/hidden wait functions. Use <see cref=\"Seconds\"/> instead.\n\t\t/// </summary>\n\t\tpublic int Period {\n\t\t\tget => _period;\n\t\t\tset => _period = value;\n\t\t}\n\t\tint _period;\n\t}\n}\n"
  },
  {
    "path": "Au/Other/print.cs",
    "content": "using COL = System.Collections;\n\nnamespace Au;\n\n/// <summary>\n/// Writes text to the output window, console, log file or custom writer.\n/// </summary>\n[DebuggerStepThrough]\npublic static partial class print {\n\t/// <summary>\n\t/// Returns <c>true</c> if this process is attached to a console.\n\t/// </summary>\n\tpublic static bool isConsoleProcess => Api.GetConsoleOutputCP() != 0; //fast\n\t\n\t//public static bool isConsoleProcess => Api.GetStdHandle(Api.STD_INPUT_HANDLE) is not (0 or -1); //no, may be true even if not attached to a console, eg if this non-console program started from cmd/bat on Win7\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if is writing to console, <c>false</c> if to the output window etc.\n\t/// </summary>\n\t/// <remarks>\n\t/// Does not write to console in these cases:\n\t/// - <see cref=\"isConsoleProcess\"/> is <c>false</c>.\n\t/// - <see cref=\"ignoreConsole\"/> is <c>true</c>.\n\t/// - <see cref=\"logFile\"/> is not <c>null</c>.\n\t/// - The startup info of this process tells to not show console window and to not redirect the standard output.\n\t/// </remarks>\n\tpublic static bool isWritingToConsole {\n\t\tget {\n\t\t\tif (!isConsoleProcess || ignoreConsole || logFile != null) return false;\n\t\t\tif (!_isVisibleConsole.HasValue) {\n\t\t\t\tApi.GetStartupInfo(out var x);\n\t\t\t\t_isVisibleConsole = x.hStdOutput != default || 0 == (x.dwFlags & 1) || 0 != x.wShowWindow; //redirected stdout, or visible console window\n\t\t\t}\n\t\t\treturn _isVisibleConsole.Value;\n\t\t}\n\t}\n\tstatic bool? _isVisibleConsole;\n\t\n\t/// <summary>\n\t/// If <c>true</c>, in console process will not use the console window. Then everything is like in non-console process.\n\t/// </summary>\n\t/// <seealso cref=\"redirectConsoleOutput\"/>\n\t/// <seealso cref=\"redirectDebugOutput\"/>\n\tpublic static bool ignoreConsole { get; set; }\n\t\n\t/// <summary>\n\t/// Clears the output window or console text (if <see cref=\"isWritingToConsole\"/>) or log file (if <see cref=\"logFile\"/> not <c>null</c>).\n\t/// </summary>\n\tpublic static void clear() {\n\t\tif (logFile != null) {\n\t\t\t_ClearToLogFile();\n\t\t} else if (isWritingToConsole) {\n\t\t\t_ConsoleAction(PrintServerMessageType.Clear);\n\t\t} else if (qm2.use) {\n\t\t\tqm2.clear();\n\t\t} else {\n\t\t\t_ServerAction(PrintServerMessageType.Clear);\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Scrolls the output window or console to the top.\n\t/// </summary>\n\tpublic static void scrollToTop() {\n\t\tif (logFile != null) {\n\t\t} else if (isWritingToConsole) {\n\t\t\t_ConsoleAction(PrintServerMessageType.ScrollToTop);\n\t\t} else if (qm2.use) {\n\t\t} else {\n\t\t\t_ServerAction(PrintServerMessageType.ScrollToTop);\n\t\t}\n\t}\n\t\n\t[MethodImpl(MethodImplOptions.NoInlining)] //avoid loading System.Console.dll\n\tstatic void _ConsoleAction(PrintServerMessageType action) {\n\t\ttry {\n\t\t\tswitch (action) {\n\t\t\tcase PrintServerMessageType.Clear:\n\t\t\t\t//exception if redirected, it is documented.\n\t\t\t\t//if (!Console.IsOutputRedirected) Console.Clear(); //no, Clear does something more than if(IsOutputRedirected)\n\t\t\t\tConsole.Clear();\n\t\t\t\tbreak;\n\t\t\tcase PrintServerMessageType.ScrollToTop:\n\t\t\t\tConsole.SetCursorPosition(0, 0);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tcatch { }\n\t}\n\t\n\t/// <summary>\n\t/// Writes string to the output.\n\t/// </summary>\n\t/// <remarks>\n\t/// Appends newline (<c>\"\\r\\n\"</c>), unless text is like <c>\"&lt;&gt;text&lt;nonl&gt;\"</c>.\n\t/// \n\t/// Can display links, colors, images, etc. More info: [](xref:output_tags).\n\t/// \n\t/// Where the text goes:\n\t/// - If redirected, to wherever it is redirected. See <see cref=\"writer\"/>.\n\t/// - Else if using log file (<see cref=\"logFile\"/> not <c>null</c>), writes to the file.\n\t/// - Else if using console (<see cref=\"isWritingToConsole\"/> returns <c>true</c>), writes to console.\n\t/// - Else if using local <see cref=\"PrintServer\"/> (in this process), writes to it.\n\t/// - Else if exists global <see cref=\"PrintServer\"/> (in any process), writes to it.\n\t/// - Else nowhere.\n\t/// </remarks>\n\tpublic static void it(string value) {\n\t\twriter.WriteLine(value);\n\t}\n\t\n\t/// <summary>\n\t/// Writes value of any type to the output.\n\t/// </summary>\n\t/// <param name=\"value\">Value of any type. If <c>null</c>, writes <c>\"null\"</c>.</param>\n\t/// <remarks>\n\t/// If the type is unsigned integer (<c>uint</c>, <c>ulong</c>, <c>ushort</c>, <c>byte</c>, <c>nuint</c>), writes in hexadecimal format with prefix <c>\"0x\"</c>, unless <see cref=\"noHex\"/> <c>true</c>.\n\t/// \n\t/// This overload is used for all types except: strings, arrays, generic collections. They have own overloads; to use this function need to cast to object.\n\t/// For <see cref=\"Span{T}\"/> and other ref struct types use <c>print.it(x.ToString());</c>.\n\t/// </remarks>\n\tpublic static void it(object value) {\n\t\tit(util.toString(value));\n\t}\n\t\n\t/// <summary>\n\t/// Writes interpolated string to the output.\n\t/// </summary>\n\t/// <param name=\"value\">Interpolated string. Can contain <c>:print</c> format like in the example, to display the value like <see cref=\"it(object)\"/>.</param>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// int[] a = { 1, 2, 3 };\n\t/// print.it($\"a: {a}\"); //a: System.Int32[]\n\t/// print.it($\"a: {a:print}\"); //a: { 1, 2, 3 }\n\t/// ]]></code>\n\t/// </example>\n\tpublic static void it(InterpolatedString value) {\n\t\twriter.WriteLine(value.GetFormattedText());\n\t}\n\t\n\t/// <summary>\n\t/// Writes an array or generic collection to the output.\n\t/// </summary>\n\t/// <param name=\"value\">\n\t/// Array or generic collection of any type.\n\t/// If <c>null</c>, writes <c>\"null\"</c>.\n\t/// The format depends on type:\n\t/// <br/>• <c>char[]</c> - like string.\n\t/// <br/>• <c>byte[]</c> - like <c>xx-xx-xx</c>; in hexadecimal, unless <see cref=\"noHex\"/> <c>true</c>.\n\t/// <br/>• Other - multiple lines.\n\t/// </param>\n\tpublic static void it<T>(IEnumerable<T> value) {\n\t\tif (value is char[] or byte[]) print.it(util.toString(value));\n\t\telse list(\"\\r\\n\", value);\n\t}\n\t\n\t/// <summary>\n\t/// Writes an array or generic collection to the output, as a list of items separated by <i>separator</i>.\n\t/// </summary>\n\t/// <param name=\"value\">Array or generic collection of any type. If <c>null</c>, writes <c>\"null\"</c>.</param>\n\tpublic static void list<T>(string separator, IEnumerable<T> value) {\n\t\tstring s = \"null\";\n\t\tif (value != null)\n\t\t\tusing (new StringBuilder_(out var b)) {\n\t\t\t\tbool once = false;\n\t\t\t\tforeach (var v in value) {\n\t\t\t\t\tif (!once) once = true; else b.Append(separator);\n\t\t\t\t\tutil.toString(b, v, compact: true);\n\t\t\t\t}\n\t\t\t\ts = b.ToString();\n\t\t\t}\n\t\tprint.it(s);\n\t}\n\t\n\t/// <summary>\n\t/// Writes multiple arguments of any type to the output, using separator <c>\", \"</c>.\n\t/// </summary>\n\t/// <remarks>\n\t/// If a value is <c>null</c>, writes <c>\"null\"</c>.\n\t/// If a value is unsigned integer (<c>uint</c>, <c>ulong</c>, <c>ushort</c>, <c>byte</c>, <c>nuint</c>), writes in hexadecimal format with prefix <c>\"0x\"</c>.\n\t/// </remarks>\n\tpublic static void it(object value1, object value2, params object[] more) {\n\t\tit(util.toList(\", \", value1, value2, more));\n\t}\n\t\n\t/// <summary>\n\t/// Writes multiple arguments of any type to the output, using <i>separator</i>.\n\t/// </summary>\n\t/// <inheritdoc cref=\"it(object, object, object[])\"/>\n\tpublic static void list(string separator, object value1, object value2, params object[] more) {\n\t\tit(util.toList(separator, value1, value2, more));\n\t}\n\t\n\t/// <summary>\n\t/// Writes binary data to the output, formatted like in a hex editor.\n\t/// </summary>\n\t/// <param name=\"value\"></param>\n\t/// <param name=\"columns\">The number of bytes in a row.</param>\n\tpublic static void it(RByte value, int columns) {\n\t\tit(util.toString(value, columns));\n\t}\n\t\n\t/// <summary>\n\t/// Gets or sets object that actually writes text when is called <see cref=\"it\"/>.\n\t/// </summary>\n\t/// <remarks>\n\t/// If you want to redirect or modify or just monitor output text, use code like in the example. It is known as \"output redirection\".\n\t/// Redirection is applied to whole process, not just this thread.\n\t/// Redirection affects <see cref=\"it\"/>, <see cref=\"redirectConsoleOutput\"/> and <see cref=\"redirectDebugOutput\"/>. It does not affect <see cref=\"directly\"/> and <see cref=\"clear\"/>.\n\t/// Don't call <see cref=\"it\"/> in method <c>WriteLine</c> of your writer class. It would call itself and create stack overflow. Call <see cref=\"directly\"/>, like in the example.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// print.writer = new OutputWriterWithTime();\n\t/// \n\t/// print.it(\"test\");\n\t/// \n\t/// class OutputWriterWithTime :TextWriter {\n\t/// \tpublic override void WriteLine(string value) { print.directly(DateTime.Now.ToString(\"T\") + \".  \" + value); }\n\t/// \tpublic override Encoding Encoding => Encoding.Unicode;\n\t/// }\n\t/// ]]></code>\n\t/// </example>\n\tpublic static TextWriter writer { get; set; } = new _OutputWriter();\n\t\n\t/// <summary>\n\t/// Our default writer class for the Writer property.\n\t/// </summary>\n\tclass _OutputWriter : LineWriter_ {\n\t\tpublic override Encoding Encoding => Encoding.Unicode;\n\t\t\n\t\tprotected override void WriteLineNow(string s) => directly(s);\n\t}\n\t\n\t/// <summary>\n\t/// Same as <see cref=\"it\"/>, but does not pass the string to <see cref=\"writer\"/>.\n\t/// </summary>\n\t[MethodImpl(MethodImplOptions.NoInlining)] //for stack trace, used in _WriteToServer\n\tpublic static void directly(string value) {\n\t\tvalue ??= \"\";\n\t\t//qm2.write($\"'{value}'\");\n\t\tif (logFile != null) _WriteToLogFile(value);\n\t\telse if (isWritingToConsole) _ConsoleWriteLine(value);\n\t\telse if (qm2.use) qm2.write(value);\n\t\telse _ServerWrite(value);\n\t}\n\t\n\t[MethodImpl(MethodImplOptions.NoInlining)] //avoid loading System.Console.dll\n\tstatic void _ConsoleWriteLine(string value) => Console.WriteLine(value);\n\t\n\t/// <summary>\n\t/// Writes a warning text to the output.\n\t/// By default appends the stack trace.\n\t/// </summary>\n\t/// <param name=\"text\">Warning text.</param>\n\t/// <param name=\"showStackFromThisFrame\">If >= 0, appends the stack trace, skipping this number of frames. Default 0. Does not append if <i>text</i> looks like a stack trace.</param>\n\t/// <param name=\"prefix\">Text before <i>text</i>. Default <c>\"&lt;&gt;Warning: \"</c>.</param>\n\t/// <remarks>\n\t/// Calls <see cref=\"print.it\"/>.\n\t/// Does not show more than 1 warning/second, unless <c>opt.warnings.Verbose == true</c> (see <see cref=\"OWarnings.Verbose\"/>).\n\t/// To disable some warnings, use code <c>opt.warnings.Disable(\"warning text wildcard\");</c> (see <see cref=\"OWarnings.Disable\"/>).\n\t/// </remarks>\n\t/// <seealso cref=\"OWarnings\"/>\n\t[MethodImpl(MethodImplOptions.NoInlining)]\n\tpublic static void warning(string text, int showStackFromThisFrame = 0, string prefix = \"<>Warning: \") {\n\t\tif (opt.warnings.IsDisabled(text)) return;\n\t\t\n\t\tif (!opt.warnings.Verbose) {\n\t\t\tvar t = Api.GetTickCount64();\n\t\t\tif (t - s_warningTime < 1000) return;\n\t\t\ts_warningTime = t;\n\t\t}\n\t\t\n\t\tstring s = text ?? \"\";\n\t\tif (showStackFromThisFrame >= 0 && !(s.Contains(\"\\n   at \") && s.RxIsMatch(@\"Exception: .*\\R   at \"))) { //include stack unless text contains stack\n\t\t\tvar x = new StackTrace(showStackFromThisFrame + 1, true);\n\t\t\tvar st = x.ToString(); var rn = st.Ends('\\n') ? \"\" : \"\\r\\n\";\n\t\t\ts = $\"{prefix}{s} <fold><\\a>\\r\\n{st}{rn}</\\a></fold>\";\n\t\t} else s = prefix + s;\n\t\t\n\t\tit(s);\n\t}\n\tstatic long s_warningTime;\n\t\n\t/// <summary>\n\t/// Writes an exception warning to the output.\n\t/// </summary>\n\t/// <inheritdoc cref=\"warning(string, int, string)\"/>\n\t[MethodImpl(MethodImplOptions.NoInlining)]\n\tpublic static void warning(Exception e, string prefix = \"<>Warning: \") {\n\t\twarning(e.ToString(), -1, prefix);\n\t}\n\t\n\t/// <summary>\n\t/// Let <c>Console.WriteX</c> methods in non-console process write to the same destination as <see cref=\"it\"/>.\n\t/// </summary>\n\t/// <remarks>\n\t/// The default value is <c>true</c> in non-console scripts that use class <see cref=\"Console\"/> and have role <c>miniProgram</c> (default); also <c>exeProgram</c> if started from the script editor. Also in these scripts <c>Console.ReadLine</c> uses <see cref=\"dialog.showInput\"/>.\n\t/// \n\t/// If <c>Console.Write</c> text does not end with <c>'\\n'</c> character, it is buffered and not displayed until called again with text ending with <c>'\\n'</c> character or until called <c>Console.WriteLine</c>.\n\t/// \n\t/// <c>Console.Clear</c> will not clear output; it will throw exception.\n\t/// </remarks>\n\tpublic static bool redirectConsoleOutput {\n\t\tset {\n\t\t\tif (value) {\n\t\t\t\tif (_prevConsoleOut != null || isConsoleProcess) return;\n\t\t\t\t_prevConsoleOut = Console.Out;\n\t\t\t\tConsole.SetOut(writer);\n\t\t\t} else if (_prevConsoleOut != null) {\n\t\t\t\tConsole.SetOut(_prevConsoleOut);\n\t\t\t\t_prevConsoleOut = null;\n\t\t\t}\n\t\t}\n\t\tget => _prevConsoleOut != null;\n\t}\n\tstatic TextWriter _prevConsoleOut;\n\t//note: don't call this before AllocConsole. Then can't restore, and IsOutputRedirected always returns true.\n\t\n\t/// <summary>\n\t/// Let <see cref=\"Debug.Write\"/>, <see cref=\"Trace.Write\"/> and similar methods also write to the same destination as <see cref=\"it\"/>.\n\t/// </summary>\n\t/// <remarks>\n\t/// Does not replace existing <c>Debug.Write</c> etc destinations, just add new destination.\n\t/// \n\t/// If <c>Debug.Write</c> etc argument text does not end with <c>'\\n'</c> character, it is buffered and not displayed until called again with text ending with <c>'\\n'</c> character or until called <c>Debug.WriteLine</c> etc.\n\t/// \n\t/// Tip: To write to the output window even in console process, set <c>print.ignoreConsole=true;</c> before calling this method first time.\n\t/// </remarks>\n\tpublic static bool redirectDebugOutput {\n\t\tset {\n\t\t\tif (value) {\n\t\t\t\tif (_traceListener != null) return;\n\t\t\t\t//Trace.Listeners.Add(IsWritingToConsole ? (new ConsoleTraceListener()) : (new TextWriterTraceListener(Writer)));\n\t\t\t\tTrace.Listeners.Add(_traceListener = new TextWriterTraceListener(writer));\n\t\t\t\t//speed: 5000\n\t\t\t} else if (_traceListener != null) {\n\t\t\t\tTrace.Listeners.Remove(_traceListener);\n\t\t\t\t_traceListener = null;\n\t\t\t}\n\t\t}\n\t\tget => _traceListener != null;\n\t}\n\tstatic TextWriterTraceListener _traceListener;\n\t\n\t/// <summary>\n\t/// Sets log file path.\n\t/// When set (not <c>null</c>), text passed to <see cref=\"it\"/> will be written to the file.\n\t/// If value is <c>null</c> - restores default behavior.\n\t/// </summary>\n\t/// <remarks>\n\t/// The first <see cref=\"it\"/> etc call (in this process) creates or opens the file and deletes old content if the file already exists.\n\t/// \n\t/// Also supports mailslots. Use mailslot name, as documented in <ms>CreateMailslot</ms>. Multiple processes can use the same mailslot.\n\t/// </remarks>\n\t/// <exception cref=\"ArgumentException\">The <c>set</c> function throws this exception if the value is not full path and not <c>null</c>.</exception>\n\tpublic static string logFile {\n\t\tget => _logFile;\n\t\tset {\n\t\t\tlock (_lockObj1) {\n\t\t\t\tif (_hFile != null) {\n\t\t\t\t\t_hFile.Close();\n\t\t\t\t\t_hFile = null;\n\t\t\t\t}\n\t\t\t\tif (value != null) {\n\t\t\t\t\t_logFile = pathname.normalize(value);\n\t\t\t\t} else _logFile = null;\n\t\t\t}\n\t\t}\n\t\t\n\t}\n\tstatic string _logFile;\n\tstatic _LogFile _hFile;\n\tstatic readonly object _lockObj1 = new();\n\t\n\t/// <summary>\n\t/// If <c>true</c>, will add current local time when using log file (see <see cref=\"logFile\"/>).\n\t/// </summary>\n\tpublic static bool logFileTimestamp { get; set; }\n\t\n\tstatic void _WriteToLogFile(string s) {\n\t\tlock (_lockObj1) {\n\t\t\tif (_hFile == null) {\n\t\t\t\tg1:\n\t\t\t\t_hFile = _LogFile.Open();\n\t\t\t\tif (_hFile == null) {\n\t\t\t\t\tvar e = lastError.code;\n\t\t\t\t\tif (e == Api.ERROR_SHARING_VIOLATION) {\n\t\t\t\t\t\tvar u = pathname.makeUnique(_logFile, false);\n\t\t\t\t\t\tif (u != _logFile) { _logFile = u; goto g1; }\n\t\t\t\t\t}\n\t\t\t\t\tvar logf = _logFile;\n\t\t\t\t\t_logFile = null;\n\t\t\t\t\tprint.warning($\"Failed to create or open log file '{logf}'. {lastError.messageFor(e)}\");\n\t\t\t\t\tdirectly(s);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\t_hFile.WriteLine(s);\n\t\t}\n\t}\n\t\n\tstatic void _ClearToLogFile() {\n\t\tlock (_lockObj1) {\n\t\t\tif (_hFile == null) {\n\t\t\t\ttry { filesystem.delete(_logFile); } catch { }\n\t\t\t} else {\n\t\t\t\t_hFile.Clear();\n\t\t\t}\n\t\t}\n\t}\n\t\n\tunsafe class _LogFile {\n\t\t//info: We don't use StreamWriter. It creates more problems than would make easier.\n\t\t//\tEg its finalizer does not write to file. If we try to Close it in our finalizer, it throws 'already disposed'.\n\t\t//\tAlso we don't need such buffering. Better to write to the OS file buffer immediately, it's quite fast.\n\t\t\n\t\tHandle_ _h;\n\t\tstring _name;\n\t\t\n\t\t/// <summary>\n\t\t/// Opens <c>logFile</c> file handle for writing.\n\t\t/// Uses <c>CREATE_ALWAYS</c>, <c>GENERIC_WRITE</c>, <c>FILE_SHARE_READ</c>.\n\t\t/// </summary>\n\t\tpublic static _LogFile Open() {\n\t\t\tvar path = logFile;\n\t\t\tvar h = CreateFile_(path, false);\n\t\t\tif (h.Is0) return null;\n\t\t\treturn new _LogFile() { _h = h, _name = path };\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Writes <c>s + \"\\r\\n\"</c> and optionally timestamp.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// If fails to write to file: Sets <c>logFile</c>=<c>null</c>, which closes file handle. Writes a warning and <i>s</i> to the output window or console.\n\t\t/// </remarks>\n\t\t[SkipLocalsInit]\n\t\tpublic bool WriteLine(string s) {\n\t\t\tbool ok;\n\t\t\tint n = Encoding.UTF8.GetByteCount(s ??= \"\") + 1;\n\t\t\tusing FastBuffer<byte> b = new(n + 35);\n\t\t\tbyte* p = b.p;\n\t\t\tif (logFileTimestamp) {\n\t\t\t\tApi.GetLocalTime(out var t);\n\t\t\t\tApi.wsprintfA(p, \"%i-%02i-%02i %02i:%02i:%02i.%03i   \", __arglist(t.wYear, t.wMonth, t.wDay, t.wHour, t.wMinute, t.wSecond, t.wMilliseconds));\n\t\t\t\tint nn = Ptr_.Length(p);\n\t\t\t\tEncoding.UTF8.GetBytes(s, new Span<byte>(p + nn, n));\n\t\t\t\tn += nn;\n\t\t\t\tif (s.Starts(\"<>\")) {\n\t\t\t\t\tApi.memmove(p + 2, p, nn);\n\t\t\t\t\tp[0] = (byte)'<'; p[1] = (byte)'>';\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tEncoding.UTF8.GetBytes(s, new Span<byte>(p, n));\n\t\t\t}\n\t\t\tp[n - 1] = 13; p[n++] = 10;\n\t\t\t\n\t\t\tok = Api.WriteFile(_h, p, n, out _);\n\t\t\tif (!ok) {\n\t\t\t\tstring emsg = lastError.message;\n\t\t\t\tlogFile = null;\n\t\t\t\tprint.warning($\"Failed to write to log file '{_name}'. {emsg}\");\n\t\t\t\tdirectly(s);\n\t\t\t\t//Debug.Assert(false);\n\t\t\t}\n\t\t\treturn ok;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Sets file size = 0.\n\t\t/// </summary>\n\t\tpublic bool Clear() {\n\t\t\tbool ok = Api.SetFilePointerEx(_h, 0, null, Api.FILE_BEGIN) && Api.SetEndOfFile(_h);\n\t\t\tDebug.Assert(ok);\n\t\t\treturn ok;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Closes file handle.\n\t\t/// </summary>\n\t\tpublic void Close() => _h.Dispose();\n\t}\n\t\n\t/// <summary>\n\t/// Calls <c>Api.CreateFile</c> to open file or mailslot.\n\t/// </summary>\n\t/// <param name=\"name\">File path or mailslot name.</param>\n\t/// <param name=\"openExisting\">Use <c>OPEN_EXISTING</c>. If <c>false</c>, uses <c>CREATE_ALWAYS</c>.</param>\n\tinternal static Handle_ CreateFile_(string name, bool openExisting) {\n\t\treturn Api.CreateFile(name, Api.GENERIC_WRITE, Api.FILE_SHARE_READ, openExisting ? Api.OPEN_EXISTING : Api.CREATE_ALWAYS);\n\t\t\n\t\t//tested: CREATE_ALWAYS works with mailslot too. Does not erase messages. Undocumented what to use.\n\t}\n\t\n\t///\n#if !DEBUG\n\t[EditorBrowsable(EditorBrowsableState.Never)]\n#endif\n\tpublic static class qm2 {\n\t\t/// <summary>\n\t\t/// Sets to use QM2 as the output server.\n\t\t/// </summary>\n\t\tpublic static bool use { get; set; }\n\t\t\n\t\t/// <summary>\n\t\t/// Clears QM2 output panel.\n\t\t/// </summary>\n\t\tpublic static void clear() => _WriteToQM2(null);\n\t\t\n\t\t/// <summary>\n\t\t/// Writes line to QM2.\n\t\t/// </summary>\n\t\tpublic static void write(object o) => _WriteToQM2(o?.ToString() ?? \"\");\n\t\t\n\t\t/// <summary>\n\t\t/// Writes multiple arguments of any type to the output, using separator <c>\", \"</c>.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// If a value is <c>null</c>, writes <c>\"null\"</c>.\n\t\t/// If a value is unsigned integer (<c>uint</c>, <c>ulong</c>, <c>ushort</c>, <c>byte</c>, <c>nuint</c>), writes in hexadecimal format with prefix <c>\"0x\"</c>.\n\t\t/// </remarks>\n\t\tpublic static void write(object value1, object value2, params object[] more) {\n\t\t\twrite(util.toList(\", \", value1, value2, more));\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// The same as <see cref=\"write\"/>, but with <c>[Conditional(\"DEBUG\")]</c>.\n\t\t/// </summary>\n\t\t[Conditional(\"DEBUG\")]\n\t\tpublic static void writeD(object value1, object value2, params object[] more) {\n\t\t\twrite(util.toList(\", \", value1, value2, more));\n\t\t}\n\t\t\n\t\t/// <param name=\"s\">If <c>null</c>, clears output.</param>\n\t\tstatic void _WriteToQM2(string s) {\n\t\t\tif (!_hwndQM2.IsAlive) {\n\t\t\t\t_hwndQM2 = Api.FindWindowEx(cn: \"QM_Editor\");\n\t\t\t\tif (_hwndQM2.Is0) return;\n\t\t\t}\n\t\t\t_hwndQM2.Send(Api.WM_SETTEXT, -1, s);\n\t\t}\n\t\tstatic wnd _hwndQM2;\n\t}\n\t\n\t/// <summary>\n\t/// Write unsigned numeric types in decimal format, not hexadecimal.\n\t/// </summary>\n\tpublic static bool noHex { get; set; }\n\t\n\t/// <summary>\n\t/// Some functions used by the <see cref=\"print\"/> class.\n\t/// </summary>\n\tpublic static class util {\n\t\t/// <summary>\n\t\t/// Converts value of any type to <c>string</c>. Formats it like <see cref=\"it(object)\"/>.\n\t\t/// </summary>\n\t\t/// <param name=\"value\">Value of any type. If <c>null</c>, returns <c>\"null\"</c>.</param>\n\t\t/// <param name=\"compact\">If <i>value</i> is <see cref=\"COL.IEnumerable\"/> or <see cref=\"COL.DictionaryEntry\"/>, format it like <c>\"{ item1, item2 }\"</c>.</param>\n\t\tpublic static string toString(object value, bool compact = false) {\n\t\t\tswitch (value) {\n\t\t\tcase null: return \"null\";\n\t\t\tcase string t: return t;\n\t\t\tcase ulong or uint or ushort or byte or nuint when !noHex:\n\t\t\tcase COL.IEnumerable when !value.GetType().IsCOMObject: //info: eg Excel.Range and many other Excel interfaces are IEnumerable, and this process crashes, sometimes Excel too\n\t\t\tcase COL.DictionaryEntry:\n\t\t\t\tusing (new StringBuilder_(out var b)) {\n\t\t\t\t\ttoString(b, value, compact);\n\t\t\t\t\treturn b.ToString();\n\t\t\t\t}\n\t\t\tdefault: return value.ToString();\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Appends value of any type to <c>StringBuilder</c>. Formats it like <see cref=\"it(object)\"/>.\n\t\t/// </summary>\n\t\t/// <inheritdoc cref=\"toString(object, bool)\"/>\n\t\tpublic static void toString(StringBuilder b, object value, bool compact) {\n\t\t\tswitch (value) {\n\t\t\tcase null: b.Append(\"null\"); break;\n\t\t\tcase string s: b.Append(s); break;\n\t\t\tcase ulong u: _Unsigned(b, u); break;\n\t\t\tcase uint u: _Unsigned(b, u); break;\n\t\t\tcase ushort u: _Unsigned(b, u); break;\n\t\t\tcase byte u: _Unsigned(b, u); break;\n\t\t\tcase nuint u: _Unsigned(b, u); break;\n\t\t\tcase char[] a: b.Append(a); break;\n\t\t\tcase byte[] a:\n\t\t\t\tif (noHex) b.AppendJoin('-', a);\n\t\t\t\telse b.Append(BitConverter.ToString(a));\n\t\t\t\tbreak;\n\t\t\tcase COL.IEnumerable e when !value.GetType().IsCOMObject:\n\t\t\t\tif (compact) b.Append(\"{ \");\n\t\t\t\tstring sep = null;\n\t\t\t\tforeach (var v in e) {\n\t\t\t\t\tif (sep == null) sep = compact ? \", \" : \"\\r\\n\"; else b.Append(sep);\n\t\t\t\t\ttoString(b, v, compact);\n\t\t\t\t}\n\t\t\t\tif (compact) b.Append(\" }\");\n\t\t\t\tbreak;\n\t\t\tcase COL.DictionaryEntry de:\n\t\t\t\tb.AppendFormat(\"[{0}, {1}]\", de.Key, de.Value);\n\t\t\t\tbreak;\n\t\t\tdefault: b.Append(value); break;\n\t\t\t}\n\t\t\t\n\t\t\tstatic void _Unsigned(StringBuilder b, ulong u) {\n\t\t\t\tif (noHex) b.Append(u);\n\t\t\t\telse b.Append(\"0x\").Append(u.ToString(\"X\"));\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Converts multiple values of any type to <c>string</c> like <see cref=\"list(string, object, object, object[])\"/>.\n\t\t/// </summary>\n\t\tpublic static string toList(string sep, object value1, object value2, params object[] more) {\n\t\t\tif (more == null) more = s_oaNull; //workaround for: if the third argument is null, we receive null and not object[] { null }\n\t\t\telse if (more.GetType() != typeof(object[])) more = new object[] { more }; //workaround for: if the third argument is an array, prints its elements without { }. If empty array, prints nothing (even no comma). With this workaround - only if object[], which is rare; and it's good, because may be used in a wrapper function that passes its 'params object[]' parameter here.\n\t\t\t\n\t\t\tusing (new StringBuilder_(out var b)) {\n\t\t\t\tfor (int i = 0, n = 2 + more.Length; i < n; i++) {\n\t\t\t\t\tif (i > 0) b.Append(sep);\n\t\t\t\t\tutil.toString(b, i == 0 ? value1 : (i == 1 ? value2 : more[i - 2]), compact: true);\n\t\t\t\t}\n\t\t\t\treturn b.ToString();\n\t\t\t\t\n\t\t\t\t//rejected: escape strings (eg if contains characters \"\\r\\n,\\0\"):\n\t\t\t\t//\tit can damage formatting tags etc;\n\t\t\t\t//\tthe string may be already escaped, eg wnd.ToString or elm.ToString;\n\t\t\t\t//\twe don't know whether the caller wants it;\n\t\t\t\t//\tlet the caller escape it if wants, it's easy.\n\t\t\t}\n\t\t}\n\t\tstatic readonly object[] s_oaNull = { null };\n\t\t\n\t\t/// <summary>\n\t\t/// Converts binary data to a hexadecimal + characters string, similar to the format used in hex editors.\n\t\t/// </summary>\n\t\t/// <param name=\"data\"></param>\n\t\t/// <param name=\"columns\">The number of bytes in a row.</param>\n\t\tpublic static string toString(ReadOnlySpan<byte> data, int columns) {\n\t\t\t//rejected: , char escapeChar = '.' /// <param name=\"escapeChar\">Character for bytes other than the printable ASCII characters (32-126).</param>\n\t\t\t\n\t\t\tint len = data.Length;\n\t\t\tint rows = (len + columns - 1) / columns;\n\t\t\tvar b = new StringBuilder(rows * (columns * 3 + 4 + columns + 2));\n\t\t\t\n\t\t\tfor (int i = 0; i < len; i += columns) {\n\t\t\t\t//hex\n\t\t\t\tfor (int j = 0; j < columns && i + j < len; j++) {\n\t\t\t\t\tbyte k = data[i + j];\n\t\t\t\t\tb.Append(_ToHexChar(k >> 4)).Append(_ToHexChar(k & 0x0F)).Append(' ');\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t//padding if not enough columns\n\t\t\t\tfor (int j = len - i; j < columns; j++) b.Append(\"   \");\n\t\t\t\t\n\t\t\t\tb.Append(\"    \");\n\t\t\t\t\n\t\t\t\t//text\n\t\t\t\tfor (int j = 0; j < columns && i + j < len; j++) {\n\t\t\t\t\tbyte k = data[i + j];\n\t\t\t\t\tb.Append(k >= 32 && k <= 126 ? (char)k : '.');\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tb.AppendLine();\n\t\t\t}\n\t\t\t\n\t\t\treturn b.ToString();\n\t\t\t\n\t\t\tstatic char _ToHexChar(int h) => (char)(h < 10 ? h + '0' : h - 10 + 'A');\n\t\t}\n\t}\n\t\n#pragma warning disable 1591 //no XML doc\n\t/// <summary>\n\t/// Interpolated string handler that adds <c>:print</c> format. See <see cref=\"it(InterpolatedString)\"/>.\n\t/// </summary>\n\t[InterpolatedStringHandler, EditorBrowsable(EditorBrowsableState.Never)]\n\tpublic ref struct InterpolatedString {\n\t\tDefaultInterpolatedStringHandler _f;\n\t\t\n\t\tpublic InterpolatedString(int literalLength, int formattedCount) {\n\t\t\t_f = new(literalLength, formattedCount);\n\t\t}\n\t\t\n\t\tpublic InterpolatedString(int literalLength, int formattedCount, IFormatProvider provider) {\n\t\t\t_f = new(literalLength, formattedCount, provider);\n\t\t}\n\t\t\n\t\tpublic InterpolatedString(int literalLength, int formattedCount, IFormatProvider provider, Span<char> initialBuffer) {\n\t\t\t_f = new(literalLength, formattedCount, provider, initialBuffer);\n\t\t}\n\t\t\n\t\tpublic void AppendLiteral(string value)\n\t\t\t => _f.AppendLiteral(value);\n\t\t\n\t\tpublic void AppendFormatted<T>(T value)\n\t\t\t => _f.AppendFormatted(value);\n\t\t\n\t\tpublic void AppendFormatted<T>(T value, int alignment)\n\t\t\t => _f.AppendFormatted(value, alignment);\n\t\t\n\t\tpublic void AppendFormatted<T>(T value, string format) {\n\t\t\tif (format == \"print\") _f.AppendLiteral(util.toString(value, compact: true));\n\t\t\telse _f.AppendFormatted(value, format);\n\t\t}\n\t\t\n\t\tpublic void AppendFormatted<T>(T value, int alignment, string format) {\n\t\t\tif (format == \"print\") _f.AppendFormatted(util.toString(value, compact: true), alignment);\n\t\t\telse _f.AppendFormatted(value, alignment, format);\n\t\t}\n\t\t\n\t\tpublic void AppendFormatted(RStr value)\n\t\t\t=> _f.AppendFormatted(value);\n\t\t\n\t\tpublic void AppendFormatted(RStr value, int alignment = 0, string format = null)\n\t\t\t=> _f.AppendFormatted(value, alignment, format);\n\t\t\n\t\tpublic void AppendFormatted(string value)\n\t\t\t=> _f.AppendFormatted(value);\n\t\t\n\t\tpublic void AppendFormatted(string value, int alignment = 0, string format = null)\n\t\t\t=> _f.AppendFormatted(value, alignment, format);\n\t\t\n\t\tpublic void AppendFormatted(object value, int alignment = 0, string format = null)\n\t\t\t=> _f.AppendFormatted(value, alignment, format);\n\t\t\n\t\tpublic string GetFormattedText() => _f.ToStringAndClear();\n\t}\n}\n"
  },
  {
    "path": "Au/Other/screen.cs",
    "content": "namespace Au {\n\t/// <summary>\n\t/// Represents a screen device. Gets its rectangle etc.\n\t/// </summary>\n\t/// <remarks>\n\t/// A computer can have one or more screens (aka display devices, monitors). One of them is the <i>primary</i> screen; its top-left coordinate is 0 0.\n\t/// To show or find a window or some object in a particular screen, need to identify the screen somehow. At Windows API level each screen has a unique integer identifier, known as screen handle or <ms>HMONITOR</ms>. But it is a random variable value and therefore cannot be specified directly in script etc. Instead can be used screen index or some object on that screen (window, point, rectangle).\n\t/// \n\t/// A <c>screen</c> variable can contain either a screen handle or a callback function that returns a screen handle. If empty, most functions interpret it as the primary screen.\n\t/// \n\t/// To create <c>screen</c> variables use static functions (like <c>screen.index(1)</c> or <c>screen.primary</c>) or constructors (like <c>new screen(()=>screen.index(1))</c>) or <see cref=\"at\"/>. Then call non-static functions to get screen properties.\n\t/// \n\t/// A screen handle cannot be reliably used for a long time. Screen handles may change when changing the configuration of multiple screens. Consider a \"lazy\" variable, ie with callback function <see cref=\"LazyFunc\"/>. Then, whenever a function needs a screen handle, it calls the callback function which returns a <c>screen</c> with fresh handle.\n\t/// </remarks>\n\tpublic struct screen : IEquatable<screen> {\n\t\treadonly IntPtr _h;\n\t\treadonly Func<screen> _func;\n\t\t\n\t\t/// <summary>\n\t\t/// Creates variable with screen handle, aka <ms>HMONITOR</ms>.\n\t\t/// </summary>\n\t\tpublic screen(IntPtr handle) { _h = handle; _func = null; }\n\t\t\n\t\t/// <summary>\n\t\t/// Creates \"lazy\" variable that calls your function to get screen when need.\n\t\t/// </summary>\n\t\tpublic screen(Func<screen> f) { _h = default; _func = f; }\n\t\t\n\t\t/// <summary>\n\t\t/// Gets the screen handle, aka <ms>HMONITOR</ms>. Returns <c>default(IntPtr)</c> if it wasn't set; see <see cref=\"Now\"/>.\n\t\t/// </summary>\n\t\tpublic IntPtr Handle => _h;\n\t\t\n\t\t/// <summary>\n\t\t/// Gets the callback function that returns <see cref=\"screen\"/> when need. Returns <c>null</c> if it wasn't set.\n\t\t/// </summary>\n\t\tpublic Func<screen> LazyFunc => _func;\n\t\t\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if this variable has no screen handle and no callback function.\n\t\t/// </summary>\n\t\tpublic bool IsEmpty => _h == default && _func == null;\n\t\t\n\t\tIntPtr _Handle() {\n\t\t\tif (_h != default) return _h;\n\t\t\tif (_func != null) return _func()._Handle();\n\t\t\treturn primary._h;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Returns a copy of this variable with <see cref=\"Handle\"/>.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// If this variable has <see cref=\"Handle\"/>, returns its clone. Else if has <see cref=\"LazyFunc\"/>, calls it. Else gets the primary screen.\n\t\t/// </remarks>\n\t\tpublic screen Now => new(_Handle());\n\t\t\n\t\t/// <summary>\n\t\t/// Gets the primary screen.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// The returned variable has <see cref=\"Handle\"/>. To create lazy variable (with <see cref=\"LazyFunc\"/>), use <c>screen.index(0, lazy: true)</c>.\n\t\t/// </remarks>\n\t\tpublic static screen primary => new(Api.MonitorFromWindow(default, SODefault.Primary)); //fast\n\t\t\n\t\t/// <summary>\n\t\t/// Returns a lazy <see cref=\"screen\"/> variable that later will get the screen from the mouse cursor position at that time.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// If need non-lazy: <c>screen.of(mouse.xy)</c> or <c>screen.ofMouse.Now</c>.\n\t\t/// </remarks>\n\t\tpublic static screen ofMouse => new(s_ofMouse);\n\t\t\n\t\t/// <summary>\n\t\t/// Returns a lazy <see cref=\"screen\"/> variable that later will get the screen of the active window at that time.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// If need non-lazy: <c>screen.of(wnd.active)</c> or <c>screen.ofActiveWindow.Now</c>.\n\t\t/// </remarks>\n\t\tpublic static screen ofActiveWindow => new(s_ofActiveWindow);\n\t\t\n\t\tstatic readonly Func<screen> s_ofMouse = () => of(mouse.xy);\n\t\tstatic readonly Func<screen> s_ofActiveWindow = () => of(wnd.active);\n\t\t\n\t\tinternal bool IsOfMouse_ => ReferenceEquals(_func, s_ofMouse);\n\t\tinternal bool IsOfActiveWindow_ => ReferenceEquals(_func, s_ofActiveWindow);\n\t\t\n\t\t/// <summary>\n\t\t/// Gets screen containing the biggest part of the specified window or nearest to it.\n\t\t/// </summary>\n\t\t/// <param name=\"w\">Window or control. If <c>default(wnd)</c> or invalid, gets the primary screen.</param>\n\t\t/// <param name=\"defaultScreen\"></param>\n\t\t/// <param name=\"lazy\">\n\t\t/// Create variable with <see cref=\"LazyFunc\"/> that later will get screen handle.\n\t\t/// Other ways to create lazy:\n\t\t/// <br/>• use <see cref=\"wndFinder\"/>. Example: <c>screen.of(new wndFinder(\"* Notepad\"))</c>.\n\t\t/// <br/>• use constructor. Example: <c>new screen(() => screen.of(wnd.findFast(cn: \"Notepad\")))</c>.\n\t\t/// </param>\n\t\tpublic static screen of(wnd w, SODefault defaultScreen = SODefault.Nearest, bool lazy = false)\n\t\t\t=> lazy\n\t\t\t? new screen(() => of(w, defaultScreen))\n\t\t\t: new screen(Api.MonitorFromWindow(w, defaultScreen));\n\t\t\n\t\t/// <summary>\n\t\t/// Gets screen containing the biggest part of the specified window or nearest to it.\n\t\t/// </summary>\n\t\t/// <param name=\"f\">Window finder. If window not found, gets the primary screen.</param>\n\t\t/// <param name=\"defaultScreen\"></param>\n\t\t/// <param name=\"lazy\">Create variable with <see cref=\"LazyFunc\"/> that later will find window and get screen handle. Default <c>true</c>.</param>\n\t\tpublic static screen of(wndFinder f, SODefault defaultScreen = SODefault.Nearest, bool lazy = true)\n\t\t\t=> lazy\n\t\t\t? new screen(() => of(f, defaultScreen, false))\n\t\t\t: of(f.Find(), defaultScreen);\n\t\t\n\t\t/// <summary>\n\t\t/// Gets screen containing the biggest part of the specified winforms window or control or nearest to it.\n\t\t/// </summary>\n\t\t/// <param name=\"c\">Window or control. If handle not created, gets the primary screen. Cannot be <c>null</c>.</param>\n\t\t/// <param name=\"defaultScreen\"></param>\n\t\t/// <param name=\"lazy\">Create variable with <see cref=\"LazyFunc\"/> that later will get screen handle.</param>\n\t\tpublic static screen of(System.Windows.Forms.Control c, SODefault defaultScreen = SODefault.Nearest, bool lazy = false)\n\t\t\t=> lazy\n\t\t\t? new screen(() => of(c, defaultScreen))\n\t\t\t: of(c.Hwnd(), defaultScreen);\n\t\t\n\t\t/// <summary>\n\t\t/// Gets screen containing the biggest part of the specified WPF window or nearest to it.\n\t\t/// </summary>\n\t\t/// <param name=\"w\">WPF window. If handle not created, gets the primary screen. Cannot be <c>null</c>.</param>\n\t\t/// <param name=\"defaultScreen\"></param>\n\t\t/// <param name=\"lazy\">Create variable with <see cref=\"LazyFunc\"/> that later will get screen handle.</param>\n\t\tpublic static screen of(System.Windows.Window w, SODefault defaultScreen = SODefault.Nearest, bool lazy = false)\n\t\t\t=> lazy\n\t\t\t? new screen(() => of(w, defaultScreen))\n\t\t\t: of(w.Hwnd(), defaultScreen);\n\t\t\n\t\t/// <summary>\n\t\t/// Gets screen containing the biggest part of the specified WPF element (of its rectangle) or nearest to it.\n\t\t/// </summary>\n\t\t/// <param name=\"elem\">WPF element. If not loaded, gets the primary screen. Cannot be <c>null</c>.</param>\n\t\t/// <param name=\"defaultScreen\"></param>\n\t\t/// <param name=\"lazy\">Create variable with <see cref=\"LazyFunc\"/> that later will get screen handle.</param>\n\t\tpublic static screen of(System.Windows.FrameworkElement elem, SODefault defaultScreen = SODefault.Nearest, bool lazy = false)\n\t\t\t=> lazy\n\t\t\t? new screen(() => of(elem, defaultScreen))\n\t\t\t: of(elem.RectInScreen(), defaultScreen);\n\t\t\n\t\t/// <summary>\n\t\t/// Gets screen containing the specified point or nearest to it.\n\t\t/// </summary>\n\t\t/// <param name=\"p\"></param>\n\t\t/// <param name=\"defaultScreen\"></param>\n\t\t/// <param name=\"lazy\">Create variable with <see cref=\"LazyFunc\"/> that later will get screen handle.</param>\n\t\tpublic static screen of(POINT p, SODefault defaultScreen = SODefault.Nearest, bool lazy = false)\n\t\t\t=> lazy\n\t\t\t? new screen(() => of(p, defaultScreen))\n\t\t\t: new screen(Api.MonitorFromPoint(p, defaultScreen));\n\t\t\n\t\t/// <summary>\n\t\t/// Gets screen containing the specified point or nearest to it.\n\t\t/// </summary>\n\t\t/// <param name=\"x\"></param>\n\t\t/// <param name=\"y\"></param>\n\t\t/// <param name=\"defaultScreen\"></param>\n\t\t/// <param name=\"lazy\">Create variable with <see cref=\"LazyFunc\"/> that later will get screen handle.</param>\n\t\tpublic static screen of(int x, int y, SODefault defaultScreen = SODefault.Nearest, bool lazy = false)\n\t\t\t=> lazy\n\t\t\t? new screen(() => of(x, y, defaultScreen))\n\t\t\t: of((x, y), defaultScreen);\n\t\t\n\t\t/// <summary>\n\t\t/// Gets screen containing the biggest part of the specified rectangle or nearest to it.\n\t\t/// </summary>\n\t\t/// <param name=\"r\"></param>\n\t\t/// <param name=\"defaultScreen\"></param>\n\t\t/// <param name=\"lazy\">Create variable with <see cref=\"LazyFunc\"/> that later will get screen handle.</param>\n\t\tpublic static screen of(RECT r, SODefault defaultScreen = SODefault.Nearest, bool lazy = false)\n\t\t\t=> lazy\n\t\t\t? new screen(() => of(r, defaultScreen))\n\t\t\t: new screen(Api.MonitorFromRect(r, defaultScreen));\n\t\t\n\t\t/// <summary>\n\t\t/// Gets screens at various positions relative to the primary screen.\n\t\t/// </summary>\n\t\tpublic static class at {\n\t\t\t/// <summary>Gets a screen nearest to the top edge of the primary screen.</summary>\n\t\t\t/// <param name=\"lazy\">Create variable with <see cref=\"LazyFunc\"/> that later will get screen handle.</param>\n\t\t\tpublic static screen top(bool lazy = false) => _S(0, -700, lazy);\n\t\t\t\n\t\t\t/// <summary>Gets a screen nearest to the bottom edge of the primary screen.</summary>\n\t\t\t/// <param name=\"lazy\">Create variable with <see cref=\"LazyFunc\"/> that later will get screen handle.</param>\n\t\t\tpublic static screen bottom(bool lazy = false) => _S(0, 700, lazy);\n\t\t\t\n\t\t\t/// <summary>Gets a screen nearest to the left edge of the primary screen.</summary>\n\t\t\t/// <param name=\"lazy\">Create variable with <see cref=\"LazyFunc\"/> that later will get screen handle.</param>\n\t\t\tpublic static screen left(bool lazy = false) => _S(-1000, 0, lazy);\n\t\t\t\n\t\t\t/// <summary>Gets a screen nearest to the right edge of the primary screen.</summary>\n\t\t\t/// <param name=\"lazy\">Create variable with <see cref=\"LazyFunc\"/> that later will get screen handle.</param>\n\t\t\tpublic static screen right(bool lazy = false) => _S(1000, 0, lazy);\n\t\t\t\n\t\t\t/// <summary>Gets a screen nearest to the top-left corner of the primary screen.</summary>\n\t\t\t/// <param name=\"lazy\">Create variable with <see cref=\"LazyFunc\"/> that later will get screen handle.</param>\n\t\t\tpublic static screen topLeft(bool lazy = false) => _S(-1000, -700, lazy);\n\t\t\t\n\t\t\t/// <summary>Gets a screen nearest to the top-right corner of the primary screen.</summary>\n\t\t\t/// <param name=\"lazy\">Create variable with <see cref=\"LazyFunc\"/> that later will get screen handle.</param>\n\t\t\tpublic static screen topRight(bool lazy = false) => _S(1000, -700, lazy);\n\t\t\t\n\t\t\t/// <summary>Gets a screen nearest to the bottom-left corner of the primary screen.</summary>\n\t\t\t/// <param name=\"lazy\">Create variable with <see cref=\"LazyFunc\"/> that later will get screen handle.</param>\n\t\t\tpublic static screen bottomLeft(bool lazy = false) => _S(-1000, 700, lazy);\n\t\t\t\n\t\t\t/// <summary>Gets a screen nearest to the bottom-right corner of the primary screen.</summary>\n\t\t\t/// <param name=\"lazy\">Create variable with <see cref=\"LazyFunc\"/> that later will get screen handle.</param>\n\t\t\tpublic static screen bottomRight(bool lazy = false) => _S(1000, 700, lazy);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets screen at (or nearest to) the specified offset from the primary screen (PS).\n\t\t/// </summary>\n\t\t/// <param name=\"dx\">Horizontal offset. Negative is to the left from the left edge of PS. Positive is to the right from the right edge of PS. Zero is the horizontal center of PS.</param>\n\t\t/// <param name=\"dy\">Vertical offset. Negative is up from the top edge of PS. Positive is down from the bottom edge of PS. Zero is the vertical center of PS.</param>\n\t\tstatic screen _S(int dx, int dy, bool lazy) {\n\t\t\tvar r = primary.Rect;\n\t\t\tif (dx > 0) dx += r.right; else if (dx == 0) dx = r.CenterX;\n\t\t\tif (dy > 0) dy += r.bottom; else if (dy == 0) dy = r.CenterY;\n\t\t\treturn of(dx, dy, lazy: lazy);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets all screens.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// The order of array elements may be different than in Windows Settings. The primary screen is always at index 0.\n\t\t/// The array is not cached. Each time calls API <ms>EnumDisplayMonitors</ms>.\n\t\t/// </remarks>\n\t\tpublic static screen[] all => _All();\n\t\t\n\t\t[SkipLocalsInit]\n\t\tinternal static unsafe screen[] _All(bool failed = false) {\n\t\t\tvar a = stackalloc nint[256];\n\t\t\ta[0] = 1; //array length\n\t\t\ta[1] = Api.MonitorFromWindow(default, SODefault.Primary); //fast\n\t\t\t\n\t\t\t[UnmanagedCallersOnly]\n\t\t\tstatic int _Enum(nint hmon, nint hdc, RECT* r, nint* a) {\n\t\t\t\tif (hmon != a[1]) a[++a[0]] = hmon;\n\t\t\t\treturn 1;\n\t\t\t};\n\t\t\t\n\t\t\tif (!Api.EnumDisplayMonitors(default, default, &_Enum, a)) {\n\t\t\t\tif (failed) throw new AuException(\"EnumDisplayMonitors failed\");\n\t\t\t\tDebug_.Print(\"EnumDisplayMonitors\");\n\t\t\t\t//in certain conditions EDM fails.\n\t\t\t\t//\tWhere failed, it was used in incorrect code, maybe near stack overflow.\n\t\t\t\t//\tAnyway, then call it in other thread, then works.\n\t\t\t\t//print.it(lastError.message); //0\n\t\t\t\treturn Task.Run(() => _All(true)).GetAwaiter().GetResult();\n\t\t\t}\n\t\t\t\n\t\t\tnint n = a[0];\n\t\t\tvar r = new screen[n];\n\t\t\tfor (int i = 0; i < n;) r[i++] = new(a[i]);\n\t\t\treturn r;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets screen at the specified index of the <see cref=\"all\"/> array.\n\t\t/// </summary>\n\t\t/// <param name=\"index\">0-based screen index. Index 0 is the primary screen. If index too big, gets the primary screen.</param>\n\t\t/// <param name=\"lazy\">Create variable with <see cref=\"LazyFunc\"/> that later will get screen handle.</param>\n\t\t/// <exception cref=\"ArgumentOutOfRangeException\">Negative <i>index</i>.</exception>\n\t\tpublic static screen index(int index, bool lazy = false) {\n\t\t\tif (index < 0) throw new ArgumentOutOfRangeException();\n\t\t\tif (lazy) return new screen(() => screen.index(index));\n\t\t\tif (index > 0) {\n\t\t\t\tvar a = _All();\n\t\t\t\tif (index < a.Length) return a[index];\n\t\t\t\t//print.warning(\"Invalid screen index.\");\n\t\t\t}\n\t\t\treturn primary;\n\t\t}\n\t\t//We don't use a cached array that is updated like in Screen class code, eg on SystemEvents.DisplaySettingsChanging (wm_displaychanged).\n\t\t//\tThen index functions are faster, but less reliable when changing display settings, because the array is updated with a delay.\n\t\t//\tNow index functions are fast enough. Faster than EnumWindows which is used much more often.\n\t\t\n\t\t//At first there was an implicit conversion from int that called index()? I don't remember why removed, but probably for a good reason.\n\t\t\n\t\t/// <summary>\n\t\t/// Gets index of this screen in the <see cref=\"all\"/> array.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Returns 0 (index of primary screen) if this variable is empty. Returns -1 if the screen handle is invalid; it can happen after changing display settings, but is rare.\n\t\t/// </remarks>\n\t\tpublic int ScreenIndex {\n\t\t\tget {\n\t\t\t\tif (_h == default && _func == null) return 0;\n\t\t\t\tvar h = _Handle();\n\t\t\t\tvar a = _All();\n\t\t\t\tfor (int i = 0; i < a.Length; i++) if (a[i]._h == h) return i;\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets screen rectangle and other info.\n\t\t/// </summary>\n\t\t/// <returns>\n\t\t/// Tuple containing:\n\t\t/// <br/>• <c>rect</c> - screen rectangle.\n\t\t/// <br/>• <c>workArea</c> - work area rectangle.\n\t\t/// <br/>• <c>isPrimary</c> - <c>true</c> if it is the primary screen.\n\t\t/// <br/>• <c>isAlive</c> - <c>false</c> if the screen handle is invalid; then the function gets info of the primary screen.\n\t\t/// </returns>\n\t\t/// <remarks>\n\t\t/// If this variable holds a callback function, this function calls it to get screen handle. See also <see cref=\"Now\"/>.\n\t\t/// </remarks>\n\t\tpublic unsafe (RECT rect, RECT workArea, bool isPrimary, bool isAlive) Info {\n\t\t\tget {\n\t\t\t\tvar h = _func != null ? _Handle() : _h;\n\t\t\t\tfor (int i = h != default ? 0 : 1; i < 10; i++, h = primary._h) { //retry if fails\n\t\t\t\t\tif (Api.GetMonitorInfo(h, out var m)) //fast\n\t\t\t\t\t\treturn (m.rcMonitor, m.rcWork, 0 != (m.dwFlags & 1), i == 0);\n\t\t\t\t}\n\t\t\t\treturn default;\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Calls <see cref=\"Info\"/> and returns rectangle of the screen or its work area.\n\t\t/// </summary>\n\t\t/// <param name=\"workArea\">Get work area rectangle.</param>\n\t\tpublic RECT GetRect(bool workArea = false) {\n\t\t\tvar v = Info;\n\t\t\treturn workArea ? v.workArea : v.rect;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Calls <see cref=\"Info\"/> and returns screen rectangle.\n\t\t/// </summary>\n\t\tpublic RECT Rect => Info.rect;\n\t\t\n\t\t/// <summary>\n\t\t/// Calls <see cref=\"Info\"/> and returns work area rectangle.\n\t\t/// </summary>\n\t\tpublic RECT WorkArea => Info.workArea;\n\t\t\n\t\t/// <summary>\n\t\t/// Gets DPI of this screen.\n\t\t/// Calls <see cref=\"Dpi.OfScreen\"/>.\n\t\t/// </summary>\n\t\tpublic int Dpi => More.Dpi.OfScreen(_Handle());\n\t\t//public int Dpi(bool supportWin81 = false) => More.Dpi.OfScreen(_Handle(), supportWin81); //no, rarely need, don't complicate everything. When need, can use Dpi.OfScreen.\n\t\t\n\t\t/// <summary>\n\t\t/// True if the screen handle is valid.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Don't use with variables that hold a callback function. This function does not call it and returns <c>false</c>.\n\t\t/// </remarks>\n\t\tpublic unsafe bool IsAlive => _h != default && Api.GetMonitorInfo(_h, out _);\n\t\t\n\t\t///\n\t\tpublic override string ToString() => _h.ToString() + \" \" + Rect.ToString();\n\t\t\n\t\t///\n\t\tpublic override int GetHashCode() => (int)_Handle();\n\t\t\n\t\t///\n\t\tpublic override bool Equals(object obj) => obj is screen && Equals((screen)obj);\n\t\t\n\t\t///\n\t\tpublic bool Equals(screen other) => other._Handle() == _Handle();\n\t\t\n\t\t///\n\t\tpublic static bool operator ==(screen a, screen b) => a.Equals(b);\n\t\t///\n\t\tpublic static bool operator !=(screen a, screen b) => !a.Equals(b);\n\t\t\n\t\t//rejected. GetHashCode gets hmonitor but is undocumented. Rarely used.\n\t\t///// <summary>Converts from <see cref=\"Screen\"/>.</summary>\n\t\t//public static implicit operator screen(Screen scrn) => new screen((IntPtr)scrn.GetHashCode());\n\t\t\n\t\t///// <summary>Converts to <see cref=\"Screen\"/>. Returns <c>null</c> if failed.</summary>\n\t\t//public static implicit operator Screen(screen scrn) { int h=(int)scrn._Handle(); return Screen.AllScreens.FirstOrDefault(o => o.GetHashCode() == h);\n\t\t\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if point <i>p</i> is in some screen.\n\t\t/// </summary>\n\t\tpublic static bool isInAnyScreen(POINT p) => Api.MonitorFromPoint(p, SODefault.Zero) != default;\n\t\t\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if rectangle <i>r</i> intersects with some screen.\n\t\t/// </summary>\n\t\tpublic static bool isInAnyScreen(RECT r) => Api.MonitorFromRect(r, SODefault.Zero) != default;\n\t\t\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if rectangle of window <i>w</i> intersects with some screen.\n\t\t/// </summary>\n\t\tpublic static bool isInAnyScreen(wnd w) => Api.MonitorFromWindow(w, SODefault.Zero) != default;\n\t\t\n\t\t/// <summary>\n\t\t/// Gets bounding rectangle of all screens.\n\t\t/// </summary>\n\t\tpublic static RECT virtualScreen\n\t\t\t=> new(Api.GetSystemMetrics(Api.SM_XVIRTUALSCREEN), Api.GetSystemMetrics(Api.SM_YVIRTUALSCREEN), Api.GetSystemMetrics(Api.SM_CXVIRTUALSCREEN), Api.GetSystemMetrics(Api.SM_CYVIRTUALSCREEN));\n\t\t\n\t\t//20 times faster, but maybe less reliable\n\t\t//public static RECT virtualScreen2 => wnd.getwnd.shellWindow.Rect;\n\t\t\n\t\tinternal screen ThrowIfWithHandle_ => _h == default ? this : throw new ArgumentException(\"screen with Handle. Must be lazy or empty.\");\n\t}\n}\n\nnamespace Au.Types {\n\t/// <summary>\n\t/// Used with <see cref=\"screen.of\"/> to specify what screen to return if the window/point/etc is not in a screen or if the window handle is invalid etc.\n\t/// </summary>\n\tpublic enum SODefault {\n\t\t/// <summary>Create empty variable.</summary>\n\t\tZero, //MONITOR_DEFAULTTONULL\n\t\t\n\t\t/// <summary>The primary screen.</summary>\n\t\tPrimary, //MONITOR_DEFAULTTOPRIMARY\n\t\t\n\t\t/// <summary>The nearest screen. If window handle is invalid - the primary screen.</summary>\n\t\tNearest, //MONITOR_DEFAULTTONEAREST\n\t}\n}\n"
  },
  {
    "path": "Au/Other/script+.cs",
    "content": "using System.Text.Json;\n\nnamespace Au.Types {\n\t/// <summary>\n\t/// <see cref=\"script.role\"/>.\n\t/// </summary>\n\tpublic enum SRole {\n\t\t/// <summary>\n\t\t/// The task runs as normal <c>.exe</c> program.\n\t\t/// It can be started from editor or not. It can run on computers where editor not installed.\n\t\t/// </summary>\n\t\tExeProgram,\n\t\t\n\t\t/// <summary>\n\t\t/// The task runs in <c>Au.Task-x64.exe</c> or <c>Au.Task-arm.exe</c> process, started from editor.\n\t\t/// </summary>\n\t\tMiniProgram,\n\t\t\n\t\t/// <summary>\n\t\t/// The task runs in editor process.\n\t\t/// </summary>\n\t\tEditorExtension,\n\t}\n\t\n\t/// <summary>\n\t/// Flags for <see cref=\"script.setup\"/> parameter <i>exception</i>. Defines what to do on unhandled exception.\n\t/// Default is <c>Print</c>, even if <c>script.setup</c> not called (with default compiler only).\n\t/// </summary>\n\t[Flags]\n\tpublic enum UExcept {\n\t\t/// <summary>\n\t\t/// Display exception info in output.\n\t\t/// </summary>\n\t\tPrint = 1,\n\t\t\n\t\t/// <summary>\n\t\t/// Show dialog with exception info.\n\t\t/// If editor available, the dialog contains links to functions in the call stack. To close the dialog when a link clicked, add flag <c>Print</c>.\n\t\t/// </summary>\n\t\tDialog = 2,\n\t}\n\t\n\t/// <summary>\n\t/// The default compiler adds this attribute to the assembly.\n\t/// </summary>\n\t[AttributeUsage(AttributeTargets.Assembly)]\n\tpublic sealed class PathInWorkspaceAttribute : Attribute {\n\t\t/// <summary>Path of main source file in workspace, like <c>@\"\\Script1.cs\"</c> or <c>@\"\\Folder1\\Script1.cs\"</c>.</summary>\n\t\tpublic readonly string Path;\n\t\t\n\t\t/// <summary>Full path of main source file.</summary>\n\t\tpublic readonly string FilePath;\n\t\t\n\t\t///\n\t\tpublic PathInWorkspaceAttribute(string path, string filePath) { Path = path; FilePath = filePath; }\n\t}\n\t\n\t/// <summary>\n\t/// The default compiler adds this attribute to the main assembly if using non-default references (meta <c>r</c> or <c>nuget</c>). Allows to find them at run time. Only if role <c>miniProgram</c> (default) or <c>editorExtension</c>.\n\t/// </summary>\n\t[AttributeUsage(AttributeTargets.Assembly)]\n\tpublic sealed class RefPathsAttribute : Attribute {\n\t\t/// <summary>Dll paths separated with <c>|</c>.</summary>\n\t\tpublic readonly string Paths;\n\t\t\n\t\t/// <param name=\"paths\">Dll paths separated with <c>|</c>.</param>\n\t\tpublic RefPathsAttribute(string paths) { Paths = paths; }\n\t}\n\t\n\t/// <summary>\n\t/// The default compiler adds this attribute to the main assembly if using NuGet packages with native dlls. Allows to find the dlls at run time. Only if role <c>miniProgram</c> (default) or <c>editorExtension</c>.\n\t/// </summary>\n\t[AttributeUsage(AttributeTargets.Assembly)]\n\tpublic sealed class NativePathsAttribute : Attribute {\n\t\t/// <summary>Dll paths separated with <c>|</c>.</summary>\n\t\tpublic readonly string Paths;\n\t\t\n\t\t/// <param name=\"paths\">Dll paths separated with <c>|</c>.</param>\n\t\tpublic NativePathsAttribute(string paths) { Paths = paths; }\n\t}\n\t\n\t/// <summary>\n\t/// <see cref=\"ScriptEditor.GetCommandState\"/>.\n\t/// </summary>\n\t[Flags]\n\tpublic enum ECommandState {\n\t\t///\n\t\tChecked = 1,\n\t\t///\n\t\tDisabled = 2,\n\t}\n\t\n\t/// <summary>\n\t/// For <see cref=\"ScriptEditor.GetIcon\"/>.\n\t/// </summary>\n\tpublic enum EGetIcon {\n\t\t/// <summary>\n\t\t/// Input is a file or folder in current workspace. Can be relative path in workspace (like <c>@\"\\Folder\\File.cs\"</c>) or full path or filename.\n\t\t/// Output must be icon name, like <c>\"*Pack.Icon color\"</c>. See <see cref=\"ImageUtil.LoadWpfImageElement\"/>.\n\t\t/// </summary>\n\t\tPathToIconName,\n\t\t\n\t\t/// <summary>\n\t\t/// Input is a file or folder in current workspace (see <c>PathToIconName</c>).\n\t\t/// Output must be icon XAML.\n\t\t/// </summary>\n\t\tPathToIconXaml,\n\t\t\n\t\t/// <summary>\n\t\t/// Input is icon name, like <c>\"*Pack.Icon color\"</c>. See <see cref=\"ImageUtil.LoadWpfImageElement\"/>.\n\t\t/// Output must be icon XAML.\n\t\t/// </summary>\n\t\tIconNameToXaml,\n\t\t\n\t\t//PathToGdipBitmap,\n\t\t//IconNameToGdipBitmap,\n\t}\n\t\n\t/// <summary>\n\t/// See <see cref=\"ScriptEditor.GetFileInfo\"/>.\n\t/// </summary>\n\t/// <param name=\"name\">File name, like <c>\"File.cs\"</c>.</param>\n\t/// <param name=\"path\">Path in workspace, like <c>@\"\\Folder\\File.cs\"</c>.</param>\n\t/// <param name=\"text\">File text; null if <i>needText</i> false or if failed to get text. If the file is open in editor, it's the editor text, else it's the saved text.</param>\n\t/// <param name=\"kind\"> </param>\n\t/// <param name=\"id\">File id.</param>\n\t/// <param name=\"filePath\">Full path.</param>\n\t/// <param name=\"workspace\">Path of the workspace folder.</param>\n\tpublic record class EFileInfo(string name, string path, string text, EFileKind kind, uint id, string filePath, string workspace);\n\t\n#pragma warning disable CS1591 //Missing XML comment for publicly visible type or member\n\t/// <summary>\n\t/// See <see cref=\"EFileInfo\"/>.\n\t/// </summary>\n\tpublic enum EFileKind { Script, Class, Other }\n#pragma warning restore CS1591 //Missing XML comment for publicly visible type or member\n}\n\nnamespace Au.More {\n\t/// <summary>\n\t/// Contains compilation info passed to current <c>preBuild</c>/<c>postBuild</c> script.\n\t/// </summary>\n\t/// <param name=\"outputFile\">Full path of the output exe or dll file.</param>\n\t/// <param name=\"outputPath\">Meta comment <c>outputPath</c>.</param>\n\t/// <param name=\"source\">Path of this C# code file in the workspace.</param>\n\t/// <param name=\"role\">Meta comment <c>role</c>.</param>\n\t/// <param name=\"optimize\">Meta comment <c>optimize</c>.</param>\n\t/// <param name=\"platform\">Meta comment <c>platform</c>.</param>\n\t/// <param name=\"preBuild\"><c>true</c> if the script used with meta <c>preBuild</c>, <c>false</c> if with <c>postBuild</c>.</param>\n\t/// <param name=\"publish\"><c>true</c> when publishing.</param>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// /*/ role editorExtension; /*/\n\t/// var c = PrePostBuild.Info;\n\t/// print.it(c);\n\t/// print.it(c.outputFile);\n\t/// ]]></code>\n\t/// </example>\n\tpublic record class PrePostBuild(string outputFile, string outputPath, string source, string role, bool optimize, string platform, bool preBuild, bool publish) {\n\t\t/// <summary>\n\t\t/// Gets compilation info passed to current <c>preBuild</c>/<c>postBuild</c> script.\n\t\t/// </summary>\n\t\tpublic static PrePostBuild Info { get; internal set; }\n\t\t\n\t\t///\n\t\t[Obsolete(\"Use platform.\"), EditorBrowsable(EditorBrowsableState.Never)]\n\t\tpublic bool bit32 => platform == \"x86\";\n\t}\n}\n"
  },
  {
    "path": "Au/Other/script.cs",
    "content": "namespace Au;\n\n/// <summary>\n/// Script task functions. Run, get properties, set options, etc.\n/// A script task is a running script, except if role <c>editorExtension</c>. Each script task is a separate process.\n/// </summary>\n/// <seealso cref=\"process\"/>\npublic static class script {\n\t#region properties\n\t\n\t/// <summary>\n\t/// Gets the script name, like <c>\"Script123\"</c>.\n\t/// </summary>\n\t/// <remarks>\n\t/// If role <c>miniProgram</c> (default), returns the script file name without extension.\n\t/// Else returns <see cref=\"AppDomain.FriendlyName\"/>, like <c>\"MainAssemblyName\"</c>.\n\t/// </remarks>\n\tpublic static string name {\n\t\tget => s_name ??= AppDomain.CurrentDomain.FriendlyName; //info: in framework 4 with \".exe\", now without (now it is the entry assembly name)\n\t\tinternal set { s_name = value; }\n\t}\n\tstatic string s_name;\n\t\n\t/// <summary>\n\t/// Gets the script role (<c>miniProgram</c>, <c>exeProgram</c> or <c>editorExtension</c>).\n\t/// </summary>\n\tpublic static SRole role { get; internal set; }\n\t\n\t/// <summary>\n\t/// Gets path of the caller source code file.\n\t/// </summary>\n\t/// <param name=\"f_\">[](xref:caller_info)</param>\n\t/// <seealso cref=\"CallerFilePathAttribute\"/>\n\t/// <seealso cref=\"folders.sourceCode(string)\"/>\n\tpublic static string sourcePath([CallerFilePath] string f_ = null) => f_;\n\t\n\t/// <summary>\n\t/// Gets path of the main source code file of this program or of a library.\n\t/// </summary>\n\t/// <param name=\"inWorkspace\">Get path in the workspace, like <c>@\"\\Script1.cs\"</c> or <c>@\"\\Folder1\\Script1.cs\"</c>.</param>\n\t/// <param name=\"asm\">An assembly compiled by LibreAutomate. If <c>null</c>, uses <see cref=\"Assembly.GetEntryAssembly\"/>.</param>\n\t/// <returns><c>null</c> if failed.</returns>\n\t/// <remarks>\n\t/// When compiling, LibreAutomate adds <see cref=\"PathInWorkspaceAttribute\"/> to the assembly. Then at run time this function gets its value. Returns <c>null</c> if compiled by some other compiler.\n\t/// </remarks>\n\t/// <seealso cref=\"folders.sourceCodeMain(Assembly)\"/>\n\tpublic static string sourcePath(bool inWorkspace, Assembly asm = null) {\n\t\tasm ??= AssemblyUtil_.GetEntryAssembly();\n\t\tif (asm?.GetCustomAttribute<PathInWorkspaceAttribute>() is not { } a) return null;\n\t\treturn inWorkspace ? a.Path : a.FilePath;\n\t}\n\t\n\t/// <summary>\n\t/// Gets workspace path of the main source code file of this program, like <c>@\"\\Script1.cs\"</c> or <c>@\"\\Folder1\\Script1.cs\"</c>.\n\t/// Calls <see cref=\"sourcePath(bool, Assembly)\"/>.\n\t/// </summary>\n\t[EditorBrowsable(EditorBrowsableState.Never)] //replaced with sourcePath. Limited and unclear.\n\tpublic static string path => sourcePath(true);\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if this script task was started from editor with the <b>Run</b> button or menu command.\n\t/// Always <c>false</c> if role <c>editorExtension</c>.\n\t/// </summary>\n\tpublic static bool testing { get; internal set; }\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if the build configuration of the main assembly is Debug (default). Returns <c>false</c> if Release (<c>optimize true</c>).\n\t/// </summary>\n\tpublic static bool isDebug => s_debug ??= AssemblyUtil_.IsDebug(AssemblyUtil_.GetEntryAssembly());\n\tstatic bool? s_debug;\n\t//note: GetEntryAssembly returns null in func called by host through coreclr_create_delegate.\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if running in WPF preview mode.\n\t/// </summary>\n\tpublic static bool isWpfPreview {\n\t\tget {\n\t\t\tif (role != SRole.MiniProgram) return false;\n\t\t\tvar s = Environment.CommandLine;\n\t\t\t//return s.Contains(\" WPF_PREVIEW \") && s.RxIsMatch(@\" WPF_PREVIEW (-?\\d+) (-?\\d+)$\"); //slower JIT\n\t\t\treturn s.Contains(\" WPF_PREVIEW \") && _IsWpfPreview(s);\n\t\t\t\n\t\t\t//[MethodImpl(MethodImplOptions.NoInlining)]\n\t\t\tstatic bool _IsWpfPreview(string s) => s.RxIsMatch(@\" WPF_PREVIEW (-?\\d+) (-?\\d+)$\");\n\t\t\t\n\t\t\t//don't cache. It makes JIT slower. Now fast after JIT.\n\t\t}\n\t}\n\t\n\t#endregion\n\t\n\t#region AppModuleInit_, setup\n\t\n\t/// <summary>\n\t/// If role <c>miniProgram</c> or <c>exeProgram</c>, default compiler adds module initializer that calls this with <i>auCompiler</i> <c>true</c>.\n\t/// When compiling single-file exe with <c>dotnet publish</c>, adds module initializer that calls this with <i>auCompiler</i> <c>false</c>.\n\t/// If using other compiler, called from <c>script.setup</c> with <i>auCompiler</i> <c>false</c>.\n\t/// </summary>\n\t[EditorBrowsable(EditorBrowsableState.Never)]\n\tpublic static unsafe void AppModuleInit_(bool auCompiler) {\n\t\tif (s_appModuleInit) return;\n\t\ts_appModuleInit = true;\n\t\t\n\t\tprocess.thisProcessCultureIsInvariant = true;\n\t\t\n\t\tCpp.Cpp_UEF(true); //2 ms. Loads the C++ dll.\n\t\t\n\t\tApi.SetErrorMode(Api.SEM_NOGPFAULTERRORBOX | Api.SEM_FAILCRITICALERRORS);\n\t\t//SEM_NOGPFAULTERRORBOX disables WER. See also the workaround below. //CONSIDER: add setup parameter enableWER.\n\t\t//SEM_FAILCRITICALERRORS disables some error message boxes, eg when removable media not found; MSDN recommends too.\n\t\t\n\t\tAppDomain.CurrentDomain.UnhandledException += _UnhandledException;\n\t\t\n\t\tAppDomain.CurrentDomain.ProcessExit += (_, _) => {\n\t\t\tExiting_ = true;\n\t\t\tCpp.Cpp_UEF(false);\n\t\t};\n\t\t\n\t\tif (role == SRole.ExeProgram) {\n\t\t\t//set STA thread if Main without [MTAThread]\n\t\t\tif (Thread.CurrentThread.GetApartmentState() != ApartmentState.STA) { //speed: 150 mcs\n\t\t\t\tif (null == Assembly.GetEntryAssembly().EntryPoint.GetCustomAttribute<MTAThreadAttribute>()) { //1.5 ms\n\t\t\t\t\tprocess.ThisThreadSetComApartment_(ApartmentState.STA); //1.6 ms\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tint pidEditor = 0;\n\t\t\tif (auCompiler) {\n\t\t\t\tMiniProgram_.ResolveNugetRuntimes_(AppContext.BaseDirectory);\n\t\t\t\t\n\t\t\t\tvar cd = Environment.CurrentDirectory;\n\t\t\t\tconst string c_ep = \"\\\\Roslyn\\\\.exeProgram\";\n\t\t\t\tif (cd.Ends(c_ep, true)) { //started from editor\n\t\t\t\t\tEnvironment.CurrentDirectory = folders.ThisApp;\n\t\t\t\t\t\n\t\t\t\t\tvar p = &SharedMemory_.Ptr->script;\n\t\t\t\t\tpidEditor = p->pidEditor;\n\t\t\t\t\ts_wndEditorMsg = (wnd)p->hwndMsg;\n\t\t\t\t\ts_idMainFile = p->idMainFile;\n\t\t\t\t\tif (0 != (p->flags & 2)) script.testing = true;\n\t\t\t\t\tif (0 != (p->flags & 4)) ScriptEditor.IsPortable = true;\n\t\t\t\t\tif (0 != (p->flags & 8)) s_wrPipeName = p->pipe;\n\t\t\t\t\tif (0 != (p->flags & 16)) MiniProgram_.RedirectConsole_();\n\t\t\t\t\tfolders.Editor = new(cd[..^c_ep.Length]);\n\t\t\t\t\tfolders.Workspace = new(p->workspace);\n\t\t\t\t\t\n\t\t\t\t\tvar hevent = Api.OpenEvent(Api.EVENT_MODIFY_STATE, false, \"Au.event.exeProgram.1\");\n\t\t\t\t\tif (!Api.SetEvent(hevent)) Environment.Exit(4);\n\t\t\t\t\tApi.CloseHandle(hevent);\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tStarting_(AppDomain.CurrentDomain.FriendlyName, pidEditor);\n\t\t}\n\t}\n\tstatic bool s_appModuleInit;\n\tstatic UExcept s_setupException = UExcept.Print;\n\tinternal static Exception s_unhandledException; //for process.thisProcessExit\n\tinternal static wnd s_wndEditorMsg;\n\t\n\tinternal static bool Exiting_ { get; private set; }\n\t\n\t[DebuggerNonUserCode]\n\tstatic void _UnhandledException(object sender, UnhandledExceptionEventArgs u) {\n\t\tif (!u.IsTerminating) return; //never seen, but anyway\n\t\tExiting_ = true;\n\t\tCpp.Cpp_UEF(false);\n\t\tvar e = (Exception)u.ExceptionObject; //probably non-Exception object is impossible in C#\n\t\ts_unhandledException = e;\n\t\tif (Debugger.IsAttached) return;\n\t\tif (s_setupException.Has(UExcept.Print)) print.it(e);\n\t\tif (s_setupException.Has(UExcept.Dialog)) {\n\t\t\tvar text = e.ToStringWithoutStack();\n\t\t\tvar d = new dialog(\"Task failed\", null, \"Close\", flags: DFlags.ExpandDown, expandedText: e.ToString());\n\t\t\tif (ScriptEditor.Available) {\n\t\t\t\tvar st = new StackTrace(e, true);\n\t\t\t\tvar b = new StringBuilder(text + \"\\n\");\n\t\t\t\tfor (int i = 0; i < st.FrameCount; i++) {\n\t\t\t\t\tvar f = st.GetFrame(i);\n\t\t\t\t\tif (f.HasSource()) b.Append($\"\\n<a href=\\\"{i}\\\">{f.GetMethod()?.Name}</a> in {pathname.getName(f.GetFileName())}:{f.GetFileLineNumber()}\");\n\t\t\t\t}\n\t\t\t\ttext = b.ToString();\n\t\t\t\td.HyperlinkClicked += e => {\n\t\t\t\t\tif (s_setupException.Has(UExcept.Print)) e.d.Send.Close();\n\t\t\t\t\tvar f = st.GetFrame(e.LinkHref.ToInt());\n\t\t\t\t\tScriptEditor.Open(f.GetFileName(), f.GetFileLineNumber(), Math.Max(0, f.GetFileColumnNumber() - 1));\n\t\t\t\t};\n\t\t\t}\n\t\t\td.Text2(text);\n\t\t\td.ShowDialog();\n\t\t}\n\t\t\n\t\t//workaround for .NET bug: randomly changes error mode.\n\t\t//\tUsually 0x3 -> 0x8001 (removed SEM_NOGPFAULTERRORBOX), sometimes even 0x0. Usually never restores.\n\t\t//\tThen on unhandled exception starts werfault.exe (with \"wait\" cursor), and the process exits with 1 s delay, even if WER disabled.\n\t\t//\tTested: same in a standard simplest .NET program. But less frequently.\n\t\t//\tUsually it happens while _AuxThread is starting, often in Cpp_UEF (now removed). Rarely if _AuxThread not used.\n\t\t//\tSeveral ms before or after the script code starts.\n\t\t//\tThe bug is in several places in CLR code.\n\t\t//\t\tIt sets error mode, then executes code without lock and exception handling, then restores (if no exception).\n\t\t//\t\tWhy it does not use SetThreadErrorMode? Why it uses the obsolete flag SEM_NOOPENFILEERRORBOX? Why it does not check maybe SEM_FAILCRITICALERRORS is already set (as recommended in doc)? Why it does not | the new error mode flags with current flags?\n\t\t//\tCould move _AuxThread to the C++ dll (not very easy).\n\t\t//\t\tOr move most of the _AuxThread startup code to the main thread (making script startup slower).\n\t\t//\t\tBut it just would make this less frequent.\n\t\t//\t\tAnyway, moved Cpp_UEF here. It's better to load the dll sync, not at a random time later.\n\t\t//\tNever mind. This workaround solves the biggest problem for this library. Maybe future .NET will fix it.\n\t\tApi.SetErrorMode(Api.SEM_NOGPFAULTERRORBOX | Api.SEM_FAILCRITICALERRORS);\n\t}\n\t\n\t[StructLayout(LayoutKind.Sequential, Size = 256 + 1024)] //note: this struct is in shared memory. Size must be same in all library versions.\n\tinternal unsafe struct SharedMemoryData_ {\n\t\tpublic int flags; //1 not received (let editor wait), 2 testing, 4 isPortable, 8 has pipe\n\t\tpublic int pidEditor;\n\t\tpublic int hwndMsg;\n\t\tpublic uint idMainFile;\n\t\tint _pipeLen;\n\t\tfixed char _pipeData[64];\n\t\tint _workspaceLen;\n\t\tfixed char _workspaceData[1024];\n\t\t\n\t\tpublic string pipe {\n\t\t\tget { fixed (char* p = _pipeData) return new(p, 0, _pipeLen); }\n\t\t\tset {\n\t\t\t\tfixed (char* p = _pipeData) value.AsSpan().CopyTo(new Span<char>(p, 64));\n\t\t\t\t_pipeLen = value.Length;\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic string workspace {\n\t\t\tget { fixed (char* p = _workspaceData) return new(p, 0, _workspaceLen); }\n\t\t\tset {\n\t\t\t\tfixed (char* p = _workspaceData) value.AsSpan().CopyTo(new Span<char>(p, 1024));\n\t\t\t\t_workspaceLen = value.Length;\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Adds various features to this script task (running script): tray icon, exit on <c>Ctrl+Alt+Delete</c>, etc.\n\t/// </summary>\n\t/// <param name=\"trayIcon\">Add tray icon. See <see cref=\"trayIcon\"/>.</param>\n\t/// <param name=\"sleepExit\">End this process when computer is going to sleep or hibernate.</param>\n\t/// <param name=\"lockExit\">\n\t/// End this process when the active desktop has been switched (PC locked, <c>Ctrl+Alt+Delete</c>, screen saver, etc, except UAC consent).\n\t/// Then to end this process you can use hotkeys <c>Win+L</c> (lock computer) and <c>Ctrl+Alt+Delete</c>.\n\t/// Most mouse, keyboard, clipboard and window functions don't work when other desktop is active. Many of them then throw exception, and the script would end anyway.\n\t/// </param>\n\t/// <param name=\"debug\">Call <see cref=\"DebugTraceListener.Setup\"/> with <i>usePrint</i> <c>true</c>. It makes <see cref=\"Debug.Assert\"/> etc useful when not debugging.</param>\n\t/// <param name=\"exception\">What to do on unhandled exception (event <see cref=\"AppDomain.UnhandledException\"/>).</param>\n\t/// <param name=\"exitKey\">\n\t/// If not 0, the script task will end when this key pressed. Will call <see cref=\"Environment.Exit\"/>.\n\t/// Example: <c>exitKey: KKey.MediaStop</c>.\n\t/// <para>\n\t/// Recommended keys: media, volume, browser and applaunch keys. They work even when the process of the active window is admin (UAC) and this script isn't. In any case, the key does not work if somewhere used for a global hotkey, trigger, <i>exitKey</i> or <i>pauseKey</i>. Also the key does not work when at that time a modifier key is pressed by a script; it also can be dangerous because may generate a trigger or hotkey used by an app or OS.\n\t/// </para>\n\t/// </param>\n\t/// <param name=\"pauseKey\">\n\t/// Let <see cref=\"pause\"/> pause/resume when this key pressed. Default: <c>ScrollLock</c> (<c>Fn+S</c>, <c>Fn+K</c> or similar).\n\t/// If <c>CapsLock</c>, pauses when it is toggled (even if was toggled at startup) and resumes when untoggled.\n\t/// <para>\n\t/// <c>ScrollLock</c>, <c>CapsLock</c> and <c>NumLock</c> are the most reliable. Other keys have the same problems as with <i>exitKey</i>.\n\t/// </para>\n\t/// </param>\n\t/// <param name=\"f_\">[](xref:caller_info). Don't use. Or use like <c>f_: null</c> to disable script editing via tray icon.</param>\n\t/// <exception cref=\"InvalidOperationException\">Already called.</exception>\n\t/// <remarks>\n\t/// Tip: in <b>Options > Templates</b> you can set default code for new scripts.\n\t/// \n\t/// If your program was compiled not in LibreAutomate, call this function (maybe with zero arguments) if you want the program behave like if it was compiled with LibreAutomate (invariant culture, <c>STAThread</c>, unhandled exception action).\n\t/// \n\t/// Does nothing if role <c>editorExtension</c> or if running in WPF preview mode.\n\t/// </remarks>\n\tpublic static void setup(bool trayIcon = false, bool sleepExit = false, bool lockExit = false, bool debug = false, UExcept exception = UExcept.Print, KKey exitKey = 0, KKey pauseKey = KKey.ScrollLock, [CallerFilePath] string f_ = null) {\n\t\tif (role == SRole.EditorExtension || isWpfPreview) return;\n\t\tif (s_setupOnce) throw new InvalidOperationException(\"script.setup already called\");\n\t\ts_setupOnce = true;\n\t\t\n\t\ts_setupException = exception;\n\t\tif (!s_appModuleInit) AppModuleInit_(auCompiler: false); //if role miniProgram, called by MiniProgram_.Init; else if default compiler, the call is compiled into code; else called now.\n\t\t\n\t\tif (debug) DebugTraceListener.Setup(usePrint: true); //info: default false, because slow and rarely used.\n\t\t\n\t\ts_exitKey = exitKey;\n\t\ts_pauseKey = pauseKey;\n\t\ts_pauseSetupDone = true;\n\t\t\n\t\tif (sleepExit || lockExit || exitKey != 0 || pauseKey is not (0 or KKey.CapsLock)) {\n\t\t\ts_sleepExit = sleepExit;\n\t\t\ts_lockExit = lockExit;\n\t\t\ts_exitKey = exitKey;\n\t\t\ts_auxThread.QueueAPC(static () => {\n\t\t\t\tif (s_sleepExit) {\n\t\t\t\t\tif (osVersion.minWin8) {\n\t\t\t\t\t\t//if Modern Standby, need RegisterSuspendResumeNotification to receive WM_POWERBROADCAST.\n\t\t\t\t\t\t//\tThe API and MS are unavailable on Win7.\n\t\t\t\t\t\t//\tThe API supports window handle and callback. With handle less problems.\n\t\t\t\t\t\tvar h1 = Api.RegisterSuspendResumeNotification(s_auxWnd.Handle, 0);\n\t\t\t\t\t\tprocess.thisProcessExit += _ => { Api.UnregisterSuspendResumeNotification(h1); };\n\t\t\t\t\t} else {\n\t\t\t\t\t\tWndUtil.CreateWindowDWP_(messageOnly: false, t_eocWP = (w, m, wp, lp) => {\n\t\t\t\t\t\t\tif (m == Api.WM_POWERBROADCAST && wp == Api.PBT_APMSUSPEND) _SleepLockExit(true);\n\t\t\t\t\t\t\treturn Api.DefWindowProc(w, m, wp, lp);\n\t\t\t\t\t\t}); //message-only windows don't receive WM_POWERBROADCAST, unless used RegisterSuspendResumeNotification\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (s_lockExit) {\n\t\t\t\t\tnew WinEventHook(EEvent.SYSTEM_DESKTOPSWITCH, 0, k => {\n\t\t\t\t\t\tif (miscInfo.isInputDesktop()) return;\n\t\t\t\t\t\tif (process.exists(\"consent.exe\", ofThisSession: true)) return; //UAC\n\t\t\t\t\t\tk.hook.Dispose();\n\t\t\t\t\t\t_SleepLockExit(false);\n\t\t\t\t\t});\n\t\t\t\t\t//tested: on Win+L works immediately. OS switches desktop 2 times. At first briefly, then makes defaul again, then on key etc switches again to show password field.\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (s_exitKey != 0) {\n\t\t\t\t\tif (!_RegisterKey(s_exitKey, 16)) {\n\t\t\t\t\t\tlong i1 = 0;\n\t\t\t\t\t\ttimer.every(500, t => {\n\t\t\t\t\t\t\tif (_RegisterKey(s_exitKey, 16)) t.Stop();\n\t\t\t\t\t\t\telse if (++i1 == 4) print.warning($\"{name}: script.setup failed to register the exit key. Will retry.\", -1);\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (s_pauseKey is not (0 or KKey.CapsLock)) _PauseSetKey();\n\t\t\t});\n\t\t}\n\t\t\n\t\tif (trayIcon) _TrayIcon(f_: f_);\n\t}\n\tstatic bool s_setupOnce, s_sleepExit, s_lockExit;\n\tstatic KKey s_exitKey;\n\t[ThreadStatic] static WNDPROC t_eocWP;\n\t\n\t/// <summary>\n\t/// Ensures that multiple processes that call this function don't run simultaneously. Like C# <c>lock</c> keyword for threads.\n\t/// </summary>\n\t/// <param name=\"mutex\">Mutex name. If another process called this function with this mutex name, this process cannot run, and this function calls <c>Environment.Exit(3);</c>.</param>\n\t/// <param name=\"wait\">Milliseconds to wait until this process can run. No timeout if -1.</param>\n\t/// <param name=\"silent\">Don't print <c>\"cannot run\"</c>.</param>\n\t/// <param name=\"ifCantRun\">Called if this process cannot run.</param>\n\t/// <exception cref=\"InvalidOperationException\">This function already called.</exception>\n\t/// <remarks>\n\t/// This function is useful when this script has role <c>exeProgram</c> and the compiled program is launched not from the script editor, because then the <c>/*/ ifRunning /*/</c> property is ignored.\n\t/// </remarks>\n\t/// <seealso cref=\"AppSingleInstance\"/>\n\tpublic static void single(string mutex = \"Au-mutex-script.single\", int wait = 0, bool silent = false, Action ifCantRun = null) {\n\t\t//FUTURE: parameter bool endOther. Like meta ifRunning restart.\n\t\t\n\t\tvar m = Api.CreateMutex(null, false, mutex ?? \"Au-mutex-script.single\"); //tested: don't need Api.SECURITY_ATTRIBUTES.ForLowIL\n\t\tif (default != Interlocked.CompareExchange(ref s_singleMutex, m, default)) { Api.CloseHandle(m); throw new InvalidOperationException(); }\n\t\tvar r = Api.WaitForSingleObject(s_singleMutex, wait);\n\t\tif (r is not (0 or Api.WAIT_ABANDONED)) {\n\t\t\tifCantRun?.Invoke();\n\t\t\tif (!silent) print.it($\"<>Note: script task <open {sourcePath(true)}|||script.single>{name}<> cannot run because a task is running.\");\n\t\t\tEnvironment.Exit(3);\n\t\t}\n\t\t//never mind: should release mutex.\n\t\t//\tCannot release in process exit event. It runs in another thread.\n\t\t//\tCannot use UsingEndAction, because then caller code must be like 'using var single = script.single();'.\n\t\t//return new(() => Api.ReleaseMutex(s_singleMutex));\n\t}\n\tstatic IntPtr s_singleMutex;\n\t\n\t/// <summary>\n\t/// Low-level version of <see cref=\"single\"/>. No <c>Environment.Exit</c>, no exception, no print. Just <c>CreateMutex</c> and <c>WaitForSingleObject</c>.\n\t/// </summary>\n\t/// <returns>false if another process owns the mutex.</returns>\n\tinternal static bool TrySingle_(string mutex, int wait = 0) => Api.WaitForSingleObject(Api.CreateMutex(null, false, mutex), wait) is 0 or Api.WAIT_ABANDONED;\n\t\n\t//public static bool single(out nint mutexHandle, string mutexName, int wait = 0) => Api.WaitForSingleObject(mutexHandle = Api.CreateMutex(null, false, mutexName), wait) is 0 or Api.WAIT_ABANDONED;\n\t\n\t/// <summary>\n\t/// Adds standard tray icon.\n\t/// </summary>\n\t/// <param name=\"delay\">Delay, milliseconds.</param>\n\t/// <param name=\"init\">Called before showing the tray icon. Can set its properties and event handlers.</param>\n\t/// <param name=\"menu\">Called before showing context menu. Can add menu items. Menu item actions must not block messages etc for long time; if need, run in other thread or process (<see cref=\"script.run\"/>).</param>\n\t/// <param name=\"f_\">[](xref:caller_info). Don't use. Or set = <c>null</c> to disable script editing via the tray icon.</param>\n\t/// <remarks>\n\t/// Uses other thread. The <i>init</i> and <i>menu</i> actions run in that thread too. It dispatches messages, therefore they also can set timers (<see cref=\"timer\"/>), create hidden windows, etc. Current thread does not have to dispatch messages.\n\t/// \n\t/// Does nothing if role <c>editorExtension</c>.\n\t/// </remarks>\n\t/// <example>\n\t/// How to change icon and tooltip.\n\t/// <code><![CDATA[\n\t/// script.trayIcon(init: t => { t.Icon = icon.stock(StockIcon.HELP); t.Tooltip = \"Example\"; });\n\t/// ]]></code>\n\t/// How to add menu items.\n\t/// <code><![CDATA[\n\t/// script.trayIcon(menu: (t, m) => {\n\t/// \tm[\"Example\"] = o => { dialog.show(\"Example\"); };\n\t/// \tm[\"Run another script\"] = o => { script.run(\"Example.cs\"); };\n\t/// });\n\t/// ]]></code>\n\t/// </example>\n\t/// <seealso cref=\"Au.trayIcon\"/>\n\tpublic static void trayIcon(int delay = 500, Action<trayIcon> init = null, Action<trayIcon, popupMenu> menu = null, [CallerFilePath] string f_ = null) {\n\t\tif (role == SRole.EditorExtension) return;\n\t\tif (!s_appModuleInit) AppModuleInit_(auCompiler: false);\n\t\t_TrayIcon(delay, init, menu, f_);\n\t}\n\t\n\tstatic void _TrayIcon(int delay = 500, Action<trayIcon> init = null, Action<trayIcon, popupMenu> menu = null, [CallerFilePath] string f_ = null) {\n\t\ts_auxThread.QueueAPC(() => timer.after(delay, _Delayed));\n\t\t\n\t\tvoid _Delayed(timer t_) {\n\t\t\tvar ti = new trayIcon { Tooltip = script.name };\n\t\t\tinit?.Invoke(ti);\n\t\t\tti.Icon ??= icon.trayIcon();\n\t\t\tbool canEdit = f_ != null && ScriptEditor.Available;\n\t\t\tif (canEdit) ti.Click += _ => ScriptEditor.Open(f_);\n\t\t\tti.RightClick += e => {\n\t\t\t\tvar m = new popupMenu();\n\t\t\t\tif (menu != null) {\n\t\t\t\t\tmenu(ti, m);\n\t\t\t\t\tif (m.Last != null && !m.Last.IsSeparator) m.Separator();\n\t\t\t\t}\n\t\t\t\tif (canEdit) m[\"Open script\"] = _ => ScriptEditor.Open(f_);\n\t\t\t\tm[\"End task\"] = _ => Environment.Exit(2);\n\t\t\t\tif (canEdit) m[\"End and open\"] = _ => { ScriptEditor.Open(f_); Environment.Exit(2); };\n\t\t\t\tm.Show(PMFlags.AlignCenterH | PMFlags.AlignRectBottomTop, /*excludeRect: ti.GetRect(out var r1) ? r1 : null,*/ owner: ti.Hwnd);\n\t\t\t};\n\t\t\tti.Visible = true;\n\t\t}\n\t}\n\t\n\t#endregion\n\t\n\t#region aux thread\n\t\n\tinternal static unsafe void Starting_(string name, int pidEditor, bool preloaded = false) {\n\t\ts_name = name;\n\t\ts_auxThread = new(() => _AuxThread(pidEditor, preloaded));\n\t\t//using CreateThread because need thread handle ASAP\n\t}\n\tstatic NativeThread_ s_auxThread;\n\t\n\t/// <summary>\n\t/// Gets the aux thread object. Auto-creates (starts thread and does not wait) if used in an app that does not call <c>Starting_</c> at startup (compiled not by LA).\n\t/// Thread-safe.\n\t/// </summary>\n\tinternal static NativeThread_ GetAuxThread_() {\n\t\tif (s_auxThread != null) return s_auxThread;\n\t\tDebug.Assert(role != SRole.MiniProgram);\n\t\tlock (\"s_auxThread\") {\n\t\t\treturn s_auxThread ??= new(() => _AuxThread(0, false));\n\t\t}\n\t}\n\t\n\t//Auxiliary thread for various tasks:\n\t//\tExit when editor process terminated or crashed.\n\t//\tTerminate script processes in a less brutal way.\n\t//\tTray icon.\n\t//\tscript.setup(sleepExit, lockExit)\n\t//\tCpp_InactiveWindowWorkaround for miniProgram.\n\t//\tCan be used for various triggers.\n\t//\tEtc.\n\tstatic unsafe void _AuxThread(int pidEditor, bool preloaded) {\n\t\tThread.CurrentThread.Name = \"Au.Aux\";\n\t\tWndUtil.UacEnableMessages(Api.WM_COPYDATA, Api.WM_USER, Api.WM_CLOSE, c_msg_IconImageCache_ClearAll);\n\t\tWndUtil.RegisterWindowClass(c_auxWndClassName, _AuxWndProc);\n\t\ts_auxWnd = WndUtil.CreateMessageOnlyWindow(c_auxWndClassName, Api.GetCurrentProcessId().ToS());\n\t\t\n\t\t_MessageLoop(pidEditor, preloaded);\n\t\t\n\t\t[MethodImpl(MethodImplOptions.NoInlining)] //need fast JIT of the main func, to make s_auxWnd available ASAP\n\t\tstatic void _MessageLoop(int pidEditor, bool preloaded) {\n\t\t\t//pidEditor 0 if exeProgram started not from editor\n\t\t\tvar hp = pidEditor == 0 ? default : (IntPtr)Handle_.OpenProcess(pidEditor, Api.SYNCHRONIZE);\n\t\t\t\n\t\t\t//Cpp.Cpp_UEF(true); //moved to AppModuleInit_\n\t\t\t\n\t\t\tif (preloaded) Cpp.Cpp_InactiveWindowWorkaround(true);\n\t\t\t\n\t\t\tNativeThread_.OfThisThread.ThreadInited();\n\t\t\t\n\t\t\tint nh = hp == default ? 0 : 1;\n\t\t\tfor (; ; ) {\n\t\t\t\tvar k = Api.MsgWaitForMultipleObjectsEx(nh, &hp, -1, Api.QS_ALLINPUT, Api.MWMO_ALERTABLE | Api.MWMO_INPUTAVAILABLE);\n\t\t\t\tif (k == nh) {\n\t\t\t\t\tif (!wait.doEvents()) break;\n\t\t\t\t} else if (k == 0) { //editor process terminated or crashed\n\t\t\t\t\t_AuxExit();\n\t\t\t\t} else if (k != Api.WAIT_IO_COMPLETION) {\n\t\t\t\t\tDebug_.Print(k);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Class name of the auxiliary message-only window.\n\t/// </summary>\n\tinternal const string c_auxWndClassName = \"Au.Task.m3gVxcTJN02pDrHiQ00aSQ\";\n\t\n\tstatic unsafe nint _AuxWndProc(wnd w, int message, nint wp, nint lp) {\n\t\tswitch (message) {\n\t\t//case Api.WM_COPYDATA:\n\t\t//\treturn 0;\n\t\t//case Api.WM_USER:\n\t\t//\treturn 0;\n\t\tcase Api.WM_POWERBROADCAST:\n\t\t\tif (s_sleepExit && osVersion.minWin8 && wp == Api.PBT_APMSUSPEND) _SleepLockExit(true);\n\t\t\tbreak;\n\t\tcase Api.WM_HOTKEY:\n\t\t\tif (wp is >= 0 and <= 15) {\n\t\t\t\ts_paused ^= true;\n\t\t\t} else if (wp is >= 16 and <= 31) {\n\t\t\t\tEnvironment.Exit(2);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase c_msg_IconImageCache_ClearAll:\n\t\t\tIconImageCache.ClearAll_();\n\t\t\tbreak;\n\t\tcase Api.WM_SETTEXT when wp != 0:\n\t\t\ttry {\n\t\t\t\tstring s = new((char*)lp);\n\t\t\t\tif (wp == c_msg_wmsettext_UpdateEnvVar) { //sent from LA by EnvVarUpdater on WM_SETTINGCHANGE\n\t\t\t\t\tvar csv = csvTable.parse(s);\n\t\t\t\t\tforeach (var a in csv.Rows) Environment.SetEnvironmentVariable(a[0], a[1]);\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception e1) { Debug_.Print(e1); }\n\t\t\treturn 0;\n\t\t}\n\t\t\n\t\tvar R = Api.DefWindowProc(w, message, wp, lp);\n\t\t\n\t\tif (message == Api.WM_DESTROY) _AuxExit();\n\t\t\n\t\treturn R;\n\t}\n\t\n\tinternal const int c_msg_IconImageCache_ClearAll = Api.WM_USER + 5;\n\tinternal const int c_msg_wmsettext_UpdateEnvVar = -100;\n\t\n\tstatic void _AuxExit() {\n\t\tEnvironment.Exit(1);\n\t\t\n\t\t//same speed\n\t\t//process.thisProcessExitInvoke();\n\t\t//Api.ExitProcess(1);\n\t}\n\t\n\t/// <summary>\n\t/// Gets the message-only window of the aux thread.\n\t/// Waits if still not created.\n\t/// </summary>\n\tinternal static wnd AuxWnd_ {\n\t\tget {\n\t\t\tif (s_auxWnd.Is0) {\n\t\t\t\tGetAuxThread_();\n\t\t\t\twhile (s_auxWnd.Is0) {\n\t\t\t\t\tDebug_.Print(\"waiting for s_auxWnd\");\n\t\t\t\t\tThread.Sleep(12);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn s_auxWnd;\n\t\t}\n\t}\n\tstatic wnd s_auxWnd;\n\t\n\t#endregion\n\t\n\t#region run\n\t\n\t/// <summary>\n\t/// Starts executing a script. Does not wait.\n\t/// </summary>\n\t/// <param name=\"script\">Script name like <c>\"Script5.cs\"</c>, or path like <c>@\"\\Folder\\Script5.cs\"</c>.</param>\n\t/// <param name=\"args\">Command line arguments. In the script it will be variable <i>args</i>. Should not contain <c>'\\0'</c> characters.</param>\n\t/// <returns>\n\t/// Native process id of the task process.\n\t/// Returns -1 if failed, for example if the script contains errors or cannot run second task instance.\n\t/// Returns 0 if task start is deferred because the script is running (<c>ifRunning</c> <c>wait</c>/<c>wait_restart</c>).\n\t/// If role <c>editorExtension</c>, waits until the script ends, then returns 0.\n\t/// </returns>\n\t/// <exception cref=\"FileNotFoundException\">Script file not found.</exception>\n\t/// <exception cref=\"AuException\">Script editor not running.</exception>\n\tpublic static int run([ParamString(PSFormat.CodeFile)] string script, params string[] args)\n\t\t=> _Run(0, script, args, out _);\n\t\n\t/// <summary>\n\t/// Starts executing a script and waits until the task ends.\n\t/// </summary>\n\t/// <returns>The exit code of the task process. See <see cref=\"Environment.ExitCode\"/>.</returns>\n\t/// <exception cref=\"FileNotFoundException\">Script file not found.</exception>\n\t/// <exception cref=\"AuException\">Failed to start script task. For example: the script contains errors; cannot start second task instance; script editor not running.</exception>\n\t/// <inheritdoc cref=\"run\"/>\n\tpublic static int runWait([ParamString(PSFormat.CodeFile)] string script, params string[] args)\n\t\t=> _Run(1, script, args, out _);\n\t\n\t/// <summary>\n\t/// Starts executing a script, waits until the task ends and then gets <see cref=\"writeResult\"/> text.\n\t/// </summary>\n\t/// <param name=\"results\">Receives <see cref=\"writeResult\"/> text.</param>\n\t/// <returns>The exit code of the task process. See <see cref=\"Environment.ExitCode\"/>.</returns>\n\t/// <exception cref=\"FileNotFoundException\">Script file not found.</exception>\n\t/// <exception cref=\"AuException\">Failed to start script task. For example: the script contains errors; cannot start second task instance; script editor not running.</exception>\n\t/// <inheritdoc cref=\"run\"/>\n\tpublic static int runWait(out string results, [ParamString(PSFormat.CodeFile)] string script, params string[] args)\n\t\t=> _Run(3, script, args, out results);\n\t\n\t/// <summary>\n\t/// Starts executing a script, waits until the task ends and gets <see cref=\"writeResult\"/> text in real time.\n\t/// </summary>\n\t/// <param name=\"results\">Receives <see cref=\"writeResult\"/> output whenever the task calls it.</param>\n\t/// <returns>The exit code of the task process. See <see cref=\"Environment.ExitCode\"/>.</returns>\n\t/// <exception cref=\"FileNotFoundException\">Script file not found.</exception>\n\t/// <exception cref=\"AuException\">Failed to start script task. For example: the script contains errors; cannot start second task instance; script editor not running.</exception>\n\t/// <inheritdoc cref=\"run\"/>\n\tpublic static int runWait(Action<string> results, [ParamString(PSFormat.CodeFile)] string script, params string[] args)\n\t\t=> _Run(3, script, args, out _, results);\n\t\n\t//mode flags: 1 - wait, 3 - wait and get script.writeResult output, 4 restarting\n\tstatic int _Run(int mode, string script, string[] args, out string resultS, Action<string> resultA = null) {\n\t\tresultS = null;\n\t\t\n\t\tvar w = ScriptEditor.WndMsg_; if (w.Is0) throw new AuException(\"Editor process not found.\");\n\t\t//CONSIDER: run editor program, if installed\n\t\t\n\t\tbool wait = 0 != (mode & 1), needResult = 0 != (mode & 2);\n\t\tusing var tr = new _TaskResults();\n\t\tif (needResult && !tr.Init()) throw new AuException(\"*get task results\");\n\t\t\n\t\tvar data = Serializer_.Serialize(script, args, tr.pipeName);\n\t\tint pid = (int)WndCopyData.Send<byte>(w, 100, data, mode);\n\t\tif (pid == 0) pid--; //RunResult_.failed\n\t\t\n\t\tswitch ((RunResult_)pid) {\n\t\tcase RunResult_.failed:\n\t\t\treturn !wait ? -1 : throw new AuException(\"*start task\");\n\t\tcase RunResult_.notFound:\n\t\t\tthrow new FileNotFoundException($\"Script '{script}' not found.\");\n\t\tcase RunResult_.deferred: //possible only if !wait\n\t\tcase RunResult_.editorThread: //the script ran sync and already returned\n\t\t\treturn 0;\n\t\t}\n\t\t\n\t\tif (wait) {\n\t\t\tusing var hProcess = WaitHandle_.FromProcessId(pid, Api.SYNCHRONIZE | Api.PROCESS_QUERY_LIMITED_INFORMATION);\n\t\t\tif (hProcess == null) throw new AuException(\"*wait for task\");\n\t\t\t\n\t\t\tif (!needResult) hProcess.WaitOne(-1);\n\t\t\telse if (!tr.WaitAndRead(hProcess, resultA)) throw new AuException(\"*get task result\");\n\t\t\telse if (resultA == null) resultS = tr.ResultString;\n\t\t\t\n\t\t\tif (!Api.GetExitCodeProcess(hProcess.SafeWaitHandle.DangerousGetHandle(), out pid)) pid = int.MinValue;\n\t\t}\n\t\treturn pid;\n\t}\n\t\n\t//Called from editor's CommandLine. Almost same as _Run. Does not throw.\n\tinternal static int RunCL_(wnd w, int mode, string script, string[] args, Action<string> resultA, int schedPid = 0) {\n\t\tbool wait = 0 != (mode & 1), needResult = 0 != (mode & 2);\n\t\tusing var tr = new _TaskResults();\n\t\tif (needResult && !tr.Init()) return (int)RunResult_.cannotGetResult;\n\t\t\n\t\tvar data = Serializer_.Serialize(script, args, tr.pipeName, schedPid);\n\t\tint taskProcessId = (int)WndCopyData.Send<byte>(w, 101, data, mode);\n\t\tif (taskProcessId == 0) taskProcessId--; //RunResult_.failed\n\t\t\n\t\tswitch ((RunResult_)taskProcessId) {\n\t\tcase RunResult_.failed:\n\t\tcase RunResult_.notFound:\n\t\t\treturn taskProcessId;\n\t\tcase RunResult_.deferred: //possible only if !wait\n\t\tcase RunResult_.editorThread: //the script ran sync and already returned. Ignore needResult, as it it auto-detected, not explicitly specified.\n\t\t\treturn 0;\n\t\t}\n\t\t\n\t\tif (wait) {\n\t\t\tusing var hProcess = WaitHandle_.FromProcessId(taskProcessId, Api.SYNCHRONIZE | Api.PROCESS_QUERY_LIMITED_INFORMATION);\n\t\t\tif (hProcess == null) return (int)RunResult_.cannotWait;\n\t\t\t\n\t\t\tif (!needResult) hProcess.WaitOne(-1);\n\t\t\telse if (!tr.WaitAndRead(hProcess, resultA)) return (int)RunResult_.cannotWaitGetResult;\n\t\t\t\n\t\t\tif (!Api.GetExitCodeProcess(hProcess.SafeWaitHandle.DangerousGetHandle(), out taskProcessId)) taskProcessId = int.MinValue;\n\t\t}\n\t\treturn taskProcessId;\n\t}\n\t\n\tinternal enum RunResult_ {\n\t\t//errors returned by sendmessage(wm_copydata)\n\t\tfailed = -1, //script contains errors, or cannot run because of ifRunning, or sendmessage(wm_copydata) failed\n\t\tnotFound = -2, //script not found\n\t\tdeferred = -3, //script cannot run now, but will run later if don't need to wait. If need to wait, in such case cannot be deferred (then failed).\n\t\teditorThread = -4, //role editorExtension\n\t\t\n\t\t//other errors\n\t\tnoEditor = -5,\n\t\tcannotWait = -6,\n\t\tcannotGetResult = -7,\n\t\tcannotWaitGetResult = -8,\n\t}\n\t\n\tunsafe struct _TaskResults : IDisposable {\n\t\tHandle_ _hPipe;\n\t\tpublic string pipeName;\n\t\tstring _s;\n\t\tStringBuilder _sb;\n\t\t\n\t\tpublic bool Init() {\n\t\t\tvar tid = Api.GetCurrentThreadId();\n\t\t\tpipeName = @\"\\\\.\\pipe\\Au.CL-\" + tid.ToString(); //will send this string to the task\n\t\t\t_hPipe = Api.CreateNamedPipe(pipeName,\n\t\t\t\tApi.PIPE_ACCESS_INBOUND | Api.FILE_FLAG_OVERLAPPED, //use async pipe because also need to wait for task process exit\n\t\t\t\tApi.PIPE_TYPE_MESSAGE | Api.PIPE_READMODE_MESSAGE | Api.PIPE_REJECT_REMOTE_CLIENTS,\n\t\t\t\t1, 0, 0, 0, Api.SECURITY_ATTRIBUTES.ForPipes);\n\t\t\treturn !_hPipe.Is0;\n\t\t}\n\t\t\n\t\tpublic bool WaitAndRead(WaitHandle hProcess, Action<string> results) {\n\t\t\tbool R = false;\n\t\t\tchar* b = null; const int bLen = 7900;\n\t\t\tvar ev = new ManualResetEvent(false);\n\t\t\ttry {\n\t\t\t\tvar ha = new WaitHandle[2] { ev, hProcess };\n\t\t\t\tfor (bool useSB = false; ; useSB = results == null) {\n\t\t\t\t\tvar o = new Api.OVERLAPPED { hEvent = ev.SafeWaitHandle.DangerousGetHandle() };\n\t\t\t\t\tif (!Api.ConnectNamedPipe(_hPipe, &o)) {\n\t\t\t\t\t\tint e = lastError.code;\n\t\t\t\t\t\tif (e != Api.ERROR_PIPE_CONNECTED) {\n\t\t\t\t\t\t\tif (e != Api.ERROR_IO_PENDING) break;\n\t\t\t\t\t\t\tint wr = WaitHandle.WaitAny(ha);\n\t\t\t\t\t\t\tif (wr != 0) { Api.CancelIo(_hPipe); R = true; break; } //task ended\n\t\t\t\t\t\t\tif (!Api.GetOverlappedResult(_hPipe, ref o, out _, false)) { Api.DisconnectNamedPipe(_hPipe); break; }\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tif (b == null) b = (char*)MemoryUtil.Alloc(bLen);\n\t\t\t\t\tbool readOK;\n\t\t\t\t\twhile (((readOK = Api.ReadFile(_hPipe, b, bLen, out int n, null)) || (lastError.code == Api.ERROR_MORE_DATA)) && n > 0) {\n\t\t\t\t\t\tn /= 2;\n\t\t\t\t\t\tif (!readOK) useSB = true;\n\t\t\t\t\t\tif (useSB) { //rare\n\t\t\t\t\t\t\t_sb ??= new StringBuilder(bLen);\n\t\t\t\t\t\t\tif (results == null && _s != null) _sb.Append(_s);\n\t\t\t\t\t\t\t_s = null;\n\t\t\t\t\t\t\t_sb.Append(b, n);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t_s = new string(b, 0, n);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (readOK) {\n\t\t\t\t\t\t\tif (results != null) {\n\t\t\t\t\t\t\t\tresults(ResultString);\n\t\t\t\t\t\t\t\t_sb?.Clear();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t//note: MSDN says must use OVERLAPPED with ReadFile too, but works without it.\n\t\t\t\t\t}\n\t\t\t\t\tApi.DisconnectNamedPipe(_hPipe);\n\t\t\t\t\tif (!readOK) break;\n\t\t\t\t}\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tev.Dispose();\n\t\t\t\tMemoryUtil.Free(b);\n\t\t\t}\n\t\t\treturn R;\n\t\t}\n\t\t\n\t\tpublic string ResultString => _s ?? _sb?.ToString();\n\t\t\n\t\tpublic void Dispose() => _hPipe.Dispose();\n\t};\n\t\n\t/// <summary>\n\t/// Writes a string result for the task that called <see cref=\"runWait(out string, string, string[])\"/> or <see cref=\"runWait(Action{string}, string, string[])\"/> to run this task, or for the program that started this task using command line like <c>\"Au.Editor.exe *Script5.cs\"</c>.\n\t/// </summary>\n\t/// <returns><c>false</c> if this task was not started in such a way. Or if failed to write, except when <i>s</i> is <c>null</c>/<c>\"\"</c>.</returns>\n\t/// <param name=\"s\">A string. This function does not append newline characters.</param>\n\t/// <remarks>\n\t/// <see cref=\"runWait(Action{string}, string, string[])\"/> can read the string in real time.\n\t/// <see cref=\"runWait(out string, string, string[])\"/> gets all strings joined when the task ends.\n\t/// The program that started this task using command line like <c>\"Au.Editor.exe *Script5.cs\"</c> can read the string from the redirected standard output in real time, or the string is displayed to its console in real time. The string encoding is UTF-8; if you use a <c>.bat</c> file or <c>cmd.exe</c> and want to get correct Unicode text, execute this before, to change console code page to UTF-8: <c>chcp 65001</c>.\n\t/// \n\t/// Does not work if script role is <c>editorExtension</c>.\n\t/// </remarks>\n#if true\n\tpublic static unsafe bool writeResult(string s) {\n\t\tif (s_wrPipeName == null) return false;\n\t\tif (s.NE()) return true;\n\t\tif (Api.WaitNamedPipe(s_wrPipeName, 3000)) { //15 mcs\n\t\t\tusing var pipe = Api.CreateFile(s_wrPipeName, Api.GENERIC_WRITE, 0, Api.OPEN_EXISTING, 0); //7 mcs\n\t\t\tif (!pipe.Is0) {\n\t\t\t\tfixed (char* p = s) if (Api.WriteFile(pipe, p, s.Length * 2, out _)) return true; //17 mcs\n\t\t\t}\n\t\t}\n\t\tDebug_.PrintNativeError();\n\t\treturn false;\n\t\t//TODO3: optimize. Eg the app may override TextWriter.Write(char) and call this on each char in a string etc.\n\t\t//\tNow 40 mcs. Console.Write(char) 20 mcs.\n\t}\n\tinternal static string s_wrPipeName;\n#else //does not work\n\tpublic static unsafe bool writeResult(string s) {\n\t\tif (s_wrPipeName == null) return false;\n\t\tif (s.NE()) return true;\n\t\tif (s_wrPipe.Is0) {\n\t\t\tif (Api.WaitNamedPipe(s_wrPipeName, 3000)) { //15 mcs\n\t\t\t\tlock (s_wrPipeName) {\n\t\t\t\t\tif (s_wrPipe.Is0) {\n\t\t\t\t\t\ts_wrPipe = Api.CreateFile(s_wrPipeName, Api.GENERIC_WRITE, 0, default, Api.OPEN_EXISTING, 0);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tDebug_.PrintNativeError_(s_wrPipe.Is0);\n\t\t}\n\t\tif (!s_wrPipe.Is0) {\n\t\t\tfixed (char* p = s)\n\t\t\t\tif (Api.WriteFile(s_wrPipe, p, s.Length * 2, out _)) return true; //17 mcs\n\t\t\t\telse Debug_.PrintNativeError_(); //No process is on the other end of the pipe (0xE9)\n\t\t}\n\t\treturn false;\n\t}\n\tstatic string s_wrPipeName;\n\tstatic Handle_ s_wrPipe;\n#endif\n\t\n\t/// <summary>\n\t/// Starts this script or program again.\n\t/// </summary>\n\t/// <param name=\"args\">Command line arguments. Should not contain <c>'\\0'</c> characters.</param>\n\t/// <returns>\n\t/// Native process id of the new process. Returns -1 if failed.\n\t/// </returns>\n\t/// <exception cref=\"FileNotFoundException\">Script file not found.</exception>\n\t/// <exception cref=\"InvalidOperationException\">This script has role <c>editorExtension</c>.</exception>\n\t/// <remarks>\n\t///\tDoes not end this process. The new process runs simultaneously, like with <c>/*/ ifRunning run; /*/</c>. Let this process exit as it wants, for example return from the main script code.\n\t///\n\t/// If this process was started by LibreAutomate, the new process will be started by LibreAutomate too. Else this function simply starts a new instance of this program.\n\t/// </remarks>\n\tpublic static int restart(params string[] args) {\n\t\tif (s_idMainFile != 0) return _Run(4, $\":{s_idMainFile}\", args, out _);\n\t\tif (role != SRole.ExeProgram) throw new InvalidOperationException(); //editorExtension\n\t\t\n\t\tvar ps = new ProcessStarter_(process.thisExePath, StringUtil.CommandLineFromArray(args), null, rawExe: true);\n\t\ttry { return ps.Start(inheritUiaccess: true).pid; }\n\t\tcatch (Exception e1) { print.warning(e1); return -1; }\n\t}\n\t\n\tinternal static uint s_idMainFile;\n\t\n\t/// <summary>\n\t/// Starts executing a script in child session running in picture-in-picture (PiP) window. Does not wait.\n\t/// </summary>\n\t/// <param name=\"script\">Script name like <c>\"Script5.cs\"</c>, or path like <c>@\"\\Folder\\Script5.cs\"</c>.</param>\n\t/// <param name=\"args\">Command line arguments. In the script it will be variable <i>args</i>. Should not contain <c>'\\0'</c> characters.</param>\n\t/// <exception cref=\"FileNotFoundException\">Script file not found.</exception>\n\t/// <exception cref=\"AuException\">Script editor not running (in this session). No exception if cannot run the script in PiP session.</exception>\n\tpublic static void runInPip([ParamString(PSFormat.CodeFile)] string script, params string[] args) {\n\t\tvar w = ScriptEditor.WndMsg_; if (w.Is0) throw new AuException(\"Editor process not found.\");\n\t\t//CONSIDER: run editor program, if installed\n\t\t\n\t\tvar data = Serializer_.Serialize(script, args);\n\t\tRunResult_ r = (RunResult_)WndCopyData.Send<byte>(w, 102, data);\n\t\t\n\t\tswitch (r) {\n\t\t//case RunResult_.failed:\n\t\t//\tthrow new AuException(\"*start task in PiP\");\n\t\tcase RunResult_.notFound:\n\t\t\tthrow new FileNotFoundException($\"Script '{script}' not found.\");\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Returns true if this process is running in a child session (aka Picture-in-Picture).\n\t/// This function is an alias of <see cref=\"miscInfo.isChildSession\"/>.\n\t/// </summary>\n\tpublic static bool isInPip => miscInfo.isChildSession;\n\t\n\t#endregion\n\t\n\t#region end\n\t\n\t/// <summary>\n\t/// Ends this process.\n\t/// </summary>\n\t/// <remarks>\n\t/// Calls <see cref=\"Environment.Exit\"/>.\n\t/// \n\t/// It executes process exit event handlers. Does not execute <c>finally</c> code blocks. Does not execute GC.\n\t/// </remarks>\n\tpublic static void end() {\n\t\tEnvironment.Exit(0);\n\t}\n\t\n\t/// <summary>\n\t/// Ends another script process.\n\t/// </summary>\n\t/// <param name=\"processId\">Script process id, for example returned by <see cref=\"script.run\"/>.</param>\n\t/// <returns><c>true</c> if ended, <c>false</c> if failed, <c>null</c> if wasn't running.</returns>\n\t/// <exception cref=\"ArgumentException\"><i>processId</i> is 0 or id of this process.</exception>\n\t/// <remarks>\n\t/// The script process can be started from editor or not.\n\t/// \n\t/// The process executes process exit event handlers. Does not execute <c>finally</c> code blocks and GC.\n\t/// \n\t/// Returns <c>null</c> if <i>processId</i> is invalid (probably because the script is already ended). Returns <c>false</c> if <i>processId</i> is valid but not of a script process (probably the script ended long time ago and the id is reused for another process).\n\t/// </remarks>\n\tpublic static bool? end(int processId) {\n\t\tif (processId == 0 || processId == Api.GetCurrentProcessId()) throw new ArgumentException();\n\t\t\n\t\tusing var h = Handle_.OpenProcess(processId, Api.SYNCHRONIZE | Api.PROCESS_TERMINATE); //tested: UAC OK\n\t\tif (h.Is0) {\n\t\t\tif (lastError.code == Api.ERROR_INVALID_PARAMETER) return null;\n\t\t\treturn false;\n\t\t}\n\t\tif (Api.WaitForSingleObject(h, 0) == 0) return null;\n\t\t\n\t\tvar w = wait.until(-1d, () => wnd.findFast(processId.ToS(), c_auxWndClassName, messageOnly: true));\n\t\tif (w.Is0) return 0 == Api.WaitForSingleObject(h, 1000); //don't terminate, maybe it's not a script process\n\t\tw.Post(Api.WM_CLOSE);\n\t\tif (0 == Api.WaitForSingleObject(h, 1000)) return true;\n\t\t\n\t\tif (!Api.TerminateProcess(h, -1))\n\t\t\treturn 0 == Api.WaitForSingleObject(h, 500); //TerminateProcess ERROR_ACCESS_DENIED when the process is ending\n\t\tApi.WaitForSingleObject(h, 500); //TerminateProcess is async. Usually the process ends after several ms.\n\t\treturn true;\n\t}\n\t\n\t/// <summary>\n\t/// Ends all task processes of a script.\n\t/// </summary>\n\t/// <param name=\"name\">\n\t/// Script file name (like <c>\"Script43.cs\"</c>) or path in workspace (like <c>@\"\\Folder\\Script43.cs\"</c>), or full file path.\n\t/// If <c>\"\"</c>, ends all other processes of this script.\n\t/// </param>\n\t/// <returns><c>true</c> if ended, <c>false</c> if failed (probably file not found), <c>null</c> if wasn't running.</returns>\n\t/// <exception cref=\"AuException\">Editor process not found.</exception>\n\t/// <remarks>\n\t/// Can end only script processes started from the editor.\n\t/// \n\t/// The process executes process exit event handlers. Does not execute <c>finally</c> code blocks and GC.\n\t/// </remarks>\n\tpublic static bool? end([ParamString(PSFormat.CodeFile)] string name) {\n\t\tvar w = ScriptEditor.WndMsg_; if (w.Is0) throw new AuException(\"Editor process not found.\");\n\t\tint exceptPid = 0; if (name == \"\") (name, exceptPid) = ($\":{s_idMainFile}\", Api.GetCurrentProcessId());\n\t\tint r = (int)WndCopyData.Send<char>(w, 5, name, exceptPid);\n\t\treturn r == 1 ? true : r == 2 ? null : false;\n\t}\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if the specified script task is running.\n\t/// </summary>\n\t/// <param name=\"name\">Script file name (like <c>\"Script43.cs\"</c>) or path in workspace (like <c>@\"\\Folder\\Script43.cs\"</c>), or full file path.</param>\n\tpublic static bool isRunning([ParamString(PSFormat.CodeFile)] string name) {\n\t\tvar w = ScriptEditor.WndMsg_; if (w.Is0) return false;\n\t\treturn 0 != WndCopyData.Send<char>(w, 6, name);\n\t}\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if the specified script task is running.\n\t/// </summary>\n\t/// <param name=\"processId\">Script process id, for example returned by <see cref=\"script.run\"/>.</param>\n\t/// <exception cref=\"ArgumentException\"><i>processId</i> is 0 or id of this process.</exception>\n\t/// <remarks>\n\t/// The script process can be started from editor or not.\n\t/// </remarks>\n\tpublic static bool isRunning(int processId) {\n\t\tif (processId == 0 || processId == Api.GetCurrentProcessId()) throw new ArgumentException();\n\t\t\n\t\tusing var h = Handle_.OpenProcess(processId, Api.SYNCHRONIZE);\n\t\tif (h.Is0 || Api.WaitForSingleObject(h, 0) == 0) return false;\n\t\t\n\t\tvar w1 = wait.until(-0.5, () => wnd.findFast(processId.ToS(), script.c_auxWndClassName, messageOnly: true));\n\t\treturn !w1.Is0;\n\t}\n\t\n\t#endregion\n\t\n\t#region debug, pause\n\t\n\t/// <summary>\n\t/// Attaches the LibreAutomate's debugger to this process, or waits for a debugger attached to this process.\n\t/// Does nothing if a debugger is already attached.\n\t/// </summary>\n\t/// <param name=\"showDialog\">Show dialog with process name and id. If <c>false</c>, attaches the LA debugger.</param>\n\t/// <remarks>\n\t/// When debugger is attached, this function returns and the script continues to run. The step mode begins when the script encounters one of:\n\t/// - breakpoint (set in the debugger's IDE).\n\t/// - exception.\n\t/// - clicked <b>Pause</b> button in IDE.\n\t/// - <see cref=\"Debugger.Break\"/>, <see cref=\"Debug.Assert(bool)\"/> etc.\n\t/// \n\t/// If <i>showDialog</i> is <c>false</c> and LibreAutomate is running, attaches the LA debugger. Cannot attach if it's busy (debugging).\n\t/// \n\t/// Some other programs that have a .NET debugger:\n\t/// - Visual Studio. It's the best, but huge (~10 GB). The community edition is free. Use menu <b>Debug > Attach to process</b>.\n\t/// - Visual Studio Code. It's much smaller. Free.\n\t/// - JetBrains Rider.\n\t/// \n\t/// <note>If the script process is running as administrator, the debugger process must run as administrator too.</note>\n\t/// \n\t/// <note>When attaching an external debugger (Visual Studio etc), make sure it debugs .NET code, not native code etc.</note>\n\t/// \n\t/// See also [](xref:debugger).\n\t/// </remarks>\n\t[DebuggerStepThrough]\n\tpublic static void debug(bool showDialog = false) {\n\t\tif (Debugger.IsAttached) return;\n\t\tif (!showDialog && ScriptEditor.WndMsg_ is var w && !w.Is0) {\n\t\t\tif (0 != w.Send(Api.WM_USER, 30, process.thisProcessId)) {\n\t\t\t\tif (wait.until(-30, () => Debugger.IsAttached)) return;\n\t\t\t}\n\t\t\tend();\n\t\t}\n\t\tvar d = new dialog(\"Waiting for debugger to attach\", $\"{script.name}\\nProcess: {process.thisExeName}  {process.thisProcessId}\", title: \"Attach debugger\");\n\t\td.InScreen(screen.ofMouse);\n\t\td.ShowDialogNoWait();\n\t\twait.until(0, () => Debugger.IsAttached);\n\t\td.Send.Close();\n\t\t\n\t\t//note: don't add Debugger.Break(); in this func. It creates problems.\n\t}\n\t\n\t/// <summary>\n\t/// If was pressed the pause key, waits until the user presses it again.\n\t/// </summary>\n\t/// <param name=\"text\">Text to display in the \"Paused script\" UI.</param>\n\t/// <param name=\"doEvents\">Process Windows messages and other events while waiting. For example, windows of this thread can respond, and timers of this thread can run.</param>\n\t/// <remarks>\n\t/// The default pause key is <c>ScrollLock</c> (<c>Fn+S</c>, <c>Fn+K</c> or similar). To change, use <see cref=\"setup\"/> parameter <i>pauseKey</i>. If <c>script.setup</c> not called, this function uses <c>ScrollLock</c> but does not pause when called the first time.\n\t///\n\t/// A script can be paused only if it calls this function. Pausing at a random place would be dangerous and is not supported. Call this function in places where it is safe to pause, and where it makes sense, for example in a loop that preses keys or mouse buttons. To pause/resume, let the user press the pause key.\n\t///\n\t/// If the pause key is <c>CapsLock</c>, waits if it is toggled, even if was toggled when this script started.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// script.setup(trayIcon: true, pauseKey: KKey.MediaPlayPause);\n\t/// using var t = osdText.showTransparentText(\"         \", -1);\n\t/// for (int i = 0; i < 1000; i++) {\n\t/// \tscript.pause();\n\t/// \t//script.pause(\"Next: continue the loop.\");\n\t/// \tt.Text = i.ToS();\n\t/// \t250.ms();\n\t/// }\n\t/// ]]></code>\n\t/// </example>\n\tpublic static void pause(string text = null, bool doEvents = false) {\n\t\tif (!s_pauseSetupDone) { //script.setup not called\n\t\t\ts_pauseSetupDone = true;\n\t\t\ts_pauseWhenUntoggled = keys.gui.isToggled(s_pauseKey = KKey.ScrollLock);\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tif (paused) {\n\t\t\tvar s = $\"Paused: {script.name}.\";\n\t\t\tif (s_pauseKey != 0) s += $\"\\nKey: {s_pauseKey}.\";\n\t\t\tif (!text.NE()) s += $\"\\n{text}\";\n\t\t\tusing var icon = ImageUtil.LoadGdipBitmapFromXaml(\"<Viewbox Width='32' Height='32' xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'><Path Data='M13,16V8H15V16H13M9,16V8H11V16H9M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4Z' Stretch='Uniform' Fill='#FFD631' UseLayoutRounding='False' SnapsToDevicePixels='False' /></Viewbox>\", screen.primary.Dpi);\n\t\t\tusing (osdText.showText(s, -1, new(y: ^10), icon, 0xffffff, 0x444444, showMode: OsdMode.WeakThread)) {\n\t\t\t\twait.until(new(0) { Period = 2, DoEvents = doEvents }, () => !paused);\n\t\t\t}\n\t\t}\n\t}\n\t//FUTURE: UI to end task when paused.\n\t//CONSIDER: add option to save-restore mouse xy, active window, its state.\n\t//CONSIDER: auto call pause in key/mouse/etc functions if there are no pressed modifier keys and mouse buttons.\n\t\n\t/// <summary>\n\t/// If <c>true</c>, next call to <see cref=\"pause\"/> will wait until <c>false</c>, or already is waiting.\n\t/// </summary>\n\tpublic static bool paused {\n\t\tget => s_paused || (_PauseIsLockKey && keys.gui.isToggled(s_pauseKey) != s_pauseWhenUntoggled);\n\t\tset { s_paused = value; }\n\t}\n\t\n\t//in aux thread\n\tstatic void _PauseSetKey() {\n\t\tif (_PauseIsLockKey) {\n\t\t\ts_pauseWhenUntoggled = keys.gui.isToggled(s_pauseKey);\n\t\t} else {\n\t\t\tif (!_RegisterKey(s_pauseKey, 0)) {\n\t\t\t\t//print.warning(\"script.setup failed to register the pause key. Will use ScrollLock.\", -1);\n\t\t\t\t//s_pauseWhenUntoggled = keys.gui.isToggled(s_pauseKey = KKey.ScrollLock);\n\t\t\t\t\n\t\t\t\tlong i1 = 0;\n\t\t\t\ttimer.every(500, t => {\n\t\t\t\t\tif (_RegisterKey(s_pauseKey, 0)) t.Stop();\n\t\t\t\t\telse if (++i1 == 4) print.warning($\"{name}: script.setup failed to register the pause key. Will retry.\", -1);\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\tstatic KKey s_pauseKey;\n\tstatic bool s_paused, s_pauseSetupDone, s_pauseWhenUntoggled;\n\t\n\tstatic bool _PauseIsLockKey => s_pauseKey is KKey.ScrollLock or KKey.CapsLock or KKey.NumLock;\n\t\n\t#endregion\n\t\n\t#region util\n\t\n\tstatic void _SleepLockExit(bool sleep) {\n\t\tprint.it($\"<>Info: task <open {sourcePath(true)}|||script.setup>{name}<> ended because of {(sleep ? \"PC sleep\" : \"switched desktop\")} at {DateTime.Now.ToShortTimeString()}.\");\n\t\tTask.Run(() => Environment.Exit(2));\n\t\t//why Task.Run: with RegisterSuspendResumeNotification does not work well in same thread.\n\t}\n\t\n\tstatic bool _RegisterKey(KKey key, int idBase) {\n\t\treturn Api.RegisterHotKey(s_auxWnd, idBase, Api.MOD_NOREPEAT, key);\n\t\t\n\t\t//rejected: try to register all mod combinations. Else does not work if the script pressed a mod key at that time.\n\t\t//\t\tfor (int i = 0; i < 16; i++) {\n\t\t//\t\t\tvar k = key;\n\t\t//\t\t\tif (k == KKey.Pause && 0 != (i & Api.MOD_CONTROL)) k = KKey.Break; //Ctrl+Pause = Break\n\t\t//\t\t\tif (!Api.RegisterHotKey(s_auxWnd, idBase + i, (uint)i | Api.MOD_NOREPEAT, k)) {\n\t\t//#if !true\n\t\t//\t\t\t\t//print.it(i, k, s_auxWnd, lastError.message);\n\t\t//\t\t\t\tif (k == KKey.Pause && i == 8) continue; //Win+Pause opens something in Windows Settings\n\t\t//\t\t\t\twhile (--i >= 0) Api.UnregisterHotKey(s_auxWnd, idBase + i);\n\t\t//\t\t\t\treturn false;\n\t\t//\t\t\t\t//any failed to register hotkey would be dangerous, because the user-pressed key combined with script-pressed modifiers would invoke the hotkey in its owner app\n\t\t\n\t\t//\t\t\t\t//Not good. Many keys fail because a hotkey with some modifiers is registered. Eg Esc, arrows, Home. Many are registered by OS with mod Win.\n\t\t//\t\t\t\t//\tMaybe instead just print warning \"Failed to register hotkey X. It can be dangerous... Consider using media keys etc instead.\". Fail only if cannot register the key without modifiers.\n\t\t//\t\t\t\t//\tOr use LL hook. Then can detect script-pressed keys. But no, it's too heavy; maybe every script will have script.setup with that key.\n\t\t//#else\n\t\t//\t\t\t\tif (i == 0) return false;\n\t\t//#endif\n\t\t//\t\t\t}\n\t\t//\t\t}\n\t\t//\t\treturn true;\n\t}\n\t\n\t#endregion\n}\n"
  },
  {
    "path": "Au/Resources/AssemblyInfo.cs",
    "content": "\n[assembly: AssemblyTitle(\"Au\")]\n[assembly: AssemblyDescription(\"\")]\n[assembly: AssemblyConfiguration(\"\")]\n//more in global2.cs\n\n// The following GUID is for the ID of the typelib if this project is exposed to COM\n[assembly: Guid(\"d3087fac-a12d-4365-a620-7574cd89b17f\")]\n\n// Version information for an assembly consists of the following four values:\n//\n//      Major Version\n//      Minor Version \n//      Build Number\n//      Revision\n//\n// You can specify all the values or you can default the Build and Revision Numbers \n// by using the '*' as shown below:\n// [assembly: AssemblyVersion(\"1.0.*\")]\n//[assembly: AssemblyVersion(\"1.0.0.*\")]\n//[assembly: AssemblyVersion(\"1.0.1.0\")] //moved to global2.cs\n//rejected: auto increment.\n//\tCreates more problems and work than is useful. Eg after modifying this project always need to rebuild all exe projects, else fails to load this dll.\n//\tVS adds 20-300 to the revision at each build. Why not 1? Now ~ 23000. What happens when it becomes the max possible 0xffff?\n//\tVS does not auto-increment the build number when \"1.0.*\". Now 7288. Where it gets these values? And does not reset them when I change eg minor.\n\n//[assembly: AssemblyFileVersion(\"1.0.0.0\")]\n\n//This can be used eg to find public/protected _Identifier.\n//However most warnings are about uint. We disable them in project Properties: 3001,3002,3003,3009.\n//[assembly: CLSCompliant(true)] //rejected. It isn't.\n\n#if IDE_LA\n[assembly: InternalsVisibleTo(\"Au.Editor\")]\n[assembly: InternalsVisibleTo(\"Au.Controls\")]\n#else\n[assembly: InternalsVisibleTo(\"Au.Editor, PublicKey=0024000004800000940000000602000000240000525341310004000001000100095c6b7a0fe60fbe4a77e52dd10a09331ee3c3a7399aa9cc17db8a015647469a19784d5e33a2450a0a49c37bf17c0c3223674f64104eae649ba27c51a90c24989faec87d59217d7850efc8151109bbf9b027b7714fc01788317d2b991b2c2669836a7725e942f76607efde5cdacd8c497a45c5f9673fcf102fdbf92237a524a4\")]\n[assembly: InternalsVisibleTo(\"Au.Controls, PublicKey=0024000004800000940000000602000000240000525341310004000001000100095c6b7a0fe60fbe4a77e52dd10a09331ee3c3a7399aa9cc17db8a015647469a19784d5e33a2450a0a49c37bf17c0c3223674f64104eae649ba27c51a90c24989faec87d59217d7850efc8151109bbf9b027b7714fc01788317d2b991b2c2669836a7725e942f76607efde5cdacd8c497a45c5f9673fcf102fdbf92237a524a4\")]\n#endif\n\n[assembly: AssemblyMetadata(\"RepositoryUrl\", \"https://github.com/qgindi/LibreAutomate\")]\n"
  },
  {
    "path": "Au/String/ExtString.cs",
    "content": "//FUTURE: now in intellisense RStr methods are mixed with that of string. Many of them are not useful. Maybe .NET 10 or later will add an attribute to clean it up.\n\nusing System.Buffers;\n\nnamespace Au.Types;\n\n/// <summary>\n/// Adds extension methods for <see cref=\"String\"/>.\n/// </summary>\n/// <remarks>\n/// Some .NET <see cref=\"String\"/> methods use <see cref=\"StringComparison.CurrentCulture\"/> by default, while others use ordinal or invariant comparison. It is confusing (difficult to remember), dangerous (easy to make bugs), slower and rarely useful.\n/// Microsoft recommends to specify <see cref=\"StringComparison.Ordinal\"/> <see cref=\"StringComparison.OrdinalIgnoreCase\"/>. See <google>Best practices for comparing strings in .NET</google>.\n/// This class adds ordinal comparison versions of these methods. Same or similar name, for example <c>Ends</c> for <c>EndsWith</c>.\n/// See also <see cref=\"process.thisProcessCultureIsInvariant\"/>.\n/// \n/// This class also adds more methods.\n/// You also can find string functions in other classes of this library, including <see cref=\"StringUtil\"/>, <see cref=\"regexp\"/>, <see cref=\"pathname\"/>, <see cref=\"csvTable\"/>, <see cref=\"keys.more\"/>, <see cref=\"Convert2\"/>, <see cref=\"Hash\"/>.\n/// </remarks>\npublic static unsafe partial class ExtString {\n\t/// <summary>\n\t/// Compares this and other string. Returns <c>true</c> if equal.\n\t/// </summary>\n\t/// <param name=\"t\">This string. Can be <c>null</c>.</param>\n\t/// <param name=\"s\">Other string. Can be <c>null</c>.</param>\n\t/// <param name=\"ignoreCase\">Case-insensitive.</param>\n\t/// <remarks>\n\t/// Uses ordinal comparison (does not depend on current culture/locale).\n\t/// </remarks>\n\t/// <seealso cref=\"Eq(RStr, RStr)\"/>\n\t/// <seealso cref=\"Eqi(RStr, RStr)\"/>\n\t/// <seealso cref=\"string.Compare\"/>\n\t/// <seealso cref=\"string.CompareOrdinal\"/>\n\tpublic static bool Eq(this string t, string s, bool ignoreCase = false) {\n\t\treturn ignoreCase ? string.Equals(t, s, StringComparison.OrdinalIgnoreCase) : string.Equals(t, s);\n\t}\n\n\t/// <summary>\n\t/// Compares this strings with multiple strings.\n\t/// Returns 1-based index of the matching string, or 0 if none.\n\t/// </summary>\n\t/// <param name=\"t\">This string. Can be <c>null</c>.</param>\n\t/// <param name=\"ignoreCase\">Case-insensitive.</param>\n\t/// <param name=\"strings\">Other strings. Strings can be <c>null</c>.</param>\n\t/// <remarks>\n\t/// Uses ordinal comparison (does not depend on current culture/locale).\n\t/// </remarks>\n\tpublic static int Eq(this string t, bool ignoreCase, params ReadOnlySpan<string> strings) {\n\t\tfor (int i = 0; i < strings.Length; i++) if (Eq(t, strings[i], ignoreCase)) return i + 1;\n\t\treturn 0;\n\t}\n\n\t/// <summary>\n\t/// Compares part of this string with other string. Returns <c>true</c> if equal.\n\t/// </summary>\n\t/// <param name=\"t\">This string.</param>\n\t/// <param name=\"startIndex\">Offset in this string. If invalid, returns <c>false</c>.</param>\n\t/// <param name=\"s\">Other string.</param>\n\t/// <param name=\"ignoreCase\">Case-insensitive.</param>\n\t/// <exception cref=\"ArgumentNullException\"><i>s</i> is <c>null</c>.</exception>\n\t/// <remarks>\n\t/// Uses ordinal comparison (does not depend on current culture/locale).\n\t/// </remarks>\n\t/// <seealso cref=\"Eq(RStr, int, RStr, bool)\"/>\n\t/// <seealso cref=\"string.Compare\"/>\n\t/// <seealso cref=\"string.CompareOrdinal\"/>\n\tpublic static bool Eq(this string t, int startIndex, RStr s, bool ignoreCase = false) {\n\t\tint nt = t.Length, ns = s.LengthThrowIfNull_();\n\t\tif ((uint)startIndex > nt || ns > nt - startIndex) return false;\n\t\tvar span = t.AsSpan(startIndex, ns);\n\t\tif (!ignoreCase) return span.SequenceEqual(s);\n\t\treturn span.Equals(s, StringComparison.OrdinalIgnoreCase);\n\t\t//Faster than string.Compare[Ordinal].\n\t\t//With Tables_.LowerCase similar speed. Depends on whether match. \n\t}\n\n\t/// <summary>\n\t/// Compares part of this string with multiple strings.\n\t/// Returns 1-based index of the matching string, or 0 if none.\n\t/// </summary>\n\t/// <param name=\"t\">This string.</param>\n\t/// <param name=\"startIndex\">Offset in this string. If invalid, returns <c>false</c>.</param>\n\t/// <param name=\"ignoreCase\">Case-insensitive.</param>\n\t/// <param name=\"strings\">Other strings.</param>\n\t/// <exception cref=\"ArgumentNullException\">A string in <i>strings</i> is <c>null</c>.</exception>\n\t/// <remarks>\n\t/// Uses ordinal comparison (does not depend on current culture/locale).\n\t/// </remarks>\n\tpublic static int Eq(this string t, int startIndex, bool ignoreCase = false, params ReadOnlySpan<string> strings) {\n\t\tfor (int i = 0; i < strings.Length; i++) if (t.Eq(startIndex, strings[i], ignoreCase)) return i + 1;\n\t\treturn 0;\n\t}\n\n\t/// <summary>\n\t/// Compares part of this string with other string. Returns <c>true</c> if equal.\n\t/// </summary>\n\t/// <param name=\"t\">This string.</param>\n\t/// <param name=\"range\">Range of this string. Can return <c>true</c> only if its length <c>== s.Length</c>. If invalid, returns <c>false</c>.</param>\n\t/// <param name=\"s\">Other string.</param>\n\t/// <param name=\"ignoreCase\">Case-insensitive.</param>\n\t/// <exception cref=\"ArgumentNullException\"><i>s</i> is <c>null</c>.</exception>\n\t/// <remarks>\n\t/// Uses ordinal comparison (does not depend on current culture/locale).\n\t/// </remarks>\n\t/// <seealso cref=\"Eq(RStr, RStr)\"/>\n\t/// <seealso cref=\"Eqi(RStr, RStr)\"/>\n\t/// <seealso cref=\"string.Compare\"/>\n\t/// <seealso cref=\"string.CompareOrdinal\"/>\n\tpublic static bool Eq(this string t, Range range, RStr s, bool ignoreCase = false) {\n\t\tint nt = t.Length, ns = s.LengthThrowIfNull_();\n\t\tint i = range.Start.GetOffset(nt), len = range.End.GetOffset(nt) - i;\n\t\treturn ns == len && t.Eq(i, s, ignoreCase);\n\t}\n\n\t/// <summary>\n\t/// Returns <c>true</c> if the specified character is at the specified position in this string.\n\t/// </summary>\n\t/// <param name=\"t\">This string.</param>\n\t/// <param name=\"index\">Offset in this string. If invalid, returns <c>false</c>.</param>\n\t/// <param name=\"c\">Character.</param>\n\tpublic static bool Eq(this string t, int index, char c) {\n\t\tif ((uint)index >= t.Length) return false;\n\t\treturn t[index] == c;\n\t}\n\n\t/// <summary>\n\t/// Compares this and other string ignoring case (case-insensitive). Returns <c>true</c> if equal.\n\t/// </summary>\n\t/// <param name=\"t\">This string. Can be <c>null</c>.</param>\n\t/// <param name=\"s\">Other string. Can be <c>null</c>.</param>\n\t/// <remarks>\n\t/// Uses ordinal comparison (does not depend on current culture/locale).\n\t/// </remarks>\n\tpublic static bool Eqi(this string t, string s) => string.Equals(t, s, StringComparison.OrdinalIgnoreCase);\n\n\t//rejected. Not so often used.\n\t//public static bool Eqi(this string t, int startIndex, string s) => Eq(t, startIndex, s, true);\n\n\t/// <summary>\n\t/// Compares end of this string with other string. Returns <c>true</c> if equal.\n\t/// </summary>\n\t/// <param name=\"t\">This string.</param>\n\t/// <param name=\"s\">Other string.</param>\n\t/// <param name=\"ignoreCase\">Case-insensitive.</param>\n\t/// <exception cref=\"ArgumentNullException\"><i>s</i> is <c>null</c>.</exception>\n\t/// <remarks>\n\t/// Uses ordinal comparison (does not depend on current culture/locale).\n\t/// </remarks>\n\tpublic static bool Ends(this string t, RStr s, bool ignoreCase = false) {\n\t\tint nt = t.Length, ns = s.LengthThrowIfNull_();\n\t\tif (ns > nt) return false;\n\t\tvar span = t.AsSpan(nt - ns);\n\t\tif (!ignoreCase) return span.SequenceEqual(s);\n\t\treturn span.Equals(s, StringComparison.OrdinalIgnoreCase);\n\t\t//faster than EndsWith\n\t}\n\n\t/// <summary>\n\t/// Compares end of this string with multiple strings.\n\t/// Returns 1-based index of the matching string, or 0 if none.\n\t/// </summary>\n\t/// <param name=\"t\">This string.</param>\n\t/// <param name=\"ignoreCase\">Case-insensitive.</param>\n\t/// <param name=\"strings\">Other strings.</param>\n\t/// <exception cref=\"ArgumentNullException\">A string in <i>strings</i> is <c>null</c>.</exception>\n\t/// <remarks>\n\t/// Uses ordinal comparison (does not depend on current culture/locale).\n\t/// </remarks>\n\tpublic static int Ends(this string t, bool ignoreCase, params ReadOnlySpan<string> strings) {\n\t\tfor (int i = 0; i < strings.Length; i++) if (Ends(t, strings[i], ignoreCase)) return i + 1;\n\t\treturn 0;\n\t}\n\n\t/// <summary>\n\t/// Returns <c>true</c> if this string ends with the specified character.\n\t/// </summary>\n\t/// <param name=\"t\">This string.</param>\n\t/// <param name=\"c\">Character.</param>\n\tpublic static bool Ends(this string t, char c) {\n\t\tint i = t.Length - 1;\n\t\treturn i >= 0 && t[i] == c;\n\t}\n\n\t/// <summary>\n\t/// Compares beginning of this string with other string. Returns <c>true</c> if equal.\n\t/// </summary>\n\t/// <param name=\"t\">This string.</param>\n\t/// <param name=\"s\">Other string.</param>\n\t/// <param name=\"ignoreCase\">Case-insensitive.</param>\n\t/// <exception cref=\"ArgumentNullException\"><i>s</i> is <c>null</c>.</exception>\n\t/// <remarks>\n\t/// Uses ordinal comparison (does not depend on current culture/locale).\n\t/// </remarks>\n\tpublic static bool Starts(this string t, RStr s, bool ignoreCase = false) {\n\t\tint nt = t.Length, ns = s.LengthThrowIfNull_();\n\t\tif (ns > nt) return false;\n\t\tvar span = t.AsSpan(0, ns);\n\t\tif (!ignoreCase) return span.SequenceEqual(s);\n\t\treturn span.Equals(s, StringComparison.OrdinalIgnoreCase);\n\t\t//faster than StartsWith\n\t}\n\n\t/// <summary>\n\t/// Compares beginning of this string with multiple strings.\n\t/// Returns 1-based index of the matching string, or 0 if none.\n\t/// </summary>\n\t/// <param name=\"t\">This string.</param>\n\t/// <param name=\"ignoreCase\">Case-insensitive.</param>\n\t/// <param name=\"strings\">Other strings.</param>\n\t/// <exception cref=\"ArgumentNullException\">A string in <i>strings</i> is <c>null</c>.</exception>\n\t/// <remarks>\n\t/// Uses ordinal comparison (does not depend on current culture/locale).\n\t/// </remarks>\n\tpublic static int Starts(this string t, bool ignoreCase, params ReadOnlySpan<string> strings) {\n\t\tfor (int i = 0; i < strings.Length; i++) if (Starts(t, strings[i], ignoreCase)) return i + 1;\n\t\treturn 0;\n\t}\n\n\t/// <summary>\n\t/// Returns <c>true</c> if this string starts with the specified character.\n\t/// </summary>\n\t/// <param name=\"t\">This string.</param>\n\t/// <param name=\"c\">Character.</param>\n\tpublic static bool Starts(this string t, char c) {\n\t\treturn t.Length > 0 && t[0] == c;\n\t}\n\n\t//Speed test results with text of length 5_260_070 and 'find' text \"inheritdoc\":\n\t//IndexOf(Ordinal)\t\t\t\t\t\t\t6 ms (depends on 'find' text; can be much faster if starts with a rare character)\n\t//IndexOf(OrdinalIgnoreCase)\t\t\t\t32 ms\n\t//FindStringOrdinal()\t\t\t\t\t\t8 ms\n\t//FindStringOrdinal(true)\t\t\t\t\t32 ms\n\t//Like(\"*\" + x + \"*\")\t\t\t\t\t\t10 ms\n\t//Like(\"*\" + x + \"*\", true)\t\t\t\t\t12 ms\n\t//RxIsMatch(LITERAL)\t\t\t\t\t\t13 ms\n\t//RxIsMatch(LITERAL|CASELESS)\t\t\t\t19 ms\n\t//Regex.Match(CultureInvariant)\t\t\t\t4 ms (when no regex-special characters or if escaped)\n\t//Regex.Match(CultureInvariant|IgnoreCase)\t9 ms\n\t//Find2(true)\t\t\t\t\t\t\t\t10 ms\n\n\t//Could optimize the case-insensitive Find.\n\t//\tEither use table (like Like), or for very long strings use Regex.\n\t//\tBut maybe then result would be different in some cases, not sure.\n\t//\tHow if contains Unicode surrogates?\n\t//\tBad: slower startup, because need to create table or JIT Regex.\n\t//\tNever mind.\n\n\t//public static int Find2(this string t, string s, bool ignoreCase = false) {\n\t//\tif (!ignoreCase) return t.IndexOf(s, StringComparison.Ordinal);\n\t//\tint n = t.Length - s.Length;\n\t//\tif (n >= 0) {\n\t//\t\tif (s.Length == 0) return 0;\n\t//\t\tvar m = Tables_.LowerCase;\n\t//\t\tchar first = m[s[0]];\n\t//\t\tfor (int i = 0; i <= n; i++) {\n\t//\t\t\tif (m[t[i]] == first) {\n\t//\t\t\t\tint j = 1; while (j < s.Length && m[t[i + j]] == m[s[j]]) j++;\n\t//\t\t\t\tif (j == s.Length) return i;\n\t//\t\t\t}\n\t//\t\t}\n\t//\t}\n\t//\treturn -1;\n\t//}\n\n\t/// <summary>\n\t/// Finds substring in this string. Returns its 0-based index, or -1 if not found.\n\t/// </summary>\n\t/// <param name=\"t\">This string.</param>\n\t/// <param name=\"s\">Substring to find.</param>\n\t/// <param name=\"ignoreCase\">Case-insensitive.</param>\n\t/// <exception cref=\"ArgumentNullException\"><i>s</i> is <c>null</c>.</exception>\n\t/// <remarks>\n\t/// Uses ordinal comparison (does not depend on current culture/locale).\n\t/// </remarks>\n\tpublic static int Find(this string t, string s, bool ignoreCase = false) {\n\t\treturn t.IndexOf(s, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);\n\t}\n\n\t/// <summary>\n\t/// Finds substring in part of this string. Returns its 0-based index, or -1 if not found.\n\t/// </summary>\n\t/// <param name=\"t\">This string.</param>\n\t/// <param name=\"s\">Substring to find.</param>\n\t/// <param name=\"startIndex\">The search start index.</param>\n\t/// <param name=\"ignoreCase\">Case-insensitive.</param>\n\t/// <exception cref=\"ArgumentNullException\"><i>s</i> is <c>null</c>.</exception>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Invalid <i>startIndex</i>.</exception>\n\t/// <remarks>\n\t/// Uses ordinal comparison (does not depend on current culture/locale).\n\t/// </remarks>\n\tpublic static int Find(this string t, string s, int startIndex, bool ignoreCase = false) {\n\t\treturn t.IndexOf(s, startIndex, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);\n\t}\n\n\t/// <summary>\n\t/// Finds substring in part of this string. Returns its 0-based index, or -1 if not found.\n\t/// </summary>\n\t/// <param name=\"t\">This string.</param>\n\t/// <param name=\"s\">Substring to find.</param>\n\t/// <param name=\"range\">The search range.</param>\n\t/// <param name=\"ignoreCase\">Case-insensitive.</param>\n\t/// <exception cref=\"ArgumentNullException\"><i>s</i> is <c>null</c>.</exception>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Invalid <i>range</i>.</exception>\n\t/// <remarks>\n\t/// Uses ordinal comparison (does not depend on current culture/locale).\n\t/// </remarks>\n\tpublic static int Find(this string t, string s, Range range, bool ignoreCase = false) {\n\t\tvar (start, count) = range.GetOffsetAndLength(t.Length);\n\t\treturn t.IndexOf(s, start, count, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);\n\t}\n\n\t//CONSIDER: make public.\n\t/// <summary>\n\t/// Like <see cref=\"Find(string, string, bool)\"/>, but with a predicate.\n\t/// </summary>\n\t/// <param name=\"also\">Called for each found substring until returns <c>true</c>. Receives this string and start and end offsets of the substring.</param>\n\tinternal static int Find_(this string t, string s, Func<string, int, int, bool> also, bool ignoreCase = false) {\n\t\tfor (int i = 0; ; i++) {\n\t\t\ti = t.Find(s, i, ignoreCase);\n\t\t\tif (i < 0) break;\n\t\t\tif (also(t, i, i + s.Length)) return i;\n\t\t}\n\t\treturn -1;\n\t}\n\n\t/// <summary>\n\t/// Finds the first character specified in <i>chars</i>. Returns its index, or -1 if not found.\n\t/// </summary>\n\t/// <param name=\"t\">This string.</param>\n\t/// <param name=\"chars\">Characters.</param>\n\t/// <param name=\"range\">The search range.</param>\n\t/// <exception cref=\"ArgumentNullException\"><i>chars</i> is <c>null</c>.</exception>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Invalid <i>range</i>.</exception>\n\t[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] //functions with Range parameter are very slow until fully optimized\n\tpublic static int FindAny(this string t, string chars, Range? range = null) {\n\t\tvar (start, len) = range.GetOffsetAndLength(t.Length);\n\t\tint r = t.AsSpan(start, len).IndexOfAny(chars);\n\t\treturn r < 0 ? r : r + start;\n\t}\n\n\t/// <summary>\n\t/// Finds the first character not specified in <i>chars</i>. Returns its index, or -1 if not found.\n\t/// </summary>\n\t/// <param name=\"t\">This string.</param>\n\t/// <param name=\"chars\">Characters.</param>\n\t/// <param name=\"range\">The search range.</param>\n\t/// <exception cref=\"ArgumentNullException\"><i>chars</i> is <c>null</c>.</exception>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Invalid <i>range</i>.</exception>\n\t[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]\n\tpublic static int FindNot(this string t, string chars, Range? range = null) {\n\t\tvar (start, len) = range.GetOffsetAndLength(t.Length);\n\t\tint r = t.AsSpan(start, len).IndexOfAnyExcept(chars);\n\t\treturn r < 0 ? r : r + start;\n\t}\n\n\t/// <summary>\n\t/// Finds the last character specified in <i>chars</i> (searches right to left). Returns its index, or -1 if not found.\n\t/// </summary>\n\t/// <param name=\"t\">This string.</param>\n\t/// <param name=\"chars\">Characters.</param>\n\t/// <param name=\"range\">The search range.</param>\n\t/// <exception cref=\"ArgumentNullException\"><i>chars</i> is <c>null</c>.</exception>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Invalid <i>range</i>.</exception>\n\t[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]\n\tpublic static int FindLastAny(this string t, string chars, Range? range = null) {\n\t\tvar (start, len) = range.GetOffsetAndLength(t.Length);\n\t\tint r = t.AsSpan(start, len).LastIndexOfAny(chars);\n\t\treturn r < 0 ? r : r + start;\n\t}\n\n\t/// <summary>\n\t/// Finds the last character not specified in <i>chars</i> (searches right to left). Returns its index, or -1 if not found.\n\t/// </summary>\n\t/// <param name=\"t\">This string.</param>\n\t/// <param name=\"chars\">Characters.</param>\n\t/// <param name=\"range\">The search range.</param>\n\t/// <exception cref=\"ArgumentNullException\"><i>chars</i> is <c>null</c>.</exception>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Invalid <i>range</i>.</exception>\n\t[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]\n\tpublic static int FindLastNot(this string t, string chars, Range? range = null) {\n\t\tvar (start, len) = range.GetOffsetAndLength(t.Length);\n\t\tint r = t.AsSpan(start, len).LastIndexOfAnyExcept(chars);\n\t\treturn r < 0 ? r : r + start;\n\t}\n\n\t/// <summary>\n\t/// Removes specified characters from the start and end of this string.\n\t/// </summary>\n\t/// <returns>The result string.</returns>\n\t/// <param name=\"t\">This string.</param>\n\t/// <param name=\"chars\">Characters to remove.</param>\n\t/// <exception cref=\"ArgumentNullException\"><i>chars</i> is <c>null</c>.</exception>\n\t[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]\n\tpublic static string Trim(this string t, string chars) {\n\t\tvar span = t.AsSpan().Trim(chars);\n\t\treturn span.Length == t.Length ? t : new string(span);\n\t}\n\n\t/// <summary>\n\t/// Removes specified characters from the start of this string.\n\t/// </summary>\n\t/// <returns>The result string.</returns>\n\t/// <param name=\"t\">This string.</param>\n\t/// <param name=\"chars\">Characters to remove.</param>\n\t/// <exception cref=\"ArgumentNullException\"><i>chars</i> is <c>null</c>.</exception>\n\t[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]\n\tpublic static string TrimStart(this string t, string chars) {\n\t\tvar span = t.AsSpan().TrimStart(chars);\n\t\treturn span.Length == t.Length ? t : new string(span);\n\t}\n\n\t/// <summary>\n\t/// Removes specified characters from the end of this string.\n\t/// </summary>\n\t/// <returns>The result string.</returns>\n\t/// <param name=\"t\">This string.</param>\n\t/// <param name=\"chars\">Characters to remove.</param>\n\t/// <exception cref=\"ArgumentNullException\"><i>chars</i> is <c>null</c>.</exception>\n\t[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]\n\tpublic static string TrimEnd(this string t, string chars) {\n\t\tvar span = t.AsSpan().TrimEnd(chars);\n\t\treturn span.Length == t.Length ? t : new string(span);\n\t}\n\n\t/// <summary>\n\t/// Finds whole word. Returns its 0-based index, or -1 if not found.\n\t/// </summary>\n\t/// <param name=\"t\">This string.</param>\n\t/// <param name=\"s\">Substring to find.</param>\n\t/// <param name=\"range\">The search range.</param>\n\t/// <param name=\"ignoreCase\">Case-insensitive.</param>\n\t/// <param name=\"otherWordChars\">Additional word characters. For example <c>\"_\"</c>.</param>\n\t/// <param name=\"isWordChar\">Function that returns <c>true</c> for word characters. If <c>null</c>, uses <see cref=\"char.IsLetterOrDigit\"/>.</param>\n\t/// <exception cref=\"ArgumentNullException\"><i>s</i> is <c>null</c>.</exception>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Invalid <i>range</i>.</exception>\n\t/// <remarks>\n\t/// If <i>s</i> starts with a word character, finds substring that is not preceded by a word character.\n\t/// If <i>s</i> ends with a word character, finds substring that is not followed by a word character.\n\t/// Word characters are those for which <i>isWordChar</i> or <see cref=\"char.IsLetterOrDigit\"/> returns <c>true</c> plus those specified in <i>otherWordChars</i>.\n\t/// Uses ordinal comparison (does not depend on current culture/locale).\n\t/// For Unicode surrogates (2-<c>char</c> characters) calls <see cref=\"char.IsLetterOrDigit(string, int)\"/> and ignores <i>isWordChar</i> and <i>otherWordChars</i>.\n\t/// </remarks>\n\tpublic static int FindWord(this string t, string s, Range? range = null, bool ignoreCase = false, string otherWordChars = null, Func<char, bool> isWordChar = null) {\n\t\tNot_.Null(s);\n\t\tvar (start, end) = range.GetStartEnd(t.Length);\n\t\tint lens = s.Length;\n\t\tif (lens == 0) return 0; //like IndexOf and Find\n\n\t\tbool wordStart = _IsWordChar(s, 0, false),\n\t\t\twordEnd = _IsWordChar(s, lens - 1, true);\n\n\t\tfor (int i = start, iMax = end - lens; i <= iMax; i++) {\n\t\t\ti = t.IndexOf(s, i, end - i, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);\n\t\t\tif (i < 0) break;\n\t\t\tif (wordStart && i > 0 && _IsWordChar(t, i - 1, true)) continue;\n\t\t\tif (wordEnd && i < iMax && _IsWordChar(t, i + lens, false)) continue;\n\t\t\treturn i;\n\t\t}\n\t\treturn -1;\n\n\t\tbool _IsWordChar(string s, int i, bool expandLeft) {\n\t\t\t//CONSIDER: use Rune\n\t\t\tchar c = s[i];\n\t\t\tif (c >= '\\uD800' && c <= '\\uDFFF') { //Unicode surrogates\n\t\t\t\tif (expandLeft) {\n\t\t\t\t\tif (char.IsLowSurrogate(s[i])) return i > 0 && char.IsHighSurrogate(s[i - 1]) && char.IsLetterOrDigit(s, i - 1);\n\t\t\t\t} else {\n\t\t\t\t\tif (char.IsHighSurrogate(s[i])) return i < s.Length - 1 && char.IsLowSurrogate(s[i + 1]) && char.IsLetterOrDigit(s, i);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (isWordChar?.Invoke(c) ?? char.IsLetterOrDigit(c)) return true;\n\t\t\t\tif (otherWordChars?.Contains(c) ?? false) return true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/// <summary>\n\t/// Returns <see cref=\"string.Length\"/>. Returns 0 if this string is <c>null</c>.\n\t/// </summary>\n\t/// <param name=\"t\">This string.</param>\n\t[DebuggerStepThrough]\n\tpublic static int Lenn(this string t) => t?.Length ?? 0;\n\n\t/// <summary>\n\t/// Returns <c>true</c> if this string is <c>null</c> or empty (<c>\"\"</c>).\n\t/// </summary>\n\t/// <param name=\"t\">This string.</param>\n\t[DebuggerStepThrough]\n\tpublic static bool NE(this string t) => t == null || t.Length == 0;\n\n\t/// <summary>\n\t/// Returns this string, or <c>null</c> if it is <c>\"\"</c> or <c>null</c>.\n\t/// </summary>\n\t/// <param name=\"t\">This string.</param>\n\t[DebuggerStepThrough]\n\tinternal static string NullIfEmpty_(this string t) => t.NE() ? null : t;\n\t//not public because probably too rarely used.\n\n#if !DEBUG\n\t/// <summary>\n\t/// This function can be used with <c>foreach</c> to split this string into substrings as start/end offsets.\n\t/// </summary>\n\t/// <param name=\"t\">This string.</param>\n\t/// <param name=\"separators\">Characters that delimit the substrings. Or one of <see cref=\"SegSep\"/> constants.</param>\n\t/// <param name=\"flags\"></param>\n\t/// <param name=\"range\">Part of this string to split.</param>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// string s = \"one * two three \";\n\t/// foreach(var t in s.Segments(\" \")) print.it(s[t.start..t.end]);\n\t/// foreach(var t in s.Segments(SegSep.Word, SegFlags.NoEmpty)) print.it(s[t.start..t.end]);\n\t/// ]]></code>\n\t/// </example>\n\t[EditorBrowsable(EditorBrowsableState.Never)] //obsolete. Use Split or Lines. They are faster, have \"trim\" option; the returned array is easier to use and not too expensive. For words use regex.\n\tpublic static SegParser Segments(this string t, string separators, SegFlags flags = 0, Range? range = null) {\n\t\treturn new SegParser(t, separators, flags, range);\n\t}\n#endif\n\t\n\t/// <summary>\n\t/// Splits this string into substrings as start/end offsets.\n\t/// </summary>\n\tpublic static StartEnd[] SplitSE(this string t, Range range, char separator, StringSplitOptions flags = 0) {\n\t\tvar (start, len) = range.GetOffsetAndLength(t.Length);\n\t\tvar a = t.AsSpan(start, len).SplitSE(separator, flags);\n\t\treturn _SplitOffset(a, start);\n\t}\n\t\n#if !DEBUG\n\t/// <summary>Alias of <c>SplitSE</c>.</summary>\n\t[EditorBrowsable(EditorBrowsableState.Never)] //renamed\n\tpublic static StartEnd[] Split(this string t, Range range, char separator, StringSplitOptions flags = 0) => SplitSE(t, range, separator, flags);\n#endif\n\t\n\t/// <summary>\n\t/// Splits this string into substrings as start/end offsets.\n\t/// </summary>\n\tpublic static StartEnd[] SplitSE(this string t, Range range, string separator, StringSplitOptions flags = 0) {\n\t\tvar (start, len) = range.GetOffsetAndLength(t.Length);\n\t\tvar a = t.AsSpan(start, len).SplitSE(separator, flags);\n\t\treturn _SplitOffset(a, start);\n\t}\n\t\n#if !DEBUG\n\t/// <summary>Alias of <c>SplitSE</c>.</summary>\n\t[EditorBrowsable(EditorBrowsableState.Never)] //renamed\n\tpublic static StartEnd[] Split(this string t, Range range, string separator, StringSplitOptions flags = 0) => SplitSE(t, range, separator, flags);\n#endif\n\t\n\t/// <summary>\n\t/// Splits this string into substrings as start/end offsets. Can be used multiple separators.\n\t/// </summary>\n\tpublic static StartEnd[] SplitAnySE(this string t, Range range, RStr separators, StringSplitOptions flags = 0) {\n\t\tvar (start, len) = range.GetOffsetAndLength(t.Length);\n\t\tvar a = t.AsSpan(start, len).SplitAnySE(separators, flags);\n\t\treturn _SplitOffset(a, start);\n\t}\n\t\n#if !DEBUG\n\t/// <summary>Alias of <c>SplitAnySE</c>.</summary>\n\t[EditorBrowsable(EditorBrowsableState.Never)] //renamed\n\tpublic static StartEnd[] Split(this string t, Range range, StringSplitOptions flags, RStr separators) => SplitAnySE(t, range, separators, flags);\n#endif\n\t\n\t/// <summary>\n\t/// Splits this string into substrings as start/end offsets. Can be used multiple separators.\n\t/// </summary>\n\tpublic static StartEnd[] SplitAnySE(this string t, Range range, ReadOnlySpan<string> separators, StringSplitOptions flags = 0) {\n\t\tvar (start, len) = range.GetOffsetAndLength(t.Length);\n\t\tvar a = t.AsSpan(start, len).SplitAnySE(separators, flags);\n\t\treturn _SplitOffset(a, start);\n\t}\n\t\n#if !DEBUG\n\t/// <summary>Alias of <c>SplitAnySE</c>.</summary>\n\t[EditorBrowsable(EditorBrowsableState.Never)] //renamed\n\tpublic static StartEnd[] Split(this string t, Range range, StringSplitOptions flags, ReadOnlySpan<string> separators) => SplitAnySE(t, range, separators, flags);\n#endif\n\t\n\t/// <summary>\n\t/// Splits this string span into substrings as start/end offsets.\n\t/// </summary>\n\tpublic static StartEnd[] SplitSE(this RStr t, char separator, StringSplitOptions flags = 0)\n\t\t=> _Split(t, flags, false, 1, separator).a1;\n\t\n#if !DEBUG\n\t/// <summary>Alias of <c>SplitSE</c>.</summary>\n\t[EditorBrowsable(EditorBrowsableState.Never)] //renamed\n\tpublic static StartEnd[] Split(this RStr t, char separator, StringSplitOptions flags = 0) => SplitSE(t, separator, flags);\n#endif\n\t\n\t/// <summary>\n\t/// Splits this string span into substrings as start/end offsets.\n\t/// </summary>\n\tpublic static StartEnd[] SplitSE(this RStr t, string separator, StringSplitOptions flags = 0)\n\t\t=> _Split(t, flags, false, 2, sep23: separator).a1;\n\t\n#if !DEBUG\n\t/// <summary>Alias of <c>SplitSE</c>.</summary>\n\t[EditorBrowsable(EditorBrowsableState.Never)] //renamed\n\tpublic static StartEnd[] Split(this RStr t, string separator, StringSplitOptions flags = 0) => SplitSE(t, separator, flags);\n#endif\n\t\n\t/// <summary>\n\t/// Splits this string span into substrings as start/end offsets. Can be used multiple separators.\n\t/// </summary>\n\tpublic static StartEnd[] SplitAnySE(this RStr t, RStr separators, StringSplitOptions flags = 0)\n\t\t=> _Split(t, flags, false, 3, sep23: separators).a1;\n\t\n#if !DEBUG\n\t/// <summary>Alias of <c>SplitAnySE</c>.</summary>\n\t[EditorBrowsable(EditorBrowsableState.Never)] //renamed\n\tpublic static StartEnd[] SplitAny(this RStr t, RStr separators, StringSplitOptions flags = 0) => SplitAnySE(t, separators, flags);\n#endif\n\t\n\t/// <summary>\n\t/// Splits this string span into substrings as start/end offsets. Can be used multiple separators.\n\t/// </summary>\n\tpublic static StartEnd[] SplitAnySE(this RStr t, ReadOnlySpan<string> separators, StringSplitOptions flags = 0)\n\t\t=> _Split(t, flags, false, 4, sep4: separators).a1;\n\t\n#if !DEBUG\n\t/// <summary>Alias of <c>SplitAnySE</c>.</summary>\n\t[EditorBrowsable(EditorBrowsableState.Never)] //renamed\n\tpublic static StartEnd[] SplitAny(this RStr t, ReadOnlySpan<string> separators, StringSplitOptions flags = 0) => SplitAnySE(t, separators, flags);\n#endif\n\t\n#if !DEBUG //FUTURE: delete these etc. Hidden in v1.12 2025-05-17.\n\t/// <summary>\n\t/// Splits this string span into substrings.\n\t/// </summary>\n\t[EditorBrowsable(EditorBrowsableState.Never)] //C# 14 mess\n\tpublic static string[] SplitS(this RStr t, char separator, StringSplitOptions flags = 0)\n\t\t=> _Split(t, flags, true, 1, separator).a2;\n\t\n\t/// <summary>\n\t/// Splits this string span into substrings.\n\t/// </summary>\n\t[EditorBrowsable(EditorBrowsableState.Never)] //C# 14 mess\n\tpublic static string[] SplitS(this RStr t, string separator, StringSplitOptions flags = 0)\n\t\t=> _Split(t, flags, true, 2, sep23: separator).a2;\n\t\n\t/// <summary>\n\t/// Splits this string span into substrings. Can be used multiple separators.\n\t/// </summary>\n\t[EditorBrowsable(EditorBrowsableState.Never)] //C# 14 mess\n\tpublic static string[] SplitAnyS(this RStr t, RStr separators, StringSplitOptions flags = 0)\n\t\t=> _Split(t, flags, true, 3, sep23: separators).a2;\n\t\n\t/// <summary>\n\t/// Splits this string span into substrings. Can be used multiple separators.\n\t/// </summary>\n\t[EditorBrowsable(EditorBrowsableState.Never)] //C# 14 mess\n\tpublic static string[] SplitAnyS(this RStr t, ReadOnlySpan<string> separators, StringSplitOptions flags = 0)\n\t\t=> _Split(t, flags, true, 4, sep4: separators).a2;\n#endif\n\t\n\t[SkipLocalsInit]\n\tstatic (StartEnd[] a1, string[] a2) _Split(RStr t, StringSplitOptions flags, bool retStr, int sep, char sep1 = default, RStr sep23 = default, ReadOnlySpan<string> sep4 = default) {\n\t\tconst int na = 100;\n\t\tSpan<StartEnd> a = stackalloc StartEnd[na];\n\t\tSpan<Range> a2 = MemoryMarshal.Cast<StartEnd, Range>(a);\n\t\tList<StartEnd> list = null;\n\t\tfor (int add = 0; ;) {\n\t\t\tint n = sep switch { 1 => t.Split(a2, sep1, flags), 2 => t.Split(a2, sep23, flags), 3 => t.SplitAny(a2, sep23, flags), _ => t.SplitAny(a2, sep4, flags) };\n\t\t\t//print.it(n, na);\n\t\t\tif (add > 0) _Offset(a, n, add);\n\t\t\tif (n < na) {\n\t\t\t\tif (retStr) {\n\t\t\t\t\tstring[] r;\n\t\t\t\t\tif (list != null) {\n\t\t\t\t\t\tr = new string[list.Count + n];\n\t\t\t\t\t\tfor (int i = 0; i < list.Count; i++) r[i] = t[list[i].Range].ToString();\n\t\t\t\t\t\tfor (int i = 0, j = list.Count; i < n; i++) r[j++] = t[a[i].Range].ToString();\n\t\t\t\t\t} else {\n\t\t\t\t\t\tr = new string[n];\n\t\t\t\t\t\tfor (int i = 0; i < n; i++) r[i] = t[a[i].Range].ToString();\n\t\t\t\t\t}\n\t\t\t\t\treturn (null, r);\n\t\t\t\t} else {\n\t\t\t\t\treturn (list == null ? a[..n].ToArray() : [.. list, .. a[..n]], null);\n\t\t\t\t}\n\t\t\t}\n\t\t\tint last = a[^1].start;\n\t\t\tt = t[last..];\n\t\t\tadd += last;\n\t\t\t(list ??= new(na * 2)).AddRange(a[..^1]);\n\t\t}\n\t\t\n\t\tstatic void _Offset(Span<StartEnd> ar, int n, int add) {\n\t\t\tforeach (ref var v in ar) {\n\t\t\t\tv.start += add;\n\t\t\t\tv.end += add;\n\t\t\t}\n\t\t}\n\t}\n\t\n\tstatic StartEnd[] _SplitOffset(StartEnd[] a, int start) {\n\t\tif (start != 0) {\n\t\t\tfor (int i = 0; i < a.Length; i++) {\n\t\t\t\ta[i].start += start;\n\t\t\t\ta[i].end += start;\n\t\t\t}\n\t\t}\n\t\treturn a;\n\t}\n\n\t/// <summary>\n\t/// Splits this string into lines.\n\t/// </summary>\n\t/// <returns>Array containing lines as strings. Does not include the last empty line, unless <i>preferMore</i> true.</returns>\n\t/// <param name=\"t\">This string.</param>\n\t/// <param name=\"noEmpty\">Don't need empty lines.</param>\n\t/// <param name=\"preferMore\">Add 1 array element if the string ends with a line separator or its length is 0.</param>\n\t/// <param name=\"rareNewlines\">If <c>false</c> (default), recognizes these newlines: <c>\"\\r\\n\"</c>, <c>\"\\n\"</c> and <c>\"\\r\"</c>. If <c>true</c>, also recognizes <c>\"\\f\"</c>, <c>\"\\x0085\"</c>, <c>\"\\x2028\"</c> and <c>\"\\x2029\"</c>.</param>\n\t/// <seealso cref=\"StringReader.ReadLine\"/>\n\tpublic static string[] Lines(this string t, bool noEmpty = false, bool preferMore = false, bool rareNewlines = false)\n\t\t=> _Lines(t, noEmpty, preferMore, rareNewlines, true).a2;\n\n\t/// <summary>\n\t/// Splits this string or a range in it into lines as start/end offsets.\n\t/// </summary>\n\t/// <returns>Array containing start/end offsets of lines in the string (not in the range). Does not include the last empty line, unless <i>preferMore</i> true.</returns>\n\t/// <param name=\"t\">This string.</param>\n\t/// <param name=\"range\">Range of this string. Example: <c>var a = s.Lines(..); //split entire string</c>.</param>\n\t/// <param name=\"noEmpty\">Don't need empty lines.</param>\n\t/// <param name=\"preferMore\">Add 1 array element if the string range ends with a line separator or its length is 0.</param>\n\t/// <param name=\"rareNewlines\">If <c>false</c> (default), recognizes these newlines: <c>\"\\r\\n\"</c>, <c>\"\\n\"</c> and <c>\"\\r\"</c>. If <c>true</c>, also recognizes <c>\"\\f\"</c>, <c>\"\\x0085\"</c>, <c>\"\\x2028\"</c> and <c>\"\\x2029\"</c>.</param>\n\t/// <seealso cref=\"Lines(ReadOnlySpan{char}, bool, bool, bool)\"/>\n\tpublic static StartEnd[] Lines(this string t, Range range, bool noEmpty = false, bool preferMore = false, bool rareNewlines = false) {\n\t\tvar (start, len) = range.GetOffsetAndLength(t.Length);\n\t\tvar a = t.AsSpan(start, len).Lines(noEmpty, preferMore, rareNewlines);\n\t\treturn _SplitOffset(a, start);\n\t}\n\n\t/// <summary>\n\t/// Splits this string into lines as start/end offsets.\n\t/// </summary>\n\t/// <returns>Array containing start/end offsets of lines. Does not include the last empty line, unless <i>preferMore</i> true.</returns>\n\t/// <param name=\"t\">This string.</param>\n\t/// <param name=\"noEmpty\">Don't need empty lines.</param>\n\t/// <param name=\"preferMore\">Add 1 array element if the string span ends with a line separator or its length is 0.</param>\n\t/// <param name=\"rareNewlines\">If <c>false</c> (default), recognizes these newlines: <c>\"\\r\\n\"</c>, <c>\"\\n\"</c> and <c>\"\\r\"</c>. If <c>true</c>, also recognizes <c>\"\\f\"</c>, <c>\"\\x0085\"</c>, <c>\"\\x2028\"</c> and <c>\"\\x2029\"</c>.</param>\n\tpublic static StartEnd[] Lines(this RStr t, bool noEmpty = false, bool preferMore = false, bool rareNewlines = false)\n\t\t=> _Lines(t, noEmpty, preferMore, rareNewlines, false).a1;\n\n\t[SkipLocalsInit, MethodImpl(MethodImplOptions.AggressiveOptimization)]\n\tstatic (StartEnd[] a1, string[] a2) _Lines(RStr t, bool noEmpty, bool preferMore, bool rareNewlines, bool retStr) {\n\t\tusing var f = new FastBuffer<StartEnd>();\n\n\t\tvar newline = rareNewlines ? s_newlineAll : s_newlineRN;\n\t\tint n = 0;\n\t\tfor (int pos = 0; ;) {\n\t\t\tif (pos == t.Length && !preferMore) break;\n\t\t\tif (n == f.n) f.More(preserve: true);\n\t\t\tvar span = t[pos..];\n\t\t\tint len = span.IndexOfAny(newline);\n\t\t\tif (len < 0) {\n\t\t\t\tf[n++] = new(pos, t.Length);\n\t\t\t\tbreak;\n\t\t\t} else {\n\t\t\t\tif (!(noEmpty && len == 0)) f[n++] = new(pos, pos + len);\n\t\t\t\tpos += len + 1;\n\t\t\t\tif (span[len] == '\\r' && pos < t.Length && t[pos] == '\\n') pos++;\n\t\t\t}\n\t\t}\n\n\t\tif (!retStr) return (new Span<StartEnd>(f.p, n).ToArray(), null);\n\n\t\tvar a = new string[n];\n\t\tfor (int i = 0; i < n; i++) a[i] = t[f[i].Range].ToString();\n\t\treturn (null, a);\n\t}\n\n\t/// <summary>\n\t/// Returns the number of lines.\n\t/// </summary>\n\t/// <param name=\"t\">This string.</param>\n\t/// <param name=\"preferMore\">Add 1 if the string ends with a line separator or its length is 0.</param>\n\t/// <param name=\"range\">Part of this string or <c>null</c> (default).</param>\n\t/// <param name=\"rareNewlines\">If <c>false</c> (default), recognizes these newlines: <c>\"\\r\\n\"</c>, <c>\"\\n\"</c> and <c>\"\\r\"</c>. If <c>true</c>, also recognizes <c>\"\\f\"</c>, <c>\"\\x0085\"</c>, <c>\"\\x2028\"</c> and <c>\"\\x2029\"</c>.</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\"></exception>\n\t/// <seealso cref=\"StringUtil.LineAndColumn\"/>\n\tpublic static int LineCount(this string t, bool preferMore = false, Range? range = null, bool rareNewlines = false)\n\t\t=> LineCount(range is null ? t : t.AsSpan(range.Value), preferMore, rareNewlines);\n\n\t/// <inheritdoc cref=\"LineCount(string, bool, Range?, bool)\"/>\n\t[MethodImpl(MethodImplOptions.AggressiveOptimization)]\n\tpublic static int LineCount(this RStr t, bool preferMore = false, bool rareNewlines = false) {\n\t\tvar newline = rareNewlines ? s_newlineAll : s_newlineRN;\n\t\tint n = 0;\n\t\tfor (int pos = 0; ;) {\n\t\t\tif (pos == t.Length) {\n\t\t\t\tif (preferMore) n++;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tn++;\n\t\t\tvar span = t[pos..];\n\t\t\tint len = span.IndexOfAny(newline);\n\t\t\tif (len < 0) break;\n\t\t\tpos += len + 1;\n\t\t\tif (span[len] == '\\r' && pos < t.Length && t[pos] == '\\n') pos++;\n\t\t}\n\t\treturn n;\n\t}\n\n\tstatic readonly SearchValues<char> s_newlineRN = SearchValues.Create(['\\r', '\\n']),\n\t\ts_newlineAll = SearchValues.Create(['\\r', '\\n', '\\f', '\\x85', '\\x2028', '\\x2029']);\n\n\t/// <summary>\n\t/// Converts this string to lower case.\n\t/// </summary>\n\t/// <returns>The result string.</returns>\n\t/// <param name=\"t\">This string.</param>\n\t/// <remarks>\n\t/// Calls <see cref=\"string.ToLowerInvariant\"/>.\n\t/// </remarks>\n\tpublic static string Lower(this string t) => t.ToLowerInvariant();\n\n\t/// <summary>\n\t/// Converts this string to upper case.\n\t/// </summary>\n\t/// <returns>The result string.</returns>\n\t/// <param name=\"t\">This string.</param>\n\t/// <remarks>\n\t/// Calls <see cref=\"string.ToUpperInvariant\"/>.\n\t/// </remarks>\n\tpublic static string Upper(this string t) => t.ToUpperInvariant();\n\n\t/// <summary>\n\t/// Converts this string or only the first character to upper case or all words to title case.\n\t/// </summary>\n\t/// <returns>The result string.</returns>\n\t/// <param name=\"t\">This string.</param>\n\t/// <param name=\"how\"></param>\n\t/// <param name=\"culture\">Culture, for example <c>CultureInfo.CurrentCulture</c>. If <c>null</c> (default) uses invariant culture.</param>\n\tpublic static unsafe string Upper(this string t, SUpper how, CultureInfo culture = null) {\n\t\tif (how == SUpper.FirstChar) {\n\t\t\tif (t.Length == 0 || !char.IsLower(t, 0)) return t;\n\t\t\tvar r = Rune.GetRuneAt(t, 0);\n\t\t\tr = culture != null ? Rune.ToUpper(r, culture) : Rune.ToUpperInvariant(r);\n\t\t\tint n = r.IsBmp ? 1 : 2;\n\t\t\tvar m = new Span<char>(&r, n);\n\t\t\tif (n == 2) r.EncodeToUtf16(m);\n\t\t\treturn string.Concat(m, t.AsSpan(n));\n\t\t}\n\t\tvar ti = (culture ?? CultureInfo.InvariantCulture).TextInfo;\n\t\tt = t ?? throw new NullReferenceException();\n\t\tif (how == SUpper.TitleCase) return ti.ToTitleCase(t);\n\t\treturn ti.ToUpper(t);\n\t}\n\n\t#region ToNumber\n\n\t[MethodImpl(MethodImplOptions.AggressiveOptimization)]\n\tstatic long _ToInt(RStr t, int startIndex, out int numberEndIndex, bool toLong, STIFlags flags) {\n\t\tnumberEndIndex = 0;\n\n\t\tint len = t.Length;\n\t\tif ((uint)startIndex > len) throw new ArgumentOutOfRangeException(\"startIndex\");\n\t\tint i = startIndex;\n\t\tchar c;\n\n\t\t//skip spaces\n\t\tfor (; ; i++) {\n\t\t\tif (i == len) return 0;\n\t\t\tc = t[i];\n\t\t\tif (c > ' ') break;\n\t\t\tif (c == ' ') continue;\n\t\t\tif (c < '\\t' || c > '\\r') break; //\\t \\n \\v \\f \\r\n\t\t}\n\t\tif (i > startIndex && 0 != (flags & STIFlags.DontSkipSpaces)) return 0;\n\n\t\t//skip arabic letter mark etc\n\t\tif (c >= '\\x61C' && c is '\\x61C' or '\\x200E' or '\\x200F') {\n\t\t\tif (++i == len) return 0;\n\t\t\tc = t[i];\n\t\t}\n\n\t\t//skip -+\n\t\tbool minus = false;\n\t\tif (c is '-' or '−' or '+') {\n\t\t\tif (++i == len) return 0;\n\t\t\tif (c != '+') minus = true;\n\t\t\tc = t[i];\n\t\t}\n\n\t\t//is hex?\n\t\tbool isHex = false;\n\t\tswitch (flags & (STIFlags.NoHex | STIFlags.IsHexWithout0x)) {\n\t\tcase 0:\n\t\t\tif (c == '0' && i <= len - 3)\n\t\t\t\tif (isHex = t[i + 1] is 'x' or 'X' && t[i + 2] is (>= '0' and <= '9') or (>= 'A' and <= 'F') or (>= 'a' and <= 'f')) i += 2;\n\t\t\tbreak;\n\t\tcase STIFlags.IsHexWithout0x:\n\t\t\tisHex = true;\n\t\t\tbreak;\n\t\t}\n\n\t\t//skip '0'\n\t\tint i0 = i;\n\t\twhile (i < len && t[i] == '0') i++;\n\n\t\tlong R = 0; //result\n\n\t\tint nDigits = 0;\n\t\tif (isHex) {\n\t\t\tint nMaxDigits = toLong ? 16 : 8;\n\t\t\tfor (; i < len; i++) {\n\t\t\t\tint k = _CharHexToDec(t[i]); if (k < 0) break;\n\t\t\t\tif (++nDigits > nMaxDigits) return 0;\n\t\t\t\tR = (R << 4) + k;\n\t\t\t}\n\t\t} else { //decimal or not a number\n\t\t\tint nMaxDigits = toLong ? 20 : 10;\n\t\t\tfor (; i < len; i++) {\n\t\t\t\tint k = t[i] - '0'; if (k < 0 || k > 9) break;\n\t\t\t\tR = R * 10 + k;\n\t\t\t\t//is too long?\n\t\t\t\tif (++nDigits >= nMaxDigits) {\n\t\t\t\t\tif (nDigits > nMaxDigits) return 0;\n\t\t\t\t\tif (toLong) {\n\t\t\t\t\t\tif (t.Slice(i + 1 - nDigits).CompareTo(\"18446744073709551615\", StringComparison.Ordinal) > 0) return 0;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (R > uint.MaxValue) return 0;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (i == i0) return 0; //not a number\n\t\tnumberEndIndex = i;\n\t\treturn minus ? -R : R;\n\t}\n\n\t[MethodImpl(MethodImplOptions.AggressiveInlining)]\n\tstatic int _CharHexToDec(char c) {\n\t\tif (c >= '0' && c <= '9') return c - '0';\n\t\tif (c >= 'A' && c <= 'F') return c - ('A' - 10);\n\t\tif (c >= 'a' && c <= 'f') return c - ('a' - 10);\n\t\treturn -1;\n\t}\n\n\t/// <summary>\n\t/// Converts part of this string to <c>int</c> number and gets the number end index.\n\t/// </summary>\n\t/// <returns>The number, or 0 if failed to convert.</returns>\n\t/// <param name=\"t\">This string. Can be <c>null</c>.</param>\n\t/// <param name=\"startIndex\">Offset in this string where to start parsing.</param>\n\t/// <param name=\"numberEndIndex\">Receives offset in this string where the number part ends. If fails to convert, receives 0.</param>\n\t/// <param name=\"flags\"></param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\"><i>startIndex</i> is less than 0 or greater than string length.</exception>\n\t/// <remarks>\n\t/// Fails to convert when string is <c>null</c>, <c>\"\"</c>, does not start with a number or the number is too big.\n\t/// \n\t/// Unlike <see cref=\"int.Parse\"/> and <see cref=\"Convert.ToInt32\"/>:\n\t/// - The number in string can be followed by more text, like <c>\"123text\"</c>.\n\t/// - Has <i>startIndex</i> parameter that allows to get number from middle, like <c>\"text123text\"</c>.\n\t/// - Gets the end of the number part.\n\t/// - No exception when cannot convert.\n\t/// - The number can be decimal (like <c>\"123\"</c>) or hexadecimal (like <c>\"0x1A\"</c>); don't need separate flags for each style.\n\t/// - Does not depend on current culture. As minus sign recognizes <c>'-'</c> and <c>'−'</c>.\n\t/// - Faster.\n\t/// \n\t/// The number in string can start with ASCII whitespace (spaces, newlines, etc), like <c>\" 5\"</c>.\n\t/// The number in string can be with <c>\"-\"</c> or <c>\"+\"</c>, like <c>\"-5\"</c>, but not like <c>\"- 5\"</c>.\n\t/// Fails if the number is greater than +- <see cref=\"uint.MaxValue\"/> (0xffffffff).\n\t/// The return value becomes negative if the number is greater than <see cref=\"int.MaxValue\"/>, for example <c>\"0xffffffff\"</c> is -1, but it becomes correct if assigned to <c>uint</c> (need cast).\n\t/// Does not support non-integer numbers; for example, for <c>\"3.5E4\"</c> returns 3 and sets <c>numberEndIndex=startIndex+1</c>.\n\t/// </remarks>\n\tpublic static int ToInt(this string t, int startIndex, out int numberEndIndex, STIFlags flags = 0) {\n\t\treturn (int)_ToInt(t, startIndex, out numberEndIndex, false, flags);\n\t}\n\n\t/// <summary>\n\t/// Converts part of this string to <c>int</c> number.\n\t/// </summary>\n\t/// <remarks></remarks>\n\t/// <inheritdoc cref=\"ToInt(string, int, out int, STIFlags)\"/>\n\tpublic static int ToInt(this string t, int startIndex = 0, STIFlags flags = 0) {\n\t\treturn (int)_ToInt(t, startIndex, out _, false, flags);\n\t}\n\n\t/// <returns><c>false</c> if failed.</returns>\n\t/// <param name=\"result\">Receives the result, or 0 if failed.</param>\n\t/// <remarks></remarks>\n\t/// <inheritdoc cref=\"ToInt(string, int, out int, STIFlags)\"/>\n\tpublic static bool ToInt(this string t, out int result, int startIndex, out int numberEndIndex, STIFlags flags = 0) {\n\t\tresult = (int)_ToInt(t, startIndex, out numberEndIndex, false, flags);\n\t\treturn numberEndIndex != 0;\n\t}\n\n\t/// <returns><c>false</c> if failed.</returns>\n\t/// <param name=\"result\">Receives the result, or 0 if failed.</param>\n\t/// <remarks></remarks>\n\t/// <inheritdoc cref=\"ToInt(string, int, STIFlags)\"/>\n\tpublic static bool ToInt(this string t, out int result, int startIndex = 0, STIFlags flags = 0)\n\t\t=> ToInt(t, out result, startIndex, out _, flags);\n\n\t/// <summary>\n\t/// Converts part of this string to <c>uint</c> number and gets the number end index.\n\t/// </summary>\n\t/// <remarks></remarks>\n\t/// <inheritdoc cref=\"ToInt(string, out int, int, out int, STIFlags)\"/>\n\tpublic static bool ToInt(this string t, out uint result, int startIndex, out int numberEndIndex, STIFlags flags = 0) {\n\t\tresult = (uint)_ToInt(t, startIndex, out numberEndIndex, false, flags);\n\t\treturn numberEndIndex != 0;\n\t}\n\n\t/// <summary>\n\t/// Converts part of this string to <c>uint</c> number.\n\t/// </summary>\n\t/// <remarks></remarks>\n\t/// <inheritdoc cref=\"ToInt(string, out int, int, STIFlags)\"/>\n\tpublic static bool ToInt(this string t, out uint result, int startIndex = 0, STIFlags flags = 0)\n\t\t=> ToInt(t, out result, startIndex, out _, flags);\n\n\t/// <summary>\n\t/// Converts part of this string to <c>long</c> number and gets the number end index.\n\t/// </summary>\n\t/// <remarks></remarks>\n\t/// <inheritdoc cref=\"ToInt(string, out int, int, out int, STIFlags)\"/>\n\tpublic static bool ToInt(this string t, out long result, int startIndex, out int numberEndIndex, STIFlags flags = 0) {\n\t\tresult = _ToInt(t, startIndex, out numberEndIndex, true, flags);\n\t\treturn numberEndIndex != 0;\n\t}\n\n\t/// <summary>\n\t/// Converts part of this string to <c>long</c> number.\n\t/// </summary>\n\t/// <remarks></remarks>\n\t/// <inheritdoc cref=\"ToInt(string, out int, int, STIFlags)\"/>\n\tpublic static bool ToInt(this string t, out long result, int startIndex = 0, STIFlags flags = 0)\n\t\t=> ToInt(t, out result, startIndex, out _, flags);\n\n\t/// <summary>\n\t/// Converts part of this string to <c>ulong</c> number and gets the number end index.\n\t/// </summary>\n\t/// <remarks></remarks>\n\t/// <inheritdoc cref=\"ToInt(string, out int, int, out int, STIFlags)\"/>\n\tpublic static bool ToInt(this string t, out ulong result, int startIndex, out int numberEndIndex, STIFlags flags = 0) {\n\t\tresult = (ulong)_ToInt(t, startIndex, out numberEndIndex, true, flags);\n\t\treturn numberEndIndex != 0;\n\t}\n\n\t/// <summary>\n\t/// Converts part of this string to <c>ulong</c> number.\n\t/// </summary>\n\t/// <remarks></remarks>\n\t/// <inheritdoc cref=\"ToInt(string, out int, int, STIFlags)\"/>\n\tpublic static bool ToInt(this string t, out ulong result, int startIndex = 0, STIFlags flags = 0)\n\t\t=> ToInt(t, out result, startIndex, out _, flags);\n\n\t//FUTURE: all `ToInt(this string...)` -> `ToInt(this RStr...)`.\n\tinternal static int ToInt_(this RStr t, STIFlags flags = 0)\n\t\t=> (int)_ToInt(t, 0, out _, false, flags);\n\n\tinternal static bool ToInt_(this RStr t, out int result, out int numberEndIndex, STIFlags flags = 0) {\n\t\tresult = (int)_ToInt(t, 0, out numberEndIndex, false, flags);\n\t\treturn numberEndIndex != 0;\n\t}\n\n\t/// <summary>\n\t/// Converts this string or its part to double number.\n\t/// </summary>\n\t/// <returns>The number, or 0 if failed to convert.</returns>\n\t/// <param name=\"t\">This string. Can be <c>null</c>.</param>\n\t/// <param name=\"range\">Part of this string or <c>null</c> (default).</param>\n\t/// <param name=\"style\">The permitted number format in the string.</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Invalid <i>range</i>.</exception>\n\t/// <exception cref=\"ArgumentException\">Invalid <i>style</i>.</exception>\n\t/// <remarks>\n\t/// Calls <see cref=\"double.TryParse(RStr, NumberStyles, IFormatProvider, out double)\"/> with <see cref=\"CultureInfo\"/> <c>InvariantCulture</c>.\n\t/// Fails if the string is <c>null</c> or <c>\"\"</c> or isn't a valid floating-point number.\n\t/// Examples of valid numbers: <c>\"12\"</c>, <c>\" -12.3 \"</c>, <c>\".12\"</c>, <c>\"12.\"</c>, <c>\"12E3\"</c>, <c>\"12.3e-45\"</c>, <c>\"1,234.5\"</c> (with <i>style</i> <c>NumberStyles.Float | NumberStyles.AllowThousands</c>). String like <c>\"2text\"</c> is invalid, unless <i>range</i> is <c>0..1</c>.\n\t/// </remarks>\n\tpublic static double ToNumber(this string t, Range? range = null, NumberStyles style = NumberStyles.Float) {\n\t\tToNumber(t, out double r, range, style);\n\t\treturn r;\n\t}\n\n\t/// <returns><c>false</c> if failed.</returns>\n\t/// <param name=\"result\">Receives the result, or 0 if failed.</param>\n\t/// <inheritdoc cref=\"ToNumber(string, Range?, NumberStyles)\"/>\n\tpublic static bool ToNumber(this string t, out double result, Range? range = null, NumberStyles style = NumberStyles.Float) {\n\t\treturn double.TryParse(_NumSpan(t, range, out var ci), style, ci, out result);\n\t}\n\n\t/// <summary>\n\t/// Converts this string or its part to float number.\n\t/// </summary>\n\t/// <remarks>\n\t/// Calls <see cref=\"float.TryParse(RStr, NumberStyles, IFormatProvider, out float)\"/> with <see cref=\"CultureInfo\"/> <c>InvariantCulture</c>.\n\t/// </remarks>\n\t/// <inheritdoc cref=\"ToNumber(string, out double, Range?, NumberStyles)\"/>\n\tpublic static bool ToNumber(this string t, out float result, Range? range = null, NumberStyles style = NumberStyles.Float) {\n\t\treturn float.TryParse(_NumSpan(t, range, out var ci), style, ci, out result);\n\t}\n\n\t/// <summary>\n\t/// Converts this string or its part to <c>int</c> number.\n\t/// </summary>\n\t/// <remarks>\n\t/// Calls <see cref=\"int.TryParse(RStr, NumberStyles, IFormatProvider, out int)\"/> with <see cref=\"CultureInfo\"/> <c>InvariantCulture</c>.\n\t/// </remarks>\n\t/// <inheritdoc cref=\"ToNumber(string, out double, Range?, NumberStyles)\"/>\n\tpublic static bool ToNumber(this string t, out int result, Range? range = null, NumberStyles style = NumberStyles.Integer) {\n\t\treturn int.TryParse(_NumSpan(t, range, out var ci), style, ci, out result);\n\n\t\t//note: exception if NumberStyles.Integer | NumberStyles.AllowHexSpecifier.\n\t\t//\tCan parse either decimal or hex, not any.\n\t\t//\tDoes not support \"0x\". With AllowHexSpecifier eg \"11\" is 17, but \"0x11\" is invalid.\n\t}\n\n\t/// <summary>\n\t/// Converts this string or its part to <c>uint</c> number.\n\t/// </summary>\n\t/// <remarks>\n\t/// Calls <see cref=\"uint.TryParse(RStr, NumberStyles, IFormatProvider, out uint)\"/> with <see cref=\"CultureInfo\"/> <c>InvariantCulture</c>.\n\t/// </remarks>\n\t/// <inheritdoc cref=\"ToNumber(string, out double, Range?, NumberStyles)\"/>\n\tpublic static bool ToNumber(this string t, out uint result, Range? range = null, NumberStyles style = NumberStyles.Integer) {\n\t\treturn uint.TryParse(_NumSpan(t, range, out var ci), style, ci, out result);\n\t}\n\n\t/// <summary>\n\t/// Converts this string or its part to <c>long</c> number.\n\t/// </summary>\n\t/// <remarks>\n\t/// Calls <see cref=\"long.TryParse(RStr, NumberStyles, IFormatProvider, out long)\"/> with <see cref=\"CultureInfo\"/> <c>InvariantCulture</c>.\n\t/// </remarks>\n\t/// <inheritdoc cref=\"ToNumber(string, out double, Range?, NumberStyles)\"/>\n\tpublic static bool ToNumber(this string t, out long result, Range? range = null, NumberStyles style = NumberStyles.Integer) {\n\t\treturn long.TryParse(_NumSpan(t, range, out var ci), style, ci, out result);\n\t}\n\n\t/// <summary>\n\t/// Converts this string or its part to <c>ulong</c> number.\n\t/// </summary>\n\t/// <remarks>\n\t/// Calls <see cref=\"ulong.TryParse(RStr, NumberStyles, IFormatProvider, out ulong)\"/>. Uses <see cref=\"CultureInfo.InvariantCulture\"/> if the string range contains only ASCII characters, else uses current culture.\n\t/// </remarks>\n\t/// <inheritdoc cref=\"ToNumber(string, out double, Range?, NumberStyles)\"/>\n\tpublic static bool ToNumber(this string t, out ulong result, Range? range = null, NumberStyles style = NumberStyles.Integer) {\n\t\treturn ulong.TryParse(_NumSpan(t, range, out var ci), style, ci, out result);\n\t}\n\n\t[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]\n\tstatic RStr _NumSpan(string t, Range? range, out CultureInfo ci) {\n\t\tci = CultureInfo.InvariantCulture;\n\t\tif (t == null) return default;\n\t\tvar (start, len) = range.GetOffsetAndLength(t.Length);\n\n\t\t//Workaround for .NET 5 preview 7 bug: if current user culture is eg Norvegian or Lithuanian,\n\t\t//\t'number to/from string' functions use '−' (Unicode minus), not '-' (ASCII hyphen), even if in Control Panel is ASCII hyphen.\n\t\t//\tTested: no bug in .NET Core 3.1.\n\t\t//Also, in some cultures eg Arabic there are more chars.\n\t\tif (!t.AsSpan(start, len).IsAscii()) ci = CultureInfo.CurrentCulture;\n\n\t\treturn t.AsSpan(start, len);\n\t}\n\n\t#endregion\n\n\t/// <summary>\n\t/// Inserts other string.\n\t/// </summary>\n\t/// <returns>The result string.</returns>\n\t/// <param name=\"t\">This string.</param>\n\t/// <param name=\"startIndex\">Offset in this string. Can be from end, like <c>^4</c>.</param>\n\t/// <param name=\"s\">String to insert.</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Invalid <i>startIndex</i>.</exception>\n\tpublic static string Insert(this string t, Index startIndex, string s) {\n\t\treturn t.Insert(startIndex.GetOffset(t.Length), s);\n\t}\n\n\t/// <summary>\n\t/// Replaces part of this string with other string.\n\t/// </summary>\n\t/// <returns>The result string.</returns>\n\t/// <param name=\"t\">This string.</param>\n\t/// <param name=\"startIndex\">Offset in this string.</param>\n\t/// <param name=\"count\">Count of characters to replace.</param>\n\t/// <param name=\"s\">The replacement string.</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Invalid <i>startIndex</i> or <i>count</i>.</exception>\n\tpublic static string ReplaceAt(this string t, int startIndex, int count, string s) {\n\t\tint i = startIndex;\n\t\tif (count == 0) return t.Insert(i, s);\n\t\treturn string.Concat(t.AsSpan(0, i), s, t.AsSpan(i + count));\n\t}\n\n\t/// <summary>\n\t/// Replaces part of this string with other string.\n\t/// </summary>\n\t/// <returns>The result string.</returns>\n\t/// <param name=\"t\">This string.</param>\n\t/// <param name=\"range\">Part of this string to replace.</param>\n\t/// <param name=\"s\">The replacement string.</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Invalid <i>range</i>.</exception>\n\tpublic static string ReplaceAt(this string t, Range range, string s) {\n\t\tvar (i, count) = range.GetOffsetAndLength(t.Length);\n\t\treturn ReplaceAt(t, i, count, s);\n\t}\n\n\t/// <summary>\n\t/// Removes part of this string.\n\t/// </summary>\n\t/// <returns>The result string.</returns>\n\t/// <param name=\"t\">This string.</param>\n\t/// <param name=\"range\">Part of this string to remove.</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Invalid <i>ranget</i>.</exception>\n\tpublic static string Remove(this string t, Range range) {\n\t\tvar (i, count) = range.GetOffsetAndLength(t.Length);\n\t\treturn t.Remove(i, count);\n\t}\n\n\t//rejected. Use [..^count].\n\t///// <summary>\n\t///// Removes <i>count</i> characters from the end of this string.\n\t///// </summary>\n\t///// <returns>The result string.</returns>\n\t///// <param name=\"t\">This string.</param>\n\t///// <param name=\"count\">Count of characters to remove.</param>\n\t///// <exception cref=\"ArgumentOutOfRangeException\"></exception>\n\t//public static string RemoveSuffix(this string t, int count) => t[^count];\n\n\t/// <summary>\n\t/// Removes <i>suffix</i> string from the end.\n\t/// </summary>\n\t/// <returns>The result string. Returns this string if does not end with <i>suffix</i>.</returns>\n\t/// <param name=\"t\">This string.</param>\n\t/// <param name=\"suffix\">Substring to remove.</param>\n\t/// <param name=\"ignoreCase\">Case-insensitive.</param>\n\t/// <exception cref=\"ArgumentNullException\"><i>suffix</i> is <c>null</c>.</exception>\n\tpublic static string RemoveSuffix(this string t, string suffix, bool ignoreCase = false) {\n\t\tif (!t.Ends(suffix, ignoreCase)) return t;\n\t\treturn t[..^suffix.Length];\n\t}\n\n\t/// <summary>\n\t/// Removes <i>suffix</i> character from the end.\n\t/// </summary>\n\t/// <returns>The result string. Returns this string if does not end with <i>suffix</i>.</returns>\n\t/// <param name=\"t\">This string.</param>\n\t/// <param name=\"suffix\">Character to remove.</param>\n\t/// <exception cref=\"ArgumentNullException\"><i>suffix</i> is <c>null</c>.</exception>\n\tpublic static string RemoveSuffix(this string t, char suffix) {\n\t\tif (!t.Ends(suffix)) return t;\n\t\treturn t[..^1];\n\t}\n\n\t/// <summary>\n\t/// If this string is longer than <i>limit</i>, returns its substring 0 to <i>limit</i>-1 with appended <c>'…'</c> character.\n\t/// Else returns this string.\n\t/// </summary>\n\t/// <param name=\"t\">This string.</param>\n\t/// <param name=\"limit\">Maximal length of the result string. If less than 1, uses 1.</param>\n\t/// <param name=\"middle\">Let <c>\"…\"</c> be in the middle. For example it is useful when the string is a file path, to avoid removing the filename.</param>\n\t/// <param name=\"lines\"><i>limit</i> is lines, not characters.</param>\n\tpublic static string Limit(this string t, int limit, bool middle = false, bool lines = false) {\n\t\tif (limit < 1) limit = 1;\n\t\tif (lines) {\n\t\t\tvar a = t.AsSpan().Lines();\n\t\t\tint k = a.Length;\n\t\t\tif (k > limit) {\n\t\t\t\tlimit--; //for \"…\" line\n\t\t\t\tif (limit == 0) return t[a[0].Range] + \"…\";\n\t\t\t\tif (middle) {\n\t\t\t\t\tif (limit == 1) return t[a[0].Range] + \"\\r\\n…\";\n\t\t\t\t\tint half = limit - limit / 2; //if limit is odd number, prefer more lines at the start\n\t\t\t\t\tint half2 = a.Length - (limit - half);\n\t\t\t\t\t//if (half2 == a.Length - 1 && a[half2].Length == 0) return t[..a[half].end] + \"\\r\\n…\"; //rejected: if ends with newline, prefer more lines at the start than \"\\r\\n…\\r\\n\" at the end\n\t\t\t\t\treturn t.ReplaceAt(a[half - 1].end..a[half2].start, \"\\r\\n…\\r\\n\");\n\t\t\t\t} else {\n\t\t\t\t\treturn t[..a[limit - 1].end] + \"\\r\\n…\";\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (t.Length > limit) {\n\t\t\tlimit--; //for \"…\"\n\t\t\tif (middle) {\n\t\t\t\tint i = _Correct(t, limit / 2);\n\t\t\t\tint j = _Correct(t, t.Length - (limit - i), 1);\n\t\t\t\treturn t.ReplaceAt(i..j, \"…\");\n\t\t\t} else {\n\t\t\t\tlimit = _Correct(t, limit);\n\t\t\t\treturn t[..limit] + \"…\";\n\t\t\t}\n\n\t\t\t//ensure not in the middle of a surrogate pair or \\r\\n\n\t\t\tstatic int _Correct(string s, int i, int d = -1) {\n\t\t\t\tif (i > 0 && i < s.Length) {\n\t\t\t\t\tchar c = s[i - 1];\n\t\t\t\t\tif ((c == '\\r' && s[i] == '\\n') || char.IsSurrogatePair(c, s[i])) i += d;\n\t\t\t\t}\n\t\t\t\treturn i;\n\t\t\t}\n\t\t}\n\t\treturn t;\n\t}\n\n\t/// <summary>\n\t/// Replaces unsafe characters with C# escape sequences.\n\t/// If the string contains these characters, replaces and returns new string. Else returns this string.\n\t/// </summary>\n\t/// <param name=\"t\">This string.</param>\n\t/// <param name=\"limit\">If the final string is longer than <i>limit</i>, get its substring 0 to <i>limit</i>-1 with appended <c>'…'</c> character. The enclosing <c>\"\"</c> are not counted.</param>\n\t/// <param name=\"quote\">Enclose in <c>\"\"</c>.</param>\n\t/// <remarks>\n\t/// Replaces these characters: <c>'\\\\'</c>, <c>'\"'</c>, <c>'\\t'</c>, <c>'\\n'</c>, <c>'\\r'</c> and all in range 0-31.\n\t/// </remarks>\n\t[MethodImpl(MethodImplOptions.AggressiveOptimization)]\n\tpublic static string Escape(this string t, int limit = 0, bool quote = false) {\n\t\tint i, len = t.Length;\n\t\tif (len == 0) return quote ? \"\\\"\\\"\" : t;\n\n\t\tif (limit > 0) {\n\t\t\tif (len > limit) len = limit - 1; else limit = 0;\n\t\t}\n\n\t\tfor (i = 0; i < len; i++) {\n\t\t\tvar c = t[i];\n\t\t\tif (c < ' ' || c == '\\\\' || c == '\"') goto g1;\n\t\t\t//tested: Unicode line-break chars in most controls don't break lines, therefore don't need to escape\n\t\t}\n\t\tif (limit > 0) t = Limit(t, limit);\n\t\tif (quote) t = \"\\\"\" + t + \"\\\"\";\n\t\treturn t;\n\t\tg1:\n\t\tusing (new StringBuilder_(out var b, len + len / 16 + 100)) {\n\t\t\tif (quote) b.Append('\"');\n\t\t\tfor (i = 0; i < len; i++) {\n\t\t\t\tvar c = t[i];\n\t\t\t\tif (c < ' ') {\n\t\t\t\t\tswitch (c) {\n\t\t\t\t\tcase '\\t': b.Append(\"\\\\t\"); break;\n\t\t\t\t\tcase '\\n': b.Append(\"\\\\n\"); break;\n\t\t\t\t\tcase '\\r': b.Append(\"\\\\r\"); break;\n\t\t\t\t\tcase '\\0': b.Append(\"\\\\0\"); break;\n\t\t\t\t\tdefault: b.Append(\"\\\\u\").Append(((ushort)c).ToString(\"x4\")); break;\n\t\t\t\t\t}\n\t\t\t\t} else if (c == '\\\\') b.Append(\"\\\\\\\\\");\n\t\t\t\telse if (c == '\"') b.Append(\"\\\\\\\"\");\n\t\t\t\telse b.Append(c);\n\n\t\t\t\tif (limit > 0 && b.Length - (quote ? 1 : 0) >= len) break;\n\t\t\t}\n\n\t\t\tif (limit > 0) b.Append('…');\n\t\t\tif (quote) b.Append('\"');\n\t\t\treturn b.ToString();\n\t\t}\n\t}\n\n\t/// <summary>\n\t/// Replaces C# escape sequences to characters in this string.\n\t/// </summary>\n\t/// <returns><c>false</c> if the string contains an invalid or unsupported escape sequence.</returns>\n\t/// <param name=\"t\">This string.</param>\n\t/// <param name=\"result\">Receives the result string. It is this string if there are no escape sequences or if failed.</param>\n\t/// <remarks>\n\t/// Supports all escape sequences of <see cref=\"Escape\"/>: <c>\\\\</c>, <c>\\\"</c>, <c>\\t</c>, <c>\\n</c>, <c>\\r</c>, <c>\\0</c>, <c>\\uXXXX</c>.\n\t/// Does not support <c>\\a</c>, <c>\\b</c>, <c>\\f</c>, <c>\\v</c>, <c>\\e</c>, <c>\\'</c>, <c>\\xXXXX</c>, <c>\\UXXXXXXXX</c>.\n\t/// </remarks>\n\t[MethodImpl(MethodImplOptions.AggressiveOptimization)]\n\tpublic static bool Unescape(this string t, out string result) {\n\t\tresult = t;\n\t\tint i = t.IndexOf('\\\\');\n\t\tif (i < 0) return true;\n\n\t\tusing (new StringBuilder_(out var b, t.Length)) {\n\t\t\tb.Append(t, 0, i);\n\n\t\t\tfor (; i < t.Length; i++) {\n\t\t\t\tchar c = t[i];\n\t\t\t\tif (c == '\\\\') {\n\t\t\t\t\tif (++i == t.Length) return false;\n\t\t\t\t\tswitch (c = t[i]) {\n\t\t\t\t\tcase '\\\\': case '\"': break;\n\t\t\t\t\tcase 't': c = '\\t'; break;\n\t\t\t\t\tcase 'n': c = '\\n'; break;\n\t\t\t\t\tcase 'r': c = '\\r'; break;\n\t\t\t\t\tcase '0': c = '\\0'; break;\n\t\t\t\t\t//case 'a': c = '\\a'; break;\n\t\t\t\t\t//case 'b': c = '\\b'; break;\n\t\t\t\t\t//case 'f': c = '\\f'; break;\n\t\t\t\t\t//case 'v': c = '\\v'; break;\n\t\t\t\t\t//case 'e': c = '\\e'; break;\n\t\t\t\t\t//also we don't support U and x\n\t\t\t\t\tcase 'u':\n\t\t\t\t\t\tif (!_Uni(t, ++i, 4, out int u)) return false;\n\t\t\t\t\t\tc = (char)u;\n\t\t\t\t\t\ti += 3;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault: return false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tb.Append(c);\n\t\t\t}\n\n\t\t\tresult = b.ToString();\n\t\t\treturn true;\n\t\t}\n\n\t\tstatic bool _Uni(string t, int i, int maxLen, out int R) {\n\t\t\tR = 0;\n\t\t\tint to = i + maxLen; if (to > t.Length) return false;\n\t\t\tfor (; i < to; i++) {\n\t\t\t\tint k = _CharHexToDec(t[i]); if (k < 0) return false;\n\t\t\t\tR = (R << 4) + k;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t}\n\n\t/// <returns>New string if replaced, else this string. Does not replace if the string does not contain escape sequences or if some escape sequences are invalid or unsupported.</returns>\n\t/// <inheritdoc cref=\"Unescape(string, out string)\"/>\n\tpublic static string Unescape(this string t) {\n\t\tUnescape(t, out t);\n\t\treturn t;\n\t}\n\n\t/// <summary>\n\t/// Reverses this string, like <c>\"Abc\"</c> -> <c>\"cbA\"</c>.\n\t/// </summary>\n\t/// <returns>The result string.</returns>\n\t/// <param name=\"t\"></param>\n\t/// <param name=\"raw\">Ignore <c>char</c> sequences such as Unicode surrogates and grapheme clusters. Faster, but if the string contains these sequences, the result string is incorrect.</param>\n\tpublic static unsafe string ReverseString(this string t, bool raw) {\n\t\tif (t.Length < 2) return t;\n\t\tvar r = new string('\\0', t.Length);\n\t\tfixed (char* p = r) {\n\t\t\tif (raw || t.IsAscii()) {\n\t\t\t\tfor (int i = 0, j = t.Length; i < t.Length; i++) {\n\t\t\t\t\tp[--j] = t[i];\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tvar a = StringInfo.ParseCombiningCharacters(t); //speed: same as StringInfo.GetTextElementEnumerator+MoveNext+ElementIndex\n\t\t\t\tfor (int gTo = t.Length, j = 0, i = a.Length; --i >= 0; gTo = a[i]) {\n\t\t\t\t\tfor (int g = a[i]; g < gTo; g++) p[j++] = t[g];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn r;\n\n\t\t//tested: string.Create slower.\n\t}\n\n\t/// <summary>\n\t/// Returns <c>true</c> if does not contain non-ASCII characters.\n\t/// </summary>\n\tpublic static bool IsAscii(this string t) => t.AsSpan().IsAscii();\n\t//FUTURE: remove this and other functions that in C# 14+ are duplicate or useless.\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if does not contain non-ASCII characters.\n\t/// </summary>\n\tpublic static bool IsAscii(this RStr t) => !t.ContainsAnyExceptInRange((char)0, (char)127);\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if does not contain non-ASCII character bytes.\n\t/// </summary>\n\tpublic static bool IsAscii(this RByte t) => !t.ContainsAnyExceptInRange((byte)0, (byte)127);\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if null pointer.\n\t/// </summary>\n\t[MethodImpl(MethodImplOptions.AggressiveInlining)]\n\tpublic static bool IsNull(this RStr t) => t == RStr.Empty;\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if null pointer.\n\t/// </summary>\n\t[MethodImpl(MethodImplOptions.AggressiveInlining)]\n\tpublic static bool IsNull(this RByte t) => t == RByte.Empty;\n\n\t[MethodImpl(MethodImplOptions.AggressiveInlining)]\n\tinternal static int LengthThrowIfNull_(this RStr t) {\n\t\tint n = t.Length;\n\t\tif (n == 0 && t.IsNull()) throw new ArgumentNullException();\n\t\treturn n;\n\t}\n\n\t/// <summary>\n\t/// Returns <c>true</c> if equals to string <i>s</i>, case-sensitive.\n\t/// </summary>\n\t/// <param name=\"t\">This span.</param>\n\t/// <param name=\"s\">Other string. Can be <c>null</c>.</param>\n\t/// <remarks>\n\t/// Uses ordinal comparison (does not depend on current culture/locale).\n\t/// </remarks>\n\tpublic static bool Eq(this RStr t, RStr s) => t.Equals(s, StringComparison.Ordinal);\n\n\t/// <summary>\n\t/// Returns <c>true</c> if equals to string <i>s</i>, case-insensitive.\n\t/// </summary>\n\t/// <param name=\"t\">This span.</param>\n\t/// <param name=\"s\">Other string. Can be <c>null</c>.</param>\n\t/// <remarks>\n\t/// Uses ordinal comparison (does not depend on current culture/locale).\n\t/// </remarks>\n\tpublic static bool Eqi(this RStr t, RStr s) => t.Equals(s, StringComparison.OrdinalIgnoreCase);\n\n\t/// <summary>\n\t/// Compares part of this span with string <i>s</i>. Returns <c>true</c> if equal.\n\t/// </summary>\n\t/// <param name=\"t\">This span.</param>\n\t/// <param name=\"startIndex\">Offset in this span. If invalid, returns <c>false</c>.</param>\n\t/// <param name=\"s\">Other string.</param>\n\t/// <param name=\"ignoreCase\">Case-insensitive.</param>\n\t/// <exception cref=\"ArgumentNullException\"><i>s</i> is <c>null</c>.</exception>\n\t/// <remarks>\n\t/// Uses ordinal comparison (does not depend on current culture/locale).\n\t/// </remarks>\n\tpublic static bool Eq(this RStr t, int startIndex, RStr s, bool ignoreCase = false) {\n\t\tint ns = s.LengthThrowIfNull_();\n\t\tint to = startIndex + ns, tlen = t.Length;\n\t\tif (to > tlen || (uint)startIndex > tlen) return false;\n\t\tt = t[startIndex..to];\n\t\tif (!ignoreCase) return t.SequenceEqual(s);\n\t\treturn t.Equals(s, StringComparison.OrdinalIgnoreCase);\n\t}\n\n\t/// <summary>\n\t/// Returns <c>true</c> if the specified character is at the specified position in this span.\n\t/// </summary>\n\t/// <param name=\"t\">This span.</param>\n\t/// <param name=\"index\">Offset in this span. If invalid, returns <c>false</c>.</param>\n\t/// <param name=\"c\">Character.</param>\n\tpublic static bool Eq(this RStr t, int index, char c) {\n\t\tif ((uint)index >= t.Length) return false;\n\t\treturn t[index] == c;\n\t}\n\n\t/// <summary>\n\t/// Returns <c>true</c> if starts with string <i>s</i>.\n\t/// </summary>\n\t/// <param name=\"t\">This span.</param>\n\t/// <param name=\"s\">Other string.</param>\n\t/// <param name=\"ignoreCase\">Case-insensitive.</param>\n\t/// <remarks>\n\t/// Uses ordinal comparison (does not depend on current culture/locale).\n\t/// </remarks>\n\tpublic static bool Starts(this RStr t, RStr s, bool ignoreCase = false) => t.StartsWith(s, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);\n\n\t/// <summary>\n\t/// Returns <c>true</c> if starts with string <i>s</i>.\n\t/// </summary>\n\t/// <param name=\"t\">This span.</param>\n\t/// <param name=\"s\">Other string.</param>\n\t/// <param name=\"ignoreCase\">Case-insensitive.</param>\n\t/// <remarks>\n\t/// Uses ordinal comparison (does not depend on current culture/locale).\n\t/// </remarks>\n\tpublic static bool Ends(this RStr t, RStr s, bool ignoreCase = false) => t.EndsWith(s, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);\n\t\n\t/// <summary>\n\t/// Finds character <i>c</i> in this span, starting from <i>startIndex</i>.\n\t/// </summary>\n\t/// <returns>Character index in this span, or -1 if not found.</returns>\n\t/// <exception cref=\"ArgumentOutOfRangeException\"></exception>\n\tpublic static int IndexOf(this RStr t, int startIndex, char c) {\n\t\tint i = t[startIndex..].IndexOf(c);\n\t\treturn i < 0 ? i : i + startIndex;\n\t}\n\n\t/// <summary>\n\t/// Finds character <i>c</i> in <i>range</i> of this span.\n\t/// </summary>\n\t/// <returns>Character index in this span, or -1 if not found.</returns>\n\t/// <exception cref=\"ArgumentOutOfRangeException\"></exception>\n\tpublic static int IndexOf(this RStr t, Range range, char c) {\n\t\tint i = t[range].IndexOf(c);\n\t\tif (i < 0) return i;\n\t\treturn i + range.Start.GetOffset(t.Length);\n\t}\n\n\t/// <summary>\n\t/// Finds string <i>s</i> in this span, starting from <i>startIndex</i>.\n\t/// </summary>\n\t/// <returns>Character index in this span, or -1 if not found.</returns>\n\t/// <exception cref=\"ArgumentOutOfRangeException\"></exception>\n\t/// <exception cref=\"ArgumentNullException\"><i>s</i> is <c>null</c>.</exception>\n\tpublic static int IndexOf(this RStr t, int startIndex, RStr s, bool ignoreCase = false) {\n\t\tif (s.IsNull()) throw new ArgumentNullException();\n\t\tint i = t[startIndex..].IndexOf(s, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);\n\t\treturn i < 0 ? i : i + startIndex;\n\t}\n\n\t/// <summary>\n\t/// Finds string <i>s</i> in <i>range</i> of this span.\n\t/// </summary>\n\t/// <returns>Character index in this span, or -1 if not found.</returns>\n\t/// <exception cref=\"ArgumentOutOfRangeException\"></exception>\n\t/// <exception cref=\"ArgumentNullException\"><i>s</i> is <c>null</c>.</exception>\n\tpublic static int IndexOf(this RStr t, Range range, RStr s, bool ignoreCase = false) {\n\t\tif (s.IsNull()) throw new ArgumentNullException();\n\t\tint i = t[range].IndexOf(s, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);\n\t\tif (i < 0) return i;\n\t\treturn i + range.Start.GetOffset(t.Length);\n\t}\n\n#if !DEBUG\n\t/// <summary>\n\t/// Alias of <c>IndexOfAnyExcept</c>.\n\t/// </summary>\n\t[EditorBrowsable(EditorBrowsableState.Never)] //.NET now has IndexOfAnyExcept\n\tpublic static int IndexOfNot(this RStr t, string chars) => t.IndexOfAnyExcept(chars);\n\t\n\t/// <summary>\n\t/// Alias of <c>LastIndexOfAnyExcept</c>.\n\t/// </summary>\n\t[EditorBrowsable(EditorBrowsableState.Never)] //.NET now has LastIndexOfAnyExcept\n\tpublic static int LastIndexOfNot(this RStr t, string chars) => t.LastIndexOfAnyExcept(chars);\n#endif\n\n\tinternal static void CopyTo_(this string t, char* p)\n\t\t=> t.AsSpan().CopyTo(new Span<char>(p, t.Length));\n\n\t/// <summary>\n\t/// Converts to UTF-8 (<c>Encoding.UTF8.GetBytes</c>).\n\t/// </summary>\n\tpublic static byte[] ToUTF8(this string t)\n\t\t=> Encoding.UTF8.GetBytes(t);\n\n\t/// <summary>\n\t/// Converts to UTF-8.\n\t/// </summary>\n\t/// <param name=\"append0\">Return 0-terminated UTF-8 string.</param>\n\tpublic static byte[] ToUTF8(this RStr t, bool append0 = false)\n\t\t=> Convert2.Utf8Encode(t, append0 ? \"\\0\" : null);\n\n\t/// <summary>\n\t/// Converts to UTF-8.\n\t/// </summary>\n\t/// <param name=\"append0\">Return 0-terminated UTF-8 string.</param>\n\tpublic static byte[] ToUTF8(this Span<char> t, bool append0 = false)\n\t\t=> Convert2.Utf8Encode(t, append0 ? \"\\0\" : null);\n\n\t/// <summary>\n\t/// Converts UTF-8 string to string.\n\t/// </summary>\n\tpublic static string ToStringUTF8(this byte[] t)\n\t\t=> Encoding.UTF8.GetString(t);\n\n\t/// <summary>\n\t/// Converts UTF-8 string to string.\n\t/// </summary>\n\tpublic static string ToStringUTF8(this RByte t)\n\t\t=> Encoding.UTF8.GetString(t);\n\n\t/// <summary>\n\t/// Converts UTF-8 string to string.\n\t/// </summary>\n\tpublic static string ToStringUTF8(this Span<byte> t)\n\t\t=> Encoding.UTF8.GetString(t);\n\n\t/// <summary>\n\t/// Splits this string. Trims, and removes empty.\n\t/// </summary>\n\t/// <returns>Array containing 0 or more strings.</returns>\n\tinternal static string[] Split_(this string t, char c) {\n\t\treturn t.Split(c, StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);\n\t}\n\n\t/// <summary>\n\t/// Splits this string and creates <c>HashSet</c>. Trims, and removes empty.\n\t/// </summary>\n\t/// <returns><c>HashSet</c> containing 0 or more strings.</returns>\n\tinternal static HashSet<string> SplitHS_(this string t, char c, bool ignoreCase) {\n\t\treturn t.Split_(c).ToHashSet(ignoreCase ? StringComparer.OrdinalIgnoreCase : StringComparer.Ordinal);\n\t}\n\n\t/// <summary>\n\t/// Splits string <i>s</i> into 2 strings.\n\t/// Tip: there are 2 overloads: for string and for <c>RStr</c>.\n\t/// </summary>\n\t/// <param name=\"t\"></param>\n\t/// <param name=\"c\">Separator character.</param>\n\t/// <param name=\"s1\">String at the left.</param>\n\t/// <param name=\"s2\">String at the right.</param>\n\t/// <param name=\"minLen1\">Minimal <i>s1</i> length.</param>\n\t/// <param name=\"minLen2\">Minimal <i>s2</i> length.</param>\n\t/// <returns>Returns <c>false</c> if character <i>c</i> not found or if strings are too short.</returns>\n\t/// <remarks>\n\t///\tCan be used to split strings like <c>\"name=value\"</c> or <c>\"name: value\"</c> or <c>\"s1 s2\"</c> etc. Trims spaces.\n\t/// </remarks>\n\tinternal static bool Split2_(this string t, char c, out string s1, out string s2, int minLen1, int minLen2) {\n\t\tif (!Split2_(t, c, out RStr k1, out RStr k2, minLen1, minLen2)) {\n\t\t\ts1 = s2 = null;\n\t\t\treturn false;\n\t\t}\n\t\ts1 = k1.ToString();\n\t\ts2 = k2.ToString();\n\t\treturn true;\n\t}\n\n\t/// <inheritdoc cref=\"Split2_(string, char, out string, out string, int, int)\"/>\n\tinternal static bool Split2_(this RStr t, char c, out RStr s1, out RStr s2, int minLen1, int minLen2) {\n\t\tt = t.Trim();\n\t\tint i = t.IndexOf(c);\n\t\tif (i >= 0) {\n\t\t\ts1 = t[..i].TrimEnd();\n\t\t\ts2 = t[++i..].TrimStart();\n\t\t\tif (s1.Length >= minLen1 && s2.Length >= minLen2) return true;\n\t\t}\n\t\ts1 = default;\n\t\ts2 = default;\n\t\treturn false;\n\t}\n\n\t/// <summary>\n\t/// If <i>index</i> is in this string, returns character at <i>index</i>. Else <c>'\\0'</c>.\n\t/// </summary>\n\tinternal static char At_(this string t, int index) => (uint)index < t.Length ? t[index] : default;\n\n\t/// <summary>\n\t/// If <i>index</i> is in this string span, returns character at <i>index</i>. Else <c>'\\0'</c>.\n\t/// </summary>\n\tinternal static char At_(this RStr t, int index) => (uint)index < t.Length ? t[index] : default;\n}\n\n/// <summary>\n/// Flags for <see cref=\"ExtString.ToInt\"/> and similar functions.\n/// </summary>\n[Flags]\npublic enum STIFlags {\n\t/// <summary>\n\t/// Don't support hexadecimal numbers (numbers with prefix <c>\"0x\"</c>).\n\t/// </summary>\n\tNoHex = 1,\n\n\t/// <summary>\n\t/// The number in string is hexadecimal without a prefix, like <c>\"1A\"</c>.\n\t/// </summary>\n\tIsHexWithout0x = 2,\n\n\t/// <summary>\n\t/// Fail if string starts with a whitespace character.\n\t/// </summary>\n\tDontSkipSpaces = 4,\n}\n\n/// <summary>\n/// Used with <see cref=\"ExtString.Upper(string, SUpper, CultureInfo)\"/>\n/// </summary>\npublic enum SUpper {\n\t/// <summary>\n\t/// Convert all characters to upper case.\n\t/// </summary>\n\tAllChars,\n\n\t/// <summary>\n\t/// Convert only the first character to upper case.\n\t/// </summary>\n\tFirstChar,\n\n\t/// <summary>\n\t/// Convert the first character of each word to upper case and other characters to lower case.\n\t/// Calls <see cref=\"TextInfo.ToTitleCase\"/>.\n\t/// </summary>\n\tTitleCase,\n}\n"
  },
  {
    "path": "Au/String/SegParser.cs",
    "content": "//Modified version of Microsoft.Extensions.Primitives.StringSegment. It is from github; current .NET does not have it, need to get from NuGet.\n//Can be used instead of String.Split, especially when you want less garbage. Faster (the github version with StringTokenizer was slower).\n\n#if !DEBUG\nnamespace Au.More {\n\t/// <summary>\n\t/// Splits a string into substrings as start/end offsets or strings.\n\t/// </summary>\n\t/// <remarks>\n\t/// Can be used with <c>foreach</c>. Normally you don't create <c>SegParser</c> instances explicitly; instead use <see cref=\"ExtString.Segments\"/> with <c>foreach</c>.\n\t/// </remarks>\n\t[EditorBrowsable(EditorBrowsableState.Never)] //obsolete. See comments in ExtString.Segments.\n\tpublic struct SegParser : IEnumerable<StartEnd>, IEnumerator<StartEnd> {\n\t\treadonly string _separators;\n\t\treadonly string _s;\n\t\treadonly int _sStart, _sEnd;\n\t\tint _start, _end;\n\t\tushort _sepLength;\n\t\tSegFlags _flags;\n\t\t\n\t\t/// <summary>\n\t\t/// Initializes this instance to split a string.\n\t\t/// </summary>\n\t\t/// <param name=\"s\">The string.</param>\n\t\t/// <param name=\"separators\">A string containing characters that delimit substrings. Or one of <see cref=\"SegSep\"/> constants.</param>\n\t\t/// <param name=\"flags\"></param>\n\t\t/// <param name=\"range\">Part of the string to split.</param>\n\t\tpublic SegParser(string s, string separators, SegFlags flags = 0, Range? range = null) {\n\t\t\t_separators = separators;\n\t\t\t_s = s;\n\t\t\t_sepLength = 1;\n\t\t\t_flags = flags;\n\t\t\t(_sStart, _sEnd) = range.GetStartEnd(s.Length);\n\t\t\t_start = 0;\n\t\t\t_end = _sStart - 1;\n\t\t}\n\t\t\n#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member\n\t\tpublic SegParser GetEnumerator() => this;\n\t\t\n\t\tIEnumerator<StartEnd> IEnumerable<StartEnd>.GetEnumerator() => this;\n\t\t\n\t\tIEnumerator IEnumerable.GetEnumerator() => this;\n\t\t\n\t\tpublic StartEnd Current => new(_start, _end);\n\t\t\n\t\tobject IEnumerator.Current => Current;\n\t\t\n\t\t[MethodImpl(MethodImplOptions.AggressiveOptimization)]\n\t\tpublic bool MoveNext() {\n\t\t\tgStart:\n\t\t\tint i = _end + _sepLength, to = _sEnd;\n\t\t\tif (i > to) return false;\n\t\t\t_start = i;\n\t\t\tstring s = _s, sep = _separators;\n\t\t\tswitch (sep.Length) {\n\t\t\tcase 1: {\n\t\t\t\t\tvar c = sep[0];\n\t\t\t\t\tfor (; i < to; i++) if (s[i] == c) goto g1;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 22:\n\t\t\t\tif (ReferenceEquals(sep, SegSep.Whitespace)) {\n\t\t\t\t\tfor (; i < to; i++)\n\t\t\t\t\t\tif (char.IsWhiteSpace(s[i])) goto g1;\n\t\t\t\t} else if (ReferenceEquals(sep, SegSep.Word)) {\n\t\t\t\t\tfor (; i < to; i++)\n\t\t\t\t\t\tif (!char.IsLetterOrDigit(s[i])) goto g1;\n\t\t\t\t} else if (ReferenceEquals(sep, SegSep.Line)) {\n\t\t\t\t\t_sepLength = 1;\n\t\t\t\t\tfor (; i < to; i++) {\n\t\t\t\t\t\tvar c = s[i];\n\t\t\t\t\t\tif (c > '\\r') continue;\n\t\t\t\t\t\tif (c == '\\r') goto g2; else if (c == '\\n') goto g1;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t\tg2:\n\t\t\t\t\tif (i < to - 1 && s[i + 1] == '\\n') _sepLength = 2;\n\t\t\t\t\tbreak;\n\t\t\t\t} else goto default;\n\t\t\t\tbreak;\n\t\t\tdefault: {\n\t\t\t\t\tfor (; i < to; i++) {\n\t\t\t\t\t\tvar c = s[i];\n\t\t\t\t\t\tfor (int j = 0; j < sep.Length; j++) if (c == sep[j]) goto g1; //speed: reverse slower\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tg1:\n\t\t\t_end = i;\n\t\t\tif (i == _start && 0 != (_flags & SegFlags.NoEmpty)) goto gStart;\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\tvoid IDisposable.Dispose() {\n\t\t\t//rejected. Normally this variable is not reused because GetEnumerator returns a copy.\n\t\t\t//_end = _sStart - 1;\n\t\t}\n\t\t\n\t\tpublic void Reset() {\n\t\t\t_end = _sStart - 1;\n\t\t}\n#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member\n\t\t\n\t\t/// <summary>\n\t\t/// Returns segment values as <c>string[]</c>.\n\t\t/// </summary>\n\t\t/// <param name=\"maxCount\">The maximal number of substrings to get. If negative (default), gets all. Else if there are more substrings, the last element will contain single substring, unlike with <see cref=\"String.Split\"/>.</param>\n\t\t[MethodImpl(MethodImplOptions.AggressiveOptimization)]\n\t\tpublic unsafe string[] ToStringArray(int maxCount = -1) {\n\t\t\t//All this big code is just to make this function as fast as String.Split. Also less garbage.\n\t\t\t//\tSimple code is slower when substrings are very short.\n\t\t\t//\tWith short substrings, the parsing code takes less than half of time. Creating strings and arrays is the slow part.\n\t\t\t\n\t\t\tstring100 a1 = new(); //at first use array on stack. When it is filled, use a2. Note: no [SkipLocalsInit], because references are always inited.\n\t\t\tList<string> a2 = null;\n\t\t\t\n\t\t\tint n = 0;\n\t\t\tfor (; MoveNext(); n++) { //slightly faster than foreach\n\t\t\t\tif (maxCount >= 0 && n == maxCount) break;\n\t\t\t\tvar s = _s[_start.._end];\n\t\t\t\tif (n < c_a1Size) {\n\t\t\t\t\ta1[n] = s;\n\t\t\t\t} else {\n\t\t\t\t\ta2 ??= new List<string>(c_a1Size * 2);\n\t\t\t\t\ta2.Add(s);\n\t\t\t\t}\n\t\t\t}\n\t\t\t_end = _sStart - 1; //reset, because this variable can be reused later. never mind: try/finally; it makes slightly slower.\n\t\t\t\n\t\t\tvar r = new string[n];\n\t\t\tfor (int i = 0, to = Math.Min(n, c_a1Size); i < to; i++) {\n\t\t\t\tr[i] = a1[i];\n\t\t\t\ta1[i] = null;\n\t\t\t}\n\t\t\tfor (int i = c_a1Size; i < r.Length; i++) {\n\t\t\t\tr[i] = a2[i - c_a1Size];\n\t\t\t}\n\t\t\treturn r;\n\t\t}\n\t\t\n\t\tconst int c_a1Size = 50;\n\t\t[InlineArray(c_a1Size)]\n\t\tstruct string100 { string s; }\n\t}\n}\n\nnamespace Au.Types {\n\t/// <summary>\n\t/// Contains several string constants that can be used with some \"split string\" functions of this library to specify separators.\n\t/// </summary>\n\t[EditorBrowsable(EditorBrowsableState.Never)] //obsolete\n\tpublic static class SegSep {\n\t\t/// <summary>\n\t\t/// Specifies that separators are spaces, tabs, newlines and other characters for which <see cref=\"char.IsWhiteSpace(char)\"/> returns <c>true</c>.\n\t\t/// </summary>\n\t\tpublic const string Whitespace = \"SSlkGrJUMUutrbSK3s6Crw\";\n\t\t\n\t\t/// <summary>\n\t\t/// Specifies that separators are all characters for which <see cref=\"char.IsLetterOrDigit(char)\"/> returns <c>false</c>.\n\t\t/// </summary>\n\t\tpublic const string Word = \"WWVL0EtrK0ShqYWb4n1CmA\";\n\t\t\n\t\t/// <summary>\n\t\t/// Specifies that separators are substrings <c>\"\\r\\n\"</c>, as well as single characters <c>'\\r'</c> and <c>'\\n'</c>.\n\t\t/// </summary>\n\t\tpublic const string Line = \"LLeg5AWCNkGTZDkWuyEa2g\";\n\t\t\n\t\t//note: all must be of length 22.\n\t}\n\t\n\t/// <summary>\n\t/// Flags for <see cref=\"ExtString.Segments\"/> and some other functions.\n\t/// </summary>\n\t[Flags]\n\t[EditorBrowsable(EditorBrowsableState.Never)] //obsolete\n\tpublic enum SegFlags : byte {\n\t\t/// <summary>\n\t\t/// Don't return empty substrings.\n\t\t/// For example, is string is <c>\"one  two \"</c> and separators is <c>\" \"</c>, return <c>{\"one\", \"two\"}</c> instead of <c>{\"one\", \"\", \"two\", \"\"}</c>.\n\t\t/// </summary>\n\t\tNoEmpty = 1,\n\t}\n}\n#endif\n"
  },
  {
    "path": "Au/String/StringUtil.cs",
    "content": "using System.Text.Json;\n\nnamespace Au.More;\n\n/// <summary>\n/// Miscellaneous rarely used string functions. Parsing etc.\n/// </summary>\npublic static class StringUtil {\n\t/// <summary>\n\t/// Parses a function parameter that can optionally have a <c>\"***name \"</c> prefix, like <c>\"***value xyz\"</c>.\n\t/// </summary>\n\t/// <returns>0 - <i>s</i> does not start with <c>\"***\"</c>; <c>i+1</c> - <i>s</i> starts with <c>\"***names[i] \"</c>; -1 - <i>s</i> is invalid.</returns>\n\t/// <param name=\"s\">Parameter. If starts with <c>\"***\"</c> and is valid, receives the <c>\"value\"</c> part; else unchanged. Can be <c>null</c>.</param>\n\t/// <param name=\"names\">List of supported <c>\"name\"</c>.</param>\n\t/// <remarks>\n\t/// Used to parse parameters like <i>name</i> of <see cref=\"wnd.Child\"/>.\n\t/// </remarks>\n\tinternal static int ParseParam3Stars_(ref string s, params ReadOnlySpan<string> names) {\n\t\tif (s == null || !s.Starts(\"***\")) return 0;\n\t\tfor (int i = 0; i < names.Length; i++) {\n\t\t\tvar ni = names[i];\n\t\t\tif (s.Length - 3 <= ni.Length || !s.Eq(3, ni)) continue;\n\t\t\tint j = 3 + ni.Length;\n\t\t\tchar c = s[j]; if (c != ' ') break;\n\t\t\ts = s[(j + 1)..];\n\t\t\treturn i + 1;\n\t\t}\n\t\treturn -1;\n\t}\n\t\n\t/// <summary>\n\t/// Removes characters used to underline next character when the text is displayed in UI. Replaces two such characters with single.\n\t/// </summary>\n\t/// <param name=\"s\">Can be <c>null</c>.</param>\n\t/// <param name=\"underlineChar\"></param>\n\t/// <remarks>\n\t/// Character <c>'&amp;'</c> (in WPF <c>'_'</c>) is used to underline next character in displayed text of dialog controls and menu items. Two such characters are used to display single.\n\t/// The underline is displayed when using the keyboard with <c>Alt</c> key to select dialog controls and menu items.\n\t/// </remarks>\n\t[SkipLocalsInit]\n\tpublic static unsafe string RemoveUnderlineChar(string s, char underlineChar = '&') {\n\t\tif (s != null && s.Contains(underlineChar)) {\n\t\t\tusing FastBuffer<char> b = new(s.Length);\n\t\t\tint j = 0; bool was = false;\n\t\t\tfor (int i = 0; i < s.Length; i++) {\n\t\t\t\tif (s[i] == underlineChar) {\n\t\t\t\t\tif (i < s.Length - 1 && s[i + 1] == underlineChar) i++;\n\t\t\t\t\telse if (!was) { was = underlineChar == '_'; continue; } //WPF removes only first single _\n\t\t\t\t}\n\t\t\t\tb.p[j++] = s[i];\n\t\t\t}\n\t\t\ts = new string(b.p, 0, j);\n\t\t}\n\t\treturn s;\n\t}\n\t\n\t/// <summary>\n\t/// Finds character used to underline next character when the text is displayed in UI.\n\t/// </summary>\n\t/// <returns>Character index, or -1 if not found.</returns>\n\t/// <param name=\"s\">Can be <c>null</c>.</param>\n\t/// <param name=\"underlineChar\"></param>\n\tpublic static int FindUnderlineChar(string s, char underlineChar = '&') {\n\t\tif (s != null) {\n\t\t\tfor (int i = 0; i < s.Length; i++) {\n\t\t\t\tif (s[i] == underlineChar) {\n\t\t\t\t\tif (++i < s.Length && s[i] != underlineChar) return i;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn -1;\n\t}\n\t\n\t/// <summary>\n\t/// Converts array of command line arguments to string that can be passed to a \"start process\" function, for example <see cref=\"run.it\"/>, <see cref=\"Process.Start\"/>.\n\t/// </summary>\n\t/// <returns><c>null</c> if <i>a</i> is <c>null</c> or empty.</returns>\n\t/// <param name=\"a\"></param>\n\tpublic static string CommandLineFromArray(string[] a) {\n\t\tif (a == null || a.Length == 0) return null;\n\t\tStringBuilder b = null;\n\t\tforeach (var v in a) {\n\t\t\tint esc = 0;\n\t\t\tif (v.NE()) esc = 1; else if (v.Contains('\"')) esc = 2; else foreach (var c in v) if (c <= ' ') { esc = 1; break; }\n\t\t\tif (esc == 0 && a.Length == 1) return a[0];\n\t\t\tif (b == null) b = new StringBuilder(); else b.Append(' ');\n\t\t\tif (esc == 0) b.Append(v);\n\t\t\telse {\n\t\t\t\tb.Append('\"');\n\t\t\t\tvar s = v;\n\t\t\t\tif (esc == 2) {\n\t\t\t\t\tif (s.Find(@\"\\\"\"\") < 0) s = s.Replace(@\"\"\"\", @\"\\\"\"\");\n\t\t\t\t\telse s = s.RxReplace(@\"(\\\\*)\"\"\", @\"$1$1\\\"\"\");\n\t\t\t\t}\n\t\t\t\tif (s.Ends('\\\\')) s = s.RxReplace(@\"(\\\\+)$\", \"$1$1\");\n\t\t\t\tb.Append(s).Append('\"');\n\t\t\t}\n\t\t}\n\t\treturn b.ToString();\n\t}\n\t\n\t/// <summary>\n\t/// Parses command line arguments.\n\t/// Calls API <ms>CommandLineToArgvW</ms>.\n\t/// </summary>\n\t/// <returns>Empty array if <i>s</i> is <c>null</c> or <c>\"\"</c>.</returns>\n\tpublic static unsafe string[] CommandLineToArray(string s) {\n\t\tif (s.NE()) return [];\n\t\tchar** p = Api.CommandLineToArgvW(s, out int n);\n\t\tvar a = new string[n];\n\t\tfor (int i = 0; i < n; i++) a[i] = new string(p[i]);\n\t\tApi.LocalFree(p);\n\t\treturn a;\n\t}\n\t\n\t/// <summary>\n\t/// If string contains a number at <i>startIndex</i>, gets that number as <c>int</c>, also gets the string part that follows it, and returns <c>true</c>.\n\t/// </summary>\n\t/// <param name=\"s\"></param>\n\t/// <param name=\"num\">Receives the number. Receives 0 if no number.</param>\n\t/// <param name=\"tail\">Receives the string part that follows the number, or <c>\"\"</c>. Receives <c>null</c> if no number. Can be this variable.</param>\n\t/// <param name=\"startIndex\">Offset in this string where to start parsing.</param>\n\t/// <param name=\"flags\"></param>\n\t/// <remarks>\n\t/// For example, for string <c>\"25text\"</c> or <c>\"25 text\"</c> gets <i>num</i> = <c>25</c>, <i>tail</i> = <c>\"text\"</c>.\n\t/// Everything else is the same as with <see cref=\"ExtString.ToInt(string, int, out int, STIFlags)\"/>.\n\t/// </remarks>\n\tpublic static bool ParseIntAndString(string s, out int num, out string tail, int startIndex = 0, STIFlags flags = 0) {\n\t\tnum = s.ToInt(startIndex, out int end, flags);\n\t\tif (end == 0) {\n\t\t\ttail = null;\n\t\t\treturn false;\n\t\t}\n\t\tif (end < s.Length && s[end] == ' ') end++;\n\t\ttail = s[end..];\n\t\treturn true;\n\t}\n\t\n\t/// <summary>\n\t/// Creates <c>int[]</c> from string containing space-separated numbers, like <c>\"4 100 -8 0x10\"</c>.\n\t/// </summary>\n\t/// <param name=\"s\">Decimal or/and hexadecimal numbers separated by single space. If <c>null</c> or <c>\"\"</c>, returns empty array.</param>\n\t/// <remarks>\n\t/// For vice versa use <c>string.Join(\" \", array)</c>.\n\t/// </remarks>\n\tpublic static int[] StringToIntArray(string s) {\n\t\tif (s.NE()) return [];\n\t\tint n = 1; foreach (var v in s) if (v == ' ') n++;\n\t\tvar a = new int[n];\n\t\ta[0] = s.ToInt(0, STIFlags.DontSkipSpaces);\n\t\tfor (int i = 0, j = 0; j < s.Length;) if (s[j++] == ' ') a[++i] = s.ToInt(j, STIFlags.DontSkipSpaces);\n\t\treturn a;\n\t}\n\t\n\t/// <summary>\n\t/// Converts character index in string to line index and character index in that line.\n\t/// </summary>\n\t/// <param name=\"s\"></param>\n\t/// <param name=\"index\">Character index in string <i>s</i>.</param>\n\t/// <param name=\"lineIndex\">Receives 0-based line index.</param>\n\t/// <param name=\"indexInLine\">Receives 0-based character index in that line.</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\"></exception>\n\tpublic static void LineAndColumn(string s, int index, out int lineIndex, out int indexInLine) {\n\t\tif ((uint)index > s.Length) throw new ArgumentOutOfRangeException();\n\t\tint line = 0, lineStart = 0;\n\t\tfor (int i = 0; i < index; i++) {\n\t\t\tchar c = s[i];\n\t\t\tif (c > '\\r') continue;\n\t\t\tif (c != '\\n') {\n\t\t\t\tif (c != '\\r') continue;\n\t\t\t\tif (i < s.Length - 1 && s[i + 1] == '\\n') continue;\n\t\t\t}\n\t\t\t\n\t\t\tlineStart = i + 1;\n\t\t\tline++;\n\t\t}\n\t\tlineIndex = line;\n\t\tindexInLine = index - lineStart;\n\t}\n\t\n\t/// <summary>\n\t/// Converts flags to string where flags are mapped to characters.\n\t/// </summary>\n\t/// <remarks>\n\t/// Don't use ASCII digits for flags, because <see cref=\"FlagsFromLetters\"/> interprets a number-string as the flags value. Any other characters are OK, not only letters.\n\t/// </remarks>\n\tpublic static string FlagsToLetters(int flags, params (int flag, char c)[] map) {\n\t\tSpan<char> a = stackalloc char[32];\n\t\tint len = 0;\n\t\tforeach (var (f, c) in map) {\n\t\t\tif (0 != (flags & f)) a[len++] = c;\n\t\t}\n\t\treturn a[..len].ToString();\n\t}\n\t\n\t/// <summary>\n\t/// Parses flags from string where flags are mapped to characters. See <see cref=\"FlagsToLetters\"/>\n\t/// </summary>\n\t/// <remarks>\n\t/// If the string is a number, interprets it as the flags value. Don't use ASCII digits for flags. Any other characters are OK, not only letters.\n\t/// </remarks>\n\tpublic static int FlagsFromLetters(string s, params (int flag, char c)[] map) {\n\t\tint r = 0;\n\t\tif (!s.NE() && !s.ToInt(out r)) {\n\t\t\tforeach (var (f, c) in map) {\n\t\t\t\tif (s.Contains(c)) r |= f;\n\t\t\t}\n\t\t}\n\t\treturn r;\n\t}\n\t\n\t/// <summary>\n\t/// Calculates the Levenshtein distance between two strings, which tells how much they are different.\n\t/// </summary>\n\t/// <remarks>\n\t/// It is the number of character edits (removals, inserts, replacements) that must occur to get from string <i>s1</i> to string <i>s2</i>.\n\t/// Can be used to measure similarity and match approximate strings with fuzzy logic.\n\t/// Uses code and info from <see href=\"https://www.dotnetperls.com/levenshtein\"/>.\n\t/// </remarks>\n\tpublic static int LevenshteinDistance(RStr s1, RStr s2) {\n\t\tint n = s1.Length;\n\t\tint m = s2.Length;\n\t\t\n\t\t// Step 1\n\t\tif (n == 0) return m;\n\t\tif (m == 0) return n;\n\t\t\n\t\t// Step 2\n\t\tint[,] d = new int[n + 1, m + 1];\n\t\tfor (int i = 0; i <= n; d[i, 0] = i++) { }\n\t\tfor (int j = 0; j <= m; d[0, j] = j++) { }\n\t\t\n\t\t// Step 3\n\t\tfor (int i = 1; i <= n; i++) {\n\t\t\t//Step 4\n\t\t\tfor (int j = 1; j <= m; j++) {\n\t\t\t\t// Step 5\n\t\t\t\tint cost = (s2[j - 1] == s1[i - 1]) ? 0 : 1;\n\t\t\t\t\n\t\t\t\t// Step 6\n\t\t\t\td[i, j] = Math.Min(\n\t\t\t\t\tMath.Min(d[i - 1, j] + 1, d[i, j - 1] + 1),\n\t\t\t\t\td[i - 1, j - 1] + cost);\n\t\t\t}\n\t\t}\n\t\t// Step 7\n\t\treturn d[n, m];\n\t}\n\t\n\t/// <summary>\n\t/// Returns the number of characters common to the start of each string.\n\t/// </summary>\n\tpublic static int CommonPrefix(RStr s1, RStr s2) {\n\t\tint n = Math.Min(s1.Length, s2.Length);\n\t\tfor (int i = 0; i < n; i++) {\n\t\t\tif (s1[i] != s2[i]) return i;\n\t\t}\n\t\treturn n;\n\t}\n\t\n\t/// <summary>\n\t/// Returns the number of characters common to the end of each string.\n\t/// </summary>\n\tpublic static int CommonSuffix(RStr s1, RStr s2) {\n\t\tint len1 = s1.Length;\n\t\tint len2 = s2.Length;\n\t\tint n = Math.Min(len1, len2);\n\t\tfor (int i = 1; i <= n; i++) {\n\t\t\tif (s1[len1 - i] != s2[len2 - i]) return i - 1;\n\t\t}\n\t\treturn n;\n\t}\n\t\n\t/// <summary>\n\t/// Converts JSON element to multiline indented JSON string.\n\t/// </summary>\n\tpublic static string JsonMultiline(JsonElement json) {\n\t\tvar so = s_jsOptions.Value;\n\t\treturn JsonSerializer.Serialize(json, so);\n\t}\n\t\n\t/// <summary>\n\t/// Converts single-line JSON string to multiline indented JSON string.\n\t/// </summary>\n\tpublic static string JsonMultiline(string json) {\n\t\tvar so = s_jsOptions.Value;\n\t\tvar v = JsonSerializer.Deserialize<JsonElement>(json, so);\n\t\treturn JsonSerializer.Serialize(v, so);\n\t}\n\t\n\tstatic readonly Lazy<JsonSerializerOptions> s_jsOptions = new(() => new() {\n\t\tWriteIndented = true\n\t\t//Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,\n\t});\n\t\n\tinternal static JsonSerializerOptions JsonOptions_ => s_jsOptions.Value;\n\t\n\t/// <summary>Calls <see cref=\"Encoding.GetEncoding(string)\"/>, and <see cref=\"Encoding.RegisterProvider\"/> if need.</summary>\n\t/// <returns><c>null</c> if failed.</returns>\n\tpublic static Encoding GetEncoding(string name) => _GetEncoding(name ?? throw new ArgumentNullException());\n\t\n\t/// <summary>Calls <see cref=\"Encoding.GetEncoding(int)\"/>, and <see cref=\"Encoding.RegisterProvider\"/> if need.</summary>\n\t/// <param name=\"codepage\">If -1, uses the current Windows ANSI code page (API <ms>GetACP</ms>).</param>\n\t/// <returns><c>null</c> if failed.</returns>\n\tpublic static Encoding GetEncoding(int codepage) => _GetEncoding(null, codepage == -1 ? Api.GetACP() : codepage);\n\t\n\tstatic Encoding _GetEncoding(string name = null, int codepage = 0) {\n\t\tg1:\n\t\ttry { return name != null ? Encoding.GetEncoding(name) : Encoding.GetEncoding(codepage); }\n\t\tcatch {\n\t\t\tif (Interlocked.CompareExchange(ref s_encodingInited, 1, 0) == 0) {\n\t\t\t\tEncoding.RegisterProvider(CodePagesEncodingProvider.Instance);\n\t\t\t\tgoto g1;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\tstatic int s_encodingInited;\n}\n"
  },
  {
    "path": "Au/String/csvTable.cs",
    "content": "namespace Au;\n\n/// <summary>\n/// Parses and composes CSV text. CSV table data in memory as a <c>List</c> of string arrays.\n/// </summary>\n/// <remarks>\n/// CSV is a text format used to store a single table of data in human-readable/editable way.\n/// It is a list of lines (called rows or records) containing one or more values (called fields or cells) separated by a separator character.\n/// \n/// There is no strictly defined CSV standard. This class uses these rules:\n/// <br/>• Fields containing separator characters (default <c>','</c>), quote characters (default <c>'\"'</c>) and multiple lines are enclosed in quote characters. Example: <c>\"ab, cd\"</c>.\n/// <br/>• Each quote character in such fields is escaped (replaced) with two quote characters. Example: <c>\"ab \"\"cd\"\" ef\"</c>.\n/// <br/>• If a field value starts or ends with ASCII space or tab characters, it is enclosed in quote characters. Example: <c>\" ab \"</c>. Or use parameter <i>trimSpaces</i> <c>false</c> when parsing.\n/// <br/>• Rows in CSV text can have different field count. All rows in in-memory CSV table have equal field count.\n/// </remarks>\n/// <example>\n/// <code><![CDATA[\n/// var c = new csvTable();\n/// c.AddRow(\"A\", \"B\");\n/// c.AddRow(\"C\", \"D\");\n/// var csv = c.ToString();\n/// print.it(csv);\n/// \n/// var k = csvTable.parse(csv);\n/// for (int row = 0; row < k.RowCount; row++) {\n/// \tprint.it(k[row, 0], k[row, 1]);\n/// }\n/// ]]></code>\n/// </example>\npublic class csvTable {\n\treadonly List<string[]> _a;\n\t\n\t/// <summary>\n\t/// Initializes new <see cref=\"csvTable\"/> variable that can be used to add rows.\n\t/// To create new variables from CSV text, file or dictionary, instead use static functions, for example <see cref=\"parse\"/>.\n\t/// </summary>\n\tpublic csvTable() { _a = new(); }\n\t\n\tcsvTable(List<string[]> a, int columnCount) { _a = a; _columnCount = columnCount; }\n\t\n\t/// <summary>\n\t/// Gets the internal <c>List</c> containing rows as string arrays.\n\t/// </summary>\n\t/// <remarks>\n\t/// It's not a copy; changing its content will change content of this <see cref=\"csvTable\"/> variable.\n\t/// You can do anything with the <c>List</c>. For example, sort it, find rows containing certain field values, get/set field values directly, add/remove rows directly.\n\t/// All row arrays have <c>Length</c> equal to <see cref=\"ColumnCount\"/>, and it must remain so; you can change <c>Length</c>, but then need to call <c>ColumnCount=newLength</c>.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// x.Rows.Sort((a,b) => string.CompareOrdinal(a[0], b[0]));\n\t/// ]]></code>\n\t/// </example>\n\tpublic List<string[]> Rows => _a;\n\t\n\t/// <summary>\n\t/// Sets or gets the field separator character used when composing CSV text.\n\t/// Initially it is <c>','</c>.\n\t/// </summary>\n\tpublic char Separator { get; set; } = ',';\n\t\n\t/// <summary>\n\t/// Sets or gets the quote character used when composing CSV text.\n\t/// Initially it is <c>'\"'</c>.\n\t/// </summary>\n\tpublic char Quote { get; set; } = '\"';\n\t\n\t/// <summary>\n\t/// Parses CSV string and creates new <see cref=\"csvTable\"/> variable that contains data in internal <c>List</c> of string arrays.\n\t/// </summary>\n\t/// <param name=\"csv\">\n\t/// CSV text.\n\t/// If rows in CSV text have different field count, the longest row sets the <see cref=\"ColumnCount\"/> property and lengths of all row arrays; array elements of missing CSV fields will be <c>null</c>.\n\t/// </param>\n\t/// <param name=\"separator\">Field separator character used in CSV text. Default <c>','</c>.</param>\n\t/// <param name=\"quote\">Character used in CSV text to enclose some fields. Default <c>'\"'</c>.</param>\n\t/// <param name=\"trimSpaces\">Ignore ASCII space and tab characters surrounding fields in CSV text. Default <c>true</c>.</param>\n\t/// <exception cref=\"FormatException\">Invalid CSV, eg contains incorrectly enclosed fields.</exception>\n\t[MethodImpl(MethodImplOptions.AggressiveOptimization)]\n\tpublic static unsafe csvTable parse(string csv, char separator = ',', char quote = '\"', bool trimSpaces = true) {\n\t\tif (csv.NE()) return new csvTable();\n\t\t\n\t\tvar a = new List<string[]>();\n\t\tvar tempRow = new List<string>(8);\n\t\tstring sQuote1 = null, sQuote2 = null;\n\t\t\n\t\tfixed (char* s0 = csv) {\n\t\t\tchar* s = s0, se = s0 + csv.Length;\n\t\t\tint nCol = 0;\n\t\t\t\n\t\t\tfor (; s < se; s++) {\n\t\t\t\t//Read a field.\n\t\t\t\tstring field = \"\";\n\t\t\t\tif (trimSpaces) { //ltrim\n\t\t\t\t\twhile (*s == ' ' || *s == '\\t') if (++s == se) goto g1;\n\t\t\t\t}\n\t\t\t\tchar* f, e; //field beginning and end, not including spaces\n\t\t\t\tbool hasEscapedQuote = false;\n\t\t\t\tif (*s == quote) {\n\t\t\t\t\tf = ++s;\n\t\t\t\t\tbool hasClosingQuote = false;\n\t\t\t\t\twhile (s < se) {\n\t\t\t\t\t\tif (*s++ == quote) {\n\t\t\t\t\t\t\tif (s < se && *s == quote) { s++; hasEscapedQuote = true; } //escaped quote\n\t\t\t\t\t\t\telse { hasClosingQuote = true; break; }\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (!hasClosingQuote) throw new FormatException($\"Invalid CSV format. Cells that start with {quote} must end with {quote}.\");\n\t\t\t\t\te = s - 1; //before quote\n\t\t\t\t\tif (trimSpaces) { //rtrim\n\t\t\t\t\t\twhile (s < se && (*s == ' ' || *s == '\\t')) s++;\n\t\t\t\t\t}\n\t\t\t\t\tif (s < se && !(*s == separator || *s == '\\n')) {\n\t\t\t\t\t\tif (*s == '\\r' && s < se + 1 && s[1] == '\\n') s++;\n\t\t\t\t\t\telse throw new FormatException($\"Invalid CSV format. For {quote} in enclosed cells use {quote}{quote}.\");\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tf = s; //field start\n\t\t\t\t\twhile (s < se && *s != separator && *s != '\\n') s++; //skip field, space and \\r\n\t\t\t\t\te = s; if (e > f && *e == '\\n' && e[-1] == '\\r') e--;\n\t\t\t\t\tif (trimSpaces) { //rtrim\n\t\t\t\t\t\twhile (e > f && (e[-1] == ' ' || e[-1] == '\\t')) e--;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfield = new string(f, 0, (int)(e - f)); //field to string\n\t\t\t\tif (hasEscapedQuote) {\n\t\t\t\t\tif (sQuote1 == null) { sQuote1 = new string(quote, 1); sQuote2 = new string(quote, 2); }\n\t\t\t\t\tfield = field.Replace(sQuote2, sQuote1);\n\t\t\t\t}\n\t\t\t\tg1:\n\t\t\t\t\n\t\t\t\t//print.it(field);\n\t\t\t\t\n\t\t\t\ttempRow.Add(field);\n\t\t\t\tif (s >= se || *s == '\\n') {\n\t\t\t\t\t//print.it(a.Count);\n\t\t\t\t\t//print.it(tempRow);\n\t\t\t\t\t\n\t\t\t\t\ta.Add(tempRow.ToArray());\n\t\t\t\t\tif (tempRow.Count > nCol) nCol = tempRow.Count;\n\t\t\t\t\ttempRow.Clear();\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tvar R = new csvTable(a, 0);\n\t\t\tR.ColumnCount = nCol; //make all rows of equal length and set _columnCount\n\t\t\treturn R;\n\t\t} //fixed\n\t}\n\t\n\t/// <summary>\n\t/// Composes CSV text from the internal <c>List</c> of string arrays.\n\t/// </summary>\n\t/// <remarks>\n\t/// Depends on these properties: <see cref=\"Separator\"/> (initially <c>','</c>), <see cref=\"Quote\"/> (initially <c>'\"'</c>).\n\t/// </remarks>\n\tpublic override string ToString() {\n\t\tif (RowCount == 0 || ColumnCount == 0) return \"\";\n\t\t\n\t\tusing (new StringBuilder_(out var b)) {\n\t\t\tchar quote = Quote;\n\t\t\tstring sQuote1 = null, sQuote2 = null;\n\t\t\t\n\t\t\tfor (int r = 0; r < _a.Count; r++) {\n\t\t\t\tfor (int c = 0; c < _columnCount; c++) {\n\t\t\t\t\tvar field = _a[r][c];\n\t\t\t\t\tif (!field.NE()) {\n\t\t\t\t\t\tbool hasQuote = field.Contains(quote);\n\t\t\t\t\t\tif (hasQuote || field.Contains(Separator) || field[0] == ' ' || field[^1] == ' ') {\n\t\t\t\t\t\t\tif (hasQuote) {\n\t\t\t\t\t\t\t\tif (sQuote1 == null) { sQuote1 = new string(quote, 1); sQuote2 = new string(quote, 2); }\n\t\t\t\t\t\t\t\tfield = field.Replace(sQuote1, sQuote2);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tb.Append(quote).Append(field).Append(quote);\n\t\t\t\t\t\t} else b.Append(field);\n\t\t\t\t\t}\n\t\t\t\t\tif (c < _columnCount - 1) b.Append(Separator);\n\t\t\t\t}\n\t\t\t\tb.AppendLine();\n\t\t\t}\n\t\t\treturn b.ToString();\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Gets or sets row count.\n\t/// The <c>get</c> function returns the <c>Count</c> property of the internal <c>List</c> of string arrays.\n\t/// The <c>set</c> function can add new rows or remove rows at the end.\n\t/// </summary>\n\tpublic int RowCount {\n\t\tget => _a.Count;\n\t\tset {\n\t\t\tif (value > _a.Count) {\n\t\t\t\t_a.Capacity = value;\n\t\t\t\twhile (_a.Count < value) _a.Add(_columnCount > 0 ? new string[_columnCount] : null);\n\t\t\t} else if (value < _a.Count) {\n\t\t\t\t_a.RemoveRange(value, _a.Count - value);\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Gets or sets column count.\n\t/// The <c>get</c> function returns the length of all string arrays in the internal <c>List</c>.\n\t/// The <c>set</c> function can add new columns or remove columns at the right.\n\t/// </summary>\n\tpublic int ColumnCount {\n\t\tget => _columnCount;\n\t\tset {\n\t\t\tif (value <= 0) throw new ArgumentOutOfRangeException();\n\t\t\t//if(value == _columnCount) return;\n\t\t\tfor (int r = 0; r < _a.Count; r++) {\n\t\t\t\tvar o = _a[r];\n\t\t\t\tif (o == null) _a[r] = new string[value];\n\t\t\t\telse if (o.Length != value) {\n\t\t\t\t\tvar t = new string[value];\n\t\t\t\t\tif (value > _columnCount) o.CopyTo(t, 0);\n\t\t\t\t\telse {\n\t\t\t\t\t\tfor (int c = 0; c < value; c++) t[c] = o[c];\n\t\t\t\t\t}\n\t\t\t\t\t_a[r] = t;\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t_columnCount = value;\n\t\t}\n\t}\n\tint _columnCount;\n\t\n\t/// <summary>\n\t/// Gets or sets a field.\n\t/// </summary>\n\t/// <param name=\"row\">0-based row index. The <c>set</c> function adds new row if negative or equal to <see cref=\"RowCount\"/>.</param>\n\t/// <param name=\"column\">0-based column index. With the <c>set</c> function it can be >= <see cref=\"ColumnCount\"/> and &lt; 1000; then sets <c>ColumnCount = column + 1</c>.</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\"></exception>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// var c = new csvTable { ColumnCount = 2 };\n\t/// c[0, 0] = \"A1\";\n\t/// c[0, 1] = \"A2\";\n\t/// c[1, 0] = \"B1\";\n\t/// c[1, 1] = \"B2\";\n\t/// print.it(c);\n\t/// string s = c[0, 0];\n\t/// print.it(s);\n\t/// ]]></code>\n\t/// </example>\n\tpublic string this[int row, int column] {\n\t\tget {\n\t\t\tif ((uint)column >= _columnCount) throw new ArgumentOutOfRangeException(\"column\");\n\t\t\tif ((uint)row >= RowCount) throw new ArgumentOutOfRangeException(\"row\");\n\t\t\t\n\t\t\treturn _a[row][column];\n\t\t}\n\t\tset {\n\t\t\t//Auto-add columns.\n\t\t\tif (column < 0) column = int.MaxValue;\n\t\t\tif (column >= _columnCount) {\n\t\t\t\tif (column >= 1000) throw new ArgumentOutOfRangeException(\"column\");\n\t\t\t\tColumnCount = column + 1;\n\t\t\t}\n\t\t\t//Auto-add row.\n\t\t\tif (row < 0) row = RowCount;\n\t\t\tif (row >= RowCount) {\n\t\t\t\tif (row > RowCount) throw new ArgumentOutOfRangeException(\"row\");\n\t\t\t\t_a.Add(new string[_columnCount]);\n\t\t\t}\n\t\t\t\n\t\t\t_a[row][column] = value;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Gets or sets a field.\n\t/// </summary>\n\t/// <param name=\"row\">0-based row index. Can be from the end; for example <c>^1</c> is the last row. The <c>set</c> function adds new row if <c>^0</c>.</param>\n\t/// <param name=\"column\">0-based column index. With the <c>set</c> function it can be >= <see cref=\"ColumnCount\"/> and &lt; 1000; then sets <c>ColumnCount = column + 1</c>.</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\"></exception>\n\tpublic string this[Index row, int column] {\n\t\tget => this[row.GetOffset(_a.Count), column];\n\t\tset { this[row.GetOffset(_a.Count), column] = value; }\n\t}\n\t\n\t/// <summary>\n\t/// Gets or sets fields in a row.\n\t/// </summary>\n\t/// <param name=\"row\">0-based row index. The <c>set</c> function adds new row if negative or equal to <see cref=\"RowCount\"/>.</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\"></exception>\n\t/// <remarks>\n\t/// The <c>get</c> function gets the row array. It's not a copy; changing its elements will change content of this <see cref=\"csvTable\"/> variable.\n\t/// The <c>set</c> function sets the row array. Does not copy the array, unless its <c>Length</c> is less than <see cref=\"ColumnCount\"/>.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// var c = new csvTable();\n\t/// c[-1] = [\"A\", \"B\"];\n\t/// c[-1] = [\"C\", \"D\"];\n\t/// print.it(c);\n\t/// for (int row = 0; row < c.RowCount; row++) {\n\t/// \tprint.it(c[row, 0], c[row, 1]);\n\t/// }\n\t/// ]]></code>\n\t/// </example>\n\tpublic string[] this[int row] {\n\t\tget {\n\t\t\tif ((uint)row >= RowCount) throw new ArgumentOutOfRangeException();\n\t\t\t\n\t\t\treturn _a[row];\n\t\t}\n\t\tset {\n\t\t\t//Auto-add row.\n\t\t\tif (row < 0) row = RowCount;\n\t\t\tif (row >= RowCount) {\n\t\t\t\tif (row > RowCount) throw new ArgumentOutOfRangeException();\n\t\t\t\t_a.Add(null);\n\t\t\t}\n\t\t\t\n\t\t\tvar t = value;\n\t\t\tif (value == null || value.Length < _columnCount) {\n\t\t\t\t//make row length = _columnCount\n\t\t\t\tt = new string[_columnCount];\n\t\t\t\tvalue?.CopyTo(t, 0);\n\t\t\t} else if (value.Length > _columnCount) {\n\t\t\t\t//auto-add columns\n\t\t\t\tColumnCount = value.Length;\n\t\t\t}\n\t\t\t\n\t\t\t_a[row] = t;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Gets or sets fields in a row.\n\t/// </summary>\n\t/// <param name=\"row\">0-based row index. Can be from the end; for example <c>^1</c> is the last row. The <c>set</c> function adds new row if <c>^0</c>.</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\"></exception>\n\t/// <remarks>\n\t/// The <c>get</c> function gets the row array. It's not a copy; changing its elements will change content of this <see cref=\"csvTable\"/> variable.\n\t/// The <c>set</c> function sets the row array. Does not copy the array, unless its <c>Length</c> is less than <see cref=\"ColumnCount\"/>.\n\t/// </remarks>\n\tpublic string[] this[Index row] {\n\t\tget => this[row.GetOffset(_a.Count)];\n\t\tset { this[row.GetOffset(_a.Count)] = value; }\n\t}\n\t\n\t/// <summary>\n\t/// Adds new row and sets its fields.\n\t/// </summary>\n\t/// <param name=\"fields\">Row fields. Can be a string array or multiple string arguments. Does not copy the array, unless its <c>Length</c> is less than <see cref=\"ColumnCount\"/>. Adds new columns if array <c>Length</c> (or the number of string arguments) is greater than <c>ColumnCount</c>.</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\"></exception>\n\tpublic void AddRow(params string[] fields) => InsertRow(-1, fields);\n\t\n\t/// <summary>\n\t/// Inserts new row and sets its fields.\n\t/// </summary>\n\t/// <param name=\"index\">0-based row index. If negative or equal to <see cref=\"RowCount\"/>, adds to the end.</param>\n\t/// <param name=\"fields\">Row fields. Can be a string array or multiple string arguments. Does not copy the array, unless its <c>Length</c> is less than <see cref=\"ColumnCount\"/>. Adds new columns if array <c>Length</c> (or the number of string arguments) is greater than <c>ColumnCount</c>.</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\"></exception>\n\tpublic void InsertRow(int index, params string[] fields) {\n\t\tif (index < 0) index = RowCount;\n\t\t_a.Insert(index, null);\n\t\tthis[index] = fields;\n\t}\n\t\n\t/// <summary>\n\t/// Inserts new empty row.\n\t/// </summary>\n\t/// <param name=\"index\">0-based row index. If negative or equal to <see cref=\"RowCount\"/>, adds to the end.</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\"></exception>\n\tpublic void InsertRow(int index) {\n\t\tInsertRow(index, null);\n\t}\n\t\n\t/// <summary>\n\t/// Removes one or more rows.\n\t/// </summary>\n\t/// <param name=\"index\">0-based row index.</param>\n\t/// <param name=\"count\">How many rows to remove, default 1.</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\"></exception>\n\t/// <exception cref=\"ArgumentException\"></exception>\n\tpublic void RemoveRow(int index, int count = 1) {\n\t\t_a.RemoveRange(index, count);\n\t}\n\t\n\t//FUTURE: implement these. Rarely used. Quite much code.\n\t//Don't need to implement many others because users can call our _a (the Data property) methods directly.\n\t//public void MoveRow(int from, int to)\n\t//{\n\t\n\t//}\n\t\n\t//public void InsertColumn(int index)\n\t//{\n\t\n\t//}\n\t\n\t//public void RemoveColumn(int index)\n\t//{\n\t\n\t//}\n\t\n\t/// <summary>\n\t/// Loads and parses a CSV file.\n\t/// </summary>\n\t/// <param name=\"file\">File. Must be full path. Can contain environment variables etc, see <see cref=\"pathname.expand\"/>.</param>\n\t/// <param name=\"separator\">Field separator character used in CSV text. Default <c>','</c>.</param>\n\t/// <param name=\"quote\">Character used in CSV text to enclose some fields. Default <c>'\"'</c>.</param>\n\t/// <param name=\"trimSpaces\">Ignore ASCII space and tab characters surrounding fields in CSV text. Default <c>true</c>.</param>\n\t/// <exception cref=\"ArgumentException\">Not full path.</exception>\n\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"File.ReadAllText(string)\"/>.</exception>\n\t/// <exception cref=\"FormatException\">Invalid CSV, eg contains incorrectly enclosed fields.</exception>\n\t/// <remarks>\n\t/// Calls <see cref=\"File.ReadAllText(string)\"/> and <see cref=\"parse\"/>. Also uses <see cref=\"filesystem.waitIfLocked\"/>.\n\t/// </remarks>\n\tpublic static csvTable load(string file, char separator = ',', char quote = '\"', bool trimSpaces = true) {\n\t\tvar csv = filesystem.loadText(file);\n\t\treturn parse(csv, separator, quote, trimSpaces);\n\t}\n\t\n\t/// <summary>\n\t/// Composes CSV and saves to a file.\n\t/// </summary>\n\t/// <param name=\"file\">File. Must be full path. Can contain environment variables etc, see <see cref=\"pathname.expand\"/>. The file can exist or not; this function overwrites it.</param>\n\t/// <param name=\"backup\">Create backup file named <c>file + \"~backup\"</c>.</param>\n\t/// <exception cref=\"ArgumentException\">Not full path.</exception>\n\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"File.WriteAllText(string, string)\"/>.</exception>\n\t/// <remarks>\n\t/// Calls <see cref=\"ToString\"/> and <see cref=\"File.WriteAllText(string, string)\"/>. Also uses <see cref=\"filesystem.save\"/>.\n\t/// </remarks>\n\tpublic void Save(string file, bool backup = false) {\n\t\tvar csv = ToString();\n\t\tfilesystem.saveText(file, csv, backup);\n\t}\n\t\n\t/// <summary>\n\t/// Creates 2-column CSV table from dictionary keys and values of type <c>string</c>.\n\t/// </summary>\n\t/// <param name=\"d\"></param>\n\t/// <exception cref=\"ArgumentNullException\"></exception>\n\tpublic static csvTable fromDictionary(Dictionary<string, string> d) {\n\t\tNot_.Null(d);\n\t\tvar a = new List<string[]>(d.Count);\n\t\tforeach (var v in d) a.Add(new string[] { v.Key, v.Value });\n\t\treturn new csvTable(a, 2);\n\t}\n\t\n\t/// <summary>\n\t/// Creates 2-column CSV table from dictionary keys and values of any type, using a callback function to convert values to <c>string</c>.\n\t/// </summary>\n\t/// <param name=\"d\"></param>\n\t/// <param name=\"valueToString\">Callback function that converts value of type <c>T</c> to <c>string</c>.</param>\n\t/// <exception cref=\"ArgumentNullException\"></exception>\n\tpublic static csvTable fromDictionary<T>(Dictionary<string, T> d, Func<T, string> valueToString) {\n\t\tNot_.Null(d, valueToString);\n\t\tvar a = new List<string[]>(d.Count);\n\t\tforeach (var v in d) {\n\t\t\tvar t = valueToString(v.Value);\n\t\t\ta.Add(new string[] { v.Key, t });\n\t\t}\n\t\treturn new csvTable(a, 2);\n\t}\n\t\n\t/// <summary>\n\t/// Creates CSV table of any column count from dictionary keys and values of any type, using a callback function to convert values to cell strings.\n\t/// </summary>\n\t/// <param name=\"d\"></param>\n\t/// <param name=\"columnCount\">CSV column count. Must be 2 or more.</param>\n\t/// <param name=\"valueToCells\">Callback function that converts value of type <c>T</c> to one or more strings and puts them in row array elements starting from index 1. At index 0 is key.</param>\n\t/// <exception cref=\"ArgumentNullException\"></exception>\n\t/// <exception cref=\"ArgumentOutOfRangeException\"><i>columnCount</i> less than 2.</exception>\n\tpublic static csvTable fromDictionary<T>(Dictionary<string, T> d, int columnCount, Action<T, string[]> valueToCells) {\n\t\tNot_.Null(d, valueToCells);\n\t\tif (columnCount < 2) throw new ArgumentOutOfRangeException();\n\t\tvar a = new List<string[]>(d.Count);\n\t\tforeach (var v in d) {\n\t\t\tvar t = new string[columnCount];\n\t\t\tt[0] = v.Key;\n\t\t\tvalueToCells(v.Value, t);\n\t\t\ta.Add(t);\n\t\t}\n\t\treturn new csvTable(a, columnCount);\n\t}\n\t\n\t/// <summary>\n\t/// Creates dictionary from this 2-column CSV table.\n\t/// </summary>\n\t/// <param name=\"ignoreCase\">Case-insensitive dictionary keys.</param>\n\t/// <param name=\"ignoreDuplicates\">Don't throw exception if column 0 contains duplicate strings. Replace old value with new value.</param>\n\t/// <exception cref=\"InvalidOperationException\"><see cref=\"ColumnCount\"/> not 2.</exception>\n\t/// <exception cref=\"ArgumentException\">Column 0 contains duplicate strings.</exception>\n\tpublic Dictionary<string, string> ToDictionary(bool ignoreCase, bool ignoreDuplicates) {\n\t\tif (_columnCount != 2) throw new InvalidOperationException(\"ColumnCount must be 2\");\n\t\tvar d = new Dictionary<string, string>(ignoreCase ? StringComparer.OrdinalIgnoreCase : null);\n\t\tforeach (var v in _a) {\n\t\t\tif (ignoreDuplicates) d[v[0]] = v[1];\n\t\t\telse d.Add(v[0], v[1]);\n\t\t}\n\t\treturn d;\n\t}\n\t\n\t/// <summary>\n\t/// Creates dictionary from this CSV table of any column count, using a callback function to convert cell strings to dictionary values of any type.\n\t/// </summary>\n\t/// <param name=\"ignoreCase\">Case-insensitive dictionary keys.</param>\n\t/// <param name=\"ignoreDuplicates\">Don't throw exception if column 0 contains duplicate strings. Replace old value with new value.</param>\n\t/// <param name=\"rowToValue\">Callback function that converts one or more cell strings to single value of type <c>T</c>. The array is whole row; element 0 is key, and usually is not used.</param>\n\t/// <exception cref=\"ArgumentNullException\"></exception>\n\t/// <exception cref=\"InvalidOperationException\"><see cref=\"ColumnCount\"/> less than 2.</exception>\n\t/// <exception cref=\"ArgumentException\">Column 0 contains duplicate strings.</exception>\n\tpublic Dictionary<string, T> ToDictionary<T>(bool ignoreCase, bool ignoreDuplicates, Func<string[], T> rowToValue) {\n\t\tNot_.Null(rowToValue);\n\t\tif (_columnCount < 2) throw new InvalidOperationException(\"ColumnCount must be >= 2\");\n\t\tvar d = new Dictionary<string, T>(ignoreCase ? StringComparer.OrdinalIgnoreCase : null);\n\t\tforeach (var v in _a) {\n\t\t\tvar t = rowToValue(v);\n\t\t\tif (ignoreDuplicates) d[v[0]] = t;\n\t\t\telse d.Add(v[0], t);\n\t\t}\n\t\treturn d;\n\t}\n\t\n\t//rejected, because: 1. In some cases can fail to resolve overloads. 2. Almost duplicate of the string[] overload.\n\t///// <summary>\n\t///// Creates dictionary from this 2-column CSV table, using a callback function to convert cell strings to dictionary values of any type.\n\t///// </summary>\n\t///// <param name=\"ignoreCase\">Case-insensitive dictionary keys.</param>\n\t///// <param name=\"stringToValue\">Callback function that converts cell string to value of type <c>T</c>.</param>\n\t///// <exception cref=\"ArgumentNullException\"></exception>\n\t///// <exception cref=\"InvalidOperationException\"><see cref=\"ColumnCount\"/> not 2.</exception>\n\t///// <exception cref=\"ArgumentException\">Column 0 contains duplicate values.</exception>\n\t//public Dictionary<string, T> ToDictionary<T>(bool ignoreCase, Func<string, T> stringToValue)\n\t//{\n\t//\tif(stringToValue == null) throw new ArgumentNullException();\n\t//\tif(_columnCount != 2) throw new InvalidOperationException(\"ColumnCount must be 2\");\n\t//\tvar d = new Dictionary<string, T>(ignoreCase ? StringComparer.OrdinalIgnoreCase : null);\n\t//\tforeach(var v in _a) d.Add(v[0], stringToValue(v[1]));\n\t//\treturn d;\n\t//}\n\t\n\t/// <summary>\n\t/// Obsolete, use <see cref=\"Set\"/>.\n\t/// </summary>\n\t[EditorBrowsable(EditorBrowsableState.Never)]\n\tpublic void SetInt(int row, int column, int value, bool hex = false) {\n\t\tthis[row, column] = hex ? \"0x\" + value.ToString(\"X\") : value.ToString();\n\t}\n\t\n\t/// <summary>\n\t/// Obsolete, use <see cref=\"Get\"/>.\n\t/// </summary>\n\t[EditorBrowsable(EditorBrowsableState.Never)]\n\tpublic int GetInt(int row, int column) {\n\t\treturn this[row, column].ToInt();\n\t}\n\t\n\t/// <summary>\n\t/// Obsolete, use <see cref=\"Set\"/>.\n\t/// </summary>\n\t[EditorBrowsable(EditorBrowsableState.Never)]\n\tpublic void SetDouble(int row, int column, double value) {\n\t\tthis[row, column] = value.ToS();\n\t}\n\t\n\t/// <summary>\n\t/// Obsolete, use <see cref=\"Get\"/>.\n\t/// </summary>\n\t[EditorBrowsable(EditorBrowsableState.Never)]\n\tpublic double GetDouble(int row, int column) {\n\t\tthis[row, column].ToNumber(out double R);\n\t\treturn R;\n\t}\n\t\n\t/// <summary>\n\t/// Converts a number to string and sets a field.\n\t/// </summary>\n\t/// <param name=\"row\">0-based row index. Can be from the end; for example <c>^1</c> is the last row. Adds new row if <c>^0</c>.</param>\n\t/// <param name=\"column\">0-based column index. If >= <see cref=\"ColumnCount\"/> and &lt; 1000, sets <c>ColumnCount = column + 1</c>.</param>\n\t/// <param name=\"value\"></param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Invalid <i>row</i> or <i>column</i>.</exception>\n\tpublic void Set(Index row, int column, int value) { this[row, column] = value.ToS(); }\n\t\n\t/// <summary>\n\t/// Converts a number to hex string and sets a field.\n\t/// </summary>\n\t/// <inheritdoc cref=\"Set(Index, int, int)\"/>\n\tpublic void Set(Index row, int column, uint value) { this[row, column] = \"0x\" + value.ToString(\"X\"); }\n\t\n\t/// <inheritdoc cref=\"Set(Index, int, int)\"/>\n\tpublic void Set(Index row, int column, long value) { this[row, column] = value.ToS(); }\n\t\n\t/// <inheritdoc cref=\"Set(Index, int, uint)\"/>\n\tpublic void Set(Index row, int column, ulong value) { this[row, column] = \"0x\" + value.ToString(\"X\"); }\n\t\n\t/// <inheritdoc cref=\"Set(Index, int, int)\"/>\n\tpublic void Set(Index row, int column, double value) { this[row, column] = value.ToS(); }\n\t\n\t/// <inheritdoc cref=\"Set(Index, int, int)\"/>\n\tpublic void Set(Index row, int column, float value) { this[row, column] = value.ToS(); }\n\t\n\t/// <summary>\n\t/// Converts a <c>bool</c> to string <c>\"true\"</c> or <c>\"false\"</c> and sets a field.\n\t/// </summary>\n\t/// <inheritdoc cref=\"Set(Index, int, int)\"/>\n\tpublic void Set(Index row, int column, bool value) { this[row, column] = value ? \"true\" : \"false\"; }\n\t\n\t/// <summary>\n\t/// Gets a field value converted to int. See <see cref=\"ExtString.ToInt(string, out int, int, STIFlags)\"/>.\n\t/// </summary>\n\t/// <param name=\"row\">0-based row index.</param>\n\t/// <param name=\"column\">0-based column index.</param>\n\t/// <param name=\"value\">Receives the result, or 0 if failed.</param>\n\t/// <returns>False if failed to convert from string.</returns>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Invalid <i>row</i> or <i>column</i>.</exception>\n\tpublic bool Get(Index row, int column, out int value) => this[row, column].ToInt(out value);\n\t\n\t/// <summary>\n\t/// Gets a field value converted to <c>uint</c>. See <see cref=\"ExtString.ToInt(string, out uint, int, STIFlags)\"/>.\n\t/// </summary>\n\t/// <inheritdoc cref=\"Get(Index, int, out int)\"/>\n\tpublic bool Get(Index row, int column, out uint value) => this[row, column].ToInt(out value);\n\t\n\t/// <summary>\n\t/// Gets a field value converted to <c>long</c>. See <see cref=\"ExtString.ToInt(string, out long, int, STIFlags)\"/>.\n\t/// </summary>\n\t/// <inheritdoc cref=\"Get(Index, int, out int)\"/>\n\tpublic bool Get(Index row, int column, out long value) => this[row, column].ToInt(out value);\n\t\n\t/// <summary>\n\t/// Gets a field value converted to <c>ulong</c>. See <see cref=\"ExtString.ToInt(string, out ulong, int, STIFlags)\"/>.\n\t/// </summary>\n\t/// <inheritdoc cref=\"Get(Index, int, out int)\"/>\n\tpublic bool Get(Index row, int column, out ulong value) => this[row, column].ToInt(out value);\n\t\n\t/// <summary>\n\t/// Gets a field value converted to double. See <see cref=\"ExtString.ToNumber(string, out double, Range?, NumberStyles)\"/>.\n\t/// </summary>\n\t/// <inheritdoc cref=\"Get(Index, int, out int)\"/>\n\tpublic bool Get(Index row, int column, out double value) => this[row, column].ToNumber(out value);\n\t\n\t/// <summary>\n\t/// Gets a field value converted to float. See <see cref=\"ExtString.ToNumber(string, out float, Range?, NumberStyles)\"/>.\n\t/// </summary>\n\t/// <inheritdoc cref=\"Get(Index, int, out int)\"/>\n\tpublic bool Get(Index row, int column, out float value) => this[row, column].ToNumber(out value);\n\t\n\t/// <summary>\n\t/// Gets a field value like <c>\"true\"</c> or <c>\"false\"</c> converted to <c>bool</c>. Case-insensitive.\n\t/// </summary>\n\t/// <inheritdoc cref=\"Get(Index, int, out int)\"/>\n\tpublic bool Get(Index row, int column, out bool value) => Boolean.TryParse(this[row, column], out value);\n\t\n\t//rejected: ToXml, ToHtml. Could be pasted in Excel, but need special format, difficult to make fully compatible. OpenOffice supports only HTML.\n}\n"
  },
  {
    "path": "Au/String/regexp.cs",
    "content": "\nusing System.Text.RegularExpressions; //for XML doc links\n\nnamespace Au;\n\n/// <summary>\n/// PCRE regular expression.\n/// </summary>\n/// <remarks>\n/// PCRE is a regular expression library: <see href=\"https://www.pcre.org/\"/>.\n/// PCRE regular expression syntax: <see href=\"https://www.pcre.org/current/doc/html/pcre2pattern.html\">full</see>, <see href=\"https://www.pcre.org/current/doc/html/pcre2syntax.html\">short</see>.\n/// Some websites with tutorials and info: <see href=\"https://www.rexegg.com/\">rexegg</see>, <see href=\"https://www.regular-expressions.info/\">regular-expressions.info</see>.\n/// \n/// This class is an alternative to the .NET <see cref=\"Regex\"/> class. The regular expression syntax is similar. PCRE has some features unavailable in .NET, and vice versa. In most cases PCRE is faster. You can use any of these classes. Functions of <see cref=\"elm\"/> class support only PCRE.\n/// \n/// Terms used in this documentation and in names of functions and types:\n/// - <i>regular expression</i> - regular expression string. Also known as <i>pattern</i>.\n/// - <i>subject string</i> - the string in which to search for the regular expression. Also known as <i>input string</i>.\n/// - <i>match</i> - the part (substring) of the subject string that matches the regular expression.\n/// - <i>groups</i> - regular expression parts enclosed in <c>()</c>. Except non-capturing parts, like <c>(?:...)</c> and <c>(?options)</c>. Also known as <i>capturing group</i>, <i>capturing subpattern</i>. Often term <i>group</i> also is used for group matches.\n/// - <i>group match</i> - the part (substring) of the subject string that matches the group. Also known as <i>captured substring</i>.\n/// \n/// This library uses an unmanaged code dll <c>AuCpp.dll</c> that contains PCRE code. This class is a managed wrapper for it. The main PCRE API functions used by this class are <see href=\"https://www.pcre.org/current/doc/html/pcre2api.html\">pcre2_compile and pcre2_match</see>. The <c>regexp</c> constructor calls <c>pcre2_compile</c> and stores the compiled code in the variable. Other <c>regexp</c> functions call <c>pcre2_match</c>. Compiling to native code (JIT) is not supported.\n/// \n/// A <c>regexp</c> variable can be used by multiple threads simultaneously.\n/// \n/// Also there are several <see cref=\"String\"/> extension methods that use this class. The string variable is the subject string. These methods create and use cached <c>regexp</c> instances for speed. The <c>regexp</c> constructor does not use caching.\n/// </remarks>\n/// <example>\n/// <code><![CDATA[\n/// var s = \"one two22, three333,four\"; //subject string\n/// var x = new regexp(@\"\\b(\\w+?)(\\d+)\\b\"); //regular expression\n///  \n///  print.it(\"//IsMatch:\");\n/// print.it(x.IsMatch(s));\n///  \n///  print.it(\"//Match:\");\n/// if(x.Match(s, out var m)) print.it(m.Value, m[1].Value, m[2].Value);\n///  \n///  print.it(\"//FindAll with foreach:\");\n/// foreach(var v in x.FindAll(s)) print.it(v.Value, v[1].Value, v[2].Value);\n///  print.it(\"//FindAll, get only strings of group 2:\");\n/// print.it(x.FindAll(s, 2));\n///  \n///  print.it(\"//Replace:\");\n/// print.it(x.Replace(s, \"'$2$1'\"));\n///  print.it(\"//Replace with callback:\");\n/// print.it(x.Replace(s, o => o.Value.Upper()));\n///  print.it(\"//Replace with callback and ExpandReplacement:\");\n/// print.it(x.Replace(s, o => { if(o.Length > 5) return o.ExpandReplacement(\"'$2$1'\"); else return o[1].Value; }));\n///  \n///  print.it(\"//Split:\");\n/// print.it(new regexp(@\" *, *\").Split(s));\n/// ]]></code>\n///  Examples with <see cref=\"String\"/> extension methods. \n/// <code><![CDATA[\n/// var s = \"one two22, three333,four\"; //subject string\n/// var rx = @\"\\b(\\w+?)(\\d+)\\b\"; //regular expression\n///  \n///  print.it(\"//RxIsMatch:\");\n/// print.it(s.RxIsMatch(rx));\n///  \n///  print.it(\"//RxMatch:\");\n/// if(s.RxMatch(rx, out var m)) print.it(m.Value, m[1].Value, m[2].Value);\n///  \n///  print.it(\"//RxMatch, get only string:\");\n/// if(s.RxMatch(rx, 0, out var s0)) print.it(s0);\n///  print.it(\"//RxMatch, get only string of group 1:\");\n/// if(s.RxMatch(rx, 1, out var s1)) print.it(s1);\n///  \n///  print.it(\"//RxFindAll with foreach:\");\n/// foreach(var v in s.RxFindAll(rx)) print.it(v.Value, v[1].Value, v[2].Value);\n///  \n///  print.it(\"//RxFindAll with foreach, get only strings:\");\n/// foreach(var v in s.RxFindAll(rx, 0)) print.it(v);\n///  print.it(\"//RxFindAll with foreach, get only strings of group 2:\");\n/// foreach(var v in s.RxFindAll(rx, 2)) print.it(v);\n///  \n///  print.it(\"//RxFindAll, get array:\");\n/// if(s.RxFindAll(rx, out var am)) foreach(var k in am) print.it(k.Value, k[1].Value, k[2].Value);\n///  \n///  print.it(\"//RxFindAll, get array of strings:\");\n/// if(s.RxFindAll(rx, 0, out var av)) print.it(av);\n///  print.it(\"//RxFindAll, get array of group 2 strings:\");\n/// if(s.RxFindAll(rx, 2, out var ag)) print.it(ag);\n///  \n///  print.it(\"//RxReplace:\");\n/// print.it(s.RxReplace(rx, \"'$2$1'\"));\n///  \n///  print.it(\"//RxReplace with callback:\");\n/// print.it(s.RxReplace(rx, o => o.Value.Upper()));\n///  print.it(\"//RxReplace with callback and ExpandReplacement:\");\n/// print.it(s.RxReplace(rx, o => { if(o.Length > 5) return o.ExpandReplacement(\"'$2$1'\"); else return o[1].Value; }));\n///  \n///  print.it(\"//RxReplace, get replacement count:\");\n/// if(0 != s.RxReplace(rx, \"'$2$1'\", out var s2)) print.it(s2);\n///  \n///  print.it(\"//RxReplace with callback, get replacement count:\");\n/// if(0 != s.RxReplace(rx, o => o.Value.Upper(), out var s3)) print.it(s3);\n///  \n///  print.it(\"//RxSplit:\");\n/// print.it(s.RxSplit(@\" *, *\"));\n/// ]]></code></example>\npublic unsafe class regexp {\n\treadonly IntPtr _codeUnsafe; //pcre2_code_16*. Don't pass to PCRE API directly, because then GC can collect this object\n\tCpp.PcreCalloutT _pcreCallout; //our callout that calls the user's callout. This field protects the delegates from GC.\n\treadonly byte _matchFlags; //RXMatchFlags specified in hi byte of ctor flags\n\t\n\tinternal HandleRef _CodeHR => new HandleRef(this, _codeUnsafe); //pass this to PCRE API\n\t\n\t/// <summary>\n\t/// Compiles regular expression string.\n\t/// </summary>\n\t/// <param name=\"rx\">Regular expression. Cannot be <c>null</c>.</param>\n\t/// <param name=\"flags\">\n\t/// Options.\n\t/// Default 0. Flag UTF is implicitly added if <i>rx</i> contains non-ASCII characters and not used flag <c>NEVER_UTF</c>.\n\t/// </param>\n\t/// <exception cref=\"ArgumentNullException\"></exception>\n\t/// <exception cref=\"ArgumentException\">Invalid regular expression. Or failed to compile it for some other reason (unlikely).</exception>\n\t/// <remarks>\n\t/// Calls PCRE API function <see href=\"https://www.pcre.org/current/doc/html/pcre2api.html\">pcre2_compile</see>.\n\t/// \n\t/// PCRE regular expression syntax: <see href=\"https://www.pcre.org/current/doc/html/pcre2pattern.html\">full</see>, <see href=\"https://www.pcre.org/current/doc/html/pcre2syntax.html\">short</see>.\n\t/// \n\t/// Examples in class help: <see cref=\"regexp\"/>.\n\t/// </remarks>\n\tpublic regexp([ParamString(PSFormat.Regexp)] string rx, RXFlags flags = 0) {\n\t\tNot_.Null(rx);\n\t\t_matchFlags = (byte)((ulong)flags >> 56); flags = (RXFlags)((ulong)flags & 0xffffff_ffffffff);\n\t\t_codeUnsafe = Cpp.Cpp_RegexCompile(rx, rx.Length, flags, out int codeSize, out BSTR errStr);\n\t\tif (_codeUnsafe == default) throw new ArgumentException(errStr.ToStringAndDispose());\n\t\tGC.AddMemoryPressure(codeSize);\n\t}\n\t\n\t///\n\t~regexp() {\n\t\t//print.it(\"dtor\");\n\t\tif (_codeUnsafe == default) return;\n\t\tint codeSize = Cpp.Cpp_RegexDtor(_codeUnsafe);\n\t\tGC.RemoveMemoryPressure(codeSize);\n\t}\n\t\n\t/// <summary>\n\t/// Sets callout callback function.\n\t/// </summary>\n\t/// <value>Callback delegate (eg lambda) or <c>null</c>.</value>\n\t/// <remarks>\n\t/// Callouts can be used to:\n\t/// <br/>• Track the matching progress.\n\t/// <br/>• Get all instances of a group that can match multiple times.\n\t/// <br/>• Evaluate and reject some matches or match parts.\n\t/// <br/>• Etc.\n\t/// The callback function is called by <see cref=\"IsMatch\"/>, <see cref=\"Match\"/>, <see cref=\"FindAll\"/>, <see cref=\"Replace\"/>, <see cref=\"Split\"/> and similar functions, when they reach callout points in regular expression. To insert callout points use <c>(?C)</c>, <c>(?C1)</c>, <c>(?C2)</c>, <c>(?C'name')</c> etc or pass flag <c>AUTO_CALLOUT</c> to the constructor.\n\t/// More info in PCRE help topic <see href=\"https://www.pcre.org/current/doc/html/pcre2callout.html\">pcre2callout</see>.\n\t/// See also: <see href=\"https://www.rexegg.com/pcre-callouts.html\"/>\n\t/// </remarks>\n\t/// <example>\n\t/// Track the matching progress.\n\t/// <code><![CDATA[\n\t/// var s = \"text <a href='url'>link</a> text\";\n\t/// var rx = @\"(?C1)<a (?C2)href='.+?'>(?C3)[^<]*(?C4)</a>\";\n\t/// var x = new regexp(rx);\n\t/// x.Callout = o => { print.it(o.callout_number, o.start_match, o.current_position, s[o.start_match..o.current_position], rx.Substring(o.pattern_position, o.next_item_length)); };\n\t/// print.it(x.IsMatch(s));\n\t/// ]]></code>\n\t/// Track the matching progress with flag <c>AUTO_CALLOUT</c>.\n\t/// <code><![CDATA[\n\t/// var s = \"one 'two' three\";\n\t/// var rx = @\"'(.+?)'\";\n\t/// var x = new regexp(rx, RXFlags.AUTO_CALLOUT);\n\t/// x.Callout = o => print.it(o.current_position, o.pattern_position, rx.Substring(o.pattern_position, o.next_item_length));\n\t/// print.it(x.IsMatch(s));\n\t/// ]]></code>\n\t/// Get all instances of a group that can match multiple times.\n\t/// <code><![CDATA[\n\t/// var s = \"BEGIN 111 2222 333 END\";\n\t/// var x = new regexp(@\"^(\\w+) (?:(\\d+) (?C1))+(\\w+)$\");\n\t/// var a = new List<string>();\n\t/// x.Callout = o => a.Add(o.LastGroupValue);\n\t/// if(!x.Match(s, out var m)) { print.it(\"no match\"); return; }\n\t/// print.it(m[1]);\n\t/// print.it(a); //all numbers. m[2] contains only the last number.\n\t/// print.it(m[3]);\n\t/// ]]></code>\n\t/// Evaluate and reject some matches or match parts. This code rejects matches longer than 5.\n\t/// <code><![CDATA[\n\t/// var s = \"one 123-5 two 12-456 three 1-34 four\";\n\t/// var x = new regexp(@\"\\b\\d+-\\d+\\b(?C1)\");\n\t/// x.Callout = o => { int len = o.current_position - o.start_match; /*print.it(len);*/ if(len > 5) o.Result = 1; };\n\t/// print.it(x.FindAll(s, 0));\n\t/// ]]></code>\n\t/// </example>\n\tpublic Action<RXCalloutData> Callout {\n\t\tset {\n\t\t\tlock (this) {\n\t\t\t\tif (value == null) {\n\t\t\t\t\t_pcreCallout = null;\n\t\t\t\t} else {\n\t\t\t\t\t_pcreCallout = (void* calloutBlock, void* param) => {\n\t\t\t\t\t\tvar b = new RXCalloutData(calloutBlock);\n\t\t\t\t\t\tvalue(b);\n\t\t\t\t\t\treturn b.Result;\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Finds a named group and returns its 1-based index. Returns -1 if not found.\n\t/// </summary>\n\t/// <param name=\"groupName\">\n\t/// Group name.\n\t/// In regular expression, to set name of group <c>(text)</c>, use <c>(?&lt;NAME&gt;text)</c>.\n\t/// </param>\n\t/// <exception cref=\"ArgumentNullException\"></exception>\n\t/// <exception cref=\"ArgumentException\">Multiple groups have this name.</exception>\n\t/// <seealso cref=\"RXMatch.GroupNumberFromName(string)\"/>\n\t/// <seealso cref=\"RXMatch.GroupNumberFromName(string, out bool)\"/>\n\tpublic int GetGroupNumberOf(string groupName) {\n\t\tNot_.Null(groupName);\n\t\tfixed (char* p = groupName) {\n\t\t\tint R = Cpp.pcre2_substring_nametable_scan(_CodeHR, p, null, null);\n\t\t\tif (R <= 0) {\n\t\t\t\tif (R == -50) throw new ArgumentException(\"Multiple groups have name \" + groupName); //-50 PCRE2_ERROR_NOUNIQUESUBSTRING\n\t\t\t\tR = -1;\n\t\t\t}\n\t\t\treturn R;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Returns the highest capture group number in the regular expression. If <c>(?|</c> not used, this is also the total count of capture groups.\n\t/// </summary>\n\tpublic int GetMaxGroupNumber() {\n\t\tint R = 0;\n\t\tCpp.pcre2_pattern_info(_CodeHR, Cpp.PCRE2_INFO_.CAPTURECOUNT, &R);\n\t\treturn R;\n\t}\n\t\n\t//Calls Cpp_RegexMatch and returns its results.\n\t//Throws if it returns less than -1.\n\t//m.vec array is thread_local. Next call reallocates/overwrites it, except when called by a callout of the same call.\n\t//m.mark is set even if no match, if available.\n\t//s - subject. If null, returns -1.\n\t//rawFlags - pass flags as is. If false, calls _GetMatchFlags. If true, flags must be result of _GetMatchFlags.\n\t//group - 0 or group number. Used only to throw if invalid.\n\tint _PcreMatch(RStr s, int start, RXMatchFlags flags, bool rawFlags, out Cpp.RegexMatch m, bool needM, int group = 0) {\n\t\tif (s.IsNull()) { m = default; return -1; } //null\n\t\tfixed (char* p = s) {\n\t\t\tif (!rawFlags) flags = _GetMatchFlags(flags);\n\t\t\tint rc = Cpp.Cpp_RegexMatch(_CodeHR, p != null ? p : (char*)&p, s.Length, start, flags, _pcreCallout, out m, needM, out BSTR errStr);\n\t\t\tif (rc < -1) throw new AuException(errStr.ToStringAndDispose());\n\t\t\tif (group != 0 && rc >= 0 && (uint)group >= m.vecCount) throw new ArgumentOutOfRangeException(nameof(group));\n\t\t\treturn rc;\n\t\t\t//print.it(rc);\n\t\t\t//info: 0 is partial match, -1 is no match, <-1 is error\n\t\t}\n\t}\n\t\n\t//Gets span and returns start.\n\t//If range is null, sets span = s and returns 0.\n\t//Else if range is invalid, throws ArgumentOutOfRangeException.\n\t//Else sets span.Length = range and returns 0. In any case span.Start is 0.\n\tstatic int _GetSpan(string s, Range? range, out RStr span) {\n\t\tspan = s;\n\t\tif (!range.HasValue) return 0;\n\t\tvar (i, end) = range.GetStartEnd(span.Length);\n\t\tif (end != span.Length) span = span[..end];\n\t\treturn i;\n\t}\n\t\n\tstatic int _GetSpan(ref RStr span, Range? range) {\n\t\tif (!range.HasValue) return 0;\n\t\tvar (i, end) = range.GetStartEnd(span.Length);\n\t\tif (end != span.Length) span = span[..end];\n\t\treturn i;\n\t}\n\t\n\tRXMatchFlags _GetMatchFlags(RXMatchFlags matchFlags, bool throwIfPartial = false) {\n\t\tvar f = (RXMatchFlags)_matchFlags | matchFlags;\n\t\tif (throwIfPartial) {\n\t\t\tif (0 != (f & (RXMatchFlags.PARTIAL_SOFT | RXMatchFlags.PARTIAL_HARD)))\n\t\t\t\tthrow new ArgumentException(\"This function does not support PARTIAL_ flags.\", nameof(matchFlags));\n\t\t}\n\t\treturn f;\n\t}\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if string <i>s</i> matches this regular expression.\n\t/// </summary>\n\t/// <returns><c>true</c> if full or partial match. Partial match is possible if used a <c>PARTIAL_</c> flag.</returns>\n\t/// <param name=\"s\">\n\t/// Subject string.\n\t/// If <c>null</c>, returns <c>false</c>, even if the regular expression matches empty string.\n\t/// </param>\n\t/// <param name=\"range\">\n\t/// Start and end offsets in the subject string. If <c>null</c> (default), uses whole string.\n\t/// Examples: <c>i..j</c> (from <c>i</c> to <c>j</c>), <c>i..</c> (from <c>i </c>to the end), <c>..j</c> (from 0 to <c>j</c>).\n\t/// The subject part before the start index is not ignored if regular expression starts with a lookbehind assertion or anchor, eg <c>^</c> or <c>\\b</c> or <c>(?&lt;=...)</c>. Instead of <c>^</c> you can use <c>\\G</c> or flag <c>RXFlags.ANCHORED</c>. More info in PCRE documentation topic <see href=\"https://www.pcre.org/current/doc/html/pcre2api.html\">pcre2api</see>, chapter \"The string to be matched by pcre2_match()\".\n\t/// The subject part after the end index is always ignored.\n\t/// </param>\n\t/// <param name=\"matchFlags\">Options.\n\t/// The same options also can be set in <see cref=\"regexp\"/> constructor's <i>flags</i>. Constructor's flags and <i>matchFlags</i> are added, which means that <i>matchFlags</i> cannot unset flags set by constructor.\n\t/// </param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Invalid <i>range</i>.</exception>\n\t/// <exception cref=\"AuException\">The PCRE API function <c>pcre2_match</c> failed. Unlikely.</exception>\n\t/// <remarks>\n\t/// This function is similar to <see cref=\"Regex.IsMatch(string)\"/>.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// var s = \"one two22 three333 four\";\n\t/// var x = new regexp(@\"\\b(\\w+?)(\\d+)\\b\");\n\t/// print.it(x.IsMatch(s));\n\t/// ]]></code>\n\t/// </example>\n\tpublic bool IsMatch(string s, Range? range = null, RXMatchFlags matchFlags = 0) {\n\t\tint start = _GetSpan(s, range, out var span);\n\t\treturn _PcreMatch(span, start, matchFlags, rawFlags: false, out _, needM: false) >= 0;\n\t}\n\t\n\t/// <inheritdoc cref=\"IsMatch(string, Range?, RXMatchFlags)\"/>\n\tpublic bool IsMatch(RStr s, Range? range = null, RXMatchFlags matchFlags = 0) {\n\t\tint start = _GetSpan(ref s, range);\n\t\treturn _PcreMatch(s, start, matchFlags, rawFlags: false, out _, needM: false) >= 0;\n\t}\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if string <i>s</i> matches this regular expression.\n\t/// Gets match info as <see cref=\"RXMatch\"/>.\n\t/// </summary>\n\t/// <returns>\n\t/// <br/>• If full match, returns <c>true</c>, and <i>result</i> contains the match and all groups that exist in the regular expressions.\n\t/// <br/>• If partial match, returns <c>true</c>, and <i>result</i> contains the match without groups. Partial match is possible if used a <c>PARTIAL_</c> flag.\n\t/// <br/>• If no match, returns <c>false</c>, and <i>result</i> normally is <c>null</c>. But if a mark is available, <i>result</i> is an object with two valid properties - <see cref=\"RXMatch.Exists\"/> (<c>false</c>) and <see cref=\"RXMatch.Mark\"/>; other properties have undefined values or throw exception.\n\t/// </returns>\n\t/// <param name=\"result\">Receives match info.</param>\n\t/// <remarks>\n\t/// This function is similar to <see cref=\"Regex.Match(string)\"/>.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// var s = \"one two22 three333 four\";\n\t/// var x = new regexp(@\"\\b(\\w+?)(\\d+)\\b\");\n\t/// if(x.Match(s, out var m)) print.it(m.Value, m[1].Value, m[2].Value);\n\t/// ]]></code>\n\t/// </example>\n\t/// <inheritdoc cref=\"IsMatch\"/>\n\tpublic bool Match(string s, out RXMatch result, Range? range = null, RXMatchFlags matchFlags = 0) {\n\t\tresult = null;\n\t\tint start = _GetSpan(s, range, out var span);\n\t\tint rc = _PcreMatch(span, start, matchFlags, rawFlags: false, out var m, needM: true);\n\t\tif (rc >= 0 || m.mark != null) {\n\t\t\tresult = new RXMatch(this, s, rc, in m);\n\t\t}\n\t\treturn rc >= 0;\n\t}\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if string <i>s</i> matches this regular expression.\n\t/// Gets whole match or some group, as <see cref=\"RXGroup\"/> (index, length, value).\n\t/// </summary>\n\t/// <returns>\n\t/// <br/>• If full match, returns <c>true</c>, and <i>result</i> contains the match or the specified group.\n\t/// <br/>• If partial match, returns <c>true</c>. Partial match is possible if used a <c>PARTIAL_</c> flag. Then cannot get groups, therefore <i>group</i> should be 0.\n\t/// <br/>• If no match, returns <c>false</c>, and <i>result</i> is empty.\n\t/// </returns>\n\t/// <param name=\"s\">\n\t/// Subject string.\n\t/// If <c>null</c>, returns <c>false</c>, even if the regular expression matches empty string.\n\t/// </param>\n\t/// <param name=\"group\">\n\t/// Group number (1-based index) of result. If 0 - whole match.\n\t/// See also <see cref=\"GetGroupNumberOf\"/>.\n\t/// </param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Invalid <i>group</i> or <i>range</i>.</exception>\n\t/// <exception cref=\"AuException\">The PCRE API function <c>pcre2_match</c> failed. Unlikely.</exception>\n\t/// <remarks>\n\t/// This function is a simplified version of <see cref=\"Match(string, out RXMatch, Range?, RXMatchFlags)\"/>.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// var s = \"one two22 three333 four\";\n\t/// var x = new regexp(@\"\\b(\\w+?)(\\d+)\\b\");\n\t/// if(x.Match(s, 0, out RXGroup g)) print.it(g.Value, g.Start);\n\t/// ]]></code>\n\t/// </example>\n\t/// <inheritdoc cref=\"Match(string, out RXMatch, Range?, RXMatchFlags)\"/>\n\tpublic bool Match(string s, int group, out RXGroup result, Range? range = null, RXMatchFlags matchFlags = 0) {\n\t\tint start = _GetSpan(s, range, out var span);\n\t\tint rc = _PcreMatch(span, start, matchFlags, rawFlags: false, out var m, needM: true, group);\n\t\tif (rc < 0) {\n\t\t\tresult = default;\n\t\t\treturn false;\n\t\t}\n\t\tresult = new RXGroup(s, m.vec[group]);\n\t\treturn true;\n\t}\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if string <i>s</i> matches this regular expression.\n\t/// Gets whole match or some group, as string.\n\t/// </summary>\n\t/// <returns>\n\t/// <br/>• If full match, returns <c>true</c>, and <i>result</i> contains the value of the match or of the specifed group.\n\t/// <br/>• If partial match, returns <c>true</c>. Partial match is possible if used a <c>PARTIAL_</c> flag. Then cannot get groups, therefore <i>group</i> should be 0.\n\t/// <br/>• If no match, returns <c>false</c>, and <i>result</i> is <c>null</c>.\n\t/// </returns>\n\t/// <param name=\"s\">\n\t/// Subject string.\n\t/// If <c>null</c>, returns <c>false</c>, even if the regular expression matches empty string.\n\t/// </param>\n\t/// <param name=\"result\">Receives the match value.</param>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// var s = \"one two22 three333 four\";\n\t/// var x = new regexp(@\"\\b(\\w+?)(\\d+)\\b\");\n\t/// if(x.Match(s, 0, out string v)) print.it(v);\n\t/// ]]></code>\n\t/// </example>\n\t/// <inheritdoc cref=\"Match(string, int, out RXGroup, Range?, RXMatchFlags)\"/>\n\tpublic bool Match(string s, int group, out string result, Range? range = null, RXMatchFlags matchFlags = 0) {\n\t\tresult = null;\n\t\tif (!Match(s, group, out RXGroup g, range, matchFlags)) return false;\n\t\tresult = g.Value;\n\t\treturn true;\n\t}\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if string span <i>s</i> matches this regular expression.\n\t/// Gets whole match or some group, as <see cref=\"StartEnd\"/>.\n\t/// </summary>\n\t/// <inheritdoc cref=\"Match(string, int, out RXGroup, Range?, RXMatchFlags)\"/>\n\tpublic bool Match(RStr s, int group, out StartEnd result, Range? range = null, RXMatchFlags matchFlags = 0) {\n\t\tint start = _GetSpan(ref s, range);\n\t\tint rc = _PcreMatch(s, start, matchFlags, rawFlags: false, out var m, needM: true, group);\n\t\tif (rc < 0) {\n\t\t\tresult = default;\n\t\t\treturn false;\n\t\t}\n\t\tresult = m.vec[group];\n\t\treturn true;\n\t}\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if string span <i>s</i> matches this regular expression.\n\t/// Writes match info to caller-allocated memory (array, stackalloc array, etc).\n\t/// </summary>\n\t/// <param name=\"result\">Receives match info: main match in <c>result[0]</c> and group matches in other elements. <c>result.Length</c> must be equal to the number of groups + 1. If a group does not exists, the element's <c>start</c> and <c>end</c> are <c>-1</c>.</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Invalid <i>range</i>.</exception>\n\t/// <exception cref=\"AuException\">The PCRE API function <c>pcre2_match</c> failed. Unlikely.</exception>\n\t/// <exception cref=\"ArgumentException\"><i>result</i> array too short.</exception>\n\t/// <inheritdoc cref=\"Match(string, out RXMatch, Range?, RXMatchFlags)\" path=\"/param\"/>\n\tpublic bool Match(RStr s, Span<StartEnd> result, Range? range = null, RXMatchFlags matchFlags = 0) {\n\t\tint start = _GetSpan(ref s, range);\n\t\tint rc = _PcreMatch(s, start, matchFlags, rawFlags: false, out var m, needM: true);\n\t\tif (rc >= 0 || m.mark != null) {\n\t\t\tif (result.Length < m.vecCount) throw new ArgumentException(\"result array too short\");\n\t\t\tnew Span<StartEnd>(m.vec, m.vecCount).CopyTo(result);\n\t\t}\n\t\treturn rc >= 0;\n\t}\n\t\n\t///// <inheritdoc cref=\"Match(string, out RXMatch, Range?, RXMatchFlags)\"/>\n\t///// <param name=\"result\">Receives match info. Its \"get string value\" functions cannot be used.</param>\n\t//public bool Match(RStr s, out RXMatch result, Range? range = null, RXMatchFlags matchFlags = 0) {\n\t//\tresult = null;\n\t//\tint start = _GetSpan(ref s, range);\n\t//\tint rc = _PcreMatch(s, start, matchFlags, rawFlags: false, out var m, needM: true);\n\t//\tif (rc >= 0 || m.mark != null) {\n\t//\t\tresult = new RXMatch(this, null, rc, in m);\n\t//\t}\n\t//\treturn rc >= 0;\n\t//}\n\t\n\t//Used by FindAllX and ReplaceAllX to easily find matches in loop.\n\tstruct _MatchEnum {\n\t\tregexp _rx;\n\t\tstring _subject;\n\t\tCpp.RegexMatch _m;\n\t\tRXMatchFlags _matchFlags;\n\t\tint _group, _from, _to, _maxCount, _rc;\n\t\tpublic int foundCount;\n\t\t\n\t\t//Throws if s is null or if invalid start/end or used 'partial' flags.\n\t\tpublic _MatchEnum(regexp rx, string s, int group, Range? range, RXMatchFlags matchFlags, int maxCount = -1) {\n\t\t\tNot_.Null(s);\n\t\t\t(_from, _to) = range.GetStartEnd(s.Length);\n\t\t\t_rx = rx;\n\t\t\t_subject = s;\n\t\t\t_group = group;\n\t\t\t_matchFlags = rx._GetMatchFlags(matchFlags, throwIfPartial: true);\n\t\t\t_maxCount = maxCount;\n\t\t\tfoundCount = _rc = 0;\n\t\t\t_m = default;\n\t\t}\n\t\t\n\t\t//Calls Cpp_RegexMatch, remembers its results, increments foundCount if found.\n\t\t//Returns false if it returns -1. Throws if it returns < -1. Throws if invalid group.\n\t\t//To get results, use properties Match or GroupX. Don't call Next or any other match function before it.\n\t\tpublic bool Next() {\n\t\t\tif (foundCount >= (uint)_maxCount) return false;\n\t\t\t_rc = _rx._PcreMatch(_subject.AsSpan(0, _to), _from, _matchFlags, rawFlags: true, out _m, needM: true, _group);\n\t\t\tif (_rc < 0) return false;\n\t\t\t_SetNextFrom();\n\t\t\t_matchFlags |= RXMatchFlags.NO_UTF_CHECK;\n\t\t\tfoundCount++;\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\tvoid _SetNextFrom() {\n\t\t\tvar p = _m.vec[0]; //x=start, y=end\n\t\t\t_from = p.end;\n\t\t\t//empty match?\n\t\t\tif (_from <= p.start) {\n\t\t\t\tif (_from < p.start) throw new ArgumentException(@\"This function does not support (?=...\\K).\");\n\t\t\t\tif (++_from < _to) {\n\t\t\t\t\tvar c = _subject[_from];\n\t\t\t\t\tif (c == '\\n') { //skip \\n if inside \\r\\n\n\t\t\t\t\t\tif (_subject[_from - 1] == '\\r') _from++;\n\t\t\t\t\t} else if ((c & 0xfc00) == 0xdc00) { //skip the second part of surrogate pair\n\t\t\t\t\t\tif (0 != (_rx._InfoAllOptions & RXFlags.UTF)) _from++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (_from > _to) _maxCount = 0;\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic RXMatch Match => new RXMatch(_rx, _subject, _rc, in _m);\n\t\t\n\t\tpublic StartEnd GroupR => _m.vec[_group];\n\t\t\n\t\tpublic RXGroup GroupG => new(_subject, GroupR);\n\t\t\n\t\tpublic string GroupS { get { var r = GroupR; return r.start < 0 ? null : _subject[r.start..r.end]; } }\n\t}\n\t\n\t/// <summary>\n\t/// Finds all match instances of the regular expression.\n\t/// </summary>\n\t/// <returns>A lazy <c>IEnumerable&lt;RXMatch&gt;</c> that can be used with <c>foreach</c>.</returns>\n\t/// <param name=\"s\">Subject string. Cannot be <c>null</c>.</param>\n\t/// <exception cref=\"ArgumentNullException\"><i>s</i> is <c>null</c>.</exception>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Invalid <i>range</i>.</exception>\n\t/// <exception cref=\"ArgumentException\">\n\t/// <br/>• Used a <c>PARTIAL_</c> flag.\n\t/// <br/>• The regular expression contains <c>(?=...\\K)</c>.\n\t/// </exception>\n\t/// <exception cref=\"AuException\">The PCRE API function <c>pcre2_match</c> failed. Unlikely.</exception>\n\t/// <remarks>\n\t/// This function is similar to <see cref=\"Regex.Matches(string)\"/>.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// var s = \"one two22 three333 four\";\n\t/// var x = new regexp(@\"\\b(\\w+?)(\\d+)\\b\");\n\t/// foreach(var m in x.FindAll(s)) print.it(m.Value, m[1].Value, m[2].Value);\n\t/// ]]></code>\n\t/// </example>\n\t/// <inheritdoc cref=\"IsMatch\" path=\"/param\"/>\n\tpublic IEnumerable<RXMatch> FindAll(string s, Range? range = null, RXMatchFlags matchFlags = 0) {\n\t\tvar e = new _MatchEnum(this, s, 0, range, matchFlags);\n\t\twhile (e.Next()) yield return e.Match;\n\t}\n\t\n\t/// <returns>A lazy <c>IEnumerable&lt;string&gt;</c> that can be used with <c>foreach</c>.</returns>\n\t/// <param name=\"group\">\n\t/// Group number (1-based index) of results. If 0 - whole match.\n\t/// See also <see cref=\"GetGroupNumberOf\"/>.\n\t/// </param>\n\t/// <exception cref=\"ArgumentNullException\"><i>s</i> is <c>null</c>.</exception>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Invalid <i>group</i> or <i>range</i>.</exception>\n\t/// <exception cref=\"ArgumentException\">\n\t/// <br/>• Used a <c>PARTIAL_</c> flag.\n\t/// <br/>• The regular expression contains <c>(?=...\\K)</c>.\n\t/// </exception>\n\t/// <exception cref=\"AuException\">The PCRE API function <c>pcre2_match</c> failed. Unlikely.</exception>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// var s = \"one two three\";\n\t/// var x = new regexp(@\"\\b\\w+\\b\");\n\t/// foreach(var v in x.FindAll(s, 0)) print.it(v);\n\t/// ]]></code>\n\t/// </example>\n\t/// <inheritdoc cref=\"FindAll(string, Range?, RXMatchFlags)\"/>\n\tpublic IEnumerable<string> FindAll(string s, int group, Range? range = null, RXMatchFlags matchFlags = 0) {\n\t\tvar e = new _MatchEnum(this, s, group, range, matchFlags);\n\t\twhile (e.Next()) yield return e.GroupS;\n\t}\n\t\n\t/// <returns>A lazy <c>IEnumerable&lt;RXGroup&gt;</c> that can be used with <c>foreach</c>.</returns>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// var s = \"one two three\";\n\t/// var x = new regexp(@\"\\b\\w+\\b\");\n\t/// foreach(var g in x.FindAllG(s, 0)) print.it(g.Start, g.Value);\n\t/// ]]></code>\n\t/// </example>\n\t/// <inheritdoc cref=\"FindAll(string, int, Range?, RXMatchFlags)\"/>\n\tpublic IEnumerable<RXGroup> FindAllG(string s, int group, Range? range = null, RXMatchFlags matchFlags = 0) {\n\t\tvar e = new _MatchEnum(this, s, group, range, matchFlags);\n\t\twhile (e.Next()) yield return e.GroupG;\n\t}\n\t\n\t/// <summary>\n\t/// Finds all match instances of the regular expression. Gets array of <see cref=\"RXMatch\"/>.\n\t/// </summary>\n\t/// <returns><c>true</c> if found 1 or more matches.</returns>\n\t/// <param name=\"result\">Receives all found matches.</param>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// var s = \"one two22 three333 four\";\n\t/// var x = new regexp(@\"\\b(\\w+?)(\\d+)\\b\");\n\t/// if(!x.FindAll(s, out var a)) { print.it(\"not found\"); return; }\n\t/// foreach(var m in a) print.it(m.Value, m[1].Value, m[2].Value);\n\t/// ]]></code>\n\t/// </example>\n\t/// <inheritdoc cref=\"FindAll(string, Range?, RXMatchFlags)\"/>\n\tpublic bool FindAll(string s, out RXMatch[] result, Range? range = null, RXMatchFlags matchFlags = 0) {\n\t\tresult = FindAll(s, range, matchFlags).ToArray();\n\t\treturn result.Length != 0;\n\t}\n\t\n\t/// <summary>\n\t/// Finds all match instances of the regular expression. Gets array of strings.\n\t/// </summary>\n\t/// <returns><c>true</c> if found 1 or more matches.</returns>\n\t/// <param name=\"result\">Receives all found matches.</param>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// var s = \"one two three\";\n\t/// var x = new regexp(@\"\\b\\w+\\b\");\n\t/// if(!x.FindAll(s, 0, out var a)) { print.it(\"not found\"); return; }\n\t/// foreach(var v in a) print.it(v);\n\t/// ]]></code>\n\t/// </example>\n\t/// <inheritdoc cref=\"FindAll(string, int, Range?, RXMatchFlags)\"/>\n\tpublic bool FindAll(string s, int group, out string[] result, Range? range = null, RXMatchFlags matchFlags = 0) {\n\t\tresult = FindAll(s, group, range, matchFlags).ToArray();\n\t\treturn result.Length != 0;\n\t}\n\t\n\t/// <summary>\n\t/// Finds all match instances of the regular expression. Gets array of <see cref=\"RXGroup\"/> (index, length, value).\n\t/// </summary>\n\t/// <returns><c>true</c> if found 1 or more matches.</returns>\n\t/// <param name=\"result\">Receives all found matches.</param>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// var s = \"one two three\";\n\t/// var x = new regexp(@\"\\b\\w+\\b\");\n\t/// if(!x.FindAllG(s, 0, out var a)) { print.it(\"not found\"); return; }\n\t/// foreach(var g in a) print.it(g.Start, g.Value);\n\t/// ]]></code>\n\t/// </example>\n\t/// <inheritdoc cref=\"FindAll(string, int, Range?, RXMatchFlags)\"/>\n\tpublic bool FindAllG(string s, int group, out RXGroup[] result, Range? range = null, RXMatchFlags matchFlags = 0) {\n\t\tresult = FindAllG(s, group, range, matchFlags).ToArray();\n\t\treturn result.Length != 0;\n\t}\n\t\n\tint _Replace(string s, out string result, string repl, Func<RXMatch, string> replFunc, int maxCount, Range? range, RXMatchFlags matchFlags) {\n\t\tStringBuilder b = null;\n\t\tStringBuilder_ bCache = default;\n\t\tint prevEnd = 0;\n\t\tint replType = 0; //0 empty, 1 simple, 2 with $, 3 callback\n\t\t\n\t\tvar e = new _MatchEnum(this, s, 0, range, matchFlags, maxCount);\n\t\twhile (e.Next()) {\n\t\t\t//init variables\n\t\t\tif (b == null) {\n\t\t\t\tbCache = new StringBuilder_(out b, s.Length + 100);\n\t\t\t\tif (replFunc != null) replType = 3; else if (!repl.NE()) replType = repl.IndexOf('$') < 0 ? 1 : 2;\n\t\t\t}\n\t\t\t//append s part before this match\n\t\t\tvar p = e.GroupR; //x=start, y=end\n\t\t\tint nBefore = p.start - prevEnd;\n\t\t\tif (nBefore != 0) b.Append(s, prevEnd, nBefore);\n\t\t\tprevEnd = p.end;\n\t\t\t//append replacement\n\t\t\tstring re = null;\n\t\t\tif (replType >= 2) {\n\t\t\t\tvar m = e.Match; //FUTURE: optimization: if no callback, use single instance and set fields.\n\t\t\t\tif (replFunc != null) re = replFunc(m);\n\t\t\t\telse ExpandReplacement_(m, repl, b);\n\t\t\t} else re = repl;\n\t\t\tif (!re.NE()) b.Append(re);\n\t\t}\n\t\t\n\t\t//append s part after last match\n\t\tif (e.foundCount != 0) {\n\t\t\tint nAfter = s.Length - prevEnd;\n\t\t\tif (nAfter > 0) b.Append(s, prevEnd, nAfter);\n\t\t\tresult = b.ToString();\n\t\t\tbCache.Dispose();\n\t\t} else result = s;\n\t\t\n\t\treturn e.foundCount;\n\t}\n\t\n\t/// <summary>\n\t/// Finds and replaces all match instances of the regular expression.\n\t/// </summary>\n\t/// <returns>The result string.</returns>\n\t/// <param name=\"s\">Subject string. Cannot be <c>null</c>.</param>\n\t/// <param name=\"repl\">\n\t/// Replacement pattern.\n\t/// Can consist of any combination of literal text and substitutions like <c>$1</c>.\n\t/// Supports .NET regular expression substitution syntax. See <see cref=\"Regex.Replace(string, string, int)\"/>. Also: replaces <c>$*</c> with the name of the last encountered mark; replaces <c>${+func}</c> etc with the return value of a function registered with <see cref=\"addReplaceFunc\"/>.\n\t/// </param>\n\t/// <param name=\"maxCount\">Maximal count of replacements to make. If -1 (default), replaces all.</param>\n\t/// <exception cref=\"ArgumentNullException\"><i>s</i> is <c>null</c>.</exception>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Invalid <i>range</i>.</exception>\n\t/// <exception cref=\"ArgumentException\">\n\t/// - Invalid <c>$replacement</c>.\n\t/// - Used a <c>PARTIAL_</c> flag.\n\t/// - The regular expression contains <c>(?=...\\K)</c>.\n\t/// </exception>\n\t/// <exception cref=\"AuException\">The PCRE API function <c>pcre2_match</c> failed. Unlikely.</exception>\n\t/// <remarks>\n\t/// This function is similar to <see cref=\"Regex.Replace(string, string, int)\"/>.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// var s = \"one two22 three333 four\";\n\t/// var x = new regexp(@\"\\b(\\w+?)(\\d+)\\b\");\n\t/// s = x.Replace(s, \"'$2$1'\");\n\t/// print.it(s);\n\t/// ]]></code>\n\t/// </example>\n\t/// <inheritdoc cref=\"IsMatch\" path=\"/param\"/>\n\tpublic string Replace(string s,\n\t\t[ParamString(PSFormat.RegexpReplacement)] string repl = null,\n\t\tint maxCount = -1, Range? range = null, RXMatchFlags matchFlags = 0) {\n\t\t_Replace(s, out var R, repl, null, maxCount, range, matchFlags);\n\t\treturn R;\n\t}\n\t\n\t/// <returns>The number of replacements made. Returns the result string through an out parameter.</returns>\n\t/// <param name=\"result\">The result string. Can be the same variable as the subject string.</param>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// var s = \"one two22 three333 four\";\n\t/// var x = new regexp(@\"\\b(\\w+?)(\\d+)\\b\");\n\t/// if(0 == x.Replace(s, \"'$2$1'\", out s)) print.it(\"not found\");\n\t/// else print.it(s);\n\t/// ]]></code>\n\t/// </example>\n\t/// <inheritdoc cref=\"Replace(string, string, int, Range?, RXMatchFlags)\"/>\n\tpublic int Replace(string s,\n\t\t[ParamString(PSFormat.RegexpReplacement)] string repl,\n\t\tout string result, int maxCount = -1, Range? range = null, RXMatchFlags matchFlags = 0) {\n\t\treturn _Replace(s, out result, repl, null, maxCount, range, matchFlags);\n\t}\n\t\n\t/// <summary>\n\t/// Finds and replaces all match instances of the regular expression. Uses a callback function.\n\t/// </summary>\n\t/// <param name=\"replFunc\">\n\t/// Callback function's delegate, eg lambda. Called for each found match. Returns the replacement.\n\t/// In the callback function you can use <see cref=\"RXMatch.ExpandReplacement\"/>.\n\t/// </param>\n\t/// <remarks>\n\t/// This function is similar to <see cref=\"Regex.Replace(string, MatchEvaluator, int)\"/>.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// var s = \"one two22 three333 four\";\n\t/// var x = new regexp(@\"\\b(\\w+?)(\\d+)\\b\");\n\t/// s = x.Replace(s, o => o.Value.Upper());\n\t/// print.it(s);\n\t/// ]]></code>\n\t/// </example>\n\t/// <inheritdoc cref=\"Replace(string, string, int, Range?, RXMatchFlags)\"/>\n\tpublic string Replace(string s, Func<RXMatch, string> replFunc, int maxCount = -1, Range? range = null, RXMatchFlags matchFlags = 0) {\n\t\t_Replace(s, out var R, null, replFunc, maxCount, range, matchFlags);\n\t\treturn R;\n\t}\n\t\n\t/// <summary>\n\t/// Finds and replaces all match instances of the regular expression. Uses a callback function.\n\t/// </summary>\n\t/// <param name=\"replFunc\">\n\t/// Callback function's delegate, eg lambda. Called for each found match. Returns the replacement.\n\t/// In the callback function you can use <see cref=\"RXMatch.ExpandReplacement\"/>.\n\t/// </param>\n\t/// <remarks>\n\t/// This function is similar to <see cref=\"Regex.Replace(string, MatchEvaluator, int)\"/>.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// var s = \"one two22 three333 four\";\n\t/// var x = new regexp(@\"\\b(\\w+?)(\\d+)\\b\");\n\t/// if(0 == x.Replace(s, o => o.Value.Upper(), out s)) print.it(\"not found\");\n\t/// else print.it(s);\n\t/// ]]></code>\n\t/// </example>\n\t/// <inheritdoc cref=\"Replace(string, string, out string, int, Range?, RXMatchFlags)\"/>\n\tpublic int Replace(string s, Func<RXMatch, string> replFunc, out string result, int maxCount = -1, Range? range = null, RXMatchFlags matchFlags = 0) {\n\t\treturn _Replace(s, out result, null, replFunc, maxCount, range, matchFlags);\n\t}\n\t\n\t/// <summary>\n\t/// Used by <c>_ReplaceAll</c> and <c>RXMatch.ExpandReplacement</c>.\n\t/// Fully supports .NET regular expression substitution syntax. Also: replaces <c>$*</c> with the name of the last encountered mark; replaces <c>${+func}</c> etc with the return value of a function registered with <see cref=\"addReplaceFunc\"/>.\n\t/// </summary>\n\t[MethodImpl(MethodImplOptions.AggressiveOptimization)]\n\tinternal static void ExpandReplacement_(RXMatch m, string repl, StringBuilder b) {\n\t\tfixed (char* s0fixed = repl) {\n\t\t\tchar* s0 = s0fixed, s = s0, eos = s + repl.Length, e = s; //e is the end of s part added to b\n\t\t\twhile (s < eos) {\n\t\t\t\tif (*s == '$') {\n\t\t\t\t\tif (s > e) { b.Append(e, (int)(s - e)); e = s; }\n\t\t\t\t\t\n\t\t\t\t\tchar ch = *++s;\n\t\t\t\t\t\n\t\t\t\t\tif (ch == '$') { //escaped $\n\t\t\t\t\t\te = s++;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tchar* s1e = s; //for errors only\n\t\t\t\t\tint group = -1;\n\t\t\t\t\tif (ch == '{') { //${name} or ${number}\n\t\t\t\t\t\tchar* t = ++s; while (t < eos && *t != '}') t++;\n\t\t\t\t\t\tif (t == eos) break;\n\t\t\t\t\t\tch = *s;\n\t\t\t\t\t\tif (ch == '+') { //${+userFunc} or ${+userFunc(group)} or ${+userFunc(group, param)}\n\t\t\t\t\t\t\ts++;\n\t\t\t\t\t\t\tstring funcName = null, funcParam = null; int groupNumber = 0;\n\t\t\t\t\t\t\tif (t[-1] == ')') {\n\t\t\t\t\t\t\t\tt--;\n\t\t\t\t\t\t\t\tvar sArgs = s; while (sArgs < t && *sArgs != '(') sArgs++;\n\t\t\t\t\t\t\t\tif (sArgs < t) {\n\t\t\t\t\t\t\t\t\tfuncName = new(s, 0, (int)(sArgs - s));\n\t\t\t\t\t\t\t\t\tvar sParam = ++sArgs; while (sParam < t && *sParam != ',') sParam++;\n\t\t\t\t\t\t\t\t\tgroupNumber = _GetGroup(sArgs, sParam);\n\t\t\t\t\t\t\t\t\tif (groupNumber >= m.GroupCountPlusOne) funcName = null;\n\t\t\t\t\t\t\t\t\telse if (sParam < t) {\n\t\t\t\t\t\t\t\t\t\tif (*++sParam == ' ') sParam++;\n\t\t\t\t\t\t\t\t\t\tfuncParam = new(sParam, 0, (int)(t - sParam));\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tt++;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tfuncName = new string(s, 0, (int)(t - s));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (funcName == null || !s_userReplFuncs.TryGetValue(funcName, out var replFunc)) group = int.MaxValue;\n\t\t\t\t\t\t\telse b.Append(replFunc(m, groupNumber, funcParam));\n\t\t\t\t\t\t} else group = _GetGroup(s, t);\n\t\t\t\t\t\t\n\t\t\t\t\t\tint _GetGroup(char* start, char* end) {\n\t\t\t\t\t\t\tif (*start >= '0' && *start <= '9') { //${number}. info: group name cannot start with a digit, then PCRE returns error.\n\t\t\t\t\t\t\t\tint i = repl.ToInt((int)(start - s0), out int numEnd, STIFlags.NoHex);\n\t\t\t\t\t\t\t\tif (s0 + numEnd == end && i >= 0) return i;\n\t\t\t\t\t\t\t} else { //${name}\n\t\t\t\t\t\t\t\tint i = m.GroupNumberFromName_(start, (int)(end - start), out _); //speed: 40-100 ns\n\t\t\t\t\t\t\t\tif (i >= 0) return i;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn int.MaxValue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\ts = t + 1;\n\t\t\t\t\t} else if (ch >= '0' && ch <= '9') { //$number\n\t\t\t\t\t\tgroup = repl.ToInt((int)(s - s0), out int numEnd, STIFlags.NoHex);\n\t\t\t\t\t\tif (numEnd == 0 || group < 0) group = int.MaxValue;\n\t\t\t\t\t\ts = s0 + numEnd;\n\t\t\t\t\t} else {\n\t\t\t\t\t\ts++;\n\t\t\t\t\t\tif (ch == '`') { //part before match\n\t\t\t\t\t\t\tint i = m.Start;\n\t\t\t\t\t\t\tif (i > 0) b.Append(m.Subject, 0, i);\n\t\t\t\t\t\t} else if (ch == '\\'') { //part after match\n\t\t\t\t\t\t\tvar subject = m.Subject;\n\t\t\t\t\t\t\tint i = m.End, len = subject.Length - i;\n\t\t\t\t\t\t\tif (len > 0) b.Append(subject, i, len);\n\t\t\t\t\t\t} else if (ch == '&') { //whole match\n\t\t\t\t\t\t\tgroup = 0;\n\t\t\t\t\t\t} else if (ch == '+') { //last group\n\t\t\t\t\t\t\tgroup = m.GroupCountPlusOne - 1;\n\t\t\t\t\t\t} else if (ch == '_') { //subject\n\t\t\t\t\t\t\tb.Append(m.Subject);\n\t\t\t\t\t\t} else if (ch == '*') { //last mark\n\t\t\t\t\t\t\tb.Append(m.Mark);\n\t\t\t\t\t\t} else group = int.MaxValue;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tif (group >= 0) {\n\t\t\t\t\t\t//if $invalid, throw exception. Would be harmful to ignore when replacing in multiple files.\n\t\t\t\t\t\tif (group >= m.GroupCountPlusOne) throw new ArgumentException($\"Invalid regex replacement: {new string(--s1e, 0, (int)(s - s1e))}\");\n\t\t\t\t\t\t\n\t\t\t\t\t\tvar g = m[group];\n\t\t\t\t\t\tif (g.Length > 0) b.Append(g.Subject_, g.Start, g.Length);\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\te = s;\n\t\t\t\t} else s++;\n\t\t\t}\n\t\t\t\n\t\t\tint tail = (int)(eos - e);\n\t\t\tif (tail > 0) b.Append(e, tail);\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Adds or replaces a function that is called when a regular expression replacement string contains <c>${+name}</c> or <c>${+name(g)}</c> or <c>${+name(g, v)}</c>, where <i>g</i> is group number or name and <i>v</i> is any string.\n\t/// </summary>\n\t/// <param name=\"name\">A string used to identify the function. Can contain any characters except <c>'}'</c>, <c>'('</c> and <c>')'</c>.</param>\n\t/// <param name=\"replFunc\">\n\t/// Callback function. Called for each found match. Returns the replacement.\n\t/// Parameters:\n\t/// <br/>• current match.\n\t/// <br/>• group number <i>g</i>, if replacement is like <c>${+name(g)}</c> or <c>${+name(g, v)}</c>; else 0.\n\t/// <br/>• string <i>v</i>, if replacement is like <c>${+name(g, v)}</c>; else <c>null</c>.\n\t/// \n\t/// <para>\n\t/// In the callback function you can use <see cref=\"RXMatch.ExpandReplacement\"/>.\n\t/// </para>\n\t/// </param>\n\t/// <remarks>\n\t/// Useful when there is no way to use <see cref=\"Replace\"/> overloads with a <i>replFunc</i> parameter. For example in Find/Replace UI.\n\t/// </remarks>\n\t/// <example>\n\t/// Create new script in editor and add this code. In <b>Properties</b> set role <c>editorExtension</c>. Run.\n\t/// Then in the <b>Find</b> panel in the replacement field you can use <c>${+Lower}</c>, <c>${+Lower(1)}</c>, <c>${+Lower(2)}</c> etc.\n\t/// <code><![CDATA[\n\t/// regexp.addReplaceFunc(\"Lower\", (m, g, v) => m[g].Value.Lower()); //make lowercase\n\t/// ]]></code>\n\t/// Another example. Replacement could be like <c>${+mul(1, 10)}</c>.\n\t/// <code><![CDATA[\n\t/// regexp.addReplaceFunc(\"mul\", (m, g, v) => (m[g].Value.ToInt() * v.ToInt()).ToString()); //multiply by v\n\t/// ]]></code>\n\t/// </example>\n\tpublic static void addReplaceFunc(string name, Func<RXMatch, int, string, string> replFunc) {\n\t\ts_userReplFuncs[name] = replFunc;\n\t}\n\tstatic ConcurrentDictionary<string, Func<RXMatch, int, string, string>> s_userReplFuncs = new();\n\t\n\t//rejected: use pcre2_substitute. Not useful because: we cannot implement RXMatch.ExpandReplacement with it; we have addReplaceFunc.\n\t\n\t/// <summary>\n\t/// Returns an array of substrings that in the subject string are delimited by regular expression matches.\n\t/// </summary>\n\t/// <param name=\"s\">Subject string. Cannot be <c>null</c>.</param>\n\t/// <param name=\"maxCount\">Maximal count of substrings to get. The last substring contains the unsplit remainder of the subject string. If 0 (default) or negative, gets all.</param>\n\t/// <exception cref=\"ArgumentNullException\"><i>s</i> is <c>null</c>.</exception>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Invalid <i>range</i>.</exception>\n\t/// <exception cref=\"ArgumentException\">\n\t/// <br/>• Used a <c>PARTIAL_</c> flag.\n\t/// <br/>• The regular expression contains <c>(?=...\\K)</c>.\n\t/// </exception>\n\t/// <exception cref=\"AuException\">The PCRE API function <c>pcre2_match</c> failed. Unlikely.</exception>\n\t/// <remarks>\n\t/// Element 0 of the returned array is <i>s</i> substring until the first match of the regular expression, element 1 is substring between the first and second match, and so on. If no matches, the array contains single element and it is <i>s</i>.\n\t/// \n\t/// This function is similar to <see cref=\"Regex.Split(string, int)\"/>.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// var s = \"one, two,three , four\";\n\t/// var x = new regexp(@\" *, *\");\n\t/// var a = x.Split(s);\n\t/// for(int i = 0; i < a.Length; i++) print.it(i, a[i]);\n\t/// ]]></code>\n\t/// </example>\n\t/// <inheritdoc cref=\"IsMatch\" path=\"/param\"/>\n\tpublic string[] Split(string s, int maxCount = 0, Range? range = null, RXMatchFlags matchFlags = 0) {\n\t\tif (maxCount < 0) maxCount = 0;\n\t\tif (maxCount != 1) {\n\t\t\tvar a = new List<string>();\n\t\t\tint prevEnd = 0;\n\t\t\tvar e = new _MatchEnum(this, s, 0, range, matchFlags, maxCount - 1);\n\t\t\twhile (e.Next()) {\n\t\t\t\tvar p = e.GroupR;\n\t\t\t\ta.Add(s[prevEnd..p.start]);\n\t\t\t\tprevEnd = p.end;\n\t\t\t}\n\t\t\tif (e.foundCount > 0) {\n\t\t\t\ta.Add(s[prevEnd..]);\n\t\t\t\treturn a.ToArray();\n\t\t\t}\n\t\t}\n\t\treturn new string[] { s };\n\t}\n\t\n\t/// <summary>\n\t/// Returns <see cref=\"RXGroup\"/> array of substrings delimited by regular expression matches.\n\t/// </summary>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// var s = \"one, two,three , four\";\n\t/// var x = new regexp(@\" *, *\");\n\t/// var a = x.SplitG(s);\n\t/// foreach(var v in a) print.it(v.Start, v.Value);\n\t/// ]]></code>\n\t/// </example>\n\t/// <inheritdoc cref=\"Split\"/>\n\tpublic RXGroup[] SplitG(string s, int maxCount = 0, Range? range = null, RXMatchFlags matchFlags = 0) {\n\t\tif (maxCount < 0) maxCount = 0;\n\t\tif (maxCount != 1) {\n\t\t\tvar a = new List<RXGroup>();\n\t\t\tint prevEnd = 0;\n\t\t\tvar e = new _MatchEnum(this, s, 0, range, matchFlags, maxCount - 1);\n\t\t\twhile (e.Next()) {\n\t\t\t\tvar p = e.GroupR;\n\t\t\t\ta.Add(new RXGroup(s, prevEnd, p.start));\n\t\t\t\tprevEnd = p.end;\n\t\t\t}\n\t\t\tif (e.foundCount > 0) {\n\t\t\t\ta.Add(new RXGroup(s, prevEnd, s.Length));\n\t\t\t\treturn a.ToArray();\n\t\t\t}\n\t\t}\n\t\treturn new RXGroup[] { new RXGroup(s, 0, s.Length) };\n\t}\n\t\n\t//rejected: probably rarely used. Or need IEnumerable<string> too.\n\t//public IEnumerable<RXGroup> SplitE(string s, int maxCount = 0, Range? range = null, RXMatchFlags matchFlags = 0)\n\t//{\n\t//\tif(maxCount< 0) maxCount = 0;\n\t//\tif(maxCount != 1) {\n\t//\t\tint prevEnd = 0;\n\t//\t\tvar e = new _MatchEnum(this, s, 0, range, matchFlags, maxCount - 1);\n\t//\t\twhile(e.Next()) {\n\t//\t\t\tvar p = e.GroupP;\n\t//\t\t\tyield return new RXGroup(s, prevEnd, p.start);\n\t//\t\t\tprevEnd = p.end;\n\t//\t\t}\n\t//\t\tif(e.foundCount > 0) {\n\t//\t\t\tyield return new RXGroup(s, prevEnd, s.Length);\n\t//\t\t\tyield break;\n\t//\t\t}\n\t//\t}\n\t//\tyield return new RXGroup(s, 0, s.Length);\n\t//}\n\t\n\t//Calls pcre2_pattern_info(ALLOPTIONS), which returns flags passed to the ctor and possibly modified by (*OPTION) and possibly added UTF if contains non-ASCII characters.\n\t//Actually RXFlags is long, where the high 32 bits is extended options. This func gets only the main options (the low 32 bits).\n\tRXFlags _InfoAllOptions {\n\t\tget {\n\t\t\tRXFlags R;\n\t\t\tCpp.pcre2_pattern_info(_CodeHR, Cpp.PCRE2_INFO_.ALLOPTIONS, &R);\n\t\t\treturn R;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Encloses string in <c>\\Q \\E</c> if it contains metacharacters <c>\\^$.[|()?*+{</c> or if <i>always</i> == <c>true</c>.\n\t/// </summary>\n\t/// <param name=\"s\">Can be <c>null</c>.</param>\n\t/// <param name=\"always\">Enclose always, even if the string does not contain metacharacters. Should be <c>true</c> if the regular expression in which this string will be used has option \"extended\", because then whitespace is ignored and <c>#</c> is a special character too.</param>\n\t/// <remarks>\n\t/// Such enclosed substring in a regular expression is interpreted as a literal string.\n\t/// This function also escapes <c>\\E</c>, so that it does not end the literal string.\n\t/// </remarks>\n\tpublic static string escapeQE(string s, bool always = false) {\n\t\tif (s == null) return s;\n\t\tif (always) goto g1;\n\t\tfor (int i = 0; i < s.Length; i++) {\n\t\t\tchar c = s[i];\n\t\t\tif ((c >= '(' && c <= '+') || c == '\\\\' || c == '.' || c == '?' || c == '{' || c == '[' || c == '|' || c == '$' || c == '^') goto g1;\n\t\t\t//this is slower\n\t\t\t//if(c < 128) {\n\t\t\t//\tif(c < 64) {\n\t\t\t//\t\tif(0 != (0b1000000000000000010011110001000000000000000000000000000000000000UL & (1UL << c))) goto g1;\n\t\t\t//\t} else {\n\t\t\t//\t\tif(0 != (0b0001100000000000000000000000000001011000000000000000000000000000UL & (1UL << (c - 64)))) goto g1;\n\t\t\t//\t}\n\t\t\t//}\n\t\t}\n\t\treturn s;\n\t\tg1:\n\t\treturn @\"\\Q\" + s.Replace(@\"\\E\", @\"\\E\\\\E\\Q\") + @\"\\E\";\n\t}\n}\n"
  },
  {
    "path": "Au/String/regexp_ExtString.cs",
    "content": "namespace Au.Types;\n\npublic static partial class ExtString {\n\t/// <summary>\n\t/// Returns <c>true</c> if this string matches PCRE regular expression <i>rx</i>.\n\t/// </summary>\n\t/// <param name=\"t\">This string. If <c>null</c>, returns <c>false</c>.</param>\n\t/// <exception cref=\"ArgumentException\">Invalid regular expression.</exception>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Invalid <i>range</i>.</exception>\n\t/// <exception cref=\"AuException\">Failed (unlikely).</exception>\n\t/// <remarks>More info and examples: <see cref=\"regexp\"/>.</remarks>\n\t/// <example></example>\n\t/// <inheritdoc cref=\"regexp.IsMatch\"/>\n\t/// <inheritdoc cref=\"regexp(string, RXFlags)\" path=\"/param\"/>\n\tpublic static bool RxIsMatch(this string t,\n\t\t[ParamString(PSFormat.Regexp)] string rx,\n\t\tRXFlags flags = 0, Range? range = null) {\n\t\tvar x = _cache.AddOrGet(rx, flags);\n\t\treturn x.IsMatch(t, range);\n\t}\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if this string matches PCRE regular expression <i>rx</i>.\n\t/// Gets match info as <see cref=\"RXMatch\"/>.\n\t/// </summary>\n\t/// <param name=\"t\">This string. If <c>null</c>, returns <c>false</c>.</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Invalid <i>range</i>.</exception>\n\t/// <exception cref=\"ArgumentException\">Invalid regular expression.</exception>\n\t/// <exception cref=\"AuException\">Failed (unlikely).</exception>\n\t/// <remarks>More info and examples: <see cref=\"regexp\"/>.</remarks>\n\t/// <example></example>\n\t/// <inheritdoc cref=\"regexp.Match(string, out RXMatch, Range?, RXMatchFlags)\"/>\n\t/// <inheritdoc cref=\"regexp(string, RXFlags)\" path=\"/param\"/>\n\tpublic static bool RxMatch(this string t,\n\t\t[ParamString(PSFormat.Regexp)] string rx,\n\t\tout RXMatch result, RXFlags flags = 0, Range? range = null) {\n\t\tvar x = _cache.AddOrGet(rx, flags);\n\t\treturn x.Match(t, out result, range);\n\t}\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if this string matches PCRE regular expression <i>rx</i>.\n\t/// Gets whole match or some group, as string.\n\t/// </summary>\n\t/// <param name=\"t\">This string. If <c>null</c>, returns <c>false</c>.</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Invalid <i>group</i> or <i>range</i>.</exception>\n\t/// <exception cref=\"ArgumentException\">Invalid regular expression.</exception>\n\t/// <exception cref=\"AuException\">Failed (unlikely).</exception>\n\t/// <remarks>More info and examples: <see cref=\"regexp\"/>.</remarks>\n\t/// <example></example>\n\t/// <inheritdoc cref=\"regexp.Match(string, int, out string, Range?, RXMatchFlags)\"/>\n\t/// <inheritdoc cref=\"regexp(string, RXFlags)\" path=\"/param\"/>\n\tpublic static bool RxMatch(this string t,\n\t\t[ParamString(PSFormat.Regexp)] string rx,\n\t\tint group, out string result, RXFlags flags = 0, Range? range = null) {\n\t\tvar x = _cache.AddOrGet(rx, flags);\n\t\treturn x.Match(t, group, out result, range);\n\t}\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if this string matches PCRE regular expression <i>rx</i>.\n\t/// Gets whole match or some group, as index and length.\n\t/// </summary>\n\t/// <param name=\"t\">This string. If <c>null</c>, returns <c>false</c>.</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Invalid <i>group</i> or <i>range</i>.</exception>\n\t/// <exception cref=\"ArgumentException\">Invalid regular expression.</exception>\n\t/// <exception cref=\"AuException\">Failed (unlikely).</exception>\n\t/// <remarks>More info and examples: <see cref=\"regexp\"/>.</remarks>\n\t/// <example></example>\n\t/// <inheritdoc cref=\"regexp.Match(string, int, out RXGroup, Range?, RXMatchFlags)\"/>\n\t/// <inheritdoc cref=\"regexp(string, RXFlags)\" path=\"/param\"/>\n\tpublic static bool RxMatch(this string t,\n\t\t[ParamString(PSFormat.Regexp)] string rx,\n\t\tint group, out RXGroup result, RXFlags flags = 0, Range? range = null) {\n\t\tvar x = _cache.AddOrGet(rx, flags);\n\t\treturn x.Match(t, group, out result, range);\n\t}\n\t\n\t/// <summary>\n\t/// Finds all match instances of PCRE regular expression <i>rx</i>.\n\t/// </summary>\n\t/// <param name=\"t\">This string.</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Invalid <i>range</i>.</exception>\n\t/// <exception cref=\"ArgumentException\">Invalid regular expression. Or used a <c>PARTIAL_</c> flag.</exception>\n\t/// <exception cref=\"AuException\">Failed (unlikely).</exception>\n\t/// <remarks>More info and examples: <see cref=\"regexp\"/>.</remarks>\n\t/// <example></example>\n\t/// <inheritdoc cref=\"regexp.FindAll(string, Range?, RXMatchFlags)\"/>\n\t/// <inheritdoc cref=\"regexp(string, RXFlags)\" path=\"/param\"/>\n\tpublic static IEnumerable<RXMatch> RxFindAll(this string t,\n\t\t[ParamString(PSFormat.Regexp)] string rx,\n\t\tRXFlags flags = 0, Range? range = null) {\n\t\tif (t == null) throw new NullReferenceException();\n\t\tvar x = _cache.AddOrGet(rx, flags);\n\t\treturn x.FindAll(t, range);\n\t}\n\t\n\t/// <summary>\n\t/// Finds all match instances of PCRE regular expression <i>rx</i>. Gets array of <see cref=\"RXMatch\"/>.\n\t/// </summary>\n\t/// <param name=\"t\">This string.</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Invalid <i>range</i>.</exception>\n\t/// <exception cref=\"ArgumentException\">Invalid regular expression. Or used a <c>PARTIAL_</c> flag.</exception>\n\t/// <exception cref=\"AuException\">Failed (unlikely).</exception>\n\t/// <remarks>More info and examples: <see cref=\"regexp\"/>.</remarks>\n\t/// <example></example>\n\t/// <inheritdoc cref=\"regexp.FindAll(string, out RXMatch[], Range?, RXMatchFlags)\"/>\n\t/// <inheritdoc cref=\"regexp(string, RXFlags)\" path=\"/param\"/>\n\tpublic static bool RxFindAll(this string t,\n\t\t[ParamString(PSFormat.Regexp)] string rx,\n\t\tout RXMatch[] result, RXFlags flags = 0, Range? range = null) {\n\t\tif (t == null) throw new NullReferenceException();\n\t\tvar x = _cache.AddOrGet(rx, flags);\n\t\treturn x.FindAll(t, out result, range);\n\t}\n\t\n\t/// <summary>\n\t/// Finds all match instances of PCRE regular expression <i>rx</i>.\n\t/// </summary>\n\t/// <param name=\"t\">This string.</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Invalid <i>group</i> or <i>range</i>.</exception>\n\t/// <exception cref=\"ArgumentException\">Invalid regular expression. Or used a <c>PARTIAL_</c> flag.</exception>\n\t/// <exception cref=\"AuException\">Failed (unlikely).</exception>\n\t/// <remarks>More info and examples: <see cref=\"regexp\"/>.</remarks>\n\t/// <example></example>\n\t/// <inheritdoc cref=\"regexp.FindAll(string, int, Range?, RXMatchFlags)\"/>\n\t/// <inheritdoc cref=\"regexp(string, RXFlags)\" path=\"/param\"/>\n\tpublic static IEnumerable<string> RxFindAll(this string t,\n\t\t[ParamString(PSFormat.Regexp)] string rx,\n\t\tint group, RXFlags flags = 0, Range? range = null) {\n\t\tif (t == null) throw new NullReferenceException();\n\t\tvar x = _cache.AddOrGet(rx, flags);\n\t\treturn x.FindAll(t, group, range);\n\t}\n\t\n\t/// <summary>\n\t/// Finds all match instances of PCRE regular expression <i>rx</i>. Gets array of strings.\n\t/// </summary>\n\t/// <param name=\"t\">This string.</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Invalid <i>group</i> or <i>range</i>.</exception>\n\t/// <exception cref=\"ArgumentException\">Invalid regular expression. Or used a <c>PARTIAL_</c> flag.</exception>\n\t/// <exception cref=\"AuException\">Failed (unlikely).</exception>\n\t/// <remarks>More info and examples: <see cref=\"regexp\"/>.</remarks>\n\t/// <example></example>\n\t/// <inheritdoc cref=\"regexp.FindAll(string, int, out string[], Range?, RXMatchFlags)\"/>\n\t/// <inheritdoc cref=\"regexp(string, RXFlags)\" path=\"/param\"/>\n\tpublic static bool RxFindAll(this string t,\n\t\t[ParamString(PSFormat.Regexp)] string rx,\n\t\tint group, out string[] result, RXFlags flags = 0, Range? range = null) {\n\t\tif (t == null) throw new NullReferenceException();\n\t\tvar x = _cache.AddOrGet(rx, flags);\n\t\treturn x.FindAll(t, group, out result, range);\n\t}\n\t\n\t//rejected. Rarely used.\n\t///// <summary>\n\t///// Finds all match instances of PCRE regular expression <i>rx</i>. Gets array of <see cref=\"RXGroup\"/>.\n\t///// </summary>\n\t///// <param name=\"t\">This string.</param>\n\t///// <exception cref=\"ArgumentOutOfRangeException\">Invalid <i>group</i> or <i>range</i>.</exception>\n\t///// <exception cref=\"ArgumentException\">Invalid regular expression. Or used a <c>PARTIAL_</c> flag.</exception>\n\t///// <exception cref=\"AuException\">Failed (unlikely).</exception>\n\t///// <remarks>More info and examples: <see cref=\"regexp\"/>.</remarks>\n\t///// <example></example>\n\t///// <inheritdoc cref=\"regexp.FindAllG(string, int, out RXGroup[], Range?, RXMatchFlags)\"/>\n\t///// <inheritdoc cref=\"regexp(string, RXFlags)\" path=\"/param\"/>\n\t//public static bool RxFindAll(this string t,\n\t//\t[ParamString(PSFormat.regexp)] string rx,\n\t//\tint group, out RXGroup[] result, RXFlags flags = 0, Range? range = null) {\n\t//\tif (t == null) throw new NullReferenceException();\n\t//\tvar x = _cache.AddOrGet(rx, flags);\n\t//\treturn x.FindAllG(t, group, out result, range);\n\t//}\n\t\n\t/// <summary>\n\t/// Finds and replaces all match instances of PCRE regular expression <i>rx</i>.\n\t/// </summary>\n\t/// <param name=\"t\">This string.</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Invalid <i>range</i>.</exception>\n\t/// <exception cref=\"ArgumentException\">\n\t/// - Invalid regular expression.\n\t/// - Invalid <c>$replacement</c>.\n\t/// - Used a <c>PARTIAL_</c> flag.\n\t/// - The regular expression contains <c>(?=...\\K)</c>.\n\t/// </exception>\n\t/// <exception cref=\"AuException\">Failed (unlikely).</exception>\n\t/// <remarks>More info and examples: <see cref=\"regexp\"/>.</remarks>\n\t/// <example></example>\n\t/// <inheritdoc cref=\"regexp.Replace(string, string, int, Range?, RXMatchFlags)\"/>\n\t/// <inheritdoc cref=\"regexp(string, RXFlags)\" path=\"/param\"/>\n\tpublic static string RxReplace(this string t,\n\t\t[ParamString(PSFormat.Regexp)] string rx,\n\t\t[ParamString(PSFormat.RegexpReplacement)] string repl,\n\t\tint maxCount = -1, RXFlags flags = 0, Range? range = null) {\n\t\tif (t == null) throw new NullReferenceException();\n\t\tvar x = _cache.AddOrGet(rx, flags);\n\t\treturn x.Replace(t, repl, maxCount, range);\n\t}\n\t\n\t/// <summary>\n\t/// Finds and replaces all match instances of PCRE regular expression <i>rx</i>.\n\t/// </summary>\n\t/// <param name=\"t\">This string.</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Invalid <i>range</i>.</exception>\n\t/// <exception cref=\"ArgumentException\">\n\t/// - Invalid regular expression.\n\t/// - Invalid <c>$replacement</c>.\n\t/// - Used a <c>PARTIAL_</c> flag.\n\t/// - The regular expression contains <c>(?=...\\K)</c>.\n\t/// </exception>\n\t/// <exception cref=\"AuException\">Failed (unlikely).</exception>\n\t/// <remarks>More info and examples: <see cref=\"regexp\"/>.</remarks>\n\t/// <example></example>\n\t/// <inheritdoc cref=\"regexp.Replace(string, string, out string, int, Range?, RXMatchFlags)\"/>\n\t/// <inheritdoc cref=\"regexp(string, RXFlags)\" path=\"/param\"/>\n\tpublic static int RxReplace(this string t,\n\t\t[ParamString(PSFormat.Regexp)] string rx,\n\t\t[ParamString(PSFormat.RegexpReplacement)] string repl,\n\t\tout string result, int maxCount = -1, RXFlags flags = 0, Range? range = null) {\n\t\tif (t == null) throw new NullReferenceException();\n\t\tvar x = _cache.AddOrGet(rx, flags);\n\t\treturn x.Replace(t, repl, out result, maxCount, range);\n\t}\n\t\n\t/// <summary>\n\t/// Finds and replaces all match instances of PCRE regular expression <i>rx</i>. Uses a callback function.\n\t/// </summary>\n\t/// <param name=\"t\">This string.</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Invalid <i>range</i>.</exception>\n\t/// <exception cref=\"ArgumentException\">\n\t/// - Invalid regular expression.\n\t/// - Invalid <c>$replacement</c>.\n\t/// - Used a <c>PARTIAL_</c> flag.\n\t/// - The regular expression contains <c>(?=...\\K)</c>.\n\t/// </exception>\n\t/// <exception cref=\"AuException\">Failed (unlikely).</exception>\n\t/// <remarks>More info and examples: <see cref=\"regexp\"/>.</remarks>\n\t/// <example></example>\n\t/// <inheritdoc cref=\"regexp.Replace(string, Func{RXMatch, string}, int, Range?, RXMatchFlags)\"/>\n\t/// <inheritdoc cref=\"regexp(string, RXFlags)\" path=\"/param\"/>\n\tpublic static string RxReplace(this string t,\n\t\t[ParamString(PSFormat.Regexp)] string rx,\n\t\tFunc<RXMatch, string> replFunc, int maxCount = -1, RXFlags flags = 0, Range? range = null) {\n\t\tif (t == null) throw new NullReferenceException();\n\t\tvar x = _cache.AddOrGet(rx, flags);\n\t\treturn x.Replace(t, replFunc, maxCount, range);\n\t}\n\t\n\t/// <summary>\n\t/// Finds and replaces all match instances of PCRE regular expression <i>rx</i>. Uses a callback function.\n\t/// </summary>\n\t/// <param name=\"t\">This string.</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Invalid <i>range</i>.</exception>\n\t/// <exception cref=\"ArgumentException\">\n\t/// - Invalid regular expression.\n\t/// - Invalid <c>$replacement</c>.\n\t/// - Used a <c>PARTIAL_</c> flag.\n\t/// - The regular expression contains <c>(?=...\\K)</c>.\n\t/// </exception>\n\t/// <exception cref=\"AuException\">Failed (unlikely).</exception>\n\t/// <remarks>More info and examples: <see cref=\"regexp\"/>.</remarks>\n\t/// <example></example>\n\t/// <inheritdoc cref=\"regexp.Replace(string, Func{RXMatch, string}, out string, int, Range?, RXMatchFlags)\"/>\n\t/// <inheritdoc cref=\"regexp(string, RXFlags)\" path=\"/param\"/>\n\tpublic static int RxReplace(this string t,\n\t\t[ParamString(PSFormat.Regexp)] string rx,\n\t\tFunc<RXMatch, string> replFunc, out string result, int maxCount = -1, RXFlags flags = 0, Range? range = null) {\n\t\tif (t == null) throw new NullReferenceException();\n\t\tvar x = _cache.AddOrGet(rx, flags);\n\t\treturn x.Replace(t, replFunc, out result, maxCount, range);\n\t}\n\t\n\t/// <summary>\n\t/// Returns an array of substrings that in this string are delimited by regular expression matches.\n\t/// </summary>\n\t/// <param name=\"t\">This string.</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Invalid <i>range</i>.</exception>\n\t/// <exception cref=\"ArgumentException\">Invalid regular expression. Or used a <c>PARTIAL_</c> flag.</exception>\n\t/// <exception cref=\"AuException\">Failed (unlikely).</exception>\n\t/// <remarks>More info and examples: <see cref=\"regexp\"/>.</remarks>\n\t/// <example></example>\n\t/// <inheritdoc cref=\"regexp.Split(string, int, Range?, RXMatchFlags)\"/>\n\t/// <inheritdoc cref=\"regexp(string, RXFlags)\" path=\"/param\"/>\n\tpublic static string[] RxSplit(this string t,\n\t\t[ParamString(PSFormat.Regexp)] string rx,\n\t\tint maxCount = 0, RXFlags flags = 0, Range? range = null) {\n\t\tif (t == null) throw new NullReferenceException();\n\t\tvar x = _cache.AddOrGet(rx, flags);\n\t\treturn x.Split(t, maxCount, range);\n\t}\n\t\n\tstatic _RegexCache _cache = new();\n\t\n\t//Cache of compiled regular expressions.\n\t//Can make ~10 times faster when the subject string is short.\n\t//The algorithm is from .NET Regex source code.\n\tclass _RegexCache {\n\t\tstruct _RXCode {\n\t\t\tpublic string regex;\n\t\t\tpublic regexp code; //note: could instead cache only PCRE code (nint), but it makes quite difficult\n\t\t\tpublic RXFlags flags;\n\t\t}\n\t\t\n\t\tLinkedList<_RXCode> _list = new();\n\t\tconst int c_maxCount = 15;\n\t\t\n\t\t/// <summary>\n\t\t/// If <i>rx</i>/<i>flags</i> is in the cache, returns the cached code.\n\t\t/// Else compiles <i>rx</i>/<i>flags</i>, adds to the cache and returns the code.\n\t\t/// </summary>\n\t\t/// <param name=\"rx\"></param>\n\t\t/// <param name=\"flags\"></param>\n\t\t/// <exception cref=\"ArgumentException\">Invalid regular expression. Or failed to compile it for some other reason.</exception>\n\t\tpublic regexp AddOrGet(string rx, RXFlags flags) {\n\t\t\tlock (this) {\n\t\t\t\tint len = rx.Length;\n\t\t\t\tfor (var x = _list.First; x != null; x = x.Next) {\n\t\t\t\t\tvar v = x.Value.regex;\n\t\t\t\t\tif (v.Length == len && v == rx && x.Value.flags == flags) {\n\t\t\t\t\t\tif (x != _list.First) {\n\t\t\t\t\t\t\t_list.Remove(x);\n\t\t\t\t\t\t\t_list.AddFirst(x);\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn x.Value.code;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t{\n\t\t\t\t\tvar code = new regexp(rx, flags);\n\t\t\t\t\t\n\t\t\t\t\tvar x = new _RXCode() { code = code, regex = rx, flags = flags };\n\t\t\t\t\t_list.AddFirst(x);\n\t\t\t\t\tif (_list.Count > c_maxCount) _list.RemoveLast();\n\t\t\t\t\t//note: now cannot free the PCRE code, because another thread may be using it. GC will do it safely.\n\t\t\t\t\t\n\t\t\t\t\treturn code;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au/String/regexp_types.cs",
    "content": "\nusing System.Text.RegularExpressions; //for XML doc links\n\nnamespace Au.Types {\n\t/// <summary>\n\t/// Regular expression match info.\n\t/// Used with <see cref=\"regexp\"/> class functions and <see cref=\"String\"/> extension methods like <see cref=\"ExtString.RxMatch\"/>.\n\t/// </summary>\n\t/// <remarks>\n\t/// Contains info about a regular expression match found in the subject string: index, length, substring, etc.\n\t/// Also contains an array of group matches, as <see cref=\"RXGroup\"/>. Groups are regular expression parts enclosed in <c>()</c>, except <c>(?...)</c>.\n\t/// Group matches can be accessed like array elements. Group 0 is whole match. Group 1 is the first group. See examples.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// var s = \"ab cd-45-ef gh\";\n\t/// if(s.RxMatch(@\"\\b([a-z]+)-(\\d+)\\b\", out RXMatch m))\n\t/// \tprint.it(\n\t/// \t\tm.GroupCountPlusOne, //3 (whole match and 2 groups)\n\t/// \t\tm.Start, //3, same as m[0].Index\n\t/// \t\tm.Value, //\"cd-45-ef\", same as m[0].Value\n\t/// \t\tm[1].Start, //3\n\t/// \t\tm[1].Value, //\"cd\"\n\t/// \t\tm[2].Start, //6\n\t/// \t\tm[2].Value //\"45\"\n\t/// \t\t);\n\t/// ]]></code>\n\t/// A group in the subject string may not exist even if whole match found. Then its <see cref=\"Exists\"/> property is <c>false</c>, <see cref=\"Start\"/> -1, <see cref=\"Length\"/> 0, <see cref=\"Value\"/> <c>null</c>.\n\t/// <code><![CDATA[\n\t/// var s = \"ab cd--ef gh\";\n\t/// if(s.RxMatch(@\"\\b([a-z]+)-(\\d+)?-([a-z]+)\\b\", out RXMatch m))\n\t/// \tprint.it(\n\t/// \t\tm.GroupCountPlusOne, //4 (whole match and 3 groups)\n\t/// \t\tm[2].Exists, //false\n\t/// \t\tm[2].Start, //-1\n\t/// \t\tm[2].Length, //0\n\t/// \t\tm[2].Value //null\n\t/// \t\t);\n\t/// ]]></code>\n\t/// </example>\n\tpublic unsafe class RXMatch {\n\t\tinternal RXMatch(regexp rx, string subject, int rc, in Cpp.RegexMatch k) {\n\t\t\tMark = k.Mark;\n\t\t\tif (rc < 0) return;\n\t\t\tExists = true;\n\t\t\tIsPartial = rc == 0;\n\t\t\tStartNoK = k.indexNoK;\n\t\t\t_rx = rx;\n\t\t\t//_subject = subject;\n\n\t\t\tvar g = _groups = new RXGroup[k.vecCount];\n\t\t\tvar v = k.vec;\n\t\t\tfor (int i = 0; i < g.Length; i++) {\n\t\t\t\tg[i] = new RXGroup(subject, v[i]);\n\t\t\t}\n\t\t}\n\n\t\t//string readonly _subject;\n\t\treadonly regexp _rx;\n\t\treadonly RXGroup[] _groups;\n\n\t\t/// <summary>\n\t\t/// Gets the subject string in which this match was found.\n\t\t/// </summary>\n\t\tpublic string Subject => _groups[0].Subject_;\n\n\t\t/// <summary>\n\t\t/// Gets the number of groups in the regular expression, + 1 for the whole match.\n\t\t/// </summary>\n\t\tpublic int GroupCountPlusOne => _groups.Length;\n\n\t\t/// <summary>\n\t\t/// Gets start offset of the match in the subject string. The same as that of group 0 (<see cref=\"RXGroup.Start\"/>).\n\t\t/// </summary>\n\t\tpublic int Start => _groups[0].Start;\n\n\t\t/// <summary>\n\t\t/// Gets length of the match in the subject string. The same as that of group 0 (<see cref=\"RXGroup.Length\"/>).\n\t\t/// </summary>\n\t\tpublic int Length => _groups[0].Length;\n\n\t\t/// <summary>\n\t\t/// Gets end offset of the match in the subject string (<see cref=\"Start\"/> + <see cref=\"Length\"/>). The same as that of group 0 (<see cref=\"RXGroup.End\"/>).\n\t\t/// </summary>\n\t\tpublic int End => _groups[0].End;\n\n\t\t/// <summary>\n\t\t/// Gets substring of the subject string from <see cref=\"Start\"/> to <see cref=\"End\"/>. The same as that of group 0 (<see cref=\"RXGroup.Value\"/>).\n\t\t/// </summary>\n\t\tpublic string Value => _groups[0].Value;\n\n\t\t/// <summary>\n\t\t/// Gets span of the subject string from <see cref=\"Start\"/> to <see cref=\"End\"/>. The same as that of group 0 (<see cref=\"RXGroup.Span\"/>).\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Unlike <see cref=\"Value\"/>, does not create new string.\n\t\t/// </remarks>\n\t\tpublic RStr Span => _groups[0].Span;\n\n\t\t/// <summary>\n\t\t/// Returns <see cref=\"RXGroup.ToString\"/> of group 0.\n\t\t/// </summary>\n\t\tpublic override string ToString() => _groups[0].ToString();\n\n\t\t/// <summary>\n\t\t/// Gets substring of the subject string from <see cref=\"Start\"/> to <see cref=\"End\"/>. The same as that of group 0 (<see cref=\"RXGroup.GetValue_\"/>).\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Use this function instead of <see cref=\"Value\"/> with results of <see cref=\"regexp\"/> functions where subject is <c>ReadOnlySpan</c>.\n\t\t/// </remarks>\n\t\t/// <param name=\"subject\">Must be the same subject string as passed to the <see cref=\"regexp\"/> function that returned this result.</param>\n\t\tinternal string GetValue_(RStr subject) => _groups[0].GetValue_(subject);\n\n\t\t/// <summary>\n\t\t/// Gets span of the subject string from <see cref=\"Start\"/> to <see cref=\"End\"/>. The same as that of group 0 (<see cref=\"RXGroup.GetSpan_\"/>).\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Use this function instead of <see cref=\"Span\"/> with results of <see cref=\"regexp\"/> functions where subject is <c>ReadOnlySpan</c>.\n\t\t/// </remarks>\n\t\t/// <param name=\"subject\">Must be the same subject string as passed to the <see cref=\"regexp\"/> function that returned this result.</param>\n\t\tinternal RStr GetSpan_(RStr subject) => _groups[0].GetSpan_(subject);\n\n\t\t/// <summary>\n\t\t/// Gets start offset of whole match regardless of <c>\\K</c>.\n\t\t/// When the regular expression contains <c>\\K</c>, this is less than <see cref=\"Start\"/>.\n\t\t/// </summary>\n\t\tpublic int StartNoK { get; private set; }\n\n\t\t/// <summary>\n\t\t/// Gets the name of a found mark, or <c>null</c>.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Marks can be inserted in regular expression pattern like <c>(*MARK:name)</c> or <c>(*:name)</c>.\n\t\t/// After a full successful match, it is the last mark encountered on the matching path through the pattern. After a \"no match\" or a partial match, it is the last encountered mark. For example, consider this pattern: <c>\"^(*MARK:A)((*MARK:B)a|b)c\"</c>. When it matches <c>\"bc\"</c>, the mark is <c>A</c>. The <c>B</c> mark is \"seen\" in the first branch of the group, but it is not on the matching path. On the other hand, when this pattern fails to match <c>\"bx\"</c>, the mark is <c>B</c>.\n\t\t/// </remarks>\n\t\tpublic string Mark { get; private set; }\n\n\t\t/// <summary>\n\t\t/// Gets the return value of the <see cref=\"regexp.Match(string, out RXMatch, Range?, RXMatchFlags)\"/> call.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Can be <c>false</c> only when the function returned <c>false</c> but a mark is available (see <see cref=\"Mark\"/>). Otherwise, when the function returns <c>false</c>, it returns <c>null</c> instead of a <see cref=\"RXMatch\"/> object.\n\t\t/// When <c>false</c>, all properties except <see cref=\"Exists\"/> and <see cref=\"Mark\"/> have undefined values or throw exception.\n\t\t/// </remarks>\n\t\tpublic bool Exists { get; private set; }\n\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if this match is <see href=\"https://www.pcre.org/current/doc/html/pcre2partial.html\">partial</see>.\n\t\t/// Partial match is possible if used a <c>PARTIAL_</c> flag.\n\t\t/// </summary>\n\t\tpublic bool IsPartial { get; private set; }\n\n\t\t/// <summary>\n\t\t/// Gets group info. Index 0 is whole match. Index 1 is the first group.\n\t\t/// </summary>\n\t\t/// <param name=\"group\">1-based group index, or 0 for whole match.</param>\n\t\t/// <exception cref=\"IndexOutOfRangeException\">Invalid <i>group</i>. Max valid value is <see cref=\"GroupCountPlusOne\"/>.</exception>\n\t\tpublic ref RXGroup this[int group] => ref _groups[group];\n\n\t\t/// <summary>\n\t\t/// Gets group info of a named group.\n\t\t/// </summary>\n\t\t/// <param name=\"groupName\">\n\t\t/// Group name.\n\t\t/// In regular expression, to set name of group <c>(text)</c>, use <c>(?&lt;NAME&gt;text)</c>.\n\t\t/// </param>\n\t\t/// <exception cref=\"ArgumentException\">Unknown group name.</exception>\n\t\t/// <remarks>\n\t\t/// If multiple groups have this name, prefers the first group that matched (<see cref=\"RXGroup.Exists\"/> is <c>true</c>).\n\t\t/// </remarks>\n\t\tpublic ref RXGroup this[string groupName] {\n\t\t\tget {\n\t\t\t\tint i = GroupNumberFromName(groupName);\n\t\t\t\tif (i < 0) throw new ArgumentException(\"Unknown group name.\");\n\t\t\t\treturn ref _groups[i];\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Finds a named group and returns its 1-based index. Returns -1 if not found.\n\t\t/// </summary>\n\t\t/// <param name=\"groupName\">\n\t\t/// Group name.\n\t\t/// In regular expression, to set name of group <c>(text)</c>, use <c>(?&lt;NAME&gt;text)</c>.\n\t\t/// </param>\n\t\t/// <exception cref=\"ArgumentNullException\"></exception>\n\t\t/// <remarks>\n\t\t/// If multiple groups have this name, prefers the first group that matched (<see cref=\"RXGroup.Exists\"/> is <c>true</c>).\n\t\t/// </remarks>\n\t\t/// <seealso cref=\"regexp.GetGroupNumberOf\"/>\n\t\tpublic int GroupNumberFromName(string groupName) {\n\t\t\tNot_.Null(groupName);\n\t\t\tfixed (char* p = groupName) return GroupNumberFromName_(p, groupName.Length, out _);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Finds a named group and returns its 1-based index. Returns -1 if not found.\n\t\t/// </summary>\n\t\t/// <param name=\"groupName\">\n\t\t/// Group name.\n\t\t/// In regular expression, to set name of group <c>(text)</c>, use <c>(?&lt;NAME&gt;text)</c>.\n\t\t/// </param>\n\t\t/// <param name=\"notUnique\">Receives <c>true</c> if multiple groups have this name.</param>\n\t\t/// <exception cref=\"ArgumentNullException\"></exception>\n\t\t/// <remarks>\n\t\t/// If multiple groups have this name, prefers the first group that matched (<see cref=\"RXGroup.Exists\"/> is <c>true</c>).\n\t\t/// </remarks>\n\t\t/// <seealso cref=\"regexp.GetGroupNumberOf\"/>\n\t\tpublic int GroupNumberFromName(string groupName, out bool notUnique) {\n\t\t\tNot_.Null(groupName);\n\t\t\tfixed (char* p = groupName) return GroupNumberFromName_(p, groupName.Length, out notUnique);\n\t\t}\n\n\t\t//Used by regexp.ReplaceAll to avoid repl.Substring.\n\t\tinternal int GroupNumberFromName_(char* s, int len, out bool notUnique) {\n\t\t\tnotUnique = false;\n\t\t\tif (len > 32 || len < 1) return -1;\n\t\t\tint step; ushort* first, last;\n\t\t\tif (s[len] == '\\0') {\n\t\t\t\tstep = Cpp.pcre2_substring_nametable_scan(_rx._CodeHR, s, &first, &last);\n\t\t\t} else {\n\t\t\t\tvar p = stackalloc char[33];\n\t\t\t\tint i; for (i = 0; i < len; i++) p[i] = s[i]; p[i] = '\\0';\n\t\t\t\tstep = Cpp.pcre2_substring_nametable_scan(_rx._CodeHR, p, &first, &last);\n\t\t\t}\n\t\t\tif (step <= 0) return -1;\n\t\t\tint R = 0;\n\t\t\tnotUnique = last > first;\n\t\t\tfor (; first <= last; first += step) {\n\t\t\t\tint r = *first;\n\t\t\t\tif (_groups[r].Start >= 0) return r; //return the first that is set\n\t\t\t\tif (R == 0) R = r; //if none is set, return the first\n\t\t\t}\n\t\t\treturn R;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Returns expanded version of the specified replacement pattern.\n\t\t/// </summary>\n\t\t/// <param name=\"repl\">\n\t\t/// Replacement pattern.\n\t\t/// Can consist of any combination of literal text and substitutions like <c>$1</c>.\n\t\t/// Supports .NET regular expression substitution syntax. See <see cref=\"Regex.Replace(string, string, int)\"/>. Also: replaces <c>$*</c> with the name of the last encountered mark; replaces <c>${+func}</c> and <c>${+func(n)}</c> with the return value of a function registered with <see cref=\"regexp.addReplaceFunc\"/>.\n\t\t/// </param>\n\t\t/// <exception cref=\"ArgumentException\">\n\t\t/// - Invalid <c>$replacement</c>.\n\t\t/// - Used a <c>PARTIAL_</c> flag.\n\t\t/// - The regular expression contains <c>(?=...\\K)</c>.\n\t\t/// </exception>\n\t\t/// <remarks>\n\t\t/// Works like <see cref=\"Match.Result\"/>.\n\t\t/// See also: <see cref=\"regexp.Replace(string, string, int, Range?, RXMatchFlags)\"/>.\n\t\t/// </remarks>\n\t\tpublic string ExpandReplacement(string repl) {\n\t\t\tif (repl.NE()) return repl;\n\t\t\tusing (new StringBuilder_(out var b)) {\n\t\t\t\tregexp.ExpandReplacement_(this, repl, b);\n\t\t\t\treturn b.ToString();\n\t\t\t}\n\t\t}\n\t}\n\n\t/// <summary>\n\t/// Regular expression group match info.\n\t/// Used with <see cref=\"RXMatch\"/>, <see cref=\"regexp\"/> and some <see cref=\"String\"/> extension methods.\n\t/// </summary>\n\t/// <remarks>\n\t/// Groups are regular expression parts enclosed in <c>()</c>. Except non-capturing parts, like <c>(?:...)</c> and <c>(?options)</c>. A <c>RXGroup</c> variable contains info about a group found in the subject string: index, length, substring.\n\t/// \n\t/// Some groups specified in regular expression may not exist in the subject string even if it matches the regular expression. For example, regular expression <c>\"A(\\d+)?B\"</c> matches string <c>\"AB\"</c>, but group <c>(\\d+)</c> does not exist. Then <see cref=\"Exists\"/> is <c>false</c>, <see cref=\"Start\"/> -1, <see cref=\"Length\"/> 0, <see cref=\"Value\"/> <c>null</c>.\n\t/// \n\t/// When a group matches multiple times, the <c>RXGroup</c> variable contains only the last instance. For example, if subject is <c>\"begin 12 345 67 end\"</c> and regular expression is <c>(\\d+ )+</c>, value of group 1 is <c>\"67\"</c>. If you need all instances (<c>\"12\"</c>, <c>\"345\"</c>, <c>\"67\"</c>), instead use .NET <see cref=\"Regex\"/> and <see cref=\"Group.Captures\"/>. Also you can get all instances with <see cref=\"regexp.Callout\"/>.\n\t/// \n\t/// Examples and more info: <see cref=\"RXMatch\"/>, <see cref=\"regexp\"/>.\n\t/// </remarks>\n\tpublic struct RXGroup {\n\t\treadonly string _subject;\n\t\treadonly int _index; //offset in _subject, or -1 if this group does not exist\n\t\treadonly int _len; //length, or 0 if this group match does not exist\n\n\t\tinternal RXGroup(string subject, int start, int end) {\n\t\t\t_subject = subject;\n\t\t\t_index = start;\n\t\t\t_len = end - start; //note: can be <0 if (?=...\\K). It's OK.\n\t\t}\n\n\t\tinternal RXGroup(string subject, StartEnd r) {\n\t\t\t_subject = subject;\n\t\t\t_index = r.start;\n\t\t\t_len = r.Length; //note: can be <0 if (?=...\\K). It's OK.\n\t\t}\n\n\t\tinternal string Subject_ => _subject;\n\n\t\t/// <summary>\n\t\t/// Gets start offset of the group match in the subject string.\n\t\t/// </summary>\n\t\tpublic int Start => _index;\n\n\t\t/// <summary>\n\t\t/// Gets length of the group match in the subject string.\n\t\t/// </summary>\n\t\tpublic int Length => _len;\n\n\t\t/// <summary>\n\t\t/// Gets end offset of the group match in the subject string (<see cref=\"Start\"/> + <see cref=\"Length\"/>).\n\t\t/// </summary>\n\t\tpublic int End => _index + _len;\n\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if the group exists in the subject string, <c>false</c> if does not exist.\n\t\t/// More info in <see cref=\"RXGroup\"/> topic. Example in <see cref=\"RXMatch\"/> topic.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Other ways to detect it: if a group does not exist, its <see cref=\"Start\"/> is -1 and <see cref=\"Value\"/> is <c>null</c>.\n\t\t/// </remarks>\n\t\tpublic bool Exists => _index >= 0;\n\n\t\t/// <summary>\n\t\t/// Gets span of the subject string from <see cref=\"Start\"/> to <see cref=\"End\"/>.\n\t\t/// </summary>\n\t\t/// <returns><c>default</c> if the group does not exist in the subject string (see <see cref=\"Exists\"/>).</returns>\n\t\t/// <remarks>\n\t\t/// Unlike <see cref=\"Value\"/>, does not create new string.\n\t\t/// </remarks>\n\t\tpublic RStr Span => _len > 0 ? _subject.AsSpan(_index, _len) : (_index < 0 ? default : \"\"); //_len can be < 0\n\n\t\t/// <summary>\n\t\t/// Gets substring of the subject string from <see cref=\"Start\"/> to <see cref=\"End\"/>.\n\t\t/// </summary>\n\t\t/// <returns><c>null</c> if the group does not exist in the subject string (see <see cref=\"Exists\"/>).</returns>\n\t\t/// <remarks>\n\t\t/// Creates new string each time. See also <see cref=\"Span\"/>.\n\t\t/// </remarks>\n\t\tpublic string Value => _len > 0 ? _subject[_index..End] : (_index < 0 ? null : \"\"); //_len can be < 0\n\n\t\t/// <summary>\n\t\t/// Returns <see cref=\"Value\"/>.\n\t\t/// </summary>\n\t\tpublic override string ToString() => Value;\n\t\t//public override string ToString() => _subject != null ? Value : $\"{_index}..{End}\";\n\n\t\t/// <summary>\n\t\t/// Gets substring of the subject string from <see cref=\"Start\"/> to <see cref=\"End\"/>.\n\t\t/// Returns <c>null</c> if the group does not exist in the subject string (see <see cref=\"Exists\"/>).\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Use this function instead of <see cref=\"Value\"/> with results of <see cref=\"regexp\"/> functions where subject is <c>ReadOnlySpan</c>.\n\t\t/// </remarks>\n\t\t/// <param name=\"subject\">Must be the same subject string as passed to the <see cref=\"regexp\"/> function that returned this result.</param>\n\t\tinternal string GetValue_(RStr subject) => _len > 0 ? subject[_index..End].ToString() : (_index < 0 ? null : \"\");\n\n\t\t/// <summary>\n\t\t/// Gets span of the subject string from <see cref=\"Start\"/> to <see cref=\"End\"/>.\n\t\t/// Returns <c>null</c> if the group does not exist in the subject string (see <see cref=\"Exists\"/>).\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Use this function instead of <see cref=\"Span\"/> with results of <see cref=\"regexp\"/> functions where subject is <c>ReadOnlySpan</c>.\n\t\t/// </remarks>\n\t\t/// <param name=\"subject\">Must be the same subject string as passed to the <see cref=\"regexp\"/> function that returned this result.</param>\n\t\tinternal RStr GetSpan_(RStr subject) => _len > 0 ? subject.Slice(_index, _len) : (_index < 0 ? default : \"\");\n\n\t\t///\n\t\tpublic static implicit operator Range(RXGroup g) => g.Exists ? g.Start..g.End : default;\n\t}\n\n\n\t#region callout\n\n\t/// <summary>\n\t/// Managed version of PCRE API struct <c>pcre2_callout_block</c>.\n\t/// When you set <see cref=\"regexp.Callout\"/>, your callout function's parameter is of this type.\n\t/// </summary>\n\t/// <remarks>\n\t/// More info in PCRE help topic <see href=\"https://www.pcre.org/current/doc/html/pcre2callout.html\">pcre2callout</see>.\n\t/// Most properties are <c>pcre2_callout_block</c> fields as documented in PCRE help. Other properties and methods are easier/safer versions of unsafe fields like <c>offset_vector</c>.\n\t/// </remarks>\n\tpublic unsafe struct RXCalloutData {\n#pragma warning disable 649 //field never assigned\n\t\tstruct pcre2_callout_block {\n\t\t\tpublic int version;\n\t\t\tpublic readonly int callout_number, capture_top, capture_last;\n\t\t\tpublic readonly nint* vec;\n\t\t\tpublic readonly char* mark, subject;\n\t\t\tpublic readonly nint subject_length;\n\t\t\tpublic readonly nint start_match;\n\t\t\tpublic readonly nint current_position;\n\t\t\tpublic readonly nint pattern_position;\n\t\t\tpublic readonly nint next_item_length;\n\t\t\tpublic readonly nint callout_string_offset;\n\t\t\tpublic readonly nint callout_string_length;\n\t\t\tpublic readonly char* callout_string;\n\t\t\tpublic readonly int callout_flags;\n\t\t}\n#pragma warning restore 649\n\n\t\t//We use pointer instead of adding pcre2_callout_block fields to this struct. Other ways are not good:\n\t\t//\tPassing whole block to the final callback by value is slow (104 bytes, tested speed). Also then cannot have Result like now.\n\t\t//\tWith 'in' fast, but then users have to declare lambda parameters like 'in RXCalloutData d'. Now just 'd'.\n\t\tpcre2_callout_block* _p;\n\n\t\t/// <summary>\n\t\t/// Sets the return value of the callout function, as documented in PCRE help topic <see href=\"https://www.pcre.org/current/doc/html/pcre2callout.html\">pcre2callout</see>.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Default 0.\n\t\t/// If 1, matching fails at the current point, but the testing of other matching possibilities goes ahead, just as if a lookahead assertion had failed.\n\t\t/// If -1 (<c>PCRE2_ERROR_NOMATCH</c>), the match function returns <c>false</c> (no match). Values less tan -2 are PCRE error codes and cause exception.\n\t\t/// </remarks>\n\t\tpublic int Result { set => _p->version = value; internal get => _p->version; }\n\n\t\tinternal RXCalloutData(void* calloutBlock) {\n\t\t\t_p = (pcre2_callout_block*)calloutBlock;\n\t\t\tResult = 0;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Callout number, eg 5 for <c>\"(?C5)\"</c>.\n\t\t/// More info in PCRE help topic <see href=\"https://www.pcre.org/current/doc/html/pcre2callout.html\">pcre2callout</see>.\n\t\t/// </summary>\n\t\tpublic int callout_number => _p->callout_number;\n\n\t\t/// <summary>\n\t\t/// One more than the number of the highest numbered captured group so far.\n\t\t/// More info in PCRE help topic <see href=\"https://www.pcre.org/current/doc/html/pcre2callout.html\">pcre2callout</see>.\n\t\t/// </summary>\n\t\tpublic int capture_top => _p->capture_top;\n\n\t\t/// <summary>\n\t\t/// The number of the most recently captured group.\n\t\t/// More info in PCRE help topic <see href=\"https://www.pcre.org/current/doc/html/pcre2callout.html\">pcre2callout</see>.\n\t\t/// </summary>\n\t\tpublic int capture_last => _p->capture_last;\n\n\t\t/// <summary>\n\t\t/// Flags.\n\t\t/// 1 <c>PCRE2_CALLOUT_STARTMATCH</c>, 2 <c>PCRE2_CALLOUT_BACKTRACK</c>.\n\t\t/// More info in PCRE help topic <see href=\"https://www.pcre.org/current/doc/html/pcre2callout.html\">pcre2callout</see>.\n\t\t/// </summary>\n\t\tpublic int callout_flags => _p->callout_flags;\n\n\t\t/// <summary>\n\t\t/// The offset within the subject string at which the current match attempt started. But depends on <c>\\K</c> etc.\n\t\t/// More info in PCRE help topic <see href=\"https://www.pcre.org/current/doc/html/pcre2callout.html\">pcre2callout</see>.\n\t\t/// </summary>\n\t\tpublic int start_match => (int)_p->start_match;\n\n\t\t/// <summary>\n\t\t/// The current offset within the subject string.\n\t\t/// </summary>\n\t\tpublic int current_position => (int)_p->current_position;\n\n\t\t/// <summary>\n\t\t/// The offset in the regular expression to the next item to be matched.\n\t\t/// </summary>\n\t\tpublic int pattern_position => (int)_p->pattern_position;\n\n\t\t/// <summary>\n\t\t/// The length of the next item to be processed in the regular expression.\n\t\t/// More info in PCRE help topic <see href=\"https://www.pcre.org/current/doc/html/pcre2callout.html\">pcre2callout</see>.\n\t\t/// </summary>\n\t\tpublic int next_item_length => (int)_p->next_item_length;\n\n\t\t/// <summary>\n\t\t/// The callout string offset in the regular expression. Used with callouts like <c>\"(?C'calloutString')\"</c>.\n\t\t/// More info in PCRE help topic <see href=\"https://www.pcre.org/current/doc/html/pcre2callout.html\">pcre2callout</see>.\n\t\t/// </summary>\n\t\tpublic int callout_string_offset => (int)_p->callout_string_offset;\n\n\t\t/// <summary>\n\t\t/// The callout string, eg <c>\"xyz\"</c> for <c>\"(?C'xyz')\"</c>.\n\t\t/// More info in PCRE help topic <see href=\"https://www.pcre.org/current/doc/html/pcre2callout.html\">pcre2callout</see>.\n\t\t/// </summary>\n\t\tpublic string callout_string => _p->callout_string == null ? null : new string(_p->callout_string, 0, (int)_p->callout_string_length);\n\n\t\t/// <summary>\n\t\t/// The most recently passed <c>(*MARK)</c>, <c>(*PRUNE)</c>, or <c>(*THEN)</c> item in the match, or <c>null</c> if no such items have been passed.\n\t\t/// More info in PCRE help topic <see href=\"https://www.pcre.org/current/doc/html/pcre2callout.html\">pcre2callout</see>.\n\t\t/// </summary>\n\t\tpublic string mark => _p->mark == null ? null : new string(_p->mark);\n\n\t\t/// <summary>\n\t\t/// Gets the start index and length of the specified group in the subject string.\n\t\t/// </summary>\n\t\t/// <param name=\"group\">Group number (1-based index).</param>\n\t\t/// <exception cref=\"ArgumentOutOfRangeException\"><i>group</i> must be > 0 and &lt; <see cref=\"capture_top\"/>.</exception>\n\t\tpublic (int index, int length) Group(int group) {\n\t\t\tif (group <= 0 || group >= _p->capture_top) throw new ArgumentOutOfRangeException(nameof(group), \"Must be > 0 and < capture_top.\");\n\t\t\tvar v = _p->vec;\n\t\t\tint i = (int)v[group *= 2];\n\t\t\treturn (i, (int)v[group + 1] - i);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets the value (substring) of the specified group.\n\t\t/// </summary>\n\t\t/// <param name=\"group\">Group number (1-based index).</param>\n\t\t/// <exception cref=\"ArgumentOutOfRangeException\"><i>group</i> must be > 0 and &lt; <see cref=\"capture_top\"/>.</exception>\n\t\tpublic string GroupValue(int group) {\n\t\t\tvar (i, len) = Group(group);\n\t\t\tif (i < 0) return null;\n\t\t\tif (len == 0) return \"\";\n\t\t\treturn new string(_p->subject, i, len);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets the start index and length of the most recently captured group in the subject string.\n\t\t/// </summary>\n\t\tpublic (int index, int length) LastGroup => Group(_p->capture_last);\n\n\t\t/// <summary>\n\t\t/// Gets the value (substring) of the most recently captured group.\n\t\t/// </summary>\n\t\tpublic string LastGroupValue => GroupValue(_p->capture_last);\n\t}\n\n\t#endregion\n\n#pragma warning disable 1591 //no XML doc\n\t/// <summary>\n\t/// Flags for <see cref=\"regexp\"/> constructor.\n\t/// Documented in PCRE help topic <see href=\"https://www.pcre.org/current/doc/html/pcre2api.html\">pcre2api</see>.\n\t/// </summary>\n\t/// <remarks>\n\t/// Many options also can be specified in regular expression (RE):\n\t/// - These can be anywhere in RE: <c>(?i)</c> <c>CASELESS</c>, <c>(?m)</c> <c>MULTILINE</c>, <c>(?s)</c> <c>DOTALL</c>, <c>(?n)</c> <c>NO_AUTO_CAPTURE</c>, <c>(?x)</c> <c>EXTENDED</c>, <c>(?xx)</c> <c>EXTENDED_MORE</c>, <c>(?J)</c> <c>DUPNAMES</c>, <c>(?U)</c> <c>UNGREEDY</c>. Can be multiple, like <c>(?ms)</c>. Can be unset, like <c>(?-i)</c>. RE <c>\"\\Qtext\\E\"</c> is like RE <c>\"text\"</c> with flag <c>LITERAL</c>.\n\t/// - Instead of <c>ANCHORED</c> can be used <c>\\G</c> at the start of RE. Or <c>^</c>, except in multiline mode.\n\t/// - Instead of <c>ENDANCHORED</c> can be used <c>\\z</c> at the end of RE. Or <c>$</c>, except in multiline mode.\n\t/// - Flag UTF is implicitly added if RE contains non-ASCII characters and there is no flag <c>NEVER_UTF</c>.\n\t/// - These must be at the very start and are named like flags: <c>(*UTF)</c>, <c>(*UCP)</c>, <c>(*NOTEMPTY)</c>, <c>(*NOTEMPTY_ATSTART)</c>, <c>(*NO_AUTO_POSSESS)</c>, <c>(*NO_DOTSTAR_ANCHOR)</c>, <c>(*NO_START_OPT)</c>.\n\t/// - More info in <see href=\"https://www.pcre.org/current/doc/html/pcre2pattern.html\">PCRE syntax reference</see>.\n\t/// \n\t/// Some of <c>RXFlags</c> flags also exist in <see cref=\"RXMatchFlags\"/>. You can set them either when calling <see cref=\"regexp\"/> constructor or when calling <see cref=\"regexp\"/> functions that have parameter <i>more</i>. You can use different flags for each function call with the same <see cref=\"regexp\"/> variable.\n\t/// </remarks>\n\t[Flags]\n\tpublic enum RXFlags : ulong {\n\t\t/// <summary>Match only at the first position</summary>\n\t\tANCHORED = 0x80000000,\n\n\t\t/// <summary>Pattern can match only at end of subject</summary>\n\t\tENDANCHORED = 0x20000000,\n\n\t\t/// <summary>Do not check the pattern for UTF validity</summary>\n\t\tNO_UTF_CHECK = 0x40000000,\n\n\t\t/// <summary>Allow empty classes</summary>\n\t\tALLOW_EMPTY_CLASS = 0x00000001,\n\n\t\t/// <summary>Alternative handling of <c>\\u</c>, <c>\\U</c>, and <c>\\x</c></summary>\n\t\tALT_BSUX = 0x00000002,\n\n\t\t/// <summary>Compile automatic callouts</summary>\n\t\tAUTO_CALLOUT = 0x00000004,\n\n\t\t/// <summary>Do caseless matching</summary>\n\t\tCASELESS = 0x00000008,\n\n\t\t/// <summary><c>$</c> not to match newline at end</summary>\n\t\tDOLLAR_ENDONLY = 0x00000010,\n\n\t\t/// <summary><c>.</c> matches anything including newlines</summary>\n\t\tDOTALL = 0x00000020,\n\n\t\t/// <summary>Allow duplicate names for subpatterns</summary>\n\t\tDUPNAMES = 0x00000040,\n\n\t\t/// <summary>Ignore white space and <c>#</c> comments</summary>\n\t\tEXTENDED = 0x00000080,\n\n\t\t/// <summary>Force matching to be before newline</summary>\n\t\tFIRSTLINE = 0x00000100,\n\n\t\t/// <summary>Match unset backreferences</summary>\n\t\tMATCH_UNSET_BACKREF = 0x00000200,\n\n\t\t/// <summary><c>^</c> and <c>$</c> match newlines within data</summary>\n\t\tMULTILINE = 0x00000400,\n\n\t\t/// <summary>Lock out <c>UCP</c>, e.g. via <c>(*UCP)</c></summary>\n\t\tNEVER_UCP = 0x00000800,\n\n\t\t/// <summary>Lock out <c>UTF</c>, e.g. via <c>(*UTF)</c></summary>\n\t\tNEVER_UTF = 0x00001000,\n\n\t\t/// <summary>Disable numbered capturing paren-theses (named ones available)</summary>\n\t\tNO_AUTO_CAPTURE = 0x00002000,\n\n\t\t/// <summary>Disable auto-possessification</summary>\n\t\tNO_AUTO_POSSESS = 0x00004000,\n\n\t\t/// <summary>Disable automatic anchoring for <c>.*</c></summary>\n\t\tNO_DOTSTAR_ANCHOR = 0x00008000,\n\n\t\t/// <summary>Disable match-time start optimizations</summary>\n\t\tNO_START_OPTIMIZE = 0x00010000,\n\n\t\t/// <summary>Use Unicode properties for <c>\\d</c>, <c>\\w</c>, etc</summary>\n\t\tUCP = 0x00020000,\n\n\t\t/// <summary>Invert greediness of quantifiers</summary>\n\t\tUNGREEDY = 0x00040000,\n\n\t\t/// <summary>\n\t\t/// Fully support Unicode text (case-insensitivity etc). More info in PCRE documentation topic <see href=\"https://www.pcre.org/current/doc/html/pcre2unicode.html\">pcre2unicode</see>.\n\t\t/// This flag is implicitly added if regular expression contains non-ASCII characters and there is no flag <c>NEVER_UTF</c>.\n\t\t/// </summary>\n\t\tUTF = 0x00080000,\n\n\t\t/// <summary>Lock out the use of <c>\\C</c> in patterns</summary>\n\t\tNEVER_BACKSLASH_C = 0x00100000,\n\n\t\t/// <summary>Alternative handling of <c>^</c> in multiline mode</summary>\n\t\tALT_CIRCUMFLEX = 0x00200000,\n\n\t\t/// <summary>Process backslashes in verb names</summary>\n\t\tALT_VERBNAMES = 0x00400000,\n\n\t\t//USE_OFFSET_LIMIT = 0x00800000, //used with pcre2_set_offset_limit(), but currently we don't support it\n\n\t\t/// <summary></summary>\n\t\tEXTENDED_MORE = 0x01000000,\n\n\t\t/// <summary>Pattern characters are all literal</summary>\n\t\tLITERAL = 0x02000000,\n\n\t\t/// <summary>Enable support for matching invalid UTF</summary>\n\t\tMATCH_INVALID_UTF = 0x04000000,\n\n\t\t/// <summary>Alternative extended character class syntax</summary>\n\t\tALT_EXTENDED_CLASS = 0x08000000,\n\n\t\t//PCRE2_EXTRA_ flags.\n\n\t\t//EXTRA_ALLOW_SURROGATE_ESCAPES = 0x1L << 32, //not used with UTF-16\n\t\t//EXTRA_BAD_ESCAPE_IS_LITERAL = 0x2L << 32, //dangerous\n\n\t\t[EditorBrowsable(EditorBrowsableState.Never)] //bad name\n\t\tMATCH_WORD = 0x4L << 32,\n\t\t[EditorBrowsable(EditorBrowsableState.Never)] //bad name\n\t\tMATCH_LINE = 0x8L << 32,\n\n\t\t/// <summary>Pattern matches \"words\"</summary>\n\t\tEXTRA_MATCH_WORD = 0x4L << 32,\n\n\t\t/// <summary>Pattern matches whole lines</summary>\n\t\tEXTRA_MATCH_LINE = 0x8L << 32,\n\n\t\t//EXTRA_ALT_BSUX //noise\n\t\t//EXTRA_ALLOW_LOOKAROUND_BSK //fbc\n\n\t\t/// <summary>Disable mixed ASCII/non-ASCII case folding</summary>\n\t\tEXTRA_CASELESS_RESTRICT = 0x80L << 32,\n\n\t\t/// <summary><c>\\d</c> remains ASCII in UCP mode</summary>\n\t\tEXTRA_ASCII_BSD = 0x100L << 32,\n\n\t\t/// <summary><c>\\s</c> remains ASCII in UCP mode</summary>\n\t\tEXTRA_ASCII_BSS = 0x200L << 32,\n\n\t\t/// <summary><c>\\w</c> remains ASCII in UCP mode</summary>\n\t\tEXTRA_ASCII_BSW = 0x400L << 32,\n\n\t\t/// <summary>POSIX classes remain ASCII in UCP mode</summary>\n\t\tEXTRA_ASCII_POSIX = 0x800L << 32,\n\n\t\t/// <summary><c>[:digit:]</c> and <c>[:xdigit:]</c> POSIX classes remain ASCII in UCP mode</summary>\n\t\tEXTRA_ASCII_DIGIT = 0x1000L << 32,\n\n\t\t/// <summary>Disallow callouts in pattern</summary>\n\t\tEXTRA_NEVER_CALLOUT = 0x8000L << 32,\n\n\t\t/// <summary>Use Turkish I case folding</summary>\n\t\tEXTRA_TURKISH_CASING = 0x10000L << 32,\n\n\t\t//EXTRA_PYTHON_OCTAL\n\t\t//EXTRA_NO_BS0 //rare\n\n\t\t//Match API flags. regexp ctor moves them to a field that later is combined with RXMatchFlags when calling the match API.\n\n\t\t/// <summary>Subject string is not the beginning of a line</summary>\n\t\tNOTBOL = 0x00000001L << 56, //hi byte of long\n\n\t\t/// <summary>Subject string is not the end of a line</summary>\n\t\tNOTEOL = 0x00000002L << 56,\n\n\t\t/// <summary>An empty string is not a valid match</summary>\n\t\tNOTEMPTY = 0x00000004L << 56,\n\n\t\t/// <summary>An empty string at the start of the subject is not a valid match</summary>\n\t\tNOTEMPTY_ATSTART = 0x00000008L << 56,\n\n\t\t/// <summary>Allow partial soft match. See <see cref=\"RXMatch.IsPartial\"/>.</summary>\n\t\tPARTIAL_SOFT = 0x00000010L << 56,\n\n\t\t/// <summary>Allow partial hard match. See <see cref=\"RXMatch.IsPartial\"/>.</summary>\n\t\tPARTIAL_HARD = 0x00000020L << 56,\n\t}\n\n\t/// <summary>\n\t/// Flags for <see cref=\"regexp\"/> class functions.\n\t/// Documented in PCRE help topic <see href=\"https://www.pcre.org/current/doc/html/pcre2api.html\">pcre2api</see>.\n\t/// </summary>\n\t/// <remarks>\n\t/// These flags also exist in <see cref=\"RXFlags\"/> (<see cref=\"regexp\"/> constructor flags). You can set them either when calling constructor or when calling other functions.\n\t/// </remarks>\n\t[Flags]\n\tpublic enum RXMatchFlags : uint {\n\t\t//These are the same as in RXFlags, and can be used either when compiling or when matching.\n\n\t\t/// <summary>Match only at the first position</summary>\n\t\tANCHORED = 0x80000000,\n\n\t\t/// <summary>Pattern can match only at end of subject</summary>\n\t\tENDANCHORED = 0x20000000,\n\n\t\t/// <summary>Do not check the subject for UTF validity</summary>\n\t\tNO_UTF_CHECK = 0x40000000,\n\n\t\t//These are only for matching. Also added to the hi int of RXFlags.\n\n\t\t/// <summary>Subject string is not the beginning of a line</summary>\n\t\tNOTBOL = 0x00000001,\n\n\t\t/// <summary>Subject string is not the end of a line</summary>\n\t\tNOTEOL = 0x00000002,\n\n\t\t/// <summary>An empty string is not a valid match</summary>\n\t\tNOTEMPTY = 0x00000004,\n\n\t\t/// <summary>An empty string at the start of the subject is not a valid match</summary>\n\t\tNOTEMPTY_ATSTART = 0x00000008,\n\n\t\t/// <summary>Allow partial soft match. See <see cref=\"RXMatch.IsPartial\"/>.</summary>\n\t\tPARTIAL_SOFT = 0x00000010,\n\n\t\t/// <summary>Allow partial hard match. See <see cref=\"RXMatch.IsPartial\"/>.</summary>\n\t\tPARTIAL_HARD = 0x00000020,\n\n\t\t//COPY_MATCHED_SUBJECT //this library does not use the PCRE API for wchich would need this\n\t\t//DISABLE_RECURSELOOP_CHECK //for PCRE testing\n\t\t//NO_JIT\n\t}\n#pragma warning restore 1591\n\n\tinternal static unsafe partial class Cpp {\n\t\t/// <summary>This and related API are documented in the C++ dll project.</summary>\n\t\t[DllImport(\"AuCpp.dll\", CallingConvention = CallingConvention.Cdecl)]\n\t\tinternal static extern nint Cpp_RegexCompile(string rx, nint len, RXFlags flags, out int codeSize, out BSTR errStr);\n\n\t\t[DllImport(\"AuCpp.dll\", CallingConvention = CallingConvention.Cdecl)]\n\t\tinternal static extern int Cpp_RegexDtor(IntPtr code);\n\n\t\t/// <summary>This and related API are documented in the C++ dll project.</summary>\n\t\tinternal struct RegexMatch {\n\t\t\tpublic StartEnd* vec;\n\t\t\tpublic int vecCount;\n\t\t\tpublic int indexNoK;\n\t\t\tpublic char* mark;\n\n\t\t\tpublic string Mark => mark == null ? null : new(mark, 0, mark[-1]);\n\t\t}\n\n\t\tinternal unsafe delegate int PcreCalloutT(void* calloutBlock, void* param);\n\n\t\t/// <summary>This and related API are documented in the C++ dll project.</summary>\n\t\t[DllImport(\"AuCpp.dll\", CallingConvention = CallingConvention.Cdecl)]\n\t\tinternal static extern int Cpp_RegexMatch(HandleRef code, char* s, nint len, nint start, RXMatchFlags flags,\n\t\t\tPcreCalloutT callout, out RegexMatch m, bool needM, out BSTR errStr);\n\t\t//note: don't use [MarshalAs(UnmanagedType.BStr)] out string errStr, it makes much slower.\n\n\t\t#region PCRE API\n\n\t\t//internal enum PCRE2_ERROR_\n\t\t//{\n\t\t//\tPARTIAL = 0, //note: the PCRE API value is -2, but our C++ dll API then returns 0\n\t\t//\tNOMATCH = -1,\n\t\t//\tCALLOUT = -37,\n\t\t//\tNOMEMORY = -48,\n\t\t//\tNOUNIQUESUBSTRING = -50,\n\t\t//\t//others not useful\n\t\t//}\n\n\t\t[DllImport(\"AuCpp.dll\", CallingConvention = CallingConvention.Cdecl)]\n\t\tinternal static extern int pcre2_pattern_info(HandleRef code, PCRE2_INFO_ what, void* where);\n\n\t\t[DllImport(\"AuCpp.dll\", CallingConvention = CallingConvention.Cdecl)]\n\t\tinternal static extern int pcre2_substring_nametable_scan(HandleRef code, char* name, ushort** first, ushort** last);\n\n\t\tinternal enum PCRE2_INFO_ {\n\t\t\tALLOPTIONS = 0,\n\t\t\t//ARGOPTIONS = 1,\n\t\t\t//BACKREFMAX = 2,\n\t\t\t//BSR = 3,\n\t\t\tCAPTURECOUNT = 4,\n\t\t\t//FIRSTCODEUNIT = 5,\n\t\t\t//FIRSTCODETYPE = 6,\n\t\t\t//FIRSTBITMAP = 7,\n\t\t\t//HASCRORLF = 8,\n\t\t\t//JCHANGED = 9,\n\t\t\t//JITSIZE = 10,\n\t\t\t//LASTCODEUNIT = 11,\n\t\t\t//LASTCODETYPE = 12,\n\t\t\t//MATCHEMPTY = 13,\n\t\t\t//MATCHLIMIT = 14,\n\t\t\t//MAXLOOKBEHIND = 15,\n\t\t\t//MINLENGTH = 16,\n\t\t\t//NAMECOUNT = 17,\n\t\t\t//NAMEENTRYSIZE = 18,\n\t\t\t//NAMETABLE = 19,\n\t\t\t//NEWLINE = 20,\n\t\t\t//DEPTHLIMIT = 21,\n\t\t\t//SIZE = 22,\n\t\t\t//HASBACKSLASHC = 23,\n\t\t\t//FRAMESIZE = 24,\n\t\t\t//HEAPLIMIT = 25,\n\t\t}\n\n\t\t#endregion\n\t}\n}\n"
  },
  {
    "path": "Au/String/wildcard.cs",
    "content": "using System.Text.RegularExpressions;\n\nnamespace Au {\n\t/// <summary>\n\t/// Parses and compares [wildcard expression](xref:wildcard_expression).\n\t/// </summary>\n\t/// <remarks>\n\t/// Used in \"find\" functions. For example in <see cref=\"wnd.find\"/> to compare window name, class name and program.\n\t/// The \"find\" function creates a <c>wildex</c> instance (which parses the wildcard expression), then calls <see cref=\"Match\"/> for each item (eg window) to compare some its property text.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// //This version does not support wildcard expressions.\n\t/// Document Find1(string name, string date) {\n\t/// \treturn Documents.Find(x => x.Name.Eqi(name) && x.Date.Eqi(date));\n\t/// }\n\t/// \n\t/// //This version supports wildcard expressions.\n\t/// //null-string arguments are not compared.\n\t/// Document Find2(string name, string date) {\n\t/// \twildex n = name, d = date; //null if the string is null\n\t/// \treturn Documents.Find(x => (n == null || n.Match(x.Name)) && (d == null || d.Match(x.Date)));\n\t/// }\n\t/// \n\t/// //Example of calling such function.\n\t/// //Find item whose name is \"example\" (case-insensitive) and date starts with \"2017-\".\n\t/// var item = x.Find2(\"example\", \"2017-*\");\n\t/// ]]></code>\n\t/// </example>\n\tpublic class wildex {\n\t\t//note: could be struct, but somehow then slower. Slower instance creation, calling methods, in all cases.\n\t\t\n\t\treadonly object _o; //string, regexp, Regex or wildex[]. Tested: getting string etc with '_o as string' is fast.\n\t\treadonly WXType _type;\n\t\treadonly bool _ignoreCase;\n\t\treadonly bool _not;\n\t\t\n\t\t/// <param name=\"wildcardExpression\">\n\t\t/// [Wildcard expression](xref:wildcard_expression).\n\t\t/// Cannot be <c>null</c> (throws exception).\n\t\t/// <c>\"\"</c> will match <c>\"\"</c>.\n\t\t/// </param>\n\t\t/// <param name=\"matchCase\">Case-sensitive even if there is no <c>**c</c>.</param>\n\t\t/// <param name=\"noException\">If <i>wildcardExpression</i> is invalid, don't throw exception; let <see cref=\"Match(string)\"/> always return <c>false</c>.</param>\n\t\t/// <exception cref=\"ArgumentNullException\"></exception>\n\t\t/// <exception cref=\"ArgumentException\">Invalid <c>\"**options \"</c> or regular expression.</exception>\n\t\tpublic wildex([ParamString(PSFormat.Wildex)] string wildcardExpression, bool matchCase = false, bool noException = false) {\n\t\t\tNot_.Null(wildcardExpression);\n\t\t\tvar s = wildcardExpression;\n\t\t\ttry {\n\t\t\t\t_type = WXType.Wildcard;\n\t\t\t\t_ignoreCase = !matchCase;\n\t\t\t\tstring split = null;\n\t\t\t\t\n\t\t\t\tif (s is ['*', '*', _, ..]) {\n\t\t\t\t\tfor (int i = 2, j; i < s.Length; i++) {\n\t\t\t\t\t\tswitch (s[i]) {\n\t\t\t\t\t\tcase ' ':\n\t\t\t\t\t\t\ts = s[(i + 1)..];\n\t\t\t\t\t\t\tgoto g1;\n\t\t\t\t\t\tcase 't' or 'r' or 'R' or 'm' when _type == WXType.Wildcard:\n\t\t\t\t\t\t\t_type = s[i] switch { 't' => WXType.Text, 'r' => WXType.RegexPcre, 'R' => WXType.RegexNet, _ => WXType.Multi };\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'c':\n\t\t\t\t\t\t\t_ignoreCase = false;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'n':\n\t\t\t\t\t\t\t_not = true;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase '(':\n\t\t\t\t\t\t\tif (s[i - 1] != 'm') goto ge;\n\t\t\t\t\t\t\tfor (j = ++i; j < s.Length; j++) if (s[j] == ')') break;\n\t\t\t\t\t\t\tif (j == s.Length || j == i) goto ge;\n\t\t\t\t\t\t\tsplit = s[i..j];\n\t\t\t\t\t\t\ti = j;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tdefault: goto ge;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tge:\n\t\t\t\t\tthrow new ArgumentException(\"Invalid \\\"**options \\\" in wildcard expression.\");\n\t\t\t\t\tg1:\n\t\t\t\t\tswitch (_type) {\n\t\t\t\t\tcase WXType.RegexNet:\n\t\t\t\t\t\tvar ro = _ignoreCase ? (RegexOptions.IgnoreCase | RegexOptions.CultureInvariant) : RegexOptions.CultureInvariant;\n\t\t\t\t\t\t_o = new Regex(s, ro);\n\t\t\t\t\t\treturn;\n\t\t\t\t\tcase WXType.RegexPcre:\n\t\t\t\t\t\t_o = new regexp(s, _ignoreCase ? RXFlags.CASELESS : 0);\n\t\t\t\t\t\treturn;\n\t\t\t\t\tcase WXType.Multi:\n\t\t\t\t\t\tvar a = s.Split(split ?? \"||\");\n\t\t\t\t\t\tvar multi = new wildex[a.Length];\n\t\t\t\t\t\tfor (int i = 0; i < a.Length; i++) multi[i] = new wildex(a[i], !_ignoreCase);\n\t\t\t\t\t\t_o = multi;\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (_type == WXType.Wildcard && !hasWildcardChars(s)) _type = WXType.Text;\n\t\t\t\t_o = s;\n\t\t\t}\n\t\t\tcatch when (noException) { _type = WXType.Error; }\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Creates new <see cref=\"wildex\"/> from wildcard expression string.\n\t\t/// If the string is <c>null</c>, returns <c>null</c>.\n\t\t/// </summary>\n\t\t/// <param name=\"wildcardExpression\">[Wildcard expression](xref:wildcard_expression). </param>\n\t\t/// <exception cref=\"ArgumentException\">Invalid <c>\"**options \"</c> or regular expression.</exception>\n\t\tpublic static implicit operator wildex([ParamString(PSFormat.Wildex)] string wildcardExpression) {\n\t\t\tif (wildcardExpression == null) return null;\n\t\t\treturn new wildex(wildcardExpression);\n\t\t}\n\t\t\n\t\t//rejected: ReadOnlySpan<char>. Then cannot use eg .NET Regex.\n\t\t\n\t\t/// <summary>\n\t\t/// Compares a string with the [wildcard expression](xref:wildcard_expression) used to create this <see cref=\"wildex\"/>. Returns <c>true</c> if they match.\n\t\t/// </summary>\n\t\t/// <param name=\"s\">String. If <c>null</c>, returns <c>false</c>. If <c>\"\"</c>, returns <c>true</c> if it was <c>\"\"</c> or <c>\"*\"</c> or a regular expression that matches <c>\"\"</c>.</param>\n\t\tpublic bool Match(string s) {\n\t\t\tif (s == null) return false;\n\t\t\t\n\t\t\tbool R = false;\n\t\t\tswitch (_type) {\n\t\t\tcase WXType.Wildcard:\n\t\t\t\tR = s.Like(_o as string, _ignoreCase);\n\t\t\t\tbreak;\n\t\t\tcase WXType.Text:\n\t\t\t\tR = s.Eq(_o as string, _ignoreCase);\n\t\t\t\tbreak;\n\t\t\tcase WXType.RegexPcre:\n\t\t\t\tR = (_o as regexp).IsMatch(s);\n\t\t\t\tbreak;\n\t\t\tcase WXType.RegexNet:\n\t\t\t\tR = (_o as Regex).IsMatch(s);\n\t\t\t\tbreak;\n\t\t\tcase WXType.Multi:\n\t\t\t\tvar multi = _o as wildex[];\n\t\t\t\t//[n] parts: all must match (with their option n applied)\n\t\t\t\tint nNot = 0;\n\t\t\t\tfor (int i = 0; i < multi.Length; i++) {\n\t\t\t\t\tvar v = multi[i];\n\t\t\t\t\tif (v.Not) {\n\t\t\t\t\t\tif (!v.Match(s)) return _not; //!v.Match(s) means 'matches if without option n applied'\n\t\t\t\t\t\tnNot++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (nNot == multi.Length) return !_not; //there are no parts without option n\n\t\t\t\t\n\t\t\t\t//non-[n] parts: at least one must match\n\t\t\t\tfor (int i = 0; i < multi.Length; i++) {\n\t\t\t\t\tvar v = multi[i];\n\t\t\t\t\tif (!v.Not && v.Match(s)) return !_not;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tdefault: //Error\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn R ^ _not;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Returns the text or wildcard string.\n\t\t/// <c>null</c> if <see cref=\"TextType\"/> is not <c>Text</c> or <c>Wildcard</c>.\n\t\t/// </summary>\n\t\tpublic string Text => _o as string;\n\t\t\n\t\t/// <summary>\n\t\t/// Returns the <see cref=\"regexp\"/> object created from regular expression string.\n\t\t/// <c>null</c> if <see cref=\"TextType\"/> is not <c>RegexPcre</c> (no option <c>r</c>).\n\t\t/// </summary>\n\t\tpublic regexp RegexPcre => _o as regexp;\n\t\t\n\t\t/// <summary>\n\t\t/// Gets the <c>Regex</c> object created from regular expression string.\n\t\t/// <c>null</c> if <see cref=\"TextType\"/> is not <c>RegexNet</c> (no option <c>R</c>).\n\t\t/// </summary>\n\t\tpublic Regex RegexNet => _o as Regex;\n\t\t\n\t\t/// <summary>\n\t\t/// Array of <see cref=\"wildex\"/> variables, one for each part in multi-part text.\n\t\t/// <c>null</c> if <see cref=\"TextType\"/> is not <c>Multi</c> (no option <c>m</c>).\n\t\t/// </summary>\n\t\tpublic wildex[] MultiArray => _o as wildex[];\n\t\t\n\t\t/// <summary>\n\t\t/// Gets the type of text (wildcard, regex, etc).\n\t\t/// </summary>\n\t\tpublic WXType TextType => _type;\n\t\t\n\t\t/// <summary>\n\t\t/// Is case-insensitive?\n\t\t/// </summary>\n\t\tpublic bool IgnoreCase => _ignoreCase;\n\t\t\n\t\t/// <summary>\n\t\t/// Has option <c>n</c>?\n\t\t/// </summary>\n\t\tpublic bool Not => _not;\n\t\t\n\t\t///\n\t\tpublic override string ToString() {\n\t\t\treturn _o?.ToString();\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if string contains wildcard characters: <c>'*'</c>, <c>'?'</c>.\n\t\t/// </summary>\n\t\t/// <param name=\"s\">Can be <c>null</c>.</param>\n\t\tpublic static bool hasWildcardChars(RStr s) {\n\t\t\tforeach (var c in s) if (c is '*' or '?') return true;\n\t\t\treturn false;\n\t\t}\n\t}\n}\n\nnamespace Au.Types {\n\tpublic static unsafe partial class ExtString {\n\t\t#region Like\n\t\t\n\t\t/// <summary>\n\t\t/// Compares this string with a string that possibly contains wildcard characters.\n\t\t/// Returns <c>true</c> if the strings match.\n\t\t/// </summary>\n\t\t/// <param name=\"t\">This string. If <c>null</c>, returns <c>false</c>. If <c>\"\"</c>, returns <c>true</c> if <i>pattern</i> is <c>\"\"</c> or <c>\"*\"</c>.</param>\n\t\t/// <param name=\"pattern\">String that possibly contains wildcard characters. Cannot be <c>null</c>. If <c>\"\"</c>, returns <c>true</c> if this string is <c>\"\"</c>. If <c>\"*\"</c>, always returns <c>true</c> except when this string is <c>null</c>.</param>\n\t\t/// <param name=\"ignoreCase\">Case-insensitive.</param>\n\t\t/// <exception cref=\"ArgumentNullException\"><i>pattern</i> is <c>null</c>.</exception>\n\t\t/// <remarks>\n\t\t/// Wildcard characters:\n\t\t/// \n\t\t/// | Character | Will match | Examples\n\t\t/// | -\n\t\t/// | <c>*</c> | Zero or more of any characters. | <c>\"start*\"</c>, <c>\"*end\"</c>, <c>\"*middle*\"</c>\n\t\t/// | <c>?</c> | Any single character. | <c>\"date ????-??-??\"</c>\n\t\t/// \n\t\t/// There are no escape sequences for <c>*</c> and <c>?</c> characters.\n\t\t/// \n\t\t/// Uses ordinal comparison, ie does not depend on current culture.\n\t\t/// \n\t\t/// See also: [wildcard expression](xref:wildcard_expression).\n\t\t/// </remarks>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// string s = @\"C:\\abc\\mno.xyz\";\n\t\t/// if(s.Like(@\"C:\\abc\\mno.xyz\")) print.it(\"matches whole text (no wildcard characters)\");\n\t\t/// if(s.Like(@\"C:\\abc\\*\")) print.it(\"starts with\");\n\t\t/// if(s.Like(@\"*.xyz\")) print.it(\"ends with\");\n\t\t/// if(s.Like(@\"*mno*\")) print.it(\"contains\");\n\t\t/// if(s.Like(@\"C:\\*.xyz\")) print.it(\"starts and ends with\");\n\t\t/// if(s.Like(@\"?:*\")) print.it(\"any character, : and possibly more text\");\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\t/// <seealso cref=\"wildex\"/>\n#if false //somehow speed depends on dll version. With some versions same as C# code, with some slower. Also depends on string. With shortest strings 50% slower.\n\t\tpublic static bool Like(this string t, string pattern, bool ignoreCase = false)\n\t\t{\n\t\t\tif(t == null) return false;\n\t\t\tfixed (char* pt = t, pw = pattern)\n\t\t\t\treturn Cpp.Cpp_StringLike(pt, t.Length, pw, pattern.Length, ignoreCase);\n\t\t}\n#else\n\t\t[MethodImpl(MethodImplOptions.AggressiveOptimization)]\n\t\tpublic static bool Like(this string t, string pattern, bool ignoreCase = false) {\n\t\t\tNot_.Null(pattern);\n\t\t\tint patLen = pattern.Length;\n\t\t\tif (t == null) return false;\n\t\t\tif (patLen == 0) return t.Length == 0;\n\t\t\tif (patLen == 1 && pattern[0] == '*') return true;\n\t\t\tif (t.Length == 0) return false;\n\t\t\t\n\t\t\tfixed (char* str = t, pat = pattern) {\n\t\t\t\treturn _WildcardCmp(str, pat, t.Length, patLen, ignoreCase ? Tables_.LowerCase : null);\n\t\t\t}\n\t\t\t\n\t\t\t//Microsoft.VisualBasic.CompilerServices.Operators.LikeString() supports more wildcard characters etc. Depends on current culture, has bugs, slower 6-250 times.\n\t\t\t//System.IO.Enumeration.FileSystemName.MatchesSimpleExpression supports \\escaping. Slower 2 - 100 times.\n\t\t}\n\t\t\n\t\t/// <inheritdoc cref=\"Like(string, string, bool)\" path=\"//summary|//param|//exception\"/>\n\t\t[MethodImpl(MethodImplOptions.AggressiveOptimization)]\n\t\tpublic static bool Like(this RStr t, string pattern, bool ignoreCase = false) {\n\t\t\tNot_.Null(pattern);\n\t\t\tint patLen = pattern.Length;\n\t\t\tif (patLen == 0) return t.Length == 0;\n\t\t\tif (patLen == 1 && pattern[0] == '*') return true;\n\t\t\tif (t.Length == 0) return false;\n\t\t\t\n\t\t\tfixed (char* str = t, pat = pattern) {\n\t\t\t\treturn _WildcardCmp(str, pat, t.Length, patLen, ignoreCase ? Tables_.LowerCase : null);\n\t\t\t}\n\t\t\t\n\t\t\t//Microsoft.VisualBasic.CompilerServices.Operators.LikeString() supports more wildcard characters etc. Depends on current culture, has bugs, slower 6-250 times.\n\t\t\t//System.IO.Enumeration.FileSystemName.MatchesSimpleExpression supports \\escaping. Slower 2 - 100 times.\n\t\t}\n\t\t\n\t\t[MethodImpl(MethodImplOptions.AggressiveOptimization)]\n\t\tstatic bool _WildcardCmp(char* s, char* w, int lenS, int lenW, char* table) {\n\t\t\tchar* se = s + lenS, we = w + lenW;\n\t\t\t\n\t\t\t//find '*' from start. Makes faster in some cases.\n\t\t\tfor (; w < we && s < se; w++, s++) {\n\t\t\t\tchar cS = s[0], cW = w[0];\n\t\t\t\tif (cW == '*') goto g1;\n\t\t\t\tif (cW == cS || cW == '?') continue;\n\t\t\t\tif ((table == null) || (table[cW] != table[cS])) return false;\n\t\t\t}\n\t\t\tif (w == we) return s == se; //w ended?\n\t\t\tgoto gr; //s ended\n\t\t\tg1:\n\t\t\t\n\t\t\t//find '*' from end. Makes \"*text\" much faster.\n\t\t\tfor (; we > w && se > s; we--, se--) {\n\t\t\t\tchar cS = se[-1], cW = we[-1];\n\t\t\t\tif (cW == '*') break;\n\t\t\t\tif (cW == cS || cW == '?') continue;\n\t\t\t\tif ((table == null) || (table[cW] != table[cS])) return false;\n\t\t\t}\n\t\t\t\n\t\t\t//Algorithm by Alessandro Felice Cantatore, http://xoomer.virgilio.it/acantato/dev/wildcard/wildmatch.html\n\t\t\t//Changes: supports '\\0' in string; case-sensitive or not; restructured, in many cases faster.\n\t\t\t\n\t\t\tint i = 0;\n\t\t\tgStar: //info: goto used because C# compiler makes the loop faster when it contains less code\n\t\t\tw += i + 1;\n\t\t\tif (w == we) return true;\n\t\t\ts += i;\n\t\t\t\n\t\t\tfor (i = 0; s + i < se; i++) {\n\t\t\t\tchar sW = w[i];\n\t\t\t\tif (sW == '*') goto gStar;\n\t\t\t\tif (sW == s[i] || sW == '?') continue;\n\t\t\t\tif ((table != null) && (table[sW] == table[s[i]])) continue;\n\t\t\t\ts++; i = -1;\n\t\t\t}\n\t\t\t\n\t\t\tw += i;\n\t\t\tgr:\n\t\t\twhile (w < we && *w == '*') w++;\n\t\t\treturn w == we;\n\t\t\t\n\t\t\t//info: Could implement escape sequence ** for * and maybe *? for ?.\n\t\t\t//\tBut it makes code slower etc.\n\t\t\t//\tNot so important.\n\t\t\t//\tMost users would not know about it.\n\t\t\t//\tUsually can use ? for literal * and ?.\n\t\t\t//\tUsually can use regular expression if need such precision.\n\t\t\t//\tThen cannot use \"**options \" for wildcard expressions.\n\t\t\t//\tCould use other escape sequences, eg [*], [?] and [[], but it makes slower and is more harmful than useful.\n\t\t\t\n\t\t\t//The first two loops are fast, but Eq much faster when !ignoreCase. We cannot use such optimizations that it can.\n\t\t\t//The slowest case is \"*substring*\", because then the first two loops don't help.\n\t\t\t//\tThen similar speed as string.IndexOf(ordinal) and API <ms>FindStringOrdinal</ms>.\n\t\t\t//\tPossible optimization, but need to add much code, and makes not much faster, and makes other cases slower, difficult to avoid it.\n\t\t}\n#endif\n\t\t\n\t\t/// <summary>\n\t\t/// Calls <see cref=\"Like(string, string, bool)\"/> for each wildcard pattern specified in the argument list until it returns <c>true</c>.\n\t\t/// Returns 1-based index of the matching pattern, or 0 if none.\n\t\t/// </summary>\n\t\t/// <param name=\"t\"></param>\n\t\t/// <param name=\"ignoreCase\">Case-insensitive.</param>\n\t\t/// <param name=\"patterns\">One or more wildcard strings. The strings cannot be <c>null</c>.</param>\n\t\t/// <exception cref=\"ArgumentNullException\">A string in <i>patterns</i> is <c>null</c>.</exception>\n\t\tpublic static int Like(this string t, bool ignoreCase = false, params ReadOnlySpan<string> patterns) {\n\t\t\tfor (int i = 0; i < patterns.Length; i++) if (t.Like(patterns[i], ignoreCase)) return i + 1;\n\t\t\treturn 0;\n\t\t}\n\t\t\n\t\t#endregion Like\n\t}\n\t\n\t//rejected: struct WildexStruct - struct version of wildex class. Moved to the Unused project.\n\t//\tDoes not make faster, although in most cases creates less garbage.\n\t\n\t/// <summary>\n\t/// The type of text (wildcard expression) of a <see cref=\"wildex\"/> variable.\n\t/// </summary>\n\tpublic enum WXType : byte {\n\t\t/// <summary>\n\t\t/// Simple text (option <c>t</c>, or no <c>*?</c> characters and no <c>t</c>, <c>r</c>, <c>R</c> options).\n\t\t/// </summary>\n\t\tText,\n\t\t\n\t\t/// <summary>\n\t\t/// Wildcard (has <c>*?</c> characters and no <c>t</c>, <c>r</c>, <c>R</c> options).\n\t\t/// <see cref=\"wildex.Match\"/> calls <see cref=\"ExtString.Like(string, string, bool)\"/>.\n\t\t/// </summary>\n\t\tWildcard,\n\t\t\n\t\t/// <summary>\n\t\t/// PCRE regular expression (option <c>r</c>).\n\t\t/// <see cref=\"wildex.Match\"/> calls <see cref=\"regexp.IsMatch\"/>.\n\t\t/// </summary>\n\t\tRegexPcre,\n\t\t\n\t\t/// <summary>\n\t\t/// .NET regular expression (option <c>R</c>).\n\t\t/// <see cref=\"wildex.Match\"/> calls <see cref=\"Regex.IsMatch(string)\"/>.\n\t\t/// </summary>\n\t\tRegexNet,\n\t\t\n\t\t/// <summary>\n\t\t/// Multiple parts (option <c>m</c>).\n\t\t/// <see cref=\"wildex.Match\"/> calls <c>Match</c> for each part (see <see cref=\"wildex.MultiArray\"/>) and returns <c>true</c> if all negative (option <c>n</c>) parts return <c>true</c> (or there are no such parts) and some positive (no option <c>n</c>) part returns <c>true</c> (or there are no such parts).\n\t\t/// If you want to implement a different logic, call <c>Match</c> for each <see cref=\"wildex.MultiArray\"/> element (instead of calling <c>Match</c> for this variable).\n\t\t/// </summary>\n\t\tMulti,\n\t\t\n\t\t/// <summary>\n\t\t/// The regular expression was invalid, and parameter <i>noException</i> <c>true</c>.\n\t\t/// </summary>\n\t\tError,\n\t}\n}\n"
  },
  {
    "path": "Au/System/CpuUsage.cs",
    "content": "namespace Au.More;\n\n/// <summary>\n/// Gets CPU usage.\n/// </summary>\n/// <remarks>\n/// You can use the static functions (easier) or a <c>CpuUsage</c> instance. A <c>CpuUsage</c> instance must be disposed (use <c>using</c>).\n/// </remarks>\n/// <example>\n/// <code><![CDATA[\n/// using var u = new CpuUsage(); //all processes\n/// //using var u = new CpuUsage(process.getProcessId(\"Au.Editor.exe\")); //single process\n/// //using var u = new CpuUsage(process.getProcessIds(\"chrome.exe\")); //all Chrome processes\n/// for (; ; ) {\n/// \tif (!u.Start()) break;\n/// \tThread.Sleep(500);\n/// \tprint.it(u.Stop());\n/// }\n/// ]]></code>\n/// </example>\npublic sealed class CpuUsage : IDisposable {\n\tbool _ofProcess, _started;\n\tHandle_ _ph;\n\tHandle_[] _aph;\n\tlong _mcs, _cycles;\n\t\n\t/// <summary>\n\t/// Use this constructor to get CPU usage of all processes (sum).\n\t/// </summary>\n\tpublic CpuUsage() {\n\t\t\n\t}\n\t\n\t/// <summary>\n\t/// Use this constructor to get CPU usage of a process.\n\t/// </summary>\n\t/// <param name=\"processId\">Process id.</param>\n\tpublic CpuUsage(int processId) {\n\t\t_ofProcess = true;\n\t\t_ph = Handle_.OpenProcess(processId);\n\t}\n\t\n\t/// <summary>\n\t/// Use this constructor to get CPU usage of multiple processes (sum).\n\t/// </summary>\n\t/// <param name=\"processes\">Process ids.</param>\n\tpublic CpuUsage(IEnumerable<int> processes) {\n\t\t_ofProcess = true;\n\t\t_aph = processes.Select(o => Handle_.OpenProcess(o)).Where(o => !o.Is0).ToArray();\n\t\tif (_aph.Length == 0) _aph = null;\n\t}\n\t\n\t///\n\tpublic void Dispose() {\n\t\tif (_ofProcess) {\n\t\t\tif (_aph == null) _ph.Dispose();\n\t\t\telse for (int i = 0; i < _aph.Length; i++) _aph[i].Dispose();\n\t\t}\n\t\tGC.SuppressFinalize(this);\n\t}\n\t\n\t///\n\t~CpuUsage() { Dispose(); }\n\t\n\t/// <summary>\n\t/// Starts measuring CPU usage.\n\t/// </summary>\n\t/// <returns><c>false</c> if <i>processId</i> is invalid.</returns>\n\tpublic bool Start() {\n\t\t_started = _GetCycles(out _cycles);\n\t\t_mcs = perf.mcs;\n\t\treturn _started;\n\t}\n\t\n\t/// <summary>\n\t/// Ends measuring CPU usage, and gets result.\n\t/// Call this after calling <see cref=\"Start\"/> and waiting at least 1 ms. Don't call if <c>Start</c> returned <c>false</c>.\n\t/// </summary>\n\t/// <returns>CPU usage 0 to 100 %.</returns>\n\t/// <exception cref=\"InvalidOperationException\">Called without successful <see cref=\"Start\"/>.</exception>\n\tpublic double Stop() {\n\t\tif (!_started) throw new InvalidOperationException();\n\t\t_started = false;\n\t\t\n\t\tif (!_GetCycles(out var cycles)) return 0;\n\t\tlong mcs = perf.mcs - _mcs;\n\t\t\n\t\tvar r = (cycles - _cycles) / _CyclesMcs() / mcs;\n\t\tDebug_.PrintIf(r > 1.1);\n\t\tr = Math.Min(r, 1);\n\t\tr = _ofProcess ? r : 1 - r;\n\t\treturn Math.Round(r * 100, 2);\n\t}\n\t\n\tbool _GetCycles(out long r) {\n\t\tif (!_ofProcess) { r = _QIPCT(); return true; }\n\t\t\n\t\tif (_aph == null) return Api.QueryProcessCycleTime(_ph, out r); //tested: succeeds and gets cycles even if the process ended, if handle is valid\n\t\t\n\t\tr = 0;\n\t\tfor (int i = _aph.Length; --i >= 0;) {\n\t\t\tApi.QueryProcessCycleTime(_aph[i], out var v);\n\t\t\tr += v;\n\t\t}\n\t\treturn true;\n\t\t\n\t\t[SkipLocalsInit]\n\t\tstatic unsafe long _QIPCT() {\n\t\t\tvar a = stackalloc long[64];\n\t\t\tlong r = 0;\n\t\t\tfor (ushort g = 0, ng = Api.GetActiveProcessorGroupCount(); g < ng; g++) {\n\t\t\t\tint size = 64 * 8;\n\t\t\t\tApi.QueryIdleProcessorCycleTimeEx(g, ref size, a);\n\t\t\t\tfor (int i = 0; i < size / 8; i++) r += a[i];\n\t\t\t}\n\t\t\treturn r;\n\t\t}\n\t}\n\t\n\t//Gets CPU cycles / microsecond * CPU count.\n\tstatic double _CyclesMcs() {\n\t\tif (s_cycles.timeMeasured == 0) { //JIT-compile\n\t\t\t_ = perf.mcs;\n\t\t}\n\t\t\n\t\tif (Environment.TickCount64 != s_cycles.timeMeasured) {\n\t\t\tnint th = process.thisThreadHandle;\n\t\t\tint tp0 = Api.GetThreadPriority(th);\n\t\t\tApi.SetThreadPriority(th, Api.THREAD_PRIORITY_TIME_CRITICAL);\n\t\t\ttry {\n\t\t\t\tint nCpu = ProcessorCount;\n\t\t\t\tdouble r = 0;\n\t\t\t\tfor (int i = 0; i < 3; i++) {\n\t\t\t\t\tApi.QueryThreadCycleTime(th, out long t1);\n\t\t\t\t\tlong mcs = perf.mcs;\n\t\t\t\t\twhile (perf.mcs < mcs + 300) { }\n\t\t\t\t\tApi.QueryThreadCycleTime(th, out long t2);\n\t\t\t\t\tmcs = perf.mcs - mcs;\n\t\t\t\t\tvar v = (double)(t2 - t1) * nCpu / mcs;\n\t\t\t\t\t//if(i>0 && Math.Abs(v-r)>1000) print.it($\"<><c red>{r}, {v}, {v-r}<>\");\n\t\t\t\t\tif (v > r) r = v;\n\t\t\t\t}\n\t\t\t\t//if (Math.Abs(22436 - r) > 1000) print.it($\"<><c red>cycles={r}<>\"); //else print.it($\"cycles={r}\");\n\t\t\t\ts_cycles = (Environment.TickCount64, r);\n\t\t\t}\n\t\t\tfinally { Api.SetThreadPriority(th, tp0); }\n\t\t}\n\t\treturn s_cycles.result;\n\t\t\n\t\t//To avoid incorrect result when this thread interrupted etc, this func measures 3 times and gets max result. Also sets max thread priority.\n\t}\n\tstatic (long timeMeasured, double result) s_cycles;\n\t\n\t/// <summary>\n\t/// Gets CPU usage of all processes (sum).\n\t/// </summary>\n\t/// <param name=\"duration\">How long to measure, milliseconds. Default 10. Min 1. Calls <c>Thread.Sleep(duration);</c>.</param>\n\t/// <returns>CPU usage 0 to 100 %.</returns>\n\tpublic static unsafe double OfAllProcesses(int duration = 10) {\n\t\tArgumentOutOfRangeException.ThrowIfLessThan(duration, 1);\n\t\tusing var u = new CpuUsage();\n\t\tu.Start();\n\t\tThread.Sleep(duration);\n\t\treturn u.Stop();\n\t}\n\t\n\t/// <summary>\n\t/// Gets CPU usage of a process.\n\t/// </summary>\n\t/// <param name=\"processId\">Process id.</param>\n\t/// <param name=\"duration\">How long to measure, milliseconds. Default 10. Min 1. Calls <c>Thread.Sleep(duration);</c>.</param>\n\t/// <returns>CPU usage 0 to 100 %. Returns 0 if <i>processId</i> invalid.</returns>\n\tpublic static double OfProcess(int processId, int duration = 10) {\n\t\tArgumentOutOfRangeException.ThrowIfLessThan(duration, 1);\n\t\tusing var u = new CpuUsage(processId);\n\t\tif (!u.Start()) return 0;\n\t\tThread.Sleep(duration);\n\t\treturn u.Stop();\n\t}\n\t\n\t/// <summary>\n\t/// Gets CPU usage of multiple processes (sum).\n\t/// </summary>\n\t/// <param name=\"processes\">Process ids.</param>\n\t/// <param name=\"duration\">How long to measure, milliseconds. Default 10. Min 1. Calls <c>Thread.Sleep(duration);</c>.</param>\n\t/// <returns>CPU usage 0 to 100 %. Returns 0 if all ids invalid.</returns>\n\tpublic static double OfProcesses(IEnumerable<int> processes, int duration = 10) {\n\t\tArgumentOutOfRangeException.ThrowIfLessThan(duration, 1);\n\t\tusing var u = new CpuUsage(processes);\n\t\tif (!u.Start()) return 0;\n\t\tThread.Sleep(duration);\n\t\treturn u.Stop();\n\t}\n\t\n\t/// <summary>\n\t/// Get the number of logical CPU in the system.\n\t/// </summary>\n\t/// <remarks>\n\t/// Unlike <see cref=\"Environment.ProcessorCount\"/>, does not depend on process affinity etc.\n\t/// </remarks>\n\tpublic static unsafe int ProcessorCount {\n\t\tget {\n\t\t\tint r = 0;\n\t\t\tfor (ushort g = 0, ng = Api.GetActiveProcessorGroupCount(); g < ng; g++) {\n\t\t\t\tint n = 0;\n\t\t\t\tApi.QueryIdleProcessorCycleTimeEx(g, ref n, null);\n\t\t\t\tr += n / 8;\n\t\t\t}\n\t\t\treturn r;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au/System/ProcessMemory.cs",
    "content": "namespace Au.More;\n\n/// <summary>\n/// Allocates, writes and reads memory in other process.\n/// </summary>\n/// <remarks>\n/// Must be disposed. Example: <c>using(var pm=new ProcessMemory(...)) { ... }</c>.\n/// </remarks>\npublic unsafe class ProcessMemory : IDisposable {\n\tHandle_ _hproc;\n\tHandleRef _HprocHR => new(this, _hproc);\n\t\n\t///\n\tprotected virtual void Dispose(bool disposing) {\n\t\tif (_hproc.Is0) return;\n\t\tif (MemAllocated != default) {\n\t\t\tvar mem = MemAllocated; MemAllocated = default;\n\t\t\tif (!Api.VirtualFreeEx(_HprocHR, mem)) print.warning(\"Failed to free process memory. \" + lastError.message);\n\t\t}\n\t\tMem = default;\n\t\t_hproc.Dispose();\n\t}\n\t\n\t///\n\tpublic void Dispose() {\n\t\tDispose(true);\n\t\tGC.SuppressFinalize(this);\n\t}\n\t\n\t///\n\t~ProcessMemory() => Dispose(false);\n\t\n\t/// <summary>\n\t/// Process handle.\n\t/// Opened with access <c>PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE</c>.\n\t/// </summary>\n\tpublic IntPtr ProcessHandle => _hproc;\n\t\n\t/// <summary>\n\t/// Address in that process used by the read and write functions.\n\t/// </summary>\n\t/// <remarks>\n\t/// Most read/write functions of this class don't have a parameter \"address in that process\". Instead they use <c>Mem</c>, which initially is == <see cref=\"MemAllocated\"/>. But you can set <c>Mem</c> = any valid address in that process; usually you do it when no memory is allocated by the constructor (<i>nBytes</i> 0).\n\t/// The address is invalid in this process.\n\t/// </remarks>\n\tpublic IntPtr Mem { get; set; }\n\t\n\t/// <summary>\n\t/// Address of memory allocated in that process.\n\t/// </summary>\n\t/// <remarks>\n\t/// The constructor allocates memory with API <ms>VirtualAllocEx</ms> if <i>nBytes</i> != 0. Finally <c>Dispose</c> will free it with API <ms>VirtualFreeEx</ms>.\n\t/// The setter normally isn't used; if you set <c>MemAllocated = default</c>, <c>Dispose</c> will not free the memory.\n\t/// The address is invalid in this process.\n\t/// </remarks>\n\tpublic IntPtr MemAllocated { get; set; }\n\t\n\tvoid _Alloc(int pid, wnd w, int nBytes, bool noException) {\n\t\tstring err;\n\t\tconst uint fl = Api.PROCESS_VM_OPERATION | Api.PROCESS_VM_READ | Api.PROCESS_VM_WRITE;\n\t\t_hproc = w.Is0 ? Handle_.OpenProcess(pid, fl) : Handle_.OpenProcess(w, fl);\n\t\tif (_hproc.Is0) { err = \"Failed to open process handle.\"; goto ge; }\n\t\t\n\t\tif (nBytes != 0) {\n\t\t\tMem = MemAllocated = Api.VirtualAllocEx(_HprocHR, default, nBytes);\n\t\t\tif (MemAllocated == default) { err = \"Failed to allocate process memory.\"; goto ge; }\n\t\t}\n\t\treturn;\n\t\tge:\n\t\tif (noException) Dispose();\n\t\telse {\n\t\t\tvar e = new AuException(0, err);\n\t\t\tDispose();\n\t\t\tthrow e;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Opens process handle and optionally allocates memory in that process.\n\t/// </summary>\n\t/// <param name=\"processId\">Process id.</param>\n\t/// <param name=\"nBytes\">If not 0, allocates memory of this size in that process.</param>\n\t/// <param name=\"noException\">Don't throw exceptions. If failed, sets <see cref=\"ProcessHandle\"/> = default.</param>\n\t/// <exception cref=\"AuException\">Failed to open process handle (usually because of [](xref:uac)) or allocate memory.</exception>\n\tpublic ProcessMemory(int processId, int nBytes, bool noException = false) {\n\t\t_Alloc(processId, default, nBytes, noException);\n\t}\n\t\n\t/// <summary>\n\t/// Opens process handle and optionally allocates memory in that process.\n\t/// </summary>\n\t/// <param name=\"w\">A window of that process.</param>\n\t/// <param name=\"nBytes\">If not 0, allocates memory of this size in that process.</param>\n\t/// <param name=\"noException\">Don't throw exceptions. If failed, sets <see cref=\"ProcessHandle\"/> = default.</param>\n\t/// <exception cref=\"AuWndException\"><i>w</i> invalid.</exception>\n\t/// <exception cref=\"AuException\">Failed to open process handle or allocate memory.</exception>\n\tpublic ProcessMemory(wnd w, int nBytes, bool noException = false) {\n\t\tif (!noException) w.ThrowIfInvalid();\n\t\t_Alloc(0, w, nBytes, noException);\n\t}\n\t\n\t/// <summary>\n\t/// Copies a string from this process to that process (memory address <see cref=\"Mem\"/>).\n\t/// In that process writes the string as <c>'\\0'</c>-terminated <c>char</c> string (UTF-16).\n\t/// </summary>\n\t/// <returns><c>false</c> if failed.</returns>\n\t/// <param name=\"s\">A string in this process.</param>\n\t/// <param name=\"offsetBytes\">Offset in <see cref=\"Mem\"/>.</param>\n\t/// <remarks>\n\t/// In that process is used <c>(s.Length+1)*2</c> bytes of memory (+1 for the <c>'\\0'</c>, <c>*</c> 2 because UTF-16 character size is 2 bytes).\n\t/// </remarks>\n\tpublic bool WriteCharString(string s, int offsetBytes = 0) {\n\t\tif (Mem == default) return false;\n\t\tif (s.NE()) return true;\n\t\tfixed (char* p = s) {\n\t\t\treturn Api.WriteProcessMemory(_HprocHR, Mem + offsetBytes, p, (s.Length + 1) * 2, null);\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Copies a string from this process to that process (memory address <see cref=\"Mem\"/>).\n\t/// In that process writes the string as <c>'\\0'</c>-terminated <c>byte</c> string.\n\t/// </summary>\n\t/// <returns><c>false</c> if failed.</returns>\n\t/// <param name=\"s\">A string in this process. Normal C# string (UTF-16).</param>\n\t/// <param name=\"offsetBytes\">Offset in <see cref=\"Mem\"/>.</param>\n\t/// <param name=\"enc\">Encoding for converting <c>char</c> string to <c>byte</c> string. If <c>null</c>, uses <see cref=\"Encoding.Default\"/> (UTF-8).</param>\n\tpublic bool WriteByteString(string s, int offsetBytes = 0, Encoding enc = null) {\n\t\tif (Mem == default) return false;\n\t\tif (s.NE()) return true;\n\t\tenc ??= Encoding.Default;\n\t\tvar a = enc.GetBytes(s).InsertAt(-1);\n\t\tfixed (byte* p = a) {\n\t\t\treturn Api.WriteProcessMemory(_HprocHR, Mem + offsetBytes, p, a.Length, null);\n\t\t}\n\t}\n\t\n\t[SkipLocalsInit]\n\tstring _ReadString(bool ansiString, int nChars, int offsetBytes, bool findLength, Encoding enc = null) {\n\t\tif (Mem == default) return null;\n\t\tint n = nChars + 1; if (!ansiString) n *= 2; //bytes with '\\0'\n\t\tusing FastBuffer<byte> b = new(n);\n\t\tif (!Api.ReadProcessMemory(_HprocHR, Mem + offsetBytes, b.p, n, null)) return null;\n\t\tif (findLength) nChars = ansiString ? Ptr_.Length(b.p, nChars) : Ptr_.Length((char*)b.p, nChars);\n\t\tenc ??= Encoding.Default;\n\t\treturn ansiString ? new((sbyte*)b.p, 0, nChars, enc) : new((char*)b.p, 0, nChars);\n\t}\n\t\n\t/// <summary>\n\t/// Copies a <c>char</c> string from that process (memory address <see cref=\"Mem\"/>) to this process.\n\t/// In that process the string must be in Unicode UTF-16 format.\n\t/// </summary>\n\t/// <returns>The copied string, or <c>null</c> if failed.</returns>\n\t/// <param name=\"length\">Number of characters to copy, not including the terminating <c>'\\0'</c>. In both processes a character is 2 bytes.</param>\n\t/// <param name=\"offsetBytes\">Offset in <see cref=\"Mem\"/>.</param>\n\t/// <param name=\"findLength\">Find string length by searching for <c>'\\0'</c> character in <i>length</i> range. If <c>false</c>, the returned string is of <i>length</i> length even if contains <c>'\\0'</c> characters.</param>\n\tpublic string ReadCharString(int length, int offsetBytes = 0, bool findLength = false) {\n\t\treturn _ReadString(false, length, offsetBytes, findLength);\n\t}\n\t\n\t/// <summary>\n\t/// Copies a <c>byte</c> string from that process (memory address <see cref=\"Mem\"/>) to this process.\n\t/// In that process the string must be array of bytes (not Unicode UTF-16).\n\t/// </summary>\n\t/// <returns>The copied string, or <c>null</c> if failed.</returns>\n\t/// <param name=\"length\">Number bytes to copy, not including the terminating <c>'\\0'</c>. In that process a character is 1 or more bytes (depending on encoding). In this process will be 2 bytes (normal C# string).</param>\n\t/// <param name=\"offsetBytes\">Offset in <see cref=\"Mem\"/>.</param>\n\t/// <param name=\"findLength\">Find string length by searching for <c>'\\0'</c> character in <i>length</i> range.</param>\n\t/// <param name=\"enc\">Encoding for converting <c>byte</c> string to <c>char</c> string. If <c>null</c>, uses <see cref=\"Encoding.Default\"/> (UTF-8).</param>\n\tpublic string ReadByteString(int length, int offsetBytes = 0, bool findLength = false, Encoding enc = null) {\n\t\treturn _ReadString(true, length, offsetBytes, findLength, enc);\n\t}\n\t\n\t/// <summary>\n\t/// Copies memory from this process to that process (memory address <see cref=\"Mem\"/>).\n\t/// </summary>\n\t/// <returns><c>false</c> if failed.</returns>\n\t/// <param name=\"ptrFrom\">Address of memory in this process.</param>\n\t/// <param name=\"nBytes\">Number of bytes to copy.</param>\n\t/// <param name=\"offsetBytes\">Offset in <see cref=\"Mem\"/>.</param>\n\tpublic bool Write(void* ptrFrom, int nBytes, int offsetBytes = 0) {\n\t\tif (Mem == default) return false;\n\t\treturn Api.WriteProcessMemory(_HprocHR, Mem + offsetBytes, ptrFrom, nBytes, null);\n\t}\n\t\n\t/// <summary>\n\t/// Copies memory from this process to a known address in that process.\n\t/// </summary>\n\t/// <returns><c>false</c> if failed.</returns>\n\t/// <param name=\"ptrTo\">Address of memory in that process.</param>\n\t/// <param name=\"ptrFrom\">Address of memory in this process.</param>\n\t/// <param name=\"nBytes\">Number of bytes to copy.</param>\n\tpublic bool Write(IntPtr ptrTo, void* ptrFrom, int nBytes) {\n\t\treturn Api.WriteProcessMemory(_HprocHR, ptrTo, ptrFrom, nBytes, null);\n\t}\n\t\n\t/// <summary>\n\t/// Copies memory from that process (memory address <see cref=\"Mem\"/>) to this process.\n\t/// </summary>\n\t/// <returns><c>false</c> if failed.</returns>\n\t/// <param name=\"ptrTo\">Address of memory in this process.</param>\n\t/// <param name=\"nBytes\">Number of bytes to copy.</param>\n\t/// <param name=\"offsetBytes\">Offset in <see cref=\"Mem\"/>.</param>\n\tpublic bool Read(void* ptrTo, int nBytes, int offsetBytes = 0) {\n\t\tif (Mem == default) return false;\n\t\treturn Api.ReadProcessMemory(_HprocHR, Mem + offsetBytes, ptrTo, nBytes, null);\n\t}\n\t\n\t/// <summary>\n\t/// Copies memory from a known address in that process to this process.\n\t/// </summary>\n\t/// <returns><c>false</c> if failed.</returns>\n\t/// <param name=\"ptrFrom\">Address of memory in that process.</param>\n\t/// <param name=\"ptrTo\">Address of memory in this process.</param>\n\t/// <param name=\"nBytes\">Number of bytes to copy.</param>\n\tpublic bool Read(IntPtr ptrFrom, void* ptrTo, int nBytes) {\n\t\treturn Api.ReadProcessMemory(_HprocHR, ptrFrom, ptrTo, nBytes, null);\n\t}\n}\n"
  },
  {
    "path": "Au/System/computer.cs",
    "content": "//FUTURE: GetCpuUsage.\n\nusing Microsoft.Win32;\n\nnamespace Au {\n\t/// <summary>\n\t/// Computer shutdown etc.\n\t/// </summary>\n\tpublic static unsafe class computer {\n\t\t/// <summary>\n\t\t/// Gets the number of milliseconds elapsed since Windows startup, not including the time when the computer sleeps or hibernates.\n\t\t/// To get time with sleep, use <see cref=\"Environment.TickCount64\"/>.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Uses API <ms>QueryUnbiasedInterruptTime</ms>.\n\t\t/// Uses the low-resolution system timer. Its period usually is 15.25 ms.\n\t\t/// Independent of computer clock time changes.\n\t\t/// </remarks>\n\t\tpublic static long tickCountWithoutSleep {\n\t\t\tget {\n\t\t\t\tif (!Api.QueryUnbiasedInterruptTime(out long t)) return Api.GetTickCount64();\n\t\t\t\treturn t / 10000;\n\t\t\t}\n\t\t}\n\n\t\t//public static void setTime(DateTime time) {\n\n\t\t//}\n\n\t\t/// <summary>\n\t\t/// Initiates computer shutdown or restart operation.\n\t\t/// </summary>\n\t\t/// <returns><c>false</c> if failed. Supports <see cref=\"lastError\"/>.</returns>\n\t\t/// <param name=\"restart\">Reboot.</param>\n\t\t/// <param name=\"force\">Don't allow to cancel. Applications with unsaved changes will be forcibly closed.</param>\n\t\t/// <param name=\"timeoutS\">The length of time to display the shutdown dialog box, in seconds.</param>\n\t\t/// <param name=\"message\">Display this text in the shutdown dialog box and write to the event log.</param>\n\t\t/// <param name=\"computer\">The network name of the computer to be shut down. If <c>null</c> (default), shuts down this computer. If used, this process must be admin.</param>\n\t\t/// <remarks>\n\t\t/// Calls API <ms>InitiateSystemShutdown</ms>.\n\t\t/// </remarks>\n\t\tpublic static bool shutdown(bool restart = false, bool force = false, int timeoutS = 0, string message = null, string computer = null) {\n\t\t\tSecurityUtil.SetPrivilege(\"SeShutdownPrivilege\", true);\n\t\t\tif (!computer.NE()) SecurityUtil.SetPrivilege(\"SeRemoteShutdownPrivilege\", true, computer);\n\t\t\treturn Api.InitiateSystemShutdown(computer, message, timeoutS, force, restart);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Initiates computer shutdown or restart operation.\n\t\t/// </summary>\n\t\t/// <returns><c>false</c> if failed. Supports <see cref=\"lastError\"/>.</returns>\n\t\t/// <param name=\"flags\"><ms>ExitWindowsEx</ms> parameter <i>uFlags</i>.</param>\n\t\t/// <param name=\"reason\"><ms>ExitWindowsEx</ms> parameter <i>dwReason</i>.</param>\n\t\t/// <remarks>\n\t\t/// Calls API <ms>ExitWindowsEx</ms>.\n\t\t/// </remarks>\n\t\tpublic static bool shutdown(int flags, uint reason = 0) {\n\t\t\tSecurityUtil.SetPrivilege(\"SeShutdownPrivilege\", true);\n\t\t\treturn Api.ExitWindowsEx(flags, reason);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Initiates computer logoff (sign out) operation.\n\t\t/// </summary>\n\t\t/// <returns><c>false</c> if failed. Supports <see cref=\"lastError\"/>.</returns>\n\t\t/// <param name=\"force\">Don't allow to cancel. Applications with unsaved changes will be forcibly closed.</param>\n\t\tpublic static bool logoff(bool force = false) {\n\t\t\tSecurityUtil.SetPrivilege(\"SeShutdownPrivilege\", true);\n\t\t\treturn Api.ExitWindowsEx(force ? 4 : 16, 0);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Computer sleep, hibernate or monitor off.\n\t\t/// </summary>\n\t\t/// <returns><c>false</c> if failed. Supports <see cref=\"lastError\"/>.</returns>\n\t\t/// <param name=\"how\"></param>\n\t\t/// <remarks>\n\t\t/// To sleep or hibernate uses API <ms>SetSuspendState</ms>. To turn off display uses <ms>WM_SYSCOMMAND</ms>.\n\t\t/// \n\t\t/// The <ms>SetSuspendState</ms> behavior is undefined if the system does not support S1-S3 sleep or S4 hibernate power states. It may fail or use hibernation instead of sleep. About power states: <ms>System Power States</ms>. Available sleep states: <c>run.console(\"powercfg.exe\", \"/A\");</c>\n\t\t/// </remarks>\n\t\tpublic static bool suspend(CSuspend how) {\n\t\t\tif (how == CSuspend.SleepOrDisplay) how = 0 != Api.IsPwrSuspendAllowed() ? CSuspend.Sleep : CSuspend.Display;\n\t\t\tif (how == CSuspend.Display) {\n\t\t\t\tvar w = WndUtil.CreateWindowDWP_(messageOnly: true);\n\t\t\t\tApi.DefWindowProc(w, Api.WM_SYSCOMMAND, Api.SC_MONITORPOWER, 2);\n\t\t\t\tApi.DestroyWindow(w);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tSecurityUtil.SetPrivilege(\"SeShutdownPrivilege\", true);\n\t\t\treturn 0 != Api.SetSuspendState((byte)(how == CSuspend.Hibernate ? 1 : 0), 0, 0);\n\t\t\t//documented: parameter bForce has no effect.\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Initiates computer lock operation.\n\t\t/// </summary>\n\t\t/// <returns><c>false</c> if failed. Supports <see cref=\"lastError\"/>.</returns>\n\t\t/// <remarks>\n\t\t/// Uses API <ms>LockWorkStation</ms>.\n\t\t/// </remarks>\n\t\tpublic static bool lockOrSwitchUser() {\n\t\t\t//if (switchUser) return Api.WTSDisconnectSession(default, -1, false); //on Win10 the same as lock\n\t\t\treturn Api.LockWorkStation();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if the computer is using battery power.\n\t\t/// </summary>\n\t\t/// <seealso cref=\"SystemInformation.PowerStatus\"/>\n\t\tpublic static bool isOnBattery => SystemInformation.PowerStatus.PowerLineStatus == System.Windows.Forms.PowerLineStatus.Offline; //first time 4 ms\n\n\t\t//public static bool isOnBattery => System.Windows.SystemParameters.PowerLineStatus == System.Windows.PowerLineStatus.Offline; //first time 21 ms\n\n\t\t//FUTURE: events desktopSwitchEvent, sleepEvent. Like SystemEvents.\n\t\t//public static event Action desktopSwitchEvent {\n\t\t//\tadd {\n\n\t\t//\t}\n\t\t//\tremove {\n\n\t\t//\t}\n\t\t//}\n\n\t\t//public static void waitForDesktop(Seconds timeout, bool normalDesktop) { //normal, UAC, lock, screensaver, etc\n\t\t//\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   //using var hook = new WinEventHook(EEvent.SYSTEM_DESKTOPSWITCH);\n\t\t//}\n\n\t\t#region suspendResumeEvent\n\n\t\tstatic Action<PowerModes> _srAction;\n\t\tstatic object _srLock = new();\n\t\tstatic IntPtr _srHandle;\n\n\t\t/// <summary>\n\t\t/// When the computer is about to enter a suspended state (sleep or hibernate) or has resumed operation after being suspended.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Many system events are available in <see cref=\"SystemEvents\"/> class. For suspend/resume notifications could be used <see cref=\"SystemEvents.PowerModeChanged\"/>, but it does not work on most computers. Use this event instead.\n\t\t///\n\t\t/// The event handler is executed in other thread. The parameter can be only <c>Resume</c> or <c>Suspend</c>. See API <ms>PBT_APMSUSPEND</ms> and <ms>PBT_APMRESUMESUSPEND</ms>.\n\t\t/// </remarks>\n\t\tpublic static event Action<PowerModes> suspendResumeEvent {\n\t\t\tadd {\n\t\t\t\tlock (_srLock) {\n\t\t\t\t\tif (osVersion.minWin8) {\n\t\t\t\t\t\tif (_srHandle == default) {\n\t\t\t\t\t\t\tApi.DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS p = new() { Callback = &_SRCallback };\n\t\t\t\t\t\t\t_srHandle = Api.RegisterSuspendResumeNotification((IntPtr)(&p), 2);\n\t\t\t\t\t\t\tif (_srHandle == default) throw new AuException(0);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tSystemEvents.PowerModeChanged += _SystemEvents_PowerModeChanged;\n\t\t\t\t\t}\n\t\t\t\t\t_srAction += value;\n\t\t\t\t}\n\t\t\t}\n\t\t\tremove {\n\t\t\t\tlock (_srLock) {\n\t\t\t\t\t_srAction -= value;\n\t\t\t\t\tif (osVersion.minWin8) {\n\t\t\t\t\t\tif (_srAction == null && _srHandle != default) {\n\t\t\t\t\t\t\tApi.UnregisterSuspendResumeNotification(_srHandle);\n\t\t\t\t\t\t\t_srHandle = default;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tSystemEvents.PowerModeChanged -= _SystemEvents_PowerModeChanged;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t}\n\t\t}\n\n\t\t[UnmanagedCallersOnly]\n\t\tstatic int _SRCallback(void* context, int type, void* setting) {\n\t\t\tif (type is 4 or 7) _srAction?.Invoke(type == 4 ? PowerModes.Suspend : PowerModes.Resume);\n\t\t\treturn 0;\n\t\t}\n\n\t\tstatic void _SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e) {\n\t\t\tif (e.Mode is PowerModes.Suspend or PowerModes.Resume) _srAction?.Invoke(e.Mode);\n\t\t}\n\n\t\t#endregion\n\t}\n}\n\nnamespace Au.Types {\n\t/// <summary>\n\t/// Used with <see cref=\"computer.suspend\"/>.\n\t/// </summary>\n\tpublic enum CSuspend {\n\t\t/// <summary>Sleep (power state S1-S3). If these power states unavailable, the function may hibernate instead.</summary>\n\t\tSleep,\n\n\t\t/// <summary>Hibernate.</summary>\n\t\tHibernate,\n\n\t\t/// <summary>Turn off display. It should activate Modern Suspend S0 if available.</summary>\n\t\tDisplay,\n\n\t\t/// <summary>Sleep if power states S1-S3 available, else turn off display (it should activate Modern Suspend S0 if available).</summary>\n\t\tSleepOrDisplay,\n\t}\n}"
  },
  {
    "path": "Au/System/consoleProcess.cs",
    "content": "namespace Au;\n\n/// <summary>\n/// Runs a console program in hidden mode. Gets its output text and can write input text.\n/// </summary>\n/// <remarks>\n/// Must be disposed. In the example the <c>using</c> statement does it.\n/// </remarks>\n/// <seealso cref=\"run.console(Action{string}, string, string, string, Encoding, bool)\"/>\n/// <example>\n/// <code><![CDATA[\n/// using var c = new consoleProcess(folders.Workspace + @\"exe\\console1\\console1.exe\");\n/// //c.Encoding = Console.OutputEncoding;\n/// while (c.Read(out var s)) {\n/// \tif (c.IsLine) {\n/// \t\tprint.it($\"<><c green><_>{s}</_><>\");\n/// \t} else {\n/// \t\tif (s == \"User: \") c.Write(\"A\");\n/// \t\telse if (s == \"Password: \") c.Write(\"B\");\n/// \t\t//else if (c.Wait()) continue; //let next Read wait for more text and get old + new text. Use this if other prompts are not possible.\n/// \t\telse if (c.Wait(500)) continue; //wait for more text max 500 ms. If received, let next Read get old + new text.\n/// \t\telse if (dialog.showInput(out var s1, null, s)) c.Write(s1);\n/// \t\t//else print.it($\"<><c blue><_>{s}</_><><nonl>\");\n/// \t\telse throw new OperationCanceledException();\n/// \t}\n/// }\n/// if (c.ExitCode is int ec && ec != 0) throw new Exception($\"Failed. Exit code: {ec}\");\n/// ]]></code>\n/// <code><![CDATA[\n/// using var c = new consoleProcess(\"example.exe\");\n/// c.Prompt(\"User: \", \"A\");\n/// c.Prompt(\"Password: \", \"B\");\n/// while (c.Read(out var s)) print.it(s);\n/// print.it(c.ExitCode);\n/// ]]></code>\n/// </example>\npublic sealed unsafe class consoleProcess : IDisposable {\n\tHandle_ _hProcess, _hOutRead, _hInWrite;\n\tDecoder _decoder;\n\tbyte[] _b;\n\tchar[] _c;\n\tint _n, _i;\n\tbool _skipN;\n\tStringBuilder _sb;\n\t\n\t/// <summary>\n\t/// Starts specified console program.\n\t/// </summary>\n\t/// <inheritdoc cref=\"run.console(string, string, string, Encoding)\" path=\"/param\"/>\n\t/// <exception cref=\"AuException\">Failed, for example file not found.</exception>\n\tpublic consoleProcess(string exe, string args = null, string curDir = null) {\n\t\t//rejected: bool separateError (separete stderr from stdout). Why:\n\t\t//\tImpossible to make it sync with stdout.\n\t\t//\tComplicated library code.\n\t\t//\tComlicated user code.\n\t\t//\tRarely used. And because of the above reasons would be very very rarely used.\n\t\t\n\t\texe = run.NormalizeFile_(true, exe, out _, out _);\n\t\tvar ps = new ProcessStarter_(exe, args, curDir, rawExe: true);\n\t\t\n\t\tHandle_ hInRead = default, hOutWrite = default/*, hErrWrite = default*/;\n\t\tApi.PROCESS_INFORMATION pi = default;\n\t\ttry {\n\t\t\tvar sa = new Api.SECURITY_ATTRIBUTES(null) { bInheritHandle = 1 };\n\t\t\tif (!Api.CreatePipe(out _hOutRead, out hOutWrite, sa, 0)) throw new AuException(0);\n\t\t\t//if (!Api.CreatePipe(out _hErrRead, out hErrWrite, sa, 0)) throw new AuException(0);\n\t\t\tif (!Api.CreatePipe(out hInRead, out _hInWrite, sa, 0)) throw new AuException(0);\n\t\t\t\n\t\t\tApi.SetHandleInformation(_hInWrite, 1, 0); //remove HANDLE_FLAG_INHERIT\n\t\t\tApi.SetHandleInformation(_hOutRead, 1, 0);\n\t\t\t//Api.SetHandleInformation(_hErrRead, 1, 0);\n\t\t\t\n\t\t\tps.si.dwFlags |= Api.STARTF_USESTDHANDLES | Api.STARTF_USESHOWWINDOW;\n\t\t\tps.si.hStdInput = hInRead;\n\t\t\tps.si.hStdOutput = hOutWrite;\n\t\t\t//ps.si.hStdError = hErrWrite;\n\t\t\tps.si.hStdError = hOutWrite;\n\t\t\tps.flags |= Api.CREATE_NEW_CONSOLE;\n\t\t\t\n\t\t\tif (!ps.StartL(out pi, inheritHandles: true)) {\n\t\t\t\tDispose();\n\t\t\t\tthrow new AuException(0);\n\t\t\t}\n\t\t\t\n\t\t\t_hProcess = pi.hProcess;\n\t\t\tTerminateFinally = true;\n\t\t\tprocess.thisProcessExit += _OnExit;\n\t\t}\n\t\tfinally {\n\t\t\thInRead.Dispose();\n\t\t\thOutWrite.Dispose();\n\t\t\t//hErrWrite.Dispose();\n\t\t\tpi.hThread.Dispose();\n\t\t\tEnded = _hProcess.Is0;\n\t\t}\n\t}\n\t\n\t///\n\tpublic void Dispose() {\n\t\t//print.it(Api.WaitForSingleObject(_hProcess, 0)); //info: all tested processes ended after 0-5 ms after ERROR_BROKEN_PIPE\n\t\tif (TerminateFinally && !Ended && Api.WaitForSingleObject(_hProcess, 100) != Api.WAIT_TIMEOUT) TerminateFinally = false;\n\t\t_Dispose();\n\t\tGC.SuppressFinalize(this);\n\t}\n\t\n\tvoid _Dispose() {\n\t\tif (_hOutRead.Is0) return;\n\t\tprocess.thisProcessExit -= _OnExit;\n\t\tif (TerminateFinally && !Ended) TerminateNow();\n\t\t_hProcess.Dispose();\n\t\t_hInWrite.Dispose();\n\t\t_hOutRead.Dispose();\n\t\t//_hErrRead.Dispose();\n\t}\n\t\n\t///\n\t~consoleProcess() {\n\t\tprint.warning(\"consoleProcess not disposed\", -1);\n\t\t_Dispose();\n\t}\n\t\n\tvoid _OnExit(Exception e) {\n\t\tif (TerminateFinally && !Ended) TerminateNow();\n\t}\n\n\t/// <summary>\n\t/// Closes the standard input stream (stdin), signaling end of input to the process.\n\t/// </summary>\n\t/// <remarks>\n\t/// Some console programs expect you to close stdin after writing all input data (see <see cref=\"Write\"/>). This signals \"end of file\" (EOF).\n\t/// </remarks>\n\tpublic void EndInput() {\n\t\t_hInWrite.Dispose();\n\t}\n\t\n\t/// <summary>\n\t/// Console's text encoding.\n\t/// Default is <see cref=\"Encoding.UTF8\"/>.\n\t/// </summary>\n\t/// <remarks>\n\t/// If wrong encoding, the received text may contain garbage. Try <see cref=\"Console.OutputEncoding\"/> or <see cref=\"Encoding.Unicode\"/>.\n\t/// </remarks>\n\tpublic Encoding Encoding {\n\t\tget => _encoding ??= Encoding.UTF8;\n\t\tset {\n\t\t\tif (value != _encoding) {\n\t\t\t\t_encoding = value;\n\t\t\t\t_decoder = null;\n\t\t\t}\n\t\t}\n\t}\n\tEncoding _encoding;\n\t\n\t/// <summary>\n\t/// Input text encoding for <see cref=\"Write\"/>. If <c>null</c> (default), will use <see cref=\"Encoding\"/>.\n\t/// </summary>\n\tpublic Encoding InputEncoding { get; set; }\n\t\n\t/// <summary>\n\t/// Waits and reads next full line.\n\t/// </summary>\n\t/// <param name=\"s\">Receives the text. It does not contain newline characters (<c>'\\r'</c>, <c>'\\n'</c>).</param>\n\t/// <returns><c>false</c> if there is no more text to read because the console process ended or is ending.</returns>\n\t/// <exception cref=\"AuException\">Failed.</exception>\n\t/// <remarks>\n\t/// Waits for new text from console, reads it into an internal buffer, and then returns it one full line at a time.\n\t/// \n\t/// To read a prompt (incomplete line that asks for user input), use <see cref=\"Read\"/> instead. This function would just hang waiting for full line ending with newline characters.\n\t/// </remarks>\n\tpublic bool ReadLine(out string s) => _Read(out s, true);\n\t\n\t/// <summary>\n\t/// Waits and reads next full or partial line.\n\t/// </summary>\n\t/// <param name=\"s\">Receives the text. It does not contain newline characters (<c>'\\r'</c>, <c>'\\n'</c>).</param>\n\t/// <returns><c>false</c> if there is no more text to read because the console process ended or is ending.</returns>\n\t/// <exception cref=\"AuException\">Failed.</exception>\n\t/// <remarks>\n\t/// Waits for new text from console, reads it into an internal buffer, and then returns it one line at a time.\n\t/// \n\t/// Sets <see cref=\"IsLine\"/> = <c>true</c> if the line text in the buffer is terminated with newline characters. Else sets <c>IsLine</c> = <c>false</c>.\n\t///\n\t/// When <c>IsLine</c> is <c>false</c>, <i>s</i> text can be either a prompt (incomplete line that asks for user input) or an incomplete line/prompt text (the remainder will be retrieved later). Your script should somehow distinguish it. If it's a known prompt, let it call <see cref=\"Write\"/>. Else call <see cref=\"Wait\"/>. See example.\n\t///\n\t/// It's impossible to automatically distinguish a prompt from a partial line or partial prompt. The console program can write line text in parts, possibly with delays in between. Or it can write a prompt text and wait for user input. Also this function may receive line text in parts because of limited buffer size etc.\n\t/// </remarks>\n\t/// <inheritdoc cref=\"consoleProcess\" path=\"/example\"/>\n\tpublic bool Read(out string s) => _Read(out s, false);\n\t\n\t/// <summary>\n\t/// Returns:\n\t/// <br/>• 1 - there is data available to read.\n\t/// <br/>• 0 - no data.\n\t/// <br/>• -1 - error, eg process ended. Supports <see cref=\"lastError\"/>.\n\t/// </summary>\n\tinternal int CanReadNow_ => _i < _n ? 1 : !Api.PeekNamedPipe(_hOutRead, null, 0, out _, out int n) ? -1 : n > 0 ? 1 : 0;\n\t\n\tinternal IntPtr OutputHandle_ => _hOutRead;\n\t\n\tbool _ReadPipe() {\n\t\t//native console API allows any buffer size when writing, but wrappers usually use small buffer, eg .NET 4-5 KB, msvcrt 5 KB, C++ not tested\n\t\t_b ??= new byte[8000];\n\t\t_c ??= new char[_b.Length + 10];\n\t\t_sb ??= new();\n\t\t_n = _i = 0;\n\t\t\n\t\tfor (; ; ) {\n\t\t\tIntPtr hRead = _hOutRead;\n\t\t\t//IsError = hRead == _hErrRead;\n\t\t\t\n\t\t\tif (!Api.ReadFileArr(hRead, _b, out int nr)) return false;\n\t\t\tif (nr > 0) {\n\t\t\t\tvar b = _b.AsSpan(0, nr);\n\t\t\t\t\n\t\t\t\t//BOM? Noticed with: robocopy /unicode (UTF-16 BOM followed by ASCII).\n\t\t\t\tif (b is [0xEF, 0xBB, 0xBF, ..]) b = b[3..]; else if (b is [0xFF, 0xFE, ..]) b = b[2..];\n\t\t\t\t\n\t\t\t\tif (_skipN && _b[0] == 10) {\n\t\t\t\t\t_skipN = false;\n\t\t\t\t\tb = b[1..];\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t_decoder ??= Encoding.GetDecoder(); //ensures we'll not get partial multibyte chars (UTF8 etc) at buffer end/start\n\t\t\t\t_n = _decoder.GetChars(b, _c, false);\n\t\t\t\tif (_n > 0) return true;\n\t\t\t}\n\t\t}\n\t}\n\t\n\tbool _Read(out string text, bool needLine, Func<string, bool> prompt = null) {\n\t\ttext = null;\n\t\tif (Ended) return _R(false);\n\t\t\n\t\tif (_wasReadMore) _wasReadMore = false; else _sb?.Clear();\n\t\t\n\t\treadPipe:\n\t\tif (_i >= _n) {\n\t\t\tif (!_ReadPipe()) return _R(_Ended(ref text));\n\t\t}\n\t\t\n\t\tvar k = new Span<char>(_c, _i, _n - _i);\n\t\t\n\t\tint i = k.IndexOfAny('\\n', '\\r');\n\t\tif (i < 0) {\n\t\t\t_sb.Append(k);\n\t\t\t_n = 0;\n\t\t\tif (!needLine) {\n\t\t\t\tswitch (_PeekWait(30, 1)) {\n\t\t\t\tcase _PWResult.OK: goto readPipe;\n\t\t\t\tcase _PWResult.End: return _R(_Ended(ref text));\n\t\t\t\t}\n\t\t\t\tvar s1 = _sb.ToString();\n\t\t\t\tif (prompt != null) {\n\t\t\t\t\tif (prompt(s1)) {\n\t\t\t\t\t\t_sb.Clear();\n\t\t\t\t\t\treturn _R(_ReturnText(ref text, s1, false));\n\t\t\t\t\t}\n\t\t\t\t\tif (_PeekWait(5000, 1) == _PWResult.OK) goto readPipe;\n\t\t\t\t\ttext = s1; //for exception message\n\t\t\t\t\treturn false;\n\t\t\t\t} else {\n\t\t\t\t\treturn _R(_ReturnText(ref text, s1, false));\n\t\t\t\t}\n\t\t\t}\n\t\t\tgoto readPipe;\n\t\t}\n\t\t\n\t\tstring s = _sb.Length > 0 ? _sb.Append(k[0..i]).ToString() : k[0..i].ToString();\n\t\tif (k[i++] == '\\r') {\n\t\t\tif (i == k.Length) _skipN = true;\n\t\t\telse if (k[i] == '\\n') i++;\n\t\t}\n\t\t_i += i;\n\t\t\n\t\treturn _R(_ReturnText(ref text, s, true));\n\t\t\n\t\tbool _ReturnText(ref string text, string s, bool isLine) {\n\t\t\ttext = s;\n\t\t\tIsLine = isLine;\n\t\t\tif (isLine) _sb.Clear();\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\tbool _Ended(ref string text) {\n\t\t\tif (lastError.code != Api.ERROR_BROKEN_PIPE) throw new AuException(0);\n\t\t\tEnded = true;\n\t\t\tif (_sb.Length == 0) return false;\n\t\t\treturn _ReturnText(ref text, _sb.ToString(), true);\n\t\t}\n\t\t\n\t\tbool _R(bool r, [CallerLineNumber] int l_ = 0) {\n\t\t\t//print.it(\"return\", l_);\n\t\t\treturn r;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Waits for more text and tells next <see cref=\"Read\"/> to get old + new text.\n\t/// </summary>\n\t/// <param name=\"timeout\">Timeout, ms. The function returns <c>false</c> if did not receive more text during that time. If -1, returns <c>true</c> without waiting (next <see cref=\"Read\"/> will wait).</param>\n\t/// <returns><c>true</c> if received more text or if <i>timeout</i> is -1.</returns>\n\t/// <exception cref=\"InvalidOperationException\"><see cref=\"IsLine\"/> <c>true</c>. Or multiple <see cref=\"Wait\"/> without <see cref=\"Read\"/>.</exception>\n\t/// <remarks>\n\t/// If returns <c>true</c>, next <see cref=\"Read\"/> will get the old text + new text. If the console process ends while waiting, next <c>Read</c> will get the old text, and <see cref=\"IsLine\"/> will be <c>true</c>.\n\t/// </remarks>\n\tpublic bool Wait(int timeout = -1) {\n\t\tif (IsLine) throw new InvalidOperationException(\"IsLine true\");\n\t\tif (_wasReadMore) throw new InvalidOperationException(\"multiple Wait without Read\");\n\t\tif (timeout < 0) {\n\t\t\tif (timeout != -1) throw new ArgumentException();\n\t\t} else {\n\t\t\tif (_PeekWait(timeout, 15) == _PWResult.Timeout) return false; //let next _Read clear _sb\n\t\t}\n\t\treturn _wasReadMore = true; //let next _Read don't clear _sb but set _wasReadMore = false\n\t}\n\tbool _wasReadMore;\n\t\n\t/// <summary>\n\t/// Waits for next prompt (incomplete line that asks for user input). Reads the prompt and all lines before it. Then can write input text and <c>\"\\n\"</c>.\n\t/// </summary>\n\t/// <param name=\"prompt\">Prompt text. Format: [wildcard expression](xref:wildcard_expression).</param>\n\t/// <param name=\"input\">Input text. If <c>\"\"</c>, writes just <c>\"\\n\"</c>. If <c>null</c>, does not write.</param>\n\t/// <returns>List of lines before the prompt. The last item is the prompt.</returns>\n\t/// <exception cref=\"AuException\">Next prompt text does not match <i>prompt</i> (after waiting 5 s for full prompt). Or the console process ended. Or failed to write <i>input</i>.</exception>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// using var c = new consoleProcess(\"example.exe\");\n\t/// c.Prompt(\"User: \", \"A\");\n\t/// c.Prompt(\"Password: \", \"B\");\n\t/// while (c.Read(out var s)) print.it(s);\n\t/// ]]></code>\n\t/// <code><![CDATA[\n\t/// var a = c.Prompt(\"User:\");\n\t/// print.it(a);\n\t/// c.Write(a.Any(o => o.Contains(\"keyword\")) ? \"A\" : \"B\");\n\t/// ]]></code>\n\t/// <code><![CDATA[\n\t/// using var c = new consoleProcess(\"cmd.exe\");\n\t/// var prompt = @\"C:\\*>\";\n\t/// c.Prompt(prompt, \"example.exa\");\n\t/// foreach (var s in c.Prompt(prompt).SkipLast(1)) print.it(s);\n\t/// c.Write(\"exit\");\n\t/// ]]></code>\n\t/// </example>\n\tpublic List<string> Prompt(string prompt, string input = null) {\n\t\twildex wild = prompt;\n\t\tbool ok = false;\n\t\tFunc<string, bool> f = s => ok = wild.Match(s);\n\t\tList<string> a = new();\n\t\twhile (!ok) {\n\t\t\tif (!_Read(out var s, false, f))\n\t\t\t\tthrow new AuException(Ended ? $\"The console process ended while waiting for prompt \\\"{prompt}\\\".\" : $\"The prompt text does not match \\\"{prompt}\\\". It is \\\"{s}\\\".\");\n\t\t\ta.Add(s);\n\t\t}\n\t\tif (input != null) Write(input);\n\t\treturn a;\n\t}\n\t\n\t/// <summary>\n\t/// Sends text to the console's input. Also sends character <c>'\\n'</c> (like key <c>Enter</c>), unless <i>text</i> ends with <c>'\\n'</c> or <i>noNL</i> is <c>true</c>.\n\t/// </summary>\n\t/// <param name=\"text\"></param>\n\t/// <param name=\"noNL\">Don't append character <c>'\\n'</c> when <i>text</i> does not end with <c>'\\n'</c>.</param>\n\t/// <exception cref=\"AuException\">Failed.</exception>\n\tpublic void Write(string text, bool noNL = false) {\n\t\tif (!text.NE()) _Write(text);\n\t\tif (!noNL && text is not [.., '\\n']) _Write(\"\\n\");\n\t\t\n\t\tvoid _Write(string s) {\n\t\t\tbool ok = Api.WriteFile2(_hInWrite, (InputEncoding ?? _encoding ?? Encoding.UTF8).GetBytes(s), out _);\n\t\t\tif (!ok) throw new AuException(0);\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// <see cref=\"Read\"/> sets this property = <c>true</c> if in console output the line text ended with newline characters; <c>false</c> if not.\n\t/// </summary>\n\t/// <remarks>\n\t/// If returns <c>false</c>, the text returned by the last <see cref=\"Read\"/> is either a prompt (incomplete line that asks for user input) or an incomplete line. You can use <see cref=\"Wait\"/> to wait for more text.\n\t/// </remarks>\n\tpublic bool IsLine { get; private set; }\n\t\n\t///// <summary>\n\t///// Returns <c>true</c> if the last <c>ReadX</c> function retrieved text from the standard error stream; <c>false</c> if from the standard output stream.\n\t///// </summary>\n\t//public bool IsError { get; private set; }\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if a <c>ReadX</c> function detected that the console output stream is closed. The process is ended or ending.\n\t/// </summary>\n\tpublic bool Ended { get; private set; }\n\t\n\t/// <summary>\n\t/// Gets the exit code of the console process.\n\t/// If the process is still running, waits until it exits.\n\t/// </summary>\n\t/// <value>If fails, returns <c>int.MinValue</c>.</value>\n\tpublic int ExitCode {\n\t\tget {\n\t\t\tbool retry = false; g1:\n\t\t\tif (!Api.GetExitCodeProcess(_hProcess, out int r)) return int.MinValue;\n\t\t\tif (r == 259 && !retry && (retry = 0 == Api.WaitForSingleObject(_hProcess, -1))) goto g1; //STILL_ACTIVE\n\t\t\treturn r;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Terminates the console process.\n\t/// </summary>\n\t/// <param name=\"exitCode\"></param>\n\tpublic void TerminateNow(int exitCode = -1) {\n\t\tApi.TerminateProcess(_hProcess, exitCode);\n\t\tTerminateFinally = false;\n\t}\n\t\n\t/// <summary>\n\t/// If the console process is still running when this variable is dying, terminate it.\n\t/// Default <c>true</c>.\n\t/// </summary>\n\tpublic bool TerminateFinally { get; set; }\n\t\n\t_PWResult _PeekWait(int time, int minPeriod, int maxPeriod = 100) {\n\t\tfor (int period = minPeriod; time > 0;) {\n\t\t\tif (period > 0) {\n\t\t\t\tperiod.ms();\n\t\t\t\ttime -= period;\n\t\t\t}\n\t\t\tif (period < maxPeriod) period++;\n\t\t\tif (!Api.PeekNamedPipe(_hOutRead, null, 0, out _, out int nr)) return _PWResult.End;\n\t\t\tif (nr > 0) return _PWResult.OK;\n\t\t}\n\t\treturn _PWResult.Timeout;\n\t}\n\t\n\tenum _PWResult { OK, Timeout, End }\n\t\n\t/// <summary>\n\t/// Reads all console output text until its process ends. Returns that text.\n\t/// </summary>\n\t/// <remarks>\n\t/// Does not parse lines. Does not normalize newline characters.\n\t/// </remarks>\n\t/// <exception cref=\"AuException\">Failed.</exception>\n\tpublic string ReadAllText() {\n\t\twhile (_ReadPipe()) _sb.Append(_c, 0, _n);\n\t\tif (lastError.code != Api.ERROR_BROKEN_PIPE) throw new AuException(0);\n\t\tvar s = _sb.ToString();\n\t\t_sb.Clear();\n\t\treturn s;\n\t}\n\t\n\t/// <summary>\n\t/// Reads all console output text until its process ends. Calls callback function.\n\t/// </summary>\n\t/// <param name=\"output\">Called for each text chunk received from console.</param>\n\t/// <remarks>\n\t/// Does not parse lines. Does not normalize newline characters.\n\t/// </remarks>\n\t/// <exception cref=\"AuException\">Failed.</exception>\n\tpublic void ReadAllText(Action<string> output) {\n\t\tif (_sb?.Length > 0) {\n\t\t\tvar s = _sb.ToString();\n\t\t\t_sb.Clear();\n\t\t\toutput(s);\n\t\t}\n\t\twhile (_ReadPipe()) output(new(_c, 0, _n));\n\t\tif (lastError.code != Api.ERROR_BROKEN_PIPE) throw new AuException(0);\n\t}\n}\n"
  },
  {
    "path": "Au/System/osVersion.cs",
    "content": "namespace Au;\n\n/// <summary>\n/// Provides Windows version info. Also some info about the current process (eg 32/64 bit) and this library.\n/// </summary>\n/// <remarks>\n/// The Windows version properties return the true Windows version; it does not depend on manifest etc.\n/// </remarks>\n/// <seealso cref=\"OperatingSystem\"/>\n/// <seealso cref=\"RuntimeInformation\"/>\npublic static unsafe class osVersion {\n\tstatic osVersion() {\n\t\tApi.RTL_OSVERSIONINFOW x = default; x.dwOSVersionInfoSize = sizeof(Api.RTL_OSVERSIONINFOW);\n\t\tApi.RtlGetVersion(ref x); //use this because Environment.OSVersion.Version (GetVersionEx) lies, even if we have correct manifest when is debugger present\n\t\t_winver = Math2.MakeWord(_winminor = (int)x.dwMinorVersion, _winmajor = (int)x.dwMajorVersion);\n\t\t_winbuild = (int)x.dwBuildNumber;\n\n\t\t_minWin8 = _winver >= win8;\n\t\t_minWin8_1 = _winver >= win8_1;\n\t\t_minWin10 = _winver >= win10;\n\t\tif (_minWin10) _win10build = _winbuild;\n\t\t//print.it(_win10build);\n\n\t\t//this is to remind to add new members for new Windows 10/11 versions\n\t\t//Debug_.PrintIf(_win10build > 19044, $\"{_win10build} {Microsoft.Win32.Registry.GetValue(@\"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\", \"DisplayVersion\", \"failed\")}\");\n\n\t\t_is32BitOS = sizeof(nint) == 4 && !(Api.IsWow64Process(Api.GetCurrentProcess(), out _isWow64) && _isWow64);\n\t}\n\n\tstatic readonly int _winmajor, _winminor, _winver, _winbuild, _win10build;\n\tstatic readonly bool _minWin8, _minWin8_1, _minWin10;\n\tstatic readonly bool _is32BitOS, _isWow64;\n\n\t/// <summary>\n\t/// Gets Windows major version.\n\t/// </summary>\n\tpublic static int winMajor => _winmajor;\n\n\t/// <summary>\n\t/// Gets Windows minor version.\n\t/// </summary>\n\tpublic static int winMinor => _winminor;\n\n\t/// <summary>\n\t/// Gets Windows build number.\n\t/// For example 14393 for Windows 10 version 1607.\n\t/// </summary>\n\tpublic static int winBuild => _winbuild;\n\n\t/// <summary>\n\t/// Gets Windows major and minor version in single <c>int</c>: Win7 - 0x601; Win8 - 0x602; Win8.1 - 0x603; Win10/11 - 0xA00.\n\t/// Example: <c>if (osVersion.winVer >= osVersion.win8) ...</c>\n\t/// </summary>\n\tpublic static int winVer => _winver;\n\n\t/// <summary>\n\t/// Windows version major+minor value that can be used with <see cref=\"winVer\"/>.\n\t/// Example: <c>if (osVersion.winVer >= osVersion.win8) ...</c>\n\t/// </summary>\n\tpublic const int win7 = 0x601, win8 = 0x602, win8_1 = 0x603, win10 = 0xA00;\n\n\t/// <summary>\n\t/// <c>true</c> if Windows 8.0 or later.\n\t/// </summary>\n\tpublic static bool minWin8 => _minWin8;\n\n\t/// <summary>\n\t/// <c>true</c> if Windows 8.1 or later.\n\t/// </summary>\n\tpublic static bool minWin8_1 => _minWin8_1;\n\n\t/// <summary>\n\t/// <c>true</c> if Windows 10 or later.\n\t/// </summary>\n\tpublic static bool minWin10 => _minWin10;\n\n\t/// <summary>\n\t/// <c>true</c> if Windows 10 version 1607 or later.\n\t/// </summary>\n\tpublic static bool minWin10_1607 => _win10build >= 14393;\n\n\t/// <summary>\n\t/// <c>true</c> if Windows 10 version 1703 or later.\n\t/// </summary>\n\tpublic static bool minWin10_1703 => _win10build >= 15063;\n\n\t/// <summary>\n\t/// <c>true</c> if Windows 10 version 1709 or later.\n\t/// </summary>\n\tpublic static bool minWin10_1709 => _win10build >= 16299;\n\n\t/// <summary>\n\t/// <c>true</c> if Windows 10 version 1803 or later.\n\t/// </summary>\n\tpublic static bool minWin10_1803 => _win10build >= 17134;\n\n\t/// <summary>\n\t/// <c>true</c> if Windows 10 version 1809 or later.\n\t/// </summary>\n\tpublic static bool minWin10_1809 => _win10build >= 17763;\n\n\t/// <summary>\n\t/// <c>true</c> if Windows 10 version 1903 or later.\n\t/// </summary>\n\tpublic static bool minWin10_1903 => _win10build >= 18362;\n\n\t/// <summary>\n\t/// <c>true</c> if Windows 10 version 1909 or later.\n\t/// </summary>\n\tpublic static bool minWin10_1909 => _win10build >= 18363;\n\n\t/// <summary>\n\t/// <c>true</c> if Windows 10 version 2004 or later.\n\t/// </summary>\n\tpublic static bool minWin10_2004 => _win10build >= 19041;\n\n\t/// <summary>\n\t/// <c>true</c> if Windows 10 version 20H2 or later.\n\t/// </summary>\n\tpublic static bool minWin10_20H2 => _win10build >= 19042;\n\n\t/// <summary>\n\t/// <c>true</c> if Windows 10 version 21H1 or later.\n\t/// </summary>\n\tpublic static bool minWin10_21H1 => _win10build >= 19043;\n\n\t/// <summary>\n\t/// <c>true</c> if Windows 10 version 21H2 or later.\n\t/// </summary>\n\tpublic static bool minWin10_21H2 => _win10build >= 19044;\n\n\t/// <summary>\n\t/// <c>true</c> if Windows 10 version 22H2 or later.\n\t/// </summary>\n\tpublic static bool minWin10_22H2 => _win10build >= 19045;\n\n\t/// <summary>\n\t/// <c>true</c> if Windows 11 or later.\n\t/// </summary>\n\tpublic static bool minWin11 => _win10build >= 22000;\n\n\t/// <summary>\n\t/// <c>true</c> if Windows 11 version 22H2 or later.\n\t/// </summary>\n\tpublic static bool minWin11_22H2 => _win10build >= 22621;\n\n\t/// <summary>\n\t/// <c>true</c> if Windows 11 version 23H2 or later.\n\t/// </summary>\n\tpublic static bool minWin11_23H2 => _win10build >= 22631;\n\n\t/// <summary>\n\t/// <c>true</c> if Windows 11 version 24H2 or later.\n\t/// </summary>\n\tpublic static bool minWin11_24H2 => _win10build >= 26100;\n\n\t/// <summary>\n\t/// <c>true</c> if Windows 11 version 25H2 or later.\n\t/// </summary>\n\tpublic static bool minWin11_25H2 => _win10build >= 26200;\n\n\t/// <summary>\n\t/// <c>true</c> if this process is ARM64.\n\t/// </summary>\n\t/// <value><c>RuntimeInformation.ProcessArchitecture == Architecture.Arm64</c></value>\n\tpublic static bool isArm64Process => RuntimeInformation.ProcessArchitecture == Architecture.Arm64;\n\n\t/// <summary>\n\t/// <c>true</c> if Windows ARM64.\n\t/// </summary>\n\t/// <value><c>RuntimeInformation.OSArchitecture == Architecture.Arm64</c></value>\n\tpublic static bool isArm64OS => RuntimeInformation.OSArchitecture == Architecture.Arm64;\n\n\t/// <summary>\n\t/// <c>true</c> if this process is 32-bit, <c>false</c> if 64-bit (x64 or ARM64).\n\t/// The same as <c>sizeof(nint) == 4</c>.\n\t/// </summary>\n\tpublic static bool is32BitProcess => sizeof(nint) == 4;\n\n\t/// <summary>\n\t/// <c>true</c> if Windows 32-bit, <c>false</c> if 64-bit (x64 or ARM64).\n\t/// </summary>\n\tpublic static bool is32BitOS => _is32BitOS;\n\n\t/// <summary>\n\t/// Returns <c>true</c> if this process is a 32-bit process running on 64-bit Windows. Also known as WOW64 process.\n\t/// </summary>\n\tpublic static bool is32BitProcessAnd64BitOS => _isWow64;\n\n\t/// <summary>\n\t/// Gets the version string of this library (<c>Au.dll</c>), like <c>\"1.2.3\"</c>.\n\t/// </summary>\n\t/// <remarks>\n\t/// This function is fast, unlike <c>typeof(osVersion).Assembly.GetName().Version.ToString(3)</c>.\n\t/// \n\t/// LibreAutomate uses <c>Au.dll</c> of the same version as of LibreAutomate.\n\t/// </remarks>\n\tpublic static string thisLibrary => Au_.Version;\n\n\t/// <summary>\n\t/// Gets string containing OS version, .NET version and <c>Au.dll</c> version, like <c>\"10.0.22621-64|6.0.8|1.2.3\"</c>.\n\t/// Can be used for example to rebuild various caches when it's changed.\n\t/// </summary>\n\tpublic static string onaString => _environment.Value;\n\tstatic readonly Lazy<string> _environment = new(() => $\"{_winmajor.ToS()}.{_winminor.ToS()}.{_winbuild.ToS()}-{(_is32BitOS ? \"32\" : \"64\")}|{Environment.Version}|{Au_.Version}\");\n}\n"
  },
  {
    "path": "Au/System/process.cs",
    "content": "//#define USE_WTS\n\n//FUTURE: GetCpuUsage.\n\nnamespace Au {\n\t/// <summary>\n\t/// Process functions. Find, enumerate, get basic info, terminate, triggers, etc. Also includes properties and events of current process and thread.\n\t/// </summary>\n\t/// <seealso cref=\"run\"/>\n\t/// <seealso cref=\"script\"/>\n\t/// <seealso cref=\"Process\"/>\n\tpublic static unsafe class process {\n\t\t/// <summary>\n\t\t/// Gets process executable file name (like <c>\"notepad.exe\"</c>) or full path.\n\t\t/// </summary>\n\t\t/// <returns><c>null</c> if failed.</returns>\n\t\t/// <param name=\"processId\">Process id.</param>\n\t\t/// <param name=\"fullPath\">\n\t\t/// Get full path.\n\t\t/// Note: Fails to get full path if the process belongs to another user session, unless current process is running as administrator; also fails to get full path of some system processes.\n\t\t/// </param>\n\t\t/// <param name=\"noSlowAPI\">When the fast API <ms>QueryFullProcessImageName</ms> fails, don't try to use another much slower API <ms>WTSEnumerateProcesses</ms>. Not used if <i>fullPath</i> is <c>true</c>.</param>\n\t\t/// <remarks>\n\t\t/// This function is much slower than getting window name or class name.\n\t\t/// </remarks>\n\t\t/// <seealso cref=\"wnd.ProgramName\"/>\n\t\t/// <seealso cref=\"wnd.ProgramPath\"/>\n\t\t/// <seealso cref=\"wnd.ProcessId\"/>\n\t\tpublic static string getName(int processId, bool fullPath = false, bool noSlowAPI = false) {\n\t\t\tif (processId == 0) return null;\n\t\t\tstring R = null;\n\t\t\t\n\t\t\t//var t = perf.mcs;\n\t\t\t//if(s_time != 0) print.it(t - s_time);\n\t\t\t//s_time = t;\n\t\t\t\n\t\t\tusing var ph = Handle_.OpenProcess(processId);\n\t\t\tif (!ph.Is0) {\n\t\t\t\t//In non-admin process fails if the process is of another user session.\n\t\t\t\t//Also fails for some system processes: nvvsvc, nvxdsync, dwm. For dwm fails even in admin process.\n\t\t\t\t\n\t\t\t\t//getting native path is faster, but it gets like \"\\Device\\HarddiskVolume5\\Windows\\System32\\notepad.exe\" and I don't know API to convert to normal\n\t\t\t\tif (_QueryFullProcessImageName(ph, !fullPath, out var s)) {\n\t\t\t\t\tR = s;\n\t\t\t\t\tif (pathname.IsPossiblyDos_(R)) {\n\t\t\t\t\t\tif (fullPath || _QueryFullProcessImageName(ph, false, out s)) {\n\t\t\t\t\t\t\tR = pathname.ExpandDosPath_(s);\n\t\t\t\t\t\t\tif (!fullPath) R = _GetFileName(R);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (!noSlowAPI && !fullPath) {\n\t\t\t\t//the slow way. Can get only names, not paths.\n\t\t\t\tusing (var p = new AllProcesses_(false)) {\n\t\t\t\t\tfor (int i = 0; i < p.Count; i++)\n\t\t\t\t\t\tif (p.Id(i) == processId) {\n\t\t\t\t\t\t\tR = p.Name(i, cannotOpen: true);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t//TEST: NtQueryInformationProcess, like in getCommandLine.\n\t\t\t}\n\t\t\t\n\t\t\treturn R;\n\t\t\t\n\t\t\t//Would be good to cache process names here. But process id can be reused quickly. Use GetNameCached_ instead.\n\t\t\t//\ttested: a process id is reused after creating ~100 processes (and waiting until exits). It takes ~2 s.\n\t\t\t//\tThe window finder is optimized to call this once for each process and not for each window.\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Same as <c>GetName</c>, but faster when called several times for same window, like <c>if(w.ProgramName==\"A\" || w.ProgramName==\"B\")</c>.\n\t\t/// </summary>\n\t\tinternal static string GetNameCached_(wnd w, int processId, bool fullPath = false) {\n\t\t\tif (processId == 0) return null;\n\t\t\tvar cache = _LastWndProps.OfThread;\n\t\t\tcache.Begin(w);\n\t\t\tvar R = fullPath ? cache.ProgramPath : cache.ProgramName;\n\t\t\tif (R == null) {\n\t\t\t\tR = getName(processId, fullPath);\n\t\t\t\tif (fullPath) cache.ProgramPath = R; else cache.ProgramName = R;\n\t\t\t}\n\t\t\treturn R;\n\t\t}\n\t\t\n\t\tclass _LastWndProps {\n\t\t\twnd _w;\n\t\t\tlong _time;\n\t\t\tinternal string ProgramName, ProgramPath;\n\t\t\t\n\t\t\tinternal void Begin(wnd w) {\n\t\t\t\tvar t = Api.GetTickCount64();\n\t\t\t\tif (w != _w || t - _time > 300) { _w = w; ProgramName = ProgramPath = null; }\n\t\t\t\t_time = t;\n\t\t\t}\n\t\t\t\n\t\t\t[ThreadStatic] static _LastWndProps _ofThread;\n\t\t\tinternal static _LastWndProps OfThread => _ofThread ??= new();\n\t\t}\n\t\t\n\t\t[SkipLocalsInit]\n\t\tstatic bool _QueryFullProcessImageName(IntPtr hProcess, bool getFilename, out string s) {\n\t\t\ts = null;\n\t\t\tusing FastBuffer<char> b = new();\n\t\t\tfor (; ; b.More()) {\n\t\t\t\tint n = b.n;\n\t\t\t\tif (Api.QueryFullProcessImageName(hProcess, getFilename, b.p, ref n)) {\n\t\t\t\t\ts = getFilename ? _GetFileName(b.p, n) : new string(b.p, 0, n);\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tif (lastError.code != Api.ERROR_INSUFFICIENT_BUFFER) return false;\n\t\t\t}\n\t\t}\n\t\t\n#if USE_WTS //simple, safe, but ~2 times slower\n\t\tstruct _AllProcesses :IDisposable\n\t\t{\n\t\t\tProcessInfo_* _p;\n\n\t\t\tpublic _AllProcesses(out ProcessInfo_* p, out int count)\n\t\t\t{\n\t\t\t\tif(WTSEnumerateProcessesW(default, 0, 1, out p, out count)) _p = p; else _p = null;\n\t\t\t}\n\n\t\t\tpublic void Dispose()\n\t\t\t{\n\t\t\t\tif(_p != null) WTSFreeMemory(_p);\n\t\t\t}\n\n\t\t\t[DllImport(\"wtsapi32.dll\", SetLastError = true)]\n\t\t\tstatic extern bool WTSEnumerateProcessesW(IntPtr serverHandle, uint reserved, uint version, out ProcessInfo_* ppProcessInfo, out int pCount);\n\n\t\t\t[DllImport(\"wtsapi32.dll\", SetLastError = false)]\n\t\t\tstatic extern void WTSFreeMemory(ProcessInfo_* memory);\n\t\t}\n#else //the .NET Process class uses this. But it creates about 0.4 MB of garbage (last time tested was 0.2 MB).\n\t\tinternal unsafe struct AllProcesses_ : IDisposable {\n\t\t\treadonly _ProcessInfo* _p;\n\t\t\treadonly int _count;\n\t\t\tstatic int s_bufferSize = 500_000;\n\t\t\t\n\t\t\tpublic AllProcesses_(bool ofThisSession) {\n\t\t\t\tint sessionId = ofThisSession ? thisProcessSessionId : 0;\n\t\t\t\tApi.SYSTEM_PROCESS_INFORMATION* b = null;\n\t\t\t\ttry {\n\t\t\t\t\tfor (int na = s_bufferSize; ;) {\n\t\t\t\t\t\tMemoryUtil.FreeAlloc(ref b, na);\n\t\t\t\t\t\tint status = Api.NtQuerySystemInformation(5, b, na, out na);\n\t\t\t\t\t\t//print.it(na); //~300_000, Win10, year 2021\n\t\t\t\t\t\tif (status == 0) { s_bufferSize = na + 100_000; break; }\n\t\t\t\t\t\tif (status != Api.STATUS_INFO_LENGTH_MISMATCH) throw new AuException(status);\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tint nProcesses = 0, nbNames = 0;\n\t\t\t\t\tfor (var p = b; ; p = (Api.SYSTEM_PROCESS_INFORMATION*)((byte*)p + p->NextEntryOffset)) {\n\t\t\t\t\t\tif (!ofThisSession || p->SessionId == sessionId) {\n\t\t\t\t\t\t\tnProcesses++;\n\t\t\t\t\t\t\tnbNames += p->NameLength; //bytes, not chars\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (p->NextEntryOffset == 0) break;\n\t\t\t\t\t}\n\t\t\t\t\t_count = nProcesses;\n\t\t\t\t\t_p = (_ProcessInfo*)MemoryUtil.Alloc(nProcesses * sizeof(_ProcessInfo) + nbNames);\n\t\t\t\t\t_ProcessInfo* r = _p;\n\t\t\t\t\tchar* names = (char*)(_p + nProcesses);\n\t\t\t\t\tfor (var p = b; ; p = (Api.SYSTEM_PROCESS_INFORMATION*)((byte*)p + p->NextEntryOffset)) {\n\t\t\t\t\t\tif (!ofThisSession || p->SessionId == sessionId) {\n\t\t\t\t\t\t\tr->processID = (int)p->UniqueProcessId;\n\t\t\t\t\t\t\tr->sessionID = (int)p->SessionId;\n\t\t\t\t\t\t\tint len = p->NameLength / 2;\n\t\t\t\t\t\t\tr->nameLen = len;\n\t\t\t\t\t\t\tif (len > 0) {\n\t\t\t\t\t\t\t\t//copy name to _p memory because it's in the huge buffer that will be released in this func\n\t\t\t\t\t\t\t\tr->nameOffset = (int)(names - (char*)_p);\n\t\t\t\t\t\t\t\tMemoryUtil.Copy((char*)p->NamePtr, names, len * 2);\n\t\t\t\t\t\t\t\tnames += len;\n\t\t\t\t\t\t\t} else r->nameOffset = 0; //Idle\n\t\t\t\t\t\t\tr++;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (p->NextEntryOffset == 0) break;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfinally { MemoryUtil.Free(b); }\n\t\t\t}\n\t\t\t\n\t\t\tpublic void Dispose() {\n\t\t\t\tMemoryUtil.Free(_p);\n\t\t\t}\n\t\t\t\n\t\t\tpublic int Count => _count;\n\t\t\t\n\t\t\t//public ProcessInfo this[int i] => new(ProcessName(i), _p[i].processID, _p[i].sessionID); //rejected, could be used where shouldn't, making code slower etc\n\t\t\tpublic ProcessInfo Info(int i) => new(Name(i), _p[i].processID, _p[i].sessionID);\n\t\t\t\n\t\t\tpublic int Id(int i) => (uint)i < _count ? _p[i].processID : throw new IndexOutOfRangeException();\n\t\t\t\n\t\t\tpublic int SessionId(int i) => (uint)i < _count ? _p[i].sessionID : throw new IndexOutOfRangeException();\n\t\t\t\n\t\t\tpublic string Name(int i, bool cannotOpen = false) => (uint)i < _count ? _p[i].GetName(_p, cannotOpen) : throw new IndexOutOfRangeException();\n\t\t\t\n\t\t\tstruct _ProcessInfo {\n\t\t\t\tpublic int sessionID;\n\t\t\t\tpublic int processID;\n\t\t\t\tpublic int nameOffset;\n\t\t\t\tpublic int nameLen;\n\t\t\t\t\n\t\t\t\tpublic string GetName(void* p, bool cannotOpen) {\n\t\t\t\t\tif (nameOffset == 0) {\n\t\t\t\t\t\tif (processID == 0) return \"Idle\";\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\t\t\t\t\tstring R = new((char*)p + nameOffset, 0, nameLen);\n\t\t\t\t\tif (!cannotOpen && pathname.IsPossiblyDos_(R)) {\n\t\t\t\t\t\tusing var ph = Handle_.OpenProcess(processID);\n\t\t\t\t\t\tif (!ph.Is0 && _QueryFullProcessImageName(ph, false, out var s)) {\n\t\t\t\t\t\t\tR = _GetFileName(pathname.ExpandDosPath_(s));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn R;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n#endif\n\t\t\n\t\t/// <summary>\n\t\t/// Gets basic info of all processes: name, id, session id.\n\t\t/// </summary>\n\t\t/// <param name=\"ofThisSession\">Get processes only of this user session (skip services etc).</param>\n\t\t/// <exception cref=\"AuException\">Failed. Unlikely.</exception>\n\t\tpublic static ProcessInfo[] allProcesses(bool ofThisSession = false) {\n\t\t\tusing (var p = new AllProcesses_(ofThisSession)) {\n\t\t\t\tvar a = new ProcessInfo[p.Count];\n\t\t\t\tfor (int i = 0; i < a.Length; i++) a[i] = p.Info(i);\n\t\t\t\treturn a;\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets process ids of all processes of the specified program.\n\t\t/// </summary>\n\t\t/// <returns>Array containing zero or more elements.</returns>\n\t\t/// <param name=\"processName\">\n\t\t/// Process executable file name, like <c>\"notepad.exe\"</c>.\n\t\t/// String format: [wildcard expression](xref:wildcard_expression).\n\t\t/// </param>\n\t\t/// <param name=\"fullPath\">\n\t\t/// <i>processName</i> is full path.\n\t\t/// If <c>null</c>, calls <see cref=\"pathname.isFullPathExpand(ref string, bool?)\"/>.\n\t\t/// Note: Fails to get full path if the process belongs to another user session, unless current process is running as administrator; also fails to get full path of some system processes.\n\t\t/// </param>\n\t\t/// <param name=\"ofThisSession\">Get processes only of this user session.</param>\n\t\t/// <exception cref=\"ArgumentException\">\n\t\t/// - <i>processName</i> is <c>\"\"</c> or <c>null</c>.\n\t\t/// - Invalid wildcard expression (<c>\"**options \"</c> or regular expression).\n\t\t/// </exception>\n\t\tpublic static int[] getProcessIds([ParamString(PSFormat.Wildex)] string processName, bool? fullPath = false, bool ofThisSession = false) {\n\t\t\tList<int> a = null;\n\t\t\tbool fp = _NameOrPath(ref processName, fullPath);\n\t\t\tGetProcessesByName_(ref a, processName, fp, ofThisSession);\n\t\t\treturn a?.ToArray() ?? [];\n\t\t}\n\t\t\n\t\tstatic bool _NameOrPath(ref string processName, bool? fullPath) {\n\t\t\tif (processName.NE()) throw new ArgumentException();\n\t\t\treturn fullPath switch {\n\t\t\t\tfalse => false,\n\t\t\t\ttrue => pathname.isFullPathExpand(ref processName, strict: false) | true,\n\t\t\t\t_ => pathname.isFullPathExpand(ref processName, strict: false)\n\t\t\t};\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets process id of the first found process of the specified program.\n\t\t/// </summary>\n\t\t/// <returns>0 if not found.</returns>\n\t\t/// <inheritdoc cref=\"getProcessIds\"/>\n\t\tpublic static int getProcessId([ParamString(PSFormat.Wildex)] string processName, bool? fullPath = false, bool ofThisSession = false) {\n\t\t\tList<int> a = null;\n\t\t\tbool fp = _NameOrPath(ref processName, fullPath);\n\t\t\treturn GetProcessesByName_(ref a, processName, fp, ofThisSession, true);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if a process of the specified program is running.\n\t\t/// </summary>\n\t\t/// <inheritdoc cref=\"getProcessIds\"/>\n\t\tpublic static bool exists([ParamString(PSFormat.Wildex)] string processName, bool? fullPath = false, bool ofThisSession = false)\n\t\t\t=> 0 != getProcessId(processName, fullPath, ofThisSession);\n\t\t\n\t\tinternal static int GetProcessesByName_(ref List<int> a, wildex processName, bool fullPath = false, bool ofThisSession = false, bool first = false) {\n\t\t\ta?.Clear();\n\t\t\tusing (var p = new AllProcesses_(ofThisSession)) {\n\t\t\t\tfor (int i = 0; i < p.Count; i++) {\n\t\t\t\t\tstring s = fullPath ? getName(p.Id(i), true) : p.Name(i);\n\t\t\t\t\tif (s != null && processName.Match(s)) {\n\t\t\t\t\t\tint pid = p.Id(i);\n\t\t\t\t\t\tif (first) return pid;\n\t\t\t\t\t\ta ??= new List<int>();\n\t\t\t\t\t\ta.Add(pid);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn 0;\n\t\t}\n\t\t\n\t\tstatic string _GetFileName(char* s, int len) {\n\t\t\tif (s == null) return null;\n\t\t\tchar* ss = s + len;\n\t\t\tfor (; ss > s; ss--) if (ss[-1] == '\\\\' || ss[-1] == '/') break;\n\t\t\treturn new string(ss, 0, len - (int)(ss - s));\n\t\t}\n\t\t\n\t\tstatic string _GetFileName(string s) {\n\t\t\tfixed (char* p = s) return _GetFileName(p, s.Length);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets version info of process executable file.\n\t\t/// </summary>\n\t\t/// <returns><c>null</c> if failed.</returns>\n\t\t/// <param name=\"processId\">Process id.</param>\n\t\tpublic static FileVersionInfo getVersionInfo(int processId) {\n\t\t\tvar s = getName(processId, true);\n\t\t\tif (s != null) {\n\t\t\t\ttry { return FileVersionInfo.GetVersionInfo(s); } catch { }\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets description of process executable file.\n\t\t/// </summary>\n\t\t/// <returns><c>null</c> if failed.</returns>\n\t\t/// <param name=\"processId\">Process id.</param>\n\t\t/// <remarks>\n\t\t/// Calls <see cref=\"getVersionInfo\"/> and <see cref=\"FileVersionInfo.FileDescription\"/>.\n\t\t/// </remarks>\n\t\tpublic static string getDescription(int processId) => getVersionInfo(processId)?.FileDescription;\n\t\t\n\t\t/// <summary>\n\t\t/// Gets process id from handle (API <ms>GetProcessId</ms>).\n\t\t/// </summary>\n\t\t/// <returns>0 if failed. Supports <see cref=\"lastError\"/>.</returns>\n\t\t/// <param name=\"processHandle\">Process handle.</param>\n\t\tpublic static int processIdFromHandle(IntPtr processHandle) => Api.GetProcessId(processHandle); //fast\n\t\t\n\t\t//public static Process processObjectFromHandle(IntPtr processHandle)\n\t\t//{\n\t\t//\tint pid = GetProcessId(processHandle);\n\t\t//\tif(pid == 0) return null;\n\t\t//\treturn Process.GetProcessById(pid); //slow, makes much garbage, at first gets all processes just to throw exception if pid not found...\n\t\t//}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets user session id of a process (API <ms>ProcessIdToSessionId</ms>).\n\t\t/// </summary>\n\t\t/// <returns>Returns -1 if failed. Supports <see cref=\"lastError\"/>.</returns>\n\t\t/// <param name=\"processId\">Process id.</param>\n\t\tpublic static int getSessionId(int processId) {\n\t\t\tif (!Api.ProcessIdToSessionId(processId, out var R)) return -1;\n\t\t\treturn R;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets process creation and execution times (API <ms>GetProcessTimes</ms>).\n\t\t/// </summary>\n\t\t/// <returns><c>false</c> if failed. Supports <see cref=\"lastError\"/>.</returns>\n\t\t/// <param name=\"processId\">Process id.</param>\n\t\t/// <param name=\"created\">Creation time. As absolute <ms>FILETIME</ms>, UTC. If you need <see cref=\"DateTime\"/>, use <see cref=\"DateTime.FromFileTimeUtc\"/>.</param>\n\t\t/// <param name=\"executed\">Amount of time spent executing code (using CPU). As <ms>FILETIME</ms>. If you need <see cref=\"TimeSpan\"/>, use <see cref=\"TimeSpan.FromTicks\"/>.</param>\n\t\tpublic static bool getTimes(int processId, out long created, out long executed) {\n\t\t\tcreated = 0; executed = 0;\n\t\t\tusing var ph = Handle_.OpenProcess(processId);\n\t\t\tif (ph.Is0 || !Api.GetProcessTimes(ph, out created, out _, out long tk, out long tu)) return false;\n\t\t\texecuted = tk + tu;\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if the process is 32-bit, <c>false</c> if 64-bit.\n\t\t/// Also returns <c>false</c> if failed. Supports <see cref=\"lastError\"/>.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// <note>If you know it is current process, instead use <see cref=\"osVersion\"/> functions or <c>IntPtr.Size==4</c>. This function is much slower.</note>\n\t\t/// </remarks>\n\t\t/// <seealso cref=\"RuntimeInformation\"/>\n\t\tpublic static bool is32Bit(int processId) {\n\t\t\tbool is32bit = osVersion.is32BitOS;\n\t\t\tif (!is32bit) {\n\t\t\t\tusing var ph = Handle_.OpenProcess(processId);\n\t\t\t\tif (ph.Is0 || !Api.IsWow64Process(ph, out is32bit)) return false;\n\t\t\t}\n\t\t\tlastError.clear();\n\t\t\treturn is32bit;\n\t\t\t\n\t\t\t//info: don't use Process.GetProcessById, it does not have a desiredAccess parameter and fails with higher IL processes.\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if the process is 32-bit, <c>false</c> if 64-bit.\n\t\t/// Also returns <c>false</c> if failed. Supports <see cref=\"lastError\"/>.\n\t\t/// </summary>\n\t\tpublic static bool is32Bit(IntPtr processHandle) {\n\t\t\tbool is32bit = osVersion.is32BitOS;\n\t\t\tif (!is32bit) {\n\t\t\t\tif (!Api.IsWow64Process(processHandle, out is32bit)) return false;\n\t\t\t}\n\t\t\tlastError.clear();\n\t\t\treturn is32bit;\n\t\t}\n\t\t\n\t\t//rejected: isArm64, or architecture, or cpuArch. Rarely used.\n\t\t\n\t\t/// <summary>\n\t\t/// Gets the command line string used to start the specified process.\n\t\t/// </summary>\n\t\t/// <returns><c>null</c> if failed.</returns>\n\t\t/// <param name=\"processId\">Process id.</param>\n\t\t/// <param name=\"removeProgram\">Remove program path. Return only arguments, or empty string if there is no arguments.</param>\n\t\t/// <remarks>\n\t\t/// The string starts with program file path or name, often enclosed in <c>\"\"</c>, and may be followed by arguments. Some processes may modify it; then this function gets the modified string.\n\t\t/// Fails if the specified process is admin and this process isn't. May fail with some system processes. Fails if this is a 32-bit process.\n\t\t/// </remarks>\n\t\tpublic static unsafe string getCommandLine(int processId, bool removeProgram = false) {\n\t\t\tif (osVersion.is32BitProcess) return null; //can't get PEB address of 64-bit processes. Never mind 32-bit OS.\n\t\t\tusing var pm = new ProcessMemory(processId, 0, noException: true);\n\t\t\tif (pm.ProcessHandle == default) return null;\n\t\t\tApi.PROCESS_BASIC_INFORMATION pbi = default;\n\t\t\tif (0 == Api.NtQueryInformationProcess(pm.ProcessHandle, 0, &pbi, sizeof(Api.PROCESS_BASIC_INFORMATION), out _)) {\n\t\t\t\tlong upp; Api.RTL_USER_PROCESS_PARAMETERS up;\n\t\t\t\tif (pm.Read((IntPtr)pbi.PebBaseAddress + 32, &upp, 8) && pm.Read((IntPtr)upp, &up, sizeof(Api.RTL_USER_PROCESS_PARAMETERS))) {\n\t\t\t\t\tpm.Mem = (IntPtr)up.CommandLine.Buffer;\n\t\t\t\t\tvar s = pm.ReadCharString(up.CommandLine.Length / 2)\n\t\t\t\t\t\t?.Trim() //many end with space, usually when without commandline args\n\t\t\t\t\t\t?.Replace('\\0', ' '); //sometimes '\\0' instead of spaces before args\n\t\t\t\t\tif (removeProgram) s = s.RxReplace(@\"(?i)^(?:\"\".+?\"\"|\\S+)(?:\\s+(.*))?\", \"$1\");\n\t\t\t\t\treturn s;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t\t\n\t\t\t//speed: ~25 mcs cold. WMI Win32_Process ~50 ms (2000 times slower).\n\t\t}\n\t\t\n\t\tinternal static unsafe int GetParentProcessId_() {\n\t\t\tApi.PROCESS_BASIC_INFORMATION pbi = default;\n\t\t\tif (0 != Api.NtQueryInformationProcess(thisProcessHandle, 0, &pbi, sizeof(Api.PROCESS_BASIC_INFORMATION), out _)) return 0;\n\t\t\treturn (int)pbi.ParentProcessId;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Terminates (ends) the specified process.\n\t\t/// </summary>\n\t\t/// <returns><c>false</c> if failed. Supports <see cref=\"lastError\"/>.</returns>\n\t\t/// <param name=\"processId\">Process id.</param>\n\t\t/// <param name=\"exitCode\">Process exit code.</param>\n\t\t/// <remarks>\n\t\t/// Uses API <ms>WTSTerminateProcess</ms> or <ms>TerminateProcess</ms>. They are async; usually the process ends after 2 - 200 ms, depending on program etc.\n\t\t/// \n\t\t/// Does not try to end process \"softly\" (close main window). Unsaved data will be lost.\n\t\t/// \n\t\t/// Alternatives: run <c>taskkill.exe</c> or <c>pskill.exe</c> (download). See <see cref=\"run.console\"/>. More info on the internet.\n\t\t/// </remarks>\n\t\t/// <example>\n\t\t/// Restart the shell process (explorer).\n\t\t/// <code><![CDATA[\n\t\t/// process.terminate(wnd.getwnd.shellWindow.ProcessId, 1);\n\t\t/// if (!dialog.showYesNo(\"Restart explorer?\")) return;\n\t\t/// run.it(folders.Windows + @\"explorer.exe\", flags: RFlags.InheritAdmin);\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic static bool terminate(int processId, int exitCode = 0) {\n\t\t\tif (Api.WTSTerminateProcess(default, processId, exitCode)) return true;\n\t\t\tif (lastError.code != Api.ERROR_INVALID_PARAMETER) {\n\t\t\t\tusing var h = Handle_.OpenProcess(processId, Api.SYNCHRONIZE | Api.PROCESS_TERMINATE);\n\t\t\t\tif (!h.Is0) {\n\t\t\t\t\tif (!Api.TerminateProcess(h, exitCode))\n\t\t\t\t\t\treturn 0 == Api.WaitForSingleObject(h, 500); //ERROR_ACCESS_DENIED when the process is ending\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t\t//note: TerminateProcess and WTSTerminateProcess are async. Tested programs ended after 3 - 150 ms, depending on program.\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Terminates (ends) all processes of the specified program or programs.\n\t\t/// </summary>\n\t\t/// <returns>The number of successfully terminated processes.</returns>\n\t\t/// <param name=\"processName\">\n\t\t/// Process executable file name (like <c>\"notepad.exe\"</c>) or full path.\n\t\t/// String format: [wildcard expression](xref:wildcard_expression).\n\t\t/// </param>\n\t\t/// <param name=\"allSessions\">Processes of any user session. If <c>false</c> (default), only processes of this user session.</param>\n\t\t/// <param name=\"exitCode\">Process exit code.</param>\n\t\t/// <exception cref=\"ArgumentException\">\n\t\t/// - <i>processName</i> is <c>\"\"</c> or <c>null</c>.\n\t\t/// - Invalid wildcard expression (<c>\"**options \"</c> or regular expression).\n\t\t/// </exception>\n\t\tpublic static int terminate(string processName, bool allSessions = false, int exitCode = 0) {\n\t\t\tint n = 0;\n\t\t\tforeach (int pid in getProcessIds(processName, fullPath: null, ofThisSession: !allSessions)) {\n\t\t\t\tif (terminate(pid, exitCode)) n++;\n\t\t\t}\n\t\t\treturn n;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Suspends or resumes the specified process.\n\t\t/// </summary>\n\t\t/// <returns><c>false</c> if failed. Supports <see cref=\"lastError\"/>.</returns>\n\t\t/// <param name=\"suspend\"><c>true</c> suspend, <c>false</c> resume.</param>\n\t\t/// <param name=\"processId\">Process id.</param>\n\t\t/// <remarks>\n\t\t/// If suspended multiple times, must be resumed the same number of times.\n\t\t/// </remarks>\n\t\tpublic static bool suspend(bool suspend, int processId) {\n\t\t\tusing var hp = Handle_.OpenProcess(processId, Api.PROCESS_SUSPEND_RESUME);\n\t\t\tif (!hp.Is0) {\n\t\t\t\tint status = suspend ? Api.NtSuspendProcess(hp) : Api.NtResumeProcess(hp);\n\t\t\t\tlastError.code = status;\n\t\t\t\treturn status == 0;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Suspends or resumes all processes of the specified program or programs.\n\t\t/// </summary>\n\t\t/// <returns>The number of successfully suspended/resumed processes.</returns>\n\t\t/// <param name=\"suspend\"><c>true</c> suspend, <c>false</c> resume.</param>\n\t\t/// <param name=\"processName\">\n\t\t/// Process executable file name (like <c>\"notepad.exe\"</c>) or full path.\n\t\t/// String format: [wildcard expression](xref:wildcard_expression).\n\t\t/// </param>\n\t\t/// <param name=\"allSessions\">Processes of any user session. If <c>false</c> (default), only processes of this user session.</param>\n\t\t/// <exception cref=\"ArgumentException\">\n\t\t/// - <i>processName</i> is <c>\"\"</c> or <c>null</c>.\n\t\t/// - Invalid wildcard expression (<c>\"**options \"</c> or regular expression).\n\t\t/// </exception>\n\t\t/// <remarks>\n\t\t/// If suspended multiple times, must be resumed the same number of times.\n\t\t/// </remarks>\n\t\tpublic static int suspend(bool suspend, string processName, bool allSessions = false) {\n\t\t\tint n = 0;\n\t\t\tforeach (int pid in getProcessIds(processName, fullPath: null, ofThisSession: !allSessions)) {\n\t\t\t\tif (process.suspend(suspend, pid)) n++;\n\t\t\t}\n\t\t\treturn n;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Waits until the process ends.\n\t\t/// </summary>\n\t\t/// <returns><c>true</c> when the process ended. On timeout returns <c>false</c> if <i>timeout</i> is negative; else exception.</returns>\n\t\t/// <param name=\"timeout\">Timeout, seconds. Can be 0 (infinite), >0 (exception) or &lt;0 (no exception). More info: [](xref:wait_timeout).</param>\n\t\t/// <param name=\"processId\">Process id. If invalid but not 0, the function returns <c>true</c> and sets <c>exitCode = int.MinValue</c>; probably the process is already ended.</param>\n\t\t/// <param name=\"exitCode\">Receives the exit code.</param>\n\t\t/// <exception cref=\"TimeoutException\"><i>timeout</i> time has expired (if > 0).</exception>\n\t\t/// <exception cref=\"AuException\">Failed.</exception>\n\t\t/// <exception cref=\"ArgumentException\"><i>processId</i> is 0.</exception>\n\t\tpublic static bool waitForExit(Seconds timeout, int processId, out int exitCode) {\n\t\t\tif (processId == 0) throw new ArgumentException(\"processId 0\", nameof(processId));\n\t\t\tusing var h = Handle_.OpenProcess(processId, Api.SYNCHRONIZE | Api.PROCESS_QUERY_LIMITED_INFORMATION);\n\t\t\tif (h.Is0) {\n\t\t\t\tvar e = lastError.code;\n\t\t\t\tif (e == Api.ERROR_INVALID_PARAMETER) { exitCode = int.MinValue; return true; };\n\t\t\t\tthrow new AuException(e);\n\t\t\t}\n\t\t\texitCode = 0;\n\t\t\tif (0 == wait.forHandle(timeout, 0, h)) return false;\n\t\t\tApi.GetExitCodeProcess(h, out exitCode);\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Provides process started/ended triggers in <c>foreach</c> loop. See examples.\n\t\t/// </summary>\n\t\t/// <returns>\n\t\t/// An object that retrieves process trigger info (started/ended, name, id, session id) when used with <c>foreach</c>.\n\t\t/// If need more process properties, your code can call <see cref=\"process\"/> class functions with the process id.\n\t\t/// </returns>\n\t\t/// <param name=\"started\">Trigger events: <c>true</c> - started, <c>false</c> - ended, <c>null</c> (default) - both.</param>\n\t\t/// <param name=\"processName\">\n\t\t/// Process executable file name, like <c>\"notepad.exe\"</c>.\n\t\t/// String format: [wildcard expression](xref:wildcard_expression).\n\t\t/// <c>null</c> matches all.\n\t\t/// </param>\n\t\t/// <param name=\"ofThisSession\">Watch processes only of this user session.</param>\n\t\t/// <param name=\"period\">\n\t\t/// The period in milliseconds of retrieving the list of processes for detecting new and ended processes. Default 100, min 10, max 1000.\n\t\t/// Smaller = smaller average delay and less missing triggers (when process lifetime is very short) but more CPU usage.\n\t\t/// </param>\n\t\t/// <exception cref=\"ArgumentException\">Invalid wildcard expression (<c>\"**options \"</c> or regular expression).</exception>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// //all started and ended processes\n\t\t/// foreach (var v in process.triggers()) {\n\t\t/// \tprint.it(v);\n\t\t/// }\n\t\t/// \n\t\t/// //started notepad processes in current user session\n\t\t/// foreach (var v in process.triggers(started: <c>true</c>, \"notepad.exe\", ofThisSession: true)) {\n\t\t/// \tprint.it(v);\n\t\t/// }\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic static IEnumerable<ProcessTriggerInfo> triggers(bool? started = null, [ParamString(PSFormat.Wildex)] string processName = null, bool ofThisSession = false, int period = 100) {\n\t\t\twildex wild = processName;\n\t\t\tperiod = Math.Clamp(period, 10, 1000) - 2;\n\t\t\tvar comparer = new _PiComparer();\n\t\t\tvar hs = new HashSet<ProcessInfo>(comparer);\n\t\t\tvar ap = allProcesses(ofThisSession);\n\t\t\tfor (; ; ) {\n\t\t\t\tperiod.ms();\n\t\t\t\t//Debug_.MemorySetAnchor_();\n\t\t\t\t//perf.first();\n\t\t\t\tusing (var p = new AllProcesses_(ofThisSession)) {\n\t\t\t\t\t//perf.next();\n\t\t\t\t\tbool eq = p.Count == ap.Length;\n\t\t\t\t\tif (eq) for (int i = 0; i < ap.Length; i++) if (!(eq = ap[i].Id == p.Id(i))) break;\n\t\t\t\t\tif (!eq) {\n\t\t\t\t\t\tvar a = new ProcessInfo[p.Count];\n\t\t\t\t\t\tfor (int i = 0; i < a.Length; i++) a[i] = p.Info(i);\n\t\t\t\t\t\tfor (int i = 0; i < 2; i++) {\n\t\t\t\t\t\t\tProcessInfo[] a1, a2;\n\t\t\t\t\t\t\tif (i == 0) {\n\t\t\t\t\t\t\t\tif (started == true) continue;\n\t\t\t\t\t\t\t\ta1 = a; a2 = ap;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tif (started == false) continue;\n\t\t\t\t\t\t\t\ta1 = ap; a2 = a;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\ths.Clear(); hs.UnionWith(a1);\n\t\t\t\t\t\t\tforeach (var v in a2) {\n\t\t\t\t\t\t\t\tif (hs.Add(v)) {\n\t\t\t\t\t\t\t\t\tif (wild == null || wild.Match(v.Name))\n\t\t\t\t\t\t\t\t\t\tyield return new(i == 1, v.Name, v.Id, v.SessionId);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tap = a;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t//perf.nw();\n\t\t\t\t//Debug_.MemoryPrint_();\n\t\t\t}\n\t\t}\n\t\t\n\t\tclass _PiComparer : IEqualityComparer<ProcessInfo> {\n\t\t\t//public bool Equals(ProcessInfo x, ProcessInfo y) => x.Id == y.Id;\n\t\t\tpublic bool Equals(ProcessInfo x, ProcessInfo y) => x.Id == y.Id && x.Name == y.Name;\n\t\t\tpublic int GetHashCode(ProcessInfo obj) => obj.Id;\n\t\t}\n\t\t\n\t\t#region this process\n\t\t\n\t\t/// <summary>\n\t\t/// Gets current process id.\n\t\t/// See API <ms>GetCurrentProcessId</ms>.\n\t\t/// </summary>\n\t\tpublic static int thisProcessId => Api.GetCurrentProcessId();\n\t\t\n\t\t/// <summary>\n\t\t/// Returns current process handle.\n\t\t/// See API <ms>GetCurrentProcess</ms>.\n\t\t/// Don't need to close the handle.\n\t\t/// </summary>\n\t\tpublic static IntPtr thisProcessHandle => Api.GetCurrentProcess();\n\t\t\n\t\t//rejected. Too simple and rare.\n\t\t///// <summary>\n\t\t///// Gets native module handle of the program file of this process.\n\t\t///// </summary>\n\t\t//public static IntPtr thisExeModuleHandle => Api.GetModuleHandle(null);\n\t\t\n\t\t/// <summary>\n\t\t/// Gets full path of the program file of this process.\n\t\t/// </summary>\n\t\t[SkipLocalsInit]\n\t\tpublic static unsafe string thisExePath => Environment.ProcessPath;\n\t\t\n\t\t/// <summary>\n\t\t/// Gets file name of the program file of this process, like <c>\"name.exe\"</c>.\n\t\t/// </summary>\n\t\tpublic static string thisExeName => s_exeName ??= pathname.getName(thisExePath);\n\t\tstatic string s_exeName;\n\t\t\n\t\t/// <summary>\n\t\t/// Gets user session id of this process.\n\t\t/// </summary>\n\t\tpublic static int thisProcessSessionId => getSessionId(Api.GetCurrentProcessId());\n\t\t\n\t\t/// <summary>\n\t\t/// Gets or sets whether <see cref=\"CultureInfo.DefaultThreadCurrentCulture\"/> and <see cref=\"CultureInfo.DefaultThreadCurrentUICulture\"/> are <see cref=\"CultureInfo.InvariantCulture\"/>.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// If your app doesn't want to use current culture (default in .NET apps), it can set these properties = <see cref=\"CultureInfo.InvariantCulture\"/> or set this property = <c>true</c>.\n\t\t/// It prevents potential bugs when app/script/components don't specify invariant culture in string functions and \"number to/from string\" functions.\n\t\t/// Also, there is a bug in \"number to/from string\" functions in some .NET versions with some cultures: they use wrong minus sign, not ASCII <c>'-'</c> which is specified in Control Panel.\n\t\t/// The default compiler sets this property = <c>true</c>; as well as <see cref=\"script.setup\"/>.\n\t\t/// </remarks>\n\t\tpublic static bool thisProcessCultureIsInvariant {\n\t\t\tget {\n\t\t\t\tvar ic = CultureInfo.InvariantCulture;\n\t\t\t\treturn CultureInfo.DefaultThreadCurrentCulture == ic && CultureInfo.DefaultThreadCurrentUICulture == ic;\n\t\t\t}\n\t\t\tset {\n\t\t\t\tif (value) {\n\t\t\t\t\tvar ic = CultureInfo.InvariantCulture;\n\t\t\t\t\tCultureInfo.DefaultThreadCurrentCulture = ic;\n\t\t\t\t\tCultureInfo.DefaultThreadCurrentUICulture = ic;\n\t\t\t\t} else {\n\t\t\t\t\tCultureInfo.DefaultThreadCurrentCulture = null;\n\t\t\t\t\tCultureInfo.DefaultThreadCurrentUICulture = null;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// <c>true</c> in LA main process. LA sets it, except in tools/pip/dragdrop processes.\n\t\t/// </summary>\n\t\tinternal static bool IsLaProcess_;\n\n\t\t/// <summary>\n\t\t/// <c>true</c> in main thread of main LA process. LA sets it, except in tools/pip/dragdrop processes. This is a [ThreadStatic] variable.\n\t\t/// NOTE: don't use <c>Environment.CurrentManagedThreadId == 1</c>, it's not always 1 in the main thread.\n\t\t/// </summary>\n\t\t[ThreadStatic]\n\t\tinternal static bool IsLaMainThread_;\n\t\t\n\t\t/// <summary>\n\t\t/// After <i>afterMS</i> milliseconds invokes GC and calls API <c>SetProcessWorkingSetSize</c>.\n\t\t/// </summary>\n\t\tinternal static void ThisProcessMinimizePhysicalMemory_(int afterMS) {\n\t\t\tTask.Delay(afterMS).ContinueWith(_ => {\n\t\t\t\tGC.Collect();\n\t\t\t\tGC.WaitForPendingFinalizers();\n\t\t\t\tApi.SetProcessWorkingSetSize(Api.GetCurrentProcess(), -1, -1);\n\t\t\t});\n\t\t}\n\t\t\n\t\t//internal static (long WorkingSet, long PageFile) ThisProcessGetMemoryInfo_()\n\t\t//{\n\t\t//\tApi.PROCESS_MEMORY_COUNTERS m = default; m.cb = sizeof(Api.PROCESS_MEMORY_COUNTERS);\n\t\t//\tApi.GetProcessMemoryInfo(ProcessHandle, ref m, m.cb);\n\t\t//\treturn ((long)m.WorkingSetSize, (long)m.PagefileUsage);\n\t\t//}\n\t\t\n\t\t/// <summary>\n\t\t/// Before this process exits, either normally or on unhandled exception.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// The event handler is called on:\n\t\t/// <br/>• <see cref=\"AppDomain.ProcessExit\"/>, with parameter = <c>null</c>.\n\t\t/// <br/>• <see cref=\"AppDomain.UnhandledException\"/>, with parameter = the <see cref=\"Exception\"/>.\n\t\t/// </remarks>\n\t\tpublic static event Action<Exception> thisProcessExit {\n\t\t\tadd {\n\t\t\t\tif (!_haveEventExit) {\n\t\t\t\t\tlock (\"AVCyoRcQCkSl+3W8ZTi5oA\") {\n\t\t\t\t\t\tif (!_haveEventExit) {\n\t\t\t\t\t\t\tvar d = AppDomain.CurrentDomain;\n\t\t\t\t\t\t\td.ProcessExit += _ThisProcessExit;\n\t\t\t\t\t\t\td.UnhandledException += _ThisProcessExit; //because ProcessExit is missing on exception\n\t\t\t\t\t\t\t_haveEventExit = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t_eventExit += value;\n\t\t\t}\n\t\t\tremove {\n\t\t\t\t_eventExit -= value;\n\t\t\t}\n\t\t}\n\t\tstatic Action<Exception> _eventExit;\n\t\tstatic bool _haveEventExit;\n\t\t\n\t\tstatic void _ThisProcessExit(object sender, EventArgs ea) { //sender: AppDomain on process exit, null on unhandled exception\n\t\t\tException e;\n\t\t\tif (ea is UnhandledExceptionEventArgs u) {\n\t\t\t\tif (!u.IsTerminating) return; //never seen, but anyway\n\t\t\t\te = (Exception)u.ExceptionObject; //probably non-Exception object is impossible in C#\n\t\t\t} else {\n\t\t\t\te = script.s_unhandledException;\n\t\t\t}\n\t\t\tvar k = _eventExit;\n\t\t\tif (k != null) {\n\t\t\t\ttry { _eventExit = null; k(e); }\n\t\t\t\tcatch (Exception e1) { print.qm2.writeD(\"_ThisProcessExit\", e1); }\n\t\t\t}\n\t\t\tthisProcessExitDone_?.Invoke();\n\t\t}\n\t\t\n\t\tinternal static event Action thisProcessExitDone_;\n\t\t\n\t\t/// <summary>\n\t\t/// Calls and removes all <see cref=\"thisProcessExit\"/> event handlers.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Call this if <see cref=\"thisProcessExit\"/> event handlers don't run because this process is terminated before it. For example when current session is ending (shutdown, restart, logoff); to detect it can be used <c>Application.SessionEnding</c>, <c>Application.OnSessionEnding</c> or <ms>WM_QUERYENDSESSION</ms>.\n\t\t/// </remarks>\n\t\tpublic static void thisProcessExitInvoke() {\n\t\t\tvar k = _eventExit;\n\t\t\tif (k != null) try { _eventExit = null; k(null); } catch { }\n\t\t}\n\t\t\n\t\t#endregion\n\t\t\n\t\t#region this thread\n\t\t\n\t\t/// <summary>\n\t\t/// Gets native thread id of this thread (API <ms>GetCurrentThreadId</ms>).\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// It is not the same as <see cref=\"Environment.CurrentManagedThreadId\"/>.\n\t\t/// </remarks>\n\t\t/// <seealso cref=\"wnd.ThreadId\"/>\n\t\tpublic static int thisThreadId => Api.GetCurrentThreadId();\n\t\t//speed: fast, but several times slower than Environment.CurrentManagedThreadId. Caching in a ThreadStatic variable makes even slower.\n\t\t\n\t\t/// <summary>\n\t\t/// Returns native thread handle of this thread (API <ms>GetCurrentThread</ms>).\n\t\t/// </summary>\n\t\tpublic static IntPtr thisThreadHandle => Api.GetCurrentThread();\n\t\t\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if this thread has a .NET message loop (winforms or WPF).\n\t\t/// </summary>\n\t\t/// <param name=\"isWPF\">Has WPF message loop and no winforms message loop.</param>\n\t\t/// <seealso cref=\"wnd.getwnd.threadWindows\"/>\n\t\tpublic static bool thisThreadHasMessageLoop(out bool isWPF) {\n\t\t\t//info: we don't call .NET functions directly to avoid loading assemblies.\n\t\t\t\n\t\t\tisWPF = false;\n\t\t\tint f = AssemblyUtil_.IsLoadedWinformsWpf();\n\t\t\tif (0 != (f & 1) && _HML_Forms()) return true;\n\t\t\tif (0 != (f & 2) && _HML_Wpf()) return isWPF = true;\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\t///\n\t\tpublic static bool thisThreadHasMessageLoop() => thisThreadHasMessageLoop(out _);\n\t\t\n\t\t[MethodImpl(MethodImplOptions.NoInlining)]\n\t\tstatic bool _HML_Forms() => System.Windows.Forms.Application.MessageLoop;\n\t\t\n\t\t[MethodImpl(MethodImplOptions.NoInlining)]\n\t\tstatic bool _HML_Wpf() {\n\t\t\tif (SynchronizationContext.Current is System.Windows.Threading.DispatcherSynchronizationContext) {\n\t\t\t\tvar d = System.Windows.Threading.Dispatcher.FromThread(Thread.CurrentThread);\n\t\t\t\tif (d != null) {\n\t\t\t\t\tvar f = typeof(System.Windows.Threading.Dispatcher).GetField(\"_frameDepth\", BindingFlags.Instance | BindingFlags.NonPublic);\n\t\t\t\t\tDebug_.PrintIf(f == null);\n\t\t\t\t\treturn f == null || f.GetValue(d) is not int i || i > 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t\t//static bool _HML_Wpf() => System.Windows.Threading.Dispatcher.FromThread(Thread.CurrentThread) != null; //no. Not null after a loop ends or even after XamlReader.Parse.\n\t\t//static bool _HML_Wpf() => SynchronizationContext.Current is System.Windows.Threading.DispatcherSynchronizationContext; //no. True eg in Dispatcher.Invoke callback.\n\t\t\n\t\t[MethodImpl(MethodImplOptions.AggressiveInlining)]\n\t\tinternal static void ThisThreadSetComApartment_(ApartmentState state) {\n\t\t\tvar t = Thread.CurrentThread;\n\t\t\tt.TrySetApartmentState(ApartmentState.Unknown);\n\t\t\tt.TrySetApartmentState(state);\n\t\t\t//CONSIDER: use OleInitialize instead of t.TrySetApartmentState(state).\n\t\t\t//\tSomehow RegisterDragDrop in UacDragDrop fails if ThisThreadSetComApartment_.\n\t\t\t//\tBut RDD in SciCode works with this.\n\t\t\t\n\t\t\t//This is undocumented, but works if we set ApartmentState.Unknown at first.\n\t\t\t//With [STAThread] slower, and the process initially used to have +2 threads.\n\t\t\t//Speed when called to set STA at startup: 1.7 ms. If apphost calls OleInitialize, 1.5 ms.\n\t\t\t//tested: OleUninitialize in apphost does not make GetApartmentState return MTA.\n\t\t}\n\t\t\n\t\t#endregion\n\t}\n}\n\nnamespace Au.Types {\n\t/// <summary>\n\t/// Contains process name (like <c>\"notepad.exe\"</c>), id and user session id.\n\t/// </summary>\n\tpublic record struct ProcessInfo(string Name, int Id, int SessionId);\n\t//use record to auto-implement ==, eg for code like var a=process.allProcesses(); 5.s(); print.it(process.allProcesses().Except(a));\n\t\n\t/// <summary>\n\t/// Contains process trigger info retrieved by <see cref=\"process.triggers\"/>.\n\t/// </summary>\n\tpublic record class ProcessTriggerInfo(bool Started, string Name, int Id, int SessionId);\n}\n"
  },
  {
    "path": "Au/System/run.cs",
    "content": "using Microsoft.Win32.SafeHandles;\n\nnamespace Au {\n\t/// <summary>\n\t/// Execute or open programs, files, folders, web pages, etc, start new threads.\n\t/// </summary>\n\tpublic static class run {\n\t\t/// <summary>\n\t\t/// Runs/opens a program, document, directory (folder), URL, new email, etc.\n\t\t/// </summary>\n\t\t/// <returns>Process info (id etc).</returns>\n\t\t/// <param name=\"file\">\n\t\t/// Examples:\n\t\t/// <br/>• <c>@\"C:\\file.txt\"</c>\n\t\t/// <br/>• <c>folders.Documents</c>\n\t\t/// <br/>• <c>folders.System + \"notepad.exe\"</c>\n\t\t/// <br/>• <c>@\"%folders.System%\\notepad.exe\"</c>\n\t\t/// <br/>• <c>@\"%TMP%\\file.txt\"</c>\n\t\t/// <br/>• <c>\"notepad.exe\"</c>\n\t\t/// <br/>• <c>@\"..\\folder\\x.exe\"</c>\n\t\t/// <br/>• <c>\"http://a.b.c/d\"</c>\n\t\t/// <br/>• <c>\"file:///path\"</c>\n\t\t/// <br/>• <c>\"mailto:a@b.c\"</c>\n\t\t/// <br/>• <c>\":: ITEMIDLIST\"</c>\n\t\t/// <br/>• <c>@\"shell:::{CLSID}\"</c>\n\t\t/// <br/>• <c>@\"shell:AppsFolder\\Microsoft.WindowsCalculator_8wekyb3d8bbwe!App\"</c>.\n\t\t/// </param>\n\t\t/// <param name=\"args\">\n\t\t/// Command line arguments.\n\t\t/// This function expands environment variables if starts with <c>\"%\"</c> or <c>\"\\\"%\"</c>.\n\t\t/// </param>\n\t\t/// <param name=\"flags\"></param>\n\t\t/// <param name=\"dirEtc\">\n\t\t/// Allows to specify more parameters: current directory, verb, etc.\n\t\t/// If string, it sets initial current directory for the new process. If <c>\"\"</c>, gets it from <i>file</i>. More info: <see cref=\"ROptions.CurrentDirectory\"/>.\n\t\t/// </param>\n\t\t/// <exception cref=\"ArgumentException\">Used both <see cref=\"ROptions.Verb\"/> and <see cref=\"RFlags.Admin\"/> and this process isn't admin.</exception>\n\t\t/// <exception cref=\"AuException\">Failed. For example, the file does not exist.</exception>\n\t\t/// <remarks>\n\t\t/// It works like when you double-click a file icon. It may start new process or not. For example it may just activate window if the program is already running.\n\t\t/// Uses API <ms>ShellExecuteEx</ms>.\n\t\t/// Similar to <see cref=\"Process.Start(string, string)\"/>.\n\t\t/// \n\t\t/// The <i>file</i> parameter can be:\n\t\t/// - Full path of a file or directory. Examples: <c>@\"C:\\file.txt\"</c>, <c>folders.Documents</c>, <c>folders.System + \"notepad.exe\"</c>, <c>@\"%folders.System%\\notepad.exe\"</c>.\n\t\t/// - Filename of a file or directory, like <c>\"notepad.exe\"</c>. The function calls <see cref=\"filesystem.searchPath\"/>.\n\t\t/// - Path relative to <see cref=\"folders.ThisApp\"/>. Examples: <c>\"x.exe\"</c>, <c>@\"subfolder\\x.exe\"</c>, <c>@\".\\subfolder\\x.exe\"</c>, <c>@\"..\\another folder\\x.exe\"</c>.\n\t\t/// - URL. Examples: <c>\"https://www.example.com\"</c>, <c>\"file:///path\"</c>.\n\t\t/// - Email, like <c>\"mailto:a@b.c\"</c>. Subject, body etc also can be specified, and Google knows how.\n\t\t/// - Shell object's <c>ITEMIDLIST</c> like <c>\":: ITEMIDLIST\"</c>. See <see cref=\"Pidl.ToHexString\"/>, <see cref=\"folders.shell\"/>. Can be used to open virtual folders and items like Control Panel.\n\t\t/// - Shell object's parsing name, like <c>@\"shell:::{CLSID}\"</c> or <c>@\"::{CLSID}\"</c>. See <see cref=\"Pidl.ToShellString\"/>. Can be used to open virtual folders and items like Control Panel.\n\t\t/// - To run a Windows Store App, use <c>@\"shell:AppsFolder\\WinStoreAppId\"</c> format. Example: <c>@\"shell:AppsFolder\\Microsoft.WindowsCalculator_8wekyb3d8bbwe!App\"</c>. To discover the string use hotkey <c>Ctrl+Shift+Q</c> or function <see cref=\"WndUtil.GetWindowsStoreAppId\"/> or Google.\n\t\t/// - To open a Windows Settings page can be used <google>ms-settings</google>, like <c>\"ms-settings:display\"</c>. To open Settings use <c>\"ms-settings:\"</c>.\n\t\t/// \n\t\t/// Supports environment variables, like <c>@\"%TMP%\\file.txt\"</c>. See <see cref=\"pathname.expand\"/>.\n\t\t/// \n\t\t/// By default the new process isn't admin even if this process is admin. It's a unique feature of this function. More info: <see cref=\"RFlags\"/>.\n\t\t/// \n\t\t/// The new process inherits environment variables of this process only if both processes are admin or non-admin. To ensure it, use flag <see cref=\"RFlags.InheritAdmin\"/> or some other \"start process\" function (<see cref=\"Process.Start\"/>, <see cref=\"run.console\"/>, Windows API).\n\t\t/// </remarks>\n\t\t/// <seealso cref=\"wnd.find\"/>\n\t\t/// <seealso cref=\"wnd.findOrRun\"/>\n\t\t/// <seealso cref=\"wnd.runAndFind\"/>\n\t\t/// <example>\n\t\t/// Run Notepad and wait for an active Notepad window.\n\t\t/// <code><![CDATA[\n\t\t/// run.it(\"notepad.exe\");\n\t\t/// 1.s();\n\t\t/// wnd w = wnd.wait(10, true, \"*- Notepad\", \"Notepad\");\n\t\t/// ]]></code>\n\t\t/// Run Notepad or activate a Notepad window.\n\t\t/// <code><![CDATA[\n\t\t/// wnd w = wnd.findOrRun(\"*- Notepad\", run: () => run.it(\"notepad.exe\"));\n\t\t/// ]]></code>\n\t\t/// Run File Explorer and wait for new folder window. Ignores matching windows that already existed.\n\t\t/// <code><![CDATA[\n\t\t/// var w = wnd.runAndFind(\n\t\t/// \t() => run.it(@\"explorer.exe\"),\n\t\t/// \t10, cn: \"CabinetWClass\");\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic static RResult it(string file, string args = null, RFlags flags = 0, ROptions dirEtc = null) {\n\t\t\tApi.SHELLEXECUTEINFO x = default;\n\t\t\tx.cbSize = Api.SizeOf(x);\n\t\t\tx.fMask = Api.SEE_MASK_NOZONECHECKS | Api.SEE_MASK_NOASYNC | Api.SEE_MASK_CONNECTNETDRV | Api.SEE_MASK_UNICODE;\n\t\t\tx.nShow = Api.SW_SHOWNORMAL;\n\t\t\t\n\t\t\tbool curDirFromFile = false;\n\t\t\tvar more = dirEtc;\n\t\t\tif (more != null) {\n\t\t\t\tx.lpVerb = more.Verb;\n\t\t\t\tif (x.lpVerb != null) x.fMask |= Api.SEE_MASK_INVOKEIDLIST; //makes slower. But verbs are rarely used.\n\t\t\t\t\n\t\t\t\tif (more.CurrentDirectory is string cd) {\n\t\t\t\t\tif (cd.Length == 0) curDirFromFile = true; else cd = pathname.expand(cd);\n\t\t\t\t\tx.lpDirectory = cd;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (!more.OwnerWindow.IsEmpty) x.hwnd = more.OwnerWindow.Hwnd.Window;\n\t\t\t\t\n\t\t\t\tswitch (more.WindowState) {\n\t\t\t\tcase ProcessWindowStyle.Hidden: x.nShow = Api.SW_HIDE; break;\n\t\t\t\tcase ProcessWindowStyle.Minimized: x.nShow = Api.SW_SHOWMINIMIZED; break;\n\t\t\t\tcase ProcessWindowStyle.Maximized: x.nShow = Api.SW_SHOWMAXIMIZED; break;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tx.fMask &= ~more.FlagsRemove;\n\t\t\t\tx.fMask |= more.FlagsAdd;\n\t\t\t}\n\t\t\t\n\t\t\tif (flags.Has(RFlags.Admin)) {\n\t\t\t\tif (x.lpVerb == null || x.lpVerb.Eqi(\"runas\")) x.lpVerb = \"runas\";\n\t\t\t\telse if (!uacInfo.isAdmin) throw new ArgumentException(\"Cannot use Verb with flag Admin, unless this process is admin\");\n\t\t\t}\n\t\t\t\n\t\t\tfile = NormalizeFile_(false, file, out bool isFullPath, out bool isShellPath);\n\t\t\tPidl pidl = null;\n\t\t\tif (isShellPath) { //\":: ITEMIDLIST\" or \"::{CLSID}...\" (we convert it too because the API does not support many)\n\t\t\t\tpidl = Pidl.FromString(file); //does not throw\n\t\t\t\tif (pidl != null) {\n\t\t\t\t\tx.lpIDList = pidl.UnsafePtr;\n\t\t\t\t\tx.fMask |= Api.SEE_MASK_INVOKEIDLIST;\n\t\t\t\t} else x.lpFile = file;\n\t\t\t} else {\n\t\t\t\tx.lpFile = file;\n\t\t\t\t\n\t\t\t\tif (curDirFromFile && isFullPath) x.lpDirectory = pathname.getDirectory(file);\n\t\t\t}\n\t\t\tx.lpDirectory ??= Directory.GetCurrentDirectory();\n\t\t\tif (!args.NE()) x.lpParameters = pathname.expand(args);\n\t\t\t\n\t\t\tif (0 == (flags & RFlags.ShowErrorUI)) x.fMask |= Api.SEE_MASK_FLAG_NO_UI;\n\t\t\tif (0 == (flags & RFlags.WaitForExit)) x.fMask |= Api.SEE_MASK_NO_CONSOLE;\n\t\t\tif (0 != (flags & RFlags.MostUsed)) x.fMask |= Api.SEE_MASK_FLAG_LOG_USAGE;\n\t\t\tx.fMask |= Api.SEE_MASK_NOCLOSEPROCESS;\n\t\t\t\n\t\t\tWndUtil.EnableActivate(-1);\n\t\t\t\n\t\t\tbool waitForExit = 0 != (flags & RFlags.WaitForExit);\n\t\t\tbool needHandle = flags.Has(RFlags.NeedProcessHandle);\n\t\t\t\n\t\t\tbool ok = false; int pid = 0, errorCode = 0;\n\t\t\tbool asUser = !flags.HasAny(RFlags.Admin | RFlags.InheritAdmin) && uacInfo.ofThisProcess.Elevation == UacElevation.Full; //info: new process does not inherit uiAccess\n\t\t\tif (asUser) {\n\t\t\t\tok = Cpp.Cpp_ShellExec(x, out pid, out int injectError, out int execError);\n\t\t\t\tif (!ok) {\n\t\t\t\t\tif (injectError != 0) {\n\t\t\t\t\t\tprint.warning(\"Failed to run as non-admin.\");\n\t\t\t\t\t\t//once in TT process started to always fail. More info in UnmarshalAgentIAccessible().\n\t\t\t\t\t\tasUser = false;\n\t\t\t\t\t} else errorCode = execError;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!asUser) {\n\t\t\t\tok = Api.ShellExecuteEx(ref x);\n\t\t\t\tif (!ok) errorCode = lastError.code;\n\t\t\t}\n\t\t\tpidl?.Dispose();\n\t\t\tif (!ok) throw new AuException(errorCode, $\"*run '{file}'\");\n\t\t\t\n\t\t\tvar R = new RResult();\n\t\t\tWaitHandle_ ph = null;\n\t\t\t\n\t\t\tif (needHandle || waitForExit) {\n\t\t\t\tif (pid != 0) x.hProcess = Handle_.OpenProcess(pid, Api.PROCESS_ALL_ACCESS);\n\t\t\t\tif (!x.hProcess.Is0) ph = new WaitHandle_(x.hProcess, true);\n\t\t\t}\n\t\t\t\n\t\t\tif (!waitForExit) {\n\t\t\t\tif (pid != 0) R.ProcessId = pid;\n\t\t\t\telse if (!x.hProcess.Is0) R.ProcessId = process.processIdFromHandle(x.hProcess);\n\t\t\t}\n\t\t\t\n\t\t\ttry {\n\t\t\t\tApi.AllowSetForegroundWindow();\n\t\t\t\t\n\t\t\t\tif (x.lpVerb != null && Thread.CurrentThread.GetApartmentState() == ApartmentState.STA)\n\t\t\t\t\tThread.CurrentThread.Join(50); //need min 5-10 for file Properties. And not Sleep.\n\t\t\t\t\n\t\t\t\tif (ph != null) {\n\t\t\t\t\tif (waitForExit) {\n\t\t\t\t\t\tph.WaitOne();\n\t\t\t\t\t\tif (Api.GetExitCodeProcess(x.hProcess, out var exitCode)) R.ProcessExitCode = exitCode;\n\t\t\t\t\t}\n\t\t\t\t\tif (needHandle) R.ProcessHandle = ph;\n\t\t\t\t}\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tif (R.ProcessHandle == null) {\n\t\t\t\t\tif (ph != null) ph.Dispose();\n\t\t\t\t\telse x.hProcess.Dispose();\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\treturn R;\n\t\t\t\n\t\t\t//tested: works well in MTA thread.\n\t\t\t//rejected: in QM2, run also has a 'window' parameter. However it just makes limited, unclear etc, and therefore rarely used. Instead use wnd.findOrRun etc like in the examples.\n\t\t\t//rejected: in QM2, run also has 'autodelay'. Better don't add such hidden things. Let the script decide what to do.\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Calls <see cref=\"it\"/> and handles exceptions.\n\t\t/// If <see cref=\"it\"/> throws exception, writes it to the output as warning and returns <c>null</c>.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// This function is useful when you don't care whether <see cref=\"it\"/> succeeded and don't want to use try/catch.\n\t\t/// Handles only exception of type <see cref=\"AuException\"/>. It is thrown when fails, usually when the file does not exist.\n\t\t/// </remarks>\n\t\t/// <seealso cref=\"print.warning\"/>\n\t\t/// <seealso cref=\"OWarnings.Disable\"/>\n\t\t/// <seealso cref=\"wnd.findOrRun\"/>\n\t\t/// <inheritdoc cref=\"it\" path=\"/param\"/>\n\t\t[MethodImpl(MethodImplOptions.NoInlining)] //uses stack\n\t\tpublic static RResult itSafe(string file, string args = null, RFlags flags = 0, ROptions dirEtc = null) {\n\t\t\ttry {\n\t\t\t\treturn it(file, args, flags, dirEtc);\n\t\t\t}\n\t\t\tcatch (AuException e) {\n\t\t\t\tprint.warning(e);\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t\t\n\t\tinternal static string NormalizeFile_(bool runConsole, string file, out bool isFullPath, out bool isShellPath) {\n\t\t\tisShellPath = isFullPath = false;\n\t\t\tfile = pathname.expand(file);\n\t\t\tif (file.NE()) throw new ArgumentException();\n\t\t\tif (runConsole || !(isShellPath = pathname.IsShellPath_(file))) {\n\t\t\t\tif (isFullPath = pathname.isFullPath(file)) {\n\t\t\t\t\tvar fl = runConsole ? PNFlags.DontExpandDosPath : PNFlags.DontExpandDosPath | PNFlags.DontPrefixLongPath;\n\t\t\t\t\tfile = pathname.Normalize_(file, fl, true);\n\t\t\t\t\t\n\t\t\t\t\t//ShellExecuteEx supports long path prefix for exe but not for documents.\n\t\t\t\t\t//Process.Start supports long path prefix, except when the exe is .NET.\n\t\t\t\t\tif (!runConsole) file = pathname.unprefixLongPath(file);\n\t\t\t\t\t\n\t\t\t\t\tif (FileSystemRedirection.IsSystem64PathIn32BitProcess(file) && !filesystem.exists(file)) {\n\t\t\t\t\t\tfile = FileSystemRedirection.GetNonRedirectedSystemPath(file);\n\t\t\t\t\t}\n\t\t\t\t} else if (!pathname.isUrl(file)) {\n\t\t\t\t\t//ShellExecuteEx searches everywhere except in app folder.\n\t\t\t\t\t//Process.Start prefers current directory.\n\t\t\t\t\tvar s2 = filesystem.searchPath(file);\n\t\t\t\t\tif (s2 != null) {\n\t\t\t\t\t\tfile = s2;\n\t\t\t\t\t\tisFullPath = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn file;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Runs a console program in hidden mode, waits until its process ends, and prints its output text.\n\t\t/// Writes text lines to the output in real time.\n\t\t/// </summary>\n\t\t/// <param name=\"exe\">\n\t\t/// Path or name of an <c>.exe</c> or <c>.bat</c> file. Can be:\n\t\t/// <br/>• Full path. Examples: <c>@\"C:\\folder\\x.exe\"</c>, <c>folders.System + \"x.exe\"</c>, <c>@\"%folders.System%\\x.exe\"</c>.\n\t\t/// <br/>• Filename, like <c>\"x.exe\"</c>. This function calls <see cref=\"filesystem.searchPath\"/>.\n\t\t/// <br/>• Path relative to <see cref=\"folders.ThisApp\"/>. Examples: <c>\"x.exe\"</c>, <c>@\"subfolder\\x.exe\"</c>, <c>@\".\\subfolder\\x.exe\"</c>, <c>@\"..\\folder\\x.exe\"</c>.\n\t\t/// \n\t\t/// <br/>Supports environment variables, like <c>@\"%TMP%\\x.bat\"</c>. See <see cref=\"pathname.expand\"/>.\n\t\t/// </param>\n\t\t/// <param name=\"args\"><c>null</c> or command line arguments.</param>\n\t\t/// <param name=\"curDir\">\n\t\t/// Initial current directory of the new process.\n\t\t/// <br/>• If <c>null</c>, uses <c>Directory.GetCurrentDirectory()</c>.\n\t\t/// <br/>• Else if <c>\"\"</c>, calls <c>pathname.getDirectory(exe)</c>.\n\t\t/// <br/>• Else calls <see cref=\"pathname.expand\"/>.\n\t\t/// </param>\n\t\t/// <param name=\"encoding\">\n\t\t/// Console's text encoding.\n\t\t/// If <c>null</c> (default), uses <see cref=\"Encoding.UTF8\"/>. If you get garbage text, try <see cref=\"Console.OutputEncoding\"/> or <see cref=\"Encoding.Unicode\"/>.\n\t\t/// </param>\n\t\t/// <returns>The process exit code. Usually a non-0 value means error.</returns>\n\t\t/// <exception cref=\"AuException\">Failed, for example file not found.</exception>\n\t\t/// <remarks>\n\t\t/// The console window is hidden. The text that would be displayed in it is redirected to this function.\n\t\t/// \n\t\t/// Console programs have two output text streams - standard output and standard error. This function gets both. Alternatively use <see cref=\"Process.Start\"/>; it gets the output and error streams separately, and some lines may be received in incorrect order in time.\n\t\t/// </remarks>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// string v = \"example\";\n\t\t/// run.console(@\"C:\\Test\\console.exe\", $@\"/an \"\"{v}\"\" /etc\");\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic static int console(string exe, string args = null, string curDir = null, Encoding encoding = null) {\n\t\t\treturn _RunConsole(print.it, out _, exe, args, curDir, encoding, true);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Runs a console program in hidden mode, waits until its process ends, and gets its output text.\n\t\t/// </summary>\n\t\t/// <param name=\"output\">A variable that receives the output text.</param>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// run.console(out var text, @\"C:\\Test\\console.exe\", encoding: Console.OutputEncoding);\n\t\t/// print.it(text);\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\t/// <inheritdoc cref=\"console(string, string, string, Encoding)\"/>\n\t\tpublic static int console(out string output, string exe, string args = null, string curDir = null, Encoding encoding = null) {\n\t\t\tvar r = _RunConsole(null, out output, exe, args, curDir, encoding, false);\n\t\t\treturn r;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Runs a console program in hidden mode, waits until its process ends, and gets its output text.\n\t\t/// Uses a callback function that receives text lines in real time.\n\t\t/// </summary>\n\t\t/// <param name=\"output\">\n\t\t/// Callback function that receives the output text.\n\t\t/// Unless <i>rawText</i> <c>true</c>:\n\t\t/// <br/>• it isn't called until is retrieved full line with line break characters;\n\t\t/// <br/>• it receives single full line at a time, without line break characters.\n\t\t/// </param>\n\t\t/// <param name=\"rawText\">Call the callback function whenever text is retrieved (don't wait for full line). Pass raw text, in chunks of any size.</param>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// run.console(s => print.it(s), @\"C:\\Test\\console.exe\");\n\t\t///\n\t\t/// run.console(s => { print.it($\"<><_>{s}</_><nonl>\"); }, @\"C:\\Test\\console.exe\", rawText: true);\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\t/// <seealso cref=\"consoleProcess\"/>\n\t\t/// <inheritdoc cref=\"console(string, string, string, Encoding)\"/>\n\t\tpublic static int console(Action<string> output, string exe, string args = null, string curDir = null, Encoding encoding = null, bool rawText = false) {\n\t\t\treturn _RunConsole(output, out _, exe, args, curDir, encoding, !rawText);\n\t\t}\n\t\t\n\t\tstatic unsafe int _RunConsole(Action<string> outAction, out string outStr, string exe, string args, string curDir, Encoding encoding, bool needLines) {\n\t\t\toutStr = null;\n\t\t\tusing var c = new consoleProcess(exe, args, curDir) { Encoding = encoding };\n\t\t\tif (needLines) {\n\t\t\t\twhile (c.ReadLine(out var s)) outAction(s);\n\t\t\t} else if (outAction != null) {\n\t\t\t\tc.ReadAllText(outAction);\n\t\t\t} else {\n\t\t\t\toutStr = c.ReadAllText();\n\t\t\t}\n\t\t\treturn c.ExitCode;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Opens parent folder in File Explorer (folder window) and selects the file.\n\t\t/// </summary>\n\t\t/// <returns><c>false</c> if failed, for example if the file does not exist.</returns>\n\t\t/// <param name=\"path\">\n\t\t/// Full path of a file or directory or other shell object.\n\t\t/// Supports <c>@\"%environmentVariable%\\...\"</c> (see <see cref=\"pathname.expand\"/>) and <c>\"::...\"</c> (see <see cref=\"Pidl.ToHexString\"/>).\n\t\t/// </param>\n\t\tpublic static bool selectInExplorer(string path) {\n\t\t\tusing var pidl = Pidl.FromString(path);\n\t\t\tif (pidl == null) return false;\n\t\t\treturn 0 == Api.SHOpenFolderAndSelectItems(pidl.HandleRef, 0, null, 0);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Starts new thread: creates new <see cref=\"Thread\"/> object, sets some properties and calls <see cref=\"Thread.Start\"/>.\n\t\t/// </summary>\n\t\t/// <returns>The <c>Thread</c> variable.</returns>\n\t\t/// <param name=\"threadProc\">Thread procedure. Parameter <i>start</i> of <see cref=\"Thread\"/> constructor.</param>\n\t\t/// <param name=\"background\">\n\t\t/// If <c>true</c> (default), sets <see cref=\"Thread.IsBackground\"/> = <c>true</c>.\n\t\t/// The process ends when the main thread and all foreground threads end; background threads then are terminated.\n\t\t/// </param>\n\t\t/// <param name=\"sta\">If <c>true</c> (default), sets <see cref=\"ApartmentState.STA\"/>.</param>\n\t\t/// <exception cref=\"OutOfMemoryException\"></exception>\n\t\tpublic static Thread thread(Action threadProc, bool background = true, bool sta = true) {\n\t\t\tvar t = new Thread(threadProc.Invoke);\n\t\t\tif (background) t.IsBackground = true;\n\t\t\tif (sta) t.SetApartmentState(ApartmentState.STA);\n\t\t\tt.Start();\n\t\t\treturn t;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Starts new thread like <see cref=\"thread(Action, bool, bool)\"/> and gets thread handle and native id.\n\t\t/// </summary>\n\t\t/// <param name=\"id\">Native thread id.</param>\n\t\t/// <param name=\"thread\"><c>Thread</c> object.</param>\n\t\t/// <param name=\"init\">Called in the new thread before <i>threadProc</i>. This function (<c>run.thread</c>) waits until it returns.</param>\n\t\t/// <returns>Thread handle. Don't forget to dispose.</returns>\n\t\t/// <inheritdoc cref=\"thread(Action, bool, bool)\"/>\n\t\tpublic static unsafe SafeWaitHandle thread(out int id, out Thread thread, Action threadProc, bool background = true, bool sta = true, Action init = null) {\n\t\t\tSafeWaitHandle h = null; int i = 0;\n\t\t\tusing var ev = Api.CreateEvent(false);\n\t\t\tthread = new Thread(() => {\n\t\t\t\tinit?.Invoke();\n\t\t\t\th = new(Api.OpenThread(Api.THREAD_ALL_ACCESS, false, i = Api.GetCurrentThreadId()), ownsHandle: true);\n\t\t\t\tApi.SetEvent(ev);\n\t\t\t\tthreadProc();\n\t\t\t});\n\t\t\tif (background) thread.IsBackground = true;\n\t\t\tif (sta) thread.SetApartmentState(ApartmentState.STA);\n\t\t\tthread.Start();\n\t\t\tApi.WaitForSingleObject(ev, -1);\n\t\t\tid = i;\n\t\t\treturn h;\n\t\t\t\n\t\t\t//Almost same speed as other overload when JITed, but first time several times slower, eg 1 -> 2.5 ms.\n\t\t\t//\tWith CreateThread faster, but it cannot be used in a public function (then some .NET features work differently).\n\t\t}\n\t}\n}\n\nnamespace Au.Types {\n\t/// <summary>\n\t/// Flags for <see cref=\"run.it\"/>.\n\t/// </summary>\n\t[Flags]\n\tpublic enum RFlags {\n\t\t/// <summary>\n\t\t/// Show error message box if fails, for example if file not found.\n\t\t/// Note: this does not disable exceptions. To avoid exceptions use try/catch or <see cref=\"run.itSafe\"/>.\n\t\t/// </summary>\n\t\tShowErrorUI = 1,\n\t\t\n\t\t/// <summary>\n\t\t/// If started new process, wait until it exits.\n\t\t/// </summary>\n\t\tWaitForExit = 2,\n\t\t\n\t\t/// <summary>\n\t\t/// If started new process, get process handle (<see cref=\"RResult.ProcessHandle\"/>).\n\t\t/// </summary>\n\t\tNeedProcessHandle = 4,\n\t\t\n\t\t/// <summary>\n\t\t/// Run new process as administrator.\n\t\t/// <br/>If this process isn't admin:\n\t\t/// <br/>• Shows UAC consent dialog.\n\t\t/// <br/>• Uses verb <c>\"runas\"</c>, therefore other verb cannot be specified.\n\t\t/// <br/>• Cannot set current directory for the new process.\n\t\t/// <br/>• The new process does not inherit environment variables of this process.\n\t\t/// </summary>\n\t\tAdmin = 8,\n\t\t\n\t\t/// <summary>\n\t\t/// If this process runs as administrator, run new process as administrator too.\n\t\t/// <br/>Without this flag, if this process runs as administrator:\n\t\t/// <br/>• Starts new process as non-administrator from the shell process (<c>explorer.exe</c>).\n\t\t/// <br/>• If it fails (for example if shell process isn't running), calls <see cref=\"print.warning\"/> and starts new process as administrator.\n\t\t/// <br/>• The new process does not inherit environment variables of this process.\n\t\t/// </summary>\n\t\tInheritAdmin = 16,\n\t\t\n\t\t/// <summary>\n\t\t/// Add the app to the \"Most used\" list in the Start menu if launched often.\n\t\t/// </summary>\n\t\tMostUsed = 32,\n\t}\n\t\n\t/// <summary>\n\t/// More parameters for <see cref=\"run.it\"/>.\n\t/// </summary>\n\t/// <remarks>\n\t/// Implicit conversion from <c>string</c> sets <see cref=\"CurrentDirectory\"/>.\n\t/// </remarks>\n\tpublic class ROptions {\n\t\t/// <summary>\n\t\t/// Sets <see cref=\"CurrentDirectory\"/>.\n\t\t/// </summary>\n\t\tpublic static implicit operator ROptions(string curDir) => new ROptions { CurrentDirectory = curDir };\n\t\t\n\t\t/// <summary>\n\t\t/// Initial current directory for the new process.\n\t\t/// If <c>null</c> (default), the new process will inherit the current directory of this process.\n\t\t/// If <c>\"\"</c>, the function gets parent directory path from the <i>file</i> parameter, if possible (if full path is specified or found); if not possible, same as <c>null</c>.\n\t\t/// </summary>\n\t\tpublic string CurrentDirectory;\n\t\t\n\t\t/// <summary>\n\t\t/// File's right-click menu command, also known as verb. For example <c>\"edit\"</c>, <c>\"print\"</c>, <c>\"properties\"</c>.\n\t\t/// The default verb is bold in the menu.\n\t\t/// Not all menu items will work. Some may have different name than in the menu.\n\t\t/// </summary>\n\t\tpublic string Verb;\n\t\t\n\t\t/// <summary>\n\t\t/// Owner window for error message boxes.\n\t\t/// Also, new window should be opened on the same screen, but many programs ignore it.\n\t\t/// </summary>\n\t\tpublic AnyWnd OwnerWindow;\n\t\t\n\t\t/// <summary>\n\t\t/// Preferred window state.\n\t\t/// Many programs ignore it.\n\t\t/// </summary>\n\t\tpublic ProcessWindowStyle WindowState;\n\t\t\n\t\t/// <summary>\n\t\t/// Flags to add to <ms>SHELLEXECUTEINFO</ms> field <c>fMask</c>.\n\t\t/// Default flags: <c>SEE_MASK_NOZONECHECKS</c>, <c>SEE_MASK_NOASYNC</c>, <c>SEE_MASK_NOCLOSEPROCESS</c>, <c>SEE_MASK_CONNECTNETDRV</c>, <c>SEE_MASK_UNICODE</c>, <c>SEE_MASK_FLAG_NO_UI</c> (if no flag <c>ShowErrorUI</c>), <c>SEE_MASK_NO_CONSOLE</c> (if no flag <c>WaitForExit</c>), <c>SEE_MASK_FLAG_LOG_USAGE</c> (if flag <c>MostUsed</c>); also <c>SEE_MASK_INVOKEIDLIST</c> if need.\n\t\t/// </summary>\n\t\tpublic uint FlagsAdd;\n\t\t\n\t\t/// <summary>\n\t\t/// Flags to remove from <ms>SHELLEXECUTEINFO</ms> field <c>fMask</c>.\n\t\t/// Default flags: see <see cref=\"FlagsAdd\"/>.\n\t\t/// </summary>\n\t\tpublic uint FlagsRemove;\n\t\t\n\t\t//no. If need, caller can get window and call EnsureInScreen etc.\n\t\t//public screen Screen;\n\t\t//this either does not work or I could not find a program that uses default window position (does not save/restore)\n\t\t//if(!more.Screen.IsNull) { x._14.hMonitor = more.Screen.ToDevice().Handle; x.fMask |= Api.SEE_MASK_HMONITOR; }\n\t}\n\t\n\t/// <summary>\n\t/// Results of <see cref=\"run.it\"/>.\n\t/// </summary>\n\tpublic class RResult {\n\t\t/// <summary>\n\t\t/// The exit code of the process.\n\t\t/// </summary>\n\t\t/// <value>0 if no flag <c>WaitForExit</c> or if cannot wait.</value>\n\t\t/// <remarks>\n\t\t/// Usually the exit code is 0 or a process-defined error code.\n\t\t/// </remarks>\n\t\tpublic int ProcessExitCode { get; internal set; }\n\t\t\n\t\t/// <summary>\n\t\t/// The process id.\n\t\t/// </summary>\n\t\t/// <value>0 if used flag <c>WaitForExit</c> or if did not start new process (eg opened the document in an existing process) or if cannot get it.</value>\n\t\tpublic int ProcessId { get; internal set; }\n\t\t\n\t\t/// <summary>\n\t\t/// If used flag <c>NeedProcessHandle</c>, contains process handle. Later the <see cref=\"WaitHandle\"/> variable must be disposed.\n\t\t/// </summary>\n\t\t/// <value><c>null</c> if no flag or if did not start new process (eg opened the document in an existing process) or if cannot get it.</value>\n\t\t/// <example>\n\t\t/// This code does the same as <c>run.it(@\"notepad.exe\", flags: SRFlags.WaitForExit);</c>\n\t\t/// <code><![CDATA[\n\t\t/// var r = run.it(@\"notepad.exe\", flags: SRFlags.NeedProcessHandle);\n\t\t/// using(var h = r.ProcessHandle) h?.WaitOne();\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic WaitHandle ProcessHandle { get; internal set; }\n\t\t\n\t\t/// <summary>\n\t\t/// Returns <see cref=\"ProcessId\"/> as string.\n\t\t/// </summary>\n\t\tpublic override string ToString() => ProcessId.ToString();\n\t}\n}\n"
  },
  {
    "path": "Au/System/sound.cs",
    "content": "using Microsoft.Win32;\n\nnamespace Au {\n\t/// <summary>\n\t/// Plays short sounds and speaks text.\n\t/// </summary>\n\tpublic static class sound {\n\t\t/// <summary>\n\t\t/// Gets or sets the sound volume of this program. Percent 0-100 of the master volume.\n\t\t/// </summary>\n\t\t/// <exception cref=\"ArgumentException\"></exception>\n\t\t/// <remarks>\n\t\t/// Used for speech and <c>.wav</c> files, but not for system sounds.\n\t\t/// Sets volume for each program seperately. The program remembers it after restarting. Note: all scripts with role <i>miniProgram</i> (default) run in the same program (<c>Au.Task.exe</c>).\n\t\t/// </remarks>\n\t\tpublic static int volume {\n\t\t\tget {\n\t\t\t\tApi.waveOutGetVolume(default, out var v);\n\t\t\t\tv &= 0xffff;\n\t\t\t\treturn (int)Math.Round((double)v * 100 / 0xffff);\n\t\t\t}\n\t\t\tset {\n\t\t\t\tuint v = (uint)value;\n\t\t\t\tif (v > 100) throw new ArgumentException(\"Must be 0-100.\");\n\t\t\t\tv = (uint)(0xffff * v / 100);\n\t\t\t\tApi.waveOutSetVolume(default, v << 16 | v);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Plays a custom sound (<c>.wav</c> file).\n\t\t/// </summary>\n\t\t/// <param name=\"wavFile\"><c>.wav</c> file.</param>\n\t\t/// <param name=\"async\">Don't wait until the sound ends. Note: the sound ends when this process exits.</param>\n\t\t/// <param name=\"system\">Use the sound volume channel \"System Sounds\". Then <see cref=\"volume\"/> isn't used.</param>\n\t\tpublic static bool playWav(string wavFile, bool async = false, bool system = false) {\n\t\t\tvar s = wavFile.NE() ? null : pathname.expand(wavFile);\n\t\t\tvar f = Api.SND_FILENAME | Api.SND_NODEFAULT;\n\t\t\tif (async) f |= Api.SND_ASYNC;\n\t\t\tif (system) f |= Api.SND_SYSTEM;\n\t\t\treturn Api.PlaySound(s, default, f);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Plays a system event sound.\n\t\t/// </summary>\n\t\t/// <param name=\"name\">Sound event name. If <c>null</c>, displays all available names.</param>\n\t\t/// <param name=\"async\">Don't wait until the sound ends. Note: the sound ends when this process exits.</param>\n\t\t/// <param name=\"system\">Use the sound volume channel \"System Sounds\". Then <see cref=\"volume\"/> isn't used.</param>\n\t\t/// <param name=\"orDefault\">Play default sound if the specified sound not found or does not have a <c>.wav</c> file assigned.</param>\n\t\t/// <remarks>\n\t\t/// Sounds can be changed in the Control Panel's Sound dialog.\n\t\t/// </remarks>\n\t\tpublic static bool playEvent(string name, bool async = false, bool system = false, bool orDefault = false) {\n\t\t\tif (name == null) {\n\t\t\t\tusing var k1 = Registry.CurrentUser.OpenSubKey(@\"AppEvents\\Schemes\\Apps\\.Default\");\n\t\t\t\tforeach (var s in k1.GetSubKeyNames()) {\n\t\t\t\t\tusing var k2 = k1.OpenSubKey(s + @\"\\.Current\");\n\t\t\t\t\tif (k2?.GetValue(\"\") is string file && file.Length > 0) {\n\t\t\t\t\t\tvar label = Registry.GetValue(@\"HKEY_CURRENT_USER\\AppEvents\\EventLabels\\\" + s, \"\", null) as string;\n\t\t\t\t\t\tprint.it($\"{s,-30}   {label,-30}   {file}\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t} else {\n\t\t\t\tuint f = Api.SND_ALIAS;\n\t\t\t\t//f |= Api.SND_APPLICATION; //doesn't work. Plays only sounds from the \".Default\" key, with or without this flag.\n\t\t\t\tif (!orDefault) f |= Api.SND_NODEFAULT;\n\t\t\t\tif (async) f |= Api.SND_ASYNC;\n\t\t\t\tif (system) f |= Api.SND_SYSTEM;\n\t\t\t\treturn Api.PlaySound(name, default, f);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Plays the system default sound.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Does not wait until the sound ends. The sound can continue even when this process ends.\n\t\t/// </remarks>\n\t\tpublic static void playDefault() {\n\t\t\tApi.MessageBeep(0x40);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Plays the system error sound.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Does not wait until the sound ends. The sound can continue even when this process ends.\n\t\t/// </remarks>\n\t\tpublic static void playError() {\n\t\t\tApi.MessageBeep(0x10);\n\t\t}\n\n\t\t//other system sounds now are silent or same as default.\n\n\t\t/// <summary>\n\t\t/// Generates sound of specified frequency and duration. Waits until it ends.\n\t\t/// </summary>\n\t\t/// <param name=\"freq\">Frequency, 37-32767 hertz.</param>\n\t\t/// <param name=\"duration\">Duration, in milliseconds.</param>\n\t\t/// <param name=\"async\">Don't wait. Note: the sound ends when this process exits.</param>\n\t\tpublic static void beep(int freq, int duration, bool async = false) {\n\t\t\tif (async) {\n\t\t\t\tTask.Run(() => Api.Beep(freq, duration));\n\t\t\t} else {\n\t\t\t\tApi.Beep(freq, duration);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Speaks text.\n\t\t/// </summary>\n\t\t/// <param name=\"text\">Text to speak. If <c>null</c>, stops speaking.</param>\n\t\t/// <param name=\"async\">Don't wait. Note: the sound ends when this process exits.</param>\n\t\t/// <param name=\"voice\">\n\t\t/// A voice name from <b>Control Panel > Speech > Text to speech</b>. Can be partial, case-insensitive. Example: <c>\"Zira\"</c>.\n\t\t/// If <c>null</c>, uses default voice.\n\t\t/// Voice attributes can be specified using string format <c>\"voice|reqAttr\"</c> or <c>\"voice|reqAttr|optAttr\"</c>. Here <i>reqAttr</i> and <i>optAttr</i> are arguments for <google>ISpObjectTokenCategory.EnumTokens</google>. Each part can be empty. Example: <c>\"|Gender=Female\"</c>.\n\t\t/// </param>\n\t\t/// <param name=\"rate\">Speed adjustment, +- 10.</param>\n\t\t/// <param name=\"volume\">Volume, 0-100. See also <see cref=\"volume\"/>.</param>\n\t\t/// <seealso cref=\"SpeakVoice\"/>\n#if true\n\t\tpublic static void speak(string text, bool async = false, string voice = null, int rate = 0, int volume = 100) {\n\t\t\tlock (s_lock) {\n\t\t\t\ts_voice?.Stop();\n\t\t\t\tif (text.NE()) return;\n\n\t\t\t\ts_voice ??= new SpeakVoice();\n\t\t\t\tif (voice != s_sVoice) s_voice.SetVoice_(s_sVoice = voice);\n\t\t\t\ts_voice.Rate = rate;\n\t\t\t\ts_voice.Volume = volume;\n\t\t\t}\n\t\t\ts_voice.Speak(text, async);\n\t\t}\n\t\tstatic string s_sVoice;\n#else //use new SpVoice each time\n\tpublic static void speak(string text, bool async = false, string voice = null, int rate = 0, int volume = 100) {\n\t\tSpeakVoice v = null;\n\t\tlock (s_lock) {\n\t\t\tif (s_voice!=null) {\n\t\t\t\ts_voice.Dispose();\n\t\t\t\ts_voice=null;\n\t\t\t}\n\t\t\tif (text.NE()) return;\n\t\t\t\n\t\t\tv = new SpeakVoice(voice);\n\t\t\tif (rate != 0) v.Rate = rate;\n\t\t\tif (volume != 100) v.Volume = volume;\n\t\t\t\n\t\t\ts_voice=v;\n\t\t}\n\t\tv.Speak(text, async);\n\t\tif(!async) {\n\t\t\tlock (s_lock) { if (s_voice==v) { s_voice=null; v.Dispose(); } }\n\t\t}\n\t\t//else v.EndStream+=(o, e) => print.it(\"end\"); //need to process messages\n\t}\n#endif\n\t\tstatic SpeakVoice s_voice;\n\t\tstatic readonly object s_lock = new();\n\t}\n}\n\nnamespace Au.More {\n\t/// <summary>\n\t/// Speaks text.\n\t/// </summary>\n\t/// <seealso cref=\"sound.speak\"/>\n\tpublic class SpeakVoice : IDisposable {\n\t\tSAPI.ISpVoice _v;\n\t\t\n\t\t/// <summary>\n\t\t/// Creates a text-to-speech (speech synthesis) voice instance.\n\t\t/// </summary>\n\t\t/// <param name=\"voice\">A voice name from <b>Control Panel > Speech > Text to speech</b>. Can be partial, case-insensitive. Example: <c>\"Zira\"</c>. If <c>null</c>, uses default voice.</param>\n\t\tpublic SpeakVoice(string voice = null) {\n\t\t\t_v = new SAPI.SpVoice() as SAPI.ISpVoice;\n\t\t\tGC.AddMemoryPressure(250_000);\n\t\t\tif (voice != null) SetVoice_(voice);\n\t\t}\n\t\t\n\t\tinternal void SetVoice_(string voice) {\n\t\t\tif (!voice.NE()) {\n\t\t\t\tvar cat = new SAPI.SpObjectTokenCategory() as SAPI.ISpObjectTokenCategory;\n\t\t\t\tcat.SetId(SAPI.SPCAT_VOICES, false);\n\t\t\t\tvar a = voice.Split('|');\n\t\t\t\tvoice = a[0];\n\t\t\t\tvar et = cat.EnumTokens(a.Length > 1 && !a[1].NE() ? a[1] : null, a.Length > 2 && !a[2].NE() ? a[2] : null);\n\t\t\t\tfor (int i = 0, n = et.GetCount(); i < n; i++) {\n\t\t\t\t\tvar v = et.Item(i);\n\t\t\t\t\tif (!voice.NE()) {\n\t\t\t\t\t\tif (0 != v.OpenKey(\"Attributes\", out var k)) continue;\n\t\t\t\t\t\tif (0 != k.GetStringValue(\"Name\", out var s)) continue;\n\t\t\t\t\t\tif (s.Find(voice, true) < 0) continue;\n\t\t\t\t\t}\n\t\t\t\t\t_v.SetVoice(v);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t} else _v.SetVoice(null);\n\t\t}\n\t\t\n\t\t///\n\t\tprotected virtual void Dispose(bool disposing) {\n\t\t\tif (_v != null) {\n\t\t\t\tif (disposing) Marshal.ReleaseComObject(_v); //stops speaking if async\n\t\t\t\t_v = null;\n\t\t\t\tGC.RemoveMemoryPressure(250_000);\n\t\t\t}\n\t\t}\n\t\t\n\t\t///\n\t\tpublic void Dispose() {\n\t\t\tStop();\n\t\t\tDispose(true);\n\t\t\tGC.SuppressFinalize(this);\n\t\t}\n\t\t\n\t\t///\n\t\t~SpeakVoice() {\n\t\t\t//print.it(\"~\");\n\t\t\tDispose(false);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets or sets the speed adjustment, +- 10.\n\t\t/// </summary>\n\t\tpublic int Rate {\n\t\t\tget => _v.GetRate();\n\t\t\tset { _v.SetRate(value); }\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets or sets the volume, 0-100. See also <see cref=\"sound.volume\"/>.\n\t\t/// </summary>\n\t\tpublic int Volume {\n\t\t\tget => _v.GetVolume();\n\t\t\tset { _v.SetVolume((ushort)value); }\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Pauses speaking.\n\t\t/// </summary>\n\t\tpublic void Pause() => _v.Pause();\n\t\t\n\t\t/// <summary>\n\t\t/// Resumes speaking.\n\t\t/// </summary>\n\t\tpublic void Resume() => _v.Resume();\n\t\t\n\t\t/// <summary>\n\t\t/// Skips <i>count</i> milliseconds of speech.\n\t\t/// </summary>\n\t\t/// <param name=\"count\">Forward if positive, else backward.</param>\n\t\tpublic void SkipMilliseconds(int count) => _v.Skip(\"MILLISECOND\", count);\n\t\t\n\t\t/// <summary>\n\t\t/// Skips <i>count</i> sentences of speech.\n\t\t/// </summary>\n\t\t/// <param name=\"count\">Forward if positive, else backward. If 0, repeats current sentence.</param>\n\t\tpublic void SkipSentence(int count) => _v.Skip(\"SENTENCE\", count);\n\t\t\n\t\t/// <summary>\n\t\t/// Stops speaking.\n\t\t/// </summary>\n\t\tpublic void Stop() => SkipSentence(int.MaxValue);\n\t\t\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if currently is speaking. Returns <c>false</c> if finished or not started.\n\t\t/// </summary>\n\t\tpublic bool IsSpeaking => _RunningState() == SAPI.SpeechRunState.SRSEIsSpeaking;\n\t\t\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if finished speaking.\n\t\t/// </summary>\n\t\tpublic bool IsDone => _RunningState() == SAPI.SpeechRunState.SRSEDone;\n\t\t\n\t\tSAPI.SpeechRunState _RunningState() {\n\t\t\t_v.GetStatus(out var r);\n\t\t\treturn r.RunningState;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Waits until the async speech ends.\n\t\t/// </summary>\n\t\t/// <param name=\"msTimeout\">Timeout milliseconds, or -1.</param>\n\t\tpublic bool WaitUntilDone(int msTimeout) => 0 == _v.WaitUntilDone(msTimeout);\n\t\t\n\t\t/// <summary>\n\t\t/// Speaks the specified text.\n\t\t/// </summary>\n\t\t/// <param name=\"text\">Text to speak.</param>\n\t\t/// <param name=\"async\">Don't wait. Note: the sound ends when this process exits.</param>\n\t\tpublic void Speak(string text, bool async = false) {\n\t\t\tSpeak(text, async ? SVFlags.ASYNC : 0);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Speaks the specified text.\n\t\t/// </summary>\n\t\t/// <param name=\"text\">Text to speak.</param>\n\t\t/// <param name=\"flags\"></param>\n\t\tpublic void Speak(string text, SVFlags flags) {\n\t\t\tif (flags.Has(SVFlags.IS_FILENAME)) text = pathname.expand(text);\n\t\t\t_v.Speak(text, (uint)flags);\n\t\t\tGC.KeepAlive(this);\n\t\t\tif (flags.Has(SVFlags.ASYNC)) { //protect from GC while speaking\n\t\t\t\tTask.Run(() => { _v.WaitUntilDone(-1); GC.KeepAlive(this); });\n\t\t\t}\n\t\t}\n\t}\n\t\n\t//Easier would be to use the SAPI type library, but it creates problems, eg dotnet pack fails.\n\tunsafe class SAPI : NativeApi {\n\t\t[ComImport, Guid(\"6C44DF74-72B9-4992-A1EC-EF996E0422D4\"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\n\t\tinternal interface ISpVoice {\n\t\t\tvoid _0();\n\t\t\tvoid _1();\n\t\t\tvoid _2();\n\t\t\tvoid _3();\n\t\t\tvoid _4();\n\t\t\tvoid _5();\n\t\t\tvoid _6();\n\t\t\tvoid _7();\n\t\t\tvoid _8();\n\t\t\tvoid _9();\n\t\t\tvoid _a();\n\t\t\tvoid _b();\n\t\t\tvoid _c();\n\t\t\tvoid Pause();\n\t\t\tvoid Resume();\n\t\t\tvoid SetVoice(ISpObjectToken pToken);\n\t\t\tvoid _d();\n\t\t\tint Speak([MarshalAs(UnmanagedType.LPWStr)] string pwcs, uint dwFlags);\n\t\t\tvoid _e();\n\t\t\tvoid GetStatus(out SPVOICESTATUS pStatus, nint ppszLastBookmark = 0);\n\t\t\tint Skip([MarshalAs(UnmanagedType.LPWStr)] string pItemType, int lNumItems);\n\t\t\tvoid _f();\n\t\t\tvoid _g();\n\t\t\tvoid _h();\n\t\t\tvoid _i();\n\t\t\tvoid SetRate(int RateAdjust);\n\t\t\tint GetRate();\n\t\t\tvoid SetVolume(ushort usVolume);\n\t\t\tushort GetVolume();\n\t\t\t[PreserveSig] int WaitUntilDone(int msTimeout);\n\t\t}\n\t\t\n\t\tinternal struct SPVOICESTATUS {\n\t\t\tfixed uint _1[3];\n\t\t\tpublic SpeechRunState RunningState;\n\t\t\tfixed uint _2[9];\n\t\t}\n\t\t\n\t\t[ComImport, Guid(\"14056589-E16C-11D2-BB90-00C04F8EE6C0\"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\n\t\tinternal interface ISpObjectToken {\n\t\t\tvoid _0();\n\t\t\tvoid _1();\n\t\t\tvoid _2();\n\t\t\tvoid _3();\n\t\t\tvoid _4();\n\t\t\tvoid _5();\n\t\t\t[PreserveSig] int OpenKey([MarshalAs(UnmanagedType.LPWStr)] string pszSubKeyName, out ISpDataKey ppSubKey);\n\t\t}\n\t\t\n\t\t[ComImport, Guid(\"2D3D3845-39AF-4850-BBF9-40B49780011D\"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\n\t\tinternal interface ISpObjectTokenCategory {\n\t\t\tvoid _0();\n\t\t\tvoid _1();\n\t\t\tvoid _2();\n\t\t\tvoid _3();\n\t\t\tvoid _4();\n\t\t\tvoid _5();\n\t\t\tvoid _6();\n\t\t\tvoid _7();\n\t\t\tvoid _8();\n\t\t\tvoid _9();\n\t\t\tvoid _a();\n\t\t\tvoid _b();\n\t\t\tvoid SetId([MarshalAs(UnmanagedType.LPWStr)] string pszCategoryId, [MarshalAs(UnmanagedType.Bool)] bool fCreateIfNotExist);\n\t\t\tvoid _c();\n\t\t\tvoid _d();\n\t\t\tIEnumSpObjectTokens EnumTokens([MarshalAs(UnmanagedType.LPWStr)] string pzsReqAttribs, [MarshalAs(UnmanagedType.LPWStr)] string pszOptAttribs);\n\t\t}\n\t\t\n\t\t[ComImport, Guid(\"14056581-E16C-11D2-BB90-00C04F8EE6C0\"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\n\t\tinternal interface ISpDataKey {\n\t\t\tvoid _0();\n\t\t\tvoid _1();\n\t\t\tvoid _2();\n\t\t\t[PreserveSig] int GetStringValue([MarshalAs(UnmanagedType.LPWStr)] string pszValueName, [MarshalAs(UnmanagedType.LPWStr)] out string s);\n\t\t}\n\t\t\n\t\t[ComImport, Guid(\"06B64F9E-7FDA-11D2-B4F2-00C04F797396\"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\n\t\tinternal interface IEnumSpObjectTokens {\n\t\t\tvoid _0();\n\t\t\tvoid _1();\n\t\t\tvoid _2();\n\t\t\tvoid _3();\n\t\t\tISpObjectToken Item(int Index);\n\t\t\tint GetCount();\n\t\t}\n\t\t\n\t\t[ComImport, Guid(\"96749377-3391-11D2-9EE3-00C04F797396\"), ClassInterface(ClassInterfaceType.None)]\n\t\tinternal class SpVoice { }\n\t\t\n\t\tinternal enum SpeechRunState {\n\t\t\tSRSEDone = 1,\n\t\t\tSRSEIsSpeaking\n\t\t}\n\t\t\n\t\t[ComImport, Guid(\"A910187F-0C7A-45AC-92CC-59EDAFB77B53\"), ClassInterface(ClassInterfaceType.None)]\n\t\tinternal class SpObjectTokenCategory { }\n\t\t\n\t\tinternal const string SPCAT_VOICES = \"HKEY_LOCAL_MACHINE\\\\SOFTWARE\\\\Microsoft\\\\Speech\\\\Voices\";\n\t}\n}\n\nnamespace Au.Types {\n\t/// <summary>\n\t/// Flags for <see cref=\"sound.speak\"/>. See <ms>SPEAKFLAGS</ms>.\n\t/// </summary>\n\t[Flags]\n\tpublic enum SVFlags {\n#pragma warning disable 1591 //XML doc\n\t\tASYNC = 0x0001,\n\t\tPURGEBEFORESPEAK = 0x0002,\n\t\tIS_FILENAME = 0x0004,\n\t\tIS_XML = 0x0008,\n\t\tIS_NOT_XML = 0x0010,\n\t\tPERSIST_XML = 0x0020,\n\t\tNLP_SPEAK_PUNC = 0x0040,\n\t\tPARSE_SAPI = 0x0080,\n\t\tPARSE_SSML = 0x0100,\n#pragma warning restore\n\t}\n}\n"
  },
  {
    "path": "Au/System/uacInfo.cs",
    "content": "namespace Au {\n\t/// <summary>\n\t/// Gets [](xref:uac) integrity level and other security info of this and other processes.\n\t/// </summary>\n\t/// <remarks>\n\t/// An <c>uacInfo</c> variable contains a process access token handle that is used to get security info. Always dispose <c>uacInfo</c> variables to close the handle.\n\t/// </remarks>\n\tpublic sealed class uacInfo : IDisposable {\n\t\t///\n\t\t~uacInfo() => _htoken.Dispose();\n\n\t\t///\n\t\tpublic void Dispose() {\n\t\t\t_htoken.Dispose();\n\t\t\tGC.SuppressFinalize(this);\n\t\t}\n\n\t\tHandle_ _htoken;\n\t\tHandleRef _HtokenHR => new HandleRef(this, _htoken);\n\n\t\t/// <summary>\n\t\t/// The access token handle.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// The handle is managed by this variable and will be closed when disposing or GC-collecting it. Use <see cref=\"GC.KeepAlive\"/> where need.\n\t\t/// </remarks>\n\t\tpublic IntPtr UnsafeTokenHandle => _htoken;\n\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if the last called property function failed.\n\t\t/// Normally it should never fail. Only <see cref=\"ofProcess\"/> can fail (then it returns <c>null</c>).\n\t\t/// </summary>\n\t\tpublic bool Failed { get; private set; }\n\n\t\t/// <summary>\n\t\t/// Gets the [](xref:uac) elevation type of the process.\n\t\t/// </summary>\n\t\tpublic UacElevation Elevation {\n\t\t\tget {\n\t\t\t\tif (_haveElevation == 0) {\n\t\t\t\t\tunsafe {\n\t\t\t\t\t\tUacElevation elev;\n\t\t\t\t\t\tif (!Api.GetTokenInformation(_HtokenHR, Api.TOKEN_INFORMATION_CLASS.TokenElevationType, &elev, 4, out _)) _haveElevation = 2;\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t_haveElevation = 1;\n\t\t\t\t\t\t\t_Elevation = elev;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (Failed = (_haveElevation == 2)) return UacElevation.Unknown;\n\t\t\t\treturn _Elevation;\n\t\t\t}\n\t\t}\n\t\tUacElevation _Elevation; byte _haveElevation;\n\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if the process has [](xref:uac) uiAccess property.\n\t\t/// A uiAccess process can access/automate all windows of processes running in the same user session.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Most processes don't have this property. They cannot access/automate windows of higher integrity level (High, System, uiAccess) processes and Windows 8 store apps. For example, cannot send keys and Windows messages.\n\t\t/// Note: High IL (admin) processes also can have this property, therefore <c>IsUIAccess</c> is not the same as <c>IntegrityLevel==IL.UIAccess</c> (<see cref=\"IntegrityLevel\"/> returns <c>UIAccess</c> only for Medium+uiAccess processes; for High+uiAccess processes it returns <c>High</c>). Some Windows API work slightly differently with uiAccess and non-uiAccess admin processes.\n\t\t/// This property is rarely useful. Instead use other properties of this class.\n\t\t/// </remarks>\n\t\tpublic bool IsUIAccess {\n\t\t\tget {\n\t\t\t\tif (_haveIsUIAccess == 0) {\n\t\t\t\t\tunsafe {\n\t\t\t\t\t\tuint uia;\n\t\t\t\t\t\tif (!Api.GetTokenInformation(_HtokenHR, Api.TOKEN_INFORMATION_CLASS.TokenUIAccess, &uia, 4, out var siz)) _haveIsUIAccess = 2;\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t_haveIsUIAccess = 1;\n\t\t\t\t\t\t\t_isUIAccess = uia != 0;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (Failed = (_haveIsUIAccess == 2)) return false;\n\t\t\t\treturn _isUIAccess;\n\t\t\t}\n\t\t}\n\t\tbool _isUIAccess; byte _haveIsUIAccess;\n\n\t\t//not very useful. Returns false for ApplicationFrameWindow. Can use wnd.IsWindows10StoreApp.\n\t\t///// <summary>\n\t\t///// Returns <c>true</c> if the process is a Windows Store app.\n\t\t///// </summary>\n\t\t//public unsafe bool IsAppContainer\n\t\t//{\n\t\t//\tget\n\t\t//\t{\n\t\t//\t\tif(!osVersion.minWin8) return false;\n\t\t//\t\tuint isac;\n\t\t//\t\tif(Failed = !Api.GetTokenInformation(_HtokenHR, Api.TOKEN_INFORMATION_CLASS.TokenIsAppContainer, &isac, 4, out var siz)) return false;\n\t\t//\t\treturn isac != 0;\n\t\t//\t}\n\t\t//}\n\n\t\tstatic class _Api {\n#pragma warning disable 649\n\t\t\tinternal struct TOKEN_MANDATORY_LABEL { public IntPtr Sid; public uint Attributes; }\n#pragma warning restore 649\n\t\t\tinternal const uint SECURITY_MANDATORY_UNTRUSTED_RID = 0x00000000;\n\t\t\tinternal const uint SECURITY_MANDATORY_LOW_RID = 0x00001000;\n\t\t\tinternal const uint SECURITY_MANDATORY_MEDIUM_RID = 0x00002000;\n\t\t\tinternal const uint SECURITY_MANDATORY_MEDIUM_PLUS_RID = SECURITY_MANDATORY_MEDIUM_RID + 0x100;\n\t\t\tinternal const uint SECURITY_MANDATORY_HIGH_RID = 0x00003000;\n\t\t\tinternal const uint SECURITY_MANDATORY_SYSTEM_RID = 0x00004000;\n\t\t\tinternal const uint SECURITY_MANDATORY_PROTECTED_PROCESS_RID = 0x00005000;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets the [](xref:uac) integrity level (IL) of the process.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// IL from lowest to highest value: <c>Untrusted</c>, <c>Low</c>, <c>Medium</c>, <c>UIAccess</c>, <c>High</c>, <c>System</c>, <c>Protected</c>, <c>Unknown</c>.\n\t\t/// The IL enum member values can be used like <c>if(x.IntegrityLevel > IL.Medium) ...</c> .\n\t\t/// If UAC is turned off, most non-service processes on administrator account have High IL; on non-administrator - Medium.\n\t\t/// </remarks>\n\t\tpublic UacIL IntegrityLevel => _GetIntegrityLevel();\n\t\tUacIL _GetIntegrityLevel() {\n\t\t\tif (_haveIntegrityLevel == 0) {\n\t\t\t\tunsafe {\n\t\t\t\t\tApi.GetTokenInformation(_HtokenHR, Api.TOKEN_INFORMATION_CLASS.TokenIntegrityLevel, null, 0, out var siz);\n\t\t\t\t\tif (lastError.code != Api.ERROR_INSUFFICIENT_BUFFER) _haveIntegrityLevel = 2;\n\t\t\t\t\telse {\n\t\t\t\t\t\tvar b = stackalloc byte[(int)siz];\n\t\t\t\t\t\tvar tml = (_Api.TOKEN_MANDATORY_LABEL*)b;\n\t\t\t\t\t\tif (!Api.GetTokenInformation(_HtokenHR, Api.TOKEN_INFORMATION_CLASS.TokenIntegrityLevel, tml, siz, out siz)) _haveIntegrityLevel = 2;\n\t\t\t\t\t\tuint x = *Api.GetSidSubAuthority(tml->Sid, (uint)(*Api.GetSidSubAuthorityCount(tml->Sid) - 1));\n\n\t\t\t\t\t\tif (x < _Api.SECURITY_MANDATORY_LOW_RID) _integrityLevel = UacIL.Untrusted;\n\t\t\t\t\t\telse if (x < _Api.SECURITY_MANDATORY_MEDIUM_RID) _integrityLevel = UacIL.Low;\n\t\t\t\t\t\telse if (x < _Api.SECURITY_MANDATORY_HIGH_RID) _integrityLevel = UacIL.Medium;\n\t\t\t\t\t\telse if (x < _Api.SECURITY_MANDATORY_SYSTEM_RID) {\n\t\t\t\t\t\t\tif (IsUIAccess && Elevation != UacElevation.Full) _integrityLevel = UacIL.UIAccess; //fast. Note: don't use if(andUIAccess) here.\n\t\t\t\t\t\t\telse _integrityLevel = UacIL.High;\n\t\t\t\t\t\t} else if (x < _Api.SECURITY_MANDATORY_PROTECTED_PROCESS_RID) _integrityLevel = UacIL.System;\n\t\t\t\t\t\telse _integrityLevel = UacIL.Protected;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (Failed = (_haveIntegrityLevel == 2)) return UacIL.Unknown;\n\t\t\treturn _integrityLevel;\n\t\t}\n\t\tUacIL _integrityLevel; byte _haveIntegrityLevel;\n\n\t\tuacInfo(Handle_ hToken) => _htoken = hToken;\n\n\t\tstatic uacInfo _Create(IntPtr hProcess) {\n\t\t\tif (!Api.OpenProcessToken(hProcess, Api.TOKEN_QUERY | Api.TOKEN_QUERY_SOURCE, out Handle_ hToken)) return null;\n\t\t\treturn new uacInfo(hToken);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Opens process access token and creates/returns new <see cref=\"uacInfo\"/> variable that holds it. Then you can use its properties.\n\t\t/// </summary>\n\t\t/// <returns><c>null</c> if failed. For example fails for services and some other processes if current process is not administrator.</returns>\n\t\t/// <param name=\"processId\">Process id. If you have a window, use <see cref=\"wnd.ProcessId\"/>.</param>\n\t\t/// <remarks>\n\t\t/// To get <see cref=\"uacInfo\"/> of this process, use <see cref=\"ofThisProcess\"/>.\n\t\t/// </remarks>\n\t\tpublic static uacInfo ofProcess(int processId) {\n\t\t\tif (processId == 0) return null;\n\t\t\tusing var hp = Handle_.OpenProcess(processId);\n\t\t\tif (hp.Is0) return null;\n\t\t\treturn _Create(hp);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets <see cref=\"uacInfo\"/> variable for this process.\n\t\t/// </summary>\n\t\tpublic static uacInfo ofThisProcess {\n\t\t\tget {\n\t\t\t\tif (s_thisProcess == null) {\n\t\t\t\t\ts_thisProcess = _Create(Api.GetCurrentProcess());\n\t\t\t\t\tDebug.Assert(s_thisProcess != null);\n\t\t\t\t}\n\t\t\t\treturn s_thisProcess;\n\t\t\t}\n\t\t}\n\t\tstatic uacInfo s_thisProcess;\n\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if this process is running as administrator.\n\t\t/// </summary>\n#if true\n\t\tpublic static bool isAdmin => s_isAdmin ??= Api.IsUserAnAdmin();\n#else //too slow, eg 15 ms vs 1 ms\n\t\tpublic static bool IsAdmin {\n\t\t\tget {\n\t\t\t\tif(!s_isAdmin.HasValue) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tWindowsIdentity id = WindowsIdentity.GetCurrent();\n\t\t\t\t\t\tWindowsPrincipal principal = new WindowsPrincipal(id);\n\t\t\t\t\t\ts_isAdmin = principal.IsInRole(WindowsBuiltInRole.Administrator);\n\t\t\t\t\t}\n\t\t\t\t\tcatch { }\n\t\t\t\t}\n\t\t\t\treturn s_isAdmin.GetValueOrDefault();\n\t\t\t}\n\t\t}\n#endif\n\t\tstatic bool? s_isAdmin;\n\n\t\t/*\n\t\tpublic struct SID_IDENTIFIER_AUTHORITY\n\t\t{\n\t\t\tpublic byte b0, b1, b2, b3, b4, b5;\n\t\t}\n\n\t\t[DllImport(\"advapi32.dll\")]\n\t\tinternal static extern bool AllocateAndInitializeSid(in SID_IDENTIFIER_AUTHORITY pIdentifierAuthority, byte nSubAuthorityCount, uint nSubAuthority0, uint nSubAuthority1, uint nSubAuthority2, uint nSubAuthority3, uint nSubAuthority4, uint nSubAuthority5, uint nSubAuthority6, uint nSubAuthority7, out IntPtr pSid);\n\n\t\t[DllImport(\"advapi32.dll\")]\n\t\tinternal static extern bool CheckTokenMembership(IntPtr TokenHandle, IntPtr SidToCheck, out bool IsMember);\n\n\t\t[DllImport(\"advapi32.dll\")]\n\t\tinternal static extern IntPtr FreeSid(IntPtr pSid);\n\n\t\tpublic const int SECURITY_BUILTIN_DOMAIN_RID = 32;\n\t\tpublic const int DOMAIN_ALIAS_RID_ADMINS = 544;\n\n\t\t//This is from CheckTokenMembership reference.\n\t\t//In QM2 it is very fast, but here quite slow first time, although then becomes the fastest. Advapi32.dll is already loaded, but maybe it loads other dlls.\n\t\t//IsUserAnAdmin first time can be slowest. It loads shell32.dll.\n\t\t//The .NET principal etc first time usually is fastest, althoug later is slower several times. (old info)\n\t\t//All 3 tested on admin and user accounts, also when UAC is turned off, also with System IL.\n\t\tpublic static bool isAdmin\n\t\t{\n\t\t\tget\n\t\t\t{\n\t\t\t\tvar NtAuthority = new SID_IDENTIFIER_AUTHORITY() { b5 = 5 }; //SECURITY_NT_AUTHORITY\n\t\t\t\tIntPtr AdministratorsGroup;\n\t\t\t\tif(!AllocateAndInitializeSid(NtAuthority, 2,\n\t\t\t\t\tSECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS,\n\t\t\t\t\t0, 0, 0, 0, 0, 0,\n\t\t\t\t\tout AdministratorsGroup\n\t\t\t\t\t))\n\t\t\t\t\treturn false;\n\t\t\t\tbool _r;\n\t\t\t\tif(!CheckTokenMembership(default, AdministratorsGroup, out _r)) _r = false;\n\t\t\t\tFreeSid(AdministratorsGroup);\n\t\t\t\treturn _r;\n\t\t\t}\n\t\t}\n\t\t*/\n\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if [](xref:uac) is disabled (turned off) completely (not just disabled UAC consent screen/dialog).\n\t\t/// </summary>\n\t\tpublic static bool isUacDisabled {\n\t\t\tget {\n\t\t\t\tif (!_haveIsUacDisabled) {\n\t\t\t\t\t_isUacDisabled = _IsUacDisabled();\n\t\t\t\t\t_haveIsUacDisabled = true;\n\t\t\t\t}\n\t\t\t\treturn _isUacDisabled;\n\t\t\t}\n\t\t}\n\n\t\tstatic bool _isUacDisabled, _haveIsUacDisabled;\n\t\tstatic bool _IsUacDisabled() {\n\t\t\t//if(osVersion.minWin8) return false; //UAC cannot be disabled so easily, but can\n\t\t\tuacInfo x = ofThisProcess;\n\t\t\tswitch (x.Elevation) {\n\t\t\tcase UacElevation.Full:\n\t\t\tcase UacElevation.Limited:\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t//if(x.IsUIAccess) return false; //uiAccess in non-admin user session. Rare.\n\n\t\t\tint r = 1;\n\t\t\ttry {\n\t\t\t\tif (!Api.GetDelegate(out Api.CheckElevationEnabled d, \"kernel32.dll\", \"CheckElevationEnabled\") || 0 != d(out r)) {\n\t\t\t\t\tDebug_.Print(\"CheckElevationEnabled\");\n\t\t\t\t\tr = Microsoft.Win32.Registry.GetValue(@\"HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System\", \"EnableLUA\", null) is int v ? v : 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception e) { Debug_.Print(e); }\n\t\t\treturn r == 0;\n\t\t}\n\t}\n}\n\nnamespace Au.Types {\n\t/// <summary>\n\t/// UAC integrity level.\n\t/// See <see cref=\"uacInfo.IntegrityLevel\"/>.\n\t/// </summary>\n\tpublic enum UacIL {\n\t\t/// <summary>The most limited rights. Rare.</summary>\n\t\tUntrusted,\n\n\t\t/// <summary>Very limited rights. Used by web browser tab processes, Windows Store apps.</summary>\n\t\tLow,\n\n\t\t/// <summary>Limited rights. Most processes (unless UAC turned off).</summary>\n\t\tMedium,\n\n\t\t/// <summary>Medium IL + can access/automate High IL windows (user interface).</summary>\n\t\tUIAccess,\n\n\t\t/// <summary>Most rights. Processes that run as administrator.</summary>\n\t\tHigh,\n\n\t\t/// <summary>Almost all rights. Services, some system processes.</summary>\n\t\tSystem,\n\n\t\t/// <summary>Undocumented. Rare.</summary>\n\t\tProtected,\n\n\t\t/// <summary>Failed to get IL. Unlikely.</summary>\n\t\tUnknown = 100\n\t}\n\n\t/// <summary>\n\t/// <see cref=\"uacInfo.Elevation\"/>.\n\t/// </summary>\n\tpublic enum UacElevation {\n\t\t/// <summary>Failed to get. Normally it never happens.</summary>\n\t\tUnknown,\n\n\t\t/// <summary>\n\t\t/// Processes in this user session cannot be elevated.\n\t\t/// Can be: non-administrator user session (processes have limited rights); service session (processes have all rights); UAC is turned off (most processes have administrator rights).\n\t\t/// </summary>\n\t\tDefault,\n\n\t\t/// <summary>Runs as administrator (<c>High</c> or <c>System</c> integrity level, see <see cref=\"UacIL\"/>), and UAC is not turned off. Also known as \"elevated\".</summary>\n\t\tFull,\n\n\t\t/// <summary>Runs as standard user (<c>Medium</c>, <c>UIAccess</c> or <c>Low</c> integrity level, see <see cref=\"UacIL\"/>) in administrator user session (because of UAC).</summary>\n\t\tLimited\n\t}\n}\n"
  },
  {
    "path": "Au/Time/WaitLoop.cs",
    "content": "namespace Au.More {\n\t/// <summary>\n\t/// Can be used to easily implement \"wait for\" functions with a timeout.\n\t/// </summary>\n\t/// <remarks>\n\t/// See examples. The code works like most \"wait for\" functions of this library: throws exception when timed out, unless the timeout value is negative.\n\t/// Similar code is used by <see cref=\"wait.until\"/> and many other \"wait for\" functions of this library.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// public static bool WaitForMouseLeftButtonDown(Seconds timeout) {\n\t/// \tvar x = new WaitLoop(timeout);\n\t/// \tfor(; ; ) {\n\t/// \t\tif(mouse.isPressed(MButtons.Left)) return true;\n\t/// \t\tif(!x.Sleep()) return false;\n\t/// \t}\n\t/// }\n\t/// ]]></code>\n\t/// The same with <see cref=\"wait.until\"/>.\n\t/// <code><![CDATA[\n\t/// static bool WaitForMouseLeftButtonDown2(Seconds timeout) {\n\t/// \treturn wait.until(timeout, () => mouse.isPressed(MButtons.Left));\n\t/// }\n\t/// ]]></code>\n\t/// </example>\n\tpublic struct WaitLoop {\n\t\tlong _timeRemaining, _timePrev;\n\t\tbool _hasTimeout, _throw, _doEvents, _precisionIsSet;\n\t\tfloat _step;\n\t\t\n\t\t/// <summary>\n\t\t/// Sets timeout and possibly more wait parameters.\n\t\t/// </summary>\n\t\t/// <param name=\"timeout\">Timeout in seconds, like <c>3</c> or <c>0.5</c>. Or a <c>Seconds</c> variable containing timeout etc, like <c>new(3, period: 5)</c>. If timeout is 0, will wait indefinitely. If <c>></c> 0, <see cref=\"Sleep\"/> throws <see cref=\"TimeoutException\"/> when timed out. If &lt; 0, <c>Sleep</c> then returns <c>false</c> instead.</param>\n\t\tpublic WaitLoop(Seconds timeout) {\n\t\t\tPeriod = timeout.Period ?? 10;\n\t\t\t_step = Period / 10f;\n\t\t\tMaxPeriod = timeout.MaxPeriod ?? Period * 50f;\n\t\t\t_doEvents = timeout.DoEvents ?? opt.wait.DoEvents; //use opt.wait fbc\n\t\t\tCancel = timeout.Cancel;\n\t\t\t\n\t\t\tdouble t = timeout.Time;\n\t\t\tif (_hasTimeout = !(t is 0d or > 9223372036854775d or < -9223372036854775d)) { //long.MaxValue/1000 = 292_471_208 years\n\t\t\t\t_throw = t > 0 && !timeout.noException_;\n\t\t\t\t_timeRemaining = (long)(Math.Abs(t) * 1000d);\n\t\t\t\t_timePrev = computer.tickCountWithoutSleep;\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <param name=\"secondsTimeout\">Timeout in seconds. If 0, will wait indefinitely. If <c>></c> 0, <see cref=\"Sleep\"/> throws <see cref=\"TimeoutException\"/> when timed out. If &lt; 0, <c>Sleep</c> then returns <c>false</c> instead.</param>\n\t\t/// <param name=\"options\">Options. If <c>null</c>, uses <see cref=\"opt.wait\"/>.</param>\n\t\t[Obsolete, EditorBrowsable(EditorBrowsableState.Never)]\n\t\tpublic WaitLoop(double secondsTimeout, OWait options) : this(new(secondsTimeout) { Period = (options ?? opt.wait).Period, DoEvents = (options ?? opt.wait).DoEvents }) { }\n\t\t\n\t\t/// <summary>\n\t\t/// Current period (<see cref=\"Sleep\"/> sleep time). Milliseconds.\n\t\t/// Initially it is <see cref=\"Seconds.Period\"/>, or 10 ms if it was <c>null</c>. Then each <see cref=\"Sleep\"/> increments it until <see cref=\"MaxPeriod\"/>.\n\t\t/// </summary>\n\t\tpublic float Period { get; set; }\n\t\t\n\t\t/// <summary>\n\t\t/// Maximal period (<see cref=\"Sleep\"/> sleep time). Milliseconds.\n\t\t/// Initially it is <see cref=\"Seconds.MaxPeriod\"/>, or <see cref=\"Period\"/> <c>*</c> 50 if it is <c>null</c> (eg <c>10*50=500</c>).\n\t\t/// </summary>\n\t\tpublic float MaxPeriod { get; set; }\n\t\t\n\t\t/// <summary>\n\t\t/// Gets or sets the remaining time. Milliseconds.\n\t\t/// </summary>\n\t\tpublic long TimeRemaining { get => _timeRemaining; set => _timeRemaining = value; }\n\t\t\n\t\t/// <summary>\n\t\t/// Calls <see cref=\"IsTimeout\"/>. If it returns <c>true</c>, returns <c>false</c>.\n\t\t/// Else sleeps <see cref=\"Period\"/> milliseconds, increments <c>Period</c> if it is less than <see cref=\"MaxPeriod\"/>, and returns <c>true</c>.\n\t\t/// </summary>\n\t\t/// <exception cref=\"TimeoutException\">The timeout time has expired (if > 0).</exception>\n\t\tpublic unsafe bool Sleep() {\n\t\t\tif (IsTimeout()) return false;\n\t\t\tint t = (int)Period;\n\t\t\t\n\t\t\tif (t < 10 && !_precisionIsSet) { //default Period is 10\n\t\t\t\t_precisionIsSet = true;\n\t\t\t\twait.SleepPrecision_.TempSet1();\n\t\t\t}\n\t\t\t\n\t\t\tif (Cancel.CanBeCanceled && t > 100) { //t > 100: avoid creating Cancel.WaitHandle while period is small. By default will create after ~5 s.\n\t\t\t\tvar h = Cancel.WaitHandle.SafeWaitHandle.DangerousGetHandle();\n\t\t\t\tint r = wait.Wait_(t, _doEvents ? WHFlags.DoEvents : 0, new ReadOnlySpan<IntPtr>(&h, 1));\n\t\t\t\tif (r == 0) Cancel.ThrowIfCancellationRequested();\n\t\t\t\tif (r != Api.WAIT_TIMEOUT) throw new AuException(0);\n\t\t\t} else {\n\t\t\t\tCancel.ThrowIfCancellationRequested();\n\t\t\t\tif (_doEvents) {\n\t\t\t\t\twait.doEvents(t);\n\t\t\t\t} else {\n\t\t\t\t\tThread.Sleep(t);\n\t\t\t\t}\n\t\t\t\tCancel.ThrowIfCancellationRequested();\n\t\t\t}\n\t\t\t\n\t\t\tif (Period < MaxPeriod) Period += _step;\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// If the timeout time is not expired, returns <c>false</c>.\n\t\t/// Else if the timeout was negative, returns <c>true</c>.\n\t\t/// Else throws <see cref=\"TimeoutException\"/>.\n\t\t/// </summary>\n\t\t/// <exception cref=\"TimeoutException\"></exception>\n\t\tpublic bool IsTimeout() {\n\t\t\tif (!_hasTimeout) return false;\n\t\t\tvar t = computer.tickCountWithoutSleep;\n\t\t\t_timeRemaining -= t - _timePrev;\n\t\t\t_timePrev = t;\n\t\t\tif (_timeRemaining > 0) return false;\n\t\t\tif (_throw) throw new TimeoutException();\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Can be used to cancel the wait operation.\n\t\t/// </summary>\n\t\tpublic CancellationToken Cancel { get; set; }\n\t}\n}\n\nnamespace Au.Types {\n\t/// <summary>\n\t/// Used with wait functions. Contains a wait timeout in seconds, and possibly wait options.\n\t/// </summary>\n\t/// <remarks>\n\t/// Many wait functions of this library internally use <see cref=\"WaitLoop\"/>. They have a timeout parameter of <c>Seconds</c> type which allows to pass timeout and more options to <c>WaitLoop</c> in single parameter. You can pass a <c>Seconds</c> variable, like <c>new(3, period: 5)</c>. If don't need options etc, you can pass just timeout, like <c>3</c> or <c>0.5</c>.\n\t///\n\t/// Other wait functions have a timeout parameter of <c>Seconds</c> type but instead of <c>WaitLoop</c> use various hooks, events, Windows wait API, etc. They support only these <c>Seconds</c> properties: <c>Time</c>, <c>Cancel</c>, maybe <c>DoEvents</c>. Some always work like with <c>DoEvents</c> <c>true</c>.\n\t///\n\t/// More info: [](xref:wait_timeout).\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// var w = wnd.find(new Seconds(3) { Period = 5, MaxPeriod = 50 }, \"Name\");\n\t/// ]]></code>\n\t/// <code><![CDATA[\n\t/// var to = new Seconds(0) { MaxPeriod = 50 };\n\t/// wait.until(to, () => keys.isCtrl );\n\t/// wait.until(to with { Time = 30 }, () => keys.isCtrl );\n\t/// ]]></code>\n\t/// </example>\n\tpublic struct Seconds {\n\t\tfloat _time;\n\t\tint _period, _maxPeriod;\n\t\tbyte _doEvents;\n\t\t//bool _inited;\n\t\tinternal bool noException_;\n\t\t//Now Unsafe.SizeOf is 24.\n\t\t//\tDon't use bool? etc. Nullable<T> takes at least 8 bytes if the struct also has a managed-type field.\n\t\t\n\t\t/// <summary>\n\t\t/// Sets timeout.\n\t\t/// Example: <c>var w = wnd.find(new Seconds(3) { Period = 5, MaxPeriod = 50 }, \"Name\");</c>.\n\t\t/// </summary>\n\t\t/// <param name=\"time\">\n\t\t/// Timeout, in seconds.\n\t\t/// Negative value means \"don't throw exception when timed out\".\n\t\t/// Value 0 means \"wait indefinitely\" when used with <c>WaitX</c> functions; with <c>FindX</c> functions it means \"don't wait\".\n\t\t/// More info: [](xref:wait_timeout).\n\t\t/// </param>\n\t\tpublic Seconds(double time) {\n\t\t\t_time = (float)time;\n\t\t\t//_inited = true;\n\t\t}\n\t\t\n\t\t///\n\t\tpublic static implicit operator Seconds(double time) => new(time);\n\t\t\n\t\t/// <summary>\n\t\t/// Timeout, in seconds.\n\t\t/// Negative value means \"don't throw exception when timed out\".\n\t\t/// Value 0 means \"wait indefinitely\" when used with <c>WaitX</c> functions; with <c>FindX</c> functions it means \"don't wait\".\n\t\t/// More info: [](xref:wait_timeout).\n\t\t/// </summary>\n\t\tpublic double Time {\n\t\t\tget => _time;\n\t\t\tset { _time = (float)value; }\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// The sleep time between checking the wait condition periodically. Milliseconds.\n\t\t/// If <c>null</c>, will be used a value that usually is best for that wait function, in most cases 10.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Most wait functions of this library use <see cref=\"WaitLoop\"/>, which repeatedly checks the wait condition and sleeps (waits) several ms. This property sets the initial sleep time (<see cref=\"WaitLoop.Period\"/>), which then is incremented by <c>Period/10</c> ms in each loop until reaches <see cref=\"WaitLoop.MaxPeriod\"/>, which is <c>Period*50</c> by default.\n\t\t/// This property makes the response time shorter or longer. If less than the default value, makes it shorter (faster response), but increases CPU usage; if greater, makes it longer (slower response).\n\t\t/// </remarks>\n\t\tpublic int? Period {\n\t\t\tget => _period != 0 ? _period : null;\n\t\t\tset { _period = value.HasValue ? Math.Max(1, value.Value) : 0; }\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Sets <see cref=\"WaitLoop.MaxPeriod\"/>.\n\t\t/// If <c>null</c> (default), it will use <c>Period*50</c>.\n\t\t/// </summary>\n\t\tpublic int? MaxPeriod {\n\t\t\tget => _maxPeriod != 0 ? _maxPeriod : null;\n\t\t\tset { _maxPeriod = value.HasValue ? Math.Max(1, value.Value) : 0; }\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Use <see cref=\"wait.doEvents(int)\"/> instead of <see cref=\"wait.ms\"/>.\n\t\t/// If <c>null</c>, will be used <c>false</c>.\n\t\t/// </summary>\n\t\tpublic bool? DoEvents {\n\t\t\tget => _doEvents != 0 ? _doEvents == 1 : null;\n\t\t\tset { _doEvents = value switch { true => 1, false => 2, _ => 0 }; }\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Can be used to cancel the wait operation.\n\t\t/// </summary>\n\t\tpublic CancellationToken Cancel { get; set; }\n\t\t\n\t\t///// <summary>\n\t\t///// Returns <c>true</c> if ctor wasn't called.\n\t\t///// </summary>\n\t\t//internal bool IsNull_ => !_inited;\n\t\t\n\t\t/// <summary>\n\t\t/// Sets <c>noException_ = true</c> and returns <c>Time == 0d</c>.\n\t\t/// </summary>\n\t\tinternal bool Exists_() {\n\t\t\tnoException_ = true;\n\t\t\treturn _time == 0d;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// If <c>Time &lt; 0</c>, returns <c>false</c>, else throws <see cref=\"NotFoundException\"/>.\n\t\t/// </summary>\n\t\tinternal bool ReturnFalseOrThrowNotFound_() => Time < 0 ? false : throw new NotFoundException();\n\t\t//internal T ReturnOrThrowNotFound_<T>(T def) => Time < 0 ? def : throw new NotFoundException();\n\t}\n}\n"
  },
  {
    "path": "Au/Time/perf.cs",
    "content": "//#define PREPAREMETHOD //now slow anyway. Does not JIT everything.\n\nnamespace Au {\n\t/// <summary>\n\t/// Code execution time measurement with high precision (API <ms>QueryPerformanceCounter</ms>).\n\t/// </summary>\n\tpublic static unsafe class perf {\n\t\t//info: we don't use Stopwatch. It used to load System.dll (before .NET Core, now don't know), which is slow and can make speed measurement incorrect and confusing in some cases.\n\t\t\n\t\tstatic readonly double s_freqMCS = Api.QueryPerformanceFrequency(out long f) ? 1_000_000d / f : 0d, s_freqMS = s_freqMCS / 1000d;\n\t\t//note: don't use static ctor. Makes some simplest functions much slower.\n\t\t\n\t\t/// <summary>\n\t\t/// Gets the number of microseconds elapsed since Windows startup. Uses the high-resolution system timer (API <ms>QueryPerformanceCounter</ms>).\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// This function is used to measure time differences with 1 microsecond precision, like <c>var t1=perf.mcs; ... var t2=perf.mcs; var diff=t2-t1;</c>.\n\t\t/// Independent of computer clock time changes.\n\t\t/// See also: <ms>Acquiring high-resolution time stamps</ms>.\n\t\t/// </remarks>\n\t\t/// <seealso cref=\"perf\"/>\n\t\tpublic static long mcs { get { Api.QueryPerformanceCounter(out var t); return (long)(t * s_freqMCS); } }\n\t\t//On current OS and hardware QueryPerformanceCounter is reliable and fast enough.\n\t\t//Speed of 1000 calls when cold CPU: PerfMilliseconds 97, WinMillisecondsWithoutSleep 76, WinMilliseconds64/GetTickCount64 12.\n\t\t//The double cast/multiply/cast is fast. The API is much much slower. And Math.BigMul is much slower.\n\t\t//rejected: make corrections based on GetTickCount64. It makes slower and is not necessary.\n\t\t\n\t\t/// <summary>\n\t\t/// Gets the number of milliseconds elapsed since Windows startup. Uses the high-resolution system timer (API <ms>QueryPerformanceCounter</ms>).\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// This function is used to measure time differences with 1 ms precision, like <c>var t1=perf.ms; ... var t2=perf.ms; var diff=t2-t1;</c>.\n\t\t/// The return value equals <see cref=\"mcs\"/>/1000 but is slightly different than of <see cref=\"Environment.TickCount64\"/> and most Windows API. Never compare times returned by different functions.\n\t\t/// Independent of computer clock time changes.\n\t\t/// </remarks>\n\t\tpublic static long ms { get { Api.QueryPerformanceCounter(out var t); return (long)(t * s_freqMS); } }\n\t\t\n\t\t/// <summary>\n\t\t/// Performs time measurements and stores measurement data.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Static functions of <see cref=\"perf\"/> class use a single static variable of this type to perform measurements.\n\t\t/// Use a variable of this type instead when you want to have multiple independent measurements. See <see cref=\"perf.local\"/>.\n\t\t/// Variables of this type usually are used as local (in single function), but also can be used anywhere (class fields, unmanaged memory).\n\t\t/// This type is a struct (value type), and not small (184 bytes). Don't use it as a function parameter without ref. To share a variable between functions use a field in your class.\n\t\t/// Don't need to dispose variables of this type. The <see cref=\"Dispose\"/> function just calls <see cref=\"NW\"/>.\n\t\t/// </remarks>\n\t\tpublic unsafe struct Instance : IDisposable {\n\t\t\tstatic Instance() {\n\t\t\t\t//Prevent JIT delay when calling Next etc\n#if PREPAREMETHOD\n\t\t\t\tJit_.Compile(typeof(Instance), \"Next\", \"NW\", \"Dispose\");\n#if DEBUG //else these methods are inlined\n\t\t\t\tJit_.Compile(typeof(perf), \"Next\", \"NW\");\n#endif\n#else\n\t\t\t\tperf.next(); perf.nw(); perf.first(); //JIT-compiles everything we need. s_enabled prevents calling print.it etc.\n\t\t\t\ts_enabled = true;\n#endif\n\t\t\t\t\n\t\t\t\t//JIT speed: 1 ms.\n\t\t\t}\n\t\t\t\n#if !PREPAREMETHOD\n\t\t\treadonly static bool s_enabled;\n#endif\n\t\t\tconst int _nElem = 16;\n\t\t\t\n\t\t\tfixed long _a[_nElem];\n\t\t\tfixed char _aMark[_nElem];\n\t\t\tvolatile int _counter;\n\t\t\tbool _incremental;\n\t\t\tint _nMeasurements; //used with incremental to display n measurements and average times\n\t\t\tlong _time0;\n\t\t\t\n\t\t\t/// <summary>See <see cref=\"perf.incremental\"/>.</summary>\n\t\t\t/// <example>\n\t\t\t/// <code><![CDATA[\n\t\t\t/// var p1 = new perf.Instance { Incremental = true };\n\t\t\t/// for(int i = 0; i < 5; i++) {\n\t\t\t/// \tThread.Sleep(100); //not included in the measurement\n\t\t\t/// \tp1.First();\n\t\t\t/// \tThread.Sleep(30); //will make sum ~150000\n\t\t\t/// \tp1.Next();\n\t\t\t/// \tThread.Sleep(10); //will make sum ~50000\n\t\t\t/// \tp1.Next();\n\t\t\t/// \tThread.Sleep(100); //not included in the measurement\n\t\t\t/// }\n\t\t\t/// p1.Write(); //speed:  154317  51060  (205377)\n\t\t\t/// p1.Incremental = false;\n\t\t\t/// ]]></code>\n\t\t\t/// </example>\n\t\t\tpublic bool Incremental {\n\t\t\t\tget => _incremental;\n\t\t\t\tset {\n\t\t\t\t\tif (_incremental = value) {\n\t\t\t\t\t\tfixed (long* p = _a) { for (int i = 0; i < _nElem; i++) p[i] = 0; }\n\t\t\t\t\t\t_nMeasurements = 0;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t/// <summary><see cref=\"perf.first()\"/></summary>\n\t\t\tpublic void First() {\n#if !PREPAREMETHOD\n\t\t\t\tif (!s_enabled) return; //called by the static ctor\n#endif\n\t\t\t\tApi.QueryPerformanceCounter(out _time0);\n\t\t\t\t_counter = 0;\n\t\t\t\t_nMeasurements++;\n\t\t\t}\n\t\t\t\n\t\t\t//rejected. See comments in static.\n\t\t\t///// <summary>\n\t\t\t///// Calls <see cref=\"Cpu\"/> and <see cref=\"First()\"/>.\n\t\t\t///// </summary>\n\t\t\t//public void First(int timeSpeedUpCPU)\n\t\t\t//{\n\t\t\t//\tCpu(timeSpeedUpCPU);\n\t\t\t//\tFirst();\n\t\t\t//}\n\t\t\t\n\t\t\t/// <summary><see cref=\"perf.next\"/></summary>\n\t\t\tpublic void Next(char cMark = '\\0') {\n#if !PREPAREMETHOD\n\t\t\t\tif (!s_enabled) return; //called by the static ctor\n#endif\n\t\t\t\tint n = _counter; if (n >= _nElem) return;\n\t\t\t\t_counter++;\n\t\t\t\tfixed (long* p = _a) {\n\t\t\t\t\tApi.QueryPerformanceCounter(out long pc); long t = pc - _time0;\n\t\t\t\t\tif (_incremental) p[n] += t; else p[n] = t;\n\t\t\t\t\t//fixed (char* c = _aMark) c[n] = cMark;\n\t\t\t\t\tchar* c = (char*)(p + _nElem); c[n] = cMark;\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// Calls <see cref=\"Next\"/> and <see cref=\"Write\"/>.\n\t\t\t/// </summary>\n\t\t\t/// <param name=\"cMark\">A character to add to the results string like <c>\"A=150\"</c>.</param>\n\t\t\t[MethodImpl(MethodImplOptions.NoInlining)]\n\t\t\tpublic void NW(char cMark = '\\0') { Next(cMark); Write(); }\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// Calls <see cref=\"NW\"/>, which calls <see cref=\"Next\"/> and <see cref=\"Write\"/>.\n\t\t\t/// </summary>\n\t\t\t/// <remarks>\n\t\t\t/// Don't need to dispose variables of this type. This function just allows to use <c>using</c> instead of <see cref=\"NW\"/>. See example.\n\t\t\t/// \n\t\t\t/// If <see cref=\"Incremental\"/>, calls just <see cref=\"Write\"/>.\n\t\t\t/// </remarks>\n\t\t\t/// <example>\n\t\t\t/// <code><![CDATA[\n\t\t\t/// using(var p1 = perf.local()) { //p1.First();\n\t\t\t/// \t1.ms();\n\t\t\t/// \tp1.Next();\n\t\t\t/// \t1.ms();\n\t\t\t/// } //p1.NW();\n\t\t\t/// ]]></code>\n\t\t\t/// </example>\n\t\t\tpublic void Dispose() {\n\t\t\t\tif (!_incremental) Next();\n\t\t\t\tWrite();\n\t\t\t}\n\t\t\t//public void Dispose() => NW();\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// Formats a string from time values collected by calling <see cref=\"First\"/> and <see cref=\"Next\"/>, and shows it in the output.\n\t\t\t/// The string contains the number of microseconds of each code execution between calling <c>First</c> and each <c>Next</c>.\n\t\t\t/// </summary>\n\t\t\tpublic void Write() {\n#if !PREPAREMETHOD\n\t\t\t\tif (!s_enabled) return; //called by the static ctor\n#endif\n\t\t\t\tprint.it(ToString());\n\t\t\t}\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// Formats a string from time values collected by calling <see cref=\"First\"/> and <see cref=\"Next\"/>.\n\t\t\t/// The string contains the number of microseconds of each code execution between calling <c>First</c> and each <c>Next</c>.\n\t\t\t/// </summary>\n\t\t\tpublic override string ToString() {\n\t\t\t\tusing (new StringBuilder_(out var b)) {\n\t\t\t\t\tb.Append(\"speed:\");\n\t\t\t\t\t_Results(Math.Min(_counter, _nElem), b, null);\n\t\t\t\t\treturn b.ToString();\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// Return array of time values collected by calling <see cref=\"First\"/> and <see cref=\"Next\"/>.\n\t\t\t/// Each element is the number of microseconds of each code execution between calling <c>First</c> and each <c>Next</c>.\n\t\t\t/// </summary>\n\t\t\tpublic long[] ToArray() {\n\t\t\t\tint n = Math.Min(_counter, _nElem);\n\t\t\t\tvar a = new long[n];\n\t\t\t\t_Results(n, null, a);\n\t\t\t\treturn a;\n\t\t\t}\n\t\t\t\n\t\t\tvoid _Results(int n, StringBuilder b, long[] a) {\n\t\t\t\tif (n == 0) return;\n\t\t\t\tbool average = false; int nMeasurements = 1;\n\t\t\t\t\n\t\t\t\tfixed (long* p = _a) fixed (char* c = _aMark) {\n\t\t\t\t\tg1:\n\t\t\t\t\tdouble t = 0d, tPrev = 0d;\n\t\t\t\t\tfor (int i = 0; i < n; i++) {\n\t\t\t\t\t\tt = s_freqMCS * p[i];\n\t\t\t\t\t\tdouble d = t - tPrev; tPrev = t; //could add 0.5 to round up, but assume that QueryPerformanceCounter call time is 0 - 0.5.\n\t\t\t\t\t\tif (average) d /= nMeasurements;\n\t\t\t\t\t\tlong dLong = (long)d;\n\t\t\t\t\t\tif (b != null) {\n\t\t\t\t\t\t\tb.Append(\"  \");\n\t\t\t\t\t\t\tif (c[i] != '\\0') b.Append(c[i]).Append('=');\n\t\t\t\t\t\t\tb.Append(dLong.ToString());\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\ta[i] = dLong;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (b == null) return;\n\t\t\t\t\t\n\t\t\t\t\tif (n > 1) {\n\t\t\t\t\t\tif (average) t /= nMeasurements;\n\t\t\t\t\t\tb.Append(\"  (\").Append((long)t).Append(\")\");\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tif (!average && _incremental && (nMeasurements = _nMeasurements) > 1) {\n\t\t\t\t\t\taverage = true;\n\t\t\t\t\t\tb.Append(\";  measured \").Append(nMeasurements).Append(\" times, average\");\n\t\t\t\t\t\tgoto g1;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// Gets the number of microseconds between <see cref=\"First\"/> and the last <see cref=\"Next\"/>.\n\t\t\t/// </summary>\n\t\t\tpublic long TimeTotal {\n\t\t\t\tget {\n\t\t\t\t\tint n = _counter;\n\t\t\t\t\tif (n == 0) return 0;\n\t\t\t\t\tif (n > _nElem) n = _nElem;\n\t\t\t\t\tfixed (long* p = _a) { return (long)(s_freqMCS * p[n - 1]); }\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// This static variable is used by the static functions.\n\t\t/// </summary>\n\t\tstatic Instance s_static;\n\t\t//rejected: public. Needed for Serialize. Maybe in the future will be implemented somehow differently.\n\t\t\n\t\t/// <summary>\n\t\t/// Creates and returns new <see cref=\"Instance\"/> variable and calls its <see cref=\"Instance.First\"/>.\n\t\t/// </summary>\n\t\tpublic static Instance local() {\n\t\t\tvar R = new Instance();\n\t\t\tR.First();\n\t\t\treturn R;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// If <c>true</c>, times of each new <c>first next next...</c> measurement are added to previous measurement times.\n\t\t/// Finally you can call <see cref=\"write\"/> or <see cref=\"toString\"/> to get the sums.\n\t\t/// Usually used to measure code in loops. See example.\n\t\t/// </summary>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// perf.incremental = true;\n\t\t/// for(int i = 0; i < 5; i++) {\n\t\t/// \tThread.Sleep(100); //not included in the measurement\n\t\t/// \tperf.first();\n\t\t/// \tThread.Sleep(30); //will make sum ~150000\n\t\t/// \tperf.next();\n\t\t/// \tThread.Sleep(10); //will make sum ~50000\n\t\t/// \tperf.next();\n\t\t/// \tThread.Sleep(100); //not included in the measurement\n\t\t/// }\n\t\t/// perf.write(); //speed:  154317  51060  (205377)\n\t\t/// perf.incremental = false;\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic static bool incremental {\n\t\t\tget => s_static.Incremental;\n\t\t\tset => s_static.Incremental = value;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Stores current time in the first element of an internal array.\n\t\t/// </summary>\n\t\tpublic static void first() => s_static.First();\n\t\t\n\t\t//rejected. Unclear. Can confuse int timeSpeedUpCPU with char cMark.\n\t\t///// <summary>\n\t\t///// Calls <see cref=\"Cpu\"/> and <see cref=\"First()\"/>.\n\t\t///// </summary>\n\t\t//public static void first(int timeSpeedUpCPU) => s_static.First(timeSpeedUpCPU);\n\t\t\n\t\t/// <summary>\n\t\t/// Stores current time in next element of an internal array.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Don't call <c>next</c> more than 16 times after <see cref=\"first\"/>, because the array size is fixed.\n\t\t/// </remarks>\n\t\t/// <param name=\"cMark\">A character to mark this time in the results string, like <c>\"A=150\"</c>.</param>\n\t\tpublic static void next(char cMark = '\\0') => s_static.Next(cMark);\n\t\t\n\t\t/// <summary>\n\t\t/// Calls <see cref=\"next\"/> and <see cref=\"write\"/>.\n\t\t/// </summary>\n\t\t/// <param name=\"cMark\">A character to add to the results string like <c>\"A=150\"</c>.</param>\n\t\tpublic static void nw(char cMark = '\\0') => s_static.NW(cMark);\n\t\t\n\t\t/// <summary>\n\t\t/// Formats a string from time values collected by calling <see cref=\"first\"/> and <see cref=\"next\"/>, and shows it in the output.\n\t\t/// The string contains the number of microseconds of each code execution between calling <see cref=\"first\"/> and each <see cref=\"next\"/>.\n\t\t/// </summary>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// perf.first(100);\n\t\t/// CODE1;\n\t\t/// perf.next();\n\t\t/// CODE2;\n\t\t/// perf.next();\n\t\t/// perf.write(); //speed:  timeOfCODE1  timeOfCODE2  (totalTime)\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic static void write() => s_static.Write();\n\t\t\n\t\t/// <summary>\n\t\t/// Formats a string from time values collected by calling <see cref=\"first\"/> and <see cref=\"next\"/>.\n\t\t/// The string contains the number of microseconds of each code execution between calling <see cref=\"first\"/> and each <see cref=\"next\"/>.\n\t\t/// </summary>\n\t\tpublic static string toString() => s_static.ToString();\n\t\t\n\t\t/// <summary>\n\t\t/// Return array of time values collected by calling <see cref=\"first\"/> and <see cref=\"next\"/>.\n\t\t/// Each element is the number of microseconds of each code execution between calling <see cref=\"first\"/> and each <see cref=\"next\"/>.\n\t\t/// </summary>\n\t\tpublic static long[] toArray() => s_static.ToArray();\n\t\t\n\t\t/// <summary>\n\t\t/// Gets the number of microseconds between <see cref=\"first\"/> and the last <see cref=\"next\"/>.\n\t\t/// </summary>\n\t\tpublic static long timeTotal => s_static.TimeTotal;\n\t\t\n\t\t/// <summary>\n\t\t/// Executes some code in loop for the specified amount of time. It should make CPU to run at full speed.\n\t\t/// </summary>\n\t\t/// <param name=\"timeMilliseconds\">How long to speed up CPU, milliseconds. The minimal required time probably is about 100 ms, but depends on CPU.</param>\n\t\t/// <remarks>\n\t\t/// Code speed measurements often are misleading because of variable CPU speed. Most CPU don't run at full speed when not actively used.\n\t\t/// \n\t\t/// You can make CPU speed constant in <b>Control Panel > Power Options > ... Advanced > Processor power management > Minimum or maximum power state</b>.\n\t\t/// There are programs that show current CPU speed. For example HWMonitor.\n\t\t/// </remarks>\n\t\tpublic static void cpu(int timeMilliseconds = 200) {\n\t\t\tint n = 0;\n\t\t\tfor (long t0 = mcs; mcs - t0 < timeMilliseconds * 1000L; n++) { }\n\t\t\t//print.it(n);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets a reference to a <see cref=\"Instance\"/> variable in shared memory.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// The variable can be used by multiple processes, for example to measure process startup time.\n\t\t/// Note: slow first time in process, eg 3 ms. It's because need to JIT-compile functions and open shared memory.\n\t\t/// </remarks>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// ref var p = ref perf.shared;\n\t\t/// p.First();\n\t\t/// //or\n\t\t/// perf.shared.First();\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic static ref Instance shared => ref SharedMemory_.Ptr->perf;\n\t}\n}\n"
  },
  {
    "path": "Au/Time/timer.cs",
    "content": "namespace Au;\n\n/// <summary>\n/// Timer that calls callback function in same thread, which must have a message loop.\n/// </summary>\n/// <remarks>\n/// Uses API <ms>SetTimer</ms> and <ms>WM_TIMER</ms>.\n/// Works only in threads that have a message loop which retrieves/dispatches posted messages. For example threads with windows (except console).\n/// Timer action delegates are protected from GC.\n/// </remarks>\n/// <example>\n/// This example sets 3 timers.\n/// <code><![CDATA[\n/// timer.after(500, _ => print.it(\"after 500 ms\"));\n/// timer.every(1000, _ => print.it(\"every 1000 ms\"));\n/// var t3 = new timer(_ => print.it(\"after 3000 ms\")); t3.After(3000); //the same as timer.after\n/// dialog.show(\"timer\"); //shows a dialog window and waits until closed. The dialog retrieves/dispatches messages in its message loop.\n/// ]]></code>\n/// </example>\npublic class timer {\n\treadonly Action<timer> _action;\n\tnint _id;\n\tint _threadId;\n\tbool _singlePeriod;\n\t\n\t//To control object lifetime we use a thread-static Dictionary.\n\t//Tried GCHandle, but could not find a way to delete object when thread ends.\n\t//Calling KillTimer when thread ends is optional. Need just to re-enable garbage collection for this object.\n\t[ThreadStatic] static Dictionary<nint, timer> t_timers;\n\t\n\t/// <summary>\n\t/// Sets callback function.\n\t/// </summary>\n\tpublic timer(Action<timer> timerAction) {\n\t\t_action = timerAction;\n\t}\n\t//rejected: add overload with hwnd. Or optional parameter. Or another class. Never needed in several years.\n\t\n\t/// <summary>\n\t/// Something to attach to this variable.\n\t/// </summary>\n\tpublic object Tag { get; set; }\n\t\n\t/// <summary>\n\t/// <c>true</c> if the timer is started and not stopped.\n\t/// Note: single-period timer is automatically stopped before calling the callback function.\n\t/// </summary>\n\tpublic bool IsRunning => _id != default;\n\t\n\t/// <summary>\n\t/// Starts one-time timer. If already started, resets and changes its period.\n\t/// </summary>\n\t/// <param name=\"milliseconds\">Time interval after which to call the callback function. The actual minimal interval is 10-20 ms.</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Negative.</exception>\n\t/// <exception cref=\"InvalidOperationException\">Called not in the same thread as previously.</exception>\n\t/// <exception cref=\"Win32Exception\">API <ms>SetTimer</ms> returned 0. Unlikely.</exception>\n\t/// <remarks>\n\t/// The timer will be stopped before calling the callback function. The callback function can start it again.\n\t/// If already started, this function must be called in the same thread as when started.\n\t/// </remarks>\n\tpublic void After(int milliseconds) => _Start(true, milliseconds);\n\t\n\t/// <summary>\n\t/// Starts periodic timer. If already started, resets and changes its period.\n\t/// </summary>\n\t/// <param name=\"milliseconds\">Time interval (period) of calling the callback function. The actual minimal period is 10-20 ms.</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Negative.</exception>\n\t/// <exception cref=\"InvalidOperationException\">Called not in the same thread as previously.</exception>\n\t/// <exception cref=\"Win32Exception\">API <ms>SetTimer</ms> returned 0. Unlikely.</exception>\n\t/// <remarks>\n\t/// The callback function can stop the timer or restart with different period.\n\t/// If already started, this function must be called in the same thread as when started.\n\t/// </remarks>\n\tpublic void Every(int milliseconds) => _Start(false, milliseconds);\n\t\n\tvoid _Start(bool singlePeriod, int milliseconds) {\n\t\tif (milliseconds < 0) throw new ArgumentOutOfRangeException();\n\t\tbool isNew = _id == 0;\n\t\tif (!isNew) _ThreadTrap();\n\t\tnint r = Api.SetTimer(default, _id, milliseconds, s_timerProc);\n\t\tif (r == 0) throw new Win32Exception();\n\t\tDebug.Assert(isNew || r == _id);\n\t\t_id = r;\n\t\t_singlePeriod = singlePeriod;\n\t\tif (isNew) {\n\t\t\t_threadId = Environment.CurrentManagedThreadId;\n\t\t\t(t_timers ??= new Dictionary<nint, timer>()).Add(_id, this);\n\t\t}\n\t\t//print.it($\"Start: {_id}  isNew={isNew}  singlePeriod={singlePeriod}  _threadId={_threadId}\");\n\t}\n\t\n\tstatic readonly Api.TIMERPROC s_timerProc = _TimerProc;\n\tstatic void _TimerProc(wnd w, int msg, nint idEvent, uint time) {\n\t\t//print.it(t_timers.Count, idEvent);\n\t\tif (!t_timers.TryGetValue(idEvent, out var t)) {\n\t\t\t//Debug_.Print($\"timer id {idEvent} not in t_timers\");\n\t\t\treturn;\n\t\t\t//It is possible after killing timer.\n\t\t\t//\tNormally API KillTimer removes WM_TIMER message from queue (tested), but in some conditions our callback can still be called several times.\n\t\t\t//\tFor example if multiple messages are retrieved from the OS queue without dispatching each, and then all are dispatched.\n\t\t\t//\tUsually we can safely ignore it. But not good if the same timer id is reused for another timer. Tested on Win10: OS does not reuse ids soon.\n\t\t}\n\t\tif (t._singlePeriod) t.Stop();\n\t\t\n\t\ttry { t._action(t); }\n\t\tcatch (Exception ex) { print.warning(ex); }\n\t\t//info: OS handles exceptions in timer procedure.\n\t}\n\t\n\t/// <summary>\n\t/// Stops the timer.\n\t/// </summary>\n\t/// <exception cref=\"InvalidOperationException\">Called not in the same thread as previously.</exception>\n\t/// <remarks>\n\t/// The callback function will not be called after this.\n\t/// Later you can start the timer again (call <see cref=\"After\"/> or <see cref=\"Every\"/>).\n\t/// Don't need to call this function for single-period timers. For periodic timers it is optional; the timer stops when the thread ends.\n\t/// This function must be called in the same thread as <see cref=\"After\"/> or <see cref=\"Every\"/>.\n\t/// </remarks>\n\tpublic void Stop() {\n\t\tif (_id != 0) {\n\t\t\t//print.it($\"Stop: {_id}          _threadId={_threadId}\");\n\t\t\t_ThreadTrap();\n\t\t\tApi.KillTimer(default, _id);\n\t\t\t//tested: KillTimer removes pending WM_TIMER messages from queue. MSDN lies. Tested on Win 10 and 7.\n\t\t\tt_timers.Remove(_id);\n\t\t\t_id = 0;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Execute the timer action now.\n\t/// </summary>\n\t/// <remarks>\n\t/// Does not change any properties. Just calls the callback function. Does not handle exceptions.\n\t/// </remarks>\n\tpublic void Now() => _action(this);\n\t\n\tvoid _ThreadTrap() {\n\t\tbool isSameThread = _threadId == Environment.CurrentManagedThreadId;\n\t\tDebug.Assert(isSameThread);\n\t\tif (!isSameThread) throw new InvalidOperationException(nameof(timer) + \" used in multiple threads.\");\n\t\t//FUTURE: somehow allow other thread. It is often useful.\n\t}\n\t\n\t//~timer() { print.it(\"dtor\"); } //don't call Stop() here, we are in other thread\n\t\n\tstatic timer _StartNew(bool singlePeriod, int milliseconds, Action<timer> timerAction, object tag = null) {\n\t\tvar t = new timer(timerAction) { Tag = tag };\n\t\tt._Start(singlePeriod, milliseconds);\n\t\treturn t;\n\t}\n\t\n\t/// <summary>\n\t/// Creates and starts new one-time timer.\n\t/// </summary>\n\t/// <returns>New <see cref=\"timer\"/> object. Usually you don't need it.</returns>\n\t/// <param name=\"milliseconds\">Time interval after which to call the callback function. The actual minimal interval is 10-20 ms.</param>\n\t/// <param name=\"timerAction\">Callback function.</param>\n\t/// <param name=\"tag\">Something to pass to the callback function as <see cref=\"Tag\"/>.</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Negative.</exception>\n\t/// <exception cref=\"Win32Exception\">API <ms>SetTimer</ms> returned 0. Unlikely.</exception>\n\t/// <remarks>\n\t/// The timer will be stopped before calling the callback function. The callback function can start it again.\n\t/// The callback function will be called in this thread.\n\t/// This thread must get/dispatch posted messages, eg call <c>Application.Run</c> or <see cref=\"dialog.show\"/>. The callback function is not called while this thread does not do it.\n\t/// </remarks>\n\tpublic static timer after(int milliseconds, Action<timer> timerAction, object tag = null)\n\t\t=> _StartNew(true, milliseconds, timerAction, tag);\n\t\n\t/// <summary>\n\t/// Creates and starts new periodic timer.\n\t/// </summary>\n\t/// <returns>New <see cref=\"timer\"/> object that can be used to modify timer properties if you want to do it not in the callback function; usually don't need it.</returns>\n\t/// <param name=\"milliseconds\">Time interval (period) of calling the callback function. The actual minimal period is 10-20 ms.</param>\n\t/// <param name=\"timerAction\">Callback function.</param>\n\t/// <param name=\"tag\">Something to pass to the callback function as <see cref=\"Tag\"/>.</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Negative.</exception>\n\t/// <exception cref=\"Win32Exception\">API <ms>SetTimer</ms> returned 0. Unlikely.</exception>\n\t/// <remarks>\n\t/// The callback function can stop the timer or restart with different period.\n\t/// The callback function will be called in this thread.\n\t/// This thread must get/dispatch posted messages, eg call <c>Application.Run</c> or <see cref=\"dialog.show\"/>. The callback function is not called while this thread does not do it.\n\t/// </remarks>\n\tpublic static timer every(int milliseconds, Action<timer> timerAction, object tag = null)\n\t\t=> _StartNew(false, milliseconds, timerAction, tag);\n}\n"
  },
  {
    "path": "Au/Time/timer2.cs",
    "content": "namespace Au;\n\n/// <summary>\n/// Timer that calls callback function in other thread (thread pool) and can be used in any thread.\n/// </summary>\n/// <remarks>\n/// Uses <see cref=\"Timer\"/>.\n/// Unlike <see cref=\"timer\"/>, the thread that sets the timer does not have to retrieve/dispatch messages.\n/// The callback function is called in a random thread of the thread pool, therefore its code is not thread-safe (may need to lock etc).\n/// The actual minimal time interval/period is 10-20 ms, because the system timer period usually is 15.25 ms.\n/// Timer action delegates are protected from GC.\n/// </remarks>\n/// <example>\n/// This example sets 3 timers.\n/// <code><![CDATA[\n/// timer2.after(500, _ => print.it(\"after 500 ms\"));\n/// timer2.every(1000, _ => print.it(\"every 1000 ms\"));\n/// var t3 = new timer2(_ => print.it(\"after 3000 ms\")); t3.After(3000); //the same as timer2.after\n/// 5.s();\n/// ]]></code>\n/// </example>\npublic class timer2 {\n\treadonly Timer _tim;\n\treadonly Action<timer2> _action;\n\t\n\t/// <summary>\n\t/// Sets callback function.\n\t/// </summary>\n\tpublic timer2(Action<timer2> timerAction) {\n\t\t_action = timerAction;\n\t\t_tim = new Timer(o => {\n\t\t\tvar t = o as timer2;\n\t\t\ttry { t._action(t); }\n\t\t\tcatch (Exception ex) { print.warning(ex); }\n\t\t}, this, -1, -1);\n\t\t//note: don't pass dueTime/period to Timer ctor. Could call callback before _tim is assigned.\n\t\t\n\t\t//s_cwt.Add(_tim, new());\n\t\t//_tim = null;\n\t}\n\t\n\t//GC can collect either when explicitly stopped or when 'after' timer completes (somehow). Only after the callback returns.\n\t//~timer2() { print.it(\"fin\"); }\n\t//static ConditionalWeakTable<Timer, gc> s_cwt=new();\n\t//class gc { ~gc() { print.it(\"gc\"); } }\n\t\n\t/// <summary>\n\t/// Stops the timer, and by default disposes.\n\t/// </summary>\n\t/// <param name=\"canReuse\">Just stop but don't dispose. If <c>false</c> (default), can't use the timer again.</param>\n\tpublic void Stop(bool canReuse = false) {\n\t\tif (canReuse) _tim.Change(-1, -1);\n\t\telse _tim.Dispose();\n\t}\n\t\n\t/// <summary>\n\t/// Starts one-time timer or changes timeout/period.\n\t/// </summary>\n\t/// <param name=\"milliseconds\">Time interval after which to call the callback function. Valid values are 0 - <see cref=\"uint.MaxValue\"/> - 2. If -1, stops without disposing.</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\"></exception>\n\t/// <exception cref=\"ObjectDisposedException\">Called <see cref=\"Stop\"/> (unless <i>canReuse</i> true).</exception>\n\t/// <remarks>\n\t/// Calls <see cref=\"Timer.Change(long, long)\"/>.\n\t/// </remarks>\n\tpublic void After(long milliseconds) {\n\t\t_tim.Change(milliseconds, -1);\n\t}\n\t\n\t/// <summary>\n\t/// Starts periodic timer or changes timeout/period.\n\t/// </summary>\n\t/// <param name=\"milliseconds\">Time interval (period) of calling the callback function. Valid values are 0 - <see cref=\"uint.MaxValue\"/> - 2.</param>\n\t/// <param name=\"firstAfter\"><c>null</c> (default) or time interval after which to call the callback function first time. Valid values are 0 - <see cref=\"uint.MaxValue\"/> - 2.</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\"></exception>\n\t/// <exception cref=\"ObjectDisposedException\">Called <see cref=\"Stop\"/> (unless <i>canReuse</i> true).</exception>\n\t/// <remarks>\n\t/// Calls <see cref=\"Timer.Change(long, long)\"/>.\n\t/// </remarks>\n\tpublic void Every(long milliseconds, long? firstAfter = null) {\n\t\t_tim.Change(firstAfter ?? milliseconds, milliseconds);\n\t}\n\t\n\t/// <summary>\n\t/// Something to attach to this variable.\n\t/// </summary>\n\tpublic object Tag { get; set; }\n\t\n\t/// <summary>\n\t/// Creates and starts new one-time timer.\n\t/// </summary>\n\t/// <param name=\"milliseconds\">Time interval after which to call the callback function. Valid values are 0 - <see cref=\"uint.MaxValue\"/> - 2. If -1, stops without disposing.</param>\n\t/// <param name=\"timerAction\">Callback function.</param>\n\t/// <param name=\"tag\">Something to pass to the callback function as <see cref=\"Tag\"/>.</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\"></exception>\n\t/// <remarks>\n\t/// Calls <see cref=\"Timer.Change(long, long)\"/>.\n\t/// </remarks>\n\tpublic static timer2 after(long milliseconds, Action<timer2> timerAction, object tag = null) {\n\t\tvar t = new timer2(timerAction) { Tag = tag };\n\t\tt.After(milliseconds);\n\t\treturn t;\n\t}\n\t\n\t/// <summary>\n\t/// Creates and starts new periodic timer.\n\t/// </summary>\n\t/// <param name=\"milliseconds\">Time interval (period) of calling the callback function. Valid values are 0 - <see cref=\"uint.MaxValue\"/> - 2.</param>\n\t/// <param name=\"timerAction\">Callback function.</param>\n\t/// <param name=\"tag\">Something to pass to the callback function as <see cref=\"Tag\"/>.</param>\n\t/// <param name=\"firstAfter\"><c>null</c> (default) or time interval after which to call the callback function first time. Valid values are 0 - <see cref=\"uint.MaxValue\"/> - 2.</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\"></exception>\n\t/// <remarks>\n\t/// Calls <see cref=\"Timer.Change(long, long)\"/>.\n\t/// </remarks>\n\tpublic static timer2 every(long milliseconds, Action<timer2> timerAction, object tag = null, long? firstAfter = null) {\n\t\tvar t = new timer2(timerAction) { Tag = tag };\n\t\tt.Every(milliseconds, firstAfter);\n\t\treturn t;\n\t}\n}\n"
  },
  {
    "path": "Au/Time/wait.cs",
    "content": "namespace Au;\n\n/// <summary>\n/// Contains functions to wait for a custom condition, handle, etc, or simply sleep.\n/// </summary>\n/// <remarks>\n/// Specialized \"wait for\" functions are in other classes, for example <see cref=\"wnd.wait\"/>.\n/// \n/// All \"wait for\" functions have a timeout parameter. It is the maximal time to wait, in seconds. If 0, waits indefinitely. If <c>></c> 0, throws <see cref=\"TimeoutException\"/> when timed out. If &lt; 0, then stops waiting and returns default value of that type (<c>false</c>, etc).\n/// \n/// While waiting, most functions by default don't dispatch Windows messages, events, hooks, timers, COM/RPC, etc. For example, if used in a <c>Window</c>/<c>Form</c>/<c>Control</c> event handler, the window would stop responding. Use another thread, for example <c>async</c>/<c>await</c>/<c>Task</c>, like in the example. Or <see cref=\"Seconds.DoEvents\"/>.\n/// </remarks>\n/// <example>\n/// <code><![CDATA[\n/// wait.until(0, () => keys.isScrollLock);\n/// print.it(\"ScrollLock now is toggled\");\n/// ]]></code>\n/// Using in a WPF window with async/await.\n/// <code><![CDATA[\n/// using System.Windows;\n/// var b = new wpfBuilder(\"Window\").WinSize(250);\n/// b.R.AddButton(\"Wait\", async _ => {\n/// \t  print.it(\"waiting for ScrollLock...\");\n/// \t  var result = await Task.Run(() => wait.until(-10, () => keys.isScrollLock));\n/// \t  print.it(result);\n/// });\n/// if (!b.ShowDialog()) return;\n/// ]]></code>\n/// </example>\npublic static partial class wait {\n\t/// <summary>\n\t/// Waits <i>timeMilliseconds</i> milliseconds.\n\t/// </summary>\n\t/// <param name=\"timeMilliseconds\">Time to wait, milliseconds. Or <see cref=\"Timeout.Infinite\"/> (-1).</param>\n\t/// <remarks>\n\t/// Calls <see cref=\"Thread.Sleep(int)\"/>.\n\t/// Does not process Windows messages and other events, therefore should not be used in threads with windows, timers, hooks, events or COM, unless <i>timeMilliseconds</i> is small. Supports APC.\n\t/// If the computer goes to sleep or hibernate during that time, the real time is the specified time + the sleep/hibernate time.\n\t/// \n\t/// Tip: the script editor replaces code like <c>100ms</c> with <c>100.ms();</c> when typing.\n\t/// </remarks>\n\t/// <exception cref=\"ArgumentOutOfRangeException\"><i>timeMilliseconds</i> is negative and not -1 (<c>Timeout.Infinite</c>).</exception>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// wait.ms(500);\n\t/// 500.ms(); //the same (ms is an extension method)\n\t/// wait.s(0.5); //the same\n\t/// ]]></code>\n\t/// </example>\n\tpublic static void ms(this int timeMilliseconds) {\n\t\tSleepPrecision_.TempSet1_(timeMilliseconds);\n\t\tif (timeMilliseconds < 2000) {\n\t\t\tThread.Sleep(timeMilliseconds);\n\t\t} else { //workaround for Thread.Sleep bug: if there are APC, returns too soon after sleep/hibernate.\n\t\t\tg1:\n\t\t\tlong t = computer.tickCountWithoutSleep;\n\t\t\tThread.Sleep(timeMilliseconds);\n\t\t\tt = timeMilliseconds - (computer.tickCountWithoutSleep - t);\n\t\t\tif (t >= 500) { timeMilliseconds = (int)t; goto g1; }\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Waits <i>timeSeconds</i> seconds.\n\t/// The same as <see cref=\"ms\"/>, but the time is specified in seconds, not milliseconds.\n\t/// </summary>\n\t/// <param name=\"timeSeconds\">Time to wait, seconds.</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\"><i>timeSeconds</i> is less than 0 or greater than 2147483 (<see cref=\"int.MaxValue\"/> / 1000, 24.8 days).</exception>\n\t/// <remarks>\n\t/// Tip: the script editor replaces code like <c>100ms</c> with <c>100.ms();</c> when typing.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// wait.s(5);\n\t/// 5.s(); //the same (s is an extension method)\n\t/// 5000.ms(); //the same\n\t/// ]]></code>\n\t/// </example>\n\tpublic static void s(this int timeSeconds) {\n\t\tif ((uint)timeSeconds > int.MaxValue / 1000) throw new ArgumentOutOfRangeException();\n\t\tms(timeSeconds * 1000);\n\t}\n\t\n\t/// <summary>\n\t/// Waits <i>timeSeconds</i> seconds.\n\t/// The same as <see cref=\"ms\"/>, but the time is specified in seconds, not milliseconds.\n\t/// </summary>\n\t/// <param name=\"timeSeconds\">Time to wait, seconds. The smallest value is 0.001 (1 ms).</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\"><i>timeSeconds</i> is less than 0 or greater than 2147483 (<see cref=\"int.MaxValue\"/> / 1000, 24.8 days).</exception>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// wait.s(2.5);\n\t/// 2500.ms(); //the same\n\t/// ]]></code>\n\t/// </example>\n\tpublic static void s(this double timeSeconds) {\n\t\tdouble t = timeSeconds * 1000d;\n\t\tif (t > int.MaxValue || t < 0) throw new ArgumentOutOfRangeException();\n\t\tms((int)t);\n\t}\n\t//Maybe this should not be an extension method.\n\t//\tCode like 0.5.s() looks weird. Better 500.ms(). Rarely need non-integer time when > 1 s.\n\t//\tBut: 1. Symmetry. 2. Easier to convert QM code, like 0.5 to 0.5.s(); not 500.ms();.\n\t\n\t/// <summary>\n\t/// Waits <i>timeMS</i> milliseconds. While waiting, retrieves and dispatches Windows messages and other events.\n\t/// </summary>\n\t/// <param name=\"timeMS\">Time to wait, milliseconds. Or <see cref=\"Timeout.Infinite\"/> (-1).</param>\n\t/// <remarks>\n\t/// Unlike <see cref=\"ms\"/>, this function retrieves and dispatches Windows messages, calls .NET event handlers, hook procedures, timer functions, COM, etc.\n\t/// This function can be used in threads with windows. However usually there are better ways, for example timer, other thread, <c>async</c>/<c>await</c>/<c>Task</c>.\n\t/// If <i>timeMS</i> is -1, returns when receives <ms>WM_QUIT</ms> message.\n\t/// </remarks>\n\t/// <exception cref=\"ArgumentOutOfRangeException\"><i>timeMS</i> is negative and not -1 (<c>Timeout.Infinite</c>).</exception>\n\tpublic static unsafe void doEvents(int timeMS) {\n\t\tif (timeMS < -1) throw new ArgumentOutOfRangeException();\n\t\tif (timeMS == 0) {\n\t\t\tApi.SleepEx(0, true); //call APC\n\t\t\tdoEvents();\n\t\t} else {\n\t\t\tusing var mp = new MessagePump_();\n\t\t\tfor (long time = timeMS, timePrev = 0; ;) {\n\t\t\t\tif (timeMS > 0) {\n\t\t\t\t\tlong timeNow = computer.tickCountWithoutSleep;\n\t\t\t\t\tif (timePrev > 0) time -= timeNow - timePrev;\n\t\t\t\t\tif (time <= 0) return;\n\t\t\t\t\ttimePrev = timeNow;\n\t\t\t\t}\n\t\t\t\tswitch (Api.MsgWaitForMultipleObjectsEx(0, null, (int)time, Api.QS_ALLINPUT, Api.MWMO_ALERTABLE | Api.MWMO_INPUTAVAILABLE)) {\n\t\t\t\tcase 0: if (!mp.Pump() && timeMS < 0) return; break;\n\t\t\t\tcase Api.WAIT_TIMEOUT: return;\n\t\t\t\tcase Api.WAIT_IO_COMPLETION: break;\n\t\t\t\tdefault: throw new Win32Exception();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t//info: Thread.Sleep is alertable too. One reason I know is Thread.Interrupt; but with doEvents it does not work.\n\t}\n\t\n\tinternal struct MessagePump_ : IDisposable {\n\t\tint? _quit;\n\t\t\n\t\t/// <summary>\n\t\t/// If <see cref=\"Pump\"/> received <c>WM_QUIT</c>, calls <c>PostQuitMessage</c>, unless detects that it could cause infinite loop.\n\t\t/// </summary>\n\t\tpublic void Dispose() {\n\t\t\tif (_quit != null) {\n\t\t\t\t//prevent infinite loop when eg the caller uses a loop like in doEvents(int) and ignores WM_QUIT\n\t\t\t\tvar stack = new StackTrace(false).FrameCount;\n\t\t\t\tif (t_doeventsStack == 0 || stack < t_doeventsStack) {\n\t\t\t\t\tt_doeventsStack = stack;\n\t\t\t\t\tApi.PostQuitMessage(_quit.Value);\n\t\t\t\t} else {\n\t\t\t\t\tprint.warning(\"duplicate WM_QUIT\");\n\t\t\t\t}\n\t\t\t\t_quit = null;\n\t\t\t}\n\t\t}\n\t\t[ThreadStatic] static int t_doeventsStack;\n\t\t\n\t\t/// <summary>\n\t\t/// Calls <c>PeekMessage</c>/<c>TranslateMessage</c>/<c>DispatchMessage</c> while there are messages.\n\t\t/// </summary>\n\t\t/// <returns><c>false</c> if received <c>WM_QUIT</c>.</returns>\n\t\tpublic bool Pump() {\n\t\t\twhile (Api.PeekMessage(out var m)) {\n\t\t\t\tif (m.message == Api.WM_QUIT) { _quit = (int)m.wParam; return false; }\n\t\t\t\tApi.TranslateMessage(m);\n\t\t\t\tApi.DispatchMessage(m);\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Like <see cref=\"Pump\"/>, but can call a callback function. Used by <see cref=\"Wait_\"/>.\n\t\t/// </summary>\n\t\t/// <param name=\"msgCallback\">\n\t\t/// <c>null</c> or callback function of type:\n\t\t/// <br/>• <see cref=\"WPMCallback\"/> - called before dispatching a message. If returns <c>true</c>, not called for other messages. Can modify the <c>MSG</c>. Can set <c>MSG.message</c> = 0 to prevent dispatching it.\n\t\t/// <br/>• <c>Func&lt;bool&gt;</c> - called after dispatching all messages.\n\t\t/// </param>\n\t\t/// <returns><c>true</c> if <i>msgCallback</i> returned <c>true</c>.</returns>\n\t\tpublic bool PumpWithCallback(Delegate msgCallback) {\n\t\t\tbool R = false;\n\t\t\twhile (Api.PeekMessage(out var m)) {\n\t\t\t\tif (msgCallback is WPMCallback callback1) {\n\t\t\t\t\tif (callback1(ref m)) { msgCallback = null; R = true; }\n\t\t\t\t\tif (m.message == 0) continue;\n\t\t\t\t}\n\t\t\t\tif (m.message == Api.WM_QUIT) { _quit = (int)m.wParam; continue; } //now ignore, but finally repost\n\t\t\t\tApi.TranslateMessage(m);\n\t\t\t\tApi.DispatchMessage(m);\n\t\t\t}\n\t\t\tif (msgCallback is Func<bool> callback2) R = callback2();\n\t\t\treturn R;\n\t\t}\n\t\t\n\t\t//note: never use \"dispatch only sent messages\". It's dangerous and not useful.\n\t\t//\tIf thread has windows, hangs if we don't get posted messages.\n\t\t//\tElse PeekMessage usually does not harm.\n\t\t\n\t\t//note: with PeekMessage don't use |Api.PM_QS_SENDMESSAGE when don't need posted messages.\n\t\t//\tThen setwineventhook hook does not work. Although setwindowshookex hook works. COM RPC not tested.\n\t}\n\t\n\t/// <summary>\n\t/// Retrieves and dispatches events and Windows messages from the message queue of this thread.\n\t/// </summary>\n\t/// <returns><c>false</c> if received <c>WM_QUIT</c> message.</returns>\n\t/// <remarks>\n\t/// Similar to <see cref=\"System.Windows.Forms.Application.DoEvents\"/>, but more lightweight. Uses API functions <ms>PeekMessage</ms>, <ms>TranslateMessage</ms> and <ms>DispatchMessage</ms>.\n\t/// </remarks>\n\tpublic static bool doEvents() {\n\t\tusing var mp = new MessagePump_();\n\t\treturn mp.Pump();\n\t}\n\t\n\t/// <summary>\n\t/// Calls <see cref=\"SleepPrecision_.TempSet1_\"/> and <see cref=\"doEvents(int)\"/>.\n\t/// </summary>\n\tinternal static unsafe void doEventsPrecise_(int timeMS) {\n\t\tSleepPrecision_.TempSet1_(timeMS);\n\t\tdoEvents(timeMS);\n\t}\n\t\n\t/// <summary>\n\t/// Temporarily changes the time resolution/precision of <c>Thread.Sleep</c> and some other functions.\n\t/// </summary>\n\t/// <remarks>\n\t/// Uses API <ms>timeBeginPeriod</ms>, which requests a time resolution for various system timers and wait functions. Actually it is the system thread scheduling timer period.\n\t/// Normal resolution on Windows 7-10 is 15.625 ms. It means that, for example, <c>Thread.Sleep(1);</c> sleeps not 1 but 1-15 ms. If you set resolution 1, it sleeps 1-2 ms.\n\t/// The new resolution is revoked (<ms>timeEndPeriod</ms>) when disposing the <c>SleepPrecision_</c> variable or when this process ends. See example. See also <see cref=\"TempSet1\"/>.\n\t/// The resolution is applied to all threads and processes. Other applications can change it too. For example, often web browsers temporarily set resolution 1 ms when opening a web page.\n\t/// The system uses the smallest period (best resolution) that currently is set by any application. You cannot make it bigger than current value.\n\t/// <note>It is not recommended to keep small period (high resolution) for a long time. It can be bad for power saving.</note>\n\t/// Don't need this for <see cref=\"wait.ms\"/> etc and functions that use them. They call <see cref=\"TempSet1\"/> when the sleep time is 1-89 ms.\n\t/// This does not change the minimal period of timers.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// _Test(\"before\");\n\t/// using(new wait.SleepPrecision_(2)) {\n\t/// \t_Test(\"in\");\n\t/// }\n\t/// _Test(\"after\");\n\t/// \n\t/// void _Test(string name) {\n\t/// \tprint.it(name);\n\t/// \tperf.first();\n\t/// \tfor(int i = 0; i < 8; i++) { Thread.Sleep(1); perf.next(); }\n\t/// \tperf.write();\n\t/// }\n\t/// ]]></code>\n\t/// </example>\n\tinternal sealed class SleepPrecision_ : IDisposable {\n\t\t//info: this class could be public, but probably not useful. wait.ms automatically sets 1 ms period if need.\n\t\t\n\t\tint _period;\n\t\t\n\t\t/// <summary>\n\t\t/// Calls API <ms>timeBeginPeriod</ms>.\n\t\t/// </summary>\n\t\t/// <param name=\"periodMS\">\n\t\t/// New system timer period, milliseconds.\n\t\t/// Should be 1. Other values may stuck and later cannot be made smaller due to bugs in OS or some applications; this bug would impact many functions of this library.\n\t\t/// </param>\n\t\t/// <exception cref=\"ArgumentOutOfRangeException\"><i>periodMS</i> &lt;= 0.</exception>\n\t\tpublic SleepPrecision_(int periodMS) {\n\t\t\tif (periodMS <= 0) throw new ArgumentOutOfRangeException();\n\t\t\tif (Api.timeBeginPeriod((uint)periodMS) != 0) return;\n\t\t\t//print.it(\"set\");\n\t\t\t_period = periodMS;\n\t\t\t\n\t\t\t//Bug in OS or drivers or some apps:\n\t\t\t//\tOn my main PC often something briefly sets 0.5 ms resolution.\n\t\t\t//\tIf at that time this process already has set a resolution of more than 1 ms, then after that time this process cannot change resolution.\n\t\t\t//\tIt means that if this app eg has set 10 ms resolution, then wait.ms(1) will sleep 10 ms and not the normal 1-2 ms.\n\t\t\t//\tKnown workaround (but don't use, sometimes does not work, eg cannot end period that was set by another process):\n\t\t\t//\t\ttimeBeginPeriod(periodMS);\n\t\t\t//\t\tvar r=(int)Current; if(r>periodMS) { timeEndPeriod(periodMS); timeEndPeriod(r); timeBeginPeriod(r); timeBeginPeriod(periodMS); }\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Calls API <ms>timeEndPeriod</ms>.\n\t\t/// </summary>\n\t\tpublic void Dispose() {\n\t\t\t_Dispose();\n\t\t\tGC.SuppressFinalize(this);\n\t\t}\n\t\t\n\t\tvoid _Dispose() {\n\t\t\tif (_period == 0) return;\n\t\t\t//print.it(\"revoke\");\n\t\t\tApi.timeEndPeriod((uint)_period); _period = 0;\n\t\t}\n\t\t\n\t\t///\n\t\t~SleepPrecision_() { _Dispose(); }\n\t\t\n\t\t/// <summary>\n\t\t/// Gets current actual system time resolution (period).\n\t\t/// </summary>\n\t\t/// <returns>The return value usually is between 0.5 and 15.625 milliseconds. Returns 0 if failed.</returns>\n\t\tpublic static float Current {\n\t\t\tget {\n\t\t\t\tif (0 != Api.NtQueryTimerResolution(out _, out _, out var t)) return 0f;\n\t\t\t\treturn (float)t / 10000;\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Temporarily sets the system wait precision to 1 ms. It will be revoked after the specified time or when this process ends.\n\t\t/// If already set, just updates the revoking time.\n\t\t/// </summary>\n\t\t/// <param name=\"endAfterMS\">Revoke after this time, milliseconds.</param>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// print.it(wait.SleepPrecision_.Current); //probably 15.625\n\t\t/// wait.SleepPrecision_.TempSet1(500);\n\t\t/// print.it(wait.SleepPrecision_.Current); //1\n\t\t/// Thread.Sleep(600);\n\t\t/// print.it(wait.SleepPrecision_.Current); //probably 15.625 again\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic static void TempSet1(int endAfterMS = 1111) {\n\t\t\tlock (\"2KgpjPxRck+ouUuRC4uBYg\") {\n\t\t\t\ts_TS1_EndTime = computer.tickCountWithoutSleep + endAfterMS;\n\t\t\t\tif (s_TS1_Obj == null) {\n\t\t\t\t\ts_TS1_Obj = new SleepPrecision_(1); //info: instead could call the API directly, but may need to auto-revoke using the finalizer\n\t\t\t\t\tThreadPool.QueueUserWorkItem(endAfterMS2 => {\n\t\t\t\t\t\tThread.Sleep((int)endAfterMS2); //note: don't use captured variables. It creates new garbage all the time.\n\t\t\t\t\t\tfor (; ; ) {\n\t\t\t\t\t\t\tint t;\n\t\t\t\t\t\t\tlock (\"2KgpjPxRck+ouUuRC4uBYg\") {\n\t\t\t\t\t\t\t\tt = (int)(s_TS1_EndTime - computer.tickCountWithoutSleep);\n\t\t\t\t\t\t\t\tif (t <= 0) {\n\t\t\t\t\t\t\t\t\ts_TS1_Obj.Dispose();\n\t\t\t\t\t\t\t\t\ts_TS1_Obj = null;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tThread.Sleep(t);\n\t\t\t\t\t\t}\n\t\t\t\t\t}, endAfterMS);\n\t\t\t\t\t//performance (old info): single QueueUserWorkItem adds 3 threads, >=2 adds 5. But Thread.Start is too slow etc.\n\t\t\t\t\t//QueueUserWorkItem speed first time is similar to Thread.Start, then ~8.\n\t\t\t\t\t//Task.Run and Task.Delay are much much slower first time. Single Delay adds 5 threads.\n\t\t\t\t}\n\t\t\t}\n\t\t\t//tested: Task Manager shows 0% CPU. If we set/revoke period for each Sleep(1) in loop, shows ~0.5% CPU.\n\t\t}\n\t\tstatic SleepPrecision_ s_TS1_Obj;\n\t\tstatic long s_TS1_EndTime;\n\t\t\n\t\t//never mind: finalizer is not called on process exit.\n\t\t//\tNot a problem, because OS clears our set value (tested). Or we could use process.thisProcessExit event.\n\t\t\n\t\t/// <summary>\n\t\t/// Calls TempSet1 if <i>sleepTimeMS</i> is 1-89.\n\t\t/// </summary>\n\t\t/// <param name=\"sleepTimeMS\">milliseconds of the caller \"sleep\" function.</param>\n\t\tinternal static void TempSet1_(int sleepTimeMS) {\n\t\t\tif (sleepTimeMS is < 90 and > 0) TempSet1(1111);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au/Time/wait_for.cs",
    "content": "namespace Au {\n\tpublic static partial class wait {\n\t\t/// <summary>\n\t\t/// Waits for a user-defined condition. Until the callback function returns a value other than <c>default(T)</c>, for example <c>true</c>.\n\t\t/// </summary>\n\t\t/// <param name=\"timeout\">Timeout, seconds. Can be 0 (infinite), >0 (exception) or &lt;0 (no exception). More info: [](xref:wait_timeout).</param>\n\t\t/// <param name=\"condition\">Callback function (eg lambda). It is called repeatedly, until returns a value other than <c>default(T)</c>. Default period is 10, and can be changed like <c>wait.until(new(5) { Timeout = 100 }, ...)</c>.</param>\n\t\t/// <returns>Returns the value returned by the callback function. On timeout returns <c>default(T)</c> if <i>timeout</i> is negative; else exception.</returns>\n\t\t/// <exception cref=\"TimeoutException\"></exception>\n\t\t/// <example>See <see cref=\"wait\"/>.</example>\n\t\tpublic static T until<T>(Seconds timeout, Func<T> condition) {\n\t\t\tvar loop = new WaitLoop(timeout);\n\t\t\tfor (; ; ) {\n\t\t\t\tT r = condition();\n\t\t\t\tif (!EqualityComparer<T>.Default.Equals(r, default)) return r;\n\t\t\t\tif (!loop.Sleep()) return r;\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Obsolete. Use <c>wait.until</c>.\n\t\t/// Waits for a user-defined condition. Until the callback function returns a value other than <c>default(T)</c>, for example <c>true</c>.\n\t\t/// </summary>\n\t\t/// <param name=\"secondsTimeout\">Timeout, seconds. Can be 0 (infinite), >0 (exception) or &lt;0 (no exception). More info: [](xref:wait_timeout).</param>\n\t\t/// <param name=\"condition\">Callback function (eg lambda). It is called repeatedly, until returns a value other than <c>default(T)</c>. The calling period depends on <i>options</i>.</param>\n\t\t/// <param name=\"options\">Options. If <c>null</c>, uses <c>opt.wait</c>.</param>\n\t\t/// <returns>Returns the value returned by the callback function. On timeout returns <c>default(T)</c> if <i>secondsTimeout</i> is negative; else exception.</returns>\n#if DEBUG\n\t\t[Obsolete]\n#endif\n\t\t[EditorBrowsable(EditorBrowsableState.Never)]\n\t\tpublic static T forCondition<T>(double secondsTimeout, Func<T> condition, OWait options = null) {\n#pragma warning disable CS0612 // Type or member is obsolete\n\t\t\tvar loop = new WaitLoop(secondsTimeout, options);\n#pragma warning restore CS0612 // Type or member is obsolete\n\t\t\tfor (; ; ) {\n\t\t\t\tT r = condition();\n\t\t\t\tif (!EqualityComparer<T>.Default.Equals(r, default)) return r;\n\t\t\t\tif (!loop.Sleep()) return r;\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Calls callback function <i>action</i>. If it throws an exception, waits/retries until it does not throw exceptions or until timeout.\n\t\t/// </summary>\n\t\t/// <param name=\"timeout\">Timeout, seconds. Can be 0 (infinite), >0 (exception) or &lt;0 (no exception). More info: [](xref:wait_timeout).</param>\n\t\t/// <param name=\"action\">Callback function (eg lambda). It is called repeatedly, until does not throw an exception. Default period is 10, and can be changed like <c>wait.retry(new(5) { Timeout = 100 }, ...)</c>.</param>\n\t\t/// <param name=\"catchWhen\">Called on exception. Return <c>true</c> to handle the exception (and wait/retry). Return <c>false</c> to not handle the exception. If <c>null</c> (default), handles all exceptions.</param>\n\t\t/// <returns>Returns <c>true</c> when <i>action</i> succeeded. On timeout returns <c>false</c> if <i>timeout</i> is negative; else exception.</returns>\n\t\t/// <exception cref=\"TimeoutException\"></exception>\n\t\t/// <example>\n\t\t/// This example uses both <c>wait.retry</c> overloads - with parameter <c>Func func</c> and with parameter <c>Action action</c>.\n\t\t/// <code><![CDATA[\n\t\t/// string file = @\"C:\\Test\\test.txt\";\n\t\t/// string s = wait.retry(5, () => File.ReadAllText(file));\n\t\t/// s = s.Upper();\n\t\t/// wait.retry(5, () => { File.WriteAllText(file, s); });\n\t\t/// print.it(\"ok\");\n\t\t/// ]]></code>\n\t\t/// Set the retry period.\n\t\t/// <code><![CDATA[\n\t\t/// wait.retry(new(5) { Period = 100, MaxPeriod = 1000 }, () => { File.WriteAllText(file, s); });\n\t\t/// ]]></code>\n\t\t/// Handle only exceptions of some types.\n\t\t/// <code><![CDATA[\n\t\t/// wait.retry(5, () => { File.WriteAllText(file, s); }, e => e is IOException);\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic static bool retry(Seconds timeout, Action action, Func<Exception, bool> catchWhen = null) {\n\t\t\tvar loop = new WaitLoop(timeout);\n\t\t\tfor (; ; ) {\n\t\t\t\ttry { action(); return true; }\n\t\t\t\tcatch (Exception e) when (catchWhen?.Invoke(e) != false) { }\n\t\t\t\tif (!loop.Sleep()) return false;\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Calls callback function <i>func</i> and returns its result. If it throws an exception, waits/retries until it does not throw exceptions or until timeout.\n\t\t/// </summary>\n\t\t/// <param name=\"timeout\">Timeout, seconds. Can be 0 (infinite), >0 (exception) or &lt;0 (no exception). More info: [](xref:wait_timeout).</param>\n\t\t/// <param name=\"func\">Callback function (eg lambda). It is called repeatedly, until does not throw an exception. Default period is 10, and can be changed like <c>wait.retry(new(5) { Timeout = 100 }, ...)</c>.</param>\n\t\t/// <param name=\"catchWhen\">Called on exception. Return <c>true</c> to handle the exception (and wait/retry). Return <c>false</c> to not handle the exception. If <c>null</c> (default), handles all exceptions.</param>\n\t\t/// <returns>Returns the value returned by the callback function. On timeout returns <c>default(T)</c> if <i>timeout</i> is negative; else exception.</returns>\n\t\t/// <exception cref=\"TimeoutException\"></exception>\n\t\tpublic static T retry<T>(Seconds timeout, Func<T> func, Func<Exception, bool> catchWhen = null) {\n\t\t\tvar loop = new WaitLoop(timeout);\n\t\t\tfor (; ; ) {\n\t\t\t\ttry { return func(); }\n\t\t\t\tcatch (Exception e) when (catchWhen?.Invoke(e) != false) { }\n\t\t\t\tif (!loop.Sleep()) return default;\n\t\t\t}\n\t\t}\n\t\t\n\t\t//rejected. This overload could be useful when need exact try count and period. But probably rarely useful when we have the overloads with Seconds. Maybe in the future, if really useful. Also need Func overload, CancellationToken, max period, doevents option, etc.\n\t\t//public static bool retry(int tryCount, int periodMS, Action action, Func<Exception, bool> catchWhen = null) {\n\t\t//\tArgumentOutOfRangeException.ThrowIfEqual(tryCount, 0);\n\t\t//\tArgumentOutOfRangeException.ThrowIfNegative(periodMS);\n\t\t//\tbool noThrow = tryCount < 0; if (noThrow) tryCount = -tryCount;\n\t\t//\twhile (tryCount-- > 0) {\n\t\t//\t\ttry { action(); return true; }\n\t\t//\t\tcatch (Exception e) when ((tryCount > 0 || noThrow) && catchWhen?.Invoke(e) != false) { }\n\t\t//\t\tif (tryCount > 0 && periodMS > 0) Thread.Sleep(periodMS);\n\t\t//\t}\n\t\t//\treturn false;\n\t\t//}\n\t\t\n\t\t/// <summary>\n\t\t/// Waits for a kernel object (event, mutex, etc).\n\t\t/// </summary>\n\t\t/// <param name=\"timeout\">Timeout, seconds. Can be 0 (infinite), >0 (exception) or &lt;0 (no exception). More info: [](xref:wait_timeout).</param>\n\t\t/// <param name=\"flags\"></param>\n\t\t/// <param name=\"handles\">One or more handles of kernel objects. Max 63.</param>\n\t\t/// <returns>\n\t\t/// Returns 1-based index of the first signaled handle. Negative if abandoned mutex.\n\t\t/// On timeout returns 0 if <i>timeout</i> is negative; else exception.\n\t\t/// </returns>\n\t\t/// <exception cref=\"TimeoutException\"><i>timeout</i> time has expired (if > 0).</exception>\n\t\t/// <exception cref=\"AuException\">Failed. For example a handle is invalid.</exception>\n\t\t/// <remarks>\n\t\t/// Uses API <ms>WaitForMultipleObjectsEx</ms> or <ms>MsgWaitForMultipleObjectsEx</ms>. Alertable.\n\t\t/// Does not use <see cref=\"WaitLoop\"/>, <see cref=\"Seconds.Period\"/> and <see cref=\"Seconds.MaxPeriod\"/>.\n\t\t/// </remarks>\n\t\tpublic static int forHandle(Seconds timeout, WHFlags flags, params ReadOnlySpan<IntPtr> handles) {\n\t\t\treturn WaitS_(timeout, flags, handles);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Waits for <i>handles</i>, or/and <i>msgCallback</i> returning <c>true</c>, or/and <i>stopVar</i> becoming <c>true</c>.\n\t\t/// Calls <see cref=\"Wait_\"/>.\n\t\t/// </summary>\n\t\t/// <returns>\n\t\t/// <br/>• 0 if timeout (if <i>timeout</i> &lt; 0),\n\t\t/// <br/>• <c>1-handles.Length</c> if signaled,\n\t\t/// <br/>• <c>-(1-handles.Length)</c> if abandoned mutex,\n\t\t/// <br/>• <c>1+handles.Length</c> if <i>msgCallback</i> returned <c>true</c>,\n\t\t/// <br/>• <c>2+handles.Length</c> if stop became <c>true</c>.\n\t\t/// </returns>\n\t\tinternal static int WaitS_(Seconds timeout, WHFlags flags, ReadOnlySpan<IntPtr> handles = default, Delegate msgCallback = null, WaitVariable_ stopVar = null) {\n\t\t\tif (timeout.Period != null || timeout.MaxPeriod != null) print.warning(\"This wait function does not use Seconds.Period/MaxPeriod.\");\n\t\t\tif (flags.Has(WHFlags.DoEvents)) {\n\t\t\t\tif (timeout.DoEvents != null) print.warning(\"This wait function does not use Seconds.DoEvents. It always works like if it is true.\");\n\t\t\t} else if (timeout.DoEvents == true) flags |= WHFlags.DoEvents;\n\t\t\t\n\t\t\tlong timeMS = _TimeoutS2MS(timeout, out bool canThrow);\n\t\t\t\n\t\t\tint r = Wait_(timeMS, flags, handles, msgCallback, stopVar, timeout.Cancel);\n\t\t\tif (r < 0) throw new AuException(0);\n\t\t\tif (r == Api.WAIT_TIMEOUT) {\n\t\t\t\tif (canThrow) throw new TimeoutException();\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tr++; if (r > Api.WAIT_ABANDONED_0) r = -r;\n\t\t\treturn r;\n\t\t}\n\t\t\n\t\tstatic long _TimeoutS2MS(Seconds timeout, out bool canThrow) {\n\t\t\tcanThrow = false;\n\t\t\tvar t = timeout.Time;\n\t\t\tif (t == 0) return -1;\n\t\t\tif (t < 0) t = -t; else canThrow = true;\n\t\t\treturn checked((long)(t * 1000d));\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Waits for <i>handles</i>, or/and <i>msgCallback</i> returning <c>true</c>, or/and <i>stopVar</i> becoming <c>true</c>. Or just sleeps, if <i>handles</i> etc are <c>null</c>/empty.\n\t\t/// If flag <c>DoEvents</c>, dispatches received messages etc.\n\t\t/// Calls API <ms>WaitForMultipleObjectsEx</ms> or <ms>MsgWaitForMultipleObjectsEx</ms> with <c>QS_ALLINPUT</c>. Alertable.\n\t\t/// </summary>\n\t\t/// <param name=\"msgCallback\">\n\t\t/// Called when dispatching messages. If returns <c>true</c>, stops waiting and returns <c>handles.Length</c>.\n\t\t/// \tIf it is <c>WPMCallback</c>, calls it before dispatching a posted message.\n\t\t/// \tIf it is <c>Func{bool}</c>, calls it after dispatching one or more messages.\n\t\t/// </param>\n\t\t/// <param name=\"stopVar\">When becomes <c>true</c>, stops waiting and returns <c>handles.Length + 1</c>.</param>\n\t\t/// <returns>\n\t\t/// When a handle becomes signaled, returns its 0-based index. If abandoned mutex, returns 0-based index + <c>WAIT_ABANDONED_0</c> (0x80).\n\t\t/// If <c>timeMS>0</c>, waits max <i>timeMS</i> and on timeout returns <c>WAIT_TIMEOUT</c>.\n\t\t/// If failed, returns -1. Supports <see cref=\"lastError\"/>.\n\t\t/// </returns>\n\t\tinternal static unsafe int Wait_(long timeMS, WHFlags flags, ReadOnlySpan<IntPtr> handles = default, Delegate msgCallback = null, WaitVariable_ stopVar = null, CancellationToken cancel = default) {\n\t\t\t//rejected. Don't complicate code to implement rarely used features.\n\t\t\t//if (cancel.CanBeCanceled) {\n\t\t\t//\tvar ch = cancel.WaitHandle.SafeWaitHandle.DangerousGetHandle();\n\t\t\t//\thandles = handles == null ? new[] { ch } : handles.InsertAt(-1, ch);\n\t\t\t//}\n\t\t\t\n\t\t\tbool doEvents = flags.Has(WHFlags.DoEvents);\n\t\t\tDebug.Assert(doEvents || (msgCallback == null && stopVar == null));\n\t\t\tint nHandles = handles.Length;\n\t\t\tbool all = flags.Has(WHFlags.All) && nHandles > 1;\n\t\t\t\n\t\t\tusing var mp = new MessagePump_();\n\t\t\tfixed (IntPtr* ha = handles) {\n\t\t\t\tfor (long timePrev = 0; ;) {\n\t\t\t\t\tcancel.ThrowIfCancellationRequested();\n\t\t\t\t\tif (stopVar != null && stopVar.waitVar) return nHandles + 1;\n\t\t\t\t\t\n\t\t\t\t\tint timeSlice = all && doEvents ? 50 : cancel.CanBeCanceled ? 250 : 5000;\n\t\t\t\t\tif (timeMS > 0) {\n\t\t\t\t\t\tlong timeNow = computer.tickCountWithoutSleep;\n\t\t\t\t\t\tif (timePrev > 0) timeMS -= timeNow - timePrev;\n\t\t\t\t\t\tif (timeMS <= 0) return Api.WAIT_TIMEOUT;\n\t\t\t\t\t\tif (timeSlice > timeMS) timeSlice = (int)timeMS;\n\t\t\t\t\t\ttimePrev = timeNow;\n\t\t\t\t\t} else if (timeMS == 0) timeSlice = 0;\n\t\t\t\t\t\n\t\t\t\t\tint k;\n\t\t\t\t\tif (doEvents && !all) {\n\t\t\t\t\t\tk = Api.MsgWaitForMultipleObjectsEx(nHandles, ha, timeSlice, Api.QS_ALLINPUT, Api.MWMO_ALERTABLE | Api.MWMO_INPUTAVAILABLE);\n\t\t\t\t\t\tif (k == nHandles) { //message, COM, hook, etc\n\t\t\t\t\t\t\tif (mp.PumpWithCallback(msgCallback)) return nHandles;\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (nHandles > 0) k = Api.WaitForMultipleObjectsEx(nHandles, ha, all, timeSlice, true);\n\t\t\t\t\t\telse { k = Api.SleepEx(timeSlice, true); if (k == 0) k = Api.WAIT_TIMEOUT; }\n\t\t\t\t\t\tif (doEvents) if (mp.PumpWithCallback(msgCallback)) return nHandles;\n\t\t\t\t\t}\n\t\t\t\t\tif (k is not (Api.WAIT_TIMEOUT or Api.WAIT_IO_COMPLETION)) return k; //signaled handle, abandoned mutex, WAIT_FAILED (-1)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Waits for a posted message received by this thread.\n\t\t/// </summary>\n\t\t/// <param name=\"timeout\">Timeout, seconds. Can be 0 (infinite), >0 (exception) or &lt;0 (no exception). More info: [](xref:wait_timeout).</param>\n\t\t/// <param name=\"callback\">Callback function that returns <c>true</c> to stop waiting. More info in Remarks.</param>\n\t\t/// <returns>Returns <c>true</c>. On timeout returns <c>false</c> if <i>timeout</i> is negative; else exception.</returns>\n\t\t/// <exception cref=\"TimeoutException\"><i>timeout</i> time has expired (if > 0).</exception>\n\t\t/// <remarks>\n\t\t/// While waiting, dispatches Windows messages etc, like <see cref=\"doEvents(int)\"/>. Before dispatching a posted message, calls the callback function. Stops waiting when it returns <c>true</c>. Does not dispatch the message if the function sets the message field = 0.\n\t\t/// Does not use <see cref=\"WaitLoop\"/>, <see cref=\"Seconds.Period\"/>, <see cref=\"Seconds.MaxPeriod\"/> and <see cref=\"Seconds.DoEvents\"/>.\n\t\t/// </remarks>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// timer.after(2000, t => { print.it(\"timer\"); });\n\t\t/// wait.forPostedMessage(5, (ref MSG m) => { print.it(m); return m.message == 0x113; }); //WM_TIMER\n\t\t/// print.it(\"finished\");\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic static bool forPostedMessage(Seconds timeout, WPMCallback callback) {\n\t\t\treturn 1 == WaitS_(timeout, WHFlags.DoEvents, msgCallback: callback);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Waits for a condition to be changed while processing messages or other events received by this thread.\n\t\t/// </summary>\n\t\t/// <param name=\"timeout\">Timeout, seconds. Can be 0 (infinite), >0 (exception) or &lt;0 (no exception). More info: [](xref:wait_timeout).</param>\n\t\t/// <param name=\"condition\">Callback function that returns <c>true</c> to stop waiting. More info in Remarks.</param>\n\t\t/// <returns>Returns <c>true</c>. On timeout returns <c>false</c> if <i>timeout</i> is negative; else exception.</returns>\n\t\t/// <exception cref=\"TimeoutException\"><i>timeout</i> time has expired (if > 0).</exception>\n\t\t/// <remarks>\n\t\t/// While waiting, dispatches Windows messages etc, like <see cref=\"doEvents(int)\"/>. After dispatching one or more messages or other events (posted messages, messages sent by other threads, hooks, COM, APC, etc), calls the callback function. Stops waiting when it returns <c>true</c>.\n\t\t/// Similar to <see cref=\"until\"/>. Differences: 1. Always dispatches messages etc. 2. Does not call the callback function when there are no messages etc. 3. Does not use <see cref=\"WaitLoop\"/>, <see cref=\"Seconds.Period\"/>, <see cref=\"Seconds.MaxPeriod\"/> and <see cref=\"Seconds.DoEvents\"/>.\n\t\t/// </remarks>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// bool stop = false;\n\t\t/// timer.after(2000, t => { print.it(\"timer\"); stop = true; });\n\t\t/// wait.doEventsUntil(5, () => stop);\n\t\t/// print.it(stop);\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic static bool doEventsUntil(Seconds timeout, Func<bool> condition) {\n\t\t\treturn 1 == WaitS_(timeout, WHFlags.DoEvents, msgCallback: condition);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Obsolete. Use <c>wait.doEventsUntil</c>.\n\t\t/// </summary>\n\t\t/// <inheritdoc cref=\"doEventsUntil\"/>\n#if DEBUG\n\t\t[Obsolete] //just renamed\n#endif\n\t\t[EditorBrowsable(EditorBrowsableState.Never)]\n\t\tpublic static bool forMessagesAndCondition(double secondsTimeout, Func<bool> condition) => doEventsUntil(secondsTimeout, condition);\n\t\t\n\t\t//rejected. Rarely used; type-limited. Let use wait.until.\n\t\t//public static bool forVariable(Seconds timeout, in bool variable, OWait options = null) { }\n\t\t\n\t\t//FUTURE: add misc wait functions implemented using WindowsHook and WinEventHook.\n\t}\n}\n\nnamespace Au.Types {\n\t/// <summary>\n\t/// Flags for <see cref=\"wait.forHandle\"/>\n\t/// </summary>\n\t[Flags]\n\tpublic enum WHFlags {\n\t\t/// <summary>\n\t\t/// Wait until all handles are signaled.\n\t\t/// </summary>\n\t\tAll = 1,\n\t\t\n\t\t/// <summary>\n\t\t/// While waiting, dispatch Windows messages, events, hooks etc. Like <see cref=\"wait.doEvents(int)\"/>.\n\t\t/// </summary>\n\t\tDoEvents = 2,\n\t}\n\t\n\t/// <summary>\n\t/// Delegate type for <see cref=\"wait.forPostedMessage\"/>.\n\t/// </summary>\n\t/// <param name=\"m\">API <ms>MSG</ms>.</param>\n\tpublic delegate bool WPMCallback(ref MSG m);\n\t\n\t/// <summary>\n\t/// Used with <c>Wait_</c> etc instead of <c>ref bool</c>.\n\t/// </summary>\n\tinternal class WaitVariable_ {\n\t\tpublic bool waitVar;\n\t}\n}\n\n//CONSIDER: in QM2 these functions are created:\n//\tWaitForFocus, WaitWhileWindowBusy,\n//\tWaitForFileReady, WaitForChangeInFolder,\n//\tChromeWait, FirefoxWait\n//\tWaitForTime,\n//\tthese are in System: WaitIdle, WaitForThreads,\n\n//CONSIDER: WaitForFocusChanged\n//\tEg when showing Open/SaveAs dialog, the file Edit control receives focus after 200 ms. Sending text to it works anyway, but the script fails if then it clicks OK not with keys (eg with elm).\n"
  },
  {
    "path": "Au/Triggers/Trigger.cs",
    "content": "namespace Au.Triggers;\n\n/// <summary>\n/// Base of classes of all action trigger types.\n/// </summary>\npublic abstract class ActionTrigger {\n\tinternal ActionTrigger next; //linked list when eg same hotkey is used in multiple scopes\n\tinternal readonly ActionTriggers triggers;\n\tinternal readonly TOptions options; //Triggers.Options\n\treadonly TriggerFunc[] _funcAfter, _funcBefore; //Triggers.FuncOf. _funcAfter used by all triggers; _funcBefore - like scope.\n\tinternal readonly Delegate action;\n\t\n\t/// <summary>\n\t/// <c>Triggers.Of.WindowX</c>. Used by hotkey, autotext and mouse triggers.\n\t/// </summary>\n\tpublic TriggerScope Scope { get; }\n\t\n\t///\n\tpublic string SourceFile { get; }\n\t\n\t///\n\tpublic int SourceLine { get; }\n\t\n\tinternal ActionTrigger(ActionTriggers triggers, Delegate action, bool usesWindowScope, (string, int) source) {\n\t\tthis.SourceFile = source.Item1 ?? throw new ArgumentNullException();\n\t\tthis.SourceLine = source.Item2;\n\t\tthis.action = action;\n\t\tthis.triggers = triggers;\n\t\tvar to = triggers.options_;\n\t\toptions = to.Current;\n\t\tEnabledAlways = to.EnabledAlways;\n\t\tif (usesWindowScope) Scope = triggers.scopes_.Current_;\n\t\tvar tf = triggers.funcs_;\n\t\t_funcBefore = _Func(tf.commonBefore, tf.nextBefore); tf.nextBefore = null;\n\t\t_funcAfter = _Func(tf.nextAfter, tf.commonAfter); tf.nextAfter = null;\n\t\t\n\t\tTriggerFunc[] _Func(TFunc f1, TFunc f2) {\n\t\t\tvar f3 = f1 + f2; if (f3 == null) return null;\n\t\t\tvar a1 = f3.GetInvocationList();\n\t\t\tvar r1 = new TriggerFunc[a1.Length];\n\t\t\tfor (int i = 0; i < a1.Length; i++) {\n\t\t\t\tvar f4 = a1[i] as TFunc;\n\t\t\t\tif (!tf.perfDict.TryGetValue(f4, out var fs)) tf.perfDict[f4] = fs = new TriggerFunc { f = f4 };\n\t\t\t\tr1[i] = fs;\n\t\t\t}\n\t\t\treturn r1;\n\t\t}\n\t}\n\t\n\tinternal void DictAdd_<TKey>(Dictionary<TKey, ActionTrigger> d, TKey key) {\n\t\tif (!d.TryGetValue(key, out var o)) d.Add(key, this);\n\t\telse { //append to the linked list\n\t\t\twhile (o.next != null) o = o.next;\n\t\t\to.next = this;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Called through <see cref=\"TriggerActionThreads.Run\"/> in action thread.\n\t/// Possibly runs later.\n\t/// </summary>\n\tinternal abstract void Run_(TriggerArgs args);\n\t\n\t/// <summary>\n\t/// Makes simpler to implement <see cref=\"Run_\"/>.\n\t/// </summary>\n\tprivate protected void RunT_<T>(T args) => (action as Action<T>)(args);\n\t\n\t/// <summary>\n\t/// Returns a trigger type string, like <c>\"Hotkey\"</c>, <c>\"Mouse\"</c>, <c>\"Window.ActiveNew\"</c>.\n\t/// </summary>\n\tpublic abstract string TypeString { get; }\n\t\n\t/// <summary>\n\t/// Returns a string containing trigger parameters.\n\t/// </summary>\n\tpublic abstract string ParamsString { get; }\n\t\n\t/// <summary>\n\t/// Returns <c>TypeString + \" \" + ParamsString</c>.\n\t/// </summary>\n\tpublic override string ToString() => TypeString + \" \" + ParamsString;\n\t\n\tinternal bool MatchScopeWindowAndFunc_(TriggerHookContext thc) {\n\t\ttry {\n\t\t\tfor (int i = 0; i < 3; i++) {\n\t\t\t\tif (i == 1) {\n\t\t\t\t\tif (Scope != null) {\n\t\t\t\t\t\tthc.PerfStart();\n\t\t\t\t\t\tbool ok = Scope.Match(thc.Window, thc);\n\t\t\t\t\t\tthc.PerfEnd(false, ref Scope.perfTime);\n\t\t\t\t\t\tif (!ok) return false;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tvar af = i == 0 ? _funcBefore : _funcAfter;\n\t\t\t\t\tif (af != null) {\n\t\t\t\t\t\tforeach (var v in af) {\n\t\t\t\t\t\t\tthc.PerfStart();\n\t\t\t\t\t\t\tbool ok = v.f(thc.args);\n\t\t\t\t\t\t\tthc.PerfEnd(true, ref v.perfTime);\n\t\t\t\t\t\t\tif (!ok) return false;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tprint.it(ex);\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t\t\n\t\t//never mind: when same scope used several times (probably with different functions),\n\t\t//\tshould compare it once, and don't call 'before' functions again if did not match. Rare.\n\t}\n\t\n\tinternal bool CallFunc_(TriggerArgs args) {\n#if true\n\t\tif (_funcAfter != null) {\n\t\t\ttry {\n\t\t\t\tforeach (var v in _funcAfter) {\n\t\t\t\t\tvar t1 = perf.ms;\n\t\t\t\t\tbool ok = v.f(args);\n\t\t\t\t\tvar td = perf.ms - t1;\n\t\t\t\t\tif (td > 200) print.warning($\"Too slow Triggers.FuncOf function of a window trigger. Should be < 10 ms, now {td} ms. Task name: {script.name}.\", -1);\n\t\t\t\t\tif (!ok) return false;\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tprint.it(ex);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n#else\n\t\tfor(int i = 0; i < 2; i++) {\n\t\t\tvar af = i == 0 ? _funcBefore : _funcAfter;\n\t\t\tif(af != null) {\n\t\t\t\tforeach(var v in af) {\n\t\t\t\t\tbool ok = v.f(args);\n\t\t\t\t\tif(!ok) return false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n#endif\n\t\treturn true;\n\t\t//TODO3: measure time more intelligently, like in MatchScope, but maybe give more time.\n\t}\n\t\n\tinternal bool HasFunc_ => _funcBefore != null || _funcAfter != null;\n\t\n\t//probably not useful. Or also need a property for eg HotkeyTriggers in derived classes.\n\t///// <summary>\n\t///// The <see cref=\"ActionTriggers\"/> instance to which this trigger belongs.\n\t///// </summary>\n\t//public ActionTriggers Triggers => triggers;\n\t\n\t/// <summary>\n\t/// Gets or sets whether this trigger is disabled.\n\t/// Does not depend on <see cref=\"ActionTriggers.Disabled\"/>, <see cref=\"ActionTriggers.DisabledEverywhere\"/>, <see cref=\"EnabledAlways\"/>.\n\t/// </summary>\n\tpublic bool Disabled { get; set; }\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if <see cref=\"Disabled\"/>; also if <see cref=\"ActionTriggers.Disabled\"/> or <see cref=\"ActionTriggers.DisabledEverywhere\"/>, unless <see cref=\"EnabledAlways\"/>.\n\t/// </summary>\n\tpublic bool DisabledThisOrAll => Disabled || (!EnabledAlways && (triggers.Disabled | ActionTriggers.DisabledEverywhere));\n\t\n\t/// <summary>\n\t/// Gets or sets whether this trigger ignores <see cref=\"ActionTriggers.Disabled\"/> and <see cref=\"ActionTriggers.DisabledEverywhere\"/>.\n\t/// </summary>\n\t/// <remarks>\n\t/// When adding the trigger, this property is set to the value of <see cref=\"TriggerOptions.EnabledAlways\"/> at that time.\n\t/// </remarks>\n\tpublic bool EnabledAlways { get; set; }\n\t\n\t/// <summary>\n\t/// Starts the action like when its trigger is activated.\n\t/// </summary>\n\t/// <param name=\"args\"></param>\n\t/// <exception cref=\"InvalidOperationException\">Called before or after <see cref=\"ActionTriggers.Run\"/>.</exception>\n\t/// <remarks>\n\t/// This function must be called while the main triggers thread is in <see cref=\"ActionTriggers.Run\"/>, for example from another trigger action. It is asynchronous (does not wait).\n\t/// If called from a trigger action (hotkey etc), make sure this action runs in another thread or can be queued. Else both actions cannot run simultaneously.\n\t/// </remarks>\n\tpublic void RunAction(TriggerArgs args) {\n\t\ttriggers.ThrowIfNotRunning_();\n\t\tif (triggers.IsMainThread) {\n\t\t\ttriggers.RunAction_(this, args);\n\t\t} else {\n\t\t\ttriggers.SendMsg_(false, () => triggers.RunAction_(this, args));\n\t\t}\n\t}\n}\n\n/// <summary>\n/// Base of trigger action argument classes of all trigger types.\n/// </summary>\npublic abstract class TriggerArgs {\n\t/// <summary>\n\t/// Gets the trigger as <see cref=\"ActionTrigger\"/> (the base class of all trigger type classes).\n\t/// </summary>\n\tpublic abstract ActionTrigger TriggerBase { get; }\n\t\n\t/// <summary>\n\t/// Disables the trigger. Enables later when the toolbar is closed.\n\t/// Use to implement single-instance toolbars.\n\t/// </summary>\n\tpublic void DisableTriggerUntilClosed(toolbar t) {\n\t\tTriggerBase.Disabled = true;\n\t\tt.Closed += () => TriggerBase.Disabled = false;\n\t}\n}\n\n/// <summary>\n/// Allows to specify working windows for multiple triggers of these types: hotkey, autotext, mouse.\n/// </summary>\n/// <example>\n/// Note: the <c>Triggers</c> in examples is a field or property like <c>readonly ActionTriggers Triggers = new();</c>.\n/// <code><![CDATA[\n/// Triggers.Hotkey[\"Ctrl+K\"] = o => print.it(\"this trigger works with all windows\");\n/// Triggers.Of.Window(\"* Notepad\"); //specifies a working window for triggers added afterwards\n/// Triggers.Hotkey[\"Ctrl+F11\"] = o => print.it(\"this trigger works only when a Notepad window is active\");\n/// Triggers.Hotkey[\"Ctrl+F12\"] = o => print.it(\"this trigger works only when a Notepad window is active\");\n/// var chrome = Triggers.Of.Window(\"* Chrome\"); //specifies another working window for triggers added afterwards\n/// Triggers.Hotkey[\"Ctrl+F11\"] = o => print.it(\"this trigger works only when a Chrome window is active\");\n/// Triggers.Hotkey[\"Ctrl+F12\"] = o => print.it(\"this trigger works only when a Chrome window is active\");\n/// Triggers.Of.AllWindows(); //let triggers added afterwards work with all windows\n/// Triggers.Mouse[TMEdge.RightInTop25] = o => print.it(\"this trigger works with all windows\");\n/// Triggers.Of.Again(chrome); //sets a previously specified working window for triggers added afterwards\n/// Triggers.Mouse[TMEdge.RightInBottom25] = o => print.it(\"this trigger works only when a Chrome window is active\");\n/// Triggers.Mouse[TMMove.DownUp] = o => print.it(\"this trigger works only when a Chrome window is active\");\n/// Triggers.Mouse[TMClick.Middle] = o => print.it(\"this trigger works only when the mouse is in a Chrome window\");\n/// Triggers.Mouse[TMWheel.Forward] = o => print.it(\"this trigger works only when the mouse is in a Chrome window\");\n/// Triggers.Run();\n/// ]]></code>\n/// </example>\npublic class TriggerScopes {\n\tinternal TriggerScopes() { }\n\t\n\tinternal TriggerScope Current_ { get; private set; }\n\t\n\t//rejected. More confusing than useful.\n\t///// <summary>\n\t///// Sets the scope that was active before the last call to any \"set scope\" function.\n\t///// </summary>\n\t//public void PreviousScope() => Current = _previous;\n\t//internal TriggerScope Current_ { get => _current; private set { _previous = _current; _current = value; } }\n\t//TriggerScope _current, _previous;\n\t\n\t/// <summary>\n\t/// Sets scope \"all windows\" again. Hotkey, autotext and mouse triggers added afterwards will work with all windows.\n\t/// </summary>\n\t/// <remarks>\n\t/// Example in class help.\n\t/// </remarks>\n\tpublic void AllWindows() => Current_ = null;\n\t\n\t/// <summary>\n\t/// Sets (reuses) a previously specified scope.\n\t/// </summary>\n\t/// <remarks>\n\t/// Example in class help.\n\t/// </remarks>\n\t/// <param name=\"scope\">The return value of function <see cref=\"Window\"/>, <see cref=\"NotWindow\"/>, <see cref=\"Windows\"/> or <see cref=\"NotWindows\"/>.</param>\n\tpublic void Again(TriggerScope scope) => Current_ = scope;\n\t\n\t/// <summary>\n\t/// Sets scope \"only this window\". Hotkey, autotext and mouse triggers added afterwards will work only when the specified window is active.\n\t/// </summary>\n\t/// <returns>Returns an object that can be later passed to <see cref=\"Again\"/> to reuse this scope.</returns>\n\t/// <example><see cref=\"TriggerScopes\"/></example>\n\t/// <inheritdoc cref=\"wnd.find\" path=\"//param|//exception\"/>\n\tpublic TriggerScope Window(\n\t\t[ParamString(PSFormat.Wildex)] string name = null,\n\t\t[ParamString(PSFormat.Wildex)] string cn = null,\n\t\t[ParamString(PSFormat.Wildex)] WOwner of = default,\n\t\tFunc<wnd, bool> also = null, WContains contains = default)\n\t\t=> _Window(false, name, cn, of, also, contains);\n\t\n\t/// <summary>\n\t/// Sets scope \"not this window\". Hotkey, autotext and mouse triggers added afterwards will not work when the specified window is active.\n\t/// </summary>\n\t/// <inheritdoc cref=\"Window(string, string, WOwner, Func{wnd, bool}, WContains)\"/>\n\tpublic TriggerScope NotWindow(\n\t\t[ParamString(PSFormat.Wildex)] string name = null,\n\t\t[ParamString(PSFormat.Wildex)] string cn = null,\n\t\t[ParamString(PSFormat.Wildex)] WOwner of = default,\n\t\tFunc<wnd, bool> also = null, WContains contains = default)\n\t\t=> _Window(true, name, cn, of, also, contains);\n\t\n\tTriggerScope _Window(bool not, string name, string cn, WOwner of, Func<wnd, bool> also, WContains contains)\n\t\t=> _Add(not, new wndFinder(name, cn, of, 0, also, contains));\n\t\n\t/// <summary>\n\t/// Sets scope \"only this window\". Hotkey, autotext and mouse triggers added afterwards will work only when the specified window is active.\n\t/// </summary>\n\t/// <returns>Returns an object that can be later passed to <see cref=\"Again\"/> to reuse this scope.</returns>\n\tpublic TriggerScope Window(wndFinder f)\n\t\t=> _Add(false, f);\n\t\n\t/// <summary>\n\t/// Sets scope \"not this window\". Hotkey, autotext and mouse triggers added afterwards will not work when the specified window is active.\n\t/// </summary>\n\t/// <returns>Returns an object that can be later passed to <see cref=\"Again\"/> to reuse this scope.</returns>\n\tpublic TriggerScope NotWindow(wndFinder f)\n\t\t=> _Add(true, f);\n\t\n\t//rejected. May be used incorrectly. Rare. When really need, can use the 'also' parameter.\n\t///// <summary>\n\t///// Sets scope \"only this window\". Hotkey, autotext and mouse triggers added afterwards will work only when the specified window is active.\n\t///// </summary>\n\t///// <returns>Returns an object that can be later passed to <see cref=\"Again\"/> to reuse this scope.</returns>\n\t///// <exception cref=\"AuWndException\">Invalid window handle.</exception>\n\t//public TriggerScope Window(wnd w)\n\t//\t=> _Add(false, w);\n\t\n\t///// <summary>\n\t///// Sets scope \"not this window\". Hotkey, autotext and mouse triggers added afterwards will not work when the specified window is active.\n\t///// </summary>\n\t///// <returns>Returns an object that can be later passed to <see cref=\"Again\"/> to reuse this scope.</returns>\n\t///// <exception cref=\"AuWndException\">Invalid window handle.</exception>\n\t//public TriggerScope NotWindow(wnd w)\n\t//\t=> _Add(true, w);\n\t\n\t/// <summary>\n\t/// Sets scope \"only these windows\". Hotkey, autotext and mouse triggers added afterwards will work only when one of the specified windows is active.\n\t/// </summary>\n\t/// <returns>Returns an object that can be later passed to <see cref=\"Again\"/> to reuse this scope.</returns>\n\t/// <param name=\"any\">Specifies windows, like <c>new(\"Window1\"), new(\"Window2\")</c>.</param>\n\tpublic TriggerScope Windows(params wndFinder[] any)\n\t\t=> _Add(false, any);\n\t\n\t/// <summary>\n\t/// Sets scope \"not these windows\". Hotkey, autotext and mouse triggers added afterwards will not work when one of the specified windows is active.\n\t/// </summary>\n\t/// <returns>Returns an object that can be later passed to <see cref=\"Again\"/> to reuse this scope.</returns>\n\t/// <param name=\"any\">Specifies windows, like <c>new(\"Window1\"), new(\"Window2\")</c>.</param>\n\tpublic TriggerScope NotWindows(params wndFinder[] any)\n\t\t=> _Add(true, any);\n\t\n\tTriggerScope _Add(bool not, wndFinder f) {\n\t\tNot_.Null(f);\n\t\tUsed_ = true;\n\t\treturn Current_ = new TriggerScope(f, not);\n\t}\n\t\n\tTriggerScope _Add(bool not, wndFinder[] a) {\n\t\tif (a.Length == 1) return _Add(not, a[0]);\n\t\tforeach (var v in a) if (v == null) throw new ArgumentNullException();\n\t\tUsed_ = true;\n\t\treturn Current_ = new TriggerScope(a, not);\n\t}\n\t\n\tinternal bool Used_ { get; private set; }\n}\n\n/// <summary>\n/// A trigger scope returned by functions like <see cref=\"TriggerScopes.Window\"/> and used with <see cref=\"TriggerScopes.Again\"/>.\n/// </summary>\n/// <example>See <see cref=\"TriggerScopes\"/>.</example>\npublic class TriggerScope {\n\tinternal readonly object o; //wndFinder, wndFinder[]\n\tinternal readonly bool not;\n\tinternal int perfTime;\n\t\n\tinternal TriggerScope(object o, bool not) {\n\t\tthis.o = o;\n\t\tthis.not = not;\n\t}\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if the window matches this scope.\n\t/// </summary>\n\tpublic bool Match(wnd w, WFCache cache) {\n\t\tbool yes = false;\n\t\tif (!w.Is0) {\n\t\t\tswitch (o) {\n\t\t\tcase wndFinder f:\n\t\t\t\tyes = f.IsMatch(w, cache);\n\t\t\t\tbreak;\n\t\t\tcase wndFinder[] a:\n\t\t\t\tforeach (var v in a) {\n\t\t\t\t\tif (yes = v.IsMatch(w, cache)) break;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn yes ^ not;\n\t}\n}\n\n/// <summary>\n/// Allows to define custom scopes/contexts/conditions for triggers.\n/// </summary>\n/// <remarks>\n/// Similar to <see cref=\"TriggerScopes\"/> (code like <c>Triggers.Of.Window(...);</c>), but allows to define any scope/condition/etc, not just the active window.\n/// \n/// To define a scope, you create a callback function (CF) that checks some conditions and returns <c>true</c> to allow the trigger action to run or <c>false</c> to not allow. Assign the CF to some property of this class and then add the trigger, like in the examples below. The CF will be assigned to the trigger and called when need.\n/// \n/// You may ask: why to use CF when the trigger action (TA) can do the same?\n/// 1. CF runs synchronously; if it returns <c>false</c>, the trigger key or mouse button message is passed to other triggers, hooks and apps. TA cannot do it reliably; it runs asynchronously, and the message is already stealed from other apps/triggers/hooks.\n/// 2. CF is faster to call. It is simply called in the same thread that processes trigger messages. TA usually runs in another thread.\n/// 3. A CF can be assigned to multiple triggers with a single line of code. Don't need to add the same code in all trigger actions.\n/// \n/// A trigger can have up to 4 CF delegates and a window scope (<c>Triggers.Of...</c>). They are called in this order: CF assigned through <see cref=\"FollowingTriggersBeforeWindow\"/>, <see cref=\"NextTriggerBeforeWindow\"/>, window scope, <see cref=\"NextTrigger\"/>, <see cref=\"FollowingTriggers\"/>. The <c>NextX</c> properties assign the CF to the next single trigger. The <c>FollowingX</c> properties assign the CF to all following triggers until you assign another CF or <c>null</c>. If several are assigned, the trigger action runs only if all CF return <c>true</c> and the window scope matches. The <c>XBeforeWindow</c> properties are used only with hotkey, autotext and mouse triggers.\n/// \n/// All CF must be as fast as possible. Slow CF can make triggers slower (or even all keyboard/mouse input); also may cause warnings and trigger failures. A big problem is the low-level hooks timeout that Windows applies to trigger hooks; see <see cref=\"More.WindowsHook.LowLevelHooksTimeout\"/>. A related problem - slow JIT and loading of assemblies, which can make the CF too slow the first time; in some rare cases may even need to preload assemblies or pre-JIT functions to avoid the timeout warning.\n///\n/// In CF never use functions that generate keyboard or mouse events or activate windows.\n/// </remarks>\n/// <example>\n/// Note: the <c>Triggers</c> in examples is a field or property like <c>readonly ActionTriggers Triggers = new();</c>.\n/// <code><![CDATA[\n/// //examples of assigning a callback function (CF) to a single trigger\n/// Triggers.FuncOf.NextTrigger = o => keys.isCapsLock; //o => keys.isCapsLock is the callback function (lambda)\n/// Triggers.Hotkey[\"Ctrl+K\"] = o => print.it(\"action: Ctrl+K while CapsLock is on\");\n/// Triggers.FuncOf.NextTrigger = o => { var v = o as HotkeyTriggerArgs; print.it($\"func: mod={v.Mod}\"); return mouse.isPressed(MButtons.Left); };\n/// Triggers.Hotkey[\"Ctrl+Shift?+B\"] = o => print.it(\"action: mouse left button + Ctrl+B or Ctrl+Shift+B\");\n/// \n/// //examples of assigning a CF to multiple triggers\n/// Triggers.FuncOf.FollowingTriggers = o => { var v = o as HotkeyTriggerArgs; print.it(\"func\", v); return true; };\n/// Triggers.Hotkey[\"Ctrl+F8\"] = o => print.it(\"action: \" + o);\n/// Triggers.Hotkey[\"Ctrl+F9\"] = o => print.it(\"action: \" + o);\n/// Triggers.FuncOf.FollowingTriggers = null; //stop assigning the CF to triggers added afterwards\n/// \n/// //sometimes all work can be done in CF and you don't need the trigger action\n/// Triggers.FuncOf.NextTrigger = o => { var v = o as HotkeyTriggerArgs; print.it(\"func: \" + v); return true; };\n/// Triggers.Hotkey[\"Ctrl+F12\"] = null;\n/// \n/// Triggers.Run();\n/// ]]></code>\n/// </example>\npublic class TriggerFuncs {\n\tinternal TriggerFuncs() { }\n\t\n\tinternal Dictionary<TFunc, TriggerFunc> perfDict = new Dictionary<TFunc, TriggerFunc>();\n\t\n\t//internal bool Used_ { get; private set; }\n\t\n\tinternal TFunc nextAfter, nextBefore, commonAfter, commonBefore;\n\t\n\t/// <summary>\n\t/// Sets callback function for the next added trigger.\n\t/// Called after evaluating the window scope (if any).\n\t/// </summary>\n\tpublic TFunc NextTrigger {\n\t\tget => nextAfter;\n\t\tset => nextAfter = _Func(value);\n\t}\n\t\n\t/// <summary>\n\t/// Sets callback function for the next added trigger (except window triggers).\n\t/// Called before evaluating the window scope (if any).\n\t/// </summary>\n\tpublic TFunc NextTriggerBeforeWindow {\n\t\tget => nextBefore;\n\t\tset => nextBefore = _Func(value);\n\t}\n\t\n\t/// <summary>\n\t/// Sets callback function for triggers added afterwards.\n\t/// Called after evaluating the window scope (if any).\n\t/// </summary>\n\tpublic TFunc FollowingTriggers {\n\t\tget => commonAfter;\n\t\tset => commonAfter = _Func(value);\n\t}\n\t\n\t/// <summary>\n\t/// Sets callback function for triggers added afterwards (except window triggers).\n\t/// Called before evaluating the window scope (if any).\n\t/// </summary>\n\tpublic TFunc FollowingTriggersBeforeWindow {\n\t\tget => commonBefore;\n\t\tset => commonBefore = _Func(value);\n\t}\n\t\n\tTFunc _Func(TFunc f) {\n\t\t//if(f != null) Used = true;\n\t\treturn f;\n\t}\n\t\n\t/// <summary>\n\t/// Clears all properties (sets = <c>null</c>).\n\t/// </summary>\n\tpublic void Reset() {\n\t\tnextAfter = null;\n\t\tnextBefore = null;\n\t\tcommonAfter = null;\n\t\tcommonBefore = null;\n\t}\n}\n\nclass TriggerFunc {\n\tinternal TFunc f;\n\tinternal int perfTime;\n}\n\n/// <summary>\n/// Type of functions used with class <see cref=\"TriggerFuncs\"/> to define custom scope for triggers.\n/// </summary>\n/// <param name=\"args\">Trigger action arguments. Example: <see cref=\"TriggerFuncs\"/>.</param>\n/// <returns>Return <c>true</c> to run the trigger action, or <c>false</c> to not run.</returns>\npublic delegate bool TFunc(TriggerArgs args);\n"
  },
  {
    "path": "Au/Triggers/Triggers.cs",
    "content": "namespace Au.Triggers;\n\n/// <summary>\n/// The main class of action triggers.\n/// </summary>\n/// <remarks>\n/// This class manages action triggers. Action triggers are used to call functions (aka <i>trigger actions</i>) in a running script in response to events such as hotkey, typed text, mouse action, activated window. To launch scripts are used other ways: manually, at startup, command line, <see cref=\"script.run\"/>, output link.\n/// \n/// If your script has a variable, field or property like <c>ActionTriggers Triggers = new();</c>, through it you can access all trigger types (hotkey, window, etc) and add triggers to them.\n/// \n/// Code syntax to add an action trigger:\n/// <code>Triggers.TriggerType[parameters] = action;</code>\n/// Examples:\n/// <code><![CDATA[\n/// Triggers.Hotkey[\"Ctrl+K\"] = o => print.it(o);\n/// Triggers.Hotkey[\"Ctrl+Shift+K\"] = o => {\n/// \tprint.it(\"This is a trigger action (lambda function).\");\n/// \tprint.it($\"It runs when you press {o}.\");\n/// };\n/// Triggers.Run();\n/// ]]></code>\n/// \n/// Also you can set options (<see cref=\"TriggerOptions\"/>), window scopes (<see cref=\"TriggerScopes\"/>) and custom scopes (<see cref=\"TriggerFuncs\"/>) for triggers added afterwards.\n/// \n/// Finally call <see cref=\"Run\"/> or <see cref=\"RunThread\"/>. It runs all the time and launches trigger actions (functions) when need. Actions run in other thread(s) by default.\n/// \n/// To quickly restart the script when editing, click the <b>Run</b> button.\n/// \n/// Avoid multiple scripts with triggers. Each running instance uses some CPU. All triggers should be in single script, if possible. It's OK to run additional scripts temporarily, for example to test new triggers without restarting the main script. From trigger actions you can call <see cref=\"script.run\"/> to run other scripts in new process; see example.\n/// \n/// Trigger actions may not inherit <see cref=\"opt\"/> options that are set before adding triggers. The example shows how to correctly set <see cref=\"opt\"/> options for multiple actions. Also you can set them in action code. Next action running in the same thread will not inherit <see cref=\"opt\"/> options set by previous action.\n/// </remarks>\n/// <example>\n/// This is a single script with many action triggers.\n/// <code><![CDATA[\n/// using Au.Triggers;\n/// \n/// ActionTriggers Triggers = new();\n/// //readonly ActionTriggers Triggers = new(); //or add this field if you'll use triggers in a class\n/// \n/// //you can set options for triggers added afterwards\n/// Triggers.Options.Thread(0, 500);\n/// \n/// //you can use variables if don't want to type \"Triggers.Hotkey\" etc for each trigger\n/// var hk = Triggers.Hotkey;\n/// var mouse = Triggers.Mouse;\n/// var win = Triggers.Window;\n/// var tt = Triggers.Autotext;\n/// \n/// //hotkey triggers\n/// \n/// hk[\"Ctrl+K\"] = o => print.it(o); //it means: execute code \"o => print.it(o)\" when I press Ctrl+K\n/// hk[\"Ctrl+Shift+F11\"] = o => {\n/// \tprint.it(o);\n/// \tvar w1 = wnd.findOrRun(\"* Notepad\", run: () => run.it(folders.System + \"notepad.exe\"));\n/// \tkeys.sendt(\"text\");\n/// \tw1.Close();\n/// };\n/// hk[\"Win+Alt+K\"] = o => script.run(\"Example.cs\"); //run another script in new process\n/// \n/// //triggers that work only with some windows\n/// \n/// Triggers.Of.Window(\"* Chrome\", \"Chrome_WidgetWin_1\"); //let the following triggers work only when a Chrome window is active\n/// hk[\"Ctrl+F5\"] = o => print.it(o, o.Window);\n/// hk[\"Ctrl+F6\"] = o => print.it(o, o.Window);\n/// \n/// var notepad = Triggers.Of.Window(\"* Notepad\"); //let the following triggers work only when a Notepad window is active\n/// hk[\"Ctrl+F5\"] = o => print.it(o, o.Window);\n/// hk[\"Ctrl+F6\"] = o => print.it(o, o.Window);\n/// \n/// Triggers.Of.AllWindows(); //let the following triggers work with all windows\n/// \n/// //mouse triggers\n/// \n/// mouse[TMClick.Right, \"Ctrl+Shift\", TMFlags.ButtonModUp] = o => print.it(o);\n/// mouse[TMEdge.RightInCenter50] = o => { print.it(o); dialog.show(\"Bang!\", x: Coord.Max); };\n/// mouse[TMMove.LeftRightInCenter50] = o => wnd.switchActiveWindow();\n/// \n/// Triggers.FuncOf.NextTrigger = o => keys.isScrollLock; //example of a custom scope (aka context, condition)\n/// mouse[TMWheel.Forward] = o => print.it($\"{o} while ScrollLock is on\");\n/// \n/// Triggers.Of.Again(notepad); //let the following triggers work only when a Notepad window is active\n/// mouse[TMMove.LeftRightInBottom25] = o => { print.it(o); o.Window.Close(); };\n/// Triggers.Of.AllWindows();\n/// \n/// //window triggers. Note: window triggers don't depend on Triggers.Of.\n/// \n/// win[TWEvent.ActiveNew, \"* Notepad\", \"Notepad\"] = o => print.it(\"opened Notepad window\");\n/// win[TWEvent.ActiveNew, \"Notepad\", \"#32770\", contains: \"Do you want to save *\"] = o => {\n/// \tprint.it(\"opened Notepad's 'Do you want to save' dialog\");\n/// \t//keys.send(\"Alt+S\"); //click the Save button\n/// };\n/// \n/// //autotext triggers\n/// \n/// tt[\"los\"] = o => o.Replace(\"Los Angeles\");\n/// tt[\"WIndows\", TAFlags.MatchCase] = o => o.Replace(\"Windows\");\n/// tt.DefaultPostfixType = TAPostfix.None;\n/// tt[\"<b>\"] = o => o.Replace(\"<b>[[|]]</b>\");\n/// tt[\"#file\"] = o => {\n/// \to.Replace(\"\");\n/// \tvar fd = new FileOpenSaveDialog();\n/// \tif(fd.ShowOpen(out string file)) keys.sendt(file);\n/// };\n/// tt.DefaultPostfixType = default;\n/// \n/// //shorter auto-replace code\n/// \n/// var ts = Triggers.Autotext.SimpleReplace;\n/// ts[\"#so\"] = \"Some text\"; //the same as tt[\"#so\"] = o => o.Replace(\"Some text\");\n/// ts[\"#mo\"] = \"More text\";\n/// \n/// //how to set opt options for trigger actions\n/// \n/// Triggers.Options.BeforeAction = o => { opt.key.TextHow = OKeyText.Paste; }; //sets opt before executing an action\n/// ts[\"#p1\"] = \"text 1\";\n/// ts[\"#p2\"] = \"text 2\";\n/// Triggers.Options.BeforeAction = null;\n/// \n/// //initial opt for ALL trigger actions also can be set ONCE when adding triggers. Use Triggers.Options.BeforeAction if want to override some options for some trigger actions.\n/// \n/// opt.key.TextHow = OKeyText.Paste;\n/// //then add all triggers\n/// \n/// //how to stop and disable/enable triggers\n/// \n/// hk[\"Ctrl+Alt+Q\"] = o => Triggers.Stop(); //let Triggers.Run() end its work and return\n/// hk.Last.EnabledAlways = true;\n/// \n/// hk[\"Ctrl+Alt+D\"] = o => Triggers.Disabled ^= true; //disable/enable triggers here\n/// hk.Last.EnabledAlways = true;\n/// \n/// hk[\"Ctrl+Alt+Win+D\"] = o => ActionTriggers.DisabledEverywhere ^= true; //disable/enable triggers in all processes\n/// hk.Last.EnabledAlways = true;\n/// \n/// hk[\"Ctrl+F7\"] = o => print.it(\"This trigger can be disabled/enabled with Ctrl+F8.\");\n/// var t1 = hk.Last;\n/// hk[\"Ctrl+F8\"] = o => t1.Disabled ^= true; //disable/enable a trigger\n/// \n/// //finally call Triggers.Run(). Without it the triggers won't work.\n/// Triggers.Run();\n/// //Triggers.Run returns when is called Triggers.Stop (see the \"Ctrl+Alt+Q\" trigger above).\n/// print.it(\"called Triggers.Stop\");\n/// ]]></code>\n/// </example>\npublic partial class ActionTriggers {\n\treadonly ITriggers[] _t;\n\tITriggers this[TriggerType e] => _t[(int)e];\n\t\n\t/// <summary>\n\t/// Initializes a new instance of this class.\n\t/// </summary>\n\tpublic ActionTriggers() {\n\t\t_t = new ITriggers[4];\n\t\tscopes_ = new TriggerScopes();\n\t\tfuncs_ = new TriggerFuncs();\n\t\toptions_ = new TriggerOptions();\n\t}\n\t\n\t//public TriggerScopes Of {\n\t//\tget {\n\t//\t\tif(_test == false){\n\t//\t\t\tDebug_.MemorySetAnchor_();\n\t//\t\t\ttimer.after(500, _ => Debug_.MemoryPrint_());\n\t//\t\t}\n\t\n\t//\t\tvar k=new StackFrame(1, true);\n\t//\t\t//new StackTrace(1, true);\n\t//\t\tvar s = k.GetFileName();\n\t\n\t//\t\t//if(_test == false) _s1 = s; else print.it(ReferenceEquals(s, _s1));\n\t\n\t//\t\t\t _test = true;\n\t\n\t//\t\treturn scopes;\n\t//\t}\n\t//}\n\t//static bool _test;\n\t//static string _s1;\n\t\n\t/// <summary>\n\t/// Allows to set window scopes (working windows) for triggers.\n\t/// </summary>\n\t/// <remarks>Examples: <see cref=\"TriggerScopes\"/>, <see cref=\"ActionTriggers\"/>.</remarks>\n\tpublic TriggerScopes Of => scopes_;\n\tinternal readonly TriggerScopes scopes_;\n\t\n\t/// <summary>\n\t/// Allows to set custom scopes/contexts/conditions for triggers.\n\t/// </summary>\n\t/// <remarks>More info and examples: <see cref=\"TriggerFuncs\"/>, <see cref=\"ActionTriggers\"/>.</remarks>\n\tpublic TriggerFuncs FuncOf => funcs_;\n\tinternal readonly TriggerFuncs funcs_;\n\t\n\t/// <summary>\n\t/// Allows to set some options for multiple triggers and their actions.\n\t/// </summary>\n\t/// <remarks>More info and examples: <see cref=\"TriggerOptions\"/>, <see cref=\"ActionTriggers\"/>.</remarks>\n\tpublic TriggerOptions Options => options_;\n\tinternal readonly TriggerOptions options_;\n\t\n\t/// <summary>\n\t/// Clears all options (of <see cref=\"Options\"/>, <see cref=\"Of\"/>, <see cref=\"FuncOf\"/>, <see cref=\"Autotext\"/>).\n\t/// </summary>\n\tpublic void ResetOptions() {\n\t\tOf.AllWindows();\n\t\tFuncOf.Reset();\n\t\tOptions.Reset();\n\t\tAutotext.ResetOptions();\n\t}\n\t\n\tITriggers _Get(TriggerType e) {\n\t\tint i = (int)e;\n\t\tvar t = _t[i];\n\t\tif (t == null) {\n\t\t\tswitch (e) {\n\t\t\tcase TriggerType.Hotkey: t = new HotkeyTriggers(this); break;\n\t\t\tcase TriggerType.Autotext: t = new AutotextTriggers(this); break;\n\t\t\tcase TriggerType.Mouse: t = new MouseTriggers(this); break;\n\t\t\tcase TriggerType.Window: t = new WindowTriggers(this); break;\n\t\t\tdefault: Debug.Assert(false); break;\n\t\t\t}\n\t\t\t_t[i] = t;\n\t\t}\n\t\treturn t;\n\t}\n\t\n\t/// <summary>\n\t/// Hotkey triggers.\n\t/// </summary>\n\t/// <example>See <see cref=\"ActionTriggers\"/>.</example>\n\tpublic HotkeyTriggers Hotkey => _Get(TriggerType.Hotkey) as HotkeyTriggers;\n\t\n\t/// <summary>\n\t/// Autotext triggers.\n\t/// </summary>\n\t/// <example>See <see cref=\"ActionTriggers\"/>.</example>\n\tpublic AutotextTriggers Autotext => _Get(TriggerType.Autotext) as AutotextTriggers;\n\t\n\t/// <summary>\n\t/// Mouse triggers.\n\t/// </summary>\n\t/// <example>See <see cref=\"ActionTriggers\"/>.</example>\n\tpublic MouseTriggers Mouse => _Get(TriggerType.Mouse) as MouseTriggers;\n\t\n\t/// <summary>\n\t/// Window triggers.\n\t/// </summary>\n\t/// <example>See <see cref=\"ActionTriggers\"/>.</example>\n\tpublic WindowTriggers Window => _Get(TriggerType.Window) as WindowTriggers;\n\t\n\t/// <summary>\n\t/// Makes triggers alive.\n\t/// </summary>\n\t/// <remarks>\n\t/// This function monitors hotkeys, activated windows and other events. When an event matches an added trigger, launches the trigger's action, which runs in other thread by default.\n\t/// Does not return immediately. Runs until this process is terminated or <see cref=\"Stop\"/> called.\n\t/// </remarks>\n\t/// <example>See <see cref=\"ActionTriggers\"/>.</example>\n\t/// <exception cref=\"InvalidOperationException\">Already running.</exception>\n\t/// <exception cref=\"AuException\">Something failed.</exception>\n\tpublic unsafe void Run() {\n\t\t//Debug_.PrintLoadedAssemblies(true, true, true);\n\t\t\n\t\tThrowIfRunning_();\n\t\t\n\t\t//bool haveTriggers = false;\n\t\tHooksThread.UsedEvents hookEvents = 0;\n\t\t_windowTriggers = null;\n\t\tfor (int i = 0; i < _t.Length; i++) {\n\t\t\tvar t = _t[i];\n\t\t\tif (t == null || !t.HasTriggers) continue;\n\t\t\t//haveTriggers = true;\n\t\t\tswitch ((TriggerType)i) {\n\t\t\tcase TriggerType.Hotkey: hookEvents |= HooksThread.UsedEvents.Keyboard; break;\n\t\t\tcase TriggerType.Autotext: hookEvents |= HooksThread.UsedEvents.Keyboard | HooksThread.UsedEvents.Mouse; break;\n\t\t\tcase TriggerType.Mouse: hookEvents |= (t as MouseTriggers).UsedHookEvents_; break;\n\t\t\tcase TriggerType.Window: _windowTriggers = t as WindowTriggers; break;\n\t\t\t}\n\t\t}\n\t\t//print.it(haveTriggers, (uint)llHooks);\n\t\t//if(!haveTriggers) return; //no. The message loop may be used for toolbars etc.\n\t\t\n\t\tif (!s_wasRun) {\n\t\t\ts_wasRun = true;\n\t\t\tWndUtil.RegisterWindowClass(c_cn);\n\t\t}\n\t\t_wMsg = WndUtil.CreateMessageOnlyWindow(_WndProc, c_cn);\n\t\t_mainThreadId = Api.GetCurrentThreadId();\n\t\t_winTimerPeriod = 0;\n\t\t_winTimerLastTime = 0;\n\t\t\n\t\tif (hookEvents != 0) {\n\t\t\t//prevent big delay (JIT) later on first LL hook event while hook proc waits\n\t\t\tif (!s_wasKM) {\n\t\t\t\ts_wasKM = true;\n\t\t\t\tThreadPool.QueueUserWorkItem(_ => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\t//using var p1 = perf.local();\n\t\t\t\t\t\tnew wndFinder(\"*a\").IsMatch(wnd.getwnd.root); //if used window scopes etc\n\t\t\t\t\t\t_ = WindowsHook.LowLevelHooksTimeout; //slow JIT of registry functions\n\t\t\t\t\t\tJit_.Compile(typeof(ActionTriggers), nameof(_WndProc), nameof(_KeyMouseEvent));\n\t\t\t\t\t\tJit_.Compile(typeof(TriggerHookContext), nameof(TriggerHookContext.InitContext), nameof(TriggerHookContext.PerfEnd), nameof(TriggerHookContext.PerfWarn));\n\t\t\t\t\t\tJit_.Compile(typeof(ActionTrigger), nameof(ActionTrigger.MatchScopeWindowAndFunc_));\n\t\t\t\t\t\tJit_.Compile(typeof(HotkeyTriggers), nameof(HotkeyTriggers.HookProc));\n\t\t\t\t\t\tAutotextTriggers.JitCompile();\n\t\t\t\t\t\tMouseTriggers.JitCompile();\n\t\t\t\t\t}\n\t\t\t\t\tcatch (Exception ex) { Debug_.Print(ex); }\n\t\t\t\t});\n\t\t\t}\n\t\t\t\n\t\t\t_thc = new TriggerHookContext(this);\n\t\t\t\n\t\t\t_ht = new HooksThread(hookEvents, _wMsg);\n\t\t}\n\t\t\n\t\ttry {\n\t\t\t_evStop = Api.CreateEvent(false);\n\t\t\t_StartStopAll(true);\n\t\t\tIntPtr h = _evStop;\n\t\t\t_Wait(&h, 1);\n\t\t}\n\t\tfinally {\n\t\t\tif (hookEvents != 0) {\n\t\t\t\t_ht.Dispose(); _ht = null;\n\t\t\t}\n\t\t\tApi.DestroyWindow(_wMsg); _wMsg = default;\n\t\t\tStopping?.Invoke(this, EventArgs.Empty);\n\t\t\t_evStop.Dispose();\n\t\t\t_StartStopAll(false);\n\t\t\t_mainThreadId = 0;\n\t\t\t_threads?.Dispose(); _threads = null;\n\t\t}\n\t\t\n\t\tvoid _StartStopAll(bool start) {\n\t\t\tforeach (var t in _t) {\n\t\t\t\tif (t?.HasTriggers == true) t.StartStop(start);\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Executes <see cref=\"Run\"/> in new thread and waits like <see cref=\"wait.doEvents(int)\"/>.\n\t/// </summary>\n\tpublic void RunThread() {\n\t\tusing var th = run.thread(out _, out _, Run);\n\t\twait.forHandle(0, WHFlags.DoEvents, th.DangerousGetHandle());\n\t}\n\t\n\tint _mainThreadId;\n\twnd _wMsg;\n\tHooksThread _ht;\n\tTriggerHookContext _thc;\n\tstatic bool s_wasRun, s_wasKM;\n\tconst string c_cn = \"Au.Triggers.Hooks\";\n\t\n\tnint _WndProc(wnd w, int message, nint wParam, nint lParam) {\n\t\ttry {\n\t\t\tswitch (message) {\n\t\t\tcase Api.WM_USER + 1:\n\t\t\t\t//_ht.Return((int)wParam, false); //test speed without _KeyMouseEvent\n\t\t\t\t_KeyMouseEvent((int)wParam, (HooksThread.UsedEvents)lParam);\n\t\t\t\treturn 0;\n\t\t\t//case Api.WM_USER + 2: //rejected\n\t\t\t//\tShowTriggersListWindow((int)wParam);\n\t\t\t//\treturn 0;\n\t\t\tcase Api.WM_USER + 10: //run any action\n\t\t\t\tif(((GCHandle)lParam).Target is Action ac) {\n\t\t\t\t\t((GCHandle)lParam).Free();\n\t\t\t\t\tac();\n\t\t\t\t}\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t}\n\t\tcatch (Exception ex) { Debug_.Print(ex); return default; }\n\t\t\n\t\treturn Api.DefWindowProc(w, message, wParam, lParam);\n\t}\n\t\n\tunsafe void _KeyMouseEvent(int messageId, HooksThread.UsedEvents eventType) {\n\t\t//perf.first();\n\t\t//perf.next();\n\t\t_thc.InitContext();\n\t\t//perf.next();\n\t\tbool eat = false;\n\t\tif (eventType == HooksThread.UsedEvents.Keyboard) {\n\t\t\t//print.it(\"key\");\n\t\t\tif (!_ht.GetKeyData(messageId, out var data)) return;\n\t\t\tvar k = new HookData.Keyboard(null, (nint)(&data)); //TODO3: now probably can be simplified, because not using a server\n\t\t\t_thc.InitMod(k);\n\t\t\tif (this[TriggerType.Hotkey] is HotkeyTriggers tk) { //if not null\n\t\t\t\teat = tk.HookProc(k, _thc);\n\t\t\t}\n\t\t\tif (!eat /*&& _thc.trigger == null*/ && this[TriggerType.Autotext] is AutotextTriggers ta) {\n\t\t\t\tta.HookProc(k, _thc);\n\t\t\t}\n\t\t} else if (eventType == HooksThread.UsedEvents.MouseEdgeMove) {\n\t\t\t//print.it(\"edge/move\");\n\t\t\tif (this[TriggerType.Mouse] is MouseTriggers tm) {\n\t\t\t\tif (!_ht.GetEdgeMoveData(messageId, out var data)) return;\n\t\t\t\ttm.HookProcEdgeMove(data, _thc);\n\t\t\t}\n\t\t} else {\n\t\t\t//print.it(\"click/wheel\");\n\t\t\tif (this[TriggerType.Mouse] is MouseTriggers tm) {\n\t\t\t\tif (!_ht.GetClickWheelData(messageId, out var data, out int message)) return;\n\t\t\t\tvar k = new HookData.Mouse(null, message, (nint)(&data));\n\t\t\t\teat = tm.HookProcClickWheel(k, _thc);\n\t\t\t}\n\t\t}\n\t\t//perf.next();\n\t\t_thc.PerfWarn();\n\t\t//perf.next();\n\t\t\n\t\t//var mem = GC.GetTotalMemory(false);\n\t\t//if(mem != _debugMem && _debugMem != 0) print.it(mem - _debugMem);\n\t\t//_debugMem = mem;\n\t\t\n\t\tif (!_ht.Return(messageId, eat)) return;\n\t\t//perf.nw();\n\t\tif (_thc.trigger != null) RunAction_(_thc.trigger, _thc.args, _thc.muteMod);\n\t}\n\t\n\t//long _debugMem;\n\t\n\tinternal void RunAction_(ActionTrigger trigger, TriggerArgs args, int muteMod = 0) {\n\t\tif (trigger.action != null) {\n\t\t\t_threads ??= new TriggerActionThreads();\n\t\t\t_threads.Run(trigger, args, muteMod);\n\t\t} else Debug.Assert(muteMod == 0);\n\t}\n\tTriggerActionThreads _threads;\n\t\n\t/// <summary>\n\t/// Stops trigger engines and causes <see cref=\"Run\"/> to return.\n\t/// </summary>\n\t/// <remarks>\n\t/// Does not abort threads of trigger actions that are still running.\n\t/// </remarks>\n\t/// <example>\n\t/// Note: the <c>Triggers</c> in examples is a field or property like <c>readonly ActionTriggers Triggers = new();</c>.\n\t/// <code><![CDATA[\n\t/// Triggers.Hotkey[\"Ctrl+T\"] = o => print.it(\"Ctrl+T\");\n\t/// Triggers.Hotkey[\"Ctrl+Q\"] = o => { print.it(\"Ctrl+Q (stop)\"); Triggers.Stop(); };\n\t/// Triggers.Hotkey.Last.EnabledAlways = true;\n\t/// Triggers.Run();\n\t/// print.it(\"stopped\");\n\t/// ]]></code>\n\t/// </example>\n\tpublic void Stop() {\n\t\tApi.SetEvent(_evStop);\n\t}\n\tHandle_ _evStop;\n\t\n\t/// <summary>\n\t/// Occurs before <see cref=\"Run\"/> stops trigger engines and returns.\n\t/// </summary>\n\tpublic event EventHandler Stopping;\n\t\n\t/// <summary>\n\t/// True if executing <see cref=\"Run\"/>.\n\t/// </summary>\n\tinternal bool Running_ => !_evStop.Is0;\n\t\n\t/// <summary>\n\t/// Throws <see cref=\"InvalidOperationException\"/> if executing <see cref=\"Run\"/>.\n\t/// </summary>\n\tinternal void ThrowIfRunning_() {\n\t\tif (Running_) throw new InvalidOperationException(\"Must be before or after Run.\");\n\t}\n\t\n\t/// <summary>\n\t/// Throws <see cref=\"InvalidOperationException\"/> if not executing <see cref=\"Run\"/>.\n\t/// </summary>\n\tinternal void ThrowIfNotRunning_() {\n\t\tif (!Running_) throw new InvalidOperationException(\"Cannot be before or after Run.\");\n\t}\n\t\n\t/// <summary>\n\t/// Throws <see cref=\"InvalidOperationException\"/> if not thread of <see cref=\"Run\"/>.\n\t/// </summary>\n\tinternal void ThrowIfNotMainThread_() {\n\t\tif (!IsMainThread) throw new InvalidOperationException(\"Must be in thread of Run (for example in a FuncOf function).\");\n\t}\n\t\n\t/// <summary>\n\t/// Returns <i>true</i> in thread of <see cref=\"Run\"/>.\n\t/// </summary>\n\tinternal bool IsMainThread => Api.GetCurrentThreadId() == _mainThreadId;\n\t\n\t/// <summary>\n\t/// Gets or sets whether triggers of this <see cref=\"ActionTriggers\"/> instance are disabled.\n\t/// </summary>\n\t/// <remarks>\n\t/// Does not depend on <see cref=\"DisabledEverywhere\"/>.\n\t/// Does not end/pause threads of trigger actions.\n\t/// </remarks>\n\t/// <seealso cref=\"ActionTrigger.EnabledAlways\"/>\n\t/// <seealso cref=\"TriggerOptions.EnabledAlways\"/>\n\t/// <example>\n\t/// Note: the <c>Triggers</c> in examples is a field or property like <c>readonly ActionTriggers Triggers = new();</c>.\n\t/// <code><![CDATA[\n\t/// Triggers.Hotkey[\"Ctrl+T\"] = o => print.it(\"Ctrl+T\");\n\t/// Triggers.Hotkey[\"Ctrl+D\"] = o => { print.it(\"Ctrl+D (disable/enable)\"); Triggers.Disabled ^= true; }; //toggle\n\t/// Triggers.Hotkey.Last.EnabledAlways = true;\n\t/// Triggers.Run();\n\t/// ]]></code>\n\t/// </example>\n\tpublic bool Disabled { get; set; }\n\t\n\t/// <summary>\n\t/// Gets or sets whether triggers are disabled in all processes that use this library in this user session.\n\t/// </summary>\n\t/// <example>See <see cref=\"ActionTriggers\"/>.</example>\n\t/// <seealso cref=\"Disabled\"/>\n\t/// <seealso cref=\"TriggerOptions.EnabledAlways\"/>\n\tpublic static unsafe bool DisabledEverywhere {\n\t\tget => SharedMemory_.Ptr->triggers.disabled;\n\t\tset {\n\t\t\tif (value == DisabledEverywhere) return;\n\t\t\tSharedMemory_.Ptr->triggers.disabled = value;\n\t\t\tvar w = ScriptEditor.WndMsg_;\n\t\t\tif (!w.Is0) w.SendNotify(Api.WM_USER, 20); //update tray icon etc\n\t\t}\n\t}\n\t\n\t[StructLayout(LayoutKind.Sequential, Size = 16)] //note: this struct is in shared memory. Size must be same in all library versions.\n\tinternal struct SharedMemoryData_ {\n\t\tpublic bool disabled;\n\t\tpublic bool resetAutotext;\n\t}\n\t\n\t/// <summary>\n\t/// Sends a sync or async message to _wMsg (_WndProc in the triggers thread).\n\t/// </summary>\n\tinternal void SendMsg_(bool sync, int message, nint wParam = 0, nint lParam = 0) {\n\t\tif (sync) _wMsg.Send(message, wParam, lParam);\n\t\telse _wMsg.SendNotify(message, wParam, lParam);\n\t}\n\t\n\t/// <summary>\n\t/// Sends a sync or async message to _wMsg (_WndProc in the triggers thread).\n\t/// </summary>\n\tinternal void SendMsg_(bool sync, Action a) {\n\t\tSendMsg_(sync, Api.WM_USER + 10, 0, (nint)GCHandle.Alloc(a));\n\t}\n\t\n\tunsafe int _Wait(IntPtr* ha, int nh) {\n\t\tfor (; ; ) {\n\t\t\tint slice = -1;\n\t\t\tif (_winTimerPeriod > 0) {\n\t\t\t\tlong t = perf.ms;\n\t\t\t\tif (_winTimerLastTime == 0) _winTimerLastTime = t;\n\t\t\t\tint td = (int)(t - _winTimerLastTime);\n\t\t\t\tint period = _Period();\n\t\t\t\tif (td >= period - 5) {\n\t\t\t\t\t_winTimerLastTime = t;\n\t\t\t\t\t_windowTriggers?.Timer_();\n\t\t\t\t\tslice = _Period();\n\t\t\t\t} else slice = period - td;\n\t\t\t\t\n\t\t\t\tint _Period() => _winTimerPeriod / 15 * 15 + 10;\n\t\t\t\t\n\t\t\t\t//This code is a variable-frequency timer that uses less CPU than Windows timer.\n\t\t\t\t//\tNever mind: the timer does not work if user code creates a nested message loop in this thread. They should avoid it. It is documented, such functions must return ASAP.\n\t\t\t}\n\t\t\tvar k = Api.MsgWaitForMultipleObjectsEx(nh, ha, slice, Api.QS_ALLINPUT, Api.MWMO_ALERTABLE | Api.MWMO_INPUTAVAILABLE);\n\t\t\tif (k == nh) { //message, COM, hook, etc\n\t\t\t\tif (!wait.doEvents()) return -1;\n\t\t\t} else if (k is not (Api.WAIT_TIMEOUT or Api.WAIT_IO_COMPLETION)) return k; //signaled handle, abandoned mutex, WAIT_FAILED (-1)\n\t\t}\n\t}\n\tlong _winTimerLastTime;\n\tWindowTriggers _windowTriggers;\n\t\n\tinternal int WinTimerPeriod_ {\n\t\tget => _winTimerPeriod;\n\t\tset {\n\t\t\tlong t = perf.ms;\n\t\t\tint td = (int)(t - _winTimerLastTime);\n\t\t\tif (td > 10) _winTimerLastTime = t;\n\t\t\t_winTimerPeriod = value;\n\t\t}\n\t}\n\tint _winTimerPeriod;\n}\n\nenum TriggerType {\n\tHotkey,\n\tAutotext,\n\tMouse,\n\tWindow,\n}\n\ninterface ITriggers {\n\t/// <summary>\n\t/// Return <c>true</c> if added triggers of this type.\n\t/// </summary>\n\tbool HasTriggers { get; }\n\t\n\t/// <summary>\n\t/// Optionally start/stop the trigger engine (hooks etc).\n\t/// </summary>\n\t/// <param name=\"start\"></param>\n\tvoid StartStop(bool start);\n}\n\nclass TriggerHookContext : WFCache {\n\t//internal readonly ActionTriggers triggers;\n\twnd _w;\n\tbool _haveWnd, _mouseWnd; POINT _p;\n\t\n\tpublic TriggerHookContext(ActionTriggers triggers) {\n\t\t//this.triggers = triggers;\n\t\t_perfList = new _ScopeTime[32];\n\t\tbase.CacheName = true; //we'll call Clear(onlyName: true) at the start of each event\n\t}\n\t\n\tpublic wnd Window {\n\t\t[MethodImpl(MethodImplOptions.AggressiveInlining)]\n\t\tget {\n\t\t\tif (!_haveWnd) {\n\t\t\t\t_haveWnd = true;\n\t\t\t\t_w = _mouseWnd ? wnd.fromXY(_p, WXYFlags.NeedWindow) : wnd.active;\n\t\t\t}\n\t\t\treturn _w;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Trigger/action to run. Set by a hook proc of a trigger engine.\n\t/// </summary>\n\tpublic ActionTrigger trigger;\n\t\n\t/// <summary>\n\t/// Used with <see cref=\"trigger\"/>.\n\t/// </summary>\n\tpublic TriggerArgs args;\n\t\n\t/// <summary>\n\t/// Used with <see cref=\"trigger\"/>.\n\t/// Can be 0 or one of <c>TriggerActionThreads.c_</c> constants.\n\t/// </summary>\n\tpublic int muteMod;\n\t\n\t///// <summary>\n\t///// This event was processed (not ignored). Set by a hook proc of a trigger engine.\n\t///// </summary>\n\t//public bool processed;\n\t\n\t/// <summary>\n\t/// Called before processing each hook event. Clears most properties and fields.\n\t/// </summary>\n\tpublic void InitContext() {\n\t\t_w = default; _haveWnd = _mouseWnd = false;\n\t\tbase.Clear(onlyName: true);\n\t\ttrigger = null; args = null; muteMod = 0;\n\t\t_perfLen = 0;\n\t}\n\t\n\t/// <summary>\n\t/// Tells to get window (for scope) from the specified point. If not called, will use the active window. In any case, gets window on demand.\n\t/// </summary>\n\tpublic void UseWndFromPoint(POINT p) {\n\t\t_mouseWnd = true; _p = p;\n\t}\n\t\n\tstruct _ScopeTime {\n\t\tpublic int time_, avgTime;\n\t}\n\t\n\tlong _perfTime;\n\t_ScopeTime[] _perfList; //don't use List<> because its JIT is too slow in time-critical code\n\tint _perfLen;\n\t\n\tpublic void PerfStart() {\n\t\t_perfTime = perf.mcs;\n\t}\n\t\n\tpublic void PerfEnd(bool isFunc, ref int perfTime) {\n\t\tlong tLong = perf.mcs - _perfTime;\n\t\tint t = (int)Math.Min(tLong, 1_000_000_000);\n\t\t\n\t\t//calc average time of this scope. Assume the first time is 0.\n\t\tif (perfTime != 0) perfTime = Math.Max(1, (int)(((long)perfTime * 7 + t) / 8));\n\t\t//print.it($\"time={time}, avg={perfTime}\");\n\t\t\n\t\tif (isFunc) t |= unchecked((int)0x80000000);\n\t\tif (_perfLen == _perfList.Length) Array.Resize(ref _perfList, _perfList.Length * 2);\n\t\t_perfList[_perfLen++] = new _ScopeTime { time_ = t, avgTime = perfTime };\n\t\tif (perfTime == 0) perfTime = 1;\n\t}\n\t\n\tpublic void PerfWarn() {\n\t\tif (_perfLen == 0) return;\n\t\tlong ttTrue = 0, ttCompare = 0;\n\t\tfor (int i = 0; i < _perfLen; i++) {\n\t\t\tvar v = _perfList[i];\n\t\t\tint t = v.time_ & 0x7fffffff, ta;\n\t\t\tttTrue += t;\n\t\t\tif (v.avgTime == 0) ta = Math.Max(t - 150_000, 0); //first time. Can be slow JIT and assembly loading.\n\t\t\telse ta = Math.Min(t, v.avgTime);\n\t\t\tttCompare += ta;\n\t\t}\n\t\tttCompare /= 1000; ttTrue /= 1000;\n\t\t//print.it(ttTrue, ttCompare);\n\t\tif (ttCompare <= 25 && (ttTrue < 200 || ttTrue < WindowsHook.LowLevelHooksTimeout - 100)) return;\n\t\tvar b = new StringBuilder();\n\t\tb.AppendFormat(\"<>Warning: Too slow trigger scope detection (Triggers.Of or Triggers.FuncOf). Time: {0} ms. Task name: {1}. <fold>\", ttTrue, script.name);\n\t\tfor (int i = 0; i < _perfLen; i++) {\n\t\t\tvar v = _perfList[i];\n\t\t\tint t = v.time_ & 0x7fffffff;\n\t\t\tb.AppendFormat(\"\\t{0} {1,8}.{2:D3} ms\", v.time_ < 0 ? \"F\" : \"W\", t / 1000, t % 1000);\n\t\t\tif (v.avgTime > 0) b.AppendFormat(\", average {0}.{1:D3} ms\", v.avgTime / 1000, v.avgTime % 1000);\n\t\t\tb.AppendLine();\n\t\t}\n\t\tb.Append(\"* W - Triggers.Of (window); F - Triggers.FuncOf.</fold>\");\n\t\tThreadPool.QueueUserWorkItem(print.it, b.ToString()); //4 ms first time. Async because JIT slow.\n\t}\n\t\n\t/// <summary>\n\t/// Currently pressed modifier keys. Valid only in hotkey and autotext triggers.\n\t/// </summary>\n\tpublic KMod Mod => _mod;\n\t\n\t/// <summary>\n\t/// Currently pressed left-side modifier keys. Valid only in hotkey and autotext triggers.\n\t/// </summary>\n\tpublic KMod ModL => _modL;\n\t\n\t/// <summary>\n\t/// Currently pressed right-side modifier keys. Valid only in hotkey and autotext triggers.\n\t/// </summary>\n\tpublic KMod ModR => _modR;\n\t\n\t/// <summary>\n\t/// Not 0 if this key event is a modifier key. Valid only in hotkey and autotext triggers.\n\t/// </summary>\n\tpublic KMod ModThis => _modThis;\n\t\n\tKMod _mod, _modL, _modR, _modThis;\n\tlong _lastKeyTime;\n\t\n\t/// <summary>\n\t/// Called before processing each keyboard hook event.\n\t/// Updates <see cref=\"Mod\"/>, <see cref=\"ModL\"/>, <see cref=\"ModR\"/>, <see cref=\"ModThis\"/>. They are used by hotkey and autotext triggers.\n\t/// </summary>\n\tpublic void InitMod(HookData.Keyboard k) {\n\t\tKMod modL = 0, modR = 0;\n\t\tswitch (k.vkCode) {\n\t\tcase KKey.LCtrl: modL = KMod.Ctrl; break;\n\t\tcase KKey.LShift: modL = KMod.Shift; break;\n\t\tcase KKey.LAlt: modL = KMod.Alt; break;\n\t\tcase KKey.Win: modL = KMod.Win; break;\n\t\tcase KKey.RCtrl: modR = KMod.Ctrl; break;\n\t\tcase KKey.RShift: modR = KMod.Shift; break;\n\t\tcase KKey.RAlt: modR = KMod.Alt; break;\n\t\tcase KKey.RWin: modR = KMod.Win; break;\n\t\t}\n\t\t\n\t\tif ((_modThis = (modL | modR)) != 0) {\n\t\t\tif (k.IsUp) {\n\t\t\t\t_modL &= ~modL; _modR &= ~modR;\n\t\t\t} else {\n\t\t\t\t_modL |= modL; _modR |= modR;\n\t\t\t}\n\t\t\t_mod = _modL | _modR;\n\t\t} else if (!k.IsUp) {\n\t\t\t//We cannot trust _mod, because hooks are unreliable. We may not receive some events because of hook timeout, other hooks, OS quirks, etc. Also triggers may start while a modifier key is pressed.\n\t\t\t//And we cannot use keys.isPressed, because our triggers release modifiers. Also Key() etc. Then triggers could not be auto-repeated.\n\t\t\t//We use both. If IsPressed(mod), add mod to _mod. Else remove from _mod after >5 s since the last seen key event. The max auto-repeat delay that you can set in CP is ~1 s.\n\t\t\tTrigUtil.GetModLR(out modL, out modR);\n\t\t\t//Debug_.PrintIf(modL != _modL || modR != _modR, $\"KEY={k.vkCode}    modL={modL}  _modL={_modL}    modR={modR}  _modR={_modR}\"); //normally should be only when auto-repeating a trigger\n\t\t\t_modL |= modL; _modR |= modR;\n\t\t\tlong time = Environment.TickCount64;\n\t\t\tif (time - _lastKeyTime > 5000) {\n\t\t\t\t_modL &= modL; _modR &= modR;\n\t\t\t}\n\t\t\t_mod = _modL | _modR;\n\t\t\t//print.it(_mod, k.vkCode);\n\t\t\t_lastKeyTime = time;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au/Triggers/TriggersListWindow.cs",
    "content": "using Au.Triggers;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Controls.Primitives;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing System.Windows.Markup;\nusing System.Windows.Threading;\nusing System.Collections.ObjectModel;\nusing System.Windows.Data;\n\nnamespace Au.Triggers;\n\npublic partial class ActionTriggers {\n\t/// <summary>\n\t/// Shows a temporary window with a list of currently active triggers for the active window (hotkey, autotext and mouse edge/move triggers) or the mouse window (mouse click/wheel triggers).\n\t/// </summary>\n\t/// <param name=\"triggerType\">Which trigger type to select initially: 0 hotkey, 1 autotext, 2 mouse, 3 window, -1 previous.</param>\n\t/// <remarks>\n\t/// To determine which triggers are active in the target window, the function uses window scopes specified in code (like <c>Triggers.Of.Window(\"Window name\")</c>). However it ignores code like <c>Triggers.FuncOf...</c>, therefore the list includes triggers deactivated by your <c>FuncOf</c> functions.\n\t/// </remarks>\n\t/// <example>\n\t/// Code in file <c>Hotkey triggers.cs</c>. Calls this function when pressed hotkey <c>Alt+?</c>.\n\t/// <code><![CDATA[\n\t/// hk[\"Alt+?\"] = o => Triggers.ShowTriggersListWindow();\n\t/// ]]></code>\n\t/// </example>\n\tpublic void ShowTriggersListWindow(int triggerType = -1) {\n\t\trun.thread(() => TriggersListWindow.Show_(this, triggerType));\n\t\t\n\t\t//triggers.SendMsg_(false, () => TriggersListWindow.Show_(this, triggerType)); //no. The triggers thread uses a special message loop etc. Possible various anomalies.\n\t}\n\t\n\tinternal bool triggersListWindowIsActive_;\n}\n\nclass TriggersListWindow : Window {\n\tstatic TriggersListWindow s_w;\n\t\n\tinternal static void Show_(ActionTriggers triggers, int triggerType) {\n\t\t//single instance\n\t\tif (s_w != null) {\n\t\t\ts_w.Dispatcher.InvokeAsync(() => {\n\t\t\t\ts_w.Hwnd().ActivateL(true);\n\t\t\t\tif (triggerType is >= 0 and <= 3) {\n\t\t\t\t\ts_w._arb[triggerType].IsChecked = true;\n\t\t\t\t}\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\t\n\t\ts_w = new(triggers, triggerType);\n\t\ts_w.ShowDialog();\n\t\ts_w.Dispatcher.InvokeShutdown();\n\t\ts_w = null;\n\t}\n\t\n\tActionTriggers _triggers;\n\twnd _wActive, _wMouse;\n\tPOINT _pMouse;\n\tRadioButton[] _arb;\n\tListView _lv;\n\tListCollectionView _view;\n\t\n\tTriggersListWindow(ActionTriggers triggers, int triggerType) {\n\t\t_triggers = triggers;\n\t\tif (triggerType is < 0 or > 3) triggerType = Math.Clamp(s_sett.triggerType, 0, 3);\n\t\t\n\t\t_pMouse = mouse.xy;\n\t\t_wMouse = wnd.fromXY(_pMouse, WXYFlags.NeedWindow);\n\t\t_wActive = wnd.active;\n\t\t\n\t\tvar a = _GetTriggers();\n\t\t\n\t\tTitle = \"Triggers\";\n\t\tvar b = new wpfBuilder(this).WinSize(500, 700);\n\t\tb.R.AddToolBar_(out _, out var tb, hideOverflow: true, controlBrush: true).Margin(\"0\");\n\t\tb.Add(out TextBox tFilter).Margin(\"L0\").Focus();\n\t\ttFilter.PreviewMouseUp += (_, e) => { if (e.ChangedButton == MouseButton.Middle) tFilter.Clear(); };\n\t\t\n\t\tb.Row(-1).Add(out _lv);\n\t\t_lv.SelectionMode = SelectionMode.Single;\n\t\tVirtualizingStackPanel.SetVirtualizationMode(_lv, VirtualizationMode.Recycling);\n\t\tScrollViewer.SetHorizontalScrollBarVisibility(_lv, ScrollBarVisibility.Disabled);\n\t\t_SetItemTemplate();\n\t\t_lv.ItemsSource = a;\n\t\t\n\t\t_lv.MouseUp += (_, e) => {\n\t\t\tif (e.ChangedButton is MouseButton.Left or MouseButton.Right) {\n\t\t\t\tif (s_sett.runOn2Click && e.ChangedButton is MouseButton.Left) return;\n\t\t\t\tif (_lv.ContainerFromElement(e.OriginalSource as DependencyObject) is ListViewItem { Content: _TLItem t } lvi) _Menu(t, lvi, 1);\n\t\t\t}\n\t\t};\n\t\t_lv.MouseDoubleClick += (_, e) => {\n\t\t\tif (s_sett.runOn2Click && e.ChangedButton is MouseButton.Left) {\n\t\t\t\tif (_lv.ContainerFromElement(e.OriginalSource as DependencyObject) is ListViewItem { Content: _TLItem t } lvi) _Menu(t, lvi, 2);\n\t\t\t}\n\t\t};\n\t\t\n\t\t_lv.GotKeyboardFocus += (_, _) => {\n\t\t\tDispatcher.InvokeAsync(() => {\n\t\t\t\tif (_lv.Items.Count > 0) {\n\t\t\t\t\tint i = _lv.SelectedIndex;\n\t\t\t\t\tif (i < 0) _lv.SelectedIndex = i = 0;\n\t\t\t\t\t_lv.ScrollIntoView(_lv.Items.GetItemAt(i));\n\t\t\t\t\tif (_lv.ItemContainerGenerator.ContainerFromIndex(i) is ListViewItem lvi) lvi.Focus();\n\t\t\t\t}\n\t\t\t});\n\t\t};\n\t\t\n\t\tb.End();\n\t\t\n\t\tstring filter = null;\n\t\t_view = (ListCollectionView)CollectionViewSource.GetDefaultView(a);\n\t\t_view.Filter = o => {\n\t\t\tif (o is _TLItem t) {\n\t\t\t\tif (_TriggerTypeToInt(t) != triggerType) return false;\n\t\t\t\tif (!filter.NE()) {\n\t\t\t\t\tif (!(t.trigger.Contains(filter, StringComparison.OrdinalIgnoreCase) || (t.action?.Contains(filter, StringComparison.OrdinalIgnoreCase) ?? false))) return false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t};\n\t\tif (s_sett.sort) _view.CustomSort = new _SortComparer();\n\t\t\n\t\tstring[] aTriggersTypeStrings = { \"_Hotkey\", \"_Autotext\", \"_Mouse\", \"_Window\" };\n\t\t_arb = new RadioButton[aTriggersTypeStrings.Length];\n\t\tfor (int i = 0; i < _arb.Length; i++) {\n\t\t\ttb.Items.Add(_arb[i] = new RadioButton { Content = new AccessText { Text = aTriggersTypeStrings[i] }, Width = 60, Margin = new(0, 0, 3, 0), BorderBrush = SystemColors.ActiveBorderBrush });\n\t\t\tint tt = i;\n\t\t\t_arb[i].Checked += (_, e) => { s_sett.triggerType = triggerType = tt; _view.Refresh(); };\n\t\t\t_arb[i].Focusable = false; //to avoid focusing on access key\n\t\t}\n\t\t_arb[triggerType].IsChecked = true;\n\t\ttFilter.TextChanged += (_, _) => { filter = tFilter.Text; _view.Refresh(); };\n\t\t\n\t\tSourceInitialized += (_, _) => {\n\t\t\tvar w = this.Hwnd();\n\t\t\tw.MoveToScreenCenter(); //workaround for WPF bug: incorrectly centers in a non-primary screen with different DPI\n\t\t};\n\t\tLoaded += (_, _) => {\n\t\t\tvar w = this.Hwnd();\n\t\t\tw.ActivateL();\n\t\t};\n\t\tActivated += (_, _) => {\n\t\t\t_triggers.triggersListWindowIsActive_ = true;\n\t\t};\n\t\tDeactivated += (_, _) => {\n\t\t\t_triggers.triggersListWindowIsActive_ = false;\n\t\t\tif (s_sett.autoClose && IsVisible) timer.after(100, _ => {\n\t\t\t\tif (!wnd.active.IsOfThisThread) Close();\n\t\t\t});\n\t\t};\n\t}\n\t\n\tprotected override void OnPreviewKeyDown(KeyEventArgs e) {\n\t\tswitch (e.Key) {\n\t\tcase Key.Escape: Close(); e.Handled = true; return;\n\t\tcase Key.Down when Keyboard.FocusedElement is TextBox: _lv.Focus(); e.Handled = true; return;\n\t\tcase Key.Enter:\n\t\t\tif (e.OriginalSource is ListViewItem { Content: _TLItem t } lvi) {\n\t\t\t\t_Menu(t, lvi, 0);\n\t\t\t} else {\n\t\t\t\t_lv.Focus();\n\t\t\t\ttimer.after(1, _ => {\n\t\t\t\t\tif (_lv.SelectedItem is _TLItem t && _lv.ItemContainerGenerator.ContainerFromItem(t) is ListViewItem lvi) _Menu(t, lvi, 0);\n\t\t\t\t});\n\t\t\t}\n\t\t\te.Handled = true;\n\t\t\treturn;\n\t\t}\n\t\tbase.OnPreviewKeyDown(e);\n\t}\n\t\n\tvoid _Menu(_TLItem t, ListViewItem li, int clickCount) {\n\t\tif ((s_sett.runOnEnter && clickCount == 0) || (s_sett.runOn2Click && clickCount == 2)) {\n\t\t\t_RunAction(t);\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tvar m = new popupMenu { CheckDontClose = true };\n\t\t\n\t\tm[\"&Run\"] = o => _RunAction(t);\n\t\tif (clickCount == 0) m.FocusedItem = m.Last;\n\t\t\n\t\tm[\"&Edit\"] = o => {\n\t\t\tif (s_sett.autoClose) Close();\n\t\t\tScriptEditor.Open(t.t.SourceFile, t.t.SourceLine);\n\t\t};\n\t\tm.Separator();\n\t\tm.Submenu(\"&Settings\", m => {\n\t\t\tm.AddCheck(\"Auto-close this window\", s_sett.autoClose, o => { s_sett.autoClose = o.IsChecked; });\n\t\t\tm.Separator();\n\t\t\tm.AddCheck(\"Run on 2*click\", s_sett.runOn2Click, o => { s_sett.runOn2Click = o.IsChecked; });\n\t\t\tm.AddCheck(\"Run on single Enter\", s_sett.runOnEnter, o => { s_sett.runOnEnter = o.IsChecked; });\n\t\t\tm.Separator();\n\t\t\tm.AddCheck(\"Compact list\", s_sett.compact, o => { s_sett.compact = o.IsChecked; _SetItemTemplate(); });\n\t\t\tm.AddCheck(\"Sort\", s_sett.sort, o => { s_sett.sort = o.IsChecked; _view.CustomSort = s_sett.sort ? new _SortComparer() : null; });\n\t\t\tm.AddCheck(\"Sort hotkeys by modifier\", s_sett.sortByMod, o => { s_sett.sortByMod = o.IsChecked; if (s_sett.sort) _view.CustomSort = new _SortComparer(); });\n\t\t});\n\t\tm[\"About\"] = o => _About();\n\t\t\n\t\tvar r = li.RectInScreen();\n\t\tPOINT p = clickCount==0 ? new(r.left, r.bottom) : mouse.xy;\n\t\tm.Show(PMFlags.Underline | PMFlags.AlignRectBottomTop | (clickCount==0 ? 0 : PMFlags.AlignCenterH), xy: p, excludeRect: r, owner: this);\n\t}\n\t\n\tvoid _RunAction(_TLItem t) {\n\t\tif (s_sett.autoClose) Close();\n\t\twnd ww = default;\n\t\tif (t.t is WindowTrigger tw) {\n\t\t\tww = tw.Finder.Find();\n\t\t\tif (ww.Is0) { dialog.showInfo(\"Cannot run trigger action\", $\"Window not found.\\n\\n{tw.Finder}\", flags: DFlags.CenterMouse); return; }\n\t\t\tif (tw.Event is TWEvent.Active or TWEvent.ActiveNew or TWEvent.ActiveOnce || !ww.HasExStyle(WSE.NOACTIVATE)) ww.ActivateL(true);\n\t\t} else if (t.t is MouseTrigger { Kind: TMKind.Click or TMKind.Wheel } mt) {\n\t\t\tif (_wMouse == _wActive || wnd.fromXY(_pMouse, WXYFlags.NeedWindow) != _wMouse) _wMouse.ActivateL(true);\n\t\t} else {\n\t\t\t_wActive.ActivateL(true);\n\t\t}\n\t\tif (!s_sett.autoClose && this.Hwnd().IsActive) this.Hwnd().ShowMinimized(1);\n\t\t\n\t\ttimer2.after(200, _ => _RunNow(t.t, ww));\n\t\t\n\t\tvoid _RunNow(ActionTrigger t, wnd ww) {\n\t\t\twnd w, w2; string s2 = null;\n\t\t\tif (t is WindowTrigger tw) {\n\t\t\t\tw = w2 = ww;\n\t\t\t\tif (!w.IsVisible) return;\n\t\t\t\tif (tw.Event is TWEvent.Active or TWEvent.ActiveNew or TWEvent.ActiveOnce && !w.IsActive) (w2, s2) = (default, \"active\");\n\t\t\t} else {\n\t\t\t\tif (t is MouseTrigger { Kind: TMKind.Click or TMKind.Wheel } mt) {\n\t\t\t\t\ttry { mouse.move(_pMouse); 100.ms(); } catch { return; }\n\t\t\t\t\t(w, w2, s2) = (_wMouse, wnd.fromXY(_pMouse, WXYFlags.NeedWindow), \"mouse\");\n\t\t\t\t} else {\n\t\t\t\t\t(w, w2, s2) = (_wActive, wnd.active, \"active\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (w2 != w) { dialog.showInfo(\"Cannot run trigger action\", $\"The {s2} window must be:\\n\\n{w}\", flags: DFlags.CenterMouse); return; }\n\t\t\tTriggerArgs ta = t switch {\n\t\t\t\tHotkeyTrigger k => new HotkeyTriggerArgs(k, w, 0, 0),\n\t\t\t\tAutotextTrigger k => new AutotextTriggerArgs(k, w, \"\", false),\n\t\t\t\tMouseTrigger k => new MouseTriggerArgs(k, w, 0),\n\t\t\t\tWindowTrigger k => new WindowTriggerArgs(k, w, 0),\n\t\t\t\t_ => null\n\t\t\t};\n\t\t\tt.RunAction(ta);\n\t\t}\n\t}\n\t\n\tvoid _SetItemTemplate() {\n\t\t_lv.ItemTemplate = (DataTemplate)XamlReader.Parse($$$\"\"\"\n<DataTemplate xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\" xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\">\n\t<TextBlock TextWrapping=\"Wrap\" Padding=\"0,1,0,2\">\n\t\t<TextBlock.Resources>\n\t\t\t<Style TargetType=\"Run\">\n\t\t\t\t<Style.Triggers>\n\t\t\t\t\t<DataTrigger Binding=\"{Binding t.Disabled}\" Value=\"True\">\n\t\t\t\t\t\t<Setter Property=\"Foreground\" Value=\"Gray\"/>\n\t\t\t\t\t</DataTrigger>\n\t\t\t\t\t<DataTrigger Binding=\"{Binding t.Disabled}\" Value=\"False\">\n\t\t\t\t\t\t<Setter Property=\"Foreground\" Value=\"{{{(WpfUtil_.IsHighContrastDark ? \"#FFFF2D\" : \"#0060F0\")}}}\"/>\n\t\t\t\t\t</DataTrigger>\n\t\t\t\t</Style.Triggers>\n\t\t\t</Style>\n\t\t</TextBlock.Resources>\n\t\t<Bold><Run Text=\"{Binding trigger, Mode=OneWay}\"/></Bold><Run Text=\"{Binding options, Mode=OneWay}\" Foreground=\"YellowGreen\"/><Run Text=\"{{{(s_sett.compact ? \"\" : \"&#x0a;\")}}}        \" Foreground=\"Black\"/><Run Text=\"{Binding action, Mode=OneWay}\" Foreground=\"{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}\"/>\n\t</TextBlock>\n</DataTemplate>\n\"\"\");\n\t}\n\t\n\tvoid _About() {\n\t\tvar s = $\"\"\"\nThe list contains triggers that work in the active window (hotkey, autotext and mouse edge/move triggers) or the mouse window (mouse click/wheel triggers).\n\nActive: {_WndToStr(_wActive)}\nMouse: {_WndToStr(_wMouse)}\n\nCode like `Triggers.Of...` is applied. Code like `Triggers.FuncOf...` is ignored.\n\"\"\";\n\t\t\n\t\tdialog.showInfo(\"About Triggers\", s, owner: this);\n\t\t\n\t\tstatic string _WndToStr(wnd w) {\n\t\t\tvar name = w.Name;\n\t\t\treturn name.NE() ? $\"cn={w.ClassName}, program={w.ProgramName}\" : $\"name={name}, program={w.ProgramName}\";\n\t\t}\n\t}\n\t\n\tObservableCollection<_TLItem> _GetTriggers() {\n\t\tList<_TLItem> a = new();\n\t\tList<(string path, string text, int[] lines)> aFiles = new();\n\t\tDictionary<TriggerScope, bool> dScopes = new();\n\t\tWFCache wfCache = new() { CacheName = true, NoTimeout = true, IgnoreVisibility = true };\n\t\t\n\t\tforeach (var t in _triggers.Hotkey) {\n\t\t\tif (!_InScope(t, _wActive)) continue;\n\t\t\tstring sTrigger = t.ParamsString, sOptions = null;\n\t\t\tif (t.Flags != 0) {\n\t\t\t\tsTrigger = sTrigger[..sTrigger.Find(\" (\")];\n\t\t\t\tvar f1 = t.Flags & (TKFlags.LeftMod | TKFlags.RightMod | TKFlags.Numpad | TKFlags.NumpadNot); //display only flags that are important here\n\t\t\t\tif (f1 != 0) sOptions = $\"    {f1}\";\n\t\t\t}\n\t\t\t_Add(t, sTrigger, sOptions);\n\t\t}\n\t\t\n\t\tstring postfixKey = _triggers.Autotext.PostfixKey.ToString();\n\t\t(string s, TAPostfix pt, string pc) atPrev = default; //optimization: don't build identical options\n\t\tforeach (var t in _triggers.Autotext) {\n\t\t\tif (!_InScope(t, _wActive)) continue;\n\t\t\tstring sOptions = null;\n\t\t\tif (t.PostfixType == atPrev.pt && t.PostfixChars == atPrev.pc) {\n\t\t\t\tsOptions = atPrev.s;\n\t\t\t} else {\n\t\t\t\tif (t.PostfixType != TAPostfix.None) {\n\t\t\t\t\tusing (new StringBuilder_(out var b)) {\n\t\t\t\t\t\tb.Append(\"    + \");\n\t\t\t\t\t\tswitch (t.PostfixType) {\n\t\t\t\t\t\tcase TAPostfix.Key: b.Append(postfixKey); break;\n\t\t\t\t\t\tcase TAPostfix.Char: b.Append(t.PostfixChars ?? \"char\"); break;\n\t\t\t\t\t\tdefault: b.Append(postfixKey).Append(\" or \").Append(t.PostfixChars ?? \"char\"); break;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tsOptions = b.ToString();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tatPrev = (sOptions, t.PostfixType, t.PostfixChars);\n\t\t\t}\n\t\t\t_Add(t, t.Text, sOptions);\n\t\t}\n\t\t\n\t\tforeach (var t in _triggers.Mouse) {\n\t\t\tif (!_InScope(t, t.Kind is TMKind.Click or TMKind.Wheel ? _wMouse : _wActive)) continue;\n\t\t\tstring s = t.ParamsString, sOptions = null;\n\t\t\tif (t.Flags != 0) {\n\t\t\t\ts = s.RxReplace(@\" \\(.+?\\)\", \"\", 1);\n\t\t\t\tvar f1 = t.Flags & (TMFlags.LeftMod | TMFlags.RightMod); //display only flags that are important here\n\t\t\t\tif (f1 != 0) sOptions = $\"    {f1}\";\n\t\t\t}\n\t\t\t_Add(t, s, sOptions);\n\t\t}\n\t\t\n\t\tforeach (var t in _triggers.Window) {\n\t\t\tvar s2 = \"    \" + t.Event.ToString();\n\t\t\tif (t.Later != 0) s2 = s2 + \", later \" + t.Later;\n\t\t\t_Add(t, t.ParamsString, s2);\n\t\t}\n\t\t\n\t\treturn new(a.OrderBy(o => _TriggerTypeToInt(o)).ThenBy(o => o.fileIndex).ThenBy(o => o.t.SourceLine));\n\t\t\n\t\tbool _InScope(ActionTrigger t, wnd w) {\n\t\t\tif (t.Scope is { } scope) {\n\t\t\t\tif (!dScopes.TryGetValue(t.Scope, out bool match)) {\n\t\t\t\t\tdScopes[scope] = match = scope.Match(w, wfCache);\n\t\t\t\t}\n\t\t\t\tif (!match) return false;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\tvoid _Add(ActionTrigger t, string sTrigger, string sOptions) {\n\t\t\tvar sa = _GetAction(t, out int fileIndex);\n\t\t\t//print.it($\"<><open {t.SourceFile}|{t.SourceLine}>{sTrigger}<>  <\\a>{sa}</\\a>\");\n\t\t\tif (t.Disabled) sOptions += \"    DISABLED\";\n\t\t\ta.Add(new(t, sTrigger, sOptions, sa, fileIndex));\n\t\t}\n\t\t\n\t\tstring _GetAction(ActionTrigger t, out int fileIndex) {\n\t\t\tstring text, file = t.SourceFile; int[] lines;\n\t\t\tfor (fileIndex = aFiles.Count; --fileIndex >= 0;) if (aFiles[fileIndex].path == file) break;\n\t\t\tif (fileIndex < 0) {\n\t\t\t\tfileIndex = aFiles.Count;\n\t\t\t\ttry { text = filesystem.loadText(file); } catch (Exception) { text = \"\"; }\n\t\t\t\t\n\t\t\t\t//find line offsets once, not for each trigger (slow)\n\t\t\t\ttext = text.ReplaceLineEndings(\"\\n\");\n\t\t\t\tlines = new int[text.AsSpan().Count('\\n') + 1];\n\t\t\t\tfor (int j = 1; j < lines.Length; j++) lines[j] = text.IndexOf('\\n', lines[j - 1]) + 1;\n\t\t\t\t\n\t\t\t\taFiles.Add((file, text, lines));\n\t\t\t} else {\n\t\t\t\ttext = aFiles[fileIndex].text;\n\t\t\t\tlines = aFiles[fileIndex].lines;\n\t\t\t}\n\t\t\t\n\t\t\tint sourceLine = t.SourceLine - 1;\n\t\t\tif ((uint)sourceLine < lines.Length) {\n\t\t\t\tint start = lines[sourceLine], end = sourceLine + 1 < lines.Length ? lines[sourceLine + 1] - 1 : text.Length;\n\t\t\t\tif (!text.RxMatch(@\"\\]\\s*=\\s*(?|\\w+\\s*=>\\s*(.+)|(.+))\", 1, out string s, range: start..end)) return null;\n\t\t\t\tif (s.Ends(\"\\\"\\\"\\\"\")) {\n\t\t\t\t\tif (text.RxMatch(@\"(?s)\\s+(.+?)\\R\\h*\"\"\"\"\"\"\", 1, out string s2, range: end..)) s += s2.RxReplace(@\"\\R\\h*\", \"    \");\n\t\t\t\t} else if (t is AutotextTrigger && s.Ends(\".Menu(\")) {\n\t\t\t\t\tif (text.RxMatch(@\"(?s)\\s+(.+?)\\R\\h*\\);\", 1, out string s2, range: end..)) s += s2.RxReplace(@\"\\R\\h*\", \"    \");\n\t\t\t\t}\n\t\t\t\treturn s.Limit(200);\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t}\n\t\n\tstatic int _TriggerTypeToInt(_TLItem t) => t.t switch { HotkeyTrigger => 0, AutotextTrigger => 1, MouseTrigger => 2, _ => 3 };\n\t\n\trecord _TLItem(ActionTrigger t, string trigger, string options, string action, int fileIndex) {\n\t\tpublic ReadOnlySpan<char> GetSortInfo(out int keyWeight, out int modWeight) {\n\t\t\tif (t is HotkeyTrigger k) {\n\t\t\t\tint mod = (int)k.modMask ^ 15 | (int)k.modMasked;\n\t\t\t\tmodWeight = (((mod & 2) << 3 | (mod & 4) << 3 | (mod & 1) << 6 | (mod & 8) << 4)) >> 4 | System.Numerics.BitOperations.PopCount((uint)mod) << 4;\n\t\t\t\t\n\t\t\t\tint i = trigger.LastIndexOf('+') + 1;\n\t\t\t\tvar s = trigger.AsSpan(i).Trim();\n\t\t\t\tkeyWeight = s.Length > 1 ? 2 : s[0].IsAsciiAlphaDigit() ? 1 : 0;\n\t\t\t\t\n\t\t\t\treturn s;\n\t\t\t}\n\t\t\tkeyWeight = modWeight = 0;\n\t\t\treturn trigger;\n\t\t}\n\t}\n\t\n\tclass _SortComparer : System.Collections.IComparer {\n\t\tint System.Collections.IComparer.Compare(object o1, object o2) {\n\t\t\tif (o1 is _TLItem i1 && o2 is _TLItem i2) {\n\t\t\t\tvar s1 = i1.GetSortInfo(out int keyWeight1, out int modWeight1);\n\t\t\t\tvar s2 = i2.GetSortInfo(out int keyWeight2, out int modWeight2);\n\t\t\t\tif (i1.t is not HotkeyTrigger) return s1.CompareTo(s2, StringComparison.CurrentCultureIgnoreCase);\n\t\t\t\tif (s_sett.sortByMod) {\n\t\t\t\t\tint r = modWeight1 - modWeight2;\n\t\t\t\t\tif (r == 0) r = keyWeight1 - keyWeight2;\n\t\t\t\t\tif (r == 0) r = s1.CompareTo(s2, StringComparison.CurrentCultureIgnoreCase);\n\t\t\t\t\treturn r;\n\t\t\t\t} else {\n\t\t\t\t\tint r = keyWeight1 - keyWeight2;\n\t\t\t\t\tif (r == 0) r = s1.CompareTo(s2, StringComparison.CurrentCultureIgnoreCase);\n\t\t\t\t\tif (r == 0) r = modWeight1 - modWeight2;\n\t\t\t\t\treturn r;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn 0;\n\t\t}\n\t}\n\t\n\tinternal record class _Settings : JSettings {\n\t\tpublic static readonly string File = folders.ThisAppDataRoaming + @\"TriggersListWindow settings.json\";\n\t\t\n\t\tpublic static _Settings Load() => Load<_Settings>(File);\n\t\t\n\t\tpublic bool autoClose = true;\n\t\tpublic bool compact;\n\t\tpublic bool sort, sortByMod;\n\t\tpublic bool runOnEnter, runOn2Click;\n\t\tpublic int triggerType;\n\t}\n\t\n\tstatic readonly _Settings s_sett = _Settings.Load();\n}\n"
  },
  {
    "path": "Au/Triggers/Triggers_actions.cs",
    "content": "namespace Au.Triggers;\n\nclass TOptions {\n\tpublic Action<TOBAArgs> before;\n\tpublic Action<TOBAArgs> after;\n\tpublic sbyte thread; //>=0 dedicated or <0 TOThread\n\tpublic TOFlags flags;\n\tpublic int wait;\n\tpublic PostToThisThread_ thisThread;\n\t\n\tpublic TOptions Clone() => this.MemberwiseClone() as TOptions;\n}\n\nclass TOThread { public const sbyte OfTriggers = -1, New = -2, Pool = -3, This = -4; }\n\n[Flags]\nenum TOFlags : byte {\n\tNoWarning = 1,\n\tSingle = 2,\n\t//MtaThread = 4,\n\t//BackgroundThread=8, //rejected. Always background. Foreground makes no sense here. If need, can easily set in code.\n}\n\n/// <summary>\n/// Allows to set some options for multiple triggers and their actions.\n/// </summary>\n/// <remarks>\n/// You set options through a thread-static property <see cref=\"ActionTriggers.Options\"/>.\n/// Changed options are applied to all triggers/actions added afterwards in this thread.\n/// </remarks>\n/// <example>\n/// <code><![CDATA[\n/// Triggers.Options.ThreadNew();\n/// Triggers.Options.BeforeAction = o => { opt.key.KeySpeed = 10; };\n/// Triggers.Hotkey[\"Ctrl+K\"] = o => print.it(opt.key.KeySpeed); //10\n/// Triggers.Hotkey[\"Ctrl+Shift+K\"] = o => print.it(opt.key.KeySpeed); //10\n/// Triggers.Options.BeforeAction = o => { opt.key.KeySpeed = 20; };\n/// Triggers.Hotkey[\"Ctrl+L\"] = o => print.it(opt.key.KeySpeed); //20\n/// Triggers.Hotkey[\"Ctrl+Shift+L\"] = o => print.it(opt.key.KeySpeed); //20\n/// ]]></code>\n/// </example>\npublic class TriggerOptions {\n\tTOptions _new, _prev;\n\t\n\tTOptions _New() => _new ??= (_prev?.Clone() ?? new TOptions());\n\t\n\t/// <summary>\n\t/// Run actions always in the same dedicated thread that does not end when actions end.\n\t/// </summary>\n\t/// <param name=\"thread\">A number that you want to use to identify the thread. Can be 0-127. Default 0.</param>\n\t/// <param name=\"wait\">Defines when to start an action if an action (other or same) is currently running in this thread. If 0 (default), don't run. If -1 (<c>Timeout.Infinite</c>), run when that action ends (and possibly other queued actions). If > 0, run when that action ends, if it ends within this time from now; the time is in milliseconds.</param>\n\t/// <param name=\"noWarning\">No warning when cannot start an action because an action is running and <i>wait</i> == 0.</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\"></exception>\n\t/// <remarks>\n\t/// Multiple actions in same thread cannot run simultaneously. Actions in different threads can run simultaneously.\n\t/// There is no \"end old running action\" feature. If need it, use other script. Example: <c>Triggers.Hotkey[\"Ctrl+M\"] = o => script.runWait(\"Other Script\");</c>.\n\t/// There is no \"temporarily pause old running action to run new action\" feature. As well as for scripts.\n\t/// The thread has <see cref=\"ApartmentState.STA\"/>.\n\t/// There are several <c>ThreadX</c> functions. Only the last called function is active. If none called, it is the same as called this function without arguments.\n\t/// </remarks>\n\tpublic void Thread(int thread = 0, int wait = 0, bool noWarning = false) {\n\t\t_New();\n\t\tif ((uint)thread > 127) throw new ArgumentOutOfRangeException();\n\t\t_new.thread = (sbyte)thread;\n\t\t_new.wait = wait >= -1 ? wait : throw new ArgumentOutOfRangeException();\n\t\t_new.flags = noWarning ? TOFlags.NoWarning : 0;\n\t}\n\t//CONSIDER: make default ifRunningWaitMS = 1000 if it is another action.\n\t\n\t/// <summary>\n\t/// Run trigger actions in the same thread as <see cref=\"ActionTriggers.Run\"/>. Dangerous, rarely used.\n\t/// </summary>\n\t/// <remarks>\n\t/// This should not be used without a good reason. Trigger actions must be programmed carefully, to not interfere with triggers. They must be as fast as possible, else will block triggers, hooks and user input.\n\t///\n\t/// Before v0.16 this was named <see cref=\"ThreadMain\"/> and used in the <c>\"Triggers and toolbars\"</c> script. Problem: blocks hooks etc when need long time to get file icons. Now the script uses <see cref=\"ThreadThis\"/> instead, and calls <c>Triggers.Run</c> in another thread. Your script possibly still uses the old code. You can replace it with the new version, which can be found in menu <b>File > New > Default > Triggers and toolbars</b>.\n\t/// </remarks>\n\tpublic void ThreadOfTriggers() {\n\t\t_New();\n\t\t_new.thread = TOThread.OfTriggers;\n\t\t_new.wait = 0;\n\t\t_new.flags = 0;\n\t}\n\t\n\t/// <summary>\n\t/// Alias of <see cref=\"ThreadOfTriggers\"/>.\n\t/// </summary>\n\t[EditorBrowsable(EditorBrowsableState.Never)] //renamed\n\tpublic void ThreadMain() => ThreadOfTriggers();\n\t\n\t/// <summary>\n\t/// Run trigger actions in this thread (which called this function).\n\t/// </summary>\n\t/// <remarks>\n\t/// This function can be used only if <see cref=\"ActionTriggers.Run\"/> runs in another thread. This thread must have a message loop (wait and dispatch messages). For it can be used <see cref=\"ActionTriggers.RunThread\"/>.\n\t///\n\t/// Trigger actions should be fast, else other trigger actions may be delayed. If a trigger action dispatches messages, other trigger actions can run in the meantime.\n\t/// \n\t/// Can be used to create and show toolbars (<see cref=\"toolbar\"/>). Used in the default <c>\"Triggers and toolbars\"</c> script since v0.16.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// print.it(Environment.CurrentManagedThreadId);\n\t/// ActionTriggers Triggers = new();\n\t/// Triggers.Options.ThreadThis();\n\t/// Triggers.Hotkey[\"F11\"] = o => { print.it(Environment.CurrentManagedThreadId); };\n\t/// Triggers.RunThread();\n\t/// ]]></code>\n\t/// </example>\n\tpublic void ThreadThis() {\n\t\t_New();\n\t\t_new.thread = TOThread.This;\n\t\t_new.wait = 0;\n\t\t_new.flags = 0;\n\t\t_new.thisThread = PostToThisThread_.OfThisThread;\n\t}\n\t\n\t/// <summary>\n\t/// Run trigger actions in new threads.\n\t/// </summary>\n\t/// <param name=\"single\">Don't run if this action is already running. If <c>false</c>, multiple action instances can run parallelly in multiple threads.</param>\n\t/// <remarks>\n\t/// The action can run simultaneously with other actions. The thread is STA.\n\t/// </remarks>\n\tpublic void ThreadNew(bool single = false) {\n\t\t_New();\n\t\t_new.thread = TOThread.New;\n\t\t_new.wait = 0;\n\t\tTOFlags f = 0;\n\t\tif (single) f |= TOFlags.Single;\n\t\t_new.flags = f;\n\t}\n\t\n\t/// <summary>\n\t/// Run trigger actions in thread pool threads.\n\t/// </summary>\n\t/// <param name=\"single\">Don't run if this action is already running. If <c>false</c>, multiple action instances can run parallelly in multiple threads.</param>\n\t/// <remarks>\n\t/// The action can run simultaneously with other actions. May start later if the pool is busy.\n\t/// You should know how to use thread pool correctly. The action runs in the .NET thread pool through <see cref=\"Task.Run\"/>.\n\t/// </remarks>\n\tpublic void ThreadPool(bool single = false) {\n\t\t_New();\n\t\t_new.thread = TOThread.Pool;\n\t\t_new.wait = 0;\n\t\t_new.flags = single ? TOFlags.Single : 0;\n\t}\n\t\n\t/// <summary>\n\t/// A function to run before the trigger action.\n\t/// For example, it can set <see cref=\"opt\"/> options.\n\t/// </summary>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// Triggers.Options.BeforeAction = o => { opt.key.KeySpeed = 20; opt.key.TextSpeed = 5; };\n\t/// ]]></code>\n\t/// </example>\n\tpublic Action<TOBAArgs> BeforeAction { set => _New().before = value; }\n\t\n\t/// <summary>\n\t/// A function to run after the trigger action.\n\t/// For example, it can log exceptions.\n\t/// </summary>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// Triggers.Options.AfterAction = o => { if(o.Exception!=null) print.it(o.Exception.Message); else print.it(\"completed successfully\"); };\n\t/// ]]></code>\n\t/// </example>\n\tpublic Action<TOBAArgs> AfterAction { set => _New().after = value; }\n\t\n\tinternal TOptions Current {\n\t\tget {\n\t\t\tif (_new != null) { _prev = _new; _new = null; }\n\t\t\treturn _prev ?? (s_empty ??= new TOptions());\n\t\t}\n\t}\n\tstatic TOptions s_empty;\n\t\n\t/// <summary>\n\t/// If <c>true</c>, triggers added afterwards don't depend on <see cref=\"ActionTriggers.Disabled\"/> and <see cref=\"ActionTriggers.DisabledEverywhere\"/>.\n\t/// This property sets the <see cref=\"ActionTrigger.EnabledAlways\"/> property of triggers added afterwards.\n\t/// </summary>\n\tpublic bool EnabledAlways { get; set; }\n\t\n\t/// <summary>\n\t/// Clears all options.\n\t/// </summary>\n\tpublic void Reset() {\n\t\t_new = null;\n\t\t_prev = null;\n\t}\n}\n\n/// <summary>\n/// Arguments for <see cref=\"TriggerOptions.BeforeAction\"/> and <see cref=\"TriggerOptions.AfterAction\"/>.\n/// </summary>\npublic struct TOBAArgs {\n\tinternal TOBAArgs(TriggerArgs args) {\n\t\tActionArgs = args;\n\t\tException = null;\n\t}\n\t\n\t/// <summary>\n\t/// Trigger event info. The same variable as passed to the trigger action.\n\t/// To access the info, cast to <see cref=\"HotkeyTriggerArgs\"/> etc, depending on trigger type.\n\t/// </summary>\n\tpublic TriggerArgs ActionArgs { get; }\n\t\n\t/// <summary>\n\t/// If action ended with an exception, the exception. Else <c>null</c>.\n\t/// </summary>\n\tpublic Exception Exception { get; internal set; }\n}\n\nclass TriggerActionThreads {\n\tpublic void Run(ActionTrigger trigger, TriggerArgs args, int muteMod) {\n\t\t//perf.first();\n\t\tAction actionWrapper = () => {\n\t\t\tvar o = trigger.options;\n\t\t\tvar oldOpt = o.thread is TOThread.New or TOThread.Pool ? default : opt.scope.all(inherit: true);\n\t\t\ttry {\n\t\t\t\t_MuteMod(ref muteMod);\n\t\t\t\t\n\t\t\t\tstring sTrigger = null; long startTime = 0;\n\t\t\t\t//perf.next();\n\t\t\t\tif (script.role == SRole.MiniProgram) {\n\t\t\t\t\tsTrigger = trigger.ToString();\n\t\t\t\t\tApi.QueryPerformanceCounter(out startTime);\n\t\t\t\t\tprint.TaskEvent_(\"AS \" + sTrigger, startTime, trigger.SourceFile, trigger.SourceLine);\n\t\t\t\t\t//perf.next();\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvar baArgs = new TOBAArgs(args); //struct\n\t\t\t\to.before?.Invoke(baArgs);\n\t\t\t\ttry {\n\t\t\t\t\t//perf.nw();\n\t\t\t\t\ttrigger.Run_(args);\n\t\t\t\t\t\n\t\t\t\t\tif (sTrigger != null) print.TaskEvent_(\"AE\", startTime, trigger.SourceFile, trigger.SourceLine);\n\t\t\t\t}\n\t\t\t\tcatch (Exception e1) {\n\t\t\t\t\tif (sTrigger != null) print.TaskEvent_(\"AF\", startTime, trigger.SourceFile, trigger.SourceLine);\n\t\t\t\t\t\n\t\t\t\t\tbaArgs.Exception = e1;\n\t\t\t\t\tprint.it(e1);\n\t\t\t\t}\n\t\t\t\to.after?.Invoke(baArgs);\n\t\t\t}\n\t\t\tcatch (Exception e2) {\n\t\t\t\tprint.it(e2);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\toldOpt.Dispose();\n\t\t\t\tif (o.flags.Has(TOFlags.Single)) _d.TryRemove(trigger, out _);\n\t\t\t\tif (o.thread is not (TOThread.OfTriggers or TOThread.This)) toolbar.TriggerActionEndedInToolbarUnfriendlyThread_();\n\t\t\t}\n\t\t};\n\t\t//never mind: we should not create actionWrapper if cannot run. But such cases are rare. Fast and small, about 64 bytes.\n\t\t\n\t\tvar opt1 = trigger.options;\n\t\tint threadId = opt1.thread;\n\t\tif (threadId >= 0) { //dedicated thread\n\t\t\t_Thread h = null; foreach (var v in _a) if (v.id == threadId) { h = v; break; }\n\t\t\tif (h == null) _a.Add(h = new _Thread(threadId));\n\t\t\tif (h.RunAction(actionWrapper, trigger)) return;\n\t\t} else if (threadId == TOThread.OfTriggers) {\n\t\t\tactionWrapper();\n\t\t\treturn;\n\t\t\t//note: can reenter. Probably it is better than to cancel if already running.\n\t\t} else if (threadId == TOThread.This) {\n\t\t\tif (opt1.thisThread.ManagedThreadId == Environment.CurrentManagedThreadId) print.warning(\"If called ThreadThis, triggers should run in another thread.\");\n\t\t\topt1.thisThread.Post(actionWrapper);\n\t\t\treturn;\n\t\t\t//note: can reenter.\n\t\t} else {\n\t\t\tbool canRun = true;\n\t\t\tbool single = opt1.flags.Has(TOFlags.Single);\n\t\t\tif (single) {\n\t\t\t\t_d ??= new();\n\t\t\t\tif (_d.TryGetValue(trigger, out var tt)) {\n\t\t\t\t\tswitch (tt) {\n\t\t\t\t\tcase Thread thread:\n\t\t\t\t\t\tif (thread.IsAlive) canRun = false;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase Task task:\n\t\t\t\t\t\t//print.it(task.Status);\n\t\t\t\t\t\tswitch (task.Status) {\n\t\t\t\t\t\tcase TaskStatus.RanToCompletion: case TaskStatus.Faulted: case TaskStatus.Canceled: break;\n\t\t\t\t\t\tdefault: canRun = false; break;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tif (canRun) {\n\t\t\t\tif (threadId == TOThread.New) {\n\t\t\t\t\tvar thread = new Thread(actionWrapper.Invoke) { IsBackground = true };\n\t\t\t\t\t//if (!opt1.flags.Has(TOFlags.MtaThread))\n\t\t\t\t\tthread.SetApartmentState(ApartmentState.STA);\n\t\t\t\t\tif (single) _d[trigger] = thread;\n\t\t\t\t\ttry { thread.Start(); }\n\t\t\t\t\tcatch (OutOfMemoryException) { //too many threads, probably 32-bit process\n\t\t\t\t\t\tif (single) _d.TryRemove(trigger, out _);\n\t\t\t\t\t\t_OutOfMemory();\n\t\t\t\t\t\t//TODO3: before starting thread, warn if there are too many action threads.\n\t\t\t\t\t\t//\tIn 32-bit process normally fails at ~3000 threads.\n\t\t\t\t\t\t//\tUnlikely to fail in 64-bit process, but at ~15000 threads starts to hang temporarily, which causes hook timeout, slow mouse, other anomalies.\n\t\t\t\t\t}\n\t\t\t\t} else { //thread pool\n\t\t\t\t\tvar task = new Task(actionWrapper);\n\t\t\t\t\tif (single) _d[trigger] = task;\n\t\t\t\t\ttask.Start();\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (muteMod != 0) ThreadPool.QueueUserWorkItem(_ => _MuteMod(ref muteMod));\n\t}\n\t\n\tpublic void Dispose() {\n\t\tforeach (var v in _a) v.Dispose();\n\t}\n\t\n\t[MethodImpl(MethodImplOptions.AggressiveInlining)]\n\tstatic void _OutOfMemory() {\n\t\tprint.warning(\"There is not enough memory available to start the trigger action thread.\", -1); //info: -1 because would need much memory for stack trace\n\t}\n\t\n\tList<_Thread> _a = new();\n\tConcurrentDictionary<ActionTrigger, object> _d;\n\t\n\tclass _Thread {\n\t\trecord struct _Action(Action actionWrapper, long time);\n\t\t\n\t\tAutoResetEvent _event;\n\t\tQueue<_Action> _q;\n\t\tbool _running;\n\t\tbool _disposed;\n\t\tpublic readonly int id;\n\t\t\n\t\tpublic _Thread(int id) { this.id = id; }\n\t\t\n\t\t/// <summary>\n\t\t/// Adds the action to the queue and notifies the thread to execute it.\n\t\t/// If the thread is busy, returns <c>false</c>; if <i>ifRunning</i>!=0, the action possibly will run later.\n\t\t/// </summary>\n\t\tpublic bool RunAction(Action actionWrapper, ActionTrigger trigger) {\n\t\t\tif (_disposed) return false;\n\t\t\tif (_q == null) {\n\t\t\t\t_q = new Queue<_Action>();\n\t\t\t\t_event = new(false);\n\t\t\t\ttry {\n\t\t\t\t\trun.thread(() => {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\twhile (!_disposed && _event.WaitOne()) {\n\t\t\t\t\t\t\t\twhile (!_disposed) {\n\t\t\t\t\t\t\t\t\t_Action x;\n\t\t\t\t\t\t\t\t\tlock (_q) {\n\t\t\t\t\t\t\t\t\t\tg1:\n\t\t\t\t\t\t\t\t\t\tif (_q.Count == 0) { _running = false; break; }\n\t\t\t\t\t\t\t\t\t\tx = _q.Dequeue();\n\t\t\t\t\t\t\t\t\t\tif (x.time != 0 && perf.ms > x.time) goto g1;\n\t\t\t\t\t\t\t\t\t\t_running = true;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tx.actionWrapper();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfinally {\n\t\t\t\t\t\t\t_event.Dispose(); _event = null;\n\t\t\t\t\t\t\t_q = null; _running = false; //restart if aborted\n\t\t\t\t\t\t\t//print.it(\"thread ended\");\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tcatch (OutOfMemoryException) { //too many threads, probably 32-bit process\n\t\t\t\t\t_event.Dispose(); _event = null;\n\t\t\t\t\t_OutOfMemory();\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tbool R = true;\n\t\t\tlock (_q) {\n\t\t\t\tint ifRunningWaitMS = trigger.options.wait;\n\t\t\t\tif (_running) {\n\t\t\t\t\tif (ifRunningWaitMS == 0) {\n\t\t\t\t\t\tif (!trigger.options.flags.Has(TOFlags.NoWarning))\n\t\t\t\t\t\t\tprint.it($\"<>Warning: can't run the trigger action because an action is running in this thread. <open {trigger.SourceFile}|{trigger.SourceLine}>Trigger<>: {trigger}.\"\n\t\t\t\t\t\t\t\t+ \" <fold>\\tTo run simultaneously or wait, use one of Triggers.Options.ThreadX functions.\\r\\n\\tTo disable this warning: Triggers.Options.Thread(noWarning: true);.</fold>\");\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tR = false;\n\t\t\t\t} else {\n\t\t\t\t\t_running = true;\n\t\t\t\t\t//if(ifRunningWaitMS > 0 && ifRunningWaitMS < 1000000000) ifRunningWaitMS += 1000;\n\t\t\t\t}\n\t\t\t\t_q.Enqueue(new _Action(actionWrapper, ifRunningWaitMS <= 0 ? 0 : perf.ms + ifRunningWaitMS));\n\t\t\t}\n\t\t\t_event.Set();\n\t\t\treturn R;\n\t\t}\n\t\t\n\t\tpublic void Dispose() {\n\t\t\tif (_disposed) return; _disposed = true;\n\t\t\t_event.Set();\n\t\t}\n\t}\n\t//This old version uses WaitForSingleObject which blocks COM etc, which may cause problems.\n\t//\tIn the above code, WaitOne dispatches COM etc. But still little tested.\n\t//class _Thread\n\t//{\n\t//\tstruct _Action { public Action actionWrapper; public long time; }\n\t\n\t//\tHandle_ _event;\n\t//\tQueue<_Action> _q;\n\t//\tbool _running;\n\t//\tbool _disposed;\n\t//\tpublic readonly int id;\n\t\n\t//\tpublic _Thread(int id) { this.id = id; }\n\t\n\t//\t/// <summary>\n\t//\t/// Adds the action to the queue and notifies the thread to execute it.\n\t//\t/// If the thread is busy, returns false; if ifRunning!=0, the action possibly will run later.\n\t//\t/// </summary>\n\t//\tpublic bool RunAction(Action actionWrapper, ActionTrigger trigger)\n\t//\t{\n\t//\t\tif(_disposed) return false;\n\t//\t\tif(_q == null) {\n\t//\t\t\t_q = new Queue<_Action>();\n\t//\t\t\t_event = Api.CreateEvent(false);\n\t//\t\t\ttry {\n\t//\t\t\t\trun.thread(() => {\n\t//\t\t\t\t\ttry {\n\t//\t\t\t\t\t\twhile(!_disposed && 0 == Api.WaitForSingleObject(_event, -1)) {\n\t//\t\t\t\t\t\t\twhile(!_disposed) {\n\t//\t\t\t\t\t\t\t\t_Action x;\n\t//\t\t\t\t\t\t\t\tlock(_q) {\n\t//\t\t\t\t\t\t\t\t\tg1:\n\t//\t\t\t\t\t\t\t\t\tif(_q.Count == 0) { _running = false; break; }\n\t//\t\t\t\t\t\t\t\t\tx = _q.Dequeue();\n\t//\t\t\t\t\t\t\t\t\tif(x.time != 0 && perf.ms > x.time) goto g1;\n\t//\t\t\t\t\t\t\t\t\t_running = true;\n\t//\t\t\t\t\t\t\t\t}\n\t//\t\t\t\t\t\t\t\tx.actionWrapper();\n\t//\t\t\t\t\t\t\t}\n\t//\t\t\t\t\t\t}\n\t//\t\t\t\t\t}\n\t//\t\t\t\t\tfinally {\n\t//\t\t\t\t\t\t_event.Dispose();\n\t//\t\t\t\t\t\t_q = null; _running = false; //restart if aborted\n\t//\t\t\t\t\t\t\t\t\t\t\t\t\t //print.it(\"thread ended\");\n\t//\t\t\t\t\t}\n\t//\t\t\t\t});\n\t//\t\t\t}\n\t//\t\t\tcatch(OutOfMemoryException) { //too many threads, probably 32-bit process\n\t//\t\t\t\t_event.Dispose();\n\t//\t\t\t\t_OutOfMemory();\n\t//\t\t\t}\n\t//\t\t}\n\t\n\t//\t\tbool R = true;\n\t//\t\tlock(_q) {\n\t//\t\t\tint ifRunningWaitMS = trigger.options.ifRunningWaitMS;\n\t//\t\t\tif(_running) {\n\t//\t\t\t\tif(ifRunningWaitMS == 0) {\n\t//\t\t\t\t\tif(!trigger.options.flags.Has(TOFlags.NoWarning))\n\t//\t\t\t\t\t\tprint.it(\"Warning: can't run the trigger action because an action is running in this thread.\" +\n\t//\t\t\t\t\t\t\t\" To run simultaneously or wait, use one of Triggers.Options.ThreadX functions.\" +\n\t//\t\t\t\t\t\t\t\" To disable this warning: Triggers.Options.Thread(noWarning: true);.\" +\n\t//\t\t\t\t\t\t\t\" Trigger: \" + trigger);\n\t//\t\t\t\t\treturn false;\n\t//\t\t\t\t}\n\t//\t\t\t\tR = false;\n\t//\t\t\t} else {\n\t//\t\t\t\t_running = true;\n\t//\t\t\t\t//if(ifRunningWaitMS > 0 && ifRunningWaitMS < 1000000000) ifRunningWaitMS += 1000;\n\t//\t\t\t}\n\t//\t\t\t_q.Enqueue(new _Action { actionWrapper = actionWrapper, time = ifRunningWaitMS <= 0 ? 0 : perf.ms + ifRunningWaitMS });\n\t//\t\t}\n\t//\t\tApi.SetEvent(_event);\n\t//\t\treturn R;\n\t//\t}\n\t\n\t//\tpublic void Dispose()\n\t//\t{\n\t//\t\tif(_disposed) return; _disposed = true;\n\t//\t\tApi.SetEvent(_event);\n\t//\t}\n\t//}\n\t\n\tstatic void _MuteMod(ref int muteMod) {\n\t\tswitch (Interlocked.Exchange(ref muteMod, 0)) {\n\t\tcase c_modRelease:\n\t\t\tkeys.Internal_.ReleaseModAndDisableModMenu(dontThrow: true);\n\t\t\tbreak;\n\t\tcase c_modCtrl:\n\t\t\tkeys.Internal_.SendKey(KKey.Ctrl, dontThrow: true); //disable Alt/Win menu\n\t\t\tbreak;\n\t\t}\n\t}\n\t\n\tpublic const int c_modRelease = 1, c_modCtrl = 2;\n}\n"
  },
  {
    "path": "Au/Triggers/Triggers_hooks.cs",
    "content": "//Key/mouse/autotext triggers use low-level keyboard and mouse hooks. The hooks are in a separate thread, because:\n//\t1. Safer when user code is slow or incorrect.\n//\t2. Works well with COM. In LL hook procedure some COM functions fail, eg find elm with some windows.\n//\t\tError \"An outgoing call cannot be made since the application is dispatching an input-synchronous call\". Like when using SendMessage for IPC.\n//\t\tIt is important because scripts often use UI elements in scope context functions etc that run in the main thread.\n\n//Low-level key/mouse hooks also have other problems, but not too big:\n//\t1. UAC. Hooks of non-admin processes don't work when an admin window is active.\n//\t\tWorkaround: let users don't use triggers in non-admin processes. Editor normally is admin, and by default creates task processes of same UAC IL.\n//\t2. Scripts may use raw input or directX, and Windows has this bug: then low-level keyboard hook does not work in that process.\n//\t\tWorkaround: let users run such scripts in separate processes.\n//\t3. Each LL hook uses CPU on key/mouse events.\n//\t\tWorkaround: let users put all triggers in single script. But it's OK to use another script temporarily eg for testing.\n//\t\tActually in real conditions even 10 hooks don't use a significant part of CPU compared to CPU used by the target app and OS.\n//Rejected: single hook server in editor process. It would mitigate some of these problems. Tested. Much code and little benefit.\n\n//For window triggers we use winevent hooks. They use less CPU for IPC.\n//\tTested: renaming a toolwindow with no title bar every 1-2 ms in loop:\n//\t\tCPU usage with no winevent hooks is 4%. With 1 hook - 7%. With 10 hooks in different processes - 7%.\n\nnamespace Au.Triggers;\n\n/// <summary>\n/// Thread containing low-level keyboard and mouse hooks.\n/// </summary>\nclass HooksThread : IDisposable {\n\t[Flags]\n\tpublic enum UsedEvents {\n\t\tKeyboard = 1, //Hotkey and Autotext triggers\n\t\tMouse = 2, //Mouse and Autotext triggers. Just sets the hook and resets autotext; to receive events, also add other MouseX flags.\n\t\tMouseClick = 0x10, //Mouse click triggers\n\t\tMouseWheel = 0x20, //Mouse wheel triggers\n\t\tMouseEdgeMove = 0x40, //Mouse edge and move triggers\n\t}\n\t\n\tint _tid;\n\tUsedEvents _usedEvents;\n\twnd _wMsg;\n\tMouseTriggers.EdgeMoveDetector_ _emDetector;\n\tHandle_ _eventStartStop = Api.CreateEvent(false);\n\t\n\tpublic HooksThread(UsedEvents usedEvents, wnd wMsg) {\n\t\t_usedEvents = usedEvents;\n\t\t_wMsg = wMsg;\n\t\trun.thread(_Thread, sta: false); //important: not STA, because we use lock, which dispatches sent messages if STA\n\t\tApi.WaitForSingleObject(_eventStartStop, -1);\n\t}\n\t\n\tpublic void Dispose() {\n\t\tApi.PostThreadMessage(_tid, Api.WM_QUIT, 0, 0);\n\t\tApi.WaitForSingleObject(_eventStartStop, -1);\n\t\t_eventStartStop.Dispose();\n\t\t_eventSendData.Dispose();\n\t}\n\t\n\tvoid _Thread() {\n\t\t_tid = Api.GetCurrentThreadId();\n\t\t\n\t\tWindowsHook hookK = null, hookM = null;\n\t\tif (_usedEvents.Has(UsedEvents.Keyboard)) {\n\t\t\thookK = WindowsHook.Keyboard(_KeyboardHookProc); //note: if lambda, very slow JIT on first hook event\n\t\t}\n\t\tif (_usedEvents.Has(UsedEvents.Mouse)) {\n\t\t\thookM = WindowsHook.MouseRaw_(_MouseHookProc);\n\t\t}\n\t\tif (_usedEvents.Has(UsedEvents.MouseEdgeMove)) {\n\t\t\t_emDetector = new MouseTriggers.EdgeMoveDetector_();\n\t\t}\n\t\t//tested: don't need JIT-compiling.\n\t\t\n\t\tnint idTimer = (hookK != null || hookM != null) ? Api.SetTimer(default, 0, 10_000, null) : 0;\n\t\t\n\t\tApi.SetEvent(_eventStartStop);\n\t\t\n\t\twhile (Api.GetMessage(out var m)) {\n\t\t\tif (m.message == Api.WM_TIMER && m.wParam == idTimer) {\n\t\t\t\tif (Debugger.IsAttached) continue;\n\t\t\t\thookK?.Restore();\n\t\t\t\thookM?.Restore();\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tApi.DispatchMessage(m);\n\t\t}\n\t\t\n\t\t//print.it(\"hooks thread ended\");\n\t\thookK?.Dispose();\n\t\thookM?.Dispose();\n\t\t_emDetector = null;\n\t\tApi.SetEvent(_eventStartStop);\n\t}\n\t\n\tunsafe void _KeyboardHookProc(HookData.Keyboard k) {\n\t\t_keyData = *k.NativeStructPtr_;\n\t\tif (_Send(UsedEvents.Keyboard)) k.BlockEvent();\n\t}\n\t\n\tunsafe bool _MouseHookProc(nint wParam, nint lParam) {\n\t\tint msg = (int)wParam;\n\t\tif (msg == Api.WM_MOUSEMOVE) {\n\t\t\tif (_usedEvents.Has(UsedEvents.MouseEdgeMove)) {\n\t\t\t\tvar mll = (Api.MSLLHOOKSTRUCT*)lParam;\n\t\t\t\tif (_emDetector.Detect(mll->pt)) {\n\t\t\t\t\t_emData = _emDetector.result;\n\t\t\t\t\t_Send(UsedEvents.MouseEdgeMove);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tbool wheel = msg is Api.WM_MOUSEWHEEL or Api.WM_MOUSEHWHEEL;\n\t\t\tif ((_usedEvents.Has(UsedEvents.MouseWheel) && wheel) || (_usedEvents.Has(UsedEvents.MouseClick) && !wheel)) {\n\t\t\t\t_mouseMessage = (int)wParam;\n\t\t\t\t_mouseData = *(Api.MSLLHOOKSTRUCT*)lParam;\n\t\t\t\treturn _Send(wheel ? UsedEvents.MouseWheel : UsedEvents.MouseClick);\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\t\n\t/// <summary>\n\t/// Sends key/mouse event data (copied to <c>_keyData</c> etc) to the main thread.\n\t/// Returns <c>true</c> to eat (block, discard) the event.\n\t/// On 1100 ms timeout returns <c>false</c>.\n\t/// </summary>\n\tbool _Send(UsedEvents eventType) {\n\t\t//using var p1 = perf.local();\n\t\tbool ok = _wMsg.SendNotify(Api.WM_USER + 1, _messageId, (int)eventType);\n\t\tbool timeout = Api.WaitForSingleObject(_eventSendData, 1100) == Api.WAIT_TIMEOUT;\n\t\tlock (this) {\n\t\t\tif (timeout) timeout = Api.WaitForSingleObject(_eventSendData, 0) == Api.WAIT_TIMEOUT; //other thread may SetEvent between WaitForSingleObject and lock\n\t\t\t_messageId++;\n\t\t\treturn _eat && !timeout;\n\t\t}\n\t\t//info: HookWin._HookProcLL will print warning if > LowLevelHooksTimeout-50. Max LowLevelHooksTimeout is 1000.\n\t}\n\t\n\t//fields for passing key/mouse event data to the main thread and getting its return value\n\tHandle_ _eventSendData = Api.CreateEvent(false); //sync\n\tint _messageId; //sync\n\tbool _eat; //return value\n\tApi.KBDLLHOOKSTRUCT _keyData;\n\tApi.MSLLHOOKSTRUCT _mouseData;\n\tint _mouseMessage;\n\tMouseTriggers.EdgeMoveDetector_.Result _emData;\n\t\n\t/// <summary>\n\t/// Called by the main thread to resume the hooks thread (<c>_Send</c>) and pass the return value (eat).\n\t/// Returns <c>false</c> on timeout.\n\t/// </summary>\n\tpublic bool Return(int messageId, bool eat) {\n\t\tlock (this) {\n\t\t\tif (messageId != _messageId) return false;\n\t\t\t_eat = eat;\n\t\t\tApi.SetEvent(_eventSendData);\n\t\t}\n\t\treturn true;\n\t}\n\t\n\t/// <summary>\n\t/// Called by the main thread to get key event data sent by <c>_Send</c>.\n\t/// Returns <c>false</c> on timeout.\n\t/// </summary>\n\tpublic bool GetKeyData(int messageId, out Api.KBDLLHOOKSTRUCT data) {\n\t\tdata = _keyData;\n\t\treturn messageId == _messageId;\n\t}\n\t\n\t/// <summary>\n\t/// Called by the main thread to get mouse click/wheel event data sent by <c>_Send</c>.\n\t/// Returns <c>false</c> on timeout.\n\t/// </summary>\n\tpublic bool GetClickWheelData(int messageId, out Api.MSLLHOOKSTRUCT data, out int message) {\n\t\tdata = _mouseData;\n\t\tmessage = _mouseMessage;\n\t\treturn messageId == _messageId;\n\t}\n\t\n\t/// <summary>\n\t/// Called by the main thread to get mouse edge/move event data sent by <c>_Send</c>.\n\t/// Returns <c>false</c> on timeout.\n\t/// </summary>\n\tpublic bool GetEdgeMoveData(int messageId, out MouseTriggers.EdgeMoveDetector_.Result data) {\n\t\tdata = _emData;\n\t\treturn messageId == _messageId;\n\t}\n}\n"
  },
  {
    "path": "Au/Triggers/Triggers_util.cs",
    "content": "namespace Au.Triggers;\n\nstatic class TrigUtil {\n\t/// <summary>\n\t/// Gets left and right modifiers. Uses <see cref=\"keys.isPressed\"/>.\n\t/// Returns <c>modL | modR</c>.\n\t/// </summary>\n\tpublic static KMod GetModLR(out KMod modL, out KMod modR) {\n\t\tKMod L = 0, R = 0;\n\t\tif (keys.isPressed(KKey.LCtrl)) L |= KMod.Ctrl;\n\t\tif (keys.isPressed(KKey.LShift)) L |= KMod.Shift;\n\t\tif (keys.isPressed(KKey.LAlt)) L |= KMod.Alt;\n\t\tif (keys.isPressed(KKey.Win)) L |= KMod.Win;\n\t\tif (keys.isPressed(KKey.RCtrl)) R |= KMod.Ctrl;\n\t\tif (keys.isPressed(KKey.RShift)) R |= KMod.Shift;\n\t\tif (keys.isPressed(KKey.RAlt)) R |= KMod.Alt;\n\t\tif (keys.isPressed(KKey.RWin)) R |= KMod.Win;\n\t\tmodL = L; modR = R;\n\t\treturn L | R;\n\t}\n}\n"
  },
  {
    "path": "Au/Triggers/Types/t-autotext.cs",
    "content": "//CONSIDER: trigger type: When pressed CapsLock, eat trigger text. When triggered, turn off CapsLock.\n//\tTo cancel, user can turn off CapsLock.\n//\tDisplay typed text in OSD. Allow Backspace.\n//\tEg I can use it for tags instead of Alt+B etc.\n//\tAllow to use instead of CapsLock: ScrollLock, Insert. Or not, because unavailable in many keyboards.\n//\tAnother way: press CapsLock, type text, press CapsLock again (then triggers).\n\nnamespace Au.Triggers;\n\n/// <summary>\n/// Flags of autotext triggers.\n/// </summary>\n/// <remarks>\n/// To avoid passing flags to each trigger as the <i>flags</i> parameter, use <see cref=\"AutotextTriggers.DefaultFlags\"/>; its initial value is 0, which means: case-insensitive, erase the typed text with <c>Backspace</c>, modify the replacement text depending on the case of the typed text.\n/// </remarks>\n[Flags]\npublic enum TAFlags : byte {\n\t/// <summary>\n\t/// Case-sensitive.\n\t/// </summary>\n\tMatchCase = 1,\n\t\n\t/// <summary>\n\t/// Let <see cref=\"AutotextTriggerArgs.Replace\"/> don't erase the user-typed text.\n\t/// Without this flag it erases text with the <c>Backspace</c> key or selects with <c>Shift+Left</c>. If <c>Replace</c> not called, text is not erased/selected regardless of this flag.\n\t/// </summary>\n\tDontErase = 2,\n\t\n\t/// <summary>\n\t/// Let <see cref=\"AutotextTriggerArgs.Replace\"/> don't modify the replacement text.\n\t/// <br/>Without <c>ReplaceRaw</c> or <c>MatchCase</c> it:\n\t/// <br/>• If the first character of the typed text is uppercase, makes the first character of the replacement text uppercase.\n\t/// <br/>• If all typed text is uppercase, makes the replacement text uppercase.\n\t/// </summary>\n\tReplaceRaw = 4,\n\t\n\t/// <summary>\n\t/// Let <see cref=\"AutotextTriggerArgs.Replace\"/> remove the postfix delimiter character.\n\t/// </summary>\n\tRemovePostfix = 8,\n\t\n\t/// <summary>\n\t/// Let <see cref=\"AutotextTriggerArgs.Replace\"/> call <see cref=\"AutotextTriggerArgs.Confirm\"/> and do nothing if it returns <c>false</c>.\n\t/// </summary>\n\tConfirm = 16,\n\t\n\t/// <summary>\n\t/// Let <see cref=\"AutotextTriggerArgs.Replace\"/> select text with <c>Shift+Left</c> instead of erasing with <c>Backspace</c>. Except in console windows.\n\t/// See also <see cref=\"AutotextTriggerArgs.ShiftLeft\"/>.\n\t/// </summary>\n\tShiftLeft = 32,\n}\n\n/// <summary>\n/// Postfix type of autotext triggers.\n/// The trigger action runs only when the user ends the autotext with a postfix character or key, unless postfix type is <c>None</c>.\n/// Default: <c>CharOrKey</c>.\n/// </summary>\npublic enum TAPostfix : byte {\n\t/// <summary>A postfix character (see <c>Char</c>) or key (see <c>Key</c>).</summary>\n\tCharOrKey,\n\t\n\t/// <summary>A postfix character specified in the <i>postfixChars</i> parameter or <see cref=\"AutotextTriggers.DefaultPostfixChars\"/> property. If not specified - any non-word character.</summary>\n\tChar,\n\t\n\t/// <summary>The <c>Ctrl</c> or <c>Shift</c> key. Default is <c>Ctrl</c>. You can change it with <see cref=\"AutotextTriggers.PostfixKey\"/>.</summary>\n\tKey,\n\t\n\t/// <summary>Don't need a postfix. The action runs immediately when the user types the autotext.</summary>\n\tNone,\n}\n\n/// <summary>\n/// See <see cref=\"AutotextTriggers.MenuOptions\"/>;\n/// </summary>\n/// <param name=\"pmFlags\"></param>\npublic record class TAMenuOptions(PMFlags pmFlags = PMFlags.ByCaret);\n\n/// <summary>\n/// Represents an autotext trigger.\n/// </summary>\npublic class AutotextTrigger : ActionTrigger {\n\treadonly string _paramsString;\n\tinternal readonly TAMenuOptions menuOptions;\n\t\n\t///\n\tpublic string Text { get; }\n\t\n\t///\n\tpublic TAFlags Flags { get; }\n\t\n\t///\n\tpublic TAPostfix PostfixType { get; }\n\t\n\t///\n\tpublic string PostfixChars { get; }\n\t\n\tinternal AutotextTrigger(ActionTriggers triggers, Action<AutotextTriggerArgs> action, string text, TAFlags flags, TAPostfix postfixType, string postfixChars, TAMenuOptions menuOptions, (string, int) source)\n\t\t: base(triggers, action, true, source) {\n\t\tText = text;\n\t\tFlags = flags;\n\t\tPostfixType = postfixType;\n\t\tPostfixChars = postfixChars;\n\t\tthis.menuOptions = menuOptions;\n\t\t\n\t\tif (flags == 0 && postfixType == 0 && postfixChars == null) {\n\t\t\t_paramsString = text;\n\t\t} else {\n\t\t\tusing (new StringBuilder_(out var b)) {\n\t\t\t\tb.Append(text);\n\t\t\t\tif (flags != 0) b.Append(\"  (\").Append(flags.ToString()).Append(')');\n\t\t\t\tif (postfixType != 0) b.Append(\"  postfixType=\").Append(postfixType.ToString());\n\t\t\t\tif (postfixChars != null) b.Append(\"  postfixChars=\").Append(postfixChars);\n\t\t\t\t_paramsString = b.ToString();\n\t\t\t}\n\t\t}\n\t\t//print.it(this);\n\t}\n\t\n\tinternal override void Run_(TriggerArgs args) => RunT_(args as AutotextTriggerArgs);\n\t\n\t/// <summary>\n\t/// Returns <c>\"Autotext\"</c>.\n\t/// </summary>\n\tpublic override string TypeString => \"Autotext\";\n\t\n\t/// <summary>\n\t/// Returns a string containing trigger parameters.\n\t/// </summary>\n\tpublic override string ParamsString => _paramsString;\n}\n\n/// <summary>\n/// Autotext triggers.\n/// </summary>\n/// <example>See <see cref=\"ActionTriggers\"/>.</example>\npublic class AutotextTriggers : ITriggers, IEnumerable<AutotextTrigger> {\n\tActionTriggers _triggers;\n\tDictionary<int, ActionTrigger> _d = new();\n\t\n\tinternal AutotextTriggers(ActionTriggers triggers) {\n\t\t_triggers = triggers;\n\t\t_simpleReplace = new TASimpleReplace(this);\n\t}\n\t\n\t/// <summary>\n\t/// Adds an autotext trigger.\n\t/// </summary>\n\t/// <param name=\"text\">The action runs when the user types this text and a postfix character or key. By default case-insensitive.</param>\n\t/// <param name=\"flags\">Options. If omitted or <c>null</c>, uses <see cref=\"DefaultFlags\"/>. Some flags are used by <see cref=\"AutotextTriggerArgs.Replace\"/>.</param>\n\t/// <param name=\"postfixType\">Postfix type (character, key, any or none). If omitted or <c>null</c>, uses <see cref=\"DefaultPostfixType\"/>; default - a non-word character or the <c>Ctrl</c> key.</param>\n\t/// <param name=\"postfixChars\">Postfix characters used when postfix type is <c>Char</c> or <c>CharOrKey</c> (default). If omitted or <c>null</c>, uses <see cref=\"DefaultPostfixChars\"/>; default - non-word characters.</param>\n\t/// <param name=\"f_\">[](xref:caller_info)</param>\n\t/// <param name=\"l_\">[](xref:caller_info)</param>\n\t/// <exception cref=\"ArgumentException\">\n\t/// - Text is empty or too long. Can be 1 - 100 characters.\n\t/// - Postfix characters contains letters or digits.\n\t/// </exception>\n\t/// <exception cref=\"InvalidOperationException\">Cannot add triggers after <see cref=\"ActionTriggers.Run\"/> was called, until it returns.</exception>\n\t/// <example>See <see cref=\"ActionTriggers\"/>.</example>\n\tpublic Action<AutotextTriggerArgs> this[string text, TAFlags? flags = null, TAPostfix? postfixType = null, string postfixChars = null, [CallerFilePath] string f_ = null, [CallerLineNumber] int l_ = 0] {\n\t\tset {\n\t\t\t_triggers.ThrowIfRunning_();\n\t\t\tint len = text.Lenn(); if (len < 1 || len > 100) throw new ArgumentException(\"Text length must be 1 - 100.\");\n\t\t\tif (text.Contains('\\n')) { text = text.RxReplace(@\"\\r?\\n\", \"\\r\"); len = text.Length; }\n\t\t\tTAFlags fl = flags ?? DefaultFlags;\n\t\t\tbool matchCase = 0 != (fl & TAFlags.MatchCase);\n\t\t\tif (!matchCase) text = text.Lower();\n\t\t\tvar t = new AutotextTrigger(_triggers, value, text, fl,\n\t\t\t\tpostfixType ?? DefaultPostfixType,\n\t\t\t\t_CheckPostfixChars(postfixChars) ?? DefaultPostfixChars,\n\t\t\t\tMenuOptions,\n\t\t\t\t(f_, l_));\n\t\t\t//create dictionary key from 1-4 last characters lowercase\n\t\t\tint k = 0;\n\t\t\tfor (int i = len - 1, j = 0; i >= 0 && j <= 24; i--, j += 8) {\n\t\t\t\tvar c = text[i]; if (matchCase) c = char.ToLowerInvariant(c);\n\t\t\t\tk |= (byte)c << j;\n\t\t\t}\n\t\t\t//print.it((uint)k);\n\t\t\tt.DictAdd_(_d, k);\n\t\t\t_lastAdded = t;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Allows to add triggers in a more concise way - assign a string, not a function. The string will replace the user-typed text.\n\t/// </summary>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// var ts = Triggers.Autotext.SimpleReplace;\n\t/// ts[\"#su\"] = \"Sunday\"; //the same as Triggers.Autotext[\"#su\"] = o => o.Replace(\"Sunday\");\n\t/// ts[\"#mo\"] = \"Monday\";\n\t/// ]]></code>\n\t/// </example>\n\tpublic TASimpleReplace SimpleReplace => _simpleReplace;\n\tTASimpleReplace _simpleReplace;\n\t\n\t#region options\n\t\n\t/// <summary>\n\t/// Default value for the <i>flags</i> parameter used for triggers added afterwards.\n\t/// </summary>\n\tpublic TAFlags DefaultFlags { get; set; }\n\t\n\t/// <summary>\n\t/// Default value for the <i>postfixType</i> parameter used for triggers added afterwards.\n\t/// </summary>\n\tpublic TAPostfix DefaultPostfixType { get; set; }\n\t\n\t/// <summary>\n\t/// Default value for the <i>postfixChars</i> parameter used for triggers added afterwards.\n\t/// Default: <c>null</c>.\n\t/// </summary>\n\t/// <remarks>\n\t/// If <c>null</c> (default), postfix characters are all except alpha-numeric (see <see cref=\"char.IsLetterOrDigit\"/>).\n\t/// The value cannot contain alpha-numeric characters (exception) and <see cref=\"WordCharsPlus\"/> characters (triggers will not work).\n\t/// For <c>Enter</c> use <c>'\\r'</c>.\n\t/// </remarks>\n\t/// <exception cref=\"ArgumentException\">The value contains letters or digits.</exception>\n\tpublic string DefaultPostfixChars {\n\t\tget => _defaultPostfixChars;\n\t\tset => _defaultPostfixChars = _CheckPostfixChars(value);\n\t}\n\tstring _defaultPostfixChars;\n\t\n\tstatic string _CheckPostfixChars(string s) {\n\t\tif (s.NE()) return null;\n\t\tint k = 0;\n\t\tfor (int i = 0; i < s.Length; i++) {\n\t\t\tchar c = s[i];\n\t\t\tif (char.IsLetterOrDigit(c)) throw new ArgumentException(\"Postfix characters contains letters or digits.\");\n\t\t\tif (c == '\\r') k |= 1;\n\t\t\tif (c == '\\n') k |= 2;\n\t\t}\n\t\tif (k == 2) print.warning(\"Postfix characters contains \\\\n (Ctrl+Enter) but no \\\\r (Enter).\");\n\t\treturn s;\n\t}\n\t\n\t/// <summary>\n\t/// The postfix key for all triggers where postfix type is <see cref=\"TAPostfix.Key\"/> or <see cref=\"TAPostfix.CharOrKey\"/> (default).\n\t/// Can be <c>Ctrl</c> (default), <c>Shift</c>, <c>LCtrl</c>, <c>RCtrl</c>, <c>LShift</c> or <c>RShift</c>.\n\t/// </summary>\n\t/// <exception cref=\"ArgumentException\">The value is not <c>Ctrl</c> or <c>Shift</c>.</exception>\n\t/// <remarks>\n\t/// This property is applied to all triggers, not just to those added afterwards.\n\t/// </remarks>\n\tpublic KKey PostfixKey {\n\t\tget => _postfixKey;\n\t\tset {\n\t\t\tvar mod = keys.Internal_.KeyToMod(value);\n\t\t\tswitch (mod) {\n\t\t\tcase KMod.Ctrl: case KMod.Shift: break;\n\t\t\tdefault: throw new ArgumentException(\"Must be Ctrl, Shift, LCtrl, RCtrl, LShift or RShift.\");\n\t\t\t}\n\t\t\t_postfixMod = mod; _postfixKey = value;\n\t\t}\n\t}\n\tKKey _postfixKey = KKey.Ctrl;\n\tKMod _postfixMod = KMod.Ctrl;\n\t\n\t/// <summary>\n\t/// Additional word characters (non-delimiters).\n\t/// Default: <c>null</c>.\n\t/// </summary>\n\t/// <remarks>\n\t/// By default, only alpha-numeric characters (<see cref=\"char.IsLetterOrDigit\"/> returns <c>true</c>) are considered word characters. You can use this property to add more word characters, for example <c>\"_#\"</c>.\n\t/// This is used to avoid activating triggers when a trigger text found inside a word.\n\t/// This property is applied to all triggers, not just to those added afterwards.\n\t/// </remarks>\n\tpublic string WordCharsPlus { get; set; }\n\t\n\t/// <summary>\n\t/// Options for menus shown by <see cref=\"AutotextTriggerArgs.Menu\"/> and <see cref=\"AutotextTriggerArgs.Confirm\"/>.\n\t/// Used for triggers added afterwards.\n\t/// </summary>\n\t/// <example>\n\t/// Show menus by the text cursor. If impossible - in the center of the active window.\n\t/// <code><![CDATA[\n\t/// tt.MenuOptions = new(PMFlags.ByCaret | PMFlags.WindowCenter);\n\t/// ]]></code>\n\t/// </example>\n\t/// <seealso cref=\"popupMenu.caretRectFunc\"/>\n\tpublic TAMenuOptions MenuOptions { get; set; }\n\t\n\t/// <summary>\n\t/// Clears all options that affect autotext triggers created after setting the option: <see cref=\"DefaultFlags\"/>, <see cref=\"DefaultPostfixType\"/>, <see cref=\"DefaultPostfixChars\"/>, <see cref=\"MenuOptions\"/>.\n\t/// </summary>\n\tpublic void ResetOptions() {\n\t\tthis.DefaultFlags = 0;\n\t\tthis.DefaultPostfixType = 0;\n\t\tthis._defaultPostfixChars = null;\n\t\tthis.MenuOptions = null;\n\t\t\n\t\t//cannot reset these because they are for all triggers, not only for triggers added afterwards\n\t\t//this.PostfixKey = KKey.Ctrl;\n\t\t//this.WordCharsPlus = null;\n\t}\n\t\n\t#endregion\n\t\n\t/// <summary>\n\t/// The last added trigger.\n\t/// </summary>\n\tpublic AutotextTrigger Last => _lastAdded;\n\tAutotextTrigger _lastAdded;\n\t\n\tbool ITriggers.HasTriggers => _lastAdded != null;\n\t\n\tvoid ITriggers.StartStop(bool start) {\n\t\tthis._len = 0;\n\t\tthis._singlePK = false;\n\t\tthis._wFocus = default;\n\t\tthis._deadKey = default;\n\t}\n\t\n\tinternal unsafe void HookProc(HookData.Keyboard k, TriggerHookContext thc) {\n\t\tDebug.Assert(!k.IsInjectedByAu); //server must ignore\n\t\t\n\t\t//print.it(k);\n\t\t//perf.first();\n\t\t\n\t\tif (ResetEverywhere) { //set by mouse hooks on click left|right and by keyboard hooks on Au-injected key events. In shared memory.\n\t\t\tResetEverywhere = false;\n\t\t\t_Reset();\n\t\t}\n\t\t\n\t\tif (k.IsUp) {\n\t\t\tif (_singlePK) {\n\t\t\t\t_singlePK = false;\n\t\t\t\tif (_IsPostfixMod(thc.ModThis)) {\n\t\t\t\t\t//print.it(\"< Ctrl up >\");\n\t\t\t\t\t_Trigger(default, true, _GetFocusedWindow(), thc);\n\t\t\t\t\t//goto gReset; //no, resets if triggered, else don't reset\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tbool _IsPostfixMod(KMod mod) => mod == _postfixMod && (_postfixKey <= KKey.Ctrl || k.vkCode == _postfixKey) && !k.IsInjected;\n\t\t\n\t\tvar modd = thc.ModThis;\n\t\tif (modd != 0) {\n\t\t\t_singlePK = _IsPostfixMod(modd) && thc.Mod == _postfixMod;\n\t\t\treturn;\n\t\t}\n\t\t_singlePK = false;\n\t\t\n\t\t//TODO3: use KeyToTextConverter.\n\t\tif (k.IsAlt && 0 == (thc.Mod & (KMod.Ctrl | KMod.Shift))) goto gReset; //Alt+key without other modifiers. Info: AltGr can add Ctrl, therefore we process it. Info: still not menu mode. Tested: never types a character, except Alt+numpad numbers.\n\t\t\n\t\tvar vk = k.vkCode;\n\t\tif (vk >= KKey.PageUp && vk <= KKey.Down) goto gReset; //PageUp, PageDown, End, Home, Left, Up, Right, Down\n\t\t\n\t\twnd wFocus = _GetFocusedWindow();\n\t\tif (wFocus.Is0) goto gReset;\n\t\t\n\t\tvar c = stackalloc char[8]; int n;\n\t\tif (vk == KKey.Packet) {\n\t\t\tc[0] = (char)k.scanCode;\n\t\t\tn = 1;\n\t\t} else {\n\t\t\tn = _KeyToChar(c, vk, k.scanCode, wFocus, thc.Mod);\n\t\t\tif (n == 0) { //non-char key\n\t\t\t\tif (thc.Mod == 0) switch (vk) { case KKey.CapsLock: case KKey.NumLock: case KKey.ScrollLock: case KKey.Insert: case KKey.Delete: return; }\n\t\t\t\tgoto gReset;\n\t\t\t}\n\t\t\tif (n < 0) return; //dead key\n\t\t}\n\t\t//print.it(n, c[0], c[1]);\n\t\t\n\t\tfor (int i = 0; i < n; i++) _Trigger(c[i], false, wFocus, thc);\n\t\t\n\t\treturn;\n\t\tgReset:\n\t\t_Reset();\n\t}\n\t\n\tstatic wnd _GetFocusedWindow() {\n\t\tif (!miscInfo.getGUIThreadInfo(out var gt)) return wnd.active;\n\t\tif (0 != (gt.flags & (GTIFlags.INMENUMODE | GTIFlags.INMOVESIZE))) return default; //the character will not be typed when showing menu (or just Alt or F10 pressed) or moving/resizing window. Of course this will not work with nonstandard menus, eg in Word, as well as with other controls that don't accept text.\n\t\treturn gt.hwndFocus; //if no focus, the thread will not receive wm-keydown etc\n\t}\n\t\n\tint _len; //count of valid user-typed characters in _text\n\tbool _singlePK; //used to detect postfix key (Ctrl or Shift)\n\twnd _wFocus; //the focused window/control. Used to reset if focus changed.\n\t\n\tvoid _Reset() {\n\t\t_len = 0;\n\t\t_singlePK = false;\n\t\t//_wFocus = default;\n\t}\n\t\n\tinternal static unsafe bool ResetEverywhere {\n\t\tget => SharedMemory_.Ptr->triggers.resetAutotext;\n\t\tset => SharedMemory_.Ptr->triggers.resetAutotext = value;\n\t}\n\t\n\tunsafe void _Trigger(char c, bool isPK, wnd wFocus, TriggerHookContext thc) {\n\t\t//perf.next();\n\t\tif (wFocus != _wFocus) {\n\t\t\t_Reset();\n\t\t\t_wFocus = wFocus;\n\t\t}\n\t\tif (wFocus.Is0) return;\n\t\t\n\t\tint nc = _len;\n\t\t_DetectedPostfix postfixType;\n\t\tchar postfixChar = default;\n\t\tif (isPK) {\n\t\t\tpostfixType = _DetectedPostfix.Key;\n\t\t} else {\n\t\t\t//print.it((int)c);\n\t\t\t\n\t\t\tif (c < ' ' || c == 127) {\n\t\t\t\tswitch (c) {\n\t\t\t\tcase (char)8: //Backspace\n\t\t\t\t\tif (_len > 0) _len--;\n\t\t\t\t\treturn;\n\t\t\t\tcase '\\t':\n\t\t\t\tcase '\\r':\n\t\t\t\tcase '\\n':\n\t\t\t\t\tbreak;\n\t\t\t\tdefault: //Ctrl+C etc generate control characters. Also Esc.\n\t\t\t\t\t_Reset();\n\t\t\t\t\treturn;\n\t\t\t\t\t//tested: control codes <32 in most windows don't type characters\n\t\t\t\t\t//tested: Ctrl+Backspace (127) in some windows types a rectangle, in others erases previous word\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tbool isWordChar = _IsWordChar(c);\n\t\t\tpostfixType = isWordChar ? _DetectedPostfix.None : _DetectedPostfix.Delim;\n\t\t\t\n\t\t\tconst int c_bufLen = 127;\n\t\t\tif (nc >= c_bufLen) { //buffer full. Remove word from beginning.\n\t\t\t\tint i;\n\t\t\t\tfor (i = 0; i < c_bufLen; i++) if (!_text[i].isWordChar) break;\n\t\t\t\tif (i == c_bufLen) {\n\t\t\t\t\tif (!isWordChar) { _len = 0; return; }\n\t\t\t\t\ti = c_bufLen - 20; //remove several first chars. Triggers will not match anyway, because max string lenhth is 100.\n\t\t\t\t}\n\t\t\t\tnc = c_bufLen - ++i;\n\t\t\t\tfixed (_Char* p = _text) Api.memmove(p, p + i, nc * sizeof(_Char));\n\t\t\t}\n\t\t\t\n\t\t\t_text[nc] = new _Char(c, isWordChar);\n\t\t\t_len = nc + 1;\n\t\t\tif (isWordChar) nc++; else postfixChar = c;\n\t\t\t\n\t\t\t//DebugPrintText();\n\t\t}\n\t\t\n\t\tif (nc == 0) return;\n\t\t//perf.next();\n\t\tg1:\n\t\tfor (int k = 0, ii = nc - 1, jj = 0; ii >= 0 && jj <= 24; ii--, jj += 8) { //create dictionary key from 1-4 last characters lowercase\n\t\t\tk |= (byte)_text[ii].cLow << jj;\n\t\t\t//print.it((uint)k);\n\t\t\tif (_d.TryGetValue(k, out var v)) {\n\t\t\t\tAutotextTriggerArgs args = null;\n\t\t\t\tfor (; v != null; v = v.next) {\n\t\t\t\t\tvar x = v as AutotextTrigger;\n\t\t\t\t\t\n\t\t\t\t\tvar s = x.Text;\n\t\t\t\t\tint i = nc - s.Length;\n\t\t\t\t\tif (i < 0) continue;\n\t\t\t\t\tif (i > 0 && _text[i - 1].isWordChar) continue;\n\t\t\t\t\t\n\t\t\t\t\tif (0 != (x.Flags & TAFlags.MatchCase)) {\n\t\t\t\t\t\tfor (int j = 0; i < nc; i++, j++) if (_text[i].c != s[j]) break;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tfor (int j = 0; i < nc; i++, j++) if (_text[i].cLow != s[j]) break;\n\t\t\t\t\t}\n\t\t\t\t\tif (i < nc) continue;\n\t\t\t\t\t\n\t\t\t\t\tswitch (x.PostfixType) {\n\t\t\t\t\tcase TAPostfix.CharOrKey:\n\t\t\t\t\t\tif (postfixType == _DetectedPostfix.None) continue;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase TAPostfix.Char:\n\t\t\t\t\t\tif (postfixType != _DetectedPostfix.Delim) continue;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase TAPostfix.Key:\n\t\t\t\t\t\tif (postfixType != _DetectedPostfix.Key) continue;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tif (x.PostfixChars != null && postfixType == _DetectedPostfix.Delim && x.PostfixChars.IndexOf(c) < 0) continue;\n\t\t\t\t\t\n\t\t\t\t\tif (v.DisabledThisOrAll) continue;\n\t\t\t\t\tif (_triggers.triggersListWindowIsActive_) continue;\n\t\t\t\t\t\n\t\t\t\t\tif (args == null) { //may need for scope callbacks too\n\t\t\t\t\t\tbool hasPChar = postfixType == _DetectedPostfix.Delim;\n\t\t\t\t\t\tint n = s.Length, to = nc; if (hasPChar) { n++; to++; }\n\t\t\t\t\t\tvar tt = new string('\\0', n);\n\t\t\t\t\t\ti = to - n; fixed (char* p = tt) for (int j = 0; i < to;) p[j++] = _text[i++].c;\n\t\t\t\t\t\tthc.args = args = new AutotextTriggerArgs(x, thc.Window, tt, hasPChar);\n\t\t\t\t\t} else args.Trigger = x;\n\t\t\t\t\t\n\t\t\t\t\tif (!x.MatchScopeWindowAndFunc_(thc)) continue;\n\t\t\t\t\t\n\t\t\t\t\t_Reset(); //CONSIDER: flag DontReset. If the action generates keyboard events or mouse clicks, our kooks will reset.\n\t\t\t\t\t\n\t\t\t\t\tthc.trigger = x;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t}\n\t\t}\n\t\t//maybe there are items where text ends with delim and no postfix\n\t\tif (postfixType == _DetectedPostfix.Delim) {\n\t\t\tpostfixType = _DetectedPostfix.None;\n\t\t\tpostfixChar = '\\0';\n\t\t\tnc++;\n\t\t\tgoto g1;\n\t\t}\n\t\t//perf.nw(); //about 90% of time takes _KeyToChar (ToUnicodeEx and GetKeyboardLayout).\n\t}\n\t\n\tunsafe int _KeyToChar(char* c, KKey vk, uint sc, wnd wFocus, KMod mod) {\n\t\tvar hkl = Api.GetKeyboardLayout(wFocus.ThreadId);\n\t\tvar ks = stackalloc byte[256];\n\t\t_SetKS(mod);\n\t\tbool win10 = osVersion.minWin10_1607; //the API resets dead key etc, but on new OS flag 4 prevents it\n\t\tint n = Api.ToUnicodeEx((uint)vk, sc, ks, c, 8, win10 ? 4u : 0u, hkl);\n\t\tif (!win10) {\n\t\t\t//if need, set dead key again\n\t\t\tvar d = stackalloc char[8];\n\t\t\tif (_deadKey.vk != 0 && _deadKey.hkl == hkl) {\n\t\t\t\t_SetKS(_deadKey.mod);\n\t\t\t\tApi.ToUnicodeEx((uint)_deadKey.vk, _deadKey.sc, ks, d, 8, 0, hkl);\n\t\t\t\t_deadKey.vk = 0;\n\t\t\t} else if (n < 0) {\n\t\t\t\t_deadKey = new(vk, mod, sc, hkl);\n\t\t\t\tApi.ToUnicodeEx((uint)vk, sc, ks, d, 8, 0, hkl);\n\t\t\t}\n\t\t}\n\t\t\n\t\tvoid _SetKS(KMod m) {\n\t\t\tks[(int)KKey.Shift] = (byte)((0 != (m & KMod.Shift)) ? 0x80 : 0);\n\t\t\tks[(int)KKey.Ctrl] = (byte)((0 != (m & KMod.Ctrl)) ? 0x80 : 0);\n\t\t\tks[(int)KKey.Alt] = (byte)((0 != (m & KMod.Alt)) ? 0x80 : 0);\n\t\t\tks[(int)KKey.Win] = (byte)((0 != (m & KMod.Win)) ? 0x80 : 0);\n\t\t\tks[(int)KKey.CapsLock] = (byte)(keys.isCapsLock ? 1 : 0); //don't need this for num lock\n\t\t}\n\t\t\n\t\treturn n;\n\t\t\n\t\t//info: this works, but:\n\t\t//1. Does not work with eg Chinese input method.\n\t\t//2. Catches everything that would later be changed by the app, or by a next hook, etc.\n\t\t//3. Don't know how to get Alt+numpad characters. Ignore them.\n\t\t//\tOn Alt up could call tounicodeex with sc with flag 0x8000. It gets the char, but resets keyboard state, and the char is not typed.\n\t\t//4. In console windows does not work with Unicode characters.\n\t\t\n\t\t//if(MapVirtualKeyEx(vk, MAPVK_VK_TO_CHAR, hkl)&0x80000000) { print.it(\"DEAD\"); return -1; } //this cannot be used because resets dead key\n\t}\n\t\n\t_DeadKey _deadKey;\n\trecord struct _DeadKey(KKey vk, KMod mod, uint sc, nint hkl);\n\t\n\t//User-typed characters. _len characters are valid.\n\t_Char[] _text = new _Char[128];\n\t\n\tstruct _Char {\n\t\tpublic char c, cLow;\n\t\tpublic bool isWordChar;\n\t\t\n\t\tpublic _Char(char ch, bool isWordChar) {\n\t\t\tc = ch;\n\t\t\tcLow = char.ToLowerInvariant(ch);\n\t\t\tthis.isWordChar = isWordChar;\n\t\t}\n\t}\n\t\n\tenum _DetectedPostfix { None, Delim, Key }\n\t\n\t[MethodImpl(MethodImplOptions.AggressiveInlining)]\n\tbool _IsWordChar(char c) {\n\t\tif (char.IsLetterOrDigit(c)) return true; //speed: 4 times faster than Api.IsCharAlphaNumeric. Tested with a string containing 90% ASCII chars.\n\t\tvar v = WordCharsPlus;\n\t\treturn v != null && v.Contains(c);\n\t}\n\t\n\t[Conditional(\"DEBUG\")]\n\tunsafe void _DebugPrintText() {\n\t\tvar s = new string(' ', _len);\n\t\tfixed (char* p = s) for (int i = 0; i < s.Length; i++) p[i] = _text[i].c;\n\t\tprint.it(s);\n\t}\n\t\n\tinternal static unsafe void JitCompile() {\n\t\tJit_.Compile(typeof(AutotextTriggers), nameof(HookProc), nameof(_Trigger), nameof(_KeyToChar));\n\t}\n\t\n\t/// <summary>\n\t/// Used by <c>foreach</c> to enumerate added triggers.\n\t/// </summary>\n\tpublic IEnumerator<AutotextTrigger> GetEnumerator() {\n\t\tforeach (var kv in _d) {\n\t\t\tfor (var v = kv.Value; v != null; v = v.next) {\n\t\t\t\tvar x = v as AutotextTrigger;\n\t\t\t\tyield return x;\n\t\t\t}\n\t\t}\n\t}\n\t\n\tIEnumerator IEnumerable.GetEnumerator() => GetEnumerator();\n}\n\n/// <summary>\n/// Arguments for actions of autotext triggers.\n/// You can use functions <see cref=\"Replace\"/> and <see cref=\"Menu\"/> to replace user-typed text.\n/// </summary>\npublic class AutotextTriggerArgs : TriggerArgs {\n\t///\n\tpublic AutotextTrigger Trigger { get; internal set; }\n\t\n\t///\n\t[EditorBrowsable(EditorBrowsableState.Never)]\n\tpublic override ActionTrigger TriggerBase => Trigger;\n\t\n\t/// <summary>\n\t/// The active window.\n\t/// </summary>\n\tpublic wnd Window { get; }\n\t\n\t/// <summary>\n\t/// The user-typed text. If <see cref=\"HasPostfixChar\"/>==<c>true</c>, the last character is the postfix delimiter character.\n\t/// </summary>\n\tpublic string Text { get; }\n\t\n\t/// <summary>\n\t/// <c>true</c> if the autotext activated when the user typed a postfix delimiter character. Then it is the last character in <see cref=\"Text\"/>.\n\t/// </summary>\n\tpublic bool HasPostfixChar { get; }\n\t\n\t/// <summary>\n\t/// If <c>true</c>, <see cref=\"Replace\"/> will select text with <c>Shift+Left</c> instead of erasing with <c>Backspace</c>. Except in console windows.\n\t/// </summary>\n\t/// <remarks>\n\t/// Initially <c>true</c> if flag <see cref=\"TAFlags.ShiftLeft\"/> is set. Can be changed by a callback function, for example to use or not use <c>Shift+Left</c> only with some windows.\n\t/// </remarks>\n\tpublic bool ShiftLeft { get; set; }\n\t\n\t///\n\tpublic AutotextTriggerArgs(AutotextTrigger trigger, wnd w, string text, bool hasPChar) {\n\t\tTrigger = trigger;\n\t\tWindow = w;\n\t\tText = text;\n\t\tHasPostfixChar = hasPChar;\n\t\tShiftLeft = trigger.Flags.Has(TAFlags.ShiftLeft);\n\t\t\n\t\t//print.it($\"'{text}'\", hasPChar);\n\t}\n\t\n\t///\n\tpublic override string ToString() => \"Trigger: \" + Trigger;\n\t\n\t/// <summary>\n\t/// Replaces the user-typed text with the specified text or/and HTML.\n\t/// </summary>\n\t/// <param name=\"text\">\n\t/// The replacement text. Can be <c>null</c>.\n\t/// Can contain <c>[[|]]</c> to move the text cursor (caret) there with the <c>Left</c> key; not if <i>html</i> specified.\n\t/// </param>\n\t/// <param name=\"html\">\n\t/// The replacement HTML. Can be full HTML or fragment. See <see cref=\"clipboardData.AddHtml\"/>.\n\t/// Can be specified only <i>text</i> or only <i>html</i> or both. If both, will paste <i>html</i> in apps that support it, elsewhere <i>text</i>. If only <i>html</i>, in apps that don't support HTML will paste <i>html</i> as text.\n\t/// </param>\n\t/// <remarks>\n\t/// Options for this function can be specified when adding triggers, in the <i>flags</i> parameter. Or before adding triggers, with <see cref=\"AutotextTriggers.DefaultFlags\"/>.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// Triggers.Autotext[\"#exa\"] = o => o.Replace(\"<example>[[|]]</example>\");\n\t/// ]]></code>\n\t/// More examples: <see cref=\"ActionTriggers\"/>.\n\t/// </example>\n\tpublic void Replace(string text, string html = null) {\n\t\tif (text == \"\") text = null;\n\t\tif (html == \"\") html = null;\n\t\t_Replace(text, html, null);\n\t}\n\t\n\t/// <summary>\n\t/// Replaces the user-typed text with the specified text, keys, clipboard data, etc.\n\t/// </summary>\n\t/// <remarks>\n\t/// Options for this function can be specified when adding triggers, in the <i>flags</i> parameter. Or before adding triggers, with <see cref=\"AutotextTriggers.DefaultFlags\"/>. This function uses <see cref=\"TAFlags.Confirm\"/>, <see cref=\"TAFlags.DontErase\"/>, <see cref=\"TAFlags.ShiftLeft\"/>, <see cref=\"TAFlags.RemovePostfix\"/>.\n\t/// \n\t/// If used flag <see cref=\"TAFlags.Confirm\"/>, for label can be used first argument with prefix <c>\"!!\"</c>; else displays all string arguments.\n\t/// </remarks>\n\t/// <inheritdoc cref=\"keys.send\" path=\"/param\"/>\n\tpublic void Replace2([ParamString(PSFormat.Keys)] params KKeysEtc[] keysEtc) {\n\t\tNot_.Null(keysEtc);\n\t\t_Replace(null, null, keysEtc);\n\t}\n\t\n\tvoid _Replace(string r, string html, KKeysEtc[] ke) {\n\t\tbool onlyText = r != null && html == null;\n\t\tvar flags = this.Trigger.Flags;\n\t\t\n\t\tstring t = this.Text;\n\t\t\n\t\tint caret = -1;\n\t\tif (onlyText) {\n\t\t\tcaret = r.Find(\"[[|]]\");\n\t\t\tif (caret >= 0) r = r.Remove(caret, 5);\n\t\t\t\n\t\t\tif (!flags.HasAny(TAFlags.ReplaceRaw | TAFlags.MatchCase)) {\n\t\t\t\tint len = t.Length; if (this.HasPostfixChar) len--;\n\t\t\t\tint i; for (i = 0; i < len; i++) if (char.IsLetterOrDigit(t[i])) break; //eg if t is <c>\"#abc\"</c>, we need a, not #\n\t\t\t\tif (i < len && char.IsUpper(t[i])) {\n\t\t\t\t\tbool allUpper = false; //make r ucase if t contains 0 lcase chars and >=2 ucase chars\n\t\t\t\t\twhile (++i < len) {\n\t\t\t\t\t\tvar uc = char.GetUnicodeCategory(t[i]);\n\t\t\t\t\t\tif (uc == UnicodeCategory.LowercaseLetter) { allUpper = false; break; }\n\t\t\t\t\t\tif (uc == UnicodeCategory.UppercaseLetter) allUpper = true;\n\t\t\t\t\t}\n\t\t\t\t\tr = r.Upper(allUpper ? SUpper.AllChars : SUpper.FirstChar);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (flags.Has(TAFlags.Confirm)) {\n\t\t\tstring confirmText;\n\t\t\tif (!ke.NE_()) {\n\t\t\t\tconfirmText = null;\n\t\t\t\tif (ke[0].Value is string s2 && s2.Starts(\"!!\")) {\n\t\t\t\t\tconfirmText = s2[2..];\n\t\t\t\t\tke = ke.RemoveAt(0);\n\t\t\t\t} else {\n\t\t\t\t\tforeach (var v in ke) if (v.Value is string s1) { if (confirmText != null) confirmText += \", \"; confirmText += s1; }\n\t\t\t\t}\n\t\t\t} else confirmText = r ?? html;\n\t\t\tif (!Confirm(confirmText)) return;\n\t\t}\n\t\t\n\t\tvar k = new keys(opt.key);\n\t\tvar optk = k.Options;\n\t\t\n\t\t//UWP is very slow. If text is long and fast: 1. Often does not display part of it until next key. 2. Starts to display with a long delay.\n\t\t//\tWinUI3 even slower, and has problem 2 but not 1. Because of 2 it's better to send text slower.\n\t\twnd ww = this.Window.Window;\n\t\tbool uwp = 0 != ww.IsUwpApp || ww.IsWinUI_;\n\t\t//bool uwp = keys.isScrollLock;\n\t\tif (uwp) {\n\t\t\toptk.TextSpeed = Math.Max(optk.TextSpeed, 10); //default 0\n\t\t\toptk.KeySpeed = Math.Max(optk.KeySpeed, 20); //default 2\n\t\t\toptk.KeySpeedClipboard = Math.Max(optk.KeySpeedClipboard, 30); //default 5\n\t\t\tint n1 = optk.PasteLength - 100; if (n1 > 0) optk.PasteLength = 100 + n1 / 5; //default 200 -> 120\n\t\t} else {\n\t\t\toptk.KeySpeed = Math.Clamp(optk.KeySpeed, 2, 20);\n\t\t\toptk.TextSpeed = Math.Min(optk.TextSpeed, 10);\n\t\t}\n\t\toptk.PasteWorkaround = true;\n\t\t//info: later Options.Hook can override these values.\n\t\t\n\t\tint erase = flags.Has(TAFlags.DontErase) ? (this.HasPostfixChar ? 1 : 0) : t.Length;\n\t\tif (erase > 0) {\n\t\t\tbool shiftLeft = this.ShiftLeft && !wnd.active.IsConsole;\n\t\t\tif (shiftLeft) { k.AddKey(KKey.Shift, true); k.AddKey(KKey.Left); } else k.AddKey(KKey.Back);\n\t\t\tif (erase > 1) k.AddRepeat(erase);\n\t\t\tif (shiftLeft) k.AddKey(KKey.Shift, false);\n\t\t\t//note: Back down down ... up does not work with some apps\n\t\t\t\n\t\t\t//some apps have async input and eg don't erase all if too fast.\n\t\t\t//\tUWP is the champion. Also noticed in Chrome address bar (rare), Dreamweaver when pasting (1/5 times), etc.\n\t\t\tint sleep = 5 + erase; if (uwp) sleep *= 10;\n\t\t\tk.AddSleep(sleep);\n\t\t\tk.Pasting += (_, _) => wait.ms(sleep * 2);\n\t\t} else if (uwp) {\n\t\t\tk.Pasting += (_, _) => wait.ms(50);\n\t\t}\n\t\t\n\t\tKKey pKey = default; char pChar = default;\n\t\tif (this.HasPostfixChar && !flags.Has(TAFlags.RemovePostfix)) {\n\t\t\tchar ch = t[^1];\n\t\t\tif (ch == ' ' || ch == '\\r' || ch == '\\t') pKey = (KKey)ch; //avoid trimming of pasted text or pasting '\\r'; here VK_ == ch.\n\t\t\telse if (onlyText) r += ch.ToString();\n\t\t\telse pChar = ch;\n\t\t}\n\t\t\n\t\tif (ke != null) k.Add(ke); else k.AddText(r, html);\n\t\t\n\t\tif (pKey != default) k.AddKey(pKey); else if (pChar != default) k.AddText(pChar.ToString(), OKeyText.KeysOrChar);\n\t\t\n\t\tif (caret >= 0) {\n\t\t\tint keyLeft = 0;\n\t\t\tfor (int i = caret; i < r.Length; keyLeft++) {\n\t\t\t\tchar c = r[i++];\n\t\t\t\tif (c == '\\r') {\n\t\t\t\t\tif (i < r.Length && r[i] == '\\n') i++;\n\t\t\t\t} else if (char.IsHighSurrogate(c)) {\n\t\t\t\t\tif (i < r.Length && char.IsLowSurrogate(r[i])) i++;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (pKey != default || pChar != default) keyLeft++;\n\t\t\tif (keyLeft > 0) {\n\t\t\t\tk.AddKey(KKey.Left);\n\t\t\t\tif (keyLeft > 1) k.AddRepeat(keyLeft);\n\t\t\t}\n\t\t}\n\t\t\n\t\ttry { k.SendNow(); }\n\t\tcatch { } //unlikely\n\t}\n\t\n\t/// <summary>\n\t/// If <see cref=\"HasPostfixChar\"/>==<c>true</c>, sends the postfix character (last character of <see cref=\"Text\"/>) to the active window.\n\t/// </summary>\n\tpublic void SendPostfix() {\n\t\tif (this.HasPostfixChar) {\n\t\t\tvar k = new keys(opt.key).AddText(this.Text[^1..], OKeyText.KeysOrChar);\n\t\t\ttry { k.SendNow(); }\n\t\t\tcatch { } //unlikely\n\t\t}\n\t\t//CONSIDER: AddText -> AddChar. Also in other place. But the speed option is different.\n\t}\n\t\n\t/// <summary>\n\t/// Shows a 1-item menu below the text cursor (caret) or mouse cursor.\n\t/// </summary>\n\t/// <returns>Returns <c>true</c> if the user clicked the item or pressed <c>Enter</c> or <c>Tab</c>.</returns>\n\t/// <param name=\"text\">Text to display. This function limits it to 300 characters. Default: <c>\"Replace\"</c>.</param>\n\t/// <remarks>\n\t/// This function is used by <see cref=\"Replace\"/> when used flag <see cref=\"TAFlags.Confirm\"/>.\n\t/// \n\t/// The user can close the menu with <c>Enter</c>, <c>Tab</c> or <c>Esc</c>. Other keys close the menu and are passed to the active window.\n\t/// </remarks>\n\t/// <seealso cref=\"AutotextTriggers.MenuOptions\"/>\n\t/// <seealso cref=\"popupMenu.defaultFont\"/>\n\t/// <seealso cref=\"popupMenu.defaultMetrics\"/>\n\t/// <example>\n\t/// Code in file <c>Autotext triggers</c>.\n\t/// <code><![CDATA[\n\t/// //var tt = Triggers.Autotext;\n\t/// tt[\"con1\", TAFlags.Confirm] = o => o.Replace(\"Flag Confirm\");\n\t/// tt[\"con2\"] = o => { if(o.Confirm(\"Example\")) o.Replace(\"Function Confirm\"); };\n\t/// ]]></code>\n\t/// </example>\n\tpublic bool Confirm(string text = \"Replace\") {\n\t\ttext ??= \"Replace\";\n\t\tvar m = new popupMenu { RawText = true };\n\t\tm.Add(1, text.Limit(300));\n\t\tm.KeyboardHook = (m, g) => {\n\t\t\tif (g.Key is KKey.Enter or KKey.Tab or KKey.Escape) {\n\t\t\t\tif (g.Key != KKey.Escape) m.FocusedItem = m.Items.First();\n\t\t\t\treturn PMKHook.Default;\n\t\t\t}\n\t\t\treturn PMKHook.Close;\n\t\t};\n\t\t\n\t\treturn 1 == _ShowMenu(m);\n\t}\n\t\n\t/// <summary>\n\t/// Creates and shows a menu below the text cursor (caret) or mouse cursor, and calls <see cref=\"Replace(string, string)\"/>.\n\t/// </summary>\n\t/// <param name=\"items\">\n\t/// Menu items. An item can be specified as:\n\t/// <br/>• string - the replacement text. Also it's the menu item label.\n\t/// <br/>• <see cref=\"TAMenuItem\"/> - allows to set custom label and the replacement text and/or HTML.\n\t/// <br/>• <c>null</c> - separator.\n\t/// <br/>Label can contain tooltip like <c>\"Text\\0 Tooltip\"</c>.\n\t/// Replacement text can contain <c>[[|]]</c> to move the caret there (see <see cref=\"AutotextTriggerArgs.Replace\"/>).\n\t/// </param>\n\t/// <remarks>\n\t/// Keyboard:\n\t/// - <c>Esc</c> - close the menu.\n\t/// - <c>Enter</c>, <c>Tab</c> - select the focused or the first item.\n\t/// - <c>Down</c>, <c>Up</c>, <c>End</c>, <c>Home</c>, <c>PageDown</c>, <c>PageUp</c> - focus menu items.\n\t/// - Also to select menu items can type the number characters displayed at the right.\n\t/// - Other keys close the menu and are passed to the active window.\n\t/// </remarks>\n\t/// <seealso cref=\"AutotextTriggers.MenuOptions\"/>\n\t/// <seealso cref=\"popupMenu.defaultFont\"/>\n\t/// <seealso cref=\"popupMenu.defaultMetrics\"/>\n\t/// <example>\n\t/// Code in file <c>Autotext triggers</c>.\n\t/// <code><![CDATA[\n\t/// //var tt = Triggers.Autotext;\n\t/// tt[\"m1\"] = o => o.Menu([\n\t/// \t\"https://www.example.com\",\n\t/// \t\"<tag>[[|]]</tag>\",\n\t/// \tnew(\"Label example\", \"TEXT1\"),\n\t/// \tnull,\n\t/// \tnew(\"HTML example\", \"TEXT2\", \"<b>TEXT2</b>\"),\n\t/// \tnew(null, \"TEXT3\"),\n\t/// \t]);\n\t/// ]]></code>\n\t/// </example>\n\tpublic void Menu(params TAMenuItem[] items) {\n\t\tif (items.NE_()) return;\n\t\t\n\t\tvar m = new popupMenu(null, Trigger.SourceFile, Trigger.SourceLine) {\n\t\t\tExtractIconPathFromCode = false,\n\t\t\tRawText = true,\n\t\t};\n\t\t\n\t\tfor (int i = 0; i < items.Length; i++) {\n\t\t\tvar v = items[i];\n\t\t\tif (v == null) {\n\t\t\t\tm.Separator();\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tstring lab = v.Label, text = v.Text ?? v.Html;\n\t\t\tif (lab == null) {\n\t\t\t\tlab = text.Limit(50).RxReplace(@\"\\R\", \" \");\n\t\t\t\tif (v.Text != null) lab = lab.Replace(\"[[|]]\", null);\n\t\t\t}\n#if true\n\t\t\tvar mi = m.Add(i + 1, lab, f_: Trigger.SourceFile, l_: v.l_ > 0 ? v.l_ : Trigger.SourceLine);\n\t\t\tif (text != lab) mi.Tooltip ??= text;\n\t\t\t//if (i == 0) m.FocusedItem = mi; //then no tooltip\n\t\t\tif (i < 9) mi.Hotkey = (i + 1).ToS();\n#else //CONSIDER: option \"numbers at left side\". User suggestion.\n\t\t\tbool numbersAtLeft = true;\n\t\t\tstring lab2 = numbersAtLeft && i < 9 ? $\"{(i + 1).ToS()}. {lab}\" : lab;\n\t\t\tvar mi = m.Add(i + 1, lab2, f_: Trigger.sourceFile, l_: v.l_ > 0 ? v.l_ : Trigger.sourceLine);\n\t\t\tif (text != lab) mi.Tooltip ??= text;\n\t\t\t//if (i == 0) m.FocusedItem = mi; //then no tooltip\n\t\t\tif (!numbersAtLeft && i < 9) mi.Hotkey = (i + 1).ToS();\n#endif\n\t\t}\n\t\t\n\t\tKeyToTextConverter kt = null;\n\t\tm.KeyboardHook = (m, g) => {\n\t\t\tif (g.Key is KKey.Enter or KKey.Tab) {\n\t\t\t\tm.FocusedItem ??= m.ItemsAndSeparators[0];\n\t\t\t\treturn PMKHook.Default;\n\t\t\t}\n\t\t\tif (g.Key is KKey.Escape or KKey.Down or KKey.Up or KKey.End or KKey.Home or KKey.PageDown or KKey.PageUp) {\n\t\t\t\treturn PMKHook.Default;\n\t\t\t}\n\t\t\tif (g.Mod != 0) return PMKHook.None;\n\t\t\tkt ??= new();\n\t\t\tif (kt.Convert(out var c, g.vkCode, g.scanCode, keys.getMod(), Window.ThreadId) && c.c is >= '1' and <= '9') {\n\t\t\t\tint i = c.c - '1';\n\t\t\t\tif (i >= m.ItemsAndSeparators.Count) return PMKHook.Default; //block\n\t\t\t\tm.FocusedItem = m.ItemsAndSeparators[i];\n\t\t\t\treturn PMKHook.ExecuteFocused;\n\t\t\t}\n\t\t\treturn PMKHook.Close;\n\t\t};\n\t\t\n\t\tint r = _ShowMenu(m) - 1;\n\t\tif (r >= 0) Replace(items[r].Text, items[r].Html);\n\t}\n\t\n\tint _ShowMenu(popupMenu m) {\n\t\tvar mo = Trigger.menuOptions;\n\t\treturn m.Show(mo?.pmFlags ?? PMFlags.ByCaret);\n\t}\n}\n\n/// <summary>\n/// See <see cref=\"AutotextTriggers.SimpleReplace\"/>.\n/// </summary>\npublic class TASimpleReplace {\n\tAutotextTriggers _host;\n\t\n\tinternal TASimpleReplace(AutotextTriggers host) {\n\t\t_host = host;\n\t}\n\t\n\t/// <summary>\n\t/// Adds an autotext trigger. Its action calls <see cref=\"AutotextTriggerArgs.Replace(string, string)\"/>.\n\t/// </summary>\n\t/// <inheritdoc cref=\"AutotextTriggers.this[string, TAFlags?, TAPostfix?, string, string, int]\"/>\n\tpublic string this[string text, TAFlags? flags = null, TAPostfix? postfixType = null, string postfixChars = null, [CallerFilePath] string f_ = null, [CallerLineNumber] int l_ = 0] {\n\t\tset {\n\t\t\t_host[text, flags, postfixType, postfixChars, f_, l_] = o => o.Replace(value);\n\t\t}\n\t}\n}\n\n/// <summary>\n/// Used with <see cref=\"AutotextTriggerArgs.Menu\"/>.\n/// </summary>\npublic class TAMenuItem {\n\t///\n\tpublic string Label { get; set; }\n\t///\n\tpublic string Text { get; set; }\n\t///\n\tpublic string Html { get; set; }\n\t\n\tinternal int l_;\n\t\n\t/// <summary>\n\t/// Sets menu item label and replacement text.\n\t/// </summary>\n\t/// <param name=\"label\">Menu item label. If <c>null</c>, uses <see cref=\"Text\"/>. Can contain tooltip like <c>\"Label\\0 Tooltip\"</c>.</param>\n\t/// <param name=\"text\">The replacement text. Can be <c>null</c>. Can contain caret placeholder <c>[[|]]</c>. See <see cref=\"AutotextTriggerArgs.Replace(string, string)\"/>.</param>\n\t/// <param name=\"html\">The replacement HTML. Can be <c>null</c>. See <see cref=\"AutotextTriggerArgs.Replace(string, string)\"/>.</param>\n\t/// <param name=\"l_\">[](xref:caller_info)</param>\n\tpublic TAMenuItem(string label, string text, string html = null, [CallerLineNumber] int l_ = 0) {\n\t\tLabel = label;\n\t\tText = text;\n\t\tHtml = html;\n\t\tthis.l_ = l_;\n\t}\n\t\n\t/// <summary>\n\t/// Creates <c>TAMenuItem</c> with only <see cref=\"Text\"/>.\n\t/// </summary>\n\tpublic static implicit operator TAMenuItem(string text) => new(null, text, null, 0);\n}\n"
  },
  {
    "path": "Au/Triggers/Types/t-hotkey.cs",
    "content": "//CONSIDER: AltGr generates RAlt+LCtrl. Should then ignore Ctrl?\n\nnamespace Au.Triggers;\n\n/// <summary>\n/// Flags of hotkey triggers.\n/// </summary>\n[Flags]\npublic enum TKFlags {\n\t/// <summary>\n\t/// Allow other apps to receive the key down message too.\n\t/// <br/>Without this flag, other apps usually receive only modifier keys. Also, OS always receives <c>Ctrl+Alt+Delete</c> and some other hotkeys.\n\t/// <br/>To receive and block key messages is used a low-level hook. Other hooks may receive blocked messages or not, depending on when they were set. \n\t/// </summary>\n\tShareEvent = 1,\n\t\n\t/// <summary>\n\t/// Run the action when the key and modifier keys are released.\n\t/// </summary>\n\tKeyModUp = 2,\n\t\n\t/// <summary>\n\t/// The trigger works only with left-side modifier keys.\n\t/// </summary>\n\tLeftMod = 4,\n\t\n\t/// <summary>\n\t/// The trigger works only with right-side modifier keys.\n\t/// </summary>\n\tRightMod = 8,\n\t\n\t/// <summary>\n\t/// Don't release modifier keys.\n\t/// <br/>Without this flag, for example if trigger is <c>[\"Ctrl+K\"]</c>, when the user presses <c>Ctrl</c> and <c>K</c> down, the trigger sends <c>Ctrl</c> key-up event, making the key logically released, although it is still physically pressed. Then modifier keys don't interfere with the action. However functions like <see cref=\"keys.getMod\"/> and <see cref=\"keys.waitForKey\"/> (and any such functions in any app) will not know that the key is physically pressed; there is no API to get physical key state.\n\t/// <br/>Other flags that prevent releasing modifier keys: <c>KeyUp</c>, <c>ShareEvent</c>. Then don't need this flag.\n\t/// <br/>Note: Unreleased modifier keys will interfere with mouse functions like <see cref=\"mouse.click\"/>. Will not interfere with keyboard and clipboard functions of this library, because they release modifier keys, unless <c>opt.key.NoModOff</c> is <c>true</c>. Will not interfere with functions that send text, unless <c>opt.key.NoModOff</c> is <c>true</c> and <c>opt.key.TextHow</c> is <c>OKeyText.KeysX</c>.\n\t/// </summary>\n\tNoModOff = 16,\n\t\n\t/// <summary>\n\t/// The key must be an \"extended key\". See <see cref=\"KKeyScan\"/> example code. Don't use this flag with <c>NumpadX</c> flags.\n\t/// </summary>\n\tExtendedYes = 32,\n\t\n\t/// <summary>\n\t/// The key must not be an \"extended key\". See <see cref=\"KKeyScan\"/> example code. Don't use this flag with <c>NumpadX</c> flags.\n\t/// </summary>\n\tExtendedNo = 64,\n\t\n\t/// <summary>\n\t/// The trigger works only with the key that is on the numeric keypad. This flag can be used with <c>Enter</c>, <c>Home</c>, <c>End</c>, <c>PgUp</c>, <c>PgDown</c>, arrows, <c>Ins</c> and <c>Del</c>.\n\t/// </summary>\n\tNumpad = 128,\n\t\n\t/// <summary>\n\t/// The trigger works only with the key that is not on the numeric keypad. This flag can be used with <c>Enter</c>, <c>Home</c>, <c>End</c>, <c>PgUp</c>, <c>PgDown</c>, arrows, <c>Ins</c> and <c>Del</c>.\n\t/// </summary>\n\tNumpadNot = 0x100,\n}\n\n/// <summary>\n/// Represents a hotkey trigger.\n/// </summary>\npublic class HotkeyTrigger : ActionTrigger {\n\tstring _paramsString;\n\tinternal readonly KMod modMasked, modMask;\n\t\n\t///\n\tpublic TKFlags Flags { get; }\n\t\n\tinternal HotkeyTrigger(ActionTriggers triggers, Action<HotkeyTriggerArgs> action, KMod mod, KMod modAny, TKFlags flags, string paramsString, (string, int) source)\n\t\t: base(triggers, action, true, source) {\n\t\tconst KMod csaw = KMod.Ctrl | KMod.Shift | KMod.Alt | KMod.Win;\n\t\tmodMask = ~modAny & csaw;\n\t\tmodMasked = mod & modMask;\n\t\tFlags = flags;\n\t\t_paramsString = flags == 0 ? paramsString : paramsString + \" (\" + flags.ToString() + \")\"; //print.it(_paramsString);\n\t}\n\t\n\tinternal override void Run_(TriggerArgs args) => RunT_(args as HotkeyTriggerArgs);\n\t\n\t/// <summary>\n\t/// Returns <c>\"Hotkey\"</c>.\n\t/// </summary>\n\tpublic override string TypeString => \"Hotkey\";\n\t\n\t/// <summary>\n\t/// Returns a string containing trigger parameters.\n\t/// </summary>\n\tpublic override string ParamsString => _paramsString;\n}\n\n/// <summary>\n/// Hotkey triggers.\n/// </summary>\n/// <example>See <see cref=\"ActionTriggers\"/>.</example>\npublic class HotkeyTriggers : ITriggers, IEnumerable<HotkeyTrigger> {\n\tActionTriggers _triggers;\n\tDictionary<int, ActionTrigger> _d = new Dictionary<int, ActionTrigger>();\n\t\n\tinternal HotkeyTriggers(ActionTriggers triggers) {\n\t\t_triggers = triggers;\n\t}\n\t\n\t/// <summary>\n\t/// Adds a hotkey trigger.\n\t/// </summary>\n\t/// <param name=\"hotkey\">\n\t/// A hotkey, like with <see cref=\"keys.send\"/>. See [key names and operators](xref:key_names).\n\t/// Can contain 0 to 4 modifier keys (<c>Ctrl</c>, <c>Shift</c>, <c>Alt</c>, <c>Win</c>) and 1 non-modifier key.\n\t/// Examples: <c>\"F11\"</c>, <c>\"Ctrl+K\"</c>, <c>\"Ctrl+Shift+Alt+Win+A\"</c>.\n\t/// To ignore modifiers: <c>\"?+K\"</c>. Then the trigger works with any combination of modifiers.\n\t/// To ignore a modifier: <c>\"Ctrl?+K\"</c>. Then the trigger works with or without the modifier. More examples: <c>\"Ctrl?+Shift?+K\"</c>, <c>\"Ctrl+Shift?+K\"</c>.\n\t/// </param>\n\t/// <param name=\"flags\"></param>\n\t/// <param name=\"f_\">[](xref:caller_info)</param>\n\t/// <param name=\"l_\">[](xref:caller_info)</param>\n\t/// <exception cref=\"ArgumentException\">Invalid hotkey string or flags.</exception>\n\t/// <exception cref=\"InvalidOperationException\">Cannot add triggers after <see cref=\"ActionTriggers.Run\"/> was called, until it returns.</exception>\n\t/// <example>See <see cref=\"ActionTriggers\"/>.</example>\n\tpublic Action<HotkeyTriggerArgs> this[[ParamString(PSFormat.HotkeyTrigger)] string hotkey, TKFlags flags = 0, [CallerFilePath] string f_ = null, [CallerLineNumber] int l_ = 0] {\n\t\tset {\n\t\t\t//This could be used instead of [CallerX] parameters, but can be too slow if many triggers. Definitely too slow for menus and toolbars.\n\t\t\t//perf.first();\n\t\t\t//var uu = new StackFrame(1, true); //first time 30 ms, then 30 mcs\n\t\t\t//perf.next();\n\t\t\t//var us = uu.GetFileName(); //0\n\t\t\t//perf.next();\n\t\t\t//var ui = uu.GetFileLineNumber(); //0\n\t\t\t//perf.next();\n\t\t\t//uu = new StackFrame(2, true); //slow too\n\t\t\t//perf.nw();\n\t\t\t////print.it(us, ui);\n\t\t\t////print.it(uu);\n\t\t\t\n\t\t\tif (!keys.more.parseTriggerString(hotkey, out var mod, out var modAny, out var key, false)) throw new ArgumentException(\"Invalid hotkey string.\");\n\t\t\t_Add(value, key, mod, modAny, flags, hotkey, (f_, l_));\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Adds a hotkey trigger.\n\t/// </summary>\n\t/// <param name=\"key\"></param>\n\t/// <param name=\"modKeys\">\n\t/// Modifier keys. See [key names and operators](xref:key_names).\n\t/// Examples: <c>\"Ctrl\"</c>, <c>\"Ctrl+Shift+Alt+Win\"</c>.\n\t/// To ignore modifiers: <c>\"?\"</c>. Then the trigger works with any combination of modifiers.\n\t/// To ignore a modifier: <c>\"Ctrl?\"</c>. Then the trigger works with or without the modifier. More examples: <c>\"Ctrl?+Shift?\"</c>, <c>\"Ctrl+Shift?\"</c>.\n\t/// </param>\n\t/// <param name=\"flags\"></param>\n\t/// <param name=\"f_\">[](xref:caller_info)</param>\n\t/// <param name=\"l_\">[](xref:caller_info)</param>\n\t/// <exception cref=\"ArgumentException\">Invalid <i>modKeys</i> string or <i>flags</i>.</exception>\n\t/// <exception cref=\"InvalidOperationException\">Cannot add triggers after <see cref=\"ActionTriggers.Run\"/> was called, until it returns.</exception>\n\tpublic Action<HotkeyTriggerArgs> this[KKey key, [ParamString(PSFormat.TriggerMod)] string modKeys, TKFlags flags = 0, [CallerFilePath] string f_ = null, [CallerLineNumber] int l_ = 0] {\n\t\tset {\n\t\t\tvar ps = key.ToString(); if (ps[0].IsAsciiDigit()) ps = \"VK\" + ps;\n\t\t\tif (!modKeys.NE()) ps = modKeys + \"+\" + ps;\n\t\t\t\n\t\t\tif (!keys.more.parseTriggerString(modKeys, out var mod, out var modAny, out _, true)) throw new ArgumentException(\"Invalid modKeys string.\");\n\t\t\t_Add(value, key, mod, modAny, flags, ps, (f_, l_));\n\t\t}\n\t}\n\t\n\tvoid _Add(Action<HotkeyTriggerArgs> action, KKey key, KMod mod, KMod modAny, TKFlags flags, string paramsString, (string, int) source) {\n\t\tif (mod == 0 && flags.HasAny((TKFlags.LeftMod | TKFlags.RightMod))) throw new ArgumentException(\"Invalid flags.\");\n\t\t_triggers.ThrowIfRunning_();\n\t\t//actually could safely add triggers while running.\n\t\t//\tCurrently would need just lock(_d) in several places. Also some triggers of this type must be added before starting, else we would not have the hook etc.\n\t\t//\tBut probably not so useful. Makes programming more difficult. If need, can Stop, add triggers, then Run again.\n\t\t\n\t\t//print.it($\"key={key}, mod={mod}, modAny={modAny}\");\n\t\tvar t = new HotkeyTrigger(_triggers, action, mod, modAny, flags, paramsString, source);\n\t\tt.DictAdd_(_d, (int)key);\n\t\t_lastAdded = t;\n\t}\n\t\n\t/// <summary>\n\t/// The last added trigger.\n\t/// </summary>\n\tpublic HotkeyTrigger Last => _lastAdded;\n\tHotkeyTrigger _lastAdded;\n\t\n\tbool ITriggers.HasTriggers => _lastAdded != null;\n\t\n\tvoid ITriggers.StartStop(bool start) {\n\t\t_UpClear();\n\t\t_eatUp = 0;\n\t}\n\t\n\tinternal bool HookProc(HookData.Keyboard k, TriggerHookContext thc) {\n\t\t//print.it(k.vkCode, !k.IsUp);\n\t\tDebug.Assert(!k.IsInjectedByAu); //server must ignore\n\t\t\n\t\tKKey key = k.vkCode;\n\t\tKMod mod = thc.Mod;\n\t\tbool up = k.IsUp;\n\t\tif (!up) _UpClear();\n\t\t\n\t\tif (thc.ModThis != 0) {\n\t\t\tif (_upTrigger != null && mod == 0 && _upKey == 0) _UpTriggered(thc);\n\t\t} else if (up) {\n\t\t\tif (key == _upKey) {\n\t\t\t\t_upKey = 0;\n\t\t\t\tif (_upTrigger != null && mod == 0) _UpTriggered(thc);\n\t\t\t}\n\t\t\tif (key == _eatUp) {\n\t\t\t\t_eatUp = 0;\n\t\t\t\treturn true;\n\t\t\t\t//To be safer, could return false if keys.isPressed(_eatUp), but then can interfere with the trigger action.\n\t\t\t}\n\t\t\t//CONSIDER: _upTimeout.\n\t\t} else {\n\t\t\t//if(key == _eatUp) _eatUp = 0;\n\t\t\t_eatUp = 0;\n\t\t\t\n\t\t\tif (_d.TryGetValue((int)key, out var v)) {\n\t\t\t\tHotkeyTriggerArgs args = null;\n\t\t\t\tfor (; v != null; v = v.next) {\n\t\t\t\t\tvar x = v as HotkeyTrigger;\n\t\t\t\t\tif ((mod & x.modMask) != x.modMasked) continue;\n\t\t\t\t\t\n\t\t\t\t\tif ((x.Flags & (TKFlags.Numpad | TKFlags.NumpadNot)) is TKFlags.Numpad or TKFlags.NumpadNot && key is KKey.Enter or KKey.Home or KKey.End or KKey.PageUp or KKey.PageDown or KKey.Left or KKey.Right or KKey.Up or KKey.Down or KKey.Insert or KKey.Delete) {\n\t\t\t\t\t\tif (k.IsExtended ^ key is KKey.Enter == x.Flags.Has(TKFlags.Numpad)) continue;\n\t\t\t\t\t}\n\t\t\t\t\tswitch (x.Flags & (TKFlags.ExtendedYes | TKFlags.ExtendedNo)) {\n\t\t\t\t\tcase TKFlags.ExtendedYes: if (!k.IsExtended) continue; break;\n\t\t\t\t\tcase TKFlags.ExtendedNo: if (k.IsExtended) continue; break;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tswitch (x.Flags & (TKFlags.LeftMod | TKFlags.RightMod)) {\n\t\t\t\t\tcase TKFlags.LeftMod: if (thc.ModL != mod) continue; break;\n\t\t\t\t\tcase TKFlags.RightMod: if (thc.ModR != mod) continue; break;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tif (v.DisabledThisOrAll) continue;\n\t\t\t\t\t\n\t\t\t\t\tif (args == null) thc.args = args = new HotkeyTriggerArgs(x, thc.Window, key, mod); //may need for scope callbacks too\n\t\t\t\t\telse args.Trigger = x;\n\t\t\t\t\t\n\t\t\t\t\tif (!x.MatchScopeWindowAndFunc_(thc)) continue;\n\t\t\t\t\t\n\t\t\t\t\tif (x.action != null) {\n\t\t\t\t\t\tif (0 != (x.Flags & TKFlags.KeyModUp)) {\n\t\t\t\t\t\t\t_upTrigger = x;\n\t\t\t\t\t\t\t_upArgs = args;\n\t\t\t\t\t\t\t_upKey = key;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthc.trigger = x;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\t//print.it(key, mod);\n\t\t\t\t\tif (0 != (x.Flags & TKFlags.ShareEvent)) return false;\n\t\t\t\t\t\n\t\t\t\t\tif (thc.trigger == null) { //KeyModUp or action==null\n\t\t\t\t\t\tif (mod is KMod.Alt or KMod.Win or (KMod.Alt | KMod.Win)) {\n\t\t\t\t\t\t\t//print.it(\"need Ctrl\");\n\t\t\t\t\t\t\tThreadPool.QueueUserWorkItem(o => keys.Internal_.SendKey(KKey.Ctrl, dontThrow: true)); //disable Alt/Win menu\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (mod != 0) {\n\t\t\t\t\t\tif (0 == (x.Flags & TKFlags.NoModOff)) thc.muteMod = TriggerActionThreads.c_modRelease;\n\t\t\t\t\t\telse if (mod is KMod.Alt or KMod.Win or (KMod.Alt | KMod.Win)) thc.muteMod = TriggerActionThreads.c_modCtrl;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\t_eatUp = key;\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\t\n\tHotkeyTrigger _upTrigger;\n\tHotkeyTriggerArgs _upArgs;\n\tKKey _upKey;\n\tKKey _eatUp;\n\t\n\tvoid _UpTriggered(TriggerHookContext thc) {\n\t\tthc.args = _upArgs;\n\t\tthc.trigger = _upTrigger;\n\t\t_UpClear();\n\t}\n\t\n\tvoid _UpClear() {\n\t\t_upTrigger = null;\n\t\t_upArgs = null;\n\t\t_upKey = 0;\n\t}\n\t\n\t/// <summary>\n\t/// Used by <c>foreach</c> to enumerate added triggers.\n\t/// </summary>\n\tpublic IEnumerator<HotkeyTrigger> GetEnumerator() {\n\t\tforeach (var kv in _d) {\n\t\t\tfor (var v = kv.Value; v != null; v = v.next) {\n\t\t\t\tvar x = v as HotkeyTrigger;\n\t\t\t\tyield return x;\n\t\t\t}\n\t\t}\n\t}\n\t\n\tIEnumerator IEnumerable.GetEnumerator() => GetEnumerator();\n}\n\n/// <summary>\n/// Arguments for actions of hotkey triggers.\n/// </summary>\npublic class HotkeyTriggerArgs : TriggerArgs {\n\t///\n\tpublic HotkeyTrigger Trigger { get; internal set; }\n\t\n\t///\n\t[EditorBrowsable(EditorBrowsableState.Never)]\n\tpublic override ActionTrigger TriggerBase => Trigger;\n\t\n\t/// <summary>\n\t/// The active window.\n\t/// </summary>\n\tpublic wnd Window { get; }\n\t\n\t/// <summary>\n\t/// The pressed key.\n\t/// </summary>\n\tpublic KKey Key { get; }\n\t\n\t/// <summary>\n\t/// The pressed modifier keys.\n\t/// </summary>\n\t/// <remarks>\n\t/// Can be useful when the trigger ignores modifiers. For example <c>\"?+F11\"</c> or <c>\"Shift?+A\"</c>.\n\t/// </remarks>\n\tpublic KMod Mod { get; }\n\t\n\t///\n\tpublic HotkeyTriggerArgs(HotkeyTrigger trigger, wnd w, KKey key, KMod mod) {\n\t\tTrigger = trigger;\n\t\tWindow = w; Key = key; Mod = mod;\n\t}\n\t\n\t///\n\tpublic override string ToString() => \"Trigger: \" + Trigger;\n}\n"
  },
  {
    "path": "Au/Triggers/Types/t-mouse.cs",
    "content": "namespace Au.Triggers;\n\n/// <summary>\n/// Flags of mouse triggers.\n/// </summary>\n[Flags]\npublic enum TMFlags : byte {\n\t/// <summary>\n\t/// Allow other apps to receive the mouse button or wheel message too.\n\t/// Used only with the click and wheel triggers.\n\t/// To receive and block mouse messages is used a low-level hook. Other hooks may receive blocked messages or not, depending on when they were set. \n\t/// </summary>\n\tShareEvent = 1,\n\t\n\t/// <summary>\n\t/// Run the action when the mouse button and modifier keys are released.\n\t/// </summary>\n\tButtonModUp = 2,\n\t\n\t/// <summary>\n\t/// The trigger works only with left-side modifier keys.\n\t/// </summary>\n\tLeftMod = 4,\n\t\n\t/// <summary>\n\t/// The trigger works only with right-side modifier keys.\n\t/// </summary>\n\tRightMod = 8,\n\t\n\t//rejected. We always mod-off and eat auto-repeated and up events with a temp hook. Because OS does not disable auto-repeating like for hotkeys.\n\t///// <summary>\n\t///// Don't release modifier keys.\n\t///// More info: <see cref=\"TKFlags.NoModOff\"/>.\n\t///// </summary>\n\t//NoModOff = 16,\n}\n\n/// <summary>\n/// Represents a mouse trigger.\n/// </summary>\npublic class MouseTrigger : ActionTrigger {\n\treadonly string _paramsString;\n\treadonly byte _data;\n\tinternal readonly KMod modMasked, modMask;\n\t\n\tinternal MouseTrigger(ActionTriggers triggers, Action<MouseTriggerArgs> action,\n\t\tTMKind kind, byte data, KMod mod, KMod modAny, TMFlags flags, screen screen,\n\t\tstring paramsString, (string, int) source) : base(triggers, action, true, source) {\n\t\tconst KMod csaw = KMod.Ctrl | KMod.Shift | KMod.Alt | KMod.Win;\n\t\tmodMask = ~modAny & csaw;\n\t\tmodMasked = mod & modMask;\n\t\t_data = data;\n\t\t_paramsString = paramsString;\n\t\tFlags = flags;\n\t\tKind = kind;\n\t\tScreen = screen;\n\t}\n\t\n\tinternal override void Run_(TriggerArgs args) => RunT_(args as MouseTriggerArgs);\n\t\n\t/// <summary>\n\t/// Returns <c>\"Mouse\"</c>.\n\t/// </summary>\n\tpublic override string TypeString => \"Mouse\";\n\t\n\t/// <summary>\n\t/// Returns a string containing trigger parameters.\n\t/// </summary>\n\tpublic override string ParamsString => _paramsString;\n\t\n\t///\n\tpublic TMKind Kind { get; }\n\t\n\t///\n\tpublic TMClick Button => Kind == TMKind.Click ? (TMClick)_data : default;\n\t\n\t///\n\tpublic TMWheel Wheel => Kind == TMKind.Wheel ? (TMWheel)_data : default;\n\t\n\t///\n\tpublic TMEdge Edge => Kind == TMKind.Edge ? (TMEdge)_data : default;\n\t\n\t///\n\tpublic TMMove Move => Kind == TMKind.Move ? (TMMove)_data : default;\n\t\n\t///\n\tpublic screen Screen { get; }\n\t\n\t///\n\tpublic TMFlags Flags { get; }\n}\n\n/// <summary>\n/// Mouse triggers.\n/// </summary>\n/// <example> See <see cref=\"ActionTriggers\"/>.</example>\npublic class MouseTriggers : ITriggers, IEnumerable<MouseTrigger> {\n\tActionTriggers _triggers;\n\tDictionary<int, ActionTrigger> _d = new Dictionary<int, ActionTrigger>();\n\t\n\tinternal MouseTriggers(ActionTriggers triggers) {\n\t\t_triggers = triggers;\n\t}\n\t\n\t/// <summary>\n\t/// Adds a mouse click trigger.\n\t/// </summary>\n\t/// <param name=\"button\"></param>\n\t/// <param name=\"modKeys\">\n\t/// Modifier keys. See [key names and operators](xref:key_names).\n\t/// Examples: <c>\"Ctrl\"</c>, <c>\"Ctrl+Shift+Alt+Win\"</c>.\n\t/// To ignore modifiers: <c>\"?\"</c>. Then the trigger works with any combination of modifiers.\n\t/// To ignore a modifier: <c>\"Ctrl?\"</c>. Then the trigger works with or without the modifier. More examples: <c>\"Ctrl?+Shift?\"</c>, <c>\"Ctrl+Shift?\"</c>.\n\t/// </param>\n\t/// <param name=\"flags\"></param>\n\t/// <param name=\"f_\">[](xref:caller_info)</param>\n\t/// <param name=\"l_\">[](xref:caller_info)</param>\n\t/// <exception cref=\"ArgumentException\">Invalid <i>modKeys</i> string or <i>flags</i>.</exception>\n\t/// <exception cref=\"InvalidOperationException\">Cannot add triggers after <see cref=\"ActionTriggers.Run\"/> was called, until it returns.</exception>\n\t/// <example>See <see cref=\"ActionTriggers\"/>.</example>\n\tpublic Action<MouseTriggerArgs> this[TMClick button, [ParamString(PSFormat.TriggerMod)] string modKeys = null, TMFlags flags = 0, [CallerFilePath] string f_ = null, [CallerLineNumber] int l_ = 0] {\n\t\tset {\n\t\t\t_Add(value, TMKind.Click, (byte)button, modKeys, flags, default, button.ToString(), (f_, l_));\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Adds a mouse wheel trigger.\n\t/// </summary>\n\t/// <inheritdoc cref=\"this[TMClick, string, TMFlags, string, int]\"/>\n\tpublic Action<MouseTriggerArgs> this[TMWheel direction, [ParamString(PSFormat.TriggerMod)] string modKeys = null, TMFlags flags = 0, [CallerFilePath] string f_ = null, [CallerLineNumber] int l_ = 0] {\n\t\tset {\n\t\t\t_Add(value, TMKind.Wheel, (byte)direction, modKeys, flags, default, direction.ToString(), (f_, l_));\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Adds a mouse screen edge trigger.\n\t/// </summary>\n\t/// <param name=\"screen\">\n\t/// The trigger will work in this screen (display monitor). Default: the primary screen.\n\t/// Should be lazy or default; else the function calls <see cref=\"print.warning\"/>.\n\t/// Examples: <c>screen.at.left(true)</c>, <c>screen.index(1, true)</c>.\n\t/// If <c>screen.ofMouse</c>, the trigger will work in any screen.\n\t/// </param>\n\t/// <param name=\"a1_\">[](xref:caller_info)</param>\n\t/// <inheritdoc cref=\"this[TMClick, string, TMFlags, string, int]\"/>\n\tpublic Action<MouseTriggerArgs> this[TMEdge edge, [ParamString(PSFormat.TriggerMod)] string modKeys = null, TMFlags flags = 0, screen screen = default, [CallerFilePath] string f_ = null, [CallerLineNumber] int l_ = 0, [CallerArgumentExpression(\"screen\")] string a1_ = null] {\n\t\tset {\n\t\t\t_Add(value, TMKind.Edge, (byte)edge, modKeys, flags, screen, edge.ToString(), (f_, l_), a1_);\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Adds a mouse move trigger.\n\t/// </summary>\n\t/// <inheritdoc cref=\"this[TMEdge, string, TMFlags, screen, string, int, string]\"/>\n\tpublic Action<MouseTriggerArgs> this[TMMove move, [ParamString(PSFormat.TriggerMod)] string modKeys = null, TMFlags flags = 0, screen screen = default, [CallerFilePath] string f_ = null, [CallerLineNumber] int l_ = 0, [CallerArgumentExpression(\"screen\")] string a1_ = null] {\n\t\tset {\n\t\t\t_Add(value, TMKind.Move, (byte)move, modKeys, flags, screen, move.ToString(), (f_, l_), a1_);\n\t\t}\n\t}\n\t\n\tMouseTrigger _Add(Action<MouseTriggerArgs> f, TMKind kind, byte data, string modKeys, TMFlags flags, screen screen, string sData, (string, int) source, string a1_ = null) {\n\t\t_triggers.ThrowIfRunning_();\n\t\t\n\t\tif (screen.Handle != default) print.warning(\"screen should be lazy or default\");\n\t\t//CONSIDER: store device id or xy, and repair when handle becomes invalid.\n\t\t\n\t\tbool noMod = modKeys.NE();\n\t\t\n\t\tstring ps;\n\t\tusing (new StringBuilder_(out var b)) {\n\t\t\tb.Append(kind.ToString()).Append(' ').Append(sData);\n\t\t\tif (!noMod) b.Append(\" + \").Append(modKeys == \"?\" ? \"any\" : modKeys);\n\t\t\tif (flags != 0) b.Append(\" (\").Append(flags.ToString()).Append(')');\n\t\t\tif (kind is TMKind.Edge or TMKind.Move) {\n\t\t\t\tif (screen.IsEmpty) b.Append(\", primary screen\");\n\t\t\t\telse if (screen.IsOfMouse_) b.Append(\", any screen\");\n\t\t\t\telse b.Append(\", \").Append(a1_);\n\t\t\t}\n\t\t\tps = b.ToString(); //print.it(ps);\n\t\t}\n\t\t\n\t\tKMod mod = 0, modAny = 0;\n\t\tif (noMod) {\n\t\t\tif (flags.HasAny(kind == TMKind.Click ? TMFlags.LeftMod | TMFlags.RightMod : TMFlags.LeftMod | TMFlags.RightMod | TMFlags.ButtonModUp)) throw new ArgumentException(\"Invalid flags.\");\n\t\t} else {\n\t\t\tif (!keys.more.parseTriggerString(modKeys, out mod, out modAny, out _, true)) throw new ArgumentException(\"Invalid modKeys string.\");\n\t\t}\n\t\tvar t = new MouseTrigger(_triggers, f, kind, data, mod, modAny, flags, screen, ps, source);\n\t\tt.DictAdd_(_d, _DictKey(kind, data));\n\t\t_lastAdded = t;\n\t\tUsedHookEvents_ |= HooksThread.UsedEvents.Mouse; //just sets the hook\n\t\tUsedHookEvents_ |= kind switch {\n\t\t\tTMKind.Click => HooksThread.UsedEvents.MouseClick,\n\t\t\tTMKind.Wheel => HooksThread.UsedEvents.MouseWheel,\n\t\t\t_ => HooksThread.UsedEvents.MouseEdgeMove,\n\t\t};\n\t\treturn t;\n\t}\n\t\n\tstatic int _DictKey(TMKind kind, byte data) => (data << 8) | (byte)kind;\n\t\n\t/// <summary>\n\t/// The last added trigger.\n\t/// </summary>\n\tpublic MouseTrigger Last => _lastAdded;\n\tMouseTrigger _lastAdded;\n\t\n\tbool ITriggers.HasTriggers => _lastAdded != null;\n\t\n\tinternal HooksThread.UsedEvents UsedHookEvents_ { get; private set; }\n\t\n\tvoid ITriggers.StartStop(bool start) {\n\t\tif (start) {\n\t\t} else {\n\t\t\t_ResetUpAndUnhookTempKeybHook();\n\t\t\t_eatUp = 0;\n\t\t}\n\t}\n\t\n\tinternal bool HookProcClickWheel(HookData.Mouse k, TriggerHookContext thc) {\n\t\t//print.it(k.Event, k.pt);\n\t\tDebug.Assert(!k.IsInjectedByAu); //server must ignore\n\t\t\n\t\tTMKind kind;\n\t\tbyte data;\n\t\t\n\t\tif (k.IsButton) {\n\t\t\tif (k.IsButtonUp) {\n\t\t\t\tif (k.Event == _upEvent) {\n\t\t\t\t\t_upEvent = 0;\n\t\t\t\t\tif (_upMod == 0 && _upTrigger != null) {\n\t\t\t\t\t\tthc.args = _upArgs;\n\t\t\t\t\t\tthc.trigger = _upTrigger;\n\t\t\t\t\t\t_ResetUp();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (k.Event == _eatUp) {\n\t\t\t\t\t_eatUp = 0;\n\t\t\t\t\treturn true;\n\t\t\t\t\t//To be safer, could return false if mouse.isPressed(k.Button), but then can interfere with the trigger action.\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t\t//CONSIDER: _upTimeout.\n\t\t\t}\n\t\t\tif (k.Event == _eatUp) _eatUp = 0;\n\t\t\t\n\t\t\tkind = TMKind.Click;\n\t\t\tvar b = k.Event switch {\n\t\t\t\tHookData.MouseEvent.LeftButton => TMClick.Left,\n\t\t\t\tHookData.MouseEvent.RightButton => TMClick.Right,\n\t\t\t\tHookData.MouseEvent.MiddleButton => TMClick.Middle,\n\t\t\t\tHookData.MouseEvent.X1Button => TMClick.X1,\n\t\t\t\t_ => TMClick.X2,\n\t\t\t};\n\t\t\tdata = (byte)b;\n\t\t} else { //wheel\n\t\t\tkind = TMKind.Wheel;\n\t\t\tvar b = k.Event switch {\n\t\t\t\tHookData.MouseEvent.WheelForward => TMWheel.Forward,\n\t\t\t\tHookData.MouseEvent.WheelBackward => TMWheel.Backward,\n\t\t\t\tHookData.MouseEvent.WheelLeft => TMWheel.Left,\n\t\t\t\t_ => TMWheel.Right,\n\t\t\t};\n\t\t\tdata = (byte)b;\n\t\t}\n\t\t\n\t\treturn _HookProc2(thc, false, kind, k.Event, k.pt, data, 0);\n\t}\n\t\n\tinternal void HookProcEdgeMove(EdgeMoveDetector_.Result d, TriggerHookContext thc) //d not in\n\t{\n\t\tTMKind kind;\n\t\tbyte data, dataAnyPart;\n\t\t\n\t\tif (d.edgeEvent != 0) {\n\t\t\tkind = TMKind.Edge;\n\t\t\tdata = (byte)d.edgeEvent; dataAnyPart = (byte)d.edgeEventAnyPart;\n\t\t} else {\n\t\t\tkind = TMKind.Move;\n\t\t\tdata = (byte)d.moveEvent; dataAnyPart = (byte)d.moveEventAnyPart;\n\t\t}\n\t\t\n\t\t_HookProc2(thc, true, kind, HookData.MouseEvent.Move, d.pt, data, dataAnyPart);\n\t}\n\t\n\tbool _HookProc2(TriggerHookContext thc, bool isEdgeMove, TMKind kind, HookData.MouseEvent mEvent, POINT pt, byte data, byte dataAnyPart) {\n\t\tvar mod = TrigUtil.GetModLR(out var modL, out var modR) | _eatMod;\n\t\tMouseTriggerArgs args = null;\n\t\tg1:\n\t\tif (_d.TryGetValue(_DictKey(kind, data), out var v)) {\n\t\t\tif (!isEdgeMove) thc.UseWndFromPoint(pt);\n\t\t\tfor (; v != null; v = v.next) {\n\t\t\t\tvar x = v as MouseTrigger;\n\t\t\t\tif ((mod & x.modMask) != x.modMasked) continue;\n\t\t\t\t\n\t\t\t\tswitch (x.Flags & (TMFlags.LeftMod | TMFlags.RightMod)) {\n\t\t\t\tcase TMFlags.LeftMod: if (modL != mod) continue; break;\n\t\t\t\tcase TMFlags.RightMod: if (modR != mod) continue; break;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (isEdgeMove && !x.Screen.IsOfMouse_) {\n\t\t\t\t\tvar sh = x.Screen.Now;\n\t\t\t\t\tif (!sh.Rect.Contains(pt)) continue;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (v.DisabledThisOrAll) continue;\n\t\t\t\t\n\t\t\t\tif (args == null) thc.args = args = new MouseTriggerArgs(x, thc.Window, mod); //may need for scope callbacks too\n\t\t\t\telse args.Trigger = x;\n\t\t\t\t\n\t\t\t\tif (!x.MatchScopeWindowAndFunc_(thc)) continue;\n\t\t\t\t\n\t\t\t\tif (x.action == null) {\n\t\t\t\t\t_ResetUp();\n\t\t\t\t} else if (0 != (x.Flags & TMFlags.ButtonModUp) && (mod != 0 || kind == TMKind.Click)) {\n\t\t\t\t\t_upTrigger = x;\n\t\t\t\t\t_upArgs = args;\n\t\t\t\t\t_upEvent = kind == TMKind.Click ? mEvent : 0;\n\t\t\t\t\t_upMod = mod;\n\t\t\t\t} else {\n\t\t\t\t\tthc.trigger = x;\n\t\t\t\t\t_ResetUp();\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t_eatMod = mod;\n\t\t\t\tif (mod != 0) {\n\t\t\t\t\t_SetTempKeybHook();\n\t\t\t\t\tif (thc.trigger != null) thc.muteMod = TriggerActionThreads.c_modRelease;\n\t\t\t\t\telse ThreadPool.QueueUserWorkItem(_ => keys.Internal_.ReleaseModAndDisableModMenu(dontThrow: true));\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t//print.it(mEvent, pt, mod);\n\t\t\t\tif (isEdgeMove || 0 != (x.Flags & TMFlags.ShareEvent)) return false;\n\t\t\t\tif (kind == TMKind.Click) _eatUp = mEvent;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\tif (dataAnyPart != 0) {\n\t\t\tdata = dataAnyPart;\n\t\t\tdataAnyPart = 0;\n\t\t\tgoto g1;\n\t\t}\n\t\treturn false;\n\t}\n\t\n\t//these are used to activate trigger when button and modifiers released\n\tMouseTrigger _upTrigger;\n\tMouseTriggerArgs _upArgs;\n\tHookData.MouseEvent _upEvent;\n\tKMod _upMod;\n\t//this is used to eat modifier keys, regardless when the trigger is activated\n\tKMod _eatMod;\n\t//these are used to eat modifier keys and to activate trigger when modifiers released\n\tWindowsHook _keyHook;\n\tlong _keyHookTimeout;\n\t//this is used to eat button-up event, regardless when the trigger is activated\n\tHookData.MouseEvent _eatUp;\n\t\n\tvoid _ResetUp() {\n\t\t_upTrigger = null;\n\t\t_upArgs = null;\n\t\t_upEvent = 0;\n\t\t_upMod = 0;\n\t}\n\t\n\tvoid _UnhookTempKeybHook() {\n\t\tif (_keyHook != null) {\n\t\t\t//print.it(\". unhook\");\n\t\t\t_keyHook.Unhook();\n\t\t\t_keyHookTimeout = _keyHook.DontBlockModInOtherHooks_(0);\n\t\t}\n\t}\n\t\n\tvoid _ResetUpAndUnhookTempKeybHook() {\n\t\t_ResetUp();\n\t\t_eatMod = 0;\n\t\t_UnhookTempKeybHook();\n\t}\n\t\n\tvoid _SetTempKeybHook() {\n\t\t//print.it(\". hook\");\n\t\tif (_keyHook == null) {\n\t\t\t_keyHook = WindowsHook.Keyboard(k => {\n\t\t\t\tif (Environment.TickCount64 >= _keyHookTimeout) {\n\t\t\t\t\t_ResetUpAndUnhookTempKeybHook();\n\t\t\t\t\tDebug_.Print(\"hook timeout\");\n\t\t\t\t} else {\n\t\t\t\t\tvar mod = k.Mod;\n\t\t\t\t\tif (0 != (mod & _upMod) && k.IsUp) {\n\t\t\t\t\t\t_upMod &= ~mod;\n\t\t\t\t\t\tif (_upMod == 0 && _upEvent == 0 && _upTrigger != null) {\n\t\t\t\t\t\t\t_triggers.RunAction_(_upTrigger, _upArgs);\n\t\t\t\t\t\t\t_ResetUp();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (0 != (mod & _eatMod)) {\n\t\t\t\t\t\t//print.it(k);\n\t\t\t\t\t\tif (k.IsUp) _eatMod &= ~mod;\n\t\t\t\t\t\telse k.BlockEvent();\n\t\t\t\t\t}\n\t\t\t\t\tif (0 == (_upMod | _eatMod)) _UnhookTempKeybHook();\n\t\t\t\t}\n\t\t\t}, setNow: false);\n\t\t}\n\t\tif (!_keyHook.IsSet) _keyHook.Hook();\n\t\t_keyHookTimeout = _keyHook.DontBlockModInOtherHooks_(5000);\n\t}\n\t\n\tinternal static void JitCompile() {\n\t\tJit_.Compile(typeof(MouseTriggers), nameof(HookProcClickWheel), nameof(HookProcEdgeMove), nameof(_HookProc2));\n\t\twnd.fromXY(default, WXYFlags.NeedWindow);\n\t}\n\t\n\t/// <summary>\n\t/// Detects trigger events of types <c>Edge</c> and <c>Move</c>.\n\t/// </summary>\n\t/// <remarks>\n\t/// Used in the hook server, to avoid sending all mouse move events to clients, which would use 2 or more times more CPU, eg 0.9% instead of 0.45%. Tested: raw input uses slightly less CPU.\n\t/// </remarks>\n\tinternal class EdgeMoveDetector_ {\n\t\tint _x, _y; //mouse position. Relative to the primary screen.\n\t\tint _xmin, _ymin, _xmax, _ymax; //min and max possible mouse position in current screen. Relative to the primary screen.\n\t\t_State _prev;\n\t\tint _sens;\n\t\tpublic Result result;\n\t\t\n\t\tinternal struct Result {\n\t\t\tpublic POINT pt;\n\t\t\tpublic TMEdge edgeEvent, edgeEventAnyPart;\n\t\t\tpublic TMMove moveEvent, moveEventAnyPart;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// State data set by previous events.\n\t\t/// </summary>\n\t\tstruct _State {\n\t\t\tpublic int xx, yy; //previous coords\n\t\t\tpublic long time; //previous time\n\t\t\t\n\t\t\t//these used for Move events\n\t\t\tpublic int mx1, my1, mx2, my2;\n\t\t\tpublic TMMove mDirection;\n\t\t\tpublic long mTimeout;\n\t\t\t\n\t\t\t//these used for Edge events\n\t\t\tpublic long eTimeout;\n\t\t\t\n#if DEBUG\n\t\t\t//public int debug;\n#endif\n\t\t}\n\t\t\n\t\tpublic bool Detect(POINT pt) {\n\t\t\t//get normal x y. In pt can be outside screen when cursor moved fast and was stopped by a screen edge. Tested: never for click/wheel events.\n\t\t\t//print.it(pt, mouse.xy);\n\t\t\t//var scrn = screen.of(pt); //problem with empty corners between 2 unaligned screens: when mouse tries to quickly diagonally cut such a corner, may activate a wrong trigger\n\t\t\tvar scrn = screen.of(mouse.xy); //smaller problem: mouse.xy gets previous coordinates\n\t\t\tvar r = scrn.Rect;\n\t\t\t_xmin = r.left; _ymin = r.top; _xmax = r.right - 1; _ymax = r.bottom - 1;\n\t\t\t_x = Math.Clamp(pt.x, _xmin, _xmax);\n\t\t\t_y = Math.Clamp(pt.y, _ymin, _ymax);\n\t\t\t//print.it(pt, _x, _y, r);\n\t\t\t_sens = scrn.Dpi / 4;\n\t\t\t\n\t\t\tresult = default;\n\t\t\tresult.pt = (_x, _y);\n\t\t\t\n\t\t\t_Detect();\n\t\t\t_prev.xx = _x; _prev.yy = _y;\n\t\t\t\n#if DEBUG\n\t\t\t//print.it(++_prev.debug, edgeEvent, moveEvent, (_x, _y));\n#endif\n\t\t\treturn result.edgeEvent != 0 || result.moveEvent != 0;\n\t\t}\n\t\t\n\t\tvoid _Detect() {\n\t\t\tif (mouse.isPressed(MButtons.Left | MButtons.Right | MButtons.Middle)) {\n\t\t\t\t_prev.mDirection = 0;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\tint x = _x, y = _y;\n\t\t\tif (x == _prev.xx && y == _prev.yy) { /*print.it(\"same x y\");*/ return; }\n\t\t\t\n\t\t\tlong time = perf.ms;\n\t\t\tint dt = (int)(time - _prev.time);\n\t\t\t_prev.time = time;\n\t\t\tif (dt <= 0) return; //never noticed\n\t\t\t\n\t\t\t//print.it((x, y), mouse.xy, time%10000);\n\t\t\t\n\t\t\tif (y == _ymin || y == _ymax || x == _xmin || x == _xmax) {\n\t\t\t\t_prev.mDirection = 0;\n\t\t\t\tif (time < _prev.eTimeout) return; //prevent double trigger when OS sometimes gives strange coords if some hook blocks the event\n\t\t\t\tif (y == _ymin) { //top\n\t\t\t\t\tif (_prev.yy <= _ymin) return;\n\t\t\t\t\tif (screen.isInAnyScreen((x, y - 1))) return;\n\t\t\t\t\tresult.edgeEvent = (TMEdge)((int)(result.edgeEventAnyPart = TMEdge.Top) + _PartX(x));\n\t\t\t\t} else if (y == _ymax) { //bottom\n\t\t\t\t\tif (_prev.yy >= _ymax) return;\n\t\t\t\t\tif (screen.isInAnyScreen((x, y + 1))) return;\n\t\t\t\t\tresult.edgeEvent = (TMEdge)((int)(result.edgeEventAnyPart = TMEdge.Bottom) + _PartX(x));\n\t\t\t\t} else if (x == _xmin) { //left\n\t\t\t\t\tif (_prev.xx <= _xmin) return;\n\t\t\t\t\tif (screen.isInAnyScreen((x - 1, y))) return;\n\t\t\t\t\tresult.edgeEvent = (TMEdge)((int)(result.edgeEventAnyPart = TMEdge.Left) + _PartY(y));\n\t\t\t\t} else /*if(x == _xmax)*/ { //right\n\t\t\t\t\tif (_prev.xx >= _xmax) return;\n\t\t\t\t\tif (screen.isInAnyScreen((x + 1, y))) return;\n\t\t\t\t\tresult.edgeEvent = (TMEdge)((int)(result.edgeEventAnyPart = TMEdge.Right) + _PartY(y));\n\t\t\t\t}\n\t\t\t\t_prev.eTimeout = time + 100;\n\t\t\t} else {\n\t\t\t\tif (_prev.mDirection == 0) {\n\t\t\t\t\tif (time < _prev.mTimeout) return;\n\t\t\t\t\t\n\t\t\t\t\tint dx = (x - _prev.xx) * 16 / dt, dy = (y - _prev.yy) * 21 / dt;\n\t\t\t\t\tTMMove e = 0;\n\t\t\t\t\tif (dx > 0) {\n\t\t\t\t\t\tif (dy > 0) {\n\t\t\t\t\t\t\tif (dx > dy) {\n\t\t\t\t\t\t\t\tif (dx > _sens) e = TMMove.RightLeft;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tif (dy > _sens) e = TMMove.DownUp;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tif (dx > -dy) {\n\t\t\t\t\t\t\t\tif (dx > _sens) e = TMMove.RightLeft;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tif (-dy > _sens) e = TMMove.UpDown;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (dy > 0) {\n\t\t\t\t\t\t\tif (-dx > dy) {\n\t\t\t\t\t\t\t\tif (-dx > _sens) e = TMMove.LeftRight;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tif (dy > _sens) e = TMMove.DownUp;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tif (-dx > -dy) {\n\t\t\t\t\t\t\t\tif (-dx > _sens) e = TMMove.LeftRight;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tif (-dy > _sens) e = TMMove.UpDown;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (e != 0) {\n\t\t\t\t\t\t//print.it(e);\n\t\t\t\t\t\t_prev.mDirection = e;\n\t\t\t\t\t\t_prev.mTimeout = time + 250;\n\t\t\t\t\t\t_prev.mx1 = _prev.xx; _prev.my1 = _prev.yy; _prev.mx2 = x; _prev.my2 = y;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif (time > _prev.mTimeout) { _prev.mDirection = 0; return; }\n\t\t\t\t\tint part = 0;\n\t\t\t\t\tvar e = _prev.mDirection;\n\t\t\t\t\tswitch (e) {\n\t\t\t\t\tcase TMMove.RightLeft:\n\t\t\t\t\t\tif (x < (_prev.mx1 + _prev.mx2) >> 1) {\n\t\t\t\t\t\t\t_prev.mDirection = 0;\n\t\t\t\t\t\t\tint dy = (_prev.my1 + _prev.my2) >> 1, dx = (_prev.mx2 - _prev.mx1) >> 2;\n\t\t\t\t\t\t\tif (Math.Abs(y - dy) > dx || Math.Abs(_prev.my1 - _prev.my2) > dx << 1) break;\n\t\t\t\t\t\t\tpart = _PartY(y);\n\t\t\t\t\t\t} else if (x > _prev.mx2) { _prev.mx2 = x; _prev.my2 = y; }\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase TMMove.LeftRight:\n\t\t\t\t\t\tif (x > (_prev.mx1 + _prev.mx2) >> 1) {\n\t\t\t\t\t\t\t_prev.mDirection = 0;\n\t\t\t\t\t\t\tint dy = (_prev.my1 + _prev.my2) >> 1, dx = (_prev.mx1 - _prev.mx2) >> 2;\n\t\t\t\t\t\t\tif (Math.Abs(y - dy) > dx || Math.Abs(_prev.my1 - _prev.my2) > dx << 1) break;\n\t\t\t\t\t\t\tpart = _PartY(y);\n\t\t\t\t\t\t} else if (x < _prev.mx2) { _prev.mx2 = x; _prev.my2 = y; }\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase TMMove.DownUp:\n\t\t\t\t\t\tif (y < (_prev.my1 + _prev.my2) >> 1) {\n\t\t\t\t\t\t\t_prev.mDirection = 0;\n\t\t\t\t\t\t\tint dx = (_prev.mx1 + _prev.mx2) >> 1, dy = (_prev.my2 - _prev.my1) >> 2;\n\t\t\t\t\t\t\tif (Math.Abs(x - dx) > dy || Math.Abs(_prev.mx1 - _prev.mx2) > dy << 1) break;\n\t\t\t\t\t\t\tpart = _PartX(x);\n\t\t\t\t\t\t} else if (y > _prev.my2) { _prev.mx2 = x; _prev.my2 = y; }\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase TMMove.UpDown:\n\t\t\t\t\t\tif (y > (_prev.my1 + _prev.my2) >> 1) {\n\t\t\t\t\t\t\t_prev.mDirection = 0;\n\t\t\t\t\t\t\tint dx = (_prev.mx1 + _prev.mx2) >> 1, dy = (_prev.my1 - _prev.my2) >> 2;\n\t\t\t\t\t\t\tif (Math.Abs(x - dx) > dy && Math.Abs(_prev.mx1 - _prev.mx2) > dy << 1) break;\n\t\t\t\t\t\t\tpart = _PartX(x);\n\t\t\t\t\t\t} else if (y < _prev.my2) { _prev.mx2 = x; _prev.my2 = y; }\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tif (part != 0) {\n\t\t\t\t\t\tresult.moveEventAnyPart = e;\n\t\t\t\t\t\tresult.moveEvent = (TMMove)((int)e + part);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tint _PartX(int x) {\n\t\t\tint q = (_xmax - _xmin) / 4;\n\t\t\tif (x < _xmin + q) return 2;\n\t\t\tif (x >= _xmax - q) return 3;\n\t\t\treturn 1;\n\t\t}\n\t\t\n\t\tint _PartY(int y) {\n\t\t\tint q = (_ymax - _ymin) / 4;\n\t\t\tif (y < _ymin + q) return 2;\n\t\t\tif (y >= _ymax - q) return 3;\n\t\t\treturn 1;\n\t\t}\n\t\t\n\t\t///// <summary>\n\t\t///// Sensitivity of <see cref=\"TMMove\"/> triggers.\n\t\t///// Default: 5. Can be 0 (less sensitive) to 10 (more sensitive).\n\t\t///// </summary>\n\t\t//public int MoveSensitivity {\n\t\t//\tget => _sensPublic;\n\t\t//\tset {\n\t\t//\t\tif((uint)value > 10) throw new ArgumentOutOfRangeException(null, \"0-10\");\n\t\t//\t\t_sensPublic = value;\n\t\t//\t\t_sens = (int)(Math.Pow(1.414, 14 - value) * 1.3);\n\t\t//\t\tprint.it(_sens); //29 when _sensPublic 5 (default)\n\t\t//\t}\n\t\t//}\n\t\t//int _sensPublic;\n\t}\n\t\n\t/// <summary>\n\t/// Used by <c>foreach</c> to enumerate added triggers.\n\t/// </summary>\n\tpublic IEnumerator<MouseTrigger> GetEnumerator() {\n\t\tforeach (var kv in _d) {\n\t\t\tfor (var v = kv.Value; v != null; v = v.next) {\n\t\t\t\tvar x = v as MouseTrigger;\n\t\t\t\tyield return x;\n\t\t\t}\n\t\t}\n\t}\n\t\n\tIEnumerator IEnumerable.GetEnumerator() => GetEnumerator();\n}\n\n/// <summary>\n/// Arguments for actions of mouse triggers.\n/// </summary>\npublic class MouseTriggerArgs : TriggerArgs {\n\t///\n\tpublic MouseTrigger Trigger { get; internal set; }\n\t\n\t///\n\t[EditorBrowsable(EditorBrowsableState.Never)]\n\tpublic override ActionTrigger TriggerBase => Trigger;\n\t\n\t/// <summary>\n\t/// The active window (<c>Edge</c> and <c>Move</c> triggers) or the mouse window (<c>Click</c> and <c>Wheel</c> triggers).\n\t/// </summary>\n\tpublic wnd Window { get; }\n\t\n\t/// <summary>\n\t/// The pressed modifier keys.\n\t/// </summary>\n\t/// <remarks>\n\t/// Can be useful when the trigger ignores modifiers. For example <i>modKeys</i> is <c>\"?\"</c> or <c>\"Shift?\"</c>.\n\t/// </remarks>\n\tpublic KMod Mod { get; }\n\t\n\t///\n\tpublic MouseTriggerArgs(MouseTrigger trigger, wnd w, KMod mod) {\n\t\tTrigger = trigger;\n\t\tWindow = w; Mod = mod;\n\t}\n\t\n\t///\n\tpublic override string ToString() => \"Trigger: \" + Trigger;\n}\n\n#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member\n\n/// <summary>\n/// What kind of mouse triggers.\n/// </summary>\npublic enum TMKind : byte { Click, Wheel, Edge, Move }\n\n/// <summary>\n/// Button for mouse click triggers.\n/// </summary>\npublic enum TMClick : byte { Left = 1, Right, Middle, X1, X2 }\n\n/// <summary>\n/// Mouse wheel direction for mouse wheel triggers.\n/// </summary>\npublic enum TMWheel : byte { Forward = 1, Backward, Left, Right }\n\n/// <summary>\n/// Screen edge for mouse edge triggers.\n/// </summary>\n/// <remarks>\n/// To activate a screen edge trigger, the user touches a screen edge with the mouse pointer.\n/// Each screen edge is divided into 3 parts: 1 - center 50%; 2 - left or top 25%; 3 - right or bottom 25%. Constants like <c>TopInCenter50</c> specify an edge and part; the trigger works only in that part of that edge. Constants like <c>Top</c> specify just an edge; the trigger works in all parts of that edge.\n/// </remarks>\npublic enum TMEdge : byte {\n\tTop = 1, TopInCenter50, TopInLeft25, TopInRight25,\n\tBottom, BottomInCenter50, BottomInLeft25, BottomInRight25,\n\tLeft, LeftInCenter50, LeftInTop25, LeftInBottom25,\n\tRight, RightInCenter50, RightInTop25, RightInBottom25,\n}\n\n/// <summary>\n/// Mouse movement directions for mouse move triggers.\n/// </summary>\n/// <remarks>\n/// To activate a mouse move trigger, the user quickly moves the mouse pointer to the specified direction and back.\n/// The screen is divided into 3 parts: 1 - center 50%; 2 - left or top 25%; 3 - right or bottom 25%. Constants like <c>UpDownInCenter50</c> specify a direction and screen part; the trigger works only in that screen part. Constants like <c>UpDown</c> specify just a direction; the trigger works in whole screen.\n/// </remarks>\npublic enum TMMove : byte {\n\tRightLeft = 1, RightLeftInCenter50, RightLeftInTop25, RightLeftInBottom25,\n\tLeftRight, LeftRightInCenter50, LeftRightInTop25, LeftRightInBottom25,\n\tUpDown, UpDownInCenter50, UpDownInLeft25, UpDownInRight25,\n\tDownUp, DownUpInCenter50, DownUpInLeft25, DownUpInRight25,\n}\n\n//FUTURE: remove.\n///\n[Obsolete(\"use code like screen.at.left(true) or screen.index(1, true) or screen.ofMouse\"), EditorBrowsable(EditorBrowsableState.Never)]\npublic static class TMScreen {\n\tpublic static screen Primary => default;\n\tpublic static screen Any => screen.ofMouse;\n\tpublic static screen OfActiveWindow => screen.ofActiveWindow;\n\tpublic static screen NonPrimary1 => screen.index(1, true);\n\tpublic static screen NonPrimary2 => screen.index(2, true);\n\tpublic static screen NonPrimary3 => screen.index(3, true);\n\tpublic static screen NonPrimary4 => screen.index(4, true);\n\tpublic static screen NonPrimary5 => screen.index(5, true);\n}\n#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member\n"
  },
  {
    "path": "Au/Triggers/Types/t-window.cs",
    "content": "namespace Au.Triggers;\n\n/// <summary>\n/// Flags of window triggers.\n/// </summary>\n[Flags]\npublic enum TWFlags : byte {\n\t/// <summary>\n\t/// Run the action when <see cref=\"ActionTriggers.Run\"/> called, if the window then is active (for <c>ActiveOnce</c> etc triggers) or visible (for <c>VisibleOnce</c> etc triggers).\n\t/// </summary>\n\tRunAtStartup = 1,\n\t\n\t/// <summary>\n\t/// When using the <i>later</i> parameter, call the currently active <c>Triggers.FuncOf</c> (see <see cref=\"ActionTriggers.FuncOf\"/>) functions on \"later\" events too.\n\t/// If the function returns <c>false</c>, the action will not run.\n\t/// The function runs synchronously in the same thread that called <see cref=\"ActionTriggers.Run\"/>. The action runs asynchronously in another thread, which is slower to start.\n\t/// As always, <c>Triggers.FuncOf</c> functions must not contain slow code; should take less than 10 ms.\n\t/// </summary>\n\tLaterCallFunc = 2,\n}\n\n/// <summary>\n/// Events for window triggers.\n/// </summary>\n/// <remarks>\n/// Cloaked windows are considered invisible. See <see cref=\"wnd.IsCloaked\"/>.\n///\n/// The \"active\" events don't occur if/while the activated window is invisible. To catch invisible windows too, use a <see cref=\"WinEventHook\"/> instead of a trigger. See example.\n/// </remarks>\n/// <example>\n/// How to use a hook instaed of a window trigger. This code can be in the same file as your window triggers.\n/// <code><![CDATA[\n/// new WinEventHook(EEvent.SYSTEM_FOREGROUND, 0, k => {\n/// \tprint.it(k.w);\n/// \tif (k.w.IsMatch(\"* Name\", \"classname\")) { print.it(\"fast code can be here\", k.w); };\n/// \t//if (k.w.IsMatch(\"* Name\", \"classname\")) run.thread(() => { print.it(\"slower code can be here\", k.w); });\n/// \t//if (k.w.IsMatch(\"* Name\", \"classname\")) script.run(\"large code.cs\", k.w.Handle.ToS());\n/// });\n/// ]]></code>\n/// </example>\npublic enum TWEvent {\n\t/// <summary>\n\t/// When the specified window becomes active (each time).\n\t/// </summary>\n\tActive,\n\t\n\t/// <summary>\n\t/// When the specified window becomes active the first time in the trigger's life.\n\t/// </summary>\n\tActiveOnce,\n\t\n\t/// <summary>\n\t/// When the specified window is created and then becomes active.\n\t/// The same as <see cref=\"ActiveOnce\"/>, but windows created before calling <see cref=\"ActionTriggers.Run\"/> are ignored.\n\t/// </summary>\n\tActiveNew,\n\t\n\t/// <summary>\n\t/// When the specified window becomes visible (each time).\n\t/// </summary>\n\tVisible,\n\t\n\t/// <summary>\n\t/// When the specified window becomes visible the first time in the trigger's life.\n\t/// </summary>\n\tVisibleOnce,\n\t\n\t/// <summary>\n\t/// When the specified window is created and then becomes visible.\n\t/// The same as <see cref=\"VisibleOnce\"/>, but windows created before calling <see cref=\"ActionTriggers.Run\"/> are ignored.\n\t/// </summary>\n\tVisibleNew,\n}\n\n/// <summary>\n/// Window events for the <i>later</i> parameter of window triggers. See <see cref=\"WindowTriggers.this\"/>.\n/// </summary>\n[Flags]\npublic enum TWLater : ushort {\n\t/// <summary>\n\t/// Name changed.\n\t/// This event occurs only when the window is active. If name changed while inactive - when afterwards activated.\n\t/// </summary>\n\tName = 1,\n\t\n\t/// <summary>\n\t/// Destroyed (closed).\n\t/// </summary>\n\tDestroyed = 2,\n\t\n\t/// <summary>\n\t/// Activated (became the foreground window).\n\t/// </summary>\n\tActive = 4,\n\t\n\t/// <summary>\n\t/// Deactivated (lose the foreground window status).\n\t/// This event also occurs when closing the window, if it was active; then the window possibly is already destroyed, and the handle is invalid.\n\t/// </summary>\n\tInactive = 8,\n\t\n\t/// <summary>\n\t/// Became visible (<see cref=\"wnd.IsVisible\"/> true).\n\t/// The window can be new or was temporarily hidden.\n\t/// The window is not actually visible if cloaked, minimized, etc.\n\t/// </summary>\n\tVisible = 16,\n\t\n\t/// <summary>\n\t/// Became invisible (<see cref=\"wnd.IsVisible\"/> false).\n\t/// This event also occurs when closing the window, if it was visible; then the window possibly is already destroyed, and the handle is invalid.\n\t/// </summary>\n\tInvisible = 32,\n\t\n\t/// <summary>\n\t/// The window has been cloaked (<see cref=\"wnd.IsCloaked\"/> true).\n\t/// </summary>\n\tCloaked = 64,\n\t\n\t/// <summary>\n\t/// The window has been uncloaked (<see cref=\"wnd.IsCloaked\"/> false).\n\t/// </summary>\n\tUncloaked = 128,\n\t\n\t/// <summary>\n\t/// The window has been minimized.\n\t/// </summary>\n\tMinimized = 0x100,\n\t\n\t/// <summary>\n\t/// The window has been restored from the minimized state.\n\t/// </summary>\n\tUnminimized = 0x200,\n\t\n\t//rejected. Not useful when we don't have notifications for each location change.\n\t///// <summary>\n\t///// The user started to move or resize the window.\n\t///// This event does not occur when maximizing/restoring and when the window is moved/resized not by the user.\n\t///// </summary>\n\t//MoveSizeStart = 0x400,\n\t\n\t///// <summary>\n\t///// The user finished (or canceled) to move or resize the window.\n\t///// This event does not occur when maximizing/restoring and when the window is moved/resized not by the user.\n\t///// </summary>\n\t//MoveSizeEnd = 0x800,\n\t\n\t//rejected: Location. Use timer or thread-specific EEvent.OBJECT_LOCATIONCHANGE.\n\t//\tProbably it should not be a trigger. If timer, too slow in many cases. If hook, too frequent trigger when drag-moving etc.\n\t//\tIf a script wants to track window location, it can easily set WinEventHook(EEvent.OBJECT_LOCATIONCHANGE) instead.\n\t//rejected: Focus (when eg a child control focused). Use timer or EEvent.OBJECT_FOCUSED.\n\t//\tRarely used. A script can easily use WinEventHook(EEvent.OBJECT_FOCUSED).\n\t//rejected: Timer.\n}\n\n/// <summary>\n/// Represents a window trigger.\n/// </summary>\n/// <example>\n/// <code><![CDATA[\n/// Triggers.Window[TWEvent.ActiveNew, \"Window name\"] = o => print.it(o.Window);\n/// var v = Triggers.Window.Last; //v is the new WindowTrigger. Rarely used.\n/// ]]></code>\n/// </example>\npublic class WindowTrigger : ActionTrigger {\n\tstring _typeString, _paramsString;\n\t\n\t///\n\tpublic TWEvent Event { get; }\n\t///\n\tpublic wndFinder Finder { get; }\n\t///\n\tpublic TWFlags Flags { get; }\n\t///\n\tpublic TWLater Later { get; }\n\t\n\tinternal WindowTrigger(ActionTriggers triggers, Action<WindowTriggerArgs> action, TWEvent ev, wndFinder finder, TWFlags flags, TWLater later, (string, int) source)\n\t\t: base(triggers, action, false, source) {\n\t\tthis.Event = ev;\n\t\tthis.Finder = finder;\n\t\tthis.Flags = flags;\n\t\tthis.Later = later;\n\t}\n\t\n\tinternal override void Run_(TriggerArgs args) => RunT_(args as WindowTriggerArgs);\n\t\n\t/// <summary>\n\t/// Returns a trigger type string, like <c>\"Window.ActiveNew\"</c>.\n\t/// </summary>\n\tpublic override string TypeString => _typeString ??= \"Window.\" + Event;\n\t\n\t/// <summary>\n\t/// Returns a string containing trigger parameters.\n\t/// </summary>\n\tpublic override string ParamsString => _paramsString ??= Finder.ToString();\n\t\n\tinternal bool IsVisible => Event >= TWEvent.Visible;\n\t\n\tinternal bool IsOnce => Event is TWEvent.ActiveOnce or TWEvent.VisibleOnce;\n\t\n\tinternal bool IsNew => Event is TWEvent.ActiveNew or TWEvent.VisibleNew;\n\t\n\tinternal bool IsAlways => Event is TWEvent.Active or TWEvent.Visible;\n}\n\n/// <summary>\n/// Window triggers.\n/// </summary>\n/// <example>\n/// Note: the <c>Triggers</c> in examples is a field or property like <c>readonly ActionTriggers Triggers = new();</c>.\n/// <code><![CDATA[\n/// var wt = Triggers.Window; //wt is a WindowTriggers instance\n/// wt[TWEvent.ActiveNew, \"Window name\"] = o => print.it(o.Window);\n/// wt[TWEvent.Visible, \"Window2 name\"] = o => print.it(o.Window);\n/// Triggers.Run();\n/// ]]></code>\n/// More examples: <see cref=\"ActionTriggers\"/>.\n/// </example>\npublic class WindowTriggers : ITriggers, IEnumerable<WindowTrigger> {\n\tActionTriggers _triggers;\n\t\n\tinternal WindowTriggers(ActionTriggers triggers) {\n\t\t_triggers = triggers;\n\t}\n\t\n\t/// <summary>\n\t/// Adds a window trigger and its action.\n\t/// </summary>\n\t/// <param name=\"winEvent\">Trigger event.</param>\n\t/// <param name=\"flags\">Trigger flags.</param>\n\t/// <param name=\"later\">\n\t/// Can optionally specify one or more additional events.\n\t/// This starts to work when the primary trigger is activated, and works only for that window.\n\t/// For example, to be notified when the window is closed or renamed, specify <c>later: TWLater.Destroyed | TWLater.Name</c>.\n\t/// When a \"later\" event occurs, the trigger action is executed. The <see cref=\"WindowTriggerArgs.Later\"/> property then is that event; it is 0 when it is the primary trigger.\n\t/// The \"later\" triggers are not disabled when primary triggers are disabled.\n\t/// </param>\n\t/// <param name=\"f_\">[](xref:caller_info)</param>\n\t/// <param name=\"l_\">[](xref:caller_info)</param>\n\t/// <exception cref=\"InvalidOperationException\">Cannot add triggers after <see cref=\"ActionTriggers.Run\"/> was called, until it returns.</exception>\n\t/// <exception cref=\"ArgumentException\">See <see cref=\"wnd.find\"/>.</exception>\n\t/// <seealso cref=\"Last\"/>\n\t/// <inheritdoc cref=\"wnd.find(string, string, WOwner, WFlags, Func{wnd, bool}, WContains)\" path=\"/param\"/>\n\tpublic Action<WindowTriggerArgs> this[TWEvent winEvent,\n\t\t\t[ParamString(PSFormat.Wildex)] string name = null,\n\t\t\t[ParamString(PSFormat.Wildex)] string cn = null,\n\t\t\t[ParamString(PSFormat.Wildex)] WOwner of = default,\n\t\t\tFunc<wnd, bool> also = null,\n\t\t\tWContains contains = default,\n\t\t\tTWFlags flags = 0,\n\t\t\tTWLater later = 0,\n\t\t\t[CallerFilePath] string f_ = null,\n\t\t\t[CallerLineNumber] int l_ = 0\n\t\t\t] {\n\t\tset {\n\t\t\tvar f = new wndFinder(name, cn, of, 0, also, contains);\n\t\t\tthis[winEvent, f, flags, later, f_, l_] = value;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Adds a window trigger and its action.\n\t/// </summary>\n\t/// <exception cref=\"InvalidOperationException\">Cannot add triggers after <see cref=\"ActionTriggers.Run\"/> was called, until it returns.</exception>\n\t/// <inheritdoc cref=\"this[TWEvent, string, string, WOwner, Func{wnd, bool}, WContains, TWFlags, TWLater, string, int]\" path=\"/param\"/>\n\tpublic Action<WindowTriggerArgs> this[TWEvent winEvent, wndFinder f, TWFlags flags = 0, TWLater later = 0, [CallerFilePath] string f_ = null, [CallerLineNumber] int l_ = 0] {\n\t\tset {\n\t\t\t_triggers.ThrowIfRunning_();\n\t\t\tif (f.Props.contains.Value is uiimageFinder) print.warning(\"Window triggers with 'contains image' are unreliable.\");\n\t\t\telse if (f.Props.contains.Value is ocrFinder) print.warning(\"Window triggers with 'contains OCR text' are slow.\");\n\t\t\t\n\t\t\tvar t = new WindowTrigger(_triggers, value, winEvent, f, flags, later, (f_, l_));\n\t\t\tref var last = ref _tActive; if (t.IsVisible) last = ref _tVisible;\n\t\t\tif (last == null) {\n\t\t\t\tlast = t;\n\t\t\t\tlast.next = last;\n\t\t\t} else {\n\t\t\t\tt.next = last.next; //first\n\t\t\t\tlast.next = t;\n\t\t\t\tlast = t;\n\t\t\t}\n\t\t\t_laterEvents |= later;\n\t\t\t_lastAdded = t;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// The last added trigger.\n\t/// </summary>\n\tpublic WindowTrigger Last => _lastAdded;\n\tWindowTrigger _lastAdded;\n\t\n\tbool ITriggers.HasTriggers => _lastAdded != null;\n\t\n\tWindowTrigger _tActive, _tVisible; //null or last trigger in linked list\n\tTWLater _laterEvents, _allEvents;\n\tbool _usesVisibleArray; //0 != (_allEvents & (TWLater.Visible | TWLater.Invisible))\n\t\n\tunsafe void ITriggers.StartStop(bool start) {\n\t\tif (start) {\n\t\t\tif (_enumWinProc == null) {\n\t\t\t\t_enumWinProc = _EnumWinProc;\n\t\t\t\t_aTriggered.a = new wnd[100];\n\t\t\t\t_aTriggeredData = new _TriggeredData[100];\n\t\t\t\t_aVisible.a = new wnd[100];\n\t\t\t\t_aVisibleOld.a = new wnd[100];\n\t\t\t\t_hsOld = new HashSet<wnd>(1000);\n\t\t\t\t_hsSeenActivating = new HashSet<wnd>(50);\n\t\t\t\t_winPropCache = new WFCache { CacheName = true, NoTimeout = true, IgnoreVisibility = true };\n\t\t\t} else {\n\t\t\t\tArray.Clear(_aTriggeredData, 0, _aTriggered.len);\n\t\t\t\t_aTriggered.len = _aVisible.len = 0;\n\t\t\t\t_hsOld.Clear();\n\t\t\t\t_hsSeenActivating.Clear();\n\t\t\t\t_winPropCache.Clear();\n\t\t\t}\n\t\t\t\n\t\t\t_allEvents = _laterEvents | TWLater.Active | TWLater.Name;\n\t\t\tif (_tVisible != null) _allEvents |= TWLater.Visible | TWLater.Uncloaked;\n\t\t\t_usesVisibleArray = 0 != (_allEvents & (TWLater.Visible | TWLater.Invisible));\n\t\t\t\n\t\t\tvar ah = new EEvent[5];\n\t\t\tah[0] = EEvent.SYSTEM_FOREGROUND;\n\t\t\tif (osVersion.minWin8) {\n\t\t\t\tif (0 != (_allEvents & TWLater.Uncloaked)) ah[1] = EEvent.OBJECT_UNCLOAKED;\n\t\t\t\tif (0 != (_allEvents & TWLater.Cloaked)) ah[2] = EEvent.OBJECT_CLOAKED;\n\t\t\t}\n\t\t\tif (0 != (_allEvents & TWLater.Minimized)) ah[3] = EEvent.SYSTEM_MINIMIZESTART;\n\t\t\tif (0 != (_allEvents & TWLater.Unminimized)) ah[4] = EEvent.SYSTEM_MINIMIZEEND;\n\t\t\t//if(0 != (_allEvents & TWLater.MoveSizeStart)) ah[5] = EEvent.SYSTEM_MOVESIZESTART;\n\t\t\t//if(0 != (_allEvents & TWLater.MoveSizeEnd)) ah[6] = EEvent.SYSTEM_MOVESIZEEND;\n\t\t\t_hooks = new WinEventHook(ah, _HookProc);\n\t\t\t_hookEventQueue = new Queue<(EEvent, int)>();\n\t\t\t\n\t\t\t_triggers.WinTimerPeriod_ = 250;\n\t\t\t\n\t\t\tApi.IVirtualDesktopManager dm = null;\n\t\t\tApi.EnumWindows((w, param) => {\n\t\t\t\tif (w.IsVisible) {\n\t\t\t\t\tif (_usesVisibleArray) _aVisible.Add(w);\n\t\t\t\t\t//skip empty winstore hosts that later probably will be used as new windows. Speed: ~100 mcs, first time ~10 ms.\n\t\t\t\t\tif (osVersion.minWin10 && w.HasExStyle(WSE.NOREDIRECTIONBITMAP) && w.IsCloaked && w.ClassNameIs(\"ApplicationFrameWindow\")) {\n\t\t\t\t\t\t//is it a window in an inactive virtual desktop? In both cases it does not have a child Windows.UI.Core.CoreWindow.\n\t\t\t\t\t\tdm ??= new Api.VirtualDesktopManager() as Api.IVirtualDesktopManager;\n\t\t\t\t\t\tif (0 == dm.GetWindowDesktopId(w.Get.RootOwnerOrThis(), out var guid) && guid == default) {\n\t\t\t\t\t\t\t//print.it(w);\n\t\t\t\t\t\t\treturn 1;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t_hsOld.Add(w);\n\t\t\t\treturn 1;\n\t\t\t});\n\t\t\tApi.ReleaseComObject(dm);\n\t\t\t\n\t\t\t_wActive = default; _nameActive = null;\n\t\t\t\n\t\t\t//run trigers that have flag TWFlags.RunAtStartup\n\t\t\tfor (int i = 0; i < _aVisible.len; i++) _Proc(TWLater.Visible, _aVisible.a[i], _ProcCaller.Startup);\n\t\t\tvar wa = wnd.active;\n\t\t\tif (!wa.Is0) _Proc(TWLater.Active, wa, _ProcCaller.Startup);\n\t\t} else {\n\t\t\t_hooks?.Dispose();\n\t\t\t_hooks = null;\n\t\t\t_hookEventQueue = null;\n\t\t}\n\t}\n\t\n\tWinEventHook _hooks;\n\tQueue<(EEvent, int)> _hookEventQueue;\n\tWFCache _winPropCache;\n\t\n\tstruct _TriggeredData {\n\t\tpublic object triggered; //WindowTrigger or List<WindowTrigger>\n\t\tpublic string name; //for Name triggers\n\t}\n\t\n\t_WndArray _aTriggered, _aVisible, _aVisibleOld;\n\t_TriggeredData[] _aTriggeredData;\n\tHashSet<wnd> _hs1, _hs2; //to find added and removed visible windows\n\tHashSet<wnd> _hsOld, _hsSeenActivating;\n\twnd _wActive;\n\tstring _nameActive;\n\tint _timerCounter10;\n\t\n\t/// <summary>\n\t/// Called from the message loop every 250 or less ms.\n\t/// </summary>\n\tinternal unsafe void Timer_() {\n\t\t//bool print = !keys.isNumLock;\n\t\t//if(print) print.it(perf.ms % 10000);\n\t\t\n\t\tint period = _triggers.WinTimerPeriod_;\n\t\tif (period < 250) _triggers.WinTimerPeriod_ = Math.Min(period += period / 10 + 1, 250);\n\t\t\n\t\t//bool verbose = !keys.isNumLock;\n\t\t//if(keys.isNumLock) return;\n\t\t//perf.first();\n\t\t//var a = wnd.getwnd.allWindows(true);\n\t\t\n\t\t//wnd.getwnd.allWindows(ref _listVisible, true);\n\t\t\n\t\t//Array.Sort(a);\n\t\t\n\t\t//using(var a=wnd.Internal_.EnumWindows2(wnd.Internal_.EnumAPI.EnumWindows, true)) {\n\t\t\n\t\t//}\n\t\t\n\t\t//if(_aVisibleOld != null) {\n\t\t//\t//var added = a.Except(_aVisibleOld);\n\t\t//\t//var removed = _aVisibleOld.Except(a);\n\t\t//\t//int n1 = added.Count(), n2 = removed.Count();\n\t\t\n\t\t\n\t\t\n\t\t//\t//if(n1 + n2 > 0) print.it(n1, n2);\n\t\t//}\n\t\t//_aVisibleOld = a;\n\t\t//Debug_.MemoryPrint_();\n\t\t\n\t\t//bool needEW = false;\n\t\t//if(Visible.HasTriggers) needEW = true;\n\t\t//else if(Active.HasTriggers) { if(++_timerCounterActiveEW >= 20) { _timerCounterActiveEW = 0; needEW = true; } }\n\t\t\n\t\tif (_usesVisibleArray) {\n\t\t\t\n\t\t\t//print.it(perf.ms % 10000);\n\t\t\t\n\t\t\tvar t1 = _aVisibleOld; _aVisibleOld = _aVisible; _aVisible = t1;\n\t\t\t_aVisible.len = 0;\n\t\t\tApi.EnumWindows(_enumWinProc);\n\t\t\t//perf.next();\n\t\t\t_VisibleAddedRemoved();\n\t\t\t\n\t\t\t//perf.nw();\n\t\t\t//speed with  3 main windows: 200, +IsVisible 270, +IsCloaked 450, +Name-IsCloaked 444\n\t\t\t//speed with 20 main windows (79 visible, 541 total): 320, +IsVisible 430 (CPU 0.05). With 480 CPU 0.06, sometimes 0.05.\n\t\t\t//print.it(n);\n\t\t\t//print.it(_listVisible.Count);\n\t\t}\n\t\t\n\t\tvar a = _aTriggered.a;\n\t\tfor (int i = 0; i < _aTriggered.len; i++) {\n\t\t\tif (!Api.IsWindow(a[i])) {\n\t\t\t\t_ProcLater(TWLater.Destroyed, a[i], i);\n\t\t\t\tint last = --_aTriggered.len;\n\t\t\t\ta[i] = a[last];\n\t\t\t\t_aTriggeredData[i] = _aTriggeredData[last];\n\t\t\t\t_aTriggeredData[last] = default;\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (++_timerCounter10 >= 10) { //every 2.5 s\n\t\t\t_timerCounter10 = 0;\n\t\t\t_hsOld.RemoveWhere(o => !Api.IsWindow(o));\n\t\t\t_hsSeenActivating.RemoveWhere(o => !Api.IsWindow(o));\n\t\t\t_winPropCache.Clear();\n\t\t}\n\t\t\n\t\tvar w = wnd.active;\n\t\tif (w.Is0) {\n\t\t\tif (!_wActive.Is0) _ProcLater(TWLater.Inactive, _wActive);\n\t\t\t_wActive = default; _nameActive = null;\n\t\t} else if (w != _wActive) {\n\t\t\t_Proc(TWLater.Active, w);\n\t\t} else {\n\t\t\tvar name = w.NameTL_;\n\t\t\tif (name != null && name != _nameActive) {\n\t\t\t\t_nameActive = name;\n\t\t\t\t_Proc(TWLater.Name, w, name: name);\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Callback of <c>EnumWindows</c> used by <c>Timer_</c> to get visible windows.\n\t/// </summary>\n\tApi.WNDENUMPROC _enumWinProc;\n\tunsafe int _EnumWinProc(wnd w, void* _) {\n\t\tif (w.IsVisible) _aVisible.Add(w);\n\t\treturn 1;\n\t}\n\t\n\t/// <summary>\n\t/// Called by <c>Timer_</c> when it swaps <c>_aVisible</c> with <c>_aVisibleOld</c> and calls <c>EnumWindows</c> to populate <c>_aVisible</c> with visible windows.\n\t/// Finds what windows became visible or invisible and runs <c>Visible</c>/<c>Invisible</c> triggers for them.\n\t/// </summary>\n\tvoid _VisibleAddedRemoved() {\n\t\t//perf.first();\n\t\twnd[] aNew = _aVisible.a, aOld = _aVisibleOld.a;\n\t\tint nNew = _aVisible.len, nOld = _aVisibleOld.len;\n\t\tint to = Math.Min(nOld, nNew), diffFrom;\n\t\tfor (diffFrom = 0; diffFrom < to; diffFrom++) if (aNew[diffFrom] != aOld[diffFrom]) break;\n\t\tif (nNew == nOld && diffFrom == nNew) return;\n\t\tint diffTo1 = nOld, diffTo2 = nNew;\n\t\twhile (diffTo1 > 0 && diffTo2 > 0) if (aNew[--diffTo2] != aOld[--diffTo1]) { diffTo1++; diffTo2++; break; }\n\t\t//print.it(diffFrom, diffTo1, diffTo2, Math.Max(diffTo1 - diffFrom, diffTo2 - diffFrom));\n\t\tint n1 = diffTo1 - diffFrom, n2 = diffTo2 - diffFrom;\n\t\t//print.it($\"from={diffFrom} to1={diffTo1} to2={diffTo2}    n1={n1} n2={n2}  n1*n2={n1*n2}\");\n\t\tif (n1 == 0) { //only added\n\t\t\tif (0 != (_allEvents & TWLater.Visible)) {\n\t\t\t\tfor (int i = diffFrom; i < diffTo2; i++) _Added(i);\n\t\t\t}\n\t\t} else if (n2 == 0) { //only removed\n\t\t\tif (0 != (_allEvents & TWLater.Invisible)) {\n\t\t\t\tfor (int i = diffFrom; i < diffTo1; i++) _Removed(i);\n\t\t\t}\n\t\t} else { //reordered or/and added/removed\n\t\t\t//print.it($\"n1={n1} n2={n2}  n1*n2={n1 * n2}\");\n\t\t\t//perf.next();\n\t\t\t//TODO3: optimize. Now slow when large array reordered. Eg divide the changed range into two.\n\t\t\t//n1 = n2 = 0;\n\t\t\tif (0 != (_allEvents & TWLater.Invisible)) {\n\t\t\t\tif (_hs2 == null) _hs2 = new HashSet<wnd>(500); else _hs2.Clear();\n\t\t\t\tfor (int i = diffFrom; i < diffTo2; i++) _hs2.Add(aNew[i]);\n\t\t\t\tfor (int i = diffFrom; i < diffTo1; i++) if (!_hs2.Remove(aOld[i])) _Removed(i);\n\t\t\t}\n\t\t\tif (0 != (_allEvents & TWLater.Visible)) {\n\t\t\t\tif (_hs1 == null) _hs1 = new HashSet<wnd>(500); else _hs1.Clear();\n\t\t\t\tfor (int i = diffFrom; i < diffTo1; i++) _hs1.Add(aOld[i]);\n\t\t\t\tfor (int i = diffFrom; i < diffTo2; i++) if (!_hs1.Remove(aNew[i])) _Added(i);\n\t\t\t}\n\t\t\t//perf.nw();\n\t\t}\n\t\t//if(n1 + n2 > 0) print.it($\"<><bc yellow>added {n2}, removed {n1}<>\");\n\t\t\n\t\tvoid _Added(int i) {\n\t\t\t//n2++; //print.it(\"added\", aNew[i]);\n\t\t\t_Proc(TWLater.Visible, aNew[i]);\n\t\t}\n\t\t\n\t\tvoid _Removed(int i) {\n\t\t\t//n1++; //print.it(\"removed\", aOld[i]);\n\t\t\t_ProcLater(TWLater.Invisible, aOld[i]);\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// <c>WinEventHook</c> hook procedure.\n\t/// </summary>\n\t/// <param name=\"k\"></param>\n\tvoid _HookProc(HookData.WinEvent k) {\n\t\t//if(!keys.isNumLock) print.it(k.event_, k.idObject, k.idChild, k.w);\n\t\t\n\t\tif (k.idObject != EObjid.WINDOW) return;\n\t\tif (k.idChild != 0 || k.w.Is0) return;\n\t\t\n\t\tvar event_ = k.event_;\n\t\tvar w = k.w;\n\t\t\n\t\tif (w.IsChild) {\n\t\t\t//if(event_ == EEvent.OBJECT_UNCLOAKED) { } //rejected: wait for winstore app host child window\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tif (_inProc) {\n\t\t\t_hookEventQueue.Enqueue((event_, (int)w));\n\t\t\t//print.it(_hookEventQueue.Count);\n\t\t\tDebug_.PrintIf(_hookEventQueue.Count > 4, \"_hookEventQueue.Count=\" + _hookEventQueue.Count);\n\t\t\treturn;\n\t\t}\n\t\t_inProc = true;\n\t\ttry {\n\t\t\t//wait.doEvents(300); //test queue\n\t\t\t//perf.cpu();\n\t\t\tfor (; ; ) {\n\t\t\t\tTWLater e = 0;\n\t\t\t\tswitch (event_) {\n\t\t\t\tcase EEvent.SYSTEM_FOREGROUND:\n\t\t\t\t\t//Debug_.PrintIf(!w.IsActive, $\"{perf.ms % 10000}, SYSTEM_FOREGROUND but not active: {w}\"); //it is normal. The window either will be active soon (and timer will catch it), or will not be activated (eg prevented by Windows, hooks, etc), or another window became active.\n\t\t\t\t\tif (w != _wActive && w.IsActive) e = TWLater.Active;\n\t\t\t\t\tbreak;\n\t\t\t\tcase EEvent.OBJECT_UNCLOAKED: e = TWLater.Uncloaked; break;\n\t\t\t\tcase EEvent.OBJECT_CLOAKED: e = TWLater.Cloaked; break;\n\t\t\t\tcase EEvent.SYSTEM_MINIMIZESTART: e = TWLater.Minimized; break;\n\t\t\t\tcase EEvent.SYSTEM_MINIMIZEEND: e = TWLater.Unminimized; break;\n\t\t\t\t\t//case EEvent.SYSTEM_MOVESIZESTART: e = TWLater.MoveSizeStart; break;\n\t\t\t\t\t//case EEvent.SYSTEM_MOVESIZEEND: e = TWLater.MoveSizeEnd; break;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (e != 0) _Proc(e, w, _ProcCaller.Hook);\n\t\t\t\t\n\t\t\t\t//Normal timer period is 255. It is optimized for quite small CPU usage and quite good response time.\n\t\t\t\t//To improve response time, we temporarily set smaller period on SYSTEM_FOREGROUND. Need it when the window is not active too.\n\t\t\t\t//If activating a new window first time, set period = 15, to quickly catch Visible and Name events.\n\t\t\t\t//Else set period = 105, for Name events after closing an \"Open File\" dialog (then usually its owner becomes active and then changes name).\n\t\t\t\tif (event_ == EEvent.SYSTEM_FOREGROUND) {\n\t\t\t\t\tbool fast = _hsSeenActivating.Add(w) && !_hsOld.Contains(w);\n\t\t\t\t\t//print.it(\"fast timer\", fast);\n\t\t\t\t\t_triggers.WinTimerPeriod_ = fast ? 1 : 100;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (_hookEventQueue.Count == 0) break;\n\t\t\t\tvar q = _hookEventQueue.Dequeue();\n\t\t\t\tevent_ = q.Item1;\n\t\t\t\tw = (wnd)q.Item2;\n\t\t\t}\n\t\t\t_inProc = false;\n\t\t}\n\t\tfinally {\n\t\t\tif (_inProc) { _inProc = false; _hookEventQueue.Clear(); } //exception. Unlikely, because we handle script exceptions, except thread abort.\n\t\t}\n\t}\n\tbool _inProc;\n\t\n\tenum _ProcCaller { Timer, Hook, Run, Startup }\n\t\n\t/// <summary>\n\t/// Processes events for main triggers (active, visible) and most \"later\" triggers.\n\t/// Called from hook (<c>_HookProc</c>), timer (<c>Timer_</c>), at startup (<c>StartStop</c>) and <c>SimulateActiveNew</c>/<c>SimulateVisibleNew</c>.\n\t/// </summary>\n\tvoid _Proc(TWLater e, wnd w, _ProcCaller caller = _ProcCaller.Timer, string name = null) {\n\t\t//e can be:\n\t\t//Used for main triggers (Active, Visible) and other triggers:\n\t\t//\tActive - from SYSTEM_FOREGROUND hook or from timer. Now the window is active and not 0.\n\t\t//\tName - from timer, and only when the window is active.\n\t\t//\tVisible - from timer.\n\t\t//\tUncloaked - from OBJECT_UNCLOAKED hook.\n\t\t//Used only for other triggers:\n\t\t//\tCloaked - from OBJECT_CLOAKED hook.\n\t\t//\tMinimized from SYSTEM_MINIMIZESTART hook.\n\t\t//\tUnminimized from SYSTEM_MINIMIZEEND hook.\n\t\t//\trejected: MoveSizeStart, MoveSizeEnd.\n\t\tDebug.Assert(e == TWLater.Active || e == TWLater.Name || e == TWLater.Visible || e == TWLater.Uncloaked || e == TWLater.Cloaked || e == TWLater.Minimized || e == TWLater.Unminimized/* || e == TWLater.MoveSizeStart || e == TWLater.MoveSizeEnd*/);\n\t\t\n\t\tbool callerHT = caller == _ProcCaller.Hook || caller == _ProcCaller.Timer;\n\t\t\n\t\t//Ignore Active if invisible. Triggering while invisible creates more problems than is useful. The timer will call us later.\n\t\t//\tIt makes the trigger slower in many cases. Usually the speed is important when closing unwanted messageboxes etc.\n\t\t//\tIf closed while invisible, the user usually never sees the window. It seems like it would be better, but can create problems:\n\t\t//\t1. If the trigger uses 'contains', in some windows we cannot find the object because it is still invisible etc; example - TaskDialog windows.\n\t\t//\t2. If the trigger is not perfectly specified and its action closes wrong windows, the user does not know what is going on.\n\t\t//\t3. It temporarily deactivates the current window anyway, and often the user is unaware about it.\n\t\tif (e == TWLater.Active && callerHT && !w.IsVisibleAndNotCloaked) return;\n\t\t//if(e == TWLater.Active && callerHT) { if(!w.IsVisibleAndNotCloaked) { _perf.First(); return; } else if(caller== _ProcCaller.Timer) _perf.NW('A'); }\n\t\t\n\t\tint iTriggered = _aTriggered.Find(w);\n\t\t\n\t\tbool detectedVisibleNow = e != TWLater.Visible && _usesVisibleArray && callerHT && w.IsVisible && _aVisible.Find(w) < 0;\n\t\tif (detectedVisibleNow) {\n\t\t\t_aVisible.Add(w);\n\t\t\t_ProcLater(TWLater.Visible, w, iTriggered);\n\t\t}\n\t\t\n\t\tbool runActive = false;\n\t\tif (e == TWLater.Active) {\n\t\t\tname = w.NameTL_;\n\t\t\tif (callerHT) {\n\t\t\t\t_ProcLater(TWLater.Name, w, iTriggered, name); //maybe name changed while inactive\n\t\t\t\tif (!_wActive.Is0) _ProcLater(TWLater.Inactive, _wActive);\n\t\t\t}\n\t\t\t_wActive = w; _nameActive = name;\n\t\t\tif (callerHT) _ProcLater(e, w, iTriggered);\n\t\t\trunActive = _tActive != null;\n\t\t} else {\n\t\t\tif (callerHT) _ProcLater(e, w, iTriggered, name);\n\t\t\tswitch (e) {\n\t\t\tcase TWLater.Name: //from timer, when active window name changed\n\t\t\t\trunActive = _tActive != null;\n\t\t\t\tbreak;\n\t\t\tcase TWLater.Visible:\n\t\t\tcase TWLater.Uncloaked:\n\t\t\t\tname = w.NameTL_;\n\t\t\t\tbreak;\n\t\t\tdefault: return;\n\t\t\t}\n\t\t}\n\t\t\n\t\tbool runVisible = _tVisible != null && w.IsVisibleAndNotCloaked;\n\t\tif (!(runVisible | runActive)) return;\n\t\t\n\t\tbool oldWindow = callerHT && _hsOld.Contains(w);\n\t\t\n\t\tif (_log && callerHT && (_logSkip == null || !_logSkip(w))) {\n\t\t\tif (detectedVisibleNow) _LogEvent(TWLater.Visible, w, caller, oldWindow);\n\t\t\t_LogEvent(e, w, caller, oldWindow);\n\t\t}\n\t\t\n\t\tobject triggered = iTriggered >= 0 ? _aTriggeredData[iTriggered].triggered : null, triggeredOld = triggered;\n\t\t\n\t\t_winPropCache.Begin(w); //info: don't need to call Clear now. Timer calls it every 2.5 s.\n\t\t_winPropCache.Name = name;\n\t\t\n\t\t//perf.first();\n\t\tWindowTriggerArgs args = null;\n\t\t\n\t\t//print.it(runVisible,runActive,w.IsActive, oldWindow);\n\t\t\n\t\tif (runVisible) _Do(true, runActive && !detectedVisibleNow);\n\t\tif (runActive && w.IsActive) _Do(false, e != TWLater.Active);\n\t\t\n\t\tvoid _Do(bool visible, bool secondaryEvent) {\n\t\t\tActionTrigger last = visible ? _tVisible : _tActive, v = last;\n\t\t\tif (last == null) return;\n\t\t\t\n\t\t\tdo {\n\t\t\t\tv = v.next;\n\t\t\t\tif (v.DisabledThisOrAll) continue;\n\t\t\t\tvar t = v as WindowTrigger;\n\t\t\t\t\n\t\t\t\tif (caller == _ProcCaller.Startup && 0 == (t.Flags & TWFlags.RunAtStartup)) continue;\n\t\t\t\tif (oldWindow && (t.IsNew || secondaryEvent)) continue;\n\t\t\t\tif (triggered != null && (!t.IsAlways || secondaryEvent)) {\n\t\t\t\t\tswitch (triggered) {\n\t\t\t\t\tcase WindowTrigger wt1: if (wt1 == t) continue; break;\n\t\t\t\t\tcase List<WindowTrigger> awt1: if (awt1.Contains(t)) continue; break;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ttry {\n\t\t\t\t\tif (!t.Finder.IsMatch(w, _winPropCache)) continue;\n\t\t\t\t\t\n\t\t\t\t\t//rejected: if 'contains' is image and it does not match, wait several seconds until it matches.\n\t\t\t\t\t//\tNow such triggers sometimes don't work because need more time to show the image.\n\t\t\t\t\t//\tRare and too difficult. Cannot wait now. Would need to use timer or threadpool.\n\t\t\t\t}\n\t\t\t\tcatch (Exception ex) {\n\t\t\t\t\tprint.it(ex);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (args == null) args = new WindowTriggerArgs(t, w, 0); else args.Trigger = t;\n\t\t\t\t\n\t\t\t\tif (!t.CallFunc_(args)) continue; //info: handles exceptions\n\t\t\t\t\n\t\t\t\tswitch (triggered) {\n\t\t\t\tcase null: triggered = t; break;\n\t\t\t\tcase WindowTrigger wt1: if (wt1 != t) triggered = new List<WindowTrigger> { wt1, t }; break;\n\t\t\t\tcase List<WindowTrigger> awt1: if (!awt1.Contains(t)) awt1.Add(t); break;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t//if(!visible && !w.IsActive) break; //no\n\t\t\t\t\n\t\t\t\tif (_log) print.it($\"<><c red>{t.TypeString}<>\");\n\t\t\t\t\n\t\t\t\t_triggers.RunAction_(t, args);\n\t\t\t} while (v != last);\n\t\t\t//perf.nw(); //speed ms/triggers when cold CPU: ~1/1000, 10/10000, 50/100000, 130/1000000\n\t\t}\n\t\t\n\t\tif (triggered != triggeredOld) {\n\t\t\tif (iTriggered < 0) {\n\t\t\t\tiTriggered = _aTriggered.len;\n\t\t\t\t_aTriggered.Add(w);\n\t\t\t\tif (iTriggered == _aTriggeredData.Length) Array.Resize(ref _aTriggeredData, iTriggered * 2);\n\t\t\t\t_aTriggeredData[iTriggered].name = name; //later managed by _Proc2\n\t\t\t}\n\t\t\t_aTriggeredData[iTriggered].triggered = triggered;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Called to process \"later\" events from <c>_Proc</c> and timer.\n\t/// <i>iTriggered</i> is <i>w</i> index in <c>_aTriggered</c>, or -1 if not found, or -2 (default) to let this func find.\n\t/// </summary>\n\tvoid _ProcLater(TWLater e, wnd w, int iTriggered = -2, string name = null) {\n\t\tif (0 == (_laterEvents & e)) return;\n\t\tif (iTriggered < -1) iTriggered = _aTriggered.Find(w);\n\t\tif (iTriggered < 0) return;\n\t\t\n\t\tif (e == TWLater.Name) { //called in 2 cases: 1. From timer, when name changed. 2. From _Proc on Active event (hook or timer).\n\t\t\tif (name == null || _aTriggeredData[iTriggered].name == name) return;\n\t\t\t_aTriggeredData[iTriggered].name = name;\n\t\t}\n\t\t\n\t\tif (_log) print.it($\"\\t{perf.ms % 10000,4}, {e,-11}, {w}\");\n\t\t\n\t\tWindowTriggerArgs args = null;\n\t\tvar triggered = _aTriggeredData[iTriggered].triggered;\n\t\tvar a = triggered as List<WindowTrigger>;\n\t\tint n = a?.Count ?? 1;\n\t\tfor (int i = 0; i < n; i++) {\n\t\t\tvar t = a?[i] ?? (triggered as WindowTrigger);\n\t\t\tif (0 == (t.Later & e)) continue;\n\t\t\t//if(t.DisabledThisOrAll) continue; //no\n\t\t\tif (args == null) args = new WindowTriggerArgs(t, w, e); else args.Trigger = t;\n\t\t\tif (0 != (t.Flags & TWFlags.LaterCallFunc)) {\n\t\t\t\tif (!t.CallFunc_(args)) continue;\n\t\t\t}\n\t\t\t\n\t\t\tif (_log) print.it($\"<><c red>\\t{e}<>\");\n\t\t\t\n\t\t\t_triggers.RunAction_(t, args);\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Simulates event \"activated new window\" as if the specified window is that window.\n\t/// </summary>\n\t/// <exception cref=\"InvalidOperationException\">Called before or after <see cref=\"ActionTriggers.Run\"/>.</exception>\n\t/// <remarks>\n\t/// This function usually is used to run <c>ActiveNew</c> triggers for a window created before calling <see cref=\"ActionTriggers.Run\"/>. Here \"run triggers\" means \"compare window properties etc with those specified in triggers and run actions of triggers that match\". Normally such triggers don't run because the window is considered old. This function runs triggers as it was a new window. Triggers like <c>ActiveNew</c> and <c>ActiveOnce</c> will run once, as usually.\n\t/// This function must be called while the main triggers thread is in <see cref=\"ActionTriggers.Run\"/>, for example from another trigger action. It is asynchronous (does not wait).\n\t/// If called from a trigger action (hotkey etc), make sure the window trigger action runs in another thread or can be queued. Else both actions cannot run simultaneously. See example.\n\t/// </remarks>\n\t/// <example>\n\t/// Note: the <c>Triggers</c> in examples is a field or property of type <see cref=\"ActionTriggers\"/>.\n\t/// <code><![CDATA[\n\t/// Triggers.Options.ThreadNew(true);\n\t/// Triggers.Window[TWEvent.ActiveNew, \"* Notepad\"] = o => o.Window.Resize(500, 200);\n\t/// Triggers.Hotkey[\"Ctrl+T\"] = o => Triggers.Window.SimulateActiveNew(wnd.active);\n\t/// ]]></code>\n\t/// </example>\n\tpublic void SimulateActiveNew(wnd w) => _SimulateNew(TWLater.Active, w);\n\t\n\t/// <summary>\n\t/// Simulates event \"visible new window\" as if the specified window is that window.\n\t/// Similar to <see cref=\"SimulateActiveNew\"/>.\n\t/// </summary>\n\t/// <param name=\"w\"></param>\n\t/// <exception cref=\"InvalidOperationException\">Called before or after <see cref=\"ActionTriggers.Run\"/>.</exception>\n\t/// <remarks>\n\t/// Cannot be called before or after <see cref=\"ActionTriggers.Run\"/>.\n\t/// </remarks>\n\tpublic void SimulateVisibleNew(wnd w) => _SimulateNew(TWLater.Visible, w);\n\t\n\tvoid _SimulateNew(TWLater e, wnd w) {\n\t\t_triggers.ThrowIfNotRunning_();\n\t\t_triggers.SendMsg_(false, () => { if (w.IsAlive) _Proc(e, w, _ProcCaller.Run); });\n\t}\n\t\n\tbool _log;\n\tFunc<wnd, bool> _logSkip;\n\t\n\t/// <summary>\n\t/// Starts or stops to log (write in output) window events that can help to create or debug window triggers.\n\t/// </summary>\n\t/// <param name=\"on\">Start (<c>true</c>) or stop.</param>\n\t/// <param name=\"skip\">An optional callback function that can be used to reduce noise, eg skip tooltip windows. Return <c>true</c> to skip that window.</param>\n\t/// <remarks>\n\t/// For primary trigger events is logged this info:\n\t/// <ol>\n\t/// <li>Time milliseconds. Shows only the remainder of dividing by 10 seconds, therefore it starts from 0 again when reached 9999 (9 seconds and 999 milliseconds).</li>\n\t/// <li>Event (see <see cref=\"TWLater\"/>).</li>\n\t/// <li>Letters for window state etc:\n\t/// <ul>\n\t/// <li><c>A</c> - the window is active.</li>\n\t/// <li><c>H</c> - the window is invisible (!<see cref=\"wnd.IsVisible\"/>).</li>\n\t/// <li><c>C</c> - the window is cloaked (<see cref=\"wnd.IsCloaked\"/>).</li>\n\t/// <li><c>O</c> - the window is considered old, ie created before calling <see cref=\"ActionTriggers.Run\"/>.</li>\n\t/// <li><c>T</c> - the even has been detected using a timer, which means slower response time. Else detected using a hook.</li>\n\t/// </ul>\n\t/// </li>\n\t/// <li>Window (handle, class, name, program, rectangle).</li>\n\t/// </ol>\n\t/// \n\t/// Colors are used for window event types used for primary triggers: blue if activated; green if became visible; yellow if name changed.\n\t/// For \"later\" events is logged time, event and window. Black, tab-indented. Only events that are specified in triggers.\n\t/// When a trigger is activated, the event type is red.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// Triggers.Window.LogEvents(true, o => 0 != o.ClassNameIs(\"*tooltip*\", \"SysShadow\", \"#32774\", \"TaskList*\"));\n\t/// ]]></code>\n\t/// </example>\n\tpublic void LogEvents(bool on, Func<wnd, bool> skip = null) {\n\t\t_log = on;\n\t\t_logSkip = skip;\n\t}\n\t\n\t/// <summary>\n\t/// Called by <c>_Proc</c>.\n\t/// </summary>\n\tvoid _LogEvent(TWLater e, wnd w, _ProcCaller caller, bool oldWindow) {\n\t\tstring col = \"0\";\n\t\tswitch (e) {\n\t\tcase TWLater.Active: col = \"0x0000ff\"; break;\n\t\tcase TWLater.Visible: case TWLater.Uncloaked: col = \"0x00c000\"; break;\n\t\tcase TWLater.Name: col = \"0xC0C000\"; break;\n\t\t}\n\t\tvar A = w.IsActive ? \"A\" : \" \";\n\t\tvar H = w.IsVisible ? \" \" : \"H\";\n\t\tvar C = w.IsCloaked ? \"C\" : \" \";\n\t\tvar O = oldWindow ? \"O\" : \" \";\n\t\tvar T = caller == _ProcCaller.Timer ? \"T\" : \" \";\n\t\tprint.it($\"<><c {col}>{perf.ms % 10000,4}, {e,-11}, {A}{H}{C}{O}{T}, {w}</c>\");\n\t}\n\t\n\t/// <summary>\n\t/// For <c>_aTriggered</c>, <c>_aVisible</c> and <c>_aVisibleOld</c> we use resizable array, not <c>List</c>.\n\t/// We access elements by index in time-critical code. With <c>List</c> it is much slower.\n\t/// </summary>\n\tstruct _WndArray {\n\t\tpublic wnd[] a;\n\t\tpublic int len;\n\t\t\n\t\tpublic void Add(wnd w) {\n\t\t\tif (len == a.Length) Array.Resize(ref a, len * 2);\n\t\t\ta[len++] = w;\n\t\t}\n\t\t\n\t\tpublic unsafe int Find(wnd w) {\n\t\t\tfixed (wnd* p = a) {\n\t\t\t\tfor (int i = 0, n = len; i < n; i++) if (p[i] == w) return i;\n\t\t\t}\n\t\t\treturn -1;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Used by <c>foreach</c> to enumerate added triggers.\n\t/// </summary>\n\tpublic IEnumerator<WindowTrigger> GetEnumerator() {\n\t\tActionTrigger last, v;\n\t\tif (_tActive != null) {\n\t\t\tlast = v = _tActive;\n\t\t\tdo {\n\t\t\t\tv = v.next;\n\t\t\t\tvar x = v as WindowTrigger;\n\t\t\t\tyield return x;\n\t\t\t} while (v != last);\n\t\t}\n\t\tif (_tVisible != null) {\n\t\t\tlast = v = _tVisible;\n\t\t\tdo {\n\t\t\t\tv = v.next;\n\t\t\t\tvar x = v as WindowTrigger;\n\t\t\t\tyield return x;\n\t\t\t} while (v != last);\n\t\t}\n\t}\n\t\n\tIEnumerator IEnumerable.GetEnumerator() => GetEnumerator();\n}\n\n/// <summary>\n/// Arguments for actions of window triggers.\n/// </summary>\npublic class WindowTriggerArgs : TriggerArgs {\n\t/// <summary>\n\t/// The trigger.\n\t/// </summary>\n\tpublic WindowTrigger Trigger { get; internal set; }\n\t\n\t///\n\t[EditorBrowsable(EditorBrowsableState.Never)]\n\tpublic override ActionTrigger TriggerBase => Trigger;\n\t\n\t/// <summary>\n\t/// The window.\n\t/// </summary>\n\tpublic wnd Window { get; }\n\t\n\t/// <summary>\n\t/// The \"later\" event, or 0 if it is the primary trigger (<c>ActiveNew</c> etc). See example.\n\t/// </summary>\n\t/// <example>\n\t/// Note: the <c>Triggers</c> in examples is a field or property like <c>readonly ActionTriggers Triggers = new();</c>.\n\t/// <code><![CDATA[\n\t/// Triggers.Window[TWEvent.ActiveOnce, \"*- Notepad\", later: TWLater.Active | TWLater.Inactive] = o => print.it(o.Later, o.Window);\n\t/// Triggers.Run();\n\t/// ]]></code>\n\t/// </example>\n\tpublic TWLater Later { get; }\n\t\n\t///\n\tpublic WindowTriggerArgs(WindowTrigger trigger, wnd w, TWLater later) {\n\t\tTrigger = trigger;\n\t\tWindow = w;\n\t\tLater = later;\n\t}\n\t\n\t///\n\tpublic override string ToString() => \"Trigger: \" + Trigger;\n\t\n\t/// <summary>\n\t/// Shows or hides a window-attached toolbar depending on window name.\n\t/// Use in window trigger action, like in examples.\n\t/// </summary>\n\t/// <param name=\"tbFunc\">Toolbar function. Let it create a window-attached toolbar and returns the <see cref=\"toolbar\"/> object.</param>\n\t/// <param name=\"windowName\">Window name when the toolbar should be visible. String format: [wildcard expression](xref:wildcard_expression).</param>\n\t/// <exception cref=\"ArgumentException\">Invalid wildcard expression (<c>\"**options \"</c> or regular expression).</exception>\n\t/// <remarks>\n\t/// The trigger must have argument <c>, later: TWLater.Name</c> (see <see cref=\"TWLater\"/>) and match the window with any name.\n\t/// </remarks>\n\t/// <example>\n\t/// Show toolbar <c>Toolbar_Chrome1</c> on a Chrome window when window name starts with <c>\"NuGet\"</c>.\n\t/// <code><![CDATA[\n\t/// Triggers.Window[TWEvent.ActiveOnce, \"*Google Chrome\", \"Chrome_WidgetWin_1\", later: TWLater.Name] =\n\t/// \tta => ta.ShowToolbarWhenWindowName(Toolbar_Chrome1, \"NuGet*\");\n\t/// ]]></code>\n\t/// Toolbar <c>Toolbar_Chrome1</c> for the above example.\n\t/// <code><![CDATA[\n\t/// toolbar Toolbar_Chrome1(WindowTriggerArgs ta) { var t = new toolbar(); /* add buttons */ t.Show(ta); return t; }\n\t/// ]]></code>\n\t/// Show Toolbar_A when window name starts with <c>\"A -\"</c>, Toolbar_B when <c>\"B -\"</c>, and Toolbar_C always.\n\t/// <code><![CDATA[\n\t/// Triggers.Window[TWEvent.ActiveOnce, \"*Google Chrome\", \"Chrome_WidgetWin_1\", later: TWLater.Name] = ta => {\n\t/// \tta.ShowToolbarWhenWindowName(Toolbar_A, \"A -*\");\n\t/// \tta.ShowToolbarWhenWindowName(Toolbar_B, \"B -*\");\n\t/// \tif (ta.Later == 0) Toolbar_C(ta);\n\t/// };\n\t/// ]]></code>\n\t/// </example>\n\tpublic void ShowToolbarWhenWindowName(Func<WindowTriggerArgs, toolbar> tbFunc, [ParamString(PSFormat.Wildex)] string windowName) {\n\t\tvar wild = new wildex(windowName);\n\t\tShowToolbarWhenWindowName(tbFunc, o => wild.Match(o.Name));\n\t}\n\t//rejected: shorter syntax. Limited, eg can't use the same trigger for multiple toolbars. Examples:\n\t//\tTriggers.Window[TWEvent.ActiveOnce, \"*Google Chrome\", \"Chrome_WidgetWin_1\", whenName: \"NuGet*\"] = Toolbar_Chrome1;\n\t//\tTriggers.Window.Toolbar(Toolbar_Chrome1, \"*Google Chrome\", \"Chrome_WidgetWin_1\", whenName: \"NuGet*\");\n\t\n\t/// <summary>\n\t/// Shows or hides a window-attached toolbar depending on window name.\n\t/// Use in window trigger action, like in examples.\n\t/// </summary>\n\t/// <param name=\"tbFunc\">Toolbar function. Let it create a window-attached toolbar and returns the <see cref=\"toolbar\"/> object.</param>\n\t/// <param name=\"windowName\">Callback function that returns <c>true</c> if the toolbar should be visible.</param>\n\t/// <example>\n\t/// Show toolbar <c>Toolbar_Chrome2</c> on a Chrome window when web page URL starts with <c>\"https://www.youtube.com/\"</c>.\n\t/// <code><![CDATA[\n\t/// Triggers.Window[TWEvent.ActiveOnce, \"*Google Chrome\", \"Chrome_WidgetWin_1\", later: TWLater.Name] = ta => ta.ShowToolbarWhenWindowName(Toolbar_Chrome2, w => {\n\t/// \tvar e = w.Elm[\"web:DOCUMENT\"].Find(-1);\n\t/// \treturn e != null && e.Value.Starts(\"https://www.youtube.com/\");\n\t/// });\n\t/// ]]></code>\n\t/// </example>\n\tpublic void ShowToolbarWhenWindowName(Func<WindowTriggerArgs, toolbar> tbFunc, Func<wnd, bool> windowName) {\n\t\tvar mi = tbFunc.Method;\n\t\t(t_mt ??= new()).TryGetValue(mi, out var v);\n\t\tif (!windowName(Window)) { v?.Close(); return; }\n\t\tif (v != null) return;\n\t\tv = tbFunc(this);\n\t\tif (v != null) {\n\t\t\tt_mt[mi] = v;\n\t\t\tv.Closed += () => { t_mt.Remove(mi); };\n\t\t} else {\n\t\t\tt_mt.Remove(mi);\n\t\t}\n\t}\n\t\n\t[ThreadStatic] static Dictionary<MethodInfo, toolbar> t_mt;\n}\n"
  },
  {
    "path": "Au/UI objects/CaptureScreen.cs",
    "content": "using System.Drawing;\nusing System.Drawing.Imaging;\n\nnamespace Au.More {\n\t/// <summary>\n\t/// Contains functions and tools to capture image, color, window, rectangle or point from screen.\n\t/// </summary>\n\tpublic static class CaptureScreen {\n\t\t#region capture image\n\n\t\t/// <summary>\n\t\t/// Creates image from a rectangle of screen pixels.\n\t\t/// </summary>\n\t\t/// <param name=\"r\">Rectangle in screen.</param>\n\t\t/// <exception cref=\"ArgumentException\">Empty rectangle.</exception>\n\t\t/// <exception cref=\"AuException\">Failed. Probably there is not enough memory for bitmap of this size (<c>width*height*4</c> bytes).</exception>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// var file = folders.Temp + \"notepad.png\";\n\t\t/// wnd w = wnd.find(\"* Notepad\");\n\t\t/// w.GetRect(out var r, true);\n\t\t/// using(var b = CaptureScreen.Image(r)) { b.Save(file); }\n\t\t/// run.it(file);\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic static Bitmap Image(RECT r) {\n\t\t\tusing var c = new CaptureScreenImage();\n\t\t\tc.Capture(r);\n\t\t\treturn c.ToBitmap();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Creates image from a rectangle of window client area pixels.\n\t\t/// </summary>\n\t\t/// <param name=\"w\">Window or control.</param>\n\t\t/// <param name=\"r\">Rectangle in <i>w</i> client area coordinates. If <c>null</c>, uses <c>w.ClientRect</c>.</param>\n\t\t/// <exception cref=\"AuWndException\">Invalid <i>w</i>.</exception>\n\t\t/// <exception cref=\"ArgumentException\">The rectangle is empty or does not intersect with the window's client area.</exception>\n\t\t/// <exception cref=\"AuException\">Failed. For example there is not enough memory for bitmap of this size (<c>width*height*4</c> bytes).</exception>\n\t\t/// <remarks>\n\t\t/// If <i>flags</i> contains <c>WindowDC</c> (default) or <c>PrintWindow</c>:\n\t\t/// - If the window is partially or completely transparent, captures its non-transparent view.\n\t\t/// - If the window is DPI-scaled, captures its non-scaled view. However <i>r</i> must contain scaled coordinates.\n\t\t/// </remarks>\n\t\tpublic static Bitmap Image(wnd w, RECT? r = null, CIFlags flags = CIFlags.WindowDC) {\n\t\t\tusing var c = new CaptureScreenImage();\n\t\t\tif (!c.Capture(w, r, flags)) return null;\n\t\t\treturn c.ToBitmap();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets pixel colors from a rectangle in screen.\n\t\t/// </summary>\n\t\t/// <returns>2-dimensional array <c>[row, column]</c> containing pixel colors in <c>0xAARRGGBB</c> format. Alpha <c>0xFF</c>.</returns>\n\t\t/// <param name=\"r\">Rectangle in screen.</param>\n\t\t/// <exception cref=\"ArgumentException\">Empty rectangle.</exception>\n\t\t/// <exception cref=\"AuException\">Failed. Probably there is not enough memory for bitmap of this size (<c>width*height*4</c> bytes).</exception>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// print.clear();\n\t\t/// var a = CaptureScreen.Pixels(new(100, 100, 4, 10));\n\t\t/// for(int i = 0, nRows = a.GetLength(0); i < nRows; i++) print.it(a[i,0], a[i,1], a[i,2], a[i,3]);\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic static uint[,] Pixels(RECT r) {\n\t\t\tusing var c = new CaptureScreenImage();\n\t\t\tc.Capture(r);\n\t\t\treturn c.ToArray2D();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets pixel colors from a rectangle in window client area.\n\t\t/// </summary>\n\t\t/// <returns>2-dimensional array <c>[row, column]</c> containing pixel colors in <c>0xAARRGGBB</c> format. Alpha <c>0xFF</c>.</returns>\n\t\t/// <inheritdoc cref=\"Image(wnd, RECT?, CIFlags)\"/>\n\t\tpublic static uint[,] Pixels(wnd w, RECT? r = null, CIFlags flags = CIFlags.WindowDC) {\n\t\t\tusing var c = new CaptureScreenImage();\n\t\t\tif (!c.Capture(w, r, flags)) return null;\n\t\t\treturn c.ToArray2D();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets color of a screen pixel.\n\t\t/// </summary>\n\t\t/// <param name=\"p\">x y in screen.</param>\n\t\t/// <returns>Pixel color in <c>0xAARRGGBB</c> format. Alpha 0xFF. Returns 0 if fails, eg if x y is not in screen.</returns>\n\t\tpublic static unsafe uint Pixel(POINT p) {\n\t\t\tusing var dc = new ScreenDC_();\n\t\t\tuint R = Api.GetPixel(dc, p.x, p.y);\n\t\t\tif (R == 0xFFFFFFFF) return 0;\n\t\t\treturn ColorInt.SwapRB(R) | 0xFF000000;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets color of a window pixel.\n\t\t/// </summary>\n\t\t/// <param name=\"p\">x y in <i>w</i> client area.</param>\n\t\t/// <returns>Pixel color in <c>0xAARRGGBB</c> format. Alpha 0xFF.</returns>\n\t\t/// <inheritdoc cref=\"Image(wnd, RECT?, CIFlags)\"/>\n\t\tpublic static unsafe uint Pixel(wnd w, POINT p, CIFlags flags = CIFlags.WindowDC) {\n\t\t\tusing var c = new CaptureScreenImage();\n\t\t\tif (!c.Capture(w, new(p.x, p.y, 1, 1), flags)) return 0;\n\t\t\treturn c.Pixels[0];\n\t\t}\n\n\t\t#endregion\n\n\t\t#region capture image UI\n\n\t\t/// <summary>\n\t\t/// UI for capturing an image, color or rectangle on screen.\n\t\t/// </summary>\n\t\t/// <returns><c>false</c> if canceled.</returns>\n\t\t/// <param name=\"result\">Receives results.</param>\n\t\t/// <param name=\"flags\"></param>\n\t\t/// <param name=\"owner\">A window to minimize temporarily.</param>\n\t\t/// <param name=\"wCapture\">A window to capture immediately instead of waiting for <c>F3</c> key. Used only with a \"get window pixels\" flag.</param>\n\t\t/// <remarks>\n\t\t/// Gets all screen pixels and shows in a full-screen topmost window, where the user can select an area.\n\t\t/// \n\t\t/// Cannot capture windows that are always on top of normal topmost windows: 1. Start menu. 2. Topmost windows of UAC uiAccess processes (rare).\n\t\t/// </remarks>\n\t\tpublic static bool ImageColorRectUI(out CIUResult result, CIUFlags flags = 0, AnyWnd owner = default, wnd wCapture = default) {\n\t\t\tresult = default;\n\n\t\t\tswitch (flags & (CIUFlags.Image | CIUFlags.Color | CIUFlags.Rectangle)) {\n\t\t\tcase 0 or CIUFlags.Image or CIUFlags.Color or CIUFlags.Rectangle: break;\n\t\t\tdefault: throw new ArgumentException();\n\t\t\t}\n\n\t\t\tList<wnd> amw = new();\n\t\t\ttry {\n\t\t\t\tif (!owner.IsEmpty) {\n\t\t\t\t\tif (wCapture.Is0) {\n\t\t\t\t\t\tusing (new inputBlocker(BIEvents.MouseClicks)) {\n\t\t\t\t\t\t\tvar w = owner.Hwnd.Get.RootOwnerOrThis();\n\t\t\t\t\t\t\tif (!w.Is0) {\n\t\t\t\t\t\t\t\tw.ShowMinimized(1);\n\t\t\t\t\t\t\t\tamw.Add(w);\n\n\t\t\t\t\t\t\t\t//also minimize editor etc if need\n\t\t\t\t\t\t\t\tfor (int i = 0; i < 7; i++) {\n\t\t\t\t\t\t\t\t\twait.doEvents(10);\n\t\t\t\t\t\t\t\t\tw = wnd.active;\n\t\t\t\t\t\t\t\t\tif (!w.IsOfThisProcess || w.IsMinimized) break;\n\t\t\t\t\t\t\t\t\tw = w.Get.RootOwnerOrThis();\n\t\t\t\t\t\t\t\t\tw.ShowMinimized(1);\n\t\t\t\t\t\t\t\t\tamw.Add(w);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\twait.doEvents(300); //time for animations\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tvar w = owner.Hwnd;\n\t\t\t\t\t\tif (w.IsMinimized) amw.Add(w);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tbool windowPixels = flags.HasAny(CIUFlags.WindowDC | CIUFlags.PrintWindow);\n\t\t\t\tg1:\n\t\t\t\tRECT rs = screen.virtualScreen;\n\t\t\t\t//RECT rs = screen.primary.Rect; //for testing, to see print output in other screen\n\t\t\t\tBitmap bs;\n\t\t\t\twnd wTL = default;\n\t\t\t\tRECT rc = default;\n\t\t\t\tSIZE size = default;\n\t\t\t\tvar avw = windowPixels ? null : wnd.getwnd.allWindows(onlyVisible: true)\n\t\t\t\t\t.Where(o => !(o.IsMinimized || o.IsCloaked))\n\t\t\t\t\t.Select(o => (w: o, r: o.ClientRectInScreen)).ToArray();\n\n\t\t\t\tif (windowPixels) {\n\t\t\t\t\tif (!wCapture.Is0) {\n\t\t\t\t\t\twTL = wCapture;\n\t\t\t\t\t\twCapture = default;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (!_WaitForHotkey(\"Press F3 to select window from mouse pointer. Or Esc.\")) return false;\n\t\t\t\t\t\twTL = wnd.fromMouse(WXYFlags.NeedWindow);\n\t\t\t\t\t}\n\t\t\t\t\trc = wTL.ClientRect;\n\t\t\t\t\tusing var bw = Image(wTL, rc, flags.ToCIFlags_());\n\t\t\t\t\tbs = new Bitmap(rs.Width, rs.Height);\n\t\t\t\t\tusing var g = Graphics.FromImage(bs);\n\t\t\t\t\tg.Clear(Color.Gray);\n\t\t\t\t\twTL.MapClientToScreen(ref rc);\n\t\t\t\t\tg.DrawImage(bw, rc.left - rs.left, rc.top - rs.top);\n\t\t\t\t\tsize = bw.Size;\n\t\t\t\t} else {\n\t\t\t\t\tbs = Image(rs);\n\t\t\t\t}\n\n\t\t\t\tvar wui = new _ImageUIWindow();\n\t\t\t\tswitch (wui.Show(bs, flags, rs)) {\n\t\t\t\tcase 1: break;\n\t\t\t\tcase 2:\n\t\t\t\t\tif (!windowPixels && !_WaitForHotkey(\"Press F3 when ready for new screenshot. Or Esc.\")) return false;\n\t\t\t\t\tgoto g1;\n\t\t\t\tdefault: return false;\n\t\t\t\t}\n\n\t\t\t\tvar r = wui.Result;\n\n\t\t\t\t//if the window is DPI-scaled, scale r.rect\n\t\t\t\tif (windowPixels && (size.width != rc.Width || size.height != rc.Height) && Dpi.AwarenessContext.Available) { //Win10+, to match the unscaling code which can't support older OS\n\t\t\t\t\tint dpiw = Dpi.OfWindow(wTL), dpis = screen.of(wTL).Dpi;\n\t\t\t\t\tvar rr = r.rect;\n\t\t\t\t\tr.rect = RECT.FromLTRB(\n\t\t\t\t\t\trc.left + Math2.MulDiv(rr.left - rc.left, dpis, dpiw),\n\t\t\t\t\t\trc.top + Math2.MulDiv(rr.top - rc.top, dpis, dpiw),\n\t\t\t\t\t\trc.left + Math2.MulDiv(rr.right - rc.left, dpis, dpiw),\n\t\t\t\t\t\trc.top + Math2.MulDiv(rr.bottom - rc.top, dpis, dpiw)\n\t\t\t\t\t\t);\n\t\t\t\t\tr.dpiScale = (double)dpis / dpiw;\n\t\t\t\t} else r.dpiScale = 1;\n\n\t\t\t\tr.w = _WindowFromRect(r, wTL);\n\n\t\t\t\t//if that window is eg a menu, it probably disappeared. We get correct screenshot, but wrong window.\n\t\t\t\t//\tWorkaround: try to detect it and set r.possiblyWrongWindow.\n\t\t\t\t//\tThen the 'find image' tool will prompt the user to capture the window with the 'find window' tool.\n\t\t\t\tif (windowPixels) r.possiblyWrongWindow = r.w.Window != wTL;\n\t\t\t\telse if (!r.w.Is0) {\n\t\t\t\t\tvar w1 = avw.FirstOrDefault(o => o.r.Contains(r.rect)).w;\n\t\t\t\t\tif (w1 != r.w.Window) r.possiblyWrongWindow = !w1.IsVisible || w1.IsCloaked || w1.IsMinimized;\n\t\t\t\t}\n\n\t\t\t\tresult = r;\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tif (amw.Count > 0) {\n\t\t\t\t\tfor (int i = amw.Count; --i >= 0;) try { amw[i].ShowNotMinimized(); } catch { }\n\t\t\t\t\tamw[0].ActivateL();\n\t\t\t\t}\n\t\t\t\tApi.GetKeyState(1); //let OS update key states. Else the API later may not work eg in wndproc on some messages.\n\t\t\t}\n\t\t\treturn true;\n\n\t\t\tstatic wnd _WindowFromRect(CIUResult r, wnd wTL) {\n\t\t\t\t//after closing our window, may need several ms until OS sets correct Z order. Until that may get different w1 and w2.\n\t\t\t\tThread.Sleep(25);\n\n\t\t\t\twnd w1, w2;\n\t\t\t\tvar r1 = r.rect;\n\t\t\t\tif (!wTL.Is0 && wTL.MapScreenToClient(ref r1) && wTL.ClientRect.Contains(r1)) {\n\t\t\t\t\tw1 = wTL.ChildFromXY((r1.left, r1.top), WXYCFlags.OrThis);\n\t\t\t\t\tw2 = (r.image == null) ? w1 : wTL.ChildFromXY((r1.right - 1, r1.bottom - 1), WXYCFlags.OrThis);\n\t\t\t\t} else {\n\t\t\t\t\tw1 = wnd.fromXY((r.rect.left, r.rect.top));\n\t\t\t\t\tw2 = (r.image == null) ? w1 : wnd.fromXY((r.rect.right - 1, r.rect.bottom - 1));\n\t\t\t\t}\n\n\t\t\t\tif (w2 != w1 || !_IsInClientArea(w1)) {\n\t\t\t\t\twnd w3 = w1.Window, w4 = w2.Window;\n\t\t\t\t\tw1 = (w4 == w3 && _IsInClientArea(w3)) ? w3 : default;\n\t\t\t\t}\n\t\t\t\treturn w1;\n\n\t\t\t\tbool _IsInClientArea(wnd w) => w.GetClientRect(out var rc, true) && rc.Contains(r.rect);\n\t\t\t}\n\n\t\t\tstatic bool _WaitForHotkey(string info) {\n\t\t\t\tusing (osdText.showText(info, Timeout.Infinite)) {\n\t\t\t\t\t//try { keys.waitForHotkey(0, KKey.F3); }\n\t\t\t\t\t//catch(AuException) { dialog.showError(\"Failed to register hotkey F3\"); return false; }\n\n\t\t\t\t\treturn KKey.F3 == keys.waitForKeys(0, k => !k.IsUp && k.Key is KKey.F3 or KKey.Escape, block: true);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tclass _ImageUIWindow {\n\t\t\twnd _w;\n\t\t\tBitmap _img;\n\t\t\tbool _paintedOnce;\n\t\t\tbool _magnMoved;\n\t\t\tbool _capturing;\n\t\t\tCIUFlags _flags;\n\t\t\tMouseCursor _cursor;\n\t\t\tSIZE _textSize;\n\t\t\tint _dpi;\n\t\t\tint _res;\n\n\t\t\tpublic CIUResult Result;\n\n\t\t\t/// <returns>0 <b>Cancel</b>, 1 <b>OK</b>, 2 <b>Retry</b>.</returns>\n\t\t\tpublic int Show(Bitmap img, CIUFlags flags, RECT r) {\n\t\t\t\t_img = img;\n\t\t\t\t_flags = flags;\n\t\t\t\t//TODO3: cursor almost invisible on my 200% DPI tablet (somehow transparent). Test on true 200% DPI screen.\n\t\t\t\t_cursor = MouseCursor.Load(ResourceUtil.GetBytes(\"<Au>resources/red_cross_cursor.cur\"), 32);\n\t\t\t\t_dpi = screen.primary.Dpi;\n\t\t\t\t_w = WndUtil.CreateWindow(_WndProc, true, WndUtil.WindowClassDWP_, \"Au.CaptureScreen\", WS.POPUP | WS.VISIBLE, WSE.TOOLWINDOW | WSE.TOPMOST, r.left, r.top, r.Width, r.Height);\n\t\t\t\t_w.ActivateL();\n\n\t\t\t\ttry {\n\t\t\t\t\twhile (Api.GetMessage(out var m) && m.message != Api.WM_APP) {\n\t\t\t\t\t\tswitch (m.message) {\n\t\t\t\t\t\tcase Api.WM_KEYDOWN when !_capturing:\n\t\t\t\t\t\t\tswitch ((KKey)(int)m.wParam) {\n\t\t\t\t\t\t\tcase KKey.Escape: return 0;\n\t\t\t\t\t\t\tcase KKey.F3: return 2;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase Api.WM_RBUTTONUP when m.hwnd == _w:\n\t\t\t\t\t\t\tswitch (popupMenu.showSimple(\"1 Retry\\tF3|2 Cancel\\tEsc\", owner: _w)) {\n\t\t\t\t\t\t\tcase 1: return 2;\n\t\t\t\t\t\t\tcase 2: return 0;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tApi.DispatchMessage(m);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfinally {\n\t\t\t\t\tvar w = _w; _w = default;\n\t\t\t\t\tApi.DestroyWindow(w);\n\t\t\t\t}\n\t\t\t\treturn _res;\n\t\t\t}\n\n\t\t\tnint _WndProc(wnd w, int msg, nint wParam, nint lParam) {\n\t\t\t\t//WndUtil.PrintMsg(w, msg, wParam, lParam);\n\n\t\t\t\tswitch (msg) {\n\t\t\t\tcase Api.WM_NCDESTROY:\n\t\t\t\t\t_img.Dispose();\n\t\t\t\t\t_cursor?.Dispose();\n\t\t\t\t\tif (_w != default) {\n\t\t\t\t\t\t_w = default;\n\t\t\t\t\t\t_w.Post(Api.WM_APP);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase Api.WM_SETCURSOR:\n\t\t\t\t\tApi.SetCursor(_cursor.Handle);\n\t\t\t\t\treturn 1;\n\t\t\t\tcase Api.WM_ERASEBKGND:\n\t\t\t\t\treturn default;\n\t\t\t\tcase Api.WM_PAINT:\n\t\t\t\t\tvar dc = Api.BeginPaint(w, out var ps);\n\t\t\t\t\t_WmPaint(dc);\n\t\t\t\t\tApi.EndPaint(w, ps);\n\t\t\t\t\treturn default;\n\t\t\t\tcase Api.WM_MOUSEMOVE:\n\t\t\t\t\t_WmMousemove(Math2.NintToPOINT(lParam));\n\t\t\t\t\tbreak;\n\t\t\t\tcase Api.WM_LBUTTONDOWN:\n\t\t\t\t\t_WmLbuttondown(Math2.NintToPOINT(lParam));\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\treturn Api.DefWindowProc(w, msg, wParam, lParam);\n\t\t\t}\n\n\t\t\tunsafe void _WmPaint(IntPtr dc) {\n#if true\n\t\t\t\tusing var bd = _img.Data(ImageLockMode.ReadOnly);\n\t\t\t\tvar bi = new Api.BITMAPINFO(bd.Width, -bd.Height);\n\t\t\t\tApi.SetDIBitsToDevice(dc, 0, 0, bd.Width, bd.Height, 0, 0, 0, bd.Height, (void*)bd.Scan0, &bi);\n#else //very slow\n\t\t\t\tusing var g = Graphics.FromHdc(dc);\n\t\t\t\tg.DrawImageUnscaled(_img, 0, 0);\n#endif\n\t\t\t\t_paintedOnce = true;\n\t\t\t}\n\n\t\t\tvoid _WmMousemove(POINT pc) {\n\t\t\t\tif (!_paintedOnce) return;\n\n\t\t\t\t//format text to draw below magnifier\n\t\t\t\tstring text;\n\t\t\t\tusing (new StringBuilder_(out var s)) {\n\t\t\t\t\tvar ic = _flags & (CIUFlags.Image | CIUFlags.Color | CIUFlags.Rectangle);\n\t\t\t\t\tif (ic == 0) ic = CIUFlags.Image | CIUFlags.Color;\n\t\t\t\t\tbool canColor = ic.Has(CIUFlags.Color);\n\t\t\t\t\tif (canColor) {\n\t\t\t\t\t\tvar color = _img.GetPixel(pc.x, pc.y).ToArgb() & 0xffffff;\n\t\t\t\t\t\ts.Append(\"Color  #\").Append(color.ToString(\"X6\")).Append('\\n');\n\t\t\t\t\t}\n\t\t\t\t\tif (ic == CIUFlags.Color) {\n\t\t\t\t\t\ts.Append(\"Click to capture color.\\n\");\n\t\t\t\t\t} else if (ic == CIUFlags.Rectangle) {\n\t\t\t\t\t\ts.Append(\"Mouse-drag to capture rectangle.\\n\");\n\t\t\t\t\t} else if (!canColor) {\n\t\t\t\t\t\ts.Append(\"Mouse-drag to capture image.\\n\");\n\t\t\t\t\t} else {\n\t\t\t\t\t\ts.Append(\"Mouse-drag to capture image,\\nor Ctrl+click to capture color.\\n\");\n\t\t\t\t\t}\n\t\t\t\t\ts.Append(\"More:  right-click\"); //\"  cancel:  key Esc\\n  retry:  key F3 ... F3\"\n\t\t\t\t\ttext = s.ToString();\n\t\t\t\t}\n\n\t\t\t\tvar font = NativeFont_.RegularCached(_dpi);\n\t\t\t\tint magnWH = Dpi.Scale(200, _dpi) / 10 * 10; //width and height of the magnified image without borders etc\n\t\t\t\tif (_textSize == default) using (var tr = new FontDC_(font)) _textSize = tr.MeasureDT(text, TFFlags.NOPREFIX);\n\t\t\t\tint width = Math.Max(magnWH, _textSize.width) + 2, height = magnWH + 4 + _textSize.height;\n\t\t\t\tusing var mb = new MemoryBitmap(width, height);\n\t\t\t\tvar dc = mb.Hdc;\n\t\t\t\tusing var wdc = new WindowDC_(_w);\n\n\t\t\t\t//draw frames and color background. Also erase magnifier, need when near screen edges.\n\t\t\t\tApi.FillRect(dc, (0, 0, width, height), Api.GetStockObject(4)); //BLACK_BRUSH\n\n\t\t\t\t//copy from captured screen image to magnifier image. Magnify 5 times.\n\t\t\t\tint k = magnWH / 10;\n\t\t\t\tApi.StretchBlt(dc, 1, 1, magnWH, magnWH, wdc, pc.x - k, pc.y - k, k * 2, k * 2, Api.SRCCOPY);\n\n\t\t\t\t//draw red crosshair\n\t\t\t\tk = magnWH / 2;\n\t\t\t\tusing (var pen = new GdiPen_(0xff)) {\n\t\t\t\t\tpen.DrawLine(dc, (k, 1), (k, magnWH + 1));\n\t\t\t\t\tpen.DrawLine(dc, (1, k), (magnWH + 1, k));\n\t\t\t\t}\n\n\t\t\t\t//draw text below magnifier\n\t\t\t\tvar rc = new RECT(1, magnWH + 2, _textSize.width, _textSize.height);\n\t\t\t\tApi.SetTextColor(dc, 0x32CD9A); //Color.YellowGreen\n\t\t\t\tApi.SetBkMode(dc, 1);\n\t\t\t\tvar oldFont = Api.SelectObject(dc, font);\n\t\t\t\tApi.DrawText(dc, text, ref rc, TFFlags.NOPREFIX);\n\t\t\t\tApi.SelectObject(dc, oldFont);\n\n\t\t\t\t//set magninifier position far from cursor\n\t\t\t\tvar pm = new POINT(4, 4); _w.MapScreenToClient(ref pm);\n\t\t\t\tint xMove = magnWH * 3;\n\t\t\t\tif (_magnMoved) pm.Offset(xMove, 0);\n\t\t\t\tvar rm = new RECT(pm.x, pm.y, width, height); rm.Inflate(magnWH / 2, magnWH / 2);\n\t\t\t\tif (rm.Contains(pc)) {\n\t\t\t\t\tApi.InvalidateRect(_w, (pm.x, pm.y, width, height));\n\t\t\t\t\t_magnMoved ^= true;\n\t\t\t\t\tpm.Offset(_magnMoved ? xMove : -xMove, 0);\n\t\t\t\t}\n\n\t\t\t\tApi.BitBlt(wdc, pm.x, pm.y, width, height, dc, 0, 0, Api.SRCCOPY);\n\t\t\t}\n\n\t\t\tvoid _WmLbuttondown(POINT p0) {\n\t\t\t\tif (Result != null) return;\n\n\t\t\t\t//bool isAnyShape = false; //rejected. Not useful.\n\t\t\t\tbool isColor = false;\n\t\t\t\tvar ic = _flags & (CIUFlags.Image | CIUFlags.Color | CIUFlags.Rectangle);\n\t\t\t\tif (ic == CIUFlags.Color) {\n\t\t\t\t\tisColor = true;\n\t\t\t\t} else {\n\t\t\t\t\tvar mod = keys.gui.getMod();\n\t\t\t\t\tif (mod != 0 && ic == CIUFlags.Rectangle) return;\n\t\t\t\t\tswitch (mod) {\n\t\t\t\t\tcase 0: break;\n\t\t\t\t\tcase KMod.Ctrl when ic == 0: isColor = true; break;\n\t\t\t\t\tdefault: return;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tResult = new CIUResult();\n\t\t\t\tvar r = new RECT(p0.x, p0.y, 0, 0);\n\t\t\t\tif (isColor) {\n\t\t\t\t\tResult.color = (uint)_img.GetPixel(p0.x, p0.y).ToArgb();\n\t\t\t\t\tr.right++; r.bottom++;\n\t\t\t\t} else {\n\t\t\t\t\tvar pen = Pens.Red;\n\t\t\t\t\tbool notFirstMove = false;\n\t\t\t\t\t_capturing = true;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (!WndUtil.DragLoop(_w, MButtons.Left, m => {\n\t\t\t\t\t\t\tif (m.msg.message != Api.WM_MOUSEMOVE) return;\n\t\t\t\t\t\t\tPOINT p = m.msg.pt; _w.MapScreenToClient(ref p);\n\t\t\t\t\t\t\tusing var g = Graphics.FromHwnd(_w.Handle);\n\t\t\t\t\t\t\tif (notFirstMove) { //erase prev rect\n\t\t\t\t\t\t\t\tr.right++; r.bottom++;\n\t\t\t\t\t\t\t\tg.DrawImage(_img, r, r, GraphicsUnit.Pixel);\n\t\t\t\t\t\t\t\t//FUTURE: prevent flickering. Also don't draw under magnifier.\n\t\t\t\t\t\t\t} else notFirstMove = true;\n\t\t\t\t\t\t\tr = RECT.FromLTRB(p0.x, p0.y, p.x, p.y);\n\t\t\t\t\t\t\tr.Normalize(true);\n\t\t\t\t\t\t\tg.DrawRectangle(pen, r);\n\t\t\t\t\t\t})) { //Esc key etc\n\t\t\t\t\t\t\tApi.InvalidateRect(_w);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tfinally { _capturing = false; }\n\n\t\t\t\t\tr.right++; r.bottom++;\n\t\t\t\t\tif (r.NoArea) {\n\t\t\t\t\t\tApi.DestroyWindow(_w);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (ic != CIUFlags.Rectangle) {\n\t\t\t\t\t\tResult.image = _img.Clone(r, PixelFormat.Format32bppArgb);\n\t\t\t\t\t}\n\n\t\t\t\t}\n\t\t\t\t_w.MapClientToScreen(ref r);\n\t\t\t\tResult.rect = r;\n\n\t\t\t\tif (isColor) { //bad things may happen if this window closed while the mouse button or Ctrl pressed\n\t\t\t\t\tfor (int i = 200; --i >= 0 && (Api.GetKeyState(1) < 0 || Api.GetKeyState(17) < 0);) wait.doEvents(15);\n\t\t\t\t}\n\n\t\t\t\t_res = 1;\n\t\t\t\tApi.DestroyWindow(_w);\n\t\t\t}\n\t\t}\n\n\t\t#endregion\n\n\t\t#region other\n\n\t\t/// <summary>\n\t\t/// UI for capturing a rectangle, point or/and window on screen with <c>Shift</c> key.\n\t\t/// </summary>\n\t\t/// <returns><c>true</c> if captured, <c>false</c> if pressed <c>Esc</c>.</returns>\n\t\t/// <param name=\"result\"></param>\n\t\t/// <param name=\"type\"></param>\n\t\t/// <param name=\"rectInClient\">Get rectangle in window client area.</param>\n\t\t/// <param name=\"wxyFlags\"></param>\n\t\tpublic static unsafe bool RectPointWindowUI(out CRUResult result, CRUType type, bool rectInClient = false, WXYFlags wxyFlags = 0) {\n\t\t\tresult = default;\n\t\t\tvar wxyFlags2 = wxyFlags & (WXYFlags.NeedWindow | WXYFlags.NeedControl);\n\n\t\t\tvar s = type switch {\n\t\t\t\tCRUType.Window => \"Press Shift to capture %\",\n\t\t\t\tCRUType.Rect => \"Shift+mouse move to capture rectangle on screen.\",\n\t\t\t\tCRUType.Point => \"Press Shift to capture mouse coordinates.\",\n\t\t\t\tCRUType.WindowAndPoint => \"Press Shift to capture mouse coordinates in a %.\",\n\t\t\t\tCRUType.WindowAndRect => \"Shift+mouse move to capture rectangle in a %.\",\n\t\t\t\t_ => \"Press Shift to capture %.\\nOr Shift+mouse move to capture rectangle in a %.\"\n\t\t\t};\n\t\t\ts = s.Replace(\"%\", wxyFlags2 switch { WXYFlags.NeedWindow => \"window\", WXYFlags.NeedControl => \"control\", _ => \"window or control\" });\n\t\t\ts += \"\\nOr press Esc to cancel.\";\n\t\t\tusing var osd = osdText.showText(s, -1);\n\n\t\t\twnd w = default, wClip = default;\n\t\t\tbool needWindow = type is not (CRUType.Rect or CRUType.Point);\n\t\t\tif (needWindow) { //draw black rectangle around window or control from mouse\n\t\t\t\tusing var osrw = new osdRect { };\n\t\t\t\tosrw.Show();\n\t\t\t\tfor (; ; ) {\n\t\t\t\t\twait.doEvents(12);\n\t\t\t\t\tif (keys.isPressed(KKey.Escape)) return false;\n\t\t\t\t\tw = wnd.fromMouse(wxyFlags);\n\t\t\t\t\tosrw.Rect = w.Rect;\n\t\t\t\t\tif (keys.isShift) break;\n\t\t\t\t}\n\t\t\t\tif (w == default) return false;\n\t\t\t} else {\n\t\t\t\tfor (; ; ) {\n\t\t\t\t\twait.doEvents(12);\n\t\t\t\t\tif (keys.isPressed(KKey.Escape)) return false;\n\t\t\t\t\tif (keys.isShift) break;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tvar p = mouse.xy;\n\t\t\tRECT r = new(p.x, p.y, 0, 0);\n\n\t\t\tif (type is not (CRUType.Window or CRUType.Point or CRUType.WindowAndPoint)) { //draw red rectangle\n\t\t\t\tif (needWindow) {\n\t\t\t\t\twClip = wxyFlags2 == WXYFlags.NeedControl ? w : w.Window;\n\t\t\t\t\tvar rw = rectInClient ? wClip.ClientRectInScreen : wClip.Rect;\n\t\t\t\t\tApi.ClipCursor(&rw);\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tusing var osrr = new osdRect { Color = 0xff0000, Thickness = 1, Rect = r };\n\t\t\t\t\tosrr.Show();\n\t\t\t\t\tfor (var r1 = r; ;) {\n\t\t\t\t\t\twait.doEvents(12);\n\t\t\t\t\t\tif (keys.isPressed(KKey.Escape)) return false;\n\t\t\t\t\t\tif (!keys.isShift) break;\n\t\t\t\t\t\tp = mouse.xy;\n\t\t\t\t\t\tr1.right = p.x; r1.bottom = p.y;\n\t\t\t\t\t\tr = r1; r.Normalize(true);\n\t\t\t\t\t\tosrr.Rect = r;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfinally { if (needWindow) Api.ClipCursor(null); }\n\n\t\t\t\tif (needWindow && w != wClip) { //if rect spans multiple controls, get top-level window\n\t\t\t\t\tvar w2 = wnd.fromMouse(wxyFlags);\n\t\t\t\t\tif (w2 != w) w = wClip;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (needWindow && rectInClient) w.MapScreenToClient(ref r);\n\n\t\t\tresult = new(w, !r.NoArea, r);\n\t\t\treturn true;\n\t\t}\n\n\t\t#endregion\n\t}\n\n\t/// <summary>\n\t/// Captures image pixels from screen or window.\n\t/// </summary>\n\t/// <remarks>\n\t/// This class is used by <see cref=\"CaptureScreen\"/>, <see cref=\"uiimage\"/> and <see cref=\"uiimageFinder\"/>. Also you can use it directly. For example it can get pixels directly without copying to a <see cref=\"Bitmap\"/> or array.\n\t/// \n\t/// How to use:\n\t/// 1. Create variable.\n\t/// 2. Call <see cref=\"Capture\"/>.\n\t/// 3. Call other functions to get result in various formats.\n\t/// 4. If need, repeat 2-3 (for example when waiting for image).\n\t/// 5. Dispose (important).\n\t/// \n\t/// Pixel format: <c>Format32bppArgb</c>, alpha 0xff.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// var w = wnd.find(\"LibreAutomate\").Child(\"document\");\n\t/// \n\t/// using var c = new CaptureScreenImage();\n\t/// if (!c.Capture(w, new(0, 0, 200, 200))) return;\n\t/// using var b = c.ToBitmap();\n\t/// \n\t/// var f = folders.Temp + \"test.png\"; b.Save(f); run.it(f);\n\t/// ]]></code>\n\t/// </example>\n\tpublic unsafe sealed class CaptureScreenImage : IDisposable {\n\t\tMemoryBitmap _mb;\n\t\tuint* _pixels;\n\t\tint _width, _height;\n\t\tint _dibWidth, _dibHeight;\n\t\tbool _alphaOk;\n\t\t//_DwmThumbnail _dwm;\n\n\t\t/// <summary>\n\t\t/// Frees image memory.\n\t\t/// </summary>\n\t\tpublic void Dispose() {\n\t\t\t_mb?.Dispose(); _mb = null;\n\t\t\t_pixels = null;\n\t\t\t_width = _height = 0;\n\t\t\t//_dwm?.Dispose(); _dwm = null;\n\t\t}\n\n\t\tinternal void SetExternalData_(uint* pixels, int width, int height) {\n\t\t\tDebug.Assert(_mb == null);\n\t\t\t_pixels = pixels;\n\t\t\t_width = width;\n\t\t\t_height = height;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Let <c>Capture</c> don't set alpha = 0xff. Slightly faster. Then pixelformat will be Rgb instead of Argb.\n\t\t/// Default <c>false</c>.\n\t\t/// </summary>\n\t\tinternal bool DontSetAlpha_ { get; set; }\n\n\t\t/// <summary>\n\t\t/// Captures image from window client area into memory stored in this variable.\n\t\t/// </summary>\n\t\t/// <param name=\"w\">Window or control.</param>\n\t\t/// <param name=\"r\">Rectangle in <i>w</i> client area coordinates. If <c>null</c>, uses <c>w.ClientRect</c>.</param>\n\t\t/// <returns><c>false</c> if <i>r</i> empty or not in the client area and used flag <c>Relaxed</c> (else exception).</returns>\n\t\tpublic bool Capture(wnd w, RECT? r = null, CIFlags flags = CIFlags.WindowDC) => _Capture(w.ThrowIf0(), r, flags);\n\n\t\t/// <summary>\n\t\t/// Captures image from screen into memory stored in this variable.\n\t\t/// </summary>\n\t\t/// <param name=\"relaxed\">If <i>r</i> empty, return <c>false</c> instead of exception.</param>\n\t\t/// <returns><c>false</c> if <i>r</i> empty and <i>relaxed</i> <c>true</c> (else exception).</returns>\n\t\tpublic bool Capture(RECT r, bool relaxed = false) => _Capture(default, r, flags: relaxed ? CIFlags.Relaxed : 0);\n\n\t\tbool _Capture(wnd w, RECT? rect, CIFlags flags) {\n\t\t\tconst CIFlags c_howMask = CIFlags.WindowDC | CIFlags.PrintWindow /*| CIFlags.WindowDwm*/;\n\t\t\tif ((flags & c_howMask) is not (0 or CIFlags.WindowDC or CIFlags.PrintWindow /*or CIFlags.WindowDwm*/)) throw new ArgumentException();\n\t\t\tbool fromWindow = flags.HasAny(c_howMask), printWindow = flags.Has(CIFlags.PrintWindow);\n\t\t\tRECT r = rect ?? default, rc = default;\n\t\t\tif (!w.Is0) {\n\t\t\t\t//if (flags.Has(CIFlags.WindowDwm)) { //w must be top-level window\n\t\t\t\t//\tvar ww = w.Window;\n\t\t\t\t//\tif (ww != w) {\n\t\t\t\t//\t\tww.ThrowIf0();\n\t\t\t\t//\t\tif (rect == null) r = w.ClientRect;\n\t\t\t\t//\t\tw.MapClientToClientOf(ww, ref r);\n\t\t\t\t//\t\trect = r;\n\t\t\t\t//\t\tw = ww;\n\t\t\t\t//\t}\n\t\t\t\t//}\n\n\t\t\t\tvar rc2 = w.ClientRect;\n\t\t\t\tbool dpiScaled = fromWindow && Dpi.IsWindowVirtualizedWin10_(w);\n\t\t\t\tusing var dac = dpiScaled ? new Dpi.AwarenessContext(w) : default;\n\t\t\t\tif (!w.GetClientRect(out rc)) w.ThrowUseNative();\n\t\t\t\tif (rect == null || r == rc2) {\n\t\t\t\t\tr = rc;\n\t\t\t\t} else {\n\t\t\t\t\tif (dpiScaled) { //unscale r\n\t\t\t\t\t\tdac.Dispose();\n\t\t\t\t\t\tvar ww = w.Window;\n\t\t\t\t\t\tint dpiw = Dpi.OfWindow(ww), dpis = screen.of(ww).Dpi;\n\t\t\t\t\t\tr = RECT.FromLTRB(Math2.MulDiv(r.left, dpiw, dpis), Math2.MulDiv(r.top, dpiw, dpis), Math2.MulDiv(r.right, dpiw, dpis), Math2.MulDiv(r.bottom, dpiw, dpis));\n\t\t\t\t\t}\n\t\t\t\t\tif (!r.Intersect(rc)) return flags.Has(CIFlags.Relaxed) ? false : throw new ArgumentException(\"rectangle not in window\");\n\t\t\t\t}\n\n\t\t\t\t//if (flags.Has(CIFlags.WindowDwm)) {\n\t\t\t\t//\t_dwm ??= new();\n\t\t\t\t//\tif (!_dwm.Init(w, r, dpiScaled)) return flags.Has(CIFlags.Relaxed) ? false : throw new ArgumentException(\"rectangle not in window\");\n\t\t\t\t//\tw = _dwm.WndThumbnail;\n\t\t\t\t//}\n\t\t\t}\n\t\t\tif (r.NoArea) return flags.Has(CIFlags.Relaxed) ? false : throw new ArgumentException(\"empty rectangle\");\n\n\t\t\tint dibWidth = printWindow ? rc.Width : r.Width, dibHeight = printWindow ? rc.Height : r.Height;\n\t\t\tif (_mb == null || dibWidth != _dibWidth || dibHeight != _dibHeight) {\n\t\t\t\tvar bi = new Api.BITMAPINFO(dibWidth, -dibHeight);\n\t\t\t\tvar dib = Api.CreateDIBSection(default, bi, 0, out var pixels);\n\t\t\t\tif (dib == default) throw new AuException(\"*create memory bitmap of specified size\");\n\t\t\t\t_pixels = pixels;\n\t\t\t\t_mb ??= new MemoryBitmap();\n\t\t\t\t_mb.Attach(dib); //and deletes old bitmap\n\t\t\t\t_dibWidth = dibWidth;\n\t\t\t\t_dibHeight = dibHeight;\n\t\t\t}\n\t\t\t_width = r.Width;\n\t\t\t_height = r.Height;\n\n\t\t\tif (printWindow) { //must capture entire client area\n\t\t\t\tvar pw = Api.PW_CLIENTONLY;\n\t\t\t\tif (osVersion.minWin8_1) pw |= Api.PW_RENDERFULLCONTENT;\n\t\t\t\t//PW_RENDERFULLCONTENT is new in Win8.1. Undocumented in MSDN, but defined in h. Then works with windows like Chrome, winstore.\n\t\t\t\t//\tBug: from some controls randomly gets partially painted image. Eg classic toolbar, treeview.\n\t\t\t\t//\tRejected: if PrintClient|WindowDC, capture without PW_RENDERFULLCONTENT. Makes no sense.\n\n\t\t\t\tif (!Api.PrintWindow(w, _mb.Hdc, pw)) w.ThrowNoNative(\"*get pixels\");\n\t\t\t\tif (r.left != 0 || r.top != 0 || _width != dibWidth) { //move pixels to the start of the bitmap memory\n\t\t\t\t\tfor (int y = r.top; y < r.bottom; y++) {\n\t\t\t\t\t\tvar spanFrom = new Span<uint>(_pixels + y * dibWidth + r.left, _width);\n\t\t\t\t\t\tvar spanTo = new Span<uint>(_pixels + (y - r.top) * _width, _width);\n\t\t\t\t\t\tspanFrom.CopyTo(spanTo);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t//} else if (flags.Has(CIFlags.WindowDwm)) {\n\t\t\t\t//\tif (!Api.PrintWindow(w, _mb.Hdc, Api.PW_CLIENTONLY | Api.PW_RENDERFULLCONTENT)) w.ThrowNoNative(\"*get pixels\");\n\t\t\t\t//\t_alphaOk = true;\n\t\t\t\t//\treturn true;\n\t\t\t} else {\n\t\t\t\tif (!w.Is0 && !fromWindow) {\n\t\t\t\t\tw.MapClientToScreen(ref r);\n\t\t\t\t\tw = default;\n\t\t\t\t}\n\t\t\t\tusing var dc = new WindowDC_(w);\n\t\t\t\tif (dc.Is0) w.ThrowNoNative(\"*get pixels\");\n\t\t\t\tuint rop = !w.Is0 ? Api.SRCCOPY : Api.SRCCOPY | Api.CAPTUREBLT;\n\t\t\t\tApi.BitBlt(_mb.Hdc, 0, 0, _width, _height, dc, r.left, r.top, rop); //fails only if a HDC is invalid\n\t\t\t}\n\n\t\t\tif (_alphaOk = !DontSetAlpha_) {\n\t\t\t\tbyte* p = (byte*)_pixels, pe = p + _width * _height * 4;\n\t\t\t\tfor (p += 3; p < pe; p += 4) *p = 0xff;\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Width of the captured image.\n\t\t/// </summary>\n\t\tpublic int Width => _width;\n\n\t\t/// <summary>\n\t\t/// Height of the captured image.\n\t\t/// </summary>\n\t\tpublic int Height => _height;\n\n\t\t/// <summary>\n\t\t/// Pixels of the captured image.\n\t\t/// </summary>\n\t\tpublic uint* Pixels => _pixels;\n\n\t\t/// <summary>\n\t\t/// Copies pixels of the captured image to new 1D array.\n\t\t/// </summary>\n\t\t[SkipLocalsInit]\n\t\tpublic uint[] ToArray1D() {\n\t\t\tvar a = GC.AllocateUninitializedArray<uint>(_height * _width);\n\t\t\tfixed (uint* p = a) { MemoryUtil.Copy(_pixels, p, _width * _height * 4); }\n\t\t\treturn a;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Copies pixels of the captured image to new 2D array <c>[row, column]</c>.\n\t\t/// </summary>\n\t\tpublic uint[,] ToArray2D() {\n\t\t\tvar a = new uint[_height, _width];\n\t\t\tfixed (uint* p = a) { MemoryUtil.Copy(_pixels, p, _width * _height * 4); }\n\t\t\treturn a;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Creates new <c>Bitmap</c> from pixels of the captured image.\n\t\t/// </summary>\n\t\tpublic Bitmap ToBitmap() {\n\t\t\tvar b = new Bitmap(_width, _height, _alphaOk ? PixelFormat.Format32bppArgb : PixelFormat.Format32bppRgb);\n\t\t\tGC_.AddObjectMemoryPressure(b, _width * _height * 4);\n\t\t\tusing var d = b.Data(new(0, 0, _width, _height), ImageLockMode.ReadWrite);\n\t\t\tMemoryUtil.Copy(_pixels, (uint*)d.Scan0, _width * _height * 4);\n\t\t\treturn b;\n\t\t}\n\n\t\t//rejected. Unreliable. Not all windows that require PW_RENDERFULLCONTENT have WS_EX_NOREDIRECTIONBITMAP. Eg Chrome on Win8.1.\n\t\t//static uint _GetPrintWindowFlags(CIFlags flags, wnd w, RECT r) {\n\t\t//\tif (!flags.Has(CIFlags.PrintWindow)) return 0;\n\t\t//\tvar f = Api.PW_CLIENTONLY;\n\t\t//\tif (osVersion.minWin8_1) {\n\t\t//\t\tvar wtl = w.Window;\n\t\t//\t\tif (wtl.HasExStyle(WSE.NOREDIRECTIONBITMAP)) f |= Api.PW_RENDERFULLCONTENT;\n\t\t//\t\telse\n\t\t//\t\t\tapi.EnumChildWindows(wtl, (c, _) => {\n\t\t//\t\t\t\tif (c.HasExStyle(WSE.NOREDIRECTIONBITMAP) && (c == w || (c.IsVisible && c.GetRectIn(w, out var rr) && rr.IntersectsWith(r)))) {\n\t\t//\t\t\t\t\tf |= Api.PW_RENDERFULLCONTENT;\n\t\t//\t\t\t\t\treturn false;\n\t\t//\t\t\t\t}\n\t\t//\t\t\t\treturn true;\n\t\t//\t\t\t}, 0);\n\t\t//\t}\n\t\t//\tif (flags.Has(CIFlags.WindowDC) && 0 == (f & Api.PW_RENDERFULLCONTENT)) f = 0;\n\t\t//\treturn f;\n\t\t//}\n\t}\n}\n\nnamespace Au.Types {\n\t/// <summary>\n\t/// Used with <see cref=\"CaptureScreen\"/> functions.\n\t/// </summary>\n\t[Flags]\n\tpublic enum CIFlags {\n\t\t/// <inheritdoc cref=\"IFFlags.WindowDC\"/>\n\t\tWindowDC = 1,\n\n\t\t/// <inheritdoc cref=\"IFFlags.PrintWindow\"/>\n\t\tPrintWindow = 2,\n\n\t\t///// <inheritdoc cref=\"IFFlags.WindowDwm\"/>\n\t\t//WindowDwm = 4,\n\n\t\t//note: the above values must be the same in CIFlags, CIUFlags, IFFlags, OcrFlags.\n\n\t\t/// <summary>\n\t\t/// Flag: don't throw exception when the specified rectangle or point does not intersect with the window client area or when the rectangle is empty. Instead return <c>null</c> or 0.\n\t\t/// </summary>\n\t\tRelaxed = 0x100,\n\n\t\t//rejected. Or would need a tool to capture rect/pont in logical coord.\n\t\t///// <summary>\n\t\t///// Flag: the specified rectangle or point uses logical (non-scaled) coordinates when the window is DPI-scaled. Used only with flags <c>WindowDC</c> (default) or <c>PrintWindow</c>.\n\t\t///// </summary>\n\t\t//RectLogical = 0x200,\n\t}\n\n\tstatic partial class ExtMisc {\n\t\tinternal static CIFlags ToCIFlags_(this CIUFlags t) => (CIFlags)t & (CIFlags.WindowDC | CIFlags.PrintWindow /*| CIFlags.WindowDwm*/);\n\t\tinternal static CIFlags ToCIFlags_(this IFFlags t) => (CIFlags)t & (CIFlags.WindowDC | CIFlags.PrintWindow /*| CIFlags.WindowDwm*/);\n\t\tinternal static CIFlags ToCIFlags_(this OcrFlags t) => (CIFlags)t & (CIFlags.WindowDC | CIFlags.PrintWindow /*| CIFlags.WindowDwm*/);\n\t}\n\n\t/// <summary>\n\t/// Flags for <see cref=\"CaptureScreen.ImageColorRectUI\"/>.\n\t/// </summary>\n\t/// <remarks>\n\t/// Only one of flags <c>Image</c>, <c>Color</c> and <c>Rectangle</c> can be used. If none, can capture image or color.\n\t/// </remarks>\n\t[Flags]\n\tpublic enum CIUFlags {\n\t\t/// <inheritdoc cref=\"IFFlags.WindowDC\"/>\n\t\tWindowDC = 1,\n\n\t\t/// <inheritdoc cref=\"IFFlags.PrintWindow\"/>\n\t\tPrintWindow = 2,\n\n\t\t//could not make it work well with \"glass\" areas.\n\t\t///// <inheritdoc cref=\"IFFlags.WindowDwm\"/>\n\t\t//WindowDwm = 4,\n\n\t\t//note: the above values must be the same in CIFlags, CIUFlags, IFFlags, OcrFlags.\n\n\t\t/// <summary>Can capture only image, not color.</summary>\n\t\tImage = 0x100,\n\n\t\t/// <summary>Can capture only color, not image.</summary>\n\t\tColor = 0x200,\n\n\t\t/// <summary>Capture only rectangle, not image/color.</summary>\n\t\tRectangle = 0x400,\n\t}\n\n#pragma warning disable 1591 //XML doc\n\t[Flags, Obsolete(\"Renamed to CIUFlags\"), EditorBrowsable(EditorBrowsableState.Never)]\n\tpublic enum ICFlags {\n\t\tWindowDC = 1,\n\t\tPrintWindow = 2,\n\t\tImage = 0x100,\n\t\tColor = 0x200,\n\t\tRectangle = 0x400,\n\t}\n\n\t[Obsolete(\"Renamed to CIUResult\"), EditorBrowsable(EditorBrowsableState.Never)]\n\tpublic class ICResult : CIUResult { }\n#pragma warning restore 1591 //XML doc\n\n\t/// <summary>\n\t/// Results of <see cref=\"CaptureScreen.ImageColorRectUI\"/>.\n\t/// </summary>\n\tpublic class CIUResult {\n\t\t/// <summary>\n\t\t/// Captured image.\n\t\t/// <c>null</c> if captured single pixel color or used flag <see cref=\"CIUFlags.Rectangle\"/>.\n\t\t/// </summary>\n\t\tpublic Bitmap image;\n\n\t\t/// <summary>\n\t\t/// Captured color in <c>0xAARRGGBB</c> format. Alpha 0xFF.\n\t\t/// </summary>\n\t\tpublic uint color;\n\n\t\t/// <summary>\n\t\t/// Location of the captured image or rectangle, in screen coordinates.\n\t\t/// </summary>\n\t\tpublic RECT rect;\n\n\t\t/// <summary>\n\t\t/// Window or control containing the captured image or rectangle, if whole image is in its client area.\n\t\t/// In some cases may be incorrect, for example if windows moved/opened/closed/etc while capturing.\n\t\t/// </summary>\n\t\tpublic wnd w;\n\n\t\t/// <summary>\n\t\t/// If used flags to get window pixels and the window is DPI-scaled (smaller when capturing), on Windows 10 and later contains the scale factor. Else 1.\n\t\t/// </summary>\n\t\tpublic double dpiScale;\n\n\t\t/// <summary>\n\t\t/// If <c>true</c>, most likely <see cref=\"w\"/> is incorrect window, because the window that was there before capturing disappeared while capturing, for example it was a popup menu.\n\t\t/// If captured from screen (without flags like <c>WindowDC</c>), <see cref=\"w\"/> may be correct even if this is <c>true</c> (can't detect reliably), else certainly incorrect.\n\t\t/// </summary>\n\t\tpublic bool possiblyWrongWindow;\n\t}\n\n\t/// <summary>\n\t/// <see cref=\"CaptureScreen.RectPointWindowUI\"/> UI type.\n\t/// </summary>\n\tpublic enum CRUType {\n\t\t/// <summary>Capture only window.</summary>\n\t\tWindow,\n\n\t\t/// <summary>Capture only rectangle in screen.</summary>\n\t\tRect,\n\n\t\t/// <summary>Capture rectangle in window.</summary>\n\t\tWindowAndRect,\n\n\t\t/// <summary>Capture window and optionally rectangle in it.</summary>\n\t\tWindowOrRect,\n\n\t\t/// <summary>Capture only point (coordinates) in screen.</summary>\n\t\tPoint,\n\n\t\t/// <summary>Capture point in window.</summary>\n\t\tWindowAndPoint,\n\t}\n\n\t/// <summary>\n\t/// <see cref=\"CaptureScreen.RectPointWindowUI\"/> results.\n\t/// </summary>\n\tpublic record struct CRUResult(wnd w, bool hasRect, RECT r);\n}\n"
  },
  {
    "path": "Au/UI objects/OcrGoogleCloud.cs",
    "content": "using System.Drawing;\nusing System.Text.Json.Nodes;\nusing System.Net.Http;\n\nnamespace Au.More;\n\n/// <summary>\n/// This OCR engine uses <see href=\"https://cloud.google.com/vision/docs/reference/rest/v1/images/annotate\">Google Cloud Vision API</see>.\n/// </summary>\n/// <remarks>\n/// Sends image to Google Cloud and gets results. The OCR engine is accurate but much slower than the default engine or Tesseract. Depends on internet connection speed.\n/// \n/// To use this engine, need to have a Google Cloud account, enable Vision API and get API key. The service isn't free, but 1000 or so requests/month are free.\n/// </remarks>\npublic class OcrGoogleCloud : IOcrEngine {\n\tstring _apiKey;\n\t\n\t/// <param name=\"apiKey\">API key.</param>\n\tpublic OcrGoogleCloud(string apiKey) {\n\t\t_apiKey = apiKey;\n\t}\n\t\n\t/// <summary>\n\t/// Feature type, like <c>\"TEXT_DETECTION\"</c>. Or JSON of <c>features</c> array content, like <c>\"\"\"{ \"type\": \"TEXT_DETECTION\", \"model\": \"builtin/latest\" }\"\"\"</c>.\n\t/// If <c>null</c>, uses <c>\"DOCUMENT_TEXT_DETECTION\"</c>.\n\t/// </summary>\n\tpublic string Features { get; set; }\n\t\n\t/// <summary>\n\t/// JSON of <c>imageContext</c>, like <c>\"\"\"{ \"languageHints\": [ \"ja\" ] }\"\"\"</c>. Optional.\n\t/// </summary>\n\tpublic string ImageContext { get; set; }\n\t\n\t/// <inheritdoc cref=\"IOcrEngine.DpiScale\"/>\n\tpublic bool DpiScale { get; set; }\n\t\n\t/// <inheritdoc cref=\"IOcrEngine.Recognize\"/>\n\t/// <exception cref=\"Exception\">Failed.</exception>\n\tpublic OcrWord[] Recognize(Bitmap b, bool dispose, double scale) {\n\t\tvar b0 = b;\n\t\tb = IOcrEngine.PrepareBitmap(b, dispose, scale);\n\t\tvar png = IOcrEngine.GetBitmapPngFileData(b);\n\t\tif (dispose || b != b0) b.Dispose();\n\t\t\n\t\tvar url = \"https://vision.googleapis.com/v1/images:annotate?key=\" + _apiKey;\n\t\t\n\t\tvar feat = Features ?? \"DOCUMENT_TEXT_DETECTION\";\n\t\tif (!feat.Starts('{')) feat = $$\"\"\"{ \"type\": \"{{feat}}\" }\"\"\";\n\t\t\n\t\tstring ic = ImageContext, ic2 = ic.NE() ? null : \",\\r\\n      \\\"imageContext\\\": \";\n\t\t\n\t\tvar requestJson = $$\"\"\"\n{\n  \"requests\": [\n    {\n      \"features\": [\n        {{feat}}\n      ],\n      \"image\": {\n        \"content\": \"{{Convert.ToBase64String(png)}}\"\n      }{{ic2}}{{ic}}\n    }\n  ]\n}\n\"\"\";\n\t\t\n\t\t//perf.first();\n\t\tif (!internet.http_.TryPost(out var r, url, internet.jsonContent(requestJson), [\"Accept-Encoding: br, gzip, deflate\"], dontWait: true))\n\t\t\tthrow new AuException(r.Text(true));\n\t\t//perf.next();\n#if true //can be faster > 10 times. Also we use compression. Together it makes this part 100 times faster than the TryPost.\n\t\tvar j = _ReadResponse(r);\n\t\t//perf.nw();\n\t\treturn _ParseJson(j[\"responses\"][0], scale);\n#else\n\t\tvar j=r.Json();\n\t\t//perf.nw();\n\t\treturn _ParseJson(j[\"responses\"][0], scale);\n#endif\n\t}\n\t\n\t//Reads response until \"fullTextAnnotation\".\n\t//Can make the download size smaller > 10 times.\n\tstatic unsafe JsonNode _ReadResponse(HttpResponseMessage rm) {\n\t\tvar find = \"\\\"fullTextAnnotation\\\":\"u8;\n\t\tusing var ab = new ArrayBuilder_<byte>() { Capacity = 250_000 };\n\t\tint have = 0;\n\t\tusing (var stream = rm.Content.ReadAsStream()) {\n\t\t\tfor (; ; ) {\n\t\t\t\tif (have + 17000 > ab.Capacity) ab.ReAlloc(ab.Capacity * 2);\n\t\t\t\tint n = stream.Read(new Span<byte>(ab.Ptr + have, ab.Capacity - have - 10));\n\t\t\t\tif (n == 0) break;\n\t\t\t\tint old = Math.Min(find.Length, have);\n\t\t\t\tint i = new RByte(ab.Ptr + have - old, n + old).IndexOf(find);\n\t\t\t\tif (i > 0) {\n\t\t\t\t\ti += have - old;\n\t\t\t\t\twhile (ab.Ptr[i - 1] is 32 or 9 or 10 or 13 or (byte)',') i--;\n\t\t\t\t\tab.Ptr[i++] = (byte)'}';\n\t\t\t\t\tab.Ptr[i++] = (byte)']';\n\t\t\t\t\tab.Ptr[i++] = (byte)'}';\n\t\t\t\t\thave = i;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\thave += n;\n\t\t\t}\n\t\t}\n\t\trm.Dispose();\n\t\treturn JsonNode.Parse(new RByte(ab.Ptr, have));\n\t}\n\t\n\tstatic OcrWord[] _ParseJson(JsonNode j, double scale) {\n\t\tif (j[\"textAnnotations\"]?.AsArray() is not { } ta) return []; //no text detected\n\t\tList<OcrWord> a = new();\n\t\tstring text = null;\n\t\tint i = 0;\n\t\tforeach (var word in ta) {\n\t\t\tvar s = (string)word[\"description\"];\n\t\t\tif (text == null) {\n\t\t\t\ttext = s;\n\t\t\t} else {\n\t\t\t\tint i2 = text.Find(s, i);\n\t\t\t\tvar sep = text[i..i2]; if (sep == \"\\n\") sep = \"\\r\\n\";\n\t\t\t\ta.Add(new(sep, s, _PolyToRect(word), scale));\n\t\t\t\ti = i2 + s.Length;\n\t\t\t}\n\t\t}\n\t\t\n\t\treturn a.ToArray();\n\t\t\n\t\tstatic RECT _PolyToRect(JsonNode n) {\n\t\t\tvar a = n[\"boundingPoly\"][\"vertices\"].AsArray();\n\t\t\tJsonNode tl = a[0], tr = a[1], br = a[2], bl = a[3];\n\t\t\treturn IOcrEngine.PolyToRect(tl[\"x\"], tl[\"y\"], tr[\"x\"], tr[\"y\"], br[\"x\"], br[\"y\"], bl[\"x\"], bl[\"y\"]);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au/UI objects/OcrMicrosoftAzure.cs",
    "content": "using System.Drawing;\nusing System.Text.Json.Nodes;\nusing System.Net.Http;\n\nnamespace Au.More;\n\n/// <summary>\n/// This OCR engine uses Microsoft Azure Computer Vision OCR.\n/// </summary>\n/// <remarks>\n/// Sends image to Microsoft Azure and gets results. The OCR engine is accurate but much slower than the default engine or Tesseract, and usually slower than Google Cloud.\n/// \n/// To use this engine, need to have a Microsoft Azure account and get API key and endpoint URL. The service isn't free, but 500 or so requests/month are free.\n/// </remarks>\npublic class OcrMicrosoftAzure : IOcrEngine {\n\tstring _endpointUrl, _apiKey;\n\t\n\t/// <param name=\"endpointUrl\">Endpoint URL, like <c>\"https://xxxx.cognitiveservices.azure.com/\"</c>.</param>\n\t/// <param name=\"apiKey\">API key.</param>\n\tpublic OcrMicrosoftAzure(string endpointUrl, string apiKey) {\n\t\tif (!endpointUrl.Like(\"https://*.cognitiveservices.azure.com/\")) print.warning(\"endpointUrl should be like https://xxxx.cognitiveservices.azure.com/\");\n\t\t_endpointUrl = endpointUrl;\n\t\t_apiKey = apiKey;\n\t}\n\t\n\t/// <inheritdoc cref=\"IOcrEngine.DpiScale\"/>\n\tpublic bool DpiScale { get; set; }\n\t\n\t/// <inheritdoc cref=\"IOcrEngine.Recognize\"/>\n\t/// <exception cref=\"Exception\">Failed.</exception>\n\tpublic OcrWord[] Recognize(Bitmap b, bool dispose, double scale) {\n\t\tvar b0 = b;\n\t\tb = IOcrEngine.PrepareBitmap(b, dispose, scale, 50, 50);\n\t\tvar png = IOcrEngine.GetBitmapPngFileData(b);\n\t\tif (dispose || b != b0) b.Dispose();\n\t\t\n\t\tvar url = $\"{_endpointUrl}formrecognizer/documentModels/prebuilt-read:analyze?api-version=2022-08-31\"; //&stringIndexType=textElements\n\t\tvar headers = new[] { \"Ocp-Apim-Subscription-Key: \" + _apiKey };\n\t\tvar requestJson = $$\"\"\"\n{\n\"base64Source\": \"{{Convert.ToBase64String(png)}}\"\n}\n\"\"\";\n\t\tif (!internet.http_.TryPost(out var r, url, internet.jsonContent(requestJson), headers))\n\t\t\tthrow new AuException(r.Text(true));\n\t\turl = r.Headers.GetValues(\"Operation-Location\").First();\n\t\t//perf.next();\n\t\t500.ms();\n\t\tvar j = wait.until(new(90) { Period = 300 }, () => {\n\t\t\tvar v = internet.http_.Get(url, headers: headers).Json();\n\t\t\treturn (string)v[\"status\"] == \"succeeded\" ? v : null;\n\t\t});\n\t\t//perf.nw();\n\t\treturn _ParseJson(j[\"analyzeResult\"], scale);\n\t}\n\t\n\tstatic OcrWord[] _ParseJson(JsonNode j, double scale) {\n\t\tList<OcrWord> a = new();\n\t\tHashSet<int> hs = new();\n\t\tint i = 0;\n\t\tforeach (var page in j[\"pages\"].AsArray()) {\n\t\t\tforeach (var line in page[\"lines\"].AsArray()) {\n\t\t\t\ths.Add((int)line[\"spans\"][0][\"offset\"]);\n\t\t\t}\n\t\t\tforeach (var word in page[\"words\"].AsArray()) {\n\t\t\t\tvar sep = i++ == 0 ? null : hs.Contains((int)word[\"span\"][\"offset\"]) ? \"\\r\\n\" : \" \";\n\t\t\t\ta.Add(new(sep, (string)word[\"content\"], _PolyToRect(word), scale));\n\t\t\t}\n\t\t}\n\t\t\n\t\treturn a.ToArray();\n\t\t\n\t\tstatic RECT _PolyToRect(JsonNode n) {\n\t\t\tvar a = n[\"polygon\"].AsArray();\n\t\t\treturn IOcrEngine.PolyToRect(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7]);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au/UI objects/OcrTesseract.cs",
    "content": "using Microsoft.Win32;\nusing System.Drawing;\nusing System.Drawing.Imaging;\n\nnamespace Au.More;\n\n/// <summary>\n/// This OCR engine uses <c>tesseract.exe</c> from Tesseract installed on this computer.\n/// </summary>\n/// <remarks>\n/// Slower than <see cref=\"OcrWin10\"/> (the default engine). The accuracy is poor.\n/// Supports more languages. You choose what languages to install when you install Tesseract.\n/// \n/// <see href=\"https://github.com/UB-Mannheim/tesseract/wiki\">Download Tesseract</see>\n/// </remarks>\npublic class OcrTesseract : IOcrEngine {\n\t/// <param name=\"tesseractPath\">Full path of Tesseract folder. If <c>null</c>, uses path written in the registry by the installer.</param>\n\t/// <exception cref=\"FileNotFoundException\"></exception>\n\tpublic OcrTesseract(string tesseractPath = null) {\n\t\t_tesseractPath = tesseractPath ?? Registry.GetValue(@\"HKEY_LOCAL_MACHINE\\SOFTWARE\\Tesseract-OCR\", \"Path\", null) as string;\n\t\tvar exe = _tesseractPath + @\"\\tesseract.exe\";\n\t\tif (!filesystem.exists(exe).File) throw new FileNotFoundException(\"tesseract.exe not found. Download/install Tesseract, or set correct tesseractPath argument.\");\n\t\t_tesseractExe = exe;\n\t}\n\treadonly string _tesseractPath, _tesseractExe;\n\n\t/// <summary>\n\t/// One or more of installed languages, like <c>\"deu\"</c> or <c>\"eng+deu\"</c>. If <c>null</c> (default), uses <c>\"eng\"</c>.\n\t/// </summary>\n\tpublic string Language { get; set; }\n\n\t/// <summary>\n\t/// Gets OCR languages that are installed on this computer and can be used for <see cref=\"Language\"/>.\n\t/// </summary>\n\tpublic string[] AvailableLanguages {\n\t\tget {\n\t\t\tvar a = new List<string>();\n\t\t\trun.console(s => { if (!s.NE() && !s.Ends(':') && !s.Eqi(\"osd\")) a.Add(s); }, _tesseractExe, \"--list-langs\");\n\t\t\treturn a.ToArray();\n\n\t\t\t//note: can be in subfolders, like \"test/deu\".\n\t\t}\n\t}\n\n\t///// <summary>\n\t///// Adds command line <c>--psm</c> (page segmentation mode).\n\t///// Valid values are 0-13, but documented only 3 (default) and 6 (no segmentation).\n\t///// </summary>\n\t//public int Psm { get; set; } = 3;\n\n\t/// <summary>\n\t/// Additional command line arguments.\n\t/// </summary>\n\tpublic string CommandLine { get; set; }\n\n\t/// <inheritdoc cref=\"IOcrEngine.DpiScale\"/>\n\tpublic bool DpiScale { get; set; } = true;\n\n\t/// <inheritdoc cref=\"IOcrEngine.Recognize\"/>\n\tpublic OcrWord[] Recognize(Bitmap b, bool dispose, double scale) {\n\t\tvar b0 = b;\n\t\tb = IOcrEngine.PrepareBitmap(b, dispose, scale);\n\n\t\tusing var temp = new TempFile(\".png\");\n\t\tb.Save(temp, ImageFormat.Png);\n\t\tif (dispose || b != b0) b.Dispose();\n\n\t\tvar cl = $\"\\\"{temp}\\\" stdout\";\n\t\tif (!Language.NE()) cl += $\" -l {Language}\";\n\t\t//if (Psm != 3) cl += $\" --psm {Psm}\";\n\t\tif (!CommandLine.NE()) cl += $\" {CommandLine}\";\n\t\tcl += \" quiet tsv\"; //must be at the end, else all -x parameters ignored\n\n\t\tif (0 != run.console(out string result, _tesseractExe, cl, encoding: Encoding.UTF8)) throw new AuException(result);\n\n\t\t//print.it(result);\n\n\t\tregexp rx = new(@\"^(?:\\d+\\t){6}(\\d+)\\t(\\d+)\\t(\\d+)\\t(\\d+)\\t\\S+\\t(\\S.*)$\");\n\t\tList<OcrWord> a = new();\n\t\tstring sep = null;\n\t\tRECT pr = default;\n\t\tforeach (var s in result.Lines()) {\n\t\t\tswitch (s[0]) {\n\t\t\tcase '4': //line\n\t\t\t\tif (sep != null) sep = \"\\r\\n\";\n\t\t\t\tbreak;\n\t\t\tcase '5': //word\n\t\t\t\tif (rx.Match(s, out var m)) { //else text is space\n\t\t\t\t\tRECT r = new(m[1].Value.ToInt(), m[2].Value.ToInt(), m[3].Value.ToInt(), m[4].Value.ToInt());\n\n\t\t\t\t\t//break line if big space between words\n\t\t\t\t\tif (sep == \" \" && r.left - pr.right > pr.Height + r.Height) sep = \"\\r\\n\";\n\t\t\t\t\tpr = r;\n\n\t\t\t\t\ta.Add(new(sep, m[5].Value, r, scale));\n\t\t\t\t\tsep = \" \";\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\treturn a.ToArray();\n\t}\n}\n"
  },
  {
    "path": "Au/UI objects/OcrWin10.cs",
    "content": "using System.Drawing;\nusing System.Drawing.Imaging;\n\nnamespace Au.More {\n\t/// <summary>\n\t/// The default OCR engine. Available on Windows 10 and later.\n\t/// </summary>\n\t/// <remarks>\n\t/// Uses the Windows 10/11 OCR engine. It's the fastest. The accuracy is poor.\n\t/// If need better accuracy, use a cloud OCR engine (<see cref=\"OcrGoogleCloud\"/>, <see cref=\"OcrMicrosoftAzure\"/>).\n\t/// If need a non-cloud OCR for older Windows, install Tesseract and use <see cref=\"OcrTesseract\"/>. Also it supports more languages.\n\t/// </remarks>\n\tpublic unsafe class OcrWin10 : IOcrEngine {\n\t\t/// <exception cref=\"NotSupportedException\">OS version older than Windows 10.</exception>\n\t\tpublic OcrWin10() {\n\t\t\tif (!osVersion.minWin10) throw new NotSupportedException(\"This OCR engine is available only on Windows 10 and later. Use another engine.\");\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Language, like <c>\"en-US\"</c>. See <see cref=\"AvailableLanguages\"/>. If <c>null</c> (default), uses the default OCR language of this computer.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// You can install languages in Windows <b>Settings > Time and language > Language and region</b>. Not all languages are supported.\n\t\t/// </remarks>\n\t\tpublic string Language { get; set; }\n\n\t\t/// <summary>\n\t\t/// Gets OCR languages that are installed on this computer and can be used for <see cref=\"Language\"/>.\n\t\t/// </summary>\n\t\tpublic (string tag, string displayName)[] AvailableLanguages {\n\t\t\tget {\n\t\t\t\tusing var oes = WinRT.IOcrEngineStatics.CreateStatics();\n\t\t\t\tusing var a1 = oes.AvailableRecognizerLanguages;\n\t\t\t\tvar a2 = new (string, string)[a1.Size];\n\t\t\t\tfor (int i = 0; i < a2.Length; i++) {\n\t\t\t\t\tusing var lang = a1[i];\n\t\t\t\t\ta2[i] = (lang.LanguageTag, lang.DisplayName);\n\t\t\t\t}\n\t\t\t\treturn a2;\n\t\t\t}\n\t\t}\n\n\t\t/// <inheritdoc cref=\"IOcrEngine.DpiScale\"/>\n\t\tpublic bool DpiScale { get; set; } = true;\n\n\t\t/// <inheritdoc cref=\"IOcrEngine.Recognize\"/>\n\t\tpublic OcrWord[] Recognize(Bitmap b, bool dispose, double scale) {\n\t\t\tvar b0 = b;\n\t\t\tb = IOcrEngine.PrepareBitmap(b, dispose, scale, 50, 50);\n\t\t\tusing var sb = WinRT.ISoftwareBitmap.FromBitmap(b);\n\t\t\tif (dispose || b != b0) b.Dispose();\n\n\t\t\tusing var engine = WinRT.IOcrEngine.CreateEngine(Language);\n\t\t\tusing var result = engine.RecognizeAsync(sb).Await<WinRT.IOcrResult>();\n\n\t\t\tvar a = new List<OcrWord>();\n\t\t\tstring sep = null;\n\t\t\tusing var lines = result.Lines;\n\t\t\tforeach (var line in lines.Items()) {\n\t\t\t\tusing var words = line.Words;\n\t\t\t\tforeach (var word in words.Items()) {\n\t\t\t\t\ta.Add(new(sep, word.Text, word.BoundingRect, scale));\n\t\t\t\t\tsep = \" \";\n\t\t\t\t}\n\t\t\t\tsep = \"\\r\\n\";\n\t\t\t}\n\n\t\t\treturn a.ToArray();\n\t\t}\n\t}\n}\n\nnamespace Au.Types {\n#pragma warning disable 649, 169 //field never assigned/used\n\tstatic unsafe partial class WinRT {\n\t\tinternal struct IOcrResult : IComPtr {\n\t\t\tIUnknown _u; public IUnknown U => _u;\n\t\t\tpublic void Dispose() => _u.Dispose();\n\n\t\t\tpublic IVectorView<IOcrLine> Lines => _u.GetPtr<IVectorView<IOcrLine>>(6);\n\n\t\t\t//public string Text => _u.GetString(8);\n\t\t}\n\n\t\tinternal struct IOcrLine : IComPtr {\n\t\t\tIUnknown _u; public IUnknown U => _u;\n\t\t\tpublic void Dispose() => _u.Dispose();\n\n\t\t\tpublic IVectorView<IOcrWord> Words => _u.GetPtr<IVectorView<IOcrWord>>(6);\n\n\t\t\t//public string Text => _u.GetString(7);\n\t\t}\n\n\t\tinternal struct IOcrWord : IComPtr {\n\t\t\tIUnknown _u; public IUnknown U => _u;\n\t\t\tpublic void Dispose() => _u.Dispose();\n\n\t\t\tpublic RECT BoundingRect {\n\t\t\t\tget {\n\t\t\t\t\tHR(((delegate* unmanaged[Stdcall]<IntPtr, out RectangleF, int>)_u[6])(_u, out var r));\n\t\t\t\t\treturn RECT.From(r, true);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpublic string Text => _u.GetString(7);\n\t\t}\n\n\t\t[Guid(\"5BFFA85A-3384-3540-9940-699120D428A8\")]\n\t\tinternal struct IOcrEngineStatics : IComPtr {\n\t\t\tIUnknown _u; public IUnknown U => _u;\n\t\t\tpublic void Dispose() => _u.Dispose();\n\n\t\t\t//public int MaxImageDimension { get; }\n\n\t\t\tpublic IVectorView<ILanguage> AvailableRecognizerLanguages => _u.GetPtr<IVectorView<ILanguage>>(7);\n\n\t\t\t//public bool IsLanguageSupported(ILanguage language);\n\n\t\t\tpublic IOcrEngine TryCreateFromLanguage(ILanguage language) {\n\t\t\t\tHR(((delegate* unmanaged[Stdcall]<IntPtr, ILanguage, out IOcrEngine, int>)_u[9])(_u, language, out var r));\n\t\t\t\treturn r;\n\t\t\t}\n\n\t\t\tpublic IOcrEngine TryCreateFromUserProfileLanguages() => _u.GetPtr<IOcrEngine>(10);\n\n\t\t\tpublic static IOcrEngineStatics CreateStatics() => Create<IOcrEngineStatics>(\"Windows.Media.Ocr.OcrEngine\");\n\t\t}\n\n\t\tinternal struct IOcrEngine : IComPtr {\n\t\t\tIUnknown _u; public IUnknown U => _u;\n\t\t\tpublic void Dispose() => _u.Dispose();\n\n\t\t\tpublic IAsyncOperation RecognizeAsync(ISoftwareBitmap bitmap) {\n\t\t\t\tHR(((delegate* unmanaged[Stdcall]<IntPtr, ISoftwareBitmap, out IAsyncOperation, int>)_u[6])(_u, bitmap, out var r));\n\t\t\t\treturn r;\n\t\t\t}\n\n\t\t\t//public ILanguage RecognizerLanguage { get; }\n\n\t\t\tpublic static IOcrEngine CreateEngine(string language = null) {\n\t\t\t\tusing var oes = IOcrEngineStatics.CreateStatics();\n\t\t\t\tif (language.NE()) return oes.TryCreateFromUserProfileLanguages();\n\t\t\t\tusing var lf = Create<ILanguageFactory>(\"Windows.Globalization.Language\");\n\t\t\t\tusing var lang = lf.CreateLanguage(language);\n\t\t\t\treturn oes.TryCreateFromLanguage(lang);\n\t\t\t}\n\t\t}\n\n\t\t[Guid(\"DF0385DB-672F-4A9D-806E-C2442F343E86\")]\n\t\tstruct ISoftwareBitmapStatics : IComPtr {\n\t\t\tIUnknown _u; public IUnknown U => _u;\n\t\t\tpublic void Dispose() => _u.Dispose();\n\n\t\t\tpublic ISoftwareBitmap CreateCopyFromBuffer(IBuffer source, int width, int height) {\n\t\t\t\tHR(((delegate* unmanaged[Stdcall]<IntPtr, IBuffer, int, int, int, out ISoftwareBitmap, int>)_u[9])(_u, source, 87, width, height, out var r)); //Bgra8 = 87\n\t\t\t\treturn r;\n\t\t\t}\n\t\t}\n\n\t\tinternal struct ISoftwareBitmap : IComPtr {\n\t\t\tIUnknown _u; public IUnknown U => _u;\n\t\t\tpublic void Dispose() => _u.Dispose();\n\n\t\t\tpublic static ISoftwareBitmap FromBitmap(Bitmap b) {\n\t\t\t\tusing var d = b.Data(ImageLockMode.ReadOnly, b.PixelFormat == PixelFormat.Format32bppRgb ? PixelFormat.Format32bppRgb : PixelFormat.Format32bppArgb);\n\t\t\t\tif (d.Stride < 0) throw new ArgumentException();\n\t\t\t\tusing var cbs = Create<ICryptographicBufferStatics>(\"Windows.Security.Cryptography.CryptographicBuffer\");\n\t\t\t\tusing var sbs = Create<ISoftwareBitmapStatics>(\"Windows.Graphics.Imaging.SoftwareBitmap\");\n\t\t\t\tusing var buffer = cbs.CreateFromByteArray(d.Height * d.Stride, (byte*)d.Scan0);\n\t\t\t\treturn sbs.CreateCopyFromBuffer(buffer, d.Width, d.Height);\n\t\t\t}\n\t\t}\n\n\t\t[Guid(\"320B7E22-3CB0-4CDF-8663-1D28910065EB\")]\n\t\tstruct ICryptographicBufferStatics : IComPtr {\n\t\t\tIUnknown _u; public IUnknown U => _u;\n\t\t\tpublic void Dispose() => _u.Dispose();\n\n\t\t\t//the easiest way to create IBuffer which is required to create ISoftwareBitmap\n\t\t\tpublic IBuffer CreateFromByteArray(int size, byte* value) {\n\t\t\t\tHR(((delegate* unmanaged[Stdcall]<IntPtr, int, byte*, out IBuffer, int>)_u[9])(_u, size, value, out var r));\n\t\t\t\treturn r;\n\t\t\t}\n\t\t}\n\n\t\tstruct IBuffer : IComPtr {\n\t\t\tIUnknown _u; public IUnknown U => _u;\n\t\t\tpublic void Dispose() => _u.Dispose();\n\t\t}\n\n\t\tinternal struct ILanguage : IComPtr {\n\t\t\tIUnknown _u; public IUnknown U => _u;\n\t\t\tpublic void Dispose() => _u.Dispose();\n\n\t\t\tpublic string LanguageTag => _u.GetString(6);\n\t\t\tpublic string DisplayName => _u.GetString(7);\n\t\t\t//public string NativeName => _u.GetString(8);\n\t\t\t//public string Script => _u.GetString(9);\n\t\t}\n\n\t\t[Guid(\"9B0252AC-0C27-44F8-B792-9793FB66C63E\")]\n\t\tinternal struct ILanguageFactory : IComPtr {\n\t\t\tIUnknown _u; public IUnknown U => _u;\n\t\t\tpublic void Dispose() => _u.Dispose();\n\n\t\t\tpublic ILanguage CreateLanguage(string languageTag) {\n\t\t\t\tusing var s1 = new _Hstring(languageTag);\n\t\t\t\tHR(((delegate* unmanaged[Stdcall]<IntPtr, IntPtr, out ILanguage, int>)_u[6])(_u, s1, out var r));\n\t\t\t\treturn r;\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au/UI objects/elm.cs",
    "content": "namespace Au;\n\n/// <summary>\n/// Represents a UI element. Clicks, gets properties, etc.\n/// </summary>\n/// <remarks>\n/// <para>\n/// UI elements are user interface (UI) parts that are accessible through programming interfaces (API). For example buttons, links, list items.\n/// This class can find them, get properties, click, etc.\n/// Web pages and most other windows support UI elements.\n/// </para>\n/// <para>\n/// An <c>elm</c> variable contains a COM interface pointer (<ms>IAccessible</ms> or other) and uses methods of that interface or/and related API.\n/// </para>\n/// <para>\n/// <c>elm</c> functions that get properties don't throw exception when the COM etc method failed (returned an error code of <ms>HRESULT</ms> type).\n/// Then they return <c>\"\"</c> (string properties), 0, <c>false</c>, <c>null</c> or empty collection, depending on return type.\n/// Applications implement UI elements differently, often with bugs, and their COM interface functions return a variety of error codes.\n/// It's impossible to reliably detect whether the error code means an error or the property is merely unavailable.\n/// These <c>elm</c> functions also set the last error code of this thread = the return value (<c>HRESULT</c>) of the COM function, and callers can use <see cref=\"lastError\"/> to get it.\n/// If <see cref=\"lastError.code\"/> returns 1 (<c>S_FALSE</c>), in most cases it's not an error, just the property is unavailable. On error it will probably be a negative error code.\n/// </para>\n/// <para>\n/// You can dispose <c>elm</c> variables to release the COM object, but it is not necessary (GC will do it later).\n/// </para>\n/// <para>\n/// An <c>elm</c> variable cannot be used in multiple threads. Only <c>Dispose</c> can be called in any thread.\n/// </para>\n/// <para>\n/// UI elements are implemented and live in their applications. This class just communicates with them.\n/// [Known UI element issues in various applications](xref:ui_element_issues)\n/// </para>\n/// </remarks>\n/// <example>\n/// Click link <c>\"Example\"</c> in Chrome.\n/// <code><![CDATA[\n/// var w = wnd.find(0, \"* Chrome\");\n/// var e = w.Elm[\"web:LINK\", \"Example\"].Find(5);\n/// e.Invoke();\n/// ]]></code>\n/// Click a link, wait for new web page, click a link in it.\n/// <code><![CDATA[\n/// var w = wnd.find(0, \"* Chrome\");\n/// var e = w.Elm[\"web:LINK\", \"Link 1\"].Find(5);\n/// e.WebInvoke();\n/// w.Elm[\"web:LINK\", \"Link 2\"].Find(5).WebInvoke();\n/// ]]></code>\n/// </example>\n[StructLayout(LayoutKind.Sequential)]\npublic unsafe sealed partial class elm : IDisposable {\n\t//FUTURE: elm.more.EnableElmInChromeWebPagesWhenItStarts\n\t//FUTURE: elm.more.EnableElmInJavaWindows (see JavaEnableJAB in QM2)\n\t//FUTURE: add functions to marshal to another thread.\n\n\tinternal struct Misc_ {\n\t\tpublic EMiscFlags flags;\n\t\tpublic byte roleByte; //for optimization. 0 if not set or failed to get. 0xFF (ERole.Custom) if VT_BSTR or not 1-ROLE_MAX.\n\t\tpublic ushort level; //for ToString. 0 if not set.\n\t\t\n\t\tpublic void SetRole(ERole role) { this.roleByte = (byte)(role <= 0 || role > ERole.TREEBUTTON ? ERole.Custom : role); }\n\t\tpublic void SetLevel(int level) { this.level = (ushort)Math.Clamp(level, 0, 0xffff); }\n\t}\n\t\n\tinternal IntPtr _iacc;\n\tinternal int _elem;\n\tinternal Misc_ _misc;\n\t//Real elm object memory size with header: 32 bytes on 64-bit.\n\t//We don't use RCW<IAccessible>, which would add another 32 bytes.\n\t\n\t/// <summary>\n\t/// Creates elm from <c>IAccessible</c> and child id.\n\t/// By default does not <c>AddRef</c>.\n\t/// <i>iacc</i> must not be 0.\n\t/// </summary>\n\tinternal elm(IntPtr iacc, int elem = 0, bool addRef = false) {\n\t\t_Set(iacc, elem, default, addRef);\n\t}\n\t\n\t/// <summary>\n\t/// Creates <see cref=\"elm\"/> from <c>Cpp_Acc</c>.\n\t/// By default does not <c>AddRef</c>.\n\t/// <c>x.acc</c> must not be 0.\n\t/// </summary>\n\tinternal elm(Cpp.Cpp_Acc x, bool addRef = false) {\n\t\t_Set(x.acc, x.elem, x.misc, addRef);\n\t}\n\t\n\t/// <summary>\n\t/// Sets fields.\n\t/// <c>_iacc</c> must be 0, <i>iacc</i> not 0.\n\t/// </summary>\n\tvoid _Set(IntPtr iacc, int elem = 0, Misc_ misc = default, bool addRef = false) {\n\t\tDebug.Assert(_iacc == default);\n\t\tDebug.Assert(iacc != default);\n\t\tif (addRef) Marshal.AddRef(iacc);\n\t\t_iacc = iacc;\n\t\t_elem = elem;\n\t\t_misc = misc;\n\t\t\n\t\tint mp = _MemoryPressure;\n\t\tGC.AddMemoryPressure(mp);\n\t\t//s_dmp += mp; if(s_dmp > DebugMaxMemoryPressure) DebugMaxMemoryPressure = s_dmp;\n\t\t//DebugMemorySum += mp;\n\t}\n\t\n\tint _MemoryPressure => _elem == 0 ? c_memoryPressure : c_memoryPressure / 10;\n\tconst int c_memoryPressure = 1000; //assume this is the average UI element memory size in both processes\n\t\n\t//internal static int DebugMaxMemoryPressure;\n\t//static int s_dmp;\n\t//internal static int DebugMemorySum;\n\t\n\t///\n\tvoid Dispose(bool disposing) {\n\t\t//print.it(disposing);\n\t\tif (_iacc != default) {\n\t\t\tvar t = _iacc; _iacc = default;\n\t\t\t//perf.first();\n\t\t\tMarshal.Release(t);\n\t\t\t//perf.nw();\n\t\t\t//print.it($\"rel: {t}  {Marshal.Release(t)}\");\n\t\t\t\n\t\t\tint mp = _MemoryPressure;\n\t\t\tGC.RemoveMemoryPressure(mp);\n\t\t\t//s_dmp -= mp;\n\t\t}\n\t\t_elem = 0;\n\t\t_misc = default;\n\t}\n\t\n\t/// <summary>\n\t/// Releases COM object and clears this variable.\n\t/// </summary>\n\tpublic void Dispose() {\n\t\tDispose(true);\n\t\tGC.SuppressFinalize(this);\n\t}\n\t\n\t///\n\t~elm() {\n#if DEBUG\n\t\t//print.it(\"~elm\");\n\t\ttry { Dispose(false); }\n\t\tcatch (Exception ex) { print.it(ex); }\n#else\n\t\tDispose(false);\n#endif\n\t}\n\t//TODO2: try: release elm COM objects in process.Exit event. Use weak reference. Or GC.Collect.\n\t\n\t//internal void Debug1() {\n\t//\tprint.it(_iacc, Debug_.GetComObjRefCount_(_iacc));\n\t//}\n\t\n\t//not used in this library, but sometimes may need it for testing something in scripts.\n\tinternal IntPtr Iacc_ => _iacc;\n\t\n\t/// <summary>\n\t/// Gets or changes simple element id, also known as child id.\n\t/// </summary>\n\t/// <remarks>\n\t/// Most UI elements are not simple elements. Then this property is 0.\n\t/// Often (but not always) this property is the 1-based item index in parent. For example <c>LISTITEM</c> in <c>LIST</c>.\n\t/// The <c>set</c> function sometimes can be used as a fast alternative to <see cref=\"Navigate\"/>. It modifies only this variable. It does not check whether the value is valid.\n\t/// Simple elements cannot have child elements.\n\t/// </remarks>\n\tpublic int Item { get => _elem; set { _misc.roleByte = 0; _elem = value; } }\n\t\n\t/// <summary>\n\t/// Returns some additional info about this variable, such as how the UI element was retrieved (inproc, UIA, Java).\n\t/// </summary>\n\tpublic EMiscFlags MiscFlags => _misc.flags;\n\t\n\t/// <summary>\n\t/// Gets or sets indentation level for <see cref=\"ToString\"/>.\n\t/// </summary>\n\t/// <remarks>\n\t/// When <see cref=\"elmFinder.Find\"/> or similar function finds a UI element, it sets this property of the <see cref=\"elm\"/> variable. If <see cref=\"fromXY\"/> etc, it is 0 (unknown).\n\t/// When searching in a window, at level 0 are direct children of the <c>WINDOW</c>. When searching in controls (specified class or id), at level 0 is the control. When searching in <see cref=\"elm\"/>, at level 0 are its direct children. When searching in web page (role prefix <c>\"web:\"</c> etc), at level 0 is the web page (role <c>DOCUMENT</c> or <c>PANE</c>).\n\t/// </remarks>\n\tpublic int Level { get => _misc.level; set => _misc.SetLevel(value); }\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if this variable is disposed.\n\t/// </summary>\n\tbool _Disposed => _iacc == default;\n\t\n\tinternal void ThrowIfDisposed_() {\n\t\tif (_Disposed) throw new ObjectDisposedException(nameof(elm));\n\t\tWarnInSendMessage_();\n\t}\n\t\n\tinternal static void WarnInSendMessage_() {\n\t\tif (Api.InSendMessageBlocked) { //as fast as TickCount64\n\t\t\tlong t = Environment.TickCount64;\n\t\t\tif (t - s_wismTime > 500) {\n\t\t\t\ts_wismTime = t;\n\t\t\t\tprint.warning(\"elm functions may not work here. This code is called by another thread through SendMessage or is a low-level hook. Try workarounds: call ReplyMessage (Windows API); use an asynchronous way to call the elm function (timer, Dispatcher.InvokeAsync, etc); let that thread instead of SendMessage use an asynchronous function such as PostMessage.\");\n\t\t\t}\n\t\t}\n\t}\n\tstatic long s_wismTime;\n\t\n\t/// <summary>\n\t/// Gets UI element of window or control. Or some its standard part - client area, titlebar etc.\n\t/// </summary>\n\t/// <param name=\"w\">Window or control.</param>\n\t/// <param name=\"objid\">Window part id. Default <c>EObjid.WINDOW</c>. Also can be a custom id supported by that window, cast <c>int</c> to <c>EObjid</c>.</param>\n\t/// <param name=\"flags\">Flags.</param>\n\t/// <exception cref=\"AuWndException\">Invalid window.</exception>\n\t/// <exception cref=\"AuException\">Failed. For example, window of a higher [](xref:uac) integrity level process.</exception>\n\t/// <exception cref=\"ArgumentException\"><i>objid</i> is <c>QUERYCLASSNAMEIDX</c> or <c>NATIVEOM</c>.</exception>\n\t/// <remarks>\n\t/// Uses API <ms>AccessibleObjectFromWindow</ms>.\n\t/// </remarks>\n\tpublic static elm fromWindow(wnd w, EObjid objid = EObjid.WINDOW, EWFlags flags = 0) {\n\t\tWarnInSendMessage_();\n\t\t\n\t\tbool spec = false;\n\t\tswitch (objid) {\n\t\tcase EObjid.QUERYCLASSNAMEIDX: //use WM_GETOBJECT\n\t\tcase EObjid.NATIVEOM: //use API AccessibleObjectFromWindow\n\t\t\tthrow new ArgumentException();\n\t\tcase EObjid.CARET: //w should be 0\n\t\tcase EObjid.CURSOR: //w should be 0\n\t\tcase EObjid.ALERT: //only with AccessibleObjectFromEvent?\n\t\tcase EObjid.SOUND: //only with AccessibleObjectFromEvent?\n\t\t\tspec = true; flags |= EWFlags.NotInProc;\n\t\t\tbreak;\n\t\t}\n\t\t\n\t\tvar hr = Cpp.Cpp_AccFromWindow(flags.Has(EWFlags.NotInProc) ? 1 : 0, w, objid, out var a, out _);\n\t\tif (hr != 0) {\n\t\t\tif (flags.Has(EWFlags.NoThrow)) return null;\n\t\t\tif (spec && w.Is0) throw new AuException();\n\t\t\tw.ThrowIfInvalid();\n\t\t\t_WndThrow(hr, w, \"*get UI element from window.\");\n\t\t}\n\t\treturn new elm(a);\n\t}\n\t\n\tstatic void _WndThrow(int hr, wnd w, string es) {\n\t\tw.UacCheckAndThrow_(es);\n\t\tthrow new AuException(hr, es);\n\t}\n\t\n\t/// <summary>\n\t/// Gets UI element from point.\n\t/// </summary>\n\t/// <returns>Returns <c>null</c> if failed. Usually fails if the window is of a higher [](xref:uac) integrity level process. With some windows can fail occasionally.</returns>\n\t/// <param name=\"p\">\n\t/// Coordinates.\n\t/// Tip: To specify coordinates relative to the right, bottom, work area or a non-primary screen, use <see cref=\"Coord.Normalize\"/>, like in the example.\n\t/// </param>\n\t/// <param name=\"flags\"></param>\n\t/// <example>\n\t/// Get UI element at 100 200.\n\t/// <code><![CDATA[\n\t/// var e = elm.fromXY((100, 200));\n\t/// print.it(e);\n\t/// ]]></code>\n\t/// \n\t/// Get UI element at 50 from left and 100 from the bottom edge of the work area.\n\t/// <code><![CDATA[\n\t/// var e = elm.fromXY(Coord.Normalize(50, ^100, workArea: true));\n\t/// print.it(e);\n\t/// ]]></code>\n\t/// </example>\n\tpublic static elm fromXY(POINT p, EXYFlags flags = 0) {\n\t\tWarnInSendMessage_();\n\t\treturn fromXY_(p, flags);\n\t}\n\t\n\t//used by fromXY and Delm\n\tinternal static elm fromXY_(POINT p, EXYFlags flags, Cpp.Cpp_AccFromPointCallbackT callback = null) {\n\t\twnd w = default;\n\t\tbool retry = false;\n\t\tgRetry:\n\t\t\n\t\tint hr = Cpp.Cpp_AccFromPoint(p, flags, (flags, wFP, wTL) => {\n\t\t\tw = wFP;\n\t\t\tif (callback is {  } cb) flags = cb(flags, wFP, wTL);\n\t\t\t\n\t\t\tif (osVersion.minWin8_1 ? !flags.Has(EXYFlags.NotInProc) : flags.Has(EXYFlags.UIA)) {\n\t\t\t\tbool dpiV = Dpi.IsWindowVirtualized(wTL);\n\t\t\t\tif (dpiV) flags |= Enum_.EXYFlags_DpiScaled;\n\t\t\t}\n\t\t\treturn flags;\n\t\t}, out var a);\n\t\t\n\t\tif (hr != 0) return null;\n\t\t\n\t\t//workaround for Chrome bug. Sometimes DOCUMENT. Eg after scrolling. Also in some pages or page parts, eg LA home page when scrolled to the bottom. Same in all modes except acc notinproc.\n\t\tif (!retry && a.misc.roleByte == (byte)ERole.DOCUMENT && w.ClassNameIs(\"Chrome*\")) {\n\t\t\tMarshal.Release(a.acc);\n\t\t\tretry = true;\n\t\t\tgoto gRetry;\n\t\t}\n\t\t\n\t\treturn new elm(a);\n\t}\n\t\n\t/// <inheritdoc cref=\"fromXY(POINT, EXYFlags)\"/>\n\tpublic static elm fromXY(int x, int y, EXYFlags flags = 0) => fromXY(new POINT(x, y), flags);\n\t\n\t//rejected: FromXY(Coord, Coord, ...). Coord makes no sense.\n\t\n\t/// <summary>\n\t/// Gets UI element from mouse cursor (pointer) position.\n\t/// </summary>\n\t/// <param name=\"flags\"></param>\n\t/// <exception cref=\"AuException\">Failed. For example, window of a higher [](xref:uac) integrity level process.</exception>\n\t/// <remarks>\n\t/// Uses API <ms>AccessibleObjectFromPoint</ms>.\n\t/// </remarks>\n\tpublic static elm fromMouse(EXYFlags flags = 0) {\n\t\treturn fromXY(mouse.xy, flags);\n\t}\n\t\n\t/// <summary>\n\t/// Gets the keyboard-focused UI element.\n\t/// </summary>\n\t/// <returns><c>null</c> if failed.</returns>\n\tpublic static elm focused(EFocusedFlags flags = 0) {\n\t\tWarnInSendMessage_();\n\t\t\n\t\tvar w = wnd.focused;\n\t\tg1:\n\t\tif (w.Is0) return null;\n\t\tint hr = Cpp.Cpp_AccGetFocused(w, flags, out var a);\n\t\tif (hr != 0) {\n\t\t\tvar w2 = wnd.focused;\n\t\t\tif (w2 != w) { w = w2; goto g1; }\n\t\t\treturn null;\n\t\t}\n\t\treturn new elm(a);\n\t}\n\t\n\t/// <summary>\n\t/// Gets the UI element that generated the event that is currently being processed by the callback function used with API <ms>SetWinEventHook</ms> or <see cref=\"WinEventHook\"/>.\n\t/// </summary>\n\t/// <returns><c>null</c> if failed. Supports <see cref=\"lastError\"/>.</returns>\n\t/// <param name=\"w\"></param>\n\t/// <param name=\"idObject\"></param>\n\t/// <param name=\"idChild\"></param>\n\t/// <remarks>\n\t/// The parameters are of the callback function.\n\t/// Uses API <ms>AccessibleObjectFromEvent</ms>.\n\t/// Often fails because the UI element already does not exist, because the callback function is called asynchronously, especially when the event is <c>OBJECT_DESTROY</c>, <c>OBJECT_HIDE</c>, <c>SYSTEM_xEND</c>.\n\t/// Returns <c>null</c> if failed. Always check the return value, to avoid <see cref=\"NullReferenceException\"/>. An exception in the callback function kills this process.\n\t/// </remarks>\n\tpublic static elm fromEvent(wnd w, EObjid idObject, int idChild) {\n\t\tint hr = Api.AccessibleObjectFromEvent(w, idObject, idChild, out var iacc, out var v);\n\t\tif (hr == 0 && iacc == default) hr = Api.E_FAIL;\n\t\tif (hr != 0) { lastError.code = hr; return null; }\n\t\tint elem = v.vt == Api.VARENUM.VT_I4 ? v.ValueInt : 0;\n\t\treturn new elm(iacc, elem);\n\t}\n\t\n#if false //rejected: not useful. Maybe in the future.\n\t/// <summary>\n\t/// Gets UI element from a COM object of any type that supports it.\n\t/// </summary>\n\t/// <returns><c>null</c> if failed.</returns>\n\t/// <param name=\"x\">Unmanaged COM object.</param>\n\t/// <remarks>\n\t/// The COM object type can be <c>IAccessible</c>, <c>IAccessible2</c>, <c>IHTMLElement</c>, <c>ISimpleDOMNode</c> or any other COM interface type that can give <ms>IAccessible</ms> interface pointer through API <ms>IUnknown.QueryInterface</ms> or <ms>IServiceProvider.QueryService</ms>.\n\t/// For <c>IHTMLElement</c> and <c>ISimpleDOMNode</c> returns <c>null</c> if the HTML element is not an accessible object. Then you can try to get UI element of its parent HTML element, parent's parent and so on, until succeeds.\n\t/// </remarks>\n\tpublic static elm fromComObject(IntPtr x)\n\t{\n\t\tif(x == default) return null;\n\t\tif(MarshalUtil.QueryInterface(x, out IntPtr iacc, Api.IID_IAccessible)\n\t\t\t|| MarshalUtil.QueryService(x, out iacc, Api.IID_IAccessible)\n\t\t\t) return new elm(iacc);\n\t\treturn null;\n\t}\n\n\t/// <summary>\n\t/// Gets UI element from a COM object of any type that supports it.\n\t/// Returns <c>null</c> if failed.\n\t/// </summary>\n\t/// <param name=\"x\">Managed COM object.</param>\n\t/// <remarks>\n\t/// The COM object type can be <c>IAccessible</c>, <c>IAccessible2</c>, <c>IHTMLElement</c>, <c>ISimpleDOMNode</c> or any other COM interface type that can give <ms>IAccessible</ms> interface pointer through API <ms>IUnknown.QueryInterface</ms> or <ms>IServiceProvider.QueryService</ms>.\n\t/// For <c>IHTMLElement</c> and <c>ISimpleDOMNode</c> returns <c>null</c> if the HTML element is not an accessible object. Then you can try to get UI element of its parent HTML element, parent's parent and so on, until succeeds.\n\t/// </remarks>\n\tpublic static elm fromComObject(object x)\n\t{\n\t\tif(x == null) return null;\n\n\t\t//FUTURE: support UIA. Don't use LegacyIAccessible, it work not with all windows. Instead wrap in UIAccessible.\n\t\t//if(x is UIA.IElement e) { //info: IElement2-7 are IElement too\n\t\t//\tvar pat = e.GetCurrentPattern(UIA.PatternId.LegacyIAccessible) as UIA.ILegacyIAccessiblePattern;\n\t\t//\tx = pat?.GetIAccessible();\n\t\t//\tif(x == null) return null;\n\t\t//}\n\n\t\tvar ip = Marshal.GetIUnknownForObject(x);\n\t\tif(ip == default) return null;\n\t\ttry { return FromComObject(ip); }\n\t\tfinally { Marshal.Release(ip); }\n\t}\n#endif\n\t\n\t/// <summary>\n\t/// Used only for debug.\n\t/// </summary>\n\tenum _FuncId { name = 1, value, description, default_action, role, state, rectangle, parent_object, child_object, container_window, child_count, child_objects, help_text, keyboard_shortcut, html, selection, uiaid, uiacn }\n\t\n\t/// <summary>\n\t/// Calls <c>SetLastError</c> and returns <i>hr</i>.\n\t/// In Debug config also outputs error in red.\n\t/// If hr looks like not an error but just the property or action is unavailable, changes it to <c>S_FALSE</c> and does not show error. These are: <c>S_FALSE</c>, <c>DISP_E_MEMBERNOTFOUND</c>, <c>E_NOTIMPL</c>.\n\t/// <c>_FuncId</c> also can be <c>char</c>, like <c>(_FuncId)'n'</c> for name.\n\t/// </summary>\n\tint _Hresult(_FuncId funcId, int hr) {\n\t\tif (hr != 0) {\n\t\t\tswitch (hr) {\n\t\t\tcase Api.DISP_E_MEMBERNOTFOUND: case Api.E_NOTIMPL: hr = Api.S_FALSE; break;\n\t\t\tcase (int)Cpp.EError.InvalidParameter: throw new ArgumentException(\"Invalid argument value.\");\n\t\t\tdefault: Debug.Assert(!Cpp.IsCppError(hr)); break;\n\t\t\t}\n#if DEBUG\n\t\t\tif (hr != Api.S_FALSE) {\n\t\t\t\t_DebugPropGet(funcId, hr);\n\t\t\t}\n#endif\n\t\t}\n\t\tlastError.code = hr;\n\t\treturn hr;\n\t}\n\t\n#if DEBUG\n\tvoid _DebugPropGet(_FuncId funcId, int hr) {\n\t\tif (t_debugNoRecurse || _Disposed) return;\n\t\t\n\t\tif (funcId >= (_FuncId)'A') {\n\t\t\tswitch ((char)funcId) {\n\t\t\tcase 'R': funcId = _FuncId.role; break;\n\t\t\tcase 'n': funcId = _FuncId.name; break;\n\t\t\tcase 'v': funcId = _FuncId.value; break;\n\t\t\tcase 'd': funcId = _FuncId.description; break;\n\t\t\tcase 'h': funcId = _FuncId.help_text; break;\n\t\t\tcase 'a': funcId = _FuncId.default_action; break;\n\t\t\tcase 'k': funcId = _FuncId.keyboard_shortcut; break;\n\t\t\tcase 's': funcId = _FuncId.state; break;\n\t\t\tcase 'r': funcId = _FuncId.rectangle; break;\n\t\t\tcase 'u': funcId = _FuncId.uiaid; break;\n\t\t\tcase 'U': funcId = _FuncId.uiacn; break;\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (hr == Api.E_FAIL && funcId == _FuncId.default_action) return; //many in old VS etc\n\t\tt_debugNoRecurse = true;\n\t\ttry {\n\t\t\tvar s = ToString();\n\t\t\tprint.it($\"<><c #ff>-{funcId}, 0x{hr:X} - {lastError.messageFor(hr)}    {s}</c>\");\n\t\t}\n\t\tfinally { t_debugNoRecurse = false; }\n\t}\n\t[ThreadStatic] static bool t_debugNoRecurse;\n#endif\n\t\n\t/// <summary>\n\t/// Formats string from main properties of this UI element.\n\t/// </summary>\n\t/// <remarks>\n\t/// The string starts with role. Other properties have format like <c>x=\"value\"</c>, where <c>x</c> is a property character like with <see cref=\"GetProperties\"/>; character <c>e</c> is <see cref=\"Item\"/>. HTML attributes have format <c>@name=\"value\"</c>. In string values are used C# escape sequences, for example <c>\\r\\n</c> for new line.\n\t/// Indentation depends on <see cref=\"Level\"/>.\n\t/// </remarks>\n\tpublic override string ToString() {\n\t\tif (_Disposed) return \"<disposed>\";\n\t\tif (!GetProperties(\"Rnsvdarw@\", out var k)) return \"<failed>\";\n\t\t\n\t\tusing (new StringBuilder_(out var b)) {\n\t\t\tif (Level > 0) b.Append(' ', Level);\n\t\t\tb.Append(k.Role);\n\t\t\t_Add('n', k.Name);\n\t\t\tif (k.State != 0) _Add('s', k.State.ToString(), '(', ')');\n\t\t\t_Add('v', k.Value);\n\t\t\t_Add('d', k.Description);\n\t\t\t_Add('a', k.DefaultAction);\n\t\t\tif (!k.Rect.Is0) _Add('r', k.Rect.ToString(), '\\0', '\\0');\n\t\t\tif (Item != 0) b.Append(\",  e=\").Append(Item);\n\t\t\tforeach (var kv in k.HtmlAttributes) {\n\t\t\t\tb.Append(\",  @\").Append(kv.Key).Append('=').Append('\"');\n\t\t\t\tb.Append(kv.Value.Escape(limit: 250)).Append('\"');\n\t\t\t}\n\t\t\t_Add('w', k.WndContainer.ClassName ?? \"\");\n\t\t\t\n\t\t\tvoid _Add(char name, string value, char q1 = '\"', char q2 = '\"') {\n\t\t\t\tif (value.Length == 0) return;\n\t\t\t\tvar t = value; if (q1 == '\"') t = t.Escape(limit: 250);\n\t\t\t\tb.Append(\",  \").Append(name).Append('=');\n\t\t\t\tif (q1 != '\\0') b.Append(q1);\n\t\t\t\tb.Append(t);\n\t\t\t\tif (q1 != '\\0') b.Append(q2);\n\t\t\t}\n\t\t\t\n\t\t\treturn b.ToString();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au/UI objects/elmFinder.cs",
    "content": "namespace Au;\n\n/// <summary>\n/// Finds UI elements (<see cref=\"elm\"/>). Contains name and other parameters of elements to find.\n/// </summary>\n/// <example>\n/// Find link <c>\"Example\"</c> in web page, and click. Wait max 5 s. Exception if not found.\n/// <code><![CDATA[\n/// var w = wnd.find(0, \"* Chrome\");\n/// w.Elm[\"web:LINK\", \"Example\"].Find(5).Invoke();\n/// ]]></code>\n/// Find window that contains certain UI element, and get the UI element too.\n/// <code><![CDATA[\n/// var f = new elmFinder(\"BUTTON\", \"Apply\"); //or var f = elm.path[\"BUTTON\", \"Apply\"];\n/// wnd w = wnd.find(cn: \"#32770\", also: t => f.In(t).Exists()); //or t => t.HasElm(f)\n/// print.it(w);\n/// print.it(f.Result);\n/// ]]></code>\n/// </example>\npublic unsafe class elmFinder {\n\treadonly string _role, _name, _prop, _navig;\n\treadonly EFFlags _flags;\n\treadonly int _skip;\n\treadonly Func<elm, bool> _also;\n\tCpp.Cpp_AccFindCallbackT _also2;\n\telmFinder _next;\n\tchar _resultProp;\n\twnd _wnd;\n\telm _elm;\n\n\t/// <summary>\n\t/// The found UI element.\n\t/// <c>null</c> if not found or if used <see cref=\"ResultGetProperty\"/>.\n\t/// </summary>\n\tpublic elm Result { get; private set; }\n\n\t/// <summary>\n\t/// The requested property of the found UI element, depending on <see cref=\"ResultGetProperty\"/>.\n\t/// <c>null</c> if: 1. UI element not found. 2. <c>ResultGetProperty</c> not used or is <c>'-'</c>. 3. Failed to get the property.\n\t/// </summary>\n\t/// <remarks>\n\t/// The type depends on the property. Most properties are of type <c>String</c>. Others: <see cref=\"elm.Rect\"/>, <see cref=\"elm.State\"/>, <see cref=\"elm.WndContainer\"/>, <see cref=\"elm.HtmlAttributes\"/>.\n\t/// </remarks>\n\tpublic object ResultProperty { get; private set; }\n\n\t/// <summary>\n\t/// Set this when you need only some property of the UI element (name, etc) and not the UI element itself.\n\t/// The value is a character like with <see cref=\"elm.GetProperties\"/>, for example <c>'n'</c> for <c>Name</c>. Use <c>'-'</c> if you don't need any property.\n\t/// </summary>\n\t/// <exception cref=\"ArgumentException\">Used parameter <i>also</i>, <i>navig</i> or <i>next</i>.</exception>\n\tpublic char ResultGetProperty {\n\t\tset {\n\t\t\tif (_also != null) throw new ArgumentException(\"ResultGetProperty cannot be used with parameter 'also'.\");\n\t\t\tif (_navig != null) throw new ArgumentException(\"ResultGetProperty cannot be used with parameter 'navig'.\");\n\t\t\tif (_next != null) throw new ArgumentException(\"ResultGetProperty cannot be used with a path finder.\");\n\t\t\t_resultProp = value;\n\t\t}\n\t}\n\n\tvoid _ClearResult() {\n\t\tResult = null;\n\t\tResultProperty = null;\n\t}\n\n\t/// <summary>\n\t/// Stores the specified UI element properties in this object.\n\t/// </summary>\n\t/// <inheritdoc cref=\"this\" path=\"//param|//remarks\"/>\n\tpublic elmFinder(string role = null,\n\t\t[ParamString(PSFormat.Wildex)] string name = null,\n\t\tStrings prop = default, EFFlags flags = 0, Func<elm, bool> also = null,\n\t\tint skip = 0, string navig = null\n\t\t) {\n\t\t_role = role;\n\t\t_name = name;\n\t\t_prop = prop.Value switch { null => null, string s => s.Replace('|', '\\0'), _ => string.Join('\\0', prop.ToArray()) };\n\t\t_flags = flags;\n\t\t_also = also;\n\t\t_skip = skip >= -1 ? skip : throw new ArgumentOutOfRangeException(nameof(skip));\n\t\t_navig = navig;\n\t}\n\n\tinternal elmFinder(wnd w, elm e) {\n\t\t_wnd = w;\n\t\t_elm = e;\n\t\t_flags = _EFFlags_Empty;\n\t}\n\n\tconst EFFlags _EFFlags_Empty = (EFFlags)0x10000000;\n\n\t/// <summary>\n\t/// Creates an <see cref=\"elmFinder\"/> for finding a UI element. Supports path like <c>var e = w.Elm[\"ROLE1\", \"Name1\"][\"ROLE2\", \"Name2\"].Find();</c>.\n\t/// </summary>\n\t/// <returns>The new finder or the first finder in path.</returns>\n\t/// <param name=\"role\">\n\t/// UI element role (<see cref=\"elm.Role\"/>), like <c>\"LINK\"</c>.\n\t/// Can have prefix <c>\"web:\"</c>, <c>\"firefox:\"</c> or <c>\"chrome:\"</c> which means \"search only in web page\" and enables Chrome UI elements.\n\t/// Case-sensitive. Not wildcard. <c>null</c> means \"can be any\". Cannot be <c>\"\"</c>.\n\t/// More info in Remarks.\n\t/// </param>\n\t/// <param name=\"name\">\n\t/// UI element name (<see cref=\"elm.Name\"/>).\n\t/// String format: [wildcard expression](xref:wildcard_expression).\n\t/// <c>null</c> means \"any\". <c>\"\"</c> means \"empty or unavailable\".\n\t/// </param>\n\t/// <param name=\"prop\">\n\t/// Other UI element properties and search settings.\n\t/// Examples: <c>\"value=xxx|@href=yyy\"</c>, <c>new(\"value=xxx\", \"@href=yyy\")</c>.\n\t/// More info in Remarks.\n\t/// </param>\n\t/// <param name=\"flags\"></param>\n\t/// <param name=\"also\">\n\t/// Callback function. Called for each matching UI element. Let it return <c>true</c> if this is the wanted UI element.\n\t/// Example: the UI element must contain point x y: <c>o => o.GetRect(out var r, o.WndTopLevel) &amp;&amp; r.Contains(266, 33)</c>\n\t/// </param>\n\t/// <param name=\"skip\">\n\t/// 0-based index of matching UI element to use. Will skip this number of matching elements.\n\t/// Value -1 means \"any\", and can be useful when this finder is intermediate (ie not the last) in a path or when it has <i>navig</i>. If intermediate, will search for next element in all matching intermediate elements. If has <i>navig</i>, will retry with other matching elements if fails to navigate in the first found. It is slower and not so often useful, therefore the default value of this parameter is 0, not -1.\n\t/// Cannot be used with <see cref=\"FindAll\"/>, unless it is not in the last part of path.\n\t/// </param>\n\t/// <param name=\"navig\">If not <c>null</c>, after finding the specified UI element will call <see cref=\"elm.Navigate\"/> with this string and use its result instead of the found element.</param>\n\t/// <exception cref=\"ArgumentException\"><i>flags</i> contains <c>UIA</c> or <c>ClientArea</c> when appending (only the first finder can have these flags).</exception>\n\t/// <remarks>\n\t/// To create code for this function, use tool <b>Find UI element</b>.\n\t/// \n\t/// In [wildcard expression](xref:wildcard_expression) supports PCRE regular expressions (prefix <c>\"**r \"</c>) but not .NET regular expressions (prefix <c>\"**R \"</c>). They are similar.\n\t/// \n\t/// When using path like <c>[\"ROLE1\", \"Name1\"][\"ROLE2\", \"Name2\"][\"ROLE3\", \"Name3\"]</c>, multiple finders are linked like finder1 -> finder2 -> finder3, so that the chain of finders will find UI element specified by the last finder.\n\t/// \n\t/// More info in <see cref=\"elm\"/> topic.\n\t/// \n\t/// <h5>About the <i>role</i> parameter</h5>\n\t/// \n\t/// Can be standard role (see <see cref=\"ERole\"/>) like <c>\"LINK\"</c> or custom role like <c>\"div\"</c>. See <see cref=\"elm.Role\"/>.\n\t/// \n\t/// Can have a prefix:\n\t/// - <c>\"web:\"</c> - search only in the visible web page, not in whole window. Example: <c>\"web:LINK\"</c>.\\\n\t///   Supports Chrome, Firefox, Internet Explorer (IE) and apps that use same code (Edge, Opera...). With other windows, searches in the first found visible UI element that has <c>DOCUMENT</c> role.\\\n\t///   Tip: To search only NOT in web pages, use <i>prop</i> <c>\"notin=DOCUMENT\"</c> (Chrome, Firefox) or <c>\"notin=PANE\"</c> (IE).\n\t/// - <c>\"firefox:\"</c> - search only in the visible web page of Firefox or Firefox-based web browser. If <i>w</i> window class name starts with <c>\"Mozilla\"</c>, can be used <c>\"web:\"</c> instead.\n\t/// - <c>\"chrome:\"</c> - search only in the visible web page of Chrome or Chrome-based web browser. If <i>w</i> window class name starts with <c>\"Chrome\"</c>, can be used <c>\"web:\"</c> instead.\n\t/// \n\t/// <note>Chrome web page UI elements normally are disabled (don't exist). Use prefix <c>\"web:\"</c> or <c>\"chrome:\"</c> to enable.</note>\n\t/// \n\t/// Prefix cannot be used:\n\t/// - if <i>prop</i> contains <c>\"id\"</c> or <c>\"class\"</c>;\n\t/// - with flags <c>UIA</c>, <c>ClientArea</c>;\n\t/// - when searching in <see cref=\"elm\"/>.\n\t/// \n\t/// <h5>About the <i>prop</i> parameter</h5>\n\t/// \n\t/// Format: one or more <c>\"name=value\"</c> strings, like <c>new(\"key=xxx\", \"@href=yyy\")</c> or <c>\"key=xxx|@href=yyy\"</c>. Names must match case. Values of most string properties are [wildcard expressions](xref:wildcard_expression).\n\t/// \n\t/// - <c>\"class\"</c> - search only in child controls that have this class name (see <see cref=\"wnd.ClassName\"/>).\\\n\t///   Cannot be used when searching in a UI element.\n\t/// - <c>\"id\"</c> - search only in child controls that have this id (see <see cref=\"wnd.ControlId\"/>). If the value is not a number - Windows Forms control name (see <see cref=\"wnd.NameWinforms\"/>); case-sensitive, not wildcard.\\\n\t///   Cannot be used when searching in a UI element.\n\t/// - <c>\"value\"</c> - <see cref=\"elm.Value\"/>.\n\t/// - <c>\"desc\"</c> - <see cref=\"elm.Description\"/>.\n\t/// - <c>\"state\"</c> - <see cref=\"elm.State\"/>. List of states the UI element must have and/or not have.\\\n\t///   Example: <c>\"state=CHECKED, FOCUSABLE, !DISABLED\"</c>.\\\n\t///   Example: <c>\"state=0x100010, !0x1\"</c>.\\\n\t///   Will find UI element that has all states without <c>\"!\"</c> prefix and does not have any of states with <c>\"!\"</c> prefix.\n\t/// - <c>\"rect\"</c> - <see cref=\"elm.GetRect(out RECT, bool)\"/> with <i>raw</i> <c>true</c>. Can be specified left, top, width and/or height, using <see cref=\"RECT.ToString\"/> format.\\\n\t///   Example: <c>\"rect={L=1155 T=1182 W=132 H=13}\"</c>.\n\t///   Example: <c>\"rect={W=132 T=1182}\"</c>.\n\t///   The <c>L T</c> coordinates are relative to the primary screen.\n\t/// - <c>\"level\"</c> - level (see <see cref=\"elm.Level\"/>) at which the UI element can be found. Can be exact level, or minimal and maximal level separated by space.\\\n\t///   The default value is 0 1000.\n\t/// - <c>\"item\"</c> - <see cref=\"elm.Item\"/>.\n\t/// - <c>\"action\"</c> - <see cref=\"elm.DefaultAction\"/>.\n\t/// - <c>\"key\"</c> - <see cref=\"elm.KeyboardShortcut\"/>.\n\t/// - <c>\"help\"</c> - <see cref=\"elm.Help\"/>.\n\t/// - <c>\"uiaid\"</c> - <see cref=\"elm.UiaId\"/>.\n\t/// - <c>\"uiacn\"</c> - <see cref=\"elm.UiaCN\"/>.\n\t/// - <c>\"url\"</c> - URL of the container DOCUMENT in Chromium-based web browser. Used together with role prefix <c>\"web:\"</c> or <c>\"chrome:\"</c>. Use when the document is a side panel or developer tools or a special page like Settings. Don't need (but can be used too) when the document is a web page (URL starts with <c>\"https:\"</c>, <c>\"http:\"</c> or <c>\"file:\"</c>).\n\t/// - <c>\"maxcc\"</c> - when searching, skip children of UI elements that have more than this number of direct children. Default 10000, min 1, max 1000000.\\\n\t///   It can make faster. It also prevents hanging or crashing when a UI element in the UI element tree has large number of children. For example OpenOffice Calc <c>TABLE</c> has one billion children.\n\t/// - <c>\"notin\"</c> - when searching, skip children of UI elements that have these roles. It can make faster.\\\n\t///   Example: <c>\"notin=TREE,LIST,TOOLBAR\"</c>.\\\n\t///   Roles in the list must be separated with <c>\",\"</c> or <c>\", \"</c>. Case-sensitive, not wildcard. See also: <see cref=\"EFFlags.MenuToo\"/>.\n\t/// - <c>\"@attr\"</c> - <see cref=\"elm.HtmlAttribute\"/>. Here <c>\"attr\"</c> is any attribute name. Example: <c>\"@href=example\"</c>.\n\t/// </remarks>\n\t/// <seealso cref=\"elm.path\"/>\n\t/// <seealso cref=\"Next\"/>\n\tpublic elmFinder this[string role = null,\n\t\t[ParamString(PSFormat.Wildex)] string name = null,\n\t\tStrings prop = default, EFFlags flags = 0, Func<elm, bool> also = null,\n\t\tint skip = 0, string navig = null\n\t\t] {\n\t\tget {\n\t\t\tvar f = new elmFinder(role, name, prop, flags, also, skip, navig);\n\t\t\tif (_flags == _EFFlags_Empty) { f._wnd = _wnd; f._elm = _elm; return f; }\n\t\t\t_Last().Next = f;\n\t\t\treturn this;\n\t\t}\n\t}\n\t//rejected: default skip = -1. Rarely need.\n\t//\tIn some cases could find an unexpected element. Better to not find than to find (and click etc) wrong element.\n\t//\tInstead the tool gives info to try -1 when not found or found wrong element.\n\n\t/// <summary>\n\t/// Gets or sets next finder in path (immediately after this finder).\n\t/// </summary>\n\t/// <exception cref=\"ArgumentException\"><i>flags</i> contains <c>UIA</c> or <c>ClientArea</c>.</exception>\n\t/// <remarks>\n\t/// The setter creates or modifies a path (a chain of linked finders). Unlike <see cref=\"this\"/>, which appends to the last finder in path, this function appends to this finder.\n\t/// </remarks>\n\tpublic elmFinder Next {\n\t\tget => _next;\n\t\tset {\n\t\t\tif (_flags.Has(_EFFlags_Empty)) throw new InvalidOperationException();\n\t\t\tif (value != _next) {\n\t\t\t\tif (value != null) {\n\t\t\t\t\tif (value._flags.HasAny(EFFlags.UIA | EFFlags.ClientArea)) throw new ArgumentException(\"Don't use flags UIA and ClientArea when searching in elm.\");\n\t\t\t\t}\n\t\t\t\t_next = value;\n\t\t\t\t_also2 = null;\n\t\t\t}\n\t\t}\n\t}\n\n\telmFinder _Last() {\n\t\tvar n = this; while (n._next != null) n = n._next;\n\t\treturn n;\n\t}\n\n\t/// <summary>\n\t/// Sets or changes window or control where <see cref=\"Find\"/> etc will search.\n\t/// </summary>\n\t/// <returns>This.</returns>\n\t/// <seealso cref=\"elm.path\"/>\n\tpublic elmFinder In(wnd w) {\n\t\tif (_flags.Has(_EFFlags_Empty)) throw new InvalidOperationException();\n\t\t_wnd = w;\n\t\treturn this;\n\t}\n\n\t/// <summary>\n\t/// Sets or changes parent UI element where <see cref=\"Find\"/> etc will search.\n\t/// </summary>\n\t/// <returns>This.</returns>\n\t/// <seealso cref=\"elm.path\"/>\n\tpublic elmFinder In(elm e) {\n\t\tif (_flags.Has(_EFFlags_Empty)) throw new InvalidOperationException();\n\t\t_elm = e;\n\t\treturn this;\n\t}\n\n\t/// <summary>\n\t/// Finds the first matching descendant UI element in the window or UI element.\n\t/// </summary>\n\t/// <returns>If found, returns <see cref=\"Result\"/>, else <c>null</c>.</returns>\n\t/// <exception cref=\"ArgumentException\">\n\t/// - <i>role</i> is <c>\"\"</c> or invalid.\n\t/// - <i>name</i> is invalid wildcard expression (<c>\"**options \"</c> or regular expression).\n\t/// - <i>prop</i> contains unknown property names or errors in wildcard expressions.\n\t/// - <i>navig</i> string is invalid.\n\t/// - <i>flags</i> has <c>UIA</c> or <c>ClientArea</c> when searching in web page (role prefix <c>\"web:\"</c> etc) or <see cref=\"elm\"/>.\n\t/// - <i>role</i> has a prefix (<c>\"web:\"</c> etc) when searching in <see cref=\"elm\"/>.\n\t/// - <see cref=\"elm.Item\"/> not 0 when searching in <see cref=\"elm\"/>.\n\t/// </exception>\n\t/// <exception cref=\"AuWndException\">Invalid window handle (0 or closed). See also <see cref=\"In(wnd)\"/>.</exception>\n\t/// <exception cref=\"AuException\">Failed. For example, window of a higher [](xref:uac) integrity level process.</exception>\n\t/// <remarks>\n\t/// To create code for this function, use tool <b>Find UI element</b>.\n\t/// \n\t/// More info in <see cref=\"elm\"/> topic.\n\t/// </remarks>\n\t/// <example>\n\t/// Find link <c>\"Example\"</c> in web page, and click. Wait max 5 s. Throw <see cref=\"NotFoundException\"/> if not found.\n\t/// <code><![CDATA[\n\t/// var w = wnd.find(0, \"* Chrome\");\n\t/// w.Elm[\"web:LINK\", \"Example\"].Find(5).Invoke();\n\t/// ]]></code>\n\t/// Try to find link <c>\"Example\"</c> in web page. Return if not found. Click if found.\n\t/// <code><![CDATA[\n\t/// var w = wnd.find(0, \"* Chrome\");\n\t/// var e = w.Elm[\"web:LINK\", \"Example\"].Find();\n\t/// //var e = w.Elm[\"web:LINK\", \"Example\"].Find(-5); //waits max 5 s\n\t/// if(e == null) { print.it(\"not found\"); return; }\n\t/// e.Invoke();\n\t/// ]]></code>\n\t/// </example>\n\tpublic elm Find() => Exists() ? Result : null;\n\n\t/// <summary>\n\t/// Finds the first matching descendant UI element in the window or UI element. Can wait and throw <see cref=\"NotFoundException\"/>.\n\t/// </summary>\n\t/// <returns>If found, returns <see cref=\"Result\"/>. Else throws exception or returns <c>null</c> (if <i>wait</i> negative).</returns>\n\t/// <param name=\"wait\">The wait timeout, seconds. If 0, does not wait. If negative, does not throw exception when not found.</param>\n\t/// <exception cref=\"NotFoundException\" />\n\t/// <inheritdoc cref=\"Find()\" path=\"/exception\"/>\n\tpublic elm Find(Seconds wait) => Exists(wait) ? Result : null;\n\n\t/// <summary>\n\t/// Finds the first matching descendant UI element in the window or UI element. Like <see cref=\"Find\"/>, just different return type.\n\t/// </summary>\n\t/// <returns>If found, sets <see cref=\"Result\"/> and returns <c>true</c>, else <c>false</c>.</returns>\n\t/// <inheritdoc cref=\"Find()\" path=\"/exception\"/>\n\tpublic bool Exists() => Find_(_elm != null, _wnd, _elm);\n\n\t/// <summary>\n\t/// Finds the first matching descendant UI element in the window or UI element. Can wait and throw <see cref=\"NotFoundException\"/>. Like <see cref=\"Find(Seconds)\"/>, just different return type.\n\t/// </summary>\n\t/// <returns>If found, sets <see cref=\"Result\"/> and returns <c>true</c>. Else throws exception or returns <c>false</c> (if <i>wait</i> negative).</returns>\n\t/// <inheritdoc cref=\"Find(Seconds)\" path=\"//param|//exception\"/>\n\tpublic bool Exists(Seconds wait) {\n\t\tif (Find_(_elm != null, _wnd, _elm, wait.Exists_() ? null : wait)) return true;\n\t\treturn wait.ReturnFalseOrThrowNotFound_();\n\t}\n\n\t/// <summary>\n\t/// Waits for a matching descendant UI element to appear in the window or UI element.\n\t/// </summary>\n\t/// <returns>If found, returns <see cref=\"Result\"/>. On timeout returns <c>null</c> if <i>timeout</i> is negative; else exception.</returns>\n\t/// <param name=\"timeout\">Timeout, seconds. Can be 0 (infinite), >0 (exception) or &lt;0 (no exception). More info: [](xref:wait_timeout).</param>\n\t/// <exception cref=\"TimeoutException\" />\n\t/// <remarks>\n\t/// Same as <see cref=\"Find(Seconds)\"/>, except:\n\t/// - 0 timeout means infinite.\n\t/// - on timeout throws <see cref=\"TimeoutException\"/>, not <see cref=\"NotFoundException\"/>.\n\t/// </remarks>\n\t/// <inheritdoc cref=\"Find()\" path=\"/exception\"/>\n\tpublic elm Wait(Seconds timeout) => Find_(_elm != null, _wnd, _elm, timeout) ? Result : null;\n\n\t//CONSIDER: public bool WaitNot(Seconds timeout) => wait.until(timeout, () => !Exists());\n\n\tinternal bool Find_(bool inElm, wnd w, elm eParent, Seconds? waitS = null, bool isNext = false, bool fromFindAll = false) {\n\t\tif (_flags.Has(_EFFlags_Empty) && !fromFindAll) throw new InvalidOperationException();\n\t\tif (_flags.Has(EFFlags.UIA | EFFlags.ClientArea)) throw new ArgumentException(\"Don't use flags UIA and ClientArea together.\");\n\t\tif (_role != null) {\n\t\t\tif (_role == \"\") throw new ArgumentException(\"role cannot be \\\"\\\".\");\n\t\t\tif (inElm && 0 != _role.Starts(false, \"web:\", \"chrome:\", \"firefox:\")) throw new ArgumentException(\"Don't use role prefix when searching in elm.\");\n\t\t}\n\n\t\tCpp.Cpp_Acc aParent; Cpp.Cpp_Acc* pParent = null;\n\t\tEFFlags flags = _flags;\n\t\tif (inElm) {\n\t\t\tif (!isNext) {\n\t\t\t\tif (_flags.HasAny(EFFlags.UIA | EFFlags.ClientArea)) throw new ArgumentException(\"Don't use flags UIA and ClientArea when searching in elm.\");\n\t\t\t\tif (eParent == null) throw new ArgumentNullException();\n\t\t\t\teParent.ThrowIfDisposed_();\n\t\t\t\tif (eParent.Item != 0) throw new ArgumentException(\"Item not 0.\");\n\t\t\t}\n\t\t\taParent = new(eParent); pParent = &aParent;\n\t\t\tif (!eParent.MiscFlags.Has(EMiscFlags.InProc)) flags |= EFFlags.NotInProc;\n\t\t} else {\n\t\t\tw.ThrowIfInvalid();\n\t\t\taParent = default;\n\t\t}\n\n\t\telm.WarnInSendMessage_();\n\n\t\tbool inProc = !flags.Has(EFFlags.NotInProc);\n\n\t\t_ClearResult();\n\n\t\tvar ap = new Cpp.Cpp_AccFindParams(_role, _name, _prop, flags, Math.Max(0, _skip), _resultProp);\n\n\t\tif (!inElm) ap.RolePrefix(w); //converts role prefix to flags (C++ internal) and may enable Chrome AOs\n\n\t\t//if used skip<0 and path or navig, need to search in all possible paths. For it we use the 'also' callback.\n\t\t//FUTURE: optimize. Add part of code to the C++ dll. Now can walk same tree branches multiple times.\n\t\tCpp.Cpp_AccFindCallbackT also = null;\n\t\tbool allPaths = _skip < 0 && (_next != null || _navig != null);\n\t\tbool findAll = t_findAll?.ContainsKey(this) == true; //in FindAll. This is the last finder in path.\n\t\tif (allPaths) {\n\t\t\talso = _also2 ??= (ca, _) => {\n\t\t\t\tvar e = new elm(ca);\n\t\t\t\tif (!_AlsoNavigNext(ref e)) return 0;\n\t\t\t\tResult = e;\n\t\t\t\treturn 1;\n\t\t\t};\n\t\t} else if (findAll) {\n\t\t\talso = (ca, _) => {\n\t\t\t\tvar e = new elm(ca);\n\t\t\t\t_AlsoNavigNext(ref e);\n\t\t\t\treturn 0;\n\t\t\t};\n\t\t} else {\n\t\t\tif (_also != null) also = _also2 ??= (ca, _) => _also(new elm(ca)) ? 1 : 0;\n\t\t}\n\n\t\tSeconds seconds = waitS ?? new(-1);\n\t\tseconds.Period ??= inProc ? 10 : 40;\n\t\tvar loop = new WaitLoop(seconds);\n\t\tfor (bool doneUAC = false, doneThread = false; ;) {\n\t\t\tvar hr = Cpp.Cpp_AccFind(w, pParent, ap, also, out var ca, out string sResult);\n\t\t\tif (findAll) {\n\t\t\t\tGC.KeepAlive(also);\n\t\t\t\tif (hr == Cpp.EError.NotFound) return false;\n\t\t\t\t//else error. Cannot be 0.\n\t\t\t}\n\n\t\t\tif (hr == 0) {\n\t\t\t\tswitch (_resultProp) {\n\t\t\t\tcase '\\0':\n\t\t\t\t\tif (!allPaths) {\n\t\t\t\t\t\tvar e = new elm(ca);\n\t\t\t\t\t\tif (_AlsoNavigNext(ref e, noAlso: true)) Result = e; else hr = Cpp.EError.NotFound;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'r' or 'D' or 's' or 'w' or '@':\n\t\t\t\t\tif (sResult == null) break;\n\t\t\t\t\tunsafe {\n\t\t\t\t\t\tfixed (char* p = sResult) {\n\t\t\t\t\t\t\tswitch (_resultProp) {\n\t\t\t\t\t\t\tcase 'r' or 'D': ResultProperty = *(RECT*)p; break;\n\t\t\t\t\t\t\tcase 's': ResultProperty = *(EState*)p; break;\n\t\t\t\t\t\t\tcase 'w': ResultProperty = (wnd)(*(int*)p); break;\n\t\t\t\t\t\t\tcase '@': ResultProperty = elm.AttributesToDictionary_(p, sResult.Length); break;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tResultProperty = sResult;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (hr == 0) return true;\n\t\t\t}\n\n\t\t\tif (hr == Cpp.EError.InvalidParameter) throw new ArgumentException(sResult);\n\t\t\tif ((hr == Cpp.EError.WindowClosed) || (!w.Is0 && !w.IsAlive)) return false; //FUTURE: check if a is disconnected etc. Or then never wait.\n\n\t\t\tif (!doneUAC) {\n\t\t\t\tdoneUAC = true;\n\t\t\t\tw.UacCheckAndThrow_(); //CONSIDER: don't throw. Maybe show warning.\n\t\t\t}\n\n\t\t\t//print.it(hr > 0 ? $\"hr={hr}\" : $\"hr={(int)hr:X}\");\n\t\t\tif (hr == Cpp.EError.NotFound) {\n\t\t\t\tif (waitS == null) return false;\n\t\t\t} else {\n\t\t\t\tDebug.Assert(!Cpp.IsCppError((int)hr));\n\t\t\t\tif (hr == (Cpp.EError)Api.RPC_E_SERVER_CANTMARSHAL_DATA && !_flags.Has(EFFlags.NotInProc))\n\t\t\t\t\tthrow new AuException((int)hr, \"For this UI element need flag NotInProc\");\n\t\t\t\tthrow new AuException((int)hr);\n\t\t\t}\n\n\t\t\tif (!doneThread) {\n\t\t\t\tdoneThread = true;\n\t\t\t\tif (!w.Is0 && w.IsOfThisThread) return false;\n\t\t\t}\n\n\t\t\tif (!loop.Sleep()) return false;\n\t\t\tGC.KeepAlive(eParent);\n\t\t}\n\t}\n\n\tbool _AlsoNavigNext(ref elm e, bool noAlso = false) {\n\t\tif (!noAlso && _also != null && !_also(e)) return false;\n\t\tif (_navig != null) {\n\t\t\tvar e2 = e.Navigate(_navig);\n\t\t\tif (e2 == null) return false;\n\t\t\tif (t_navigResult.need) t_navigResult = (true, e, e2);\n\t\t\te = e2;\n\t\t}\n\t\tif (_next != null) {\n\t\t\tif (e.Item != 0) return false;\n\t\t\tif (!_next.Find_(true, default, e, isNext: true)) return false;\n\t\t\te = _next.Result;\n\t\t} else if (t_findAll?.TryGetValue(this, out var a) == true) {\n\t\t\ta.Add(e);\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\n\t/// <summary>\n\t/// Finds all matching descendant UI elements in the window or UI element.\n\t/// </summary>\n\t/// <returns>Array of 0 or more elements.</returns>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// var w = wnd.find(1, \"\", \"Shell_TrayWnd\");\n\t/// print.it(\"---- buttons ----\");\n\t/// print.it(w.Elm[\"BUTTON\"].FindAll());\n\t/// print.it(\"---- all ----\");\n\t/// print.it(w.Elm.FindAll());\n\t/// print.it(\"---- all buttons in elm at level 0 ----\");\n\t/// var e = w.Elm[\"TOOLBAR\", \"Running applications\"].Find();\n\t/// print.it(e.Elm[\"BUTTON\", prop: \"level=0\"].FindAll());\n\t/// print.it(\"---- all in elm ----\");\n\t/// print.it(e.Elm.FindAll());\n\t/// ]]></code>\n\t/// </example>\n\t/// <remarks>See <see cref=\"Find\"/>.</remarks>\n\t/// <inheritdoc cref=\"Find\" path=\"/exception\"/>\n\tpublic elm[] FindAll() {\n\t\tvar a = new List<elm>();\n\t\tvar last = _Last();\n\t\tif (last._skip != 0) throw new ArgumentException(\"FindAll does not support *skip* in the last part of path\");\n\t\t(t_findAll ??= new()).Add(last, a);\n\t\ttry { Find_(_elm != null, _wnd, _elm, fromFindAll: true); }\n\t\tfinally { t_findAll.Remove(last); }\n\t\treturn a.ToArray();\n\t}\n\n\t//This dictionary is used to temporarily attach the List that collects FindAll results to the elmFinder.\n\t//\tAnother way - add a field to elmFinder and somehow make it thread-safe. But elmFinder is immutable.\n\t[ThreadStatic] static Dictionary<elmFinder, List<elm>> t_findAll;\n\n\tinternal static List<(elm, RECT)> GetAllWithRect_(wnd w, EFFlags flags) {\n\t\tList<(elm, RECT)> a = [];\n\n\t\tCpp.Cpp_AccFindCallbackT also = (ca, rect) => {\n\t\t\tvar e = new elm(ca);\n\t\t\ta.Add((e, *rect));\n\t\t\treturn 0;\n\t\t};\n\t\t\n\t\tvar ap = new Cpp.Cpp_AccFindParams(null, null, null, flags, 0, default);\n\t\tCpp.Cpp_AccFind(w, null, ap, also, out var ca, out string sResult, getRects: true);\n\t\tGC.KeepAlive(also);\n\t\treturn a;\n\t\t\n\t\t//TODO2: try async. Let the in-proc code return immediately and later post results.\n\t\t//\tAlternatively can use CoMarshalInterThreadInterfaceInStream, but it makes ~50% slower if need to marshal all. Maybe can optimize to marshal only some.\n\t\t//TODO2: add `elm.MarshalToThisThread`.\n\t\t//TODO2: (heavy change) maybe instead of `IAccessible` use a UIA element. Wrap acc in it like now uia in acc.\n\t\t//\tWhy: UIA elements are free-threaded (unlike acc).\n\t\t//\tWhy: maybe then easier to wrap more UIA API.\n\t\t//\tOr some way to avoid passing COM objects to this process.\n\t}\n\n\t///\n\tpublic override string ToString() {\n\t\tusing (new StringBuilder_(out var b)) {\n\t\t\tfor (var n = this; n != null; n = n._next) n._ToString(b);\n\t\t\treturn b.ToString();\n\t\t}\n\t}\n\n\tvoid _ToString(StringBuilder b) {\n\t\tb.Append('[');\n\t\tint n = 0;\n\t\t_Add(0, null, _role);\n\t\t_Add(1, \"name\", _name);\n\t\t_Add(2, \"prop\", _prop?.Replace('\\0', '|'));\n\t\tif (_flags != 0) _Add(3, \"flags\", _flags.ToString(), false);\n\t\tif (_also != null) _Add(4, \"also\", \"...\", false);\n\t\tif (_skip != 0) _Add(5, \"skip\", _skip.ToString(), false);\n\t\t_Add(6, \"navig\", _navig);\n\t\tb.Append(']');\n\n\t\tvoid _Add(int i, string name, string value, bool isString = true) {\n\t\t\tif (value == null) return;\n\t\t\tif (n > 0) b.Append(\", \");\n\t\t\tif (i > n++) b.Append(name).Append(\": \");\n\t\t\tif (isString) value = value.Escape(limit: 50, quote: true);\n\t\t\tb.Append(value);\n\t\t}\n\t}\n\n\t[ThreadStatic] internal static (bool need, elm before, elm after) t_navigResult;\n\n\t//rejected. Rarely used. Maybe in the future, but different API, maybe ...In(controls).Find(), and move the code into _Find.\n\t///// <summary>\n\t///// Finds UI element in the specified control of window <i>w</i>.\n\t///// </summary>\n\t///// <returns>If found, returns <see cref=\"Result\"/>, else <c>null</c>.</returns>\n\t///// <param name=\"w\">Window that contains the control.</param>\n\t///// <param name=\"controls\">Control properties. This functions searches in all matching controls.</param>\n\t///// <exception cref=\"Exception\">Exceptions of <see cref=\"Find(wnd)\"/>.</exception>\n\t///// <remarks>\n\t///// Functions <c>Find</c> and <c>Exists</c> differ only in their return types.\n\t///// \n\t///// Alternatively you can specify control class name or id in role. How this function is different: 1. Allows to specify more control properties. 2. Works better/faster when the control is of a different process or thread than the parent window; else slightly slower.\n\t///// </remarks>\n\t//public elm Find(wnd w, wndChildFinder controls) => Exists(w, controls) ? Result : null;\n\n\t///// <returns>If found, sets <see cref=\"Result\"/> and returns <c>true</c>, else <c>false</c>.</returns>\n\t///// <inheritdoc cref=\"Find(wnd, wndChildFinder)\"/>\n\t//public bool Exists(wnd w, wndChildFinder controls) {\n\t//\tw.ThrowIfInvalid();\n\t//\tforeach (var c in controls.FindAll(w)) {\n\t//\t\ttry {\n\t//\t\t\tif (_Find(false, c, null)) {\n\t//\t\t\t\tcontrols.Result = c;\n\t//\t\t\t\treturn true;\n\t//\t\t\t}\n\t//\t\t}\n\t//\t\tcatch (AuException ex) when (!c.IsAlive) { Debug_.Print(ex); } //don't throw AuWndException/AuException if the window or a control is destroyed while searching, but throw AuException if eg access denied\n\t//\t}\n\t//\treturn false;\n\t//}\n\n\t//rejected. Better slightly longer code than unclear and possibly ambiguous code where you have to learn string parsing rules.\n\t//public static implicit operator elmFinder(string roleNameProp) {\n\t//\tif (roleNameProp.NE()) throw new ArgumentException();\n\t//\tint i = roleNameProp.FindAny(\",\\0\");\n\t//\tif (i < 0) return new(roleNameProp);\n\t//\tvar a = roleNameProp.Split(roleNameProp[i], 3);\n\t//\treturn new(a[0].Length > 0 ? a[0] : null, a[1], a.Length < 3 ? null : a[2]);\n\t//}\n}\n\npartial class elm {\n\t/// <summary>\n\t/// Gets an <see cref=\"elmFinder\"/> for finding UI elements in a window or UI element that can be set later with <see cref=\"elmFinder.In\"/>.\n\t/// Example: <c>var e = elm.path[\"ROLE\", \"Name\"].In(w).Find();</c>. Same as <c>var e = w.Elm[\"ROLE\", \"Name\"].Find();</c>.\n\t/// Example: <c>var e = elm.path[\"ROLE1\", \"Name1\"][\"ROLE2\", \"Name2\"][\"ROLE3\", \"Name3\"].In(w).Find();</c>.\n\t/// </summary>\n\t/// <seealso cref=\"elmFinder.this\"/>\n\tpublic static elmFinder path { get; } = new(default, null);\n\n\t/// <summary>\n\t/// Gets an <see cref=\"elmFinder\"/> for finding UI elements in this UI element.\n\t/// Example: <c>var e2 = e1.Elm[\"ROLE\", \"Name\"].Find();</c>.\n\t/// Example: <c>var e2 = e1.Elm[\"ROLE1\", \"Name1\"][\"ROLE2\", \"Name2\"][\"ROLE3\", \"Name3\"].Find();</c>.\n\t/// Example: <c>print.it(e.Elm.FindAll());</c>.\n\t/// </summary>\n\tpublic elmFinder Elm => new(default, this);\n}\n\npublic partial struct wnd {\n\t/// <summary>\n\t/// Gets an <see cref=\"elmFinder\"/> for finding UI elements in this window or control.\n\t/// Example: <c>var e = w.Elm[\"ROLE\", \"Name\"].Find();</c>.\n\t/// Example: <c>var e = w.Elm[\"ROLE1\", \"Name1\"][\"ROLE2\", \"Name2\"][\"ROLE3\", \"Name3\"].Find();</c>.\n\t/// Example: <c>print.it(w.Elm.FindAll());</c>.\n\t/// </summary>\n\tpublic elmFinder Elm => new(this, null);\n\n\t//public static wndFinder f_(\n\t//\t[ParamString(PSFormat.wildex)] string name = null,\n\t//\t[ParamString(PSFormat.wildex)] string cn = null,\n\t//\t[ParamString(PSFormat.wildex)] WOwner of = default,\n\t//\tWFlags flags = 0, Func<wnd, bool> also = null, WContains contains = default\n\t//\t) => new(name, cn, of, flags, also, contains);\n\n\t//public wnd this[\n\t//\t[ParamString(PSFormat.wildex)] string name = null,\n\t//\t[ParamString(PSFormat.wildex)] string cn = null,\n\t//\tint? id = null, WCFlags flags = 0, Func<wnd, bool> also = null, int skip = 0,\n\t//\t] {\n\t//\tget => Child(name, cn, k, also, skip);\n\t//}\n}\n"
  },
  {
    "path": "Au/UI objects/elm_func.cs",
    "content": "//FUTURE: ChildFromXY. Like wnd.ChildFromXY. Use IAccessible.accHitTest.\n\nnamespace Au {\n\tpublic unsafe partial class elm {\n\t\t/// <summary>\n\t\t/// Gets the container window or control of this UI element.\n\t\t/// </summary>\n\t\t/// <returns><c>default(wnd)</c> if failed. Supports <see cref=\"lastError\"/>.</returns>\n\t\t/// <remarks>\n\t\t/// All UI elements must support this property, but some have bugs and can fail or return a wrong window.\n\t\t/// Uses API <ms>WindowFromAccessibleObject</ms>.\n\t\t/// </remarks>\n\t\tpublic wnd WndContainer {\n\t\t\tget {\n\t\t\t\tThrowIfDisposed_();\n\t\t\t\t_Hresult(_FuncId.container_window, _GetWnd(out var w));\n\t\t\t\treturn w;\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Low-level version of <see cref=\"WndContainer\"/>. Does not call <c>ThrowIfDisposed_</c> and <c>_Hresult</c> (<c>lastError</c>).\n\t\t/// </summary>\n\t\t/// <returns><c>HRESULT</c></returns>\n\t\tint _GetWnd(out wnd w) {\n\t\t\tint hr = Cpp.Cpp_AccGetInt(this, 'w', out var i);\n\t\t\tGC.KeepAlive(this);\n\t\t\tw = (wnd)i;\n\t\t\treturn hr;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets the top-level window that contains this UI element.\n\t\t/// </summary>\n\t\t/// <returns><c>default(wnd)</c> if failed. Supports <see cref=\"lastError\"/>.</returns>\n\t\t/// <remarks>\n\t\t/// All UI elements must support this property, but some have bugs and can return <c>default(wnd)</c>.\n\t\t/// Uses API <ms>WindowFromAccessibleObject</ms> and API <ms>GetAncestor</ms>.\n\t\t/// </remarks>\n\t\tpublic wnd WndTopLevel => WndContainer.Window;\n\t\t//note: named not WndWindow, to avoid using accidentally instead of WndContainer.\n\t\t\n\t\t/// <summary>\n\t\t/// Gets location of this UI element in screen.\n\t\t/// </summary>\n\t\t/// <returns>Empty rectangle if failed or this property is unavailable. Supports <see cref=\"lastError\"/>.</returns>\n\t\t/// <remarks>\n\t\t/// Calls <see cref=\"GetRect(out RECT, bool)\"/>.\n\t\t/// Most but not all UI elements support this property.\n\t\t/// </remarks>\n\t\tpublic RECT Rect { get { GetRect(out var r); return r; } }\n\t\t\n\t\tinternal RECT RectRawDpi_ { get { GetRect(out var r, true); return r; } }\n\t\t\n\t\t/// <summary>\n\t\t/// Gets location of this UI element in screen.\n\t\t/// </summary>\n\t\t/// <returns><c>false</c> if failed or this property is unavailable. Supports <see cref=\"lastError\"/>.</returns>\n\t\t/// <param name=\"r\">Rectangle in screen coordinates.</param>\n\t\t/// <param name=\"raw\">\n\t\t/// Don't DPI-scale. When the element is in a DPI-scaled/virtualized window (see <see cref=\"Dpi.IsWindowVirtualized\"/>), the raw rectangle may not match the visible rectangle.\n\t\t/// This parameter is ignored on Windows 7 and 8.0 or if this element was retrieved not in-process.\n\t\t/// </param>\n\t\t/// <remarks>\n\t\t/// Most but not all UI elements support this property.\n\t\t/// </remarks>\n\t\tpublic bool GetRect(out RECT r, bool raw = false) {\n\t\t\tThrowIfDisposed_();\n\t\t\tif (!raw && MiscFlags.Has(EMiscFlags.InProc) && osVersion.minWin8_1) {\n\t\t\t\tif (!GetProperties(\"D\", out var p)) { r = default; return false; }\n\t\t\t\tr = p.Rect;\n\t\t\t} else {\n\t\t\t\tvar hr = _Hresult(_FuncId.rectangle, Cpp.Cpp_AccGetRect(this, out r));\n\t\t\t\tGC.KeepAlive(this);\n\t\t\t\tif (hr != 0) return false;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets location of this UI element in the client area of window <i>w</i>.\n\t\t/// </summary>\n\t\t/// <returns><c>false</c> if failed or this property is unavailable. Supports <see cref=\"lastError\"/>.</returns>\n\t\t/// <param name=\"r\">Receives rectangle in <i>w</i> client area coordinates.</param>\n\t\t/// <param name=\"w\">Window or control.</param>\n\t\t/// <param name=\"intersect\">Intersect the rectangle with the <i>w</i> client area, possibly making it smaller or empty.</param>\n\t\t/// <remarks>\n\t\t/// Most but not all UI elements support this property.\n\t\t/// Uses <see cref=\"GetRect(out RECT, bool)\"/> and <see cref=\"wnd.MapScreenToClient(ref RECT)\"/>.\n\t\t/// </remarks>\n\t\tpublic bool GetRect(out RECT r, wnd w, bool intersect = false) {\n\t\t\tif (!(GetRect(out r) && w.MapScreenToClient(ref r))) return false;\n\t\t\tif (intersect) r.Intersect(_GetContainerClientRect(w));\n\t\t\treturn true;\n\t\t\t\n\t\t\tRECT _GetContainerClientRect(wnd w) {\n\t\t\t\tvar rc = w.ClientRect;\n\t\t\t\t//if w is a classic listview in report view, exclude header\n\t\t\t\tif (Item > 0 && RoleInt == ERole.LISTITEM) {\n\t\t\t\t\tvar h = w.ChildFast(null, \"SysHeader32\");\n\t\t\t\t\tif (h.IsVisible) rc.top = h.Rect.Height;\n\t\t\t\t}\n\t\t\t\treturn rc;\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets role as enum <see cref=\"ERole\"/>.\n\t\t/// </summary>\n\t\t/// <returns>0 (<c>ERole.None</c>) if failed. Supports <see cref=\"lastError\"/>.</returns>\n\t\t/// <remarks>\n\t\t/// Most UI elements have a standard role, defined in enum <see cref=\"ERole\"/> (except <c>None</c> and <c>Custom</c>). Some UI elements have a custom role, usually as string; then returns <c>ERole.Custom</c>.\n\t\t/// All UI elements must support this property. If failed, probably the <see cref=\"elm\"/> is invalid, for example the window is closed.\n\t\t/// </remarks>\n\t\tpublic ERole RoleInt {\n\t\t\tget {\n\t\t\t\tThrowIfDisposed_();\n\t\t\t\tif (_misc.roleByte != 0) return (ERole)_misc.roleByte;\n\t\t\t\t_Hresult(_FuncId.role, _GetRole(out var role, out _, dontNeedStr: true));\n\t\t\t\t//Debug_.Print(\"roleByte 0 -> \" + role + \", \" + Role); //it's OK in some cases, eg when retrieved not inproc, or after navigating or changing simpleelementid\n\t\t\t\treturn role;\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Like <see cref=\"RoleInt\"/>, but returns 0 if int role not available (does not go to get it).\n\t\t/// </summary>\n\t\tinternal ERole RoleInt_ => (ERole)_misc.roleByte;\n\t\t\n\t\t/// <summary>\n\t\t/// Gets standard or custom role, as string.\n\t\t/// </summary>\n\t\t/// <returns><c>\"\"</c> if failed. Supports <see cref=\"lastError\"/>.</returns>\n\t\t/// <remarks>\n\t\t/// Most UI elements have a standard role, defined in enum <see cref=\"ERole\"/> (except <c>None</c> and <c>Custom</c>). Some UI elements have a custom role, usually as string.\n\t\t/// For standard roles this function returns enum <see cref=\"ERole\"/> member name. For string roles - the string. For unknown non-string roles - the <c>int</c> value like <c>\"0\"</c> or <c>\"500\"</c>.\n\t\t/// All UI elements must support this property. If failed, probably the <see cref=\"elm\"/> is invalid, for example the window is closed.\n\t\t/// </remarks>\n\t\tpublic string Role {\n\t\t\tget {\n\t\t\t\tThrowIfDisposed_();\n\t\t\t\tvar role = (ERole)_misc.roleByte;\n\t\t\t\tif (role is 0 or ERole.Custom) {\n\t\t\t\t\tif (0 != _Hresult(_FuncId.role, _GetRole(out role, out var roleStr, dontNeedStr: false))) return \"\";\n\t\t\t\t\tif (roleStr != null) return roleStr;\n\t\t\t\t}\n\t\t\t\tvar a = s_roles; uint u = (uint)role;\n\t\t\t\treturn (u < a.Length) ? a[u] : ((int)role).ToString();\n\t\t\t}\n\t\t}\n\t\t\n\t\tstatic readonly string[] s_roles = { \"0\", \"TITLEBAR\", \"MENUBAR\", \"SCROLLBAR\", \"GRIP\", \"SOUND\", \"CURSOR\", \"CARET\", \"ALERT\", \"WINDOW\", \"CLIENT\", \"MENUPOPUP\", \"MENUITEM\", \"TOOLTIP\", \"APPLICATION\", \"DOCUMENT\", \"PANE\", \"CHART\", \"DIALOG\", \"BORDER\", \"GROUPING\", \"SEPARATOR\", \"TOOLBAR\", \"STATUSBAR\", \"TABLE\", \"COLUMNHEADER\", \"ROWHEADER\", \"COLUMN\", \"ROW\", \"CELL\", \"LINK\", \"HELPBALLOON\", \"CHARACTER\", \"LIST\", \"LISTITEM\", \"TREE\", \"TREEITEM\", \"PAGETAB\", \"PROPERTYPAGE\", \"INDICATOR\", \"IMAGE\", \"STATICTEXT\", \"TEXT\", \"BUTTON\", \"CHECKBOX\", \"RADIOBUTTON\", \"COMBOBOX\", \"DROPLIST\", \"PROGRESSBAR\", \"DIAL\", \"HOTKEYFIELD\", \"SLIDER\", \"SPINBUTTON\", \"DIAGRAM\", \"ANIMATION\", \"EQUATION\", \"BUTTONDROPDOWN\", \"BUTTONMENU\", \"BUTTONDROPDOWNGRID\", \"WHITESPACE\", \"PAGETABLIST\", \"CLOCK\", \"SPLITBUTTON\", \"IPADDRESS\", \"TREEBUTTON\" };\n\t\t\n\t\t//Returns HRESULT.\n\t\tint _GetRole(out ERole roleInt, out string roleStr, bool dontNeedStr) {\n\t\t\troleStr = null;\n\t\t\tDebug.Assert((ERole)_misc.roleByte is 0 or ERole.Custom);\n\t\t\tvar hr = Cpp.Cpp_AccGetRole(this, out roleInt, out var b);\n\t\t\tif (hr == 0) {\n\t\t\t\tif (!b.Is0) {\n\t\t\t\t\troleInt = ERole.Custom;\n\t\t\t\t\tif (dontNeedStr) b.Dispose(); else roleStr = b.ToStringAndDispose();\n\t\t\t\t}\n\t\t\t\t_misc.SetRole(roleInt);\n\t\t\t}\n\t\t\treturn hr;\n\t\t}\n\t\t\n\t\tint _GetState(out EState state) {\n\t\t\tint hr = Cpp.Cpp_AccGetInt(this, 's', out int i);\n\t\t\tGC.KeepAlive(this);\n\t\t\tstate = (EState)i;\n\t\t\treturn hr;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets UI element state (flags).\n\t\t/// </summary>\n\t\t/// <returns>0 if failed. Supports <see cref=\"lastError\"/>.</returns>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// if(e.State.Has(EState.INVISIBLE)) print.it(\"has state INVISIBLE\");\n\t\t/// if(e.IsInvisible) print.it(\"invisible\");\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic EState State {\n\t\t\tget {\n\t\t\t\tThrowIfDisposed_();\n\t\t\t\t_Hresult(_FuncId.state, _GetState(out var state));\n\t\t\t\treturn state;\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>Calls <see cref=\"State\"/> and returns <c>true</c> if has state <c>CHECKED</c>.</summary>\n\t\tpublic bool IsChecked => State.Has(EState.CHECKED);\n\t\t\n\t\t/// <summary>Calls <see cref=\"State\"/> and returns <c>true</c> if has state <c>CHECKED</c>, <c>null</c> if has state <c>MIXED</c>, else <c>false</c>. Use this function with 3-state checkboxes.</summary>\n\t\tpublic bool? IsChecked2 => (State & (EState.CHECKED | EState.MIXED)) switch { EState.CHECKED => true, 0 => false, _ => null };\n\t\t\n\t\t/// <summary>Calls <see cref=\"State\"/> and returns <c>true</c> if has state <c>UNAVAILABLE</c>.</summary>\n\t\t/// <remarks>Does not check whether this UI element is in a disabled parent/ancestor UI element.</remarks>\n\t\tpublic bool IsDisabled => State.Has(EState.DISABLED);\n\t\t\n\t\t/// <summary>Calls <see cref=\"State\"/> and returns <c>true</c> if has state <c>FOCUSED</c>.</summary>\n\t\tpublic bool IsFocused => State.Has(EState.FOCUSED);\n\t\t\n\t\t/// <summary>Calls <see cref=\"State\"/> and returns <c>true</c> if has state <c>INVISIBLE</c> and does not have state <c>OFFSCREEN</c>.</summary>\n\t\t/// <remarks>\n\t\t/// If the UI element has both <c>INVISIBLE</c> and <c>OFFSCREEN</c> states, it is either invisible or just offscreen, depending on application etc. Then this function works like <see cref=\"elmFinder.Find\"/> and similar functions: for most UI elements returns <c>false</c> (is visible), but for UI elements that have these roles returns <c>true</c> (invisible): <c>WINDOW</c>, <c>DOCUMENT</c>, <c>PROPERTYPAGE</c>, <c>GROUPING</c>, <c>ALERT</c>, <c>MENUPOPUP</c>.\n\t\t/// Does not check whether this UI element is in an invisible parent/ancestor UI element.\n\t\t/// </remarks>\n\t\tpublic bool IsInvisible => IsInvisible_(State);\n\t\t\n\t\tinternal bool IsInvisible_(EState state) {\n\t\t\tif (!state.Has(EState.INVISIBLE)) return false;\n\t\t\tif (!state.Has(EState.OFFSCREEN)) return true;\n\t\t\tswitch (RoleInt) {\n\t\t\tcase ERole.WINDOW:\n\t\t\tcase ERole.DOCUMENT:\n\t\t\tcase ERole.PROPERTYPAGE:\n\t\t\tcase ERole.GROUPING:\n\t\t\tcase ERole.ALERT:\n\t\t\tcase ERole.MENUPOPUP:\n\t\t\t\treturn true;\n\t\t\t\t//note: these roles must be the same as in _IsRoleToSkipIfInvisible in \"acc find.cpp\"\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\t/// <summary>Calls <see cref=\"State\"/> and returns <c>true</c> if has state <c>OFFSCREEN</c>.</summary>\n\t\tpublic bool IsOffscreen => State.Has(EState.OFFSCREEN);\n\t\t\n\t\t/// <summary>Calls <see cref=\"State\"/> and returns <c>true</c> if has state <c>PROTECTED</c>.</summary>\n\t\t/// <remarks>This state is used for password fields.</remarks>\n\t\tpublic bool IsPassword => State.Has(EState.PROTECTED);\n\t\t\n\t\t/// <summary>Calls <see cref=\"State\"/> and returns <c>true</c> if has state <c>PRESSED</c>.</summary>\n\t\tpublic bool IsPressed => State.Has(EState.PRESSED);\n\t\t\n\t\t/// <summary>Calls <see cref=\"State\"/> and returns <c>true</c> if has state <c>READONLY</c>.</summary>\n\t\tpublic bool IsReadonly => State.Has(EState.READONLY);\n\t\t\n\t\t/// <summary>Calls <see cref=\"State\"/> and returns <c>true</c> if has state <c>SELECTED</c>.</summary>\n\t\tpublic bool IsSelected => State.Has(EState.SELECTED);\n\t\t\n\t\t/// <summary>\n\t\t/// Converts <c>BSTR</c> to string and disposes the <c>BSTR</c>.\n\t\t/// If hr is not 0, returns <c>\"\"</c> (never <c>null</c>).\n\t\t/// </summary>\n\t\tstatic string _BstrToString(int hr, BSTR b) {\n\t\t\tif (hr == 0) return b.ToStringAndDispose() ?? \"\";\n\t\t\treturn \"\";\n\t\t}\n\t\t\n\t\tstring _GetStringProp(char prop) {\n\t\t\tThrowIfDisposed_();\n\t\t\tint hr = Cpp.Cpp_AccGetStringProp(this, prop, out var b);\n\t\t\tGC.KeepAlive(this);\n\t\t\tvar s = _BstrToString(hr, b);\n\t\t\t_Hresult((_FuncId)prop, hr);\n\t\t\treturn s;\n\t\t}\n\t\t\n\t\tstatic string _GetStringPropL(Cpp.Cpp_Acc a, char prop) {\n\t\t\tint hr = Cpp.Cpp_AccGetStringProp(a, prop, out var b);\n\t\t\treturn _BstrToString(hr, b);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets name.\n\t\t/// </summary>\n\t\t/// <returns><c>\"\"</c> if name is unavailable or if failed. Supports <see cref=\"lastError\"/>.</returns>\n\t\t/// <remarks>\n\t\t/// UI element name usually is its read-only text (eg button text, link text), or its adjacent read-only text (eg text label by this edit box). It usually does not change, therefore can be used to find or identify the UI element.\n\t\t/// </remarks>\n\t\tpublic string Name {\n\t\t\tget => _GetStringProp('n');\n\t\t\t\n\t\t\t//note: bug of standard toolbar buttons: fails to get name if all these are true:\n\t\t\t//\tThis process is 64-bit and the target process is 32-bit.\n\t\t\t//\tButton Name property comes from its tooltip.\n\t\t\t//\tFound not inproc, eg with flag NotInProc.\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets <see cref=\"Name\"/> of window/control <i>w</i>.\n\t\t/// Returns <c>null</c> if <i>w</i> invalid. Returns <c>\"\"</c> if failed to get name.\n\t\t/// </summary>\n\t\tinternal static string NameOfWindow_(wnd w) {\n\t\t\tif (!w.IsAlive) return null;\n\t\t\tvar hr = Cpp.Cpp_AccFromWindow(1 | 2, w, 0, out _, out var b);\n\t\t\treturn _BstrToString(hr, b);\n\t\t\t\n\t\t\t//speed: inproc ~10% faster. But first time slower, especially if process of different bitness.\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets or sets value.\n\t\t/// </summary>\n\t\t/// <exception cref=\"AuException\">Failed to set value.</exception>\n\t\t/// <remarks>\n\t\t/// Usually it is editable text or some other value that can be changed at run time, therefore in most cases it cannot be used to find or identify the UI element reliably.\n\t\t/// The <c>get</c> function returns <c>\"\"</c> if this property is unavailable or if failed. Supports <see cref=\"lastError\"/>.\n\t\t/// Most UI elements don't support <c>set</c>.\n\t\t/// </remarks>\n\t\tpublic string Value {\n\t\t\tget => _GetStringProp('v');\n\t\t\tset {\n\t\t\t\tThrowIfDisposed_();\n\t\t\t\tAuException.ThrowIfHresultNot0(_InvokeL('v', value));\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets description.\n\t\t/// </summary>\n\t\t/// <returns><c>\"\"</c> if this property is unavailable or if failed. Supports <see cref=\"lastError\"/>.</returns>\n\t\tpublic string Description {\n\t\t\tget => _GetStringProp('d');\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets help text.\n\t\t/// </summary>\n\t\t/// <returns><c>\"\"</c> if this property is unavailable or if failed. Supports <see cref=\"lastError\"/>.</returns>\n\t\tpublic string Help {\n\t\t\tget => _GetStringProp('h');\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets the UI Automation <c>AutomationId</c> property.\n\t\t/// </summary>\n\t\t/// <returns><c>\"\"</c> if this property is unavailable or if failed. Supports <see cref=\"lastError\"/>.</returns>\n\t\t/// <remarks>\n\t\t/// Only UI elements found with flag <see cref=\"EFFlags.UIA\"/> can have this property.\n\t\t/// </remarks>\n\t\tpublic string UiaId {\n\t\t\tget => _GetStringProp('u');\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets the UI Automation <c>ClassName</c> property.\n\t\t/// </summary>\n\t\t/// <returns><c>\"\"</c> if this property is unavailable or if failed. Supports <see cref=\"lastError\"/>.</returns>\n\t\t/// <remarks>\n\t\t/// Only UI elements found with flag <see cref=\"EFFlags.UIA\"/> can have this property.\n\t\t/// </remarks>\n\t\tpublic string UiaCN {\n\t\t\tget => _GetStringProp('U');\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets keyboard shortcut.\n\t\t/// </summary>\n\t\t/// <returns><c>\"\"</c> if this property is unavailable or if failed. Supports <see cref=\"lastError\"/>.</returns>\n\t\tpublic string KeyboardShortcut {\n\t\t\tget => _GetStringProp('k');\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets default action of <see cref=\"Invoke\"/>.\n\t\t/// </summary>\n\t\t/// <returns><c>\"\"</c> if this property is unavailable or if failed. Supports <see cref=\"lastError\"/>.</returns>\n\t\t/// <remarks>\n\t\t/// If this is a Java UI element, returns all actions that can be used with <see cref=\"JavaInvoke\"/>, like <c>\"action1, action2, action3\"</c>, from which the first is considered default and is used by <see cref=\"Invoke\"/>.\n\t\t/// Note: the string is supposed to be localized, ie depends on UI language; except Java.\n\t\t/// </remarks>\n\t\tpublic string DefaultAction {\n\t\t\tget => _GetStringProp('a');\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Performs the UI element's default action (see <see cref=\"DefaultAction\"/>). Usually it is \"click\", \"press\" or similar. Like a mouse click but without moving the mouse cursor or pressing mouse buttons.\n\t\t/// </summary>\n\t\t/// <exception cref=\"AuException\">Failed.</exception>\n\t\t/// <remarks>\n\t\t/// Fails if the UI element does not have a default action. Then you can use <see cref=\"MouseClick\"/>, or try <see cref=\"PostClick\"/>, <see cref=\"Check\"/>, <see cref=\"SendKeys\"/>, other functions.\n\t\t/// The action can take long time, for example show a dialog. This function normally does not wait. It allows the caller to automate the dialog. If it waits, try <see cref=\"JavaInvoke\"/> or one of the above functions (<see cref=\"MouseClick\"/> etc).\n\t\t/// </remarks>\n\t\tpublic void Invoke() {\n\t\t\tThrowIfDisposed_();\n\t\t\t_Invoke();\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Calls <c>_InvokeL</c>, with <c>ButtonPostClickWorkaround_</c> if need. Exception if failed.\n\t\t/// </summary>\n\t\tvoid _Invoke(char action = 'a', string param = null, string errMsg = null) {\n\t\t\tint hr;\n\t\t\tif (!MiscFlags.HasAny(EMiscFlags.UIA | EMiscFlags.Java) && RoleInt is ERole.BUTTON or ERole.SPLITBUTTON or ERole.CHECKBOX or ERole.RADIOBUTTON) {\n\t\t\t\tusing var workaround = new mouse.ButtonPostClickWorkaround_(WndContainer);\n\t\t\t\thr = _InvokeL(action, param);\n\t\t\t} else {\n\t\t\t\thr = _InvokeL(action, param);\n\t\t\t}\n\t\t\tAuException.ThrowIfHresultNot0(hr, errMsg);\n\t\t\t//_MinimalSleep(); //don't need. It does not make more reliable.\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Calls <c>EnableActivate(-1)</c> and <c>Cpp_AccAction</c>.\n\t\t/// </summary>\n\t\t/// <returns><c>HRESULT</c></returns>\n\t\tint _InvokeL(char action = 'a', string param = null) {\n\t\t\t//UIA bug: if window inactive, in some cases tries to activate, and waits ~10 s if failed.\n\t\t\t//\tEg ExpandCollapse pattern (always) and Invoke/Toggle patterns (buttons/checkboxes, not always).\n\t\t\t//\tNon-UIA servers also could try to activate, although now I don't remember such cases.\n\t\t\tWndUtil.EnableActivate(-1); //usually quite fast, and often faster than WndContainer\n\t\t\t\n\t\t\tint hr = Cpp.Cpp_AccAction(this, action, param);\n\t\t\tGC.KeepAlive(this);\n\t\t\treturn hr;\n\t\t}\n\t\t\n\t\t//void _MinimalSleep()\n\t\t//{\n\t\t//\tThread.Sleep(15);\n\t\t//\t//if(0 == _iacc.GetWnd(out var w)) w.MinimalSleepIfOtherThread_(); //better don't call getwnd\n\t\t//}\n\t\t\n\t\t/// <summary>\n\t\t/// Performs one of actions supported by this Java UI element.\n\t\t/// </summary>\n\t\t/// <param name=\"action\">\n\t\t/// Action name. See <see cref=\"DefaultAction\"/>.\n\t\t/// If <c>null</c> (default), performs default action (like <see cref=\"Invoke\"/>) or posts <c>Space</c> key message. More info in Remarks.</param>\n\t\t/// <exception cref=\"AuException\">Failed.</exception>\n\t\t/// <remarks>\n\t\t/// Read more about Java UI elements in <see cref=\"elm\"/> topic.\n\t\t/// \n\t\t/// Problem: if the action opens a dialog, <see cref=\"Invoke\"/>/<see cref=\"JavaInvoke\"/> do not return until the dialog is closed (or fail after some time). The caller then waits and cannot automate the dialog. Also then this process cannot exit until the dialog is closed. If the action parameter is <c>null</c> and the UI element is focusable, this function tries a workaround: it makes the UI element (button etc) focused and posts <c>Space</c> key message, which should press the button; then this function does not wait.\n\t\t/// </remarks>\n\t\tpublic void JavaInvoke(string action = null) {\n\t\t\t//problem: if the button click action opens a modal dialog, doAccessibleActions waits until closed.\n\t\t\t//\tWaits 8 s and then returns true. Somehow in QM2 returns false.\n\t\t\t//\tDuring that time any JAB calls (probably from another thread) are blocked and fail. Tried various combinations.\n\t\t\t//\tAlso then releaseJavaObject does not return until the dialog closed. It even does not allow our process to exit. In QM2 the same.\n\t\t\t//\tPreviously (with old Java version?) then whole JAB crashed. Now doesn't. Or crashes only in some conditions that I now cannot reproduce.\n\t\t\t\n\t\t\tThrowIfDisposed_();\n\t\t\t\n\t\t\tif (action == null\n\t\t\t\t&& 0 == _GetState(out var state)\n\t\t\t\t&& (state & (EState.FOCUSABLE | EState.SELECTABLE)) == EState.FOCUSABLE //must be only focusable. If SELECTABLE, probably don't need this workaround.\n\t\t\t\t&& 0 == _GetWnd(out var w)\n\t\t\t\t&& 0 == Cpp.Cpp_AccSelect(this, ESelect.TAKEFOCUS)\n\t\t\t\t&& 0 == _GetState(out state) && state.Has(EState.FOCUSED) //avoid sending keys to another control\n\t\t\t\t) {\n\t\t\t\tGC.KeepAlive(this);\n\t\t\t\tw.Post(Api.WM_KEYDOWN, (byte)KKey.Space, 0);\n\t\t\t\tw.Post(Api.WM_KEYUP, (byte)KKey.Space, 0);\n\t\t\t\t//tested: works even if the window is inactive.\n\t\t\t\tw.MinimalSleepNoCheckThread_();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\t_Invoke('a', action);\n\t\t\t//_MinimalSleep(); //don't need. JAB doAccessibleActions is sync, which is bad.\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Calls <see cref=\"Invoke\"/> or <i>action</i> and waits until changes the web page (window name and page name).\n\t\t/// </summary>\n\t\t/// <param name=\"timeout\">Timeout, seconds. Can be 0 (infinite), >0 (exception) or &lt;0 (no exception). More info: [](xref:wait_timeout).\n\t\t/// Default 60 seconds.\n\t\t/// </param>\n\t\t/// <param name=\"action\">If used, calls it instead of <see cref=\"Invoke\"/>.</param>\n\t\t/// <returns>Returns <c>true</c>. On timeout returns <c>false</c> if <i>timeout</i> &lt; 0; else exception.</returns>\n\t\t/// <exception cref=\"TimeoutException\"><i>timeout</i> time has expired (if > 0).</exception>\n\t\t/// <exception cref=\"AuException\">Failed. For example, when this UI element is invalid, or its top-level window does not contain a web page.</exception>\n\t\t/// <exception cref=\"AuWndException\">The window was closed while waiting.</exception>\n\t\t/// <exception cref=\"Exception\">Exceptions thrown by <see cref=\"Invoke\"/> or by the <i>action</i> function.</exception>\n\t\t/// <remarks>\n\t\t/// This function is used to click a link in a web page and wait until current web page is gone. It prevents a following \"wait for UI element\" function from finding a matching UI element in the old page, which would be bad.\n\t\t/// Does not wait until the new page is completely loaded. There is no reliable/universal way for it. Instead, after calling it you can call a \"wait for UI element\" function which waits for a known UI element that must be in the new page.\n\t\t/// This function cannot be used when the new page has the same title as current page. Then it waits until <i>timeout</i> time or forever. The same if the invoked action does not open a web page.\n\t\t/// </remarks>\n\t\tpublic bool WebInvoke(Seconds? timeout = null, Action<elm> action = null) {\n\t\t\twnd w = WndTopLevel; if (w.Is0) throw new AuException(\"*get window\");\n\t\t\telm doc = w.Elm[\"web:\"].Find(-1) ?? throw new AuException(\"*find web page\");\n\t\t\t\n\t\t\tstring wndName = w.NameTL_, docName = doc.Name; Debug.Assert(!wndName.NE() && !docName.NE());\n\t\t\tbool wndOK = false, docOK = false;\n\t\t\telmFinder f = null;\n\t\t\t\n\t\t\tif (action == null) Invoke(); else action(this);\n\t\t\t\n\t\t\t//wait until window name and document name both are changed. They can change in any order.\n\t\t\tvar seconds = timeout ?? new(60);\n\t\t\tseconds.Period ??= 25;\n\t\t\tvar loop = new WaitLoop(seconds);\n\t\t\twhile (loop.Sleep()) {\n\t\t\t\tw.ThrowIfInvalid();\n\t\t\t\tif (!wndOK) {\n\t\t\t\t\tvar s = w.NameTL_;\n\t\t\t\t\tif (s == null) continue; //probably invalid, will throw in next loop\n\t\t\t\t\twndOK = s != wndName;\n\t\t\t\t}\n\t\t\t\tif (wndOK && !docOK) {\n\t\t\t\t\tf ??= new elmFinder(\"web:\") { ResultGetProperty = 'n' };\n\t\t\t\t\tif (!w.HasElm(f)) continue; //eg in Firefox for some time there is no DOCUMENT\n\t\t\t\t\tif (f.ResultProperty is not string s) continue;\n\t\t\t\t\tdocOK = s != docName;\n\t\t\t\t\t//if(!docOK) print.it(\"doc is late\");\n\t\t\t\t}\n\t\t\t\tif (wndOK && docOK) {\n\t\t\t\t\tw.ThrowIfInvalid();\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t\t\n#if false\n\t\t//This function is finished and normally works well.\n\t\t//However web browsers not always fire the event. For some pages never, or only when not cached.\n\t\t//Also, some pages are like never finish loading (the browser waiting animation does not stop spinning). Or finish when the wanted UI element is there for long time, so why to wait. Or finish, then continue loading again...\n\t\t//Also, this function inevitably will stop working with some new web browser version with new bugs. Too unreliable.\n\t\tpublic bool InvokeAndWaitForWebPageLoaded(double secondsTimeout = 60, Action<elm> action = null, wnd w = default)\n\t\t{\n\t\t\tThrowIfDisposed_();\n\n\t\t\tint timeout; bool throwTimeout = false;\n\t\t\tif(secondsTimeout == 0) timeout = Timeout.Infinite;\n\t\t\telse {\n\t\t\t\tif(secondsTimeout < 0) secondsTimeout = -secondsTimeout; else throwTimeout = true;\n\t\t\t\ttimeout = checked((int)(secondsTimeout * 1000));\n\t\t\t}\n\n\t\t\tif(w.Is0) {\n\t\t\t\tw = WndContainer;\n\t\t\t\tif(w.Is0) throw new AuException(\"*get window\");\n\t\t\t}\n\n\t\t\tvar hookEvent = EEvent.IA2_DOCUMENT_LOAD_COMPLETE;\n\t\t\tint browser = w.ClassNameIs(Api.string_IES, \"Mozilla*\", \"Chrome*\");\n\t\t\tswitch(browser) {\n\t\t\tcase 0:\n\t\t\t\twnd ies = w.Child(null, Api.string_IES); if(ies.Is0) break;\n\t\t\t\tw = ies; goto case 1;\n\t\t\tcase 1: hookEvent = EEvent.OBJECT_CREATE; break;\n\t\t\t}\n\n\t\t\tint tid = w.ThreadId; if(tid == 0) w.ThrowUseNative();\n\t\t\tAutoResetEvent eventNotify = null;\n\t\t\tint debugIndex = 0;\n\n\t\t\tApi.WINEVENTPROC hook = (IntPtr hWinEventHook, EEvent ev, wnd hwnd, int idObject, int idChild, int idEventThread, int time) =>\n\t\t\t{\n\t\t\t\tif(eventNotify == null) { /*print.it(\"null 1\");*/ return; }\n\t\t\t\tif(ev == EEvent.OBJECT_CREATE && hwnd != w) return; //note: in Chrome hwnd is Chrome_RenderWidgetHostHWND\n\t\t\t\tint di = ++debugIndex;\n\t\t\t\tusing(var a = elm.fromEvent(hwnd, idObject, idChild)) {\n\t\t\t\t\tif(a == null) { /*Debug_.Print(\"elm.fromEvent null\");*/ return; } //often IE, but these are not useful UI elements\n\t\t\t\t\tif(eventNotify == null) { /*print.it(\"null 2\");*/ return; }\n\t\t\t\t\tif(ev == EEvent.IA2_DOCUMENT_LOAD_COMPLETE) { //Chrome, Firefox\n\n\t\t\t\t\t\t//filter out frame/iframe\n\t\t\t\t\t\tif(browser == 2) { //Firefox does not fire events for frame/iframe. But the Chrome code would work too.\n\t\t\t\t\t\t} else if(0 == a._iacc.get_accParent(out var a2)) { //bug in some Chrome versions: fails for main document\n\t\t\t\t\t\t\tusing(a2) {\n\t\t\t\t\t\t\t\tif(eventNotify == null) { /*print.it(\"null 3\");*/ return; }\n\t\t\t\t\t\t\t\tbool isFrame;\n\t\t\t\t\t\t\t\tvar hr = a2.GetRole(0, out var role, out var roleStr); if(hr != 0) Debug_.Print((uint)hr);\n\t\t\t\t\t\t\t\tif(eventNotify == null) { /*print.it(\"null 4\");*/ return; }\n\t\t\t\t\t\t\t\tif(hr != 0) isFrame = false;\n\t\t\t\t\t\t\t\telse if(roleStr != null) isFrame = roleStr.Ends(\"frame\", true);\n\t\t\t\t\t\t\t\telse isFrame = !(role == ERole.WINDOW || role == ERole.CLIENT);\n\t\t\t\t\t\t\t\t//print.it(role, roleStr);\n\t\t\t\t\t\t\t\tif(isFrame) return;\n\t\t\t\t\t\t\t\t//browser    main        frame     iframe\n\t\t\t\t\t\t\t\t//Firefox    \"browser\"   \"frame\"   \"iframe\"\n\t\t\t\t\t\t\t\t//Chrome     WINDOW      \"FRAME\"   DOCUMENT\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} else { //IE (OBJECT_CREATE)\n\t\t\t\t\t\tif(a._elem != 0) return;\n\t\t\t\t\t\tif(0 != a._iacc.GetRole(0, out var role) || role != ERole.PANE) return;\n\t\t\t\t\t\tif(eventNotify == null) { /*print.it(\"null 3\");*/ return; }\n\n\t\t\t\t\t\t//filter out frame/iframe\n\t\t\t\t\t\tif(a.IsInvisible) return;\n\t\t\t\t\t\tif(eventNotify == null) { /*print.it(\"null 4\");*/ return; }\n\t\t\t\t\t\tusing(var aCLIENT = _FromWindow(w, EObjid.CLIENT, noThrow: true)) {\n\t\t\t\t\t\t\tif(eventNotify == null) { /*print.it(\"null 5\");*/ return; }\n\t\t\t\t\t\t\tif(aCLIENT != null) {\n\t\t\t\t\t\t\t\tvar URL1 = a.Value; Debug.Assert(URL1.Length > 0); //print.it(URL1); //http:..., about:...\n\t\t\t\t\t\t\t\taCLIENT.get_accName(0, out var URL2, 0); Debug.Assert(URL2.Length > 0);\n\t\t\t\t\t\t\t\tif(URL1 != URL2) return;\n\t\t\t\t\t\t\t\tif(eventNotify == null) { /*print.it(\"null 6\");*/ return; }\n\t\t\t\t\t\t\t} else Debug_.Print(\"aCLIENT null\");\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t//print.it(di, ev, a);\n\t\t\t\t\teventNotify.Set();\n\t\t\t\t\teventNotify = null;\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tvar hh = Api.SetWinEventHook(hookEvent, hookEvent, default, hook, 0, tid, 0); if(hh == default) throw new AuException();\n\t\t\ttry {\n\t\t\t\teventNotify = new AutoResetEvent(false);\n\t\t\t\tif(action != null) action(this); else Invoke();\n\t\t\t\tif(eventNotify.WaitOne(timeout)) {\n\t\t\t\t\t//Thread.CurrentThread.Join(2000);\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t\tfinally { Api.UnhookWinEvent(hh); eventNotify?.Dispose(); }\n\t\t\tGC.KeepAlive(hook);\n\n\t\t\tif(throwTimeout) throw new TimeoutException();\n\t\t\treturn false;\n\t\t}\n#endif\n\t\t\n\t\t/// <summary>\n\t\t/// Selects or deselects.\n\t\t/// </summary>\n\t\t/// <param name=\"how\">Specifies whether to select, focus, add to selection etc. Can be two flags, for example <c>ESelect.TAKEFOCUS | ESelect.TAKESELECTION</c>. With flag <c>TAKEFOCUS</c> activates the window like <see cref=\"Focus(bool)\"/>.</param>\n\t\t/// <exception cref=\"AuException\">Failed.</exception>\n\t\t/// <exception cref=\"AuWndException\">Failed to activate the window (<see cref=\"wnd.Activate\"/>) or focus the control (<see cref=\"wnd.Focus\"/>).</exception>\n\t\t/// <remarks>\n\t\t/// Not all UI elements support it. Most UI elements support not all flags. It depends on <see cref=\"EState\"/> <c>FOCUSABLE</c>, <c>SELECTABLE</c>, <c>MULTISELECTABLE</c>, <c>EXTSELECTABLE</c>, <c>DISABLED</c>.\n\t\t/// Many UI elements have bugs, especially with flag <c>TAKEFOCUS</c>. More bugs when the UI element has been found with flag <see cref=\"EFFlags.NotInProc\"/>.\n\t\t/// </remarks>\n\t\tpublic void Select(ESelect how = ESelect.TAKESELECTION) {\n\t\t\tThrowIfDisposed_();\n\t\t\t\n\t\t\t//Workaround for Windows controls bugs, part 1.\n\t\t\twnd w = default, wTL = default; bool focusingControl = false;\n\t\t\tif (how.Has(ESelect.TAKEFOCUS)) {\n\t\t\t\t//Always activate/focus the window, because used by functions that then will send keys etc.\n\t\t\t\t//CONSIDER: option TAKEFOCUSNOACTIVATE to focus without activate/focus the window.\n\t\t\t\tw = WndContainer;\n\t\t\t\t//if(!w.IsEnabled(true)) throw new AuException(\"*set focus. Disabled\"); //accSelect would not fail //rejected. In some cases the UI element may be focusable although window disabled, eg KTreeView.\n\t\t\t\twTL = w.Window;\n\t\t\t\twTL.Activate();\n\t\t\t\tif (focusingControl = (w != wTL))\n\t\t\t\t\tif (w.IsEnabled()) //see above. Would be exception if disabled.\n\t\t\t\t\t\tw.Focus();\n\t\t\t\tif (IsFocused) how &= ~ESelect.TAKEFOCUS;\n\t\t\t\tif (how == 0) return;\n\t\t\t} else {\n\t\t\t\t//same as with Invoke\n\t\t\t\tWndUtil.EnableActivate(-1);\n\t\t\t}\n\t\t\t\n\t\t\tfor (int i = 0; i < 2; i++) {\n\t\t\t\tvar hr = Cpp.Cpp_AccSelect(this, how);\n\t\t\t\tGC.KeepAlive(this);\n\t\t\t\tif (hr == 0) break;\n\t\t\t\tif (hr == 1) continue; //some UI elements return S_FALSE even if did what asked. Eg combobox (focuses the child Edit), slider. Or may need to retry, eg when trying to focus a listitem in a non-focused listbox.\n\t\t\t\tif (hr == Api.DISP_E_MEMBERNOTFOUND) throw new AuException(\"This UI element does not support this state\");\n\t\t\t\tAuException.ThrowIfHresultNegative(hr);\n\t\t\t}\n\t\t\t\n\t\t\tif (!w.Is0) w.MinimalSleepIfOtherThread_(); //sleep only when focusing. Assume selection is sync. Also need for the bug, because the control may be activated a millisecond later.\n\t\t\t\n\t\t\t//Workaround for Windows controls bugs, part 2.\n\t\t\tif (focusingControl && w.IsActive) {\n\t\t\t\t//Debug_.Print(\"activated control\");\n\t\t\t\twTL.ActivateL();\n\t\t\t}\n\t\t\t\n\t\t\t//tested: IAccessible.accSelect(TAKEFOCUS):\n\t\t\t//\tMost Windows controls have this bug: activates the control with SetForegroundWindow, which deactivates the top-level window.\n\t\t\t//\t\tEspecially if the control is already focused.\n\t\t\t//\t\tIf not already focused, fails if eg listbox item. But then works well with eg buttons.\n\t\t\t//\t\tMSDN: If IAccessible::accSelect is called with the SELFLAG_TAKEFOCUS flag on a child UI element that has an HWND, the flag takes effect only if the UI element's parent has the focus.\n\t\t\t//\t\tTested, does not help: LockSetForegroundWindow, AttachThreadInput.\n\t\t\t//\t\tGood news: works well if the UI element found inproc, ie without flag NotInproc.\n\t\t\t//\t\t\tBut then need to focus the control, else does not work.\n\t\t\t//\t\t\tUse the same workaround. It focuses the control.\n\t\t\t//\tWorks well with web browsers, WinForms.\n\t\t\t//\tWith WPF initially almost does not work. After using a navigation key (Tab etc) starts to work well.\n\t\t\t//tested: UIA.IElement.SetFocus:\n\t\t\t//\tIn most cases works well with standard controls, all web browsers, WinForms.\n\t\t\t//\tWith WPF same as elm.\n\t\t\t//\tBug: If standard control is disabled, deactivates parent window and draws focus rectangle on the control.\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Makes this UI element focused for keyboard input.\n\t\t/// </summary>\n\t\t/// <param name=\"andSelect\">Add flag <c>TAKESELECTION</c>. Note: it is for selecting a list item, not for selecting text in a text box.</param>\n\t\t/// <exception cref=\"AuException\">Failed.</exception>\n\t\t/// <exception cref=\"AuWndException\">Failed to activate the window (<see cref=\"wnd.Activate\"/>) or focus the control (<see cref=\"wnd.Focus\"/>).</exception>\n\t\t/// <remarks>\n\t\t/// Calls <see cref=\"Select\"/> with flag <c>TAKEFOCUS</c> and optionally <c>TAKESELECTION</c>.\n\t\t/// Not all UI elements support this action and not all work correctly. More info in <c>Select</c> documentation.\n\t\t/// </remarks>\n\t\tpublic void Focus(bool andSelect = false) {\n\t\t\tvar how = ESelect.TAKEFOCUS;\n\t\t\tif (andSelect) how |= ESelect.TAKESELECTION;\n\t\t\tSelect(how);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets selected direct child items.\n\t\t/// </summary>\n\t\t/// <returns>Empty array if there are no selected items of if failed. Supports <see cref=\"lastError\"/>.</returns>\n\t\tpublic elm[] SelectedChildren {\n\t\t\tget {\n\t\t\t\tThrowIfDisposed_();\n\t\t\t\tif (_elem != 0) { lastError.clear(); return []; }\n\t\t\t\t//return _iacc.get_accSelection();\n\t\t\t\tif (0 != _Hresult(_FuncId.selection, Cpp.Cpp_AccGetSelection(this, out var b)) || b.Is0) return [];\n\t\t\t\tGC.KeepAlive(this);\n\t\t\t\tvar p = (Cpp.Cpp_Acc*)b.Ptr; int n = b.Length / sizeof(Cpp.Cpp_Acc);\n\t\t\t\tvar r = new elm[n];\n\t\t\t\tfor (int i = 0; i < n; i++) r[i] = new elm(p[i]);\n\t\t\t\tb.Dispose();\n\t\t\t\treturn r;\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets the number of direct child UI elements.\n\t\t/// </summary>\n\t\tpublic int ChildCount {\n\t\t\tget {\n\t\t\t\tThrowIfDisposed_();\n\t\t\t\tif (_elem != 0) { lastError.clear(); return 0; }\n\t\t\t\t_Hresult(_FuncId.child_count, Cpp.Cpp_AccGetInt(this, 'c', out int cc));\n\t\t\t\tGC.KeepAlive(this);\n\t\t\t\treturn cc;\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets multiple properties.\n\t\t/// </summary>\n\t\t/// <param name=\"props\">\n\t\t/// String that specifies properties to get, for example <c>\"nv\"</c> for name and value.\n\t\t/// <br/>• <c>R</c> - <see cref=\"Role\"/>.\n\t\t/// <br/>• <c>n</c> - <see cref=\"Name\"/>.\n\t\t/// <br/>• <c>v</c> - <see cref=\"Value\"/>.\n\t\t/// <br/>• <c>d</c> - <see cref=\"Description\"/>.\n\t\t/// <br/>• <c>h</c> - <see cref=\"Help\"/>.\n\t\t/// <br/>• <c>a</c> - <see cref=\"DefaultAction\"/>.\n\t\t/// <br/>• <c>k</c> - <see cref=\"KeyboardShortcut\"/>.\n\t\t/// <br/>• <c>u</c> - <see cref=\"UiaId\"/>.\n\t\t/// <br/>• <c>U</c> - <see cref=\"UiaCN\"/>.\n\t\t/// <br/>• <c>s</c> - <see cref=\"State\"/>.\n\t\t/// <br/>• <c>r</c> - <see cref=\"GetRect(out RECT, bool)\"/> with <i>raw</i> <c>true</c>.\n\t\t/// <br/>• <c>D</c> - <see cref=\"Rect\"/> or <see cref=\"GetRect(out RECT, bool)\"/> with <i>raw</i> <c>false</c>. Don't use with <c>r</c>.\n\t\t/// <br/>• <c>w</c> - <see cref=\"WndContainer\"/>.\n\t\t/// <br/>• <c>o</c> - <see cref=\"Html\"/> outer.\n\t\t/// <br/>• <c>i</c> - <see cref=\"Html\"/> inner.\n\t\t/// <br/>• <c>@</c> - <see cref=\"HtmlAttributes\"/>.\n\t\t/// </param>\n\t\t/// <param name=\"result\">Receives results.</param>\n\t\t/// <returns><c>false</c> if failed, for example when the UI element's window is closed. Supports <see cref=\"lastError\"/>.</returns>\n\t\t/// <exception cref=\"ArgumentException\">Unknown property character.</exception>\n\t\t/// <remarks>\n\t\t/// The returned variable contains values of properties specified in <i>props</i>. When a property is empty or failed to get, the member variable is <c>\"\"</c>, empty dictionary or default value of that type; never <c>null</c>.\n\t\t/// \n\t\t/// Normally this function is faster than calling multiple property functions, because it makes single remote procedure call. But not if this UI element was found with flag <see cref=\"EFFlags.NotInProc\"/> etc.\n\t\t/// </remarks>\n\t\tpublic bool GetProperties(string props, out EProperties result) {\n\t\t\t//CONSIDER: use cached role. Or not, because now can help to catch bugs where the cached role is incorrect.\n\t\t\t\n\t\t\tresult = null;\n\t\t\tThrowIfDisposed_();\n\t\t\tif (props.Length == 0) return true;\n\t\t\tint hr = Cpp.Cpp_AccGetProps(this, props, out var b);\n\t\t\tGC.KeepAlive(this);\n\t\t\tif (hr != 0) {\n\t\t\t\tif (hr == (int)Cpp.EError.InvalidParameter) throw new ArgumentException(\"Unknown property character.\");\n\t\t\t\tlastError.code = hr;\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tresult = new();\n\t\t\tusing (b) {\n\t\t\t\tvar offsets = (int*)b.Ptr;\n\t\t\t\tfor (int i = 0; i < props.Length; i++) {\n\t\t\t\t\tint offs = offsets[i], len = ((i == props.Length - 1) ? b.Length : offsets[i + 1]) - offs;\n\t\t\t\t\tvar p = b.Ptr + offs;\n\t\t\t\t\tswitch (props[i]) {\n\t\t\t\t\tcase 'r' or 'D': result.Rect = len > 0 ? *(RECT*)p : default; break;\n\t\t\t\t\tcase 's': result.State = len > 0 ? *(EState*)p : default; break;\n\t\t\t\t\tcase 'w': result.WndContainer = len > 0 ? (wnd)(*(int*)p) : default; break;\n\t\t\t\t\tcase '@': result.HtmlAttributes = AttributesToDictionary_(p, len); break;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tvar s = (len == 0) ? \"\" : new string(p, 0, len);\n\t\t\t\t\t\tswitch (props[i]) {\n\t\t\t\t\t\tcase 'R': result.Role = s; break;\n\t\t\t\t\t\tcase 'n': result.Name = s; break;\n\t\t\t\t\t\tcase 'v': result.Value = s; break;\n\t\t\t\t\t\tcase 'd': result.Description = s; break;\n\t\t\t\t\t\tcase 'h': result.Help = s; break;\n\t\t\t\t\t\tcase 'a': result.DefaultAction = s; break;\n\t\t\t\t\t\tcase 'k': result.KeyboardShortcut = s; break;\n\t\t\t\t\t\tcase 'u': result.UiaId = s; break;\n\t\t\t\t\t\tcase 'U': result.UiaCN = s; break;\n\t\t\t\t\t\tcase 'o': result.OuterHtml = s; break;\n\t\t\t\t\t\tcase 'i': result.InnerHtml = s; break;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\tinternal static Dictionary<string, string> AttributesToDictionary_(char* p, int len) {\n\t\t\tvar d = new Dictionary<string, string>();\n\t\t\tint ik = 0, iv = 0;\n\t\t\tfor (int i = 0; i < len; i++) {\n\t\t\t\tvar c = p[i];\n\t\t\t\tif (c == '\\0' && iv > ik) {\n\t\t\t\t\tstring sk = new string(p, ik, iv - ik - 1);\n\t\t\t\t\tstring sv = new string(p, iv, i - iv);\n\t\t\t\t\td[sk] = sv;\n\t\t\t\t\tik = i + 1;\n\t\t\t\t} else if (c == '=' && iv <= ik) {\n\t\t\t\t\tiv = i + 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\t//print.it(d);\n\t\t\treturn d;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets an adjacent or related UI element - next, child, parent, etc.\n\t\t/// </summary>\n\t\t/// <returns><c>null</c> if not found.</returns>\n\t\t/// <param name=\"navig\">\n\t\t/// String consisting of one or more navigation direction strings separated by space, like <c>\"parent next child4 first\"</c>.\n\t\t/// <br/>• <c>\"next\"</c> - next sibling UI element in the same parent UI element.\n\t\t/// <br/>• <c>\"previous\"</c> - previous sibling UI element in the same parent UI element.\n\t\t/// <br/>• <c>\"first\"</c> - first child UI element.\n\t\t/// <br/>• <c>\"last\"</c> - last child UI element.\n\t\t/// <br/>• <c>\"parent\"</c> - parent (container) UI element.\n\t\t/// <br/>• <c>\"child\"</c> - child UI element by 1-based index. Example: <c>\"child3\"</c> (3-th child). Negative index means from end, for example -1 is the last child.\n\t\t/// <br/>• <c>\"#N\"</c> - custom. More info in Remarks.\n\t\t/// <br/>• Some elements also support <c>\"up\"</c>, <c>\"down\"</c>, <c>\"left\"</c>, <c>\"right\"</c>.\n\t\t/// </param>\n\t\t/// <param name=\"waitS\">Wait for the wanted UI element max this number of seconds. Default 0 (don't wait). If negative, waits forever.</param>\n\t\t/// <exception cref=\"ArgumentException\">Invalid <i>navig</i> string.</exception>\n\t\t/// <remarks>\n\t\t/// Can be 2 letters, like <c>\"pr\"</c> for <c>\"previous\"</c>.\n\t\t/// A string like <c>\"next3\"</c> or <c>\"next,3\"</c> is the same as <c>\"next next next\"</c>. Except for <c>\"child\"</c>.\n\t\t/// Use string like <c>\"#1000\"</c> to specify a custom <i>navDir</i> value to pass to <ms>IAccessible.accNavigate</ms>.\n\t\t/// \n\t\t/// For <c>\"child\"</c> the function calls API <ms>AccessibleChildren</ms>.\n\t\t/// For <c>\"parent\"</c> the function calls <ms>IAccessible.get_accParent</ms>. Few UI elements don't support. Some UI elements return a different parent than in the tree of UI elements.\n\t\t/// For others the function calls <ms>IAccessible.accNavigate</ms>. Not all UI elements support it. Some UI elements skip invisible siblings. Instead you can use <c>\"parent childN\"</c> or <c>\"childN\"</c>.\n\t\t/// </remarks>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// a = e.Navigate(\"parent next ch3\");\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic elm Navigate(string navig, double waitS = 0) {\n\t\t\tNot_.Null(navig);\n\t\t\tThrowIfDisposed_();\n\t\t\tint hr; Cpp.Cpp_Acc ca;\n\t\t\tif (waitS == 0) {\n\t\t\t\thr = Cpp.Cpp_AccNavigate(this, navig, out ca);\n\t\t\t} else {\n\t\t\t\tvar loop = new WaitLoop(waitS > 0 ? -waitS : 0d);\n\t\t\t\tdo hr = Cpp.Cpp_AccNavigate(this, navig, out ca);\n\t\t\t\twhile (hr != 0 && hr != (int)Cpp.EError.InvalidParameter && loop.Sleep());\n\t\t\t}\n\t\t\tGC.KeepAlive(this);\n\t\t\tif (hr == (int)Cpp.EError.InvalidParameter) throw new ArgumentException(\"Invalid navig string.\");\n\t\t\tlastError.code = hr;\n\t\t\treturn hr == 0 ? new elm(ca) : null;\n\t\t\t\n\t\t\t//FUTURE: when fails, possibly this is disconnected etc. Retry find with same elmFinder.\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets parent element. Same as <see cref=\"Navigate\"/> with argument <c>\"pa\"</c>.\n\t\t/// </summary>\n\t\t/// <returns><c>null</c> if failed.</returns>\n\t\tpublic elm Parent => Navigate(\"pa\");\n\t\t//info: Navigate(\"pa\") is optimized in C++\n\t\t//Chrome bug: the parent element retrieved in this way has some incorrect properties.\n\t\t//\tEg Parent.WndContainer is the legacy control, whereas this.WndContainer is the top-level window.\n\t\t\n\t\t/// <summary>\n\t\t/// Gets HTML.\n\t\t/// </summary>\n\t\t/// <returns><c>\"\"</c> if this is not a HTML element or if failed. Supports <see cref=\"lastError\"/>.</returns>\n\t\t/// <param name=\"outer\">If <c>true</c>, gets outer HTML (with tag and attributes), else inner HTML.</param>\n\t\t/// <remarks>\n\t\t/// Works with Chrome, Internet Explorer and apps that use their code (Edge, Opera, web browser controls...). This UI element must be found without flag <c>NotInProc</c>.\n\t\t/// If this is the root of web page (role <c>DOCUMENT</c> or <c>PANE</c>), gets web page body HTML.\n\t\t/// </remarks>\n\t\tpublic string Html(bool outer) {\n\t\t\tThrowIfDisposed_();\n\t\t\tint hr = _Hresult(_FuncId.html, Cpp.Cpp_AccWeb(this, outer ? \"'o\" : \"'i\", out BSTR s));\n\t\t\tGC.KeepAlive(this);\n\t\t\treturn _BstrToString(hr, s);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets a HTML attribute.\n\t\t/// </summary>\n\t\t/// <returns><c>\"\"</c> if this is not a HTML element or does not have the specified attribute or failed. Supports <see cref=\"lastError\"/>.</returns>\n\t\t/// <param name=\"name\">Attribute name, for example <c>\"href\"</c>, <c>\"id\"</c>, <c>\"class\"</c>. Full, case-sensitive.</param>\n\t\t/// <remarks>\n\t\t/// Works with Chrome, Internet Explorer and apps that use their code (Edge, Opera, web browser controls...). This UI element must be found without flag <c>NotInProc</c>.\n\t\t/// </remarks>\n\t\t/// <exception cref=\"ArgumentException\"><i>name</i> is <c>null</c>/<c>\"\"</c>/invalid.</exception>\n\t\tpublic string HtmlAttribute(string name) {\n\t\t\tThrowIfDisposed_();\n\t\t\tif (name.NE() || name[0] == '\\'') throw new ArgumentException(\"Invalid name.\");\n\t\t\tint hr = _Hresult(_FuncId.html, Cpp.Cpp_AccWeb(this, name, out BSTR s));\n\t\t\tGC.KeepAlive(this);\n\t\t\treturn _BstrToString(hr, s);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets all HTML attributes.\n\t\t/// </summary>\n\t\t/// <returns>Empty dictionary if this is not a HTML element or does not have attributes or failed. Supports <see cref=\"lastError\"/>.</returns>\n\t\t/// <remarks>\n\t\t/// Works with Chrome, Internet Explorer and apps that use their code (Edge, Opera, web browser controls...). This UI element must be found without flag <c>NotInProc</c>.\n\t\t/// </remarks>\n\t\tpublic Dictionary<string, string> HtmlAttributes() {\n\t\t\tThrowIfDisposed_();\n\t\t\tint hr = Cpp.Cpp_AccWeb(this, \"'a\", out BSTR s);\n\t\t\tGC.KeepAlive(this);\n\t\t\t_Hresult(_FuncId.html, hr);\n\t\t\tif (hr != 0) return new();\n\t\t\tusing (s) return AttributesToDictionary_(s.Ptr, s.Length);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Scrolls this UI element into view.\n\t\t/// </summary>\n\t\t/// <exception cref=\"AuException\">Failed to scroll, or the UI element does not support scrolling.</exception>\n\t\t/// <remarks>\n\t\t/// This function works with these UI elements:\n\t\t/// - Web page elements in Chrome, Internet Explorer and apps that use their code (Edge, Opera, web browser controls...). To find UI element, use role prefix <c>\"web:\"</c>, <c>\"firefox:\"</c> or <c>\"chrome:\"</c>, and don't use flag <see cref=\"EFFlags.NotInProc\"/>.\n\t\t/// - Standard treeview, listview and listbox controls.\n\t\t/// - Some other controls if found with flag <see cref=\"EFFlags.UIA\"/>.\n\t\t/// \n\t\t/// Some apps after scrolling update <see cref=\"Rect\"/> with a delay. Some apps never update it for existing <see cref=\"elm\"/> variables. This function does not wait.\n\t\t/// </remarks>\n\t\tpublic void ScrollTo() {\n\t\t\tThrowIfDisposed_();\n\t\t\tAuException.ThrowIfHresultNot0(_ScrollTo(), \"*scroll\");\n\t\t}\n\t\t\n\t\tint _ScrollTo() {\n\t\t\tint hr = 1;\n\t\t\tif (MiscFlags.Has(EMiscFlags.UIA)) {\n\t\t\t\thr = _InvokeL('s');\n\t\t\t} else if (Item == 0) {\n\t\t\t\thr = Cpp.Cpp_AccWeb(this, \"'s\", out _);\n\t\t\t\t//tested: Chrome and Firefox don't support UI Automation scrolling (IUIAutomationScrollItemPattern).\n\t\t\t} else if (RoleInt is ERole.LISTITEM or ERole.TREEITEM) { //try messages of some standard controls\n\t\t\t\tvar w = WndContainer;\n\t\t\t\tswitch (w.CommonControlType) {\n\t\t\t\tcase WControlType.Listview when RoleInt is ERole.LISTITEM:\n\t\t\t\t\tif (0 != w.Send(LVM_ENSUREVISIBLE, Item - 1, 1)) hr = 0;\n\t\t\t\t\tbreak;\n\t\t\t\tcase WControlType.Listbox when RoleInt is ERole.LISTITEM:\n\t\t\t\t\tif (-1 != w.Send(LB_SETTOPINDEX, Item - 1)) hr = 0;\n\t\t\t\t\tbreak;\n\t\t\t\tcase WControlType.Treeview when RoleInt is ERole.TREEITEM:\n\t\t\t\t\tif (osVersion.is32BitProcessAnd64BitOS && !w.Is32Bit) break; //cannot get 64-bit HTREEITEM\n\t\t\t\t\tnint hi = w.Send(TVM_MAPACCIDTOHTREEITEM, Item); if (hi == 0) break;\n\t\t\t\t\tw.Send(TVM_ENSUREVISIBLE, 0, hi);\n\t\t\t\t\thr = 0; //the API returns nonzero only if actually scrolls, not if already visible\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tGC.KeepAlive(this);\n\t\t\treturn hr;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Waits for a user-defined state/condition of this UI element. For example enabled, checked, changed name.\n\t\t/// </summary>\n\t\t/// <param name=\"timeout\">Timeout, seconds. Can be 0 (infinite), >0 (exception) or &lt;0 (no exception). More info: [](xref:wait_timeout).</param>\n\t\t/// <param name=\"condition\">Callback function (eg lambda). It is called repeatedly, until returns a value other than <c>default(T)</c>, for example <c>true</c>.</param>\n\t\t/// <returns>Returns the value returned by the callback function. On timeout returns <c>default(T)</c> if <i>timeout</i> is negative; else exception.</returns>\n\t\t/// <exception cref=\"TimeoutException\"><i>timeout</i> time has expired (if > 0).</exception>\n\t\t/// <exception cref=\"AuWndException\">Failed to get container window (<see cref=\"WndContainer\"/>), or it was closed while waiting.</exception>\n\t\tpublic T WaitFor<T>(Seconds timeout, Func<elm, T> condition) {\n\t\t\tvar w = WndContainer; //calls ThrowIfDisposed_\n\t\t\tvar loop = new WaitLoop(timeout);\n\t\t\tfor (; ; ) {\n\t\t\t\tw.ThrowIfInvalid();\n\t\t\t\tT r = condition(this);\n\t\t\t\tbool ok = !EqualityComparer<T>.Default.Equals(r, default);\n\t\t\t\tw.ThrowIfInvalid(); //eg when waiting for button enabled, if window closed while in callback, the DISABLED state may be removed\n\t\t\t\tif (ok) return r;\n\t\t\t\tif (!loop.Sleep()) return default;\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Moves the cursor (mouse pointer) to this UI element.\n\t\t/// </summary>\n\t\t/// <param name=\"x\">X coordinate in the bounding rectangle of this UI element. Default - center. Examples: <c>10</c>, <c>^10</c> (reverse), <c>.5f</c> (fraction).</param>\n\t\t/// <param name=\"y\">Y coordinate in the bounding rectangle of this UI element. Default - center.</param>\n\t\t/// <param name=\"scroll\">If not 0, the function at first calls <see cref=\"ScrollTo\"/>. If it succeeds, waits <i>scroll</i> number of milliseconds (let the target app update the UI element rectangle etc).</param>\n\t\t/// <exception cref=\"AuException\">Failed to get UI element rectangle in container window (<see cref=\"elm.WndContainer\"/>).</exception>\n\t\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"mouse.move(wnd, Coord, Coord, bool)\"/>.</exception>\n\t\t/// <remarks>\n\t\t/// Calls <see cref=\"mouse.move(wnd, Coord, Coord, bool)\"/>. To get rectangle in window, uses <see cref=\"GetRect(out RECT, wnd, bool)\"/> with <i>intersect</i> <c>true</c>.\n\t\t/// </remarks>\n\t\tpublic void MouseMove(Coord x = default, Coord y = default, int scroll = 0)\n\t\t\t=> _ElmMouseAction(false, x, y, default, scroll);\n\t\t\n\t\t/// <summary>\n\t\t/// Clicks this UI element.\n\t\t/// </summary>\n\t\t/// <param name=\"x\">X coordinate in the bounding rectangle of this UI element. Default - center. Examples: <c>10</c>, <c>^10</c> (reverse), <c>.5f</c> (fraction).</param>\n\t\t/// <param name=\"y\">Y coordinate in the bounding rectangle of this UI element. Default - center.</param>\n\t\t/// <param name=\"button\">Which button and how to use it.</param>\n\t\t/// <param name=\"scroll\">If not 0, the function at first calls <see cref=\"ScrollTo\"/>. If it succeeds, waits <i>scroll</i> number of milliseconds (let the target app update the UI element rectangle etc). Valid values are 0-5000. Tip: if does not scroll, try to find the UI element with flag <c>UIA</c>.</param>\n\t\t/// <exception cref=\"AuException\">Failed to get UI element rectangle in container window (<see cref=\"elm.WndContainer\"/>).</exception>\n\t\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"mouse.clickEx(MButton, wnd, Coord, Coord, bool)\"/>.</exception>\n\t\t/// <remarks>\n\t\t/// Calls <see cref=\"mouse.clickEx(MButton, wnd, Coord, Coord, bool)\"/>. To get rectangle in window, uses <see cref=\"GetRect(out RECT, wnd, bool)\"/> with <i>intersect</i> <c>true</c>.\n\t\t/// </remarks>\n\t\tpublic MRelease MouseClick(Coord x = default, Coord y = default, MButton button = MButton.Left, int scroll = 0) {\n\t\t\t_ElmMouseAction(true, x, y, button, scroll);\n\t\t\treturn button;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Double-clicks this UI element.\n\t\t/// </summary>\n\t\t/// <inheritdoc cref=\"MouseClick(Coord, Coord, MButton, int)\"/>\n\t\tpublic void MouseClickD(Coord x = default, Coord y = default, int scroll = 0)\n\t\t\t=> MouseClick(x, y, MButton.DoubleClick, scroll);\n\t\t\n\t\t/// <summary>\n\t\t/// Right-clicks this UI element.\n\t\t/// </summary>\n\t\t/// <inheritdoc cref=\"MouseClick(Coord, Coord, MButton, int)\"/>\n\t\tpublic void MouseClickR(Coord x = default, Coord y = default, int scroll = 0)\n\t\t\t=> MouseClick(x, y, MButton.Right, scroll);\n\t\t\n\t\tvoid _ElmMouseAction(bool click, Coord x, Coord y, MButton button, int scroll) {\n\t\t\tvar (w, r) = _GetWndAndRectForClick(scroll);\n\t\t\tvar p = Coord.NormalizeInRect(x, y, r, centerIfEmpty: true);\n\t\t\t//if (!w.Is0) {\n\t\t\tif (button == 0) mouse.move(w, p.x, p.y);\n\t\t\telse mouse.clickEx(button, w, p.x, p.y);\n\t\t\t//} else { //no. Unsafe, can click in another window.\n\t\t\t//\tif (button == 0) mouse.move(p);\n\t\t\t//\telse mouse.clickEx(button, p);\n\t\t\t//}\n\t\t}\n\t\t\n\t\t(wnd w, RECT r) _GetWndAndRectForClick(int scroll) {\n\t\t\tif (scroll != 0) {\n\t\t\t\tif ((uint)scroll > 5000) throw new ArgumentException(\"Valid values 0-5000\", nameof(scroll));\n\t\t\t\tif (0 == _ScrollTo()) wait.ms(scroll);\n\t\t\t}\n\t\t\t\n\t\t\tif (!GetRect(out var r)) throw new AuException(0, \"*get UI element rectangle\");\n\t\t\tif (r.NoArea) throw new AuException(IsOffscreen ? \"The UI element is offscreen. Try scroll.\" : \"The UI element rectangle is empty\");\n\t\t\t//var w = WndContainer; //need window for mouse functions, else could click another window etc\n\t\t\tvar w = WndTopLevel; //direct parent control may be zero-size etc, eg in Win11 Paint when found with UIA\n\t\t\tbool retry = false; var r0 = r;\n\t\t\tg1:\n\t\t\tif (!w.GetRect(out var rw)) throw new AuException(0, \"*get container window\");\n\t\t\tif (!r.Intersect(rw)) {\n\t\t\t\tif (!retry && scroll != 0) { //workaround for: WndContainer of a popup item may be the popup's owner. Eg WPF combobox.\n\t\t\t\t\tw = w.Window.Get.EnabledOwned();\n\t\t\t\t\tif (retry = !w.Is0) { r = r0; goto g1; }\n\t\t\t\t}\n\t\t\t\tthrow new AuException(\"The UI element rectangle is not in the container window.\" + (scroll != 0 ? null : \" Try scroll.\"));\n\t\t\t}\n\t\t\tif (!w.MapScreenToClient(ref r)) throw new AuException(0);\n\t\t\treturn (w, r);\n\t\t}\n\t\t\n\t\t//rejected: automatically scroll if need.\n\t\t//\tImpossible to reliably detect whether need to scroll.\n\t\t//\tThis was an attempt, but it does not work well. And can't click non-client elements.\n\t\t//(wnd w, RECT r) _GetWndAndRectForClick() {\n\t\t//\t//var w = WndContainer; //with window the mouse functions are more reliable, eg will not click another window\n\t\t//\tvar w = WndTopLevel; //direct parent control may be zero-size etc, eg in Win11 Paint when found with UIA\n\t\t//\tif (w.Is0) throw new AuException(0, \"*get container window\");\n\t\t//\tRECT r = _GetRect(), rr = r;\n\t\t//\tprint.it(r);\n\t\t//\tbool retry = false;\n\t\t//\tgRetry:\n\t\t//\tbool bad = r.NoArea;\n\t\t//\tif (!bad) {\n\t\t//\t\tbad = (!retry && IsOffscreen) || !r.Intersect(_GetContainerClientRect(w));\n\t\t//\t}\n\t\t//\tprint.it(bad);\n\t\t//\tif (bad) {\n\t\t//\t\tif (!retry) {\n\t\t//\t\t\tif (0 == _ScrollTo())\n\t\t//\t\t\t\tif (wait.until(-2, () => (r = _GetRect()) != rr)) {\n\t\t//\t\t\t\t\t30.ms(); retry = true; goto gRetry;\n\t\t//\t\t\t\t}\n\t\t//\t\t}\n\t\t//\t\tthrow new AuException(0, \"The UI element rectangle is \" + (rr.NoArea ? \"empty.\" : \"offscreen.\"));\n\t\t//\t}\n\t\t//\treturn (w, r);\n\t\t\n\t\t//\tRECT _GetRect() => GetRect(out var r, w) ? r : throw new AuException(0, \"*get UI element rectangle\");\n\t\t\n\t\t//\t//todo: now cannot click in nonclient area.\n\t\t\n\t\t//\t//never mind: should intersect with all ancestor elements and windows.\n\t\t//\t//\tNow no OFFSCREEN state if partially clipped by an ancestor rect.\n\t\t//\t//\tSlow and unreliable, because visible children can be not in parent rect. Eg pagetab or treeitem.\n\t\t\n\t\t//\t//problem: Firefox never updates Rect after ScrollTo. Need to find again.\n\t\t//\t//\tChrome updates after several ms. Chrome does not update OFFSCREEN (or with a delay?).\n\t\t\n\t\t//\t//problem: some fake container windows may be zero-size etc, and the element is drawn on another window.\n\t\t//\t//\tshoulddo: test more.\n\t\t//}\n\t\t\n\t\t/// <summary>\n\t\t/// Posts mouse-click messages to the container window, using coordinates in this UI element.\n\t\t/// </summary>\n\t\t/// <param name=\"x\">X coordinate in the bounding rectangle of this UI element. Default - center. Examples: <c>10</c>, <c>^10</c> (reverse), <c>.5f</c> (fraction).</param>\n\t\t/// <param name=\"y\">Y coordinate in the bounding rectangle of this UI element. Default - center.</param>\n\t\t/// <param name=\"button\">Can specify the left (default), right or middle button. Also flag for double-click, press or release.</param>\n\t\t/// <param name=\"scroll\">If not 0, the function calls <see cref=\"ScrollTo\"/>. If it succeeds, waits <i>scroll</i> number of milliseconds (let the target app update the UI element rectangle etc). Valid values are 0-5000. Tip: if does not scroll, try to find the UI element with flag <c>UIA</c>.</param>\n\t\t/// <exception cref=\"AuException\">\n\t\t/// - Failed to get rectangle or container window.\n\t\t/// - The element is invisible/offscreen.\n\t\t/// </exception>\n\t\t/// <exception cref=\"ArgumentException\">Unsupported button specified.</exception>\n\t\t/// <remarks>\n\t\t/// Does not move the mouse.\n\t\t/// Does not wait until the target application finishes processing the message.\n\t\t/// Works not with all elements.\n\t\t/// Try this function when <see cref=\"Invoke\"/> does not work and you don't want to use <c>MouseClick</c>.\n\t\t/// </remarks>\n\t\tpublic void PostClick(Coord x = default, Coord y = default, MButton button = MButton.Left, int scroll = 0) {\n\t\t\tvar (w, r) = _GetWndAndRectForClick(scroll);\n\t\t\t\n\t\t\tmouse.PostClick_(w, r, x, y, button);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Posts mouse-double-click messages to the container window, using coordinates in this UI element.\n\t\t/// </summary>\n\t\t/// <exception cref=\"AuException\">\n\t\t/// - Failed to get rectangle or container window.\n\t\t/// - The element is invisible/offscreen.\n\t\t/// </exception>\n\t\t/// <inheritdoc cref=\"PostClick(Coord, Coord, MButton, int)\" path=\"//param|//remarks\"/>\n\t\tpublic void PostClickD(Coord x = default, Coord y = default) => PostClick(x, y, MButton.DoubleClick);\n\t\t\n\t\t/// <summary>\n\t\t/// Posts mouse-right-click messages to the container window, using coordinates in this UI element.\n\t\t/// </summary>\n\t\t/// <inheritdoc cref=\"PostClickD(Coord, Coord)\"/>\n\t\tpublic void PostClickR(Coord x = default, Coord y = default) => PostClick(x, y, MButton.Right);\n\t\t\n\t\t/// <summary>\n\t\t/// Makes this UI element focused (<see cref=\"Focus\"/>) and calls <see cref=\"keys.send\"/>.\n\t\t/// </summary>\n\t\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"Focus\"/> and <see cref=\"keys.send\"/>.</exception>\n\t\t/// <inheritdoc cref=\"keys.send\" path=\"/param\"/>\n\t\tpublic void SendKeys([ParamString(PSFormat.Keys)] params KKeysEtc[] keysEtc) {\n\t\t\tbool andSelect = RoleInt is ERole.TREEITEM or ERole.LISTITEM;\n\t\t\tFocus(andSelect);\n\t\t\tkeys.send(keysEtc);\n\t\t}\n\t\t\n\t\t//rejected: too simple and limited. No x y, scroll, button.\n\t\t///// <summary>\n\t\t///// Clicks this UI element and calls <see cref=\"keys.send\"/>.\n\t\t///// </summary>\n\t\t///// <param name=\"doubleClick\">If <c>true</c>, calls <see cref=\"MouseClickD\"/>, else <see cref=\"MouseClick\"/>.</param>\n\t\t///// <exception cref=\"Exception\">Exceptions of <see cref=\"MouseClick\"/> and <see cref=\"keys.send\"/>.</exception>\n\t\t///// <inheritdoc cref=\"keys.send\" path=\"/param\"/>\n\t\t//public void SendKeys(bool doubleClick, [ParamString(PSFormat.keys)] params KKeysEtc[] keysEtc) {\n\t\t//\tif (doubleClick) MouseClickD(); else MouseClick();\n\t\t//\tkeys.send(keysEtc);\n\t\t//}\n\t\t\n\t\t//rejected. Use SendKeys, it allows to specify keys to replace (Ctrl+A), append (Ctrl+End), etc.\n\t\t//public void SendText(string text, bool replace, [more options?]) {\n\t\t//\tFocus();\n\t\t//\t...\n\t\t//\tkeys.sendt(text);\n\t\t//}\n\t\t\n\t\tbool _CheckNeedToggle(bool check) {\n\t\t\tThrowIfDisposed_();\n\t\t\tvar state = State;\n\t\t\t//if (state.Has(EState.DISABLED)) throw new AuException(\"Disabled.\"); //can do more bad than good, eg if DISABLED state is when not actually disabled\n\t\t\tbool isChecked = state.Has(EState.CHECKED);\n\t\t\tif (!isChecked) isChecked = state.Has(EState.PRESSED) && !MiscFlags.HasAny(EMiscFlags.UIA | EMiscFlags.Java) && RoleInt == ERole.BUTTON /*&& WndContainer.ClassNameIs(\"Chrome*\")*/; //eg the toggle buttons in Chrome settings have state PRESSED instead of CHECKED\n\t\t\treturn isChecked != check;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Checks or unchecks this checkbox or toggle-button, or selects this radio button. Uses <see cref=\"Invoke\"/> or <see cref=\"SendKeys\"/>.\n\t\t/// </summary>\n\t\t/// <param name=\"check\"><c>true</c> to check, <c>false</c> to uncheck.</param>\n\t\t/// <param name=\"keys\">Keys for <see cref=\"SendKeys\"/>. If <c>\"\"</c>, uses <c>\"Space\"</c>. If <c>null</c> (default), uses <see cref=\"Invoke\"/>.</param>\n\t\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"Invoke\"/> or <see cref=\"SendKeys\"/>.</exception>\n\t\t/// <remarks>\n\t\t/// Does nothing if the UI element already has the requested checked/unchecked state. Else tries to change the state and does not verify whether it actually worked.\n\t\t/// \n\t\t/// Does not work with 3-state checkboxes and with elements that never have <c>CHECKED</c> state.\n\t\t/// </remarks>\n\t\tpublic void Check(bool check = true, [ParamString(PSFormat.Keys)] string keys = null) {\n\t\t\tif (!_CheckNeedToggle(check)) return;\n\t\t\tif (keys != null) {\n\t\t\t\tSendKeys(keys.Length == 0 ? \"Space\" : keys);\n\t\t\t} else {\n\t\t\t\t_Invoke(MiscFlags.Has(EMiscFlags.UIA) ? 'c' : 'a');\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Checks or unchecks this checkbox or toggle-button, or selects this radio button. To check/uncheck calls callback function.\n\t\t/// </summary>\n\t\t/// <param name=\"action\">Callback function that should check or uncheck this UI element. Its parameter is this variable.</param>\n\t\t/// <exception cref=\"Exception\">Exceptions of the callback function.</exception>\n\t\t/// <inheritdoc cref=\"Check(bool, string)\"/>\n\t\tpublic void Check(bool check, Action<elm> action) {\n\t\t\tif (!_CheckNeedToggle(check)) return;\n\t\t\taction(this);\n\t\t}\n\t\t\n\t\t//rejected: Check and Expand overloads for mouse.\n\t\t//\tScript code like 'e.Check(true, false) looks not good.\n\t\t//\tBetter 'e.Check(true, e => e.MouseClick())' or 'e.Check(true, e => e.PostClick())'.\n\t\t\n\t\t/// <summary>\n\t\t/// Expands or collapse this expandable UI element (tree item, combo box, expander, dropdown button).\n\t\t/// </summary>\n\t\t/// <param name=\"expand\"><c>true</c> to expand, <c>false</c> to collapse.</param>\n\t\t/// <param name=\"keys\">If not <c>null</c>, makes this element focused and presses these keys. See <see cref=\"keys.send\"/>. If <c>\"\"</c>, uses keys commonly used for that UI element type, for example <c>Right</c>/<c>Left</c> for <c>TREEITEM</c>, <c>Alt+Down</c> for <c>COMBOBOX</c>. If <c>null</c>, uses <see cref=\"Invoke\"/> or similar functions, which often are available only if the element was found with flag <c>UIA</c>; if unavailable or fails, works like with <i>keys</i> <c>\"\"</c>.</param>\n\t\t/// <param name=\"waitS\">If not 0, waits for new expanded/collapsed state max this number of seconds; on timeout throws exception, unless negative.</param>\n\t\t/// <param name=\"ignoreState\">Ignore initial <c>EXPANDED</c>/<c>COLLAPSED</c> state and always perform the expand/collapse action. Can be useful when <see cref=\"State\"/> <c>EXPANDED</c>/<c>COLLAPSED</c> is incorrect. To ignore final state, use negative <i>waitS</i> instead, for example -0.001.</param>\n\t\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"SendKeys\"/>.</exception>\n\t\t/// <exception cref=\"TimeoutException\">The state didn't change in <i>waitS</i> seconds (if > 0).</exception>\n\t\t/// <remarks>\n\t\t/// Does nothing if the UI element already has the requested expanded/collapsed state.\n\t\t/// \n\t\t/// Works with UI elements that have <see cref=\"State\"/> <c>EXPANDED</c> when expanded and <c>COLLAPSED</c> when collapsed. Also with UI elements that have state <c>CHECKED</c> or <c>PRESSED</c> when expanded and don't have this state when collapsed.\n\t\t/// </remarks>\n\t\tpublic void Expand(bool expand = true, [ParamString(PSFormat.Keys)] string keys = null, double waitS = 1, bool ignoreState = false) {\n\t\t\t_Expand(expand, keys, null, waitS, ignoreState);\n\t\t}\n\t\t\n\t\t/// <param name=\"action\">Callback function that should expand or collapse this UI element. Its parameter is this variable.</param>\n\t\t/// <exception cref=\"Exception\">Exceptions of the callback function.</exception>\n\t\t/// <inheritdoc cref=\"Expand(bool, string, double, bool)\"/>\n\t\tpublic void Expand(bool expand, Action<elm> action, double waitS = 1, bool ignoreState = false) {\n\t\t\t_Expand(expand, null, action, waitS, ignoreState);\n\t\t}\n\t\t\n\t\tvoid _Expand(bool expand, string keys, Action<elm> action, double waitS, bool ignoreState) {\n\t\t\tThrowIfDisposed_();\n\t\t\t\n\t\t\tbool _NeedToggle(bool expand) {\n\t\t\t\tvar state = State;\n\t\t\t\t//print.it(RoleInt, Role, state);\n\t\t\t\t//note: ignore DISABLED state. Some non-disabled elements have it. Also probably would need to get state of parent TREEVIEW.\n\t\t\t\tbool isExpanded = !state.Has(EState.COLLAPSED) && state.HasAny(EState.EXPANDED | EState.CHECKED | EState.PRESSED);\n\t\t\t\treturn isExpanded != expand;\n\t\t\t}\n\t\t\tif (!ignoreState) if (!_NeedToggle(expand)) return;\n\t\t\t\n\t\t\tint how = action != null ? 2 : keys != null ? 1 : 0;\n\t\t\tif (how == 0) {\n\t\t\t\thow = 1;\n\t\t\t\tif (MiscFlags.Has(EMiscFlags.UIA)) {\n\t\t\t\t\tif (0 == _InvokeL(expand ? 'E' : 'e')) how = 0;\n\t\t\t\t} else if (_ClassicTreeview()) {\n\t\t\t\t\thow = 0;\n\t\t\t\t} else {\n\t\t\t\t\tvar da = DefaultAction;\n\t\t\t\t\tif (!da.NE()) {\n\t\t\t\t\t\tif (MiscFlags.Has(EMiscFlags.Java)) {\n\t\t\t\t\t\t\t//usually treeitem's default action is \"toggleexpand\", and it works. Other ways (mouse, focus+keys) are unreliable.\n\t\t\t\t\t\t\t//combobox action is \"togglePopup\", and requires active window. Does not wait until popup closed (good).\n\t\t\t\t\t\t\tif (da != \"toggleexpand\") WndTopLevel.Activate();\n\t\t\t\t\t\t\tif (0 == _InvokeL('a', da)) how = 0;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tif (0 == _InvokeL()) how = 0;\n\t\t\t\t\t\t\t//never mind: not all actions Expand/Collapse, even if TREEITEM. Eg in Thunderbird.\n\t\t\t\t\t\t\t//\tCould use keys etc if state didn't change eg in 0.5 s, but it can make less reliable.\n\t\t\t\t\t\t\t//\tLet users choose another overload.\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (RoleInt == ERole.COMBOBOX) { //classic combobox?\n\t\t\t\t\t\tif (0 == _GetWnd(out wnd w) && w.CommonControlType == WControlType.Combobox) {\n\t\t\t\t\t\t\tw.SendNotify(0x014F, expand ? 1 : 0); //CB_SHOWDROPDOWN\n\t\t\t\t\t\t\thow = 0;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tif (how == 1) {\n\t\t\t\tFocus(andSelect: RoleInt is ERole.TREEITEM or ERole.LISTITEM or ERole.Custom); //exception if fails\n\t\t\t\tif (keys.NE()) {\n\t\t\t\t\tvar role = MiscFlags.Has(EMiscFlags.Java) && Role == \"combo box\" ? ERole.COMBOBOX : RoleInt;\n\t\t\t\t\tkeys = role switch {\n\t\t\t\t\t\tERole.TREEITEM or ERole.LISTITEM or ERole.Custom => expand ? \"Right\" : \"Left\", //LISTITEM for treeviews made from listviews (not tested); Custom because we prefer treeviews\n\t\t\t\t\t\tERole.COMBOBOX or ERole.DROPLIST => expand ? \"Alt+Down\" : \"Esc\", //DROPLIST used by the classic date/time picker, but does not work because state always 0\n\t\t\t\t\t\tERole.BUTTON or ERole.CHECKBOX => \"Space\", //eg expander\n\t\t\t\t\t\tERole.BUTTONDROPDOWN or ERole.BUTTONDROPDOWNGRID or ERole.BUTTONMENU => expand ? \"Space\" : \"Esc\",\n\t\t\t\t\t\t_ => expand ? \"Down\" : \"Esc\", //eg classic toolbar's SPLITBUTTON's dropdown part (MENUITEM); also classic SPLITBUTTON\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\tAu.keys.send(keys);\n\t\t\t} else if (how == 2) {\n\t\t\t\taction(this);\n\t\t\t}\n\t\t\t\n\t\t\tif (waitS != 0) {\n\t\t\t\t//10.ms();\n\t\t\t\twait.until(waitS, () => !_NeedToggle(expand));\n\t\t\t}\n\t\t\t\n\t\t\tbool _ClassicTreeview() {\n\t\t\t\tif (Item == 0 || RoleInt != ERole.TREEITEM) return false;\n\t\t\t\tif (0 != _GetWnd(out wnd w) || w.CommonControlType != WControlType.Treeview) return false;\n\t\t\t\tif (osVersion.is32BitProcessAnd64BitOS && !w.Is32Bit) return false; //cannot get 64-bit HTREEITEM\n\t\t\t\tnint hi = w.Send(TVM_MAPACCIDTOHTREEITEM, Item); if (hi == 0) return false;\n\t\t\t\t_Expand_ClassicTreeview(w, hi, expand);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\t\n\t\tstatic void _Expand_ClassicTreeview(wnd w, nint hi, bool expand) {\n#if false //like MSAA Invoke. Bad: no TVN_ITEMEXPANDED etc; eg does not change folder icon open/closed.\n\t\t\tw.Send(0x1102, expand ? 2 : 1, hi); //TVM_EXPAND(TVE_EXPAND:TVE_COLLAPSE)\n\t\t\tif (expand) w.Send(0x1114, 0, hi); //TVM_ENSUREVISIBLE\n#else //like UIA expand/collapse. Bad: dirty; selects, which may do more than expand/collapse; for TVSI_NOSINGLEEXPAND need manifest.\n\t\t\tfor (int i = 0; i < 5; i++) { //eg in old VS first time returns 0, although works. UIA sends 2 times.\n\t\t\t\tif (0 != w.Send(TVM_SELECTITEM, TVGN_CARET | TVSI_NOSINGLEEXPAND, hi)) break;\n\t\t\t\tDebug_.PrintIf(i > 0, \"elm.Expand\");\n\t\t\t\t//if (i > 0) wait.ms(i * 10);\n\t\t\t}\n\t\t\tint k = (int)(expand ? KKey.Right : KKey.Left);\n\t\t\tw.SendNotify(Api.WM_KEYDOWN, k);\n\t\t\tw.SendNotify(Api.WM_KEYUP, k);\n\t\t\t10.ms();\n\t\t\t//UIA posts, but then eg in old VS does not work if the control wasn't focused. Maybe that is why UIA always sets real focus.\n\t\t\t//CONSIDER: wnd.PostKey(), wnd.PostText().\n#endif\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Expands multiple treeview control items using a path string.\n\t\t/// </summary>\n\t\t/// <param name=\"path\">\n\t\t/// String or array consisting of names (<see cref=\"Name\"/>) of <c>TREEITEM</c> elements, like <c>\"One|Two|Three\"</c> or <c>[\"One\", \"Two\", \"Three\"]</c>.\n\t\t/// Name string format: [wildcard expression](xref:wildcard_expression).\n\t\t/// </param>\n\t\t/// <param name=\"keys\"><c>null</c> or keys to use to expand each element specified in <i>path</i>. See <see cref=\"Expand(bool, string, double, bool)\"/>.</param>\n\t\t/// <param name=\"waitS\">If not 0, after expanding each element waits for expanded state max this number of seconds; on timeout throws exception, unless negative. Also waits for each element this number of seconds; always exception if not found.</param>\n\t\t/// <param name=\"notLast\">Find but don't expand the last element specified in <i>path</i>. For example if it's not a folder, and therefore can't expand it, but you need to find it (this function returns it).</param>\n\t\t/// <returns><see cref=\"elm\"/> of the last element specified in <i>path</i>.</returns>\n\t\t/// <exception cref=\"ArgumentException\"><i>path</i> contains an invalid wildcard expression (<c>\"**options \"</c> or regular expression).</exception>\n\t\t/// <exception cref=\"NotFoundException\">Failed to find an element specified in <i>path</i>.</exception>\n\t\t/// <exception cref=\"AuException\">Failed.</exception>\n\t\t/// <exception cref=\"TimeoutException\">The state didn't change in <i>waitS</i> seconds (if > 0).</exception>\n\t\t/// <exception cref=\"NotSupportedException\">The treeview control type is not supported when this is a 32-bit process running on 64-bit OS (unlikely).</exception>\n\t\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"SendKeys\"/>.</exception>\n\t\t/// <remarks>\n\t\t/// This element can be a <c>TREE</c> or <c>TREEITEM</c>. If it is a collapsed <c>TREEITEM</c>, expands it. Then finds and expands elements specified in <i>path</i>.\n\t\t/// \n\t\t/// Does not work if all <c>TREEITEM</c> elements in the <c>TREE</c> control are its direct children, unless it's the standard Windows treeview control.\n\t\t/// </remarks>\n\t\tpublic elm Expand(Strings path, [ParamString(PSFormat.Keys)] string keys = null, double waitS = 3, bool notLast = false) {\n\t\t\treturn _ExpandPath(path, keys, null, waitS, notLast);\n\t\t}\n\t\t\n\t\t/// <param name=\"action\">Callback function that should expand UI elements.</param>\n\t\t/// <exception cref=\"Exception\">Exceptions of the callback function.</exception>\n\t\t/// <remarks></remarks>\n\t\t/// <inheritdoc cref=\"Expand(Au.Types.Strings, string, double, bool)\"/>\n\t\tpublic elm Expand(Strings path, Action<elm> action, double waitS = 3, bool notLast = false) {\n\t\t\treturn _ExpandPath(path, null, action, waitS, notLast);\n\t\t}\n\t\t\n\t\telm _ExpandPath(Strings path, string keys, Action<elm> action, double waitS, bool notLast) {\n\t\t\tThrowIfDisposed_();\n\t\t\tvar a = path.ToArray();\n\t\t\tvar e = this;\n\t\t\t//for classic treeview controls need special code if not UIA, because the MSAA tree is flat\n\t\t\tif (!MiscFlags.HasAny(EMiscFlags.UIA | EMiscFlags.Java) && 0 == _GetWnd(out wnd w) && w.CommonControlType == WControlType.Treeview) {\n\t\t\t\tif (osVersion.is32BitProcessAnd64BitOS && !w.Is32Bit) throw new NotSupportedException(\"32-bit process.\"); //cannot get 64-bit HTREEITEM\n\t\t\t\t\n\t\t\t\tnint hi = 0;\n\t\t\t\tif (Item > 0) {\n\t\t\t\t\thi = w.Send(TVM_MAPACCIDTOHTREEITEM, Item); if (hi == 0) throw new AuException();\n\t\t\t\t\t_ExpandIfNeed(this, hi);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tfor (int i = 0; i < a.Length; i++) {\n\t\t\t\t\tvar name = a[i];\n\t\t\t\t\tbool ok = waitS == 0 ? _Find(name) : wait.until(-Math.Abs(waitS), () => _Find(name));\n\t\t\t\t\tif (!ok) throw new NotFoundException(\"Not found elm expand path part: \" + name);\n\t\t\t\t\tif (notLast && i == a.Length - 1) break;\n\t\t\t\t\t_ExpandIfNeed(e, hi);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tbool _Find(wildex name) {\n\t\t\t\t\telm temp = null;\n\t\t\t\t\tfor (nint h = w.Send(TVM_GETNEXTITEM, hi == 0 ? TVGN_ROOT : TVGN_CHILD, hi); h != 0; h = w.Send(TVM_GETNEXTITEM, TVGN_NEXT, h)) {\n\t\t\t\t\t\tint item = (int)w.Send(TVM_MAPHTREEITEMTOACCID, h); if (item == 0) throw new AuException();\n\t\t\t\t\t\tvar acc = new Cpp.Cpp_Acc(_iacc, item) { misc = _misc };\n\t\t\t\t\t\tif (Item == 0) { acc.misc.level++; acc.misc.roleByte = (byte)ERole.TREEITEM; }\n\t\t\t\t\t\tvar s = _GetStringPropL(acc, 'n'); //the slowest part of this 'for'. FUTURE: if somewhere too slow, could make inproc.\n\t\t\t\t\t\tGC.KeepAlive(this);\n\t\t\t\t\t\tif (name.Match(s)) {\n\t\t\t\t\t\t\tif (temp == null) temp = new(acc, addRef: true); else temp.Item = item;\n\t\t\t\t\t\t\te = temp;\n\t\t\t\t\t\t\thi = h;\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvoid _ExpandIfNeed(elm e, nint hi) {\n\t\t\t\t\tif (_IsExpanded(w, hi)) return;\n\t\t\t\t\t\n\t\t\t\t\tif (keys != null) e.SendKeys(keys.Length > 0 ? keys : \"Right\");\n\t\t\t\t\telse if (action != null) action(e);\n\t\t\t\t\telse _Expand_ClassicTreeview(w, hi, true);\n\t\t\t\t\t//p1.Next();\n\t\t\t\t\t\n\t\t\t\t\tif (waitS != 0) wait.until(waitS, () => _IsExpanded(w, hi));\n\t\t\t\t\telse _IsExpanded(w, hi); //usually waits\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tstatic bool _IsExpanded(wnd w, nint hi) => 0 != ((int)w.Send(TVM_GETITEMSTATE, hi, TVIS_EXPANDED) & TVIS_EXPANDED);\n\t\t\t} else {\n\t\t\t\tif (State.Has(EState.COLLAPSED))\n\t\t\t\t\t_Expand(true, keys, action, waitS, ignoreState: true);\n\t\t\t\t\n\t\t\t\tforeach (var name in a) {\n\t\t\t\t\tint level = e.Level;\n\t\t\t\t\te = e.Elm[null, name, \"level=0\", also: o => o.State.HasAny(EState.EXPANDED | EState.COLLAPSED)].Find(-(Math.Abs(waitS) + .01));\n\t\t\t\t\tif (e == null) throw new NotFoundException(\"Not found elm expand path part: \" + name);\n\t\t\t\t\te.Level = level + 1;\n\t\t\t\t\te._Expand(true, keys, action, waitS, ignoreState: false);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn e;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Finds and selects an item in the drop-down list of this combo box or drop-down button.\n\t\t/// </summary>\n\t\t/// <param name=\"item\">\n\t\t/// Item name (<see cref=\"Name\"/>).\n\t\t/// String format: [wildcard expression](xref:wildcard_expression).\n\t\t/// </param>\n\t\t/// <param name=\"how\">\n\t\t/// Try this parameter if the function fails to select the item etc.\n\t\t/// \n\t\t/// <para>\n\t\t/// In the string can be used these characters to specify how to select the item and close the drop-down list:\n\t\t/// <br/>• <c>i</c> - call <see cref=\"Invoke\"/>.\n\t\t/// <br/>• <c>s</c> - call <see cref=\"Select\"/>.\n\t\t/// <br/>• <c>c</c> - close the list with <see cref=\"Expand\"/>.\n\t\t/// <br/>• <c>m</c> - call <see cref=\"MouseClick\"/>; often can't be used because fails to get correct rectangle or to scroll.\n\t\t/// <br/>• <c>k</c> - call <see cref=\"Focus\"/> and <see cref=\"keys.send\"/> (<c>Home</c>, <c>Down</c>, <c>Enter</c>).\n\t\t/// <br/>• space - nothing.\n\t\t/// </para>\n\t\t/// \n\t\t/// If the string is <c>null</c> (default) or <c>\"\"</c> or does not contain these characters, the function tries to detect and use what usually works for this UI element type, but it's impossible to detect always.\n\t\t/// \n\t\t/// Usually need just a single character (string like <c>\"i\"</c> or <c>\"m\"</c>). If there are more characters, the functions are called in the specified order.\n\t\t/// \n\t\t/// The string also can contain sleep times. For example <c>\"300m\"</c> will wait 300 ms and click; the first sleep will be between expanding and finding.\n\t\t/// \n\t\t/// If the string starts with <c>\"~\"</c>, the function does not expand the drop-down list (it should be already expanded).\n\t\t/// \n\t\t/// If the string isn't <c>null</c>/empty but does not contain characters <c>iscmk</c> (for example is <c>\" \"</c> or <c>\"~ \"</c> or <c>\"200 \"</c>), the function does not select/close. For example these codes do the same: <c>e.ComboSelect(\"Red\", \"i\");</c> and <c>e.ComboSelect(\"Red\", \" \").Invoke();</c>.\n\t\t/// </param>\n\t\t/// <param name=\"waitS\">Seconds to wait for expanded state (if not 0) and for the item. Can be negative to avoid timeout exceptions.</param>\n\t\t/// <returns>The item.</returns>\n\t\t/// <exception cref=\"ArgumentException\">Error in <i>item</i> string (wildex options or regex) or <i>how</i>.</exception>\n\t\t/// <exception cref=\"NotFoundException\">Item not found.</exception>\n\t\t/// <exception cref=\"Exception\">Exceptions of used functions.</exception>\n\t\t/// <remarks>\n\t\t/// The function at first calls <see cref=\"Expand(bool, string, double, bool)\"/> to show the drop-down list, unless <i>how</i> starts with <c>\"~\"</c>. Then finds the item by name, selects it and closes the drop-down list.\n\t\t/// </remarks>\n\t\tpublic elm ComboSelect(string item, string how = null, double waitS = 3) {\n\t\t\t//Works with most tested combobox types: native, web, WPF, UWP, ribbon, Office, Qt, Java, JavaFX.\n\t\t\t//\tDoes not work with OpenOffice: can't expand, need Invoke() but it isn't called because there is no DefaultAction. Didn't try to select items. Never mind, anyway no API in dialogs, only in main window.\n\t\t\t//\tNot tested GTK. Don't have apps with working API.\n\t\t\t//Problems in Chrome:\n\t\t\t//\tSelect() does not work. Use Invoke(); it also collapses.\n\t\t\t//Problems in Firefox:\n\t\t\t//\tStandard <select> CB don't change EXPANDED state. Eg in MyBB forum search.\n\t\t\t//\tWith some CB Select() does not work, and Invoke() works. With others vice versa.\n\t\t\t//\tWith some CB Invoke() collapses, with others not.\n\t\t\t//Problems in Chrome and Firefox:\n\t\t\t//\tCB items may be not attached to the CB in the tree. Etc. More info below in _FindWebNotCOMBOBOX.\n\t\t\t//\tWith standard CB MouseClick() does not work, because gives item rect same as CB rect.\n\t\t\t//\tWith some CB selects but it does not work like when clicked. Eg does not refresh the page (eg aruodas).\n\t\t\t//\t\tIn FF can't click because of bad rect, but may work together with Select(). Sometimes Enter works.\n\t\t\t\n\t\t\tThrowIfDisposed_();\n\t\t\t\n\t\t\telm e = null;\n\t\t\tbool auto = how.NE() || how.FindNot(\"-1234567890\") < 0;\n\t\t\tint h = how.NE() ? -1 : 0; //index in how\n\t\t\tbool invoke = false;\n\t\t\t\n\t\t\t#region detect window type\n\t\t\tint iw = 0; //1 Chrome, 2 Firefox, 3 ribbon, 4 Qt5, 10 Java, 11 JavaFX\n\t\t\twnd w = default;\n\t\t\tif (MiscFlags.Has(EMiscFlags.Java)) {\n\t\t\t\tiw = 10;\n\t\t\t} else if (0 == _GetWnd(out w)) {\n\t\t\t\tif (MiscFlags.Has(EMiscFlags.UIA)) {\n\t\t\t\t\tif (w.ClassNameIs(\"GlassWndClass*\")) iw = 11;\n\t\t\t\t} else {\n\t\t\t\t\tiw = w.ClassNameIs(\"Chrome*\", \"Mozilla*\", \"NetUIHWND\"/*ribbon*/, \"Qt5QWindow*\");\n\t\t\t\t\tinvoke = iw is 1 or 3;\n\t\t\t\t}\n\t\t\t}\n\t\t\t#endregion\n\t\t\t\n\t\t\t#region activate/focus if need\n\t\t\tif (!auto) {\n\t\t\t\tif (how.Contains('k')) {\n\t\t\t\t\tFocus();\n\t\t\t\t} else if (how.Contains('m')) {\n\t\t\t\t\tif (iw == 10) _GetWnd(out w);\n\t\t\t\t\tw.Window.Activate();\n\t\t\t\t}\n\t\t\t} else if (iw is 4 or 10) { //Qt, Java\n\t\t\t\tFocus();\n\t\t\t}\n\t\t\t#endregion\n\t\t\t\n\t\t\t#region expand\n\t\t\tbool noExpand = false, noState = false;\n\t\t\tif (RoleInt == ERole.COMBOBOX) {\n\t\t\t\tvar state = State;\n\t\t\t\tnoExpand = state.Has(EState.EXPANDED);\n\t\t\t\tnoState = !state.HasAny(EState.COLLAPSED | EState.EXPANDED); //eg ribbon\n\t\t\t}\n\t\t\tif (h == 0 && how[h] == '~') {\n\t\t\t\th++;\n\t\t\t} else if (!noExpand) {\n\t\t\t\tExpand(true, waitS: iw == 2 || noState ? -.1 : waitS);\n\t\t\t\tif (h == 0) _Sleep(); else 10.ms();\n\t\t\t}\n\t\t\t#endregion\n\t\t\t\n\t\t\t#region find item\n\t\t\twaitS = -Math.Abs(waitS);\n\t\t\tvar f = Elm[null, item, flags: EFFlags.HiddenToo]; //usually CB item role is LISTITEM, sometimes MENUITEM, Java \"label\"; let's support any. May need HiddenToo for offscreen items, eg Java.\n\t\t\tif (iw is 1 or 2 && RoleInt is not ERole.COMBOBOX) {\n\t\t\t\t_FindWebNotCOMBOBOX();\n\t\t\t} else {\n\t\t\t\tvar loop = new WaitLoop(waitS);\n\t\t\t\tfor (; ; ) {\n\t\t\t\t\te = f.Find();\n\t\t\t\t\tif (e == null) {\n\t\t\t\t\t\tif (iw == 11) { //JavaFX drop-down list isn't connected to the CB\n\t\t\t\t\t\t\tvar wp = w.Get.EnabledOwned();\n\t\t\t\t\t\t\t//TODO3: now unreliable. Can detect a wrong window, eg a random tooltip.\n\t\t\t\t\t\t\t//\tTry to detect more reliably. Eg must have certain styles and rect, contain LIST or MENUPOPUP, etc.\n\t\t\t\t\t\t\t//\tThen could use with any window, not only JavaFX.\n\t\t\t\t\t\t\t//\tAlso then could detect expanded state even when there is no EXPANDED state.\n\t\t\t\t\t\t\t//\tEnabledOwned isn't reliable in other places too.\n\t\t\t\t\t\t\tif (!wp.Is0) e = wp.Elm[\"LISTITEM\", item, flags: EFFlags.UIA].Find();\n\t\t\t\t\t\t} else if (RoleInt != ERole.COMBOBOX && ChildCount == 0) { //eg ribbon dropdown button\n\t\t\t\t\t\t\te = f.In(Parent).Find();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (e != null) break;\n\t\t\t\t\tif (!loop.Sleep()) break;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (e == null) throw new NotFoundException($\"Can't find combo box item \\\"{item}\\\".\");\n\t\t\t#endregion\n\t\t\t\n\t\t\t#region select and collapse\n\t\t\tif (!auto) {\n\t\t\t\tfor (; h < how.Length; h++) {\n\t\t\t\t\tbool hasSelect = false;\n\t\t\t\t\tswitch (how[h]) {\n\t\t\t\t\tcase 's':\n\t\t\t\t\t\te.Select();\n\t\t\t\t\t\thasSelect = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 'i':\n\t\t\t\t\t\te.Invoke();\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 'm':\n\t\t\t\t\t\tif (iw == 2 && !hasSelect) e.Select(); //if bad rect, with this usually works anyway in FF\n\t\t\t\t\t\te.MouseClick(scroll: iw == 2 ? 0 : 10);\n\t\t\t\t\t\t//FF bug: if scroll!=0, scrolls the combo (entire page), not the dropdown list item. Good: Select() scrolls.\n\t\t\t\t\t\t//note: WPF CB item rect changes because of dropdown animation\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 'k':\n\t\t\t\t\t\t_Keys();\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 'c':\n\t\t\t\t\t\tExpand(!true, waitS: -0.01, ignoreState: true);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ' ':\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\t_Sleep();\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (invoke) {\n\t\t\t\te.Invoke();\n\t\t\t} else if (iw is 4 or 10) { //Qt, Java\n\t\t\t\t_Keys(); //nothing else works, only mouse if don't need to scroll\n\t\t\t} else {\n\t\t\t\tbool ignoreState = noState || (iw == 2 && !State.Has(EState.EXPANDED)); //standard <select> CB in FF does not change state\n\t\t\t\te.Select();\n\t\t\t\tExpand(!true, waitS: -0.01, ignoreState: ignoreState);\n\t\t\t}\n\t\t\t#endregion\n\t\t\treturn e;\n\t\t\t\n\t\t\tvoid _Keys() {\n\t\t\t\tvar a = e.Parent.Elm[e.Role, prop: \"level=0\", flags: EFFlags.HiddenToo].FindAll(); //these are slow, but much faster than keys.send\n\t\t\t\twildex wild = item;\n\t\t\t\tint i = Array.FindIndex(a, o => wild.Match(o.Name));\n\t\t\t\tif (i < 0) throw new AuException();\n\t\t\t\tAu.keys.send($\"Home PgUp*{a.Length / 4} Down*{i} Enter\"); //Home doesn't work with editable CB; PgUp may not work with some CB\n\t\t\t}\n\t\t\t\n\t\t\tvoid _Sleep() {\n\t\t\t\tif (!how[h].IsAsciiDigit()) { if (e == null) return; throw new ArgumentException(\"Invalid character \" + how[h], \"how\"); }\n\t\t\t\tif (!how.ToInt(out int ms, h, out h)) throw new ArgumentException(\"Invalid number\", \"how\");\n\t\t\t\tms.ms();\n\t\t\t}\n\t\t\t\n\t\t\tvoid _FindWebNotCOMBOBOX() {\n\t\t\t\t//Web pages have multiple CB types. Sometimes the dropdown list isn't a descendant of the CB.\n\t\t\t\t//\tIn Google Advanced Search it is LIST with 2 children: MENUITEM and LISTITEM with same name (the selected).\n\t\t\t\t//\t\tThe list is a MENUPOPUP/MENUITEM near the bottom of the document. It may not contain the selected item.\n\t\t\t\t//\tIn Github profile Stars it is BUTTON with 0 children.\n\t\t\t\t//\t\tThe list is a MENUPOPUP/MENUITEM in its parent.\n\t\t\t\t\n\t\t\t\telm pa = null, doc = null;\n\t\t\t\tvar loop = new WaitLoop(waitS);\n\t\t\t\tfor (; ; ) {\n\t\t\t\t\te = f.Find(); //if the item is already selected, the dropdown list may not contain it, and this finds it\n\t\t\t\t\tif (e != null) break;\n\t\t\t\t\tpa ??= Parent; if (pa == null) break;\n\t\t\t\t\tvar mp = pa.Elm[\"MENUPOPUP\"].Find();\n\t\t\t\t\tif (mp != null) {\n\t\t\t\t\t\te = mp.Elm[\"MENUITEM\", item].Find();\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (doc == null) {\n\t\t\t\t\t\t\tfor (var p = pa; p != null; p = p.Parent) if (p.RoleInt == ERole.DOCUMENT) { doc = p; break; }\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (doc != null) {\n\t\t\t\t\t\t\te = doc.Elm[\"MENUPOPUP\", flags: EFFlags.Reverse].Find()?.Elm[\"MENUITEM\", item].Find();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (e != null) {\n\t\t\t\t\t\tinvoke = true; //selects and closes in FF too; else difficult/unreliable.\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tif (!loop.Sleep()) break;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\t//wnd _GetOwnedPopupWindow(wnd w, RECT? r = null) {\n\t\t//\tvar p = w.Window.Get.EnabledOwned();\n\t\t//\tif (!p.Is0) {\n\t\t//\t\tvar k = r ?? Rect;\n\t\t//\t\tint i = k.Height / 2;\n\t\t//\t\tk.Inflate(i, i);\n\t\t//\t\tvar pr = p.Rect;\n\t\t//\t\tif (!pr.IntersectsWith(k) || pr.Height < k.Height * 2) p = default;\n\t\t//\t}\n\t\t//\treturn p;\n\t\t//}\n\t\t\n\t\tconst int TVM_MAPACCIDTOHTREEITEM = 0x112A;\n\t\tconst int TVM_GETNEXTITEM = 0x110A;\n\t\tconst int TVM_MAPHTREEITEMTOACCID = 0x112B;\n\t\tconst int TVM_GETITEMSTATE = 0x1127;\n\t\tconst int TVM_ENSUREVISIBLE = 0x1114;\n\t\tconst int TVM_SELECTITEM = 0x110B;\n\t\t\n\t\tconst int TVGN_ROOT = 0x0;\n\t\tconst int TVGN_CHILD = 0x4;\n\t\tconst int TVGN_NEXT = 0x1;\n\t\tconst int TVGN_CARET = 0x9;\n\t\tconst int TVSI_NOSINGLEEXPAND = 0x8000;\n\t\t\n\t\tconst int TVIS_EXPANDED = 0x20;\n\t\t\n\t\tconst int LVM_ENSUREVISIBLE = 0x1013;\n\t\t\n\t\tconst int LB_SETTOPINDEX = 0x197;\n\t\t\n\t}\n}\n\n//rejected:\n//\tMenuSelect(path) - click, wait for popup menu, find/click, wait for another popup, .... Can check/uncheck.\n//\t\tMuch easier and better to use keys.\n//\tContextMenuSelect(path).\n//\t\tLet use MouseClickR and keys.\n\n//TEST:\n//\tIUIAutomationScrollPattern: Scroll, SetScrollPercent.\n//\tIUIAutomationWindowPattern: Close, WaitForInputIdle, CurrentIsModal, CurrentWindowInteractionState.\n"
  },
  {
    "path": "Au/UI objects/elm_types.cs",
    "content": "namespace Au.Types {\n\t/// <summary>\n\t/// Flags for \"find UI element\" functions (<see cref=\"elmFinder\"/>).\n\t/// </summary>\n\t[Flags]\n\tpublic enum EFFlags {\n\t\t/// <summary>\n\t\t/// Search in reverse order. It can make faster.\n\t\t/// When control class or id is specified in the <i>prop</i> argument, controls are searched not in reverse order. Only UI elements in them are searched in reverse order.\n\t\t/// </summary>\n\t\tReverse = 1,\n\n\t\t/// <summary>\n\t\t/// The UI element can be invisible.\n\t\t/// Without this flag skips UI elements that are invisible (have state <c>INVISIBLE</c>) or are descendants of invisible <c>WINDOW</c>, <c>DOCUMENT</c>, <c>PROPERTYPAGE</c>, <c>GROUPING</c>, <c>ALERT</c>, <c>MENUPOPUP</c>.\n\t\t/// Regardless of this flag, always skips invisible standard UI elements of nonclient area: <c>TITLEBAR</c>, <c>MENUBAR</c>, <c>SCROLLBAR</c>, <c>GRIP</c>.\n\t\t/// </summary>\n\t\tHiddenToo = 2,\n\n\t\t/// <summary>\n\t\t/// Always search in <c>MENUITEM</c>.\n\t\t/// Without this flag skips <c>MENUITEM</c> descendant elements (for speed), unless <i>role</i> argument is <c>MENUITEM</c> or <c>MENUPOPUP</c> or searching in web page.\n\t\t/// </summary>\n\t\tMenuToo = 4,\n\n\t\t/// <summary>\n\t\t/// Search only in the client area of the window or control.\n\t\t/// Skips the title bar, standard menubars and scrollbars. Searches only in the client area root UI element (but will not find the UI element itself).\n\t\t/// When control class or id is specified in the <i>prop</i> argument, this flag is applied to these controls. Not applied to other controls.\n\t\t/// Don't use this flag when searching in elm or web page (role prefix <c>\"web:\"</c> etc) or with flag <c>UIA</c>.\n\t\t/// </summary>\n\t\tClientArea = 8,\n\n\t\t/// <summary>\n\t\t/// Search without loading dll into the target process.\n\t\t/// Disadvantages: 1. Much slower. 2. Some properties are not supported, for example HTML attributes (while searching and later). 3. And more.\n\t\t/// Even without this flag, the default search method is not used with Windows Store app windows, console windows, most Java windows, windows of protected processes and processes of higher [](xref:uac) integrity level.\n\t\t/// Some windows have child controls that belong to a different process or thread than the window. For example the Windows Task Scheduler window. When searching in such windows, the default search method is not used when searching in these controls. Workaround - find the control (<see cref=\"wnd.Child\"/> etc) and search in it.\n\t\t/// Don't need this flag when searching in elm (then it is inherited from the elm variable).\n\t\t/// See also: <see cref=\"elm.MiscFlags\"/>.\n\t\t/// </summary>\n\t\tNotInProc = 0x100,\n\n\t\t/// <summary>\n\t\t/// Use UI Automation API.\n\t\t/// Need this flag to find UI elements in windows that don't support accessible objects but support UI Automation elements.\n\t\t/// UI elements found with this flag never have <c>HtmlX</c> properties, but can have <c>UiaX</c> properties.\n\t\t/// This flag can be used with most other windows too.\n\t\t/// Don't use this flag when searching in elm (then it is inherited from the elm variable) or web page (role prefix <c>\"web:\"</c> etc).\n\t\t/// See also: <see cref=\"elm.MiscFlags\"/>.\n\t\t/// </summary>\n\t\tUIA = 0x200,\n\n\t\t//Internal. See Enum_.AFFlags_Mark.\n\t\t//Mark = 0x10000,\n\t}\n\n\t/// <summary>\n\t/// Adds internal members to public enums.\n\t/// </summary>\n\tinternal static partial class Enum_ {\n\t\t/// <summary>\n\t\t/// Used by <c>Delm</c>, together with <c>ElmMiscFlags_Marked</c>.\n\t\t/// </summary>\n\t\tinternal static EFFlags EFFlags_Mark = (EFFlags)0x10000;\n\n\t\t/// <summary>\n\t\t/// Used by <c>Delm</c>, together with <c>AFFlags_Mark</c>.\n\t\t/// </summary>\n\t\tinternal static EMiscFlags EMiscFlags_Marked = (EMiscFlags)128;\n\n\t\tinternal static EXYFlags EXYFlags_DpiScaled = (EXYFlags)0x10000;\n\t\tinternal static EXYFlags EXYFlags_Fail = (EXYFlags)0x20000; //currently not used\n\t}\n\n\t/// <summary>\n\t/// Flags for <see cref=\"elm.fromWindow\"/>.\n\t/// </summary>\n\t[Flags]\n\tpublic enum EWFlags {\n\t\t/// <summary>Don't throw exception when fails. Then returns <c>null</c>.</summary>\n\t\tNoThrow = 1,\n\n\t\t/// <summary>\n\t\t/// Don't load dll into the target process.\n\t\t/// More info: <see cref=\"EFFlags.NotInProc\"/>.\n\t\t/// </summary>\n\t\tNotInProc = 2,\n\t}\n\n\t/// <summary>\n\t/// Flags for <see cref=\"elm.fromXY\"/>.\n\t/// </summary>\n\t[Flags]\n\tpublic enum EXYFlags {\n\t\t/// <summary>\n\t\t/// Don't load dll into the target process.\n\t\t/// More info: <see cref=\"EFFlags.NotInProc\"/>.\n\t\t/// </summary>\n\t\tNotInProc = 1,\n\n\t\t/// <summary>\n\t\t/// Use UI Automation API.\n\t\t/// More info: <see cref=\"EFFlags.UIA\"/>.\n\t\t/// </summary>\n\t\tUIA = 2,\n\n\t\t/// <summary>\n\t\t/// Get the direct parent UI element if probably it would be much more useful, for example if its role is <c>LINK</c> or <c>BUTTON</c>.\n\t\t/// Usually links have one or more children of type <c>TEXT</c>, <c>STATICTEXT</c>, <c>IMAGE</c> or other.\n\t\t/// </summary>\n\t\tPreferLink = 4,\n\n\t\t[Obsolete]\n#pragma warning disable CS1591 //Missing XML comment for publicly visible type or member\n\t\tTrySmaller = 8,\n#pragma warning restore CS1591 //Missing XML comment for publicly visible type or member\n\n\t\t/// <summary>\n\t\t/// Use UI Automation API if the default API fails and in some other cases.\n\t\t/// The <b>Find UI element</b> tool uses this flag when <c>UIA</c> is in indeterminate state.\n\t\t/// </summary>\n\t\tOrUIA = 16,\n\n\t\t//note: don't change values. They are passed to the cpp function.\n\t}\n\n\t/// <summary>\n\t/// Flags for <see cref=\"elm.focused\"/>.\n\t/// </summary>\n\t[Flags]\n\tpublic enum EFocusedFlags {\n\t\t/// <summary>\n\t\t/// Don't load dll into the target process.\n\t\t/// More info: <see cref=\"EFFlags.NotInProc\"/>.\n\t\t/// </summary>\n\t\tNotInProc = 1,\n\n\t\t/// <summary>\n\t\t/// Use UI Automation API.\n\t\t/// Need this flag with some windows that don't support accessible objects but support UI Automation elements. Can be used with most other windows too.\n\t\t/// More info: <see cref=\"EFFlags.UIA\"/>.\n\t\t/// </summary>\n\t\tUIA = 2,\n\n\t\t//note: don't change values. They are passed to the cpp function.\n\t}\n\n\t/// <summary>\n\t/// Flags returned by <see cref=\"elm.MiscFlags\"/>.\n\t/// </summary>\n\t[Flags]\n\tpublic enum EMiscFlags : byte {\n\t\t/// <summary>\n\t\t/// This UI element was retrieved by the dll loaded into its process.\n\t\t/// More info: <see cref=\"EFFlags.NotInProc\"/>.\n\t\t/// </summary>\n\t\tInProc = 1,\n\n\t\t/// <summary>\n\t\t/// This UI element was retrieved using UI Automation API.\n\t\t/// More info: <see cref=\"EFFlags.UIA\"/>.\n\t\t/// </summary>\n\t\tUIA = 2,\n\n\t\t/// <summary>\n\t\t/// This UI element was retrieved using Java Access Bridge API.\n\t\t/// More info: <see cref=\"elm\"/>.\n\t\t/// </summary>\n\t\tJava = 4,\n\n\t\t//Internal. See Enum_.ElmMiscFlags_Marked.\n\t\t//Marked = 128,\n\t}\n\n#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member\n\n\t/// <summary>\n\t/// Object ids of window parts and some special UI elements.\n\t/// Used with <see cref=\"elm.fromWindow\"/>\n\t/// </summary>\n\t/// <remarks>\n\t/// Most names are as in API <ms>AccessibleObjectFromWindow</ms> documentation but without prefix <c>OBJID_</c>.\n\t/// </remarks>\n\tpublic enum EObjid {\n\t\tWINDOW = 0,\n\t\tSYSMENU = -1,\n\t\tTITLEBAR = -2,\n\t\tMENU = -3,\n\t\tCLIENT = -4,\n\t\tVSCROLL = -5,\n\t\tHSCROLL = -6,\n\t\tSIZEGRIP = -7,\n\t\tCARET = -8,\n\t\tCURSOR = -9,\n\t\tALERT = -10,\n\t\tSOUND = -11,\n\t\t/// <summary>Not used with <see cref=\"elm.fromWindow\"/>.</summary>\n\t\tQUERYCLASSNAMEIDX = -12,\n\t\t/// <summary>Not used with <see cref=\"elm.fromWindow\"/>.</summary>\n\t\tNATIVEOM = -16,\n\t\t/// <summary>Not used with <see cref=\"elm.fromWindow\"/>.</summary>\n\t\tUiaRootObjectId = -25,\n\n\t\t//ours\n\n\t\t/// <summary>\n\t\t/// The root Java UI element. Can be used when the window's class name starts with <c>\"SunAwt\"</c>.\n\t\t/// </summary>\n\t\tJava = -100,\n\n\t\t/// <summary>\n\t\t/// Use UI Automation API.\n\t\t/// More info: <see cref=\"EFFlags.UIA\"/>.\n\t\t/// </summary>\n\t\tUIA = -101,\n\t}\n\n\t/// <summary>\n\t/// Standard roles of UI elements.\n\t/// Used with <see cref=\"elm.RoleInt\"/>\n\t/// </summary>\n\t/// <remarks>\n\t/// Most names are as in API <ms>IAccessible.get_accRole Object Roles</ms> documentation but without prefix <c>ROLE_SYSTEM_</c>. These are renamed: <c>PUSHBUTTON</c> to <c>BUTTON</c>, <c>CHECKBUTTON</c> to <c>CHECKBOX</c>, <c>GRAPHIC</c> to <c>IMAGE</c>, <c>OUTLINE</c> to <c>TREE</c>, <c>OUTLINEITEM</c> to <c>TREEITEM</c>, <c>OUTLINEBUTTON</c> to <c>TREEBUTTON</c>.\n\t/// </remarks>\n\tpublic enum ERole {\n\t\tTITLEBAR = 0x1,\n\t\tMENUBAR = 0x2,\n\t\tSCROLLBAR = 0x3,\n\t\tGRIP = 0x4,\n\t\tSOUND = 0x5,\n\t\tCURSOR = 0x6,\n\t\tCARET = 0x7,\n\t\tALERT = 0x8,\n\t\tWINDOW = 0x9,\n\t\tCLIENT = 0xA,\n\t\tMENUPOPUP = 0xB,\n\t\tMENUITEM = 0xC,\n\t\tTOOLTIP = 0xD,\n\t\tAPPLICATION = 0xE,\n\t\tDOCUMENT = 0xF,\n\t\tPANE = 0x10,\n\t\tCHART = 0x11,\n\t\tDIALOG = 0x12,\n\t\tBORDER = 0x13,\n\t\tGROUPING = 0x14,\n\t\tSEPARATOR = 0x15,\n\t\tTOOLBAR = 0x16,\n\t\tSTATUSBAR = 0x17,\n\t\tTABLE = 0x18,\n\t\tCOLUMNHEADER = 0x19,\n\t\tROWHEADER = 0x1A,\n\t\tCOLUMN = 0x1B,\n\t\tROW = 0x1C,\n\t\tCELL = 0x1D,\n\t\tLINK = 0x1E,\n\t\tHELPBALLOON = 0x1F,\n\t\tCHARACTER = 0x20,\n\t\tLIST = 0x21,\n\t\tLISTITEM = 0x22,\n\t\tTREE = 0x23, //OUTLINE\n\t\tTREEITEM = 0x24, //OUTLINEITEM\n\t\tPAGETAB = 0x25,\n\t\tPROPERTYPAGE = 0x26,\n\t\tINDICATOR = 0x27,\n\t\tIMAGE = 0x28, //GRAPHIC\n\t\tSTATICTEXT = 0x29,\n\t\tTEXT = 0x2A,\n\t\tBUTTON = 0x2B, //PUSHBUTTON\n\t\tCHECKBOX = 0x2C, //CHECKBUTTON\n\t\tRADIOBUTTON = 0x2D,\n\t\tCOMBOBOX = 0x2E,\n\t\tDROPLIST = 0x2F,\n\t\tPROGRESSBAR = 0x30,\n\t\tDIAL = 0x31,\n\t\tHOTKEYFIELD = 0x32,\n\t\tSLIDER = 0x33,\n\t\tSPINBUTTON = 0x34,\n\t\tDIAGRAM = 0x35,\n\t\tANIMATION = 0x36,\n\t\tEQUATION = 0x37,\n\t\tBUTTONDROPDOWN = 0x38,\n\t\tBUTTONMENU = 0x39,\n\t\tBUTTONDROPDOWNGRID = 0x3A,\n\t\tWHITESPACE = 0x3B,\n\t\tPAGETABLIST = 0x3C,\n\t\tCLOCK = 0x3D,\n\t\tSPLITBUTTON = 0x3E,\n\t\tIPADDRESS = 0x3F,\n\t\tTREEBUTTON = 0x40, //OUTLINEBUTTON\n\n\t\t/// <summary>Failed to get role.</summary>\n\t\tNone = 0,\n\n\t\t/// <summary>Not one of predefined roles. Usually string.</summary>\n\t\tCustom = 0xFF,\n\t}\n\n\t/// <summary>\n\t/// UI element state flags.\n\t/// Used by <see cref=\"elm.State\"/>.\n\t/// </summary>\n\t/// <remarks>\n\t/// Most names are as in API <ms>IAccessible.get_accState Object State Constants</ms> documentation but without prefix <c>STATE_SYSTEM_</c>.\n\t/// </remarks>\n\t[Flags]\n\tpublic enum EState {\n\t\t//NORMAL = 0x0,\n\t\tDISABLED = 0x1, //original name UNAVAILABLE\n\t\tSELECTED = 0x2,\n\t\tFOCUSED = 0x4,\n\t\tPRESSED = 0x8,\n\t\tCHECKED = 0x10,\n\t\tMIXED = 0x20,\n\t\tREADONLY = 0x40,\n\t\tHOTTRACKED = 0x80,\n\t\tDEFAULT = 0x100,\n\t\tEXPANDED = 0x200,\n\t\tCOLLAPSED = 0x400,\n\t\tBUSY = 0x800,\n\t\tFLOATING = 0x1000,\n\t\tMARQUEED = 0x2000,\n\t\tANIMATED = 0x4000,\n\t\tINVISIBLE = 0x8000,\n\t\tOFFSCREEN = 0x10000,\n\t\tSIZEABLE = 0x20000,\n\t\tMOVEABLE = 0x40000,\n\t\tSELFVOICING = 0x80000,\n\t\tFOCUSABLE = 0x100000,\n\t\tSELECTABLE = 0x200000,\n\t\tLINKED = 0x400000,\n\t\tTRAVERSED = 0x800000,\n\t\tMULTISELECTABLE = 0x1000000,\n\t\tEXTSELECTABLE = 0x2000000,\n\t\tALERT_LOW = 0x4000000,\n\t\tALERT_MEDIUM = 0x8000000,\n\t\tALERT_HIGH = 0x10000000,\n\t\tPROTECTED = 0x20000000,\n\t\tHASPOPUP = 0x40000000,\n\t}\n\n\t/// <summary>\n\t/// UI element selection flags.\n\t/// Used by <see cref=\"elm.Select\"/>.\n\t/// </summary>\n\t/// <remarks>\n\t/// The names are as in API <ms>IAccessible.accSelect</ms> documentation but without prefix <c>SELFLAG_</c>.\n\t/// </remarks>\n\t[Flags]\n\tpublic enum ESelect {\n\t\tTAKEFOCUS = 0x1,\n\t\tTAKESELECTION = 0x2,\n\t\tEXTENDSELECTION = 0x4,\n\t\tADDSELECTION = 0x8,\n\t\tREMOVESELECTION = 0x10,\n\t}\n\n\t/// <summary>\n\t/// Event constants for <see cref=\"More.WinEventHook\"/>.\n\t/// </summary>\n\t/// <remarks>\n\t/// The names are as in API <ms>SetWinEventHook Event Constants</ms> documentation but without prefix <c>EVENT_</c>.\n\t/// </remarks>\n\tpublic enum EEvent {\n\t\tMIN = 0x1,\n\t\tMAX = 0x7FFFFFFF,\n\n\t\tSYSTEM_SOUND = 0x1,\n\t\tSYSTEM_ALERT = 0x2,\n\t\tSYSTEM_FOREGROUND = 0x3,\n\t\tSYSTEM_MENUSTART = 0x4,\n\t\tSYSTEM_MENUEND = 0x5,\n\t\tSYSTEM_MENUPOPUPSTART = 0x6,\n\t\tSYSTEM_MENUPOPUPEND = 0x7,\n\t\tSYSTEM_CAPTURESTART = 0x8,\n\t\tSYSTEM_CAPTUREEND = 0x9,\n\t\tSYSTEM_MOVESIZESTART = 0xA,\n\t\tSYSTEM_MOVESIZEEND = 0xB,\n\t\tSYSTEM_CONTEXTHELPSTART = 0xC,\n\t\tSYSTEM_CONTEXTHELPEND = 0xD,\n\t\tSYSTEM_DRAGDROPSTART = 0xE,\n\t\tSYSTEM_DRAGDROPEND = 0xF,\n\t\tSYSTEM_DIALOGSTART = 0x10,\n\t\tSYSTEM_DIALOGEND = 0x11,\n\t\tSYSTEM_SCROLLINGSTART = 0x12,\n\t\tSYSTEM_SCROLLINGEND = 0x13,\n\t\tSYSTEM_SWITCHSTART = 0x14,\n\t\tSYSTEM_SWITCHEND = 0x15,\n\t\tSYSTEM_MINIMIZESTART = 0x16,\n\t\tSYSTEM_MINIMIZEEND = 0x17,\n\t\tSYSTEM_DESKTOPSWITCH = 0x20,\n\t\tSYSTEM_SWITCHER_APPGRABBED = 0x24,\n\t\tSYSTEM_SWITCHER_APPOVERTARGET = 0x25,\n\t\tSYSTEM_SWITCHER_APPDROPPED = 0x26,\n\t\tSYSTEM_SWITCHER_CANCELLED = 0x27,\n\t\tSYSTEM_IME_KEY_NOTIFICATION = 0x29,\n\t\tSYSTEM_END = 0xFF,\n\n\t\tOEM_DEFINED_START = 0x101,\n\t\tOEM_DEFINED_END = 0x1FF,\n\n\t\tUIA_EVENTID_START = 0x4E00,\n\t\tUIA_EVENTID_END = 0x4EFF,\n\t\tUIA_PROPID_START = 0x7500,\n\t\tUIA_PROPID_END = 0x75FF,\n\n\t\tCONSOLE_CARET = 0x4001,\n\t\tCONSOLE_UPDATE_REGION = 0x4002,\n\t\tCONSOLE_UPDATE_SIMPLE = 0x4003,\n\t\tCONSOLE_UPDATE_SCROLL = 0x4004,\n\t\tCONSOLE_LAYOUT = 0x4005,\n\t\tCONSOLE_START_APPLICATION = 0x4006,\n\t\tCONSOLE_END_APPLICATION = 0x4007,\n\t\tCONSOLE_END = 0x40FF,\n\n\t\tOBJECT_CREATE = 0x8000,\n\t\tOBJECT_DESTROY = 0x8001,\n\t\tOBJECT_SHOW = 0x8002,\n\t\tOBJECT_HIDE = 0x8003,\n\t\tOBJECT_REORDER = 0x8004,\n\t\tOBJECT_FOCUS = 0x8005,\n\t\tOBJECT_SELECTION = 0x8006,\n\t\tOBJECT_SELECTIONADD = 0x8007,\n\t\tOBJECT_SELECTIONREMOVE = 0x8008,\n\t\tOBJECT_SELECTIONWITHIN = 0x8009,\n\t\tOBJECT_STATECHANGE = 0x800A,\n\t\tOBJECT_LOCATIONCHANGE = 0x800B,\n\t\tOBJECT_NAMECHANGE = 0x800C,\n\t\tOBJECT_DESCRIPTIONCHANGE = 0x800D,\n\t\tOBJECT_VALUECHANGE = 0x800E,\n\t\tOBJECT_PARENTCHANGE = 0x800F,\n\t\tOBJECT_HELPCHANGE = 0x8010,\n\t\tOBJECT_DEFACTIONCHANGE = 0x8011,\n\t\tOBJECT_ACCELERATORCHANGE = 0x8012,\n\t\tOBJECT_INVOKED = 0x8013,\n\t\tOBJECT_TEXTSELECTIONCHANGED = 0x8014,\n\t\tOBJECT_CONTENTSCROLLED = 0x8015,\n\t\tSYSTEM_ARRANGMENTPREVIEW = 0x8016,\n\t\tOBJECT_CLOAKED = 0x8017,\n\t\tOBJECT_UNCLOAKED = 0x8018,\n\t\tOBJECT_LIVEREGIONCHANGED = 0x8019,\n\t\tOBJECT_HOSTEDOBJECTSINVALIDATED = 0x8020,\n\t\tOBJECT_DRAGSTART = 0x8021,\n\t\tOBJECT_DRAGCANCEL = 0x8022,\n\t\tOBJECT_DRAGCOMPLETE = 0x8023,\n\t\tOBJECT_DRAGENTER = 0x8024,\n\t\tOBJECT_DRAGLEAVE = 0x8025,\n\t\tOBJECT_DRAGDROPPED = 0x8026,\n\t\tOBJECT_IME_SHOW = 0x8027,\n\t\tOBJECT_IME_HIDE = 0x8028,\n\t\tOBJECT_IME_CHANGE = 0x8029,\n\t\tOBJECT_TEXTEDIT_CONVERSIONTARGETCHANGED = 0x8030,\n\t\tOBJECT_END = 0x80FF,\n\n\t\tAIA_START = 0xA000,\n\t\tAIA_END = 0xAFFF,\n\n\t\tIA2_ACTION_CHANGED = 0x101,\n\t\tIA2_ACTIVE_DESCENDANT_CHANGED = 0x102,\n\t\tIA2_DOCUMENT_ATTRIBUTE_CHANGED = 0x103,\n\t\tIA2_DOCUMENT_CONTENT_CHANGED = 0x104,\n\t\tIA2_DOCUMENT_LOAD_COMPLETE = 0x105,\n\t\tIA2_DOCUMENT_LOAD_STOPPED = 0x106,\n\t\tIA2_DOCUMENT_RELOAD = 0x107,\n\t\tIA2_HYPERLINK_END_INDEX_CHANGED = 0x108,\n\t\tIA2_HYPERLINK_NUMBER_OF_ANCHORS_CHANGED = 0x109,\n\t\tIA2_HYPERLINK_SELECTED_LINK_CHANGED = 0x10a,\n\t\tIA2_HYPERTEXT_LINK_ACTIVATED = 0x10b,\n\t\tIA2_HYPERTEXT_LINK_SELECTED = 0x10c,\n\t\tIA2_HYPERLINK_START_INDEX_CHANGED = 0x10d,\n\t\tIA2_HYPERTEXT_CHANGED = 0x10e,\n\t\tIA2_HYPERTEXT_NLINKS_CHANGED = 0x10f,\n\t\tIA2_OBJECT_ATTRIBUTE_CHANGED = 0x110,\n\t\tIA2_PAGE_CHANGED = 0x111,\n\t\tIA2_SECTION_CHANGED = 0x112,\n\t\tIA2_TABLE_CAPTION_CHANGED = 0x113,\n\t\tIA2_TABLE_COLUMN_DESCRIPTION_CHANGED = 0x114,\n\t\tIA2_TABLE_COLUMN_HEADER_CHANGED = 0x115,\n\t\tIA2_TABLE_MODEL_CHANGED = 0x116,\n\t\tIA2_TABLE_ROW_DESCRIPTION_CHANGED = 0x117,\n\t\tIA2_TABLE_ROW_HEADER_CHANGED = 0x118,\n\t\tIA2_TABLE_SUMMARY_CHANGED = 0x119,\n\t\tIA2_TEXT_ATTRIBUTE_CHANGED = 0x11a,\n\t\tIA2_TEXT_CARET_MOVED = 0x11b,\n\t\tIA2_TEXT_CHANGED = 0x11c,\n\t\tIA2_TEXT_COLUMN_CHANGED = 0x11d,\n\t\tIA2_TEXT_INSERTED = 0x11e,\n\t\tIA2_TEXT_REMOVED = 0x11f,\n\t\tIA2_TEXT_UPDATED = 0x120,\n\t\tIA2_TEXT_SELECTION_CHANGED = 0x121,\n\t\tIA2_VISIBLE_DATA_CHANGED = 0x122,\n\t}\n\n\t/// <summary>\n\t/// Flags for <see cref=\"More.WinEventHook\"/>.\n\t/// </summary>\n\t/// <remarks>\n\t/// The names are as in API <ms>SetWinEventHook</ms> documentation but without prefix <c>WINEVENT_</c>.\n\t/// There are no flags for <c>OUTOFCONTEXT</c> and <c>INCONTEXT</c>. <c>OUTOFCONTEXT</c> is default (0). <c>INCONTEXT</c> cannot be used in managed code.\n\t/// </remarks>\n\t[Flags]\n\tpublic enum EHookFlags {\n\t\tNone,\n\t\t/// <summary>Don't receive events generated by this thread.</summary>\n\t\tSKIPOWNTHREAD = 0x1,\n\n\t\t/// <summary>Don't receive events generated by threads of this process.</summary>\n\t\tSKIPOWNPROCESS = 0x2,\n\n\t\t//OUTOFCONTEXT = 0x0,\n\t\t//INCONTEXT = 0x4,\n\t}\n\n\t/// <summary>\n\t/// Used with <see cref=\"elm.GetProperties\"/>.\n\t/// </summary>\n\tpublic class EProperties {\n\t\tpublic string Role, Name, Value, Description, Help, DefaultAction, KeyboardShortcut, UiaId, UiaCN, OuterHtml, InnerHtml;\n\t\tpublic EState State;\n\t\tpublic RECT Rect;\n\t\tpublic wnd WndContainer;\n\t\tpublic Dictionary<string, string> HtmlAttributes;\n\t}\n\n#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member\n}\n"
  },
  {
    "path": "Au/UI objects/ocr.cs",
    "content": "using System.Drawing;\n\nnamespace Au;\n\n/// <summary>\n/// OCR functions. Recognizes text on screen or in image, gets word positions, finds text, clicks found words.\n/// </summary>\npublic class ocr {\n\treadonly IFArea _area;\n\n\tstatic regexp s_rx1 = new(@\"\\s+\");\n\n\tinternal ocr(OcrWord[] words, IFArea area) {\n\t\t_area = area;\n\t\tWords = words;\n\t\tTextForFind = _BuildText(true);\n\t}\n\n\t/// <summary>\n\t/// Recognized words (text, rectangle, etc).\n\t/// </summary>\n\tpublic OcrWord[] Words { get; private set; }\n\n\t/// <summary>\n\t/// Recognized text as single line.\n\t/// Any whitespace between words is replaced with single space.\n\t/// <see cref=\"find\"/> and similar functions search in this text.\n\t/// </summary>\n\tpublic string TextForFind { get; private set; }\n\n\t/// <summary>\n\t/// Recognized text.\n\t/// </summary>\n\tpublic string Text => _textML ??= _BuildText(false);\n\tstring _textML;\n\n\tstring _BuildText(bool forFind) {\n\t\tvar b = new StringBuilder();\n\t\tforeach (var word in Words) {\n\t\t\tvar s = word.Separator;\n\t\t\tif (forFind) if (!s.NE() && s != \" \") s = s is \"\\r\\n\" or \"\\n\" ? \" \" : s_rx1.Replace(s, \" \"); //replace any whitespace with \" \"\n\t\t\tb.Append(s);\n\t\t\tif (forFind) word.Offset = b.Length;\n\t\t\tb.Append(word.Text);\n\t\t}\n\t\treturn b.ToString();\n\t}\n\n\t/// <summary>\n\t/// Index (in <see cref=\"Words\"/>) of the first found word.\n\t/// If did not search for text (for example called <see cref=\"recognize\"/>), this property is 0.\n\t/// </summary>\n\tpublic int FoundWordIndex { get; internal set; }\n\n\t/// <summary>\n\t/// Range of found text in <see cref=\"TextForFind\"/>.\n\t/// </summary>\n\tpublic StartEnd FoundTextRange { get; internal set; }\n\n\t//internal void SortResults_() {\n\t//\tArray.Sort(Words, (x, y) => {\n\t//\t\tif (y.Rect.top >= x.Rect.bottom) return -1;\n\t//\t\tif (x.Rect.top >= y.Rect.bottom) return 1;\n\t//\t\treturn x.Rect.left - y.Rect.left;\n\t//\t});\n\t//}\n\n\tinternal void AdjustResults_(POINT resultOffset, OcrFlags flags) {\n\t\tif (flags.HasAny(OcrFlags.WindowDC | OcrFlags.PrintWindow) && Dpi.GetScalingInfo_(_area.W, out bool scaled, out _, out _) && scaled) {\n\t\t\tint d1 = screen.of(_area.W.Window).Dpi, d2 = Dpi.OfWindow(_area.W);\n\t\t\tforeach (ref var word in Words.AsSpan()) {\n\t\t\t\tvar r = word.Rect;\n\t\t\t\tr.left = Math2.MulDiv(r.left, d1, d2);\n\t\t\t\tr.top = Math2.MulDiv(r.top, d1, d2);\n\t\t\t\tr.right = Math2.MulDiv(r.right, d1, d2);\n\t\t\t\tr.bottom = Math2.MulDiv(r.bottom, d1, d2);\n\t\t\t\tword.Rect = r;\n\t\t\t}\n\t\t}\n\n\t\tif (resultOffset.x != 0 || resultOffset.y != 0) {\n\t\t\tforeach (ref var word in Words.AsSpan()) {\n\t\t\t\tvar r = word.Rect;\n\t\t\t\tr.Offset(resultOffset.x, resultOffset.y);\n\t\t\t\tword.Rect = r;\n\t\t\t}\n\t\t}\n\t}\n\n\t/// <summary>\n\t/// Gets or sets the default OCR engine.\n\t/// </summary>\n\t/// <remarks>\n\t/// If not set, the <c>get</c> function returns a static <see cref=\"OcrWin10\"/> object. To use another OCR engine, create and assign an object of type <see cref=\"OcrWin10\"/>, <see cref=\"OcrTesseract\"/>, <see cref=\"OcrGoogleCloud\"/>, <see cref=\"OcrMicrosoftAzure\"/> or other class that implements <see cref=\"IOcrEngine\"/>.\n\t/// </remarks>\n\tpublic static IOcrEngine engine {\n\t\tget => s_engine ??= new OcrWin10();\n\t\tset { s_engine = value; }\n\t}\n\tstatic IOcrEngine s_engine;\n\n\t/// <summary>\n\t/// Performs OCR (text recognition).\n\t/// </summary>\n\t/// <returns>\n\t/// Returns an <see cref=\"ocr\"/> object that contains recognized words etc.\n\t/// Returns <c>null</c> if the area is empty.\n\t/// </returns>\n\t/// <inheritdoc cref=\"find(IFArea, string, OcrFlags, double, IOcrEngine, int)\"/>\n\t/// <remarks>\n\t/// Captures image from screen or window (unless <i>area</i> is <see cref=\"Bitmap\"/>) and passes it to the OCR engine (calls <see cref=\"IOcrEngine.Recognize\"/>). Then creates and returns an <see cref=\"ocr\"/> object that contains results.\n\t///\n\t/// The speed depends on engine, area size and amount of text.\n\t/// </remarks>\n\tpublic static ocr recognize(IFArea area, OcrFlags flags = 0, double scale = 0, IOcrEngine engine = null) {\n\t\tengine ??= ocr.engine;\n\t\tarea.Before_(flags.HasAny(OcrFlags.WindowDC | OcrFlags.PrintWindow));\n\t\tif (!area.GetOcrData_(flags, out var b, out var resultOffset)) return null;\n\t\tscale = area.GetOcrScale_(scale, engine);\n\t\tvar a = engine.Recognize(b, dispose: area.Type != IFArea.AreaType.Bitmap, scale);\n\t\tvar r = new ocr(a, area);\n\t\tr.AdjustResults_(resultOffset, flags);\n\t\treturn r;\n\t}\n\n\t/// <summary>\n\t/// Performs OCR (text recognition) and finds text in results.\n\t/// </summary>\n\t/// <returns>\n\t/// Returns an <see cref=\"ocr\"/> object that contains the word index and can click it etc.\n\t/// Returns <c>null</c> if not found.\n\t/// </returns>\n\t/// <param name=\"area\">\n\t/// On-screen area or image:\n\t/// <br/>• <see cref=\"wnd\"/> - window or control (its client area).\n\t/// <br/>• <see cref=\"elm\"/> - UI element.\n\t/// <br/>• <see cref=\"Bitmap\"/> - image.\n\t/// <br/>• <see cref=\"RECT\"/> - a rectangle area in screen.\n\t/// <br/>• <see cref=\"IFArea\"/> - can contain <see cref=\"wnd\"/>, <see cref=\"elm\"/> or <see cref=\"Bitmap\"/>. Also allows to specify a rectangle in it, which makes the area smaller and the function faster. Example: <c>new(w, (left, top, width, height))</c>.\n\t/// </param>\n\t/// <param name=\"text\">\n\t/// Text to find in <see cref=\"TextForFind\"/>. Can have prefix:\n\t/// <br/>• <c>\"**r \"</c> - PCRE regular expression. Example: <c>@\"**r \\bwhole words\\b\"</c>.\n\t/// <br/>• <c>\"**R \"</c> - .NET regular expression.\n\t/// <br/>• <c>\"**i \"</c> - case-insensitive.\n\t/// <br/>• <c>\"**t \"</c> - case-sensitive (default).\n\t/// </param>\n\t/// <param name=\"flags\"></param>\n\t/// <param name=\"scale\">\n\t/// Scale factor (how much to resize the image before performing OCR).\n\t/// Value 2 or 3 may improve results of OCR engine <see cref=\"OcrWin10\"/> or <see cref=\"OcrTesseract\"/>.\n\t/// If 0 (default), depends on engine's <see cref=\"IOcrEngine.DpiScale\"/> and area's DPI.\n\t/// </param>\n\t/// <param name=\"engine\">OCR engine. Default: <see cref=\"engine\"/> (<see cref=\"OcrWin10\"/> if not specified).</param>\n\t/// <param name=\"skip\">Skip this count of found text instances.</param>\n\t/// <exception cref=\"AuWndException\">Invalid window handle (the <i>area</i> argument).</exception>\n\t/// <exception cref=\"ArgumentException\">An argument is/contains a <c>null</c>/invalid value.</exception>\n\t/// <exception cref=\"AuException\">Something failed.</exception>\n\t/// <remarks>\n\t/// The function captures image from screen or window (unless area is <see cref=\"Bitmap\"/>) and passes it to the OCR engine (calls <see cref=\"IOcrEngine.Recognize\"/>). Then finds the specified text in results. If found, creates and returns an <see cref=\"ocr\"/> object that contains results.\n\t///\n\t/// The speed depends on engine, area size and amount of text.\n\t/// </remarks>\n\tpublic static ocr find(IFArea area, string text, OcrFlags flags = 0, double scale = 0, IOcrEngine engine = null, int skip = 0)\n\t\t=> new ocrFinder(text, flags, scale, engine, skip).Find(area);\n\n\t/// <summary>\n\t/// Performs OCR (text recognition) and finds text in results. Can wait and throw <see cref=\"NotFoundException\"/>.\n\t/// </summary>\n\t/// <returns>\n\t/// Returns an <see cref=\"ocr\"/> object that contains the word index and can click it etc.\n\t/// If not found, throws exception or returns <c>null</c> (if <i>wait</i> negative).\n\t/// </returns>\n\t/// <param name=\"wait\">The wait timeout, seconds. If 0, does not wait. If negative, does not throw exception when not found.</param>\n\t/// <exception cref=\"NotFoundException\" />\n\t/// <exception cref=\"AuWndException\">Invalid window handle (the area argument), or the window closed while waiting.</exception>\n\t/// <inheritdoc cref=\"find(IFArea, string, OcrFlags, double, IOcrEngine, int)\"/>\n\tpublic static ocr find(Seconds wait, IFArea area, string text, OcrFlags flags = 0, double scale = 0, IOcrEngine engine = null, int skip = 0)\n\t\t=> new ocrFinder(text, flags, scale, engine, skip).Find(area, wait);\n\n\t/// <summary>\n\t/// Performs OCR (text recognition) and finds text in results. Waits until found.\n\t/// </summary>\n\t/// <returns>Returns an <see cref=\"ocr\"/> object that contains the word index and can click it etc. On timeout returns <c>null</c> if <i>timeout</i> is negative; else exception.</returns>\n\t/// <param name=\"timeout\">Timeout, seconds. Can be 0 (infinite), >0 (exception) or &lt;0 (no exception). More info: [](xref:wait_timeout).</param>\n\t/// <exception cref=\"TimeoutException\"><i>timeout</i> time has expired (if > 0).</exception>\n\t/// <exception cref=\"AuWndException\">Invalid window handle (the area argument), or the window closed while waiting.</exception>\n\t/// <inheritdoc cref=\"find(IFArea, string, OcrFlags, double, IOcrEngine, int)\"/>\n\tpublic static ocr wait(Seconds timeout, IFArea area, string text, OcrFlags flags = 0, double scale = 0, IOcrEngine engine = null, int skip = 0)\n\t\t=> new ocrFinder(text, flags, scale, engine, skip).Wait(timeout, area);\n\n\t/// <summary>\n\t/// Performs OCR (text recognition) and waits until the specified text does not exist in results.\n\t/// </summary>\n\t/// <returns>Returns <c>true</c>. On timeout returns <c>false</c> if <i>timeout</i> is negative; else exception.</returns>\n\t/// <param name=\"timeout\">Timeout, seconds. Can be 0 (infinite), >0 (exception) or &lt;0 (no exception). More info: [](xref:wait_timeout).</param>\n\t/// <inheritdoc cref=\"wait(Seconds, IFArea, string, OcrFlags, double, IOcrEngine, int)\"/>\n\tpublic static bool waitNot(Seconds timeout, IFArea area, string text, OcrFlags flags = 0, double scale = 0, IOcrEngine engine = null, int skip = 0)\n\t\t=> new ocrFinder(text, flags, scale, engine, skip).WaitNot(timeout, area);\n\n\t/// <summary>\n\t/// Gets the rectangle of the found word.\n\t/// </summary>\n\t/// <param name=\"inScreen\">Convert to screen coordinates. If <c>false</c>, it's in <i>area</i> coordinates (window client area, etc) without rectangle offset.</param>\n\t/// <param name=\"word\">Word index offset from <see cref=\"FoundWordIndex\"/>.</param>\n\tpublic RECT GetRect(bool inScreen, int word = 0) {\n\t\tint i = FoundWordIndex + word; if ((uint)i >= Words.Length) throw new ArgumentOutOfRangeException(\"word\");\n\t\tvar r = Words[i].Rect;\n\t\tif (inScreen) {\n\t\t\tif (_area.Type == IFArea.AreaType.Wnd) {\n\t\t\t\t_area.W.MapClientToScreen(ref r);\n\t\t\t} else if (_area.Type == IFArea.AreaType.Elm) {\n\t\t\t\tif (!_area.E.GetRect(out var rr)) return default;\n\t\t\t\tr.Offset(rr.left, rr.top);\n\t\t\t}\n\t\t}\n\t\treturn r;\n\t}\n\n\t/// <summary>\n\t/// Moves the mouse to the found text (the first word).\n\t/// </summary>\n\t/// <param name=\"x\">X coordinate in the word. Default - center. Examples: <c>10</c>, <c>^10</c> (reverse), <c>.5f</c> (fraction).</param>\n\t/// <param name=\"y\">Y coordinate in the word. Default - center.</param>\n\t/// <param name=\"word\">Word index offset from <see cref=\"FoundWordIndex\"/>.</param>\n\t/// <exception cref=\"InvalidOperationException\"><i>area</i> is <c>Bitmap</c>.</exception>\n\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"mouse.move(wnd, Coord, Coord, bool)\"/>.</exception>\n\t/// <remarks>\n\t/// Calls <see cref=\"mouse.move(wnd, Coord, Coord, bool)\"/>.\n\t/// </remarks>\n\tpublic void MouseMove(Coord x = default, Coord y = default, int word = 0) => _MouseAction(x, y, 0, word);\n\n\t/// <summary>\n\t/// Clicks the found text (the first word).\n\t/// </summary>\n\t/// <param name=\"x\">X coordinate in the word. Default - center. Examples: <c>10</c>, <c>^10</c> (reverse), <c>.5f</c> (fraction).</param>\n\t/// <param name=\"y\">Y coordinate in the word. Default - center.</param>\n\t/// <param name=\"button\">Which button and how to use it.</param>\n\t/// <param name=\"word\">Word index offset from <see cref=\"FoundWordIndex\"/>.</param>\n\t/// <exception cref=\"InvalidOperationException\"><i>area</i> is <c>Bitmap</c>.</exception>\n\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"mouse.clickEx(MButton, wnd, Coord, Coord, bool)\"/>.</exception>\n\t/// <remarks>\n\t/// Calls <see cref=\"mouse.clickEx(MButton, wnd, Coord, Coord, bool)\"/>.\n\t/// </remarks>\n\tpublic MRelease MouseClick(Coord x = default, Coord y = default, MButton button = MButton.Left, int word = 0) {\n\t\t_MouseAction(x, y, button == 0 ? MButton.Left : button, word);\n\t\treturn button;\n\t}\n\n\t/// <summary>\n\t/// Double-clicks the found text (the first word).\n\t/// </summary>\n\t/// <inheritdoc cref=\"MouseClick(Coord, Coord, MButton, int)\"/>\n\tpublic void MouseClickD(Coord x = default, Coord y = default, int word = 0) => MouseClick(x, y, MButton.DoubleClick, word);\n\n\t/// <summary>\n\t/// Right-clicks the found text (the first word).\n\t/// </summary>\n\t/// <inheritdoc cref=\"MouseClick(Coord, Coord, MButton, int)\"/>\n\tpublic void MouseClickR(Coord x = default, Coord y = default, int word = 0) => MouseClick(x, y, MButton.Right, word);\n\n\tvoid _MouseAction(Coord x, Coord y, MButton button, int word) {\n\t\tif (_area.Type == IFArea.AreaType.Bitmap) throw new InvalidOperationException();\n\n\t\tvar r = GetRect(false, word);\n\t\tif (r.NoArea) return;\n\n\t\tvar p = Coord.NormalizeInRect(x, y, r, centerIfEmpty: true);\n\n\t\tif (_area.Type == IFArea.AreaType.Screen) {\n\t\t\tif (button == 0) mouse.move(p);\n\t\t\telse mouse.clickEx(button, p);\n\t\t} else {\n\t\t\tvar w = _area.W;\n\t\t\tif (_area.Type == IFArea.AreaType.Elm) {\n\t\t\t\tif (!_area.E.GetRect(out var rr, w) || rr.NoArea) throw new AuException(0, \"*get rectangle\");\n\t\t\t\tp.x += rr.left; p.y += rr.top;\n\t\t\t}\n\t\t\tif (button == 0) mouse.move(w, p.x, p.y);\n\t\t\telse mouse.clickEx(button, w, p.x, p.y);\n\t\t}\n\t}\n\n\t/// <summary>\n\t/// Posts mouse-click messages to the window, using coordinates in the found text (the first word).\n\t/// </summary>\n\t/// <param name=\"x\">X coordinate in the word. Default - center. Examples: <c>10</c>, <c>^10</c> (reverse), <c>.5f</c> (fraction).</param>\n\t/// <param name=\"y\">Y coordinate in the word. Default - center.</param>\n\t/// <param name=\"button\">Can specify the left (default), right or middle button. Also flag for double-click, press or release.</param>\n\t/// <param name=\"word\">Word index offset from <see cref=\"FoundWordIndex\"/>.</param>\n\t/// <exception cref=\"InvalidOperationException\"><i>area</i> is <c>Bitmap</c> or <c>screen</c>.</exception>\n\t/// <exception cref=\"AuException\">Failed to get UI element rectangle (when searched in a UI element).</exception>\n\t/// <remarks>\n\t/// Does not move the mouse.\n\t/// Does not wait until the target application finishes processing the message.\n\t/// Works not with all elements.\n\t/// </remarks>\n\tpublic void PostClick(Coord x = default, Coord y = default, MButton button = MButton.Left, int word = 0) {\n\t\tvar w = _area.W;\n\t\tif (w.Is0) throw new InvalidOperationException();\n\n\t\tvar r = GetRect(false, word);\n\t\tif (r.NoArea) return;\n\n\t\tif (_area.Type == IFArea.AreaType.Elm) {\n\t\t\tif (!_area.E.GetRect(out var rr, w) || r.NoArea) throw new AuException(0, \"*get rectangle\");\n\t\t\tr.Offset(rr.left, rr.top);\n\t\t}\n\n\t\tmouse.PostClick_(w, r, x, y, button);\n\t}\n\n\t//rejected: PostClickD, PostClickR. Rarely used.\n}\n"
  },
  {
    "path": "Au/UI objects/ocrFinder.cs",
    "content": "using System.Drawing;\nusing System.Drawing.Imaging;\nusing System.Text.RegularExpressions;\n\nnamespace Au;\n\n/// <summary>\n/// Performs OCR (text recognition) and find text in OCR results. Contains text to find and OCR parameters.\n/// </summary>\n/// <remarks>\n/// Can be used instead of <see cref=\"ocr.find\"/>.\n/// </remarks>\npublic class ocrFinder {\n\treadonly object _text;\n\treadonly int _textType;\n\treadonly OcrFlags _flags;\n\treadonly double _scale;\n\treadonly IOcrEngine _engine;\n\treadonly int _skip;\n\n\tIFArea _area;\n\tHash.MD5Result _md5;\n\tbool _waitNot;\n\n\tinternal enum Action_ { Find, Wait, WaitNot }\n\n\t/// <summary>\n\t/// Stores text to find and OCR parameters.\n\t/// </summary>\n\t/// <exception cref=\"ArgumentException\"><i>text</i> is empty or contains invalid regular expression.</exception>\n\t/// <inheritdoc cref=\"ocr.find(IFArea, string, OcrFlags, double, IOcrEngine, int)\" path=\"/param\"/>\n\tpublic ocrFinder(string text, OcrFlags flags = 0, double scale = 0, IOcrEngine engine = null, int skip = 0) {\n\t\t//**r - regexp (PCRE)\n\t\t//**R - Regex (.NET)\n\t\t//**i - text, ignore case\n\t\t//**t - text\n\t\t_textType = text.Starts(false, \"**r \", \"**R \", \"**i \", \"**t \");\n\t\tif (_textType > 0) text = text[4..];\n\t\tif (string.IsNullOrWhiteSpace(text)) throw new ArgumentException(\"empty text\");\n\t\t_text = _textType switch {\n\t\t\t1 => new regexp(text),\n\t\t\t2 => new Regex(text, RegexOptions.CultureInvariant),\n\t\t\t_ => text\n\t\t};\n\n\t\t_flags = flags;\n\t\t_scale = scale;\n\t\t_engine = engine ?? ocr.engine;\n\t\t_skip = skip;\n\t}\n\n\t/// <summary>\n\t/// Returns an <see cref=\"ocr\"/> object that contains the word index and can click it etc.\n\t/// </summary>\n\tpublic ocr Result { get; internal set; }\n\n\t/// <inheritdoc cref=\"ocr.find(IFArea, string, OcrFlags, double, IOcrEngine, int)\"/>\n\tpublic ocr Find(IFArea area) => Exists(area) ? Result : null;\n\n\t/// <inheritdoc cref=\"ocr.find(Seconds, IFArea, string, OcrFlags, double, IOcrEngine, int)\"/>\n\tpublic ocr Find(IFArea area, Seconds wait) => Exists(area, wait) ? Result : null;\n\n\t/// <returns>If found, sets <see cref=\"Result\"/> and returns <c>true</c>, else <c>false</c>.</returns>\n\t/// <inheritdoc cref=\"Find(IFArea)\"/>\n\tpublic bool Exists(IFArea area) {\n\t\t_Before(area, Action_.Find);\n\t\treturn _Find(false);\n\t}\n\n\t/// <returns>If found, sets <see cref=\"Result\"/> and returns <c>true</c>. Else throws exception or returns <c>false</c> (if <i>wait</i> negative).</returns>\n\t/// <inheritdoc cref=\"Find(IFArea, Seconds)\"/>\n\tpublic bool Exists(IFArea area, Seconds wait) {\n\t\tbool r = wait.Exists_() ? Exists(area) : Wait_(Action_.Wait, wait, area);\n\t\treturn r || wait.ReturnFalseOrThrowNotFound_();\n\t}\n\n\t/// <inheritdoc cref=\"ocr.wait\"/>\n\tpublic ocr Wait(Seconds timeout, IFArea area)\n\t\t=> Wait_(Action_.Wait, timeout, area) ? Result : null;\n\t//TODO3: suspend waiting while a mouse button is pressed.\n\t//\tNow, eg if finds while scrolling, although MouseMove waits until buttons released, but moves to the old (wrong) place.\n\n\t/// <inheritdoc cref=\"ocr.waitNot\"/>\n\tpublic bool WaitNot(Seconds timeout, IFArea area)\n\t\t=> Wait_(Action_.WaitNot, timeout, area);\n\n\tinternal bool Wait_(Action_ action, Seconds timeout, IFArea area) {\n\t\tif (area.Type == IFArea.AreaType.Bitmap) throw new ArgumentException(\"Bitmap and wait\");\n\t\t_Before(area, action);\n\t\t_md5 = default;\n\t\ttry { return wait.until(timeout, () => _Find(true) ^ _waitNot); }\n\t\tfinally { _area = null; }\n\t}\n\n\t//called at the start of _Find and Wait_\n\tvoid _Before(IFArea area, Action_ action) {\n\t\tNot_.Null(area);\n\t\tarea.Before_(_flags.HasAny(OcrFlags.WindowDC | OcrFlags.PrintWindow));\n\t\t_area = area;\n\t\t_waitNot = action == Action_.WaitNot;\n\t}\n\n\t/// <summary>\n\t/// If <c>testing</c> <c>true</c>, the finder after OCR sets <c>result</c>. Then you can access it when text not found (or found).\n\t/// </summary>\n\t[ThreadStatic] internal static (bool testing, ocr result) testing_;\n\n\tbool _Find(bool waiting) {\n\t\tif (!_area.GetOcrData_(_flags, out var b, out var resultOffset)) return false;\n\t\tbool inBitmap = _area.Type == IFArea.AreaType.Bitmap;\n\n\t\tif (waiting) { //don't OCR if nothing changed. Can be expensive ($) if using cloud.\n\t\t\tvar m = _BitmapHash(b);\n\t\t\tif (m == _md5) {\n\t\t\t\tif (!inBitmap) b.Dispose();\n\t\t\t\treturn _waitNot;\n\t\t\t}\n\t\t\t_md5 = m;\n\t\t}\n\n\t\tvar scale = _area.GetOcrScale_(_scale, _engine);\n\t\tvar a = _engine.Recognize(b, dispose: !inBitmap, scale);\n\t\tvar r = new ocr(a, _area);\n\t\tif (testing_.testing) testing_.result = r;\n\n\t\tif (!_FindText(r)) return false;\n\n\t\tr.AdjustResults_(resultOffset, _flags);\n\n\t\tResult = r;\n\t\treturn true;\n\n\t\tstatic unsafe Hash.MD5Result _BitmapHash(Bitmap b) {\n\t\t\tusing var d = b.Data(ImageLockMode.ReadOnly);\n\t\t\treturn Hash.MD5(new RByte((void*)d.Scan0, d.Height * d.Stride));\n\t\t}\n\t}\n\n\tbool _FindText(ocr r) {\n\t\tstring s1 = r.TextForFind, s2 = _text as string;\n\t\tif (s2 != null) {\n\t\t\ts1 = s1.Replace('I', 'l').Replace('1', 'l').Replace('0', 'O');\n\t\t\ts2 = s2.Replace('I', 'l').Replace('1', 'l').Replace('0', 'O');\n\t\t}\n\n\t\tint startIndex = 0, skip = _skip;\n\t\tg1:\n\t\tint i = -1, i2 = i;\n\t\tswitch (_text) {\n\t\tcase regexp rx:\n\t\t\tif (rx.Match(s1, 0, out RXGroup g, startIndex..)) { i = g.Start; i2 = g.End; }\n\t\t\tbreak;\n\t\tcase Regex rx:\n\t\t\tif (rx.Match(s1, startIndex) is var m && m.Success) { i = m.Index; i2 = i + m.Length; }\n\t\t\tbreak;\n\t\tdefault:\n\t\t\ti = s1.Find(s2, startIndex, _textType == 3);\n\t\t\ti2 = i + s2.Length;\n\t\t\tbreak;\n\t\t}\n\n\t\tif (i >= 0) {\n\t\t\tif (skip > 0) { skip--; startIndex = i2; goto g1; }\n\t\t\tr.FoundTextRange = new(i, i2);\n\t\t\tfor (int j = r.Words.Length; --j >= 0;) {\n\t\t\t\tif (r.Words[j].Offset <= i) { r.FoundWordIndex = j; return true; }\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t///\n\tpublic override string ToString() => _text.ToString();\n}\n"
  },
  {
    "path": "Au/UI objects/ocr_types.cs",
    "content": "using System.Drawing;\nusing System.Drawing.Imaging;\nusing System.Text.Json.Nodes;\n\nnamespace Au.Types;\n\n/// <summary>\n/// Used by <see cref=\"ocr\"/> and <see cref=\"ocrFinder\"/>.\n/// </summary>\npublic interface IOcrEngine {\n\t/// <summary>\n\t/// Recognizes text in image.\n\t/// </summary>\n\t/// <param name=\"b\">Image.</param>\n\t/// <param name=\"dispose\">Call <c>b.Dispose()</c>. The image will not be used by the caller.</param>\n\t/// <param name=\"scale\">Scale factor.</param>\n\t/// <returns>Recognized words.</returns>\n\t/// <remarks>\n\t/// The class that implements this function can use static <see cref=\"IOcrEngine\"/> functions to prepare the image (scale etc) and get image data.\n\t/// </remarks>\n\tOcrWord[] Recognize(Bitmap b, bool dispose, double scale);\n\n\t/// <summary>\n\t/// Let OCR functions scale images captured from a window or screen, 1-2 times depending on DPI, unless non-zero <i>scale</i> parameter is specified.\n\t/// </summary>\n\tbool DpiScale { get; set; }\n\n\t#region util\n\n\t/// <summary>\n\t/// If need, resizes the image and/or ensures it isn't too small.\n\t/// </summary>\n\t/// <param name=\"dispose\">Call <c>b.Dispose()</c> if created new image. The input image will not be used by the caller.</param>\n\t/// <param name=\"scale\">Scale factor.</param>\n\t/// <param name=\"minWidth\">Minimal image width accepted by the OCR engine.</param>\n\t/// <param name=\"minHeight\">Minimal image height accepted by the OCR engine.</param>\n\tprotected static Bitmap PrepareBitmap(Bitmap b, bool dispose, double scale, int minWidth = 0, int minHeight = 0) {\n\t\tvar b0 = b;\n\t\tif (scale > 1) b = b.Resize(scale, BRFilter.Lanczos3, dispose);\n\n\t\t//Microsoft OCR (Win10 and Azure) reject small images\n\t\tif (minWidth > 0 && minHeight > 0) {\n\t\t\tvar zi = b.Size;\n\t\t\tif (zi.Width < minWidth || zi.Height < minHeight) {\n\t\t\t\tint wid = Math.Max(zi.Width, minWidth), hei = Math.Max(zi.Height, minHeight);\n\t\t\t\tvar b2 = new Bitmap(wid, hei, b.PixelFormat);\n\t\t\t\tb2.SetResolution(b.HorizontalResolution, b.VerticalResolution);\n\t\t\t\tusing var g = Graphics.FromImage(b2);\n\t\t\t\tg.Clear(Color.White);\n\t\t\t\tg.DrawImage(b, 0, 0);\n\t\t\t\tif (dispose || b != b0) b.Dispose();\n\t\t\t\tb = b2;\n\t\t\t}\n\t\t}\n\n\t\t//var file = folders.Temp + \"test.png\"; b.Save(file); run.it(file);\n\t\treturn b;\n\t}\n\n\t/// <summary>\n\t/// Gets image pixels.\n\t/// </summary>\n\t/// <remarks>The pixel format is either <c>Format32bppRgb</c> (if it's <i>b</i> format) or <c>Format32bppArgb</c>.</remarks>\n\tprotected static unsafe byte[] GetBitmapData(Bitmap b) {\n\t\tusing var d = b.Data(ImageLockMode.ReadOnly, b.PixelFormat == PixelFormat.Format32bppRgb ? PixelFormat.Format32bppRgb : PixelFormat.Format32bppArgb);\n\t\treturn new Span<byte>((void*)d.Scan0, d.Width * d.Height * 4).ToArray();\n\t}\n\n\t/// <summary>\n\t/// Gets image data in <c>.png</c> file format.\n\t/// </summary>\n\tprotected static unsafe byte[] GetBitmapPngFileData(Bitmap b) {\n\t\tusing var ms = new MemoryStream();\n\t\tb.Save(ms, ImageFormat.Png);\n\t\tvar a = ms.ToArray();\n\t\treturn a;\n\t}\n\n\t/// <summary>\n\t/// Converts word polygon JSON nodes to <see cref=\"RECT\"/>.\n\t/// </summary>\n\tprotected static RECT PolyToRect(JsonNode xTL, JsonNode yTL, JsonNode xTR, JsonNode yTR, JsonNode xBR, JsonNode yBR, JsonNode xBL, JsonNode yBL) {\n\t\treturn RECT.FromLTRB((i(xTL) + i(xBL) + 1) / 2, (i(yTL) + i(yTR) + 1) / 2, (i(xTR) + i(xBR)) / 2, (i(yBR) + i(yBL)) / 2);\n\t\tstatic int i(JsonNode n) => n != null ? (int)n : 0; //the node may be missing in JSON if has default value\n\t}\n\n\t#endregion\n}\n\n/// <summary>\n/// Stores text and rectangle of a word in OCR results.\n/// See <see cref=\"ocr.Words\"/>.\n/// </summary>\npublic record class OcrWord {\n\t/// <param name=\"separator\">Separator before the word (space, new line, etc). Also can be <c>\"\"</c> or <c>null</c>.</param>\n\t/// <param name=\"text\">Word text.</param>\n\t/// <param name=\"rect\">Word rectangle in <i>area</i>, possibly scaled.</param>\n\t/// <param name=\"scale\">The <i>scale</i> parameter of the OCR function. This function unscales <i>rect</i> if need.</param>\n\tpublic OcrWord(string separator, string text, RECT rect, double scale) {\n\t\tSeparator = separator;\n\t\tText = text;\n\t\tRect = scale > 1 ? RECT.FromLTRB(i(rect.left), i(rect.top), i(rect.right), i(rect.bottom)) : rect;\n\n\t\tint i(int x) => (x / scale).ToInt();\n\t}\n\n\t/// <summary>Separator before the word (space, new line, etc). Also can be <c>\"\"</c> or <c>null</c>.</summary>\n\tpublic string Separator { get; }\n\n\t/// <summary>Word text.</summary>\n\tpublic string Text { get; }\n\n\t/// <summary>Word rectangle in <i>area</i>.</summary>\n\tpublic RECT Rect { get; internal set; }\n\n\t/// <summary>Word offset in <see cref=\"ocr.TextForFind\"/>.</summary>\n\tpublic int Offset { get; internal set; }\n}\n\n/// <summary>\n/// Flags for <see cref=\"ocr\"/> and <see cref=\"ocrFinder\"/>.\n/// </summary>\n[Flags]\npublic enum OcrFlags {\n\t/// <inheritdoc cref=\"IFFlags.WindowDC\"/>\n\tWindowDC = 1,\n\n\t/// <inheritdoc cref=\"IFFlags.PrintWindow\"/>\n\tPrintWindow = 2,\n\n\t///// <inheritdoc cref=\"IFFlags.WindowDwm\"/>\n\t//WindowDwm = 4,\n\n\t//note: the above values must be the same in CIFlags, CIUFlags, IFFlags, OcrFlags.\n\n\t//rejected. Maybe in the future.\n\t///// <summary>\n\t///// Sort words by word rectangle position, left-to-right and top-to-bottom.\n\t///// </summary>\n\t//Sort = 0x100,\n}\n"
  },
  {
    "path": "Au/UI objects/uiimage.cs",
    "content": "using System.Drawing;\n\nnamespace Au;\n\n/// <summary>\n/// Captures, finds and clicks images and colors in windows.\n/// </summary>\n/// <remarks>\n/// An image is any visible rectangular part of a window. A color is any visible pixel (the same as image of size 1x1).\n/// A <c>uiimage</c> variable holds results of <see cref=\"find\"/> and similar functions (rectangle etc).\n/// </remarks>\npublic class uiimage {\n\t#region results\n\n\treadonly IFArea _area;\n\n\t///// <summary>\n\t///// <i>area</i> parameter of the function.\n\t///// </summary>\n\t//public IFArea Area => _area;\n\n\tinternal uiimage(IFArea area) {\n\t\t_area = area;\n\t}\n\n\t/// <summary>\n\t/// Gets location of the found image, relative to the search area.\n\t/// </summary>\n\t/// <remarks>\n\t/// Relative to the window/control client area (if area type is <see cref=\"wnd\"/>), UI element (if <see cref=\"elm\"/>), image (if <see cref=\"Bitmap\"/>) or screen (if <see cref=\"RECT\"/>).\n\t/// More info: <see cref=\"find\"/>.\n\t/// </remarks>\n\tpublic RECT Rect { get; init; }\n\n\t/// <summary>\n\t/// Gets location of the found image in screen coordinates.\n\t/// </summary>\n\t/// <remarks>\n\t/// Slower than <see cref=\"Rect\"/>.\n\t/// </remarks>\n\tpublic RECT RectInScreen {\n\t\tget {\n\t\t\tRECT r;\n\t\t\tswitch (_area.Type) {\n\t\t\tcase IFArea.AreaType.Wnd:\n\t\t\t\tr = Rect;\n\t\t\t\t_area.W.MapClientToScreen(ref r);\n\t\t\t\treturn r;\n\t\t\tcase IFArea.AreaType.Elm:\n\t\t\t\tif (!_area.E.GetRect(out var rr)) return default;\n\t\t\t\tr = Rect;\n\t\t\t\tr.Offset(rr.left, rr.top);\n\t\t\t\treturn r;\n\t\t\t}\n\t\t\treturn Rect; //screen or bitmap\n\t\t}\n\t}\n\n\t/// <summary>\n\t/// Gets 0-based index of current matching image instance.\n\t/// </summary>\n\t/// <remarks>\n\t/// Can be useful in <i>also</i> callback functions.\n\t/// When the <i>image</i> argument is a list of images, <c>MatchIndex</c> starts from 0 for each list image.\n\t/// </remarks>\n\tpublic int MatchIndex { get; init; }\n\n\t/// <summary>\n\t/// When the <i>image</i> argument is a list of images, gets 0-based index of the list image.\n\t/// </summary>\n\tpublic int ListIndex { get; init; }\n\n\t/// <summary>\n\t/// Can be used in <i>also</i> callback function to skip <i>n</i> matching images. Example: <c>also: o => o.Skip(n)</c>.\n\t/// </summary>\n\t/// <param name=\"n\">How many matching images to skip.</param>\n\tpublic IFAlso Skip(int n) => MatchIndex == n ? IFAlso.OkReturn : (MatchIndex < n ? IFAlso.FindOther : IFAlso.FindOtherOfList);\n\n\t/// <summary>\n\t/// Moves the mouse to the found image.\n\t/// </summary>\n\t/// <param name=\"x\">X coordinate in the found image. Default - center. Examples: <c>10</c>, <c>^10</c> (reverse), <c>.5f</c> (fraction).</param>\n\t/// <param name=\"y\">Y coordinate in the found image. Default - center.</param>\n\t/// <exception cref=\"InvalidOperationException\"><i>area</i> is <c>Bitmap</c>.</exception>\n\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"mouse.move(wnd, Coord, Coord, bool)\"/>.</exception>\n\t/// <remarks>\n\t/// Calls <see cref=\"mouse.move(wnd, Coord, Coord, bool)\"/>.\n\t/// </remarks>\n\tpublic void MouseMove(Coord x = default, Coord y = default) => _MouseAction(x, y, 0);\n\n\t/// <summary>\n\t/// Clicks the found image.\n\t/// </summary>\n\t/// <param name=\"x\">X coordinate in the found image. Default - center. Examples: <c>10</c>, <c>^10</c> (reverse), <c>.5f</c> (fraction).</param>\n\t/// <param name=\"y\">Y coordinate in the found image. Default - center.</param>\n\t/// <param name=\"button\">Which button and how to use it.</param>\n\t/// <exception cref=\"InvalidOperationException\"><i>area</i> is <c>Bitmap</c>.</exception>\n\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"mouse.clickEx(MButton, wnd, Coord, Coord, bool)\"/>.</exception>\n\t/// <remarks>\n\t/// Calls <see cref=\"mouse.clickEx(MButton, wnd, Coord, Coord, bool)\"/>.\n\t/// </remarks>\n\tpublic MRelease MouseClick(Coord x = default, Coord y = default, MButton button = MButton.Left) {\n\t\t_MouseAction(x, y, button == 0 ? MButton.Left : button);\n\t\treturn button;\n\t}\n\n\t/// <summary>\n\t/// Double-clicks the found image.\n\t/// </summary>\n\t/// <inheritdoc cref=\"MouseClick(Coord, Coord, MButton)\"/>\n\tpublic void MouseClickD(Coord x = default, Coord y = default) => MouseClick(x, y, MButton.DoubleClick);\n\n\t/// <summary>\n\t/// Right-clicks the found image.\n\t/// </summary>\n\t/// <inheritdoc cref=\"MouseClick(Coord, Coord, MButton)\"/>\n\tpublic void MouseClickR(Coord x = default, Coord y = default) => MouseClick(x, y, MButton.Right);\n\n\tvoid _MouseAction(Coord x, Coord y, MButton button) {\n\t\tif (_area.Type == IFArea.AreaType.Bitmap) throw new InvalidOperationException();\n\n\t\tDebug.Assert(!Rect.NoArea);\n\t\tif (Rect.NoArea) return;\n\n\t\t//rejected: Click will activate it. Don't activate if just Move.\n\t\t//if(0 != (_f._flags & IFFlags.WindowDC)) {\n\t\t//\tif(_area.W.IsCloaked) _area.W.ActivateL();\n\t\t//}\n\n\t\tvar p = Coord.NormalizeInRect(x, y, Rect, centerIfEmpty: true);\n\n\t\tif (_area.Type == IFArea.AreaType.Screen) {\n\t\t\tif (button == 0) mouse.move(p);\n\t\t\telse mouse.clickEx(button, p);\n\t\t} else {\n\t\t\tvar w = _area.W;\n\t\t\tif (_area.Type == IFArea.AreaType.Elm) {\n\t\t\t\tif (!_area.E.GetRect(out var r, w) || r.NoArea) throw new AuException(0, \"*get rectangle\");\n\t\t\t\tp.x += r.left; p.y += r.top;\n\t\t\t}\n\t\t\tif (button == 0) mouse.move(w, p.x, p.y);\n\t\t\telse mouse.clickEx(button, w, p.x, p.y);\n\t\t}\n\t}\n\n\t/// <summary>\n\t/// Posts mouse-click messages to the window, using coordinates in the found image.\n\t/// </summary>\n\t/// <param name=\"button\">Can specify the left (default), right or middle button. Also flag for double-click, press or release.</param>\n\t/// <exception cref=\"ArgumentException\">Unsupported button specified.</exception>\n\t/// <inheritdoc cref=\"PostClickD(Coord, Coord)\"/>\n\tpublic void PostClick(Coord x = default, Coord y = default, MButton button = MButton.Left) {\n\t\tvar w = _area.W;\n\t\tif (w.Is0) throw new InvalidOperationException();\n\n\t\tDebug.Assert(!Rect.NoArea);\n\t\tif (Rect.NoArea) return;\n\n\t\tvar r = Rect;\n\t\tif (_area.Type == IFArea.AreaType.Elm) {\n\t\t\tif (!_area.E.GetRect(out var rr, w) || r.NoArea) throw new AuException(0, \"*get rectangle\");\n\t\t\tr.Offset(rr.left, rr.top);\n\t\t}\n\n\t\tmouse.PostClick_(w, r, x, y, button);\n\t}\n\n\t/// <summary>\n\t/// Posts mouse-double-click messages to the window, using coordinates in the found image.\n\t/// </summary>\n\t/// <param name=\"x\">X coordinate in the found image. Default - center. Examples: <c>10</c>, <c>^10</c> (reverse), <c>.5f</c> (fraction).</param>\n\t/// <param name=\"y\">Y coordinate in the found image. Default - center.</param>\n\t/// <exception cref=\"InvalidOperationException\"><i>area</i> is <c>Bitmap</c> or <c>screen</c>.</exception>\n\t/// <exception cref=\"AuException\">Failed to get UI element rectangle (when searched in a UI element).</exception>\n\t/// <remarks>\n\t/// Does not move the mouse.\n\t/// Does not wait until the target application finishes processing the message.\n\t/// Works not with all elements.\n\t/// </remarks>\n\tpublic void PostClickD(Coord x = default, Coord y = default) => PostClick(x, y, MButton.DoubleClick);\n\n\t/// <summary>\n\t/// Posts mouse-right-click messages to the window, using coordinates in the found image.\n\t/// </summary>\n\t/// <inheritdoc cref=\"PostClickD(Coord, Coord)\"/>\n\tpublic void PostClickR(Coord x = default, Coord y = default) => PostClick(x, y, MButton.Right);\n\n\t///\n\tpublic override string ToString() => $\"{ListIndex}, {MatchIndex}, {Rect}\";\n\n\t#endregion\n\n\t/// <summary>\n\t/// Finds image(s) or color(s) displayed in a window or other area.\n\t/// </summary>\n\t/// <returns>\n\t/// Returns a <see cref=\"uiimage\"/> object that contains the rectangle of the found image and can click it etc.\n\t/// Returns <c>null</c> if not found.\n\t/// </returns>\n\t/// <param name=\"area\">\n\t/// Where to search:\n\t/// <br/>• <see cref=\"wnd\"/> - window or control (its client area).\n\t/// <br/>• <see cref=\"elm\"/> - UI element.\n\t/// <br/>• <see cref=\"Bitmap\"/> - image.\n\t/// <br/>• <see cref=\"RECT\"/> - a rectangle area in screen.\n\t/// <br/>• <see cref=\"IFArea\"/> - can contain <see cref=\"wnd\"/>, <see cref=\"elm\"/> or <see cref=\"Bitmap\"/>. Also allows to specify a rectangle in it, which makes the area smaller and the function faster. Example: <c>new(w, (left, top, width, height))</c>.\n\t/// </param>\n\t/// <param name=\"image\">Image or color to find. Or array of them. More info: <see cref=\"IFImage\"/>.</param>\n\t/// <param name=\"flags\"></param>\n\t/// <param name=\"diff\">Maximal allowed color difference. Can be 0 - 100, but should be as small as possible. Use to find images with slightly different colors than the specified image.</param>\n\t/// <param name=\"also\">\n\t/// Callback function. Called for each found image instance and receives its rectangle, match index and list index. Can return one of <see cref=\"IFAlso\"/> values.\n\t/// <br/>Examples:\n\t/// <br/>• Skip 2 matching images: <c>also: o => o.Skip(2)</c>\n\t/// <br/>• Skip some matching images if some condition is <c>false</c>: <c>also: o => condition ? IFAlso.OkReturn : IFAlso.FindOther</c>\n\t/// <br/>• Get rectangles etc of all matching images: <c>also: o => { list.Add(o); return IFAlso.OkFindMore; }</c>\n\t/// <br/>• Do different actions depending on which list images found: <c>var found = new BitArray(images.Length); uiimage.find(w, images, also: o => { found[o.ListIndex] = true; return IFAlso.OkFindMoreOfList; }); if(found[0]) print.it(0); if(found[1]) print.it(1);</c>\n\t/// </param>\n\t/// <exception cref=\"AuWndException\">Invalid window handle (the <i>area</i> argument).</exception>\n\t/// <exception cref=\"ArgumentException\">An argument is/contains a <c>null</c>/invalid value.</exception>\n\t/// <exception cref=\"FileNotFoundException\">Image file does not exist.</exception>\n\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"ImageUtil.LoadGdipBitmap\"/>.</exception>\n\t/// <exception cref=\"AuException\">Something failed.</exception>\n\t/// <remarks>\n\t/// To create code for this function, use tool <b>Find image or color in window</b>.\n\t/// \n\t/// The speed mostly depends on:\n\t/// 1. The size of the search area. Use the smallest possible area (control or UI element or rectangle in window).\n\t/// 2. Flags <see cref=\"IFFlags.WindowDC\"/> (makes faster), <see cref=\"IFFlags.PrintWindow\"/>. The speed depends on window.\n\t/// 3. Video driver. Can be much slower if incorrect, generic or virtual PC driver is used. The above flags should help.\n\t/// 4. <i>diff</i>. Should be as small as possible.\n\t/// \n\t/// If flag <see cref=\"IFFlags.WindowDC\"/> or <see cref=\"IFFlags.PrintWindow\"/> not used, the search area must be visible on the screen, because this function then gets pixels from the screen.\n\t/// \n\t/// Can find only images that exactly match the specified image. With <i>diff</i> can find images with slightly different colors and brightness.\n\t/// \n\t/// Transparent and partially transparent pixels of <i>image</i> are ignored. You can draw transparent areas with an image editor that supports it, for example Paint.NET.\n\t/// \n\t/// This function is not the best way to find objects when the script is intended for long use or for use on multiple computers or must be very reliable. Because it may fail to find the image after changing some settings - system theme, application theme, text size (DPI), font smoothing (if the image contains text), etc. Also are possible various unexpected temporary conditions that may distort or hide the image, for example adjacent window shadow, a tooltip or some temporary window. If possible, in such scripts instead use other functions, eg find control or UI element.\n\t/// \n\t/// Flags <see cref=\"IFFlags.WindowDC\"/> and <see cref=\"IFFlags.PrintWindow\"/> cannot be used if <i>area</i> is <c>Bitmap</c> or <see cref=\"RECT\"/>.\n\t/// </remarks>\n\t/// <example>\n\t/// Code created with tool <b>Find image or color in window</b>.\n\t/// <code><![CDATA[\n\t/// var w = wnd.find(1, \"Window Name\");\n\t/// string image = \"image:iVBORw0KGgoAAAANSUhEUgAAABYAAAANCAYAAACtpZ5jAAAAAXNSR0IArs4c...\";\n\t/// var im = uiimage.find(1, w, image);\n\t/// im.MouseClick();\n\t/// ]]></code>\n\t/// </example>\n\tpublic static uiimage find(IFArea area, IFImage image, IFFlags flags = 0, int diff = 0, Func<uiimage, IFAlso> also = null)\n\t\t=> new uiimageFinder(image, flags, diff, also).Find(area);\n\n\t/// <summary>\n\t/// Finds image(s) or color(s) displayed in a window or other area. Can wait and throw <see cref=\"NotFoundException\"/>.\n\t/// </summary>\n\t/// <returns>\n\t/// Returns a <see cref=\"uiimage\"/> object that contains the rectangle of the found image and can click it etc.\n\t/// If not found, throws exception or returns <c>null</c> (if <i>wait</i> negative).\n\t/// </returns>\n\t/// <param name=\"wait\">The wait timeout, seconds. If 0, does not wait. If negative, does not throw exception when not found.</param>\n\t/// <exception cref=\"NotFoundException\" />\n\t/// <exception cref=\"AuWndException\">Invalid window handle (the area argument), or the window closed while waiting.</exception>\n\t/// <inheritdoc cref=\"find(IFArea, IFImage, IFFlags, int, Func{uiimage, IFAlso})\"/>\n\tpublic static uiimage find(Seconds wait, IFArea area, IFImage image, IFFlags flags = 0, int diff = 0, Func<uiimage, IFAlso> also = null)\n\t\t=> new uiimageFinder(image, flags, diff, also).Find(area, wait);\n\n\t/// <summary>\n\t/// Finds image(s) or color(s) displayed in a window or other area. Waits until found.\n\t/// More info: <see cref=\"find\"/>.\n\t/// </summary>\n\t/// <returns>Returns <see cref=\"uiimage\"/> object containing the rectangle of the found image. On timeout returns <c>null</c> if <i>timeout</i> is negative; else exception.</returns>\n\t/// <param name=\"timeout\">Timeout, seconds. Can be 0 (infinite), >0 (exception) or &lt;0 (no exception). More info: [](xref:wait_timeout).</param>\n\t/// <exception cref=\"TimeoutException\"><i>timeout</i> time has expired (if > 0).</exception>\n\t/// <exception cref=\"AuWndException\">Invalid window handle (the <i>area</i> argument), or the window closed while waiting.</exception>\n\t/// <inheritdoc cref=\"find(IFArea, IFImage, IFFlags, int, Func{uiimage, IFAlso})\"/>\n\tpublic static uiimage wait(Seconds timeout, IFArea area, IFImage image, IFFlags flags = 0, int diff = 0, Func<uiimage, IFAlso> also = null)\n\t\t=> new uiimageFinder(image, flags, diff, also).Wait(timeout, area);\n\n\t/// <summary>\n\t/// Waits until image(s) or color(s) is not displayed in a window or other area.\n\t/// More info: <see cref=\"find\"/>.\n\t/// </summary>\n\t/// <returns>Returns <c>true</c>. On timeout returns <c>false</c> if <i>timeout</i> is negative; else exception.</returns>\n\t/// <inheritdoc cref=\"wait(Seconds, IFArea, IFImage, IFFlags, int, Func{uiimage, IFAlso})\"/>\n\tpublic static bool waitNot(Seconds timeout, IFArea area, IFImage image, IFFlags flags = 0, int diff = 0, Func<uiimage, IFAlso> also = null)\n\t\t=> new uiimageFinder(image, flags, diff, also).WaitNot(timeout, area);\n\n\t/// <summary>\n\t/// Waits until something visually changes in a window or other area.\n\t/// More info: <see cref=\"find\"/>.\n\t/// </summary>\n\t/// <remarks>\n\t/// Like <see cref=\"waitNot\"/>, but instead of <i>image</i> parameter this function captures the area image at the beginning.\n\t/// </remarks>\n\t/// <inheritdoc cref=\"waitNot(Seconds, IFArea, IFImage, IFFlags, int, Func{uiimage, IFAlso})\"/>\n\tpublic static bool waitChanged(Seconds timeout, IFArea area, IFFlags flags = 0, int diff = 0) {\n\t\tvar f = new uiimageFinder(default, flags, diff, null);\n\t\treturn f.Wait_(uiimageFinder.Action_.WaitChanged, timeout, area);\n\t}\n\n\t#region obsolete\n\n\t/// <inheritdoc cref=\"CaptureScreen.Image(RECT)\"/>\n\t[Obsolete(\"Use CaptureScreen.Image\"), EditorBrowsable(EditorBrowsableState.Never)]\n\tpublic static Bitmap capture(RECT r) => CaptureScreen.Image(r);\n\n\t/// <inheritdoc cref=\"CaptureScreen.Image(wnd, RECT?, CIFlags)\"/>\n\t[Obsolete(\"Use CaptureScreen.Image\"), EditorBrowsable(EditorBrowsableState.Never)]\n\tpublic static Bitmap capture(wnd w, RECT r, bool printWindow = false)\n\t\t=> CaptureScreen.Image(w, r, printWindow ? CIFlags.PrintWindow : CIFlags.WindowDC);\n\n\t/// <inheritdoc cref=\"CaptureScreen.Pixels(RECT)\"/>\n\t[Obsolete(\"Use CaptureScreen.Pixels\"), EditorBrowsable(EditorBrowsableState.Never)]\n\tpublic static uint[,] getPixels(RECT r) => CaptureScreen.Pixels(r);\n\n\t/// <inheritdoc cref=\"CaptureScreen.Pixels(wnd, RECT?, CIFlags)\"/>\n\t[Obsolete(\"Use CaptureScreen.Pixels\"), EditorBrowsable(EditorBrowsableState.Never)]\n\tpublic static uint[,] getPixels(wnd w, RECT r, bool printWindow = false)\n\t\t=> CaptureScreen.Pixels(w, r, printWindow ? CIFlags.PrintWindow : CIFlags.WindowDC);\n\n\t/// <inheritdoc cref=\"CaptureScreen.Pixel\"/>\n\t[Obsolete(\"Use CaptureScreen.Pixel\"), EditorBrowsable(EditorBrowsableState.Never)]\n\tpublic static unsafe uint getPixel(POINT p) => CaptureScreen.Pixel(p);\n\n\t/// <inheritdoc cref=\"CaptureScreen.ImageColorRectUI\"/>\n\t[Obsolete(\"Use CaptureScreen.ImageColorRectUI\"), EditorBrowsable(EditorBrowsableState.Never)]\n\tpublic static bool captureUI(out ICResult result, ICFlags flags = 0, AnyWnd owner = default) {\n\t\tbool R = CaptureScreen.ImageColorRectUI(out var r1, (CIUFlags)flags, owner);\n\t\tresult = Unsafe.As<ICResult>(r1);\n\t\treturn R;\n\t}\n\n\t#endregion\n}\n"
  },
  {
    "path": "Au/UI objects/uiimageFinder.cs",
    "content": "//FUTURE: option to allow % of image completely different. Eg button with/without focus rectangle.\n//\tOr/and in the tool allow to erase some areas (make alpha 0).\n\n//#define WI_TEST_NO_OPTIMIZATION\n\nusing System.Drawing;\nusing System.Drawing.Imaging;\n\nnamespace Au;\n\n/// <summary>\n/// Finds images displayed in user interface (UI). Contains data and parameters of image(s) or color(s) to find.\n/// </summary>\n/// <remarks>\n/// Can be used instead of <see cref=\"uiimage.find\"/>.\n/// </remarks>\npublic unsafe class uiimageFinder {\n\tclass _Image {\n\t\tpublic uint[] pixels;\n\t\tpublic int width, height;\n\t\tpublic _OptimizationData optim;\n\n\t\tpublic _Image(string file) {\n\t\t\tusing var b = ImageUtil.LoadGdipBitmap(file);\n\t\t\t_BitmapToData(b);\n\t\t}\n\n\t\tpublic _Image(Bitmap b) {\n\t\t\tb = b ?? throw new ArgumentException(\"null Bitmap\");\n\t\t\t_BitmapToData(b);\n\t\t}\n\n\t\tvoid _BitmapToData(Bitmap b) {\n\t\t\tvar z = b.Size;\n\t\t\twidth = z.Width; height = z.Height;\n\t\t\tpixels = new uint[width * height];\n\t\t\tfixed (uint* p = pixels) {\n\t\t\t\tvar d = new BitmapData { Scan0 = (IntPtr)p, Height = height, Width = width, Stride = width * 4, PixelFormat = PixelFormat.Format32bppArgb };\n\t\t\t\td = b.LockBits(new Rectangle(default, z), ImageLockMode.ReadOnly | ImageLockMode.UserInputBuffer, PixelFormat.Format32bppArgb, d);\n\t\t\t\tb.UnlockBits(d);\n\t\t\t\tif (d.Stride < 0) throw new ArgumentException(\"bottom-up Bitmap\"); //Image.FromHbitmap used to create bottom-up bitmap (stride<0) from compatible bitmap. Now cannot reproduce.\n\t\t\t}\n\t\t}\n\n\t\tpublic _Image(ColorInt color) {\n\t\t\twidth = height = 1;\n\t\t\tpixels = new uint[1] { (uint)color.argb | 0xff000000 };\n\t\t}\n\n\t\tpublic _Image() { }\n\t}\n\n\t//ctor parameters\n\treadonly List<_Image> _images; //support multiple images\n\treadonly IFFlags _flags;\n\treadonly uint _diff;\n\treadonly Func<uiimage, IFAlso> _also;\n\n\tAction_ _action;\n\tIFArea _area;\n\tCaptureScreenImage _ad; //area data\n\tPOINT _resultOffset; //to map the found rectangle from the captured area coordinates to the specified area coordinates\n\n\t/// <summary>\n\t/// Returns <see cref=\"uiimage\"/> object that contains the rectangle of the found image and can click it etc.\n\t/// </summary>\n\tpublic uiimage Result { get; private set; }\n\n\t/// <summary>\n\t/// Stores image/color data and search settings in this object. Loads images if need. See <see cref=\"uiimage.find\"/>.\n\t/// </summary>\n\t/// <exception cref=\"ArgumentException\">An argument is/contains a <c>null</c>/invalid value.</exception>\n\t/// <exception cref=\"FileNotFoundException\">Image file does not exist.</exception>\n\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"ImageUtil.LoadGdipBitmap\"/>.</exception>\n\t/// <inheritdoc cref=\"uiimage.find(IFArea, IFImage, IFFlags, int, Func{uiimage, IFAlso})\" path=\"/param\"/>\n\tpublic uiimageFinder(IFImage image, IFFlags flags = 0, int diff = 0, Func<uiimage, IFAlso> also = null) {\n\t\t_flags = flags;\n\t\tuint d = (uint)diff; _diff = d switch { <= 30 => d, <= 60 => 30 + (d - 30) * 2, <= 100 => 90 + (d - 60) * 3, _ => throw new ArgumentOutOfRangeException(\"diff range: 0 - 100\") }; //make slightly exponential, 0 - 210\n\t\t_also = also;\n\n\t\t_images = new List<_Image>();\n\t\tif (image.Value != null) _AddImage(image);\n\n\t\tvoid _AddImage(IFImage image) {\n\t\t\tswitch (image.Value) {\n\t\t\tcase string s:\n\t\t\t\t_images.Add(new _Image(s));\n\t\t\t\tbreak;\n\t\t\tcase Bitmap b:\n\t\t\t\t_images.Add(new _Image(b));\n\t\t\t\tbreak;\n\t\t\tcase ColorInt c:\n\t\t\t\t_images.Add(new _Image(c));\n\t\t\t\tbreak;\n\t\t\tcase IFImage[] a:\n\t\t\t\tforeach (var v in a) _AddImage(v);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t/// <summary>\n\t/// Finds the first image displayed in the specified window or other area.\n\t/// See <see cref=\"uiimage.find\"/>.\n\t/// </summary>\n\t/// <returns>If found, returns <see cref=\"Result\"/>, else <c>null</c>.</returns>\n\t/// <exception cref=\"AuWndException\">Invalid window handle.</exception>\n\t/// <exception cref=\"ArgumentException\">An argument of this function or of constructor is invalid.</exception>\n\t/// <exception cref=\"AuException\">Something failed.</exception>\n\t/// <remarks>\n\t/// Functions <c>Find</c> and <see cref=\"Exists\"/> differ only in their return types.\n\t/// </remarks>\n\t/// <inheritdoc cref=\"uiimage.find\" path=\"/param\"/>\n\tpublic uiimage Find(IFArea area) => Exists(area) ? Result : null;\n\n\t/// <summary>\n\t/// Finds the first image displayed in the specified window or other area. Can wait and throw <see cref=\"NotFoundException\"/>.\n\t/// </summary>\n\t/// <returns>If found, returns <see cref=\"Result\"/>. Else throws exception or returns <c>null</c> (if <i>wait</i> negative).</returns>\n\t/// <param name=\"wait\">The wait timeout, seconds. If 0, does not wait. If negative, does not throw <see cref=\"NotFoundException\"/>.</param>\n\t/// <exception cref=\"AuWndException\">Invalid window handle.</exception>\n\t/// <exception cref=\"ArgumentException\">An argument of this function or of constructor is invalid.</exception>\n\t/// <exception cref=\"AuException\">Something failed.</exception>\n\t/// <exception cref=\"NotFoundException\" />\n\t/// <remarks>\n\t/// Functions <c>Find</c> and <see cref=\"Exists\"/> differ only in their return types.\n\t/// </remarks>\n\t/// <inheritdoc cref=\"uiimage.find\" path=\"/param\"/>\n\tpublic uiimage Find(IFArea area, Seconds wait) => Exists(area, wait) ? Result : null;\n\n\t/// <returns>If found, sets <see cref=\"Result\"/> and returns <c>true</c>, else false.</returns>\n\t/// <inheritdoc cref=\"Find(IFArea)\"/>\n\tpublic bool Exists(IFArea area) {\n\t\t_Before(area, Action_.Find);\n\t\treturn _Find();\n\t}\n\n\t/// <returns>If found, sets <see cref=\"Result\"/> and returns <c>true</c>. Else throws exception or returns <c>false</c> (if <i>wait</i> negative).</returns>\n\t/// <inheritdoc cref=\"Find(IFArea, Seconds)\"/>\n\tpublic bool Exists(IFArea area, Seconds wait) {\n\t\tbool r = wait.Exists_() ? Exists(area) : Wait_(Action_.Wait, wait, area);\n\t\treturn r || wait.ReturnFalseOrThrowNotFound_();\n\t}\n\n\t/// <summary>\n\t/// See <see cref=\"uiimage.wait\"/>.\n\t/// </summary>\n\t/// <param name=\"timeout\">Timeout, seconds. Can be 0 (infinite), >0 (exception) or &lt;0 (no exception). More info: [](xref:wait_timeout).</param>\n\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"uiimage.wait\"/>, except those of the constructor.</exception>\n\t/// <remarks>\n\t/// Same as <see cref=\"Find(IFArea, Seconds)\"/>, except:\n\t/// - 0 timeout means infinite.\n\t/// - on timeout throws <see cref=\"TimeoutException\"/>, not <see cref=\"NotFoundException\"/>.\n\t/// </remarks>\n\t/// <inheritdoc cref=\"Find(IFArea, Seconds)\" path=\"/param\"/>\n\tpublic uiimage Wait(Seconds timeout, IFArea area)\n\t\t=> Wait_(Action_.Wait, timeout, area) ? Result : null;\n\t//TODO3: suspend waiting while a mouse button is pressed.\n\t//\tNow, eg if finds while scrolling, although MouseMove waits until buttons released, but moves to the old (wrong) place.\n\n\t/// <summary>\n\t/// See <see cref=\"uiimage.waitNot\"/>.\n\t/// </summary>\n\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"uiimage.waitNot\"/>, except those of the constructor.</exception>\n\t/// <inheritdoc cref=\"Wait(Seconds, IFArea)\" path=\"/param\"/>\n\tpublic bool WaitNot(Seconds timeout, IFArea area)\n\t\t=> Wait_(Action_.WaitNot, timeout, area);\n\n\tinternal bool Wait_(Action_ action, Seconds timeout, IFArea area) {\n\t\tif (area.Type == IFArea.AreaType.Bitmap) throw new ArgumentException(\"Bitmap and wait\");\n\t\t_Before(area, action);\n\t\ttry { return wait.until(timeout, () => _Find() ^ (action > Action_.Wait)); }\n\t\tfinally { _After(); }\n\n\t\t//tested: does not create garbage while waiting.\n\t}\n\n\tinternal enum Action_ { Find, Wait, WaitNot, WaitChanged }\n\n\t//called at the start of _Find and Wait_\n\tvoid _Before(IFArea area, Action_ action) {\n\t\tNot_.Null(area);\n\t\t_action = action;\n\t\t_area = area;\n\t\t_ad ??= new();\n\n\t\tif (_action == Action_.WaitChanged) {\n\t\t\tDebug.Assert(_images.Count == 0 && _also == null); //the first _Find will capture the area and add to _images\n\t\t} else {\n\t\t\tif (_images.Count == 0) throw new ArgumentException(\"no image\");\n\t\t}\n\n\t\t_area.Before_(_flags.HasAny(IFFlags.WindowDC | IFFlags.PrintWindow));\n\t}\n\n\t//called at the end of _Find (if not waiting) and Wait_\n\tvoid _After() {\n\t\t_ad.Dispose();\n\t\t_area = null;\n\t\tif (_action == Action_.WaitChanged) _images.Clear();\n\t}\n\n\tbool _Find() {\n\t\t//using var p1 = perf.local();\n\t\tResult = null;\n\n\t\tif (!_area.GetRect_(out var r, out _resultOffset, _flags)) return false;\n\n\t\t//If WaitChanged, first time just get area pixels into _images[0].\n\t\tif (_action == Action_.WaitChanged && _images.Count == 0) {\n\t\t\treturn _GetAreaPixels(r, true);\n\t\t}\n\n\t\t//Return false if all images are bigger than the search area.\n\t\tfor (int i = _images.Count; --i >= 0;) {\n\t\t\tvar v = _images[i];\n\t\t\tif (v.width <= r.Width && v.height <= r.Height) goto g1;\n\t\t}\n\t\treturn false; g1:\n\n\t\tBitmapData bitmapBD = null;\n\t\ttry {\n\t\t\t//Get area pixels.\n\t\t\tbool havePixels = _area.Type == IFArea.AreaType.Bitmap;\n\t\t\tif (havePixels) {\n\t\t\t\tvar pf = (_area.B.PixelFormat == PixelFormat.Format32bppArgb) ? PixelFormat.Format32bppArgb : PixelFormat.Format32bppRgb; //if possible, use PixelFormat of _area, to avoid conversion/copying. Both these formats are ok, we don't use alpha.\n\t\t\t\tbitmapBD = _area.B.LockBits(r, ImageLockMode.ReadOnly, pf);\n\t\t\t\tif (bitmapBD.Stride < 0) throw new ArgumentException(\"bottom-up Bitmap\");\n\t\t\t\t_ad.SetExternalData_((uint*)bitmapBD.Scan0, bitmapBD.Width, bitmapBD.Height);\n\t\t\t\t//note: don't support rect in Bitmap. LockBits does not copy bits if same pixelformat. Would need to create new Bitmap from that rect, or use stride when searching.\n\t\t\t} else {\n\t\t\t\thavePixels = _GetAreaPixels(r);\n\t\t\t}\n\t\t\t//p1.Next();\n\n\t\t\tif (havePixels) {\n\t\t\t\t//Find image(s) in area.\n\t\t\t\tuiimage alsoResult = null;\n\t\t\t\t_DpiScaling dpiScaling = null;\n\t\t\t\tif (_action == Action_.WaitChanged)\n\t\t\t\t\treturn _FindImage(0, ref alsoResult, ref dpiScaling);\n\t\t\t\tif (_flags.Has(IFFlags.Parallel) && _images.Count > 1) {\n\t\t\t\t\tParallel.For(0, _images.Count, (i, pls) => {\n\t\t\t\t\t\t_FindImage(i, ref alsoResult, ref dpiScaling, pls);\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\tfor (int i = 0; i < _images.Count; i++) {\n\t\t\t\t\t\tif (_FindImage(i, ref alsoResult, ref dpiScaling)) break;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tResult ??= alsoResult;\n\t\t\t\tif (Result != null) return true;\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}\n\t\tfinally {\n\t\t\tif (bitmapBD != null) _area.B.UnlockBits(bitmapBD);\n\t\t\tif (_action == Action_.Find) _After();\n\t\t}\n\t}\n\n\t[MethodImpl(MethodImplOptions.AggressiveOptimization)]\n\tbool _FindImage(int listIndex, ref uiimage alsoResult, ref _DpiScaling dpiScaling, ParallelLoopState pls = null) {\n\t\t//note: can run in multiple threads simultaneously. Don't modify this fields and ref params without lock.\n\n\t\tvar image = _images[listIndex];\n\t\tvar alsoAction = IFAlso.FindOtherOfList;\n\t\tint matchIndex = 0;\n\n\t\tint imageWidth = image.width, imageHeight = image.height;\n\t\tif (_ad.Width < imageWidth || _ad.Height < imageHeight) return false;\n\t\tfixed (uint* imagePixels = image.pixels) {\n\t\t\tuint* imagePixelsTo = imagePixels + imageWidth * imageHeight;\n\t\t\tuint* areaPixels = _ad.Pixels;\n\n\t\t\t//rejected: if image is of same size as area, simply compare. For example, when action is WaitChanged.\n\t\t\t//\tDoes not make faster, just adds more code.\n\n\t\t\tif (!image.optim.Init(image, _ad.Width)) return false;\n\t\t\tvar optim = image.optim; //copy struct, size = 9*int\n\t\t\tint o_pos0 = optim.v0.pos;\n\t\t\tvar o_a1 = &optim.v1; var o_an = o_a1 + (optim.N - 1);\n\n\t\t\t//find first pixel. This part is very important for speed.\n\t\t\t//int nTimesFound = 0; //debug\n\n\t\t\tvar areaWidthMinusImage = _ad.Width - imageWidth;\n\t\t\tvar pFirst = areaPixels + o_pos0;\n\t\t\tvar pLast = pFirst + _ad.Width * (_ad.Height - imageHeight) + areaWidthMinusImage;\n\n\t\t\t//this is a workaround for compiler not using registers for variables in fast loops (part 1)\n\t\t\tvar f = new _FindData {\n\t\t\t\tcolor = (optim.v0.color & 0xffffff) | (_diff << 24),\n\t\t\t\tp = pFirst - 1,\n\t\t\t\tpLineLast = pFirst + areaWidthMinusImage\n\t\t\t};\n\n\t\t\t#region fast_code\n\n\t\t\t//This for loop must be as fast as possible.\n\t\t\t//\tThere are too few 32-bit registers. Must be used a many as possible registers. See comments below.\n\t\t\t//\tNo problems if 64-bit.\n\n\t\t\tgContinue:\n\t\t\tif (pls?.IsStopped ?? false) goto gNotFound;\n\t\t\t{\n\t\t\t\tvar f_ = &f; //part 2 of the workaround\n\t\t\t\tvar p_ = f_->p + 1; //register\n\t\t\t\tvar color_ = f_->color; //register\n\t\t\t\tvar pLineLast_ = f_->pLineLast; //register\n\t\t\t\tfor (; ; ) { //lines\n\t\t\t\t\tif (color_ < 0x1000000) {\n\t\t\t\t\t\tfor (; p_ <= pLineLast_; p_++) {\n\t\t\t\t\t\t\tif (color_ == (*p_ & 0xffffff)) goto gPixelFound;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t//all variables except f.pLineLast are in registers\n\t\t\t\t\t\t//\tIt is very sensitive to other code. Compiler can take some registers for other code and not use here.\n\t\t\t\t\t\t//\tThen still not significantly slower, but I like to have full speed.\n\t\t\t\t\t\t//\tCode above fast_code region should not contain variables that are used in loops below this block.\n\t\t\t\t\t\t//\tAlso don't use class members in fast_code region, because then compiler may take a register for 'this' pointer.\n\t\t\t\t\t\t//\tHere we use f.pLineLast instead of pLineLast_, else d2_ would be in memory (it is used 3 times).\n\t\t\t\t\t\tvar d_ = color_ >> 24; //register\n\t\t\t\t\t\tvar d2_ = d_ * 2; //register\n\t\t\t\t\t\tfor (; p_ <= f.pLineLast; p_++) {\n\t\t\t\t\t\t\tif ((color_ & 0xff) - ((byte*)p_)[0] + d_ > d2_) continue;\n\t\t\t\t\t\t\tif ((color_ >> 8 & 0xff) - ((byte*)p_)[1] + d_ > d2_) continue;\n\t\t\t\t\t\t\tif ((color_ >> 16 & 0xff) - ((byte*)p_)[2] + d_ > d2_) continue;\n\t\t\t\t\t\t\tgoto gPixelFound;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (p_ > pLast) goto gNotFound;\n\t\t\t\t\tp_--; p_ += imageWidth;\n\t\t\t\t\tf.pLineLast = pLineLast_ = p_ + areaWidthMinusImage;\n\t\t\t\t}\n\t\t\t\tgPixelFound:\n\t\t\t\tf.p = p_;\n\t\t\t}\n\n\t\t\t//nTimesFound++;\n\t\t\tvar ap = f.p - o_pos0; //the first area pixel of the top-left of the image\n\n\t\t\t//compare other 0-3 selected pixels\n\t\t\tfor (var op = o_a1; op < o_an; op++) {\n\t\t\t\tuint aPix = ap[op->pos], iPix = op->color;\n\t\t\t\tvar colorDiff = f.color >> 24;\n\t\t\t\tif (colorDiff == 0) {\n\t\t\t\t\tif (!_MatchPixelExact(aPix, iPix)) goto gContinue;\n\t\t\t\t} else {\n\t\t\t\t\tif (!_MatchPixelDiff(aPix, iPix, colorDiff)) goto gContinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t//now compare all pixels of the image\n\t\t\t//perf.first();\n\t\t\tuint* ip = imagePixels, ipLineTo = ip + imageWidth;\n\t\t\tfor (; ; ) { //lines\n\t\t\t\tif (f.color < 0x1000000) {\n\t\t\t\t\tdo {\n\t\t\t\t\t\tif (!_MatchPixelExact(*ap, *ip)) goto gContinue;\n\t\t\t\t\t\tap++;\n\t\t\t\t\t}\n\t\t\t\t\twhile (++ip < ipLineTo);\n\t\t\t\t} else {\n\t\t\t\t\tvar colorDiff = f.color >> 24;\n\t\t\t\t\tdo {\n\t\t\t\t\t\tif (!_MatchPixelDiff(*ap, *ip, colorDiff)) goto gContinue;\n\t\t\t\t\t\tap++;\n\t\t\t\t\t}\n\t\t\t\t\twhile (++ip < ipLineTo);\n\t\t\t\t}\n\t\t\t\tif (ip == imagePixelsTo) break;\n\t\t\t\tap += areaWidthMinusImage;\n\t\t\t\tipLineTo += imageWidth;\n\t\t\t}\n\t\t\t//perf.nw();\n\t\t\t//print.it(nTimesFound);\n\n\t\t\t#endregion\n\n\t\t\tif (_action != Action_.WaitChanged) {\n\t\t\t\tint iFound = (int)(f.p - o_pos0 - areaPixels);\n\t\t\t\tRECT r = new(iFound % _ad.Width, iFound / _ad.Width, imageWidth, imageHeight);\n\n\t\t\t\tlock (this) {\n\t\t\t\t\tif (pls?.IsStopped ?? false) goto gNotFound;\n\n\t\t\t\t\tif (_flags.HasAny(IFFlags.WindowDC | IFFlags.PrintWindow)) {\n\t\t\t\t\t\tdpiScaling ??= new(_area.W);\n\t\t\t\t\t\tdpiScaling.ScaleRect(ref r);\n\t\t\t\t\t}\n\t\t\t\t\tr.Offset(_resultOffset.x, _resultOffset.y);\n\n\t\t\t\t\tuiimage tempResult = new(_area) { Rect = r, MatchIndex = matchIndex, ListIndex = listIndex };\n\n\t\t\t\t\tif (_also != null) {\n\t\t\t\t\t\talsoAction = _also(tempResult);\n\t\t\t\t\t\tif (alsoAction is IFAlso.OkFindMoreOfThis or IFAlso.FindOtherOfThis && pls != null) {\n\t\t\t\t\t\t\t//stop other threads, but not this thread\n\t\t\t\t\t\t\tpls.Stop();\n\t\t\t\t\t\t\tpls = null;\n\t\t\t\t\t\t\t//never mind: if later _also returns \"continue to search in list\" (unlikely), will not search.\n\t\t\t\t\t\t\t//\tpls.Stop must be called while locked, to prevent other threads calling _also afterwards.\n\t\t\t\t\t\t\t//\tNow using the simple way (lock).\n\t\t\t\t\t\t\t//\tThe complex way - use Monitor.Enter/Exit. Call Stop/Exit when returning. Bad: other threads would continue to search unnecessarily.\n\t\t\t\t\t\t}\n\t\t\t\t\t\tswitch (alsoAction) {\n\t\t\t\t\t\tcase IFAlso.OkFindMore or IFAlso.OkFindMoreOfThis:\n\t\t\t\t\t\t\talsoResult = tempResult;\n\t\t\t\t\t\t\tmatchIndex++;\n\t\t\t\t\t\t\tgoto gContinue;\n\t\t\t\t\t\tcase IFAlso.FindOther or IFAlso.FindOtherOfThis:\n\t\t\t\t\t\t\tmatchIndex++;\n\t\t\t\t\t\t\tgoto gContinue;\n\t\t\t\t\t\tcase IFAlso.OkFindMoreOfList:\n\t\t\t\t\t\t\talsoResult = tempResult;\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\tcase IFAlso.FindOtherOfList:\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (alsoAction != IFAlso.NotFound) Result = tempResult;\n\t\t\t\t\tpls?.Stop();\n\t\t\t\t}\n\t\t\t}\n\t\t} //fixed\n\n\t\treturn true;\n\t\tgNotFound:\n\t\treturn alsoAction is IFAlso.FindOtherOfThis or IFAlso.OkFindMoreOfThis;\n\n\t\t//returns true to stop seaching (skip other images in list)\n\t}\n\n\tstruct _FindData {\n\t\tpublic uint color;\n\t\tpublic uint* p, pLineLast;\n\t}\n\n\t[MethodImpl(MethodImplOptions.AggressiveInlining)]\n\tstatic bool _MatchPixelExact(uint ap, uint ip) {\n\t\tif (ip == (ap | 0xff000000)) return true;\n\t\treturn ip < 0xff000000; //transparent?\n\t}\n\n\t[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]\n\tstatic bool _MatchPixelDiff(uint ap, uint ip, uint colorDiff) {\n\t\t//info: optimized. Don't modify.\n\t\t//\tAll variables are in registers.\n\t\t//\tOnly 3.5 times slower than _MatchPixelExact (when all pixels match), which is inline.\n\n\t\tif (ip >= 0xff000000) { //else transparent\n\t\t\tuint d = colorDiff, d2 = d * 2;\n\t\t\tif (((ip & 0xff) - (ap & 0xff) + d) > d2) goto gFalse;\n\t\t\tif (((ip >> 8 & 0xff) - (ap >> 8 & 0xff) + d) > d2) goto gFalse;\n\t\t\tif (((ip >> 16 & 0xff) - (ap >> 16 & 0xff) + d) > d2) goto gFalse;\n\t\t}\n\t\treturn true;\n\t\tgFalse:\n\t\treturn false;\n\t}\n\n\t//bool _CompareSameSize(uint* area, uint* image, uint* imageTo, uint colorDiff)\n\t//{\n\t//\tif(colorDiff == 0) {\n\t//\t\tdo {\n\t//\t\t\tif(!_MatchPixelExact(*area, *image)) break;\n\t//\t\t\tarea++;\n\t//\t\t} while(++image < imageTo);\n\t//\t} else {\n\t//\t\tdo {\n\t//\t\t\tif(!_MatchPixelDiff(*area, *image, colorDiff)) break;\n\t//\t\t\tarea++;\n\t//\t\t} while(++image < imageTo);\n\t//\t}\n\t//\treturn image == imageTo;\n\t//}\n\n\tstatic bool _IsTransparent(uint color) => color < 0xff000000;\n\n\tstruct _OptimizationData {\n\t\tinternal struct POSCOLOR {\n\t\t\tpublic int pos; //the position in area (not in image) from which to start searching. Depends on where in the image is the color.\n\t\t\tpublic uint color;\n\t\t};\n\n#pragma warning disable 649 //never assigned\n\t\tpublic POSCOLOR v0, v1, v2, v3; //POSCOLOR[] would be slower\n#pragma warning restore 649\n\t\tpublic int N; //A valid count\n\t\tint _areaWidth;\n\n\t\tpublic bool Init(_Image image, int areaWidth) {\n\t\t\tif (areaWidth != _areaWidth) { _areaWidth = areaWidth; N = 0; }\n\t\t\tif (N != 0) return N > 0;\n\n\t\t\tint imageWidth = image.width, imageHeight = image.height;\n\t\t\tint imagePixelCount = imageWidth * imageHeight;\n\t\t\tvar imagePixels = image.pixels;\n\t\t\tint i;\n\n#if WI_TEST_NO_OPTIMIZATION\n\t\t\t_Add(image, 0, areaWidth);\n#else\n\n\t\t\t//Find several unique-color pixels for first-pixel search.\n\t\t\t//This greatly reduces the search time in most cases.\n\n\t\t\t//find first nontransparent pixel\n\t\t\tfor (i = 0; i < imagePixelCount; i++) if (!_IsTransparent(imagePixels[i])) break;\n\t\t\tif (i == imagePixelCount) { N = -1; return false; } //not found because all pixels in image are transparent\n\n\t\t\t//find first nonbackground pixel (consider top-left pixel is background)\n\t\t\tbool singleColor = false;\n\t\t\tif (i == 0) {\n\t\t\t\ti = _FindDifferentPixel(0);\n\t\t\t\tif (i < 0) { singleColor = true; i = 0; }\n\t\t\t}\n\n\t\t\t_Add(image, i, areaWidth);\n\t\t\tif (!singleColor) {\n\t\t\t\t//find second different pixel\n\t\t\t\tint i0 = i;\n\t\t\t\ti = _FindDifferentPixel(i);\n\t\t\t\tif (i >= 0) {\n\t\t\t\t\t_Add(image, i, areaWidth);\n\t\t\t\t\t//find other different pixels\n\t\t\t\t\tfixed (POSCOLOR* p = &v0) {\n\t\t\t\t\t\twhile (N < 4) {\n\t\t\t\t\t\t\tfor (++i; i < imagePixelCount; i++) {\n\t\t\t\t\t\t\t\tvar c = imagePixels[i];\n\t\t\t\t\t\t\t\tif (_IsTransparent(c)) continue;\n\t\t\t\t\t\t\t\tint j = N - 1;\n\t\t\t\t\t\t\t\tfor (; j >= 0; j--) if (c == p[j].color) break; //find new color\n\t\t\t\t\t\t\t\tif (j < 0) break; //found\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (i >= imagePixelCount) break;\n\t\t\t\t\t\t\t_Add(image, i, areaWidth);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tfor (i = imagePixelCount - 1; i > i0; i--) if (!_IsTransparent(imagePixels[i])) break;\n\t\t\t\t\t_Add(image, i, areaWidth);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t//fixed (POSCOLOR* o_pc = &v0) for(int j = 0; j < N; j++) print.it($\"{o_pc[j].pos} 0x{o_pc[j].color:X}\");\n#endif\n\t\t\treturn true;\n\n\t\t\tint _FindDifferentPixel(int iCurrent) {\n\t\t\t\tint m = iCurrent, n = imagePixelCount;\n\t\t\t\tuint notColor = imagePixels[m++];\n\t\t\t\tfor (; m < n; m++) {\n\t\t\t\t\tvar c = imagePixels[m];\n\t\t\t\t\tif (c == notColor || _IsTransparent(c)) continue;\n\t\t\t\t\treturn m;\n\t\t\t\t}\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t}\n\n\t\tvoid _Add(_Image image, int i, int areaWidth) {\n\t\t\tfixed (POSCOLOR* p0 = &v0) {\n\t\t\t\tvar p = p0 + N++;\n\t\t\t\tp->color = image.pixels[i];\n\t\t\t\tint w = image.width, x = i % w, y = i / w;\n\t\t\t\tp->pos = y * areaWidth + x;\n\t\t\t}\n\t\t}\n\t}\n\n\tbool _GetAreaPixels(RECT r, bool toImage0 = false) {\n\t\t//Transfer from screen/window DC to memory DC (does not work without this) and get pixels.\n\t\t//This is the slowest part of Find, especially BitBlt.\n\t\t//Speed depends on computer, driver, OS version, theme, size.\n\t\t//For example, with Aero theme 2-15 times slower (on Windows 8/10 cannot disable Aero).\n\t\t//With incorrect/generic video driver can be 10 times slower. Eg on vmware virtual PC.\n\t\t//Much faster when using window DC. Then same speed as without Aero.\n\n\t\t_ad.DontSetAlpha_ = !toImage0;\n\t\tbool ok = _area.Type == IFArea.AreaType.Screen\n\t\t\t? _ad.Capture(r, relaxed: true)\n\t\t\t: _ad.Capture(_area.W, r, _flags.ToCIFlags_() | CIFlags.Relaxed);\n\t\tif (!ok) return false; //r not in client area. Probably the window resized since r.Intersect(clientArea).\n\n\t\tif (toImage0) {\n\t\t\tvar im = new _Image { width = r.Width, height = r.Height, pixels = _ad.ToArray1D() };\n\t\t\t_images.Add(im);\n\t\t}\n\n\t\treturn true;\n\t}\n\n#if false\n\t//r is relative to the search area\n\tvoid _DpiScaleRect(ref RECT r) {\n\t\tif (!_flags.HasAny(IFFlags.WindowDC | IFFlags.PrintWindow)) return;\n\t\tvar w = _area.W.Window;\n\t\tif (!Dpi.IsWindowVirtualizedWin10_(w)) return; //makes faster on Win10+; don't scale on older OS\n\t\tint d1 = screen.of(w).Dpi, d2 = Dpi.OfWindow(w);\n\t\tr.left = Math2.MulDiv(r.left, d1, d2);\n\t\tr.top = Math2.MulDiv(r.top, d1, d2);\n\t\tr.right = Math2.MulDiv(r.right, d1, d2);\n\t\tr.bottom = Math2.MulDiv(r.bottom, d1, d2);\n\t}\n#else\n\t//cache DPI scaling info. Getting it can make much slower if many matches found. Above is the non-cached version.\n\tclass _DpiScaling {\n\t\twnd _w;\n\t\tushort _dScreen, _dWindow;\n\t\tbool _inited, _scaled;\n\n\t\tpublic _DpiScaling(wnd w) { _w = w; }\n\n\t\t//r is relative to the search area\n\t\tpublic void ScaleRect(ref RECT r) {\n\t\t\tif (!_inited) {\n\t\t\t\t_inited = true;\n\t\t\t\tif (_scaled = Dpi.IsWindowVirtualizedWin10_(_w = _w.Window)) {\n\t\t\t\t\t_dScreen = (ushort)screen.of(_w).Dpi;\n\t\t\t\t\t_dWindow = (ushort)Dpi.OfWindow(_w);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (_scaled) {\n\t\t\t\tr.left = Math2.MulDiv(r.left, _dScreen, _dWindow);\n\t\t\t\tr.top = Math2.MulDiv(r.top, _dScreen, _dWindow);\n\t\t\t\tr.right = Math2.MulDiv(r.right, _dScreen, _dWindow);\n\t\t\t\tr.bottom = Math2.MulDiv(r.bottom, _dScreen, _dWindow);\n\t\t\t}\n\t\t}\n\t}\n#endif\n}\n"
  },
  {
    "path": "Au/UI objects/uiimage_types.cs",
    "content": "using System.Drawing;\n\nnamespace Au.Types;\n\n#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member\n/// <summary>\n/// Defines the search area for <see cref=\"uiimage.find\"/> and similar functions.\n/// </summary>\n/// <remarks>\n/// It can be a window/control, UI element, image or a rectangle in screen.\n/// Has implicit conversions from <see cref=\"wnd\"/>, <see cref=\"elm\"/>, <see cref=\"Bitmap\"/> and <see cref=\"RECT\"/> (rectangle in screen).\n/// Constructors can be used to specify a rectangle in window or UI element, which makes the area smaller and the function faster.\n/// Example: <c>uiimage.find(new(w, (left, top, width, height)), image);</c>.\n/// </remarks>\npublic class IFArea {\n\tinternal enum AreaType : byte { Screen, Wnd, Elm, Bitmap }\n\n\tinternal AreaType Type;\n\treadonly bool _hasRect, _hasCoord;\n\tinternal wnd W;\n\tinternal elm E;\n\tinternal Bitmap B;\n\tRECT _r;\n\treadonly Coord _cLeft, _cTop, _cRight, _cBottom;\n\n\tIFArea(AreaType t) { Type = t; }\n\n\t/// <summary>Specifies a window or control and a rectangle in its client area.</summary>\n\tpublic IFArea(wnd w, RECT r) { Type = AreaType.Wnd; W = w; _r = r; _hasRect = true; }\n\n\t/// <summary>Specifies a UI element and a rectangle in it.</summary>\n\tpublic IFArea(elm e, RECT r) { Type = AreaType.Elm; E = e; _r = r; _hasRect = true; }\n\n\t//rejected. Rare etc.\n\t//public IFArea(Bitmap b, RECT r) { ... }\n\n\t/// <summary>\n\t/// Specifies a window or control and a rectangle in its client area.\n\t/// The parameters are of <see cref=\"Coord\"/> type, therefore can be easily specified reverse and fractional coordinates, like <c>^10</c> and <c>.5f</c>. Use <c>^0</c> for right or bottom edge.\n\t/// </summary>\n\tpublic IFArea(wnd w, Coord left, Coord top, Coord right, Coord bottom) {\n\t\tType = AreaType.Wnd;\n\t\tW = w;\n\t\t_cLeft = left;\n\t\t_cTop = top;\n\t\t_cRight = right;\n\t\t_cBottom = bottom;\n\t\t_hasRect = _hasCoord = true;\n\t}\n\n\t/// <summary>\n\t/// Specifies a UI element and a rectangle in it.\n\t/// The parameters are of <see cref=\"Coord\"/> type, therefore can be easily specified reverse and fractional coordinates, like <c>^10</c> and <c>.5f</c>. Use <c>^0</c> for right or bottom edge.\n\t/// </summary>\n\tpublic IFArea(elm e, Coord left, Coord top, Coord right, Coord bottom) {\n\t\tType = AreaType.Elm;\n\t\tE = e;\n\t\t_cLeft = left;\n\t\t_cTop = top;\n\t\t_cRight = right;\n\t\t_cBottom = bottom;\n\t\t_hasRect = _hasCoord = true;\n\t}\n\n\tpublic static implicit operator IFArea(wnd w) => new(AreaType.Wnd) { W = w };\n\tpublic static implicit operator IFArea(elm e) => new(AreaType.Elm) { E = e };\n\tpublic static implicit operator IFArea(Bitmap b) => new(AreaType.Bitmap) { B = b };\n\tpublic static implicit operator IFArea(RECT r) => new(AreaType.Screen) { _r = r };\n\n\tinternal void Before_(bool windowPixels) {\n\t\tif (windowPixels && Type is AreaType.Screen or AreaType.Bitmap) throw new ArgumentException(\"Invalid flags for this area type\");\n\n\t\tswitch (Type) {\n\t\tcase AreaType.Wnd:\n\t\t\tW.ThrowIfInvalid();\n\t\t\tbreak;\n\t\tcase AreaType.Elm:\n\t\t\tif (E == null) throw new ArgumentNullException(\"area elm\");\n\t\t\tW = E.WndContainer;\n\t\t\tgoto case AreaType.Wnd;\n\t\tcase AreaType.Bitmap:\n\t\t\tif (B == null) throw new ArgumentNullException(\"area Bitmap\");\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tinternal bool GetRect_(out RECT r, out POINT resultOffset, IFFlags flags) {\n\t\tr = default;\n\t\tresultOffset = default;\n\n\t\tif (!W.Is0) {\n\t\t\tif (!W.IsVisible || W.IsMinimized) return false;\n\t\t\tif (!flags.HasAny(IFFlags.WindowDC | IFFlags.PrintWindow) && W.IsCloaked) return false;\n\t\t}\n\n\t\t//Get area rectangle.\n\t\tbool failed = false;\n\t\tswitch (Type) {\n\t\tcase AreaType.Wnd:\n\t\t\tfailed = !W.GetClientRect(out r);\n\t\t\tbreak;\n\t\tcase AreaType.Elm:\n\t\t\tfailed = !E.GetRect(out r, W);\n\t\t\tbreak;\n\t\tcase AreaType.Bitmap:\n\t\t\tr = new RECT(0, 0, B.Width, B.Height);\n\t\t\tbreak;\n\t\tdefault: //Screen\n\t\t\tr = _r;\n\t\t\tif (!screen.isInAnyScreen(r)) r = default;\n\t\t\tresultOffset.x = r.left; resultOffset.y = r.top;\n\t\t\tbreak;\n\t\t}\n\t\tif (failed) {\n\t\t\tW.ThrowIfInvalid();\n\t\t\tthrow new AuException(\"*get rectangle\");\n\t\t}\n\n\t\t//r is the area from where to get pixels. If Wnd or Elm, it is relative to W client area.\n\t\t//Intermediate results will be relative to r. Then will be added resultOffset if need.\n\n\t\tif (_hasRect) {\n\t\t\tRECT rr;\n\t\t\tif (_hasCoord) {\n\t\t\t\tRECT rc = new(0, 0, r.Width, r.Height);\n\t\t\t\tPOINT p1 = Coord.NormalizeInRect(_cLeft, _cTop, rc), p2 = Coord.NormalizeInRect(_cRight, _cBottom, rc);\n\t\t\t\trr = RECT.FromLTRB(p1.x, p1.y, p2.x, p2.y);\n\t\t\t} else {\n\t\t\t\trr = _r;\n\t\t\t}\n\t\t\tresultOffset.x = rr.left; resultOffset.y = rr.top;\n\t\t\trr.Offset(r.left, r.top);\n\t\t\tr.Intersect(rr);\n\t\t}\n\n\t\tif (Type == AreaType.Elm) {\n\t\t\t//adjust r and resultOffset,\n\t\t\t//\tbecause object rectangle may be bigger than client area (eg WINDOW object)\n\t\t\t//\tor its part is not in client area (eg scrolled web page).\n\t\t\t//\tIf not adjusted, then may capture part of parent or sibling controls or even other windows...\n\t\t\t//\tNever mind: should also adjust control rectangle in ancestors in the same way.\n\t\t\t//\t\tThis is not so important because usually whole control is visible (resized, not clipped).\n\t\t\tint x = r.left, y = r.top;\n\t\t\tW.GetClientRect(out var rw);\n\t\t\tr.Intersect(rw);\n\t\t\tx -= r.left; y -= r.top;\n\t\t\tresultOffset.x -= x; resultOffset.y -= y;\n\t\t}\n\n\t\treturn !r.NoArea; //never mind: if WaitChanged and this is the first time, immediately returns 'changed'\n\t}\n\n\t/// <summary>\n\t/// Calls <c>GetRect_</c>, and <c>CaptureScreen.Image</c> if need.\n\t/// </summary>\n\t/// <returns><c>false</c> if <c>GetRect_</c> returns <c>false</c> (empty rectangle).</returns>\n\tinternal bool GetOcrData_(OcrFlags flags, out Bitmap b, out POINT resultOffset) {\n\t\tif (Type == AreaType.Bitmap) { b = B; resultOffset = default; return true; }\n\t\tif (!GetRect_(out RECT r, out resultOffset, (IFFlags)flags)) { b = null; return false; }\n\t\tb = Type == AreaType.Screen ? CaptureScreen.Image(r) : CaptureScreen.Image(W, r, flags.ToCIFlags_());\n\t\treturn true;\n\t}\n\n\t/// <summary>\n\t/// If <i>scale</i>!=0, returns <i>scale</i>. Else returns 1...2 depending on <i>engine</i> and <i>area</i> DPI.\n\t/// </summary>\n\tinternal double GetOcrScale_(double scale, IOcrEngine engine) {\n\t\tif (scale != 0) return scale;\n\t\tif (engine.DpiScale) {\n\t\t\tint dpi = Type switch {\n\t\t\t\tAreaType.Wnd or AreaType.Elm => Dpi.OfWindow(W),\n\t\t\t\tAreaType.Screen => screen.of(_r).Dpi,\n\t\t\t\t_ => 200\n\t\t\t};\n\t\t\tif (dpi < 192) return 192d / dpi;\n\t\t}\n\t\treturn 1d;\n\t}\n}\n\n/// <summary>\n/// Image(s) or color(s) for <see cref=\"uiimage.find\"/> and similar functions.\n/// </summary>\n/// <remarks>\n/// Has implicit conversions from:\n/// - string - path of <c>.png</c> or <c>.bmp</c> file. If not full path, uses <see cref=\"folders.ThisAppImages\"/>.\n/// - string that starts with <c>\"resources/\"</c> or has prefix <c>\"resource:\"</c> - resource name; see <see cref=\"ResourceUtil.GetGdipBitmap\"/>.\n/// - string with prefix <c>\"image:\"</c> - Base64 encoded <c>.png</c> image.\\\n///   Can be created with tool <b>Find image or color in window</b> or with function <c>Au.Controls.KImageUtil.ImageToString</c> (in <c>Au.Controls.dll</c>).\n/// - <see cref=\"ColorInt\"/>, <c>int</c> or <c>uint</c> in <c>0xRRGGBB</c> color format, <see cref=\"Color\"/> - color. Alpha isn't used.\n/// - <see cref=\"Bitmap\"/> - image object.\n/// - <c>IFImage[]</c> - multiple images or/and colors. Action - find any. To create a different action can be used callback function (parameter <i>also</i>).\n/// \n/// Icons are not supported directly; you can use <see cref=\"icon\"/> to get icon and convert to bitmap.\n/// </remarks>\npublic struct IFImage {\n\treadonly object _o;\n\tIFImage(object o) { _o = o; }\n\n\tpublic static implicit operator IFImage(string pathEtc) => new(pathEtc);\n\tpublic static implicit operator IFImage(Bitmap image) => new(image);\n\tpublic static implicit operator IFImage(ColorInt color) => new(color);\n\tpublic static implicit operator IFImage(int color) => new((ColorInt)color);\n\tpublic static implicit operator IFImage(uint color) => new((ColorInt)color);\n\tpublic static implicit operator IFImage(Color color) => new((ColorInt)color);\n\tpublic static implicit operator IFImage(System.Windows.Media.Color color) => new((ColorInt)color);\n\t//public static implicit operator IFImage(IEnumerable<IFImage> list) => new(list); //error: cannot convert from interfaces\n\tpublic static implicit operator IFImage(IFImage[] list) => new(list);\n\t//public static implicit operator IFImage(List<IFImage> list) => new(list); //rare, can use ToArray()\n\n\t/// <summary>\n\t/// Gets the raw value stored in this variable. Can be <c>string</c>, <see cref=\"Bitmap\"/>, <see cref=\"ColorInt\"/>, <see cref=\"IFImage\"/><c>[]</c>, <c>null</c>.\n\t/// </summary>\n\tpublic object Value => _o;\n}\n#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member\n\n/// <summary>\n/// Flags for <see cref=\"uiimage.find\"/> and similar functions.\n/// </summary>\n[Flags]\npublic enum IFFlags {\n\t/// <summary>\n\t/// Get pixels from the device context (DC) of the window client area, not from screen DC. Usually much faster.\n\t/// Can get pixels from window parts that are covered by other windows or offscreen. But not from hidden and minimized windows.\n\t/// Does not work on Windows 7 if Aero theme is turned off. Then this flag is ignored.\n\t/// Cannot find images in some windows (including Windows Store apps), and in some window parts (glass). All pixels captured from these windows/parts are black.\n\t/// If the window is partially or completely transparent, the image must be captured from its non-transparent version.\n\t/// If the window is DPI-scaled, the image must be captured from its non-scaled version. However if used a limiting rectangle, it must contain scaled coordinates.\n\t/// </summary>\n\tWindowDC = 1,\n\n\t/// <summary>\n\t/// Use API <ms>PrintWindow</ms> to get window pixels.\n\t/// Like <c>WindowDC</c>, works with background windows, etc. Differences:\n\t/// <br/>• On Windows 8.1 and later works with windows where <c>WindowDC</c> doesn't.\n\t/// <br/>• Works without Aero theme too.\n\t/// <br/>• Slower.\n\t/// <br/>• Some windows flicker.\n\t/// <br/>• From some controls randomly gets partial image (API bug).\n\t/// <br/>• Does not work with windows of higher [](xref:uac) integrity level (throws exception).\n\t/// <br/>• Unreliable with DPI-scaled windows; the window image slightly changes when resizing etc.\n\t/// </summary>\n\tPrintWindow = 2,\n\t//rejected, makes no sense: /// <br/>• If used together with flag <c>WindowDC</c>, calls <c>PrintWindow</c> without flag <c>PW_RENDERFULLCONTENT</c>. Faster, less flickering, no bugs, but has the <c>WindowDC</c> problem: can't get pixels from some windows or window parts.\n\n\t///// <summary>\n\t///// Use DWM thumbnail API to get window pixels.\n\t///// Like <c>WindowDC</c>, works with background windows, etc. Differences:\n\t///// <br/>• Works with windows where <c>WindowDC</c> doesn't.\n\t///// <br/>• Requires Windows 10 or later. Exception if used on older OS.\n\t///// <br/>• Slower.\n\t///// <br/>• May not work with some windows (rare).\n\t///// </summary>\n\t//WindowDwm = 4,\n\n\t//note: the above values must be the same in CIFlags, CIUFlags, IFFlags, OcrFlags.\n\n\t/// <summary>\n\t/// This flag can make the function faster when <i>image</i> is a list of images. To search for each image, the function will use <see cref=\"Parallel.For\"/> instead of <c>for</c>. For example, if the CPU has 4 cores (8 threads), can search for max 8 images simultaneously. However it does not mean it will be 8 times faster. Can be max 2 or 3 times faster, depending on the number of images, flag <c>WindowDC</c>, <i>diff</i>, <i>also</i>, CPU, RAM, area size, finds or not, image position, etc. Can be even slower. To measure speed, use <see cref=\"perf\"/>.\n\t/// If used <i>also</i> callback function, it runs in any thread and any order, but one at a time (inside <c>lock() { }</c>).\n\t/// </summary>\n\tParallel = 0x100\n\n\t//rejected: this was used in QM2. Now can use png alpha instead.\n\t///// <summary>\n\t///// Use the top-left pixel color of the image as transparent color (don't compare pixels that have this color).\n\t///// </summary>\n\t//MakeTransparent = ,\n}\n\n/// <summary>\n/// Used with <see cref=\"uiimage.find\"/> and <see cref=\"uiimage.wait\"/>. Its callback function (parameter <i>also</i>) can return one of these values.\n/// </summary>\npublic enum IFAlso {\n\t/// <summary>\n\t/// Stop searching.\n\t/// Let the main function return current result.\n\t/// </summary>\n\tOkReturn,\n\n\t/// <summary>\n\t/// Find more instances of current image. If used list of images, also search for other images.\n\t/// Then let the main function return current result.\n\t/// </summary>\n\tOkFindMore,\n\n\t/// <summary>\n\t/// Find more instances of current image. When used list of images, don't search for other images.\n\t/// Then let the main function return current result.\n\t/// </summary>\n\tOkFindMoreOfThis,\n\n\t/// <summary>\n\t/// If used list of images, search for other images. Don't search for more instances of current image.\n\t/// Then let the main function return current result.\n\t/// </summary>\n\tOkFindMoreOfList,\n\n\t/// <summary>\n\t/// Stop searching.\n\t/// Let the main function return <c>null</c> or throw exception or continue waiting. But if a <c>OkFindX</c> value used previously, return that result.\n\t/// </summary>\n\tNotFound,\n\n\t/// <summary>\n\t/// Find more instances of current image. If used list of images, also search for other images.\n\t/// If not found, let the main function return <c>null</c> or throw exception or continue waiting; but if a <c>OkFindX</c> value used previously, return that result.\n\t/// </summary>\n\tFindOther,\n\n\t/// <summary>\n\t/// Find more instances of current image. When used list of images, don't search for other images.\n\t/// If not found, let the main function return <c>null</c> or throw exception or continue waiting; but if a <c>OkFindX</c> value used previously, return that result.\n\t/// </summary>\n\tFindOtherOfThis,\n\n\t/// <summary>\n\t/// If used list of images, search for other images. Don't search for more instances of current image.\n\t/// If not found, let the main function return <c>null</c> or throw exception or continue waiting; but if a <c>OkFindX</c> value used previously, return that result.\n\t/// </summary>\n\tFindOtherOfList,\n}\n"
  },
  {
    "path": "Au/resources/global2.cs",
    "content": "//This file is used by several projects: Au, Au.Controls, Au.Editor.\n\nglobal using Au;\nglobal using Au.Types;\nglobal using Au.More;\nglobal using System;\nglobal using System.Collections.Generic;\nglobal using System.Collections.Concurrent;\nglobal using System.Linq;\nglobal using System.Text;\nglobal using System.Diagnostics;\nglobal using System.Runtime.CompilerServices;\nglobal using System.Runtime.InteropServices;\nglobal using System.IO;\nglobal using System.Threading;\nglobal using System.Threading.Tasks;\nglobal using System.Reflection;\nglobal using System.Globalization;\n\nglobal using SystemInformation = System.Windows.Forms.SystemInformation;\nglobal using RStr = System.ReadOnlySpan<char>;\nglobal using RByte = System.ReadOnlySpan<byte>;\nglobal using System.ComponentModel;\nglobal using IEnumerable = System.Collections.IEnumerable;\nglobal using IEnumerator = System.Collections.IEnumerator;\n\n[module: DefaultCharSet(CharSet.Unicode)]\n[assembly: ComVisible(false)]\n\n[assembly: AssemblyCompany(\"Gintaras Didžgalvis\")]\n[assembly: AssemblyProduct(\"LibreAutomate\")]\n[assembly: AssemblyCopyright(\"Copyright 2020-2026 Gintaras Didžgalvis\")]\n[assembly: AssemblyCulture(\"\")]\n\n[assembly: AssemblyVersion(Au_.Version)]\n\n#if AU\nnamespace Au.More;\n\n///\n[EditorBrowsable(EditorBrowsableState.Never)]\npublic class Au_ {\n\t///\n\tpublic const string Version = \"1.15.0\";\n}\n#endif\n"
  },
  {
    "path": "Au/wnd/WProp.cs",
    "content": "namespace Au.Types\n{\n\t/// <summary>\n\t/// Sets, gets, removes and lists window properties using API <ms>SetProp</ms> and co.\n\t/// </summary>\n\tpublic struct WProp\n\t{\n\t\treadonly wnd _w;\n\n\t\tinternal WProp(wnd w) => _w = w;\n\n\t\t/// <summary>\n\t\t/// Gets a window property.\n\t\t/// Calls API <ms>GetProp</ms> and returns its return value.\n\t\t/// </summary>\n\t\t/// <param name=\"name\">Property name.</param>\n\t\t/// <remarks>Supports <see cref=\"lastError\"/>.</remarks>\n\t\tpublic nint this[string name] => Api.GetProp(_w, name);\n\n\t\t/// <summary>\n\t\t/// Gets a window property.\n\t\t/// Calls API <ms>GetProp</ms> and returns its return value.\n\t\t/// </summary>\n\t\t/// <param name=\"atom\">Property name atom in the global atom table.</param>\n\t\t/// <remarks>\n\t\t/// This overload uses atom instead of string. It's about 3 times faster. See API <ms>GlobalAddAtom</ms>, <ms>GlobalDeleteAtom</ms>.\n\t\t/// </remarks>\n\t\tpublic nint this[ushort atom] => Api.GetProp(_w, atom);\n\n\t\t/// <summary>\n\t\t/// Sets a window property.\n\t\t/// Calls API <ms>SetProp</ms> and returns its return value.\n\t\t/// </summary>\n\t\t/// <param name=\"name\">Property name.</param>\n\t\t/// <param name=\"value\">Property value.</param>\n\t\t/// <remarks>\n\t\t/// Supports <see cref=\"lastError\"/>.\n\t\t/// \n\t\t/// Later call <see cref=\"Remove(string)\"/> to remove the property. If you use many unique property names and don't remove the properties, the property name strings can fill the global atom table which is of a fixed size (about 48000) and which is used by all processes for various purposes.\n\t\t/// </remarks>\n\t\tpublic bool Set(string name, nint value) {\n\t\t\treturn Api.SetProp(_w, name, value);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Sets a window property.\n\t\t/// Calls API <ms>SetProp</ms> and returns its return value.\n\t\t/// </summary>\n\t\t/// <param name=\"atom\">Property name atom in the global atom table.</param>\n\t\t/// <param name=\"value\">Property value.</param>\n\t\t/// <remarks>\n\t\t/// This overload uses atom instead of string. It's about 3 times faster. See API <ms>GlobalAddAtom</ms>, <ms>GlobalDeleteAtom</ms>.\n\t\t/// </remarks>\n\t\tpublic bool Set(ushort atom, nint value) {\n\t\t\treturn Api.SetProp(_w, atom, value);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Removes a window property.\n\t\t/// Calls API <ms>RemoveProp</ms> and returns its return value.\n\t\t/// </summary>\n\t\t/// <param name=\"name\">Property name. Other overload allows to use global atom instead, which is faster.</param>\n\t\t/// <remarks>Supports <see cref=\"lastError\"/>.</remarks>\n\t\tpublic nint Remove(string name) {\n\t\t\treturn Api.RemoveProp(_w, name);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Removes a window property.\n\t\t/// Calls API <ms>RemoveProp</ms> and returns its return value.\n\t\t/// </summary>\n\t\t/// <param name=\"atom\">Property name atom in the global atom table.</param>\n\t\tpublic nint Remove(ushort atom) {\n\t\t\treturn Api.RemoveProp(_w, atom);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets list of window properties.\n\t\t/// Uses API <ms>EnumPropsEx</ms>.\n\t\t/// </summary>\n\t\t/// <returns>0-length list if failed. Fails if invalid window or access denied ([](xref:uac)). Supports <see cref=\"lastError\"/>.</returns>\n\t\tpublic Dictionary<string, nint> GetList() {\n\t\t\tvar a = new Dictionary<string, nint>();\n\t\t\tApi.EnumPropsEx(_w, (w, name, data, p) => {\n\t\t\t\tstring s;\n\t\t\t\tif ((long)name < 0x10000) s = \"#\" + (int)name; else s = Marshal.PtrToStringUni(name);\n\t\t\t\ta.Add(s, data);\n\t\t\t\treturn true;\n\t\t\t}, default);\n\t\t\treturn a;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Calls <see cref=\"GetList\"/> and converts to string.\n\t\t/// </summary>\n\t\tpublic override string ToString() {\n\t\t\treturn string.Join(\"\\r\\n\", GetList());\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au/wnd/WTaskbarButton.cs",
    "content": "namespace Au.Types;\n\n/// <summary>\n/// Taskbar button flash, progress, add/delete.\n/// </summary>\npublic unsafe class WTaskbarButton {\n\treadonly wnd _w;\n\t\n\tinternal WTaskbarButton(wnd w) => _w = w;\n\t\n\t/// <summary>\n\t/// Starts or stops flashing the taskbar button of this window.\n\t/// </summary>\n\t/// <param name=\"count\">The number of times to flash. If 0, stops flashing.</param>\n\tpublic void Flash(int count) {\n\t\t//const uint FLASHW_STOP = 0;\n\t\t//const uint FLASHW_CAPTION = 0x00000001;\n\t\tconst uint FLASHW_TRAY = 0x00000002;\n\t\t//const uint FLASHW_ALL = FLASHW_CAPTION | FLASHW_TRAY;\n\t\t//const uint FLASHW_TIMER = 0x00000004;\n\t\t//const uint FLASHW_TIMERNOFG = 0x0000000C;\n\t\t\n\t\tvar fi = new Api.FLASHWINFO { cbSize = sizeof(Api.FLASHWINFO), hwnd = _w };\n\t\tif (count > 0) {\n\t\t\tfi.uCount = count;\n\t\t\t//fi.dwTimeout = (uint)periodMS; //not useful\n\t\t\tfi.dwFlags = FLASHW_TRAY;\n\t\t}\n\t\tApi.FlashWindowEx(ref fi);\n\t\t\n\t\t//tested. FlashWindow is easier but does not work for taskbar button, only for title bar when no taskbar button.\n\t}\n\t\n\t/// <summary>\n\t/// Sets the state of the progress indicator displayed on the taskbar button of this window.\n\t/// Calls <ms>ITaskbarList3.SetProgressState</ms>.\n\t/// </summary>\n\t/// <param name=\"state\">Progress indicator state and color.</param>\n\tpublic void SetProgressState(WTBProgressState state) {\n\t\t_TL?.SetProgressState(_w, state);\n\t}\n\t\n\t/// <summary>\n\t/// Sets the value of the progress indicator displayed on the taskbar button of this window.\n\t/// Calls <ms>ITaskbarList3.SetProgressValue</ms>.\n\t/// </summary>\n\t/// <param name=\"progressValue\">Progress indicator value, 0 to <i>progressTotal</i>.</param>\n\t/// <param name=\"progressTotal\">Max progress indicator value.</param>\n\tpublic void SetProgressValue(int progressValue, int progressTotal = 100) {\n\t\t_TL?.SetProgressValue(_w, progressValue, progressTotal);\n\t}\n\t\n\t/// <summary>\n\t/// Adds taskbar button of this window.\n\t/// Calls <ms>ITaskbarList.AddTab</ms>.\n\t/// </summary>\n\tpublic void Add() {\n\t\t_TL?.AddTab(_w);\n\t\t//tested: always returns 0, even if w is 0. Did not test other ITaskbarList3 methods.\n\t}\n\t\n\t/// <summary>\n\t/// Deletes taskbar button of this window.\n\t/// Calls <ms>ITaskbarList.DeleteTab</ms>.\n\t/// </summary>\n\tpublic void Delete() {\n\t\t_TL?.DeleteTab(_w);\n\t}\n\t\n\tstatic ITaskbarList3 _TL {\n\t\tget {\n\t\t\tvar r = (ITaskbarList3)new TaskbarList();\n\t\t\tif (0 != r.HrInit()) return null;\n\t\t\treturn r;\n\t\t}\n\t}\n\t\n\t[ComImport, Guid(\"56fdf344-fd6d-11d0-958a-006097c9a090\"), ClassInterface(ClassInterfaceType.None)]\n\tclass TaskbarList { }\n\t\n\t[ComImport, Guid(\"ea1afb91-9e28-4b86-90e9-9e9f8a5eefaf\"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\n\tinterface ITaskbarList3 {\n\t\t// ITaskbarList\n\t\t[PreserveSig] int HrInit();\n\t\t[PreserveSig] int AddTab(wnd hwnd);\n\t\t[PreserveSig] int DeleteTab(wnd hwnd);\n\t\t[PreserveSig] int ActivateTab(wnd hwnd);\n\t\t[PreserveSig] int SetActiveAlt(wnd hwnd);\n\t\t\n\t\t// ITaskbarList2\n\t\t[PreserveSig] int MarkFullscreenWindow(wnd hwnd, bool fFullscreen);\n\t\t\n\t\t// ITaskbarList3\n\t\t[PreserveSig] int SetProgressValue(wnd hwnd, long ullCompleted, long ullTotal);\n\t\t[PreserveSig] int SetProgressState(wnd hwnd, WTBProgressState state);\n\t\t[PreserveSig] int RegisterTab(wnd hwndTab, wnd hwndMDI);\n\t\t[PreserveSig] int UnregisterTab(wnd hwndTab);\n\t\t[PreserveSig] int SetTabOrder(wnd hwndTab, wnd hwndInsertBefore);\n\t\t[PreserveSig] int SetTabActive(wnd hwndTab, wnd hwndMDI, uint dwReserved);\n\t\t[PreserveSig] int ThumbBarAddButtons(wnd hwnd, uint cButtons, IntPtr pButton); //LPTHUMBBUTTON\n\t\t[PreserveSig] int ThumbBarUpdateButtons(wnd hwnd, uint cButtons, IntPtr pButton); //LPTHUMBBUTTON\n\t\t[PreserveSig] int ThumbBarSetImageList(wnd hwnd, IntPtr himl);\n\t\t[PreserveSig] int SetOverlayIcon(wnd hwnd, IntPtr hIcon, string pszDescription);\n\t\t[PreserveSig] int SetThumbnailTooltip(wnd hwnd, string pszTip);\n\t\t[PreserveSig] int SetThumbnailClip(wnd hwnd, ref RECT prcClip);\n\t}\n}\n\n/// <summary>\n/// Used by <see cref=\"WTaskbarButton.SetProgressState\"/>.\n/// </summary>\npublic enum WTBProgressState {\n#pragma warning disable 1591 //XML doc\n\tNoProgress = 0,\n\tIndeterminate = 0x1,\n\tNormal = 0x2,\n\tError = 0x4,\n\tPaused = 0x8\n#pragma warning restore 1591 //XML doc\n}\n"
  },
  {
    "path": "Au/wnd/WndCopyData.cs",
    "content": "namespace Au.More {\n\t/// <summary>\n\t/// Send/receive data to/from other process using message <ms>WM_COPYDATA</ms>.\n\t/// </summary>\n\t/// <remarks>\n\t/// This struct is <ms>COPYDATASTRUCT</ms>.\n\t/// <note>By default [](xref:uac) blocks messages sent from processes of lower integrity level. Call <see cref=\"EnableReceivingWM_COPYDATA\"/> if need.</note>\n\t/// </remarks>\n\t/// <seealso cref=\"System.IO.MemoryMappedFiles.MemoryMappedFile\"/>\n\t/// <seealso cref=\"System.IO.Pipes.NamedPipeServerStream\"/>\n\tpublic unsafe struct WndCopyData {\n\t\t//COPYDATASTRUCT fields\n\t\tnint _dwData;\n\t\tint _cbData;\n\t\tbyte* _lpData;\n\n\t\t#region receive\n\n\t\t/// <summary>\n\t\t/// Initializes this variable from <i>lParam</i> of a received <ms>WM_COPYDATA</ms> message.\n\t\t/// Then you can call functions of this variable to get data in managed format.\n\t\t/// </summary>\n\t\t/// <param name=\"lParam\"><i>lParam</i> of a <ms>WM_COPYDATA</ms> message received in a window procedure. It is <ms>COPYDATASTRUCT</ms> pointer.</param>\n\t\tpublic WndCopyData(nint lParam) {\n\t\t\tvar p = (WndCopyData*)lParam;\n\t\t\t_dwData = p->_dwData; _cbData = p->_cbData; _lpData = p->_lpData;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Data id. It is <ms>COPYDATASTRUCT.dwData</ms>.\n\t\t/// </summary>\n\t\tpublic int DataId { get => (int)_dwData; set => _dwData = value; }\n\n\t\t/// <summary>\n\t\t/// Unmanaged data pointer. It is <ms>COPYDATASTRUCT.lpData</ms>.\n\t\t/// </summary>\n\t\tpublic byte* RawData { get => _lpData; set => _lpData = value; }\n\n\t\t/// <summary>\n\t\t/// Unmanaged data size. It is <ms>COPYDATASTRUCT.cbData</ms>.\n\t\t/// </summary>\n\t\tpublic int RawDataSize { get => _cbData; set => _cbData = value; }\n\n\t\t/// <summary>\n\t\t/// Gets received data as string.\n\t\t/// </summary>\n\t\tpublic string GetString() {\n\t\t\treturn new string((char*)_lpData, 0, _cbData / 2);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets received data as <c>byte[]</c>.\n\t\t/// </summary>\n\t\tpublic byte[] GetBytes() {\n\t\t\tvar a = new byte[_cbData];\n\t\t\tMarshal.Copy((IntPtr)_lpData, a, 0, a.Length);\n\t\t\treturn a;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Calls API <ms>ChangeWindowMessageFilter</ms>(<c>WM_COPYDATA</c>). Then windows of this process can receive this message from lower [](xref:uac) integrity level processes.\n\t\t/// </summary>\n\t\tpublic static void EnableReceivingWM_COPYDATA() {\n\t\t\tApi.ChangeWindowMessageFilter(Api.WM_COPYDATA, 1);\n\t\t}\n\n\t\t#endregion\n\n\t\t#region send\n\n\t\t/// <summary>\n\t\t/// Sends string or other data to a window of any process. Uses API <ms>SendMessage</ms> <ms>WM_COPYDATA</ms>.\n\t\t/// </summary>\n\t\t/// <typeparam name=\"T\">Type of data elements. For example, <c>char</c> for string, <c>byte</c> for <c>byte[]</c>.</typeparam>\n\t\t/// <param name=\"w\">The window.</param>\n\t\t/// <param name=\"dataId\">Data id. It is <ms>COPYDATASTRUCT.dwData</ms>.</param>\n\t\t/// <param name=\"data\">Data. For example string or <c>byte[]</c>. String can contain <c>'\\0'</c> characters.</param>\n\t\t/// <param name=\"wParam\">Can be any value. Optional.</param>\n\t\t/// <returns><c>SendMessage</c>'s return value.</returns>\n\t\tpublic static unsafe nint Send<T>(wnd w, int dataId, ReadOnlySpan<T> data, nint wParam = 0) where T : unmanaged {\n\t\t\tfixed (T* p = data) {\n\t\t\t\tvar c = new WndCopyData { _dwData = dataId, _cbData = data.Length * sizeof(T), _lpData = (byte*)p };\n\t\t\t\treturn w.Send(Api.WM_COPYDATA, wParam, &c);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Type of <see cref=\"SendReceive{TSend, TReceive}(wnd, int, ReadOnlySpan{TSend}, ResultReader{TReceive})\"/> callback function.\n\t\t/// </summary>\n\t\t/// <param name=\"span\">Received data buffer. The callback function can convert it to array, string, etc.</param>\n\t\tpublic delegate void ResultReader<TReceive>(ReadOnlySpan<TReceive> span) where TReceive : unmanaged;\n\t\t//compiler error if Action<ReadOnlySpan<TReceive>>.\n\t\t//could instead use System.Buffers.ReadOnlySpanAction, but then need TState, which is difficult to use for return, and nobody would use, and would not make faster etc.\n\n\t\tstatic readonly Lazy<IntPtr> s_mutex = new(Api.CreateMutex(null, false, \"Au-mutex-WndUtil.Data\")); //tested: don't need Api.SECURITY_ATTRIBUTES.ForLowIL\n\n\t\t/// <summary>\n\t\t/// Sends string or other data to a window of any process. Uses API <ms>SendMessage</ms> <ms>WM_COPYDATA</ms>.\n\t\t/// Receives string or other data returned by that window with <see cref=\"Return\"/>.\n\t\t/// </summary>\n\t\t/// <typeparam name=\"TSend\">Type of data elements. For example, <c>char</c> for string, <c>byte</c> for <c>byte[]</c></typeparam>\n\t\t/// <typeparam name=\"TReceive\">Type of received data elements. For example, <c>char</c> for string, <c>byte</c> for <c>byte[]</c>.</typeparam>\n\t\t/// <param name=\"w\">The window.</param>\n\t\t/// <param name=\"dataId\">Data id. It is <ms>COPYDATASTRUCT.dwData</ms>.</param>\n\t\t/// <param name=\"send\">Data to send. For example string or <c>byte[]</c>. String can contain <c>'\\0'</c> characters.</param>\n\t\t/// <param name=\"receive\">Callback function that can convert the received data to desired format.</param>\n\t\t/// <returns><c>false</c> if failed.</returns>\n\t\tpublic static unsafe bool SendReceive<TSend, TReceive>(wnd w, int dataId, ReadOnlySpan<TSend> send, ResultReader<TReceive> receive) where TSend : unmanaged where TReceive : unmanaged {\n\t\t\tvar mutex = s_mutex.Value;\n\t\t\tif (Api.WaitForSingleObject(mutex, -1) is not (0 or Api.WAIT_ABANDONED_0)) return false;\n\t\t\ttry {\n\t\t\t\tint len = (int)Send(w, dataId, send, Api.GetCurrentProcessId());\n\t\t\t\tif (len == 0) return false;\n\t\t\t\tvar sm = SharedMemory_.ReturnDataPtr;\n\t\t\t\tif (len > 0) { //shared memory\n\t\t\t\t\tif (len <= SharedMemory_.ReturnDataSize) {\n\t\t\t\t\t\treceive(new ReadOnlySpan<TReceive>((TReceive*)sm, len / sizeof(TReceive)));\n\t\t\t\t\t} else {\n\t\t\t\t\t\tusing var m2 = SharedMemory_.Mapping.CreateOrOpen(new((char*)sm), len);\n\t\t\t\t\t\treceive(new ReadOnlySpan<TReceive>((TReceive*)m2.Mem, len / sizeof(TReceive)));\n\t\t\t\t\t}\n\t\t\t\t} else { //process memory\n\t\t\t\t\tvar pm = (void*)*(long*)sm;\n\t\t\t\t\treceive(new ReadOnlySpan<TReceive>((TReceive*)pm, -len / sizeof(TReceive)));\n\t\t\t\t\tbool ok = Api.VirtualFree(pm);\n\t\t\t\t\tDebug_.PrintIf(!ok, \"VirtualFree\");\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tfinally { Api.ReleaseMutex(mutex); }\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Calls <see cref=\"SendReceive{TSend, TReceive}(wnd, int, ReadOnlySpan{TSend}, ResultReader{TReceive})\"/> and gets the received data as <c>byte[]</c>.\n\t\t/// </summary>\n\t\t/// <param name=\"received\">The received data.</param>\n\t\t/// <inheritdoc cref=\"SendReceive{TSend, TReceive}(wnd, int, ReadOnlySpan{TSend}, ResultReader{TReceive})\"/>\n\t\tpublic static bool SendReceive<TSend>(wnd w, int dataId, ReadOnlySpan<TSend> send, out byte[] received) where TSend : unmanaged {\n\t\t\tbyte[] r = null;\n\t\t\tbool R = SendReceive<TSend, byte>(w, dataId, send, span => r = span.ToArray());\n\t\t\treceived = r;\n\t\t\treturn R;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Calls <see cref=\"SendReceive{TSend, TReceive}(wnd, int, ReadOnlySpan{TSend}, ResultReader{TReceive})\"/> and gets the received string.\n\t\t/// </summary>\n\t\t/// <param name=\"received\">The received data.</param>\n\t\t/// <inheritdoc cref=\"SendReceive{TSend, TReceive}(wnd, int, ReadOnlySpan{TSend}, ResultReader{TReceive})\"/>\n\t\tpublic static bool SendReceive<TSend>(wnd w, int dataId, ReadOnlySpan<TSend> send, out string received) where TSend : unmanaged {\n\t\t\tstring r = null;\n\t\t\tbool R = SendReceive<TSend, char>(w, dataId, send, span => r = span.ToString());\n\t\t\treceived = r;\n\t\t\treturn R;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Returns data to <see cref=\"SendReceive\"/>.\n\t\t/// </summary>\n\t\t/// <param name=\"data\"></param>\n\t\t/// <param name=\"length\"></param>\n\t\t/// <param name=\"wParam\"><i>wParam</i> of the received <c>WM_COPYDATA</c> message. Important, pass unchanged.</param>\n\t\t/// <returns>Your window procedure must return this value.</returns>\n\t\tpublic static unsafe int Return(void* data, int length, nint wParam) {\n\t\t\tvar sm = SharedMemory_.ReturnDataPtr;\n\n\t\t\t//use shared memory of this library. Max 1 MB.\n\t\t\tif (length <= SharedMemory_.ReturnDataSize) {\n\t\t\t\tMemoryUtil.Copy(data, sm, length);\n\t\t\t\treturn length;\n\t\t\t}\n\n\t\t\t//allocate memory in caller process\n\t\t\tusing var pm = new ProcessMemory((int)wParam, length, noException: true);\n\t\t\tif (pm.ProcessHandle != default) { //fails if that process has higher UAC IL. Rare.\n\t\t\t\tpm.Write(data, length);\n\t\t\t\t*(long*)sm = (long)pm.Mem;\n\t\t\t\tpm.MemAllocated = default;\n\t\t\t\treturn -length;\n\t\t\t}\n\n\t\t\t//allocate new shared memory\n\t\t\ttry {\n\t\t\t\tvar smname = \"Au-memory-\" + Guid.NewGuid().ToString();\n\t\t\t\tfixed (char* p = smname) MemoryUtil.Copy(p, sm, smname.Length * 2 + 2);\n\t\t\t\tvar m2 = SharedMemory_.Mapping.CreateOrOpen(smname, length);\n\t\t\t\tMemoryUtil.Copy(data, m2.Mem, length);\n\t\t\t\tTask.Run(() => { //wait until caller returns and then close the shared memory in this process\n\t\t\t\t\tvar mutex = s_mutex.Value;\n\t\t\t\t\tif (Api.WaitForSingleObject(mutex, -1) is not (0 or Api.WAIT_ABANDONED_0)) { Debug_.Print(\"WaitForSingleObject\"); return; }\n\t\t\t\t\tApi.ReleaseMutex(mutex);\n\t\t\t\t\tm2.Dispose();\n\t\t\t\t});\n\t\t\t\treturn length;\n\t\t\t}\n\t\t\tcatch { return 0; }\n\n\t\t\t//speed when size 1 MB and hot CPU:\n\t\t\t//\tshared memory: 1000 mcs\n\t\t\t//\tprocess memory: 1500 mcs\n\t\t\t//\tshared memory 2: 2500 mcs\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Returns string or other data to <see cref=\"SendReceive\"/>.\n\t\t/// </summary>\n\t\t/// <typeparam name=\"T\">Type of data elements. For example, <c>char</c> for string, <c>byte</c> for <c>byte[]</c></typeparam>\n\t\t/// <param name=\"data\"></param>\n\t\t/// <param name=\"wParam\"><i>wParam</i> of the received <c>WM_COPYDATA</c> message. Important, pass unchanged.</param>\n\t\t/// <returns>Your window procedure must return this value.</returns>\n\t\tpublic static unsafe int Return<T>(ReadOnlySpan<T> data, nint wParam) where T : unmanaged {\n\t\t\tfixed (T* f = data) return Return(f, data.Length * sizeof(T), wParam);\n\t\t}\n\n\t\t//rejected. Don't need too many not important overloads. Good: in most cases data size is 2 times smaller. Same: speed.\n\t\t//[SkipLocalsInit]\n\t\t//public static unsafe int ReturnStringUtf8_(RStr data, nint wParam) {\n\t\t//\tvar e = Encoding.UTF8;\n\t\t//\tusing var b = new FastBuffer<byte>(e.GetByteCount(data));\n\t\t//\tint len = e.GetBytes(data, new Span<byte>(b.p, b.n));\n\t\t//\treturn ReturnData_(b.p, len, wParam);\n\t\t//}\n\n\t\t#endregion\n\t}\n}\n"
  },
  {
    "path": "Au/wnd/WndSavedRect.cs",
    "content": "//TODO3: it seems makes WPF window visible too early.\n//TODO3: if window not resizable, restore only position, not size.\n\nnamespace Au.More;\n\n/// <summary>\n/// Helps to save and restore window rectangle and state. Ensures in screen, per-monitor-DPI-aware, etc.\n/// </summary>\n/// <example>\n/// WPF window created with <see cref=\"wpfBuilder\"/>.\n/// <code><![CDATA[\n/// const string c_rkey = @\"HKEY_CURRENT_USER\\Software\\Au\\Test\", c_rvalue = @\"Wpf7.Rect\";\n/// var b = new wpfBuilder(\"Window\").WinSize(400).R.AddOkCancel().End();\n/// \t\n/// WndSavedRect.Restore(b.Window, Registry.GetValue(c_rkey, c_rvalue, null) as string, s1 => Registry.SetValue(c_rkey, c_rvalue, s1));\n/// \n/// //the same\n/// //b.WinSaved(Registry.GetValue(c_rkey, c_rvalue, null) as string, s1 => Registry.SetValue(c_rkey, c_rvalue, s1));\n/// \n/// if (!b.ShowDialog()) return;\n/// ]]></code>\n/// </example>\npublic struct WndSavedRect {\n\t/// <summary>\n\t/// Window rectangle in normal state (not maximized/minimized), as retrieved by API <ms>GetWindowPlacement</ms>.\n\t/// </summary>\n\tpublic RECT RawRect { get => _r; set => _r = value; }\n\tRECT _r;\n\n\t/// <summary>\n\t/// <see cref=\"Dpi.OfWindow\"/>.\n\t/// </summary>\n\tpublic int Dpi { get; set; }\n\n\t/// <summary>\n\t/// The window should be maximized.\n\t/// </summary>\n\tpublic bool Maximize { get; set; }\n\n\t/// <summary>\n\t/// <see cref=\"wnd.IsToolWindow\"/>. If <c>false</c>, <see cref=\"RawRect\"/> may have an offset that depends on work area.\n\t/// </summary>\n\tpublic bool IsToolWindow { get; set; }\n\n\t/// <summary>\n\t/// Converts this object to string for saving.\n\t/// The string is very simple, like <c>\"1 2 3 4 5 6\"</c>.\n\t/// </summary>\n\tpublic override string ToString() {\n\t\treturn $\"{_r.left} {_r.top} {_r.Width} {_r.Height} {Dpi} {(Maximize ? 1 : 0) | (IsToolWindow ? 2 : 0)}\";\n\t}\n\n\t/// <summary>\n\t/// Creates <see cref=\"WndSavedRect\"/> from string created by <see cref=\"ToString\"/>.\n\t/// </summary>\n\t/// <returns><c>false</c> if the string is <c>null</c> or invalid.</returns>\n\t/// <param name=\"saved\">String created by <see cref=\"ToString\"/>.</param>\n\t/// <param name=\"x\">Result.</param>\n\tpublic static bool FromString(string saved, out WndSavedRect x) {\n\t\tx = default;\n\t\tif (saved == null) return false;\n\t\tvar a = new int[6];\n\t\tfor (int i = 0, j = 0; i < a.Length; i++) if (!saved.ToInt(out a[i], j, out j)) return false;\n\t\tx._r = (a[0], a[1], a[2], a[3]);\n\t\tx.Dpi = a[4];\n\t\tvar flags = a[5];\n\t\tx.Maximize = 0 != (flags & 1);\n\t\tx.IsToolWindow = 0 != (flags & 2);\n\t\treturn true;\n\t}\n\n\t/// <summary>\n\t/// Gets window rectangle and state for saving. Usually called when closing the window.\n\t/// See also <see cref=\"ToString\"/>.\n\t/// </summary>\n\t/// <exception cref=\"AuWndException\">Failed to get rectangle, probably invalid window handle.</exception>\n\tpublic WndSavedRect(wnd w) {\n\t\tif (!w.GetWindowPlacement_(out var p, false)) w.ThrowUseNative();\n\t\t_r = p.rcNormalPosition;\n\t\tDpi = More.Dpi.OfWindow(w);\n\t\tMaximize = p.showCmd == Api.SW_SHOWMAXIMIZED || (p.showCmd == Api.SW_SHOWMINIMIZED && 0 != (p.flags & Api.WPF_RESTORETOMAXIMIZED));\n\t\tIsToolWindow = w.IsToolWindow;\n\t}\n\n\t/// <summary>\n\t/// Gets window rectangle and state for saving. Usually called when closing the window.\n\t/// See also <see cref=\"ToString\"/>.\n\t/// </summary>\n\t/// <exception cref=\"AuWndException\">Failed to get rectangle, probably invalid window handle.</exception>\n\tpublic WndSavedRect(System.Windows.Window w) : this(w.Hwnd()) { }\n\n\t/// <summary>\n\t/// Gets window rectangle and state for saving. Usually called when closing the window.\n\t/// See also <see cref=\"ToString\"/>.\n\t/// </summary>\n\t/// <exception cref=\"AuWndException\">Failed to get rectangle, probably invalid window handle.</exception>\n\tpublic WndSavedRect(System.Windows.Forms.Form form) : this(form.Hwnd()) { }\n\n\t/// <summary>\n\t/// Gets real rectangle for restoring saved window rectangle.\n\t/// </summary>\n\t/// <remarks>\n\t/// It is recommended to call this before creating window, and create window with the returned rectangle. Also set maximized state if <see cref=\"Maximize\"/>.\n\t/// If it is not possible, can be called later, for example when window is created but still invisible. However then possible various problems, for example may need to set window rectangle two times, because the window may be for example DPI-scaled when moving to another screen etc.\n\t/// \n\t/// This function ensures the window is in screen, ensures correct size when screen DPI changed, etc.\n\t/// </remarks>\n\tpublic RECT NormalizeRect() {\n\t\tvar r = _r;\n\t\tvar scr = screen.of(r);\n\t\tint dpi = scr.Dpi;\n\t\tif (dpi != this.Dpi) {\n\t\t\tr.Width = Math2.MulDiv(r.Width, dpi, this.Dpi);\n\t\t\tr.Height = Math2.MulDiv(r.Height, dpi, this.Dpi);\n\t\t\t//don't change xy. Anyway we cannot cover all cases, eg changed DPI of another screen that could affect xy of the window in this screen.\n\t\t}\n\t\tif (!IsToolWindow) {\n\t\t\tvar v = scr.Info;\n\t\t\tr.Offset(v.workArea.left - v.rect.left, v.workArea.top - v.rect.top);\n\t\t}\n\t\tr.EnsureInScreen(scr, workArea: !IsToolWindow); //TODO3: use simple rect adjust. Or add EnsureInRect.\n\t\treturn r;\n\t}\n\n\t/// <summary>\n\t/// Calls <see cref=\"FromString\"/>. If it returns <c>true</c>, calls <see cref=\"NormalizeRect\"/>, <see cref=\"ExtWpf.SetRect\"/>, maximizes if need and returns <c>true</c>.\n\t/// Call this function before showing window.\n\t/// </summary>\n\t/// <param name=\"w\"></param>\n\t/// <param name=\"saved\">String created by <see cref=\"ToString\"/>.</param>\n\t/// <param name=\"save\">If not <c>null</c>, called when closing the window. Receives string for saving. Can save it in registry, file, anywhere.</param>\n\t/// <exception cref=\"InvalidOperationException\">Window is loaded.</exception>\n\tpublic static bool Restore(System.Windows.Window w, string saved, Action<string> save = null) {\n\t\tif (w.IsLoaded) throw new InvalidOperationException(\"Window is loaded.\");\n\t\tbool ret = FromString(saved, out var v);\n\t\tif (ret) {\n\t\t\tvar r = v.NormalizeRect();\n\t\t\tif (v.Maximize) w.WindowState = System.Windows.WindowState.Maximized;\n\t\t\tw.SetRect(r);\n\t\t}\n\t\tif (save != null) {\n\t\t\tw.Closing += (_, _) => {\n\t\t\t\tif (w.IsLoaded) save(new WndSavedRect(w).ToString());\n\t\t\t};\n\t\t}\n\t\treturn ret;\n\t}\n\n\t/// <summary>\n\t/// Calls <see cref=\"FromString\"/>. If it returns <c>true</c>, sets <i>form</i> bounds = <see cref=\"NormalizeRect\"/>, maximizes if need, sets <c>StartPosition</c> = <c>Manual</c>, and returns <c>true</c>.\n\t/// Call this function before showing window.\n\t/// </summary>\n\t/// <param name=\"form\"></param>\n\t/// <param name=\"saved\">String created by <see cref=\"ToString\"/>.</param>\n\t/// <param name=\"save\">If not <c>null</c>, called when closing the window. Receives string for saving. Can save it in registry, file, anywhere.</param>\n\tpublic static bool Restore(System.Windows.Forms.Form form, string saved, Action<string> save = null) {\n\t\tbool ret = FromString(saved, out var v);\n\t\tif (ret) {\n\t\t\tform.StartPosition = System.Windows.Forms.FormStartPosition.Manual;\n\t\t\tform.Bounds = v.NormalizeRect();\n\t\t\tif (v.Maximize) form.WindowState = System.Windows.Forms.FormWindowState.Maximized;\n\t\t}\n\t\tif (save != null) {\n\t\t\tform.FormClosing += (_, _) => {\n\t\t\t\tif (form.IsHandleCreated) save(new WndSavedRect(form).ToString());\n\t\t\t};\n\t\t}\n\t\treturn ret;\n\t}\n\n\t//probably not useful. Unfinished. Or move to wnd.\n\t///// <summary>\n\t///// Calls <see cref=\"FromString\"/>. If it returns <c>true</c>, sets <i>w</i> rectangle = <see cref=\"NormalizeRect\"/>, maximizes if need, and returns <c>true</c>.\n\t///// </summary>\n\t///// <param name=\"w\"></param>\n\t///// <param name=\"saved\">String created by <see cref=\"ToString\"/>.</param>\n\t///// <param name=\"allStates\">If need to maximize and the window already is maximized, set the restored window rectangle too.</param>\n\t//public static unsafe bool Restore(wnd w, string saved, bool allStates = false) {\n\t//\tw.ThrowIfInvalid();\n\t//\tbool ret = FromString(saved, out var v);\n\t//\tif (ret) {\n\t//\t\t//var p = new Api.WINDOWPLACEMENT {\n\t//\t\t//\trcNormalPosition = v.NormalizeRect(),\n\t//\t\t//\tshowCmd = v.Maximize ? Api.SW_SHOWMAXIMIZED : Api.SW_RESTORE\n\t//\t\t//};\n\t//\t\t//w.SetWindowPlacement_(ref p, false, \"Failed to restore window position\");\n\n\t//\t\tif (!allStates && v.Maximize && w.IsMaximized) return true;\n\t//\t\tif (w.IsMaximized) w.ShowNotMinMax(true);\n\t//\t\tif(!w.MoveL(v.NormalizeRect())) w.ThrowUseNative(\"Failed to restore window position\");\n\t//\t\tif (v.Maximize) w.ShowMaximized(true);\n\t//\t}\n\t//\treturn ret;\n\t//}\n}\n"
  },
  {
    "path": "Au/wnd/WndUtil.cs",
    "content": "namespace Au.More {\n\t/// <summary>\n\t/// Miscellaneous window-related functions. Rarely used in automation scripts.\n\t/// </summary>\n\tpublic static class WndUtil {\n\t\t//public void ShowAnimate(bool show)\n\t\t//{\n\t\t//\t//Don't add wnd function, because:\n\t\t//\t\t//Rarely used.\n\t\t//\t\t//Api.AnimateWindow() works only with windows of current thread.\n\t\t//\t\t//Only programmers would need it, and they can call the API directly.\n\t\t//}\n\n\t\t/// <summary>\n\t\t/// Registers new window class in this process.\n\t\t/// </summary>\n\t\t/// <param name=\"className\">Class name.</param>\n\t\t/// <param name=\"wndProc\">\n\t\t/// Delegate of a window procedure. See <ms>Window Procedures</ms>.\n\t\t/// \n\t\t/// Use <c>null</c> when you need a different delegate (method or target object) for each window instance; create windows with <see cref=\"CreateWindow(WNDPROC, bool, string, string, WS, WSE, int, int, int, int, wnd, nint, IntPtr, nint)\"/> or <see cref=\"CreateMessageOnlyWindow(WNDPROC, string, string)\"/>.\n\t\t/// If not <c>null</c>, it must be a static named method; create windows with any other function, including API <ms>CreateWindowEx</ms>.\n\t\t/// </param>\n\t\t/// <param name=\"etc\">\n\t\t/// Can be used to specify API <ms>WNDCLASSEX</ms> fields.\n\t\t/// To set cursor use field <c>mCursor</c> (standard cursor) or <c>hCursor</c> (native handle of a custom cursor).\n\t\t/// If <c>null</c>, this function sets arrow cursor and style <c>CS_VREDRAW | CS_HREDRAW</c>.\n\t\t/// </param>\n\t\t/// <exception cref=\"ArgumentException\"><i>wndProc</i> is an instance method. Must be static method or <c>null</c>. If need instance method, use <c>null</c> here and pass <i>wndProc</i> to <see cref=\"CreateWindow\"/>.</exception>\n\t\t/// <exception cref=\"InvalidOperationException\">The class already registered with this function and different <i>wndProc</i> (another method or another target object).</exception>\n\t\t/// <exception cref=\"Win32Exception\">Failed, for example if the class already exists and was registered not with this function.</exception>\n\t\t/// <remarks>\n\t\t/// Calls API <ms>RegisterClassEx</ms>.\n\t\t/// The window class is registered until this process ends. Don't need to unregister.\n\t\t/// If called next time for the same window class, does nothing if <i>wndProc</i> is equal to the previous (or both <c>null</c>). Then ignores <i>etc</i>. Throws exception if different.\n\t\t/// Thread-safe.\n\t\t/// Protects the <i>wndProc</i> delegate from GC.\n\t\t/// </remarks>\n\t\tpublic static unsafe void RegisterWindowClass(string className, WNDPROC wndProc = null, RWCEtc etc = null) {\n\t\t\tif (wndProc?.Target != null) throw new ArgumentException(\"wndProc must be static method or null\");\n\t\t\t//never mind: Target of static lambda != null. Could use code `&& !wndProc.Target.GetType().FullName.Ends(\"+<>c\")`, but it's undocumented and may stop working in new .NET version.\n\n\t\t\tlock (s_classes) {\n\t\t\t\tif (s_classes.TryGetValue(className, out var wpPrev)) {\n\t\t\t\t\tif (wpPrev != wndProc) throw new InvalidOperationException(\"Window class already registered\"); //another method or another target object\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tvar x = new Api.WNDCLASSEX(etc);\n\n\t\t\t\tfixed (char* pCN = className) {\n\t\t\t\t\tx.lpszClassName = pCN;\n\t\t\t\t\tif (wndProc != null) {\n\t\t\t\t\t\tx.lpfnWndProc = Marshal.GetFunctionPointerForDelegate(wndProc);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tx.lpfnWndProc = s_cwProcFP;\n\t\t\t\t\t}\n\t\t\t\t\tx.style |= Api.CS_GLOBALCLASS;\n\n\t\t\t\t\tif (0 == Api.RegisterClassEx(x)) throw new Win32Exception();\n\t\t\t\t\t//note: we don't return atom because: 1. Rarely used. 2. If assigned to an unused field, compiler may remove the function call.\n\n\t\t\t\t\ts_classes.Add(className, wndProc);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tinternal static bool IsClassRegistered_(string name, out WNDPROC wndProc) {\n\t\t\tlock (s_classes) {\n\t\t\t\treturn s_classes.TryGetValue(name, out wndProc);\n\t\t\t}\n\t\t}\n\n\t\tstatic Dictionary<string, WNDPROC> s_classes = new(StringComparer.OrdinalIgnoreCase); //allows to find registered classes and protects their wndProc delegates from GC\n\t\t[ThreadStatic] static Dictionary<wnd, WNDPROC> t_windows; //allows to dispatch messages and protects wndProc delegates of windows created in this thread from GC\n\n\t\tstatic nint _CWProc(wnd w, int msg, nint wParam, nint lParam) {\n\t\t\t//PrintMsg(w, msg, wParam, lParam);\n\t\t\tif (t_cwUnsafe) {\n\t\t\t\tt_cwUnsafe = false;\n\t\t\t\tvar wndProc = t_cwProc;\n\t\t\t\tt_cwProc = null;\n\t\t\t\tApi.SetWindowLongPtr(w, GWL.WNDPROC, Marshal.GetFunctionPointerForDelegate(wndProc));\n\t\t\t\t//print.it(\"subclassed\", w);\n\t\t\t\treturn wndProc(w, msg, wParam, lParam);\n\t\t\t} else {\n\t\t\t\tvar a = t_windows;\n\t\t\t\tif (a == null || !a.TryGetValue(w, out var wndProc)) {\n\t\t\t\t\twndProc = t_cwProc;\n\t\t\t\t\tif (wndProc == null) {\n\t\t\t\t\t\t//print.it(\"DefWindowProc\", w);\n\t\t\t\t\t\treturn Api.DefWindowProc(w, msg, wParam, lParam); //creating not with our CreateWindow(wndProc, ...)\n\t\t\t\t\t}\n\t\t\t\t\ta[w] = wndProc;\n\t\t\t\t\t//print.it(\"added\", a.Count, w);\n\t\t\t\t\tt_cwProc = null;\n\t\t\t\t}\n\n\t\t\t\tvar R = wndProc(w, msg, wParam, lParam);\n\n\t\t\t\tif (msg == Api.WM_NCDESTROY) {\n\t\t\t\t\ta.Remove(w);\n\t\t\t\t\t//print.it(\"removed\", a.Count, w);\n\t\t\t\t}\n\n\t\t\t\treturn R;\n\t\t\t}\n\t\t}\n\t\tstatic WNDPROC s_cwProc; //GC\n\t\tstatic IntPtr s_cwProcFP = Marshal.GetFunctionPointerForDelegate(s_cwProc = _CWProc);\n\t\t[ThreadStatic] static WNDPROC t_cwProc;\n\t\t[ThreadStatic] static bool t_cwUnsafe;\n\n\t\t/// <summary>\n\t\t/// Creates native/unmanaged window (API <ms>CreateWindowEx</ms>) and sets its window procedure.\n\t\t/// </summary>\n\t\t/// <param name=\"wndProc\">Window procedure.</param>\n\t\t/// <param name=\"keepAlive\">\n\t\t/// Protect <i>wndProc</i> from GC (garbage collector) until the window is destroyed (message <ms>WM_NCDESTROY</ms> received or thread ended).\n\t\t/// <para>IMPORTANT: In some cases it may prevent destroying the window until thread ends, and it can be a big memory leak. For example WPF then does not destroy <c>HwndHost</c>-ed controls. Then let <i>keepAlive</i>=<c>false</c> and manually manage <i>wndProc</i> lifetime, for example keep it as a field of the wrapper class.</para>\n\t\t/// </param>\n\t\t/// <exception cref=\"AuException\">Failed to create window. Unlikely.</exception>\n\t\t/// <remarks>\n\t\t/// If the class was registered with <see cref=\"RegisterWindowClass\"/> with <c>null</c> <i>wndProc</i>, the <i>wndProc</i> function will receive all messages. Else will not receive messages sent before <c>CreateWindowEx</c> returns (<c>WM_CREATE</c> etc).\n\t\t/// \n\t\t/// To destroy the window can be used any function, including API <ms>DestroyWindow</ms>, <see cref=\"DestroyWindow\"/>, <see cref=\"wnd.Close\"/>, API <ms>WM_CLOSE</ms>.\n\t\t/// </remarks>\n\t\tpublic static wnd CreateWindow(WNDPROC wndProc, bool keepAlive, string className, string name = null, WS style = 0, WSE exStyle = 0, int x = 0, int y = 0, int width = 0, int height = 0, wnd parent = default, nint controlId = 0, IntPtr hInstance = default, nint param = 0) {\n\t\t\tNot_.Null(wndProc, className);\n\n\t\t\tt_windows ??= new();\n\t\t\twnd w;\n\t\t\tif (IsClassRegistered_(className, out var wp) && wp == null) {\n\t\t\t\t//if keepAlive, need to cubclass the new window, else add hwnd+wndProc to t_windows.\n\t\t\t\t//\tBut not after CreateWindowEx, because wndProc must receive all messages.\n\t\t\t\t//\tLet _CWProc do it on first message.\n\t\t\t\tt_cwProc = wndProc;\n\t\t\t\tt_cwUnsafe = !keepAlive;\n\t\t\t\ttry { w = Api.CreateWindowEx(exStyle, className, name, style, x, y, width, height, parent, controlId, hInstance, param); }\n\t\t\t\tfinally { t_cwProc = null; t_cwUnsafe = false; } //if CreateWindowEx failed and _CWProc not called\n\t\t\t\tif (w.Is0) throw new AuException(0);\n\t\t\t} else {\n\t\t\t\tw = Api.CreateWindowEx(exStyle, className, name, style, x, y, width, height, parent, controlId, hInstance, param);\n\t\t\t\tif (w.Is0) throw new AuException(0);\n\t\t\t\tif (keepAlive) {\n\t\t\t\t\tt_windows[w] = wndProc;\n\t\t\t\t\tApi.SetWindowLongPtr(w, GWL.WNDPROC, s_cwProcFP);\n\t\t\t\t} else {\n\t\t\t\t\tApi.SetWindowLongPtr(w, GWL.WNDPROC, Marshal.GetFunctionPointerForDelegate(wndProc));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn w;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Creates native/unmanaged window.\n\t\t/// </summary>\n\t\t/// <exception cref=\"AuException\">Failed to create window. Unlikely.</exception>\n\t\t/// <remarks>\n\t\t/// Calls API <ms>CreateWindowEx</ms>.\n\t\t/// To destroy the window can be used any function, including API <ms>DestroyWindow</ms>, <see cref=\"DestroyWindow\"/>, <see cref=\"wnd.Close\"/>, API <ms>WM_CLOSE</ms>.\n\t\t/// </remarks>\n\t\t/// <seealso cref=\"RegisterWindowClass\"/>\n\t\tpublic static wnd CreateWindow(string className, string name = null, WS style = 0, WSE exStyle = 0, int x = 0, int y = 0, int width = 0, int height = 0, wnd parent = default, nint controlId = 0, IntPtr hInstance = default, nint param = 0) {\n\t\t\tvar w = Api.CreateWindowEx(exStyle, className, name, style, x, y, width, height, parent, controlId, hInstance, param);\n\t\t\tif (w.Is0) throw new AuException(0);\n\t\t\treturn w;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Creates native/unmanaged <ms>message-only window</ms>.\n\t\t/// </summary>\n\t\t/// <param name=\"className\">Window class name. Can be any existing class.</param>\n\t\t/// <param name=\"name\">Window name or <c>null</c>.</param>\n\t\t/// <exception cref=\"AuException\">Failed to create window. Unlikely.</exception>\n\t\t/// <remarks>\n\t\t/// Styles: <ms>WS_POPUP</ms>, <c>WS_EX_NOACTIVATE</c>.\n\t\t/// To destroy the window can be used any function, including API <ms>DestroyWindow</ms>, <see cref=\"DestroyWindow\"/>, <see cref=\"wnd.Close\"/>, API <ms>WM_CLOSE</ms>.\n\t\t/// </remarks>\n\t\tpublic static wnd CreateMessageOnlyWindow(string className, string name = null) {\n\t\t\treturn CreateWindow(className, name, WS.POPUP, WSE.NOACTIVATE, parent: SpecHWND.MESSAGE);\n\t\t\t//note: WS_EX_NOACTIVATE is important.\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Creates native/unmanaged <ms>message-only window</ms> and sets its window procedure.\n\t\t/// </summary>\n\t\t/// <param name=\"wndProc\"></param>\n\t\t/// <param name=\"className\">Window class name.</param>\n\t\t/// <param name=\"name\">Window name or <c>null</c>.</param>\n\t\t/// <exception cref=\"AuException\">Failed to create window. Unlikely.</exception>\n\t\t/// <remarks>\n\t\t/// Styles: <ms>WS_POPUP</ms>, <c>WS_EX_NOACTIVATE</c>.\n\t\t/// Calls <see cref=\"CreateWindow(WNDPROC, bool, string, string, WS, WSE, int, int, int, int, wnd, nint, IntPtr, nint)\"/> with <i>keepAlive</i>=<c>true</c>.\n\t\t/// </remarks>\n\t\tpublic static wnd CreateMessageOnlyWindow(WNDPROC wndProc, string className, string name = null) {\n\t\t\treturn CreateWindow(wndProc, true, className, name, WS.POPUP, WSE.NOACTIVATE, parent: SpecHWND.MESSAGE);\n\t\t\t//note: WS_EX_NOACTIVATE is important.\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Auto-registers window class <c>\"Au.DWP\"</c> with <c>wndproc = DefWindowProc</c> and creates hidden window.\n\t\t/// </summary>\n\t\t/// <param name=\"messageOnly\"></param>\n\t\t/// <param name=\"wndProcUnsafe\">If not <c>null</c>, replaces window procedure (<c>SetWindowLongPtr</c>). The caller must protect the delegate from GC.</param>\n\t\t/// <exception cref=\"AuException\">Failed to create window. Unlikely.</exception>\n\t\tinternal static wnd CreateWindowDWP_(bool messageOnly, WNDPROC wndProcUnsafe = null) {\n\t\t\tvar cn = WindowClassDWP_;\n\t\t\tvar w = messageOnly ? CreateMessageOnlyWindow(cn) : CreateWindow(cn);\n\t\t\tif (wndProcUnsafe != null) Api.SetWindowLongPtr(w, GWL.WNDPROC, Marshal.GetFunctionPointerForDelegate(wndProcUnsafe));\n\t\t\treturn w;\n\t\t}\n\t\tstatic int s_registeredDWP;\n\t\tconst string c_wndClassDWP = \"Au.DWP\";\n\n\t\t/// <summary>\n\t\t/// Auto-registers window class <c>\"Au.DWP\"</c> with <c>wndproc = DefWindowProc</c> and returns <c>\"Au.DWP\"</c>.\n\t\t/// </summary>\n\t\tinternal static unsafe string WindowClassDWP_ {\n\t\t\tget {\n\t\t\t\tif (0 == Interlocked.CompareExchange(ref s_registeredDWP, 1, 0)) {\n\t\t\t\t\tvar x = new Api.WNDCLASSEX { cbSize = sizeof(Api.WNDCLASSEX), style = Api.CS_GLOBALCLASS };\n\t\t\t\t\tfixed (char* pCN = c_wndClassDWP) {\n\t\t\t\t\t\tx.lpszClassName = pCN;\n\t\t\t\t\t\tx.lpfnWndProc = Api.GetProcAddress(\"user32.dll\", \"DefWindowProcW\");\n\t\t\t\t\t\tif (0 == Api.RegisterClassEx(x)) throw new Win32Exception();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn c_wndClassDWP;\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Replaces window procedure (<c>SetWindowLongPtr</c>). Returns previous window procedure.\n\t\t/// The caller must protect the delegate from GC.\n\t\t/// </summary>\n\t\tinternal static IntPtr SubclassUnsafe_(wnd w, WNDPROC wndProc) {\n\t\t\treturn Api.SetWindowLongPtr(w, GWL.WNDPROC, Marshal.GetFunctionPointerForDelegate(wndProc));\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Destroys a native window of this thread.\n\t\t/// Calls API <ms>DestroyWindow</ms>.\n\t\t/// </summary>\n\t\t/// <returns><c>false</c> if failed. Supports <see cref=\"lastError\"/>.</returns>\n\t\t/// <seealso cref=\"wnd.Close\"/>\n\t\tpublic static bool DestroyWindow(wnd w) {\n\t\t\treturn Api.DestroyWindow(w);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Sets font.\n\t\t/// </summary>\n\t\t/// <param name=\"w\"></param>\n\t\t/// <param name=\"font\">\n\t\t/// Native font handle.\n\t\t/// If <c>default(IntPtr)</c>, sets font that is used by most windows and controls on this computer, usually <c>Segoe UI</c> 9, DPI-scaled for <i>w</i> screen.\n\t\t/// </param>\n\t\t/// <remarks>\n\t\t/// Sends <ms>WM_SETFONT</ms> message.\n\t\t/// </remarks>\n\t\tpublic static void SetFont(wnd w, IntPtr font = default) {\n\t\t\tw.Send(Api.WM_SETFONT, font != default ? font : NativeFont_.RegularCached(Dpi.OfWindow(w)).Handle);\n\t\t}\n\n\t\t//rejected. Rarely used. Easy to send message.\n\t\t///// <summary>\n\t\t///// Gets native font handle.\n\t\t///// Sends message API <ms>WM_GETFONT</ms>.\n\t\t///// Does not copy the font; don't need to dispose.\n\t\t///// Use this function only with windows of current process.\n\t\t///// </summary>\n\t\t//public static IntPtr GetFont(wnd w)\n\t\t//{\n\t\t//\treturn w.Send(Api.WM_GETFONT);\n\t\t//}\n\n\t\t/// <summary>\n\t\t/// Gets window Windows Store app user model id, like <c>\"Microsoft.WindowsCalculator_8wekyb3d8bbwe!App\"</c>.\n\t\t/// </summary>\n\t\t/// <returns><c>null</c> if failed. On Windows 7 returns <c>null</c> unless <i>getExePathIfNotWinStoreApp</i> <c>true</c>.</returns>\n\t\t/// <param name=\"w\">A top-level window.</param>\n\t\t/// <param name=\"prependShellAppsFolder\">Prepend <c>@\"shell:AppsFolder\\\"</c> (to run or get icon).</param>\n\t\t/// <param name=\"getExePathIfNotWinStoreApp\">Get program path if it is not a Windows Store app.</param>\n\t\t/// <remarks>\n\t\t/// Most Windows Store app windows have class name <c>\"Windows.UI.Core.CoreWindow\"</c> or <c>\"ApplicationFrameWindow\"</c>.\n\t\t/// </remarks>\n\t\tpublic static unsafe string GetWindowsStoreAppId(wnd w, bool prependShellAppsFolder = false, bool getExePathIfNotWinStoreApp = false) {\n\t\t\tstring appId = null;\n\n\t\t\tif (osVersion.minWin8) {\n\t\t\t\tvar cn = w.ClassName;\n\t\t\t\tif (osVersion.minWin10 && cn == \"ApplicationFrameWindow\") {\n\t\t\t\t\tvar w2 = w.ChildFast(null, \"Windows.UI.Core.CoreWindow\");\n\t\t\t\t\tif (!w2.Is0) {\n\t\t\t\t\t\tw = w2;\n\t\t\t\t\t\tcn = \"Windows.UI.Core.CoreWindow\";\n\t\t\t\t\t} else { //probably minimized. Very slow, ~20 times slower than GetApplicationUserModelId.\n\t\t\t\t\t\tif (0 == Api.SHGetPropertyStoreForWindow(w, Api.IID_IPropertyStore, out Api.IPropertyStore ps)) {\n\t\t\t\t\t\t\tif (0 == ps.GetValue(Api.PKEY_AppUserModel_ID, out var v)) {\n\t\t\t\t\t\t\t\tif (v.vt == Api.VARENUM.VT_LPWSTR) appId = Marshal.PtrToStringUni(v.value);\n\t\t\t\t\t\t\t\tv.Dispose();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tMarshal.ReleaseComObject(ps);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t//this code works with \"Windows.UI.Core.CoreWindow\" and WinUI 3 windows. Not with \"ApplicationFrameWindow\".\n\t\t\t\tif (appId == null) {\n\t\t\t\t\tusing var p = Handle_.OpenProcess(w);\n\t\t\t\t\tif (!p.Is0) {\n\t\t\t\t\t\tint na = 1024; var b = stackalloc char[na];\n\t\t\t\t\t\tif (0 == Api.GetApplicationUserModelId(p, ref na, b) && na > 1) appId = new(b, 0, na - 1);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (appId != null) {\n\t\t\t\t\tif (cn is not (\"Windows.UI.Core.CoreWindow\" or \"ApplicationFrameWindow\")) { //is it really a Store window?\n\t\t\t\t\t\tvar s = w.ProgramPath;\n\t\t\t\t\t\tif (s != null && !s.Starts(folders.ProgramFiles + @\"WindowsApps\\\", true)) {\n\t\t\t\t\t\t\tDebug_.Print(s);\n\t\t\t\t\t\t\treturn getExePathIfNotWinStoreApp ? s : null;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (prependShellAppsFolder) appId = @\"shell:AppsFolder\\\" + appId;\n\t\t\t\t\treturn appId;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn getExePathIfNotWinStoreApp ? w.ProgramPath : null;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Calls API <ms>GetClassLongPtr</ms>.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Supports <see cref=\"lastError\"/>.\n\t\t/// For <i>index</i> can be used constants from <see cref=\"GCL\"/>. All values are the same in 32-bit and 64-bit process.\n\t\t/// In 32-bit process actually calls <c>GetClassLong</c>, because <c>GetClassLongPtr</c> is unavailable.\n\t\t/// </remarks>\n\t\tpublic static nint GetClassLong(wnd w, int index) => Api.GetClassLongPtr(w, index);\n\n\t\t/// <summary>\n\t\t/// Changes the owner window.\n\t\t/// </summary>\n\t\t/// <returns>If fails, returns <c>false</c>; supports <see cref=\"lastError\"/>.</returns>\n\t\t/// <remarks>\n\t\t/// A window that has an owner window is always on top of it.\n\t\t/// Don't call this for controls, they don't have an owner window.\n\t\t/// Fails for example if the owner's process has higher [](xref:uac) integrity level or is a Store app.\n\t\t/// </remarks>\n\t\t/// <seealso cref=\"wnd.getwnd.Owner\"/>\n\t\tpublic static bool SetOwnerWindow(wnd w, wnd owner) {\n\t\t\tApi.SetWindowLongPtr(w, GWL.HWNDPARENT, (nint)owner);\n\t\t\tif (w.Get.Owner != owner) return false;\n\t\t\tif (!owner.Is0) {\n\t\t\t\tbool tm = owner.IsTopmost;\n\t\t\t\tif (tm != w.IsTopmost) { if (tm) w.ZorderTopmost(); else w.ZorderNoTopmost(); }\n\t\t\t\tif (!w.ZorderIsAbove(owner)) w.ZorderAbove(owner);\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\t//probably not useful. Dangerous.\n\t\t///// <summary>\n\t\t///// Calls API <ms>SetClassLongPtr</ms> (<c>SetClassLong</c> in 32-bit process).\n\t\t///// </summary>\n\t\t///// <exception cref=\"AuWndException\"/>\n\t\t//public static nint SetClassLong(wnd w, int index, nint newValue)\n\t\t//{\n\t\t//\tlastError.clear();\n\t\t//\tnint R = Api.SetClassLongPtr(w, index, newValue);\n\t\t//\tif(R == 0 && lastError.code != 0) w.ThrowUseNative();\n\t\t//\treturn R;\n\t\t//}\n\n\t\t//rejected. Does not work with many windows. Unreliable. Rarely used.\n\t\t///// <summary>\n\t\t///// Gets atom of a window class.\n\t\t///// To get class atom when you have a window <i>w</i>, use <c>WndUtil.GetClassLong(w, GCL.ATOM)</c>.\n\t\t///// </summary>\n\t\t///// <param name=\"className\">Class name.</param>\n\t\t///// <param name=\"moduleHandle\">Native module handle of the exe or dll that registered the class. Don't use if it is a global class (<c>CS_GLOBALCLASS</c> style).</param>\n\t\t//public static ushort GetClassAtom(string className, IntPtr moduleHandle = default)\n\t\t//{\n\t\t//\tvar x = new Api.WNDCLASSEX();\n\t\t//\tx.cbSize = Api.SizeOf(x);\n\t\t//\treturn Api.GetClassInfoEx(moduleHandle, className, ref x);\n\t\t//}\n\n\t\t/// <summary>\n\t\t/// Calls API <ms>RegisterWindowMessage</ms>.\n\t\t/// </summary>\n\t\t/// <param name=\"name\">Message name. Can be any unique string.</param>\n\t\t/// <param name=\"uacEnable\">Also call API <ms>ChangeWindowMessageFilter</ms> for the message. More info: <see cref=\"UacEnableMessages\"/>.</param>\n\t\tpublic static int RegisterMessage(string name, bool uacEnable = false) {\n\t\t\tvar m = Api.RegisterWindowMessage(name);\n\t\t\tif (uacEnable && m != 0) Api.ChangeWindowMessageFilter(m, 1);\n\t\t\treturn m;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Calls API <ms>ChangeWindowMessageFilter</ms> for each message in the list of messages.\n\t\t/// It allows processes of lower [](xref:uac) integrity level to send these messages to this process.\n\t\t/// </summary>\n\t\tpublic static void UacEnableMessages(params int[] messages) {\n\t\t\tforeach (var m in messages) Api.ChangeWindowMessageFilter(m, 1);\n\t\t}\n\n\t\t#region print msg\n\n\t\t/// <summary>\n\t\t/// Writes a Windows message to a string.\n\t\t/// If the message is specified in <i>options</i>, sets <c>s=null</c> and returns <c>false</c>.\n\t\t/// </summary>\n\t\tpublic static bool PrintMsg(out string s, wnd w, int msg, nint wParam, nint lParam, PrintMsgOptions options = null, [CallerMemberName] string m_ = null) {\n\t\t\t//Could instead use System.Windows.Forms.Message.ToString, but its list is incomplete, eg no dpichange messages.\n\t\t\t//\thttps://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/MessageDecoder.cs,b19021e2f4480d57\n\n\t\t\tif (options?.Skip is int[] a) {\n\t\t\t\ts = null;\n\t\t\t\tint prev = 0;\n\t\t\t\tforeach (var v in a) {\n\t\t\t\t\tif (v < 0) {\n\t\t\t\t\t\tif (msg >= prev && msg <= (v == int.MinValue ? int.MaxValue : -v)) return false;\n\t\t\t\t\t\tprev = int.MaxValue;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (v == msg) return false;\n\t\t\t\t\t\tprev = v;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tvar (name, plus) = _Name(ref msg, out bool reflect);\n\n\t\t\tusing (new StringBuilder_(out var b)) {\n\t\t\t\tif (options?.Number ?? true) {\n\t\t\t\t\t//uint counter = (uint)w.Prop[\"PrintMsg\"]; w.Prop.Set(\"PrintMsg\", ++counter);\n\t\t\t\t\t//b.Append(counter).Append(\". \");\n\t\t\t\t\tb.Append(++s_pm_counter).Append(\". \");\n\t\t\t\t}\n\n\t\t\t\tif (options?.Indent ?? true) { //makes ~10 times slower, but not too slow\n\t\t\t\t\tint i = 0;\n\t\t\t\t\tMethodBase m0 = null;\n\t\t\t\t\tforeach (var f in new StackTrace(1).GetFrames()) {\n\t\t\t\t\t\tvar m1 = f.GetMethod();\n\t\t\t\t\t\tif (m1.Name != m_) continue;\n\t\t\t\t\t\tif (m0 == null) m0 = m1; else if ((object)m1 == m0) i += 4;\n\t\t\t\t\t}\n\t\t\t\t\tif (i > 0) b.Append(' ', i);\n\t\t\t\t}\n\n\t\t\t\tif (reflect) b.Append(\"WM_REFLECT+\");\n\t\t\t\tif (name == null) b.AppendFormat(\"0x{0:X}\", msg);\n\t\t\t\telse if (plus != 0) b.AppendFormat(\"{0}+0x{1:X}\", name, plus);\n\t\t\t\telse if (msg >= 0xc000 && msg <= 0xffff) b.AppendFormat(\"\\\"{0}\\\"\", name);\n\t\t\t\telse b.Append(name);\n\n\t\t\t\tb.AppendFormat(\", 0x{0:X8}, 0x{1:X8}, hwnd={2}\", (int)wParam, (int)lParam, w.Handle);\n\t\t\t\tif (options?.WindowProperties ?? false) {\n\t\t\t\t\tif (!w.Is0) b.AppendFormat(\" ({0} \\\"{1}\\\" {{{2}}})\", w.ClassName?.Limit(30), w.Name?.Limit(30), w.Rect.ToStringSimple());\n\t\t\t\t}\n\n\t\t\t\ts = b.ToString();\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tstatic (string name, int plus) _Name(ref int m, out bool reflect) {\n\t\t\t\treflect = false;\n\t\t\t\tif (m >= 0x10000) return default; //reserved by the system\n\t\t\t\tif (m >= 0xC000) return (ClipFormats.GetName(m, orNull: true), 0); //registered\n\t\t\t\tif (m >= Api.WM_APP) return (\"WM_APP\", m - Api.WM_APP); //0x8000\n\t\t\t\tif (reflect = m >= Api.WM_REFLECT && m < Api.WM_REFLECT * 2) m -= Api.WM_REFLECT; //0x2000\n\t\t\t\tif (m >= Api.WM_USER) return (\"WM_USER\", m - Api.WM_USER); //0x400\n\t\t\t\t#region switch\n\t\t\t\tvar s = m switch {\n\t\t\t\t\t0x0 => \"WM_NULL\",\n\t\t\t\t\t0x1 => \"WM_CREATE\",\n\t\t\t\t\t0x2 => \"WM_DESTROY\",\n\t\t\t\t\t0x3 => \"WM_MOVE\",\n\t\t\t\t\t0x5 => \"WM_SIZE\",\n\t\t\t\t\t0x6 => \"WM_ACTIVATE\",\n\t\t\t\t\t0x7 => \"WM_SETFOCUS\",\n\t\t\t\t\t0x8 => \"WM_KILLFOCUS\",\n\t\t\t\t\t0xA => \"WM_ENABLE\",\n\t\t\t\t\t0xB => \"WM_SETREDRAW\",\n\t\t\t\t\t0xC => \"WM_SETTEXT\",\n\t\t\t\t\t0xD => \"WM_GETTEXT\",\n\t\t\t\t\t0xE => \"WM_GETTEXTLENGTH\",\n\t\t\t\t\t0xF => \"WM_PAINT\",\n\t\t\t\t\t0x10 => \"WM_CLOSE\",\n\t\t\t\t\t0x11 => \"WM_QUERYENDSESSION\",\n\t\t\t\t\t0x13 => \"WM_QUERYOPEN\",\n\t\t\t\t\t0x16 => \"WM_ENDSESSION\",\n\t\t\t\t\t0x12 => \"WM_QUIT\",\n\t\t\t\t\t0x14 => \"WM_ERASEBKGND\",\n\t\t\t\t\t0x15 => \"WM_SYSCOLORCHANGE\",\n\t\t\t\t\t0x18 => \"WM_SHOWWINDOW\",\n\t\t\t\t\t0x1A => \"WM_SETTINGCHANGE\",\n\t\t\t\t\t0x1B => \"WM_DEVMODECHANGE\",\n\t\t\t\t\t0x1C => \"WM_ACTIVATEAPP\",\n\t\t\t\t\t0x1D => \"WM_FONTCHANGE\",\n\t\t\t\t\t0x1E => \"WM_TIMECHANGE\",\n\t\t\t\t\t0x1F => \"WM_CANCELMODE\",\n\t\t\t\t\t0x20 => \"WM_SETCURSOR\",\n\t\t\t\t\t0x21 => \"WM_MOUSEACTIVATE\",\n\t\t\t\t\t0x22 => \"WM_CHILDACTIVATE\",\n\t\t\t\t\t0x23 => \"WM_QUEUESYNC\",\n\t\t\t\t\t0x24 => \"WM_GETMINMAXINFO\",\n\t\t\t\t\t0x26 => \"WM_PAINTICON\",\n\t\t\t\t\t0x27 => \"WM_ICONERASEBKGND\",\n\t\t\t\t\t0x28 => \"WM_NEXTDLGCTL\",\n\t\t\t\t\t0x2A => \"WM_SPOOLERSTATUS\",\n\t\t\t\t\t0x2B => \"WM_DRAWITEM\",\n\t\t\t\t\t0x2C => \"WM_MEASUREITEM\",\n\t\t\t\t\t0x2D => \"WM_DELETEITEM\",\n\t\t\t\t\t0x2E => \"WM_VKEYTOITEM\",\n\t\t\t\t\t0x2F => \"WM_CHARTOITEM\",\n\t\t\t\t\t0x30 => \"WM_SETFONT\",\n\t\t\t\t\t0x31 => \"WM_GETFONT\",\n\t\t\t\t\t0x32 => \"WM_SETHOTKEY\",\n\t\t\t\t\t0x33 => \"WM_GETHOTKEY\",\n\t\t\t\t\t0x37 => \"WM_QUERYDRAGICON\",\n\t\t\t\t\t0x39 => \"WM_COMPAREITEM\",\n\t\t\t\t\t0x3D => \"WM_GETOBJECT\",\n\t\t\t\t\t0x41 => \"WM_COMPACTING\",\n\t\t\t\t\t0x44 => \"WM_COMMNOTIFY\",\n\t\t\t\t\t0x46 => \"WM_WINDOWPOSCHANGING\",\n\t\t\t\t\t0x47 => \"WM_WINDOWPOSCHANGED\",\n\t\t\t\t\t0x48 => \"WM_POWER\",\n\t\t\t\t\t0x4A => \"WM_COPYDATA\",\n\t\t\t\t\t0x4B => \"WM_CANCELJOURNAL\",\n\t\t\t\t\t0x4E => \"WM_NOTIFY\",\n\t\t\t\t\t0x50 => \"WM_INPUTLANGCHANGEREQUEST\",\n\t\t\t\t\t0x51 => \"WM_INPUTLANGCHANGE\",\n\t\t\t\t\t0x52 => \"WM_TCARD\",\n\t\t\t\t\t0x53 => \"WM_HELP\",\n\t\t\t\t\t0x54 => \"WM_USERCHANGED\",\n\t\t\t\t\t0x55 => \"WM_NOTIFYFORMAT\",\n\t\t\t\t\t0x7B => \"WM_CONTEXTMENU\",\n\t\t\t\t\t0x7C => \"WM_STYLECHANGING\",\n\t\t\t\t\t0x7D => \"WM_STYLECHANGED\",\n\t\t\t\t\t0x7E => \"WM_DISPLAYCHANGE\",\n\t\t\t\t\t0x7F => \"WM_GETICON\",\n\t\t\t\t\t0x80 => \"WM_SETICON\",\n\t\t\t\t\t0x81 => \"WM_NCCREATE\",\n\t\t\t\t\t0x82 => \"WM_NCDESTROY\",\n\t\t\t\t\t0x83 => \"WM_NCCALCSIZE\",\n\t\t\t\t\t0x84 => \"WM_NCHITTEST\",\n\t\t\t\t\t0x85 => \"WM_NCPAINT\",\n\t\t\t\t\t0x86 => \"WM_NCACTIVATE\",\n\t\t\t\t\t0x87 => \"WM_GETDLGCODE\",\n\t\t\t\t\t0x88 => \"WM_SYNCPAINT\",\n\t\t\t\t\t0xA0 => \"WM_NCMOUSEMOVE\",\n\t\t\t\t\t0xA1 => \"WM_NCLBUTTONDOWN\",\n\t\t\t\t\t0xA2 => \"WM_NCLBUTTONUP\",\n\t\t\t\t\t0xA3 => \"WM_NCLBUTTONDBLCLK\",\n\t\t\t\t\t0xA4 => \"WM_NCRBUTTONDOWN\",\n\t\t\t\t\t0xA5 => \"WM_NCRBUTTONUP\",\n\t\t\t\t\t0xA6 => \"WM_NCRBUTTONDBLCLK\",\n\t\t\t\t\t0xA7 => \"WM_NCMBUTTONDOWN\",\n\t\t\t\t\t0xA8 => \"WM_NCMBUTTONUP\",\n\t\t\t\t\t0xA9 => \"WM_NCMBUTTONDBLCLK\",\n\t\t\t\t\t0xAB => \"WM_NCXBUTTONDOWN\",\n\t\t\t\t\t0xAC => \"WM_NCXBUTTONUP\",\n\t\t\t\t\t0xAD => \"WM_NCXBUTTONDBLCLK\",\n\t\t\t\t\t0xFE => \"WM_INPUT_DEVICE_CHANGE\",\n\t\t\t\t\t0xFF => \"WM_INPUT\",\n\t\t\t\t\t0x100 => \"WM_KEYDOWN\",\n\t\t\t\t\t0x101 => \"WM_KEYUP\",\n\t\t\t\t\t0x102 => \"WM_CHAR\",\n\t\t\t\t\t0x103 => \"WM_DEADCHAR\",\n\t\t\t\t\t0x104 => \"WM_SYSKEYDOWN\",\n\t\t\t\t\t0x105 => \"WM_SYSKEYUP\",\n\t\t\t\t\t0x106 => \"WM_SYSCHAR\",\n\t\t\t\t\t0x107 => \"WM_SYSDEADCHAR\",\n\t\t\t\t\t0x109 => \"WM_UNICHAR\",\n\t\t\t\t\t0x10D => \"WM_IME_STARTCOMPOSITION\",\n\t\t\t\t\t0x10E => \"WM_IME_ENDCOMPOSITION\",\n\t\t\t\t\t0x10F => \"WM_IME_COMPOSITION\",\n\t\t\t\t\t0x110 => \"WM_INITDIALOG\",\n\t\t\t\t\t0x111 => \"WM_COMMAND\",\n\t\t\t\t\t0x112 => \"WM_SYSCOMMAND\",\n\t\t\t\t\t0x113 => \"WM_TIMER\",\n\t\t\t\t\t0x114 => \"WM_HSCROLL\",\n\t\t\t\t\t0x115 => \"WM_VSCROLL\",\n\t\t\t\t\t0x116 => \"WM_INITMENU\",\n\t\t\t\t\t0x117 => \"WM_INITMENUPOPUP\",\n\t\t\t\t\t0x119 => \"WM_GESTURE\",\n\t\t\t\t\t0x11A => \"WM_GESTURENOTIFY\",\n\t\t\t\t\t0x11F => \"WM_MENUSELECT\",\n\t\t\t\t\t0x120 => \"WM_MENUCHAR\",\n\t\t\t\t\t0x121 => \"WM_ENTERIDLE\",\n\t\t\t\t\t0x122 => \"WM_MENURBUTTONUP\",\n\t\t\t\t\t0x123 => \"WM_MENUDRAG\",\n\t\t\t\t\t0x124 => \"WM_MENUGETOBJECT\",\n\t\t\t\t\t0x125 => \"WM_UNINITMENUPOPUP\",\n\t\t\t\t\t0x126 => \"WM_MENUCOMMAND\",\n\t\t\t\t\t0x127 => \"WM_CHANGEUISTATE\",\n\t\t\t\t\t0x128 => \"WM_UPDATEUISTATE\",\n\t\t\t\t\t0x129 => \"WM_QUERYUISTATE\",\n\t\t\t\t\t0x132 => \"WM_CTLCOLORMSGBOX\",\n\t\t\t\t\t0x133 => \"WM_CTLCOLOREDIT\",\n\t\t\t\t\t0x134 => \"WM_CTLCOLORLISTBOX\",\n\t\t\t\t\t0x135 => \"WM_CTLCOLORBTN\",\n\t\t\t\t\t0x136 => \"WM_CTLCOLORDLG\",\n\t\t\t\t\t0x137 => \"WM_CTLCOLORSCROLLBAR\",\n\t\t\t\t\t0x138 => \"WM_CTLCOLORSTATIC\",\n\t\t\t\t\t0x200 => \"WM_MOUSEMOVE\",\n\t\t\t\t\t0x201 => \"WM_LBUTTONDOWN\",\n\t\t\t\t\t0x202 => \"WM_LBUTTONUP\",\n\t\t\t\t\t0x203 => \"WM_LBUTTONDBLCLK\",\n\t\t\t\t\t0x204 => \"WM_RBUTTONDOWN\",\n\t\t\t\t\t0x205 => \"WM_RBUTTONUP\",\n\t\t\t\t\t0x206 => \"WM_RBUTTONDBLCLK\",\n\t\t\t\t\t0x207 => \"WM_MBUTTONDOWN\",\n\t\t\t\t\t0x208 => \"WM_MBUTTONUP\",\n\t\t\t\t\t0x209 => \"WM_MBUTTONDBLCLK\",\n\t\t\t\t\t0x20A => \"WM_MOUSEWHEEL\",\n\t\t\t\t\t0x20B => \"WM_XBUTTONDOWN\",\n\t\t\t\t\t0x20C => \"WM_XBUTTONUP\",\n\t\t\t\t\t0x20D => \"WM_XBUTTONDBLCLK\",\n\t\t\t\t\t0x20E => \"WM_MOUSEHWHEEL\",\n\t\t\t\t\t0x210 => \"WM_PARENTNOTIFY\",\n\t\t\t\t\t0x211 => \"WM_ENTERMENULOOP\",\n\t\t\t\t\t0x212 => \"WM_EXITMENULOOP\",\n\t\t\t\t\t0x213 => \"WM_NEXTMENU\",\n\t\t\t\t\t0x214 => \"WM_SIZING\",\n\t\t\t\t\t0x215 => \"WM_CAPTURECHANGED\",\n\t\t\t\t\t0x216 => \"WM_MOVING\",\n\t\t\t\t\t0x218 => \"WM_POWERBROADCAST\",\n\t\t\t\t\t0x219 => \"WM_DEVICECHANGE\",\n\t\t\t\t\t0x220 => \"WM_MDICREATE\",\n\t\t\t\t\t0x221 => \"WM_MDIDESTROY\",\n\t\t\t\t\t0x222 => \"WM_MDIACTIVATE\",\n\t\t\t\t\t0x223 => \"WM_MDIRESTORE\",\n\t\t\t\t\t0x224 => \"WM_MDINEXT\",\n\t\t\t\t\t0x225 => \"WM_MDIMAXIMIZE\",\n\t\t\t\t\t0x226 => \"WM_MDITILE\",\n\t\t\t\t\t0x227 => \"WM_MDICASCADE\",\n\t\t\t\t\t0x228 => \"WM_MDIICONARRANGE\",\n\t\t\t\t\t0x229 => \"WM_MDIGETACTIVE\",\n\t\t\t\t\t0x230 => \"WM_MDISETMENU\",\n\t\t\t\t\t0x231 => \"WM_ENTERSIZEMOVE\",\n\t\t\t\t\t0x232 => \"WM_EXITSIZEMOVE\",\n\t\t\t\t\t0x233 => \"WM_DROPFILES\",\n\t\t\t\t\t0x234 => \"WM_MDIREFRESHMENU\",\n\t\t\t\t\t0x238 => \"WM_POINTERDEVICECHANGE\",\n\t\t\t\t\t0x239 => \"WM_POINTERDEVICEINRANGE\",\n\t\t\t\t\t0x23A => \"WM_POINTERDEVICEOUTOFRANGE\",\n\t\t\t\t\t0x240 => \"WM_TOUCH\",\n\t\t\t\t\t0x241 => \"WM_NCPOINTERUPDATE\",\n\t\t\t\t\t0x242 => \"WM_NCPOINTERDOWN\",\n\t\t\t\t\t0x243 => \"WM_NCPOINTERUP\",\n\t\t\t\t\t0x245 => \"WM_POINTERUPDATE\",\n\t\t\t\t\t0x246 => \"WM_POINTERDOWN\",\n\t\t\t\t\t0x247 => \"WM_POINTERUP\",\n\t\t\t\t\t0x249 => \"WM_POINTERENTER\",\n\t\t\t\t\t0x24A => \"WM_POINTERLEAVE\",\n\t\t\t\t\t0x24B => \"WM_POINTERACTIVATE\",\n\t\t\t\t\t0x24C => \"WM_POINTERCAPTURECHANGED\",\n\t\t\t\t\t0x24D => \"WM_TOUCHHITTESTING\",\n\t\t\t\t\t0x24E => \"WM_POINTERWHEEL\",\n\t\t\t\t\t0x24F => \"WM_POINTERHWHEEL\",\n\t\t\t\t\t0x251 => \"WM_POINTERROUTEDTO\",\n\t\t\t\t\t0x252 => \"WM_POINTERROUTEDAWAY\",\n\t\t\t\t\t0x253 => \"WM_POINTERROUTEDRELEASED\",\n\t\t\t\t\t0x281 => \"WM_IME_SETCONTEXT\",\n\t\t\t\t\t0x282 => \"WM_IME_NOTIFY\",\n\t\t\t\t\t0x283 => \"WM_IME_CONTROL\",\n\t\t\t\t\t0x284 => \"WM_IME_COMPOSITIONFULL\",\n\t\t\t\t\t0x285 => \"WM_IME_SELECT\",\n\t\t\t\t\t0x286 => \"WM_IME_CHAR\",\n\t\t\t\t\t0x288 => \"WM_IME_REQUEST\",\n\t\t\t\t\t0x290 => \"WM_IME_KEYDOWN\",\n\t\t\t\t\t0x291 => \"WM_IME_KEYUP\",\n\t\t\t\t\t0x2A1 => \"WM_MOUSEHOVER\",\n\t\t\t\t\t0x2A3 => \"WM_MOUSELEAVE\",\n\t\t\t\t\t0x2A0 => \"WM_NCMOUSEHOVER\",\n\t\t\t\t\t0x2A2 => \"WM_NCMOUSELEAVE\",\n\t\t\t\t\t0x2B1 => \"WM_WTSSESSION_CHANGE\",\n\t\t\t\t\t0x2E0 => \"WM_DPICHANGED\",\n\t\t\t\t\t0x2E2 => \"WM_DPICHANGED_BEFOREPARENT\",\n\t\t\t\t\t0x2E3 => \"WM_DPICHANGED_AFTERPARENT\",\n\t\t\t\t\t0x2E4 => \"WM_GETDPISCALEDSIZE\",\n\t\t\t\t\t0x300 => \"WM_CUT\",\n\t\t\t\t\t0x301 => \"WM_COPY\",\n\t\t\t\t\t0x302 => \"WM_PASTE\",\n\t\t\t\t\t0x303 => \"WM_CLEAR\",\n\t\t\t\t\t0x304 => \"WM_UNDO\",\n\t\t\t\t\t0x305 => \"WM_RENDERFORMAT\",\n\t\t\t\t\t0x306 => \"WM_RENDERALLFORMATS\",\n\t\t\t\t\t0x307 => \"WM_DESTROYCLIPBOARD\",\n\t\t\t\t\t0x308 => \"WM_DRAWCLIPBOARD\",\n\t\t\t\t\t0x309 => \"WM_PAINTCLIPBOARD\",\n\t\t\t\t\t0x30A => \"WM_VSCROLLCLIPBOARD\",\n\t\t\t\t\t0x30B => \"WM_SIZECLIPBOARD\",\n\t\t\t\t\t0x30C => \"WM_ASKCBFORMATNAME\",\n\t\t\t\t\t0x30D => \"WM_CHANGECBCHAIN\",\n\t\t\t\t\t0x30E => \"WM_HSCROLLCLIPBOARD\",\n\t\t\t\t\t0x30F => \"WM_QUERYNEWPALETTE\",\n\t\t\t\t\t0x310 => \"WM_PALETTEISCHANGING\",\n\t\t\t\t\t0x311 => \"WM_PALETTECHANGED\",\n\t\t\t\t\t0x312 => \"WM_HOTKEY\",\n\t\t\t\t\t0x317 => \"WM_PRINT\",\n\t\t\t\t\t0x318 => \"WM_PRINTCLIENT\",\n\t\t\t\t\t0x319 => \"WM_APPCOMMAND\",\n\t\t\t\t\t0x31A => \"WM_THEMECHANGED\",\n\t\t\t\t\t0x31D => \"WM_CLIPBOARDUPDATE\",\n\t\t\t\t\t0x31E => \"WM_DWMCOMPOSITIONCHANGED\",\n\t\t\t\t\t0x31F => \"WM_DWMNCRENDERINGCHANGED\",\n\t\t\t\t\t0x320 => \"WM_DWMCOLORIZATIONCOLORCHANGED\",\n\t\t\t\t\t0x321 => \"WM_DWMWINDOWMAXIMIZEDCHANGE\",\n\t\t\t\t\t0x323 => \"WM_DWMSENDICONICTHUMBNAIL\",\n\t\t\t\t\t0x326 => \"WM_DWMSENDICONICLIVEPREVIEWBITMAP\",\n\t\t\t\t\t0x33F => \"WM_GETTITLEBARINFOEX\",\n\t\t\t\t\t0x8000 => \"WM_APP\",\n\t\t\t\t\t0x400 => \"WM_USER\",\n\t\t\t\t\t_ => null\n\t\t\t\t};\n\t\t\t\t#endregion\n\t\t\t\treturn (s, 0);\n\t\t\t}\n\t\t}\n\t\t[ThreadStatic] static uint s_pm_counter;\n\n#if !true //this script creates the switch { ... }\n//var a=new List<string>();\nprint.clear();\nvar b = new StringBuilder(\"var s = m switch {\\r\\n\");\nvar s1 = File.ReadAllText(@\"C:\\code\\au\\Au\\Api\\Api_const.cs\");\nforeach (var m in s1.RxFindAll(@\"(?m)^\\h*internal const int (WM_\\w+) *= *(\\w+);\")) {\n\tvar s = m[1].Value;\n\tif (s.Ends(\"FIRST\") || s.Ends(\"LAST\") || s.Starts(\"WM_PSD_\") || s.Starts(\"WM_DDE_\") || s.Starts(\"WM_CHOOSEFONT_\") || s == \"WM_WININICHANGE\") {\n\t\t//print.it(s);\n\t\tcontinue;\n\t}\n\t//print.it(s, m[2]);\n\t//a.Add(s);\n\tb.AppendFormat(\"{0} => \\\"{1}\\\",\\r\\n\", m[2].Value, s);\n}\nb.Append(\"_ => null};\\r\\nreturn (s, 0);\");\n//a.Sort();\n//print.it(a);\nvar s2 = b.ToString();\nprint.it(s2);\n#endif\n\n\t\t/// <summary>\n\t\t/// Writes a Windows message to the output, unless it is specified in <i>options</i>.\n\t\t/// </summary>\n\t\tpublic static void PrintMsg(wnd w, int msg, nint wParam, nint lParam, PrintMsgOptions options = null, [CallerMemberName] string m_ = null) {\n\t\t\tif (PrintMsg(out string s, w, msg, wParam, lParam, options, m_)) print.it(s);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Writes a Windows message to a string.\n\t\t/// If the message is specified in <i>options</i>, sets <c>s=null</c> and returns <c>false</c>.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// The <i>m</i> parameter also accepts <see cref=\"System.Windows.Interop.MSG\"/> (WPF) and <see cref=\"System.Windows.Forms.Message\"/>.\n\t\t/// </remarks>\n\t\tpublic static bool PrintMsg(out string s, in MSG m, PrintMsgOptions options = null, [CallerMemberName] string m_ = null) {\n\t\t\treturn PrintMsg(out s, m.hwnd, m.message, m.wParam, m.lParam, options, m_);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Writes a Windows message to the output, unless it is specified in <i>options</i>.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// The <i>m</i> parameter also accepts <see cref=\"System.Windows.Interop.MSG\"/> (WPF) and <see cref=\"System.Windows.Forms.Message\"/>.\n\t\t/// </remarks>\n\t\tpublic static void PrintMsg(in MSG m, PrintMsgOptions options = null, [CallerMemberName] string m_ = null) {\n\t\t\tPrintMsg(m.hwnd, m.message, m.wParam, m.lParam, options, m_);\n\t\t}\n\n\t\t#endregion\n\n\t\t/// <summary>\n\t\t/// Simple non-OLE drag operation.\n\t\t/// </summary>\n\t\t/// <returns><c>true</c> if dropped, <c>false</c> if canceled.</returns>\n\t\t/// <param name=\"window\">Window or control that owns the drag operation. Must be of this thread.</param>\n\t\t/// <param name=\"mouseButton\">Mouse button that is used for the drag operation: <c>Left</c>, <c>Right</c>, <c>Middle</c>.</param>\n\t\t/// <param name=\"onMouseKeyMessage\">Callback function, called on each received mouse/key message. Optional.</param>\n\t\tpublic static bool DragLoop(AnyWnd window, MButtons mouseButton = MButtons.Left, Action<WDLArgs> onMouseKeyMessage = null) {\n\t\t\twnd w = window.Hwnd;\n\t\t\tApi.SetCapture(w);\n\n\t\t\tbool R = false;\n\t\t\tvar x = new WDLArgs();\n\t\t\tfor (; ; ) {\n\t\t\t\tif (Api.GetCapture() != w) return false;\n\t\t\t\tif (!Api.GetMessage(out x.msg)) {\n\t\t\t\t\tif (x.msg.message == Api.WM_QUIT) Api.PostQuitMessage((int)x.msg.wParam);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tbool call = false;\n\t\t\t\tint m = x.msg.message;\n\t\t\t\tif (m >= Api.WM_MOUSEFIRST && m <= Api.WM_MOUSELAST) {\n\t\t\t\t\tif (m == Api.WM_LBUTTONUP) {\n\t\t\t\t\t\tif (R = mouseButton.Has(MButtons.Left)) break;\n\t\t\t\t\t} else if (m == Api.WM_RBUTTONUP) {\n\t\t\t\t\t\tif (R = mouseButton.Has(MButtons.Right)) break;\n\t\t\t\t\t} else if (m == Api.WM_MBUTTONUP) {\n\t\t\t\t\t\tif (R = mouseButton.Has(MButtons.Middle)) break;\n\t\t\t\t\t}\n\t\t\t\t\tcall = true;\n\t\t\t\t} else if (m == Api.WM_KEYDOWN || m == Api.WM_KEYUP || m == Api.WM_SYSKEYDOWN || m == Api.WM_SYSKEYUP) {\n\t\t\t\t\t//on key down/up caller may want to update cursor when eg Ctrl pressed/released\n\t\t\t\t\tif (x.msg.wParam == (byte)KKey.Escape) break;\n\t\t\t\t\tcall = true;\n\t\t\t\t}\n\n\t\t\t\tif (call && onMouseKeyMessage != null) {\n\t\t\t\t\tonMouseKeyMessage(x);\n\t\t\t\t\tif (x._stopped) break;\n\t\t\t\t\tif (x.cursor != default) {\n\t\t\t\t\t\tApi.SetCursor(x.cursor);\n\t\t\t\t\t\tx.cursor = default;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tApi.DispatchMessage(x.msg);\n\t\t\t}\n\n\t\t\tApi.ReleaseCapture();\n\t\t\treturn R;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Waits while there is no active window.\n\t\t/// </summary>\n\t\t/// <param name=\"doEvents\">While waiting call <see cref=\"wait.doEvents\"/> to process Windows messages etc.</param>\n\t\t/// <remarks>\n\t\t/// When there is no active window, functions <see cref=\"wnd.active\"/> and API <ms>GetForegroundWindow</ms> return 0.\n\t\t/// It sometimes happens after closing, minimizing or switching the active window, briefly until another window becomes active.\n\t\t/// This function waits max 500 ms, then returns <c>false</c> if there is no active window.\n\t\t/// Don't need to call this after calling functions of this library.\n\t\t/// </remarks>\n\t\tpublic static bool WaitForAnActiveWindow(bool doEvents = false) {\n\t\t\tfor (int i = 0; ;) {\n\t\t\t\tif (doEvents) wait.doEvents();\n\t\t\t\tif (!wnd.active.Is0) return true;\n\t\t\t\tif (++i == 32) break;\n\t\t\t\twait.ms(i);\n\t\t\t}\n\t\t\treturn false;\n\t\t\t//Call WaitForAnActiveWindow(true) after showing a dialog API.\n\t\t\t//\tIn a thread that does not process messages, after closing a dialog may be not updated key states.\n\t\t\t//\tProcessing remaining unprocessed messages fixes it.\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Temporarily enables this process to activate windows with API <ms>SetForegroundWindow</ms>.\n\t\t/// </summary>\n\t\t/// <returns><c>false</c> if failed.</returns>\n\t\t/// <param name=\"processId\">Process id. If not 0, enables that process to activate windows too. If -1, all processes will be enabled.</param>\n\t\t/// <remarks>\n\t\t/// In some cases you may need this function because Windows often disables API <ms>SetForegroundWindow</ms> to not allow background applications to activate windows while the user is working (using keyboard/mouse) with the currently active window. Then <c>SetForegroundWindow</c> usually just makes the window's taskbar button flash.\n\t\t/// Usually you don't call <c>SetForegroundWindow</c> directly. It is called by some other functions.\n\t\t/// Don't need to call this function before calling <see cref=\"wnd.Activate\"/> and other functions of this library that activate windows.\n\t\t/// </remarks>\n\t\tpublic static bool EnableActivate(int processId = 0) {\n\t\t\tif (!wnd.Internal_.EnableActivate(false)) return false;\n\t\t\treturn processId == 0 || Api.AllowSetForegroundWindow(processId);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Calls API <ms>PostThreadMessage</ms>. \n\t\t/// </summary>\n\t\t/// <returns><c>false</c> if failed. Supports <see cref=\"lastError\"/>.</returns>\n\t\tpublic static bool PostThreadMessage(int threadId, int message, nint wParam = 0, nint lParam = 0) {\n\t\t\treturn Api.PostThreadMessage(threadId, message, wParam, lParam);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Subclasses a window.\n\t\t/// </summary>\n\t\t/// <param name=\"w\">A window or control of this thread.</param>\n\t\t/// <param name=\"proc\">The new window procedure. It is called on every message received by the window (unless blocked by another subclass added later). Let it call <see cref=\"DefSubclassProc\"/>, except when you want to block the message.</param>\n\t\t/// <returns>A cookie for <see cref=\"Unsubclass\"/>. Returns <c>null</c> if failed.</returns>\n\t\t/// <remarks>\n\t\t/// Uses API <ms>SetWindowSubclass</ms>.\n\t\t/// Implicitly unsubclasses when the window is destroyed.\n\t\t/// Protects <i>proc</i> from GC for as long as need.\n\t\t/// </remarks>\n\t\tpublic static object Subclass(wnd w, WNDPROC proc) {\n\t\t\tApi.SUBCLASSPROC sp = null;\n\t\t\tif (!Api.SetWindowSubclass(w, sp = _WndProc, 1)) return null;\n\t\t\t(t_asp ??= new()).Add(sp);\n\t\t\tnint _WndProc(wnd w, int msg, nint wp, nint lp, nint idSubclass, nint refData) {\n\t\t\t\tvar r = proc(w, msg, wp, lp);\n\t\t\t\tif (msg == Api.WM_NCDESTROY) if (Api.RemoveWindowSubclass(w, sp, 1)) t_asp.Remove(sp);\n\t\t\t\treturn r;\n\t\t\t}\n\t\t\treturn new _SubclassCookie(w, sp);\n\t\t}\n\t\t[ThreadStatic] static List<Api.SUBCLASSPROC> t_asp; //GC\n\t\trecord class _SubclassCookie(wnd w, Api.SUBCLASSPROC sp);\n\n\t\t/// <summary>\n\t\t/// Unsubclasses window subclassed by <see cref=\"Subclass\"/>.\n\t\t/// Unsubclassing is optional; the window is implicitly unsubclassed when closed.\n\t\t/// </summary>\n\t\t/// <param name=\"cookie\">The return value of <see cref=\"Subclass\"/>.</param>\n\t\tpublic static void Unsubclass(object cookie) {\n\t\t\tif (cookie == null) return;\n\t\t\tif (cookie is not _SubclassCookie c) throw new ArgumentException();\n\t\t\tif (Api.RemoveWindowSubclass(c.w, c.sp, 1) || !c.w.IsAlive) t_asp.Remove(c.sp);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Let your callback function used with <see cref=\"Subclass\"/> call this function and return its return value. After or before processing the message.\n\t\t/// </summary>\n\t\tpublic static nint DefSubclassProc(wnd w, int msg, nint wp, nint lp)\n\t\t\t=> Api.DefSubclassProc(w, msg, wp, lp);\n\t}\n}\n\nnamespace Au.Types {\n\t/// <summary>\n\t/// Options for <see cref=\"WndUtil.PrintMsg\"/>.\n\t/// </summary>\n\tpublic class PrintMsgOptions {\n\t\t///\n\t\tpublic PrintMsgOptions() { }\n\n\t\t/// <summary>\n\t\t/// Sets <see cref=\"Skip\"/>.\n\t\t/// </summary>\n\t\tpublic PrintMsgOptions(params int[] skip) { Skip = skip; }\n\n\t\t/// <summary>\n\t\t/// Prepend counter 1, 2, 3...\n\t\t/// Default <c>true</c>.\n\t\t/// </summary>\n\t\tpublic bool Number { get; set; } = true;\n\n\t\t/// <summary>\n\t\t/// Prepend one or more tabs if the caller function (usually a window procedure) is called recursively.\n\t\t/// Default <c>true</c>.\n\t\t/// </summary>\n\t\tpublic bool Indent { get; set; } = true;\n\n\t\t/// <summary>\n\t\t/// Ignore these messages.\n\t\t/// To specify a range of messages, use two array elements: first message and negative last message.\n\t\t/// </summary>\n\t\tpublic int[] Skip { get; set; }\n\n\t\t/// <summary>\n\t\t/// Append window classname, name and rectangle.\n\t\t/// </summary>\n\t\tpublic bool WindowProperties { get; set; }\n\t}\n\n\t/// <summary>\n\t/// <see cref=\"WndUtil.DragLoop\"/> callback function arguments.\n\t/// </summary>\n\tpublic class WDLArgs {\n\t\t/// <summary>\n\t\t/// Current message retrieved by API <ms>GetMessage</ms>.\n\t\t/// API <ms>MSG</ms>.\n\t\t/// </summary>\n\t\tpublic MSG msg;\n\n\t\t/// <summary>\n\t\t/// Native cursor handle. The callback function can set this to temporarily set cursor.\n\t\t/// </summary>\n\t\tpublic IntPtr cursor;\n\n\t\t/// <summary>\n\t\t/// The callback function can call this to end the operation.\n\t\t/// </summary>\n\t\tpublic void Stop() { _stopped = true; }\n\t\tinternal bool _stopped;\n\t}\n\n\t/// <summary>\n\t/// Used with <see cref=\"WndUtil.RegisterWindowClass\"/>.\n\t/// </summary>\n\tpublic class RWCEtc {\n#pragma warning disable 1591 //XML doc\n\t\tpublic uint style;\n\t\tpublic int cbClsExtra;\n\t\tpublic int cbWndExtra;\n\t\tpublic IntPtr hIcon;\n\t\tpublic IntPtr hCursor;\n\t\tpublic MCursor mCursor;\n\t\tpublic nint hbrBackground;\n\t\tpublic IntPtr hIconSm;\n#pragma warning restore 1591 //XML doc\n\t}\n}\n"
  },
  {
    "path": "Au/wnd/inactive/desktop.cs",
    "content": "﻿//rejected.\n//\tExisting functions are rarely used and can be invoked with hotkeys, eg Win+D = Show Desktop, Win+Tab = Task View, Win+M = Minimize All, Win+Shift+M = Undo.\n//\tMaybe will be added in the future, when added more functions, eg for virtual desktops.\n#if !true\nnamespace Au\n{\n\t/// <summary>\n\t/// Arranges windows, shows/hides desktop. The same as the taskbar right-click menu commands.\n\t/// </summary>\n\tpublic static class desktop\n\t{\n\t\t/// <summary>\n\t\t/// Shows or hides desktop.\n\t\t/// If there are non-minimized main windows, minimizes them. Else restores windows recently minimized by this function.\n\t\t/// </summary>\n\t\tpublic static void toggleShowDesktop() => _Do(0);\n\n\t\t/// <summary>\n\t\t/// Minimizes main windows.\n\t\t/// </summary>\n\t\tpublic static void minimizeWindows() => _Do(1);\n\n\t\t/// <summary>\n\t\t/// Cascades non-minimized main windows.\n\t\t/// </summary>\n\t\tpublic static void cascadeWindows() => _Do(3);\n\n\t\t/// <summary>\n\t\t/// Arranges non-minimized main windows horizontally or vertically.\n\t\t/// </summary>\n\t\tpublic static void tileWindows(bool vertically) => _Do(vertically ? 5 : 4);\n\n\t\t/// <summary>\n\t\t/// Restores windows recently minimized, cascaded or tiled with other functions of this class.\n\t\t/// </summary>\n\t\tpublic static void undoMinimizeEtc() => _Do(2);\n\n\t\t/// <summary>\n\t\t/// Shows the Task View, aka Window Switcher.\n\t\t/// May not work on some Windows versions.\n\t\t/// </summary>\n\t\tpublic static void taskView() => _Do(6);\n\n\t\tstatic void _Do(int what) {\n\t\t\ttry {\n\t\t\t\tdynamic shell = Activator.CreateInstance(Type.GetTypeFromProgID(\"Shell.Application\")); //speed: faster than calling a method\n\t\t\t\tswitch (what) {\n\t\t\t\tcase 0: shell.ToggleDesktop(); break;\n\t\t\t\tcase 1: shell.MinimizeAll(); break;\n\t\t\t\tcase 2: shell.UndoMinimizeALL(); break;\n\t\t\t\tcase 3: shell.CascadeWindows(); break;\n\t\t\t\tcase 4: shell.TileHorizontally(); break;\n\t\t\t\tcase 5: shell.TileVertically(); break;\n\t\t\t\tcase 6: shell.WindowSwitcher(); break;\n\t\t\t\t}\n\t\t\t\tMarshal.ReleaseComObject(shell);\n\n\t\t\t\t//The COM object does not do exactly the same as the true Explorer commands.\n\t\t\t\t//Eg MinimizeAll does not activete desktop. Then a minimized window is active.\n\t\t\t\tif (what == 1 && wnd.active.IsMinimized && wnd.getwnd.desktop(out var wd, out _)) wd.ActivateL();\n\t\t\t}\n\t\t\tcatch { }\n\n\t\t\twnd.getwnd.shellWindow.MinimalSleepIfOtherThread_();\n\t\t}\n\t}\n\n\t//FUTURE: use IVirtualDesktopManager to manage virtual desktops.\n\t//Its MoveWindowToDesktop does not work with windows of other processes, but we can inject a dll into another process.\n\t//The inteface also has IsWindowOnCurrentVirtualDesktop and GetWindowDesktopId.\n\t//Also there are internal/undocumented interfaces to add/remove/switch desktops etc. There is a GitHub library. And another library that injects.\n}\n#endif\n"
  },
  {
    "path": "Au/wnd/wnd.cs",
    "content": "namespace Au;\n\n/// <summary>\n/// A variable of <c>wnd</c> type represents a window or control. It is a window handle, also known as <ms>HWND</ms>.\n/// </summary>\n/// <remarks>\n/// <c>wnd</c> functions can be used with windows and controls of any process/thread. Also can be used with .NET form/control and WPF window class variables, like <c>wnd w=form.Hwnd(); w.Method(...);</c>.\n/// \n/// There are two main types of windows - top-level windows and controls. Controls are child windows of top-level windows.\n/// \n/// More window functions are in types named like <c>WndX</c>, in namespace <c>Au.More</c>. They are used mostly in programming, rarely in automation scripts.\n/// \n/// What happens when a <c>wnd</c> member function fails:\n/// - Functions that get window properties don't throw exceptions. They return <c>false</c>/0/<c>null</c>/empty. Most of them support <see cref=\"lastError\"/>, and it is mentioned in function documentation.\n/// - Many functions that change window properties throw exception. Exceptions are listed in function documentation. Almost all these functions throw only <see cref=\"AuWndException\"/>.\n/// - Other functions that change window properties return <c>false</c>. They are more often used in programming than in automation scripts.\n/// - When a \"find\" function does not find the window or control, it returns <c>default(wnd)</c> (window handle 0). Then <see cref=\"Is0\"/> will return <c>true</c>.\n/// - If a function does not follow these rules, it is mentioned in function documentation.\n/// \n/// Many functions fail if the window's process has a higher [](xref:uac) integrity level (administrator, uiAccess) than this process, unless this process has uiAccess level. Especially the functions that change window properties. Some functions that still work: <c>Activate</c>, <c>ActivateL</c>, <c>ShowMinimized</c>, <c>ShowNotMinimized</c>, <c>ShowNotMinMax</c>, <c>Close</c>.\n/// \n/// The <c>wnd</c> type can be used with native Windows API functions without casting. Use <c>wnd</c> for the parameter type in the declaration, like <c>[DllImport(...)] static extern bool NativeFunction(wnd hWnd, ...)</c>.\n/// \n/// See also: <ms>Window Features</ms>.\n/// </remarks>\n/// <example>\n/// <code><![CDATA[\n/// wnd w = wnd.find(\"* - Notepad\");\n/// if(w.Is0) { print.it(\"window not found\"); return; }\n/// w.Activate();\n/// wnd c = w.Child(cn: \"Button\");\n/// print.it(c.Name);\n/// ]]></code>\n/// </example>\npublic unsafe partial struct wnd : IEquatable<wnd>, IComparable<wnd> {\n#if false\n\t/// <remarks>\n\t/// Why <c>wnd</c> is struct, not class:\n\t/// \tAdvantages:\n\t/// \t- Lightweight. Same as <c>nint</c> (8 or 4 bytes).\n\t/// \t- Easier to create overloaded functions that have a window parameter. If it was a class, then a <c>null</c> argument could be ambiguous if eg could also be a string etc.\n\t/// \t- When a find-window function does not find the window, calling next function (without checking the return value) does not throw <c>null</c>-reference exception. Instead the function can throw a more specific exception or just return <c>false</c> etc.\n\t/// \t- The handle actually already is a reference (to a window object managed by the OS). We don't own the object; we usually don't need to destroy the window finally; it is more like a numeric window id.\n\t/// \t- Code where a window argument is <c>default(wnd)</c> is more clear. If it would be <c>null</c>, then it is unclear how the function interprets it: as a 0 handle or as \"don't use it\". Now if we want a \"don't use it\" behavior, we'll use an overload.\n\t/// \t- In my experience, it makes programming/scripting easier than if it would be a class. Because windows are not found so often (in automation scripts). A find-window function could throw a \"not found\" exception, but it is not good (it's easier to check the return value than to use try/catch or throwing/non-throwing overloads).\n\t/// \t- Probably it is not a \"bad practice\" to have a struct with many member functions, because eg the .NET <c>DateTime</c> is struct.\n\t/// \t\n\t/// \tDisadvantages:\n\t/// \t- Cannot be a base class of other classes. Workaround: Use it as a public field or property of the other class (or struct); in some cases it can be even better, because <c>wnd</c> has very many methods, and the non-inherited methods of that class would be difficult to find; now they are separated, and can be used like <c>x.NewClassMethod()</c> and <c>x.w.WndMethod()</c>; anyway, in most cases we'll need the new window classes only for the functions that they add, not for <c>wnd</c> functions, eg we would use a class <c>ButtonWnd</c> mostly only for button functions, not for general window functions.\n\t/// \t- In some cases C# does not allow to call a property-set function. <c>wnd</c> has few such functions, maybe none.\n\t/// \t\n\t/// </remarks>\n\t//note: don't use :IWin32Window, because it loads System.Windows.Forms.dll always when wnd used.\n#endif\n\t\n\treadonly nint _h;\n\t\n\t#region constructors, operators, overrides\n\t\n#pragma warning disable 1591 //XML doc\n\tpublic wnd(nint hwnd) { _h = hwnd; }\n\t\n\t//note: don't need implicit conversions. It creates more problems than is useful.\n\t\n\tpublic static explicit operator wnd(nint hwnd) => new(hwnd);\n\tpublic static explicit operator nint(wnd w) => w.Handle;\n\tpublic static explicit operator wnd(int hwnd) => new(hwnd);\n\tpublic static explicit operator int(wnd w) => (int)w._h;\n\t\n\t/// <summary>\n\t/// Converts from a special handle value.\n\t/// </summary>\n\t/// <param name=\"hwnd\">See API <ms>SetWindowPos</ms>.</param>\n\tpublic static implicit operator wnd(SpecHWND hwnd) => new((int)hwnd);\n\t\n\t/// <summary>Compares window handles.</summary>\n\tpublic static bool operator ==(wnd w1, wnd w2) => w1._h == w2._h;\n\t\n\t/// <summary>Compares window handles.</summary>\n\tpublic static bool operator !=(wnd w1, wnd w2) => w1._h != w2._h;\n\t\n\t//Prevent accidental usage wnd==null. The C# compiler used to allow it without a warning; level-5 warning since C# 9. Bad: disables wnd==wnd?.\n\t//[Obsolete(\"Replace wnd==wnd? with wnd.Equals(wnd?). Replace wnd==null with wnd.Is0.\", true), NoDoc]\n\t//public static bool operator ==(wnd w1, wnd? w2) => false;\n\t//[Obsolete(\"Replace wnd==wnd? with wnd.Equals(wnd?). Replace wnd==null with wnd.Is0.\", true), NoDoc]\n\t//public static bool operator !=(wnd w1, wnd? w2) => true;\n\t//[Obsolete(\"Replace wnd==wnd? with wnd.Equals(wnd?). Replace wnd==null with wnd.Is0.\", true), NoDoc]\n\t//public static bool operator ==(wnd? w1, wnd w2) => false;\n\t//[Obsolete(\"Replace wnd==wnd? with wnd.Equals(wnd?). Replace wnd==null with wnd.Is0.\", true), NoDoc]\n\t//public static bool operator !=(wnd? w1, wnd w2) => true;\n#pragma warning restore 1591 //XML doc\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if <c>w != null</c> and <c>w.Value == this</c>.\n\t/// </summary>\n\tpublic bool Equals(wnd? w) => w != null && w.GetValueOrDefault() == this;\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if <i>obj</i> is <see cref=\"wnd\"/> and contains the same window handle.\n\t/// </summary>\n\tpublic override bool Equals(object obj) => obj is wnd w && this == w;\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if <c>other == this</c>.\n\t/// </summary>\n\tpublic bool Equals(wnd other) => other == this; //IEquatable<wnd>.Equals, to avoid boxing with eg Dictionary<wnd, T2>\n\t\n\t/// <summary>\n\t/// Implements <see cref=\"IComparable{T}\"/>. It allows to sort a collection.\n\t/// </summary>\n\tpublic int CompareTo(wnd other) => _h.CompareTo(other);\n\t\n\t///\n\tpublic override int GetHashCode() => (int)_h;\n\t//window handles are always 32-bit int, although in a 64-bit process stored in 64-bit variables.\n\t\n\t/// <summary>\n\t/// Gets window handle as <c>IntPtr</c>.\n\t/// Code <c>w.Handle</c> is the same as <c>(IntPtr)w</c> .\n\t/// </summary>\n\tpublic IntPtr Handle => _h;\n\t\n\t/// <summary>\n\t/// Formats string <c>$\"{handle}  {ClassName}  \\\"{Name}\\\"  {ProgramName}  {Rect}\"</c>.\n\t/// </summary>\n\tpublic override string ToString() {\n\t\tif (Is0) return \"0\";\n\t\tvar cn = ClassName;\n\t\tvar sh = Handle.ToString();\n\t\tif (cn == null) return sh + \" <invalid handle>\";\n\t\tstring s = Name;\n\t\tif (s != null) s = s.Escape(limit: 250);\n\t\treturn $\"{sh}  {cn}  \\\"{s}\\\"  {ProgramName}  {Rect.ToString()}\";\n\t}\n\t\n\t#endregion\n\t\n\t#region send/post message\n\t\n\t/// <summary>\n\t/// Calls API <ms>SendMessage</ms>.\n\t/// </summary>\n\t/// <remarks>Supports <see cref=\"lastError\"/>.</remarks>\n\tpublic nint Send(int message, nint wParam = 0, nint lParam = 0) {\n\t\tDebug_.PrintIf(Is0);\n\t\treturn Api.SendMessage(this, message, wParam, lParam);\n\t}\n\t\n\t/// <summary>\n\t/// Calls API <ms>SendMessage</ms> where <i>lParam</i> is string.\n\t/// </summary>\n\t/// <remarks>Supports <see cref=\"lastError\"/>.</remarks>\n\tpublic nint Send(int message, nint wParam, string lParam) {\n\t\tDebug_.PrintIf(Is0);\n\t\tfixed (char* p = lParam)\n\t\t\treturn Api.SendMessage(this, message, wParam, (nint)p);\n\t\t//info: don't use overload, then eg ambiguous if null.\n\t}\n\t\n\t/// <summary>\n\t/// Calls API <ms>SendMessage</ms> where <i>lParam</i> is any pointer.\n\t/// </summary>\n\t/// <remarks>Supports <see cref=\"lastError\"/>.</remarks>\n\tpublic nint Send(int message, nint wParam, void* lParam) {\n\t\tDebug_.PrintIf(Is0);\n\t\treturn Api.SendMessage(this, message, wParam, (nint)lParam);\n\t\t//info: don't use overload, then eg ambiguous if null.\n\t}\n\t\n\t//rejected: overload without out result parameter. Confusing intellisense when bad types of other parameters. Not so often used, and not so hard to put ', out _'.\n\t///// <summary>\n\t///// Calls API <ms>SendMessageTimeout</ms>.\n\t///// Returns its return value (<c>false</c> if failed). Supports <see cref=\"lastError\"/>.\n\t///// </summary>\n\t//public bool SendTimeout(int millisecondsTimeout, int message, nint wParam = 0, nint lParam = 0, SMTFlags flags = SMTFlags.ABORTIFHUNG) {\n\t//\tDebug_.PrintIf(Is0);\n\t//\treturn 0 != Api.SendMessageTimeout(this, message, wParam, lParam, flags, millisecondsTimeout, out _);\n\t//}\n\t\n\t/// <summary>\n\t/// Calls/returns API <ms>SendMessageTimeout</ms> and gets the result of the message processing.\n\t/// </summary>\n\t/// <returns><c>false</c> if failed. Supports <see cref=\"lastError\"/>.</returns>\n\tpublic bool SendTimeout(int millisecondsTimeout, out nint result, int message, nint wParam = 0, nint lParam = 0, SMTFlags flags = SMTFlags.ABORTIFHUNG) {\n\t\tDebug_.PrintIf(Is0);\n\t\treturn 0 != Api.SendMessageTimeout(this, message, wParam, lParam, flags, millisecondsTimeout, out result);\n\t}\n\t\n\t/// <summary>\n\t/// Calls/returns API <ms>SendMessageTimeout</ms> where <i>lParam</i> is string.\n\t/// </summary>\n\t/// <returns><c>false</c> if failed. Supports <see cref=\"lastError\"/>.</returns>\n\tpublic bool SendTimeout(int millisecondsTimeout, out nint result, int message, nint wParam, string lParam, SMTFlags flags = SMTFlags.ABORTIFHUNG) {\n\t\tDebug_.PrintIf(Is0);\n\t\tresult = 0;\n\t\tfixed (char* p = lParam)\n\t\t\treturn 0 != Api.SendMessageTimeout(this, message, wParam, (nint)p, flags, millisecondsTimeout, out result);\n\t}\n\t\n\t/// <summary>\n\t/// Calls/returns API <ms>SendMessageTimeout</ms> and gets the result of the message processing.\n\t/// </summary>\n\t/// <returns><c>false</c> if failed. Supports <see cref=\"lastError\"/>.</returns>\n\tpublic bool SendTimeout(int millisecondsTimeout, out nint result, int message, nint wParam, void* lParam, SMTFlags flags = SMTFlags.ABORTIFHUNG) {\n\t\tDebug_.PrintIf(Is0);\n\t\treturn 0 != Api.SendMessageTimeout(this, message, wParam, (nint)lParam, flags, millisecondsTimeout, out result);\n\t}\n\t\n\t/// <summary>\n\t/// Calls/returns API <ms>SendNotifyMessage</ms>.\n\t/// </summary>\n\t/// <returns><c>false</c> if failed. Supports <see cref=\"lastError\"/>.</returns>\n\tpublic bool SendNotify(int message, nint wParam = 0, nint lParam = 0) {\n\t\tDebug_.PrintIf(Is0);\n\t\treturn Api.SendNotifyMessage(this, message, wParam, lParam);\n\t}\n\t\n\t/// <summary>\n\t/// Calls/returns API <ms>PostMessage</ms>.\n\t/// </summary>\n\t/// <returns><c>false</c> if failed. Supports <see cref=\"lastError\"/>.</returns>\n\t/// <seealso cref=\"WndUtil.PostThreadMessage\"/>\n\tpublic bool Post(int message, nint wParam = 0, nint lParam = 0) {\n\t\t//Debug.Assert(!Is0); //no, can be used for \"post thread message\"\n\t\treturn Api.PostMessage(this, message, wParam, lParam);\n\t}\n\t\n\t#endregion\n\t\n\t#region throw, valid\n\t\n\t/// <summary>\n\t/// If <see cref=\"Is0\"/>, throws <see cref=\"AuWndException\"/>.\n\t/// </summary>\n\t/// <returns>This.</returns>\n\t/// <exception cref=\"AuWndException\"></exception>\n\tpublic wnd ThrowIf0() {\n\t\tif (Is0) throw new AuWndException(this, Api.ERROR_INVALID_WINDOW_HANDLE);\n\t\treturn this;\n\t}\n\t\n\t/// <summary>\n\t/// If <see cref=\"Is0\"/> or not <see cref=\"IsAlive\"/>, throws <see cref=\"AuWndException\"/>.\n\t/// </summary>\n\t/// <returns>This.</returns>\n\t/// <exception cref=\"AuWndException\"></exception>\n\tpublic wnd ThrowIfInvalid() {\n\t\tif (Is0 || !Api.IsWindow(this)) throw new AuWndException(this, Api.ERROR_INVALID_WINDOW_HANDLE);\n\t\treturn this;\n\t}\n\t\n\t///// <summary>\n\t///// If <see cref=\"Is0\"/>, throws <see cref=\"AuWndException\"/>. Returns <c>!IsAlive</c>.\n\t///// </summary>\n\t///// <exception cref=\"AuWndException\"></exception>\n\t//public bool IsInvalidThrowIf0()\n\t//{\n\t//\tif(Is0) throw new AuWndException(this, Api.ERROR_INVALID_WINDOW_HANDLE);\n\t//\treturn !Api.IsWindow(this);\n\t//}\n\t//CONSIDER: in many places replace ThrowIfInvalid with ThrowIf0 or IsInvalidThrowIf0.\n\t\n\t/// <summary>\n\t/// Throws <see cref=\"AuWndException\"/> that uses the last Windows API error (code and message).\n\t/// Also the message depends on whether the window handle is 0/invalid.\n\t/// </summary>\n\t/// <exception cref=\"AuWndException\"></exception>\n\tpublic void ThrowUseNative() {\n\t\tthrow new AuWndException(this, 0);\n\t}\n\t\n\t/// <summary>\n\t/// Throws <see cref=\"AuWndException\"/> that uses the specified Windows API error code and its message.\n\t/// Also the message depends on whether the window handle is 0/invalid.\n\t/// </summary>\n\t/// <exception cref=\"AuWndException\"></exception>\n\tpublic void ThrowUseNative(int errorCode) {\n\t\tthrow new AuWndException(this, errorCode);\n\t}\n\t\n\t/// <summary>\n\t/// Throws <see cref=\"AuWndException\"/> that uses <i>mainMessage</i> and the last Windows API error (code and message).\n\t/// Also the message depends on whether the window handle is 0/invalid.\n\t/// </summary>\n\t/// <exception cref=\"AuWndException\"></exception>\n\tpublic void ThrowUseNative(string mainMessage) {\n\t\tthrow new AuWndException(this, 0, mainMessage);\n\t}\n\t\n\t/// <summary>\n\t/// Throws <see cref=\"AuWndException\"/> that uses <i>mainMessage</i> and the specified Windows API error code.\n\t/// Also the message depends on whether the window handle is 0/invalid.\n\t/// </summary>\n\t/// <exception cref=\"AuWndException\"></exception>\n\tpublic void ThrowUseNative(int errorCode, string mainMessage) {\n\t\tthrow new AuWndException(this, errorCode, mainMessage);\n\t}\n\t\n\t/// <summary>\n\t/// Throws <see cref=\"AuWndException\"/> that uses <i>mainMessage</i> and does not use the last Windows API error.\n\t/// Also the message depends on whether the window handle is 0/invalid.\n\t/// </summary>\n\t/// <exception cref=\"AuWndException\"></exception>\n\tpublic void ThrowNoNative(string mainMessage) {\n\t\tthrow new AuWndException(this, mainMessage);\n\t}\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if the window handle is 0 (this variable == <c>default(wnd)</c>).\n\t/// </summary>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// wnd w = wnd.find(\"Window*\");\n\t/// if(w.Is0) { print.it(\"window not found\"); return; }\n\t/// ]]></code>\n\t/// </example>\n\t/// <seealso cref=\"IsAlive\"/>\n\tpublic bool Is0 => _h == default;\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if the window exists (the window handle is valid).\n\t/// Returns <c>false</c> if the handle is 0 or invalid.\n\t/// Invalid non-0 handle usually means that the window is closed/destroyed.\n\t/// </summary>\n\t/// <remarks>\n\t/// Calls <see cref=\"Is0\"/> and API <ms>IsWindow</ms>.\n\t/// Although a <see cref=\"wnd\"/> variable holds a window handle, which is like a reference to a window, it does not prevent closing that window and making the handle invalid. After closing the window, the OS can even assign the same handle value to a new window, although normally it can happen only after long time.\n\t/// <note>Use this carefully with windows of other applications or threads. The window can be closed at any moment, even when your thread is still in this function.</note>\n\t/// </remarks>\n\tpublic bool IsAlive => !Is0 && Api.IsWindow(this);\n\t\n\t#endregion\n\t\n\t#region visible, enabled, cloaked\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if the window is visible.\n\t/// Returns <c>false</c> if is invisible or is a child of invisible parent.\n\t/// Also returns <c>false</c> when fails (probably window closed or 0 handle). Supports <see cref=\"lastError\"/>.\n\t/// </summary>\n\t/// <remarks>\n\t/// Calls API <ms>IsWindowVisible</ms>. Does not call <see cref=\"IsCloaked\"/>.\n\t/// \n\t/// Even when this function returns <c>true</c>, the window may be actually invisible. It can be cloaked, on an inactive Windows 10 virtual desktop (cloaked), inactive Windows Store app (cloaked), transparent, zero-size, minimized, off-screen, covered by other windows or can have zero-size window region.\n\t/// </remarks>\n\t/// <seealso cref=\"IsCloaked\"/>\n\t/// <seealso cref=\"IsVisibleAndNotCloaked\"/>\n\t/// <seealso cref=\"Show\"/>\n\t/// <seealso cref=\"Activate\"/>\n\tpublic bool IsVisible => Api.IsWindowVisible(this);\n\t\n\t//rejected. Unreliable, eg then does not find Store apps in inactive desktops. Instead now Find skips all cloaked by default.\n\t///// <summary>\n\t///// Returns <c>true</c> if the window is visible.\n\t///// Returns <c>false</c> if is invisible or is a child of invisible parent.\n\t///// Also returns <c>false</c> when fails (probably window closed or 0 handle). Supports <see cref=\"lastError\"/>.\n\t///// </summary>\n\t///// <remarks>\n\t///// Returns <c>false</c> if API <ms>IsWindowVisible</ms> returns <c>false</c>.\n\t///// Also returns <c>false</c> if <see cref=\"IsCloaked\"/> returns <c>true</c>, but only for some popup windows that usually are useless and could cause problems if considered visible.\n\t///// Else returns <c>true</c>.\n\t///// \n\t///// Even when this function returns <c>true</c>, the window may be actually invisible. It can be cloaked (except the above case), on an inactive Windows 10 virtual desktop (cloaked), inactive Windows Store app (cloaked), transparent, zero-size, minimized, off-screen, covered by other windows or can have zero-size window region.\n\t///// </remarks>\n\t///// <seealso cref=\"IsVisible\"/>\n\t///// <seealso cref=\"IsCloaked\"/>\n\t///// <seealso cref=\"IsVisibleAndNotCloaked\"/>\n\t///// <seealso cref=\"Show\"/>\n\t///// <seealso cref=\"Activate()\"/>\n\t//public bool IsVisibleEx {\n\t//\tget {\n\t//\t\tif(!Api.IsWindowVisible(this)) return false;\n\t\n\t//\t\tvar style = Style;\n\t//\t\tif((style & (WS.POPUP | WS.CHILD)) == WS.POPUP) {\n\t//\t\t\tif((style & WS.CAPTION) != WS.CAPTION) return !IsCloaked;\n\t\n\t//\t\t\t//is it a ghost ApplicationFrameWindow, like closed Calculator on Win10?\n\t//\t\t\tif(osVersion.minWin10 && HasExStyle(WSE.NOREDIRECTIONBITMAP) && IsCloaked && ClassNameIs(\"ApplicationFrameWindow\")) {\n\t//\t\t\t\tvar isGhost = default == Api.FindWindowEx(this, default, \"Windows.UI.Core.CoreWindow\", null);\n\t//\t\t\t\t//print.it(isGhost, this);\n\t//\t\t\t\treturn !isGhost;\n\t//\t\t\t}\n\t//\t\t}\n\t//\t\treturn true;\n\t//\t}\n\t//}\n\t\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if <see cref=\"IsVisible\"/> returns <c>true</c> and <see cref=\"IsCloaked\"/> returns <c>false</c>.\n\t/// </summary>\n\tpublic bool IsVisibleAndNotCloaked => IsVisible && !IsCloaked;\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if this window is visible in the specified parent or ancestor window.\n\t/// Like <see cref=\"IsVisible\"/>, but does not check the visibility of the specified parent/ancestor window.\n\t/// </summary>\n\t/// <param name=\"wParent\">Parent or ancestor window.</param>\n\tinternal bool IsVisibleIn_(wnd wParent) {\n\t\tif (IsVisible) return true; //these two make faster in most cases (when wTL is visible)\n\t\tif (wParent.IsVisible) return false;\n\t\tvar c = this;\n\t\tfor (int i = 0; i < 10000; i++) {\n\t\t\t//print.it(c);\n\t\t\tif (!c.HasStyle(WS.VISIBLE)) return false;\n\t\t\tc = c.ParentGWL_;\n\t\t\tif (c == wParent) return true;\n\t\t\tif (c.Is0) break;\n\t\t}\n\t\tDebug.Assert(false);\n\t\treturn false;\n\t}\n\t\n\t/// <summary>\n\t/// Shows (if hidden) or hides this window.\n\t/// </summary>\n\t/// <remarks>\n\t/// Does not activate/deactivate/zorder.\n\t/// This window can be of any thread. If you know it is of this thread, use <see cref=\"ShowL\"/>. This function calls it.\n\t/// </remarks>\n\t/// <exception cref=\"AuWndException\"/>\n\tpublic void Show(bool show) {\n\t\tif (!ShowL(show)) ThrowUseNative(show ? \"*show*\" : \"*hide*\");\n\t\tMinimalSleepIfOtherThread_();\n\t}\n\t\n\t/// <summary>\n\t/// Shows (if hidden) or hides this window.\n\t/// </summary>\n\t/// <returns>Returns <c>false</c> if failed. Supports <see cref=\"lastError\"/>.</returns>\n\t/// <remarks>\n\t/// Does not activate/deactivate/zorder.\n\t/// \n\t/// There are two similar functions to show/hide a window: \n\t/// - <see cref=\"Show\"/> is better to use in automation scripts, with windows of any process/thread. It calls <c>ShowL</c>, and throws exception if it fails. Adds a small delay if the window is of another thread.\n\t/// - <c>ShowL</c> is better to use in programming, with windows of current thread. It is more lightweight. Does not throw exceptions. Does not add a delay. But both functions can be used with windows of any thread.\n\t/// </remarks>\n\tpublic bool ShowL(bool show) {\n\t\tif (show == HasStyle(WS.VISIBLE)) return true; //avoid messages and make much faster. Never mind: if show==false, returns true even if invalid hwnd.\n\t\tSend(Api.WM_SHOWWINDOW, show ? 1 : 0); //not necessary for most windows, but eg winforms would not update the Visible property without it. ShowWindow sends it but SetWindowPos doesn't.\n\t\treturn SetWindowPos((show ? SWPFlags.SHOWWINDOW : SWPFlags.HIDEWINDOW)\n\t\t\t| SWPFlags.NOSIZE | SWPFlags.NOMOVE | SWPFlags.NOACTIVATE | SWPFlags.NOZORDER | SWPFlags.NOOWNERZORDER);\n\t\t\n\t\t//This code is similar to ShowWindow(SW_SHOWNA). Don't use it because:\n\t\t//\tMay change Z order. Eg makes above other windows of same thread.\n\t\t//\tAlso may not work first time in process. The documentation is unclear. Not tested.\n\t}\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if the window is enabled for mouse and keyboard input.\n\t/// Returns <c>false</c> if disabled. Also <c>false</c> if failed (probably window closed or 0 handle). Supports <see cref=\"lastError\"/>.\n\t/// </summary>\n\t/// <param name=\"ancestorsToo\">Check whether all ancestors of this control are enabled too. If <c>false</c> (default), this function simply calls API <ms>IsWindowEnabled</ms>, which usually returns <c>true</c> for controls in disabled windows.</param>\n\tpublic bool IsEnabled(bool ancestorsToo = false) {\n\t\tif (!ancestorsToo) return Api.IsWindowEnabled(this);\n\t\tfor (var w = this; ;) {\n\t\t\tif (!Api.IsWindowEnabled(w)) return false;\n\t\t\tw = w.Get.DirectParent; if (w.Is0) break;\n\t\t}\n\t\treturn true;\n\t}\n\t\n\t/// <summary>\n\t/// Enables or disables.\n\t/// Calls API <ms>EnableWindow</ms>.\n\t/// </summary>\n\t/// <param name=\"enable\">Enable or disable.</param>\n\t/// <exception cref=\"AuWndException\"/>\n\tpublic void Enable(bool enable) {\n\t\tlastError.clear();\n\t\tApi.EnableWindow(this, enable);\n\t\tif (lastError.code != 0) ThrowUseNative(\"*enable/disable*\");\n\t}\n\t\n\t/// <summary>\n\t/// Gets the cloaked state (it's a way to hide a window).\n\t/// </summary>\n\t/// <value>\n\t/// <br/>• 0 if not cloaked or if failed.\n\t/// <br/>• 1 cloaked by its application.\n\t/// <br/>• 2 cloaked by Windows.\n\t/// <br/>• 4 cloaked because its owner window is cloaked.\n\t/// \n\t/// <para>\n\t/// On Windows 7 returns 0 because there is no \"cloaked window\" feature.\n\t/// </para>\n\t/// </value>\n\t/// <seealso cref=\"IsCloaked\"/>\n\tpublic int IsCloakedGetState {\n\t\tget {\n\t\t\tif (!osVersion.minWin8) return 0;\n\t\t\tint cloaked = 0;\n\t\t\tint hr = Api.DwmGetWindowAttribute(this, Api.DWMWA.CLOAKED, &cloaked, 4);\n\t\t\treturn cloaked;\n\t\t}\n\t}\n\t/// <summary>\n\t/// Detects whether the window is cloaked (it's a way to hide a window).\n\t/// </summary>\n\t/// <value><c>true</c> if the window is cloaked. <c>false</c> if not cloaked or if failed.</value>\n\t/// <remarks>\n\t/// On Windows 7 returns <c>false</c> because there is no \"cloaked window\" feature.\n\t/// Windows 10 uses window cloaking mostly to hide windows on inactive desktops. Windows 8 - mostly to hide Windows Store app windows.\n\t/// </remarks>\n\t/// <seealso cref=\"IsCloakedGetState\"/>\n\tpublic bool IsCloaked => IsCloakedGetState != 0;\n\t\n\t#endregion\n\t\n\t#region minimized, maximized\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if minimized, <c>false</c> if not.\n\t/// Also returns <c>false</c> when fails (probably window closed or 0 handle). Supports <see cref=\"lastError\"/>.\n\t/// Calls API <ms>IsIconic</ms>.\n\t/// </summary>\n\tpublic bool IsMinimized => Api.IsIconic(this);\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if maximized, <c>false</c> if not.\n\t/// Also returns <c>false</c> when fails (probably window closed or 0 handle). Supports <see cref=\"lastError\"/>.\n\t/// Calls API <ms>IsZoomed</ms>.\n\t/// </summary>\n\tpublic bool IsMaximized => Api.IsZoomed(this);\n\t\n\t/// <summary>\n\t/// If not minimized, minimizes.\n\t/// Also unhides.\n\t/// </summary>\n\t/// <param name=\"how\">What API to use: 0 <ms>ShowWindow</ms>, 1 <ms>SetWindowPlacement</ms> (no animation), 2 <ms>WM_SYSCOMMAND</ms>.</param>\n\t/// <exception cref=\"AuWndException\">The API call failed. No exception if the window did not obey.</exception>\n\tpublic void ShowMinimized(int how = 0) => _MinMaxRes(Api.SW_MINIMIZE, how);\n\t\n\t/// <summary>\n\t/// If not minimized, minimizes.\n\t/// Also unhides.\n\t/// </summary>\n\t/// <param name=\"how\">What API to use: 0 <ms>ShowWindow</ms>, 1 <ms>SetWindowPlacement</ms> (no animation), 2 <ms>WM_SYSCOMMAND</ms>.</param>\n\t/// <exception cref=\"AuWndException\">The API call failed. No exception if the window did not obey.</exception>\n\tpublic void ShowMaximized(int how = 0) => _MinMaxRes(Api.SW_SHOWMAXIMIZED, how);\n\t\n\t/// <summary>\n\t/// If maximized or minimized, makes normal (not min/max).\n\t/// Also unhides.\n\t/// </summary>\n\t/// <param name=\"how\">What API to use: 0 <ms>ShowWindow</ms>, 1 <ms>SetWindowPlacement</ms> (no animation), 2 <ms>WM_SYSCOMMAND</ms>.</param>\n\t/// <exception cref=\"AuWndException\">The API call failed. No exception if the window did not obey.</exception>\n\tpublic void ShowNotMinMax(int how = 0) => _MinMaxRes(Api.SW_SHOWNORMAL, how);\n\t\n\t/// <summary>\n\t/// If minimized, restores previous non-minimized state (maximized or normal).\n\t/// Also unhides.\n\t/// </summary>\n\t/// <param name=\"how\">What API to use: 0 <ms>ShowWindow</ms>, 1 <ms>SetWindowPlacement</ms> (no animation), 2 <ms>WM_SYSCOMMAND</ms>.</param>\n\t/// <exception cref=\"AuWndException\">The API call failed. No exception if the window did not obey.</exception>\n\tpublic void ShowNotMinimized(int how = 0) => _MinMaxRes(Api.SW_RESTORE, how);\n\t\n\t///\n\t[Obsolete, EditorBrowsable(EditorBrowsableState.Never)]\n\tpublic void ShowMinimized(bool noAnimation) => _MinMaxRes(Api.SW_MINIMIZE, noAnimation ? 1 : 0);\n\t///\n\t[Obsolete, EditorBrowsable(EditorBrowsableState.Never)]\n\tpublic void ShowMaximized(bool noAnimation) => _MinMaxRes(Api.SW_SHOWMAXIMIZED, noAnimation ? 1 : 0);\n\t///\n\t[Obsolete, EditorBrowsable(EditorBrowsableState.Never)]\n\tpublic void ShowNotMinMax(bool noAnimation) => _MinMaxRes(Api.SW_SHOWNORMAL, noAnimation ? 1 : 0);\n\t///\n\t[Obsolete, EditorBrowsable(EditorBrowsableState.Never)]\n\tpublic void ShowNotMinimized(bool noAnimation) => _MinMaxRes(Api.SW_RESTORE, noAnimation ? 1 : 0);\n\t\n\t/// <summary>\n\t/// Sets window min/max/normal/restore state.\n\t/// Also unhides.\n\t/// </summary>\n\t/// <param name=\"state\">Must be <c>SW_MINIMIZE</c>, <c>SW_RESTORE</c> (restores to normal/max if minimized), <c>SW_SHOWNORMAL</c> or <c>SW_SHOWMAXIMIZED</c>.</param>\n\t/// <param name=\"how\">0 <c>ShowWindow</c>, 1 <c>SetWindowPlacement</c> (no animation), 2 <c>WM_SYSCOMMAND</c>.</param>\n\t/// <exception cref=\"AuWndException\"/>\n\tvoid _MinMaxRes(int state, int how) {\n\t\tDebug.Assert(state == Api.SW_MINIMIZE || state == Api.SW_RESTORE || state == Api.SW_SHOWNORMAL || state == Api.SW_SHOWMAXIMIZED);\n\t\tThrowIfInvalid();\n\t\t\n\t\tbool ok = false, wasMinimized = IsMinimized;\n\t\t\n\t\tswitch (state) {\n\t\tcase Api.SW_MINIMIZE:\n\t\t\tok = wasMinimized;\n\t\t\tbreak;\n\t\tcase Api.SW_RESTORE:\n\t\t\tok = !wasMinimized;\n\t\t\tbreak;\n\t\tcase Api.SW_SHOWMAXIMIZED:\n\t\t\tok = IsMaximized;\n\t\t\tbreak;\n\t\tdefault: //SW_SHOWNORMAL\n\t\t\tok = !wasMinimized && !IsMaximized; //info: if invalid handle, Show() will return false, don't need to check here.\n\t\t\tbreak;\n\t\t}\n\t\t\n\t\tif (ok) {\n\t\t\tif (IsVisible) return;\n\t\t\tShow(true);\n\t\t} else {\n\t\t\t//WPF bug: if using SizeToContent, maximizing with ShowWindow/SetWindowPlacement sets incorrect size.\n\t\t\t//\tExample: if SizeToContent=vertical, resizes vertically for content, and horizontally for entire screen (not work area).\n\t\t\t//\t\tIf inactive, resizes vertically for content, and horizontally does not resize.\n\t\t\t//\tWorks well with WM_SYSCOMMAND.\n\t\t\t//if(how!=2 && state == Api.SW_SHOWMAXIMIZED && ClassNameIs(\"HwndWrapper[*\")) how=2; //no. Never mind.\n\t\t\t\n\t\t\tif (how == 0) {\n\t\t\t\tApi.ShowWindow(this, state);\n\t\t\t\t//note: The API returns TRUE if was visible, not if succeeded. Tested: lastError can't be used.\n\t\t\t\tok = _IsState(this, state);\n\t\t\t} else if (how == 1 && (ok = GetWindowPlacement_(out var p, false))) {\n\t\t\t\tint state2 = state;\n\t\t\t\tswitch (state) {\n\t\t\t\tcase Api.SW_MINIMIZE:\n\t\t\t\t\tif (p.showCmd == Api.SW_SHOWMAXIMIZED) p.flags |= Api.WPF_RESTORETOMAXIMIZED; else p.flags &= ~Api.WPF_RESTORETOMAXIMIZED; //Windows forgets to remove the flag\n\t\t\t\t\tbreak;\n\t\t\t\tcase Api.SW_RESTORE:\n\t\t\t\t\tif ((p.showCmd == Api.SW_SHOWMINIMIZED) && (p.flags & Api.WPF_RESTORETOMAXIMIZED) != 0) state2 = Api.SW_SHOWMAXIMIZED; //without this would make normal\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t//if(wasMinimized) p.flags|=Api.WPF_ASYNCWINDOWPLACEMENT; //Windows bug: if window of another thread, deactivates currently active window and does not activate this window. However then animates window. If we set this while the window is not minimized, it would set blinking caret in inactive window. Instead we use another workaround, see below.\n\t\t\t\tp.showCmd = state2;\n\t\t\t\tok = SetWindowPlacement_(ref p, false);\n\t\t\t}\n\t\t\t\n\t\t\tstatic bool _IsState(wnd w, int state) => state switch {\n\t\t\t\tApi.SW_MINIMIZE => w.IsMinimized,\n\t\t\t\tApi.SW_RESTORE => !w.IsMinimized,\n\t\t\t\tApi.SW_SHOWMAXIMIZED => w.IsMaximized,\n\t\t\t\t_ => !(w.IsMinimized || w.IsMaximized)\n\t\t\t} && w.IsVisible;\n\t\t\t\n\t\t\tif (!ok) {\n\t\t\t\tif (how == 2 || UacAccessDenied) {\n\t\t\t\t\tvar cmd = state switch {\n\t\t\t\t\t\tApi.SW_MINIMIZE => Api.SC_MINIMIZE,\n\t\t\t\t\t\tApi.SW_SHOWMAXIMIZED => Api.SC_MAXIMIZE, //fails, never mind\n\t\t\t\t\t\t_ => Api.SC_RESTORE,\n\t\t\t\t\t};\n\t\t\t\t\tSend(Api.WM_SYSCOMMAND, cmd);\n\t\t\t\t\t//if was minimized, now can be maximized, need to restore if SW_SHOWNORMAL\n\t\t\t\t\tif (state == Api.SW_SHOWNORMAL && IsMaximized) Send(Api.WM_SYSCOMMAND, cmd);\n\t\t\t\t\t//note: the Send return value is unreliable\n\t\t\t\t\tok = _IsState(this, state);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (!ok) ThrowNoNative(\"*minimize/maximize/restore*\");\n\t\t\t}\n\t\t\t\n\t\t\tif (!IsOfThisThread) {\n\t\t\t\tif (wasMinimized) ActivateL(); //Windows bug: if window of another thread, deactivates currently active window and does not activate this window\n\t\t\t\telse if (state == Api.SW_MINIMIZE) WndUtil.WaitForAnActiveWindow();\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (how != 1) MinimalSleepIfOtherThread_();\n\t}\n\t\n\t/// <summary>\n\t/// Initializes a <c>WINDOWPLACEMENT</c> struct and calls API <ms>GetWindowPlacement</ms>.\n\t/// </summary>\n\t/// <param name=\"wp\"></param>\n\t/// <param name=\"rectInScreen\">Remove workarea thickness from <c>wp.rcNormalPosition</c>.</param>\n\t/// <param name=\"errStr\">If not <c>null</c>, throws it if failed.</param>\n\t/// <remarks>Supports <see cref=\"lastError\"/>.</remarks>\n\t/// <exception cref=\"AuWndException\">Failed. Throws, only if <c>errStr!=null</c>, else returns <c>false</c>.</exception>\n\tinternal bool GetWindowPlacement_(out Api.WINDOWPLACEMENT wp, bool rectInScreen, string errStr = null) {\n\t\t//initially this was public, but probably don't need.\n\t\t\n\t\twp = new Api.WINDOWPLACEMENT(); wp.length = Api.SizeOf(wp);\n\t\tif (Api.GetWindowPlacement(this, ref wp)) {\n\t\t\tif (rectInScreen) _Wp1(ref wp, false);\n\t\t\treturn true;\n\t\t}\n\t\tif (errStr != null) ThrowUseNative(errStr);\n\t\treturn false;\n\t}\n\t\n\t/// <summary>\n\t/// Sets <c>WINDOWPLACEMENT</c> <c>length</c> field and calls API <ms>SetWindowPlacement</ms>.\n\t/// </summary>\n\t/// <param name=\"wp\"></param>\n\t/// <param name=\"rectInScreen\"><c>wp.rcNormalPosition</c> is without workarea thickness.</param>\n\t/// <param name=\"errStr\">If not <c>null</c>, throws it if failed.</param>\n\t/// <exception cref=\"AuWndException\">Failed. Throws, only if <c>errStr!=null</c>, else returns <c>false</c>.</exception>\n\tinternal bool SetWindowPlacement_(ref Api.WINDOWPLACEMENT wp, bool rectInScreen, string errStr = null) {\n\t\t//initially this was public, but probably don't need.\n\t\t\n\t\tif (rectInScreen) _Wp1(ref wp, true);\n\t\twp.length = Api.SizeOf(wp);\n\t\tif (Api.SetWindowPlacement(this, wp)) return true;\n\t\tif (errStr != null) ThrowUseNative(errStr);\n\t\treturn false;\n\t}\n\t\n\tvoid _Wp1(ref Api.WINDOWPLACEMENT wp, bool set) {\n\t\tif (!IsToolWindow) {\n\t\t\tvar s = set ? screen.of(wp.rcNormalPosition) : screen.of(this);\n\t\t\tvar v = s.Info;\n\t\t\tint i = set ? -1 : 1;\n\t\t\twp.rcNormalPosition.Offset((v.workArea.left - v.rect.left) * i, (v.workArea.top - v.rect.top) * i);\n\t\t}\n\t}\n\t\n\t#endregion\n\t\n\t#region activate, focus\n\t\n\tinternal static partial class Internal_ {\n\t\t/// <summary>\n\t\t/// No exceptions.\n\t\t/// </summary>\n\t\tinternal static bool EnableActivate(bool goingToActivateAWindow) {\n\t\t\tif (_EnableActivate_AllowSetFore()) return true; //not locked, or already successfully called ASF_Key\n\t\t\t\n\t\t\t_EnableActivate_SendKey(true);\n\t\t\tif (_EnableActivate_AllowSetFore()) return true;\n\t\t\t//First time fails if the foreground window is of higher IL. Then sending keys does not work.\n\t\t\t\n\t\t\t//_EnableActivate_MinRes() makes no active window...\n\t\t\tif (goingToActivateAWindow) {\n\t\t\t\t_EnableActivate_MinRes();\n\t\t\t\treturn _EnableActivate_AllowSetFore();\n\t\t\t}\n\t\t\t\n\t\t\tvar wFore = active; bool retry = false;\n\t\t\tg1: _EnableActivate_MinRes();\n\t\t\tif (!_EnableActivate_AllowSetFore()) return false;\n\t\t\tif (!wFore.Is0 && !retry) {\n\t\t\t\tApi.SetForegroundWindow(wFore);\n\t\t\t\tif (!_EnableActivate_AllowSetFore()) { retry = true; goto g1; } //didn't notice this but let's be safer\n\t\t\t}\n\t\t\treturn true;\n\t\t\t\n\t\t\t//Other possible ways to allow set foreground window:\n\t\t\t//1. Instead of key can use attachthreadinput. But it is less reliable, eg works first time only, and does not allow our process to activate later easily. Does not work if foreground window is higher IL.\n\t\t\t//2. Call allowsetforegroundwindow from a hook from the foreground process (or from the shell process, not tested). Too dirty. Need 2 native dlls (32/64-bit). Cannot inject if higher IL.\n\t\t\t\n\t\t\t//Other possible ways to set foreground window:\n\t\t\t//1. Create temp window, RegisterHotKey, SendInput, and call SetForegroundWindow on WM_HOTKEY. Does not work if foreground window is higher IL.\n\t\t\t//2. WM_SETHOTKEY. More info below. Could not make it work well in all cases.\n\t\t\t//3. IUIAutomationElement.SetFocus. Does not work if foreground window is higher IL. Slow. Briefly makes the taskbar button red.\n\t\t\t\n\t\t\t//tested: cannot disable the foreground lock timeout with SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT) when this process cannot activate windows.\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Sends a key (<c>VK_0</c> up). It allows to activate now.\n\t\t/// Later this process usually (but not always) can activate easily (without key etc). It works even with higher IL windows.\n\t\t/// Don't know why is this behavior. Tested on all OS.\n\t\t/// Does not work if the foreground process has higher UAC IL.\n\t\t/// Does not work if bad input desktop.\n\t\t/// </summary>\n\t\tstatic void _EnableActivate_SendKey(bool debugOut) {\n\t\t\t//if (debugOut) Debug_.Print(\"EnableActivate: need key\"); //FUTURE: temporarily enable to see maybe there are too many calls\n\t\t\t\n\t\t\tvar x = new Api.INPUTK(0, 128, Api.KEYEVENTF_KEYUP);\n\t\t\tApi.SendInput(&x, dontThrow: true);\n\t\t\t//info: works without waiting.\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Creates a temporary minimized window and restores it. It activates the window and allows us to activate.\n\t\t/// Then sets \"no active window\" to prevent auto-activating another window when destroying the temporary window.\n\t\t/// </summary>\n\t\tstatic void _EnableActivate_MinRes() {\n\t\t\t//Debug_.Print(\"EnableActivate: need min/res\");\n\t\t\tDebug_.Print($\"EnableActivate: need min/res, {miscInfo.isInputDesktop(false)} {miscInfo.isInputDesktop(true)}, {wnd.active}\");\n\t\t\t//CONSIDER: don't try if !miscInfo.isInputDesktop(true)\n\t\t\t\n\t\t\twnd t = WndUtil.CreateWindow(WndUtil.WindowClassDWP_, null, WS.POPUP | WS.MINIMIZE | WS.VISIBLE, WSE.TOOLWINDOW);\n\t\t\t//info: When restoring, the window must be visible, or may not work.\n\t\t\ttry {\n\t\t\t\tvar wp = new Api.WINDOWPLACEMENT { showCmd = Api.SW_RESTORE };\n\t\t\t\tt.SetWindowPlacement_(ref wp, false); //activates t; fast (no animation)\n\t\t\t\t_EnableActivate_SendKey(false); //makes so that later our process can always activate\n\t\t\t\t_EnableActivate_AllowSetFore();\n\t\t\t\tApi.SetForegroundWindow(getwnd.root); //set no foreground window, or may activate the higher IL window (maybe does not activate, but winevents hook gets events, in random order). Other way would be to destroy our window later, but more difficult to implement.\n\t\t\t}\n\t\t\tfinally { Api.DestroyWindow(t); }\n\t\t\t\n\t\t\t//Another way:\n\t\t\t//\tt.Send(Api.WM_SETHOTKEY, ...);\n\t\t\t//\tApi.SendInputKey(...)\n\t\t\t//Works if UAC allows SendInput.\n\t\t\t//WM_SETHOTKEY works only if visible. No wm_syscommand if inactive, but activates. Unlike registerhotkey, not blocked by UAC even without modifiers. Need more testing.\n\t\t\t//This does not work: t.Send(WM_SYSCOMMAND, SC_HOTKEY);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Calls <c>Api.AllowSetForegroundWindow(Api.GetCurrentProcessId())</c>.\n\t\t/// </summary>\n\t\tstatic bool _EnableActivate_AllowSetFore() {\n\t\t\treturn Api.AllowSetForegroundWindow(Api.GetCurrentProcessId());\n\t\t}\n\t\t\n\t\tinternal static bool ActivateL(wnd w) {\n\t\t\tif (w.IsActiveOrNoActiveAndThisIsWndRoot_) return true;\n\t\t\t\n\t\t\ttry {\n\t\t\t\tbool canAct = EnableActivate(true);\n\t\t\t\t\n\t\t\t\tif (!Api.SetForegroundWindow(w)) {\n\t\t\t\t\tif (!canAct || !w.IsAlive) return false;\n\t\t\t\t\t//It happens when foreground process called LockSetForegroundWindow.\n\t\t\t\t\t//Although AllowSetForegroundWindow returns true, SetForegroundWindow fails.\n\t\t\t\t\t//It happens only before this process sends keys. Eg after first _EnableActivate_SendKey this never happens again.\n\t\t\t\t\t//If it has higher IL (and this process is User), also need _EnableActivate_MinRes.\n\t\t\t\t\t_EnableActivate_SendKey(true);\n\t\t\t\t\tif (!Api.SetForegroundWindow(w)) {\n\t\t\t\t\t\t_EnableActivate_MinRes();\n\t\t\t\t\t\tif (!Api.SetForegroundWindow(w)) return false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t//Often after SetForegroundWindow there is no active window for several ms. Not if the window is of this thread.\n\t\t\t\t//\thttps://devblogs.microsoft.com/oldnewthing/20161118-00/?p=94745\n\t\t\t\tif (w == getwnd.root) return active.Is0;\n\t\t\t\tif (WndUtil.WaitForAnActiveWindow()) return true;\n\t\t\t\tw.SendTimeout(1000, out _, 0);\n\t\t\t\treturn !active.Is0;\n\t\t\t}\n\t\t\t//catch(AuWndException) { return false; }\n\t\t\tcatch { return false; }\n\t\t}\n\t\t\n\t\t[Flags]\n\t\tinternal enum ActivateFlags {\n\t\t\t/// <summary>\n\t\t\t/// Don't call <see cref=\"ThrowIfInvalid\"/> (ie caller ensures it is valid).\n\t\t\t/// </summary>\n\t\t\tNoThrowIfInvalid = 1,\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// Don't try to get top-level window (ie caller ensures it's a top-level window, not control).\n\t\t\t/// </summary>\n\t\t\tNoGetWindow = 2,\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// Don't activate if has <ms>WS_EX_NOACTIVATE</ms> style or is toolwindow without title bar, unless cloaked.\n\t\t\t/// Then just calls <see cref=\"ZorderTop\"/>, which in most cases does not work (inactive window).\n\t\t\t/// </summary>\n\t\t\tIgnoreIfNoActivateStyleEtc = 4,\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// Wait for window animations to end. Eg when switching Win10 desktops.\n\t\t\t/// </summary>\n\t\t\tForScreenCapture = 8,\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Activates this window (brings to the foreground).\n\t/// The same as <see cref=\"Activate\"/>, but has some options.\n\t/// Returns <c>false</c> if does not activate because of flag <c>IgnoreIfNoActivateStyleEtc</c>.\n\t/// </summary>\n\t/// <exception cref=\"AuWndException\"/>\n\t/// <exception cref=\"InputDesktopException\"></exception>\n\tinternal bool Activate_(Internal_.ActivateFlags flags) {\n\t\tif (!flags.Has(Internal_.ActivateFlags.NoThrowIfInvalid)) ThrowIfInvalid();\n\t\tif (flags.Has(Internal_.ActivateFlags.NoGetWindow)) Debug.Assert(!IsChild);\n\t\telse {\n\t\t\tvar w = Window;\n\t\t\tif (w != this) {\n\t\t\t\tif (!w.Is0) print.warning(\"Child windows can't be activated. You may want to call Focus() instead.\");\n\t\t\t\treturn w.Activate_((flags | Internal_.ActivateFlags.NoGetWindow) & ~Internal_.ActivateFlags.NoThrowIfInvalid);\n\t\t\t}\n\t\t}\n\t\t\n\t\tbool R, noAct = false, isMinimized = false, ofThisThread = IsOfThisThread;\n\t\tbool forScreenCapture = 0 != (flags & Internal_.ActivateFlags.ForScreenCapture);\n\t\t\n\t\tif (IsMinimized) {\n\t\t\tShowNotMinimized(1);\n\t\t\tisMinimized = IsMinimized;\n\t\t\tif (forScreenCapture && !isMinimized && !ofThisThread) Thread.Sleep(250); //although we use noAnimation, in some cases still restores with animation\n\t\t}\n\t\tif (!IsVisible) Show(true);\n\t\t\n\t\tR = IsActiveOrNoActiveAndThisIsWndRoot_;\n\t\tif (!R) {\n\t\t\tif (0 != (flags & Internal_.ActivateFlags.IgnoreIfNoActivateStyleEtc)) {\n\t\t\t\tif (IsNoActivateStyle_() && !IsCloaked) {\n\t\t\t\t\tZorderTop();\n\t\t\t\t\treturn false; //if cloaked, need to activate to uncloak\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tfor (int i = 0; i < 3; i++) {\n\t\t\t\tbool ok = Internal_.ActivateL(this);\n\t\t\t\t\n\t\t\t\tif (!ofThisThread) {\n\t\t\t\t\tMinimalSleepNoCheckThread_();\n\t\t\t\t\tMinimalSleepNoCheckThread_();\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (ok) {\n\t\t\t\t\twnd f = active;\n\t\t\t\t\tif (f == this) R = true;\n\t\t\t\t\telse if (this == getwnd.root) R = f.Is0; //activating GetDesktopWindow makes \"no active window\"\n\t\t\t\t\telse { //forgive if the target app instead activated another window of same thread\n\t\t\t\t\t\tint tid = ThreadId; if (tid == 0) break;\n\t\t\t\t\t\tif (f.ThreadId == tid) {\n\t\t\t\t\t\t\t//at first try to recognize such known windows, to avoid the hard way\n\t\t\t\t\t\t\tif (isMinimized || (f.Get.Owner == this && Rect.NoArea)) {\n\t\t\t\t\t\t\t\tR = true;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tR = Api.SetForegroundWindow(getwnd.root) && ActivateL() && active.ThreadId == tid;\n\t\t\t\t\t\t\t\tif (R && !ofThisThread) {\n\t\t\t\t\t\t\t\t\tMinimalSleepNoCheckThread_();\n\t\t\t\t\t\t\t\t\tR = active.ThreadId == tid;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t//Example 1:\n\t\t\t\t\t\t\t//Excel creates a minimized window for each workbook opened in that excel process.\n\t\t\t\t\t\t\t//These windows just add taskbar buttons. Also it allows to find and activate workbooks.\n\t\t\t\t\t\t\t//When you activate such window, Excel instead activates its main window, where it displays all workbooks.\n\t\t\t\t\t\t\t//For this reason we would fail (not always, because this may be temporarily active).\n\t\t\t\t\t\t\t//Same with PowerPoint. Other Office apps no.\n\t\t\t\t\t\t\t//Example 2:\n\t\t\t\t\t\t\t//Inno Setup, SQLite Expert. They have a zero-size owner window that just adds taskbar button.\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (R) break;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (noAct) break;\n\t\t\t\tThread.Sleep(30);\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (R && !ofThisThread && this != getwnd.root) {\n\t\t\t//If we activate a window that is on an inactive Win10 desktop, its desktop becomes active.\n\t\t\t//Windows on inactive desktops are cloaked. They are uncloaked after ~15 ms.\n\t\t\tif (IsCloaked) {\n\t\t\t\tR = false;\n\t\t\t\tfor (int i = 0; i < 50; i++) { Thread.Sleep(30); if (R = !IsCloaked) break; }\n\t\t\t\tif (R) {\n\t\t\t\t\tif (forScreenCapture) Thread.Sleep(800); //need minimum 600 for 'find image' functions, because of animation while switching Win10 desktops.\n\t\t\t\t\tMinimalSleepNoCheckThread_();\n\t\t\t\t\tR = IsActive;\n\t\t\t\t\tif (!R && ActivateL()) {\n\t\t\t\t\t\tMinimalSleepNoCheckThread_();\n\t\t\t\t\t\tR = IsActive;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (!R) {\n\t\t\tInputDesktopException.ThrowIfBadDesktop(\"*activate window\", detectLocked: true);\n\t\t\tThrowNoNative(\"*activate*\");\n\t\t}\n\t\tif (forScreenCapture) MinimalSleepIfOtherThread_();\n\t\t\n\t\treturn true;\n\t\t\n\t\t//tested: if the window is hung, activates the ghost window and fails (exception). It's OK.\n\t}\n\t\n\tinternal bool IsNoActivateStyle_() {\n\t\tvar es = ExStyle;\n\t\tif ((es & WSE.NOACTIVATE) != 0) return true;\n\t\tif ((es & (WSE.TOOLWINDOW | WSE.APPWINDOW)) == WSE.TOOLWINDOW) return !HasStyle(WS.CAPTION);\n\t\treturn false;\n\t}\n\t\n\t/// <summary>\n\t/// Activates this window. Also makes it visible and not minimized.\n\t/// The active window is in the foreground and receives keyboard and mouse input.\n\t/// </summary>\n\t/// <returns>Self.</returns>\n\t/// <remarks>\n\t/// Activating a window usually also uncloaks it, for example switches to its virtual desktop on Windows 10/11.\n\t/// Fails (throws exception) if cannot activate this window, except:\n\t/// - When this is a control. Then activates its top-level parent window.\n\t/// - When the window's process instead activates another window of the same thread.\n\t/// - If this is <see cref=\"getwnd.root\"/>, just deactivates the currently active window.\n\t/// </remarks>\n\t/// <exception cref=\"AuWndException\">Failed to activate.</exception>\n\t/// <exception cref=\"InputDesktopException\"></exception>\n\t/// <seealso cref=\"ActivateL\"/>\n\t/// <seealso cref=\"IsActive\"/>\n\t/// <seealso cref=\"active\"/>\n\t/// <seealso cref=\"switchActiveWindow\"/>\n\tpublic wnd Activate() {\n\t\tActivate_(0);\n\t\treturn this;\n\t}\n\t//CONSIDER: if fails to activate:\n\t//dialog.show(\"Failed to activate window\", w.ToString(), footer: The script will continue if you activate the window in {x} s.\", timeout: 10);\n\t\n\t//rejected: Activate(double waitS = 0) /// <param name=\"waitS\">Max time interval (seconds) to wait until the window naturally becomes active before activating it.</param>\n\t//\tIt would have sense if we somehow know that the window was created by previous action (or several actions).\n\t//\tWould need an explicit function call before the action(s). Nobody would use it.\n\t\n\t/// <summary>\n\t/// Lightweight version of <see cref=\"Activate\"/>. Does not throw exceptions.\n\t/// </summary>\n\t/// <param name=\"full\">\n\t/// Call <see cref=\"Activate\"/> and handle exceptions; on exception return <c>false</c>.\n\t/// If <c>false</c> (default), just activates; does not show hidden, does not restore minimized, does not check whether it is a top-level window or control, does not check whether activated exactly this window, etc.\n\t/// </param>\n\t/// <returns><c>false</c> if failed.</returns>\n\tpublic bool ActivateL(bool full = false) {\n\t\tif (!full) return Internal_.ActivateL(this);\n\t\ttry {\n\t\t\tActivate();\n\t\t\treturn true;\n\t\t}\n\t\tcatch { return false; }\n\t}\n\t\n\t//Too unreliable.\n\t///// <summary>\n\t///// Calls API <c>LockSetForegroundWindow</c>, which temporarily prevents other applications from activating windows easily with <c>SetForegroundWindow</c>.\n\t///// If <c>LockSetForegroundWindow</c> fails, calls <c>EnableActivate</c> and retries.\n\t///// </summary>\n\t///// <param name=\"on\">Lock or unlock.</param>\n\t//public static bool lockActiveWindow(bool on)\n\t//{\n\t//\tuint f = on ? Api.LSFW_LOCK : Api.LSFW_UNLOCK;\n\t//\tif(Api.LockSetForegroundWindow(f)) return true;\n\t//\treturn EnableActivate() && Api.LockSetForegroundWindow(f);\n\t//}\n\t\n\t/// <summary>\n\t/// Sets the keyboard input focus to this control.\n\t/// Also activates its top-level parent window (see <see cref=\"Activate\"/>).\n\t/// </summary>\n\t/// <remarks>\n\t/// The control can belong to any process/thread. With controls of this thread you can use the more lightweight function <see cref=\"thisThread.focus\"/>.\n\t/// Works not with all windows. For example, does not work with Windows Store apps. Then use <see cref=\"elm.Focus\"/>.\n\t/// Can instead focus a child control. For example, if this is a combo box, it will focus its child Edit control. Then does not throw exception.\n\t/// This can be control or top-level window. Top-level windows also can have focus.\n\t/// Fails when the target process is admin or uiAccess and this process isn't. See [](xref:uac).\n\t/// </remarks>\n\t/// <exception cref=\"AuWndException\">\n\t/// - Invalid handle.\n\t/// - Disabled.\n\t/// - Failed to set focus.\n\t/// - Failed to activate parent window.\n\t/// </exception>\n\t/// <exception cref=\"InputDesktopException\"></exception>\n\t/// <seealso cref=\"focused\"/>\n\t/// <seealso cref=\"IsFocused\"/>\n\t/// <seealso cref=\"elm.Focus\"/>\n\tpublic void Focus() {\n\t\tThrowIfInvalid();\n\t\twnd wTL = Window;\n\t\tif (!wTL.IsActive) wTL.Activate_(Internal_.ActivateFlags.NoGetWindow);\n\t\t\n\t\tint tid = ThreadId;\n\t\tif (tid == Api.GetCurrentThreadId()) {\n\t\t\tif (!thisThread.focus(this)) {\n\t\t\t\tif (!IsEnabled(true)) goto gDisabled;\n\t\t\t\tgoto gFailed;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tif (IsFocused) return;\n\t\tif (!IsEnabled(true)) goto gDisabled;\n\t\t\n\t\tbool ok = false;\n\t\tusing (new AttachThreadInput_(tid, out bool atiOK)) {\n\t\t\tif (atiOK) { //FUTURE: if fails, try elm.Focus or UIA. AttachThreadInput is unreliable.\n\t\t\t\tfor (int i = 0; i < 5; i++) {\n\t\t\t\t\tif (i > 0) Thread.Sleep(30);\n\t\t\t\t\tlastError.clear();\n\t\t\t\t\tif (thisThread.focus(this)) {\n\t\t\t\t\t\twnd f = focused;\n\t\t\t\t\t\tif (f == this || f.IsChildOf(this)) { ok = true; break; }\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (!ok) goto gFailed;\n\t\t\n\t\tMinimalSleepNoCheckThread_();\n\t\treturn;\n\t\tgDisabled: //SetFocus fails if disabled\n\t\tThrowIfInvalid();\n\t\tThrowNoNative(\"*set focus. Disabled\");\n\t\tgFailed:\n\t\tThrowUseNative(\"*set focus\");\n\t}\n\t\n\t/// <summary>\n\t/// Gets the control or window that has the keyboard input focus.\n\t/// </summary>\n\t/// <remarks>\n\t/// The control/window can belong to any process/thread. With controls/windows of this thread you can use the more lightweight function <see cref=\"thisThread.focused\"/>.\n\t/// Calls API <ms>GetGUIThreadInfo</ms>.\n\t/// </remarks>\n\t/// <seealso cref=\"Focus\"/>\n\t/// <seealso cref=\"IsFocused\"/>\n\tpublic static wnd focused {\n\t\tget {\n\t\t\tmiscInfo.getGUIThreadInfo(out var g);\n\t\t\treturn g.hwndFocus;\n\t\t}\n\t}\n\t//FUTURE: need functions that wait eg max 1 s until a window is focused or active.\n\t//\tExample: after activating a window using the taskbar, there is no active window for 100 ms or more.\n\t//\tExample: after opening a common file dialog, the Edit control is focused after 200 ms, until that there is no focus.\n\t// For 'active' we already have More.WaitForAnActiveWindow.\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if this is the control or window that has the keyboard input focus.\n\t/// </summary>\n\t/// <remarks>\n\t/// This control/window can belong to any process/thread. With controls/windows of this thread you can use the more lightweight function <see cref=\"thisThread.isFocused\"/>.\n\t/// Calls <see cref=\"focused\"/>.\n\t/// </remarks>\n\t/// <seealso cref=\"Focus\"/>\n\tpublic bool IsFocused => !this.Is0 && this == focused;\n\t\n\t/// <summary>\n\t/// Functions that can be used only with windows/controls of this thread.\n\t/// </summary>\n\tpublic static class thisThread {\n\t\t/// <summary>\n\t\t/// Calls API <ms>SetFocus</ms>. It sets the keyboard input focus to the specified control or window, which must be of this thread.\n\t\t/// </summary>\n\t\t/// <returns><c>false</c> if failed. Supports <see cref=\"lastError\"/>.</returns>\n\t\t/// <remarks>\n\t\t/// Fails if the control/window belongs to another thread or is invalid or disabled.\n\t\t/// Can instead focus a child control. For example, if combo box, will focus its child Edit control. Then returns <c>true</c>.\n\t\t/// </remarks>\n\t\tpublic static bool focus(wnd w) {\n\t\t\tif (w.Is0) { Api.SetLastError(Api.ERROR_INVALID_WINDOW_HANDLE); return false; }\n\t\t\tvar f = Api.GetFocus(); if (f == w) return true;\n\t\t\tif (!Api.SetFocus(w).Is0) return true;\n\t\t\tif (f.Is0) return !Api.GetFocus().Is0;\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets the focused control or window of this thread.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Calls API <ms>GetFocus</ms>.\n\t\t/// </remarks>\n\t\tpublic static wnd focused => Api.GetFocus();\n\t\t\n\t\t//rejected.\n\t\t///// <summary>\n\t\t///// Gets the focused control or form of this thread.\n\t\t///// </summary>\n\t\t///// <remarks>\n\t\t///// Calls API <ms>GetFocus</ms> and <see cref=\"System.Windows.Forms.Control.FromHandle\"/>.\n\t\t///// </remarks>\n\t\t//public static System.Windows.Forms.Control focusedWinformsControl => System.Windows.Forms.Control.FromHandle(Api.GetFocus().Handle);\n\t\t\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if <i>w</i> is the focused control or window of this thread.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Calls API <ms>GetFocus</ms>.\n\t\t/// </remarks>\n\t\tpublic static bool isFocused(wnd w) => !w.Is0 && w == Api.GetFocus();\n\t\t\n\t\t/// <summary>\n\t\t/// Gets the active window of this thread.\n\t\t/// Calls API <ms>GetActiveWindow</ms>.\n\t\t/// </summary>\n\t\tpublic static wnd active => Api.GetActiveWindow();\n\t}\n\t\n\t#endregion\n\t\n\t#region rect\n\t\n\t/// <summary>\n\t/// Gets rectangle (position and size) in screen coordinates.\n\t/// </summary>\n\t/// <param name=\"r\">Receives the rectangle. Will be <c>default(RECT)</c> if failed.</param>\n\t/// <param name=\"withoutExtendedFrame\">Don't include the transparent part of window border. For it is used API <ms>DwmGetWindowAttribute</ms>(<c>DWMWA_EXTENDED_FRAME_BOUNDS</c>); it is less reliable.</param>\n\t/// <remarks>\n\t/// The same as the <see cref=\"Rect\"/> property.\n\t/// Calls API <ms>GetWindowRect</ms> and returns its return value.\n\t/// Supports <see cref=\"lastError\"/>.\n\t/// </remarks>\n\tpublic bool GetRect(out RECT r, bool withoutExtendedFrame = false) {\n\t\tif (withoutExtendedFrame) {\n\t\t\tRECT t;\n\t\t\tif (0 == Api.DwmGetWindowAttribute(this, Api.DWMWA.EXTENDED_FRAME_BOUNDS, &t, 16)) {\n\t\t\t\tr = t;\n\t\t\t\treturn true;\n\t\t\t\t//tested: on Win7 gets physical (not logical) coords of DPI-scaled window\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (Api.GetWindowRect(this, out r)) return true;\n\t\tr = default;\n\t\treturn false;\n\t}\n\t\n\t/// <summary>\n\t/// Gets rectangle (position and size) in screen coordinates.\n\t/// </summary>\n\t/// <remarks>\n\t/// Calls <see cref=\"GetRect\"/>. Returns <c>default(RECT)</c> if fails (eg window closed).\n\t/// Supports <see cref=\"lastError\"/>.\n\t/// </remarks>\n\tpublic RECT Rect {\n\t\tget {\n\t\t\tGetRect(out RECT r);\n\t\t\treturn r;\n\t\t}\n\t}\n\t\n\t///// <summary>\n\t///// Gets width and height.\n\t///// </summary>\n\t///// <param name=\"z\">Receives width and height. Will be <c>default(SIZE)</c> if failed.</param>\n\t///// <remarks>\n\t///// The same as the <see cref=\"Size\"/> property.\n\t///// Calls API <ms>GetWindowRect</ms> and returns its return value.\n\t///// Supports <see cref=\"lastError\"/>.\n\t///// </remarks>\n\t//public bool GetSize(out SIZE z) {\n\t//\tif (Api.GetWindowRect(this, out RECT r)) { z = new SIZE(r.Width, r.Height); return true; }\n\t//\tz = default;\n\t//\treturn false;\n\t//}\n\t\n\t///// <summary>\n\t///// Gets width and height.\n\t///// </summary>\n\t///// <remarks>\n\t///// Calls <see cref=\"GetSize\"/>. Returns <c>default(SIZE)</c> if fails (eg window closed).\n\t///// Supports <see cref=\"lastError\"/>.\n\t///// </remarks>\n\t//public SIZE Size {\n\t//\tget {\n\t//\t\tGetSize(out SIZE z);\n\t//\t\treturn z;\n\t//\t}\n\t//}\n\t\n\t///// <summary>\n\t///// Gets position in screen coordinates.\n\t///// </summary>\n\t///// <remarks>\n\t///// Calls <see cref=\"GetRect\"/>. Returns <c>default(POINT)</c> if fails (eg window closed).\n\t///// Supports <see cref=\"lastError\"/>.\n\t///// </remarks>\n\t//public POINT XY {\n\t//\tget { var r = Rect; return new(r.left, r.top); }\n\t//}\n\t\n\t///// <summary>\n\t///// Gets horizontal position in screen coordinates.\n\t///// </summary>\n\t///// <remarks>Calls <see cref=\"GetRect\"/>.</remarks>\n\t//public int X {\n\t//\tget => Rect.left;\n\t//}\n\t\n\t///// <summary>\n\t///// Gets vertical position in screen coordinates.\n\t///// </summary>\n\t///// <remarks>Calls <see cref=\"GetRect\"/>.</remarks>\n\t//public int Y {\n\t//\tget => Rect.top;\n\t//}\n\t\n\t///// <summary>\n\t///// Gets width.\n\t///// </summary>\n\t///// <remarks>Calls <see cref=\"GetRect\"/>.</remarks>\n\t//public int Width {\n\t//\tget => Rect.Width;\n\t//}\n\t\n\t///// <summary>\n\t///// Gets height.\n\t///// </summary>\n\t///// <remarks>Calls <see cref=\"GetRect\"/>.</remarks>\n\t//public int Height {\n\t//\tget => Rect.Height;\n\t//}\n\t\n\t/// <summary>\n\t/// Gets client area rectangle.\n\t/// </summary>\n\t/// <param name=\"r\">Receives the rectangle. Will be <c>default(RECT)</c> if failed.</param>\n\t/// <param name=\"inScreen\">\n\t/// Get rectangle in screen coordinates; like <see cref=\"GetWindowAndClientRectInScreen\"/> but faster.\n\t/// If <c>false</c> (default), calls API <ms>GetClientRect</ms>; the same as <see cref=\"ClientRect\"/>.</param>\n\t/// <remarks>\n\t/// Supports <see cref=\"lastError\"/>.\n\t/// </remarks>\n\tpublic bool GetClientRect(out RECT r, bool inScreen = false) {\n#if true\n\t\tif (Api.GetClientRect(this, out r) && (!inScreen || Api.MapWindowPoints(this, default, ref r, out _))) return true;\n\t\tr = default; return false;\n#else //>= 2 times slower\n\t\t\tif(inScreen) return GetWindowAndClientRectInScreen(out _, out r);\n\t\t\tif(Api.GetClientRect(this, out r)) return true;\n\t\t\tr = default;\n\t\t\treturn false;\n#endif\n\t}\n\t\n\t/// <summary>\n\t/// Gets client area rectangle (width and height).\n\t/// </summary>\n\t/// <remarks>\n\t/// The left and top fields are always 0.\n\t/// Calls <see cref=\"GetClientRect\"/>. Returns <c>default(RECT)</c> if fails (eg window closed).\n\t/// </remarks>\n\tpublic RECT ClientRect {\n\t\tget {\n\t\t\tGetClientRect(out RECT r);\n\t\t\treturn r;\n\t\t}\n\t}\n\t\n\t///// <summary>\n\t///// Gets client area width and height.\n\t///// </summary>\n\t///// <param name=\"z\">Receives width and height. Will be <c>default(RECT)</c> if failed.</param>\n\t///// <remarks>\n\t///// The same as the <see cref=\"ClientSize\"/> property.\n\t///// The same as <see cref=\"GetClientRect\"/>, just the parameter type is different.\n\t///// Calls API <ms>GetClientRect</ms> and returns its return value.\n\t///// Supports <see cref=\"lastError\"/>.\n\t///// </remarks>\n\t//public bool GetClientSize(out SIZE z) {\n\t//\tif (Api.GetClientRect(this, out RECT r)) { z = new SIZE(r.right, r.bottom); return true; }\n\t//\tz = default;\n\t//\treturn false;\n\t//}\n\t\n\t/// <summary>\n\t/// Gets client area rectangle (width and height) in screen.\n\t/// </summary>\n\t/// <remarks>\n\t/// Calls <see cref=\"GetClientRect\"/>. Returns <c>default(RECT)</c> if fails (eg window closed).\n\t/// </remarks>\n\tpublic RECT ClientRectInScreen {\n\t\tget {\n\t\t\tGetClientRect(out RECT r, true);\n\t\t\treturn r;\n\t\t}\n\t}\n\t\n\t///// <summary>\n\t///// Gets client area width and height.\n\t///// </summary>\n\t///// <remarks>\n\t///// The same as <see cref=\"ClientRect\"/>, just the return type is different.\n\t///// Calls <see cref=\"GetClientSize\"/>. Returns <c>default(SIZE)</c> if fails (eg window closed).\n\t///// </remarks>\n\t//public SIZE ClientSize {\n\t//\tget {\n\t//\t\tGetClientSize(out SIZE z);\n\t//\t\treturn z;\n\t//\t}\n\t//}\n\t\n\t///// <summary>\n\t///// Gets client area width.\n\t///// </summary>\n\t///// <remarks>Calls <see cref=\"GetClientSize\"/>.</remarks>\n\t//public int ClientWidth {\n\t//\tget => ClientSize.width;\n\t//}\n\t\n\t///// <summary>\n\t///// Gets client area height.\n\t///// </summary>\n\t///// <remarks>Calls <see cref=\"GetClientSize\"/>.</remarks>\n\t//public int ClientHeight {\n\t//\tget => ClientSize.height;\n\t//}\n\t\n\t/// <summary>\n\t/// Resizes this window to match the specified client area size.\n\t/// Calls <see cref=\"ResizeL\"/>.\n\t/// </summary>\n\t/// <param name=\"width\">Client area width. Use <c>null</c> to not change.</param>\n\t/// <param name=\"height\">Client area height. Use <c>null</c> to not change.</param>\n\t/// <exception cref=\"AuWndException\"/>\n\tpublic void ResizeClient(int? width, int? height) {\n\t\tif (GetWindowInfo_(out var u)) {\n\t\t\tint W = width ?? u.rcClient.Width; W += u.rcWindow.Width - u.rcClient.Width;\n\t\t\tint H = height ?? u.rcClient.Height; H += u.rcWindow.Height - u.rcClient.Height;\n\t\t\t\n\t\t\tif (ResizeL(W, H)) return;\n\t\t}\n\t\t\n\t\tThrowUseNative();\n\t}\n\t\n\t/// <summary>\n\t/// Calls API <ms>GetWindowInfo</ms>.\n\t/// </summary>\n\t/// <param name=\"wi\">Receives window/client rectangles, styles etc.</param>\n\t/// <remarks>Supports <see cref=\"lastError\"/>.</remarks>\n\tinternal bool GetWindowInfo_(out Api.WINDOWINFO wi) {\n\t\t//initially this was public, but probably don't need.\n\t\t\n\t\twi = default; wi.cbSize = Api.SizeOf(wi);\n\t\treturn Api.GetWindowInfo(this, ref wi);\n\t}\n\t\n\t/// <summary>\n\t/// Gets window rectangle and client area rectangle, both in screen coordinates.\n\t/// </summary>\n\t/// <param name=\"rWindow\">Receives window rectangle.</param>\n\t/// <param name=\"rClient\">Receives client area rectangle.</param>\n\t/// <remarks>Calls API <ms>GetWindowInfo</ms>. Supports <see cref=\"lastError\"/>.</remarks>\n\tpublic bool GetWindowAndClientRectInScreen(out RECT rWindow, out RECT rClient) {\n\t\tif (GetWindowInfo_(out var u)) {\n\t\t\trWindow = u.rcWindow;\n\t\t\trClient = u.rcClient;\n\t\t\treturn true;\n\t\t}\n\t\trWindow = default;\n\t\trClient = default;\n\t\treturn false;\n\t}\n\t\n\t//rejected: because now GetClientRect has 'inScreen' parameter.\n\t///// <summary>\n\t///// Gets client area rectangle in screen coordinates.\n\t///// </summary>\n\t///// <remarks>\n\t///// Calls <see cref=\"GetWindowAndClientRectInScreen\"/>. Returns <c>default(RECT)</c> if fails (eg window closed).\n\t///// </remarks>\n\t//public RECT ClientRectInScreen\n\t//{\n\t//\tget\n\t//\t{\n\t//\t\tGetWindowAndClientRectInScreen(out var rw, out var rc);\n\t//\t\treturn rc;\n\t//\t}\n\t//}\n\t\n\t/// <summary>\n\t/// Converts coordinates relative to the client area of this window to coordinates relative to the client area of window <i>w</i>.\n\t/// </summary>\n\t/// <remarks>Supports <see cref=\"lastError\"/>.</remarks>\n\tpublic bool MapClientToClientOf(wnd w, ref RECT r) => !Is0 && !w.Is0 && Api.MapWindowPoints(this, w, ref r, out _);\n\t\n\t/// <summary>\n\t/// Converts coordinates relative to the client area of this window to coordinates relative to the client area of window <i>w</i>.\n\t/// </summary>\n\t/// <remarks>Supports <see cref=\"lastError\"/>.</remarks>\n\tpublic bool MapClientToClientOf(wnd w, ref POINT p) => !Is0 && !w.Is0 && Api.MapWindowPoints(this, w, ref p, out _);\n\t\n\t/// <summary>\n\t/// Converts coordinates relative to the client area of this window to coordinates relative to the screen.\n\t/// </summary>\n\t/// <remarks>Supports <see cref=\"lastError\"/>.</remarks>\n\tpublic bool MapClientToScreen(ref RECT r) => !Is0 && Api.MapWindowPoints(this, default, ref r, out _);\n\t\n\t/// <summary>\n\t/// Converts coordinates relative to the client area of this window to coordinates relative to the screen.\n\t/// </summary>\n\t/// <remarks>Supports <see cref=\"lastError\"/>.</remarks>\n\tpublic bool MapClientToScreen(ref POINT p) => Api.ClientToScreen(this, ref p);\n\t\n\t/// <summary>\n\t/// Converts coordinates relative to the screen to coordinates relative to the client area of this window.\n\t/// </summary>\n\t/// <remarks>Supports <see cref=\"lastError\"/>.</remarks>\n\tpublic bool MapScreenToClient(ref RECT r) => !Is0 && Api.MapWindowPoints(default, this, ref r, out _);\n\t\n\t/// <summary>\n\t/// Converts coordinates relative to the screen to coordinates relative to the client area of this window.\n\t/// </summary>\n\t/// <remarks>Supports <see cref=\"lastError\"/>.</remarks>\n\tpublic bool MapScreenToClient(ref POINT p) => Api.ScreenToClient(this, ref p);\n\t\n\t/// <summary>\n\t/// Converts coordinates relative to the client area of this window to coordinates relative to the top-left corner of this window.\n\t/// </summary>\n\t/// <remarks>Supports <see cref=\"lastError\"/>.</remarks>\n\tpublic bool MapClientToWindow(ref POINT p) {\n\t\tif (!GetWindowAndClientRectInScreen(out var rw, out var rc)) return false;\n\t\tp.x += rc.left - rw.left; p.y += rc.top - rw.top;\n\t\treturn true;\n\t}\n\t\n\t/// <summary>\n\t/// Converts coordinates relative to the client area of this window to coordinates relative to the top-left corner of this window.\n\t/// </summary>\n\t/// <remarks>Supports <see cref=\"lastError\"/>.</remarks>\n\tpublic bool MapClientToWindow(ref RECT r) {\n\t\tif (!GetWindowAndClientRectInScreen(out var rw, out var rc)) return false;\n\t\tr.Offset(rc.left - rw.left, rc.top - rw.top);\n\t\treturn true;\n\t}\n\t\n\t/// <summary>\n\t/// Converts coordinates relative to the top-left corner of this window to coordinates relative to the client area of this window.\n\t/// </summary>\n\t/// <remarks>Supports <see cref=\"lastError\"/>.</remarks>\n\tpublic bool MapWindowToClient(ref POINT p) {\n\t\tif (!GetWindowAndClientRectInScreen(out var rw, out var rc)) return false;\n\t\tp.x += rw.left - rc.left; p.y += rw.top - rc.top;\n\t\treturn true;\n\t}\n\t\n\t/// <summary>\n\t/// Converts coordinates relative to the top-left corner of this window to coordinates relative to the client area of this window.\n\t/// </summary>\n\t/// <remarks>Supports <see cref=\"lastError\"/>.</remarks>\n\tpublic bool MapWindowToClient(ref RECT r) {\n\t\tif (!GetWindowAndClientRectInScreen(out var rw, out var rc)) return false;\n\t\tr.Offset(rw.left - rc.left, rw.top - rc.top);\n\t\treturn true;\n\t}\n\t\n\t/// <summary>\n\t/// Converts coordinates relative to the top-left corner of this window to screen coordinates.\n\t/// </summary>\n\t/// <remarks>Supports <see cref=\"lastError\"/>.</remarks>\n\tpublic bool MapWindowToScreen(ref POINT p) {\n\t\tif (!GetRect(out var rw)) return false;\n\t\tp.x += rw.left; p.y += rw.top;\n\t\treturn true;\n\t}\n\t\n\t/// <summary>\n\t/// Converts coordinates relative to the top-left corner of this window to screen coordinates.\n\t/// </summary>\n\t/// <remarks>Supports <see cref=\"lastError\"/>.</remarks>\n\tpublic bool MapWindowToScreen(ref RECT r) {\n\t\tif (!GetRect(out var rw)) return false;\n\t\tr.Offset(rw.left, rw.top);\n\t\treturn true;\n\t}\n\t\n\t/// <summary>\n\t/// Gets rectangle of this window (usually control) relative to the client area of another window (usually parent).\n\t/// </summary>\n\t/// <param name=\"w\">Parent, ancestor or any other window or control. If <c>default(wnd)</c>, gets rectangle in screen.</param>\n\t/// <param name=\"r\">Receives the rectangle.</param>\n\t/// <remarks>Supports <see cref=\"lastError\"/>.</remarks>\n\t/// <seealso cref=\"RectInDirectParent\"/>\n\t/// <seealso cref=\"RectInWindow\"/>\n\tpublic bool GetRectIn(wnd w, out RECT r) {\n\t\tif (w.Is0) return GetRect(out r);\n\t\treturn GetRect(out r) && w.MapScreenToClient(ref r);\n\t}\n\t\n\t/// <summary>\n\t/// Gets child window rectangle in the client area of the direct parent window.\n\t/// </summary>\n\t/// <remarks>\n\t/// Calls <see cref=\"getwnd.DirectParent\"/> and <see cref=\"GetRectIn\"/>. Returns <c>default(RECT)</c> if fails (eg window closed).\n\t/// </remarks>\n\tpublic RECT RectInDirectParent => GetRectIn(Get.DirectParent, out var r) ? r : default;\n\t\n\t/// <summary>\n\t/// Gets child window rectangle in the client area of the top-level parent window.\n\t/// </summary>\n\t/// <remarks>\n\t/// Calls <see cref=\"Window\"/> and <see cref=\"GetRectIn\"/>. Returns <c>default(RECT)</c> if fails (eg window closed).\n\t/// </remarks>\n\tpublic RECT RectInWindow => GetRectIn(Window, out var r) ? r : default;\n\t\n\t/// <summary>\n\t/// Gets rectangle of normal (restored) window even if currently it is minimized or maximized.\n\t/// </summary>\n\t/// <remarks>Supports <see cref=\"lastError\"/>.</remarks>\n\tpublic bool GetRectNotMinMax(out RECT r) {\n\t\tif (!(IsMinimized || IsMaximized)) return GetRect(out r);\n\t\tif (!GetWindowPlacement_(out var p, true)) { r = default; return false; }\n\t\tr = p.rcNormalPosition;\n\t\treturn true;\n\t}\n\t\n\t//TEST: undocumented API GetWindowMinimizeRect. Gets rect of taskbar button or where it would be minimized above the Start button. But probably not useful.\n\t\n\t/// <summary>\n\t/// Returns mouse pointer position relative to the client area of this window.\n\t/// </summary>\n\tpublic POINT MouseClientXY {\n\t\tget {\n\t\t\tApi.GetCursorPos(out var p);\n\t\t\tif (!MapScreenToClient(ref p)) p = default;\n\t\t\treturn p;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if this window (its rectangle) contains the specified point.\n\t/// </summary>\n\t/// <param name=\"x\">X coordinate in screen. Not used if <c>default</c>. Examples: <c>10</c>, <c>^10</c> (reverse), <c>.5f</c> (fraction).</param>\n\t/// <param name=\"y\">Y coordinate in screen. Not used if <c>default</c>.</param>\n\tpublic bool ContainsScreenXY(Coord x, Coord y) {\n\t\tPOINT p = Coord.Normalize(x, y);\n\t\tif (!GetRect(out RECT r)) return false;\n\t\tif (!r.Contains(x.IsEmpty ? r.left : p.x, y.IsEmpty ? r.top : p.y)) return false;\n\t\treturn true;\n\t\t\n\t\t//note: we don't use name ContainsXY and 2 overloads, mostly because of possible incorrect usage. Also now easier to read the code.\n\t}\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if this control (its rectangle) contains the specified point in parent window.\n\t/// </summary>\n\t/// <param name=\"parent\">\n\t/// Direct or indirect parent window. The coordinates are relative to its client area.\n\t/// Actually this and parent can be any windows or controls, the function does not check whether this is a child of parent.\n\t/// </param>\n\t/// <param name=\"x\">X coordinate. Not used if <c>default</c>. Examples: <c>10</c>, <c>^10</c> (reverse), <c>.5f</c> (fraction).</param>\n\t/// <param name=\"y\">Y coordinate. Not used if <c>default</c>.</param>\n\tpublic bool ContainsWindowXY(wnd parent, Coord x, Coord y) {\n\t\tif (!parent.IsAlive) return false;\n\t\tPOINT p = Coord.NormalizeInWindow(x, y, parent);\n\t\tif (!GetRectIn(parent, out RECT r)) return false;\n\t\tif (!r.Contains(x.IsEmpty ? r.left : p.x, y.IsEmpty ? r.top : p.y)) return false;\n\t\treturn true;\n\t}\n\t\n\t/// <summary>\n\t/// This overload calls <see cref=\"ContainsWindowXY(wnd, Coord, Coord)\"/> with <see cref=\"Window\"/>, <i>x</i> and <i>y</i>.\n\t/// </summary>\n\t/// <inheritdoc cref=\"ContainsWindowXY\"/>\n\tpublic bool ContainsWindowXY(Coord x, Coord y) {\n\t\treturn ContainsWindowXY(Window, x, y);\n\t}\n\t\n\t#endregion\n\t\n\t#region move, resize, SetWindowPos\n\t\n\t/// <summary>\n\t/// Calls API <ms>SetWindowPos</ms>.\n\t/// </summary>\n\t/// <param name=\"swpFlags\"></param>\n\t/// <param name=\"x\"></param>\n\t/// <param name=\"y\"></param>\n\t/// <param name=\"cx\"></param>\n\t/// <param name=\"cy\"></param>\n\t/// <param name=\"zorderAfter\">A window or a constant from <see cref=\"SpecHWND\"/> (<c>TOP</c>, <c>BOTTOM</c>, <c>TOPMOST</c>, <c>NOTOPMOST</c>).</param>\n\t/// <remarks>\n\t/// Supports <see cref=\"lastError\"/>.\n\t/// </remarks>\n\tpublic bool SetWindowPos(SWPFlags swpFlags, int x = 0, int y = 0, int cx = 0, int cy = 0, wnd zorderAfter = default) {\n\t\treturn Api.SetWindowPos(this, zorderAfter, x, y, cx, cy, swpFlags);\n\t}\n\t\n\t/// <summary>\n\t/// Moves and resizes.\n\t/// </summary>\n\t/// <remarks>\n\t/// See also <see cref=\"Move(Coord, Coord, Coord, Coord, bool, screen, bool)\"/>. It is better to use in automation scripts, with windows of any process/thread. It throws exceptions, supports optional/reverse/fractional/workarea coordinates, restores if min/max, does not support <c>SWP</c> flags.\n\t/// This function is more lightweight, it just calls API <ms>SetWindowPos</ms> with flags <c>NOZORDER|NOOWNERZORDER|NOACTIVATE|swpFlagsToAdd</c>. It is better to use in programming, with windows of current thread.\n\t/// Supports <see cref=\"lastError\"/>.\n\t/// \n\t/// For top-level windows use screen coordinates. For controls - direct parent client coordinates.\n\t/// </remarks>\n\t/// <seealso cref=\"SetWindowPos\"/>\n\tpublic bool MoveL(int x, int y, int width, int height, SWPFlags swpFlagsToAdd = 0) {\n\t\treturn SetWindowPos(SWPFlags.NOZORDER | SWPFlags.NOOWNERZORDER | SWPFlags.NOACTIVATE | swpFlagsToAdd, x, y, width, height);\n\t}\n\t\n\t/// <summary>\n\t/// Moves and resizes. Same as <see cref=\"MoveL(int, int, int, int, SWPFlags)\"/>.\n\t/// </summary>\n\t/// <param name=\"visibleRect\">Use the visible rectangle without the transparent frame. Note: the window should be visible. This parameter not used with child windows.</param>\n\tpublic bool MoveL(RECT r, SWPFlags swpFlagsToAdd = 0, bool visibleRect = false) {\n\t\tif (visibleRect && GetRect(out var rTrue) && GetRect(out var rVisible, withoutExtendedFrame: true) && rVisible != rTrue) {\n\t\t\tr.left -= rVisible.left - rTrue.left;\n\t\t\tr.top -= rVisible.top - rTrue.top;\n\t\t\tr.right += rTrue.right - rVisible.right;\n\t\t\tr.bottom += rTrue.bottom - rVisible.bottom;\n\t\t}\n\t\t\n\t\treturn MoveL(r.left, r.top, r.Width, r.Height, swpFlagsToAdd);\n\t}\n\t\n\t/// <summary>\n\t/// Moves.\n\t/// </summary>\n\t/// <remarks>\n\t/// See also <see cref=\"Move(Coord, Coord, bool, screen, bool)\"/>. It is better to use in automation scripts, with windows of any process/thread. It throws exceptions, supports optional/reverse/fractional/workarea coordinates, restores if min/max.\n\t/// This function is more lightweight, it just calls API <ms>SetWindowPos</ms> with flags <c>NOSIZE|NOZORDER|NOOWNERZORDER|NOACTIVATE</c>. It is better to use in programming, with windows of current thread.\n\t/// Supports <see cref=\"lastError\"/>.\n\t/// \n\t/// For top-level windows use screen coordinates. For controls - direct parent client coordinates.\n\t/// </remarks>\n\t/// <seealso cref=\"SetWindowPos\"/>\n\tpublic bool MoveL(int x, int y) {\n\t\treturn MoveL(x, y, 0, 0, SWPFlags.NOSIZE);\n\t}\n\t\n\tinternal bool MoveL_(POINT p) => MoveL(p.x, p.y);\n\t\n\t/// <summary>\n\t/// Resizes.\n\t/// </summary>\n\t/// <remarks>\n\t/// See also <see cref=\"Resize(Coord, Coord, bool, screen, bool)\"/>. It is better to use in automation scripts, with windows of any process/thread. It throws exceptions, supports optional/reverse/fractional/workarea coordinates, restores if min/max.\n\t/// This function is more lightweight, it just calls API <ms>SetWindowPos</ms> with flags <c>NOMOVE|NOZORDER|NOOWNERZORDER|NOACTIVATE</c>. It is better to use in programming, with windows of current thread.\n\t/// Supports <see cref=\"lastError\"/>.\n\t/// </remarks>\n\t/// <seealso cref=\"SetWindowPos\"/>\n\tpublic bool ResizeL(int width, int height) {\n\t\treturn MoveL(0, 0, width, height, SWPFlags.NOMOVE);\n\t}\n\t\n\tinternal bool ResizeL_(SIZE z) => ResizeL(z.width, z.height);\n\t\n\t/// <summary>\n\t/// Moves and/or resizes.\n\t/// </summary>\n\t/// <param name=\"x\">Left. If <c>default</c>, does not move in X axis. Examples: <c>10</c>, <c>^10</c> (reverse), <c>.5f</c> (fraction).</param>\n\t/// <param name=\"y\">Top. If <c>default</c>, does not move in Y axis.</param>\n\t/// <param name=\"width\">Width. If <c>default</c>, does not change width.</param>\n\t/// <param name=\"height\">Height. If <c>default</c>, does not change height.</param>\n\t/// <param name=\"workArea\"><i>x y width height</i> are relative to the work area. This parameter not used with child windows.</param>\n\t/// <param name=\"screen\"><i>x y width height</i> are relative to this screen or its work area. Default - primary. Example: <c>screen.index(1)</c>. This parameter not used with child windows.</param>\n\t/// <param name=\"visibleRect\">Use the visible rectangle without the transparent frame. Note: the window should be visible. This parameter not used with child windows.</param>\n\t/// <exception cref=\"AuWndException\"/>\n\t/// <remarks>\n\t/// Also restores the visible top-level window if it is minimized or maximized.\n\t/// For top-level windows use screen coordinates. For controls - direct parent client area coordinates.\n\t/// With windows of current thread usually it's better to use <see cref=\"MoveL\"/>.\n\t/// </remarks>\n\tpublic void Move(Coord x, Coord y, Coord width, Coord height, bool workArea = false, screen screen = default, bool visibleRect = false) {\n\t\tThrowIfInvalid();\n\t\t\n\t\twnd w = Get.DirectParent;\n\t\tPOINT xy, wh;\n\t\tif (!w.Is0) {\n\t\t\txy = Coord.NormalizeInWindow(x, y, w);\n\t\t\twh = Coord.NormalizeInWindow(width, height, w);\n\t\t} else {\n\t\t\txy = Coord.Normalize(x, y, workArea, screen);\n\t\t\twh = Coord.Normalize(width, height, workArea, screen, widthHeight: true);\n\t\t}\n\t\t\n\t\tSWPFlags f = 0; uint getRect = 0;\n\t\tif (x.IsEmpty && y.IsEmpty) f |= SWPFlags.NOMOVE; else if (x.IsEmpty) getRect |= 1; else if (y.IsEmpty) getRect |= 2;\n\t\tif (width.IsEmpty && height.IsEmpty) f |= SWPFlags.NOSIZE; else if (width.IsEmpty) getRect |= 4; else if (height.IsEmpty) getRect |= 8;\n\t\t\n\t\tif (getRect != 0) {\n\t\t\tif (!GetRectIn(w, out RECT r)) ThrowUseNative(\"*move/resize*\");\n\t\t\tif ((getRect & 1) != 0) xy.x = r.left;\n\t\t\tif ((getRect & 2) != 0) xy.y = r.top;\n\t\t\tif ((getRect & 4) != 0) wh.x = r.Width;\n\t\t\tif ((getRect & 8) != 0) wh.y = r.Height;\n\t\t}\n\t\t\n\t\t//restore min/max, except if child or hidden\n\t\tif (w.Is0 && (IsMinimized || IsMaximized) && IsVisible) {\n\t\t\tShowNotMinMax(1);\n\t\t\t//info: '&& IsVisible' because ShowNotMinMax unhides\n\t\t}\n\t\t\n\t\tif (visibleRect && GetRect(out var rTrue) && GetRect(out var rVisible, withoutExtendedFrame: true) && rVisible != rTrue) {\n\t\t\tint dx = rVisible.left - rTrue.left, dy = rVisible.top - rTrue.top;\n\t\t\tif (!x.IsEmpty) xy.x -= dx;\n\t\t\tif (!y.IsEmpty) xy.y -= dy;\n\t\t\tif (!width.IsEmpty) wh.x += rTrue.right - rVisible.right + dx;\n\t\t\tif (!height.IsEmpty) wh.y += rTrue.bottom - rVisible.bottom + dy;\n\t\t}\n\t\t\n\t\tif (!MoveL(xy.x, xy.y, wh.x, wh.y, f)) ThrowUseNative(\"*move/resize*\");\n\t\t\n\t\tMinimalSleepIfOtherThread_();\n\t}\n\t\n\t/// <summary>\n\t/// Moves.\n\t/// </summary>\n\t/// <param name=\"workArea\"><i>x y</i> are relative to the work area. This parameter not used with child windows.</param>\n\t/// <param name=\"screen\"><i>x y</i> are relative to this screen or its work area. Default - primary. Example: <c>screen.index(1)</c>. This parameter not used with child windows.</param>\n\t/// <inheritdoc cref=\"Move(Coord, Coord, Coord, Coord, bool, screen, bool)\"/>\n\tpublic void Move(Coord x, Coord y, bool workArea = false, screen screen = default, bool visibleRect = false) {\n\t\tMove(x, y, default, default, workArea, screen, visibleRect);\n\t}\n\t\n\t/// <summary>\n\t/// Resizes.\n\t/// </summary>\n\t/// <param name=\"width\">Width. If <c>default</c>, does not change width.</param>\n\t/// <param name=\"height\">Height. If <c>default</c>, does not change height.</param>\n\t/// <param name=\"workArea\">For <see cref=\"Coord.Fraction\"/> etc use width/height of the work area. This parameter not used with child windows.</param>\n\t/// <param name=\"screen\">For <see cref=\"Coord.Fraction\"/> etc use width/height of this screen. Default - primary. Example: <c>screen.index(1)</c>. This parameter not used with child windows.</param>\n\t/// <param name=\"visibleRect\">Use the visible rectangle without the transparent frame. Note: the window should be visible. This parameter not used with child windows.</param>\n\t/// <exception cref=\"AuWndException\"/>\n\t/// <remarks>\n\t/// Also restores the visible top-level window if it is minimized or maximized.\n\t/// With windows of current thread usually it's better to use <see cref=\"ResizeL(int, int)\"/>.\n\t/// </remarks>\n\tpublic void Resize(Coord width, Coord height, bool workArea = false, screen screen = default, bool visibleRect = false) {\n\t\tMove(default, default, width, height, workArea, screen, visibleRect);\n\t}\n\t\n\t#endregion\n\t\n\t#region MoveInScreen, EnsureInScreen, Screen\n\t\n\tinternal static partial class Internal_ {\n\t\t\n\t\tstatic void _MoveRect(ref RECT r, Coord left, Coord top, RECT rs, bool ensureIn, bool ensureMethod) {\n\t\t\tint x, y;\n\t\t\tif (ensureMethod) {\n\t\t\t\tDebug.Assert(ensureIn && left.IsEmpty && top.IsEmpty); //left/top unused\n\t\t\t\tx = r.left;\n\t\t\t\ty = r.top;\n\t\t\t} else {\n\t\t\t\tif (left.IsEmpty) left = Coord.Center;\n\t\t\t\tif (top.IsEmpty) top = Coord.Center;\n\t\t\t\t(x, y) = Coord.NormalizeInRect(left, top, rs);\n\t\t\t\tswitch (left.Type) { case CoordType.Reverse: x -= r.Width; break; case CoordType.Fraction: x -= (int)(r.Width * left.FractionValue); break; }\n\t\t\t\tswitch (top.Type) { case CoordType.Reverse: y -= r.Height; break; case CoordType.Fraction: y -= (int)(r.Height * top.FractionValue); break; }\n\t\t\t}\n\t\t\t\n\t\t\tif (ensureIn) {\n\t\t\t\tx = Math.Max(Math.Min(x, rs.right - r.Width), rs.left);\n\t\t\t\ty = Math.Max(Math.Min(y, rs.bottom - r.Height), rs.top);\n\t\t\t\tif (r.Width > rs.Width) r.Width = rs.Width;\n\t\t\t\tif (r.Height > rs.Height) r.Height = rs.Height;\n\t\t\t}\n\t\t\t\n\t\t\tr.Move(x, y);\n\t\t}\n\t\t\n\t\tpublic static void MoveRectInRect(ref RECT r, Coord left, Coord top, RECT rs, bool ensureIn) {\n\t\t\t_MoveRect(ref r, left, top, rs, ensureIn, false);\n\t\t}\n\t\t\n\t\tpublic static void MoveRectInScreen(bool ensureMethod, ref RECT r, Coord left, Coord top, screen screen, bool workArea, bool ensureIn) {\n\t\t\tvar scr2 = !screen.IsEmpty ? screen.Now : ensureMethod ? screen.of(r) : screen.primary;\n\t\t\tvar rs = scr2.GetRect(workArea);\n\t\t\t_MoveRect(ref r, left, top, rs, ensureIn, ensureMethod);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Moves <i>w</i> to <i>left top</i> in screen, and/or ensures it's in screen.\n\t\t/// </summary>\n\t\t/// <param name=\"ensureMethod\">Just ensure in screen. Not used parameters: left, top.</param>\n\t\t/// <param name=\"w\"></param>\n\t\t/// <param name=\"left\">If empty, uses center.</param>\n\t\t/// <param name=\"top\">If empty, uses center.</param>\n\t\t/// <param name=\"screen\">If empty, uses screen of <i>w</i>.</param>\n\t\t/// <param name=\"workArea\"></param>\n\t\t/// <param name=\"ensureIn\"></param>\n\t\tpublic static void MoveWindowInScreen(bool ensureMethod, wnd w, Coord left, Coord top, screen screen, bool workArea, bool ensureIn) {\n\t\t\tvar scr2 = !screen.IsEmpty ? screen.Now : screen.of(w);\n\t\t\tvar rs = scr2.GetRect(workArea);\n\t\t\tvar scr1 = screen.of(w);\n\t\t\tbool moveToOtherScreen = scr2 != scr1;\n\t\t\t\n\t\t\tbool isMaximized = w.IsMaximized, isMinimized = !isMaximized && w.IsMinimized;\n\t\t\tif (isMaximized || isMinimized) {\n\t\t\t\tbool visible = w.IsVisible;\n\t\t\t\tif (moveToOtherScreen) {\n\t\t\t\t\tbool restoreMaximized = false;\n\t\t\t\t\tif (isMinimized && w.GetWindowPlacement_(out var wp1, false, \"*move*\") && 0 != (wp1.flags & 2)) {\n\t\t\t\t\t\twp1.flags &= ~2u;\n\t\t\t\t\t\trestoreMaximized = w.SetWindowPlacement_(ref wp1, false, \"*move*\");\n\t\t\t\t\t}\n\t\t\t\t\tApi.ShowWindow(w, Api.SW_SHOWNOACTIVATE); //less flickering than with SetWindowPlacement; same speed\n\t\t\t\t\t_Move();\n\t\t\t\t\tApi.ShowWindow(w, isMaximized ? Api.SW_SHOWMAXIMIZED : Api.SW_SHOWMINNOACTIVE);\n\t\t\t\t\tif (restoreMaximized && w.GetWindowPlacement_(out var wp2, false)) {\n\t\t\t\t\t\twp2.flags |= 2;\n\t\t\t\t\t\tw.SetWindowPlacement_(ref wp2, false);\n\t\t\t\t\t}\n\t\t\t\t\tif (!visible) w.ShowL(false);\n\t\t\t\t\t\n\t\t\t\t\t//With SetWindowPlacement could avoid restoring and showing, but then does not resize for different DPI.\n\t\t\t\t\t//Also tested: temporarily remove WS_MAXIMIZE. With some windows does not work well, eg Firefox. Does not work with minimized. Dangerous, undocumented.\n\t\t\t\t\t\n\t\t\t\t\t//never mind: old Dreamweaver maximizes to the old screen. Need to repeat or before maximizing wait ~300 ms.\n\t\t\t\t} else {\n\t\t\t\t\tw.GetWindowPlacement_(out var wp, true, \"*move*\");\n\t\t\t\t\t_MoveRect(ref wp.rcNormalPosition, left, top, rs, ensureIn, ensureMethod);\n\t\t\t\t\twp.showCmd = visible ? Api.SW_SHOWNA : Api.SW_HIDE;\n\t\t\t\t\tw.SetWindowPlacement_(ref wp, true, \"*move*\");\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t//would need this if using SetWindowPlacement when moving to other screen\n\t\t\t\t//if (isMaximized && moveToOtherScreen) {\n\t\t\t\t//\t//When moved to screen's coordinates and sized to screen's work area size, OS adjusts window pos to be correct, ie border is outside screen, but invisible in adjacent screen.\n\t\t\t\t//\t//Must call SetWindowPos twice, or it may refuse to move at all.\n\t\t\t\t//\t//OS does this on Win+Shift+Left or Right.\n\t\t\t\t//\t//Some windows does not respond well. Eg old Dreamweaver.\n\t\t\t\t//\t//The biggest problem is DPI-scaling. Even the OS hotkey does not scale correctly (because the app does not have a chance to scale itself).\n\t\t\t\t//\trs = scr2.WorkArea;\n\t\t\t\t//\tif (!w.MoveL(rs.left, rs.top) || !w.ResizeL(rs.Width, rs.Height)) w.ThrowUseNative(\"*move*\");\n\t\t\t\t//}\n\t\t\t} else {\n\t\t\t\t_Move();\n\t\t\t}\n\t\t\t\n\t\t\tvoid _Move() {\n\t\t\t\tif (moveToOtherScreen) {\n\t\t\t\t\tint dpi1 = scr1.Dpi, dpi2 = scr2.Dpi;\n\t\t\t\t\tif (dpi2 != dpi1 || !ensureIn) {\n\t\t\t\t\t\t//resize w if would be too big for that screen. Because DPI may change if bigger part will be in another screen.\n\t\t\t\t\t\tint wid = rs.Width, hei = rs.Height;\n\t\t\t\t\t\tif (dpi2 != dpi1) { wid = Math2.MulDiv(wid, dpi1, dpi2); hei = Math2.MulDiv(hei, dpi1, dpi2); }\n\t\t\t\t\t\tvar k = w.Rect;\n\t\t\t\t\t\tif (k.Width > wid || k.Height > hei) {\n\t\t\t\t\t\t\tw.ResizeL(Math.Min(k.Width, wid), Math.Min(k.Height, hei));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (dpi2 != dpi1) {\n\t\t\t\t\t\t//at first move to the screen, let it DPI-scale self. Some windows don't DPI-scale self when moving with resizing. Anyway would need to correct afterwards.\n\t\t\t\t\t\tw.MoveL(rs.left, rs.top);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvar r = w.Rect;\n\t\t\t\t_MoveRect(ref r, left, top, rs, ensureIn, ensureMethod);\n\t\t\t\tw.MoveL(r);\n\t\t\t}\n\t\t\t\n\t\t\tw.MinimalSleepIfOtherThread_();\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Moves this window to coordinates <i>x y</i> in specified screen, and ensures that entire window is in that screen.\n\t/// </summary>\n\t/// <param name=\"x\">X coordinate in the specified screen. If <c>default</c> - screen center. Examples: <c>10</c>, <c>^10</c> (reverse), <c>.5f</c> (fraction).</param>\n\t/// <param name=\"y\">Y coordinate in the specified screen. If <c>default</c> - screen center.</param>\n\t/// <param name=\"screen\">Move to this screen (see <see cref=\"screen\"/>). If default, uses screen of this window. Example: <c>screen.index(1)</c>.</param>\n\t/// <param name=\"workArea\">Use the work area, not whole screen. Default <c>true</c>.</param>\n\t/// <param name=\"ensureInScreen\">If part of window is not in screen, move and/or resize it so that entire window would be in screen. Default <c>true</c>.</param>\n\t/// <exception cref=\"AuWndException\"/>\n\t/// <remarks>\n\t/// If the window is maximized, minimized or hidden, it will have the new position and size when restored, not immediately, except when moving maximized to another screen.\n\t/// </remarks>\n\t/// <seealso cref=\"RECT.MoveInScreen\"/>\n\tpublic void MoveInScreen(Coord x, Coord y, screen screen = default, bool workArea = true, bool ensureInScreen = true) {\n\t\tInternal_.MoveWindowInScreen(false, this, x, y, screen, workArea, ensureInScreen);\n\t}\n\t\n\t/// <summary>\n\t/// Moves this window if need, to ensure that entire window is in that screen.\n\t/// </summary>\n\t/// <param name=\"screen\">Move to this screen (see <see cref=\"screen\"/>). If default, uses screen of this window.</param>\n\t/// <param name=\"workArea\">Use the work area, not whole screen. Default <c>true</c>.</param>\n\t/// <exception cref=\"AuWndException\"/>\n\t/// <remarks>\n\t/// If the window is maximized, minimized or hidden, it will have the new position and size when restored, not immediately.\n\t/// </remarks>\n\t/// <seealso cref=\"RECT.EnsureInScreen\"/>\n\tpublic void EnsureInScreen(screen screen = default, bool workArea = true) {\n\t\tInternal_.MoveWindowInScreen(true, this, default, default, screen, workArea, true);\n\t}\n\t\n\t/// <summary>\n\t/// Moves this window to the center of the screen. Ensures that entire window is in that screen.\n\t/// </summary>\n\t/// <param name=\"screen\">Move to this screen (see <see cref=\"screen\"/>). If default, uses screen of this window.</param>\n\t/// <exception cref=\"AuWndException\"/>\n\t/// <remarks>Calls <c>ShowNotMinMax(true)</c> and <c>MoveInScreen(default, default, screen, true)</c>. See <see cref=\"MoveInScreen\"/>.</remarks>\n\t/// <seealso cref=\"RECT.MoveInScreen\"/>\n\tpublic void MoveToScreenCenter(screen screen = default) {\n\t\tShowNotMinMax();\n\t\tMoveInScreen(default, default, screen, true);\n\t}\n\t\n\t/// <summary>\n\t/// Gets <see cref=\"screen\"/> of the screen that contains this window (the biggest part of it) or is nearest to it.\n\t/// If this window handle is <c>default(wnd)</c> or invalid, gets the primary screen.\n\t/// Calls <see cref=\"screen.of\"/>.\n\t/// </summary>\n\tpublic screen Screen => screen.of(this);\n\t\n\t#endregion\n\t\n\t#region Zorder\n\tconst SWPFlags _SWP_ZORDER_OWNER = SWPFlags.NOMOVE | SWPFlags.NOSIZE | SWPFlags.NOACTIVATE,\n\t\t_SWP_ZORDER_NOOWNER = SWPFlags.NOMOVE | SWPFlags.NOSIZE | SWPFlags.NOACTIVATE | SWPFlags.NOOWNERZORDER;\n\t//info: with SWP_NOOWNERZORDER the API does not zorder owner and owned windows.\n\t//note: without SWP_NOOWNERZORDER the API is full of bugs. Eg fails if the window has a topmost owned window.\n\t\n\t/// <summary>\n\t/// Calls <c>SetWindowPos</c> with zorder-only flags.\n\t/// Does not check owners etc.\n\t/// </summary>\n\t/// <param name=\"wAfter\"></param>\n\t/// <param name=\"before\">Call <c>wAfter = wAfter.Get.Previous();</c>.</param>\n\t/// <param name=\"noownerzorder\">With <c>SWP_NOOWNERZORDER</c>.</param>\n\t/// <returns></returns>\n\tinternal bool ZorderL_(wnd wAfter, bool before = false, bool noownerzorder = false) {\n\t\tif (before) {\n\t\t\tvar w0 = wAfter;\n\t\t\twAfter = wAfter.Get.Previous();\n\t\t\tif (wAfter == this) return true;\n\t\t\tif (wAfter.Is0 && !w0.IsAlive) return false;\n\t\t}\n\t\treturn SetWindowPos(noownerzorder ? _SWP_ZORDER_NOOWNER : _SWP_ZORDER_OWNER, zorderAfter: wAfter);\n\t}\n\t\n\t/// <summary>\n\t/// Places this window above window <i>w</i> in the Z order.\n\t/// </summary>\n\t/// <param name=\"w\">Another window.</param>\n\t/// <param name=\"ownerToo\">Place owner windows below this window. If <c>false</c> (default), does it only if they otherwise would be on top of this window.</param>\n\t/// <remarks>\n\t/// This window and <i>w</i> can be both top-level windows or both controls of same parent.\n\t/// Can make this window topmost or non-topmost, depending on where <i>w</i> is in the Z order.\n\t/// Also affects owned and owner windows, but does not make them topmost/non-topmost if not necessary.\n\t/// Uses API <ms>SetWindowPos</ms>.\n\t/// Supports <see cref=\"lastError\"/>.\n\t/// </remarks>\n\tpublic bool ZorderAbove(wnd w, bool ownerToo = false)\n\t\t=> _ZorderAB(w, true, ownerToo);\n\t\n\t/// <summary>\n\t/// Places this window below window <i>w</i> in the Z order.\n\t/// </summary>\n\t/// <param name=\"w\">Another window. If <c>default(wnd)</c>, calls <see cref=\"ZorderTop\"/>.</param>\n\t/// <inheritdoc cref=\"ZorderAbove(wnd, bool)\"/>\n\tpublic bool ZorderBelow(wnd w, bool ownerToo = false)\n\t\t=> _ZorderAB(w, false, ownerToo);\n\t\n\tbool _ZorderAB(wnd w, bool before, bool ownerToo, bool top = false) {\n\t\tif (!top) {\n\t\t\tif (before || !w.Is0) {\n\t\t\t\tif (!w.IsAlive || w == this) return false;\n\t\t\t\tif (before) {\n\t\t\t\t\tg1: var wp = w.Get.Previous();\n\t\t\t\t\tif (wp.Is0 && !w.IsAlive) return false; //w is at the very top?\n\t\t\t\t\tw = wp;\n\t\t\t\t\tfor (wnd ow = Get.Owner; !ow.Is0; ow = ow.Get.Owner) if (w == ow) goto g1;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (IsChild) return SetWindowPos(_SWP_ZORDER_NOOWNER, zorderAfter: w);\n\t\t\n\t\t//All ZorderX functions work almost like SetWindowPos without SWP_NOOWNERZORDER should work as documented.\n\t\t//\tHowever cannot simply call it because of its bugs.\n\t\t//\tTo simulate it, we zorder this and each owned/owner window with SWP_NOOWNERZORDER.\n\t\t//\tThe flag isn't well documented. With it zorders only this window; ignores owner and owned windows.\n\t\t//\tSpeed: 10% slower than SetWindowPos without SWP_NOOWNERZORDER.\n\t\t//\tStill can't overcome this API bug: can't zorder above a topmost uiAccess window; also Start menu.\n\t\t\n\t\tvar od = new getwnd.OwnedDescendants_();\n\t\tvar all = od.all;\n\t\tint iLastTopmost = -1; for (int i = 0; i < all.Length && all[i].IsTopmost; i++) iLastTopmost = i;\n\t\tint iThis = Array.IndexOf(all, this);\n\t\tif (iThis < 0) return false;\n\t\tint iW = -1;\n\t\tif (top) { //make this the top window in the topmost or nontopmost group\n\t\t\tif (iThis > iLastTopmost && iLastTopmost >= 0) w = all[iW = iLastTopmost];\n\t\t} else if (!w.Is0) {\n\t\t\tiW = Array.IndexOf(all, w);\n\t\t\tif (iW < 0) return false;\n\t\t}\n\t\tbool wasTopmost = iThis <= iLastTopmost;\n\t\tbool willBeTopmost = iW < iLastTopmost || (iW == iLastTopmost && wasTopmost);\n\t\t\n\t\t//descendants\n\t\tbool skipTopmostOwned = !willBeTopmost && !wasTopmost;\n\t\tList<int> ai = od.GetIndices(this, skipTopmostOwned ? i => i <= iLastTopmost || i == iW : i => i == iW);\n\t\tai.Add(iThis);\n\t\t\n\t\t//owners\n\t\tint iSkip = iThis;\n\t\tfor (wnd ow = Get.Owner; !ow.Is0; ow = ow.Get.Owner) {\n\t\t\tint i2 = Array.IndexOf(all, ow);\n\t\t\tif (i2 < 0 || i2 == iW) break;\n\t\t\tif (willBeTopmost && i2 > iLastTopmost) break; //ignore nontopmost owners\n\t\t\tif (!ownerToo && i2 > iW) break; //don't zorder owners unless they would be on top of this\n\t\t\tList<int> ai2 = od.GetIndices(ow, i => i == iSkip || (!willBeTopmost && i <= iLastTopmost));\n\t\t\tai.AddRange(ai2);\n\t\t\tai.Add(iSkip = i2);\n\t\t}\n\t\t\n\t\tif (ai.Count == 1) return SetWindowPos(_SWP_ZORDER_NOOWNER, zorderAfter: w);\n\t\t\n\t\t//With DeferWindowPos faster and no flickering.\n\t\t//\tBut it's unreliable. Eg can't change topmost/notopmost.\n\t\t//\tIf fails, retry with SetWindowPos.\n\t\t\n\t\tbool dwpWillFail = willBeTopmost; //will fail if need to move above topmost uiAccess etc windows\n\t\tif (!dwpWillFail) { //will fail if need to change topmost/notopmost\n\t\t\tfor (int i = 0; i < ai.Count; i++) { if (dwpWillFail = ai[i] <= iLastTopmost) break; }\n\t\t}\n\t\t\n\t\tvar wPrev = w;\n\t\tif (!dwpWillFail) {\n\t\t\tvar hp = Api.BeginDeferWindowPos(ai.Count);\n\t\t\tfor (int i = 0; i < ai.Count; i++) {\n\t\t\t\tvar k = all[ai[i]];\n\t\t\t\thp = Api.DeferWindowPos(hp, k, wPrev, 0, 0, 0, 0, _SWP_ZORDER_NOOWNER);\n\t\t\t\tif (hp == default) break;\n\t\t\t\twPrev = k;\n\t\t\t}\n\t\t\tif (hp != default) {\n\t\t\t\tApi.EndDeferWindowPos(hp); //usually returns true when fails\n\t\t\t\twPrev = w;\n\t\t\t\tfor (int i = 0; ;) {\n\t\t\t\t\tvar k = all[ai[i]];\n\t\t\t\t\tif (k.Get.Previous() != wPrev) break;\n\t\t\t\t\tif (++i == ai.Count) return true;\n\t\t\t\t\twPrev = k;\n\t\t\t\t}\n\t\t\t}\n\t\t\tDebug_.Print(\"DeferWindowPos failed\");\n\t\t\twPrev = w;\n\t\t}\n\t\t\n\t\tfor (int i = 0; i < ai.Count; i++) {\n\t\t\tvar k = all[ai[i]];\n\t\t\tbool ok = k.SetWindowPos(_SWP_ZORDER_NOOWNER, zorderAfter: wPrev);\n\t\t\tif (!ok) {\n\t\t\t\tif (k == this) return false;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\twPrev = k;\n\t\t}\n\t\treturn true;\n\t}\n\t\n\t/// <summary>\n\t/// Places this window or control at the top of the Z order. Does not activate.\n\t/// If the window was topmost, it will be at the top of topmost windows, else at the top of other windows.\n\t/// </summary>\n\t/// <param name=\"ownerToo\">Place owner windows below this window. If <c>false</c> (default), does it only if they otherwise would be on top of this window.</param>\n\t/// <remarks>\n\t/// Also affects owner and owned windows.\n\t/// Uses API <ms>SetWindowPos</ms>.\n\t/// Supports <see cref=\"lastError\"/>.\n\t/// </remarks>\n\tpublic bool ZorderTop(bool ownerToo = false)\n\t\t=> _ZorderAB(default, false, ownerToo, top: true);\n\t\n\t/// <summary>\n\t/// Calls <c>SetWindowPos</c> with <c>HWND_TOP</c> and <c>SWP_NOOWNERZORDER</c> (ignores owner and owned windows).\n\t/// </summary>\n\tinternal bool ZorderTopRaw_() {\n\t\tDebug_.PrintIf(!IsChild && !Get.EnabledOwned().Is0, \"has owned windows: \" + ToString());\n\t\tif (IsChild || IsTopmost || IsActive) return SetWindowPos(_SWP_ZORDER_NOOWNER);\n\t\tgetwnd.top2(out var lastTopmost);\n\t\treturn SetWindowPos(_SWP_ZORDER_NOOWNER, zorderAfter: lastTopmost);\n\t}\n\t\n\t/// <summary>\n\t/// Places this window or control at the bottom of the Z order.\n\t/// </summary>\n\t/// <remarks>\n\t/// Also affects owner and owned windows.\n\t/// If the window was topmost, makes it and its owner and owned windows non-topmost.\n\t/// Uses API <ms>SetWindowPos</ms>.\n\t/// Supports <see cref=\"lastError\"/>.\n\t/// </remarks>\n\tpublic bool ZorderBottom() {\n\t\treturn SetWindowPos(_SWP_ZORDER_OWNER, zorderAfter: SpecHWND.BOTTOM);\n\t\t//Bug in older Windows 10: the first HWND_BOTTOM moves a topmost window to the top of non-topmost windows.\n\t}\n\t\n\t/// <summary>\n\t/// Makes this window topmost (always on top of non-topmost windows in the Z order). Does not activate.\n\t/// </summary>\n\t/// <remarks>\n\t/// This cannot be a control.\n\t/// Also affects owned windows. Does not affect owners.\n\t/// Uses API <ms>SetWindowPos</ms>.\n\t/// Supports <see cref=\"lastError\"/>.\n\t/// </remarks>\n\tpublic bool ZorderTopmost() {\n\t\tif (!SetWindowPos(_SWP_ZORDER_NOOWNER, zorderAfter: SpecHWND.TOPMOST)) return false;\n\t\tvar a = Get.AllOwned(allDescendants: true);\n\t\tfor (int i = a.Length; --i >= 0;) {\n\t\t\ta[i].SetWindowPos(_SWP_ZORDER_NOOWNER, zorderAfter: SpecHWND.TOPMOST);\n\t\t}\n\t\treturn true;\n\t\t//note: cannot use without SWP_NOOWNERZORDER because of API bugs.\n\t}\n\t\n\t///\n\t[Obsolete, EditorBrowsable(EditorBrowsableState.Never)]\n\tpublic bool ZorderTopmost(bool ownerToo)\n\t\t=> (ownerToo ? Get.RootOwnerOrThis() : this).ZorderTopmost();\n\t\n\t/// <summary>\n\t/// Makes this window non-topmost.\n\t/// </summary>\n\t/// <param name=\"afterActiveWindow\">Also place this window below the active non-topmost window in the Z order, unless the active window is this or owner.</param>\n\t/// <remarks>\n\t/// This cannot be a control.\n\t/// Also affects owner and owned windows.\n\t/// Uses API <ms>SetWindowPos</ms>.\n\t/// Supports <see cref=\"lastError\"/>.\n\t/// </remarks>\n\tpublic bool ZorderNoTopmost(bool afterActiveWindow = false) {\n\t\tif (!IsTopmost) return IsAlive;\n\t\tif (afterActiveWindow) {\n\t\t\twnd wa = active;\n\t\t\tif (wa != this && !wa.Is0 && !wa.IsTopmost && !Get.Owners().Contains(wa)) {\n\t\t\t\tif (SetWindowPos(_SWP_ZORDER_OWNER, zorderAfter: wa) && !IsTopmost) return true;\n\t\t\t}\n\t\t}\n\t\treturn SetWindowPos(_SWP_ZORDER_OWNER, zorderAfter: SpecHWND.NOTOPMOST);\n\t}\n\t\n\t//Windows 7-10 bug: cannot make a topmost uiAccess window non-topmost (with HWND_NOTOPMOST, HWND_BOTTOM or non-topmost hwndInsertAfter).\n\t//\tWorkaround: call SWP 2 times. With some Windows updates also need SWP_NOOWNERZORDER.\n\t//\tIt seems it's fixed in current Win10.\n\t//Bug with SWP_NOOWNERZORDER: if used with non-uiAccess windows, then later HWND_TOPMOST does not work.\n\t//\tIt seems it's fixed in current Win10. Can reproduce on Win8.1.\n\t//More problems with topmost uiAccess windows:\n\t//\tCan't insert a uiAccess window after a normal window.\n\t//\tProblems with HWND_BOTTOM and owned windows.\n\t//\tAnd so on. Possibly some now fixed.\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if this is a topmost (always-on-top) window.\n\t/// </summary>\n\tpublic bool IsTopmost => HasExStyle(WSE.TOPMOST);\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if this window is above window <i>w</i> in the Z order.\n\t/// </summary>\n\tpublic bool ZorderIsAbove(wnd w) {\n\t\tif (w.Is0) return false;\n\t\tfor (wnd t = this; !t.Is0;) {\n\t\t\tt = Api.GetWindow(t, Api.GW_HWNDNEXT);\n\t\t\tif (t == w) return true;\n\t\t}\n\t\treturn false;\n\t}\n\t\n\t#endregion\n\t\n\t#region style, exStyle\n\t\n\t/// <summary>\n\t/// Gets window style.\n\t/// </summary>\n\t/// <value>One or more <see cref=\"WS\"/> flags and/or class-specific style flags. Reference: <ms>window styles</ms>.</value>\n\t/// <remarks>Supports <see cref=\"lastError\"/>.</remarks>\n\t/// <seealso cref=\"HasStyle\"/>\n\t/// <seealso cref=\"SetStyle\"/>\n\tpublic WS Style {\n\t\tget => (WS)(uint)GetWindowLong(GWL.STYLE);\n\t}\n\t\n\t/// <summary>\n\t/// Gets window extended style.\n\t/// </summary>\n\t/// <value>One or more <see cref=\"WSE\"/> flags. Reference: <ms>extended window styles</ms>.</value>\n\t/// <remarks>Supports <see cref=\"lastError\"/>.</remarks>\n\t/// <seealso cref=\"HasExStyle\"/>\n\t/// <seealso cref=\"SetExStyle\"/>\n\tpublic WSE ExStyle {\n\t\tget => (WSE)(uint)GetWindowLong(GWL.EXSTYLE);\n\t}\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if the window has all specified style flags (see <see cref=\"Style\"/>).\n\t/// </summary>\n\t/// <param name=\"style\">One or more styles.</param>\n\t/// <param name=\"any\">\n\t/// Return <c>true</c> if has any (not necessary all) of the specified styles.\n\t/// Note: don't use <see cref=\"WS.CAPTION\"/>, because it consists of two other styles - <c>BORDER</c> and <c>DLGFRAME</c>.\n\t/// </param>\n\t/// <remarks>Supports <see cref=\"lastError\"/>.</remarks>\n\tpublic bool HasStyle(WS style, bool any = false) {\n\t\tvar k = Style & style;\n\t\treturn any ? k != 0 : k == style;\n\t}\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if the window has all specified extended style flags (see <see cref=\"ExStyle\"/>).\n\t/// </summary>\n\t/// <param name=\"exStyle\">One or more extended styles.</param>\n\t/// <param name=\"any\">Return <c>true</c> if has any (not necessary all) of the specified styles.</param>\n\t/// <remarks>Supports <see cref=\"lastError\"/>.</remarks>\n\tpublic bool HasExStyle(WSE exStyle, bool any = false) {\n\t\tvar k = ExStyle & exStyle;\n\t\treturn any ? k != 0 : k == exStyle;\n\t}\n\t\n\t/// <summary>\n\t/// Changes window style.\n\t/// </summary>\n\t/// <param name=\"style\">One or more <see cref=\"WS\"/> flags and/or class-specific style flags. Reference: <ms>window styles</ms>.</param>\n\t/// <param name=\"flags\"></param>\n\t/// <returns>Previous value.</returns>\n\t/// <exception cref=\"AuWndException\"/>\n\t/// <seealso cref=\"Style\"/>\n\tpublic WS SetStyle(WS style, WSFlags flags = 0)\n\t\t=> (WS)_SetStyle(false, (int)style, flags);\n\t\n\t/// <summary>\n\t/// Changes window extended style.\n\t/// </summary>\n\t/// <param name=\"style\">One or more <see cref=\"WSE\"/> flags. Reference: <ms>extended window styles</ms>.</param>\n\t/// <param name=\"flags\"></param>\n\t/// <returns>Previous value.</returns>\n\t/// <exception cref=\"AuWndException\"/>\n\t/// <seealso cref=\"ExStyle\"/>\n\tpublic WSE SetExStyle(WSE style, WSFlags flags = 0)\n\t\t=> (WSE)_SetStyle(true, (int)style, flags);\n\t\n\tnint _SetStyle(bool ex, int style, WSFlags flags) {\n\t\tvar gwl = ex ? GWL.EXSTYLE : GWL.STYLE;\n\t\tswitch (flags & (WSFlags.Add | WSFlags.Remove)) {\n\t\tcase WSFlags.Add: style = (int)GetWindowLong(gwl) | style; break;\n\t\tcase WSFlags.Remove: style = (int)GetWindowLong(gwl) & ~style; break;\n\t\t}\n\t\tnint R = SetWindowLong(gwl, style, noException: flags.Has(WSFlags.NoException));\n\t\t\n\t\tif (flags.Has(WSFlags.UpdateNonclient)) SetWindowPos(SWPFlags.FRAMECHANGED | SWPFlags.NOMOVE | SWPFlags.NOSIZE | SWPFlags.NOZORDER | SWPFlags.NOOWNERZORDER | SWPFlags.NOACTIVATE);\n\t\tif (flags.Has(WSFlags.UpdateClient)) Api.InvalidateRect(this, true);\n\t\t\n\t\treturn R;\n\t}\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if has <see cref=\"WS.POPUP\"/> style.\n\t/// </summary>\n\t/// <remarks>Supports <see cref=\"lastError\"/>.</remarks>\n\tpublic bool IsPopupWindow => HasStyle(WS.POPUP);\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if has <see cref=\"WSE.TOOLWINDOW\"/> style.\n\t/// </summary>\n\t/// <remarks>Supports <see cref=\"lastError\"/>.</remarks>\n\tpublic bool IsToolWindow => HasExStyle(WSE.TOOLWINDOW);\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if has <see cref=\"WS.THICKFRAME\"/> style.\n\t/// </summary>\n\t/// <remarks>Supports <see cref=\"lastError\"/>.</remarks>\n\tpublic bool IsResizable => HasStyle(WS.THICKFRAME);\n\t\n\t#endregion\n\t\n\t#region window/class long, control id, prop\n\t\n\t/// <summary>\n\t/// Calls API <ms>GetWindowLongPtr</ms>.\n\t/// </summary>\n\t/// <param name=\"index\">A constant from <see cref=\"GWL\"/>, or an offset in window memory reserved when registering window class.</param>\n\t/// <remarks>\n\t/// Supports <see cref=\"lastError\"/>.\n\t/// In 32-bit process actually calls <c>GetWindowLong</c>, because <c>GetWindowLongPtr</c> is unavailable.\n\t/// </remarks>\n\tpublic nint GetWindowLong(int index) => Api.GetWindowLongPtr(this, index);\n\t\n\t/// <summary>\n\t/// Calls API <ms>SetWindowLongPtr</ms>.\n\t/// </summary>\n\t/// <param name=\"index\">A constant from <see cref=\"GWL\"/>, or an offset in window memory reserved when registering window class.</param>\n\t/// <param name=\"newValue\">New value.</param>\n\t/// <param name=\"noException\">Don't throw exception when fails.</param>\n\t/// <returns>Previous value. If fails and <i>noException</i> <c>true</c>, returns 0, and can be used <see cref=\"lastError\"/>.</returns>\n\t/// <exception cref=\"AuWndException\"/>\n\t/// <remarks>\n\t/// See also API <ms>SetWindowSubclass</ms>.\n\t/// </remarks>\n\tpublic nint SetWindowLong(int index, nint newValue, bool noException = false) {\n\t\tlastError.clear();\n\t\tvar prev = Api.SetWindowLongPtr(this, index, newValue);\n\t\tif (prev == 0 && !noException) {\n\t\t\tvar e = lastError.code;\n\t\t\tif (e != 0 && (Api.GetWindowLongPtr(this, index) != newValue || !IsAlive)) ThrowUseNative(e);\n\t\t\t//if GWL_HWNDPARENT(0), last error \"invalid window handle\"\n\t\t}\n\t\treturn prev;\n\t}\n\t\n\t/// <summary>\n\t/// Gets or sets id of this control.\n\t/// The <c>get</c> function supports <see cref=\"lastError\"/>.\n\t/// </summary>\n\t/// <exception cref=\"AuWndException\">The <c>set</c> function failed.</exception>\n\tpublic int ControlId {\n\t\tget => Api.GetDlgCtrlID(this);\n\t\tset { SetWindowLong(GWL.ID, value); }\n\t}\n\t\n\t/// <summary>\n\t/// Returns an object that manages window properties using API <ms>SetProp</ms> and co.\n\t/// </summary>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// var w = wnd.find(\"* Explorer\");\n\t/// w.Prop.Set(\"example\", 5);\n\t/// print.it(w.Prop[\"example\"]);\n\t/// print.it(w.Prop); //all w properties\n\t/// w.Prop.Remove(\"example\"); //you should always remove window properties if don't want to see unrelated applications crashing after some time. And don't use many unique property names.\n\t/// ]]></code>\n\t/// </example>\n\tpublic WProp Prop => new(this);\n\t\n\t/// <summary>\n\t/// Returns an object that manages the taskbar button of this window: flash, progress, add/delete.\n\t/// </summary>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// var w = wnd.find(0, \"*Notepad\", \"Notepad\");\n\t/// w.TaskbarButton.Delete();\n\t/// ]]></code>\n\t/// </example>\n\tpublic WTaskbarButton TaskbarButton => new(this);\n\t\n\t#endregion\n\t\n\t#region thread, process, is Unicode, is 64-bit, is hung/ghost, is console, UAC, is message-only\n\t\n\t/// <summary>\n\t/// Gets native thread id and process id of this window.\n\t/// Calls API <ms>GetWindowThreadProcessId</ms>.\n\t/// </summary>\n\t/// <returns>Thread id, or 0 if failed. Supports <see cref=\"lastError\"/>.</returns>\n\t/// <remarks>\n\t/// It is not the same as <see cref=\"Environment.CurrentManagedThreadId\"/>.\n\t/// </remarks>\n\tpublic unsafe int GetThreadProcessId(out int processId) {\n\t\tint pid = 0, tid = Api.GetWindowThreadProcessId(this, &pid);\n\t\tprocessId = pid;\n\t\treturn tid;\n\t}\n\t\n\t/// <summary>\n\t/// Gets native thread id of this window. Calls API <ms>GetWindowThreadProcessId</ms>.\n\t/// </summary>\n\t/// <returns>0 if failed. Supports <see cref=\"lastError\"/>.</returns>\n\t/// <remarks>\n\t/// It is not the same as <see cref=\"Environment.CurrentManagedThreadId\"/>.\n\t/// </remarks>\n\tpublic int ThreadId => Api.GetWindowThreadProcessId(this, null); //2 times faster when pid null\n\t\n\t/// <summary>\n\t/// Gets native process id of this window. Calls API <ms>GetWindowThreadProcessId</ms>.\n\t/// </summary>\n\t/// <returns>0 if failed. Supports <see cref=\"lastError\"/>.</returns>\n\tpublic int ProcessId { get { GetThreadProcessId(out var pid); return pid; } }\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if this window belongs to the current thread, <c>false</c> if to another thread.\n\t/// Also returns <c>false</c> when fails (probably window closed or 0 handle). Supports <see cref=\"lastError\"/>.\n\t/// Calls API <ms>GetWindowThreadProcessId</ms>.\n\t/// </summary>\n\tpublic bool IsOfThisThread => Api.GetCurrentThreadId() == ThreadId;\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if this window belongs to the current process, <c>false</c> if to another process.\n\t/// Also returns <c>false</c> when fails (probably window closed or 0 handle). Supports <see cref=\"lastError\"/>.\n\t/// Calls API <ms>GetWindowThreadProcessId</ms>.\n\t/// </summary>\n\tpublic bool IsOfThisProcess => Api.GetCurrentProcessId() == ProcessId;\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if the window is a Unicode window, <c>false</c> if ANSI.\n\t/// Also returns <c>false</c> when fails (probably window closed or 0 handle). Supports <see cref=\"lastError\"/>.\n\t/// Calls API <ms>IsWindowUnicode</ms>.\n\t/// </summary>\n\tpublic bool IsUnicode => Api.IsWindowUnicode(this);\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if the window is of a 32-bit process, <c>false</c> if of a 64-bit process.\n\t/// Also returns <c>false</c> if failed. Supports <see cref=\"lastError\"/>.\n\t/// </summary>\n\t/// <remarks>\n\t/// <note>If you know that the window belongs to current process, instead use <see cref=\"osVersion\"/> functions or <c>IntPtr.Size==4</c>. This function is much slower.</note>\n\t/// </remarks>\n\tpublic bool Is32Bit => process.is32Bit(ProcessId);\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if thread of this window is considered hung (not responding).\n\t/// Calls API <ms>IsHungAppWindow</ms>.\n\t/// </summary>\n\t/// <remarks>Supports <see cref=\"lastError\"/>.</remarks>\n\tpublic bool IsHung => Api.IsHungAppWindow(this);\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if the window is a ghost window that the system creates over a hung (not responding) window to allow the user to minimally interact with it.\n\t/// </summary>\n\tpublic bool IsHungGhost {\n\t\tget => IsHung && ClassNameIs(\"Ghost\") && ProgramName.Eqi(\"DWM.exe\");\n\t\t//Class is \"Ghost\", exe is \"DWM\" (even if no Aero), text sometimes ends with \"(Not Responding)\".\n\t\t//IsHungWindow returns true for ghost window, although it is not actually hung. It is the fastest.\n\t\t//TEST: undocumented API HungWindowFromGhostWindow\n\t}\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if this is a console window (class name <c>\"ConsoleWindowClass\"</c>).\n\t/// </summary>\n\t/// <remarks>Supports <see cref=\"lastError\"/>.</remarks>\n\tpublic bool IsConsole => ClassNameIs(\"ConsoleWindowClass\");\n\t\n\t//FUTURE: GetConsoleText. See QM2.\n\t\n\tinternal void UacCheckAndThrow_(string prefix = null) {\n\t\tif (!Is0 && UacAccessDenied) {\n\t\t\tif (prefix == null) prefix = \"Failed. The\"; else if (prefix.Ends('.')) prefix += \" The\"; //this is to support prefix used by mouse.move: \"The active\"\n\t\t\tthrow new AuException(Api.ERROR_ACCESS_DENIED, prefix + \" window's process has a higher UAC integrity level (admin or uiAccess) than this process.\");\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Gets UAC info of the process.\n\t/// </summary>\n\tpublic uacInfo Uac => uacInfo.ofProcess(ProcessId);\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if [](xref:uac) would not allow to automate the window.\n\t/// It happens when current process has lower UAC integrity level and is not uiAccess, unless UAC is turned off.\n\t/// </summary>\n\t/// <remarks>Supports <see cref=\"lastError\"/>.</remarks>\n\tpublic bool UacAccessDenied {\n\t\tget {\n\t\t\tif (uacInfo.isAdmin) return false;\n\t\t\tlastError.clear();\n\t\t\t//Api.RemoveProp(this, 0); //documented ERROR_ACCESS_DENIED, and used to work, but on latest Win10 GetLastError always returns 0.\n\t\t\tApi.SetWindowLongPtr(this, -1111, 0); //ERROR_INVALID_INDEX or ERROR_ACCESS_DENIED\n\t\t\treturn lastError.code == Api.ERROR_ACCESS_DENIED;\n\t\t}\n\t}\n\t\n\t//These are not useful. Use UacAccessDenied or class uacInfo.\n\t///// <summary>\n\t///// Gets UAC integrity level of window's process.\n\t///// Returns <c>UacIL.Unknown</c> if failed.\n\t///// This function considers <c>UIAccess</c> equal to <c>High</c>.\n\t///// See also: class <see cref=\"uacInfo\"/>.\n\t///// </summary>\n\t//public UacIL UacIntegrityLevel\n\t//{\n\t//\tget { var p = uacInfo.ofProcess(ProcessId); return p == null ? UacIL.Unknown : p.IntegrityLevel; }\n\t//}\n\t\n\t///// <summary>\n\t///// Returns <c>true</c> if window's process has higher UAC integrity level (IL) than current process.\n\t///// Returns <c>true</c> if fails to open process handle, which usually means that the process has higher integrity level.\n\t///// This function considers <c>UIAccess</c> equal to <c>High</c>.\n\t///// See also: class <see cref=\"uacInfo\"/>.\n\t///// </summary>\n\t//public bool UacIntegrityLevelIsHigher\n\t//{\n\t//\tget => UacIntegrityLevel > uacInfo.ofThisProcess.IntegrityLevel;\n\t//}\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if this is a <ms>message-only window</ms>.\n\t/// </summary>\n\t/// <remarks>\n\t/// To find message-only windows use <see cref=\"findFast\"/>.\n\t/// </remarks>\n\tpublic bool IsMessageOnly {\n\t\tget {\n\t\t\tvar t = HasStyle(WS.CHILD) ? Window : this;\n\t\t\tvar v = t.ParentGWL_;\n\t\t\tif (v != default) {\n\t\t\t\tif (!s_messageOnlyParent.IsAlive) {\n\t\t\t\t\tfor (int i = 0; i < 5; i++) {\n\t\t\t\t\t\tvar w = findFast(null, null, true); //find a message-only window\n\t\t\t\t\t\tif (w.Is0) return false; //if there are no message-only windows, this window isn't message only\n\t\t\t\t\t\tw = w.ParentGWL_;\n\t\t\t\t\t\tif (!w.Is0) { s_messageOnlyParent = w; break; } //if 0, either w destroyed/reparented or GetWindowLong(GWL_HWNDPARENT) always returns 0 on this OS version\n\t\t\t\t\t}\n\t\t\t\t\tDebug.Assert(!s_messageOnlyParent.Is0);\n\t\t\t\t}\n\t\t\t\treturn v == s_messageOnlyParent;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t}\n\tstatic wnd s_messageOnlyParent;\n\t\n\t#endregion\n\t\n\t#region text, class, program\n\t\n\t/// <summary>\n\t/// Gets window class name.\n\t/// </summary>\n\t/// <returns><c>null</c> if failed, eg if the window is closed. Supports <see cref=\"lastError\"/>.</returns>\n\t[SkipLocalsInit]\n\tpublic string ClassName {\n\t\tget {\n\t\t\tvar b = stackalloc char[260];\n\t\t\tint n = Api.GetClassName(this, b, 260);\n\t\t\tif (n == 0) return null;\n\t\t\treturn new string(b, 0, n);\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if the class name of this window matches <i>cn</i>. Else returns <c>false</c>.\n\t/// Also returns <c>false</c> when fails (probably window closed or 0 handle). Supports <see cref=\"lastError\"/>.\n\t/// </summary>\n\t/// <param name=\"cn\">Class name. Case-insensitive wildcard. See <see cref=\"ExtString.Like(string, string, bool)\"/>. Cannot be <c>null</c>.</param>\n\t/// <seealso cref=\"IsMatch\"/>\n\tpublic bool ClassNameIs(string cn) => ClassName.Like(cn, true);\n\t\n\t/// <summary>\n\t/// If window class name matches one of strings in <i>classNames</i>, returns 1-based string index. Else returns 0.\n\t/// Also returns 0 if fails to get class name (probably window closed or 0 handle). Supports <see cref=\"lastError\"/>.\n\t/// </summary>\n\t/// <param name=\"classNames\">Class names. Case-insensitive wildcard. See <see cref=\"ExtString.Like(string, string, bool)\"/>. The array and strings cannot be <c>null</c>.</param>\n\tpublic int ClassNameIs(params ReadOnlySpan<string> classNames) {\n\t\tstring s = ClassName; if (s == null) return 0;\n\t\treturn s.Like(true, classNames);\n\t}\n\t\n\t/// <summary>\n\t/// Gets name.\n\t/// </summary>\n\t/// <returns>Returns <c>\"\"</c> if no name. Returns <c>null</c> if failed, eg if the window is closed. Supports <see cref=\"lastError\"/>.</returns>\n\t/// <remarks>\n\t/// Top-level window name usually its title bar text.\n\t/// Control name usually is its text that does not change, for example button or static (label) control text.\n\t/// Unlike <see cref=\"ControlText\"/>, this function usually does not get variable text, for example Edit control editable text, combo box control selected item text, status bar text.\n\t/// Calls <see cref=\"GetText\"/><c>(false, true)</c>.\n\t/// </remarks>\n\t/// <seealso cref=\"SetText\"/>\n\t/// <seealso cref=\"ControlText\"/>\n\t/// <seealso cref=\"NameElm\"/>\n\t/// <seealso cref=\"NameWinforms\"/>\n\tpublic string Name => GetText(false, true);\n\t\n\t/// <summary>\n\t/// If window name matches one of strings in <i>names</i>, returns 1-based string index. Else returns 0.\n\t/// Also returns 0 if fails to get name (probably window closed or 0 handle). Supports <see cref=\"lastError\"/>.\n\t/// </summary>\n\t/// <param name=\"names\">Window names. Case-insensitive wildcard. See <see cref=\"ExtString.Like(string, string, bool)\"/>. The array and strings cannot be <c>null</c>.</param>\n\tpublic int NameIs(params ReadOnlySpan<string> names) {\n\t\tstring s = Name; if (s == null) return 0;\n\t\treturn s.Like(true, names);\n\t}\n\t\n\t/// <summary>\n\t/// Gets window name using API <c>InternalGetWindowText</c>. The same as <c>GetText(false, false)</c>.\n\t/// This should be a top-level window, because does not process ampersands.\n\t/// </summary>\n\tinternal string NameTL_ => _GetTextFast(false);\n\t\n\t/// <summary>\n\t/// Gets control text.\n\t/// </summary>\n\t/// <returns>Returns <c>\"\"</c> if no text. Returns <c>null</c> if failed, eg if the window is closed. Supports <see cref=\"lastError\"/>.</returns>\n\t/// <remarks>\n\t/// Unlike <see cref=\"Name\"/>, this function prefers variable text, for example Edit control text, combo box selected item text, status bar text.\n\t/// For controls that cannot have such text (eg button, static), it usually gets the same text as <c>Name</c>. For example button and static (label) controls.\n\t/// Much slower than <c>Name</c>. Fails if the window is hung.\n\t/// Calls <see cref=\"GetText\"/><c>(true, false)</c>.\n\t/// </remarks>\n\t/// <seealso cref=\"SetText\"/>\n\t/// <seealso cref=\"Name\"/>\n\tpublic string ControlText => GetText(true, false);\n\t\n\t/// <summary>\n\t/// Gets window/control name or control text.\n\t/// This is a low-level function. You can instead use <see cref=\"Name\"/> and <see cref=\"ControlText\"/>.\n\t/// </summary>\n\t/// <returns>Returns <c>\"\"</c> if no text. Returns <c>null</c> if failed, eg if the window is closed. Supports <see cref=\"lastError\"/>.</returns>\n\t/// <param name=\"getText\">\n\t/// How to get text:\n\t/// <br/>• <c>false</c> - use API <ms>InternalGetWindowText</ms>. This is used by <see cref=\"Name\"/>.\n\t/// <br/>• <c>true</c> - use API <ms>WM_GETTEXT</ms>. It is slow and prefers editable text. This is used by <see cref=\"ControlText\"/>. Fails if the window is hung.\n\t/// <br/>• <c>null</c> - try <c>InternalGetWindowText</c>. If it gets <c>\"\"</c> and this is a control, then try <c>WM_GETTEXT</c>.\n\t/// </param>\n\t/// <param name=\"removeUnderlineAmpersand\">\n\t/// Remove the invisible <c>'&amp;'</c> characters that are used to underline keyboard shortcuts with the <c>Alt</c> key.\n\t/// Removes only if this is a control (has style <see cref=\"WS.CHILD\"/>).\n\t/// Calls <see cref=\"StringUtil.RemoveUnderlineChar\"/>.\n\t/// </param>\n\t/// <seealso cref=\"SetText\"/>\n\t/// <seealso cref=\"NameElm\"/>\n\t/// <seealso cref=\"NameWinforms\"/>\n\tpublic string GetText(bool? getText = null, bool removeUnderlineAmpersand = true) {\n\t\tvar R = getText switch { true => _GetTextSlow(), false => _GetTextFast(false), _ => _GetTextFast(true) };\n\t\t\n\t\tif (removeUnderlineAmpersand\n\t\t\t&& !R.NE()\n\t\t\t//&& R.Contains('&') //slower than HasStyle if the string is longer than 20\n\t\t\t&& HasStyle(WS.CHILD)\n\t\t\t) R = StringUtil.RemoveUnderlineChar(R);\n\t\t\n\t\treturn R;\n\t}\n\t\n\t/// <summary>\n\t/// Gets text.\n\t/// Returns <c>\"\"</c> if it is empty.\n\t/// Calls API <ms>InternalGetWindowText</ms>. If it fails, and <c>getControlTextIfEmpty</c>==<c>true</c>, and this is a control, calls <c>_GetTextSlow</c>, which uses <c>WM_GETTEXT</c>.\n\t/// </summary>\n\t/// <returns><c>null</c> if failed, eg if the control is destroyed or its thread is hung. Supports <see cref=\"lastError\"/>.</returns>\n\t[SkipLocalsInit]\n\tstring _GetTextFast(bool useSlowIfEmpty) {\n\t\tif (Is0) { lastError.code = Api.ERROR_INVALID_WINDOW_HANDLE; return null; } //return early. Used in properties of wnd and other types. They can be called not explicitly from code, eg by debugger, record.ToString(), etc. Eg a record may have a property of wnd type, and it may be 0 (it's valid).\n\t\tusing FastBuffer<char> b = new();\n\t\tfor (; ; b.More()) {\n\t\t\tlastError.clear();\n\t\t\tint nr = Api.InternalGetWindowText(this, b.p, b.n);\n\t\t\tif (nr < b.n - 1) {\n\t\t\t\tif (nr > 0) return new string(b.p, 0, nr);\n\t\t\t\tif (lastError.code != 0) return null;\n\t\t\t\tif (useSlowIfEmpty && HasStyle(WS.CHILD)) return _GetTextSlow();\n\t\t\t\treturn \"\";\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Gets text.\n\t/// Returns <c>\"\"</c> if it is empty.\n\t/// Uses <c>WM_GETTEXT</c>.\n\t/// </summary>\n\t/// <returns><c>null</c> if failed, eg if the control is destroyed or its thread is hung. Supports <see cref=\"lastError\"/>.</returns>\n\t[SkipLocalsInit]\n\tstring _GetTextSlow() {\n\t\tif (Is0) { lastError.code = Api.ERROR_INVALID_WINDOW_HANDLE; return null; }\n\t\tif (!SendTimeout(5000, out nint n, Api.WM_GETTEXTLENGTH)) return null;\n\t\tif (n < 1) return \"\";\n\t\t\n\t\tusing FastBuffer<char> b = new((int)n + 1);\n\t\tif (!SendTimeout(30000, out n, Api.WM_GETTEXT, b.n, b.p)) return null;\n\t\tif (n < 1) return \"\";\n\t\tb.p[n] = '\\0';\n\t\treturn b.GetStringFindLength(); //info: some controls return incorrect n, eg including '\\0'\n\t\t\n\t\t//note: cannot do this optimization:\n\t\t//\tAt first allocate stack memory and send WM_GETTEXT without WM_GETTEXTLENGTH. Then use WM_GETTEXTLENGTH/WM_GETTEXT if returned size is buffer length - 1.\n\t\t//\tIt works with most controls, but some controls return 0 if buffer is too small. Eg SysLink. The WM_GETTEXT documentation does not say what should happen when buffer is too small.\n\t\t//\tThe speed is important for wnd.Child().\n\t\t//\tIt is ~30% faster when all controls have text, but not much if called for many controls that don't have text (then we don't use WM_GETTEXT).\n\t}\n\t\n\t/// <summary>\n\t/// Sets window/control name or control text.\n\t/// </summary>\n\t/// <param name=\"text\">Text. Can be <c>null</c>, it is the same as <c>\"\"</c>.</param>\n\t/// <remarks>\n\t/// Uses API <ms>WM_SETTEXT</ms>.\n\t/// Top-level window name usually its title bar text.\n\t/// For variable-text controls (edit, combo box, status bar, ...) this usually is the text that <see cref=\"ControlText\"/> would get.\n\t/// For other controls (button, static, ...) and top-level windows this usually is the text that <see cref=\"Name\"/> would get.\n\t/// </remarks>\n\t/// <exception cref=\"AuWndException\">Failed, for example the window is closed.</exception>\n\t/// <seealso cref=\"GetText\"/>\n\t/// <seealso cref=\"Name\"/>\n\t/// <seealso cref=\"ControlText\"/>\n\tpublic void SetText(string text) {\n\t\tif (!SendTimeout(30000, out var _, Api.WM_SETTEXT, 0, text ?? \"\", 0)) ThrowUseNative();\n\t}\n\t\n\t//rejected: faster but not better than NameElm. Or must be improved.\n\t//\tInstead, let Child find label control, and then navigate with Get.Right() etc.\n\t///// <summary>\n\t///// Gets <see cref=\"Name\"/> of previous (in Z order) sibling control.\n\t///// Returns <c>null</c> if there is no such control.\n\t///// </summary>\n\t///// <remarks>\n\t///// Can be used to identify controls that have no name but have a named control (label) at the left or above. For example <c>Edit</c> and <c>ComboBox</c> controls in dialogs.\n\t///// In such cases this function works like <see cref=\"NameElm\"/>, but is faster and supports any sibling control type.\n\t///// </remarks>\n\t//public string NameLabel\n\t//{\n\t//\tget\n\t//\t{\n\t//\t\tfor(var w = this; ;) {\n\t//\t\t\tvar p = w.Get.DirectParent;\n\t//\t\t\tif(p.Is0) return null;\n\t//\t\t\tw = w.Get.Previous(); if(!w.Is0) return w.Name;\n\t//\t\t\tw = p;\n\t//\t\t}\n\t//\t\t//_todo: if the label control is not at the left/above, find topmost control at the left/above\n\t//\t}\n\t//}\n\t\n\t/// <summary>\n\t/// Gets <see cref=\"elm.Name\"/> of the UI element (role <c>WINDOW</c>) of this window or control.\n\t/// </summary>\n\t/// <returns>Returns <c>\"\"</c> if the object has no name or failed to get it. Returns <c>null</c> if invalid window handle.</returns>\n\tpublic string NameElm => elm.NameOfWindow_(this);\n\t\n\t/// <summary>\n\t/// Gets the <see cref=\"System.Windows.Forms.Control.Name\"/> property value of a .NET Windows Forms control.\n\t/// </summary>\n\t/// <returns><c>null</c> if it is not a Windows Forms control or if failed.</returns>\n\t/// <remarks>\n\t/// <note>Use this with controls of other processes. Don't use with your controls, when you have a <c>Control</c> object.</note>\n\t/// \n\t/// <note>Slow when getting names of multiple controls in a window. Instead create a <see cref=\"WinformsControlNames\"/> instance and call its <see cref=\"WinformsControlNames.GetControlName\"/> method for each control.</note>\n\t/// </remarks>\n\t/// <seealso cref=\"WinformsControlNames.IsWinformsControl\"/>\n\tpublic string NameWinforms => WinformsControlNames.GetSingleControlName(this);\n\t\n\t/// <summary>\n\t/// Gets filename of process executable file, like <c>\"notepad.exe\"</c>.\n\t/// </summary>\n\t/// <returns><c>null</c> if failed.</returns>\n\t/// <remarks>\n\t/// Calls <see cref=\"ProcessId\"/> and <see cref=\"process.getName\"/>.\n\t/// This function is much slower than getting window name or class name. Don't use code like <c>if(w.ProgramName==\"A\" || w.ProgramName==\"B\")</c>. Instead use <c>var s=w.ProgramName; if(s==\"A\" || s==\"B\")</c>.\n\t/// </remarks>\n\tpublic string ProgramName => process.GetNameCached_(this, ProcessId);\n\t\n\t/// <summary>\n\t/// If window program name matches one of strings in <i>programNames</i>, returns 1-based string index. Else returns 0.\n\t/// Also returns 0 if fails to get program name (probably window closed or 0 handle). Supports <see cref=\"lastError\"/>.\n\t/// </summary>\n\t/// <param name=\"programNames\">Program names, like <c>\"notepad.exe\"</c>. Case-insensitive wildcard. See <see cref=\"ExtString.Like(string, string, bool)\"/>. The array and strings cannot be <c>null</c>.</param>\n\tpublic int ProgramNameIs(params ReadOnlySpan<string> programNames) {\n\t\tstring s = ProgramName; if (s == null) return 0;\n\t\treturn s.Like(true, programNames);\n\t}\n\t\n\t/// <summary>\n\t/// Gets full path of process executable file.\n\t/// </summary>\n\t/// <returns><c>null</c> if failed.</returns>\n\t/// <remarks>\n\t/// Calls <see cref=\"ProcessId\"/> and <see cref=\"process.getName\"/>.\n\t/// This function is much slower than getting window name or class name. Don't use code like <c>if(w.ProgramPath==\"A\" || w.ProgramPath==\"B\")</c>. Instead use <c>var s=w.ProgramPath; if(s==\"A\" || s==\"B\")</c>.\n\t/// </remarks>\n\tpublic string ProgramPath => process.GetNameCached_(this, ProcessId, true);\n\t\n\t/// <summary>\n\t/// Gets description of process executable file.\n\t/// </summary>\n\t/// <returns><c>null</c> if failed.</returns>\n\t/// <remarks>\n\t/// Calls <see cref=\"ProcessId\"/> and <see cref=\"process.getDescription\"/>.\n\t/// This function is slow. Much slower than <see cref=\"ProgramName\"/>.\n\t/// </remarks>\n\tpublic string ProgramDescription => process.getDescription(ProcessId);\n\t\n\t#endregion\n\t\n\t#region close, destroy\n\t\n\t/// <summary>\n\t/// Closes the window.\n\t/// </summary>\n\t/// <returns><c>true</c> if successfully closed or if it was already closed (the handle is 0 or invalid) or if <i>noWait</i>==<c>true</c>.</returns>\n\t/// <param name=\"noWait\">\n\t/// If <c>true</c>, does not wait until the window is closed.\n\t/// If <c>false</c>, waits about 1 s (depends on window type etc) until the window is destroyed or disabled.\n\t/// </param>\n\t/// <param name=\"useXButton\">\n\t/// If <c>false</c> (default), uses API message <ms>WM_CLOSE</ms>.\n\t/// If <c>true</c>, uses API message <ms>WM_SYSCOMMAND SC_CLOSE</ms>, like when the user clicks the <c>X</c> button in the title bar.\n\t/// Most windows can be closed with any of these messages, but some respond properly only to one of them. For example, some applications on <c>WM_CLOSE</c> don't exit, although the main window is closed. Some applications don't respond to <c>WM_SYSCOMMAND</c> if it is posted soon after opening the window.\n\t/// </param>\n\t/// <remarks>\n\t/// The window may refuse to be closed. For example, it may be hung, or hide itself instead, or display a \"Save?\" message box, or is a dialog without <c>X</c> button, or just need more time to close it.\n\t/// If the window is of this thread, just calls <see cref=\"Send\"/> or <see cref=\"Post\"/> (if <i>noWait</i>==<c>true</c>) and returns <c>true</c>.\n\t/// </remarks>\n\t/// <seealso cref=\"WaitForClosed\"/>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// //close all Notepad windows\n\t/// foreach (var w in wnd.findAll(\"* Notepad\", \"Notepad\")) w.Close();\n\t/// ]]></code>\n\t/// </example>\n\tpublic bool Close(bool noWait = false, bool useXButton = false) {\n\t\tif (!IsAlive) return true;\n\t\t\n\t\tint msg = Api.WM_CLOSE; int wparam = 0;\n\t\tif (useXButton) { msg = Api.WM_SYSCOMMAND; wparam = Api.SC_CLOSE; }\n\t\t\n\t\tif (IsOfThisThread) {\n\t\t\tif (noWait) Post(msg, wparam);\n\t\t\telse Send(msg, wparam);\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\t//Some windows cannot be properly closed using only WM_CLOSE. For example, VS 7 window closed, but the process not.\n\t\t//Some windows (eg IE), ignore SC_CLOSE if it is posted soon after starting the program. Only if using Post, not if SendNotify.\n\t\t//When the user closes a window, Windows sends SC_CLOSE. DefWindowProc on SC_CLOSE sends WM_CLOSE, except if disabled.\n\t\t\n\t\t//Other apps use either WM_CLOSE or SC_CLOSE.\n\t\t//\tTask Manager and taskbar send (not post) SC_CLOSE.\n\t\t//\tProcess Explorer and taskkill post WM_CLOSE.\n\t\t\n\t\t//For our purposes WM_CLOSE is probably better.\n\t\t//\tQM2 used SC_CLOSE or WM_CLOSE depending on window style.\n\t\t\n\t\t//note: Don't use SendX messages because of possible crashes and other anomalies.\n\t\t//\tEg it can hide (not close) some windows, and next app instance does not start (eg Firefox).\n\t\t//\tImagine if the target window's app is in SendMessage which dispatches foreign messages, and after it returns, the window is already died.\n\t\t\n\t\tbool ok = Post(msg, wparam);\n\t\tif (!ok) {\n\t\t\t//print.it(lastError.code); //0 when UAC access denied\n\t\t\tif (!useXButton) ok = Post(Api.WM_SYSCOMMAND, Api.SC_CLOSE); //UAC blocks WM_CLOSE but not WM_SYSCOMMAND\n\t\t}\n\t\t\n\t\t//if(!noWait.HasValue) noWait = this_thread_is_UI; //rejected\n\t\tif (noWait) return true;\n\t\t\n\t\tif (ok) {\n\t\t\tfor (int i = 0; i < 100; i++) {\n\t\t\t\tThread.Sleep(15);\n\t\t\t\tif (!IsEnabled(false)) break; //destroyed or has an owned modal dialog box, eg \"Save?\"\n\t\t\t\t\n\t\t\t\t//Wait less if hidden, eg has a tray icon.\n\t\t\t\t//Also if a popup, eg a Yes/No message box (disabled X button).\n\t\t\t\t//Also if child.\n\t\t\t\t//if(!IsVisible && !_IsBusy(2)) i += 4; //unreliable, too dirty\n\t\t\t\tif (!IsVisible || (Style & (WS.POPUP | WS.CHILD)) != 0) i += 2;\n\t\t\t\t\n\t\t\t\tif (i >= 50) {\n\t\t\t\t\tif (!SendTimeout(200, out _, 0)) {\n\t\t\t\t\t\tif (!IsAlive || IsHung) break;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tMinimalSleepNoCheckThread_();\n\t\tWndUtil.WaitForAnActiveWindow();\n\t\t\n\t\treturn !IsAlive;\n\t}\n\t\n\t//bool _IsBusy(int milliseconds)\n\t//{\n\t//\t//Need to measure time. Cannot use just 2 ms timeout and ST return value because of the system timer default period 15.6 ms etc.\n\t//\tvar t = perf.mcs;\n\t//\tSendTimeout(5 + milliseconds, 0, flags: 0);\n\t//\tvar d = perf.mcs - t;\n\t//\t//print.it(d);\n\t//\treturn (d >= milliseconds * 1000L);\n\t//}\n\t\n\t//Rarely used. It is easy, and there is example in Close() help: foreach (var w in wnd.findAll(\"* Notepad\", \"Notepad\")) w.Close();\n\t///// <summary>\n\t///// Closes all matching windows.\n\t///// Calls <see cref=\"FindAll\"/>. All parameters etc are the same. Then calls <see cref=\"Close\"/> for each found window.\n\t///// Returns the number of found windows.\n\t///// </summary>\n\t//public static int closeAll(\n\t//\tstring name, string cn = null, WOwner of = default,\n\t//\tWFlags flags = 0, Func<wnd, bool> f = null, WContains contains = default\n\t//\t)\n\t//{\n\t//\tvar a = FindAll(name, cn, of, flags, f, contains);\n\t//\tforeach(wnd w in a) w.Close();\n\t//\treturn a.Count;\n\t//}\n\t\n\t#endregion\n\t\n}\n"
  },
  {
    "path": "Au/wnd/wndChildFinder.cs",
    "content": "using static Au.wnd.Internal_;\n\nnamespace Au;\n\n/// <summary>\n/// Finds window controls (child windows). Contains name and other parameters of controls to find.\n/// </summary>\n/// <remarks>\n/// Can be used instead of <see cref=\"wnd.Child\"/> or <see cref=\"wnd.ChildAll\"/>.\n/// Also can be used to find window that contains certain control, like in examples.\n/// </remarks>\n/// <example>\n/// Find window that contains certain control, and get the control too.\n/// <code><![CDATA[\n/// var cf = new wndChildFinder(\"Password*\", \"Static\");\n/// wnd w = wnd.find(cn: \"#32770\", also: t => t.HasChild(cf));\n/// print.it(w);\n/// print.it(cf.Result);\n/// ]]></code>\n/// The same with parameter <i>contains</i>.\n/// <code><![CDATA[\n/// var cf = new wndChildFinder(\"Password*\", \"Static\");\n/// wnd w = wnd.find(cn: \"#32770\", contains: cf);\n/// print.it(w);\n/// print.it(cf.Result);\n/// ]]></code>\n/// </example>\npublic class wndChildFinder {\n\tenum _NameIs : byte { name, text, elmName, wfName }\n\t\n\treadonly wildex _name;\n\treadonly wildex _cn;\n\treadonly Func<wnd, bool> _also;\n\tWinformsControlNames _wfControls;\n\treadonly int _skipCount;\n\treadonly WCFlags _flags;\n\treadonly int? _id;\n\treadonly _NameIs _nameIs;\n\t\n\t/// <summary>\n\t/// See <see cref=\"wnd.Child\"/>.\n\t/// </summary>\n\t/// <exception cref=\"ArgumentException\">\n\t/// - <i>name</i> starts with <c>\"***\"</c>, but the prefix is invalid.\n\t/// - <i>cn</i> is <c>\"\"</c>. To match any, use <c>null</c>.\n\t/// - Invalid wildcard expression (<c>\"**options \"</c> or regular expression).\n\t/// </exception>\n\t/// <inheritdoc cref=\"wnd.Child(string, string, WCFlags, int?, Func{wnd, bool}, int)\" path=\"/param\"/>\n\tpublic wndChildFinder(\n\t\t[ParamString(PSFormat.Wildex)] string name = null,\n\t\t[ParamString(PSFormat.Wildex)] string cn = null,\n\t\tWCFlags flags = 0, int? id = null, Func<wnd, bool> also = null, int skip = 0\n\t\t) {\n\t\tif (cn != null) {\n\t\t\tif (cn.Length == 0) throw new ArgumentException(\"Class name cannot be \\\"\\\". Use null.\");\n\t\t\t_cn = cn;\n\t\t}\n\t\tif (name != null) {\n\t\t\tswitch (StringUtil.ParseParam3Stars_(ref name, \"text\", \"elmName\", \"wfName\"/*, \"label\"*/)) {\n\t\t\tcase -1: throw new ArgumentException(\"Invalid name prefix. Can be: \\\"***text \\\", \\\"***elmName \\\", \\\"***wfName \\\".\"); //, \\\"***label \\\"\n\t\t\tcase 1: _nameIs = _NameIs.text; break;\n\t\t\tcase 2: _nameIs = _NameIs.elmName; break;\n\t\t\tcase 3: _nameIs = _NameIs.wfName; break;\n\t\t\t\t//case 4: _nameIs = _NameIs.label; break;\n\t\t\t}\n\t\t\t_name = name;\n\t\t}\n\t\t_flags = flags;\n\t\t_id = id;\n\t\t_also = also;\n\t\t_skipCount = skip;\n\t}\n\t\n\t/// <summary>\n\t/// The found control.\n\t/// </summary>\n\tpublic wnd Result { get; internal set; }\n\t\n\t/// <summary>\n\t/// Finds the specified child control, like <see cref=\"wnd.Child\"/>.\n\t/// </summary>\n\t/// <returns>If found, returns <see cref=\"Result\"/>, else <c>default(wnd)</c>.</returns>\n\t/// <param name=\"wParent\">Direct or indirect parent window. Can be top-level window or control.</param>\n\t/// <exception cref=\"AuWndException\">Invalid <i>wParent</i>.</exception>\n\t/// <remarks>\n\t/// Functions <c>Find</c> and <see cref=\"Exists\"/> differ only in their return types.\n\t/// </remarks>\n\tpublic wnd Find(wnd wParent) => Exists(wParent) ? Result : default;\n\t\n\t/// <summary>\n\t/// Finds the specified child control, like <see cref=\"wnd.Child\"/>. Can wait and throw <see cref=\"NotFoundException\"/>.\n\t/// </summary>\n\t/// <returns>If found, returns <see cref=\"Result\"/>. Else throws exception or returns <c>default(wnd)</c> (if <i>wait</i> negative).</returns>\n\t/// <param name=\"wParent\">Direct or indirect parent window. Can be top-level window or control.</param>\n\t/// <param name=\"wait\">The wait timeout, seconds. If 0, does not wait. If negative, does not throw exception when not found.</param>\n\t/// <exception cref=\"AuWndException\">Invalid <i>wParent</i>.</exception>\n\t/// <exception cref=\"NotFoundException\" />\n\t/// <remarks>\n\t/// Functions <c>Find</c> and <see cref=\"Exists\"/> differ only in their return types.\n\t/// </remarks>\n\tpublic wnd Find(wnd wParent, Seconds wait) => Exists(wParent, wait) ? Result : default;\n\t\n\t/// <returns>If found, sets <see cref=\"Result\"/> and returns <c>true</c>, else <c>false</c>.</returns>\n\t/// <inheritdoc cref=\"Find(wnd)\"/>\n\tpublic bool Exists(wnd wParent) {\n\t\tusing var k = new WndList_(_AllChildren(wParent));\n\t\treturn _FindInList(wParent, k) >= 0;\n\t}\n\t\n\t/// <returns>If found, sets <see cref=\"Result\"/> and returns <c>true</c>. Else throws exception or returns <c>false</c> (if <i>wait</i> negative).</returns>\n\t/// <inheritdoc cref=\"Find(wnd, Seconds)\"/>\n\tpublic bool Exists(wnd wParent, Seconds wait) {\n\t\tvar r = wait.Exists_() ? Exists(wParent) : Au.wait.until(wait, () => Exists(wParent));\n\t\treturn r || wait.ReturnFalseOrThrowNotFound_();\n\t}\n\t\n\tArrayBuilder_<wnd> _AllChildren(wnd wParent) {\n\t\twParent.ThrowIfInvalid();\n\t\treturn EnumWindows2(EnumAPI.EnumChildWindows,\n\t\t\tonlyVisible: 0 == (_flags & WCFlags.HiddenToo),\n\t\t\tsortFirstVisible: true,\n\t\t\twParent: wParent,\n\t\t\tdirectChild: 0 != (_flags & WCFlags.DirectChild));\n\t}\n\t\n\t/// <summary>\n\t/// Finds the specified control in a list of controls.\n\t/// The <see cref=\"Result\"/> property will be the control.\n\t/// </summary>\n\t/// <returns>0-based index, or -1 if not found.</returns>\n\t/// <param name=\"a\">List of controls, for example returned by <see cref=\"wnd.getwnd.Children\"/>.</param>\n\t/// <param name=\"wParent\">Direct or indirect parent window. Used only for flag <c>DirectChild</c>.</param>\n\tpublic int FindInList(IEnumerable<wnd> a, wnd wParent = default) {\n\t\tusing var k = new WndList_(a);\n\t\treturn _FindInList(wParent, k);\n\t}\n\t\n\t/// <summary>\n\t/// Finds all matching child controls, like <see cref=\"wnd.ChildAll\"/>.\n\t/// </summary>\n\t/// <returns>Array containing zero or more <see cref=\"wnd\"/>.</returns>\n\t/// <param name=\"wParent\">Direct or indirect parent window. Can be top-level window or control.</param>\n\t/// <exception cref=\"AuWndException\">Invalid <i>wParent</i>.</exception>\n\tpublic wnd[] FindAll(wnd wParent) {\n\t\treturn _FindAll(new WndList_(_AllChildren(wParent)), wParent);\n\t}\n\t\n\t/// <summary>\n\t/// Finds all matching controls in a list of controls.\n\t/// </summary>\n\t/// <returns>Array containing zero or more <see cref=\"wnd\"/>.</returns>\n\t/// <param name=\"a\">List of controls, for example returned by <see cref=\"wnd.getwnd.Children\"/>.</param>\n\t/// <param name=\"wParent\">Direct or indirect parent window. Used only for flag <c>DirectChild</c>.</param>\n\tpublic wnd[] FindAllInList(IEnumerable<wnd> a, wnd wParent = default) {\n\t\treturn _FindAll(new WndList_(a), wParent);\n\t}\n\t\n\twnd[] _FindAll(WndList_ k, wnd wParent) {\n\t\tusing (k) {\n\t\t\tusing var ab = new ArrayBuilder_<wnd>();\n\t\t\t_FindInList(wParent, k, w => ab.Add(w)); //CONSIDER: ab could be part of _WndList. Now the delegate creates garbage.\n\t\t\treturn ab.ToArray();\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Returns index of matching element or -1.\n\t/// </summary>\n\t/// <param name=\"wParent\">Parent window. Can be <c>default(wnd)</c> if <i>inList</i> and no <c>DirectChild</c> flag and not using winforms name.</param>\n\t/// <param name=\"a\">List of <see cref=\"wnd\"/>. Does not dispose it.</param>\n\t/// <param name=\"getAll\">If not <c>null</c>, calls it for all matching and returns -1.</param>\n\tint _FindInList(wnd wParent, WndList_ a, Action<wnd> getAll = null) {\n\t\tResult = default;\n\t\tif (a.Type == WndList_.ListType.None) return -1;\n\t\tbool inList = a.Type != WndList_.ListType.ArrayBuilder;\n\t\tint skipCount = _skipCount;\n\t\t\n\t\ttry { //will need to dispose something\n\t\t\tfor (int index = 0; a.Next(out wnd w); index++) {\n\t\t\t\tif (w.Is0) continue;\n\t\t\t\t\n\t\t\t\tif (inList) { //else the enum function did this\n\t\t\t\t\tif (!_flags.Has(WCFlags.HiddenToo)) {\n\t\t\t\t\t\tif (!w.IsVisibleIn_(wParent)) continue;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tif (_flags.Has(WCFlags.DirectChild) && !wParent.Is0) {\n\t\t\t\t\t\tif (w.ParentGWL_ != wParent) continue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (_id != null) {\n\t\t\t\t\tif (w.ControlId != _id.Value) continue;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (_cn != null) {\n\t\t\t\t\tif (!_cn.Match(w.ClassName)) continue;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (_name != null) {\n\t\t\t\t\tstring s;\n\t\t\t\t\tswitch (_nameIs) {\n\t\t\t\t\tcase _NameIs.text:\n\t\t\t\t\t\ts = w.ControlText;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase _NameIs.elmName:\n\t\t\t\t\t\ts = w.NameElm;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase _NameIs.wfName:\n\t\t\t\t\t\tif (_wfControls == null) {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t_wfControls = new WinformsControlNames(wParent.Is0 ? w : wParent);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcatch (AuWndException) { //invalid parent window\n\t\t\t\t\t\t\t\treturn -1;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcatch (AuException e) { //probably process of higher UAC integrity level\n\t\t\t\t\t\t\t\tprint.warning($\"Failed to get winforms control names. {e.Message}\");\n\t\t\t\t\t\t\t\treturn -1;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\ts = _wfControls.GetControlName(w);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t//case _NameIs.label:\n\t\t\t\t\t//\ts = w.NameLabel;\n\t\t\t\t\t//\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\ts = w.Name;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tif (!_name.Match(s)) continue;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (_also != null && !_also(w)) continue;\n\t\t\t\t\n\t\t\t\tif (getAll != null) {\n\t\t\t\t\tgetAll(w);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (skipCount-- > 0) continue;\n\t\t\t\t\n\t\t\t\tResult = w;\n\t\t\t\treturn index;\n\t\t\t}\n\t\t}\n\t\tfinally {\n\t\t\tif (_wfControls != null) { _wfControls.Dispose(); _wfControls = null; }\n\t\t}\n\t\t\n\t\treturn -1;\n\t}\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if control <c>c</c> properties match the specified properties.\n\t/// </summary>\n\t/// <param name=\"c\">A control. Can be 0/invalid, then returns <c>false</c>.</param>\n\t/// <param name=\"wParent\">Direct or indirect parent window. If used, returns <c>false</c> if it isn't parent (also depends on flag <c>DirectChild</c>).</param>\n\tpublic bool IsMatch(wnd c, wnd wParent = default) {\n\t\tif (!wParent.Is0 && !c.IsChildOf(wParent)) {\n\t\t\tResult = default;\n\t\t\treturn false;\n\t\t}\n\t\treturn 0 == _FindInList(wParent, new WndList_(c));\n\t}\n\t\n\t///\n\tpublic override string ToString() {\n\t\tusing (new StringBuilder_(out var b)) {\n\t\t\tb.Append('[');\n\t\t\t_Append(\"name\", _name, true);\n\t\t\t_Append(\"cn\", _cn, true);\n\t\t\tif (_id != null) _Append(\"id\", _id.ToString(), false);\n\t\t\tif (_flags != 0) _Append(\"flags\", _flags.ToString(), false);\n\t\t\tif (_also != null) _Append(\"also\", \"...\", false);\n\t\t\tif (_skipCount != 0) _Append(\"skip\", _skipCount.ToString(), false);\n\t\t\tb.Append(']');\n\t\t\treturn b.ToString();\n\t\t\t\n\t\t\tvoid _Append(string k, object v, bool isString) {\n\t\t\t\tif (v == null) return;\n\t\t\t\tif (b.Length > 1) b.Append(\", \");\n\t\t\t\tvar s = v.ToString();\n\t\t\t\tif (isString) s = s.Escape(limit: 50, quote: true);\n\t\t\t\tb.Append(k).Append(\": \").Append(s);\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au/wnd/wndFinder.cs",
    "content": "using static Au.wnd.Internal_;\n\n//TODO3: if no name, prefer non-tooltip window. Now often finds tooltip instead of the wanted window. Eg Java menus.\n\nnamespace Au;\n\n/// <summary>\n/// Finds top-level windows (<see cref=\"wnd\"/>). Contains name and other parameters of windows to find.\n/// </summary>\n/// <remarks>\n/// Can be used instead of <see cref=\"wnd.find\"/> or <see cref=\"wnd.findAll\"/>.\n/// These codes are equivalent:\n/// <code><![CDATA[wnd w = wnd.find(a, b, c, d, e); if(!w.Is0) print.it(w);]]></code>\n/// <code><![CDATA[var p = new wndFinder(a, b, c, d, e); if(p.Exists()) print.it(p.Result);]]></code>\n/// Also can find in a list of windows.\n/// </remarks>\npublic class wndFinder {\n\treadonly wildex _name;\n\treadonly wildex _cn;\n\treadonly wildex _program;\n\treadonly int _processId;\n\treadonly int _threadId;\n\treadonly wnd _owner;\n\treadonly Func<wnd, bool> _also;\n\treadonly WFlags _flags;\n\treadonly WContains _contains;\n\n#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member\n\t/// <summary>\n\t/// Parsed parameter values. All read-only.\n\t/// </summary>\n\tpublic TProps Props => new(this);\n\n\t[NoDoc]\n\tpublic struct TProps {\n\t\treadonly wndFinder _f;\n\t\tinternal TProps(wndFinder f) { _f = f; }\n\n\t\tpublic wildex name => _f._name;\n\t\tpublic wildex cn => _f._cn;\n\t\tpublic wildex program => _f._program;\n\t\tpublic int processId => _f._processId;\n\t\tpublic int threadId => _f._threadId;\n\t\tpublic wnd owner => _f._owner;\n\t\tpublic WFlags flags => _f._flags;\n\t\tpublic Func<wnd, bool> also => _f._also;\n\t\tpublic WContains contains => _f._contains;\n\n\t\t/// <summary>\n\t\t/// After unsuccessful <see cref=\"IsMatch\"/> indicates the parameter that does not match.\n\t\t/// </summary>\n\t\tpublic EProps DoesNotMatch => _f._stopProp;\n\t}\n\n\tEProps _stopProp;\n\n\t[NoDoc]\n\tpublic enum EProps { name = 1, cn, of, also, contains, visible, cloaked, }\n\n\tpublic override string ToString() {\n\t\tusing (new StringBuilder_(out var b)) {\n\t\t\t_Append(\"name\", _name);\n\t\t\t_Append(\"cn\", _cn);\n\t\t\tif (_program != null) _Append(\"program\", _program); else if (_processId != 0) _Append(\"processId\", _processId); else if (_threadId != 0) _Append(\"threadId\", _threadId);\n\t\t\tif (_also != null) _Append(\"also\", \"\");\n\t\t\t_Append(\"contains\", _contains.Value);\n\t\t\treturn b.ToString();\n\n\t\t\tvoid _Append(string k, object v) {\n\t\t\t\tif (v == null) return;\n\t\t\t\tif (b.Length != 0) b.Append(\", \");\n\t\t\t\tb.Append(k).Append('=').Append(v);\n\t\t\t}\n\t\t}\n\t}\n#pragma warning restore CS1591\n\n\t/// <inheritdoc cref=\"wnd.find\" path=\"//param|//exception\"/>\n\tpublic wndFinder(\n\t\t[ParamString(PSFormat.Wildex)] string name = null,\n\t\t[ParamString(PSFormat.Wildex)] string cn = null,\n\t\t[ParamString(PSFormat.Wildex)] WOwner of = default,\n\t\tWFlags flags = 0, Func<wnd, bool> also = null, WContains contains = default) {\n\t\t_name = name;\n\t\tif (cn != null) _cn = cn.Length != 0 ? cn : throw new ArgumentException(\"cn cannot be \\\"\\\". Use null.\");\n\t\tof.GetValue(out _program, out _processId, out _threadId, out _owner);\n\t\t_flags = flags;\n\t\t_also = also;\n\t\t_contains = contains;\n\t}\n\n\t//rejected. Better slightly longer code than unclear and possibly ambiguous code where need to learn string parsing rules.\n\t///// <summary>\n\t///// Implicit conversion from string that can contain window name, class name, program and/or a <i>contains</i> object.\n\t///// Examples: <c>\"name,cn,program\"</c>, <c>\"name\"</c>, <c>\",cn\"</c>, <c>\"*,,program\"</c>, <c>\"name,cn\"</c>, <c>\"name,,program\"</c>, <c>\",cn,program\"</c>, <c>\"name,,,object\"</c>.\n\t///// </summary>\n\t///// <param name=\"s\">\n\t///// One or more comma-separated window properties: name, class, program and/or a <i>contains</i> object. Empty name means <c>\"\"</c> (for any use *); other empty parts mean <c>null</c>.\n\t///// The same as parameters of <see cref=\"wnd.find\"/>. The first 3 parts are <i>name</i>, <i>cn</i> and <i>of</i>. The last part is <i>contains</i> as string; can specify a UI element, control or image.\n\t///// The first 3 comma-separated parts cannot contain commas. Alternatively, parts can be separated by <c>'\\0'</c> characters, like <c>\"name\\0\"+\"cn\\0\"+\"program\\0\"+\"object\"</c>. Then parts can contain commas. Example: <c>\"*one, two, three*\\0\"</c> (name with commas).\n\t///// </param>\n\t///// <exception cref=\"ArgumentException\">See <see cref=\"wnd.find\"/>.</exception>\n\t///// <exception cref=\"Exception\">If specifies a <i>contains</i> object: exceptions of constructor of <see cref=\"wndChildFinder\"/> or <see cref=\"elmFinder\"/> or <see cref=\"uiimageFinder\"/>.</exception>\n\t//public static implicit operator wndFinder(string s) {\n\t//\tstring name = null, cn = null, prog = null, contains = null;\n\t//\tchar[] sep = null; if (s.Contains('\\0')) sep = s_sepZero; else if (s.Contains(',')) sep = s_sepComma;\n\t//\tif (sep == null) name = s;\n\t//\telse {\n\t//\t\tvar ra = s.Split(sep, 4);\n\t//\t\tname = ra[0];\n\t//\t\tif (ra[1].Length > 0) cn = ra[1];\n\t//\t\tif (ra.Length > 2 && ra[2].Length > 0) prog = ra[2];\n\t//\t\tif (ra.Length > 3 && ra[3].Length > 0) contains = ra[3];\n\t//\t}\n\t//\treturn new wndFinder(name, cn, prog, contains: contains);\n\t//}\n\t//static readonly char[] s_sepComma = { ',' }, s_sepZero = { '\\0' };\n\n\t/// <summary>\n\t/// The found window.\n\t/// </summary>\n\tpublic wnd Result { get; internal set; }\n\n\t/// <summary>\n\t/// Finds the specified window, like <see cref=\"wnd.find\"/>.\n\t/// </summary>\n\t/// <returns>If found, returns <see cref=\"Result\"/>, else <c>default(wnd)</c>.</returns>\n\t/// <remarks>\n\t/// Functions <c>Find</c> and <see cref=\"Exists\"/> differ only in their return types.\n\t/// </remarks>\n\tpublic wnd Find() => Exists() ? Result : default;\n\n\t/// <summary>\n\t/// Finds the specified window, like <see cref=\"wnd.find\"/>. Can wait and throw <see cref=\"NotFoundException\"/>.\n\t/// </summary>\n\t/// <returns>If found, returns <see cref=\"Result\"/>. Else throws exception or returns <c>default(wnd)</c> (if <i>wait</i> negative).</returns>\n\t/// <param name=\"wait\">The wait timeout, seconds. If 0, does not wait. If negative, does not throw exception when not found.</param>\n\t/// <exception cref=\"NotFoundException\" />\n\t/// <remarks>\n\t/// Functions <c>Find</c> and <see cref=\"Exists\"/> differ only in their return types.\n\t/// </remarks>\n\tpublic wnd Find(Seconds wait) => Exists(wait) ? Result : default;\n\n\t/// <returns>If found, sets <see cref=\"Result\"/> and returns <c>true</c>, else <c>false</c>.</returns>\n\t/// <inheritdoc cref=\"Find()\"/>\n\tpublic bool Exists() {\n\t\tusing var k = new WndList_(_AllWindows());\n\t\treturn _FindOrMatch(k) >= 0;\n\t}\n\n\t/// <returns>If found, sets <see cref=\"Result\"/> and returns <c>true</c>. Else throws exception or returns <c>false</c> (if <i>wait</i> negative).</returns>\n\t/// <inheritdoc cref=\"Find(Seconds)\"/>\n\tpublic bool Exists(Seconds wait) {\n\t\tvar r = wait.Exists_() ? Exists() : !Wait(wait, false).Is0;\n\t\treturn r || wait.ReturnFalseOrThrowNotFound_();\n\t}\n\n\t/// <summary>\n\t/// Waits until window exists or is active.\n\t/// </summary>\n\t/// <returns>Returns <see cref=\"Result\"/>. On timeout returns <c>default(wnd)</c> if <i>timeout</i> is negative; else exception.</returns>\n\t/// <param name=\"timeout\">Timeout, seconds. Can be 0 (infinite), >0 (exception) or &lt;0 (no exception). More info: [](xref:wait_timeout).</param>\n\t/// <param name=\"active\">The window must be the active window (<see cref=\"wnd.active\"/>), and not minimized.</param>\n\t/// <exception cref=\"TimeoutException\"><i>timeout</i> time has expired (if > 0).</exception>\n\t/// <remarks>\n\t/// Same as <see cref=\"Find(Seconds)\"/>, except:\n\t/// - 0 timeout means infinite.\n\t/// - on timeout throws <see cref=\"TimeoutException\"/>, not <see cref=\"NotFoundException\"/>.\n\t/// - has parameter <i>active</i>.\n\t/// </remarks>\n\tpublic wnd Wait(Seconds timeout, bool active) {\n\t\tvar loop = new WaitLoop(timeout);\n\t\tfor (; ; ) {\n\t\t\tif (active) {\n\t\t\t\twnd w = wnd.active;\n\t\t\t\tif (IsMatch(w) && !w.IsMinimized) return Result = w;\n\t\t\t\t//CONSIDER: also wait until released mouse buttons.\n\t\t\t} else {\n\t\t\t\tif (Exists()) return Result;\n\t\t\t}\n\t\t\tif (!loop.Sleep()) return default;\n\t\t}\n\t}\n\n\tArrayBuilder_<wnd> _AllWindows() {\n\t\t//FUTURE: optimization: if cn not wildcard etc, at first find atom.\n\t\t//\tIf not found, don't search. If found, compare atom, not class name string.\n\n\t\tvar f = _threadId != 0 ? EnumAPI.EnumThreadWindows : EnumAPI.EnumWindows;\n\t\treturn EnumWindows2(f, 0 == (_flags & WFlags.HiddenToo), true, wParent: _owner, threadId: _threadId);\n\t}\n\n\t/// <summary>\n\t/// Finds the specified window in a list of windows, and sets <see cref=\"Result\"/>.\n\t/// </summary>\n\t/// <returns>Returns 0-based index, or -1 if not found.</returns>\n\t/// <param name=\"a\">Array or list of windows, for example returned by <see cref=\"wnd.getwnd.allWindows\"/>.</param>\n\tpublic int FindInList(IEnumerable<wnd> a) {\n\t\tusing var k = new WndList_(a);\n\t\treturn _FindOrMatch(k);\n\t}\n\n\t/// <summary>\n\t/// Finds all matching windows, like <see cref=\"wnd.findAll\"/>.\n\t/// </summary>\n\t/// <returns>Array containing 0 or more window handles as <see cref=\"wnd\"/>.</returns>\n\tpublic wnd[] FindAll() {\n\t\treturn _FindAll(new WndList_(_AllWindows()));\n\t}\n\n\t/// <summary>\n\t/// Finds all matching windows in a list of windows.\n\t/// </summary>\n\t/// <returns>Array containing 0 or more window handles as <see cref=\"wnd\"/>.</returns>\n\t/// <param name=\"a\">Array or list of windows, for example returned by <see cref=\"wnd.getwnd.allWindows\"/>.</param>\n\tpublic wnd[] FindAllInList(IEnumerable<wnd> a) {\n\t\treturn _FindAll(new WndList_(a));\n\t}\n\n\twnd[] _FindAll(WndList_ k) {\n\t\tusing (k) {\n\t\t\tusing var ab = new ArrayBuilder_<wnd>();\n\t\t\t_FindOrMatch(k, w => ab.Add(w)); //CONSIDER: ab could be part of WndList_. Now the delegate creates garbage.\n\t\t\treturn ab.ToArray();\n\t\t}\n\t}\n\n\t/// <summary>\n\t/// Returns index of matching element or -1.\n\t/// Returns -1 if using <i>getAll</i>.\n\t/// </summary>\n\t/// <param name=\"a\">List of <see cref=\"wnd\"/>. Does not dispose it.</param>\n\t/// <param name=\"getAll\">If not <c>null</c>, calls it for all matching and returns -1.</param>\n\t/// <param name=\"cache\"></param>\n\tint _FindOrMatch(WndList_ a, Action<wnd> getAll = null, WFCache cache = null) {\n\t\tResult = default;\n\t\t_stopProp = 0;\n\t\tif (a.Type == WndList_.ListType.None) return -1;\n\t\tbool inList = a.Type != WndList_.ListType.ArrayBuilder;\n\t\tbool ignoreVisibility = cache?.IgnoreVisibility ?? false;\n\t\tbool mustBeVisible = inList && (_flags & WFlags.HiddenToo) == 0 && !ignoreVisibility;\n\t\tbool isOwner = inList && !_owner.Is0;\n\t\tint ownerTid = 0;\n\t\tbool isTid = inList ? _threadId != 0 : false;\n\t\tList<int> pids = null; bool programNamePlanB = false; //variables for faster getting/matching program name\n\n\t\tint index = 0;\n\t\tfor (; a.Next(out wnd w); index++) {\n\t\t\tif (w.Is0) continue;\n\n\t\t\t//Speed of getting properties of 1000 windows with hot CPU:\n\t\t\t//name 1000, class 1000\n\t\t\t//foreign tid/pid 900/1800,\n\t\t\t//visible 30, cloaked 1100,\n\t\t\t//owner 40, style 40, exstyle 40,\n\t\t\t//isOwned(2) 2000, isOwned(2, ref tid) 1000,\n\t\t\t//rect 500,\n\t\t\t//GetProp(randomString) 1700, GetProp(existingString) 3000, GetProp(atom) 1000, GlobalFindAtom 1700,\n\t\t\t//program 6000\n\n\t\t\tif (mustBeVisible) {\n\t\t\t\tif (!w.IsVisible) { _stopProp = EProps.visible; continue; }\n\t\t\t}\n\n\t\t\tif (isOwner) {\n\t\t\t\tif (!w.IsOwnedBy2_(_owner, 2, ref ownerTid)) { _stopProp = EProps.of; continue; }\n\t\t\t}\n\n\t\t\tcache?.Begin(w);\n\n\t\t\tif (_name != null) {\n\t\t\t\tvar s = cache != null && cache.CacheName ? (cache.Name ??= w.NameTL_) : w.NameTL_;\n\t\t\t\tif (!_name.Match(s)) { _stopProp = EProps.name; continue; }\n\t\t\t\t//note: name is before classname. It makes faster in slowest cases (HiddenToo), because most windows are nameless.\n\t\t\t}\n\n\t\t\tif (_cn != null) {\n\t\t\t\tvar s = cache != null ? (cache.Class ?? (cache.Class = w.ClassName)) : w.ClassName;\n\t\t\t\tif (!_cn.Match(s)) { _stopProp = EProps.cn; continue; }\n\t\t\t}\n\n\t\t\tif (0 == (_flags & WFlags.CloakedToo) && !ignoreVisibility) {\n\t\t\t\tif (w.IsCloaked) { _stopProp = EProps.cloaked; continue; }\n\t\t\t}\n\n\t\t\tint pid = 0, tid = 0;\n\t\t\tif (_program != null || _processId != 0 || isTid) {\n\t\t\t\tif (cache != null) {\n\t\t\t\t\tif (cache.Tid == 0) cache.Tid = w.GetThreadProcessId(out cache.Pid);\n\t\t\t\t\ttid = cache.Tid; pid = cache.Pid;\n\t\t\t\t} else tid = w.GetThreadProcessId(out pid);\n\t\t\t\tif (tid == 0) { _stopProp = EProps.of; continue; }\n\t\t\t\t//speed: with foreign processes the same speed as getting name or class name. Much faster if same process.\n\t\t\t}\n\n\t\t\tif (isTid) {\n\t\t\t\tif (_threadId != tid) { _stopProp = EProps.of; continue; }\n\t\t\t}\n\n\t\t\tif (_processId != 0) {\n\t\t\t\tif (_processId != pid) { _stopProp = EProps.of; continue; }\n\t\t\t}\n\n\t\t\tif (_program != null) {\n\t\t\t\t//Getting program name is one of slowest parts.\n\t\t\t\t//Usually it does not slow down much because need to do it only 1 or several times, only when window name, class etc match.\n\t\t\t\t//The worst case is when only program is specified, and the very worst case is when also using flag HiddenToo.\n\t\t\t\t//We are prepared for the worst case.\n\t\t\t\t//Normally we call process.getName. In most cases it is quite fast.\n\t\t\t\t//Anyway, we use this optimization:\n\t\t\t\t//\tAdd pid of processes that don't match the specified name in the pids list (bad pids).\n\t\t\t\t//\tNext time, if pid is in the bad pids list, just continue, don't need to get program name again.\n\t\t\t\t//However in the worst case we would encounter some processes that process.getName cannot get name using the fast API.\n\t\t\t\t//For each such process it would then use the much slower 'get all processes' API, which is almost as slow as Process.GetProcessById(pid).ProgramName.\n\t\t\t\t//To solve this:\n\t\t\t\t//We tell process.getName to not use the slow API, but just return null when the fast API fails.\n\t\t\t\t//When it happens (process.getName returns null):\n\t\t\t\t//\tIf need full path: continue, we cannot do anything more.\n\t\t\t\t//\tSwitch to plan B and no longer use all the above. Plan B:\n\t\t\t\t//\tGet list of pids of all processes that match _program. For it we call process.GetProcessesByName_, which uses the same slow API, but we call it just one time.\n\t\t\t\t//\tIf it returns null (it means there are no matching processes), break (window not found).\n\t\t\t\t//\tFrom now, in each loop will need just to find pid in the returned list, and continue if not found.\n\n\t\t\t\t_stopProp = EProps.of;\n\t\t\t\tg1:\n\t\t\t\tif (programNamePlanB) {\n\t\t\t\t\tif (!pids.Contains(pid)) continue;\n\t\t\t\t} else {\n\t\t\t\t\tif (pids != null && pids.Contains(pid)) continue; //is known bad pid?\n\n\t\t\t\t\tstring pname = cache != null ? (cache.Program ?? (cache.Program = _Program())) : _Program();\n\t\t\t\t\tstring _Program() => process.getName(pid, false, true);\n\t\t\t\t\t//string _Program() => process.getName(pid, 0!=(_flags&WFlags.ProgramPath), true);\n\n\t\t\t\t\tif (pname == null) {\n\t\t\t\t\t\t//if(0!=(_flags&WFlags.ProgramPath)) continue;\n\n\t\t\t\t\t\t//switch to plan B\n\t\t\t\t\t\tprocess.GetProcessesByName_(ref pids, _program);\n\t\t\t\t\t\tif (pids.NE_()) break;\n\t\t\t\t\t\tprogramNamePlanB = true;\n\t\t\t\t\t\tgoto g1;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!_program.Match(pname)) {\n\t\t\t\t\t\tif (a.Type == WndList_.ListType.SingleWnd) break;\n\t\t\t\t\t\tpids ??= new List<int>(16);\n\t\t\t\t\t\tpids.Add(pid); //add bad pid\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t_stopProp = 0;\n\t\t\t}\n\n\t\t\tif (_also != null) {\n\t\t\t\tbool ok = false;\n\t\t\t\ttry { ok = _also(w); }\n\t\t\t\tcatch (AuWndException) { } //don't throw if w destroyed\n\t\t\t\tif (!ok) { _stopProp = EProps.also; continue; }\n\t\t\t}\n\n\t\t\tif (_contains.Value != null) {\n\t\t\t\tbool found = false;\n\t\t\t\ttry {\n\t\t\t\t\tswitch (_contains.Value) {\n\t\t\t\t\tcase elmFinder f: found = w.HasElm(f); break;\n\t\t\t\t\tcase wndChildFinder f: found = f.Exists(w); break;\n\t\t\t\t\tcase uiimageFinder f: found = f.Exists(w); break;\n\t\t\t\t\tcase ocrFinder f: found = f.Exists(w); break;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (Exception ex) {\n\t\t\t\t\tif (!(ex is AuWndException)) print.warning(\"Exception when tried to find the 'contains' object. \" + ex, -1);\n\t\t\t\t}\n\t\t\t\tif (!found) { _stopProp = EProps.contains; continue; }\n\t\t\t}\n\n\t\t\tif (getAll != null) {\n\t\t\t\tgetAll(w);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tResult = w;\n\t\t\treturn index;\n\t\t}\n\n\t\tif (index == 0 && !inList) if (!_owner.Is0 || _threadId != 0) _stopProp = EProps.of;\n\n\t\treturn -1;\n\t}\n\n\t/// <summary>\n\t/// Returns <c>true</c> if window <i>w</i> properties match the specified properties.\n\t/// </summary>\n\t/// <param name=\"w\">A top-level window. If 0 or invalid, returns <c>false</c>.</param>\n\t/// <param name=\"cache\">Can be used to make faster when multiple <see cref=\"wndFinder\"/> variables are used with same window. The function gets window name/class/program once, and stores in <i>cache</i>; next time it gets these strings from <i>cache</i>.</param>\n\t/// <seealso cref=\"wnd.IsMatch\"/>\n\tpublic bool IsMatch(wnd w, WFCache cache = null) {\n\t\treturn 0 == _FindOrMatch(new WndList_(w), cache: cache);\n\t}\n}\n"
  },
  {
    "path": "Au/wnd/wnd_child.cs",
    "content": "namespace Au {\n\tpublic unsafe partial struct wnd {\n\t\t/// <summary>\n\t\t/// Finds a child control and returns its handle as <see cref=\"wnd\"/>.\n\t\t/// </summary>\n\t\t/// <returns>Returns <c>default(wnd)</c> if not found. See also: <see cref=\"Is0\"/>.</returns>\n\t\t/// <param name=\"name\">\n\t\t/// Control name.\n\t\t/// String format: [wildcard expression](xref:wildcard_expression).\n\t\t/// <c>null</c> means \"can be any\". <c>\"\"</c> means \"no name\".\n\t\t/// \n\t\t/// By default to get control names this function uses <see cref=\"Name\"/>.\n\t\t/// Can start with these prefix strings:\n\t\t/// <br/>• <c>\"***text \"</c> - use <see cref=\"ControlText\"/>. Slower and less reliable because can get editable text. If a character can be underlined with <c>Alt</c>, insert <c>'&amp;'</c> before it.\n\t\t/// <br/>• <c>\"***elmName \"</c> - use <see cref=\"NameElm\"/>. Slower.\n\t\t/// <br/>• <c>\"***wfName \"</c> - use .NET Forms control name (see <see cref=\"WinformsControlNames\"/>). Slower and can fail because of [](xref:uac).\n\t\t/// </param>\n\t\t/// <param name=\"cn\">\n\t\t/// Control class name.\n\t\t/// String format: [wildcard expression](xref:wildcard_expression).\n\t\t/// <c>null</c> means \"can be any\". Cannot be <c>\"\"</c>.\n\t\t/// </param>\n\t\t/// <param name=\"flags\"></param>\n\t\t/// <param name=\"id\">Control id. See <see cref=\"ControlId\"/>. Not used if <c>null</c> (default).</param>\n\t\t/// <param name=\"also\">\n\t\t/// Callback function. Called for each matching control.\n\t\t/// It can evaluate more properties of the control and return <c>true</c> when they match.\n\t\t/// Example: <c>also: t => t.IsEnabled</c>\n\t\t/// </param>\n\t\t/// <param name=\"skip\">\n\t\t/// 0-based index of matching control.\n\t\t/// For example, if 1, the function skips the first matching control and returns the second.\n\t\t/// </param>\n\t\t/// <exception cref=\"AuWndException\">This variable is invalid (window not found, closed, etc).</exception>\n\t\t/// <exception cref=\"ArgumentException\">\n\t\t/// - <i>name</i> starts with <c>\"***\"</c>, but the prefix is invalid.\n\t\t/// - <i>cn</i> is <c>\"\"</c>. To match any, use <c>null</c>.\n\t\t/// - Invalid wildcard expression (<c>\"**options \"</c> or regular expression).\n\t\t/// </exception>\n\t\t/// <remarks>\n\t\t/// To create code for this function, use tool <b>Find window</b>.\n\t\t/// </remarks>\n\t\tpublic wnd Child(\n\t\t\t[ParamString(PSFormat.Wildex)] string name = null,\n\t\t\t[ParamString(PSFormat.Wildex)] string cn = null,\n\t\t\tWCFlags flags = 0, int? id = null, Func<wnd, bool> also = null, int skip = 0\n\t\t\t) => new wndChildFinder(name, cn, flags, id, also, skip).Find(this);\n\n\t\t/// <summary>\n\t\t/// Finds a child control and returns its handle as <see cref=\"wnd\"/>. Can wait and throw <see cref=\"NotFoundException\"/>.\n\t\t/// </summary>\n\t\t/// <returns>Child control handle. If not found, throws exception or returns <c>default(wnd)</c> (if <i>wait</i> negative).</returns>\n\t\t/// <param name=\"wait\">The wait timeout, seconds. If 0, does not wait. If negative, does not throw exception when not found.</param>\n\t\t/// <exception cref=\"AuWndException\">This variable is invalid (window not found, closed, etc). Or closed while waiting.</exception>\n\t\t/// <exception cref=\"NotFoundException\" />\n\t\t/// <inheritdoc cref=\"Child(string, string, WCFlags, int?, Func{wnd, bool}, int)\"/>\n\t\tpublic wnd Child(\n\t\t\tSeconds wait,\n\t\t\t[ParamString(PSFormat.Wildex)] string name = null,\n\t\t\t[ParamString(PSFormat.Wildex)] string cn = null,\n\t\t\tWCFlags flags = 0, int? id = null, Func<wnd, bool> also = null, int skip = 0\n\t\t\t) => new wndChildFinder(name, cn, flags, id, also, skip).Find(this, wait);\n\n\t\t/// <summary>\n\t\t/// Finds all matching child controls.\n\t\t/// </summary>\n\t\t/// <returns>Array containing zero or more <see cref=\"wnd\"/>.</returns>\n\t\t/// <remarks>\n\t\t/// Everything except the return type is the same as with <see cref=\"Child\"/>.\n\t\t/// \n\t\t/// In the returned array, hidden controls (when using <see cref=\"WCFlags.HiddenToo\"/>) are always after visible controls.\n\t\t/// </remarks>\n\t\t/// <seealso cref=\"getwnd.Children\"/>\n\t\t/// <inheritdoc cref=\"Child(string, string, WCFlags, int?, Func{wnd, bool}, int)\"/>\n\t\tpublic wnd[] ChildAll(\n\t\t\t[ParamString(PSFormat.Wildex)] string name = null,\n\t\t\t[ParamString(PSFormat.Wildex)] string cn = null,\n\t\t\tWCFlags flags = 0, int? id = null, Func<wnd, bool> also = null) {\n\t\t\t//ThrowIfInvalid(); //will be called later\n\t\t\tvar f = new wndChildFinder(name, cn, flags, id, also);\n\t\t\treturn f.FindAll(this);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if this window contains the specified control.\n\t\t/// Calls <see cref=\"wndChildFinder.Exists\"/>.\n\t\t/// </summary>\n\t\t/// <exception cref=\"AuWndException\"/>\n\t\t/// <example>\n\t\t/// Find window that contains certain control, and get the control too.\n\t\t/// <code><![CDATA[\n\t\t/// var cf = new wndChildFinder(\"Password*\", \"Static\");\n\t\t/// wnd w = wnd.find(cn: \"#32770\", also: t => t.HasChild(cf));\n\t\t/// print.it(w);\n\t\t/// print.it(cf.Result);\n\t\t/// ]]></code>\n\t\t/// The same with parameter <i>contains</i>.\n\t\t/// <code><![CDATA[\n\t\t/// var cf = new wndChildFinder(\"Password*\", \"Static\");\n\t\t/// wnd w = wnd.find(cn: \"#32770\", contains: cf);\n\t\t/// print.it(w);\n\t\t/// print.it(cf.Result);\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic bool HasChild(wndChildFinder f) => f.Exists(this);\n\n\t\t//rejected. Rare. Can use w.HasChild(new(\"Apply\", \"Button\")) or !w.Child(...).Is0 etc. Or would also need HasElm(string...).\n\t\t///// <summary>\n\t\t///// Returns <c>true</c> if this window contains the specified control.\n\t\t///// Calls <see cref=\"Child\"/>.\n\t\t///// <para>NOTE: Calling this function many times with same arguments is inefficient. Instead create new <see cref=\"wndChildFinder\"/> and call <see cref=\"wndChildFinder.Exists\"/> or <see cref=\"HasChild(wndChildFinder)\"/>. See example.</para>\n\t\t///// </summary>\n\t\t///// <remarks></remarks>\n\t\t///// <example></example>\n\t\t///// <inheritdoc cref=\"Child(string, string, WCFlags, int?, Func{wnd, bool}, int)\"/>\n\t\t//public bool HasChild(\n\t\t//\t[ParamString(PSFormat.Wildex)] string name = null,\n\t\t//\t[ParamString(PSFormat.Wildex)] string cn = null,\n\t\t//\tWCFlags flags = 0, int? id = null, Func<wnd, bool> also = null, int skip = 0) {\n\t\t//\treturn default != Child(name, cn, flags, id, also, skip);\n\t\t//}\n\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if this window contains the specified UI element.\n\t\t/// Calls <see cref=\"elmFinder.Exists\"/>.\n\t\t/// </summary>\n\t\t/// <exception cref=\"AuWndException\"/>\n\t\t/// <example>\n\t\t/// Find window that contains certain UI element, and get the UI element too.\n\t\t/// <code><![CDATA[\n\t\t/// var f = new elmFinder(\"BUTTON\", \"OK\");\n\t\t/// wnd w = wnd.find(cn: \"#32770\", also: t => t.HasElm(f));\n\t\t/// print.it(w);\n\t\t/// print.it(f.Result);\n\t\t/// ]]></code>\n\t\t/// The same with parameter <i>contains</i>.\n\t\t/// <code><![CDATA[\n\t\t/// var f = new elmFinder(\"BUTTON\", \"OK\");\n\t\t/// wnd w = wnd.find(cn: \"#32770\", contains: f);\n\t\t/// print.it(w);\n\t\t/// print.it(f.Result);\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic bool HasElm(elmFinder f) => f.Find_(false, this, null);\n\n\t\t//rejected. Use Child. Don't need 2 function for the same. Also the wait parameter is confusing. Also there is no finder.\n\t\t//\tThis is faster and less garbage, but it's not so important. Also there is ChildFast(id) for direct children.\n\t\t///// <summary>\n\t\t///// Finds a child control by its id and returns its handle as <see cref=\"wnd\"/>.\n\t\t///// </summary>\n\t\t///// <returns>Child control handle, or <c>default(wnd)</c> if not found. See also: <see cref=\"Is0\"/>.</returns>\n\t\t///// <param name=\"id\">Control id.</param>\n\t\t///// <param name=\"flags\">This function supports flags <c>DirectChild</c> and <c>HiddenToo</c>. If both are set, it is much faster because uses API <ms>GetDlgItem</ms>. Else uses API <ms>EnumChildWindows</ms>, like <see cref=\"Child\"/>.</param>\n\t\t///// <exception cref=\"AuWndException\">This variable is invalid (window not found, closed, etc).</exception>\n\t\t///// <remarks>\n\t\t///// To create code for this function, use tool <b>Find window</b>.\n\t\t///// \n\t\t///// Not all controls have a useful id. If control id is not unique or is different in each window instance, this function is not useful.\n\t\t///// </remarks>\n\t\t//public wnd ChildById(int id, WCFlags flags = 0) {\n\t\t//\tThrowIfInvalid();\n\t\t//\tif (flags.Has(WCFlags.DirectChild | WCFlags.HiddenToo)) return Api.GetDlgItem(this, id); //fast\n\n\t\t//\tvar d = new _KidEnumData() { wThis = this, id = id }; //info: to avoid garbage delegates, we use _KidEnumData instead of captured variables\n\t\t//\tvar wParent = this;\n\t\t//\tApi.EnumChildWindows(this, (c, p) => {\n\t\t//\t\tref var x = ref *(_KidEnumData*)p;\n\t\t//\t\tif (c.ControlId == x.id) {\n\t\t//\t\t\tif (x.flags.Has(WCFlags.DirectChild) && c.ParentGWL_ != x.wThis) return 1;\n\t\t//\t\t\tif (c.IsVisibleIn_(wParent)) { x.cVisible = c; return 0; }\n\t\t//\t\t\tif (x.flags.Has(WCFlags.HiddenToo) && x.cHidden.Is0) x.cHidden = c;\n\t\t//\t\t}\n\t\t//\t\treturn 1;\n\t\t//\t}, &d);\n\t\t//\treturn d.cVisible.Is0 ? d.cHidden : d.cVisible;\n\t\t//}\n\n\t\t///// <summary>\n\t\t///// Finds a child control by its id and returns its handle as <see cref=\"wnd\"/>. Can wait and throw <see cref=\"NotFoundException\"/>.\n\t\t///// </summary>\n\t\t///// <returns>Child control handle. If not found, throws exception or returns <c>default(wnd)</c> (if <i>wait</i> negative).</returns>\n\t\t///// <param name=\"wait\">The wait timeout, seconds. If 0, does not wait. If negative, does not throw exception when not found.</param>\n\t\t///// <param name=\"id\"></param>\n\t\t///// <param name=\"flags\"></param>\n\t\t///// <exception cref=\"AuWndException\">This variable is invalid (window not found, closed, etc). Or closed while waiting.</exception>\n\t\t///// <exception cref=\"NotFoundException\" />\n\t\t//public wnd ChildById(Seconds wait, int id, WCFlags flags = 0) {\n\t\t//\twnd r;\n\t\t//\tif (wait.Exists_()) {\n\t\t//\t\tr = ChildById(id, flags);\n\t\t//\t} else {\n\t\t//\t\tvar to = new WaitLoop(wait);\n\t\t//\t\tdo { r = ChildById(id, flags); if (!r.Is0) break; } while (to.Sleep());\n\t\t//\t}\n\t\t//\treturn !r.Is0 || wait.Time < 0 ? r : throw new NotFoundException();\n\t\t//}\n\n\t\t//struct _KidEnumData\n\t\t//{\n\t\t//\tpublic wnd wThis, cVisible, cHidden;\n\t\t//\tpublic int id;\n\t\t//\tpublic WCFlags flags;\n\t\t//}\n\n\t\t/// <summary>\n\t\t/// Finds a direct child control by name and/or class name and returns its handle as <see cref=\"wnd\"/>.\n\t\t/// </summary>\n\t\t/// <returns>Returns <c>default(wnd)</c> if not found. See also: <see cref=\"Is0\"/>. Supports <see cref=\"lastError\"/>.</returns>\n\t\t/// <param name=\"name\">\n\t\t/// Name.\n\t\t/// Full, case-insensitive. Wildcard etc not supported.\n\t\t/// <c>null</c> means \"can be any\". <c>\"\"</c> means \"no name\".\n\t\t/// Must include the invisible <c>'&amp;'</c> characters that are used to underline keyboard shortcuts with the <c>Alt</c> key.\n\t\t/// </param>\n\t\t/// <param name=\"cn\">\n\t\t/// Class name.\n\t\t/// Full, case-insensitive. Wildcard etc not supported.\n\t\t/// <c>null</c> means \"can be any\". Cannot be <c>\"\"</c>.\n\t\t/// </param>\n\t\t/// <param name=\"wAfter\">If used, starts searching from the next control in the Z order.</param>\n\t\t/// <remarks>\n\t\t/// Calls API <ms>FindWindowEx</ms>.\n\t\t/// Faster than <see cref=\"Child\"/>, which uses API <ms>EnumChildWindows</ms>.\n\t\t/// Can be used only when you know full name and/or class name.\n\t\t/// Finds hidden controls too. Finds only direct children, not other descendants.\n\t\t/// </remarks>\n\t\tpublic wnd ChildFast(string name, string cn, wnd wAfter = default) {\n\t\t\t//ThrowIfInvalid(); //no, it can be HWND_MESSAGE\n\t\t\tif (Is0) {\n\t\t\t\tApi.SetLastError(Api.ERROR_INVALID_WINDOW_HANDLE);\n\t\t\t\treturn default;\n\t\t\t}\n\t\t\treturn Api.FindWindowEx(this, wAfter, cn, name);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Finds a direct child control by its id and returns its handle as <see cref=\"wnd\"/>.\n\t\t/// </summary>\n\t\t/// <returns>Returns <c>default(wnd)</c> if not found. See also: <see cref=\"Is0\"/>. Supports <see cref=\"lastError\"/>.</returns>\n\t\t/// <param name=\"id\">Control id.</param>\n\t\t/// <remarks>\n\t\t/// Calls API <ms>GetDlgItem</ms>.\n\t\t/// Faster than <see cref=\"Child\"/>, which uses API <ms>EnumChildWindows</ms>.\n\t\t/// Finds only direct children, not other descendants. Finds hidden controls too.\n\t\t/// Not all controls have a useful id. If control id is not unique or is different in each window instance, this function is not useful.\n\t\t/// </remarks>\n\t\tpublic wnd ChildFast(int id) {\n\t\t\t//ThrowIfInvalid(); //no, let it be same as other overload\n\t\t\treturn Api.GetDlgItem(this, id);\n\t\t}\n\n\t\tpublic partial struct getwnd {\n\t\t\t/// <summary>\n\t\t\t/// Gets child controls, including all descendants.\n\t\t\t/// </summary>\n\t\t\t/// <returns>Array containing zero or more <see cref=\"wnd\"/>.</returns>\n\t\t\t/// <param name=\"onlyVisible\">Need only visible controls.</param>\n\t\t\t/// <param name=\"sortFirstVisible\">Place all array elements of hidden controls at the end of the array.</param>\n\t\t\t/// <param name=\"directChild\">Need only direct children, not all descendants.</param>\n\t\t\t/// <exception cref=\"AuWndException\">This variable is invalid (window not found, closed, etc).</exception>\n\t\t\t/// <remarks>\n\t\t\t/// Calls API <ms>EnumChildWindows</ms>.\n\t\t\t/// </remarks>\n\t\t\t/// <seealso cref=\"ChildAll\"/>\n\t\t\tpublic wnd[] Children(bool onlyVisible = false, bool sortFirstVisible = false, bool directChild = false) {\n\t\t\t\t_w.ThrowIfInvalid();\n\t\t\t\treturn Internal_.EnumWindows(Internal_.EnumAPI.EnumChildWindows, onlyVisible, sortFirstVisible, _w, directChild);\n\t\t\t}\n\n\t\t\t//rejected\n\t\t\t///// <summary>\n\t\t\t///// Gets child controls, including all descendants.\n\t\t\t///// </summary>\n\t\t\t///// <param name=\"a\">Receives results. If <c>null</c>, this function creates new <c>List</c>, else clears before adding items.</param>\n\t\t\t///// <param name=\"onlyVisible\">Need only visible controls.</param>\n\t\t\t///// <param name=\"sortFirstVisible\">Place all array elements of hidden controls at the end of the array.</param>\n\t\t\t///// <param name=\"directChild\">Need only direct children, not all descendants.</param>\n\t\t\t///// <exception cref=\"AuWndException\">This variable is invalid (window not found, closed, etc).</exception>\n\t\t\t///// <remarks>\n\t\t\t///// Use this overload to avoid much garbage when calling frequently with the same <c>List</c> variable. Other overload always allocates new array. This overload in most cases reuses memory allocated for the list variable.\n\t\t\t///// </remarks>\n\t\t\t//public void Children(ref List<wnd> a, bool onlyVisible = false, bool sortFirstVisible = false, bool directChild = false) {\n\t\t\t//\t_w.ThrowIfInvalid();\n\t\t\t//\tInternal_.EnumWindows2(Internal_.EnumAPI.EnumChildWindows, onlyVisible, sortFirstVisible, _w, directChild, list: a ??= new List<wnd>());\n\t\t\t//}\n\n\t\t\t//rejected: unreliable.\n\t\t\t///// <summary>\n\t\t\t///// Gets list of direct child controls.\n\t\t\t///// Faster than API <c>EnumChildWindows</c>.\n\t\t\t///// Should be used only with windows of current thread. Else it is unreliable because, if some controls are zordered or destroyed while enumerating, some controls can be skipped or retrieved more than once.\n\t\t\t///// </summary>\n\t\t\t//public wnd[] DirectChildrenFastUnsafe(string cn = null)\n\t\t\t//{\n\t\t\t//\twildex wild = cn;\n\t\t\t//\tvar a = new List<wnd>();\n\t\t\t//\tfor(wnd c = FirstChild; !c.Is0; c = c.Next) {\n\t\t\t//\t\tif(wild != null && !c._ClassNameIs(wild)) continue;\n\t\t\t//\t\ta.Add(c);\n\t\t\t//\t}\n\t\t\t//\treturn a.ToArray();\n\t\t\t//}\n\t\t}\n\n\t\t///// <summary>\n\t\t///// Casts this to <see cref=\"WButton\"/>.\n\t\t///// </summary>\n\t\t//public WButton AsButton => new(this);\n\n\t\t/// <summary>\n\t\t/// Finds and clicks a button in this window. Does not use the mouse.\n\t\t/// </summary>\n\t\t/// <param name=\"id\">Control id of the button. See <see cref=\"Child\"/>.</param>\n\t\t/// <exception cref=\"AuWndException\">This variable is invalid (window not found, closed, etc).</exception>\n\t\t/// <exception cref=\"NotFoundException\">Button not found.</exception>\n\t\t/// <remarks>\n\t\t/// This function just calls <see cref=\"Child\"/> and <see cref=\"mouse.postClick\"/>.\n\t\t/// Uses this code: <c>mouse.postClick(this.Child(1, id: id));</c>\n\t\t/// </remarks>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// wnd.find(\"Options\").ButtonClick(2);\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic void ButtonClick(int id) => mouse.postClick(this.Child(1, id: id));\n\n\t\t/// <summary>\n\t\t/// Finds and clicks a button in this window. Does not use the mouse.\n\t\t/// </summary>\n\t\t/// <param name=\"name\">Button name. String format: [wildcard expression](xref:wildcard_expression).</param>\n\t\t/// <param name=\"asControl\">\n\t\t/// If <c>true</c>, finds/clicks as child control: <c>mouse.postClick(this.Child(1, name, roleCN ?? \"*Button*\"));</c>.\n\t\t/// If <c>false</c>, finds/clicks as UI element: <c>this.Elm[roleCN ?? \"BUTTON\", name].Find(1).Invoke();</c>.\n\t\t/// Default is <c>false</c>; it's slower but works with more windows.\n\t\t/// </param>\n\t\t/// <param name=\"roleCN\">UI element role or control class name (if <i>asControl</i> <c>true</c>). String format: [wildcard expression](xref:wildcard_expression). Default role is <c>\"BUTTON\"</c>, class name <c>\"*Button*\"</c>.</param>\n\t\t/// <remarks>\n\t\t/// This function is just a shorter way to call other functions that have more options but require more code to call. If <i>asControl</i> <c>true</c>, it calls <see cref=\"Child\"/> and <see cref=\"mouse.postClick\"/>. Else <see cref=\"Elm\"/>, <see cref=\"elmFinder.this\"/>, <see cref=\"elmFinder.Find\"/> and <see cref=\"elm.Invoke\"/>.\n\t\t/// </remarks>\n\t\t/// <exception cref=\"ArgumentException\">Invalid <i>name</i> (when starts with <c>\"***\"</c>). See <see cref=\"elmFinder.this\"/>, <see cref=\"Child\"/>.</exception>\n\t\t/// <exception cref=\"AuWndException\">This variable is invalid (window not found, closed, etc).</exception>\n\t\t/// <exception cref=\"NotFoundException\">Button not found.</exception>\n\t\t/// <exception cref=\"AuException\">Failed to click. For example need to activate the window. No exception if <i>asControl</i> <c>true</c>.</exception>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// wnd.find(\"Options\").ButtonClick(\"Cancel\");\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic void ButtonClick([ParamString(PSFormat.Wildex)] string name, bool asControl = false, string roleCN = null) {\n\t\t\tif (asControl) {\n\t\t\t\tmouse.postClick(this.Child(1, name, roleCN ?? \"*Button*\"));\n\t\t\t} else {\n\t\t\t\tthis.Elm[roleCN ?? \"BUTTON\", name].Find(1).Invoke();\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Posts a \"menu item clicked\" notification (<ms>WM_COMMAND</ms>) as if that menu item has been clicked. Does not use the mouse.\n\t\t/// </summary>\n\t\t/// <param name=\"itemId\">Menu item id. Must be in range 1 to 0xffff.</param>\n\t\t/// <param name=\"systemMenu\">The menu item is in the title bar's context menu, not in the menu bar. Posts <ms>WM_SYSCOMMAND</ms> instead.</param>\n\t\t/// <exception cref=\"AuWndException\">Invalid window.</exception>\n\t\t/// <exception cref=\"ArgumentOutOfRangeException\">Invalid <i>itemId</i>.</exception>\n\t\t/// <remarks>\n\t\t/// Works only with classic menus. The drop-down menu window class name must be <c>\"#32768\"</c>. Works with menu items in window menu bar, system menu and some context menus.\n\t\t/// Does not use the menu itself. Just posts <c>WM_COMMAND</c> or <c>WM_SYSCOMMAND</c> message. Even if a menu item with this id does not exist.\n\t\t/// This variable is the window that contains the menu bar or system menu. Or the drop-down menu window (class <c>\"#32768\"</c>) that contains the menu item.\n\t\t/// </remarks>\n\t\tpublic void MenuClick(int itemId, bool systemMenu = false) {\n\t\t\tif ((uint)(itemId - 1) >= 0xffff) throw new ArgumentOutOfRangeException();\n\t\t\tThrowIfInvalid();\n\t\t\tvar w = this;\n\t\t\tif (ClassNameIs(\"#32768\") && miscInfo.getGUIThreadInfo(out var g, ThreadId) && !g.hwndMenuOwner.Is0) w = g.hwndMenuOwner;\n\t\t\tw.Post(systemMenu ? Api.WM_SYSCOMMAND : Api.WM_COMMAND, itemId);\n\t\t\tw.MinimalSleepIfOtherThread_();\n\t\t}\n\n\t\t//rejected: use elm functions instead.\n\t\t///// <summary>\n\t\t///// Finds a menu item by name and posts a \"menu item clicked\" notification as if that menu item was clicked. Does not use the mouse.\n\t\t///// Works with all standard menus and some non-standard menus.\n\t\t///// </summary>\n\t\t///// <param name=\"itemName\">\n\t\t///// Menu item name.\n\t\t///// String format: [wildcard expression](xref:wildcard_expression).\n\t\t///// </param>\n\t\t///// <param name=\"systemMenu\">The menu item is in the title bar's context menu, not in the menu bar.</param>\n\t\t//public void Click([ParamString(PSFormat.wildex)] string itemName, bool systemMenu = false)\n\t\t//{\n\t\t//\t\n\t\t//}\n\n\t\t//rejected: need just 1 function. To get state, use elm.\n\t\t///// <summary>\n\t\t///// Click standard (classic) menu items, get state.\n\t\t///// </summary>\n\t\t//public static class menu\n\t\t//{\n\n\t\t//}\n\t}\n}\n\nnamespace Au.Types {\n\t/// <summary>\n\t/// Flags for <see cref=\"wnd.Child\"/>.\n\t/// </summary>\n\t[Flags]\n\tpublic enum WCFlags {\n\t\t/// <summary>Can find hidden controls.</summary>\n\t\tHiddenToo = 1,\n\n\t\t/// <summary>Skip indirect descendant controls (children of children and so on).</summary>\n\t\tDirectChild = 2,\n\t}\n\n#if !true //rejected. Nobody would use this when there is elm. Eg BM_CLICK is the same as elm.Invoke or elm.PostClick. For Check can use code if(!e.IsChecked) e.Invoke();.\n\t/// <summary>\n\t/// Like <see cref=\"wnd\"/>, but has only button, check box and radio button functions - <c>Click</c>, <c>Check</c> etc.\n\t/// See also <see cref=\"wnd.AsButton\"/>.\n\t/// </summary>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// wnd.find(\"Options\").Child(\"Cancel\").AsButton.Click();\n\t/// ]]></code>\n\t/// </example>\n\tpublic struct WButton\n\t{\n\t\t/// <summary>\n\t\t/// Button handle as <see cref=\"wnd\"/>.\n\t\t/// </summary>\n\t\tpublic wnd W { get; }\n\n\t\tinternal WButton(wnd w) { W = w; }\n\n\t\t///\n\t\tpublic static implicit operator wnd(WButton b) => b.W;\n\n\t\t/////\n\t\t//public static explicit operator WButton(wnd w) => new(w);\n\n\t\t///\n\t\tpublic override string ToString() => W.ToString();\n\n\t\t/// <summary>\n\t\t/// Posts a \"click\" message to this button control. Does not use the mouse.\n\t\t/// </summary>\n\t\t/// <param name=\"useElm\">Use <see cref=\"elm.Invoke\"/>. If <c>false</c> (default), posts <ms>BM_CLICK</ms> message.</param>\n\t\t/// <exception cref=\"AuWndException\">This window is invalid.</exception>\n\t\t/// <exception cref=\"AuException\">Failed.</exception>\n\t\t/// <remarks>\n\t\t/// Works not with all button controls. Sometimes does not work if the window is inactive.\n\t\t/// Check boxes and radio buttons also are buttons. This function can click them.\n\t\t/// </remarks>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// wnd.find(\"Options\").Child(\"Cancel\").AsButton.Click();\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic void Click(bool useElm = false) {\n\t\t\tW.ThrowIfInvalid();\n\t\t\tif (useElm) {\n\t\t\t\tusing var e = elm.fromWindow(W, EObjid.CLIENT); //exception if failed\n\t\t\t\te.Invoke();\n\t\t\t} else {\n\t\t\t\t_PostBmClick(); //async if other thread, because may show a dialog.\n\t\t\t}\n\t\t\tW.MinimalSleepIfOtherThread_();\n\t\t\t//FUTURE: sync better\n\t\t}\n\n\t\tvoid _PostBmClick() {\n\t\t\tvar w = W.Window;\n\t\t\tbool workaround = !w.IsActive;\n\t\t\tif (workaround) w.Post(Api.WM_ACTIVATE, 1); //workaround for the documented BM_CLICK bug\n\t\t\tW.Post(BM_CLICK); //it sends WM_LBUTTONDOWN/UP\n\t\t\tif (workaround) w.Post(Api.WM_ACTIVATE, 0);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Checks or unchecks this check box. Does not use the mouse.\n\t\t/// Calls <see cref=\"SetCheckState\"/> with state 0 or 1.\n\t\t/// </summary>\n\t\t/// <param name=\"on\">Checks if <c>true</c>, unchecks if <c>false</c>.</param>\n\t\t/// <param name=\"useElm\"></param>\n\t\t/// <exception cref=\"AuWndException\">This window is invalid.</exception>\n\t\t/// <exception cref=\"AuException\">Failed.</exception>\n\t\t/// <remarks>\n\t\t/// Works not with all button controls. Sometimes does not work if the window is inactive.\n\t\t/// If this is a radio button, does not uncheck other radio buttons in its group.\n\t\t/// </remarks>\n\t\tpublic void Check(bool on, bool useElm = false) {\n\t\t\tSetCheckState(on ? 1 : 0, useElm);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Sets checkbox state. Does not use the mouse.\n\t\t/// </summary>\n\t\t/// <param name=\"state\">0 unchecked, 1 checked, 2 indeterminate.</param>\n\t\t/// <param name=\"useElm\">Use <see cref=\"elm.Invoke\"/>. If <c>false</c> (default), posts <ms>BM_SETCHECK</ms> message and also <c>BN_CLICKED</c> notification to the parent window; if that is not possible, instead uses <ms>BM_CLICK</ms> message.</param>\n\t\t/// <exception cref=\"ArgumentOutOfRangeException\">Invalid state.</exception>\n\t\t/// <exception cref=\"AuWndException\">This window is invalid.</exception>\n\t\t/// <exception cref=\"AuException\">Failed.</exception>\n\t\t/// <remarks>\n\t\t/// Does nothing if the check box already has the specified check state (if can get it).\n\t\t/// Works not with all button controls. Sometimes does not work if the window is inactive.\n\t\t/// If this is a radio button, does not uncheck other radio buttons in its group.\n\t\t/// </remarks>\n\t\tpublic void SetCheckState(int state, bool useElm = false) {\n\t\t\tif (state < 0 || state > 2) throw new ArgumentOutOfRangeException();\n\t\t\tW.ThrowIfInvalid();\n\t\t\tint id;\n\t\t\tif (useElm || !_IsCheckbox() || (uint)((id = W.ControlId) - 1) >= 0xffff) {\n\t\t\t\tusing var e = elm.fromWindow(W, EObjid.CLIENT); //exception if failed\n\t\t\t\tint k = _GetElmCheckState(e);\n\t\t\t\tif (k == state) return;\n\t\t\t\tif (useElm) e.Invoke(); else _PostBmClick();\n\t\t\t\tbool clickAgain = false;\n\t\t\t\tswitch (state) {\n\t\t\t\tcase 0:\n\t\t\t\t\tif (k == 1) {\n\t\t\t\t\t\tW.MinimalSleepIfOtherThread_();\n\t\t\t\t\t\tif (GetCheckState(true) == 2) clickAgain = true;\n\t\t\t\t\t\telse return;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase 1:\n\t\t\t\t\tif (k == 2) clickAgain = true;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 2:\n\t\t\t\t\tif (k == 0) clickAgain = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (clickAgain) {\n\t\t\t\t\tif (useElm) e.Invoke(); else _PostBmClick();\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (state == W.Send(BM_GETCHECK)) return;\n\t\t\t\tW.Post(BM_SETCHECK, state);\n\t\t\t\tW.Get.DirectParent.Post(Api.WM_COMMAND, id, (nint)W);\n\t\t\t}\n\t\t\tW.MinimalSleepIfOtherThread_();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets check state of this check box or radio button.\n\t\t/// Calls <see cref=\"GetCheckState\"/> and returns <c>true</c> if it returns 1.\n\t\t/// </summary>\n\t\tpublic bool IsChecked(bool useElm = false) {\n\t\t\treturn 1 == GetCheckState(useElm);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets check state of this check box or radio button.\n\t\t/// Returns 0 if unchecked, 1 if checked, 2 if indeterminate. Also returns 0 if this is not a button or if failed to get state.\n\t\t/// </summary>\n\t\t/// <param name=\"useElm\">Use <see cref=\"elm.State\"/>. If <c>false</c> (default) and this button has a standard checkbox style, uses API <ms>BM_GETCHECK</ms>.</param>\n\t\tpublic int GetCheckState(bool useElm = false) {\n\t\t\tif (useElm || !_IsCheckbox()) {\n\t\t\t\t//info: Windows Forms controls are user-drawn and don't have one of the styles, therefore BM_GETCHECK does not work.\n\t\t\t\ttry { //avoid exception in property-get functions\n\t\t\t\t\tusing var e = elm.fromWindow(W, EObjid.CLIENT, flags: EWFlags.NoThrow);\n\t\t\t\t\tif (e == null) return 0;\n\t\t\t\t\treturn _GetElmCheckState(e);\n\t\t\t\t}\n\t\t\t\tcatch (Exception ex) { Debug_.Print(ex); } //CONSIDER: if fails, show warning. In all wnd property-get functions.\n\t\t\t\treturn 0;\n\t\t\t} else {\n\t\t\t\treturn (int)W.Send(BM_GETCHECK);\n\t\t\t}\n\t\t}\n\n\t\tint _GetElmCheckState(elm e) {\n\t\t\tvar state = e.State;\n\t\t\tif (state.Has(EState.MIXED)) return 2;\n\t\t\tif (state.Has(EState.CHECKED)) return 1;\n\t\t\treturn 0;\n\t\t}\n\n\t\tbool _IsCheckbox() {\n\t\t\tswitch ((uint)W.Style & 15) {\n\t\t\tcase BS_CHECKBOX:\n\t\t\tcase BS_AUTOCHECKBOX:\n\t\t\tcase BS_RADIOBUTTON:\n\t\t\tcase BS_3STATE:\n\t\t\tcase BS_AUTO3STATE:\n\t\t\tcase BS_AUTORADIOBUTTON:\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tinternal const int BM_CLICK = 0xF5;\n\t\tinternal const int BM_GETCHECK = 0xF0;\n\t\tinternal const int BM_SETCHECK = 0xF1;\n\n\t\tinternal const uint BS_CHECKBOX = 0x2;\n\t\tinternal const uint BS_AUTOCHECKBOX = 0x3;\n\t\tinternal const uint BS_RADIOBUTTON = 0x4;\n\t\tinternal const uint BS_3STATE = 0x5;\n\t\tinternal const uint BS_AUTO3STATE = 0x6;\n\t\tinternal const uint BS_AUTORADIOBUTTON = 0x9;\n\n\t}\n#endif\n}\n"
  },
  {
    "path": "Au/wnd/wnd_find.cs",
    "content": "namespace Au {\n\tpublic unsafe partial struct wnd {\n\t\t/// <summary>\n\t\t/// Finds a top-level window and returns its handle as <see cref=\"wnd\"/>.\n\t\t/// </summary>\n\t\t/// <returns>Window handle, or <c>default(wnd)</c> if not found. See also: <see cref=\"Is0\"/>.</returns>\n\t\t/// <param name=\"name\">\n\t\t/// Window name. Usually it is the title bar text.\n\t\t/// String format: [wildcard expression](xref:wildcard_expression).\n\t\t/// <c>null</c> means \"can be any\". <c>\"\"</c> means \"no name\".\n\t\t/// </param>\n\t\t/// <param name=\"cn\">\n\t\t/// Window class name.\n\t\t/// String format: [wildcard expression](xref:wildcard_expression).\n\t\t/// <c>null</c> means \"can be any\". Cannot be <c>\"\"</c>.\n\t\t/// </param>\n\t\t/// <param name=\"of\">\n\t\t/// Owner window, program or thread. Depends on argument type:\n\t\t/// <br/>• <see cref=\"wnd\"/> - owner window. Will use <see cref=\"IsOwnedBy(wnd, int)\"/> with level 2.\n\t\t/// <br/>• <c>string</c> - program file name, like <c>\"notepad.exe\"</c>. String format: [wildcard expression](xref:wildcard_expression). Cannot be <c>\"\"</c> or path.\n\t\t/// <br/>• <see cref=\"WOwner\"/> - <see cref=\"WOwner.Process\"/>(process id), <see cref=\"WOwner.Thread\"/>(thread id).\n\t\t/// \n\t\t/// <para>\n\t\t/// See <see cref=\"getwnd.Owner\"/>, <see cref=\"ProcessId\"/>, <see cref=\"process.thisProcessId\"/>, <see cref=\"ThreadId\"/>, <see cref=\"process.thisThreadId\"/>.\n\t\t/// </para>\n\t\t/// </param>\n\t\t/// <param name=\"flags\"></param>\n\t\t/// <param name=\"also\">\n\t\t/// Callback function. Called for each matching window.\n\t\t/// It can evaluate more properties of the window and return <c>true</c> when they match.\n\t\t/// Example: <c>also: t => !t.IsPopupWindow</c>.\n\t\t/// Called after evaluating all other parameters except <i>contains</i>.\n\t\t/// </param>\n\t\t/// <param name=\"contains\">\n\t\t/// Defines an object that must be in the client area of the window:\n\t\t/// <br/>• UI element: <see cref=\"elmFinder\"/> or string like <c>\"name\"</c> or <c>\"e 'role' name\"</c> or <c>\"e 'role'\"</c>.\n\t\t/// <br/>• Child control: <see cref=\"wndChildFinder\"/> or string like <c>\"c 'cn' name\"</c> or <c>\"c '' name\"</c> or <c>\"c 'cn'\"</c>.\n\t\t/// <br/>• Image(s) or color(s): <see cref=\"uiimageFinder\"/> or string <c>\"image:...\"</c> (uses a <see cref=\"uiimageFinder\"/> with flag <see cref=\"IFFlags.WindowDC\"/>).\n\t\t/// <br/>• OCR text: <see cref=\"ocrFinder\"/> or string <c>\"ocr:...\"</c> (uses an <see cref=\"ocrFinder\"/> with flag <see cref=\"OcrFlags.WindowDC\"/>).\n\t\t/// </param>\n\t\t/// <exception cref=\"ArgumentException\">\n\t\t/// - <i>cn</i> is <c>\"\"</c>. To match any, use <c>null</c>.\n\t\t/// - <i>of</i> is <c>\"\"</c> or 0 or contains character <c>'\\\\'</c> or <c>'/'</c>. To match any, use <c>null</c>.\n\t\t/// - Invalid wildcard expression (<c>\"**options \"</c> or regular expression).\n\t\t/// </exception>\n\t\t/// <remarks>\n\t\t/// To create code for this function, use tool <b>Find window</b>.\n\t\t/// \n\t\t/// If there are multiple matching windows, gets the first in the Z order matching window, preferring visible windows.\n\t\t/// \n\t\t/// On Windows 8 and later may skip Windows Store app Metro-style windows (on Windows 10 few such windows exist). It happens if this program does not have <c>disableWindowFiltering = true</c> in its manifest and is not uiAccess; to find such windows you can use <see cref=\"findFast\"/>.\n\t\t/// \n\t\t/// To find message-only windows use <see cref=\"findFast\"/> instead.\n\t\t/// </remarks>\n\t\t/// <example>\n\t\t/// Try to find Notepad window. Return if not found.\n\t\t/// <code><![CDATA[\n\t\t/// wnd w = wnd.find(\"* Notepad\");\n\t\t/// if(w.Is0) { print.it(\"not found\"); return; }\n\t\t/// ]]></code>\n\t\t/// Try to find Notepad window. Throw <see cref=\"NotFoundException\"/> if not found.\n\t\t/// <code><![CDATA[\n\t\t/// wnd w1 = wnd.find(0, \"* Notepad\");\n\t\t/// ]]></code>\n\t\t/// Wait for Notepad window max 3 seconds. Throw <see cref=\"NotFoundException\"/> if not found during that time.\n\t\t/// <code><![CDATA[\n\t\t/// wnd w1 = wnd.find(3, \"* Notepad\");\n\t\t/// ]]></code>\n\t\t/// Wait for Notepad window max 3 seconds. Return if not found during that time.\n\t\t/// <code><![CDATA[\n\t\t/// wnd w1 = wnd.find(-3, \"* Notepad\");\n\t\t/// if(w.Is0) { print.it(\"not found\"); return; }\n\t\t/// ]]></code>\n\t\t/// Wait for Notepad window max 3 seconds. Throw <see cref=\"NotFoundException\"/> if not found during that time. When found, wait max 1 s until becomes active, then activate.\n\t\t/// <code><![CDATA[\n\t\t/// wnd w1 = wnd.find(3, \"* Notepad\").Activate(1);\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic static wnd find(\n\t\t\t[ParamString(PSFormat.Wildex)] string name = null,\n\t\t\t[ParamString(PSFormat.Wildex)] string cn = null,\n\t\t\t[ParamString(PSFormat.Wildex)] WOwner of = default,\n\t\t\tWFlags flags = 0, Func<wnd, bool> also = null, WContains contains = default\n\t\t\t) => new wndFinder(name, cn, of, flags, also, contains).Find();\n\t\t\n\t\t//rejected: single overload with last parameter double? wait.\n\t\t//\tThen in scripts almost always would need eg ' , wait: 1'. Or would need ', wait: 0' just for 'exception if not found'.\n\t\t\n\t\t/// <summary>\n\t\t/// Finds a top-level window and returns its handle as <see cref=\"wnd\"/>. Can wait and throw <see cref=\"NotFoundException\"/>.\n\t\t/// </summary>\n\t\t/// <returns>Window handle. If not found, throws exception or returns <c>default(wnd)</c> (if <i>wait</i> negative).</returns>\n\t\t/// <param name=\"wait\">The wait timeout, seconds. If 0, does not wait. If negative, does not throw exception when not found.</param>\n\t\t/// <exception cref=\"NotFoundException\" />\n\t\t/// <inheritdoc cref=\"find(string, string, WOwner, WFlags, Func{wnd, bool}, WContains)\" path=\"//param|//exception\"/>\n\t\tpublic static wnd find(\n\t\t\tSeconds wait,\n\t\t\t[ParamString(PSFormat.Wildex)] string name = null,\n\t\t\t[ParamString(PSFormat.Wildex)] string cn = null,\n\t\t\t[ParamString(PSFormat.Wildex)] WOwner of = default,\n\t\t\tWFlags flags = 0, Func<wnd, bool> also = null, WContains contains = default\n\t\t\t) => new wndFinder(name, cn, of, flags, also, contains).Find(wait);\n\t\t\n\t\t//rejected: probably most users will not understand/use it. It's easy and more clear to create and use wndFinder instances.\n\t\t///// <summary>\n\t\t///// Gets arguments and result of this thread's last call to <see cref=\"Find\"/> or <see cref=\"FindAll\"/>.\n\t\t///// </summary>\n\t\t///// <remarks>\n\t\t///// <c>wnd.wait</c> and similar functions don't change this property. <see cref=\"FindOrRun\"/> and some other functions of this library change this property because they call <see cref=\"Find\"/> internally.\n\t\t///// </remarks>\n\t\t///// <example>\n\t\t///// This example is similar to what <see cref=\"FindOrRun\"/> does.\n\t\t///// <code><![CDATA[\n\t\t///// wnd w = wnd.find(\"*- Notepad\", \"Notepad\");\n\t\t///// if(w.Is0) { run.it(\"notepad.exe\"); w = wnd.waitAny(60, true, wnd.LastFind).w; }\n\t\t///// ]]></code>\n\t\t///// </example>\n\t\t//[field: ThreadStatic]\n\t\t//public static wndFinder lastFind { get; set; }\n\t\t\n\t\t//CONSIDER: add property: [field: ThreadStatic] public static wnd last { get; set; }\n\t\t\n\t\t/// <summary>\n\t\t/// Finds all matching windows.\n\t\t/// </summary>\n\t\t/// <returns>Array containing zero or more <see cref=\"wnd\"/>.</returns>\n\t\t/// <remarks>\n\t\t/// The list is sorted to match the Z order, however hidden windows (when using <see cref=\"WFlags.HiddenToo\"/>) and IME windows are always after visible windows.\n\t\t/// </remarks>\n\t\t/// <seealso cref=\"getwnd.allWindows\"/>\n\t\t/// <seealso cref=\"getwnd.mainWindows\"/>\n\t\t/// <seealso cref=\"getwnd.threadWindows\"/>\n\t\t/// <inheritdoc cref=\"find(string, string, WOwner, WFlags, Func{wnd, bool}, WContains)\" path=\"//param|//exception\"/>\n\t\tpublic static wnd[] findAll(\n\t\t\t[ParamString(PSFormat.Wildex)] string name = null,\n\t\t\t[ParamString(PSFormat.Wildex)] string cn = null,\n\t\t\t[ParamString(PSFormat.Wildex)] WOwner of = default,\n\t\t\tWFlags flags = 0, Func<wnd, bool> also = null, WContains contains = default) {\n\t\t\tvar f = new wndFinder(name, cn, of, flags, also, contains);\n\t\t\tvar a = f.FindAll();\n\t\t\t//LastFind = f;\n\t\t\treturn a;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Finds a top-level window and returns its handle as <see cref=\"wnd\"/>.\n\t\t/// </summary>\n\t\t/// <returns>Returns <c>default(wnd)</c> if not found. See also: <see cref=\"Is0\"/>.</returns>\n\t\t/// <param name=\"name\">\n\t\t/// Name.\n\t\t/// Full, case-insensitive. Wildcard etc not supported.\n\t\t/// <c>null</c> means \"can be any\". <c>\"\"</c> means \"no name\".\n\t\t/// </param>\n\t\t/// <param name=\"cn\">\n\t\t/// Class name.\n\t\t/// Full, case-insensitive. Wildcard etc not supported.\n\t\t/// <c>null</c> means \"can be any\". Cannot be <c>\"\"</c>.\n\t\t/// </param>\n\t\t/// <param name=\"messageOnly\">Search only message-only windows.</param>\n\t\t/// <param name=\"wAfter\">If used, starts searching from the next window in the Z order.</param>\n\t\t/// <remarks>\n\t\t/// Calls API <ms>FindWindowEx</ms>.\n\t\t/// Faster than <see cref=\"find\"/>, which uses API <ms>EnumWindows</ms>.\n\t\t/// Finds hidden windows too.\n\t\t/// Supports <see cref=\"lastError\"/>.\n\t\t/// It is not recommended to use this function in a loop to enumerate windows. It would be unreliable because window positions in the Z order can be changed while enumerating. Also then it would be slower than <see cref=\"find\"/> and <see cref=\"findAll\"/>.\n\t\t/// </remarks>\n\t\tpublic static wnd findFast(string name = null, string cn = null, bool messageOnly = false, wnd wAfter = default) {\n\t\t\treturn Api.FindWindowEx(messageOnly ? SpecHWND.MESSAGE : default, wAfter, cn, name);\n\t\t}\n\t\t\n\t\tinternal struct Cached_ {\n\t\t\twnd _w;\n\t\t\tlong _time;\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// Calls/returns <see cref=\"findFast\"/> and stores found <see cref=\"wnd\"/> and time. Returns the cached <see cref=\"wnd\"/> if called frequently and it's still valid.\n\t\t\t/// </summary>\n\t\t\tpublic wnd FindFast(string name, string cn, bool messageOnly) {\n\t\t\t\tlong t = Environment.TickCount64;\n\t\t\t\tif (t - _time > 1000 || !_w.IsAlive) {\n\t\t\t\t\tlock (\"x5rX3BZJrE+pOTqszh4ttQ\") {\n\t\t\t\t\t\tif (t - _time > 1000 || !_w.IsAlive) {\n\t\t\t\t\t\t\t_w = findFast(name, cn, messageOnly);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t_time = t;\n\t\t\t\treturn _w;\n\t\t\t}\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// Calls/returns callback <i>f</i> and stores found <see cref=\"wnd\"/> and time. Returns the cached <see cref=\"wnd\"/> if called frequently and it's still valid.\n\t\t\t/// </summary>\n\t\t\tpublic wnd Get(Func<wnd> f) {\n\t\t\t\tlong t = Environment.TickCount64;\n\t\t\t\tif (t - _time > 1000 || !_w.IsAlive) {\n\t\t\t\t\tlock (\"x5rX3BZJrE+pOTqszh4ttQ\") {\n\t\t\t\t\t\tif (t - _time > 1000 || !_w.IsAlive) {\n\t\t\t\t\t\t\t_w = f();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t_time = t;\n\t\t\t\treturn _w;\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Finds a top-level window, like <see cref=\"find\"/>. If found, activates (optionally), else calls callback function and waits for the window. The callback should open the window, for example call <see cref=\"run.it\"/>.\n\t\t/// </summary>\n\t\t/// <returns>Window handle as <see cref=\"wnd\"/>. On timeout returns <c>default(wnd)</c> if <i>wait</i> &lt; 0 (else exception).</returns>\n\t\t/// <param name=\"run\">Callback function. See example.</param>\n\t\t/// <param name=\"wait\">How long to wait for the window after calling the callback function. Seconds. Default 60.</param>\n\t\t/// <param name=\"activate\">Activate the window. Default: <c>true</c>.</param>\n\t\t/// <exception cref=\"NotFoundException\"><i>wait</i> time has expired (if >= 0).</exception>\n\t\t/// <exception cref=\"AuWndException\">Failed to activate.</exception>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// wnd w = wnd.findOrRun(\"* Notepad\", run: () => run.it(\"notepad.exe\"));\n\t\t/// print.it(w);\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\t/// <inheritdoc cref=\"find(string, string, WOwner, WFlags, Func{wnd, bool}, WContains)\" path=\"//param|//exception\"/>\n\t\tpublic static wnd findOrRun(\n\t\t\t[ParamString(PSFormat.Wildex)] string name = null,\n\t\t\t[ParamString(PSFormat.Wildex)] string cn = null,\n\t\t\t[ParamString(PSFormat.Wildex)] WOwner of = default,\n\t\t\tWFlags flags = 0, Func<wnd, bool> also = null, WContains contains = default,\n\t\t\tAction run = null, Seconds? wait = default, bool activate = true) {\n\t\t\twnd w = default;\n\t\t\tvar f = new wndFinder(name, cn, of, flags, also, contains);\n\t\t\tif (f.Exists()) {\n\t\t\t\tw = f.Result;\n\t\t\t\tif (activate) w.Activate();\n\t\t\t} else {\n\t\t\t\trun();\n\t\t\t\tif (!f.Exists(wait ?? new(60))) return default;\n\t\t\t\tw = f.Result;\n\t\t\t\tif (activate) w._ActivateAfterRun();\n\t\t\t}\n\t\t\treturn w;\n\t\t}\n\t\t\n\t\t//What to do if activate true and the window started inactive? Activate immediately or wait, and how long?\n\t\t//\tPossible cases:\n\t\t//\t\tStarts inactive, but soon becomes active naturally.\n\t\t//\t\tOccasionally starts inactive and never would become active naturally, for example if the user clicked another window after starting the process and therefore OS disabled setforegroundwindow in the new process.\n\t\t//\t\tAlways starts inactive and never becomes active naturally.\n\t\t//\tThis code waits max 1 s. It's better if the window becomes active naturally (case 1), but need to activate in cases 2 and 3.\n\t\t//\tTested many windows. Most were active after 0-10 ms, few after 11-70 ms, PowerShell after 250 ms, dotPeek after 1000 ms.\n\t\t//\tSome windows start active but soon a dialog pops up.\n\t\t//\tAlso tested what happens if we activate the \"slow\" window without waiting.\n\t\t//\t\tSome are OK (eg PowerShell). Anyway, many windows render content after showing/activating.\n\t\t//\t\tBut some windows (eg dotPeek) then soon become inactive temporarily, because the app activates another window.\n\t\tvoid _ActivateAfterRun() {\n\t\t\tif (!IsActive && !WaitFor(-1, w => w.IsActive)) {\n\t\t\t\tActivate();\n\t\t\t}\n\t\t\t//note: exception if closed while waiting. As well as if fails to activate.\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Opens and finds new window. Ignores old windows. Activates.\n\t\t/// </summary>\n\t\t/// <returns>Window handle as <see cref=\"wnd\"/>. On timeout returns <c>default(wnd)</c> if <i>timeout</i> &lt; 0 (else exception).</returns>\n\t\t/// <param name=\"timeout\">How long to wait for the window. Seconds. Can be 0 (infinite), >0 (exception on timeout) or &lt;0 (no exception). More info: [](xref:wait_timeout).</param>\n\t\t/// <param name=\"run\">Callback function. Should open the window. See example.</param>\n\t\t/// <param name=\"activate\">Activate the window. Default: <c>true</c>.</param>\n\t\t/// <exception cref=\"TimeoutException\"><i>timeout</i> time has expired (if > 0).</exception>\n\t\t/// <exception cref=\"AuWndException\">Failed to activate.</exception>\n\t\t/// <remarks>\n\t\t/// This function isn't the same as just two statements <see cref=\"run.it\"/> and <see cref=\"wnd.find\"/>. It never returns a window that already existed before calling it.\n\t\t/// </remarks>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// var w = wnd.runAndFind(\n\t\t/// \t() => run.it(folders.Windows + @\"explorer.exe\"),\n\t\t/// \t10, cn: \"CabinetWClass\");\n\t\t/// print.it(w);\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\t/// <inheritdoc cref=\"find(string, string, WOwner, WFlags, Func{wnd, bool}, WContains)\" path=\"//param|//exception\"/>\n\t\tpublic static wnd runAndFind(Action run, Seconds timeout,\n\t\t\t[ParamString(PSFormat.Wildex)] string name = null,\n\t\t\t[ParamString(PSFormat.Wildex)] string cn = null,\n\t\t\t[ParamString(PSFormat.Wildex)] WOwner of = default,\n\t\t\tWFlags flags = 0, Func<wnd, bool> also = null, WContains contains = default,\n\t\t\tbool activate = true) {\n\t\t\tvar f = new wndFinder(name, cn);\n\t\t\tvar a = f.FindAll();\n\t\t\t\n\t\t\trun();\n\t\t\t\n\t\t\tvar loop = new WaitLoop(timeout);\n\t\t\twhile (loop.Sleep()) {\n\t\t\t\tvar w = f.Find();\n\t\t\t\tif (!w.Is0 && !a.Contains(w)) {\n\t\t\t\t\tif (activate) w._ActivateAfterRun();\n\t\t\t\t\treturn w;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn default;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Compares window name and other properties like <see cref=\"find\"/> does.\n\t\t/// </summary>\n\t\t/// <returns><c>true</c> if all specified (non-<c>null</c>/default) properties match.</returns>\n\t\t/// <remarks>\n\t\t/// Creates new <see cref=\"wndFinder\"/> and calls <see cref=\"wndFinder.IsMatch\"/>.\n\t\t/// To compare single parameter, use more lightweight code. Examples: <c>if (w.Name.Like(\"* Notepad\"))</c>, <c>if (w.ClassNameIs(\"CabinetWClass\"))</c>.\n\t\t/// </remarks>\n\t\t/// <seealso cref=\"Name\"/>\n\t\t/// <seealso cref=\"ClassName\"/>\n\t\t/// <seealso cref=\"ClassNameIs\"/>\n\t\t/// <seealso cref=\"ProgramName\"/>\n\t\t/// <inheritdoc cref=\"find(string, string, WOwner, WFlags, Func{wnd, bool}, WContains)\" path=\"//param|//exception\"/>\n\t\tpublic bool IsMatch(\n\t\t\t[ParamString(PSFormat.Wildex)] string name = null,\n\t\t\t[ParamString(PSFormat.Wildex)] string cn = null,\n\t\t\t[ParamString(PSFormat.Wildex)] WOwner of = default,\n\t\t\tWFlags flags = 0,\n\t\t\tFunc<wnd, bool> also = null,\n\t\t\tWContains contains = default\n\t\t\t) {\n\t\t\tvar f = new wndFinder(name, cn, of, flags, also, contains);\n\t\t\treturn f.IsMatch(this);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Compares window name and other properties like <see cref=\"find\"/> does. Can be specified multiple windows.\n\t\t/// </summary>\n\t\t/// <returns>1-based index of the match, or 0 if none.</returns>\n\t\t/// <example>\n\t\t/// <code><![CDATA[\n\t\t/// var w = wnd.active;\n\t\t/// int i = w.IsMatch([new(\"name1\", \"class1\"), new(\"name2\", of: \"program2\")]);\n\t\t/// print.it(i);\n\t\t/// ]]></code>\n\t\t/// Cache finders to improve performance when IsMatch is called multiple times. Creating all finders each time is expensive.\n\t\t/// <code><![CDATA[\n\t\t/// class C {\n\t\t/// \tstatic wndFinder[] s_wf1;\n\t\t/// \t\n\t\t/// \tvoid F(wnd w) {\n\t\t/// \t\tint i = w.IsMatch(s_wf1 ??= [new(\"name1\", \"class1\"), new(\"name2\", of: \"program2\")]);\n\t\t/// \t\tprint.it(i);\n\t\t/// \t}\n\t\t/// }\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic int IsMatch(ReadOnlySpan<wndFinder> windows) {\n\t\t\tWFCache cache = windows.Length > 1 ? new() { CacheName = true, NoTimeout = true } : null;\n\t\t\tfor (int i = 0; i < windows.Length; i++) {\n\t\t\t\tif (windows[i].IsMatch(this, cache)) return i + 1;\n\t\t\t}\n\t\t\treturn 0;\n\t\t}\n\t\t\n\t\tpublic partial struct getwnd {\n\t\t\t/// <summary>\n\t\t\t/// Gets top-level windows.\n\t\t\t/// </summary>\n\t\t\t/// <returns>Array containing zero or more <see cref=\"wnd\"/>.</returns>\n\t\t\t/// <param name=\"onlyVisible\">\n\t\t\t/// Need only visible windows.\n\t\t\t/// Note: this function does not check whether windows are cloaked, as it is rather slow. Use <see cref=\"IsCloaked\"/> if need.\n\t\t\t/// </param>\n\t\t\t/// <param name=\"sortFirstVisible\">\n\t\t\t/// Place hidden windows at the end of the array.\n\t\t\t/// Not used when <i>onlyVisible</i> is <c>true</c>.</param>\n\t\t\t/// <remarks>\n\t\t\t/// Calls API <ms>EnumWindows</ms>.\n\t\t\t/// Although undocumented, the API retrieves most windows as in the Z order, however places IME windows (hidden) at the end. See also: <see cref=\"allWindowsZorder\"/>;\n\t\t\t/// <note>The array can be bigger than you expect, because there are many invisible windows, tooltips, etc. See also <see cref=\"mainWindows\"/>.</note>\n\t\t\t/// Skips message-only windows; use <see cref=\"findFast\"/> if need.\n\t\t\t/// On Windows 8 and later may skip Windows Store app Metro-style windows (on Windows 10 few such windows exist). It happens if this program does not have <c>disableWindowFiltering = true</c> in its manifest and is not uiAccess; to find such windows you can use <see cref=\"findFast\"/>.\n\t\t\t/// Tip: To get top-level and child windows in single array: <c>var a = wnd.getwnd.root.Get.Children();</c>.\n\t\t\t/// </remarks>\n\t\t\t/// <seealso cref=\"Children\"/>\n\t\t\t/// <seealso cref=\"findAll\"/>\n\t\t\tpublic static wnd[] allWindows(bool onlyVisible = false, bool sortFirstVisible = false) {\n\t\t\t\treturn Internal_.EnumWindows(Internal_.EnumAPI.EnumWindows, onlyVisible, sortFirstVisible);\n\t\t\t}\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// Gets top-level windows ordered as in the Z order.\n\t\t\t/// </summary>\n\t\t\t/// <returns>Array containing zero or more <see cref=\"wnd\"/>.</returns>\n\t\t\t/// <remarks>\n\t\t\t/// Uses API <ms>GetWindow</ms> and ensures it is reliable.\n\t\t\t/// </remarks>\n\t\t\tpublic static wnd[] allWindowsZorder() {\n\t\t\t\t//Algorithm to make getting all windows with GetWindow reliable:\n\t\t\t\t//\tGet all windows 2 times, and compare results. If different, wait 1-2 ms and repeat.\n\t\t\t\t//\tStill occasionally 1 window missing.\n\t\t\t\t//\t\tIt seems, when OS is reordering windows, it removes a window from its internal array and inserts in another place not atomically.\n\t\t\t\t//\t\tTo fix it, wait 1 ms after the first _GetWindows if results are different than previously.\n\t\t\t\t//Tested in stress conditions and compared with EnumWindows results. Never failed.\n\t\t\t\t//Speed with cold CPU: ~20% faster than EnumWindows.\n\t\t\t\t\n\t\t\t\tWeakReference<List<wnd>> wr1 = t_awz.wr1, wr2 = t_awz.wr2;\n\t\t\t\tif (wr1 == null) t_awz = (wr1 = new(null), wr2 = new(null), default);\n\t\t\t\tif (!wr1.TryGetTarget(out var a1)) wr1.SetTarget(a1 = new(800));\n\t\t\t\tif (!wr2.TryGetTarget(out var a2)) wr2.SetTarget(a2 = new(800));\n\t\t\t\t\n\t\t\t\tint nRetry = 0;\n\t\t\t\t_GetWindows(a1);\n\t\t\t\t\n\t\t\t\t//the fix\n\t\t\t\tvar hash1 = _Hash(a1);\n\t\t\t\tif (hash1 != t_awz.hash1) 1.ms();\n\t\t\t\t\n\t\t\t\tg1:\n\t\t\t\t_GetWindows(a2);\n\t\t\t\tif (a1.SequenceEqual(a2)) {\n\t\t\t\t\tDebug_.PrintIf(nRetry > 50, nRetry);\n\t\t\t\t\tt_awz.hash1 = nRetry == 0 ? hash1 : _Hash(a2);\n\t\t\t\t\treturn a2.ToArray();\n\t\t\t\t}\n\t\t\t\tMath2.Swap(ref a1, ref a2);\n\t\t\t\t1.ms();\n\t\t\t\tnRetry++;\n\t\t\t\tgoto g1;\n\t\t\t\t\n\t\t\t\tvoid _GetWindows(List<wnd> a) {\n\t\t\t\t\ta.Clear();\n\t\t\t\t\tfor (var w = wnd.getwnd.top; !w.Is0; w = w.Get.Next()) a.Add(w);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tHash.MD5Result _Hash(List<wnd> a) {\n\t\t\t\t\treturn Hash.MD5(MemoryMarshal.AsBytes(CollectionsMarshal.AsSpan(a)));\n\t\t\t\t}\n\t\t\t}\n\t\t\t[ThreadStatic] static (WeakReference<List<wnd>> wr1, WeakReference<List<wnd>> wr2, Hash.MD5Result hash1) t_awz;\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// Gets top-level windows of a thread.\n\t\t\t/// </summary>\n\t\t\t/// <returns>Array containing zero or more <see cref=\"wnd\"/>.</returns>\n\t\t\t/// <param name=\"threadId\">\n\t\t\t/// Unmanaged thread id.\n\t\t\t/// See <see cref=\"process.thisThreadId\"/>, <see cref=\"ThreadId\"/>.\n\t\t\t/// If 0, throws exception. If other invalid value (ended thread?), returns empty list. Supports <see cref=\"lastError\"/>.\n\t\t\t/// </param>\n\t\t\t/// <param name=\"onlyVisible\">Need only visible windows.</param>\n\t\t\t/// <param name=\"sortFirstVisible\">Place all array elements of hidden windows at the end of the array, even if the hidden windows are before some visible windows in the Z order.</param>\n\t\t\t/// <exception cref=\"ArgumentException\"><i>threadId</i> is 0.</exception>\n\t\t\t/// <remarks>\n\t\t\t/// Calls API <ms>EnumThreadWindows</ms>.\n\t\t\t/// </remarks>\n\t\t\t/// <seealso cref=\"process.thisThreadHasMessageLoop\"/>\n\t\t\tpublic static wnd[] threadWindows(int threadId, bool onlyVisible = false, bool sortFirstVisible = false) {\n\t\t\t\tif (threadId == 0) throw new ArgumentException(\"0 threadId.\");\n\t\t\t\treturn Internal_.EnumWindows(Internal_.EnumAPI.EnumThreadWindows, onlyVisible, sortFirstVisible, threadId: threadId);\n\t\t\t}\n\t\t\t\n\t\t\t//rejected\n\t\t\t///// <param name=\"a\">Receives results. If <c>null</c>, this function creates new <c>List</c>, else clears before adding items.</param>\n\t\t\t///// <remarks>This overload can be used to avoid much garbage when calling frequently.</remarks>\n\t\t\t///// <inheritdoc cref=\"threadWindows(int, bool, bool)\"/>\n\t\t\t//public static void threadWindows(ref List<wnd> a, int threadId, bool onlyVisible = false, bool sortFirstVisible = false) {\n\t\t\t//\tif (threadId == 0) throw new ArgumentException(\"0 threadId.\");\n\t\t\t//\tInternal_.EnumWindows2(Internal_.EnumAPI.EnumThreadWindows, onlyVisible, sortFirstVisible, threadId: threadId, list: a ??= new List<wnd>());\n\t\t\t//}\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// Gets the first in Z order window of this thread.\n\t\t\t/// </summary>\n\t\t\t/// <param name=\"onlyVisible\"></param>\n\t\t\t/// <param name=\"nonPopup\">Skip <c>WS_POPUP</c> without <c>WS_CAPTION</c>.</param>\n\t\t\tinternal static wnd TopThreadWindow_(bool onlyVisible, bool nonPopup) {\n\t\t\t\twnd r = default;\n\t\t\t\tApi.EnumThreadWindows(Api.GetCurrentThreadId(), (w, _) => {\n\t\t\t\t\tif (onlyVisible && !w.IsVisible) return 1;\n\t\t\t\t\tif (nonPopup) if ((w.Style & (WS.POPUP | WS.CAPTION)) == WS.POPUP) return 1;\n\t\t\t\t\tr = w;\n\t\t\t\t\treturn 0;\n\t\t\t\t});\n\t\t\t\treturn r;\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Internal static functions.\n\t\t/// </summary>\n\t\tinternal static partial class Internal_ {\n\t\t\tinternal enum EnumAPI { EnumWindows, EnumThreadWindows, EnumChildWindows, }\n\t\t\t\n\t\t\tinternal static wnd[] EnumWindows(EnumAPI api,\n\t\t\t\tbool onlyVisible, bool sortFirstVisible, wnd wParent = default, bool directChild = false, int threadId = 0) {\n\t\t\t\tusing var a = EnumWindows2(api, onlyVisible, sortFirstVisible, wParent, directChild, threadId);\n\t\t\t\treturn a.ToArray();\n\t\t\t}\n\t\t\t\n\t\t\t/// <summary>\n\t\t\t/// This version creates much less garbage.\n\t\t\t/// The caller must dispose the returned <c>ArrayBuilder_</c>, unless list is not <c>null</c>.\n\t\t\t/// If list is not <c>null</c>, adds windows there (clears at first) and returns <c>default(ArrayBuilder_)</c>.\n\t\t\t/// </summary>\n\t\t\tinternal static ArrayBuilder_<wnd> EnumWindows2(EnumAPI api,\n\t\t\t\tbool onlyVisible, bool sortFirstVisible = false, wnd wParent = default, bool directChild = false, int threadId = 0,\n\t\t\t\tFunc<wnd, object, bool> predicate = null, object predParam = default, List<wnd> list = null\n\t\t\t\t) {\n\t\t\t\tif (directChild && wParent == getwnd.root) { api = EnumAPI.EnumWindows; wParent = default; }\n\t\t\t\t\n\t\t\t\tArrayBuilder_<wnd> ab = default;\n\t\t\t\tbool disposeArray = true;\n\t\t\t\tvar d = new _EnumData { api = api, onlyVisible = onlyVisible, directChild = directChild, wParent = wParent };\n\t\t\t\ttry {\n\t\t\t\t\tswitch (api) {\n\t\t\t\t\tcase EnumAPI.EnumWindows:\n\t\t\t\t\t\tApi.EnumWindows(_wndEnumProc, &d);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase EnumAPI.EnumThreadWindows:\n\t\t\t\t\t\tApi.EnumThreadWindows(threadId, _wndEnumProc, &d);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase EnumAPI.EnumChildWindows:\n\t\t\t\t\t\tApi.EnumChildWindows(wParent, _wndEnumProc, &d);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tint n = d.len;\n\t\t\t\t\tif (n > 0) {\n\t\t\t\t\t\tif (predicate != null) {\n\t\t\t\t\t\t\tn = 0;\n\t\t\t\t\t\t\tfor (int i = 0; i < d.len; i++) {\n\t\t\t\t\t\t\t\tif (predicate((wnd)d.a[i], predParam)) d.a[n++] = d.a[i];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\tif (list != null) {\n\t\t\t\t\t\t\tlist.Clear();\n\t\t\t\t\t\t\tif (list.Capacity < n) list.Capacity = n + n / 2;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tab.Alloc(n, zeroInit: false, noExtra: true);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (sortFirstVisible && !onlyVisible) {\n\t\t\t\t\t\t\tint j = 0;\n\t\t\t\t\t\t\tfor (int i = 0; i < n; i++) {\n\t\t\t\t\t\t\t\tvar w = (wnd)d.a[i];\n\t\t\t\t\t\t\t\tif (!_EnumIsVisible(w, api, wParent)) continue;\n\t\t\t\t\t\t\t\tif (list != null) list.Add(w); else ab[j++] = w;\n\t\t\t\t\t\t\t\td.a[i] = 0;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tfor (int i = 0; i < n; i++) {\n\t\t\t\t\t\t\t\tint wi = d.a[i];\n\t\t\t\t\t\t\t\tif (wi == 0) continue;\n\t\t\t\t\t\t\t\tvar w = (wnd)wi;\n\t\t\t\t\t\t\t\tif (list != null) list.Add(w); else ab[j++] = w;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else if (list != null) {\n\t\t\t\t\t\t\tfor (int i = 0; i < n; i++) list.Add((wnd)d.a[i]);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tfor (int i = 0; i < n; i++) ab[i] = (wnd)d.a[i];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tdisposeArray = false;\n\t\t\t\t\treturn ab;\n\t\t\t\t}\n\t\t\t\tfinally {\n\t\t\t\t\tMemoryUtil.Free(d.a);\n\t\t\t\t\tif (disposeArray) ab.Dispose();\n\t\t\t\t}\n\t\t\t}\n\t\t\tstatic Api.WNDENUMPROC _wndEnumProc = (w, p) => ((_EnumData*)p)->Proc(w);\n\t\t\t\n\t\t\tstruct _EnumData {\n\t\t\t\tpublic int* a;\n\t\t\t\tpublic int len;\n\t\t\t\tint _cap;\n\t\t\t\tpublic EnumAPI api;\n\t\t\t\tpublic bool onlyVisible, directChild;\n\t\t\t\tpublic wnd wParent;\n\t\t\t\tint _ownerTid;\n\t\t\t\tbool _ownerFound;\n\t\t\t\t\n\t\t\t\tpublic int Proc(wnd w) {\n\t\t\t\t\tif (api == EnumAPI.EnumChildWindows) {\n\t\t\t\t\t\tif (onlyVisible && !w.IsVisibleIn_(wParent)) return 1;\n\t\t\t\t\t\tif (directChild && w.ParentGWL_ != wParent) return 1;\n\t\t\t\t\t} else if (wParent.Is0) {\n\t\t\t\t\t\tif (onlyVisible && !w.IsVisible) return 1;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (!_ownerFound && w == wParent) { _ownerFound = true; return 1; }\n\t\t\t\t\t\tif (onlyVisible && !w.IsVisible) return 1;\n\t\t\t\t\t\tif (!w.IsOwnedBy2_(wParent, _ownerFound ? 1 : 2, ref _ownerTid)) return 1;\n\t\t\t\t\t\t//if _ownerFound, still call with level 1, in case of bug \"owned window is behind owner\"\n\t\t\t\t\t}\n\t\t\t\t\tif (a == null) a = MemoryUtil.Alloc<int>(_cap = onlyVisible ? 200 : 1000);\n\t\t\t\t\telse if (len == _cap) MemoryUtil.ReAlloc(ref a, _cap *= 2);\n\t\t\t\t\ta[len++] = (int)w;\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t//note: need this in exe manifest. Else EnumWindows skips \"immersive\" windows if this process is not admin/uiAccess.\n\t\t\t\t/*\n<asmv3:application>\n...\n<asmv3:windowsSettings xmlns=\"http://schemas.microsoft.com/SMI/2011/WindowsSettings\">\n  <disableWindowFiltering>true</disableWindowFiltering>\n</asmv3:windowsSettings>\n</asmv3:application>\n\t\t\t\t*/\n\t\t\t}\n\t\t\t\n\t\t\tstatic bool _EnumIsVisible(wnd w, EnumAPI api, wnd wParent)\n\t\t\t\t=> api == EnumAPI.EnumChildWindows ? w.IsVisibleIn_(wParent) : w.IsVisible;\n\t\t}\n\t}\n}\n\nnamespace Au.Types {\n\t/// <summary>\n\t/// Flags of <see cref=\"wnd.find\"/> and similar functions.\n\t/// </summary>\n\t[Flags]\n\tpublic enum WFlags {\n\t\t/// <summary>\n\t\t/// Can find invisible windows. See <see cref=\"wnd.IsVisible\"/>.\n\t\t/// Use this carefully. Always use <i>cn</i> (class name), not just <i>name</i>, to avoid finding a wrong window with the same name.\n\t\t/// </summary>\n\t\tHiddenToo = 1,\n\t\t\n\t\t/// <summary>\n\t\t/// Can find cloaked windows. See <see cref=\"wnd.IsCloaked\"/>.\n\t\t/// Cloaked are windows hidden not in the classic way, therefore <see cref=\"wnd.IsVisible\"/> does not detect it, but <see cref=\"wnd.IsCloaked\"/> detects. For example, windows on inactive Windows 10 virtual desktops, ghost windows of inactive Windows Store apps, various hidden system windows.\n\t\t/// Use this carefully. Always use <i>cn</i> (class name), not just <i>name</i>, to avoid finding a wrong window with the same name.\n\t\t/// </summary>\n\t\tCloakedToo = 2,\n\t}\n\t\n\t/// <summary>\n\t/// Used with <see cref=\"wnd.find\"/> and similar functions to specify an owner of the window.\n\t/// Can be program name (like <c>\"notepad.exe\"</c>), process id (<see cref=\"Process\"/>), thread id (<see cref=\"Thread\"/> or <see cref=\"ThisThread\"/>), owner window.\n\t/// </summary>\n\tpublic struct WOwner {\n\t\treadonly string _s; //program\n\t\treadonly int _i; //wnd, tid, pid\n\t\treadonly byte _what; //0 _o, 1 owner, 2 tid, 3 pid\n\t\t\n\t\t//readonly byte _ownerLevel; //rejected. Rarely used. Can use *also* instead.\n\t\t\n\t\tWOwner(string s) => _s = s;\n\t\t\n\t\tWOwner(int i, byte what) { _i = i; _what = what; }\n\t\t\n\t\t/// <summary>Program name like <c>\"notepad.exe\"</c>, or <c>null</c>. See <see cref=\"wnd.ProgramName\"/>.</summary>\n\t\tpublic static implicit operator WOwner([ParamString(PSFormat.Wildex)] string program) => new(program);\n\t\t\n\t\t/// <summary>Owner window. See <see cref=\"wnd.getwnd.Owner\"/>. Will use <see cref=\"wnd.IsOwnedBy(wnd, int)\"/> with level 2.</summary>\n\t\tpublic static implicit operator WOwner(wnd ownerWindow) => new((int)ownerWindow, 1);\n\t\t\n\t\t///// <summary>Owner window. See <see cref=\"wnd.getwnd.Owner\"/>. Will use <see cref=\"wnd.IsOwnedBy(wnd, int)\"/> with level 2.</summary>\n\t\t//public static implicit operator WOwner(System.Windows.DependencyObject ownerWindow) => new((int)ownerWindow.Hwnd(), 1);\n\t\t\n\t\t/// <summary>Process id. See <see cref=\"wnd.ProcessId\"/>.</summary>\n\t\tpublic static WOwner Process(int processId) => new(processId, 3);\n\t\t\n\t\t/// <summary>Thread id. See <see cref=\"wnd.ThreadId\"/>.</summary>\n\t\tpublic static WOwner Thread(int threadId) => new(threadId, 2);\n\t\t\n\t\t/// <summary>Thread id of this thread.</summary>\n\t\tpublic static WOwner ThisThread => new(Api.GetCurrentThreadId(), 2);\n\t\t\n\t\t/// <summary>\n\t\t/// Gets program name or process id or thread id or owner window.\n\t\t/// Other variables will be <c>null</c>/0.\n\t\t/// </summary>\n\t\t/// <exception cref=\"ArgumentException\">The value is <c>\"\"</c> or 0 or contains characters <c>'\\\\'</c> or <c>'/'</c> or is invalid wildcard expression.</exception>\n\t\tpublic void GetValue(out wildex program, out int pid, out int tid, out wnd owner) {\n\t\t\tprogram = null; pid = 0; tid = 0; owner = default;\n\t\t\tswitch (_what) {\n\t\t\tcase 0 when _s != null:\n\t\t\t\tif (_s.Length == 0) throw new ArgumentException(\"Program name cannot be \\\"\\\". Use null.\");\n\t\t\t\tif (!_s.Starts(\"**\")) { //can be regex\n\t\t\t\t\tif (_s.FindAny(@\"\\/\") >= 0) throw new ArgumentException(\"Program name contains \\\\ or /.\");\n\t\t\t\t\tif (pathname.findExtension(_s) < 0 && !wildex.hasWildcardChars(_s)) print.warning(\"Program name without .exe.\");\n\t\t\t\t}\n\t\t\t\tprogram = _s;\n\t\t\t\tbreak;\n\t\t\tcase 1:\n\t\t\t\towner = (wnd)_i;\n\t\t\t\tif (owner.Is0) throw new ArgumentException(\"owner window 0\");\n\t\t\t\tbreak;\n\t\t\tcase 2:\n\t\t\t\tif ((tid = _i) == 0) throw new ArgumentException(\"thread id 0\");\n\t\t\t\tbreak;\n\t\t\tcase 3:\n\t\t\t\tif ((pid = _i) == 0) throw new ArgumentException(\"process id 0\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if nothing was assigned to this variable.\n\t\t/// </summary>\n\t\tpublic bool IsEmpty => _what == 0 && _s == null;\n\t}\n\t\n\t/// <summary>\n\t/// The <i>contains</i> parameter of <see cref=\"wnd.find\"/> and similar functions.\n\t/// Specifies text, image or other object that must be in the window.\n\t/// </summary>\n\tpublic struct WContains {\n\t\treadonly object _o;\n\t\tWContains(object o) => _o = o;\n\t\t\n\t\t///\n\t\tpublic static implicit operator WContains(wndChildFinder f) => new(f);\n\t\t\n\t\t///\n\t\tpublic static implicit operator WContains(elmFinder f) => new(f);\n\t\t\n\t\t///\n\t\tpublic static implicit operator WContains(uiimageFinder f) => new(f);\n\t\t\n\t\t///\n\t\tpublic static implicit operator WContains(ocrFinder f) => new(f);\n\t\t\n\t\t/// <summary>\n\t\t/// Converts from string to <see cref=\"wndChildFinder\"/>, <see cref=\"elmFinder\"/>, <see cref=\"uiimageFinder\"/> or <see cref=\"ocrFinder\"/>.\n\t\t/// See <see cref=\"wnd.find\"/>.\n\t\t/// </summary>\n\t\t/// <exception cref=\"Exception\">Exceptions of constructor of <see cref=\"wndChildFinder\"/>, <see cref=\"elmFinder\"/>, <see cref=\"uiimageFinder\"/> or <see cref=\"ocrFinder\"/>.</exception>\n\t\tpublic static implicit operator WContains(string s) => new(_ParseString(s));\n\t\t\n\t\tstatic object _ParseString(string s) {\n\t\t\tif (s.NE()) return null;\n\t\t\tstring role = null, name = s;\n\t\t\tswitch (s[0]) {\n\t\t\tcase 'e': //\"e 'role' name\" or just \"name\"\n\t\t\tcase 'c': //\"c 'class' text\"\n\t\t\t\tif (s.RxMatch(@\"^. ?'(.+?)?' ?((?s).+)?$\", out var m)) {\n\t\t\t\t\trole = m[1].Value; name = m[2].Value;\n\t\t\t\t\tif (s[0] == 'c') return new wndChildFinder(name, role);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 'i' when s.Starts(\"image:\"):\n\t\t\t\treturn new uiimageFinder(s, IFFlags.WindowDC);\n\t\t\tcase 'o' when s.Starts(\"ocr:\"):\n\t\t\t\treturn new ocrFinder(s[4..], OcrFlags.WindowDC);\n\t\t\t}\n\t\t\treturn new elmFinder(role, name, flags: EFFlags.ClientArea) { ResultGetProperty = '-' };\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets object stored in this variable. Can be <c>null</c>, <see cref=\"wndChildFinder\"/>, <see cref=\"elmFinder\"/>, <see cref=\"uiimageFinder\"/> or <see cref=\"ocrFinder\"/>.\n\t\t/// </summary>\n\t\tpublic object Value => _o;\n\t}\n\t\n\t/// <summary>\n\t/// Can be used with <see cref=\"wndFinder.IsMatch\"/>.\n\t/// </summary>\n\tpublic class WFCache {\n\t\twnd _w;\n\t\tlong _time;\n\t\tinternal string Name, Class, Program;\n\t\tinternal int Tid, Pid;\n\t\t\n\t\t/// <summary>\n\t\t/// Cache window name.\n\t\t/// Default: <c>false</c>.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Window name is not cached by default because can be changed. Window class name and program name are always cached because cannot be changed.\n\t\t/// </remarks>\n\t\tpublic bool CacheName { get; set; }\n\t\t\n\t\t/// <summary>\n\t\t/// Don't auto-clear cached properties on timeout.\n\t\t/// </summary>\n\t\tpublic bool NoTimeout { get; set; }\n\t\t\n\t\tinternal void Begin(wnd w) {\n\t\t\tif (NoTimeout) {\n\t\t\t\tif (w != _w) {\n\t\t\t\t\tClear();\n\t\t\t\t\t_w = w;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tvar t = Api.GetTickCount64();\n\t\t\t\tif (w != _w || t - _time > 2500) {\n\t\t\t\t\tClear();\n\t\t\t\t\tif (w.IsAlive) { _w = w; _time = t; }\n\t\t\t\t}\n\t\t\t\t//else if(CacheName && t - _time > 100) Name = null; //no, instead let call Clear if need\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Clears all cached properties, or only name.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Usually don't need to call this function. It is implicitly called when the variable is used with a new window.\n\t\t/// </remarks>\n\t\t/// <param name=\"onlyName\">Clear only name (because it may change, unlike other cached properties).</param>\n\t\t[MethodImpl(MethodImplOptions.AggressiveInlining)]\n\t\tpublic void Clear(bool onlyName = false) {\n\t\t\tif (onlyName) Name = null;\n\t\t\telse { _w = default; _time = 0; Name = Class = Program = null; Tid = Pid = 0; }\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Match invisible and cloaked windows too, even if the flags are not set (see <see cref=\"WFlags\"/>).\n\t\t/// </summary>\n\t\tpublic bool IgnoreVisibility { get; set; }\n\t}\n}\n"
  },
  {
    "path": "Au/wnd/wnd_fromxy.cs",
    "content": "namespace Au {\n\tpublic partial struct wnd {\n\t\t/// <summary>\n\t\t/// Gets visible top-level window or control from point.\n\t\t/// </summary>\n\t\t/// <param name=\"p\">\n\t\t/// Coordinates.\n\t\t/// Tip: To specify coordinates relative to the right, bottom, work area or a non-primary screen, use <see cref=\"Coord.Normalize\"/>, like in the example.\n\t\t/// </param>\n\t\t/// <param name=\"flags\"></param>\n\t\t/// <remarks>\n\t\t/// Unlike API <ms>WindowFromPhysicalPoint</ms> etc, this function: does not skip disabled controls; always skips transparent control like group box if a smaller sibling is there. All this is not true with flag <c>Raw</c>.\n\t\t/// </remarks>\n\t\t/// <example>\n\t\t/// Find window at 100 200.\n\t\t/// <code><![CDATA[\n\t\t/// var w = wnd.FromXY((100, 200), WXYFlags.NeedWindow);\n\t\t/// print.it(w);\n\t\t/// ]]></code>\n\t\t/// \n\t\t/// Find window or control at 50 from left and 100 from bottom of the work area.\n\t\t/// <code><![CDATA[\n\t\t/// var w = wnd.FromXY(Coord.Normalize(50, ^100, workArea: true));\n\t\t/// print.it(w);\n\t\t/// ]]></code>\n\t\t/// </example>\n\t\tpublic static wnd fromXY(POINT p, WXYFlags flags = 0) {\n\t\t\tbool needW = flags.Has(WXYFlags.NeedWindow);\n\t\t\tbool needC = flags.Has(WXYFlags.NeedControl);\n\t\t\tif (needW && needC) throw new ArgumentException(\"\", \"flags\");\n\t\t\t\n\t\t\tif (flags.HasAny(WXYFlags.Raw | WXYFlags.NeedWindow)) {\n\t\t\t\tvar w = Api.WindowFromPoint(p);\n\t\t\t\tif (needW) return w.Window;\n\t\t\t\treturn !needC || w.IsChild ? w : default;\n\t\t\t} else {\n\t\t\t\tvar w = _FromXY(p, out bool isChild);\n\t\t\t\treturn !needC || isChild ? w : default;\n\t\t\t}\n\t\t\t\n\t\t\t//info:\n\t\t\t//WindowFromPoint is the most reliable. It skips really transparent top-level windows (TL). Unfortunately it skips disabled controls (but not TL).\n\t\t\t//ChildWindowFromPointEx with CWP_SKIPINVISIBLE|CWP_SKIPTRANSPARENT skips all with WS_EX_TRANSPARENT, although without WS_EX_LAYERED they aren't actually transparent.\n\t\t\t//RealChildWindowFromPoint does not skip transparent TL. It works like ChildWindowFromPointEx(CWP_SKIPINVISIBLE).\n\t\t\t//None of the above API prefers a really visible control that is under a transparent part of a sibling control. RealChildWindowFromPoint does it only for group buttons, but not for tab controls etc.\n\t\t\t//All API skip windows that have a hole etc in window region at that point.\n\t\t\t//If same thread, WindowFromPoint uses WM_NCHITTEST+HTTRANSPARENT. Other API tested only with TL of other processes and don't use.\n\t\t\t//AccessibleObjectFromPoint too dirty, unreliable and often very slow, because sends WM_GETOBJECT etc.\n\t\t\t//IUIAutomation.FromPoint very slow. Getting window handle from it is not easy, > 0.5 ms.\n\t\t}\n\t\t\n\t\t/// <inheritdoc cref=\"fromXY(POINT, WXYFlags)\"/>\n\t\tpublic static wnd fromXY(int x, int y, WXYFlags flags = 0) => fromXY(new POINT(x, y), flags);\n\t\t\n\t\t//rejected: FromXY(Coord, Coord, ...). Coord makes no sense.\n\t\t\n\t\t/// <summary>\n\t\t/// Gets visible top-level window or control from mouse cursor position.\n\t\t/// More info: <see cref=\"fromXY\"/>.\n\t\t/// </summary>\n\t\tpublic static wnd fromMouse(WXYFlags flags = 0) => fromXY(mouse.xy, flags);\n\t\t\n\t\t/// <summary>\n\t\t/// Gets descendant control from point.\n\t\t/// </summary>\n\t\t/// <returns>By default returns <c>default(wnd)</c> if the point is not in a child control; it depends on <i>flags</i>.</returns>\n\t\t/// <param name=\"x\">X coordinate in client area or screen (if flag <c>ScreenXY</c>). Examples: <c>10</c>, <c>^10</c> (reverse), <c>.5f</c> (fraction).</param>\n\t\t/// <param name=\"y\">Y coordinate.</param>\n\t\t/// <param name=\"flags\"></param>\n\t\t/// <exception cref=\"AuWndException\">This variable is invalid (window not found, closed, etc).</exception>\n\t\tpublic wnd ChildFromXY(Coord x, Coord y, WXYCFlags flags = 0) {\n\t\t\tThrowIfInvalid();\n\t\t\tPOINT p = flags.Has(WXYCFlags.ScreenXY) ? Coord.Normalize(x, y) : Coord.NormalizeInWindow(x, y, this);\n\t\t\treturn _ChildFromXY(p, flags);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets descendant control from point.\n\t\t/// </summary>\n\t\t/// <returns>By default returns <c>default(wnd)</c> if the point is not in a child control; it depends on <i>flags</i>.</returns>\n\t\t/// <param name=\"p\">Coordinates in client area or screen (if flag <c>ScreenXY</c>).</param>\n\t\t/// <param name=\"flags\"></param>\n\t\t/// <exception cref=\"AuWndException\">This variable is invalid (window not found, closed, etc).</exception>\n\t\tpublic wnd ChildFromXY(POINT p, WXYCFlags flags = 0) {\n\t\t\tThrowIfInvalid();\n\t\t\treturn _ChildFromXY(p, flags);\n\t\t}\n\t\t\n\t\t//Gets real control or window from point in screen.\n\t\t//The input control can be eg from WindowFromPhysicalPoint, which skips disabled and transparent controls and does not get controls under transparent siblings such as groupbox button.\n\t\t//In such cases this struct replaces the input control (hwnd field) with the real control (descendant or sibling).\n\t\t//Fully DPI-aware on Win8.1+.\n\t\tstruct _WindowFromPoint : IDisposable {\n\t\t\tPOINT _p;\n\t\t\tIntPtr _hr;\n\t\t\tpublic wnd hwnd;\n\t\t\tpublic RECT r;\n\t\t\t\n\t\t\tpublic _WindowFromPoint(POINT p, wnd hwndStart) {\n\t\t\t\tr = default;\n\t\t\t\t_hr = default;\n\t\t\t\thwnd = hwndStart;\n\t\t\t\t_p = p;\n\t\t\t\t\n\t\t\t\t//rejected. This library does not support DPI-scaled windows on Win7-8; too expensive.\n\t\t\t\t//if (!osVersion.minWin8_1 && !Api.PhysicalToLogicalPoint(hwnd, ref _p)) _p = p;\n\t\t\t\t////print.it(p, _p);\n\t\t\t\t\n\t\t\t\t//tested: on Win10 WM_NCHITTEST and GetWindowRgn use physical coords with DPI-scaled windows.\n\t\t\t}\n\t\t\t\n\t\t\tpublic void Dispose() { if (_hr != default) Api.DeleteObject(_hr); }\n\t\t\t\n\t\t\tpublic (bool inWindow, bool inClient) IsInWindow() {\n\t\t\t\tif (hwnd.GetWindowInfo_(out var k)) return (k.rcWindow.Contains(_p), k.rcClient.Contains(_p));\n\t\t\t\treturn default;\n\t\t\t}\n\t\t\t\n\t\t\tbool _IsPointVisibleIn_1(wnd c) {\n\t\t\t\treturn c.HasStyle(WS.VISIBLE) && Api.GetWindowRect(c, out r) && r.Contains(_p);\n\t\t\t}\n\t\t\t\n\t\t\tbool _IsPointVisibleIn_2(wnd c) {\n\t\t\t\tif (_hr == default) _hr = Api.CreateRectRgn(0, 0, 0, 0);\n\t\t\t\tif (Api.GetWindowRgn(c, _hr) > 1 && !Api.PtInRegion(_hr, _p.x - r.left, _p.y - r.top)) return false;\n\t\t\t\tif (c.HasExStyle(WSE.LAYERED) && c.IsCloaked) return false;\n\t\t\t\treturn true;\n\t\t\t\t//if(osVersion.minWin8 && c.HasExStyle(WSE.LAYERED) && Api.GetLayeredWindowAttributes(c, ... //never mind. We can get alpha 0 (probably rare), but difficult or impossible to detect whether the point is transparent because of color key (probably not so rare).\n\t\t\t\t//tested: WindowFromPoint skips all: region, cloaked, transparent (by alpha or colorkey).\n\t\t\t\t//tested: RealChildWindowFromPoint skips region and cloaked, but not transparent.\n\t\t\t\t//tested: UI Automation skips region and cloaked, but not transparent.\n\t\t\t\t//tested: AccessibleObjectFromPoint skips only cloaked.\n\t\t\t\t//tested: Spy++ skips none.\n\t\t\t\t//tested: NtUserWindowFromPoint is the same as WindowFromPoint.\n\t\t\t}\n\t\t\t\n\t\t\tpublic bool FindSibling() {\n\t\t\t\tbool found = false;\n\t\t\t\tif (Api.GetWindowRect(hwnd, out RECT r1)) {\n\t\t\t\t\tfor (wnd c2 = hwnd; !(c2 = Api.GetWindow(c2, Api.GW_HWNDNEXT)).Is0;) { //GetWindow often is the slowest part. EnumChildWindows slower.\n\t\t\t\t\t\tif (_IsPointVisibleIn_1(c2)) {\n\t\t\t\t\t\t\tif (r.left >= r1.left && r.top >= r1.top && r.right <= r1.right && r.bottom <= r1.bottom && (r.right - r.left < r1.right - r1.left || r.bottom - r.top < r1.bottom - r1.top)) {\n\t\t\t\t\t\t\t\tif (_IsPointVisibleIn_2(c2)) {\n\t\t\t\t\t\t\t\t\tif (hwnd.SendTimeout(1000, out var ht, Api.WM_NCHITTEST, 0, Math2.MakeLparam(_p), SMTFlags.ABORTIFHUNG | SMTFlags.BLOCK) && ht != Api.HTTRANSPARENT) break;\n\t\t\t\t\t\t\t\t\thwnd = c2;\n\t\t\t\t\t\t\t\t\tr1 = r;\n\t\t\t\t\t\t\t\t\tfound = true;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn found;\n\t\t\t}\n\t\t\t\n\t\t\tpublic bool FindDescendant(bool directChild = false) {\n\t\t\t\tbool found = false;\n\t\t\t\tgNextGeneration:\n\t\t\t\tfor (wnd c2 = Api.GetWindow(hwnd, Api.GW_CHILD); !c2.Is0; c2 = Api.GetWindow(c2, Api.GW_HWNDNEXT)) {\n\t\t\t\t\tif (_IsPointVisibleIn_1(c2) && _IsPointVisibleIn_2(c2)) {\n\t\t\t\t\t\thwnd = c2;\n\t\t\t\t\t\tfound = true;\n\t\t\t\t\t\tif (directChild) break;\n\t\t\t\t\t\tgoto gNextGeneration;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn found;\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets window or control from point.\n\t\t/// Returns <c>default(wnd)</c> if failed (unlikely).\n\t\t/// </summary>\n\t\t/// <param name=\"p\">Point in screen.</param>\n\t\t/// <param name=\"isChild\">Receives <c>true</c> if control, false if top-level or failed.</param>\n\t\tstatic wnd _FromXY(POINT p, out bool isChild) {\n\t\t\twnd w = Api.WindowFromPoint(p);\n\t\t\tif (w.Is0) { isChild = false; return default; }\n\t\t\tusing var k = new _WindowFromPoint(p, w);\n\t\t\tbool findSibling = w.IsChild, findDescendant = true;\n\t\t\tfor (; ; ) {\n\t\t\t\t//find a smaller sibling fully covered by c\n\t\t\t\tif (findSibling) findDescendant |= k.FindSibling();\n\t\t\t\t\n\t\t\t\t//find descendant, because: 1. WindowFromPoint does not find disabled controls. 2. The above code may change k.hwnd.\n\t\t\t\tif (!findDescendant) break;\n\t\t\t\tif (!k.FindDescendant()) break;\n\t\t\t\tfindSibling = true; findDescendant = false;\n\t\t\t}\n\t\t\tisChild = findSibling;\n\t\t\treturn k.hwnd;\n\t\t\t\n\t\t\t//note: don't use API [Real]ChildWindowFromPoint[Ex]. Bugs:\n\t\t\t//\t1. Returns wrong control in DPI-scaled windows in non-primary screen with different DPI.\n\t\t\t//\t2. Returns wrong control in RTL windows.\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets descendant control from point. This can be top-level window or control.\n\t\t/// If there is no descendant, the return value depends on <i>flags</i>.\n\t\t/// </summary>\n\t\t/// <param name=\"p\">Point in client area or screen (if flag <c>ScreenXY</c>).</param>\n\t\t/// <param name=\"flags\"></param>\n\t\twnd _ChildFromXY(POINT p, WXYCFlags flags = 0) {\n\t\t\tif (!flags.Has(WXYCFlags.ScreenXY)) Api.ClientToScreen(this, ref p);\n\t\t\t\n\t\t\tbool directChild = flags.Has(WXYCFlags.DirectChild),\n\t\t\t\torThis = flags.Has(WXYCFlags.OrThis),\n\t\t\t\tinside = flags.Has(WXYCFlags.Inside);\n\t\t\t\n\t\t\tusing var k = new _WindowFromPoint(p, this);\n\t\t\tif (inside) {\n\t\t\t\tvar v = k.IsInWindow();\n\t\t\t\tif (!v.inWindow) return default;\n\t\t\t\tif (!v.inClient) return orThis ? this : default;\n\t\t\t}\n\t\t\t\n\t\t\tfor (; ; ) {\n\t\t\t\t//find descendant\n\t\t\t\tif (!k.FindDescendant(directChild)) break;\n\t\t\t\t\n\t\t\t\t//find a smaller sibling fully covered by c\n\t\t\t\tif (!k.FindSibling()) break;\n\t\t\t\tif (directChild) break;\n\t\t\t}\n\t\t\t\n\t\t\tvar R = k.hwnd;\n\t\t\tif (R == this) {\n\t\t\t\tif (!orThis || (!inside && !k.IsInWindow().inWindow)) R = default;\n\t\t\t}\n\t\t\treturn R;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets sibling control in space: left, right, above or below.\n\t\t/// Returns <c>default(wnd)</c> if there is no sibling.\n\t\t/// </summary>\n\t\t/// <param name=\"direction\"></param>\n\t\t/// <param name=\"distance\">Distance from this control (from its edge) in the specified direction.</param>\n\t\t/// <param name=\"edgeOffset\">\n\t\t/// Distance in perpendicular direction, along the specified edge. Default 5.\n\t\t/// If <i>direction</i> is <c>Left</c> or <c>Right</c>, 0 is the top edge, 1 is 1 pixel down, -1 is 1 pixel up, and so on.\n\t\t/// If <i>direction</i> is <c>Above</c> or <c>Below</c>, 0 is the left edge, 1 is 1 pixel to the right, -1 is 1 pixel to the left, and so on.\n\t\t/// </param>\n\t\t/// <param name=\"topChild\">If at that point is a visible child or descendant of the sibling, get that child/descendant. Default <c>false</c>.</param>\n\t\t/// <exception cref=\"AuWndException\">This variable is invalid (window not found, closed, etc).</exception>\n\t\t/// <remarks>\n\t\t/// This function is used mostly with controls, but supports top-level windows too.\n\t\t/// </remarks>\n\t\twnd _SiblingXY(_SibXY direction, int distance, int edgeOffset = 5, bool topChild = false) {\n\t\t\tThrowIfInvalid();\n\t\t\twnd w = Get.DirectParent;\n\t\t\tif (!(w.Is0 ? GetRect(out RECT r) : GetRectIn(w, out r))) ThrowUseNative(); //note: most other wnd 'get' functions don't throw, but here it's better to throw.\n\t\t\tPOINT p = default;\n\t\t\tswitch (direction) {\n\t\t\tcase _SibXY.Left: p = (r.left - distance, r.top + edgeOffset); break;\n\t\t\tcase _SibXY.Right: p = (r.right + distance, r.top + edgeOffset); break;\n\t\t\tcase _SibXY.Above: p = (r.left + edgeOffset, r.top - distance); break;\n\t\t\tcase _SibXY.Below: p = (r.left + edgeOffset, r.bottom + distance); break;\n\t\t\t}\n\t\t\t//print.it(p); if(w.Is0) mouse.move(p); else mouse.move(w, p.x, p.y);\n\t\t\twnd R = w.Is0\n\t\t\t\t? fromXY(p, topChild ? 0 : WXYFlags.NeedWindow)\n\t\t\t\t: w._ChildFromXY(p, topChild ? 0 : WXYCFlags.DirectChild);\n\t\t\treturn R == this ? default : R; //cannot return self, but can return child if topChild, it's ok\n\t\t}\n\t\t\n\t\tenum _SibXY { Left, Right, Above, Below }\n\t\t\n\t\twnd _SiblingXY(_SibXY direction) {\n\t\t\tThrowIfInvalid();\n\t\t\twnd desktop = default; if (!this.IsChild) getwnd.desktop(out desktop, out _);\n\t\t\tvar r = Rect;\n\t\t\twnd nearest = default; int nearestDist = int.MaxValue;\n\t\t\tfor (var c = Api.GetWindow(this, Api.GW_HWNDFIRST); !c.Is0; c = Api.GetWindow(c, Api.GW_HWNDNEXT)) {\n\t\t\t\tif (c == this) continue;\n\t\t\t\tif (!c.IsVisible) continue;\n\t\t\t\tvar k = c.Rect;\n\t\t\t\tint dist = 0;\n\t\t\t\tswitch (direction) {\n\t\t\t\tcase _SibXY.Left: if (k.left >= r.left || k.bottom < r.top || k.top >= r.bottom) continue; dist = r.left - k.right; break;\n\t\t\t\tcase _SibXY.Above: if (k.top >= r.top || k.right < r.left || k.left >= r.right) continue; dist = r.top - k.bottom; break;\n\t\t\t\tcase _SibXY.Right: if (k.right <= r.right || k.bottom < r.top || k.top >= r.bottom) continue; dist = k.left - r.right; break;\n\t\t\t\tcase _SibXY.Below: if (k.bottom <= r.bottom || k.right < r.left || k.left >= r.right) continue; dist = k.top - r.bottom; break;\n\t\t\t\t}\n\t\t\t\tdist = Math.Max(dist, 0);\n\t\t\t\tif (dist < nearestDist) {\n\t\t\t\t\tif (c.IsCloaked || c.IsMinimized || c.IsMaximized) continue;\n\t\t\t\t\tif (!desktop.Is0) if (c == desktop || c.ThreadId == desktop.ThreadId) continue;\n\t\t\t\t\tnearestDist = dist;\n\t\t\t\t\tnearest = c;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn nearest;\n\t\t}\n\t}\n}\n\nnamespace Au.Types {\n\t/// <summary>\n\t/// Flags for <see cref=\"wnd.fromXY\"/> and <see cref=\"wnd.fromMouse\"/>.\n\t/// </summary>\n\t[Flags]\n\tpublic enum WXYFlags {\n\t\t/// <summary>\n\t\t/// Need top-level window. If at that point is a control, gets its top-level parent.\n\t\t/// Don't use together with <c>NeedControl</c>.\n\t\t/// </summary>\n\t\tNeedWindow = 1,\n\t\t\n\t\t/// <summary>\n\t\t/// Need a control (child window). Returns <c>default(wnd)</c> if there is no control at that point.\n\t\t/// Don't use together with <c>NeedWindow</c>.\n\t\t/// Without flags <c>NeedWindow</c> and <c>NeedControl</c> the function gets control or top-level window.\n\t\t/// </summary>\n\t\tNeedControl = 2,\n\t\t\n\t\t/// <summary>\n\t\t/// Just call API <ms>WindowFromPhysicalPoint</ms>.\n\t\t/// Faster, but skips disabled controls and in some cases gets transparent control like group box although a smaller visible sibling is there.\n\t\t/// Not used with flag <c>NeedWindow</c>.\n\t\t/// </summary>\n\t\tRaw = 4,\n\t}\n\t\n\t/// <summary>\n\t/// Flags for <see cref=\"wnd.ChildFromXY\"/>.\n\t/// </summary>\n\t[Flags]\n\tpublic enum WXYCFlags {\n\t\t/// <summary>\n\t\t/// If the point is in this window but not in a descendant control, return this. Default - return <c>default(wnd)</c>.\n\t\t/// </summary>\n\t\tOrThis = 1,\n\t\t\n\t\t/// <summary>\n\t\t/// The point is in screen coordinates. Default - client area.\n\t\t/// </summary>\n\t\tScreenXY = 2,\n\t\t\n\t\t/// <summary>\n\t\t/// Must be direct child of this. Default - any descendant.\n\t\t/// </summary>\n\t\tDirectChild = 4,\n\t\t\n\t\t/// <summary>\n\t\t/// If the point is not in client area, don't look for descendants; if with flag <c>OrThis</c> and the point is in this (non-client area), return this, else return <c>default(wnd)</c>.\n\t\t/// </summary>\n\t\tInside = 8,\n\t}\n}\n"
  },
  {
    "path": "Au/wnd/wnd_get.cs",
    "content": "namespace Au;\n\npublic partial struct wnd {\n\t/// <summary>\n\t/// Gets related windows and controls.\n\t/// Use like <c>wnd w2 = w1.Get.Owner;</c> (here <i>w1</i> is a <see cref=\"wnd\"/> variable).\n\t/// </summary>\n\tpublic getwnd Get => new(this);\n\n\t/// <summary>\n\t/// Static functions of this class are used to get special windows (used like <c>wnd w = wnd.getwnd.top;</c>) and all windows.\n\t/// Instances of this class are used to get related windows and controls, like <c>wnd w2 = w1.Get.FirstChild;</c> (here <i>w1</i> is a <see cref=\"wnd\"/> variable).\n\t/// </summary>\n\tpublic partial struct getwnd {\n\t\twnd _w;\n\t\t///\n\t\tpublic getwnd(wnd wThis) => _w = wThis;\n\n\t\t#region instance\n\n\t\t/// <summary>\n\t\t/// Gets nearest visible sibling control to the left from this.\n\t\t/// </summary>\n\t\t/// <returns><c>default(wnd)</c> if not found.</returns>\n\t\t/// <exception cref=\"AuWndException\">This variable is invalid (window not found, closed, etc).</exception>\n\t\t/// <remarks>\n\t\t/// This function is used mostly with controls, but supports top-level windows too. Skips maximized/minimized windows and desktop.\n\t\t/// </remarks>\n\t\tpublic wnd SiblingLeft() => _w._SiblingXY(_SibXY.Left);\n\t\t//FUTURE: add these to the window tool, like navig in Delm. Also add previous/next/parent/child in Z order.\n\n\t\t/// <summary>\n\t\t/// Gets nearest visible sibling control to the right from this.\n\t\t/// </summary>\n\t\t/// <returns><c>default(wnd)</c> if not found.</returns>\n\t\t/// <exception cref=\"AuWndException\">This variable is invalid (window not found, closed, etc).</exception>\n\t\t/// <remarks>\n\t\t/// This function is used mostly with controls, but supports top-level windows too. Skips maximized/minimized windows and desktop.\n\t\t/// </remarks>\n\t\tpublic wnd SiblingRight() => _w._SiblingXY(_SibXY.Right);\n\n\t\t/// <summary>\n\t\t/// Gets nearest visible sibling control above this.\n\t\t/// </summary>\n\t\t/// <returns><c>default(wnd)</c> if not found.</returns>\n\t\t/// <exception cref=\"AuWndException\">This variable is invalid (window not found, closed, etc).</exception>\n\t\t/// <remarks>\n\t\t/// This function is used mostly with controls, but supports top-level windows too. Skips maximized/minimized windows and desktop.\n\t\t/// </remarks>\n\t\tpublic wnd SiblingAbove() => _w._SiblingXY(_SibXY.Above);\n\n\t\t/// <summary>\n\t\t/// Gets nearest visible sibling control to below this.\n\t\t/// </summary>\n\t\t/// <returns><c>default(wnd)</c> if not found.</returns>\n\t\t/// <exception cref=\"AuWndException\">This variable is invalid (window not found, closed, etc).</exception>\n\t\t/// <remarks>\n\t\t/// This function is used mostly with controls, but supports top-level windows too. Skips maximized/minimized windows and desktop.\n\t\t/// </remarks>\n\t\tpublic wnd SiblingBelow() => _w._SiblingXY(_SibXY.Below);\n\n\t\t/// <summary>\n\t\t/// Gets a visible sibling control to the left from this.\n\t\t/// </summary>\n\t\t/// <returns><c>default(wnd)</c> if there is no sibling.</returns>\n\t\t/// <param name=\"distance\">Horizontal distance from the left of this control.</param>\n\t\t/// <param name=\"yOffset\">Vertical offset from the top of this control. If negative - up. Default 5.</param>\n\t\t/// <param name=\"topChild\">If at that point is a visible child of the sibling, get that child. Default <c>false</c>.</param>\n\t\t/// <exception cref=\"AuWndException\">This variable is invalid (window not found, closed, etc).</exception>\n\t\t/// <remarks>\n\t\t/// This function is used mostly with controls, but supports top-level windows too.\n\t\t/// </remarks>\n\t\tpublic wnd SiblingLeft(int distance, int yOffset = 5, bool topChild = false) => _w._SiblingXY(_SibXY.Left, distance, yOffset, topChild);\n\n\t\t/// <summary>\n\t\t/// Gets a visible sibling control to the right from this.\n\t\t/// </summary>\n\t\t/// <returns><c>default(wnd)</c> if there is no sibling.</returns>\n\t\t/// <param name=\"distance\">Horizontal distance from the right of this control.</param>\n\t\t/// <param name=\"yOffset\">Vertical offset from the top of this control. If negative - up. Default 5.</param>\n\t\t/// <param name=\"topChild\">If at that point is a visible child of the sibling, get that child. Default <c>false</c>.</param>\n\t\t/// <exception cref=\"AuWndException\">This variable is invalid (window not found, closed, etc).</exception>\n\t\t/// <remarks>\n\t\t/// This function is used mostly with controls, but supports top-level windows too.\n\t\t/// </remarks>\n\t\tpublic wnd SiblingRight(int distance, int yOffset = 5, bool topChild = false) => _w._SiblingXY(_SibXY.Right, distance, yOffset, topChild);\n\n\t\t/// <summary>\n\t\t/// Gets a visible sibling control above this.\n\t\t/// </summary>\n\t\t/// <returns><c>default(wnd)</c> if there is no sibling.</returns>\n\t\t/// <param name=\"distance\">Vertical distance from the top of this control.</param>\n\t\t/// <param name=\"xOffset\">Horizontal offset from the left of this control. If negative - to the left. Default 5.</param>\n\t\t/// <param name=\"topChild\">If at that point is a visible child of the sibling, get that child. Default <c>false</c>.</param>\n\t\t/// <exception cref=\"AuWndException\">This variable is invalid (window not found, closed, etc).</exception>\n\t\t/// <remarks>\n\t\t/// This function is used mostly with controls, but supports top-level windows too.\n\t\t/// </remarks>\n\t\tpublic wnd SiblingAbove(int distance, int xOffset = 5, bool topChild = false) => _w._SiblingXY(_SibXY.Above, distance, xOffset, topChild);\n\n\t\t/// <summary>\n\t\t/// Gets a visible sibling control below this.\n\t\t/// </summary>\n\t\t/// <returns><c>default(wnd)</c> if there is no sibling.</returns>\n\t\t/// <param name=\"distance\">Vertical distance from the bottom of this control.</param>\n\t\t/// <param name=\"xOffset\">Horizontal offset from the left of this control. If negative - to the left. Default 5.</param>\n\t\t/// <param name=\"topChild\">If at that point is a visible child of the sibling, get that child. Default <c>false</c>.</param>\n\t\t/// <exception cref=\"AuWndException\">This variable is invalid (window not found, closed, etc).</exception>\n\t\t/// <remarks>\n\t\t/// This function is used mostly with controls, but supports top-level windows too.\n\t\t/// </remarks>\n\t\tpublic wnd SiblingBelow(int distance, int xOffset = 5, bool topChild = false) => _w._SiblingXY(_SibXY.Below, distance, xOffset, topChild);\n\n\t\twnd _GetWindow(int dir, int skip) {\n\t\t\tif (skip < 0) return default;\n\t\t\twnd w;\n\t\t\tfor (w = _w; skip >= 0 && !w.Is0; skip--) {\n\t\t\t\tw = Api.GetWindow(w, dir);\n\t\t\t}\n\t\t\treturn w;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets next sibling window or control in the Z order.\n\t\t/// </summary>\n\t\t/// <returns><c>default(wnd)</c> if this is the last or if failed.</returns>\n\t\t/// <param name=\"skip\">How many next windows to skip.</param>\n\t\t/// <remarks>\n\t\t/// If this is a top-level window, gets next top-level window, else gets next control of the same direct parent.\n\t\t/// Calls API <ms>GetWindow</ms>(<c>GW_HWNDNEXT</c>).\n\t\t/// Supports <see cref=\"lastError\"/>.\n\t\t/// </remarks>\n\t\tpublic wnd Next(int skip = 0) => _GetWindow(Api.GW_HWNDNEXT, skip);\n\n\t\t/// <summary>\n\t\t/// Gets previous sibling window or control in the Z order.\n\t\t/// </summary>\n\t\t/// <returns><c>default(wnd)</c> if this is the first or if failed.</returns>\n\t\t/// <param name=\"skip\">How many previous windows to skip.</param>\n\t\t/// <remarks>\n\t\t/// If this is a top-level window, gets previous top-level window, else gets previous control of the same direct parent.\n\t\t/// Calls API <ms>GetWindow</ms>(<c>GW_HWNDPREV</c>).\n\t\t/// Supports <see cref=\"lastError\"/>.\n\t\t/// </remarks>\n\t\tpublic wnd Previous(int skip = 0) => _GetWindow(Api.GW_HWNDPREV, skip);\n\n\t\t/// <summary>\n\t\t/// Gets the first sibling window or control in the Z order.\n\t\t/// If this is the first, returns this.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// If this is a top-level window, gets the first top-level window, else gets the first control of the same direct parent.\n\t\t/// Calls API <ms>GetWindow</ms>(this, <c>GW_HWNDFIRST</c>).\n\t\t/// Supports <see cref=\"lastError\"/>.\n\t\t/// </remarks>\n\t\tpublic wnd FirstSibling => Api.GetWindow(_w, Api.GW_HWNDFIRST);\n\n\t\t/// <summary>\n\t\t/// Gets the last sibling window or control in the Z order.\n\t\t/// If this is the last, returns this, not <c>default(wnd)</c>.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// If this is a top-level window, gets the last top-level window, else gets the last control of the same direct parent.\n\t\t/// Calls API <ms>GetWindow</ms>(this, <c>GW_HWNDLAST</c>).\n\t\t/// Supports <see cref=\"lastError\"/>.\n\t\t/// </remarks>\n\t\tpublic wnd LastSibling => Api.GetWindow(_w, Api.GW_HWNDLAST);\n\n\t\t/// <summary>\n\t\t/// Gets the first direct child control in the Z order.\n\t\t/// </summary>\n\t\t/// <returns><c>default(wnd)</c> if no children or if failed.</returns>\n\t\t/// <remarks>\n\t\t/// Calls API <ms>GetWindow</ms>(<c>GW_CHILD</c>).\n\t\t/// Supports <see cref=\"lastError\"/>.\n\t\t/// </remarks>\n\t\tpublic wnd FirstChild => Api.GetWindow(_w, Api.GW_CHILD);\n\n\t\t/// <summary>\n\t\t/// Gets the last direct child control in the Z order.\n\t\t/// </summary>\n\t\t/// <returns><c>default(wnd)</c> if no children or if failed.</returns>\n\t\t/// <remarks>\n\t\t/// Calls API <ms>GetWindow</ms>.\n\t\t/// Supports <see cref=\"lastError\"/>.\n\t\t/// </remarks>\n\t\tpublic wnd LastChild { get { var t = Api.GetWindow(_w, Api.GW_CHILD); return t.Is0 ? t : Api.GetWindow(t, Api.GW_HWNDLAST); } }\n\n\t\t/// <summary>\n\t\t/// Gets a direct child control by index.\n\t\t/// </summary>\n\t\t/// <returns><c>default(wnd)</c> if no children or if index is invalid or if failed.</returns>\n\t\t/// <param name=\"index\">0-based index of the child control in the Z order.</param>\n\t\t/// <remarks>\n\t\t/// Calls API <ms>GetWindow</ms>.\n\t\t/// Supports <see cref=\"lastError\"/>.\n\t\t/// </remarks>\n\t\tpublic wnd Child(int index) {\n\t\t\tif (index < 0) return default;\n\t\t\twnd c = Api.GetWindow(_w, Api.GW_CHILD);\n\t\t\tfor (; index > 0 && !c.Is0; index--) c = Api.GetWindow(c, Api.GW_HWNDNEXT);\n\t\t\treturn c;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets the owner window of this top-level window.\n\t\t/// </summary>\n\t\t/// <returns><c>default(wnd)</c> if this window isn't owned or if failed.</returns>\n\t\t/// <remarks>\n\t\t/// A window that has an owner window is always on top of it.\n\t\t/// Controls don't have an owner window.\n\t\t/// Supports <see cref=\"lastError\"/>.\n\t\t/// </remarks>\n\t\t/// <seealso cref=\"WndUtil.SetOwnerWindow\"/>\n\t\tpublic wnd Owner => Api.GetWindow(_w, Api.GW_OWNER);\n\n\t\t/// <summary>\n\t\t/// Gets the top-level parent window of this control.\n\t\t/// If this is a top-level window, returns this.\n\t\t/// </summary>\n\t\t/// <returns><c>default(wnd)</c> if failed.</returns>\n\t\t/// <remarks>\n\t\t/// Supports <see cref=\"lastError\"/>.\n\t\t/// This function is the same as <see cref=\"wnd.Window\"/>.\n\t\t/// </remarks>\n\t\tpublic wnd Window => _w.Window;\n\n\t\t/// <summary>\n\t\t/// Gets the direct parent window of this control. It can be the top-level window or another control.\n\t\t/// </summary>\n\t\t/// <returns><c>default(wnd)</c> if this is a top-level window or if failed.</returns>\n\t\t/// <remarks>\n\t\t/// Supports <see cref=\"lastError\"/>.\n\t\t/// Unlike API <ms>GetParent</ms>, this function never returns the owner window.\n\t\t/// </remarks>\n\t\tpublic wnd DirectParent {\n\t\t\tget {\n#if true\n\t\t\t\tvar p = _w.GetWindowLong(GWL.HWNDPARENT);\n\t\t\t\tif (p == default) {\n\t\t\t\t\t//#if DEBUG\n\t\t\t\t\t//var p2 = Api.GetAncestor(_w, Api.GA_PARENT);\n\t\t\t\t\t//Debug.Assert(p2.Is0 || p2 == Root);\n\t\t\t\t\t//#endif\n\t\t\t\t\treturn default;\n\t\t\t\t}\n\t\t\t\tlastError.clear();\n\t\t\t\tvar o = Api.GetWindow(_w, Api.GW_OWNER);\n\t\t\t\tif (o.Is0) {\n\t\t\t\t\tvar ec = lastError.code;\n\t\t\t\t\tif (ec == 0) return (wnd)p;\n\t\t\t\t\tDebug.Assert(ec == Api.ERROR_INVALID_WINDOW_HANDLE);\n\t\t\t\t}\n\t\t\t\treturn default;\n#else //the ms-recommended version, but >=6 times slower. The same IsTopLevelWindow (undocumented API).\n\t\t\t\t\tvar p = Api.GetAncestor(_w, Api.GA_PARENT);\n\t\t\t\t\tif(p.Is0 || p == Root) return default;\n\t\t\t\t\treturn p;\n#endif\n\t\t\t}\n\t\t}\n\n\t\t//rejected. Not much faster than our DirectParent.\n\t\t///// <summary>\n\t\t///// Calls API <ms>GetParent</ms>.\n\t\t///// </summary>\n\t\t///// <remarks>\n\t\t///// The API function is fast but unreliable. It can get parent or owner window, and fails in some cases. Read more in the API documentation. It is reliable only if you know that this window is a child window and has <c>WS_CHILD</c> style.\n\t\t///// Supports <see cref=\"lastError\"/>.\n\t\t///// </remarks>\n\t\t//[Obsolete(\"Unreliable\")]\n\t\t//public wnd GetParentApi => Api.GetParent(_w);\n\n\t\t/// <summary>\n\t\t/// Gets the first (in Z order) enabled window owned by this window.\n\t\t/// </summary>\n\t\t/// <param name=\"orThis\">Return this window if there are no enabled owned windows. If <c>false</c>, then returns <c>default(wnd)</c>.</param>\n\t\t/// <remarks>\n\t\t/// This window should be top-level, not child; see <see cref=\"wnd.Window\"/>.\n\t\t/// \n\t\t/// Calls API <ms>GetWindow</ms>(<c>GW_ENABLEDPOPUP</c>). Supports <see cref=\"lastError\"/>.\n\t\t/// </remarks>\n\t\tpublic wnd EnabledOwned(bool orThis = false) {\n\t\t\tvar r = Api.GetWindow(_w, Api.GW_ENABLEDPOPUP);\n\t\t\tif (orThis) {\n\t\t\t\tif (r.Is0) r = _w;\n\t\t\t} else {\n\t\t\t\tif (r == _w) r = default;\n\t\t\t}\n\t\t\treturn r;\n\t\t\t//MSDN doc says it returns this window if there are no owned, but actually returns 0.\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets the most recently active window in the chain of windows owned by this window, or this window itself if there are no such windows.\n\t\t/// </summary>\n\t\t/// <returns><c>default(wnd)</c> if failed.</returns>\n\t\t/// <param name=\"includeOwners\">Can return an owner (or owner's owner and so on) of this window too.</param>\n\t\t/// <remarks>\n\t\t/// Supports <see cref=\"lastError\"/>.\n\t\t/// </remarks>\n\t\tpublic wnd LastActiveOwnedOrThis(bool includeOwners = false) {\n\t\t\tvar wRoot = RootOwnerOrThis(); //always use the root owner because GetLastActivePopup always returns _w if _w is owned\n\t\t\tvar R = Api.GetLastActivePopup(wRoot);\n\t\t\tif (!includeOwners) {\n\t\t\t\tif (R != _w && wRoot != _w && !R.Is0) {\n\t\t\t\t\tfor (var t = _w; !t.Is0; t = t.Get.Owner) if (t == R) return _w;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn R;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets the bottom-most owner window in the chain of owner windows of this window.\n\t\t/// If this window is not owned, returns this window.\n\t\t/// </summary>\n\t\t/// <returns><c>default(wnd)</c> if failed.</returns>\n\t\t/// <param name=\"supportControls\">If this is a child window, use its top-level parent window instead.</param>\n\t\t/// <remarks>Supports <see cref=\"lastError\"/>.</remarks>\n\t\tpublic wnd RootOwnerOrThis(bool supportControls = false) {\n\t\t\t//return Api.GetAncestor(_w, Api.GA_ROOTOWNER); //slow, and can return Get.Root, eg for combolbox\n\n\t\t\tif (supportControls) {\n\t\t\t\tvar r = Api.GetAncestor(_w, Api.GA_ROOTOWNER);\n\t\t\t\treturn r == root ? _w : r;\n\t\t\t\t//never mind speed, better make it simple\n\t\t\t} else { //fast\n\t\t\t\tfor (wnd r = _w, t; ; r = t) {\n\t\t\t\t\tt = r.Get.Owner;\n\t\t\t\t\tif (t.Is0) return r.IsAlive ? r : default;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets all owner windows (owner, its owner and so on) of this window.\n\t\t/// </summary>\n\t\t/// <param name=\"andThisWindow\">Add this window (or its top-level parent if control) as the first list element.</param>\n\t\t/// <param name=\"onlyVisible\">Skip invisible windows.</param>\n\t\t/// <remarks>\n\t\t/// This window can be top-level window or control.\n\t\t/// </remarks>\n\t\tpublic wnd[] AllOwners(bool andThisWindow = false, bool onlyVisible = false)\n\t\t\t=> Owners(andThisWindow, onlyVisible).ToArray();\n\n\t\t///\n\t\t[EditorBrowsable(EditorBrowsableState.Never)] //returns List, whereas all other public functions return array\n\t\tpublic List<wnd> Owners(bool andThisWindow = false, bool onlyVisible = false) {\n\t\t\tvar a = new List<wnd>();\n\t\t\tfor (var w = Window; !w.Is0 && w != root; w = w.Get.Owner) {\n\t\t\t\tif (!andThisWindow) { andThisWindow = true; continue; }\n\t\t\t\tif (!onlyVisible || w.IsVisible) a.Add(w);\n\t\t\t}\n\t\t\treturn a;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets windows owned by this window.\n\t\t/// </summary>\n\t\t/// <param name=\"allDescendants\">Also get indirectly owned windows (owned by owned windows).</param>\n\t\t/// <param name=\"andThisWindow\">Add this window to the array. Since the array is sorted like in the Z order, usually it is the last element, but not always.</param>\n\t\t/// <returns>Array containing zero or more elements. Sorted like in the Z order.</returns>\n\t\tpublic wnd[] AllOwned(bool allDescendants, bool andThisWindow = false) {\n\t\t\tif (allDescendants) {\n\t\t\t\tvar od = new OwnedDescendants_();\n\t\t\t\tvar ai = od.GetIndices(_w, andOwner: andThisWindow);\n\t\t\t\tif (ai.Count == 0) return [];\n\t\t\t\tvar ar = new wnd[ai.Count];\n\t\t\t\tfor (int i = 0; i < ai.Count; i++) ar[i] = od.all[ai[i]];\n\t\t\t\treturn ar;\n\t\t\t} else {\n\t\t\t\tvar ar = new List<wnd>();\n\t\t\t\tvar all = allWindowsZorder(); //the slowest part\n\t\t\t\tforeach (var k in all) {\n\t\t\t\t\tif (k == _w) { if (andThisWindow) ar.Add(k); continue; }\n\t\t\t\t\tif (k.Get.Owner == _w) ar.Add(k);\n\t\t\t\t}\n\t\t\t\treturn ar.ToArray();\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets indices of owned windows of the specified window, including all descendants.\n\t\t/// </summary>\n\t\tinternal class OwnedDescendants_ {\n\t\t\t/// <summary>\n\t\t\t/// All top-level windows in Z order.\n\t\t\t/// The array is created once in ctor.\n\t\t\t/// </summary>\n\t\t\tpublic readonly wnd[] all;\n\n\t\t\t/// <summary>\n\t\t\t/// Owners of all windows that are in the <i>all</i> array.\n\t\t\t/// The array is created once in ctor.\n\t\t\t/// </summary>\n\t\t\tpublic readonly int[] owners;\n\n\t\t\tpublic OwnedDescendants_() {\n\t\t\t\tall = getwnd.allWindowsZorder();\n\t\t\t\towners = new int[all.Length];\n\t\t\t\tfor (int i = 0; i < all.Length; i++) owners[i] = (int)all[i].Get.Owner;\n\t\t\t}\n\n\t\t\t/// <summary>\n\t\t\t/// Gets indices of owned windows of the specified window, including all descendants.\n\t\t\t/// Can be called multiple times for different owner windows; uses arrays created in ctor (the slowest part).\n\t\t\t/// </summary>\n\t\t\t/// <param name=\"owner\">Owner window.</param>\n\t\t\t/// <param name=\"skip\">A callback function that receives descendant index and can return <c>true</c> to skip that window and its descendants.</param>\n\t\t\t/// <param name=\"andOwner\">Add <i>owner</i> to the list too, at the position matching the Z order.</param>\n\t\t\t/// <returns>List of <see cref=\"all\"/> indices of owned windows. Sorted like in the Z order. Not <c>null</c>.</returns>\n\t\t\tpublic List<int> GetIndices(wnd owner, Func<int, bool> skip = null, bool andOwner = false) {\n\t\t\t\tvar ai = new List<int>();\n\t\t\t\t_Owned(owner);\n\n\t\t\t\tvoid _Owned(wnd owner) {\n\t\t\t\t\tint oint = (int)owner;\n\t\t\t\t\tfor (int i = 0; i < owners.Length; i++) {\n\t\t\t\t\t\tif (owners[i] == oint) {\n\t\t\t\t\t\t\tif (skip != null && skip(i)) continue;\n\t\t\t\t\t\t\tai.Add(i);\n\t\t\t\t\t\t\t_Owned(all[i]);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (andOwner) {\n\t\t\t\t\tint j = Array.IndexOf(all, owner);\n\t\t\t\t\tif (j >= 0) ai.Add(j); else Debug_.Print(\"owner not in all\");\n\t\t\t\t}\n\t\t\t\tai.Sort();\n\t\t\t\treturn ai;\n\t\t\t}\n\t\t}\n\n\t\t#endregion\n\n\t\t#region static\n\n\t\t/// <summary>\n\t\t/// Gets the first top-level window in the Z order.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Probably it is a topmost window. To get the first non-topmost window, use <see cref=\"top2\"/>.\n\t\t/// Calls API <ms>GetTopWindow</ms>.\n\t\t/// </remarks>\n\t\tpublic static wnd top => Api.GetTopWindow(default);\n\n\t\t/// <summary>\n\t\t/// Finds and returns the first non-topmost window in the Z order.\n\t\t/// </summary>\n\t\t/// <param name=\"lastTopmost\">Receives the last topmost window.</param>\n\t\t/// <remarks>\n\t\t/// This function is slower than <see cref=\"top\"/> etc. Enumerates windows, because there is no API to get directly.\n\t\t/// </remarks>\n\t\tpublic static wnd top2(out wnd lastTopmost) {\n\t\t\twnd w, lastTM, w2 = default, lastTM2 = default;\n\t\t\tfor (; ; ) { //repeat until gets same results 2 times. At any time windows can be destroyed, reordered, reparented. The second time 2-3 times faster.\n\t\t\t\tlastTM = default; w = top; if (w.Is0) break; //no windows in this session\n\t\t\t\tfor (; w.IsTopmost; w = w.Get.Next()) lastTM = w;\n\t\t\t\tif (w == w2 && lastTM == lastTM2 && !w.Is0) break;\n\t\t\t\tw2 = w; lastTM2 = lastTM;\n\t\t\t}\n\t\t\tlastTopmost = lastTM;\n\t\t\treturn w;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Calls API <ms>GetDesktopWindow</ms>. It gets the virtual parent window of all top-level windows.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// <note>It is not the desktop window (see <see cref=\"desktop\"/>) that displays icons and wallpaper.</note>\n\t\t/// </remarks>\n\t\tpublic static wnd root { get; } = Api.GetDesktopWindow();\n\n\t\t/// <summary>\n\t\t/// Calls API <ms>GetShellWindow</ms>. It gets a window of the shell process (usually process <c>\"explorer\"</c>, class name <c>\"Progman\"</c>).\n\t\t/// </summary>\n\t\t/// <returns><c>default(wnd)</c> if there is no shell process, for example Explorer process killed/crashed and still not restarted, or if using a custom shell that does not register a shell window.</returns>\n\t\t/// <remarks>\n\t\t/// It can be the window that contains desktop icons (see <see cref=\"desktop\"/>) or other window of the same thread.\n\t\t/// </remarks>\n\t\tpublic static wnd shellWindow => Api.GetShellWindow();\n\n\t\t/// <summary>\n\t\t/// Gets the desktop window and its child control that displays desktop icons and wallpaper.\n\t\t/// </summary>\n\t\t/// <returns><c>false</c> if failed.</returns>\n\t\t/// <param name=\"desktopWindow\">Receives the top-level desktop window. Class name <c>\"Progman\"</c> or <c>\"WorkerW\"</c>.</param>\n\t\t/// <param name=\"control\">Receives the control of <c>\"SysListView32\"</c> class that contains icons and wallpaper.</param>\n\t\t/// <remarks>\n\t\t/// This function is not very reliable. May stop working on a new Windows version or don't work with a custom shell.\n\t\t/// Fails if there is no shell process, for example Explorer process killed/crashed and still not restarted, or if using a custom shell that does not register a shell window.\n\t\t/// </remarks>\n\t\tpublic static bool desktop(out wnd desktopWindow, out wnd control) {\n\t\t\twnd w = shellWindow;\n\t\t\tvar f = new wndChildFinder(cn: \"SysListView32\");\n\t\t\tif (!f.Exists(w)) w = wnd.find(null, \"WorkerW\", WOwner.Thread(w.ThreadId), also: t => f.Exists(t));\n\t\t\tdesktopWindow = w;\n\t\t\tcontrol = f.Result;\n\t\t\treturn !w.Is0;\n\n\t\t\t//info:\n\t\t\t//If no wallpaper, desktop is GetShellWindow, else a visible WorkerW window.\n\t\t\t//When was no wallpaper and user selects a wallpaper, explorer creates WorkerW and moves the same SysListView32 control to it.\n\n\t\t\t//tested: with COM (IShellWindows -> IShellBrowser -> IShellView.GetWindow) slower 17 times.\n\t\t}\n\n\t\t//FUTURE:\n\t\t//public static wnd editorWindow =>\n\t\t//public static wnd editorCodeControl =>\n\n\t\t#endregion\n\t}\n\n\t#region main windows\n\n\tpublic partial struct getwnd {\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if window <i>w</i> is considered a main window, ie probably is in the Windows taskbar.\n\t\t/// Returns <c>false</c> if it is invisible, cloaked, owned, toolwindow, menu, etc.\n\t\t/// </summary>\n\t\t/// <param name=\"w\">A top-level window.</param>\n\t\t/// <param name=\"allDesktops\">On Windows 10 include (return <c>true</c> for) windows on all virtual desktops. On Windows 8 include Windows Store apps if possible; read more: <see cref=\"allWindows(bool, bool)\"/>.</param>\n\t\t/// <param name=\"skipMinimized\">Return <c>false</c> if <i>w</i> is minimized.</param>\n\t\tpublic static bool isMainWindow(wnd w, bool allDesktops = false, bool skipMinimized = false) {\n\t\t\tif (!w.IsVisible) return false;\n\n\t\t\tvar exStyle = w.ExStyle;\n\t\t\tif ((exStyle & WSE.APPWINDOW) == 0) {\n\t\t\t\tif ((exStyle & (WSE.TOOLWINDOW | WSE.NOACTIVATE)) != 0) return false;\n\t\t\t\tif (!w.Get.Owner.Is0) return false;\n\t\t\t}\n\n\t\t\tif (skipMinimized && w.IsMinimized) return false;\n\n\t\t\tif (osVersion.minWin10) {\n\t\t\t\tif (w.IsCloaked) {\n\t\t\t\t\tif (!allDesktops) return false;\n\t\t\t\t\tif ((exStyle & WSE.NOREDIRECTIONBITMAP) != 0) { //probably a store app\n\t\t\t\t\t\tswitch (w.ClassNameIs(\"Windows.UI.Core.CoreWindow\", \"ApplicationFrameWindow\")) {\n\t\t\t\t\t\tcase 1: return false; //Windows search, experience host, etc. Also app windows that normally would sit on ApplicationFrameWindow windows.\n\t\t\t\t\t\tcase 2: if (_WindowsStoreAppFrameChild(w).Is0) return false; break; //skip hosts\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (osVersion.minWin8) {\n\t\t\t\tif ((exStyle & WSE.NOREDIRECTIONBITMAP) != 0 && !w.HasStyle(WS.CAPTION)) {\n\t\t\t\t\tif (!allDesktops && (exStyle & WSE.TOPMOST) != 0) return false; //skip store apps\n\t\t\t\t\tif (shellWindow.GetThreadProcessId(out var pidShell) != 0 && w.GetThreadProcessId(out var pid) != 0 && pid == pidShell) return false; //skip shell windows with no title bar\n\t\t\t\t}\n\t\t\t\t//On Win8 impossible to get next window like Alt+Tab.\n\t\t\t\t//\tAll store apps are topmost, covering non-topmost desktop windows.\n\t\t\t\t//\tDwmGetWindowAttribute makes no sense here.\n\t\t\t\t//\tDesktop windows are never cloaked, inactive store windows are cloaked, etc.\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets main windows, ie those that probably are in the Windows taskbar.\n\t\t/// </summary>\n\t\t/// <returns>Array containing zero or more <see cref=\"wnd\"/>.</returns>\n\t\t/// <param name=\"allDesktops\">On Windows 10 include windows on all virtual desktops. On Windows 8 include Windows Store apps if possible; read more: <see cref=\"allWindows(bool, bool)\"/>.</param>\n\t\t/// <remarks>\n\t\t/// Uses <see cref=\"isMainWindow\"/>.\n\t\t/// Does not match the order of buttons in the Windows taskbar.\n\t\t/// </remarks>\n\t\tpublic static wnd[] mainWindows(bool allDesktops = false) {\n\t\t\tvar a = new List<wnd>();\n\t\t\tforeach (var w in allWindows(onlyVisible: true)) {\n\t\t\t\tif (isMainWindow(w, allDesktops: allDesktops)) a.Add(w);\n\t\t\t}\n\t\t\treturn a.ToArray();\n\n\t\t\t//Another way - UI Automation:\n\t\t\t//\tvar x = new CUIAutomation8();\n\t\t\t//\tvar cond = x.CreatePropertyCondition(30003, 0xC370); //UIA_ControlTypePropertyId, UIA_WindowControlTypeId\n\t\t\t//\tvar a = x.GetRootElement().FindAll(TreeScope.TreeScope_Children, cond);\n\t\t\t//\tfor(int i = 0; i < a.Length; i++) print.it((wnd)a.GetElement(i).CurrentNativeWindowHandle);\n\t\t\t//Advantages: 1. Maybe can filter unwanted windows more reliably, although I did't notice a difference.\n\t\t\t//Disadvantages: 1. Skips windows of higher integrity level (UAC). 2. Cannot include cloaked windows, eg those in inactive Win10 virtual desktops. 3. About 1000 times slower, eg 70 ms vs 70 mcs; cold 140 ms.\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets next window in the Z order, skipping invisible and other windows that probably are not in the Windows taskbar.\n\t\t/// </summary>\n\t\t/// <returns><c>default(wnd)</c> if there are no such windows.</returns>\n\t\t/// <param name=\"w\">Start from this window. If <c>default(wnd)</c>, starts from the top of the Z order.</param>\n\t\t/// <param name=\"allDesktops\">On Windows 10 include windows on all virtual desktops. On Windows 8 include Windows Store apps if possible; read more: <see cref=\"allWindows(bool, bool)\"/>.</param>\n\t\t/// <param name=\"skipMinimized\">Skip minimized windows.</param>\n\t\t/// <param name=\"retryFromTop\">If <i>w</i> is not <c>default(wnd)</c> and there are no matching windows after it, retry from the top of the Z order. Then can return <i>w</i>.</param>\n\t\t/// <remarks>\n\t\t/// Uses <see cref=\"isMainWindow\"/>.\n\t\t/// This function is quite slow. Does not match the order of buttons in the Windows taskbar.\n\t\t/// </remarks>\n\t\tpublic static wnd nextMain(wnd w = default, bool allDesktops = false, bool skipMinimized = false, bool retryFromTop = false) {\n\t\t\tif (w.Is0) retryFromTop = false;\n\n\t\t\tfor (; ; ) {\n\t\t\t\tw = w.Is0 ? getwnd.top : w.Get.Next();\n\t\t\t\tif (w.Is0) {\n\t\t\t\t\tif (retryFromTop) { retryFromTop = false; continue; }\n\t\t\t\t\treturn default;\n\t\t\t\t}\n\t\t\t\tif (isMainWindow(w, allDesktops: allDesktops, skipMinimized: skipMinimized)) return w;\n\t\t\t}\n\t\t}\n\t}\n\n\t/// <summary>\n\t/// Activates next non-minimized main window, like with <c>Alt+Tab</c>.\n\t/// </summary>\n\t/// <returns><c>true</c> if activated; <c>false</c> if there is no such window or if failed to activate.</returns>\n\t/// <remarks>\n\t/// Uses <see cref=\"getwnd.nextMain\"/>, <see cref=\"getwnd.LastActiveOwnedOrThis\"/>, <see cref=\"Activate\"/>.\n\t/// An alternative way - send <c>Alt+Tab</c> keys, but it works not everywhere.\n\t/// </remarks>\n\tpublic static bool switchActiveWindow() {\n\t\ttry {\n\t\t\twnd wActive = active, wRO = wActive.Get.RootOwnerOrThis();\n\t\t\twnd wMain = getwnd.nextMain(wRO, skipMinimized: true, retryFromTop: true);\n\t\t\tif (!wMain.Is0 && wMain != wActive && wMain != wRO) {\n\t\t\t\tvar wMainOrOwned = wMain.Get.LastActiveOwnedOrThis();\n\t\t\t\tif (!wMainOrOwned.Is0) {\n\t\t\t\t\t//print.it(wMainOrOwned);\n\t\t\t\t\twMainOrOwned.Activate();\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcatch (AuWndException) { }\n\t\treturn false;\n\n\t\t//notes:\n\t\t//This function ignores minimized windows, because:\n\t\t//\tImpossible to get exactly the same window as Alt+Tab, which on Win10 is the most recently minimized.\n\t\t//\tActivating a random minimized window is not very useful. Maybe in the future.\n\t\t//The order of windows used by Alt+Tab is not the same as the Z order, especially when there are minimized windows.\n\t\t//\tWe cannot get that order and have to use the Z order. It seems that the order is different on Win10 but not on Win7.\n\t\t//After minimizing a window its position in the Z order is undefined.\n\t\t//\tMost windows then are at the bottom when used the Minimize button or ShowWindow etc.\n\t\t//\tBut some are at the top, just after the active window, for example MS Document Explorer (Win7 SDK).\n\t\t//\tAlso at the top when minimized with the taskbar button.\n\t}\n\n\t#endregion\n\n\t/// <summary>\n\t/// Gets the top-level parent window of this control.\n\t/// If this is a top-level window, returns this.\n\t/// </summary>\n\t/// <returns><c>default(wnd)</c> if this window is invalid.</returns>\n\t/// <remarks>Supports <see cref=\"lastError\"/>.</remarks>\n\t/// <seealso cref=\"Get\"/>\n\tpublic wnd Window {\n\t\tget {\n\t\t\tvar w = Api.GetAncestor(this, Api.GA_ROOT);\n\t\t\tif (w.Is0 && this == getwnd.root) w = this;\n\t\t\treturn w;\n\t\t}\n\t}\n\t//This is in getwnd and here, because frequently used.\n\n\t/// <summary>\n\t/// Returns <c>true</c> if this is a child window (control), <c>false</c> if top-level window.\n\t/// </summary>\n\t/// <remarks>\n\t/// Supports <see cref=\"lastError\"/>.\n\t/// Another way is <c>w.HasStyle(WS.CHILD)</c>. It is faster but less reliable, because some top-level windows have <c>WS_CHILD</c> style and some child windows don't.\n\t/// </remarks>\n\t/// <seealso cref=\"getwnd.DirectParent\"/>\n\tpublic bool IsChild => !Get.DirectParent.Is0;\n\n\t/// <summary>\n\t/// Returns <c>true</c> if this is a child or descendant of window <i>w</i>.\n\t/// </summary>\n\t/// <remarks>\n\t/// Calls API <ms>IsChild</ms>.\n\t/// Supports <see cref=\"lastError\"/>.\n\t/// </remarks>\n\tpublic bool IsChildOf(wnd w) { return Api.IsChild(w, this); }\n\n\t/// <summary>\n\t/// Returns <c>(wnd)GetWindowLong(GWL.HWNDPARENT)</c>.\n\t/// </summary>\n\tinternal wnd ParentGWL_ => (wnd)GetWindowLong(GWL.HWNDPARENT);\n\n\t/// <summary>\n\t/// Gets the active (foreground) window.\n\t/// Calls API <ms>GetForegroundWindow</ms>.\n\t/// </summary>\n\t/// <value>Returns <c>default(wnd)</c> if there is no active window; more info: <see cref=\"WndUtil.WaitForAnActiveWindow\"/>.</value>\n\tpublic static wnd active => Api.GetForegroundWindow();\n\n\t/// <summary>\n\t/// Returns <c>true</c> if this window is the active (foreground) window.\n\t/// </summary>\n\tpublic bool IsActive => !Is0 && this == Api.GetForegroundWindow();\n\n\t//FUTURE: static bool IsActiveAny(list of wnd or wndFinder).\n\n\t/// <summary>\n\t/// Returns <c>true</c> if this window is the active (foreground) window.\n\t/// If this is <see cref=\"getwnd.root\"/>, returns <c>true</c> if there is no active window.\n\t/// </summary>\n\tinternal bool IsActiveOrNoActiveAndThisIsWndRoot_ {\n\t\tget {\n\t\t\tif (Is0) return false;\n\t\t\tvar f = Api.GetForegroundWindow();\n\t\t\treturn this == (f.Is0 ? getwnd.root : f);\n\t\t}\n\t}\n\n\t/// <summary>\n\t/// Returns <c>true</c> if this window is directly or indirectly owned by window <i>w</i>.\n\t/// </summary>\n\t/// <param name=\"w\"></param>\n\t/// <param name=\"level\">\n\t/// Return <c>true</c> if:\n\t/// <br/>• 0 - <i>w</i> is direct owner. See <see cref=\"getwnd.Owner\"/>.\n\t/// <br/>• 1 - <i>w</i> is direct or indirect owner (eg owner's owner).\n\t/// <br/>• 2 - <i>w</i> is direct or indirect owner, or this is a menu-like window that looks like owned by <i>w</i> (same thread, topmost, etc).\n\t/// </param>\n\t/// <remarks>\n\t/// Many popup menus and similar temporary windows don't have an owner window. Instead they have topmost style. If <i>level</i> is 2, this function tries to detect this case. In most cases it works, but not always (depends on styles).\n\t/// </remarks>\n\tpublic bool IsOwnedBy(wnd w, int level) {\n\t\tif ((uint)level > 2) throw new ArgumentOutOfRangeException(nameof(level));\n\t\tint tid = 0;\n\t\treturn IsOwnedBy2_(w, level, ref tid);\n\t}\n\n\t/// <summary>\n\t/// This version is used by \"find window\" functions.\n\t/// Uses <i>tid</i> to avoid getting <i>w</i> thread id (slow) for each window; let it be 0 initially.\n\t/// </summary>\n\tinternal bool IsOwnedBy2_(wnd w, int level, ref int tid) {\n\t\tfor (var o = Get.Owner; !o.Is0; o = o.Get.Owner) {\n\t\t\tif (o == w) return true;\n\t\t\tif (level == 0) break;\n\t\t}\n\n\t\tif (level > 1) {\n\t\t\t//print.it(\"level>1\");\n\t\t\tvar es = ExStyle;\n\t\t\tif (es.Has(WSE.TOPMOST) && es.HasAny(WSE.TOOLWINDOW | WSE.NOACTIVATE) && !es.Has(WSE.APPWINDOW)) {\n\t\t\t\tvar s = Style;\n\t\t\t\tif (!s.Has(WS.CAPTION) && !s.Has(WS.THICKFRAME))\n\t\t\t\t\tswitch (s & (WS.CHILD | WS.POPUP)) {\n\t\t\t\t\tcase WS.POPUP:\n\t\t\t\t\tcase WS.CHILD when !w.IsChild: //ComboLBox\n\t\t\t\t\t\tif (tid == 0) tid = w.ThreadId; //much slower than style/exstyle\n\t\t\t\t\t\tif (ThreadId == tid)\n\t\t\t\t\t\t\treturn !w.IsTopmost || ZorderIsAbove(w);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n}\n"
  },
  {
    "path": "Au/wnd/wnd_other.cs",
    "content": "namespace Au {\n\tpublic partial struct wnd {\n\t\t/// <summary>\n\t\t/// Sets window transparency attributes (opacity and/or transparent color).\n\t\t/// </summary>\n\t\t/// <param name=\"allowTransparency\">Set or remove <ms>WS_EX_LAYERED</ms> style that is required for transparency. If <c>false</c>, other parameters are not used.</param>\n\t\t/// <param name=\"opacity\">Opacity from 0 (completely transparent) to 255 (opaque). Does not change if <c>null</c>. If less than 0 or greater than 255, makes 0 or 255.</param>\n\t\t/// <param name=\"colorKey\">Make pixels of this color completely transparent. Does not change if <c>null</c>. The alpha byte is not used.</param>\n\t\t/// <param name=\"noException\">Don't throw exception when fails.</param>\n\t\t/// <exception cref=\"AuWndException\"/>\n\t\t/// <remarks>\n\t\t/// Uses API <ms>SetLayeredWindowAttributes</ms>.\n\t\t/// On Windows 7 works only with top-level windows, not with controls.\n\t\t/// Fails with WPF windows (class name starts with <c>\"HwndWrapper\"</c>).\n\t\t/// </remarks>\n\t\tpublic void SetTransparency(bool allowTransparency, int? opacity = null, ColorInt? colorKey = null, bool noException = false) {\n\t\t\tvar est = ExStyle;\n\t\t\tbool layered = est.Has(WSE.LAYERED);\n\t\t\t\n\t\t\tif (allowTransparency) {\n\t\t\t\tuint col = 0, f = 0; byte op = 0;\n\t\t\t\tif (colorKey != null) { f |= 1; col = (uint)colorKey.Value.ToBGR(); }\n\t\t\t\tif (opacity != null) { f |= 2; op = (byte)Math.Clamp(opacity.Value, 0, 255); }\n\t\t\t\t\n\t\t\t\tif (!layered) SetExStyle(est | WSE.LAYERED, noException ? WSFlags.NoException : 0);\n\t\t\t\tif (!Api.SetLayeredWindowAttributes(this, col, op, f) && !noException) ThrowUseNative();\n\t\t\t} else if (layered) {\n\t\t\t\tSetExStyle(est & ~WSE.LAYERED, noException ? WSFlags.NoException : 0);\n\t\t\t\t//tested: resets attributes, ie after adding WSE.LAYERED the window will be normal\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets window transparency attributes (opacity and transparency color).\n\t\t/// </summary>\n\t\t/// <param name=\"opacity\">If this function returns <c>true</c> and the window has an opacity attribute, receives the opacity value 0-255, else <c>null</c>.</param>\n\t\t/// <param name=\"colorKey\">If this function returns <c>true</c> and the window has a transparency color attribute, receives the color, else <c>null</c>.</param>\n\t\t/// <returns>True if the window has transparency attributes set with <see cref=\"SetTransparency\"/> or API <ms>SetLayeredWindowAttributes</ms>. Supports <see cref=\"lastError\"/>.</returns>\n\t\t/// <remarks>\n\t\t/// Uses API <ms>GetLayeredWindowAttributes</ms>.\n\t\t/// </remarks>\n\t\tpublic bool GetTransparency(out int? opacity, out ColorInt? colorKey) {\n\t\t\topacity = default; colorKey = default;\n\t\t\tif (/*ExStyle.Has(WSE.LAYERED) && */Api.GetLayeredWindowAttributes(this, out uint col, out byte op, out uint f)) {\n\t\t\t\tif (0 != (f & 1)) colorKey = col;\n\t\t\t\tif (0 != (f & 2)) opacity = op;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if this is a full-screen window and not desktop.\n\t\t/// </summary>\n\t\tpublic bool IsFullScreen => IsFullScreen_(out _);\n\t\t\n\t\tinternal unsafe bool IsFullScreen_(out screen scrn) {\n\t\t\tscrn = default;\n\t\t\tif (Is0) return false;\n\t\t\t\n\t\t\t//is client rect equal to window rect (no border)?\n\t\t\tRECT r, rc, rm;\n\t\t\tr = Rect; //fast\n\t\t\tint cx = r.right - r.left, cy = r.bottom - r.top;\n\t\t\tif (cx < 400 || cy < 300) return false; //too small\n\t\t\trc = ClientRect; //fast\n\t\t\tif (rc.right != cx || rc.bottom != cy) {\n\t\t\t\tif (cx - rc.right > 2 || cy - rc.bottom > 2) return false; //some windows have 1-pixel border\n\t\t\t}\n\t\t\t\n\t\t\t//covers whole screen rect?\n\t\t\tscrn = screen.of(this, SODefault.Zero); if (scrn.IsEmpty) return false;\n\t\t\trm = scrn.Rect;\n\t\t\t\n\t\t\tif (r.left > rm.left || r.top > rm.top || r.right < rm.right || r.bottom < rm.bottom - 1) return false; //info: -1 for inactive Chrome\n\t\t\t\n\t\t\t//is it desktop?\n\t\t\tif (IsOfShellThread_) return false;\n\t\t\tif (this == getwnd.root) return false;\n\t\t\t\n\t\t\treturn true;\n\t\t\t\n\t\t\t//This is the best way to test for fullscreen (FS) window. Fast.\n\t\t\t//Window and client rect was equal of almost all my tested FS windows. Except Winamp visualization.\n\t\t\t//Most FS windows are same size as screen, but some slightly bigger.\n\t\t\t//Don't look at window styles. For some FS windows they are not as should be.\n\t\t\t//Returns false if the active window is owned by a fullscreen window. This is different than appbar API interprets it. It's OK for our purposes.\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if this belongs to <c>GetShellWindow</c>'s thread (usually it is the desktop window).\n\t\t/// </summary>\n\t\tinternal bool IsOfShellThread_ {\n\t\t\tget => 1 == s_isShellWindow.IsShellWindow(this);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if this belongs to <c>GetShellWindow</c>'s process (eg a folder window, desktop, taskbar).\n\t\t/// </summary>\n\t\tinternal bool IsOfShellProcess_ {\n\t\t\tget => 0 != s_isShellWindow.IsShellWindow(this);\n\t\t}\n\t\t\n\t\tstruct _ISSHELLWINDOW {\n\t\t\tint _tidW, _tidD, _pidW, _pidD;\n\t\t\tIntPtr _w, _wDesk; //not wnd because then TypeLoadException\n\t\t\t\n\t\t\tpublic int IsShellWindow(wnd w) {\n\t\t\t\tif (w.Is0) return 0;\n\t\t\t\twnd wDesk = getwnd.shellWindow; //fast\n\t\t\t\tif (w == wDesk) return 1; //Progman. Other window (WorkerW) may be active when desktop active.\n\t\t\t\t\n\t\t\t\t//cache because GetWindowThreadProcessId quite slow\n\t\t\t\tif (w.Handle != _w) { _w = w.Handle; _tidW = w.GetThreadProcessId(out _pidW); }\n\t\t\t\tif (wDesk.Handle != _wDesk) { _wDesk = wDesk.Handle; _tidD = wDesk.GetThreadProcessId(out _pidD); }\n\t\t\t\t\n\t\t\t\tif (_tidW == _tidD) return 1;\n\t\t\t\tif (_pidW == _pidD) return 2;\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t}\n\t\tstatic _ISSHELLWINDOW s_isShellWindow;\n\t\t\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if this window has Metro style, ie is not a classic desktop window.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// On Windows 8/8.1 most Windows Store app windows and many shell windows have Metro style.\n\t\t/// On Windows 10 few windows have Metro style.\n\t\t/// On Windows 7 there are no Metro style windows.\n\t\t/// </remarks>\n\t\t/// <seealso cref=\"WndUtil.GetWindowsStoreAppId\"/>\n\t\tpublic bool IsWindows8MetroStyle {\n\t\t\tget {\n\t\t\t\tif (!osVersion.minWin8) return false;\n\t\t\t\tif (!HasExStyle(WSE.TOPMOST | WSE.NOREDIRECTIONBITMAP) || (Style & WS.CAPTION) != 0) return false;\n\t\t\t\tif (ClassNameIs(\"Windows.UI.Core.CoreWindow\")) return true;\n\t\t\t\tif (!osVersion.minWin10 && IsOfShellProcess_) return true;\n\t\t\t\treturn false;\n\t\t\t\t//could use IsImmersiveProcess, but this is better\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// On Windows 10 and later returns non-zero if this top-level window is a UWP app window: 1 if class name is <c>\"ApplicationFrameWindow\"</c>, 2 if <c>\"Windows.UI.Core.CoreWindow\"</c>.\n\t\t/// </summary>\n\t\t/// <seealso cref=\"WndUtil.GetWindowsStoreAppId\"/>\n\t\tpublic int IsUwpApp {\n\t\t\tget {\n\t\t\t\tif (!osVersion.minWin10) return 0;\n\t\t\t\tif (!HasExStyle(WSE.NOREDIRECTIONBITMAP)) return 0;\n\t\t\t\treturn ClassNameIs(\"ApplicationFrameWindow\", \"Windows.UI.Core.CoreWindow\");\n\t\t\t\t//could use IsImmersiveProcess, but this is better\n\t\t\t}\n\t\t}\n\t\t\n\t\t//This is too litle tested to be public. Tested only with WinUI 3 Controls Gallery. Also WinUI3 is still kinda experimental and rare (2021).\n\t\t/// <summary>\n\t\t/// On Windows 10 and later returns <c>true</c> if this top-level window is a WinUI app window (class name <c>\"WinUIDesktopWin32WindowClass\"</c>).\n\t\t/// </summary>\n\t\tinternal bool IsWinUI_ {\n\t\t\tget {\n\t\t\t\tif (!osVersion.minWin10) return false;\n\t\t\t\t//if (!HasExStyle(WSE.NOREDIRECTIONBITMAP)) return 0; //only the control (cn \"Microsoft.UI.Content.ContentWindowSiteBridge\") has this style\n\t\t\t\treturn ClassNameIs(\"WinUIDesktopWin32WindowClass\");\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// If this control is (or is based on) a standard control provided by Windows, such as button or treeview, returns the control type. Else returns <c>None</c>.\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t/// Sends message <c>WM_GETOBJECT</c> <ms>QUERYCLASSNAMEIDX</ms>. Slower than <see cref=\"ClassName\"/> or <see cref=\"ClassNameIs(string)\"/>, but can detect the base type of controls based on standard Windows controls but with a different class name.\n\t\t/// </remarks>\n\t\tpublic WControlType CommonControlType => (WControlType)Send(Api.WM_GETOBJECT, 0, (nint)EObjid.QUERYCLASSNAMEIDX);\n\t}\n}\n\nnamespace Au.Types {\n\t/// <summary>\n\t/// Flags for <see cref=\"wnd.SetStyle\"/> and <see cref=\"wnd.SetExStyle\"/>.\n\t/// </summary>\n\t[Flags]\n\tpublic enum WSFlags {\n\t\t/// <summary>Add the specified styles and don't change others.</summary>\n\t\tAdd = 1,\n\t\t\n\t\t/// <summary>Remove the specified styles and don't change others.</summary>\n\t\tRemove = 2,\n\t\t\n\t\t/// <summary>Update non-client area (frame, title bar).</summary>\n\t\tUpdateNonclient = 4,\n\t\t\n\t\t/// <summary>Update client area.</summary>\n\t\tUpdateClient = 8,\n\t\t\n\t\t/// <summary>Don't throw exception when fails.</summary>\n\t\tNoException = 16,\n\t}\n\t\n\t/// <summary>\n\t/// <see cref=\"wnd.CommonControlType\"/>\n\t/// </summary>\n\tpublic enum WControlType {\n#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member\n\t\tNone,\n\t\tListbox = 65536,\n\t\tButton = 65536 + 2,\n\t\tStatic,\n\t\tEdit,\n\t\tCombobox,\n\t\tScrollbar = 65536 + 10,\n\t\tStatus,\n\t\tToolbar,\n\t\tProgress,\n\t\tAnimate,\n\t\tTab,\n\t\tHotkey,\n\t\tHeader,\n\t\tTrackbar,\n\t\tListview,\n\t\tUpdown = 65536 + 22,\n\t\tToolTips = 65536 + 24,\n\t\tTreeview,\n\t\tRichEdit = 65536 + 28\n#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member\n\t}\n}"
  },
  {
    "path": "Au/wnd/wnd_private.cs",
    "content": "namespace Au;\n\npublic unsafe partial struct wnd {\n\t/// <summary>\n\t/// <c>if(!IsOfThisThread) { Thread.Sleep(15); SendTimeout(1000, 0); }</c>\n\t/// </summary>\n\tinternal void MinimalSleepIfOtherThread_() {\n\t\tif (!IsOfThisThread) MinimalSleepNoCheckThread_();\n\t}\n\t\n\t/// <summary>\n\t/// <c>Thread.Sleep(15); SendTimeout(1000, 0);</c>\n\t/// </summary>\n\tinternal void MinimalSleepNoCheckThread_() {\n\t\tDebug.Assert(!IsOfThisThread);\n\t\t//perf.first();\n\t\tThread.Sleep(15);\n\t\tSendTimeout(1000, out _, 0);\n\t\t//perf.nw();\n\t}\n\t\n\t/// <summary>\n\t/// On Win10+, if <i>w</i> is <c>\"ApplicationFrameWindow\"</c>, returns the real app window <c>\"Windows.UI.Core.CoreWindow\"</c> hosted by <i>w</i>.\n\t/// If <i>w</i> is minimized, cloaked (eg on other desktop) or the app is starting, the <c>\"Windows.UI.Core.CoreWindow\"</c> is not its child. Then searches for a top-level window named like <i>w</i>. It is unreliable, but no API for this.\n\t/// Info: <c>\"Windows.UI.Core.CoreWindow\"</c> windows hosted by <c>\"ApplicationFrameWindow\"</c> belong to separate processes. All <c>\"ApplicationFrameWindow\"</c> windows belong to a single process.\n\t/// </summary>\n\tstatic wnd _WindowsStoreAppFrameChild(wnd w) {\n\t\tbool retry = false;\n\t\tstring name;\n\t\tg1:\n\t\tif (!osVersion.minWin10 || !w.ClassNameIs(\"ApplicationFrameWindow\")) return default;\n\t\twnd c = Api.FindWindowEx(w, default, \"Windows.UI.Core.CoreWindow\", null);\n\t\tif (!c.Is0) return c;\n\t\tif (retry) return default;\n\t\t\n\t\tname = w.NameTL_; if (name.NE()) return default;\n\t\t\n\t\tfor (; ; ) {\n\t\t\tc = Api.FindWindowEx(default, c, \"Windows.UI.Core.CoreWindow\", name); //I could not find API for it\n\t\t\tif (c.Is0) break;\n\t\t\tif (c.IsCloaked) return c; //else probably it is an unrelated window\n\t\t}\n\t\t\n\t\tretry = true;\n\t\tgoto g1;\n\t}\n\t\n\t//not used\n\t///// <summary>\n\t///// The reverse of <c>_WindowsStoreAppFrameChild</c>.\n\t///// </summary>\n\t//static wnd _WindowsStoreAppHost(wnd w)\n\t//{\n\t//\tif(!osVersion.minWin10 || !w.ClassNameIs(\"Windows.UI.Core.CoreWindow\")) return default;\n\t//\twnd wo = w.Get.DirectParent; if(!wo.Is0 && wo.ClassNameIs(\"ApplicationFrameWindow\")) return wo;\n\t//\tstring s = w.GetText(false, false); if(s.NE()) return default;\n\t//\treturn Api.FindWindow(\"ApplicationFrameWindow\", s);\n\t//}\n\t\n\tinternal static partial class Internal_ {\n\t\t/// <summary>\n\t\t/// Calls API <c>SetProp</c>/<c>GetProp</c> to set/get misc flags for a window.\n\t\t/// Currently unused.\n\t\t/// </summary>\n\t\tinternal static class WinFlags {\n\t\t\tstatic readonly ushort s_atom = Api.GlobalAddAtom(\"Au.WFlags_\"); //atom is much faster than string\n\t\t\t//note: cannot delete atom, eg in static dtor. Deletes even if currently used by a window prop, making the prop useless.\n\t\t\t\n\t\t\tinternal static bool Set(wnd w, WFlags_ flags, bool? setAddRem = null) {\n\t\t\t\tswitch (setAddRem) {\n\t\t\t\tcase true: flags = Get(w) | flags; break;\n\t\t\t\tcase false: flags = Get(w) & ~flags; break;\n\t\t\t\t}\n\t\t\t\treturn w.Prop.Set(s_atom, (int)flags);\n\t\t\t}\n\t\t\t\n\t\t\tinternal static WFlags_ Get(wnd w) {\n\t\t\t\treturn (WFlags_)(int)w.Prop[s_atom];\n\t\t\t}\n\t\t\t\n\t\t\tinternal static WFlags_ Remove(wnd w) {\n\t\t\t\treturn (WFlags_)(int)w.Prop.Remove(s_atom);\n\t\t\t}\n\t\t\t\n\t\t\t[Flags]\n\t\t\tinternal enum WFlags_ {\n\t\t\t\t//these were used by elm.\n\t\t\t\t//ChromeYes = 1,\n\t\t\t\t//ChromeNo = 2,\n\t\t\t}\n\t\t}\n\t\t\n\t\t//internal class LastWndProps\n\t\t//{\n\t\t//\twnd _w;\n\t\t//\tlong _time;\n\t\t//\tstring _class, _programName, _programPath;\n\t\t//\tint _tid, _pid;\n\t\t\n\t\t//\tvoid _GetCommon(wnd w)\n\t\t//\t{\n\t\t//\t\tvar t = perf.ms;\n\t\t//\t\tif(w != _w || t - _time > 100) { _w = w; _class = _programName= _programPath = null; _tid = _pid = 0; }\n\t\t//\t\t_time = t;\n\t\t//\t}\n\t\t\n\t\t//\t//internal string GetName(wnd w) { _GetCommon(w); return _name; }\n\t\t\n\t\t//\tinternal string GetClass(wnd w) { _GetCommon(w); return _class; }\n\t\t\n\t\t//\tinternal string GetProgram(wnd w, bool fullPath) { _GetCommon(w); return fullPath ? _programPath : _programName; }\n\t\t\n\t\t//\tinternal int GetTidPid(wnd w, out int pid) { _GetCommon(w); pid = _pid; return _tid; }\n\t\t\n\t\t//\t//internal void SetName(string s) => _name = s;\n\t\t\n\t\t//\tinternal void SetClass(string s) => _class = s;\n\t\t\n\t\t//\tinternal void SetProgram(string s, bool fullPath) { if(fullPath) _programPath = s; else _programName = s; }\n\t\t\n\t\t//\tinternal void SetTidPid(int tid, int pid) { _tid = tid; _pid = pid; }\n\t\t\n\t\t//\t[ThreadStatic] static LastWndProps _ofThread;\n\t\t//\tinternal static LastWndProps OfThread => _ofThread ??= new LastWndProps();\n\t\t//}\n\t\t\n\t\t/// <summary>\n\t\t/// Returns <c>true</c> if <i>w</i> contains a non-zero special handle value (<see cref=\"SpecHWND\"/>).\n\t\t/// Note: <c>SpecHWND.TOP</c> is 0.\n\t\t/// </summary>\n\t\tpublic static bool IsSpecHwnd(wnd w) {\n\t\t\tint i = (int)w;\n\t\t\treturn (i <= 1 && i >= -3) || i == 0xffff;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Converts object to <see cref=\"wnd\"/>.\n\t\t/// Object can contain <c>null</c>, <see cref=\"wnd\"/>, <c>Control</c>, or <c>System.Windows.DependencyObject</c> (must be in element 0 of <c>object[]</c>).\n\t\t/// Avoids loading Forms and WPF dlls when not used.\n\t\t/// </summary>\n\t\tpublic static wnd FromObject(object o) => o switch {\n\t\t\tnull => default,\n\t\t\twnd w => w,\n\t\t\tobject[] a => _Wpf(a[0]),\n\t\t\t_ => _Control(o)\n\t\t};\n\t\t\n\t\t[MethodImpl(MethodImplOptions.NoInlining)] //prevents loading Forms dlls when don't need\n\t\tstatic wnd _Control(object o) => (o as System.Windows.Forms.Control).Hwnd();\n\t\t\n\t\t[MethodImpl(MethodImplOptions.NoInlining)] //prevents loading WPF dlls when don't need\n\t\tstatic wnd _Wpf(object o) => (o as System.Windows.DependencyObject).Hwnd();\n\t\t\n\t\t/// <summary>\n\t\t/// If <i>w</i> is handle of a WPF element (<c>Window</c>, <c>Popup</c>, <c>HwndHost</c>-ed control, <c>HwndSource.RootVisual</c>), returns that element, else <c>null</c>.\n\t\t/// Slow if <c>HwndHost</c>-ed control.\n\t\t/// <i>w</i> can be <c>default</c>.\n\t\t/// </summary>\n\t\tpublic static System.Windows.FrameworkElement ToWpfElement(wnd w) {\n\t\t\tif (!w.Is0) {\n\t\t\t\tif (System.Windows.Interop.HwndSource.FromHwnd(w.Handle) is System.Windows.Interop.HwndSource hs) return hs.RootVisual as System.Windows.FrameworkElement;\n\t\t\t\tfor (var p = w; !(p = p.Get.DirectParent).Is0; w = p) {\n\t\t\t\t\tif (System.Windows.Interop.HwndSource.FromHwnd(p.Handle)?.RootVisual is System.Windows.Media.Visual v) {\n\t\t\t\t\t\treturn v.FindVisualDescendant(d => d is System.Windows.Interop.HwndHost hh && hh.Handle == w.Handle, orSelf: true) as System.Windows.FrameworkElement; //speed: 200 mcs\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// An enumerable list of <see cref=\"wnd\"/> for <see cref=\"wndFinder._FindOrMatch\"/> and <see cref=\"wndChildFinder._FindInList\"/>.\n\t\t/// Holds <c>ArrayBuilder_</c> or <c>IEnumerator</c> or single <see cref=\"wnd\"/> or none.\n\t\t/// Must be disposed if it is <c>ArrayBuilder_</c> or <c>IEnumerator</c>, else disposing is optional.\n\t\t/// </summary>\n\t\tinternal struct WndList_ : IDisposable {\n\t\t\tinternal enum ListType { None, ArrayBuilder, Enumerator, SingleWnd }\n\t\t\t\n\t\t\tListType _t;\n\t\t\tint _i;\n\t\t\twnd _w;\n\t\t\tIEnumerator<wnd> _en;\n\t\t\tArrayBuilder_<wnd> _ab;\n\t\t\t\n\t\t\tinternal WndList_(ArrayBuilder_<wnd> ab) {\n\t\t\t\t_ab = ab;\n\t\t\t\t_t = ListType.ArrayBuilder;\n\t\t\t}\n\t\t\t\n\t\t\tinternal WndList_(IEnumerable<wnd> en) {\n\t\t\t\tvar e = en?.GetEnumerator();\n\t\t\t\tif (e != null) {\n\t\t\t\t\t_en = e;\n\t\t\t\t\t_t = ListType.Enumerator;\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tinternal WndList_(wnd w) {\n\t\t\t\tif (!w.Is0) {\n\t\t\t\t\t_w = w;\n\t\t\t\t\t_t = ListType.SingleWnd;\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tinternal ListType Type => _t;\n\t\t\t\n\t\t\tinternal bool Next(out wnd w) {\n\t\t\t\tw = default;\n\t\t\t\tswitch (_t) {\n\t\t\t\tcase ListType.ArrayBuilder:\n\t\t\t\t\tif (_i == _ab.Count) return false;\n\t\t\t\t\tw = _ab[_i++];\n\t\t\t\t\tbreak;\n\t\t\t\tcase ListType.Enumerator:\n\t\t\t\t\tif (!_en.MoveNext()) return false;\n\t\t\t\t\tw = _en.Current;\n\t\t\t\t\tbreak;\n\t\t\t\tcase ListType.SingleWnd:\n\t\t\t\t\tif (_i > 0) return false;\n\t\t\t\t\t_i = 1; w = _w;\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\t\n\t\t\tpublic void Dispose() {\n\t\t\t\tswitch (_t) {\n\t\t\t\tcase ListType.ArrayBuilder: _ab.Dispose(); break;\n\t\t\t\tcase ListType.Enumerator: _en.Dispose(); break;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au/wnd/wnd_wait.cs",
    "content": "namespace Au;\n\npublic partial struct wnd {\n\t/// <summary>\n\t/// Waits until window exists or is active.\n\t/// </summary>\n\t/// <returns>Window handle. On timeout returns <c>default(wnd)</c> if <i>timeout</i> is negative; else exception.</returns>\n\t/// <param name=\"timeout\">Timeout, seconds. Can be 0 (infinite), >0 (exception) or &lt;0 (no exception). More info: [](xref:wait_timeout).</param>\n\t/// <param name=\"active\">The window must be the active window (<see cref=\"active\"/>), and not minimized.</param>\n\t/// <exception cref=\"TimeoutException\"><i>timeout</i> time has expired (if > 0).</exception>\n\t/// <exception cref=\"ArgumentException\" />\n\t/// <remarks>\n\t/// Parameters etc are the same as <see cref=\"find\"/>.\n\t/// By default ignores invisible and cloaked windows. Use <i>flags</i> if need.\n\t/// If you have a window's <see cref=\"wnd\"/> variable, to wait until it is active/visible/etc use <see cref=\"WaitFor\"/> instead.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// wnd w = wnd.wait(10, false, \"* Notepad\");\n\t/// print.it(w);\n\t/// ]]></code>\n\t/// Using in a WPF window with async/await.\n\t/// <code><![CDATA[\n\t/// using System.Windows;\n\t/// var b = new wpfBuilder(\"Window\").WinSize(250);\n\t/// b.R.AddButton(\"Wait\", async _ => {\n\t/// \t  print.it(\"waiting for Notepad...\");\n\t/// \t  wnd w = await Task.Run(() => wnd.wait(-10, false, \"* Notepad\"));\n\t/// \t  if(w.Is0) print.it(\"timeout\"); else print.it(w);\n\t/// });\n\t/// if (!b.ShowDialog()) return;\n\t/// ]]></code>\n\t/// </example>\n\t/// <inheritdoc cref=\"find\"/>\n\tpublic static wnd wait(Seconds timeout, bool active,\n\t\t[ParamString(PSFormat.Wildex)] string name = null,\n\t\t[ParamString(PSFormat.Wildex)] string cn = null,\n\t\t[ParamString(PSFormat.Wildex)] WOwner of = default,\n\t\tWFlags flags = 0, Func<wnd, bool> also = null, WContains contains = default\n\t\t) => new wndFinder(name, cn, of, flags, also, contains).Wait(timeout, active);\n\t\n\t/// <summary>\n\t/// Waits until any of specified windows exists or is active.\n\t/// </summary>\n\t/// <param name=\"timeout\">Timeout, seconds. Can be 0 (infinite), >0 (exception) or &lt;0 (no exception). More info: [](xref:wait_timeout).</param>\n\t/// <param name=\"active\">The window must be the active window (<see cref=\"active\"/>), and not minimized.</param>\n\t/// <param name=\"windows\">Specifies windows, like <c>new(\"Window1\"), new(\"Window2\")</c>.</param>\n\t/// <returns>1-based index and window handle. On timeout returns <c>(0, default(wnd))</c> if <i>timeout</i> is negative; else exception.</returns>\n\t/// <exception cref=\"TimeoutException\"><i>timeout</i> time has expired (if > 0).</exception>\n\t/// <remarks>\n\t/// By default ignores invisible and cloaked windows. Use <see cref=\"wndFinder\"/> flags if need.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// var (i, w) = wnd.waitAny(10, true, new(\"* Notepad\"), new(\"* Word\"));\n\t/// print.it(i, w);\n\t/// ]]></code>\n\t/// </example>\n\tpublic static (int index, wnd w) waitAny(Seconds timeout, bool active, params wndFinder[] windows) {\n\t\tforeach (var f in windows) f.Result = default;\n\t\tWFCache cache = active && windows.Length > 1 ? new WFCache() : null;\n\t\tvar loop = new WaitLoop(timeout);\n\t\tfor (; ; ) {\n\t\t\tif (active) {\n\t\t\t\twnd w = wnd.active;\n\t\t\t\tfor (int i = 0; i < windows.Length; i++) {\n\t\t\t\t\tif (windows[i].IsMatch(w, cache) && !w.IsMinimized) return (i + 1, w);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfor (int i = 0; i < windows.Length; i++) {\n\t\t\t\t\tvar f = windows[i];\n\t\t\t\t\tif (f.Exists()) return (i + 1, f.Result);\n\t\t\t\t}\n\t\t\t\t//FUTURE: optimization: get list of windows once (Lib.EnumWindows2).\n\t\t\t\t//\tProblem: list filtering depends on wndFinder flags. Even if all finders have same flags, its easy to make bugs.\n\t\t\t}\n\t\t\tif (!loop.Sleep()) return default;\n\t\t}\n\t}\n\t\n\t//rejected. Not useful. Use the non-static WaitForClosed.\n\t//\t\t/// <summary>\n\t//\t\t/// Waits until window does not exist.\n\t//\t\t/// </summary>\n\t//\t\t/// <param name=\"timeout\">Timeout, seconds. Can be 0 (infinite), >0 (exception) or &lt;0 (no exception). More info: [](xref:wait_timeout).</param>\n\t//\t\t/// <returns>Returns true. On timeout returns false if <i>timeout</i> is negative; else exception.</returns>\n\t//\t\t/// <exception cref=\"TimeoutException\"><i>timeout</i> time has expired (if > 0).</exception>\n\t//\t\t/// <exception cref=\"Exception\">Exceptions of <see cref=\"Find\"/>.</exception>\n\t//\t\t/// <remarks>\n\t//\t\t/// Parameters etc are the same as <see cref=\"Find\"/>.\n\t//\t\t/// By default ignores invisible and cloaked windows. Use flags if need.\n\t//\t\t/// If you have a window's wnd variable, to wait until it is closed use <see cref=\"WaitForClosed\"/> instead.\n\t//\t\t/// Examples: <see cref=\"Wait\"/>.\n\t//\t\t/// </remarks>\n\t//\t\tpublic static bool waitNot(Seconds timeout,\n\t//\t\t\t[ParamString(PSFormat.wildex)] string name = null,\n\t//\t\t\t[ParamString(PSFormat.wildex)] string cn = null,\n\t//\t\t\t[ParamString(PSFormat.wildex)] WOwner of = default,\n\t//\t\t\tWFlags flags = 0, Func<wnd, bool> also = null, WContents contains = default)\n\t//\t\t{\n\t//\t\t\tvar f = new wndFinder(name, cn, of, flags, also, contains);\n\t//\t\t\treturn WaitNot(timeout, out _, f);\n\t//\t\t}\n\t\n\t//\t\t/// <summary>\n\t//\t\t/// Waits until window does not exist.\n\t//\t\t/// </summary>\n\t//\t\t/// <param name=\"timeout\"></param>\n\t//\t\t/// <param name=\"wFound\">On timeout receives the first found matching window that exists.</param>\n\t//\t\t/// <param name=\"f\">Window properties etc. Can be string, see <see cref=\"wndFinder.op_Implicit(string)\"/>.</param>\n\t//\t\t/// <exception cref=\"TimeoutException\"><i>timeout</i> time has expired (if > 0).</exception>\n\t//\t\tpublic static bool waitNot(Seconds timeout, out wnd wFound, wndFinder f)\n\t//\t\t{\n\t//\t\t\twFound = default;\n\t//\t\t\tvar to = new WaitLoop(timeout);\n\t//\t\t\twnd w = default;\n\t//\t\t\tfor(; ; ) {\n\t//\t\t\t\tif(!w.IsAlive || !f.IsMatch(w)) { //if first time, or closed (!IsAlive), or changed properties (!IsMatch)\n\t//\t\t\t\t\tif(!f.Exists()) { wFound = default; return true; }\n\t//\t\t\t\t\twFound = w = f.Result;\n\t//\t\t\t\t}\n\t//\t\t\t\tif(!to.Sleep()) return false;\n\t//\t\t\t}\n\t//\t\t}\n\t\n\t//rejected. Cannot use implicit conversion string to wndFinder.\n\t//public static bool waitNot(Seconds timeout, wndFinder f)\n\t//\t=> WaitNot(timeout, out _, f);\n\t\n\t//Not often used. It's easy with await Task.Run. Anyway, need to provide an example of similar size.\n\t//public static async Task<wnd> waitAsync(Seconds timeout, string name)\n\t//{\n\t//\treturn await Task.Run(() => wait(timeout, name));\n\t//}\n\t\n\t/// <summary>\n\t/// Waits for a user-defined state/condition of this window. For example active, visible, enabled, closed, contains something.\n\t/// </summary>\n\t/// <param name=\"timeout\">Timeout, seconds. Can be 0 (infinite), >0 (exception) or &lt;0 (no exception). More info: [](xref:wait_timeout).</param>\n\t/// <param name=\"condition\">Callback function (eg lambda). It is called repeatedly, until returns a value other than <c>default(T)</c>, for example <c>true</c>.</param>\n\t/// <param name=\"dontThrowIfClosed\">\n\t/// Do not throw exception when the window handle is invalid or the window was closed while waiting.\n\t/// In such case the callback function must return a non-default value, like in examples with <see cref=\"IsAlive\"/>. Else exception is thrown (with a small delay) to prevent infinite waiting.\n\t/// </param>\n\t/// <returns>Returns the value returned by the callback function. On timeout returns <c>default(T)</c> if <i>timeout</i> is negative; else exception.</returns>\n\t/// <exception cref=\"TimeoutException\"><i>timeout</i> time has expired (if > 0).</exception>\n\t/// <exception cref=\"AuWndException\">The window handle is invalid or the window was closed while waiting.</exception>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// wnd w = wnd.find(\"* Notepad\");\n\t/// \n\t/// //wait max 30 s until window w is active. Exception on timeout or if closed.\n\t/// w.WaitFor(30, t => t.IsActive);\n\t/// print.it(\"active\");\n\t/// \n\t/// //wait max 30 s until window w is enabled. Exception on timeout or if closed.\n\t/// w.WaitFor(30, t => t.IsEnabled);\n\t/// print.it(\"enabled\");\n\t/// \n\t/// //wait until window w is closed\n\t/// w.WaitFor(0, t => !t.IsAlive, true); //same as w.WaitForClosed()\n\t/// print.it(\"closed\");\n\t/// \n\t/// //wait until window w is minimized or closed\n\t/// w.WaitFor(0, t => t.IsMinimized || !t.IsAlive, true);\n\t/// if(!w.IsAlive) { print.it(\"closed\"); return; }\n\t/// print.it(\"minimized\");\n\t/// \n\t/// //wait until window w contains focused control classnamed \"Edit\"\n\t/// var c = new wndChildFinder(cn: \"Edit\");\n\t/// w.WaitFor(10, t => c.Exists(t) && c.Result.IsFocused);\n\t/// print.it(\"control focused\");\n\t/// ]]></code>\n\t/// </example>\n\tpublic T WaitFor<T>(Seconds timeout, Func<wnd, T> condition, bool dontThrowIfClosed = false) {\n\t\tbool wasInvalid = false;\n\t\tvar loop = new WaitLoop(timeout);\n\t\tfor (; ; ) {\n\t\t\tif (!dontThrowIfClosed) ThrowIfInvalid();\n\t\t\tT r = condition(this);\n\t\t\tif (!EqualityComparer<T>.Default.Equals(r, default)) return r;\n\t\t\tif (dontThrowIfClosed) {\n\t\t\t\tif (wasInvalid) ThrowIfInvalid();\n\t\t\t\twasInvalid = !IsAlive;\n\t\t\t}\n\t\t\tif (!loop.Sleep()) return default;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Waits until this window has the specified name.\n\t/// </summary>\n\t/// <param name=\"timeout\">Timeout, seconds. Can be 0 (infinite), >0 (exception) or &lt;0 (no exception). More info: [](xref:wait_timeout).</param>\n\t/// <param name=\"name\">\n\t/// Window name. Usually it is the title bar text.\n\t/// String format: [wildcard expression](xref:wildcard_expression).\n\t/// </param>\n\t/// <param name=\"not\">Wait until this window does not have the specified name.</param>\n\t/// <returns>Returns <c>true</c>. On timeout returns <c>false</c> if <i>timeout</i> is negative; else exception.</returns>\n\t/// <exception cref=\"TimeoutException\"><i>timeout</i> time has expired (if > 0).</exception>\n\t/// <exception cref=\"AuWndException\">The window handle is invalid or the window was closed while waiting.</exception>\n\t/// <exception cref=\"ArgumentException\">Invalid wildcard expression.</exception>\n\tpublic bool WaitForName(Seconds timeout, [ParamString(PSFormat.Wildex)] string name, bool not = false) {\n\t\twildex x = name; //ArgumentNullException, ArgumentException\n\t\treturn WaitFor(timeout, t => x.Match(t.Name) != not);\n\t}\n\t\n\t/// <summary>\n\t/// Waits until this window is closed/destroyed or until its process ends.\n\t/// </summary>\n\t/// <param name=\"timeout\">Timeout, seconds. Can be 0 (infinite), >0 (exception) or &lt;0 (no exception). More info: [](xref:wait_timeout).</param>\n\t/// <param name=\"waitUntilProcessEnds\">Wait until the process of this window ends.</param>\n\t/// <returns>Returns <c>true</c>. On timeout returns <c>false</c> if <i>timeout</i> is negative; else exception.</returns>\n\t/// <exception cref=\"TimeoutException\"><i>timeout</i> time has expired (if > 0).</exception>\n\t/// <exception cref=\"AuException\">Failed to open process handle when <i>waitUntilProcessEnds</i> is <c>true</c>.</exception>\n\t/// <remarks>\n\t/// If the window is already closed, immediately returns <c>true</c>.\n\t/// </remarks>\n\tpublic bool WaitForClosed(Seconds timeout, bool waitUntilProcessEnds = false) {\n\t\tif (!waitUntilProcessEnds) return WaitFor(timeout, t => !t.IsAlive, true);\n\t\t\n\t\t//TODO3: if window of this thread or process...\n\t\t\n\t\tif (!IsAlive) return true;\n\t\tusing var ph = Handle_.OpenProcess(this, Api.SYNCHRONIZE);\n\t\tif (ph.Is0) {\n\t\t\tvar e = new AuException(0, \"*open process handle\"); //info: with SYNCHRONIZE can open process of higher IL\n\t\t\tif (!IsAlive) return true;\n\t\t\tthrow e;\n\t\t}\n\t\treturn 0 != Au.wait.forHandle(timeout, 0, ph);\n\t}\n}\n"
  },
  {
    "path": "Au/x/NuGet.md",
    "content": "LibreAutomate is an automation library for Windows. Mostly desktop and web UI automation.\n\nTo get the most of it, install the [LibreAutomate](https://www.libreautomate.com/) app.\n\nThe host program should use a manifest like [this](https://github.com/qgindi/LibreAutomate/blob/master/_/default.exe.manifest). [More info](https://www.libreautomate.com/articles/Library.html)."
  },
  {
    "path": "Au.AppHost/AppHost.cpp",
    "content": "#define WIN32_LEAN_AND_MEAN\n#define _CRT_SECURE_NO_WARNINGS\n#pragma warning(disable: 6386 6385 6255 6305 6011 28251)\n\n#include <string>\n#include <vector>\n#include <windows.h>\n#include \"coreclrhost.h\"\n\n//min supported .NET version. Installed major must be ==, minor >=, patch any (we'll use highest found), preview any (we'll use release or highest preview).\n#define NETVERMAJOR 10\n#define NETVERMINOR 0\n#define NETVERPATCH 0\n//#define NETVERPATCH 2 //see the workaround comment below\n#define NETVER_SUPPORT_PREVIEW 0 //eg fails with 5.0.0 preview, MethodNotFound exception\n\n/*\nWorkaround for .NET SDK 6.0.2+ bug: WPF and winforms apps can't start if installed older .NET 6 Runtime.\nhttps://github.com/dotnet/core/issues/7176\nThe suggested workaround works, tested. But need to edit 4 project files, install the old SDK, build ref.db with the old SDK, maybe more.\nInstead let show a message box \"please upgrade .NET\".\n*/\n\n#if 0\ntemplate<typename ... Args>\nvoid Print(LPCSTR frm, Args ... args) {\n\tHWND w = FindWindowW(L\"QM_Editor\", nullptr); if (w == 0) return;\n\tsize_t size = sizeof...(Args) > 0 ? snprintf(nullptr, 0, frm, args ...) : 0;\n\tchar* buf = size > 0 ? (char*)malloc(++size) : nullptr;\n\tif (buf != nullptr) {\n\t\tsnprintf(buf, size, frm, args ...);\n\t\tfrm = (LPCSTR)buf;\n\t} else {\n\t\tif (frm == nullptr) frm = \"\";\n\t\tsize = strlen(frm) + 1;\n\t}\n\tauto u = (wchar_t*)malloc(size * 2);\n\tMultiByteToWideChar(CP_UTF8, 0, frm, (int)size, u, (int)size);\n\tSendMessage(w, WM_SETTEXT, -1, (LPARAM)u);\n\tfree(u);\n\tif (buf != nullptr) free(buf);\n}\n#else\n#define Print __noop\n#endif\n\n#define lenof(x) (sizeof(x) / sizeof(*(x)))\n#define is32bit (sizeof(void*) == 4)\n\nvoid _WstringFrom(std::wstring& r, LPCWSTR w1, size_t len1, LPCWSTR w2, size_t len2) {\n\tif (len1 == (size_t)(-1)) len1 = w1 == nullptr ? 0 : wcslen(w1);\n\tif (len2 == (size_t)(-1)) len2 = w2 == nullptr ? 0 : wcslen(w2);\n\tif (len1 > 0) {\n\t\tr.reserve(len1 + len2);\n\t\tr.assign(w1, len1);\n\t\tif (len2 > 0) r.append(w2, len2);\n\t} else r.assign(w2, len2);\n}\n\nvoid _WstringFrom(std::wstring& r, const std::wstring& w1, LPCWSTR w2, size_t len2) {\n\t_WstringFrom(r, w1.c_str(), w1.length(), w2, len2);\n}\n\nvoid _ToUtf8(LPCWSTR w, size_t len, std::string& r) {\n\tr.resize(WideCharToMultiByte(CP_UTF8, 0, w, (int)len, nullptr, 0, nullptr, nullptr));\n\tWideCharToMultiByte(CP_UTF8, 0, w, (int)len, &r[0], (int)r.length(), nullptr, nullptr);\n\t//note: reserve/resize does not work, because resize fills with 0 chars. It seems there is no way to avoid.\n}\n\nvoid _ToUtf8(const std::wstring& w, std::string& r) {\n\t_ToUtf8(w.c_str(), (int)w.length(), r);\n}\n\nint _ToUtf8(LPCWSTR w, size_t len, LPSTR utf8, size_t lenUtf8) {\n\treturn WideCharToMultiByte(CP_UTF8, 0, w, (int)len, utf8, (int)lenUtf8, nullptr, nullptr);\n}\n\n//int _ToUtf16(LPCSTR utf8, int lenUtf8, LPWSTR utf16, int lenUtf16) {\n//\treturn MultiByteToWideChar(CP_UTF8, 0, utf8, lenUtf8, utf16, lenUtf16);\n//}\n\nbool _StrEqualI(const std::wstring& s1, LPCWSTR s2) {\n\tint len = (int)s1.length();\n\treturn wcslen(s2) == len && 2 == CompareStringOrdinal(s1.c_str(), len, s2, len, 1);\n}\n//bool _StrEqualI(const std::wstring& s1, const std::wstring& s2) {\n//\tint len = s1.length();\n//\treturn s2.length() == len && 2 == CompareStringOrdinal(s1.c_str(), len, s2.c_str(), len, 1);\n//}\n\nbool _FileExists(LPCWSTR path) {\n\treturn 0 == (FILE_ATTRIBUTE_DIRECTORY & GetFileAttributesW(path));\n}\n\nbool _DirExists(LPCWSTR path) {\n\tDWORD att = GetFileAttributesW(path);\n\treturn att != 0xffffffff && 0 != (att & FILE_ATTRIBUTE_DIRECTORY);\n}\n\nbool _IsProcessWow64() {\n#if _WIN64\n\treturn false;\n#else\n\tBOOL isWow = 0;\n\treturn IsWow64Process(GetCurrentProcess(), &isWow) && isWow;\n#endif\n}\n\n//bool _IsProcessArm64() {\n//#if _M_ARM64\n//\treturn true;\n//#else\n//\treturn false;\n//#endif\n//}\n\nLPCWSTR _ProcessPlatformString() {\n#if _M_ARM64\n\treturn L\"arm64\";\n#elif _WIN64\n\treturn L\"x64\";\n#else\n\treturn L\"x86\";\n#endif\n}\n\nbool _IsProcessX64andOsArm64() {\n#if _M_X64\n\tstatic bool have, is;\n\tif (!have) {\n\t\thave = true;\n\t\tUSHORT processMachine = 0, nativeMachine = 0;\n\t\tauto IsWow64Process2 = (BOOL(WINAPI*)(HANDLE, USHORT*, USHORT*))GetProcAddress(GetModuleHandleW(L\"kernel32.dll\"), \"IsWow64Process2\");\n\t\tif (IsWow64Process2)\n\t\t\tis = IsWow64Process2(GetCurrentProcess(), &processMachine, &nativeMachine) && nativeMachine == IMAGE_FILE_MACHINE_ARM64;\n\t}\n\treturn is;\n#else\n\treturn false;\n#endif\n}\n\nstruct PATHS {\n\tstd::wstring appDir, netCore, netDesktop, coreclrDll, exeName;\n\tstd::string asmDll, exePath;\n\tbool isPrivateNetRuntime;\n\tBYTE isEditorOrTaskExe; //1 Au.Editor.exe, 2 Au.Task.exe\n};\n\nstruct VERSTRUCT {\n\tint vMinor, vPatch;\n\tbool preview;\n\tLPWSTR dirName;\n};\n\nbool GetRuntimeDir(LPWSTR dotnetDir, size_t len, std::wstring& rtDir, VERSTRUCT& ver, bool desktop) {\n\t//get all compatible installed .NET versions\n\t//\tSee C:\\Downloads\\runtime-master\\src\\installer\\corehost\\cli\\fxr\\fx_resolver.cpp\n\tstd::vector<VERSTRUCT> a;\n\twcscpy(dotnetDir + len, desktop ? L\"\\\\shared\\\\Microsoft.WindowsDesktop.App\\\\*\" : L\"\\\\shared\\\\Microsoft.NETCore.App\\\\*\");\n\t//#define TESTVERSIONS\n#ifdef TESTVERSIONS\n\tLPCWSTR atest[] = { L\"5.2.1\", /*L\"5.0.1\",*/ L\"5.0.0-preview.2\", /*L\"5.0.5-preview.2\",*/ L\"5.1.2\", L\"5.2.2\", };\n\t//LPCWSTR atest[] = { L\"3.0.1\", L\"5.0.0-preview.2\", L\"6.0.0\", };\n\tfor (int itest = 0; itest < lenof(atest); itest++) {\n\t\tLPWSTR s = (LPWSTR)atest[itest], s0 = s;\n#else\n\tWIN32_FIND_DATAW fd;\n\tHANDLE h = FindFirstFileW(dotnetDir, &fd);\n\tif (h == INVALID_HANDLE_VALUE) return false;\n\tdo {\n\t\tLPWSTR s = fd.cFileName, s0 = s;\n\t\tif (*s < '1' || *s > '9' || 0 == (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) continue;\n#endif\n\t\t//Print(\"%S\", s);\n\t\tint vMajor = wcstol(s, &s, 10); if (vMajor != NETVERMAJOR) continue;\n\t\tif (*s++ != '.' || *s < '0' || *s > '9') continue;\n\t\tint vMinor = wcstol(s, &s, 10); if (vMinor < NETVERMINOR) continue;\n\t\t//if(desktop && vMinor != ver.vMinor) continue; //no, dotnet apphost eats it\n\t\tif (*s++ != '.' || *s < '0' || *s > '9') continue;\n\t\tint vPatch = wcstol(s, &s, 10); if (vPatch < NETVERPATCH) continue;\n\t\tVERSTRUCT v;\n\t\tv.vMinor = vMinor;\n\t\tv.vPatch = vPatch;\n\t\tv.preview = s[0] == '-' && s[1] == 'p';\n\t\tif (!NETVER_SUPPORT_PREVIEW && v.preview && vMinor == NETVERMINOR && v.vPatch == 0) continue;\n\t\tv.dirName = _wcsdup(s0);\n\t\t//Print(\"%i %i %i %i\", vMajor, v.vMinor, v.vPatch, v.preview);\n\t\ta.push_back(v);\n#ifdef TESTVERSIONS\n\t}\n#else\n\t} while (FindNextFileW(h, &fd));\n\tFindClose(h);\n#endif\n\tint n = (int)a.size();\n\tif (n == 0) return false;\n\n\t//get the best match\n\t//\thttps://learn.microsoft.com/en-us/dotnet/core/versions/selection\n\t//\tLike documented there, we roll forward to the nearest minor version and use the highest patch version.\n\t//\tBut dotnet apphost doesn't, eg fails if need 3.1 but found 3.2, and even if need 3.1.2 but found 3.1.0. Only rolls forward patch version.\n\t//\t\tTested long ago. Not tested with .NET 5. Maybe this was set in json by VS.\n\tint iBest = -1, bestMinor = -1, bestPatch = -1;\n\tfor (int i = 0; i < n; i++) {\n\t\tint v = a[i].vMinor;\n\t\tif (desktop && v == ver.vMinor) { bestMinor = v; break; }\n\t\tif ((DWORD)v < (DWORD)bestMinor) bestMinor = v;\n\t}\n\tfor (int i = 0; i < n; i++) {\n\t\tif (a[i].vMinor != bestMinor) continue;\n\t\tint v = a[i].vPatch;\n\t\tif (desktop && v == ver.vPatch) { bestPatch = v; iBest = i; break; }\n\t\tif (v > bestPatch) {\n\t\t\tbestPatch = v; iBest = i;\n\t\t}\n\t}\n\tif (a[iBest].preview) { //don't need this if preview is auto-uninstalled when installing release, but I still did not have a chance to test\n\t\tfor (int i = 0; i < n; i++) {\n\t\t\tif (a[i].vMinor != bestMinor || a[i].vPatch != bestPatch) continue;\n\t\t\t//if(!a[i].preview || StrCmpLogicalW(a[i].dirName, a[iBest].dirName) > 0) iBest = i; //no, it seems old previews are auto-unisnstalled\n\t\t\tif (!a[i].preview) {\n\t\t\t\tiBest = i; break;\n\t\t\t}\n\t\t}\n\t}\n#ifdef TESTVERSIONS\n\tPrint(\"BEST: %i %i %S\", bestMinor, bestPatch, a[iBest].dirName);\n\texit(0);\n#endif\n\n\t_WstringFrom(rtDir, dotnetDir, wcslen(dotnetDir) - 1, a[iBest].dirName, -1);\n\tver = a[iBest];\n\n\tfor (int i = 0; i < n; i++) free(a[i].dirName);\n\n\treturn true;\n}\n\nchar s_asmName[800] = \"\\0hi7yl8kJNk+gqwTDFi7ekQ\";\n\nbool GetPaths(PATHS& p) {\n\t//https://github.com/dotnet/designs/blob/master/accepted/install-locations.md\n\n\twchar_t w[1000];\n\tsize_t lenApp, lenAppDir;\n\n\t//get exe full path\n\tlenApp = ::GetModuleFileNameW(0, w, lenof(w) - 100);\n\t_ToUtf8(w, lenApp, p.exePath);\n\n\t//get appDir and exeName\n\tfor (lenAppDir = lenApp; w[--lenAppDir] != '\\\\'; ) {}\n\tp.appDir.assign(w, lenAppDir);\n\tp.exeName = w + lenAppDir + 1;\n\n\t//get asmDll\n\tif (s_asmName[0] == 0) p.isEditorOrTaskExe = 2; //Au.Task.exe. Don't need p.asmDll.\n\telse {\n\t\tif (s_asmName[0] == 1) p.isEditorOrTaskExe = 1; //Au.Editor.exe (see project BuildEvents); else exe created from a script (see Compiler.cs > _AppHost).\n\t\t_ToUtf8(w, lenAppDir + 1, p.asmDll);\n\t\tp.asmDll += s_asmName + p.isEditorOrTaskExe;\n\t}\n\n#if _M_ARM64\n\tauto coreclr2 = L\"\\\\dotnetARM\\\\coreclr.dll\";\n#elif _WIN64\n\tauto coreclr2 = L\"\\\\dotnet\\\\coreclr.dll\";\n#else\n\tauto coreclr2 = L\"\\\\dotnet32\\\\coreclr.dll\";\n#endif\n\n\tp.isPrivateNetRuntime = false;\n\t//is coreclr.dll in app dir?\n\twcscpy(w + lenAppDir, L\"\\\\coreclr.dll\");\n\tif (_FileExists(w)) goto gPrivate;\n\t//is in \"dotnet\" subdir?\n\twcscpy(w + lenAppDir, coreclr2);\n\tif (_FileExists(w)) goto gPrivate;\n\n\t//is this an exeProgram launched from portable LA? Then LA dir contains coreclr.dll.\n\tif (p.isEditorOrTaskExe == 0) {\n\t\tauto n = GetCurrentDirectory(lenof(w), w);\n\t\tauto s1 = L\"\\\\Roslyn\\\\.exeProgram\"; const int len1 = 19;\n\t\tif (n >= 4 + len1 && n < lenof(w) - 50 && _wcsicmp(w + n - len1, s1) == 0) {\n\t\t\tn -= len1;\n\t\t\twcscpy(w + n, L\"\\\\coreclr.dll\");\n\t\t\tif (_FileExists(w)) goto gPrivate;\n\t\t\twcscpy(w + n, coreclr2);\n\t\t\tif (_FileExists(w)) goto gPrivate;\n\t\t}\n\t}\n\n\t//get .NET root path, like \"C:\\Program Files\\dotnet\". See C:\\Downloads\\runtime-master\\src\\installer\\corehost\\cli\\fxr_resolver.cpp.\n\tfor (int i = 0; i <= 2; i++) {\n\t\tDWORD lenDotnet;\n\t\tif (i == 0) { //env var DOTNET_ROOT\n\t\t\tauto varName = _IsProcessWow64() ? L\"DOTNET_ROOT(x86)\" : _IsProcessX64andOsArm64() ? L\"DOTNET_ROOT_X64\" : L\"DOTNET_ROOT\";\n\t\t\tlenDotnet = GetEnvironmentVariableW(varName, w, lenof(w));\n\n\t\t\tif (lenDotnet > 0) Print(\"%S=%S\", varName, w);\n\t\t} else if (i == 1) { //registry InstallLocation\n\t\t\twchar_t key[50] = LR\"(SOFTWARE\\dotnet\\Setup\\InstalledVersions\\)\";\n\t\t\twcscat(key, _ProcessPlatformString());\n\t\t\tHKEY hk1;\n\t\t\tif (0 != RegOpenKeyExW(HKEY_LOCAL_MACHINE, key, 0, KEY_READ | KEY_WOW64_32KEY, &hk1)) lenDotnet = 0;\n\t\t\telse {\n\t\t\t\tif (0 != RegQueryValueExW(hk1, L\"InstallLocation\", nullptr, nullptr, (LPBYTE)w, &(lenDotnet = lenof(w)))) lenDotnet = 0; else lenDotnet /= 2;\n\t\t\t\tRegCloseKey(hk1);\n\t\t\t}\n\n\t\t\t//if (lenDotnet > 0) Print(\"registry=%S\", w);\n\t\t} else { //default location\n\t\t\tlenDotnet = ExpandEnvironmentStringsW(L\"%ProgramFiles%\\\\dotnet\", w, lenof(w) - 100);\n\t\t\tif (w[0] == '%') lenDotnet = 0;\n\t\t\telse if (_IsProcessX64andOsArm64()) { wcscat(w, L\"\\\\x64\"); lenDotnet += 4; }\n\n\t\t\t//SHGetSpecialFolderPathW //no, don't use shell apis, it's not so important. Current dotnet versions set the registry value. Dotnet apphost even uses literal \"Program Files\" etc string.\n\n\t\t\tif (lenDotnet > 0) Print(\"default=%S\", w);\n\t\t}\n\t\tif (i > 0 && lenDotnet != 0) lenDotnet--;\n\t\tif (lenDotnet > 1 && w[lenDotnet - 1] == '\\\\') lenDotnet--;\n\t\tif (lenDotnet > 1 && lenDotnet < lenof(w) - 100 && _DirExists(w)) {\n\t\t\tVERSTRUCT ver;\n\t\t\tif (!GetRuntimeDir(w, lenDotnet, p.netCore, ver, false)) continue;\n\t\t\tif (!GetRuntimeDir(w, lenDotnet, p.netDesktop, ver, true)) continue;\n\n\t\t\t//get coreclrDll\n\t\t\t_WstringFrom(p.coreclrDll, p.netCore, L\"\\\\coreclr.dll\", -1);\n\t\t\tif (_FileExists(p.coreclrDll.c_str())) return true;\n\t\t}\n\t}\n\treturn false;\n\ngPrivate:\n\tp.isPrivateNetRuntime = true;\n\tp.coreclrDll = w;\n\tp.netCore.assign(w, p.coreclrDll.length() - 12);\n\tp.netDesktop = p.netCore;\n\treturn true;\n}\n\n//Gets TPA assemblies (.NET, Au, etc) from resource added from file dotnet_ref_editor.txt or dotnet_ref_task.txt.\n//The file is created by script \"Create dotnet_ref.txt.cs\".\n//\tThe script parses .NET deps.json, like dotnet does it at run time.\n//\t\tNeed to run it for each major .NET version.\n//\tThe script also adds Au, and in the future maybe other omnipresent assemblies.\n//\t\tFor Au.Editor also adds Au.Controls, and in the future maybe more.\n//\t\tNeed to edit/run it when these dependencies change.\n//For Au.Editor.exe and Au.Task.exe the resource is added by BuildEvents.exe (executed as a postbuild task of Au.Editor project).\n//For exeProgram the resource is added by Compiler._Resources.AddTpa.\nvoid BuildTpaList(const PATHS& p, std::string& tpaList) {\n\tstd::string dir;\n\tauto ri = FindResource(0, (LPCWSTR)1, (LPCWSTR)220);\n\tDWORD size = SizeofResource(0, ri);\n\tauto s = (LPCSTR)LockResource(LoadResource(0, ri));\n\n\tfor (LPCSTR se = s + size; s < se;) {\n\t\tauto ss = s; while (*ss != '|' && ss < se) ss++;\n\t\tif (*s == '*') {\n\t\t\tswitch (*(++s)) {\n\t\t\tcase 'd': _ToUtf8(p.netDesktop, dir); break;\n\t\t\tcase 'c': _ToUtf8(p.netCore, dir); break;\n\t\t\tdefault: _ToUtf8(p.appDir, dir); break; //'a'\n\t\t\t}\n\t\t\tdir += '\\\\';\n\t\t} else {\n\t\t\ttpaList.append(dir);\n\t\t\ttpaList.append(s, ss - s);\n\t\t\ttpaList.append(\".dll;\");\n\t\t}\n\t\ts = ss + 1;\n\t}\n\n\t//note: don't use stringstream, it makes file size *= 2.\n}\n\nconst char** ArgsUtf8(int& nArgs) {\n\tint na = __argc - 1;\n\tauto a = __wargv + 1;\n\tsize_t size = 0; for (int i = 0; i < na; i++) size += wcslen(a[i]) * 3 + 1;\n\tauto r = (const char**)malloc(na * sizeof(LPSTR) + size);\n\tLPSTR p = (LPSTR)(r + na);\n\tfor (int i = 0; i < na; i++) {\n\t\tint k = _ToUtf8(a[i], -1, p, size);\n\t\tif (k == 0) exit(-1);\n\t\tr[i] = p;\n\t\tp += k;\n\t\tsize -= k;\n\t}\n\tnArgs = na;\n\treturn r;\n}\n\nstruct _TaskInit {\n\tconst char* asmFile;\n\tconst char** args;\n\tint nArgs;\n};\n\nint APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR pCmdLine, int nCmdShow) {\n#if 0\n\t//LARGE_INTEGER c;\n\t//QueryPerformanceCounter(&c);\n\t//return c.LowPart;\n\n\n\treturn 0;\n#else\n\t//LARGE_INTEGER t1, t2, t3, t4; QueryPerformanceCounter(&t1);\n\n\tPATHS p;\n\tbool pathsOK = GetPaths(p);\n\t//Print(\"pathsOK=%i\", pathsOK);\n\t//Print(\"exeName=%S\", p.exeName.c_str());\n\t//Print(\"exePath=%s\", p.exePath.c_str());\n\t//Print(\"asmDll=%s\", p.asmDll.c_str());\n\t//Print(\"appDir=%S\", p.appDir.c_str());\n\t//Print(\"netCore=%S\", p.netCore.c_str());\n\t//Print(\"netDesktop=%S\", p.netDesktop.c_str());\n\t//Print(\"coreclrDll=%S\", p.coreclrDll.c_str());\n\n\tif (!pathsOK) {\n\t\twchar_t w[200];\n\t\twsprintfW(w, L\"To run this application, need to install:\\r\\n\\r\\n\"\n\t\t\tL\".NET %i Desktop Runtime %s\\r\\n\\r\\n\"\n\t\t\tL\"Would you like to download it now?\", NETVERMAJOR, _ProcessPlatformString());\n\t\tif (IDYES == MessageBoxW(0, w, p.exeName.c_str(), MB_ICONERROR | MB_YESNO | MB_TOPMOST | MB_SETFOREGROUND)) {\n\t\t\tAllowSetForegroundWindow(ASFW_ANY);\n\t\t\tauto ShellExecuteW = (int(WINAPI*)(HWND, LPCWSTR, LPCWSTR, LPCWSTR, LPCWSTR, INT))GetProcAddress(LoadLibraryExW(L\"shell32\", 0, 0), \"ShellExecuteW\");\n\t\t\t//wsprintfW(w, L\"https://dotnet.microsoft.com/en-us/download/dotnet/%i.%i/runtime\", NETVERMAJOR, NETVERMINOR);\n#if true\n\t\t\twsprintfW(w, L\"https://dotnet.microsoft.com/en-us/download/dotnet/%i.%i\", NETVERMAJOR, NETVERMINOR);\n\t\t\tShellExecuteW(NULL, nullptr, w, nullptr, nullptr, SW_SHOWNORMAL);\n\t\t\tSleep(1000);\n\t\t\twsprintfW(w, L\"In the dowload page please find and download this:\\r\\n\\r\\n.NET Desktop Runtime for Windows %s\", _ProcessPlatformString());\n\t\t\tMessageBoxW(0, w, p.exeName.c_str(), MB_ICONINFORMATION | MB_TOPMOST | MB_SETFOREGROUND);\n#else\n\t\t\t//rejected. It's a legacy undocumented URL. Very slow in some countries, eg China, because does not use CDN.\n\t\t\t//note: this URL is different for preview/rc (before the final version released).\n\t\t\twsprintfW(w, L\"https://aka.ms/dotnet/%i.%i/windowsdesktop-runtime-win-x%i.exe\", NETVERMAJOR, NETVERMINOR, bits); //latest patch\n\t\t\tShellExecuteW(NULL, nullptr, w, nullptr, nullptr, SW_SHOWNORMAL);\n#endif\n\t\t}\n\t\treturn -1;\n\t}\n\n\tHMODULE hm = LoadLibraryExW(p.coreclrDll.c_str(), 0, LOAD_WITH_ALTERED_SEARCH_PATH); //3 ms\n\tif (hm == NULL) return -2;\n\tauto coreclr_initialize = (coreclr_initialize_ptr)GetProcAddress(hm, \"coreclr_initialize\");\n\tauto coreclr_execute_assembly = (coreclr_execute_assembly_ptr)GetProcAddress(hm, \"coreclr_execute_assembly\");\n\tauto coreclr_shutdown = (coreclr_shutdown_ptr)GetProcAddress(hm, \"coreclr_shutdown\");\n\n\tvoid* hostHandle;\n\tunsigned int domainId;\n\tint hr;\n\t{\n\t\t//TRUSTED_PLATFORM_ASSEMBLIES\n\t\tstd::string tpaList; tpaList.reserve(30000);\n\t\tBuildTpaList(p, tpaList);\n\n\t\t//NATIVE_DLL_SEARCH_DIRECTORIES\n\t\tstd::wstring nd16; nd16.reserve(1000);\n\t\tnd16 += p.appDir;\n#if _M_ARM64 //these are mostly fbc, if somebody uses it. See Cpp.LoadAuNativeDll.\n\t\tnd16 += L\"\\\\64\\\\ARM\\\\;\";\n#elif _WIN64\n\t\tnd16 += L\"\\\\64\\\\;\";\n#else\n\t\tnd16 += L\"\\\\32\\\\;\";\n#endif\n\t\t//rejected. For NuGet packages we use another way. This would not work, because sometimes need L\"\\\\runtimes\\\\win10-x64\\\\native\\\\;\" etc.\n//\t\tnd16 += p.appDir;\n//#if _M_ARM64\n//\t\tnd16 += L\"\\\\runtimes\\\\win-arm64\\\\native\\\\;\";\n//#elif _WIN64\n//\t\tnd16 += L\"\\\\runtimes\\\\win-x64\\\\native\\\\;\";\n//#else\n//\t\tnd16 += L\"\\\\runtimes\\\\win-x86\\\\native\\\\;\";\n//#endif\n\t\tif (!p.isPrivateNetRuntime) { nd16 += p.netDesktop; nd16 += L\"\\\\;\"; }\n\t\tnd16 += p.netCore; nd16 += L\"\\\\;\";\n\t\tstd::string nd8; _ToUtf8(nd16, nd8);\n\n\t\t//APP_CONTEXT_BASE_DIRECTORY\n\t\tstd::string appDir8; _ToUtf8(p.appDir, appDir8); appDir8 += '\\\\';\n\n\t\tconst char* propertyKeys[] = { \"TRUSTED_PLATFORM_ASSEMBLIES\", \"NATIVE_DLL_SEARCH_DIRECTORIES\", \"APP_CONTEXT_BASE_DIRECTORY\", \"APP_PATHS\" };\n\t\tconst char* propertyValues[] = { tpaList.c_str(), nd8.c_str(), appDir8.c_str(), nullptr };\n\n\t\t//Note: don't add APP_PATHS. The .NET apphost does not add it too.\n\t\t//\t.NET and Au.* assemblies are in the TPA list.\n\t\t//\tFor others, on assembly resolve event we call AssemblyLoadContext.LoadFromAssemblyPath.\n\t\t//  Cannot use both APP_PATHS and event. If a dll with same name is in APP_PATHS, .NET loads it instead.\n\t\tint nProp = 3;\n#if !true\n\t\tstd::string ap8;\n\t\tif (p.isEditorOrTaskExe == 0) {\n\t\t\tap8.assign(appDir8, 0, appDir8.length() - 1);\n\t\t\tpropertyValues[nProp++] = ap8.c_str();\n\t\t}\n\t\t//this code adds only appDir. But if using nuget's \"runtimes\" subdir, at first need to add its dlls to TPA (or maybe subdirs to APP_PATHS). It is difficult here. The event code handles it.\n#endif\n\t\t//Print(\"--- %S ---\", p.exeName.c_str());\n\t\t//Print(\"TRUSTED_PLATFORM_ASSEMBLIES:\"); Print(\"%s\", propertyValues[0]);\n\t\t//Print(\"NATIVE_DLL_SEARCH_DIRECTORIES:\"); Print(\"%s\", propertyValues[1]);\n\t\t//Print(\"APP_CONTEXT_BASE_DIRECTORY:\"); Print(\"%s\", propertyValues[2]);\n\n\t\tSetEnvironmentVariableW(L\"COMPlus_legacyCorruptedStateExceptionsPolicy\", L\"1\");\n\n\t\t//QueryPerformanceCounter(&t2); //all above code 6 ms cold, 3.6 hot\n\n\t\thr = coreclr_initialize(p.exePath.c_str(), \"main\", nProp, propertyKeys, propertyValues, &hostHandle, &domainId);\n\t\tif (hr < 0) {\n\t\t\treturn -3;\n\t\t}\n\n\t\t//QueryPerformanceCounter(&t3); //22 ms cold, 16 hot\n\t} //free temp strings eg tpaList 30000\n\n\t//attaching debugger? Note, this must be after coreclr_initialize, else fails to attach.\n\tSTARTUPINFOW si = {};\n\tGetStartupInfoW(&si);\n\tif (si.dwXCountChars == 1703529821) {\n\t\tauto he = OpenEventW(SYNCHRONIZE, false, L\"Au.event.Debugger\");\n\t\tif (!he || 0 != WaitForSingleObject(he, 30000)) return -1;\n\t\tCloseHandle(he);\n\t}\n\n\tunsigned int ec = 0;\n\tif (p.isEditorOrTaskExe == 2) { //Au.Task.exe (task process for a script with role miniProgram)\n\t\tauto coreclr_create_delegate = (coreclr_create_delegate_ptr)GetProcAddress(hm, \"coreclr_create_delegate\");\n\t\tvoid (STDMETHODCALLTYPE * Init)(LPWSTR, _TaskInit&) = nullptr;\n\t\tcoreclr_create_delegate(hostHandle, domainId, \"Au\", \"Au.More.MiniProgram_\", \"Init\", (void**)&Init); //waits until editor asks to execute a task; it sends task info through pipe\n\t\t//QueryPerformanceCounter(&t4); //1 ms\n\t\t_TaskInit t = { };\n\t\tInit(pCmdLine, t);\n\t\tif (t.asmFile != nullptr) { //null when editor exits\n\t\t\thr = coreclr_execute_assembly(hostHandle, domainId, t.nArgs, t.args, t.asmFile, &ec);\n\t\t}\n\t} else {\n\t\tint nArgs = 0;\n\t\tconst char* args0[1] = {};\n\t\tconst char** args = *pCmdLine != 0 ? ArgsUtf8(nArgs) : args0;\n\n\t\thr = coreclr_execute_assembly(hostHandle, domainId, nArgs, args, p.asmDll.c_str(), &ec); //6 ms\n\t}\n\t//Print(\"%i %i %i\", (t2.LowPart - t1.LowPart) / 10, (t3.LowPart - t2.LowPart) / 10, (t4.LowPart - t3.LowPart) / 10);\n\n\t//tested: AppDomain.ProcessExit event is in coreclr_shutdown.\n\t//\tAppDomain.UnhandledException event is in coreclr_execute_assembly, and it does not return.\n\n\tcoreclr_shutdown(hostHandle, domainId);\n\n\tif (hr < 0) {\n\t\treturn -4;\n\t}\n\treturn ec;\n#endif\n}\n"
  },
  {
    "path": "Au.AppHost/Au.AppHost.vcxproj",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup Label=\"ProjectConfigurations\">\n    <ProjectConfiguration Include=\"Release|ARM64\">\n      <Configuration>Release</Configuration>\n      <Platform>ARM64</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release|Win32\">\n      <Configuration>Release</Configuration>\n      <Platform>Win32</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release|x64\">\n      <Configuration>Release</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n  </ItemGroup>\n  <PropertyGroup Label=\"Globals\">\n    <VCProjectVersion>16.0</VCProjectVersion>\n    <ProjectGuid>{5DCF20C5-9BBD-44E2-96BE-323A845B99F4}</ProjectGuid>\n    <Keyword>Win32Proj</Keyword>\n    <RootNamespace>Au.AppHost</RootNamespace>\n    <ProjectName>Au.AppHost</ProjectName>\n    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v143</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v143</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|ARM64'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v143</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\n  <ImportGroup Label=\"ExtensionSettings\">\n  </ImportGroup>\n  <ImportGroup Label=\"Shared\">\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|ARM64'\" Label=\"PropertySheets\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <PropertyGroup Label=\"UserMacros\" />\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <LinkIncremental>false</LinkIncremental>\n    <OutDir>bin\\$(Configuration)\\$(Platform)\\</OutDir>\n    <EmbedManifest>false</EmbedManifest>\n    <IntDir>obj\\$(Configuration)\\$(Platform)\\</IntDir>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <LinkIncremental>false</LinkIncremental>\n    <OutDir>bin\\$(Configuration)\\$(Platform)\\</OutDir>\n    <EmbedManifest>false</EmbedManifest>\n    <IntDir>obj\\$(Configuration)\\$(Platform)\\</IntDir>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|ARM64'\">\n    <LinkIncremental>false</LinkIncremental>\n    <OutDir>bin\\$(Configuration)\\$(Platform)\\</OutDir>\n    <EmbedManifest>false</EmbedManifest>\n    <IntDir>obj\\$(Configuration)\\$(Platform)\\</IntDir>\n  </PropertyGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <ClCompile>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>MinSpace</Optimization>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <SDLCheck>false</SDLCheck>\n      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <ConformanceMode>true</ConformanceMode>\n      <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>\n      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <DelayLoadDLLs>\n      </DelayLoadDLLs>\n      <StackReserveSize>0x200000</StackReserveSize>\n    </Link>\n    <PostBuildEvent>\n      <Command>xcopy $(TargetPath) $(SolutionDir)_\\32\\ /Y\n</Command>\n    </PostBuildEvent>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <ClCompile>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <WarningLevel>Level3</WarningLevel>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <SDLCheck>false</SDLCheck>\n      <PreprocessorDefinitions>NDEBUG;WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <ConformanceMode>true</ConformanceMode>\n      <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>\n      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\n      <LanguageStandard>Default</LanguageStandard>\n      <Optimization>MinSpace</Optimization>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <SectionAlignment>\n      </SectionAlignment>\n      <DelayLoadDLLs>\n      </DelayLoadDLLs>\n      <StackReserveSize>0x200000</StackReserveSize>\n    </Link>\n    <PostBuildEvent>\n      <Command>xcopy $(TargetPath) $(SolutionDir)_\\64\\ /Y</Command>\n    </PostBuildEvent>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|ARM64'\">\n    <ClCompile>\n      <PrecompiledHeader>\n      </PrecompiledHeader>\n      <WarningLevel>Level3</WarningLevel>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <SDLCheck>false</SDLCheck>\n      <PreprocessorDefinitions>NDEBUG;WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <ConformanceMode>true</ConformanceMode>\n      <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>\n      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\n      <LanguageStandard>Default</LanguageStandard>\n      <Optimization>MinSpace</Optimization>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <SectionAlignment>\n      </SectionAlignment>\n      <DelayLoadDLLs>\n      </DelayLoadDLLs>\n      <StackReserveSize>0x200000</StackReserveSize>\n    </Link>\n    <PostBuildEvent>\n      <Command>xcopy $(TargetPath) $(SolutionDir)_\\64\\ARM\\ /Y</Command>\n    </PostBuildEvent>\n  </ItemDefinitionGroup>\n  <ItemGroup>\n    <ClCompile Include=\"AppHost.cpp\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ClInclude Include=\"coreclrhost.h\" />\n  </ItemGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\n  <ImportGroup Label=\"ExtensionTargets\">\n  </ImportGroup>\n</Project>"
  },
  {
    "path": "Au.AppHost/Au.AppHost.vcxproj.filters",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup>\n    <ClCompile Include=\"AppHost.cpp\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ClInclude Include=\"coreclrhost.h\" />\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "Au.AppHost/ResourceHacker.txt",
    "content": "﻿[FILENAMES]\nExe=    64\\Au.AppHost.exe\nSaveAs= Au.Task.exe\nLog= CONSOLE\n[COMMANDS]\n-add ..\\Au.AppHost\\Script.ico, ICONGROUP,32512,0\n-addoverwrite ..\\Au.Editor\\Resources\\Au.manifest, MANIFEST,1,0\n-add dotnet_ref.txt, 220,1,0\n"
  },
  {
    "path": "Au.AppHost/coreclrhost.h",
    "content": "// Retrieved from https://github.com/dotnet/coreclr/blob/master/src/coreclr/hosts/inc/coreclrhost.h\n\n// Licensed to the .NET Foundation under one or more agreements.\n// The .NET Foundation licenses this file to you under the MIT license.\n// See the LICENSE file in the project root for more information.\n\n//\n// APIs for hosting CoreCLR\n//\n\n#ifndef __CORECLR_HOST_H__\n#define __CORECLR_HOST_H__\n\n#if defined(_WIN32) && defined(_M_IX86)\n#define CORECLR_CALLING_CONVENTION __stdcall\n#else\n#define CORECLR_CALLING_CONVENTION\n#endif\n\n// For each hosting API, we define a function prototype and a function pointer\n// The prototype is useful for implicit linking against the dynamic coreclr\n// library and the pointer for explicit dynamic loading (dlopen, LoadLibrary)\n#define CORECLR_HOSTING_API(function, ...) \\\n    extern \"C\" int CORECLR_CALLING_CONVENTION function(__VA_ARGS__); \\\n    typedef int (CORECLR_CALLING_CONVENTION *function##_ptr)(__VA_ARGS__)\n    \n//\n// Initialize the CoreCLR. Creates and starts CoreCLR host and creates an app domain\n//\n// Parameters:\n//  exePath                 - Absolute path of the executable that invoked the ExecuteAssembly (the native host application)\n//  appDomainFriendlyName   - Friendly name of the app domain that will be created to execute the assembly\n//  propertyCount           - Number of properties (elements of the following two arguments)\n//  propertyKeys            - Keys of properties of the app domain\n//  propertyValues          - Values of properties of the app domain\n//  hostHandle              - Output parameter, handle of the created host\n//  domainId                - Output parameter, id of the created app domain \n//\n// Returns:\n//  HRESULT indicating status of the operation. S_OK if the assembly was successfully executed\n//\nCORECLR_HOSTING_API(coreclr_initialize,\n            const char* exePath,\n            const char* appDomainFriendlyName,\n            int propertyCount,\n            const char** propertyKeys,\n            const char** propertyValues,\n            void** hostHandle,\n            unsigned int* domainId);\n\n//\n// Shutdown CoreCLR. It unloads the app domain and stops the CoreCLR host.\n//\n// Parameters:\n//  hostHandle              - Handle of the host\n//  domainId                - Id of the domain\n//\n// Returns:\n//  HRESULT indicating status of the operation. S_OK if the assembly was successfully executed\n//\nCORECLR_HOSTING_API(coreclr_shutdown,\n            void* hostHandle,\n            unsigned int domainId);\n\n//\n// Shutdown CoreCLR. It unloads the app domain and stops the CoreCLR host.\n//\n// Parameters:\n//  hostHandle              - Handle of the host\n//  domainId                - Id of the domain\n//  latchedExitCode         - Latched exit code after domain unloaded\n//\n// Returns:\n//  HRESULT indicating status of the operation. S_OK if the assembly was successfully executed\n//\nCORECLR_HOSTING_API(coreclr_shutdown_2,\n            void* hostHandle,\n            unsigned int domainId,\n            int* latchedExitCode);\n\n//\n// Create a native callable function pointer for a managed method.\n//\n// Parameters:\n//  hostHandle              - Handle of the host\n//  domainId                - Id of the domain \n//  entryPointAssemblyName  - Name of the assembly which holds the custom entry point\n//  entryPointTypeName      - Name of the type which holds the custom entry point\n//  entryPointMethodName    - Name of the method which is the custom entry point\n//  delegate                - Output parameter, the function stores a native callable function pointer to the delegate at the specified address\n//\n// Returns:\n//  HRESULT indicating status of the operation. S_OK if the assembly was successfully executed\n//\nCORECLR_HOSTING_API(coreclr_create_delegate,\n            void* hostHandle,\n            unsigned int domainId,\n            const char* entryPointAssemblyName,\n            const char* entryPointTypeName,\n            const char* entryPointMethodName,\n            void** delegate);\n\n//\n// Execute a managed assembly with given arguments\n//\n// Parameters:\n//  hostHandle              - Handle of the host\n//  domainId                - Id of the domain \n//  argc                    - Number of arguments passed to the executed assembly\n//  argv                    - Array of arguments passed to the executed assembly\n//  managedAssemblyPath     - Path of the managed assembly to execute (or NULL if using a custom entrypoint).\n//  exitCode                - Exit code returned by the executed assembly\n//\n// Returns:\n//  HRESULT indicating status of the operation. S_OK if the assembly was successfully executed\n//\nCORECLR_HOSTING_API(coreclr_execute_assembly,\n            void* hostHandle,\n            unsigned int domainId,\n            int argc,\n            const char** argv,\n            const char* managedAssemblyPath,\n            unsigned int* exitCode);\n\n#undef CORECLR_HOSTING_API\n                      \n#endif // __CORECLR_HOST_H__\n"
  },
  {
    "path": "Au.Controls/Au.Controls.cs",
    "content": "/*/\nrole classLibrary\ndefine CONTROLS,IDE_LA,NO_GLOBAL,NO_DEFAULT_CHARSET_UNICODE\nnoWarnings 1591,419,649\npreBuild ..\\@Au.Editor\\_prePostBuild.cs\noutputPath %folders.Workspace%\\..\\Au.Editor\nmiscFlags 1\nnoRef *\\Au.dll\npr ..\\@Au\\Au.cs\nresource resources\\Generic.xaml /embedded\n/*/\n"
  },
  {
    "path": "Au.Controls/Au.Controls.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n\t<PropertyGroup>\n\t\t<TargetFramework>net10.0-windows</TargetFramework>\n\t\t<UseWPF>true</UseWPF>\n\t\t<UseWindowsForms>true</UseWindowsForms>\n\t\t<GenerateAssemblyInfo>false</GenerateAssemblyInfo>\n\t\t<AssemblyName>Au.Controls</AssemblyName>\n\t\t<RootNamespace>Au.Controls</RootNamespace>\n\t\t<SignAssembly>true</SignAssembly>\n\t\t<AssemblyOriginatorKeyFile>..\\Au.snk</AssemblyOriginatorKeyFile>\n\t\t<AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n\t\t<DocumentationFile>bin\\Au.Controls.xml</DocumentationFile>\n\t\t<NoWarn>1591;419;8981</NoWarn>\n\t\t<LangVersion>preview</LangVersion>\n\t\t<NoDefaultLaunchSettingsFile>true</NoDefaultLaunchSettingsFile>\n\t\t<ProduceReferenceAssembly>False</ProduceReferenceAssembly>\n\t\t<Platforms>AnyCPU</Platforms>\n\t</PropertyGroup>\n\n\t<PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|AnyCPU'\">\n\t  <DebugType>embedded</DebugType>\n\t  <DefineConstants>$(DefineConstants);CONTROLS</DefineConstants>\n\t</PropertyGroup>\n\n\t<PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|AnyCPU'\">\n\t  <DebugType>embedded</DebugType>\n\t  <DefineConstants>$(DefineConstants);CONTROLS</DefineConstants>\n\t</PropertyGroup>\n\n\t<ItemGroup>\n\t  <Compile Include=\"..\\Au\\Resources\\global2.cs\" Link=\"resources\\global2.cs\" />\n\t</ItemGroup>\n\n\t<ItemGroup>\n\t  <ProjectReference Include=\"..\\Au\\Au.csproj\" />\n\t</ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "Au.Controls/KMenuCommands/KMenuCommands+.cs",
    "content": "using System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Controls.Primitives;\nusing System.Windows.Data;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing System.Xml.Linq;\n\nnamespace Au.Controls;\n\npublic partial class KMenuCommands {\n\t/// <summary>\n\t/// Contains a method delegate and a menu item that executes it. Implements <see cref=\"ICommand\"/> and can have one or more attached buttons etc and key/mouse shortcuts that execute it. All can be disabled/enabled with single function call.\n\t/// Also used for submenu-items (created from nested types); it allows for example to enable/disable all descendants with single function call.\n\t/// </summary>\n\tpublic class Command : ICommand {\n\t\treadonly KMenuCommands _mc;\n\t\treadonly Delegate _del; //null if submenu\n\t\treadonly CommandAttribute _ca;\n\t\tMenuItem _mi;\n\t\tbool _enabled;\n\t\t\n\t\tinternal Command(KMenuCommands mc, string name, string text, MemberInfo mi, CommandAttribute ca) {\n\t\t\t_mc = mc;\n\t\t\t_enabled = true;\n\t\t\tName = name;\n\t\t\tText = text;\n\t\t\t_ca = ca;\n\t\t\tKeys = ca.keys;\n\t\t\tif (mi is MethodInfo k) _del = k.CreateDelegate(k.GetParameters().Length == 0 ? typeof(Action) : typeof(Action<MenuItem>));\n\t\t\t//if (mi is MethodInfo k) _del = k.CreateDelegate(k.GetParameters().Length == 0 ? typeof(Action) : typeof(Action<object>));\n\t\t}\n\t\t\n\t\tinternal void SetMenuItem_(object text, string image, MenuItem miFactory = null) {\n\t\t\t_mi = miFactory ?? new MenuItem { Header = text }; //factory action may have set it\n\t\t\t_mi.Command = this;\n\t\t\tif (image != null && miFactory?.Icon == null) _SetImage(image);\n\t\t}\n\t\t\n\t\tMenuItem _Mi => _mi ?? throw new InvalidOperationException(\"Call FactoryParams.SetMenuItem before.\");\n\t\t\n\t\t///\n\t\tpublic MenuItem MenuItem => _mi;\n\t\t\n\t\t/// <summary>\n\t\t/// true if this is a submenu-item.\n\t\t/// </summary>\n\t\tpublic bool IsSubmenu => _del == null;\n\t\t\n\t\t/// <summary>\n\t\t/// Method name. If submenu-item - type name.\n\t\t/// Or <see cref=\"CommandAttribute.name\"/>.\n\t\t/// May have prefix <see cref=\"CommandAttribute.namePrefix\"/>.\n\t\t/// </summary>\n\t\tpublic string Name { get; }\n\t\t\n\t\t///\n\t\tpublic override string ToString() => Name;\n\t\t\n\t\tpublic CommandAttribute Attribute => _ca;\n\t\t\n\t\t/// <summary>\n\t\t/// Menu item text. Also button text/tooltip if not set.\n\t\t/// </summary>\n\t\tpublic string Text { get; }\n\t\t\n\t\t/// <summary>\n\t\t/// Button text or tooltip. Same as menu item text but without _ for Alt-underline.\n\t\t/// </summary>\n\t\tpublic string ButtonText { get; set; }\n\t\t\n\t\t/// <summary>\n\t\t/// <see cref=\"CommandAttribute.tooltip\"/>.\n\t\t/// </summary>\n\t\tpublic string ButtonTooltip { get; set; }\n\t\t\n\t\t/// <summary>\n\t\t/// Default or custom hotkey etc.\n\t\t/// </summary>\n\t\tpublic string Keys { get; private set; }\n\t\t\n\t\t/// <summary>\n\t\t/// Subscribes to <see cref=\"MenuItem.SubmenuOpened\"/> event.\n\t\t/// Will propagate to copied submenus.\n\t\t/// Call once.\n\t\t/// </summary>\n\t\t/// <param name=\"action\">Called on <b>SubmenuOpened</b> event. Not called for events bubbled from descendant submenus.</param>\n\t\tpublic void OnSubmenuOpened(Action<MenuItem> action) {\n\t\t\tDebug.Assert(_submenuOpened == null);\n\t\t\t_Mi.SubmenuOpened += _submenuOpened = (o, e) => {\n\t\t\t\tif (o != e.Source) return; //bubbled\n\t\t\t\taction((MenuItem)o);\n\t\t\t};\n\t\t}\n\t\tRoutedEventHandler _submenuOpened;\n\t\t\n\t\t//static void _SetSubmenuOpenedEvent(MenuItem mi, Action<MenuItem> )\n\t\t\n\t\t/// <summary>\n\t\t/// Something to attach to this object. Not used by this class.\n\t\t/// </summary>\n\t\tpublic object Tag { get; set; }\n\t\t\n\t\t/// <summary>\n\t\t/// Sets properties of a button to match properties of this menu item.\n\t\t/// </summary>\n\t\t/// <param name=\"b\">Button or checkbox etc.</param>\n\t\t/// <param name=\"imageAt\">If menu item has image, set <b>Content</b> = <b>DockPanel</b> with image and text and dock image at this side. If null (default), sets image without text. Not used if there is no image.</param>\n\t\t/// <param name=\"image\">Button image element, if different than menu item image. Must not be a child of something.</param>\n\t\t/// <param name=\"text\">Button text, if different than menu item text.</param>\n\t\t/// <param name=\"skipImage\">Don't change image.</param>\n\t\t/// <exception cref=\"InvalidOperationException\">This is a submenu. Or called from factory action before <see cref=\"FactoryParams.SetMenuItem\"/>.</exception>\n\t\t/// <remarks>\n\t\t/// Sets these properties:\n\t\t/// - <b>Content</b> (image or/and text),\n\t\t/// - <b>ToolTip</b>,\n\t\t/// - <b>Foreground</b>,\n\t\t/// - <b>Command</b> (to execute same method and automatically enable/disable together),\n\t\t/// - Automation Name (if with image),\n\t\t/// - if checkable, synchronizes checked state (the button should be a ToggleButton (CheckBox or RadioButton)).\n\t\t/// </remarks>\n\t\tpublic void CopyToButton(ButtonBase b, Dock? imageAt = null, UIElement image = null, string text = null, bool skipImage = false) {\n\t\t\tif (IsSubmenu) throw new InvalidOperationException(\"Submenu. Use CopyToMenu.\");\n\t\t\t_ = _Mi;\n\t\t\ttext ??= ButtonText;\n\t\t\t\n\t\t\tif (skipImage) {\n\t\t\t\tswitch (b.Content) {\n\t\t\t\tcase DockPanel dp: image = dp.Children[0]; dp.Children.Clear(); break;\n\t\t\t\tcase UIElement ue: image = ue; break;\n\t\t\t\tdefault: image = null; break;\n\t\t\t\t}\n\t\t\t\tb.Content = null;\n\t\t\t} else {\n\t\t\t\timage ??= CopyImage();\n\t\t\t}\n\t\t\t\n\t\t\tif (image == null) {\n\t\t\t\tb.Content = text;\n\t\t\t\tb.Padding = new Thickness(4, 1, 4, 2);\n\t\t\t\t_SetTooltip(ButtonTooltip);\n\t\t\t} else if (imageAt != null) {\n\t\t\t\tvar v = new DockPanel();\n\t\t\t\tvar dock = imageAt.Value;\n\t\t\t\tDockPanel.SetDock(image, dock);\n\t\t\t\tv.Children.Add(image);\n\t\t\t\tvar t = new TextBlock { Text = text };\n\t\t\t\tif (dock == Dock.Left || dock == Dock.Right) t.Margin = new Thickness(2, -1, 2, 1);\n\t\t\t\tv.Children.Add(t);\n\t\t\t\tb.Content = v;\n\t\t\t\t_SetTooltip(ButtonTooltip);\n\t\t\t} else { //only image\n\t\t\t\tb.Content = image;\n\t\t\t\t_SetTooltip(ButtonTooltip ?? text);\n\t\t\t}\n\t\t\tb.Foreground = _mi.Foreground;\n\t\t\tif (image != null && !text.NE()) System.Windows.Automation.AutomationProperties.SetName(b, text);\n\t\t\tb.Command = this;\n\t\t\t\n\t\t\tif (_mi.IsCheckable) {\n\t\t\t\tif (b is ToggleButton tb) tb.SetBinding(ToggleButton.IsCheckedProperty, new Binding(\"IsChecked\") { Source = _mi });\n\t\t\t\telse print.warning($\"Menu item {Name} is checkable, but button isn't a ToggleButton (CheckBox or RadioButton).\");\n\t\t\t}\n\t\t\t\n\t\t\tvoid _SetTooltip(string s) {\n\t\t\t\tstring k = this.Keys, g = _mi.InputGestureText;\n\t\t\t\tif (!k.NE() || !g.NE()) {\n\t\t\t\t\tif (!g.NE()) k = k.NE() ? g : g + \", \" + k;\n\t\t\t\t\ts = s.NE() ? k : $\"{s}\\n\\n{k}\";\n\t\t\t\t}\n\t\t\t\tb.ToolTip = s;\n\t\t\t}\n\t\t}\n\t\t\n\t\t//public void CopyToButton<T>(out T b, Dock? imageAt = null) where T : ButtonBase, new() => CopyToButton(b = new T(), imageAt);\n\t\t\n\t\t/// <summary>\n\t\t/// Sets properties of another menu item (not in this menu) to match properties of this menu item.\n\t\t/// If this is a submenu-item, copies with descendants.\n\t\t/// </summary>\n\t\t/// <param name=\"m\"></param>\n\t\t/// <param name=\"image\">Image element (<see cref=\"MenuItem.Icon\"/>), if different. Must not be a child of something.</param>\n\t\t/// <param name=\"text\">Text (<see cref=\"HeaderedItemsControl.Header\"/>), if different.</param>\n\t\t/// <exception cref=\"InvalidOperationException\">Called from factory action before <see cref=\"FactoryParams.SetMenuItem\"/>.</exception>\n\t\t/// <remarks>\n\t\t/// Sets these properties:\n\t\t/// - <b>Header</b> (if string),\n\t\t/// - <b>Icon</b> (if possible),\n\t\t/// - <b>InputGestureText</b>,\n\t\t/// - <b>ToolTip</b>,\n\t\t/// - <b>Foreground</b>,\n\t\t/// - <b>Command</b> (to execute same method and automatically enable/disable together),\n\t\t/// - <b>IsCheckable</b> (and synchronizes checked state).\n\t\t/// </remarks>\n\t\tpublic void CopyToMenu(MenuItem m, UIElement image = null, object text = null) => _CopyToMenu(_Mi, m, image, text);\n\t\t\n\t\tstatic MenuItem _CopyToMenu(MenuItem from, MenuItem to, UIElement image = null, object text = null) {\n\t\t\tif (from.Command is not Command c) return null;\n\t\t\tto ??= new();\n\t\t\t\n\t\t\tto.Icon = image ?? _CopyImage(from);\n\t\t\tif (text != null) to.Header = text;\n\t\t\telse if (from.Header is string s) {\n\t\t\t\tif (to.Role is MenuItemRole.TopLevelItem) s = StringUtil.RemoveUnderlineChar(s, '_'); //eg _Find would disable normal operation of _File\n\t\t\t\tto.Header = s;\n\t\t\t}\n\t\t\tto.InputGestureText = from.InputGestureText;\n\t\t\tto.ToolTip = from.ToolTip;\n\t\t\tto.Foreground = from.Foreground;\n\t\t\tto.Command = c;\n\t\t\t\n\t\t\tbool checkable = from.IsCheckable;\n\t\t\tto.IsCheckable = checkable;\n\t\t\tif (checkable) to.SetBinding(MenuItem.IsCheckedProperty, new Binding(\"IsChecked\") { Source = from });\n\t\t\t\n\t\t\tif (from.HasItems) {\n\t\t\t\t//lazy. Toolbar item submenus created now don't display hotkeys, because keyboard bindings are set afterwards.\n\t\t\t\tto.Items.Add(\"\");\n\t\t\t\tRoutedEventHandler smo = null;\n\t\t\t\tsmo = (_, _) => {\n\t\t\t\t\tto.SubmenuOpened -= smo;\n\t\t\t\t\tto.Items.Clear();\n\t\t\t\t\t_CopyDescendants(from, to);\n\t\t\t\t};\n\t\t\t\tto.SubmenuOpened += smo;\n\t\t\t\t\n\t\t\t\tif (c._submenuOpened != null) to.SubmenuOpened += c._submenuOpened;\n\t\t\t}\n\t\t\t\n\t\t\treturn to;\n\t\t}\n\t\t\n\t\tstatic void _CopyDescendants(ItemsControl from, ItemsControl to) {\n\t\t\tint n = 0;\n\t\t\tforeach (var v in from.Items) {\n\t\t\t\tobject k;\n\t\t\t\tswitch (v) {\n\t\t\t\tcase Separator:\n\t\t\t\t\tk = new Separator();\n\t\t\t\t\tbreak;\n\t\t\t\tcase MenuItem g:\n\t\t\t\t\tk = _CopyToMenu(g, null);\n\t\t\t\t\tif (k == null) continue; //not Command. Added dynamically. Will add again.\n\t\t\t\t\tbreak;\n\t\t\t\tdefault: continue;\n\t\t\t\t}\n\t\t\t\tto.Items.Add(k);\n\t\t\t\tn++;\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Copies descendants of this submenu to a context menu.\n\t\t/// </summary>\n\t\t/// <exception cref=\"InvalidOperationException\">This is not a submenu. Or called from factory action before <see cref=\"FactoryParams.SetMenuItem\"/>.</exception>\n\t\t/// <remarks>\n\t\t/// For each new item sets the same properties as other overload.\n\t\t/// </remarks>\n\t\tpublic void CopyToMenu(ContextMenu cm) {\n\t\t\tif (!IsSubmenu) throw new InvalidOperationException(\"Not submenu\");\n\t\t\t_CopyDescendants(_Mi, cm);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Copies menu item image element. Returns null if no image or cannot copy.\n\t\t/// </summary>\n\t\t/// <exception cref=\"InvalidOperationException\">Called from factory action before <see cref=\"FactoryParams.SetMenuItem\"/>.</exception>\n\t\tpublic UIElement CopyImage() => _CopyImage(_Mi);\n\t\t\n\t\tstatic UIElement _CopyImage(MenuItem from) {\n\t\t\tswitch (from.Icon) {\n\t\t\tcase Image im: return new Image { Source = im.Source };\n\t\t\tcase UIElement e when e.Uid is string res: //see _SetImage\n\t\t\t\tif (ResourceUtil.HasResourcePrefix(res)) return ResourceUtil.GetXamlObject(res) as UIElement;\n\t\t\t\tif (res.Starts(\"source:\")) return ImageUtil.LoadWpfImageElement(res[7..]);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\t\n\t\tbool _SetImage(string image, bool custom = false) {\n\t\t\ttry {\n\t\t\t\tif (image.NE()) {\n\t\t\t\t\t_mi.Icon = null;\n\t\t\t\t} else {\n#if DEBUG\n\t\t\t\t\tbool res = !(custom || image.Starts('*') || pathname.isFullPath(image));\n#else\n\t\t\t\t\tbool res = !(custom || image.Starts('*'));\n#endif\n\t\t\t\t\tvar ie = res\n\t\t\t\t\t\t? ResourceUtil.GetWpfImageElement(image)\n\t\t\t\t\t\t: ImageUtil.LoadWpfImageElement(image);\n\t\t\t\t\tif (ie is not Image) ie.Uid = (res ? \"resource:\" : \"source:\") + image; //xaml source for _CopyImage\n\t\t\t\t\t_mi.Icon = ie;\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tif (custom) CustomizingError(\"failed to load image\", ex);\n\t\t\t\telse print.it($\"Failed to load image {image}. {ex.ToStringWithoutStack()}\");\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets enabled/disabled state of this command, menu item and all controls with <b>Command</b> property = this (see <see cref=\"CopyToButton\"/>, <see cref=\"CopyToMenu\"/>).\n\t\t/// </summary>\n\t\tpublic bool Enabled => _enabled;\n\t\t\n\t\t/// <summary>\n\t\t/// Sets enabled/disabled state of this command, menu item and all controls with <b>Command</b> property = this (see <see cref=\"CopyToButton\"/>, <see cref=\"CopyToMenu\"/>).\n\t\t/// If submenu-item, also enables/disables all descendants. Does not actually disable/enable the submenu-item.\n\t\t/// </summary>\n\t\t/// <exception cref=\"InvalidOperationException\">Called from factory action before <see cref=\"FactoryParams.SetMenuItem\"/>.</exception>\n\t\tpublic void Enable(bool enable) {\n\t\t\t_ = _Mi;\n\t\t\tif (enable == _enabled) return;\n\t\t\t_enabled = enable;\n\t\t\tif (IsSubmenu) {\n\t\t\t\tforeach (var v in _mi.Items) if (v is MenuItem m && m.Command is Command c && !c.NoIndirectDisable) c.Enable(enable);\n\t\t\t} else {\n\t\t\t\tCanExecuteChanged?.Invoke(this, EventArgs.Empty); //enables/disables this menu item and all buttons etc with Command=this\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Don't change the enabled state indirectly when changing that of the parent menu.\n\t\t/// </summary>\n\t\tpublic bool NoIndirectDisable { get; set; }\n\t\t\n\t\t/// <summary>\n\t\t/// Gets or sets checked state of this checkable menu item and all checkable controls with <b>Command</b> property = this (see <see cref=\"CopyToButton\"/>, <see cref=\"CopyToMenu\"/>).\n\t\t/// </summary>\n\t\t/// <exception cref=\"InvalidOperationException\">Called from factory action before <see cref=\"FactoryParams.SetMenuItem\"/>.</exception>\n\t\tpublic bool Checked {\n\t\t\tget => _Mi.IsChecked;\n\t\t\tset { if (value != _Mi.IsChecked) _mi.IsChecked = value; }\n\t\t}\n\t\t\n\t\t#region ICommand\n\t\t\n\t\tpublic bool CanExecute(object parameter) => _enabled;\n\t\t\n\t\tpublic void Execute(object parameter) {\n\t\t\t_mc.ExecutingStartedEnded?.Invoke(true);\n\t\t\ttry {\n\t\t\t\tswitch (_del) {\n\t\t\t\tcase Action a0: a0(); break;\n\t\t\t\tcase Action<MenuItem> a1: a1(_mi); break;\n\t\t\t\t\t//case Action<object> a1: a1(parameter); break;\n\t\t\t\t\t//default: throw new InvalidOperationException(\"Submenu\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tfinally { _mc.ExecutingStartedEnded?.Invoke(false); }\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// When disabled or enabled with <see cref=\"Enabled\"/>.\n\t\t/// </summary>\n\t\tpublic event EventHandler CanExecuteChanged;\n\t\t\n\t\t#endregion\n\t\t\n\t\t/// <summary>\n\t\t/// Finds and returns toolbar button that has this command. Returns null if not found.\n\t\t/// </summary>\n\t\tpublic ButtonBase FindButtonInToolbar(ToolBar tb) => tb.Items.OfType<ButtonBase>().FirstOrDefault(o => o.Command == this);\n\t\t\n\t\t/// <summary>\n\t\t/// Finds and returns toolbar menu-button that has this command. Returns null if not found.\n\t\t/// </summary>\n\t\tpublic MenuItem FindMenuButtonInToolbar(ToolBar tb) {\n\t\t\tforeach (var e in tb.Items) {\n\t\t\t\tif (e is Decorator d && d.Child is Menu m && m.Items[0] is MenuItem mi && mi.Command == this) return mi;\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\t\n\t\t//public void Test() {\n\t\t//\tforeach (var v in CanExecuteChanged.GetInvocationList()) {\n\t\t//\t\tprint.it(v.Target);\n\t\t//\t}\n\t\t//}\n\t\t\n\t\tinternal void Customize_(XElement x, ToolBar toolbar) {\n\t\t\tOverflowMode hide = default;\n\t\t\tbool separator = false;\n\t\t\tstring text = null, btext = null;\n\t\t\tDock? imageAt = null;\n\t\t\t\n\t\t\tforeach (var a in x.Attributes()) {\n\t\t\t\tstring an = a.Name.LocalName, av = a.Value;\n\t\t\t\ttry {\n\t\t\t\t\tswitch (an) {\n\t\t\t\t\tcase \"keys\":\n\t\t\t\t\t\tKeys = av;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"color\":\n\t\t\t\t\t\t_mi.Foreground = new SolidColorBrush((Color)ColorConverter.ConvertFromString(av));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"image\":\n\t\t\t\t\t\t_SetImage(av, custom: true);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"text\":\n\t\t\t\t\t\t_mi.Header = text = av;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"btext\" when toolbar != null:\n\t\t\t\t\t\tbtext = av;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"separator\" when toolbar != null:\n\t\t\t\t\t\tseparator = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"hide\" when toolbar != null:\n\t\t\t\t\t\thide = Enum.Parse<OverflowMode>(av, true);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"imageAt\" when toolbar != null:\n\t\t\t\t\t\timageAt = Enum.Parse<Dock>(av, true);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tCustomizingError($\"attribute '{an}' can't be used here\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (Exception ex) { CustomizingError($\"invalid '{an}' value\", ex); }\n\t\t\t}\n\t\t\tif ((btext ?? text) != null) ButtonText = btext ?? StringUtil.RemoveUnderlineChar(text, '_');\n\t\t\t\n\t\t\tif (toolbar != null) {\n\t\t\t\ttry {\n\t\t\t\t\tif (separator) {\n\t\t\t\t\t\tvar sep = new Separator();\n\t\t\t\t\t\tif (hide != default) ToolBar.SetOverflowMode(sep, hide);\n\t\t\t\t\t\ttoolbar.Items.Add(sep);\n\t\t\t\t\t}\n\t\t\t\t\tFrameworkElement e;\n\t\t\t\t\tif (IsSubmenu) {\n\t\t\t\t\t\tvar k = new MenuItem();\n\t\t\t\t\t\tvar image = _mi.Icon;\n\t\t\t\t\t\tbool onlyImage = image != null && imageAt == null;\n\t\t\t\t\t\tif (image == null || onlyImage) k.Padding = new Thickness(3, 1, 3, 2); //make taller. If image+text, button too tall, text too high, icon too low, never mind. Never mind: not good on Win7.\n\t\t\t\t\t\tCopyToMenu(k, text: btext);\n\t\t\t\t\t\tif (onlyImage) { k.Header = k.Icon; k.Icon = null; } //make narrower\n\t\t\t\t\t\tif (ButtonTooltip != null) k.ToolTip = ButtonTooltip; else if (onlyImage) k.ToolTip = ButtonText + \"...\";\n\t\t\t\t\t\tvar m = new Menu { Background = toolbar.Background, IsMainMenu = false, UseLayoutRounding = true };\n\t\t\t\t\t\tm.Items.Add(k); //parent must be Menu, else wrong Role (must be TopLevelHeader, we can't change) and does not work\n\t\t\t\t\t\t\n\t\t\t\t\t\t//workaround for: 1. Descendant icon part black when checked. 2. Different drop-down menu style.\n\t\t\t\t\t\t//\tNever mind: different hot style.\n\t\t\t\t\t\te = new Border { Child = m };\n\t\t\t\t\t\tk.Padding = osVersion.minWin8 ? new(4, 2, 4, 2) : new(5, 3, 5, 3); //on Win7 smaller\n\t\t\t\t\t} else {\n\t\t\t\t\t\tvar b = _mi.IsCheckable ? (ButtonBase)new CheckBox() : new Button(); //rejected: support RadioButton\n\t\t\t\t\t\tb.Focusable = false;\n\t\t\t\t\t\tb.UseLayoutRounding = true;\n\t\t\t\t\t\tCopyToButton(b, imageAt);\n\t\t\t\t\t\tb.Padding = new(4, 2, 4, 2);\n\t\t\t\t\t\te = b;\n\t\t\t\t\t}\n\t\t\t\t\tif (hide != default) ToolBar.SetOverflowMode(e, hide);\n\t\t\t\t\ttoolbar.Items.Add(e);\n\t\t\t\t}\n\t\t\t\tcatch (Exception ex) { CustomizingError(\"failed to create button\", ex); }\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic void CustomizingError(string s, Exception ex = null) {\n\t\t\t_mc.OnCustomizingError?.Invoke(this, s, ex);\n\t\t}\n\t}\n\t\n\tpublic event Action<bool> ExecutingStartedEnded;\n\t\n\tclass _XElementNameEqualityComparer : IEqualityComparer<XElement> {\n\t\tbool IEqualityComparer<XElement>.Equals(XElement x, XElement y) => x.Name == y.Name;\n\t\tint IEqualityComparer<XElement>.GetHashCode(XElement x) => x.Name.GetHashCode();\n\t}\n\tstatic _XElementNameEqualityComparer s_xmlNameComparer = new();\n\t\n\t/// <summary>\n\t/// Called on error in a custom attribute.\n\t/// </summary>\n\tpublic Action<Command, string, Exception> OnCustomizingError;\n\t\n\t/// <summary>\n\t/// Parameters for factory action of <see cref=\"KMenuCommands\"/>.\n\t/// </summary>\n\tpublic class FactoryParams {\n\t\tinternal FactoryParams(Command command, MemberInfo member) { this.command = command; this.member = member; }\n\t\t\n\t\t/// <summary>\n\t\t/// The new command.\n\t\t/// <see cref=\"Command.MenuItem\"/> is still null and you can call <see cref=\"SetMenuItem\"/>.\n\t\t/// </summary>\n\t\tpublic readonly Command command;\n\t\t\n\t\t/// <summary>\n\t\t/// <see cref=\"MethodInfo\"/> of method or <see cref=\"TypeInfo\"/> of nested class.\n\t\t/// For example allows to get attributes of any type.\n\t\t/// </summary>\n\t\tpublic readonly MemberInfo member;\n\t\t\n\t\t/// <summary>\n\t\t/// Text or a WPF element to add to the text part of the menu item. In/out parameter.\n\t\t/// Text may contain _ for Alt-underline, whereas <c>command.Text</c> is without it.\n\t\t/// </summary>\n\t\tpublic object text;\n\t\t\n\t\t/// <summary><see cref=\"CommandAttribute.image\"/>. In/out parameter.</summary>\n\t\tpublic string image;\n\t\t\n\t\t/// <summary><see cref=\"CommandAttribute.param\"/>. In/out parameter. This class does not use it.</summary>\n\t\tpublic object param;\n\t\t\n\t\t/// <summary>\n\t\t/// Sets <see cref=\"Command.MenuItem\"/> property.\n\t\t/// If your factory action does not call this function, the menu item will be created after it returns.\n\t\t/// </summary>\n\t\t/// <param name=\"mi\">Your created menu item. If null, this function creates standard menu item.</param>\n\t\t/// <remarks>\n\t\t/// Uses the <i>text</i> and <i>image</i> fields; you can change them before. Sets menu item's <b>Icon</b> property if image!=null and mi?.Image==null. Sets <b>Header</b> property only if creates new item.\n\t\t/// The menu item will be added to the parent menu after your factory action returns.\n\t\t/// </remarks>\n\t\tpublic void SetMenuItem(MenuItem mi = null) => command.SetMenuItem_(text, image, mi);\n\t}\n}\n\n/// <summary>\n/// Used with <see cref=\"KMenuCommands\"/>.\n/// Allows to add menu items in the same order as methods and nested types, and optionally specify menu item text etc.\n/// </summary>\n[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false, Inherited = false)]\npublic class CommandAttribute : Attribute {\n\tinternal readonly int order_;\n\t\n\t/// <summary>\n\t/// Command name to use instead of method/type name. Use to resolve duplicate name conflict.\n\t/// </summary>\n\tpublic string name;\n\t\n\t/// <summary>\n\t/// Name prefix of this command, and default prefix of descendants. Use to resolve duplicate name conflict.\n\t/// </summary>\n\tpublic string namePrefix;\n\t\n\t/// <summary>\n\t/// Menu item text. Use _ to Alt-underline a character. If \"...\", appends it to default text.\n\t/// </summary>\n\tpublic string text;\n\t\n\t/// <summary>\n\t/// Alt-underlined character in menu item text.\n\t/// </summary>\n\tpublic char underlined;\n\t\n\t/// <summary>\n\t/// Add separator before the menu item.\n\t/// </summary>\n\tpublic bool separator;\n\t\n\t/// <summary>\n\t/// Checkable menu item.\n\t/// </summary>\n\tpublic bool checkable;\n\t\n\t/// <summary>\n\t/// Default hotkey etc. See <see cref=\"KMenuCommands.BindKeysTarget\"/>.\n\t/// </summary>\n\tpublic string keys;\n\t\n\t/// <summary>\n\t/// Element where the hotkey etc (default or customized) will work. See <see cref=\"KMenuCommands.BindKeysTarget\"/>.\n\t/// If this property applied to a class (submenu), all descendant commands without this property inherit it from the ancestor class.\n\t/// </summary>\n\tpublic string target;\n\t\n\t/// <summary>\n\t/// Text for <see cref=\"MenuItem.InputGestureText\"/>. If not set, will use <b>keys</b>.\n\t/// </summary>\n\tpublic string keysText;\n\t\n\t/// <summary>\n\t/// Image string.\n\t/// The factory action receives this string in parameters. It can load image and set menu item's <b>Icon</b> property.\n\t/// If factory action not used or does not set <b>Image</b> property and does not set image=null, this class loads image from database or exe or script resources and sets <b>Icon</b> property. The resource file can be xaml (for example converted from svg) or png etc. If using Visual Studio, to add an image to resources set its build action = Resource. More info: <see cref=\"Au.More.ResourceUtil\"/>.\n\t/// </summary>\n\tpublic string image;\n\t\n\t/// <summary>\n\t/// Tooltip text for the menu item and toolbar button (see <see cref=\"KMenuCommands.Command.CopyToButton\"/>).\n\t/// The displayed tooltip also contains menu item text in the first line.\n\t/// </summary>\n\tpublic string tooltip;\n\t\n\t/// <summary>\n\t/// A string or other value to pass to the factory action.\n\t/// </summary>\n\tpublic object param;\n\t\n\t/// <summary>\n\t/// Don't add the <b>MenuItem</b> to menu.\n\t/// </summary>\n\tpublic bool hide;\n\t\n\t/// <summary>\n\t/// Don't change the enabled state indirectly when changing that of the parent menu.\n\t/// </summary>\n\tpublic bool noIndirectDisable;\n\t\n\t/// <summary>\n\t/// Sets menu item text = method/type name with spaces instead of _ , like Select_all -> \"Select all\".\n\t/// </summary>\n\t/// <param name=\"l_\">[](xref:caller_info)</param>\n\tpublic CommandAttribute([CallerLineNumber] int l_ = 0) { order_ = l_; }\n\t\n\t/// <summary>\n\t/// Specifies menu item text.\n\t/// </summary>\n\t/// <param name=\"text\">Menu item text. Use _ to Alt-underline a character, like \"_Copy\".</param>\n\t/// <param name=\"l_\">[](xref:caller_info)</param>\n\tpublic CommandAttribute(string text, [CallerLineNumber] int l_ = 0) { this.text = text; order_ = l_; }\n\t\n\t/// <summary>\n\t/// Specifies Alt-underlined character. Sets menu item text = method/type name with spaces instead of _ , like Select_all -> \"Select all\".\n\t/// </summary>\n\t/// <param name=\"underlined\">Character to underline.</param>\n\t/// <param name=\"l_\">[](xref:caller_info)</param>\n\tpublic CommandAttribute(char underlined, [CallerLineNumber] int l_ = 0) { this.underlined = underlined; order_ = l_; }\n}\n"
  },
  {
    "path": "Au.Controls/KMenuCommands/KMenuCommands.cs",
    "content": "using System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Input;\nusing System.Xml.Linq;\n\n//TODO3: when a checkbox button command invoked with a hotkey, now does not change check state in menu and toolbar.\n//\tOnly in Edit menu. Even if target=\"\" and scintilla not focused. Works well in other menus. Don't know why.\n//\tCurrently affected code explicitly changes check state.\n\nnamespace Au.Controls;\n\n/// <summary>\n/// Builds a WPF window menu with submenus and items that execute static methods defined in a class and nested classes.\n/// Supports xaml/png/etc images, key/mouse shortcuts, auto-Alt-underline, easy creating of toolbar buttons and context menus with same/synchronized properties (command, text, image, enabled, checked, etc).\n/// </summary>\n/// <remarks>\n/// Creates submenus from public static nested types with <see cref=\"CommandAttribute\"/>.\n/// Creates executable menu items from public static methods with <see cref=\"CommandAttribute\"/>.\n/// From each such type and method creates a <see cref=\"Command\"/> object that you can access through indexer.\n/// Supports methods <c>public static void Method()</c> and <c>public static void Method(MenuItem)</c>.\n/// </remarks>\n/// <example>\n/// <code><![CDATA[\n/// var cmd=new KMenuCommands(typeof(Commands), menu);\n/// cmd[nameof(Commands.Edit.Paste)].Enabled = false;\n/// cmd[nameof(Commands.File.Rename)].SetKeys(\"F12\", _window);\n/// ]]></code>\n/// \n/// <code><![CDATA[\n/// static class Commands {\n/// \t[Command('F')]\n/// \tpublic static class File {\n/// \t\t[Command('R')]\n/// \t\tpublic static void Rename() {  }\n/// \t\t\n/// \t\t[Command('D')]\n/// \t\tpublic static void Delete() {  }\n/// \t\t\n/// \t\t[Command(\"_Properties...\", image = \"properties.xaml\")]\n/// \t\tpublic static void Properties() {  }\n/// \t\t\n/// \t\t[Command('N')]\n/// \t\tpublic static class New {\n/// \t\t\t[Command('D')]\n/// \t\t\tpublic static void Document() {  }\n/// \t\t\t\n/// \t\t\t[Command('F')]\n/// \t\t\tpublic static void Folder() {  }\n/// \t\t}\n/// \t\t\n/// \t\t[Command('x', separator = true)]\n/// \t\tpublic static void Exit(object param) {  }\n/// \t}\n/// \t\n/// \t[Command('E')]\n/// \tpublic static class Edit {\n/// \t\t[Command('t')]\n/// \t\tpublic static void Cut() {  }\n/// \t\t\n/// \t\t[Command('C')]\n/// \t\tpublic static void Copy() {  }\n/// \t\t\n/// \t\t[Command('P')]\n/// \t\tpublic static void Paste() {  }\n/// \t\t\n/// \t\t[Command('D', name = \"Edit-Delete\")]\n/// \t\tpublic static void Delete() {  }\n/// \t\t\n/// \t\t[Command('a')]\n/// \t\tpublic static void Select_all() {  }\n/// \t}\t\n/// }\n/// ]]></code>\n/// </example>\npublic partial class KMenuCommands {\n\treadonly Dictionary<string, Command> _d = new(200);\n\tMenu _menubar;\n\t\n\t/// <summary>\n\t/// Builds a WPF window menu with submenus and items that execute static methods defined in a class and nested classes.\n\t/// See example in class help.\n\t/// </summary>\n\t/// <param name=\"commands\">A type that contains nested types with methods. Must be in single source file (not partial class).</param>\n\t/// <param name=\"menu\">An empty <b>Menu</b> object. This function adds items to it.</param>\n\t/// <param name=\"autoUnderline\">Automatically insert _ in item text for Alt-underlining where not specified explicitly.</param>\n\t/// <param name=\"itemFactory\">Optional callback function that is called for each menu item. Can create menu items, set properties, create toolbar buttons, etc.</param>\n\t/// <exception cref=\"ArgumentException\">Duplicate name. Use <see cref=\"CommandAttribute.name\"/>.</exception>\n\tpublic KMenuCommands(Type commands, Menu menu, bool autoUnderline = true, Action<FactoryParams> itemFactory = null) {\n\t\t_menubar = menu;\n\t\t_CreateMenu(commands, menu, autoUnderline, itemFactory);\n\t}\n\t\n\tvoid _CreateMenu(Type type, ItemsControl parentMenu, bool autoUnderline, Action<FactoryParams> itemFactory, List<string> added = null, string namePrefix_ = null, string inheritTarget_ = null) {\n\t\tvar am = type.GetMembers(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly);\n\t\t\n\t\tif (am.Length == 0) { //dynamic submenu\n\t\t\tparentMenu.Items.Add(new Separator());\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tList<(MemberInfo mi, CommandAttribute a)> list = new(am.Length);\n\t\tforeach (var mi in am) {\n\t\t\tvar ca = mi.GetCustomAttribute<CommandAttribute>(false);\n\t\t\t//var ca = mi.GetCustomAttributes().OfType<CommandAttribute>().FirstOrDefault(); //CommandAttribute and inherited. Similar speed. Don't need because factory action receives MemberInfo an can get other attributes from it.\n\t\t\tif (ca != null) list.Add((mi, ca));\n\t\t}\n\t\t\n\t\tvar au = new List<char>();\n\t\t\n\t\tforeach (var (mi, ca) in list.OrderBy(o => o.a.order_)) {\n\t\t\tif (ca.separator && !ca.hide) parentMenu.Items.Add(new Separator());\n\t\t\t\n\t\t\tca.target ??= inheritTarget_;\n\t\t\t\n\t\t\tstring text = ca.text, buttonText, dots = null; //menu item text, possibly with _ for Alt-underline\n\t\t\tif (text == \"...\") { dots = text; text = null; }\n\t\t\tif (text != null) {\n\t\t\t\tbuttonText = StringUtil.RemoveUnderlineChar(text, '_');\n\t\t\t} else {\n\t\t\t\tbuttonText = text = mi.Name.Replace('_', ' ') + dots;\n\t\t\t\tchar u = ca.underlined;\n\t\t\t\tif (u != default) {\n\t\t\t\t\tint i = text.IndexOf(u);\n\t\t\t\t\tif (i >= 0) text = text.Insert(i, \"_\"); else print.it($\"Alt-underline character '{u}' not found in \\\"{text}\\\"\");\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tvar namePrefix = ca.namePrefix ?? namePrefix_;\n\t\t\tstring name = namePrefix + (ca.name ?? mi.Name);\n\t\t\tvar c = new Command(this, name, text, mi, ca);\n\t\t\t_d.Add(name, c);\n\t\t\tadded?.Add(name);\n\t\t\t\n\t\t\tc.ButtonText = buttonText;\n\t\t\tif (ca.tooltip is string tt) c.ButtonTooltip = parentMenu is Menu ? tt : $\"{buttonText}{(ca.checkable ? \"  (option)\" : null)}.\\n{tt}\";\n\t\t\telse if (ca.checkable) c.ButtonTooltip = $\"{buttonText}  (option)\";\n\t\t\t\n\t\t\tFactoryParams f = null;\n\t\t\tif (itemFactory != null) {\n\t\t\t\tf = new FactoryParams(c, mi) { text = text, image = ca.image, param = ca.param };\n\t\t\t\titemFactory(f);\n\t\t\t\tif (c.MenuItem == null) c.SetMenuItem_(f.text, f.image); //did not call SetMenuItem\n\t\t\t} else {\n\t\t\t\tif (c.MenuItem == null) c.SetMenuItem_(text, ca.image);\n\t\t\t}\n\t\t\tif (!ca.keysText.NE()) c.MenuItem.InputGestureText = ca.keysText;\n\t\t\tif (autoUnderline && c.MenuItem.Header is string s && _FindUnderlined(s, out char uc)) au.Add(char.ToLower(uc));\n\t\t\tif (ca.checkable) c.MenuItem.IsCheckable = true;\n\t\t\tif (c.ButtonTooltip != null) c.MenuItem.ToolTip = c.ButtonTooltip;\n\t\t\tif (ca.noIndirectDisable) c.NoIndirectDisable = true;\n\t\t\tif (parentMenu is Menu m1) c.MenuItem.MinHeight = 22;\n\t\t\t\n\t\t\tif (!ca.hide) parentMenu.Items.Add(c.MenuItem);\n\t\t\tif (mi is TypeInfo ti) {\n\t\t\t\t_CreateMenu(ti, c.MenuItem, autoUnderline, itemFactory, added, namePrefix, ca.target);\n\t\t\t\tif (ti.GetDeclaredMethod(\"_Init\") is { } init) init.Invoke(null, [this, c]);\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (autoUnderline) {\n\t\t\tforeach (var v in parentMenu.Items) {\n\t\t\t\tif (v is MenuItem m && m.Header is string s && s.Length > 0 && !_FindUnderlined(s, out _)) {\n\t\t\t\t\tint i = 0;\n\t\t\t\t\tfor (; i < s.Length; i++) {\n\t\t\t\t\t\tchar ch = s[i]; if (!char.IsLetterOrDigit(ch)) continue;\n\t\t\t\t\t\tch = char.ToLower(ch);\n\t\t\t\t\t\tif (!au.Contains(ch)) { au.Add(ch); break; }\n\t\t\t\t\t}\n\t\t\t\t\tif (i == s.Length) i = 0;\n\t\t\t\t\tm.Header = s.Insert(i, \"_\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tstatic bool _FindUnderlined(string s, out char u) {\n\t\t\tu = default;\n\t\t\tint i = 0;\n\t\t\tg1: i = s.IndexOf('_', i) + 1;\n\t\t\tif (i == 0 || i == s.Length) return false;\n\t\t\tu = s[i++];\n\t\t\tif (u == '_') goto g1;\n\t\t\treturn true;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Gets a <b>Command</b> by name.\n\t/// </summary>\n\t/// <param name=\"command\">Method name, for example \"Select_all\". Or nested type name if it's a submenu-item.</param>\n\t/// <exception cref=\"KeyNotFoundException\"></exception>\n\tpublic Command this[string command] => _d[command];\n\t\n\t/// <summary>\n\t/// Tries to find a <b>Command</b> by name. Returns false if not found.\n\t/// Same as the indexer, but does not throw exception when not found.\n\t/// </summary>\n\t/// <param name=\"command\">Method name, for example \"Select_all\". Or nested type name if it's a submenu-item.</param>\n\t/// <param name=\"c\"></param>\n\tpublic bool TryFind(string command, out Command c) => _d.TryGetValue(command, out c);\n\t\n\t/// <summary>\n\t/// Adds to <i>target</i>'s <b>InputBindings</b> all keys etc where <b>CommandAttribute.target</b> == <i>name</i>.\n\t/// </summary>\n\t/// <param name=\"target\"></param>\n\t/// <param name=\"name\"></param>\n\tpublic void BindKeysTarget(UIElement target, string name) {\n\t\t//print.it($\"---- {name} = {target}\");\n\t\tforeach (var c in _d.Values) {\n\t\t\tvar ca = c.Attribute;\n\t\t\tvar keys = c.Keys;\n\t\t\tif (!keys.NE() && ca.target == name) {\n\t\t\t\t//print.it(c, keys);\n\t\t\t\tint i = keys.Find(\", \");\n\t\t\t\tif (i < 0) _Add(keys); else foreach (var v in keys.Split(\", \")) _Add(v);\n\t\t\t\tvoid _Add(string s) {\n\t\t\t\t\tif (!Au.keys.more.parseHotkeyString(s, out var mod, out var key, out var mouse)) {\n\t\t\t\t\t\tc.CustomizingError(\"invalid keys: \" + s);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (key != default) target.InputBindings.Add(new KeyBinding(c, key, mod));\n\t\t\t\t\telse if (target is System.Windows.Interop.HwndHost) c.CustomizingError(s + \": mouse shortcuts don't work in the target control\");\n\t\t\t\t\telse target.InputBindings.Add(new MouseBinding(c, new MouseGesture(mouse, mod)));\n\t\t\t\t\t\n\t\t\t\t\t//FUTURE: support mouse shortcuts in HwndHost\n\t\t\t\t\t//if (target is System.Windows.Interop.HwndHost hh) {\n\t\t\t\t\t//\thh.MessageHook += _Hh_MessageHook;\n\t\t\t\t\t//\t//or use native mouse hook\n\t\t\t\t\t//} else {\n\t\t\t\t\t//\ttarget.InputBindings.Add(new MouseBinding(this, new MouseGesture(mouse, mod)));\n\t\t\t\t\t//}\n\t\t\t\t}\n\t\t\t\tvar mi = c.MenuItem;\n\t\t\t\tvar s = mi.InputGestureText;\n\t\t\t\tif (s.NE()) s = keys; else s = s + \", \" + keys;\n\t\t\t\tmi.InputGestureText = s;\n\t\t\t}\n\t\t}\n\t\t\n\t\t//let global key bindings work in any window of this thread, not only when target (main window) is active. Never mind mouse bindings.\n\t\tif (name == \"\") {\n\t\t\tvar a = target.InputBindings.OfType<KeyBinding>().ToArray();\n\t\t\tif (a.Length > 0) {\n\t\t\t\tEventManager.RegisterClassHandler(typeof(Window), UIElement.KeyDownEvent, new KeyEventHandler(_KeyDown));\n\t\t\t\t//InputManager.Current.PreProcessInput += _App_PreProcessInput; //works too, but more events\n\t\t\t\t\n\t\t\t\tvoid _KeyDown(object source, KeyEventArgs e) {\n\t\t\t\t\tif (!process.IsLaMainThread_) return;\n\n\t\t\t\t\t//perf.first();\n\t\t\t\t\tif (e.Handled) return;\n\t\t\t\t\tvar k = e.Key; if (k == Key.System) k = e.SystemKey;\n\t\t\t\t\tif (k is Key.LeftCtrl or Key.LeftShift or Key.LeftAlt or Key.RightCtrl or Key.RightShift or Key.RightAlt or Key.LWin or Key.RWin or Key.DeadCharProcessed or Key.ImeProcessed) return;\n\t\t\t\t\t//print.it(k);\n\t\t\t\t\tModifierKeys mod = 0; bool haveMod = false;\n\t\t\t\t\tforeach (var kb in a) {\n\t\t\t\t\t\t//print.it(kb.Command);\n\t\t\t\t\t\tif (kb.Key != k) continue;\n\t\t\t\t\t\tif (!haveMod) { haveMod = true; mod = Keyboard.Modifiers; }\n\t\t\t\t\t\tif (kb.Modifiers != mod) continue;\n\t\t\t\t\t\tvar c = kb.Command; var cp = kb.CommandParameter;\n\t\t\t\t\t\tif (c.CanExecute(cp)) c.Execute(cp);\n\t\t\t\t\t\te.Handled = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t//note: execute even if main window disabled. Maybe the command works in current window. Or maybe user wants to save (Ctrl+S).\n\t\t\t\t\t}\n\t\t\t\t\t//perf.nw(); //fast\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t//void _PreProcessInput(object sender, PreProcessInputEventArgs e) {\n\t\t\t\t//\tif (e.Canceled) return;\n\t\t\t\t//\tvar re = e.StagingItem.Input.RoutedEvent;\n\t\t\t\t//\tif (re == Keyboard.KeyDownEvent && e.StagingItem.Input is KeyEventArgs ke) {\n\t\t\t\t//\t\tvar k = ke.Key; if (k == Key.System) k = ke.SystemKey;\n\t\t\t\t//\t\tif (k is Key.LeftCtrl or Key.LeftShift or Key.LeftAlt or Key.RightCtrl or Key.RightShift or Key.RightAlt or Key.LWin or Key.RWin or Key.DeadCharProcessed or Key.ImeProcessed) return;\n\t\t\t\t//\t\tprint.it(k);\n\t\t\t\t//\t//} else { //no mouse events in hwndhosted control. It's ok, don't need global mouse shortcuts. Normal WPF bindings don't work too.\n\t\t\t\t//\t//\tprint.it(re);\n\t\t\t\t//\t}\n\t\t\t\t//}\n\t\t\t}\n\t\t}\n\t}\n\t\n\tpublic string DefaultFile { get; private set; }\n\tpublic string UserFile { get; private set; }\n\t\n\t/// <summary>\n\t/// Adds toolbar buttons specified in <i>xmlFileCustomized</i> and <i>xmlFileDefault</i>.\n\t/// Applies customizations specified there.\n\t/// </summary>\n\t/// <param name=\"xmlFileDefault\">XML file containing default toolbar buttons. See Default\\Commands.xml in editor project.</param>\n\t/// <param name=\"xmlFileCustomized\">XML file containing user-modified commands and toolbar buttons. Can be null. The file can exist or not.</param>\n\t/// <param name=\"toolbars\">Empty toolbars where to add buttons. XML tag = <b>Name</b> property.</param>\n\tpublic void InitToolbarsAndCustomize(string xmlFileDefault, string xmlFileCustomized, ToolBar[] toolbars) {\n\t\tDefaultFile = xmlFileDefault;\n\t\tUserFile = xmlFileCustomized;\n\t\t\n\t\tvar a = LoadFiles(); if (a == null) return;\n\t\t\n\t\tforeach (var x in a) {\n\t\t\tToolBar tb = null;\n\t\t\tvar tbname = x.Name.LocalName;\n\t\t\tif (tbname != \"menu\") {\n\t\t\t\ttb = toolbars.FirstOrDefault(o => o.Name == tbname);\n\t\t\t\tif (tb == null) { Debug_.Print(\"Unknown toolbar \" + tbname); continue; }\n\t\t\t}\n\t\t\t\n\t\t\tforeach (var v in x.Elements()) {\n\t\t\t\tif (_d.TryGetValue(v.Name.LocalName, out var c)) c.Customize_(v, tb);\n\t\t\t}\n\t\t}\n\t\t\n\t}\n\t\n\t/// <summary>\n\t/// Loads and merges default and customized commands files.\n\t/// </summary>\n\tpublic XElement[] LoadFiles() {\n\t\tstatic XElement[] _LoadFile(string file) {\n\t\t\ttry { return XmlUtil.LoadElem(file).Elements().ToArray(); }\n\t\t\tcatch (Exception ex) { print.it($\"<>Failed to load file <explore>{file}<>. <_>{ex.ToStringWithoutStack()}</_>\"); return null; }\n\t\t}\n\t\t\n\t\tvar a = _LoadFile(DefaultFile); if (a == null) return null;\n\t\tvar ac = UserFile != null && filesystem.exists(UserFile, true).File ? _LoadFile(UserFile) : null;\n\t\t\n\t\tif (ac != null) { //replace a elements with elements that exist in ac. If some toolbar does not exist there, use default.\n\t\t\tfor (int i = 0; i < a.Length; i++) {\n\t\t\t\tvar name = a[i].Name;\n\t\t\t\tforeach (var x in ac) if (x.Name == name && x.HasElements) { _AddMissingButtons(a[i], x); a[i] = x; break; }\n\t\t\t}\n\t\t\t\n\t\t\tvoid _AddMissingButtons(XElement xDef, XElement xUser) {\n\t\t\t\tif (xUser.Name.LocalName == \"menu\") return;\n\t\t\t\tforeach (var v in xDef.Elements().Except(xUser.Elements(), s_xmlNameComparer)) {\n\t\t\t\t\t//rejected: hide new buttons to avoid pushing some old buttons to the overflow if the toolbar size cannot grow.\n\t\t\t\t\t//\tThen new and probably important features would be used rarely and/or inconveniently.\n\t\t\t\t\t//\tInstead hide just duplicates, eg when the new button actually is an old button moved to this toolbar.\n\t\t\t\t\t//v.SetAttributeValue(\"hide\", \"always\");\n\t\t\t\t\tforeach (var tb in ac) if (tb != xUser && tb.Elements(v.Name).Any()) { v.SetAttributeValue(\"hide\", \"always\"); break; }\n\t\t\t\t\t\n\t\t\t\t\tif (v.PreviousNode is XElement xdPrev && xUser.Element(xdPrev.Name) is { } xuPrev) xuPrev.AddAfterSelf(v); //insert in default place\n\t\t\t\t\telse xUser.Add(v); //add to the end\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\treturn a;\n\t}\n\t\n\t//currently not used.\n\t//public void AddExtensionMenus(Type commands, MenuItem parentMenu = null, bool autoUnderline = true) {\n\t//\tItemsControl pa = parentMenu ?? _menubar as ItemsControl;\n\t//\t_extensions ??= new();\n\t//\tif (_extensions.Remove(commands.Name, out var t)) {\n\t//\t\tforeach (var v in t.menus) pa.Items.Remove(v);\n\t//\t\tforeach (var v in t.commands) _d.Remove(v);\n\t//\t}\n\t//\tint n = pa.Items.Count;\n\t//\tList<string> added = new();\n\t//\t_CreateMenu(commands, pa, autoUnderline, null, added);\n\t//\t_extensions.Add(commands.Name, (pa.Items.OfType<object>().Skip(n).ToArray(), added));\n\t//}\n\t//Dictionary<string, (object[] menus, List<string> commands)> _extensions;\n\t////_extensions used to support updating the same extension. Remove the code if don't need.\n}\n"
  },
  {
    "path": "Au.Controls/KPanels/FlexStackPanel.cs",
    "content": "﻿// Copyright (c) 2013 xmetropol.\n// This code is distributed under the Microsoft Public License (Ms-PL).\n// All rights reserved.\n\n//https://www.codeproject.com/Articles/598123/WPF-Flexible-StackPanel\n\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Media;\n\nnamespace Au.Controls;\n\npublic enum FlexStretchDirection {\n\tDownOnly,\n\tUpOnly,\n\tBoth,\n\tNone,\n}\n\npublic class FlexStackPanel : Panel {\n\t#region Static Fields\n\t\n\tprivate const double Tolerance = 0.001;\n\t\n\tpublic static readonly DependencyProperty OrientationProperty = DependencyProperty.Register\n\t  (\"Orientation\", typeof(Orientation), typeof(FlexStackPanel),\n\t\t  new FrameworkPropertyMetadata(Orientation.Horizontal, FrameworkPropertyMetadataOptions.AffectsMeasure, OnOrientationChanged));\n\t\n\tpublic static readonly DependencyProperty StretchDirectionProperty = DependencyProperty.Register\n\t  (\"StretchDirection\", typeof(FlexStretchDirection), typeof(FlexStackPanel),\n\t\t new FrameworkPropertyMetadata(default(FlexStretchDirection), FrameworkPropertyMetadataOptions.AffectsMeasure));\n\t\n\tpublic static readonly DependencyPropertyKey HasOverflowedChildrenPropertyKey = DependencyProperty.RegisterReadOnly\n\t  (\"HasOverflowedChildren\", typeof(bool), typeof(FlexStackPanel), new PropertyMetadata(default(bool)));\n\t\n\tpublic static readonly DependencyProperty HasOverflowedChildrenProperty = HasOverflowedChildrenPropertyKey.DependencyProperty;\n\t\n\tpublic static readonly DependencyProperty MinSlotSizeProperty = DependencyProperty.RegisterAttached\n\t  (\"MinSlotSize\", typeof(double?), typeof(FlexStackPanel),\n\t\t new FrameworkPropertyMetadata(default(double?), FrameworkPropertyMetadataOptions.AffectsParentMeasure));\n\t\n\tpublic static readonly DependencyProperty MaxSlotSizeProperty = DependencyProperty.RegisterAttached\n\t  (\"MaxSlotSize\", typeof(double?), typeof(FlexStackPanel),\n\t\t new FrameworkPropertyMetadata(default(double?), FrameworkPropertyMetadataOptions.AffectsParentMeasure));\n\t\n\tprivate static readonly DependencyPropertyKey IsOverflowedPropertyKey = DependencyProperty.RegisterAttachedReadOnly\n\t  (\"IsOverflowed\", typeof(bool), typeof(FlexStackPanel), new PropertyMetadata(default(bool)));\n\t\n\tpublic static readonly DependencyProperty IsOverflowedProperty = IsOverflowedPropertyKey.DependencyProperty;\n\t\n\tpublic static readonly DependencyProperty MeasureToSlotProperty = DependencyProperty.Register\n\t  (\"MeasureToSlot\", typeof(bool), typeof(FlexStackPanel), new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.AffectsMeasure));\n\t\n\tpublic static readonly DependencyProperty ShrinkOnOverflowProperty = DependencyProperty.RegisterAttached\n\t  (\"ShrinkOnOverflow\", typeof(bool), typeof(FlexStackPanel), new FrameworkPropertyMetadata(default(bool), FrameworkPropertyMetadataOptions.AffectsMeasure, OnAffectMeasurePropertyChanged));\n\t\n\tpublic static readonly DependencyProperty IgnoreMinConstraintsProperty = DependencyProperty.Register\n\t  (\"IgnoreMinConstraints\", typeof(bool), typeof(FlexStackPanel), new FrameworkPropertyMetadata(default(bool), FrameworkPropertyMetadataOptions.AffectsMeasure));\n\t\n\tpublic static readonly DependencyProperty IgnoreMaxConstraintsProperty = DependencyProperty.Register\n\t  (\"IgnoreMaxConstraints\", typeof(bool), typeof(FlexStackPanel), new FrameworkPropertyMetadata(default(bool), FrameworkPropertyMetadataOptions.AffectsMeasure));\n\t\n\t#endregion\n\t\n\t#region Fields\n\t\n\tprivate readonly Dictionary<UIElement, Size> childToConstraint = new Dictionary<UIElement, Size>();\n\tprivate bool isMeasureDirty;\n\tprivate bool isMeasureOverrideInProgress;\n\tprivate bool isHorizontal = true;\n\tprivate List<UIElement> orderedSequence;\n\tprivate Slot[] slots;\n\t\n\t#endregion\n\t\n\t#region Ctors\n\t\n\tstatic FlexStackPanel() {\n\t\tDefaultStyleKeyProperty.OverrideMetadata\n\t\t  (typeof(FlexStackPanel),\n\t\t   new FrameworkPropertyMetadata(typeof(FlexStackPanel)));\n\t}\n\t\n\t#endregion\n\t\n\t#region Properties\n\t\n\tprotected override bool HasLogicalOrientation {\n\t\tget { return true; }\n\t}\n\t\n\tpublic bool HasOverflowedChildren {\n\t\tget { return (bool)GetValue(HasOverflowedChildrenProperty); }\n\t\tprivate set { SetValue(HasOverflowedChildrenPropertyKey, value); }\n\t}\n\t\n\tpublic bool IgnoreMaxConstraints {\n\t\tget { return (bool)GetValue(IgnoreMaxConstraintsProperty); }\n\t\tset { SetValue(IgnoreMaxConstraintsProperty, value); }\n\t}\n\t\n\tpublic bool IgnoreMinConstraints {\n\t\tget { return (bool)GetValue(IgnoreMinConstraintsProperty); }\n\t\tset { SetValue(IgnoreMinConstraintsProperty, value); }\n\t}\n\t\n\tprotected override Orientation LogicalOrientation {\n\t\tget { return Orientation; }\n\t}\n\t\n\tpublic bool MeasureToSlot {\n\t\tget { return (bool)GetValue(MeasureToSlotProperty); }\n\t\tset { SetValue(MeasureToSlotProperty, value); }\n\t}\n\t\n\tpublic Orientation Orientation {\n\t\tget { return (Orientation)GetValue(OrientationProperty); }\n\t\tset { SetValue(OrientationProperty, value); }\n\t}\n\t\n\tpublic FlexStretchDirection StretchDirection {\n\t\tget { return (FlexStretchDirection)GetValue(StretchDirectionProperty); }\n\t\tset { SetValue(StretchDirectionProperty, value); }\n\t}\n\t\n\t#endregion\n\t\n\t#region Methods\n\t\n\tpublic static bool GetIsOverflowed(UIElement element) {\n\t\treturn (bool)element.GetValue(IsOverflowedProperty);\n\t}\n\t\n\tpublic static double? GetMaxSlotSize(UIElement element) {\n\t\treturn (double?)element.GetValue(MaxSlotSizeProperty);\n\t}\n\t\n\tpublic static double? GetMinSlotSize(UIElement element) {\n\t\treturn (double?)element.GetValue(MinSlotSizeProperty);\n\t}\n\t\n\tpublic static bool GetShrinkOnOverflow(UIElement element) {\n\t\treturn (bool)element.GetValue(ShrinkOnOverflowProperty);\n\t}\n\t\n\tpublic static void SetMaxSlotSize(UIElement element, double? value) {\n\t\telement.SetValue(MaxSlotSizeProperty, value);\n\t}\n\t\n\tpublic static void SetMinSlotSize(UIElement element, double? value) {\n\t\telement.SetValue(MinSlotSizeProperty, value);\n\t}\n\t\n\tpublic static void SetShrinkOnOverflow(UIElement element, bool value) {\n\t\telement.SetValue(ShrinkOnOverflowProperty, value);\n\t}\n\t\n\tprotected override Size ArrangeOverride(Size finalSize) {\n\t\tvar size = new Size(isHorizontal ? 0 : finalSize.Width, !isHorizontal ? 0 : finalSize.Height);\n\t\t\n\t\tvar childrenCount = Children.Count;\n\t\t\n\t\tvar rc = new Rect();\n\t\tfor (var index = 0; index < childrenCount; index++) {\n\t\t\tvar child = orderedSequence[index];\n\t\t\tif (GetIsOverflowed(child)) {\n\t\t\t\tchild.Arrange(new Rect());\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t\n\t\t\tvar slotVal = slots[index].Val;\n\t\t\tif (isHorizontal) {\n\t\t\t\trc.Width = double.IsInfinity(slotVal) ? child.DesiredSize.Width : slotVal;\n\t\t\t\trc.Height = Math.Max(finalSize.Height, child.DesiredSize.Height);\n\t\t\t\tsize.Width += rc.Width;\n\t\t\t\tsize.Height = Math.Max(size.Height, rc.Height);\n\t\t\t\tchild.Arrange(rc);\n\t\t\t\trc.X += rc.Width;\n\t\t\t} else {\n\t\t\t\trc.Width = Math.Max(finalSize.Width, child.DesiredSize.Width);\n\t\t\t\trc.Height = double.IsInfinity(slotVal) ? child.DesiredSize.Height : slotVal;\n\t\t\t\tsize.Width = Math.Max(size.Width, rc.Width);\n\t\t\t\tsize.Height += rc.Height;\n\t\t\t\tchild.Arrange(rc);\n\t\t\t\trc.Y += rc.Height;\n\t\t\t}\n\t\t}\n\t\t\n\t\treturn new Size(Math.Max(finalSize.Width, size.Width), Math.Max(finalSize.Height, size.Height));\n\t}\n\t\n\tprotected override Size MeasureOverride(Size availableSize) {\n\t\ttry {\n\t\t\tisMeasureOverrideInProgress = true;\n\t\t\t\n\t\t\tvar ignoreMinConstraints = IgnoreMinConstraints;\n\t\t\tvar ignoreMaxConstraints = IgnoreMaxConstraints;\n\t\t\t\n\t\t\tfor (var i = 0; i < 3; i++) {\n\t\t\t\tisMeasureDirty = false;\n\t\t\t\t\n\t\t\t\tvar childrenDesiredSize = new Size();\n\t\t\t\t\n\t\t\t\tvar childrenCount = Children.Count;\n\t\t\t\t\n\t\t\t\tif (childrenCount == 0)\n\t\t\t\t\treturn childrenDesiredSize;\n\t\t\t\t\n\t\t\t\tvar childConstraint = GetChildrenConstraint(availableSize);\n\t\t\t\tvar uniSize = GetUniformSize(availableSize);\n\t\t\t\t\n\t\t\t\tslots = new Slot[childrenCount];\n\t\t\t\t\n\t\t\t\torderedSequence = Children.Cast<UIElement>().ToList();\n\t\t\t\t\n\t\t\t\tfor (var index = 0; index < childrenCount; index++) {\n\t\t\t\t\tif (isMeasureDirty)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t\n\t\t\t\t\tvar child = orderedSequence[index];\n\t\t\t\t\tvar minLength = (ignoreMinConstraints ? (double?)0.0 : null) ?? GetMinSlotSize(child);\n\t\t\t\t\tvar maxLength = (ignoreMaxConstraints ? (double?)0.0 : null) ?? GetMaxSlotSize(child);\n\t\t\t\t\t\n\t\t\t\t\tvar frameworkChild = child as FrameworkElement;\n\t\t\t\t\tif (frameworkChild != null) {\n\t\t\t\t\t\tvar margin = frameworkChild.Margin;\n\t\t\t\t\t\tminLength = minLength ?? (isHorizontal ? frameworkChild.MinWidth : frameworkChild.MinHeight);\n\t\t\t\t\t\tmaxLength = maxLength ?? (isHorizontal ? frameworkChild.MaxWidth + margin.Width() : frameworkChild.MaxHeight + margin.Height());\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tminLength = minLength ?? 0.0;\n\t\t\t\t\tmaxLength = maxLength ?? double.PositiveInfinity;\n\t\t\t\t\t\n\t\t\t\t\tMeasureChild(child, childConstraint);\n\t\t\t\t\t\n\t\t\t\t\tif (isHorizontal) {\n\t\t\t\t\t\tchildrenDesiredSize.Width += child.DesiredSize.Width;\n\t\t\t\t\t\tslots[index] = new Slot(minLength.Value, maxLength.Value, StretchDirection == FlexStretchDirection.Both ? uniSize.Width : child.DesiredSize.Width);\n\t\t\t\t\t\tchildrenDesiredSize.Height = Math.Max(childrenDesiredSize.Height, child.DesiredSize.Height);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tchildrenDesiredSize.Height += child.DesiredSize.Height;\n\t\t\t\t\t\tslots[index] = new Slot(minLength.Value, maxLength.Value, StretchDirection == FlexStretchDirection.Both ? uniSize.Height : child.DesiredSize.Height);\n\t\t\t\t\t\tchildrenDesiredSize.Width = Math.Max(childrenDesiredSize.Width, child.DesiredSize.Width);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (isMeasureDirty)\n\t\t\t\t\tcontinue;\n\t\t\t\t\n\t\t\t\tvar current = slots.Sum(s => s.Val);\n\t\t\t\tvar target = GetSizePart(availableSize);\n\t\t\t\t\n\t\t\t\tvar finalSize = new Size\n\t\t\t\t  (Math.Min(availableSize.Width, isHorizontal ? current : childrenDesiredSize.Width),\n\t\t\t\t   Math.Min(availableSize.Height, isHorizontal ? childrenDesiredSize.Height : current));\n\t\t\t\t\n\t\t\t\tif (double.IsInfinity(target))\n\t\t\t\t\treturn finalSize;\n\t\t\t\t\n\t\t\t\tRecalcSlots(current, target);\n\t\t\t\t\n\t\t\t\t// Current length is greater than available and we have no possibility to stretch down -> mark elements as overflow\n\t\t\t\tcurrent = 0.0;\n\t\t\t\tfor (var index = 0; index < childrenCount; index++) {\n\t\t\t\t\tvar child = orderedSequence[index];\n\t\t\t\t\t\n\t\t\t\t\tvar slot = slots[index];\n\t\t\t\t\t\n\t\t\t\t\tif (GetShrinkOnOverflow(child) && IsGreater(current + slot.Val, target, Tolerance) && IsGreater(target, current, Tolerance)) {\n\t\t\t\t\t\tvar rest = IsGreater(target, current, Tolerance) ? target - current : 0.0;\n\t\t\t\t\t\tif (IsGreater(rest, slot.Min, Tolerance))\n\t\t\t\t\t\t\tslot.Val = rest;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tcurrent += slot.Val;\n\t\t\t\t\tSetIsOverflowed(child, IsGreater(current, target, Tolerance));\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tHasOverflowedChildren = current > target;\n\t\t\t\t\n\t\t\t\tif (MeasureToSlot)\n\t\t\t\t\tRemeasureChildren(finalSize);\n\t\t\t\t\n\t\t\t\tfinalSize = new Size\n\t\t\t\t  (Math.Min(availableSize.Width, isHorizontal ? target : childrenDesiredSize.Width),\n\t\t\t\t   Math.Min(availableSize.Height, isHorizontal ? childrenDesiredSize.Height : target));\n\t\t\t\t\n\t\t\t\tif (isMeasureDirty)\n\t\t\t\t\tcontinue;\n\t\t\t\t\n\t\t\t\treturn finalSize;\n\t\t\t}\n\t\t}\n\t\tfinally {\n\t\t\tisMeasureOverrideInProgress = false;\n\t\t}\n\t\treturn new Size();\n\t}\n\t\n\tprotected override void OnVisualChildrenChanged(DependencyObject visualAdded, DependencyObject visualRemoved) {\n\t\tbase.OnVisualChildrenChanged(visualAdded, visualRemoved);\n\t\t\n\t\tvar removedUIElement = visualRemoved as UIElement;\n\t\t\n\t\tif (removedUIElement != null)\n\t\t\tchildToConstraint.Remove(removedUIElement);\n\t}\n\t\n\tprivate static void ExpandSlots(IEnumerable<Slot> slots, double target) {\n\t\tvar sortedSlots = slots.OrderBy(v => v.Val).ToList();\n\t\tvar maxValidTarget = sortedSlots.Sum(s => s.Max);\n\t\tif (maxValidTarget < target) {\n\t\t\tforeach (var slot in sortedSlots)\n\t\t\t\tslot.Val = slot.Max;\n\t\t\treturn;\n\t\t}\n\t\tdo {\n\t\t\tvar tmpTarget = target;\n\t\t\tfor (var iSlot = sortedSlots.Count - 1; iSlot >= 0; iSlot--) {\n\t\t\t\tvar slot = sortedSlots[iSlot];\n\t\t\t\tif (slot.Val * (iSlot + 1) <= tmpTarget) {\n\t\t\t\t\tvar avg = tmpTarget / (iSlot + 1);\n\t\t\t\t\tvar success = true;\n\t\t\t\t\tfor (var jSlot = iSlot; jSlot >= 0; jSlot--) {\n\t\t\t\t\t\tvar tslot = sortedSlots[jSlot];\n\t\t\t\t\t\ttslot.Val = Math.Min(tslot.Max, avg);\n\t\t\t\t\t\t\n\t\t\t\t\t\t// Max constraint skip success expand on this iteration\n\t\t\t\t\t\tif (Math.Abs(avg - tslot.Val) <= Tolerance) continue;\n\t\t\t\t\t\t\n\t\t\t\t\t\ttarget -= tslot.Val;\n\t\t\t\t\t\tsuccess = false;\n\t\t\t\t\t\tsortedSlots.RemoveAt(jSlot);\n\t\t\t\t\t}\n\t\t\t\t\tif (success)\n\t\t\t\t\t\treturn;\n\t\t\t\t\t\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\ttmpTarget -= slot.Val;\n\t\t\t}\n\t\t} while (sortedSlots.Count > 0);\n\t}\n\t\n\tprivate Size GetChildrenConstraint(Size availableSize) {\n\t\treturn new Size\n\t\t  (isHorizontal ? double.PositiveInfinity : availableSize.Width,\n\t\t   !isHorizontal ? double.PositiveInfinity : availableSize.Height);\n\t}\n\t\n\tprivate double GetSizePart(Size size) {\n\t\treturn isHorizontal ? size.Width : size.Height;\n\t}\n\t\n\tprivate Size GetUniformSize(Size availableSize) {\n\t\tvar childrenCount = Children.Count;\n\t\tif (childrenCount == 0)\n\t\t\treturn new Size();\n\t\t\n\t\treturn new Size\n\t\t  (isHorizontal ? availableSize.Width / childrenCount : availableSize.Width,\n\t\t   !isHorizontal ? availableSize.Height / childrenCount : availableSize.Height);\n\t}\n\t\n\tprivate static bool IsGreater(double a, double b, double tolerance) {\n\t\treturn a - b > tolerance;\n\t}\n\t\n\tprivate void MeasureChild(UIElement child, Size childConstraint) {\n\t\tSize lastConstraint;\n\t\tif ((child.IsMeasureValid && childToConstraint.TryGetValue(child, out lastConstraint) && lastConstraint.Equals(childConstraint))) return;\n\t\t\n\t\tchild.Measure(childConstraint);\n\t\tchildToConstraint[child] = childConstraint;\n\t}\n\t\n\tprivate void OnAffectMeasureChanged() {\n\t\tif (isMeasureOverrideInProgress)\n\t\t\tisMeasureDirty = true;\n\t}\n\t\n\tprivate static void OnAffectMeasurePropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs) {\n\t\tvar flexStackPanel = VisualTreeHelper.GetParent(dependencyObject) as FlexStackPanel;\n\t\tif (flexStackPanel != null)\n\t\t\tflexStackPanel.OnAffectMeasureChanged();\n\t}\n\t\n\tprivate static void OnOrientationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {\n\t\tvar panel = (FlexStackPanel)d;\n\t\tpanel.isHorizontal = panel.Orientation == Orientation.Horizontal;\n\t}\n\t\n\tprivate void RecalcSlots(double current, double target) {\n\t\tvar shouldShrink = IsGreater(current, target, Tolerance) && (StretchDirection == FlexStretchDirection.DownOnly || StretchDirection == FlexStretchDirection.Both);\n\t\tvar shouldExpand = IsGreater(target, current, Tolerance) && (StretchDirection == FlexStretchDirection.UpOnly || StretchDirection == FlexStretchDirection.Both);\n\t\t\n\t\tif (shouldShrink)\n\t\t\tShrinkSlots(slots, target);\n\t\telse if (shouldExpand)\n\t\t\tExpandSlots(slots, target);\n\t}\n\t\n\tprivate void RemeasureChildren(Size availableSize) {\n\t\tvar childrenCount = Children.Count;\n\t\tif (childrenCount == 0)\n\t\t\treturn;\n\t\t\n\t\tvar childConstraint = GetChildrenConstraint(availableSize);\n\t\tfor (var index = 0; index < childrenCount; index++) {\n\t\t\tvar child = orderedSequence[index];\n\t\t\tif (Math.Abs(GetSizePart(child.DesiredSize) - slots[index].Val) > Tolerance)\n\t\t\t\tMeasureChild(child, new Size(isHorizontal ? slots[index].Val : childConstraint.Width,\n\t\t\t\t\t\t\t\t\t\t\t!isHorizontal ? slots[index].Val : childConstraint.Height));\n\t\t}\n\t}\n\t\n\tprivate static void SetIsOverflowed(UIElement element, bool value) {\n\t\telement.SetValue(IsOverflowedPropertyKey, value);\n\t}\n\t\n\tprivate static void ShrinkSlots(IEnumerable<Slot> slots, double target) {\n\t\tvar sortedSlots = slots.OrderBy(v => v.Val).ToList();\n\t\tvar minValidTarget = sortedSlots.Sum(s => s.Min);\n\t\tif (minValidTarget > target) {\n\t\t\tforeach (var slot in sortedSlots)\n\t\t\t\tslot.Val = slot.Min;\n\t\t\treturn;\n\t\t}\n\t\tdo {\n\t\t\tvar tmpTarget = target;\n\t\t\tfor (var iSlot = 0; iSlot < sortedSlots.Count; iSlot++) {\n\t\t\t\tvar slot = sortedSlots[iSlot];\n\t\t\t\tif (slot.Val * (sortedSlots.Count - iSlot) >= tmpTarget) {\n\t\t\t\t\tvar avg = tmpTarget / (sortedSlots.Count - iSlot);\n\t\t\t\t\tvar success = true;\n\t\t\t\t\tfor (var jSlot = iSlot; jSlot < sortedSlots.Count; jSlot++) {\n\t\t\t\t\t\tvar tslot = sortedSlots[jSlot];\n\t\t\t\t\t\ttslot.Val = Math.Max(tslot.Min, avg);\n\t\t\t\t\t\t\n\t\t\t\t\t\t// Min constraint skip success expand on this iteration\n\t\t\t\t\t\tif (Math.Abs(avg - tslot.Val) <= Tolerance) continue;\n\t\t\t\t\t\t\n\t\t\t\t\t\ttarget -= tslot.Val;\n\t\t\t\t\t\tsuccess = false;\n\t\t\t\t\t\tsortedSlots.RemoveAt(jSlot);\n\t\t\t\t\t\tjSlot--;\n\t\t\t\t\t}\n\t\t\t\t\tif (success)\n\t\t\t\t\t\treturn;\n\t\t\t\t\t\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\ttmpTarget -= slot.Val;\n\t\t\t}\n\t\t} while (sortedSlots.Count > 0);\n\t}\n\t\n\t#endregion\n\t\n\t#region Nested type: Slot\n\t\n\tprivate class Slot {\n\t\t#region Fields\n\t\t\n\t\tpublic readonly double Max;\n\t\tpublic readonly double Min;\n\t\tpublic double Val;\n\t\t\n\t\t#endregion\n\t\t\n\t\t#region Ctors\n\t\t\n\t\tpublic Slot(double min, double max, double val) {\n\t\t\tMin = min;\n\t\t\tMax = max;\n\t\t\tVal = val;\n\t\t\t\n\t\t\tVal = Math.Max(min, val);\n\t\t\tVal = Math.Min(max, Val);\n\t\t}\n\t\t\n\t\t#endregion\n\t}\n\t\n\t#endregion\n}\n\nfile static class MeasureUtil {\n\tpublic static double Height(this Thickness thickness) {\n\t\treturn thickness.Top + thickness.Bottom;\n\t}\n\t\n\tpublic static double Width(this Thickness thickness) {\n\t\treturn thickness.Left + thickness.Right;\n\t}\n}\n"
  },
  {
    "path": "Au.Controls/KPanels/ILeaf.cs",
    "content": "using System.Windows;\nusing System.Windows.Controls;\n\nnamespace Au.Controls;\n\npublic partial class KPanels {\n\t/// <summary>\n\t/// Interface for a leaf item (panel, toolbar or document).\n\t/// </summary>\n\tpublic interface ILeaf {\n\t\t/// <summary>\n\t\t/// Gets or sets content of panel/toolbar/document.\n\t\t/// </summary>\n\t\tFrameworkElement Content { get; set; }\n\t\t\n\t\t/// <summary>\n\t\t/// true if visible, either floating or docked.\n\t\t/// The <c>get</c> function returns true even if inactive tab item. The <c>set</c> function makes tab item active.\n\t\t/// </summary>\n\t\tbool Visible { get; set; }\n\t\t\n\t\t/// <summary>\n\t\t/// true if floating and visible.\n\t\t/// false if docked or hidden.\n\t\t/// </summary>\n\t\tbool Floating { get; set; }\n\t\t\n\t\t/// <summary>\n\t\t/// If not null, clicking on the parent tab header does not change the current focus (if possible).\n\t\t/// The action is called if the currently focused element is in the tab control; it must set focus to some visible element or call <b>Keyboard.ClearFocus</b>.\n\t\t/// </summary>\n\t\tAction DontFocusTab { get; set; }\n\t\t\n\t\t\n\t\tFunc<UIElement, bool> DontActivateFloating { get; set; }\n\t\t\n\t\t/// <summary>\n\t\t/// Adds new leaf item (panel, toolbar or document) before or after this.\n\t\t/// </summary>\n\t\t/// <param name=\"after\"></param>\n\t\t/// <param name=\"type\"></param>\n\t\t/// <param name=\"name\"></param>\n\t\t/// <param name=\"canClose\">Add \"Close    M-click\" item in context menu. It will fire <see cref=\"Closing\"/> event and call <see cref=\"Delete\"/> if not canceled.</param>\n\t\t/// <param name=\"isExtension\">Save layout etc in file.</param>\n\t\t/// <returns>Interface of the new item.</returns>\n\t\t/// <exception cref=\"ArgumentException\"><i>type</i> is not Panel/Toolbar/Document, or <i>name</i> is null, or <i>name</i> panel already exists.</exception>\n\t\t/// <remarks>\n\t\t/// Added items can be deleted with <see cref=\"Delete\"/>.\n\t\t/// Add documents only by the document placeholder or by added documents. Don't add other nodes by documents.\n\t\t/// </remarks>\n\t\tILeaf AddSibling(bool after, LeafType type, string name, bool canClose, bool isExtension);\n\t\t\n\t\t/// <summary>\n\t\t/// Deletes this leaf item added with <see cref=\"AddSibling\"/>.\n\t\t/// </summary>\n\t\t/// <exception cref=\"InvalidOperationException\">Added not with <b>AddSibling</b>.</exception>\n\t\tvoid Delete();\n\t\t\n\t\t/// <summary>\n\t\t/// Renames this document.\n\t\t/// </summary>\n\t\tvoid Rename(string name);\n\t\t\n\t\t/// <summary>\n\t\t/// Gets parent elements and index.\n\t\t/// </summary>\n\t\tParentInfo Parent { get; }\n\t\t\n\t\t/// <summary>\n\t\t/// After hiding or showing this leaf item.\n\t\t/// </summary>\n\t\tevent EventHandler<bool> VisibleChanged;\n\t\t\n\t\t/// <summary>\n\t\t/// After made floating or non-floating this leaf item.\n\t\t/// </summary>\n\t\tevent EventHandler<bool> FloatingChanged;\n\t\t\n\t\t/// <summary>\n\t\t/// When user tries to close this leaf item.\n\t\t/// Only if added with <see cref=\"AddSibling\"/> with <i>canClose</i> true.\n\t\t/// </summary>\n\t\tevent System.ComponentModel.CancelEventHandler Closing;\n\t\t\n\t\t///// <summary>\n\t\t///// When opening context menu of this leaf item.\n\t\t///// You can add menu items. All default items are already added.\n\t\t///// </summary>\n\t\t//event EventHandler<popupMenu> ContextMenuOpening;\n\t\t//FUTURE: reenable this if useful when ContextMenu_ will be public\n\t\t\n\t\t/// <summary>\n\t\t/// When this tab item selected (becomes the active item).\n\t\t/// </summary>\n\t\tevent EventHandler TabSelected;\n\t\t\n\t\t/// <summary>\n\t\t/// When moved to other tab or stack.\n\t\t/// </summary>\n\t\tevent EventHandler ParentChanged;\n\t}\n\t\n\t/// <summary>Leaf item type.</summary>\n\tpublic enum LeafType { None, Panel, Toolbar, Document }\n\t\n\tpublic struct ParentInfo {\n\t\treadonly DockPanel _panel;\n\t\treadonly FrameworkElement _elem;\n\t\treadonly int _index;\n\t\t\n\t\tinternal ParentInfo(DockPanel panel, FrameworkElement elem, int index) {\n\t\t\t_panel = panel; _elem = elem; _index = index;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets <b>DockPanel</b> that contains or will contain <see cref=\"ILeaf.Content\"/>.\n\t\t/// The first child is header, and is <b>TextBlock</b> or <b>Rectangle</b>. The second child is <b>Content</b> (if set) or none.\n\t\t/// </summary>\n\t\tpublic DockPanel Panel => _panel;\n\t\t\n\t\t/// <summary>\n\t\t/// Gets parent <b>Grid</b> if in stack, else null.\n\t\t/// </summary>\n\t\tpublic Grid Grid => _elem as Grid;\n\t\t\n\t\t/// <summary>\n\t\t/// Gets parent <b>TabControl</b> if in tab, else null.\n\t\t/// </summary>\n\t\tpublic TabControl TabControl => _elem as TabControl;\n\t\t\n\t\t/// <summary>\n\t\t/// Gets parent <b>TabItem</b> if in tab, else null.\n\t\t/// Its <b>Tag</b> is this <b>ILeaf</b>.\n\t\t/// </summary>\n\t\tpublic TabItem TabItem => TabControl?.Items[_index] as TabItem;\n\t\t\n\t\t/// <summary>\n\t\t/// Gets node index in parent node. If in tab, it is also tab item index.\n\t\t/// </summary>\n\t\tpublic int Index => _index;\n\t}\n}\n"
  },
  {
    "path": "Au.Controls/KPanels/KPanels.cs",
    "content": "using System.Xml.Linq;\nusing System.Xml;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Media;\n\nnamespace Au.Controls;\n\n/// <summary>\n/// Creates and manages window layout like in Visual Studio.\n/// Multiple docked movable/sizable/tabable/floatable/hidable/savable panels/toolbars/documents with splitters.\n/// </summary>\n/// <remarks>\n/// Layout is defined in default XML file, then saved in other XML file. See Layout.xml in Au.Editor project.\n/// If new app version adds/removes/renames panels etc, this class automatically updates the saved layout.\n/// \n/// Let your window's ctor:\n/// - call <see cref=\"Load\"/>;\n/// - set content of all leaf items (panels, toolbars, document placeholder) like <c>_panels[\"Files\"].Content = new TreeView();</c>;\n/// - set <see cref=\"Container\"/> like <c>_panels.Container = g => this.Content = g;</c>. The action is called immediately and also may be called later if need to create new root element when moving a panel etc.\n/// If want to save user-customized layout, call <see cref=\"Save\"/> when closing window or at any time before it.\n/// </remarks>\npublic partial class KPanels {\n\treadonly Dictionary<string, _Node> _dictLeaf = new();\n\treadonly Dictionary<string, _Node> _dictUserDoc = new();\n\t_Node _rootStack;\n\tstring _xmlFile;\n\tbool _loaded;\n\t\n\t/// <summary>\n\t/// Loads layout from XML file.\n\t/// </summary>\n\t/// <param name=\"xmlFileDefault\">\n\t/// XML file containing default layout. See Default\\Layout.xml in editor project.\n\t/// If starts with '&lt;', loads from XML string instead.\n\t/// </param>\n\t/// <param name=\"xmlFileCustomized\">XML file containing user-modified layout. It will be created or updated by <see cref=\"Save\"/>. If null, will not save.</param>\n\tpublic void Load(string xmlFileDefault, string xmlFileCustomized) {\n\t\tif (_loaded) throw new InvalidOperationException();\n\t\t_loaded = true;\n\t\t_xmlFile = xmlFileCustomized;\n\t\t\n\t\t//At first try to load xmlFileCustomized. If it does not exist or is invalid, load xmlFileDefault.\n\t\tstring xmlFile = xmlFileCustomized; bool useDefaultXML = xmlFileCustomized == null;\n\t\tgRetry:\n\t\tif (useDefaultXML) xmlFile = xmlFileDefault;\n\t\telse if (useDefaultXML = !filesystem.exists(xmlFile).File) goto gRetry;\n\t\t\n\t\ttry {\n\t\t\tvar x = XmlUtil.LoadElem(xmlFile);\n\t\t\tif (!useDefaultXML) _AutoUpdateXml(x, xmlFileDefault);\n\t\t\tnew _Node(this, x); //creates all and sets _rootStack\n\t\t}\n\t\tcatch (Exception e) when (!Debugger.IsAttached) {\n\t\t\tvar sErr = $\"Failed to load file '{xmlFile}'.\\r\\n\\t{e.ToStringWithoutStack()}\";\n\t\t\tif (useDefaultXML) {\n\t\t\t\t_xmlFile = null;\n\t\t\t\tdialog.showError(\"Cannot load window layout from XML file.\",\n\t\t\t\t\t$\"{sErr}\\r\\n\\r\\nReinstall the application.\",\n\t\t\t\t\texpandedText: e.StackTrace);\n\t\t\t\tEnvironment.Exit(1);\n\t\t\t} else {\n\t\t\t\tprint.warning(sErr, -1);\n\t\t\t}\n\t\t\t_dictLeaf.Clear(); _rootStack = null;\n\t\t\tuseDefaultXML = true; goto gRetry;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Saves layout to XML file <i>xmlFileCustomized</i> specified when calling <see cref=\"Load\"/>.\n\t/// Can be called at any time. When closing window, should be called in OnClosing override after calling base.OnClosing.\n\t/// Fast. Saves only if changed. Can be called eg every 30 s.\n\t/// Does nothing if <i>xmlFileCustomized</i> was null.\n\t/// </summary>\n\tpublic void Save() {\n\t\tif (_xmlFile == null) return;\n\t\ttry {\n\t\t\ts_xws ??= new XmlWriterSettings() {\n\t\t\t\tOmitXmlDeclaration = true,\n\t\t\t\tIndent = true,\n\t\t\t\tIndentChars = \"\\t\"\n\t\t\t};\n\t\t\tvar b = new StringBuilder(2000);\n\t\t\tusing (var x = XmlWriter.Create(b, s_xws)) { //not `using var...;`\n\t\t\t\tx.WriteStartDocument();\n\t\t\t\t_rootStack.Save(x);\n\t\t\t}\n\t\t\tvar s1 = b.ToString();\n\t\t\ttry { _savedXML ??= filesystem.loadText(_xmlFile); } catch { }\n\t\t\tif (s1 != _savedXML) {\n\t\t\t\tfilesystem.saveText(_xmlFile, s1);\n\t\t\t\t_savedXML = s1;\n\t\t\t}\n\t\t}\n\t\tcatch { }\n\t}\n\tstatic XmlWriterSettings s_xws;\n\tstring _savedXML;\n\t\n\tvoid _AutoUpdateXml(XElement rootStack, string xmlFileDefault) {\n\t\tvar defRootStack = XmlUtil.LoadElem(xmlFileDefault);\n\t\tvar eOld = rootStack.Descendants(\"panel\").Concat(rootStack.Descendants(\"toolbar\"))/*.Concat(rootStack.Descendants(\"document\"))*/; //same speed as with .Where(cached delegate)\n\t\tvar eNew = defRootStack.Descendants(\"panel\").Concat(defRootStack.Descendants(\"toolbar\"))/*.Concat(defRootStack.Descendants(\"document\"))*/;\n\t\tvar cmp = new _XmlNameAttrComparer();\n\t\tvar added = eNew.Except(eOld, cmp);\n\t\tvar removed = eOld.Except(eNew, cmp);\n\t\tif (removed.Any()) {\n\t\t\tforeach (var x in removed.ToArray()) {\n\t\t\t\tif (x.HasAttr(\"ext\")) continue;\n\t\t\t\t_Remove(x);\n\t\t\t\tprint.it($\"Info: {x.Name} {x.Attr(\"name\")} has been removed in this app version.\");\n\t\t\t}\n\t\t\t//removes x and ancestor stacks/tabs that would then have <= 1 child\n\t\t\tstatic void _Remove(XElement x) {\n\t\t\t\tvar pa = x.Parent;\n\t\t\t\tx.Remove();\n\t\t\t\tvar a = pa.Elements();\n\t\t\t\tint n = a.Count();\n\t\t\t\tif (n > 1) return;\n\t\t\t\tif (n == 1) {\n\t\t\t\t\tvar f = a.First();\n\t\t\t\t\tf.SetAttributeValue(\"z\", pa.Attr(\"z\"));\n\t\t\t\t\tf.SetAttributeValue(\"s\", pa.Attr(\"s\"));\n\t\t\t\t\tf.Remove();\n\t\t\t\t\tpa.AddAfterSelf(f);\n\t\t\t\t}\n\t\t\t\t_Remove(pa);\n\t\t\t}\n\t\t}\n\t\tif (added.Any()) {\n\t\t\tforeach (var x in added) {\n\t\t\t\t//try to add near the default place\n\t\t\t\tstring sAppend = null;\n\t\t\t\t//print.qm2.use=true;\n\t\t\t\t//print.clear();\n\t\t\t\tvar byDef = x.Parent.Elements().LastOrDefault(o => o != x && o.Name.LocalName is not (\"stack\" or \"tab\"));\n\t\t\t\tif (byDef != null && rootStack.Desc(byDef.Name, \"name\", byDef.Attribute(\"name\").Value) is { } by) {\n\t\t\t\t\tbool inTab = x.Parent.Name == \"tab\";\n\t\t\t\t\tif (inTab && by.Parent.Name != \"tab\") {\n\t\t\t\t\t\tvar tab = new XElement(\"tab\", x);\n\t\t\t\t\t\tif (by.Attribute(\"z\") is { } a1) { a1.Remove(); tab.SetAttributeValue(\"z\", a1.Value); }\n\t\t\t\t\t\tif (by.Attribute(\"headerAt\") is { } a2) tab.SetAttributeValue(\"headerAt\", a2.Value);\n\t\t\t\t\t\tby.AddAfterSelf(tab);\n\t\t\t\t\t\tby.Remove();\n\t\t\t\t\t\ttab.AddFirst(by);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (x.Parent.Name == \"stack\" && byDef.Parent.Name == \"stack\" && x.Parent.Attr(\"o\") != by.Parent.Attr(\"o\") && x.Parent.Elements().Count() == 2) {\n\t\t\t\t\t\t\tby.AddAfterSelf(x.Parent);\n\t\t\t\t\t\t\tby.Remove();\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tif (!inTab) x.SetAttributeValue(\"z\", 50); else x.SetAttributeValue(\"z\", null);\n\t\t\t\t\t\t\tby.AddAfterSelf(x);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tstring s1 = by.Name.LocalName; if (s1 == \"document\") s1 = \"panel\";\n\t\t\t\t\tsAppend = $\"next to the {by.Attr(\"name\")} {s1}. You can move it to a better place (right-click...){(inTab ? \"\" : \" or/and resize\")}.\";\n\t\t\t\t} else {\n\t\t\t\t\tx.SetAttributeValue(\"z\", 50); //note: set even if was no \"z\", because maybe was in a tab\n\t\t\t\t\trootStack.Add(x);\n\t\t\t\t\tsAppend = \"at the bottom of the window. Right-click its header and move it to a better place.\";\n\t\t\t\t}\n\t\t\t\tstring sAppend2 = x.Attr(out int state, \"state\") && 0 != (state & 1) ? \"\\r\\n\\tNow it's hidden. To show, right-click any panel header.\" : null;\n\t\t\t\tprint.it($\"<>New {x.Name} in this app version: <b>{x.Attr(\"name\")}<>.\\r\\n\\tIt's {sAppend}{sAppend2}\");\n\t\t\t}\n\t\t}\n\t\t//print.it(rootStack);\n\t}\n\t\n\t///// <summary>\n\t///// Deletes the user's saved layout file. Then next time will use the default file.\n\t///// </summary>\n\t//public void DeleteSavedFile() {\n\t//\tfilesystem.delete(_xmlFile);\n\t//}\n\t\n\t/// <summary>\n\t/// Action that adds the root node (Grid) to a container (for example Window), like <c>_panels.Container = g => this.Content = g;</c>.\n\t/// The action is called immediately and also may be called later if need to create new root element when moving a panel etc.\n\t/// </summary>\n\tpublic Action<Grid> Container {\n\t\tget => _setContainer;\n\t\tset {\n\t\t\t_setContainer = value;\n\t\t\tvar e = _rootStack.Elem as Grid;\n\t\t\tif (e.Parent == null) _setContainer(e);\n\t\t}\n\t}\n\tAction<Grid> _setContainer;\n\t\n\t//public Grid RootElem => _rootStack.Elem as Grid;\n\t\n\t/// <summary>\n\t/// Gets interface of a leaf item (panel, toolbar or document).\n\t/// </summary>\n\t/// <value>null if not found.</value>\n\t/// <param name=\"name\"></param>\n\t/// <param name=\"userDocument\">It is a document (not panel/toolbar) added with <see cref=\"ILeaf.AddSibling\"/>. User documents are in a separate dictionary, to avoid name conflicts.</param>\n\tpublic ILeaf this[string name, bool userDocument = false] {\n\t\tget {\n\t\t\tvar d = userDocument ? _dictUserDoc : _dictLeaf;\n\t\t\td.TryGetValue(name, out var v);\n\t\t\treturn v;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Gets interface of container leaf item (panel, toolbar or document).\n\t/// </summary>\n\t/// <param name=\"e\">Leaf's <b>Content</b> or any descendant.</param>\n\t/// <exception cref=\"NotFoundException\"></exception>\n\tpublic ILeaf this[DependencyObject e] {\n\t\tget {\n\t\t\twhile (e != null) {\n\t\t\t\tif (e is _DockPanelWithBorder d && d.Tag is ILeaf f) return f;\n\t\t\t\te = VisualTreeHelper.GetParent(e); //same with LogicalTreeHelper\n\t\t\t}\n\t\t\tthrow new NotFoundException();\n\t\t}\n\t}\n\t\n\tpublic ILeaf AddNewExtension(bool toolbar, string name, ILeaf where = null, bool after = false) {\n\t\tif (name.NE()) throw new ArgumentException();\n\t\tif (where == null) {\n\t\t\twhere = _rootStack.LastChild as ILeaf;\n\t\t\tafter = true;\n\t\t\tprint.it($\"Info: added new {(toolbar ? \"toolbar\" : \"panel\")} {name}. It is at the bottom of the window. Right-click its header and move it to a better place.\");\n\t\t}\n\t\treturn where.AddSibling(after, toolbar ? LeafType.Toolbar : LeafType.Panel, name, canClose: false, isExtension: true);\n\t}\n\t\n\t//rejected. Rarely used. Can set in Container action.\n\t///// <summary>\n\t///// Background brush of the root grid.\n\t///// Set before <see cref=\"Load\"/>.\n\t///// </summary>\n\t//public Brush Background {\n\t//\tget => _gridBackground;\n\t//\tset => _gridBackground = value;\n\t//}\n\t\n\t/// <summary>\n\t/// Background brush of panel/document header and tab strip. Default Brushes.LightSteelBlue.\n\t/// Set before <see cref=\"Load\"/>.\n\t/// </summary>\n\tpublic Brush HeaderBrush { get; set; } = Brushes.LightSteelBlue;\n\t\n\t//rejected. Looks ugly when different color, unless white.\n\t///// <summary>\n\t///// Background brush of tab strip. Default Brushes.LightSteelBlue.\n\t///// Set before <see cref=\"Load\"/>.\n\t///// </summary>\n\t//public Brush TabBrush { get; set; } = Brushes.LightSteelBlue;\n\t\n\t/// <summary>\n\t/// Background brush of splitters.\n\t/// Set before <see cref=\"Load\"/>.\n\t/// </summary>\n\tpublic Brush SplitterBrush { get; set; }\n\t\n\t/// <summary>\n\t/// Border color of panels and documents.\n\t/// Set before <see cref=\"Load\"/>.\n\t/// If not set, no borders will be added.\n\t/// </summary>\n\tpublic Brush BorderBrush { get; set; }\n\t\n\t/// <summary>\n\t/// Gets top-level window, for example to use as owner of menus/dialogs.\n\t/// Note: it may not be direct container of the root element.\n\t/// </summary>\n\tWindow _ContainerWindow => _window ??= Window.GetWindow(_rootStack.Elem);\n\tWindow _window;\n\t\n\tclass _XmlNameAttrComparer : IEqualityComparer<XElement> {\n\t\tpublic bool Equals(XElement x, XElement y) => x.Attr(\"name\") == y.Attr(\"name\");\n\t\tpublic int GetHashCode(XElement obj) => obj.Attr(\"name\").GetHashCode();\n\t\t//fast, same as with XName _name.\n\t}\n}\n"
  },
  {
    "path": "Au.Controls/KPanels/_Floating.cs",
    "content": "using System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Controls.Primitives;\nusing System.Windows.Media;\nusing System.Windows.Interop;\nusing System.Windows.Input;\n\nnamespace Au.Controls;\n\npublic partial class KPanels {\n\tpartial class _Node {\n\t\tclass _Floating : Window {\n\t\t\treadonly _Node _node;\n\t\t\treadonly Window _owner;\n\t\t\treadonly bool _noActivate;\n\t\t\t\n\t\t\tpublic _Floating(_Node node, bool onDrag) {\n\t\t\t\t_node = node;\n\t\t\t\t_owner = _node._pm._ContainerWindow;\n\t\t\t\t_noActivate = _node._IsToolbarsNode || _node.DontActivateFloating != null;\n\t\t\t\t\n\t\t\t\t//workaround for: if a HwndHost-ed native control in this panel is focused, WPF may activate the floating panel and disable next mouse click\n\t\t\t\tif (Keyboard.FocusedElement == null) {\n\t\t\t\t\tvar wFocus = Api.GetFocus();\n\t\t\t\t\tif (!wFocus.Is0 && wFocus.IsChildOf(_owner.Hwnd())) {\n\t\t\t\t\t\tif (null != _node.Elem.FindVisualDescendant(o => o is HwndHost h && h.Handle == wFocus.Handle)) _owner.Focus();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvar style = WS.THICKFRAME | WS.POPUP | WS.CLIPCHILDREN;\n\t\t\t\tif (_node._IsStack) style |= WS.CAPTION;\n\t\t\t\tbool unowned = _node._windowStyle.Has(_WindowStyle.Unowned);\n\t\t\t\tif (unowned) style |= WS.CAPTION | WS.MAXIMIZEBOX | WS.MINIMIZEBOX | WS.SYSMENU;\n\t\t\t\tvar estyle = unowned ? WSE.APPWINDOW | WSE.WINDOWEDGE : /*WSE.TOOLWINDOW |*/ WSE.WINDOWEDGE; //note: don't use WS_EX_TOOLWINDOW, because of Win11 bug: no WM_DPICHANGED when screen DPI changes; tested workarounds don't work well.\n\t\t\t\t\n\t\t\t\tRECT rect = default;\n\t\t\t\tbool defaultRect = onDrag | (_node._floatSavedRect == null);\n\t\t\t\tif (defaultRect) {\n\t\t\t\t\tvar c2 = _node._ParentIsTab ? _node.Parent._elem : _node._elem;\n\t\t\t\t\tif (c2.IsVisible) {\n\t\t\t\t\t\trect = c2.RectInScreen();\n\t\t\t\t\t\tDpi.AdjustWindowRectEx(c2, ref rect, style, estyle);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tvar p = mouse.xy;\n\t\t\t\t\t\trect = (p.x - 10, p.y - 10, 200, 200);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tbase.SourceInitialized += (_, _) => {\n\t\t\t\t\tvar w = this.Hwnd();\n\t\t\t\t\tw.SetStyle(style);\n\t\t\t\t\tif (_noActivate) w.SetExStyle(WSE.NOACTIVATE, WSFlags.Add);\n\t\t\t\t};\n\t\t\t\t\n\t\t\t\tbase.Title = script.name + \" - \" + _node.ToString();\n\t\t\t\tbase.WindowStartupLocation = WindowStartupLocation.Manual;\n\t\t\t\tbase.ShowActivated = false;\n\t\t\t\tif (!unowned) {\n\t\t\t\t\tif (!_node._windowStyle.Has(_WindowStyle.Topmost)) base.Owner = _owner;\n\t\t\t\t\t/*base.WindowStyle = WindowStyle.ToolWindow;*/\n\t\t\t\t\tbase.ShowInTaskbar = false; //never mind: if false, WPF creates a \"Hidden Window\", probably as owner, even if owner specified\n\t\t\t\t}\n\t\t\t\tif (_node._windowStyle.Has(_WindowStyle.Topmost)) base.Topmost = true;\n\t\t\t\t\n\t\t\t\tif (defaultRect) this.SetRect(rect);\n\t\t\t\telse WndSavedRect.Restore(this, _node._floatSavedRect);\n\t\t\t\t\n\t\t\t\t_owner.Closing += _Owner_Closing;\n\t\t\t\t_owner.IsVisibleChanged += _Owner_IsVisibleChanged;\n\t\t\t}\n\t\t\t\n\t\t\tpublic void ShowIfOwnerVisible() {\n\t\t\t\tif (_owner.IsVisible) Show();\n\t\t\t}\n\t\t\t\n\t\t\tprivate void _Owner_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e) {\n\t\t\t\t//never mind: no event if closed from outside.\n\t\t\t\tbool visible = (bool)e.NewValue;\n\t\t\t\t//print.it(\"owner visible\", visible);\n\t\t\t\tif (visible) Show(); else Hide();\n\t\t\t}\n\t\t\t\n\t\t\tprivate void _Owner_Closing(object sender, CancelEventArgs e) {\n\t\t\t\t//print.it(\"owner closing\", e.Cancel);\n\t\t\t\tif (!e.Cancel) this.Close();\n\t\t\t}\n\t\t\t\n\t\t\tprotected override void OnClosing(CancelEventArgs e) {\n\t\t\t\t//print.it(\"closing\", e.Cancel, _node._state);\n\t\t\t\tif (!e.Cancel) {\n\t\t\t\t\t_owner.IsVisibleChanged -= _Owner_IsVisibleChanged;\n\t\t\t\t\tSave();\n\t\t\t\t\tContent = null;\n\t\t\t\t\t_node._floatWindow = null;\n\t\t\t\t}\n\t\t\t\tbase.OnClosing(e);\n\t\t\t}\n\t\t\t\n\t\t\tpublic void Save() {\n\t\t\t\t//print.it(\"save\");\n\t\t\t\t_node._floatSavedRect = new WndSavedRect(this).ToString();\n\t\t\t}\n\t\t\t\n\t\t\tprotected override void OnSourceInitialized(EventArgs e) {\n\t\t\t\tbase.OnSourceInitialized(e);\n\t\t\t\tvar hs = PresentationSource.FromVisual(this) as HwndSource;\n\t\t\t\ths.AddHook(WndProc);\n\t\t\t}\n\t\t\t\n\t\t\tprivate IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) {\n\t\t\t\tvar w = (wnd)hwnd;\n\t\t\t\t//WndUtil.PrintMsg(w, msg, wParam, lParam);\n\t\t\t\tswitch (msg) {\n\t\t\t\tcase Api.WM_MOUSEACTIVATE:\n\t\t\t\t\tbool no = Math2.LoWord(lParam) != Api.HTCLIENT;\n\t\t\t\t\tif (!no) {\n\t\t\t\t\t\tif (_noActivate) { //activate if clicked a focusable element\n\t\t\t\t\t\t\tvar ie = Mouse.DirectlyOver;\n\t\t\t\t\t\t\tif (ie == null) { //native child control?\n\t\t\t\t\t\t\t\tif (_node.DontActivateFloating != null) {\n\t\t\t\t\t\t\t\t\tvar c = wnd.fromMouse(WXYFlags.Raw);\n\t\t\t\t\t\t\t\t\tif (c != w && !c.Is0) {\n\t\t\t\t\t\t\t\t\t\tif (wnd.Internal_.ToWpfElement(c) is { } e) no = _node.DontActivateFloating(e);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tno = true;\n\t\t\t\t\t\t\t\tfor (var e = ie as UIElement; e != null && e != _node._elem; e = VisualTreeHelper.GetParent(e) as UIElement) {\n\t\t\t\t\t\t\t\t\t//print.it(e, e.Focusable);\n\t\t\t\t\t\t\t\t\tif (e.Focusable) {\n\t\t\t\t\t\t\t\t\t\tif (e is ButtonBase) {\n\t\t\t\t\t\t\t\t\t\t\t//e.Focusable = false;\n\t\t\t\t\t\t\t\t\t\t\t//Dispatcher.InvokeAsync(() => e.Focusable = true);\n\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\tno = _node.DontActivateFloating?.Invoke(e) == true;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else if (!_node._IsStack) { //activate if clicked not panel header and not tab header\n\t\t\t\t\t\t\tFrameworkElement e;\n\t\t\t\t\t\t\tif (_node._IsTab) e = (_node._tab.tc.SelectedItem as TabItem)?.Content as FrameworkElement;\n\t\t\t\t\t\t\telse e = _node._leaf.content;\n\t\t\t\t\t\t\tno = e != null && !e.RectInScreen().Contains(mouse.xy);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (no) {\n\t\t\t\t\t\t//print.it(\"MA_NOACTIVATE\");\n\t\t\t\t\t\tw.ZorderTop();\n\t\t\t\t\t\thandled = true;\n\t\t\t\t\t\treturn (IntPtr)Api.MA_NOACTIVATE;\n\t\t\t\t\t\t//never mind: if clicked or dragged resizable border, on mouse up OS activates the window.\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase Api.WM_CLOSE:\n\t\t\t\t\tif (w.IsMinimized) w.ShowNotMinimized(); //without this would be exception (why?)\n\t\t\t\t\tif (_node._state == _DockState.Float) { //closing not by _SetDockState\n\t\t\t\t\t\thandled = true;\n\t\t\t\t\t\t_node._SetDockState(0);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\treturn default;\n\t\t\t}\n\t\t\t\n\t\t\t//Currently supports only moving but not docking. Docking is implemented in _ContextMenu_Move+_MoveTo.\n\t\t\tpublic void Drag(POINT p) {\n\t\t\t\t\n\t\t\t\t//bool canDock = false;\n\t\t\t\t//_DockTarget target = null;\n\t\t\t\tvar w = this.Hwnd();\n\t\t\t\tRECT r = w.Rect;\n\t\t\t\tPOINT offs = (p.x - r.left, p.y - r.top);\n\t\t\t\tbool ok = WndUtil.DragLoop(w, MButtons.Left, d => {\n\t\t\t\t\tif (d.msg.message != Api.WM_MOUSEMOVE) return;\n\t\t\t\t\t\n\t\t\t\t\tp = mouse.xy;\n\t\t\t\t\tw.MoveL(p.x - offs.x, p.y - offs.y);\n\t\t\t\t\t\n\t\t\t\t\t//if (!canDock && keys.gui.isAlt) {\n\t\t\t\t\t//\tcanDock = true;\n\t\t\t\t\t//\t//w.SetTransparency(true, 128);\n\t\t\t\t\t//\t//_dockIndic = new _DockIndicator(_manager, this);\n\t\t\t\t\t//\t//_dockIndic.Show(this);\n\t\t\t\t\t//}\n\t\t\t\t\t////if (canDock) _dockIndic.OnFloatMoved(_manager.PointToClient(p));\n\t\t\t\t});\n\t\t\t\t\n\t\t\t\t//if (canDock) {\n\t\t\t\t//\tw.SetTransparency(false);\n\t\t\t\t//\t_dockIndic.Close();\n\t\t\t\t//\tif (ok) {\n\t\t\t\t//\t\ttarget = _dockIndic.OnFloatDropped();\n\t\t\t\t//\t}\n\t\t\t\t//\t_dockIndic = null;\n\t\t\t\t//}\n\t\t\t\t\n\t\t\t\t//return target;\n\t\t\t}\n\t\t\t\n\t\t\tpublic void ChangeWindowStyle(_WindowStyle style) {\n\t\t\t\tbool on = _node._windowStyle.Has(style);\n\t\t\t\tvar w = this.Hwnd();\n\t\t\t\tif (style == _WindowStyle.Topmost) {\n\t\t\t\t\tbool unowned = _node._windowStyle.Has(_WindowStyle.Unowned);\n\t\t\t\t\tif (on) {\n\t\t\t\t\t\tif (!unowned) base.Owner = null;\n\t\t\t\t\t\tw.ZorderTopmost();\n\t\t\t\t\t} else {\n\t\t\t\t\t\tw.ZorderNoTopmost();\n\t\t\t\t\t\tif (!unowned) base.Owner = _owner;\n\t\t\t\t\t}\n\t\t\t\t} else if (style == _WindowStyle.Unowned) {\n\t\t\t\t\tif (!on) w.ShowNotMinMax();\n\t\t\t\t\tbool topmost = _node._windowStyle.Has(_WindowStyle.Topmost);\n\t\t\t\t\tif (!topmost) base.Owner = on ? null : _owner; //or `WndUtil.SetOwnerWindow(w, on ? default : _owner.Hwnd());`\n\t\t\t\t\tw.SetExStyle(WSE.APPWINDOW, on ? WSFlags.Add : WSFlags.Remove); //w.SetExStyle(w.ExStyle & ~(WSE.TOOLWINDOW | WSE.APPWINDOW) | (on ? WSE.APPWINDOW : WSE.TOOLWINDOW));\n\t\t\t\t\tvar ws = WS.CAPTION | WS.MAXIMIZEBOX | WS.MINIMIZEBOX | WS.SYSMENU;\n\t\t\t\t\tif (!on && _node._IsStack) ws &= ~WS.CAPTION;\n\t\t\t\t\tw.SetStyle(ws, on ? WSFlags.Add | WSFlags.UpdateNonclient : WSFlags.Remove | WSFlags.UpdateNonclient);\n\t\t\t\t\tbase.ShowInTaskbar = on;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic Func<UIElement, bool> DontActivateFloating { get; set; }\n\t}\n}\n"
  },
  {
    "path": "Au.Controls/KPanels/_Node.cs",
    "content": "using System.Xml.Linq;\nusing System.Xml;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Media;\nusing System.Windows.Shapes;\n\nnamespace Au.Controls;\n\npublic partial class KPanels {\n\tpartial class _Node : TreeBase<_Node>, ILeaf {\n\t\treadonly KPanels _pm;\n\t\treadonly _StackFields _stack;\n\t\treadonly _TabFields _tab;\n\t\treadonly _LeafFields _leaf;\n\t\treadonly FrameworkElement _elem; //_stack.grid or _tab.tc or _leaf.panel\n\t\tGridSplitter2 _splitter; //splitter before this node in stack. null if in tab or first in stack\n\t\treadonly LeafType _leafType;\n\t\tint _index; //index in parent stack or tab. Grid row/column index is _index*2, because at _index*2-1 is splitter if _index>0.\n\t\tGridLength _dockedSize;\n\t\tDock _headerAt;\n\t\t_DockState _state, _savedDockState;\n\t\t_Floating _floatWindow;\n\t\tstring _floatSavedRect;\n\t\t_WindowStyle _windowStyle;\n\t\t//_Flags _flags;\n\t\tbool _dontSave;\n\t\t\n\t\tstatic readonly Brush s_toolbarHeaderBrush = SystemColors.ControlBrush;\n\t\tconst int c_minSize = 4;\n\t\tconst int c_defaultSplitterSize = 4;\n\t\t\n\t\tclass _StackFields {\n\t\t\tpublic Grid grid;\n\t\t\tpublic bool isVertical; //vertical stack with horizontal splitters\n\t\t}\n\t\t\n\t\tclass _TabFields {\n\t\t\tpublic _TabControl tc;\n\t\t\tpublic bool isVerticalHeader; //vertical buttons at left/right\n\t\t}\n\t\t\n\t\tclass _LeafFields {\n\t\t\tpublic _DockPanelWithBorder panel;\n\t\t\tpublic FrameworkElement content; //app sets it = any element\n\t\t\tpublic FrameworkElement header; //TextBlock if panel/userdocument, Rectangle if toolbar/documentplaceholder. null if in tab.\n\t\t\tpublic string name; //used by the indexer to find it, also as header/tabitem text\n\t\t\tpublic bool addedLater; //added with AddSibling\n\t\t\tpublic bool canClose; //AddSibling(canClose). Adds context menu item \"Close\".\n\t\t\tpublic bool isExtension; //AddSibling(isExtension). Saves.\n\t\t}\n\t\t\n\t\t[Flags]\n\t\tenum _Flags { Splitter_ResizeNearest = 1 }\n\t\t\n\t\t/// <summary>\n\t\t/// Used to create root node when loading from XML.\n\t\t/// </summary>\n\t\tpublic _Node(KPanels pm, XElement x) : this(pm, x, null, 0) { }\n\t\t\n\t\t/// <summary>\n\t\t/// Used to create nodes when loading from XML.\n\t\t/// </summary>\n\t\t_Node(KPanels pm, XElement x, _Node parent, int index) {\n\t\t\t_pm = pm;\n\t\t\t_index = index;\n\t\t\t\n\t\t\tstring tag = x.Name.LocalName;\n\t\t\t\n\t\t\tif (parent == null) { //the root XML element\n\t\t\t\tif (tag != \"stack\") throw new ArgumentException(\"XML root element must be 'stack'\");\n\t\t\t\t_pm._rootStack = this;\n\t\t\t} else {\n\t\t\t\tparent.AddChild(this);\n\t\t\t}\n\t\t\t\n\t\t\tswitch (tag) {\n\t\t\tcase \"stack\":\n\t\t\t\t_stack = new _StackFields { isVertical = x.Attr(\"o\") == \"v\" };\n\t\t\t\t_elem = _stack.grid = new Grid();\n\t\t\t\tbreak;\n\t\t\tcase \"tab\":\n\t\t\t\t_tab = new();\n\t\t\t\t_elem = _tab.tc = new _TabControl();\n\t\t\t\tbreak;\n\t\t\tcase \"panel\":\n\t\t\tcase \"toolbar\":\n\t\t\tcase \"document\":\n\t\t\t\t_leaf = new();\n\t\t\t\t_elem = _leaf.panel = new();\n\t\t\t\t_leafType = tag[0] switch { 'p' => LeafType.Panel, 't' => LeafType.Toolbar, _ => LeafType.Document };\n\t\t\t\t_leaf.name = x.Attr(\"name\") ?? throw new ArgumentException(\"XML element without 'name'\");\n\t\t\t\t_leaf.isExtension = x.HasAttr(\"ext\");\n\t\t\t\t_Dictionary.Add(_leaf.name, this);\n\t\t\t\tbreak;\n\t\t\tdefault: throw new ArgumentException(\"unknown XML tag: \" + tag);\n\t\t\t}\n\t\t\t_elem.UseLayoutRounding = true;\n\t\t\t_elem.Tag = this;\n\t\t\t\n\t\t\tif (parent != null) {\n\t\t\t\tif (!_IsDocument) {\n\t\t\t\t\t_savedDockState = (_DockState)(x.Attr(\"state\", 0) & 3);\n\t\t\t\t\t_floatSavedRect = x.Attr(\"floatRect\");\n\t\t\t\t\tx.Attr(out _windowStyle, \"window\");\n\t\t\t\t}\n\t\t\t\tif (!_IsStack) x.Attr(out _headerAt, \"headerAt\");\n\t\t\t\t\n\t\t\t\tif (_ParentIsStack) {\n\t\t\t\t\t_dockedSize = _GridLengthFromString(x.Attr(\"z\")); //height in vertical stack or width in horizontal stack\n\t\t\t\t\t_AddToStack(moving: false, _index == 0 ? 0 : x.Attr(\"s\", c_defaultSplitterSize));\n\t\t\t\t\t\n\t\t\t\t\tvar flags = (_Flags)x.Attr(\"flags\", 0);\n\t\t\t\t\tif (flags.Has(_Flags.Splitter_ResizeNearest) && _splitter != null) _splitter.ResizeNearest = true;\n\t\t\t\t\t\n\t\t\t\t\tif (_IsTab) _InitTabControl();\n\t\t\t\t} else {\n\t\t\t\t\t_AddToTab(moving: false);\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tif (!_IsLeaf) { //stack or tab\n\t\t\t\tif (_ParentIsTab) throw new ArgumentException(tag + \" in 'tab'\");\n\t\t\t\tint i = 0;\n\t\t\t\tforeach (var e in x.Elements()) {\n\t\t\t\t\tnew _Node(_pm, e, this, i++);\n\t\t\t\t}\n\t\t\t\tDebug.Assert(i > 0);\n\t\t\t\t\n\t\t\t\tif (_IsTab && i > 0) {\n\t\t\t\t\t_tab.tc.SelectedIndex = Math.Clamp(x.Attr(\"active\", 0), 0, i - 1);\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t//print.it(new string('\\t', parent?.Level ?? 0) + _ntype, _ptype, Name, _indexInParent);\n\t\t\t\n\t\t\tif (parent == null) { //the root XML element\n\t\t\t\tint nVisible = 0; _Node firstHidden = null;\n\t\t\t\tList<_Node> aFloat = null;\n\t\t\t\tforeach (var v in Descendants()) {\n\t\t\t\t\tif (!v._IsStack) if (v._IsVisibleReally(true)) nVisible++; else firstHidden ??= v;\n\t\t\t\t\tvar ds = v._savedDockState;\n\t\t\t\t\tif (ds == _DockState.Float) (aFloat ??= new()).Add(v);\n\t\t\t\t\telse if (ds != 0) v._SetDockState(ds);\n\t\t\t\t}\n\t\t\t\tif (nVisible == 0 && firstHidden != null) { //if all non-stack hidden, unhide one, else user cannot unhide any because there are no headers to show the context menu\n\t\t\t\t\tfirstHidden._SetDockState(0);\n\t\t\t\t}\n\t\t\t\tif (aFloat != null) {\n\t\t\t\t\tDependencyPropertyChangedEventHandler eh = null;\n\t\t\t\t\teh = (_, e) => {\n\t\t\t\t\t\tif (e.NewValue is bool visible && visible) {\n\t\t\t\t\t\t\t_stack.grid.IsVisibleChanged -= eh;\n\t\t\t\t\t\t\t_stack.grid.Dispatcher.InvokeAsync(() => {\n\t\t\t\t\t\t\t\tforeach (var v in aFloat) {\n\t\t\t\t\t\t\t\t\tif (v._state == _DockState.Hide) { v._state |= _DockState.Float; continue; } //hidden after loading\n\t\t\t\t\t\t\t\t\tv._SetDockState(_DockState.Float);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\t\t\t\t\t_stack.grid.IsVisibleChanged += eh;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t//timer.after(1000, _ => _Test(5));\n\t\t\t\t////timer.after(5000, _ => _Test(0));\n\t\t\t\t//void _Test(int margin) {\n\t\t\t\t//\tforeach (var v in Descendants(true)) {\n\t\t\t\t//\t\tif (v._IsStack) v._stack.grid.Background = (v.Level & 3) switch { 0 => Brushes.CornflowerBlue, 1 => Brushes.Khaki, 2 => Brushes.YellowGreen, _ => Brushes.LightYellow };\n\t\t\t\t//\t\tif(v!=this) v._elem.Margin = new Thickness(margin);\n\t\t\t\t//\t\tif (v._splitter != null) v._splitter.Visibility = Visibility.Collapsed;\n\t\t\t\t//\t}\n\t\t\t\t//}\n\t\t\t\t\n\t\t\t\t////_stack.grid.PreviewMouseMove += _RootGrid_PreviewMouseMove;\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Used when moving a node, to create new parent (this) stack or tab for it and target.\n\t\t/// Also when moving a node, to create new parent (this) tab for it (target).\n\t\t/// </summary>\n\t\t_Node(_Node target, bool isTab, bool verticalStack = false) {\n\t\t\t_pm = target._pm;\n\t\t\t\n\t\t\tbool targetIsRoot = !isTab && target.Parent == null;\n\t\t\tif (targetIsRoot) {\n\t\t\t\t_pm._rootStack = this;\n\t\t\t} else {\n\t\t\t\t_index = target._index;\n\t\t\t\ttarget.AddSibling(this, after: false);\n\t\t\t\ttarget.Remove();\n\t\t\t\ttarget._index = 0;\n\t\t\t}\n\t\t\tAddChild(target);\n\t\t\t\n\t\t\tif (isTab) {\n\t\t\t\t_tab = new();\n\t\t\t\t_elem = _tab.tc = new _TabControl();\n\t\t\t} else {\n\t\t\t\t_stack = new _StackFields { isVertical = verticalStack };\n\t\t\t\t_elem = _stack.grid = new Grid();\n\t\t\t}\n\t\t\t_elem.Tag = this;\n\t\t\t\n\t\t\tif (targetIsRoot) {\n\t\t\t\t//target._dockedSize = ...; //_AddToParentWhenMovingOrAddingLater will set it for target and this\n\t\t\t\t_pm._setContainer(_stack.grid);\n\t\t\t} else {\n\t\t\t\t_ReplaceInStack(target);\n\t\t\t\tif (isTab) {\n\t\t\t\t\t_headerAt = target._headerAt;\n\t\t\t\t\t_InitTabControl();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Used when creating new leaf node later (after loading).\n\t\t/// </summary>\n\t\t_Node(_Node target, bool after, LeafType type, string name, bool canClose, bool isExtension) {\n\t\t\t_pm = target._pm;\n\t\t\t_leaf = new() { addedLater = true, name = name, canClose = canClose, isExtension = isExtension };\n\t\t\t_elem = _leaf.panel = new() { Tag = this, UseLayoutRounding = true };\n\t\t\t_leafType = type;\n\t\t\t_dontSave = !isExtension;\n\t\t\t_Dictionary.Add(name, this);\n\t\t\t_AddToParentWhenMovingOrAddingLater(target, after);\n\t\t}\n\t\t\n\t\tpublic void Save(XmlWriter x) {\n\t\t\tif (Parent == null) { //mark to not save stack/tab nodes without savable leaf descendants\n\t\t\t\t_Children(this);\n\t\t\t\tstatic bool _Children(_Node p) {\n\t\t\t\t\tbool R = false;\n\t\t\t\t\tforeach (var v in p.Children()) {\n\t\t\t\t\t\tif (!v._IsLeaf) v._dontSave = !_Children(v);\n\t\t\t\t\t\tR |= !v._dontSave;\n\t\t\t\t\t}\n\t\t\t\t\treturn R;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (_dontSave) return;\n\t\t\t}\n\t\t\t\n\t\t\tx.WriteStartElement(_IsStack ? \"stack\" : (_IsTab ? \"tab\" : (_IsToolbar ? \"toolbar\" : (_IsDocument ? \"document\" : \"panel\"))));\n\t\t\t\n\t\t\tif (_IsStack) x.WriteAttributeString(\"o\", _stack.isVertical ? \"v\" : \"h\");\n\t\t\t\n\t\t\tif (Parent != null) {\n\t\t\t\tif (_IsLeaf) {\n\t\t\t\t\tx.WriteAttributeString(\"name\", _leaf.name);\n\t\t\t\t\tif (_leaf.isExtension) x.WriteAttributeString(\"ext\", \"\");\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (_ParentIsStack) {\n\t\t\t\t\tif (!_dockedSize.IsAuto) {\n\t\t\t\t\t\tif (_IsDockedInStack) _dockedSize = _SizeDef; //update _dockedSize\n\t\t\t\t\t\tx.WriteAttributeString(\"z\", _GridLengthToString(_dockedSize));\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tvar z = _SplitterSize;\n\t\t\t\t\tif (z > 0 && z != c_defaultSplitterSize) x.WriteAttributeString(\"s\", z.ToString());\n\t\t\t\t\t\n\t\t\t\t\tif (_splitter != null && _splitter.ResizeNearest) x.WriteAttributeString(\"flags\", ((int)_Flags.Splitter_ResizeNearest).ToString());\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (_IsTab) {\n\t\t\t\t\tint i = _tab.tc.SelectedIndex;\n\t\t\t\t\tif (i > 0) x.WriteAttributeString(\"active\", i.ToString());\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (_headerAt != 0) x.WriteAttributeString(\"headerAt\", _headerAt.ToString());\n\t\t\t\tif (!_IsDocument) {\n\t\t\t\t\tif (_state != 0) x.WriteAttributeString(\"state\", ((int)_state).ToString());\n\t\t\t\t\t_floatWindow?.Save();\n\t\t\t\t\tif (_floatSavedRect != null) x.WriteAttributeString(\"floatRect\", _floatSavedRect);\n\t\t\t\t\tif (_windowStyle != 0) x.WriteAttributeString(\"window\", _windowStyle.ToString());\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tif (!_IsLeaf) foreach (var v in Children()) v.Save(x);\n\t\t\t\n\t\t\tx.WriteEndElement();\n\t\t}\n\t\t\n\t\tbool _IsStack => _stack != null;\n\t\tbool _IsTab => _tab != null;\n\t\tbool _IsLeaf => _leaf != null;\n\t\tbool _IsPanel => _leafType == LeafType.Panel;\n\t\tbool _IsToolbar => _leafType == LeafType.Toolbar;\n\t\tbool _IsDocument => _leafType == LeafType.Document;\n\t\tbool _ParentIsStack => Parent?._IsStack ?? false;\n\t\tbool _ParentIsTab => Parent?._IsTab ?? false;\n\t\tbool _ParentIsTabAndNotFloating => _ParentIsTab && _state != _DockState.Float;\n\t\t\n\t\t/// <summary>\n\t\t/// true if this is toolbar or this is stack/tab containing only toolbars.\n\t\t/// </summary>\n\t\tbool _IsToolbarsNode => _IsToolbar || (!_IsLeaf && Descendants().All(o => o._IsToolbar));\n\t\t\n\t\t/// <summary>\n\t\t/// true if this is document or this is tab containing documents (or tab with 0 children, which normally is not possible).\n\t\t/// </summary>\n\t\tbool _IsDocumentsNode => _IsDocument || (_IsTab && (FirstChild?._IsDocument ?? true));\n\t\t\n\t\tDictionary<string, _Node> _Dictionary => (_leaf.addedLater && _IsDocument) ? _pm._dictUserDoc : _pm._dictLeaf;\n\t\t\n\t\t/// <summary>\n\t\t/// Gets name of panel/toolbar/document. Exception if not leaf.\n\t\t/// </summary>\n\t\tpublic string Name => _leaf.name;\n\t\t\n\t\t/// <summary>\n\t\t/// Gets the UI element of this node. It is Grid if this is stack, or TabControl if tab, else DockPanel.\n\t\t/// </summary>\n\t\tpublic FrameworkElement Elem => _elem;\n\t\t\n\t\tpublic override string ToString() {\n\t\t\tstring s;\n\t\t\tif (_IsLeaf) {\n\t\t\t\ts = Name;\n\t\t\t\tif (_IsToolbar) s = \"tb \" + Name;\n\t\t\t\treturn s;\n\t\t\t}\n\t\t\tif (Parent == null) return \"Root stack\";\n\t\t\tvar a = Descendants().Where(o => o._IsLeaf).ToArray();\n\t\t\ts = a.Length switch {\n\t\t\t\t0 => \"\",\n\t\t\t\t1 => a[0].ToString(),\n\t\t\t\t2 => a[0].ToString() + \", \" + a[1].ToString(),\n\t\t\t\t_ => a[0].ToString() + \" ... \" + a[^1].ToString()\n\t\t\t};\n\t\t\treturn (_IsTab ? \"Tabs {\" : \"Stack {\") + s + \"}\";\n\t\t}\n\t\t\n\t\t//string _ToStringWithoutTB() {\n\t\t//\tvar s = ToString();\n\t\t//\tif (_IsToolbar) s = s[3..];\n\t\t//\treturn s;\n\t\t//}\n\t\t\n\t\t/// <summary>\n\t\t/// true if _IsPanel or (_IsDocument and _leaf.addedLater).\n\t\t/// </summary>\n\t\tbool _CanHaveHeaderWithText => _IsPanel || (_IsDocument && _leaf.addedLater);\n\t\t\n\t\tvoid _SetHeaderAt(Dock ca, bool firstTime = false) {\n\t\t\t//if (_ParentIsTab) {\n\t\t\t//\tParent._SetHeaderAt(ca, firstTime);\n\t\t\t//\treturn;\n\t\t\t//}\n\t\t\tDock old = firstTime ? Dock.Top : _headerAt;\n\t\t\t_headerAt = ca;\n\t\t\tif (_IsTab) {\n\t\t\t\tvar tc = _tab.tc;\n\t\t\t\tif (ca == tc.TabStripPlacement) return;\n\t\t\t\tif (_tab.isVerticalHeader) {\n\t\t\t\t\t_tab.isVerticalHeader = false;\n\t\t\t\t\tforeach (TabItem v in tc.Items) v.Style = null;\n\t\t\t\t}\n\t\t\t\ttc.TabStripPlacement = ca;\n\t\t\t\t_VerticalTabHeader();\n\t\t\t} else if (_leaf.header != null) {\n\t\t\t\tDockPanel.SetDock(_leaf.header, ca);\n\t\t\t\tif (_leaf.content != null) _SetToolbarOrientation();\n\t\t\t\tif (ca == old || !_CanHaveHeaderWithText) return;\n\t\t\t\tif (ca == Dock.Top || ca == Dock.Bottom) {\n\t\t\t\t\tif (old == Dock.Left || old == Dock.Right) _leaf.header.LayoutTransform = null;\n\t\t\t\t} else {\n\t\t\t\t\t_leaf.header.LayoutTransform = new RotateTransform(ca == Dock.Left ? 270d : 90d);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tvoid _AddRemoveHeaderAndBorder() {\n\t\t\tif (!_IsLeaf) return;\n\t\t\tif (_ParentIsTab && !_state.Has(_DockState.Float)) {\n\t\t\t\tif (_leaf.header != null) {\n\t\t\t\t\t_leaf.panel.Children.Remove(_leaf.header);\n\t\t\t\t\t_leaf.header = null;\n\t\t\t\t\t\n\t\t\t\t\t_leaf.panel.BorderThickness = default;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (_leaf.header == null) {\n\t\t\t\t\tif (_CanHaveHeaderWithText) {\n\t\t\t\t\t\t_leaf.header = new TextBlock {\n\t\t\t\t\t\t\tText = Name,\n\t\t\t\t\t\t\tTextAlignment = TextAlignment.Center,\n\t\t\t\t\t\t\tPadding = new Thickness(2, 1, 2, 3),\n\t\t\t\t\t\t\tBackground = _pm.HeaderBrush,\n\t\t\t\t\t\t\tForeground = Brushes.Black,\n\t\t\t\t\t\t\tTextTrimming = TextTrimming.CharacterEllipsis\n\t\t\t\t\t\t};\n\t\t\t\t\t} else if (_IsToolbar) {\n\t\t\t\t\t\tvar c = new Rectangle {\n\t\t\t\t\t\t\tMinHeight = 5,\n\t\t\t\t\t\t\tMinWidth = 5,\n\t\t\t\t\t\t\tFill = s_toolbarHeaderBrush,\n\t\t\t\t\t\t\t//note: without Fill there are no events\n\t\t\t\t\t\t};\n\t\t\t\t\t\tc.MouseEnter += (_, _) => c.Fill = _pm.HeaderBrush;\n\t\t\t\t\t\tc.MouseLeave += (_, _) => c.Fill = s_toolbarHeaderBrush;\n\t\t\t\t\t\t_leaf.header = c;\n\t\t\t\t\t} else { //document placeholder\n\t\t\t\t\t\tvar c = new Rectangle {\n\t\t\t\t\t\t\t//MinHeight = 5, MinWidth = 5, //rejected. Let be 0 (no header). User could accidentally undock when trying to scroll.\n\t\t\t\t\t\t\t//\tNot tested headerless with tabbed documents. Then probably this code not used because _CanHaveHeaderWithText true.\n\t\t\t\t\t\t\tFill = _pm.HeaderBrush,\n\t\t\t\t\t\t};\n\t\t\t\t\t\t_leaf.header = c;\n\t\t\t\t\t\t\n\t\t\t\t\t\t_leaf.panel.LastChildFill = false;\n\t\t\t\t\t\tbool hasDoc = false;\n\t\t\t\t\t\tc.SizeChanged += (_, e) => {\n\t\t\t\t\t\t\tbool has = _leaf.panel.Children.Count > 1;\n\t\t\t\t\t\t\tif (has != hasDoc) _leaf.panel.LastChildFill = hasDoc = has;\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\t_leaf.panel.Children.Insert(0, _leaf.header);\n\t\t\t\t\t_SetHeaderAt(_headerAt, true);\n\t\t\t\t\t_leaf.header.ContextMenuOpening += _HeaderContextMenu;\n\t\t\t\t\t_leaf.header.MouseDown += _OnMouseDown;\n\t\t\t\t\t\n\t\t\t\t\tif (_pm.BorderBrush != null && !_IsToolbar) {\n\t\t\t\t\t\t_leaf.panel.BorderBrush = _pm.BorderBrush;\n\t\t\t\t\t\t_leaf.panel.BorderThickness = new Thickness(1);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tvoid _SetToolbarOrientation() {\n\t\t\tif (_IsToolbar && _leaf.content is ToolBarTray t) {\n\t\t\t\tvar ori = _headerAt == Dock.Top || _headerAt == Dock.Bottom ? Orientation.Vertical : Orientation.Horizontal;\n\t\t\t\tif (t.Orientation != ori) t.Orientation = ori;\n\t\t\t}\n\t\t}\n\t\t\n\t\t#region ILeaf\n\t\t\n\t\tFrameworkElement ILeaf.Content {\n\t\t\tget => _leaf.content;\n\t\t\tset {\n\t\t\t\tif (_leaf.content != null) _leaf.panel.Children.Remove(_leaf.content);\n\t\t\t\t_leaf.panel.Children.Add(_leaf.content = value);\n\t\t\t\t_SetToolbarOrientation();\n\t\t\t}\n\t\t}\n\t\t\n\t\tbool ILeaf.Visible {\n\t\t\tget => _IsVisibleReally();\n\t\t\tset {\n\t\t\t\tif (value && _savedDockState == _DockState.Float) return; //will make float async\n\t\t\t\tif (value) _Unhide(); else _Hide();\n\t\t\t\tif (value && _ParentIsTabAndNotFloating) {\n\t\t\t\t\tParent._tab.tc.SelectedIndex = _index;\n\t\t\t\t\tif (value && !_elem.IsLoaded) _elem.UpdateLayout(); //workaround: WPF creates HwndHost control handle async. Let's create now.\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Returns true if this node does not have hidden state and is not docked in hidden tab.\n\t\t/// </summary>\n\t\tbool _IsVisibleReally(bool useSavedState = false) {\n\t\t\tvar state = useSavedState ? _savedDockState : _state;\n\t\t\tif (state.Has(_DockState.Hide)) return false;\n\t\t\tif (state == 0 && _ParentIsTab) return Parent._IsVisibleReally(useSavedState);\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\tbool ILeaf.Floating {\n\t\t\tget => _state == _DockState.Float;\n\t\t\tset {\n\t\t\t\tif (value && _savedDockState == _DockState.Float) return; //will make float async\n\t\t\t\t_SetDockState(value ? _DockState.Float : _state & ~_DockState.Float);\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic Action DontFocusTab { get; set; }\n\t\t\n\t\tParentInfo ILeaf.Parent => new ParentInfo(_leaf.panel.Panel, Parent._elem, _index);\n\t\t\n\t\tILeaf ILeaf.AddSibling(bool after, LeafType type, string name, bool canClose, bool isExtension) {\n\t\t\tif (name == null || (type is not (LeafType.Panel or LeafType.Toolbar or LeafType.Document))) throw new ArgumentException();\n\t\t\treturn new _Node(this, after, type, name, canClose, isExtension);\n\t\t}\n\t\t\n\t\tpublic void Delete() {\n\t\t\tif (!_leaf.addedLater && !_leaf.isExtension) throw new InvalidOperationException();\n\t\t\tif (_state == _DockState.Float) _SetDockState(0);\n\t\t\tvar oldParent = Parent;\n\t\t\t_RemoveFromParentWhenMovingOrDeleting();\n\t\t\t_RemoveParentIfNeedAfterMovingOrDeleting(oldParent);\n\t\t\t_Dictionary.Remove(_leaf.name);\n\t\t}\n\t\t\n\t\tvoid ILeaf.Rename(string name) {\n\t\t\t//if (!_IsLeaf) throw new InvalidOperationException(); //impossible, unless called from this class\n\t\t\tif (name == null) throw new ArgumentException();\n\t\t\t_Dictionary.Remove(_leaf.name);\n\t\t\t_Dictionary.Add(_leaf.name = name, this);\n\t\t\tif (_leaf.header is TextBlock t) t.Text = name;\n\t\t\tif (_ParentIsTab) {\n\t\t\t\t(Parent._tab.tc.Items[_index] as TabItem).Header = name;\n\t\t\t\tParent._VerticalTabHeader(onMove: true);\n\t\t\t}\n\t\t\tif (_floatWindow != null) _floatWindow.Title = name;\n\t\t}\n\t\t\n\t\tpublic event EventHandler<bool> VisibleChanged;\n\t\t\n\t\tpublic event EventHandler<bool> FloatingChanged;\n\t\t\n\t\tpublic event System.ComponentModel.CancelEventHandler Closing;\n\t\t\n\t\t//public event EventHandler<popupMenu> ContextMenuOpening;\n\t\t\n\t\tpublic event EventHandler TabSelected;\n\t\t\n\t\tpublic event EventHandler ParentChanged;\n\t\t\n\t\t#endregion\n\t}\n\t\n\tclass _DockPanelWithBorder : Border {\n\t\tpublic readonly DockPanel Panel;\n\t\t\n\t\tpublic _DockPanelWithBorder() {\n\t\t\tChild = Panel = new();\n\t\t\tSnapsToDevicePixels = true;\n\t\t}\n\t\t\n\t\tpublic UIElementCollection Children => Panel.Children;\n\t\t\n\t\tpublic bool LastChildFill { get => Panel.LastChildFill; set => Panel.LastChildFill = value; }\n\t\t\n\t\tpublic new object Tag {\n\t\t\tget => base.Tag;\n\t\t\tset { base.Tag = value; Panel.Tag = value; }\n\t\t}\n\t}\n}\n\n"
  },
  {
    "path": "Au.Controls/KPanels/dock.cs",
    "content": "using System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Input;\nusing System.Windows.Controls.Primitives;\n\nnamespace Au.Controls;\n\npublic partial class KPanels {\n\tpartial class _Node {\n\t\t[Flags]\n\t\tenum _DockState { Hide = 1, Float = 2, }\n\t\t\n\t\t[Flags]\n\t\tenum _WindowStyle { Topmost = 1, Unowned = 2, }\n\t\t\n\t\tvoid _HeaderContextMenu(object sender, ContextMenuEventArgs e) {\n\t\t\tif (!_IsGoodMouseEvent(sender, e, out var target)) return;\n\t\t\te.Handled = true;\n\t\t\ttarget._HeaderContextMenu(this);\n\t\t}\n\t\t\n\t\tvoid _HeaderContextMenu(_Node thisOrParentTab) {\n\t\t\tif (_IsDocument && !_leaf.addedLater) return;\n\t\t\tvar m = new popupMenu();\n\t\t\t\n\t\t\tbool canClose = _leaf?.canClose ?? false;\n\t\t\tif (canClose) m[\"Close\\tM-click\"] = _ => _UserClosing();\n\t\t\t_DockStateItem(_DockState.Hide, canClose ? \"Hide\" : \"Hide\\tM-click\");\n\t\t\t\n\t\t\tif (_state.Has(_DockState.Float)) _DockStateItem(0, \"Dock\\tD-click\");\n\t\t\telse _DockStateItem(_DockState.Float, \"Float\\tD-click, drag\");\n\t\t\t\n\t\t\tm.Submenu(\"Header at\", m => {\n\t\t\t\t_HeaderAtItem(Dock.Left);\n\t\t\t\t_HeaderAtItem(Dock.Top);\n\t\t\t\t_HeaderAtItem(Dock.Right);\n\t\t\t\t_HeaderAtItem(Dock.Bottom);\n\t\t\t\t\n\t\t\t\tvoid _HeaderAtItem(Dock ca) {\n\t\t\t\t\tm.AddRadio(ca.ToString(), ca == thisOrParentTab._headerAt, o => thisOrParentTab._SetHeaderAt(ca));\n\t\t\t\t}\n\t\t\t});\n\t\t\t\n\t\t\tm.Submenu(\"Window\", m => {\n\t\t\t\t_WindowItem(_WindowStyle.Topmost, \"Topmost\");\n\t\t\t\t_WindowItem(_WindowStyle.Unowned, \"Unowned, can min/max\");\n\t\t\t\t\n\t\t\t\tvoid _WindowItem(_WindowStyle ws, string s) {\n\t\t\t\t\tm.AddCheck(s, _windowStyle.Has(ws), o => { _windowStyle ^= ws; _floatWindow?.ChangeWindowStyle(ws); });\n\t\t\t\t}\n\t\t\t});\n\t\t\t\n\t\t\t_ContextMenu_Move(m);\n\t\t\t\n\t\t\tif (_leaf?.isExtension ?? false) {\n\t\t\t\tm[\"Remove...\"] = o => {\n\t\t\t\t\tvar s1 = _leafType == LeafType.Toolbar ? \"toolbar\" : \"panel\";\n\t\t\t\t\tif (!dialog.showYesNo($\"Remove this extension {s1}?\", this.Name, owner: _pm._ContainerWindow)) return;\n\t\t\t\t\tDelete();\n\t\t\t\t\tprint.it($\"Info: extension {s1} {this.Name} has been removed. To uninstall the extension, also remove its script from startup scripts in Options and delete the script.\");\n\t\t\t\t};\n\t\t\t}\n\t\t\t\n\t\t\t_ShowSubmenus();\n\t\t\t\n\t\t\t//ContextMenuOpening?.Invoke(this, m);\n\t\t\t\n\t\t\tm.Show(owner: _pm._ContainerWindow);\n\t\t\t\n\t\t\tvoid _DockStateItem(_DockState state, string text) {\n\t\t\t\tm[text] = o => _SetDockState(state);\n\t\t\t}\n\t\t\t\n\t\t\tvoid _ShowSubmenus() {\n\t\t\t\tvar a = new List<_Node>();\n\t\t\t\tforeach (var v in RootAncestor.Descendants()) {\n\t\t\t\t\tif (v._IsStack || !v._state.Has(_DockState.Hide)) continue;\n\t\t\t\t\tif (v._IsTab && v.Children().All(o => o._state.Has(_DockState.Hide))) continue;\n\t\t\t\t\ta.Add(v);\n\t\t\t\t}\n\t\t\t\tif (a.Count == 0) return;\n\t\t\t\tm.Separator();\n\t\t\t\ta.Sort((x, y) => {\n\t\t\t\t\tif (x._IsToolbar && !y._IsToolbar) return -1;\n\t\t\t\t\tif (y._IsToolbar && !x._IsToolbar) return 1;\n\t\t\t\t\treturn string.Compare(x.ToString(), y.ToString(), true);\n\t\t\t\t});\n\t\t\t\tm.Submenu(\"Show\", m => {\n\t\t\t\t\tint i = 0;\n\t\t\t\t\tforeach (var v in a) {\n\t\t\t\t\t\tif (i > 0 && a[i - 1]._IsToolbar != a[i]._IsToolbar) m.Separator();\n\t\t\t\t\t\ti++;\n\t\t\t\t\t\tm[v.ToString()] = _ => v._Unhide();\n\t\t\t\t\t}\n#if DEBUG\n\t\t\t\t\tif (a.Count > 1) {\n\t\t\t\t\t\tm.Separator();\n\t\t\t\t\t\tm[\"Show all (debug)\"] = _ => {\n\t\t\t\t\t\t\tforeach (var v in a) v._Unhide();\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n#endif\n\t\t\t\t});\n\t\t\t}\n#if DEBUG\n\t\t\tm.Separator();\n\t\t\tm.Submenu(\"Debug\", m => {\n\t\t\t\tm[\"Invalidate window\"] = _ => _Invalidate(_pm._ContainerWindow);\n\t\t\t\tm[\"Invalidate floats\"] = _ => {\n\t\t\t\t\tforeach (var v in RootAncestor.Descendants()) {\n\t\t\t\t\t\tif (v._floatWindow != null) _Invalidate(v._floatWindow);\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t\tm.Add(0, \"info: toggle ScrollLock if does not work\", disable: true);\n\t\t\t});\n\t\t\t\n\t\t\tvoid _Invalidate(Window w) {\n\t\t\t\t//if (keys.isScrollLock) Api.InvalidateRect(w.Hwnd(), IntPtr.Zero, true); //works\n\t\t\t\t////else w.UpdateLayout(); //no\n\t\t\t\t//else w.InvalidateVisual(); //no\n\t\t\t\t\n\t\t\t\tApi.InvalidateRect(w.Hwnd(), keys.isScrollLock);\n\t\t\t}\n#endif\n\t\t}\n\t\t\n\t\tprivate protected void _OnMouseDown(object sender, MouseButtonEventArgs e) {\n\t\t\tswitch (e.ChangedButton) { case MouseButton.Left: case MouseButton.Middle: break; default: return; }\n\t\t\tif (_IsGoodMouseEvent(sender, e, out var target)) target._OnMouseDown(e);\n\t\t}\n\t\t\n\t\tvoid _OnMouseDown(MouseButtonEventArgs e) {\n\t\t\tif (_IsStack) {\n\t\t\t\tif (!(e.ChangedButton == MouseButton.Left && e.ClickCount == 1 && Keyboard.Modifiers == ModifierKeys.Alt)) return;\n\t\t\t\tif (Parent == null) { e.Handled = true; return; }\n\t\t\t}\n\t\t\te.Handled = true;\n\t\t\tif (e.ChangedButton == MouseButton.Left) {\n\t\t\t\tif (e.ClickCount == 1) {\n\t\t\t\t\te.Handled = false; //if tab item, let select it\n\t\t\t\t\ttimer.after(1, _ => { //Dispatcher.InvokeAsync does not work\n\t\t\t\t\t\tPOINT p = mouse.xy;\n\t\t\t\t\t\tif (Api.DragDetect(_elem.Hwnd(), p)) {\n\t\t\t\t\t\t\t_SetDockState(_DockState.Float, onDrag: true);\n\t\t\t\t\t\t\t_floatWindow?.Drag(p);\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t} else if (e.ClickCount == 2) {\n\t\t\t\t\t_SetDockState(_state ^ _DockState.Float);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (_leaf?.canClose ?? false) _UserClosing();\n\t\t\t\telse _Hide();\n\t\t\t}\n\t\t}\n\t\t\n\t\tbool _IsGoodMouseEvent(object sender, RoutedEventArgs e, out _Node target) {\n\t\t\ttarget = null;\n\t\t\tif (e.Source == sender) {\n\t\t\t\tif (_IsTab && e.OriginalSource is not FlexStackPanel) return false; //tab control border\n\t\t\t\ttarget = sender == _splitter ? Parent : this;\n\t\t\t} else if (e.Source is TabItem ti && ti.Parent == sender) target = _NodeFromTabItem(ti);\n\t\t\telse return false;\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\tvoid _UserClosing() {\n\t\t\tif (Closing != null) {\n\t\t\t\tvar e = new CancelEventArgs();\n\t\t\t\tClosing(this, e);\n\t\t\t\tif (e.Cancel) return;\n\t\t\t}\n\t\t\tDelete();\n\t\t}\n\t\t\n\t\tvoid _Hide() => _SetDockState(_DockState.Hide);\n\t\tvoid _Unhide() => _SetDockState(_state & ~_DockState.Hide);\n\t\t\n\t\tvoid _SetDockState(_DockState state, bool onDrag = false) {\n\t\t\t//print.qm2.write(this, state, \"                    \", _state);\n\t\t\t_savedDockState = 0;\n\t\t\tif (state == _DockState.Hide) state |= _state & _DockState.Float;\n\t\t\tif (state == _state) {\n\t\t\t\tif (state == 0 && Parent._state.Has(_DockState.Hide)) Parent._Unhide(); //in hidden tab\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\tvar oldState = _state;\n\t\t\t_state = state;\n\t\t\t\n\t\t\tif (oldState == _DockState.Float) {\n\t\t\t\t_floatWindow?.Close();\n\t\t\t\t//_floatWindow sets _floatWindow=null when closing\n\t\t\t} else if (state == _DockState.Float) {\n\t\t\t\t_floatWindow = new _Floating(this, onDrag);\n\t\t\t\t//ctor uses docked rect\n\t\t\t}\n\t\t\t\n\t\t\tif (state == 0) { //dock; was hidden or floating\n\t\t\t\t_AddRemoveHeaderAndBorder();\n\t\t\t\tif (_ParentIsTab) _ShowHideInTab(true);\n\t\t\t\telse _ShowHideInStack(true);\n\t\t\t} else {\n\t\t\t\tif (oldState == 0) { //was docked; now hide or float\n\t\t\t\t\tif (_ParentIsTab) _ShowHideInTab(false);\n\t\t\t\t\telse _ShowHideInStack(false);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t_AddRemoveHeaderAndBorder();\n\t\t\t\t\n\t\t\t\tif (state == _DockState.Float) {\n\t\t\t\t\t_floatWindow.Content = _elem;\n\t\t\t\t\t_floatWindow.ShowIfOwnerVisible();\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tif ((state ^ oldState).Has(_DockState.Hide)) VisibleChanged?.Invoke(this, oldState.Has(_DockState.Hide));\n\t\t\tif ((state ^ oldState).Has(_DockState.Float)) FloatingChanged?.Invoke(this, state.Has(_DockState.Float));\n\t\t}\n\t\t\n\t\tvoid _ContextMenu_Move(popupMenu m) {\n\t\t\tm.Submenu(\"Move to\", m => {\n\t\t\t\tm.RawText = true;\n\t\t\t\tstring sThis = ToString();\n\t\t\t\tforeach (var target in RootAncestor.Descendants(andSelf: true)) {\n\t\t\t\t\tbool targetInTab = target._ParentIsTab;\n\t\t\t\t\tif (targetInTab) {\n\t\t\t\t\t\tif (!_IsLeaf || target._IsDocument != _IsDocument) continue;\n\t\t\t\t\t} else if (_IsDocument && _ParentIsTab) {\n\t\t\t\t\t\t//allow only beside parent tab or in/besides another document or doc tab. Elsewhere probably not useful, just adds many menu items.\n\t\t\t\t\t\tif (target != Parent && !target._IsDocumentsNode) continue;\n\t\t\t\t\t}\n\t\t\t\t\tif (target.Ancestors(andSelf: true).Contains(this)) continue;\n\t\t\t\t\t\n\t\t\t\t\tstring sTarget = target.ToString();\n\t\t\t\t\tvar s1 = new string(' ', target.Level * 4) + sTarget + (target._state switch { 0 => null, _DockState.Float => \" (floating)\", _ => \" (hidden)\" });\n\t\t\t\t\tm.Submenu(s1, m => {\n\t\t\t\t\t\tbool sep = false;\n\t\t\t\t\t\t//this would be duplicate of before/after\n\t\t\t\t\t\t//if (target._IsStack || (target._IsTab && _IsLeaf && (target.FirstChild?._IsDocument ?? false) == _IsDocument)) {\n\t\t\t\t\t\t//\tint i = 0;\n\t\t\t\t\t\t//\tforeach (var u in target.Children()) {\n\t\t\t\t\t\t//\t\tm[i++ == 0 ? \"First\" : ($\"Before '{u}'\")] = o => _MoveTo(u, _HowToMove.BeforeTarget);\n\t\t\t\t\t\t//\t}\n\t\t\t\t\t\t//\tm[\"Last\"] = o => _MoveTo(target.LastChild, _HowToMove.AfterTarget);\n\t\t\t\t\t\t//\tsep = true;\n\t\t\t\t\t\t//}\n\t\t\t\t\t\tif (target.Parent != null) {\n\t\t\t\t\t\t\t//if (sep) m.Separator();\n\t\t\t\t\t\t\tif (target.Previous != this) _AddMI($\"Before '{sTarget}'\", _HowToMove.BeforeTarget);\n\t\t\t\t\t\t\tif (target.Next != this) _AddMI($\"After '{sTarget}'\", _HowToMove.AfterTarget);\n\t\t\t\t\t\t\tsep = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!targetInTab) {\n\t\t\t\t\t\t\tif (sep) m.Separator();\n\t\t\t\t\t\t\tif (target._IsLeaf && _IsLeaf && target._IsDocument == _IsDocument) {\n\t\t\t\t\t\t\t\tm.Add(0, $\"Create tabs and add '{sThis}' as:\", disable: true);\n\t\t\t\t\t\t\t\t_AddMI($\"- First tab (before '{sTarget}')\", _HowToMove.FirstInNewTab);\n\t\t\t\t\t\t\t\t_AddMI($\"- Last tab (after '{sTarget}')\", _HowToMove.LastInNewTab);\n\t\t\t\t\t\t\t\tm.Separator();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tm.Add(0, $\"Create stack and add '{sThis}' at:\", disable: true);\n\t\t\t\t\t\t\t_AddMI(\"- Left\", _HowToMove.NewStack, Dock.Left);\n\t\t\t\t\t\t\t_AddMI(\"- Right\", _HowToMove.NewStack, Dock.Right);\n\t\t\t\t\t\t\t_AddMI(\"- Top\", _HowToMove.NewStack, Dock.Top);\n\t\t\t\t\t\t\t_AddMI(\"- Bottom\", _HowToMove.NewStack, Dock.Bottom);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (target._IsStack || (target._IsTab && _IsLeaf) && target.FirstChild == null) { //empty\n\t\t\t\t\t\t\tm.Separator();\n\t\t\t\t\t\t\t_AddMI($\"- Into '{sTarget}'\", _HowToMove.Child);\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\tvoid _AddMI(string text, _HowToMove how, Dock dock = default) {\n\t\t\t\t\t\t\t_MoveRect k = new(target, how, dock);\n\t\t\t\t\t\t\tm.Add(text, o => _MoveTo(k.target, k.how, k.dock)).Tag = k;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\t_RectTimer(m, true);\n\t\t\t\t\t}).Tag = target;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t_RectTimer(m, false);\n\t\t\t\t\n\t\t\t\tstatic void _RectTimer(popupMenu m, bool second) {\n\t\t\t\t\tvar osd = new osdRect { Color = second ? 0x60c000 : 0x4040ff };\n\t\t\t\t\tif (second) osd.Opacity = .5;\n\t\t\t\t\tPMItem pmi = null;\n\t\t\t\t\ttimer.every(100, t => {\n\t\t\t\t\t\tif (m.IsOpen) {\n\t\t\t\t\t\t\tvar mi = m.FocusedItem;\n\t\t\t\t\t\t\tif (mi != pmi) {\n\t\t\t\t\t\t\t\tpmi = mi;\n\t\t\t\t\t\t\t\tbool visible = false;\n\t\t\t\t\t\t\t\tif (mi != null) {\n\t\t\t\t\t\t\t\t\tif (!second) {\n\t\t\t\t\t\t\t\t\t\tvar n = mi.Tag as _Node;\n\t\t\t\t\t\t\t\t\t\tif (visible = _GetRectInScreen(n, out var r)) osd.Rect = r;\n\t\t\t\t\t\t\t\t\t} else if (mi.Tag is _MoveRect k) {\n\t\t\t\t\t\t\t\t\t\tvar n = k.target;\n\t\t\t\t\t\t\t\t\t\tif (n._floatWindow == null)\n\t\t\t\t\t\t\t\t\t\t\tif (visible = _GetRectInScreen(n, out var r)) {\n\t\t\t\t\t\t\t\t\t\t\t\tr.Inflate(-osd.Thickness, -osd.Thickness);\n\t\t\t\t\t\t\t\t\t\t\t\tif (k.how is _HowToMove.BeforeTarget or _HowToMove.AfterTarget) {\n\t\t\t\t\t\t\t\t\t\t\t\t\tbool vert = n._ParentIsTab ? n.Parent._headerAt is Dock.Left or Dock.Right : n.Parent._stack.isVertical;\n\t\t\t\t\t\t\t\t\t\t\t\t\tif (k.how == _HowToMove.BeforeTarget) {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tif (vert) r.bottom = r.top + r.Height / 2; else r.right = r.left + r.Width / 2;\n\t\t\t\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tif (vert) r.top = r.bottom - r.Height / 2; else r.left = r.right - r.Width / 2;\n\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t} else if (k.how == _HowToMove.Child) {\n\t\t\t\t\t\t\t\t\t\t\t\t\tif (n._stack.isVertical) r.bottom = r.top + r.Height / 4; else r.right = r.left + r.Width / 4;\n\t\t\t\t\t\t\t\t\t\t\t\t} else if (k.how == _HowToMove.NewStack) {\n\t\t\t\t\t\t\t\t\t\t\t\t\tif (k.dock == Dock.Left) r.right = r.left + r.Width / 2;\n\t\t\t\t\t\t\t\t\t\t\t\t\tif (k.dock == Dock.Right) r.left = r.right - r.Width / 2;\n\t\t\t\t\t\t\t\t\t\t\t\t\tif (k.dock == Dock.Top) r.bottom = r.top + r.Height / 2;\n\t\t\t\t\t\t\t\t\t\t\t\t\tif (k.dock == Dock.Bottom) r.top = r.bottom - r.Height / 2;\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\tosd.Rect = r;\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tosd.Visible = visible;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tt.Stop();\n\t\t\t\t\t\t\tosd.Dispose();\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tstatic bool _GetRectInScreen(_Node n, out RECT r) {\n\t\t\t\t\tif (n._IsVisibleReally()) {\n\t\t\t\t\t\tFrameworkElement e =\n\t\t\t\t\t\t\tn._IsStack ? n._stack.grid\n\t\t\t\t\t\t\t: n._IsTab ? n._tab.tc\n\t\t\t\t\t\t\t: n._elem.Parent is TabItem ti ? ti\n\t\t\t\t\t\t\t: n._leaf.panel.Panel;\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tr = e.RectInScreen();\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcatch (Exception) {  }\n\t\t\t\t\t}\n\t\t\t\t\tr = default;\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t\t\n\t\tenum _HowToMove //don't reorder\n\t\t{\n\t\t\tBeforeTarget, //before target in parent stack or tab\n\t\t\tAfterTarget, //after target in parent stack or tab\n\t\t\tNewStack, //create new stack in place of target; add target and this to it; use dock to set orientation of new stack and index ot this and target\n\t\t\tFirstInNewTab, //create new tab in place of target; add this (first) and target to it\n\t\t\tLastInNewTab, //create new tab in place of target; add target and this (last) to it\n\t\t\tChild, //add as child of target (target is empty stack or tab)\n\t\t}\n\t\t\n\t\trecord class _MoveRect(_Node target, _HowToMove how, Dock dock = default);\n\t\t\n\t\tvoid _MoveTo(_Node target, _HowToMove how, Dock dock = default) {\n\t\t\tif (target == this) return;\n\t\t\tbool beforeAfter = how <= _HowToMove.AfterTarget;\n\t\t\tif (_state != 0) _SetDockState(0);\n\t\t\tif (target._state != 0 && !beforeAfter) target._SetDockState(0);\n\t\t\t\n\t\t\tbool after = how == _HowToMove.AfterTarget;\n\t\t\tvar oldParent = Parent;\n\t\t\t\n\t\t\tif (beforeAfter && target.Parent == oldParent && oldParent._IsTab) { //just reorder buttons\n\t\t\t\t_ReorderInTab(target, after);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\t_RemoveFromParentWhenMovingOrDeleting();\n\t\t\t\n\t\t\tswitch (how) {\n\t\t\tcase _HowToMove.NewStack:\n\t\t\t\tnew _Node(target, isTab: false, verticalStack: dock == Dock.Top || dock == Dock.Bottom);\n\t\t\t\ttarget._AddToStack(moving: false, c_defaultSplitterSize);\n\t\t\t\tafter = dock == Dock.Right || dock == Dock.Bottom;\n\t\t\t\tbreak;\n\t\t\tcase _HowToMove.FirstInNewTab:\n\t\t\tcase _HowToMove.LastInNewTab:\n\t\t\t\tnew _Node(target, isTab: true);\n\t\t\t\ttarget._AddToTab(moving: false);\n\t\t\t\tafter = how == _HowToMove.LastInNewTab;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t\n\t\t\tif (how == _HowToMove.Child) _AddToParentWhenMoving(target);\n\t\t\telse _AddToParentWhenMovingOrAddingLater(target, after);\n\t\t\t\n#if false //debug print\n\t\t\tint i = 0;\n\t\t\tforeach (var v in Parent.Children()) {\n\t\t\t\tprint.it(i++, v._index, v);\n\t\t\t\tif (Parent._IsStack) {\n\t\t\t\t\tif (v._splitter != null) print.it(\"splitter\", _RC(v._splitter));\n\t\t\t\t\tprint.it(\"elem    \", _RC(v._elem), v._dockedSize, v._SizeDef);\n\t\t\t\t\tint _RC(FrameworkElement e) => Parent._stack.isVertical ? Grid.GetRow(e) : Grid.GetColumn(e);\n\t\t\t\t}\n\t\t\t}\n#endif\n\t\t\t\n\t\t\t_RemoveParentIfNeedAfterMovingOrDeleting(oldParent);\n\t\t\t\n\t\t\tif (how <= _HowToMove.NewStack && _IsDocument && oldParent._IsTab && !_ParentIsTab) {\n\t\t\t\t_headerAt = oldParent._headerAt;\n\t\t\t\tnew _Node(this, isTab: true);\n\t\t\t\t_AddToTab(moving: false);\n\t\t\t}\n\t\t\t\n\t\t\tif (Parent != oldParent) ParentChanged?.Invoke(this, EventArgs.Empty);\n\t\t}\n\t\t\n\t\tvoid _AddToParentWhenMovingOrAddingLater(_Node target, bool after) {\n\t\t\ttarget.AddSibling(this, after);\n\t\t\t_index = target._index + (after ? 1 : 0);\n\t\t\tif (_ParentIsTab) {\n\t\t\t\t_AddToTab(moving: true);\n\t\t\t\t_AddRemoveHeaderAndBorder();\n\t\t\t\t//if(select) Parent._tab.tc.SelectedIndex = _index;\n\t\t\t} else {\n\t\t\t\tif (!(_dockedSize.IsAuto && _IsToolbarsNode)) _dockedSize = new GridLength(100, GridUnitType.Star);\n\t\t\t\tif (Parent.Count == 2) target._SizeDef = target._dockedSize = new GridLength(100, GridUnitType.Star);\n\t\t\t\t\n\t\t\t\t_AddToStack(moving: true, c_defaultSplitterSize);\n\t\t\t}\n\t\t}\n\t\t\n\t\tvoid _AddToParentWhenMoving(_Node parent) {\n\t\t\tparent.AddChild(this, first: true);\n\t\t\t_index = 0;\n\t\t\tif (_ParentIsTab) {\n\t\t\t\t_AddToTab(moving: true);\n\t\t\t\t_AddRemoveHeaderAndBorder();\n\t\t\t\tParent._tab.tc.SelectedIndex = _index;\n\t\t\t} else {\n\t\t\t\tif (!(_dockedSize.IsAuto && _IsToolbarsNode)) _dockedSize = new GridLength(100, GridUnitType.Star);\n\t\t\t\t\n\t\t\t\t_AddToStack(moving: true, c_defaultSplitterSize);\n\t\t\t}\n\t\t}\n\t\t\n\t\tvoid _RemoveFromParentWhenMovingOrDeleting() {\n\t\t\tif (Parent._IsStack) {\n\t\t\t\t_RemoveGridRowCol(_elem);\n\t\t\t\t_RemoveSplitter();\n\t\t\t\tif (_index == 0) Next?._RemoveSplitter();\n\t\t\t} else {\n\t\t\t\tif (_elem.Parent is TabItem ti) { //null if hidden or floating\n\t\t\t\t\tti.Content = null;\n\t\t\t\t\tParent._tab.tc.Items.Remove(ti);\n\t\t\t\t}\n\t\t\t}\n\t\t\t_ShiftSiblingIndices(-1);\n\t\t\tRemove();\n\t\t}\n\t\t\n\t\tvoid _RemoveParentIfNeedAfterMovingOrDeleting(_Node oldParent) {\n\t\t\tint n = oldParent.Count;\n\t\t\tif (n == 0) {\n\t\t\t\tvar pp = oldParent.Parent;\n\t\t\t\toldParent._RemoveFromParentWhenMovingOrDeleting();\n\t\t\t\toldParent._RemoveParentIfNeedAfterMovingOrDeleting(pp);\n\t\t\t} else if (n == 1) {\n\t\t\t\tif (!_IsDocument) {\n\t\t\t\t\tvar f = oldParent.FirstChild;\n\t\t\t\t\tif (oldParent.Parent != null) {\n\t\t\t\t\t\tf._MoveTo(oldParent, _HowToMove.BeforeTarget);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tf._RemoveFromParentWhenMovingOrDeleting();\n\t\t\t\t\t\t_pm._rootStack = f;\n\t\t\t\t\t\t_pm._setContainer(f._stack.grid);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (oldParent._IsTab && Parent != oldParent) {\n\t\t\t\toldParent._VerticalTabHeader(onMove: true);\n\t\t\t}\n\t\t}\n\t\t\n\t\tvoid _ShiftSiblingIndices(int n) {\n\t\t\tfor (var v = this; (v = v.Next) != null;) v._index += n;\n\t\t}\n\t}\n\t\n\t[Conditional(\"DEBUG\")]\n\tinternal void PrintTree_(string header = null) {\n\t\tprint.qm2.write(\"-----\" + header);\n\t\tforeach (var v in _rootStack.Descendants(true)) {\n\t\t\tprint.qm2.write(new string('\\t', v.Level) + v);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au.Controls/KPanels/stack.cs",
    "content": "using System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Input;\n\nnamespace Au.Controls;\n\npublic partial class KPanels\n{\n\tpartial class _Node\n\t{\n\t\t/// <summary>\n\t\t/// Gets nodes docked and really visible in this stack (not in tab, not floating, not hidden).\n\t\t/// </summary>\n\t\tList<_Node> _Stack_DockedNodes {\n\t\t\tget {\n\t\t\t\tvar a = new List<_Node>();\n\t\t\t\tforeach (var v in Children()) if (v._elem.Parent == _stack.grid) a.Add(v);\n\t\t\t\treturn a;\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// true if parent is stack and this is docked and not floating/hidden.\n\t\t/// </summary>\n\t\tbool _IsDockedInStack => _ParentIsStack && _elem.Parent == Parent._stack.grid;\n\n\t\t_Node _PreviousDockedInStack {\n\t\t\tget {\n\t\t\t\tfor (var v = this; (v = v.Previous) != null;) if (v._IsDockedInStack) return v;\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\n\t\t//not used\n\t\t//_Node _NextDockedInStack {\n\t\t//\tget {\n\t\t//\t\tfor (var v = this; (v = v.Next) != null;) if (v._IsDockedInStack) return v;\n\t\t//\t\treturn null;\n\t\t//\t}\n\t\t//}\n\n\t\t/// <summary>\n\t\t/// Adds elements of this to parent stack. Creates splitter if need, adds row/column, sets element properties (header etc).\n\t\t/// </summary>\n\t\t/// <param name=\"moving\">Called when moving this. Inserts, shifts sibling indices, etc. If false, adds as last item in stack.</param>\n\t\t/// <param name=\"splitterSize\"></param>\n\t\tvoid _AddToStack(bool moving, int splitterSize) {\n\t\t\tint i = _index * 2;\n\t\t\tif (i > 0) _CreateSplitter(splitterSize);\n\t\t\tvar pstack = Parent._stack;\n\t\t\tvar g = pstack.grid;\n\t\t\tif (pstack.isVertical) {\n\t\t\t\tg.InsertRow(i, new RowDefinition { Height = _dockedSize, MinHeight = c_minSize });\n\t\t\t\tg.AddChild(_elem, i, 0);\n\t\t\t} else {\n\t\t\t\tg.InsertColumn(i, new ColumnDefinition { Width = _dockedSize, MinWidth = c_minSize });\n\t\t\t\tg.AddChild(_elem, 0, i);\n\t\t\t}\n\t\t\tif (moving) {\n\t\t\t\t_ShiftSiblingIndices(1);\n\n\t\t\t\tvar next = Next;\n\t\t\t\tif (next != null) {\n\t\t\t\t\tif (next._splitter == null) next._CreateSplitter(c_defaultSplitterSize);\n\n\t\t\t\t\t//workaround for: when moving a fixed-size item, 1 pixel of splitter after it may stop working. If splitter size is 1 pixel...\n\t\t\t\t\telse if (next._SplitterSize < c_defaultSplitterSize) next._SplitterSize = c_defaultSplitterSize;\n\t\t\t\t}\n\t\t\t}\n\t\t\t_AddRemoveHeaderAndBorder();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Replaces target with this in parent stack.\n\t\t/// Used when moving, to create new parent (this) tab or stack for target and the moved node.\n\t\t/// Does not add/remove tree nodes.\n\t\t/// </summary>\n\t\tvoid _ReplaceInStack(_Node target) {\n\t\t\tif (target._splitter != null) {\n\t\t\t\ttarget._SetSplitterEvents(false);\n\t\t\t\t_splitter = target._splitter; target._splitter = null;\n\t\t\t\t_SetSplitterEvents(true);\n\t\t\t}\n\n\t\t\t_dockedSize = target._dockedSize;\n\t\t\tif (_dockedSize.IsAuto) _SizeDef = _dockedSize = new GridLength(100, GridUnitType.Star);\n\n\t\t\tint i = _index * 2;\n\t\t\tvar pstack = Parent._stack;\n\t\t\tvar g = pstack.grid;\n\t\t\tif (pstack.isVertical) Grid.SetRow(_elem, i); else Grid.SetColumn(_elem, i);\n\t\t\tg.Children.Remove(target._elem);\n\t\t\tg.Children.Add(_elem);\n\t\t\t_AddRemoveHeaderAndBorder();\n\t\t\ttarget._AddRemoveHeaderAndBorder();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Removes an element of this from parent stack grid. Removes its row/column and shifts sibling indices.\n\t\t/// </summary>\n\t\t/// <param name=\"e\">_elem or _splitter</param>\n\t\tvoid _RemoveGridRowCol(FrameworkElement e) {\n\t\t\tvar pstack = Parent._stack;\n\t\t\tif (pstack.isVertical) pstack.grid.RemoveRow(e, false); else pstack.grid.RemoveColumn(e, false);\n\t\t}\n\n\t\tvoid _ShowHideInStack(bool show) {\n\t\t\tvar g = Parent._stack.grid;\n\t\t\tvar a = Parent._Stack_DockedNodes;\n\t\t\tif (!show) {\n\t\t\t\t_dockedSize = _SizeDef;\n\n\t\t\t\tg.Children.Remove(_elem);\n\t\t\t\tif (_splitter != null) _splitter.Visibility = Visibility.Collapsed;\n\t\t\t\t_SizeMin = 0;\n\t\t\t\t_SizeDef = default; //Auto\n\n\t\t\t\ta.Remove(this);\n\t\t\t\tif (a.Count == 0) {\n\t\t\t\t\tif (Parent._state == _DockState.Float) Parent._Hide();\n\t\t\t\t\telse Parent._ShowHideInStack(show);\n\t\t\t\t} else if (_dockedSize.IsStar && !a.Any(o => o._SizeDef.IsStar)) { //if hiding last star-sized, make the last visible fixed node star-sized\n\t\t\t\t\ta.LastOrDefault(o => o._SizeDef.IsAbsolute)?._ChangeSizeUnit(GridUnitType.Star, false);\n\t\t\t\t\t//never mind: later should restore. It makes everything complicated. This probably is rare, and it's easy for user to set fixed size again.\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (a.Count == 0) {\n\t\t\t\t\tif (Parent._state.Has(_DockState.Float)) Parent._SetDockState(_DockState.Float);\n\t\t\t\t\telse Parent._ShowHideInStack(show);\n\t\t\t\t}\n\n\t\t\t\t_SizeDef = _dockedSize;\n\t\t\t\t_SizeMin = c_minSize;\n\t\t\t\tg.Children.Add(_elem);\n\t\t\t}\n\t\t\tParent._Stack_UpdateSplittersVisibility();\n\t\t}\n\n\t\t#region splitter\n\n\t\tvoid _CreateSplitter(int size) {\n\t\t\tbool verticalStack = Parent._stack.isVertical;\n\t\t\tvar c = new GridSplitter2();\n\t\t\tif (verticalStack) { //horz splitter\n\t\t\t\tc.ResizeDirection = GridResizeDirection.Rows;\n\t\t\t\tc.VerticalAlignment = VerticalAlignment.Top;\n\t\t\t\tc.HorizontalAlignment = HorizontalAlignment.Stretch;\n\t\t\t} else { //vert splitter\n\t\t\t\tc.ResizeDirection = GridResizeDirection.Columns;\n\t\t\t\tc.HorizontalAlignment = HorizontalAlignment.Left;\n\t\t\t\t//default stretch\n\t\t\t}\n\t\t\tif (_pm.SplitterBrush != null) c.Background = _pm.SplitterBrush;\n\t\t\t_splitter = c;\n\t\t\t_SplitterSize = size;\n\t\t\tvar g = Parent._stack.grid;\n\t\t\tint i = _index * 2 - 1;\n\t\t\tif (verticalStack) {\n\t\t\t\tg.InsertRow(i, new RowDefinition { Height = default }); //Auto\n\t\t\t\tg.AddChild(c, i, 0);\n\t\t\t} else {\n\t\t\t\tg.InsertColumn(i, new ColumnDefinition { Width = default });\n\t\t\t\tg.AddChild(c, 0, i);\n\t\t\t}\n\t\t\t_SetSplitterEvents(true);\n\t\t}\n\n\t\tvoid _SetSplitterEvents(bool add) {\n\t\t\tif (add) {\n\t\t\t\t_splitter.ContextMenuOpening += _SplitterContextMenu;\n\t\t\t\t_splitter.PreviewMouseDown += _OnMouseDown;\n\t\t\t} else {\n\t\t\t\t_splitter.ContextMenuOpening -= _SplitterContextMenu;\n\t\t\t\t_splitter.PreviewMouseDown -= _OnMouseDown;\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// This must be stack. Hides splitter of first visible child and shows splitters of other visible children.\n\t\t/// </summary>\n\t\tvoid _Stack_UpdateSplittersVisibility() {\n\t\t\tvar a = _Stack_DockedNodes;\n\t\t\tfor (int i = 0; i < a.Count; i++) {\n\t\t\t\tvar v = a[i];\n\t\t\t\tif (i > 0) v._splitter.Visibility = Visibility.Visible;\n\t\t\t\telse if (v._splitter != null) v._splitter.Visibility = Visibility.Collapsed;\n\t\t\t}\n\t\t}\n\n\t\tvoid _RemoveSplitter() {\n\t\t\tif (_splitter != null) {\n\t\t\t\t_RemoveGridRowCol(_splitter);\n\t\t\t\t_splitter = null;\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets or sets actual height of <see cref=\"_splitter\"/> in vertical stack or width in horizontal stack.\n\t\t/// </summary>\n\t\tint _SplitterSize {\n\t\t\tget {\n\t\t\t\tif (_splitter == null) return 0;\n\t\t\t\treturn (Parent._stack.isVertical ? _splitter.ActualHeight : _splitter.ActualWidth).ToInt();\n\t\t\t}\n\t\t\tset {\n\t\t\t\tif (_splitter == null) return;\n\t\t\t\tif (Parent._stack.isVertical) _splitter.Height = value; else _splitter.Width = value;\n\t\t\t}\n\t\t}\n\n\t\tvoid _SplitterContextMenu(object sender, ContextMenuEventArgs e) {\n\t\t\te.Handled = true;\n\t\t\tvar parentStack = Parent._stack;\n\t\t\tvar m = new popupMenu();\n\t\t\tm.Submenu(\"Splitter size\", m => {\n\t\t\t\tint z = _SplitterSize;\n\t\t\t\tfor (int i = 1; i <= 10; i++) {\n\t\t\t\t\tm.AddRadio(i.ToString(), i == z, o => _SplitterSize = o.Text.ToInt());\n\t\t\t\t}\n\t\t\t});\n\t\t\tm.Separator();\n\t\t\tbool vert = parentStack.isVertical;\n\t\t\t_PreviousDockedInStack._SplitterContextMenu_Unit(m, vert ? \"Top\" : \"Left\");\n\t\t\t_SplitterContextMenu_Unit(m, vert ? \"Bottom\" : \"Right\");\n\t\t\tif ((parentStack.isVertical ? parentStack.grid.RowDefinitions.Where(o => !o.Height.IsAuto).Count() : parentStack.grid.ColumnDefinitions.Where(o => !o.Width.IsAuto).Count()) > 2) {\n\t\t\t\t//m.Separator();\n\t\t\t\tm.AddCheck(\"Resize Nearest\\tCtrl\", _splitter.ResizeNearest, _ => _splitter.ResizeNearest ^= true);\n\t\t\t}\n\t\t\tif (Parent.Parent != null) {\n\t\t\t\tm.Separator();\n\t\t\t\tm.Submenu(\"Stack\", m => {\n\t\t\t\t\tbool isFloating = Parent._state.Has(_DockState.Float);\n\t\t\t\t\tm[isFloating ? \"Dock\" : \"Float\\tAlt+drag\"] = o => Parent._SetDockState(isFloating ? 0 : _DockState.Float);\n\t\t\t\t\tParent._ContextMenu_Move(m);\n\t\t\t\t});\n\t\t\t}\n\t\t\ttimer.after(100, _ => Mouse.SetCursor(Cursors.Arrow)); //workaround. 30 too small, 50 ok\n\t\t\tm.Show();\n\t\t}\n\n\t\tvoid _SplitterContextMenu_Unit(popupMenu m, string s1) {\n\t\t\tvar unitNow = _SizeDef.GridUnitType;\n\t\t\t//var unitNow = _dockedSize.GridUnitType;\n\t\t\t//print.it(this, unitNow);\n\t\t\tbool allToolbars = _IsToolbarsNode;\n\t\t\tbool disableFixed = !allToolbars && unitNow != GridUnitType.Pixel\n\t\t\t\t&& Parent.Children().Count(o => o._SizeDef.GridUnitType == GridUnitType.Star) < (unitNow == GridUnitType.Star ? 2 : 1);\n\t\t\t_UnitItem(s1 + \" fixed\", GridUnitType.Pixel);\n\t\t\tif (allToolbars) _UnitItem(s1 + \" auto\", GridUnitType.Auto);\n\n\t\t\tvoid _UnitItem(string text, GridUnitType unit) {\n\t\t\t\tm.AddCheck(text, unit == unitNow, o => _SetUnit(unit), disable: disableFixed && unit == GridUnitType.Pixel);\n\t\t\t\t//CONSIDER: don't disable. Maybe user wants to set row A fixed and then row B star, not vice versa. But if forgets and makes window smaller, some panels and splitters may become invisible.\n\t\t\t}\n\n\t\t\tvoid _SetUnit(GridUnitType unit) {\n\t\t\t\tif (unit == _SizeDef.GridUnitType) unit = GridUnitType.Star; //unchecking Fixed or Auto\n\t\t\t\t_ChangeSizeUnit(unit, true);\n\t\t\t}\n\t\t}\n\n\t\t#endregion\n\n\t\t#region row/col, size\n\n\t\t//void _NormalizeChildStars(bool percent) {//FUTURE: remove if unused. Not sure it is finished.\n\t\t//\tvar e = Children();\n\t\t//\tdouble sizeOfStars = percent ? e.Sum(v => !v._dockedSize.IsStar ? 0 : v._SizeNowDockedOrNot) : 0;\n\t\t//\tforeach (var v in e) {\n\t\t//\t\tif (!v._dockedSize.IsStar) continue;\n\t\t//\t\tvar z = _SizeNowDockedOrNot;\n\t\t//\t\tif (percent && sizeOfStars >= 0.1) z = z * 100 / sizeOfStars;\n\t\t//\t\t_dockedSize = new GridLength(z, GridUnitType.Star);\n\t\t//\t\tif (_IsDockedInStack) _SizeDef = _dockedSize;\n\t\t//\t}\n\t\t//}\n\n\t\t///// <summary>\n\t\t///// If _IsDockedInStack, returns _SizeNow, else _dockedSize.Value.\n\t\t///// </summary>\n\t\t//double _SizeNowDockedOrNot => _IsDockedInStack ? _SizeNow : _dockedSize.Value;\n\n\t\t///// <summary>\n\t\t///// Gets actual row height in vertical stack or column width in horizontal stack.\n\t\t///// </summary>\n\t\t//double _SizeNow => new _RowCol(this).SizeNow;\n\n\t\t/// <summary>\n\t\t/// Gets or sets defined row height/unit in vertical stack or column width/unit in horizontal stack.\n\t\t/// </summary>\n\t\tGridLength _SizeDef {\n\t\t\tget => new _RowCol(this).SizeDef;\n\t\t\tset => new _RowCol(this) { SizeDef = value };\n\t\t\t//set {\n\t\t\t//\tprint.it(this, value.GridUnitType);\n\t\t\t//\tnew _RowCol(this) { SizeDef = value };\n\t\t\t//}\n\t\t}\n\n\t\t///// <summary>\n\t\t///// Gets GridLength with actual row height in vertical stack or column width in horizontal stack.\n\t\t///// </summary>\n\t\t//GridLength _SizeDefNow => new _RowCol(this).SizeDefNow;\n\n\t\t/// <summary>\n\t\t/// Gets or sets minimal row height in vertical stack or column width in horizontal stack.\n\t\t/// </summary>\n\t\tdouble _SizeMin {\n\t\t\t//get => new _RowCol(this).SizeMin;\n\t\t\tset => new _RowCol(this) { SizeMin = value };\n\t\t}\n\n\t\t///// <summary>\n\t\t///// Gets or sets maximal row height in vertical stack or column width in horizontal stack.\n\t\t///// </summary>\n\t\t//double _SizeMax {\n\t\t//\tget => new _RowCol(this).SizeMax;\n\t\t//\tset => new _RowCol(this) { SizeMax = value };\n\t\t//}\n\n\t\t/// <summary>\n\t\t/// Sets size = actual size and new unit.\n\t\t/// </summary>\n\t\tvoid _ChangeSizeUnit(GridUnitType unit, bool updateStars) {\n\t\t\tDebug.Assert(_IsDockedInStack);\n\t\t\t_dockedSize = new _RowCol(this).ChangeUnit(unit);\n\t\t\tif (updateStars && unit == GridUnitType.Star) //update other stars, else the splitter may jump\n\t\t\t\tforeach (var v in Parent._Stack_DockedNodes)\n\t\t\t\t\tif (v != this && v._dockedSize.IsStar)\n\t\t\t\t\t\tv._ChangeSizeUnit(unit, false); //unit is same, just update value in grid and _dockedSize\n\t\t}\n\n\t\tstruct _RowCol\n\t\t{\n\t\t\treadonly DefinitionBase _d;\n\n\t\t\tpublic _RowCol(_Node node) {\n\t\t\t\tvar stack = node.Parent._stack;\n\t\t\t\tint i = node._index * 2;\n\t\t\t\tif (stack.isVertical) _d = stack.grid.RowDefinitions[i];\n\t\t\t\telse _d = stack.grid.ColumnDefinitions[i];\n\t\t\t}\n\n\t\t\tpublic DefinitionBase RowCol => _d;\n\n\t\t\tpublic GridLength SizeDef {\n\t\t\t\tget => (_d is RowDefinition rd) ? rd.Height : (_d as ColumnDefinition).Width;\n\t\t\t\tset { if (_d is RowDefinition rd) rd.Height = value; else (_d as ColumnDefinition).Width = value; }\n\t\t\t}\n\n\t\t\tpublic double SizeNow => (_d is RowDefinition rd) ? rd.ActualHeight : (_d as ColumnDefinition).ActualWidth;\n\n\t\t\tpublic GridLength SizeDefNow => new GridLength(SizeNow, SizeDef.GridUnitType);\n\n\t\t\tpublic double SizeMin {\n\t\t\t\tget => (_d is RowDefinition rd) ? rd.MinHeight : (_d as ColumnDefinition).MinWidth;\n\t\t\t\tset { if (_d is RowDefinition rd) rd.MinHeight = value; else (_d as ColumnDefinition).MinWidth = value; }\n\t\t\t}\n\n\t\t\tpublic double SizeMax {\n\t\t\t\tget => (_d is RowDefinition rd) ? rd.MaxHeight : (_d as ColumnDefinition).MaxWidth;\n\t\t\t\tset { if (_d is RowDefinition rd) rd.MaxHeight = value; else (_d as ColumnDefinition).MaxWidth = value; }\n\t\t\t}\n\n\t\t\tpublic GridLength ChangeUnit(GridUnitType unit) { var r = new GridLength(SizeNow, unit); SizeDef = r; return r; }\n\t\t}\n\n\t\tstatic GridLength _GridLengthFromString(string s) {\n\t\t\tdouble w = 0; var u = GridUnitType.Star;\n\t\t\tif (s == null) u = GridUnitType.Auto;\n\t\t\telse if (s.Ends(\"*\")) w = s.Length > 1 ? s.ToNumber(..^1) : 1.0;\n\t\t\telse { w = s.ToNumber(); u = GridUnitType.Pixel; }\n\t\t\treturn new GridLength(w, u);\n\t\t\t//also tested GridLengthConverter\n\t\t}\n\n\t\tstatic string _GridLengthToString(GridLength k) {\n\t\t\tif (k.IsAuto) return null;\n\t\t\tvar s = Math.Round(k.Value, 10).ToS();\n\t\t\treturn k.IsStar ? s + \"*\" : s;\n\t\t\t//GridLength.ToString is almost same, but: for Auto returns \"Auto\"; can return long string like \"425.79999999999995\" instead of \"425.8\".\n\t\t}\n\n\t\t#endregion\n\t}\n}\n"
  },
  {
    "path": "Au.Controls/KPanels/tab.cs",
    "content": "using System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Media;\nusing System.Windows.Input;\nusing System.Windows.Interop;\n\nnamespace Au.Controls;\n\npublic partial class KPanels {\n\tpartial class _Node {\n\t\tstatic readonly Style\n\t\t\ts_styleTabControl = XamlResources.Dictionary[\"AuPanelsTabControlStyle\"] as Style,\n\t\t\ts_styleTabItem = XamlResources.Dictionary[\"AuPanelsTabItemStyle\"] as Style,\n\t\t\ts_styleTabLeft = XamlResources.Dictionary[\"TabItemVerticalLeft\"] as Style,\n\t\t\ts_tyleTabRight = XamlResources.Dictionary[\"TabItemVerticalRight\"] as Style;\n\t\t\n\t\tvoid _InitTabControl() {\n\t\t\tvar tc = _tab.tc;\n\t\t\ttc.Style = s_styleTabControl;\n\t\t\tif (_pm.HeaderBrush != Brushes.LightSteelBlue) {\n\t\t\t\ttc.ApplyTemplate();\n\t\t\t\tif (VisualTreeHelper.GetChild(tc, 0) is Grid tg) tg.Background = _pm.HeaderBrush; //note: tc must have a parent.\n\t\t\t}\n\t\t\ttc.Padding = default;\n\t\t\ttc.TabStripPlacement = _headerAt;\n\t\t\ttc.SizeChanged += (_, e) => {\n\t\t\t\tswitch (tc.TabStripPlacement) { case Dock.Top: case Dock.Bottom: return; }\n\t\t\t\tbool bigger = e.NewSize.Height > e.PreviousSize.Height;\n\t\t\t\tif (bigger != _tab.isVerticalHeader) _VerticalTabHeader(e.NewSize.Height);\n\t\t\t};\n\t\t\ttc.ContextMenuOpening += _HeaderContextMenu;\n\t\t\ttc.PreviewMouseDown += _OnMouseDown;\n\t\t\ttc.SelectionChanged += (o, e) => {\n\t\t\t\tif (e.Source != o) return; //eg a descendant ComboBox\n\t\t\t\te.Handled = true;\n\t\t\t\tif (e.AddedItems.Count == 0) return;\n\t\t\t\tvar v = (e.AddedItems[0] as TabItem).Tag as _Node;\n\t\t\t\tv.TabSelected?.Invoke(v, EventArgs.Empty);\n\t\t\t};\n\t\t\t\n\t\t\t//implement DontFocusTab (prevent changing focus when clicking a tabitem)\n\t\t\ttc.PreviewMouseLeftButtonDown += (_, e) => {\n\t\t\t\tif (e.Source is TabItem ti && ti.Tag is _Node n && n.DontFocusTab != null) {\n\t\t\t\t\tbool focusWithin;\n\t\t\t\t\tif (Keyboard.FocusedElement != null) {\n\t\t\t\t\t\tfocusWithin = tc.IsKeyboardFocusWithin;\n\t\t\t\t\t} else {\n\t\t\t\t\t\twnd w = Api.GetFocus();\n\t\t\t\t\t\tif (!w.IsChildOf(ti.Hwnd())) return;\n\t\t\t\t\t\tfocusWithin = null != tc.FindVisualDescendant(o => o is HwndHost hh && hh.Handle == w.Handle);\n\t\t\t\t\t}\n\t\t\t\t\tif (focusWithin && ti.IsSelected) return;\n\t\t\t\t\t\n\t\t\t\t\tti.PreviewGotKeyboardFocus += _PreviewGotKeyboardFocus;\n\t\t\t\t\tti.Dispatcher.InvokeAsync(() => { ti.PreviewGotKeyboardFocus -= _PreviewGotKeyboardFocus; });\n\t\t\t\t\tvoid _PreviewGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e) {\n\t\t\t\t\t\te.Handled = true;\n\t\t\t\t\t\tif (focusWithin) ti.Dispatcher.InvokeAsync(n.DontFocusTab);\n\t\t\t\t\t}\n\t\t\t\t\t//note: cannot implement it in _AddToTab, because then we don't know whether the tab header clicked or some descendant.\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t\t\n\t\tvoid _VerticalTabHeader(double height = -1, bool onMove = false) {\n\t\t\tvar tc = _tab.tc;\n\t\t\tif (tc.TabStripPlacement is Dock.Top or Dock.Bottom) return;\n\t\t\t\n\t\t\tif (height < 0) height = tc.ActualHeight;\n\t\t\t\n\t\t\tvar d = _CalcHeight(); //not too slow\n\t\t\tbool vertHeader = d < height - 10;\n\t\t\tif (vertHeader == _tab.isVerticalHeader && !onMove) return;\n\t\t\t_tab.isVerticalHeader = vertHeader;\n\t\t\tvar dock = tc.TabStripPlacement;\n\t\t\tforeach (TabItem v in tc.Items) {\n\t\t\t\tv.Style = vertHeader ? (dock == Dock.Left ? s_styleTabLeft : s_tyleTabRight) : s_styleTabItem;\n\t\t\t}\n\t\t\t\n\t\t\tdouble _CalcHeight() {\n\t\t\t\tvar cult = CultureInfo.InvariantCulture;\n\t\t\t\tvar fdir = tc.FlowDirection;\n\t\t\t\tvar font = new Typeface(tc.FontFamily, tc.FontStyle, tc.FontWeight, tc.FontStretch);\n\t\t\t\tvar fsize = tc.FontSize;\n\t\t\t\tvar brush = SystemColors.ControlTextBrush;\n\t\t\t\t//var ppd = VisualTreeHelper.GetDpi(tc).PixelsPerDip; print.it(ppd); //ignored, and we don't need it\n\t\t\t\tdouble r = 4;\n\t\t\t\tforeach (TabItem v in tc.Items) {\n\t\t\t\t\tvar f = new FormattedText(v.Header.ToString(), cult, fdir, font, fsize, brush, 1);\n\t\t\t\t\tr += f.Width + 11;\n\t\t\t\t}\n\t\t\t\treturn r;\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Adds this to parent tab at startup or when moving.\n\t\t/// Caller before must call AddChild (or AddSibling) and set _index.\n\t\t/// </summary>\n\t\tvoid _AddToTab(bool moving) {\n\t\t\tvar ti = new TabItem { Style = s_styleTabItem, Header = _leaf.name, Content = _elem, Tag = this };\n\t\t\tvar tc = Parent._tab.tc;\n\t\t\ttc.Items.Insert(_index, ti);\n\t\t\tif (moving) {\n\t\t\t\t_ShiftSiblingIndices(1);\n\t\t\t\tParent._VerticalTabHeader(onMove: true);\n\t\t\t}\n\t\t}\n\t\t\n\t\tvoid _ShowHideInTab(bool show) {\n\t\t\tvar tc = Parent._tab.tc;\n\t\t\tvar ti = tc.Items[_index] as TabItem;\n\t\t\tif (!show) {\n\t\t\t\tvar a = tc.Items.OfType<TabItem>().Where(o => o.Visibility == Visibility.Visible).ToArray();\n\t\t\t\tif (a.Length > 1) {\n\t\t\t\t\tif (ti == tc.SelectedItem) {\n\t\t\t\t\t\tint i = Array.IndexOf(a, ti);\n\t\t\t\t\t\tif (++i == a.Length) i -= 2;\n\t\t\t\t\t\ttc.SelectedItem = a[i];\n\t\t\t\t\t}\n\t\t\t\t} else if (!_IsDocument) {\n\t\t\t\t\tif (Parent._state == _DockState.Float) Parent._Hide();\n\t\t\t\t\telse if (!Parent._state.Has(_DockState.Hide)) Parent._ShowHideInStack(show);\n\t\t\t\t}\n\t\t\t\tti.Visibility = Visibility.Collapsed;\n\t\t\t\tti.Content = null;\n\t\t\t} else {\n\t\t\t\tif (tc.Parent == null) {\n\t\t\t\t\tif (Parent._state.Has(_DockState.Float)) Parent._SetDockState(_DockState.Float);\n\t\t\t\t\telse Parent._ShowHideInStack(show);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tti.Content = _elem;\n\t\t\t\tti.Visibility = Visibility.Visible;\n\t\t\t\ttc.SelectedItem = ti;\n\t\t\t}\n\t\t}\n\t\t\n\t\tvoid _ReorderInTab(_Node target, bool after) {\n\t\t\tif (target == this || (after && target.Next == this) || (!after && target.Previous == this)) return;\n\t\t\tRemove(); target.AddSibling(this, after);\n\t\t\tint index = 0; foreach (var v in Parent.Children()) v._index = index++;\n\t\t\t//to avoid auto-selecting next item when removed active item, we remove all inactive items and then add in new order.\n\t\t\tvar tc = Parent._tab.tc;\n\t\t\tvar sel = tc.SelectedItem;\n\t\t\tvar a = tc.Items.OfType<TabItem>().ToArray();\n\t\t\tfor (int i = a.Length; --i >= 0;) if (a[i] != sel) tc.Items.RemoveAt(i);\n\t\t\tArray.Sort(a, (x, y) => (x.Tag as _Node)._index - (y.Tag as _Node)._index);\n\t\t\tfor (int i = 0; i < a.Length; i++) if (a[i] != sel) tc.Items.Insert(i, a[i]);\n\t\t}\n\t\t\n\t\tstatic _Node _NodeFromTabItem(TabItem ti) => ti.Tag as _Node;\n\t}\n\t\n\tclass _TabControl : TabControl {\n\t\tprotected override void OnKeyDown(KeyEventArgs e) {\n\t\t\t//Apps often use Ctrl+Tab and Ctrl+Shift+Tab eg to switch documents, but TabControl would steal them for switching tabs.\n\t\t\t//\tTo swith TabControl tabs also can be used Shift+Tab (makes tab item focused) then arrows.\n\t\t\tif (e.Key == Key.Tab && Keyboard.Modifiers is ModifierKeys.Control or (ModifierKeys.Control | ModifierKeys.Shift)) return;\n\t\t\tbase.OnKeyDown(e);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au.Controls/KScintilla/KScintilla.cs",
    "content": "using System.Windows;\nusing System.Windows.Interop;\nusing System.Windows.Input;\n\nnamespace Au.Controls;\n\nusing static Sci;\n\n/// <summary>\n/// This .NET control wraps native Scintilla control.\n/// It is not a universal Scintilla wrapper class. Just for this library and related software.\n/// </summary>\n/// <remarks>\n/// Most functions throw ArgumentOutOfRangeException when: 1. A position or line index argument is negative. 2. Scintilla returned a negative position or line index.\n/// If a position or line index argument is greater than text length or the number of lines, some functions return the text length or the last line, and it is documented; for other functions the behaviour is undefined, eg ArgumentOutOfRangeException or Scintilla's return value or like of the documented methods.\n/// \n/// Function/event names start with aa, because VS intellisense cannot group by inheritance and would mix with 300 WPF functions/events.\n/// </remarks>\npublic unsafe partial class KScintilla : HwndHost {\n\twnd _w;\n\tWNDPROC _wndproc;\n\tnint _wndprocScintilla;\n\tnint _sciPtr;\n\tSci_NotifyCallback _notifyCallback;\n\tint _managedThreadId;\n\tinternal int _dpi;\n\t\n#if DEBUG\n\tpublic bool test_; //we use many scintilla controls, but often want to test something on one of them. Then set test_ = true...\n#endif\n\t\n\tstatic KScintilla() {\n\t\tCpp.LoadAuNativeDll(SCINTILLA_DLL);\n\t}\n\t\n\tpublic KScintilla() {\n\t\t_adapter = new(this);\n\t}\n\t\n\t//public nint AaSciPtr => _sciPtr;\n\tpublic nint AaSciPtr {\n\t\tget {\n\t\t\tDebug.Assert(_sciPtr != 0);\n\t\t\treturn _sciPtr;\n\t\t}\n\t}\n\t\n\tpublic SciImages AaImages { get; private set; }\n\n\tpublic SciTags AaTags => field ??= (AaInitTagsStyle == AaTagsStyle.NoTags ? null : new(this));\n\t//can be used to add user-defined tags before creating window handle\n\t\n\t#region HwndHost\n\t\n\tpublic wnd AaWnd => _w;\n\t\n\t/// <summary>\n\t/// Invoked by <b>AaOnHandleCreated</b>, which is called by <see cref=\"BuildWindowCore\"/> after initializing everything but before setting text and subclassing.\n\t/// </summary>\n\tpublic event Action<KScintilla> AaHandleCreated;\n\t\n\t/// <summary>\n\t/// Called by <see cref=\"BuildWindowCore\"/> after initializing everything but before setting text and subclassing.\n\t/// Invokes event <see cref=\"AaHandleCreated\"/>.\n\t/// </summary>\n\tprotected virtual void AaOnHandleCreated() => AaHandleCreated?.Invoke(this);\n\t\n\tprotected override HandleRef BuildWindowCore(HandleRef hwndParent) {\n\t\tvar wParent = (wnd)hwndParent.Handle;\n\t\t_dpi = Dpi.OfWindow(wParent);\n\t\tWS style = WS.CHILD; if (AaInitBorder) style |= WS.BORDER;\n\t\t//note: no WS_VISIBLE. WPF will manage it. It can cause visual artefacts occasionally, eg scrollbar in WPF area.\n\t\t_w = Api.CreateWindowEx(0, \"Scintilla\", Name, style, 0, 0, 0, 0, wParent);\n\t\t//size 0 0 is not the best, but it is a workaround for WPF bugs\n\t\t\n\t\t_sciPtr = _w.Send(SCI_GETDIRECTPOINTER);\n\t\t_managedThreadId = Environment.CurrentManagedThreadId;\n\t\tCall(SCI_SETNOTIFYCALLBACK, 0, Marshal.GetFunctionPointerForDelegate(_notifyCallback = _NotifyCallback));\n\t\t\n\t\tbool hasTags = AaInitTagsStyle != AaTagsStyle.NoTags;\n\t\tif (AaInitReadOnlyAlways) {\n\t\t\tMOD mask = 0;\n\t\t\tif (AaInitImages || hasTags) mask |= MOD.SC_MOD_INSERTTEXT | MOD.SC_MOD_DELETETEXT;\n\t\t\tCall(SCI_SETMODEVENTMASK, (int)mask);\n\t\t}\n\t\t_InitDocument();\n\t\tCall(SCI_SETSCROLLWIDTHTRACKING, 1);\n\t\tCall(SCI_SETSCROLLWIDTH, 1); //TODO3: later make narrower when need, eg when folded long lines (alas there is no direct notification). Maybe use timer.\n\t\tif (!AaInitUseDefaultContextMenu) Call(SCI_USEPOPUP);\n\t\tCall(SCI_SETCARETWIDTH, 2); //not DPI-scaled\n\t\t\n\t\t//Need to set selection colors or layer, because the default inactive selection color is darker than active.\n\t\t//\tIt is 0x3F808080, but alpha is ignored if SC_LAYER_BASE (default).\n\t\tCall(SCI_SETSELECTIONLAYER, SC_LAYER_UNDER_TEXT);\n\t\taaaSetElementColor(SC_ELEMENT_SELECTION_BACK, 0xA0A0A0A0); //use alpha to mix with indicators\n\t\taaaSetElementColor(SC_ELEMENT_SELECTION_INACTIVE_BACK, 0x60A0A0A0);\n\t\taaaSetElementColor(SC_ELEMENT_SELECTION_ADDITIONAL_BACK, 0x60A0A0A0);\n\t\t\n\t\tif (AaInitWrapVisuals) {\n\t\t\tCall(SCI_SETWRAPVISUALFLAGS, SC_WRAPVISUALFLAG_START | SC_WRAPVISUALFLAG_END);\n\t\t\tCall(SCI_SETWRAPVISUALFLAGSLOCATION, SC_WRAPVISUALFLAGLOC_END_BY_TEXT);\n\t\t\tCall(SCI_SETWRAPINDENTMODE, SC_WRAPINDENT_SAME);\n\t\t}\n\t\tif (AaWrapLines) {\n\t\t\tCall(SCI_SETWRAPMODE, SC_WRAP_WORD);\n\t\t}\n\t\t\n\t\t//note: cannot set styles here, because later derived class will call aaaStyleClearAll, which sets some special styles.\n\t\t\n\t\tif (AaInitImages) AaImages = new SciImages(this);\n\t\t\n\t\tif (FocusManager.GetFocusScope(this) is Window fs && FocusManager.GetFocusedElement(fs) == this && Api.GetFocus() == wParent)\n\t\t\tApi.SetFocus(_w);\n\t\t\n\t\tAaOnHandleCreated();\n\t\t\n\t\t_adapter.HandleCreated(); //after derived classes set styles etc\n\t\t\n\t\t_wndprocScintilla = Api.SetWindowLongPtr(_w, GWL.WNDPROC, Marshal.GetFunctionPointerForDelegate(_wndproc = _WndProc));\n\t\t//WPF will subclass this window. It respects the GWL.WNDPROC subclass, but breaks SetWindowSubclass.\n\t\t\n\t\treturn new HandleRef(this, _w.Handle);\n\t}\n\t\n\tvoid _InitDocument() {\n\t\t//these must be set for each document of this Scintilla window\n\t\t\n\t\tCall(SCI_SETCODEPAGE, Api.CP_UTF8);\n\t\tCall(SCI_SETTABWIDTH, 4);\n\t\tif (AaInitReadOnlyAlways) {\n\t\t\tCall(SCI_SETREADONLY, 1);\n\t\t\tCall(SCI_SETUNDOCOLLECTION);\n\t\t} //else if (_isReadOnly) Call(SCI_SETREADONLY, 1);\n\t}\n\t\n\tprotected override void DestroyWindowCore(HandleRef hwnd) {\n\t\tWndUtil.DestroyWindow((wnd)hwnd.Handle);\n\t\t_w = default;\n\t\t_sciPtr = 0;\n\t\t_acc?.Dispose(); _acc = null;\n\t\t\n\t\t//workaround for: never GC-collected if disposed before removing from parent WPF element (shouldn't do it).\n\t\tif (this is IKeyboardInputSink iks) {\n\t\t\tDebug_.PrintIf(iks.KeyboardInputSite != null);\n\t\t\tiks.KeyboardInputSite?.Unregister();\n\t\t}\n\t\t\n\t\t//GC.ReRegisterForFinalize(this); //to detect memory leak\n\t}\n\t\n\t//~KScintilla() { print.it(\"~KScintilla\"); } //to detect memory leak. Also enable the GC.ReRegisterForFinalize.\n\t\n\t//static PrintMsgOptions s_pmo = new(Api.WM_TIMER, Api.WM_MOUSEMOVE, Api.WM_SETCURSOR, Api.WM_NCHITTEST, Api.WM_PAINT, Api.WM_IME_SETCONTEXT, Api.WM_IME_NOTIFY);\n\t\n\tnint _WndProc(wnd w, int msg, nint wp, nint lp) {\n\t\t//if (Name == \"x\") WndUtil.PrintMsg(w, msg, wp, lp);\n\t\t//if(Name == \"x\") WndUtil.PrintMsg(_w, msg, wp, lp, s_pmo);\n\t\t\n\t\tswitch (msg) {\n\t\tcase Api.WM_SETFOCUS:\n\t\t\tif (!_inOnWmSetFocus) if (_OnWmSetFocus()) return 0;\n\t\t\tbreak;\n\t\tcase Api.WM_KILLFOCUS:\n\t\t\tif (_inOnWmSetFocus) return 0;\n\t\t\tbreak;\n\t\tcase Api.WM_LBUTTONDOWN or Api.WM_RBUTTONDOWN or Api.WM_MBUTTONDOWN:\n\t\t\tif (Api.GetFocus() != _w) {\n\t\t\t\tbool setFocus = !AaNoMouseSetFocus.Has(_MouseButton(msg));\n\t\t\t\tif (setFocus && msg == Api.WM_LBUTTONDOWN && AaInitReadOnlyAlways) { //don't focus if link clicked\n\t\t\t\t\tint pos = Call(SCI_CHARPOSITIONFROMPOINTCLOSE, Math2.LoShort(lp), Math2.HiShort(lp));\n\t\t\t\t\tif (pos >= 0) {\n\t\t\t\t\t\tif (aaaStyleHotspot(aaaStyleGetAt(pos))) setFocus = false;\n\t\t\t\t\t\telse { //indicator-link?\n\t\t\t\t\t\t\tuint indic = (uint)Call(SCI_INDICATORALLONFOR, pos);\n\t\t\t\t\t\t\tfor (int i = 0; indic != 0; i++, indic >>>= 1)\n\t\t\t\t\t\t\t\tif (0 != (indic & 1) && 0 != Call(SCI_INDICGETHOVERFORE, i))\n\t\t\t\t\t\t\t\t\tsetFocus = false;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (setFocus) this.Focus();\n\t\t\t}\n\t\t\tbreak;\n\t\tcase Api.WM_LBUTTONUP:\n\t\t\tif (AaInitReadOnlyAlways && Api.GetFocus() != _w)\n\t\t\t\tif (aaaHasSelection) this.Focus();\n\t\t\tbreak;\n\t\t}\n\t\t\n\t\tstatic MButtons _MouseButton(int msg) => msg switch { Api.WM_LBUTTONDOWN or Api.WM_LBUTTONUP => MButtons.Left, Api.WM_RBUTTONDOWN or Api.WM_RBUTTONUP => MButtons.Right, Api.WM_MBUTTONDOWN or Api.WM_MBUTTONUP => MButtons.Middle, _ => 0 };\n\t\t\n\t\tvar R = WndProc(w, msg, wp, lp);\n\t\t\n\t\tswitch (msg) {\n\t\tcase Api.WM_TIMER when wp == 5:\n\t\t\t//Workaround for Scintilla bug: bad scrollbar pos on \"open file and go to line\".\n\t\t\t//\tScintilla adds scrollbars after ~400 ms. For it uses this timer.\n\t\t\tApi.SCROLLINFO x = new(Api.SIF_POS);\n\t\t\tif (x.Get(w, true)) {\n\t\t\t\tint line = Call(Sci.SCI_GETFIRSTVISIBLELINE);\n\t\t\t\tif (x.nPos != line) { x.nPos = line; x.Set(w, true); }\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\t\n\t\treturn R;\n\t}\n\t\n\tprotected virtual nint WndProc(wnd w, int msg, nint wp, nint lp) {\n\t\t//return CallRetPtr(msg, wp, lp); //no, then Scintilla does not process WM_NCDESTROY\n\t\treturn Api.CallWindowProc(_wndprocScintilla, w, msg, wp, lp);\n\t}\n\t\n\tprotected override IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) {\n\t\tif (msg == Api.WM_GETOBJECT) { //WPF steals it from _WndProc\n\t\t\thandled = true;\n\t\t\treturn (_acc ??= new _Accessible(this)).WmGetobject(wParam, lParam);\n\t\t}\n\t\treturn base.WndProc(hwnd, msg, wParam, lParam, ref handled);\n\t}\n\t\n\tprotected override void OnDpiChanged(DpiScale oldDpi, DpiScale newDpi) {\n\t\tif (!_w.Is0 && newDpi.PixelsPerDip != oldDpi.PixelsPerDip) {\n\t\t\t_dpi = newDpi.PixelsPerInchY.ToInt();\n\t\t\t_MarginWidthsDpiChanged();\n\t\t}\n\t\tbase.OnDpiChanged(oldDpi, newDpi);\n\t}\n\t\n\t#region problems with focus, keyboard, destroying\n\t\n\t//Somehow WPF does not care about native control focus, normal keyboard work, destroying, etc.\n\t//1. No Tab key navigation. Also does not set focus when parent tab item selected.\n\t//\tWorkaround: override TabIntoCore and call API SetFocus.\n\t//2. Does not set logical focus to HwndHost when its native control is really focused. Then eg does not restore real focus after using menu.\n\t//\tWorkaround: set focus on WM_LBUTTONDOWN etc. Also on WM_SETFOCUS, with some tricks.\n\t//3. Steals arrow keys, Tab and Enter from native control and sets focus to other controls or closes dialog.\n\t//\tWorkaround: override TranslateAcceleratorCore, pass the keys to the control and return true.\n\t//4. When closing parent window, does not destroy hwnhosted controls. Instead moves to a hidden parking window, and destroys later on GC if you are careful.\n\t//\tNeed to always test whether hwnhosted controls are destroyed on GC, to avoid leaked windows + many managed objects.\n\t//\tEg to protect wndproc delegate from GC don't add it to a thread-static array until destroyed; let it be a field of the wrapper class.\n\t//\tOr let app dispose the HwndHost in OnClosing. But control itself cannot reliably know when to self-destroy.\n\t//5. When closing parent window, briefly tries to show native control, and focus if was focused.\n\t//\tWorkaround: let app dispose the HwndHost in OnClosing.\n\t//Never mind: after SetFocus, Keyboard.FocusedElement is null.\n\t\n\tbool _OnWmSetFocus() {\n\t\t//keep logical focus on HwndHost, else will not work eg restoring of real focus when closing menu.\n\t\tif (IsVisible && Focusable) { //info: !IsVisible when closing window without disposing this (WPF bug)\n\t\t\tvar fs = FocusManager.GetFocusScope(this);\n\t\t\tif (fs != null && FocusManager.GetFocusedElement(fs) != this) { //focused not by WPF\n\t\t\t\t_inOnWmSetFocus = true;\n\t\t\t\tFocusManager.SetFocusedElement(fs, this); //in some cases would work better than this.Focus()\n\t\t\t\t_inOnWmSetFocus = false;\n\t\t\t\t//all WPF 'Focus' functions make the main window focused. Then OnGotKeyboardFocus makes _w focused again.\n\t\t\t\t//\tWndproc receives wm_setfocus, wm_killfocus and wm_setfocus. Passes to scintilla only the last wm_setfocus.\n\t\t\t\t//\tCan prevent this, eg set IsFocusable=false before, but then WPF in some cases does not restore focus after switching windows etc.\n\t\t\t\t//\tTo prevent this on click, Wndproc calls Focus, and scintilla does not call SetFocus (mod).\n\t\t\t\t//\tCould not find a way to avoid this in other cases, never mind.\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\tbool _inOnWmSetFocus;\n\t\n\t//Makes _w focused when called this.Focus() or Keyboard.Focus(this).\n\tprotected override void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e) {\n\t\te.Handled = true;\n\t\tApi.SetFocus(_w);\n\t\tbase.OnGotKeyboardFocus(e);\n\t}\n\t\n\t//Sets focus when tabbed to this or when clicked the parent tab item. Like eg WPF TextBox.\n\tprotected override bool TabIntoCore(TraversalRequest request) {\n\t\tFocus();\n\t\treturn true;\n\t\t//base.TabIntoCore(request); //empty func, returns false\n\t}\n\t\n\tprotected override bool TranslateAcceleratorCore(ref System.Windows.Interop.MSG msg, ModifierKeys modifiers) {\n\t\tvar m = msg.message;\n\t\tvar k = (KKey)msg.wParam;\n\t\t//if (m == Api.WM_KEYDOWN) print.it(m, k);\n\t\tif (m is Api.WM_KEYDOWN or Api.WM_KEYUP /*or Api.WM_SYSKEYDOWN or Api.WM_SYSKEYUP*/)\n\t\t\tif (!modifiers.Has(ModifierKeys.Alt)) {\n\t\t\t\tswitch (k) {\n\t\t\t\tcase KKey.Left or KKey.Right or KKey.Up or KKey.Down:\n\t\t\t\tcase KKey.Enter when modifiers == 0 && !aaaIsReadonly:\n\t\t\t\tcase KKey.Tab when !modifiers.Has(ModifierKeys.Control) && !aaaIsReadonly:\n\t\t\t\t\tCall(msg.message, msg.wParam, msg.lParam); //not DispatchMessage or Send\n\t\t\t\t\treturn true;\n\t\t\t\tcase KKey.Insert when modifiers == 0: return true;\n\t\t\t\t}\n\t\t\t}\n\t\t\n\t\treturn base.TranslateAcceleratorCore(ref msg, modifiers);\n\t}\n\t\n\t//Without this, user cannot type eg character 'a' in HwndHost'ed control if there is button with text like \"_Apply\".\n\tprotected override bool TranslateCharCore(ref System.Windows.Interop.MSG msg, ModifierKeys modifiers) {\n\t\tif (msg.message is not (Api.WM_CHAR or Api.WM_DEADCHAR)) return false; //WM_SYSCHAR etc if with Alt\n\t\tif (msg.hwnd != _w.Handle) return false; //WPF bug. Eg when on key down the app makes this control focused.\n\t\tif ((int)msg.wParam <= 32) return false; //eg control chars on Ctrl+key\n\t\t_w.Send(msg.message, msg.wParam, msg.lParam); //not Call or WndProc\n\t\treturn true;\n\t}\n\t\n\t#endregion\n\t\n\t#endregion\n\t\n\tvoid _NotifyCallback(void* cbParam, ref SCNotification n) {\n\t\ttry {\n\t\t\tvar code = n.code;\n\t\t\t//if(code != NOTIF.SCN_PAINTED) print.qm2.write(code.ToString());\n\t\t\tswitch (code) {\n\t\t\tcase NOTIF.SCN_MODIFIED:\n\t\t\t\tvar mt = n.modificationType;\n\t\t\t\t//if(this.Name!= \"Output_text\") print.it(mt, n.position);\n\t\t\t\tif (mt.HasAny(MOD.SC_MOD_INSERTTEXT | MOD.SC_MOD_DELETETEXT)) {\n\t\t\t\t\t_adapter.TextModified();\n\t\t\t\t\t\n\t\t\t\t\tbool inserted = mt.Has(MOD.SC_MOD_INSERTTEXT);\n\t\t\t\t\t_RdOnModified(inserted, n);\n\t\t\t\t\t//AaImages?.OnTextChanged_(inserted, n);\n\t\t\t\t\tAaTags?.OnTextChanged_(inserted, n);\n\t\t\t\t} else if (n.modificationType.Has(Sci.MOD.SC_MOD_BEFOREDELETE)) {\n\t\t\t\t\t_DeleteMarkers(ref n);\n\t\t\t\t}\n\t\t\t\t//if(mt.Has(MOD.SC_MOD_CHANGEANNOTATION)) ChangedAnnotation?.Invoke(this, ref n);\n\t\t\t\tif (AaDisableModifiedNotifications) return;\n\t\t\t\tbreak;\n\t\t\tcase NOTIF.SCN_HOTSPOTRELEASECLICK:\n\t\t\t\tif (aaaHasSelection) return;\n\t\t\t\tAaTags?.OnLinkClick_(n.position, 0 != (n.modifiers & SCMOD_CTRL));\n\t\t\t\tbreak;\n\t\t\tcase NOTIF.SCN_INDICATORRELEASE:\n\t\t\t\tif (aaaHasSelection) return;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tAaOnSciNotify(ref n);\n\t\t}\n\t\tcatch (Exception e1) when (!Debugger.IsAttached) {\n\t\t\t//DispatcherUnhandledException not raised on exception here. Let's add handler code here like in App._Main.\n\t\t\tif (1 != dialog.showError(\"Exception\", e1.ToStringWithoutStack(), \"1 Continue|2 Exit\", DFlags.Wider, _w.Window, e1.ToString()))\n\t\t\t\tEnvironment.Exit(1);\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Raises the <see cref=\"AaNotify\"/> event.\n\t/// </summary>\n\tprotected virtual void AaOnSciNotify(ref SCNotification n) {\n\t\tAaNotify?.Invoke(new(this, ref n));\n\t\tswitch (n.code) {\n\t\tcase NOTIF.SCN_MODIFIED:\n\t\t\tif (AaTextChanged is { } e && n.modificationType.HasAny(MOD.SC_MOD_INSERTTEXT | MOD.SC_MOD_DELETETEXT)) e(new(this, ref n));\n\t\t\tbreak;\n\t\t}\n\t}\n\t\n\tpublic ref struct AaEventHandlerArgs {\n\t\tpublic readonly KScintilla c;\n\t\tpublic readonly ref SCNotification n;\n\t\tpublic AaEventHandlerArgs(KScintilla sci, ref SCNotification notif) { c = sci; n = ref notif; }\n\t}\n\t\n\tpublic delegate void AaEventHandler(AaEventHandlerArgs e);\n\t\n\t/// <summary>\n\t/// Occurs when any Scintilla notification is received.\n\t/// </summary>\n\tpublic event AaEventHandler AaNotify;\n\t\n\t/// <summary>\n\t/// Occurs when text changed (<b>SCN_MODIFIED</b> notification with <b>SC_MOD_INSERTTEXT</b> or <b>SC_MOD_DELETETEXT</b>).\n\t/// </summary>\n\tpublic event AaEventHandler AaTextChanged;\n\t\n\t//workaround for: Scintilla does not delete markers of deleted lines. Instead moves to the next or previous line.\n\tvoid _DeleteMarkers(ref SCNotification n) {\n\t\tint end = n.position + n.length;\n\t\tint line1 = aaaLineFromPos(false, n.position), line2 = aaaLineFromPos(false, end);\n\t\tif (end == aaaLen8) line2++;\n\t\tif (line2 <= line1) return;\n\t\tint start1 = aaaLineStart(false, line1);\n\t\tif (start1 < n.position) line1++;\n\t\tfor (int line = line1; line < line2; line++) {\n\t\t\tuint markers = (uint)Call(Sci.SCI_MARKERGET, line); //never mind: no folding markers\n\t\t\tif (markers != 0) AaOnDeletingLineWithMarkers(line, markers);\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Called before deleting a line that contains markers (except folding markers), unless <b>MOD.SC_MOD_BEFOREDELETE</b> notification removed with <b>SCI_SETMODEVENTMASK</b>.\n\t/// Deletes all these markers; an override can prevent it by not calling the base method.\n\t/// </summary>\n\t/// <param name=\"line\"></param>\n\t/// <param name=\"markers\">See SCI_MARKERGET.</param>\n\tprotected virtual void AaOnDeletingLineWithMarkers(int line, uint markers) {\n\t\tfor (uint i = 0, m = markers; m != 0; m >>= 1, i++) {\n\t\t\tif ((m & 1) != 0) Call(SCI_MARKERDELETE, line, (int)i);\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Sends a Scintilla message to the control and returns int.\n\t/// Don't call this function from another thread.\n\t/// </summary>\n\t[DebuggerStepThrough]\n\tpublic int Call(int sciMessage, nint wParam = 0, nint lParam = 0) => (int)CallRetPtr(sciMessage, wParam, lParam);\n\t\n\t/// <summary>\n\t/// Sends a Scintilla message to the control and returns int.\n\t/// Don't call this function from another thread.\n\t/// </summary>\n\t[DebuggerStepThrough]\n\tpublic int Call(int sciMessage, nint wParam, void* lParam) => (int)CallRetPtr(sciMessage, wParam, (nint)lParam);\n\t\n\t/// <summary>\n\t/// Sends a Scintilla message to the control and returns int.\n\t/// Don't call this function from another thread.\n\t/// </summary>\n\t[DebuggerStepThrough]\n\tpublic int Call(int sciMessage, nint wParam, bool lParam) => (int)CallRetPtr(sciMessage, wParam, lParam ? 1 : 0);\n\t\n\t/// <summary>\n\t/// Sends a Scintilla message to the control and returns int.\n\t/// Don't call this function from another thread.\n\t/// </summary>\n\t[DebuggerStepThrough]\n\tpublic int Call(int sciMessage, bool wParam, nint lParam = 0) => (int)CallRetPtr(sciMessage, wParam ? 1 : 0, lParam);\n\t\n\t/// <summary>\n\t/// Sends a Scintilla message to the control and returns nint.\n\t/// Don't call this function from another thread.\n\t/// </summary>\n\t[DebuggerStepThrough]\n\tpublic nint CallRetPtr(int sciMessage, nint wParam = 0, nint lParam = 0) {\n#if DEBUG\n\t\tif (AaDebugPrintMessages_) _DebugPrintMessage(sciMessage);\n\t\t\n\t\tDebug.Assert(_sciPtr != 0);\n\t\t//0 before creating or after destroying Scintilla window.\n\t\t//note: don't auto-create handle. It can be dangerous, create parked control, etc.\n\t\t\n\t\tDebug.Assert(Environment.CurrentManagedThreadId == _managedThreadId);\n\t\t//possible wrong thread eg if an async continuation cannot be executed in correct thread,\n\t\t//\tprobably because there is no WPF SynchronizationContext, eg Application.Run ended on exception.\n#else\n\t\tif (_sciPtr == 0) throw new InvalidOperationException(\"KScintilla.CallRetPtr: _sciPtr==null\");\n\t\tif (Environment.CurrentManagedThreadId != _managedThreadId) throw new InvalidOperationException(\"KScintilla.CallRetPtr: wrong thread\");\n#endif\n\t\t\n\t\treturn Sci_Call(_sciPtr, sciMessage, wParam, lParam);\n\t}\n\t\n#if DEBUG\n\tstatic void _DebugPrintMessage(int sciMessage) {\n\t\tif (sciMessage < SCI_START) return;\n\t\tswitch (sciMessage) {\n\t\tcase SCI_COUNTCODEUNITS:\n\t\tcase SCI_POSITIONRELATIVECODEUNITS:\n\t\tcase SCI_CANUNDO:\n\t\tcase SCI_CANREDO:\n\t\tcase SCI_GETREADONLY:\n\t\tcase SCI_GETSELECTIONEMPTY:\n\t\t\t//case SCI_GETTEXTLENGTH:\n\t\t\treturn;\n\t\t}\n\t\tif (s_debugPM == null) {\n\t\t\ts_debugPM = new();\n\t\t\tforeach (var v in typeof(Sci).GetFields()) {\n\t\t\t\tvar s = v.Name;\n\t\t\t\t//print.it(v.Name);\n\t\t\t\tif (s.Starts(\"SCI_\")) s_debugPM.Add((int)v.GetRawConstantValue(), s);\n\t\t\t}\n\t\t}\n\t\tif (!s_debugPM.TryGetValue(sciMessage, out var k)) {\n\t\t\tk = sciMessage.ToString();\n\t\t}\n\t\tprint.qm2.write(k);\n\t}\n\tstatic Dictionary<int, string> s_debugPM;\n\t\n\tinternal bool AaDebugPrintMessages_ { get; set; }\n#endif\n\t\n\t#region properties\n\t\n\t/// <summary>\n\t/// Border style.\n\t/// Must be set before creating control handle.\n\t/// </summary>\n\tpublic bool AaInitBorder { get; set; }\n\t\n\t/// <summary>\n\t/// Use the default Scintilla's context menu.\n\t/// Must be set before creating control handle.\n\t/// </summary>\n\tpublic bool AaInitUseDefaultContextMenu { get; set; }\n\t\n\t/// <summary>\n\t/// This control is used just to display text, not to edit.\n\t/// Must be set before creating control handle.\n\t/// </summary>\n\tpublic bool AaInitReadOnlyAlways { get; set; }\n\t\n\t/// <summary>\n\t/// Whether to show images specified in tags like &lt;image \"image file path\"&gt;, including icons of non-image file types.\n\t/// Must be set before creating control handle.\n\t/// If false, <see cref=\"AaImages\"/> property is null.\n\t/// </summary>\n\tpublic bool AaInitImages { get; set; }\n\t\n\t/// <summary>\n\t/// See <see cref=\"AaInitTagsStyle\"/>.\n\t/// </summary>\n\tpublic enum AaTagsStyle {\n\t\t/// <summary>Don't support tags. The <see cref=\"AaTags\"/> property is null.</summary>\n\t\tNoTags,\n\t\t\n\t\t/// <summary>Let <see cref=\"aaaText\"/>, aaaSetText and aaaAppendText parse tags when the text has prefix \"&lt;&gt;\".</summary>\n\t\tAutoWithPrefix,\n\t\t\n\t\t/// <summary>Let <see cref=\"aaaText\"/>, aaaSetText and aaaAppendText parse tags always.</summary>\n\t\tAutoAlways,\n\t\t\n\t\t/// <summary>Tags are parsed only when calling Tags.AddText.</summary>\n\t\tUser,\n\t}\n\t\n\t/// <summary>\n\t/// Whether and when supports tags.\n\t/// Must be set before creating control handle.\n\t/// </summary>\n\tpublic AaTagsStyle AaInitTagsStyle { get; set; }\n\t\n\t/// <summary>\n\t/// Whether to show arrows etc to make wrapped lines more visible.\n\t/// Must be set before creating control handle.\n\t/// </summary>\n\tpublic bool AaInitWrapVisuals { get; set; } = true;\n\t\n\t/// <summary>\n\t/// Word-wrap.\n\t/// </summary>\n\tpublic bool AaWrapLines {\n\t\tget => _wrapLines;\n\t\tset {\n\t\t\tif (value != _wrapLines) {\n\t\t\t\t_wrapLines = value;\n\t\t\t\tif (!_w.Is0) Call(SCI_SETWRAPMODE, value ? SC_WRAP_WORD : 0);\n\t\t\t}\n\t\t}\n\t}\n\tbool _wrapLines;\n\t\n\t/// <summary>\n\t/// Whether uses Enter key.\n\t/// If null (default), false if <see cref=\"AaInitReadOnlyAlways\"/> is true.\n\t/// </summary>\n\tpublic bool? AaUsesEnter { get; set; }\n\t\n\t/// <summary>\n\t/// On SCN_MODIFIED notifications suppress <see cref=\"AaOnSciNotify\"/>, <see cref=\"AaNotify\"/> and <see cref=\"AaTextChanged\"/>.\n\t/// Use to temporarily disable 'modified' notifications. Never use SCI_SETMODEVENTMASK, because then the control would stop working correctly.\n\t/// </summary>\n\tpublic bool AaDisableModifiedNotifications { get; set; }\n\t\n\t/// <summary>\n\t/// Don't set focus on mouse left/right/middle button down.\n\t/// </summary>\n\tpublic MButtons AaNoMouseSetFocus { get; set; }\n\t\n\t#endregion\n\t\n\t#region range data\n\t\n\tstruct _RangeData {\n\t\tpublic int from, to;\n\t\tpublic object data;\n\t}\n\t\n\tList<_RangeData> _rd;\n\tbool _rdLocked;\n\t\n\t/// <summary>\n\t/// Attaches any data to a range of text. Like a hidden indicator with attached data of any type.\n\t/// </summary>\n\t/// <exception cref=\"InvalidOperationException\">Called from <see cref=\"AaRangeDataRemoved\"/>.</exception>\n\tpublic void AaRangeDataAdd(bool utf16, Range r, object data) {\n\t\tif (_rdLocked) throw new InvalidOperationException(\"Called from event handler.\");\n\t\tvar (from, to) = aaaNormalizeRange(utf16, r);\n\t\t\n\t\t_rd ??= new();\n\t\t_rd.Add(new() { from = from, to = to, data = data });\n\t}\n\t\n\t/// <summary>\n\t/// Gets data of type <i>T</i> attached to a range of text with <see cref=\"AaRangeDataAdd\"/> at the specified position.\n\t/// </summary>\n\t/// <param name=\"data\">Receives data. Use type <b>object</b> to get data of any type.</param>\n\t/// <returns>true if <i>pos</i> is in a range added with <see cref=\"AaRangeDataAdd\"/> and the range data type is <i>T</i> (or inherited).</returns>\n\t/// <exception cref=\"InvalidOperationException\">Called from <see cref=\"AaRangeDataRemoved\"/>.</exception>\n\tpublic bool AaRangeDataGet<T>(bool utf16, int pos, out T data) where T : class\n\t\t=> AaRangeDataGet(utf16, pos, out data, out _, out _);\n\t\n\t/// <summary>\n\t/// Gets data of type <i>T</i> attached to a range of text with <see cref=\"AaRangeDataAdd\"/> at the specified position.\n\t/// </summary>\n\t/// <param name=\"data\">Receives data. Use type <b>object</b> to get data of any type.</param>\n\t/// <returns>true if <i>pos</i> is in a range added with <see cref=\"AaRangeDataAdd\"/> and the range data type is <i>T</i> (or inherited).</returns>\n\t/// <exception cref=\"InvalidOperationException\">Called from <see cref=\"AaRangeDataRemoved\"/>.</exception>\n\tpublic bool AaRangeDataGet<T>(bool utf16, int pos, out T data, out int from, out int to) where T : class {\n\t\tif (_rdLocked) throw new InvalidOperationException(\"Called from AaRangeDataRemoved.\");\n\t\tpos = _ParamPos(utf16, pos);\n\t\tforeach (ref var v in _rd.AsSpan()) {\n\t\t\tif (pos >= v.from && pos < v.to && v.data is T d) {\n\t\t\t\tdata = d;\n\t\t\t\tfrom = v.from;\n\t\t\t\tto = v.to;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\tdata = null;\n\t\tfrom = 0;\n\t\tto = 0;\n\t\treturn false;\n\t}\n\t\n\t/// <summary>\n\t/// Gets all datas of type <i>T</i> added with <see cref=\"AaRangeDataAdd\"/>.\n\t/// </summary>\n\t/// <returns></returns>\n\tpublic IEnumerable<T> AaRangeDataEnum<T>() {\n\t\tif (!_rd.NE_()) {\n\t\t\tforeach (var v in _rd) if (v.data is T r) yield return r;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// When a text range registered with <see cref=\"AaRangeDataAdd\"/> removed (when control text changed).\n\t/// </summary>\n\t/// <remarks>\n\t/// The event handler must not modify control text and must not call <b>AaRangeDataX</b> functions.\n\t/// </remarks>\n\tpublic event Action<object> AaRangeDataRemoved;\n\t\n\tvoid _RdOnModified(bool inserted, in SCNotification n) {\n\t\tif (_rd.NE_()) return;\n\t\tif (_rdLocked) throw new InvalidOperationException(\"Called from event handler.\");\n\t\t_rdLocked = true;\n\t\ttry {\n\t\t\tint start = n.position, end = start + n.length, len = n.length;\n\t\t\t\n\t\t\tif (inserted) {\n\t\t\t\tforeach (ref var v in _rd.AsSpan()) {\n\t\t\t\t\tif (start < v.to) {\n\t\t\t\t\t\tif (start <= v.from) v.from += len;\n\t\t\t\t\t\tv.to += len;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (start == 0 && aaaLen8 == 0) { //deleted all text\n\t\t\t\t\tif (AaRangeDataRemoved != null) foreach (var v in _rd) AaRangeDataRemoved(v.data);\n\t\t\t\t\t_rd.Clear();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tSystem.Collections.BitArray remove = null;\n\t\t\t\tint j = -1;\n\t\t\t\tforeach (ref var v in _rd.AsSpan()) {\n\t\t\t\t\tj++;\n\t\t\t\t\tif (start < v.to) {\n\t\t\t\t\t\tif (end < v.from) {\n\t\t\t\t\t\t\tv.from -= len;\n\t\t\t\t\t\t\tv.to -= len;\n\t\t\t\t\t\t} else if (start <= v.from) {\n\t\t\t\t\t\t\tif (end < v.to) {\n\t\t\t\t\t\t\t\tv.to -= len;\n\t\t\t\t\t\t\t\tv.from = start;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tremove ??= new(_rd.Count);\n\t\t\t\t\t\t\t\tremove[j] = true;\n\t\t\t\t\t\t\t\tAaRangeDataRemoved?.Invoke(v.data);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else if (end < v.to) {\n\t\t\t\t\t\t\tv.to -= len;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tv.to = start;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (remove != null) {\n\t\t\t\t\tfor (int i = remove.Count; --i >= 0;) {\n\t\t\t\t\t\tif (remove[i]) _rd.RemoveAt(i);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfinally { _rdLocked = false; }\n\t\t\n\t\t//print.it(\"ranges\", _rd.Select(o => (o.from, o.to, o.data)));\n\t}\n\t\n\t#endregion\n\t\n\t#region acc\n\t\n\t_Accessible _acc;\n\t\n\tclass _Accessible : HwndHostAccessibleBase_ {\n\t\treadonly KScintilla _sci;\n\t\t\n\t\tinternal _Accessible(KScintilla sci) : base(sci, sci.AaWnd) {\n\t\t\t_sci = sci;\n\t\t}\n\t\t\n\t\tpublic override ERole Role(int child) => _sci.AaAccessibleRole;\n\t\t\n\t\tpublic override string Name(int child) => _sci.AaAccessibleName;\n\t\t\n\t\tpublic override string Description(int child) => _sci.AaAccessibleDescription;\n\t\t\n\t\tpublic override string Value(int child) => _sci.AaAccessibleValue;\n\t\t\n\t\tpublic override EState State(int child) {\n\t\t\tvar r = base.State(child);\n\t\t\tif (_sci.aaaIsReadonly) r |= EState.READONLY;\n\t\t\treturn r;\n\t\t}\n\t}\n\t\n\tprotected virtual ERole AaAccessibleRole => ERole.TEXT;\n\t\n\tprotected virtual string AaAccessibleName => Name;\n\t\n\tprotected virtual string AaAccessibleDescription => null;\n\t\n\tprotected virtual string AaAccessibleValue => AaInitReadOnlyAlways ? aaaText?.Limit(0xffff) : null;\n\t\n\t#endregion\n}\n"
  },
  {
    "path": "Au.Controls/KScintilla/Sci API.cs",
    "content": "namespace Au.Controls;\n\npublic static unsafe class Sci {\n\t#region au modifications\n\n\tpublic const int SCI_MARGINSTYLENEXT = 9502;\n\tpublic delegate void Sci_NotifyCallback(void* cbParam, ref SCNotification n);\n\tpublic const int SCI_SETNOTIFYCALLBACK = 9503;\n\tpublic delegate int Sci_AnnotationDrawCallback(void* cbParam, ref Sci_AnnotationDrawCallbackData d);\n\tpublic const int SCI_SETANNOTATIONDRAWCALLBACK = 9504;\n\tpublic delegate void Sci_MarginDrawCallback(ref Sci_MarginDrawCallbackData d);\n\tpublic const int SCI_SETMARGINDRAWCALLBACK = 9505;\n\tpublic const int SCI_ISXINMARGIN = 9506;\n\tpublic const int SCI_DRAGDROP = 9507;\n\n\tpublic const string SCINTILLA_DLL = \"Scintilla.dll\";\n\n\t[DllImport(SCINTILLA_DLL, EntryPoint = \"Scintilla_DirectFunction\")]\n\tpublic static extern nint Sci_Call(nint sci, int message, nint wParam = 0, nint lParam = 0);\n\n\t[DllImport(SCINTILLA_DLL)]\n\tpublic static extern int Sci_Range(nint sci, int start8, int end8, out byte* p1, out byte* p2, int* length = null);\n\n\t[DllImport(SCINTILLA_DLL)]\n\tpublic static extern void Sci_SetFoldLevels(nint sci, int line, int lastLine, int len, int* a);\n\n\tpublic record struct Sci_VisibleRange {\n\t\tpublic int dlineFrom, dlineTo, vlineFrom, vlineTo, posFrom, posTo;\n\t}\n\n\t/// <summary>\n\t/// flags: 1 need pos\n\t/// </summary>\n\t[DllImport(SCINTILLA_DLL)]\n\tpublic static extern void Sci_GetVisibleRange(nint sci, out Sci_VisibleRange r);\n\n\t//[DllImport(\"Lexilla.dll\", EntryPoint = \"CreateLexer\")]\n\t//public static extern nint Sci_CreateLexer(byte[] lexer);\n\n#pragma warning disable 649\n\tpublic unsafe struct Sci_AnnotationDrawCallbackData {\n\t\tpublic int step;\n\t\tpublic IntPtr hdc;\n\t\tpublic RECT rect;\n\t\tpublic byte* text;\n\t\tpublic int textLen, line, annotLine;\n\t};\n\tpublic unsafe struct Sci_MarginDrawCallbackData {\n\t\tpublic IntPtr hdc;\n\t\tpublic RECT rect;\n\t\tpublic int margin, firstLine, lastLine;\n\t};\n\tpublic struct Sci_DragDropData {\n\t\tpublic int x, y;\n\t\tpublic byte* text;\n\t\tpublic int len;\n\t\tpublic int copy; //bool\n\t}\n#pragma warning restore 649\n\n\tpublic const int STYLE_HIDDEN = 31; //DEFAULT-1\n\n\t#endregion\n\n\tpublic delegate nint SciFnDirectStatus(nint ptr, int iMessage, nint wParam, nint lParam, out int pStatus);\n\n\tpublic const int INVALID_POSITION = -1;\n\tpublic const int SCI_START = 2000;\n\tpublic const int SCI_OPTIONAL_START = 3000;\n\tpublic const int SCI_LEXER_START = 4000;\n\tpublic const int SCI_ADDTEXT = 2001;\n\tpublic const int SCI_ADDSTYLEDTEXT = 2002;\n\tpublic const int SCI_INSERTTEXT = 2003;\n\tpublic const int SCI_CHANGEINSERTION = 2672;\n\tpublic const int SCI_CLEARALL = 2004;\n\tpublic const int SCI_DELETERANGE = 2645;\n\tpublic const int SCI_CLEARDOCUMENTSTYLE = 2005;\n\tpublic const int SCI_GETLENGTH = 2006;\n\tpublic const int SCI_GETCHARAT = 2007;\n\tpublic const int SCI_GETCURRENTPOS = 2008;\n\tpublic const int SCI_GETANCHOR = 2009;\n\t//public const int SCI_GETSTYLEAT = 2010; //obsolete, use SCI_GETSTYLEINDEXAT\n\tpublic const int SCI_GETSTYLEINDEXAT = 2038;\n\tpublic const int SCI_REDO = 2011;\n\tpublic const int SCI_SETUNDOCOLLECTION = 2012;\n\tpublic const int SCI_SELECTALL = 2013;\n\tpublic const int SCI_SETSAVEPOINT = 2014;\n\tpublic const int SCI_GETSTYLEDTEXT = 2015;\n\tpublic const int SCI_GETSTYLEDTEXTFULL = 2778;\n\tpublic const int SCI_CANREDO = 2016;\n\tpublic const int SCI_MARKERLINEFROMHANDLE = 2017;\n\tpublic const int SCI_MARKERDELETEHANDLE = 2018;\n\tpublic const int SCI_MARKERHANDLEFROMLINE = 2732;\n\tpublic const int SCI_MARKERNUMBERFROMLINE = 2733;\n\tpublic const int SCI_GETUNDOCOLLECTION = 2019;\n\tpublic const int SCWS_INVISIBLE = 0;\n\tpublic const int SCWS_VISIBLEALWAYS = 1;\n\tpublic const int SCWS_VISIBLEAFTERINDENT = 2;\n\tpublic const int SCWS_VISIBLEONLYININDENT = 3;\n\tpublic const int SCI_GETVIEWWS = 2020;\n\tpublic const int SCI_SETVIEWWS = 2021;\n\tpublic const int SCTD_LONGARROW = 0;\n\tpublic const int SCTD_STRIKEOUT = 1;\n\tpublic const int SCI_GETTABDRAWMODE = 2698;\n\tpublic const int SCI_SETTABDRAWMODE = 2699;\n\tpublic const int SCI_POSITIONFROMPOINT = 2022;\n\tpublic const int SCI_POSITIONFROMPOINTCLOSE = 2023;\n\tpublic const int SCI_GOTOLINE = 2024;\n\tpublic const int SCI_GOTOPOS = 2025;\n\tpublic const int SCI_SETANCHOR = 2026;\n\tpublic const int SCI_GETCURLINE = 2027;\n\tpublic const int SCI_GETENDSTYLED = 2028;\n\tpublic const int SC_EOL_CRLF = 0;\n\tpublic const int SC_EOL_CR = 1;\n\tpublic const int SC_EOL_LF = 2;\n\tpublic const int SCI_CONVERTEOLS = 2029;\n\tpublic const int SCI_GETEOLMODE = 2030;\n\tpublic const int SCI_SETEOLMODE = 2031;\n\tpublic const int SCI_STARTSTYLING = 2032;\n\tpublic const int SCI_SETSTYLING = 2033;\n\tpublic const int SCI_GETBUFFEREDDRAW = 2034;\n\tpublic const int SCI_SETBUFFEREDDRAW = 2035;\n\tpublic const int SCI_SETTABWIDTH = 2036;\n\tpublic const int SCI_GETTABWIDTH = 2121;\n\tpublic const int SCI_CLEARTABSTOPS = 2675;\n\tpublic const int SCI_ADDTABSTOP = 2676;\n\tpublic const int SCI_GETNEXTTABSTOP = 2677;\n\tpublic const int SC_CP_UTF8 = 65001;\n\tpublic const int SCI_SETCODEPAGE = 2037;\n\tpublic const int SCI_SETFONTLOCALE = 2760;\n\tpublic const int SCI_GETFONTLOCALE = 2761;\n\tpublic const int SC_IME_WINDOWED = 0;\n\tpublic const int SC_IME_INLINE = 1;\n\tpublic const int SCI_GETIMEINTERACTION = 2678;\n\tpublic const int SCI_SETIMEINTERACTION = 2679;\n\tpublic const int MARKER_MAX = 31;\n\tpublic const int SC_MARK_CIRCLE = 0;\n\tpublic const int SC_MARK_ROUNDRECT = 1;\n\tpublic const int SC_MARK_ARROW = 2;\n\tpublic const int SC_MARK_SMALLRECT = 3;\n\tpublic const int SC_MARK_SHORTARROW = 4;\n\tpublic const int SC_MARK_EMPTY = 5;\n\tpublic const int SC_MARK_ARROWDOWN = 6;\n\tpublic const int SC_MARK_MINUS = 7;\n\tpublic const int SC_MARK_PLUS = 8;\n\tpublic const int SC_MARK_VLINE = 9;\n\tpublic const int SC_MARK_LCORNER = 10;\n\tpublic const int SC_MARK_TCORNER = 11;\n\tpublic const int SC_MARK_BOXPLUS = 12;\n\tpublic const int SC_MARK_BOXPLUSCONNECTED = 13;\n\tpublic const int SC_MARK_BOXMINUS = 14;\n\tpublic const int SC_MARK_BOXMINUSCONNECTED = 15;\n\tpublic const int SC_MARK_LCORNERCURVE = 16;\n\tpublic const int SC_MARK_TCORNERCURVE = 17;\n\tpublic const int SC_MARK_CIRCLEPLUS = 18;\n\tpublic const int SC_MARK_CIRCLEPLUSCONNECTED = 19;\n\tpublic const int SC_MARK_CIRCLEMINUS = 20;\n\tpublic const int SC_MARK_CIRCLEMINUSCONNECTED = 21;\n\tpublic const int SC_MARK_BACKGROUND = 22;\n\tpublic const int SC_MARK_DOTDOTDOT = 23;\n\tpublic const int SC_MARK_ARROWS = 24;\n\tpublic const int SC_MARK_PIXMAP = 25;\n\tpublic const int SC_MARK_FULLRECT = 26;\n\tpublic const int SC_MARK_LEFTRECT = 27;\n\tpublic const int SC_MARK_AVAILABLE = 28;\n\tpublic const int SC_MARK_UNDERLINE = 29;\n\tpublic const int SC_MARK_RGBAIMAGE = 30;\n\tpublic const int SC_MARK_BOOKMARK = 31;\n\tpublic const int SC_MARK_VERTICALBOOKMARK = 32;\n\tpublic const int SC_MARK_BAR = 33;\n\tpublic const int SC_MARK_CHARACTER = 10000;\n\tpublic const int SC_MARKNUM_HISTORY_REVERTED_TO_ORIGIN = 21;\n\tpublic const int SC_MARKNUM_HISTORY_SAVED = 22;\n\tpublic const int SC_MARKNUM_HISTORY_MODIFIED = 23;\n\tpublic const int SC_MARKNUM_HISTORY_REVERTED_TO_MODIFIED = 24;\n\tpublic const int SC_MARKNUM_FOLDEREND = 25;\n\tpublic const int SC_MARKNUM_FOLDEROPENMID = 26;\n\tpublic const int SC_MARKNUM_FOLDERMIDTAIL = 27;\n\tpublic const int SC_MARKNUM_FOLDERTAIL = 28;\n\tpublic const int SC_MARKNUM_FOLDERSUB = 29;\n\tpublic const int SC_MARKNUM_FOLDER = 30;\n\tpublic const int SC_MARKNUM_FOLDEROPEN = 31;\n\tpublic const int SC_MASK_HISTORY = 0x01E00000;\n\tpublic const int SC_MASK_FOLDERS = unchecked((int)0xFE000000);\n\tpublic const int SCI_MARKERDEFINE = 2040;\n\tpublic const int SCI_MARKERSETFORE = 2041;\n\tpublic const int SCI_MARKERSETBACK = 2042;\n\tpublic const int SCI_MARKERSETBACKSELECTED = 2292;\n\tpublic const int SCI_MARKERSETFORETRANSLUCENT = 2294;\n\tpublic const int SCI_MARKERSETBACKTRANSLUCENT = 2295;\n\tpublic const int SCI_MARKERSETBACKSELECTEDTRANSLUCENT = 2296;\n\tpublic const int SCI_MARKERSETSTROKEWIDTH = 2297;\n\tpublic const int SCI_MARKERENABLEHIGHLIGHT = 2293;\n\tpublic const int SCI_MARKERADD = 2043;\n\tpublic const int SCI_MARKERDELETE = 2044;\n\tpublic const int SCI_MARKERDELETEALL = 2045;\n\tpublic const int SCI_MARKERGET = 2046;\n\tpublic const int SCI_MARKERNEXT = 2047;\n\tpublic const int SCI_MARKERPREVIOUS = 2048;\n\tpublic const int SCI_MARKERDEFINEPIXMAP = 2049;\n\tpublic const int SCI_MARKERADDSET = 2466;\n\tpublic const int SCI_MARKERSETALPHA = 2476;\n\tpublic const int SCI_MARKERGETLAYER = 2734;\n\tpublic const int SCI_MARKERSETLAYER = 2735;\n\tpublic const int SC_MAX_MARGIN = 4;\n\tpublic const int SC_MARGIN_SYMBOL = 0;\n\tpublic const int SC_MARGIN_NUMBER = 1;\n\tpublic const int SC_MARGIN_BACK = 2;\n\tpublic const int SC_MARGIN_FORE = 3;\n\tpublic const int SC_MARGIN_TEXT = 4;\n\tpublic const int SC_MARGIN_RTEXT = 5;\n\tpublic const int SC_MARGIN_COLOUR = 6;\n\tpublic const int SCI_SETMARGINTYPEN = 2240;\n\tpublic const int SCI_GETMARGINTYPEN = 2241;\n\tpublic const int SCI_SETMARGINWIDTHN = 2242;\n\tpublic const int SCI_GETMARGINWIDTHN = 2243;\n\tpublic const int SCI_SETMARGINMASKN = 2244;\n\tpublic const int SCI_GETMARGINMASKN = 2245;\n\tpublic const int SCI_SETMARGINSENSITIVEN = 2246;\n\tpublic const int SCI_GETMARGINSENSITIVEN = 2247;\n\tpublic const int SCI_SETMARGINCURSORN = 2248;\n\tpublic const int SCI_GETMARGINCURSORN = 2249;\n\tpublic const int SCI_SETMARGINBACKN = 2250;\n\tpublic const int SCI_GETMARGINBACKN = 2251;\n\tpublic const int SCI_SETMARGINS = 2252;\n\tpublic const int SCI_GETMARGINS = 2253;\n\tpublic const int STYLE_DEFAULT = 32;\n\tpublic const int STYLE_LINENUMBER = 33;\n\tpublic const int STYLE_BRACELIGHT = 34;\n\tpublic const int STYLE_BRACEBAD = 35;\n\tpublic const int STYLE_CONTROLCHAR = 36;\n\tpublic const int STYLE_INDENTGUIDE = 37;\n\tpublic const int STYLE_CALLTIP = 38;\n\tpublic const int STYLE_FOLDDISPLAYTEXT = 39;\n\tpublic const int STYLE_LASTPREDEFINED = 39;\n\tpublic const int STYLE_MAX = 255;\n\tpublic const int SC_CHARSET_ANSI = 0;\n\tpublic const int SC_CHARSET_DEFAULT = 1;\n\tpublic const int SC_CHARSET_BALTIC = 186;\n\tpublic const int SC_CHARSET_CHINESEBIG5 = 136;\n\tpublic const int SC_CHARSET_EASTEUROPE = 238;\n\tpublic const int SC_CHARSET_GB2312 = 134;\n\tpublic const int SC_CHARSET_GREEK = 161;\n\tpublic const int SC_CHARSET_HANGUL = 129;\n\tpublic const int SC_CHARSET_MAC = 77;\n\tpublic const int SC_CHARSET_OEM = 255;\n\tpublic const int SC_CHARSET_RUSSIAN = 204;\n\tpublic const int SC_CHARSET_OEM866 = 866;\n\tpublic const int SC_CHARSET_CYRILLIC = 1251;\n\tpublic const int SC_CHARSET_SHIFTJIS = 128;\n\tpublic const int SC_CHARSET_SYMBOL = 2;\n\tpublic const int SC_CHARSET_TURKISH = 162;\n\tpublic const int SC_CHARSET_JOHAB = 130;\n\tpublic const int SC_CHARSET_HEBREW = 177;\n\tpublic const int SC_CHARSET_ARABIC = 178;\n\tpublic const int SC_CHARSET_VIETNAMESE = 163;\n\tpublic const int SC_CHARSET_THAI = 222;\n\tpublic const int SC_CHARSET_8859_15 = 1000;\n\tpublic const int SCI_STYLECLEARALL = 2050;\n\tpublic const int SCI_STYLESETFORE = 2051;\n\tpublic const int SCI_STYLESETBACK = 2052;\n\tpublic const int SCI_STYLESETBOLD = 2053;\n\tpublic const int SCI_STYLESETITALIC = 2054;\n\tpublic const int SCI_STYLESETSIZE = 2055;\n\tpublic const int SCI_STYLESETFONT = 2056;\n\tpublic const int SCI_STYLESETEOLFILLED = 2057;\n\tpublic const int SCI_STYLERESETDEFAULT = 2058;\n\tpublic const int SCI_STYLESETUNDERLINE = 2059;\n\tpublic const int SC_CASE_MIXED = 0;\n\tpublic const int SC_CASE_UPPER = 1;\n\tpublic const int SC_CASE_LOWER = 2;\n\tpublic const int SC_CASE_CAMEL = 3;\n\tpublic const int SCI_STYLEGETFORE = 2481;\n\tpublic const int SCI_STYLEGETBACK = 2482;\n\tpublic const int SCI_STYLEGETBOLD = 2483;\n\tpublic const int SCI_STYLEGETITALIC = 2484;\n\tpublic const int SCI_STYLEGETSIZE = 2485;\n\tpublic const int SCI_STYLEGETFONT = 2486;\n\tpublic const int SCI_STYLEGETEOLFILLED = 2487;\n\tpublic const int SCI_STYLEGETUNDERLINE = 2488;\n\tpublic const int SCI_STYLEGETCASE = 2489;\n\tpublic const int SCI_STYLEGETCHARACTERSET = 2490;\n\tpublic const int SCI_STYLEGETVISIBLE = 2491;\n\tpublic const int SCI_STYLEGETCHANGEABLE = 2492;\n\tpublic const int SCI_STYLEGETHOTSPOT = 2493;\n\tpublic const int SCI_STYLESETCASE = 2060;\n\tpublic const int SC_FONT_SIZE_MULTIPLIER = 100;\n\tpublic const int SCI_STYLESETSIZEFRACTIONAL = 2061;\n\tpublic const int SCI_STYLEGETSIZEFRACTIONAL = 2062;\n\tpublic const int SC_WEIGHT_NORMAL = 400;\n\tpublic const int SC_WEIGHT_SEMIBOLD = 600;\n\tpublic const int SC_WEIGHT_BOLD = 700;\n\tpublic const int SCI_STYLESETWEIGHT = 2063;\n\tpublic const int SCI_STYLEGETWEIGHT = 2064;\n\tpublic const int SCI_STYLESETCHARACTERSET = 2066;\n\tpublic const int SCI_STYLESETHOTSPOT = 2409;\n\tpublic const int SCI_STYLESETCHECKMONOSPACED = 2254;\n\tpublic const int SCI_STYLEGETCHECKMONOSPACED = 2255;\n\tpublic const int SC_STRETCH_ULTRA_CONDENSED = 1;\n\tpublic const int SC_STRETCH_EXTRA_CONDENSED = 2;\n\tpublic const int SC_STRETCH_CONDENSED = 3;\n\tpublic const int SC_STRETCH_SEMI_CONDENSED = 4;\n\tpublic const int SC_STRETCH_NORMAL = 5;\n\tpublic const int SC_STRETCH_SEMI_EXPANDED = 6;\n\tpublic const int SC_STRETCH_EXPANDED = 7;\n\tpublic const int SC_STRETCH_EXTRA_EXPANDED = 8;\n\tpublic const int SC_STRETCH_ULTRA_EXPANDED = 9;\n\tpublic const int SCI_STYLESETSTRETCH = 2258;\n\tpublic const int SCI_STYLEGETSTRETCH = 2259;\n\tpublic const int SCI_STYLESETINVISIBLEREPRESENTATION = 2256;\n\tpublic const int SCI_STYLEGETINVISIBLEREPRESENTATION = 2257;\n\tpublic const int SC_ELEMENT_LIST = 0;\n\tpublic const int SC_ELEMENT_LIST_BACK = 1;\n\tpublic const int SC_ELEMENT_LIST_SELECTED = 2;\n\tpublic const int SC_ELEMENT_LIST_SELECTED_BACK = 3;\n\tpublic const int SC_ELEMENT_SELECTION_TEXT = 10;\n\tpublic const int SC_ELEMENT_SELECTION_BACK = 11;\n\tpublic const int SC_ELEMENT_SELECTION_ADDITIONAL_TEXT = 12;\n\tpublic const int SC_ELEMENT_SELECTION_ADDITIONAL_BACK = 13;\n\tpublic const int SC_ELEMENT_SELECTION_SECONDARY_TEXT = 14;\n\tpublic const int SC_ELEMENT_SELECTION_SECONDARY_BACK = 15;\n\tpublic const int SC_ELEMENT_SELECTION_INACTIVE_TEXT = 16;\n\tpublic const int SC_ELEMENT_SELECTION_INACTIVE_BACK = 17;\n\tpublic const int SC_ELEMENT_SELECTION_INACTIVE_ADDITIONAL_TEXT = 18;\n\tpublic const int SC_ELEMENT_SELECTION_INACTIVE_ADDITIONAL_BACK = 19;\n\tpublic const int SC_ELEMENT_CARET = 40;\n\tpublic const int SC_ELEMENT_CARET_ADDITIONAL = 41;\n\tpublic const int SC_ELEMENT_CARET_LINE_BACK = 50;\n\tpublic const int SC_ELEMENT_WHITE_SPACE = 60;\n\tpublic const int SC_ELEMENT_WHITE_SPACE_BACK = 61;\n\tpublic const int SC_ELEMENT_HOT_SPOT_ACTIVE = 70;\n\tpublic const int SC_ELEMENT_HOT_SPOT_ACTIVE_BACK = 71;\n\tpublic const int SC_ELEMENT_FOLD_LINE = 80;\n\tpublic const int SC_ELEMENT_HIDDEN_LINE = 81;\n\tpublic const int SCI_SETELEMENTCOLOUR = 2753;\n\tpublic const int SCI_GETELEMENTCOLOUR = 2754;\n\tpublic const int SCI_RESETELEMENTCOLOUR = 2755;\n\tpublic const int SCI_GETELEMENTISSET = 2756;\n\tpublic const int SCI_GETELEMENTALLOWSTRANSLUCENT = 2757;\n\tpublic const int SCI_GETELEMENTBASECOLOUR = 2758;\n\tpublic const int SCI_SETSELFORE = 2067;\n\tpublic const int SCI_SETSELBACK = 2068;\n\tpublic const int SCI_GETSELALPHA = 2477;\n\tpublic const int SCI_SETSELALPHA = 2478;\n\tpublic const int SCI_GETSELEOLFILLED = 2479;\n\tpublic const int SCI_SETSELEOLFILLED = 2480;\n\tpublic const int SC_LAYER_BASE = 0;\n\tpublic const int SC_LAYER_UNDER_TEXT = 1;\n\tpublic const int SC_LAYER_OVER_TEXT = 2;\n\tpublic const int SCI_GETSELECTIONLAYER = 2762;\n\tpublic const int SCI_SETSELECTIONLAYER = 2763;\n\tpublic const int SCI_GETCARETLINELAYER = 2764;\n\tpublic const int SCI_SETCARETLINELAYER = 2765;\n\tpublic const int SCI_GETCARETLINEHIGHLIGHTSUBLINE = 2773;\n\tpublic const int SCI_SETCARETLINEHIGHLIGHTSUBLINE = 2774;\n\tpublic const int SCI_SETCARETFORE = 2069;\n\tpublic const int SCI_ASSIGNCMDKEY = 2070;\n\tpublic const int SCI_CLEARCMDKEY = 2071;\n\tpublic const int SCI_CLEARALLCMDKEYS = 2072;\n\tpublic const int SCI_SETSTYLINGEX = 2073;\n\tpublic const int SCI_STYLESETVISIBLE = 2074;\n\tpublic const int SCI_GETCARETPERIOD = 2075;\n\tpublic const int SCI_SETCARETPERIOD = 2076;\n\tpublic const int SCI_SETWORDCHARS = 2077;\n\tpublic const int SCI_GETWORDCHARS = 2646;\n\tpublic const int SCI_SETCHARACTERCATEGORYOPTIMIZATION = 2720;\n\tpublic const int SCI_GETCHARACTERCATEGORYOPTIMIZATION = 2721;\n\tpublic const int SCI_BEGINUNDOACTION = 2078;\n\tpublic const int SCI_ENDUNDOACTION = 2079;\n\tpublic const int SCI_GETUNDOSEQUENCE = 2799;\n\tpublic const int SCI_GETUNDOACTIONS = 2790;\n\tpublic const int SCI_SETUNDOSAVEPOINT = 2791;\n\tpublic const int SCI_GETUNDOSAVEPOINT = 2792;\n\tpublic const int SCI_SETUNDODETACH = 2793;\n\tpublic const int SCI_GETUNDODETACH = 2794;\n\tpublic const int SCI_SETUNDOTENTATIVE = 2795;\n\tpublic const int SCI_GETUNDOTENTATIVE = 2796;\n\tpublic const int SCI_SETUNDOCURRENT = 2797;\n\tpublic const int SCI_GETUNDOCURRENT = 2798;\n\tpublic const int SCI_PUSHUNDOACTIONTYPE = 2800;\n\tpublic const int SCI_CHANGELASTUNDOACTIONTEXT = 2801;\n\tpublic const int SCI_GETUNDOACTIONTYPE = 2802;\n\tpublic const int SCI_GETUNDOACTIONPOSITION = 2803;\n\tpublic const int SCI_GETUNDOACTIONTEXT = 2804;\n\tpublic const int INDIC_PLAIN = 0;\n\tpublic const int INDIC_SQUIGGLE = 1;\n\tpublic const int INDIC_TT = 2;\n\tpublic const int INDIC_DIAGONAL = 3;\n\tpublic const int INDIC_STRIKE = 4;\n\tpublic const int INDIC_HIDDEN = 5;\n\tpublic const int INDIC_BOX = 6;\n\tpublic const int INDIC_ROUNDBOX = 7;\n\tpublic const int INDIC_STRAIGHTBOX = 8;\n\tpublic const int INDIC_DASH = 9;\n\tpublic const int INDIC_DOTS = 10;\n\tpublic const int INDIC_SQUIGGLELOW = 11;\n\tpublic const int INDIC_DOTBOX = 12;\n\tpublic const int INDIC_SQUIGGLEPIXMAP = 13;\n\tpublic const int INDIC_COMPOSITIONTHICK = 14;\n\tpublic const int INDIC_COMPOSITIONTHIN = 15;\n\tpublic const int INDIC_FULLBOX = 16;\n\tpublic const int INDIC_TEXTFORE = 17;\n\tpublic const int INDIC_POINT = 18;\n\tpublic const int INDIC_POINTCHARACTER = 19;\n\tpublic const int INDIC_GRADIENT = 20;\n\tpublic const int INDIC_GRADIENTCENTRE = 21;\n\tpublic const int INDIC_POINT_TOP = 22;\n\tpublic const int INDICATOR_CONTAINER = 8;\n\tpublic const int INDICATOR_IME = 32;\n\tpublic const int INDICATOR_IME_MAX = 35;\n\tpublic const int INDICATOR_HISTORY_REVERTED_TO_ORIGIN_INSERTION = 36;\n\tpublic const int INDICATOR_HISTORY_REVERTED_TO_ORIGIN_DELETION = 37;\n\tpublic const int INDICATOR_HISTORY_SAVED_INSERTION = 38;\n\tpublic const int INDICATOR_HISTORY_SAVED_DELETION = 39;\n\tpublic const int INDICATOR_HISTORY_MODIFIED_INSERTION = 40;\n\tpublic const int INDICATOR_HISTORY_MODIFIED_DELETION = 41;\n\tpublic const int INDICATOR_HISTORY_REVERTED_TO_MODIFIED_INSERTION = 42;\n\tpublic const int INDICATOR_HISTORY_REVERTED_TO_MODIFIED_DELETION = 43;\n\tpublic const int INDICATOR_MAX = 43;\n\t//deprecated\n\t//public const int INDIC_CONTAINER = 8;\n\t//public const int INDIC_IME = 32;\n\t//public const int INDIC_IME_MAX = 35;\n\t//public const int INDIC_MAX = 35;\n\t//public const int INDIC0_MASK = 0x20;\n\t//public const int INDIC1_MASK = 0x40;\n\t//public const int INDIC2_MASK = 0x80;\n\t//public const int INDICS_MASK = 0xE0;\n\tpublic const int SCI_INDICSETSTYLE = 2080;\n\tpublic const int SCI_INDICGETSTYLE = 2081;\n\tpublic const int SCI_INDICSETFORE = 2082;\n\tpublic const int SCI_INDICGETFORE = 2083;\n\tpublic const int SCI_INDICSETUNDER = 2510;\n\tpublic const int SCI_INDICGETUNDER = 2511;\n\tpublic const int SCI_INDICSETHOVERSTYLE = 2680;\n\tpublic const int SCI_INDICGETHOVERSTYLE = 2681;\n\tpublic const int SCI_INDICSETHOVERFORE = 2682;\n\tpublic const int SCI_INDICGETHOVERFORE = 2683;\n\tpublic const int SC_INDICVALUEBIT = 0x1000000;\n\tpublic const int SC_INDICVALUEMASK = 0xFFFFFF;\n\tpublic const int SC_INDICFLAG_NONE = 0;\n\tpublic const int SC_INDICFLAG_VALUEFORE = 1;\n\tpublic const int SCI_INDICSETFLAGS = 2684;\n\tpublic const int SCI_INDICGETFLAGS = 2685;\n\tpublic const int SCI_INDICSETSTROKEWIDTH = 2751;\n\tpublic const int SCI_INDICGETSTROKEWIDTH = 2752;\n\tpublic const int SCI_SETWHITESPACEFORE = 2084;\n\tpublic const int SCI_SETWHITESPACEBACK = 2085;\n\tpublic const int SCI_SETWHITESPACESIZE = 2086;\n\tpublic const int SCI_GETWHITESPACESIZE = 2087;\n\t//deprecated\n\t//public const int SCI_SETSTYLEBITS = 2090;\n\t//public const int SCI_GETSTYLEBITS = 2091;\n\tpublic const int SCI_SETLINESTATE = 2092;\n\tpublic const int SCI_GETLINESTATE = 2093;\n\tpublic const int SCI_GETMAXLINESTATE = 2094;\n\tpublic const int SCI_GETCARETLINEVISIBLE = 2095;\n\tpublic const int SCI_SETCARETLINEVISIBLE = 2096;\n\tpublic const int SCI_GETCARETLINEBACK = 2097;\n\tpublic const int SCI_SETCARETLINEBACK = 2098;\n\tpublic const int SCI_GETCARETLINEFRAME = 2704;\n\tpublic const int SCI_SETCARETLINEFRAME = 2705;\n\tpublic const int SCI_STYLESETCHANGEABLE = 2099;\n\tpublic const int SCI_AUTOCSHOW = 2100;\n\tpublic const int SCI_AUTOCCANCEL = 2101;\n\tpublic const int SCI_AUTOCACTIVE = 2102;\n\tpublic const int SCI_AUTOCPOSSTART = 2103;\n\tpublic const int SCI_AUTOCCOMPLETE = 2104;\n\tpublic const int SCI_AUTOCSTOPS = 2105;\n\tpublic const int SCI_AUTOCSETSEPARATOR = 2106;\n\tpublic const int SCI_AUTOCGETSEPARATOR = 2107;\n\tpublic const int SCI_AUTOCSELECT = 2108;\n\tpublic const int SCI_AUTOCSETCANCELATSTART = 2110;\n\tpublic const int SCI_AUTOCGETCANCELATSTART = 2111;\n\tpublic const int SCI_AUTOCSETFILLUPS = 2112;\n\tpublic const int SCI_AUTOCSETCHOOSESINGLE = 2113;\n\tpublic const int SCI_AUTOCGETCHOOSESINGLE = 2114;\n\tpublic const int SCI_AUTOCSETIGNORECASE = 2115;\n\tpublic const int SCI_AUTOCGETIGNORECASE = 2116;\n\tpublic const int SCI_USERLISTSHOW = 2117;\n\tpublic const int SCI_AUTOCSETAUTOHIDE = 2118;\n\tpublic const int SCI_AUTOCGETAUTOHIDE = 2119;\n\tpublic const int SC_AUTOCOMPLETE_NORMAL = 0;\n\tpublic const int SC_AUTOCOMPLETE_FIXED_SIZE = 1;\n\tpublic const int SC_AUTOCOMPLETE_SELECT_FIRST_ITEM = 2;\n\tpublic const int SCI_AUTOCSETOPTIONS = 2638;\n\tpublic const int SCI_AUTOCGETOPTIONS = 2639;\n\tpublic const int SCI_AUTOCSETDROPRESTOFWORD = 2270;\n\tpublic const int SCI_AUTOCGETDROPRESTOFWORD = 2271;\n\tpublic const int SCI_REGISTERIMAGE = 2405;\n\tpublic const int SCI_CLEARREGISTEREDIMAGES = 2408;\n\tpublic const int SCI_AUTOCGETTYPESEPARATOR = 2285;\n\tpublic const int SCI_AUTOCSETTYPESEPARATOR = 2286;\n\tpublic const int SCI_AUTOCSETMAXWIDTH = 2208;\n\tpublic const int SCI_AUTOCGETMAXWIDTH = 2209;\n\tpublic const int SCI_AUTOCSETMAXHEIGHT = 2210;\n\tpublic const int SCI_AUTOCGETMAXHEIGHT = 2211;\n\tpublic const int SCI_AUTOCSETSTYLE = 2109;\n\tpublic const int SCI_AUTOCGETSTYLE = 2120;\n\tpublic const int SCI_AUTOCSETIMAGESCALE = 2815;\n\tpublic const int SCI_AUTOCGETIMAGESCALE = 2816;\n\tpublic const int SCI_SETINDENT = 2122;\n\tpublic const int SCI_GETINDENT = 2123;\n\tpublic const int SCI_SETUSETABS = 2124;\n\tpublic const int SCI_GETUSETABS = 2125;\n\tpublic const int SCI_SETLINEINDENTATION = 2126;\n\tpublic const int SCI_GETLINEINDENTATION = 2127;\n\tpublic const int SCI_GETLINEINDENTPOSITION = 2128;\n\tpublic const int SCI_GETCOLUMN = 2129;\n\tpublic const int SCI_COUNTCHARACTERS = 2633;\n\tpublic const int SCI_COUNTCODEUNITS = 2715;\n\tpublic const int SCI_SETHSCROLLBAR = 2130;\n\tpublic const int SCI_GETHSCROLLBAR = 2131;\n\tpublic const int SC_IV_NONE = 0;\n\tpublic const int SC_IV_REAL = 1;\n\tpublic const int SC_IV_LOOKFORWARD = 2;\n\tpublic const int SC_IV_LOOKBOTH = 3;\n\tpublic const int SCI_SETINDENTATIONGUIDES = 2132;\n\tpublic const int SCI_GETINDENTATIONGUIDES = 2133;\n\tpublic const int SCI_SETHIGHLIGHTGUIDE = 2134;\n\tpublic const int SCI_GETHIGHLIGHTGUIDE = 2135;\n\tpublic const int SCI_GETLINEENDPOSITION = 2136;\n\tpublic const int SCI_GETCODEPAGE = 2137;\n\tpublic const int SCI_GETCARETFORE = 2138;\n\tpublic const int SCI_GETREADONLY = 2140;\n\tpublic const int SCI_SETCURRENTPOS = 2141;\n\tpublic const int SCI_SETSELECTIONSTART = 2142;\n\tpublic const int SCI_GETSELECTIONSTART = 2143;\n\tpublic const int SCI_SETSELECTIONEND = 2144;\n\tpublic const int SCI_GETSELECTIONEND = 2145;\n\tpublic const int SCI_SETEMPTYSELECTION = 2556;\n\tpublic const int SCI_SETPRINTMAGNIFICATION = 2146;\n\tpublic const int SCI_GETPRINTMAGNIFICATION = 2147;\n\tpublic const int SC_PRINT_NORMAL = 0;\n\tpublic const int SC_PRINT_INVERTLIGHT = 1;\n\tpublic const int SC_PRINT_BLACKONWHITE = 2;\n\tpublic const int SC_PRINT_COLOURONWHITE = 3;\n\tpublic const int SC_PRINT_COLOURONWHITEDEFAULTBG = 4;\n\tpublic const int SC_PRINT_SCREENCOLOURS = 5;\n\tpublic const int SCI_SETPRINTCOLOURMODE = 2148;\n\tpublic const int SCI_GETPRINTCOLOURMODE = 2149;\n\tpublic const int SCFIND_WHOLEWORD = 0x2;\n\tpublic const int SCFIND_MATCHCASE = 0x4;\n\tpublic const int SCFIND_WORDSTART = 0x00100000;\n\tpublic const int SCFIND_REGEXP = 0x00200000;\n\tpublic const int SCFIND_POSIX = 0x00400000;\n\tpublic const int SCFIND_CXX11REGEX = 0x00800000;\n\tpublic const int SCI_FINDTEXT = 2150;\n\tpublic const int SCI_FINDTEXTFULL = 2196;\n\tpublic const int SCI_FORMATRANGE = 2151;\n\tpublic const int SCI_FORMATRANGEFULL = 2777;\n\tpublic const int SC_CHANGE_HISTORY_DISABLED = 0;\n\tpublic const int SC_CHANGE_HISTORY_ENABLED = 1;\n\tpublic const int SC_CHANGE_HISTORY_MARKERS = 2;\n\tpublic const int SC_CHANGE_HISTORY_INDICATORS = 4;\n\tpublic const int SCI_SETCHANGEHISTORY = 2780;\n\tpublic const int SCI_GETCHANGEHISTORY = 2781;\n\tpublic const int SC_UNDO_SELECTION_HISTORY_DISABLED = 0;\n\tpublic const int SC_UNDO_SELECTION_HISTORY_ENABLED = 1;\n\tpublic const int SC_UNDO_SELECTION_HISTORY_SCROLL = 2;\n\tpublic const int SCI_SETUNDOSELECTIONHISTORY = 2782;\n\tpublic const int SCI_GETUNDOSELECTIONHISTORY = 2783;\n\tpublic const int SCI_SETSELECTIONSERIALIZED = 2784;\n\tpublic const int SCI_GETSELECTIONSERIALIZED = 2785;\n\tpublic const int SCI_GETFIRSTVISIBLELINE = 2152;\n\tpublic const int SCI_GETLINE = 2153;\n\tpublic const int SCI_GETLINECOUNT = 2154;\n\tpublic const int SCI_ALLOCATELINES = 2089;\n\tpublic const int SCI_SETMARGINLEFT = 2155;\n\tpublic const int SCI_GETMARGINLEFT = 2156;\n\tpublic const int SCI_SETMARGINRIGHT = 2157;\n\tpublic const int SCI_GETMARGINRIGHT = 2158;\n\tpublic const int SCI_GETMODIFY = 2159;\n\tpublic const int SCI_SETSEL = 2160;\n\tpublic const int SCI_GETSELTEXT = 2161;\n\tpublic const int SCI_GETTEXTRANGE = 2162;\n\tpublic const int SCI_GETTEXTRANGEFULL = 2039;\n\tpublic const int SCI_HIDESELECTION = 2163;\n\tpublic const int SCI_GETSELECTIONHIDDEN = 2088;\n\tpublic const int SCI_POINTXFROMPOSITION = 2164;\n\tpublic const int SCI_POINTYFROMPOSITION = 2165;\n\tpublic const int SCI_LINEFROMPOSITION = 2166;\n\tpublic const int SCI_POSITIONFROMLINE = 2167;\n\tpublic const int SCI_LINESCROLL = 2168;\n\tpublic const int SCI_SCROLLVERTICAL = 2817;\n\tpublic const int SCI_SCROLLCARET = 2169;\n\tpublic const int SCI_SCROLLRANGE = 2569;\n\tpublic const int SCI_REPLACESEL = 2170;\n\tpublic const int SCI_SETREADONLY = 2171;\n\tpublic const int SCI_NULL = 2172;\n\tpublic const int SCI_CANPASTE = 2173;\n\tpublic const int SCI_CANUNDO = 2174;\n\tpublic const int SCI_EMPTYUNDOBUFFER = 2175;\n\tpublic const int SCI_UNDO = 2176;\n\tpublic const int SCI_CUT = 2177;\n\tpublic const int SCI_COPY = 2178;\n\tpublic const int SCI_PASTE = 2179;\n\tpublic const int SCI_CLEAR = 2180;\n\tpublic const int SCI_SETTEXT = 2181;\n\tpublic const int SCI_GETTEXT = 2182;\n\tpublic const int SCI_GETTEXTLENGTH = 2183;\n\tpublic const int SCI_GETDIRECTFUNCTION = 2184;\n\tpublic const int SCI_GETDIRECTSTATUSFUNCTION = 2772;\n\tpublic const int SCI_GETDIRECTPOINTER = 2185;\n\tpublic const int SCI_SETOVERTYPE = 2186;\n\tpublic const int SCI_GETOVERTYPE = 2187;\n\tpublic const int SCI_SETCARETWIDTH = 2188;\n\tpublic const int SCI_GETCARETWIDTH = 2189;\n\tpublic const int SCI_SETTARGETSTART = 2190;\n\tpublic const int SCI_GETTARGETSTART = 2191;\n\tpublic const int SCI_SETTARGETEND = 2192;\n\tpublic const int SCI_GETTARGETEND = 2193;\n\tpublic const int SCI_SETTARGETRANGE = 2686;\n\tpublic const int SCI_GETTARGETTEXT = 2687;\n\tpublic const int SCI_TARGETFROMSELECTION = 2287;\n\tpublic const int SCI_TARGETWHOLEDOCUMENT = 2690;\n\tpublic const int SCI_REPLACETARGET = 2194;\n\tpublic const int SCI_REPLACETARGETRE = 2195;\n\tpublic const int SCI_REPLACETARGETMINIMAL = 2779;\n\tpublic const int SCI_SEARCHINTARGET = 2197;\n\tpublic const int SCI_SETSEARCHFLAGS = 2198;\n\tpublic const int SCI_GETSEARCHFLAGS = 2199;\n\tpublic const int SCI_CALLTIPSHOW = 2200;\n\tpublic const int SCI_CALLTIPCANCEL = 2201;\n\tpublic const int SCI_CALLTIPACTIVE = 2202;\n\tpublic const int SCI_CALLTIPPOSSTART = 2203;\n\tpublic const int SCI_CALLTIPSETPOSSTART = 2214;\n\tpublic const int SCI_CALLTIPSETHLT = 2204;\n\tpublic const int SCI_CALLTIPSETBACK = 2205;\n\tpublic const int SCI_CALLTIPSETFORE = 2206;\n\tpublic const int SCI_CALLTIPSETFOREHLT = 2207;\n\tpublic const int SCI_CALLTIPUSESTYLE = 2212;\n\tpublic const int SCI_CALLTIPSETPOSITION = 2213;\n\tpublic const int SCI_VISIBLEFROMDOCLINE = 2220;\n\tpublic const int SCI_DOCLINEFROMVISIBLE = 2221;\n\tpublic const int SCI_WRAPCOUNT = 2235;\n\tpublic const int SC_FOLDLEVELNONE = 0x0;\n\tpublic const int SC_FOLDLEVELBASE = 0x400;\n\tpublic const int SC_FOLDLEVELWHITEFLAG = 0x1000;\n\tpublic const int SC_FOLDLEVELHEADERFLAG = 0x2000;\n\tpublic const int SC_FOLDLEVELNUMBERMASK = 0x0FFF;\n\tpublic const int SCI_SETFOLDLEVEL = 2222;\n\tpublic const int SCI_GETFOLDLEVEL = 2223;\n\tpublic const int SCI_GETLASTCHILD = 2224;\n\tpublic const int SCI_GETFOLDPARENT = 2225;\n\tpublic const int SCI_SHOWLINES = 2226;\n\tpublic const int SCI_HIDELINES = 2227;\n\tpublic const int SCI_GETLINEVISIBLE = 2228;\n\tpublic const int SCI_GETALLLINESVISIBLE = 2236;\n\tpublic const int SCI_SETFOLDEXPANDED = 2229;\n\tpublic const int SCI_GETFOLDEXPANDED = 2230;\n\tpublic const int SCI_TOGGLEFOLD = 2231;\n\tpublic const int SCI_TOGGLEFOLDSHOWTEXT = 2700;\n\tpublic const int SC_FOLDDISPLAYTEXT_HIDDEN = 0;\n\tpublic const int SC_FOLDDISPLAYTEXT_STANDARD = 1;\n\tpublic const int SC_FOLDDISPLAYTEXT_BOXED = 2;\n\tpublic const int SCI_FOLDDISPLAYTEXTSETSTYLE = 2701;\n\tpublic const int SCI_FOLDDISPLAYTEXTGETSTYLE = 2707;\n\tpublic const int SCI_SETDEFAULTFOLDDISPLAYTEXT = 2722;\n\tpublic const int SCI_GETDEFAULTFOLDDISPLAYTEXT = 2723;\n\tpublic const int SC_FOLDACTION_CONTRACT = 0;\n\tpublic const int SC_FOLDACTION_EXPAND = 1;\n\tpublic const int SC_FOLDACTION_TOGGLE = 2;\n\tpublic const int SC_FOLDACTION_CONTRACT_EVERY_LEVEL = 4;\n\tpublic const int SCI_FOLDLINE = 2237;\n\tpublic const int SCI_FOLDCHILDREN = 2238;\n\tpublic const int SCI_EXPANDCHILDREN = 2239;\n\tpublic const int SCI_FOLDALL = 2662;\n\tpublic const int SCI_ENSUREVISIBLE = 2232;\n\tpublic const int SC_AUTOMATICFOLD_NONE = 0x0000;\n\tpublic const int SC_AUTOMATICFOLD_SHOW = 0x0001;\n\tpublic const int SC_AUTOMATICFOLD_CLICK = 0x0002;\n\tpublic const int SC_AUTOMATICFOLD_CHANGE = 0x0004;\n\tpublic const int SCI_SETAUTOMATICFOLD = 2663;\n\tpublic const int SCI_GETAUTOMATICFOLD = 2664;\n\tpublic const int SC_FOLDFLAG_NONE = 0x0000;\n\tpublic const int SC_FOLDFLAG_LINEBEFORE_EXPANDED = 0x0002;\n\tpublic const int SC_FOLDFLAG_LINEBEFORE_CONTRACTED = 0x0004;\n\tpublic const int SC_FOLDFLAG_LINEAFTER_EXPANDED = 0x0008;\n\tpublic const int SC_FOLDFLAG_LINEAFTER_CONTRACTED = 0x0010;\n\tpublic const int SC_FOLDFLAG_LEVELNUMBERS = 0x0040;\n\tpublic const int SC_FOLDFLAG_LINESTATE = 0x0080;\n\tpublic const int SCI_SETFOLDFLAGS = 2233;\n\tpublic const int SCI_ENSUREVISIBLEENFORCEPOLICY = 2234;\n\tpublic const int SCI_SETTABINDENTS = 2260;\n\tpublic const int SCI_GETTABINDENTS = 2261;\n\tpublic const int SCI_SETBACKSPACEUNINDENTS = 2262;\n\tpublic const int SCI_GETBACKSPACEUNINDENTS = 2263;\n\tpublic const int SC_TIME_FOREVER = 10000000;\n\tpublic const int SCI_SETMOUSEDWELLTIME = 2264;\n\tpublic const int SCI_GETMOUSEDWELLTIME = 2265;\n\tpublic const int SCI_WORDSTARTPOSITION = 2266;\n\tpublic const int SCI_WORDENDPOSITION = 2267;\n\tpublic const int SCI_ISRANGEWORD = 2691;\n\tpublic const int SC_IDLESTYLING_NONE = 0;\n\tpublic const int SC_IDLESTYLING_TOVISIBLE = 1;\n\tpublic const int SC_IDLESTYLING_AFTERVISIBLE = 2;\n\tpublic const int SC_IDLESTYLING_ALL = 3;\n\tpublic const int SCI_SETIDLESTYLING = 2692;\n\tpublic const int SCI_GETIDLESTYLING = 2693;\n\tpublic const int SC_WRAP_NONE = 0;\n\tpublic const int SC_WRAP_WORD = 1;\n\tpublic const int SC_WRAP_CHAR = 2;\n\tpublic const int SC_WRAP_WHITESPACE = 3;\n\tpublic const int SCI_SETWRAPMODE = 2268;\n\tpublic const int SCI_GETWRAPMODE = 2269;\n\tpublic const int SC_WRAPVISUALFLAG_NONE = 0x0000;\n\tpublic const int SC_WRAPVISUALFLAG_END = 0x0001;\n\tpublic const int SC_WRAPVISUALFLAG_START = 0x0002;\n\tpublic const int SC_WRAPVISUALFLAG_MARGIN = 0x0004;\n\tpublic const int SCI_SETWRAPVISUALFLAGS = 2460;\n\tpublic const int SCI_GETWRAPVISUALFLAGS = 2461;\n\tpublic const int SC_WRAPVISUALFLAGLOC_DEFAULT = 0x0000;\n\tpublic const int SC_WRAPVISUALFLAGLOC_END_BY_TEXT = 0x0001;\n\tpublic const int SC_WRAPVISUALFLAGLOC_START_BY_TEXT = 0x0002;\n\tpublic const int SCI_SETWRAPVISUALFLAGSLOCATION = 2462;\n\tpublic const int SCI_GETWRAPVISUALFLAGSLOCATION = 2463;\n\tpublic const int SCI_SETWRAPSTARTINDENT = 2464;\n\tpublic const int SCI_GETWRAPSTARTINDENT = 2465;\n\tpublic const int SC_WRAPINDENT_FIXED = 0;\n\tpublic const int SC_WRAPINDENT_SAME = 1;\n\tpublic const int SC_WRAPINDENT_INDENT = 2;\n\tpublic const int SC_WRAPINDENT_DEEPINDENT = 3;\n\tpublic const int SCI_SETWRAPINDENTMODE = 2472;\n\tpublic const int SCI_GETWRAPINDENTMODE = 2473;\n\tpublic const int SC_CACHE_NONE = 0;\n\tpublic const int SC_CACHE_CARET = 1;\n\tpublic const int SC_CACHE_PAGE = 2;\n\tpublic const int SC_CACHE_DOCUMENT = 3;\n\tpublic const int SCI_SETLAYOUTCACHE = 2272;\n\tpublic const int SCI_GETLAYOUTCACHE = 2273;\n\tpublic const int SCI_SETSCROLLWIDTH = 2274;\n\tpublic const int SCI_GETSCROLLWIDTH = 2275;\n\tpublic const int SCI_SETSCROLLWIDTHTRACKING = 2516;\n\tpublic const int SCI_GETSCROLLWIDTHTRACKING = 2517;\n\tpublic const int SCI_TEXTWIDTH = 2276;\n\tpublic const int SCI_SETENDATLASTLINE = 2277;\n\tpublic const int SCI_GETENDATLASTLINE = 2278;\n\tpublic const int SCI_TEXTHEIGHT = 2279;\n\tpublic const int SCI_SETVSCROLLBAR = 2280;\n\tpublic const int SCI_GETVSCROLLBAR = 2281;\n\tpublic const int SCI_APPENDTEXT = 2282;\n\tpublic const int SC_PHASES_ONE = 0;\n\tpublic const int SC_PHASES_TWO = 1;\n\tpublic const int SC_PHASES_MULTIPLE = 2;\n\tpublic const int SCI_GETPHASESDRAW = 2673;\n\tpublic const int SCI_SETPHASESDRAW = 2674;\n\tpublic const int SC_EFF_QUALITY_MASK = 0xF;\n\tpublic const int SC_EFF_QUALITY_DEFAULT = 0;\n\tpublic const int SC_EFF_QUALITY_NON_ANTIALIASED = 1;\n\tpublic const int SC_EFF_QUALITY_ANTIALIASED = 2;\n\tpublic const int SC_EFF_QUALITY_LCD_OPTIMIZED = 3;\n\tpublic const int SCI_SETFONTQUALITY = 2611;\n\tpublic const int SCI_GETFONTQUALITY = 2612;\n\tpublic const int SCI_SETFIRSTVISIBLELINE = 2613;\n\tpublic const int SC_MULTIPASTE_ONCE = 0;\n\tpublic const int SC_MULTIPASTE_EACH = 1;\n\tpublic const int SCI_SETMULTIPASTE = 2614;\n\tpublic const int SCI_GETMULTIPASTE = 2615;\n\tpublic const int SCI_GETTAG = 2616;\n\tpublic const int SCI_LINESJOIN = 2288;\n\tpublic const int SCI_LINESSPLIT = 2289;\n\tpublic const int SCI_SETFOLDMARGINCOLOUR = 2290;\n\tpublic const int SCI_SETFOLDMARGINHICOLOUR = 2291;\n\tpublic const int SC_ACCESSIBILITY_DISABLED = 0;\n\tpublic const int SC_ACCESSIBILITY_ENABLED = 1;\n\tpublic const int SCI_SETACCESSIBILITY = 2702;\n\tpublic const int SCI_GETACCESSIBILITY = 2703;\n\tpublic const int SCI_LINEDOWN = 2300;\n\tpublic const int SCI_LINEDOWNEXTEND = 2301;\n\tpublic const int SCI_LINEUP = 2302;\n\tpublic const int SCI_LINEUPEXTEND = 2303;\n\tpublic const int SCI_CHARLEFT = 2304;\n\tpublic const int SCI_CHARLEFTEXTEND = 2305;\n\tpublic const int SCI_CHARRIGHT = 2306;\n\tpublic const int SCI_CHARRIGHTEXTEND = 2307;\n\tpublic const int SCI_WORDLEFT = 2308;\n\tpublic const int SCI_WORDLEFTEXTEND = 2309;\n\tpublic const int SCI_WORDRIGHT = 2310;\n\tpublic const int SCI_WORDRIGHTEXTEND = 2311;\n\tpublic const int SCI_HOME = 2312;\n\tpublic const int SCI_HOMEEXTEND = 2313;\n\tpublic const int SCI_LINEEND = 2314;\n\tpublic const int SCI_LINEENDEXTEND = 2315;\n\tpublic const int SCI_DOCUMENTSTART = 2316;\n\tpublic const int SCI_DOCUMENTSTARTEXTEND = 2317;\n\tpublic const int SCI_DOCUMENTEND = 2318;\n\tpublic const int SCI_DOCUMENTENDEXTEND = 2319;\n\tpublic const int SCI_PAGEUP = 2320;\n\tpublic const int SCI_PAGEUPEXTEND = 2321;\n\tpublic const int SCI_PAGEDOWN = 2322;\n\tpublic const int SCI_PAGEDOWNEXTEND = 2323;\n\tpublic const int SCI_EDITTOGGLEOVERTYPE = 2324;\n\tpublic const int SCI_CANCEL = 2325;\n\tpublic const int SCI_DELETEBACK = 2326;\n\tpublic const int SCI_TAB = 2327;\n\tpublic const int SCI_LINEINDENT = 2813;\n\tpublic const int SCI_BACKTAB = 2328;\n\tpublic const int SCI_LINEDEDENT = 2814;\n\tpublic const int SCI_NEWLINE = 2329;\n\tpublic const int SCI_FORMFEED = 2330;\n\tpublic const int SCI_VCHOME = 2331;\n\tpublic const int SCI_VCHOMEEXTEND = 2332;\n\tpublic const int SCI_ZOOMIN = 2333;\n\tpublic const int SCI_ZOOMOUT = 2334;\n\tpublic const int SCI_DELWORDLEFT = 2335;\n\tpublic const int SCI_DELWORDRIGHT = 2336;\n\tpublic const int SCI_DELWORDRIGHTEND = 2518;\n\tpublic const int SCI_LINECUT = 2337;\n\tpublic const int SCI_LINEDELETE = 2338;\n\tpublic const int SCI_LINETRANSPOSE = 2339;\n\tpublic const int SCI_LINEREVERSE = 2354;\n\tpublic const int SCI_LINEDUPLICATE = 2404;\n\tpublic const int SCI_LOWERCASE = 2340;\n\tpublic const int SCI_UPPERCASE = 2341;\n\tpublic const int SCI_LINESCROLLDOWN = 2342;\n\tpublic const int SCI_LINESCROLLUP = 2343;\n\tpublic const int SCI_DELETEBACKNOTLINE = 2344;\n\tpublic const int SCI_HOMEDISPLAY = 2345;\n\tpublic const int SCI_HOMEDISPLAYEXTEND = 2346;\n\tpublic const int SCI_LINEENDDISPLAY = 2347;\n\tpublic const int SCI_LINEENDDISPLAYEXTEND = 2348;\n\tpublic const int SCI_HOMEWRAP = 2349;\n\tpublic const int SCI_HOMEWRAPEXTEND = 2450;\n\tpublic const int SCI_LINEENDWRAP = 2451;\n\tpublic const int SCI_LINEENDWRAPEXTEND = 2452;\n\tpublic const int SCI_VCHOMEWRAP = 2453;\n\tpublic const int SCI_VCHOMEWRAPEXTEND = 2454;\n\tpublic const int SCI_LINECOPY = 2455;\n\tpublic const int SCI_MOVECARETINSIDEVIEW = 2401;\n\tpublic const int SCI_LINELENGTH = 2350;\n\tpublic const int SCI_BRACEHIGHLIGHT = 2351;\n\tpublic const int SCI_BRACEHIGHLIGHTINDICATOR = 2498;\n\tpublic const int SCI_BRACEBADLIGHT = 2352;\n\tpublic const int SCI_BRACEBADLIGHTINDICATOR = 2499;\n\tpublic const int SCI_BRACEMATCH = 2353;\n\tpublic const int SCI_BRACEMATCHNEXT = 2369;\n\tpublic const int SCI_GETVIEWEOL = 2355;\n\tpublic const int SCI_SETVIEWEOL = 2356;\n\tpublic const int SCI_GETDOCPOINTER = 2357;\n\tpublic const int SCI_SETDOCPOINTER = 2358;\n\tpublic const int SCI_SETMODEVENTMASK = 2359;\n\tpublic const int EDGE_NONE = 0;\n\tpublic const int EDGE_LINE = 1;\n\tpublic const int EDGE_BACKGROUND = 2;\n\tpublic const int EDGE_MULTILINE = 3;\n\tpublic const int SCI_GETEDGECOLUMN = 2360;\n\tpublic const int SCI_SETEDGECOLUMN = 2361;\n\tpublic const int SCI_GETEDGEMODE = 2362;\n\tpublic const int SCI_SETEDGEMODE = 2363;\n\tpublic const int SCI_GETEDGECOLOUR = 2364;\n\tpublic const int SCI_SETEDGECOLOUR = 2365;\n\tpublic const int SCI_MULTIEDGEADDLINE = 2694;\n\tpublic const int SCI_MULTIEDGECLEARALL = 2695;\n\tpublic const int SCI_GETMULTIEDGECOLUMN = 2749;\n\tpublic const int SCI_SEARCHANCHOR = 2366;\n\tpublic const int SCI_SEARCHNEXT = 2367;\n\tpublic const int SCI_SEARCHPREV = 2368;\n\tpublic const int SCI_LINESONSCREEN = 2370;\n\tpublic const int SC_POPUP_NEVER = 0;\n\tpublic const int SC_POPUP_ALL = 1;\n\tpublic const int SC_POPUP_TEXT = 2;\n\tpublic const int SCI_USEPOPUP = 2371;\n\tpublic const int SCI_SELECTIONISRECTANGLE = 2372;\n\tpublic const int SCI_SETZOOM = 2373;\n\tpublic const int SCI_GETZOOM = 2374;\n\tpublic const int SC_DOCUMENTOPTION_DEFAULT = 0;\n\tpublic const int SC_DOCUMENTOPTION_STYLES_NONE = 0x1;\n\tpublic const int SC_DOCUMENTOPTION_TEXT_LARGE = 0x100;\n\tpublic const int SCI_CREATEDOCUMENT = 2375;\n\tpublic const int SCI_ADDREFDOCUMENT = 2376;\n\tpublic const int SCI_RELEASEDOCUMENT = 2377;\n\tpublic const int SCI_GETDOCUMENTOPTIONS = 2379;\n\tpublic const int SCI_GETMODEVENTMASK = 2378;\n\tpublic const int SCI_SETCOMMANDEVENTS = 2717;\n\tpublic const int SCI_GETCOMMANDEVENTS = 2718;\n\tpublic const int SCI_SETFOCUS = 2380;\n\tpublic const int SCI_GETFOCUS = 2381;\n\tpublic const int SC_STATUS_OK = 0;\n\tpublic const int SC_STATUS_FAILURE = 1;\n\tpublic const int SC_STATUS_BADALLOC = 2;\n\tpublic const int SC_STATUS_WARN_START = 1000;\n\tpublic const int SC_STATUS_WARN_REGEX = 1001;\n\tpublic const int SCI_SETSTATUS = 2382;\n\tpublic const int SCI_GETSTATUS = 2383;\n\tpublic const int SCI_SETMOUSEDOWNCAPTURES = 2384;\n\tpublic const int SCI_GETMOUSEDOWNCAPTURES = 2385;\n\tpublic const int SCI_SETMOUSEWHEELCAPTURES = 2696;\n\tpublic const int SCI_GETMOUSEWHEELCAPTURES = 2697;\n\tpublic const int SC_CURSORNORMAL = -1;\n\tpublic const int SC_CURSORARROW = 2;\n\tpublic const int SC_CURSORWAIT = 4;\n\tpublic const int SC_CURSORREVERSEARROW = 7;\n\tpublic const int SCI_SETCURSOR = 2386;\n\tpublic const int SCI_GETCURSOR = 2387;\n\tpublic const int SCI_SETCONTROLCHARSYMBOL = 2388;\n\tpublic const int SCI_GETCONTROLCHARSYMBOL = 2389;\n\tpublic const int SCI_WORDPARTLEFT = 2390;\n\tpublic const int SCI_WORDPARTLEFTEXTEND = 2391;\n\tpublic const int SCI_WORDPARTRIGHT = 2392;\n\tpublic const int SCI_WORDPARTRIGHTEXTEND = 2393;\n\tpublic const int VISIBLE_SLOP = 0x01;\n\tpublic const int VISIBLE_STRICT = 0x04;\n\tpublic const int SCI_SETVISIBLEPOLICY = 2394;\n\tpublic const int SCI_DELLINELEFT = 2395;\n\tpublic const int SCI_DELLINERIGHT = 2396;\n\tpublic const int SCI_SETXOFFSET = 2397;\n\tpublic const int SCI_GETXOFFSET = 2398;\n\tpublic const int SCI_CHOOSECARETX = 2399;\n\tpublic const int SCI_GRABFOCUS = 2400;\n\tpublic const int CARET_SLOP = 0x01;\n\tpublic const int CARET_STRICT = 0x04;\n\tpublic const int CARET_JUMPS = 0x10;\n\tpublic const int CARET_EVEN = 0x08;\n\tpublic const int SCI_SETXCARETPOLICY = 2402;\n\tpublic const int SCI_SETYCARETPOLICY = 2403;\n\tpublic const int SCI_SETPRINTWRAPMODE = 2406;\n\tpublic const int SCI_GETPRINTWRAPMODE = 2407;\n\tpublic const int SCI_SETHOTSPOTACTIVEFORE = 2410;\n\tpublic const int SCI_GETHOTSPOTACTIVEFORE = 2494;\n\tpublic const int SCI_SETHOTSPOTACTIVEBACK = 2411;\n\tpublic const int SCI_GETHOTSPOTACTIVEBACK = 2495;\n\tpublic const int SCI_SETHOTSPOTACTIVEUNDERLINE = 2412;\n\tpublic const int SCI_GETHOTSPOTACTIVEUNDERLINE = 2496;\n\tpublic const int SCI_SETHOTSPOTSINGLELINE = 2421;\n\tpublic const int SCI_GETHOTSPOTSINGLELINE = 2497;\n\tpublic const int SCI_PARADOWN = 2413;\n\tpublic const int SCI_PARADOWNEXTEND = 2414;\n\tpublic const int SCI_PARAUP = 2415;\n\tpublic const int SCI_PARAUPEXTEND = 2416;\n\tpublic const int SCI_POSITIONBEFORE = 2417;\n\tpublic const int SCI_POSITIONAFTER = 2418;\n\tpublic const int SCI_POSITIONRELATIVE = 2670;\n\tpublic const int SCI_POSITIONRELATIVECODEUNITS = 2716;\n\tpublic const int SCI_COPYRANGE = 2419;\n\tpublic const int SCI_COPYTEXT = 2420;\n\tpublic const int SC_SEL_STREAM = 0;\n\tpublic const int SC_SEL_RECTANGLE = 1;\n\tpublic const int SC_SEL_LINES = 2;\n\tpublic const int SC_SEL_THIN = 3;\n\tpublic const int SCI_SETSELECTIONMODE = 2422;\n\tpublic const int SCI_CHANGESELECTIONMODE = 2659;\n\tpublic const int SCI_GETSELECTIONMODE = 2423;\n\tpublic const int SCI_SETMOVEEXTENDSSELECTION = 2719;\n\tpublic const int SCI_GETMOVEEXTENDSSELECTION = 2706;\n\tpublic const int SCI_GETLINESELSTARTPOSITION = 2424;\n\tpublic const int SCI_GETLINESELENDPOSITION = 2425;\n\tpublic const int SCI_LINEDOWNRECTEXTEND = 2426;\n\tpublic const int SCI_LINEUPRECTEXTEND = 2427;\n\tpublic const int SCI_CHARLEFTRECTEXTEND = 2428;\n\tpublic const int SCI_CHARRIGHTRECTEXTEND = 2429;\n\tpublic const int SCI_HOMERECTEXTEND = 2430;\n\tpublic const int SCI_VCHOMERECTEXTEND = 2431;\n\tpublic const int SCI_LINEENDRECTEXTEND = 2432;\n\tpublic const int SCI_PAGEUPRECTEXTEND = 2433;\n\tpublic const int SCI_PAGEDOWNRECTEXTEND = 2434;\n\tpublic const int SCI_STUTTEREDPAGEUP = 2435;\n\tpublic const int SCI_STUTTEREDPAGEUPEXTEND = 2436;\n\tpublic const int SCI_STUTTEREDPAGEDOWN = 2437;\n\tpublic const int SCI_STUTTEREDPAGEDOWNEXTEND = 2438;\n\tpublic const int SCI_WORDLEFTEND = 2439;\n\tpublic const int SCI_WORDLEFTENDEXTEND = 2440;\n\tpublic const int SCI_WORDRIGHTEND = 2441;\n\tpublic const int SCI_WORDRIGHTENDEXTEND = 2442;\n\tpublic const int SCI_SETWHITESPACECHARS = 2443;\n\tpublic const int SCI_GETWHITESPACECHARS = 2647;\n\tpublic const int SCI_SETPUNCTUATIONCHARS = 2648;\n\tpublic const int SCI_GETPUNCTUATIONCHARS = 2649;\n\tpublic const int SCI_SETCHARSDEFAULT = 2444;\n\tpublic const int SCI_AUTOCGETCURRENT = 2445;\n\tpublic const int SCI_AUTOCGETCURRENTTEXT = 2610;\n\tpublic const int SC_CASEINSENSITIVEBEHAVIOUR_RESPECTCASE = 0;\n\tpublic const int SC_CASEINSENSITIVEBEHAVIOUR_IGNORECASE = 1;\n\tpublic const int SCI_AUTOCSETCASEINSENSITIVEBEHAVIOUR = 2634;\n\tpublic const int SCI_AUTOCGETCASEINSENSITIVEBEHAVIOUR = 2635;\n\tpublic const int SC_MULTIAUTOC_ONCE = 0;\n\tpublic const int SC_MULTIAUTOC_EACH = 1;\n\tpublic const int SCI_AUTOCSETMULTI = 2636;\n\tpublic const int SCI_AUTOCGETMULTI = 2637;\n\tpublic const int SC_ORDER_PRESORTED = 0;\n\tpublic const int SC_ORDER_PERFORMSORT = 1;\n\tpublic const int SC_ORDER_CUSTOM = 2;\n\tpublic const int SCI_AUTOCSETORDER = 2660;\n\tpublic const int SCI_AUTOCGETORDER = 2661;\n\tpublic const int SCI_ALLOCATE = 2446;\n\tpublic const int SCI_TARGETASUTF8 = 2447;\n\tpublic const int SCI_SETLENGTHFORENCODE = 2448;\n\tpublic const int SCI_ENCODEDFROMUTF8 = 2449;\n\tpublic const int SCI_FINDCOLUMN = 2456;\n\tpublic const int SCI_GETCARETSTICKY = 2457;\n\tpublic const int SCI_SETCARETSTICKY = 2458;\n\tpublic const int SC_CARETSTICKY_OFF = 0;\n\tpublic const int SC_CARETSTICKY_ON = 1;\n\tpublic const int SC_CARETSTICKY_WHITESPACE = 2;\n\tpublic const int SCI_TOGGLECARETSTICKY = 2459;\n\tpublic const int SCI_SETPASTECONVERTENDINGS = 2467;\n\tpublic const int SCI_GETPASTECONVERTENDINGS = 2468;\n\tpublic const int SCI_REPLACERECTANGULAR = 2771;\n\tpublic const int SCI_SELECTIONDUPLICATE = 2469;\n\tpublic const int SC_ALPHA_TRANSPARENT = 0;\n\tpublic const int SC_ALPHA_OPAQUE = 255;\n\tpublic const int SC_ALPHA_NOALPHA = 256;\n\tpublic const int SCI_SETCARETLINEBACKALPHA = 2470;\n\tpublic const int SCI_GETCARETLINEBACKALPHA = 2471;\n\tpublic const int CARETSTYLE_INVISIBLE = 0;\n\tpublic const int CARETSTYLE_LINE = 1;\n\tpublic const int CARETSTYLE_BLOCK = 2;\n\tpublic const int CARETSTYLE_OVERSTRIKE_BAR = 0;\n\tpublic const int CARETSTYLE_OVERSTRIKE_BLOCK = 16;\n\tpublic const int CARETSTYLE_CURSES = 0x20;\n\tpublic const int CARETSTYLE_INS_MASK = 0xF;\n\tpublic const int SCI_SETCARETSTYLE = 2512;\n\tpublic const int SCI_GETCARETSTYLE = 2513;\n\tpublic const int SCI_SETINDICATORCURRENT = 2500;\n\tpublic const int SCI_GETINDICATORCURRENT = 2501;\n\tpublic const int SCI_SETINDICATORVALUE = 2502;\n\tpublic const int SCI_GETINDICATORVALUE = 2503;\n\tpublic const int SCI_INDICATORFILLRANGE = 2504;\n\tpublic const int SCI_INDICATORCLEARRANGE = 2505;\n\tpublic const int SCI_INDICATORALLONFOR = 2506;\n\tpublic const int SCI_INDICATORVALUEAT = 2507;\n\tpublic const int SCI_INDICATORSTART = 2508;\n\tpublic const int SCI_INDICATOREND = 2509;\n\tpublic const int SCI_SETPOSITIONCACHE = 2514;\n\tpublic const int SCI_GETPOSITIONCACHE = 2515;\n\tpublic const int SCI_SETLAYOUTTHREADS = 2775;\n\tpublic const int SCI_GETLAYOUTTHREADS = 2776;\n\tpublic const int SCI_COPYALLOWLINE = 2519;\n\tpublic const int SCI_CUTALLOWLINE = 2810;\n\tpublic const int SCI_SETCOPYSEPARATOR = 2811;\n\tpublic const int SCI_GETCOPYSEPARATOR = 2812;\n\tpublic const int SCI_GETCHARACTERPOINTER = 2520;\n\tpublic const int SCI_GETRANGEPOINTER = 2643;\n\tpublic const int SCI_GETGAPPOSITION = 2644;\n\tpublic const int SCI_INDICSETALPHA = 2523;\n\tpublic const int SCI_INDICGETALPHA = 2524;\n\tpublic const int SCI_INDICSETOUTLINEALPHA = 2558;\n\tpublic const int SCI_INDICGETOUTLINEALPHA = 2559;\n\tpublic const int SCI_SETEXTRAASCENT = 2525;\n\tpublic const int SCI_GETEXTRAASCENT = 2526;\n\tpublic const int SCI_SETEXTRADESCENT = 2527;\n\tpublic const int SCI_GETEXTRADESCENT = 2528;\n\tpublic const int SCI_MARKERSYMBOLDEFINED = 2529;\n\tpublic const int SCI_MARGINSETTEXT = 2530;\n\tpublic const int SCI_MARGINGETTEXT = 2531;\n\tpublic const int SCI_MARGINSETSTYLE = 2532;\n\tpublic const int SCI_MARGINGETSTYLE = 2533;\n\tpublic const int SCI_MARGINSETSTYLES = 2534;\n\tpublic const int SCI_MARGINGETSTYLES = 2535;\n\tpublic const int SCI_MARGINTEXTCLEARALL = 2536;\n\tpublic const int SCI_MARGINSETSTYLEOFFSET = 2537;\n\tpublic const int SCI_MARGINGETSTYLEOFFSET = 2538;\n\tpublic const int SC_MARGINOPTION_NONE = 0;\n\tpublic const int SC_MARGINOPTION_SUBLINESELECT = 1;\n\tpublic const int SCI_SETMARGINOPTIONS = 2539;\n\tpublic const int SCI_GETMARGINOPTIONS = 2557;\n\tpublic const int SCI_ANNOTATIONSETTEXT = 2540;\n\tpublic const int SCI_ANNOTATIONGETTEXT = 2541;\n\tpublic const int SCI_ANNOTATIONSETSTYLE = 2542;\n\tpublic const int SCI_ANNOTATIONGETSTYLE = 2543;\n\tpublic const int SCI_ANNOTATIONSETSTYLES = 2544;\n\tpublic const int SCI_ANNOTATIONGETSTYLES = 2545;\n\tpublic const int SCI_ANNOTATIONGETLINES = 2546;\n\tpublic const int SCI_ANNOTATIONCLEARALL = 2547;\n\tpublic enum AnnotationsVisible {\n\t\tANNOTATION_HIDDEN = 0,\n\t\tANNOTATION_STANDARD = 1,\n\t\tANNOTATION_BOXED = 2,\n\t\tANNOTATION_INDENTED = 3,\n\t}\n\tpublic const int SCI_ANNOTATIONSETVISIBLE = 2548;\n\tpublic const int SCI_ANNOTATIONGETVISIBLE = 2549;\n\tpublic const int SCI_ANNOTATIONSETSTYLEOFFSET = 2550;\n\tpublic const int SCI_ANNOTATIONGETSTYLEOFFSET = 2551;\n\tpublic const int SCI_RELEASEALLEXTENDEDSTYLES = 2552;\n\tpublic const int SCI_ALLOCATEEXTENDEDSTYLES = 2553;\n\tpublic const int UNDO_MAY_COALESCE = 1;\n\tpublic const int SCI_ADDUNDOACTION = 2560;\n\tpublic const int SCI_CHARPOSITIONFROMPOINT = 2561;\n\tpublic const int SCI_CHARPOSITIONFROMPOINTCLOSE = 2562;\n\tpublic const int SCI_SETMOUSESELECTIONRECTANGULARSWITCH = 2668;\n\tpublic const int SCI_GETMOUSESELECTIONRECTANGULARSWITCH = 2669;\n\tpublic const int SCI_SETMULTIPLESELECTION = 2563;\n\tpublic const int SCI_GETMULTIPLESELECTION = 2564;\n\tpublic const int SCI_SETADDITIONALSELECTIONTYPING = 2565;\n\tpublic const int SCI_GETADDITIONALSELECTIONTYPING = 2566;\n\tpublic const int SCI_SETADDITIONALCARETSBLINK = 2567;\n\tpublic const int SCI_GETADDITIONALCARETSBLINK = 2568;\n\tpublic const int SCI_SETADDITIONALCARETSVISIBLE = 2608;\n\tpublic const int SCI_GETADDITIONALCARETSVISIBLE = 2609;\n\tpublic const int SCI_GETSELECTIONS = 2570;\n\tpublic const int SCI_GETSELECTIONEMPTY = 2650;\n\tpublic const int SCI_CLEARSELECTIONS = 2571;\n\tpublic const int SCI_SETSELECTION = 2572;\n\tpublic const int SCI_ADDSELECTION = 2573;\n\tpublic const int SCI_SELECTIONFROMPOINT = 2474;\n\tpublic const int SCI_DROPSELECTIONN = 2671;\n\tpublic const int SCI_SETMAINSELECTION = 2574;\n\tpublic const int SCI_GETMAINSELECTION = 2575;\n\tpublic const int SCI_SETSELECTIONNCARET = 2576;\n\tpublic const int SCI_GETSELECTIONNCARET = 2577;\n\tpublic const int SCI_SETSELECTIONNANCHOR = 2578;\n\tpublic const int SCI_GETSELECTIONNANCHOR = 2579;\n\tpublic const int SCI_SETSELECTIONNCARETVIRTUALSPACE = 2580;\n\tpublic const int SCI_GETSELECTIONNCARETVIRTUALSPACE = 2581;\n\tpublic const int SCI_SETSELECTIONNANCHORVIRTUALSPACE = 2582;\n\tpublic const int SCI_GETSELECTIONNANCHORVIRTUALSPACE = 2583;\n\tpublic const int SCI_SETSELECTIONNSTART = 2584;\n\tpublic const int SCI_GETSELECTIONNSTART = 2585;\n\tpublic const int SCI_SETSELECTIONNEND = 2586;\n\tpublic const int SCI_GETSELECTIONNEND = 2587;\n\tpublic const int SCI_SETRECTANGULARSELECTIONCARET = 2588;\n\tpublic const int SCI_GETRECTANGULARSELECTIONCARET = 2589;\n\tpublic const int SCI_SETRECTANGULARSELECTIONANCHOR = 2590;\n\tpublic const int SCI_GETRECTANGULARSELECTIONANCHOR = 2591;\n\tpublic const int SCI_SETRECTANGULARSELECTIONCARETVIRTUALSPACE = 2592;\n\tpublic const int SCI_GETRECTANGULARSELECTIONCARETVIRTUALSPACE = 2593;\n\tpublic const int SCI_SETRECTANGULARSELECTIONANCHORVIRTUALSPACE = 2594;\n\tpublic const int SCI_GETRECTANGULARSELECTIONANCHORVIRTUALSPACE = 2595;\n\tpublic const int SCVS_NONE = 0;\n\tpublic const int SCVS_RECTANGULARSELECTION = 1;\n\tpublic const int SCVS_USERACCESSIBLE = 2;\n\tpublic const int SCVS_NOWRAPLINESTART = 4;\n\tpublic const int SCI_SETVIRTUALSPACEOPTIONS = 2596;\n\tpublic const int SCI_GETVIRTUALSPACEOPTIONS = 2597;\n\tpublic const int SCI_SETRECTANGULARSELECTIONMODIFIER = 2598;\n\tpublic const int SCI_GETRECTANGULARSELECTIONMODIFIER = 2599;\n\tpublic const int SCI_SETADDITIONALSELFORE = 2600;\n\tpublic const int SCI_SETADDITIONALSELBACK = 2601;\n\tpublic const int SCI_SETADDITIONALSELALPHA = 2602;\n\tpublic const int SCI_GETADDITIONALSELALPHA = 2603;\n\tpublic const int SCI_SETADDITIONALCARETFORE = 2604;\n\tpublic const int SCI_GETADDITIONALCARETFORE = 2605;\n\tpublic const int SCI_ROTATESELECTION = 2606;\n\tpublic const int SCI_SWAPMAINANCHORCARET = 2607;\n\tpublic const int SCI_MULTIPLESELECTADDNEXT = 2688;\n\tpublic const int SCI_MULTIPLESELECTADDEACH = 2689;\n\tpublic const int SCI_CHANGELEXERSTATE = 2617;\n\tpublic const int SCI_CONTRACTEDFOLDNEXT = 2618;\n\tpublic const int SCI_VERTICALCENTRECARET = 2619;\n\tpublic const int SCI_MOVESELECTEDLINESUP = 2620;\n\tpublic const int SCI_MOVESELECTEDLINESDOWN = 2621;\n\tpublic const int SCI_SETIDENTIFIER = 2622;\n\tpublic const int SCI_GETIDENTIFIER = 2623;\n\tpublic const int SCI_RGBAIMAGESETWIDTH = 2624;\n\tpublic const int SCI_RGBAIMAGESETHEIGHT = 2625;\n\tpublic const int SCI_RGBAIMAGESETSCALE = 2651;\n\tpublic const int SCI_MARKERDEFINERGBAIMAGE = 2626;\n\tpublic const int SCI_REGISTERRGBAIMAGE = 2627;\n\tpublic const int SCI_SCROLLTOSTART = 2628;\n\tpublic const int SCI_SCROLLTOEND = 2629;\n\tpublic const int SC_TECHNOLOGY_DEFAULT = 0;\n\tpublic const int SC_TECHNOLOGY_DIRECTWRITE = 1;\n\tpublic const int SC_TECHNOLOGY_DIRECTWRITERETAIN = 2;\n\tpublic const int SC_TECHNOLOGY_DIRECTWRITEDC = 3;\n\tpublic const int SC_TECHNOLOGY_DIRECT_WRITE_1 = 4;\n\tpublic const int SCI_SETTECHNOLOGY = 2630;\n\tpublic const int SCI_GETTECHNOLOGY = 2631;\n\tpublic const int SCI_CREATELOADER = 2632;\n\tpublic const int SCI_FINDINDICATORSHOW = 2640;\n\tpublic const int SCI_FINDINDICATORFLASH = 2641;\n\tpublic const int SCI_FINDINDICATORHIDE = 2642;\n\tpublic const int SCI_VCHOMEDISPLAY = 2652;\n\tpublic const int SCI_VCHOMEDISPLAYEXTEND = 2653;\n\tpublic const int SCI_GETCARETLINEVISIBLEALWAYS = 2654;\n\tpublic const int SCI_SETCARETLINEVISIBLEALWAYS = 2655;\n\tpublic const int SC_LINE_END_TYPE_DEFAULT = 0;\n\tpublic const int SC_LINE_END_TYPE_UNICODE = 1;\n\tpublic const int SCI_SETLINEENDTYPESALLOWED = 2656;\n\tpublic const int SCI_GETLINEENDTYPESALLOWED = 2657;\n\tpublic const int SCI_GETLINEENDTYPESACTIVE = 2658;\n\tpublic const int SCI_SETREPRESENTATION = 2665;\n\tpublic const int SCI_GETREPRESENTATION = 2666;\n\tpublic const int SCI_CLEARREPRESENTATION = 2667;\n\tpublic const int SCI_CLEARALLREPRESENTATIONS = 2770;\n\tpublic const int SC_REPRESENTATION_PLAIN = 0;\n\tpublic const int SC_REPRESENTATION_BLOB = 1;\n\tpublic const int SC_REPRESENTATION_COLOUR = 0x10;\n\tpublic const int SCI_SETREPRESENTATIONAPPEARANCE = 2766;\n\tpublic const int SCI_GETREPRESENTATIONAPPEARANCE = 2767;\n\tpublic const int SCI_SETREPRESENTATIONCOLOUR = 2768;\n\tpublic const int SCI_GETREPRESENTATIONCOLOUR = 2769;\n\tpublic const int SCI_EOLANNOTATIONSETTEXT = 2740;\n\tpublic const int SCI_EOLANNOTATIONGETTEXT = 2741;\n\tpublic const int SCI_EOLANNOTATIONSETSTYLE = 2742;\n\tpublic const int SCI_EOLANNOTATIONGETSTYLE = 2743;\n\tpublic const int SCI_EOLANNOTATIONCLEARALL = 2744;\n\tpublic const int EOLANNOTATION_HIDDEN = 0x0;\n\tpublic const int EOLANNOTATION_STANDARD = 0x1;\n\tpublic const int EOLANNOTATION_BOXED = 0x2;\n\tpublic const int EOLANNOTATION_STADIUM = 0x100;\n\tpublic const int EOLANNOTATION_FLAT_CIRCLE = 0x101;\n\tpublic const int EOLANNOTATION_ANGLE_CIRCLE = 0x102;\n\tpublic const int EOLANNOTATION_CIRCLE_FLAT = 0x110;\n\tpublic const int EOLANNOTATION_FLATS = 0x111;\n\tpublic const int EOLANNOTATION_ANGLE_FLAT = 0x112;\n\tpublic const int EOLANNOTATION_CIRCLE_ANGLE = 0x120;\n\tpublic const int EOLANNOTATION_FLAT_ANGLE = 0x121;\n\tpublic const int EOLANNOTATION_ANGLES = 0x122;\n\tpublic const int SCI_EOLANNOTATIONSETVISIBLE = 2745;\n\tpublic const int SCI_EOLANNOTATIONGETVISIBLE = 2746;\n\tpublic const int SCI_EOLANNOTATIONSETSTYLEOFFSET = 2747;\n\tpublic const int SCI_EOLANNOTATIONGETSTYLEOFFSET = 2748;\n\tpublic const int SC_SUPPORTS_LINE_DRAWS_FINAL = 0;\n\tpublic const int SC_SUPPORTS_PIXEL_DIVISIONS = 1;\n\tpublic const int SC_SUPPORTS_FRACTIONAL_STROKE_WIDTH = 2;\n\tpublic const int SC_SUPPORTS_TRANSLUCENT_STROKE = 3;\n\tpublic const int SC_SUPPORTS_PIXEL_MODIFICATION = 4;\n\tpublic const int SC_SUPPORTS_THREAD_SAFE_MEASURE_WIDTHS = 5;\n\tpublic const int SCI_SUPPORTSFEATURE = 2750;\n\tpublic const int SC_LINECHARACTERINDEX_NONE = 0;\n\tpublic const int SC_LINECHARACTERINDEX_UTF32 = 1;\n\tpublic const int SC_LINECHARACTERINDEX_UTF16 = 2;\n\tpublic const int SCI_GETLINECHARACTERINDEX = 2710;\n\tpublic const int SCI_ALLOCATELINECHARACTERINDEX = 2711;\n\tpublic const int SCI_RELEASELINECHARACTERINDEX = 2712;\n\tpublic const int SCI_LINEFROMINDEXPOSITION = 2713;\n\tpublic const int SCI_INDEXPOSITIONFROMLINE = 2714;\n\tpublic const int SCI_STARTRECORD = 3001;\n\tpublic const int SCI_STOPRECORD = 3002;\n\tpublic const int SCI_SETILEXER = 4033;\n\tpublic const int SCI_GETLEXER = 4002;\n\tpublic const int SCI_COLOURISE = 4003;\n\tpublic const int SCI_SETPROPERTY = 4004;\n\tpublic const int KEYWORDSET_MAX = 8;\n\tpublic const int SCI_SETKEYWORDS = 4005;\n\tpublic const int SCI_GETPROPERTY = 4008;\n\tpublic const int SCI_GETPROPERTYEXPANDED = 4009;\n\tpublic const int SCI_GETPROPERTYINT = 4010;\n\tpublic const int SCI_GETLEXERLANGUAGE = 4012;\n\tpublic const int SCI_PRIVATELEXERCALL = 4013;\n\tpublic const int SCI_PROPERTYNAMES = 4014;\n\tpublic const int SC_TYPE_BOOLEAN = 0;\n\tpublic const int SC_TYPE_INTEGER = 1;\n\tpublic const int SC_TYPE_STRING = 2;\n\tpublic const int SCI_PROPERTYTYPE = 4015;\n\tpublic const int SCI_DESCRIBEPROPERTY = 4016;\n\tpublic const int SCI_DESCRIBEKEYWORDSETS = 4017;\n\tpublic const int SCI_GETLINEENDTYPESSUPPORTED = 4018;\n\tpublic const int SCI_ALLOCATESUBSTYLES = 4020;\n\tpublic const int SCI_GETSUBSTYLESSTART = 4021;\n\tpublic const int SCI_GETSUBSTYLESLENGTH = 4022;\n\tpublic const int SCI_GETSTYLEFROMSUBSTYLE = 4027;\n\tpublic const int SCI_GETPRIMARYSTYLEFROMSTYLE = 4028;\n\tpublic const int SCI_FREESUBSTYLES = 4023;\n\tpublic const int SCI_SETIDENTIFIERS = 4024;\n\tpublic const int SCI_DISTANCETOSECONDARYSTYLES = 4025;\n\tpublic const int SCI_GETSUBSTYLEBASES = 4026;\n\tpublic const int SCI_GETNAMEDSTYLES = 4029;\n\tpublic const int SCI_NAMEOFSTYLE = 4030;\n\tpublic const int SCI_TAGSOFSTYLE = 4031;\n\tpublic const int SCI_DESCRIPTIONOFSTYLE = 4032;\n\t[Flags]\n\tpublic enum MOD {\n\t\tSC_MOD_INSERTTEXT = 0x1,\n\t\tSC_MOD_DELETETEXT = 0x2,\n\t\tSC_MOD_CHANGESTYLE = 0x4,\n\t\tSC_MOD_CHANGEFOLD = 0x8,\n\t\tSC_PERFORMED_USER = 0x10,\n\t\tSC_PERFORMED_UNDO = 0x20,\n\t\tSC_PERFORMED_REDO = 0x40,\n\t\tSC_MULTISTEPUNDOREDO = 0x80,\n\t\tSC_LASTSTEPINUNDOREDO = 0x100,\n\t\tSC_MOD_CHANGEMARKER = 0x200,\n\t\tSC_MOD_BEFOREINSERT = 0x400,\n\t\tSC_MOD_BEFOREDELETE = 0x800,\n\t\tSC_MULTILINEUNDOREDO = 0x1000,\n\t\tSC_STARTACTION = 0x2000,\n\t\tSC_MOD_CHANGEINDICATOR = 0x4000,\n\t\tSC_MOD_CHANGELINESTATE = 0x8000,\n\t\tSC_MOD_CHANGEMARGIN = 0x10000,\n\t\tSC_MOD_CHANGEANNOTATION = 0x20000,\n\t\tSC_MOD_CONTAINER = 0x40000,\n\t\tSC_MOD_LEXERSTATE = 0x80000,\n\t\tSC_MOD_INSERTCHECK = 0x100000,\n\t\tSC_MOD_CHANGETABSTOPS = 0x200000,\n\t\tSC_MOD_CHANGEEOLANNOTATION = 0x400000,\n\t\tSC_MODEVENTMASKALL = 0x7FFFFF,\n\t}\n\t[Flags]\n\tpublic enum UPDATE {\n\t\tSC_UPDATE_CONTENT=1,\n\t\tSC_UPDATE_SELECTION=2,\n\t\tSC_UPDATE_V_SCROLL=4,\n\t\tSC_UPDATE_H_SCROLL=8,\n\t}\n\t\n\tpublic const int SCEN_CHANGE = 768;\n\tpublic const int SCEN_SETFOCUS = 512;\n\tpublic const int SCEN_KILLFOCUS = 256;\n\tpublic const int SCK_DOWN = 300;\n\tpublic const int SCK_UP = 301;\n\tpublic const int SCK_LEFT = 302;\n\tpublic const int SCK_RIGHT = 303;\n\tpublic const int SCK_HOME = 304;\n\tpublic const int SCK_END = 305;\n\tpublic const int SCK_PRIOR = 306;\n\tpublic const int SCK_NEXT = 307;\n\tpublic const int SCK_DELETE = 308;\n\tpublic const int SCK_INSERT = 309;\n\tpublic const int SCK_ESCAPE = 7;\n\tpublic const int SCK_BACK = 8;\n\tpublic const int SCK_TAB = 9;\n\tpublic const int SCK_RETURN = 13;\n\tpublic const int SCK_ADD = 310;\n\tpublic const int SCK_SUBTRACT = 311;\n\tpublic const int SCK_DIVIDE = 312;\n\tpublic const int SCK_WIN = 313;\n\tpublic const int SCK_RWIN = 314;\n\tpublic const int SCK_MENU = 315;\n\tpublic const int SCMOD_NORM = 0;\n\tpublic const int SCMOD_SHIFT = 1;\n\tpublic const int SCMOD_CTRL = 2;\n\tpublic const int SCMOD_ALT = 4;\n\tpublic const int SCMOD_SUPER = 8;\n\tpublic const int SCMOD_META = 16;\n\tpublic const int SC_AC_FILLUP = 1;\n\tpublic const int SC_AC_DOUBLECLICK = 2;\n\tpublic const int SC_AC_TAB = 3;\n\tpublic const int SC_AC_NEWLINE = 4;\n\tpublic const int SC_AC_COMMAND = 5;\n\tpublic const int SC_AC_SINGLE_CHOICE = 6;\n\tpublic const int SC_CHARACTERSOURCE_DIRECT_INPUT = 0;\n\tpublic const int SC_CHARACTERSOURCE_TENTATIVE_INPUT = 1;\n\tpublic const int SC_CHARACTERSOURCE_IME_RESULT = 2;\n\tpublic enum NOTIF {\n\t\tSCN_STYLENEEDED = 2000,\n\t\tSCN_CHARADDED = 2001,\n\t\tSCN_SAVEPOINTREACHED = 2002,\n\t\tSCN_SAVEPOINTLEFT = 2003,\n\t\tSCN_MODIFYATTEMPTRO = 2004,\n\t\tSCN_KEY = 2005,\n\t\tSCN_DOUBLECLICK = 2006,\n\t\tSCN_UPDATEUI = 2007,\n\t\tSCN_MODIFIED = 2008,\n\t\tSCN_MACRORECORD = 2009,\n\t\tSCN_MARGINCLICK = 2010,\n\t\tSCN_NEEDSHOWN = 2011,\n\t\tSCN_PAINTED = 2013,\n\t\tSCN_USERLISTSELECTION = 2014,\n\t\tSCN_URIDROPPED = 2015,\n\t\tSCN_DWELLSTART = 2016,\n\t\tSCN_DWELLEND = 2017,\n\t\tSCN_ZOOM = 2018,\n\t\tSCN_HOTSPOTCLICK = 2019,\n\t\tSCN_HOTSPOTDOUBLECLICK = 2020,\n\t\tSCN_CALLTIPCLICK = 2021,\n\t\tSCN_AUTOCSELECTION = 2022,\n\t\tSCN_INDICATORCLICK = 2023,\n\t\tSCN_INDICATORRELEASE = 2024,\n\t\tSCN_AUTOCCANCELLED = 2025,\n\t\tSCN_AUTOCCHARDELETED = 2026,\n\t\tSCN_HOTSPOTRELEASECLICK = 2027,\n\t\tSCN_FOCUSIN = 2028,\n\t\tSCN_FOCUSOUT = 2029,\n\t\tSCN_AUTOCCOMPLETED = 2030,\n\t\tSCN_MARGINRIGHTCLICK = 2031,\n\t\tSCN_AUTOCSELECTIONCHANGE = 2032,\n\t}\n\tpublic const int SC_BIDIRECTIONAL_DISABLED = 0;\n\tpublic const int SC_BIDIRECTIONAL_L2R = 1;\n\tpublic const int SC_BIDIRECTIONAL_R2L = 2;\n\tpublic const int SCI_GETBIDIRECTIONAL = 2708;\n\tpublic const int SCI_SETBIDIRECTIONAL = 2709;\n\n\tpublic struct Sci_CharacterRange {\n\t\tpublic int cpMin;\n\t\tpublic int cpMax;\n\t}\n\n\tpublic struct Sci_TextRange {\n\t\tpublic int cpMin;\n\t\tpublic int cpMax;\n\t\tpublic byte* lpstrText;\n\t}\n\n\tpublic struct Sci_TextToFind {\n\t\tpublic int cpMin;\n\t\tpublic int cpMax;\n\t\tpublic byte* lpstrText;\n\t\tpublic Sci_CharacterRange chrgText;\n\t}\n\n\tpublic struct Sci_Rectangle {\n\t\tpublic int left;\n\t\tpublic int top;\n\t\tpublic int right;\n\t\tpublic int bottom;\n\t}\n\n\tpublic struct Sci_RangeToFormat {\n\t\tpublic IntPtr hdc;\n\t\tpublic IntPtr hdcTarget;\n\t\tpublic Sci_Rectangle rc;\n\t\tpublic Sci_Rectangle rcPage;\n\t\tpublic Sci_CharacterRange chrg;\n\t}\n\n\tpublic struct Sci_NotifyHeader {\n\t\tpublic wnd hwndFrom;\n\t\tpublic nint idFrom;\n\t\tpublic NOTIF code;\n\t}\n\n\tpublic struct SCNotification {\n#pragma warning disable 649 //field never assigned\n\t\tpublic Sci_NotifyHeader nmhdr;\n\t\t/// <summary>Returns <c>nmhdr.code</c>.</summary>\n\t\tpublic NOTIF code => nmhdr.code;\n\t\tnint _position;\n\t\t/// <summary>Raw UTF-8 position.</summary>\n\t\tpublic int position => (int)_position;\n\t\tpublic int ch;\n\t\tpublic int modifiers;\n\t\tpublic MOD modificationType;\n\t\tpublic byte* textUTF8;\n\t\tnint _length;\n\t\tpublic int length => (int)_length;\n\t\tnint _linesAdded;\n\t\tpublic int linesAdded => (int)_linesAdded;\n\t\tpublic int message;\n\t\tpublic nint wParam;\n\t\tpublic nint lParam;\n\t\tnint _line;\n\t\tpublic int line => (int)_line;\n\t\tpublic int foldLevelNow;\n\t\tpublic int foldLevelPrev;\n\t\tpublic int margin;\n\t\tpublic int listType;\n\t\tpublic int x;\n\t\tpublic int y;\n\t\tpublic int token;\n\t\tnint _annotationLinesAdded;\n\t\tpublic int annotationLinesAdded => (int)_annotationLinesAdded;\n\t\tpublic UPDATE updated;\n\t\tpublic int listCompletionMethod;\n#pragma warning restore 649 //field never assigned\n\n\t\t/// <summary>\n\t\t/// Returns position, UTF-8. If SCN_MODIFIED(SC_MOD_INSERTTEXT|SC_MOD_BEFOREINSERT|SC_MOD_INSERTCHECK), adds length, because position then is old position.\n\t\t/// </summary>\n\t\tpublic int FinalPosition {\n\t\t\tget {\n\t\t\t\tint r = position;\n\t\t\t\tif (length > 0 && nmhdr.code == NOTIF.SCN_MODIFIED\n\t\t\t\t\t&& modificationType.HasAny(MOD.SC_MOD_INSERTTEXT | MOD.SC_MOD_BEFOREINSERT | MOD.SC_MOD_INSERTCHECK)\n\t\t\t\t\t) r += length;\n\t\t\t\treturn r;\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Converts textUTF8 to C# string.\n\t\t/// Returns null if textUTF8 is null.\n\t\t/// Don't call this property multiple times for the same notification. Store the return value in a variable and use it.\n\t\t/// </summary>\n\t\tpublic string Text {\n\t\t\tget {\n\t\t\t\tif (textUTF8 == null) return null;\n\t\t\t\tif (textUTF8[0] == 0) return \"\";\n\t\t\t\treturn new string((sbyte*)textUTF8, 0, length, Encoding.UTF8);\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au.Controls/KScintilla/Sci adapter.cs",
    "content": "namespace Au.Controls;\n\nusing static Sci;\n\npublic unsafe partial class KScintilla {\n\t_Adapter _adapter; //created in KScintilla ctor\n\t\n\t/// <summary>\n\t/// Gets or sets text.\n\t/// Uses caching, therefore the <c>get</c> function is fast and garbage-free when calling multiple times.\n\t/// </summary>\n\t/// <remarks>\n\t/// The <c>get</c> function gets cached text if called not the first time after setting or modifying control text.\n\t/// The <c>set</c> function calls <see cref=\"aaaSetText\"/> when need. Uses default parameters (with undo and notifications, unless <b>AaInitReadOnlyAlways</b>).\n\t/// Unlike the above methods, this property can be used before creating handle.\n\t/// </remarks>\n\tpublic string aaaText {\n\t\tget => _adapter.Text;\n\t\tset { _adapter.Text = value; }\n\t}\n\t\n\t/// <summary>\n\t/// UTF-8 text length.\n\t/// </summary>\n\tpublic int aaaLen8 => _adapter.Len8;\n\t\n\t/// <summary>\n\t/// UTF-16 text length.\n\t/// </summary>\n\tpublic int aaaLen16 => _adapter.Len16;\n\t\n\t/// <summary>\n\t/// Converts UTF-16 position to UTF-8 position. Fast.\n\t/// </summary>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Negative or greater than <see cref=\"aaaLen16\"/>.</exception>\n\tpublic int aaaPos8(int pos16) => _adapter.Pos8(pos16);\n\t\n\t/// <summary>\n\t/// Converts UTF-8 position to UTF-16 position. Fast.\n\t/// </summary>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Negative or greater than <see cref=\"aaaLen8\"/>.</exception>\n\tpublic unsafe int aaaPos16(int pos8) => _adapter.Pos16(pos8);\n\t\n\t/// <summary>\n\t/// Maps UTF-16 to/from UTF-8 positions.\n\t/// Gets UTF-16 and UTF-8 length.\n\t/// Gets cached UTF-16 text.\n\t/// </summary>\n\tclass _Adapter {\n\t\trecord struct _NA(int i8, int i16, int len16, int charLen);\n\t\t\n\t\treadonly KScintilla _sci;\n\t\tstring _text;\n\t\treadonly List<_NA> _a;\n\t\tint _len8, _len16;\n\t\tbool _mapDone;\n\t\t\n\t\tpublic _Adapter(KScintilla sci) {\n\t\t\t_sci = sci;\n\t\t\t_a = new();\n\t\t}\n\t\t\n\t\tpublic void HandleCreated() {\n\t\t\tif (!_text.NE()) _sci.aaaSetText(_text, SciSetTextFlags.NoUndoNoNotify);\n\t\t}\n\t\t\n\t\tpublic void TextModified() {\n\t\t\t_text = null;\n\t\t\t_a.Clear();\n\t\t\t_mapDone = false;\n\t\t}\n\t\t\n\t\tstring _GetText() => _sci._RangeText(0, Len8);\n\t\t\n\t\tpublic string Text {\n\t\t\tget {\n\t\t\t\t//if (_sci.Name == \"document\") print.qm2.write($\"Text: cached={_text != null}\");\n\t\t\t\tif (_text == null && !_sci._w.Is0) _text = _GetText(); //_NotifyModified sets _text=null\n\t\t\t\treturn _text;\n\t\t\t}\n\t\t\tset {\n\t\t\t\tif (_sci._w.Is0) _text = value; //will set control text on WM_CREATE\n\t\t\t\telse _sci.aaaSetText(value); //_NotifyModified sets _text=null. Control text can be != value, eg when tags parsed.\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic int Len8 => _mapDone ? _len8 : _sci.Call(SCI_GETTEXTLENGTH);\n\t\t\n\t\tpublic int Len16 {\n\t\t\tget {\n\t\t\t\tif (_text != null) return _text.Length;\n\t\t\t\tif (!_mapDone) _CreatePosMap();\n\t\t\t\treturn _len16;\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic int Pos8(int pos16) {\n\t\t\tif (!_mapDone) _CreatePosMap();\n\t\t\t\n\t\t\tDebug.Assert((uint)pos16 <= _len16);\n\t\t\tif ((uint)pos16 > _len16) throw new ArgumentOutOfRangeException(nameof(pos16), $\"pos16 = {pos16}, _len16 = {_len16}, _GetText().Length={_GetText().Length}\");\n\t\t\t\n\t\t\t//using binary search find max _a[r].i16 that is < pos16\n\t\t\tint r = -1, from = 0, to = _a.Count;\n\t\t\twhile (to > from) {\n\t\t\t\tint m = (from + to) / 2;\n\t\t\t\tif (_a[m].i16 < pos16) from = (r = m) + 1; else to = m;\n\t\t\t}\n\t\t\tif (r < 0) return pos16; //_a is empty (ASCII text) or pos16 <= _a[0].i16 (before first non-ASCII character)\n\t\t\tvar p = _a[r];\n\t\t\treturn p.i8 + Math.Min(pos16 - p.i16, p.len16) * p.charLen + Math.Max(pos16 - (p.i16 + p.len16), 0); //p.i8 + utf + ascii\n\t\t}\n\t\t\n\t\tpublic unsafe int Pos16(int pos8) {\n\t\t\tif (!_mapDone) _CreatePosMap();\n\t\t\t\n\t\t\tDebug.Assert((uint)pos8 <= _len8);\n\t\t\tif ((uint)pos8 > _len8) throw new ArgumentOutOfRangeException(nameof(pos8), $\"pos8 = {pos8}, _len8 = {_len8}, _sci.Call(SCI_GETTEXTLENGTH)={_sci.Call(SCI_GETTEXTLENGTH)}\");\n\t\t\t\n\t\t\t//using binary search find max _a[r].i8 that is < pos8\n\t\t\tint r = -1, from = 0, to = _a.Count;\n\t\t\twhile (to > from) {\n\t\t\t\tint m = (from + to) / 2;\n\t\t\t\tif (_a[m].i8 < pos8) from = (r = m) + 1; else to = m;\n\t\t\t}\n\t\t\tif (r < 0) return pos8; //_a is empty (ASCII text) or pos8 <= _a[0].i8 (before first non-ASCII character)\n\t\t\tvar p = _a[r];\n\t\t\tint len8 = p.len16 * p.charLen;\n\t\t\treturn p.i16 + Math.Min(pos8 - p.i8, len8) / p.charLen + Math.Max(pos8 - (p.i8 + len8), 0); //p.i16 + utf + ascii\n\t\t}\n\t\t\n\t\t[MethodImpl(MethodImplOptions.AggressiveOptimization)]\n\t\tunsafe void _CreatePosMap() {\n\t\t\t//This func is fast and often garbageless. For code edit controls don't need to optimize to avoid calling it frequently, eg for each added character.\n\t\t\t//Should not be used for output/log controls if called on each \"append text\".\n\t\t\t\n\t\t\tint textLen;\n\t\t\tint gap = Sci_Range(_sci.AaSciPtr, 0, -1, out var p, out var p2, &textLen);\n\t\t\tint to8 = p2 == null ? textLen : gap;\n\t\t\tint i8 = 0, i16 = 0;\n\t\t\tfor (; ; ) {\n\t\t\t\t//ASCII range\n\t\t\t\tint start8 = i8;\n\t\t\t\tint lenAscii8 = new RByte(p + i8, to8 - i8).IndexOfAnyExceptInRange((byte)0, (byte)127);\n\t\t\t\tif (lenAscii8 < 0) i8 = to8; else i8 += lenAscii8;\n\t\t\t\ti16 += i8 - start8;\n\t\t\t\t\n\t\t\t\t//non-ASCII range\n\t\t\t\tif (i8 < to8) {\n\t\t\t\t\tstart8 = i8;\n\t\t\t\t\tint charLen = 0;\n\t\t\t\t\twhile (i8 < to8 && p[i8] >= 0x80) {\n\t\t\t\t\t\tRune.DecodeFromUtf8(new(p + i8, to8 - i8), out _, out int nb);\n\t\t\t\t\t\tif (charLen == 0) charLen = nb; else if (nb != charLen) break;\n\t\t\t\t\t\ti8 += nb;\n\t\t\t\t\t}\n\t\t\t\t\tif (charLen == 4) charLen = 2;\n\t\t\t\t\tint len16 = (i8 - start8) / charLen;\n\t\t\t\t\t_a.Add(new(start8, i16, len16, charLen));\n\t\t\t\t\ti16 += len16;\n\t\t\t\t\tif (i8 < to8) continue;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t//the part after gap\n\t\t\t\tif (p2 == null) break;\n\t\t\t\tp = p2 - i8;\n\t\t\t\tp2 = null;\n\t\t\t\tto8 = textLen;\n\t\t\t}\n\t\t\t\n\t\t\t_len8 = textLen;\n\t\t\t_len16 = i16;\n\t\t\t_mapDone = true;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au.Controls/KScintilla/Sci loader.cs",
    "content": "namespace Au.Controls;\n\nusing static Sci;\n\npublic unsafe partial class KScintilla {\n\tpublic class aaaFileLoaderSaver {\n\t\t_Encoding _enc;\n\t\tbyte[] _text;\n\n\t\tpublic bool IsBinary => _enc == _Encoding.Binary;\n\n\t\tpublic bool IsImage { get; private set; }\n\n\t\t/// <summary>\n\t\t/// Loads file as UTF-8.\n\t\t/// Supports any encoding (UTF-8, UTF-16, etc), BOM. Remembers it for Save.\n\t\t/// </summary>\n\t\t/// <exception cref=\"Exception\">Exceptions of File.OpenRead, File.Read, Encoding.Convert.</exception>\n\t\tpublic void Load(string file) {\n\t\t\t_enc = _Encoding.Binary;\n\t\t\tIsImage = false;\n\t\t\tif (file.Ends(true, \".png\", \".bmp\", \".jpg\", \".jpeg\", \".gif\", \".tif\", \".tiff\", \".ico\", \".cur\", \".ani\") > 0) {\n\t\t\t\tif (!filesystem.exists(file).File) throw new FileNotFoundException($\"Could not find file '{file}'.\");\n\t\t\t\tIsImage = true;\n\t\t\t\t_text = Encoding.UTF8.GetBytes($\"<image \\\"{file}\\\">\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tusing var fr = filesystem.loadStream(file);\n\n\t\t\tif (fr.Length > 100_000_000) {\n\t\t\t\t_text = \"//Cannot edit. The file is too big, more than 100_000_000 bytes.\"u8.ToArray();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tint fileSize = (int)fr.Length;\n\t\t\tvar b = new byte[fileSize];\n\t\t\tif (fr.Read(b, 0, fileSize) != fileSize) throw null;\n\n\t\t\t_enc = _DetectEncoding(b);\n\t\t\t//print.it(_enc);\n\t\t\tif (_enc == _Encoding.Binary) {\n\t\t\t\t_text = \"//Cannot edit. The file is binary, not text.\"u8.ToArray();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (_enc == _Encoding.Utf8_BOM && !System.Text.Unicode.Utf8.IsValid(b[3..])) b = b.ToStringUTF8().ToUTF8(); //else Scintilla text would be bad\n\n\t\t\tif (_EncodingEnumToObject() is { } e) {\n\t\t\t\tint bomLength = (int)_enc >>> 4;\n\t\t\t\tb = Encoding.Convert(e, Encoding.UTF8, b, bomLength, (int)fileSize - bomLength);\n\t\t\t}\n\n\t\t\t_text = b;\n\t\t}\n\n\t\tEncoding _EncodingEnumToObject() {\n\t\t\tswitch (_enc) {\n\t\t\tcase _Encoding.Utf16_BOM or _Encoding.Utf16: return Encoding.Unicode;\n\t\t\tcase _Encoding.Utf16BE_BOM or _Encoding.Utf16BE: return Encoding.BigEndianUnicode;\n\t\t\tcase _Encoding.Utf32_BOM: return Encoding.UTF32;\n\t\t\tcase _Encoding.Utf32BE_BOM: return new UTF32Encoding(true, false);\n\t\t\tcase _Encoding.Ansi: return StringUtil.GetEncoding(-1);\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\tstatic unsafe _Encoding _DetectEncoding(RByte s) {\n\t\t\tint len = s.Length;\n\t\t\t//is too short to have a BOM?\n\t\t\tif (len == 0) return _Encoding.Utf8;\n\t\t\tif (len == 1) return s[0] == 0 ? _Encoding.Binary : (s[0] < 128 ? _Encoding.Utf8 : _Encoding.Ansi);\n\t\t\t//has a BOM?\n\t\t\tif (s is [0xEF, 0xBB, 0xBF, ..]) return _Encoding.Utf8_BOM;\n\t\t\tif (s is [0xFF, 0xFE, 0, 0, ..]) return _Encoding.Utf32_BOM;\n\t\t\tif (s is [0xFF, 0xFE, ..]) return _Encoding.Utf16_BOM;\n\t\t\tif (s is [0xFE, 0xFF, ..]) return _Encoding.Utf16BE_BOM;\n\t\t\tif (s is [0, 0, 0xFE, 0xFF, ..]) return _Encoding.Utf32BE_BOM;\n\t\t\t//has '\\0'?\n\t\t\tint zeroAt = s.IndexOf((byte)0);\n\t\t\tif (zeroAt is 0 or 1 && 0 == (len & 1)) {\n\t\t\t\tif (MemoryMarshal.Cast<byte, char>(s).Contains('\\0')) return _Encoding.Binary;\n\t\t\t\treturn zeroAt is 0 ? _Encoding.Utf16BE : _Encoding.Utf16;\n\t\t\t}\n\t\t\t//if (zeroAt == len - 1) { s = s[..--len]; zeroAt = -1; } //WordPad saves .rtf files with '\\0' at the end. Rejected; eg VS and VSCode detects as binary.\n\t\t\tif (zeroAt >= 0) return _Encoding.Binary;\n\t\t\treturn System.Text.Unicode.Utf8.IsValid(s) ? _Encoding.Utf8 : _Encoding.Ansi;\n\t\t}\n\n\t\tenum _Encoding : byte {\n\t\t\t/// <summary>Not a text file, or loading failed, or not initialized.</summary>\n\t\t\tBinary = 0, //must be 0\n\n\t\t\t/// <summary>ASCII or UTF-8 without BOM.</summary>\n\t\t\tUtf8 = 1,\n\n\t\t\t/// <summary>UTF-8 with BOM (3 bytes).</summary>\n\t\t\tUtf8_BOM = 1 | (3 << 4),\n\n\t\t\t/// <summary>ANSI containing non-ASCII characters, unknown code page.</summary>\n\t\t\tAnsi = 2,\n\n\t\t\t/// <summary>UTF-16 without BOM.</summary>\n\t\t\tUtf16 = 3,\n\n\t\t\t/// <summary>UTF-16 big endian without BOM.</summary>\n\t\t\tUtf16BE = 4,\n\n\t\t\t/// <summary>UTF-16 with BOM (2 bytes).</summary>\n\t\t\tUtf16_BOM = 3 | (2 << 4),\n\n\t\t\t/// <summary>UTF-16 with big endian BOM (2 bytes).</summary>\n\t\t\tUtf16BE_BOM = 4 | (2 << 4),\n\n\t\t\t/// <summary>UTF-32 with BOM (4 bytes).</summary>\n\t\t\tUtf32_BOM = 5 | (4 << 4),\n\n\t\t\t/// <summary>UTF-32 with big endian BOM (4 bytes).</summary>\n\t\t\tUtf32BE_BOM = 6 | (4 << 4),\n\n\t\t\t//rejected. .NET does not save/load with UTF-7 BOM, so we too. Several different BOM of different length.\n\t\t\t///// <summary>UTF-7 with BOM.</summary>\n\t\t\t//Utf7_BOM,\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Sets control text.\n\t\t/// If the file is image, binary or too big (<c>> 100_000_000</c>), sets to display the image or/and some short info text, makes the control read-only, sets <b>Save</b> to throw exception, and returns false. Else returns true.\n\t\t/// Uses <see cref=\"SciSetTextFlags\"/> NoUndo and NoNotify.\n\t\t/// Must be called once.\n\t\t/// </summary>\n\t\tpublic unsafe bool SetText(KScintilla k) {\n\t\t\tRByte text = _text;\n\t\t\tif (_enc == _Encoding.Utf8_BOM) text = text[3..];\n\t\t\tif (_enc == _Encoding.Binary) k.Call(SCI_SETREADONLY); //caller may set AaInitReadOnlyAlways = true\n\t\t\tusing (new _NoUndoNotif(k, SciSetTextFlags.NoUndoNoNotify)) {\n\t\t\t\tif (IsImage && k.AaTags is {  } tags) tags.AddText(text.ToStringUTF8(), false, false, dontHideImageTag: true);\n\t\t\t\telse k.aaaSetString(SCI_APPENDTEXT, text.Length, text);\n\t\t\t}\n\t\t\tif (_enc != _Encoding.Binary) return true;\n\t\t\tk.Call(SCI_SETREADONLY, 1);\n\t\t\treturn false;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Returns true if text contains newlines other than <c>\\r\\n</c> and <c>\\n</c>.\n\t\t/// </summary>\n\t\tpublic bool DetectBadNewlines() {\n\t\t\tif (_enc != _Encoding.Binary) {\n\t\t\t\tvar s = _text;\n\t\t\t\tfor (int i = 0; i < s.Length - 1;) { //ends with '\\0'\n\t\t\t\t\tswitch (s[i++]) {\n\t\t\t\t\tcase 13 when s[i] != 10: return true;\n\t\t\t\t\tcase 0xc2 when s[i] == 0x85: return true;\n\t\t\t\t\tcase 0xe2 when s[i] == 0x80 && s[i + 1] is 0xa8 or 0xa9: return true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tpublic void FinishedLoading() {\n\t\t\t_text = null; //GC\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Saves control text with the same encoding/BOM as loaded. Uses <see cref=\"filesystem.save\"/>.\n\t\t/// </summary>\n\t\t/// <param name=\"file\">To pass to filesystem.save.</param>\n\t\t/// <param name=\"tempDirectory\">To pass to filesystem.save.</param>\n\t\t/// <exception cref=\"Exception\">Exceptions of filesystem.save.</exception>\n\t\t/// <exception cref=\"InvalidOperationException\">The file is binary (then <b>SetText</b> made the control read-only), or <b>Load</b> not called.</exception>\n\t\tpublic unsafe void Save(KScintilla k, string file, string tempDirectory = null) {\n\t\t\tif (_enc == _Encoding.Binary) throw new InvalidOperationException();\n\n\t\t\t//_enc = _Encoding.; //test\n\n\t\t\tEncoding e = _EncodingEnumToObject();\n\n\t\t\tint bom = (int)_enc >> 4; //BOM length\n\t\t\tuint bomm = 0; //BOM memory\n\t\t\tif (e != null) bomm = _enc switch {\n\t\t\t\t_Encoding.Utf16_BOM or _Encoding.Utf32_BOM => 0xFEFF,\n\t\t\t\t_Encoding.Utf16BE_BOM => 0xFFFE,\n\t\t\t\t_Encoding.Utf32BE_BOM => 0xFFFE0000,\n\t\t\t\t_ => 0\n\t\t\t};\n\t\t\telse if (bom == 3) bomm = 0xBFBBEF; //UTF8; else bom 0\n\n\t\t\t//print.it(_enc, bom, bomm, e);\n\n\t\t\tfilesystem.save(file, temp => {\n\t\t\t\tusing var fs = File.Create(temp);\n\t\t\t\tif (bomm != 0) { uint u = bomm; fs.Write(new RByte((byte*)&u, bom)); } //rare\n\t\t\t\tif (e != null) { //rare\n\t\t\t\t\tvar bytes = e.GetBytes(k.aaaText); //convert encoding. aaaText likely gets cached text, fast\n\t\t\t\t\tfs.Write(bytes);\n\t\t\t\t} else {\n\t\t\t\t\tint len = k.aaaLen8;\n\t\t\t\t\tvar bytes = (byte*)k.CallRetPtr(SCI_GETCHARACTERPOINTER);\n\t\t\t\t\tfs.Write(new RByte(bytes, len));\n\t\t\t\t}\n\t\t\t}, tempDirectory: tempDirectory);\n\n\t\t\t//print.it(\"file\", File.ReadAllBytes(file));\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au.Controls/KScintilla/Sci other.cs",
    "content": "namespace Au.Controls;\n\nusing static Sci;\n\npublic partial class KScintilla {\n\t\n\t#region markers\n\t\n\t/// <summary>\n\t/// Sets marker style and colors.\n\t/// </summary>\n\t/// <param name=\"marker\">Marker index, 0-31. Indices 25-31 are used for folding markers (SC_MARKNUM_FOLDERx). Indices 21-24 are used for change history markers. Scintilla draws markers from smaller to bigger index.</param>\n\t/// <param name=\"style\">SC_MARK_.</param>\n\t/// <param name=\"foreColor\">SCI_MARKERSETFORE.</param>\n\t/// <param name=\"backColor\">SCI_MARKERSETBACK.</param>\n\tpublic void aaaMarkerDefine(int marker, int style, ColorInt? foreColor = null, ColorInt? backColor = null) {\n\t\tif ((uint)marker > 31) throw new ArgumentOutOfRangeException(nameof(marker));\n\t\tCall(SCI_MARKERDEFINE, marker, style);\n\t\tif (foreColor != null) Call(SCI_MARKERSETFORE, marker, foreColor.Value.ToBGR());\n\t\tif (backColor != null) Call(SCI_MARKERSETBACK, marker, backColor.Value.ToBGR());\n\t}\n\t\n\t/// <summary>\n\t/// SCI_MARKERADD.\n\t/// </summary>\n\t/// <returns>Marker handle, or -1 if failed.</returns>\n\tpublic int aaaMarkerAdd(int marker, int line) {\n\t\treturn Call(SCI_MARKERADD, line, marker);\n\t}\n\t\n\t/// <summary>\n\t/// SCI_MARKERADD in line containing <i>pos</i>.\n\t/// </summary>\n\t/// <returns>Marker handle, or -1 if failed.</returns>\n\tpublic int aaaMarkerAdd(int marker, bool utf16, int pos) {\n\t\treturn aaaMarkerAdd(marker, aaaLineFromPos(utf16, pos));\n\t}\n\t\n\t/// <summary>\n\t/// SCI_MARKERDELETE.\n\t/// </summary>\n\tpublic void aaaMarkerDelete(int marker, int line) {\n\t\tCall(SCI_MARKERDELETE, line, marker);\n\t}\n\t\n\t/// <summary>\n\t/// SCI_MARKERDELETE in line containing <i>pos</i>.\n\t/// </summary>\n\tpublic void aaaMarkerDelete(int marker, bool utf16, int pos) {\n\t\taaaMarkerDelete(marker, aaaLineFromPos(utf16, pos));\n\t}\n\t\n\t/// <summary>\n\t/// SCI_MARKERDELETEALL.\n\t/// </summary>\n\t/// <param name=\"marker\">If -1, delete all markers from all lines.</param>\n\tpublic void aaaMarkerDeleteAll(int marker) {\n\t\tCall(SCI_MARKERDELETEALL, marker);\n\t}\n\t\n\t/// <summary>\n\t/// SCI_MARKERDELETEHANDLE.\n\t/// </summary>\n\tpublic void aaaMarkerDeleteHandle(int handle) {\n\t\tCall(SCI_MARKERDELETEHANDLE, handle);\n\t}\n\t\n\t#endregion\n\t\n\t#region indicators\n\t\n\t/// <summary>\n\t/// Sets indicator style, color, etc.\n\t/// </summary>\n\t/// <param name=\"indic\">Indicator index, 0-31. Scintilla draws indicators from smaller to bigger index.</param>\n\t/// <param name=\"style\">Eg Sci.INDIC_FULLBOX.</param>\n\t/// <param name=\"color\">SCI_INDICSETFORE.</param>\n\t/// <param name=\"alpha\">SCI_INDICSETALPHA. Valid for some styles.</param>\n\t/// <param name=\"borderAlpha\">SCI_INDICSETOUTLINEALPHA. Valid for some styles.</param>\n\t/// <param name=\"strokeWidth\">SCI_INDICSETSTROKEWIDTH (%). Valid for some styles.</param>\n\t/// <param name=\"underText\">SCI_INDICSETUNDER (under text).</param>\n\t/// <param name=\"hoverColor\">SCI_INDICSETHOVERFORE.</param>\n\t/// <param name=\"hoverStyle\">SCI_INDICSETHOVERSTYLE</param>\n\tpublic void aaaIndicatorDefine(int indic, int style, ColorInt? color = null, int? alpha = null, int? borderAlpha = null, int? strokeWidth = null, bool underText = false, ColorInt? hoverColor = null, int? hoverStyle = null) {\n\t\tif ((uint)indic > 31) throw new ArgumentOutOfRangeException(nameof(indic));\n\t\tCall(SCI_INDICSETSTYLE, indic, style);\n\t\tif (style != INDIC_HIDDEN) {\n\t\t\tif (color != null) Call(SCI_INDICSETFORE, indic, color.Value.ToBGR());\n\t\t\tif (alpha != null) Call(SCI_INDICSETALPHA, indic, alpha.Value);\n\t\t\tif (borderAlpha != null) Call(SCI_INDICSETOUTLINEALPHA, indic, borderAlpha.Value);\n\t\t\tif (strokeWidth != null) Call(SCI_INDICSETSTROKEWIDTH, indic, strokeWidth.Value);\n\t\t\tif (underText) Call(SCI_INDICSETUNDER, indic, underText);\n\t\t}\n\t\tif (hoverColor != null) Call(SCI_INDICSETHOVERFORE, indic, hoverColor.Value.ToBGR());\n\t\tif (hoverStyle != null) Call(SCI_INDICSETHOVERSTYLE, indic, hoverStyle.Value);\n\t}\n\t\n\tpublic void aaaIndicatorClear(int indic) => aaaIndicatorClear(indic, false, ..);\n\t\n\tpublic void aaaIndicatorClear(int indic, bool utf16, Range r) {\n\t\tvar (from, to) = aaaNormalizeRange(utf16, r);\n\t\tCall(SCI_SETINDICATORCURRENT, indic);\n\t\tCall(SCI_INDICATORCLEARRANGE, from, to - from);\n\t}\n\t\n\tpublic void aaaIndicatorAdd(int indic, bool utf16, Range r, int value = 1) {\n\t\tvar (from, to) = aaaNormalizeRange(utf16, r);\n\t\tCall(SCI_SETINDICATORCURRENT, indic);\n\t\tCall(SCI_SETINDICATORVALUE, value);\n\t\tCall(SCI_INDICATORFILLRANGE, from, to - from);\n\t}\n\t\n\t/// <summary>\n\t/// SCI_INDICATORVALUEAT.\n\t/// </summary>\n\tpublic int aaaIndicatorGetValue(int indic, int pos, bool utf16 = false) {\n\t\tif (utf16) pos = aaaPos8(pos);\n\t\treturn Call(SCI_INDICATORVALUEAT, indic, pos);\n\t}\n\t\n\t/// <summary>\n\t/// SCI_INDICATORALLONFOR. Returns a bitmap value representing which indicators are non-zero at <i>pos</i>.\n\t/// </summary>\n\tpublic int aaaIndicatorGetAll(int pos, bool utf16 = false) {\n\t\tif (utf16) pos = aaaPos8(pos);\n\t\treturn Call(SCI_INDICATORALLONFOR, pos);\n\t}\n\t\n\t/// <summary>\n\t/// Finds the first range of the specified indicator.\n\t/// </summary>\n\t/// <param name=\"indic\"></param>\n\t/// <param name=\"r\">UTF-8.</param>\n\t/// <returns>false if not found.</returns>\n\tpublic bool aaaIndicatorFindFirst(int indic, out StartEnd r) {\n\t\tr = default;\n\t\tint start = Call(SCI_INDICATOREND, indic);\n\t\tif (start <= 0) {\n\t\t\tif (start < 0 || aaaIndicatorGetValue(indic, 0) == 0) return false;\n\t\t}\n\t\tint end = Call(SCI_INDICATOREND, indic, start);\n\t\tif (end <= start) return false;\n\t\tr = new(start, end);\n\t\treturn true;\n\t}\n\t\n\t#endregion\n\t\n\t#region margins\n\t\n\t/// <summary>\n\t/// SCI_SETMARGINTYPEN. Optionally SCI_SETMARGINSENSITIVEN, SCI_SETMARGINCURSORN, SCI_SETMARGINMASKN.\n\t/// </summary>\n\t/// <param name=\"margin\"></param>\n\t/// <param name=\"type\">SC_MARGIN_.</param>\n\t/// <param name=\"markersMask\">SCI_SETMARGINMASKN.</param>\n\t/// <param name=\"sensitive\">SCI_SETMARGINSENSITIVEN.</param>\n\t/// <param name=\"cursorArrow\">SCI_SETMARGINCURSORN. True SC_CURSORARROW, false SC_CURSORREVERSEARROW.</param>\n\tpublic void aaaMarginSetType(int margin, int type, int? markersMask = null, bool? sensitive = null, bool? cursorArrow = null) {\n\t\tCall(SCI_SETMARGINTYPEN, margin, type);\n\t\tif (markersMask.HasValue) Call(SCI_SETMARGINMASKN, margin, markersMask.Value);\n\t\tif (sensitive.HasValue) Call(SCI_SETMARGINSENSITIVEN, margin, sensitive.Value);\n\t\tif (cursorArrow.HasValue) Call(SCI_SETMARGINCURSORN, margin, cursorArrow == true ? SC_CURSORARROW : SC_CURSORREVERSEARROW);\n\t}\n\t\n\t/// <summary>\n\t/// SCI_SETMARGINWIDTHN or SCI_SETMARGINLEFT or SCI_SETMARGINRIGHT.\n\t/// Dpi-scales, and will scale when DPI changed.\n\t/// </summary>\n\t/// <param name=\"margin\">Margin index for SCI_SETMARGINWIDTHN, or -1 to use SCI_SETMARGINLEFT, or -2 to use SCI_SETMARGINRIGHT.</param>\n\t/// <param name=\"pixels\">Logical pixels or 0.</param>\n\t/// <param name=\"chars\">Number of character '8' widths or 0. If both <i>pixels</i> and <i>chars</i> are not 0, uses their sum.</param>\n\tpublic void aaaMarginSetWidth(int margin, int pixels, int chars = 0) {\n\t\t_marginDpi ??= new (short, short)[Call(SCI_GETMARGINS) + 2];\n\t\t_marginDpi[margin + 2] = ((short)pixels, (short)chars);\n\t\t_MarginSetWidth(margin, pixels, chars);\n\t}\n\t(short pixels, short chars)[] _marginDpi; //logical pixels\n\t\n\tvoid _MarginWidthsDpiChanged() {\n\t\tif (_marginDpi is { } a)\n\t\t\tfor (int i = a.Length; --i >= 0;) if (a[i] != default) _MarginSetWidth(i - 2, a[i].pixels, a[i].chars);\n\t}\n\t\n\tvoid _MarginSetWidth(int i, int pixels, int chars) {\n\t\tif (pixels != 0) pixels = Dpi.Scale(pixels, _dpi);\n\t\tif (chars != 0) pixels += chars * aaaStyleMeasureStringWidth(STYLE_LINENUMBER, \"8\");\n\t\tif (i == -2) Call(SCI_SETMARGINRIGHT, 0, pixels); else if (i == -1) Call(SCI_SETMARGINLEFT, 0, pixels); else Call(SCI_SETMARGINWIDTHN, i, pixels);\n\t}\n\t\n\t/// <returns>Margin index, or -1 if not in a margin.</returns>\n\tpublic int aaaMarginFromPoint(POINT p, bool screenCoord = false) {\n\t\tif (screenCoord) _w.MapScreenToClient(ref p);\n\t\tif (_w.ClientRect.Contains(p)) {\n\t\t\tfor (int i = 0, n = Call(SCI_GETMARGINS), w = 0; i < n; i++) { w += Call(SCI_GETMARGINWIDTHN, i); if (w >= p.x) return i; }\n\t\t}\n\t\treturn -1;\n\t}\n\t\n\t/// <summary>\n\t/// SCI_GETMARGINWIDTHN.\n\t/// </summary>\n\tpublic (int left, int right) aaaMarginGetX(int margin, bool dpiUnscale = false) {\n\t\tint x = 0;\n\t\tfor (int i = 0; i < margin; i++) x += Call(SCI_GETMARGINWIDTHN, i);\n\t\tvar r = (left: x, right: x + Call(SCI_GETMARGINWIDTHN, margin));\n\t\tif (dpiUnscale) {\n\t\t\tr.left = Dpi.Unscale(r.left, _dpi).ToInt();\n\t\t\tr.right = Dpi.Unscale(r.right, _dpi).ToInt();\n\t\t}\n\t\treturn r;\n\t}\n\t\n\t/// <summary>\n\t/// Initializes folding margin and optionally separator marker.\n\t/// </summary>\n\t/// <param name=\"foldMargin\">Margin index.</param>\n\t/// <param name=\"separatorMarker\">Separator marker index, or -1 if never using seperators in this control.</param>\n\t/// <param name=\"autoFold\">Set SC_AUTOMATICFOLD_CLICK.</param>\n\tpublic void aaaFoldingInit(int foldMargin = 0, int separatorMarker = -1, bool autoFold = false) {\n\t\tCall(SCI_SETMARGINTYPEN, foldMargin, SC_MARGIN_SYMBOL);\n\t\tCall(SCI_SETMARGINMASKN, foldMargin, SC_MASK_FOLDERS);\n\t\tCall(SCI_SETMARGINSENSITIVEN, foldMargin, 1);\n\t\t\n\t\tCall(SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPEN, SC_MARK_BOXMINUS);\n\t\tCall(SCI_MARKERDEFINE, SC_MARKNUM_FOLDER, SC_MARK_BOXPLUS);\n\t\tCall(SCI_MARKERDEFINE, SC_MARKNUM_FOLDERSUB, SC_MARK_VLINE);\n\t\tCall(SCI_MARKERDEFINE, SC_MARKNUM_FOLDERTAIL, SC_MARK_LCORNER);\n\t\tCall(SCI_MARKERDEFINE, SC_MARKNUM_FOLDEREND, SC_MARK_BOXPLUSCONNECTED);\n\t\tCall(SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPENMID, SC_MARK_BOXMINUSCONNECTED);\n\t\tCall(SCI_MARKERDEFINE, SC_MARKNUM_FOLDERMIDTAIL, SC_MARK_TCORNER);\n\t\tfor (int i = 25; i < 32; i++) {\n\t\t\tCall(SCI_MARKERSETFORE, i, 0xffffff);\n\t\t\tCall(SCI_MARKERSETBACK, i, 0x808080);\n\t\t\tCall(SCI_MARKERSETBACKSELECTED, i, i == SC_MARKNUM_FOLDER ? 0xFF : 0x808080);\n\t\t}\n\t\t//Call(SCI_MARKERENABLEHIGHLIGHT, 1); //red [+]\n\t\t\n\t\tint fflags = SC_AUTOMATICFOLD_SHOW //show hidden lines when header line deleted. Also when hidden text modified, and it is not always good.\n\t\t\t\t\t\t\t\t\t| SC_AUTOMATICFOLD_CHANGE; //show hidden lines when header line modified like '#region' -> '//#region'\n\t\tif (autoFold) fflags |= SC_AUTOMATICFOLD_CLICK;\n\t\tCall(SCI_SETAUTOMATICFOLD, fflags);\n\t\tCall(SCI_SETFOLDFLAGS, SC_FOLDFLAG_LINEAFTER_CONTRACTED);\n\t\tCall(SCI_FOLDDISPLAYTEXTSETSTYLE, SC_FOLDDISPLAYTEXT_STANDARD);\n\t\taaaStyleForeColor(STYLE_FOLDDISPLAYTEXT, 0x808080);\n\t\t\n\t\tCall(SCI_SETMARGINCURSORN, foldMargin, SC_CURSORARROW);\n\t\t\n\t\taaaMarginSetWidth(foldMargin, 12);\n\t\t\n\t\t//separator lines below functions, types etc\n\t\tif (separatorMarker >= 0) aaaMarkerDefine(separatorMarker, SC_MARK_UNDERLINE, backColor: 0xa0a0a0);\n\t}\n\t\n\t/// <summary>\n\t/// Adds or updates fold points and optionally separators.\n\t/// </summary>\n\t/// <param name=\"af\">Fold points. If null or empty, just clears old.</param>\n\t/// <param name=\"separatorMarker\">Separator marker index, or -1 if never using seperators in this control. The marker should have an underline style.</param>\n\tpublic void aaaFoldingApply(List<SciFoldPoint> af, int separatorMarker = -1) {\n\t\tint underlinedLine = 0;\n\t\t\n\t\tint[] a = null;\n\t\tif (af != null) {\n\t\t\ta = new int[af.Count];\n\t\t\tfor (int i = 0; i < a.Length; i++) {\n\t\t\t\tvar v = af[i];\n\t\t\t\tint pos8 = aaaPos8(v.pos);\n\t\t\t\ta[i] = pos8 | (v.start ? 0 : unchecked((int)0x80000000));\n\t\t\t\t\n\t\t\t\tif (separatorMarker >= 0 && v.separator != 0) {\n\t\t\t\t\t//add separator below, or above if start\n\t\t\t\t\tif (v.start) { //above\n\t\t\t\t\t\tint k = v.pos - v.separator; if (k <= 0) continue;\n\t\t\t\t\t\tpos8 = aaaPos8(k);\n\t\t\t\t\t}\n\t\t\t\t\tint li = aaaLineFromPos(false, pos8);\n\t\t\t\t\t_DeleteUnderlinedLineMarkers(li);\n\t\t\t\t\t//if(underlinedLine != li) print.it(\"add\", li + 1);\n\t\t\t\t\tif (underlinedLine != li) Call(SCI_MARKERADD, li, separatorMarker);\n\t\t\t\t\telse underlinedLine++;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (separatorMarker >= 0) _DeleteUnderlinedLineMarkers(int.MaxValue);\n\t\t\n\t\tvoid _DeleteUnderlinedLineMarkers(int beforeLine) {\n\t\t\tif ((uint)underlinedLine > beforeLine) return;\n\t\t\tint marker = 1 << separatorMarker;\n\t\t\tfor (; ; underlinedLine++) {\n\t\t\t\tunderlinedLine = Call(SCI_MARKERNEXT, underlinedLine, marker);\n\t\t\t\tif ((uint)underlinedLine >= beforeLine) break;\n\t\t\t\t//print.it(\"delete\", underlinedLine + 1);\n\t\t\t\tdo Call(SCI_MARKERDELETE, underlinedLine, separatorMarker);\n\t\t\t\twhile (0 != (marker & Call(SCI_MARKERGET, underlinedLine)));\n\t\t\t}\n\t\t}\n\t\t\n\t\tunsafe { //we implement folding in Scintilla. Calling many SCI_SETFOLDLEVEL here would be slow.\n\t\t\tfixed (int* ip = a) Sci_SetFoldLevels(AaSciPtr, 0, -1, a.Lenn_(), ip);\n\t\t}\n\t\t//p1.NW('F');\n\t}\n\t\n\t/// <summary>\n\t/// SCI_GETFOLDLEVEL.\n\t/// </summary>\n\t/// <returns>0-based level (never less than 0) and whether it has SC_FOLDLEVELHEADERFLAG.</returns>\n\tpublic (int level, bool isHeader) aaaFoldingLevel(int line) {\n\t\tint r = Call(SCI_GETFOLDLEVEL, line);\n\t\treturn (Math.Max(0, (r & SC_FOLDLEVELNUMBERMASK) - SC_FOLDLEVELBASE), 0 != (r & SC_FOLDLEVELHEADERFLAG));\n\t}\n\t\n\t#endregion\n}\n\n/// <summary>\n/// See <see cref=\"KScintilla.aaaFoldingApply\"/>\n/// </summary>\n/// <param name=\"pos\">A position anywhere in the line.</param>\n/// <param name=\"start\">Start or end.</param>\n/// <param name=\"separator\">If not 0, adds separator. If <i>start</i> true, adds above, at <c>pos-separator</c>; else adds below, and let it be 1.</param>\npublic record struct SciFoldPoint(int pos, bool start, ushort separator = 0);\n"
  },
  {
    "path": "Au.Controls/KScintilla/Sci styles.cs",
    "content": "namespace Au.Controls;\n\nusing static Sci;\n\npublic unsafe partial class KScintilla {\n\t\n\tpublic void aaaStyleFont(int style, string name) {\n\t\taaaSetString(SCI_STYLESETFONT, style, name);\n\t}\n\t\n\t//public string aaaStyleFont(int style)\n\t//{\n\t//\treturn aaaGetString(SCI_STYLEGETFONT, style, 100);\n\t//}\n\t\n\tpublic void aaaStyleFont(int style, string name, double size) {\n\t\taaaStyleFont(style, name);\n\t\taaaStyleFontSize(style, size);\n\t}\n\t\n\t/// <summary>Uses only font name and size. Not style etc.</summary>\n\tpublic void aaaStyleFont(int style, System.Windows.Controls.Control c) {\n\t\taaaStyleFont(style, c.FontFamily.ToString(), c.FontSize.ToInt() * 72 / 96);\n\t}\n\t\n\t/// <summary>Segoe UI, 9.</summary>\n\tpublic void aaaStyleFont(int style) {\n\t\taaaStyleFont(style, \"Segoe UI\", 9);\n\t}\n\t\n\tpublic void aaaStyleFontSize(int style, double value) {\n\t\tCall(SCI_STYLESETSIZEFRACTIONAL, style, (int)(value * 100));\n\t}\n\t\n\t//public int aaaStyleFontSize(int style)\n\t//{\n\t//\treturn Call(SCI_STYLEGETSIZE, style);\n\t//}\n\t\n\tpublic void aaaStyleHidden(int style, bool value) {\n\t\tCall(SCI_STYLESETVISIBLE, style, !value);\n\t}\n\t\n\t//public bool aaaStyleHidden(int style)\n\t//{\n\t//\treturn 0 == Call(SCI_STYLEGETVISIBLE, style);\n\t//}\n\t\n\tpublic void aaaStyleBold(int style, bool value) {\n\t\tCall(SCI_STYLESETBOLD, style, value);\n\t}\n\t\n\tpublic void aaaStyleItalic(int style, bool value) {\n\t\tCall(SCI_STYLESETITALIC, style, value);\n\t}\n\t\n\tpublic void aaaStyleUnderline(int style, bool value) {\n\t\tCall(SCI_STYLESETUNDERLINE, style, value);\n\t}\n\t\n\tpublic void aaaStyleEolFilled(int style, bool value) {\n\t\tCall(SCI_STYLESETEOLFILLED, style, value);\n\t}\n\t\n\tpublic void aaaStyleHotspot(int style, bool value) {\n\t\tCall(SCI_STYLESETHOTSPOT, style, value);\n\t}\n\t\n\tpublic bool aaaStyleHotspot(int style) {\n\t\treturn 0 != Call(SCI_STYLEGETHOTSPOT, style);\n\t}\n\t\n\tpublic void aaaStyleForeColor(int style, ColorInt color) {\n\t\tCall(SCI_STYLESETFORE, style, color.ToBGR());\n\t}\n\t\n\tpublic void aaaStyleBackColor(int style, ColorInt color) {\n\t\tCall(SCI_STYLESETBACK, style, color.ToBGR());\n\t}\n\t\n\t/// <summary>\n\t/// SCI_TEXTWIDTH.\n\t/// </summary>\n\tpublic int aaaStyleMeasureStringWidth(int style, string s) {\n\t\treturn aaaSetString(SCI_TEXTWIDTH, style, s);\n\t}\n\t\n\t/// <summary>\n\t/// Calls SCI_STYLECLEARALL, which sets all styles to be the same as STYLE_DEFAULT.\n\t/// Then also sets some special styles: STYLE_HIDDEN, SC_ELEMENT_HOT_SPOT_ACTIVE.\n\t/// </summary>\n\t/// <param name=\"belowDefault\">Clear only styles 0..STYLE_DEFAULT.</param>\n\tpublic void aaaStyleClearAll(bool belowDefault = false) {\n\t\tif (belowDefault) aaaStyleClearRange(0, STYLE_DEFAULT);\n\t\telse Call(SCI_STYLECLEARALL);\n\t\taaaStyleHidden(STYLE_HIDDEN, true);\n\t\t//aaaSetString(SCI_STYLESETINVISIBLEREPRESENTATION, STYLE_HIDDEN, \"-\"u8); //no\n\t\taaaSetElementColor(SC_ELEMENT_HOT_SPOT_ACTIVE, 0x8000FF);\n\t}\n\t\n\t/// <summary>\n\t/// Calls SCI_STYLECLEARALL(styleFrom8, styleTo8NotIncluding), which sets range of styles to be the same as STYLE_DEFAULT.\n\t/// If styleTo8NotIncluding is 0, clears all starting from styleFrom.\n\t/// </summary>\n\tpublic void aaaStyleClearRange(int styleFrom8, int styleTo8NotIncluding = 0) {\n\t\tCall(SCI_STYLECLEARALL, styleFrom8, styleTo8NotIncluding);\n\t}\n\t\n\t/// <summary>\n\t/// Gets style at position.\n\t/// Uses SCI_GETSTYLEINDEXAT.\n\t/// Returns 0 if pos is invalid.\n\t/// </summary>\n\tpublic int aaaStyleGetAt(int pos8) {\n\t\treturn Call(SCI_GETSTYLEINDEXAT, pos8);\n\t}\n\t\n\t/// <summary>\n\t/// Sets scintilla's \"end-styled position\" = to8 (default int.MaxValue), to avoid SCN_STYLENEEDED notifications.\n\t/// Fast, just sets a field in scintilla.\n\t/// </summary>\n\t/// <remarks>\n\t/// Scintilla sends SCN_STYLENEEDED, unless a lexer is set. In some cases 1 or several, in some cases many, in some cases every 500 ms.\n\t/// </remarks>\n\tpublic void aaaSetStyled(int to8 = int.MaxValue) => Call(SCI_STARTSTYLING, to8);\n\t\n\t/// <summary>\n\t/// SCI_SETELEMENTCOLOUR.\n\t/// </summary>\n\t/// <param name=\"element\">SC_ELEMENT_.</param>\n\t/// <param name=\"color\">Color. Can be with alpha. If null, calls SCI_RESETELEMENTCOLOUR.</param>\n\tpublic void aaaSetElementColor(int element, ColorInt? color) {\n\t\tif (color.HasValue) Call(SCI_SETELEMENTCOLOUR, element, color.Value.ToBGR(zeroAlpha: false));\n\t\telse Call(SCI_RESETELEMENTCOLOUR, element);\n\t}\n\t\n\t/// <summary>\n\t/// SCI_GETELEMENTCOLOUR.\n\t/// </summary>\n\t/// <param name=\"element\">SC_ELEMENT_.</param>\n\tpublic ColorInt aaaGetElementColor(int element) {\n\t\treturn ColorInt.FromBGR(Call(SCI_GETELEMENTCOLOUR, element), false);\n\t}\n\t\n\t/// <summary>\n\t/// SCI_STARTSTYLING and SCI_SETSTYLINGEX.\n\t/// </summary>\n\tpublic void aaaSetStyling(int start8, RByte styles) {\n\t\tCall(SCI_STARTSTYLING, start8);\n\t\tunsafe { fixed (byte* bp = styles) Call(SCI_SETSTYLINGEX, styles.Length, bp); }\n\t}\n\t\n\t/// <summary>\n\t/// If <i>text</i> is ASCII, returns <i>styles</i>. Else returns new array where style bytes are converted from UTF-16 offsets to UTF-8 offsets.\n\t/// </summary>\n\t/// <param name=\"styles\">Style bytes. The offsets are UTF-16. The function does not modify it.</param>\n\t/// <param name=\"text\">Text to be styled. Must be same length as <i>styles</i>.</param>\n\t/// <returns></returns>\n\tpublic static Span<byte> aaaConvertStylingBytesToUtf8(Span<byte> styles, RStr text) {\n\t\tDebug.Assert(styles.Length == text.Length);\n\t\tif (!text.IsAscii()) {\n\t\t\tvar u = new byte[Encoding.UTF8.GetByteCount(text)];\n\t\t\tint i = 0, j = 0;\n\t\t\tforeach (var r in text.EnumerateRunes()) {\n\t\t\t\tvar v = styles[i++];\n\t\t\t\tif (!r.IsBmp) i++;\n\t\t\t\tfor (int n8 = r.Utf8SequenceLength; n8-- > 0;) u[j++] = v;\n\t\t\t}\n\t\t\tstyles = u;\n\t\t}\n\t\treturn styles;\n\t}\n}\n"
  },
  {
    "path": "Au.Controls/KScintilla/Sci text.cs",
    "content": "//Functions to work with Scintilla control text, etc.\n\nnamespace Au.Controls;\n\nusing static Sci;\n\npublic unsafe partial class KScintilla {\n\t#region low level\n\t\n\t/// <summary>\n\t/// Calls a Scintilla message that sets a string which is passed using <i>lParam</i>.\n\t/// The string can be null if the Scintilla message allows it.\n\t/// If the message changes control text, this function does not work if the control is read-only. At first make non-readonly temporarily.\n\t/// Don't call this function from another thread.\n\t/// </summary>\n\tpublic int aaaSetString(int sciMessage, nint wParam, RStr lParam) {\n\t\tfixed (byte* s = _ToUtf8(lParam, out var len)) {\n\t\t\treturn Call(sciMessage, wParam, s);\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Calls a Scintilla message that sets a string which is passed using <i>lParam</i> (UTF-8 string) and <i>wParam</i> (UTF-8 string length).\n\t/// The string can be null if the Scintilla message allows it.\n\t/// If the message changes control text, this function does not work if the control is read-only. At first make non-readonly temporarily.\n\t/// Don't call this function from another thread.\n\t/// </summary>\n\tpublic int aaaSetString(int sciMessage, RStr lParam) {\n\t\tfixed (byte* s = _ToUtf8(lParam, out var len)) {\n\t\t\treturn Call(sciMessage, len, s);\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Calls a Scintilla message that sets a string which is passed using <i>wParam</i>.\n\t/// The string can be null if the Scintilla message allows it.\n\t/// If the message changes control text, this function does not work if the control is read-only. At first make non-readonly temporarily.\n\t/// Don't call this function from another thread.\n\t/// </summary>\n\tpublic int aaaSetString(int sciMessage, RStr wParam, nint lParam) {\n\t\tfixed (byte* s = _ToUtf8(wParam)) {\n\t\t\treturn Call(sciMessage, (nint)s, lParam);\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Calls a Scintilla message that sets a string which is passed using <i>lParam</i>.\n\t/// With many messages the <i>lParam</i> string must be '\\0'-terminated, eg UTF-8 string literal like <c>\"example\"u8</c>.\n\t/// The string can be null if the Scintilla message allows it.\n\t/// If the message changes control text, this function does not work if the control is read-only. At first make non-readonly temporarily.\n\t/// </summary>\n\tpublic int aaaSetString(int sciMessage, nint wParam, RByte lParam) {\n\t\tfixed (byte* p = lParam) {\n\t\t\treturn Call(sciMessage, wParam, p);\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Calls a Scintilla message and passes two strings using <i>wParam</i> and <i>lParam</i>.\n\t/// <i>wParamlParam</i> must be like \"WPARAM\\0LPARAM\". Asserts if no '\\0'.\n\t/// If the message changes control text, this function does not work if the control is read-only. At first make non-readonly temporarily.\n\t/// Don't call this function from another thread.\n\t/// </summary>\n\tpublic int aaaSetStringString(int sciMessage, RStr wParamlParam) {\n\t\tfixed (byte* s = _ToUtf8(wParamlParam, out var len)) {\n\t\t\tint i = Ptr_.Length(s);\n\t\t\tDebug.Assert(i < len);\n\t\t\treturn Call(sciMessage, (nint)s, s + i + 1);\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Calls a Scintilla message that gets a string when length is known.\n\t/// Always uses <i>utf8Length</i> bytes of the result (does not find length).\n\t/// Can get binary string (with '\\0' characters).\n\t/// </summary>\n\t/// <param name=\"sciMessage\"></param>\n\t/// <param name=\"wParam\"></param>\n\t/// <param name=\"utf8Length\">\n\t/// Known length (bytes) of the result UTF-8 string, without the terminating '\\0' character.\n\t/// If 0, returns \"\" and does not call the message.\n\t/// </param>\n\tpublic string aaaGetStringOfLength(int sciMessage, nint wParam, int utf8Length)\n\t\t=> _GetString(sciMessage, wParam, utf8Length, false);\n\t\n\t/// <summary>\n\t/// Calls a Scintilla message that gets a string. See <see cref=\"aaaGetStringOfLength\"/>.\n\t/// To get buffer size, at first calls <i>sciMessage</i> with <i>lParam</i>=0 (null buffer).\n\t/// Can get binary string (with '\\0' characters).\n\t/// Don't call this function from another thread.\n\t/// </summary>\n\t/// <param name=\"sciMessage\"></param>\n\t/// <param name=\"wParam\"></param>\n\tpublic string aaaGetStringGetLength(int sciMessage, nint wParam)\n\t\t=> _GetString(sciMessage, wParam, Call(sciMessage, wParam), false);\n\t\n\t/// <summary>\n\t/// Calls a Scintilla message that gets a '\\0'-terminated string.\n\t/// Cannot get binary string (with '\\0' characters).\n\t/// Don't call this function from another thread.\n\t/// </summary>\n\t/// <param name=\"sciMessage\"></param>\n\t/// <param name=\"wParam\"></param>\n\t/// <param name=\"bufferSize\">\n\t/// How much UTF-8 bytes to allocate for Scintilla to store the text.\n\t/// Can be either known or max expected text length, without the terminating '\\0' character. The function will find length of the retrieved string (finds '\\0').\n\t/// If 0, returns \"\" and does not call the message.\n\t/// </param>\n\tpublic string aaaGetString0Terminated(int sciMessage, nint wParam, int bufferSize)\n\t\t=> _GetString(sciMessage, wParam, bufferSize, true);\n\t\n\t[SkipLocalsInit]\n\tstring _GetString(int sciMessage, nint wParam, int len, bool findLength) {\n\t\tif (len == 0) return \"\";\n\t\tusing FastBuffer<byte> b = new(len + 1);\n\t\tb[len] = 0;\n\t\tCall(sciMessage, wParam, b.p);\n\t\tDebug.Assert(b[len] == 0);\n\t\tif (findLength) len = b.FindByteStringLength();\n\t\treturn Encoding.UTF8.GetString(b, len);\n\t}\n\t\n\tstatic string _FromUtf8(byte* b) => Convert2.Utf8Decode(b);\n\t\n\tstatic byte[] _ToUtf8(RStr s) => Convert2.Utf8Encode(s);\n\t\n\tstatic byte[] _ToUtf8(RStr s, out int utf8Length) {\n\t\tvar r = Convert2.Utf8Encode(s);\n\t\tutf8Length = r.Length - 1;\n\t\treturn r;\n\t}\n\t\n\t/// <summary>\n\t/// Optimized 'get text' function.\n\t/// </summary>\n\t/// <param name=\"start8\">Start index, UTF-8.</param>\n\t/// <param name=\"end8\">End index, UTF-8.</param>\n\t/// <remarks>\n\t/// Does not create an intermediate byte[].\n\t/// Gets big text 5 times faster than aaaGetStringOfLength. Tested with text 31K length, 1K lines.\n\t/// </remarks>\n\tstring _RangeText(int start8, int end8) {\n\t\tDebug.Assert(end8 >= start8);\n\t\tDebug.Assert((uint)end8 <= aaaLen8);\n\t\tif (end8 == start8) return \"\";\n\t\tint gap = Sci_Range(AaSciPtr, start8, end8, out var p1, out var p2);\n\t\tif (p2 != null) {\n\t\t\tint n1 = gap - start8, n2 = end8 - gap;\n\t\t\tint len1 = Encoding.UTF8.GetCharCount(p1, n1);\n\t\t\tint len2 = Encoding.UTF8.GetCharCount(p2, n2);\n\t\t\tnint k1 = (nint)p1, k2 = (nint)p2;\n\t\t\treturn string.Create(len1 + len2, (k1, k2, n1, n2), static (span, a) => {\n\t\t\t\tint len1 = Encoding.UTF8.GetChars(new RByte((byte*)a.k1, a.n1), span);\n\t\t\t\tEncoding.UTF8.GetChars(new RByte((byte*)a.k2, a.n2), span.Slice(len1));\n\t\t\t});\n\t\t} else {\n\t\t\tint n1 = end8 - start8;\n\t\t\tint len1 = Encoding.UTF8.GetCharCount(p1, n1);\n\t\t\tnint k1 = (nint)p1;\n\t\t\treturn string.Create(len1, (k1, n1), static (span, a) => {\n\t\t\t\tEncoding.UTF8.GetChars(new RByte((byte*)a.k1, a.n1), span);\n\t\t\t});\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// If <i>utf16</i>, converts <i>from</i> and <i>to</i> from characters to UTF-8 bytes.\n\t/// </summary>\n\t/// <param name=\"utf16\">Input values are UTF-16.</param>\n\t/// <param name=\"from\"></param>\n\t/// <param name=\"to\">If -1, uses <see cref=\"aaaLen8\"/>.</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Invalid argument, eg greater than text length or <i>to</i> less than <i>from</i>.</exception>\n\tpublic void aaaNormalizeRange(bool utf16, ref int from, ref int to) {\n\t\tif (from < 0 || (to < from && to != -1)) throw new ArgumentOutOfRangeException();\n\t\tif (utf16) from = aaaPos8(from);\n\t\tif (to < 0) to = aaaLen8; else if (utf16) to = aaaPos8(to);\n\t}\n\t\n\t/// <summary>\n\t/// If <i>utf16</i>, converts <i>from</i> and <i>to</i> from characters to UTF-8 bytes.\n\t/// </summary>\n\t/// <param name=\"utf16\">Input values are UTF-16.</param>\n\t/// <param name=\"r\">Range. Can be spacified from start or/and from end.</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Invalid argument, eg <i>to</i> less than <i>from</i>.</exception>\n\tpublic (int from, int to) aaaNormalizeRange(bool utf16, Range r) {\n\t\tint from, to;\n\t\tif (r.Start.IsFromEnd || r.End.IsFromEnd) {\n\t\t\t(from, to) = r.GetStartEnd(utf16 ? aaaLen16 : aaaLen8);\n\t\t\tif (utf16) {\n\t\t\t\tfrom = aaaPos8(from);\n\t\t\t\tto = aaaPos8(to);\n\t\t\t}\n\t\t} else {\n\t\t\tfrom = r.Start.Value;\n\t\t\tto = r.End.Value;\n\t\t\taaaNormalizeRange(utf16, ref from, ref to);\n\t\t}\n\t\treturn (from, to);\n\t}\n\t\n\t/// <summary>\n\t/// Same as <see cref=\"aaaNormalizeRange(bool, ref int, ref int)\"/>, but can be <i>to</i> less than <i>from</i>. If so, returns true.\n\t/// </summary>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Invalid argument, eg greater than text length.</exception>\n\tpublic bool aaaNormalizeRangeCanBeReverse(bool utf16, ref int from, ref int to, bool swapFromTo) {\n\t\tbool reverse = to >= 0 && to < from;\n\t\tif (reverse) Math2.Swap(ref from, ref to);\n\t\taaaNormalizeRange(utf16, ref from, ref to);\n\t\tif (reverse && !swapFromTo) Math2.Swap(ref from, ref to);\n\t\treturn reverse;\n\t}\n\t\n\t/// <summary>\n\t/// => utf16 ? aaaPos8(pos) : pos;\n\t/// </summary>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Negative.</exception>\n\tint _ParamPos(bool utf16, int pos) => pos >= 0 ? (utf16 ? aaaPos8(pos) : pos) : throw new ArgumentOutOfRangeException();\n\t\n\t/// <summary>\n\t/// => utf16 ? aaaPos16(pos) : pos;\n\t/// </summary>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Negative.</exception>\n\tint _ReturnPos(bool utf16, int pos) => pos >= 0 ? (utf16 ? aaaPos16(pos) : pos) : throw new ArgumentOutOfRangeException();\n\t\n\t/// <summary>\n\t/// pos >= 0 ? (utf16 ? aaaPos16(pos) : pos) : pos;\n\t/// </summary>\n\tint _ReturnPosCanBeNegative(bool utf16, int pos) => pos >= 0 ? (utf16 ? aaaPos16(pos) : pos) : pos;\n\t\n\t/// <summary>\n\t/// => line;\n\t/// </summary>\n\t/// <exception cref=\"ArgumentOutOfRangeException\">Negative.</exception>\n\tint _ParamLine(int line) => line >= 0 ? line : throw new ArgumentOutOfRangeException();\n\t\n\tstruct _NoReadonly : IDisposable {\n\t\tKScintilla _t;\n\t\tbool _ro;\n\t\t\n\t\tpublic _NoReadonly(KScintilla t) {\n\t\t\t_t = t;\n\t\t\t_ro = _t.AaInitReadOnlyAlways || _t.aaaIsReadonly;\n\t\t\tif (_ro) _t.Call(SCI_SETREADONLY, 0);\n\t\t}\n\t\t\n\t\tpublic void Dispose() {\n\t\t\tif (_ro) _t.Call(SCI_SETREADONLY, 1);\n\t\t}\n\t}\n\t\n\tstruct _NoUndoNotif : IDisposable {\n\t\tKScintilla _t;\n\t\tbool _noUndo, _noNotif;\n\t\t\n\t\tpublic _NoUndoNotif(KScintilla t, SciSetTextFlags flags) {\n\t\t\t_t = t;\n\t\t\t_noUndo = flags.Has(SciSetTextFlags.NoUndo) && 0 != _t.Call(SCI_GETUNDOCOLLECTION);\n\t\t\t_noNotif = flags.Has(SciSetTextFlags.NoNotify) && !_t.AaDisableModifiedNotifications;\n\t\t\tif (_noNotif) _t.AaDisableModifiedNotifications = true;\n\t\t\tif (_noUndo) _t.Call(SCI_SETUNDOCOLLECTION);\n\t\t}\n\t\t\n\t\tpublic void Dispose() {\n\t\t\tif (_noUndo) {\n\t\t\t\t_t.Call(SCI_EMPTYUNDOBUFFER);\n\t\t\t\t_t.Call(SCI_SETUNDOCOLLECTION, 1);\n\t\t\t}\n\t\t\tif (_noNotif) _t.AaDisableModifiedNotifications = false;\n\t\t}\n\t}\n\t\n\t#endregion\n\t\n\t#region set/get/clear all text, append text\n\t\n\t/// <summary>\n\t/// Removes all text (SCI_CLEARALL).\n\t/// </summary>\n\t/// <param name=\"flags\"></param>\n\tpublic void aaaClearText(SciSetTextFlags flags = 0) {\n\t\tif (_w.Is0) return;\n\t\tusing (new _NoUndoNotif(this, flags))\n\t\tusing (new _NoReadonly(this))\n\t\t\tCall(SCI_CLEARALL);\n\t}\n\t\n\t/// <summary>\n\t/// Replaces all text.\n\t/// Parses tags if need.\n\t/// </summary>\n\t/// <param name=\"s\">Text.</param>\n\t/// <param name=\"flags\"></param>\n\t/// <param name=\"ignoreTags\">Don't parse tags, regardless of <b>AaInitTagsStyle</b>.</param>\n\tpublic void aaaSetText(string s, SciSetTextFlags flags = 0, bool ignoreTags = false) {\n\t\tusing (new _NoUndoNotif(this, flags)) {\n\t\t\tif (!ignoreTags && _CanParseTags(s)) {\n\t\t\t\taaaClearText();\n\t\t\t\tAaTags.AddText(s, false, AaInitTagsStyle == AaTagsStyle.AutoWithPrefix);\n\t\t\t} else {\n\t\t\t\tusing (new _NoReadonly(this))\n\t\t\t\t\taaaSetString(SCI_SETTEXT, 0, s ?? \"\");\n\t\t\t}\n\t\t}\n\t}\n\t\n\tbool _CanParseTags(string s) {\n\t\tif (s.NE()) return false;\n\t\treturn AaInitTagsStyle switch {\n\t\t\tAaTagsStyle.AutoAlways => s.Contains('<'),\n\t\t\tAaTagsStyle.AutoWithPrefix => s.Starts(\"<>\"),\n\t\t\t_ => false,\n\t\t};\n\t}\n\t\n\t/// <summary>\n\t/// Appends text and optionally \"\\r\\n\".\n\t/// Parses tags if need. Optionally scrolls and moves current position to the end (SCI_GOTOPOS).\n\t/// </summary>\n\t/// <param name=\"s\"></param>\n\t/// <param name=\"andRN\">Also append \"\\r\\n\". Ignores (uses true) if parses tags.</param>\n\t/// <param name=\"scroll\">Move current position and scroll to the end.</param>\n\t/// <param name=\"ignoreTags\">Don't parse tags, regardless of <b>AaInitTagsStyle</b>.</param>\n\tpublic void aaaAppendText(string s, bool andRN, bool scroll, bool ignoreTags = false) {\n\t\ts ??= \"\";\n\t\tif (!ignoreTags && _CanParseTags(s)) {\n\t\t\tAaTags.AddText(s, true, AaInitTagsStyle == AaTagsStyle.AutoWithPrefix, scroll);\n\t\t} else {\n\t\t\tvar a = Convert2.Utf8Encode(s, andRN ? \"\\r\\n\" : \"\");\n\t\t\tusing (new _NoReadonly(this))\n\t\t\t\tfixed (byte* b = a) Call(SCI_APPENDTEXT, a.Length, b);\n\t\t\t\n\t\t\tif (scroll) Call(SCI_GOTOPOS, aaaLen8);\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// SCI_APPENDTEXT.\n\t/// </summary>\n\t/// <param name=\"s\"></param>\n\t/// <param name=\"scroll\">Move current position and scroll to the end.</param>\n\tpublic void aaaAppendText8(RByte s, bool scroll) {\n\t\tusing (new _NoReadonly(this)) {\n\t\t\tfixed (byte* p = s) Call(SCI_APPENDTEXT, s.Length, p);\n\t\t}\n\t\t\n\t\tif (scroll) Call(SCI_GOTOPOS, aaaLen8);\n\t}\n\t\n\t/// <summary>\n\t/// Sets or appends UTF-8 text of specified length. Does not parse tags.\n\t/// If <i>scroll</i>, moves current position and scrolls to the end (SCI_GOTOPOS).\n\t/// </summary>\n\tinternal void aaaAddText8_(bool append, bool scroll, byte* s, int lenToAppend) {\n\t\tusing (new _NoReadonly(this))\n\t\t\tif (append) Call(SCI_APPENDTEXT, lenToAppend, s);\n\t\t\telse Call(SCI_SETTEXT, 0, s);\n\t\t\n\t\tif (scroll) Call(SCI_GOTOPOS, aaaLen8);\n\t}\n\t\n\t//not used now\n\t///// <summary>\n\t///// Sets or appends styled UTF-8 text of specified length.\n\t///// Does not append newline (s should contain it). Does not parse tags. Moves current position and scrolls to the end.\n\t///// Uses SCI_ADDSTYLEDTEXT. Caller does not have to move cursor to the end.\n\t///// lenToAppend is length in bytes, not in cells.\n\t///// </summary>\n\t//internal void aaaAddStyledText_(bool append, byte* s, int lenBytes)\n\t//{\n\t//\tif(append) Call(SCI_SETEMPTYSELECTION, TextLengthBytes);\n\t\n\t//\tusing(new _NoReadonly(this))\n\t//\tif(!append) Call(SCI_SETTEXT);\n\t//\tCall(SCI_ADDSTYLEDTEXT, lenBytes, s);\n\t\n\t//\tif(append) Call(SCI_GOTOPOS, TextLengthBytes);\n\t//}\n\t\n\t#endregion\n\t\n\t/// <summary>\n\t/// Gets (SCI_GETCURRENTPOS) or sets (SCI_SETEMPTYSELECTION) current caret position in UTF-8 bytes.\n\t/// The <c>set</c> function makes empty selection; does not scroll and does not make visible like aaaGoToPos.\n\t/// </summary>\n\tpublic int aaaCurrentPos8 { get => Call(SCI_GETCURRENTPOS); set => Call(SCI_SETEMPTYSELECTION, value); }\n\t\n\t/// <summary>\n\t/// Gets (SCI_GETCURRENTPOS) or sets (SCI_SETEMPTYSELECTION) current caret position in UTF-16 chars.\n\t/// The <c>set</c> function makes empty selection; does not scroll and does not make visible like aaaGoToPos.\n\t/// </summary>\n\tpublic int aaaCurrentPos16 { get => aaaPos16(aaaCurrentPos8); set => Call(SCI_SETEMPTYSELECTION, aaaPos8(value)); }\n\t\n\t/// <summary>\n\t/// SCI_GETSELECTIONSTART UTF-8.\n\t/// </summary>\n\tpublic int aaaSelectionStart8 => Call(SCI_GETSELECTIONSTART);\n\t\n\t/// <summary>\n\t/// SCI_GETSELECTIONSTART UTF-16.\n\t/// </summary>\n\tpublic int aaaSelectionStart16 => aaaPos16(aaaSelectionStart8);\n\t\n\t/// <summary>\n\t/// SCI_GETSELECTIONEND UTF-8.\n\t/// Always greater or equal than SelectionStart8.\n\t/// </summary>\n\tpublic int aaaSelectionEnd8 => Call(SCI_GETSELECTIONEND);\n\t\n\t/// <summary>\n\t/// SCI_GETSELECTIONEND UTF-16.\n\t/// Always greater or equal than SelectionStart16.\n\t/// </summary>\n\tpublic int aaaSelectionEnd16 => aaaPos16(aaaSelectionEnd8);\n\t\n\t/// <summary>\n\t/// <c>utf16 ? (aaaSelectionStart16, aaaSelectionEnd16) : (aaaSelectionStart8, aaaSelectionEnd8)</c>\n\t/// </summary>\n\tpublic (int start, int end) aaaSelection(bool utf16)\n\t\t=> utf16 ? (aaaSelectionStart16, aaaSelectionEnd16) : (aaaSelectionStart8, aaaSelectionEnd8);\n\t\n\t/// <summary>\n\t/// true if !SCI_GETSELECTIONEMPTY.\n\t/// </summary>\n\tpublic bool aaaHasSelection => 0 == Call(SCI_GETSELECTIONEMPTY);\n\t\n\t/// <summary>\n\t/// Gets line index from character position.\n\t/// </summary>\n\t/// <param name=\"utf16\"></param>\n\t/// <param name=\"pos\">A position in document text. Returns the last line if too big.</param>\n\tpublic int aaaLineFromPos(bool utf16, int pos)\n\t\t=> Call(SCI_LINEFROMPOSITION, _ParamPos(utf16, pos));\n\t\n\t/// <summary>\n\t/// Gets line index at <see cref=\"aaaSelectionStart8\"/>.\n\t/// </summary>\n\tpublic int aaaLineFromPos()\n\t\t=> Call(SCI_LINEFROMPOSITION, aaaSelectionStart8);\n\t\n\t/// <summary>\n\t/// Gets line start position from line index.\n\t/// </summary>\n\t/// <param name=\"utf16\">Return UTF-16.</param>\n\t/// <param name=\"line\">0-based line index. Returns text length if too big.</param>\n\tpublic int aaaLineStart(bool utf16, int line) => _ReturnPos(utf16, _LineStart(line));\n\t\n\tint _LineStart(int line) {\n\t\tif (line < 0) throw new ArgumentOutOfRangeException();\n\t\tint R = Call(SCI_POSITIONFROMLINE, _ParamLine(line));\n\t\treturn R >= 0 ? R : aaaLen8;\n\t\t//If line < 0, Scintilla returns line start from selection start.\n\t\t//If line > number of lines, Scintilla returns -1.\n\t}\n\t\n\t/// <summary>\n\t/// Gets line end position from line index.\n\t/// </summary>\n\t/// <param name=\"utf16\">Return UTF-16.</param>\n\t/// <param name=\"line\">0-based line index. Returns text length if too big.</param>\n\t/// <param name=\"withRN\">Include \\r\\n.</param>\n\tpublic int aaaLineEnd(bool utf16, int line, bool withRN = false) {\n\t\tline = _ParamLine(line);\n\t\treturn _ReturnPos(utf16, withRN ? _LineStart(line + 1) : Call(SCI_GETLINEENDPOSITION, line));\n\t}\n\t\n\t/// <summary>\n\t/// Gets line start position from any position.\n\t/// </summary>\n\t/// <param name=\"utf16\">pos is UTF-16. Return UTF-16.</param>\n\t/// <param name=\"pos\">A position in document text. Returns text length if too big.</param>\n\tpublic int aaaLineStartFromPos(bool utf16, int pos)\n\t\t=> aaaLineStart(utf16, aaaLineFromPos(utf16, pos));\n\t\n\t/// <summary>\n\t/// Gets line start position from any position and gets line index.\n\t/// Returns start position.\n\t/// </summary>\n\t/// <param name=\"utf16\">pos is UTF-16. Return UTF-16.</param>\n\t/// <param name=\"pos\">A position in document text. Returns text length if too big.</param>\n\t/// <param name=\"line\">Receives line index.</param>\n\tpublic int aaaLineStartFromPos(bool utf16, int pos, out int line)\n\t\t=> aaaLineStart(utf16, line = aaaLineFromPos(utf16, pos));\n\t\n\t/// <summary>\n\t/// Gets line end position from any position.\n\t/// </summary>\n\t/// <param name=\"utf16\">pos is UTF-16. Return UTF-16.</param>\n\t/// <param name=\"pos\">A position in document text. Returns text length if too big.</param>\n\t/// <param name=\"withRN\">Include \\r\\n.</param>\n\t/// <param name=\"lineStartIsLineEnd\">If pos is at a line start (0 or after '\\n' character), return pos.</param>\n\tpublic int aaaLineEndFromPos(bool utf16, int pos, bool withRN = false, bool lineStartIsLineEnd = false) {\n\t\tint pos0 = pos;\n\t\tpos = _ParamPos(utf16, pos);\n\t\tif (lineStartIsLineEnd) {\n\t\t\tif (pos == 0 || aaaCharAt8(pos - 1) == '\\n') return pos0;\n\t\t}\n\t\treturn aaaLineEnd(utf16, aaaLineFromPos(false, pos), withRN);\n\t}\n\t\n\t/// <summary>\n\t/// Gets line index, start and end positions from position.\n\t/// </summary>\n\t/// <param name=\"utf16\">pos is UTF-16. Return UTF-16.</param>\n\t/// <param name=\"pos\">A position in document text. Uses the last line if too big.</param>\n\t/// <param name=\"withRN\">Include \\r\\n.</param>\n\t/// <param name=\"utf16Return\">If not null, overrides <i>utf16</i> for return values.</param>\n\tpublic (int line, int start, int end) aaaLineStartEndFromPos(bool utf16, int pos, bool withRN = false, bool? utf16Return = null) {\n\t\tint startPos = aaaLineStartFromPos(false, _ParamPos(utf16, pos), out int line);\n\t\tint endPos = aaaLineEnd(false, line, withRN);\n\t\tutf16 = utf16Return ?? utf16;\n\t\treturn (line, _ReturnPos(utf16, startPos), _ReturnPos(utf16, endPos));\n\t}\n\t\n\t/// <summary>\n\t/// Gets line text.\n\t/// </summary>\n\t/// <param name=\"line\">0-based line index. If invalid, returns \"\".</param>\n\t/// <param name=\"withRN\">Include \\r\\n.</param>\n\tpublic string aaaLineText(int line, bool withRN = false) => _RangeText(aaaLineStart(false, line), aaaLineEnd(false, line, withRN));\n\t\n\t/// <summary>\n\t/// Gets line height.\n\t/// Currently all lines are of the same height.\n\t/// </summary>\n\tpublic int aaaLineHeight() => Call(SCI_TEXTHEIGHT, 0);\n\t\n\t/// <summary>\n\t/// Gets the number of lines.\n\t/// </summary>\n\tpublic int aaaLineCount => Call(SCI_GETLINECOUNT);\n\t\n\t/// <summary>\n\t/// Gets the number of tabs + spaces/4 at the start of the line that contains the specified position.\n\t/// </summary>\n\t/// <param name=\"utf16\"></param>\n\t/// <param name=\"pos\">A position in document text.</param>\n\t/// <param name=\"extraSpaces\">Receives the number of extra spaces, 0 to 3.</param>\n\tpublic int aaaLineIndentFromPos(bool utf16, int pos, out int extraSpaces) {\n\t\tint line = aaaLineFromPos(utf16, pos);\n\t\tint i = Call(SCI_GETLINEINDENTATION, line), r = i / 4;\n\t\textraSpaces = i - r * 4;\n\t\treturn r;\n\t}\n\t\n\t/// <summary>\n\t/// Gets the number of tabs + spaces/4 at the start of the line that contains the specified position.\n\t/// </summary>\n\t/// <param name=\"utf16\"></param>\n\t/// <param name=\"pos\">A position in document text.</param>\n\tpublic int aaaLineIndentFromPos(bool utf16, int pos) => aaaLineIndentFromPos(utf16, pos, out _);\n\t\n\t/// <summary>\n\t/// Gets position from point.\n\t/// </summary>\n\t/// <param name=\"utf16\">Return UTF-16.</param>\n\t/// <param name=\"p\">Point in client area.</param>\n\t/// <param name=\"minusOneIfFar\">Return -1 if p is not in text characters.</param>\n\tpublic int aaaPosFromXY(bool utf16, POINT p, bool minusOneIfFar)\n\t\t=> _ReturnPosCanBeNegative(utf16, Call(minusOneIfFar ? SCI_POSITIONFROMPOINTCLOSE : SCI_POSITIONFROMPOINT, p.x, p.y));\n\t\n\t/// <summary>\n\t/// Gets annotation text of line.\n\t/// Returns \"\" if the line does not contain annotation or is invalid line index.\n\t/// </summary>\n\tpublic string aaaAnnotationText(int line) => AaImages?.AnnotationText_(line) ?? aaaAnnotationText_(line);\n\t\n\t/// <summary>\n\t/// Gets raw annotation text which can contain image info.\n\t/// aaaAnnotationText gets text without image info.\n\t/// Returns \"\" if the line does not contain annotation or is invalid line index.\n\t/// </summary>\n\tpublic string aaaAnnotationText_(int line) => aaaGetStringGetLength(SCI_ANNOTATIONGETTEXT, line);\n\t\n\t/// <summary>\n\t/// Sets annotation text of line.\n\t/// Does nothing if invalid line index.\n\t/// If s is null or \"\", removes annotation.\n\t/// Preserves existing image info.\n\t/// </summary>\n\tpublic void aaaAnnotationText(int line, string s, bool eol = false) {\n\t\tif (eol) aaaAnnotationText_(line, s, eol);\n\t\telse if (AaImages != null) AaImages.AnnotationText_(line, s);\n\t\telse aaaAnnotationText_(line, s);\n\t}\n\t\n\t/// <summary>\n\t/// Sets raw annotation text which can contain image info.\n\t/// If s is null or \"\", removes annotation.\n\t/// </summary>\n\tinternal void aaaAnnotationText_(int line, string s, bool eol = false) {\n\t\tif (s.NE()) s = null;\n\t\taaaSetString(eol ? SCI_EOLANNOTATIONSETTEXT : SCI_ANNOTATIONSETTEXT, line, s);\n\t}\n\t\n\t/// <summary>\n\t/// Moves <i>from</i> to the start of its line, and <i>to</i> to the end of its line.\n\t/// Does not change <i>to</i> if it is at a line start.\n\t/// </summary>\n\t/// <param name=\"utf16\"></param>\n\t/// <param name=\"from\">Start index.</param>\n\t/// <param name=\"to\">End index.</param>\n\t/// <param name=\"withRN\">Include \"\\r\\n\".</param>\n\tpublic void aaaRangeToFullLines(bool utf16, ref int from, ref int to, bool withRN = false) {\n\t\tDebug.Assert(from <= to);\n\t\tfrom = _ReturnPos(utf16, aaaLineStartFromPos(utf16, from));\n\t\tto = _ReturnPos(utf16, aaaLineEndFromPos(utf16, to, withRN, true));\n\t}\n\t\n\t/// <summary>\n\t/// SCI_INSERTTEXT.\n\t/// </summary>\n\t/// <param name=\"utf16\"></param>\n\t/// <param name=\"pos\">Start index. Cannot be negative.</param>\n\t/// <param name=\"s\">Text to insert. Can be null.</param>\n\t/// <param name=\"addUndoPointBefore\">Call <see cref=\"aaaAddUndoPoint\"/> before.</param>\n\t/// <param name=\"addUndoPointAfter\">Call <see cref=\"aaaAddUndoPoint\"/> after.</param>\n\t/// <param name=\"restoreFolding\">If <i>pos</i> is hidden because of folding, finally collapse its folding again. See <see cref=\"aaaFoldingRestorer\"/>.</param>\n\t/// <remarks>\n\t/// Does not parse tags.\n\t/// Does not change current selection, unless <i>pos</i> is in it; for it use <see cref=\"aaaReplaceSel\"/> or <see cref=\"aaaReplaceRange\"/>.\n\t/// </remarks>\n\tpublic void aaaInsertText(bool utf16, int pos, string s, bool addUndoPointBefore = false, bool addUndoPointAfter = false, bool restoreFolding = false) {\n\t\tif (addUndoPointBefore) aaaAddUndoPoint();\n\t\tusing (new _NoReadonly(this))\n\t\tusing (new aaaFoldingRestorer(restoreFolding ? this : null, pos))\n\t\t\taaaSetString(SCI_INSERTTEXT, _ParamPos(utf16, pos), s ?? \"\");\n\t\tif (addUndoPointAfter) aaaAddUndoPoint();\n\t}\n\t\n\t/// <summary>\n\t/// If ctor detects that the line from <i>pos</i> is hidden because of folding, <b>Dispose</b> collapses its folding again.\n\t/// Use when modifying text to prevent unfolding.\n\t/// </summary>\n\tpublic struct aaaFoldingRestorer : IDisposable {\n\t\tKScintilla _sci;\n\t\tint _foldLine;\n\t\t//tested: temp setting SCI_SETAUTOMATICFOLD does not work. If restoring async, does not expand, but draws incorrectly.\n\t\t\n\t\t/// <param name=\"sci\">Can be null, then does nothing.</param>\n\t\t/// <param name=\"pos\"></param>\n\t\tpublic aaaFoldingRestorer(KScintilla sci, int pos) {\n\t\t\t_sci = sci;\n\t\t\t_foldLine = -1;\n\t\t\tif (sci != null) {\n\t\t\t\tint line = sci.aaaLineFromPos(true, pos);\n\t\t\t\tif (0 == sci.Call(SCI_GETLINEVISIBLE, line)) _foldLine = sci.Call(SCI_GETFOLDPARENT, line);\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic void Dispose() {\n\t\t\tif (_foldLine < 0) return;\n\t\t\t_sci.Call(SCI_FOLDLINE, _foldLine);\n\t\t\t\n\t\t\t//If at the modified line index was a nested folding point, Scintilla will expand again, very async.\n\t\t\t//\tCould restore again with the following code, but it can be dangerous, eg document closed. Never mind.\n\t\t\t//var sci = _sci; var i = _foldLine;\n\t\t\t//timer.after(300, _ => sci.Call(SCI_FOLDLINE, i));\n\t\t}\n\t}\n\t\n\t///// <summary>\n\t///// Inserts text at current position.\n\t///// Does not parse tags.\n\t///// Does not change current selection; for it use <see cref=\"aaaReplaceSel\"/>.\n\t///// </summary>\n\t///// <param name=\"s\">Text to insert. Can be null.</param>\n\t//public void aaaInsertText(string s)\n\t//{\n\t//\tusing(new _NoReadonly(this))\n\t//\t\taaaSetString(SCI_INSERTTEXT, -1, s ?? \"\");\n\t//}\n\t\n\t/// <summary>\n\t/// SCI_DELETERANGE.\n\t/// </summary>\n\t/// <param name=\"utf16\"></param>\n\t/// <param name=\"from\">Start index.</param>\n\t/// <param name=\"to\">End index. If -1, uses control text length.</param>\n\t/// <remarks>\n\t/// Does not parse tags.\n\t/// Does not change current selection, unless it is in the range (including <i>to</i>); for it use <see cref=\"aaaReplaceSel\"/> or <see cref=\"aaaReplaceRange\"/>.\n\t/// </remarks>\n\tpublic void aaaDeleteRange(bool utf16, int from, int to) {\n\t\taaaNormalizeRange(utf16, ref from, ref to);\n\t\tusing (new _NoReadonly(this))\n\t\t\tCall(SCI_DELETERANGE, from, to - from);\n\t}\n\t\n\t/// <summary>\n\t/// Replaces text range.\n\t/// </summary>\n\t/// <param name=\"utf16\"></param>\n\t/// <param name=\"from\">Start index.</param>\n\t/// <param name=\"to\">End index. If -1, uses control text length. Can be less than <i>from</i>.</param>\n\t/// <param name=\"s\">Replacement text. Can be null.</param>\n\t/// <param name=\"moveCurrentPos\">\n\t/// After replacing set curent position at the end of the replacement. If <i>from</i> less than to - at <i>from</i>.\n\t/// Else if current position was in the range (including <i>to</i>), Scintilla sets at <i>from</i>.\n\t/// Else does not change current position and selection.\n\t/// </param>\n\t/// <remarks>\n\t/// Does not parse tags.\n\t/// By default does not change current selection, unless it is in the range (including <i>to</i>).\n\t/// </remarks>\n\tpublic void aaaReplaceRange(bool utf16, int from, int to, string s, bool moveCurrentPos = false) {\n\t\tbool reverse = aaaNormalizeRangeCanBeReverse(utf16, ref from, ref to, swapFromTo: true);\n\t\tusing (new _NoReadonly(this)) {\n\t\t\tint fromEnd = !moveCurrentPos || reverse ? 0 : aaaLen8 - to;\n\t\t\tCall(SCI_SETTARGETRANGE, from, to);\n\t\t\taaaSetString(SCI_REPLACETARGET, s ?? \"\");\n\t\t\tif (moveCurrentPos) aaaCurrentPos8 = reverse ? from : aaaLen8 - fromEnd;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Gets range text.\n\t/// </summary>\n\t/// <param name=\"utf16\"></param>\n\t/// <param name=\"from\">Start index.</param>\n\t/// <param name=\"to\">End index. If -1, uses control text length.</param>\n\tpublic string aaaRangeText(bool utf16, int from, int to) {\n\t\taaaNormalizeRange(utf16, ref from, ref to);\n\t\treturn _RangeText(from, to);\n\t}\n\t\n\t/// <summary>\n\t/// Gets direct pointer to a text range in Scintilla buffer (SCI_GETRANGEPOINTER).\n\t/// Does not validate arguments, just asserts to >= from.\n\t/// </summary>\n\t/// <param name=\"from\">UTF-8 start position.</param>\n\t/// <param name=\"to\">UTF-8 end position.</param>\n\tpublic byte* aaaRangePointer(int from, int to) {\n\t\tDebug.Assert(to >= from);\n\t\treturn (byte*)CallRetPtr(SCI_GETRANGEPOINTER, from, to - from);\n\t}\n\t\n\t/// <summary>\n\t/// Gets direct pointer to a text range in Scintilla buffer (SCI_GETRANGEPOINTER).\n\t/// Does not validate arguments, just asserts to >= from.\n\t/// </summary>\n\t/// <param name=\"from\">UTF-8 start position.</param>\n\t/// <param name=\"to\">UTF-8 end position.</param>\n\tpublic RByte aaaRangeSpan(int from, int to) => new(aaaRangePointer(from, to), to - from);\n\t\n\t/// <summary>\n\t/// SCI_REPLACESEL.\n\t/// </summary>\n\t/// <param name=\"s\">Replacement text. Can be null.</param>\n\t/// <remarks>\n\t/// Does not parse tags.\n\t/// If read-only, asserts and fails (unlike most other functions that change text).\n\t/// </remarks>\n\tpublic void aaaReplaceSel(string s) {\n\t\tDebug.Assert(!aaaIsReadonly);\n\t\taaaSetString(SCI_REPLACESEL, 0, s ?? \"\");\n\t}\n\t\n\t/// <summary>\n\t/// Sets selection (SCI_SETSEL) and replaces with new text (SCI_REPLACESEL).\n\t/// </summary>\n\t/// <param name=\"utf16\"></param>\n\t/// <param name=\"from\">Start index.</param>\n\t/// <param name=\"to\">End index. If -1, uses control text length. Can be less than from.</param>\n\t/// <param name=\"s\">Replacement text. Can be null.</param>\n\t/// <remarks>\n\t/// Does not parse tags.\n\t/// If read-only, asserts and fails (unlike most other functions that change text).\n\t/// </remarks>\n\tpublic void aaaSetAndReplaceSel(bool utf16, int from, int to, string s) {\n\t\tDebug.Assert(!aaaIsReadonly);\n\t\taaaSelect(utf16, from, to);\n\t\taaaSetString(SCI_REPLACESEL, 0, s ?? \"\");\n\t}\n\t\n\t/// <summary>\n\t/// SCI_GOTOPOS and ensures visible.\n\t/// </summary>\n\tpublic void aaaGoToPos(bool utf16, int pos) {\n\t\tpos = _ParamPos(utf16, pos);\n\t\tint line = Call(SCI_LINEFROMPOSITION, pos);\n\t\tCall(SCI_ENSUREVISIBLEENFORCEPOLICY, line);\n\t\tCall(SCI_GOTOPOS, pos);\n\t}\n\t\n\t/// <summary>\n\t/// SCI_GOTOLINE and ensures visible.\n\t/// </summary>\n\tpublic void aaaGoToLine(int line) {\n\t\tCall(SCI_ENSUREVISIBLEENFORCEPOLICY, line);\n\t\tCall(SCI_GOTOLINE, line);\n\t}\n\t\n\t/// <summary>\n\t/// SCI_SETSEL and optionally ensures visible.\n\t/// </summary>\n\t/// <param name=\"utf16\"></param>\n\t/// <param name=\"from\"></param>\n\t/// <param name=\"to\">If -1, uses text length. Else <i>to</i> can be less than <i>from</i>. Caret will be at <i>to</i>.</param>\n\t/// <param name=\"makeVisible\">Ensure line visible and selection visible. Without it in some cases selection to the left of the caret may be invisible.</param>\n\tpublic void aaaSelect(bool utf16, int from, int to, bool makeVisible = false) {\n\t\taaaNormalizeRangeCanBeReverse(utf16, ref from, ref to, swapFromTo: false);\n\t\tif (makeVisible) aaaGoToPos(false, from);\n\t\tCall(SCI_SETSEL, from, to);\n\t}\n\t\n\t/// <summary>\n\t/// SCI_GETREADONLY, SCI_SETREADONLY.\n\t/// </summary>\n\tpublic bool aaaIsReadonly {\n\t\tget => 0 != Call(SCI_GETREADONLY);\n\t\tset => Call(SCI_SETREADONLY, value ? 1 : 0);\n\t}\n\t\n\t//public bool aaaIsReadonly {\n\t//\tget => _isReadOnly;\n\t//\tset {\n\t//\t\tif (value != _isReadOnly) {\n\t//\t\t\t_isReadOnly = value;\n\t//\t\t\tif (!_w.Is0) Call(SCI_SETREADONLY, _isReadOnly);\n\t//\t\t}\n\t//\t}\n\t//}\n\t//bool _isReadOnly;\n\t\n\t/// <summary>\n\t/// Gets text and offsets of lines containing selection.\n\t/// Returns true. If <i>ifFullLines</i> is true, may return false.\n\t/// </summary>\n\t/// <param name=\"utf16\">Return UTF-16.</param>\n\t/// <param name=\"x\">Results.</param>\n\t/// <param name=\"ifFullLines\">Fail (return false) if selection length is 0 or selection start is not at a line start.</param>\n\t/// <param name=\"oneMore\">Get +1 line if selection ends at a line start, except if selection length is 0.</param>\n\tpublic bool aaaGetSelectionLines(bool utf16, out (int selStart, int selEnd, int linesStart, int linesEnd, string text) x, bool ifFullLines = false, bool oneMore = false) {\n\t\tx = default;\n\t\tx.selStart = aaaSelectionStart8; x.selEnd = aaaSelectionEnd8;\n\t\tif (ifFullLines && x.selEnd == x.selStart) return false;\n\t\tvar (_, start, end) = aaaLineStartEndFromPos(false, x.selStart);\n\t\tif (ifFullLines && start != x.selStart) return false;\n\t\tx.linesStart = start;\n\t\t\n\t\tif (x.selEnd > x.selStart) {\n\t\t\t(_, start, end) = aaaLineStartEndFromPos(false, x.selEnd);\n\t\t\tif (!oneMore && start == x.selEnd) end = start; //selection end is at line start. We need the line only if oneMore.\n\t\t\tif (ifFullLines && x.selEnd < end) return false;\n\t\t}\n\t\t\n\t\tx.linesEnd = end;\n\t\tx.text = _RangeText(x.linesStart, end);\n\t\tif (utf16) {\n\t\t\tx.linesStart = aaaPos16(x.linesStart);\n\t\t\tx.linesEnd = aaaPos16(x.linesEnd);\n\t\t\tx.selStart = aaaPos16(x.selStart);\n\t\t\tx.selEnd = aaaPos16(x.selEnd);\n\t\t}\n\t\treturn true;\n\t}\n\t\n\tpublic string aaaSelectedText() => _RangeText(aaaSelectionStart8, aaaSelectionEnd8);\n\t\n\t/// <summary>\n\t/// SCI_FINDTEXT.\n\t/// </summary>\n\t/// <param name=\"utf16\">pos is UTF-16. Return UTF-16.</param>\n\t/// <param name=\"s\"></param>\n\t/// <param name=\"start\"></param>\n\t/// <param name=\"end\">If -1, text length.</param>\n\tpublic unsafe int aaaFindText(bool utf16, RStr s, int start = 0, int end = -1) {\n\t\taaaNormalizeRange(utf16, ref start, ref end);\n\t\tfixed (byte* b = _ToUtf8(s)) {\n\t\t\tvar k = new Sci_TextToFind { cpMin = start, cpMax = end, lpstrText = b, chrgText = default };\n\t\t\treturn _ReturnPosCanBeNegative(utf16, Call(SCI_FINDTEXT, SCFIND_MATCHCASE, &k));\n\t\t}\n\t\t//tested: with SCI_SEARCHINTARGET slightly slower\n\t}\n\t\n\t/// <summary>\n\t/// SCI_GETCHARAT.\n\t/// </summary>\n\tpublic char aaaCharAt8(int pos8) => (char)Call(SCI_GETCHARAT, pos8);\n\t\n\t/// <summary>\n\t/// SCI_BEGINUNDOACTION, SCI_ENDUNDOACTION.\n\t/// </summary>\n\tpublic void aaaAddUndoPoint() {\n\t\tCall(SCI_BEGINUNDOACTION);\n\t\tCall(SCI_ENDUNDOACTION);\n\t}\n\t\n\t/// <summary>\n\t/// SCI_BEGINUNDOACTION.\n\t/// </summary>\n\t/// <returns>Final counter of nested undo actions. Called SCI_BEGINUNDOACTION if it's 1.</returns>\n\tpublic int aaaBeginUndoAction() {\n\t\tif (_inUndoAction == 0) Call(SCI_BEGINUNDOACTION);\n\t\treturn ++_inUndoAction;\n\t}\n\tint _inUndoAction;\n\t\n\t/// <summary>\n\t/// SCI_ENDUNDOACTION.\n\t/// </summary>\n\t/// <returns>Final counter of nested undo actions. Called SCI_ENDUNDOACTION if it's 0.</returns>\n\tpublic int aaaEndUndoAction() {\n\t\tDebug.Assert(_inUndoAction > 0);\n\t\tif (--_inUndoAction == 0) Call(SCI_ENDUNDOACTION);\n\t\treturn _inUndoAction;\n\t}\n\t\n\t//currently not used. Moved to SciCode, which also supports onUndoDontChangeCaretPos.\n\t///// <summary>\n\t///// <c>=> new aaaUndoAction(this);</c>\n\t///// </summary>\n\t//public aaaUndoAction aaaNewUndoAction() => new aaaUndoAction(this);\n\t\n\t///// <summary>\n\t///// Ctor calls <see cref=\"aaaBeginUndoAction\"/>. Dispose() calls <see cref=\"aaaEndUndoAction\"/>.\n\t///// Does nothing if it's a nested undo action.\n\t///// </summary>\n\t//public struct aaaUndoAction : IDisposable {\n\t//\tKScintilla _sci;\n\t\t\n\t//\t/// <summary>\n\t//\t/// Calls SCI_BEGINUNDOACTION.\n\t//\t/// </summary>\n\t//\t/// <param name=\"sci\">Can be null, then does nothing.</param>\n\t//\tpublic aaaUndoAction(KScintilla sci) {\n\t//\t\t_sci = sci;\n\t//\t\t_sci?.aaaBeginUndoAction();\n\t//\t}\n\t\t\n\t//\t/// <summary>\n\t//\t/// Calls SCI_ENDUNDOACTION and clears this variable.\n\t//\t/// </summary>\n\t//\tpublic void Dispose() {\n\t//\t\tif (_sci != null) {\n\t//\t\t\t_sci.aaaEndUndoAction();\n\t//\t\t\t_sci = null;\n\t//\t\t}\n\t//\t}\n\t//}\n}\n\n/// <summary>\n/// Flags for 'set text', 'clear text' and similar functions. Eg you can disable Undo collection or 'changed' notifications.\n/// Note: Ignores NoUndo and NoNotify if <b>AaInitReadOnlyAlways</b>, because then Undo and notifications are disabled when creating control.\n/// </summary>\n[Flags]\npublic enum SciSetTextFlags {\n\t/// <summary>\n\t/// Cannot be undone. Clear Undo buffer.\n\t/// </summary>\n\tNoUndo = 1,\n\t\n\t/// <summary>\n\t/// Don't send 'modified' and 'text changed' notifications (don't call overrides and events).\n\t/// </summary>\n\tNoNotify = 2,\n\t\n\t/// <summary>\n\t/// NoUndo | NoNotify.\n\t/// </summary>\n\tNoUndoNoNotify = 3,\n}\n\n/// <summary>\n/// Provides fast direct access to a range of UTF-8 characters in Scintilla internal text.\n/// Uses SCI_GETRANGEPOINTER. See <see cref=\"KScintilla.aaaRangePointer\"/>.\n/// Ensures that the gap is not moved (it could be slow if frequently).\n/// </summary>\nunsafe struct SciDirectRange {\n\tint _from, _to, _gap;\n\tbyte* _p1, _p2; //before and after gap\n\t\n\tpublic SciDirectRange(KScintilla sci, int from8, int to8) {\n\t\t_from = from8;\n\t\t_to = to8;\n\t\t_gap = sci.Call(SCI_GETGAPPOSITION);\n\t\t//print.it(_from, _to, _gap);\n\t\tif (_gap > _from && _gap < _to) {\n\t\t\t_p1 = sci.aaaRangePointer(_from, _gap);\n\t\t\t_p2 = sci.aaaRangePointer(_gap, _to);\n\t\t} else {\n\t\t\t_p1 = sci.aaaRangePointer(_from, _to);\n\t\t\t_p2 = null;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Returns character at position <i>i</i> in entire text (not from the start of the range).\n\t/// </summary>\n\t/// <exception cref=\"IndexOutOfRangeException\"></exception>\n\tpublic char this[int i] {\n\t\tget {\n\t\t\tif (i < _from || i >= _to) throw new IndexOutOfRangeException();\n\t\t\tif (_p2 == null || i < _gap) return (char)_p1[i - _from];\n\t\t\treturn (char)_p2[i - _gap];\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au.Controls/KScintilla/SciImages.cs",
    "content": "namespace Au.Controls;\n\nusing static Sci;\n\n/// <summary>\n/// Gets image file paths etc from <see cref=\"KScintilla\"/> control text and displays the images below that lines.\n/// </summary>\n/// <remarks>\n/// Draws images in annotation areas.\n/// Supports text annotations too, below images and in no-image lines. But it is limited:\n/// 1. To set/get it use <see cref=\"KScintilla.aaaAnnotationText(int, string, bool)\"/>, not direct Scintilla API.\n/// 2. You cannot hide all annotations (SCI_ANNOTATIONSETVISIBLE). This class sets it to show always.\n/// 3. You cannot clear all annotations (SCI_ANNOTATIONCLEARALL).\n/// 4. Setting annotation styles is currently not supported.\n/// </remarks>\npublic unsafe class SciImages {\n\tclass _Image {\n\t\tpublic long nameHash, evictionTime;\n\t\tpublic byte[] data;\n\t\tpublic int width, height;\n\t}\n\t\n\tclass _ThreadSharedData {\n\t\tList<_Image> _a;\n\t\tint _dpi;\n\t\ttimer _timer;\n\t\t\n\t\tpublic int CacheSize { get; private set; }\n\t\t\n\t\tpublic void AddImage(_Image im) {\n\t\t\t_a ??= [];\n\t\t\t_a.Add(im);\n\t\t\tCacheSize += im.data.Length;\n\t\t\t\n\t\t\tim.evictionTime = Environment.TickCount64 + 2000;\n\t\t\t//rejected: keep im in cache longer if the loading was slow. Eg AV scans exe files when we extract icons.\n\t\t\t//\tUsually only the first time is slow. Later the file is in the OS file cache.\n\t\t\t\n\t\t\t_timer ??= new(t => {\n\t\t\t\tvar now = Environment.TickCount64;\n\t\t\t\tfor (int i = _a.Count; --i >= 0;)\n\t\t\t\t\tif (now - _a[i].evictionTime > 0) {\n\t\t\t\t\t\tCacheSize -= _a[i].data.Length;\n\t\t\t\t\t\t_a.RemoveAt(i);\n\t\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (_a.Count == 0) _timer.Stop();\n\t\t\t});\n\t\t\tif (!_timer.IsRunning) _timer.Every(500);\n\t\t}\n\t\t\n\t\tpublic _Image FindImage(long nameHash, int dpi) {\n\t\t\tif (dpi != _dpi) {\n\t\t\t\tClearCache();\n\t\t\t\t_dpi = dpi;\n\t\t\t}\n\t\t\tif (!_a.NE_()) {\n\t\t\t\tfor (int j = 0; j < _a.Count; j++) if (_a[j].nameHash == nameHash) return _a[j];\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\t\n\t\tpublic void ClearCache() {\n\t\t\t_a?.Clear();\n\t\t\tCacheSize = 0;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// If cache is large (at least MaxCacheSize and 4 images), removes about 3/4 of older cached images.\n\t\t/// Will auto-reload from files etc when need.\n\t\t/// </summary>\n\t\tpublic void CompactCache() {\n\t\t\tif (_a == null) return;\n\t\t\t//print.it(_cacheSize);\n\t\t\tif (CacheSize < MaxCacheSize || _a.Count < 4) return;\n\t\t\tCacheSize = 0;\n\t\t\tint n = _a.Count, max = MaxCacheSize / 4;\n\t\t\twhile (CacheSize < max && n > 2) CacheSize += _a[--n].data.Length;\n\t\t\t_a.RemoveRange(0, n);\n\t\t}\n\t\t\n\t\tpublic int MaxCacheSize { get; set; } = 4 * 1024 * 1024;\n\t}\n\t[ThreadStatic] static _ThreadSharedData t_data; //all SciImages of a thread share single cache etc\n\t\n\tKScintilla _c;\n\tIntPtr _callbackPtr;\n\tconst int c_indicImage = 7;\n\t\n\t/// <summary>\n\t/// Prepares this variable and the Scintilla control to display images.\n\t/// Note: will call SCI_ANNOTATIONSETVISIBLE(ANNOTATION_STANDARD) to draw images in annotation areas.\n\t/// Note: uses indicator 7.\n\t/// </summary>\n\t/// <param name=\"c\">The control.</param>\n\tinternal SciImages(KScintilla c) {\n\t\tt_data ??= new();\n\t\t_c = c;\n\t\t_sci_AnnotationDrawCallback = _AnnotationDrawCallback;\n\t\t_callbackPtr = Marshal.GetFunctionPointerForDelegate(_sci_AnnotationDrawCallback);\n\t\t_c.Call(SCI_SETANNOTATIONDRAWCALLBACK, 0, _callbackPtr);\n\t\t_c.aaaIndicatorDefine(c_indicImage, INDIC_HIDDEN);\n\t}\n\t\n\t_Image _GetImageFromText(RByte s) {\n\t\t//is it an image string?\n\t\tvar imType = KImageUtil.ImageTypeFromString(out int prefixLength, s);\n\t\tif (imType == KImageUtil.ImageType.None) return null;\n\t\tif (prefixLength == 10) { s = s[prefixLength..]; prefixLength = 0; } //\"imagefile:\"\n\t\t\n\t\tvar d = t_data;\n\t\t\n\t\t//is already loaded?\n\t\tlong hash = Hash.Fnv1Long(s);\n\t\tvar im = d.FindImage(hash, _c._dpi);\n\t\t//print.qm2.write(im != null, s.ToStringUTF8());\n\t\tif (im != null) return im;\n\t\t\n\t\tstring path = s[prefixLength..].ToStringUTF8();\n\t\t\n\t\t//load\n\t\tlong t1 = computer.tickCountWithoutSleep;\n\t\tbyte[] b = KImageUtil.BmpFileDataFromString(path, imType, true, (_c._dpi, null));\n\t\tt1 = computer.tickCountWithoutSleep - t1; if (t1 > 1000) print.warning($\"Time to load image '{path}' is {t1} ms.\", -1, prefix: \"<>Note: \"); //eg if network path unavailable, may wait ~7 s\n\t\tif (b == null) return null;\n\t\tif (!KImageUtil.GetBitmapFileInfo_(b, out var q)) return null;\n\t\t\n\t\t//create _Image\n\t\tim = new _Image() {\n\t\t\tdata = b,\n\t\t\tnameHash = hash,\n\t\t\twidth = q.width,\n\t\t\theight = Math.Min(q.height + IMAGE_MARGIN_TOP + IMAGE_MARGIN_BOTTOM, 2000)\n\t\t};\n\t\t\n\t\t//add to cache\n\t\t//Compact cache to avoid memory problems when loaded many big images, eg showing all png in Program Files.\n\t\t//Will auto reload when need, it does not noticeably slow down.\n\t\t//Cache even very large images, because we draw each line separately, would need to load whole image for each line, which is VERY slow.\n\t\td.CompactCache();\n\t\td.AddImage(im);\n\t\t\n\t\treturn im;\n\t}\n\t\n\t/// <summary>\n\t/// Sets image annotations for one or more lines of text.\n\t/// Called at the end of SciTags._AddText if the added text contains image tags.\n\t/// </summary>\n\tinternal void SetImagesForTextRange_(RByte text, List<StartEnd> images, int prevLen) {\n\t\tbool allText = prevLen == 0;\n\t\tbool annotAdded = false;\n\t\tint iLine = -1, maxHeight = 0, totalWidth = 0;\n\t\tforeach (var v in images) {\n\t\t\tint line = _c.aaaLineFromPos(false, prevLen + v.start);\n\t\t\tif (line != iLine) {\n\t\t\t\t_AddLine();\n\t\t\t\t(iLine, maxHeight, totalWidth) = (line, 0, 0);\n\t\t\t}\n\t\t\tfor (int start = v.start, end = 0; start < v.end; start = end + 1) { //foreach image in image1|image2|image3\n\t\t\t\tfor (end = start; end < v.end && text[end] != '|';) end++;\n\t\t\t\tvar s = text[start..end];\n\t\t\t\tif (_GetImageFromText(s) is not _Image u) continue;\n\t\t\t\tif (maxHeight < u.height) maxHeight = u.height;\n\t\t\t\tif (totalWidth > 0) totalWidth += 30;\n\t\t\t\ttotalWidth += u.width;\n\t\t\t\t_c.aaaIndicatorAdd(c_indicImage, false, (start + prevLen)..(end + prevLen));\n\t\t\t}\n\t\t}\n\t\t_AddLine();\n\t\t\n\t\t[SkipLocalsInit]\n\t\tvoid _AddLine() {\n\t\t\tif (maxHeight == 0) return;\n\t\t\tint annotLen = _c.Call(SCI_ANNOTATIONGETTEXT, iLine); //we'll need old annotation text later, and we'll get it into the same buffer after the new image info\n\t\t\t\n\t\t\t//calculate n annotation lines from image height\n\t\t\tint lineHeight = _c.aaaLineHeight(); if (lineHeight <= 0) return;\n\t\t\tint nAnnotLines = Math.Min((maxHeight + (lineHeight - 1)) / lineHeight, 255);\n\t\t\t//print.it(lineHeight, maxHeight, nAnnotLines);\n\t\t\t\n\t\t\tusing FastBuffer<byte> buffer = new(annotLen + nAnnotLines + 20);\n\t\t\tvar p = buffer.p;\n\t\t\t*p++ = 3; Api._ltoa(totalWidth << 8 | nAnnotLines, p, 16); while (*(++p) != 0) { }\n\t\t\twhile (nAnnotLines-- > 1) *p++ = (byte)'\\n';\n\t\t\t*p = 0;\n\t\t\t\n\t\t\t//TODO2: code in this file can be simplified in several places. We don't use image+text annotations and images in editable text. Initially it was designed to support such images in code editor in editable mode.\n\t\t\t//An annotation possibly already exists. Possible cases:\n\t\t\t//1. No annotation. Need to add our image annotation.\n\t\t\t//2. A text-only annotation. Need to add our image annotation + that text.\n\t\t\t//3. Different image, no text. Need to replace it with our image annotation.\n\t\t\t//4. Different image + text. Need to replace it with our image annotation + that text.\n\t\t\t//5. This image, with or without text. Don't need to change.\n\t\t\tif (annotLen > 0) {\n\t\t\t\t//get existing annotation into the same buffer after our image info\n\t\t\t\tvar a = p + 1;\n\t\t\t\t_c.Call(SCI_ANNOTATIONGETTEXT, iLine, a);\n\t\t\t\ta[annotLen] = 0;\n\t\t\t\t//print.it($\"OLD: '{new string((sbyte*)a)}'\");\n\t\t\t\t\n\t\t\t\t//is it our image info?\n\t\t\t\tint imageLen = (int)(p - buffer.p);\n\t\t\t\tif (annotLen >= imageLen) {\n\t\t\t\t\tint j;\n\t\t\t\t\tfor (j = 0; j < imageLen; j++) if (a[j] != buffer[j]) goto g1;\n\t\t\t\t\tif (annotLen == imageLen || a[imageLen] == '\\n') return; //case 5\n\t\t\t\t}\n\t\t\t\tg1:\n\t\t\t\t//contains image?\n\t\t\t\tif (a[0] == 3) {\n\t\t\t\t\tint j = _ParseAnnotText(a, annotLen, out var _);\n\t\t\t\t\tif (j < annotLen) { //case 4\n\t\t\t\t\t\tApi.memmove(a, a + j, annotLen - j + 1);\n\t\t\t\t\t\tp[0] = (byte)'\\n';\n\t\t\t\t\t} //else case 3\n\t\t\t\t} else { //case 2\n\t\t\t\t\tp[0] = (byte)'\\n';\n\t\t\t\t}\n\t\t\t} //else case 1\n\t\t\t\n\t\t\t//print.it($\"NEW: '{new string((sbyte*)b0.p)}'\");\n\t\t\t//perf.first();\n\t\t\tif (!annotAdded) {\n\t\t\t\tannotAdded = true;\n\t\t\t\tif (allText) _c.Call(SCI_ANNOTATIONSETVISIBLE, (int)AnnotationsVisible.ANNOTATION_HIDDEN);\n\t\t\t}\n\t\t\t_c.Call(SCI_ANNOTATIONSETTEXT, iLine, buffer.p);\n\t\t\t//perf.nw();\n\t\t}\n\t\t\n\t\tif (annotAdded && allText) {\n\t\t\t_c.Call(SCI_ANNOTATIONSETVISIBLE, (int)AnnotationsVisible.ANNOTATION_STANDARD);\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Parses annotation text.\n\t/// If it starts with image info string (\"\\x3NNN\\n\\n...\"), returns its length. Else returns 0.\n\t/// </summary>\n\t/// <param name=\"s\">Annotation text. Can start with image info string or not.</param>\n\t/// <param name=\"length\">s length.</param>\n\t/// <param name=\"imageInfo\">The NNN part of image info, or 0.</param>\n\tstatic int _ParseAnnotText(byte* s, int length, out int imageInfo) {\n\t\timageInfo = 0;\n\t\tif (s == null || length < 4 || s[0] != '\\x3') return 0;\n\t\tbyte* s2;\n\t\tint k = Api.strtoi(s + 1, &s2, 16);\n\t\tint len = (int)(s2 - s); if (len < 4) return 0;\n\t\tint n = k & 0xff;\n\t\tlen += (n - 1);\n\t\tif (n < 1 || length < len) return 0;\n\t\tif (length > len) len++; //\\n between image info and visible annotation text\n\t\timageInfo = k;\n\t\treturn len;\n\t}\n\t\n\t/// <summary>\n\t/// Sets annotation text, preserving existing image info.\n\t/// </summary>\n\t/// <param name=\"line\"></param>\n\t/// <param name=\"s\">New text without image info.</param>\n\t[SkipLocalsInit]\n\tinternal void AnnotationText_(int line, string s) {\n\t\tint n = _c.Call(SCI_ANNOTATIONGETTEXT, line);\n\t\tif (n > 0) {\n\t\t\tint lens = (s == null) ? 0 : s.Length;\n\t\t\tusing FastBuffer<byte> buffer = new(n + 1 + lens * 3);\n\t\t\tvar p = buffer.p;\n\t\t\t_c.Call(SCI_ANNOTATIONGETTEXT, line, p); p[n] = 0;\n\t\t\tint imageLen = _ParseAnnotText(p, n, out var _);\n\t\t\tif (imageLen > 0) {\n\t\t\t\t//info: now len<=n\n\t\t\t\tif (lens == 0) {\n\t\t\t\t\tif (imageLen == n) return; //no \"\\nPrevText\"\n\t\t\t\t\tp[--imageLen] = 0; //remove \"\\nPrevText\"\n\t\t\t\t} else {\n\t\t\t\t\tif (imageLen == n) p[imageLen++] = (byte)'\\n'; //no \"\\nPrevText\"\n\t\t\t\t\t//Convert2.Utf8FromString(s, p + imageLen, lens * 3);\n\t\t\t\t\tEncoding.UTF8.GetBytes(s, new Span<byte>(p + imageLen, lens * 3));\n\t\t\t\t}\n\t\t\t\t_c.Call(SCI_ANNOTATIONSETTEXT, line, p);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\t_c.aaaAnnotationText_(line, s);\n\t}\n\t\n\t/// <summary>\n\t/// Gets annotation text without image info.\n\t/// </summary>\n\t[SkipLocalsInit]\n\tinternal string AnnotationText_(int line) {\n\t\tint n = _c.Call(SCI_ANNOTATIONGETTEXT, line);\n\t\tif (n > 0) {\n\t\t\tusing FastBuffer<byte> buffer = new(n);\n\t\t\tvar p = buffer.p;\n\t\t\t_c.Call(SCI_ANNOTATIONGETTEXT, line, p); p[n] = 0;\n\t\t\tint imageLen = _ParseAnnotText(p, n, out var _);\n\t\t\t//info: now len<=n\n\t\t\tif (imageLen < n) {\n\t\t\t\tif (imageLen != 0) { p += imageLen; n -= imageLen; }\n\t\t\t\treturn Encoding.UTF8.GetString(p, n);\n\t\t\t}\n\t\t}\n\t\treturn \"\";\n\t}\n\t\n\tconst int IMAGE_MARGIN_TOP = 2; //frame + 1\n\tconst int IMAGE_MARGIN_BOTTOM = 1; //just for frame. It is minimal margin, in most cases will be more.\n\t\n\tSci_AnnotationDrawCallback _sci_AnnotationDrawCallback;\n\tunsafe int _AnnotationDrawCallback(void* cbParam, ref Sci_AnnotationDrawCallbackData c) {\n\t\t//Function info:\n\t\t//Called for all annotations, not just for images.\n\t\t//Returns image width. Returns 0 if there is no image or when called for annotation text line below image.\n\t\t//Called for each line of annotation, not once for whole image. Draws each image slice separately.\n\t\t//Called 2 times for each line: step 0 - to get width; step 1 - to draw that image slice on that line. Step 0 skipped if AnnotationsVisible.ANNOTATION_STANDARD (we don't use other styles).\n\t\t\n\t\t//Get image info from annotation text. Return 0 if there is no image info, ie no image.\n\t\t//Image info is at the start. Format \"\\x3XXX\", where XXX is a hex number that contains image width and number of lines.\n\t\tbyte* s = c.text;\n\t\tif (c.textLen < 4 || s[0] != '\\x3') return 0;\n\t\tint k = Api.strtoi(++s, null, 16); if (k < 256) return 0;\n\t\tint nLines = k & 0xff, width = k >> 8;\n\t\t\n\t\tif (c.step == 0) return width + 1; //just get width\n\t\tif (c.annotLine >= nLines) return 0; //an annotation text line below the image lines\n\t\t\n\t\t//find image strings and draw the images\n\t\tbool hasImages = false;\n\t\tvar hdc = c.hdc;\n\t\tIntPtr pen = default, oldPen = default;\n\t\ttry { //Handle exceptions because SetDIBitsToDevice may read more than need, like CreateDIBitmap, although I never noticed this.\n\t\t\tint from = _c.aaaLineStart(false, c.line), to = _c.aaaLineEnd(false, c.line);\n\t\t\tRECT r = c.rect;\n\t\t\tint x = r.left + 1;\n\t\t\tfor (int end = from; ;) { //for each `IMAGE` in this line, in text like `A <image \"IMAGE\"> b <image \"IMAGE|IMAGE\"> c`\n\t\t\t\tint start = _c.Call(SCI_INDICATOREND, c_indicImage, end); if (start <= 0 || start >= to) break;\n\t\t\t\tend = _c.Call(SCI_INDICATOREND, c_indicImage, start); if (end <= start) break;\n\t\t\t\t\n\t\t\t\tvar u = _GetImageFromText(_c.aaaRangeSpan(start, end)); //find cached image or load and addd to the cache\n\t\t\t\tif (u is null) break;\n\t\t\t\thasImages = true;\n\t\t\t\t\n\t\t\t\t//draw image (single slice, for this visual line)\n\t\t\t\tif (!KImageUtil.GetBitmapFileInfo_(u.data, out var q)) { Debug.Assert(false); continue; }\n\t\t\t\tint isFirstLine = (c.annotLine == 0) ? 1 : 0, hLine = r.bottom - r.top;\n\t\t\t\tint currentTop = c.annotLine * hLine, currentBottom = currentTop + hLine, imageBottom = q.height + IMAGE_MARGIN_TOP;\n\t\t\t\tint y = r.top + isFirstLine * IMAGE_MARGIN_TOP, yy = Math.Min(currentBottom, imageBottom) - currentTop;\n\t\t\t\t\n\t\t\t\tif (imageBottom > currentTop && q.width > 0 && q.height > 0) {\n\t\t\t\t\tfixed (byte* bp = u.data) {\n\t\t\t\t\t\tKImageUtil.BITMAPFILEHEADER* f = (KImageUtil.BITMAPFILEHEADER*)bp;\n\t\t\t\t\t\tbyte* pBits = bp + f->bfOffBits;\n\t\t\t\t\t\tint bytesInLine = Math2.AlignUp(q.width * q.bitCount, 32) / 8;\n\t\t\t\t\t\tint sizF = u.data.Length - f->bfOffBits, siz = bytesInLine * q.height;\n\t\t\t\t\t\tif (q.isCompressed) {\n\t\t\t\t\t\t\t//this is slow with big images. It seems processes current line + all remaining lines. Such bitmaps are rare.\n\t\t\t\t\t\t\tint yOffs = -c.annotLine * hLine; if (isFirstLine == 0) yOffs += IMAGE_MARGIN_TOP;\n\t\t\t\t\t\t\tvar ok = Api.SetDIBitsToDevice(hdc, x, r.top + isFirstLine * IMAGE_MARGIN_TOP,\n\t\t\t\t\t\t\t\tq.width, q.height, 0, yOffs, 0, q.height,\n\t\t\t\t\t\t\t\tpBits, q.biHeader);\n\t\t\t\t\t\t\tDebug.Assert(ok > 0);\n\t\t\t\t\t\t} else if (siz <= sizF) {\n\t\t\t\t\t\t\t//this is fast, but cannot use with compressed bitmaps\n\t\t\t\t\t\t\tint hei = yy - y, bmY = q.height - (currentTop - ((isFirstLine ^ 1) * IMAGE_MARGIN_TOP) + hei);\n\t\t\t\t\t\t\tvar ok = Api.SetDIBitsToDevice(hdc, x, r.top + isFirstLine * IMAGE_MARGIN_TOP,\n\t\t\t\t\t\t\t\tq.width, hei, 0, 0, 0, hei,\n\t\t\t\t\t\t\t\tpBits + bmY * bytesInLine, q.biHeader);\n\t\t\t\t\t\t\tDebug.Assert(ok > 0);\n\t\t\t\t\t\t} else Debug.Assert(false);\n\t\t\t\t\t\t\n\t\t\t\t\t\t//could use this instead, but very slow with big images. It seems always processes whole bitmap, not just current line.\n\t\t\t\t\t\t//int hei=yy-y, bmY=q.height-(currentTop-((isFirstLine ^ 1)*IMAGE_MARGIN_TOP)+hei);\n\t\t\t\t\t\t//StretchDIBits(hdc,\n\t\t\t\t\t\t//\tx, y, q.width, hei,\n\t\t\t\t\t\t//\t0, bmY, q.width, hei,\n\t\t\t\t\t\t//\tpBits, h, 0, SRCCOPY);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t//draw frame\n\t\t\t\tif (pen == default) oldPen = Api.SelectObject(hdc, pen = Api.CreatePen(0, 1, 0x60C060)); //quite fast. Caching in a static or ThreadStatic var is difficult.\n\t\t\t\tint xx = x + q.width;\n\t\t\t\tif (isFirstLine != 0) y--;\n\t\t\t\tif (yy > y) {\n\t\t\t\t\tApi.MoveToEx(hdc, x - 1, y, out _); Api.LineTo(hdc, x - 1, yy); //left |\n\t\t\t\t\tApi.MoveToEx(hdc, xx, y, out _); Api.LineTo(hdc, xx, yy); //right |\n\t\t\t\t\tif (isFirstLine != 0) { Api.MoveToEx(hdc, x, y, out _); Api.LineTo(hdc, xx, y); } //top _\n\t\t\t\t}\n\t\t\t\tif (yy >= y && yy < hLine) { Api.MoveToEx(hdc, x - 1, yy, out _); Api.LineTo(hdc, xx + 1, yy); } //bottom _\n\t\t\t\t\n\t\t\t\tx += u.width + 30;\n\t\t\t}\n\t\t}\n\t\tcatch (Exception ex) { Debug_.Print(ex); }\n\t\tfinally { if (pen != default) Api.DeleteObject(Api.SelectObject(hdc, oldPen)); }\n\t\t//perf.nw();\n\t\t\n\t\t//If there are no image strings (text edited), delete the annotation or just its part containing image info and '\\n's.\n\t\tif (!hasImages && c.annotLine == 0) {\n\t\t\tint line = c.line; var annot = AnnotationText_(line);\n\t\t\t//_c.aaaAnnotationText_(line, annot); //dangerous\n\t\t\t_c.Dispatcher.InvokeAsync(() => { _c.aaaAnnotationText_(line, annot); });\n\t\t\treturn 1;\n\t\t}\n\t\t\n\t\treturn width + 1;\n\t\t\n\t\t//speed: fast. The fastest way. Don't need bitmap handle, memory DC, etc.\n\t\t//tested: don't know what ColorUse param of SetDIBitsToDevice does, but DIB_RGB_COLORS works for any h_biBitCount.\n\t\t//speed if drawing frame: multiple LineTo is faster than single PolyPolyline.\n\t\t//tested: GDI+ much slower, particularly DrawImage().\n\t\t//tested: in QM2 was used LZO compression, now ZIP (DeflateStream). ZIP compresses better, but not so much. LZO is faster, but ZIP is fast enough. GIF and JPG in most cases compress less than ZIP and sometimes less than LZO.\n\t\t//tested: saving in 8-bit format in most cases does not make much smaller when compressed. For screenshots we reduce colors to 4-bit.\n\t}\n}\n"
  },
  {
    "path": "Au.Controls/KScintilla/SciTags.cs",
    "content": "/*\nMost tags are like in QM2.\n\nNEW TAGS:\n   <bi> - bold italic.\n   <mono> - monospace font.\n   <size n> - font size (1-127).\n   <fold> - collapsed lines.\n   <explore> - select file in File Explorer.\n   <\\a>text</\\a> - alternative for <_>text</_>.\n   <nonl> - no newline.\n\nNEW PARAMETERS:\n   <c ColorName> - .NET color name for text color. Also color can be #RRGGBB.\n   <bc ColorName> - .NET color name for background color. Also color can be #RRGGBB.\n   <lc ColorName> - .NET color name for background color, whole line. Also color can be #RRGGBB.\n\nRENAMED TAGS:\n\t<script>, was <macro>.\n\t<bc>, was <z>.\n\t<lc>, was <Z>.\n\nREMOVED TAGS:\n\t<tip>.\n\t<mes>, <out>. Now use <fold>.\n\nDIFFERENT SYNTAX:\n\tMost tags can be closed with <> or </> or </anything>.\n\t\tExcept these: <_>text</_>, <\\a>text</\\a>, <code>code</code>, <fold>text</fold>.\n\t\tNo closing tag: <image \"file\">.\n\tAttributes can be enclosed with \"\" or '' or non-enclosed (except for <image>).\n\t\tDoes not support escape sequences. An attribute ends with \"> (if starts with \") or '> (if starts with ') or > (if non-enclosed).\n\t\tIn QM2 need \"\" for most; some can be non-enclosed. QM2 supports escape sequences.\n\tLink tag attribute parts now are separated with \"|\". In QM2 was \" /\".\n\nOTHER CHANGES:\n\tSupports user-defined link tags. Need to provide delegates of functions that implement them. Use SciTags.AddCommonLinkTag or SciTags.AddLinkTag.\n\tThese link tags are not implemented by this class, but you can provide delegates of functions that implement them:\n\t\t<open>, <script>.\n\t<help> by default calls Au.More.HelpUtil.AuHelp, which opens a topic in web browser. You can override it with SciTags.AddCommonLinkTag or SciTags.AddLinkTag.\n\t<code> attributes are not used.\n\nCHANGES IN <image>:\n\tDon't need the closing tag (</image>).\n\tCurrently supports only 16x16 icons. Does not support icon resources.\n\tSupports images embedded directly in text.\n\tMore info in help topic \"Output tags\". File \"Output tags.md\".\n*/\n\nnamespace Au.Controls;\n\nusing static Sci;\n\n/// <summary>\n/// Adds links and text formatting to a <see cref=\"KScintilla\"/> control.\n/// </summary>\n/// <remarks>\n/// Links and formatting is specified in text, using tags like in HTML. Depending on control style, may need prefix <c><![CDATA[<>]]></c>.\n/// Reference: [](xref:output_tags).\n/// Tags are supported by <see cref=\"print.it\"/> when it writes to the Au script editor.\n/// \n/// This control does not implement some predefined tags: open, script.\n/// If used, must be implemented by the program.\n/// Also you can register custom link tags that call your callback functions.\n/// See <see cref=\"AddLinkTag\"/>, <see cref=\"AddCommonLinkTag\"/>.\n/// \n/// Tags are supported by some existing controls based on <see cref=\"KScintilla\"/>. In editor it is the output (use <see cref=\"print.it\"/>, like in the example below). In this library - the <see cref=\"KSciInfoBox\"/> control. To enable tags in other <see cref=\"KScintilla\"/> controls, use <see cref=\"KScintilla.AaInitTagsStyle\"/> and optionally <see cref=\"KScintilla.AaInitImages\"/>.\n/// </remarks>\n/// <example>\n/// <code><![CDATA[\n/// print.it(\"<>Text with <i>tags<>.\");\n/// ]]></code>\n/// </example>\npublic unsafe class SciTags {\n\tconst int STYLE_FIRST_EX = STYLE_LASTPREDEFINED + 1;\n\tconst int NUM_STYLES_EX = STYLE_MAX - STYLE_LASTPREDEFINED;\n\t\n\tpublic struct TagStyle {\n\t\tuint u1, u2;\n\t\t\n\t\t//u1\n\t\tpublic int Color { get => (int)(u1 & 0xffffff); set => u1 = (u1 & 0xff000000) | ((uint)value & 0xffffff) | 0x1000000; }\n\t\tpublic bool HasColor => 0 != (u1 & 0x1000000);\n\t\tpublic int Size { get => (int)(u1 >> 25); set => u1 = (u1 & 0x1ffffff) | ((uint)Math.Clamp(value, 0, 127) << 25); }\n\t\t\n\t\t//u2\n\t\tpublic int BackColor { get => (int)(u2 & 0xffffff); set => u2 = (u2 & 0xff000000) | ((uint)value & 0xffffff) | 0x1000000; }\n\t\tpublic bool HasBackColor => 0 != (u2 & 0x1000000);\n\t\tpublic bool Bold { get => 0 != (u2 & 0x2000000); set { if (value) u2 |= 0x2000000; else u2 &= unchecked((uint)~0x2000000); } }\n\t\tpublic bool Italic { get => 0 != (u2 & 0x4000000); set { if (value) u2 |= 0x4000000; else u2 &= unchecked((uint)~0x4000000); } }\n\t\tpublic bool Underline { get => 0 != (u2 & 0x8000000); set { if (value) u2 |= 0x8000000; else u2 &= unchecked((uint)~0x8000000); } }\n\t\tpublic bool Eol { get => 0 != (u2 & 0x10000000); set { if (value) u2 |= 0x10000000; else u2 &= unchecked((uint)~0x10000000); } }\n\t\tpublic bool Hotspot { get => 0 != (u2 & 0x40000000); set { if (value) u2 |= 0x40000000; else u2 &= unchecked((uint)~0x40000000); } }\n\t\tpublic bool Mono { get => 0 != (u2 & 0x80000000); set { if (value) u2 |= 0x80000000; else u2 &= unchecked((uint)~0x80000000); } }\n\t\t\n\t\tpublic bool Equals(TagStyle x) { return x.u1 == u1 && x.u2 == u2; }\n\t\tpublic void Merge(TagStyle x) {\n\t\t\tvar t1 = x.u1;\n\t\t\tif (HasColor) t1 &= 0xff000000;\n\t\t\tif (Size > 0) t1 &= 0x1ffffff;\n\t\t\tu1 |= t1;\n\t\t\tvar t2 = x.u2;\n\t\t\tif (HasBackColor) {\n\t\t\t\tt2 &= 0xff000000;\n\t\t\t\tt2 &= unchecked((uint)~0x10000000); //don't inherit Eol\n\t\t\t}\n\t\t\tu2 |= t2;\n\t\t}\n\t\tpublic bool IsEmpty => u1 == 0 & u2 == 0;\n\t\t\n\t\tpublic TagStyle(UserDefinedStyle k) {\n\t\t\tu1 = u2 = 0;\n\t\t\tif (k.textColor != null) Color = k.textColor.Value.argb;\n\t\t\tif (k.backColor != null) BackColor = k.backColor.Value.argb;\n\t\t\tSize = k.size;\n\t\t\tBold = k.bold;\n\t\t\tItalic = k.italic;\n\t\t\tUnderline = k.underline;\n\t\t\tEol = k.eolFilled;\n\t\t\tMono = k.monospace;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// For <see cref=\"AddStyleTag\"/>.\n\t/// </summary>\n\tpublic class UserDefinedStyle {\n\t\tpublic ColorInt? textColor, backColor;\n\t\tpublic int size;\n\t\tpublic bool bold, italic, underline, eolFilled, monospace;\n\t}\n\t\n\treadonly KScintilla _c;\n\treadonly List<TagStyle> _styles = [];\n\t\n\tinternal SciTags(KScintilla c) {\n\t\t_c = c;\n\t}\n\t\n\tpublic (IReadOnlyList<TagStyle> a, int first) TagStyles => (_styles, STYLE_FIRST_EX);\n\t\n\t/// <summary>\n\t/// Font name and size for tag styles with <see cref=\"TagStyle.Mono\"/>.\n\t/// If not set, will use \"Consolas\" and default text size.\n\t/// Not used for the styled code tag.\n\t/// Does not update existing styles when the property changed.\n\t/// </summary>\n\tpublic (string name, double size) FontForMonoTags { get; set; }\n\t\n\tvoid _SetUserStyles(int from) {\n\t\tint i, j;\n\t\tfor (i = from; i < _styles.Count; i++) {\n\t\t\tTagStyle st = _styles[i];\n\t\t\tj = i + STYLE_FIRST_EX;\n\t\t\tif (st.HasColor) _c.aaaStyleForeColor(j, st.Color);\n\t\t\tif (st.HasBackColor) { _c.aaaStyleBackColor(j, st.BackColor); if (st.Eol) _c.aaaStyleEolFilled(j, true); }\n\t\t\tif (st.Bold) _c.aaaStyleBold(j, true);\n\t\t\tif (st.Italic) _c.aaaStyleItalic(j, true);\n\t\t\tif (st.Underline) _c.aaaStyleUnderline(j, true);\n\t\t\tif (st.Mono) _c.aaaStyleFont(j, FontForMonoTags.name ?? \"Consolas\");\n\t\t\tif (st.Hotspot) _c.aaaStyleHotspot(j, true);\n\t\t\tint size = st.Size;\n\t\t\tif (size > 0) {\n\t\t\t\tif (size < 6 && st.Hotspot) size = 6;\n\t\t\t\t_c.aaaStyleFontSize(j, size);\n\t\t\t} else if (st.Mono && FontForMonoTags.size >= 6) {\n\t\t\t\t_c.aaaStyleFontSize(j, FontForMonoTags.size);\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Clears user-defined (through tags) styles.\n\t/// Max number of user styles is NUM_STYLES_EX (216). Need to clear old styles before new styles can be defined.\n\t/// This func is usually called after clearing control text.\n\t/// </summary>\n\tvoid _ClearUserStyles() {\n\t\tif (_styles.Count > 0) {\n\t\t\t_c.aaaStyleClearRange(STYLE_FIRST_EX);\n\t\t\t_styles.Clear();\n\t\t}\n\t\t//QM2 also cleared the image cache, but now it is shared by all controls of this thread.\n\t}\n\t\n\tinternal void OnTextChanged_(bool inserted, in SCNotification n) {\n\t\t//if deleted or replaced all text, clear user styles\n\t\tif (!inserted && n.position == 0 && _c.aaaLen8 == 0) {\n\t\t\t_ClearUserStyles();\n\t\t\t//_linkDelegates.Clear(); //no\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Displays <see cref=\"PrintServer\"/> messages that are currently in its queue.\n\t/// </summary>\n\t/// <param name=\"ps\">The <b>PrintServer</b> instance.</param>\n\t/// <param name=\"onMessage\">\n\t/// A callback function that can be called when this function gets/removes a message from ps.\n\t/// When message type is Write, it can change message text; if null, this function ignores the message.\n\t/// It also processes messages of type TaskEvent; this function ignores them.\n\t/// </param>\n\t/// <remarks>\n\t/// Removes messages from the queue.\n\t/// Appends text messages + \"\\r\\n\" to the control's text, or clears etc (depends on message).\n\t/// Messages with tags must have prefix \"&lt;&gt;\".\n\t/// Limits text length to about 4 MB (removes oldest text when exceeded).\n\t/// </remarks>\n\t/// <seealso cref=\"PrintServer.SetNotifications\"/>\n\tpublic void PrintServerProcessMessages(PrintServer ps, Action<PrintServerMessage> onMessage = null) {\n\t\t//info: Cannot call _c.Write for each message, it's too slow. Need to join all messages.\n\t\t//\tIf multiple messages, use StringBuilder.\n\t\t//\tIf some messages have tags, use string \"<\\x15\\x0\\x4\" to separate messages. Never mind: don't escape etc.\n\t\t\n\t\tstring s = null;\n\t\tStringBuilder b = null;\n\t\tbool hasTags = false, hasTagsPrev = false, scrollToTop = false;\n\t\twhile (ps.GetMessage(out var m)) {\n\t\t\tonMessage?.Invoke(m);\n\t\t\tswitch (m.Type) {\n\t\t\tcase PrintServerMessageType.Clear:\n\t\t\t\t_c.aaaClearText();\n\t\t\t\ts = null;\n\t\t\t\tb?.Clear();\n\t\t\t\tbreak;\n\t\t\tcase PrintServerMessageType.ScrollToTop:\n\t\t\t\tscrollToTop = true;\n\t\t\t\tbreak;\n\t\t\tcase PrintServerMessageType.Write when m.Text != null:\n\t\t\t\tif (s == null) {\n\t\t\t\t\ts = m.Text;\n\t\t\t\t\thasTags = hasTagsPrev = s.Starts(\"<>\");\n\t\t\t\t} else {\n\t\t\t\t\tb ??= new StringBuilder();\n\t\t\t\t\tif (b.Length == 0) b.Append(s);\n\t\t\t\t\t\n\t\t\t\t\ts = m.Text;\n\t\t\t\t\t\n\t\t\t\t\tbool hasTagsThis = m.Text.Starts(\"<>\");\n\t\t\t\t\tif (hasTagsThis && !hasTags) { hasTags = true; b.Insert(0, \"<\\x15\\x0\\x4\"); }\n\t\t\t\t\t\n\t\t\t\t\tif (!hasTags) {\n\t\t\t\t\t\tb.Append(\"\\r\\n\");\n\t\t\t\t\t} else if (hasTagsThis) {\n\t\t\t\t\t\tb.Append(\"\\r\\n<\\x15\\x0\\x4\");\n\t\t\t\t\t\t//info: add \"\\r\\n\" here, not later, because later it would make more difficult <lc> tag\n\t\t\t\t\t} else {\n\t\t\t\t\t\tb.Append(hasTagsPrev ? \"\\r\\n<\\x15\\x0\\x4\" : \"\\r\\n\");\n\t\t\t\t\t}\n\t\t\t\t\tb.Append(s);\n\t\t\t\t\thasTagsPrev = hasTagsThis;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (s != null) { //else 0 messages or the last message is Clear\n\t\t\tif (b != null) s = b.ToString();\n\t\t\t\n\t\t\t//limit\n\t\t\tint len = _c.aaaLen8;\n\t\t\tif (len > 10_000_000) {\n\t\t\t\tlen = _c.aaaLineStartFromPos(false, len / 2);\n\t\t\t\tif (len > 0) _c.aaaReplaceRange(false, 0, len, \"...\\r\\n\");\n\t\t\t}\n\t\t\t\n\t\t\tif (hasTags) AddText(s, true, true);\n\t\t\telse _c.aaaAppendText(s, true, true, true);\n\t\t\t\n\t\t\t//test slow client\n\t\t\t//Thread.Sleep(500);\n\t\t\t//print.qm2.write(s.Length / 1048576d);\n\t\t}\n\t\t\n\t\tif (scrollToTop) _c.Call(SCI_SETFIRSTVISIBLELINE);\n\t\t//never mind: more print.it() may be after print.scrollToTop().\n\t}\n\t\n\t/// <summary>\n\t/// Sets or appends styled text.\n\t/// </summary>\n\t/// <param name=\"text\">Text with tags (optionally).</param>\n\t/// <param name=\"append\">Append. Also appends \"\\r\\n\". If false, replaces control text.</param>\n\t/// <param name=\"skipLTGT\">If text starts with \"&lt;&gt;\", skip it.</param>\n\t/// <param name=\"scroll\">Set caret and scroll to the end. If null, does it if <i>append</i> true.</param>\n\tpublic void AddText(string text, bool append, bool skipLTGT, bool? scroll = null, bool dontHideImageTag = false) {\n\t\t//perf.first();\n\t\tif (text.NE() || (skipLTGT && text == \"<>\")) {\n\t\t\tif (append) _c.aaaAppendText(\"\", true, true, true); else _c.aaaClearText();\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tint len = Encoding.UTF8.GetByteCount(text);\n\t\tbyte* buffer = MemoryUtil.Alloc(len * 2 + 8), s = buffer;\n\t\ttry {\n\t\t\tEncoding.UTF8.GetBytes(text, new Span<byte>(buffer, len));\n\t\t\tif (append) { s[len++] = (byte)'\\r'; s[len++] = (byte)'\\n'; }\n\t\t\tif (skipLTGT && s[0] == '<' && s[1] == '>') { s += 2; len -= 2; }\n\t\t\ts[len] = s[len + 1] = 0;\n\t\t\t_AddText(s, len, append, scroll, dontHideImageTag);\n\t\t}\n\t\tfinally {\n\t\t\tMemoryUtil.Free(buffer);\n\t\t}\n\t}\n\t\n\trecord struct _StackItem(byte kind, byte style, int i = 0, string s = null, ushort attrLen = 0); //kind: 0 style, 1 link, 2 fold\n\t\n\tclass _Garbage {\n\t\tpublic readonly List<_StackItem> stack = new();\n\t\tpublic readonly List<StartEnd> codes = new();\n\t\tpublic readonly List<POINT> folds = new();\n\t\tpublic readonly List<(int start, int end, string s)> links = new();\n\t\t\n\t\tpublic void Clear() {\n\t\t\tstack.Clear();\n\t\t\tcodes.Clear();\n\t\t\tfolds.Clear();\n\t\t\tlinks.Clear();\n\t\t}\n\t}\n\t[ThreadStatic] static WeakReference<_Garbage> t_garbage;\n\t\n\tvoid _AddText(byte* s, int len, bool append, bool? scroll, bool dontHideImageTag) {\n\t\t//perf.next();\n\t\tbyte* s0 = s, sEnd = s + len; //source text\n\t\tbyte* t = s0; //destination text (without some tags)\n\t\tbyte* r0 = s0 + (len + 2), r = r0; //destination style bytes\n\t\t\n\t\tint prevStylesCount = _styles.Count;\n\t\tbool hasTags = false;\n\t\tbyte currentStyle = STYLE_DEFAULT;\n\t\tList<StartEnd> images = null;\n\t\t\n\t\tif ((t_garbage ??= new(null)).TryGetTarget(out var m)) m.Clear(); else t_garbage.SetTarget(m = new());\n\t\t\n\t\twhile (s < sEnd) {\n\t\t\t//find '<'\n\t\t\tvar ch = *s++;\n\t\t\tif (ch != '<') {\n\t\t\t\t_Write(ch, currentStyle);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t\n\t\t\tvar tag = s;\n\t\t\t\n\t\t\t//end tag. Support <> and </tag>, but don't care what tag it is.\n\t\t\tif (s[0] == '/') {\n\t\t\t\ts++;\n\t\t\t\tch = *s; if ((char)ch is '+' or '.') s++;\n\t\t\t\twhile (((char)*s).IsAsciiAlpha()) s++;\n\t\t\t\tif (s[0] != '>') goto ge;\n\t\t\t}\n\t\t\tif (s[0] == '>') {\n\t\t\t\tint n = m.stack.Count - 1;\n\t\t\t\tif (n < 0) goto ge; //<> without tag\n\t\t\t\ts++;\n\t\t\t\tvar v = m.stack[n];\n\t\t\t\tif (v.kind is 0 or 1) { //the tag is a style tag or some other styled tag (eg link)\n\t\t\t\t\tif (currentStyle >= STYLE_FIRST_EX && _styles[currentStyle - STYLE_FIRST_EX].Eol) {\n\t\t\t\t\t\tif (*s == '\\r') _Write(*s++, currentStyle);\n\t\t\t\t\t\tif (*s == '\\n') _Write(*s++, currentStyle);\n\t\t\t\t\t}\n\t\t\t\t\tcurrentStyle = v.style;\n\t\t\t\t\tif (v.kind == 1) m.links.Add((v.i, (int)(t - s0), v.s));\n\t\t\t\t} else { //currently can be only 2 for <fold>\n\t\t\t\t\tif (!(s - tag == 6 && new RByte(tag + 1, 4).StartsWith(\"fold\"u8))) goto ge;\n\t\t\t\t\tm.folds.Add((v.i, (int)(t - s0)));\n\t\t\t\t\tm.links.Add((v.i, v.i + v.attrLen, \"\"));\n\t\t\t\t}\n\t\t\t\tm.stack.RemoveAt(n);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t\n\t\t\t//multi-message separator\n\t\t\tif (s[0] == 0x15 && s[1] == 0 && s[2] == 4 && (s - s0 == 1 || s[-2] == 10)) {\n\t\t\t\ts += 3;\n\t\t\t\tif (s[0] == '<' && s[1] == '>') s += 2; //message with tags\n\t\t\t\telse { //one or more messages without tags\n\t\t\t\t\twhile (s < sEnd && !(s[0] == '<' && s[1] == 0x15 && s[2] == 0 && s[3] == 4 && s[-1] == 10)) _Write(*s++, STYLE_DEFAULT);\n\t\t\t\t}\n\t\t\t\tcurrentStyle = STYLE_DEFAULT;\n\t\t\t\tm.stack.Clear();\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t\n\t\t\t//read tag name\n\t\t\tch = *s; if ((char)ch is '_' or '\\a' or '+' or '.') s++;\n\t\t\twhile (((char)*s).IsAsciiAlpha()) s++;\n\t\t\tint tagLen = (int)(s - tag);\n\t\t\tif (tagLen == 0) goto ge;\n\t\t\t\n\t\t\t//read attribute\n\t\t\tbyte* attr = null; int attrLen = 0;\n\t\t\tif (*s == 32) {\n\t\t\t\tvar quot = *(++s);\n\t\t\t\tif ((char)quot is '\\'' or '\"') s++; else quot = (byte)'>'; //never mind: escape sequences \\\\, \\', \\\"\n\t\t\t\tattr = s;\n\t\t\t\twhile (*s != quot && *s != 0) s++;\n\t\t\t\tif (*s == 0) goto ge; //either the end of string or a multi-message separator\n\t\t\t\tattrLen = (int)(s - attr);\n\t\t\t\tif (quot != '>' && *(++s) != '>') goto ge;\n\t\t\t\ts++;\n\t\t\t} else {\n\t\t\t\tif (*s++ != '>') goto ge;\n\t\t\t}\n\t\t\t\n\t\t\t//tags\n\t\t\tTagStyle style = default;\n\t\t\tbool linkTag = false;\n\t\t\tint i2;\n\t\t\tch = *tag;\n\t\t\tvar span = new RByte(tag, tagLen);\n\t\t\tswitch (tagLen << 16 | ch) {\n\t\t\tcase 1 << 16 | 'b':\n\t\t\t\tstyle.Bold = true;\n\t\t\t\tbreak;\n\t\t\tcase 1 << 16 | 'i':\n\t\t\t\tstyle.Italic = true;\n\t\t\t\tbreak;\n\t\t\tcase 2 << 16 | 'b' when tag[1] == 'i':\n\t\t\t\tstyle.Bold = style.Italic = true;\n\t\t\t\tbreak;\n\t\t\tcase 1 << 16 | 'u':\n\t\t\t\tstyle.Underline = true;\n\t\t\t\tbreak;\n\t\t\tcase 1 << 16 | 'c':\n\t\t\tcase 2 << 16 | 'b' when tag[1] == 'c':\n\t\t\tcase 2 << 16 | 'l' when tag[1] == 'c':\n\t\t\tcase 2 << 16 | 'B' when tag[1] == 'C': //fbc\n\t\t\tcase 1 << 16 | 'z' or 1 << 16 | 'Z': //fbc\n\t\t\t\tif (attr == null) goto ge;\n\t\t\t\tint color;\n\t\t\t\tif (((char)*attr).IsAsciiDigit()) color = Api.strtoi(attr);\n\t\t\t\telse if (*attr == '#') color = Api.strtoi(attr + 1, radix: 16);\n\t\t\t\telse {\n\t\t\t\t\tvar c = System.Drawing.Color.FromName(new string((sbyte*)attr, 0, attrLen));\n\t\t\t\t\tif (c.A == 0) break; //invalid color name\n\t\t\t\t\tcolor = c.ToArgb() & 0xffffff;\n\t\t\t\t}\n\t\t\t\tif (ch == 'c') style.Color = color; else style.BackColor = color;\n\t\t\t\tif ((char)ch is 'l' or 'B' or 'Z') style.Eol = true;\n\t\t\t\tbreak;\n\t\t\tcase 4 << 16 | 's' when span.SequenceEqual(\"size\"u8) && attr != null:\n\t\t\t\tstyle.Size = Api.strtoi(attr);\n\t\t\t\tbreak;\n\t\t\tcase 4 << 16 | 'm' when span.SequenceEqual(\"mono\"u8):\n\t\t\t\tstyle.Mono = true;\n\t\t\t\tbreak;\n\t\t\t//case 6 << 16 | 'h' when span.SequenceEqual(\"hidden\"u8): //rejected. Not useful; does not hide newlines.\n\t\t\t//\tstyle.Hidden = true;\n\t\t\t//\tbreak;\n\t\t\tcase 5 << 16 | 'i' when span.SequenceEqual(\"image\"u8) && attr != null:\n\t\t\t\tDebug_.PrintIf(_c.AaImages is null);\n\t\t\t\tfor (var h = tag - 1; h < s; h++) _Write(*h, (byte)(dontHideImageTag ? STYLE_DEFAULT : STYLE_HIDDEN));\n\t\t\t\thasTags = true;\n\t\t\t\tint imageOutPos = (int)((attr - s0) - (s - t));\n\t\t\t\t(images ??= []).Add(new(imageOutPos, imageOutPos + attrLen));\n\t\t\t\tcontinue;\n\t\t\tcase 4 << 16 | 'n' when span.SequenceEqual(\"nonl\"u8):\n\t\t\t\tif (s[0] == 13) s++;\n\t\t\t\tif (s[0] == 10) s++;\n\t\t\t\tcontinue;\n\t\t\tcase 1 << 16 | '_': //<_>text where tags are ignored</_>\n\t\t\tcase 1 << 16 | '\\a': //<\\a>text where tags are ignored</\\a>\n\t\t\t\ti2 = new RByte(s, (int)(sEnd - s)).IndexOf(ch == '_' ? \"</_>\"u8 : \"</\\a>\"u8); if (i2 < 0) goto ge;\n\t\t\t\twhile (i2-- > 0) _Write(*s++, currentStyle);\n\t\t\t\ts += 4;\n\t\t\t\tcontinue;\n\t\t\tcase 4 << 16 | 'c' when span.SequenceEqual(\"code\"u8): //<code>code</code>\n\t\t\t\ti2 = new RByte(s, (int)(sEnd - s)).IndexOf(\"</code>\"u8); if (i2 < 0) goto ge;\n\t\t\t\tif (CodeStylesProvider != null) {\n\t\t\t\t\tint iStartCode = (int)(t - s0);\n\t\t\t\t\tm.codes.Add(new(iStartCode, iStartCode + i2));\n\t\t\t\t\thasTags = true;\n\t\t\t\t}\n\t\t\t\twhile (i2-- > 0) _Write(*s++, STYLE_DEFAULT);\n\t\t\t\ts += 7;\n\t\t\t\tcontinue;\n\t\t\tcase 4 << 16 | 'f' when span.SequenceEqual(\"fold\"u8): //<fold>text</fold>\n\t\t\t\tbool foldHasAttr = attrLen > 0; if (foldHasAttr) attrLen = Math.Min(attrLen, ushort.MaxValue);\n\t\t\t\tm.stack.Add(new(2, 0, (int)(t - s0), attrLen: (ushort)(foldHasAttr ? attrLen : 2)));\n\t\t\t\t//add 'expand/collapse' link in this line\n\t\t\t\tbyte foldStyle = _GetStyleIndex(new TagStyle { Hotspot = true, Underline = true, Color = 0x80FF }, currentStyle, m.stack);\n\t\t\t\tif (!foldHasAttr) _WriteString(\">>\", foldStyle); else for (int i = 0; i < attrLen; i++) _Write(attr[i], foldStyle);\n\t\t\t\t//let the folded text start from next line\n\t\t\t\tvar s1 = s; if (s1[0] == '<' && (char)s1[1] is '_' or '\\a' && s1[2] == '>') s1 += 3;\n\t\t\t\tif (s1[0] is not (10 or 13)) _WriteString(\"\\r\\n\", currentStyle);\n\t\t\t\thasTags = true;\n\t\t\t\tcontinue;\n\t\t\tcase 4 << 16 | 'l' when span.SequenceEqual(\"link\"u8):\n\t\t\tcase 6 << 16 | 'g' when span.SequenceEqual(\"google\"u8):\n\t\t\tcase 4 << 16 | 'h' when span.SequenceEqual(\"help\"u8):\n\t\t\tcase 7 << 16 | 'e' when span.SequenceEqual(\"explore\"u8):\n\t\t\tcase 4 << 16 | 'o' when span.SequenceEqual(\"open\"u8):\n\t\t\tcase 6 << 16 | 's' when span.SequenceEqual(\"script\"u8):\n\t\t\t\tlinkTag = true;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\t//user-defined tag or unknown.\n\t\t\t\t//user-defined tags must start with '+' (links) or '.' (styles).\n\t\t\t\t//don't hide unknown tags, unless start with '+'. Can be either misspelled (hiding would make harder to debug) or not intended for us (forgot <_>).\n\t\t\t\tif (ch == '+') {\n\t\t\t\t\t//if(!_userLinkTags.ContainsKey(new string((sbyte*)tag, 0, tagLen))) goto ge; //no, it makes slower and creates garbage. Also would need to look in the static dictionary too. It's not so important to check now because we use '+' prefix.\n\t\t\t\t\tlinkTag = true;\n\t\t\t\t\tbreak;\n\t\t\t\t} else if (ch == '.' && _FindUserStyle(new(tag, tagLen), out style) == true) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tgoto ge;\n\t\t\t}\n\t\t\t\n\t\t\tif (linkTag) {\n\t\t\t\tif (!(ch == '+' && _FindUserStyle(new(tag, tagLen), out style))) {\n\t\t\t\t\tif (DefaultLinkStyle != null) style = new TagStyle(DefaultLinkStyle);\n\t\t\t\t\telse { style.Color = 0x0080FF; style.Underline = true; }\n\t\t\t\t}\n\t\t\t\tstyle.Hotspot = true;\n\t\t\t}\n\t\t\t\n\t\t\tbyte si = _GetStyleIndex(style, currentStyle, m.stack);\n\t\t\tif (linkTag) {\n\t\t\t\tm.stack.Add(new(1, currentStyle, (int)(t - s0), Encoding.UTF8.GetString(tag, (int)(s - tag - 1))));\n\t\t\t} else {\n\t\t\t\tm.stack.Add(new(0, currentStyle));\n\t\t\t}\n\t\t\tcurrentStyle = si;\n\t\t\t\n\t\t\thasTags = true;\n\t\t\tcontinue;\n\t\t\tge: //invalid format of the tag\n\t\t\t_Write((byte)'<', currentStyle);\n\t\t\ts = tag;\n\t\t}\n\t\t\n\t\tDebug.Assert(t <= s0 + len);\n\t\tDebug.Assert(r <= r0 + len);\n\t\tDebug.Assert(t - s0 == r - r0);\n\t\t*t = 0; len = (int)(t - s0);\n\t\t\n\t\tif (_styles.Count > prevStylesCount) _SetUserStyles(prevStylesCount);\n\t\t\n\t\t//perf.next();\n\t\tint prevLen = append ? _c.aaaLen8 : 0;\n\t\t_c.aaaAddText8_(append, scroll ?? append, s0, len);\n\t\tif (!hasTags) {\n\t\t\t_c.Call(SCI_STARTSTYLING, prevLen);\n\t\t\t_c.Call(SCI_SETSTYLING, len, STYLE_DEFAULT);\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tforeach (var v in m.links) {\n\t\t\t_c.AaRangeDataAdd(false, (prevLen + v.start)..(prevLen + v.end), v.s);\n\t\t}\n\t\t\n\t\tfor (int i = m.folds.Count; --i >= 0;) { //need reverse for nested folds\n\t\t\tvar v = m.folds[i];\n\t\t\tint lineStart = _c.Call(SCI_LINEFROMPOSITION, v.x + prevLen), lineEnd = _c.Call(SCI_LINEFROMPOSITION, v.y + prevLen);\n\t\t\tint level = _c.Call(SCI_GETFOLDLEVEL, lineStart) & SC_FOLDLEVELNUMBERMASK;\n\t\t\t_c.Call(SCI_SETFOLDLEVEL, lineStart, level | SC_FOLDLEVELHEADERFLAG);\n\t\t\tfor (int j = lineStart + 1; j <= lineEnd; j++) _c.Call(SCI_SETFOLDLEVEL, j, level + 1);\n\t\t\t_c.Call(SCI_FOLDLINE, lineStart);\n\t\t}\n\t\t\n\t\tint endStyled = 0;\n\t\tforeach (var v in m.codes) {\n\t\t\t_StyleRangeTo(v.start);\n\t\t\tvar code = Encoding.UTF8.GetString(s0 + v.start, v.Length);\n\t\t\t//print.qm2.write(v, code);\n\t\t\tvar b = CodeStylesProvider(code);\n\t\t\t_c.Call(SCI_STARTSTYLING, v.start + prevLen);\n\t\t\tfixed (byte* p = b) _c.Call(SCI_SETSTYLINGEX, b.Length, p);\n\t\t\tendStyled = v.end;\n\t\t}\n\t\t\n\t\t_StyleRangeTo(len);\n\t\t//perf.next();\n\t\t//print.qm2.write(perf.ToString());\n\t\t\n\t\tif (images != null) _c.AaImages?.SetImagesForTextRange_(new(s0, len), images, prevLen);\n\t\t\n\t\tvoid _StyleRangeTo(int to) {\n\t\t\tif (endStyled < to) {\n\t\t\t\t_c.Call(SCI_STARTSTYLING, endStyled + prevLen);\n\t\t\t\t_c.Call(SCI_SETSTYLINGEX, to - endStyled, r0 + endStyled);\n\t\t\t}\n\t\t}\n\t\t\n\t\tvoid _Write(byte ch, byte style) {\n\t\t\t//print.qm2.write($\"{ch} {style}\");\n\t\t\t*t++ = ch; *r++ = style;\n\t\t}\n\t\t\n\t\tvoid _WriteString(string ss, byte style) {\n\t\t\tfor (int i_ = 0; i_ < ss.Length; i_++) _Write((byte)ss[i_], style);\n\t\t}\n\t}\n\t\n\tbyte _GetStyleIndex(TagStyle style, byte currentStyle, List<_StackItem> stack) {\n\t\t//merge nested style with ancestors\n\t\tif (currentStyle >= STYLE_FIRST_EX) style.Merge(_styles[currentStyle - STYLE_FIRST_EX]);\n\t\tfor (int j = stack.Count; --j > 0;) {\n\t\t\tvar v = stack[j];\n\t\t\tif (v.kind is not (0 or 1)) continue; //a non-styled tag\n\t\t\tif (v.style >= STYLE_FIRST_EX) style.Merge(_styles[v.style - STYLE_FIRST_EX]);\n\t\t}\n\t\t\n\t\t//find or add style\n\t\tint i, n = _styles.Count;\n\t\tfor (i = 0; i < n; i++) if (_styles[i].Equals(style)) break;\n\t\tif (i == NUM_STYLES_EX) {\n\t\t\ti = currentStyle;\n\t\t\t//CONSIDER: overwrite old styles added in previous calls. Now we just clear styles when control text cleared.\n\t\t} else {\n\t\t\tif (i == n) _styles.Add(style);\n\t\t\ti += STYLE_FIRST_EX;\n\t\t}\n\t\treturn (byte)i;\n\t}\n\t\n\t/// <summary>\n\t/// Called on SCN_HOTSPOTRELEASECLICK.\n\t/// </summary>\n\tinternal void OnLinkClick_(int pos, bool ctrl) {\n\t\tif (keys.gui.isAlt) return;\n\t\tif (!GetLinkFromPos(pos, out var tag, out var attr)) return;\n\t\t//process it async, because bad things happen if now we remove focus or change control text etc\n\t\t_c.Dispatcher.InvokeAsync(() => OnLinkClick(tag, attr));\n\t}\n\t\n\tpublic bool GetLinkFromPos(int pos, out string tag, out string attr) {\n\t\ttag = attr = null;\n\t\tif (pos >= 0 && _c.AaRangeDataGet(false, pos, out string s, out int from, out int to)) {\n\t\t\tif (s.Length == 0) { //<fold>\n\t\t\t\t_c.Call(SCI_TOGGLEFOLD, _c.Call(SCI_LINEFROMPOSITION, pos));\n\t\t\t} else if (s.RxMatch(@\"(?s)^(\\+?\\w+)(?| '([^']*)'| \"\"([^\"\"]*)\"\"| ([^>]*))?$\", out var m)) {\n\t\t\t\ttag = m[1].Value;\n\t\t\t\tattr = m[2].Value ?? _c.aaaRangeText(false, from, to);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\t\n\t//note: attr can be \"\"\n\tpublic void OnLinkClick(string tag, string attr) {\n\t\t//print.it($\"'{tag}'  '{attr}'\");\n\t\t\n\t\tif (_userLinkTags.TryGetValue(tag, out var d) || s_userLinkTags.TryGetValue(tag, out d)) {\n\t\t\td.Invoke(attr);\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tvar a = attr.Split('|');\n\t\tbool one = a.Length == 1;\n\t\tstring s1 = a[0], s2 = one ? null : a[1];\n\t\t\n\t\tswitch (tag) {\n\t\tcase \"link\":\n\t\t\trun.itSafe(s1, s2);\n\t\t\tbreak;\n\t\tcase \"help\":\n\t\t\tHelpUtil.AuHelp(attr);\n\t\t\tbreak;\n\t\tcase \"explore\":\n\t\t\trun.selectInExplorer(attr);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\t//case \"open\": case \"script\": case \"google\": //the control recognizes but cannot implement these. The lib user can implement.\n\t\t\t//others are unregistered tags. Only if start with '+' (others are displayed as text).\n\t\t\tif (opt.warnings.Verbose) dialog.showWarning(\"Debug\", \"Tag '\" + tag + \"' is not implemented.\\nUse SciTags.AddCommonLinkTag or SciTags.AddLinkTag.\");\n\t\t\tbreak;\n\t\t}\n\t}\n\t\n\tpublic UserDefinedStyle DefaultLinkStyle { get; set; }\n\t\n\treadonly Dictionary<string, Action<string>> _userLinkTags = new();\n\tstatic readonly ConcurrentDictionary<string, Action<string>> s_userLinkTags = new();\n\t\n\t/// <summary>\n\t/// Adds (registers) a user-defined link tag for this control.\n\t/// </summary>\n\t/// <param name=\"name\">\n\t/// Tag name, like \"+myTag\".\n\t/// Must start with '+'. Other characters must be 'a'-'z', 'A'-'Z'. Case-sensitive.\n\t/// Or can be one of predefined link tags, if you want to override or implement it (some are not implemented by the control).\n\t/// If already exists, replaces the delegate.\n\t/// </param>\n\t/// <param name=\"a\">\n\t/// A delegate of a callback function (probably you'll use a lambda) that is called on link click.\n\t/// It's string parameter contains tag's attribute (if \"&lt;name \"attribute\"&gt;TEXT&lt;&gt;) or link text (if \"&lt;name&gt;TEXT&lt;&gt;).\n\t/// The function is called in control's thread. The mouse button is already released. It is safe to do anything with the control, eg replace text.\n\t/// </param>\n\t/// <param name=\"style\">Style for this link tag. If null, uses <see cref=\"DefaultLinkStyle\"/>.</param>\n\t/// <remarks>\n\t/// Call this function when control handle is already created. Until that <see cref=\"KScintilla.AaTags\"/> returns null.\n\t/// </remarks>\n\t/// <seealso cref=\"AddCommonLinkTag\"/>\n\tpublic void AddLinkTag(string name, Action<string> a, UserDefinedStyle style = null) {\n\t\t_userLinkTags[name] = a;\n\t\tif (style != null) _AddUserStyle(name, style);\n\t}\n\t\n\tpublic bool HasLinkTag(string name) => _userLinkTags.ContainsKey(name);\n\t\n\t/// <summary>\n\t/// Adds (registers) a user-defined link tag for all controls.\n\t/// </summary>\n\t/// <inheritdoc cref=\"AddLinkTag\" path=\"/param\"/>\n\t/// <seealso cref=\"AddLinkTag\"/>\n\tpublic static void AddCommonLinkTag(string name, Action<string> a) {\n\t\ts_userLinkTags[name] = a;\n\t}\n\t\n\tpublic static bool HasCommonLinkTag(string name) => s_userLinkTags.ContainsKey(name);\n\t\n\t/// <summary>\n\t/// Adds (registers) a user-defined style tag for this control.\n\t/// </summary>\n\t/// <param name=\"name\">\n\t/// Tag name, like \".my\".\n\t/// Must start with '.'. Other characters must be 'a'-'z', 'A'-'Z'. Case-sensitive.\n\t/// </param>\n\t/// <param name=\"style\"></param>\n\t/// <exception cref=\"ArgumentException\">name does not start with '.'.</exception>\n\t/// <exception cref=\"InvalidOperationException\">Trying to add more than 100 styles.</exception>\n\t/// <remarks>\n\t/// Call this function when control handle is already created. Until that <see cref=\"KScintilla.AaTags\"/> returns null.\n\t/// \n\t/// Does nothing if the style is already added.\n\t/// </remarks>\n\tpublic void AddStyleTag(string name, UserDefinedStyle style) {\n\t\tif (!name.Starts('.')) throw new ArgumentException();\n\t\t_AddUserStyle(name, style);\n\t}\n\t\n\tpublic void _AddUserStyle(string name, UserDefinedStyle style) {\n\t\t_userStyles ??= new();\n\t\tif (_userStyles.Count >= 100) throw new InvalidOperationException();\n\t\tvar u8 = name.ToUTF8();\n\t\tif (!_FindUserStyle(u8, out _)) _userStyles.Add((u8, new(style)));\n\t}\n\tList<(byte[] name, TagStyle style)> _userStyles;\n\t\n\t[MethodImpl(MethodImplOptions.AggressiveOptimization)]\n\tbool _FindUserStyle(RByte tag, out TagStyle ts) {\n\t\tif (_userStyles is { } a) {\n\t\t\tforeach (ref var v in a.AsSpan()) {\n\t\t\t\tif (v.name.SequenceEqual(tag)) { ts = v.style; return true; }\n\t\t\t}\n\t\t}\n\t\tts = default;\n\t\treturn false;\n\t}\n\t\n\tpublic Func<string, byte[]> CodeStylesProvider;\n\t\n\t//FUTURE: add control-tags, like <clear> (clear output), <scroll> (ensure line visible), <mark x> (add some marker etc).\n\t//FUTURE: let our links be accessible objects.\n}\n"
  },
  {
    "path": "Au.Controls/KScintilla/SciTextBuilder.cs",
    "content": "namespace Au.Controls;\n\nusing static Sci;\n\npublic class SciTextBuilder {\n\treadonly StringBuilder _b = new();\n\treadonly List<(int marker, int pos)> _markers = new();\n\treadonly List<(int indic, int start, int end, int value)> _indicators = new();\n\treadonly List<(int indic, int start, int end, object data)> _links = new();\n\treadonly List<(int style, int start, int end)> _styles = new();\n\treadonly List<SciFoldPoint> _folding = new();\n\tStack<(int indic, int start, int value)> _stackIndicator = new();\n\tStack<(int indic, int start, object data)> _stackLink = new();\n\tStack<(int style, int start)> _stackStyle = new();\n\n\tpublic void Clear() {\n\t\t_b.Clear();\n\t\t_markers.Clear();\n\t\t_indicators.Clear();\n\t\t_links.Clear();\n\t\t_styles.Clear();\n\t\t_folding.Clear();\n\t\t_stackIndicator.Clear();\n\t\t_stackLink.Clear();\n\t\t_stackStyle.Clear();\n\t}\n\n\tpublic unsafe void Apply(KScintilla sci) {\n\t\tvar s = _b.ToString();\n\t\tsci.aaaSetText(s, ignoreTags: true);\n\t\tif (_styles.Count > 0) {\n\t\t\t//code like in GetScintillaStylingBytes\n\t\t\tvar styles8 = new byte[Encoding.UTF8.GetByteCount(s)];\n\t\t\tvar map8 = styles8.Length == s.Length ? null : Convert2.Utf8EncodeAndGetOffsets_(s).offsets;\n\t\t\tforeach (var v in _styles) {\n\t\t\t\tint i = v.start, end = v.end;\n\t\t\t\tif (map8 != null) { i = map8[i]; end = map8[end]; }\n\t\t\t\twhile (i < end) styles8[i++] = (byte)v.style;\n\t\t\t}\n\t\t\tsci.Call(SCI_STARTSTYLING);\n\t\t\tfixed (byte* p = styles8) sci.Call(SCI_SETSTYLINGEX, styles8.Length, p);\n\t\t}\n\t\tforeach (var v in _markers) {\n\t\t\tsci.aaaMarkerAdd(v.marker, true, v.pos);\n\t\t}\n\t\tforeach (var v in _indicators.AsSpan()) {\n\t\t\tsci.aaaIndicatorAdd(v.indic, true, v.start..v.end, v.value);\n\t\t}\n\t\tforeach (var v in _links.AsSpan()) {\n\t\t\tvar (start, end) = sci.aaaNormalizeRange(true, v.start..v.end);\n\t\t\tif (v.indic < 0) {\n\t\t\t\tsci.aaaIndicatorAdd(-v.indic, false, start..end);\n\t\t\t\tsci.aaaIndicatorAdd(-v.indic + 1, false, start..end);\n\t\t\t} else {\n\t\t\t\tsci.aaaIndicatorAdd(v.indic, false, start..end);\n\t\t\t}\n\t\t\tsci.AaRangeDataAdd(false, start..end, v.data);\n\t\t}\n\t\tif (_folding.Count > 0) sci.aaaFoldingApply(_folding);\n\t}\n\n\t/// <summary>\n\t/// Gets current text length.\n\t/// </summary>\n\tpublic int Length => _b.Length;\n\n\t/// <summary>\n\t/// Adds text.\n\t/// </summary>\n\tpublic SciTextBuilder Text(RStr text) {\n\t\t_b.Append(text);\n\t\treturn this;\n\t}\n\n\t/// <summary>\n\t/// Adds <c>\"\\r\\n\"</c>.\n\t/// </summary>\n\tpublic SciTextBuilder NL() {\n\t\t_b.AppendLine();\n\t\treturn this;\n\t}\n\n\t/// <summary>\n\t/// Adds marker in current or previous line.\n\t/// </summary>\n\t/// <param name=\"prevLine\">Add at the line of position <c>Length - 2</c>.</param>\n\tpublic SciTextBuilder Marker(int marker, bool prevLine = false) {\n\t\t_markers.Add((marker, Length - (prevLine ? 2 : 0)));\n\t\treturn this;\n\t}\n\n\t/// <summary>\n\t/// Adds indicator in a text range.\n\t/// </summary>\n\t/// <param name=\"indic\">Indicator index, 0-31.</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\"><i>range</i> is not within current text.</exception>\n\tpublic SciTextBuilder Indic(int indic, int start, int end, int value = 1) {\n\t\t_indicators.Add((indic, start, end, value));\n\t\treturn this;\n\t}\n\n\t/// <summary>\n\t/// Adds text with indicator.\n\t/// </summary>\n\t/// <param name=\"indic\">Indicator index, 0-31.</param>\n\tpublic SciTextBuilder Indic(int indic, RStr text, int value = 1) {\n\t\tint start = Length;\n\t\t_b.Append(text);\n\t\treturn Indic(indic, start, Length, value);\n\t}\n\n\t/// <summary>\n\t/// Starts indicator range from current text length. Later use <see cref=\"Indic_\"/> to end it.\n\t/// </summary>\n\t/// <param name=\"indic\">Indicator index, 0-31.</param>\n\tpublic SciTextBuilder Indic(int indic, int value = 1) {\n\t\t_stackIndicator.Push((indic, Length, value));\n\t\treturn this;\n\t}\n\n\t/// <summary>\n\t/// Ends indicator range started by <see cref=\"Indic(int, int)\"/>.\n\t/// </summary>\n\tpublic SciTextBuilder Indic_() {\n\t\tvar v = _stackIndicator.Pop();\n\t\treturn Indic(v.indic, v.start, Length, v.value);\n\t}\n\n\t/// <summary>\n\t/// Adds link in a text range.\n\t/// </summary>\n\t/// <param name=\"indic\">Indicator index. If negative, will be set 2 indicators: -value and -value+1.</param>\n\t/// <exception cref=\"ArgumentOutOfRangeException\"><i>range</i> is not within current text.</exception>\n\tpublic SciTextBuilder Link(object data, int start, int end, int indic) {\n\t\t_links.Add((indic, start, end, data));\n\t\treturn this;\n\t}\n\n\t/// <summary>\n\t/// Adds link in a text range.\n\t/// </summary>\n\tpublic SciTextBuilder Link(object data, RStr text, int indic) {\n\t\tint start = Length;\n\t\t_b.Append(text);\n\t\treturn Link(data, start, Length, indic);\n\t}\n\n\t/// <summary>\n\t/// Starts link range from current text length. Later use <see cref=\"Link_\"/> to end it.\n\t/// </summary>\n\tpublic SciTextBuilder Link(object data, int indic) {\n\t\t_stackLink.Push((indic, Length, data));\n\t\treturn this;\n\t}\n\n\t/// <summary>\n\t/// Ends link range started by <see cref=\"Link(object, int)\"/>.\n\t/// </summary>\n\tpublic SciTextBuilder Link_() {\n\t\tvar v = _stackLink.Pop();\n\t\treturn Link(v.data, v.start, Length, v.indic);\n\t}\n\n\t/// <summary>\n\t/// Adds style in a text range.\n\t/// </summary>\n\t/// <exception cref=\"ArgumentOutOfRangeException\"><i>range</i> is not within current text.</exception>\n\tpublic SciTextBuilder Style(int style, int start, int end) {\n\t\t_styles.Add((style, start, end));\n\t\treturn this;\n\t}\n\n\t/// <summary>\n\t/// Adds text with style.\n\t/// </summary>\n\tpublic SciTextBuilder Style(int style, RStr text) {\n\t\tint start = Length;\n\t\t_b.Append(text);\n\t\treturn Style(style, start, Length);\n\t}\n\n\t/// <summary>\n\t/// Starts style range from current text length. Later use <see cref=\"Style_\"/> to end it.\n\t/// </summary>\n\tpublic SciTextBuilder Style(int style) {\n\t\t_stackStyle.Push((style, Length));\n\t\treturn this;\n\t}\n\n\t/// <summary>\n\t/// Ends style range started by <see cref=\"Style(int)\"/>.\n\t/// </summary>\n\tpublic SciTextBuilder Style_() {\n\t\tvar v = _stackStyle.Pop();\n\t\treturn Style(v.style, v.start, Length);\n\t}\n\n\t/// <summary>\n\t/// Adds a folding start or end point.\n\t/// </summary>\n\tpublic SciTextBuilder Fold(SciFoldPoint fp) {\n\t\t_folding.Add(fp);\n\t\treturn this;\n\t}\n\n\tpublic int BoldStyle { get; set; }\n\tpublic SciTextBuilder B(int start, int end) => Style(BoldStyle, start, end);\n\tpublic SciTextBuilder B(RStr text) => Style(BoldStyle, text);\n\tpublic SciTextBuilder B() => Style(BoldStyle);\n\tpublic SciTextBuilder B_() => Style_();\n\n\tpublic int GrayStyle { get; set; }\n\tpublic SciTextBuilder Gray(int start, int end) => Style(GrayStyle, start, end);\n\tpublic SciTextBuilder Gray(RStr text) => Style(GrayStyle, text);\n\tpublic SciTextBuilder Gray() => Style(GrayStyle);\n\tpublic SciTextBuilder Gray_() => Style_();\n\n\tpublic int GreenStyle { get; set; }\n\tpublic SciTextBuilder Green(int start, int end) => Style(GreenStyle, start, end);\n\tpublic SciTextBuilder Green(RStr text) => Style(GreenStyle, text);\n\tpublic SciTextBuilder Green() => Style(GreenStyle);\n\tpublic SciTextBuilder Green_() => Style_();\n\n\tpublic int LinkIndic { get; set; }\n\tpublic SciTextBuilder Link(object data, int start, int end) => Link(data, start, end, LinkIndic);\n\tpublic SciTextBuilder Link(object data, RStr text) => Link(data, text, LinkIndic);\n\tpublic SciTextBuilder Link(object data) => Link(data, LinkIndic);\n\n\tpublic int Link2Indic { get; set; }\n\tpublic SciTextBuilder Link2(object data, int start, int end) => Link(data, start, end, Link2Indic);\n\tpublic SciTextBuilder Link2(object data, RStr text) => Link(data, text, Link2Indic);\n\tpublic SciTextBuilder Link2(object data) => Link(data, Link2Indic);\n\n\t/// <summary>\n\t/// Callers can use this as they want. Not used by this class.\n\t/// </summary>\n\tpublic (int i, object o) user;\n}\n"
  },
  {
    "path": "Au.Controls/KScintilla/other/KSciInfoBox.cs",
    "content": "using System.Windows;\nusing System.Windows.Controls;\n\nnamespace Au.Controls;\n\n/// <summary>\n/// Scintilla-based control to show formatted information text.\n/// To set text use the <see cref=\"aaaText\"/> property. For formatting and links use tags: <see cref=\"SciTags\"/>.\n/// </summary>\npublic class KSciInfoBox : KScintilla {\n\tpublic KSciInfoBox() {\n\t\tAaInitReadOnlyAlways = true;\n\t\tAaInitTagsStyle = AaTagsStyle.AutoAlways;\n\t\tAaInitImages = true;\n\t\tAaInitUseDefaultContextMenu = true;\n\t\tAaInitWrapVisuals = false;\n\t\tAaWrapLines = true;\n\t\t//TabStop = false;\n\t\tName = \"info\";\n\t}\n\n\tprotected override void AaOnHandleCreated() {\n\t\tbase.AaOnHandleCreated();\n\n\t\taaaStyleBackColor(Sci.STYLE_DEFAULT, 0xf8fff0);\n\t\tif (AaInitUseSystemFont) aaaStyleFont(Sci.STYLE_DEFAULT); //Segoe UI 9 is narrower but taller than the default Verdana 8. Also tested Calibri 9, but Verdana looks better.\n\t\taaaStyleClearAll();\n\n\t\taaaMarginSetWidth(1, 0);\n\t\taaaMarginSetWidth(-1, AaInitBlankMargins.left);\n\t\taaaMarginSetWidth(-2, AaInitBlankMargins.right);\n\t}\n\n\t/// <summary>\n\t/// Use font Segoe UI 9 instead of the default font Verdana 8.\n\t/// </summary>\n\tpublic bool AaInitUseSystemFont { get; set; }\n\n\t/// <summary>\n\t/// The width of the blank margin on both sides of the text. Logical pixels.\n\t/// </summary>\n\tpublic (int left, int right) AaInitBlankMargins { get; set; } = (1, 1);\n\n\t//protected override bool IsInputKey(Keys keyData) {\n\t//\tswitch (keyData & Keys.KeyCode) { case Keys.Tab: case Keys.Escape: case Keys.Enter: return false; }\n\t//\treturn base.IsInputKey(keyData);\n\t//}\n\n\t/// <summary>\n\t/// Sets element's tooltip text to show in this control instead of standard tooltip popup.\n\t/// Uses <b>ToolTip</b> property; don't overwrite it.\n\t/// </summary>\n\tpublic void AaAddElem(FrameworkElement c, string text) {\n\t\tc.ToolTip = text;\n\t\tc.ToolTipOpening += (o, e) => {\n\t\t\te.Handled = true;\n\t\t\tif (_suspendElems != 0) {\n\t\t\t\tif (Environment.TickCount64 < _suspendElems) return;\n\t\t\t\t_suspendElems = 0;\n\t\t\t}\n\t\t\tthis.aaaText = (o as FrameworkElement).ToolTip as string;\n\t\t};\n\t\tif (c is TextBox or ComboBox) c.GotKeyboardFocus += (o, _) => { this.aaaText = (o as FrameworkElement).ToolTip as string; };\n\t}\n\n\t/// <summary>\n\t/// Temporarily suspends showing tooltips of elements in this control. See <see cref=\"AaAddElem\"/>.\n\t/// </summary>\n\t/// <param name=\"timeMS\">Suspend for this time interval, ms. If 0, resumes.</param>\n\tpublic void AaSuspendElems(long timeMS = 3000) {\n\t\t_suspendElems = Environment.TickCount64 + timeMS;\n\t}\n\tlong _suspendElems;\n\n\t///\n\tpublic bool AaElemsSuspended => _suspendElems != 0 && Environment.TickCount64 < _suspendElems;\n\t\n\t/// <inheritdoc cref=\"KScintilla.aaaText\"/>\n\tpublic new string aaaText {\n\t\tget => base.aaaText;\n\t\tset {\n\t\t\tif (value == _text) return; //prevent scrolling to the top\n\t\t\tbase.aaaText = _text = value;\n\t\t}\n\t}\n\tstring _text;\n}\n"
  },
  {
    "path": "Au.Controls/KTreeView/KTreeView.cs",
    "content": "using System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Input;\n\nnamespace Au.Controls;\n\npublic unsafe partial class KTreeView {\n\t_VisibleItem[] _avi;\n\tDictionary<ITreeViewItem, int> _dvi; //for IndexOf\n\tint _width, _height, _itemHeight, _itemLineHeight, _imageSize, _imageMarginX, _marginLeft, _marginRight, _itemsWidth, _dpi;\n\tint _focusedIndex, _hotIndex;\n\t(int indexPlus1, bool scrollTop) _ensureVisible;\n\tNativeScrollbar_ _vscroll, _hscroll;\n\t\n\tstruct _VisibleItem {\n\t\tpublic ITreeViewItem item;\n\t\tpublic int measured;\n\t\tpublic ushort level;\n\t\tpublic TVParts noParts;\n\t\tpublic bool isSelected;\n\t\t\n\t\tpublic bool Select(bool on) {\n\t\t\tif (on && !item.IsSelectable) return false;\n\t\t\tisSelected = on;\n\t\t\treturn true;\n\t\t}\n\t}\n\t\n\t///\n\tpublic KTreeView() {\n\t\t//UseLayoutRounding = true;\n\t\tFocusable = true;\n\t\tFocusVisualStyle = null;\n\t\t_focusedIndex = _hotIndex = -1;\n\t\t_ScrollInit();\n\t}\n\t\n\t#region size, dpi, set visible items, root/index/count properties\n\t\n\tvoid _SetDpiAndItemSize(int dpi) {\n\t\t_dpi = dpi;\n\t\t_imageSize = _DpiScale(_logicalImageSize);\n\t\t_imageMarginX = _DpiScale(4);\n\t\t_marginLeft = _DpiScale(ItemMarginLeft);\n\t\t_marginRight = _DpiScale(ItemMarginRight);\n\t\t\n\t\tusing var tr = new GdiTextRenderer(dpi);\n\t\t_itemHeight = _itemLineHeight = Math.Max(_imageSize, tr.MeasureText(\"A\").height) + _DpiScale(2);\n\t\tif (CustomItemHeightAddPercent > 0) _itemHeight += Math2.PercentToValue(_itemHeight, CustomItemHeightAddPercent, true);\n\t}\n\t\n\t///\n\tprotected override void OnDpiChanged(DpiScale oldDpi, DpiScale newDpi) {\n\t\tif (_hasHwnd) {\n\t\t\t_SetDpiAndItemSize(newDpi.PixelsPerInchY.ToInt()); //don't use Dpi.OfWindow(this), it's invalid when reparenting\n\t\t\t_MeasureClear(false);\n\t\t}\n\t\tbase.OnDpiChanged(oldDpi, newDpi);\n\t}\n\t\n\t///\n\tpublic int Dpi => _dpi;\n\t\n\t/// <summary>\n\t/// Logical image size.\n\t/// Can be changed before or after creating handle.\n\t/// </summary>\n\tpublic int ImageSize {\n\t\tget => _logicalImageSize;\n\t\tset {\n\t\t\tif (value == _logicalImageSize) return;\n\t\t\t_logicalImageSize = value;\n\t\t\tif (_hasHwnd) {\n\t\t\t\t_SetDpiAndItemSize(_dpi);\n\t\t\t\t_MeasureClear(true);\n\t\t\t}\n\t\t}\n\t}\n\tint _logicalImageSize = 16;\n\t\n\tvoid _SetVisibleItems(bool init) {\n\t\tbool wasEmpty = _avi.NE_();\n\t\t_hotIndex = -1;\n\t\t_ensureVisible = default;\n\t\t\n\t\tint n = _itemsSource == null ? 0 : _CountVisible(_itemsSource);\n\t\tstatic int _CountVisible(IEnumerable<ITreeViewItem> a) {\n\t\t\tint r = 0;\n\t\t\tforeach (var v in a) {\n\t\t\t\tr++;\n\t\t\t\tif (v.IsExpanded) r += _CountVisible(v.Items);\n\t\t\t}\n\t\t\treturn r;\n\t\t} //TODO3: now calls v.Items 2 times (_CountVisible and _AddVisible). It can be expensive.\n\t\t\n\t\tif (n == 0) {\n\t\t\t_avi = [];\n\t\t\t_dvi = null;\n\t\t\t_focusedIndex = -1;\n\t\t\t_itemsWidth = 0;\n\t\t\tif (wasEmpty) return;\n\t\t} else {\n\t\t\tList<ITreeViewItem> selected = null; ITreeViewItem focused = null;\n\t\t\tif (!(init || wasEmpty)) {\n\t\t\t\tselected = SelectedItems;\n\t\t\t\tfocused = FocusedItem;\n\t\t\t}\n\t\t\t_focusedIndex = -1;\n\t\t\t_itemsWidth = 0;\n\t\t\t\n\t\t\t_avi = new _VisibleItem[n];\n\t\t\t_dvi = new Dictionary<ITreeViewItem, int>(n);\n\t\t\t_AddVisible(_itemsSource, 0, 0);\n\t\t\tint _AddVisible(IEnumerable<ITreeViewItem> a, int i, int level) {\n\t\t\t\tforeach (var v in a) {\n\t\t\t\t\t_avi[i] = new _VisibleItem { item = v, level = (ushort)level };\n\t\t\t\t\t_dvi.Add(v, i);\n\t\t\t\t\ti++;\n\t\t\t\t\tif (v.IsExpanded) i = _AddVisible(v.Items, i, level + 1);\n\t\t\t\t}\n\t\t\t\treturn i;\n\t\t\t}\n\t\t\t\n\t\t\tif (selected != null) {\n\t\t\t\tforeach (var v in selected) {\n\t\t\t\t\tint i = IndexOf(v); if (i < 0) continue;\n\t\t\t\t\t_avi[i].Select(true);\n\t\t\t\t\tif (v == focused) _focusedIndex = i;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (_hasHwnd) {\n\t\t\tif (init && _vscroll.Pos > 0) _vscroll.Pos = 0;\n\t\t\t\n\t\t\t_Measure();\n\t\t\t_Invalidate();\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Sets (adds, replaces or removes) all items.\n\t/// </summary>\n\t/// <param name=\"items\">Items at tree root. Can be null.</param>\n\t/// <param name=\"modified\">true when adding/removing one or more items in same tree/list. Preserves selection, scroll position, etc.</param>\n\tpublic void SetItems(IEnumerable<ITreeViewItem> items, bool modified = false) {\n\t\t_itemsSource = items;\n\t\t_SetVisibleItems(!modified);\n\t}\n\tIEnumerable<ITreeViewItem> _itemsSource;\n\t\n\t/// <summary>\n\t/// Gets the number of visible items.\n\t/// </summary>\n\tpublic int CountVisible => _avi.Lenn_();\n\t\n\t/// <summary>\n\t/// Gets item index in visible items.\n\t/// Returns -1 if not found.\n\t/// </summary>\n\t/// <param name=\"item\">Can be null.</param>\n\tpublic int IndexOf(ITreeViewItem item) {\n\t\tif (item != null && _dvi != null && _dvi.TryGetValue(item, out int i)) return i;\n\t\treturn -1;\n\t}\n\t\n\t/// <summary>\n\t/// Gets item index in visible items.\n\t/// Returns -1 if not found.\n\t/// </summary>\n\tpublic int IndexOf(Func<ITreeViewItem, bool> func) {\n\t\tif (!_avi.NE_()) {\n\t\t\tfor (int i = 0; i < _avi.Length; i++) {\n\t\t\t\tif (func(_avi[i].item)) return i;\n\t\t\t}\n\t\t}\n\t\treturn -1;\n\t}\n\t\n\t/// <summary>\n\t/// Gets visible item at index.\n\t/// </summary>\n\t/// <exception cref=\"IndexOutOfRangeException\"></exception>\n\t/// <exception cref=\"NullReferenceException\">Items not added.</exception>\n\tpublic ITreeViewItem this[int index] => _avi[index].item;\n\t\n\t#endregion\n\t\n\t#region scroll, top index, expand folder, ensure visible\n\t\n\tvoid _ScrollInit() {\n\t\t_vscroll = new(true, i => i * _itemHeight, i => (i + 1) * _itemHeight);\n\t\t_hscroll = new(false, i => i, i => i + 1);\n\t\t\n\t\t_vscroll.PosChanged += (sb, part) => {\n\t\t\t_hotIndex = -1;\n\t\t\t_Measure(true);\n\t\t\t_Invalidate();\n\t\t\tif (part <= -2) _OnMouseMoveOrScroll(true); //wheel, not scrollbar or keyboard\n\t\t};\n\t\t\n\t\t_hscroll.PosChanged += (sb, part) => {\n\t\t\t_Invalidate();\n\t\t};\n\t}\n\t\n\t///\n\tprotected override void OnMouseWheel(MouseWheelEventArgs e) {\n\t\te.Handled = true;\n\t\tif (!_avi.NE_() && _vscroll.Visible) _vscroll.WndProc(_w, Api.WM_MOUSEWHEEL, Math2.MakeLparam(0, e.Delta), 0);\n\t\tbase.OnMouseWheel(e);\n\t}\n\t\n\t/// <summary>\n\t/// Gets or sets index of the first item in the scroll view; it is the value of the vertical scrollbar.\n\t/// The <c>set</c> function scrolls if need. Clamps if invalid index.\n\t/// </summary>\n\tpublic int TopIndex {\n\t\tget => _vscroll.Pos;\n\t\tset { _vscroll.Pos = value; }\n\t}\n\t\n\t/// <summary>\n\t/// Expands or collapses folder.\n\t/// </summary>\n\t/// <param name=\"index\"></param>\n\t/// <param name=\"expand\">If null, toggles.</param>\n\t/// <exception cref=\"InvalidOperationException\">Not folder. Or control not created.</exception>\n\t/// <exception cref=\"IndexOutOfRangeException\"></exception>\n\tpublic void Expand(int index, bool? expand) {\n\t\tif (!_hasHwnd) throw new InvalidOperationException();\n\t\tif (!_IndexToItem(index, out var item)) throw new IndexOutOfRangeException();\n\t\tif (!item.IsFolder) throw new InvalidOperationException();\n\t\tbool wasExp = item.IsExpanded;\n\t\tbool exp = expand == null ? !wasExp : expand.Value;\n\t\tif (exp == wasExp) return;\n\t\tif (!exp && _focusedIndex > index && _IsInside(index, _focusedIndex)) _avi[_focusedIndex = index].Select(true); //select/focus item if a descendant is focused\n\t\titem.SetIsExpanded(exp);\n\t\t_SetVisibleItems(init: false);\n\t}\n\t\n\t/// <summary>\n\t/// Expands or collapses folder.\n\t/// </summary>\n\t/// <param name=\"item\"></param>\n\t/// <param name=\"expand\">If null, toggles.</param>\n\t/// <exception cref=\"InvalidOperationException\">Not folder. Or control not created.</exception>\n\t/// <exception cref=\"ArgumentException\"><i>item</i> is not a visible item in this control. No exception if <i>expand</i> == false.</exception>\n\tpublic void Expand(ITreeViewItem item, bool? expand) {\n\t\tif (!_IndexOfOrThrowIfImportant(expand != false, item, out int i)) return;\n\t\tExpand(i, expand);\n\t}\n\t\n\t//internal bool test;\n\t\n\t/// <summary>\n\t/// Scrolls if need to make item actually visible.\n\t/// </summary>\n\t/// <exception cref=\"IndexOutOfRangeException\"></exception>\n\tpublic void EnsureVisible(int index, bool scrollTop = false) {\n\t\tif (!_IsValid(index)) throw new IndexOutOfRangeException();\n\t\tif (!_hasHwnd || !IsVisible) { _ensureVisible = (index + 1, scrollTop); return; }\n\t\tbool retry = false;\n\t\tg1:\n\t\t_ensureVisible = default;\n\t\tvar r = GetRectPhysical(index);\n\t\tif (scrollTop) {\n\t\t\tif (r.top != 0) _vscroll.Pos = index;\n\t\t} else if (r.top < 0 || r.bottom > _height) {\n\t\t\tint max = _vscroll.Max;\n\t\t\t_vscroll.Pos = (r.top < 0 || _height < _itemHeight) ? index : index - _height / _itemHeight + 1;\n\t\t\tif (!retry) if (retry = _vscroll.Max > max) goto g1; //added horz scrollbar and maybe it covers the item\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Expands descendant folders and scrolls if need to make item actually visible.\n\t/// </summary>\n\t/// <returns>Item index.</returns>\n\tpublic int EnsureVisible(ITreeViewItem item, bool scrollTop = false) {\n\t\tint i = IndexOf(item);\n\t\tif (i < 0) { //expand ancestor folders\n\t\t\tif (!_Find(_itemsSource, item)) throw new ArgumentException();\n\t\t\t_SetVisibleItems(init: false);\n\t\t\ti = IndexOf(item);\n\t\t\tstatic bool _Find(IEnumerable<ITreeViewItem> a, ITreeViewItem item) {\n\t\t\t\tif (a != null)\n\t\t\t\t\tforeach (var v in a) {\n\t\t\t\t\t\tif ((object)v == item) return true;\n\t\t\t\t\t\tif (v.IsFolder && _Find(v.Items, item)) {\n\t\t\t\t\t\t\tv.SetIsExpanded(true);\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\tEnsureVisible(i, scrollTop);\n\t\treturn i;\n\t}\n\t\n\t#endregion\n\t\n\t#region mouse/keyboard input and related events\n\t\n\t//Returns false if not on item.\n\tvoid _OnMouseDown(MouseButton button, nint wParam, nint lParam, bool @double = false) {\n\t\tif (button != MouseButton.Middle && Focusable && !_w.Window.HasExStyle(WSE.NOACTIVATE)) Focus();\n\t\tvar xy = Math2.NintToPOINT(lParam);\n\t\tif (!HitTest(xy, out var h) || !Api.GetCapture().Is0) return;\n\t\t\n\t\tif (button == MouseButton.Left && h.part == TVParts.Image && h.item.IsFolder) {\n\t\t\tExpand(h.index, null);\n\t\t} else {\n\t\t\tvar mk = (0 != (wParam & Api.MK_CONTROL) ? ModifierKeys.Control : 0) | (0 != (wParam & Api.MK_SHIFT) ? ModifierKeys.Shift : 0); //never mind Alt, it's never used and may activate menubar\n\t\t\tbool multiSelect = MultiSelect && button == MouseButton.Left && !@double && (mk is ModifierKeys.Control or ModifierKeys.Shift);\n\t\t\tbool checkbox = h.part == TVParts.Checkbox && button == MouseButton.Left && !multiSelect;\n\t\t\tbool unselectOnUp = false;\n\t\t\tif (button == MouseButton.Left && !multiSelect && !checkbox) {\n\t\t\t\tunselectOnUp = MultiSelect && IsSelected(h.index); //unselect other on up, else could not drag multiple\n\t\t\t\tSelect(h.index, true, unselectOther: !unselectOnUp, focus: true);\n\t\t\t}\n\t\t\tbool clickEvent = false, activateEvent = false;\n\t\t\tif (checkbox) {\n\t\t\t\tclickEvent = true;\n\t\t\t} else if (!@double || button == MouseButton.Middle) {\n\t\t\t\tApi.SetCapture(_w);\n\t\t\t\t_mouse = (true, button, h, xy, mk, multiSelect, unselectOnUp);\n\t\t\t} else if (button == MouseButton.Left) {\n\t\t\t\t//print.it(\"double\", h.item);\n\t\t\t\tif (!(FullRowExpand && !MultiSelect) && h.part != TVParts.Image && h.item.IsFolder) Expand(h.index, null);\n\t\t\t\tclickEvent = true;\n\t\t\t\tactivateEvent = !SingleClickActivate;\n\t\t\t}\n\t\t\tif (clickEvent || activateEvent) _MouseEvents(clickEvent, activateEvent, false, button, h, xy, mk, @double);\n\t\t}\n\t}\n\t(bool active, MouseButton button, TVHitTest h, POINT xy, ModifierKeys mk, bool multiSelect, bool unselect) _mouse;\n\t\n\tvoid _MouseEvents(bool click, bool activate, bool drag, MouseButton button, in TVHitTest h, POINT xy, ModifierKeys mk, bool @double = false) {\n\t\tvar v = new TVItemEventArgs(h.item, h.index, h.part, button, @double ? 2 : 1, xy, mk);\n\t\tif (click) ItemClick?.Invoke(v);\n\t\tif (activate && !h.item.IsDisabled) ItemActivated?.Invoke(v);\n\t\tif (drag) ItemDragStart?.Invoke(v);\n\t}\n\t\n\tvoid _MouseEnd() {\n\t\tif (_mouse.active) {\n\t\t\t_mouse.active = false;\n\t\t\tApi.ReleaseCapture();\n\t\t}\n\t}\n\t\n\tbool _OnMouseUp(MouseButton button) {\n\t\tif (_mouse.active && button == _mouse.button) {\n\t\t\t_MouseEnd();\n\t\t\tif (_mouse.multiSelect) { //extend selection on up. If on down, interferes with drag.\n\t\t\t\tint i = _mouse.h.index;\n\t\t\t\tif (_mouse.mk == ModifierKeys.Shift) _ShiftSelect(i); else Select(i, !IsSelected(i), unselectOther: false);\n\t\t\t\t_focusedIndex = i;\n\t\t\t} else {\n\t\t\t\tif (_mouse.unselect) Select(_mouse.h.index, true, unselectOther: true);\n\t\t\t\tif (FullRowExpand && !MultiSelect && button == MouseButton.Left && _mouse.mk == 0 && _mouse.h.item.IsFolder) {\n\t\t\t\t\tExpand(_mouse.h.index, null);\n\t\t\t\t}\n\t\t\t\tbool activateEvent = SingleClickActivate && _mouse.button == MouseButton.Left;\n\t\t\t\t_MouseEvents(true, activateEvent, false, _mouse.button, _mouse.h, _mouse.xy, _mouse.mk);\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\t\n\tvoid _OnMouseMove(nint wParam) {\n\t\tif (_mouse.active) {\n\t\t\tif (0 != (wParam & (_mouse.button switch { MouseButton.Left => Api.MK_LBUTTON, MouseButton.Right => Api.MK_RBUTTON, _ => Api.MK_MBUTTON }))) {\n\t\t\t\t//print.it(\"move\");\n\t\t\t\tif (Math2.Distance(_w.MouseClientXY, _mouse.xy) > _itemLineHeight / 4) {\n\t\t\t\t\t//print.it(\"drag\");\n\t\t\t\t\tint i = _mouse.h.index;\n\t\t\t\t\tif (!IsSelected(i)) SelectSingle(i, andFocus: true);\n\t\t\t\t\t_MouseEnd();\n\t\t\t\t\t_MouseEvents(false, false, true, _mouse.button, _mouse.h, _mouse.xy, _mouse.mk);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t_MouseEnd();\n\t\t\t}\n\t\t}\n\t\t_OnMouseMoveOrScroll(false);\n\t}\n\t\n\tvoid _OnMouseMoveOrScroll(bool wheel) {\n\t\tint i = _ItemFromY(_w.MouseClientXY.y);\n\t\tif (i != _hotIndex) {\n\t\t\tif (HotTrack) {\n\t\t\t\tif (_hotIndex >= 0) _Invalidate(_hotIndex);\n\t\t\t\tif (i >= 0) _Invalidate(i);\n\t\t\t}\n\t\t\t\n\t\t\tif (_hotIndex < 0 != i < 0) Api.TrackMouseLeave(_w, i >= 0);\n\t\t\t_hotIndex = i;\n\t\t\t\n\t\t\tif (ShowLabelTip) (_labeltip ??= new(this)).HotChanged(i);\n\t\t\t\n\t\t\t(_tooltip ??= new(this)).HotChanged(i);\n\t\t}\n\t}\n\t\n\tvoid _OnMouseLeave() {\n\t\tif (_hotIndex >= 0) {\n\t\t\tif (HotTrack) _Invalidate(_hotIndex);\n\t\t\t_hotIndex = -1;\n\t\t}\n\t\t_labeltip?.Hide();\n\t\t_tooltip?.Hide();\n\t}\n\t\n\t/// <summary>\n\t/// Whether to higlight an item when mouse is over.\n\t/// </summary>\n\tpublic bool HotTrack { get; set; }\n\t\n\t/// <summary>\n\t/// Whether to show a tooltip with full item text when mouse is over an item with partially visible text. Default true.\n\t/// </summary>\n\tpublic bool ShowLabelTip { get; set; } = true;\n\t\n\t/// <summary>\n\t/// Whether to activate an item on single click instead of double click.\n\t/// </summary>\n\t/// <seealso cref=\"ItemActivated\"/>\n\tpublic bool SingleClickActivate { get; set; }\n\t\n\t/// <summary>\n\t/// To expand/collapse folders can click not only icon.\n\t/// Ignored if <see cref=\"MultiSelect\"/>.\n\t/// </summary>\n\tpublic bool FullRowExpand { get; set; }\n\t\n\t///\n\tprotected override void OnKeyDown(KeyEventArgs e) {\n\t\t//print.it(e.Key, _focusedIndex);\n\t\tif (EditingLabel) {\n\t\t\tvar k = e.Key;\n\t\t\tswitch (k) {\n\t\t\tcase Key.Enter: EndEditLabel(); e.Handled = true; break;\n\t\t\tcase Key.Escape: EndEditLabel(true); e.Handled = true; break;\n\t\t\t}\n\t\t} else {\n\t\t\tbase.OnKeyDown(e);\n\t\t\tif (!e.Handled) e.Handled = ProcessKey(e.Key);\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Processes keys such as arrow, page, Enter, Ctrl+A.\n\t/// Returns true if handled.\n\t/// </summary>\n\t/// <seealso cref=\"keys.more.KKeyToWpf\"/>\n\tpublic bool ProcessKey(Key k) {\n\t\tbool handled = false;\n\t\tif (!_avi.NE_()) {\n\t\t\tvar mod = Keyboard.Modifiers;\n\t\t\tbool isFocus = _IsValid(_focusedIndex);\n\t\t\tint selIndex = -1, selAdd = 0;\n\t\t\tswitch (k) {\n\t\t\tcase Key.Enter:\n\t\t\t\thandled = true;\n\t\t\t\tif (isFocus) {\n\t\t\t\t\tvar v = _IndexToItem(_focusedIndex);\n\t\t\t\t\tif (!v.IsDisabled) ItemActivated?.Invoke(new(v, _focusedIndex, Mod: mod));\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase Key.Home or Key.End or Key.Down or Key.Up or Key.PageDown or Key.PageUp:\n\t\t\t\tselIndex = _vscroll.KeyNavigate(_focusedIndex, keys.more.KKeyFromWpf(k));\n\t\t\t\tbreak;\n\t\t\tcase Key.Left:\n\t\t\tcase Key.Right:\n\t\t\t\tif (mod != 0) break;\n\t\t\t\thandled = true;\n\t\t\t\tif (isFocus) {\n\t\t\t\t\tvar v = _avi[_focusedIndex];\n\t\t\t\t\tif (v.item.IsFolder) {\n\t\t\t\t\t\tif ((k == Key.Right && !v.item.IsExpanded) || (k == Key.Left && v.item.IsExpanded)) {\n\t\t\t\t\t\t\tExpand(_focusedIndex, k == Key.Right);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (k == Key.Right) { selAdd = 1; break; } //folder -> child\n\t\t\t\t\t}\n\t\t\t\t\tif (k == Key.Left && v.level > 0) { //child -> folder\n\t\t\t\t\t\tfor (int i = _focusedIndex; --i >= 0; selAdd--) if (_avi[i].level < v.level) break;\n\t\t\t\t\t\tselAdd--;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase Key.A when mod == ModifierKeys.Control && MultiSelect:\n\t\t\t\thandled = true;\n\t\t\t\tSelect(.., true);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t\n\t\t\tif (selAdd != 0) selIndex = Math.Clamp((isFocus ? _focusedIndex : (selAdd > 0 ? -1 : _avi.Length)) + selAdd, 0, _avi.Length - 1);\n\t\t\tif (selIndex >= 0 && (mod == 0 || (mod == ModifierKeys.Shift && MultiSelect))) {\n\t\t\t\thandled = true;\n\t\t\t\tif (selIndex != _focusedIndex) {\n\t\t\t\t\tif (mod == 0) SelectSingle(selIndex, andFocus: false); else _ShiftSelect(selIndex);\n\t\t\t\t\tSetFocusedItem(selIndex);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn handled;\n\t}\n\t\n\t/// <summary>\n\t/// When an item double-clicked. Or clicked, if <see cref=\"SingleClickActivate\"/>. Also on Enter key if focused.\n\t/// Not sent if disabled.\n\t/// </summary>\n\tpublic event Action<TVItemEventArgs> ItemActivated;\n\t\n\t/// <summary>\n\t/// When an item clicked or double-clicked with the left, right or middle mouse button.\n\t/// </summary>\n\tpublic event Action<TVItemEventArgs> ItemClick;\n\t\n\t/// <summary>\n\t/// When drag start detected.\n\t/// </summary>\n\tpublic event Action<TVItemEventArgs> ItemDragStart;\n\t\n\t/// <summary>\n\t/// Right-click non on an item.\n\t/// </summary>\n\tpublic event Action RightClickInEmptySpace;\n\t\n\t#endregion\n\t\n\t#region selection, focus, checkboxes\n\t\n\t/// <summary>\n\t/// Whether can select multiple items, for example with Ctrl or Shift key.\n\t/// </summary>\n\tpublic bool MultiSelect { get; set; } //never mind: if set false, unselect all except focused\n\t\n\t/// <summary>\n\t/// Selects or unselects item.\n\t/// </summary>\n\t/// <param name=\"index\"></param>\n\t/// <param name=\"select\">true to select, false to unselect.</param>\n\t/// <param name=\"unselectOther\">Unselect other items. Used only if <see cref=\"MultiSelect\"/> true, else always unselects other items.</param>\n\t/// <param name=\"focus\">Make the item focused and ensure visible.</param>\n\t/// <param name=\"scrollTop\">Scroll if need so that the item would be at the top of really visible range.</param>\n\t/// <exception cref=\"IndexOutOfRangeException\"></exception>\n\tpublic void Select(int index, bool select = true, bool unselectOther = false, bool focus = false, bool scrollTop = false) {\n\t\tif (!_IsValid(index)) throw new IndexOutOfRangeException();\n\t\t\n\t\tif (focus) SetFocusedItem(index, scrollTop ? TVFocus.EnsureVisible | TVFocus.ScrollTop : TVFocus.EnsureVisible);\n\t\telse if (scrollTop) _vscroll.Pos = index;\n\t\t\n\t\tSelect(index..(index + 1), select, unselectOther);\n\t\tif (select && unselectOther) SelectedSingle?.Invoke(this, index);\n\t}\n\t\n\t/// <summary>\n\t/// Selects or unselects item.\n\t/// </summary>\n\t/// <param name=\"item\"></param>\n\t/// <param name=\"select\">true to select, false to unselect.</param>\n\t/// <param name=\"unselectOther\">Unselect other items. Used only if <see cref=\"MultiSelect\"/> true, else always unselects other items.</param>\n\t/// <param name=\"focus\">Make the item focused and ensure visible.</param>\n\t/// <param name=\"scrollTop\">Scroll if need so that the item would be at the top of really visible range.</param>\n\t/// <exception cref=\"ArgumentException\"><i>item</i> not found in this control. No exception if <i>select</i> false.</exception>\n\tpublic void Select(ITreeViewItem item, bool select = true, bool unselectOther = false, bool focus = false, bool scrollTop = false) {\n\t\tint i;\n\t\tif (select) {\n\t\t\ti = EnsureVisible(item, scrollTop);\n\t\t} else {\n\t\t\ti = IndexOf(item);\n\t\t\tif (i < 0) return; //ok if tries to unselect an already unselected item in a collapsed folder\n\t\t}\n\t\tSelect(i, select, unselectOther, focus, scrollTop);\n\t}\n\t\n\t/// <summary>\n\t/// Selects or unselects range of items.\n\t/// </summary>\n\t/// <param name=\"range\">Range of item indices. For example <c>..</c> means all.</param>\n\t/// <param name=\"select\">true to select, false to unselect.</param>\n\t/// <param name=\"unselectOther\">Unselect other items. Used only if <see cref=\"MultiSelect\"/> true, else always unselects other items.</param>\n\tpublic void Select(Range range, bool select = true, bool unselectOther = false) {\n\t\tvar (from, to) = range.GetStartEnd(_avi.Lenn_());\n\t\tif (select && !MultiSelect) {\n\t\t\tif (to - from > 1) throw new InvalidOperationException();\n\t\t\tunselectOther = true;\n\t\t}\n\t\tbool invalidate = false;\n\t\tif (select && unselectOther) {\n\t\t\tfor (int k = 0; k < 2; k++) {\n\t\t\t\tint i, j; if (k == 0) { i = 0; j = from; } else { i = to; j = _avi.Length; }\n\t\t\t\tfor (; i < j; i++) if (_avi[i].isSelected) { _avi[i].isSelected = false; invalidate = true; }\n\t\t\t}\n\t\t}\n\t\tfor (; from < to; from++) if (select != IsSelected(from)) invalidate |= _avi[from].Select(select);\n\t\tif (invalidate) _Invalidate();\n\t\t\n\t\tSelectionChanged?.Invoke(this, EventArgs.Empty);\n\t}\n\t\n\t/// <summary>\n\t/// Unselects all.\n\t/// </summary>\n\tpublic void UnselectAll() => Select(.., select: false);\n\t\n\tpublic event EventHandler SelectionChanged;\n\tpublic event EventHandler<int> SelectedSingle;\n\t\n\t/// <summary>\n\t/// Selects item, unselects others, optionally makes the focused.\n\t/// </summary>\n\t/// <param name=\"andFocus\">Make the item focused and ensure visible.</param>\n\t/// <param name=\"scrollTop\">Scroll if need so that the item would be at the top of really visible range.</param>\n\tpublic void SelectSingle(int index, bool andFocus, bool scrollTop = false) => Select(index, true, true, andFocus, scrollTop);\n\t\n\t/// <summary>\n\t/// Selects item and unselects others. Makes visible and focused.\n\t/// </summary>\n\t/// <param name=\"scrollTop\">Scroll if need so that the item would be at the top of really visible range.</param>\n\t/// <exception cref=\"ArgumentException\"><i>item</i> not found in this control.</exception>\n\tpublic void SelectSingle(ITreeViewItem item, bool scrollTop = false) => Select(item, true, true, true, scrollTop);\n\t\n\tvoid _ShiftSelect(int last) {\n\t\tint from; if (last >= _focusedIndex) from = Math.Max(_focusedIndex, 0); else { from = last; last = _focusedIndex; }\n\t\tSelect(from..++last, true, unselectOther: false);\n\t}\n\t\n\t/// <summary>\n\t/// Returns true if the item is selected.\n\t/// </summary>\n\t/// <seealso cref=\"MultiSelect\"/>\n\t/// <exception cref=\"IndexOutOfRangeException\"></exception>\n\tpublic bool IsSelected(int index) => _avi[index].isSelected;\n\t\n\t/// <summary>\n\t/// Returns true if the item is visible and selected.\n\t/// </summary>\n\t/// <seealso cref=\"MultiSelect\"/>\n\tpublic bool IsSelected(ITreeViewItem item) {\n\t\tint i = IndexOf(item); //if not found, probably it is an unselected item in a collapsed folder\n\t\treturn i >= 0 && _avi[i].isSelected;\n\t}\n\t\n\t/// <summary>\n\t/// Gets selected items. Returns empty list if none selected.\n\t/// </summary>\n\t/// <seealso cref=\"MultiSelect\"/>\n\tpublic List<int> SelectedIndices {\n\t\tget {\n\t\t\tvar a = new List<int>();\n\t\t\tfor (int i = 0; i < _avi.Length; i++) if (_avi[i].isSelected) a.Add(i);\n\t\t\treturn a;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Gets selected items. Returns empty list if none selected.\n\t/// </summary>\n\t/// <seealso cref=\"MultiSelect\"/>\n\tpublic List<ITreeViewItem> SelectedItems {\n\t\tget {\n\t\t\tvar a = new List<ITreeViewItem>();\n\t\t\tfor (int i = 0; i < _avi.Length; i++) if (_avi[i].isSelected) a.Add(_avi[i].item);\n\t\t\treturn a;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Returns index of first selected item, or -1 if no selection.\n\t/// </summary>\n\tpublic int SelectedIndex {\n\t\tget {\n\t\t\tfor (int i = 0; i < _avi.Length; i++) if (_avi[i].isSelected) return i;\n\t\t\treturn -1;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Returns first selected item, or null if no selection.\n\t/// </summary>\n\tpublic ITreeViewItem SelectedItem {\n\t\tget {\n\t\t\tint i = SelectedIndex; return i >= 0 ? _avi[i].item : null;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Gets index of item that has logical focus within the control. Can be -1.\n\t/// </summary>\n\tpublic int FocusedIndex => _focusedIndex;\n\t\n\t/// <summary>\n\t/// Gets item that has logical focus within the control. Can be null.\n\t/// </summary>\n\tpublic ITreeViewItem FocusedItem => _IndexToItem(_focusedIndex);\n\t\n\t/// <summary>\n\t/// Sets item that has logical focus within the control.\n\t/// </summary>\n\t/// <param name=\"index\">Can be -1.</param>\n\t/// <param name=\"flags\"></param>\n\t/// <exception cref=\"IndexOutOfRangeException\"></exception>\n\t/// <remarks>\n\t/// Used with keyboard actions (Enter-activate, arrows, page down/up), range selection (Shift+click when <see cref=\"MultiSelect\"/> true), optionally <see cref=\"Select\"/>.\n\t/// </remarks>\n\tpublic void SetFocusedItem(int index, TVFocus flags = TVFocus.EnsureVisible) {\n\t\tif (!_IsValid(index) && index != -1) throw new IndexOutOfRangeException();\n\t\t_focusedIndex = index;\n\t\tif (flags.HasAny(TVFocus.EnsureVisible | TVFocus.ScrollTop)) if (index >= 0) EnsureVisible(index, flags.Has(TVFocus.ScrollTop));\n\t}\n\t\n\t/// <summary>\n\t/// Sets item that has logical focus within the control.\n\t/// </summary>\n\t/// <param name=\"item\">Can be null.</param>\n\t/// <param name=\"flags\"></param>\n\t/// <exception cref=\"ArgumentException\">The item is not a visible item in this control.</exception>\n\tpublic void SetFocusedItem(ITreeViewItem item, TVFocus flags = TVFocus.EnsureVisible)\n\t\t=> SetFocusedItem(_IndexOfOrThrow(item, canBeNull: true), flags);\n\t\n\t///\n\tprotected override void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e) {\n\t\t_Invalidate();\n\t\tbase.OnGotKeyboardFocus(e);\n\t}\n\t\n\t///\n\tprotected override void OnLostKeyboardFocus(KeyboardFocusChangedEventArgs e) {\n\t\t_MouseEnd();\n\t\t_Invalidate();\n\t\tbase.OnLostKeyboardFocus(e);\n\t}\n\t\n\t/// <summary>\n\t/// Has checkboxes.\n\t/// </summary>\n\t/// <remarks>\n\t/// To get checkbox states the control calls <see cref=\"ITreeViewItem.CheckState\"/>.\n\t/// The control does not automatically set checkbox states. You can do it in <see cref=\"ItemClick\"/> event handler like in the example.\n\t/// </remarks>\n\t/// <example>\n\t/// Event handler.\n\t/// <code><![CDATA[\n\t/// _tv.ItemClick+=(_,e)=>{\n\t/// \tvar v=e.Item as TvItem; //TvItem is your class that implements ITreeViewItem interface\n\t/// \tif(e.ClickedPart==KTreeView.TVParts.Checkbox && !v.IsDisabled) v.CheckState=v.CheckState==default ? KTreeView.TVCheck.Checked : default;\n\t/// };\n\t/// ]]></code>\n\t/// Property of your TvItem class that implements ITreeViewItem interface.\n\t/// <code><![CDATA[\n\t/// KTreeView.TVCheck CheckState {\n\t/// \tget => _checkState;\n\t/// \tset { if(value!=_checkState) { _checkState=value; _tv.Redraw(this); } }\n\t/// }\n\t/// KTreeView.TVCheck _checkState;\n\t/// ]]></code>\n\t/// </example>\n\tpublic bool HasCheckboxes {\n\t\tget => _hasCheckboxes;\n\t\tset {\n\t\t\tif (value != _hasCheckboxes) {\n\t\t\t\t_hasCheckboxes = value;\n\t\t\t\t_MeasureClear(true);\n\t\t\t}\n\t\t}\n\t}\n\tbool _hasCheckboxes;\n\t\n\t/// <summary>\n\t/// Gets checked items (<see cref=\"TVCheck.Checked\"/>). Returns empty list if none checked.\n\t/// </summary>\n\tpublic List<int> CheckedIndices {\n\t\tget {\n\t\t\tvar a = new List<int>();\n\t\t\tfor (int i = 0; i < _avi.Length; i++) if (_avi[i].item.CheckState == TVCheck.Checked) a.Add(i);\n\t\t\treturn a;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Gets checked items (<see cref=\"TVCheck.Checked\"/>). Returns empty list if none checked.\n\t/// </summary>\n\tpublic List<ITreeViewItem> CheckedItems {\n\t\tget {\n\t\t\tvar a = new List<ITreeViewItem>();\n\t\t\tfor (int i = 0; i < _avi.Length; i++) if (_avi[i].item.CheckState == TVCheck.Checked) a.Add(_avi[i].item);\n\t\t\treturn a;\n\t\t}\n\t}\n\t\n\t//rejected. Then also need setter, but CheckState is read-only and I don't want to make it read-write.\n\t//\t/// <summary>\n\t//\t/// Gets checked items (<see cref=\"TVCheck.Checked\"/>) as flags.\n\t//\t/// Skips items that don't have state <b>Checked</b> or <b>Unchecked</b>.\n\t//\t/// </summary>\n\t//\tpublic ulong CheckedBits {\n\t//\t\tget {\n\t//\t\t\tulong flags=0; ulong j=1;\n\t//\t\t\tfor (int i = 0; i < _avi.Length; i++) {\n\t//\t\t\t\tswitch (_avi[i].item.CheckState) {\n\t//\t\t\t\tcase TVCheck.Checked: flags|=j; break;\n\t//\t\t\t\tcase TVCheck.Unchecked: break;\n\t//\t\t\t\tdefault: continue;\n\t//\t\t\t\t}\n\t//\t\t\t\tif(j<0x8000000000000000) j<<=1; else break;\n\t//\t\t\t}\n\t//\t\t\treturn flags;\n\t//\t\t}\n\t//\t}\n\t\n\t#endregion\n\t\n\t#region edit label\n\t\n\t/// <summary>\n\t/// Starts focused item text editing.\n\t/// The type must implement <see cref=\"ITreeViewItem.SetNewText\"/>.\n\t/// </summary>\n\t/// <exception cref=\"InvalidOperationException\">Control not created.</exception>\n\tpublic void EditLabel(Action<bool> ended = null) { int i = _focusedIndex; if (i >= 0) EditLabel(i, ended); }\n\t\n\t/// <summary>\n\t/// Starts item text editing.\n\t/// The type must implement <see cref=\"ITreeViewItem.SetNewText\"/>.\n\t/// </summary>\n\t/// <exception cref=\"ArgumentException\"></exception>\n\t/// <exception cref=\"InvalidOperationException\">Control not created.</exception>\n\tpublic void EditLabel(ITreeViewItem item, Action<bool> ended = null) => _EditLabel(item, _IndexOfOrThrow(item), ended);\n\t\n\t/// <summary>\n\t/// Starts item text editing.\n\t/// The type must implement <see cref=\"ITreeViewItem.SetNewText\"/>.\n\t/// </summary>\n\t/// <exception cref=\"IndexOutOfRangeException\"></exception>\n\t/// <exception cref=\"InvalidOperationException\">Control not created.</exception>\n\tpublic void EditLabel(int index, Action<bool> ended = null) {\n\t\tif (!_IndexToItem(index, out var item)) throw new IndexOutOfRangeException();\n\t\t_EditLabel(item, index, ended);\n\t}\n\t\n\tvoid _EditLabel(ITreeViewItem item, int index, Action<bool> ended) {\n\t\tEndEditLabel();\n\t\tEnsureVisible(index);\n\t\tif (!IsFocused) Focus();\n\t\t\n\t\tvar r = GetRectPhysical(index, TVParts.Text | TVParts.MarginRight | TVParts.Right);\n\t\tr.left -= _imageMarginX;\n\t\tr.Intersect(_w.ClientRect);\n\t\tif (r.Width < 8 || r.Height < 8) return;\n\t\t\n\t\t_leItem = item;\n\t\t_leEnded = ended;\n\t\t\n\t\t//add border. Cannot add WS_BORDER to _leEdit because it also adds padding and therefore must be taller than we need, else there is no top border.\n\t\t_leBorder = WndUtil.CreateWindow((w, msg, wp, lp) => {\n\t\t\tif (msg == Api.WM_COMMAND && Math2.HiWord(wp) == /*EN_KILLFOCUS*/ 0x0200) EndEditLabel();\n\t\t\treturn Api.DefWindowProc(w, msg, wp, lp);\n\t\t}, true, \"Static\", null, WS.CHILD | WS.VISIBLE | WS.BORDER | (WS)6 /*SS_WHITERECT*/, 0, r.left, r.top, r.Width, r.Height, _w);\n\t\t\n\t\t_leEdit = WndUtil.CreateWindow(\"Edit\", item.DisplayText, WS.CHILD | WS.VISIBLE | Api.ES_AUTOHSCROLL, 0, 0, 0, r.Width - 2, r.Height - 2, _leBorder);\n\t\tWndUtil.SetFont(_leEdit);\n\t\tEditLabelStartedEventArgs e = new(this, item);\n\t\te.SelectText();\n\t\t\n\t\tApi.SetFocus(_leEdit);\n\t\t\n\t\tEditLabelStarted?.Invoke(e);\n\t}\n\t\n\twnd _leEdit, _leBorder;\n\tITreeViewItem _leItem;\n\tAction<bool> _leEnded;\n\t\n\t/// <summary>\n\t/// When item text editing started.\n\t/// </summary>\n\tpublic event Action<EditLabelStartedEventArgs> EditLabelStarted;\n\t\n\tpublic record class EditLabelStartedEventArgs(KTreeView tv, ITreeViewItem item) {\n\t\tpublic string Text {\n\t\t\tget => item.DisplayText;\n\t\t\tset { tv._leEdit.SetText(value); }\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Selects text. To select all, use 0 -1.\n\t\t/// </summary>\n\t\tpublic void SelectText(int start = 0, int end = -1) {\n\t\t\ttv._leEdit.Send(/*EM_SETSEL*/ 0x00B1, start, end);\n\t\t}\n\t}\n\t\n\tpublic bool EditingLabel => !_leEdit.Is0;\n\t\n\t/// <summary>\n\t/// Ends item text editing.\n\t/// </summary>\n\tpublic void EndEditLabel(bool cancel = false) {\n\t\tif (!EditingLabel) return;\n\t\tint index = cancel ? -1 : IndexOf(_leItem);\n\t\tif (!cancel) cancel = index < 0 || !IsVisible;\n\t\t//print.it(\"EndEditLabel, cancel=\", cancel);\n\t\tbool focus = Api.GetFocus() == _leEdit;\n\t\tvar text = cancel ? null : _leEdit.ControlText;\n\t\tvar item = _leItem; _leItem = null;\n\t\tvar ended = _leEnded; _leEnded = null;\n\t\t_leEdit = default;\n\t\tApi.DestroyWindow(_leBorder); _leBorder = default;\n\t\tif (focus) Focus();\n\t\tif (!cancel && text != item.DisplayText) {\n\t\t\tint meas = _avi[index].measured;\n\t\t\titem.SetNewText(text);\n\t\t\tif (_avi[index].measured == meas) Redraw(index, remeasure: true); //SetNewText (app) should do it, but may forget\n\t\t}\n\t\tended?.Invoke(!cancel);\n\t}\n\t\n\t#endregion\n\t\n\t#region drag & drop\n\t\n\t///\n\tprotected override void OnDragLeave(DragEventArgs e) {\n\t\tbase.OnDragLeave(e);\n\t\t_dd?.ClearInsertMark();\n\t\t_dd = null;\n\t}\n\t\n\t///\n\tprotected override void OnDrop(DragEventArgs e) {\n\t\tbase.OnDrop(e);\n\t\t_dd?.ClearInsertMark();\n\t\t_dd = null;\n\t}\n\t\n\t/// <summary>\n\t/// Can be called from \"drag over\" override or event handler to show/hide insertion mark, expand/collapse folder and scroll if need.\n\t/// </summary>\n\t/// <param name=\"canDrop\">Can drop here. If false, hides insertion mark and returns.</param>\n\t/// <remarks>\n\t/// Draws black line between nearest items. If mouse is on a folder vertical center, draws rectangle.\n\t/// When mouse is on a folder, expands it if pressed key Right and collapses if Left.\n\t/// Scrolls when mouse is near top or bottom or pressed key Down, Up, PageDown, PageUp, Home or End.\n\t/// </remarks>\n\t/// <seealso cref=\"GetDropInfo\"/>\n\tpublic void OnDragOver2(bool canDrop) {\n\t\tif (!canDrop || _avi.NE_()) { _dd?.ClearInsertMark(); return; }\n\t\t_dd ??= new _DragDrop(this);\n\t\tvar p = _w.MouseClientXY;\n\t\tGetDropInfo(p, out var d);\n\t\tbool noMark = false;\n\t\tKey key;\n\t\t//expand/collapse folder\n\t\tif (Keyboard.IsKeyDown(key = Key.Right) || Keyboard.IsKeyDown(key = Key.Left)) {\n\t\t\tif (d.targetItem?.IsFolder ?? false) Expand(d.targetIndex, key == Key.Right);\n\t\t\treturn;\n\t\t}\n\t\t//scroll. When not moving mouse, this is called every 64 ms.\n\t\tif (_vscroll.Visible) {\n\t\t\tint scroll = 0, dist = _imageSize * 3 / 2;\n\t\t\tif (Keyboard.IsKeyDown(key = Key.Down)) scroll = 3;\n\t\t\telse if (Keyboard.IsKeyDown(key = Key.Up)) scroll = -3;\n\t\t\telse if (Keyboard.IsKeyDown(key = Key.PageDown)) scroll = _height / _itemHeight;\n\t\t\telse if (Keyboard.IsKeyDown(key = Key.PageUp)) scroll = -_height / _itemHeight;\n\t\t\telse if (Keyboard.IsKeyDown(Key.Home)) noMark = _Scroll(0, true);\n\t\t\telse if (Keyboard.IsKeyDown(Key.End)) noMark = _Scroll(_vscroll.Max, true);\n\t\t\telse if (_height > dist * 3) {\n\t\t\t\tkey = 0;\n\t\t\t\tif (p.y < dist) scroll--; else if (p.y > _height - dist) scroll++;\n\t\t\t}\n\t\t\tif (scroll == 0) {\n\t\t\t\t_dd.scrolling = false;\n\t\t\t\t_dd.scrollDelayTime = 0;\n\t\t\t\t_dd.scrollTime = 0;\n\t\t\t} else if (_dd.scrolling || key != 0) {\n\t\t\t\tlong time = Environment.TickCount64;\n\t\t\t\tif (time - _dd.scrollTime > 110) {\n\t\t\t\t\t_dd.scrollTime = time;\n\t\t\t\t\tnoMark = _Scroll(scroll, false);\n\t\t\t\t} else noMark = true;\n\t\t\t} else if (_dd.scrollDelayTime == 0 || p != _dd.scrollDelayPoint) {\n\t\t\t\t_dd.scrollDelayTime = Environment.TickCount64;\n\t\t\t\t_dd.scrollDelayPoint = p;\n\t\t\t} else if (Environment.TickCount64 - _dd.scrollDelayTime > 400) {\n\t\t\t\t_dd.scrolling = true;\n\t\t\t}\n\t\t}\n\t\t//insertion mark\n\t\tif (!noMark) _dd.SetInsertMark(d.targetIndex, d.insertAfter, d.intoFolder);\n\t\t\n\t\tbool _Scroll(int scroll, bool absolute) {\n\t\t\tif (!absolute) scroll += _vscroll.Pos;\n\t\t\tint i = Math.Clamp(scroll, 0, _vscroll.Max);\n\t\t\tif (i == _vscroll.Pos) return false;\n\t\t\t_dd.ClearInsertMark();\n\t\t\t_vscroll.Pos = i;\n\t\t\treturn true;\n\t\t}\n\t}\n\t\n\t//fields for drag scrolling and insertion mark\n\tclass _DragDrop {\n\t\tKTreeView _tv;\n\t\tpublic bool scrolling;\n\t\tpublic long scrollDelayTime, scrollTime;\n\t\tpublic POINT scrollDelayPoint;\n\t\tpublic int insertIndex;\n\t\tpublic bool insertAfter, insertFolder;\n\t\t\n\t\tpublic _DragDrop(KTreeView tv) {\n\t\t\t_tv = tv;\n\t\t\tinsertIndex = -1;\n\t\t}\n\t\t\n\t\tpublic void SetInsertMark(int i, bool after, bool folder) {\n\t\t\tif (i == insertIndex && after == insertAfter && folder == insertFolder) return;\n\t\t\tint pi = insertIndex;\n\t\t\tinsertIndex = i; insertAfter = after; insertFolder = folder;\n\t\t\tif (pi >= 0) _tv._Invalidate(pi);\n\t\t\tif (i >= 0) _tv._Invalidate(i);\n\t\t}\n\t\t\n\t\tpublic void ClearInsertMark() => SetInsertMark(-1, false, false);\n\t}\n\t_DragDrop _dd;\n\t\n\t/// <summary>\n\t/// Can be called from \"drag over\" and \"drop\" overrides or event handlers to get drop info.\n\t/// This overload uses mouse position (<see cref=\"wnd.MouseClientXY\"/>).\n\t/// </summary>\n\t/// <param name=\"d\"></param>\n\t/// <seealso cref=\"OnDragOver2\"/>\n\tpublic void GetDropInfo(out TVDropInfo d) => GetDropInfo(_w.MouseClientXY, out d);\n\t\n\t/// <summary>\n\t/// Can be called from \"drag over\" and \"drop\" overrides or event handlers to get drop info.\n\t/// </summary>\n\t/// <param name=\"xy\">A point relative to the top-left of the control without border. Physical pixels.</param>\n\t/// <param name=\"d\"></param>\n\t/// <seealso cref=\"OnDragOver2\"/>\n\tpublic void GetDropInfo(POINT xy, out TVDropInfo d) {\n\t\td = default;\n\t\td.xy = xy;\n\t\tif (_avi.NE_()) {\n\t\t\td.targetIndex = -1;\n\t\t\treturn;\n\t\t}\n\t\tif (HitTest(d.xy, out var h)) {\n\t\t\td.targetIndex = h.index;\n\t\t\tdouble y = d.xy.y, top = _ItemTop(h.index), quarter = _itemHeight / 4d;\n\t\t\tbool after = y >= top + quarter * 2;\n\t\t\tif (h.item.IsFolder && y >= top + quarter) {\n\t\t\t\tif (y < top + quarter * 3) d.intoFolder = true;\n\t\t\t\telse if (h.index < _avi.Length - 1 && _avi[h.index + 1].level > _avi[h.index].level) d.targetIndex++; //between folder and its first child\n\t\t\t\telse d.insertAfter = after;\n\t\t\t} else d.insertAfter = after;\n\t\t\t\n\t\t\td.targetItem = _avi[d.targetIndex].item;\n\t\t} else {\n\t\t\td.targetIndex = -1;\n\t\t}\n\t}\n\t\n\t#endregion\n}\n"
  },
  {
    "path": "Au.Controls/KTreeView/tv-acc.cs",
    "content": "//Implements MSAA IAccessible.\n\n//Problem: UIA \"object from point\" returns CLIENT, not item, although has items in tree. MSAA works well.\n//\tFor UIA to work correctly, need to implement UIA (AutomationPeer for control and items).\n//\t\tBut then problem: if not implemented MSAA, MSAA \"object from point\" does not return item, although has items in tree.\n//\t\t\tIf implemented both, tree contains duplicate subtrees; example - SysListView32 control.\n//\t\tAlso, cannot implement it directly for HwndHost, because WPF then throws exception. But can implement for its parent.\n//\tI did not find a way to make both UIA and MSAA work correctly with WPF HwndHost.\n//\tGood: NVDA now works anyway.\n\n//Strange: QM2 does not find items and client unless checked +invisible, although no invisible state. Never mind.\n\nusing NAVDIR = Au.Types.Api.NAVDIR;\n\nnamespace Au.Controls;\n\npublic partial class KTreeView {\n\tclass _Accessible : HwndHostAccessibleBase_ {\n\t\tKTreeView _tv;\n\n\t\tinternal _Accessible(KTreeView tv) : base(tv, tv.Hwnd) {\n\t\t\t_tv = tv;\n\t\t}\n\n\t\tITreeViewItem _Item(int child) {\n\t\t\tif (child == -1) return null;\n\t\t\tif ((uint)child < _tv.CountVisible) return _tv._avi[child].item;\n\t\t\tthrow new ArgumentException();\n\t\t}\n\n\t\tpublic override int ChildCount => _tv.CountVisible;\n\n\t\tpublic override string Name(int child) {\n\t\t\tvar k = _Item(child);\n\t\t\tif (k == null) return _tv.Name;\n\t\t\treturn k.DisplayText;\n\t\t}\n\n\t\tpublic override ERole Role(int child) {\n\t\t\treturn child == -1 ? ERole.TREE : ERole.TREEITEM;\n\t\t}\n\n\t\tpublic override EState State(int child) {\n\t\t\tif (child == -1) return base.State(child);\n\t\t\tvar k = _Item(child);\n\t\t\tEState r = 0;\n\t\t\tif (_tv.Focusable) {\n\t\t\t\tr |= EState.FOCUSABLE;\n\t\t\t\tif (child == _tv._focusedIndex && _tv.IsKeyboardFocused) r |= EState.FOCUSED;\n\t\t\t}\n\t\t\tif (k.IsSelectable) {\n\t\t\t\tr |= EState.SELECTABLE;\n\t\t\t\tif (_tv.MultiSelect) r |= EState.MULTISELECTABLE;\n\t\t\t\tif (_tv.IsSelected(child)) r |= EState.SELECTED;\n\t\t\t}\n\t\t\tif (k.IsFolder) r |= k.IsExpanded ? EState.EXPANDED : EState.COLLAPSED;\n\t\t\tvar (from, to) = _tv._GetViewRange(); if (child < from || child >= to) r |= EState.INVISIBLE | EState.OFFSCREEN;\n\t\t\tif (k.IsDisabled) r |= EState.DISABLED;\n\t\t\tswitch (k.CheckState) { case TVCheck.Checked: case TVCheck.RadioChecked: r |= EState.CHECKED; break; case TVCheck.Mixed: r |= EState.MIXED; break; }\n\t\t\t//if(child==_tv._hotIndex) r|=EState.HOTTRACKED;\n\t\t\t//if(!k.IsEditable) r|=EState.READONLY;\n\t\t\treturn r;\n\t\t}\n\n\t\tpublic override int FocusedChild => _tv._focusedIndex;\n\n\t\tpublic override List<int> SelectedChildren => _tv.SelectedIndices;\n\n\t\tpublic override string DefaultAction(int child) {\n\t\t\tvar k = _Item(child);\n\t\t\tif (k == null) return null;\n\t\t\tif (k.IsFolder) return k.IsExpanded ? \"Collapse\" : \"Expand\";\n\t\t\treturn \"Activate\";\n\t\t}\n\n\t\tpublic override void SelectChild(ESelect flagsSelect, int child) {\n\t\t\t//if(flagsSelect.HasAny(ESelect.ADDSELECTION|ESelect.EXTENDSELECTION) && !_tv.MultiSelect) throw new InvalidOperationException();\n\t\t\t//int anchor=Math.Max(_tv.FocusedIndex, 0);\n\t\t\tswitch (flagsSelect & (ESelect.TAKESELECTION | ESelect.ADDSELECTION | ESelect.EXTENDSELECTION | ESelect.REMOVESELECTION)) {\n\t\t\tcase 0: break;\n\t\t\tcase ESelect.TAKESELECTION:\n\t\t\t\t_tv.SelectSingle(child, andFocus: false);\n\t\t\t\tbreak;\n\t\t\tcase ESelect.REMOVESELECTION:\n\t\t\t\t_tv.Select(child, false);\n\t\t\t\tbreak;\n\t\t\tcase ESelect.ADDSELECTION when _tv.MultiSelect:\n\t\t\t\t_tv.Select(child, true);\n\t\t\t\tbreak;\n\t\t\t//case ESelect.EXTENDSELECTION: //rarely used\n\t\t\t//case ESelect.ADDSELECTION|ESelect.EXTENDSELECTION:\n\t\t\t//\tbreak;\n\t\t\t//case ESelect.REMOVESELECTION|ESelect.EXTENDSELECTION:\n\t\t\t//\tbreak;\n\t\t\tdefault: throw new ArgumentException();\n\t\t\t}\n\t\t\tif (flagsSelect.Has(ESelect.TAKEFOCUS)) {\n\t\t\t\t//_tv.Focus();\n\t\t\t\t_tv.SetFocusedItem(child);\n\t\t\t}\n\t\t}\n\n\t\tpublic override RECT ChildRect(int child) => _tv.GetRectPhysical(child);\n\n\t\tpublic override int? Navigate(NAVDIR navDir, int childStart) {\n\t\t\tif (childStart == -1) { //navDir can be only first or last\n\t\t\t\tint n = _tv.CountVisible; if (n == 0) return null;\n\t\t\t\treturn navDir == NAVDIR.FIRSTCHILD ? 0 : n - 1;\n\t\t\t} else { //navDir cannot be first or last\n\t\t\t\tswitch (navDir) {\n\t\t\t\tcase NAVDIR.PREVIOUS:\n\t\t\t\tcase NAVDIR.UP:\n\t\t\t\t\tif (childStart > 0) return childStart - 1;\n\t\t\t\t\tbreak;\n\t\t\t\tcase NAVDIR.NEXT:\n\t\t\t\tcase NAVDIR.DOWN:\n\t\t\t\t\tif (++childStart < _tv.CountVisible) return childStart;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\n\t\tpublic override int HitTest(int x, int y) => _tv._ItemFromY(y);\n\n\t\tpublic override void DoDefaultAction(int child) {\n\t\t\tvar k = _Item(child);\n\t\t\tif (k == null) return;\n\t\t\tif (k.IsFolder) _tv.Expand(child, null);\n\t\t\telse {\n\t\t\t\t_tv.SelectSingle(child, andFocus: true);\n\t\t\t\t_tv.ItemActivated?.Invoke(new(k, child));\n\t\t\t}\n\t\t}\n\t}\n}"
  },
  {
    "path": "Au.Controls/KTreeView/tv-hh.cs",
    "content": "using System.Windows;\nusing System.Windows.Interop;\nusing System.Windows.Media;\nusing System.Windows.Input;\n\nnamespace Au.Controls;\n\npublic unsafe partial class KTreeView : HwndHost {\n\twnd _w;\n\tbool _hasHwnd;\n\t\n\tpublic wnd Hwnd => _w;\n\t\n\t//const string c_winClassName = \"KTreeView\";\n\t//static KTreeView() {\n\t//\tWndUtil.RegisterWindowClass(c_winClassName);\n\t//}\n\t\n\t//bool _test;\n\t\n\tprotected override HandleRef BuildWindowCore(HandleRef hwndParent) {\n\t\t//_test = Name == \"Files_list\";\n\t\t\n\t\tvar wParent = (wnd)hwndParent.Handle;\n\t\t_w = WndUtil.CreateWindow(_wndProc = _WndProc, false, \"Static\", Name, WS.CHILD | WS.CLIPCHILDREN, 0, 0, 0, 10, 10, wParent);\n\t\t_hasHwnd = true;\n\t\t_SetDpiAndItemSize(More.Dpi.OfWindow(_w));\n\t\t\n\t\treturn new HandleRef(this, _w.Handle);\n\t}\n\t\n\tprotected override void DestroyWindowCore(HandleRef hwnd) {\n\t\t//print.it(\"DestroyWindowCore\", (wnd)hwnd.Handle);\n\t\tApi.DestroyWindow(_w);\n\t}\n\t\n\tWNDPROC _wndProc;\n\tnint _WndProc(wnd w, int msg, nint wParam, nint lParam) {\n\t\t//var pmo = new PrintMsgOptions(Api.WM_NCHITTEST, Api.WM_SETCURSOR, Api.WM_MOUSEMOVE, Api.WM_NCMOUSEMOVE, 0x10c1);\n\t\t//if (WndUtil.PrintMsg(out string s, _w, msg, wParam, lParam, pmo)) print.it(\"<><c green>\" + s + \"<>\");\n\t\t//if (_test) if (WndUtil.PrintMsg(out string s, _w, msg, wParam, lParam)) print.it(\"<><c green>\" + s + \"<>\");\n\t\t\n\t\tif (_vscroll.WndProc(w, msg, wParam, lParam) || _hscroll.WndProc(w, msg, wParam, lParam)) return default;\n\t\t\n\t\tswitch (msg) {\n\t\t//case Api.WM_NCCREATE:\n\t\t//\t_w = w;\n\t\t//\t_hasHwnd = true;\n\t\t//\t_SetDpiAndItemSize(More.Dpi.OfWindow(_w));\n\t\t//\tbreak;\n\t\tcase Api.WM_NCDESTROY:\n\t\t\t_w = default;\n\t\t\t_hasHwnd = false;\n\t\t\t_acc?.Dispose(); _acc = null;\n\t\t\tbreak;\n\t\tcase Api.WM_PAINT:\n\t\t\tusing (var bp = new BufferedPaint(w, true)) _Render(bp.DC, bp.UpdateRect);\n\t\t\treturn default;\n\t\tcase Api.WM_SHOWWINDOW when wParam == 1:\n\t\t\tint iev = _ensureVisible.indexPlus1 - 1;\n\t\t\tif (iev > 0) EnsureVisible(iev, _ensureVisible.scrollTop);\n\t\t\tbreak;\n\t\tcase Api.WM_SIZE:\n\t\t\t_width = Math2.LoWord(lParam);\n\t\t\t_height = Math2.HiWord(lParam);\n\t\t\t_Measure();\n\t\t\tbreak;\n\t\tcase Api.WM_SYSCOMMAND when (wParam & 0xFFF0) is Api.SC_VSCROLL or Api.SC_HSCROLL: //note: Windows bug: swapped SC_VSCROLL and SC_HSCROLL\n\t\t\ttry {\n\t\t\t\t_inScrollbarScroll = true;\n\t\t\t\treturn Api.DefWindowProc(w, msg, wParam, lParam);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\t_ScrollEnded();\n\t\t\t\t_inScrollbarScroll = false;\n\t\t\t}\n\t\tcase >= Api.WM_MOUSEFIRST and <= Api.WM_MOUSELAST:\n\t\t\tswitch (msg) {\n\t\t\tcase Api.WM_LBUTTONDOWN:\n\t\t\t\t_OnMouseDown(System.Windows.Input.MouseButton.Left, wParam, lParam);\n\t\t\t\tbreak;\n\t\t\tcase Api.WM_RBUTTONDOWN:\n\t\t\t\t_OnMouseDown(System.Windows.Input.MouseButton.Right, wParam, lParam);\n\t\t\t\tbreak;\n\t\t\tcase Api.WM_MBUTTONDOWN:\n\t\t\t\t_OnMouseDown(System.Windows.Input.MouseButton.Middle, wParam, lParam);\n\t\t\t\tbreak;\n\t\t\tcase Api.WM_LBUTTONDBLCLK:\n\t\t\t\t_OnMouseDown(System.Windows.Input.MouseButton.Left, wParam, lParam, true);\n\t\t\t\tbreak;\n\t\t\tcase Api.WM_MBUTTONDBLCLK:\n\t\t\t\t_OnMouseDown(System.Windows.Input.MouseButton.Middle, wParam, lParam, true);\n\t\t\t\tbreak;\n\t\t\tcase Api.WM_LBUTTONUP:\n\t\t\t\t_OnMouseUp(System.Windows.Input.MouseButton.Left);\n\t\t\t\tbreak;\n\t\t\tcase Api.WM_RBUTTONUP:\n\t\t\t\tif (!_OnMouseUp(System.Windows.Input.MouseButton.Right))\n\t\t\t\t\tif (!HitTest(Math2.NintToPOINT(lParam), out _)) RightClickInEmptySpace?.Invoke();\n\t\t\t\tbreak;\n\t\t\tcase Api.WM_MBUTTONUP:\n\t\t\t\t_OnMouseUp(System.Windows.Input.MouseButton.Middle);\n\t\t\t\tbreak;\n\t\t\tcase Api.WM_MOUSEMOVE:\n\t\t\t\t_OnMouseMove(wParam);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\treturn 0;\n\t\tcase Api.WM_MOUSELEAVE:\n\t\t\t_OnMouseLeave();\n\t\t\tbreak;\n\t\tcase Api.WM_CAPTURECHANGED:\n\t\t\t_mouse.active = false;\n\t\t\tbreak;\n\t\t}\n\t\t\n\t\tvar R = Api.DefWindowProc(w, msg, wParam, lParam);\n\t\t\n\t\tswitch (msg) {\n\t\tcase Api.WM_NCHITTEST when R == Api.HTCLIENT: //let WPF manage drag-drop\n\t\t\tif (Api.GetCapture().ClassNameIs(\"CLIPBRDWNDCLASS\")) R = Api.HTTRANSPARENT;\n\t\t\tbreak;\n\t\t}\n\t\t\n\t\treturn R;\n\t}\n\t\n\tprotected override nint WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) {\n\t\tif (msg == Api.WM_GETOBJECT) { //not in _WndProc, because WPF steals it if passed to base.WndProc\n\t\t\thandled = true;\n\t\t\treturn (_acc ??= new _Accessible(this)).WmGetobject(wParam, lParam);\n\t\t}\n\t\treturn base.WndProc(hwnd, msg, wParam, lParam, ref handled);\n\t}\n\t\n\t//protected override System.Windows.Automation.Peers.AutomationPeer OnCreateAutomationPeer() => null; //removes unused object from MSAA tree, but then no UIA\n\t_Accessible _acc;\n\t\n\tprotected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e) {\n\t\t//print.it(e.Property);\n\t\tif (!_leEdit.Is0 && e.Property.Name == \"IsVisible\" && e.NewValue is bool y && !y) EndEditLabel(true);\n\t\tbase.OnPropertyChanged(e);\n\t}\n\t\n\t///\n\tprotected override bool TabIntoCore(TraversalRequest r) {\n\t\tFocus();\n\t\treturn true;\n\t}\n\t\n\t///\n\tprotected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters)\n\t\t=> new PointHitTestResult(this, hitTestParameters.HitPoint);\n\t//need this for drag-drop. See case Api.WM_NCHITTEST above.\n}\n"
  },
  {
    "path": "Au.Controls/KTreeView/tv-misc.cs",
    "content": "using System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Controls.Primitives;\n\nnamespace Au.Controls;\n\npublic unsafe partial class KTreeView {\n\tbool _IsValid(int index) => (uint)index < _avi.Lenn_();\n\t\n\t//unused\n\t//bool _IndexToItemStruct(int i, out _VisibleItem item) {\n\t//\tbool r = _IsValid(i);\n\t//\titem = r ? _avi[i] : default;\n\t//\treturn r;\n\t//}\n\t\n\tbool _IndexToItem(int i, out ITreeViewItem item) {\n\t\tbool r = _IsValid(i);\n\t\titem = r ? _avi[i].item : null;\n\t\treturn r;\n\t}\n\t\n\tITreeViewItem _IndexToItem(int i) => _IsValid(i) ? _avi[i].item : null;\n\t\n\tint _IndexOfOrThrow(ITreeViewItem item, bool canBeNull = false) {\n\t\tint i = -1;\n\t\tif (item != null) { i = IndexOf(item); if (i < 0) throw new ArgumentException(); } else if (!canBeNull) throw new ArgumentNullException();\n\t\treturn i;\n\t}\n\t\n\tbool _IndexOfOrThrowIfImportant(bool important, ITreeViewItem item, out int index) {\n\t\tindex = IndexOf(item);\n\t\tif (index < 0) return !important ? false : throw new ArgumentException();\n\t\treturn true;\n\t}\n\t\n\tbool _IsInside(int iParent, int iChild) {\n\t\tint i = iParent;\n\t\tif (iChild > i) {\n\t\t\tvar level = _avi[i].level;\n\t\t\twhile (++i < _avi.Length && _avi[i].level > level) if (i == iChild) return true;\n\t\t}\n\t\t//never mind: faster would be to use Parent. Would need to add Parent to the interface. Currently don't need speed.\n\t\treturn false;\n\t}\n\t\n\t//unused\n\t//bool _IsInside(ITreeViewItem parent, int iChild) {\n\t//\tint i = IndexOf(parent);\n\t//\treturn i >= 0 && _IsInside(i, iChild);\n\t//}\n\t\n\tint _DpiScale(int value) => More.Dpi.Scale(value, _dpi);\n\t\n\tint _ItemTop(int index) => (index - _vscroll.Pos) * _itemHeight;\n\t\n\t/// <summary>\n\t/// Returns item index, or -1 if not on item.\n\t/// </summary>\n\t/// <param name=\"y\">In control coord, physical.</param>\n\tint _ItemFromY(int y) {\n\t\tint i = y / _itemHeight + _vscroll.Pos;\n\t\tif (!_IsValid(i)) i = -1;\n\t\treturn i;\n\t}\n\t\n\tstruct _PartOffsets {\n\t\tpublic int left, checkbox, marginLeft, image, text, marginRight, right;\n\t\tpublic bool hasCheckbox;\n\t}\n\t\n\tvoid _GetPartOffsets(int i, out _PartOffsets p, bool measuring = false) {\n\t\tvar no = _avi[i].noParts;\n\t\tp.left = measuring ? 0 : -_hscroll.Offset;\n\t\tp.checkbox = p.left + _Indent(_avi[i]);\n\t\tp.hasCheckbox = HasCheckboxes && !no.Has(TVParts.Checkbox);\n\t\tp.marginLeft = p.checkbox + (p.hasCheckbox ? _itemLineHeight : 0);\n\t\tp.image = p.marginLeft + (no.Has(TVParts.MarginLeft) ? 0 : _marginLeft);\n\t\tp.text = p.image + (no.Has(TVParts.Image) ? 0 : _imageSize + _imageMarginX) + _imageMarginX;\n\t\tp.marginRight = p.text + _avi[i].measured;\n\t\tp.right = p.marginRight + (no.Has(TVParts.MarginRight) ? 0 : _marginRight);\n\t\t//print.it(p.checkbox, p.marginLeft, p.image, p.text, p.marginRight, p.right);\n\t\t\n\t\tint _Indent(in _VisibleItem v) {\n\t\t\tint r = v.level; if (r > 0 && v.noParts.Has(TVParts.Left)) r--;\n\t\t\tint j = _imageSize; if (SmallIndent) j /= 2;\n\t\t\treturn r * j;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Gets item rectangle in physical pixel units.\n\t/// Horizontally the rectangle is limited to the visible area.\n\t/// </summary>\n\t/// <exception cref=\"IndexOutOfRangeException\"></exception>\n\t/// <exception cref=\"InvalidOperationException\">Control not created.</exception>\n\tpublic RECT GetRectPhysical(int index, TVParts parts = 0, bool inScreen = false, bool clampX = false) {\n\t\tif (!_IsValid(index)) throw new IndexOutOfRangeException();\n\t\tif (!_hasHwnd) throw new InvalidOperationException();\n\t\tint y = _ItemTop(index);\n\t\tvar r = new RECT(0, y, _width, _itemHeight);\n\t\tif (parts != 0) {\n\t\t\t_GetPartOffsets(index, out var k);\n\t\t\t//left\n\t\t\tif (parts.Has(TVParts.Left)) r.left = k.left;\n\t\t\telse if (parts.Has(TVParts.Checkbox)) r.left = k.checkbox;\n\t\t\telse if (parts.Has(TVParts.MarginLeft)) r.left = k.marginLeft;\n\t\t\telse if (parts.Has(TVParts.Image)) r.left = k.image;\n\t\t\telse if (parts.Has(TVParts.Text)) r.left = k.text;\n\t\t\telse if (parts.Has(TVParts.MarginRight)) r.left = k.marginRight;\n\t\t\telse r.left = k.right;\n\t\t\t//right\n\t\t\tif (parts.Has(TVParts.Right)) r.right = Math.Max(k.right, _width);\n\t\t\telse if (parts.Has(TVParts.MarginRight)) r.right = k.right;\n\t\t\telse if (parts.Has(TVParts.Text)) r.right = k.marginRight;\n\t\t\telse if (parts.Has(TVParts.Image)) r.right = k.text;\n\t\t\telse if (parts.Has(TVParts.MarginLeft)) r.right = k.image;\n\t\t\telse if (parts.Has(TVParts.Checkbox)) r.right = k.marginLeft;\n\t\t\telse r.right = k.checkbox;\n\t\t\t//clamp\n\t\t\tif (clampX) {\n\t\t\t\tr.left = Math.Clamp(r.left, 0, _width);\n\t\t\t\tr.right = Math.Clamp(r.right, 0, _width);\n\t\t\t}\n\t\t} else if (!clampX) {\n\t\t\tr.left = -_hscroll.Offset;\n\t\t\tr.right = Math.Max(_width, _itemsWidth - r.left);\n\t\t}\n\t\tif (inScreen) _w.MapClientToScreen(ref r);\n\t\treturn r;\n\t}\n\t\n\t/// <summary>\n\t/// Gets item rectangle in WPF logical pixel units.\n\t/// </summary>\n\t/// <exception cref=\"IndexOutOfRangeException\"></exception>\n\tpublic Rect GetRectLogical(int index, TVParts parts = 0, bool inScreen = false, bool clampX = false) {\n\t\tvar r = GetRectPhysical(index, parts, inScreen, clampX);\n\t\tdouble f = 96d / _dpi;\n\t\treturn new Rect(r.left * f, r.top * f, r.Width * f, r.Height * f);\n\t}\n\t\n\t/// <summary>\n\t/// Gets item from point, and its part.\n\t/// Returns false if not on an item.\n\t/// </summary>\n\t/// <param name=\"p\">Point in control coordinates. Physical pixels.</param>\n\t/// <param name=\"h\">Results.</param>\n\tpublic bool HitTest(POINT p, out TVHitTest h) {\n\t\th = default;\n\t\tif ((uint)p.x < _width && (uint)p.y < _height) {\n\t\t\tint i = _ItemFromY(p.y);\n\t\t\tif (_IndexToItem(i, out var v)) {\n\t\t\t\th.index = i;\n\t\t\t\th.item = v;\n\t\t\t\t_GetPartOffsets(i, out var k);\n\t\t\t\tint x = p.x;\n\t\t\t\tif (x < k.checkbox) h.part = TVParts.Left;\n\t\t\t\telse if (x < k.marginLeft) h.part = TVParts.Checkbox;\n\t\t\t\telse if (x < k.image) h.part = TVParts.MarginLeft;\n\t\t\t\telse if (x < k.text) h.part = TVParts.Image;\n\t\t\t\telse if (x < k.marginRight) h.part = TVParts.Text;\n\t\t\t\telse if (x < k.right) h.part = TVParts.MarginRight;\n\t\t\t\telse h.part = TVParts.Right;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\th.index = -1;\n\t\treturn false;\n\t}\n\t\n\t/// <summary>\n\t/// Gets item from mouse, and its part.\n\t/// Returns false if not on an item.\n\t/// </summary>\n\t/// <param name=\"h\">Results.</param>\n\tpublic bool HitTest(out TVHitTest h) => HitTest(_w.MouseClientXY, out h);\n\t\n\t_LabelTip _labeltip;\n\t\n\tclass _LabelTip {\n\t\treadonly KTreeView _tv;\n\t\tToolTip _tt;\n\t\ttimer _timer;\n\t\tint _i;\n\t\t_VisibleItem[] _avi;\n\t\t\n\t\tpublic _LabelTip(KTreeView tv) { _tv = tv; }\n\t\t\n\t\tpublic void HotChanged(int i) {\n\t\t\tif (i >= 0) {\n\t\t\t\tvar r = _tv.GetRectPhysical(i, TVParts.Text, clampX: true);\n\t\t\t\tif (_tv._avi[i].measured > r.Width) {\n\t\t\t\t\t_timer ??= new timer(_Show);\n\t\t\t\t\t_avi = _tv._avi; _i = i;\n\t\t\t\t\t_timer.After(300);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tHide();\n\t\t}\n\t\t\n\t\tvoid _Show(timer t) {\n\t\t\tif (_avi != _tv._avi || _i != _tv._hotIndex) return;\n\t\t\tif (_tt == null) {\n\t\t\t\t_tt = new ToolTip() {\n\t\t\t\t\tPlacementTarget = _tv, //need for correct DPI in non-primary screen\n\t\t\t\t\tPlacement = PlacementMode.Right,\n\t\t\t\t\tHorizontalOffset = 4,\n\t\t\t\t\tHasDropShadow = false //why no shadow on Win10?\n\t\t\t\t};\n\t\t\t\tif (osVersion.minWin8) _tt.Padding = new(2, -1, 2, 1); //on Win7 makes too small\n\t\t\t}\n\t\t\tvar r = _tv.GetRectLogical(_i, clampX: true);\n\t\t\tr.X -= 4; r.Width += 4;\n\t\t\t_tt.PlacementRectangle = r;\n\t\t\t_tt.Content = _avi[_i].item.DisplayText;\n\t\t\t_tt.IsOpen = true;\n\t\t}\n\t\t\n\t\tpublic void Hide() {\n\t\t\t_avi = null;\n\t\t\t_timer?.Stop();\n\t\t\tif (_tt != null && _tt.IsOpen) _tt.IsOpen = false;\n\t\t}\n\t}\n\t\n\t_Tooltip _tooltip;\n\t\n\tclass _Tooltip {\n\t\treadonly KTreeView _tv;\n\t\tToolTip _tt;\n\t\ttimer _timer;\n\t\t_VisibleItem[] _avi;\n\t\tint _i, _td;\n\t\t\n\t\tpublic _Tooltip(KTreeView tv) { _tv = tv; }\n\t\t\n\t\tpublic void HotChanged(int i) {\n\t\t\tHide();\n\t\t\tif (i >= 0 && _tv._IndexToItem(i, out var item) && item.TooltipDelay is int td && td > 0) {\n\t\t\t\t_timer ??= new timer(_Timer);\n\t\t\t\t_avi = _tv._avi; _i = i;\n\t\t\t\t_td = td;\n\t\t\t\t_timer.Every(50);\n\t\t\t}\n\t\t}\n\t\t\n\t\tvoid _Timer(timer t) {\n\t\t\tif (_avi != _tv._avi || _i != _tv._hotIndex) { t.Stop(); return; }\n\t\t\tif (Environment.TickCount64 - Api.GetLastInputTime() < _td) return;\n\t\t\tt.Stop();\n\t\t\t_tt ??= new ToolTip() {\n\t\t\t\tPlacementTarget = _tv, //need for correct DPI in non-primary screen\n\t\t\t\tPlacement = PlacementMode.Mouse,\n\t\t\t\tHorizontalOffset = 20,\n\t\t\t};\n\t\t\t_avi[_i].item.TooltipSetContent(_tt);\n\t\t\t_tt.IsOpen = true;\n\t\t}\n\t\t\n\t\tpublic void Hide() {\n\t\t\t_avi = null;\n\t\t\t_timer?.Stop();\n\t\t\tif (_tt != null && _tt.IsOpen) _tt.IsOpen = false;\n\t\t}\n\t}\n\t\n\t\n}\n"
  },
  {
    "path": "Au.Controls/KTreeView/tv-render.cs",
    "content": "using System.Drawing;\n\nnamespace Au.Controls;\n\npublic unsafe partial class KTreeView {\n\tbool _dontMeasure;\n\t\n\t//Called on resize, scroll, set visible items (replace, clear, expand/collapse, add/remove/move).\n\tvoid _Measure(bool onScroll = false) {\n\t\tif (_dontMeasure) return; //this func is adding/removing scrollbars\n\t\tEndEditLabel();\n\t\t_labeltip?.Hide();\n\t\t_tooltip?.Hide();\n\t\t\n\t\tint sbV = More.Dpi.ScrollbarV_(_dpi), sbH = More.Dpi.ScrollbarH_(_dpi);\n\t\tvar rw = _w.Rect; //never mind: minus border. We don't use border.\n\t\tint width = rw.Width, height = rw.Height;\n\t\tif (width <= sbV || height <= sbH || _avi.NE_()) {\n\t\t\tNativeScrollbar_.ShowVH(_vscroll, false, _hscroll, false);\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tint oldScrollPos = _vscroll.Pos;\n\t\tg1:\n\t\tvar range = _GetViewRange();\n\t\t\n\t\tint maxWidth = _itemsWidth;\n\t\tGdiTextRenderer tr = null;\n\t\tfor (int i = range.from; i < range.to; i++) {\n\t\t\tif (_avi[i].measured > 0) continue;\n\t\t\ttr ??= new GdiTextRenderer(_dpi);\n\t\t\tvar item = _avi[i].item;\n\t\t\tbool bold = item.IsBold; if (bold) tr.FontBold();\n\t\t\tint textWidth = item.MesureTextWidth(tr);\n\t\t\tif (textWidth < 0) textWidth = tr.MeasureText(item.DisplayText).width;\n\t\t\tif (bold) tr.FontNormal();\n\t\t\t_avi[i].measured = textWidth + 2;\n\t\t\t_avi[i].noParts = item.NoParts;\n\t\t\t_GetPartOffsets(i, out var p, true); //parts left-to-right: indent, checkbox, left margin, image, text, right margin\n\t\t\tmaxWidth = Math.Max(maxWidth, p.right);\n\t\t}\n\t\ttr?.Dispose();\n\t\tif (maxWidth > _itemsWidth) _itemsWidth = maxWidth; else if (onScroll) return;\n\t\t\n\t\t//set scrollbars\n\t\tint itemsHeight = _avi.Length * _itemHeight;\n\t\tbool needH = _itemsWidth > width && height >= _imageSize + sbH; if (needH) height -= sbH;\n\t\tbool needV = itemsHeight > height && _avi.Length > 1;\n\t\tif (needV) { width -= sbV; if (!needH) needH = _itemsWidth > width && height >= _imageSize + sbH; }\n\t\tif (_scrollCorrection = (needH && onScroll && _inScrollbarScroll && !_hscroll.Visible)) needH = false;\n\t\t_dontMeasure = true;\n\t\tNativeScrollbar_.ShowVH(_vscroll, needV, _hscroll, needH);\n\t\t_dontMeasure = false;\n\t\tif (needV) _vscroll.SetRange(_avi.Length); else _vscroll.NItems = _avi.Length;\n\t\tif (needH) _hscroll.SetRange(_itemsWidth); else _hscroll.NItems = 0;\n\t\t\n\t\tif (!onScroll && _vscroll.Pos != oldScrollPos && oldScrollPos >= 0) { //may scroll up after collapsing a folder\n\t\t\toldScrollPos = -1;\n\t\t\tgoto g1;\n\t\t}\n\t}\n\tbool _inScrollbarScroll;\n\tbool _scrollCorrection;\n\t\n\tvoid _ScrollEnded() {\n\t\t//workaround for: if we add other scrollbar while SB_THUMBTRACK-scrolling, does not scroll to the very bottom. Would need to scroll 2 times.\n\t\t//\tAlso, if while SB_LINEDOWN-scrolling, the scroll box arrow remains not erased at the bottom-right corner.\n\t\t//\tAlas scintilla has these problems too.\n\t\tif (_scrollCorrection) {\n\t\t\t_scrollCorrection = false;\n\t\t\tint max = _vscroll.Max, pos = _vscroll.Pos;\n\t\t\t_hscroll.Visible = true;\n\t\t\t_hscroll.SetRange(_itemsWidth);\n\t\t\tif (_vscroll.Max > max && pos == max) EnsureVisible(_avi.Length - 1);\n\t\t}\n\t}\n\t\n\tvoid _MeasureClear(bool updateNow) {\n\t\tif (!_hasHwnd) return;\n\t\tif (_avi != null) for (int i = 0; i < _avi.Length; i++) _avi[i].measured = 0;\n\t\t_itemsWidth = 0;\n\t\tif (updateNow) {\n\t\t\t_Measure();\n\t\t\t_Invalidate();\n\t\t}\n\t}\n\t\n\t(int from, int to) _GetViewRange() {\n\t\tint len = _avi.Lenn_(); if (len == 0) return default;\n\t\tint i = _vscroll.Pos + (_height + _itemHeight - 1) / _itemHeight;\n\t\treturn (_vscroll.Pos, Math.Min(i, len));\n\t}\n\t\n\tvoid _Invalidate(RECT* r = null) {\n\t\tif (_hasHwnd) Api.InvalidateRect(_w, r, false);\n\t}\n\t\n\tvoid _Invalidate(int index) {\n\t\tif (!_hasHwnd) return;\n\t\tvar r = GetRectPhysical(index, clampX: true);\n\t\tif (r.bottom > 0 && r.top < _height) Api.InvalidateRect(_w, &r, false);\n\t}\n\t\n\t/// <summary>\n\t/// Asynchronously redraws item.\n\t/// Does nothing if the control is not created.\n\t/// </summary>\n\t/// <param name=\"index\"></param>\n\t/// <param name=\"remeasure\">Remeasure item width. My need this when changed text or text style.</param>\n\t/// <exception cref=\"IndexOutOfRangeException\"></exception>\n\tpublic void Redraw(int index, bool remeasure = false) {\n\t\tif (!_hasHwnd) return;\n\t\tif (!_IsValid(index)) throw new IndexOutOfRangeException();\n\t\tif (remeasure) {\n\t\t\t//if this was the widest measured item, need to remeasure all, else would not update horz scrollbar if this item become narrower\n\t\t\tint max = 0; for (int i = 0; i < _avi.Length; i++) max = Math.Max(max, _avi[i].measured);\n\t\t\tif (max == _avi[index].measured) {\n\t\t\t\t_MeasureClear(updateNow: true);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t_avi[index].measured = 0;\n\t\t\t_Measure();\n\t\t}\n\t\t_Invalidate(index);\n\t}\n\t\n\t/// <summary>\n\t/// Asynchronously redraws item.\n\t/// Does nothing if the control is not created or <i>item</i> is not a visible item in this control.\n\t/// </summary>\n\t/// <param name=\"item\"></param>\n\t/// <param name=\"remeasure\">Remeasure item width. My need this when changed text or text style.</param>\n\tpublic void Redraw(ITreeViewItem item, bool remeasure = false) {\n\t\tif (!_hasHwnd) return;\n\t\tint i = IndexOf(item);\n\t\tif (i >= 0) Redraw(i, remeasure);\n\t}\n\t\n\t/// <summary>\n\t/// Asynchronously redraws all items.\n\t/// Does nothing if the control is not created.\n\t/// </summary>\n\t/// <param name=\"remeasure\">Remeasure item width. My need this when changed text or text style.</param>\n\tpublic void Redraw(bool remeasure = false) {\n\t\tif (!_hasHwnd) return;\n\t\tif (remeasure) _MeasureClear(updateNow: true);\n\t\telse _Invalidate();\n\t}\n\t\n\t//\t/// <summary>\n\t//\t/// Remeasure item widths and redraws. My need this when changed text or text style.\n\t//\t/// Does nothing if the control is not created.\n\t//\t/// </summary>\n\t//\t/// <param name=\"indices\"></param>\n\t//\t/// <exception cref=\"ArgumentOutOfRangeException\"></exception>\n\t//\tpublic void Remeasure(Range indices) {\n\t//\t\tif(_border==null) return;\n\t//\t\tvar (i, to)=indices.GetStartEnd(_avi.Lenn_());\n\t//\t\twhile(i<to) _avi[i++].measured=0;\n\t//\t\t_Measure();\n\t//\t\t_Invalidate();\n\t//\t}\n\t\n\t/// <summary>\n\t/// Image background brush.\n\t/// Does not redraw.\n\t/// </summary>\n\tpublic Brush ImageBrush { get; set; }\n\t\n\t/// <summary>\n\t/// Background color.\n\t/// Ignored if high contrast.\n\t/// </summary>\n\tpublic ColorInt? BackgroundColor { get; set; }\n\t\n\t//public ColorInt? TextColor { get; set; }\n\t\n\tpublic const int ColorSelectedItemFocused = 0xc4d5ff;\n\t\n\tpublic const int ColorSelectedItemNonfocused = 0xe0e0e0;\n\t\n\tpublic const int ColorHotItem = 0xe8f0ff;\n\t\n\t/// <summary>\n\t/// Smaller indentation.\n\t/// </summary>\n\tpublic bool SmallIndent { get; set; }\n\t\n\tvoid _Render(IntPtr dc, RECT rUpdate) {\n\t\tGraphics graphics = null;\n\t\ttry {\n\t\t\tTVColorInfo colorInfo = new() { isHighContrast = System.Windows.SystemParameters.HighContrast };\n\t\t\t\n\t\t\tbool haveBackColor = BackgroundColor != null && !colorInfo.isHighContrast;\n\t\t\tif (haveBackColor) {\n\t\t\t\tgraphics = Graphics.FromHdc(dc);\n\t\t\t\tgraphics.Clear((Color)BackgroundColor.Value);\n\t\t\t} else {\n\t\t\t\tApi.FillRect(dc, rUpdate, (IntPtr)(Api.COLOR_WINDOW + 1));\n\t\t\t}\n\t\t\tif (_avi.NE_()) return;\n\t\t\t\n\t\t\tvar range = _GetViewRange();\n\t\t\tint nDraw = range.to - range.from;\n\t\t\tif (nDraw > 0) {\n\t\t\t\tint backColor = haveBackColor ? BackgroundColor.Value.ToBGR() : Api.GetSysColor(Api.COLOR_WINDOW);\n\t\t\t\tint textColor = Api.GetSysColor(Api.COLOR_WINDOWTEXT);\n\t\t\t\t\n\t\t\t\tcolorInfo.isTextBlack = textColor == 0;\n\t\t\t\tif (colorInfo.isHighContrast) colorInfo.isHighContrastDark = WpfUtil_.IsHighContrastDark;\n\t\t\t\tcolorInfo.isFocusedControl = this.IsKeyboardFocused;\n\t\t\t\t\n\t\t\t\tint yyImages = (_itemLineHeight + 1 - _imageSize) / 2;\n\t\t\t\t//int yyText = _itemLineHeight <= 22 ? 1 : _itemLineHeight <= 28 ? 0 : -1; //when high DPI, text used to be too low, but now on Win11 it seems good, don't need this correction\n\t\t\t\tint yyText = 1;\n\t\t\t\t\n\t\t\t\tgraphics ??= Graphics.FromHdc(dc);\n\t\t\t\tvar tr = new GdiTextRenderer(dc, _dpi);\n\t\t\t\tIntPtr checkTheme = HasCheckboxes ? Api.OpenThemeData(_w, \"Button\", _dpi) : default;\n\t\t\t\ttry {\n\t\t\t\t\tgraphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;\n\t\t\t\t\tSIZE cSize = default;\n\t\t\t\t\tif (HasCheckboxes)\n\t\t\t\t\t\tif (checkTheme == default || 0 != Api.GetThemePartSize(checkTheme, dc, 3, 1, null, Api.THEMESIZE.TS_TRUE, out cSize))\n\t\t\t\t\t\t\tcSize.width = cSize.height = More.Dpi.Scale(13, _dpi);\n\t\t\t\t\t\n\t\t\t\t\tvar cd = CustomDraw;\n\t\t\t\t\tvar cdi = cd == null ? null : new TVDrawInfo(this, dc, graphics, _dpi);\n\t\t\t\t\tcd?.Begin(cdi, tr);\n\t\t\t\t\t\n\t\t\t\t\tfor (int i = 0; i < nDraw; i++) {\n\t\t\t\t\t\tint y = i * _itemHeight;\n\t\t\t\t\t\tvar r = new RECT(0, y, _width, _itemHeight);\n\t\t\t\t\t\tif (!r.IntersectsWith(rUpdate)) continue;\n\t\t\t\t\t\t\n\t\t\t\t\t\tint index = i + range.from;\n\t\t\t\t\t\tvar v = _avi[index];\n\t\t\t\t\t\tvar item = v.item;\n\t\t\t\t\t\tvar noParts = v.noParts;\n\t\t\t\t\t\t_GetPartOffsets(index, out var p); //parts left-to-right: indent, checkbox, left margin, image, text, right margin\n\t\t\t\t\t\tint xImage = p.image + _imageMarginX, yImage = y + yyImages;\n\t\t\t\t\t\tint yText = y + yyText;\n\t\t\t\t\t\t\n\t\t\t\t\t\tcolorInfo.isSelected = v.isSelected;\n\t\t\t\t\t\tcolorInfo.isHot = index == _hotIndex;\n\t\t\t\t\t\tcolorInfo.isFocusedItem = index == _focusedIndex;\n\t\t\t\t\t\t\n\t\t\t\t\t\tif (cdi != null) {\n\t\t\t\t\t\t\tcdi.index = index;\n\t\t\t\t\t\t\tcdi.item = item;\n\t\t\t\t\t\t\tcdi.rect = r;\n\t\t\t\t\t\t\tcdi.imageRect = new RECT(xImage, yImage, noParts.Has(TVParts.Image) ? 0 : _imageSize, _imageSize);\n\t\t\t\t\t\t\tcdi.xText = p.text;\n\t\t\t\t\t\t\tcdi.yText = yText;\n\t\t\t\t\t\t\tcdi.xLeft = p.marginLeft;\n\t\t\t\t\t\t\tcdi.xRight = p.marginRight;\n\t\t\t\t\t\t\tcdi.lineHeight = _itemLineHeight;\n\t\t\t\t\t\t\tcdi.colorInfo = colorInfo;\n\t\t\t\t\t\t\tcdi.marginLeft = noParts.Has(TVParts.MarginLeft) ? 0 : _marginLeft;\n\t\t\t\t\t\t\tcdi.marginRight = noParts.Has(TVParts.MarginRight) ? 0 : _marginRight;\n\t\t\t\t\t\t\tcdi.checkSize = p.hasCheckbox ? cSize : default;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\t//background\n\t\t\t\t\t\tbool hiliteSysColor = false;\n\t\t\t\t\t\tif (cd == null || !cd.DrawBackground()) {\n\t\t\t\t\t\t\tint color = item.Color(colorInfo);\n\t\t\t\t\t\t\tif (color != -1 || (backColor & 0xe0e0e0) == 0xe0e0e0) { //custom color, or default color is [almost] white\n\t\t\t\t\t\t\t\tif (color != -1) //draw custom color\n\t\t\t\t\t\t\t\t\tusing (var b1 = GdiObject_.ColorBrush(color)) b1.BrushFill(dc, r);\n\t\t\t\t\t\t\t\tint alpha = color >>> 24;\n\t\t\t\t\t\t\t\tif (color == -1 || alpha is >= 1 and <= 3) { //if no custom color or if custom color's alpha is 1 - 3, draw selection or hot background\n\t\t\t\t\t\t\t\t\tcolor = -1;\n\t\t\t\t\t\t\t\t\tif (v.isSelected) {\n\t\t\t\t\t\t\t\t\t\tif (0 != (alpha & 1)) {\n\t\t\t\t\t\t\t\t\t\t\tcolor = item.SelectedColor(colorInfo);\n\t\t\t\t\t\t\t\t\t\t\tif (color == -1) color = colorInfo.isFocusedControl ? ColorSelectedItemFocused : ColorSelectedItemNonfocused;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t} else if (HotTrack && index == _hotIndex) {\n\t\t\t\t\t\t\t\t\t\tif (0 != (alpha & 2)) color = ColorHotItem;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tif (color != -1) {\n\t\t\t\t\t\t\t\t\t\tvar r2 = r; r2.left = p.text - _imageMarginX / 2; //don't draw selection background under icon and checkbox\n\t\t\t\t\t\t\t\t\t\tusing (var b2 = GdiObject_.ColorBrush(color)) b2.BrushFill(dc, r2);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else { //probably high contrast or dark BackgroundColor\n\t\t\t\t\t\t\t\tif (hiliteSysColor = v.isSelected) Api.FillRect(dc, r, (IntPtr)(Api.COLOR_HIGHLIGHT + 1));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tcolor = item.BorderColor(colorInfo);\n\t\t\t\t\t\t\tif (color != -1) {\n\t\t\t\t\t\t\t\tint alpha = color >>> 24;\n\t\t\t\t\t\t\t\tvar r2 = r; if (alpha == 1) r2.left = p.text - _imageMarginX / 2 - 1;\n\t\t\t\t\t\t\t\tusing (var b3 = GdiObject_.ColorBrush(color)) b3.BrushRect(dc, r2);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\t//checkbox\n\t\t\t\t\t\tif (p.hasCheckbox && item.CheckState != TVCheck.None) {\n\t\t\t\t\t\t\tif (cd == null || !cd.DrawCheckbox()) {\n\t\t\t\t\t\t\t\t//if(1==(i&3)) item.CheckState=TVCheck.Checked; if(2==(i&3)) item.CheckState=TVCheck.Mixed; if(3==(i&3)) item.CheckState=TVCheck.Excluded; if(0!=(i&4)) v.IsDisabled=true; //test\n\t\t\t\t\t\t\t\tvar rr = new RECT(p.marginLeft - cSize.width - (p.marginLeft - p.checkbox - cSize.width) * 2 / 5, y + (_itemLineHeight - cSize.height) / 2, cSize.width, cSize.height);\n\t\t\t\t\t\t\t\tvar ch = item.CheckState;\n\t\t\t\t\t\t\t\tif (checkTheme != default) {\n\t\t\t\t\t\t\t\t\tint state = ch switch { TVCheck.Checked => 5, TVCheck.RadioChecked => 5, TVCheck.Mixed => 9, TVCheck.Excluded => 17, _ => 1 }; //CBS_x,RBS_x\n\t\t\t\t\t\t\t\t\tif (item.IsDisabled) state += 3; else if (index == _hotIndex) state += 1;\n\t\t\t\t\t\t\t\t\tApi.DrawThemeBackground(checkTheme, dc, (ch == TVCheck.RadioChecked || ch == TVCheck.RadioUnchecked) ? 2 : 3, state, rr); //BP_RADIOBUTTON,BP_CHECKBOX\n\t\t\t\t\t\t\t\t} else if (ch != TVCheck.Excluded) {\n\t\t\t\t\t\t\t\t\tint state = ch switch { TVCheck.Checked => 0x400, TVCheck.Mixed => 0x408, TVCheck.RadioUnchecked => 0x4, TVCheck.RadioChecked => 0x404, _ => 0 }; //DFCS_x\n\t\t\t\t\t\t\t\t\tif (item.IsDisabled) state |= 0x100; else if (index == _hotIndex) state |= 0x1000;\n\t\t\t\t\t\t\t\t\tKApi.DrawFrameControl(dc, rr, 4, state); //DFC_BUTTON\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t//cannot use .NET CheckBoxRenderer etc because no per-monitor DPI.\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\tif (!noParts.Has(TVParts.Image)) {\n\t\t\t\t\t\t\t//image background\n\t\t\t\t\t\t\tif (ImageBrush != null) {\n\t\t\t\t\t\t\t\tint imm = (_imageMarginX + 1) / 2;\n\t\t\t\t\t\t\t\tgraphics.FillRectangle(ImageBrush, xImage - imm, y, _imageSize + imm * 2, _itemLineHeight);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t//image\n\t\t\t\t\t\t\t_DrawImage(item.Image);\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tvoid _DrawImage(object imo) {\n\t\t\t\t\t\t\t\tBitmap b = null;\n\t\t\t\t\t\t\t\tswitch (imo) {\n\t\t\t\t\t\t\t\tcase Bitmap v:\n\t\t\t\t\t\t\t\t\tb = v;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tcase string v:\n\t\t\t\t\t\t\t\t\tif (v == \"link:\") { v = s_stockLinkIcon.Value; if (v == null) break; }\n\t\t\t\t\t\t\t\t\tb = ImageCache.Get(v, _dpi, ImageUtil.HasImageOrResourcePrefix(v));\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tcase IEnumerable<object> v:\n\t\t\t\t\t\t\t\t\tforeach (var o in v) _DrawImage(o);\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tif (b != null) {\n\t\t\t\t\t\t\t\t\ttry { //exception if Image.LoadBitmap loaded an invalid or ico file\n\t\t\t\t\t\t\t\t\t\tif (cd == null || !cd.DrawImage(b)) {\n\t\t\t\t\t\t\t\t\t\t\tgraphics.DrawImage(b, new Rectangle(xImage, yImage, _imageSize, _imageSize));\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tcatch (Exception ex) { Debug_.Print(ex); }\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\t//text\n\t\t\t\t\t\tif (cd == null || !cd.DrawText()) {\n\t\t\t\t\t\t\tbool bold = item.IsBold; if (bold) tr.FontBold();\n\t\t\t\t\t\t\tint color = item.TextColor(colorInfo);\n\t\t\t\t\t\t\tif (color == -1) color = item.IsDisabled ? Api.GetSysColor(Api.COLOR_GRAYTEXT) : hiliteSysColor ? Api.GetSysColor(Api.COLOR_HIGHLIGHTTEXT) : textColor;\n\t\t\t\t\t\t\telse color = ColorInt.SwapRB(color);\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t//tr.DrawText(item.DisplayText, (p.text, yText), color);\n\t\t\t\t\t\t\tRECT rt = new(p.text, yText, v.measured, r.bottom - yText);\n\t\t\t\t\t\t\ttr.DrawText(item.DisplayText, rt, color);\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tif (bold) tr.FontNormal();\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\tif (cd != null) {\n\t\t\t\t\t\t\tif (!noParts.Has(TVParts.MarginLeft)) cd.DrawMarginLeft();\n\t\t\t\t\t\t\tif (!noParts.Has(TVParts.MarginRight)) cd.DrawMarginRight();\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\t//drag & drop insertion mark\n\t\t\t\t\t\tif (_dd != null && _dd.insertIndex == index) {\n\t\t\t\t\t\t\tint thick = More.Dpi.Scale(3, _dpi);\n\t\t\t\t\t\t\tusing var pen = new Pen(SystemColors.WindowText, thick);\n\t\t\t\t\t\t\ty += thick / 2; int h1 = _itemHeight - thick;\n\t\t\t\t\t\t\tif (_dd.insertFolder) {\n\t\t\t\t\t\t\t\tgraphics.DrawRectangle(pen, xImage, y, _imageSize, h1);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tif (_dd.insertAfter) y += h1;\n\t\t\t\t\t\t\t\tgraphics.DrawLine(pen, p.checkbox, y, _width, y);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tcd?.End();\n\t\t\t\t}\n\t\t\t\tfinally {\n\t\t\t\t\ttr.Dispose();\n\t\t\t\t\tif (checkTheme != default) Api.CloseThemeData(checkTheme);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfinally { graphics?.Dispose(); }\n\t}\n\t\n\tstatic string _StockLinkLocation() {\n\t\tif (!icon.GetStockIconLocation_(StockIcon.LINK, out var ipath, out int iindex)) return null;\n\t\treturn ipath + \",\" + iindex.ToS();\n\t}\n\tstatic Lazy<string> s_stockLinkIcon = new(_StockLinkLocation);\n\t\n\t/// <summary>\n\t/// Gets or sets image cache.\n\t/// If not set, uses <see cref=\"IconImageCache.Common\"/>.\n\t/// </summary>\n\tpublic IconImageCache ImageCache {\n\t\tget => _imageCache ??= IconImageCache.Common;\n\t\tset { _imageCache = value; }\n\t}\n\tIconImageCache _imageCache;\n\t\n\t/// <summary>\n\t/// Width of custom-draw area before image. For example for state images.\n\t/// Use WPF logical units, not physical pixels.\n\t/// </summary>\n\tpublic int ItemMarginLeft {\n\t\tget => _itemMarginLeft_;\n\t\tset {\n\t\t\tif (value != _itemMarginLeft_) {\n\t\t\t\t_itemMarginLeft_ = value;\n\t\t\t\t_marginLeft = _DpiScale(value);\n\t\t\t\t_MeasureClear(true);\n\t\t\t}\n\t\t}\n\t}\n\tint _itemMarginLeft_;\n\t\n\t/// <summary>\n\t/// Width of custom-draw area after item text.\n\t/// Use WPF logical units, not physical pixels.\n\t/// </summary>\n\tpublic int ItemMarginRight {\n\t\tget => _itemMarginRight_;\n\t\tset {\n\t\t\tif (value != _itemMarginRight_) {\n\t\t\t\t_itemMarginRight_ = value;\n\t\t\t\t_marginRight = _DpiScale(value);\n\t\t\t\t_MeasureClear(true);\n\t\t\t}\n\t\t}\n\t}\n\tint _itemMarginRight_;\n\t\n\t/// <summary>\n\t/// Custom-draw interface.\n\t/// </summary>\n\tpublic ITVCustomDraw CustomDraw {\n\t\tget => _customDraw_;\n\t\tset {\n\t\t\tif (value != _customDraw_) {\n\t\t\t\t_customDraw_ = value;\n\t\t\t\t_Invalidate();\n\t\t\t}\n\t\t}\n\t}\n\tITVCustomDraw _customDraw_;\n\t\n\t/// <summary>\n\t/// Add this % to the item height. The area is below the main text/icon/checkbox. The control does not draw in it. Let your custom draw class draw there.\n\t/// </summary>\n\tpublic int CustomItemHeightAddPercent { get; set; }\n\t\n\t///// <summary>\n\t///// If not null, fills background with this color instead of the system window background color.\n\t///// Set this property at startup (does not update control when changed).\n\t///// </summary>\n\t//public ColorInt? BackgroundColor { get; set; }\n#pragma warning restore 1591\n}\n"
  },
  {
    "path": "Au.Controls/KTreeView/tv-types.cs",
    "content": "using System.Windows.Input;\n\nnamespace Au.Controls;\n\n/// <summary>\n/// Interface for <see cref=\"KTreeView\"/> items. Provides text and other properties.\n/// </summary>\npublic interface ITreeViewItem {\n\t/// <summary>\n\t/// Folder's child items. Return null if not folder.\n\t/// </summary>\n\tIEnumerable<ITreeViewItem> Items => null;\n\n\t/// <summary>\n\t/// Is folder.\n\t/// </summary>\n\tbool IsFolder => false;\n\n\t/// <summary>\n\t/// Is expanded folder.\n\t/// </summary>\n\tbool IsExpanded => false;\n\n\t/// <summary>\n\t/// Called after expanding or collapsing this folder.\n\t/// </summary>\n\tvoid SetIsExpanded(bool yes) { }\n\n\t/// <summary>\n\t/// Text to display.\n\t/// </summary>\n\tstring DisplayText { get; }\n\n\t/// <summary>\n\t/// Called after label editing.\n\t/// </summary>\n\tvoid SetNewText(string text) { }\n\n\t/// <summary>\n\t/// Image as:\n\t/// <br/>• string - image source.\n\t/// <br/>• <b>System.Drawing.Bitmap</b>.\n\t/// <br/>• <b>IEnumerable&lt;object&gt;</b> of these, to draw image and overlay(s).\n\t/// <para>\n\t/// If string, will use <see cref=\"IconImageCache.Get\"/>; see <see cref=\"KTreeView.ImageCache\"/>.\n\t/// <br/>String prefix:\n\t/// <br/>• none - gets file icon; see <see cref=\"icon.of\"/>.\n\t/// <br/>• <c>\"*\"</c> - icon name like <c>\"*Pack.Icon color\"</c>.\n\t/// <br/>• <c>\"imagefile:\"</c> - path of png/bmp/jpg/gif/tif or xaml file.\n\t/// <br/>• <c>\"resource:\"</c> - path of png/bmp/jpg/gif/tif or xaml resource. Don't need prefix if starts with \"resources/\", like \"resources/file.png\".\n\t/// <br/>• <c>\"image:\"</c> - Base64 encoded image file.\n\t/// <br/>• <c>\"link:\"</c> (and nothing more) - link overlay image.\n\t/// </para>\n\t/// <para>\n\t/// If <b>Bitmap</b>, and its DPI does not match control's DPI (<see cref=\"KTreeView.Dpi\"/>), draws it auto-scaled.\n\t/// </para>\n\t/// </summary>\n\tobject Image => null;\n\n\t/// <summary>\n\t/// How to draw checkbox when <see cref=\"KTreeView.HasCheckboxes\"/> true. Default <b>Unchecked</b>.\n\t/// </summary>\n\tTVCheck CheckState => default;\n\n\t/// <summary>\n\t/// Draw text and checkbox like disabled (usually gray).\n\t/// </summary>\n\tbool IsDisabled => false;\n\n\t/// <summary>\n\t/// Draw bold text.\n\t/// </summary>\n\tbool IsBold => false;\n\n\t/// <summary>\n\t/// Can be selected. Default true.\n\t/// </summary>\n\tbool IsSelectable => true;\n\n\t/// <summary>\n\t/// Background color 0xRRGGBB. If -1 (default), uses default colors, depending on state (normal, selected, hot).\n\t/// If alpha is 1 - 3: if contains flag 1, draws selection color when selected; if 2, draws hot color when hot.\n\t/// </summary>\n\tint Color(TVColorInfo ci) => -1;\n\n\t/// <summary>\n\t/// Selected item background color 0xRRGGBB. If -1 (default), uses default color.\n\t/// Not called if the background color isn't near white or if <see cref=\"Color\"/> sets a custom color.\n\t/// </summary>\n\tint SelectedColor(TVColorInfo ci) => -1;\n\n\t/// <summary>\n\t/// Text color 0xRRGGBB. If -1 (default), uses default colors, depending on state (normal, disabled).\n\t/// </summary>\n\tint TextColor(TVColorInfo ci) => -1;\n\n\t/// <summary>\n\t/// Border color 0xRRGGBB. No border if -1 (default).\n\t/// If alpha is 1, draws left edge between text and icon.\n\t/// </summary>\n\tint BorderColor(TVColorInfo ci) => -1;\n\t\n\t/// <summary>\n\t/// Called to measure text width.\n\t/// If not implemented or returns -1, will measure <see cref=\"DisplayText\"/>.\n\t/// </summary>\n\tint MesureTextWidth(GdiTextRenderer tr) => -1;\n\t\n\t/// <summary>\n\t/// Don't measure/draw these parts.\n\t/// Valid flags: <b>Checkbox</b>, <b>MarginLeft</b>, <b>Image</b>, <b>MarginRight</b>, <b>Left</b> (minus 1 indent).\n\t/// </summary>\n\tTVParts NoParts => 0;\n\t\n\t/// <summary>\n\t/// Called when mouse enters this item. If returns &gt; 0, after this hover time (ms) will call <see cref=\"TooltipSetContent\"/>\n\t/// </summary>\n\tint TooltipDelay => 0;\n\t\n\t/// <summary>\n\t/// Called before showing tooltip, to set tooltip content and maybe change tooltip properties.\n\t/// The controls calls this and shows the tooltip only if <see cref=\"TooltipDelay\"/> &gt; 0.\n\t/// </summary>\n\tvoid TooltipSetContent(System.Windows.Controls.ToolTip tt) {  }\n}\n\n/// <summary>\n/// <see cref=\"KTreeView\"/> checkbox state.\n/// </summary>\npublic enum TVCheck : byte { Unchecked, Checked, Mixed, Excluded, RadioUnchecked, RadioChecked, None }\n\n/// <summary>\n/// <see cref=\"KTreeView\"/> item parts.\n/// </summary>\n[Flags]\npublic enum TVParts : byte {\n\t/// <summary>Empty area at the left (indentation).</summary>\n\tLeft = 1,\n\n\t/// <summary>Checkbox rectangle.</summary>\n\tCheckbox = 2,\n\n\t/// <summary>Left margin rectangle.</summary>\n\tMarginLeft = 4,\n\n\t/// <summary>Image rectangle.</summary>\n\tImage = 8,\n\n\t/// <summary>Text rectangle.</summary>\n\tText = 16,\n\n\t/// <summary>Right margin rectangle.</summary>\n\tMarginRight = 32,\n\n\t/// <summary>Empty area at the right.</summary>\n\tRight = 64,\n\n\t//note: values must be increasing from left to right\n}\n\n/// <summary>\n/// <see cref=\"KTreeView.HitTest\"/> results.\n/// </summary>\npublic struct TVHitTest {\n\t/// <summary>Item, or null if not on an item.</summary>\n\tpublic ITreeViewItem item;\n\n\t/// <summary>Item index, or -1 if not on an item.</summary>\n\tpublic int index;\n\n\t/// <summary>Item part.</summary>\n\tpublic TVParts part;\n}\n\n/// <param name=\"Item\"></param>\n/// <param name=\"Index\"></param>\n/// <param name=\"Part\">Valid if clicked with mouse.</param>\n/// <param name=\"Button\">Valid if clicked with mouse.</param>\n/// <param name=\"ClickCount\">1 click, 2 double-click, 0 key or acc.</param>\n/// <param name=\"XY\">Valid if clicked with mouse.</param>\n/// <param name=\"Mod\"></param>\npublic record class TVItemEventArgs(ITreeViewItem Item, int Index, TVParts Part = 0, MouseButton Button = 0, int ClickCount = 0, POINT XY = default, ModifierKeys Mod = 0);\n\n/// <summary>\n/// Custom-draw interface used with <see cref=\"KTreeView.CustomDraw\"/>.\n/// </summary>\n/// <remarks>\n/// When drawing the control, the first and last called functions are <b>Begin</b> and <b>End</b>. For each actually visible item are called functions in this order: <b>DrawBackground</b>, <b>DrawCheckbox</b>, <b>DrawImage</b>, <b>DrawText</b>, <b>DrawMarginLeft</b>, <b>DrawMarginRight</b>. If a bool function returns false, the control draws that part of the item.\n/// </remarks>\npublic interface ITVCustomDraw {\n\tvoid Begin(TVDrawInfo cd, GdiTextRenderer tr);\n\tbool DrawBackground() => false;\n\tbool DrawCheckbox() => false;\n\tbool DrawImage(System.Drawing.Bitmap image) => false;\n\tbool DrawText() => false;\n\tvoid DrawMarginLeft() { }\n\tvoid DrawMarginRight() { }\n\tvoid End() { }\n}\n\n/// <summary>\n/// Custom-draw info passed to <see cref=\"ITVCustomDraw\"/>.\n/// </summary>\npublic class TVDrawInfo {\n\tpublic readonly KTreeView control;\n\n\t/// <summary>GDI device context handle.</summary>\n\tpublic readonly IntPtr dc;\n\n\tpublic readonly System.Drawing.Graphics graphics;\n\tpublic readonly int dpi;\n\n\t/// <summary>Index of current item.</summary>\n\tpublic int index;\n\n\t/// <summary>Current item.</summary>\n\tpublic ITreeViewItem item;\n\n\t/// <summary>Background drawing rectangle of current item.</summary>\n\tpublic RECT rect;\n\n\t/// <summary>Image drawing rectangle of current item.</summary>\n\tpublic RECT imageRect;\n\n\t/// <summary>Text drawing X offset of current item.</summary>\n\tpublic int xText;\n\n\t/// <summary>Text drawing Y offset of current item.</summary>\n\tpublic int yText;\n\n\t/// <summary>Left margin drawing X offset of current item.</summary>\n\tpublic int xLeft;\n\n\t/// <summary>Right margin drawing X offset of current item.</summary>\n\tpublic int xRight;\n\n\t/// <summary>Left margin width of current item.</summary>\n\tpublic int marginLeft;\n\n\t/// <summary>Right margin width of current item.</summary>\n\tpublic int marginRight;\n\n\t/// <summary>Checkbox size.</summary>\n\tpublic SIZE checkSize;\n\t\n\t/// <summary>\n\t/// Item height without added custom height (<see cref=\"KTreeView.CustomItemHeightAddPercent\"/>).\n\t/// </summary>\n\tpublic int lineHeight;\n\t\n\tpublic TVColorInfo colorInfo;\n\n\tpublic TVDrawInfo(KTreeView tv, IntPtr dc, System.Drawing.Graphics graphics, int dpi) {\n\t\tcontrol = tv;\n\t\tthis.dc = dc;\n\t\tthis.graphics = graphics;\n\t\tthis.dpi = dpi;\n\t}\n}\n\npublic struct TVColorInfo {\n\tpublic bool isSelected, isHot, isFocusedItem, isFocusedControl, isHighContrast, isHighContrastDark, isTextBlack;\n}\n\n/// <summary>See <see cref=\"KTreeView.GetDropInfo\"/>.</summary>\npublic struct TVDropInfo {\n\t/// <summary>Point relative to the top-left of the control without border. Physical pixels.</summary>\n\tpublic POINT xy;\n\n\t/// <summary>Item at <b>targetIndex</b>, or null if <b>xy</b> is not on an item.</summary>\n\tpublic ITreeViewItem targetItem;\n\n\t/// <summary>Index of item from <b>xy</b>, or -1 if <b>xy</b> is not on an item.</summary>\n\tpublic int targetIndex;\n\n\t/// <summary>If true, should insert after the drop target item. Else before. Not used if <b>intoFolder</b> is true.</summary>\n\tpublic bool insertAfter;\n\n\t/// <summary><b>xy</b> is in a folder center, therefore should move to the folder.</summary>\n\tpublic bool intoFolder;\n}\n\n/// <summary>\n/// Used with <see cref=\"KTreeView.SetFocusedItem\"/> and functions that call it.\n/// </summary>\n[Flags]\npublic enum TVFocus {\n\t/// <summary>\n\t/// Call <see cref=\"EnsureVisible\"/>. Default.\n\t/// </summary>\n\tEnsureVisible = 1,\n\t\n\t/// <summary>\n\t/// Scroll if need so that the item would be at the top of really visible range.\n\t/// </summary>\n\tScrollTop = 2,\n}\n"
  },
  {
    "path": "Au.Controls/Simple/KCheckBox.cs",
    "content": "using System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Media;\nusing System.Windows.Controls.Primitives;\n\nnamespace Au.Controls;\n\n/// <summary>\n/// Replaces bool? IsChecked with bool IsChecked.\n/// Adds events/overrides for \"checked state changed\".\n/// </summary>\npublic class KCheckBox : CheckBox {\n\tpublic new bool IsChecked {\n\t\tget => base.IsChecked == true;\n\t\tset => base.IsChecked = value;\n\t}\n\n\tprotected override void OnChecked(RoutedEventArgs e) {\n\t\tbase.OnChecked(e);\n\t\tOnCheckChanged(e);\n\t}\n\n\tprotected override void OnUnchecked(RoutedEventArgs e) {\n\t\tbase.OnUnchecked(e);\n\t\tOnCheckChanged(e);\n\t}\n\n\tprotected override void OnIndeterminate(RoutedEventArgs e) {\n\t\tbase.OnIndeterminate(e);\n\t\tOnCheckChanged(e);\n\t}\n\n\t/// <summary>\n\t/// Raises <see cref=\"CheckChanged\"/> event.\n\t/// </summary>\n\tprotected virtual void OnCheckChanged(RoutedEventArgs e) {\n\t\tCheckChanged?.Invoke(this, e);\n\t}\n\n\t/// <summary>\n\t/// When check state changed (checked/unchecked/indeterminate).\n\t/// Can be used to avoid 2-3 event handlers (Checked/Unchecked/Indeterminate).\n\t/// </summary>\n\tpublic event RoutedEventHandler CheckChanged;\n}\n\n/// <summary>\n/// <see cref=\"KCheckBox\"/> without the box part.\n/// </summary>\nclass KCheckNoBox : KCheckBox {\n\tstatic KCheckNoBox() {\n\t\tbool contrast = SystemParameters.HighContrast;\n\t\t\n\t\tvar borderFactory = new FrameworkElementFactory(typeof(Border));\n\t\tborderFactory.Name = \"border\";\n\t\tborderFactory.SetValue(Border.CornerRadiusProperty, new CornerRadius(8));\n\t\tborderFactory.SetValue(Border.BorderThicknessProperty, new Thickness(contrast ? 2 : 1));\n\t\tborderFactory.SetValue(Border.BorderBrushProperty, SystemColors.ControlLightBrush);\n\t\tborderFactory.SetValue(Border.BackgroundProperty, SystemColors.ControlLightLightBrush);\n\t\tborderFactory.SetValue(Border.PaddingProperty, new Thickness(4, 0, 4, 1));\n\t\t\n\t\tvar contentPresenter = new FrameworkElementFactory(typeof(ContentPresenter));\n\t\tcontentPresenter.SetValue(System.Windows.Documents.TextElement.ForegroundProperty, new TemplateBindingExtension(ForegroundProperty));\n\t\tcontentPresenter.SetValue(ContentPresenter.RecognizesAccessKeyProperty, true);\n\t\t\n\t\tborderFactory.AppendChild(contentPresenter);\n\t\t\n\t\tvar template = new ControlTemplate(typeof(KCheckNoBox));\n\t\ttemplate.VisualTree = borderFactory;\n\t\t\n\t\tif (contrast) {\n\t\t\tvar t1 = new Trigger { Property = IsCheckedProperty, Value = true };\n\t\t\tt1.Setters.Add(new Setter(Border.BackgroundProperty, SystemColors.HighlightBrush, \"border\"));\n\t\t\tt1.Setters.Add(new Setter(ForegroundProperty, SystemColors.HighlightTextBrush));\n\t\t\ttemplate.Triggers.Add(t1);\n\t\t} else {\n\t\t\tvar brushes = GetBrushes_();\n\t\t\t\n\t\t\tvar t1 = new Trigger { Property = IsCheckedProperty, Value = true };\n\t\t\tt1.Setters.Add(new Setter(Border.BackgroundProperty, brushes.checkedCold, \"border\"));\n\t\t\tt1.Setters.Add(new Setter(Border.BorderBrushProperty, SystemColors.ControlLightLightBrush, \"border\"));\n\t\t\ttemplate.Triggers.Add(t1);\n\t\t\t\n\t\t\tvar t2 = new Trigger { Property = IsMouseOverProperty, Value = true };\n\t\t\tt2.Setters.Add(new Setter(Border.BackgroundProperty, brushes.uncheckedHot, \"border\"));\n\t\t\ttemplate.Triggers.Add(t2);\n\t\t\t\n\t\t\tvar mt = new MultiTrigger(); //checked hot\n\t\t\tmt.Conditions.Add(new Condition { Property = IsCheckedProperty, Value = true });\n\t\t\tmt.Conditions.Add(new Condition { Property = IsMouseOverProperty, Value = true });\n\t\t\tmt.Setters.Add(new Setter(Border.BackgroundProperty, brushes.checkedHot, \"border\"));\n\t\t\ttemplate.Triggers.Add(mt);\n\t\t}\n\t\t\n\t\tvar style = new Style(typeof(KCheckNoBox));\n\t\tstyle.Setters.Add(new Setter(TemplateProperty, template));\n\t\t\n\t\tStyleProperty.OverrideMetadata(typeof(KCheckNoBox), new FrameworkPropertyMetadata(style));\n\t}\n\t\n\tinternal static (Brush checkedCold, Brush checkedHot, Brush uncheckedHot) GetBrushes_() {\n\t\tif (s_checkedCold == null) {\n\t\t\tvar c = (Color)new ColorInt(0x92C1E6);\n\t\t\tSolidColorBrush brush1 = new(c), brush2 = new(Color.FromArgb(180, c.R, c.G, c.B)), brush3 = new(Color.FromArgb(80, c.R, c.G, c.B));\n\t\t\tbrush1.Freeze();\n\t\t\tbrush2.Freeze();\n\t\t\tbrush3.Freeze();\n\t\t\ts_checkedHot = brush2;\n\t\t\ts_uncheckedHot = brush3;\n\t\t\ts_checkedCold = brush1;\n\t\t}\n\t\treturn (s_checkedCold, s_checkedHot, s_uncheckedHot);\n\t}\n\tstatic Brush s_checkedCold, s_checkedHot, s_uncheckedHot;\n}\n\n/// <summary>\n/// Child <c>Button</c> for <see cref=\"KCheckNoBox\"/>.\n/// </summary>\nclass KButtonRoundNoBorder : Button {\n\tstatic KButtonRoundNoBorder() {\n\t\tbool contrast = SystemParameters.HighContrast;\n\t\t\n\t\tvar borderFactory = new FrameworkElementFactory(typeof(Border));\n\t\tborderFactory.Name = \"border\";\n\t\tborderFactory.SetValue(Border.CornerRadiusProperty, new CornerRadius(8));\n\t\tborderFactory.SetValue(Border.BackgroundProperty, SystemColors.ControlLightLightBrush);\n\t\tborderFactory.SetValue(Border.PaddingProperty, new Thickness(3, 0, 3, 0));\n\t\t\n\t\tvar contentPresenter = new FrameworkElementFactory(typeof(ContentPresenter));\n\t\tcontentPresenter.SetValue(System.Windows.Documents.TextElement.ForegroundProperty, new TemplateBindingExtension(ForegroundProperty));\n\t\tcontentPresenter.SetValue(ContentPresenter.RecognizesAccessKeyProperty, true);\n\t\t\n\t\tborderFactory.AppendChild(contentPresenter);\n\t\t\n\t\tvar template = new ControlTemplate(typeof(KButtonRoundNoBorder));\n\t\ttemplate.VisualTree = borderFactory;\n\t\t\n\t\tif (!contrast) {\n\t\t\tvar brushes = KCheckNoBox.GetBrushes_();\n\t\t\t\n\t\t\tvar t2 = new Trigger { Property = IsMouseOverProperty, Value = true };\n\t\t\tt2.Setters.Add(new Setter(Border.BackgroundProperty, brushes.uncheckedHot, \"border\"));\n\t\t\ttemplate.Triggers.Add(t2);\n\t\t}\n\t\t\n\t\tvar style = new Style(typeof(KButtonRoundNoBorder));\n\t\tstyle.Setters.Add(new Setter(TemplateProperty, template));\n\t\t\n\t\tStyleProperty.OverrideMetadata(typeof(KButtonRoundNoBorder), new FrameworkPropertyMetadata(style));\n\t}\n}\n\n/// <summary>\n/// KCheckBox and KTextBox. Optionally + Button and KPopupListBox.\n/// </summary>\npublic class KCheckTextBox {\n\tpublic readonly KCheckBox c;\n\tpublic readonly KTextBox t;\n\treadonly Button _button;\n\tKPopupListBox _popup;\n\tobject _items; //List<string> or Func<List<string>>\n\t\n\t///\n\tpublic KCheckTextBox(KCheckBox c, KTextBox t, Button button = null) {\n\t\tthis.c = c;\n\t\tthis.t = t;\n\t\tc.Tag = this;\n\t\tt.Tag = this;\n\t\tt.Small = true;\n\t\t_button = button;\n\t\tif (_button != null) {\n\t\t\t//_button.ClickMode = ClickMode.Press; //open on button down. But then Popup.StaysOpen=false does not work. Tried async, but same. //TODO3: replace Popup in KPopupListBox with KPopup.\n\t\t\t_button.Click += (_, _) => {\n\t\t\t\tif (_popup?.IsOpen ?? false) {\n\t\t\t\t\t_popup.IsOpen = false;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tList<string> a = null;\n\t\t\t\tswitch (_items) {\n\t\t\t\tcase List<string> u: a = u; break;\n\t\t\t\tcase Func<List<string>> u: a = u(); break;\n\t\t\t\t}\n\t\t\t\tif (a.NE_()) return;\n\t\t\t\tif (_popup == null) {\n\t\t\t\t\t_popup = new KPopupListBox { PlacementTarget = t };\n\t\t\t\t\t_popup.OK += o => {\n\t\t\t\t\t\tc.IsChecked = true;\n\t\t\t\t\t\tvar s = o as string;\n\t\t\t\t\t\tt.Text = s;\n\t\t\t\t\t\tt.Focus();\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\t_popup.Control.ItemsSource = null;\n\t\t\t\t_popup.Control.ItemsSource = a;\n\t\t\t\t_popup.Control.MinWidth = t.ActualWidth + _button.ActualWidth - 1;\n\t\t\t\t_popup.IsOpen = true;\n\t\t\t};\n\t\t}\n\t}\n\t\n\t///\n\tpublic void Deconstruct(out KCheckBox c, out TextBox t) { c = this.c; t = this.t; }\n\t\n\t/// <summary>\n\t/// Gets or sets <b>Visibility</b> of controls. If false, <b>Visibility</b> is <b>Collapsed</b>.\n\t/// </summary>\n\tpublic bool Visible {\n\t\tget => t.Visibility == Visibility.Visible;\n\t\tset {\n\t\t\tvar vis = value ? Visibility.Visible : Visibility.Collapsed;\n\t\t\tc.Visibility = vis;\n\t\t\tt.Visibility = vis;\n\t\t\tif (_button != null) _button.Visibility = vis;\n\t\t}\n\t}\n\t\n\tpublic void Set(bool check, string text) {\n\t\tc.IsChecked = check;\n\t\tt.Text = text;\n\t}\n\t\n\tpublic void Set(bool check, string text, List<string> items) {\n\t\tc.IsChecked = check;\n\t\tt.Text = text;\n\t\t_items = items;\n\t}\n\t\n\tpublic void Set(bool check, string text, Func<List<string>> items) {\n\t\tc.IsChecked = check;\n\t\tt.Text = text;\n\t\t_items = items;\n\t}\n\t\n\tpublic void Clear(string text = null) {\n\t\tc.IsChecked = false;\n\t\tt.Text = text;\n\t\t_items = null;\n\t}\n\t\n\t/// <summary>\n\t/// If checked and visible and text not empty, gets text and returns true. Else sets s=null and returns false.\n\t/// </summary>\n\t/// <param name=\"s\"></param>\n\t/// <param name=\"emptyToo\">If text empty, get \"\" and return true.</param>\n\tpublic bool GetText(out string s, bool emptyToo = false) {\n\t\ts = null;\n\t\tif (!c.IsChecked || !Visible) return false;\n\t\tvar v = t.Text;\n\t\tif (!emptyToo && v.Length == 0) return false;\n\t\ts = v;\n\t\treturn true;\n\t}\n\t\n\tpublic bool CheckIfTextNotEmpty() {\n\t\tif (!c.IsChecked && t.Text.Length > 0) { c.IsChecked = true; return true; }\n\t\treturn false;\n\t}\n}\n\n/// <summary>\n/// KCheckBox and ComboBox.\n/// </summary>\npublic class KCheckComboBox {\n\tpublic readonly KCheckBox c;\n\tpublic readonly ComboBox t;\n\t\n\t///\n\tpublic KCheckComboBox(KCheckBox c, ComboBox t) {\n\t\tthis.c = c;\n\t\tthis.t = t;\n\t\tc.Tag = this;\n\t\tt.Tag = this;\n\t}\n\t\n\t///\n\tpublic void Deconstruct(out KCheckBox c, out ComboBox t) { c = this.c; t = this.t; }\n\t\n\t/// <summary>\n\t/// Gets or sets <b>Visibility</b> of controls. If false, <b>Visibility</b> is <b>Collapsed</b>.\n\t/// </summary>\n\tpublic bool Visible {\n\t\tget => t.Visibility == Visibility.Visible;\n\t\tset {\n\t\t\tvar vis = value ? Visibility.Visible : Visibility.Collapsed;\n\t\t\tc.Visibility = vis;\n\t\t\tt.Visibility = vis;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// If checked and visible, gets selected item index and returns true. Else sets index=-1 and returns false.\n\t/// </summary>\n\tpublic bool GetIndex(out int index) {\n\t\tindex = -1;\n\t\tif (!c.IsChecked || !Visible) return false;\n\t\tindex = t.SelectedIndex;\n\t\treturn index >= 0;\n\t}\n\t\n\t/// <summary>\n\t/// If checked and visible, gets selected item text and returns true. Else sets text=null and returns false.\n\t/// </summary>\n\tpublic bool GetText(out string text) {\n\t\tif (!GetIndex(out int i)) { text = null; return false; }\n\t\ttext = t.Items[i] as string;\n\t\treturn true;\n\t}\n}\n"
  },
  {
    "path": "Au.Controls/Simple/KCheckDropdownBox.cs",
    "content": "using System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Controls.Primitives;\nusing System.Windows.Input;\nusing System.Numerics;\n\nnamespace Au.Controls;\n\n/// <summary>\n/// Read-only <b>TextBox</b> that shows a dropdown list of checkboxes and displays the list of checked items as its text.\n/// </summary>\npublic class KCheckDropdownBox : TextBox {\n\t///\n\tpublic KCheckDropdownBox() {\n\t\tIsReadOnly = true;\n\t\t//TextWrapping = TextWrapping.Wrap;\n\t\tCursor = Cursors.Arrow;\n\t\tAddHandler(UIElement.MouseLeftButtonUpEvent, new MouseButtonEventHandler(_MouseLeftButtonUp), handledEventsToo: true);\n\t}\n\t\n\t/// <summary>\n\t/// Checkbox names. Max 64.\n\t/// </summary>\n\tpublic object[] Checkboxes {\n\t\tget => _checkboxes;\n\t\tset {\n\t\t\tif (value?.Length > 64) throw new ArgumentException(\"Max 64.\");\n\t\t\t_checkboxes = value;\n\t\t\tChecked = 0;\n\t\t}\n\t}\n\tobject[] _checkboxes;\n\t\n\t/// <summary>\n\t/// Sets or gets bits that correspond to checked checkboxes.\n\t/// Set this property after setting <see cref=\"Checkboxes\"/>.\n\t/// </summary>\n\t/// <exception cref=\"ArgumentException\">The setter called before setting <see cref=\"Checkboxes\"/>.</exception>\n\tpublic ulong Checked {\n\t\tget => _checked;\n\t\tset {\n\t\t\tint n = _checkboxes?.Length ?? 0;\n\t\t\tif (n == 0 && value != 0) throw new ArgumentException(\"Set Checkboxes before Checked.\");\n\t\t\t\n\t\t\t_checked = value;\n\t\t\t\n\t\t\tstring s = \"\";\n\t\t\tif (_checked != 0) {\n\t\t\t\tStringBuilder b = new();\n\t\t\t\tstring sep = null;\n\t\t\t\tfor (int i = 0; i < n; i++) {\n\t\t\t\t\tif (0 != (_checked & 1UL << i)) {\n\t\t\t\t\t\tb.Append(sep).Append(_checkboxes[i]);\n\t\t\t\t\t\tsep = \", \";\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\ts = b.ToString();\n\t\t\t\tif (AllText is { } s1) if (BitOperations.TrailingZeroCount(~_checked) >= n) s = string.Format(s1, s);\n\t\t\t}\n\t\t\tText = s;\n\t\t}\n\t}\n\tulong _checked;\n\t\n\t/// <summary>\n\t/// Text of \"check all\" checkbox, or null (default) to not add the checkbox.\n\t/// </summary>\n\tpublic object AllItem { get; set; }\n\t\n\t/// <summary>\n\t/// Control text when all checked, or null (default) to display the list.\n\t/// Can contain placeholder {0} for the list.\n\t/// </summary>\n\tpublic string AllText { get; set; }\n\t\n\t/// <summary>\n\t/// Used with <see cref=\"DropdownSettings\"/>.\n\t/// </summary>\n\t/// <param name=\"panel\">Panel that will contain checkboxes. Can be <b>WrapPanel</b>, <b>UniformGrid</b> or other simple panel type. Unsupported types: <b>Grid</b>, <b>Canvas</b>. Default: vertical <b>StackPanel</b>.</param>\n\t/// <param name=\"fixedWidth\">Set the dropdown width = the control width. If false, can be wider.</param>\n\t/// <param name=\"noHScroll\">Disable horizontal scrolling.</param>\n\t/// <param name=\"noVScroll\">Disable vertical scrolling.</param>\n\tpublic record DDSettings(Panel panel = null, bool fixedWidth = false, bool noHScroll = false, bool noVScroll = false);\n\t\n\t/// <summary>\n\t/// Callback function that can provide the dropdown panel and settings. Called whenever starting to create a dropdown.\n\t/// </summary>\n\tpublic Func<DDSettings> DropdownSettings { get; set; }\n\t\n\t/// <summary>\n\t/// When dropdown initialized, before showing.\n\t/// </summary>\n\tpublic event Action<DropdownEventData> DropdownOpening;\n\t\n\t/// <summary>\n\t/// When dropdown closed.\n\t/// </summary>\n\tpublic event Action<DropdownEventData> DropdownClosed;\n\t\n\t/// <summary>\n\t/// Used with events.\n\t/// </summary>\n\t/// <param name=\"cb\">This control.</param>\n\t/// <param name=\"popup\">The dropdown window.</param>\n\t/// <param name=\"checkboxes\">Array of checkboxes.</param>\n\t/// <param name=\"cancel\">True when dropdown closed with <c>Esc</c> key and therefore checkbox states are ignored.</param>\n\tpublic record DropdownEventData(KCheckDropdownBox cb, Popup popup, CheckBox[] checkboxes, bool cancel);\n\t\n\t///\n\tprotected override void OnPreviewMouseLeftButtonUp(MouseButtonEventArgs e) {\n\t\t_clickedThis = IsMouseCaptured;\n\t\tbase.OnPreviewMouseLeftButtonUp(e);\n\t}\n\t\n\tbool _clickedThis;\n\t\n\tvoid _MouseLeftButtonUp(object sender, MouseButtonEventArgs e) {\n\t\tif (!_clickedThis) return;\n\t\t_clickedThis = false;\n\t\tif (SelectionLength == 0) {\n\t\t\t_ShowDropdown();\n\t\t}\n\t}\n\t\n\tvoid _ShowDropdown() {\n\t\tint n = _checkboxes?.Length ?? 0;\n\t\tif (n == 0) return;\n\t\t\n\t\tvar ps = DropdownSettings?.Invoke() ?? new();\n\t\t\n\t\tPanel panel = ps.panel ?? new StackPanel();\n\t\tpanel.Margin = new(2);\n\t\t\n\t\tvar ac = new CheckBox[n];\n\t\tfor (int i = 0; i < n; i++) {\n\t\t\tvar c = new CheckBox { Content = _checkboxes[i], Margin = new(1) };\n\t\t\tif (0 != (_checked & 1UL << i)) c.IsChecked = true;\n\t\t\tpanel.Children.Add(c);\n\t\t\tac[i] = c;\n\t\t}\n\t\tvar cFirst = ac[0];\n\t\t\n\t\tif (AllItem is { } cai) {\n\t\t\tcFirst = new CheckBox { Content = cai, Margin = new(1), IsChecked = BitOperations.TrailingZeroCount(~_checked) >= n };\n\t\t\tpanel.Children.Insert(0, cFirst);\n\t\t\tcFirst.Checked += (_, _) => _SetAll(true);\n\t\t\tcFirst.Unchecked += (_, _) => _SetAll(false);\n\t\t\tvoid _SetAll(bool check) {\n\t\t\t\tforeach (var c in ac) c.IsChecked = check;\n\t\t\t}\n\t\t}\n\t\t\n\t\tvar sv = new ScrollViewer {\n\t\t\tContent = panel,\n\t\t\tHorizontalScrollBarVisibility = ps.noHScroll ? ScrollBarVisibility.Disabled : ScrollBarVisibility.Auto,\n\t\t\tVerticalScrollBarVisibility = ps.noVScroll ? ScrollBarVisibility.Disabled : ScrollBarVisibility.Auto,\n\t\t};\n\t\t\n\t\tvar border = new Border {\n\t\t\tChild = sv,\n\t\t\tBackground = SystemColors.WindowBrush,\n\t\t\tBorderThickness = new(1),\n\t\t\tBorderBrush = SystemColors.ActiveBorderBrush,\n\t\t};\n\t\t\n\t\t_popup = new Popup { Child = border, PlacementTarget = this, StaysOpen = false };\n\t\tif (ps.fixedWidth) _popup.Width = ActualWidth; else _popup.MinWidth = ActualWidth;\n\t\t\n\t\tbool esc = false;\n\t\t_popup.Closed += (_, _) => {\n\t\t\tvar popup = _popup;\n\t\t\t_popup = null;\n\t\t\t\n\t\t\tif (!esc) {\n\t\t\t\tulong r = 0, k = 1;\n\t\t\t\tforeach (var c in ac) {\n\t\t\t\t\tif (c.IsChecked == true) r |= k;\n\t\t\t\t\tk <<= 1;\n\t\t\t\t}\n\t\t\t\tChecked = r;\n\t\t\t}\n\t\t\t\n\t\t\tif (popup.IsKeyboardFocusWithin) Focus();\n\t\t\t\n\t\t\tDropdownClosed?.Invoke(new(this, popup, ac, esc));\n\t\t};\n\t\t\n\t\t_popup.PreviewKeyDown += (_, e) => {\n\t\t\tif (e.Key == Key.Space) { //workaround for: if StaysOpen false, on Space key disappears if mouse is not in it or its PlacementTarget\n\t\t\t\te.Handled = true;\n\t\t\t\tif (Keyboard.FocusedElement is CheckBox c) c.IsChecked = !c.IsChecked;\n\t\t\t} else if (e.Key is Key.Escape or Key.Enter) {\n\t\t\t\te.Handled = true;\n\t\t\t\tesc = e.Key == Key.Escape;\n\t\t\t\t_popup.IsOpen = false;\n\t\t\t}\n\t\t};\n\t\t\n\t\tborder.MouseLeave += (_, _) => { IsDropdownOpen = false; };\n\t\t\n\t\tDropdownOpening?.Invoke(new(this, _popup, ac, false));\n\t\t\n\t\t_popup.IsOpen = true;\n\t\tif (cFirst.IsVisible) cFirst.Focus();\n\t}\n\tPopup _popup;\n\t\n\t/// <summary>\n\t/// Gets whether the dropdown is open, or opens/closes it.\n\t/// </summary>\n\tpublic bool IsDropdownOpen {\n\t\tget => _popup != null;\n\t\tset {\n\t\t\tif (value) {\n\t\t\t\tif (_popup == null) _ShowDropdown();\n\t\t\t} else {\n\t\t\t\tif (_popup != null) _popup.IsOpen = false;\n\t\t\t}\n\t\t}\n\t}\n\t\n\t///\n\tprotected override void OnPreviewKeyDown(KeyEventArgs e) {\n\t\tif (e.SystemKey == Key.Down) {\n\t\t\tIsDropdownOpen = true;\n\t\t}\n\t\tbase.OnPreviewKeyDown(e);\n\t}\n\t\n\t///\n\tprotected override void OnPreviewTextInput(TextCompositionEventArgs e) {\n\t\tif (IsReadOnly) IsDropdownOpen = true;\n\t\tbase.OnPreviewTextInput(e);\n\t}\n}\n"
  },
  {
    "path": "Au.Controls/Simple/KColorPicker.cs",
    "content": "using System.Windows.Interop;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Media;\nusing System.Windows.Shapes;\n\nnamespace Au.Controls;\n\n/// <summary>\n/// WPF control for selecting color.\n/// </summary>\npublic class KColorPicker : UserControl {\n\tTextBox _tColor;\n\t_Palette _pal;\n\tbool _hlsChanging, _palSelecting;\n\t\n\tpublic KColorPicker() {\n\t\tStackPanel p1 = new();\n\t\tStackPanel p2 = new() { Orientation = Orientation.Horizontal, Margin = new Thickness(0, 0, 0, 3) };\n\t\tp1.Children.Add(p2);\n\t\tStackPanel p3 = new() { Orientation = Orientation.Horizontal, Margin = new Thickness(0, 0, 4, 0), Background = Brushes.White };\n\t\tp2.Children.Add(p3);\n\t\t\n\t\tTextBox tH = null, tL = null, tS = null;\n\t\t\n\t\tvar fColor = new TextBlock { Text = \"Color\", ToolTip = \"Color RRGGBB\", Foreground = Brushes.Black };\n\t\tp3.Children.Add(fColor);\n\t\t\n\t\tvar bColor = new Rectangle { Width = 9, Height = 9, Fill = Brushes.Black, Margin = new Thickness(4, 2, 4, 0) };\n\t\tp3.Children.Add(bColor);\n\t\t\n\t\t_tColor = new() { Width = 64, Text = \"000000\", ToolTip = fColor.ToolTip };\n\t\t_tColor.TextChanged += (o, e) => {\n\t\t\tint col = _GetColor(bgr: true);\n\t\t\tif (!_hlsChanging) {\n\t\t\t\tvar (H, L, S) = ColorInt.ToHLS(col, bgr: true);\n\t\t\t\t_hlsChanging = true;\n\t\t\t\tif (S != 0) tH.Text = H.ToString();\n\t\t\t\ttL.Text = L.ToString();\n\t\t\t\ttS.Text = S.ToString();\n\t\t\t\t_hlsChanging = false;\n\t\t\t}\n\t\t\t\n\t\t\tif (!_palSelecting) _pal.SelectColor(col);\n\t\t\t\n\t\t\tvar brush = new SolidColorBrush(System.Windows.Media.Color.FromRgb((byte)col, (byte)(col >> 8), (byte)(col >> 16)));\n\t\t\tfColor.Foreground = brush;\n\t\t\tbColor.Fill = brush;\n\t\t\tp3.Background = ColorInt.GetPerceivedBrightness(col, bgr: true) <= .8 ? Brushes.White : Brushes.DimGray;\n\t\t\t\n\t\t\tif (ColorChanged != null) {\n\t\t\t\tif (!BGR) col = ColorInt.SwapRB(col);\n\t\t\t\tColorChanged(col);\n\t\t\t}\n\t\t};\n\t\tp2.Children.Add(_tColor);\n\t\t\n\t\t//hls: 0 H, 1 L, 2 S.\n\t\tTextBox _AddHLS(int hls, string label, string tooltip) {\n\t\t\tp2.Children.Add(new TextBlock { Text = label, Margin = new Thickness(hls == 0 ? 18 : 12, 0, 4, 0), ToolTip = tooltip });\n\t\t\tTextBox t = new() { Width = 34, Text = \"0\", ToolTip = tooltip };\n\t\t\tt.TextChanged += (o, e) => {\n\t\t\t\tif (_hlsChanging) return;\n\t\t\t\tvar tb = o as TextBox;\n\t\t\t\tint i = tb.Text.ToInt();\n\t\t\t\tif (i < 0 || i > 240) {\n\t\t\t\t\t_hlsChanging = true;\n\t\t\t\t\ttb.Text = Math.Clamp(i, 0, 240).ToS();\n\t\t\t\t\t_hlsChanging = false;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tint H = tH.Text.ToInt(), L = tL.Text.ToInt(), S = tS.Text.ToInt();\n\t\t\t\tint col = ColorInt.FromHLS(H, L, S, bgr: true);\n\t\t\t\t_hlsChanging = true;\n\t\t\t\t_SetColor(col, bgr: true);\n\t\t\t\t_hlsChanging = false;\n\t\t\t};\n\t\t\tt.MouseWheel += (o, e) => {\n\t\t\t\tint d = e.Delta / 15; //+-8\n\t\t\t\tif (o == tH) d /= 2; //hue is more sensitive; 8 would jump directly from box to box\n\t\t\t\tint i = t.Text.ToInt() + d;\n\t\t\t\tif (hls == 0) { if (i < 0) i += 240; else if (i > 240) i -= 240; }\n\t\t\t\tint v = Math.Clamp(i, 0, 240);\n\t\t\t\tt.Text = v.ToS();\n\t\t\t};\n\t\t\tp2.Children.Add(t);\n\t\t\treturn t;\n\t\t}\n\t\t\n\t\ttH = _AddHLS(0, \"H\", \"Hue 0-240. Change with mouse wheel.\");\n\t\ttL = _AddHLS(1, \"L\", \"Luminance 0-240. Change with mouse wheel.\");\n\t\ttS = _AddHLS(2, \"S\", \"Saturation 0-240. Change with mouse wheel.\");\n\t\t\n\t\t_pal = new _Palette(this);\n\t\tp1.Children.Add(_pal);\n\t\t\n\t\tbase.Content = p1;\n\t}\n\t\n\tclass _Palette : HwndHost {\n\t\tKColorPicker _cp;\n\t\twnd _w;\n\t\tint _dpi;\n\t\tint _cellSize;\n\t\tconst int c_nHue = 30; //number of hue columns. Must be 240-divisible.\n\t\tconst int c_nLum = 7; //number of luminance rows. Must be 240-divisible minus 1.\n\t\t\n\t\tpublic wnd Hwnd => _w;\n\t\t\n\t\tpublic _Palette(KColorPicker cp) {\n\t\t\t_cp = cp;\n\t\t}\n\t\t\n\t\tprotected override HandleRef BuildWindowCore(HandleRef hwndParent) {\n\t\t\tvar wParent = (wnd)hwndParent.Handle;\n\t\t\t_w = WndUtil.CreateWindow(_wndProc = _WndProc, false, \"Static\", null, WS.CHILD | WS.CLIPCHILDREN, 0, 0, 0, 10, 10, wParent);\n\t\t\t\n\t\t\treturn new HandleRef(this, _w.Handle);\n\t\t}\n\t\t\n\t\tprotected override void DestroyWindowCore(HandleRef hwnd) {\n\t\t\tApi.DestroyWindow(_w);\n\t\t}\n\t\t\n\t\tprotected override Size MeasureOverride(Size constraint) => _Measure();\n\t\t\n\t\tWNDPROC _wndProc;\n\t\tnint _WndProc(wnd w, int msg, nint wParam, nint lParam) {\n\t\t\t//var pmo = new PrintMsgOptions(Api.WM_NCHITTEST, Api.WM_SETCURSOR, Api.WM_MOUSEMOVE, Api.WM_NCMOUSEMOVE, 0x10c1);\n\t\t\t//if (WndUtil.PrintMsg(out string s, _w, msg, wParam, lParam, pmo)) print.it(\"<><c green>\" + s + \"<>\");\n\t\t\t\n\t\t\tswitch (msg) {\n\t\t\tcase Api.WM_NCDESTROY:\n\t\t\t\t_w = default;\n\t\t\t\tbreak;\n\t\t\t//case Api.WM_NCHITTEST: //never mind: if in Popup, probably click closes. Currently not using in popups.\n\t\t\t//\treturn Api.HTTRANSPARENT;\n\t\t\tcase Api.WM_LBUTTONDOWN:\n\t\t\t\t_WmLbuttondown(lParam);\n\t\t\t\tbreak;\n\t\t\tcase Api.WM_PAINT:\n\t\t\t\tusing (var bp = new BufferedPaint(w, true)) _Paint(bp.DC);\n\t\t\t\treturn default;\n\t\t\t}\n\t\t\t\n\t\t\treturn Api.DefWindowProc(w, msg, wParam, lParam);\n\t\t}\n\t\t\n\t\tSize _Measure() {\n\t\t\t_dpi = Dpi.OfWindow(_w);\n\t\t\tint i = _cellSize = Dpi.Scale(10, _dpi);\n\t\t\tvar z = new SIZE(i, i);\n\t\t\tz.width *= c_nHue; z.width++;\n\t\t\tz.height *= c_nLum + 1; z.height++; z.height += _cellSize / 4;\n\t\t\treturn Dpi.Unscale(z, _dpi);\n\t\t}\n\t\t\n\t\tvoid _Paint(nint dc) {\n\t\t\tusing var g = System.Drawing.Graphics.FromHdc(dc);\n\t\t\tusing var penSel = new System.Drawing.Pen(System.Drawing.Color.Black) { DashStyle = System.Drawing.Drawing2D.DashStyle.Dot };\n\t\t\tg.Clear(System.Drawing.Color.White);\n\t\t\t\n\t\t\tvar z = _cellSize;\n\t\t\t\n\t\t\t//colors. Hue in x axis, luminance in y axis.\n\t\t\tint y = 0;\n\t\t\tfor (int i = 0, lum = 240; (lum -= 240 / (c_nLum + 1)) > 0; i++) {\n\t\t\t\tfor (int j = 0, hue = 0; hue < 240; j++, hue += 240 / c_nHue) {\n\t\t\t\t\t_Draw(s_ac[j, i], j * z);\n\t\t\t\t}\n\t\t\t\ty += z;\n\t\t\t}\n\t\t\t\n\t\t\t//black...white\n\t\t\ty += _cellSize / 4;\n\t\t\tfor (int j = 0, gray = 0; j < c_nHue; j++, gray += gray < 120 ? 10 : 8) {\n\t\t\t\t_Draw(s_ac[j, c_nLum], j * z);\n\t\t\t}\n\t\t\t\n\t\t\t//selection\n\t\t\ty = _select.lum * z; if (_select.lum == c_nLum) y += _cellSize / 4;\n\t\t\tg.DrawRectangle(penSel, _select.hue * z, y, z, z);\n\t\t\t\n\t\t\tvoid _Draw(int col, int x) {\n\t\t\t\tRECT r = (x + 1, y + 1, z - 1, z - 1);\n\t\t\t\tvar brush = Api.CreateSolidBrush(col);\n\t\t\t\tApi.FillRect(dc, r, brush);\n\t\t\t\tApi.DeleteObject(brush);\n\t\t\t}\n\t\t}\n\t\t\n\t\tstatic int[,] _GenerateColors() {\n\t\t\tint[,] a = new int[c_nHue, c_nLum + 1];\n\t\t\tfor (int i = 0, lum = 240; (lum -= 240 / (c_nLum + 1)) > 0; i++) {\n\t\t\t\tfor (int j = 0, hue = 0; hue < 240; j++, hue += 240 / c_nHue) {\n\t\t\t\t\t//var col=_ColorHLSToRGB(hue, lum, 240);\n\t\t\t\t\t\n\t\t\t\t\tint lum2 = lum;\n\t\t\t\t\tif (lum >= 120) {\n\t\t\t\t\t\tint d = Math.Abs(80 - hue); //diff from green\n\t\t\t\t\t\tif (d <= 64) {\n\t\t\t\t\t\t\td /= 4;\n\t\t\t\t\t\t\t//if(lum==120) print.it(Math.Max(d, 10), (240-lum)/Math.Max(d, 10));\n\t\t\t\t\t\t\tlum2 -= (240 - lum) / Math.Max(d, 8);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tvar col = ColorInt.FromHLS(hue, lum2, 240, bgr: true);\n\t\t\t\t\t\n\t\t\t\t\ta[j, i] = col;\n\t\t\t\t\t\n\t\t\t\t\t//if(lum==120 /*&& hue<=180*/) {\n\t\t\t\t\t//\tColorInt k=ColorInt.FromBGR(col, true);\n\t\t\t\t\t//\tvar b=k.GetPerceivedBrightness();\n\t\t\t\t\t//\tprint.it(col.ToString(\"X6\"), hue, lum, b);\n\t\t\t\t\t//}\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t//black...white\n\t\t\tfor (int j = 0, gray = 0; j < c_nHue; j++, gray += gray < 120 ? 10 : 8) {\n\t\t\t\t//print.it(gray);\n\t\t\t\tif (gray > 255) gray = 255;\n\t\t\t\tvar col = (gray << 16) | (gray << 8) | gray;\n\t\t\t\ta[j, c_nLum] = col;\n\t\t\t}\n\t\t\t\n\t\t\treturn a;\n\t\t}\n\t\t\n\t\tstatic readonly int[,] s_ac = _GenerateColors();\n\t\t\n\t\t(int hue, int lum) _select = (0, c_nLum);\n\t\t\n\t\t//public int SelectedColor => _ac[_select.hue, _select.lum];\n\t\t\n\t\t//public bool SelectedGray => _select.lum==c_nLum;\n\t\t\n\t\tpublic void SelectColor(int col) {\n\t\t\t//print.it((uint)col);\n\t\t\tvar (H, L, S) = ColorInt.ToHLS(col, bgr: true);\n\t\t\tif (S == 0) { //gray\n\t\t\t\tint lum = L * 255 / 240;\n\t\t\t\tfor (int j = 0; j < c_nHue; j++) {\n\t\t\t\t\tint gray = s_ac[j, c_nLum] & 255;\n\t\t\t\t\tif (gray >= lum) { _select = (j, c_nLum); break; }\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tconst int nLum = c_nLum + 1, hueStep = 240 / c_nHue, lumStep = 240 / nLum;\n\t\t\t\tdouble dif = 1000000;\n\t\t\t\tfor (int hue = 0; hue < 240; hue += hueStep) {\n\t\t\t\t\tfor (int lum = 240; (lum -= lumStep) > 0;) {\n\t\t\t\t\t\tint dH = hue - H, dL = lum - L;\n\t\t\t\t\t\tdouble d = Math.Sqrt(dH * dH + dL * dL);\n\t\t\t\t\t\tif (d < dif) { dif = d; _select = (hue / hueStep, c_nLum - lum / lumStep); }\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tInvalidate();\n\t\t}\n\t\t\n\t\tvoid _WmLbuttondown(nint xy) {\n\t\t\tvar (x, y) = Math2.NintToPOINT(xy);\n\t\t\tx--; x /= _cellSize;\n\t\t\ty--; y /= _cellSize;\n\t\t\tx = Math.Clamp(x, 0, c_nHue - 1);\n\t\t\ty = Math.Clamp(y, 0, c_nLum);\n\t\t\t_select = (x, y);\n\t\t\t_cp._palSelecting = true;\n\t\t\tint col = s_ac[x, y];\n\t\t\t_cp._SetColor(col, bgr: true);\n\t\t\t_cp._palSelecting = false;\n\t\t\tInvalidate();\n\t\t}\n\t\t\n\t\tpublic unsafe void Invalidate() {\n\t\t\tvar w = Hwnd;\n\t\t\tif (w.IsVisible) Api.InvalidateRect(w, null, false);\n\t\t}\n\t}\n\t\n\tvoid _SetColor(int color, bool bgr) {\n\t\tif (bgr) color = ColorInt.SwapRB(color);\n\t\tvar s = color.ToS(\"X6\");\n\t\t_tColor.Text = s;\n\t}\n\t\n\tint _GetColor(bool bgr) {\n\t\tvar s = _tColor.Text;\n\t\tint col = s.ToInt(s.Starts('#') ? 1 : (s.Starts(\"0x\") ? 2 : 0), STIFlags.IsHexWithout0x);\n\t\tif (bgr) col = ColorInt.SwapRB(col);\n\t\treturn col;\n\t}\n\t\n\t/// <summary>\n\t/// With public functions and events use color format 0xBBGGRR. If false (default), uses 0xRRGGBB.\n\t/// </summary>\n\tpublic bool BGR { get; set; }\n\t\n\tpublic int Color {\n\t\tget => _GetColor(BGR);\n\t\tset => _SetColor(value, BGR);\n\t}\n\t\n\tpublic event Action<int> ColorChanged;\n\t\n\tpublic static void ColorTool(Action<string> result, Window owner, bool modal, bool add0xRgbButton, bool addBgrButton) {\n\t\tvar w = new KDialogWindow { Title = \"Color\", ShowInTaskbar = owner == null, Owner = owner };\n\t\tvar b = new wpfBuilder(w);\n\t\tw.ResizeMode = ResizeMode.NoResize;\n\t\tb.R.Add(out KColorPicker palette);\n\t\tb.R.StartGrid().Columns(-1, -1, -1);\n\t\tb.R.AddButton(\"#RRGGBB\", _ => _Result($\"#{palette.Color:X6}\")).Span(1);\n\t\tif (add0xRgbButton) b.AddButton(\"0xRRGGBB\", _ => _Result($\"0x{palette.Color:X6}\")).Span(1);\n\t\tif (addBgrButton) b.AddButton(\"0xBBGGRR\", _ => _Result($\"0x{ColorInt.SwapRB(palette.Color):X6}\"));\n\t\tb.End();\n\t\tb.R.Add(\"Color name\", out _ComboBox cbNamed).Brush(Brushes.White);\n\t\tb.End();\n\t\t\n\t\tList<(string name, ColorInt c, int H)> aNamed = new();\n\t\t//cbNamed.MaxDropDownHeight = 1000;\n\t\tcbNamed.DropDownOpened += _cbNet_DropDownOpened;\n\t\tvoid _cbNet_DropDownOpened(object sender, EventArgs e) {\n\t\t\tcbNamed.DropDownOpened -= _cbNet_DropDownOpened;\n\t\t\t\n\t\t\tforeach (var mi in typeof(Colors).GetProperties()) {\n\t\t\t\tif (mi.GetValue(0) is Color co) {\n\t\t\t\t\tvar name = mi.Name;\n\t\t\t\t\tif (name == \"Transparent\") continue;\n\t\t\t\t\tvar ci = (ColorInt)co;\n\t\t\t\t\tvar (H, L, S) = ColorInt.ToHLS(ci.argb, false);\n\t\t\t\t\tif (S < 50) H = L - 1000;\n\t\t\t\t\telse H = (H + 240 - 43) % 240;\n\t\t\t\t\taNamed.Add((name, ci, H));\n\t\t\t\t}\n\t\t\t}\n\t\t\taNamed.Sort((x, y) => x.H - y.H);\n\t\t\tvar darkBrush = new SolidColorBrush(System.Windows.Media.Color.FromRgb(64, 64, 64));\n\t\t\tfor (int i = 0; i < aNamed.Count; i++) {\n\t\t\t\tvar name = aNamed[i].name;\n\t\t\t\tvar ci = aNamed[i].c;\n\t\t\t\tvar brush = new SolidColorBrush((Color)ci);\n\t\t\t\tvar k = new StackPanel { Orientation = Orientation.Horizontal };\n\t\t\t\tk.Children.Add(new Rectangle { Fill = brush, Stroke = Brushes.White, Width = 16, StrokeThickness = 3 });\n\t\t\t\tk.Children.Add(new TextBlock { Text = \"Text\", Foreground = brush, Background = Brushes.White, Padding = new(0, 0, 3, 0) });\n\t\t\t\tk.Children.Add(new TextBlock { Text = \"Text\", Foreground = brush, Background = darkBrush, Padding = new(3, 0, 3, 0) });\n\t\t\t\tk.Children.Add(new TextBlock { Text = name, Padding = new(3, 0, 3, 0) });\n\t\t\t\tcbNamed.Items.Add(k);\n\t\t\t}\n\t\t\tcbNamed.SelectionChanged += (_, e) => { if (cbNamed.SelectedIndex is int i && i >= 0) _Result(aNamed[i].name); };\n\t\t}\n\t\t\n\t\tif (modal) w.ShowDialog(); else w.Show();\n\t\t\n\t\tvoid _Result(string color) {\n\t\t\tresult(color);\n\t\t\tw.Close();\n\t\t}\n\t}\n\t\n\tclass _ComboBox : ComboBox {\n\t\tpublic _ComboBox() {\n\t\t\t//workaround for the ComboBox slowness\n\t\t\tItemsPanel = new ItemsPanelTemplate();\n\t\t\tvar stackPanelTemplate = new FrameworkElementFactory(typeof(VirtualizingStackPanel));\n\t\t\tItemsPanel.VisualTree = stackPanelTemplate;\n\t\t}\n\t\t\n\t\t//public override void OnApplyTemplate() {\n\t\t//\tbase.OnApplyTemplate();\n\t\t//\tif (Template.FindName(\"PART_Popup\", this) is Popup p) p.PopupAnimation = PopupAnimation.None;\n\t\t//}\n\t}\n}\n"
  },
  {
    "path": "Au.Controls/Simple/KDateTime.cs",
    "content": "using System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Controls.Primitives;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing CalendarControl = System.Windows.Controls.Calendar;\n\nnamespace Au.Controls;\n\n/// <summary>\n/// Text box for editing date and time.\n/// </summary>\npublic class KDateTime : TextBox {\n\tconst string c_format = \"yyyy-MM-dd HH:mm:ss\";\n\t\n\t///\n\tpublic KDateTime() {\n\t\tAddHandler(UIElement.MouseLeftButtonUpEvent, new MouseButtonEventHandler(_MouseLeftButtonUp), handledEventsToo: true);\n\t}\n\t\n\t/// <summary>\n\t/// <b>Text</b> as <b>DateTime</b>.\n\t/// </summary>\n\t/// <value>\n\t/// Setter sets control text, or clears if null.\n\t/// Getter returns control text converted to <b>DateTime</b>, or null if fails to convert.\n\t/// Use <see cref=\"wpfBuilder.Validation\"/> with <see cref=\"Validation\"/> to ensure that <b>Text</b> is valid date-time.\n\t/// </value>\n\tpublic DateTime? Value {\n\t\tget => _ParseDate(Text, out var d) ? d : null;\n\t\tset { Text = value?.ToString(c_format); }\n\t}\n\t\n\t/// <summary>\n\t/// Validation function for <see cref=\"wpfBuilder.Validation\"/>.\n\t/// </summary>\n\tpublic static string Validation(FrameworkElement e) => ((KDateTime)e)._Validation();\n\t\n\tstring _Validation() {\n\t\tif (IsEnabled && Visibility == Visibility.Visible) {\n\t\t\tstring s = Text;\n\t\t\tif (s.Length == 0) return \"Date/time empty\";\n\t\t\tif (!_ParseDate(s, out var d)) return \"Date/time invalid\";\n\t\t\tif (d.Year < ValidationFirstYear) return \"Min year \" + ValidationFirstYear;\n\t\t\tif (ValidationDateMustBeAfter?.Invoke() is DateTime d2 && d <= d2) return \"Must be > \" + d2.ToString(c_format);\n\t\t}\n\t\treturn null;\n\t}\n\t\n\t/// <summary>\n\t/// Validation fails if the control's year is less than this value.\n\t/// Default 1602. Note: if less than 1601-01-02, user code may not work because can't convert to FILETIME.\n\t/// </summary>\n\tpublic int ValidationFirstYear { get; set; } = 1602;\n\t\n\t/// <summary>\n\t/// Validation fails if the control's date isn't after that of returned by the callback function (if returns not null).\n\t/// </summary>\n\tpublic Func<DateTime?> ValidationDateMustBeAfter { get; set; }\n\t\n\t/// <summary>\n\t/// Selects and spins field from mouse.\n\t/// </summary>\n\tprotected override void OnMouseWheel(MouseWheelEventArgs e) {\n\t\tint pos = _MouseCharPos(e, false);\n\t\tif (pos >= 0) {\n\t\t\tint add = e.Delta > 0 ? 1 : -1;\n\t\t\tvar mod = Keyboard.Modifiers;\n\t\t\tif (mod is 0 or ModifierKeys.Control) {\n\t\t\t\tif (mod == ModifierKeys.Control) add *= 10;\n\t\t\t\t_FieldUpDown(pos, add);\n\t\t\t\tFocus();\n\t\t\t\tbase.Cursor = Cursors.None;\n\t\t\t\te.Handled = true;\n\t\t\t}\n\t\t}\n\t\tbase.OnMouseWheel(e);\n\t}\n\n\t///\n\tprotected override void OnMouseMove(MouseEventArgs e) {\n\t\tif (base.Cursor == Cursors.None) base.Cursor = null;\n\t\tbase.OnMouseMove(e);\n\t}\n\t\n\t///\n\tprotected override void OnPreviewMouseLeftButtonUp(MouseButtonEventArgs e) {\n\t\t_clickedThis = IsMouseCaptured;\n\t\tbase.OnPreviewMouseLeftButtonUp(e);\n\t}\n\t\n\tbool _clickedThis;\n\t\n\tvoid _MouseLeftButtonUp(object sender, MouseButtonEventArgs e) {\n\t\tif (!_clickedThis) return;\n\t\t_clickedThis = false;\n\t\tif (SelectionLength == 0 && _PartFromPos(Text, _MouseCharPos(e, true), out int part, out var m)) {\n\t\t\tbase.Select(m[part].Start, m[part].Length);\n\t\t\tif (part <= 3) _ShowCalendar(part, false);\n\t\t}\n\t}\n\t\n\tvoid _ShowCalendar(int part, bool focusCalendar) {\n\t\tif (!_ParseDate(Text, out var dt)) return;\n\t\t\n\t\tvar cis = new Style(typeof(CalendarItem));\n\t\tcis.Setters.Add(new Setter(FrameworkElement.MarginProperty, new Thickness(0)));\n\t\t\n\t\tvar c = new CalendarControl {\n\t\t\tCalendarItemStyle = cis,\n\t\t\tLanguage = System.Windows.Markup.XmlLanguage.GetLanguage(CultureInfo.InstalledUICulture.IetfLanguageTag),\n\t\t\tDisplayMode = part switch { 1 => CalendarMode.Decade, 2 => CalendarMode.Year, _ => CalendarMode.Month },\n\t\t\tDisplayDate = dt,\n\t\t};\n\t\t//var v = new Viewbox { Child = c, Width = 220,  }; //make font bigger. But then blurred.\n\t\t\n\t\t_calendarPopup = new Popup() { Child = c, PlacementTarget = this, StaysOpen = false };\n\t\t\n\t\t_calendarPopup.Closed += (_, _) => {\n\t\t\tif (_calendarPopup.IsKeyboardFocusWithin) Focus();\n\t\t\t_calendarPopup = null;\n\t\t};\n\t\t\n\t\t_calendarPopup.IsOpen = true;\n\t\t\n\t\tif (_calendarPopup != null) {\n\t\t\tif (focusCalendar) c.Focus();\n\t\t\t\n\t\t\tc.PreviewMouseLeftButtonUp += (sender, e) => {\n\t\t\t\tif (c.DisplayMode == CalendarMode.Month && e.OriginalSource is UIElement k && k.FindVisualAncestor<CalendarDayButton>(true, sender, false) is { } cdb) {\n\t\t\t\t\t_CloseCalendar(c);\n\t\t\t\t}\n\t\t\t};\n\t\t\t\n\t\t\tc.PreviewKeyDown += (_, e) => {\n\t\t\t\tif (e.Key is Key.Enter && c.DisplayMode == CalendarMode.Month) e.Handled = _CloseCalendar(c);\n\t\t\t\tif (e.Key is Key.Escape) e.Handled = _CloseCalendar();\n\t\t\t};\n\t\t\t\n\t\t\t_calendarPopup.PreviewMouseWheel += (_, e) => {\n\t\t\t\tif (!c.RectInScreen().Contains(mouse.xy)) _calendarPopup.IsOpen = false;\n\t\t\t};\n\t\t}\n\t}\n\t\n\tPopup _calendarPopup;\n\t\n\tbool _CloseCalendar(CalendarControl c = null) {\n\t\tbool r = _calendarPopup?.IsOpen == true;\n\t\tif (r) {\n\t\t\tif (c != null) {\n\t\t\t\tvar d = c.SelectedDate ?? c.DisplayDate;\n\t\t\t\tvar s = Text;\n\t\t\t\tif (_ParseRX(s, out var m)) {\n\t\t\t\t\tText = d.ToString(\"yyyy-MM-dd\") + s[m[3].End..];\n\t\t\t\t\tSelect(m[4].Start, m[4].Length);\n\t\t\t\t}\n\t\t\t}\n\t\t\t_calendarPopup.IsOpen = false;\n\t\t}\n\t\treturn r;\n\t}\n\t\n\t/// <summary>\n\t/// Selects adjacent field on Left/Right/space. Spins current field on Up/Down. Opens calendar on Alt+Down. Closes calendar.\n\t/// </summary>\n\tprotected override void OnPreviewKeyDown(KeyEventArgs e) {\n\t\te.Handled = _CloseCalendar() && e.Key is Key.Escape or Key.Enter;\n\t\t\n\t\tif (e.Key is Key.Left or Key.Right or Key.Down or Key.Up) {\n\t\t\tvar mod = Keyboard.Modifiers;\n\t\t\tif (mod is 0 or ModifierKeys.Control) {\n\t\t\t\tvar s = Text;\n\t\t\t\tint pos = CaretIndex;\n\t\t\t\tif (_PartFromPos(s, pos, out int part, out var m)) {\n\t\t\t\t\tif (e.Key is Key.Left or Key.Right) {\n\t\t\t\t\t\tpart += e.Key == Key.Right ? 1 : -1;\n\t\t\t\t\t\tif (part == 0) part = 6; else if (part == 7) part = 1;\n\t\t\t\t\t\tbase.Select(m[part].Start, m[part].Length);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tint add = e.Key is Key.Up ? 1 : -1;\n\t\t\t\t\t\tif (mod == ModifierKeys.Control) add *= 10;\n\t\t\t\t\t\t_FieldUpDown(pos, add);\n\t\t\t\t\t}\n\t\t\t\t\te.Handled = true;\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (e.Key is Key.Space) { //no OnPreviewTextInput\n\t\t\te.Handled = _OnPreviewTextInput(' ');\n\t\t} else if (e.SystemKey == Key.Down) {\n\t\t\te.Handled = true;\n\t\t\tif (_PartFromPos(Text, CaretIndex, out int part, out var m)) {\n\t\t\t\tif (part <= 3) {\n\t\t\t\t\tbase.Select(m[part].Start, m[part].Length);\n\t\t\t\t\t_ShowCalendar(part, true);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (!e.Handled) base.OnPreviewKeyDown(e);\n\t}\n\t\n\t/// <summary>\n\t/// Limits the number of digits in current field. Selects adjacent field on -/:. Blocks other characters.\n\t/// </summary>\n\tprotected override void OnPreviewTextInput(TextCompositionEventArgs e) {\n\t\tif (e.Text is [(>= '0' and <= '9') or '-' or ':' or ' '] s) e.Handled = _OnPreviewTextInput(s[0]);\n\t\telse e.Handled = true;\n\t\tbase.OnPreviewTextInput(e);\n\t}\n\t\n\tbool _OnPreviewTextInput(char c) {\n\t\tint pos = CaretIndex;\n\t\tif (_PartFromPos(Text, pos, out int part, out var m)) {\n\t\t\tif (c is >= '0' and <= '9') {\n\t\t\t\tint k = part == 1 ? 4 : 2;\n\t\t\t\tif (m[part].Length == k && SelectionLength == 0) {\n\t\t\t\t\tSelect(m[part].Start, m[part].Length);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (++part < 7) Select(m[part].Start, m[part].Length);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\t\n\tvoid _FieldUpDown(int pos, int add) {\n\t\tvar s = Text;\n\t\tif (!_PartFromPos(s, pos, out int part, out _)) return;\n\t\tif (!_ParseDate(s, out var d)) return;\n\t\ttry {\n\t\t\tswitch (part) {\n\t\t\tcase 1: d = d.AddYears(add); break;\n\t\t\tcase 2: d = d.AddMonths(add); break;\n\t\t\tcase 3: d = d.AddDays(add); break;\n\t\t\tcase 4: d = d.AddHours(add); break;\n\t\t\tcase 5: d = d.AddMinutes(add); break;\n\t\t\tcase 6: d = d.Add(TimeSpan.FromSeconds(add)); break;\n\t\t\t}\n\t\t\tText = s = d.ToString(c_format);\n\t\t\tif (_ParseRX(s, out var m)) {\n\t\t\t\tbase.Select(m[part].Start, m[part].Length);\n\t\t\t}\n\t\t}\n\t\tcatch { }\n\t}\n\t\n\tbool _ParseRX(string s, out RXMatch m) {\n\t\t_rx ??= new(@\"^(\\d{0,4})-(\\d{0,2})-(\\d{0,2}) +(\\d{0,2}):(\\d{0,2}):(\\d{0,2})$\");\n\t\treturn _rx.Match(s, out m);\n\t}\n\tregexp _rx;\n\t\n\tbool _PartFromPos(string s, int pos, out int part, out RXMatch m) {\n\t\tif (!_ParseRX(s, out m)) { part = 0; return false; }\n\t\tpart = 6; while (part > 0 && !(pos >= m[part].Start && pos <= m[part].End)) part--;\n\t\treturn part > 0;\n\t}\n\t\n\tstatic bool _ParseDate(string s, out DateTime d) {\n\t\treturn DateTime.TryParseExact(s, \"yyyy-M-d H:m:s\", null, DateTimeStyles.None, out d);\n\t}\n\t\n\tint _MouseCharPos(MouseEventArgs e, bool snapToText) => base.GetCharacterIndexFromPoint(e.GetPosition(this), snapToText);\n}\n"
  },
  {
    "path": "Au.Controls/Simple/KDialogWindow.cs",
    "content": "using System.Windows;\n\nnamespace Au.Controls;\n\n/// <summary>\n/// Can be used as base class for WPF windows used as dialogs.\n/// Adds WS_POPUP style, which prevents activating an unrelated window when closing this active owned nonmodal window (OS bug).\n/// Also adds functions <see cref=\"ShowSingle\"/>, <see cref=\"ShowAndWait\"/>.\n/// </summary>\npublic class KDialogWindow : Window {\n\t/// <summary>\n\t/// Shows window of type T (the caller's class) and ensures that there is single window of that type at a time. \n\t/// If a window of type T already exists (created with this function), activates it. Else calls <i>fNew</i> and <b>Show</b>. Will use <b>OnClose</b> to forget that window.\n\t/// Not thread-safe.\n\t/// Example: <c>ShowSingle(() => new DThisClass());</c>\n\t/// </summary>\n\t/// <param name=\"fNew\">Called to create new T if there is no T window.</param>\n\tprotected static T ShowSingle<T>(Func<T> fNew) where T : KDialogWindow {\n\t\tDebug_.PrintIf(!process.IsLaMainThread_);\n\n\t\tif (s_single.Find(o => o is T) is {  } d) {\n\t\t\td.Hwnd().ActivateL(true);\n\t\t} else {\n\t\t\ts_single.Add(d = fNew());\n\t\t\td.Show();\n\t\t}\n\t\treturn d as T;\n\t}\n\tstatic List<KDialogWindow> s_single = new();\n\t\n\tprotected override void OnClosed(EventArgs e) {\n\t\ts_single.Remove(this);\n\t\tbase.OnClosed(e);\n\t}\n\t\n\t/// <summary>\n\t/// If a window of type T already exists (created with <see cref=\"ShowSingle\"/>), gets it and returns true, else returns false.\n\t/// </summary>\n\tpublic static bool GetSingle<T>(out T window) where T : KDialogWindow\n\t\t=> null != (window = s_single.OfType<T>().FirstOrDefault());\n\t\n\t/// <summary>\n\t/// Sets <b>Title</b>, <b>Owner</b>, <b>ShowInTaskbar</b>.\n\t/// </summary>\n\t/// <param name=\"owner\">Window or element or null.</param>\n\tpublic void InitWinProp(string title, DependencyObject owner, bool showInTaskbar = false) {\n\t\tTitle = title;\n\t\tif (owner != null) Owner = owner as Window ?? GetWindow(owner);\n\t\tShowInTaskbar = showInTaskbar;\n\t}\n\t\n\tprotected override void OnSourceInitialized(EventArgs e) {\n\t\tvar w = this.Hwnd();\n\t\tw.SetStyle(WS.POPUP, WSFlags.Add);\n\t\tif (process.IsLaProcess_ && !process.IsLaMainThread_) w.Prop.Set(\"close me on exit\", 1);\n\t\tbase.OnSourceInitialized(e);\n\t}\n\t\n\t/// <summary>\n\t/// Sets <b>Owner</b> and calls <b>ShowDialog</b> without disabling thread windows.\n\t/// </summary>\n\t/// <returns>True if clicked OK (<b>DialogResult</b> true).</returns>\n\t/// <param name=\"owner\">If not null, sets <b>Owner</b>.</param>\n\t/// <param name=\"hideOwner\">Temporarily hide owner.</param>\n\t/// <param name=\"disableOwner\">Temporarily disable owner.</param>\n\tpublic bool ShowAndWait(Window owner = null, bool hideOwner = false, bool disableOwner = false) {\n\t\tif (owner != null) Owner = owner; else hideOwner = disableOwner = false;\n\t\twnd ow = hideOwner || disableOwner ? Owner.Hwnd() : default;\n\t\tif (hideOwner) ow.ShowL(false); //not owner.Hide(), it closes owner if it is modal\n\t\tif (disableOwner) {\n\t\t\tow.Enable(false);\n\t\t\tClosing += (_, e) => { if (!e.Cancel) ow.Enable(true); }; //the best time to enable. Later would activate wrong window.\n\t\t}\n\t\t\n\t\t//To prevent disabling thread windows, temporarily disable all visible enabled thread windows.\n\t\t//\tSee WPF code in Window.cs functions EnableThreadWindows, ThreadWindowsCallback, ShowDialog.\n\t\t//\tDisabling/enabing a window is fast and does not send messages to it, even wm_stylechanging/ed.\n\t\t//\tAnother way: Show and Dispatcher.PushFrame. Problem: does not set DialogResult. How to know how the dialog was closed?\n\t\tbool reenable = false;\n\t\tvar tw = wnd.getwnd.threadWindows(process.thisThreadId, onlyVisible: true);\n\t\tfor (int i = 0; i < tw.Length; i++) { if (tw[i].IsEnabled()) { reenable = true; tw[i].Enable(false); } else tw[i] = default; }\n\t\tRoutedEventHandler eh = null; //would be less code with Dispatcher.InvokeAsync or timer, but unreliable, eg can be too soon or interfere with another dialog\n\t\tif (reenable) Loaded += eh = (_, _) => {\n\t\t\teh = null;\n\t\t\tforeach (var v in tw) if (!v.Is0) v.Enable(true);\n\t\t};\n\t\t\n\t\tif (owner == null && ShowActivated && !wnd.active.IsOfThisProcess) {\n\t\t\tLoaded += (_, _) => this.Hwnd().ActivateL();\n\t\t}\n\t\t\n\t\ttry { return ShowDialog() == true; }\n\t\tfinally {\n\t\t\teh?.Invoke(null, null); //if failed to load\n\t\t\tif (hideOwner) { ow.ShowL(true); ow.ActivateL(); }\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au.Controls/Simple/KGroupBox.cs",
    "content": "using System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Media;\n\nnamespace Au.Controls;\n\n/// <summary>\n/// Makes the GroupBox more vivid.\n/// </summary>\npublic class KGroupBox : GroupBox {\n\tprotected private readonly bool _no;\n\t\n\tpublic KGroupBox() {\n\t\tif (_no = SystemParameters.HighContrast) return;\n\t\tBorderBrush = SystemColors.ActiveBorderBrush; //default is almost invisible\n\t}\n\t\n\tprotected override void OnHeaderChanged(object oldHeader, object newHeader) {\n\t\tif (newHeader is string s && !_no) {\n\t\t\tif (oldHeader is null) {\n\t\t\t\tHeader = new TextBlock { Text = s, FontWeight = FontWeights.Bold };\n\t\t\t\treturn;\n\t\t\t} else if (oldHeader is TextBlock t) {\n\t\t\t\tt.Text = s;\n\t\t\t\tHeader = t;\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tbase.OnHeaderChanged(oldHeader, newHeader);\n\t}\n}\n\n/// <summary>\n/// Makes the GroupBox look like separator with label.\n/// </summary>\npublic class KGroupBoxSeparator : KGroupBox {\n\tpublic KGroupBoxSeparator() {\n\t\tif (_no) return; //WPF draws badly\n\t\tBorderThickness = new(0, 1, 0, 0);\n\t\tPadding = new(-6, 0, -6, -6);\n\t}\n}\n"
  },
  {
    "path": "Au.Controls/Simple/KHotkeyControl.cs",
    "content": "using System.Windows;\nusing System.Windows.Documents;\nusing System.Windows.Controls;\n\nnamespace Au.Controls;\n\npublic class KHotkeyControl : UserControl {\n\tTextBox _tHotkey;\n\tCheckBox _cCtrl, _cShift, _cAlt, _cWin;\n\tWindowsHook _hook;\n\tKKey _key;\n\tbool _noEvents;\n\tbool _forTrigger, _onlyMod;\n\t\n\tpublic KHotkeyControl(bool forTrigger = false, bool onlyMod = false) {\n\t\t_forTrigger = forTrigger;\n\t\t_onlyMod = onlyMod;\n\t\t\n\t\tvar b = new wpfBuilder(this);\n\t\tif (_onlyMod) b.Columns(0, 0, 0, 0, -1); else if (_forTrigger) b.Columns(-1, 0, 0, 0, 0); else b.Columns(-1);\n\t\tb.Options(margin: _forTrigger || _onlyMod ? new(0, 0, 10, 0) : new());\n\t\t\n\t\tif (!_onlyMod) {\n\t\t\tb.R.Add<AdornerDecorator>().Child().Add(out _tHotkey).Readonly(caretVisible: true)\n\t\t\t\t.Watermark(\"Hotkey\")\n\t\t\t\t.Tooltip(\"Focus this field and press a key with any combination of Ctrl, Shift, Alt, Win\");\n\t\t}\n\t\t\n\t\tif (_forTrigger || _onlyMod) {\n\t\t\tb.Add(out _cCtrl, \"Ctrl\").Add(out _cShift, \"Shift\").Add(out _cAlt, \"Alt\").Add(out _cWin, \"Win\");\n\t\t\tforeach (var c in b.Panel.Children.OfType<CheckBox>()) (c.IsThreeState, c.IsEnabled) = (_forTrigger, _onlyMod);\n\t\t\tRoutedEventHandler modCheck = (_, _) => {\n\t\t\t\tif (_noEvents) return;\n\t\t\t\tKMod mod = 0, modAny = 0;\n\t\t\t\t_Mod(KMod.Ctrl, _cCtrl);\n\t\t\t\t_Mod(KMod.Shift, _cShift);\n\t\t\t\t_Mod(KMod.Alt, _cAlt);\n\t\t\t\t_Mod(KMod.Win, _cWin);\n\t\t\t\t_Format(mod, modAny);\n\t\t\t\tChanged?.Invoke(default);\n\t\t\t\t\n\t\t\t\tvoid _Mod(KMod m, CheckBox cb) {\n\t\t\t\t\tbool? c = cb.IsChecked;\n\t\t\t\t\tif (c != false) { mod |= m; if (c == null) modAny |= m; }\n\t\t\t\t}\n\t\t\t};\n\t\t\t_cCtrl.Checked += modCheck;\n\t\t\t_cCtrl.Unchecked += modCheck;\n\t\t\t_cCtrl.Indeterminate += modCheck;\n\t\t\t_cShift.Checked += modCheck;\n\t\t\t_cShift.Unchecked += modCheck;\n\t\t\t_cShift.Indeterminate += modCheck;\n\t\t\t_cAlt.Checked += modCheck;\n\t\t\t_cAlt.Unchecked += modCheck;\n\t\t\t_cAlt.Indeterminate += modCheck;\n\t\t\t_cWin.Checked += modCheck;\n\t\t\t_cWin.Unchecked += modCheck;\n\t\t\t_cWin.Indeterminate += modCheck;\n\t\t}\n\t\t\n\t\tif (!_onlyMod) {\n\t\t\t_tHotkey.GotKeyboardFocus += (_, _) => {\n\t\t\t\tWindowsHook.DontRestoreKeyboardHooks_ = true;\n\t\t\t\t\n\t\t\t\t_hook = WindowsHook.Keyboard(k => {\n\t\t\t\t\tif (k.IsUp) return;\n\t\t\t\t\tKMod mod = 0;\n\t\t\t\t\tif (k.Mod == 0) {\n\t\t\t\t\t\t_key = k.Key;\n\t\t\t\t\t\tmod = keys.getMod();\n\t\t\t\t\t\tif (!(_key is KKey.CapsLock or KKey.NumLock or KKey.ScrollLock)) k.BlockEvent();\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (_key == 0) return;\n\t\t\t\t\t\t_key = 0;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tif (_forTrigger) {\n\t\t\t\t\t\t_noEvents = true;\n\t\t\t\t\t\t_cCtrl.IsChecked = mod.Has(KMod.Ctrl);\n\t\t\t\t\t\t_cShift.IsChecked = mod.Has(KMod.Shift);\n\t\t\t\t\t\t_cAlt.IsChecked = mod.Has(KMod.Alt);\n\t\t\t\t\t\t_cWin.IsChecked = mod.Has(KMod.Win);\n\t\t\t\t\t\t_noEvents = false;\n\t\t\t\t\t\tbool en = _key != 0;\n\t\t\t\t\t\t_cCtrl.IsEnabled = en;\n\t\t\t\t\t\t_cShift.IsEnabled = en;\n\t\t\t\t\t\t_cAlt.IsEnabled = en;\n\t\t\t\t\t\t_cWin.IsEnabled = en;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\t_Format(mod);\n\t\t\t\t\tChanged?.Invoke(k);\n\t\t\t\t});\n\t\t\t};\n\t\t\t\n\t\t\t_tHotkey.LostKeyboardFocus += (_, _) => {\n\t\t\t\tif (_hook != null) {\n\t\t\t\t\t_hook.Dispose();\n\t\t\t\t\t_hook = null;\n\t\t\t\t\tWindowsHook.DontRestoreKeyboardHooks_ = false;\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t}\n\t\n\tvoid _Format(KMod mod, KMod modAny = 0) {\n\t\tif (_onlyMod ? mod != 0 : _key != 0) {\n\t\t\tStringBuilder b = new();\n\t\t\tif (modAny == (KMod)15) {\n\t\t\t\tb.Append(\"?+\");\n\t\t\t} else {\n\t\t\t\t_Mod(KMod.Ctrl);\n\t\t\t\t_Mod(KMod.Shift);\n\t\t\t\t_Mod(KMod.Alt);\n\t\t\t\t_Mod(KMod.Win);\n\t\t\t\t\n\t\t\t\tvoid _Mod(KMod m) {\n\t\t\t\t\tif (!mod.Has(m)) return;\n\t\t\t\t\tb.Append(m);\n\t\t\t\t\tif (modAny.Has(m)) b.Append('?');\n\t\t\t\t\tb.Append('+');\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (_onlyMod) b.Remove(b.Length - 1, 1); else b.Append(_key);\n\t\t\tResult = b.ToString();\n\t\t\tif (!_onlyMod) {\n\t\t\t\t_tHotkey.Text = Result;\n\t\t\t\t_tHotkey.CaretIndex = Result.Length;\n\t\t\t}\n\t\t} else {\n\t\t\tResult = null;\n\t\t\t_tHotkey?.Clear();\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Returns the hotkey string or null.\n\t/// </summary>\n\tpublic string Result { get; private set; }\n\t\n\t/// <summary>\n\t/// Hotkey changed.\n\t/// The parameter is null if checked/unchecked a checkbox.\n\t/// </summary>\n\tpublic event Action<HookData.Keyboard?> Changed;\n\t\n\t///\n\tpublic new void Focus() {\n\t\t_tHotkey?.Focus();\n\t}\n}\n"
  },
  {
    "path": "Au.Controls/Simple/KListBoxItemWithImage.cs",
    "content": "using System.Windows;\nusing System.Windows.Controls;\n\nnamespace Au.Controls;\n\npublic class KListBoxItemWithImage : ListBoxItem {\n\treadonly TextBlock _tb;\n\t\n\t/// <param name=\"image\">String for <see cref=\"ImageUtil.LoadWpfImageElement\"/> or <see cref=\"FrameworkElement\"/> or null.</param>\n\t/// <param name=\"text\"></param>\n\t/// <param name=\"imageMarginTB\">Image's top and bottom margins.</param>\n\tpublic KListBoxItemWithImage(object image, string text, int imageMarginTB = 0) {\n\t\t(var p, _, _tb) = CreateContent(image, text, imageMarginTB);\n\t\tContent = p;\n\t}\n\t\n\tpublic void SetText(string text) {\n\t\t_tb.Text = text;\n\t}\n\t\n\tpublic void SetText(string text, string tooltip) {\n\t\t_tb.Text = text;\n\t\tToolTip = tooltip;\n\t}\n\t\n\tpublic override string ToString() => _tb.Text;\n\t\n\tpublic static (StackPanel panel, FrameworkElement image, TextBlock text) CreateContent(object image, string text, int imageMarginTB) {\n\t\tvar p = new StackPanel { Orientation = Orientation.Horizontal };\n\t\t\n\t\tvar im = image as FrameworkElement;\n\t\tif (im == null && image is string s) try { im = ImageUtil.LoadWpfImageElement(s); } catch {  }\n\t\tif (im != null) {\n\t\t\tim.Margin = new(0, imageMarginTB, 4, imageMarginTB);\n\t\t\tp.Children.Add(im);\n\t\t}\n\t\t\n\t\tvar tb = new TextBlock { Text = text };\n\t\tp.Children.Add(tb);\n\t\treturn (p, im, tb);\n\t}\n}\n\n//not used\n//public class KComboBoxItemWithImage : ComboBoxItem {\n//\treadonly TextBlock _tb;\n\n//\t/// <param name=\"image\">String for <see cref=\"ImageUtil.LoadWpfImageElement\"/> or <see cref=\"ImageSource\"/> or null.</param>\n//\t/// <param name=\"text\"></param>\n//\tpublic KComboBoxItemWithImage(object image, string text) {\n//\t\t(var p, _, _tb) = KListBoxItemWithImage.CreateContent(image, text);\n//\t\tContent = p;\n//\t}\n\n//\tpublic override string ToString() => _tb.Text;\n//}\n"
  },
  {
    "path": "Au.Controls/Simple/KPasswordBox.cs",
    "content": "using System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Controls.Primitives;\n\nnamespace Au.Controls;\n\n/// <summary>\n/// <c>PasswordBox</c> box with a toggle button \"Environment variable\" that turns it into <c>TextBox</c>.\n/// </summary>\nclass KPasswordBox : UserControl {\n\tDockPanel _dp;\n\tTextBox _tb;\n\tPasswordBox _pb;\n\tToggleButton _toggle;\n\t\n\tpublic KPasswordBox() {\n\t\tThickness pad = new(2, 1, 2, 2);\n\t\t_tb = new() { Padding = pad };\n\t\t_pb = new() { Padding = pad };\n\t\t_toggle = new() { Content = ImageUtil.LoadWpfImageElement(\"*Material.Variable #404040 @12\"), ToolTip = \"Environment variable\" };\n\t\t//DockPanel.SetDock(_toggle, Dock.Left);\n\t\t_dp = new();\n\t\t_dp.Children.Add(_toggle);\n\t\t_dp.Children.Add(_pb);\n\t\tbase.Content = _dp;\n\t\t\n\t\t_toggle.Checked += (_, _) => {\n\t\t\t_dp.Children.Remove(_pb);\n\t\t\t_dp.Children.Add(_tb);\n\t\t\t_tb.Text = _pb.Password;\n\t\t\t_tb.SelectAll();\n\t\t\tif (_toggle.IsFocused) _tb.Focus();\n\t\t};\n\t\t_toggle.Unchecked += (_, _) => {\n\t\t\t_dp.Children.Remove(_tb);\n\t\t\t_dp.Children.Add(_pb);\n\t\t\t_pb.Password = _tb.Text;\n\t\t\t_pb.SelectAll();\n\t\t\tif (_toggle.IsFocused) _pb.Focus();\n\t\t};\n\t}\n\t\n\t/// <summary>\n\t/// Gets or sets the state of the \"Environment variable\" toggle button and the type of the text field (<c>TextBox</c> if <c>true</c>, else <c>PasswordBox</c>). Default <c>false</c>.\n\t/// </summary>\n\tpublic bool IsEnvVar {\n\t\tget => _toggle.IsChecked == true;\n\t\tset { _toggle.IsChecked = value; }\n\t}\n\t\n\t/// <summary>\n\t/// Gets or sets the password or environment variable (if <see cref=\"IsEnvVar\"/> <c>true</c>).\n\t/// </summary>\n\tpublic string Text {\n\t\tget => IsEnvVar ? _tb.Text : _pb.Password;\n\t\tset { if (IsEnvVar) _tb.Text = value; else _pb.Password = value; }\n\t}\n\t\n\tpublic TextBox TheTextBox => _tb;\n\t\n\tpublic PasswordBox ThePasswordBox => _pb;\n}\n"
  },
  {
    "path": "Au.Controls/Simple/KPopup.cs",
    "content": "using System.Windows.Interop;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Input;\n\nnamespace Au.Controls;\n\n/// <summary>\n/// HwndSource-based window to use for various temporary tool/info popup windows.\n/// For example, in editor used for code info windows (completion list, parameters, \"Regex\" etc) and for some tooltips.\n/// Unlike Window and Popup, you can set any window style, easily show at any position at any DPI, etc.\n/// Like Popup, can show by rectangle. Unlike Popup, can be resizable.\n/// Like Popup, can be click-closed, and does not eat mouse events like Popup.\n/// </summary>\npublic class KPopup {\n\tWS _style;\n\tWSE _exStyle;\n\tSizeToContent _sizeToContent;\n\tbool _shadow;\n\tHwndSource _hs;\n\twnd _w;\n\tbool _inSizeMove;\n\t\n\t///\n\tpublic KPopup(WS style = WS.POPUP | WS.THICKFRAME, WSE exStyle = WSE.TOOLWINDOW | WSE.NOACTIVATE, bool shadow = false, SizeToContent sizeToContent = default) {\n\t\t_style = style;\n\t\t_exStyle = exStyle;\n\t\t_shadow = shadow;\n\t\t_sizeToContent = sizeToContent;\n\t}\n\t\n\tHwndSource _Create() {\n\t\tif (_hs == null) {\n\t\t\tvar p = new HwndSourceParameters {\n\t\t\t\tWindowStyle = (int)_style,\n\t\t\t\tExtendedWindowStyle = (int)_exStyle,\n\t\t\t\tWindowClassStyle = _shadow && !_style.Has(WS.THICKFRAME) ? (int)Api.CS_DROPSHADOW : 0,\n\t\t\t\tWindowName = _windowName,\n\t\t\t\t//AcquireHwndFocusInMenuMode = false,\n\t\t\t\t//RestoreFocusMode = System.Windows.Input.RestoreFocusMode.None,\n\t\t\t\t//TreatAsInputRoot = false,\n\t\t\t\tHwndSourceHook = _Hook\n\t\t\t};\n\t\t\t_hs = new _HwndSource(p) { kpopup = this, RootVisual = Border, SizeToContent = default };\n\t\t\t//print.it(_hs.AcquireHwndFocusInMenuMode, _hs.RestoreFocusMode, p.TreatAsInputRoot); //True, Auto, True\n\t\t\t\n\t\t\tOnHandleCreated();\n\t\t}\n\t\treturn _hs;\n\t}\n\t\n\tclass _HwndSource : HwndSource {\n\t\tpublic _HwndSource(HwndSourceParameters p) : base(p) { }\n\t\tpublic KPopup kpopup;\n\t}\n\t\n\tprotected virtual void OnHandleCreated() { HandleCreated?.Invoke(); }\n\tpublic event Action HandleCreated;\n\t\n\tpublic static KPopup FromHwnd(wnd w) {\n\t\tif (w.IsAlive && HwndSource.FromHwnd(w.Handle) is _HwndSource hs) return hs.kpopup;\n\t\treturn null;\n\t}\n\t\n\t/// <summary>\n\t/// Gets popup window handle. Returns default(wnd) if not created (also after destroying).\n\t/// </summary>\n\tpublic wnd Hwnd => _w;\n\t\n\t//public HwndSource HwndSource => _hs ?? _Create();\n\t\n\t/// <summary>\n\t/// Gets or sets window name.\n\t/// </summary>\n\tpublic string WindowName {\n\t\tget => _windowName;\n\t\tset {\n\t\t\t_windowName = value;\n\t\t\tif (!_w.Is0) _w.SetText(value);\n\t\t}\n\t}\n\tstring _windowName;\n\t\n\t/// <summary>\n\t/// Gets or sets this <b>KPopup</b> object name. It is not window name.\n\t/// </summary>\n\tpublic string Name { get; set; }\n\t\n\t/// <summary>\n\t/// Gets or sets WPF content. It is child of <see cref=\"Border\"/>.\n\t/// </summary>\n\tpublic UIElement Content {\n\t\tget => _content;\n\t\tset {\n\t\t\t_content = value;\n\t\t\tif (_border != null) _border.Child = value;\n\t\t}\n\t}\n\tUIElement _content;\n\t\n\t/// <summary>\n\t/// Gets the WPF root object (<see cref=\"HwndSource.RootVisual\"/>) of the popup window. Its child is <see cref=\"Content\"/>.\n\t/// </summary>\n\tpublic Border Border => _border ??= new Border { Child = _content, SnapsToDevicePixels = true };\n\tBorder _border;\n\t//workaround for: if content is eg FlowDocumentScrollViewer, it has focus problems if it is RootVisual.\n\t//\tEg context menu items disabled. Need a container, eg Border or Panel.\n\t\n\t/// <summary>\n\t/// Desired window size. WPF logical pixels.\n\t/// Actual size can be smaller if would not fit in screen.\n\t/// </summary>\n\tpublic SIZE Size {\n\t\tget => _size;\n\t\tset {\n\t\t\t_size = value;\n\t\t\tif (IsVisible) _w.ResizeL_(Dpi.Scale(_size, _w));\n\t\t}\n\t}\n\tSIZE _size;\n\t\n\t/// <summary>\n\t/// Set <see cref=\"HwndSource.SizeToContent\"/>.\n\t/// If false (default), this class calculates content size when showing, and does not update while showing. If true, may align incorrectly.\n\t/// </summary>\n\tpublic bool WpfSizeToContent { get; set; }\n\t\n\t/// <summary>\n\t/// Shows the popup window by a window, WPF element or rectangle.\n\t/// </summary>\n\t/// <param name=\"owner\">Provides owner window and optionally rectangle. Can be <b>FrameworkElement</b>, <b>KPopup</b>, <b>wnd</b> or null.</param>\n\t/// <param name=\"side\">Show at this side of rectangle, or opposite side if does not fit in screen. If null, shows in rectangle.</param>\n\t/// <param name=\"rScreen\">Rectangle in screen (physical pixels). If null, uses owner's rectangle. Cannot be both null.</param>\n\t/// <param name=\"exactSize\">If does not fit in screen, cover part of rectangle but don't make smaller.</param>\n\t/// <param name=\"exactSide\">Never show at opposite side.</param>\n\t/// <exception cref=\"NotSupportedException\">Unsupported <i>owner</i> type.</exception>\n\t/// <exception cref=\"ArgumentException\">Both owner and rScreen are null. Or owner handle not created.</exception>\n\t/// <remarks>\n\t/// If <see cref=\"Size\"/> not set, uses <see cref=\"SizeToContent.WidthAndHeight\"/>.\n\t/// </remarks>\n\tpublic void ShowByRect(object owner, Dock? side, RECT? rScreen = null, bool exactSize = false, bool exactSide = false) {\n\t\t//CloseHides = true; //print.it(_w); //18 -> 5 ms\n\t\t//perf.first(); if(owner is FrameworkElement test) test.Dispatcher.InvokeAsync(() => perf.nw());\n\t\t\n\t\twnd ow = default;\n\t\tswitch (owner) {\n\t\tcase null:\n\t\t\tif (rScreen == null) throw new ArgumentException(\"owner and rScreen are null\");\n\t\t\tbreak;\n\t\tcase FrameworkElement e:\n\t\t\tow = e.Hwnd().Window;\n\t\t\trScreen ??= e.RectInScreen();\n\t\t\tbreak;\n\t\tcase KPopup p:\n\t\t\tow = p._w;\n\t\t\trScreen ??= ow.Rect;\n\t\t\tbreak;\n\t\tcase wnd w:\n\t\t\tow = w;\n\t\t\trScreen ??= ow.Rect;\n\t\t\tbreak;\n\t\tdefault: throw new NotSupportedException(\"owner type\");\n\t\t}\n\t\tif (owner != null && ow.Is0) throw new ArgumentException(\"owner window not created\");\n\t\tif (_size == default) _sizeToContent = SizeToContent.WidthAndHeight;\n\t\t\n\t\t_Create();\n\t\t\n\t\t//could use API CalculatePopupWindowPosition instead of this code, but it is not exactly what need here.\n\t\tRECT r = rScreen.Value;\n\t\tvar scrn = screen.of(r);\n\t\tvar rs = scrn.WorkArea;\n\t\tint dpi = scrn.Dpi;\n\t\tSIZE size = Dpi.Scale(Size, dpi);\n\t\t\n\t\tif (_sizeToContent != default) {\n\t\t\tRECT nc = default;\n\t\t\tDpi.AdjustWindowRectEx(dpi, ref nc, _style, _exStyle);\n\t\t\trs.left -= nc.left; rs.right -= nc.right; rs.top -= nc.top; rs.bottom -= nc.bottom;\n\t\t\t\n\t\t\tif (_sizeToContent.Has(SizeToContent.Width)) size.width = rs.Width; else size.width = Math.Min(size.width, rs.Width);\n\t\t\tif (_sizeToContent.Has(SizeToContent.Height)) size.height = rs.Height; else size.height = Math.Min(size.height, rs.Height);\n\t\t\t_border.Measure(Dpi.Unscale(size, dpi));\n\t\t\t//never mind: it seems FlowDocument measures only height.\n\t\t\t//\tIf need width, could instead use TextBlock. It does not support paragraph etc, but we can use multiple TextBlock etc in StackPanel.\n\t\t\t//\tBut then no select/copy, not so easy scrolling, etc.\n\t\t\t_border.UpdateLayout();\n\t\t\tvar ds = _border.DesiredSize;\n\t\t\tds.Width = Math.Ceiling(ds.Width + .5); ds.Height = Math.Ceiling(ds.Height + .5); //avoid scrollbars\n\t\t\tsize = Dpi.Scale(ds, dpi);\n\t\t\tsize.width += nc.Width; size.height += nc.Height;\n\t\t}\n\t\tsize.width = Math.Min(size.width, rs.Width);\n\t\tsize.height = Math.Min(size.height, rs.Height);\n\t\t\n\t\t_hs.SizeToContent = WpfSizeToContent ? _sizeToContent : default;\n\t\t\n\t\tif (side != null) {\n\t\t\tint spaceT = r.top - rs.top, spaceB = rs.bottom - r.bottom, spaceL = r.left - rs.left, spaceR = rs.right - r.right;\n\t\t\tif (!exactSide) {\n\t\t\t\tswitch (side) {\n\t\t\t\tcase Dock.Left:\n\t\t\t\t\tif (size.width > spaceL && spaceR > spaceL) side = Dock.Right;\n\t\t\t\t\tbreak;\n\t\t\t\tcase Dock.Right:\n\t\t\t\t\tif (size.width > spaceR && spaceL > spaceR) side = Dock.Left;\n\t\t\t\t\tbreak;\n\t\t\t\tcase Dock.Top:\n\t\t\t\t\tif (size.height > spaceT && spaceB > spaceT) side = Dock.Bottom;\n\t\t\t\t\tbreak;\n\t\t\t\tcase Dock.Bottom:\n\t\t\t\t\tif (size.height > spaceB && spaceT > spaceB) side = Dock.Top;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!exactSize) {\n\t\t\t\tswitch (side) {\n\t\t\t\tcase Dock.Left:\n\t\t\t\t\tif (size.width > spaceL) size.width = Math.Max(spaceL, size.width / 2);\n\t\t\t\t\tbreak;\n\t\t\t\tcase Dock.Right:\n\t\t\t\t\tif (size.width > spaceR) size.width = Math.Max(spaceR, size.width / 2);\n\t\t\t\t\tbreak;\n\t\t\t\tcase Dock.Top:\n\t\t\t\t\tif (size.height > spaceT) size.height = Math.Max(spaceT, size.height / 2);\n\t\t\t\t\tbreak;\n\t\t\t\tcase Dock.Bottom:\n\t\t\t\t\tif (size.height > spaceB) size.height = Math.Max(spaceB, size.height / 2);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tswitch (side) {\n\t\t\tcase Dock.Left: r.left -= size.width; break;\n\t\t\tcase Dock.Right: r.left = r.right; break;\n\t\t\tcase Dock.Top: r.top -= size.height; break;\n\t\t\tcase Dock.Bottom: r.top = r.bottom; break;\n\t\t\t}\n\t\t}\n\t\t\n\t\tr.left = Math.Clamp(r.left, rs.left, rs.right - size.width);\n\t\tr.top = Math.Clamp(r.top, rs.top, rs.bottom - size.height);\n\t\tr.Width = size.width; r.Height = size.height;\n\t\t\n\t\t_inSizeMove = false;\n\t\t_w.MoveL(r);\n\t\tif (_w.Get.Owner != ow) WndUtil.SetOwnerWindow(_w, ow);\n\t\tif (!IsVisible) _w.SetWindowPos(SWPFlags.SHOWWINDOW | SWPFlags.NOMOVE | SWPFlags.NOSIZE | SWPFlags.NOACTIVATE | SWPFlags.NOOWNERZORDER);\n\t}\n\t\n\t/// <summary>\n\t/// Destroys or hides the popup window, depending on <see cref=\"CloseHides\"/>.\n\t/// </summary>\n\tpublic void Close() {\n\t\tif (_hs == null) return;\n\t\tif (CloseHides) _w.ShowL(false);\n\t\telse _hs.Dispose();\n\t}\n\t\n\t/// <summary>\n\t/// Don't destroy the popup window when closing, but just hide.\n\t/// In any case, if destroyed, <b>ShowX</b> will create new window.\n\t/// </summary>\n\tpublic bool CloseHides { get; set; }\n\t\n\t/// <summary>\n\t/// true if closed (or hidden if <see cref=\"CloseHides\"/>) when the user clicked the x button.\n\t/// </summary>\n\tpublic bool UserClosed { get; set; }\n\t\n\t/// <summary>\n\t/// Whether the popup window is currently visible.\n\t/// </summary>\n\tpublic bool IsVisible => _w.IsVisible;\n\t\n\t/// <summary>\n\t/// When the popup window becomes invisible. It also happend when destroying.\n\t/// </summary>\n\tpublic event EventHandler Hidden;\n\t\n\t/// <summary>\n\t/// When destroying the popup window (WM_NCDESTROY).\n\t/// </summary>\n\tpublic event EventHandler Destroyed;\n\t\n\t/// <summary>\n\t/// Close when mouse clicked.\n\t/// </summary>\n\tpublic CC ClickClose { get; set; }\n\n\t/// <summary>\n\t/// Activate the owner window when clicked, if this and owner both are inactive. Only if has <b>WS_EX_NOACTIVATE</b> style.\n\t/// </summary>\n\tpublic bool ClickInactiveActivateOwner { get; set; }\n\t\n\tvoid _ClickCloseTimer(bool? start) {\n\t\tif (start == null) {\n\t\t\tint state = (keys.gui.getKeyState(KKey.MouseLeft) & 1)\n\t\t\t\t| (keys.gui.getKeyState(KKey.MouseRight) & 1) << 1\n\t\t\t\t| (keys.gui.getKeyState(KKey.MouseMiddle) & 1) << 2;\n\t\t\tif (_ccState < 0) _ccState = state; //first time\n\t\t\tif (state == _ccState) return;\n\t\t\t_ccState = state;\n\t\t\tif (ClickClose != CC.Anywhere) {\n\t\t\t\tvar wm = wnd.fromMouse(WXYFlags.NeedWindow);\n\t\t\t\tif (ClickClose == CC.Inside) {\n\t\t\t\t\tif (wm != _w) return;\n\t\t\t\t} else {\n\t\t\t\t\tif (wm == _w) return;\n\t\t\t\t\tif (wm.IsOfThisThread && wm.ZorderIsAbove(_w)) return;\n\t\t\t\t}\n\t\t\t}\n\t\t\tClose();\n\t\t} else if (start == true) {\n\t\t\tif (!ClickClose.HasAny(CC.Anywhere)) return;\n\t\t\t_ccTimer = 0 != Api.SetTimer(_w, c_ccTimer, 100, null);\n\t\t\t_ccState = -1; //GetKeyState may not work if called from WM_WINDOWPOSCHANGED. Call from WM_TIMER.\n\t\t} else if (_ccTimer) {\n\t\t\t_ccTimer = false;\n\t\t\tApi.KillTimer(_w, c_ccTimer);\n\t\t}\n\t}\n\tconst int c_ccTimer = 10;\n\tbool _ccTimer;\n\tint _ccState;\n\t\n\t[Flags]\n\tpublic enum CC { Inside = 1, Outside = 2, Anywhere = 3 };\n\t\n\tunsafe nint _Hook(nint hwnd, int msg, nint wParam, nint lParam, ref bool handled) {\n\t\tvar r = _Hook((wnd)hwnd, msg, wParam, lParam);\n\t\thandled = r != null;\n\t\treturn r ?? 0;\n\t}\n\t\n\tunsafe nint? _Hook(wnd w, int msg, nint wParam, nint lParam) {\n\t\t//WndUtil.PrintMsg(w, msg, wParam, lParam);\n\t\t//if(WndUtil.PrintMsg(out string s1, w, msg, wParam, lParam)) print.qm2.write(s1);\n\t\t//if (msg == Api.WM_ACTIVATE && wParam != 0) print.it(\"ACTIVATE\");\n\t\t\n\t\tswitch (msg) {\n\t\tcase Api.WM_NCCREATE:\n\t\t\t_w = w;\n\t\t\tbreak;\n\t\tcase Api.WM_NCDESTROY:\n\t\t\tDestroyed?.Invoke(this, EventArgs.Empty);\n\t\t\t_hs.RootVisual = null;\n\t\t\t_hs = null;\n\t\t\t_w = default;\n\t\t\tbreak;\n\t\tcase Api.WM_WINDOWPOSCHANGED:\n\t\t\tvar wp = (Api.WINDOWPOS*)lParam;\n\t\t\t//print.it(wp->flags & SWPFlags._KNOWNFLAGS, IsVisible);\n\t\t\tif (!wp->flags.Has(SWPFlags.NOSIZE) && _inSizeMove) _size = SIZE.From(Dpi.Unscale((wp->cx, wp->cy), w), true);\n\t\t\tif (wp->flags.Has(SWPFlags.SHOWWINDOW)) {\n\t\t\t\tUserClosed = false;\n\t\t\t\t_ClickCloseTimer(true);\n\t\t\t}\n\t\t\tif (wp->flags.Has(SWPFlags.HIDEWINDOW)) {\n\t\t\t\t_ClickCloseTimer(false);\n\t\t\t\tif (Border.IsKeyboardFocusWithin) Keyboard.ClearFocus();\n\t\t\t\tHidden?.Invoke(this, EventArgs.Empty);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase Api.WM_ENTERSIZEMOVE:\n\t\t\t_inSizeMove = true;\n\t\t\tbreak;\n\t\tcase Api.WM_EXITSIZEMOVE:\n\t\t\t_inSizeMove = false;\n\t\t\tbreak;\n\t\tcase Api.WM_MOUSEACTIVATE:\n\t\t\t//OS ignores WS_EX_NOACTIVATE if the active window is of this thread. Workaround: on WM_MOUSEACTIVATE return MA_NOACTIVATE.\n\t\t\tswitch (Math2.HiShort(lParam)) {\n\t\t\tcase Api.WM_MBUTTONDOWN:\n\t\t\t\tClose(); //never mind: we probably don't receive this message if our thread is inactive\n\t\t\t\treturn Api.MA_NOACTIVATEANDEAT;\n\t\t\t}\n\t\t\tif (_exStyle.Has(WSE.NOACTIVATE)) {\n\t\t\t\tif (ClickInactiveActivateOwner && w.Get.Owner is var ow && ow.IsAlive && !ow.IsActive && !w.IsActive) {\n\t\t\t\t\tow.ActivateL();\n\t\t\t\t}\n\t\t\t\treturn Api.MA_NOACTIVATE;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase Api.WM_NCLBUTTONDOWN:\n\t\t\tif (_exStyle.Has(WSE.NOACTIVATE)) {\n\t\t\t\t//OS activates when clicked in non-client area, eg when moving or resizing. Workaround: on WM_NCLBUTTONDOWN suppress activation with a CBT hook.\n\t\t\t\t//When moving or resizing, WM_NCLBUTTONDOWN returns when moving/resizing ends. On resizing would activate on mouse button up.\n\t\t\t\tvar wa = wnd.thisThread.active;\n\t\t\t\tif (wa != default && wa != w) {\n\t\t\t\t\tusing (WindowsHook.ThreadCbt(d => d.code == HookData.CbtEvent.ACTIVATE && d.Hwnd == w))\n\t\t\t\t\t\tApi.DefWindowProc(w, msg, wParam, lParam);\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\tcase Api.WM_SYSCOMMAND when (wParam & 0xFFF0) == Api.SC_CLOSE:\n\t\t\tUserClosed = true;\n\t\t\tbreak;\n\t\tcase Api.WM_CLOSE:\n\t\t\tif (CloseHides) { _w.ShowL(false); return 0; }\n\t\t\tbreak;\n\t\tcase Api.WM_DPICHANGED:\n\t\t\tif (!_inSizeMove) *(RECT*)lParam = _w.Rect; //prevent changing rect, it's already calculated for the DPI\n\t\t\t_hs.DpiChangedWorkaround();\n\t\t\tbreak;\n\t\tcase Api.WM_TIMER when wParam == c_ccTimer:\n\t\t\t_ClickCloseTimer(null);\n\t\t\tbreak;\n\t\tcase Api.WM_SHOWWINDOW:\n\t\t\tif (wParam == 0) {\n\t\t\t\tif (lParam == 1) _w.ShowL(false); //minimizing owner. Prevent showing again when restoring.\n\t\t\t\telse if (w.IsActive) {\n\t\t\t\t\tvar ow = w.Get.Owner;\n\t\t\t\t\t_hs.Dispatcher.InvokeAsync(() => { if(w.IsActive && ow.IsVisible && !ow.IsMinimized) ow.ActivateL(); });\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\treturn null;\n\t}\n}\n"
  },
  {
    "path": "Au.Controls/Simple/KPopupListBox.cs",
    "content": "using System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Controls.Primitives;\nusing System.Windows.Input;\n\nnamespace Au.Controls;\n\n/// <summary>\n/// Simple <see cref=\"Popup\"/> with child <see cref=\"ListBox\"/>.\n/// </summary>\n/// <remarks>\n/// The <see cref=\"Control\"/> property gets the <b>ListBox</b>. Add items to it.\n/// Show the popup as usually (set <b>PlacementTarget</b> etc and <b>IsOpen</b>=true).\n/// \n/// When an item clicked, closes the popup and fires <see cref=\"OK\"/> event. Also when pressed Enter key when an item is selected.\n/// Closes the popup without the event when clicked outside or pressed Esc key.\n/// </remarks>\npublic class KPopupListBox : Popup {\n\treadonly ListBox _lb;\n\t\n\t///\n\tpublic KPopupListBox() {\n\t\tChild = _lb = new ListBox();\n\t\tStaysOpen = false;\n\t}\n\t\n\t/// <summary>\n\t/// Gets the <b>ListBox</b>.\n\t/// </summary>\n\tpublic ListBox Control => _lb;\n\t\n\t/// <summary>\n\t/// When an item clicked, or pressed Enter key and there is a selected item.\n\t/// The popup is already closed.\n\t/// </summary>\n\tpublic event Action<object> OK;\n\t\n\tvoid _CloseOK() {\n\t\tIsOpen = false;\n\t\tif (_lb.SelectedItem is { } v) OK?.Invoke(v);\n\t}\n\t\n\t///\n\tprotected override void OnMouseUp(MouseButtonEventArgs e) {\n\t\tswitch (e.ChangedButton) {\n\t\tcase MouseButton.Left: _CloseOK(); break;\n\t\tcase MouseButton.Middle: IsOpen = false; break;\n\t\t}\n\t\tbase.OnMouseUp(e);\n\t}\n\t\n\t#region steal keyboard from the focused HwndHost-ed control\n\t\n\tprotected override void OnKeyUp(KeyEventArgs e) { //if OnKeyDown, next time Esc defocuses the HwndHost-ed control\n\t\tswitch (e.Key) {\n\t\tcase Key.Enter: _CloseOK(); break;\n\t\tcase Key.Escape: IsOpen = false; break;\n\t\t}\n\t\tbase.OnKeyUp(e);\n\t}\n\t\n\tprotected override void OnOpened(EventArgs e) {\n\t\t_lb.Focus();\n\t\tbase.OnOpened(e);\n\t\t_hook = WindowsHook.ThreadKeyboard(_Hook);\n\t}\n\tWindowsHook _hook; //also tested ComponentDispatcher.ThreadPreprocessMessage, but it does not steal messages from HwndHost-ed controls\n\t\n\tprotected override void OnClosed(EventArgs e) {\n\t\t_hook?.Dispose();\n\t\t_hook = null;\n\t\tbase.OnClosed(e);\n\t}\n\t\n\tbool _Hook(HookData.ThreadKeyboard k) {\n\t\tif (!_lb.IsKeyboardFocusWithin) return false;\n\t\tif (PresentationSource.FromVisual(_lb) is not { } inputSource) return false;\n\t\tvar key = KeyInterop.KeyFromVirtualKey((int)k.key);\n\t\tvar e = new KeyEventArgs(Keyboard.PrimaryDevice, inputSource, 0, key) { RoutedEvent = k.IsUp ? Keyboard.KeyUpEvent : Keyboard.KeyDownEvent };\n\t\t_lb.RaiseEvent(e);\n\t\treturn true;\n\t}\n\t\n\t#endregion\n}\n"
  },
  {
    "path": "Au.Controls/Simple/KScreenComboBox.cs",
    "content": "using System.Windows.Controls;\n\nnamespace Au.Controls;\n\n/// <summary>\n/// <b>ComboBox</b> to select screen.\n/// Ctor adds items. Finally call <b>Result</b>.\n/// </summary>\npublic class KScreenComboBox : ComboBox {\n\t///\n\tpublic KScreenComboBox() {\n\t\tItems.Add(\"Primary screen\");\n\t\tSelectedIndex = 0;\n\t\t\n\t\tvar a = screen.all;\n\t\tif (a.Length > 1) {\n\t\t\t//add functions of screen.at\n\t\t\tforeach (var v in typeof(screen.at).GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public)) {\n\t\t\t\tItems.Add(\"screen.at.\" + v.Name);\n\t\t\t}\n\t\t\t\n\t\t\t//if defined type \"screens\", add its public static properties that return screen\n\t\t\t//\tThis code works, but probably this feature would be rarely used. Now undocumented.\n\t\t\t//if (_compilation.GetSymbolsWithName(\"screens\", SymbolFilter.Type).FirstOrDefault() is INamedTypeSymbol screens) {\n\t\t\t//\tforeach (var v in screens.GetMembers()) {\n\t\t\t//\t\tif (v is not IPropertySymbol p || !v.IsStatic || v.DeclaredAccessibility is not Microsoft.CodeAnalysis.Accessibility.Public) continue;\n\t\t\t//\t\tif (p.Type.ToString() != \"Au.screen\") continue;\n\t\t\t//\t\tItems.Add(\"screens.\" + v.Name);\n\t\t\t//\t}\n\t\t\t//}\n\t\t\t\n\t\t\t//if (a.Length == 2) Items.Add(\"screen.index(1)\"); //no. More screens may be added in the future, and indices may change then.\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Formats <i>screen</i> argument code.\n\t/// </summary>\n\t/// <param name=\"trigger\"></param>\n\t/// <returns>null if primary screen.</returns>\n\tpublic string Result(bool trigger) {\n\t\tint iScreen = SelectedIndex;\n\t\tif (iScreen == 0) return null;\n\t\tvar s = Items[iScreen] as string;\n\t\t//if (s.Starts(\"screens.\")) return s;\n\t\tif (s.Starts(\"screen.at.\")) s += trigger ? \"(true)\" : \"()\";\n\t\telse if (trigger && s.Like(\"screen.index(*)\")) s = s.Insert(^1, \", lazy: true\");\n\t\treturn s;\n\t}\n}\n"
  },
  {
    "path": "Au.Controls/Simple/KTextBox.cs",
    "content": "using System.Windows;\nusing System.Windows.Media;\nusing System.Windows.Controls;\nusing System.Windows.Documents;\nusing System.Windows.Input;\n\nnamespace Au.Controls;\n\n/// <summary>\n/// Adds some features:\n/// <br/>• Changes caret position when clicked left margin.\n/// <br/>• Clears on middle-click.\n/// <br/>• Can disable horizontal scrollbar when not focused.\n/// </summary>\npublic class KTextBox : TextBox {\n\t/// <summary>\n\t/// Disables horizontal scrollbar when not focused.\n\t/// </summary>\n\tpublic bool Small {\n\t\tget => _small;\n\t\tset {\n\t\t\tif (_small) throw new InvalidOperationException();\n\t\t\t_small = true;\n\t\t\tHorizontalScrollBarVisibility = ScrollBarVisibility.Disabled;\n\t\t}\n\t}\n\tbool _small;\n\t\n\tprotected override void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e) {\n\t\tif (_small) HorizontalScrollBarVisibility = ScrollBarVisibility.Auto;\n\t\tbase.OnGotKeyboardFocus(e);\n\t}\n\t\n\tprotected override void OnLostKeyboardFocus(KeyboardFocusChangedEventArgs e) {\n\t\tif (_small) HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled;\n\t\tbase.OnLostKeyboardFocus(e);\n\t}\n\t\n\tprotected override void OnMouseDown(MouseButtonEventArgs e) {\n\t\t//Workaround for this nasty default behavior of TextBox: does not set the caret position when clicked the left padding area.\n\t\t//\tThen difficult to set the caret position eg at the start, because the width of the sensitive area is 0.5 of the width of the first character (eg 2 pixels if `i`).\n\t\t//\tClassic Edit controls etc don't have this problem. Users often click the padding area to move the caret at the start, but in WPF TextBox it does not work.\n\t\tif (e.ChangedButton == MouseButton.Left && e.OriginalSource is Grid g && e.GetPosition(g).X < Padding.Left + 3) {\n\t\t\tvar p = e.GetPosition(this);\n\t\t\tint i = base.GetCharacterIndexFromPoint(p, true);\n\t\t\tif (i >= 0) this.CaretIndex = i;\n\t\t\t//never mind: cursor not I-beam.\n\t\t\t//never mind: can't drag-select starting from the padding area.\n\t\t}\n\t\tif (e.ChangedButton == MouseButton.Middle) {\n\t\t\tClear();\n\t\t}\n\t\tbase.OnMouseDown(e);\n\t}\n\t\n\tprotected override void OnContextMenuOpening(ContextMenuEventArgs e) {\n\t\tif (ContextMenu == null) {\n\t\t\tthis.xAddCutCopyPasteToContextMenu(addClear: true, setStateNow: true);\n\t\t}\n\t\tbase.OnContextMenuOpening(e);\n\t}\n}\n\n/// <summary>\n/// TextBox for a file or folder path.\n/// Supports drag-drop and Browse dialog (right-click).\n/// </summary>\npublic class KTextBoxFile : TextBox {\n\tbool _canDrop;\n\t\n\t///\n\tprotected override void OnPreviewDragEnter(DragEventArgs e) {\n\t\tif (e.Handled = _canDrop = e.Data.GetDataPresent(DataFormats.FileDrop)) e.Effects = DragDropEffects.Link;\n\t\tbase.OnPreviewDragEnter(e);\n\t}\n\t\n\t///\n\tprotected override void OnPreviewDragOver(DragEventArgs e) {\n\t\tif (e.Handled = _canDrop) e.Effects = DragDropEffects.Link;\n\t\tbase.OnPreviewDragEnter(e);\n\t}\n\t\n\t///\n\tprotected override void OnDrop(DragEventArgs e) {\n\t\tif (e.Data.GetData(DataFormats.FileDrop) is string[] a && a.Length > 0) {\n\t\t\tvar s = a[0];\n\t\t\tif (s.Ends(\".lnk\", true) && filesystem.exists(s).File)\n\t\t\t\ttry { s = shortcutFile.getTarget(s); }\n\t\t\t\tcatch (Exception) { }\n\t\t\tText = s;\n\t\t}\n\t\tbase.OnDrop(e);\n\t}\n\t\n\t///\n\tprotected override void OnContextMenuOpening(ContextMenuEventArgs e) {\n\t\tvar m = new popupMenu { CheckDontClose = true };\n\t\t\n\t\tm[\"Browse...\"] = o => {\n\t\t\tvar d = new FileOpenSaveDialog(ClientGuid ?? (IsFolder ? \"3d4a9167-929a-4346-adfb-e2f03427412c\" : \"6a7d02c0-7f98-4808-b764-84985ca6e767\"));\n\t\t\tif (d.ShowOpen(out string s, owner: this.Hwnd(), selectFolder: IsFolder)) _SetText(s);\n\t\t};\n\t\tm[\"folders.EditMe + @\\\"EditMe\\\"\"] = o => _SetText(o.Text);\n\t\tm.Submenu(\"Known folders\", m => {\n\t\t\tforeach (var v in typeof(folders).GetProperties()) {\n\t\t\t\tbool nac = folders.noAutoCreate;\n\t\t\t\tfolders.noAutoCreate = true;\n\t\t\t\tstring path = v.GetValue(null) switch { string k => k, FolderPath k => k.Path, _ => null };\n\t\t\t\tfolders.noAutoCreate = nac;\n\t\t\t\tif (path == null) continue;\n\t\t\t\tvar name = v.Name;\n\t\t\t\tm[$\"{name}\\t{path.Limit(80, middle: true)}\"] = o => _SetText(Unexpand ? $\"folders.{name} + @\\\"EditMe\\\"\" : path + (path.Ends('\\\\') ? \"\" : \"\\\\\"));\n\t\t\t}\n\t\t});\n\t\tm.Submenu(\"Environment variables\", m => {\n\t\t\tforeach (var (name, path) in Environment.GetEnvironmentVariables().OfType<System.Collections.DictionaryEntry>().Select(o => (o.Key as string, o.Value as string)).OrderBy(o => o.Item1)) {\n\t\t\t\tif (!pathname.isFullPath(path, orEnvVar: true)) continue;\n\t\t\t\tint i = path.IndexOf(';'); if (i > 0 && pathname.isFullPath(path.AsSpan(i + 1), orEnvVar: true)) continue; //list of paths\n\t\t\t\tif (IsFolder && filesystem.exists(path).File) continue;\n\t\t\t\tm[$\"{name}\\t{path.Limit(80, middle: true)}\"] = o => _SetText(Unexpand ? $\"%{name}%\\\\\" : pathname.expand(path));\n\t\t\t}\n\t\t});\n\t\tm.Submenu(\"Folder windows\", m => {\n\t\t\tforeach (var v in ExplorerFolder.All(onlyFilesystem: true).Select(o => o.GetFolderPath())) {\n\t\t\t\tm[v.Limit(100, middle: true)] = o => _SetText(v);\n\t\t\t}\n\t\t});\n\t\tm.Separator();\n\t\tm.AddCheck(\"Unexpand\", Unexpand, o => { Unexpand ^= true; UnexpandChanged?.Invoke(); });\n\t\t\n\t\tvoid _SetText(string s) {\n\t\t\tText = s;\n\t\t\tCaretIndex = s.Length;\n\t\t}\n\t\t\n\t\tm.Show(owner: this.Hwnd());\n\t\te.Handled = true;\n\t}\n\t\n\t/// <summary>\n\t/// true if this control is for a folder path.\n\t/// </summary>\n\tpublic bool IsFolder { get; set; }\n\t\n\t/// <summary>\n\t/// For <see cref=\"FileOpenSaveDialog\"/>.\n\t/// If not set, uses 2 different GUIDs: one for folders (see <see cref=\"IsFolder\"/>), other for files.\n\t/// </summary>\n\tpublic string ClientGuid { get; set; }\n\t\n\t/// <summary>\n\t/// Let <b>GetCode</b> unexpand path.\n\t/// Also used/changed by the context menu.\n\t/// Default true.\n\t/// </summary>\n\tpublic bool Unexpand { get; set; } = true;\n\t\n\tpublic event Action UnexpandChanged;\n\t\n\t/// <summary>\n\t/// Formats code like '@\"path\"' or 'folders.X + @\"relative\"' or 'folders.X'.\n\t/// </summary>\n\tpublic string GetCode() {\n\t\tvar s = Text;\n\t\tif (s.Contains('\"') || s.Starts(\"folders.\")) return s;\n\t\tif (Unexpand && folders.unexpandPath(s, out var s1, out var s2)) return s2.NE() ? s1 : $\"{s1} + @\\\"{s2}\\\"\";\n\t\treturn $\"@\\\"{s}\\\"\";\n\t}\n}\n"
  },
  {
    "path": "Au.Controls/Simple/KWpfMenu.cs",
    "content": "using System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Media;\nusing System.Windows.Threading;\nusing System.Windows.Input;\nusing System.Windows.Interop;\nusing System.Windows.Media.Imaging;\n\nnamespace Au.Controls;\n\n/// <summary>\n/// Based on WPF <see cref=\"ContextMenu\"/>, makes simpler to use it.\n/// </summary>\n/// <example>\n/// <code><![CDATA[\n/// var m = new KWpfMenu();\n/// m[\"One\"] = o => print.it(o);\n/// using(m.Submenu(\"Sub\")) {\n/// \tm[\"Three\"] = o => print.it(o);\n/// \tm[\"Four\"] = o => print.it(o);\n/// }\n/// m.Separator();\n/// m[\"Two\"] = o => { print.it(o); };\n/// m.Show(this); //or m.IsOpen=true;\n/// ]]></code>\n/// </example>\npublic class KWpfMenu : ContextMenu {\n\t///\n\tpublic KWpfMenu() {\n\t}\n\t\n\t/// <summary>\n\t/// Creates new <see cref=\"MenuItem\"/> and adds to the menu. Returns it.\n\t/// </summary>\n\t/// <param name=\"text\">\n\t/// Label. See <see cref=\"HeaderedItemsControl.Header\"/>.\n\t/// If contains '\\0' character, uses text before it for label and text after it for <see cref=\"MenuItem.InputGestureText\"/>; example: \"Text\\0\" + \"Ctrl+E\".\n\t/// </param>\n\t/// <param name=\"click\">Action called on click.</param>\n\t/// <param name=\"icon\">See <see cref=\"this[string, bool, object]\"/>.</param>\n\t/// <remarks>\n\t/// Usually it's easier to use the indexer instead. It just calls this function. See example.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// m[\"Example\"] = o => print.it(o);\n\t/// m.Last.IsChecked = true;\n\t/// ]]></code>\n\t/// </example>\n\tpublic MenuItem Add(object text, Action<WpfMenuActionArgs> click = null, object icon = null) {\n\t\tstring gest = null;\n\t\tif (text is string s && s.Contains('\\0')) {\n\t\t\tint j = s.IndexOf('\\0');\n\t\t\ttext = s[0..j];\n\t\t\tgest = s[++j..];\n\t\t}\n\t\tvar item = new _MenuItem(this) {\n\t\t\taction = click,\n\t\t\tactionException = ActionException,\n\t\t\tHeader = text\n\t\t};\n\t\tif (gest != null) item.InputGestureText = gest;\n\t\titem.Icon = MenuItemIcon_(icon);\n\t\tCurrentAddMenu.Items.Add(Last = item);\n\t\tItemAdded?.Invoke(item);\n\t\treturn item;\n\t}\n\t\n\t/// <summary>\n\t/// Creates new <see cref=\"MenuItem\"/> and adds to the menu.\n\t/// </summary>\n\t/// <param name=\"text\">\n\t/// Label. See <see cref=\"HeaderedItemsControl.Header\"/>.\n\t/// If contains '\\0' character, uses text before it for label and text after it for <see cref=\"MenuItem.InputGestureText\"/>; example: \"Text\\0\" + \"Ctrl+E\".\n\t/// </param>\n\t/// <param name=\"enabled\">Disabled if false. Default true.</param>\n\t/// <param name=\"icon\">\n\t/// Can be:\n\t/// - <see cref=\"Image\"/> or other WPF control to assign directly to <see cref=\"MenuItem.Icon\"/>.\n\t/// - <see cref=\"ImageSource\"/> - a WPF image. To create image from icon, use <see cref=\"icon.ToWpfImage\"/>.\n\t/// - string - image file path, or resource path that starts with \"resources/\" or has prefix \"resource:\", or Base64 encoded image with prefix \"image:\". Can be XAML file or resource. See <see cref=\"ImageUtil.LoadWpfImageElement\"/>. Supports environment variables. If not full path, looks in <see cref=\"folders.ThisAppImages\"/>.\n\t/// - <see cref=\"Uri\"/> - image file path, or resource pack URI, or URL. Does not support environment variables and <see cref=\"folders.ThisAppImages\"/>.\n\t/// \n\t/// If failed to find or load image file, prints warning (<see cref=\"print.warning\"/>).\n\t/// To create Base64 string, use menu Code > Find image.\n\t/// To add an image resource in Visual Studio, use build action \"Resource\" for the image file.\n\t/// </param>\n\t/// <value>Action called on click.</value>\n\t/// <remarks>\n\t/// Calls <see cref=\"Add(object, Action{WpfMenuActionArgs}, object)\"/>.\n\t/// </remarks>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// m[\"Example\"] = o => print.it(o);\n\t/// m.Last.IsChecked = true;\n\t/// ]]></code>\n\t/// </example>\n\tpublic Action<WpfMenuActionArgs> this[string text, bool enabled = true, object icon = null] {\n\t\tset {\n\t\t\tvar v = Add(text, value, icon);\n\t\t\tif (!enabled) v.IsEnabled = false;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Adds separator.\n\t/// </summary>\n\tpublic void Separator() { CurrentAddMenu.Items.Add(new Separator()); }\n\t\n\t/// <summary>\n\t/// Creates new <see cref=\"MenuItem\"/> for a submenu and adds to the menu.\n\t/// </summary>\n\t/// <param name=\"text\">Label. See <see cref=\"HeaderedItemsControl.Header\"/>.</param>\n\t/// <param name=\"icon\"><see cref=\"MenuItem.Icon\"/>.</param>\n\t/// <param name=\"click\">Action called on click. Rarely used.</param>\n\t/// <remarks>\n\t/// Then the add-item functions will add items to the submenu, until the returned variable is disposed.\n\t/// </remarks>\n\t/// <example><see cref=\"KWpfMenu\"/></example>\n\tpublic UsingEndAction Submenu(object text, object icon = null, Action<WpfMenuActionArgs> click = null) {\n\t\tvar mi = Add(text, click, icon);\n\t\t_submenuStack.Push(mi);\n\t\treturn new UsingEndAction(() => _submenuStack.Pop());\n\t\t//CONSIDER: copy some properties of current menu. Or maybe WPF copies automatically, need to test.\n\t}\n\t\n\tStack<MenuItem> _submenuStack = new();\n\t//bool _AddingSubmenuItems => _submenuStack.Count > 0;\n\t\n\t/// <summary>\n\t/// Gets <see cref=\"ItemsControl\"/> of the menu or submenu where new items currently would be added.\n\t/// </summary>\n\tpublic ItemsControl CurrentAddMenu => _submenuStack.Count > 0 ? _submenuStack.Peek() : this;\n\t\n\t/// <summary>\n\t/// Gets the last added <see cref=\"MenuItem\"/>.\n\t/// </summary>\n\tpublic MenuItem Last { get; private set; }\n\t\n\t/// <summary>\n\t/// Called when added a non-separator item.\n\t/// </summary>\n\tpublic Action<MenuItem> ItemAdded { get; set; }\n\t\n\t/// <summary>\n\t/// Whether to handle exceptions in item action code. If false (default), handles exceptions and on exception calls <see cref=\"print.warning\"/>.\n\t/// Applied to menu items added afterwards.\n\t/// </summary>\n\tpublic bool ActionException { get; set; }\n\t\n\t/// <summary>\n\t/// Sets <see cref=\"ContextMenu.PlacementTarget\"/> = <i>owner</i> and <see cref=\"ContextMenu.IsOpen\"/> = true.\n\t/// </summary>\n\t/// <param name=\"owner\"><see cref=\"ContextMenu.PlacementTarget\"/>. The menu uses its DPI. If null, uses DPI of primary screen (WPF bug).</param>\n\t/// <param name=\"byCaret\">Show by caret (text cursor) position if possible.</param>\n\t/// <param name=\"modal\">Wait until closed.</param>\n\tpublic void Show(UIElement owner, bool byCaret = false, bool modal = false) {\n\t\tif (byCaret && miscInfo.getTextCursorRect(out RECT cr, out _)) {\n\t\t\tvar r = owner == null ? cr : new Rect(owner.PointFromScreen(new Point(cr.left, cr.top)), owner.PointFromScreen(new Point(cr.right, cr.bottom)));\n\t\t\tr.Inflate(30, 2);\n\t\t\tPlacementRectangle = r;\n\t\t\tPlacement = System.Windows.Controls.Primitives.PlacementMode.Bottom;\n\t\t}\n\t\tPlacementTarget = owner;\n\t\t\n\t\t//tested: VisualTreeHelper.SetRootDpi does not work.\n\t\t\n\t\t//workaround for: if focused is a native control in a HwndHost, it remains focused, and menu keyboard does not work normally.\n\t\t//\tTemporarily remove native focus from the control.\n\t\t//\tAlso tried to redirect key messages with a hook, but it does not work for arrow keys.\n\t\t//\tAlso tried to remove focus in OnOpened, but it closes the menu.\n\t\t//\tNever mind: hides caret. In notepad etc menus don't hide caret. But eg in VS hide too.\n\t\tif (owner is HwndHost hh && hh.IsFocused && Api.GetFocus() == (wnd)hh.Handle && FocusManager.GetFocusScope(hh) is UIElement fs) {\n\t\t\tfs.Focus();\n\t\t}\n\t\t\n\t\tIsOpen = true;\n\t\t\n\t\tif (modal) {\n\t\t\t_dispFrame = new DispatcherFrame();\n\t\t\tDispatcher.PushFrame(_dispFrame);\n\t\t}\n\t}\n\tDispatcherFrame _dispFrame;\n\t\n\tvoid _EndModal() {\n\t\tif (_dispFrame != null) {\n\t\t\t_dispFrame.Continue = false;\n\t\t\t_dispFrame = null;\n\t\t}\n\t}\n\t\n\t///\n\tprotected override void OnClosed(RoutedEventArgs e) {\n\t\t_EndModal();\n\t\tbase.OnClosed(e);\n\t}\n\t\n\tinternal static object MenuItemIcon_(object icon) {\n\t\tif (icon != null) {\n\t\t\ttry {\n\t\t\t\tImageSource iso = null;\n\t\t\t\tswitch (icon) {\n\t\t\t\tcase string s:\n\t\t\t\t\treturn ImageUtil.LoadWpfImageElement(s);\n\t\t\t\tcase Uri s:\n\t\t\t\t\tiso = BitmapFrame.Create(s);\n\t\t\t\t\tbreak;\n\t\t\t\tcase ImageSource s:\n\t\t\t\t\tiso = s;\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\treturn icon;\n\t\t\t\t}\n\t\t\t\tif (iso != null) return new Image { Source = iso };\n\t\t\t}\n\t\t\tcatch (Exception ex) { print.warning(\"Failed to load menu item image. \" + ex.ToStringWithoutStack()); }\n\t\t}\n\t\treturn null;\n\t\t//rejected: cache, like popupMenu.\n\t}\n\t\n\t/// <summary>\n\t/// Creates and shows popup menu where items use ids instead of actions.\n\t/// Returns selected item id, or 0 if canceled.\n\t/// </summary>\n\t/// <param name=\"items\">\n\t/// Menu items. Can be string[], List&lt;string&gt; or string like \"One|Two|Three\".\n\t/// Item id can be optionally specified like \"1 One|2 Two|3 Three\". If missing, uses id of previous non-separator item + 1. Example: \"One|Two|100 Three Four\" //1|2|100|101.\n\t/// For separators use null or empty strings: \"One|Two||Three|Four\".\n\t/// </param>\n\t/// <param name=\"owner\"><see cref=\"ContextMenu.PlacementTarget\"/>. The menu uses its DPI. If null, uses DPI of primary screen (WPF bug).</param>\n\t/// <param name=\"byCaret\">Show by caret (text cursor) position if possible.</param>\n\t/// <param name=\"beforeShow\">Called after adding menu items, before showing the menu. For example can set placement properties.</param>\n\t/// <remarks>\n\t/// The menu is modal; the function returns when closed.\n\t/// </remarks>\n\t/// <seealso cref=\"dialog.showList\"/>\n\tpublic static int ShowSimple(Strings items, UIElement owner, bool byCaret = false, Action<KWpfMenu> beforeShow = null) {\n\t\tvar a = items.ToArray();\n\t\tvar m = new KWpfMenu();\n\t\t//\tvar dispFrame = new DispatcherFrame();\n\t\t//\tm.Closed+=(_,_)=>dispFrame.Continue=false;\n\t\tint result = 0, autoId = 0;\n\t\tforeach (var v in a) {\n\t\t\tvar s = v;\n\t\t\tif (s.NE()) {\n\t\t\t\tm.Separator();\n\t\t\t} else {\n\t\t\t\tif (s.ToInt(out int id, 0, out int end)) {\n\t\t\t\t\tif (s.Eq(end, ' ')) end++;\n\t\t\t\t\ts = s[end..];\n\t\t\t\t\tautoId = id;\n\t\t\t\t} else {\n\t\t\t\t\tid = ++autoId;\n\t\t\t\t}\n\t\t\t\tm.Add(s, o => result = (int)o.Item.Tag).Tag = id;\n\t\t\t\t//\t\t\tm.Add(s, o => {\n\t\t\t\t//\t\t\t\tresult = (int)o.Item.Tag;\n\t\t\t\t//\t\t\t\tdispFrame.Continue=false;\n\t\t\t\t//\t\t\t}).Tag = id;\n\t\t\t}\n\t\t}\n\t\tbeforeShow?.Invoke(m);\n\t\tm.Show(owner, byCaret: byCaret, modal: true);\n\t\t//\tm.IsOpen=true;\n\t\t//\tDispatcher.PushFrame(dispFrame);\n\t\treturn result;\n\t}\n\t\n\tclass _MenuItem : MenuItem {\n\t\tKWpfMenu _m;\n\t\tpublic Action<WpfMenuActionArgs> action;\n\t\tpublic bool actionException;\n\t\t\n\t\tpublic _MenuItem(KWpfMenu m) { _m = m; }\n\t\t\n\t\tprotected override void OnClick() {\n\t\t\tbase.OnClick(); //must be first, because changes IsChecked (if IsCheckable)\n\t\t\t\n\t\t\t_m._EndModal(); //workaround for: OnClosed called with 160 ms delay. Same with native message loop.\n\t\t\tif (action != null) {\n\t\t\t\ttry { action(new WpfMenuActionArgs(this)); }\n\t\t\t\tcatch (Exception ex) when (!actionException) { print.warning(ex); }\n\t\t\t}\n\t\t}\n\t\t\n\t\tprotected override void OnPreviewMouseUp(MouseButtonEventArgs e) {\n\t\t\tswitch (e.ChangedButton) {\n\t\t\t//case MouseButton.Right:\n\t\t\t//\tif (this.HasItems && e.Source != this) break;\n\t\t\t//\te.Handled = true;\n\t\t\t//\tbreak;\n\t\t\tcase MouseButton.Middle:\n\t\t\t\t_m.IsOpen = false;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbase.OnPreviewMouseUp(e);\n\t\t}\n\t}\n}\n//}\n\n//namespace Au.Types\n//{\n/// <summary>\n/// Arguments for <see cref=\"KWpfMenu\"/> item actions.\n/// </summary>\npublic class WpfMenuActionArgs {\n\t///\n\tpublic WpfMenuActionArgs(MenuItem item) { Item = item; }\n\t\n\t/// <summary>\n\t/// The menu item object.\n\t/// </summary>\n\tpublic MenuItem Item { get; }\n\t\n\t///\n\tpublic override string ToString() {\n\t\tvar d = Item.Dispatcher;\n\t\tif (d.Thread == Thread.CurrentThread) return Item.Header.ToString();\n\t\treturn d.Invoke(() => Item.Header.ToString());\n\t}\n}"
  },
  {
    "path": "Au.Controls/Util, Api/HwndHostAccessibleBase_.cs",
    "content": "using System.Windows;\nusing IAccessible = Au.Types.Api.IAccessible;\nusing VarInt = Au.Types.Api.VarInt;\nusing NAVDIR = Au.Types.Api.NAVDIR;\n\nnamespace Au.Controls;\n\n//public //if non-public, GetIDispatchForObject throws, and with GetIUnknownForObject does not work too.\n//\tSee https://learn.microsoft.com/en-us/dotnet/standard/native-interop/qualify-net-types-for-interoperation.\n//\tBut somehow works if implements IReflect, even if all functions just return default. Winforms use it.\n//\tNow with Cpp_AccWorkaround works without all that.\nabstract class HwndHostAccessibleBase_ : IAccessible, IDisposable/*, IReflect*/\n{\n\tFrameworkElement _e;\n\twnd _w;\n\t\n\t/// <param name=\"e\">HwndHost or its container control (if the HwndHost is part of the control).</param>\n\t/// <param name=\"w\">Native control hosted by the HwndHost.</param>\n\tpublic HwndHostAccessibleBase_(FrameworkElement e, wnd w) {\n\t\t_e = e;\n\t\t_w = w;\n\t}\n\t\n\tIAccessible _StdAO {\n\t\tget {\n\t\t\tif (_stdAO == null) Api.CreateStdAccessibleObject(_w, EObjid.CLIENT, typeof(IAccessible).GUID, out _stdAO);\n\t\t\treturn _stdAO;\n\t\t\t//note: not in ctor. It is called om WM_GETOBJECT, and CreateStdAccessibleObject sends WM_GETOBJECT too when called there (but not when called later).\n\t\t}\n\t}\n\tIAccessible _stdAO;\n\t\n\tpublic void Dispose() {\n\t\tif (_stdAO != null) {\n\t\t\tMarshal.ReleaseComObject(_stdAO);\n\t\t\t_stdAO = null;\n\t\t}\n\t}\n\t\n\t#region IAccessible\n\t\n\tIAccessible IAccessible.get_accParent() => _StdAO?.get_accParent();\n\t\n\t//Once was exception on process exit, while debugging. _StdAO returned null. Cannot reproduce. Replaced `.` with `?.` everywhere.\n\t//IAccessible IAccessible.get_accParent() {\n\t//\tprint.qm2.write(\"get_accParent\");\n\t//\tprint.qm2.write(_w);\n\t//\tprint.qm2.write(_w.Get.DirectParent);\n\t//\tprint.qm2.write(new StackTrace(true));\n\t//\tif (_StdAO is { } k) return k.get_accParent();\n\t//\treturn null;\n\t//}\n\t\n\t/// <summary>\n\t/// Returns 0.\n\t/// </summary>\n\tpublic virtual int ChildCount => 0;\n\tint IAccessible.get_accChildCount() => ChildCount;\n\t\n\t//[PreserveSig]\n\tint IAccessible.get_accChild(VarInt varChild, [MarshalAs(UnmanagedType.IDispatch)] out object ppdispChild) {\n\t\tppdispChild = null;\n\t\treturn 1;\n\t\t//currently this class supports only simple element children.\n\t}\n\t\n\t/// <summary>\n\t/// Returns FrameworkElement.Name.\n\t/// </summary>\n\tpublic virtual string Name(int child) => _e.Name;\n\tstring IAccessible.get_accName(VarInt varChild) => Name(varChild);\n\t\n\t/// <summary>\n\t/// Returns null.\n\t/// </summary>\n\tpublic virtual string Value(int child) => null;\n\tstring IAccessible.get_accValue(VarInt varChild) => Value(varChild);\n\t\n\t/// <summary>\n\t/// Returns null.\n\t/// </summary>\n\tpublic virtual string Description(int child) => null;\n\tstring IAccessible.get_accDescription(VarInt varChild) => Description(varChild);\n\t\n\tpublic abstract ERole Role(int child);\n\tVarInt IAccessible.get_accRole(VarInt varChild) => (int)Role(varChild) - 1;\n\t\n\t/// <summary>\n\t/// If self (child -1), returns combination of FOCUSABLE, FOCUSED, DISABLED, INVISIBLE. Else returns 0.\n\t/// </summary>\n\t/// <param name=\"child\"></param>\n\t/// <returns></returns>\n\tpublic virtual EState State(int child) {\n\t\tif (child != -1) return 0;\n\t\tEState r = 0;\n\t\tif (_e.Focusable) {\n\t\t\tr |= EState.FOCUSABLE;\n\t\t\tif (wnd.thisThread.isFocused(_w) || _e.IsKeyboardFocused) r |= EState.FOCUSED;\n\t\t}\n\t\tif (!_e.IsEnabled) r |= EState.DISABLED;\n\t\tif (!_e.IsVisible) r |= EState.INVISIBLE;\n\t\treturn r;\n\t}\n\tVarInt IAccessible.get_accState(VarInt varChild) => (int)State(varChild) - 1;\n\t\n\t/// <summary>\n\t/// Returns null.\n\t/// </summary>\n\tpublic virtual string Help(int child) => null;\n\tstring IAccessible.get_accHelp(VarInt varChild) => Help(varChild);\n\t\n\tint IAccessible.get_accHelpTopic(out string pszHelpFile, VarInt varChild) => throw new NotImplementedException();\n\t\n\t/// <summary>\n\t/// Returns null.\n\t/// </summary>\n\tpublic virtual string KeyboardShortcut(int child) => null;\n\tstring IAccessible.get_accKeyboardShortcut(VarInt varChild) => KeyboardShortcut(varChild);\n\t\n\t/// <summary>\n\t/// Returns -1.\n\t/// Called only if the control is focused.\n\t/// </summary>\n\tpublic virtual int FocusedChild => -1;\n\tobject IAccessible.get_accFocus() => wnd.thisThread.isFocused(_w) ? FocusedChild + 1 : null;\n\t//object IAccessible.get_accFocus() {\n\t//\tprint.it(\"get_accFocus\", wnd.thisThread.isFocused(_w));//TODO3. Now this not called for KTreeView. The native control normally is never focused. IAccessible.get_accFocus called by QM2 returns unknown error 0x80131509.\n\t//\treturn wnd.thisThread.isFocused(_w) ? FocusedChild + 1 : null;\n\t//}\n\t\n\t/// <summary>\n\t/// Returns null.\n\t/// </summary>\n\tpublic virtual List<int> SelectedChildren => null;\n\tobject IAccessible.get_accSelection() {\n\t\tvar a = SelectedChildren;\n\t\tif (a.NE_()) return null;\n\t\tif (a.Count == 1) return a[0] + 1;\n\t\treturn new _EnumSelected(a);\n\t}\n\t\n\tunsafe class _EnumSelected : IEnumVarInt {\n\t\tList<int> _a;\n\t\tint _i;\n\t\tpublic _EnumSelected(List<int> a) { _a = a; }\n\t\t\n\t\tpublic int Next(int celt, VarInt* rgVar, int* pCeltFetched) {\n\t\t\tint n = Math.Min(celt, _a.Count - _i);\n\t\t\tif (pCeltFetched != null) *pCeltFetched = n;\n\t\t\tfor (int i = 0; i < n;) rgVar[i++] = _a[_i++];\n\t\t\treturn n == celt ? 0 : 1;\n\t\t}\n\t\t\n\t\tpublic int Skip(int celt) {\n\t\t\tlong i = (long)_i + (uint)celt;\n\t\t\tif (i > _a.Count) return 1;\n\t\t\t_i = (int)i;\n\t\t\treturn 0;\n\t\t}\n\t\t\n\t\tpublic void Reset() { _i = 0; }\n\t\t\n\t\tpublic IEnumVarInt Clone() => throw new NotImplementedException();\n\t}\n\t\n\t[ComImport, Guid(\"00020404-0000-0000-C000-000000000046\"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\n\tinternal unsafe interface IEnumVarInt {\n\t\t[PreserveSig] int Next(int celt, VarInt* rgVar, int* pCeltFetched);\n\t\t[PreserveSig] int Skip(int celt);\n\t\tvoid Reset();\n\t\tIEnumVarInt Clone();\n\t}\n\t\n\t/// <summary>\n\t/// Returns null.\n\t/// </summary>\n\tpublic virtual string DefaultAction(int child) => null;\n\tstring IAccessible.get_accDefaultAction(VarInt varChild) => DefaultAction(varChild);\n\t\n\t/// <summary>\n\t/// Does nothing.\n\t/// Not called for self.\n\t/// </summary>\n\tpublic virtual void SelectChild(ESelect flagsSelect, int child) { }\n\tvoid IAccessible.accSelect(ESelect flagsSelect, VarInt varChild) {\n\t\tint child = varChild;\n\t\tif (flagsSelect.Has(ESelect.TAKEFOCUS)) Api.SetFocus(_w); // _e.Focus();//TODO3: now Api.SetFocus makes KTreeView item nonfocused (works like focused but displayed like not)\n\t\tif (child == -1) {\n\t\t\tif (flagsSelect is not (ESelect.TAKEFOCUS or 0)) throw new ArgumentException();\n\t\t} else {\n\t\t\tSelectChild(flagsSelect, child);\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Returns default.\n\t/// Return child rect in client area.\n\t/// Not called for self.\n\t/// </summary>\n\tpublic virtual RECT ChildRect(int child) => default;\n\tvoid IAccessible.accLocation(out int pxLeft, out int pyTop, out int pcxWidth, out int pcyHeight, VarInt varChild) {\n\t\tint child = varChild;\n\t\tif (child == -1) {\n\t\t\tpxLeft = pyTop = pcxWidth = pcyHeight = 0;\n\t\t\t_StdAO?.accLocation(out pxLeft, out pyTop, out pcxWidth, out pcyHeight, varChild);\n\t\t} else {\n\t\t\tvar r = ChildRect(child);\n\t\t\t_w.MapClientToScreen(ref r);\n\t\t\tpxLeft = r.left; pyTop = r.top; pcxWidth = r.Width; pcyHeight = r.Height;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Returns null.\n\t/// If self (childStart==-1), navDir is FIRSTCHILD or LASTCHILD, else navDir is any except these.\n\t/// </summary>\n\tpublic virtual int? Navigate(NAVDIR navDir, int childStart) => null;\n\tobject IAccessible.accNavigate(NAVDIR navDir, VarInt varStart) {\n\t\tint i = varStart;\n\t\tif (navDir == NAVDIR.FIRSTCHILD || navDir == NAVDIR.LASTCHILD) {\n\t\t\tif (i != -1) return null;\n\t\t} else {\n\t\t\tif (i == -1) return _StdAO?.accNavigate(navDir, varStart); //never mind: gets some not adjacent UI element\n\t\t}\n\t\tvar v = Navigate(navDir, i);\n\t\tif (v == null) return null;\n\t\treturn v.Value + 1;\n\t}\n\t\n\t/// <summary>\n\t/// Returns -1.\n\t/// x y are in client area.\n\t/// Not called if not in client area.\n\t/// </summary>\n\tpublic virtual int HitTest(int x, int y) => -1;\n\tVarInt IAccessible.accHitTest(int xLeft, int yTop) {\n\t\tPOINT p = (xLeft, yTop); _w.MapScreenToClient(ref p);\n\t\tif (_w.ClientRect.Contains(p)) return HitTest(p.x, p.y);\n\t\tif (_StdAO is { } k) return k.accHitTest(xLeft, yTop);\n\t\treturn default;\n\t}\n\t\n\t/// <summary>\n\t/// Does nothing.\n\t/// </summary>\n\tpublic virtual void DoDefaultAction(int child) { }\n\tvoid IAccessible.accDoDefaultAction(VarInt varChild) => DoDefaultAction(varChild);\n\t\n\tvoid IAccessible.put_accName(VarInt varChild, string szName) { }\n\t\n\tvoid IAccessible.put_accValue(VarInt varChild, string szValue) { }\n\t\n\t#endregion\n\t\n\t\n\t//#region IReflect\n\t\n\t//FieldInfo IReflect.GetField(string name, BindingFlags bindingAttr) => null;\n\t//FieldInfo[] IReflect.GetFields(BindingFlags bindingAttr) => null;\n\t//MemberInfo[] IReflect.GetMember(string name, BindingFlags bindingAttr) => null;\n\t//MemberInfo[] IReflect.GetMembers(BindingFlags bindingAttr) => null;\n\t//MethodInfo IReflect.GetMethod(string name, BindingFlags bindingAttr) => null;\n\t//MethodInfo IReflect.GetMethod(string name, BindingFlags bindingAttr, Binder binder, Type[] types, ParameterModifier[] modifiers) => null;\n\t//MethodInfo[] IReflect.GetMethods(BindingFlags bindingAttr) => null;\n\t//PropertyInfo[] IReflect.GetProperties(BindingFlags bindingAttr) => null;\n\t//PropertyInfo IReflect.GetProperty(string name, BindingFlags bindingAttr) => null;\n\t//PropertyInfo IReflect.GetProperty(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers) => null;\n\t//object IReflect.InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, ParameterModifier[] modifiers, System.Globalization.CultureInfo culture, string[] namedParameters) => null;\n\t//Type IReflect.UnderlyingSystemType => null;\n\t\n\t//#endregion\n\t\n\t/// <summary>\n\t/// Call in hook wndproc on WM_GETOBJECT like this: <c>handled = true; return (_acc ??= new _Accessible(this)).WmGetobject(wParam, lParam);</c>.\n\t/// If lParam is EObjid.CLIENT, calls API LresultFromObject(this), else calls API DefWindowProc.\n\t/// </summary>\n\tpublic nint WmGetobject(nint wParam, nint lParam) {\n\t\tvar oid = (EObjid)lParam;\n\t\t//print.it(oid);\n\t\tif (oid != EObjid.CLIENT) return Api.DefWindowProc(_w, Api.WM_GETOBJECT, wParam, lParam);\n\t\t\n\t\treturn Cpp.Cpp_AccWorkaround(this, wParam, ref _accWorkaround);\n\t\t\n\t\t//cannot use this because of .NET bug: then calls our IAccessible implementation methods in other thread.\n\t\t//var accIP = Marshal.GetIUnknownForObject(this);\n\t\t////var accIP=Marshal.GetIDispatchForObject(this);\n\t\t//var r = Api.LresultFromObject(typeof(IAccessible).GUID, wParam, accIP);\n\t\t////Marshal.AddRef(accIP); print.it(Marshal.Release(accIP));\n\t\t//Marshal.Release(accIP);\n\t\t//return r;\n\t}\n\tnint _accWorkaround;\n\t\n\t~HwndHostAccessibleBase_() { if (_accWorkaround != 0) Cpp.Cpp_AccWorkaround(null, 0, ref _accWorkaround); }\n\t\n}\n"
  },
  {
    "path": "Au.Controls/Util, Api/KApi.cs",
    "content": "﻿namespace Au.Controls;\n\n[DebuggerStepThrough]\nstatic unsafe class KApi {\n\t\n\t[DllImport(\"user32.dll\")]\n\tinternal static extern bool DrawFrameControl(IntPtr hdc, in RECT r, int type, int state);\n\t\n\t\n}\n\n"
  },
  {
    "path": "Au.Controls/Util, Api/KExtWpf.cs",
    "content": "using System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Controls.Primitives;\nusing System.Windows.Data;\nusing System.Windows.Input;\n\nnamespace Au.Controls;\n\n/// <summary>\n/// Extension methods for dialogs.\n/// </summary>\npublic static class KExtWpf {\n\t/// <summary>\n\t/// Adds KCheckBox, CheckBox or RadioButton that can be used with TextBox in a propertygrid row. Or alone in a grid or stack row.\n\t/// </summary>\n\t/// <param name=\"b\"></param>\n\t/// <param name=\"c\"></param>\n\t/// <param name=\"name\">Checkbox text.</param>\n\t/// <param name=\"noR\">Don't add new row.</param>\n\t/// <param name=\"check\">Checkbox state.</param>\n\tpublic static wpfBuilder xAddCheck<T>(this wpfBuilder b, out T c, string name, bool noR = false, bool check = false) where T : ToggleButton, new() {\n\t\tif (!noR && b.Panel is Grid) b.Row(0);\n\t\tb.Add<T>(out c, name).Height(18);\n\t\tif (check) b.Checked();\n\t\treturn b;\n\t}\n\t\n\t/// <summary>\n\t/// Adds KTextBox that can be used with KCheckBox in a propertygrid row. Or alone in a grid or stack row.\n\t/// Sets multiline with limited height. If in grid, sets padding/margin for propertygrid.\n\t/// </summary>\n\tpublic static wpfBuilder xAddText<TTBox>(this wpfBuilder b, out TTBox t, string text = null) where TTBox : KTextBox, new() {\n\t\tb.Add(out t, text).Multiline(..55, wrap: TextWrapping.NoWrap);\n\t\tif (b.Panel is Grid) b.Padding(new Thickness(0, 0, 0, 1)).Margin(left: 4);\n\t\treturn b;\n\t}\n\t\n\t/// <summary>\n\t/// Adds KCheckBox (<see cref=\"xAddCheck\"/>) and multiline KTextBox (<see cref=\"xAddText\"/>) in a propertygrid row.\n\t/// </summary>\n\t/// <param name=\"b\"></param>\n\t/// <param name=\"name\">Checkbox text.</param>\n\t/// <param name=\"text\">Textbox text.</param>\n\t/// <param name=\"noR\">Don't add new row.</param>\n\t/// <param name=\"check\">Checkbox state.</param>\n\tpublic static KCheckTextBox xAddCheckText<TTBox>(this wpfBuilder b, string name, string text = null, bool noR = false, bool check = false) where TTBox : KTextBox, new() {\n\t\txAddCheck(b, out KCheckBox c, name, noR, check);\n\t\tvar m = c.Margin; if (m.Top <= 1) c.Margin = m with { Top = m.Top + 1 };\n\t\txAddText(b, out TTBox t, text);\n\t\treturn new(c, t);\n\t}\n\t\n\t/// <inheritdoc cref=\"xAddCheckText{TTBox}(wpfBuilder, string, string, bool, bool)\"/>\n\tpublic static KCheckTextBox xAddCheckText(this wpfBuilder b, string name, string text = null, bool noR = false, bool check = false)\n\t\t=> xAddCheckText<KTextBox>(b, name, text, noR, check);\n\t\n\t/// <summary>\n\t/// Adds KCheckBox (<see cref=\"xAddCheck\"/>) and multiline KTextBox (<see cref=\"xAddText\"/>) in a propertygrid row.\n\t/// Also adds ▾ button that shows a drop-down list (see <see cref=\"KCheckTextBox.Set(bool, string, List{string})\"/>).\n\t/// Unlike ComboBox, text can be multiline and isn't selected when receives focus.\n\t/// </summary>\n\t/// <param name=\"b\"></param>\n\t/// <param name=\"name\">Checkbox text.</param>\n\t/// <param name=\"text\">Textbox text.</param>\n\t/// <param name=\"check\">Checkbox state.</param>\n\tpublic static KCheckTextBox xAddCheckTextDropdown<TTBox>(this wpfBuilder b, string name, string text = null, bool check = false) where TTBox : KTextBox, new() {\n\t\txAddCheck(b, out KCheckBox c, name, check: check);\n\t\txAddText(b, out TTBox t, text);\n\t\tb.And(14).Add(out Button k, \"▾\").Padding(new Thickness(0)).Border(); //tested: ok on Win7\n\t\tk.Width += 4;\n\t\treturn new(c, t, k);\n\t}\n\t\n\t/// <inheritdoc cref=\"xAddCheckTextDropdown{TTBox}(wpfBuilder, string, string, bool)\"/>\n\tpublic static KCheckTextBox xAddCheckTextDropdown(this wpfBuilder b, string name, string text = null, bool check = false)\n\t\t=> xAddCheckTextDropdown<KTextBox>(b, name, text, check);\n\t\n\t/// <summary>\n\t/// Adds KCheckBox (<see cref=\"xAddCheck\"/>) and readonly ComboBox (<see cref=\"xAddOther\"/>) in a propertygrid row.\n\t/// </summary>\n\t/// <param name=\"b\"></param>\n\t/// <param name=\"name\">Checkbox text.</param>\n\t/// <param name=\"items\">Combobox items like \"One|Two\".</param>\n\t/// <param name=\"index\">Combobox selected index.</param>\n\tpublic static KCheckComboBox xAddCheckCombo(this wpfBuilder b, string name, string items, int index = 0) {\n\t\txAddCheck(b, out KCheckBox c, name);\n\t\txAddOther(b, out ComboBox t);\n\t\tif (!items.NE()) {\n\t\t\tb.Items(items);\n\t\t\tif (index != 0) t.SelectedIndex = index;\n\t\t}\n\t\treturn new(c, t);\n\t}\n\t\n\t/// <summary>\n\t/// Adds any control that can be used in a propertygrid row.\n\t/// </summary>\n\tpublic static wpfBuilder xAddOther<T>(this wpfBuilder b, out T other, string text = null, string label = null) where T : FrameworkElement, new() {\n\t\tif (label != null) b.xAddOther(out TextBlock _, label);\n\t\tb.Add(out other, text);\n\t\t_xSetOther(b, other);\n\t\treturn b;\n\t}\n\t\n\tstatic void _xSetOther(wpfBuilder b, FrameworkElement e) {\n\t\tb.Height(19).Margin(left: 4);\n\t\tif (e is Control) b.Padding(e is ComboBox ? new Thickness(4, 1, 4, 0) : new Thickness(4, 0, 4, 0)); //tested with Button and ComboBox\n\t}\n\t\n\t/// <summary>\n\t/// Adds button that can be used in a propertygrid row.\n\t/// </summary>\n\tpublic static wpfBuilder xAddButton(this wpfBuilder b, out Button button, string text, Action<WBButtonClickArgs> click) {\n\t\tb.AddButton(out button, text, click);\n\t\t_xSetOther(b, button);\n\t\treturn b;\n\t}\n\t\n\t/// <summary>\n\t/// Adds button that can be used in a propertygrid row.\n\t/// </summary>\n\tpublic static wpfBuilder xAddButton(this wpfBuilder b, string text, Action<WBButtonClickArgs> click) => xAddButton(b, out _, text, click);\n\t\n\t/// <summary>\n\t/// Adds KCheckBox (<see cref=\"xAddCheck\"/>) and other control (<see cref=\"xAddOther\"/>) in a propertygrid row.\n\t/// </summary>\n\t/// <param name=\"name\">Checkbox text.</param>\n\t/// <param name=\"text\">Text of other control.</param>\n\tpublic static wpfBuilder xAddCheckAnd<T>(this wpfBuilder b, out KCheckBox r, string name, out T other, string text = null) where T : FrameworkElement, new() {\n\t\txAddCheck(b, out r, name);\n\t\txAddOther(b, out other, text);\n\t\treturn b;\n\t}\n\t\n\t/// <summary>\n\t/// Adds Border with standard thickness/color and an element in it.\n\t/// </summary>\n\tpublic static Border xAddInBorder<T>(this wpfBuilder b, out T var, string margin = null, Thickness? thickness = null) where T : FrameworkElement, new() {\n\t\tb.Add(out Border c).Border(thickness2: thickness);\n\t\tif (margin != null) b.Margin(margin);\n\t\tb.Child().Add(out var);\n\t\treturn c;\n\t}\n\t\n\t/// <summary>\n\t/// Adds Border with standard thickness/color and an element in it.\n\t/// </summary>\n\tpublic static Border xAddInBorder(this wpfBuilder b, FrameworkElement e, string margin = null, Thickness? thickness = null) {\n\t\tb.Add(out Border c).Border(thickness2: thickness);\n\t\tif (margin != null) b.Margin(margin);\n\t\tb.Child().Add(e);\n\t\treturn c;\n\t}\n\t\n\t/// <summary>\n\t/// Adds KCheckBox with icon like in a toolbar.\n\t/// </summary>\n\tpublic static wpfBuilder xAddCheckIcon(this wpfBuilder b, out KCheckBox r, string icon, string tooltip, Action checkChanged = null) {\n\t\tb.Add(out r, ImageUtil.LoadWpfImageElement(icon)).Tooltip(tooltip);\n\t\tr.Style = b.Panel.FindResource(ToolBar.CheckBoxStyleKey) as Style;\n\t\tr.Focusable = false;\n\t\tif (checkChanged != null) r.CheckChanged += (_, _) => checkChanged();\n\t\t//var p = (r.Content as Viewbox).Child as System.Windows.Shapes.Path;\n\t\t//r.Checked += (_, _) => p.Fill.Opacity = 1;\n\t\t//r.Unchecked += (_, _) => p.Fill.Opacity = 0.3;\n\t\treturn b;\n\t}\n\t\n\t/// <summary>\n\t/// Adds Button with icon like in a toolbar.\n\t/// </summary>\n\tpublic static wpfBuilder xAddButtonIcon(this wpfBuilder b, out Button r, string icon, Action<WBButtonClickArgs> click, string tooltip) {\n\t\tb.AddButton(out r, ImageUtil.LoadWpfImageElement(icon), click).Tooltip(tooltip);\n\t\tr.Style = b.Panel.FindResource(ToolBar.ButtonStyleKey) as Style;\n\t\tr.Focusable = false;\n\t\treturn b;\n\t}\n\t\n\t/// <summary>\n\t/// Adds Button with icon like in a toolbar.\n\t/// </summary>\n\tpublic static wpfBuilder xAddButtonIcon(this wpfBuilder b, string icon, Action<WBButtonClickArgs> click, string tooltip)\n\t\t=> xAddButtonIcon(b, out _, icon, click, tooltip);\n\t\n\t/// <summary>\n\t/// Adds <b>Grid</b> with two horizontal separators and <b>TextBlock</b>.\n\t/// Looks almost like <see cref=\"KGroupBoxSeparator\"/>, but is not inside a GroupBox+Panel.\n\t/// </summary>\n\t/// <param name=\"text\">String (to add new <b>TextBlock</b>) or <b>FrameworkElement</b> (probably <b>TextBlock</b>).</param>\n\tpublic static wpfBuilder xAddGroupSeparator(this wpfBuilder b, object text, bool center = false) {\n\t\tb.StartGrid().Columns(center ? -1 : 10, 0, -1);\n\t\tb.AddSeparator(vertical: false).Margin(right: 0);\n\t\tDebug.Assert(text is string or FrameworkElement);\n\t\tif (text is string s) {\n\t\t\tb.Add<TextBlock>(s).Margin(left: 3, right: 4).Font(bold: true);\n\t\t} else if (text is FrameworkElement e) {\n\t\t\tb.Add(e);\n\t\t}\n\t\tb.AddSeparator(vertical: false).Margin(left: 0);\n\t\tb.End();\n\t\treturn b;\n\t}\n\t\n\tpublic static ToolBar xAddToolBar(this wpfBuilder t, bool vertical = false, bool hideOverflow = false, bool controlBrush = false) {\n\t\tvar tt = new ToolBarTray { IsLocked = true };\n\t\tif (vertical) tt.Orientation = Orientation.Vertical;\n\t\tvar tb = new ToolBar();\n\t\tif (controlBrush) {\n\t\t\ttt.Background = SystemColors.ControlBrush;\n\t\t\ttb.Background = SystemColors.ControlBrush;\n\t\t}\n\t\tKeyboardNavigation.SetTabNavigation(tb, KeyboardNavigationMode.Once);\n\t\ttt.ToolBars.Add(tb);\n\t\tif (hideOverflow) tb.HideGripAndOverflow(false);\n\t\tt.Add(tt);\n\t\treturn tb;\n\t}\n\t\n\t/// <summary>\n\t/// Adds a toolbar button with icon and tooltip.\n\t/// </summary>\n\t/// <param name=\"click\">Can be null.</param>\n\t/// <param name=\"padding\">Set <c>Padding = new(4, 2, 4, 2)</c>.</param>\n\tpublic static Button AddButton(this ToolBar t, string icon, Action<Button> click, string tooltip, bool enabled = true, bool padding = true) {\n\t\tvar c = new Button { Content = ImageUtil.LoadWpfImageElement(icon), ToolTip = tooltip };\n\t\tif (click != null) c.Click += (_, _) => click(c);\n\t\tif (padding) c.Padding = new(4, 2, 4, 2);\n\t\tif (!enabled) c.IsEnabled = false;\n\t\tt.Items.Add(c);\n\t\treturn c;\n\t}\n\t\n\t/// <summary>\n\t/// Adds a toolbar checkbox with icon and tooltip.\n\t/// </summary>\n\t/// <param name=\"padding\">Set <c>Padding = new(4, 2, 4, 2)</c>.</param>\n\tpublic static KCheckBox AddCheckbox(this ToolBar t, string icon, string tooltip, bool enabled = true, bool padding = true) {\n\t\tvar c = new KCheckBox { Content = ImageUtil.LoadWpfImageElement(icon), ToolTip = tooltip };\n\t\tc.Style = t.FindResource(ToolBar.CheckBoxStyleKey) as Style; //need because this is KCheckBox, not CheckBox\n\t\tif (padding) c.Padding = new(4, 2, 4, 2);\n\t\tif (!enabled) c.IsEnabled = false;\n\t\tt.Items.Add(c);\n\t\treturn c;\n\t}\n\t\n\t/// <summary>\n\t/// Adds <b>MouseRightButtonDown</b> event handler which shows a context menu.\n\t/// </summary>\n\t/// <param name=\"t\"></param>\n\t/// <param name=\"fill\">Let it fill the menu.</param>\n\tpublic static void xContextMenu(this ButtonBase t, Action<popupMenu> fill) {\n\t\tt.MouseRightButtonDown += (_, _) => {\n\t\t\tvar m = new popupMenu();\n\t\t\tfill(m);\n\t\t\tm.Show(owner: t);\n\t\t\t//var r = t.RectInScreen();\n\t\t\t//m.Show(xy: new(r.left, r.bottom), excludeRect: r, owner: t);\n\t\t};\n\t}\n\t\n\t/// <summary>\n\t/// Adds <b>Click</b> event handler which shows a drop-down menu.\n\t/// </summary>\n\t/// <param name=\"t\"></param>\n\t/// <param name=\"fill\">Let it fill the menu.</param>\n\tpublic static void xDropdownMenu(this ButtonBase t, Action<popupMenu> fill) {\n\t\tt.Click += (_, _) => {\n\t\t\tvar m = new popupMenu();\n\t\t\tfill(m);\n\t\t\tvar r = t.RectInScreen();\n\t\t\tm.Show(xy: new(r.left, r.bottom), excludeRect: r, owner: t);\n\t\t};\n\t}\n\t\n\t/// <summary>\n\t/// Adds ScrollViewer, adds 2-column grid or vertical stack panel in it (StartGrid, StartStack), and calls <c>Options(modifyPadding: false, margin: new(1))</c>.\n\t/// </summary>\n\tpublic static ScrollViewer xStartPropertyGrid(this wpfBuilder b, string margin = null, bool stack = false) {\n\t\tb.Add(out ScrollViewer v);\n\t\tif (margin != null) b.Margin(margin);\n\t\tv.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;\n\t\tv.FocusVisualStyle = null;\n\t\tif (stack) b.StartStack(vertical: true, childOfLast: true); else b.StartGrid(childOfLast: true);\n\t\tb.Options(modifyPadding: false, margin: new(1));\n\t\treturn v;\n\t}\n\t\n\t/// <summary>\n\t/// Ends grid/stack set by <see cref=\"xStartPropertyGrid\"/> and restores options.\n\t/// </summary>\n\t/// <param name=\"b\"></param>\n\tpublic static void xEndPropertyGrid(this wpfBuilder b) {\n\t\tb.Options(modifyPadding: true, margin: new Thickness(3));\n\t\tb.End();\n\t}\n\t\n\t/// <summary>\n\t/// Sets header control properties: center, bold.\n\t/// It can be Label, TextBlock or CheckBox. Not tested others.\n\t/// </summary>\n\tpublic static void xSetHeaderProp(this wpfBuilder b) {\n\t\t//b.Font(bold: true).Brush(foreground: SystemColors.ControlDarkDarkBrush).Align(\"C\");\n\t\tb.Font(bold: true).Align(\"C\");\n\t}\n\t//public static void xSetHeaderProp(this wpfBuilder b, bool vertical = false) {\n\t//\tb.Font(bold: true).Brush(foreground: SystemColors.ControlDarkDarkBrush);\n\t//\tif (vertical) {\n\t//\t\tb.Align(y: \"C\");\n\t//\t\tb.Last.LayoutTransform = new RotateTransform(270d);\n\t//\t} else {\n\t//\t\tb.Align(\"C\");\n\t//\t}\n\t//}\n\t\n\t/// <summary>\n\t/// Adds vertical splitter.\n\t/// </summary>\n\tpublic static void xAddSplitterV(this wpfBuilder b, int span = 1, double thickness = 4) {\n\t\tb.Add<GridSplitter2>().Splitter(true, span, thickness);\n\t}\n\t\n\t/// <summary>\n\t/// Adds horizontal splitter.\n\t/// </summary>\n\tpublic static void xAddSplitterH(this wpfBuilder b, int span = 1, double thickness = 4) {\n\t\tb.R.Add<GridSplitter2>().Splitter(false, span, thickness);\n\t}\n\t\n\t/// <summary>\n\t/// Adds <b>TextBlock</b> with green background, wrapping and some padding, and calls <see cref=\"wpfBuilder.FormatText\"/>.\n\t/// </summary>\n\t/// <inheritdoc cref=\"wpfBuilder.FormatText(wpfBuilder.InterpolatedString)\" path=\"/param\"/>\n\tpublic static wpfBuilder xAddInfoBlockF(this wpfBuilder t, wpfBuilder.InterpolatedString text, bool scrollViewer = false) {\n\t\tvar r = t.xAddInfoBlockT(null, scrollViewer);\n\t\tt.FormatText(text);\n\t\treturn r;\n\t}\n\t\n\t/// <summary>\n\t/// Adds <b>TextBlock</b> with green background, wrapping and some padding.\n\t/// </summary>\n\tpublic static wpfBuilder xAddInfoBlockT(this wpfBuilder t, string text, bool scrollViewer = false) //not overload. Somehow then it is used with $\"string\" too.\n\t\t=> xAddInfoBlockT(t, out _, text, scrollViewer);\n\t\n\t/// <summary>\n\t/// Adds <b>TextBlock</b> with green background, wrapping and some padding.\n\t/// </summary>\n\tpublic static wpfBuilder xAddInfoBlockT(this wpfBuilder t, out TextBlock r, string text = null, bool scrollViewer = false) {\n\t\tif (scrollViewer) t.Add(new ScrollViewer { VerticalScrollBarVisibility = ScrollBarVisibility.Auto }).Child();\n\t\treturn t.Add(out r, text).Wrap().Brush(WpfUtil_.IsHighContrastDark ? 0x2E4D00 : 0xf8fff0).Padding(2, 1, 2, 2);\n\t}\n\t\n\t/// <summary>\n\t/// Adds icon-button \"Help\" for a control.\n\t/// </summary>\n\tpublic static wpfBuilder xAddControlHelpButton(this wpfBuilder t, Action<WBButtonClickArgs> click, string tooltip = null) {\n\t\treturn xAddButtonIcon(t, \"*Material.HelpCircleOutline #4080FF|#99CCFF\", click, tooltip).Margin(0);\n\t}\n\t\n\t/// <summary>\n\t/// Adds icon-button \"Help\" for a control. On click calls <see cref=\"HelpUtil.AuHelp\"/> with <i>helpTopic</i>.\n\t/// </summary>\n\tpublic static wpfBuilder xAddControlHelpButton(this wpfBuilder t, string helpTopic, string tooltip = null)\n\t\t=> xAddControlHelpButton(t, _ => HelpUtil.AuHelp(helpTopic), tooltip);\n\t\n\t/// <summary>\n\t/// Adds icon-button \"Help\" for a dialog window. On click or F1 calls <i>clickF1</i>.\n\t/// </summary>\n\tpublic static wpfBuilder xAddDialogHelpButtonAndF1(this wpfBuilder t, Action clickF1) {\n\t\tt.Window.PreviewKeyDown += (_, e) => {\n\t\t\tif (e.Key == Key.F1 && Keyboard.Modifiers == 0) {\n\t\t\t\tclickF1();\n\t\t\t\te.Handled = true;\n\t\t\t}\n\t\t};\n\t\treturn xAddButtonIcon(t, \"*Material.HelpBox #4080FF|#99CCFF\", _ => clickF1(), \"Help (F1)\").Width(24).Margin(20);\n\t}\n\t\n\t/// <summary>\n\t/// Adds icon-button \"Help\" for a dialog window. On click or F1 calls <see cref=\"HelpUtil.AuHelp\"/> with <i>helpTopic</i>.\n\t/// </summary>\n\tpublic static wpfBuilder xAddDialogHelpButtonAndF1(this wpfBuilder t, string helpTopic)\n\t\t=> xAddDialogHelpButtonAndF1(t, () => HelpUtil.AuHelp(helpTopic));\n\t\n\t/// <summary>\n\t/// Can be used like <see cref=\"wpfBuilder.Validation\"/> with hotkey <b>TextBox</b> controls.\n\t/// </summary>\n\tpublic static wpfBuilder xValidateHotkey(this wpfBuilder b, bool errorIfEmpty = false) {\n\t\treturn b.Validation(e => {\n\t\t\tvar s = (e as TextBox).Text;\n\t\t\tif (!errorIfEmpty && s.NE()) return null;\n\t\t\tif (keys.more.parseHotkeyString(s, out _, out _)) return null;\n\t\t\treturn \"Invalid hotkey\";\n\t\t});\n\t}\n\t\n\t/// <summary>\n\t/// Sets binding to show/hide the last added element when the specified <b>CheckBox</b> checked/unchecked.\n\t/// </summary>\n\tpublic static wpfBuilder xBindCheckedVisible(this wpfBuilder t, CheckBox c, bool setLabeledBy = false) {\n\t\tt.Bind(FrameworkElement.VisibilityProperty, new Binding(\"IsChecked\") { Source = c, Converter = s_bvc ??= new() });\n\t\tif (setLabeledBy) {\n\t\t\tDebug.Assert(!(t.Last is ButtonBase or Label or TextBlock));\n\t\t\tSystem.Windows.Automation.AutomationProperties.SetLabeledBy(t.Last, c);\n\t\t}\n\t\treturn t;\n\t}\n\tstatic BooleanToVisibilityConverter s_bvc;\n\t\n\t/// <summary>\n\t/// Sets binding to enable/disable the last added element when the specified <b>CheckBox</b> checked/unchecked.\n\t/// </summary>\n\tpublic static wpfBuilder xBindCheckedEnabled(this wpfBuilder t, CheckBox c, bool setLabeledBy = false) {\n\t\tt.Bind(FrameworkElement.IsEnabledProperty, new Binding(\"IsChecked\") { Source = c });\n\t\tif (setLabeledBy) {\n\t\t\tDebug.Assert(!(t.Last is ButtonBase or Label or TextBlock));\n\t\t\tSystem.Windows.Automation.AutomationProperties.SetLabeledBy(t.Last, c);\n\t\t}\n\t\treturn t;\n\t}\n\t\n\t/// <summary>\n\t/// Calls <b>DockPanel.SetDock</b> and <c>t.Children.Add</c>.\n\t/// </summary>\n\tpublic static void xAddDocked(this DockPanel t, UIElement e, Dock dock) {\n\t\tDockPanel.SetDock(e, dock);\n\t\tt.Children.Add(e);\n\t}\n\t\n\t/// <summary>\n\t/// Adds standard <c>Cut Copy Paste</c> items to the <c>ContextMenu</c> of this <c>TextBox</c>.\n\t/// On <c>ContextMenuOpening</c> enables/disables items depending on the state of the <c>TextBox</c> control and clipboard.\n\t/// Creates/sets new context menu if it's <c>null</c>.\n\t/// </summary>\n\t/// <returns>The context menu.</returns>\n\tpublic static ContextMenu xAddCutCopyPasteToContextMenu(this TextBox t, bool addClear, bool setStateNow) {\n\t\tvar m = t.ContextMenu ??= new();\n\t\t\n\t\tvar cut = m.xAdd(\"Cut\", \"Ctrl+X\", (_, _) => t.Cut(), false);\n\t\tvar copy = m.xAdd(\"Copy\", \"Ctrl+C\", (_, _) => t.Copy(), false);\n\t\tvar paste = m.xAdd(\"Paste\", \"Ctrl+V\", (_, _) => t.Paste(), false);\n\t\tif (addClear) m.xAdd(\"Clear\", \"M-click\", (_, _) => t.Clear());\n\t\t\n\t\tt.ContextMenuOpening += _t_ContextMenuOpening;\n\t\tif (setStateNow) _t_ContextMenuOpening(null, null);\n\t\t\n\t\tvoid _t_ContextMenuOpening(object sender, ContextMenuEventArgs e) {\n\t\t\tcut.IsEnabled = t.SelectionLength > 0 && !t.IsReadOnly;\n\t\t\tcopy.IsEnabled = t.SelectionLength > 0;\n\t\t\tpaste.IsEnabled = !t.IsReadOnly && Clipboard.ContainsText();\n\t\t}\n\t\t\n\t\treturn m;\n\t}\n\t\n\t/// <summary>\n\t/// Adds new menu item.\n\t/// </summary>\n\t/// <param name=\"t\"></param>\n\t/// <param name=\"name\"></param>\n\t/// <param name=\"hotkey\"></param>\n\t/// <param name=\"click\"></param>\n\t/// <param name=\"enabled\"></param>\n\t/// <returns></returns>\n\tpublic static MenuItem xAdd(this ContextMenu t, string name, string hotkey, RoutedEventHandler click, bool enabled = true) {\n\t\tvar k = new MenuItem { Header = name, InputGestureText = hotkey, IsEnabled = enabled };\n\t\tk.Click += click;\n\t\tt.Items.Add(k);\n\t\treturn k;\n\t}\n\t\n\t/// <summary>\n\t/// Adds new separator.\n\t/// </summary>\n\tpublic static void xAddSeparator(this ContextMenu t) {\n\t\tt.Items.Add(new Separator());\n\t}\n}\n"
  },
  {
    "path": "Au.Controls/Util, Api/KImageUtil.cs",
    "content": "using System.Drawing;\nusing System.Drawing.Imaging;\n\nnamespace Au.Controls;\n\n#pragma warning disable 649\n\npublic static unsafe class KImageUtil {\n\t#region API\n\t\n\t[StructLayout(LayoutKind.Sequential, Pack = 2)]\n\tinternal struct BITMAPFILEHEADER {\n\t\tpublic ushort bfType;\n\t\tpublic int bfSize;\n\t\tpublic ushort bfReserved1;\n\t\tpublic ushort bfReserved2;\n\t\tpublic int bfOffBits;\n\t}\n\t\n\tinternal struct BITMAPCOREHEADER {\n\t\tpublic int bcSize;\n\t\tpublic ushort bcWidth;\n\t\tpublic ushort bcHeight;\n\t\tpublic ushort bcPlanes;\n\t\tpublic ushort bcBitCount;\n\t}\n\t\n\tinternal struct BITMAPV5HEADER {\n\t\tpublic int bV5Size;\n\t\tpublic int bV5Width;\n\t\tpublic int bV5Height;\n\t\tpublic ushort bV5Planes;\n\t\tpublic ushort bV5BitCount;\n\t\tpublic int bV5Compression;\n\t\tpublic int bV5SizeImage;\n\t\tpublic int bV5XPelsPerMeter;\n\t\tpublic int bV5YPelsPerMeter;\n\t\tpublic int bV5ClrUsed;\n\t\tpublic int bV5ClrImportant;\n\t\tpublic uint bV5RedMask;\n\t\tpublic uint bV5GreenMask;\n\t\tpublic uint bV5BlueMask;\n\t\tpublic uint bV5AlphaMask;\n\t\tpublic uint bV5CSType;\n\t\tpublic CIEXYZTRIPLE bV5Endpoints;\n\t\tpublic uint bV5GammaRed;\n\t\tpublic uint bV5GammaGreen;\n\t\tpublic uint bV5GammaBlue;\n\t\tpublic uint bV5Intent;\n\t\tpublic uint bV5ProfileData;\n\t\tpublic uint bV5ProfileSize;\n\t\tpublic uint bV5Reserved;\n\t}\n\t\n\tinternal struct CIEXYZTRIPLE {\n\t\tpublic CIEXYZ ciexyzRed;\n\t\tpublic CIEXYZ ciexyzGreen;\n\t\tpublic CIEXYZ ciexyzBlue;\n\t}\n\t\n\tinternal struct CIEXYZ {\n\t\tpublic int ciexyzX;\n\t\tpublic int ciexyzY;\n\t\tpublic int ciexyzZ;\n\t}\n\t\n\t#endregion\n\t\n\tinternal struct BitmapFileInfo_ {\n\t\t/// <summary>\n\t\t/// Can be BITMAPINFOHEADER/BITMAPV5HEADER or BITMAPCOREHEADER.\n\t\t/// </summary>\n\t\tpublic void* biHeader;\n\t\tpublic int width, height, bitCount;\n\t\tpublic bool isCompressed;\n\t}\n\t\n\t/// <summary>\n\t/// Gets some info from BITMAPINFOHEADER or BITMAPCOREHEADER.\n\t/// Checks if it is valid bitmap file header. Returns false if invalid.\n\t/// </summary>\n\tinternal static bool GetBitmapFileInfo_(byte[] mem, out BitmapFileInfo_ x) {\n\t\tx = new BitmapFileInfo_();\n\t\tfixed (byte* bp = mem) {\n\t\t\tBITMAPFILEHEADER* f = (BITMAPFILEHEADER*)bp;\n\t\t\tint minHS = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPCOREHEADER);\n\t\t\tif (mem.Length <= minHS || f->bfType != (((byte)'M' << 8) | (byte)'B') || f->bfOffBits >= mem.Length || f->bfOffBits < minHS)\n\t\t\t\treturn false;\n\t\t\t\n\t\t\tApi.BITMAPINFOHEADER* h = (Api.BITMAPINFOHEADER*)(f + 1);\n\t\t\tint siz = h->biSize;\n\t\t\tif (siz >= sizeof(Api.BITMAPINFOHEADER) && siz <= sizeof(BITMAPV5HEADER) * 2) {\n\t\t\t\tx.width = h->biWidth;\n\t\t\t\tx.height = h->biHeight;\n\t\t\t\tx.bitCount = h->biBitCount;\n\t\t\t\tx.isCompressed = h->biCompression != 0;\n\t\t\t} else if (siz == sizeof(BITMAPCOREHEADER)) {\n\t\t\t\tBITMAPCOREHEADER* ch = (BITMAPCOREHEADER*)h;\n\t\t\t\tx.width = ch->bcWidth;\n\t\t\t\tx.height = ch->bcHeight;\n\t\t\t\tx.bitCount = ch->bcBitCount;\n\t\t\t} else return false;\n\t\t\t\n\t\t\t//x.height = Math.Abs(x.height); //no, then draws incorrectly if top-down\n\t\t\tif (x.height <= 0) return false; //rare\n\t\t\t\n\t\t\tx.biHeader = h;\n\t\t\treturn true;\n\t\t\t//note: don't check f->bfSize. Sometimes it is incorrect (> or < memSize). All programs open such files. Instead check other data later.\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Image type detected by <see cref=\"ImageTypeFromString(out int, string)\"/>.\n\t/// </summary>\n\tpublic enum ImageType {\n\t\t/// <summary>The string isn't image.</summary>\n\t\tNone,\n\t\t\n\t\t/// <summary>Base64 encoded image file data with prefix \"image:\" (.png/gif/jpg) or \"image:WkJN\" (compressed .bmp). See <see cref=\"ImageToString(string)\"/>.</summary>\n\t\tBase64Image,\n\t\t\n\t\t/// <summary>.bmp file path.</summary>\n\t\tBmp,\n\t\t\n\t\t/// <summary>.png, .gif or .jpg file path.</summary>\n\t\tPngGifJpg,\n\t\t\n\t\t/// <summary>.ico file path.</summary>\n\t\tIco,\n\t\t\n\t\t/// <summary>.cur or .ani file path.</summary>\n\t\tCur,\n\t\t\n\t\t/// <summary>Icon from a .dll or other file containing icons, like @\"C:\\a\\b.dll,15\".</summary>\n\t\tIconLib,\n\t\t\n\t\t/// <summary>None of other image types.</summary>\n\t\tShellIcon,\n\t\t\n\t\t/// <summary>XAML image data.</summary>\n\t\tXaml,\n\t\t\n\t\t/// <summary>XAML icon name \"*Pack.Icon color\".</summary>\n\t\tXamlIconName,\n\t\t\n\t\t//rejected. Where it used, we cannot know the assembly; maybe it is script's assembly and in editor we cannot get its resources or it is difficult (need to use meta).\n\t\t///// <summary>\"resource:name\". An image resource name from managed resources of the entry assembly. In Visual Studio use build action \"Resource\".</summary>\n\t\t//Resource,\n\t}\n\t\n\t/// <summary>\n\t/// Gets image type from string.\n\t/// </summary>\n\t/// <param name=\"prefixLength\">Length of prefix \"imagefile:\" or \"image:\".</param>\n\t/// <param name=\"s\">File path etc. See <see cref=\"ImageType\"/>. It is UTF-8 because used directly with Scintilla text.</param>\n\tinternal static ImageType ImageTypeFromString(out int prefixLength, RByte s) {\n\t\tprefixLength = 0;\n\t\tif (s.Length < 2) return default; //C:\\x.bmp or .h\n\t\tchar c1 = (char)s[0], c2 = (char)s[1];\n\t\t\n\t\t//special strings\n\t\tswitch (c1) {\n\t\tcase 'i':\n\t\t\tif (s.StartsWith(\"image:\"u8)) {\n\t\t\t\tif (s.Length < 10) return default;\n\t\t\t\tprefixLength = 6;\n\t\t\t\treturn ImageType.Base64Image;\n\t\t\t}\n\t\t\tif (s.StartsWith(\"imagefile:\"u8)) {\n\t\t\t\ts = s[10..];\n\t\t\t\tif (s.Length < 8) return default;\n\t\t\t\tprefixLength = 10;\n\t\t\t\tc1 = (char)s[0]; c2 = (char)s[1];\n\t\t\t}\n\t\t\tbreak;\n\t\t//case 'r': if (s.StartsWith(\"resource:\"u8)) return ImageType.Resource; break;\n\t\tcase '<':\n\t\t\tif (s.IndexOf(\"xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'\"u8) > 0) {\n\t\t\t\tif (s.IndexOf(\"<Path \"u8) >= 0) return ImageType.Xaml;\n\t\t\t\tif (s.IndexOf(\"<GeometryDrawing \"u8) >= 0) return ImageType.Xaml;\n\t\t\t}\n\t\t\treturn default;\n\t\tcase '*':\n\t\t\tif (_DetectIconString(s)) return ImageType.XamlIconName;\n\t\t\treturn default;\n\t\t}\n\t\t\n\t\t//file path\n\t\tif (s.Length >= 8 && (c1 == '%' || (c2 == ':' && c1.IsAsciiAlpha()) || (c1 == '\\\\' && c2 == '\\\\'))) { //is image file path?\n\t\t\tif (s[^4] == '.') {\n\t\t\t\tSpan<byte> e = stackalloc byte[3];\n\t\t\t\tfor (int j = 0, k = s.Length - 3; j < 3;) e[j++] = (byte)char.ToLowerInvariant((char)s[k++]);\n\t\t\t\tif (e.SequenceEqual(\"bmp\"u8)) return ImageType.Bmp;\n\t\t\t\tif (e.SequenceEqual(\"png\"u8)) return ImageType.PngGifJpg;\n\t\t\t\tif (e.SequenceEqual(\"gif\"u8)) return ImageType.PngGifJpg;\n\t\t\t\tif (e.SequenceEqual(\"jpg\"u8)) return ImageType.PngGifJpg;\n\t\t\t\tif (e.SequenceEqual(\"ico\"u8)) return ImageType.Ico;\n\t\t\t\tif (e.SequenceEqual(\"cur\"u8)) return ImageType.Cur;\n\t\t\t\tif (e.SequenceEqual(\"ani\"u8)) return ImageType.Cur;\n\t\t\t} else if (((char)s[^1]).IsAsciiDigit()) { //can be like C:\\x.dll,10\n\t\t\t\tint i = s.LastIndexOf((byte)',');\n\t\t\t\tif (i >= 8 && s[i - 4] == '.' && char.IsAsciiLetter((char)s[i - 1])) {\n\t\t\t\t\tif (s[++i] == '-') i++;\n\t\t\t\t\tif (!s[i..].ContainsAnyExceptInRange((byte)'0', (byte)'9')) return ImageType.IconLib;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\treturn ImageType.ShellIcon; //can be other file type, URL, .ext, :: ITEMIDLIST, ::{CLSID}\n\t\t\n\t\t//copied from WpfUtil_.DetectIconString(RStr)\n\t\tstatic bool _DetectIconString(RByte s) {\n\t\t\tif (s.Length < 8 || s[0] != '*') return false;\n\t\t\tint pack = 1;\n\t\t\tif (s[1] == '<') { // *<library>*icon\n\t\t\t\tpack = s.IndexOf((byte)'>');\n\t\t\t\tif (pack++ < 0 || s.Length - pack < 8 || s[pack] != '*') return false;\n\t\t\t\tpack++;\n\t\t\t}\n\t\t\tint name = pack; while (name < s.Length && s[name] != '.') name++; if (name < pack + 4) return false;\n\t\t\tint end = ++name; while (end < s.Length && s[end] != ' ') end++;\n\t\t\treturn end > name;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Gets image type from string.\n\t/// </summary>\n\t/// <param name=\"prefixLength\">Length of prefix \"imagefile:\" or \"image:\".</param>\n\t/// <param name=\"s\">File path etc. See <see cref=\"ImageType\"/>.</param>\n\tpublic static ImageType ImageTypeFromString(out int prefixLength, string s) {\n\t\treturn ImageTypeFromString(out prefixLength, Encoding.UTF8.GetBytes(s));\n\t}\n\t\n\t/// <summary>\n\t/// Loads image and returns its data in .bmp file format.\n\t/// Returns null if fails, for example file not found or invalid Base64 string.\n\t/// </summary>\n\t/// <param name=\"s\">Depends on t. File path or resource name without prefix \"imagefile:\" or Base64 image data without prefix \"image:\".</param>\n\t/// <param name=\"t\">Image type and string format.</param>\n\t/// <param name=\"searchPath\">Use <see cref=\"filesystem.searchPath\"/></param>\n\t/// <param name=\"xaml\">If not null, supports XAML images. See <see cref=\"ImageUtil.LoadGdipBitmapFromXaml\"/>.</param>\n\t/// <remarks>Supports environment variables etc. If not full path, searches in <see cref=\"folders.ThisAppImages\"/>.</remarks>\n\tpublic static byte[] BmpFileDataFromString(string s, ImageType t, bool searchPath = false, (int dpi, SIZE? size)? xaml = null) {\n\t\t//print.qm2.write(t);\n\t\ttry {\n\t\t\tswitch (t) {\n\t\t\tcase ImageType.Bmp or ImageType.PngGifJpg or ImageType.Cur:\n\t\t\t\tif (searchPath) {\n\t\t\t\t\ts = filesystem.searchPath(s, folders.ThisAppImages);\n\t\t\t\t\tif (s == null) return null;\n\t\t\t\t} else {\n\t\t\t\t\tif (!pathname.isFullPathExpand(ref s)) return null;\n\t\t\t\t\ts = pathname.normalize(s, folders.ThisAppImages);\n\t\t\t\t\tif (!filesystem.exists(s).File) return null;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tg1:\n\t\t\tswitch (t) {\n\t\t\tcase ImageType.Base64Image:\n\t\t\t\tvar a = Convert.FromBase64String(s);\n\t\t\t\tif (s.AsSpan().TrimStart().StartsWith(\"WkJN\")) return Convert2.BrotliDecompress(a.AsSpan(3));\n\t\t\t\treturn _ImageToBytes(new(new MemoryStream(a, false)));\n\t\t\t//case ImageType.Resource:\n\t\t\t//\treturn _ImageToBytes(ResourceUtil.GetGdipBitmap(s));\n\t\t\tcase ImageType.Bmp:\n\t\t\t\treturn File.ReadAllBytes(s);\n\t\t\tcase ImageType.PngGifJpg:\n\t\t\t\treturn _ImageToBytes(new(s));\n\t\t\tcase ImageType.Ico or ImageType.IconLib or ImageType.ShellIcon or ImageType.Cur:\n\t\t\t\treturn _IconToBytes(s, t == ImageType.Cur, searchPath);\n\t\t\tcase ImageType.XamlIconName when xaml != null:\n\t\t\t\ts = ScriptEditor.GetIcon(s, EGetIcon.IconNameToXaml);\n\t\t\t\tif (s == null) break;\n\t\t\t\tt = ImageType.Xaml;\n\t\t\t\tgoto g1;\n\t\t\tcase ImageType.Xaml when xaml != null:\n\t\t\t\tvar xv = xaml.Value;\n\t\t\t\treturn _ImageToBytes(ImageUtil.LoadGdipBitmapFromXaml(s, xv.dpi, xv.size));\n\t\t\t}\n\t\t}\n\t\tcatch (Exception ex) { Debug_.PrintIf(t != ImageType.Xaml, ex.Message + \"    \" + s); }\n\t\treturn null;\n\t}\n\t\n\tstatic byte[] _ImageToBytes(Bitmap im) {\n\t\tif (im == null) return null;\n\t\ttry {\n\t\t\t//workaround for the black alpha problem. Does not make slower.\n\t\t\tvar flags = (ImageFlags)im.Flags;\n\t\t\tif (0 != (flags & ImageFlags.HasAlpha)) { //most png have it\n\t\t\t\tvar t = new Bitmap(im.Width, im.Height);\n\t\t\t\tt.SetResolution(im.HorizontalResolution, im.VerticalResolution);\n\t\t\t\t\n\t\t\t\tusing (var g = Graphics.FromImage(t)) {\n\t\t\t\t\tg.Clear(Color.White);\n\t\t\t\t\tg.DrawImageUnscaled(im, 0, 0);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvar old = im; im = t; old.Dispose();\n\t\t\t}\n\t\t\t\n\t\t\tvar m = new MemoryStream();\n\t\t\tim.Save(m, ImageFormat.Bmp);\n\t\t\treturn m.ToArray();\n\t\t}\n\t\tcatch (Exception ex) { Debug_.Print(ex); return null; }\n\t\tfinally { im.Dispose(); }\n\t}\n\t\n\tstatic byte[] _IconToBytes(string s, bool isCursor, bool searchPath) {\n\t\t//info: would be much easier with Icon.ToBitmap(), but it creates bitmap that is displayed incorrectly when we draw it in Scintilla.\n\t\t//\tMask or alpha areas then are black.\n\t\t//\tAnother way - draw it like in the png case. Not tested. Maybe would not work with cursors. Probably slower.\n\t\t\n\t\tIntPtr hi; int siz;\n\t\tif (isCursor) {\n\t\t\thi = Api.LoadImage(default, s, Api.IMAGE_CURSOR, 0, 0, Api.LR_LOADFROMFILE | Api.LR_DEFAULTSIZE);\n\t\t\tsiz = Api.GetSystemMetrics(Api.SM_CXCURSOR);\n\t\t\t//note: if LR_DEFAULTSIZE, uses SM_CXCURSOR, normally 32. It may be not what Explorer displays eg in Cursors folder. But without it gets the first cursor, which often is large, eg 128.\n\t\t} else {\n\t\t\thi = icon.of(s, 16, searchPath ? 0 : IconGetFlags.DontSearch)?.Detach() ?? default;\n\t\t\tsiz = 16;\n\t\t}\n\t\tif (hi == default) return null;\n\t\ttry {\n\t\t\tusing var m = new MemoryBitmap(siz, siz);\n\t\t\tRECT r = new(0, 0, siz, siz);\n\t\t\tApi.FillRect(m.Hdc, r, Api.GetStockObject(0)); //WHITE_BRUSH\n\t\t\tif (!Api.DrawIconEx(m.Hdc, 0, 0, hi, siz, siz)) return null;\n\t\t\t\n\t\t\tint headersSize = sizeof(BITMAPFILEHEADER) + sizeof(Api.BITMAPINFOHEADER);\n\t\t\tvar a = new byte[headersSize + siz * siz * 3];\n\t\t\tfixed (byte* p = a) {\n\t\t\t\tvar f = (BITMAPFILEHEADER*)p;\n\t\t\t\tf->bfType = ((byte)'M' << 8) | (byte)'B'; f->bfOffBits = headersSize; f->bfSize = a.Length;\n\t\t\t\tvar bi = new Api.BITMAPINFO(siz, siz, 24);\n\t\t\t\tif (siz != Api.GetDIBits(m.Hdc, m.Hbitmap, 0, siz, p + headersSize, ref bi, 0)) return null; //DIB_RGB_COLORS\n\t\t\t\tMemoryUtil.Copy(&bi, f + 1, bi.biSize);\n\t\t\t}\n\t\t\treturn a;\n\t\t}\n\t\tfinally {\n\t\t\tApi.DestroyIcon(hi);\n\t\t}\n\t}\n\t\n#if false //currently not used. If using, move to icon class.\n\n\t/// <summary>\n\t/// Gets icon or cursor size from its native handle.\n\t/// Returns 0 if failed.\n\t/// </summary>\n\tpublic static int GetIconSizeFromHandle(IntPtr hi)\n\t{\n\t\tif(!Api.GetIconInfo(hi, out var ii)) return 0;\n\t\ttry {\n\t\t\tvar hb = (ii.hbmColor != default) ? ii.hbmColor : ii.hbmMask;\n\t\t\tApi.BITMAP b;\n\t\t\tif(0 == Api.GetObject(hb, sizeof(BITMAP), &b)) return 0;\n\t\t\tprint.it(b.bmWidth, b.bmHeight, b.bmBits);\n\t\t\treturn b.bmWidth;\n\t\t}\n\t\tfinally {\n\t\t\tApi.DeleteObject(ii.hbmColor);\n\t\t\tApi.DeleteObject(ii.hbmMask);\n\t\t}\n\t}\n#endif\n\t\n\t/// <summary>\n\t/// Compresses .bmp file data (<see cref=\"Convert2.BrotliCompress\"/>) and Base64-encodes.\n\t/// Returns string with \"image:\" prefix.\n\t/// </summary>\n\tpublic static string BmpFileDataToString(byte[] bmpFileData) {\n\t\tif (bmpFileData == null) return null;\n\t\treturn \"image:WkJN\" + Convert.ToBase64String(Convert2.BrotliCompress(bmpFileData));\n\t\t//WkJN is Base64 of \"ZBM\"\n\t}\n\t\n\t/// <summary>\n\t/// Converts image file data to string that can be used in source code instead of file path. It is supported by some functions of this library, for example <see cref=\"uiimage.find\"/>.\n\t/// Supports all <see cref=\"ImageType\"/> formats. For non-image files gets icon. Converts icons to bitmap.\n\t/// If <i>path</i> is of <see cref=\"ImageType\"/> type <b>Base64Image</b>, <b>Xaml</b> or <b>XamlIconName</b>, returns <i>path</i>. Else returns Base64 encoded file data with prefix \"image:\" (.png/gif/jpg) or \"image:WkJN\" (compressed .bmp).\n\t/// Returns null if path is not a valid image string or the file does not exist or failed to load.\n\t/// </summary>\n\t/// <remarks>Supports environment variables etc. If not full path, searches in <see cref=\"folders.ThisAppImages\"/> and standard directories.</remarks>\n\tpublic static string ImageToString(string path) {\n\t\tvar t = ImageTypeFromString(out _, path);\n\t\tswitch (t) {\n\t\tcase ImageType.None: return null;\n\t\tcase ImageType.Base64Image or ImageType.Xaml or ImageType.XamlIconName: return path;\n\t\t//case ImageType.Resource: return path;\n\t\tcase ImageType.PngGifJpg:\n\t\t\tpath = filesystem.searchPath(path, folders.ThisAppImages); if (path == null) return null;\n\t\t\ttry { return \"image:\" + Convert.ToBase64String(filesystem.loadBytes(path)); }\n\t\t\tcatch (Exception ex) { Debug_.Print(ex); return null; }\n\t\t}\n\t\treturn BmpFileDataToString(BmpFileDataFromString(path, t, true));\n\t}\n\t\n\t//currently not used.\n\t//\tWhen tried to display the WPF image in Image control, was blurry when high DPI, although size is correct (because bitmap's DPI is correct).\n\t//\tUseLayoutRounding did not help. It seems WPF scales/unscales the image don't know how many times.\n\t///// <summary>\n\t///// Converts GDI+ bitmap to WPF bitmap image.\n\t///// </summary>\n\t//public static System.Windows.Media.Imaging.BitmapSource WinformsBitmapToWpf(Bitmap bmp, bool dispose, int? dpi = null) {\n\t//\tvar size = bmp.Size;\n\t//\tvar bd = bmp.LockBits(new Rectangle(default, size), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);\n\t//\ttry {\n\t//\t\treturn System.Windows.Media.Imaging.BitmapSource.Create(\n\t//\t\t\tsize.Width, size.Height,\n\t//\t\t\tdpi ?? bmp.HorizontalResolution.ToInt(),\n\t//\t\t\tdpi ?? bmp.VerticalResolution.ToInt(),\n\t//\t\t\tpixelFormat: System.Windows.Media.PixelFormats.Bgra32,\n\t//\t\t\tnull, bd.Scan0, bd.Height * bd.Stride, bd.Stride);\n\t//\t}\n\t//\tfinally {\n\t//\t\tbmp.UnlockBits(bd);\n\t//\t\tif (dispose) bmp.Dispose();\n\t//\t}\n\t//}\n\t\n\t///// <summary>\n\t///// If <i>dpi</i> &gt; 96 (100%) and image resolution is different, returns scaled image.Size. Else returns image.Size.\n\t///// </summary>\n\t//public static SIZE ImageSize(Image image, int dpi) {\n\t//\tif (image == null) return default;\n\t//\tSIZE z = image.Size;\n\t//\tif (dpi > 96) {\n\t//\t\tz.width = Math2.MulDiv(z.width, dpi, image.HorizontalResolution.ToInt());\n\t//\t\tz.height = Math2.MulDiv(z.height, dpi, image.VerticalResolution.ToInt());\n\t//\t}\n\t//\treturn z;\n\t//}\n\t\n\t///// <summary>\n\t///// If <i>dpi</i> &gt; 96 (100%) and image resolution is different, returns scaled copy of <i>image</i>. Else returns <i>image</i>.\n\t///// </summary>\n\t///// <param name=\"image\"></param>\n\t///// <param name=\"dpi\"></param>\n\t///// <param name=\"disposeOld\">If performed scaling (it means created new image), dispose old image.</param>\n\t///// <remarks>\n\t///// Unlike <see cref=\"System.Windows.Forms.Control.ScaleBitmapLogicalToDevice\"/>, returns same object if don't need scaling.\n\t///// </remarks>\n\t//public static Image ScaleImage(Image image, int dpi, bool disposeOld) {\n\t//\tif (image != null && dpi > 96) {\n\t//\t\tint xRes = image.HorizontalResolution.ToInt(), yRes = image.VerticalResolution.ToInt();\n\t//\t\t//print.it(xRes, yRes, dpi);\n\t//\t\tif (xRes != dpi || yRes != dpi) {\n\t//\t\t\tvar z = image.Size;\n\t//\t\t\tvar r = _ScaleBitmap(image, Math2.MulDiv(z.Width, dpi, xRes), Math2.MulDiv(z.Height, dpi, yRes), z);\n\t//\t\t\tif (disposeOld) image.Dispose();\n\t//\t\t\timage = r;\n\t//\t\t}\n\t//\t}\n\t//\treturn image;\n\t//}\n\t\n\t////From .NET DpiHelper.ScaleBitmapToSize, which is used by Control.ScaleBitmapLogicalToDevice.\n\t//private static Bitmap _ScaleBitmap(Image oldImage, int width, int height, Size oldSize) {\n\t//\t//note: could simply return new Bitmap(oldImage, width, height). It uses similar code, but lower quality.\n\t\n\t//\tvar r = new Bitmap(width, height, oldImage.PixelFormat);\n\t\n\t//\tusing var graphics = Graphics.FromImage(r);\n\t//\tvar mode = InterpolationMode.HighQualityBicubic;\n\t//\t//if(width % oldSize.Width == 0 && height % oldSize.Height == 0) mode = InterpolationMode.NearestNeighbor; //DpiHelper does it, but maybe it isn't a good idea\n\t//\tgraphics.InterpolationMode = mode;\n\t//\tgraphics.CompositingQuality = CompositingQuality.HighQuality;\n\t\n\t//\tvar sourceRect = new RectangleF(-0.5f, -0.5f, oldSize.Width, oldSize.Height);\n\t//\tvar destRect = new RectangleF(0, 0, width, height);\n\t\n\t//\tgraphics.DrawImage(oldImage, destRect, sourceRect, GraphicsUnit.Pixel);\n\t\n\t//\treturn r;\n\t//}\n\t\n\t//currently not used.\n\t///// <summary>\n\t///// Creates GDI+ <b>Bitmap</b> from a GDI bitmap.\n\t///// </summary>\n\t///// <param name=\"hbitmap\">GDI bitmap handle.</param>\n\t///// <remarks>\n\t///// How this function is different from <see cref=\"System.Drawing.Image.FromHbitmap\"/>:\n\t///// 1. <b>Image.FromHbitmap</b> usually creates bottom-up bitmap, which is incompatible with <see cref=\"uiimage.find\"/>. This function creates normal top-down bitmap, like <c>new Bitmap(...)</c>, <c>Bitmap.FromFile(...)</c> etc.\n\t///// 2. This function always creates bitmap of <b>PixelFormat</b> <b>Format32bppRgb</b>.\n\t///// </remarks>\n\t///// <exception cref=\"AuException\">Failed. For example <i>hbitmap</i> is default(IntPtr).</exception>\n\t///// <exception cref=\"Exception\">Exceptions of Bitmap(int, int, PixelFormat) constructor.</exception>\n\t//public static unsafe System.Drawing.Bitmap ConvertHbitmapToGdipBitmap(IntPtr hbitmap) {\n\t//\tvar bh = new Api.BITMAPINFOHEADER() { biSize = sizeof(Api.BITMAPINFOHEADER) };\n\t//\tusing (var dcs = new ScreenDC_()) {\n\t//\t\tif (0 == Api.GetDIBits(dcs, hbitmap, 0, 0, null, &bh, 0)) goto ge;\n\t//\t\tint wid = bh.biWidth, hei = bh.biHeight;\n\t//\t\tif (hei > 0) bh.biHeight = -bh.biHeight; else hei = -hei;\n\t//\t\tbh.biBitCount = 32;\n\t\n\t//\t\tvar R = new System.Drawing.Bitmap(wid, hei, System.Drawing.Imaging.PixelFormat.Format32bppRgb);\n\t//\t\tvar d = R.LockBits(new System.Drawing.Rectangle(0, 0, wid, hei), System.Drawing.Imaging.ImageLockMode.ReadWrite, R.PixelFormat);\n\t//\t\tbool ok = hei == Api.GetDIBits(dcs, hbitmap, 0, hei, (void*)d.Scan0, &bh, 0);\n\t//\t\tR.UnlockBits(d);\n\t//\t\tif (!ok) { R.Dispose(); goto ge; }\n\t//\t\treturn R;\n\t//\t}\n\t//\tge:\n\t//\tthrow new AuException();\n\t//}\n}\n"
  },
  {
    "path": "Au.Controls/resources/AssemblyInfo.cs",
    "content": "using System.Windows;\n\n[assembly: AssemblyTitle(\"Au.Controls\")]\n[assembly: AssemblyDescription(\"\")]\n[assembly: AssemblyConfiguration(\"\")]\n//more in global2.cs\n\n#if IDE_LA\n[assembly: InternalsVisibleTo(\"Au.Editor\")]\n#else\n[assembly: InternalsVisibleTo(\"Au.Editor, PublicKey=0024000004800000940000000602000000240000525341310004000001000100095c6b7a0fe60fbe4a77e52dd10a09331ee3c3a7399aa9cc17db8a015647469a19784d5e33a2450a0a49c37bf17c0c3223674f64104eae649ba27c51a90c24989faec87d59217d7850efc8151109bbf9b027b7714fc01788317d2b991b2c2669836a7725e942f76607efde5cdacd8c497a45c5f9673fcf102fdbf92237a524a4\")]\n#endif\n\n[assembly: ThemeInfo(\n\tResourceDictionaryLocation.None, //where theme specific resource dictionaries are located\n\t\t\t\t\t\t\t\t\t //(used if a resource is not found in the page,\n\t\t\t\t\t\t\t\t\t // or application resource dictionaries)\n\tResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located\n\t\t\t\t\t\t\t\t\t\t\t  //(used if a resource is not found in the page,\n\t\t\t\t\t\t\t\t\t\t\t  // app, or any theme specific resource dictionaries)\n)]\n"
  },
  {
    "path": "Au.Controls/resources/Generic.xaml",
    "content": "<!-- note: don't use local types (clr-namespace). Then VS compiles project 2 times. -->\n<ResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                    xmlns:controls=\"clr-namespace:Au.Controls\">\n    <SolidColorBrush x:Key=\"TabItem.Selected.Background\" Color=\"#FFFFFF\"/>\n    <SolidColorBrush x:Key=\"TabItem.Selected.Border\" Color=\"#ACACAC\"/>\n    <Style x:Key=\"AuPanelsTabControlStyle\" TargetType=\"{x:Type TabControl}\">\n        <Setter Property=\"Padding\" Value=\"2\"/>\n        <Setter Property=\"HorizontalContentAlignment\" Value=\"Center\"/>\n        <Setter Property=\"VerticalContentAlignment\" Value=\"Center\"/>\n        <Setter Property=\"Background\" Value=\"{StaticResource TabItem.Selected.Background}\"/>\n        <Setter Property=\"BorderBrush\" Value=\"{StaticResource TabItem.Selected.Border}\"/>\n        <Setter Property=\"BorderThickness\" Value=\"1\"/>\n        <Setter Property=\"Foreground\" Value=\"{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}\"/>\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"{x:Type TabControl}\">\n                    <Grid x:Name=\"templateRoot\" Background=\"LightSteelBlue\" ClipToBounds=\"true\" SnapsToDevicePixels=\"true\" KeyboardNavigation.TabNavigation=\"Local\">\n                        <Grid.ColumnDefinitions>\n                            <ColumnDefinition x:Name=\"ColumnDefinition0\"/>\n                            <ColumnDefinition x:Name=\"ColumnDefinition1\" Width=\"0\"/>\n                        </Grid.ColumnDefinitions>\n                        <Grid.RowDefinitions>\n                            <RowDefinition x:Name=\"RowDefinition0\" Height=\"Auto\"/>\n                            <RowDefinition x:Name=\"RowDefinition1\" Height=\"*\"/>\n                        </Grid.RowDefinitions>\n                        <controls:FlexStackPanel x:Name=\"headerPanel\" Background=\"Transparent\" Grid.Column=\"0\" IsItemsHost=\"true\" Margin=\"2,2,8,0\" Grid.Row=\"0\" KeyboardNavigation.TabIndex=\"1\" Panel.ZIndex=\"1\"/>\n                        <Border x:Name=\"contentPanel\" Background=\"{TemplateBinding Background}\" BorderThickness=\"{TemplateBinding BorderThickness}\" BorderBrush=\"{TemplateBinding BorderBrush}\" Grid.Column=\"0\" KeyboardNavigation.DirectionalNavigation=\"Contained\" Grid.Row=\"1\" KeyboardNavigation.TabNavigation=\"Local\" KeyboardNavigation.TabIndex=\"2\">\n                            <ContentPresenter x:Name=\"PART_SelectedContentHost\" ContentSource=\"SelectedContent\" Margin=\"{TemplateBinding Padding}\" SnapsToDevicePixels=\"{TemplateBinding SnapsToDevicePixels}\"/>\n                        </Border>\n                    </Grid>\n                    <ControlTemplate.Triggers>\n                        <Trigger Property=\"TabStripPlacement\" Value=\"Bottom\">\n                            <Setter Property=\"Grid.Row\" TargetName=\"headerPanel\" Value=\"1\"/>\n                            <Setter Property=\"Grid.Row\" TargetName=\"contentPanel\" Value=\"0\"/>\n                            <Setter Property=\"Height\" TargetName=\"RowDefinition0\" Value=\"*\"/>\n                            <Setter Property=\"Height\" TargetName=\"RowDefinition1\" Value=\"Auto\"/>\n                            <Setter Property=\"Margin\" TargetName=\"headerPanel\" Value=\"2,0,8,2\"/>\n                        </Trigger>\n                        <Trigger Property=\"TabStripPlacement\" Value=\"Left\">\n                            <Setter Property=\"Orientation\" Value=\"Vertical\" TargetName=\"headerPanel\"/>\n                            <Setter Property=\"Grid.Row\" TargetName=\"headerPanel\" Value=\"0\"/>\n                            <Setter Property=\"Grid.Row\" TargetName=\"contentPanel\" Value=\"0\"/>\n                            <Setter Property=\"Grid.Column\" TargetName=\"headerPanel\" Value=\"0\"/>\n                            <Setter Property=\"Grid.Column\" TargetName=\"contentPanel\" Value=\"1\"/>\n                            <Setter Property=\"Width\" TargetName=\"ColumnDefinition0\" Value=\"Auto\"/>\n                            <Setter Property=\"Width\" TargetName=\"ColumnDefinition1\" Value=\"*\"/>\n                            <Setter Property=\"Height\" TargetName=\"RowDefinition0\" Value=\"*\"/>\n                            <Setter Property=\"Height\" TargetName=\"RowDefinition1\" Value=\"0\"/>\n                            <Setter Property=\"Margin\" TargetName=\"headerPanel\" Value=\"2,2,0,8\"/>\n                        </Trigger>\n                        <Trigger Property=\"TabStripPlacement\" Value=\"Right\">\n                            <Setter Property=\"Orientation\" Value=\"Vertical\" TargetName=\"headerPanel\"/>\n                            <Setter Property=\"Grid.Row\" TargetName=\"headerPanel\" Value=\"0\"/>\n                            <Setter Property=\"Grid.Row\" TargetName=\"contentPanel\" Value=\"0\"/>\n                            <Setter Property=\"Grid.Column\" TargetName=\"headerPanel\" Value=\"1\"/>\n                            <Setter Property=\"Grid.Column\" TargetName=\"contentPanel\" Value=\"0\"/>\n                            <Setter Property=\"Width\" TargetName=\"ColumnDefinition0\" Value=\"*\"/>\n                            <Setter Property=\"Width\" TargetName=\"ColumnDefinition1\" Value=\"Auto\"/>\n                            <Setter Property=\"Height\" TargetName=\"RowDefinition0\" Value=\"*\"/>\n                            <Setter Property=\"Height\" TargetName=\"RowDefinition1\" Value=\"0\"/>\n                            <Setter Property=\"Margin\" TargetName=\"headerPanel\" Value=\"0,2,2,8\"/>\n                        </Trigger>\n                        <Trigger Property=\"IsEnabled\" Value=\"false\">\n                            <Setter Property=\"TextElement.Foreground\" TargetName=\"templateRoot\" Value=\"{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}\"/>\n                        </Trigger>\n                    </ControlTemplate.Triggers>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n    </Style>\n    <Style x:Key=\"FocusVisual\">\n        <Setter Property=\"Control.Template\">\n            <Setter.Value>\n                <ControlTemplate>\n                    <Rectangle Margin=\"2\" StrokeDashArray=\"1 2\" SnapsToDevicePixels=\"true\" StrokeThickness=\"1\" Stroke=\"{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}\"/>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n    </Style>\n    <LinearGradientBrush x:Key=\"TabItem.Static.Background\" EndPoint=\"0,1\" StartPoint=\"0,0\">\n        <GradientStop Color=\"#F0F0F0\" Offset=\"0.0\"/>\n        <GradientStop Color=\"#E5E5E5\" Offset=\"1.0\"/>\n    </LinearGradientBrush>\n    <SolidColorBrush x:Key=\"TabItem.Static.Border\" Color=\"#ACACAC\"/>\n    <LinearGradientBrush x:Key=\"TabItem.MouseOver.Background\" EndPoint=\"0,1\" StartPoint=\"0,0\">\n        <GradientStop Color=\"#ECF4FC\" Offset=\"0.0\"/>\n        <GradientStop Color=\"#DCECFC\" Offset=\"1.0\"/>\n    </LinearGradientBrush>\n    <SolidColorBrush x:Key=\"TabItem.MouseOver.Border\" Color=\"#7EB4EA\"/>\n    <SolidColorBrush x:Key=\"TabItem.Selected.Background1\" Color=\"#FFFFFF\"/>\n    <SolidColorBrush x:Key=\"TabItem.Selected.Border1\" Color=\"#ACACAC\"/>\n    <SolidColorBrush x:Key=\"TabItem.Disabled.Background\" Color=\"#F0F0F0\"/>\n    <SolidColorBrush x:Key=\"TabItem.Disabled.Border\" Color=\"#D9D9D9\"/>\n    <Style x:Key=\"AuPanelsTabItemStyle\" TargetType=\"{x:Type TabItem}\">\n        <Setter Property=\"FocusVisualStyle\" Value=\"{StaticResource FocusVisual}\"/>\n        <Setter Property=\"Foreground\" Value=\"Black\"/>\n        <Setter Property=\"Background\" Value=\"{StaticResource TabItem.Static.Background}\"/>\n        <Setter Property=\"BorderBrush\" Value=\"{StaticResource TabItem.Static.Border}\"/>\n        <Setter Property=\"Margin\" Value=\"0\"/>\n        <Setter Property=\"Padding\" Value=\"6,2,6,2\"/>\n        <Setter Property=\"HorizontalContentAlignment\" Value=\"Stretch\"/>\n        <Setter Property=\"VerticalContentAlignment\" Value=\"Stretch\"/>\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"{x:Type TabItem}\">\n                    <Grid x:Name=\"templateRoot\" SnapsToDevicePixels=\"true\">\n                        <Border x:Name=\"mainBorder\" Background=\"{TemplateBinding Background}\" BorderThickness=\"1,1,1,0\" BorderBrush=\"{TemplateBinding BorderBrush}\" Margin=\"0\">\n                            <Border x:Name=\"innerBorder\" Background=\"{StaticResource TabItem.Selected.Background1}\" BorderThickness=\"1,1,1,0\" BorderBrush=\"{StaticResource TabItem.Selected.Border1}\" Margin=\"-1\" Opacity=\"0\"/>\n                        </Border>\n                        <ContentPresenter x:Name=\"contentPresenter\" ContentSource=\"Header\" Focusable=\"False\" HorizontalAlignment=\"{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}\" Margin=\"{TemplateBinding Padding}\" RecognizesAccessKey=\"True\" SnapsToDevicePixels=\"{TemplateBinding SnapsToDevicePixels}\" VerticalAlignment=\"{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}\"/>\n                    </Grid>\n                    <ControlTemplate.Triggers>\n                        <MultiDataTrigger>\n                            <MultiDataTrigger.Conditions>\n                                <Condition Binding=\"{Binding IsMouseOver, RelativeSource={RelativeSource Self}}\" Value=\"true\"/>\n                                <Condition Binding=\"{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}\" Value=\"Left\"/>\n                            </MultiDataTrigger.Conditions>\n                            <Setter Property=\"Background\" TargetName=\"mainBorder\" Value=\"{StaticResource TabItem.MouseOver.Background}\"/>\n                            <Setter Property=\"BorderBrush\" TargetName=\"mainBorder\" Value=\"{StaticResource TabItem.MouseOver.Border}\"/>\n                            <Setter Property=\"BorderThickness\" TargetName=\"innerBorder\" Value=\"1,1,0,1\"/>\n                            <Setter Property=\"BorderThickness\" TargetName=\"mainBorder\" Value=\"1,1,0,1\"/>\n                        </MultiDataTrigger>\n                        <MultiDataTrigger>\n                            <MultiDataTrigger.Conditions>\n                                <Condition Binding=\"{Binding IsMouseOver, RelativeSource={RelativeSource Self}}\" Value=\"true\"/>\n                                <Condition Binding=\"{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}\" Value=\"Bottom\"/>\n                            </MultiDataTrigger.Conditions>\n                            <Setter Property=\"Background\" TargetName=\"mainBorder\" Value=\"{StaticResource TabItem.MouseOver.Background}\"/>\n                            <Setter Property=\"BorderBrush\" TargetName=\"mainBorder\" Value=\"{StaticResource TabItem.MouseOver.Border}\"/>\n                            <Setter Property=\"BorderThickness\" TargetName=\"innerBorder\" Value=\"1,0,1,1\"/>\n                            <Setter Property=\"BorderThickness\" TargetName=\"mainBorder\" Value=\"1,0,1,1\"/>\n                        </MultiDataTrigger>\n                        <MultiDataTrigger>\n                            <MultiDataTrigger.Conditions>\n                                <Condition Binding=\"{Binding IsMouseOver, RelativeSource={RelativeSource Self}}\" Value=\"true\"/>\n                                <Condition Binding=\"{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}\" Value=\"Right\"/>\n                            </MultiDataTrigger.Conditions>\n                            <Setter Property=\"Background\" TargetName=\"mainBorder\" Value=\"{StaticResource TabItem.MouseOver.Background}\"/>\n                            <Setter Property=\"BorderBrush\" TargetName=\"mainBorder\" Value=\"{StaticResource TabItem.MouseOver.Border}\"/>\n                            <Setter Property=\"BorderThickness\" TargetName=\"innerBorder\" Value=\"0,1,1,1\"/>\n                            <Setter Property=\"BorderThickness\" TargetName=\"mainBorder\" Value=\"0,1,1,1\"/>\n                        </MultiDataTrigger>\n                        <MultiDataTrigger>\n                            <MultiDataTrigger.Conditions>\n                                <Condition Binding=\"{Binding IsMouseOver, RelativeSource={RelativeSource Self}}\" Value=\"true\"/>\n                                <Condition Binding=\"{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}\" Value=\"Top\"/>\n                            </MultiDataTrigger.Conditions>\n                            <Setter Property=\"Background\" TargetName=\"mainBorder\" Value=\"{StaticResource TabItem.MouseOver.Background}\"/>\n                            <Setter Property=\"BorderBrush\" TargetName=\"mainBorder\" Value=\"{StaticResource TabItem.MouseOver.Border}\"/>\n                            <Setter Property=\"BorderThickness\" TargetName=\"innerBorder\" Value=\"1,1,1,0\"/>\n                            <Setter Property=\"BorderThickness\" TargetName=\"mainBorder\" Value=\"1,1,1,0\"/>\n                        </MultiDataTrigger>\n                        <MultiDataTrigger>\n                            <MultiDataTrigger.Conditions>\n                                <Condition Binding=\"{Binding IsEnabled, RelativeSource={RelativeSource Self}}\" Value=\"false\"/>\n                                <Condition Binding=\"{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}\" Value=\"Left\"/>\n                            </MultiDataTrigger.Conditions>\n                            <Setter Property=\"Opacity\" TargetName=\"contentPresenter\" Value=\"0.56\"/>\n                            <Setter Property=\"Background\" TargetName=\"mainBorder\" Value=\"{StaticResource TabItem.Disabled.Background}\"/>\n                            <Setter Property=\"BorderBrush\" TargetName=\"mainBorder\" Value=\"{StaticResource TabItem.Disabled.Border}\"/>\n                            <Setter Property=\"BorderThickness\" TargetName=\"innerBorder\" Value=\"1,1,0,1\"/>\n                            <Setter Property=\"BorderThickness\" TargetName=\"mainBorder\" Value=\"1,1,0,1\"/>\n                        </MultiDataTrigger>\n                        <MultiDataTrigger>\n                            <MultiDataTrigger.Conditions>\n                                <Condition Binding=\"{Binding IsEnabled, RelativeSource={RelativeSource Self}}\" Value=\"false\"/>\n                                <Condition Binding=\"{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}\" Value=\"Bottom\"/>\n                            </MultiDataTrigger.Conditions>\n                            <Setter Property=\"Opacity\" TargetName=\"contentPresenter\" Value=\"0.56\"/>\n                            <Setter Property=\"Background\" TargetName=\"mainBorder\" Value=\"{StaticResource TabItem.Disabled.Background}\"/>\n                            <Setter Property=\"BorderBrush\" TargetName=\"mainBorder\" Value=\"{StaticResource TabItem.Disabled.Border}\"/>\n                            <Setter Property=\"BorderThickness\" TargetName=\"innerBorder\" Value=\"1,0,1,1\"/>\n                            <Setter Property=\"BorderThickness\" TargetName=\"mainBorder\" Value=\"1,0,1,1\"/>\n                        </MultiDataTrigger>\n                        <MultiDataTrigger>\n                            <MultiDataTrigger.Conditions>\n                                <Condition Binding=\"{Binding IsEnabled, RelativeSource={RelativeSource Self}}\" Value=\"false\"/>\n                                <Condition Binding=\"{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}\" Value=\"Right\"/>\n                            </MultiDataTrigger.Conditions>\n                            <Setter Property=\"Opacity\" TargetName=\"contentPresenter\" Value=\"0.56\"/>\n                            <Setter Property=\"Background\" TargetName=\"mainBorder\" Value=\"{StaticResource TabItem.Disabled.Background}\"/>\n                            <Setter Property=\"BorderBrush\" TargetName=\"mainBorder\" Value=\"{StaticResource TabItem.Disabled.Border}\"/>\n                            <Setter Property=\"BorderThickness\" TargetName=\"innerBorder\" Value=\"0,1,1,1\"/>\n                            <Setter Property=\"BorderThickness\" TargetName=\"mainBorder\" Value=\"0,1,1,1\"/>\n                        </MultiDataTrigger>\n                        <MultiDataTrigger>\n                            <MultiDataTrigger.Conditions>\n                                <Condition Binding=\"{Binding IsEnabled, RelativeSource={RelativeSource Self}}\" Value=\"false\"/>\n                                <Condition Binding=\"{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}\" Value=\"Top\"/>\n                            </MultiDataTrigger.Conditions>\n                            <Setter Property=\"Opacity\" TargetName=\"contentPresenter\" Value=\"0.56\"/>\n                            <Setter Property=\"Background\" TargetName=\"mainBorder\" Value=\"{StaticResource TabItem.Disabled.Background}\"/>\n                            <Setter Property=\"BorderBrush\" TargetName=\"mainBorder\" Value=\"{StaticResource TabItem.Disabled.Border}\"/>\n                            <Setter Property=\"BorderThickness\" TargetName=\"innerBorder\" Value=\"1,1,1,0\"/>\n                            <Setter Property=\"BorderThickness\" TargetName=\"mainBorder\" Value=\"1,1,1,0\"/>\n                        </MultiDataTrigger>\n                        <MultiDataTrigger>\n                            <MultiDataTrigger.Conditions>\n                                <Condition Binding=\"{Binding IsSelected, RelativeSource={RelativeSource Self}}\" Value=\"false\"/>\n                                <Condition Binding=\"{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}\" Value=\"Left\"/>\n                            </MultiDataTrigger.Conditions>\n                            <Setter Property=\"BorderThickness\" TargetName=\"innerBorder\" Value=\"1,1,0,1\"/>\n                            <Setter Property=\"BorderThickness\" TargetName=\"mainBorder\" Value=\"1,1,0,1\"/>\n                        </MultiDataTrigger>\n                        <MultiDataTrigger>\n                            <MultiDataTrigger.Conditions>\n                                <Condition Binding=\"{Binding IsSelected, RelativeSource={RelativeSource Self}}\" Value=\"true\"/>\n                                <Condition Binding=\"{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}\" Value=\"Left\"/>\n                            </MultiDataTrigger.Conditions>\n                            <Setter Property=\"Panel.ZIndex\" Value=\"1\"/>\n                            <Setter Property=\"Opacity\" TargetName=\"innerBorder\" Value=\"1\"/>\n                            <Setter Property=\"BorderThickness\" TargetName=\"innerBorder\" Value=\"1,1,0,1\"/>\n                            <Setter Property=\"BorderThickness\" TargetName=\"mainBorder\" Value=\"1,1,0,1\"/>\n                        </MultiDataTrigger>\n                        <MultiDataTrigger>\n                            <MultiDataTrigger.Conditions>\n                                <Condition Binding=\"{Binding IsSelected, RelativeSource={RelativeSource Self}}\" Value=\"false\"/>\n                                <Condition Binding=\"{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}\" Value=\"Bottom\"/>\n                            </MultiDataTrigger.Conditions>\n                            <Setter Property=\"BorderThickness\" TargetName=\"innerBorder\" Value=\"1,0,1,1\"/>\n                            <Setter Property=\"BorderThickness\" TargetName=\"mainBorder\" Value=\"1,0,1,1\"/>\n                        </MultiDataTrigger>\n                        <MultiDataTrigger>\n                            <MultiDataTrigger.Conditions>\n                                <Condition Binding=\"{Binding IsSelected, RelativeSource={RelativeSource Self}}\" Value=\"true\"/>\n                                <Condition Binding=\"{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}\" Value=\"Bottom\"/>\n                            </MultiDataTrigger.Conditions>\n                            <Setter Property=\"Panel.ZIndex\" Value=\"1\"/>\n                            <Setter Property=\"Opacity\" TargetName=\"innerBorder\" Value=\"1\"/>\n                            <Setter Property=\"BorderThickness\" TargetName=\"innerBorder\" Value=\"1,0,1,1\"/>\n                            <Setter Property=\"BorderThickness\" TargetName=\"mainBorder\" Value=\"1,0,1,1\"/>\n                        </MultiDataTrigger>\n                        <MultiDataTrigger>\n                            <MultiDataTrigger.Conditions>\n                                <Condition Binding=\"{Binding IsSelected, RelativeSource={RelativeSource Self}}\" Value=\"false\"/>\n                                <Condition Binding=\"{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}\" Value=\"Right\"/>\n                            </MultiDataTrigger.Conditions>\n                            <Setter Property=\"BorderThickness\" TargetName=\"innerBorder\" Value=\"0,1,1,1\"/>\n                            <Setter Property=\"BorderThickness\" TargetName=\"mainBorder\" Value=\"0,1,1,1\"/>\n                        </MultiDataTrigger>\n                        <MultiDataTrigger>\n                            <MultiDataTrigger.Conditions>\n                                <Condition Binding=\"{Binding IsSelected, RelativeSource={RelativeSource Self}}\" Value=\"true\"/>\n                                <Condition Binding=\"{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}\" Value=\"Right\"/>\n                            </MultiDataTrigger.Conditions>\n                            <Setter Property=\"Panel.ZIndex\" Value=\"1\"/>\n                            <Setter Property=\"Opacity\" TargetName=\"innerBorder\" Value=\"1\"/>\n                            <Setter Property=\"BorderThickness\" TargetName=\"innerBorder\" Value=\"0,1,1,1\"/>\n                            <Setter Property=\"BorderThickness\" TargetName=\"mainBorder\" Value=\"0,1,1,1\"/>\n                        </MultiDataTrigger>\n                        <MultiDataTrigger>\n                            <MultiDataTrigger.Conditions>\n                                <Condition Binding=\"{Binding IsSelected, RelativeSource={RelativeSource Self}}\" Value=\"false\"/>\n                                <Condition Binding=\"{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}\" Value=\"Top\"/>\n                            </MultiDataTrigger.Conditions>\n                            <Setter Property=\"BorderThickness\" TargetName=\"innerBorder\" Value=\"1,1,1,0\"/>\n                            <Setter Property=\"BorderThickness\" TargetName=\"mainBorder\" Value=\"1,1,1,0\"/>\n                        </MultiDataTrigger>\n                        <MultiDataTrigger>\n                            <MultiDataTrigger.Conditions>\n                                <Condition Binding=\"{Binding IsSelected, RelativeSource={RelativeSource Self}}\" Value=\"true\"/>\n                                <Condition Binding=\"{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}\" Value=\"Top\"/>\n                            </MultiDataTrigger.Conditions>\n                            <Setter Property=\"Panel.ZIndex\" Value=\"1\"/>\n                            <Setter Property=\"Opacity\" TargetName=\"innerBorder\" Value=\"1\"/>\n                            <Setter Property=\"BorderThickness\" TargetName=\"innerBorder\" Value=\"1,1,1,0\"/>\n                            <Setter Property=\"BorderThickness\" TargetName=\"mainBorder\" Value=\"1,1,1,0\"/>\n                        </MultiDataTrigger>\n                    </ControlTemplate.Triggers>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n    </Style>\n    <Style x:Key='TabItemVerticalLeft' TargetType='{x:Type TabItem}' BasedOn='{StaticResource AuPanelsTabItemStyle}'>\n        <Setter Property='HeaderTemplate'>\n            <Setter.Value>\n                <DataTemplate>\n                    <ContentPresenter Content='{TemplateBinding Content}'>\n                        <ContentPresenter.LayoutTransform>\n                            <RotateTransform Angle='270' />\n                        </ContentPresenter.LayoutTransform>\n                    </ContentPresenter>\n                </DataTemplate>\n            </Setter.Value>\n        </Setter>\n        <Setter Property='Padding' Value='2,6,2,6' />\n    </Style>\n    <Style x:Key='TabItemVerticalRight' TargetType='{x:Type TabItem}' BasedOn='{StaticResource AuPanelsTabItemStyle}'>\n        <Setter Property='HeaderTemplate'>\n            <Setter.Value>\n                <DataTemplate>\n                    <ContentPresenter Content='{TemplateBinding Content}'>\n                        <ContentPresenter.LayoutTransform>\n                            <RotateTransform Angle='90' />\n                        </ContentPresenter.LayoutTransform>\n                    </ContentPresenter>\n                </DataTemplate>\n            </Setter.Value>\n        </Setter>\n        <Setter Property='Padding' Value='2,6,2,6' />\n    </Style>\n</ResourceDictionary>"
  },
  {
    "path": "Au.Controls/resources/XamlResources.cs",
    "content": "using System.Windows;\n\nnamespace Au.Controls;\n\npublic class XamlResources {\n#if IDE_LA\n\tstatic XamlResources() {\n\t\t//Dictionary = System.Windows.Markup.XamlReader.Load(typeof(XamlResources).Assembly.GetManifestResourceStream(\"Generic.xaml\")) as ResourceDictionary;\n\t\t\n\t\t//workaround for: at run time fails to load XAML if without `;assembly=Au.Controls`, but VS fails to compile if with.\n\t\tusing var stream = typeof(XamlResources).Assembly.GetManifestResourceStream(\"Generic.xaml\");\n\t\tstring xaml = new StreamReader(stream).ReadToEnd();\n\t\txaml = xaml.Replace(\"clr-namespace:Au.Controls\", \"clr-namespace:Au.Controls;assembly=Au.Controls\");\n\t\tusing var xmlReader = System.Xml.XmlReader.Create(new StringReader(xaml));\n\t\tDictionary = (ResourceDictionary)System.Windows.Markup.XamlReader.Load(xmlReader);\n\t}\n\t\n\tpublic static readonly ResourceDictionary Dictionary;\n#else\n\tpublic static readonly ResourceDictionary Dictionary = Application.LoadComponent(new(\"/Au.Controls;component/resources/generic.xaml\", UriKind.Relative)) as ResourceDictionary; //build action = Page (default)\n\t//public static ResourceDictionary Dictionary = System.Windows.Markup.XamlReader.Load(typeof(XamlResources).Assembly.GetManifestResourceStream(\"Au.Controls.Themes.Generic.xaml\")) as ResourceDictionary; //build action = embedded resource. Works, but debugger prints handled exception. And probably slower.\n#endif\n}\n"
  },
  {
    "path": "Au.Editor/App/App-resources.xaml",
    "content": "<!-- note: don't use local types (clr-namespace). Then VS compiles project 2 times. -->\n<ResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                    >\n        <Style TargetType=\"{x:Type Paragraph}\">\n            <Setter Property=\"Margin\" Value=\"4,6,4,6\"/>\n        </Style>\n        <Style x:Key=\"type\" TargetType=\"{x:Type Run}\">\n            <Setter Property=\"Foreground\" Value=\"#088\" />\n        </Style>\n        <Style x:Key=\"keyword\" TargetType=\"{x:Type Run}\">\n            <Setter Property=\"Foreground\" Value=\"#00f\" />\n        </Style>\n        <Style x:Key=\"string\" TargetType=\"{x:Type Run}\">\n            <Setter Property=\"Foreground\" Value=\"#a74\" />\n        </Style>\n        <Style x:Key=\"number\" TargetType=\"{x:Type Run}\">\n            <Setter Property=\"Foreground\" Value=\"#a40\" />\n        </Style>\n        <Style x:Key=\"namespace\" TargetType=\"{x:Type Run}\">\n            <Setter Property=\"Foreground\" Value=\"#777\" />\n        </Style>\n        <Style x:Key=\"comment\" TargetType=\"{x:Type Run}\">\n            <Setter Property=\"Foreground\" Value=\"#080\" />\n        </Style>\n        <Style x:Key=\"dot\" TargetType=\"{x:Type Run}\">\n            <Setter Property=\"Foreground\" Value=\"#ccc\" />\n        </Style>\n        <Style x:Key=\"dotSelected\" TargetType=\"{x:Type Run}\">\n            <Setter Property=\"Foreground\" Value=\"#c0f\" />\n        </Style>\n        <Style x:Key=\"codeSpan\" TargetType=\"{x:Type Span}\">\n            <Setter Property=\"Background\" Value=\"#f0f0f0\" />\n            <Setter Property=\"FontFamily\" Value=\"Consolas\" />\n        </Style>\n        <Style x:Key=\"codeBlock\" TargetType=\"{x:Type Paragraph}\">\n            <Setter Property=\"Background\" Value=\"#f0f0f0\" />\n            <Setter Property=\"FontFamily\" Value=\"Consolas\" />\n            <Setter Property=\"Margin\" Value=\"2,6,2,6\"/>\n            <Setter Property=\"Padding\" Value=\"2,3,2,3\"/>\n        </Style>\n        <Style x:Key=\"hilite\" TargetType=\"{x:Type Span}\">\n            <Setter Property=\"Background\" Value=\"#fca\" />\n        </Style>\n        <Style x:Key=\"div\" TargetType=\"{x:Type Paragraph}\">\n            <Setter Property=\"Margin\" Value=\"4,0,4,0\"/>\n        </Style>\n        <Style x:Key=\"header\" TargetType=\"{x:Type Paragraph}\">\n            <Setter Property=\"Margin\" Value=\"4,8,4,4\"/>\n            <Setter Property=\"FontWeight\" Value=\"Bold\"/>\n        </Style>\n        <LinearGradientBrush x:Key=\"overflowGradient\" StartPoint=\"0.5,0\" EndPoint=\"0.5,1\">\n            <GradientStop Color=\"#fffff0\"/>\n            <GradientStop Color=\"#fffcd0\" Offset=\"1\"/>\n        </LinearGradientBrush>\n        <Style x:Key=\"overload\" TargetType=\"{x:Type Paragraph}\">\n            <Setter Property=\"Margin\" Value=\"0\"/>\n            <Setter Property=\"Padding\" Value=\"4,1,4,2\"/>\n            <Setter Property=\"BorderThickness\" Value=\"0,0,0,1\" />\n            <Setter Property=\"BorderBrush\" Value=\"#ccc\" />\n            <Setter Property=\"Background\" Value=\"{StaticResource overflowGradient}\" />\n        </Style>\n        <Style x:Key=\"overloadSelected\" TargetType=\"{x:Type Paragraph}\" BasedOn=\"{StaticResource overload}\">\n            <Setter Property=\"Background\" Value=\"#f8f0a0\" />\n        </Style>\n        <Style x:Key=\"parameter\" TargetType=\"{x:Type Paragraph}\">\n            <Setter Property=\"Background\" Value=\"#dec\" />\n            <Setter Property=\"Margin\" Value=\"2,0,2,0\"/>\n            <Setter Property=\"Padding\" Value=\"2,0,2,2\"/>\n        </Style>\n        <Style TargetType=\"{x:Type Hyperlink}\">\n            <Setter Property=\"Focusable\" Value=\"False\" />\n        </Style>\n        <Style x:Key=\"divLink\" TargetType=\"{x:Type Hyperlink}\">\n            <Setter Property=\"TextDecorations\" Value=\"\" />\n            <Setter Property=\"Foreground\" Value=\"#000\" />\n            <Setter Property=\"Focusable\" Value=\"False\" />\n        </Style>\n\n        <Style TargetType=\"Viewbox\">\n            <Style.Triggers>\n                <Trigger Property=\"IsEnabled\" Value=\"False\">\n                    <Setter Property=\"Opacity\" Value=\"0.3\" />\n                </Trigger>\n            </Style.Triggers>\n        </Style>\n</ResourceDictionary>\n"
  },
  {
    "path": "Au.Editor/App/App.TrayIcon.cs",
    "content": "namespace LA;\n\nstatic partial class App {\n\tinternal static class TrayIcon {\n\t\tstatic IntPtr[] _icons;\n\t\tstatic bool _disabled = Au.Triggers.ActionTriggers.DisabledEverywhere;\n\t\tstatic wnd _wNotify;\n\t\t\n\t\tconst int c_msgNotify = Api.WM_APP + 1;\n\t\tstatic int s_msgTaskbarCreated;\n\t\t\n\t\tinternal static void Update_() {\n\t\t\tif (_icons == null) {\n\t\t\t\t_icons = new IntPtr[2];\n\t\t\t\t\n\t\t\t\ts_msgTaskbarCreated = WndUtil.RegisterMessage(\"TaskbarCreated\", uacEnable: true);\n\t\t\t\t\n\t\t\t\tWndUtil.RegisterWindowClass(\"Au.Editor.TrayNotify\", _WndProc);\n\t\t\t\t_wNotify = WndUtil.CreateWindow(\"Au.Editor.TrayNotify\", null, WS.POPUP, WSE.NOACTIVATE);\n\t\t\t\t//not message-only, because must receive s_msgTaskbarCreated and also used for context menu\n\t\t\t\t\n\t\t\t\tprocess.thisProcessExit += _ => {\n\t\t\t\t\tvar d = new Api.NOTIFYICONDATA(_wNotify);\n\t\t\t\t\tApi.Shell_NotifyIcon(Api.NIM_DELETE, d);\n\t\t\t\t};\n\t\t\t\t\n\t\t\t\t_Add(false);\n\t\t\t} else {\n\t\t\t\tvar d = new Api.NOTIFYICONDATA(_wNotify, Api.NIF_ICON) { hIcon = _GetIcon() };\n\t\t\t\tbool ok = Api.Shell_NotifyIcon(Api.NIM_MODIFY, d);\n\t\t\t\tDebug_.PrintIf(!ok, lastError.message);\n\t\t\t}\n\t\t}\n\t\t\n\t\tstatic void _Add(bool restore) {\n\t\t\tvar d = new Api.NOTIFYICONDATA(_wNotify, Api.NIF_MESSAGE | Api.NIF_ICON | Api.NIF_TIP /*| Api.NIF_SHOWTIP*/) { //need NIF_SHOWTIP if called NIM_SETVERSION(NOTIFYICON_VERSION_4)\n\t\t\t\tuCallbackMessage = c_msgNotify,\n\t\t\t\thIcon = _GetIcon(),\n\t\t\t\tszTip = App.AppName\n\t\t\t};\n\t\t\tif (Api.Shell_NotifyIcon(Api.NIM_ADD, d)) {\n\t\t\t\t//d.uFlags = 0;\n\t\t\t\t//d.uVersion = Api.NOTIFYICON_VERSION_4;\n\t\t\t\t//Api.Shell_NotifyIcon(Api.NIM_SETVERSION, d);\n\t\t\t\t\n\t\t\t\t//timer.after(2000, _ => Update(TrayIconState.Disabled));\n\t\t\t\t//timer.after(3000, _ => Update(TrayIconState.Running));\n\t\t\t\t//timer.after(4000, _ => Update(TrayIconState.Normal));\n\t\t\t} else if (!restore) { //restore when \"TaskbarCreated\" message received. It is also received when taskbar DPI changed.\n\t\t\t\tDebug_.Print(lastError.message);\n\t\t\t}\n\t\t}\n\t\t\n\t\tstatic IntPtr _GetIcon() {\n\t\t\tint i = _disabled ? 1 : 0;\n\t\t\tref IntPtr icon = ref _icons[i];\n\t\t\tif (icon == default) Api.LoadIconMetric(Api.GetModuleHandle(null), Api.IDI_APPLICATION + i, 0, out icon);\n\t\t\treturn icon;\n\t\t\t//Windows 10 on DPI change automatically displays correct non-scaled icon if it is from native icon group resource.\n\t\t\t//\tI guess then it calls GetIconInfoEx to get module/resource and extracts new icon from same resource.\n\t\t}\n\t\t\n\t\tstatic void _Notified(nint wParam, nint lParam) {\n\t\t\tint msg = Math2.LoWord(lParam);\n\t\t\t//if (msg != Api.WM_MOUSEMOVE) WndUtil.PrintMsg(default, msg, 0, 0);\n\t\t\tswitch (msg) {\n\t\t\tcase Api.WM_LBUTTONUP:\n\t\t\t\tShowWindow();\n\t\t\t\tbreak;\n\t\t\tcase Api.WM_RBUTTONUP:\n\t\t\t\t_ContextMenu();\n\t\t\t\tbreak;\n\t\t\t//case Api.WM_MBUTTONDOWN: //does not work on Win11\n\t\t\t//\tTriggersAndToolbars.DisableTriggers(null);\n\t\t\t//\tbreak;\n\t\t\t}\n\t\t}\n\t\t\n\t\tstatic unsafe nint _WndProc(wnd w, int m, nint wParam, nint lParam) {\n\t\t\t//WndUtil.PrintMsg(w, m, wParam, lParam);\n\t\t\tif (m == c_msgNotify) _Notified(wParam, lParam);\n\t\t\telse if (m == s_msgTaskbarCreated) _Add(true); //when explorer restarted or taskbar DPI changed\n\t\t\telse if (m == Api.WM_DESTROY) _Exit();\n\t\t\telse if (m == Api.WM_DISPLAYCHANGE) {\n\t\t\t\tTasks.OnWM_DISPLAYCHANGE();\n\t\t\t} else if (m == Api.WM_SETTINGCHANGE) {\n\t\t\t\tif (lParam != 0) {\n\t\t\t\t\tstring s = null;\n\t\t\t\t\ttry { s = new((char*)lParam); }\n\t\t\t\t\tcatch (Exception e1) { Debug_.Print(e1); }\n\t\t\t\t\t//print.it(\"WM_SETTINGCHANGE\", wParam, s);\n\t\t\t\t\tif (s == \"Environment\") App._envVarUpdater.WmSettingchange();\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\treturn Api.DefWindowProc(w, m, wParam, lParam);\n\t\t}\n\t\t\n\t\tstatic void _ContextMenu() {\n\t\t\tvar m = new popupMenu();\n\t\t\tm.AddCheck(\"Disable triggers\", check: _disabled, _ => TriggersAndToolbars.DisableTriggers(null));\n\t\t\tm.Separator();\n\t\t\tm.Add(\"Exit\", _ => _Exit());\n\t\t\tm.Show(PMFlags.AlignBottom | PMFlags.AlignCenterH);\n\t\t}\n\t\t\n\t\tstatic void _Exit() {\n\t\t\t_app.Shutdown();\n\t\t}\n\t\t\n\t\tpublic static bool Disabled {\n\t\t\tget => _disabled;\n\t\t\tset { if (value == _disabled) return; _disabled = value; Update_(); }\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/App/App.cs",
    "content": "using Au.Controls;\nusing System.Runtime.Loader;\nusing System.Windows;\nusing System.Windows.Threading;\n\n[assembly: AssemblyTitle(LA.App.AppName)]\n//more attributes in global2.cs\n\nnamespace LA;\n\nstatic partial class App {\n\tpublic const string AppName = \"LibreAutomate\";\n\t\n\tinternal static PrintServer PrintServer;\n\tpublic static AppSettings Settings;\n\tpublic static KMenuCommands Commands;\n\tpublic static FilesModel Model;\n\tpublic static RunningTasks Tasks;\n\tstatic EnvVarUpdater _envVarUpdater;\n\t\n\t//[STAThread] //no, makes command line etc slower. Will set STA later.\n\tstatic int Main(string[] args) {\n#if IDE_LA //test tools\n\t\t//\t\tprint.clear();\n\t\t\n\t\t//\t\targs = [\"/tool\", \"0\", \"Dwnd\", \"0\", \"0\"];\n\t\t//args = [\"/tool\", \"0\", \"Delm\"];\n\t\t//\t\t//args = [\"/tool\", \"0\", \"Duiimage\", \"0\"];\n\t\t//\t\t//args = [\"/tool\", \"0\", \"Docr\"];\n#endif\n\t\t\n#if DEBUG && !IDE_LA //note: not static ctor. Eg Settings used in scripts while creating some new parts of the app. The ctor would run there.\n\t\tif (!(args is [\"/tool\" or \"/pip\" or \"/dd\" or \"/mcp\", ..])) {\n\t\t\tprint.qm2.use = true;\n\t\t\t//print.clear(); \n\t\t\t//print.redirectConsoleOutput = true; //cannot be before the CommandLine.ProgramStarted1 call.\n\t\t}\n#endif\n\t\t\n\t\tscript.role = SRole.EditorExtension; //used by the folders class\n\t\tscript.name = AppName;\n\t\tThread.CurrentThread.Name = \"@Au.Main\";\n\t\t\n\t\tif (CommandLine.ProgramStarted1(args, out int exitCode)) return exitCode;\n\t\t\n\t\t//restart as admin if started as non-admin on admin user account\n\t\tif (args.Length > 0 && args[0] is \"/n\" or \"-n\") {\n\t\t\targs = args.RemoveAt(0);\n\t\t\t_raaResult = WinScheduler.RResult.ArgN;\n\t\t} else if (uacInfo.ofThisProcess.Elevation == UacElevation.Limited) {\n\t\t\tif (_RestartAsAdmin(args)) return 0;\n\t\t}\n\t\t\n\t\tprocess.IsLaProcess_ = true;\n\t\tprocess.IsLaMainThread_ = true; //[ThreadStatic]\n\t\tInitThisAppFoldersEtc_(args);\n\t\t\n\t\t//This would make startup faster eg 900 ms -> 700 ms.\n\t\t//\tUnfortunately can't use it. It tries to optimize editorExtension asseblies too. Then exception because can't load.\n\t\t//\tNever mind: possible workaround: load it in _Assembly_Resolving().\n\t\t//try {\n\t\t//\tvar poDir = folders.ThisAppDataLocal + \"optimization\";\n\t\t//\tif (!Directory.Exists(poDir)) Directory.CreateDirectory(poDir);\n\t\t//\tAssemblyLoadContext.Default.SetProfileOptimizationRoot(poDir);\n\t\t//\tAssemblyLoadContext.Default.StartProfileOptimization(\"main\");\n\t\t//}\n\t\t//catch (Exception ex) { Debug_.Print(ex); }\n\t\t\n\t\t//Debug_.PrintLoadedAssemblies(true, !true);\n\t\t\n\t\t//load settings in parallel, while Settings still not used. Saves 50 ms. After SetThisAppFoldersEtc_.\n\t\tTask task1 = Task.Run(() => {\n\t\t\tAppSettings.Load();\n\t\t\t//Debug_.PrintLoadedAssemblies(true, !true);\n\t\t});\n\t\t\n\t\t_Main(args, task1);\n\t\treturn 0;\n\t}\n\t\n\t[MethodImpl(MethodImplOptions.NoInlining)]\n\tstatic void _Main(string[] args, Task task1) {\n\t\t//Debug_.PrintLoadedAssemblies(true, !true);\n\t\t\n\t\tAppDomain.CurrentDomain.UnhandledException += _UnhandledException;\n\t\tprocess.ThisThreadSetComApartment_(ApartmentState.STA);\n\t\tprocess.thisProcessCultureIsInvariant = true;\n\t\tif (!Debugger.IsAttached) DebugTraceListener.Setup(usePrint: true);\n\t\tDirectory.SetCurrentDirectory(folders.ThisApp); //it is c:\\windows\\system32 when restarted as admin\n\t\tApi.SetSearchPathMode(Api.BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE | Api.BASE_SEARCH_PATH_PERMANENT); //let SearchPath search in current directory after system directories\n\t\tApi.SetErrorMode(Api.SEM_FAILCRITICALERRORS); //disable some error message boxes, eg when removable media not found; MSDN recommends too.\n\t\t\n\t\tif (CommandLine.ProgramStarted2(args)) return;\n\t\t\n#if IDE_LA\n\t\tPrintServer = new(miscInfo.isChildSession) { NoNewline = true };\n#else\n\t\tPrintServer = new(true) { NoNewline = true };\n#endif\n\t\tPrintServer.Start();\n#if DEBUG\n\t\tprint.qm2.use = !true;\n\t\t_RemindToBuildAllPlatforms();\n#endif\n\t\t_envVarUpdater = new();\n\t\t\n\t\tAssemblyLoadContext.Default.Resolving += _Assembly_Resolving;\n\t\tAssemblyLoadContext.Default.ResolvingUnmanagedDll += _UnmanagedDll_Resolving;\n\t\t\n\t\t_app = new() { ShutdownMode = ShutdownMode.OnMainWindowClose }; //before LoadWorkspace etc, because need _app.Dispatcher ASAP\n\t\tSynchronizationContext.SetSynchronizationContext(new DispatcherSynchronizationContext()); //some code may use await before Application.Run. Without this it would continue in a TP thread.\n\t\t\n\t\tTasks = new RunningTasks();\n\t\tScriptEditor.IconNameToXaml_ = DIcons.GetIconString;\n\t\t\n\t\ttask1.Wait(); //note: code executed before this must not use Settings\n\t\t\n\t\tFilesModel.LoadWorkspace(CommandLine.WorkspaceDirectory);\n\t\tCommandLine.ProgramLoaded();\n\t\tLoaded = AppState.LoadedWorkspace;\n\t\t\n\t\tTrayIcon.Update_();\n\t\t\n\t\t_app.MainWindow = Wmain = new MainWindow();\n\t\tif (!Settings.runHidden || CommandLine.StartVisible || (App.Settings.startVisibleIfNotAutoStarted && !CommandLine.AutoStarted)) ShowWindow();\n\t\t\n\t\t_timer = timer.every(1000, _TimerProc);\n\t\t\n\t\t_app.Dispatcher.InvokeAsync(() => {\n\t\t\tAppSettings.SetReloadModifiedExternally();\n\t\t\tModel.RunStartupScripts(false);\n\t\t\tif (miscInfo.isChildSession) PipIPC.StartPipeServerThread(); //after RunStartupScripts. If pipe server now will start a script, it will start after startup scripts with role editorExtension.\n\t\t});\n\t\t\n\t\tAppDomain.CurrentDomain.UnhandledException -= _UnhandledException;\n\t\tif (!Debugger.IsAttached) {\n\t\t\t_app.DispatcherUnhandledException += (_, e) => {\n\t\t\t\te.Handled = 1 == dialog.showError(\"Exception\", e.Exception.ToStringWithoutStack(), \"1 Continue|2 Exit\", DFlags.Wider, Hmain, e.Exception.ToString());\n\t\t\t};\n\t\t}\n\t\t\n\t\ttry {\n\t\t\t_app.Run();\n\t\t\t//Hidden app should start as fast as possible, because usually starts with Windows.\n\t\t\t//Tested with native message loop (before show window). Faster by 70 ms (240 vs 310 without the .NET startup time).\n\t\t\t//\tBut then problems. Eg cannot auto-create main window synchronously, because need to exit native loop and start WPF loop.\n\t\t}\n\t\tcatch (Exception e1) when (!Debugger.IsAttached) {\n\t\t\t_timer.Stop();\n\t\t\tWmain.Close();\n\t\t\tdialog.showError(\"Exception\", e1.ToString(), flags: DFlags.Wider);\n\t\t}\n\t\tfinally { MainFinally_(); }\n\t}\n\t\n\tinternal static void MainFinally_() {\n\t\tif (Loaded == AppState.Unloaded) return;\n\t\tLoaded = AppState.Unloading;\n\t\t\n\t\t_timer.Stop(); //eg if will show a Debug.Assert dialog\n\t\t\n\t\tvar fm = Model; Model = null;\n\t\tfm.Dispose(); //stops tasks etc\n\t\t\n\t\tLoaded = AppState.Unloaded;\n\t\t\n\t\tPrintServer.Stop();\n\t}\n\t\n\tclass _Application : Application {\n\t\tprotected override void OnSessionEnding(SessionEndingCancelEventArgs e) {\n\t\t\tbase.OnSessionEnding(e);\n\t\t\tWmain.Close();\n\t\t\tMainFinally_();\n\t\t\tprocess.thisProcessExitInvoke(); //OS terminates this process before or during process.thisProcessExit event\n\t\t}\n\t}\n\t\n\t//public static Application Instance => _app; //Application.Current\n\tstatic _Application _app;\n\t\n\t/// <summary>\n\t/// <b>Dispatcher</b> of main thread.\n\t/// </summary>\n\tpublic static Dispatcher Dispatcher => _app?.Dispatcher;\n\t\n\t/// <summary>\n\t/// true if called in the main thread.\n\t/// NOTE: don't use <c>Environment.CurrentManagedThreadId == 1</c>, it's not always 1 in the main thread.\n\t/// </summary>\n\tpublic static bool IsMainThread => process.IsLaMainThread_;\n\t\n\t/// <summary>\n\t/// Main window.\n\t/// Not loaded if never was visible.\n\t/// Use only in main thread; if other threads need <b>Dispatcher</b> of main thread, use <see cref=\"Dispatcher\"/>.\n\t/// </summary>\n\tpublic static MainWindow Wmain { get; private set; }\n\t\n\t/// <summary>\n\t/// Main window handle.\n\t/// defaul(wnd) if never was visible.\n\t/// </summary>\n\tpublic static wnd Hmain { get; internal set; }\n\t\n\tpublic static void ShowWindow() {\n\t\t//workaround for WPF bug: Window.Show pumps posted messages.\n\t\t//\tAnd crashes if a message causes to call Show again.\n\t\t//\tTo reproduce, let the program start hidden, and double-click the tray icon.\n\t\tif (_sw1) return;\n\t\t\n\t\t_sw1 = true;\n\t\tWmain.Show(); //auto-creates MainWindow if never was visible\n\t\t_sw1 = false;\n\t\tHmain.ActivateL(true);\n\t}\n\tstatic bool _sw1;\n\t\n\tstatic void _UnhandledException(object sender, UnhandledExceptionEventArgs e) {\n#if DEBUG\n\t\tprint.qm2.write(e.ExceptionObject);\n#else\n\t\tdialog.showError(\"Exception\", e.ExceptionObject.ToString(), flags: DFlags.Wider);\n#endif\n\t}\n\t\n\tprivate static Assembly _Assembly_Resolving(AssemblyLoadContext alc, AssemblyName an) {\n\t\tif (0 == an.Name.Starts(false, \"NuGet.\", \"Microsoft.Web.WebView2.\")) {\n\t\t\tvar dlls = _arDlls ??= filesystem.enumFiles(folders.ThisAppBS + \"Roslyn\", \"*.dll\", FEFlags.UseRawPath)\n\t\t\t\t.ToDictionary(o => o.Name[..^4], o => o.FullPath);\n\t\t\tif (dlls.TryGetValue(an.Name, out var path)) return alc.LoadFromAssemblyPath(path);\n\t\t\t\n\t\t\tif (_FindEditorExtensionInStack(out var asm)) return MiniProgram_.ResolveAssemblyFromRefPathsAttribute_(alc, an, asm);\n\t\t\t\n\t\t\tDebug_.Print(an.FullName);\n\t\t\t//print.qm2.write(an);\n\t\t}\n\t\treturn alc.LoadFromAssemblyPath(folders.ThisAppBS + an.Name + \".dll\");\n\t}\n\tstatic Dictionary<string, string> _arDlls;\n\t\n\tprivate static IntPtr _UnmanagedDll_Resolving(Assembly _, string name) {\n\t\t//print.it(\"_UnmanagedDll_Resolving\", name);\n\t\t\n\t\t//resolve native dlls used by meta pr libraries that are used by editorExtension scripts.\n\t\t//\tThese libraries are loaded in default context.\n\t\t//\teditorExtension assemblies are loaded in other contexts.\n\t\t//\tDlls directly used by editorExtension assemblies are resolved in RunAssembly.Run.\n\t\tif (_FindEditorExtensionInStack(out var asm)) return MiniProgram_.ResolveUnmanagedDllFromNativePathsAttribute_(name, asm);\n\t\treturn default;\n\t}\n\t\n\tstatic bool _FindEditorExtensionInStack(out Assembly asm) {\n\t\tvar st = new StackTrace(2); //not too slow\n\t\tfor (int i = 0; ; i++) {\n\t\t\tvar f = st.GetFrame(i); if (f == null) break;\n\t\t\tasm = f.GetMethod()?.DeclaringType?.Assembly;\n\t\t\tif (asm != null && asm.GetName().Name.Contains('|')) return true; //ScriptName|GUID\n\t\t}\n\t\tasm = null;\n\t\treturn false;\n\t}\n\t\n\tinternal static void InitThisAppFoldersEtc_(string[] args = null) {\n\t\tdialog.options.defaultTitle = AppName + \" message\";\n\t\tfolders.Editor = folders.ThisApp;\n\t\t\n\t\tif (args != null) {\n\t\t\tif (filesystem.exists(folders.ThisAppBS + \"data\")) {\n\t\t\t\tIsPortable = true;\n\t\t\t\tScriptEditor.IsPortable = true;\n\t\t\t\t\n\t\t\t\t//CONSIDER: when changed portable user (SID), delete folders.ThisAppDataLocal (\\data\\appLocal).\n\t\t\t\t//\tLA/Au currently uses it only for the icon cache.\n\t\t\t\t//\tBut some scripts may not want it.\n\t\t\t\t//\tProbably should delete folders.ThisAppTemp (\\data\\temp).\n\t\t\t\t\n\t\t\t\t//on ARM64, if Au.Editor.exe is x64, run Au.Editor-arm.exe instead\n\t\t\t\tif (!osVersion.isArm64Process && osVersion.isArm64OS) _RestartArm64(args);\n\t\t\t}\n\t\t\t\n\t\t\ttry {\n\t\t\t\t//create now if does not exist\n\t\t\t\t_ = folders.ThisAppDocuments;\n\t\t\t\t_ = folders.ThisAppDataLocal;\n\t\t\t\t_ = folders.ThisAppDataRoaming;\n#if IDE_LA\n\t\t\t\tfolders.ThisAppTemp = new(folders.Temp + \"LibreAutomate_2\");\n#else\n\t\t\t\tfolders.ThisAppTemp = new(folders.Temp + (miscInfo.isChildSession ? @\"LibreAutomate\\PiP\" : \"LibreAutomate\"));\n#endif\n\t\t\t\t//these are currently not used in editor and library, but may be used in scripts. Just prevent changing.\n\t\t\t\tfolders.noAutoCreate = true;\n\t\t\t\t_ = folders.ThisAppDataCommon; //Created by the installer (it requires admin rights). Should be used only for data installed by the installer.\n\t\t\t\t_ = folders.ThisAppImages;\n\t\t\t\tfolders.noAutoCreate = false;\n\t\t\t}\n\t\t\tcatch (Exception e1) {\n\t\t\t\tdialog.showError(\"Failed to set app folders\", e1.ToString());\n\t\t\t\tEnvironment.Exit(1);\n\t\t\t}\n\t\t\t\n\t\t} else {\n\t\t\tif (filesystem.exists(folders.ThisAppBS + \"data\")) {\n\t\t\t\tScriptEditor.IsPortable = true;\n\t\t\t}\n\t\t}\n\t}\n\t\n#if DEBUG\n\tstatic void _RemindToBuildAllPlatforms() {\n\t\tif (IsAtHome)\n\t\t\tif (filesystem.GetTime_(folders.ThisAppBS + @\"..\\Cpp\", out var t64)) {\n\t\t\t\tif (!filesystem.GetTime_(folders.ThisAppBS + @\"32\\AuCpp.dll\", out var t32) || t64 > t32) print.it(\"Note: may need to build Cpp project x86.\");\n\t\t\t\tif (!filesystem.GetTime_(folders.ThisAppBS + @\"64\\ARM\\AuCpp.dll\", out var tARM) || t64 > tARM) print.it(\"Note: may need to build Cpp project ARM64.\");\n\t\t\t}\n\t}\n#endif\n\t\n\tinternal static void OnMainWindowLoaded_() {\n\t\tif (IsPortable) {\n\t\t\tprint.it($\"<>Info: <help editor/Portable app>portable mode<>. Using <link {folders.PortableData_}>data<> folder.\");\n\t\t} else {\n\t\t\t//in v0.12 changed some spec folders from \"...\\Au\" to \"...\\LibreAutomate\\_script\"\n\t\t\t//\tFUTURE: delete this code.\n\t\t\t_Folder(folders.Documents, \"folders.ThisAppDocuments\");\n\t\t\t_Folder(folders.LocalAppData, \"folders.ThisAppDataLocal\");\n\t\t\tstatic void _Folder(string dir, string name) {\n\t\t\t\tvar dir1 = dir + @\"\\Au\";\n\t\t\t\tif (filesystem.exists(dir1, useRawPath: true)) {\n\t\t\t\t\tvar dir2 = dir + @\"\\LibreAutomate\\_script\";\n\t\t\t\t\tif (!filesystem.exists(dir2, useRawPath: true)) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tfilesystem.copy(dir1, dir2);\n\t\t\t\t\t\t\tprint.it($\"\"\"\n\t\t\t\t\t\t\t<>Note: in this program version has been changed <help>{name}<> path.\n\t\t\t\t\t\t\t\tOld: <explore>{dir1}<>. The folder is no longer used. You can delete it.\n\t\t\t\t\t\t\t\tNew: <explore>{dir2}<>. The old folder has been copied here.\n\t\t\t\t\t\t\t\"\"\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcatch { }\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tif (_raaResult is not (WinScheduler.RResult.None or WinScheduler.RResult.ArgN)) {\n\t\t\t\tvar s1 = _raaResult == WinScheduler.RResult.TaskNotFound ? null : $\"\\r\\n\\tFailed to run as administrator. Error: {_raaResult}.\";\n\t\t\t\tvar s = $\"\"\"\n<>Info: running not as administrator. <fold>\n\tWithout admin rights this program cannot automate admin windows etc. See <help articles/UAC>UAC<>.{s1}\n\tRestart as administrator: <+restartAdmin >now<>, <+restartAdmin /raa>now and always<> (later without UAC consent).\n\t</fold>\n\"\"\";\n\t\t\t\tprint.it(s);\n\t\t\t\tPanels.Output.Scintilla.AaTags.AddLinkTag(\"+restartAdmin\", k => Restart(k, admin: true));\n\t\t\t} else if (CommandLine.Raa) { //restarted because clicked link \"Restart as administrator: now and always\"\n\t\t\t\tvar name = IsAtHome ? \"_Au.Editor\" : \"Au.Editor\";\n\t\t\t\tbool ok = 0 == WinScheduler.CreateTaskWithoutTriggers(\"Au\", name, UacIL.System, process.thisExePath, \"/s $(Arg0)\", AppName);\n\t\t\t\tif (!ok) print.warning(@\"Failed to create Windows Task Scheduler task \\Au\\Au.Editor.\", -1);\n\t\t\t\t\n\t\t\t\t//note: don't create the task in the setup program. It requires a C++ dll, and it triggers AV false positives.\n\t\t\t}\n\t\t}\n\t}\n\t\n\tinternal static void OnMainWindowClosed_() {\n\t\t_timer.Stop();\n\t}\n\t\n\tstatic WinScheduler.RResult _raaResult;\n\t\n\tstatic bool _RestartAsAdmin(string[] args) {\n\t\tif (Debugger.IsAttached) return false; //very fast\n\t\tbool home = IsAtHome;\n\t\tstring sesId = process.thisProcessSessionId.ToS();\n\t\targs = args.Length == 0 ? [sesId] : args.InsertAt(0, sesId);\n\t\t(int pid, _raaResult) = WinScheduler.RunTask(\"Au\",\n\t\t\thome ? \"_Au.Editor\" : \"Au.Editor\", //in C:\\code\\au\\_ or <installed path>\n\t\t\tprocess.thisExePath, true, args);\n\t\tif (pid == 0) { //probably this program is not installed (no scheduled task)\n\t\t\tif (home) print.qm2.write(\"failed to run as admin\", _raaResult);\n\t\t\treturn false;\n\t\t}\n\t\t//Api.AllowSetForegroundWindow(pid); //fails and makes no sense\n\t\treturn true;\n\t}\n\t\n\t/// <summary>\n\t/// Restarts this program.\n\t/// </summary>\n\t/// <param name=\"commandLine\">Command line arguments to append. Don't use /n and /v because this func manages it.</param>\n\t/// <param name=\"admin\">UAC-elevate (verb runas).</param>\n\tpublic static void Restart(string commandLine = null, bool admin = false) {\n\t\tDebug.Assert(Loaded == AppState.LoadedUI);\n\t\tvar cl = Hmain.IsVisible ? \"/n /v /restart\" : \"/n /restart\";\n\t\tif (!commandLine.NE()) cl = cl + \" \" + commandLine;\n\t\tprocess.thisProcessExit += _ => { run.it(process.thisExePath, cl, admin ? RFlags.Admin : RFlags.InheritAdmin); };\n\t\t_app.Shutdown(); //closes window async, with no possibility to cancel\n\t}\n\t\n\tstatic unsafe void _RestartArm64(string[] args) {\n\t\tvar s = process.thisExePath;\n\t\tif (!s.Ends(@\"\\Au.Editor.exe\", true)) return;\n\t\ts = s.Insert(^4, \"-arm\");\n\t\tif (!filesystem.exists(s).File) return;\n\t\t\n\t\tvar sa = StringUtil.CommandLineFromArray(args.Where(o => !(o is \"/n\" or \"-n\" or \"/restart\")).Prepend(\"/restart\").Prepend(\"/n\").ToArray());\n\t\t\n\t\tvar ps = new ProcessStarter_(s, sa);\n\t\ttry { ps.Start(inheritUiaccess: true); }\n\t\tcatch { return; }\n\t\t\n\t\tEnvironment.Exit(0);\n\t}\n\t\n\t/// <summary>\n\t/// Timer with 1 s period.\n\t/// </summary>\n\tpublic static event Action Timer1s;\n\t\n\t/// <summary>\n\t/// Timer with 1 s period when main window hidden and 0.25 s period when visible.\n\t/// </summary>\n\tpublic static event Action Timer1sOr025s;\n\t\n\t/// <summary>\n\t/// Timer with 0.25 s period, only when main window visible.\n\t/// </summary>\n\tpublic static event Action Timer025sWhenVisible;\n\t\n\t/// <summary>\n\t/// Timer with 1 s period, only when main window visible.\n\t/// </summary>\n\tpublic static event Action Timer1sWhenVisible;\n\t\n\t/// <summary>\n\t/// True if Timer1sOr025s period is 0.25 s (when main window visible), false if 1 s (when hidden).\n\t/// </summary>\n\tpublic static bool IsTimer025 => _timerCounter > 0;\n\tstatic uint _timerCounter;\n\t\n\tstatic void _TimerProc(timer t) {\n\t\tTimer1sOr025s?.Invoke();\n\t\tbool needFast = Wmain.IsVisible;\n\t\tif (needFast != (_timerCounter > 0)) t.Every(needFast ? 250 : 1000);\n\t\tif (needFast) {\n\t\t\tTimer025sWhenVisible?.Invoke();\n\t\t\t_timerCounter++;\n\t\t} else _timerCounter = 0;\n\t\tif (0 == (_timerCounter & 3)) {\n\t\t\tTimer1s?.Invoke();\n\t\t\tif (needFast) Timer1sWhenVisible?.Invoke();\n\t\t}\n\t}\n\tstatic timer _timer;\n\t\n\tpublic static AppState Loaded;\n\t\n\tpublic static bool IsAtHome { get; } = Api.EnvironmentVariableExists(\"Au.Home<PC>\") && folders.ThisAppBS.Eqi(@\"C:\\code\\au\\_\\\");\n\t\n\tpublic static bool IsPortable { get; private set; }\n\t\n\t/// <summary>\n\t/// Calls <i>action</i> in try/catch, and manages filesystem sync (still not implemented). On exception prints message and returns false.\n\t/// </summary>\n\t/// <param name=\"paths\">Path of the destination file used in the filesystem operation. On move pass 2 paths: destination and source. This info is used by filesystem watchers to detect and ignore files saved by own process.</param>\n\t/// <param name=\"action\"></param>\n\tpublic static bool TryFileOperation(ReadOnlySpan<string> paths, Action action) {\n\t\ttry { action(); }\n\t\tcatch (Exception ex) { print.warning(ex); return false; }\n\t\treturn true;\n\t}\n\t\n\tpublic static async void CheckForUpdates(System.Windows.Controls.Button b = null) {\n\t\tbool forceNow = b != null;\n\t\tint day = (int)(DateTime.Now.Ticks / 864000000000);\n\t\tif (!forceNow && day == App.Settings.checkForUpdatesDay) return;\n\t\tApp.Settings.checkForUpdatesDay = day;\n\t\t\n\t\tif (forceNow) b.IsEnabled = false;\n\t\ttry {\n\t\t\tvar r = await internet.http.GetAsync(\"https://www.libreautomate.com/version.txt\");\n\t\t\tr.EnsureSuccessStatusCode();\n\t\t\tvar s = await r.Content.ReadAsStringAsync();\n\t\t\ts = s.Lines()[0];\n\t\t\tif (s != Au_.Version && System.Version.TryParse(Au_.Version, out var v1) && System.Version.TryParse(s, out var v2) && v2 > v1) {\n\t\t\t\t//Panels.Output.Scintilla.AaTags.AddLinkTag(\"+appUpdate\", _Update);\n\t\t\t\t//print.it($\"<>{AppNameShort} {s} is available. The installed version is {Au_.Version}.  [<+appUpdate>update...<>]  [<link https://github.com/qgindi/LibreAutomate/tree/master/Other/DocFX/_doc/changes>changes<>]  [<link https://www.libreautomate.com>website<>]\");\n\t\t\t\tprint.it($\"<>{AppName} {s} is available. The installed version is {Au_.Version}.  [<link https://github.com/qgindi/LibreAutomate/tree/master/Other/DocFX/_doc/changes>changes<>]  [<link https://www.libreautomate.com>download<>]\");\n\t\t\t} else if (forceNow) {\n\t\t\t\tdialog.showInfo(null, $\"{AppName} is up to date. Version {Au_.Version}.\", owner: Hmain);\n\t\t\t}\n\t\t}\n\t\tcatch (Exception e1) { if (forceNow) print.warning(e1); }\n\t\tfinally { if (forceNow) b.IsEnabled = true; }\n\t\t\n\t\t//static async Task _Update(string s) {\n\t\t//\tif (!dialog.showOkCancel(null, $\"This will download and install the new {AppNameShort} version.\")) return;\n\t\t//\ttry {\n\t\t\n\t\t//\t}\n\t\t//\tcatch (Exception e1) { print.warning(e1); }\n\t\t//}\n\t}\n}\n\nenum AppState {\n\t/// <summary>\n\t/// Before the first workspace fully loaded.\n\t/// </summary>\n\tLoading,\n\t\n\t/// <summary>\n\t/// The first workspace is fully loaded etc, but the main window not.\n\t/// </summary>\n\tLoadedWorkspace,\n\t\n\t/// <summary>\n\t/// The main window is loaded and either visible now or was visible and now hidden.\n\t/// </summary>\n\tLoadedUI,\n\t\n\t/// <summary>\n\t/// Unloading workspace, stopping everything.\n\t/// </summary>\n\tUnloading,\n\t\n\t/// <summary>\n\t/// Main window closed, workspace unloaded, everything stopped.\n\t/// </summary>\n\tUnloaded,\n}\n"
  },
  {
    "path": "Au.Editor/App/AppSettings.cs",
    "content": "using System.Text.Json;\nusing System.Text.Json.Serialization;\n\nnamespace LA;\n//note: used in ToolLand too.\n\n/// <summary>\n/// Application settings.\n/// folders.ThisAppDocuments + @\".settings\\Settings.json\"\n/// </summary>\nrecord AppSettings : JSettings {\n\t//This is loaded at startup and therefore must be fast.\n\t//\tNOTE: Don't use types that would cause to load UI dlls (WPF etc). Eg when it is a nested type and its parent class is a WPF etc control.\n\t//\tSpeed: first time 80 ms. Mostly to load/jit/etc dlls used by JsonSerializer. Later fast regardless of data size.\n\t\n\tpublic static JsonSerializerOptions SerializerOptions2 { get; } = new(JSettings.SerializerOptions) { PreferredObjectCreationHandling = JsonObjectCreationHandling.Populate, IgnoreReadOnlyFields = false };\n\t//note: don't modify JSettings.SerializerOptions. Would be unexpected for editorExtension scripts.\n\t\n\tpublic static void Load() { //in TP thread\n\t\tvar r = Load<AppSettings>(DirBS + \"Settings.json\", jsOpt: SerializerOptions2);\n\t\tr._Loaded();\n\t\tApp.Settings = r;\n\t}\n\t\n\tpublic static void SetReloadModifiedExternally() { //in main thread\n\t\tApp.Settings.ModifiedExternally += static () => {\n\t\t\tif (!App.Settings.Reload(out AppSettings r)) return;\n\t\t\tr._Loaded();\n\t\t\tDebug_.PrintIf(r.session.user != App.Settings.session.user);\n\t\t\tr.session.user = App.Settings.session.user;\n\t\t\tApp.Settings = r;\n\t\t};\n\t\t\n\t\t//Never mind: Currently only Settings.json is synced. There are many other settings/snippets/etc files. Some are separate in PiP. It's important to sync Settings.json if using PiP.\n\t}\n\t\n\tvoid _Loaded() {\n\t\tif (session_main == null) _InitSessionSettings();\n\t\tsession = miscInfo.isChildSession ? session_pip ??= new() : session_main;\n\t\t_NE(ref session.user) ??= Guid.NewGuid().ToString();\n\t\tsession.hotkeys ??= new();\n\t\t(font_output ??= new()).Normalize(\"Consolas\", 9);\n\t\t(font_find ??= new()).Normalize(\"Consolas\", 9);\n\t}\n\t\n\tstatic ref string _NE(ref string s) {\n\t\tif (s == \"\") s = null;\n\t\treturn ref s;\n\t}\n\t\n#if IDE_LA\n\tpublic static readonly string DirBS = folders.ThisAppDocuments + @\".settings_\\\";\n#else\n\tpublic static readonly string DirBS = folders.ThisAppDocuments + @\".settings\\\";\n#endif\n\t\n\t#region settings that are different in main and child (PiP) session\n\t\n\tpublic record session_t {\n\t\tpublic string user;\n\t\tpublic bool runHidden, startVisibleIfNotAutoStarted;\n\t\tpublic bool checkForUpdates;\n\t\tpublic int checkForUpdatesDay;\n\t\tpublic int wpfpreview_xy;\n\t\tpublic wndpos_t wndpos;\n\t\tpublic hotkeys_t hotkeys;\n\t}\n\tpublic session_t session_main, session_pip;\n\t[JsonIgnore] public session_t session;\n\t\n\t[JsonIgnore] public ref bool runHidden => ref session.runHidden;\n\t[JsonIgnore] public ref bool startVisibleIfNotAutoStarted => ref session.startVisibleIfNotAutoStarted;\n\t[JsonIgnore] public string userGuid => session.user;\n\t[JsonIgnore] public ref bool checkForUpdates => ref session.checkForUpdates;\n\t[JsonIgnore] public ref int checkForUpdatesDay => ref session.checkForUpdatesDay;\n\t[JsonIgnore] public ref int wpfpreview_xy => ref session.wpfpreview_xy;\n\t\n\t//saved positions of various windows\n\tpublic record struct wndpos_t {\n\t\tpublic string main, wnd, elm, uiimage, ocr, recorder, icons, symbol;\n\t}\n\t[JsonIgnore] public ref wndpos_t wndpos => ref session.wndpos;\n\t\n\t//Options > Hotkeys\n\tpublic record hotkeys_t {\n\t\tpublic string\n\t\t\ttool_quick = \"Ctrl+Shift+Q\",\n\t\t\ttool_wnd = \"Ctrl+Shift+W\",\n\t\t\ttool_elm = \"Ctrl+Shift+E\",\n\t\t\ttool_uiimage\n\t\t\t;\n\t}\n\t[JsonIgnore] public hotkeys_t hotkeys => session.hotkeys;\n\t\n\tvoid _InitSessionSettings() {\n\t\t//previously there were no session settings. Copy from the JSON root if need. Else existing users would lose their settings.\n\t\tif (user != null) {\n\t\t\ttry {\n\t\t\t\tvar file = DirBS + \"Settings.json\";\n\t\t\t\tif (filesystem.exists(file)) {\n\t\t\t\t\tsession_main = JsonSerializer.Deserialize<session_t>(filesystem.loadBytes(file), SerializerOptions2);\n\t\t\t\t}\n\t\t\t\tuser = null;\n\t\t\t}\n\t\t\tcatch (Exception ex) { Debug_.Print(ex); }\n\t\t}\n\t\t\n\t\tsession_main ??= new();\n\t}\n\t[JsonInclude] string user; //it's one of fields that previoulsy were in the JSON root but now are in a nested record. Now this field is used to detect the old format.\n\t\n\t#endregion\n\t\n\tpublic string workspace;\n\tpublic string[] recentWS;\n\t\n\t//When need a nested type, use record class. Everything works well; later can add/remove members like in main type.\n\t//Don't use record struct when need to set init values (now or in the future), because:\n\t//\t1. Older .NET versions don't support it, or have bugs.\n\t//\t\tEg .NET 6.0.3 throws \"InvalidCastException, Unable to cast object of type 'System.String' to type 'hotkeys_t'\" when `public record hotkeys_t()` (need the `()` when using default field values).\n\t//\t\t\tWorks if `public record hotkeys_t(string a, string b)`, but then need 'new(\"a\", \"b\")'; I don't like it etc.\n\t//\t2. If `public record hotkeys_t(string a, string b)`, and later added a new member like `, c = \"value\"`, the value is null/0. The deserializer uses the default ctor.\n\t//\t\tAlso the same happens if somebody deletes an existing member from JSON.\n\t//\t\tWorks well with `public record hotkeys_t()`, but cannot use it because of 1.\n\t//Note: deserializer always creates new object, even if default object created. Avoid custom ctors etc.\n\t//If like `public hotkeys_t hotkeys = new()`, creates new object 2 times: 1. explicit new(); 2. when deserializing. Also in JSON can be `= null`. Move the `new()` to _Loaded.\n\t//Tuple does not work well. New members are null/0. Also item names in file are like \"Item1\".\n\t\n\t//font of various UI parts\n\tpublic record font_t {\n\t\tpublic string name;\n\t\tpublic double size;\n\t\t\n\t\tstring _defName;\n\t\tdouble _defSize;\n\t\t\n\t\tinternal void Normalize(string defName, double defSize) {\n\t\t\t_defName = defName;\n\t\t\t_defSize = defSize;\n\t\t\tNormalize();\n\t\t}\n\t\t\n\t\tpublic void Normalize() {\n\t\t\tif (name.NE()) name = _defName;\n\t\t\tif (size == 0) size = _defSize; else size = Math.Clamp(size, 6, 30);\n\t\t}\n\t}\n\tpublic font_t font_output, font_find;\n\t\n\t//Options > Templates\n\tpublic int templ_use;\n\t//public int templ_flags;\n\t\n\t//Options > Other\n\tpublic string internetSearchUrl { get => field ?? \"https://www.google.com/search?q=\"; set { field = value.NullIfEmpty_(); } }\n\tpublic bool doc_web;\n\tpublic bool? comp_printCompiled = false;\n\t\n\t//code editor\n\tpublic bool edit_wrap, edit_noImages;\n\tpublic string edit_theme;\n\t\n\t//code info, autocorrection, formatting\n\tpublic bool ci_complGroup = true, ci_formatCompact = true, ci_formatTabIndent = true, ci_formatAuto = true, ci_semicolon = true;\n\tpublic bool ci_enterBeforeParen = true, ci_enterBeforeSemicolon = true, ci_tempRawEnter;\n\tpublic int ci_complParen { get => field; set { field = value.EnsureValid_(0, 2); } }\n\tpublic int ci_enterWith { get => field; set { field = value.EnsureValid_(0, 2); } }\n\tpublic int ci_rename;\n\t\n\t//AI\n\tpublic readonly DictionaryI_<string> ai_ak = new();\n\tpublic string ai_modelEmbed, ai_modelRerank/*, ai_modelChat*/;\n\t//public string ai_modelIconSearch;\n\tpublic bool ai_mcp_print;\n\t\n\t//panel Files\n\tpublic bool files_multiSelect;\n\t\n\t//file type icons\n\tpublic record struct icons_t {\n\t\tpublic string ft_script, ft_class, ft_folder, ft_folderOpen;\n\t}\n\tpublic icons_t icons;\n\t\n\t//panel Output\n\tpublic bool output_wrap, output_white;\n\t\n\t//panel Outline\n\tpublic byte outline_flags;\n\t\n\t//panel Open\n\tpublic byte openFiles_flags;\n\t\n\t//panel Mouse\n\tpublic bool mouse_singleLine;\n\tpublic int mouse_limitText = 100;\n\t\n\t//panel Debug\n\tpublic record debug_t {\n\t\tpublic bool stepIntoAll, noJMC, printVarCompact, activateLA;\n\t\tpublic double hVar = 150, hStack = 100;\n\t\tpublic byte breakT = 15; //flags: 1 enabled, 2 the exceptions list is active, 4 not exceptions in the list, 8 when caught\n\t\tpublic byte breakU = 1; //flags: 1 enabled\n\t\tconst string c_defNotExc = \"\"\"\nSystem.OperationCanceledException\nSystem.Threading.Tasks.TaskCanceledException\n\n\"\"\";\n\t\tpublic string breakListT = c_defNotExc, breakListU = c_defNotExc;\n\t\tpublic byte printEvents;\n\t}\n\tpublic readonly debug_t debug = new();\n\t\n\t//settings common to various tools\n\tpublic bool tools_pathUnexpand = true, tools_pathLnk;\n\t\n\t//DIcons\n\tpublic int dicons_listColor;\n\tpublic bool dicons_contrastUse;\n\tpublic string dicons_contrastColor = \"#E0E000\";\n\t\n\t//DPortable\n\tpublic string portable_dir;\n\tpublic int portable_check = -1;\n\tpublic string[] portable_skip;\n\t\n\t//DSnippets\n\tpublic Dictionary<string, HashSet<string>> ci_hiddenSnippets;\n\t\n\t//CiGoTo\n\tpublic record gotoAsm_t {\n\t\tpublic string repo, path, context;\n\t\tpublic bool csharp;\n\t\tpublic gotoAsm_t() { csharp = true; }\n\t}\n\tpublic Dictionary<string, gotoAsm_t> ci_gotoAsm;\n\tpublic int ci_gotoTab;\n\t\n\t//CiFindGo\n\tpublic bool ci_findgoDclick;\n\t\n\t//DInputRecorder\n\tpublic record recorder_t {\n\t\tpublic bool keys = true, text = true, text2 = true, mouse = true, wheel, drag, move;\n\t\tpublic int xyIn;\n\t\tpublic string speed = \"10\";\n\t}\n\tpublic readonly recorder_t recorder = new();\n\t\n\t//Delm\n\tpublic record delm_t {\n\t\tpublic string hk_capture = \"F3\", hk_insert = \"F4\", hk_smaller = \"Shift+F3\"; //for all tools\n\t\tpublic string def_wait, def_action;\n\t\tpublic bool? def_UIA;\n\t\tpublic int flags;\n\t}\n\tpublic readonly delm_t delm = new();\n\t\n\t//DOcr\n\tpublic record ocr_t {\n\t\tpublic string wLang, tLang, tCL, gKey, gFeat, gIC, mUrl, mKey;\n\t}\n\tpublic ocr_t ocr;\n\t\n\t//panel Find\n\tpublic string find_skip;\n\tpublic int find_searchIn, find_printSlow = 50;\n\tpublic bool find_parallel, find_case, find_word;\n\t\n\t//DNuget\n\tpublic bool nuget_noPrerelease;\n\t\n\t//other\n\tpublic int publish, export;\n\tpublic bool? minimalSDK;\n\tpublic string nilesoftShellDir;\n}\n\n/// <summary>\n/// Workspace settings.\n/// WorkspaceDirectory + @\"\\settings.json\"\n/// </summary>\nrecord WorkspaceSettings : JSettings {\n\tpublic static WorkspaceSettings Load(string jsonFile) => Load<WorkspaceSettings>(jsonFile);\n\t\n\tpublic record User(string guid) {\n\t\tpublic string startupScripts, gitUrl;\n\t\tpublic bool gitBackup;\n\t}\n\tpublic User[] users;\n\t\n\tpublic User CurrentUser {\n\t\tget {\n\t\t\tif (_cu == null) {\n\t\t\t\t_cu = users?.FirstOrDefault(o => o.guid == App.Settings.userGuid);\n\t\t\t\tif (_cu == null) {\n\t\t\t\t\t_cu = new User(App.Settings.userGuid);\n\t\t\t\t\tusers = users == null ? new[] { _cu } : users.InsertAt(0, _cu);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn _cu;\n\t\t}\n\t}\n\tUser _cu;\n\t\n\tpublic string ci_skipFolders;\n\t\n\tpublic string syncfs_skip = \"\"\"\n*\\.git\n*\\.vs\n*\\bin\n*\\obj\n\n\"\"\";\n}\n"
  },
  {
    "path": "Au.Editor/App/CommandLine.cs",
    "content": "\nnamespace LA;\n\nstatic class CommandLine {\n\t/// <summary>\n\t/// Processes the command line of this program. Called before any initialization.\n\t/// Returns true if this instance must exit.\n\t/// </summary>\n\tpublic static bool ProgramStarted1(ReadOnlySpan<string> args, out int exitCode) {\n#if IDE_LA && !true //test 2 LA instances, eg when developing filesystem sync for PiP\n\t\tif (args is [\"/second\"]) {\n\t\t\ttypeof(miscInfo).GetField(\"s_isChildSession\", BindingFlags.NonPublic | BindingFlags.Static).SetValue(null, (bool?)true);\n\t\t} else if (args.Length == 0) {\n\t\t\tTask.Run(() => {\n\t\t\t\t1.s();\n\t\t\t\tvar id2 = new ProcessStarter_(process.thisExePath, \"/second\").Start().pid;\n\t\t\t\tvar w1 = wnd.wait(-5, false, \"LibreAutomate\", of: WOwner.Process(process.thisProcessId));\n\t\t\t\tvar w2 = wnd.wait(-5, false, \"LibreAutomate\", of: WOwner.Process(id2));\n\t\t\t\tif (w1.Is0 || w2.Is0) return;\n\t\t\t\tw1.ShowNotMinMax();\n\t\t\t\tw2.ShowNotMinMax();\n\t\t\t\tvar r = screen.at.left().WorkArea;\n\t\t\t\tint k = r.right;\n\t\t\t\tr.Width /= 2;\n\t\t\t\tw1.MoveL(r);\n\t\t\t\tr.left = r.right; r.right = k;\n\t\t\t\tw2.MoveL(r);\n\t\t\t});\n\t\t}\n#endif\n\t\t\n\t\t//print.it(process.thisExeName, args.ToArray());\n\t\texitCode = 0; //note: Environment.ExitCode bug: the setter's set value is ignored and the process returns 0.\n\t\tif (args.Length > 0) {\n\t\t\tswitch (args[0]) {\n\t\t\tcase \"/s\":\n\t\t\t\texitCode = _RunEditorAsAdmin();\n\t\t\t\treturn true;\n\t\t\tcase \"/pip\":\n\t\t\t\texitCode = Pip.Run(args[1..]);\n\t\t\t\treturn true;\n\t\t\tcase \"/dd\":\n\t\t\t\tUacDragDrop.NonAdminProcess.MainDD(args[1..]);\n\t\t\t\treturn true;\n\t\t\tcase \"/tool\":\n\t\t\t\tToolLand.ToolProcess.Run(args[1..]);\n\t\t\t\treturn true;\n\t\t\tcase \"/mcp\":\n\t\t\t\tMcpServer.Run(args[1..]);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\t\n\t\t\tstring argN = null; if (args is [\"/n\" or \"-n\", ..]) { argN = args[0]; args = args[1..]; }\n\t\t\t\n\t\t\tif (args.Length > 0 && args[0] is var s && !s.NE()) {\n\t\t\t\tif (s[0] is '/' or '-') {\n\t\t\t\t} else if (!pathname.isFullPath(s)) {\n\t\t\t\t\texitCode = _LetEditorRunScript(args, argN);\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\t\n\t/// <summary>\n\t/// Processes the command line of this program. Called after partial initialization.\n\t/// Returns true if this instance must exit:\n\t/// \t1. If finds previous program instance; then sends the command line to it if need.\n\t/// \t2. If incorrect command line.\n\t/// </summary>\n\tpublic static bool ProgramStarted2(string[] args) {\n\t\tstring s = null;\n\t\tint cmd = 0; //1 open workspace, 2 import workspace, 3 import files, -5 reload workspace\n\t\tbool restarting = false;\n\t\tif (args.Length > 0) {\n\t\t\t//print.it(args);\n\t\t\ts = args[0];\n\t\t\tif (s is ['/' or '-', ..]) {\n\t\t\t\tfor (int i = 0; i < args.Length; i++) {\n\t\t\t\t\ts = args[i];\n\t\t\t\t\tbool good = s is ['/' or '-', ..];\n\t\t\t\t\tif (good) {\n\t\t\t\t\t\tswitch (s.AsSpan(1)) {\n\t\t\t\t\t\tcase \"v\":\n\t\t\t\t\t\t\tStartVisible = true;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase \"a\":\n\t\t\t\t\t\t\tAutoStarted = true;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase \"reload\":\n\t\t\t\t\t\t\tcmd = -5;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase \"restart\":\n\t\t\t\t\t\t\trestarting = true;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase \"raa\":\n\t\t\t\t\t\t\tRaa = true;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase \"test\":\n\t\t\t\t\t\t\tif (++i < args.Length) TestArg = args[i];\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase \"second\":\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tgood = false;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (!good) {\n\t\t\t\t\t\tdialog.showError(\"Unknown command line parameter\", s);\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\ts = null;\n\t\t\t} else { //one or more files\n\t\t\t\tif (args.Length == 1 && FilesModel.IsWorkspaceDirectoryOrZip_ShowDialogOpenImport(s, out cmd)) {\n\t\t\t\t\tswitch (cmd) {\n\t\t\t\t\tcase 1: WorkspaceDirectory = s; break;\n\t\t\t\t\tcase 2: _importWorkspace = s; break;\n\t\t\t\t\tdefault: return true;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tcmd = 3;\n\t\t\t\t\t_importFiles = args;\n\t\t\t\t}\n\t\t\t\tStartVisible = true;\n\t\t\t}\n\t\t}\n\t\t\n\t\t//single instance\n#if IDE_LA\n\t\ts_mutex = new Mutex(true, \"Au.Editor.Mutex.m3gVxcTJN02pDrHiQ00aSQ_IDE_LA\", out bool createdNew);\n#else\n\t\ts_mutex = new Mutex(true, \"Au.Editor.Mutex.m3gVxcTJN02pDrHiQ00aSQ\", out bool createdNew);\n#endif\n\t\tif (createdNew) return false;\n\t\tif (restarting) return Api.WaitForSingleObject(s_mutex.SafeWaitHandle.DangerousGetHandle(), 5000) is Api.WAIT_TIMEOUT or Api.WAIT_FAILED;\n\t\t\n\t\tvar w = wnd.findFast(null, ScriptEditor.c_msgWndClassName, true);\n\t\tif (!w.Is0) {\n\t\t\tw.Send(Api.WM_USER, 0, 1); //auto-creates, shows and activates main window\n\t\t\t\n\t\t\tif (cmd != 0) {\n\t\t\t\tThread.Sleep(100);\n\t\t\t\t\n\t\t\t\tif (cmd > 0) { //pass string\n\t\t\t\t\tif (cmd == 3) s = string.Join(\"\\0\", args); //import files\n\t\t\t\t\tWndCopyData.Send<char>(w, cmd, s);\n\t\t\t\t} else {\n\t\t\t\t\tw.Send(Api.WM_USER, -cmd);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\tstatic Mutex s_mutex; //GC\n\t\n\t/// <summary>\n\t/// null or argument after \"/test\".\n\t/// </summary>\n\tpublic static string TestArg;\n\t\n\t/// <summary>\n\t/// true if /v\n\t/// </summary>\n\tpublic static bool StartVisible;\n\t\n\t/// <summary>\n\t/// true if /a\n\t/// </summary>\n\tpublic static bool AutoStarted;\n\t\n\t/// <summary>\n\t/// true if /raa\n\t/// </summary>\n\tpublic static bool Raa;\n\t\n\t/// <summary>\n\t/// Called after loading workspace. Before executing startup scripts, adding tray icon and creating UI.\n\t/// </summary>\n\tpublic static void ProgramLoaded() {\n\t\tWndUtil.UacEnableMessages(Api.WM_COPYDATA, /*Api.WM_DROPFILES, 0x0049,*/ Api.WM_USER, Api.WM_CLOSE);\n\t\t//WM_COPYDATA, WM_DROPFILES and undocumented WM_COPYGLOBALDATA=0x0049 should enable drag-drop from lower UAC IL processes, but only through WM_DROPFILES/DragAcceptFiles, not OLE D&D.\n\t\t\n\t\tWndUtil.RegisterWindowClass(ScriptEditor.c_msgWndClassName, _WndProc);\n\t\t_msgWnd = WndUtil.CreateMessageOnlyWindow(ScriptEditor.c_msgWndClassName);\n\t\tscript.s_wndEditorMsg = _msgWnd;\n\t}\n\t\n\t/// <summary>\n\t/// Called from MainWindow.Loaded.\n\t/// </summary>\n\tpublic static void UILoaded() {\n\t\tif (_importWorkspace != null || _importFiles != null) {\n\t\t\tApp.Dispatcher.InvokeAsync(() => {\n\t\t\t\ttry {\n\t\t\t\t\tif (_importWorkspace != null) App.Model.ImportWorkspace(_importWorkspace);\n\t\t\t\t\telse App.Model.ImportFiles(_importFiles);\n\t\t\t\t}\n\t\t\t\tcatch (Exception ex) { print.warning(ex); }\n\t\t\t}, System.Windows.Threading.DispatcherPriority.ApplicationIdle);\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// null or workspace folder specified in command line.\n\t/// </summary>\n\tpublic static string WorkspaceDirectory;\n\t\n\tstatic string _importWorkspace;\n\tstatic string[] _importFiles;\n\t\n\tstatic wnd _msgWnd;\n\t\n\t/// <summary>\n\t/// The message-only window.\n\t/// Available after loading the first workspace.\n\t/// </summary>\n\tpublic static wnd MsgWnd => _msgWnd;\n\t\n\tstatic nint _WndProc(wnd w, int message, nint wparam, nint lparam) {\n\t\ttry {\n\t\t\tswitch (message) {\n\t\t\tcase Api.WM_USER:\n\t\t\t\tif (App.Loaded >= AppState.Unloading) return 0;\n\t\t\t\treturn _WmUser(wparam, lparam);\n\t\t\tcase Api.WM_COPYDATA:\n\t\t\t\tif (App.Loaded >= AppState.Unloading) return 0;\n\t\t\t\treturn _WmCopyData(wparam, lparam);\n\t\t\tcase RunningTasks.WM_TASK_ENDED: //WM_USER+900\n\t\t\t\tApp.Tasks.TaskEnded2(wparam, lparam);\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t}\n\t\tcatch (Exception ex) { print.warning(ex); return 0; }\n\t\t\n\t\treturn Api.DefWindowProc(w, message, wparam, lparam);\n\t}\n\t\n\tstatic nint _WmUser(nint wparam, nint lparam) {\n\t\tswitch (wparam) {\n\t\tcase 0: //ScriptEditor.MainWindow, etc\n\t\t\tif (lparam == 1) App.ShowWindow(); //else returns default(wnd) if never was visible\n\t\t\treturn App.Hmain.Handle;\n\t\tcase 1: //ScriptEditor.ShowMainWindow\n\t\t\tif (lparam == 0) lparam = App.Wmain.IsVisible ? 2 : 1; //toggle\n\t\t\tif (lparam == 1) App.ShowWindow();\n\t\t\telse App.Wmain.Hide_();\n\t\t\treturn 0;\n\t\tcase 3: //get wpf preview window saved xy\n\t\t\treturn App.Settings.wpfpreview_xy;\n\t\tcase 4: //save wpf preview window xy\n\t\t\tApp.Settings.wpfpreview_xy = (int)lparam;\n\t\t\tbreak;\n\t\tcase 5:\n\t\t\tMenus.File.Workspace.Reload_this_workspace();\n\t\t\tbreak;\n\t\tcase 10:\n\t\t\tUacDragDrop.AdminProcess.OnTransparentWindowCreated((wnd)lparam);\n\t\t\tbreak;\n\t\tcase 20: //Triggers.DisabledEverywhere\n\t\t\tTriggersAndToolbars.OnDisableTriggers();\n\t\t\tbreak;\n\t\t//case 21: //end task by process id. Currently not used.\n\t\t//\tApp.Tasks.TaskFromProcessId((int)lparam)?.End(false);\n\t\t//\tbreak;\n\t\tcase 30: //script.debug()\n\t\t\treturn _ScriptDebug((int)lparam);\n\t\t}\n\t\treturn 0;\n\t\t\n\t\tstatic nint _ScriptDebug(int processId) {\n\t\t\tif (processId == process.thisProcessId) { //a process can't debug itself\n#if IDE_LA //to debug this second LA process use the primary LA process\n\t\t\t\tvar w = wnd.findFast(null, ScriptEditor.c_msgWndClassName, true);\n\t\t\t\tif (w == MsgWnd) w = wnd.findFast(null, ScriptEditor.c_msgWndClassName, true, w);\n\t\t\t\treturn w.Send(Api.WM_USER, 30, processId);\n#else\n\t\t\t\treturn 0;\n#endif\n\t\t\t}\n\t\t\tif (!App.Wmain.IsVisible) App.ShowWindow(); else if (!App.Hmain.IsActive) { App.Hmain.TaskbarButton.Flash(3); /*timer.after(10000, _ => App.Hmain.TaskbarButton.Flash(0));*/ }\n\t\t\treturn Panels.Debug.Attach(processId) ? 1 : 0;\n\t\t}\n\t}\n\t\n\tstatic nint _WmCopyData(nint wparam, nint lparam) {\n\t\tvar c = new WndCopyData(lparam);\n\t\tint action = Math2.LoWord(c.DataId), action2 = Math2.HiWord(c.DataId);\n\t\tbool isString = action < 100;\n\t\tstring s = isString ? c.GetString() : null;\n\t\tbyte[] b = isString ? null : c.GetBytes();\n\t\tswitch (action) {\n\t\tcase 1: //command line (ProgramStarted2)\n\t\t\tFilesModel.LoadWorkspace(s);\n\t\t\tbreak;\n\t\tcase 2: //command line (ProgramStarted2)\n\t\t\tApp.Model.ImportWorkspace(s);\n\t\t\tbreak;\n\t\tcase 3: //command line (ProgramStarted2)\n\t\t\tApi.ReplyMessage(1); //avoid 'wait' cursor while we'll show dialog\n\t\t\tApp.Model.ImportFiles(s.Split('\\0'));\n\t\t\tbreak;\n\t\tcase 4: //ScriptEditor.Open\n\t\t\tApi.ReplyMessage(1);\n\t\t\t_OpenFile();\n\t\t\tbreak;\n\t\tcase 5 or 6: //script.end(name) or script.isRunning(name)\n\t\t\treturn _ScriptAction(action);\n\t\tcase 10: //ScriptEditor.GetIcon\n\t\t\ts = DIcons.GetIconString(s, (EGetIcon)action2);\n\t\t\treturn s == null ? 0 : WndCopyData.Return<char>(s, wparam);\n\t\tcase 11: //ScriptEditor.InvokeCommand\n\t\t\treturn Menus.Invoke(s, false, (int)wparam);\n\t\tcase 12: //ScriptEditor.GetCommandState\n\t\t\treturn Menus.Invoke(s, true, (int)wparam);\n\t\t//case 13: //ScriptEditor.Folders (rejected)\n\t\t//\ts = string.Join('|', (string)folders.ThisAppDocuments, (string)folders.ThisAppDataLocal, (string)folders.ThisAppTemp);\n\t\t//\treturn WndCopyData.Return<char>(s, wparam);\n\t\tcase 14: //ScriptEditor.GetFileInfo\n\t\t\treturn _GetFileInfo(s) is byte[] r1 ? WndCopyData.Return<byte>(r1, wparam) : 0;\n\t\tcase 15: //get styling for tools\n\t\t\treturn WndCopyData.Return<byte>(CiUtil.GetScintillaStylingBytes8(s), wparam);\n\t\tcase 16:\n\t\t\treturn _GetCodeFileText(s, out var text1) ? WndCopyData.Return<char>(text1, wparam) : 0;\n\t\tcase 17:\n\t\t\tInsertCode.Statements(System.Text.Json.JsonSerializer.Deserialize<InsertCode.InsertCodeParams>(s));\n\t\t\treturn 1;\n\t\tcase 18:\n\t\t\tPanelHelp.OpenRecipe(s);\n\t\t\treturn 1;\n\t\tcase 19:\n\t\t\treturn PathInfo.FromWindow((wnd)s.ToInt()) is {  } pathInfo ? WndCopyData.Return<char>(pathInfo.FormatCode(PathCode.Run), wparam) : 0;\n\t\tcase 100: //script.run/runWait\n\t\tcase 101: //run script from command line\n\t\tcase 102: //script.runInPip\n\t\t\treturn _RunScript();\n\t\tcase 110: //received from our non-admin drop-target process on OnDragEnter\n\t\t\treturn UacDragDrop.AdminProcess.DragEvent((int)wparam, b);\n\t\t//case 200: //run any action. Use when App.Dispatcher may be still unavailable.\n\t\t//\treturn 0;\n\t\tdefault:\n\t\t\tDebug_.Print(\"bad action\");\n\t\t\treturn 0;\n\t\t}\n\t\treturn 1;\n\t\t\n\t\tnint _RunScript() {\n\t\t\tint mode = (int)wparam; //1 - wait, 3 - wait and get script.writeResult output, 4 restarting (set meta ifRunning run)\n\t\t\tvar d = Serializer_.Deserialize(b);\n\t\t\tstring file = d[0];\n\t\t\tstring[] args = d[1];\n\t\t\tstring pipeName = action == 102 ? null : (string)d[2];\n\t\t\tint schedPid = action == 101 ? d[3] : 0; //if scheduled task, it is the temp LA process, else 0\n\t\t\tbool scheduled = schedPid != 0;\n\t\t\t\n\t\t\tvar f = App.Model?.FindCodeFile(file);\n\t\t\tif (f == null) {\n\t\t\t\tif (action == 101) print.it($\"{(scheduled ? \"Scheduled task\" : \"Command line\")}: script '{file}' not found.\"); //else the caller script will throw exception\n\t\t\t\treturn (int)script.RunResult_.notFound;\n\t\t\t}\n\t\t\t\n\t\t\tif (action == 102) {\n\t\t\t\tPipIPC.RunScriptInPip(f, args);\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\t\n\t\t\t//options can be specified in args[0] like \"[[name1=value1|name2=value2]]\"\n\t\t\t//MCIfRunning? ifRunning = null;\n\t\t\t//if (args.FirstOrDefault() is string k && k.Like(\"[[*]]\")) {\n\t\t\t//\tforeach (var v in k.Segments(\"|\", SegFlags.NoEmpty, 2..^2)) {\n\t\t\t//\t\tbool good = false;\n\t\t\t//\t\tif (k.IndexOf('=', v.start, v.Length) is int i && i > 0) {\n\t\t\t//\t\t\tstring sn = k[v.start..i], sv = k[++i..v.end];\n\t\t\t//\t\t\tswitch (sn) {\n\t\t\t//\t\t\tcase \"ifRunning\":\n\t\t\t//\t\t\t\tif (good = Enum.TryParse(sv, out MCIfRunning ir)) ifRunning = ir;\n\t\t\t//\t\t\t\tbreak;\n\t\t\t//\t\t\t}\n\t\t\t//\t\t}\n\t\t\t//\t\tif (!good) { print.it($\"<>Cannot start script {f.SciLink()}. Error in args[0]: {k}.\"); return (int)script.RunResult_.failed; }\n\t\t\t//\t}\n\t\t\t//\targs = args.RemoveAt(0);\n\t\t\t//}\n\t\t\t\n\t\t\tint r = CompileRun.CompileAndRun(true, f, args, noDefer: 0 != (mode & 1), wrPipeName: pipeName, ifRunning: 0 != (mode & 4) ? MCIfRunning.run : null);\n\t\t\t\n\t\t\tif (scheduled && r > 0) { //enable Task Scheduler to end script process\n\t\t\t\trun.thread(() => {\n\t\t\t\t\tusing var hp = Handle_.OpenProcess(schedPid, Api.SYNCHRONIZE);\n\t\t\t\t\tif (0 == Api.WaitForSingleObject(hp, -1)) {\n\t\t\t\t\t\tThread.Sleep(10);\n\t\t\t\t\t\tApp.Tasks.TaskFromProcessId(r)?.End(false);\n\t\t\t\t\t}\n\t\t\t\t}, sta: false);\n\t\t\t}\n\t\t\t\n\t\t\treturn r;\n\t\t}\n\t\t\n\t\tnint _ScriptAction(int action) {\n\t\t\tif (App.Model.Find(s) is FileNode f) {\n\t\t\t\treturn action switch {\n\t\t\t\t\t5 => App.Tasks.EndTasksOf(f, (int)wparam) ? 1 : 2,\n\t\t\t\t\t6 => App.Tasks.IsRunning(f) ? 1 : 0,\n\t\t\t\t\t_ => 0\n\t\t\t\t};\n\t\t\t}\n\t\t\tprint.warning($\"File not found: '{s}'.\", -1);\n\t\t\treturn 0;\n\t\t}\n\t\t\n\t\tvoid _OpenFile() {\n\t\t\tvar a = s.Split('|'); //\"file|line|offset\". line and/or offset is empty if was null.\n\t\t\ts = a[0];\n\t\t\tif (App.Model.Find(s) is FileNode f1) {\n\t\t\t\tint line = a[1].NE() ? -1 : a[1].ToInt() - 1;\n\t\t\t\tint offset = a[2].NE() ? -1 : a[2].ToInt();\n\t\t\t\tApp.Model.OpenAndGoTo(f1, line, offset);\n\t\t\t} else print.warning($\"File not found: '{s}'.\", -1);\n\t\t}\n\t\t\n\t\tbyte[] _GetFileInfo(string s) {\n\t\t\tint flags = s.ToInt(0, out int end);\n\t\t\tvar f = end == s.Length ? Panels.Editor.ActiveDoc?.EFile : App.Model.Find(s[++end..], FNFind.File);\n\t\t\tif (f != null) {\n\t\t\t\tvar kind = f.FileType switch { FNType.Script => EFileKind.Script, FNType.Class => EFileKind.Class, _ => EFileKind.Other };\n\t\t\t\tstring text = null; if (0 != (flags & 1)) f.GetCurrentText(out text, null);\n\t\t\t\treturn Serializer_.Serialize(f.ItemPath, text, (int)kind, (int)f.Id, f.FilePath, App.Model.WorkspaceDirectory);\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\t\n\t\tstatic bool _GetCodeFileText(string name, out string text) {\n\t\t\ttext = null;\n\t\t\tvar f = name.Eqi(\"global.cs\") ? App.Model.FindGlobalCs() : App.Model.FindCodeFile(name);\n\t\t\treturn f?.GetCurrentText(out text) == true;\n\t\t}\n\t}\n\t\n\t//Called when command line starts with \"/s\". This process is running as SYSTEM in session 0.\n\t//This process is started by the Task Scheduler task installed by the setup program. The task started by App._RestartAsAdmin.\n\t[MethodImpl(MethodImplOptions.NoOptimization)]\n\tstatic unsafe int _RunEditorAsAdmin() {\n\t\tvar s1 = Api.GetCommandLine();\n\t\t//_MBox(new string(s1));\n\t\t//Normally it is like \"C:\\...\\Au.Editor.exe /s sessionId\" or \"C:\\...\\Au.Editor.exe /s sessionId arguments\",\n\t\t//\tbut if started from Task Scheduler it is \"C:\\...\\Au.Editor.exe /s $(Arg0)\".\n\t\t\n\t\tint len = Ptr_.Length(s1) + 1;\n\t\tvar span = new Span<char>(s1, len);\n\t\tvar s2 = span.ToArray();\n\t\tint i = span.IndexOf(\"/s\") + 1; //info: it's safe. Can't be \"C:/s/...\" because the scheduled task wasn't created like this.\n\t\ts2[i++] = 'n'; // /n - don't try to restart as admin\n\t\t\n\t\t//get session id\n\t\tchar* se = null;\n\t\tint sesId = Api.strtoi(s1 + i, &se);\n\t\tif (se != s1 + i) { //remove the session id argument\n\t\t\tif (*se == 0) s2[i] = '\\0'; //no more arguments\n\t\t\telse for (int j = (int)(se - s1); j < len;) s2[i++] = s2[j++];\n\t\t} else { //$(Arg0) not replaced. Probably started from Task Scheduler.\n\t\t\ts2[i] = '\\0';\n\t\t\tsesId = Api.WTSGetActiveConsoleSessionId();\n\t\t\tif (sesId < 1) return 1;\n\t\t}\n\t\t//_MBox(new string(s2));\n\t\t\n\t\tif (!_Api.WTSQueryUserToken(sesId, out var hToken)) return 2;\n\t\tif (_Api.GetTokenInformation(hToken, Api.TOKEN_INFORMATION_CLASS.TokenLinkedToken, out var hToken2, sizeof(nint), out _)) { //fails if non-admin user or if UAC turned off\n\t\t\tApi.CloseHandle(hToken);\n\t\t\thToken = hToken2;\n\t\t\t\n\t\t\t//rejected: add uiAccess.\n\t\t\t//DWORD uiAccess=1; Api.SetTokenInformation(hToken, TokenUIAccess, &uiAccess, 4);\n\t\t\t\n\t\t\t//With uiAccess works better in some cases, eg on Win8 can set a window on top of metro.\n\t\t\t//Cannot use it because of SetParent API bug: fails if the new parent window is topmost and the old parent isn't (or is 0).\n\t\t\t//\tSetParent is extensively used by winforms and WPF, to move parked controls from the parking window (message-only) to the real parent window.\n\t\t\t//\tThen, if the window is topmost, SetParent fails and the control remains hidden on the parking window.\n\t\t\t//\tAlso, if it is the first control in form, form is inactive and like disabled. Something activates the parking window instead.\n\t\t\t//It seems uiAccess mode is little tested by Microsoft and little used by others. I even did not find anything about this bug.\n\t\t\t//I suspect this mode also caused some other anomalies.\n\t\t\t//\tEg sometimes activating the editor window stops working normally: it becomes active but stays behind other windows.\n\t\t\t\n\t\t} //else MBox(L\"GetTokenInformation failed\");\n\t\t\n\t\tif (!_Api.CreateEnvironmentBlock(out var eb, hToken, false)) return 3;\n\t\t\n\t\tvar si = new Api.STARTUPINFO { cb = sizeof(Api.STARTUPINFO), dwFlags = Api.STARTF_FORCEOFFFEEDBACK };\n\t\tvar desktop = stackalloc char[] { 'w', 'i', 'n', 's', 't', 'a', '0', '\\\\', 'd', 'e', 'f', 'a', 'u', 'l', 't', '\\0' }; //\"winsta0\\\\default\"\n\t\tsi.lpDesktop = desktop;\n\t\t\n\t\tif (!_Api.CreateProcessAsUser(hToken, null, s2, null, null, false, Api.CREATE_UNICODE_ENVIRONMENT, eb, null, si, out var pi)) {\n\t\t\t_MBox(\"CreateProcessAsUserW: \" + lastError.message);\n\t\t\treturn 4;\n\t\t}\n\t\t\n\t\tApi.CloseHandle(pi.hThread);\n\t\tApi.CloseHandle(pi.hProcess);\n\t\t//Api.AllowSetForegroundWindow(pi.dwProcessId); //fails\n\t\t\n\t\t_Api.DestroyEnvironmentBlock(eb);\n\t\t\n\t\tApi.CloseHandle(hToken);\n\t\treturn 0;\n\t}\n\t\n\t/// <summary>\n\t/// Shows message box in interactive session. Called from session 0.\n\t/// </summary>\n\t[Conditional(\"DEBUG\")]\n\tstatic void _MBox(object o) {\n#if DEBUG\n\t\tvar s = o.ToString();\n\t\tvar title = \"Debug\";\n\t\t_Api.WTSSendMessage(default, Api.WTSGetActiveConsoleSessionId(), title, title.Length * 2, s, s.Length * 2, _Api.MB_TOPMOST | _Api.MB_SETFOREGROUND, 0, out _, true);\n#endif\n\t}\n\t\n\t/// <summary>\n\t/// Finds the message-only window. Starts editor if not running. In any case waits for the window max 15 s.\n\t/// </summary>\n\tstatic bool _EnsureEditorRunningAndGetMsgWindow(out wnd wMsg, string args) {\n\t\twMsg = default;\n\t\tfor (int i = 0; i < 1000; i++) { //if we started editor process, wait until it fully loaded, then it creates the message-only window\n\t\t\twMsg = wnd.findFast(null, ScriptEditor.c_msgWndClassName, true);\n\t\t\tif (!wMsg.Is0) return true;\n\t\t\tif (i == 0) {\n\t\t\t\tvar ps = new ProcessStarter_(process.thisExePath, args, rawExe: true);\n\t\t\t\tif (!ps.StartL(out var pi)) break;\n\t\t\t\tApi.AllowSetForegroundWindow(pi.dwProcessId);\n\t\t\t\tpi.Dispose();\n\t\t\t\t//note: the process will restart as admin if started from non-admin process, unless the program isn't installed correctly\n\t\t\t}\n\t\t\tThread.Sleep(15);\n\t\t}\n\t\treturn false;\n\t}\n\t\n\t//Initially for this was used native exe. Rejected because of AV false positives.\n\t//\tSpeed with native exe 50 ms, now 85 ms. Never mind.\n\tstatic unsafe int _LetEditorRunScript(ReadOnlySpan<string> args, string argN) {\n\t\tif (!_EnsureEditorRunningAndGetMsgWindow(out wnd w, argN)) return (int)script.RunResult_.noEditor;\n\t\t\n\t\t//If script name has prefix *, need to wait until script process ends.\n\t\t//\tAlso auto-detect whether need to write script.writeResult to stdout.\n\t\tvar file = args[0];\n\t\tstring[] scriptArgs = args[1..].ToArray();\n\t\t\n\t\tif (file[0] == '>') { //set by DSchedule\n\t\t\treturn script.RunCL_(w, 1, file[1..], scriptArgs, null, process.thisProcessId);\n\t\t}\n\t\t\n\t\tint mode = 0; //1 - wait, 3 - wait and get script.writeResult output\n\t\tif (file[0] == '*') {\n\t\t\tfile = file[1..];\n\t\t\tmode |= 1;\n\t\t\tif ((default != Api.GetStdHandle(Api.STD_OUTPUT_HANDLE)) //redirected stdout\n\t\t\t\t|| _Api.AttachConsole(_Api.ATTACH_PARENT_PROCESS) //parent process is console\n\t\t\t\t) mode |= 2;\n\t\t}\n\t\t\n\t\tif (0 == (mode & 2)) return script.RunCL_(w, mode, file, scriptArgs, null);\n\t\t\n\t\treturn script.RunCL_(w, mode, file, scriptArgs, static o => {\n\t\t\tvar a = Encoding.UTF8.GetBytes(o);\n\t\t\tbool ok = Api.WriteFile2(Api.GetStdHandle(Api.STD_OUTPUT_HANDLE), a, out int n);\n\t\t\tif (!ok || n != a.Length) throw new AuException(0);\n\t\t\t//tested: 100_000_000 bytes OK.\n\t\t});\n\t\t//note: Console.Write does not write UTF8 if redirected. Console.OutputEncoding and SetConsoleOutputCP fail.\n\t\t//note: in cmd execute this to change cmd console code page to UTF-8: chcp 65001\n\t}\n\t\n\tstatic unsafe class _Api {\n\t\t[DllImport(\"wtsapi32.dll\")]\n\t\tinternal static extern bool WTSQueryUserToken(int SessionId, out IntPtr phToken);\n\t\t\n\t\t[DllImport(\"advapi32.dll\")]\n\t\tinternal static extern bool GetTokenInformation(IntPtr TokenHandle, Api.TOKEN_INFORMATION_CLASS TokenInformationClass, out IntPtr TokenInformation, int TokenInformationLength, out int ReturnLength);\n\t\t\n\t\t[DllImport(\"userenv.dll\")]\n\t\tinternal static extern bool CreateEnvironmentBlock(out IntPtr lpEnvironment, IntPtr hToken, bool bInherit);\n\t\t\n\t\t[DllImport(\"userenv.dll\")]\n\t\tinternal static extern bool DestroyEnvironmentBlock(IntPtr lpEnvironment);\n\t\t\n\t\t[DllImport(\"advapi32.dll\", EntryPoint = \"CreateProcessAsUserW\", SetLastError = true)]\n\t\tinternal static extern bool CreateProcessAsUser(IntPtr hToken, string lpApplicationName, char[] lpCommandLine, void* lpProcessAttributes, void* lpThreadAttributes, bool bInheritHandles, uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, in Api.STARTUPINFO lpStartupInfo, out Api.PROCESS_INFORMATION lpProcessInformation);\n\t\t\n#if DEBUG\n\t\t[DllImport(\"wtsapi32.dll\", EntryPoint = \"WTSSendMessageW\")]\n\t\tinternal static extern bool WTSSendMessage(IntPtr hServer, int SessionId, string pTitle, int TitleLength, string pMessage, int MessageLength, uint Style, int Timeout, out int pResponse, bool bWait);\n\t\tinternal const uint MB_TOPMOST = 0x40000;\n\t\tinternal const uint MB_SETFOREGROUND = 0x10000;\n#endif\n\t\t\n\t\t[DllImport(\"kernel32.dll\")]\n\t\tinternal static extern bool AttachConsole(uint dwProcessId);\n\t\t\n\t\tinternal const uint ATTACH_PARENT_PROCESS = 0xFFFFFFFF;\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/App/DOptions.cs",
    "content": "using System.Windows;\nusing System.Windows.Controls;\nusing Au.Controls;\nusing Microsoft.Win32;\nusing System.Windows.Controls.Primitives;\nusing ToolLand;\nusing System.Windows.Media;\nusing System.Windows.Documents;\nusing System.Windows.Input;\nusing EStyle = LA.SciTheme.EStyle;\n\nnamespace LA;\n\nclass DOptions : KDialogWindow {\n\tpublic enum EPage { App, Workspace, FontAndColors, CodeEditor, Templates, Hotkeys, AI, Other, OS }\n\t\n\tpublic static void AaShow(EPage? page = null) {\n\t\tvar d = ShowSingle(() => new DOptions());\n\t\tif (page != null) d._tc.SelectedIndex = (int)page.Value;\n\t}\n\t\n\twpfBuilder _b;\n\tTabControl _tc;\n\t\n\tDOptions() {\n\t\tInitWinProp(@\"Options\", App.Wmain);\n\t\t\n\t\t_b = new wpfBuilder(this).WinSize(600);\n\t\t_b.Row(-1).Add(out _tc).Height(300..);\n\t\t_b.R.StartOkCancel()\n\t\t\t.AddOkCancel(out var bOK, out var bCancel, out _, apply: \"Apply\")\n\t\t\t.xAddDialogHelpButtonAndF1(\"editor/Settings\");\n\t\tif (miscInfo.isChildSession) _b.Add<TextBlock>().FormatText($\"<a {() => HelpUtil.AuHelp(\"editor/PiP session\")}>PiP</a>\");\n\t\t_b.End();\n\t\tbOK.IsDefault = false; bCancel.IsCancel = false;\n\t\t\n\t\t_App();\n\t\t_Workspace();\n\t\t_FontAndColors();\n\t\t_CodeEditor();\n\t\t_Templates();\n\t\t_Hotkeys();\n\t\t_AI();\n\t\t_Other();\n\t\t_OS();\n\t\t\n\t\t//_tc.SelectedIndex = 2;\n\t\t\n\t\t_b.End();\n\t}\n\t\n\t/// <summary>\n\t/// Adds new TabItem to _tc. Creates and returns new wpfBuilder for building the tab page.\n\t/// </summary>\n\twpfBuilder _Page(string name, WBPanelType panelType = WBPanelType.Grid) {\n\t\tvar tp = new TabItem { Header = name, MinWidth = 30 };\n\t\t_tc.Items.Add(tp);\n\t\treturn new wpfBuilder(tp, panelType).Margin(\"3\");\n\t}\n\t\n\tvoid _App() {\n\t\tvar b = _Page(\"App\").Columns(-1, 20, -1);\n\t\t\n\t\t//left column\n\t\tb.StartGrid();\n\t\tb.R.Add(out KCheckBox startWithWin, \"Start with Windows\"); //note: must be the first checkbox in Options, and don't change text, because used for the forum registration security question\n\t\tb.R.Add(out KCheckBox startHidden, wpfBuilder.formattedText($\"Start hidden; let <s b='#C42B1C' c='white' FontFamily='Segoe UI Symbol' FontSize='11'>  ✖  </s> hide\")).Checked(App.Settings.runHidden);\n\t\tb.R.Add(out KCheckBox visibleIfNotAutoStarted, \"Visible if not auto-started\").Margin(22).Checked(App.Settings.startVisibleIfNotAutoStarted).xBindCheckedEnabled(startHidden);\n\t\tb.R.Add(out KCheckBox checkForUpdates, \"Check for updates every day\").Checked(App.Settings.checkForUpdates);\n\t\tb.R.AddButton(\"Check for updates now\", o => App.CheckForUpdates(o.Button)).Margin(22).Width(150, \"L\");\n\t\tb.End();\n\t\t\n\t\t//right column\n\t\tb.Skip().StartStack(vertical: true);\n\t\tb.End();\n\t\t\n\t\tb.End();\n\t\t\n\t\tconst string c_rkRun = @\"Software\\Microsoft\\Windows\\CurrentVersion\\Run\";\n\t\tstring init_swwValue = Registry.GetValue(@\"HKEY_CURRENT_USER\\\" + c_rkRun, \"Au.Editor\", null) as string;\n\t\tbool init_swwYes = true == init_swwValue?.RxMatch($\"^\\\"(.+?)\\\"\", 1, out string s1) && filesystem.more.isSameFile(s1, process.thisExePath);\n\t\tstartWithWin.IsChecked = init_swwYes;\n\t\tif (App.IsPortable) startWithWin.Checked += (_, _) => dialog.showWarning(\"Portable mode warning\", \"This setting will be saved in the Registry. Portable apps should not do it.\", owner: this);\n\t\t\n\t\t_b.OkApply += e => {\n\t\t\tif (startWithWin.IsChecked != init_swwYes || (init_swwYes && !init_swwValue.Ends(\" /a\"))) {\n\t\t\t\ttry {\n\t\t\t\t\tusing var rk = Registry.CurrentUser.OpenSubKey(c_rkRun, true);\n\t\t\t\t\tif (init_swwYes = startWithWin.IsChecked) rk.SetValue(\"Au.Editor\", init_swwValue = $\"\\\"{process.thisExePath}\\\" /a\");\n\t\t\t\t\telse rk.DeleteValue(\"Au.Editor\");\n\t\t\t\t}\n\t\t\t\tcatch (Exception ex) { print.it(\"Failed to change 'Start with Windows'. \" + ex.ToStringWithoutStack()); }\n\t\t\t}\n\t\t\tApp.Settings.runHidden = startHidden.IsChecked;\n\t\t\tApp.Settings.startVisibleIfNotAutoStarted = visibleIfNotAutoStarted.IsChecked;\n\t\t\tApp.Settings.checkForUpdates = checkForUpdates.IsChecked;\n\t\t};\n\t}\n\t\n\tvoid _Workspace() {\n\t\tvar b = _Page(\"Workspace\").Columns(-1.5, 20, -1);\n\t\tb.R.xAddInfoBlockT(\"Settings of current workspace\");\n\t\t\n\t\t//left column\n\t\tb.R.StartStack(vertical: true);\n\t\tb.Add(\"Startup scripts\", out TextBox startupScripts, App.Model.UserSettings.startupScripts).Multiline(110, TextWrapping.NoWrap)\n\t\t\t.Tooltip(\"Example:\\nScript1.cs\\n\\\\Folder\\\\Script2.cs\\n//Disabled.cs\\nDelay1.cs, 3s\\nDelay2.cs, 300ms\\n\\\"Comma, comma.cs\\\"\")\n\t\t\t.Validation(_startupScripts_Validation);\n\t\t\n\t\tb.Add(out KCheckBox cBackup, \"Auto-backup (Git commit)\").Checked(App.Model.UserSettings.gitBackup).Margin(\"T10\")\n\t\t\t.Tooltip(\"Silently run Git commit when LibreAutomate is visible the first time after loading this workspace or activated later after several hours from the last backup.\\nIt creates a local backup of workspace files (scripts etc). To upload etc, you can use menu File > Git.\");\n\t\tcBackup.Checked += (_, _) => { if (!Git.IsReady) App.Dispatcher.InvokeAsync(Git.Setup); };\n\t\tb.End();\n\t\t\n\t\t//right column\n\t\tb.Skip().StartStack(vertical: true);\n\t\tb.Add(\"Hide/ignore files and folders\", out TextBox tSyncFsSkip, App.Model.WSSett.syncfs_skip).Multiline(110, TextWrapping.NoWrap)\n\t\t\t.Tooltip(@\"Hide and don't use files and folders whose paths in workspace would match a wildcard from this list.\nExample:\n*.bak\n*\\FolderAnywhere\n\\Folder\n\\Folder1\\Folder2\n//Comment\");\n\t\tb.End();\n\t\t\n\t\tb.End();\n\t\t\n\t\t_b.OkApply += e => {\n\t\t\tApp.Model.UserSettings.startupScripts = startupScripts.Text.Trim().NullIfEmpty_();\n\t\t\tApp.Model.UserSettings.gitBackup = cBackup.IsChecked;\n\t\t\t\n\t\t\tstring skipOld = App.Model.WSSett.syncfs_skip, skipNew = tSyncFsSkip.TextOrNull();\n\t\t\tApp.Model.WSSett.syncfs_skip = skipNew;\n\t\t\tif (skipNew != skipOld) App.Model.SyncWithFilesystem_();\n\t\t};\n\t\t\n\t\tstatic string _startupScripts_Validation(FrameworkElement fe) {\n\t\t\t//print.it(\"validating\");\n\t\t\tstring text = (fe as TextBox).Text; if (text.NE()) return null;\n\t\t\ttry {\n\t\t\t\tvar t = csvTable.parse(text);\n\t\t\t\tif (t.ColumnCount > 2) return \"Too many commas in a line. If script name contains comma, enclose in \\\"\\\".\";\n\t\t\t\tregexp rxDelay = null;\n\t\t\t\tforeach (var v in t.Rows) {\n\t\t\t\t\tvar s0 = v[0];\n\t\t\t\t\tif (s0.Starts(\"//\")) continue;\n\t\t\t\t\tif (App.Model.FindCodeFile(s0) == null) return \"Script not found: \" + s0;\n\t\t\t\t\tvar delay = v.Length == 1 ? null : v[1];\n\t\t\t\t\tif (!delay.NE()) {\n\t\t\t\t\t\trxDelay ??= new regexp(@\"(?i)^\\d+ *m?s$\");\n\t\t\t\t\t\tif (!rxDelay.IsMatch(delay)) return \"Delay must be like 2 s or 500 ms\";\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (FormatException ex) { return ex.Message; }\n\t\t\treturn null;\n\t\t}\n\t}\n\t\n\tclass _FontControls {\n\t\tpublic ComboBox name;\n\t\tpublic TextBox size;\n\t\t\n\t\tpublic void Init(string[] fonts) {\n\t\t\tname.ItemsSource = fonts;\n\t\t\tsize.MouseWheel += static (o, e) => {\n\t\t\t\tvar tb = o as TextBox;\n\t\t\t\tif (!tb.Text.ToNumber(out double d)) d = 9;\n\t\t\t\ttb.Text = Math.Clamp(d + e.Delta / 160d, 6, 30).ToS(\"0.##\"); //120/160d=0.75\n\t\t\t};\n\t\t}\n\t\t\n\t\tpublic void Init(string[] fonts, AppSettings.font_t f) {\n\t\t\tInit(fonts);\n\t\t\tSet(f.name, f.size);\n\t\t}\n\t\t\n\t\tpublic void Set(string fontName, double fontSize) {\n\t\t\tname.SelectedItem = fontName; if (name.SelectedItem is null) name.Text = fontName;\n\t\t\tsize.Text = fontSize.ToS(\"0.##\");\n\t\t}\n\t\t\n\t\tpublic (string name, double size) Get() {\n\t\t\tvar s = name.Text.Trim(); if (s == \"\" || s.Starts(\"[ \")) s = \"Consolas\";\n\t\t\treturn (s, size.Text.ToNumber());\n\t\t}\n\t\t\n\t\tpublic bool Apply(ref AppSettings.font_t f) {\n\t\t\tvar s = name.Text.Trim(); var d = size.Text.ToNumber();\n\t\t\tif (s == f.name && d == f.size) return false;\n\t\t\tf.name = s;\n\t\t\tf.size = d;\n\t\t\tf.Normalize();\n\t\t\treturn true;\n\t\t}\n\t}\n\t\n\tvoid _FontAndColors() {\n\t\tSciTheme theme = null, savedTheme = null;\n\t\tbool ignoreColorEvents = false;\n\t\t\n\t\tvar b = _Page(\"Font, colors\", WBPanelType.Dock);\n\t\tb.Options(bindLabelVisibility: true);\n\t\t\n\t\tb.Add(out KScintilla sciStyles).Width(180);\n\t\tsciStyles.AaInitBorder = true;\n\t\tsciStyles.Name = \"styles\";\n\t\t//note: not readonly. Eg users may want to paste and see any character in multiple fonts.\n\t\t\n\t\tb.StartGrid().Columns(-1).Margin(20);\n\t\tb.R.StartGrid<KGroupBox>(out var gFont, \"Font\");\n\t\t_FontControls _AddFontControls(string label) {\n\t\t\tb.R.Add(label, out ComboBox name).Editable().And(40).Add(out TextBox size).Tooltip(\"Font size.\\nUse mouse wheel to select.\");\n\t\t\treturn new() { name = name, size = size };\n\t\t}\n\t\tvar font = _AddFontControls(\"Editor\");\n\t\tvar fontOutput = _AddFontControls(\"Output\");\n\t\tvar fontFind = _AddFontControls(\"Find\");\n\t\tb.R.xAddInfoBlockT(\"Only editor font depends on theme.\");\n\t\tb.End();\n\t\tb.R.StartGrid();\n\t\tvar pColor = b.Panel as Grid;\n\t\tb.R.Add(out KColorPicker colorPicker);\n\t\tb.R.StartStack();\n\t\tvar pFontStyle = b.Panel;\n\t\tb.Add(out KCheckBox cBold, \"Bold\");\n\t\tb.Add(out KCheckBox cItalic, \"Italic\");\n\t\tb.Add(out KCheckBox cUnderline, \"Underline\");\n\t\tb.Add(out KCheckBox cBackground, \"Background\");\n\t\tb.End();\n\t\tb.R.Add(\"\", out TextBox tAlpha).Width(50, \"L\");\n\t\tvar lAlpha = b.Last2 as Label;\n\t\tb.End();\n\t\tb.Row(-1);\n\t\tb.R.AddSeparator();\n\t\tb.R.StartStack();\n\t\tb.AddButton(\"Theme ▾\", _ThemesButtonClicked).Width(70);\n\t\tb.End().Align(\"r\");\n\t\tb.End();\n\t\tb.End();\n\t\t\n\t\tpColor.Visibility = Visibility.Collapsed;\n\t\t\n\t\tb.Loaded += () => {\n\t\t\tsciStyles.Call(Sci.SCI_SETCARETLINEFRAME, 1);\n\t\t\tsciStyles.aaaSetElementColor(Sci.SC_ELEMENT_CARET_LINE_BACK, 0xE0E0E0);\n\t\t\tsciStyles.Call(Sci.SCI_SETCARETLINEVISIBLEALWAYS, 1);\n\t\t\t\n\t\t\t//font\n\t\t\t\n\t\t\tList<string> fontsMono = new(), fontsVar = new();\n\t\t\tusing (var dc = new ScreenDC_()) {\n\t\t\t\tunsafe {\n\t\t\t\t\t_Api.EnumFontFamiliesEx(dc, default, (lf, tm, fontType, lParam) => {\n\t\t\t\t\t\tif (lf->lfFaceName[0] != '@') {\n\t\t\t\t\t\t\tvar fn = new string(lf->lfFaceName);\n\t\t\t\t\t\t\tif ((lf->lfPitchAndFamily & 0xf0) == 48) fontsMono.Add(fn); else fontsVar.Add(fn); //FF_MODERN=48\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn 1;\n\t\t\t\t\t}, default, 0);\n\t\t\t\t}\n\t\t\t}\n\t\t\tfontsMono.Sort();\n\t\t\tfontsVar.Sort();\n\t\t\tstring[] fonts = [\"[ Fixed-width fonts ]\", .. fontsMono, \"\", \"[ Variable-width fonts ]\", .. fontsVar];\n\t\t\t\n\t\t\tfont.Init(fonts);\n\t\t\tfontOutput.Init(fonts, App.Settings.font_output);\n\t\t\tfontFind.Init(fonts, App.Settings.font_find);\n\t\t\t\n\t\t\t//styles\n\t\t\t\n\t\t\tconst int indicHidden = 0;\n\t\t\tsciStyles.aaaIndicatorDefine(indicHidden, Sci.INDIC_HIDDEN);\n\t\t\tsciStyles.aaaMarginSetWidth(1, 0);\n\t\t\t\n\t\t\tconst int c_isStyle = 0, c_isIndicator = 1, c_isElementAlpha = 2, c_isFont = 3, c_isBackground = 4, c_isElementThickness = 5, c_isElementColorOnly = 6;\n\t\t\t\n\t\t\t(string name, int kind, int index)[] table = [\n\t\t\t\t(\"Font\", c_isFont, 0),\n\n\t\t\t\t(\"Background\", c_isBackground, 0),\n\n\t\t\t\t(\"Text\", c_isStyle, (int)EStyle.None),\n\t\t\t\t(\"//Comment\", c_isStyle, (int)EStyle.Comment),\n\t\t\t\t(@\"\"\"String\"\" 'c'\", c_isStyle, (int)EStyle.String),\n\t\t\t\t(@\"\\r\\n\\t\\0\\\\\", c_isStyle, (int)EStyle.StringEscape),\n\t\t\t\t(\"1234567890\", c_isStyle, (int)EStyle.Number),\n\t\t\t\t(\"()[]{},;:\", c_isStyle, (int)EStyle.Punctuation),\n\t\t\t\t(\"Operator\", c_isStyle, (int)EStyle.Operator),\n\t\t\t\t(\"Keyword\", c_isStyle, (int)EStyle.Keyword),\n\t\t\t\t(\"Namespace\", c_isStyle, (int)EStyle.Namespace),\n\t\t\t\t(\"Type\", c_isStyle, (int)EStyle.Type),\n\t\t\t\t(\"Function\", c_isStyle, (int)EStyle.Function),\n\t\t\t\t(\"Event\", c_isStyle, (int)EStyle.Event),\n\t\t\t\t(\"Local variable\", c_isStyle, (int)EStyle.LocalVariable),\n\t\t\t\t(\"Field variable\", c_isStyle, (int)EStyle.Field),\n\t\t\t\t(\"Constant\", c_isStyle, (int)EStyle.Constant),\n\t\t\t\t(\"GotoLabel\", c_isStyle, (int)EStyle.Label),\n\t\t\t\t(\"#directive\", c_isStyle, (int)EStyle.Preprocessor),\n\t\t\t\t(\"#if-disabled\", c_isStyle, (int)EStyle.Excluded),\n\t\t\t\t(\"/// doc text\", c_isStyle, (int)EStyle.XmlDocText),\n\t\t\t\t(\"/// <doc tag>\", c_isStyle, (int)EStyle.XmlDocTag),\n\t\t\t\t(\"Regexp text\", c_isStyle, (int)EStyle.RxText),\n\t\t\t\t(\"Regexp meta\", c_isStyle, (int)EStyle.RxMeta),\n\t\t\t\t(\"Regexp chars\", c_isStyle, (int)EStyle.RxChars),\n\t\t\t\t(\"Regexp option\", c_isStyle, (int)EStyle.RxOption),\n\t\t\t\t(\"Regexp escape\", c_isStyle, (int)EStyle.RxEscape),\n\t\t\t\t(\"Regexp callout\", c_isStyle, (int)EStyle.RxCallout),\n\t\t\t\t(\"Regexp comment\", c_isStyle, (int)EStyle.RxComment),\n\t\t\t\t(\"Line number text\", c_isStyle, (int)EStyle.LineNumber),\n\t\t\t\t(\"Line number margin\", c_isElementColorOnly, -1),\n\t\t\t\t(\"Marker margin\", c_isElementColorOnly, -2),\n\n\t\t\t\t(\"Text highlight\", c_isIndicator, SciTheme.Indic.Found),\n\t\t\t\t(\"Symbol highlight\", c_isIndicator, SciTheme.Indic.Refs),\n\t\t\t\t(\"Brace highlight\", c_isIndicator, SciTheme.Indic.Braces),\n\t\t\t\t(\"Debug highlight\", c_isIndicator, SciTheme.Indic.Debug),\n\t\t\t\t(\"Snippet field\", c_isIndicator, SciTheme.Indic.SnippetField),\n\t\t\t\t(\"Snippet field active\", c_isIndicator, SciTheme.Indic.SnippetFieldActive),\n\n\t\t\t\t(\"Selection\", c_isElementAlpha, Sci.SC_ELEMENT_SELECTION_BACK),\n\t\t\t\t(\"Selection no focus\", c_isElementAlpha, Sci.SC_ELEMENT_SELECTION_INACTIVE_BACK),\n\t\t\t\t\n\t\t\t\t(\"Caret\", c_isElementThickness, Sci.SC_ELEMENT_CARET),\n\t\t\t\t(\"Caret line frame\", c_isElementThickness, Sci.SC_ELEMENT_CARET_LINE_BACK),\n\t\t\t];\n\t\t\t\n\t\t\tsciStyles.aaaText = string.Join(\"\\r\\n\", table.Select(o => o.name));\n\t\t\tfor (int i = 0; i < table.Length; i++) {\n\t\t\t\tint lineStart = sciStyles.aaaLineStart(false, i), lineEnd = sciStyles.aaaLineEnd(false, i);\n\t\t\t\tsciStyles.aaaIndicatorAdd(indicHidden, false, lineStart..lineEnd, i + 1);\n\t\t\t\tvar kind = table[i].kind;\n\t\t\t\tif (kind is c_isStyle) {\n\t\t\t\t\tsciStyles.Call(Sci.SCI_STARTSTYLING, lineStart);\n\t\t\t\t\tsciStyles.Call(Sci.SCI_SETSTYLING, lineEnd - lineStart, table[i].index);\n\t\t\t\t} else if (kind is c_isIndicator) {\n\t\t\t\t\tsciStyles.aaaIndicatorAdd(table[i].index, false, lineStart..lineEnd);\n\t\t\t\t} else if (kind is c_isElementColorOnly && table[i].index is -1) { //line number margin\n\t\t\t\t\tsciStyles.Call(Sci.SCI_STARTSTYLING, lineStart);\n\t\t\t\t\tsciStyles.Call(Sci.SCI_SETSTYLING, lineEnd - lineStart, Sci.STYLE_LINENUMBER);\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t//when changed current line\n\t\t\tint currentItem = 0;\n\t\t\tsciStyles.AaNotify += e => {\n\t\t\t\tswitch (e.n.code) {\n\t\t\t\tcase Sci.NOTIF.SCN_UPDATEUI:\n\t\t\t\t\tint i = sciStyles.aaaIndicatorGetValue(indicHidden, sciStyles.aaaLineStartFromPos(false, sciStyles.aaaCurrentPos8)) - 1;\n\t\t\t\t\tif (i != currentItem && i >= 0) {\n\t\t\t\t\t\tcurrentItem = i;\n\t\t\t\t\t\tvar k = table[i];\n\t\t\t\t\t\tif (k.kind is c_isFont) {\n\t\t\t\t\t\t\tpColor.Visibility = Visibility.Collapsed;\n\t\t\t\t\t\t\tgFont.Visibility = Visibility.Visible;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tgFont.Visibility = Visibility.Collapsed;\n\t\t\t\t\t\t\tpColor.Visibility = Visibility.Visible;\n\t\t\t\t\t\t\tignoreColorEvents = true;\n\t\t\t\t\t\t\tint col;\n\t\t\t\t\t\t\tpFontStyle.Visibility = k.kind is c_isStyle ? Visibility.Visible : Visibility.Collapsed;\n\t\t\t\t\t\t\ttAlpha.Visibility = k.kind is c_isIndicator or c_isElementAlpha or c_isElementThickness ? Visibility.Visible : Visibility.Collapsed;\n\t\t\t\t\t\t\tlAlpha.Content = k.kind is c_isElementThickness ? \"Thickness 1-4\" : \"Opacity 0-255\";\n\t\t\t\t\t\t\tif (k.kind is c_isStyle) {\n\t\t\t\t\t\t\t\tref var rs = ref theme[(EStyle)k.index];\n\t\t\t\t\t\t\t\tcol = rs.color;\n\t\t\t\t\t\t\t\tcBold.IsChecked = rs.bold;\n\t\t\t\t\t\t\t\tcItalic.IsChecked = rs.italic;\n\t\t\t\t\t\t\t\tcUnderline.IsChecked = rs.underline;\n\t\t\t\t\t\t\t\tbool back = (EStyle)k.index is >= EStyle.RxText and <= EStyle.RxComment;\n\t\t\t\t\t\t\t\tif (back) cBackground.IsChecked = rs.back;\n\t\t\t\t\t\t\t\tcBackground.Visibility = back ? Visibility.Visible : Visibility.Collapsed;\n\t\t\t\t\t\t\t} else if (k.kind is c_isIndicator) {\n\t\t\t\t\t\t\t\tref var ri = ref theme.Indicator(k.index);\n\t\t\t\t\t\t\t\tcol = ri.color;\n\t\t\t\t\t\t\t\ttAlpha.Text = ri.alpha.ToS();\n\t\t\t\t\t\t\t} else if (k.kind is c_isElementAlpha or c_isElementThickness) {\n\t\t\t\t\t\t\t\tcol = theme.Element(k.index);\n\t\t\t\t\t\t\t\ttAlpha.Text = (col >>> 24).ToS();\n\t\t\t\t\t\t\t\tcol &= 0xFFFFFF;\n\t\t\t\t\t\t\t} else if (k.kind is c_isElementColorOnly) {\n\t\t\t\t\t\t\t\tcol = theme.Element(k.index) & 0xFFFFFF;\n\t\t\t\t\t\t\t} else { //Background\n\t\t\t\t\t\t\t\tcol = theme.Background;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcolorPicker.Color = col;\n\t\t\t\t\t\t\tignoreColorEvents = false;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t};\n\t\t\t\n\t\t\t//when changed values of controls\n\t\t\tTextChangedEventHandler textChanged = (_, _) => {\n\t\t\t\t(theme.FontName, theme.FontSize) = font.Get();\n\t\t\t\ttheme.ToScintilla(sciStyles);\n\t\t\t};\n\t\t\tfont.name.AddHandler(TextBoxBase.TextChangedEvent, textChanged);\n\t\t\tfont.size.AddHandler(TextBoxBase.TextChangedEvent, textChanged);\n\t\t\t\n\t\t\tcolorPicker.ColorChanged += _ => _UpdateSci();\n\t\t\tcBold.CheckChanged += (sender, _) => _UpdateSci(sender);\n\t\t\tcItalic.CheckChanged += (sender, _) => _UpdateSci(sender);\n\t\t\tcUnderline.CheckChanged += (sender, _) => _UpdateSci(sender);\n\t\t\tcBackground.CheckChanged += (sender, _) => _UpdateSci(sender);\n\t\t\ttAlpha.TextChanged += (sender, _) => _UpdateSci(sender);\n\t\t\t\n\t\t\tvoid _UpdateSci(object control = null) {\n\t\t\t\tif (ignoreColorEvents || currentItem < 0) return;\n\t\t\t\tvar k = table[currentItem];\n\t\t\t\tint col = colorPicker.Color;\n\t\t\t\tif (k.kind is c_isStyle) {\n\t\t\t\t\tref var rs = ref theme[(EStyle)k.index];\n\t\t\t\t\tif (control == cBold) rs.bold = cBold.IsChecked;\n\t\t\t\t\telse if (control == cItalic) rs.italic = cItalic.IsChecked;\n\t\t\t\t\telse if (control == cUnderline) rs.underline = cUnderline.IsChecked;\n\t\t\t\t\telse if (control == cBackground) rs.back = cBackground.IsChecked;\n\t\t\t\t\telse rs.color = col;\n\t\t\t\t} else if (k.kind is c_isIndicator) {\n\t\t\t\t\tref var ri = ref theme.Indicator(k.index);\n\t\t\t\t\tif (control == tAlpha) ri.alpha = Math.Clamp(tAlpha.Text.ToInt(), 0, 255);\n\t\t\t\t\telse ri.color = col;\n\t\t\t\t} else if (k.kind is c_isElementAlpha or c_isElementThickness or c_isElementColorOnly) {\n\t\t\t\t\tref var m = ref theme.Element(k.index);\n\t\t\t\t\tif (control == tAlpha) {\n\t\t\t\t\t\tbool cl = k.kind is c_isElementThickness; //note: don't allow caret line frame 0, which set line background color instead of frame, because it would be mixed with translucent indicators\n\t\t\t\t\t\tm = (m & 0xFFFFFF) | Math.Clamp(tAlpha.Text.ToInt(), cl ? 1 : 0, cl ? 4 : 255) << 24;\n\t\t\t\t\t} else m = (m & ~0xFFFFFF) | (col & 0xFFFFFF);\n\t\t\t\t} else if (k.kind is c_isBackground) {\n\t\t\t\t\ttheme.Background = col;\n\t\t\t\t}\n\t\t\t\ttheme.ToScintilla(sciStyles);\n\t\t\t}\n\t\t\t\n\t\t\t_b.OkApply += e => {\n\t\t\t\tbool stylesChanged = theme != savedTheme;\n\t\t\t\tbool stylesOrThemeChanged = _ThemeApply(stylesChanged);\n\t\t\t\tif (stylesChanged) { savedTheme = SciTheme.Current; theme = savedTheme with { }; }\n\t\t\t\t\n\t\t\t\tif (fontFind.Apply(ref App.Settings.font_find) || stylesOrThemeChanged) Panels.Find.CodeStylesChanged_();\n\t\t\t\tif (fontOutput.Apply(ref App.Settings.font_output)) Panels.Output.Scintilla.AaSetStyles();\n\t\t\t};\n\t\t\t\n\t\t\t_OpenTheme(SciTheme.Current);\n\t\t};\n\t\t\n\t\tvoid _OpenTheme(SciTheme t) {\n\t\t\tsavedTheme = t;\n\t\t\ttheme = savedTheme with { };\n\t\t\t\n\t\t\tfont.Set(theme.FontName, theme.FontSize);\n\t\t\ttheme.ToScintilla(sciStyles);\n\t\t\tsciStyles.aaaGoToPos(false, 0);\n\t\t}\n\t\t\n\t\tvoid _ThemesButtonClicked(WBButtonClickArgs e) {\n\t\t\tvar m = new popupMenu();\n\t\t\t_Add(\"LA.csv\");\n\t\t\tforeach (var v in filesystem.enumFiles(SciTheme.ThemesDirDefaultBS, \"*.csv\")) _Add(v.Name);\n\t\t\t\n\t\t\tvoid _Add(string fn) {\n\t\t\t\tvar s = fn[..^4];\n\t\t\t\t_Add2(s);\n\t\t\t\tif (filesystem.exists(SciTheme.ThemesDirCustomizedBS + fn)) _Add2(s + \" [customized]\");\n\t\t\t\t\n\t\t\t\tvoid _Add2(string s) {\n\t\t\t\t\tvar v = m.AddRadio(s);\n\t\t\t\t\tif (s == theme.Name) v.IsChecked = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tm.Separator();\n\t\t\tm.Submenu(\"Open folder\", m => {\n\t\t\t\tm[\"Default themes\"] = o => { run.itSafe(SciTheme.ThemesDirDefaultBS); };\n\t\t\t\tm[\"Customized themes\"] = o => { run.itSafe(SciTheme.ThemesDirCustomizedBS); };\n\t\t\t});\n\t\t\t\n\t\t\tm.Show(owner: this);\n\t\t\tif (m.Result is { IsChecked: true } r) {\n\t\t\t\t_OpenTheme(new(r.Text));\n\t\t\t}\n\t\t}\n\t\t\n\t\tbool _ThemeApply(bool modified) {\n\t\t\tif (!modified && theme.Name == SciTheme.Current.Name) return false;\n\t\t\t\n\t\t\tvar name = theme.Name;\n\t\t\tif (modified && !name.Ends(\" [customized]\")) name += \" [customized]\";\n\t\t\t\n\t\t\tvar t = theme with { Name = name };\n\t\t\tSciTheme.Current = t;\n\t\t\t\n\t\t\tApp.Settings.edit_theme = name == \"LA\" ? null : name;\n\t\t\tif (modified) t.Save();\n\t\t\t\n\t\t\tforeach (var v in Panels.Editor.OpenDocs) {\n\t\t\t\tt.ToScintilla(v);\n\t\t\t\tv.ESetLineNumberMarginWidth_();\n\t\t\t}\n\t\t\t\n\t\t\treturn true;\n\t\t}\n\t}\n\t\n\tvoid _CodeEditor() {\n\t\tvar b = _Page(\"Code editor\").Columns(-1, 20, -1);\n\t\tb.R.StartStack(vertical: true); //left\n\t\t\n\t\tb.StartGrid<KGroupBox>(\"Completion list\");\n\t\tb.R.Add(\"Append ( )\", out ComboBox complParen).Items(\"Space|Space, Tab, Enter, 2*click|None\").Select(App.Settings.ci_complParen)\n\t\t\t.Tooltip(\"These keys etc can be used to append ( ) when completing a function name or a keyword like 'if'.\\nPlus characters ;.,:([{+-*/ etc.\");\n\t\tb.End();\n\t\t\n\t\tb.StartGrid<KGroupBox>(\"Statement completion\");\n\t\tb.R.Add(\"Hotkey\", out ComboBox enterWith).Items(\"Ctrl+Enter|Shift+Enter|Ctrl+Shift+Enter\").Select(App.Settings.ci_enterWith);\n\t\tb.R.Add(out KCheckBox enterBeforeParen, \"Enter before )\").Checked(App.Settings.ci_enterBeforeParen)\n\t\t\t.Tooltip(\"Key Enter before ) or ] completes statement like with Ctrl etc.\\nExcept after comma or space or with Shift etc.\");\n\t\tb.R.Add(out KCheckBox enterBeforeSemicolon, \"Enter before ;\").Checked(App.Settings.ci_enterBeforeSemicolon)\n\t\t\t.Tooltip(\"Key Enter before ; completes statement like with Ctrl etc.\\nExcept after comma or space or with Shift etc.\");\n\t\tb.R.Add(out KCheckBox autoSemicolon, \"Add ; when starting a statement\").Checked(App.Settings.ci_semicolon);\n\t\tb.End();\n\t\t\n\t\tb.End(); //left\n\t\t\n\t\tb.Skip().StartStack(vertical: true); //right\n\t\t\n\t\tb.StartGrid<KGroupBox>(\"Formatting\");\n\t\tb.R.Add(out KCheckBox formatCompact, \"Compact\").Checked(App.Settings.ci_formatCompact)\n\t\t\t.Tooltip(\"\"\"\nBrace in same line. Don't indent case in switch.\nExamples:\n\nvoid Checked() {\n    if (true) {\n\n    } else {\n\n    }\n}\n\nvoid Unchecked()\n{\n    if (true)\n    {\n\n    }\n    else\n    {\n\n    }\n}\n\"\"\");\n\t\tb.R.Add(out KCheckBox formatTabIndent, \"Tab-indent\").Checked(App.Settings.ci_formatTabIndent)\n\t\t\t.Tooltip(\"For indent use tab character, not spaces\");\n\t\tb.R.Add(out KCheckBox formatAuto, \"Auto-format\").Checked(App.Settings.ci_formatAuto)\n\t\t\t.Tooltip(\"Automatically format statement on ;{} etc\");\n\t\tb.End();\n\t\t\n\t\tb.StartGrid<KGroupBox>(\"Find references, rename\");\n\t\tb.R.Add(\"Skip\\nfolders\", out TextBox skipFolders, App.Model.WSSett.ci_skipFolders).Multiline(55, wrap: TextWrapping.NoWrap)\n\t\t\t.Tooltip(@\"Don't search in these folders.\nExample:\n\\Garbage\n\\Folder1\\Folder2\");\n\t\tb.End();\n\t\t\n\t\tb.End(); //right\n\t\t\n\t\tb.End();\n\t\t\n\t\t//b.Loaded += () => {\n\t\t\n\t\t//};\n\t\t\n\t\t_b.OkApply += e => {\n\t\t\tApp.Settings.ci_complParen = complParen.SelectedIndex;\n\t\t\tApp.Settings.ci_enterWith = enterWith.SelectedIndex;\n\t\t\tApp.Settings.ci_enterBeforeParen = enterBeforeParen.IsChecked;\n\t\t\tApp.Settings.ci_enterBeforeSemicolon = enterBeforeSemicolon.IsChecked;\n\t\t\tApp.Settings.ci_semicolon = autoSemicolon.IsChecked;\n\t\t\t\n\t\t\tif (formatCompact.IsChecked != App.Settings.ci_formatCompact || formatTabIndent.IsChecked != App.Settings.ci_formatTabIndent) {\n\t\t\t\tApp.Settings.ci_formatCompact = formatCompact.IsChecked;\n\t\t\t\tApp.Settings.ci_formatTabIndent = formatTabIndent.IsChecked;\n\t\t\t\tModifyCode.FormattingOptions = null; //recreate\n\t\t\t\t\n\t\t\t\t//note: don't SCI_SETUSETABS(false).\n\t\t\t\t//\tEg VS does not use it, and it's good; VSCode uses, and it's bad, eg cannot insert tab in raw strings.\n\t\t\t\t//\tAll autocorrect/autoindent/format code inserts spaces if need. Users rarely have to type indent tabs.\n\t\t\t\t//\tAnd don't add options to set tab/indent size. Too many options isn't good.\n\t\t\t}\n\t\t\tApp.Settings.ci_formatAuto = formatAuto.IsChecked;\n\t\t\t\n\t\t\tApp.Model.WSSett.ci_skipFolders = skipFolders.TextOrNull();\n\t\t};\n\t\t\n\t\t//CONSIDER: completion list: option to single-click.\n\t}\n\t\n\tvoid _Templates() {\n\t\tvar b = _Page(\"Templates\").Columns(0, 100, -1, 0, 100);\n\t\tb.R.Add(\"Template\", out ComboBox template).Items(\"Script|Class\")\n\t\t\t.Skip().Add(\"Use\", out ComboBox use).Items(\"Default|Custom\");\n\t\tb.Row(-1).Add(out KSciCodeBoxWnd sci); sci.AaInitBorder = true;\n\t\t//b.R.Add(out KCheckBox fold, \"Fold script\").Checked(0 == (1 & App.Settings.templ_flags));\n\t\tb.End();\n\t\t\n\t\tstring[] customText = new string[2];\n\t\tvar useCustom = (FileNode.ETempl)App.Settings.templ_use;\n\t\t\n\t\ttemplate.SelectionChanged += _Combo_Changed;\n\t\tuse.SelectionChanged += _Combo_Changed;\n\t\tsci.AaTextChanged += _ => customText[template.SelectedIndex] = sci.aaaText;\n\t\tb.Loaded += () => {\n\t\t\t_Combo_Changed(template, null);\n\t\t};\n\t\t\n\t\t_b.OkApply += e => {\n\t\t\tfor (int i = 0; i < customText.Length; i++) {\n\t\t\t\tstring text = customText[i]; if (text == null) continue;\n\t\t\t\tvar tt = (FileNode.ETempl)(1 << i);\n\t\t\t\tvar file = FileNode.Templates.FilePathRaw(tt, true);\n\t\t\t\ttry {\n\t\t\t\t\tif (text == FileNode.Templates.Load(tt, false)) {\n\t\t\t\t\t\tfilesystem.delete(file);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tfilesystem.saveText(file, text);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (Exception ex) { print.it(ex.ToStringWithoutStack()); }\n\t\t\t}\n\t\t\tApp.Settings.templ_use = (int)useCustom;\n\t\t\t\n\t\t\t//int flags = App.Settings.templ_flags;\n\t\t\t//if (fold.IsChecked) flags &= ~1; else flags |= 1;\n\t\t\t//App.Settings.templ_flags = flags;\n\t\t};\n\t\t\n\t\tvoid _Combo_Changed(object sender, SelectionChangedEventArgs e) {\n\t\t\tint i = template.SelectedIndex;\n\t\t\tFileNode.ETempl tt = i switch { 1 => FileNode.ETempl.Class, _ => FileNode.ETempl.Script, };\n\t\t\tif (sender == template) use.SelectedIndex = useCustom.Has(tt) ? 1 : 0;\n\t\t\tbool custom = use.SelectedIndex > 0;\n\t\t\tstring text = null;\n\t\t\tif (e != null) {\n\t\t\t\tuseCustom.SetFlag(tt, custom);\n\t\t\t\tif (custom) text = customText[i];\n\t\t\t}\n\t\t\ttext ??= FileNode.Templates.Load(tt, custom);\n\t\t\tsci.AaSetText(text, readonlyFrom: custom ? -1 : 0);\n\t\t}\n\t}\n\t\n\tvoid _Hotkeys() {\n\t\tvar b = _Page(\"Hotkeys\");\n\t\tb.R.Add(\"Capture wnd and show menu\", out TextBox captureMenu, App.Settings.hotkeys.tool_quick).xValidateHotkey();\n\t\tb.R.Add(\"Capture wnd and show tool\", out TextBox captureDwnd, App.Settings.hotkeys.tool_wnd).xValidateHotkey();\n\t\tb.R.Add(\"Capture elm and show tool\", out TextBox captureDelm, App.Settings.hotkeys.tool_elm).xValidateHotkey();\n\t\tb.R.Add(\"Capture image and show tool\", out TextBox captureDuiimage, App.Settings.hotkeys.tool_uiimage).xValidateHotkey();\n\t\tb.End();\n\t\t\n\t\t_b.OkApply += e => {\n\t\t\tAppSettings.hotkeys_t v = new() {\n\t\t\t\ttool_quick = captureMenu.TextOrNull(),\n\t\t\t\ttool_wnd = captureDwnd.TextOrNull(),\n\t\t\t\ttool_elm = captureDelm.TextOrNull(),\n\t\t\t\ttool_uiimage = captureDuiimage.TextOrNull(),\n\t\t\t};\n\t\t\tif (v != App.Settings.session.hotkeys) {\n\t\t\t\tApp.Settings.session.hotkeys = v;\n\t\t\t\tRegHotkeys.UnregisterPermanent();\n\t\t\t\tRegHotkeys.RegisterPermanent();\n\t\t\t}\n\t\t};\n\t}\n\t\n\tvoid _AI() {\n\t\tvar b = _Page(\"AI\", WBPanelType.VerticalStack);\n\t\t\n\t\tb.xAddGroupSeparator(\"API keys\");\n\t\tb.StartGrid().Columns(100, -1);\n\t\tb.R.Add(out ComboBox api)\n\t\t\t.Add(out KPasswordBox apiKey).Tooltip(\"API key or environment variable (if (X) checked).\\nAPI keys are saved encrypted and can't be decrypted on other computers/accounts.\").Hidden();\n\t\tb.End();\n\t\t\n\t\tb.StartGrid().Columns(0, -1, -1);\n\t\tb.R.xAddGroupSeparator(\"AI models for documentation search\");\n\t\tb.R.Add(\"Embedding\", out ComboBox modelEmbed).Tooltip(\"Embedding model is the main component of semantic search\").Span(1);\n\t\tb.R.Add(\"Reranker\", out ComboBox modelRerank).Tooltip(\"Reranker model improves search results\").Span(1);\n\t\t\n\t\t//b.R.Add(\"Chat\", out ComboBox modelChat).Span(1);\n\t\t\n\t\t//b.R.StartGrid<KGroupBox>(\"Chat model settings\");\n\t\t//b.End();\n\t\t\n\t\t//rejected. Currently using Voyage multimodal. It supports text and images.\n\t\t//b.xAddGroupSeparator(\"Models for icon search in the Icons tool\");\n\t\t//b.R.Add(\"Search\", out ComboBox modelIconSearch).Tooltip(\"AI embedding model for icon search\");\n\t\t\n\t\tb.R.xAddGroupSeparator(\"MCP\");\n\t\tb.R.Add(out KCheckBox mcpDebug, \"Print tool calls\").Checked(App.Settings.ai_mcp_print);\n\t\t\n\t\tb.R.AddSeparator(false);\n\t\tb.R.AddButton(\"...\", _ => _MoreMenu()).Align(HorizontalAlignment.Left).Span(1);\n\t\t\n\t\tb.End();\n\t\tb.End();\n\t\t\n\t\tb.Loaded += () => {\n\t\t\tvar apiNames = AI.AiModel.Models.Select(o => o.api).Distinct(StringComparer.OrdinalIgnoreCase).ToArray();\n\t\t\tvar apiKeys = new (bool once, (string key, bool ev) now, (string key, bool ev) old)[apiNames.Length];\n\t\t\tint currentApi = -1;\n\t\t\tapi.ItemsSource = apiNames;\n\t\t\tapi.SelectionChanged += (_, e) => {\n\t\t\t\tif (currentApi >= 0) apiKeys[currentApi].now = (apiKey.Text.NullIfEmpty_(), apiKey.IsEnvVar);\n\t\t\t\tcurrentApi = api.SelectedIndex;\n\t\t\t\tapiKey.Visibility = currentApi >= 0 ? Visibility.Visible : Visibility.Hidden;\n\t\t\t\tif (currentApi < 0) return;\n\t\t\t\tif (!apiKeys[currentApi].once) {\n\t\t\t\t\tapiKeys[currentApi].once = true;\n\t\t\t\t\tApp.Settings.ai_ak.TryGetValue((string)api.SelectedItem, out var key);\n\t\t\t\t\tbool ev = false;\n\t\t\t\t\tif (key.NE()) key = null;\n\t\t\t\t\telse {\n\t\t\t\t\t\tev = key is ['%', _, .., '%'];\n\t\t\t\t\t\tkey = ev ? key[1..^1] : EdProtectedData.Unprotect(key);\n\t\t\t\t\t}\n\t\t\t\t\tapiKeys[currentApi].now = apiKeys[currentApi].old = (key, ev);\n\t\t\t\t}\n\t\t\t\t(apiKey.Text, apiKey.IsEnvVar) = apiKeys[currentApi].now;\n\t\t\t};\n\t\t\t//api.SelectedIndex = 0; //no\n\t\t\tb.Validation(apiKey, _ => {\n\t\t\t\tif (currentApi >= 0) apiKeys[currentApi].now = (apiKey.Text.NullIfEmpty_(), apiKey.IsEnvVar);\n\t\t\t\tif (apiKeys.Any(o => o.now.ev && o.now.key.Lenn() >= 32)) return \"Variable name too long. Uncheck (X) if it's not a variable.\"; //probably checked the toggle button to see the key and forgot to uncheck\n\t\t\t\treturn null;\n\t\t\t});\n\t\t\t\n\t\t\t_InitModelCombo(modelEmbed, o => o is AI.AiEmbeddingModel { isCompact: false }, App.Settings.ai_modelEmbed);\n\t\t\t_InitModelCombo(modelRerank, o => o is AI.AiRerankModel, App.Settings.ai_modelRerank, optional: true);\n\t\t\t//_InitModelCombo(modelChat, o => o is AI.AiChatModel, App.Settings.ai_modelChat);\n\t\t\t//_InitModelCombo(modelIconSearch, o => o is AI.AiEmbeddingModel { isCompact: true }, App.Settings.ai_modelIconSearch);\n\t\t\t\n\t\t\tvoid _InitModelCombo(ComboBox c, Func<AI.AiModel, bool> predicate, string select, bool optional = false) {\n\t\t\t\tvar e = AI.AiModel.Models.Where(predicate).Select(o => o.DisplayName);\n\t\t\t\tif (optional) e = e.Prepend(\"none\");\n\t\t\t\tc.ItemsSource = e.ToArray();\n\t\t\t\tc.SelectedItem = select;\n\t\t\t}\n\t\t\t\n\t\t\t_b.OkApply += e => {\n\t\t\t\tforeach (var (i, v) in apiKeys.Index()) {\n\t\t\t\t\tif (v.once && v.now != v.old) {\n\t\t\t\t\t\tapiKeys[i].old = v.now;\n\t\t\t\t\t\tvar key = v.now.key == null ? null : v.now.ev ? $\"%{v.now.key}%\" : EdProtectedData.Protect(v.now.key);\n\t\t\t\t\t\tApp.Settings.ai_ak[apiNames[i]] = key;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tApp.Settings.ai_modelEmbed = _GetModelCombo(modelEmbed);\n\t\t\t\tApp.Settings.ai_modelRerank = _GetModelCombo(modelRerank);\n\t\t\t\t//App.Settings.ai_modelChat = _GetModelCombo(modelChat);\n\t\t\t\t//App.Settings.ai_modelIconSearch = _GetModelCombo(modelIconSearch);\n\t\t\t\t\n\t\t\t\tstring _GetModelCombo(ComboBox c) => c.SelectedItem is string s && s != \"none\" ? s : null;\n\t\t\t\t\n\t\t\t\tApp.Settings.ai_mcp_print = mcpDebug.IsChecked;\n\t\t\t};\n\t\t};\n\t\t\n\t\tvoid _MoreMenu() {\n\t\t\tvar m = new popupMenu();\n\t\t\tm[\"Print all model configurations\"] = o => { print.it(AI.AiModel.Models); };\n\t\t\t//m[\"How to add new model\"] = o => { _AddCustomModel(); };\n\t\t\t\n\t\t\tvar emDir = AI.Embeddings.VectorDir;\n\t\t\tif (filesystem.exists(emDir).Directory) m[\"Open embedding vectors folder\"] = o => { var s = run.itSafe(emDir); };\n\t\t\t\n\t\t\tm.Separator();\n\t\t\tm[\"Help\"] = o => { HelpUtil.AuHelp(\"editor/LA and AI\"); };\n\t\t\tm.Show(owner: this);\n\t\t}\n\n#if !true\n\t\tvoid _AddCustomModel() {\n\t\t\tif (modelChat.SelectedIndex < 0 && modelChat.Items.Count > 0) modelChat.SelectedIndex = 0;\n\t\t\tif (!(modelChat.SelectedItem is string mdn && AI.AiModel.Models.FirstOrDefault(o => o is AI.AiChatModel && o.DisplayName == mdn) is AI.AiChatModel m)) return;\n\t\t\tstring code = $$\"\"\"\n/*/ role editorExtension; testInternal Au.Editor; r Au.Editor.dll; /*/\nusing AI;\nvar model1 = AiModel.GetModel<{{m.GetType()}}>() with { model = \"model-name\" };\nAiModel.Models.Add(model1);\n\n\"\"\";\n\t\t\tStringBuilder s = new(\"<>To add an AI model configuration, you need a script with role editorExtension (\");\n\t\t\tforeach (var v in App.Model.GetStartupScriptsExceptDisabled()) if (new MetaCommentsParser(v.f).role == \"editorExtension\") s.Append(v.f.SciLink()).Append(\", \");\n\t\t\ts.Append($$\"\"\"\n<+newStartupScriptEditorExtension {{code.Replace('>', '\\x1')}}>create<>). Add the script to <+options Workspace>startup scripts<>.\nIt's possible to create a completely new AI model configuration (any AI API, any parameters). How - please ask in the forum.\nOr clone an existing model configuration and change some properties. Example:\n<code>{{code}}</code>\n\"\"\");\n\t\t\tprint.it(s);\n\t\t\tPanels.Output.Scintilla.AaTags.AddLinkTag(\"+newStartupScriptEditorExtension\", code => {\n\t\t\t\tcode = code.Replace('\\x1', '>');\n\t\t\t\tApp.Model.NewItem(\"Script.cs\", name: \"init.cs\", beginRenaming: true, text: new(true, code));\n\t\t\t\t//note: can't auto-add to startup scripts now, because the final script name is unknown\n\t\t\t});\n\t\t}\n#endif\n\t}\n\n\tvoid _Other() {\n\t\tvar b = _Page(\"Other\");\n\t\tb.R.Add(\"Documentation\", out ComboBox localHelp).Items(\"Local docs of this app version, in Read panel|Online docs of the latest version, in web browser\").Select(App.Settings.doc_web ? 1 : 0);\n\t\tb.R.Add(\"Internet search URL\", out TextBox internetSearchUrl, App.Settings.internetSearchUrl);\n\t\tb.R.Add(out CheckBox minimalSdk, \"Use minimal .NET SDK\").Checked(App.Settings.minimalSDK, threeState: true).Tooltip(\"The SDK is used to install NuGet packages and for the Publish feature.\\nIndeterminate - use full SDK if installed, else minimal.\");\n\t\tb.R.Add(out CheckBox printCompiled, \"Always print \\\"Compiled\\\"\").Checked(App.Settings.comp_printCompiled, threeState: true)\n\t\t\t.Tooltip(\"Always print a \\\"Compiled\\\" message when a script etc compiled successfully.\\nIf unchecked, prints only if role is exeProgram or classLibrary.\\nIf 3-rd state, prints only when executing the Compile command.\");\n\t\tb.End();\n\t\t\n\t\tb.Loaded += () => {\n\t\t\t_b.OkApply += e => {\n\t\t\t\tif ((localHelp.SelectedIndex == 1) != App.Settings.doc_web) {\n\t\t\t\t\tApp.Settings.doc_web ^= true;\n\t\t\t\t\tDocsHttpServer.StartOrSwitch();\n\t\t\t\t}\n\t\t\t\tApp.Settings.internetSearchUrl = internetSearchUrl.TextOrNull();\n\t\t\t\tApp.Settings.minimalSDK = minimalSdk.IsChecked;\n\t\t\t\tApp.Settings.comp_printCompiled = printCompiled.IsChecked;\n\t\t\t};\n\t\t};\n\t}\n\t\n\tunsafe void _OS() {\n\t\tvar b = _Page(\"OS\");\n\t\tb.R.xAddInfoBlockF($\"Some Windows settings for all programs{(!App.IsPortable ? null : \"\\n<s c='red'>Portable mode warning: portable apps should not change Windows settings.</s>\")}\");\n\t\t\n\t\tb.R.Add(\"Key/mouse hook timeout, ms\", out TextBox hooksTimeout).Width(70, \"L\");\n\t\tb.R.Add(out KCheckBox cDisableLAW, \"Disable \\\"lock active window\\\"\");\n\t\tb.R.Add(out KCheckBox cUnderlineAK, \"Underline menu/dialog item access keys\");\n\t\tb.R.AddButton(\"Java...\", _ => Delm.Java.EnableDisableJabUI(this)).Width(70, \"L\");\n\t\tb.End();\n\t\t\n\t\tb.Loaded += () => {\n\t\t\thooksTimeout.Text = WindowsHook.LowLevelHooksTimeout.ToS();\n\t\t\tb.Validation(hooksTimeout, o => ((o as TextBox).Text.ToInt() is >= 300 and <= 1000) ? null : \"300-1000\");\n\t\t\tbool disableLAW = 0 == Api.SystemParametersInfo(Api.SPI_GETFOREGROUNDLOCKTIMEOUT, 0);\n\t\t\tcDisableLAW.IsChecked = disableLAW;\n\t\t\tbool underlineAK = Api.SystemParametersInfo(Api.SPI_GETKEYBOARDCUES);\n\t\t\tcUnderlineAK.IsChecked = underlineAK;\n\t\t\t\n\t\t\t_b.OkApply += e => {\n\t\t\t\tint t = hooksTimeout.Text.ToInt();\n\t\t\t\tif (t != WindowsHook.LowLevelHooksTimeout) {\n\t\t\t\t\tWindowsHook.LowLevelHooksTimeout = t;\n\t\t\t\t\tprint.it(\"Info: The new hook timeout value will be used after restarting Windows.\");\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (cDisableLAW.IsChecked != disableLAW)\n\t\t\t\t\tApi.SystemParametersInfo(Api.SPI_SETFOREGROUNDLOCKTIMEOUT, 0, (void*)(disableLAW ? 15000 : 0), save: true, notify: true);\n\t\t\t\tif (cUnderlineAK.IsChecked != underlineAK)\n\t\t\t\t\tApi.SystemParametersInfo(Api.SPI_SETKEYBOARDCUES, 0, (void*)(underlineAK ? 0 : 1), save: true, notify: true);\n\t\t\t};\n\t\t};\n\t}\n\t\n\tclass _Api : NativeApi {\n\t\t[DllImport(\"gdi32.dll\", EntryPoint = \"EnumFontFamiliesExW\")]\n\t\tinternal static extern int EnumFontFamiliesEx(IntPtr hdc, in Api.LOGFONT lpLogfont, FONTENUMPROC lpProc, nint lParam, uint dwFlags);\n\t\tinternal unsafe delegate int FONTENUMPROC(Api.LOGFONT* lf, IntPtr tm, uint fontType, nint lParam);\n\t\t\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/App/MainWindow.cs",
    "content": "using Au.Controls;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Interop;\nusing System.Windows.Input;\nusing System.Windows.Media;\n\nnamespace LA;\n\npartial class MainWindow : Window {\n\tprotected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e) {\n\t\tif (e.Property == VisibilityProperty && (Visibility)e.NewValue == Visibility.Visible) {\n\t\t\t//This is the first OnX when showing window.\n\t\t\t//\tWindow.Show just sets the Visibility property.\n\t\t\t//\tThen WPF changes Left, Top, some other properties, and calls OnInitialized.\n\t\t\tif (!_inited) { _inited = true; _Init(); }\n\t\t}\n\t\tbase.OnPropertyChanged(e);\n\t}\n\tbool _inited;\n\t\n\tvoid _Init() {\n\t\t//_StartProfileOptimization();\n\t\t\n\t\tApplication.Current.Resources = Application.LoadComponent(new(\"/Au.Editor;component/app/app-resources.xaml\", UriKind.Relative)) as ResourceDictionary;\n\t\t\n\t\tTitle = App.AppName; //don't append document name etc\n\t\t\n\t\tif (App.Settings.wndpos.main == null) {\n\t\t\tWidth = 1200;\n\t\t\tHeight = 750;\n\t\t\tWindowStartupLocation = WindowStartupLocation.CenterScreen;\n\t\t\t//and will EnsureInScreen\n\t\t}\n\t\tWndSavedRect.Restore(this, App.Settings.wndpos.main, o => App.Settings.wndpos.main = o);\n\t\t\n\t\tPanels.LoadAndCreateToolbars();\n\t\t\n\t\tApp.Commands = new KMenuCommands(typeof(Menus), Panels.Menu);\n\t\t\n\t\tApp.Commands.OnCustomizingError = (c, s, ex) => print.it($\"<>Customization error in <+DCustomize>{c.Name}<>: {s}. {ex?.ToStringWithoutStack()}\");\n\t\tvar atb = new ToolBar[6] { Panels.TTools, Panels.TFile, Panels.TRun, Panels.TEdit, Panels.TCustom1, Panels.TCustom2 };\n\t\tApp.Commands.InitToolbarsAndCustomize(folders.ThisAppBS + @\"Default\\Commands.xml\", AppSettings.DirBS + \"Commands.xml\", atb);\n\t\t\n\t\tif (App.Commands[nameof(Menus.File.New)].FindMenuButtonInToolbar(Panels.TFile) is { } bNew)\n\t\t\tbNew.MouseDoubleClick += (_, e) => { e.Handled = true; Menus.File.New.New_script(); };\n\t\tif (App.Commands[nameof(Menus.Run.Run_script)].FindButtonInToolbar(Panels.TRun) is { } bRun) { //make Run button bigger\n\t\t\tbool vert = bRun.Parent is ToolBar { Orientation: Orientation.Vertical };\n\t\t\tif (vert) bRun.MinHeight = 40; else bRun.MinWidth = 40;\n\t\t\tThickness margin = vert ? new(0, 8, 0, 8) : new(8, 0, 8, 0);\n\t\t\tbRun.Margin = margin;\n\t\t\tif (App.Commands[nameof(Menus.Run.Debug_run)].FindButtonInToolbar(Panels.TRun) is { } bDebugRun) bDebugRun.Margin = margin;\n\t\t}\n\t\t\n\t\tPanels.CreatePanels();\n\t\t\n\t\tApp.Commands.BindKeysTarget(this, \"\");\n\t\t\n\t\tPanels.PanelManager.Container = g => { this.Content = g; };\n\t\t\n\t\t_NormalizeMouseWheel();\n\t\t\n\t\tDocsHttpServer.StartOrSwitch();\n\t\t\n#if DEBUG\n\t\tApp.Timer1s += () => {\n\t\t\tvar e = Keyboard.FocusedElement as FrameworkElement;\n\t\t\tDebug_.PrintIf(e != null && !e.IsVisible, \"focused invisible\");\n\t\t\t//print.it(e, FocusManager.GetFocusedElement(App.Wmain));\n\t\t};\n#endif\n\t}\n\t\n\tprotected override void OnSourceInitialized(EventArgs e) {\n\t\tvar hs = PresentationSource.FromVisual(this) as HwndSource;\n\t\tApp.Hmain = (wnd)hs.Handle;\n\t\t\n\t\ths.CompositionTarget.RenderMode = RenderMode.SoftwareOnly;\n\t\tRenderOptions.ProcessRenderMode = RenderMode.SoftwareOnly;\n\t\t\n\t\tbase.OnSourceInitialized(e);\n\t\t\n\t\tif (App.Settings.wndpos.main == null) App.Hmain.EnsureInScreen();\n\t\t\n\t\t//workaround for: sometimes OS does not set foreground window. Then we have a fake active/focused state (blinking caret, called OnActivated, etc).\n\t\t//\t1. When started hidden, and now clicked tray icon first time. Is it because of the \"lock foreground window\"? Or WPF shows window somehow incorrectly?\n\t\t//\t2. When starting visible, if VMWare Player is active. Same with some other programs too (WPF, appstore, some other).\n\t\t//this.Activate(); //does not work with VMWare, also if user clicks a window after starting this process\n\t\tApp.Hmain.ActivateL(); //works always, possibly with workarounds\n\t\t\n\t\tPanels.PanelManager[\"Output\"].Visible = true;\n\t\t\n\t\tApp.Model.WorkspaceLoadedWithUI(onUiLoaded: true);\n\t\t\n\t\tApp.Loaded = AppState.LoadedUI;\n\t\t\n\t\t//workaround for WPF bug: on Environment.Exit its \"process exit\" event handler creates chaoss, eg pumps messages, including wmtimer, and app crashes.\n\t\tprocess.thisProcessExitDone_ += () => { //after calling all process.thisProcessExit event handlers\n\t\t\tif (App.Loaded == AppState.LoadedUI) Api.ExitProcess(Environment.ExitCode);\n\t\t};\n\t\t\n\t\tCodeInfo.UiLoaded();\n\t\t\n\t\tUacDragDrop.AdminProcess.Enable(true); //rejected: disable when hiding main window. Some other window may be visible.\n\t\t\n\t\ths.AddHook(_WndProc);\n\t\t\n\t\tRegHotkeys.RegisterPermanent();\n\t\t\n\t\tApp.OnMainWindowLoaded_();\n\t\t\n\t\t//Created?.Invoke();\n\t\t\n\t\tLoaded += (_, _) => {\n\t\t\tEditorExtension.WindowLoaded_();\n\t\t\tCommandLine.UILoaded();\n\t\t};\n\t}\n\t\n\t///// <summary>\n\t///// When window handle created.\n\t///// Documents are open, etc.\n\t///// </summary>\n\t//public event Action Created;\n\t\n\tprotected override void OnClosing(CancelEventArgs e) {\n\t\t//note: called by Window.Close (sync) and Application.Shutdown (async) even if the window never was loaded.\n\t\t\n\t\tif (App.Loaded == AppState.LoadedUI) {\n\t\t\tApp.Model.Save.AllNowIfNeed();\n\t\t\tPanels.PanelManager.Save();\n\t\t\tTUtil2.CloseDialogsInNonmainThreads(); //let they save rects etc\n\t\t}\n\t\t\n\t\tEditorExtension.ClosingWorkspace_(onExit: true); //must be called before closing documents\n\t\t\n\t\tbase.OnClosing(e);\n\t}\n\t\n\tprotected override void OnClosed(EventArgs e) {\n\t\t//note: called by Window.Close (sync) and Application.Shutdown (async) even if the window never was loaded.\n\t\t\n\t\tbool loaded = App.Loaded == AppState.LoadedUI;\n\t\tApp.Loaded = AppState.Unloading;\n\t\tApp.OnMainWindowClosed_();\n\t\tbase.OnClosed(e);\n\t\tif (loaded) {\n\t\t\tUacDragDrop.AdminProcess.Enable(false);\n\t\t\tCodeInfo.Stop();\n\t\t\tApp.Model.Save.AllNowIfNeed();\n\t\t}\n\t}\n\t\n\tunsafe nint _WndProc(nint hwnd, int msg, nint wParam, nint lParam, ref bool handled) {\n\t\tvar w = (wnd)hwnd;\n\t\t\n\t\tswitch (msg) {\n\t\tcase Api.WM_DPICHANGED:\n\t\t\tthis.DpiChangedWorkaround();\n\t\t\tbreak;\n\t\tcase Api.WM_HOTKEY:\n\t\t\thandled = true;\n\t\t\tRegHotkeys.WmHotkey_(wParam);\n\t\t\tbreak;\n\t\tcase Api.WM_ACTIVATEAPP: //note: not received at startup, because sets the hook when the window is already active\n\t\t\tOnActivatedDeactivatedAppOrPip_(wParam != 0);\n\t\t\tbreak;\n\t\tcase Api.WM_SYSCOMMAND when (wParam & 0xFFF0) == Api.SC_CLOSE:\n\t\t\tif (handled = App.Settings.runHidden) Hide_();\n\t\t\tbreak;\n\t\t}\n\t\t\n\t\treturn default;\n\t}\n\t\n\t/// <summary>\n\t/// Called on WM_ACTIVATEAPP of the main window. In LA PiP also called on WM_ACTIVATE of the host PiP window, if the main window is active.\n\t/// </summary>\n\tinternal static void OnActivatedDeactivatedAppOrPip_(bool activated) {\n\t\tif (activated) {\n\t\t\ts_appActivatedTimer ??= new(static _ => {\n\t\t\t\tPanels.Editor.OnAppActivated_();\n\t\t\t\tif (App.Settings.checkForUpdates) App.CheckForUpdates();\n\t\t\t\tGit.AutoBackup(false);\n\t\t\t});\n\t\t\ts_appActivatedTimer.After(250);\n\t\t} else {\n\t\t\ts_appActivatedTimer?.Stop();\n\t\t\tApp.Model.Save.AllNowIfNeed();\n\t\t}\n\t}\n\tstatic timer s_appActivatedTimer;\n\t\n\tinternal void Hide_() {\n\t\tif (IsVisible) {\n\t\t\tApp.Model.Save.AllNowIfNeed();\n\t\t\tPanels.PanelManager.Save();\n\t\t\tHide();\n\t\t\tprocess.ThisProcessMinimizePhysicalMemory_(1000);\n\t\t}\n\t}\n\t\n\t//this was for testing document tabs. Now we don't use document tabs. All documents now are in single panel.\n\t//void _OpenDocuments() {\n\t//\tvar docLeaf = _AddDoc(\"Document 1\");\n\t//\t_AddDoc(\"Document 2\");\n\t//\t_AddDoc(\"Document 3\");\n\t//\t_AddDoc(\"Document 4\");\n\t//\tdocLeaf.Visible = true;\n\t//\t//Panels.DocPlaceholder_.Visible = false;\n\t//\tdocLeaf.Content.Focus();\n\t\n\t//\tKPanels.ILeaf _AddDoc(string name) {\n\t//\t\t//var docPlaceholder = App.Panels[\"Open\"]; //in stack\n\t//\t\tvar docPlaceholder = Panels.DocPlaceholder_; //in tab\n\t//\t\tvar v = docPlaceholder.AddSibling(false, KPanels.LeafType.Document, name, true);\n\t//\t\tv.Closing += (_, e) => { e.Cancel = !dialog.showOkCancel(\"Close?\"); };\n\t//\t\tv.ContextMenuOpening += (o, m) => {\n\t//\t\t\tvar k = o as KPanels.ILeaf;\n\t//\t\t\tm.Separator();\n\t//\t\t\tm[\"Close 2\"] = o => k.Delete();\n\t//\t\t};\n\t//\t\tv.TabSelected += (_, _) => _OpenDoc(v);\n\t\n\t//\t\treturn v;\n\t//\t}\n\t\n\t//\tstatic void _OpenDoc(KPanels.ILeaf leaf) {\n\t//\t\tif (leaf.Content != null) return;\n\t//\t\tleaf.Content = new KScintilla();\n\t//\t}\n\t//}\n\t\n\t//Used to make faster, but now with tiered JIS makes faster only by ~100 ms.\n\tstatic void _StartProfileOptimization() {\n#if !DEBUG\n\t\tvar fProfile = folders.ThisAppDataLocal + \"ProfileOptimization\";\n\t\tfilesystem.createDirectory(fProfile);\n\t\tSystem.Runtime.ProfileOptimization.SetProfileRoot(fProfile);\n\t\tSystem.Runtime.ProfileOptimization.StartProfile(\"Au.Editor.startup\");\n#endif\n\t}\n\t\n\tpublic void AaShowAndActivate() {\n\t\tShow();\n\t\tvar w = App.Hmain;\n\t\tw.ShowNotMinimized();\n\t\tw.ActivateL();\n\t}\n\t\n\t//If winver < 10 or disabled normal mouse scrolling, sets a mouse hook to scroll the mouse control.\n\t//\tWithout it can't scroll any KTreeView even if focused.\n\tstatic unsafe void _NormalizeMouseWheel() {\n\t\tif (osVersion.minWin10 && Api.SystemParametersInfo(Api.SPI_GETMOUSEWHEELROUTING, 0) >= 2) return;\n\t\tWindowsHook.ThreadGetMessage(k => {\n\t\t\tif (k.PM_NOREMOVE) return;\n\t\t\tif (k.msg->message is Api.WM_MOUSEWHEEL or Api.WM_MOUSEHWHEEL) {\n\t\t\t\tvar w = wnd.fromMouse(WXYFlags.Raw);\n\t\t\t\tif (w.IsOfThisThread) k.msg->hwnd = w;\n\t\t\t}\n\t\t});\n\t\t//never mind other threads. Eg the wnd and elm tools.\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/App/Menus.cs",
    "content": "/*\nLibreAutomate's menu bar menus are defined in the `Menus` class. The menu hierarchy is the same as the hierarchy of nested classes.\nAt startup the program creates all menus using reflection. Also it creates customizable toolbars with buttons for the same commands.\nFor each class and method with `[Command]` attribute, the program creates a menu item and its `ICommand`. Classes define menus/submenus. Methods define leaf items.\nSome classes also have method named `_Init`. The program calls it when it created all menus. The method eg can set initial check states or event handlers to populate the submenu dynamically.\n\n## Menu item properties\nAll properties can be defined via `CommandAttribute`. Some properties are by default derived from the method/class name.\n\n- `name` (string) - command name (used to find commands, customization, etc). Assigned in one of ways: 1. By default it's the name of the method or class. 2. Attribute property `name`; this way can be used to prevent duplicates.\n- `text` (string) - the display text (in menu item and button tooltip). Assigned in one of ways: 1. By default it's the name of the method or class, but underscores are replaced with spaces; if the first argument is \"...\", it is appended. 2. The first argument as string, except if it's \"...\".\n- `checkable` (bool) - it's a toggle.\n- `hide` (bool) - it's a command without UI (menu item, toolbar button).\n- `image` (string) - icon of the menu item and toolbar button. Usually XAML icon name.\n- `keys` (string) - default hotkey. Users can change it.\n- `keysText` (string) - alternative display text for hotkey.\n- `namePrefix` (string) - name prefix of the command, and default prefix of descendants. Used to prevent duplicates.\n- `separator` (bool) - add separator before the menu item.\n- `tooltip` (string) - tooltip text of the menu item and toolbar button (to be appended to the display text).\n- `target` (string) - name of panel etc where the hotkey(s) is/are active. Inheritable. If \"\" - in entire window (used to override the inherited target).\n- `underlined` (char) - the access character. Assigned in one of ways: 1. The first argument as char. 2. Underscore in explicitly specified display text. 3. Auto-assigned by the program.\n\nWhen AI generates documentation from this code:\n- Include all menu items generated from classes and methods with `[Command]` attribute, except where `hide` is present.\n- Markdown format: Use headings for menus/submenus (hierarchical, not flat). Place leaf items in lists.\n- Generate menu item descriptions from these properties: `text`, `checkable`, `tooltip`. Ignore other properties and separators. Example: `- **Display text**. Tooltip or optionally AI-generated short description.`.\n- If a tooltip is missing and the display text does not give enough information to users, generate short description from the display text or/and the method code. Don't generate if you are unsure how the command works. Better no description than incorrect description.\n- You can generate multiline descriptions. Keep newlines in tooltips.\n- Where method `_Init` populates submenu items dynamically, mention it somehow, eg \"Populated dynamically at run time\".\n- The generated Markdown document will be part of the LibreAutomate documentation. LibreAutomate users will see it.\n*/\n\nusing Au.Controls;\nusing ToolLand;\nusing System.Windows;\nusing System.Windows.Controls;\n\nnamespace LA;\n\nstatic class Menus {\n\tconst string\n\t\tblack = EdIcons.black,\n\t\tblue = EdIcons.blue,\n\t\tgreen = EdIcons.green,\n\t\tgreen2 = EdIcons.green2,\n\t\tbrown = EdIcons.brown,\n\t\tpurple = EdIcons.purple,\n\t\tdarkYellow = EdIcons.darkYellow,\n\t\torange = EdIcons.orange,\n\t\tred = EdIcons.red\n\t\t;\n\t\n\t[Command(target = \"Files\")]\n\tpublic static class File {\n\t\t[Command(target = \"\", image = \"*Material.FilePlusOutline\" + black)]\n\t\tpublic static class New {\n\t\t\tstatic FileNode _New(string name) => App.Model.NewItem(name, beginRenaming: true);\n\t\t\t\n\t\t\t[Command('s', keys = \"Ctrl+N\", image = EdIcons.Script, tooltip = \"A script is a C# code file that can be executed like a program.\")]\n\t\t\tpublic static void New_script() { _New(\"Script.cs\"); }\n\t\t\t\n\t\t\t[Command('c', image = EdIcons.Class, tooltip = \"Class files contain C# classes/functions that can be used in other C# files.\")]\n\t\t\tpublic static void New_class() { _New(\"Class.cs\"); }\n\t\t\t\n\t\t\t[Command('t', image = \"*FeatherIcons.FileText\" + black, tooltip = \"Text files contain any text except C# code.\\nTo change the file type, edit the \\\"txt\\\" in the filename.\")]\n\t\t\tpublic static void New_text_file() { _New(\"File.txt\"); }\n\t\t\t\n\t\t\t[Command('f', image = EdIcons.Folder)]\n\t\t\tpublic static void New_folder() { _New(null); }\n\t\t\t\n\t\t\tstatic void _Init(KMenuCommands mc, KMenuCommands.Command c) {\n\t\t\t\tc.OnSubmenuOpened(mi => FilesModel.FillMenuNew(mi));\n\t\t\t}\n\t\t}\n\t\t\n\t\t[Command(\"Delete...\", separator = true, keysText = \"Delete\", image = \"*Modern.Delete\" + black)]\n\t\tpublic static void Delete() { App.Model.DeleteSelected(); }\n\t\t\n\t\t[Command(keys = \"F2\", image = \"*BoxIcons.RegularRename @15\" + black)]\n\t\tpublic static void Rename() { App.Model.RenameSelected(); }\n\t\t\n\t\t[Command(image = \"*RemixIcon.ChatSettingsLine\" + green)]\n\t\tpublic static void Properties() { App.Model.Properties(); }\n\t\t\n\t\t[Command(\"Copy, paste\")]\n\t\tpublic static class CopyPaste {\n\t\t\t[Command(\"Multi-select\", checkable = true, image = \"*Modern.ListTwo\" + green, tooltip = \"If checked:\\n    - You can select multiple items with Ctrl or Shift.\\n    - Double-click to open.\")]\n\t\t\tpublic static void MultiSelect_files() { Panels.Files.TreeControl.SetMultiSelect(toggle: true); }\n\t\t\t\n\t\t\t[Command(\"Cu_t\", separator = true, keysText = \"Ctrl+X\")]\n\t\t\tpublic static void Cut_file() { App.Model.CutCopySelected(true); }\n\t\t\t\n\t\t\t[Command(\"Copy\", keysText = \"Ctrl+C\")]\n\t\t\tpublic static void Copy_file() { App.Model.CutCopySelected(false); }\n\t\t\t\n\t\t\t[Command(\"Paste\", keysText = \"Ctrl+V\")]\n\t\t\tpublic static void Paste_file() { App.Model.Paste(); }\n\t\t\t\n\t\t\t[Command(\"Cancel Cut/Copy\", keysText = \"Esc\")]\n\t\t\tpublic static void CancelCutCopy_file() { App.Model.Uncut(); }\n\t\t\t\n\t\t\t[Command('r', separator = true, tooltip = @\"Path in this workspace (in the Files list), like \"\"\\Folder\\File.cs\"\".\")]\n\t\t\tpublic static void Copy_relative_path() { App.Model.SelectedCopyPath(false); }\n\t\t\t\n\t\t\t[Command('f', tooltip = \"Full path of the file.\")]\n\t\t\tpublic static void Copy_full_path() { App.Model.SelectedCopyPath(true); }\n\t\t}\n\t\t\n\t\t[Command(\"Open, close\")]\n\t\tpublic static class OpenClose {\n\t\t\t[Command(keysText = \"Enter\")]\n\t\t\tpublic static void Open() { App.Model.OpenSelected(1); }\n\t\t\t\n\t\t\t[Command(image = \"*MaterialDesign.OpenInNew\" + black)]\n\t\t\tpublic static void Open_in_default_app() { App.Model.OpenSelected(3); }\n\t\t\t\n\t\t\t[Command(image = \"*Material.FolderMarker\" + darkYellow)]\n\t\t\tpublic static void Select_in_Explorer() { App.Model.OpenSelected(4); }\n\t\t\t\n\t\t\t[Command(separator = true, target = \"\", keys = \"Ctrl+F4\", keysText = \"M-click\")]\n\t\t\tpublic static void Close() { App.Model.CloseEtc(FilesModel.ECloseCmd.CloseSelectedOrCurrent); }\n\t\t\t\n\t\t\t[Command(target = \"\")]\n\t\t\tpublic static void Close_all() { App.Model.CloseEtc(FilesModel.ECloseCmd.CloseAll); }\n\t\t\t\n\t\t\t[Command(separator = true, target = \"\", image = \"*Codicons.CollapseAll\" + black)]\n\t\t\tpublic static void Collapse_all_folders() { App.Model.CloseEtc(FilesModel.ECloseCmd.CollapseAllFolders); }\n\t\t\t\n\t\t\t[Command(target = \"\", image = \"*Codicons.CollapseAll\" + black)]\n\t\t\tpublic static void Collapse_inactive_folders() { App.Model.CloseEtc(FilesModel.ECloseCmd.CollapseInactiveFolders); }\n\t\t}\n\t\t\n\t\t[Command(\"Export, import\", separator = true)]\n\t\tpublic static class ExportImport {\n\t\t\t[Command(\"...\")]\n\t\t\tpublic static void Export() { App.Model.ExportSelected(); }\n\t\t\t\n\t\t\t[Command(\"Import .zip...\", separator = true)]\n\t\t\tpublic static void Import_zip() {\n\t\t\t\tvar d = new FileOpenSaveDialog(\"{4D1F3AFB-DA1A-45AC-8C12-41DDA5C51CDA}\") { Title = \"Import .zip\", FileTypes = \"Zip files|*.zip\" };\n\t\t\t\tif (d.ShowOpen(out string s, App.Hmain))\n\t\t\t\t\tApp.Model.ImportWorkspace(s);\n\t\t\t}\n\t\t\t\n\t\t\t[Command(\"...\")]\n\t\t\tpublic static void Import_workspace() {\n\t\t\t\tvar d = new FileOpenSaveDialog(\"{4D1F3AFB-DA1A-45AC-8C12-41DDA5C51CDA}\") { Title = \"Import workspace\" };\n\t\t\t\tif (d.ShowOpen(out string s, App.Hmain, selectFolder: true))\n\t\t\t\t\tApp.Model.ImportWorkspace(s);\n\t\t\t}\n\t\t\t\n\t\t\t[Command(\"...\")]\n\t\t\tpublic static void Import_files() { App.Model.ImportFiles(false); }\n\t\t\t\n\t\t\t[Command(\"...\")]\n\t\t\tpublic static void Import_folder() { App.Model.ImportFiles(true); }\n\t\t}\n\t\t\n\t\t[Command(target = \"\")]\n\t\tpublic static class Workspace {\n\t\t\tstatic void _Init(KMenuCommands mc, KMenuCommands.Command c) {\n\t\t\t\tc.OnSubmenuOpened(mi => FilesModel.FillMenuRecentWorkspaces(mi));\n\t\t\t}\n\t\t\t\n\t\t\t[Command(image = \"*Material.Reload\" + black)]\n\t\t\tpublic static void Reload_this_workspace() { FilesModel.LoadWorkspace(App.Model.WorkspaceDirectory); }\n\t\t\t\n\t\t\t[Command(\"...\", separator = true)]\n\t\t\tpublic static void Open_workspace() { FilesModel.OpenWorkspaceUI(); }\n\t\t\t\n\t\t\t[Command(\"...\")]\n\t\t\tpublic static void New_workspace() { FilesModel.NewWorkspaceUI(); }\n\t\t\t\n\t\t\t[Command(\"...\", separator = true)]\n\t\t\tpublic static void Repair_workspace() { RepairWorkspace.Repair(); }\n\t\t\t\n\t\t\t[Command(separator = true, keys = \"Ctrl+S\", image = \"*BoxIcons.RegularSave\" + black, tooltip = \"Save all changes now (don't wait for auto-save). Editor text, files, settings etc.\")]\n\t\t\tpublic static void Save_now() { App.Model?.Save.AllNowIfNeed(); }\n\t\t}\n\t\t\n\t\t[Command(target = \"\", image = \"*Material.Git\" + blue)]\n\t\tpublic static class Git {\n\t\t\t[Command(image = \"*MaterialDesign.InfoOutline\" + blue)]\n\t\t\tpublic static void Git_status() { LA.Git.Status(); }\n\t\t\t\n\t\t\t[Command(\"Commit\", image = \"*RemixIcon.GitCommitLine\" + blue)]\n\t\t\tpublic static void Git_commit() { LA.Git.Commit(); }\n\t\t\t\n\t\t\t[Command(\"Push to GitHub\", image = \"*Unicons.CloudUpload\" + blue)]\n\t\t\tpublic static void Git_push() { LA.Git.Push(); }\n\t\t\t\n\t\t\t[Command(\"Pull from GitHub\", image = \"*Unicons.CloudDownload\" + blue)]\n\t\t\tpublic static void Git_pull() { LA.Git.Pull(); }\n\t\t\t\n\t\t\t[Command(\"GitHub Desktop\", image = \"*Codicons.Github\" + purple, separator = true)]\n\t\t\tpublic static void Git_gui() { LA.Git.RunGui(); }\n\t\t\t\n\t\t\t[Command(\"Cmd\", image = \"*Material.Console\" + black)]\n\t\t\tpublic static void Git_cmd() { LA.Git.RunCmd(); }\n\t\t\t\n\t\t\t[Command(\"Workspace folder\", image = \"*Material.Folder\" + darkYellow)]\n\t\t\tpublic static void Git_workspace_folder() { LA.Git.WorkspaceFolder(); }\n\t\t\t\n\t\t\t[Command(\"Reload workspace\", image = \"*Material.Reload\" + black)]\n\t\t\tpublic static void Git_reload_workspace() { LA.Git.ReloadWorkspace(); }\n\t\t\t\n\t\t\t[Command(\"GitHub sign out\", separator = true)]\n\t\t\tpublic static void Git_sign_out() { LA.Git.Signout(); }\n\t\t\t\n\t\t\t[Command(\"Maintenance...\")]\n\t\t\tpublic static void Git_maintenance() { LA.Git.Maintenance(); }\n\t\t\t\n\t\t\t[Command(\"...\")]\n\t\t\tpublic static void Git_setup() { LA.Git.Setup(); }\n\t\t}\n\t\t\n\t\t[Command(separator = true, target = \"\", keysText = \"Alt+F4\")]\n\t\tpublic static void Close_window() { if (App.Settings.runHidden) App.Wmain.Hide_(); else App.Wmain.Close(); }\n\t\t\n\t\t[Command(target = \"\")]\n\t\tpublic static void Exit() { Application.Current.Shutdown(); }\n\t}\n\t\n\t[Command(target = \"Edit\")]\n\tpublic static class Edit {\n\t\t[Command]\n\t\tpublic static class Clipboard {\n\t\t\t[Command('t', keys = \"Ctrl+X\", image = \"*Zondicons.EditCut\" + brown)]\n\t\t\tpublic static void Cut() { Panels.Editor.ActiveDoc.Call(Sci.SCI_CUT); }\n\t\t\t\n\t\t\t[Command(keys = \"Ctrl+C\", image = \"*Material.ContentCopy\" + brown)]\n\t\t\tpublic static void Copy() { Panels.Editor.ActiveDoc.ECopy(); }\n\t\t\t\n\t\t\t[Command(keys = \"Ctrl+V\", image = EdIcons.Paste)]\n\t\t\tpublic static void Paste() { Panels.Editor.ActiveDoc.EPaste(); }\n\t\t\t\n\t\t\t[Command(\"Copy _forum code\", image = \"*Material.ForumOutline\" + brown, separator = true)]\n\t\t\tpublic static void Forum_copy() { Panels.Editor.ActiveDoc.ECopy(SciCode.ECopyAs.Forum); }\n\t\t\t\n\t\t\t[Command(\"Copy HTML <span style>\")]\n\t\t\tpublic static void Copy_HTML_span_style() { Panels.Editor.ActiveDoc.ECopy(SciCode.ECopyAs.HtmlSpanStyle); }\n\t\t\t\n\t\t\t[Command(\"Copy HTML <span class> and CSS\")]\n\t\t\tpublic static void Copy_HTML_span_class_CSS() { Panels.Editor.ActiveDoc.ECopy(SciCode.ECopyAs.HtmlSpanClassCss); }\n\t\t\t\n\t\t\t[Command(\"Copy HTML <span class>\")]\n\t\t\tpublic static void Copy_HTML_span_class() { Panels.Editor.ActiveDoc.ECopy(SciCode.ECopyAs.HtmlSpanClass); }\n\t\t\t\n\t\t\t[Command]\n\t\t\tpublic static void Copy_markdown() { Panels.Editor.ActiveDoc.ECopy(SciCode.ECopyAs.Markdown); }\n\t\t\t\n\t\t\t[Command]\n\t\t\tpublic static void Copy_without_screenshots() { Panels.Editor.ActiveDoc.ECopy(SciCode.ECopyAs.TextWithoutScreenshots); }\n\t\t}\n\t\t\n\t\t[Command(\"Undo\")]\n\t\tpublic static class UndoRedo {\n\t\t\t[Command(keys = \"Ctrl+Z\", image = EdIcons.Undo)]\n\t\t\tpublic static void Undo() { SciUndo.OfWorkspace.UndoRedo(false); }\n\t\t\t\n\t\t\t[Command(keys = \"Ctrl+Y\", image = \"*Ionicons.RedoiOS\" + brown)]\n\t\t\tpublic static void Redo() { SciUndo.OfWorkspace.UndoRedo(true); }\n\t\t\t\n\t\t\t[Command(separator = true, tooltip = \"Undo a multi-file operation, such as renaming a symbol in multiple files.\")]\n\t\t\tpublic static void Undo_in_files() { SciUndo.OfWorkspace.UndoRedoMultiFileReplace(false); }\n\t\t\t\n\t\t\t[Command(tooltip = \"Redo a multi-file operation.\")]\n\t\t\tpublic static void Redo_in_files() { SciUndo.OfWorkspace.UndoRedoMultiFileReplace(true); }\n\t\t}\n\t\t\n\t\t[Command(\"Find\", separator = true)]\n\t\tpublic static class Find {\n\t\t\t[Command(\"Find text\", keys = \"Ctrl+F\", image = \"*Material.FindReplace\" + blue)]\n\t\t\tpublic static void Find_text() { Panels.Find.CtrlF(Panels.Editor.ActiveDoc); }\n\t\t\t\n\t\t\t[Command(\"...\", keys = \"Ctrl+T\", image = \"*FontAwesome.SearchLocationSolid\" + blue)]\n\t\t\tpublic static void Find_symbol() { CiFindGo.ShowSingle(); }\n\t\t\t\n\t\t\t[Command(keys = \"Shift+F12\", image = EdIcons.References, separator = true)]\n\t\t\tpublic static void Find_references() { CiFind.FindReferencesOrImplementations(false); }\n\t\t\t\n\t\t\t[Command(image = \"*Material.InformationVariant\" + blue)]\n\t\t\tpublic static void Find_implementations() { CiFind.FindReferencesOrImplementations(true); }\n\t\t\t\n\t\t\t[Command(keys = \"F12\", image = \"*RemixIcon.WalkFill\" + blue, separator = true)]\n\t\t\tpublic static void Go_to_definition() { CiGoTo.GoToDefinition(); }\n\t\t\t\n\t\t\t[Command]\n\t\t\tpublic static void Go_to_base() { CiGoTo.GoToBase(); }\n\t\t\t\n\t\t\t[Command(keys = \"F2\", image = \"*PicolIcons.Edit\" + blue, separator = true)]\n\t\t\tpublic static void Rename_symbol() { CiFind.RenameSymbol(); }\n\t\t\t\n\t\t\t[Command(keys = \"F3\", target = \"\", separator = true)]\n\t\t\tpublic static void Find_next() { Panels.Find.FindNextInEditor(false); }\n\t\t\t\n\t\t\t[Command(keys = \"Shift+F3\", target = \"\")]\n\t\t\tpublic static void Find_previous() { Panels.Find.FindNextInEditor(true); }\n\t\t\t\n\t\t\t[Command(keys = \"F4\", target = \"\")]\n\t\t\tpublic static void Next_found() { Panels.Found.NextFound(false); }\n\t\t\t\n\t\t\t[Command(keys = \"Shift+F4\", target = \"\")]\n\t\t\tpublic static void Previous_found() { Panels.Found.NextFound(true); }\n\t\t}\n\t\t\n\t\t[Command]\n\t\tpublic static class Assist {\n\t\t\t[Command(keysText = \"Ctrl+Space\", image = \"*FontAwesome.ListUlSolid\" + blue)]\n\t\t\tpublic static void Autocompletion_list() { CodeInfo.ShowCompletionList(); }\n\t\t\t\n\t\t\t[Command(keysText = \"Ctrl+Shift+Space\", image = \"*RemixIcon.ParenthesesLine\" + blue)]\n\t\t\tpublic static void Parameter_info() { CodeInfo.ShowSignature(); }\n\t\t\t\n\t\t\tconst string c_tooltip_Autocorrect_raw_enter = \"\"\"\nTemporarily disables the \"auto-complete statement on Enter\" features that are enabled in Options > Code editor.\nOther ways to insert new line before ) or ] or ; when the auto-completion features are enabled:\n- Shift+Enter. Or Ctrl+Enter, depending on settings.\n- Space, then Enter.\n- Enter, then Ctrl+Z (undo).\n- Continue a vertical method chain by typing . on the next line after the ;.\nTo exit statement when the features are disabled, use Ctrl+Enter. Or Shift+Enter, depending on settings.\nMore info in app help topic \"Code editor\".\n\"\"\";\n\t\t\t\n\t\t\t[Command(\"Raw Enter before ) ] ;\", checkable = true, separator = true, image = \"*Unicons.Enter\" + green, tooltip = c_tooltip_Autocorrect_raw_enter)]\n\t\t\tpublic static void Autocorrect_raw_enter() {\n\t\t\t\tApp.Settings.ci_tempRawEnter ^= true;\n\t\t\t\tif (!App.Settings.ci_enterBeforeParen && !App.Settings.ci_enterBeforeSemicolon)\n\t\t\t\t\tprint.it($\"<>Note: the <b>Raw Enter before ) ] ;<> option temporarily disables the statement auto-completion features if they are enabled in <+options {DOptions.EPage.CodeEditor}>Options<>. Currently it has no effect because the features are disabled.\");\n\t\t\t}\n\t\t\t\n\t\t\tstatic void _Init(KMenuCommands mc, KMenuCommands.Command c) {\n\t\t\t\tif (App.Settings.ci_tempRawEnter) mc[nameof(Autocorrect_raw_enter)].MenuItem.IsChecked = true;\n\t\t\t}\n\t\t}\n\t\t\n\t\t[Command]\n\t\tpublic static class Navigate {\n\t\t\t[Command(image = \"*Material.Bookmark @16\" + darkYellow)]\n\t\t\tpublic static void Toggle_bookmark() { Panels.Bookmarks.ToggleBookmark(); }\n\t\t\t\n\t\t\t[Command(image = \"*JamIcons.ArrowSquareUp\" + black, keys = \"Alt+Up\", noIndirectDisable = true)]\n\t\t\tpublic static void Previous_bookmark() { Panels.Bookmarks.NextBookmark(true); }\n\t\t\t\n\t\t\t[Command(image = \"*JamIcons.ArrowSquareDown\" + black, keys = \"Alt+Down\", noIndirectDisable = true)]\n\t\t\tpublic static void Next_bookmark() { Panels.Bookmarks.NextBookmark(false); }\n\t\t\t\n\t\t\t[Command(keys = \"Alt+Left\", target = \"\", image = EdIcons.Back, separator = true, noIndirectDisable = true)]\n\t\t\tpublic static void Go_back() { App.Model.EditGoBack.GoBack(); }\n\t\t\t\n\t\t\t[Command(keys = \"Alt+Right\", target = \"\", image = \"*EvaIcons.ArrowForward\" + black, noIndirectDisable = true)]\n\t\t\tpublic static void Go_forward() { App.Model.EditGoBack.GoForward(); }\n\t\t\t\n\t\t\t[Command(separator = true, target = \"\", keys = \"Ctrl+Tab\", noIndirectDisable = true)]\n\t\t\tpublic static void Previous_document() { var a = App.Model.OpenFiles; if (a.Count > 1) App.Model.SetCurrentFile(a[1]); }\n\t\t}\n\t\t\n\t\t[Command(separator = true)]\n\t\tpublic static class Selection {\n\t\t\t[Command(keys = \"Ctrl+/\", image = $\"*Material.SlashForward{brown} %1,1,7,1,f; *FontAwesome.AsteriskSolid{brown} %9\")]\n\t\t\tpublic static void Toggle_comment() { ModifyCode.Comment(null); }\n\t\t\t\n\t\t\t[Command(keysText = \"R-click margin\", image = $\"*Material.SlashForward{brown} %1,1,7,1,f; *Material.SlashForward{brown} %7,1,1,1,f,p\")]\n\t\t\tpublic static void Toggle_line_comment() { ModifyCode.Comment(null, notSlashStar: true); }\n\t\t\t\n\t\t\t[Command(image = $\"*Material.SlashForward{brown} %1,1,7,1,f; *Material.SlashForward{brown} %5,1,3,1,f; *Material.Plus{brown} %10,10,,1,,p\")]\n\t\t\tpublic static void Comment_selection() { ModifyCode.Comment(true); }\n\t\t\t\n\t\t\t[Command(image = $\"*Material.SlashForward{brown} %1,1,7,1,f; *Material.SlashForward{brown} %5,1,3,1,f; *Material.Minus{brown} %11,10,,1,,p\")]\n\t\t\tpublic static void Uncomment_selection() { ModifyCode.Comment(false); }\n\t\t\t\n\t\t\t//TODO2: Toggle multiline (parameters, collections, etc). Hotkey Ctrl+L.\n\t\t\t\n\t\t\t[Command(\"...\", image = \"*RemixIcon.BracesLine\" + brown, separator = true)]\n\t\t\tpublic static void Surround() { CiSnippets.Surround(); }\n\t\t\t\n\t\t\t[Command(\"...\")]\n\t\t\tpublic static void Documentation_tags() { run.itSafe(\"https://www.libreautomate.com/forum/showthread.php?tid=7461\"); }\n\t\t\t\n\t\t\t[Command(separator = true, keysText = \"Ctrl+A\")]\n\t\t\tpublic static void Select_all() { Panels.Editor.ActiveDoc.Call(Sci.SCI_SELECTALL); }\n\t\t}\n\t\t\n\t\t[Command]\n\t\tpublic static class Tidy_code {\n\t\t\t[Command(image = \"*PixelartIcons.AlignLeft\" + brown)]\n\t\t\tpublic static void Format_document() { ModifyCode.Format(false); }\n\t\t\t\n\t\t\t[Command(image = \"*PixelartIcons.AlignLeft\" + brown)]\n\t\t\tpublic static void Format_selection() { ModifyCode.Format(true); }\n\t\t\t\n\t\t\t[Command]\n\t\t\tpublic static void Disable_format_selection() { InsertCode.SurroundPragmaWarningFormat(); }\n\t\t\t\n\t\t\t[Command(\"Indent selected lines\", keysText = \"Tab\", image = \"*Material.FormatIndentIncrease\" + brown, separator = true)]\n\t\t\tpublic static void Indent() { Panels.Editor.ActiveDoc.Call(Sci.SCI_TAB); }\n\t\t\t//never mind: now does not indent empty lines if was no indent.\n\t\t\t\n\t\t\t[Command(\"Unindent selected lines\", keysText = \"Shift+Tab\", image = \"*Material.FormatIndentDecrease\" + brown)]\n\t\t\tpublic static void Unindent() { Panels.Editor.ActiveDoc.Call(Sci.SCI_BACKTAB); }\n\t\t\t\n\t\t\t[Command(\"Deduplicate wnd.find\", separator = true, image = \"*Material.Broom\" + brown, tooltip = \"Remove duplicate `wnd.find` statements in all or selected text.\")]\n\t\t\tpublic static void Deduplicate_wnd_find() { ModifyCode.CleanupWndFind(); }\n\t\t\t\n\t\t\t[Command(tooltip = \"Remove screenshots in all or selected text.\\nScreenshot images are saved in text as hidden comments.\")]\n\t\t\tpublic static void Remove_screenshots() { Panels.Editor.ActiveDoc.EImageRemoveScreenshots(); }\n\t\t}\n\t\t\n\t\t[Command]\n\t\tpublic static class Generate {\n\t\t\t[Command(keys = \"Ctrl+Shift+D\", image = \"*Material.Lambda\" + brown)]\n\t\t\tpublic static void Create_delegate() { GenerateCode.CreateDelegate(); }\n\t\t\t\n\t\t\t[Command(\"Implement interface/abstract\", tooltip = \"Implement members of interface or abstract base class.\")]\n\t\t\tpublic static void Implement_interface() { GenerateCode.ImplementInterfaceOrAbstractClass(); }\n\t\t\t\n\t\t\t[Command]\n\t\t\tpublic static void Create_event_handlers() { GenerateCode.CreateEventHandlers(); }\n\t\t\t\n\t\t\t[Command]\n\t\t\tpublic static void Create_overrides() { GenerateCode.CreateOverrides(); }\n\t\t\t\n\t\t\t[Command(\"Create GUID\")]\n\t\t\tpublic static void Create_GUID() { var s = Guid.NewGuid().ToString(); clipboard.text = s; print.it($\"Clipboard: {s}\"); }\n\t\t\t//}\n\t\t\t\n\t\t\t//[Command]\n\t\t\t//public static class Refactor {\n\t\t\t[Command]\n\t\t\tpublic static void Add_function_Main() { InsertCode.AddClassProgram(); }\n\t\t\t\n\t\t\t[Command(\"Convert [PreserveSig] methods\")]\n\t\t\tpublic static void Convert_PreserveSig() { ModifyCode.ConvertInterfaceMethodPreserveSig(); }\n\t\t}\n\t\t\n\t\t[Command(separator = true)]\n\t\tpublic static class View {\n\t\t\t[Command(checkable = true, keys = \"Ctrl+W\", image = \"*Codicons.WordWrap\" + green)]\n\t\t\tpublic static void Wrap_lines() { SciCode.EToggleView_call_from_menu_only_(SciCode.EView.Wrap); }\n\t\t\t\n\t\t\t[Command(checkable = true, image = \"*Material.TooltipImageOutline\" + green, tooltip = \"If checked, displays images (file icons, screenshots, \\\"image:\\\", \\\"*icon\\\").\\nAlso captures screenshots when recording etc.\")]\n\t\t\tpublic static void Images_in_code() { SciCode.EToggleView_call_from_menu_only_(SciCode.EView.Images); }\n\t\t\t\n\t\t\t[Command(checkable = true, image = \"*Codicons.Preview\" + green)]\n\t\t\tpublic static void WPF_preview(MenuItem mi) { SciCode.WpfPreviewStartStop(mi); }\n\t\t\t\n\t\t\t[Command(\"Customize...\", separator = true)]\n\t\t\tpublic static void Customize_edit_context_menu() { DCustomizeContextMenu.Dialog(\"Edit\", \"code editor\"); }\n\t\t}\n\t}\n\t\n\t[Command(target = \"Edit\", tooltip = \"Tools for creating code\")]\n\tpublic static class Code {\n\t\t[Command(underlined: 'r', image = \"*Material.RecordRec\" + blue)]\n\t\tpublic static void Input_recorder() { DInputRecorder.ShowRecorder(); }\n\t\t\n\t\t[Command(\"Find _window\", image = \"*BoxIcons.SolidWindowAlt\" + blue)]\n\t\tpublic static void wnd() { ToolProcess.StartDwnd(); }\n\t\t\n\t\t[Command(\"Find UI _element\", image = \"*Material.CheckBoxOutline @15\" + blue)]\n\t\tpublic static void elm() { ToolProcess.StartDelm(); }\n\t\t\n\t\t[Command(\"Find _image\", image = \"*Material.ImageSearchOutline\" + blue)]\n\t\tpublic static void uiimage() { ToolProcess.StartDuiimage(); }\n\t\t\n\t\t[Command(\"Find _OCR text\", image = \"*Material.Ocr\" + blue)]\n\t\tpublic static void ocr() { ToolProcess.StartDocr(); }\n\t\t\n\t\t[Command(image = \"*Material.FolderOutline\" + blue)]\n\t\tpublic static void Get_files_in_folder() { DEnumDir.Dialog(); }\n\t\t\n\t\t[Command(keysText = \"Ctrl+Space in string\", image = EdIcons.Keys)]\n\t\tpublic static void Keys() { CiTools.CmdShowKeysWindow(); }\n\t\t\n\t\t[Command(underlined: 'x', image = EdIcons.Regex, keysText = \"Ctrl+Space in string\")]\n\t\tpublic static void Regex() { CiTools.CmdShowRegexWindow(); }\n\t\t\n\t\t[Command(image = EdIcons.Color)]\n\t\tpublic static void Color() { KColorPicker.ColorTool(s => { clipboard.text = s; print.it($\"Clipboard: {s}\"); }, App.Wmain, modal: false, add0xRgbButton: true, addBgrButton: true); }\n\t\t\n\t\t[Command(underlined: 'A', image = \"*Material.Api\" + blue)]\n\t\tpublic static void Windows_API() { new DWinapi().Show(); }\n\t\t\n\t\t[Command(separator = true)]\n\t\tpublic static void Quick_capturing_info() { QuickCapture.Info(); }\n\t}\n\t\n\t[Command(\"T\\x2009T\", target = \"\", tooltip = \"Triggers and toolbars\")]\n\tpublic static class TT {\n\t\t[Command('k'/*, separator = true*/)]\n\t\tpublic static void Hotkey_triggers() { TriggersAndToolbars.Edit(@\"Triggers\\Hotkey triggers.cs\"); }\n\t\t\n\t\t[Command]\n\t\tpublic static void Autotext_triggers() { TriggersAndToolbars.Edit(@\"Triggers\\Autotext triggers.cs\"); }\n\t\t\n\t\t[Command]\n\t\tpublic static void Mouse_triggers() { TriggersAndToolbars.Edit(@\"Triggers\\Mouse triggers.cs\"); }\n\t\t\n\t\t[Command]\n\t\tpublic static void Window_triggers() { TriggersAndToolbars.Edit(@\"Triggers\\Window triggers.cs\"); }\n\t\t\n\t\t[Command(\"...\", image = EdIcons.Trigger)]\n\t\tpublic static void New_trigger() { TriggersAndToolbars.NewTrigger(); }\n\t\t\n\t\t//rejected. It's in the quick capturing menu.\n\t\t//[Command(\"...\")]\n\t\t//public static void Trigger_scope() { TriggersAndToolbars.TriggerScope(); }\n\t\t\n\t\t//has no sense. It shows triggers for the active window. Also may be multiple trigger scripts running. Instead let use a trigger that calls ActionTriggers.TriggersListWindow.\n\t\t//[Command]\n\t\t//public static void Active_triggers() { TriggersAndToolbars.ShowActiveTriggers(); }\n\t\t\n\t\t[Command]\n\t\tpublic static void Other_triggers() { TriggersAndToolbars.Edit(@\"Triggers\\Other triggers.cs\"); PanelHelp.OpenRecipe(\"Other triggers\"); }\n\t\t\n\t\t[Command(separator = true)]\n\t\tpublic static void Toolbars() { TriggersAndToolbars.GoToToolbars(); }\n\t\t\n\t\t[Command(\"...\", image = \"*Material.ShapeRectanglePlus\" + blue)]\n\t\tpublic static void New_toolbar() { TriggersAndToolbars.NewToolbar(); }\n\t\t\n\t\t[Command(\"...\")]\n\t\tpublic static void Toolbar_trigger() { TriggersAndToolbars.SetToolbarTrigger(); }\n\t\t\n\t\t[Command]\n\t\tpublic static void Active_toolbars() { TriggersAndToolbars.ShowActiveToolbars(); }\n\t\t\n\t\t[Command(separator = true)]\n\t\tpublic static void Disable_triggers() { TriggersAndToolbars.DisableTriggers(null); }\n\t\t\n\t\tstatic void _Init(KMenuCommands mc, KMenuCommands.Command c) {\n\t\t\tif (Au.Triggers.ActionTriggers.DisabledEverywhere) mc[nameof(Disable_triggers)].Checked = true;\n\t\t}\n\t\t\n\t\t[Command]\n\t\tpublic static void Restart_TT_script() { TriggersAndToolbars.Restart(); }\n\t\t\n\t\t[Command(tooltip = \"Finds triggers of current script.\\nFinds its name in '@Triggers and toolbars' files in trigger actions, toolbar button actions and other code.\\nAlso finds scheduled tasks, workspace startup scripts and class file test scripts.\")]\n\t\tpublic static void Find_triggers() { TriggersAndToolbars.AllTriggersMenu(App.Model.CurrentFile); }\n\t\t\n\t\t[Command]\n\t\tpublic static void Triggers_list_info() { PanelHelp.OpenRecipe(\"Show triggers\"); }\n\t\t\n\t\t[Command(separator = true, image = \"*RemixIcon.TerminalLine\" + blue)]\n\t\tpublic static class Script_launchers {\n\t\t\t[Command(\"...\", image = \"*RemixIcon.TerminalLine\" + blue)]\n\t\t\tpublic static void Command_line() { DCommandline.Commandline.ShowForCurrentFile(); }\n\t\t\t\n\t\t\t[Command(\"...\", image = \"*Unicons.Schedule\" + blue)]\n\t\t\tpublic static void Schedule() { DSchedule.ShowFor(App.Model.CurrentFile); }\n\t\t\t\n\t\t\t[Command(\"...\")]\n\t\t\tpublic static void Shortcut() { DCommandline.Shortcut.ShowForCurrentFile(); }\n\t\t\t\n\t\t\t[Command(\"...\")]\n\t\t\tpublic static void Shell_menu() { DCommandline.ShellMenu.ShowForCurrentFile(); }\n\t\t\t\n\t\t\t[Command(\"...\", separator = true)]\n\t\t\tpublic static void Run_at_startup() { DOptions.AaShow(DOptions.EPage.Workspace); }\n\t\t}\n\t}\n\t\n\t[Command(target = \"Edit\")]\n\tpublic static class Run {\n\t\t[Command(image = \"*VaadinIcons.Compile\" + blue)]\n\t\tpublic static void Compile() { CompileRun.CompileAndRun(false, App.Model.CurrentFile); }\n\t\t\n\t\t[Command(\"Run\", image = \"*Codicons.DebugStart\" + green2)]\n\t\tpublic static void Run_script() { CompileRun.CompileAndRun(true, App.Model.CurrentFile, runFromEditor: true); }\n\t\t\n\t\t[Command(image = \"*Material.SquareOutline @14\" + black)]\n\t\tpublic static void End_task() {\n\t\t\tvar f = App.Model.CurrentFile;\n\t\t\tif (f != null) {\n\t\t\t\tf = f.GetProjectMainOrThis();\n\t\t\t\tif (App.Tasks.EndTasksOf(f)) return;\n\t\t\t}\n\t\t\t\n\t\t\tvar a = App.Tasks.Items;\n\t\t\tif (a.Count > 0) {\n\t\t\t\tvar m = new popupMenu { RawText = true };\n\t\t\t\tm.Submenu(\"End task\", m => {\n\t\t\t\t\tforeach (var t in a) m[t.f.DisplayName] = o => App.Tasks.EndTask(t);\n\t\t\t\t});\n\t\t\t\tm.Show(owner: App.Hmain);\n\t\t\t}\n\t\t}\n\t\t\n\t\t[Command(\"...\", image = \"*BoxIcons.RegularHistory\" + black)]\n\t\tpublic static void Recent() { RecentTT.Show(); }\n\t\t\n\t\t[Command(image = \"*Material.Bug\" + green2)]\n\t\tpublic static void Debug_run() { Panels.Debug.Start(); }\n\t\t\n\t\t[Command(\"...\", image = \"*Entypo.Publish\" + blue, separator = true)]\n\t\tpublic static void Publish() { new XPublish().Publish(); }\n\t\t\n\t\t[Command(image = \"*MaterialDesign.PictureInPictureAlt\" + blue, separator = true, tooltip = \"Another session of this user inside a Picture-in-Picture window\")]\n\t\tpublic static void PiP_session() { PipIPC.StartPip(); }\n\t\t\n\t\t[Command(image = \"*Codicons.DebugStart %,,6\" + green2 + \"; *MaterialDesign.Rectangle %10.2,10.8,,.6,f\" + blue)]\n\t\tpublic static void Run_in_PiP() { PipIPC.RunScriptInPip(App.Model.CurrentFile); }\n\t}\n\t\n\t[Command(target = \"\")]\n\tpublic static class Tools {\n\t\t[Command(image = \"*PicolIcons.Settings\" + green)]\n\t\tpublic static void Options() { DOptions.AaShow(); }\n\t\t\n\t\t[Command(image = EdIcons.Icons)]\n\t\tpublic static void Icons() { DIcons.ShowSingle(); }\n\t\t\n\t\t[Command(tooltip = \"Clear icon caches\")]\n\t\tpublic static void Update_icons() { IconImageCache.ClearAll(); }\n\t\t\n\t\t[Command(image = \"*SimpleIcons.NuGet\" + blue)]\n\t\tpublic static void NuGet() { DNuget.ShowSingle(); }\n\t\t\n\t\t[Command(image = \"*Codicons.SymbolSnippet\" + blue)]\n\t\tpublic static void Snippets() { DSnippets.ShowSingle(); }\n\t\t\n\t\t[Command]\n\t\tpublic static void Customize() { DCustomize.ShowSingle(); }\n\t\t\n\t\t[Command]\n\t\tpublic static void Portable() { DPortable.ShowSingle(); }\n\t\t\n\t\t[Command(separator = true, target = \"Output\")]\n\t\tpublic static class Output {\n\t\t\t[Command(\"Clear\", keysText = \"M-click\")]\n\t\t\tpublic static void Output_clear() { Panels.Output.Clear(); }\n\t\t\t\n\t\t\t[Command(\"Copy\", keysText = \"Ctrl+C\")]\n\t\t\tpublic static void Output_copy() { Panels.Output.Copy(); }\n\t\t\t\n\t\t\t[Command(\"History\")]\n\t\t\tpublic static void Output_history() { Panels.Output.History(); }\n\t\t\t\n\t\t\t[Command(\"Wrap lines\", separator = true, checkable = true)]\n\t\t\tpublic static void Output_wrap_lines() { Panels.Output.WrapLines ^= true; }\n\t\t\t\n\t\t\t[Command(\"White space\", checkable = true)]\n\t\t\tpublic static void Output_white_space() { Panels.Output.WhiteSpace ^= true; }\n\t\t}\n\t}\n\t\n\t[Command(target = \"\")]\n\tpublic static class Help {\n\t\t[Command(image = EdIcons.Help)]\n\t\tpublic static void LA_docs() { Panels.Help.MenuCommand(); }\n\t\t\n\t\t[Command(\"C# docs\", image = \"*Modern.LanguageCsharp\" + darkYellow)]\n\t\tpublic static void CSharp_docs() { run.itSafe(\"https://learn.microsoft.com/en-us/dotnet/csharp/\"); }\n\t\t\n\t\t[Command(keys = \"F1\", image = \"*Unicons.MapMarkerQuestion\" + darkYellow)]\n\t\tpublic static void Context_help() {\n\t\t\tvar w = Api.GetFocus();\n\t\t\tif (w.ClassNameIs(\"HwndWrapper*\")) {\n\t\t\t\t//var e = Keyboard.FocusedElement as FrameworkElement;\n\t\t\t\t\n\t\t\t} else if (Panels.Editor.ActiveDoc is SciCode doc && w == doc.AaWnd) {\n\t\t\t\tCiUtil.OpenSymbolEtcFromPosHelp();\n\t\t\t}\n\t\t}\n\t\t\n\t\t[Command(image = \"*Modern.Home\" + darkYellow)]\n\t\tpublic static void Website() { run.itSafe(\"https://www.libreautomate.com/\"); }\n\t\t\n\t\t[Command(image = \"*Material.Forum\" + darkYellow)]\n\t\tpublic static void Forum() { run.itSafe(\"https://www.libreautomate.com/forum/\"); }\n\t\t\n\t\t[Command(\"What's new\")]\n\t\tpublic static void Whats_new() { run.itSafe($\"https://github.com/qgindi/LibreAutomate/blob/master/Other/DocFX/_doc/changes/v{new Version(Au_.Version).ToString(2)}.md\"); }\n\t\t\n\t\t[Command]\n\t\tpublic static void Email() { run.itSafe($\"mailto:info@libreautomate.com?subject={App.AppName} {Au_.Version}\"); }\n\t\t\n\t\t[Command]\n\t\tpublic static void Donate() { run.itSafe(\"https://github.com/sponsors/qgindi\"); }\n\t\t\n\t\t[Command]\n\t\tpublic static void About() {\n\t\t\tprint.it($@\"<>---- {App.AppName} ----\nVersion: {Au_.Version}\nDownload: <link>https://www.libreautomate.com/<>\nSource code: <link>https://github.com/qgindi/LibreAutomate<>\nUses C# 14, <link https://dotnet.microsoft.com/download>.NET {Environment.Version}<>, <link https://github.com/dotnet/roslyn>Roslyn<>, <link https://www.scintilla.org/>Scintilla 5.5.7<>, <link https://www.pcre.org/>PCRE 10.46<>, <link https://www.sqlite.org/index.html>SQLite<>, <link https://github.com/MahApps/MahApps.Metro.IconPacks>MahApps.Metro.IconPacks 5.1<>, <link https://github.com/dotnet/docfx>DocFX<>, <link https://github.com/Samsung/netcoredbg>Samsung/netcoredbg<>, <link https://github.com/microsoft/win32metadata>win32metadata<>, <link https://github.com/google/diff-match-patch>DiffMatchPatch<>, <link https://github.com/DmitryGaravsky/ILReader>ILReader<>, <link https://github.com/nemec/porter2-stemmer>Porter2Stemmer<>, <link https://github.com/xoofx/markdig>Markdig<>.\nFolders: <link {folders.Workspace}>Workspace<>, <link {folders.ThisApp}>ThisApp<>, <link {folders.ThisAppDocuments}>ThisAppDocuments<>, <link {folders.ThisAppDataLocal}>ThisAppDataLocal<>, <link {folders.ThisAppDataRoaming}>ThisAppDataRoaming<>, <link {folders.ThisAppTemp}>ThisAppTemp<>.\n{typeof(App).Assembly.GetCustomAttribute<AssemblyCopyrightAttribute>().Copyright}.\n--------------------------\");\n\t\t}\n\t}\n\t\n#if DEBUG || IDE_LA\n\t//[Command(target = \"\", keys = \"F11\")] //no, dangerous, eg can accidentally press instead of F12\n\t[Command]\n\tpublic static void TEST() { Test.FromMenubar(); }\n\t\n\t[Command]\n\tpublic static void gc() {\n\t\tGC.Collect();\n\t\tGC.WaitForPendingFinalizers(); //GC.Collect does not free much memory without this\n\t}\n#endif\n\t\n\t//Used by ScriptEditor.InvokeCommand/GetCommandState.\n\t//flags: 1 check, 2 uncheck, 4 activate window, 8 dontWait, 16 editorExtension.\n\tinternal static int Invoke(string command, bool getState, int flags = 0) {\n\t\tvar w = App.Hmain;\n\t\tbool loading = w.Is0;\n\t\tif (loading) {\n\t\t\t//this func can't work if the main window still not created\n\t\t\tApp.ShowWindow();\n\t\t\t//let the script wait and retry. Some commands don't work until inited codeinfo etc. Until then w is disabled.\n\t\t} else {\n\t\t\t//few commands work or have sense when the window is invisible. Show it even when getState.\n\t\t\tif (0 != (flags & 4)) App.ShowWindow(); //show and activate\n\t\t\telse { //just show\n\t\t\t\tWndUtil.EnableActivate(); //enable activating if need, eg tool windows\n\t\t\t\tif (!App.Wmain.IsVisible) App.Wmain.Show();\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (App.Commands.TryFind(command, out var c)) {\n\t\t\tif (loading || !w.IsEnabled()) {\n\t\t\t\tif (0 != (flags & 16)) return getState ? (int)ECommandState.Disabled : 0; //editorExtension, can't wait\n\t\t\t\treturn -1; //let the script wait while disabled, even if getState\n\t\t\t}\n\t\t\t\n\t\t\tif (getState) {\n\t\t\t\tECommandState r = c.CanExecute(null) ? 0 : ECommandState.Disabled;\n\t\t\t\tif (c.Checked) r |= ECommandState.Checked;\n\t\t\t\treturn (int)r;\n\t\t\t} else if (c.CanExecute(null)) {\n\t\t\t\tif (0 != (flags & 8)) { //dontWait\n\t\t\t\t\tif (0 != (flags & 16)) { //editorExtension\n\t\t\t\t\t\tApp.Dispatcher.InvokeAsync(_Invoke);\n\t\t\t\t\t\treturn 1;\n\t\t\t\t\t}\n\t\t\t\t\tApi.ReplyMessage(1);\n\t\t\t\t}\n\t\t\t\t_Invoke();\n\t\t\t\treturn 1;\n\t\t\t\t\n\t\t\t\tvoid _Invoke() {\n\t\t\t\t\tint check = flags & 3;\n\t\t\t\t\tif (check is 1 or 2) c.Checked = check == 1; else c.Execute(null);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tvar names = new StringBuilder();\n\t\t\tforeach (var v in Panels.Menu.Items) _Menu(v, 0);\n\t\t\tvoid _Menu(object o, int level) {\n\t\t\t\tif (o is MenuItem mi && mi.Command is KMenuCommands.Command c) {\n\t\t\t\t\tnames.Append('\\t', level).AppendLine(c.Name);\n\t\t\t\t\tforeach (var v in mi.Items) _Menu(v, level + 1);\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tprint.it(command.NE() ? \"Commands:\\r\\n\" + names : $\"Unknown command '{command}'. Commands:\\r\\n{names}\");\n\t\t}\n\t\treturn 0;\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Au.Editor.cs",
    "content": "/*/\nrole exeProgram\ndefine IDE_LA,NO_GLOBAL,NO_DEFAULT_CHARSET_UNICODE\nnoWarnings 8002,419\ntestInternal Microsoft.CodeAnalysis,Microsoft.CodeAnalysis.CSharp,Microsoft.CodeAnalysis.Features,Microsoft.CodeAnalysis.CSharp.Features,Microsoft.CodeAnalysis.Workspaces,Microsoft.CodeAnalysis.CSharp.Workspaces\npreBuild .\\_prePostBuild.cs\npostBuild .\\_prePostBuild.cs /post $(outputPath)\noutputPath %folders.Workspace%\\..\\Au.Editor\nicon resources\\ico\nmanifest resources\\Au.manifest\nsign resources\\Au.snk\nmiscFlags 1\nnoRef *\\Au.dll\npr ..\\@Au\\Au.cs\npr ..\\@Au.Controls\\Au.Controls.cs\nr Roslyn\\Microsoft.CodeAnalysis.dll /noCopy\nr Roslyn\\Microsoft.CodeAnalysis.CSharp.dll /noCopy\nr Roslyn\\Microsoft.CodeAnalysis.Features.dll /noCopy\nr Roslyn\\Microsoft.CodeAnalysis.CSharp.Features.dll /noCopy\nr Roslyn\\Microsoft.CodeAnalysis.Workspaces.dll /alias=CAW|noCopy\nr Roslyn\\Microsoft.CodeAnalysis.CSharp.Workspaces.dll /noCopy\nr AxMSTSCLib.dll\nr MSTSCLib.dll\nr NuGet.Configuration.dll\nr NuGet.Versioning.dll\nnuget webview\\Microsoft.Web.WebView2\nresource app\\app-resources.xaml /path\nresource resources\\ci /path\nfile .\\Default\n/*/\n"
  },
  {
    "path": "Au.Editor/Au.Editor.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n\t<PropertyGroup>\n\t\t<OutputType>WinExe</OutputType>\n\t\t<TargetFramework>net10.0-windows</TargetFramework>\n\t\t<UseWPF>true</UseWPF>\n\t\t<UseWindowsForms>true</UseWindowsForms>\n\t\t<GenerateAssemblyInfo>false</GenerateAssemblyInfo>\n\t\t<AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n\t\t<SignAssembly>true</SignAssembly>\n\t\t<AssemblyOriginatorKeyFile>..\\Au.snk</AssemblyOriginatorKeyFile>\n\t\t<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>\n\t\t<OutDir>$(SolutionDir)_</OutDir>\n\t\t<LangVersion>preview</LangVersion>\n\t\t<NoDefaultLaunchSettingsFile>true</NoDefaultLaunchSettingsFile>\n\t\t<ProduceReferenceAssembly>False</ProduceReferenceAssembly>\n\t\t<GenerateDocumentationFile>True</GenerateDocumentationFile>\n\t\t<NoWarn>1701;1702;8002;419;8981;WFO1000</NoWarn>\n\t\t<SupportedOSPlatformVersion>7.0</SupportedOSPlatformVersion>\n\t\t<NoWin32Manifest>true</NoWin32Manifest>\n\t</PropertyGroup>\n\n\t<PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|AnyCPU'\">\n\t  <DebugType>embedded</DebugType>\n\t</PropertyGroup>\n\n\t<PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|AnyCPU'\">\n\t  <DebugType>embedded</DebugType>\n\t</PropertyGroup>\n\n\t<ItemGroup>\n\t\t<Compile Include=\"..\\Au\\Resources\\global2.cs\" Link=\"resources\\global2.cs\" />\n\t</ItemGroup>\n\n\t<ItemGroup>\n\t\t<Compile Remove=\"_prePostBuild.cs\" />\n\t\t<Compile Remove=\"Au.Editor.cs\" />\n\t</ItemGroup>\n\n\t<ItemGroup>\n\t\t<None Remove=\"Default\\Commands.xml\" />\n\t\t<None Remove=\"Default\\Layout.xml\" />\n\t\t<None Remove=\"Default\\Snippets.xml\" />\n\t\t<None Remove=\"Default\\Themes\\*.csv\" />\n\t</ItemGroup>\n\n\t<ItemGroup>\n\t\t<Content Include=\"Default\\Commands.xml\">\n\t\t\t<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n\t\t</Content>\n\t\t<Content Include=\"Default\\Layout.xml\">\n\t\t\t<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n\t\t\t<SubType>Designer</SubType>\n\t\t</Content>\n\t\t<Content Include=\"Default\\Snippets.xml\">\n\t\t\t<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n\t\t</Content>\n\t\t<Content Include=\"Default\\Themes\\*.csv\">\n\t\t  <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n\t\t</Content>\n\t</ItemGroup>\n\n\t<ItemGroup>\n\t\t<Page Remove=\"resources\\ci\\Class.xaml\" />\n\t\t<Page Remove=\"resources\\ci\\Constant.xaml\" />\n\t\t<Page Remove=\"resources\\ci\\Delegate.xaml\" />\n\t\t<Page Remove=\"resources\\ci\\Enum.xaml\" />\n\t\t<Page Remove=\"resources\\ci\\EnumMember.xaml\" />\n\t\t<Page Remove=\"resources\\ci\\Event.xaml\" />\n\t\t<Page Remove=\"resources\\ci\\ExpandScope.xaml\" />\n\t\t<Page Remove=\"resources\\ci\\ExtensionMethod.xaml\" />\n\t\t<Page Remove=\"resources\\ci\\Field.xaml\" />\n\t\t<Page Remove=\"resources\\ci\\GroupBy.xaml\" />\n\t\t<Page Remove=\"resources\\ci\\Interface.xaml\" />\n\t\t<Page Remove=\"resources\\ci\\Keyword.xaml\" />\n\t\t<Page Remove=\"resources\\ci\\Label.xaml\" />\n\t\t<Page Remove=\"resources\\ci\\LocalMethod.xaml\" />\n\t\t<Page Remove=\"resources\\ci\\LocalVariable.xaml\" />\n\t\t<Page Remove=\"resources\\ci\\Method.xaml\" />\n\t\t<Page Remove=\"resources\\ci\\Namespace.xaml\" />\n\t\t<Page Remove=\"resources\\ci\\Operator.xaml\" />\n\t\t<Page Remove=\"resources\\ci\\OverlayAbstract.xaml\" />\n\t\t<Page Remove=\"resources\\ci\\OverlayInternal.xaml\" />\n\t\t<Page Remove=\"resources\\ci\\OverlayPrivate.xaml\" />\n\t\t<Page Remove=\"resources\\ci\\OverlayProtected.xaml\" />\n\t\t<Page Remove=\"resources\\ci\\OverlayStatic.xaml\" />\n\t\t<Page Remove=\"resources\\ci\\Property.xaml\" />\n\t\t<Page Remove=\"resources\\ci\\Region.xaml\" />\n\t\t<Page Remove=\"resources\\ci\\Snippet.xaml\" />\n\t\t<Page Remove=\"resources\\ci\\Structure.xaml\" />\n\t\t<Page Remove=\"resources\\ci\\TypeParameter.xaml\" />\n\t</ItemGroup>\n\n\t<ItemGroup>\n\t\t<Resource Include=\"resources\\ci\\Class.xaml\" />\n\t\t<Resource Include=\"resources\\ci\\Constant.xaml\" />\n\t\t<Resource Include=\"resources\\ci\\Delegate.xaml\" />\n\t\t<Resource Include=\"resources\\ci\\Enum.xaml\" />\n\t\t<Resource Include=\"resources\\ci\\EnumMember.xaml\" />\n\t\t<Resource Include=\"resources\\ci\\Event.xaml\" />\n\t\t<Resource Include=\"resources\\ci\\ExpandScope.xaml\" />\n\t\t<Resource Include=\"resources\\ci\\ExtensionMethod.xaml\" />\n\t\t<Resource Include=\"resources\\ci\\Field.xaml\" />\n\t\t<Resource Include=\"resources\\ci\\GroupBy.xaml\" />\n\t\t<Resource Include=\"resources\\ci\\Interface.xaml\" />\n\t\t<Resource Include=\"resources\\ci\\Keyword.xaml\" />\n\t\t<Resource Include=\"resources\\ci\\Label.xaml\" />\n\t\t<Resource Include=\"resources\\ci\\LocalMethod.xaml\" />\n\t\t<Resource Include=\"resources\\ci\\LocalVariable.xaml\" />\n\t\t<Resource Include=\"resources\\ci\\Method.xaml\" />\n\t\t<Resource Include=\"resources\\ci\\Namespace.xaml\" />\n\t\t<Resource Include=\"resources\\ci\\Operator.xaml\" />\n\t\t<Resource Include=\"resources\\ci\\OverlayAbstract.xaml\" />\n\t\t<Resource Include=\"resources\\ci\\OverlayInternal.xaml\" />\n\t\t<Resource Include=\"resources\\ci\\OverlayPrivate.xaml\" />\n\t\t<Resource Include=\"resources\\ci\\OverlayProtected.xaml\" />\n\t\t<Resource Include=\"resources\\ci\\OverlayStatic.xaml\" />\n\t\t<Resource Include=\"resources\\ci\\Property.xaml\" />\n\t\t<Resource Include=\"resources\\ci\\Region.xaml\" />\n\t\t<Resource Include=\"resources\\ci\\Snippet.xaml\" />\n\t\t<Resource Include=\"resources\\ci\\Structure.xaml\" />\n\t\t<Resource Include=\"resources\\ci\\TypeParameter.xaml\" />\n\t</ItemGroup>\n\n\t<ItemGroup>\n\t\t<ProjectReference Include=\"..\\Au.Controls\\Au.Controls.csproj\" />\n\t\t<ProjectReference Include=\"..\\Au\\Au.csproj\" />\n\t</ItemGroup>\n\n\t<ItemGroup>\n\t\t<Reference Include=\"AxMSTSCLib\">\n\t\t  <HintPath>..\\_\\AxMSTSCLib.dll</HintPath>\n\t\t</Reference>\n\t\t<Reference Include=\"Microsoft.CodeAnalysis\">\n\t\t\t<HintPath>..\\_\\Roslyn\\Microsoft.CodeAnalysis.dll</HintPath>\n\t\t\t<Private>False</Private>\n\t\t</Reference>\n\t\t<Reference Include=\"Microsoft.CodeAnalysis.CSharp\">\n\t\t\t<HintPath>..\\_\\Roslyn\\Microsoft.CodeAnalysis.CSharp.dll</HintPath>\n\t\t\t<Private>False</Private>\n\t\t</Reference>\n\t\t<Reference Include=\"Microsoft.CodeAnalysis.CSharp.Features\">\n\t\t\t<HintPath>..\\_\\Roslyn\\Microsoft.CodeAnalysis.CSharp.Features.dll</HintPath>\n\t\t\t<Private>False</Private>\n\t\t</Reference>\n\t\t<Reference Include=\"Microsoft.CodeAnalysis.CSharp.Workspaces\">\n\t\t\t<HintPath>..\\_\\Roslyn\\Microsoft.CodeAnalysis.CSharp.Workspaces.dll</HintPath>\n\t\t\t<Private>False</Private>\n\t\t</Reference>\n\t\t<Reference Include=\"Microsoft.CodeAnalysis.Features\">\n\t\t\t<HintPath>..\\_\\Roslyn\\Microsoft.CodeAnalysis.Features.dll</HintPath>\n\t\t\t<Private>False</Private>\n\t\t</Reference>\n\t\t<Reference Include=\"Microsoft.CodeAnalysis.Workspaces\">\n\t\t\t<HintPath>..\\_\\Roslyn\\Microsoft.CodeAnalysis.Workspaces.dll</HintPath>\n\t\t\t<Private>False</Private>\n\t\t\t<Aliases>CAW</Aliases>\n\t\t</Reference>\n\t\t<Reference Include=\"MSTSCLib\">\n\t\t  <HintPath>..\\_\\MSTSCLib.dll</HintPath>\n\t\t</Reference>\n\t</ItemGroup>\n\n\t<ItemGroup>\n\t  <PackageReference Include=\"Microsoft.Web.WebView2\" Version=\"1.0.3595.46\" />\n\t  <PackageReference Include=\"NuGet.Configuration\" Version=\"6.14.0\" />\n\t  <PackageReference Include=\"NuGet.Versioning\" Version=\"6.14.0\" />\n\t</ItemGroup>\n\n\t<Target Name=\"PreBuild\" BeforeTargets=\"PreBuildEvent\">\n\t\t<Exec Command=\"$(SolutionDir)Other\\BuildEvents\\bin\\Debug\\BuildEvents.exe preBuild $(Configuration)\" />\n\t</Target>\n\n\t<Target Name=\"PostBuild\" AfterTargets=\"PostBuildEvent\">\n\t\t<Exec Command=\"$(SolutionDir)Other\\BuildEvents\\bin\\Debug\\BuildEvents.exe postBuild $(Configuration)\" />\n\t</Target>\n\n</Project>\n"
  },
  {
    "path": "Au.Editor/Compiler/Compiler.cs",
    "content": "extern alias CAW;\n\nusing System.Reflection.Metadata;\nusing System.Reflection.PortableExecutable;\nusing System.Xml.Linq;\n\nusing Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp;\nusing Microsoft.CodeAnalysis.CSharp.Syntax;\nusing Microsoft.CodeAnalysis.Emit;\n\nnamespace LA;\n\n/// <summary>\n/// Compiles C# files.\n/// </summary>\npartial class Compiler {\n\t/// <summary>\n\t/// Compiles C# file or project if need.\n\t/// Returns false if fails (C# errors etc).\n\t/// </summary>\n\t/// <param name=\"reason\">Whether to recompile if compiled, etc. See also Remarks.</param>\n\t/// <param name=\"r\">Results.</param>\n\t/// <param name=\"f\">C# file. If projFolder used, must be the main file of the project.</param>\n\t/// <param name=\"projFolder\">null or project folder.</param>\n\t/// <param name=\"needMeta\">Parse metacomments and set r.meta even if don't need to compile.</param>\n\t/// <param name=\"canCompile\">Called after parsing meta, creating trees and compilation, but before emit. Return false to cancel.</param>\n\t/// <param name=\"addMetaFlags\">Add these flags to the usual flags of MetaComments. Used by MetaComments._PR.</param>\n\t/// <remarks>\n\t/// Must be always called in the main UI thread.\n\t/// \n\t/// Adds <see cref=\"MetaReferences.DefaultReferences\"/>.\n\t/// \n\t/// If f role is classFile:\n\t/// \tIf CompReason.Run, does not compile (just parses meta), sets r.role=classFile and returns false.\n\t/// \tElse compiles but does not create output files.\n\t/// </remarks>\n\tpublic static bool Compile(CCReason reason, out CompResults r, FileNode f, FileNode projFolder = null, bool needMeta = false, Func<CanCompileArgs, bool> canCompile = null, MCFlags addMetaFlags = 0) {\n\t\tDebug.Assert(App.IsMainThread);\n\t\tr = null;\n\t\tvar cache = XCompiled.OfWorkspace;\n\t\tif (reason is not (CCReason.CompileAlways or CCReason.WpfPreview) && !addMetaFlags.Has(MCFlags.Publish) && cache.IsCompiled(f, out r, projFolder)) {\n\t\t\t//print.it(\"cached\");\n\t\t\tif (needMeta) {\n\t\t\t\tvar m = new MetaComments(MCFlags.PrintErrors | MCFlags.OnlyRef);\n\t\t\t\tif (!m.Parse(f, projFolder)) return false;\n\t\t\t\tr.meta = m;\n\t\t\t\t//CONSIDER: save used dll etc paths in xcompiled, to avoid parsing meta of all pr.\n\t\t\t}\n\t\t\treturn true;\n\t\t} else {\n\t\t\t//print.it(\"COMPILE\");\n\t\t\tAction aFinally = null;\n\t\t\ttry {\n\t\t\t\tvar c = new Compiler();\n\t\t\t\tif (c._Compile(reason, f, out r, projFolder, out aFinally, canCompile, addMetaFlags)) return true;\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tif (reason != CCReason.WpfPreview) print.it($\"Failed to compile '{f.Name}'. {ex}\");\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\taFinally?.Invoke();\n\t\t\t}\n\t\t\t\n\t\t\tcache.Remove(f, false);\n\t\t\treturn false;\n\t\t}\n\t}\n\t\n\t/// <summary>_Compile() output assembly info.</summary>\n\tpublic record CompResults {\n\t\t/// <summary>C# file name without \".cs\".</summary>\n\t\tpublic string name;\n\t\t\n\t\t/// <summary>Full path of assembly file.</summary>\n\t\tpublic string file;\n\t\t\n\t\tpublic MCRole role;\n\t\tpublic MCIfRunning ifRunning;\n\t\tpublic MCUac uac;\n\t\tpublic MiniProgram_.MPFlags flags;\n\t\tpublic MCPlatform platform;\n\t\t\n\t\t/// <summary>The assembly is normal .exe or .dll file, not in cache. If exe, its dependencies were copied to its directory.</summary>\n\t\tpublic bool notInCache;\n\t\t\n\t\t/// <summary>May be null if not explicitly requested.</summary>\n\t\tpublic MetaComments meta;\n\t}\n\t\n\tMetaComments _meta;\n\tCSharpCompilation _compilation;\n\tDictionary<string, string> _dr, _dn;\n\tstring _tpa;\n\t\n\tbool _Compile(CCReason reason, FileNode f, out CompResults r, FileNode projFolder, out Action aFinally, Func<CanCompileArgs, bool> canCompile, MCFlags addMetaFlags) {\n\t\t//print.it(\"COMPILE\");\n\t\t\n\t\t//var p1 = perf.local();\n\t\tr = new CompResults();\n\t\taFinally = null;\n\t\t\n\t\t_meta = new((reason == CCReason.WpfPreview ? MCFlags.WpfPreview : MCFlags.PrintErrors) | addMetaFlags);\n\t\tif (!_meta.Parse(f, projFolder)) return false;\n\t\tvar err = _meta.Errors;\n\t\tr.meta = _meta;\n\t\t//p1.Next('m');\n\t\t\n\t\tbool needOutputFiles = _meta.Role != MCRole.classFile;\n\t\t\n\t\t//if for run, don't compile if f role is classFile\n\t\tif (reason == CCReason.Run && !needOutputFiles) {\n\t\t\tr.role = MCRole.classFile;\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\tXCompiled cache = XCompiled.OfWorkspace;\n\t\tstring outPath = null, outFile = null, fileName = null;\n\t\tbool notInCache = false;\n\t\tif (needOutputFiles) {\n\t\t\tif (notInCache = _meta.OutputPath != null) {\n\t\t\t\toutPath = _meta.OutputPath;\n\t\t\t\tfileName = _meta.Name + \".dll\";\n\t\t\t} else {\n\t\t\t\toutPath = cache.CacheDirectory;\n\t\t\t\tif (reason == CCReason.WpfPreview) fileName = \"WPFpreview.dll\";\n\t\t\t\telse fileName = f.IdString + \".dll\";\n\t\t\t\t//note: must have \".dll\", else somehow don't work GetModuleHandle, Process.GetCurrentProcess().Modules etc\n\t\t\t}\n\t\t\toutFile = outPath + \"\\\\\" + fileName;\n\t\t\tfilesystem.createDirectory(outPath);\n\t\t}\n\t\t\n\t\tif (_meta.PreBuild.f != null && !_RunPrePostBuildScript(false, outFile)) return false;\n\t\t\n\t\tvar trees = CompilerUtil.CreateSyntaxTrees(_meta);\n\t\t//p1.Next('t');\n\t\t\n\t\tstring asmName = _meta.Name;\n\t\tif (_meta.Role == MCRole.editorExtension) { //cannot load multiple assemblies with same name\n\t\t\tasmName = asmName + \"|\" + Guid.NewGuid().ToString();\n\t\t\t//use GUID, not counter, because may be loaded old assembly from cache with same counter value\n\t\t} else if (_meta.Role == MCRole.miniProgram) {\n\t\t\t//workaround for: coreclr_execute_assembly and even AssemblyLoadContext.Default.LoadFromAssemblyPath fail\n\t\t\t//\tif asmName is the same as of a .NET etc assembly.\n\t\t\t//\tIt seems it at first ignores path and tries to find assembly by name.\n\t\t\t//\tBut be careful. It could break something. Eg with WPF resources use \"pack:...~AssemblyName...\".\n\t\t\tasmName = \"~\" + asmName;\n\t\t}\n\t\t\n\t\tif (_meta.TestInternal is string[] testInternal) {\n\t\t\tTestInternal.CompilerStart(asmName, testInternal);\n\t\t\taFinally += TestInternal.CompilerEnd; //this func is called from try/catch/finally which calls aFinally\n\t\t}\n\t\t\n\t\tList<ResourceDescription> resMan = null;\n\t\tif (needOutputFiles) { //before creating compilation. May modify trees[] elements.\n\t\t\tresMan = _CreateManagedResources(asmName, trees);\n\t\t\tif (err.ErrorCount != 0) { err.PrintAll(); return false; }\n\t\t\t//p1.Next('y');\n\t\t}\n\t\t\n\t\tvar cOpt = _meta.CreateCompilationOptions();\n\t\t_compilation = CSharpCompilation.Create(asmName, trees, _meta.References.Refs, cOpt);\n\t\t//p1.Next('c');\n\t\t\n\t\tif (canCompile != null && !canCompile(new(_meta, trees, _compilation))) return false;\n\t\t\n\t\tstring xdFile = null;\n\t\tStream xdStream = null;\n\t\tStream resNat = null;\n\t\tEmitOptions eOpt = null;\n\t\t\n\t\tif (needOutputFiles) {\n\t\t\tr.flags |= _AddAttributesEtc();\n\t\t\t//p1.Next('a');\n\t\t\t\n\t\t\t//rejected: if empty script, add {} to avoid error \"no Main\". See AddErrorOrWarning.\n\t\t\t\n\t\t\t//Create debug info always. It is used for run-time error links.\n\t\t\t//Embed it in assembly. It adds < 1 KB. Almost the same compiling speed. Same loading speed.\n\t\t\t//Don't use classic pdb file. It is 14 KB, 2 times slower compiling, slower loading; error with .NET Core: Unexpected error writing debug information -- 'The version of Windows PDB writer is older than required: 'diasymreader.dll''.\n\t\t\t//#if DEBUG //temp test debugger with non-user-code dll\n\t\t\t//\t\t\tif (!(_meta.Role == MCRole.classLibrary && (_meta.Optimize || _meta.Name == \"Au\")))\n\t\t\t//#endif\n\t\t\teOpt = new EmitOptions(debugInformationFormat: DebugInformationFormat.Embedded);\n\t\t\t\n\t\t\tif (_meta.XmlDoc) //allowed if role is classLibrary or exeProgram, but in Properties hidden if exeProgram (why could need it?)\n\t\t\t\txdStream = filesystem.waitIfLocked(() => File.Create(xdFile = outPath + \"\\\\\" + _meta.Name + \".xml\"));\n\t\t\t\n\t\t\tresNat = _CreateNativeResources();\n\t\t\tif (err.ErrorCount != 0) { err.PrintAll(); return false; }\n\t\t\t\n\t\t\t//EmbeddedText.FromX //it seems we can embed source code in PDB. Not tested.\n\t\t}\n\t\t\n\t\t//p1.Next();\n\t\tvar asmStream = new MemoryStream(20_000);\n\t\tvar emitResult = _compilation.Emit(asmStream, null, xdStream, resNat, resMan, eOpt);\n\t\t\n\t\tif (needOutputFiles) {\n\t\t\txdStream?.Dispose();\n\t\t\tresNat?.Dispose(); //info: compiler disposes resMan\n\t\t}\n\t\t//p1.Next('e');\n\t\t\n\t\tif (reason != CCReason.WpfPreview) {\n\t\t\tvar diag = emitResult.Diagnostics;\n\t\t\tif (!diag.IsDefaultOrEmpty) {\n\t\t\t\tforeach (var d in diag) {\n\t\t\t\t\tif (d.Severity == DiagnosticSeverity.Hidden) continue;\n\t\t\t\t\terr.AddErrorOrWarning(d, f);\n\t\t\t\t\tif (d.Severity == DiagnosticSeverity.Error && d.Id == \"CS0009\") MetaReferences.RemoveBadRefFromCache(d.GetMessage());\n\t\t\t\t}\n\t\t\t\terr.PrintAll();\n\t\t\t}\n\t\t}\n\t\tif (!emitResult.Success) {\n\t\t\tif (needOutputFiles) {\n\t\t\t\tApi.DeleteFile(outFile);\n\t\t\t\tif (xdFile != null) Api.DeleteFile(xdFile);\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\tif (needOutputFiles) {\n\t\t\tif (_meta.Role == MCRole.miniProgram) {\n\t\t\t\t//is Main with [MTAThread]? Default STA, even if Main without [STAThread].\n\t\t\t\t//FUTURE: C# ?? [assembly: MTAThread]\n\t\t\t\tif (_compilation.GetEntryPoint(default)?.GetAttributes().Any(o => o.ToString() == \"System.MTAThreadAttribute\") ?? false) r.flags |= MiniProgram_.MPFlags.MTA;\n\t\t\t\t\n\t\t\t\tif (_meta.Console) r.flags |= MiniProgram_.MPFlags.Console;\n\t\t\t}\n\t\t\t\n\t\t\t//create assembly file\n\t\t\t//p1.Next();\n\t\t\tgSave:\n\t\t\tvar hf = Api.CreateFile(outFile, Api.GENERIC_WRITE, 0, Api.CREATE_ALWAYS);\n\t\t\tif (hf.Is0) {\n\t\t\t\tvar ec = lastError.code;\n\t\t\t\tif (ec == Api.ERROR_SHARING_VIOLATION && _RenameLockedFile(outFile, notInCache: notInCache)) goto gSave;\n\t\t\t\tthrow new AuException(ec, outFile);\n\t\t\t}\n\t\t\tvar b = asmStream.GetBuffer();\n\t\t\t\n\t\t\tusing (hf) if (!Api.WriteFile2(hf, b.AsSpan(0, (int)asmStream.Length), out _)) throw new AuException(0);\n\t\t\t//saving would be fast, but with AV can take half of time.\n\t\t\t//\tWith WD now fast, but used to be slow. Now on save WD scans async, and on load scans only if still not scanned, eg if loading soon after saving.\n\t\t\t//\tWith Avast now the same as with WD.\n\t\t\t//p1.Next('s');\n\t\t\tr.file = outFile;\n\t\t\t\n\t\t\tif (_meta.Role == MCRole.exeProgram) {\n\t\t\t\t_GetDllPaths();\n\t\t\t\t\n\t\t\t\tvar need = _NeedNativeExeAndDllFilesOfPlatforms();\n\t\t\t\t\n\t\t\t\t//copy app host template exe, add native resources, set assembly name, set console flag if need\n\t\t\t\tif (need.x64) _AppHost(outFile, fileName, MCPlatform.x64);\n\t\t\t\tif (need.arm64) _AppHost(outFile, fileName, MCPlatform.arm64);\n\t\t\t\tif (need.x86) _AppHost(outFile, fileName, MCPlatform.x86);\n\t\t\t\t//p1.Next('h'); //very slow with AV. Eg with WD this part makes whole compilation several times slower.\n\t\t\t\t\n\t\t\t\t//copy dlls to the output directory\n\t\t\t\t_CopyDlls(asmStream, need64: need.x64, needArm: need.arm64, need32: need.x86);\n\t\t\t\t//p1.Next('d');\n\t\t\t}\n\t\t\t\n\t\t\tif (!_meta.Console && _meta.Role is MCRole.miniProgram or MCRole.exeProgram && !addMetaFlags.Has(MCFlags.Publish)) {\n\t\t\t\t//if using assembly System.Console, let it redirect Console.Write etc to print.it, and Console.ReadLine to dialog.showInput.\n\t\t\t\t//\tDon't redirect always, it's slow.\n\t\t\t\t//Speed of this code: 50 mcs.\n\t\t\t\tasmStream.Position = 0;\n\t\t\t\tusing var pr = new PEReader(asmStream, PEStreamOptions.LeaveOpen);\n\t\t\t\tvar mr = pr.GetMetadataReader();\n\t\t\t\tforeach (var handle in mr.AssemblyReferences) {\n\t\t\t\t\tvar name = mr.GetString(mr.GetAssemblyReference(handle).Name);\n\t\t\t\t\tif (name == \"System.Console\") { r.flags |= MiniProgram_.MPFlags.RedirectConsole; break; }\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (_meta.PostBuild.f != null && !_RunPrePostBuildScript(true, outFile)) return false;\n\t\t\n\t\tif (_meta.StartFaster) r.flags |= MiniProgram_.MPFlags.Preloaded;\n\t\t\n\t\tif (reason != CCReason.WpfPreview && !addMetaFlags.Has(MCFlags.Publish)) {\n\t\t\tif (needOutputFiles) {\n\t\t\t\tcache.AddCompiled(f, outFile, _meta, r.flags);\n\t\t\t\t\n\t\t\t\tif (_meta.Role == MCRole.classLibrary) {\n\t\t\t\t\tMetaReferences.UncacheOldFiles();\n\t\t\t\t\tif (MetaReferences.IsDefaultRef(_meta.Name)\n\t\t\t\t\t\t&& _meta.References.Refs.Any(o => _meta.Name.Eqi(o.Display.Contains('\\\\') ? pathname.getNameNoExt(o.Display) : o.Display))) //may be removed with meta noRef\n\t\t\t\t\t\tprint.warning($\"Library name '{_meta.Name}' should not be used. Rename the C# file.\", -1);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (notInCache) print.it($\"<>Compiled {f.SciLink()} -> <explore>{outFile}<>\");\n\t\t\t}\n\t\t\t\n\t\t\tif (App.Settings.comp_printCompiled != false && !notInCache)\n\t\t\t\tif (App.Settings.comp_printCompiled == true || reason == CCReason.CompileAlways)\n\t\t\t\t\tprint.it($\"<>Compiled {f.SciLink()}\");\n\t\t}\n\t\t\n\t\tr.name = _meta.Name;\n\t\tr.role = _meta.Role;\n\t\tr.ifRunning = _meta.IfRunning;\n\t\tr.uac = _meta.Uac;\n\t\tr.platform = _meta.Platform;\n\t\tr.notInCache = notInCache;\n\t\t\n\t\t//#if DEBUG\n\t\t//p1.NW('C');\n\t\t//#endif\n\t\t//print.it($\"<><c red>compiled<> {f}\");\n\t\treturn true;\n\t\t\n\t\t//TODO3: rebuild if missing apphost. Now rebuilds only if missing dll.\n\t}\n\t\n\t/// <summary>\n\t/// Adds some module/assembly attributes. Also adds module initializer for role exeProgram.\n\t/// </summary>\n\tMiniProgram_.MPFlags _AddAttributesEtc() {\n\t\tMiniProgram_.MPFlags rflags = 0;\n\t\t//bool needDefaultCharset = true;\n\t\t//foreach (var v in _compilation.SourceModule.GetAttributes()) {\n\t\t//\t//print.it(v.AttributeClass.Name);\n\t\t//\tif (v.AttributeClass.Name == \"DefaultCharSetAttribute\") { needDefaultCharset = false; break; }\n\t\t//}\n\t\tbool needTargetFramework = false, needAssemblyTitle = false;\n\t\tif (_meta.Role is MCRole.exeProgram or MCRole.classLibrary or MCRole.miniProgram) {\n\t\t\tneedTargetFramework = true; //for AppContext.TargetFrameworkName, else it will return null\n\t\t\tneedAssemblyTitle = _meta.Role is MCRole.exeProgram or MCRole.classLibrary; //displayed in various system UI as program description (else empty)\n\t\t\tforeach (var v in _compilation.Assembly.GetAttributes()) {\n\t\t\t\t//print.it(v.AttributeClass.Name);\n\t\t\t\tswitch (v.AttributeClass.Name) {\n\t\t\t\tcase \"TargetFrameworkAttribute\": needTargetFramework = false; break;\n\t\t\t\tcase \"AssemblyTitleAttribute\": needAssemblyTitle = false; break;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tusing (new StringBuilder_(out var sb)) {\n\t\t\t//sb.AppendLine(\"using System.Reflection;using System.Runtime.InteropServices;\");\n\t\t\t\n\t\t\t//rejected. Now in global.cs, #if !NO_DEFAULT_CHARSET_UNICODE\n\t\t\t//if (needDefaultCharset) sb.AppendLine(\"[module: System.Runtime.InteropServices.DefaultCharSet(System.Runtime.InteropServices.CharSet.Unicode)]\");\n\t\t\t\n\t\t\tif (needTargetFramework) sb.AppendLine($\"[assembly: System.Runtime.Versioning.TargetFramework(\\\"{AppContext.TargetFrameworkName}\\\")]\");\n\t\t\tif (needAssemblyTitle) sb.AppendLine($\"[assembly: System.Reflection.AssemblyTitle(\\\"{_meta.Name}\\\")]\");\n\t\t\t\n\t\t\tif (_meta.Role is MCRole.miniProgram or MCRole.editorExtension) {\n\t\t\t\t_GetDllPaths();\n\t\t\t\tif (_dr != null) { //add RefPaths attribute to resolve paths of managed dlls at run time\n\t\t\t\t\tforeach (var v in _dr) {\n\t\t\t\t\t\tsb.Append(rflags.Has(MiniProgram_.MPFlags.RefPaths) ? \"|\" : $\"[assembly: Au.Types.RefPaths(@\\\"\");\n\t\t\t\t\t\tsb.Append(v.Value);\n\t\t\t\t\t\trflags |= MiniProgram_.MPFlags.RefPaths;\n\t\t\t\t\t}\n\t\t\t\t\tsb.AppendLine(\"\\\")]\");\n\t\t\t\t}\n\t\t\t\tif (_dn != null) { //add NativePaths attribute to resolve paths of native dlls at run time\n\t\t\t\t\tforeach (var v in _dn) {\n\t\t\t\t\t\tsb.Append(rflags.Has(MiniProgram_.MPFlags.NativePaths) ? \"|\" : $\"[assembly: Au.Types.NativePaths(@\\\"\");\n\t\t\t\t\t\tsb.Append(v.Value);\n\t\t\t\t\t\trflags |= MiniProgram_.MPFlags.NativePaths;\n\t\t\t\t\t}\n\t\t\t\t\tsb.AppendLine(\"\\\")]\");\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tif (_meta.TestInternal != null) {\n\t\t\t\t//https://www.strathweb.com/2018/10/no-internalvisibleto-no-problem-bypassing-c-visibility-rules-with-roslyn/\n\t\t\t\t//IgnoresAccessChecksToAttribute is defined in Au assembly.\n\t\t\t\t//\tCould define here, but then warning \"already defined in assembly X\" when compiling 2 projects (meta pr) with that attribute.\n\t\t\t\t//\tnever mind: Au.dll must exist by the compiled assembly, even if not used for other purposes.\n\t\t\t\tforeach (var v in _meta.TestInternal) sb.AppendLine($\"[assembly: System.Runtime.CompilerServices.IgnoresAccessChecksTo(\\\"{v}\\\")]\");\n\t\t\t\t//sb.Append(@\"\n\t\t\t\t//namespace System.Runtime.CompilerServices {\n\t\t\t\t//[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]\n\t\t\t\t//public class IgnoresAccessChecksToAttribute : Attribute {\n\t\t\t\t//\tpublic IgnoresAccessChecksToAttribute(string assemblyName) { AssemblyName = assemblyName; }\n\t\t\t\t//\tpublic string AssemblyName { get; }\n\t\t\t\t//}}\");\n\t\t\t}\n\t\t\t\n\t\t\tvar fm = _meta.MainFile.f;\n\t\t\tsb.AppendLine($\"[assembly: Au.Types.PathInWorkspace(@\\\"{fm.ItemPath}\\\", @\\\"{fm.FilePath}\\\")]\");\n\t\t\tif (_meta.Role == MCRole.exeProgram) {\n\t\t\t\tsb.AppendLine(@\"class ModuleInit__ { [System.Runtime.CompilerServices.ModuleInitializer] internal static void Init() { Au.script.AppModuleInit_(auCompiler: true); }}\");\n\t\t\t}\n\t\t\t\n\t\t\tstring code = sb.ToString(); //print.it(code);\n\t\t\tvar tree = CSharpSyntaxTree.ParseText(code, new CSharpParseOptions(LanguageVersion.Preview)) as CSharpSyntaxTree;\n\t\t\t//insert as first, else user's module initializers would run before. Same speed.\n\t\t\t//_compilation = _compilation.AddSyntaxTrees(tree);\n\t\t\t_compilation = _compilation.RemoveAllSyntaxTrees().AddSyntaxTrees(_compilation.SyntaxTrees.Insert(0, tree));\n\t\t}\n\t\treturn rflags;\n\t}\n\t\n\t//Gets paths of used managed and unmanaged dlls (meta nuget, r, com, pr).\n\t//Sets: _dr - managed dlls, _dn - other dlls. If compiling exeProgram, _dn also contains other files from nuget.\n\t//\tFull paths are in dictionary values. Keys contain filenames or nuget relative paths, and for callers almost not useful.\n\t//Called when:\n\t//\tCompiling exeProgram. The files will be copied to the output.\n\t//\tCompiling miniProgram or editorExtension. Dll paths will be added to an assembly attribute. At run time will use it to find dlls.\n\t//Depending on meta properties, filters out unused dlls, eg 32-bit or dlls for other OS versions.\n\tvoid _GetDllPaths() {\n\t\tDictionary<string, string> dr = null, dn = null, dr2 = null; //managed, native\n\t\tHashSet<FileNode> noDup = null;\n\t\t\n\t\t_Project(_meta);\n\t\t\n\t\tvoid _Project(MetaComments m) {\n\t\t\tif (m != _meta) if (!(noDup ??= new()).Add(m.MainFile.f)) return; //skip duplicates\n\t\t\t\n\t\t\t//managed dlls, except com and nuget\n\t\t\t{\n\t\t\t\tvar refs = m.References.Refs;\n\t\t\t\tfor (int k = m.References.DefaultRefCount; k < refs.Count; k++) {\n\t\t\t\t\tif (refs[k].Properties.EmbedInteropTypes || MetaReferences.IsNuget(refs[k])) continue; //com or nuget\n\t\t\t\t\t\n\t\t\t\t\tif (m.References.NoCopyRefs is { } ncr && _meta.Role == MCRole.exeProgram) {\n\t\t\t\t\t\tif(ncr.Contains(refs[k])) continue;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tvar path = refs[k].FilePath;\n\t\t\t\t\t_Add(ref dr, pathname.getName(path), path);\n\t\t\t\t\t\n\t\t\t\t\t//add managed dlls used by that dll but not specified in meta\n\t\t\t\t\t_Deps(path);\n\t\t\t\t\tvoid _Deps(string path) {\n\t\t\t\t\t\tstring dir = null;\n\t\t\t\t\t\tusing var peReader = new PEReader(filesystem.loadStream(path));\n\t\t\t\t\t\tvar mr = peReader.GetMetadataReader();\n\t\t\t\t\t\tforeach (var arh in mr.AssemblyReferences) {\n\t\t\t\t\t\t\tvar ar = mr.GetAssemblyReference(arh);\n\t\t\t\t\t\t\tvar an = mr.GetString(ar.Name);\n\t\t\t\t\t\t\tif (MetaReferences.IsDefaultRef(an)) continue;\n\t\t\t\t\t\t\tif (dr2?.ContainsKey(an) ?? false) continue;\n\t\t\t\t\t\t\tdir ??= pathname.getDirectory(path, withSeparator: true);\n\t\t\t\t\t\t\tvar path2 = dir + an + \".dll\";\n\t\t\t\t\t\t\tif (!filesystem.exists(path2, useRawPath: true).File) continue;\n\t\t\t\t\t\t\t(dr2 ??= new()).Add(an, path2);\n\t\t\t\t\t\t\t_Deps(path2);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t//managed and native dlls from nuget. If exeProgram, also non-dll files.\n\t\t\tif (m.NugetXmlRoot is XElement xn) {\n\t\t\t\tforeach (var (package, _) in m.NugetPackages) {\n\t\t\t\t\tvar xp = xn.Elem(\"package\", \"path\", package, true);\n\t\t\t\t\tif (xp != null) {\n\t\t\t\t\t\tvar dir = App.Model.NugetDirectoryBS + pathname.getDirectory(package);\n\t\t\t\t\t\tif (xp.Attr(out int format, \"format\")) {\n\t\t\t\t\t\t\tforeach (var f in xp.Elements()) {\n\t\t\t\t\t\t\t\tswitch (f.Name.LocalName) { //see DNuget._Build\n\t\t\t\t\t\t\t\tcase \"r\" or \"rt\":\n\t\t\t\t\t\t\t\t\t_Add2(ref dr, f.Value);\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tcase \"native\":\n\t\t\t\t\t\t\t\t\t_Add2(ref dn, f.Value);\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tcase \"group\":\n\t\t\t\t\t\t\t\t\t_AddGroup(f, false);\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tcase \"natives\":\n\t\t\t\t\t\t\t\t\t_AddGroup(f, true);\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tcase \"other\" when _meta.Role == MCRole.exeProgram:\n\t\t\t\t\t\t\t\t\t//copy all other files. Except XML doc, they are not in nuget.xml.\n\t\t\t\t\t\t\t\t\t_Add2(ref dn, f.Value, isDll: false);\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tvoid _AddGroup(XElement x, bool native) {\n\t\t\t\t\t\t\t\tref var d = ref (native ? ref dn : ref dr);\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tint verPC = osVersion.minWin10 ? 100 : osVersion.minWin8_1 ? 81 : osVersion.minWin8 ? 80 : 70; //don't need Win11\n\t\t\t\t\t\t\t\tint verBest = -1;\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tvar need = _NeedNativeExeAndDllFilesOfPlatforms();\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tstring sBest = null;\n\t\t\t\t\t\t\t\tforeach (var f in x.Elements(native ? \"native\" : \"rt\")) {\n\t\t\t\t\t\t\t\t\tstring s = f.Value; //like \\runtimes\\win\\..., \\runtimes\\win-x64\\..., \\runtimes\\win10-x64\\...\n\t\t\t\t\t\t\t\t\tint i = 13, verDll = 0;\n\t\t\t\t\t\t\t\t\tif (s[i] != '-') verDll = s.ToInt(i, out i);\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\tif (i > 0 && s[i] == '-') {\n\t\t\t\t\t\t\t\t\t\tif (!need.x64 && s.Eq(i, @\"-x64\\\", true)) continue;\n\t\t\t\t\t\t\t\t\t\tif (!need.arm64 && s.Eq(i, @\"-arm64\\\", true)) continue;\n\t\t\t\t\t\t\t\t\t\tif (!need.x86 && s.Eq(i, @\"-x86\\\", true)) continue;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\tif (_meta.Role == MCRole.exeProgram) {\n\t\t\t\t\t\t\t\t\t\t_Add2(ref d, s); //will select at run time\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tif (verDll != 81) verDll *= 10;\n\t\t\t\t\t\t\t\t\t\tif (verDll > verPC) continue;\n\t\t\t\t\t\t\t\t\t\tif (verDll > verBest) {\n\t\t\t\t\t\t\t\t\t\t\tverBest = verDll;\n\t\t\t\t\t\t\t\t\t\t\tsBest = s;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tif (sBest != null) {\n\t\t\t\t\t\t\t\t\t_Add2(ref d, sBest);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t//old format (the very first)\n\t\t\t\t\t\t\tthrow new FileFormatException($\"Please reinstall NuGet package '{package}'.\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\tvoid _Add2(ref Dictionary<string, string> d, string s, bool isDll = true)\n\t\t\t\t\t\t\t=> _Add(ref d, s, dir + s, isDll);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t//unmanaged dlls specified in meta file\n\t\t\tif (m.OtherFiles != null && _meta.Role != MCRole.exeProgram) {\n\t\t\t\tforeach (var v in m.OtherFiles) {\n\t\t\t\t\tif (v.f.IsFolder) {\n\t\t\t\t\t\tforeach (var des in v.f.Descendants()) if (!des.IsFolder) _AddOther(des);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t_AddOther(v.f);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvoid _AddOther(FileNode f) {\n\t\t\t\t\tvar path = f.FilePath;\n\t\t\t\t\tif (!path.Ends(\".dll\", true)) return;\n\t\t\t\t\tdn ??= new(StringComparer.OrdinalIgnoreCase);\n\t\t\t\t\tdn.TryAdd(path, path);\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tif (m.ProjectReferences is { } pr)\n\t\t\t\tforeach (var v in pr) _Project(v.m);\n\t\t}\n\t\t\n\t\tvoid _Add(ref Dictionary<string, string> d, string s, string path, bool isDll = true) {\n\t\t\tif (isDll && _meta.Role == MCRole.exeProgram && _meta.Name.Eqi(pathname.getNameNoExt(s)))\n\t\t\t\tthrow new InvalidOperationException($@\"The program uses a dll file with the same name. Rename C# file {_meta.Name}\");\n\t\t\t\n\t\t\td ??= new(StringComparer.OrdinalIgnoreCase);\n\t\t\tif (d.TryAdd(s, path)) return;\n\t\t\tvar existing = d[s]; if (existing.Eqi(path)) return;\n\t\t\tDebug_.Print($\"compares file data of '{path}' and '{existing}'\");\n\t\t\tif (filesystem.loadBytes(existing).AsSpan().SequenceEqual(filesystem.loadBytes(path))) return;\n\t\t\t\n\t\t\tthrow new InvalidOperationException($@\"Two different versions of file:\n\t{existing}\n\t{path}\");\n\t\t}\n\t\t\n\t\tif (dr2 != null) {\n\t\t\tforeach (var (k, v) in dr2) {\n\t\t\t\t_Add(ref dr, k + \".dll\", v);\n\t\t\t}\n\t\t}\n\t\t\n\t\t//print.it(\"dr\");\n\t\t//print.it(dr);\n\t\t//print.it(\"dn\");\n\t\t//print.it(dn);\n\t\t\n\t\tif (_meta.Role == MCRole.exeProgram) {\n\t\t\t_tpa = _GetDefaultTPA();\n\t\t\tif (dr != null) {\n\t\t\t\t//remove replaced defaults. Rare.\n\t\t\t\tforeach (var v in dr.Keys) {\n\t\t\t\t\tvar fn = pathname.getNameNoExt(v);\n\t\t\t\t\tint i = _tpa.Find_(fn, static (s, from, to) => s[from - 1] == '|' && (to == s.Length || s[to] == '|'), true);\n\t\t\t\t\tif (i > 0) _tpa = _tpa.Remove(i - 1, fn.Length + 1);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tStringBuilder b = null;\n\t\t\t\tforeach (var v in dr.Keys) {\n\t\t\t\t\tif (v.Starts(@\"\\runtimes\\\", true)) continue; //managed by ResolveNugetRuntimes_, because difficult in C++\n\t\t\t\t\tb ??= new(_tpa);\n\t\t\t\t\tint bs = v[0] == '\\\\' ? 1 : 0;\n\t\t\t\t\tb.Append('|').Append(v, bs, v.Length - 4 - bs);\n\t\t\t\t}\n\t\t\t\tif (b != null) _tpa = b.ToString();\n\t\t\t}\n\t\t}\n\t\t\n\t\t_dr = dr;\n\t\t_dn = dn;\n\t}\n\t\n\tstatic string _GetDefaultTPA() {\n\t\tif (s_sDefTPA == null) {\n\t\t\tvar s = _NativeResources.LoadNativeResourceUtf8String_(220, 1);\n\t\t\tint i = s.LastIndexOf(\"|Au|\") + 3; //remove Au.Controls etc\n\t\t\ts_sDefTPA = s[..i];\n\t\t}\n\t\treturn s_sDefTPA;\n\t}\n\tstatic string s_sDefTPA;\n\t\n\tList<ResourceDescription> _CreateManagedResources(string asmName, CSharpSyntaxTree[] trees) {\n\t\tList<ResourceDescription> R = null;\n\t\tif (CompilerUtil.CreateManagedResources(_meta, asmName, trees, _ResourceException, o => { (R ??= new()).Add(new ResourceDescription(o.name, () => o.stream, true)); })) return R;\n\t\treturn null;\n\t}\n\t\n\tvoid _ResourceException(Exception e, FileNode curFile) {\n\t\tvar em = e.ToStringWithoutStack();\n\t\tvar err = _meta.Errors;\n\t\tvar f = _meta.MainFile.f;\n\t\tif (curFile == null) err.AddError(f, \"Failed to add resources. \" + em);\n\t\telse err.AddError(f, $\"Failed to add resource '{curFile.Name}'. \" + em);\n\t}\n\t\n\tStream _CreateNativeResources() {\n#if true\n\t\tif (_meta.Role is MCRole.exeProgram or MCRole.classLibrary) //add only version. If exe, will add icon and manifest to apphost exe. //rejected: support adding icons to dll; VS allows it.\n\t\t\treturn _compilation.CreateDefaultWin32Resources(versionResource: true, noManifest: true, null, null);\n\t\t\n\t\tif (_meta.IconFile != null) { //add only icon. No version, no manifest.\n\t\t\tStream icoStream = null;\n\t\t\tFileNode curFile = null;\n\t\t\ttry {\n\t\t\t\t//if(_meta.ResFile != null) return File.OpenRead((curFile = _meta.ResFile).FilePath);\n\t\t\t\ticoStream = File.OpenRead((curFile = _meta.IconFile).FilePath);\n\t\t\t\tcurFile = null;\n\t\t\t\treturn _compilation.CreateDefaultWin32Resources(versionResource: false, noManifest: true, null, icoStream);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\ticoStream?.Dispose();\n\t\t\t\t_ResourceException(e, curFile);\n\t\t\t}\n\t\t} else if (_GetMainFileIcon(out var stream)) {\n\t\t\treturn _compilation.CreateDefaultWin32Resources(versionResource: false, noManifest: true, null, stream);\n\t\t}\n\t\t\n\t\treturn null;\n#else\n\t\t\tvar manifest = _meta.ManifestFile;\n\n\t\t\tstring manifestPath = null;\n\t\t\tif(manifest != null) manifestPath = manifest.FilePath;\n\t\t\telse if(_meta.Role == MetaRole.exeProgram /*&& _meta.ResFile == null*/) manifestPath = folders.ThisAppBS + \"default.exe.manifest\"; //don't: uac\n\n\t\t\tStream manStream = null, icoStream = null;\n\t\t\tFileNode curFile = null;\n\t\t\ttry {\n\t\t\t\t//if(_meta.ResFile != null) return File.OpenRead((curFile = _meta.ResFile).FilePath);\n\t\t\t\tif(manifestPath != null) { curFile = manifest; manStream = File.OpenRead(manifestPath); }\n\t\t\t\tif(_meta.IconFile != null) icoStream = File.OpenRead((curFile = _meta.IconFile).FilePath);\n\t\t\t\tcurFile = null;\n\t\t\t\treturn _compilation.CreateDefaultWin32Resources(versionResource: true, noManifest: manifestPath == null, manStream, icoStream);\n\t\t\t}\n\t\t\tcatch(Exception e) {\n\t\t\t\tmanStream?.Dispose();\n\t\t\t\ticoStream?.Dispose();\n\t\t\t\t_ResourceException(e, curFile);\n\t\t\t\treturn null;\n\t\t\t}\n#endif\n\t}\n\t\n\tstring _AppHost(string outFile, string fileName, MCPlatform platform) {\n\t\t//A .NET Core+ exe actually is a managed dll hosted by a native exe file known as apphost.\n\t\t//When creating an exe, VS copies template apphost from \"C:\\Program Files\\dotnet\\sdk\\version\\AppHostTemplate\\apphost.exe\"\n\t\t//\tand modifies it, eg copies native resources from the dll.\n\t\t//We have own apphost exe created by the Au.AppHost project. This function copies it and modifies in a similar way like VS does.\n\t\t\n\t\t//var p1 = perf.local();\n\t\tstring exeFile = DllNameToExeName(outFile, platform != _meta.Platform ? platform : default);\n\t\t\n\t\tif (filesystem.exists(exeFile) && !Api.DeleteFile(exeFile)) {\n\t\t\tvar ec = lastError.code;\n\t\t\tif (!(ec == Api.ERROR_ACCESS_DENIED && _RenameLockedFile(exeFile, notInCache: true))) throw new AuException(ec);\n\t\t}\n\t\t\n\t\t//delete old \"program-64.exe\" if now x64, or \"program-arm.exe\" if now arm64. It may exist if user switched meta platform x64/arm64.\n\t\tif (platform is MCPlatform.x64 or MCPlatform.arm64) Api.DeleteFile(exeFile.Insert(^4, platform is MCPlatform.x64 ? \"-64\" : \"-arm\"));\n\t\t\n\t\tvar appHost = folders.ThisAppBS + platform switch { MCPlatform.x86 => \"32\", MCPlatform.arm64 => @\"64\\ARM\", _ => \"64\" } + @\"\\Au.AppHost.exe\";\n\t\tbool done = false;\n\t\ttry {\n\t\t\tvar b = File.ReadAllBytes(appHost);\n\t\t\t//p1.Next();\n\t\t\t//write assembly name in placeholder memory. In AppHost.cpp: char s_asmName[800] = \"\\0hi7yl8kJNk+gqwTDFi7ekQ\";\n\t\t\tint i = b.AsSpan().IndexOf(\"hi7yl8kJNk+gqwTDFi7ekQ\"u8) - 1;\n\t\t\ti += Encoding.UTF8.GetBytes(fileName, 0, fileName.Length, b, i);\n\t\t\tb[i] = 0;\n\t\t\t\n\t\t\tvar res = new _NativeResources();\n\t\t\tif (_meta.IconFile != null) {\n\t\t\t\t_NativeResources.ICONCONTEXT ic = default;\n\t\t\t\tif (_meta.IconFile.IsFolder) {\n\t\t\t\t\tforeach (var des in _meta.IconFile.Descendants()) {\n\t\t\t\t\t\tif (des.IsFolder) continue;\n\t\t\t\t\t\tif (des.Name.Ends(\".ico\", true)) {\n\t\t\t\t\t\t\tres.AddIcon(des.FilePath, ref ic);\n\t\t\t\t\t\t} else if (des.Name.Ends(\".xaml\", true)) {\n\t\t\t\t\t\t\t_GetIconFromXaml(filesystem.loadText(des.FilePath), out var stream);\n\t\t\t\t\t\t\tres.AddIcon(stream.ToArray(), ref ic);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tres.AddIcon(_meta.IconFile.FilePath, ref ic);\n\t\t\t\t}\n\t\t\t} else if (_GetMainFileIcon(out var stream)) {\n\t\t\t\t_NativeResources.ICONCONTEXT ic = default;\n\t\t\t\tres.AddIcon(stream.ToArray(), ref ic);\n\t\t\t}\n\t\t\tres.AddVersion(outFile);\n\t\t\tres.AddTpa(_tpa);\n\t\t\t\n\t\t\tstring manifest = null;\n\t\t\tif (_meta.ManifestFile != null) manifest = _meta.ManifestFile.FilePath;\n\t\t\telse if (_meta.Role == MCRole.exeProgram) manifest = folders.ThisAppBS + \"default.exe.manifest\"; //don't: uac\n\t\t\tif (manifest != null) res.AddManifest(manifest);\n\t\t\t\n\t\t\tres.WriteAll(exeFile, b, platform, _meta.Console);\n\t\t\t\n\t\t\t//speed: AV makes this slooow.\n\t\t\t//p1.NW();\n\t\t\tdone = true;\n\t\t}\n\t\tfinally { if (!done) Api.DeleteFile(exeFile); }\n\t\t\n\t\treturn exeFile;\n\t}\n\t\n\tbool _GetMainFileIcon(out MemoryStream ms) {\n\t\ttry {\n\t\t\tif (DIcons.TryGetIconFromDB(_meta.MainFile.f.CustomIconName, out string xaml)) {\n\t\t\t\t_GetIconFromXaml(xaml, out ms);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\tcatch (Exception e1) { _ResourceException(e1, null); }\n\t\tms = null;\n\t\treturn false;\n\t}\n\t\n\tstatic void _GetIconFromXaml(string xaml, out MemoryStream ms) {\n\t\tms = new MemoryStream();\n\t\tvar e = ImageUtil.LoadWpfImageElement(xaml);\n\t\tImageUtil.ConvertWpfImageElementToIcon_(ms, e, [16, 24, 32, 48, 64]);\n\t\tms.Position = 0;\n\t}\n\t\n\tvoid _CopyDlls(Stream asmStream, bool need64, bool needArm, bool need32) {\n\t\tasmStream.Position = 0;\n\t\t\n\t\t//note: need Au.dll and AuCpp.dll even if not used in code. Au.dll contains script.AppModuleInit_.\n\t\tCompilerUtil.CopyFileIfNeed(folders.ThisAppBS + @\"Au.dll\", _meta.OutputPath + @\"\\Au.dll\");\n\t\t//note: always need all AuCpp.dll and Au.DllHost.exe, because maybe elm will have to load AuCpp.dll into other processes.\n\t\tCompilerUtil.CopyFileIfNeed(folders.ThisAppBS + @\"64\\AuCpp.dll\", _meta.OutputPath + @\"\\64\\AuCpp.dll\");\n\t\tCompilerUtil.CopyFileIfNeed(folders.ThisAppBS + @\"64\\Au.DllHost.exe\", _meta.OutputPath + @\"\\64\\Au.DllHost.exe\");\n\t\tCompilerUtil.CopyFileIfNeed(folders.ThisAppBS + @\"32\\AuCpp.dll\", _meta.OutputPath + @\"\\32\\AuCpp.dll\");\n\t\tCompilerUtil.CopyFileIfNeed(folders.ThisAppBS + @\"32\\Au.DllHost.exe\", _meta.OutputPath + @\"\\32\\Au.DllHost.exe\");\n\t\tCompilerUtil.CopyFileIfNeed(folders.ThisAppBS + @\"64\\ARM\\AuCpp.dll\", _meta.OutputPath + @\"\\64\\ARM\\AuCpp.dll\");\n\t\tCompilerUtil.CopyFileIfNeed(folders.ThisAppBS + @\"64\\ARM\\Au.DllHost.exe\", _meta.OutputPath + @\"\\64\\ARM\\Au.DllHost.exe\");\n\t\t\n\t\t//bool usesSqlite = CompilerUtil.UsesSqlite(asmStream);\n\t\t\n\t\t//copy managed dlls, including from nuget\n\t\tif (_dr != null) {\n\t\t\tforeach (var v in _dr) {\n\t\t\t\tCompilerUtil.CopyFileIfNeed(v.Value, _meta.OutputPath + \"\\\\\" + v.Key);\n\t\t\t\t\n\t\t\t\t//if (!usesSqlite && !v.Value.Starts(App.Model.NugetDirectoryBS)) usesSqlite = CompilerUtil.UsesSqlite(v.Value);\n\t\t\t}\n\t\t}\n\t\t\n\t\t//copy other files from nuget\n\t\tif (_dn != null) {\n\t\t\tforeach (var v in _dn) {\n\t\t\t\tCompilerUtil.CopyFileIfNeed(v.Value, _meta.OutputPath + v.Key);\n\t\t\t}\n\t\t}\n\t\t\n\t\t////print.it(usesSqlite);\n\t\t//if (usesSqlite) {\n\t\t//\tif (need64) CompilerUtil.CopyFileIfNeed(folders.ThisAppBS + @\"64\\sqlite3.dll\", _meta.OutputPath + @\"\\64\\sqlite3.dll\");\n\t\t//\tif (needArm) CompilerUtil.CopyFileIfNeed(folders.ThisAppBS + @\"64\\ARM\\sqlite3.dll\", _meta.OutputPath + @\"\\64\\ARM\\sqlite3.dll\");\n\t\t//\tif (need32) CompilerUtil.CopyFileIfNeed(folders.ThisAppBS + @\"32\\sqlite3.dll\", _meta.OutputPath + @\"\\32\\sqlite3.dll\");\n\t\t//}\n\t\t\n\t\t//copy meta 'file' files\n\t\tCompilerUtil.CopyMetaFileFilesOfAllProjects(_meta, _meta.OutputPath);\n\t}\n\t\n\tbool _RunPrePostBuildScript(bool post, string outFile) {\n\t\tif (_meta.Role == MCRole.exeProgram) outFile = DllNameToExeName(outFile, default);\n\t\treturn CompilerUtil.RunPrePostBuildScript(_meta, post, outFile, false);\n\t}\n\t\n\t/// <summary>\n\t/// Replaces \".dll\" with \".exe\" (if Default) or \"-32.exe\" or \"-64.exe\" or \"-arm.exe\".\n\t/// </summary>\n\tpublic static string DllNameToExeName(string dll, MCPlatform platform)\n\t\t=> dll.ReplaceAt(^4.., platform switch { MCPlatform.x64 => \"-64.exe\", MCPlatform.arm64 => \"-arm.exe\", MCPlatform.x86 => \"-32.exe\", _ => \".exe\" });\n\t\n\t(bool x64, bool arm64, bool x86) _NeedNativeExeAndDllFilesOfPlatforms() {\n\t\tif (_meta.Role == MCRole.exeProgram) {\n\t\t\tif (_meta.Platform == MCPlatform.x86) return (false, false, true);\n\t\t\tif (_meta.Optimize) return (true, true, false);\n\t\t}\n\t\tif (_meta.Platform == MCPlatform.arm64) return (false, true, false);\n\t\treturn (true, false, false);\n\t}\n\t\n\tstatic bool _RenameLockedFile(string file, bool notInCache) {\n\t\t//If the assembly file is currently loaded, we get ERROR_SHARING_VIOLATION. But we can rename the file.\n\t\t//tested: can't rename if ERROR_USER_MAPPED_FILE or ERROR_LOCK_VIOLATION.\n\t\tstring renamed = null;\n\t\tfor (int i = 1; ; i++) {\n\t\t\trenamed = file + \"'\" + i.ToString();\n\t\t\tif (Api.MoveFileEx(file, renamed, 0)) goto g1;\n\t\t\tif (lastError.code != Api.ERROR_ALREADY_EXISTS) break;\n\t\t\tif (Api.MoveFileEx(file, renamed, Api.MOVEFILE_REPLACE_EXISTING)) goto g1;\n\t\t}\n\t\treturn false;\n\t\tg1:\n\t\tif (notInCache) {\n\t\t\tif (s_renamedFiles == null) {\n\t\t\t\ts_renamedFiles = new List<string>();\n\t\t\t\tprocess.thisProcessExit += _ => _DeleteRenamedLockedFiles(null);\n\t\t\t\ts_rfTimer = new timer(_DeleteRenamedLockedFiles);\n\t\t\t}\n\t\t\tif (!s_rfTimer.IsRunning) s_rfTimer.Every(60_000);\n\t\t\ts_renamedFiles.Add(renamed);\n\t\t}\n\t\treturn true;\n\t}\n\tstatic List<string> s_renamedFiles;\n\tstatic timer s_rfTimer;\n\t\n\t//TODO3: remove this? Probably fails anyway. Will delete when this app starts next time.\n\tstatic void _DeleteRenamedLockedFiles(timer timer) {\n\t\tvar a = s_renamedFiles;\n\t\tfor (int i = a.Count; --i >= 0;) {\n\t\t\tif (Api.DeleteFile(a[i]) || lastError.code == Api.ERROR_FILE_NOT_FOUND) a.RemoveAt(i);\n\t\t}\n\t\tif (a.Count == 0) timer?.Stop();\n\t}\n\t\n\tpublic static void Warmup(CAW::Microsoft.CodeAnalysis.Document document) {\n\t\t//using var p1 = perf.local();\n\t\tvar compilation = document.Project.GetCompilationAsync().Result_();\n\t\t//compilation.GetDiagnostics(); //just makes Emit faster, and does not make the real GetDiagnostics faster first time\n\t\t//var eOpt = new EmitOptions(debugInformationFormat: DebugInformationFormat.Embedded);\n\t\tvar asmStream = new MemoryStream(16000);\n\t\tcompilation.Emit(asmStream);\n\t\t//compilation.Emit(asmStream, null, options: eOpt); //somehow makes slower later\n\t\t//compilation.Emit(asmStream, null, xdStream, resNat, resMan, eOpt);\n\t}\n}\n\nenum CCReason { Run, CompileAlways, CompileIfNeed, WpfPreview }\n\nrecord CanCompileArgs(MetaComments m, CSharpSyntaxTree[] trees, CSharpCompilation compilation);\n"
  },
  {
    "path": "Au.Editor/Compiler/EditorExtension.cs",
    "content": "//#define STREAM\n//#define TEST_STARTUP_SPEED\n//#define TEST_UNLOAD\n\nusing System.Runtime.Loader;\nusing System.Windows;\n\nusing LA;\n\n/// <summary>\n/// Functions and events for scripts with role editorExtension.\n/// The script must have this at the start <c>/*/ role editorExtension; r Au.Editor.dll; /*/</c>.\n/// </summary>\npublic static class EditorExtension {\n\t/// <summary>\n\t/// Executes assembly in this thread.\n\t/// </summary>\n\t/// <param name=\"asmFile\">Full path of assembly file.</param>\n\t/// <param name=\"args\">To pass to Main.</param>\n\t/// <param name=\"handleExceptions\">Handle/print exceptions.</param>\n\tinternal static async void Run_(string asmFile, string[] args, bool handleExceptions) {\n\t\ttry {\n\t\t\t//using var p1 = perf.local();\n\t\t\t\n\t\t\t_LoadedScriptAssembly lsa = default;\n\t\t\tvar (asm, loaded) = lsa.Find(asmFile);\n\t\t\tif (asm == null) {\n\t\t\t\tvar alc = new AssemblyLoadContext(null, isCollectible: true);\n\t\t\t\t//p1.Next();\n\t\t\t\t\n#if STREAM\n\t\t\t\t//Uses LoadFromStream, not LoadFromAssemblyPath.\n\t\t\t\t//LoadFromAssemblyPath has this problem: does not reload modified assembly from same file.\n\t\t\t\t//\tNeed a dll file with unique name for each version of same script.\n\t\t\t\t//Note: the 'loaded' was intended to make faster in some cases. However cannot use it because of the xor.\n\t\t\t\t//tested: step-debugging works. Don't need the overload with pdb parameter.\n\n\t\t\t\tvar b = File.ReadAllBytes(asmFile); //with WD ~7 ms, but ~25 ms without xor. With Avast ~7 ms regardless of xor. Both fast if already scanned.\n\t\t\t\tfor (int i = 0; i < b.Length; i++) b[i] ^= 1; //prevented AV full dll scan twice. Now fully scans once (WD always scans when loading assembly from stream; Avast when loading from stream or earlier).\n\t\t\t\tusing var stream = new MemoryStream(b, false);\n\t\t\t\t//p1.Next();\n\t\t\t\tasm = alc.LoadFromStream(stream); //with WD always 15-25 ms. With Avast it seems always fast.\n#else\n\t\t\t\t//Uses LoadFromAssemblyPath. If LoadFromStream, no source file/line info in stack traces; also no Assembly.Location, and possibly more problems.\n\t\t\t\t//never mind: Creates and loads many dlls when edit-run many times.\n\t\t\t\t//tested: .NET unloads dlls, but later than Assembly objects, maybe after 1-2 minutes, randomly.\n\t\t\t\t\n\t\t\t\tif (loaded) {\n\t\t\t\t\tvar s = asmFile.Insert(^4, \"'\" + perf.mcs.ToString()); //info: compiler will delete all files with \"'\" on first run after editor restart\n#if true //copy file\n\t\t\t\t\tunsafe {\n\t\t\t\t\t\tif (!Api.CopyFileEx(asmFile, s, 0)) throw new AuException(0, \"failed to copy assembly file\");\n\t\t\t\t\t}\n\t\t\t\t\t//p1.Next('C');\n\t\t\t\t\t//bad: WD makes much slower. Scans 2 times. Avast scans faster, and only when copying.\n\t\t\t\t\t//never mind: compiler should create file with unique name, to avoid copying now. Probably would complicate too much, or even not possible.\n\t\t\t\t\tasm = alc.LoadFromAssemblyPath(s);\n#else //rename file. Faster, but unreliable when need to run soon again. Then compiler would not find the file and compile again. Or the new file could be replaced with the old, etc.\n\t\t\t\t\tif (!Api.MoveFileEx(asmFile, s, 0)) throw new AuException(0, \"failed to rename assembly file\");\n\t\t\t\t\tp1.Next('C'); //WD does not scan when renaming\n\t\t\t\t\tasm = alc.LoadFromAssemblyPath(s);\n\t\t\t\t\tp1.Next('L');\n\t\t\t\t\t//now need to rename or copy back. Else would compile each time.\n\t\t\t\t\t//if (!Api.MoveFileEx(s, asmFile, 0)) throw new AuException(0, \"failed to rename assembly file\"); //works, but no stack trace\n\t\t\t\t\tTask.Run(() => { Api.CopyFileEx(s, asmFile, null, default, null, 0); }); //make compiler happy next time. Now let AV scan it async.\n#endif\n\t\t\t\t} else {\n\t\t\t\t\tasm = alc.LoadFromAssemblyPath(asmFile);\n\t\t\t\t}\n#endif\n#if TEST_UNLOAD\n\t\t\t\tnew _AssemblyDtor(asm);\n#endif\n\t\t\t\t//p1.Next('L');\n\t\t\t\t\n\t\t\t\tlsa.Add(asmFile, asm);\n\t\t\t\t\n\t\t\t\t//this event will be here for editorExtension assemblies only.\n\t\t\t\t//\tLibraries used by editorExtension scripts are loaded by AssemblyLoadContext.Default.\n\t\t\t\t//\tDlls used by meta pr libraries are resolved in app project -> _UnmanagedDll_Resolving.\n\t\t\t\t//\tDon't need alc.Resolving here. Libraries are always loaded in default context.\n\t\t\t\talc.ResolvingUnmanagedDll += (asm, name) => MiniProgram_.ResolveUnmanagedDllFromNativePathsAttribute_(name, asm);\n\t\t\t}\n\t\t\t\n\t\t\tvar entryPoint = asm.EntryPoint ?? throw new InvalidOperationException(\"assembly without entry point (function Main)\");\n\t\t\t\n\t\t\t//support async. Then EntryPoint returns some `void <Main>(string[])` that calls the true entry method and hangs.\n\t\t\tbool isAsync = false;\n\t\t\tif (entryPoint.IsSpecialName) {\n\t\t\t\tvar ep2 = entryPoint.DeclaringType.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static)\n\t\t\t\t\t.FirstOrDefault(static o => o.ReturnType is var rt && (rt == typeof(Task) || rt == typeof(Task<int>)) && o.Name is \"<Main>$\" or \"Main\" && o.GetParameters() is { } pa && (pa.Length == 0 || (pa.Length == 1 && pa[0].ParameterType == typeof(string[]))) && o.IsDefined(typeof(System.Runtime.CompilerServices.AsyncStateMachineAttribute)));\n\t\t\t\tif (isAsync = ep2 != null) entryPoint = ep2;\n\t\t\t}\n\t\t\t\n\t\t\tvar epParams = entryPoint.GetParameters().Length != 0 ? new object[] { args ?? Array.Empty<string>() } : null;\n\t\t\t\n\t\t\tif (isAsync) {\n\t\t\t\tawait (Task)entryPoint.Invoke(null, epParams);\n\t\t\t} else {\n\t\t\t\tentryPoint.Invoke(null, epParams);\n\t\t\t}\n\t\t}\n\t\tcatch (Exception e1) when (handleExceptions) {\n\t\t\tprint.it(e1 is TargetInvocationException te ? te.InnerException : e1);\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Remembers and finds script assemblies loaded in this process, to avoid loading the same unchanged assembly multiple times.\n\t/// </summary>\n\tstruct _LoadedScriptAssembly {\n\t\tlong _fileTime;\n\t\t\n\t\t[MethodImpl(MethodImplOptions.NoInlining)]\n\t\tpublic (Assembly asm, bool loaded) Find(string asmFile) {\n\t\t\tif (filesystem.GetTime_(asmFile, out var time)) {\n\t\t\t\t_fileTime = time;\n\t\t\t\tif (_d.TryGetValue(asmFile, out var x)) {\n\t\t\t\t\tbool modified = x.time != _fileTime;\n\t\t\t\t\t\n\t\t\t\t\tif (x.restarting != null) {\n\t\t\t\t\t\tvar v = x.restarting; x.restarting = null;\n\t\t\t\t\t\ttry { v(modified); }\n\t\t\t\t\t\tcatch (Exception e1) { print.it(e1); }\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tif (!modified) return (x.asm, true);\n\t\t\t\t\t_d.Remove(asmFile);\n\t\t\t\t\treturn (null, true);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn default;\n\t\t}\n\t\t\n\t\t[MethodImpl(MethodImplOptions.NoInlining)]\n\t\tpublic void Add(string asmFile, Assembly asm) {\n\t\t\tif (_fileTime == default) return; //filesystem.getProperties failed\n\t\t\t_d.Add(asmFile, new _Asm { time = _fileTime, asm = asm });\n\t\t\t\n\t\t\t//foreach(var v in _d.Values) print.it(v.asm.FullName);\n\t\t\t//foreach(var v in AppDomain.CurrentDomain.GetAssemblies()) print.it(v.FullName);\n\t\t}\n\t}\n\t\n\tclass _Asm {\n\t\tpublic long time;\n\t\tpublic Assembly asm;\n\t\tpublic Action<bool> restarting;\n\t}\n\t\n\tstatic readonly Dictionary<string, _Asm> _d = new(StringComparer.OrdinalIgnoreCase);\n\t\n\tstatic _Asm _FindAsmValue(Assembly asm) {\n\t\tforeach (var v in _d.Values) if (v.asm == asm) return v;\n\t\tthrow new InvalidOperationException(\"This code must be in the script assembly\");\n\t}\n\t\n\t/// <summary>\n\t/// When starting new instance of this script.\n\t/// </summary>\n\t/// <remarks>\n\t/// <para>The bool parameter is true if the script has been modified. It also means that the new instance will be a new assembly. If false, the new instance will be the same loaded assembly.</para>\n\t/// \n\t/// <para>The event handler should end all activities of current instance: unsubscribe events, stop timers, end threads and other tasks, remove added UI elements, etc. Unless it supports multiple instances.</para>\n\t/// \n\t/// <para>If the old instance does not end its activities properly when restarting, you may notice these problems:\n\t/// <br/>• Duplicate events, timers, etc.\n\t/// <br/>• Old assemblies never unloaded after restarting modified script. Usually such memory leaks aren't dangerous, but be aware of it. To avoid it, let the event handler ensure that the script is not leaving any GC roots, for example event handlers subscribed to external events, running timers, threads and other code that could still run in the future. If the assembly is rooted, GC cannot unload it.\n\t/// </para>\n\t/// <para>If the script has static fields, and the parameter is false, the new script will inherit their values. You may want to reset them.</para>\n\t/// \n\t/// <para>When this event is raised, it is unsubscribed automatically. Don't need to unsubscribe.</para>\n\t/// </remarks>\n\tpublic static event Action<bool> Restarting {\n\t\tadd { _FindAsmValue(Assembly.GetCallingAssembly()).restarting += value; }\n\t\tremove { _FindAsmValue(Assembly.GetCallingAssembly()).restarting -= value; }\n\t}\n\t\n\t/// <summary>\n\t/// When the main window loaded. If already loaded when adding the event handler, it is called now instead.\n\t/// </summary>\n\t/// <remarks>\n\t/// <para>Don't need to unsubscribe. The event can occur max 1 time in this process, and then is unsubscribed automatically.</para>\n\t/// \n\t/// <para>If the program starts hidden (it can be set in Options), it does not create the main window until need to show it (eg clicked tray icon). While hidden, <see cref=\"App.Hmain\"/> is <c>default(wnd)</c>; <see cref=\"App.Wmain\"/> is not null, but its <b>IsLoaded</b> property returns false. The window is created the first time it must be shown, and then not closed until it's time to exit the process. If the program starts visible, the event handler is executed when trying to subscribe the event.</para>\n\t/// </remarks>\n\tpublic static event Action WindowLoaded {\n\t\tadd {\n\t\t\tif (App.Loaded == AppState.LoadedUI) {\n\t\t\t\t_InvokeAction(value);\n\t\t\t} else if (App.Loaded < AppState.LoadedUI) {\n\t\t\t\t_windowLoaded += value;\n\t\t\t}\n\t\t}\n\t\tremove { _windowLoaded -= value; }\n\t}\n\tstatic Action _windowLoaded;\n\t\n\tinternal static void WindowLoaded_() {\n\t\tvar v = _windowLoaded; _windowLoaded = null;\n\t\t_InvokeAction(v);\n\t}\n\t\n\tstatic void _InvokeAction(Action a) {\n\t\tif (a == null) return;\n\t\ttry { a(); }\n\t\tcatch (Exception e1) { print.it(e1); }\n\t}\n\t\n\t/// <summary>\n\t/// When main window loaded and is ready for editing. If already ready when adding the event handler, it is called now instead.\n\t/// </summary>\n\t/// <remarks>\n\t/// Don't need to unsubscribe. The event can occur max 1 time in this process, and then is unsubscribed automatically.\n\t/// </remarks>\n\tpublic static event Action WindowReady {\n\t\tadd { if (CodeInfo.IsReadyForEditing) _InvokeAction(value); else CodeInfo.ReadyForEditing += value; } //handles exceptions\n\t\tremove { CodeInfo.ReadyForEditing -= value; }\n\t}\n\t\n\t//note: cannot add event WindowCreated (before loaded, when still invisible). If editor starts visible, startup scripts run when it is already loaded/visible.\n\t\n\t/// <summary>\n\t/// When closing current workspace.\n\t/// </summary>\n\t/// <remarks>\n\t/// <para>The bool parameter is true on program exit, false when opening another workspace (or reopening this).</para>\n\t/// \n\t/// <para>Everything is already saved. Documents in editor still not closed. Task processes still not ended.</para>\n\t/// \n\t/// <para>When this event is raised, it is unsubscribed automatically. The script must unsubscribe only on <see cref=\"Restarting\"/>.</para>\n\t/// </remarks>\n\tpublic static event Action<bool> ClosingWorkspace {\n\t\tadd { _closingWorkspace += value; }\n\t\tremove { _closingWorkspace -= value; }\n\t}\n\t\n\tinternal static void ClosingWorkspace_(bool onExit) {\n\t\tif (_closingWorkspace == null) return;\n\t\tvar v = _closingWorkspace; _closingWorkspace = null;\n\t\ttry { v(onExit); }\n\t\tcatch (Exception e1) { dialog.showError(\"Exception in EditorExtension.ClosingWorkspace event handler\", e1.ToString(), owner: App.Hmain.IsVisible ? App.Hmain : default, secondsTimeout: 5); }\n\t\tif (App.Loaded >= AppState.LoadedUI) App.Model.Save.AllNowIfNeed();\n\t}\n\tstatic Action<bool> _closingWorkspace;\n\t\n#if TEST_UNLOAD\n\t//This shows that AssemblyLoadContext are unloaded on GC.\n\tclass _AssemblyLoadContext : AssemblyLoadContext {\n\t\tpublic _AssemblyLoadContext(string name, bool isCollectible) : base(name, isCollectible) { }\n\n\t\t~_AssemblyLoadContext() { print.it(\"AssemblyLoadContext unloaded\", Name); }\n\n\t\t//protected override Assembly Load(AssemblyName assemblyName) {\n\t\t//\t//print.it(\"Load\", assemblyName);\n\t\t//\treturn null;\n\t\t//}\n\t}\n\n\t//This shows that Assembly are unloaded on GC, although later than AssemblyLoadContext.\n\t//The dlls are unloaded even later, maybe after 1-2 minutes.\n\tclass _AssemblyDtor {\n\t\tstatic readonly ConditionalWeakTable<Assembly, _AssemblyDtor> s_cwt = new();\n\n\t\treadonly string _file, _name;\n\n\t\tpublic _AssemblyDtor(Assembly a) {\n\t\t\ts_cwt.Add(a, this);\n\t\t\t_file = a.Location;\n\t\t\t_name = a.FullName.Split('|')[0];\n\t\t}\n\n\t\t~_AssemblyDtor() {\n\t\t\tprint.it(\"Assembly unloaded\", _name, _file);\n\t\t\tvar s = _file;\n\t\t\tTask.Delay(120_000).ContinueWith(_ => { print.it(Api.DeleteFile(s)); });\n\t\t}\n\n\t\t//BAD: memory leak:\n\t\t//\tFirst assembles never GC-collected after removing from _d (when starting a modified version of the script).\n\t\t//\tOnly if the assembly loaded at startup when app started hidden.\n\t\t//\tTested with VS tools: they don't have GC roots.\n\t\t//\tIt seems it's a .NET bug. Could not find a workaround.\n\t\t//\tNever mind. It's a very small leak compared to WPF leaks.\n\t\t//\tSee also: https://learn.microsoft.com/en-us/dotnet/standard/assembly/unloadability?source=recommendations\n\n\t\t//public static void Test2() {\n\t\t//\tprint.it(\"-- _AssemblyDtor --\");\n\t\t//\tforeach(var v in s_cwt) {\n\t\t//\t\tprint.it(v.Key.Location);\n\t\t//\t}\n\t\t//}\n\t}\n\n\t//public static void Test() {\n\t//\tforeach (var v in _d) {\n\t//\t\tprint.it(v.Key, v.Value.asm.Location);\n\t//\t}\n\t//\t//_AssemblyDtor.Test2();\n\t//}\n#endif\n}\n"
  },
  {
    "path": "Au.Editor/Compiler/ErrBuilder.cs",
    "content": "using Microsoft.CodeAnalysis;\n\nnamespace LA;\n\n/// <summary>Code errors text builder.</summary>\nclass ErrBuilder {\n\tStringBuilder _b;\n\t\n\tpublic int ErrorCount { get; private set; }\n\tpublic int WarningCount { get; private set; }\n\t\n\tpublic void Clear() {\n\t\tErrorCount = 0; WarningCount = 0;\n\t\t_b = null;\n\t}\n\t\n\t/// <summary>\n\t/// Adds compiler error or warning message with a link that opens the C# file and goes to that position.\n\t/// </summary>\n\t/// <param name=\"d\">Error or warning.</param>\n\t/// <param name=\"fMain\">Main file of the compilation. Used only for failures that are not C# errors, eg when fails to open a metadata reference file.</param>\n\t/// <remarks>\n\t/// This and other AddX functions add line like \"[C:\\path\\file.cs(line,column)]: message\" or \"[C:\\path\\file.cs]: message\".\n\t/// Then our PrintServer.SetNotifications callback will convert the \"[...]\" part to a link.\n\t/// </remarks>\n\tpublic void AddErrorOrWarning(Diagnostic d, FileNode fMain) {\n\t\t_StartAdd(isWarning: d.Severity != DiagnosticSeverity.Error);\n\t\tvar s = d.ToString();\n\t\tint i = d.Location.IsInSource ? s.Find(\"): \") + 1 : 0;\n\t\tif (i > 0) {\n\t\t\t_b.AppendFormat(\"[{0}]{1}\", s[..i], s[i..]);\n\t\t} else {\n\t\t\t_b.AppendFormat(\"[{0}]: {1}\", fMain.FilePath, s);\n\t\t}\n\t\t\n\t\t//if empty script, error \"no Main\". Users would not understand why.\n\t\t//\trejected: auto-add {} (empty block, to make the script not empty). Difficult to detect when need it. Eg would give an incorrect/unclear error if Main exists but is unsuitable.\n\t\tif (d.Severity == DiagnosticSeverity.Error && d.Id == \"CS5001\") _b.Append(\". Or the script is empty.\");\n\t}\n\t\n\t/// <summary>\n\t/// Adds error message with a link that opens the C# file but does not go to a position.\n\t/// </summary>\n\tpublic void AddError(FileNode f, string message) {\n\t\t_StartAdd();\n\t\t_b.AppendFormat(\"[{0}]: {1}\", f.FilePath, message);\n\t}\n\t\n\t/// <summary>\n\t/// Adds error message with a link that opens the C# file and goes to the specified position.\n\t/// Used for meta errors.\n\t/// </summary>\n\t/// <param name=\"f\">C# file.</param>\n\t/// <param name=\"code\">f code.</param>\n\t/// <param name=\"pos\">Position in code.</param>\n\t/// <param name=\"message\">Text to append. Example: \"error: message\".</param>\n\t/// <param name=\"formatArgs\">If not null/empty, calls StringBuilder.AppendFormat(message, formatArgs).</param>\n\tpublic void AddError(FileNode f, string code, int pos, string message, params object[] formatArgs) {\n\t\t_StartAdd();\n\t\tStringUtil.LineAndColumn(code, pos, out int line, out int col);\n\t\t_Append(f, line, col, message, formatArgs);\n\t}\n\t\n\t/// <summary>\n\t/// Adds error message with a link that opens the C# file and goes to the specified position.\n\t/// </summary>\n\t/// <param name=\"pos\">Position in code.</param>\n\t/// <param name=\"message\">Text to append. Example: \"error: message\".</param>\n\t/// <param name=\"formatArgs\">If not null/empty, calls StringBuilder.AppendFormat(message, formatArgs).</param>\n\tpublic void AddError(FileNode f, Microsoft.CodeAnalysis.Text.LinePosition pos, string message, params object[] formatArgs) {\n\t\t_StartAdd();\n\t\t_Append(f, pos.Line, pos.Character, message, formatArgs);\n\t}\n\t\n\tvoid _StartAdd(bool isWarning = false) {\n\t\tif (_b == null) _b = new();\n\t\telse _b.AppendLine();\n\t\t\n\t\tif (isWarning) WarningCount++; else ErrorCount++;\n\t}\n\t\n\tvoid _Append(FileNode f, int line, int col, string message, params object[] formatArgs) {\n\t\t_Append(f.FilePath, line, col, message, formatArgs);\n\t}\n\t\n\tvoid _Append(string file, int line, int col, string message, params object[] formatArgs) {\n\t\t_b.AppendFormat(\"[{0}({1},{2})]: \", file, ++line, ++col);\n\t\tif ((formatArgs?.Length ?? 0) != 0) _b.AppendFormat(message, formatArgs); else _b.Append(message);\n\t}\n\t\n\tpublic override string ToString() => _b?.ToString();\n\t\n\t/// <summary>\n\t/// Prints all errors and warnings.\n\t/// Calls <see cref=\"Clear\"/>.\n\t/// Does nothing if no errors and warnings.\n\t/// </summary>\n\tpublic void PrintAll() {\n\t\tif (_b == null) return;\n\t\t\n\t\tvar s = ToString();\n\t\t_b.Clear();\n\t\t\n\t\t//header line\n\t\t_b.AppendFormat(\"<><lc #{0}>Compilation: \", ErrorCount != 0 ? \"F0E080\" : \"A0E0A0\");\n\t\tif (ErrorCount != 0) _b.Append(ErrorCount).Append(\" errors\").Append(WarningCount != 0 ? \", \" : \"\");\n\t\tif (WarningCount != 0) _b.Append(WarningCount).Append(\" warnings <fold>\tWarnings can be disabled with <google>C# #pragma warning<> or in <b>Properties<>.</fold>\");\n\t\t_b.AppendLine(\"<>\");\n\t\t\n\t\t//errors and warnings\n\t\t_b.Append(s);\n\t\t\n\t\tprint.it(_b.ToString());\n\t\tClear();\n\t}\n\t\n\t//currently not used.\n\t///// <summary>\n\t///// If the SyntaxTree contains errors, prints them (<see cref=\"PrintAll\"/>) and returns true.\n\t///// In any case, prints errors and warnings.\n\t///// </summary>\n\t///// <param name=\"tree\"></param>\n\t///// <param name=\"f\"></param>\n\t///// <param name=\"printWarnings\">Add warnings too (but print only if error).</param>\n\t//public bool AddAllAndPrint(SyntaxTree tree, FileNode f, bool printWarnings = false)\n\t//{\n\t//\tforeach(var v in tree.GetDiagnostics()) {\n\t//\t\tif(v.Severity == DiagnosticSeverity.Error || printWarnings) AddErrorOrWarning(v, f);\n\t//\t}\n\t//\tif(ErrorCount == 0) return false;\n\t//\tPrintAll();\n\t//\treturn true;\n\t//}\n}\n"
  },
  {
    "path": "Au.Editor/Compiler/MetaComments.cs",
    "content": "using System.Xml.Linq;\n\nusing Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp;\n\n//CONSIDER: c https://..../file.cs\n//\tProblem: security. These files could contain malicious code.\n//\tIt seems nuget supports source files, not only compiled assemblies: https://stackoverflow.com/questions/52880687/how-to-share-source-code-via-nuget-packages-for-use-in-net-core-projects\n\nnamespace LA;\n\n//This XML doc is outdated. Most info is in the Properties dialog.\n/// <summary>\n/// Extracts C# file/project settings, references, etc from meta comments in C# code.\n/// </summary>\n/// <remarks>\n/// To compile C# code, often need various settings, more files, etc. In Visual Studio you can set it in project Properties and Solution Explorer. In Au you can set it in C# code as meta comments.\n/// Meta comments is a block of comments that starts and ends with <c>/*/</c>. Must be at the start of C# code. Before can be only comments, empty lines, spaces and tabs. Example:\n/// <code><![CDATA[\n/// /*/ option1 value1; option2 value2; option2 value3 /*/\n/// ]]></code>\n/// Options and values must match case, except filenames/paths. No \"enclosing\", no escaping.\n/// Some options can be several times with different values, for example to specify several references.\n/// When compiling multiple files (project, or using option 'c'), only the main file can contain all options. Other files can contain only 'r', 'c', 'com', 'nuget', 'resource', 'file'.\n/// All available options are in the examples below. Here a|b|c means a or b or c. The //comments are not allowed in real meta comments.\n/// </remarks>\n/// <example>\n/// <h3>References</h3>\n/// <code><![CDATA[\n/// r Assembly //assembly reference. With or without \".dll\". Must be in folders.ThisApp.\n/// r C:\\X\\Y\\Assembly.dll //assembly reference using full path. If relative path, must be in folders.ThisApp.\n/// r Alias=Assembly //assembly reference that can be used with C# keyword 'extern alias'.\n/// ]]></code>\n/// Don't need to add Au.dll and .NET runtime assemblies.\n/// \n/// <h3>Other C# files to compile together</h3>\n/// <code><![CDATA[\n/// c file.cs //a class file in this C# file's folder\n/// c folder\\file.cs //path relative to this C# file's folder\n/// c .\\folder\\file.cs //the same as above\n/// c ..\\folder\\file.cs //path relative to the parent folder\n/// c \\folder\\file.cs //path relative to the workspace folder\n/// ]]></code>\n/// The file must be in this workspace. Or it can be a link (in workspace) to an external file. The same is true with most other options.\n/// If folder, compiles all its descendant class files.\n/// \n/// <h3>References to libraries created in this workspace</h3>\n/// <code><![CDATA[\n/// pr \\folder\\file.cs\n/// ]]></code>\n/// Compiles the .cs file or its project and uses the output dll file like with option r. It is like a \"project reference\" in Visual Studio.\n/// \n/// <h3>References to COM interop assemblies (.NET assemblies converted from COM type libraries)</h3>\n/// <code><![CDATA[\n/// com Accessibility 1.1 44782f49.dll\n/// ]]></code>\n/// How this different from option r:\n/// 1. If not full path, must be in @\"%folders.Workspace%\\.interop\".\n/// 2. The interop assembly is used only when compiling, not at run time. It contains only metadata, not code. The compiler copies used parts of metadata to the output assembly. The real code is in native COM dll, which at run time must be registered as COM component and must match the bitness (64-bit or 32-bit) of the process that uses it. \n/// \n/// <h3>Files to add to managed resources</h3>\n/// <code><![CDATA[\n/// resource file.png  //file as stream. Can be filename or relative path, like with 'c'.\n/// resource file.ext /byte[]  //file as byte[]\n/// resource file.txt /string  //text file as string\n/// resource file.csv /strings  //CSV file containing multiple strings as 2-column CSV (name, value)\n/// resource file.png /embedded  //file as embedded resource stream\n/// resource folder  //all files in folder, as streams\n/// resource folder /byte[]  //all files in folder, as byte[]\n/// resource folder /string  //all files in folder, file as strings\n/// resource folder /embedded  //all files in folder, as embedded resource streams\n/// ]]></code>\n/// More info in .cs of the Properties window.\n/// \n/// <h3>Other files</h3>\n/// <code><![CDATA[\n/// file file.png\n/// file file.dll /output_subfolder\n/// ]]></code>\n/// \n/// <h3>Settings used when compiling</h3>\n/// <code><![CDATA[\n/// optimize false|true //if false (default), don't optimize code; this is known as \"Debug configuration\". If true, optimizes code; then low-level code is faster, but can be difficult to debug; this is known as \"Release configuration\".\n/// define SYMBOL1,SYMBOL2,d:DEBUG_ONLY,r:RELEASE_ONLY //define preprocessor symbols that can be used with #if etc. If no optimize true, DEBUG and TRACE are added implicitly.\n/// warningLevel 1 //compiler warning level.\n/// noWarnings 3009,162 //don't show these compiler warnings\n/// testInternal Assembly1,Assembly2 //access internal symbols of specified assemblies, like with InternalsVisibleToAttribute\n/// preBuild file /arguments //run this script before compiling. More info below.\n/// postBuild file /arguments //run this script after compiled successfully. More info below.\n/// ]]></code>\n/// About options 'preBuild' and 'postBuild':\n/// The script must have meta option role editorExtension. It runs in compiler's thread. Compiler waits and does not respond during that time. To stop compilation, let the script throw an exception.\n/// The script has parameter (variable) string[] args. If there is no /arguments, args[0] is the output assembly file, full path. Else args contains the specified arguments, parsed like a command line. In arguments you can use these variables:\n/// $(outputFile) -  the output assembly file, full path; $(sourceFile) - the C# file, full path; $(source) - path of the C# file in workspace, eg \"\\folder\\file.cs\"; $(outputPath) - meta option 'outputPath', default \"\"; $(optimize) - meta option 'optimize', default \"false\".\n/// \n/// <h3>Settings used to run the compiled script</h3>\n/// <code><![CDATA[\n/// ifRunning warn_restart|warn|cancel_restart|cancel|wait_restart|wait|run_restart|run|restart|end|end_restart //what to do if this script is already running. Default: warn_restart. More info below.\n/// uac inherit|user|admin //UAC integrity level (IL) of the task process. Default: inherit. More info below.\n/// platform default|x64|arm64|x86\n/// ]]></code>\n/// Here word \"task\" is used for \"script that is running or should start\".\n/// Options 'ifRunning' and 'uac' are applied only when the task is started from editor process, not when it runs as independent exe program.\n/// \n/// About ifRunning:\n/// When trying to start this script, what to do if it is already running. Values:\n/// warn - print warning and don't run.\n/// cancel - don't run.\n/// wait - run later, when that task ends.\n/// run - run simultaneously.\n/// restart - end it and run.\n/// end - end it and don't run.\n/// If ends with _restart, the Run button/menu will restart. Useful for quick edit-test.\n/// \n/// About uac:\n/// inherit (default) - the task process has the same UAC integrity level (IL) as the editor process.\n/// user - Medium IL, like most applications. The task cannot automate high IL process windows, write some files, change some settings, etc.\n/// admin - High IL, aka \"administrator\", \"elevated\". The task has many rights, but cannot automate some apps through COM, etc.\n/// \n/// <h3>Settings used to create assembly file</h3>\n/// <code><![CDATA[\n/// role miniProgram|exeProgram|editorExtension|classLibrary|classFile //purpose of this C# file. Also the type of the output assembly file (exe, dll, none). Default: miniProgram for scripts, classFile for class files. More info below.\n/// outputPath path //create output files (.exe, .dll, etc) in this directory. Used with role exeProgram and classLibrary. Can be full path or relative path like with 'c'. Default for exeProgram: %folders.Workspace%\\exe\\filename. Default for classLibrary: %folders.Workspace%\\dll.\n/// console false|true //let the program run with console\n/// icon file.ico //icon of the .exe file. Can be filename or relative path, like with 'c'.\n/// manifest file.manifest //manifest file of the .exe file. Can be filename or relative path, like with 'c'.\n/// (rejected) resFile file.res //file containing native resources to add to the .exe/.dll file. Can be filename or relative path, like with 'c'.\n/// sign file.snk //sign the output assembly with a strong name using this .snk file. Can be filename or relative path, like with 'c'. \n/// xmlDoc false|true //create XML documentation file from XML comments. Creates in the 'outputPath' directory.\n/// ]]></code>\n/// \n/// About role:\n/// If role is 'exeProgram' or 'classLibrary', creates .exe or .dll file, named like this C# file, in 'outputPath' directory.\n/// If role is 'miniProgram' (default for scripts) or 'editorExtension', creates a temporary assembly file in subfolder \".compiled\" of the workspace folder.\n/// If role is 'classFile' (default for class files) does not create any output files from this C# file. Its purpose is to be compiled together with other C# code files.\n/// If role is 'editorExtension', the task runs in the main UI thread of the editor process. Rarely used. Can be used to create editor extensions. The user cannot see and end the task. Creates memory leaks when executing recompiled assemblies (eg after editing the script), because old assembly versions cannot be unloaded until process exits.\n/// \n/// Full path can be used with 'r', 'com', 'outputPath'. It can start with an environment variable or special folder name, like <c>%folders.ThisAppDocuments%\\file.exe</c>.\n/// Files used with other options ('c', 'resource' etc) must be in this workspace.\n/// \n/// About native resources:\n/// (rejected) If option 'resFile' specified, adds resources from the file, and cannot add other resources; error if also specified 'icon' or 'manifest'.\n/// If 'manifest' and 'resFile' not specified when creating .exe file, adds manifest from file \"default.exe.manifest\" in the main Au folder.\n/// If 'resFile' not specified when creating .exe or .dll file, adds version resource, with values from attributes such as [assembly: AssemblyVersion(\"...\")]; see how it is in Visual Studio projects, in file Properties\\AssemblyInfo.cs.\n/// </example>\nclass MetaComments {\n\t/// <summary>\n\t/// Name of the main C# file, without \".cs\".\n\t/// </summary>\n\tpublic string Name { get; private set; }\n\t\n\t/// <summary>\n\t/// The compilation entry file. Probably not <c>CodeFiles[0]</c>.\n\t/// </summary>\n\tpublic MCCodeFile MainFile { get; private set; }\n\t\n\t/// <summary>\n\t/// All C# files of this compilation.\n\t/// The order is optimized for compilation and does not match the natural order:\n\t/// - If there is global.cs, it is the first, followed by its descendant meta c files, total <see cref=\"GlobalCount\"/>.\n\t/// - Then main file, preceded by its descendant meta c files.\n\t/// - Then project files, each preceded by its descendant meta c files.\n\t/// </summary>\n\tpublic List<MCCodeFile> CodeFiles { get; private set; }\n\t\n\t/// <summary>\n\t/// Count of global files, ie global.cs and its meta c descendants. They are at the start of <see cref=\"CodeFiles\"/>.\n\t/// Note: if main file is a descendant of global.cs, it and its descendants are not included.\n\t/// </summary>\n\tpublic int GlobalCount { get; private set; }\n\t\n\t/// <summary>\n\t/// Meta option 'optimize'.\n\t/// Default: false.\n\t/// </summary>\n\tpublic bool Optimize { get; private set; }\n\t\n\t/// <summary>\n\t/// Meta option 'define' + default preprocessor symbols.\n\t/// </summary>\n\tpublic string[] Defines { get; private set; }\n\tList<string> _defines;\n\tstatic readonly string[] s_defaultDefines = [\"NET10_0\"/*delete when updating .NET*/, \"NET10_0_OR_GREATER\", \"NET9_0_OR_GREATER\", \"NET8_0_OR_GREATER\", \"NET7_0_OR_GREATER\", \"NET6_0_OR_GREATER\", \"NET5_0_OR_GREATER\", \"NETCOREAPP3_1_OR_GREATER\", \"NETCOREAPP3_0_OR_GREATER\", \"NETCOREAPP\", \"NET\", \"WINDOWS\", \"WINDOWS7_0_OR_GREATER\"]; //and no WINDOWS10_0_17763_0_OR_GREATER etc (see VS intellisense at #if Ctrl+Space)\n\t\n\t/// <summary>\n\t/// Meta option 'warningLevel'.\n\t/// Default: <see cref=\"DefaultWarningLevel\"/>.\n\t/// </summary>\n\tpublic int WarningLevel { get; private set; }\n\t\n\tpublic const int DefaultWarningLevel = 8;\n\t//Wave 7 adds 1 warning (\"The type name only contains lower-cased ascii characters\"), not useful.\n\t//When changing the value here, change it also in `File properties.md`.\n\t\n\t/// <summary>\n\t/// Meta option 'noWarnings'.\n\t/// Default: <see cref=\"DefaultNoWarnings\"/>.\n\t/// </summary>\n\tpublic List<string> NoWarnings { get; private set; }\n\t\n\t/// <summary>\n\t/// Gets or sets default meta option 'noWarnings' value. Initially CS1701,CS1702 (from default VS project properties) and CS8981 (lowercase typename).\n\t/// </summary>\n\tpublic static string[] DefaultNoWarnings { get; set; } = [\"CS1701\", \"CS1702\", \"CS8981\"];\n\t\n\t/// <summary>\n\t/// Meta 'testInternal'.\n\t/// Default: null.\n\t/// </summary>\n\tpublic string[] TestInternal { get; private set; }\n\t\n\t/// <summary>\n\t/// All meta errors of all files. Includes meta syntax errors, file 'not found' errors, exceptions.\n\t/// null if flag <b>ForCodeInfo</b> or <b>ForFindReferences</b>.\n\t/// </summary>\n\tpublic ErrBuilder Errors { get; private set; }\n\t\n\t/// <summary>\n\t/// Default references and unique references added through meta options 'r', 'com', 'nuget' and 'pr' in all C# files of this compilation.\n\t/// Use References.<see cref=\"MetaReferences.Refs\"/>.\n\t/// </summary>\n\tpublic MetaReferences References { get; private set; }\n\t\n\t/// <summary>\n\t/// Project main files added through meta option 'pr'.\n\t/// null if none.\n\t/// </summary>\n\tpublic List<(FileNode f, MetaComments m)> ProjectReferences { get; private set; }\n\t\n\t/// <summary>\n\t/// Meta nuget, like @\"-\\PackageName\".\n\t/// </summary>\n\tpublic List<(string package, string alias)> NugetPackages { get; private set; }\n\t\n\t/// <summary>\n\t/// If there are meta nuget, returns the root element of the auto-loaded XML file that contains a list of installed NuGet packages and their files. Else null.\n\t/// </summary>\n\tpublic XElement NugetXmlRoot => _xnuget;\n\tXElement _xnuget;\n\t\n\t/// <summary>\n\t/// Unique resource files added through meta option 'resource' in all C# files of this compilation.\n\t/// null if none.\n\t/// </summary>\n\tpublic List<MCFileAndString> Resources { get; private set; }\n\t\n\t/// <summary>\n\t/// Unique files added through meta option 'file' in all C# files of this compilation.\n\t/// null if none.\n\t/// </summary>\n\tpublic List<MCFileAndString> OtherFiles { get; private set; }\n\t\n\t/// <summary>\n\t/// Meta c files and folders for exporting.\n\t/// </summary>\n\tinternal List<FileNode> ExportC_ { get; private set; }\n\t\n\t/// <summary>\n\t/// Meta option 'preBuild'.\n\t/// </summary>\n\tpublic MCFileAndString PreBuild { get; private set; }\n\t\n\t/// <summary>\n\t/// Meta option 'postBuild'.\n\t/// </summary>\n\tpublic MCFileAndString PostBuild { get; private set; }\n\t\n\t/// <summary>\n\t/// Meta option 'ifRunning'.\n\t/// Default: warn_restart (warn and don't run, but restart if from editor).\n\t/// </summary>\n\tpublic MCIfRunning IfRunning { get; private set; }\n\t\n\t/// <summary>\n\t/// Meta option 'uac'.\n\t/// Default: inherit.\n\t/// </summary>\n\tpublic MCUac Uac { get; private set; }\n\t\n\t/// <summary>\n\t/// Meta option 'startFaster'.\n\t/// Default: false.\n\t/// </summary>\n\tpublic bool StartFaster { get; private set; }\n\t\n\t/// <summary>\n\t/// Meta option 'platform'.\n\t/// If meta not specified, this property is = <see cref=\"DefaultPlatform\"/> if executable role, else <b>Default</b>.\n\t/// </summary>\n\tpublic MCPlatform Platform { get; private set; }\n\t\n\t/// <summary>\n\t/// Default <see cref=\"Platform\"/> when meta 'platform' not specified for an executable role.\n\t/// Same as LA (x64 or arm64).\n\t/// </summary>\n\tpublic static MCPlatform DefaultPlatform => osVersion.isArm64Process ? MCPlatform.arm64 : MCPlatform.x64;\n\t\n\t/// <summary>\n\t/// Meta option 'console'.\n\t/// Default: false.\n\t/// </summary>\n\tpublic bool Console { get; private set; }\n\t\n\t/// <summary>\n\t/// Meta option 'icon'.\n\t/// </summary>\n\tpublic FileNode IconFile { get; private set; }\n\t\n\t/// <summary>\n\t/// Meta option 'manifest'.\n\t/// </summary>\n\tpublic FileNode ManifestFile { get; private set; }\n\t\n\t//rejected\n\t///// <summary>\n\t///// Meta option 'res'.\n\t///// </summary>\n\t//public FileNode ResFile { get; private set; }\n\t\n\t/// <summary>\n\t/// Meta option 'outputPath'.\n\t/// Default: null.\n\t/// </summary>\n\tpublic string OutputPath { get; private set; }\n\t\n\t/// <summary>\n\t/// Meta option 'role'.\n\t/// Default: miniProgram if script, else classFile.\n\t/// In WPF preview mode it's always miniProgram.\n\t/// </summary>\n\tpublic MCRole Role { get; private set; }\n\t\n\t/// <summary>\n\t/// Gets default meta option 'role' value. It is miniProgram if isScript, else classFile.\n\t/// </summary>\n\tpublic static MCRole DefaultRole(bool isScript) => isScript ? MCRole.miniProgram : MCRole.classFile;\n\t\n\t/// <summary>\n\t/// Same As <b>Role</b>, but unchanged in WPF preview mode.\n\t/// </summary>\n\tpublic MCRole UnchangedRole { get; private set; }\n\t\n\t/// <summary>\n\t/// Meta option 'sign'.\n\t/// </summary>\n\tpublic FileNode SignFile { get; private set; }\n\t\n\t/// <summary>\n\t/// Meta 'xmlDoc'.\n\t/// Default: false.\n\t/// </summary>\n\tpublic bool XmlDoc { get; private set; }\n\t\n\tpublic NullableContextOptions Nullable { get; private set; }\n\t\n\t/// <summary>\n\t/// Which options are specified.\n\t/// </summary>\n\tpublic MCSpecified Specified { get; private set; }\n\t\n\t/// <summary>\n\t/// If there is meta, gets character positions before the starting /*/ and after the ending /*/. Else default.\n\t/// </summary>\n\tpublic StartEnd MetaRange { get; private set; }\n\t\n\t/// <summary>\n\t/// Meta 'miscFlags'.\n\t/// <br/>• 1 - don't add XAML icons from code strings to resources.\n\t/// </summary>\n\tpublic int MiscFlags { get; private set; }\n\t\n\treadonly MCFlags _flags;\n\treadonly Dictionary<FileNode, string> _fileTextCache;\n\t\n\t/// <param name=\"fileTextCache\">\n\t/// If not null, tries to get file text from it; if not found, loads and adds to it.\n\t/// Use to avoid loading text of same file many times when processing multiple files. Eg for each file need global.cs and its meta c.\n\t/// </param>\n\tpublic MetaComments(MCFlags flags, Dictionary<FileNode, string> fileTextCache = null) {\n\t\t_flags = flags;\n\t\t_fileTextCache = fileTextCache;\n\t}\n\t\n\t/// <summary>\n\t/// Extracts meta comments from all C# files of this compilation, including project files and files added through meta option 'c'.\n\t/// Call once.\n\t/// </summary>\n\t/// <returns>false if there are errors, except with flag <b>ForCodeInfo</b> or <b>ForFindReferences</b>. Then use <see cref=\"Errors\"/>.</returns>\n\t/// <param name=\"f\">Main C# file. If projFolder not null, must be the main file of the project.</param>\n\t/// <param name=\"projFolder\">Project folder of the main file, or null if it is not in a project.</param>\n\tpublic bool Parse(FileNode f, FileNode projFolder) {\n\t\tif (!_flags.Has(MCFlags.ForCodeInfo)) Errors = new ErrBuilder();\n\t\tif (_flags.Has(MCFlags.Publish)) Optimize = true;\n\t\t\n\t\tif (_ParseFile(f, true, false)) {\n\t\t\tif (projFolder != null) {\n\t\t\t\tforeach (var ff in projFolder.EnumProjectClassFiles(f)) _ParseFile(ff, false, false);\n\t\t\t}\n\t\t\t\n\t\t\t//print.it(GlobalCount, CodeFiles);\n\t\t\t\n\t\t\t//define d:DEBUG_ONLY, r:RELEASE_ONLY\n\t\t\tfor (int i = _defines.Count; --i >= 0;) {\n\t\t\t\tvar s = _defines[i]; if (s.Length < 3 || s[1] != ':') continue;\n\t\t\t\tbool? del = s[0] switch { 'r' => !Optimize, 'd' => Optimize, _ => null };\n\t\t\t\tif (del == true) _defines.RemoveAt(i); else if (del == false) _defines[i] = s[2..];\n\t\t\t}\n\t\t\t\n\t\t\tif (!Optimize) {\n\t\t\t\tif (!_defines.Contains(\"DEBUG\")) _defines.Add(\"DEBUG\");\n\t\t\t\tif (!_defines.Contains(\"TRACE\")) _defines.Add(\"TRACE\");\n\t\t\t}\n\t\t\t//if(Role == MCRole.exeProgram && !_defines.Contains(\"EXE\")) _defines.Add(\"EXE\"); //rejected\n\t\t\tDefines = [.. s_defaultDefines, .. _defines];\n\t\t\t\n\t\t\tif (_flags.Has(MCFlags.ForFindReferences)) return true;\n\t\t\t\n\t\t\t_f = MainFile;\n\t\t\t_metaRange = MetaRange;\n\t\t\t_FinalCheckOptions();\n\t\t\t\n\t\t\tif (Platform == default && Role <= MCRole.editorExtension) Platform = DefaultPlatform;\n\t\t}\n\t\t\n\t\tif (Errors?.ErrorCount > 0) {\n\t\t\tif (_flags.Has(MCFlags.PrintErrors)) Errors.PrintAll();\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\tif (_flags.Has(MCFlags.Publish) && Role == MCRole.classLibrary) {\n\t\t\tOutputPath = folders.ThisAppTemp + @\"publish\\pr\\\" + Name;\n\t\t}\n\t\t\n\t\treturn true;\n\t}\n\t\n\t/// <summary>\n\t/// Gets meta comments from a C# file and its meta c descendants.\n\t/// </summary>\n\tbool _ParseFile(FileNode f, bool isMain, bool isC, bool isGlobal = false) {\n\t\tif (!isMain && _CodeFilesContains(f)) return false;\n\t\t\n\t\tstring code = null;\n\t\tif (_fileTextCache == null || !_fileTextCache.TryGetValue(f, out code)) {\n\t\t\tif (f.GetCurrentText(out code, silent: true).error is string es1) { Errors?.AddError(f, es1); return false; }\n\t\t\t_fileTextCache?.Add(f, code);\n\t\t}\n\t\t\n\t\tbool isScript = f.IsScript;\n\t\tvar cf = new MCCodeFile(f, code, isMain, isC);\n\t\t\n\t\tif (isMain) {\n\t\t\tMainFile = cf;\n\t\t\t\n\t\t\tName = pathname.getNameNoExt(f.Name);\n\t\t\t\n\t\t\tWarningLevel = DefaultWarningLevel;\n\t\t\tNoWarnings = DefaultNoWarnings != null ? new(DefaultNoWarnings) : new();\n\t\t\t_defines = new();\n\t\t\tRole = DefaultRole(isScript);\n\t\t\t\n\t\t\tCodeFiles = new();\n\t\t\tReferences = new();\n\t\t\tNugetPackages = new();\n\t\t}\n\t\t\n\t\tCodeFiles.Add(cf);\n\t\tint nc = CodeFiles.Count;\n\t\tvar fPrev = _f; _f = cf;\n\t\t\n\t\t//add global.cs\n\t\tif (isMain) {\n\t\t\tvar model = f.Model;\n\t\t\tif (!_flags.Has(MCFlags.ExportNoGlobal)) {\n\t\t\t\tvar glob = model.FindGlobalCs(); //fast, uses dictionary\n\t\t\t\tif (glob != null) {\n\t\t\t\t\tif (glob == f) isGlobal = true;\n\t\t\t\t\telse {\n\t\t\t\t\t\tif (_flags.Has(MCFlags.Export)) (ExportC_ ??= new()).Add(glob);\n\t\t\t\t\t\t_ParseFile(glob, false, true, isGlobal: true);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tvar meta = _metaRange = FindMetaComments(code);\n\t\tif (meta.end > 0) {\n\t\t\tif (isMain) MetaRange = meta;\n\t\t\tforeach (var t in EnumOptions(code, meta)) {\n\t\t\t\t//var p1 = perf.local();\n\t\t\t\t_ParseOption(t.Name, t.Value, t.nameStart, t.valueStart);\n\t\t\t\t//p1.Next(); var t1 = p1.TimeTotal; if(t1 > 5) print.it(t1, t.Name(), t.Value());\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (isMain) {\n\t\t\tthis.UnchangedRole = this.Role;\n\t\t\tif (_flags.Has(MCFlags.WpfPreview)) {\n\t\t\t\tthis.Role = MCRole.miniProgram;\n\t\t\t\tthis.IfRunning = MCIfRunning.run;\n\t\t\t\t_defines.Add(\"WPF_PREVIEW\");\n\t\t\t\tthis.Uac = default;\n\t\t\t\t//this.StartFaster = true;\n\t\t\t\tthis.Platform = default;\n\t\t\t\tthis.Console = false;\n\t\t\t\tthis.Optimize = false;\n\t\t\t\tthis.OutputPath = null;\n\t\t\t\tthis.PreBuild = default;\n\t\t\t\tthis.PostBuild = default;\n\t\t\t\tthis.XmlDoc = false;\n\t\t\t\tthis.Nullable = default;\n\t\t\t}\n\t\t}\n\t\t\n\t\t//let at first compile \"global.cs\" and meta c files. Why:\n\t\t//\t1. If they have same classes etc or assembly/module attributes, it's better to show error in current file.\n\t\t//\t2. If they have module initializers, it's better to call them first.\n\t\tif (isGlobal) {\n\t\t\tGlobalCount = CodeFiles.Count - (isMain ? 0 : 1);\n\t\t} else if (CodeFiles.Count > nc) {\n\t\t\tCodeFiles.RemoveAt(nc - 1);\n\t\t\tCodeFiles.Add(cf);\n\t\t}\n\t\t_f = fPrev;\n\t\t\n\t\treturn true;\n\t}\n\t\n\tMCCodeFile _f; //current\n\tStartEnd _metaRange; //current\n\t\n\tvoid _ParseOption(string name, string value, int iName, int iValue) {\n\t\tif (name is null) return; //disabled\n\t\t\n\t\t//print.it(name, value);\n\t\t_nameFrom = iName; _nameTo = iName + name.Length;\n\t\t_valueFrom = iValue; _valueTo = iValue + value.Length;\n\t\t\n\t\tif (value.Length == 0) { _ErrorV(\"value cannot be empty\"); return; }\n\t\tbool forCodeInfo = _flags.Has(MCFlags.ForCodeInfo),\n\t\t\tforFindRef = _flags.Has(MCFlags.ForFindReferences),\n\t\t\tforExport = _flags.Has(MCFlags.Export);\n\t\t\n\t\tswitch (name) {\n\t\tcase \"r\" or \"com\" or \"pr\" or \"nuget\":\n\t\t\tif (forExport) if (name[0] != 'p' || !_flags.Has(MCFlags.ExportPR)) return;\n\t\t\tif (!MetaReferences.ParseRefAliasEtc_(name, value, out var s1, out var alias, out bool noCopy)) {\n\t\t\t\t_ErrorV(\"invalid string\");\n\t\t\t} else if (name[0] == 'n') {\n\t\t\t\t_NuGet(s1, alias);\n\t\t\t} else {\n\t\t\t\tif (name[0] == 'p') {\n\t\t\t\t\t//Specified |= EMSpecified.pr;\n\t\t\t\t\tif (!_PR(ref s1) || forCodeInfo || forExport) return;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ttry {\n\t\t\t\t\t//var p1 = perf.local();\n\t\t\t\t\tif (!References.Resolve(s1, alias, isCOM: name[0] == 'c', isNuget: false, noCopy: noCopy)) {\n\t\t\t\t\t\t_ErrorV(\"reference assembly not found: \" + s1); //TODO3: need more info, or link to Help\n\t\t\t\t\t}\n\t\t\t\t\t//p1.NW('r');\n\t\t\t\t}\n\t\t\t\tcatch (Exception e) {\n\t\t\t\t\t_ErrorV(\"exception: \" + e.Message); //unlikely. If bad format, will be error later, without position info.\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\tcase \"c\":\n\t\t\tif (_GetFile(value, FNFind.Any) is FileNode ff) {\n\t\t\t\tif (forExport) (ExportC_ ??= new()).Add(ff);\n\t\t\t\tif (ff.IsFolder) {\n\t\t\t\t\tforeach (var v in ff.Descendants()) if (v.IsClass) _ParseFile(v, false, true);\n\t\t\t\t} else {\n\t\t\t\t\tif (ff.IsClass) _ParseFile(ff, false, true);\n\t\t\t\t\telse _ErrorV(\"must be a class file\");\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\tcase \"file\":\n\t\t\tif (!forFindRef) {\n\t\t\t\tvar fs1 = _GetFileAndString(value, FNFind.Any);\n\t\t\t\tif (!forCodeInfo && fs1.f != null) {\n\t\t\t\t\tOtherFiles ??= new();\n\t\t\t\t\tif (!OtherFiles.Exists(o => o == fs1)) OtherFiles.Add(fs1);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\tcase \"miscFlags\":\n\t\t\tif (!forCodeInfo) MiscFlags = value.ToInt();\n\t\t\treturn;\n\t\tcase \"noRef\" when _f.isMain:\n\t\t\tReferences.RemoveFromRefs(value);\n\t\t\treturn;\n\t\t}\n\t\tif (_flags.Has(MCFlags.OnlyRef)) return;\n\t\t\n\t\tif (name is \"resource\") {\n\t\t\tif (!forFindRef) {\n\t\t\t\t//if (value.Ends(\" /resources\")) { //add following resources in value.resources instead of in AssemblyName.g.resources. //rejected. Rarely used. Would need more code, because meta resource can be in multiple files.\n\t\t\t\t//\tif (!forCodeInfo) (Resources ??= new()).Add(new(null, value[..^11]));\n\t\t\t\t//\treturn;\n\t\t\t\t//}\n\t\t\t\tvar fs1 = _GetFileAndString(value, FNFind.Any);\n\t\t\t\tif (!forCodeInfo && fs1.f != null) {\n\t\t\t\t\tResources ??= new();\n\t\t\t\t\tif (!Resources.Exists(o => o == fs1)) Resources.Add(fs1);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tif (!_f.isMain) {\n\t\t\tif (!forFindRef) {\n\t\t\t\t//In class files compiled as not main silently ignore all options if the first option is role other than class.\n\t\t\t\t//\tIt allows to test a class file without a test script etc.\n\t\t\t\t//\tHow: In meta define symbol X. Then #if X, enable executable code that uses the class.\n\t\t\t\tif (name is \"role\") {\n\t\t\t\t\tif (_f.allowAnyMeta_ = _Enum(out MCRole ro1, value) && ro1 != MCRole.classFile) return;\n\t\t\t\t} else if (_f.allowAnyMeta_) {\n\t\t\t\t\tif (name is \"optimize\" or \"define\" or \"warningLevel\" or \"noWarnings\" or \"testInternal\" or \"preBuild\" or \"postBuild\" or \"outputPath\" or \"ifRunning\" or \"uac\" or \"platform\" or \"bit32\" or \"console\" or \"manifest\" or \"icon\" or \"sign\" or \"xmlDoc\") return;\n\t\t\t\t\t_ErrorN(\"unknown meta comment option\");\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t_ErrorN($\"in this file only these options can be used: c, r, pr, nuget, com, resource, file. Others only in the main file of the compilation - {MainFile.f.Name}. <help editor/Class files, projects>More info<>.\");\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tswitch (name) {\n\t\tcase \"role\":\n\t\t\t_Specified(MCSpecified.role);\n\t\t\tif (_Enum(out MCRole ro, value)) {\n\t\t\t\tRole = ro;\n\t\t\t\tif (MainFile.f.IsScript && (ro == MCRole.classFile || Role == MCRole.classLibrary)) _ErrorV(\"role classFile and classLibrary can be only in class files\");\n\t\t\t}\n\t\t\treturn;\n\t\tcase \"optimize\":\n\t\t\t_Specified(MCSpecified.optimize);\n\t\t\tif (_TrueFalse(out bool optim, value) && !_flags.Has(MCFlags.Publish)) Optimize = optim;\n\t\t\treturn;\n\t\tcase \"define\":\n\t\t\t_Specified(MCSpecified.define);\n\t\t\t_defines.AddRange(value.Split_(','));\n\t\t\treturn;\n\t\tcase \"testInternal\":\n\t\t\t_Specified(MCSpecified.testInternal);\n\t\t\tTestInternal = value.Split_(',');\n\t\t\treturn;\n\t\tcase \"sign\":\n\t\t\t_Specified(MCSpecified.sign);\n\t\t\tSignFile = _GetFile(value, FNFind.File);\n\t\t\treturn;\n\t\t}\n\t\tif (forFindRef) return;\n\t\t\n\t\tswitch (name) {\n\t\tcase \"warningLevel\":\n\t\t\t_Specified(MCSpecified.warningLevel);\n\t\t\tint wl = value.ToInt();\n\t\t\tif (wl >= 0 && wl <= 9999) WarningLevel = wl;\n\t\t\telse _ErrorV(\"must be 0 - 9999\");\n\t\t\tbreak;\n\t\tcase \"noWarnings\":\n\t\t\t_Specified(MCSpecified.noWarnings);\n\t\t\tNoWarnings.AddRange(value.Split_(','));\n\t\t\tbreak;\n\t\tcase \"preBuild\":\n\t\t\t_Specified(MCSpecified.preBuild);\n\t\t\tPreBuild = _GetFileAndString(value, FNFind.CodeFile);\n\t\t\tbreak;\n\t\tcase \"postBuild\":\n\t\t\t_Specified(MCSpecified.postBuild);\n\t\t\tPostBuild = _GetFileAndString(value, FNFind.CodeFile);\n\t\t\tbreak;\n\t\tcase \"outputPath\":\n\t\t\t_Specified(MCSpecified.outputPath);\n\t\t\tif (!forCodeInfo) OutputPath = _GetOutPath(value);\n\t\t\tbreak;\n\t\tcase \"ifRunning\":\n\t\t\t_Specified(MCSpecified.ifRunning);\n\t\t\tif (_Enum(out MCIfRunning ifR, value)) IfRunning = ifR;\n\t\t\tbreak;\n\t\tcase \"uac\":\n\t\t\t_Specified(MCSpecified.uac);\n\t\t\tif (_Enum(out MCUac uac, value)) Uac = uac;\n\t\t\tbreak;\n\t\tcase \"startFaster\": //undocumented. Likely will be removed in the future.\n\t\t\t_Specified(MCSpecified.startFaster);\n\t\t\tif (_TrueFalse(out bool startFaster, value)) StartFaster = startFaster;\n\t\t\tbreak;\n\t\tcase \"platform\":\n\t\t\t_Specified(MCSpecified.platform);\n\t\t\tif (_Enum(out MCPlatform platform, value)) Platform = platform;\n\t\t\tbreak;\n\t\tcase \"bit32\": //fbc. Now use platform instead.\n\t\t\t_Specified(MCSpecified.platform);\n\t\t\tif (_TrueFalse(out bool is32, value) && is32) Platform = MCPlatform.x86;\n\t\t\tbreak;\n\t\tcase \"console\":\n\t\t\t_Specified(MCSpecified.console);\n\t\t\tif (_TrueFalse(out bool con, value)) Console = con;\n\t\t\tbreak;\n\t\tcase \"manifest\":\n\t\t\t_Specified(MCSpecified.manifest);\n\t\t\tManifestFile = _GetFile(value, FNFind.File);\n\t\t\tbreak;\n\t\tcase \"icon\":\n\t\t\t_Specified(MCSpecified.icon);\n\t\t\tIconFile = _GetFile(value, FNFind.Any);\n\t\t\tbreak;\n\t\t//case \"resFile\":\n\t\t//\t_Specified(EMSpecified.resFile);\n\t\t//\tResFile = _GetFile(value);\n\t\t//\tbreak;\n\t\tcase \"xmlDoc\":\n\t\t\t_Specified(MCSpecified.xmlDoc);\n\t\t\tif (_TrueFalse(out bool xmlDOc, value)) XmlDoc = xmlDOc;\n\t\t\tbreak;\n\t\tcase \"nullable\":\n\t\t\t_Specified(MCSpecified.nullable);\n\t\t\tif (_Enum(out NullableContextOptions nco, value)) Nullable = nco;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\t_ErrorN(\"unknown meta comment option\");\n\t\t\tbreak;\n\t\t}\n\t}\n\t\n\t#region util\n\t\n\tint _nameFrom, _nameTo, _valueFrom, _valueTo;\n\t\n\tbool _Error(string s, int from, int to) {\n\t\tif (Errors != null) {\n\t\t\tErrors.AddError(_f.f, _f.code, from, \"error in meta: \" + s);\n\t\t} else if (_flags.Has(MCFlags.ForCodeInfoInEditor) && _f.f == Panels.Editor.ActiveDoc.EFile) {\n\t\t\tCodeInfo._diag.AddMetaError(_metaRange, from, to, s);\n\t\t}\n\t\treturn false;\n\t}\n\t\n\t/// <summary>Error in name.</summary>\n\tbool _ErrorN(string s) => _Error(s, _nameFrom, _nameTo);\n\t\n\t/// <summary>Error in value.</summary>\n\tbool _ErrorV(string s) => _Error(s, _valueFrom, _valueTo);\n\t\n\t/// <summary>Error in unknown place.</summary>\n\tbool _ErrorM(string s) => _Error(s, _metaRange.start, _metaRange.start + 3);\n\t\n\tvoid _Specified(MCSpecified what) {\n\t\tif (Specified.Has(what)) _ErrorN(\"this meta comment option is already specified\");\n\t\tSpecified |= what;\n\t}\n\t\n\tbool _TrueFalse(out bool b, string s) {\n\t\tb = false;\n\t\tswitch (s) {\n\t\tcase \"true\" or \"!false\": b = true; break;\n\t\tcase \"false\" or \"!true\": break;\n\t\tdefault: return _ErrorV(\"must be true or false or !true or !false\");\n\t\t}\n\t\treturn true;\n\t}\n\t\n\tunsafe bool _Enum<T>(out T result, string s) where T : unmanaged, Enum {\n\t\tDebug.Assert(sizeof(T) == 4);\n\t\tbool R = _Enum2(typeof(T), out int v, s);\n\t\tresult = Unsafe.As<int, T>(ref v);\n\t\treturn R;\n\t}\n\tbool _Enum2(Type t, out int result, string s) {\n\t\tresult = default;\n\t\tif (!s_enumCache.TryGetValue(t, out var r)) {\n\t\t\tvar a = t.GetFields(BindingFlags.Public | BindingFlags.Static);\n\t\t\tint n = a.Length; foreach (var v in a) if (v.Name.Starts('_')) n--;\n\t\t\tr = new (string, int)[n];\n\t\t\tfor (int i = 0, j = 0; i < a.Length; i++) {\n\t\t\t\tvar sn = a[i].Name;\n\t\t\t\tif (!sn.Starts('_')) {\n\t\t\t\t\tif (char.IsUpper(sn[0])) sn = char.ToLowerInvariant(sn[0]) + sn[1..];\n\t\t\t\t\tr[j++] = (sn, (int)a[i].GetRawConstantValue());\n\t\t\t\t}\n\t\t\t}\n\t\t\ts_enumCache[t] = r;\n\t\t}\n\t\tforeach (var v in r) if (v.name == s) { result = v.value; return true; }\n\t\treturn _ErrorV(\"must be one of:\\n\" + string.Join(\", \", r.Select(o => o.name)));\n\t}\n\tstatic readonly Dictionary<Type, (string name, int value)[]> s_enumCache = new();\n\t\n\tFileNode _GetFile(string s, FNFind kind) {\n\t\tstring s0 = s;\n\t\tvar f = _f.f.FindRelative(true, s, kind);\n\t\tif (f == null) {\n\t\t\tif (App.Model.FoundMultiple != null) _ErrorV($\"multiple '{s}' exist in this workspace. Use path, or rename a file.\");\n\t\t\telse if (kind == FNFind.Folder) _ErrorV($\"folder '{s}' does not exist in this workspace\");\n\t\t\telse if (kind != FNFind.Any && null != _f.f.FindRelative(false, s, FNFind.File)) _ErrorV($\"expected a {(kind == FNFind.Class ? \"class\" : \"C#\")} file\");\n\t\t\telse _ErrorV($\"file '{s}' does not exist in this workspace\");\n\t\t\treturn null;\n\t\t}\n\t\tint v = filesystem.exists(s = f.FilePath, true);\n\t\tif (v != (f.IsFolder ? 2 : 1)) { _ErrorV(\"file does not exist: \" + s); return null; }\n\t\t\n\t\tif (_flags.Has(MCFlags.Export) && s0.Contains('\\\\')) { _ErrorV($\"must be <c green>{pathname.getName(s0)}<>. Path would be invalid when imported elsewhere.\"); return null; }\n\t\t\n\t\treturn f;\n\t}\n\t\n\tpublic static FileNode FindFile(FileNode fRelativeTo, string metaValue, FNFind kind) {\n\t\tvar f = fRelativeTo.FindRelative(true, metaValue, kind);\n\t\tif (f != null) {\n\t\t\tint v = filesystem.exists(f.FilePath, true);\n\t\t\tif (v != (f.IsFolder ? 2 : 1)) return null;\n\t\t}\n\t\treturn f;\n\t}\n\t\n\tMCFileAndString _GetFileAndString(string s, FNFind kind) {\n\t\ts = SplitArgs_(s, out var s2);\n\t\t\n\t\t//rejected\n\t\t//if (orFullPathAnywhere && pathname.isFullPathExpand(ref s)) {\n\t\t//\t//rejected: support folders or wildcard. Users can add a link to the folder to the workspace, it is supported.\n\t\t//\tif (!filesystem.exists(s).File) { _ErrorV(\"file does not exist: \" + s); s = null; }\n\t\t//\treturn new(null, s2, s);\n\t\t//}\n\t\t\n\t\treturn new(_GetFile(s, kind), s2);\n\t}\n\t\n\tinternal static string SplitArgs_(string s, out string s2) {\n\t\tint i = s.Find(\" /\");\n\t\tif (i > 0) {\n\t\t\ts2 = s[(i + 2)..].NullIfEmpty_();\n\t\t\ts = s[..i];\n\t\t} else s2 = null;\n\t\treturn s;\n\t}\n\t\n\tstring _GetOutPath(string s) {\n\t\ts = s.TrimEnd('\\\\');\n\t\tif (!pathname.isFullPathExpand(ref s)) {\n\t\t\tif (s.Starts('%')) _ErrorV(\"relative path starts with %\");\n\t\t\tif (s.Starts('\\\\')) s = _f.f.Model.FilesDirectory + s;\n\t\t\telse s = pathname.getDirectory(_f.f.FilePath, true) + s;\n\t\t}\n\t\treturn pathname.Normalize_(s, noExpandEV: true);\n\t}\n\t\n\tbool _CodeFilesContains(FileNode f) {\n\t\t//return CodeFiles.Exists(o => o.f == f); //garbage\n\t\tvar a = CodeFiles;\n\t\tfor (int i = a.Count; --i >= 0;) if (a[i].f == f) return true;\n\t\treturn false;\n\t}\n\t\n\t#endregion\n\t\n\tbool _PR(ref string value) {\n\t\tif (_GetFile(value, FNFind.Class) is not FileNode f) return false;\n\t\tif (f.FindProject(out var projFolder, out var projMain)) f = projMain;\n\t\tif (f == MainFile.f) return _ErrorV(\"circular reference\");\n\t\tif (ProjectReferences is { } pr) foreach (var v in pr) if (v.f == f) return false;\n\t\tMetaComments m = null;\n\t\tif (_flags.Has(MCFlags.Export)) {\n\t\t\t//don't need MetaComments and recursion. The export function will do it.\n\t\t\t//m = new MetaComments(_flags | MCFlags.IsPR);\n\t\t\t//if (!m.Parse(f, projFolder)) return false;\n\t\t} else if (!_flags.Has(MCFlags.ForCodeInfo)) {\n\t\t\tif (!Compiler.Compile(CCReason.CompileIfNeed, out var r, f, projFolder, needMeta: true, addMetaFlags: (_flags & MCFlags.Publish) | MCFlags.IsPR))\n\t\t\t\treturn _ErrorV(\"failed to compile library\");\n\t\t\t//print.it(r.role, r.file);\n\t\t\tif (r.role != MCRole.classLibrary) return _ErrorV(\"it is not a class library (no meta role classLibrary)\");\n\t\t\tvalue = r.file;\n\t\t\tm = r.meta;\n\t\t}\n\t\t(ProjectReferences ??= new()).Add((f, m));\n\t\treturn true;\n\t}\n\t\n\tvoid _NuGet(string value, string alias) {\n\t\tvalue = value.Replace('/', '\\\\');\n\t\tforeach (var v in NugetPackages) if (v.package.Eqi(value)) return;\n\t\tNugetPackages.Add((value, alias));\n\t\tif (_flags.Has(MCFlags.Publish) && !_flags.Has(MCFlags.IsPR)) return;\n\t\ttry {\n\t\t\t_xnuget ??= XmlUtil.LoadElemIfExists(App.Model.NugetDirectoryBS + \"nuget.xml\");\n\t\t\tvar xx = _xnuget?.Elem(\"package\", \"path\", value, true);\n\t\t\tif (xx == null) {\n\t\t\t\tbool forCiErrors = _flags.Has(MCFlags.ForCodeInfoInEditor);\n\t\t\t\tvar b = new StringBuilder(forCiErrors ? \"<>\" : null);\n\t\t\t\tb.Append(\"NuGet package not installed: \").Append(value);\n\t\t\t\t//append \"install\" link etc\n\t\t\t\tif (value.Split('\\\\', 2) is var a && a.Length == 2 && a[0].Length > 0 && a[1].Length > 0) {\n\t\t\t\t\tvar sep = forCiErrors ? \"\\r\\n\" : \"\\r\\n\\t\";\n\t\t\t\t\tb.Append(sep);\n\t\t\t\t\t//is installed in another folder?\n\t\t\t\t\tbool appended = false;\n\t\t\t\t\tif (_xnuget != null) {\n\t\t\t\t\t\tvar s1 = \"\\\\\" + a[1];\n\t\t\t\t\t\tforeach (var v in _xnuget.Elements(\"package\")) {\n\t\t\t\t\t\t\tif (v.Attr(\"path\") is string s2 && s2.Ends(s1, true)) {\n\t\t\t\t\t\t\t\ts2 = s2[..^s1.Length];\n\t\t\t\t\t\t\t\tb.Append(!appended ? $\"Replace code `nuget {value}` with\" : $\" or\").Append($\" `nuget {s2}{s1}`\");\n\t\t\t\t\t\t\t\tappended = true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (appended) b.Append(sep).Append($\"Or <+nuget {value}>install or move<> {a[1]} to folder {a[0]}.\");\n\t\t\t\t\t}\n\t\t\t\t\tif (!appended) b.Append($\"<+nuget {value}>Install {a[1]}...<>\");\n\t\t\t\t}\n\t\t\t\t_ErrorV(b.ToString());\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tvar dir = App.Model.NugetDirectoryBS + pathname.getDirectory(value);\n\t\t\tforeach (var x in xx.Elements()) {\n\t\t\t\tif (x.Name.LocalName is not (\"r\" or \"ro\")) continue;\n\t\t\t\tvar r = dir + x.Value;\n\t\t\t\tif (!References.Resolve(r, alias, isCOM: false, isNuget: true)) {\n\t\t\t\t\t_ErrorV(\"NuGet file not found: \" + r);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\t_ErrorV(\"exception: \" + e.Message);\n\t\t}\n\t}\n\t\n\tbool _FinalCheckOptions() {\n\t\t//const MCSpecified c_spec1 = MCSpecified.ifRunning | MCSpecified.uac | MCSpecified.platform | MCSpecified.manifest | MCSpecified.icon | MCSpecified.console | MCSpecified.startFaster;\n\t\t//const string c_spec1S = \"cannot use: ifRunning, uac, manifest, icon, console, platform, startFaster\";\n\t\tconst MCSpecified c_spec1 = MCSpecified.ifRunning | MCSpecified.uac | MCSpecified.platform | MCSpecified.manifest | MCSpecified.icon | MCSpecified.console;\n\t\tconst string c_spec1S = \"cannot use: ifRunning, uac, manifest, icon, console, platform\";\n\t\t\n\t\tbool needOP = false;\n\t\tvar role = UnchangedRole;\n\t\tswitch (role) {\n\t\tcase MCRole.miniProgram:\n\t\t\tif (Specified.HasAny(MCSpecified.outputPath | MCSpecified.manifest | MCSpecified.platform | MCSpecified.xmlDoc))\n\t\t\t\treturn _ErrorM(\"with role miniProgram cannot use: outputPath, manifest, platform, xmlDoc\");\n\t\t\t//rejected: allow platform x64 on ARM64 OS. It's easy to implement (we have the ARM task exe), but rarely used etc. Let use role exeProgram.\n\t\t\tbreak;\n\t\tcase MCRole.exeProgram:\n\t\t\t//if (Specified.Has(MCSpecified.startFaster)) return _ErrorM(\"with role exeProgram cannot use: startFaster\");\n\t\t\tneedOP = true;\n\t\t\tbreak;\n\t\tcase MCRole.editorExtension:\n\t\t\tif (Specified.HasAny(c_spec1 | MCSpecified.outputPath | MCSpecified.xmlDoc))\n\t\t\t\treturn _ErrorM($\"with role editorExtension {c_spec1S}, outputPath, xmlDoc\");\n\t\t\tbreak;\n\t\tcase MCRole.classLibrary:\n\t\t\tif (Specified.HasAny(c_spec1)) return _ErrorM(\"with role classLibrary \" + c_spec1S);\n\t\t\tneedOP = true;\n\t\t\tbreak;\n\t\tcase MCRole.classFile:\n\t\t\tif (Specified != 0) return _ErrorM(\"with role classFile (default role of class files) can be used only c, com, nuget, r, resource, file\");\n\t\t\tbreak;\n\t\t}\n\t\tif (needOP && !_flags.Has(MCFlags.WpfPreview)) OutputPath ??= GetDefaultOutputPath(_f.f, role, withEnvVar: false);\n\t\t\n\t\tif (IconFile?.IsFolder ?? false) if (role != MCRole.exeProgram) return _ErrorM(\"icon folder can be used only with role exeProgram\"); //difficult to add multiple icons if miniProgram\n\t\t\n\t\t//if(ResFile != null) {\n\t\t//\tif(IconFile != null) return _ErrorM(\"cannot add both res file and icon\");\n\t\t//\tif(ManifestFile != null) return _ErrorM(\"cannot add both res file and manifest\");\n\t\t//}\n\t\t\n\t\treturn true;\n\t}\n\t\n\tpublic static string GetDefaultOutputPath(FileNode f, MCRole role, bool withEnvVar) {\n\t\tDebug.Assert(role is MCRole.exeProgram or MCRole.classLibrary or MCRole.miniProgram);\n\t\tstring r;\n\t\tif (role == MCRole.classLibrary) r = withEnvVar ? @\"%folders.Workspace%\\dll\" : App.Model.DllDirectory;\n\t\telse r = (withEnvVar ? @\"%folders.Workspace%\\exe\\\" : App.Model.WorkspaceDirectory + @\"\\exe\\\") + f.DisplayName;\n\t\treturn r;\n\t}\n\t\n\tpublic CSharpCompilationOptions CreateCompilationOptions() {\n\t\tOutputKind oKind = OutputKind.WindowsApplication;\n\t\tif (Role == MCRole.classLibrary || Role == MCRole.classFile) oKind = OutputKind.DynamicallyLinkedLibrary;\n\t\telse if (Console) oKind = OutputKind.ConsoleApplication;\n\t\t\n\t\tvar r = new CSharpCompilationOptions(\n\t\t\toKind,\n\t\t\toptimizationLevel: Optimize ? OptimizationLevel.Release : OptimizationLevel.Debug, //speed: compile the same, load Release slightly slower. Default Debug.\n\t\t\tallowUnsafe: true,\n\t\t\twarningLevel: WarningLevel,\n\t\t\tspecificDiagnosticOptions: NoWarnings?.Select(wa => new KeyValuePair<string, ReportDiagnostic>(wa[0].IsAsciiDigit() ? (\"CS\" + wa.PadLeft(4, '0')) : wa, ReportDiagnostic.Suppress)),\n\t\t\tcryptoKeyFile: SignFile?.FilePath, //also need strongNameProvider\n\t\t\tstrongNameProvider: SignFile == null ? null : new DesktopStrongNameProvider(),\n\t\t\tnullableContextOptions: Nullable\n\t\t//,metadataImportOptions: TestInternal != null ? MetadataImportOptions.Internal : MetadataImportOptions.Public\n\t\t);\n\t\t\n\t\t//Allow to use internal/protected of assemblies specified using IgnoresAccessChecksToAttribute.\n\t\t//https://www.strathweb.com/2018/10/no-internalvisibleto-no-problem-bypassing-c-visibility-rules-with-roslyn/\n\t\t//This code (the above and below commented code) is for compiler. Also Compiler._AddAttributes adds attribute for run time.\n\t\t//if (TestInternal != null) {\n\t\t//\tr = r.WithTopLevelBinderFlags(BinderFlags.IgnoreAccessibility);\n\t\t//}\n\t\t//But if using this code, code info has problems. Completion list contains internal/protected from all assemblies, and difficult to filter out. No signature info.\n\t\t//We instead modify Roslyn code and use class TestInternal. More info in project CompilerDlls here.\n\t\t\n\t\t//r = r.WithTopLevelBinderFlags(BinderFlags.SemanticModel); //should be used in editor? Tested a bit, it seems works the same.\n\t\t\n\t\treturn r;\n\t}\n\t\n\tpublic CSharpParseOptions CreateParseOptions() {\n\t\tvar docMode = DocumentationMode.None;\n\t\tif (_flags.Has(MCFlags.ForFindReferences)) docMode = DocumentationMode.Parse;\n\t\telse if (_flags.Has(MCFlags.ForCodeInfoInEditor) || XmlDoc) docMode = DocumentationMode.Diagnose;\n\t\t\n\t\treturn new(LanguageVersion.Preview, docMode, SourceCodeKind.Regular, Defines);\n\t}\n\t\n\t/// <summary>\n\t/// Returns (start, end) of metacomments \"/*/ ... /*/\" at the start of code (before can be comments, empty lines, spaces, tabs, shebang). Returns default if no metacomments.\n\t/// </summary>\n\t/// <seealso cref=\"MetaCommentsParser\"/>\n\tpublic static StartEnd FindMetaComments(RStr code) {\n\t\tint i = 0;\n\t\tif (code is ['#', '!', ..]) {\n\t\t\ti = code.IndexOf(i, '\\n') + 1; if (i == 0) return default;\n\t\t}\n\t\tfor (; i <= code.Length - 6; i++) {\n\t\t\tchar c = code[i];\n\t\t\tif (c == '/') {\n\t\t\t\tc = code[++i];\n\t\t\t\tif (c == '*') {\n\t\t\t\t\tint j = code.IndexOf(++i, \"*/\");\n\t\t\t\t\tif (j < 0) break;\n\t\t\t\t\tif (code[i] == '/' && code[j - 1] == '/') return new(i - 2, j + 2);\n\t\t\t\t\ti = j + 1;\n\t\t\t\t} else if (c == '/') {\n\t\t\t\t\ti = code.IndexOf(i, '\\n');\n\t\t\t\t\tif (i < 0) break;\n\t\t\t\t} else break;\n\t\t\t} else if (c is not ('\\r' or '\\n' or ' ' or '\\t')) break;\n\t\t}\n\t\treturn default;\n\t}\n\t\n\t/// <summary>\n\t/// Parses metacomments and returns offsets of all option names and values in code.\n\t/// </summary>\n\t/// <param name=\"code\">Code that starts with metacomments \"/*/ ... /*/\".</param>\n\t/// <param name=\"meta\">The range of metacomments, returned by <see cref=\"FindMetaComments\"/>.</param>\n\t/// <seealso cref=\"MetaCommentsParser\"/>\n\t[MethodImpl(MethodImplOptions.AggressiveOptimization)]\n\tpublic static IEnumerable<Token> EnumOptions(string code, StartEnd meta) {\n\t\tfor (int i = meta.start + 3, iEnd = meta.end - 3; i < iEnd; i++) {\n\t\t\tToken t = default;\n\t\t\tfor (; i < iEnd; i++) if (code[i] > ' ') break; //find next option\n\t\t\tif (i == iEnd) break;\n\t\t\tt.nameStart = i;\n\t\t\tif (i < iEnd && code[i] is '/') {\n\t\t\t\tt.nameEnd = i;\n\t\t\t} else {\n\t\t\t\twhile (i < iEnd && code[i] is > ' ' and not ';') i++; //find separator after name\n\t\t\t\tt.nameEnd = i;\n\t\t\t\twhile (i < iEnd && code[i] is ' ' or '\\t') i++; //find value\n\t\t\t}\n\t\t\tt.valueStart = i;\n\t\t\ti = code.AsSpan(i, iEnd - i).IndexOfAny(';', '\\n'); if (i < 0) i = iEnd; else i += t.valueStart; //find ; or newline after value\n\t\t\tint j = i; while (j > t.valueStart && code[j - 1] <= ' ') j--; //rtrim\n\t\t\tt.valueEnd = j;\n\t\t\tt.code = code;\n\t\t\tyield return t;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// <see cref=\"EnumOptions\"/>.\n\t/// </summary>\n\tpublic struct Token {\n\t\tpublic int nameStart, nameEnd, valueStart, valueEnd;\n\t\tpublic string code;\n\t\t\n\t\tpublic string Name => nameEnd > nameStart ? code[nameStart..nameEnd] : null;\n\t\tpublic string Value => code[valueStart..valueEnd];\n\t\tpublic bool NameIs(string s) => code.Eq(nameStart..nameEnd, s);\n\t\tpublic bool ValueIs(string s) => code.Eq(valueStart..valueEnd, s);\n\t\tpublic bool IsDisabled => nameEnd == nameStart;\n\t}\n}\n\n/// <param name=\"f\"></param>\n/// <param name=\"code\"></param>\n/// <param name=\"isMain\"></param>\n/// <param name=\"isC\">Added through meta 'c' or \"global.cs\".</param>\nrecord class MCCodeFile(FileNode f, string code, bool isMain, bool isC) {\n\tinternal bool allowAnyMeta_;\n\tpublic override string ToString() => f.ToString();\n}\n\nrecord struct MCFileAndString(FileNode f, string s);\n\nenum MCRole { miniProgram, exeProgram, editorExtension, classLibrary, classFile }\n\nenum MCUac { inherit, user, admin }\n\nenum MCIfRunning { warn_restart, warn, cancel_restart, cancel, wait_restart, wait, run_restart, run, restart, end, end_restart, _norestartFlag = 1 }\n\nenum MCPlatform { Default, x64, arm64, x86 }\n\n/// <summary>\n/// Flags for <see cref=\"MetaComments\"/>\n/// </summary>\n[Flags]\nenum MCFlags {\n\t/// <summary>\n\t/// Used for code info, not when compiling.\n\t/// Ignores meta such as run options (ifRunning etc) and non-code/reference files (resource etc).\n\t/// This flag is included in <b>ForCodeInfoInEditor</b> and <b>ForFindReferences</b>.\n\t/// </summary>\n\tForCodeInfo = 1,\n\t\n\t/// <summary>\n\t/// Used for code info in editor.\n\t/// Includes <b>ForCodeInfo</b>.\n\t/// Same as <b>ForCodeInfo</b>; also adds some editor-specific stuff, like CodeInfo._diag.AddMetaError and DocumentationMode.Diagnose.\n\t/// </summary>\n\tForCodeInfoInEditor = 2 | 1,\n\t\n\t/// <summary>\n\t/// Used by <see cref=\"CiProjects.GetSolutionForFindReferences\"/>.\n\t/// Includes <b>ForCodeInfo</b>.\n\t/// </summary>\n\tForFindReferences = 4 | 1,\n\t\n\t/// <summary>\n\t/// Call <see cref=\"ErrBuilder.PrintAll\"/>.\n\t/// </summary>\n\tPrintErrors = 8,\n\t\n\t/// <summary>\n\t/// Need only references (r, pr, com, nuget) and file.\n\t/// </summary>\n\tOnlyRef = 16,\n\t\n\t/// <summary>\n\t/// Compiling for WPF preview.\n\t/// Defines WPF_PREVIEW and resets some meta.\n\t/// </summary>\n\tWpfPreview = 32,\n\t\n\t/// <summary>\n\t/// Used via meta pr of another project.\n\t/// </summary>\n\tIsPR = 64,\n\t\n\t/// <summary>\n\t/// Used for Publish.\n\t/// </summary>\n\tPublish = 128,\n\t\n\t/// <summary>\n\t/// When exporting.\n\t/// Note: does not include <b>ForCodeInfo</b>.\n\t/// </summary>\n\tExport = 0x10000000,\n\t\n\t/// <summary>\n\t/// Export pr too. Use with <b>ForExport</b>.\n\t/// </summary>\n\tExportPR = 0x20000000,\n\t\n\t/// <summary>\n\t/// Don't export global.cs. Use with <b>ForExport</b>.\n\t/// </summary>\n\tExportNoGlobal = 0x40000000,\n}\n\n[Flags]\nenum MCSpecified {\n\tifRunning = 1,\n\tuac = 1 << 1,\n\tplatform = 1 << 2,\n\toptimize = 1 << 3,\n\tdefine = 1 << 4,\n\twarningLevel = 1 << 5,\n\tnoWarnings = 1 << 6,\n\ttestInternal = 1 << 7,\n\tpreBuild = 1 << 8,\n\tpostBuild = 1 << 9,\n\toutputPath = 1 << 10,\n\trole = 1 << 11,\n\ticon = 1 << 12,\n\tmanifest = 1 << 13,\n\tsign = 1 << 14,\n\txmlDoc = 1 << 15,\n\tconsole = 1 << 16,\n\tnullable = 1 << 17,\n\tstartFaster = 1 << 18,\n}\n"
  },
  {
    "path": "Au.Editor/Compiler/MetaReferences.cs",
    "content": "extern alias CAW;\n\nusing System.Collections.Immutable;\n\nusing Microsoft.CodeAnalysis;\nusing CAW::Microsoft.CodeAnalysis;\n\nnamespace LA;\n\n/// <summary>\n/// Resolves assembly metadata references (string to PortableExecutableReference).\n/// For each compilation create a MetaReferences variable, call Resolve if need non-default references, then use Refs list.\n/// </summary>\n/// <remarks>\n/// Temporarily keeps PortableExecutableReference objects in a cache. Except Au and .NET design-time assemblies (they are small, without code).\n/// Single static cache is used by all MetaReferences variables.\n/// Cache may require many MB of unmanaged memory, therefore PortableExecutableReference objects are removed and GC-collected when not used anywhere for some time. Reloading is quite fast.\n/// \tA reference to a PortableExecutableReference variable prevents removing it from cache and GC-collecting.\n/// \tA reference to a MetaReferences variable prevents removing/disposing its Refs items.\n/// \tCodeAnalysis Project etc objects also have references, therefore need to manage their lifetimes too.\n/// MetaReferences variables can be created in different threads, but a variable must be used in a single thread.\n/// </remarks>\nclass MetaReferences {\n\t/// <summary>\n\t/// Contains info used to load a PortableExecutableReference. Loads when need. Supports XML documentation.\n\t/// </summary>\n\tclass _MR {\n\t\tpublic readonly string name, path;\n\t\tpublic readonly bool isNuget;\n\t\tpublic bool ignoreThis, ignoreDef;\n\t\treadonly WeakReference<PortableExecutableReference> _wr = new(null);\n\t\tPortableExecutableReference _refKeeper;\n\t\tlong _rkTimeout;\n\t\tlong _fileTime;\n\t\t\n\t\tpublic _MR(string name, string path, bool isNuget) {\n\t\t\t//print.it(name);\n\t\t\tthis.name = name;\n\t\t\tthis.path = path;\n\t\t\tthis.isNuget = isNuget;\n\t\t}\n\t\t\n\t\tpublic PortableExecutableReference Ref {\n\t\t\tget {\n\t\t\t\tif (!_wr.TryGetTarget(out var r)) {\n\t\t\t\t\t//for(int i=0;i<10;i++) //tested: process memory does not grow when loading same file several times\n\t\t\t\t\t//perf.first();\n\t\t\t\t\tr = MetadataReference.CreateFromFile(path, (this as _MR2)?.Prop ?? default, _DocumentationProvider.Create(path));\n\t\t\t\t\tif (isNuget) s_attach.AddOrUpdate(r, \"\");\n\t\t\t\t\tfilesystem.GetTime_(path, out _fileTime);\n\t\t\t\t\t//perf.nw();\n\t\t\t\t\t\n\t\t\t\t\t_wr.SetTarget(r);\n\t\t\t\t\t//print.it(\"LOADED\", name, this is _MR2);\n\t\t\t\t} //else print.it(\"cached\", name, this is _MR2);\n\t\t\t\t\n\t\t\t\t//prevent GC too early, eg while compiling many files\n\t\t\t\t_rkTimeout = Environment.TickCount64 + 30_000;\n\t\t\t\t_refKeeper = r;\n\t\t\t\t\n\t\t\t\treturn r;\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic void UncacheIfNeed(long timeNow) {\n\t\t\tif (_fileTime != 0) {\n\t\t\t\tfilesystem.GetTime_(path, out var t);\n\t\t\t\tif (t != _fileTime) {\n\t\t\t\t\t_wr.SetTarget(null);\n\t\t\t\t\t_refKeeper = null;\n\t\t\t\t\t_fileTime = 0;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (_refKeeper != null && timeNow > _rkTimeout) _refKeeper = null;\n\t\t}\n\t\t\n#if DEBUG\n\t\tpublic bool IsCached => _wr.TryGetTarget(out _);\n#endif\n\t}\n\t\n\tclass _MR2 : _MR {\n\t\treadonly string _alias;\n\t\treadonly bool _isCOM;\n\t\t\n\t\tpublic _MR2(string name, string path, string alias, bool isCOM, bool isNuget) : base(name, path, isNuget) {\n\t\t\t_alias = alias;\n\t\t\t_isCOM = isCOM;\n\t\t}\n\t\t\n\t\tpublic MetadataReferenceProperties Prop => new(aliases: _alias == null ? default : ImmutableArray.Create(_alias), embedInteropTypes: _isCOM);\n\t\t\n\t\tpublic bool PropEq(string alias, bool isCOM) => isCOM == _isCOM && alias == _alias;\n\t}\n\t\n\tstatic readonly List<_MR> s_cache = new();\n\t\n\tpublic static bool IsNuget(PortableExecutableReference r) => s_attach.TryGetValue(r, out _);\n\tstatic ConditionalWeakTable<PortableExecutableReference, string> s_attach = new();\n\t\n\t/// <summary>\n\t/// List containing most <see cref=\"DefaultReferences\"/> + references for which was called <see cref=\"Resolve\"/> of this MetaReferences variable.\n\t/// </summary>\n\tpublic IReadOnlyList<PortableExecutableReference> Refs => _refs;\n\treadonly List<PortableExecutableReference> _refs;\n\t\n\t/// <summary>\n\t/// These references are added when compiling any script/library.\n\t/// Au.dll and all .NET design-time assemblies.\n\t/// </summary>\n\tpublic static readonly Dictionary<string, PortableExecutableReference> DefaultReferences = new(300, StringComparer.OrdinalIgnoreCase);\n\t\n\t/// <summary>\n\t/// Returns true if <i>name</i> is in <b>DefaultReferences</b> or is a private .NET assembly. Case-insensitive.\n\t/// </summary>\n\tpublic static bool IsDefaultRef(string name) => DefaultReferences.ContainsKey(name) || name.Eqi(\"System.Private.CoreLib\");\n\t\n\tstatic MetaReferences() {\n\t\t//var p1 = perf.local();\n\t\ts_netDocProvider = new _NetDocumentationProvider();\n\t\t//p1.Next('d');\n\t\t\n\t\tusing var db = EdDatabases.OpenRef();\n\t\tusing var stat = db.Statement(\"SELECT * FROM ref\");\n\t\twhile (stat.Step()) {\n\t\t\tvar asmName = stat.GetText(0);\n\t\t\tvar doc = s_netDocProvider.HaveRef(asmName) ? s_netDocProvider : null;\n\t\t\tvar r = MetadataReference.CreateFromImage(stat.GetArray<byte>(1), documentation: doc, filePath: asmName);\n\t\t\tDefaultReferences.Add(asmName, r);\n\t\t}\n\t\t//p1.Next('c');\n\t\t\n\t\tvar auPath = folders.ThisAppBS + \"Au.dll\";\n\t\tDefaultReferences.Add(\"Au\", MetadataReference.CreateFromFile(auPath, documentation: _DocumentationProvider.Create(auPath)));\n\t\t//p1.NW('a');\n\t\t\n\t\tApp.Timer1sWhenVisible += UncacheOldFiles;\n\t}\n\t\n\tpublic static void UncacheOldFiles() {\n\t\t//using var p1 = perf.local();\n\t\tlock (s_cache) {\n\t\t\tlong timeNow = Environment.TickCount64;\n\t\t\tforeach (var v in s_cache) v.UncacheIfNeed(timeNow);\n\t\t}\n\t}\n\t\n\tpublic MetaReferences() {\n\t\tvar def = DefaultReferences;\n\t\t_refs = new List<PortableExecutableReference>(def.Count + 10);\n\t\tforeach (var v in def) _refs.Add(v.Value);\n\t\t\n\t\tDefaultRefCount = _refs.Count;\n\t}\n\t\n\t//public MetaReferences(FileNode fn) : this() {\n\t//\t_test = fn.Name == \"test.cs\";\n\t//}\n\t//bool _test;\n\t\n\t/// <summary>\n\t/// Splits <i>s</i> if it is like <c>\"X /properties\"</c>.\n\t/// Called for meta r, com, pr and nuget.\n\t/// </summary>\n\t/// <param name=\"s1\">Receives <i>s</i> or its part before <c>\" /\"</c>.</param>\n\t/// <param name=\"alias\">Receives ALIAS if <i>s</i> is like <c>\"X /alias=ALIAS\"</c>, else <c>null</c>.</param>\n\t/// <param name=\"noCopy\"><i>s</i> is like <c>\"X.dll /noCopy\"</c></param>\n\t/// <returns>false if invalid \"/string\".</returns>\n\tinternal static bool ParseRefAliasEtc_(string name, string s, out string s1, out string alias, out bool noCopy) {\n\t\talias = null;\n\t\tnoCopy = false;\n\t\ts1 = MetaComments.SplitArgs_(s, out var s2);\n\t\tif (s2 != null) {\n\t\t\tforeach (var v in s2.Split('|')) {\n\t\t\t\tif (v.Starts(\"alias=\")) {\n\t\t\t\t\tif (name[0] == 'p') return false; //could support for pr too, but who will use it\n\t\t\t\t\talias = v[6..].NullIfEmpty_();\n\t\t\t\t} else if (v == \"noCopy\") {\n\t\t\t\t\tif (!(name[0] is 'r')) return false; //could support for pr and com too, but who will use it\n\t\t\t\t\tnoCopy = true;\n\t\t\t\t} else return false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\t\n\t/// <summary>\n\t/// Finds reference assembly file, creates PortableExecutableReference and adds to the cache.\n\t/// Returns false if file not found.\n\t/// </summary>\n\t/// <param name=\"reference\">\n\t/// Assembly filename (like \"My.dll\" or \"My\"), relative path or full path.\n\t/// If not full path, must be in <b>folders.ThisApp</b>; if <i>isCOM</i> - in App.Model.WorkspaceDirectory + @\"\\.interop\\\".\n\t/// </param>\n\t/// <param name=\"alias\">Alias or null.</param>\n\t/// <exception cref=\"Exception\">Eg failed to load the found file.</exception>\n\t/// <remarks>\n\t/// Loads the file but does not parse. If bad format, error later when compiling.\n\t/// </remarks>\n\tpublic bool Resolve(string reference, string alias, bool isCOM, bool isNuget, bool noCopy = false) {\n\t\tif (reference.Length == 0) return false;\n\t\t\n\t\tvar cache = s_cache;\n\t\tlock (cache) {\n\t\t\tint i;\n\t\t\tfor (i = 0; i < cache.Count; i++) {\n\t\t\t\tif (cache[i].name.Eqi(reference) && _PropEq(cache[i], alias, isCOM)) goto g1;\n\t\t\t}\n\t\t\t\n\t\t\tbool isFull = pathname.isFullPathExpand(reference, out string path);\n\t\t\tif (!isFull && isCOM) { isFull = true; path = App.Model.WorkspaceDirectory + @\"\\.interop\\\" + path; }\n\t\t\tif (!isFull) path = folders.ThisAppBS + path;\n\t\t\tpath = pathname.Normalize_(path);\n\t\t\tif (!filesystem.exists(path, useRawPath: true).File) return false;\n\t\t\t//note: we don't use Microsoft.CodeAnalysis.Scripting.ScriptMetadataResolver. It is very slow, makes compiling many times slower.\n\t\t\t\n\t\t\tfor (i = 0; i < cache.Count; i++) {\n\t\t\t\tif (cache[i].path.Eqi(path) && _PropEq(cache[i], alias, isCOM)) goto g1;\n\t\t\t}\n\t\t\t\n\t\t\t_MR k;\n\t\t\tif (isCOM || alias != null) k = new _MR2(reference, path, alias, isCOM, isNuget);\n\t\t\telse k = new _MR(reference, path, isNuget);\n\t\t\t\n\t\t\tif (!isCOM && _BadDefReplacement(k)) throw new InvalidOperationException($\"Cannot use reference '{reference}'.\");\n\t\t\t\n\t\t\tcache.Add(k);\n\t\t\t\n\t\t\tg1:\n\t\t\t_AddRef(i);\n\t\t}\n\t\t\n\t\treturn true;\n\t\t\n\t\tstatic bool _PropEq(_MR u, string alias, bool isCOM) {\n\t\t\tif (u is _MR2 m2) return m2.PropEq(alias, isCOM);\n\t\t\treturn !isCOM && alias == null;\n\t\t}\n\t\t\n\t\tvoid _AddRef(int iCache) {\n\t\t\tvar k = s_cache[iCache];\n\t\t\t\n\t\t\tif (k.ignoreThis) return;\n\t\t\t\n\t\t\tvar r = k.Ref;\n\t\t\tif (_refs.Contains(r)) return;\n\t\t\t\n\t\t\tif (k.ignoreDef) {\n\t\t\t\tvar s = pathname.getNameNoExt(k.path);\n\t\t\t\tfor (int i = DefaultRefCount; --i >= 0;) {\n\t\t\t\t\tif (_refs[i].Display.Eqi(s)) {\n\t\t\t\t\t\t_refs.RemoveAt(i);\n\t\t\t\t\t\tDefaultRefCount--;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t_refs.Add(r);\n\t\t\tif (noCopy) (NoCopyRefs ??= []).Add(r);\n\t\t}\n\t\t\n\t\t//Returns true if k name matches one of default refs, unless k is any version of that ref.\n\t\t//In the later case sets k.ignoreDef=true if k is a newer version, else sets k.ignoreThis=true.\n\t\tbool _BadDefReplacement(_MR k) {\n\t\t\tvar name = pathname.getNameNoExt(k.path);\n\t\t\tif (!DefaultReferences.TryGetValue(name, out var def)) return false;\n\t\t\t\n\t\t\tif (_hsNoRef?.Contains(name) ?? false) return false;\n\t\t\t//TODO3: what if eg two nuget packages (from different folders) have two different versions of that assembly?\n\t\t\t//\tEg from _refs already removed v6 and added v8, and now trying to add v7 or v9.\n\t\t\t//\tRare.\n\t\t\t//\tNow should try to find in _refs. If not found in default area and found in nondefault area, replace the nondefault.\n\t\t\t\n\t\t\tvar idR = _Identity(k.Ref); if (idR == null) return true;\n\t\t\tvar idD = _Identity(def);\n\t\t\t//print.it(idR, idDef);\n\t\t\tif (idR.Name != idD.Name || !idR.PublicKeyToken.SequenceEqual(idD.PublicKeyToken)) return true;\n\t\t\tif (idR.Version > idD.Version) k.ignoreDef = true; else k.ignoreThis = true;\n\t\t\treturn false;\n\t\t\t\n\t\t\tstatic AssemblyIdentity _Identity(PortableExecutableReference pe) {\n\t\t\t\tif (pe.GetMetadataNoCopy() is AssemblyMetadata am && am.GetAssembly() is PEAssembly pa) return pa.Identity;\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\t\n\t\t\t//TODO3: also for k dependencies that aren't in meta\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Gets the number of default references.\n\t/// Note: it may be less than DefaultReferences.Count (some default references can be removed).\n\t/// </summary>\n\tpublic int DefaultRefCount { get; private set; }\n\t\n\t/// <summary>\n\t/// Dlls from meta <c>r dll /noCopy</c>. Don't copy these dlls to the exeProgram's output dir.\n\t/// <c>null</c> if there are no such references.\n\t/// </summary>\n\tpublic List<PortableExecutableReference> NoCopyRefs { get; private set; }\n\t\n\t/// <summary>\n\t/// Removes from <b>Refs</b> all matching a wildex. Used for meta noRef.\n\t/// </summary>\n\tinternal void RemoveFromRefs(string wildx) {\n\t\twildex x = wildx;\n\t\tfor (int i = _refs.Count; --i >= 0;) {\n\t\t\tvar r = _refs[i];\n\t\t\tif (x.Match(r.FilePath)) {\n\t\t\t\t_refs.RemoveAt(i);\n\t\t\t\tif (i < DefaultRefCount) {\n\t\t\t\t\tDefaultRefCount--;\n\t\t\t\t\t(_hsNoRef ??= new(StringComparer.OrdinalIgnoreCase)).Add(pathname.getNameNoExt(r.Display));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tHashSet<string> _hsNoRef; //names of refs removed by meta noRef\n\t\n\t/// <summary>\n\t/// Extracts path from compiler error message CS0009 and removes the reference from cache.\n\t/// </summary>\n\t/// <param name=\"errorMessage\">\"Metadata file '....dll' could not be opened ...\"</param>\n\tpublic static void RemoveBadRefFromCache(string errorMessage) {\n\t\tif (errorMessage.RxMatch(@\"'(.+?)'\", 1, out string path))\n\t\t\tlock (s_cache) { s_cache.RemoveAll(v => v.path.Eqi(path)); }\n\t}\n\t\n\t//public static void CompactCache() => _MR.CompactCache();\n\t\n#if DEBUG\n\tinternal static void DebugPrintCachedRefs() {\n\t\tforeach (var v in s_cache) if (v.IsCached) print.it(v.name);\n\t}\n#endif\n\t\n\t/// <summary>\n\t/// Gets XML documentation for an assembly.\n\t/// Uses a 2-column SQLite database auto-created from XML file by <see cref=\"Create\"/>.\n\t/// Not XML file directly because it uses much memory. Unless the file is small or DB fails.\n\t/// </summary>\n\tclass _DocumentationProvider : DocumentationProvider {\n\t\tprotected sqlite _db;\n\t\tsqliteStatement _stat;\n\t\t\n\t\t/// <summary>\n\t\t/// Creates documentation provider for assembly <i>asmPath</i>.\n\t\t/// Returns null if its xml file does not exist.\n\t\t/// Returns _DocumentationProvider if xml file is big and found or successfully created and successfully loaded database for it.\n\t\t/// Else returns _XmlFileDocumentationProvider.\n\t\t/// </summary>\n\t\tpublic static DocumentationProvider Create(string asmPath) {\n\t\t\tif (s_d.TryGetValue(asmPath, out var dp)) return dp;\n\t\t\t\n\t\t\tvar xmlPath = Path.ChangeExtension(asmPath, \"xml\");\n\t\t\tif (!filesystem.GetProp_(xmlPath, out var px)) return null;\n\t\t\t\n\t\t\tif (px.size >= 10_000) {\n\t\t\t\t_Cleanup();\n\t\t\t\tvar md5 = new Hash.MD5Context(); md5.Add(xmlPath.Lower());\n\t\t\t\tvar dbPath = folders.ThisAppTemp + $@\"refDoc\\{md5.Hash.ToString()}.db\";\n\t\t\t\ttry {\n\t\t\t\t\tif (filesystem.GetProp_(dbPath, out var pd) && pd.time == px.time) {\n\t\t\t\t\t\tDateTime now = DateTime.UtcNow, created = DateTime.FromFileTimeUtc(pd.timeCreated);\n\t\t\t\t\t\t//print.it(\"cached\", dbPath, now - created);\n\t\t\t\t\t\tif (now - created > TimeSpan.FromDays(10)) try { File.SetCreationTimeUtc(dbPath, now); } catch { } //keep cached (see _Cleanup())\n\t\t\t\t\t} else {\n\t\t\t\t\t\t//Debug_.Print($\"creating db: {asmPath}  ->  {dbPath}\");\n\t\t\t\t\t\tfilesystem.delete(dbPath);\n\t\t\t\t\t\tbool isAu = asmPath.Ends(\"\\\\Au.dll\", true);\n\t\t\t\t\t\tusing (var d = new sqlite(dbPath)) {\n\t\t\t\t\t\t\tusing var trans = d.Transaction();\n\t\t\t\t\t\t\td.Execute(\"CREATE TABLE doc (name TEXT PRIMARY KEY, xml TEXT)\");\n\t\t\t\t\t\t\tusing var statInsert = d.Statement(\"INSERT INTO doc VALUES (?, ?)\");\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tvar xr = XmlUtil.LoadElem(xmlPath);\n\t\t\t\t\t\t\tforeach (var e in xr.Descendants(\"member\")) {\n\t\t\t\t\t\t\t\tvar name = e.Attr(\"name\");\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tif (isAu) {\n\t\t\t\t\t\t\t\t\t//remove <remarks>. Users can press F1 and read online.\n\t\t\t\t\t\t\t\t\t//\tIn editor sometimes it takes too much space. Badly formatted because optimized for markdown.\n\t\t\t\t\t\t\t\t\te.Element(\"remarks\")?.Remove();\n\t\t\t\t\t\t\t\t\te.Element(\"example\")?.Remove();\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t//CONSIDER: option to show short remarks; add link if long.\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t//rejected. Too much info for \"quick info\". Can be distracting and annoying. Ok if would show icons instead. Or maybe 1-word links.\n\t\t\t\t\t\t\t\t\t//var er = e.Element(\"remarks\");\n\t\t\t\t\t\t\t\t\t//var ee = e.Element(\"example\");\n\t\t\t\t\t\t\t\t\t//if (er != null) {\n\t\t\t\t\t\t\t\t\t//\ter.Value = ee != null\n\t\t\t\t\t\t\t\t\t//\t\t? \"More info in Remarks. Example available. Click and press F1.\"\n\t\t\t\t\t\t\t\t\t//\t\t: \"More info in Remarks. Click and press F1.\";\n\t\t\t\t\t\t\t\t\t//\tee?.Remove();\n\t\t\t\t\t\t\t\t\t//} else {\n\t\t\t\t\t\t\t\t\t//\tee?.ReplaceWith(new XElement(\"remarks\", \"Example available. Click and press F1.\"));\n\t\t\t\t\t\t\t\t\t//}\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\te.Element(\"example\")?.Remove();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tusing var reader = e.CreateReader();\n\t\t\t\t\t\t\t\treader.MoveToContent();\n\t\t\t\t\t\t\t\tvar xml = reader.ReadInnerXml();\n\t\t\t\t\t\t\t\t//print.it(name, xml);\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tstatInsert.BindAll(name, xml).Step();\n\t\t\t\t\t\t\t\tstatInsert.Reset();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\ttrans.Commit();\n\t\t\t\t\t\t\td.Execute(\"VACUUM\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\ttry { File.SetLastWriteTimeUtc(dbPath, px.TimeAsDateTime); } catch { }\n\t\t\t\t\t}\n\t\t\t\t\tvar db = new sqlite(dbPath, SLFlags.SQLITE_OPEN_READONLY); //never mind: we don't dispose it on process exit\n\t\t\t\t\ts_d[asmPath] = dp = new _DocumentationProvider { _db = db };\n\t\t\t\t\treturn dp;\n\t\t\t\t}\n\t\t\t\tcatch (Exception ex) { Debug_.Print(ex); }\n\t\t\t}\n\t\t\t//return XmlDocumentationProvider.CreateFromFile(xmlPath); //no, need XML with root element.\n\t\t\treturn new _XmlFileDocumentationProvider(xmlPath);\n\t\t}\n\t\tstatic ConcurrentDictionary<string, _DocumentationProvider> s_d = new(StringComparer.OrdinalIgnoreCase);\n\t\t\n\t\tprotected internal override string GetDocumentationForSymbol(string documentationMemberID, CultureInfo preferredCulture, CancellationToken cancellationToken = default) {\n\t\t\tif (_db != null) {\n\t\t\t\tlock (_db) { //sometimes not in main thread\n\t\t\t\t\ttry {\n\t\t\t\t\t\t//print.it(documentationMemberID);\n\t\t\t\t\t\t_stat ??= _db.Statement(\"SELECT xml FROM doc WHERE name=?\");\n\t\t\t\t\t\t//if (_stat.Bind(1, documentationMemberID).Step()) return _stat.GetText(0);\n\t\t\t\t\t\tif (_stat.Bind(1, documentationMemberID).Step()) return \"<r>\" + _stat.GetText(0) + \"</r>\";\n\t\t\t\t\t\t//Roslyn bug: inheritdoc does not work with XML fragment (without single root element).\n\t\t\t\t\t\t//\tAlso then trows/catches exception every time.\n\t\t\t\t\t\t//\tBut eg XmlDocumentationProvider.CreateFromFile provider returns fragment.\n\t\t\t\t\t\t//Debug_.Print(documentationMemberID);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (SLException ex) { Debug_.Print(ex); }\n\t\t\t\t\tfinally { _stat?.Reset(); }\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\t\n\t\tpublic override bool Equals(object obj) {\n\t\t\tDebug_.PrintIf(obj != this, \"Equals\");\n\t\t\treturn obj == this;\n\t\t}\n\t\t\n\t\tpublic override int GetHashCode() {\n\t\t\tDebug_.Print(\"GetHashCode\");\n\t\t\treturn 1;\n\t\t}\n\t\t\n\t\tstatic void _Cleanup() {\n\t\t\tif (!s_cleanup) s_cleanup = true; else return;\n\t\t\tvar now = DateTime.UtcNow;\n\t\t\tvar month = TimeSpan.FromDays(30);\n\t\t\ttry {\n\t\t\t\tvar dir = folders.ThisAppTemp + \"refDoc\";\n\t\t\t\tif (!filesystem.exists(dir).Directory) { //previously the temp db files were not in refDoc (bad)\n\t\t\t\t\tfilesystem.createDirectory(dir);\n\t\t\t\t\tforeach (var f in new DirectoryInfo(folders.ThisAppTemp).GetFiles(\"*.db\")) {\n\t\t\t\t\t\tif (f.Name.Length == 35) Api.MoveFileEx(f.FullName, f.FullName.Insert(^35, @\"refDoc\\\"), 0);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tforeach (var f in new DirectoryInfo(dir).GetFiles(\"*.db\")) {\n\t\t\t\t\tif (f.Name.Length == 35 && now - f.CreationTimeUtc > month) Api.DeleteFile(f.FullName);\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch { }\n\t\t}\n\t\tstatic bool s_cleanup;\n\t}\n\t\n\t/// <summary>\n\t/// Gets XML documentation for .NET runtime assemblies.\n\t/// Uses a 2-column SQLite database created from XML files by script \"Create .NET ref and doc databases.cs\".\n\t/// Not XML files directly because it uses about 150 MB of memory.\n\t/// </summary>\n\tclass _NetDocumentationProvider : _DocumentationProvider {\n\t\tHashSet<string> _refs;\n\t\t\n\t\tpublic _NetDocumentationProvider() {\n\t\t\ttry {\n\t\t\t\t_db = EdDatabases.OpenDoc(); //never mind: we don't dispose it on process exit\n\t\t\t\tif (_db.Get(out string s, \"SELECT xml FROM doc WHERE name='.'\")) _refs = new HashSet<string>(s.Split('\\n'));\n\t\t\t}\n\t\t\tcatch (SLException ex) { Debug_.Print(ex); }\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Returns true if the database contains XML doc of the reference assembly.\n\t\t/// </summary>\n\t\t/// <param name=\"refName\">Like \"mscorlib\" or \"System.Drawing\" or \"Au\".</param>\n\t\tpublic bool HaveRef(string refName) => _refs?.Contains(refName) ?? false;\n\t}\n\tstatic readonly _NetDocumentationProvider s_netDocProvider;\n\t\n\t/// <summary>\n\t/// Gets XML documentation from XML file.\n\t/// </summary>\n\tsealed class _XmlFileDocumentationProvider : XmlDocumentationProvider {\n\t\tprivate readonly string _filePath;\n\t\t\n\t\tpublic _XmlFileDocumentationProvider(string filePath) {\n\t\t\t_filePath = filePath;\n\t\t}\n\t\t\n\t\tprotected override Stream GetSourceStream(CancellationToken cancellationToken)\n\t\t\t=> new FileStream(_filePath, FileMode.Open, FileAccess.Read);\n\t\t\n\t\tprotected override string GetDocumentationForSymbol(string documentationMemberID, CultureInfo preferredCulture, CancellationToken cancellationToken = default) {\n\t\t\tvar s = base.GetDocumentationForSymbol(documentationMemberID, preferredCulture, cancellationToken);\n\t\t\t//print.it(documentationMemberID, s);\n\t\t\tif (!s.NE()) s = \"<r>\" + s + \"</r>\";\n\t\t\treturn s;\n\t\t}\n\t\t\n\t\tpublic override bool Equals(object obj) => obj == this;\n\t\t\n\t\tpublic override int GetHashCode() => 1;\n\t} //copied from Roslyn's private FileBasedXmlDocumentationProvider (XmlDocumentationProvider.CreateFromFile returns it) and modified\n}\n"
  },
  {
    "path": "Au.Editor/Compiler/RecentTT.cs",
    "content": "namespace LA;\n\n/// <summary>\n/// Logs started/ended tasks and trigger actions for menu Run > Recent.\n/// </summary>\nstatic class RecentTT {\n\trecord _Item {\n\t\tpublic long id;\n\t\tpublic FileNode file;\n\t\tpublic int line;\n\t\tpublic int repeated;\n\t\tpublic long startTime;\n\t\tpublic long endTime;\n\t\tpublic string trigger;\n\t\tpublic bool failed;\n\t}\n\t\n\tstatic readonly List<_Item> s_a = new();\n\t\n\tpublic static void TaskEvent(bool started, RunningTask t, int exitCode = 0) {\n\t\tApi.GetSystemTimeAsFileTime(out long time);\n\t\tif (started) _Started(t.taskId, t.f, 0, time, null);\n\t\telse _Ended(t.taskId, time, exitCode < 0);\n\t}\n\t\n\tpublic static void TriggerEvent(PrintServerMessage m) {\n\t\tstring s = m.Text, z = m.Caller;\n\t\t//z format: \"id\\0sourceFilePath\\0line1based\"\n\t\t//\tid identifies that action instance in time (start/end/fail events have same id).\n\t\t\n\t\tz.ToInt(out long id, 0, out int i); i++;\n\t\t\n\t\tint j = z.IndexOf('\\0', i);\n\t\tvar fn = App.Model.FindByFilePath(z[i..j], FNFind.CodeFile);\n\t\tif (fn == null) return; //deleted item?\n\t\tint line = z.ToInt(++j);\n\t\t\n\t\tif (s[1] == 'S') _Started(id, fn, line, m.TimeUtc, s[3..]);\n\t\telse _Ended(id, m.TimeUtc, s[1] == 'F');\n\t}\n\t\n\tstatic void _Started(long id, FileNode fn, int line, long time, string trigger) {\n\t\t//remove oldest ended items\n\t\tif (s_a.Count > 250) {\n\t\t\tvar t1 = Environment.TickCount64;\n\t\t\tif (t1 - s_shrinkTime > 1000) {\n\t\t\t\ts_shrinkTime = t1;\n\t\t\t\tvar ar = new List<_Item>(); //running\n\t\t\t\tint to = s_a.Count / 4;\n\t\t\t\tfor (int i = 0; i < to; i++) if (s_a[i].endTime == 0) ar.Add(s_a[i]);\n\t\t\t\ts_a.RemoveRange(0, to);\n\t\t\t\ts_a.InsertRange(0, ar);\n\t\t\t}\n\t\t}\n\t\t\n\t\t//join multiple same items if started/ended frequently, eg an auto-repeated hotkey\n\t\t//CONSIDER: join when ends. To avoid joining failed with succeeded and running with ended.\n\t\t//CONSIDER: in main menu display only dictinct. Display multiple run instances (start/end time, failed) in submenu or tooltip.\n\t\t_Item last = null;\n\t\tfor (int i = s_a.Count; --i >= 0;) {\n\t\t\tvar v = s_a[i];\n\t\t\tif (v.endTime == 0) continue;\n\t\t\tif (time - v.endTime > 10_000_000L * 70) break; //70 s\n\t\t\tif (v.file == fn && v.line == line && v.trigger == trigger && !v.failed) {\n\t\t\t\tlast = v;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (last != null) {\n\t\t\t//print.it(\"same\");\n\t\t\tlast.id = id; last.startTime = time; last.endTime = 0; last.repeated++;\n\t\t} else {\n\t\t\ts_a.Add(new() { id = id, file = fn, line = line, startTime = time, trigger = trigger });\n\t\t}\n\t}\n\tstatic long s_shrinkTime;\n\t\n\tstatic void _Ended(long id, long time, bool failed) {\n\t\tfor (int i = s_a.Count; --i >= 0;) if (s_a[i].id == id) {\n\t\t\t\tvar k = s_a[i];\n\t\t\t\tk.endTime = time;\n\t\t\t\tk.failed = failed;\n\t\t\t\tbreak;\n\t\t\t}\n\t}\n\t\n\tpublic static void Clear() {\n\t\ts_a.Clear();\n\t}\n\t\n\tpublic static void Show() {\n\t\tif (s_a.Count == 0) return;\n\t\tbool highContrast = System.Windows.SystemParameters.HighContrast;\n\t\tvar m = new popupMenu();\n\t\tfor (int i = s_a.Count; --i >= 0;) {\n\t\t\tvar v = s_a[i];\n\t\t\tvar s = $\"{v.trigger ?? v.file.DisplayName}\\t{_Time(v.startTime)} - {_Time(v.endTime)}\";\n\t\t\tif (v.repeated > 0) s = $\"{s} ({v.repeated + 1} times)\";\n\t\t\tvar k = m.Add(s, v.trigger != null ? EdIcons.Trigger : v.file.IconString);\n\t\t\tk.Tag = v;\n\t\t\tif (highContrast) {\n\t\t\t\tif (v.failed) k.TextColor = 0xff0000;\n\t\t\t} else {\n\t\t\t\tif (v.endTime == 0) k.TextColor = 0x0000ff; else if (v.failed) k.TextColor = 0xff0000;\n\t\t\t}\n\t\t}\n\t\tm.Show();\n\t\tif (m.Result?.Tag is _Item r) App.Model.OpenAndGoTo(r.file, r.line - 1);\n\t\t\n\t\tstatic string _Time(long t) {\n\t\t\tif (t == 0) return null;\n\t\t\treturn DateTime.FromFileTimeUtc(t).ToLocalTime().ToString(\"dd H:mm:ss\");\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Compiler/Run task.cs",
    "content": "//#define TEST_STARTUP_SPEED\n\nusing Au.Controls;\n\n//CONSIDER: for ifRunning use mutex. Release mutex as soon as script ends, ie before the process ends (need some time to unload .NET).\n//\tMaybe then could repeatedly start short tasks more frequently.\n\nnamespace LA;\n\nstatic class CompileRun {\n\t/// <summary>\n\t/// Compiles and/or executes C# file or its project.\n\t/// If <paramref name=\"run\"/> is false, returns 1 if compiled, 0 if failed to compile.\n\t/// Else returns: process id if started now, 0 if failed, (int)script.RunResult_.deferred if scheduled to run later, (int)script.RunResult_.editorThread if runs in editor thread.\n\t/// </summary>\n\t/// <param name=\"run\">If true, compiles if need and executes. If false, always compiles and does not execute.</param>\n\t/// <param name=\"f\">C# file. Does nothing if null or not C# file.</param>\n\t/// <param name=\"args\">To pass to Main.</param>\n\t/// <param name=\"noDefer\">Don't schedule to run later.</param>\n\t/// <param name=\"wrPipeName\">Pipe name for script.writeResult.</param>\n\t/// <param name=\"runFromEditor\">Starting from the Run button or menu Run command. Can restart etc.</param>\n\t/// <param name=\"ifRunning\">If not null, overrides meta ifRunning.</param>\n\t/// <param name=\"debugAttach\">Will attach the debugger.</param>\n\t/// <remarks>\n\t/// Saves editor text if need.\n\t/// Calls <see cref=\"Compiler.Compile\"/>.\n\t/// Must be always called in the main UI thread, because calls its file model functions.\n\t/// </remarks>\n\tpublic static int CompileAndRun(bool run, FileNode f, string[] args = null, bool noDefer = false, string wrPipeName = null, bool runFromEditor = false, MCIfRunning? ifRunning = null, Func<int, bool> debugAttach = null) {\n#if TEST_STARTUP_SPEED\n\t\targs = new string[] { perf.ms.ToString() }; //and in script use this code: print.it(perf.ms-Convert.ToInt64(args[0]));\n#endif\n\t\t\n\t\tApp.Model.Save.TextNowIfNeed(onlyText: true);\n\t\tApp.Model.Save.WorkspaceNowIfNeed(); //because the script may kill editor, eg if runs in editor thread\n\t\t\n\t\tif (!Compile(run, ref f, out var r)) return 0;\n\t\tif (!run) return 1;\n\t\t\n\t\tif (r.role == MCRole.editorExtension) {\n\t\t\tif (debugAttach != null) { print.it(\"Cannot debug scripts with role editorExtension.\"); return 0; }\n\t\t\t\n\t\t\tEditorExtension.Run_(r.file, args, handleExceptions: true);\n\t\t\treturn (int)script.RunResult_.editorThread;\n\t\t}\n\t\t\n\t\tif (ifRunning != null) r.ifRunning = ifRunning.Value;\n\t\t\n\t\treturn App.Tasks.RunCompiled(f, r, args, noDefer, wrPipeName, runFromEditor: runFromEditor, debugAttach: debugAttach);\n\t}\n\t\n\t/// <summary>\n\t/// Gets correct file for run or compile (project-main etc), and compiles.\n\t/// </summary>\n\t/// <param name=\"forRun\"></param>\n\t/// <param name=\"f\">Input: any file or null. Output: main code file to run or compile; if <i>forRun</i>, it's an executable code file.</param>\n\t/// <param name=\"r\">Compilation results.</param>\n\t/// <returns>false if can't get correct code file or if failed to compile.</returns>\n\tpublic static bool Compile(bool forRun, ref FileNode f, out Compiler.CompResults r) {\n\t\tr = null;\n\t\tif (f == null) return false;\n\t\tif (f.FindProject(out var projFolder, out var projMain)) f = projMain;\n\t\t\n\t\t//can be set to run other script instead.\n\t\t//\tUseful for library projects. Single files have other alternatives - move to a script project or move code to a script file.\n\t\tif (forRun && f.TestScript is { } f2) {\n\t\t\tif (!f2.FindProject(out projFolder, out projMain)) f = f2;\n\t\t\telse if (projMain != f) f = projMain;\n\t\t\telse { print.it($\"<>The test script {f2.SciLink()} cannot be in the project folder {projFolder.SciLink()}\"); return false; }\n\t\t}\n\t\t\n\t\tif (!f.IsCodeFile) return false;\n\t\t\n\t\tbool ok = Compiler.Compile(forRun ? CCReason.Run : CCReason.CompileAlways, out r, f, projFolder);\n\t\t\n\t\tif (forRun && r.role is MCRole.classFile or MCRole.classLibrary) { //info: if classFile, compiler sets r.role and returns false (does not compile)\n\t\t\t_OnRunClassFile(f, projFolder);\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\treturn ok;\n\t}\n\t\n\tpublic static void RunWpfPreview(FileNode f, Func<CanCompileArgs, bool> canCompile) {\n\t\tif (f.FindProject(out var projFolder, out var projMain)) f = projMain;\n\t\t\n\t\t//If f is a class file, run it as a script.\n\t\t//\tIgnore its test script. For a library it wouldn't work. For a simple projectless class file usually don't need.\n\t\t\n\t\tbool ok = Compiler.Compile(CCReason.WpfPreview, out var r, f, projFolder, canCompile: canCompile);\n\t\tif (!ok) return;\n\t\t\n\t\tint pid = App.Tasks.RunCompiled(f, r, new string[] { \"WPF_PREVIEW\", s_wpfPreview.pid.ToS(), s_wpfPreview.time.ToS() });\n\t\tApi.GetSystemTimeAsFileTime(out long time);\n\t\ts_wpfPreview = (pid, time);\n\t}\n\tstatic (int pid, long time) s_wpfPreview;\n\t\n\tstatic void _OnRunClassFile(FileNode f, FileNode projFolder) {\n\t\tif (!s_isRegisteredLinkRCF) { s_isRegisteredLinkRCF = true; SciTags.AddCommonLinkTag(\"+runClass\", _SciLink_RunClassFile); }\n\t\tvar ids = f.IdStringWithWorkspace;\n\t\tvar s2 = projFolder != null || f.Name.Eqi(\"global.cs\") ? \"\" : $\" or project (<+runClass 2|{ids}>create<>). Or <+runClass 1|{ids}>change<> file type or role\";\n\t\tprint.it($\"<>Cannot run '{f.Name}'. It is a <help editor/Class files, projects>class file<>. Need a test script (<+runClass 3|{ids}>create<>){s2}.\");\n\t}\n\t\n\tstatic void _SciLink_RunClassFile(string s) {\n\t\tint action = s.ToInt(); //1 change role, 2 create Script project, 3 create new test script and set \"run\" attribute\n\t\tvar f = App.Model.Find(s[2..]); if (f == null) return;\n\t\tif (action == 1) { //change role\n\t\t\tif (!App.Model.SetCurrentFile(f)) return;\n\t\t\tApp.Model.Properties();\n\t\t} else {\n\t\t\tFileNode f2;\n\t\t\tif (action == 2) { //create project\n\t\t\t\tif (!_NewItem(out f2, @\"New project\\@Script\")) return;\n\t\t\t\tf.FileMove(new(f2, FNInsert.After));\n\t\t\t} else { //create test script\n\t\t\t\tif (!_NewItem(out f2, \"Script.cs\", \"test \" + f.Name)) return;\n\t\t\t\tf.TestScript = f2;\n\t\t\t}\n\t\t\t\n\t\t\t//Creates new item above f or f's project folder.\n\t\t\tbool _NewItem(out FileNode ni, string template, string name = null) {\n\t\t\t\tbool isProject = f.FindProject(out var target, out _);\n\t\t\t\tif (!isProject) target = f;\n\t\t\t\t\n\t\t\t\t//extract code from /// <example>\n\t\t\t\tstring example = null;\n\t\t\t\tif (f.GetCurrentText(out var s) && s.RxMatch(@\"(?m)^\\h*/// *<example>\\h*\\R((\\h*/// ?.*\\R)+?)^\\h*/// *</example>\", 1, out RXGroup g)) {\n\t\t\t\t\tif (s.RxMatch(@\"(?m)^\\h*/// *<code>(<!\\[CDATA\\[)?\\s+((\\h*/// ?.*\\R)+?)^\\h*/// *(?(1)\\]\\]>)</code>\", 2, out g, range: g)) {\n\t\t\t\t\t\texample = g.Value.RxReplace(@\"(?m)^\\h*/// ?\", \"\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvar text = new NewFileText();\n\t\t\t\tif (action == 2) {\n\t\t\t\t\ttext.text = example ?? \"//Class1.Function1();\\r\\n\";\n\t\t\t\t} else {\n\t\t\t\t\ttext.meta = $\"{(isProject ? \"pr\" : \"c\")} {f.ItemPath};\";\n\t\t\t\t\ttext.text = example ?? $\"//{(isProject ? \"Library.\" : \"\")}Class1.Function1();\\r\\n\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tni = App.Model.NewItem(template, new(target, FNInsert.Before), name, text: text);\n\t\t\t\treturn ni != null;\n\t\t\t}\n\t\t}\n\t}\n\tstatic bool s_isRegisteredLinkRCF;\n}\n\n/// <summary>\n/// A running script task.\n/// Starts/ends task, watches/notifies when ended.\n/// </summary>\nclass RunningTask : ITreeViewItem {\n\tvolatile WaitHandle _process;\n\tpublic readonly FileNode f;\n\tpublic readonly int taskId;\n\tpublic readonly int processId;\n\t\n\tstatic int s_taskId;\n\t\n\tpublic RunningTask(FileNode f, WaitHandle hProcess, int processId) {\n\t\ttaskId = ++s_taskId;\n\t\tthis.f = f;\n\t\t_process = hProcess;\n\t\tthis.processId = processId;\n\t\t\n\t\tRecentTT.TaskEvent(true, this);\n\t\t\n\t\tRegisteredWaitHandle rwh = null;\n\t\trwh = ThreadPool.RegisterWaitForSingleObject(_process, (context, wasSignaled) => {\n\t\t\trwh.Unregister(_process);\n\t\t\tvar p = _process; _process = null;\n\t\t\tApi.GetExitCodeProcess(p.SafeWaitHandle.DangerousGetHandle(), out int ec);\n\t\t\tp.Dispose();\n\t\t\tApp.Tasks.TaskEnded1(taskId, ec);\n\t\t}, null, -1, true);\n\t}\n\t\n\t/// <summary>\n\t/// False if task is already ended or still not started.\n\t/// </summary>\n\tpublic bool IsRunning => _process is { } p && Api.WaitForSingleObject(p.SafeWaitHandle.DangerousGetHandle(), 0) == Api.WAIT_TIMEOUT;\n\t\n\t/// <summary>\n\t/// Ends this task (kills process), if running.\n\t/// Returns false if fails, unlikely.\n\t/// </summary>\n\t/// <param name=\"onProgramExit\">Called on program exit. Returns true even if fails. Does not wait.</param>\n\tpublic bool End(bool onProgramExit) {\n\t\tif (_process is { } p) {\n\t\t\tvar h = p.SafeWaitHandle.DangerousGetHandle();\n\t\t\t\n\t\t\tint pid = process.processIdFromHandle(h);\n\t\t\tif (pid != 0) {\n\t\t\t\tif (Panels.Debug?.EndIfDebugging(pid) == true) return true;\n\t\t\t\t\n\t\t\t\t//let it call Environment.Exit. It removes tray icons etc.\n\t\t\t\tvar w1 = wnd.findFast(pid.ToS(), script.c_auxWndClassName, messageOnly: true);\n\t\t\t\tif (!w1.Is0 && w1.Post(Api.WM_CLOSE)) {\n\t\t\t\t\tif (0 == Api.WaitForSingleObject(h, 1000)) return true;\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tif (_process != null) {\n\t\t\t\tbool ok = Api.TerminateProcess(h, -1);\n\t\t\t\tif (onProgramExit) return true;\n\t\t\t\tif (ok) {\n\t\t\t\t\t//TerminateProcess is async. Usually the process ends after several ms.\n\t\t\t\t\tif (Api.WaitForSingleObject(h, 500) == Api.WAIT_TIMEOUT) Debug_.Print(\"process not terminated\");\n\t\t\t\t} else {\n\t\t\t\t\t//ERROR_ACCESS_DENIED when the process is ending\n\t\t\t\t\tvar s = lastError.message;\n\t\t\t\t\tif (Api.WaitForSingleObject(h, 500) == Api.WAIT_TIMEOUT) { Debug_.Print(s); return false; }\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t\t//TODO3: release pressed keys.\n\t}\n\t\n\t#region ITreeViewItem\n\t\n\tstring ITreeViewItem.DisplayText => f.DisplayName;\n\t\n\tobject ITreeViewItem.Image => f.Image;\n\t\n\t//TVCheck ITreeViewItem.CheckState { get; }\n\t\n\t//bool ITreeViewItem.IsDisabled { get; }\n\t\n\t//bool ITreeViewItem.IsBold { get; }\n\t\n\t//bool ITreeViewItem.IsSelectable { get; }\n\t\n\t//int ITreeViewItem.Color { get; }\n\t\n\t//int ITreeViewItem.TextColor => 0xff0000;\n\t\n\t#endregion\n}\n\n/// <summary>\n/// Manages running script tasks.\n/// </summary>\nclass RunningTasks {\n\tclass _WaitingTask {\n\t\tpublic readonly FileNode f;\n\t\tpublic readonly Compiler.CompResults r;\n\t\tpublic readonly string[] args;\n\t\t\n\t\tpublic _WaitingTask(FileNode f, Compiler.CompResults r, string[] args) {\n\t\t\tthis.f = f; this.r = r; this.args = args;\n\t\t}\n\t}\n\t\n\treadonly List<RunningTask> _a = new();\n\treadonly List<_WaitingTask> _q = new(); //not Queue because may need to remove item at any index\n\tbool _updateUI;\n\tvolatile bool _disposed;\n\t\n\t/// <summary>\n\t/// Gets running tasks.\n\t/// Order: the last started is the first in the list.\n\t/// </summary>\n\tpublic IReadOnlyList<RunningTask> Items => _a;\n\t\n\tpublic RunningTasks() {\n\t\tApp.Timer1sOr025s += _TimerUpdateUI;\n\t}\n\t\n\tpublic void OnWorkspaceClosed() {\n\t\tbool onExit = App.Loaded >= AppState.Unloading;\n\t\t\n\t\tif (onExit) {\n\t\t\t_disposed = true;\n\t\t\tApp.Timer1sOr025s -= _TimerUpdateUI;\n\t\t}\n\t\t\n\t\tfor (int i = _a.Count; --i >= 0;) {\n\t\t\t_EndTask(_a[i], onExit: onExit);\n\t\t}\n\t\t\n\t\tif (onExit) _a.Clear();\n\t\t_q.Clear();\n\t\t\n\t\tif (!onExit) _UpdatePanels();\n\t}\n\t\n\t/// <summary>\n\t/// Adds a started task to the 'running' list.\n\t/// Must be called in the main thread.\n\t/// </summary>\n\t/// <param name=\"rt\"></param>\n\tvoid _Add(RunningTask rt) {\n\t\tDebug.Assert(!_disposed);\n\t\t_a.Insert(0, rt);\n\t\t_updateUI = true;\n\t}\n\t\n\t/// <summary>\n\t/// Called in a threadpool thread when a task process exited.\n\t/// </summary>\n\t/// <param name=\"taskId\"></param>\n\tinternal void TaskEnded1(int taskId, int exitCode) {\n\t\tif (_disposed) return;\n\t\tCommandLine.MsgWnd.Post(WM_TASK_ENDED, taskId, exitCode);\n\t}\n\t\n\t/// <summary>\n\t/// When task ended, this message is posted to CommandLine.MsgWnd, with wParam=taskId.\n\t/// </summary>\n\tpublic const int WM_TASK_ENDED = Api.WM_USER + 900;\n\t\n\t/// <summary>\n\t/// Removes an ended task from the 'running' list. If a task is queued and can run, starts it.\n\t/// When task ended, TaskEnded1 posts message WM_TASK_ENDED with task id in wParam to the message window, which calls this function.\n\t/// </summary>\n\tinternal void TaskEnded2(nint wParam, nint lParam) {\n\t\tif (_disposed) return;\n\t\t\n\t\tint taskId = (int)wParam;\n\t\tint i = _Find(taskId);\n\t\tif (i < 0) { Debug_.Print(\"not found. It's OK, but should be very rare, mostly with 1-core CPU.\"); return; }\n\t\t\n\t\tRecentTT.TaskEvent(false, _a[i], (int)lParam);\n\t\t_a.RemoveAt(i);\n\t\t\n\t\tfor (int j = _q.Count; --j >= 0;) {\n\t\t\tvar t = _q[j];\n\t\t\tif (_CanRunNow(t.f, t.r, out _)) {\n\t\t\t\t_q.RemoveAt(j);\n\t\t\t\tRunCompiled(t.f, t.r, t.args, ignoreLimits: true);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\t\n\t\t_updateUI = true;\n\t}\n\t\n\tvoid _TimerUpdateUI() {\n\t\tif (!_updateUI) return;\n\t\tif (!App.Hmain.IsVisible) return;\n\t\t_UpdatePanels();\n\t}\n\t\n\tvoid _UpdatePanels() {\n\t\t_updateUI = false;\n\t\tPanels.Tasks.UpdateList();\n\t}\n\t\n\t/// <summary>\n\t/// Returns true if one or more tasks of file f are running.\n\t/// </summary>\n\t/// <param name=\"f\">Can be null.</param>\n\tpublic bool IsRunning(FileNode f) => null != _GetRunning(f);\n\t\n\tRunningTask _GetRunning(FileNode f) {\n\t\tfor (int i = 0; i < _a.Count; i++) {\n\t\t\tvar r = _a[i];\n\t\t\tif (r.f == f && r.IsRunning) return r;\n\t\t}\n\t\treturn null;\n\t}\n\t\n\t//currently not used\n\t///// <summary>\n\t///// Returns all running files.\n\t///// For files that have multiple tasks is added 1 item in the list.\n\t///// Each time creates new list; caller can modify it.\n\t///// </summary>\n\t//public List<FileNode> GetRunningFiles() {\n\t//\tvar a = new List<FileNode>(_a.Count);\n\t//\tfor(int i = 0; i < _a.Count; i++) {\n\t//\t\tvar t = _a[i];\n\t//\t\tif(!a.Contains(t.f)) a.Add(t.f);\n\t//\t}\n\t//\treturn a;\n\t//}\n\t\n\tpublic RunningTask TaskFromProcessId(int processId) {\n\t\tforeach (var v in _a) if (v.processId == processId) return v;\n\t\treturn null;\n\t}\n\t\n\t/// <summary>\n\t/// Ends all tasks of file f.\n\t/// Returns true if was running.\n\t/// </summary>\n\t/// <param name=\"f\">Can be null.</param>\n\t/// <param name=\"exceptProcessId\"></param>\n\tpublic bool EndTasksOf(FileNode f, int exceptProcessId = 0) {\n\t\tbool wasRunning = false;\n\t\tfor (int i = _a.Count; --i >= 0;) {\n\t\t\tvar r = _a[i];\n\t\t\tif (r.f != f || !r.IsRunning) continue;\n\t\t\tif (exceptProcessId != 0 && r.processId == exceptProcessId) continue;\n\t\t\t_EndTask(r);\n\t\t\twasRunning = true;\n\t\t}\n\t\treturn wasRunning;\n\t}\n\t\n\t/// <summary>\n\t/// Ends a task, if still running.\n\t/// </summary>\n\tpublic void EndTask(RunningTask rt) {\n\t\tif (_a.Contains(rt)) _EndTask(rt);\n\t}\n\t\n\tbool _EndTask(RunningTask rt, bool onExit = false) {\n\t\tDebug.Assert(_a.Contains(rt));\n\t\treturn rt.End(onExit);\n\t}\n\t\n\tbool _CanRunNow(FileNode f, Compiler.CompResults r, out RunningTask running, bool runFromEditor = false) {\n\t\trunning = null;\n\t\tif (r.ifRunning == MCIfRunning.run || (r.ifRunning == MCIfRunning.run_restart && !runFromEditor)) return true;\n\t\trunning = _GetRunning(f);\n\t\treturn running == null;\n\t}\n\t\n\t//rejected: use shared memory instead of pipe. Tested, same speed.\n\t\n\t/// <summary>\n\t/// Executes the compiled assembly in new process.\n\t/// Returns: process id if started now, 0 if failed, (int)script.RunResult_.deferred if scheduled to run later.\n\t/// </summary>\n\t/// <param name=\"f\"></param>\n\t/// <param name=\"r\"></param>\n\t/// <param name=\"args\"></param>\n\t/// <param name=\"noDefer\">Don't schedule to run later. If cannot run now, just return 0.</param>\n\t/// <param name=\"wrPipeName\">Pipe name for script.writeResult.</param>\n\t/// <param name=\"ignoreLimits\">Don't check whether the task can run now.</param>\n\t/// <param name=\"runFromEditor\">Starting from the Run button or menu Run command. Can restart etc.</param>\n\t/// <param name=\"debugAttach\">Will attach the debugger.</param>\n\tpublic unsafe int RunCompiled(FileNode f, Compiler.CompResults r, string[] args,\n\t\tbool noDefer = false, string wrPipeName = null, bool ignoreLimits = false, bool runFromEditor = false, Func<int, bool> debugAttach = null) {\n\t\t\n\t\tg1:\n\t\tif (!ignoreLimits && !_CanRunNow(f, r, out var running, runFromEditor)) {\n\t\t\tvar ifRunning = r.ifRunning;\n\t\t\tif (!ifRunning.Has(MCIfRunning._norestartFlag) && ifRunning != MCIfRunning.restart) {\n\t\t\t\tif (runFromEditor) ifRunning = MCIfRunning.restart;\n\t\t\t\telse if (ifRunning == MCIfRunning.end_restart) ifRunning = MCIfRunning.end;\n\t\t\t\telse ifRunning |= MCIfRunning._norestartFlag;\n\t\t\t}\n\t\t\t//print.it(same, ifRunning);\n\t\t\tswitch (ifRunning) {\n\t\t\tcase MCIfRunning.cancel:\n\t\t\t\tbreak;\n\t\t\tcase MCIfRunning.wait when !noDefer:\n\t\t\t\t_q.Insert(0, new _WaitingTask(f, r, args));\n\t\t\t\treturn (int)script.RunResult_.deferred;\n\t\t\tcase MCIfRunning.restart when _EndTask(running):\n\t\t\t\tgoto g1;\n\t\t\tcase MCIfRunning.end:\n\t\t\t\t_EndTask(running);\n\t\t\t\tbreak;\n\t\t\tdefault: //warn\n\t\t\t\tprint.it($\"<>Cannot start {f.SciLink()} because it is running. You may want to <+properties {f.IdStringWithWorkspace}>change<> <c green>ifRunning<>.\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\treturn 0;\n\t\t}\n\t\t\n\t\t_SpUac uac = _SpUac.normal; int preIndex = 0;\n\t\tif (!uacInfo.isUacDisabled) {\n\t\t\t//info: to completely disable UAC on Win7: gpedit.msc/Computer configuration/Windows settings/Security settings/Local policies/Security options/User Account Control:Run all administrators in Admin Approval Mode/Disabled. Reboot.\n\t\t\t//note: when UAC disabled, if our uac is System, IsUacDisabled returns false (we probably run as SYSTEM user). It's OK.\n\t\t\tvar IL = uacInfo.ofThisProcess.IntegrityLevel;\n\t\t\tif (r.uac == MCUac.inherit) {\n\t\t\t\tswitch (IL) {\n\t\t\t\tcase UacIL.High: preIndex = 1; break;\n\t\t\t\tcase UacIL.UIAccess: uac = _SpUac.uiAccess; preIndex = 2; break;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tswitch (IL) {\n\t\t\t\tcase UacIL.Medium:\n\t\t\t\tcase UacIL.UIAccess:\n\t\t\t\t\tif (r.uac == MCUac.admin) uac = _SpUac.elevate;\n\t\t\t\t\tbreak;\n\t\t\t\tcase UacIL.High:\n\t\t\t\t\tif (r.uac == MCUac.user\n\t\t\t\t\t\t&& uacInfo.ofThisProcess.Elevation == UacElevation.Full //eg on Azure VM there are no Medium IL processes (all normal processes are High), and Elevation returns Default, although UAC not turned off\n\t\t\t\t\t\t) uac = _SpUac.userFromAdmin;\n\t\t\t\t\tbreak;\n\t\t\t\tcase UacIL.Low:\n\t\t\t\tcase UacIL.Untrusted:\n\t\t\t\tcase UacIL.Unknown:\n\t\t\t\t//break;\n\t\t\t\tcase UacIL.System:\n\t\t\t\tcase UacIL.Protected:\n\t\t\t\t\tprint.it($\"<>Cannot run {f.SciLink()}. Meta comment option <c green>uac {r.uac}<> cannot be used when the UAC integrity level of this process is {IL}. Supported levels are Medium, High and uiAccess.\");\n\t\t\t\t\treturn 0;\n\t\t\t\t\t//info: cannot start Medium IL process from System process. Would need another function. Never mind.\n\t\t\t\t}\n\t\t\t\tif (r.uac == MCUac.admin) preIndex = 1;\n\t\t\t}\n\t\t}\n\t\t\n\t\tbool exeProgram = r.notInCache, usePreloaded = false, debugger = debugAttach != null;\n\t\t_Preloaded pre = null;\n\t\tstring exeFile, argsString;\n\t\t\n\t\t//rejected: 32-bit miniProgram. The task exe has been removed because of AV false positives. And rarely used. Can use exeProgram instead.\n\t\t\n\t\tif (exeProgram) { //meta role exeProgram\n\t\t\texeFile = Compiler.DllNameToExeName(r.file, default);\n\t\t\targsString = args == null ? null : StringUtil.CommandLineFromArray(args);\n\t\t} else {\n\t\t\tif (debugger) {\n\t\t\t\tif (uac == _SpUac.elevate) { print.it(\"Cannot debug this script. Remove /*/ uac admin; /*/, or run LA as admin.\"); return 0; }\n\t\t\t\t//Don't use preloaded. It would load some assemblies before 'attach debugger' and then debugger cannot disable JIT optimizations.\n\t\t\t} else if (usePreloaded = r.flags.Has(MiniProgram_.MPFlags.Preloaded) && uac != _SpUac.elevate) {\n\t\t\t\tpre = s_preloaded[preIndex] ??= new _Preloaded(preIndex);\n\t\t\t}\n\t\t\tpre ??= new _Preloaded(100); //temporary, just to create pipe with unique name and reuse preloaded launch code\n\t\t\t\n\t\t\texeFile = folders.ThisAppBS + $\"Au.Task-{(osVersion.isArm64Process ? \"arm\" : \"x64\")}.exe\";\n\t\t\targsString = pre.pipeName;\n\t\t}\n\t\t\n\t\tint pid = 0; WaitHandle hProcess = null; bool disconnectPipe = false;\n\t\ttry {\n\t\t\tbool preloadedProcessExists = false;\n\t\t\tif (usePreloaded) {\n\t\t\t\tvar pp = pre.hProcess;\n\t\t\t\tpreloadedProcessExists = pp != null && Api.WAIT_TIMEOUT == Api.WaitForSingleObject(pp.SafeWaitHandle.DangerousGetHandle(), 0);\n\t\t\t\tif (preloadedProcessExists) {\n\t\t\t\t\thProcess = pp; pid = pre.pid;\n\t\t\t\t\tpre.hProcess = null; pre.pid = 0;\n\t\t\t\t} else if (pp != null) { //preloaded process existed but somehow ended\n\t\t\t\t\tpp.Dispose();\n\t\t\t\t\tpre.hProcess = null; pre.pid = 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!preloadedProcessExists) {\n\t\t\t\t(pid, hProcess) = _StartProcess(uac, exeFile, argsString, wrPipeName, r.notInCache, runFromEditor, f.Id, debugAttach, r.flags.Has(MiniProgram_.MPFlags.RedirectConsole));\n\t\t\t\tif (pid == 0) return 0; //failed to start debugging\n\t\t\t}\n\t\t\tApi.AllowSetForegroundWindow(pid);\n\t\t\t\n\t\t\tif (!exeProgram) {\n\t\t\t\tvar f1 = r.flags;\n\t\t\t\tif (runFromEditor) f1 |= MiniProgram_.MPFlags.FromEditor;\n\t\t\t\tif (App.IsPortable) f1 |= MiniProgram_.MPFlags.IsPortable;\n\t\t\t\tif (!preloadedProcessExists) f1 &= ~MiniProgram_.MPFlags.Preloaded;\n\t\t\t\tbyte[] taskParams = Serializer_.SerializeWithSize(r.name, r.file, (int)f1, args, wrPipeName, (string)folders.Workspace, (int)f.Id, process.thisProcessId, (int)CommandLine.MsgWnd);\n\t\t\t\t\n\t\t\t\tvar o = new Api.OVERLAPPED { hEvent = pre.overlappedEvent };\n\t\t\t\tif (!Api.ConnectNamedPipe(pre.hPipe, &o)) {\n\t\t\t\t\tint e = lastError.code;\n\t\t\t\t\tif (e != Api.ERROR_PIPE_CONNECTED) {\n\t\t\t\t\t\tif (e != Api.ERROR_IO_PENDING) throw new AuException(e);\n\t\t\t\t\t\tvar ha = stackalloc IntPtr[2] { pre.overlappedEvent, hProcess.SafeWaitHandle.DangerousGetHandle() };\n\t\t\t\t\t\t//perf.shared.Next('r');\n\t\t\t\t\t\tint wr = Api.WaitForMultipleObjectsEx(2, ha, false, -1, false);\n\t\t\t\t\t\tif (wr != 0) { Api.CancelIo(pre.hPipe); throw new AuException(\"*start task. Preloaded task process ended\"); } //note: if fails when 32-bit process, rebuild solution with platform x86\n\t\t\t\t\t\tdisconnectPipe = true;\n\t\t\t\t\t\tif (!Api.GetOverlappedResult(pre.hPipe, ref o, out _, false)) throw new AuException(0);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (!Api.WriteFile2(pre.hPipe, taskParams, out _)) throw new AuException(0);\n\t\t\t\tApi.DisconnectNamedPipe(pre.hPipe); disconnectPipe = false;\n\t\t\t\t\n\t\t\t\t//start preloaded process for next task. Let it wait for pipe connection.\n\t\t\t\tif (usePreloaded) {\n\t\t\t\t\ttry { (pre.pid, pre.hProcess) = _StartProcess(uac, exeFile, argsString, null, r.notInCache, false, 0, null, false); }\n\t\t\t\t\tcatch (Exception ex) { Debug_.Print(ex); }\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tprint.it(ex);\n\t\t\tif (disconnectPipe) Api.DisconnectNamedPipe(pre.hPipe);\n\t\t\thProcess?.Dispose();\n\t\t\treturn 0;\n\t\t}\n\t\tfinally {\n\t\t\tif (!usePreloaded) pre?.Dispose();\n\t\t}\n\t\t\n\t\tvar rt = new RunningTask(f, hProcess, pid);\n\t\t_Add(rt);\n\t\treturn pid;\n\t}\n\t\n\tclass _Preloaded {\n\t\tpublic readonly string pipeName;\n\t\tpublic readonly Handle_ hPipe;\n\t\tpublic readonly Handle_ overlappedEvent;\n\t\tpublic WaitHandle hProcess;\n\t\tpublic int pid;\n\t\t\n\t\tpublic _Preloaded(int index) {\n\t\t\tpipeName = $@\"\\\\.\\pipe\\Au.Task-{Api.GetCurrentProcessId()}-{index}\";\n\t\t\thPipe = Api.CreateNamedPipe(pipeName,\n\t\t\t\tApi.PIPE_ACCESS_OUTBOUND | Api.FILE_FLAG_OVERLAPPED, //use async pipe because editor would hang if task process exited without connecting. Same speed.\n\t\t\t\tApi.PIPE_TYPE_MESSAGE | Api.PIPE_REJECT_REMOTE_CLIENTS,\n\t\t\t\t1, 0, 0, 0, null);\n\t\t\toverlappedEvent = Api.CreateEvent(false);\n\t\t}\n\t\t\n\t\tpublic void Dispose() {\n\t\t\thPipe.Dispose();\n\t\t\toverlappedEvent.Dispose();\n\t\t}\n\t}\n\t//static _Preloaded[] s_preloaded = new _Preloaded[6]; //user, admin, uiAccess, user32, admin32, uiAccess32\n\tstatic _Preloaded[] s_preloaded = new _Preloaded[3]; //user, admin, uiAccess\n\t\n\t/// <summary>\n\t/// How _StartProcess must start process.\n\t/// Note: it is not UAC IL of the process.\n\t/// </summary>\n\tenum _SpUac {\n\t\tnormal, //start process of same IL as this process, but without uiAccess. It is how CreateProcess API works.\n\t\televate, //start admin process from this user or uiAccess process\n\t\tuserFromAdmin, //start user process from this admin process\n\t\tuiAccess, //start uiAccess process from this uiAccess process\n\t}\n\t\n\t/// <summary>\n\t/// Starts task process.\n\t/// Returns (processId, processHandle). Throws if failed. Returns 0 if failed to attach debugger.\n\t/// </summary>\n\tstatic unsafe (int pid, WaitHandle hProcess) _StartProcess(_SpUac uac, string exeFile, string args, string wrPipeName, bool exeProgram, bool runFromEditor, uint idMain, Func<int, bool> debugAttach, bool redirectConsoleInExe) {\n\t\tif (s_inSP) throw new AuException(\"_StartProcess: can't reenter\"); //starting the debugger. See wait.forHandle below. Very rare, never mind.\n\t\ts_inSP = true;\n\t\ttry {\n\t\t\t(int pid, WaitHandle hProcess) r;\n\t\t\tstring cwd;\n\t\t\t\n\t\t\tif (exeProgram) {\n\t\t\t\tif (s_cwdExe == null) {\n\t\t\t\t\t//Pass this working directory to let the exe know it was launched from editor.\n\t\t\t\t\t//\tThen its AppModuleInit_ will read from shared memory and set the event.\n\t\t\t\t\t//\tAlso AppHost uses this to find portable .NET runtime.\n\t\t\t\t\t//There are no other ways to pass data when using shellexecute(runas) when cannot use command line args.\n\t\t\t\t\t//This empty hidden directory is created by the setup program. This code creates it if missing.\n\t\t\t\t\tcwd = folders.ThisApp.Path + \"\\\\Roslyn\\\\.exeProgram\";\n\t\t\t\t\tif (!filesystem.exists(cwd).Directory) {\n\t\t\t\t\t\tfilesystem.createDirectory(cwd);\n\t\t\t\t\t\tFile.SetAttributes(cwd, FileAttributes.Directory | FileAttributes.Hidden);\n\t\t\t\t\t}\n\t\t\t\t\ts_cwdExe = cwd;\n\t\t\t\t\ts_event1 = Api.CreateEvent2(default, true, false, \"Au.event.exeProgram.1\");\n\t\t\t\t} else {\n\t\t\t\t\tcwd = s_cwdExe;\n\t\t\t\t\tApi.ResetEvent(s_event1);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvar p = &SharedMemory_.Ptr->script;\n\t\t\t\tp->pidEditor = process.thisProcessId;\n\t\t\t\tp->hwndMsg = (int)CommandLine.MsgWnd;\n\t\t\t\tp->idMainFile = idMain;\n\t\t\t\tint flags = 0;\n\t\t\t\tif (runFromEditor) flags |= 2;\n\t\t\t\tif (App.IsPortable) flags |= 4;\n\t\t\t\tif (wrPipeName != null) { flags |= 8; p->pipe = wrPipeName; }\n\t\t\t\tif (redirectConsoleInExe) flags |= 16;\n\t\t\t\tp->flags = flags;\n\t\t\t\tp->workspace = folders.Workspace;\n\t\t\t} else cwd = folders.ThisApp;\n\t\t\t\n\t\t\tif (uac == _SpUac.elevate) {\n\t\t\t\tvar k = run.it(exeFile, args, RFlags.Admin | RFlags.NeedProcessHandle, cwd);\n\t\t\t\tr = (k.ProcessId, k.ProcessHandle);\n\t\t\t\t//note: don't try to start task without UAC consent. It is not secure.\n\t\t\t\t//\tNormally editor runs as admin in admin user account, and don't need to go through this.\n\t\t\t} else {\n\t\t\t\tvar ps = new ProcessStarter_(exeFile, args, cwd, rawExe: true);\n\t\t\t\t\n\t\t\t\tif (debugAttach != null) ps.si.dwXCountChars = 1703529821; //let the process wait for \"debugger attached\" event\n\t\t\t\t\n\t\t\t\tvar need = ProcessStarter_.Result.Need.WaitHandle;\n\t\t\t\tvar psr = uac == _SpUac.userFromAdmin\n\t\t\t\t\t? ps.StartUserIL(need)\n\t\t\t\t\t: ps.Start(need, inheritUiaccess: uac == _SpUac.uiAccess);\n\t\t\t\tr = (psr.pid, psr.waitHandle);\n\t\t\t\t\n\t\t\t\tif (debugAttach != null) {\n\t\t\t\t\tif (!debugAttach(psr.pid)) {\n\t\t\t\t\t\tApi.TerminateProcess(psr.waitHandle.SafeWaitHandle.DangerousGetHandle(), 0);\n\t\t\t\t\t\tpsr.waitHandle.Dispose();\n\t\t\t\t\t\treturn default;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tif (exeProgram) {\n\t\t\t\tnint hProc = r.hProcess.SafeWaitHandle.DangerousGetHandle();\n\t\t\t\tif (debugAttach != null) {\n\t\t\t\t\t//can't block, because need to communicate with netcoredbg.exe\n\t\t\t\t\twait.forHandle(0, WHFlags.DoEvents, s_event1, hProc);\n\t\t\t\t} else { //better low-level than wait.forHandle with flags 0\n\t\t\t\t\tnint* ha = stackalloc nint[2] { s_event1, hProc };\n\t\t\t\t\tApi.WaitForMultipleObjectsEx(2, ha, false, -1, false);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tApi.ResetEvent(s_event1);\n\t\t\t}\n\t\t\t\n\t\t\treturn r;\n\t\t}\n\t\tfinally { s_inSP = false; }\n\t}\n\tstatic IntPtr s_event1;\n\tstatic string s_cwdExe;\n\tstatic bool s_inSP;\n\t\n\tint _Find(int taskId) {\n\t\tfor (int i = 0; i < _a.Count; i++) {\n\t\t\tif (_a[i].taskId == taskId) return i;\n\t\t}\n\t\treturn -1;\n\t}\n\t\n\tpublic void OnWM_DISPLAYCHANGE() {\n\t\t//End preloaded processes. Else they may use wrong DPI.\n\t\tforeach (var v in s_preloaded) {\n\t\t\tif (v?.hProcess == null) continue;\n\t\t\tif (!Api.TerminateProcess(v.hProcess.SafeWaitHandle.DangerousGetHandle(), 0)) return;\n\t\t\tv.hProcess.Dispose();\n\t\t\tv.hProcess = null;\n\t\t\tv.pid = 0;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Compiler/TestInternal.cs",
    "content": "namespace LA;\n\nstatic class TestInternal {\n\tstatic HashSet<(string user, string target)> _hsCompiler = new(), _hsCi = new(), _hsRefs = new();\n\tstatic bool _compiling, _findingRefs;\n\t\n\tstatic TestInternal() {\n\t\t//Used by everything except 'find references'.\n\t\t//Called from any thread.\n\t\tRoslynMod.TestInternal.IsInternalsVisibleCallback = static (string thisName, string toName) => {\n\t\t\t//print.it(\"IsInternalsVisibleCallback\", thisName, toName);\n\t\t\tif (_compiling) return _hsCompiler.Contains((toName, thisName));\n\t\t\tif (_findingRefs) return _hsRefs.Contains((toName, thisName));\n\t\t\tlock (_hsCi) return _hsCi.Contains((toName, thisName));\n\t\t};\n\t\t\n\t\t//Used by 'find references'. Not by 'find implementations'.\n\t\t//Called from any thread.\n\t\tRoslynMod.TestInternal.AppendInternalsVisibleCallback += static (string thisName, HashSet<string> toNames) => {\n\t\t\tforeach (var v in _hsRefs) if (v.target == thisName) toNames.Add(v.user);\n\t\t\t//print.it(\"AppendInternalsVisibleCallback\", thisName, toNames);\n\t\t};\n\t}\n\t\n\tpublic static void CompilerStart(string asmName, string[] testInternals) {\n\t\t//print.it(\"CompilerStart\", asmName, testInternals);\n\t\tDebug.Assert(!_compiling);\n\t\tforeach (var v in testInternals) _hsCompiler.Add((asmName, v));\n\t\t_compiling = true;\n\t}\n\t\n\tpublic static void CompilerEnd() {\n\t\t//print.it(\"CompilerEnd\");\n\t\t_compiling = false;\n\t\t_hsCompiler.Clear();\n\t}\n\t\n\tpublic static void CiAdd(string asmName, string[] testInternals) {\n\t\t//print.it(\"CiAdd\", asmName, testInternals);\n\t\tlock (_hsCi) {\n\t\t\tforeach (var v in testInternals) _hsCi.Add((asmName, v));\n\t\t}\n\t}\n\t\n\tpublic static void CiClear() {\n\t\t//print.it(\"CiClear\");\n\t\tlock (_hsCi) _hsCi.Clear();\n\t}\n\t\n\tpublic static void RefsStart() {\n\t\t//print.it(\"RefsStart\", _hsCi);\n\t\t_hsRefs.Clear();\n\t\t_hsRefs.UnionWith(_hsCi);\n\t\t_findingRefs = true;\n\t}\n\t\n\tpublic static void RefsEnd() {\n\t\t//print.it(\"RefsEnd\");\n\t\t_findingRefs = false;\n\t\t_hsRefs.Clear();\n\t}\n\t\n\tpublic static void RefsAdd(string asmName, string[] testInternals) {\n\t\t//print.it(\"RefsAdd\", asmName, testInternals);\n\t\tforeach (var v in testInternals) _hsRefs.Add((asmName, v));\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Compiler/XCompiled.cs",
    "content": "namespace LA;\n\npartial class Compiler {\n\t/// <summary>\n\t/// Resolves whether need to [re]compile or can run previously compiled assembly.\n\t/// </summary>\n\tunsafe class XCompiled {\n\t\treadonly string _file;\n\t\tDictionary<uint, string> _data;\n\t\tstring _environmentString;\n\t\t\n\t\tpublic string CacheDirectory { get; }\n\t\t\n\t\tpublic static XCompiled OfWorkspace => (App.Model.CompilerContext_ ??= new XCompiled()) as XCompiled;\n\t\t\n\t\tpublic XCompiled() {\n\t\t\tCacheDirectory = App.Model.WorkspaceDirectory + @\"\\.compiled\";\n\t\t\t_file = CacheDirectory + @\"\\compiled.log\";\n\t\t\t\n\t\t\t//The first line in the log file contains .NET version, Au.dll version, OS version and workspace path,\n\t\t\t//\tlike 5.0.4|1.2.3.4|A00-64|\\\\?\\Volume{GUID}\\Path\n\t\t\t//\tVolume GUID for a removable drive is:\n\t\t\t//\t\tThe same on the same computer/OS, regardless of drive letter and USB port.\n\t\t\t//\t\tDifferent on each computer/OS. It's good.\n\t\t\tfilesystem.more.getFinalPath(folders.Workspace, out var ws, format: FPFormat.VolumeGuid);\n\t\t\t_environmentString = osVersion.onaString + \"|\" + ws;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Called before executing script f. If returns true, don't need to compile.\n\t\t/// </summary>\n\t\t/// <param name=\"f\"></param>\n\t\t/// <param name=\"r\">Receives file path and execution options.</param>\n\t\t/// <param name=\"projFolder\">Project folder or null. If not null, f must be its main file.</param>\n\t\tpublic bool IsCompiled(FileNode f, out CompResults r, FileNode projFolder) {\n\t\t\tr = new CompResults();\n\t\t\t\n\t\t\tif (_data == null && !_Open()) return false;\n\t\t\t\n\t\t\tif (!_data.TryGetValue(f.Id, out string value)) return false;\n\t\t\t//Debug_.Print(value);\n\t\t\tint iPipe = 0;\n\t\t\t\n\t\t\tbool isScript = f.IsScript;\n\t\t\tr.role = MetaComments.DefaultRole(isScript);\n\t\t\t\n\t\t\tstring asmFile;\n\t\t\tif (r.notInCache = value?.Starts(\"|=\") ?? false) {\n\t\t\t\tiPipe = value.IndexOf('|', 2); if (iPipe < 0) iPipe = value.Length;\n\t\t\t\tasmFile = pathname.NormalizeMinimally_(value[2..iPipe]);\n\t\t\t} else {\n\t\t\t\tasmFile = CacheDirectory + \"\\\\\" + f.IdString + \".dll\";\n\t\t\t}\n\t\t\t//print.it(asmFile);\n\t\t\t\n\t\t\tif (!filesystem.GetTime_(asmFile, out var asmTime)) return false;\n\t\t\t\n\t\t\tif (_IsFileModified(f)) return false;\n\t\t\t\n\t\t\tbool isMultiFileProject = false;\n\t\t\tif (value != null && iPipe < value.Length) {\n\t\t\t\tiPipe++;\n\t\t\t\tforeach (var v in value.SplitSE(iPipe.., '|', StringSplitOptions.RemoveEmptyEntries)) {\n\t\t\t\t\tint offs = v.start + 1;\n\t\t\t\t\tchar ch = value[v.start];\n\t\t\t\t\tswitch (ch) {\n\t\t\t\t\tcase 't':\n\t\t\t\t\t\tr.role = (MCRole)value.ToInt(offs);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 'n':\n\t\t\t\t\t\tr.ifRunning = (MCIfRunning)value.ToInt(offs);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 'u':\n\t\t\t\t\t\tr.uac = (MCUac)value.ToInt(offs);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 'b':\n\t\t\t\t\t\tr.platform = (MCPlatform)value.ToInt(offs);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 'f':\n\t\t\t\t\t\tr.flags = (MiniProgram_.MPFlags)value.ToInt(offs);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 'p':\n\t\t\t\t\t\tisMultiFileProject = true;\n\t\t\t\t\t\tif (projFolder != null) {\n\t\t\t\t\t\t\tif (!Hash.MD5Result.FromString(value.AsSpan(offs, v.end - offs), out var md5)) return false;\n\t\t\t\t\t\t\tHash.MD5Context md = default;\n\t\t\t\t\t\t\tforeach (var f1 in projFolder.EnumProjectClassFiles(f)) {\n\t\t\t\t\t\t\t\tif (_IsFileModified(f1)) return false;\n\t\t\t\t\t\t\t\tmd.Add(f1.Id);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (md.IsEmpty || md.Hash != md5) return false;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase '*':\n\t\t\t\t\t\tstring dll;\n\t\t\t\t\t\tif (value[offs] == '*') dll = string.Concat(App.Model.NugetDirectoryBS, value.AsSpan((offs + 1)..v.end));\n\t\t\t\t\t\telse if (value[offs] == '?') dll = string.Concat(App.Model.DllDirectoryBS, value.AsSpan((offs + 1)..v.end));\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tdll = value[offs..v.end];\n\t\t\t\t\t\t\tif (!pathname.isFullPathExpand(ref dll)) dll = folders.ThisAppBS + dll;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (_IsFileModified2(dll)) return false;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 'l':\n\t\t\t\t\tcase 'c':\n\t\t\t\t\tcase 'x':\n\t\t\t\t\tcase 'y':\n\t\t\t\t\tcase 'k':\n\t\t\t\t\tcase 'm':\n\t\t\t\t\tcase 's':\n\t\t\t\t\t\tvalue.ToInt(out uint u1, offs);\n\t\t\t\t\t\tvar f2 = App.Model.FindById(u1);\n\t\t\t\t\t\tif (f2 == null) return false;\n\t\t\t\t\t\tif (ch == 'l') {\n\t\t\t\t\t\t\tif (f2.FindProject(out var projFolder2, out var projMain2)) f2 = projMain2;\n\t\t\t\t\t\t\tif (f2 == f) return false; //will be compiler error \"circular reference\"\n\t\t\t\t\t\t\t//print.it(f2, projFolder2);\n\t\t\t\t\t\t\tif (!IsCompiled(f2, out _, projFolder2)) return false;\n\t\t\t\t\t\t\t//print.it(\"library is compiled\");\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tif (_IsFileModified(f2)) return false;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault: return false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (isMultiFileProject != (projFolder != null)) {\n\t\t\t\tif (projFolder == null) return false;\n\t\t\t\tforeach (var f1 in projFolder.EnumProjectClassFiles(f)) return false; //project with single file?\n\t\t\t}\n\t\t\t//Debug_.Print(\"compiled\");\n\t\t\t\n\t\t\tr.file = asmFile;\n\t\t\tr.name = pathname.getNameNoExt(f.Name);\n\t\t\treturn true;\n\t\t\t\n\t\t\tbool _IsFileModified(FileNode f_) => _IsFileModified2(f_.FilePath);\n\t\t\t\n\t\t\tbool _IsFileModified2(string path_) {\n\t\t\t\tif (!filesystem.GetProp_(path_, out var prop_)) return true;\n\t\t\t\tDebug.Assert(!prop_.attr.Has(FileAttributes.Directory));\n\t\t\t\t//print.it(prop_.LastWriteTimeUtc, asmDate);\n\t\t\t\tif (prop_.time > asmTime) return true;\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Called when successfully compiled script f. Saves data that next time will be used by <see cref=\"IsCompiled\"/>.\n\t\t/// </summary>\n\t\t/// <param name=\"outFile\">The output assembly.</param>\n\t\tpublic void AddCompiled(FileNode f, string outFile, MetaComments m, MiniProgram_.MPFlags miniFlags) {\n\t\t\tif (_data == null && !_Open()) _data = new();\n\t\t\t\n\t\t\t/*\nIDmain|=path.exe|tN|nN|uN|bN|fN|pMD5project|cIDcode|lIDlibrary|xIDresource|yIDfile|kIDicon|mIDmanifest|sIDsign|*ref\n= - outFile\nt - role\nn - ifRunning\nu - uac\nb - platform\nf - miniFlags\np - MD5 of Id of all project files except main\nc - c\nl - pr\nx - resource\ny - file\nk - icon\nm - manifest\ns - sign\n* - r\n\t\t\t*/\n\t\t\t\n\t\t\tstring value = null;\n\t\t\tusing (new StringBuilder_(out var b)) {\n\t\t\t\tif (m.OutputPath != null) b.Append(\"|=\").Append(folders.unexpandPath(outFile)); //else f.Id in cache\n\t\t\t\tif (m.Role != MetaComments.DefaultRole(m.MainFile.f.IsScript)) b.Append(\"|t\").Append((int)m.Role);\n\t\t\t\tif (m.IfRunning != default) b.Append(\"|n\").Append((int)m.IfRunning);\n\t\t\t\tif (m.Uac != default) b.Append(\"|u\").Append((int)m.Uac);\n\t\t\t\tb.Append(\"|b\").Append((int)m.Platform);\n\t\t\t\tif (miniFlags != default) b.Append(\"|f\").Append((int)miniFlags);\n\t\t\t\t\n\t\t\t\tHash.MD5Context md = default;\n\t\t\t\tforeach (var v in m.CodeFiles) {\n\t\t\t\t\tif (v.isC) _AppendFile(\"|c\", v.f); //ids of C# files added through meta 'c'\n\t\t\t\t\telse if (!v.isMain) md.Add(v.f.Id); //MD5 hash of project files, except main\n\t\t\t\t}\n\t\t\t\tif (!md.IsEmpty) b.Append(\"|p\").Append(md.Hash.ToString());\n\t\t\t\t\n\t\t\t\tif (m.ProjectReferences is { } pr) foreach (var (v, _) in pr) _AppendFile(\"|l\", v); //ids of meta 'pr' files\n\t\t\t\tif (m.Resources != null) foreach (var v in m.Resources) _AppendFile(\"|x\", v.f); //ids of meta 'resource' files\n\t\t\t\tif (m.OtherFiles != null) foreach (var v in m.OtherFiles) _AppendFile(\"|y\", v.f); //ids of meta 'file' files\n\t\t\t\t_AppendFile(\"|k\", m.IconFile);\n\t\t\t\t_AppendFile(\"|m\", m.ManifestFile);\n\t\t\t\t_AppendFile(\"|s\", m.SignFile);\n\t\t\t\t\n\t\t\t\t//references\n\t\t\t\tvar refs = m.References.Refs;\n\t\t\t\tint j = m.References.DefaultRefCount;\n\t\t\t\tif (refs.Count > j) {\n\t\t\t\t\tstring appDir = folders.ThisAppBS, nugetDir = App.Model.NugetDirectoryBS, dllDir = App.Model.DllDirectoryBS;\n\t\t\t\t\tfor (; j < refs.Count; j++) {\n\t\t\t\t\t\tstring s1 = refs[j].FilePath, prefix = \"|*\";\n\t\t\t\t\t\tif (s1.Starts(nugetDir, true)) { //note: in portable LA it is in app dir\n\t\t\t\t\t\t\ts1 = s1[nugetDir.Length..];\n\t\t\t\t\t\t\tprefix = \"|**\";\n\t\t\t\t\t\t} else if (s1.Starts(dllDir, true)) { //note: in portable LA it is in app dir\n\t\t\t\t\t\t\ts1 = s1[dllDir.Length..];\n\t\t\t\t\t\t\tprefix = \"|*?\";\n\t\t\t\t\t\t} else if (s1.Starts(appDir, true)) {\n\t\t\t\t\t\t\ts1 = s1[appDir.Length..];\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\ts1 = folders.unexpandPath(s1);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tb.Append(prefix).Append(s1);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (b.Length != 0) value = b.ToString();\n\t\t\t\t\n\t\t\t\tvoid _AppendFile(string m, FileNode f) {\n\t\t\t\t\tif (f == null) return;\n\t\t\t\t\tif (f.IsFolder) {\n\t\t\t\t\t\tDebug.Assert(m is \"|x\" or \"|y\" or \"|k\");\n\t\t\t\t\t\tforeach (var des in f.Descendants()) if (!des.IsFolder) b.Append(m).Append(des.IdString);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tb.Append(m).Append(f.IdString);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tuint id = f.Id;\n\t\t\tif (_data.TryGetValue(id, out var oldValue) && value == oldValue) { /*Debug_.Print(\"same\");*/ return; }\n\t\t\t//Debug_.Print(\"different\");\n\t\t\t_data[id] = value;\n\t\t\t_Save();\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Removes saved f data, so that next time <see cref=\"IsCompiled\"/> will return false.\n\t\t/// </summary>\n\t\tpublic void Remove(FileNode f, bool deleteAsmFile) {\n\t\t\tif (_data == null && !_Open()) return;\n\t\t\tif (_data.Remove(f.Id)) {\n\t\t\t\t_Save();\n\t\t\t\tif (deleteAsmFile) {\n\t\t\t\t\ttry { filesystem.delete(CacheDirectory + \"\\\\\" + f.IdString + \".dll\"); }\n\t\t\t\t\tcatch (Exception ex) { Debug_.Print(ex); }\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tbool _Open() {\n\t\t\tif (_data != null) return true;\n\t\t\tif (!filesystem.exists(_file).File) return false;\n\t\t\tstring sData = filesystem.loadText(_file);\n\t\t\tforeach (var v in sData.Lines(.., noEmpty: true)) {\n\t\t\t\tif (_data == null) {\n\t\t\t\t\tif (sData[v.Range] != _environmentString) goto g1;\n\t\t\t\t\t_data = new(sData.LineCount());\n\t\t\t\t} else {\n\t\t\t\t\tsData.ToInt(out uint id, v.start, out int idEnd);\n\t\t\t\t\tif (null != App.Model.FindById(id))\n\t\t\t\t\t\t_data[id] = v.end > idEnd ? sData[idEnd..v.end] : null;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (_data == null) return false; //empty file\n\t\t\t\n\t\t\t//delete temp files\n\t\t\tforeach (var f in filesystem.enumerate(CacheDirectory, FEFlags.UseRawPath | FEFlags.IgnoreInaccessible)) {\n\t\t\t\tif (f.Name.Like(\"*'*\")) Api.DeleteFile(f.FullPath);\n\t\t\t}\n\t\t\t\n\t\t\treturn true;\n\t\t\tg1:\n\t\t\t_ClearCache();\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\tvoid _Save() {\n\t\t\tfilesystem.createDirectory(CacheDirectory);\n\t\t\tusing var b = filesystem.waitIfLocked(() => File.CreateText(_file));\n\t\t\tb.WriteLine(_environmentString);\n\t\t\tforeach (var v in _data) {\n\t\t\t\tif (v.Value == null) b.WriteLine(v.Key); else { b.Write(v.Key); b.WriteLine(v.Value); }\n\t\t\t}\n\t\t\t//tested: fast, same speed as StringBuilder+WriteAllText. With b.WriteLine(v.Key+v.Value) same speed or slower.\n\t\t}\n\t\t\n\t\tvoid _ClearCache() {\n\t\t\t_data = null;\n\t\t\ttry { filesystem.delete(CacheDirectory); }\n\t\t\tcatch (AuException e) { print.warning(\"Failed to delete compiled script assembly cache. \" + e.ToString(), -1); }\n\t\t}\n\t}\n\t\n\tpublic static void Uncache(FileNode f, bool andDescendants = false) {\n\t\tvar x = XCompiled.OfWorkspace;\n\t\tx.Remove(f, true);\n\t\tif (andDescendants && f.IsFolder) foreach (var v in f.Descendants()) x.Remove(v, true);\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Compiler/XPublish.cs",
    "content": "using Au.Controls;\nusing System.Xml.Linq;\nusing System.Windows;\nusing Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp;\nusing System.Windows.Controls;\n\nnamespace LA;\n\nclass XPublish {\n\tMetaComments _meta;\n\tstring _csprojDir, _csprojFile;\n\t\n\tpublic async void Publish() {\n\t\tvar cmd = App.Commands[nameof(Menus.Run.Publish)];\n\t\tif (!cmd.Enabled) return;\n\t\tcmd.Enable(false);\n\t\ttry {\n\t\t\tvar b = new wpfBuilder(\"Publish\").WinSize(300).WinProperties(resizeMode: ResizeMode.NoResize, showInTaskbar: false);\n\t\t\tb.R.Add(out KCheckBox cSingle, \"Single file\").Checked(0 == (1 & App.Settings.publish));\n\t\t\tb.R.Add(out CheckBox cSelfExtract, \"Self-extract\").Margin(20)\n\t\t\t\t.xBindCheckedEnabled(cSingle)\n\t\t\t\t.Tooltip(\"\"\"\nChecked - use <IncludeAllContentForSelfExtract>. Adds all files to exe. Will extract all (including .NET dlls) to a temporary directory.\nUnchecked - adds only .NET dlls to exe. Native dlls and other files (if any) will live in the exe's directory. Will use .NET dlls without extracting.\nIndeterminate - use <IncludeNativeLibrariesForSelfExtract>. Adds all dlls to exe. Will extract native dlls, and use .NET dlls without extracting.\n\"\"\")\n\t\t\t\t.Checked((App.Settings.publish >>> 8 & 3) switch { 1 => false, 2 => true, _ => null }, threeState: true);\n\t\t\tb.R.Add(out KCheckBox cNet, \"Add .NET Runtime\").Checked(0 != (2 & App.Settings.publish));\n\t\t\tb.R.Add(out KCheckBox cR2R, \"ReadyToRun\").Checked(0 != (4 & App.Settings.publish));\n\t\t\tb.R.Add(\"Platform\", out ComboBox cbPlatform).Width(100, \"L\").Items(\"x64|arm64|x86\").Select(Math.Clamp(App.Settings.publish >>> 4 & 3, 0, 2));\n\t\t\tb.R.Add(out TextBlock tProgress).Hidden(null);\n\t\t\tb.R.StartOkCancel().AddOkCancel(out var bOK, out _, out _).xAddDialogHelpButtonAndF1(\"editor/Creating exe programs\").End();\n\t\t\tb.End();\n\t\t\t\n\t\t\tDotnetUtil.MissingSdkUI(b.Window, [bOK]);\n\t\t\t\n\t\t\tif (!b.ShowDialog(App.Wmain)) return;\n\t\t\t\n\t\t\tint platform = cbPlatform.SelectedIndex;\n\t\t\tApp.Settings.publish = (cSingle.IsChecked ? 0 : 1) | (cNet.IsChecked ? 2 : 0) | (cR2R.IsChecked ? 4 : 0) | ((cSelfExtract.IsChecked switch { false => 1, true => 2, _ => 0 }) << 8) | (platform << 4);\n\t\t\t//TODO3: maybe support publish profiles like in VS: <PublishProfileFullPath>\n\t\t\t\n\t\t\tprint.it($\"<><lc YellowGreen>Building program files for publishing. Please wait until DONE.<>\");\n\t\t\t\n\t\t\tbool singleFile = cSingle.IsChecked; bool? selfExtract = cSelfExtract.IsChecked;\n\t\t\tif (!_CreateCsproj(singleFile: singleFile, selfExtract, selfContained: cNet.IsChecked, readyToRun: cR2R.IsChecked, platform)) return;\n\t\t\t\n\t\t\tvar outDir = $@\"{_csprojDir}\\release_{platform switch { 0 => \"x64\", 1 => \"arm64\", _ => \"x86\" }}\";\n\t\t\tvar outFile = $@\"{outDir}\\{_meta.Name}.exe\";\n\t\t\t_PrepareOutDir(outDir, outFile);\n\t\t\tif (_meta.PreBuild.f != null && !CompilerUtil.RunPrePostBuildScript(_meta, false, outFile, true)) return;\n\t\t\tif (!await Task.Run(() => _DotnetPublish(outDir, singleFile, selfExtract))) return;\n\t\t\tif (_meta.PostBuild.f != null && !CompilerUtil.RunPrePostBuildScript(_meta, true, outFile, true)) return;\n\t\t\t\n\t\t\t_DeleteOldOutputDir(\"64\");\n\t\t\t_DeleteOldOutputDir(\"32\");\n\t\t\tvoid _DeleteOldOutputDir(string bit) {\n\t\t\t\tvar s = $@\"{_csprojDir}\\release_{bit}\";\n\t\t\t\tif (filesystem.exists(s)) print.it($\"<>This LibreAutomate version uses different names for publish output folders. You may want to delete this old unused folder: <explore>{s}<>\");\n\t\t\t}\n\t\t\t\n\t\t\tprint.it(\"==== DONE ====\");\n\t\t}\n\t\tcatch (Exception e1) { print.it(e1); }\n\t\tfinally {\n\t\t\tfilesystem.delete(folders.ThisAppTemp + \"publish\", FDFlags.CanFail);\n\t\t\tcmd.Enable(true);\n\t\t}\n\t}\n\t\n\tstatic void _PrepareOutDir(string outDir, string outFile) {\n\t\tif (filesystem.exists(outDir).Directory) {\n\t\t\tCompilerUtil.DeleteExeFile(outFile);\n\t\t\tforeach (var v in filesystem.enumerate(outDir)) filesystem.delete(v.FullPath, FDFlags.CanFail);\n\t\t}\n\t}\n\t\n\tbool _DotnetPublish(string outDir, bool singleFile, bool? selfExtract) {\n\t\tbool dirExisted = filesystem.exists(outDir);\n\t\t\n\t\tvar buildDir = _csprojDir + @\"\\.build\";\n\t\tvar cl = $@\"publish \"\"{_csprojFile}\"\" -c Release -o \"\"{outDir}\"\" --artifacts-path \"\"{buildDir}\"\" -v q\";\n\t\tprint.it($\"<><c blue>dotnet {cl}<>\");\n\t\tif (0 != run.console(DotnetUtil.DotnetExe, cl)) { print.it(\"Failed\"); return false; }\n\t\tprint.it($@\"<>{_meta.Name} -> <explore>{outDir}\\{_meta.Name}.exe<>\");\n\t\t\n\t\tfilesystem.delete(buildDir, FDFlags.CanFail);\n\t\t\n\t\tint nFiles = 0;\n\t\tforeach (var v in filesystem.enumerate(outDir)) {\n\t\t\tvar path = v.FullPath;\n\t\t\tif (!v.IsDirectory && path.Ends(\".pdb\", true)) filesystem.delete(path, FDFlags.CanFail); //probably Au.pdb (at home only)\n\t\t\telse nFiles++;\n\t\t}\n\t\t\n\t\t//warn if singleFile with IncludeNativeLibrariesForSelfExtract produced more than single exe file. See comments in _CreateCsproj.\n\t\tif (singleFile && selfExtract != true) {\n\t\t\tif (selfExtract == false) print.it(\"<>Note: <b>Self-extract<> is unchecked, therefore only .NET dlls have been added to exe. Other files are located in the exe's directory.\");\n\t\t\telse if (nFiles > 1) print.it(\"<>Warning: If <b>Self-extract<> is indeterminate, some files used by this script cannot be added to exe, and the program may be invalid. It should be either checked or unchecked.\");\n\t\t\t//BAD: may not add some files even with selfExtract true. Eg from nuget Selenium.WebDriver.ChromeDriver.\n\t\t}\n\t\t\n\t\t//sometimes Explorer does not update the folder view\n\t\tif (dirExisted) filesystem.ShellNotify_(Api.SHCNE_UPDATEDIR, outDir);\n\t\t\n\t\treturn true;\n\t}\n\t\n\tbool _CreateCsproj(bool singleFile, bool? selfExtract, bool selfContained, bool readyToRun, int platform) {\n\t\tApp.Model.Save.TextNowIfNeed(onlyText: true);\n\t\t\n\t\tvar doc = Panels.Editor.ActiveDoc; if (doc == null) return false;\n\t\tvar f = doc.EFile;\n\t\tif (f.FindProject(out var projFolder, out var projMain)) f = projMain;\n\t\tif (!f.IsCodeFile) return false;\n\t\t\n\t\t_meta = new MetaComments(MCFlags.Publish | MCFlags.PrintErrors);\n\t\tif (!_meta.Parse(f, projFolder)) return false;\n\t\tif (_meta.Role is not (MCRole.miniProgram or MCRole.exeProgram)) return _Err(\"expected role exeProgram or miniProgram\");\n\t\tif (_meta.TestInternal != null) return _Err(\"testInternal not supported\");\n\t\t\n\t\t_csprojDir = $@\"{_meta.OutputPath ?? MetaComments.GetDefaultOutputPath(f, _meta.Role, withEnvVar: false)}\\publish\";\n\t\tfilesystem.createDirectory(_csprojDir);\n\t\t\n\t\tvar xroot = new XElement(\"Project\", new XAttribute(\"Sdk\", \"Microsoft.NET.Sdk\"));\n\t\tvar xpg = new XElement(\"PropertyGroup\");\n\t\txroot.Add(xpg);\n\t\t\n\t\t_Add(xpg, \"TargetFramework\", $\"net{Environment.Version.ToString(2)}-windows\");\n\t\t_Add(xpg, \"LangVersion\", \"preview\");\n\t\t_Add(xpg, \"AllowUnsafeBlocks\", \"true\");\n\t\t_Add(xpg, \"UseWindowsForms\", \"true\");\n\t\t_Add(xpg, \"UseWPF\", \"true\");\n\t\t_Add(xpg, \"EnableDefaultItems\", \"false\"); //https://learn.microsoft.com/en-us/dotnet/core/project-sdk/msbuild-props-desktop#wpf-default-includes-and-excludes\n\t\t_Add(xpg, \"CopyLocalLockFileAssemblies\", \"true\");\n\t\t_Add(xpg, \"ProduceReferenceAssembly\", \"false\");\n\t\t_Add(xpg, \"PublishReferencesDocumentationFiles\", \"false\");\n\t\t_Add(xpg, \"DebugType\", \"embedded\");\n\t\t_Add(xpg, \"CopyDebugSymbolFilesFromPackages\", \"false\");\n\t\t_Add(xpg, \"AppendTargetFrameworkToOutputPath\", \"false\");\n\t\t_Add(xpg, \"NuGetAudit\", \"false\"); //don't connect to nuget.org unless using nuget references. If no internet connection, waits several s and prints: warning NU1900: Error occurred while getting package vulnerability data: No such host is known. (api.nuget.org:443). But sometimes hangs. Error if command line contains --no-restore. \n\t\t_Add(xpg, \"AllowMissingPrunePackageData\", \"true\");\n\t\t\n\t\t_Add(xpg, \"AssemblyName\", _meta.Name);\n\t\t_Add(xpg, \"OutputType\", _meta.Console ? \"Exe\" : \"WinExe\");\n\t\t\n\t\t_Add(xpg, \"DisableImplicitFrameworkDefines\", \"true\");\n\t\t_Add(xpg, \"DefineConstants\", string.Join(';', _meta.Defines));\n\t\t\n\t\t_Add(xpg, \"WarningLevel\", _meta.WarningLevel);\n\t\t_Add(xpg, \"NoWarn\", string.Join(';', _meta.NoWarnings) + \";WFAC010;WFO0003;CA1416\");\n\t\tif (_meta.Nullable != 0) _Add(xpg, \"Nullable\", _meta.Nullable);\n\t\t\n\t\tvar sPlatform = platform switch { 0 => \"x64\", 1 => \"arm64\", _ => \"x86\" };\n\t\t_Add(xpg, \"PlatformTarget\", sPlatform);\n\t\t_Add(xpg, \"RuntimeIdentifier\", \"win-\" + sPlatform);\n\t\t\n\t\tif (!_Icon()) return false;\n\t\t_Add(xpg, \"ApplicationManifest\", _Path(_meta.ManifestFile) ?? folders.ThisAppBS + \"default.exe.manifest\");\n\t\t\n\t\t//if (_meta.CodeFiles.Any(o => o.f.Name.Eqi(\"AssemblyInfo.cs\"))) _Add(xpg, \"GenerateAssemblyInfo\", \"false\"); //no, users don't know it. See https://www.libreautomate.com/forum/showthread.php?tid=7591\n\t\t_Add(xpg, \"GenerateAssemblyInfo\", \"false\");\n\t\t\n\t\tif (_meta.SignFile != null) {\n\t\t\t_Add(xpg, \"SignAssembly\", \"true\");\n\t\t\t_Add(xpg, \"AssemblyOriginatorKeyFile\", _Path(_meta.SignFile));\n\t\t}\n\t\t\n\t\tif (singleFile) {\n\t\t\t_Add(xpg, \"PublishSingleFile\", \"true\");\n\t\t\tif (selfContained) _Add(xpg, \"EnableCompressionInSingleFile\", \"true\"); //else compression not supported (with IncludeAllContentForSelfExtract too)\n\t\t\tif (selfExtract == null) _Add(xpg, \"IncludeNativeLibrariesForSelfExtract\", \"true\"); else if (selfExtract == true) _Add(xpg, \"IncludeAllContentForSelfExtract\", \"true\");\n\t\t\t//About selfExtract:\n\t\t\t//IncludeNativeLibrariesForSelfExtract (selfExtract null) has problems:\n\t\t\t//\t1. Does not add data files (of most types).\n\t\t\t//\t2. Adds not only native dlls, but also exe etc. With this mess the program usually crashes. To repro, add nuget Microsoft.Playwright.\n\t\t\t//IncludeAllContentForSelfExtract (selfExtract true) solves both. Also solves the \"no assembly location etc\" problem. But has own issues.\n\t\t\t//selfExtract false solves #2.\n\t\t\t//rejected: instead use normal checkbox that adds IncludeAllContentForSelfExtract when checked.\n\t\t\t//\tIf unchecked, use IncludeNativeLibrariesForSelfExtract only it produces single file (else there is no sense to add dlls etc).\n\t\t\t//\tTo implement it probably would need to run dotnet publish twice. I don't know other ways.\n\t\t}\n\t\t_Add(xpg, \"SelfContained\", selfContained ? \"true\" : \"false\");\n\t\tif (readyToRun) _Add(xpg, \"PublishReadyToRun\", \"true\");\n\t\t\n\t\tvar xig = new XElement(\"ItemGroup\");\n\t\txroot.Add(xig);\n\t\t\n\t\t_AddAttr(xig, \"Compile\", \"Include\", _ModuleInit());\n\t\tforeach (var v in _meta.CodeFiles) _AddAttr(xig, \"Compile\", \"Include\", _Path(v.f));\n\t\t\n\t\tvar trees = CompilerUtil.CreateSyntaxTrees(_meta);\n\t\t\n\t\t_AddAuNativeDll(\"AuCpp.dll\", 0);\n\t\t_AddAuNativeDll(\"Au.DllHost.exe\", -1);\n\t\t//if (_NeedSqlite()) _AddAuNativeDll(\"sqlite3.dll\", 1);\n\t\tCompilerUtil.CopyMetaFileFilesOfAllProjects(_meta, _csprojDir, (from, to) => _AddContentFile(from, to));\n\t\t\n\t\tif (_meta.References.DefaultRefCount != MetaReferences.DefaultReferences.Count) return _Err(\"noRef not supported\"); //TODO3: try <DisableImplicitFrameworkReferences>\n\t\t_AddAttr(xig, \"Reference\", \"Include\", _meta.References.Refs[_meta.References.DefaultRefCount - 1].FilePath); //Au\n\t\t_References();\n\t\t\n\t\tif (!_Nuget()) return false;\n\t\t\n\t\tif (!_ManagedResources()) return false;\n\t\t\n\t\t//print.it(xroot);\n\t\txroot.SaveElem(_csprojFile = $@\"{_csprojDir}\\{_meta.Name}.csproj\");\n\t\treturn true;\n\t\t\n\t\tstatic bool _Err(string s) { print.it(\"Error: \" + s); return false; }\n\t\t\n\t\tstatic string _Path(FileNode f) => f?.FilePath;\n\t\t\n\t\tstatic XElement _Add(XElement parent, string tag, object value) {\n\t\t\tXElement x = new(tag, value);\n\t\t\tparent.Add(x);\n\t\t\treturn x;\n\t\t}\n\t\t\n\t\tstatic XElement _AddAttr(XElement parent, string tag, string attr, object value) {\n\t\t\tXElement x = new(tag, new XAttribute(attr, value));\n\t\t\tparent.Add(x);\n\t\t\treturn x;\n\t\t}\n\t\t\n\t\tvoid _AddContentFile(string path, string to) {\n\t\t\tvar x = _AddAttr(xig, \"Content\", \"Include\", path);\n\t\t\t_Add(x, \"CopyToOutputDirectory\", \"PreserveNewest\");\n\t\t\tif (to.PathStarts(_csprojDir)) to = to[(_csprojDir.Length + 1)..];\n\t\t\t_Add(x, \"Link\", to); //note: TargetPath should be the same, but somehow ignores files in subfolders (meta file x /sub) if single-file, unless the folder exists.\n\t\t}\n\t\t\n\t\t//platforms: 0 all, 1 only this, -1 only other\n\t\tvoid _AddAuNativeDll(string filename, int platforms) {\n\t\t\tfor (int i = 0; i < 3; i++) {\n\t\t\t\tif (platforms == 0 || (platforms > 0 ? i == platform : i != platform)) {\n\t\t\t\t\tvar s = i switch { 0 => @\"64\\\", 1 => @\"64\\ARM\\\", _ => @\"32\\\" } + filename;\n\t\t\t\t\t//_AddContentFile(folders.ThisAppBS + s, s); //no, NativeLibrary.TryLoad fails if single-file\n\t\t\t\t\t_AddContentFile(folders.ThisAppBS + s, i == platform ? filename : s);\n\t\t\t\t}\n\t\t\t}\n\t\t\t//why need AuCpp.dll and Au.DllHost.exe for other platforms: for elm functions to load AuCpp.dll into processes of different platform. See func SwitchArchAndInjectDll in in-proc.cpp.\n\t\t}\n\t\t\n\t\tstring _ModuleInit() {\n\t\t\tvar path = _csprojDir + @\"\\ModuleInit__.cs\";\n\t\t\tfilesystem.saveText(path, @\"class ModuleInit__ { [System.Runtime.CompilerServices.ModuleInitializer] internal static void Init() { Au.script.AppModuleInit_(auCompiler: false); }}\"); //like in Compiler._AddAttributesEtc but with `auCompiler: false`\n\t\t\treturn path;\n\t\t}\n\t\t\n\t\tbool _Icon() {\n\t\t\tif (_meta.IconFile is { } fIco) {\n\t\t\t\tif (fIco.IsFolder) return _Err(\"icons folder not supported\");\n\t\t\t\t_Add(xpg, \"ApplicationIcon\", _Path(fIco));\n\t\t\t} else if (DIcons.TryGetIconFromDB(f.CustomIconName, out string xaml)) {\n\t\t\t\tvar file = _csprojDir + @\"\\icon.ico\";\n\t\t\t\ttry {\n\t\t\t\t\tvar e = ImageUtil.LoadWpfImageElement(xaml);\n\t\t\t\t\tImageUtil.ConvertWpfImageElementToIcon_(file, e, [16, 24, 32, 48, 64]);\n\t\t\t\t\t_Add(xpg, \"ApplicationIcon\", file);\n\t\t\t\t}\n\t\t\t\tcatch (Exception e1) { print.it(e1); }\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\tbool _ManagedResources() {\n\t\t\tvar resourcesFile = _csprojDir + @\"\\g.resources\";\n\t\t\treturn CompilerUtil.CreateManagedResources(_meta, _meta.Name, trees, _ResourceException, o => {\n\t\t\t\tvar x = _AddAttr(xig, \"EmbeddedResource\", \"Include\", o.file);\n\t\t\t\t_Add(x, \"LogicalName\", o.name);\n\t\t\t}, resourcesFile);\n\t\t\t\n\t\t\tstatic void _ResourceException(Exception e, FileNode curFile) {\n\t\t\t\tvar em = e.ToStringWithoutStack();\n\t\t\t\tif (curFile == null) _Err(\"Failed to add resources. \" + em);\n\t\t\t\telse _Err($\"Failed to add resource '{curFile.Name}'. \" + em);\n\t\t\t}\n\t\t}\n\t\t\n\t\tvoid _References() {\n\t\t\tvar noDup = new HashSet<string>(StringComparer.OrdinalIgnoreCase);\n\t\t\t_Ref2(_meta);\n\t\t\tvoid _Ref2(MetaComments m) {\n\t\t\t\tforeach (var v in m.References.Refs.Skip(_meta.References.DefaultRefCount)) { //r, pr, com\n\t\t\t\t\tif (MetaReferences.IsNuget(v)) continue;\n\t\t\t\t\tvar x = _AddAttr(xig, \"Reference\", \"Include\", v.FilePath);\n\t\t\t\t\tif (v.Properties.EmbedInteropTypes) _Add(x, \"EmbedInteropTypes\", \"true\");\n\t\t\t\t\tif (!v.Properties.Aliases.IsDefaultOrEmpty) _Add(x, \"Aliases\", v.Properties.Aliases[0]);\n\t\t\t\t\tif (!noDup.Add(x.ToString())) x.Remove();\n\t\t\t\t}\n\t\t\t\tif (m.ProjectReferences is { } pr) {\n\t\t\t\t\tforeach (var v in pr) _Ref2(v.m);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tbool _Nuget() {\n\t\t\tvar noDup = new HashSet<string>(StringComparer.OrdinalIgnoreCase);\n\t\t\treturn _Nuget2(_meta);\n\t\t\tbool _Nuget2(MetaComments m) {\n\t\t\t\tforeach (var (p, alias) in m.NugetPackages) {\n\t\t\t\t\tvar dir = pathname.getDirectory(p);\n\t\t\t\t\tvar path = $@\"{App.Model.NugetDirectoryBS}{dir}\\{dir}.csproj\";\n\t\t\t\t\tif (XmlUtil.LoadElemIfExists(path) is not { } xproj || xproj.Desc(\"PackageReference\", \"Include\", pathname.getName(p), true) is not { } x) return _Err(\"NuGet package not installed: \" + p);\n\t\t\t\t\tif (alias != null) _Add(x, \"Aliases\", alias);\n\t\t\t\t\tif (!noDup.Add(x.ToString())) continue;\n\t\t\t\t\txig.Add(x);\n\t\t\t\t}\n\t\t\t\tif (m.ProjectReferences is { } pr) {\n\t\t\t\t\tforeach (var v in pr) if (!_Nuget2(v.m)) return false;\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\t\n\t\t//bool _NeedSqlite() {\n\t\t//\tvar cOpt = new CSharpCompilationOptions(OutputKind.WindowsApplication, allowUnsafe: true, warningLevel: 0);\n\t\t//\tvar compilation = CSharpCompilation.Create(_meta.Name, trees, _meta.References.Refs, cOpt);\n\t\t//\tvar asmStream = new MemoryStream(16000);\n\t\t//\tvar emitResult = compilation.Emit(asmStream);\n\t\t//\tasmStream.Position = 0;\n\t\t//\tif (emitResult.Success && CompilerUtil.UsesSqlite(asmStream)) return true;\n\t\t\n\t\t//\tforeach (var v in _meta.References.Refs.Skip(_meta.References.DefaultRefCount)) {\n\t\t//\t\tif (v.Properties.EmbedInteropTypes) continue; //com; and no nuget refs when publishing\n\t\t//\t\tif (CompilerUtil.UsesSqlite(v.FilePath, recursive: true)) return true;\n\t\t//\t}\n\t\t\n\t\t//\treturn false;\n\t\t//}\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Compiler/util/CompilerUtil.cs",
    "content": "using System.Reflection.Metadata;\nusing System.Reflection.PortableExecutable;\nusing System.Resources;\nusing Microsoft.CodeAnalysis.CSharp;\nusing Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp.Syntax;\n\nnamespace LA;\n\nstatic partial class CompilerUtil {\n\t/// <summary>\n\t/// Returns true if the dll file is a .NET assembly (any, not only of the .NET library).\n\t/// </summary>\n\tpublic static bool IsNetAssembly(string path) {\n\t\tusing var pr = new PEReader(filesystem.loadStream(path));\n\t\treturn pr.HasMetadata;\n\t}\n\t\n\t/// <summary>\n\t/// Returns true if the dll file is a .NET assembly. Sets isRefOnly = true if it's a reference-only assembly.\n\t/// </summary>\n\tpublic static bool IsNetAssembly(string path, out bool isRefOnly) {\n\t\tisRefOnly = false;\n\t\tusing var pr = new PEReader(filesystem.loadStream(path));\n\t\tif (!pr.HasMetadata) return false;\n\t\tvar mr = pr.GetMetadataReader();\n\t\t\n\t\tforeach (var v in mr.GetAssemblyDefinition().GetCustomAttributes()) {\n\t\t\tvar ca = mr.GetCustomAttribute(v);\n\t\t\tvar h = ca.Constructor;\n\t\t\tif (h.Kind == HandleKind.MemberReference) {\n\t\t\t\tvar m = mr.GetMemberReference((MemberReferenceHandle)h);\n\t\t\t\tvar t = mr.GetTypeReference((TypeReferenceHandle)m.Parent);\n\t\t\t\tvar s = mr.GetString(t.Name);\n\t\t\t\tif (s == \"ReferenceAssemblyAttribute\") { isRefOnly = true; break; }\n\t\t\t}\n\t\t}\n\t\t\n\t\treturn true;\n\t}\n\t\n\tpublic static CSharpSyntaxTree[] CreateSyntaxTrees(MetaComments meta) {\n\t\tvar pOpt = meta.CreateParseOptions();\n\t\tvar trees = new CSharpSyntaxTree[meta.CodeFiles.Count];\n\t\tfor (int i = 0; i < trees.Length; i++) {\n\t\t\tvar f1 = meta.CodeFiles[i];\n\t\t\t\n\t\t\t//never mind: should use Encoding.UTF8 etc if the file is with BOM. Encoding.Default is UTF-8 without BOM.\n\t\t\t//\tElse, when debugging with VS or VS Code, they say \"source code changed\" and can't set breakpoints by default.\n\t\t\t//\tBut they have an option to debug modified files anyway.\n\t\t\t//\tThis program saves new files without BOM. It seems VS Code too. VS saves with BOM (maybe depends on its settings).\n\t\t\t//\tCONSIDER: use ParseText overload with SourceText, for which use StreamReader that detects BOM. Not tested.\n\t\t\tvar encoding = Encoding.Default;\n\t\t\t\n\t\t\ttrees[i] = CSharpSyntaxTree.ParseText(f1.code, pOpt, f1.f.FilePath, encoding) as CSharpSyntaxTree;\n\t\t\t\n\t\t\t//info: file path is used later in several places: in compilation error messages, run time stack traces (from PDB), debuggers, etc.\n\t\t\t//\tOur PrintServer.SetNotifications callback will convert file/line info to links. It supports compilation errors and run time stack traces.\n\t\t}\n\t\treturn trees;\n\t}\n\t\n\t/// <summary>\n\t/// Creates managed resources for embedding.\n\t/// </summary>\n\t/// <param name=\"meta\"></param>\n\t/// <param name=\"asmName\"></param>\n\t/// <param name=\"trees\">Syntax trees of all source files. Used to extract XAML icons from strings like \"*icon\". If library, this func can replace some array elements.</param>\n\t/// <param name=\"failed\">Called when failed. Eg a file not found, or error in meta. The <b>FileNode</b> can be null.</param>\n\t/// <param name=\"result\">\n\t/// Called for each embedded resource, including the one containing non-embedded resources. Receives:\n\t/// <br/>• name - embedded resource name.\n\t/// <br/>• stream - embedded resource data. Null if <i>resourcesFile</i> not null.\n\t/// <br/>• file - path of embedded resource file. If the resource contains non-embedded resources, it is <i>resourcesFile</i> (which can be null).</param>\n\t/// <param name=\"resourcesFile\">If not null, saves non-embedded resources in this \".resources\" file. Then the caller can add the file as an embedded resource.</param>\n\t/// <returns>false if failed.</returns>\n\tpublic static bool CreateManagedResources(MetaComments meta, string asmName, CSharpSyntaxTree[] trees, Action<Exception, FileNode> failed, Action<(string name, Stream stream, string file)> result, string resourcesFile = null) {\n\t\tResourceWriter rw = null;\n\t\tMemoryStream stream = null;\n\t\tFileNode curFile = null;\n\t\tbool needStream = resourcesFile == null;\n\t\t\n\t\tvoid _RW() { rw ??= needStream ? new(stream = new()) : new(resourcesFile); }\n\t\t\n\t\ttry {\n\t\t\tvar a = meta.Resources;\n\t\t\tif (!a.NE_()) {\n\t\t\t\tforeach (var v in a) {\n\t\t\t\t\t//if (v.f == null) { // /resources //rejected\n\t\t\t\t\t//\t_End();\n\t\t\t\t\t//\tresourcesName = v.s + \".resources\";\n\t\t\t\t\t//} else\n\t\t\t\t\t\n\t\t\t\t\t//if has suffix /path, make resource name = path relative to pathRoot, like \"subfolder/filename.ext\". Else \"filename.ext\".\n\t\t\t\t\tbool usePath = false;\n\t\t\t\t\tFileNode pathRoot = null;\n\t\t\t\t\tstring resType = v.s;\n\t\t\t\t\tif (resType != null) {\n\t\t\t\t\t\tif (usePath = resType == \"path\") resType = null;\n\t\t\t\t\t\telse if (usePath = resType.Ends(\" /path\")) resType = resType[..^6].NullIfEmpty_();\n\t\t\t\t\t\tif (usePath) {\n\t\t\t\t\t\t\tcurFile = v.f;\n\t\t\t\t\t\t\tpathRoot = meta.MainFile.f.Parent;\n\t\t\t\t\t\t\tif (!v.f.IsDescendantOf(pathRoot)) throw new ArgumentException(\"/path cannot be used if the file isn't in this folder\");\n\t\t\t\t\t\t\tif (resType == \"strings\") throw new ArgumentException(\"/path cannot be used with /strings\");\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tif (v.f.IsFolder) {\n\t\t\t\t\t\tforeach (var des in v.f.Descendants()) if (!des.IsFolder) _Add(des, resType, pathRoot ?? v.f);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t_Add(v.f, resType, pathRoot);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcurFile = null;\n\t\t\t\t\n\t\t\t\tvoid _Add(FileNode f, string resType, FileNode folder = null) {\n\t\t\t\t\tcurFile = f;\n\t\t\t\t\tstring name = f.Name, path = f.FilePath;\n\t\t\t\t\tif (folder != null) for (var pa = f.Parent; pa != folder; pa = pa.Parent) name = pa.Name + \"/\" + name;\n\t\t\t\t\t//print.it(f, resType, folder, name, path);\n\t\t\t\t\tif (resType == \"embedded\") {\n\t\t\t\t\t\tresult((name, needStream ? filesystem.loadStream(path) : null, path));\n\t\t\t\t\t} else {\n\t\t\t\t\t\tname = name.Lower(); //else pack URI fails; ResourceUtil can find any.\n\t\t\t\t\t\t_RW();\n\t\t\t\t\t\tswitch (resType) {\n\t\t\t\t\t\tcase null:\n\t\t\t\t\t\t\t//rw.AddResource(name, File.OpenRead(path), closeAfterWrite: true); //no, would not close on error\n\t\t\t\t\t\t\trw.AddResource(name, new MemoryStream(filesystem.loadBytes(path)));\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase \"byte[]\":\n\t\t\t\t\t\t\trw.AddResource(name, filesystem.loadBytes(path));\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase \"string\":\n\t\t\t\t\t\t\trw.AddResource(name, filesystem.loadText(path));\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase \"strings\":\n\t\t\t\t\t\t\tvar csv = csvTable.load(path);\n\t\t\t\t\t\t\tif (csv.ColumnCount != 2) throw new ArgumentException(\"CSV must contain 2 columns separated with ,\");\n\t\t\t\t\t\t\tforeach (var row in csv.Rows) rw.AddResource(row[0], row[1]);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tdefault: throw new ArgumentException(\"error in meta: Incorrect /suffix\");\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t//documented in DProperties: This program does not URL-encode resource names.\n\t\t\t\t\t//\tVS encodes space -> %20, UTF8 -> %xx, ^ -> %5e, etc. But not like .NET URL-encoding functions do.\n\t\t\t\t\t//\tI did not find documentation about it.\n\t\t\t\t\t//\tIf not encoded, pack URI will not work.\n\t\t\t\t\t//\tBut if encoded, ResourceUtil would not work. Or should it URL-encode the parameter?\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t//add XAML icons from strings like \"*name #color\"\n\t\t\tif (meta.Role != MCRole.editorExtension && 0 == (meta.MiscFlags & 1)) {\n\t\t\t\tHashSet<string> hs = null;\n\t\t\t\tfor (int i = 0; i < trees.Length; i++) {\n\t\t\t\t\tList<LiteralExpressionSyntax> ai = null;\n\t\t\t\t\tvar tree = trees[i];\n\t\t\t\t\tvar root = tree.GetCompilationUnitRoot();\n\t\t\t\t\tforeach (var v in root.DescendantNodes()) {\n\t\t\t\t\t\tif (v is LiteralExpressionSyntax les && les.IsKind(SyntaxKind.StringLiteralExpression) && les.Token.Value is string s && s.Length >= 8 && s[0] == '*') {\n\t\t\t\t\t\t\tbool hasLibraryPrefix = s[1] == '<';\n\t\t\t\t\t\t\tif (hasLibraryPrefix) {\n\t\t\t\t\t\t\t\tint j = s.IndexOf('>');\n\t\t\t\t\t\t\t\tif (j < 0 || !s.Eq(2..j, asmName, true)) continue;\n\t\t\t\t\t\t\t\ts = s[++j..];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (!IconString_.DetectAndRemoveParametersForResources(s, out s)) continue; //remove string icon parameters to support icon strings like `\"*Pack.Icon\"+colorConstant` and `$\"*Pack.Icon{colorConstant}\"`. At run time will call IconString_.XamlSetColorSizeMargin to add them.\n\t\t\t\t\t\t\tif (DIcons.TryGetIconFromDB(s, out var xaml, forResource: true)) {\n\t\t\t\t\t\t\t\ts = s.Lower();\n\t\t\t\t\t\t\t\tif ((hs ??= new()).Add(s)) {\n\t\t\t\t\t\t\t\t\t_RW();\n\t\t\t\t\t\t\t\t\trw.AddResource(s, xaml);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (!hasLibraryPrefix && meta.Role == MCRole.classLibrary) (ai ??= new()).Add(les);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (ai != null) { //in library need \"*<asmName>*name #color\"\n\t\t\t\t\t\tvar r2 = root.ReplaceNodes(ai, (n, _) => {\n\t\t\t\t\t\t\tvar s = n.Token.Value as string;\n\t\t\t\t\t\t\tvar tok = SyntaxFactory.Literal($\"*<{asmName}>{s.Lower()}\");\n\t\t\t\t\t\t\treturn SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, tok);\n\t\t\t\t\t\t});\n\t\t\t\t\t\ttrees[i] = tree.WithRootAndOptions(r2, tree.Options) as CSharpSyntaxTree;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tif (rw != null) {\n\t\t\t\trw.Generate();\n\t\t\t\tstream?.Seek(0, SeekOrigin.Begin);\n\t\t\t\tresult((asmName + \".g.resources\", stream, resourcesFile));\n\t\t\t}\n\t\t\t\n\t\t\treturn true;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tfailed(e, curFile);\n\t\t\treturn false;\n\t\t}\n\t\tfinally {\n\t\t\tif (!needStream) rw?.Dispose(); //else Roslyn will close the stream. There is no other disposable data in rw.\n\t\t}\n\t}\n\t\n\tpublic static void CopyFileIfNeed(string sFrom, string sTo) {\n\t\tif (filesystem.GetProp_(sTo, out var p2) && filesystem.GetProp_(sFrom, out var p1) && p2 == p1) return;\n\t\tfilesystem.copy(sFrom, sTo, FIfExists.Delete);\n\t}\n\t\n\t/// <summary>\n\t/// Gathers all files from meta 'file' of <i>meta</i> project and descendant pr projects, and copies to <i>outDir</i> or calls callback for each.\n\t/// </summary>\n\t/// <param name=\"meta\"></param>\n\t/// <param name=\"outDir\"></param>\n\t/// <param name=\"copy\">Called for each file. If null, calls <see cref=\"CopyFileIfNeed\"/>.</param>\n\tpublic static void CopyMetaFileFilesOfAllProjects(MetaComments meta, string outDir, Action<string, string> copy = null) {\n\t\tHashSet<FileNode> noDup = null;\n\t\t_OtherFilesOfProject(meta);\n\t\t\n\t\tvoid _OtherFilesOfProject(MetaComments m) {\n\t\t\tif (m != meta) if (!(noDup ??= new()).Add(m.MainFile.f)) return; //skip duplicates\n\t\t\t\n\t\t\tif (m.OtherFiles != null) {\n\t\t\t\tforeach (var v in m.OtherFiles) {\n\t\t\t\t\tvar dest = outDir;\n\t\t\t\t\tif (!v.s.NE()) dest = pathname.combine(dest, v.s, s2CanBeFullPath: true);\n\t\t\t\t\t\n\t\t\t\t\t_CopyOther(v.f, dest);\n\t\t\t\t\t\n\t\t\t\t\tvoid _CopyOther(FileNode f, string dest) {\n\t\t\t\t\t\tif (f.IsFolder) {\n\t\t\t\t\t\t\tif (!f.Name.Ends('-')) dest = dest + \"\\\\\" + f.Name;\n\t\t\t\t\t\t\tforeach (var c in f.Children()) _CopyOther(c, dest);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tstring from = f.FilePath, to = dest + \"\\\\\" + pathname.getName(from);\n\t\t\t\t\t\t\tif (copy != null) copy(from, to);\n\t\t\t\t\t\t\telse CopyFileIfNeed(from, to);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tif (m.ProjectReferences is { } pr)\n\t\t\t\tforeach (var v in pr) _OtherFilesOfProject(v.m);\n\t\t}\n\t}\n\n#if false //in the past this was used for sqlite. Maybe in the future this code can be useful for something.\n\tpublic static bool UsesSqlite(Stream asmStream, bool recursive = false) {\n\t\tusing var pr = new PEReader(asmStream, PEStreamOptions.LeaveOpen);\n\t\tvar mr = pr.GetMetadataReader();\n\t\t\n\t\t//var usedRefs = mr.AssemblyReferences.Select(handle => mr.GetString(mr.GetAssemblyReference(handle).Name)).ToArray();\n\t\t//print.it(usedRefs);\n\t\t\n\t\tforeach (var handle in mr.TypeReferences) {\n\t\t\tvar tr = mr.GetTypeReference(handle);\n\t\t\t//print.it(mr.GetString(tr.Name), mr.GetString(tr.Namespace));\n\t\t\tstring type = mr.GetString(tr.Name);\n\t\t\tif ((type.Starts(\"sqlite\") && mr.GetString(tr.Namespace) == \"Au\")\n\t\t\t\t|| (type.Starts(\"SL\") && mr.GetString(tr.Namespace) == \"Au.Types\")) return true;\n\t\t}\n\t\t\n\t\tif (recursive) {\n\t\t\tvar fs = asmStream as FileStream; Debug.Assert(fs != null);\n\t\t\tvar dir = pathname.getDirectory(fs.Name, withSeparator: true);\n\t\t\tvar hs = new HashSet<string>();\n\t\t\tforeach (var arh in mr.AssemblyReferences) {\n\t\t\t\tvar ar = mr.GetAssemblyReference(arh);\n\t\t\t\tvar an = mr.GetString(ar.Name);\n\t\t\t\tif (MetaReferences.IsDefaultRef(an)) continue;\n\t\t\t\tif (hs.Contains(an)) continue;\n\t\t\t\tvar path2 = dir + an + \".dll\";\n\t\t\t\tif (!filesystem.exists(path2, useRawPath: true).File) continue;\n\t\t\t\tif (UsesSqlite(path2, true)) return true;\n\t\t\t}\n\t\t}\n\t\t\n\t\treturn false;\n\t}\n\t\n\t/// <summary>\n\t/// Returns true if the .NET assembly uses sqlite types and therefore sqlite3.dll.\n\t/// </summary>\n\t/// <param name=\"dll\">Assembly path.</param>\n\t/// <param name=\"recursive\">Include descendant reference assemblies.</param>\n\tpublic static bool UsesSqlite(string dll, bool recursive = false) {\n\t\tusing var fs = filesystem.loadStream(dll);\n\t\treturn UsesSqlite(fs, recursive);\n\t}\n#endif\n\t\n\tpublic static bool RunPrePostBuildScript(MetaComments m, bool post, string outFile, bool publish) {\n\t\tvar x = post ? m.PostBuild : m.PreBuild;\n\t\tvar f = m.MainFile.f;\n\t\tstring outPath = publish ? pathname.getDirectory(outFile) : m.OutputPath;\n\t\t\n\t\tstring[] args;\n\t\tif (x.s == null) {\n\t\t\targs = new string[] { outFile };\n\t\t} else {\n\t\t\targs = StringUtil.CommandLineToArray(x.s);\n\t\t\t\n\t\t\t//fbc: replace variables like $(variable)\n\t\t\tfor (int i = 0; i < args.Length; i++)\n\t\t\t\tif (args[i].Contains(\"$(\")) {\n\t\t\t\t\targs[i] = (s_rx1 ??= new regexp(@\"\\$\\((\\w+)\\)\")).Replace(args[i], k => k[1].Value switch {\n\t\t\t\t\t\t\"outputFile\" => outFile,\n\t\t\t\t\t\t\"outputPath\" => outPath,\n\t\t\t\t\t\t\"source\" => f.ItemPath,\n\t\t\t\t\t\t\"role\" => m.Role.ToString(),\n\t\t\t\t\t\t\"optimize\" => m.Optimize ? \"true\" : \"false\",\n\t\t\t\t\t\t\"bit32\" => m.Platform == MCPlatform.x86 ? \"true\" : \"false\",\n\t\t\t\t\t\t_ => throw new ArgumentException(\"error in meta: unknown variable \" + k.Value)\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t}\n\t\t\n\t\tbool ok = Compiler.Compile(CCReason.Run, out var r, x.f);\n\t\tif (r.role != MCRole.editorExtension) throw new ArgumentException($\"'{x.f.Name}' role must be editorExtension\");\n\t\tif (!ok) return false;\n\t\t\n\t\tPrePostBuild.Info = new(outFile, outPath, f.ItemPath, m.Role.ToString(), m.Optimize, m.Platform.ToString(), !post, publish);\n\t\ttry { EditorExtension.Run_(r.file, args, handleExceptions: false); }\n\t\tfinally { PrePostBuild.Info = null; }\n\t\treturn true;\n\t}\n\tstatic regexp s_rx1;\n\t\n\tpublic static void DeleteExeFile(string programPath) {\n\t\tif (filesystem.exists(programPath, useRawPath: true) && !Api.DeleteFile(programPath)) {\n\t\t\tvar a = process.getProcessIds(programPath, fullPath: true);\n\t\t\tif (a.Any()) {\n\t\t\t\t//if (!dialog.showYesNo(\"End process?\", $\"Need to delete this program file, but the program is running.\\n\\n{programPath}\", owner: App.Hmain)) return;\n\t\t\t\tforeach (var v in a) process.terminate(v);\n\t\t\t\twait.until(-3, () => Api.DeleteFile(programPath));\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Compiler/util/Compiler_resources.cs",
    "content": "namespace LA;\n\npartial class Compiler {\n\tunsafe class _NativeResources {\n\t\tclass _Res {\n\t\t\tpublic byte[] data;\n\t\t\tpublic ushort resType, resId;\n\t\t\t//don't need to support language and string type/name\n\n\t\t\tpublic _Res(ushort resType, ushort resId, byte[] data) {\n\t\t\t\tthis.data = data;\n\t\t\t\tthis.resType = resType;\n\t\t\t\tthis.resId = resId;\n\t\t\t}\n\t\t}\n\n\t\tList<_Res> _a = new();\n\n\t\tpublic void AddVersion(string asmFile) {\n\t\t\tvar hModule = Api.LoadLibraryEx(asmFile, default, Api.LOAD_LIBRARY_AS_DATAFILE);\n\t\t\tif (hModule == default) _Throw();\n\t\t\ttry { _a.Add(new _Res(16, 1, _LoadNativeResource(hModule, 16, 1))); } //RT_VERSION\n\t\t\tfinally { Api.FreeLibrary(hModule); }\n\t\t}\n\n\t\tpublic void AddManifest(string manifestFile) {\n\t\t\t_a.Add(new _Res(24, 1, File.ReadAllBytes(manifestFile))); //RT_MANIFEST\n\t\t}\n\n\t\tpublic void AddIcon(string iconFile, ref ICONCONTEXT ic)\n\t\t\t=> AddIcon(File.ReadAllBytes(iconFile), ref ic);\n\n\t\tpublic void AddIcon(byte[] iconBytes, ref ICONCONTEXT ic) {\n\t\t\t//info:\n\t\t\t//An icon file begins with NEWHEADER followed by multiple ICONDIRENTRY for each icon.\n\t\t\t//In resource instead of ICONDIRENTRY is RESDIR, where DWORD dwImageOffset ir replaced with WORD id.\n\t\t\t//Note that sizeof(ICONDIRENTRY) is 16 and sizeof(RESDIR) is 14.\n\t\t\t//In icon file then follow multiple ICONIMAGE (or png). Their file offsets are ICONDIRENTRY.dwImageOffset.\n\t\t\t//In resources instead there are multiple RT_ICON resources that are referenced\n\t\t\t//\tby RESDIR.wIconCursorId. An RT_ICON resource contains ICONIMAGE or png.\n\n\t\t\tif (ic.groupId == 0) ic.groupId = Api.IDI_APPLICATION;\n\n\t\t\tvar m = new MemoryStream();\n\t\t\tfixed (byte* mem = iconBytes) {\n\t\t\t\tif (iconBytes.Length <= sizeof(Api.NEWHEADER)) _Throw();\n\t\t\t\tvar ph = (Api.NEWHEADER*)mem;\n\t\t\t\tvar pi = (Api.ICONDIRENTRY*)(ph + 1);\n\t\t\t\tint n = ph->wResCount;\n\t\t\t\tif (iconBytes.Length <= sizeof(Api.NEWHEADER) + n * sizeof(Api.ICONDIRENTRY)) _Throw();\n\t\t\t\tm.Write(new RByte(ph, sizeof(Api.NEWHEADER)));\n\n\t\t\t\tfor (int i = 0; i < n; i++) {\n\t\t\t\t\tApi.ICONDIRENTRY* ide = pi + i;\n\t\t\t\t\tint offset = ide->dwImageOffset, size = ide->dwBytesInRes;\n\t\t\t\t\tif (offset + size > iconBytes.Length) _Throw();\n\t\t\t\t\tushort id = (ushort)(++ic.iconId);\n\t\t\t\t\tide->dwImageOffset = id; //RESDIR.wIconCursorId\n\t\t\t\t\tm.Write(new RByte(ide, sizeof(Api.ICONDIRENTRY) - 2)); //ICONDIRENTRY to RESDIR\n\n\t\t\t\t\t_a.Add(new _Res(3, id, new RByte(mem + offset, size).ToArray())); //RT_ICON\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t_a.Add(new _Res(14, (ushort)ic.groupId++, m.ToArray())); //RT_GROUP_ICON\n\t\t}\n\n\t\tpublic struct ICONCONTEXT { public int groupId, iconId; }\n\n\t\tpublic void AddTpa(string tpa) {\n\t\t\t_a.Add(new _Res(220, 1, Encoding.UTF8.GetBytes(tpa)));\n\t\t}\n\n\t\t//rejected. Rarely used. If need, users can add later, with tools like ResourceHacker. Here makes more difficult because need to support string type/name, language, etc.\n\t\t//public void AddRes(string resFile)\n\t\t//{\n\n\t\t//}\n\n\t\tstatic void _Throw() {\n\t\t\tthrow new AuException(\"*add resources\");\n\t\t}\n\n\t\tstatic bool _LoadNativeResource(IntPtr hModule, ushort resType, ushort resId, out byte* ptr, out int size) {\n\t\t\tptr = null; size = 0;\n\t\t\tvar hRes = Api.FindResource(hModule, resId, resType); if (hRes == default) return false;\n\t\t\tvar hGlob = LoadResource(hModule, hRes); if (hGlob == default) return false;\n\t\t\tptr = LockResource(hGlob);\n\t\t\tsize = SizeofResource(hModule, hRes);\n\t\t\treturn ptr != null && size > 0;\n\t\t\t//info: don't need to free or unlock. It is documented.\n\t\t}\n\n\t\tstatic byte[] _LoadNativeResource(IntPtr hModule, ushort resType, ushort resId) {\n\t\t\tif (!_LoadNativeResource(hModule, resType, resId, out var data, out var size)) _Throw();\n\t\t\treturn new RByte(data, size).ToArray();\n\t\t}\n\n\t\tinternal static string LoadNativeResourceUtf8String_(ushort resType, ushort resId) {\n\t\t\tif (!_LoadNativeResource(default, resType, resId, out var data, out var size)) _Throw();\n\t\t\treturn Encoding.UTF8.GetString(new RByte(data, size));\n\t\t}\n\n\t\tpublic void WriteAll(string exeFile, byte[] exeData, MCPlatform platform, bool console) {\n\t\t\tfixed (byte* pBase = exeData) {\n\t\t\t\tIMAGE_NT_HEADERS64* nth = ImageNtHeader(pBase); if (nth == null) _Throw();\n\t\t\t\tvar oh = &nth->OptionalHeader;\n\t\t\t\tuint fileAlignment = oh->FileAlignment;\n\t\t\t\tuint resRva = oh->SizeOfImage;\n\n\t\t\t\tif (fileAlignment != 512\n\t\t\t\t\t|| (uint)exeData.Length % fileAlignment != 0\n\t\t\t\t\t|| exeData.Length < 2048\n\t\t\t\t\t|| oh->DataDirectory_Resource.VirtualAddress != 0 //must not contain resources\n\t\t\t\t\t) _Throw();\n\n\t\t\t\tvar m = new MemoryStream();\n\t\t\t\tm.Position = exeData.Length; //reserve for updated exeData\n\t\t\t\tif (!_WriteResources(m, resRva)) _Throw();\n\t\t\t\tuint resSize = (uint)(m.Length - exeData.Length), resSizeAligned = Math2.AlignUp(resSize, fileAlignment);\n\t\t\t\tm.SetLength((uint)m.Length + resSizeAligned);\n\n\t\t\t\t//The exe template does not contain resources, therefore .rsrc section does not exist and ImageDirectoryEntryToDataEx fails.\n\t\t\t\t//Could add a placeholder resource, but then it is not the last section...\n\t\t\t\t//But we can simply append new section header here. There is empty space, because sections are aligned at 0x400.\n\t\t\t\tvar ish = (IMAGE_SECTION_HEADER*)((byte*)oh + nth->FileHeader.SizeOfOptionalHeader + nth->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER));\n\t\t\t\tlong ishOffset = (byte*)ish - pBase; if (ishOffset <= 512 || ishOffset > 1024 - sizeof(IMAGE_SECTION_HEADER)) _Throw();\n\n\t\t\t\t//write IMAGE_SECTION_HEADER of .rsrc section\n\t\t\t\tfor (int i = 0; i < 5; i++) ish->Name[i] = (byte)\".rsrc\"[i];\n\t\t\t\tish->VirtualSize = resSize;\n\t\t\t\tish->VirtualAddress = resRva;\n\t\t\t\tish->SizeOfRawData = Math2.AlignUp(resSize, fileAlignment);\n\t\t\t\tish->PointerToRawData = (uint)exeData.Length;\n\t\t\t\tish->Characteristics = 0x40000040;\n\n\t\t\t\tuint resSize2 = Math2.AlignUp(resSize, oh->SectionAlignment);\n\t\t\t\tif (platform == MCPlatform.x86) {\n\t\t\t\t\tvar oh32 = (IMAGE_OPTIONAL_HEADER32*)oh;\n\t\t\t\t\toh32->DataDirectory_Resource.VirtualAddress = resRva;\n\t\t\t\t\toh32->DataDirectory_Resource.Size = resSize;\n\t\t\t\t\toh32->SizeOfInitializedData += resSizeAligned;\n\t\t\t\t\toh32->SizeOfImage += resSize2;\n\t\t\t\t\tif (console) oh32->Subsystem = 3;\n\t\t\t\t\toh32->CheckSum = 0;\n\t\t\t\t} else {\n\t\t\t\t\t//in IMAGE_OPTIONAL_HEADER write RVA/size of .rsrc section\n\t\t\t\t\toh->DataDirectory_Resource.VirtualAddress = resRva;\n\t\t\t\t\toh->DataDirectory_Resource.Size = resSize;\n\n\t\t\t\t\t//update some other fields\n\t\t\t\t\toh->SizeOfInitializedData += resSizeAligned;\n\t\t\t\t\toh->SizeOfImage += resSize2;\n\t\t\t\t\tif (console) oh->Subsystem = 3; //IMAGE_SUBSYSTEM_WINDOWS_CUI\n\t\t\t\t\toh->CheckSum = 0;\n\t\t\t\t}\n\t\t\t\tnth->FileHeader.NumberOfSections++;\n\t\t\t\tnth->FileHeader.TimeDateStamp = 0;\n\n\t\t\t\t//write updated exeData to stream\n\t\t\t\tm.Position = 0;\n\t\t\t\tm.Write(exeData);\n\t\t\t\tm.Position = 0;\n\n\t\t\t\tusing var fs = new FileStream(exeFile, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None);\n\t\t\t\tm.CopyTo(fs);\n\t\t\t}\n\t\t}\n\n\t\tbool _WriteResources(Stream m, uint resRva) {\n\t\t\t//calc number of resource types and ids (sum of unique ids of all types)\n\t\t\t//We can have max 1 version, 1 manifest and multiple RT_ICONGROUP (each + its multiple RT_ICON).\n\n\t\t\t_a.Sort((v1, v2) => {\n\t\t\t\tint r = v1.resType - v2.resType;\n\t\t\t\tif (r == 0) r = v1.resId - v2.resId;\n\t\t\t\treturn r;\n\t\t\t});\n\n\t\t\tint nTypes = 0, nIds = 0, prevType = -1, prevId = -1;\n\t\t\tforeach (var r in _a) {\n\t\t\t\tif (r.resType != prevType) { prevType = r.resType; nTypes++; prevId = -1; }\n\t\t\t\tif (r.resId != prevId) { prevId = r.resId; nIds++; }\n\t\t\t}\n\n\t\t\t//calc offsets and size of all directories\n\t\t\tint sizeResDir = sizeof(IMAGE_RESOURCE_DIRECTORY);\n\t\t\tint sizeDirEntry = sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY);\n\t\t\tint baseNameIdDir = sizeResDir + nTypes * sizeDirEntry;\n\t\t\tint baseLangDir = baseNameIdDir + sizeResDir * nTypes + nIds * sizeDirEntry;\n\t\t\tint baseDataDir = baseLangDir + sizeResDir * nIds + sizeDirEntry * _a.Count;\n\t\t\tint baseResData = baseDataDir + _a.Count * sizeof(IMAGE_RESOURCE_DATA_ENTRY);\n\t\t\tint offsData = baseResData; //temp\n\n\t\t\t//create and write directories\n\t\t\tvar aDir = new byte[baseResData];\n\t\t\tfixed (byte* pDir = aDir) {\n\t\t\t\tvar dT = (IMAGE_RESOURCE_DIRECTORY*)pDir;\n\t\t\t\tvar eT = (IMAGE_RESOURCE_DIRECTORY_ENTRY*)(dT + 1);\n\t\t\t\tIMAGE_RESOURCE_DIRECTORY* dN = null, dL = null;\n\t\t\t\tIMAGE_RESOURCE_DIRECTORY_ENTRY* eN = null, eL = null;\n\t\t\t\tprevType = -1; prevId = -1;\n\t\t\t\tforeach (var r in _a) {\n\t\t\t\t\tif (r.resType != prevType) {\n\t\t\t\t\t\tprevType = r.resType;\n\t\t\t\t\t\tprevId = -1;\n\n\t\t\t\t\t\t//write resource type entry\n\t\t\t\t\t\tdT->NumberOfIdEntries++;\n\t\t\t\t\t\teT->Id = r.resType;\n\t\t\t\t\t\teT->OffsetToData = baseNameIdDir | unchecked((int)0x80000000);\n\t\t\t\t\t\teT++;\n\n\t\t\t\t\t\t//write directory of resource ids\n\t\t\t\t\t\tdN = (IMAGE_RESOURCE_DIRECTORY*)(pDir + baseNameIdDir);\n\t\t\t\t\t\tbaseNameIdDir += sizeResDir;\n\t\t\t\t\t\teN = (IMAGE_RESOURCE_DIRECTORY_ENTRY*)(dN + 1);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (r.resId != prevId) {\n\t\t\t\t\t\tprevId = r.resId;\n\n\t\t\t\t\t\t//write resource id entry\n\t\t\t\t\t\tdN->NumberOfIdEntries++;\n\t\t\t\t\t\teN->Id = r.resId;\n\t\t\t\t\t\teN->OffsetToData = baseLangDir | unchecked((int)0x80000000);\n\t\t\t\t\t\teN++;\n\t\t\t\t\t\tbaseNameIdDir += sizeDirEntry;\n\n\t\t\t\t\t\t//write directory of languages\n\t\t\t\t\t\tdL = (IMAGE_RESOURCE_DIRECTORY*)(pDir + baseLangDir);\n\t\t\t\t\t\tbaseLangDir += sizeResDir;\n\t\t\t\t\t\teL = (IMAGE_RESOURCE_DIRECTORY_ENTRY*)(dL + 1);\n\t\t\t\t\t}\n\n\t\t\t\t\t//write language entry\n\t\t\t\t\tdL->NumberOfIdEntries++;\n\t\t\t\t\teL->Id = 0;\n\t\t\t\t\teL->OffsetToData = baseDataDir;\n\t\t\t\t\teL++;\n\t\t\t\t\tbaseLangDir += sizeDirEntry;\n\n\t\t\t\t\t//write data entry\n\t\t\t\t\tvar de = (IMAGE_RESOURCE_DATA_ENTRY*)(pDir + baseDataDir);\n\t\t\t\t\tbaseDataDir += sizeof(IMAGE_RESOURCE_DATA_ENTRY);\n\t\t\t\t\tde->Size = (uint)r.data.Length;\n\t\t\t\t\tde->OffsetToData = resRva + (uint)offsData; //RVA\n\t\t\t\t\toffsData += Math2.AlignUp(r.data.Length, 4);\n\t\t\t\t}\n\t\t\t}\n\t\t\tm.Write(aDir);\n\n\t\t\t//write resource data\n\t\t\tforeach (var r in _a) {\n\t\t\t\tm.Write(r.data);\n\t\t\t\tfor (int k = Math2.AlignUp(r.data.Length, 4) - r.data.Length; k > 0; k--) m.WriteByte(0); //4-align\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\n\t\t[DllImport(\"kernel32.dll\")]\n\t\tinternal static extern IntPtr LoadResource(IntPtr hModule, IntPtr hResInfo);\n\n\t\t[DllImport(\"kernel32.dll\")]\n\t\tinternal static extern byte* LockResource(IntPtr hResData);\n\n\t\t[DllImport(\"kernel32.dll\")]\n\t\tinternal static extern int SizeofResource(IntPtr hModule, IntPtr hResInfo);\n\n\t\t[DllImport(\"dbghelp.dll\")]\n\t\tinternal static extern IMAGE_NT_HEADERS64* ImageNtHeader(byte* Base);\n\n#pragma warning disable 649 //field never assigned\n\n\t\tinternal struct IMAGE_SECTION_HEADER {\n\t\t\tpublic fixed byte Name[8];\n\t\t\tpublic uint VirtualSize;\n\t\t\tpublic uint VirtualAddress;\n\t\t\tpublic uint SizeOfRawData;\n\t\t\tpublic uint PointerToRawData;\n\t\t\tpublic uint PointerToRelocations;\n\t\t\tpublic uint PointerToLinenumbers;\n\t\t\tpublic ushort NumberOfRelocations;\n\t\t\tpublic ushort NumberOfLinenumbers;\n\t\t\tpublic uint Characteristics;\n\t\t}\n\n\t\tinternal struct IMAGE_DATA_DIRECTORY {\n\t\t\tpublic uint VirtualAddress;\n\t\t\tpublic uint Size;\n\t\t}\n\n\t\t[StructLayout(LayoutKind.Sequential, Pack = 4)]\n\t\tinternal struct IMAGE_OPTIONAL_HEADER32 {\n\t\t\tpublic ushort Magic;\n\t\t\tpublic byte MajorLinkerVersion;\n\t\t\tpublic byte MinorLinkerVersion;\n\t\t\tpublic uint SizeOfCode;\n\t\t\tpublic uint SizeOfInitializedData;\n\t\t\tpublic uint SizeOfUninitializedData;\n\t\t\tpublic uint AddressOfEntryPoint;\n\t\t\tpublic uint BaseOfCode;\n\t\t\tpublic uint BaseOfData;\n\t\t\tpublic uint ImageBase;\n\t\t\tpublic uint SectionAlignment;\n\t\t\tpublic uint FileAlignment;\n\t\t\tpublic ushort MajorOperatingSystemVersion;\n\t\t\tpublic ushort MinorOperatingSystemVersion;\n\t\t\tpublic ushort MajorImageVersion;\n\t\t\tpublic ushort MinorImageVersion;\n\t\t\tpublic ushort MajorSubsystemVersion;\n\t\t\tpublic ushort MinorSubsystemVersion;\n\t\t\tpublic uint Win32VersionValue;\n\t\t\tpublic uint SizeOfImage;\n\t\t\tpublic uint SizeOfHeaders;\n\t\t\tpublic uint CheckSum;\n\t\t\tpublic ushort Subsystem;\n\t\t\tpublic ushort DllCharacteristics;\n\t\t\tpublic uint SizeOfStackReserve;\n\t\t\tpublic uint SizeOfStackCommit;\n\t\t\tpublic uint SizeOfHeapReserve;\n\t\t\tpublic uint SizeOfHeapCommit;\n\t\t\tpublic uint LoaderFlags;\n\t\t\tpublic uint NumberOfRvaAndSizes;\n\t\t\tIMAGE_DATA_DIRECTORY _d0, _d1;\n\t\t\tpublic IMAGE_DATA_DIRECTORY DataDirectory_Resource;\n\t\t}\n\n\t\t[StructLayout(LayoutKind.Sequential, Pack = 4)]\n\t\tinternal struct IMAGE_OPTIONAL_HEADER64 {\n\t\t\tpublic ushort Magic;\n\t\t\tpublic byte MajorLinkerVersion;\n\t\t\tpublic byte MinorLinkerVersion;\n\t\t\tpublic uint SizeOfCode;\n\t\t\tpublic uint SizeOfInitializedData;\n\t\t\tpublic uint SizeOfUninitializedData;\n\t\t\tpublic uint AddressOfEntryPoint;\n\t\t\tpublic uint BaseOfCode;\n\t\t\tpublic ulong ImageBase;\n\t\t\tpublic uint SectionAlignment;\n\t\t\tpublic uint FileAlignment;\n\t\t\tpublic ushort MajorOperatingSystemVersion;\n\t\t\tpublic ushort MinorOperatingSystemVersion;\n\t\t\tpublic ushort MajorImageVersion;\n\t\t\tpublic ushort MinorImageVersion;\n\t\t\tpublic ushort MajorSubsystemVersion;\n\t\t\tpublic ushort MinorSubsystemVersion;\n\t\t\tpublic uint Win32VersionValue;\n\t\t\tpublic uint SizeOfImage;\n\t\t\tpublic uint SizeOfHeaders;\n\t\t\tpublic uint CheckSum;\n\t\t\tpublic ushort Subsystem;\n\t\t\tpublic ushort DllCharacteristics;\n\t\t\tpublic ulong SizeOfStackReserve;\n\t\t\tpublic ulong SizeOfStackCommit;\n\t\t\tpublic ulong SizeOfHeapReserve;\n\t\t\tpublic ulong SizeOfHeapCommit;\n\t\t\tpublic uint LoaderFlags;\n\t\t\tpublic uint NumberOfRvaAndSizes;\n\t\t\tIMAGE_DATA_DIRECTORY _d0, _d1;\n\t\t\tpublic IMAGE_DATA_DIRECTORY DataDirectory_Resource;\n\t\t}\n\n\t\tinternal struct IMAGE_FILE_HEADER {\n\t\t\tpublic ushort Machine;\n\t\t\tpublic ushort NumberOfSections;\n\t\t\tpublic uint TimeDateStamp;\n\t\t\tpublic uint PointerToSymbolTable;\n\t\t\tpublic uint NumberOfSymbols;\n\t\t\tpublic ushort SizeOfOptionalHeader;\n\t\t\tpublic ushort Characteristics;\n\t\t}\n\n\t\t[StructLayout(LayoutKind.Sequential, Pack = 4)]\n\t\tinternal struct IMAGE_NT_HEADERS64 {\n\t\t\tpublic uint Signature;\n\t\t\tpublic IMAGE_FILE_HEADER FileHeader;\n\t\t\tpublic IMAGE_OPTIONAL_HEADER64 OptionalHeader;\n\t\t}\n\n\t\tinternal struct IMAGE_RESOURCE_DIRECTORY {\n\t\t\tpublic uint Characteristics;\n\t\t\tpublic uint TimeDateStamp;\n\t\t\tpublic ushort MajorVersion;\n\t\t\tpublic ushort MinorVersion;\n\t\t\tpublic ushort NumberOfNamedEntries;\n\t\t\tpublic ushort NumberOfIdEntries;\n\t\t}\n\n\t\tinternal struct IMAGE_RESOURCE_DIRECTORY_ENTRY {\n\t\t\t//simplified, does not support name as string\n\t\t\tpublic ushort Id;\n\t\t\tpublic int OffsetToData;\n\t\t}\n\n\t\tinternal struct IMAGE_RESOURCE_DATA_ENTRY {\n\t\t\tpublic uint OffsetToData;\n\t\t\tpublic uint Size;\n\t\t\tpublic uint CodePage;\n\t\t\tpublic uint Reserved;\n\t\t}\n\n\t\t//Icon resource entry (part of RT_GROUP_ICON)\n\t\t//[StructLayout(LayoutKind.Sequential, Pack = 2)]\n\t\t//struct RESDIR\n\t\t//{\n\t\t//\tpublic ICONRESDIR ird;\n\t\t//\tpublic ushort wPlanes;\n\t\t//\tpublic ushort wBitCount;\n\t\t//\tpublic uint dwBytesInRes;\n\t\t//\tpublic ushort wIconCursorId;\n\t\t//};\n\n\t\t//Icon image in file. RT_ICON resource is the same.\n\t\t//[StructLayout(LayoutKind.Sequential, Pack = 2)]\n\t\t//struct ICONIMAGE\n\t\t//{\n\t\t//\tpublic BITMAPINFOHEADER icHeader;\n\t\t//\tpublic RGBQUAD icColors[1];\n\t\t//\tpublic byte icXOR[1];\n\t\t//\tpublic byte icAND[1];\n\t\t//};\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Debugger/PD-stack.cs",
    "content": "using Au.Controls;\n\nnamespace LA;\n\npartial class PanelDebug {\n\tKTreeView _tvStack;\n\t_StackViewItem[] _aStack;\n\t\n\tvoid _StackViewInit() {\n\t\t_tvStack = new() { Name = \"Callstack_list\", SingleClickActivate = true };\n\t\t_tvStack.ItemActivated += _tvStack_ItemActivated;\n\t}\n\t\n\tvoid _StackViewSetItems(_FRAME[] a, int iSelect = 0) {\n\t\tif (a == null) {\n\t\t\t_aStack = null;\n\t\t} else {\n\t\t\t_aStack = new _StackViewItem[a.Length];\n\t\t\tfor (int i = 0; i < a.Length; i++) {\n\t\t\t\t_aStack[i] = new(this, a[i]);\n\t\t\t}\n\t\t}\n\t\t\n\t\t_tvStack.SetItems(_aStack);\n\t\tif (a != null) _tvStack.SelectSingle(iSelect, andFocus: true);\n\t}\n\t\n\tvoid _tvStack_ItemActivated(TVItemEventArgs e) {\n\t\tif (!IsStopped) return;\n\t\tvar v = e.Item as _StackViewItem;\n\t\t_s.frame = v.f;\n\t\t_GoToLine(v.f, keepMarkers: true);\n\t\t//_VariablesViewChangedFrameOrThread();\n\t\t_ListVariables();\n\t}\n\t\n\tclass _StackViewItem : ITreeViewItem {\n\t\treadonly PanelDebug _panel;\n\t\treadonly public _FRAME f;\n\t\treadonly string _text;\n\t\t\n\t\tpublic _StackViewItem(PanelDebug panel, _FRAME f) {\n\t\t\t_panel = panel;\n\t\t\tthis.f = f;\n\t\t\t_text = _panel._FormatFrameString(f);\n\t\t}\n\t\t\n\t\t//ITreeViewItem\n\t\t\n\t\tstring ITreeViewItem.DisplayText => _text;\n\t\t\n\t\tTVParts ITreeViewItem.NoParts => TVParts.Image;\n\t\t\n\t\t//public bool IsDisabled => f.file.NE();\n\t\t\n\t\t//bool ITreeViewItem.IsSelectable => !IsDisabled;\n\t\t\n\t\tint ITreeViewItem.TextColor(TVColorInfo ci) => f.file.NE() ? 0x808080 : -1;\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Debugger/PD-variables.cs",
    "content": "extern alias CAW;\n\nusing Microsoft.CodeAnalysis;\nusing CAW::Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp;\nusing Microsoft.CodeAnalysis.CSharp.Syntax;\nusing Microsoft.CodeAnalysis.Text;\nusing Microsoft.CodeAnalysis.CSharp.Extensions;\nusing Microsoft.CodeAnalysis.Shared.Extensions;\nusing CAW::Microsoft.CodeAnalysis.Shared.Extensions;\n\nusing Au.Controls;\nusing System.Windows.Input;\n\n//CONSIDER: save watches.\n//CONSIDER: add separate \"Watch\" treeview. Also move <mouse> there.\n\nnamespace LA;\n\npartial class PanelDebug {\n\tKTreeView _tvVariables;\n\t_VariablesViewItem[] _aVar;\n\t_VariablesViewItem _watch;\n\t\n\tvoid _VariablesViewInit() {\n\t\t_tvVariables = new() { Name = \"Variables_list\", SingleClickActivate = true };\n\t\t_tvVariables.ItemClick += _tvVariables_ItemClick;\n\t\t_tvVariables.ItemActivated += e => { ((_VariablesViewItem)e.Item).Print(); };\n\t}\n\t\n\tvoid _ListVariables() {\n\t\tif (_s.frame.clr_addr.module_id.NE()) { //f.func \"[Native Frames]\". Would be ^error.\n\t\t\t_VariablesViewSetItems(null);\n\t\t\treturn;\n\t\t}\n\t\t_d.Send($\"-stack-list-variables --thread {_s.threadId} --frame {_s.frame.level}\");\n\t}\n\t\n\t//Rejected. See comment in VarDelete.\n\t//Called when the user selects a frame or thread.\n\t//Calls -var-delete for all _aVar items.\n\t//Not really necessary, just frees some memory in the debugger process now. Debugger itself deletes all variables on step/continue.\n\t//void _VariablesViewChangedFrameOrThread() {\n\t//\tif (_aVar == null) return;\n\t//\tforeach (var v in _aVar) v.VarDelete();\n\t//}\n\t\n\tvoid _VariablesViewSetItems(_VARIABLE[] a) {\n\t\tif (a.NE_()) {\n\t\t\t_aVar = a != null ? new _VariablesViewItem[2] : null;\n\t\t} else {\n\t\t\t_aVar = new _VariablesViewItem[a.Length + 2];\n\t\t\tfor (int i = 0; i < a.Length; i++) {\n\t\t\t\t_aVar[i + 2] = new(this, a[i]);\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (_aVar != null) {\n\t\t\t_aVar[0] = new _VariablesViewItem(this, _VVItemKind.Mouse);\n\t\t\tif (_watch == null) {\n\t\t\t\t_aVar[1] = _watch = new(this, _VVItemKind.Watch);\n\t\t\t} else {\n\t\t\t\t_aVar[1] = _watch;\n\t\t\t\tforeach (var v in _watch.Children) v.UpdateWatch();\n\t\t\t}\n\t\t}\n\t\t\n\t\t_tvVariables.SetItems(_aVar);\n\t}\n\t\n\t//called by CiQuickInfo when mouse hovers on a non-literal token\n\tinternal void SciMouseHover_(CodeInfo.Context cd) {\n\t\tif (!IsStopped || _aVar.NE_()) return;\n\t\t\n\t\t//get expression string\n\t\t//It can be a variable/field/property name, x.y, x[y] or similar.\n\t\t\n\t\tstring exp = null;\n\t\tint pos = cd.pos;\n\t\tvar tok = cd.syntaxRoot.FindToken(pos);\n\t\tvar node = tok.Parent;\n\t\t//CiUtil.PrintNode(tok);\n\t\t//CiUtil.PrintNode(node);\n\t\tvar tk = tok.Kind();\n\t\tif (tk == SyntaxKind.IdentifierToken) {\n\t\t\tif (node is VariableDeclaratorSyntax vds) {\n\t\t\t\tif (vds.Identifier.Span.ContainsOrTouches(pos)) exp = tok.Text;\n\t\t\t} else if (node is ParameterSyntax ps) {\n\t\t\t\tif (ps.Identifier.Span.ContainsOrTouches(pos)) exp = tok.Text;\n\t\t\t} else if (node is PropertyDeclarationSyntax pds) {\n\t\t\t\tif (pds.Identifier.Span.ContainsOrTouches(pos)) exp = tok.Text;\n\t\t\t} else if (node is SingleVariableDesignationSyntax svds) { //`Call(out var tok)` or `if (x is Y tok)` or tuple etc\n\t\t\t\tif (svds.Identifier.Span.ContainsOrTouches(pos)) exp = tok.Text;\n\t\t\t} else if (node is IdentifierNameSyntax /*or GenericNameSyntax*/) {\n\t\t\t\tif (cd.semanticModel.GetSymbolInfo(node).Symbol is ISymbol sym) {\n\t\t\t\t\tif (sym.Kind is SymbolKind.Field or SymbolKind.Local or SymbolKind.Parameter or SymbolKind.Property) {\n\t\t\t\t\t\tif (sym is IPropertySymbol ips && ips.IsWriteOnly) {\n\t\t\t\t\t\t} else if (tok.GetPreviousToken().Kind() is SyntaxKind.DotToken or SyntaxKind.MinusGreaterThanToken) {\n\t\t\t\t\t\t\texp = cd.code[_FindStartOfMemberAccessOrElementAccess(node)..tok.Span.End];\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\texp = tok.Text;\n\t\t\t\t\t\t}\n\t\t\t\t\t//} else if (sym.Kind is SymbolKind.Method && node.GetAncestor<InvocationExpressionSyntax>() is { } ies) { //for testing only\n\t\t\t\t\t//\tvar span = ies.Span;\n\t\t\t\t\t//\tint i = span.Start;\n\t\t\t\t\t//\tif (cd.code[i] == '.') i = _FindStartOfMemberAccessOrElementAccess(ies);\n\t\t\t\t\t//\texp = cd.code[i..span.End];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (node is BracketedArgumentListSyntax && node.Parent is ElementAccessExpressionSyntax or ElementBindingExpressionSyntax) {\n\t\t\t//if (App.Settings.debug.noFuncEval) return; //debugger crashes if it's an indexer other than of string or array. And with this flag would fail anyway.\n\t\t\texp = cd.code[_FindStartOfMemberAccessOrElementAccess(node)..node.Span.End];\n\t\t} else return;\n\t\t\n\t\tint _FindStartOfMemberAccessOrElementAccess(SyntaxNode node) {\n\t\t\tvar r = node.Parent;\n\t\t\tfor (var n = r; n is MemberAccessExpressionSyntax or ConditionalAccessExpressionSyntax or ElementAccessExpressionSyntax or ElementBindingExpressionSyntax or MemberBindingExpressionSyntax or InvocationExpressionSyntax; n = n.Parent) {\n\t\t\t\tif (n is MemberAccessExpressionSyntax or ConditionalAccessExpressionSyntax) r = n;\n\t\t\t}\n\t\t\treturn r.SpanStart;\n\t\t}\n\t\t\n\t\tif (exp != null) {\n\t\t\t//print.it(exp);\n\t\t\t//note: can be `MethodCall().member', `x[MethodCall()]` etc, not only variables, properties, indexers and operators.\n\t\t\t//info: netcoredbg does not support many expressions.\n\t\t\t//\tEg `x->y`, `n::x.y`, `stringOrArray.Length`, `string[0]`, `x[true ? 0 : 1]`, `x[Index]`, `x[a..b]`, `((C2)cc).P`, `x++`, typeof, as, is, Method<T>.\n\t\t\t//\tSee StackMachine.cs in ManagedPart project.\n\t\t\t//\tIssue: https://github.com/Samsung/netcoredbg/issues/132\n\t\t\t\n\t\t\t//get frame\n\t\t\t\n\t\t\tSyntaxNode _GetEnclosingFunction(SyntaxNode n) {\n\t\t\t\tfor (; n != null; n = n.Parent)\n\t\t\t\t\tif (n is BaseMethodDeclarationSyntax or LocalFunctionStatementSyntax or BasePropertyDeclarationSyntax or EventDeclarationSyntax or AnonymousFunctionExpressionSyntax) break;\n\t\t\t\treturn n;\n\t\t\t}\n\t\t\t\n\t\t\tint frame = 0;\n\t\t\tif (_aStack.Length > 1) {\n\t\t\t\tvar path = cd.sci.EFile.FilePath;\n\t\t\t\tvar ef = _GetEnclosingFunction(node);\n\t\t\t\tif (ef == null) { //TLS\n\t\t\t\t\tif (node.HasAncestor<GlobalStatementSyntax>()) frame = _aStack.Length - 1; //else pos is in a class but not in a func\n\t\t\t\t} else {\n\t\t\t\t\tfor (int i = 0; i < _aStack.Length; i++) {\n\t\t\t\t\t\tvar f = _aStack[i].f;\n\t\t\t\t\t\tif (f.fullname != path) continue;\n\t\t\t\t\t\tint p = cd.sci.aaaLineStart(true, f.line - 1) + f.col - 1; //position of current statement in frame i\n\t\t\t\t\t\tvar ef2 = _GetEnclosingFunction(cd.syntaxRoot.FindToken(p).Parent);\n\t\t\t\t\t\tif (ef2 == ef) { frame = i; break; }\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t//print.it(frame);\n\t\t\t\n\t\t\t//create MI variable, and display it as the first item in _tvVariables\n\t\t\t\n\t\t\tif (_VarCreate(exp, frame) is _VAR r) {\n\t\t\t\t//_aVar[0].VarDelete();\n\t\t\t\t_aVar[0] = new(this, null, r, _VVItemKind.Mouse);\n\t\t\t\t_tvVariables.SetItems(_aVar, true);\n\t\t\t\t_tvVariables.EnsureVisible(0);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\t\n\t\t//_aVar[0].VarDelete();\n\t\t_aVar[0] = new(this, _VVItemKind.Mouse);\n\t\t_tvVariables.SetItems(_aVar, true);\n\t}\n\t\n\t_VAR _VarCreate(string exp, int frame = -1) {\n\t\texp = _EscapeExpression(exp);\n\t\tif (frame < 0) frame = _s.frame.level;\n\t\tint evalFlags = 0; //enum_EVALFLAGS, https://learn.microsoft.com/en-us/visualstudio/extensibility/debugger/reference/evalflags\n\t\t//if (App.Settings.debug.noFuncEval) evalFlags |= 0x80; //EVAL_NOFUNCEVAL. Rejected. Not useful, just makes code more complex. Also tested EVAL_NOSIDEEFFECTS and EVAL_ALLOWERRORREPORT; it seems they do nothing.\n\t\tif (_d.SendSync(100, $\"-var-create - {exp} --thread {_s.threadId} --frame {frame} --evalFlags {evalFlags}\") is string s) {\n\t\t\t//print.it(\"VAR\", s);\n\t\t\tif (s.Starts(\"^done,name=\")) return new _MiRecord(s).Data<_VAR>();\n\t\t\tDebug_.Print($\"<c orange>{s}<>\");\n\t\t}\n\t\treturn null;\n\t\t//tested: debugger has a 5-10 s timeout for expression evaluation.\n\t}\n\t\n\t_VAR _VarCreateL(string exp) {\n\t\tif (_d.SendSync(100, $\"-var-create - {exp}\") is string s) {\n\t\t\tif (s.Starts(\"^done,name=\")) return new _MiRecord(s).Data<_VAR>();\n\t\t\tDebug_.Print($\"<c orange>{s}<>\");\n\t\t}\n\t\treturn null;\n\t}\n\t\n\tvoid _WatchAdd(string exp, int frame = -1) {\n\t\tif (_VarCreate(exp, frame) is _VAR r) {\n\t\t\t_VariablesViewItem v = new(this, _watch, r, _VVItemKind.Watch);\n\t\t\t_watch.AddWatch(v);\n\t\t\t_tvVariables.SetItems(_aVar, true);\n\t\t\t_tvVariables.Expand(1, true);\n\t\t\t_tvVariables.SelectSingle(v);\n\t\t}\n\t}\n\t\n\tvoid _WatchRemove(_VariablesViewItem v) {\n\t\t_watch.RemoveWatch(v);\n\t\t_tvVariables.SetItems(_aVar, true);\n\t}\n\t\n\tstatic string _EscapeExpression(string s) {\n\t\ts = s.RxReplace(@\"[\\r\\n\\t]+\", \" \").Escape();\n\t\ts = '\"' + s + '\"';\n\t\treturn s;\n\t}\n\t\n\tvoid _tvVariables_ItemClick(TVItemEventArgs e) {\n\t\tvar v = e.Item as _VariablesViewItem;\n\t\tif (e.Button == MouseButton.Right && e.ClickCount == 1 && e.Mod == 0) {\n\t\t\tvar m = new popupMenu();\n\t\t\t\n\t\t\tif (v.itemKind == _VVItemKind.Watch) {\n\t\t\t\tif (v.Parent == null) m[\"Clear\"] = o => _WatchRemove(null);\n\t\t\t\telse m[\"Remove\"] = o => _WatchRemove(v);\n\t\t\t} else {\n\t\t\t\tif (v.Exp == null) return; //<mouse>, <static>\n\t\t\t\tm[\"Watch\"] = o => _WatchAdd(v.ExpPath());\n\t\t\t}\n\t\t\t\n\t\t\tm.Show(owner: _tvVariables);\n\t\t}\n\t}\n\tstatic Stack<string> s_pathStack;\n\t\n\tclass _VariablesViewItem : ITreeViewItem {\n\t\treadonly PanelDebug _panel;\n\t\t_VAR _v;\n\t\tstring _text, _exp;\n\t\tbool _isFolder;\n\t\tbool _isExpanded;\n\t\tpublic readonly _VVItemKind itemKind;\n\t\t_VariablesViewItem[] _children;\n\t\t\n\t\tpublic _VariablesViewItem(PanelDebug panel, _VARIABLE v) {\n\t\t\t_panel = panel;\n\t\t\t_exp = v.name;\n\t\t\t_SetTextAndIsFolder(v.value);\n\t\t}\n\t\t\n\t\tvoid _SetTextAndIsFolder(string value) {\n\t\t\tif (value != null) {\n\t\t\t\t_text = $\"{_exp}={value.Limit(8000)}\";\n\t\t\t\t_isFolder = value.Starts('{') && (_v != null ? _v.numchild > 0 : !value.Ends(\"[0]}\"));\n\t\t\t} else _text = _exp;\n\t\t}\n\t\t\n\t\tpublic _VariablesViewItem(PanelDebug panel, _VariablesViewItem parent, _VAR v, _VVItemKind itemKind) {\n\t\t\t_panel = panel;\n\t\t\tParent = parent;\n\t\t\tthis.itemKind = itemKind;\n\t\t\t_v = v;\n\t\t\tif (v.value.NE() && v.exp == \"Static members\") {\n\t\t\t\t_text = \"<static>\";\n\t\t\t\t_isFolder = true;\n\t\t\t} else {\n\t\t\t\tvar s = v.exp; if (s.Ends('.')) s = \"*\" + s[..^1];\n\t\t\t\t_exp = s;\n\t\t\t\t_SetTextAndIsFolder(v.value);\n\t\t\t}\n\t\t}\n\t\t\n\t\t//Adds <mouse> or <watch>.\n\t\tpublic _VariablesViewItem(PanelDebug panel, _VVItemKind itemKind) {\n\t\t\t_panel = panel;\n\t\t\tthis.itemKind = itemKind;\n\t\t\tbool isWatch = itemKind == _VVItemKind.Watch;\n\t\t\t_text = isWatch ? \"<watch>\" : \"<mouse>\";\n\t\t\tif (isWatch) _children = [];\n\t\t}\n\t\t\n\t\t//public void VarDelete() {\n\t\t//\tif (_v == null) return;\n\t\t//\t//_panel._d.SendSync(102, $\"-var-delete {v.name}\"); //no. Somehow then every other -var-list-children fails.\n\t\t//\t_v = null;\n\t\t//\t_isFolder = _isExpanded = false;\n\t\t//\t_children = null;\n\t\t//}\n\t\t\n\t\tpublic _VariablesViewItem Parent { get; }\n\t\t\n\t\tpublic _VariablesViewItem[] Children => _children;\n\t\t\n\t\tpublic string Exp => _exp;\n\t\t\n\t\tpublic string ExpPath() {\n\t\t\tvar p = this;\n\t\t\tint n = 0; for (; p?._exp != null; p = p.Parent) n++;\n\t\t\tstring r;\n\t\t\tif (n == 1) r = _exp;\n\t\t\telse {\n\t\t\t\tvar s = s_pathStack ??= new();\n\t\t\t\ts.Clear();\n\t\t\t\tfor (p = this; p?._exp != null; p = p.Parent) s.Push(p._exp);\n\t\t\t\tr = string.Join('.', s).Replace(\".[\", \"[\");\n\t\t\t}\n\t\t\tif (p?.Parent?._v is { } u) r = u.type + \".\" + r; //if in <static>, prepend type\n\t\t\treturn r;\n\t\t}\n\t\t\n\t\tpublic void AddWatch(_VariablesViewItem v) {\n\t\t\tif (_children.NE_()) _children = [v]; else _children = _children.InsertAt(-1, v);\n\t\t\t_isFolder = true;\n\t\t}\n\t\t\n\t\t//Deletes all if v null.\n\t\tpublic void RemoveWatch(_VariablesViewItem v) {\n\t\t\tif (v == null) {\n\t\t\t\t_children = [];\n\t\t\t} else {\n\t\t\t\tvar i = Array.IndexOf(_children, v);\n\t\t\t\tif (i < 0) return;\n\t\t\t\t_children = _children.RemoveAt(i);\n\t\t\t}\n\t\t\t_isFolder = _children.Length > 0;\n\t\t}\n\t\t\n\t\tpublic void UpdateWatch() {\n\t\t\tif (_panel._VarCreate(_exp) is _VAR v) {\n\t\t\t\t_v = v;\n\t\t\t\t_SetTextAndIsFolder(v.value);\n\t\t\t} else {\n\t\t\t\t_v = null;\n\t\t\t\t_isFolder = false;\n\t\t\t\t_text = $\"{_exp}=\";\n\t\t\t}\n\t\t\t_children = null;\n\t\t}\n\t\t\n\t\tpublic void Print() {\n\t\t\tif (_exp == null) return;\n\t\t\tif (!_EnsureHaveVar()) return;\n\t\t\tif (_v.value?.Ends('}') != false) {\n\t\t\t\tvar s1 = App.Settings.debug.printVarCompact ? \"Compact\" : \"\";\n\t\t\t\tstring t = _v.type, e = ExpPath();\n\t\t\t\tif (t is \"IntPtr\" or \"UIntPtr\" or \"int\" or \"uint\" or \"long\" or \"ulong\" or \"short\" or \"ushort\" or \"byte\" or \"sbyte\" or \"double\" or \"float\" or \"char\" or \"bool\") {\n\t\t\t\t\t//actually object or dynamic. Would fail or get garbage.\n\t\t\t\t\te = $\"{e}.ToString()\";\n\t\t\t\t\tt = \"string\";\n\t\t\t\t\t//} else if (t.Ends(\"[]\")) { //array. Fails everything I tried.\n\t\t\t\t\t//t=\"System.Array\";\n\t\t\t\t\t//s1 = \"Array\";\n\t\t\t\t\t//t = t[..^2];\n\t\t\t\t}\n\t\t\t\tvar k = $\"Au.More.LaDebugger_<{t}>.Print{s1}({e})\";\n\t\t\t\t//print.it(k);\n\t\t\t\t//print.it(_v);\n\t\t\t\tif (_panel._VarCreate(k) is { } v) {\n\t\t\t\t\tif (v.value.Starts('\"')) {\n\t\t\t\t\t\tvar s = Encoding.UTF8.GetString(Convert.FromBase64String(v.value[1..^1]));\n\t\t\t\t\t\tprint.it(_AndHex(s));\n\t\t\t\t\t} else {\n\t\t\t\t\t\tprint.it(v.value); //{Some.Exception}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t} else if (_v.value.Ends('\"')) {\n\t\t\t\tprint.it(_v.value[1..^1].Unescape());\n\t\t\t} else if (_v.value.Starts('<')) {\n\t\t\t\t//\"<error>\". Eg because of option \"Don't call functions to get values\" (rejected).\n\t\t\t} else {\n\t\t\t\tprint.it(_AndHex(_v.value));\n\t\t\t}\n\t\t\t\n\t\t\tstring _AndHex(string s) {\n\t\t\t\tvar t = _v.type.TrimEnd('*');\n\t\t\t\tif (t is \"IntPtr\" or \"UIntPtr\" or \"int\" or \"uint\" or \"long\" or \"ulong\" or \"short\" or \"ushort\" or \"byte\" or \"sbyte\") {\n\t\t\t\t\tif (s.ToInt(out long x)) s = $\"{s}  0x{x:X}\";\n\t\t\t\t} else if (t is \"char\" && s.Length == 1) {\n\t\t\t\t\tvar v = s == \"'\" ? @\"\\'\" : s == \"\\\"\" ? s : s.Escape();\n\t\t\t\t\ts = $\"{(int)s[0]} '{v}'\";\n\t\t\t\t}\n\t\t\t\treturn s;\n\t\t\t}\n\t\t}\n\t\t\n\t\tbool _EnsureHaveVar() {\n\t\t\tif (_v == null && _panel._VarCreate(_exp) is { } v) {\n\t\t\t\t_v = v;\n\t\t\t}\n\t\t\treturn _v != null;\n\t\t}\n\t\t\n\t\t//ITreeViewItem\n\t\t\n\t\tstring ITreeViewItem.DisplayText => _text;\n\t\t\n\t\tIEnumerable<ITreeViewItem> ITreeViewItem.Items {\n\t\t\tget {\n\t\t\t\tif (!_panel.IsStopped) return Array.Empty<_VariablesViewItem>();\n\t\t\t\tif (_children == null) {\n\t\t\t\t\tif (_EnsureHaveVar() && _v.numchild > 0) {\n\t\t\t\t\t\t//note: if without `--all-values`, values will be null, but calls properties anyway (same speed).\n\t\t\t\t\t\t//\tIn any case, does not call properties if the parent variable was created with evalFlags EVAL_NOFUNCEVAL. Then fast.\n\t\t\t\t\t\t\n\t\t\t\t\t\t//TODO3: slow if many children. Eg WPF Window has almost 400, and the time is ~550 ms.\n\t\t\t\t\t\t//\tIf > 100 children, should get child properties when/if displaying them first time.\n\t\t\t\t\t\t//\tNow we need just names for sorting. To make it fast, call -var-create with flag EVAL_NOFUNCEVAL (-var-list-children inherits its flags).\n\t\t\t\t\t\t//\t\tThis inheritance can be a problem. Unless I'll modify netcoredbg to pass flags to -var-list-children.\n\t\t\t\t\t\t//\tTested: if -var-list-children called for each child separately, the total time for a WPF Window is ~850 ms (instead of ~550 ms).\n\t\t\t\t\t\t//\t\tBecause need to sort, we cannot call -var-list-children to get just currently displayed children in single call.\n\t\t\t\t\t\t\n\t\t\t\t\t\t//print.it(_v);\n\t\t\t\t\t\tvar max = _v.type.Ends(']') ? 100 : 5000;\n\t\t\t\t\t\tif (_panel._d.SendSync(101, $\"-var-list-children --all-values {_v.name} 0 {max}\") is string s) {\n\t\t\t\t\t\t\tif (s.Starts(\"^done,numchild=\")) {\n\t\t\t\t\t\t\t\tvar r = new _MiRecord(s).Data<_DONE_CHILDREN>();\n\t\t\t\t\t\t\t\tif (r.numchild > 0) {\n\t\t\t\t\t\t\t\t\t_children = new _VariablesViewItem[r.children.Length];\n\t\t\t\t\t\t\t\t\tfor (int i = 0; i < _children.Length; i++) {\n\t\t\t\t\t\t\t\t\t\t_children[i] = new(_panel, this, r.children[i], itemKind);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tArray.Sort(_children, (x, y) => string.Compare(x._exp ?? \"\\xffff\", y._exp ?? \"\\xffff\", StringComparison.OrdinalIgnoreCase));\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t//FUTURE: show non-raw children of List, Dictionary, IEnumerable etc. Like other debuggers.\n\t\t\t\t\t\t\t\t\t//\tIssue: https://github.com/Samsung/netcoredbg/issues/85\n\t\t\t\t\t\t\t\t\t//\tIssue: https://github.com/Samsung/netcoredbg/issues/132#issuecomment-1868233713\n\t\t\t\t\t\t\t\t\t//\tNow, as a workaround, users can click the variable to print items. But it works not with all ienumerables.\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t//CONSIDER: put private members into a folder, except if parent is 'this'. But can be slow to correctly detect private members.\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t//tested: debugger has a timeout for expression evaluation.\n\t\t\t\t\t\t\t\t//\tEg if the executed code contains code `12.s();`, waits 5 s, and that value is \"<error>\".\n\t\t\t\t\t\t\t\t//\tBut if contains code `dialog.show(\"\");`, waits 10 s and returns error.\n\t\t\t\t\t\t\t\t//\tDebugger respects [DebuggerBrowsable(DebuggerBrowsableState.Never)] but ignores other [DebuggerX].\n\t\t\t\t\t\t\t\tDebug_.Print($\"<c orange>{s}<>\");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (_children == null) _isFolder = false;\n\t\t\t\t\t_children ??= [];\n\t\t\t\t}\n\t\t\t\treturn _children;\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic bool IsFolder => _isFolder;\n\t\t\n\t\tbool ITreeViewItem.IsExpanded => _isExpanded;\n\t\t\n\t\tvoid ITreeViewItem.SetIsExpanded(bool yes) { _isExpanded = yes; }\n\t\t\n\t\tobject ITreeViewItem.Image\n\t\t\t=> _isFolder ? EdIcons.FolderArrow(_isExpanded)\n\t\t\t: null;\n\t\t\n\t\tvoid ITreeViewItem.SetNewText(string text) {\n\t\t\t\n\t\t}\n\t\t\n\t\tint ITreeViewItem.TextColor(TVColorInfo ci) => itemKind switch { _VVItemKind.Mouse => 0xE08000, _VVItemKind.Watch => 0x40A000, _ => -1 };\n\t}\n\t\n\tenum _VVItemKind : byte { Default, Mouse, Watch }\n\t\n#if DEBUG\n\tvoid _Test() {\n\t\t//if (_VarCreate($\"type\", 0) is _VAR r) {\n\t\t//\tprint.it(r);\n\t\t//\tprint.it(r.value);\n\t\t//\t_d.Send($\"-var-assign {r.name} list.GetType()\");\n\t\t//}\n\t\t\n\t\t//var s = \"Au.print.util.toString(dict)\";\n\t\t////s = \"_ToString(i)\";\n\t\t//s = \"C.Print(i)\";\n\t\t//s = \"e1.GetEnumerator().MoveNext()\";\n\t\t//if (_VarCreate($\"{s}\", 0) is _VAR r) {\n\t\t//\tprint.it(r);\n\t\t//\tprint.it(r.value);\n\t\t//}\n\t\t\n\t\tif (_d.SendSync(7, \"-test\") is string s1) {\n\t\t\tprint.it(s1);\n\t\t}\n\t}\n#endif\n}\n"
  },
  {
    "path": "Au.Editor/Debugger/PD._Debugger.cs",
    "content": "//From netcoredbg we need only netcoredbg.exe, dbgshim.dll, ManagedPart.dll, Microsoft.CodeAnalysis.dll and Microsoft.CodeAnalysis.CSharp.dll.\n//\tThe default netcoredbg also contains 2 unused Roslyn scripting dlls.\n//\tAlso these Roslyn dlls are very old.\n//\tNow debugger files are in the Debugger folder. Need just netcoredbg.exe, dbgshim.dll and ManagedPart.dll. They use the new Roslyn dlls.\n//\tUsing modified netcoredbg and ManagedPart.\n//\tUsing newer dbgshim.dll: https://www.nuget.org/packages/Microsoft.Diagnostics.DbgShim.win-x64 and Microsoft.Diagnostics.DbgShim.win-arm64\n//\tMore info in netcoredbg solution > readme.md.\n\nnamespace LA;\n\npartial class PanelDebug {\n\tclass _Debugger {\n\t\tconsoleProcess _p;\n\t\tFileStream _fs;\n\t\tAction<string> _events;\n\t\tbool _ignoreEvents;\n\t\tAction _readEvents;\n\t\t\n\t\tpublic _Debugger(Action<string> events) {\n\t\t\t_events = events;\n\t\t\t_readEvents = _ReadEvents;\n\t\t}\n\t\t\n\t\tpublic bool Init(bool arm64) {\n\t\t\t//var log = @\"C:\\Test\\debugger-log.txt\"; filesystem.delete(log); Environment.SetEnvironmentVariable(\"LOG_OUTPUT\", log);\n\t\t\t_p = new($@\"{folders.ThisAppBS}Debugger\\{(arm64 ? \"arm64\" : \"x64\")}\\netcoredbg.exe\", $\"--interpreter=mi\");\n\t\t\t\n\t\t\tif (SendSync(0, $\"-handshake\") != \"^done\") { //waits until the debugger is ready to process commands. Then we can measure the speed of other sync commands at startup.\n\t\t\t\t_Print(\"Failed to start debugger.\");\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t\n\t\t\t_fs = new(new Microsoft.Win32.SafeHandles.SafeFileHandle(_p.OutputHandle_, ownsHandle: false), FileAccess.Read);\n\t\t\ttimer.after(200, _ => _readEvents());\n\t\t\t\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\tvoid _ReadEvents() {\n\t\t\t//print.it(\">>>\");\n\t\t\twhile (!_ignoreEvents && _p != null) {\n\t\t\t\tint canRead = _p.CanReadNow_;\n\t\t\t\tif (canRead == 0) break;\n\t\t\t\tif (canRead < 0 || !_p.ReadLine(out var k)) {\n\t\t\t\t\t_Print(\"Debugger crashed.\");\n\t\t\t\t\t_events(\"^exit\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t//print.qm2.write($\"_ReadEvents:  {k}\");\n\t\t\t\tif (k is not (\"(gdb)\" or \"\")) {\n\t\t\t\t\ttry { _events(k); }\n\t\t\t\t\tcatch (Exception e1) {\n\t\t\t\t\t\tprint.it(e1);\n\t\t\t\t\t\t_events(\"^exit\");\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t//print.it(\"<<<\");\n\t\t\tif (_p == null) return;\n\t\t\t_fs.BeginRead([], 0, 0, ar => {\n\t\t\t\twhile (_ignoreEvents) Thread.Sleep(10);\n\t\t\t\tApp.Dispatcher.InvokeAsync(_readEvents);\n\t\t\t}, null);\n\t\t}\n\t\t\n\t\tpublic void Dispose() {\n\t\t\tif (_p == null) return;\n\t\t\t_p.Dispose();\n\t\t\t_p = null;\n\t\t\t_fs?.Dispose();\n\t\t}\n\t\t\n\t\tbool _Write(string s) {\n\t\t\ttry { _p.Write(s); }\n\t\t\tcatch (AuException e1) {\n\t\t\t\t_Print(\"Debugger crashed.\");\n\t\t\t\tDebug_.Print(e1);\n\t\t\t\tDispose();\n\t\t\t\tApp.Dispatcher.InvokeAsync(() => _events(\"^exit\"));\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Writes <i>s</i>. Does not read.\n\t\t/// </summary>\n\t\t/// <param name=\"s\">Command, optionally with a token &gt;=1000, like \"1020-command\".</param>\n\t\tpublic void Send(string s) {\n\t\t\t_Write(s);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Writes token+s, like \"5-command\".\n\t\t/// Then synchronously reads until received a line that starts with the token followed by '^'. For other received lines calls the events callback.\n\t\t/// </summary>\n\t\t/// <param name=\"token\">A number 1-999. Token 0 is used by this class.</param>\n\t\t/// <param name=\"noEvent\">Called on events. Return true to not call the events callback.</param>\n\t\t/// <returns>The received line without token. Returns null if the debugger process ended.</returns>\n\t\tpublic string SendSync(int token, string s, Func<string, bool> noEvent = null) {\n\t\t\tvar st = token.ToS();\n\t\t\tif (!_Write(st + s)) return null;\n\t\t\t_ignoreEvents = true;\n\t\t\ttry {\n\t\t\t\twhile (_p.ReadLine(out var k)) {\n\t\t\t\t\t//print.qm2.write($\"SendSync:  {k}\");\n\t\t\t\t\tif (k.Starts(st) && k.Eq(st.Length, '^')) return k[st.Length..];\n\t\t\t\t\tif (k is not (\"(gdb)\" or \"\")) {\n\t\t\t\t\t\tif (noEvent?.Invoke(k) == true) continue;\n\t\t\t\t\t\t_events(k);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tfinally { _ignoreEvents = false; }\n\t\t\treturn null;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Debugger/PD._MiRecord.cs",
    "content": "using System.Text.Json;\nusing System.Text.Json.Nodes;\n\nnamespace LA;\n\npartial class PanelDebug {\n\trecord class _MiRecord {\n\t\tpublic readonly int token;\n\t\tpublic readonly char kind;\n\t\tpublic readonly string name;\n\t\tpublic readonly JsonObject data;\n\t\t\n\t\tpublic _MiRecord(RStr s) {\n\t\t\tif (char.IsAsciiDigit(s[0])) {\n\t\t\t\tif (!s.ToInt_(out token, out int e)) throw new ArgumentException();\n\t\t\t\ts = s[e..];\n\t\t\t}\n\t\t\tkind = s[0];\n\t\t\tif (kind is not ('^' or '*' or '+' or '=' or '~' or '@' or '&')) throw new ArgumentException();\n\t\t\ts = s[1..];\n\t\t\tint i = s.IndexOf(',');\n\t\t\tif (i < 0) {\n\t\t\t\tname = s.ToString();\n\t\t\t} else {\n\t\t\t\tname = s[..i].ToString();\n\t\t\t\ts = s[++i..];\n\t\t\t\tdata = _ReadObject(ref s, true, name);\n\t\t\t}\n\t\t}\n\t\t\n\t\tstatic JsonObject _ReadObject(ref RStr s, bool root, string fieldName) {\n\t\t\tif (!root) s = s[1..];\n\t\t\tvar r = new JsonObject();\n\t\t\tfor (bool once = false; ;) {\n\t\t\t\tif (root) { if (s.Length == 0) break; } else if (s[0] == '}') { s = s[1..]; break; }\n\t\t\t\tif (!once) once = true; else if (s[0] == ',') s = s[1..]; else throw new ArgumentException(s.ToString());\n\t\t\t\t\n\t\t\t\tint i = s.IndexOf('=');\n\t\t\t\tstring name = s[..i].ToString().Replace('-', '_');\n\t\t\t\ts = s[++i..];\n\t\t\t\tr[name] = _ReadValue(ref s, name);\n\t\t\t}\n#if DEBUG\n\t\t\tif (fieldName != null) _PrintType(r, fieldName);\n#endif\n\t\t\treturn r;\n\t\t}\n\t\t\n\t\tstatic JsonArray _ReadList(ref RStr s, string fieldName) {\n#if DEBUG\n\t\t\tfieldName = fieldName?.TrimEnd('s'); //eg threads -> thread\n#endif\n\t\t\ts = s[1..];\n\t\t\tvar r = new JsonArray();\n\t\t\tfor (bool once = false; ;) {\n\t\t\t\tif (s[0] == ']') { s = s[1..]; break; }\n\t\t\t\tif (!once) once = true; else if (s[0] == ',') s = s[1..]; else throw new ArgumentException();\n\t\t\t\t\n\t\t\t\tif (s[0] is not ('\"' or '{' or '[')) { //name=value\n\t\t\t\t\tint i = s.IndexOf('=');\n\t\t\t\t\tstring name = s[..i].ToString();\n\t\t\t\t\ts = s[++i..];\n#if DEBUG\n\t\t\t\t\tif (fieldName != null) fieldName = name;\n#endif\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tr.Add(_ReadValue(ref s, fieldName));\n\t\t\t\tfieldName = null; //print type once\n\t\t\t}\n\t\t\treturn r;\n\t\t}\n\t\t\n\t\tstatic JsonNode _ReadValue(ref RStr s, string fieldName) {\n\t\t\treturn s[0] switch {\n\t\t\t\t'\"' => _ReadString(ref s),\n\t\t\t\t'{' => _ReadObject(ref s, false, fieldName),\n\t\t\t\t'[' => _ReadList(ref s, fieldName),\n\t\t\t\t_ => throw new ArgumentException()\n\t\t\t};\n\t\t}\n\t\t\n\t\tstatic string _ReadString(ref RStr s) {\n\t\t\tfor (int i = 1; ; i++) {\n\t\t\t\ti = s.IndexOf(i, '\"');\n\t\t\t\tbool esc = false; for (int j = i; s[--j] == '\\\\';) esc ^= true;\n\t\t\t\tif (!esc) {\n\t\t\t\t\tvar r = s[1..i].ToString().Unescape();\n\t\t\t\t\ts = s[++i..];\n\t\t\t\t\treturn r;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n#if DEBUG\n\t\tstatic void prt(RStr s) { print.it(s.ToString()); }\n#endif\n\t\t\n\t\tpublic T Data<T>() {\n\t\t\treturn data.Deserialize<T>(s_jso);\n\t\t}\n\t\t\n\t\tstatic JsonSerializerOptions s_jso = new(JsonSerializerDefaults.General) {\n\t\t\tIncludeFields = true,\n\t\t\tNumberHandling = System.Text.Json.Serialization.JsonNumberHandling.AllowReadingFromString,\n#if DEBUG\n\t\t\tUnmappedMemberHandling = System.Text.Json.Serialization.JsonUnmappedMemberHandling.Disallow\n#endif\n\t\t};\n\t\t\n#if DEBUG\n\t\tstatic void _PrintType(JsonObject x, string fieldName) {\n\t\t\t//var b = new StringBuilder();\n\t\t\t//foreach (var (k, v) in x) {\n\t\t\t//\tif (b.Length > 0) b.Append(\", \"); else b.Append(\"record \").Append('_').Append(fieldName.Replace('-', '_').Upper()).Append('(');\n\t\t\t//\tswitch (v.GetValueKind()) {\n\t\t\t//\tcase JsonValueKind.String:\n\t\t\t//\t\tvar s = (string)v;\n\t\t\t//\t\tif (s.ToInt(out int i1, 0, out int e, STIFlags.DontSkipSpaces | STIFlags.NoHex) && e == s.Length) b.Append(\"int\"); else b.Append(\"string\");\n\t\t\t//\t\tbreak;\n\t\t\t//\tcase JsonValueKind.Object:\n\t\t\t//\t\tb.Append('_').Append(k.Upper());\n\t\t\t//\t\tbreak;\n\t\t\t//\tcase JsonValueKind.Array:\n\t\t\t//\t\tb.Append('_').Append(k.Upper()).Append(\"[]\");\n\t\t\t//\t\tbreak;\n\t\t\t//\t}\n\t\t\t//\tb.Append(' ').Append(k);\n\t\t\t//}\n\t\t\t//b.Append(\");\");\n\t\t\t//print.it($\"<><c green>{b.ToString()}<>\");\n\t\t}\n#endif\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Debugger/PanelBreakpoints.cs",
    "content": "using System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Input;\nusing Au.Controls;\nusing Microsoft.CodeAnalysis.CSharp.Syntax;\nusing Microsoft.CodeAnalysis;\nusing System.Xml.Linq;\n\nnamespace LA;\n\nclass PanelBreakpoints {\n\tKTreeView _tv;\n\t_Item _root;\n\tstring _file;\n\tbool _initOnce;\n\tint _save;\n\t\n\tpublic PanelBreakpoints() {\n\t\t_tv = new() { Name = \"Breakpoints_list\", SingleClickActivate = true, HasCheckboxes = true, SmallIndent = true };\n\t\tP.Children.Add(_tv);\n\t\t\n\t\tFilesModel.AnyWorkspaceLoadedAndDocumentsOpened += _LoadIfNeed;\n\t\t\n\t\tPanels.PanelManager[\"Breakpoints\"].DontActivateFloating = e => e == _tv;\n\t}\n\t\n\tpublic DockPanel P { get; } = new();\n\t\n\tvoid _LoadIfNeed() {\n\t\tif (_root != null) return;\n\t\t_root = new(this, null, true);\n\t\t\n\t\t_file = $@\"{App.Model.WorkspaceDirectory}\\.state{(miscInfo.isChildSession ? @\"\\pip\" : null)}\\breakpoints.xml\";\n\t\tif (filesystem.exists(_file).File) {\n\t\t\ttry {\n\t\t\t\tvar xr = XElement.Load(_file);\n\t\t\t\tforeach (var xf in xr.Elements(\"file\")) {\n\t\t\t\t\tif (xf.Attr(out int id, \"id\") && App.Model.FindById((uint)id) is { } f) {\n\t\t\t\t\t\t_Item folder = new(this, f, xf.HasAttr(\"exp\"));\n\t\t\t\t\t\t_root.AddChild(folder);\n\t\t\t\t\t\tforeach (var xb in xf.Elements(\"b\")) folder.AddChild(new(this, xb));\n\t\t\t\t\t} else {\n\t\t\t\t\t\t_SaveLater(1);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception e1) { print.it(e1); }\n\t\t}\n\t\t_TvSetItems();\n\t\t\n\t\tif (!_initOnce) {\n\t\t\t_initOnce = true;\n\t\t\t\n\t\t\tFilesModel.UnloadingAnyWorkspace += () => {\n\t\t\t\tif (_root == null) return;\n\t\t\t\tSaveNowIfNeed();\n\t\t\t\t_root = null;\n\t\t\t\t_TvSetItems();\n\t\t\t};\n\t\t\t\n\t\t\tFilesModel.NeedRedraw += v => {\n\t\t\t\tif (!v.renamed) return;\n\t\t\t\tvar f = _FindItemOfFile(v.f);\n\t\t\t\tif (f != null) _tv.Redraw(f, v.remeasure);\n\t\t\t};\n\t\t\t\n\t\t\t_tv.ItemActivated += e => {\n\t\t\t\tif (e.Mod != 0) return;\n\t\t\t\tif (e.Item is _Item b && !b.IsFolder) {\n\t\t\t\t\tApp.Model.OpenAndGoTo(b.Parent.file, b.line);\n\t\t\t\t}\n\t\t\t};\n\t\t\t\n\t\t\t_tv.ItemClick += e => {\n\t\t\t\tvar b = e.Item as _Item;\n\t\t\t\tswitch (e.Button) {\n\t\t\t\tcase MouseButton.Left when e.Part is TVParts.Checkbox:\n\t\t\t\t\t_SetEnabled(b, !b.IsEnabledOrHasEnabledChildren);\n\t\t\t\t\tbreak;\n\t\t\t\tcase MouseButton.Left:\n\t\t\t\t\tswitch (e.Mod) {\n\t\t\t\t\tcase ModifierKeys.Control: _DeleteItem(b); break;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase MouseButton.Right:\n\t\t\t\t\t_ContextMenu(b);\n\t\t\t\t\tbreak;\n\t\t\t\tcase MouseButton.Middle:\n\t\t\t\t\t_SetEnabled(b, !b.IsEnabledOrHasEnabledChildren);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t};\n\t\t\t\n\t\t\t_tv.PreviewKeyDown += (_, e) => {\n\t\t\t\tif (e.Key is Key.Up or Key.Down && e.KeyboardDevice.Modifiers == ModifierKeys.Shift && _tv.FocusedItem is _Item { IsFolder: true } f) {\n\t\t\t\t\te.Handled = true;\n\t\t\t\t\t_MoveFolder(f, e.Key is Key.Up);\n\t\t\t\t}\n\t\t\t};\n\t\t\t\n\t\t\t_tv.RightClickInEmptySpace += () => _ContextMenu(null);\n\t\t\t\n\t\t\tApp.Timer1sWhenVisible += () => { if (_save != 0 && --_save <= 0) _SaveNow(); };\n\t\t}\n\t}\n\t\n\tpublic void SaveNowIfNeed() {\n\t\tif (_save != 0) _SaveNow();\n\t}\n\t\n\tvoid _SaveNow() {\n\t\tvar xr = new XElement(\"breakpoints\");\n\t\tfor (var f = _root.FirstChild; f != null; f = f.Next) {\n\t\t\tvar xf = new XElement(\"file\");\n\t\t\txf.SetAttributeValue(\"id\", f.file.IdString);\n\t\t\tif (f.IsExpanded) xf.SetAttributeValue(\"exp\", \"\");\n\t\t\txr.Add(xf);\n\t\t\tfor (var b = f.FirstChild; b != null; b = b.Next) {\n\t\t\t\tvar xb = new XElement(\"b\");\n\t\t\t\txb.SetAttributeValue(\"line\", b.line.ToS());\n\t\t\t\txb.SetAttributeValue(\"name\", b.name);\n\t\t\t\tif (b.IsEnabled) xb.SetAttributeValue(\"en\", \"\");\n\t\t\t\tif (!b.condition.NE()) xb.SetAttributeValue(\"condition\", b.condition);\n\t\t\t\tif (!b.log.NE()) xb.SetAttributeValue(\"log\", b.log);\n\t\t\t\tif (b.LogExpression) xb.SetAttributeValue(\"flags\", \"1\");\n\t\t\t\txf.Add(xb);\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (!App.Model.TryFileOperation(FOSync.PrivateFileWrite, () => { xr.SaveElem(_file); })) return;\n\t\t_save = 0;\n\t\t//print.it(\"saved\");\n\t}\n\t\n\tvoid _SaveLater(int afterS = 30) {\n\t\t//print.it(new StackTrace(true));\n\t\t_save = _save == 0 ? afterS : Math.Min(_save, afterS);\n\t}\n\t\n\tinternal void SciLoaded(SciCode doc) {\n\t\t_LoadIfNeed();\n\t\tvar f = _FindItemOfFile(doc); if (f == null) return;\n\t\tbool removed = false;\n\t\tfor (var b = f.FirstChild; b != null;) {\n\t\t\tif (b.SetMarkerInDoc()) b = b.Next;\n\t\t\telse {\n\t\t\t\tvar bb = b; b = b.Next;\n\t\t\t\tbb.Remove();\n\t\t\t\tremoved = true;\n\t\t\t\t_SaveLater(1);\n\t\t\t}\n\t\t}\n\t\tif (removed) _TvSetItems(true);\n\t}\n\t\n\t_Item _FindItemOfFile(FileNode fn) {\n\t\tif (_root != null && fn != null)\n\t\t\tfor (var f = _root.FirstChild; f != null; f = f.Next)\n\t\t\t\tif (f.file == fn) return f;\n\t\treturn null;\n\t}\n\t_Item _FindItemOfFile(SciCode doc) => _FindItemOfFile(doc?.EFile);\n\t\n\tpublic void ToggleBreakpoint(int pos8 = -1, bool logpoint = false) {\n\t\tvar doc = Panels.Editor.ActiveDoc;\n\t\tif (doc == null || !doc.EFile.IsCodeFile) return;\n\t\tbool useSelStart = pos8 < 0;\n\t\tint line = useSelStart ? doc.aaaLineFromPos() : doc.aaaLineFromPos(false, pos8);\n\t\tif (_IsBreakpoint(doc, line)) {\n\t\t\t_DeleteBreakpoint(doc, line);\n\t\t} else {\n\t\t\tif (!useSelStart) if (doc.aaaLineFromPos() == line) useSelStart = true;\n\t\t\t\n\t\t\tint h = doc.aaaMarkerAdd(logpoint ? SciTheme.Marker.BreakpointL : SciTheme.Marker.Breakpoint, line); if (h < 0) return;\n\t\t\tvar folder = _FindItemOfFile(doc);\n\t\t\tif (folder == null) {\n\t\t\t\t_root.AddChild(folder = new(this, doc.EFile, true), true);\n\t\t\t} else folder.SetIsExpanded(true);\n\t\t\t\n\t\t\tvar name = PanelBookmarks.GetMarkerName_(doc, line, useSelStart) ?? \"Breakpoint\";\n\t\t\t_Item b = new(this, line, h, name) { IsEnabled = true, log = logpoint ? \"LOGPOINT\" : null };\n\t\t\tif (folder.Children().FirstOrDefault(o => o.line > line) is { } b2) b2.AddSibling(b, false);\n\t\t\telse folder.AddChild(b);\n\t\t\t\n\t\t\t_TvSetItems(true);\n\t\t\t_tv.SelectSingle(b);\n\t\t\t_SaveLater();\n\t\t\t\n\t\t\tif (logpoint) _BreakpointProperties(b, doc);\n\t\t}\n\t}\n\t\n\tvoid _SetEnabled(_Item b, bool enabled) {\n\t\tif (b.IsFolder) {\n\t\t\tforeach (var v in b.Children()) _SetEnabled(v, enabled);\n\t\t} else if (b.IsEnabled != enabled) {\n\t\t\tb.IsEnabled = enabled;\n\t\t\t_tv.Redraw(b);\n\t\t\tb.SetMarkerInDoc();\n\t\t}\n\t\t_SaveLater();\n\t}\n\t\n\tinternal bool SciMiddleClick_(SciCode doc, int line) {\n\t\tif (_BreakpointFromLine(doc, line) is not { } b) return false;\n\t\t_SetEnabled(b, !b.IsEnabled);\n\t\treturn true;\n\t}\n\t\n\tinternal void AddMarginMenuItems_(SciCode doc, popupMenu m, int line, int pos8) {\n\t\tif (_BreakpointFromLine(doc, line) is { } b) {\n\t\t\tm[\"Delete breakpoint\", \"*Material.MinusCircle @12 #EE3000\"] = o => ToggleBreakpoint(pos8);\n\t\t\tm[\"Breakpoint properties...\"] = o => _BreakpointProperties(b, doc);\n\t\t\tm.AddCheck(\"Enabled breakpoint\\tM-click\", b.IsEnabled, o => _SetEnabled(b, o.IsChecked));\n\t\t} else {\n\t\t\tm[\"Add breakpoint\", \"*Material.Circle @12 #EE3000\"] = o => ToggleBreakpoint(pos8);\n\t\t\tm[\"Add logpoint\", \"*BootstrapIcons.DiamondFill @14\" + EdIcons.green2] = o => ToggleBreakpoint(pos8, true);\n\t\t}\n\t}\n\t\n\tvoid _BreakpointProperties(_Item x, DependencyObject owner) {\n\t\tvar w = new KDialogWindow();\n\t\tw.InitWinProp(\"Breakpoint properties\", owner);\n\t\tvar b = new wpfBuilder(w).Width(300..800);\n\t\tb.R.Add(\"Condition\", out TextBox eCondition, x.condition).Font(\"Consolas\")\n\t\t\t.Tooltip(\"\"\"\nBreak when this expression evaluates to true.\nExample: i==5\nNot all kinds of expressions are supported.\nConditional breakpoints have different marker and Breakpoints panel text color.\n\"\"\");\n\t\t//b.R.Add(\"Hit count\", out TextBox eHit).Width(50, \"L\");\n\t\tb.R.Add(\"Message\", out TextBox eLog, x.log)\n\t\t\t.Tooltip(\"\"\"\nPrint this text or expression instead of pausing.\nSuch breakpoints are known as logpoints, tracepoints.\nGreen marker and Breakpoints panel text color.\nSimple text examples:\n  Simple text\n  Text with <b>output tags<>\nExpression examples:\n  \"Multiline\\nstring\"\n  variable\n  \"variable=\" + variable\n  \"<c red>x.y=\" + x.y + \"<>\"\n  Au.clipboard.text\n\"\"\");\n\t\tb.R.Skip().Add(out KCheckBox cLE, \"Message is expression\").Checked(x.LogExpression)\n\t\t\t.Tooltip(\"\"\"\nMessage is a C# expression. See examples in Message tooltip.\nNot all kinds of expressions are supported.\n\"\"\");\n\t\tb.R.AddOkCancel();\n\t\tb.End();\n\t\tvar e1 = !x.log.NE() ? eLog : eCondition; e1.SelectAll(); e1.Focus();\n\t\tif (!w.ShowAndWait()) return;\n\t\t\n\t\tstring cond = eCondition.TextOrNull(), log = eLog.TextOrNull();\n\t\tbool isLE = cLE.IsChecked;\n\t\tif (cond != x.condition || log != x.log || isLE != x.LogExpression) {\n\t\t\tx.condition = cond;\n\t\t\tx.log = log;\n\t\t\tx.LogExpression = isLE;\n\t\t\t_SaveLater();\n\t\t\t_tv.Redraw(x);\n\t\t\tx.SetMarkerInDoc();\n\t\t\tPanels.Debug.BreakpointPropertiesChanged_(x);\n\t\t}\n\t}\n\t\n\tinternal void SciMouseDwell_(bool started, SciCode doc, in Sci.SCNotification n) {\n\t\tif (_marginTooltip != null) _marginTooltip.IsOpen = false;\n\t\tif (n.position >= 0 || !doc.EFile.IsCodeFile) return;\n\t\tif (started) {\n\t\t\tint margin = doc.aaaMarginFromPoint((n.x, n.y));\n\t\t\tif (margin != SciTheme.Margin.Markers) return;\n\t\t\tint pos = doc.aaaPosFromXY(false, (n.x, n.y), false);\n\t\t\tint line = doc.aaaLineFromPos(false, pos);\n\t\t\tif (_BreakpointFromLine(doc, line) is { } b && b.HasProperties) {\n\t\t\t\t_marginTooltip ??= new ToolTip { Placement = System.Windows.Controls.Primitives.PlacementMode.MousePoint, HorizontalOffset = 20 };\n\t\t\t\t_marginTooltip.PlacementTarget = doc;\n\t\t\t\tb.TooltipSetContent(_marginTooltip);\n\t\t\t\t_marginTooltip.IsOpen = true;\n\t\t\t}\n\t\t}\n\t}\n\tToolTip _marginTooltip;\n\t\n\tvoid _TvSetItems(bool modified = false) {\n\t\tif (_root?.Count > 0) {\n\t\t\t_tv.SetItems(_root.Children(), modified);\n\t\t} else {\n\t\t\t_tv.SetItems(null);\n\t\t}\n\t}\n\t\n\tvoid _ContextMenu(_Item b) {\n\t\tvar m = new popupMenu();\n\t\t\n\t\tif (b != null) {\n\t\t\t_tv.SelectSingle(b);\n\t\t\t\n\t\t\tif (b.IsFolder) {\n\t\t\t\tm[\"Move up\\tShift+Up\", disable: b.Previous == null] = o => _MoveFolder(b, true);\n\t\t\t\tm[\"Move down\\tShift+Down\", disable: b.Next == null] = o => _MoveFolder(b, false);\n\t\t\t\tm.Separator();\n\t\t\t\tbool haveEnabled1 = b.IsEnabledOrHasEnabledChildren;\n\t\t\t\tm[haveEnabled1 ? \"Disable these breakpoints\\tM-click\" : \"Enable these breakpoints\\tM-click\"] = o => _SetEnabled(b, !haveEnabled1);\n\t\t\t\tm[\"Delete these breakpoints\\tCtrl+click\"] = o => _DeleteItem(b);\n\t\t\t} else {\n\t\t\t\tm[\"Delete\\tCtrl+click\"] = o => _DeleteItem(b);\n\t\t\t\tm[\"Properties...\"] = o => _BreakpointProperties(b, P);\n\t\t\t\tm.AddCheck(\"Enabled\\tM-click\", b.IsEnabled, o => _SetEnabled(b, o.IsChecked));\n\t\t\t}\n\t\t\tm.Separator();\n\t\t} else if (!(_root?.Count > 0)) {\n\t\t\tprint.it(\"To add a breakpoint, click the white margin in the code editor.\");\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tbool haveEnabled = _root.IsEnabledOrHasEnabledChildren;\n\t\tm[haveEnabled ? \"Disable all breakpoints\" : \"Enable all breakpoints\"] = o => { foreach (var v in _root.Children()) _SetEnabled(v, !haveEnabled); };\n\t\tm.Submenu(\"Delete all\", m => {\n\t\t\tm[\"Delete all disabled breakpoints\"] = o => {\n\t\t\t\tforeach (var v in _root.Descendants().ToArray()) if (!v.IsFolder && !v.IsEnabled) _DeleteItem(v);\n\t\t\t};\n\t\t\tm[\"Delete all breakpoints\"] = o => {\n\t\t\t\tforeach (var v in _root.Children().ToArray()) _DeleteItem(v);\n\t\t\t};\n\t\t});\n\t\t\n\t\tm.Show(owner: _tv);\n\t}\n\t\n\tbool _IsBreakpoint(SciCode doc, int line) => (doc.Call(Sci.SCI_MARKERGET, line) & 63 << SciTheme.Marker.Breakpoint) != 0;\n\t\n\t_Item _BreakpointFromLine(SciCode doc, int line) {\n\t\tif (_FindItemOfFile(doc) is { } folder) {\n\t\t\tfor (int i = 0; ; i++) {\n\t\t\t\tint h = doc.Call(Sci.SCI_MARKERHANDLEFROMLINE, line, i);\n\t\t\t\tif (h < 0) return null;\n\t\t\t\tfor (var b = folder.FirstChild; b != null; b = b.Next) if (b.markerHandle == h) return b;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\t\n\tvoid _DeleteBreakpoint(SciCode doc, int line, bool sciDelete = true) {\n\t\tif (_BreakpointFromLine(doc, line) is { } b) {\n\t\t\tif (sciDelete) doc.aaaMarkerDeleteHandle(b.markerHandle);\n\t\t\t_DeleteBreakpointL(b);\n\t\t}\n\t}\n\t\n\tvoid _DeleteBreakpointL(_Item b) {\n\t\tif (b.IsEnabled) Panels.Debug.BreakpointAddedDeleted_(b, false);\n\t\tvar folder = b.Parent;\n\t\tb.Remove();\n\t\tif (!folder.HasChildren) folder.Remove();\n\t\t_TvSetItems(true);\n\t\t_SaveLater();\n\t}\n\t\n\tvoid _DeleteItem(_Item b) {\n\t\tif (b.IsFolder) {\n\t\t\tforeach (var v in b.Children().ToArray()) _DeleteItem(v);\n\t\t} else {\n\t\t\tvar folder = b.Parent;\n\t\t\tif (b.markerHandle != 0 && folder.file.OpenDoc is { } doc) {\n\t\t\t\tdoc.aaaMarkerDeleteHandle(b.markerHandle);\n\t\t\t}\n\t\t\t_DeleteBreakpointL(b);\n\t\t}\n\t}\n\t\n\tinternal void FileDeleted(IEnumerable<FileNode> files) {\n\t\tforeach (var file in files) {\n\t\t\tif (_FindItemOfFile(file) is { } folder) {\n\t\t\t\tforeach (var b in folder.Children()) if (b.IsEnabled) Panels.Debug.BreakpointAddedDeleted_(b, false);\n\t\t\t\tfolder.Remove();\n\t\t\t\t_TvSetItems(true);\n\t\t\t\t_SaveLater();\n\t\t\t}\n\t\t}\n\t}\n\t\n\tvoid _MoveFolder(_Item f, bool up) {\n\t\tif (up && f == _root.FirstChild) return;\n\t\tvar ff = up ? f.Previous : f.Next;\n\t\tif (ff == null) return;\n\t\tf.Remove();\n\t\tff.AddSibling(f, after: !up);\n\t\t_TvSetItems(true);\n\t\t_SaveLater();\n\t}\n\t\n\tinternal void SciDeletingLineWithMarker(SciCode doc, int line) {\n\t\t_DeleteBreakpoint(doc, line, false);\n\t}\n\t\n\tinternal void SciModified(SciCode doc, in Sci.SCNotification n) {\n\t\tif (n.linesAdded != 0) {\n\t\t\t//update the line field, and display\n\t\t\tvar folder = _FindItemOfFile(doc);\n\t\t\tif (folder == null) return;\n\t\t\tbool redraw = false;\n\t\t\tfor (var b = folder.FirstChild; b != null; b = b.Next) {\n\t\t\t\tint line = doc.Call(Sci.SCI_MARKERLINEFROMHANDLE, b.markerHandle);\n\t\t\t\tif (line != b.line) {\n\t\t\t\t\tb.line = line;\n\t\t\t\t\tredraw = true;\n\t\t\t\t\t_SaveLater(2 * 60);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (redraw && folder.IsExpanded) _tv.Redraw();\n\t\t}\n\t}\n\t\n\tinternal IEnumerable<IBreakpoint> GetBreakpoints(bool disabledToo = false) {\n\t\tfor (var f = _root.FirstChild; f != null; f = f.Next) {\n\t\t\tfor (var b = f.FirstChild; b != null; b = b.Next) {\n\t\t\t\tif (disabledToo || b.IsEnabled) yield return b;\n\t\t\t}\n\t\t}\n\t}\n\t\n\tclass _Item : TreeBase<_Item>, ITreeViewItem, IBreakpoint {\n\t\treadonly PanelBreakpoints _view;\n\t\tpublic readonly FileNode file; //if folder\n\t\tpublic int line, markerHandle; //if breakpoint\n\t\tpublic string condition, log; //if breakpoint\n\t\tpublic readonly string name;\n\t\treadonly bool _isFolder;\n\t\tbool _isExpanded;\n\t\tbool _isEnabled;\n\t\t\n\t\t//folder\n\t\tpublic _Item(PanelBreakpoints view, FileNode file, bool isExpanded) {\n\t\t\t_view = view;\n\t\t\tthis.file = file;\n\t\t\t_isFolder = true;\n\t\t\t_isExpanded = isExpanded;\n\t\t}\n\t\t\n\t\t//new breakpoint\n\t\tpublic _Item(PanelBreakpoints bv, int line, int markerHandle, string name) {\n\t\t\t_view = bv;\n\t\t\tthis.line = line;\n\t\t\tthis.markerHandle = markerHandle;\n\t\t\tthis.name = name;\n\t\t}\n\t\t\n\t\t//loading breakpoint\n\t\tpublic _Item(PanelBreakpoints bv, XElement x) {\n\t\t\t_view = bv;\n\t\t\tline = x.Attr(\"line\", 0);\n\t\t\tname = x.Attr(\"name\");\n\t\t\t_isEnabled = x.HasAttr(\"en\");\n\t\t\tcondition = x.Attr(\"condition\");\n\t\t\tlog = x.Attr(\"log\");\n\t\t\tif (x.Attr(out int flags, \"flags\")) {\n\t\t\t\tif ((flags & 1) != 0) LogExpression = true;\n\t\t\t}\n\t\t}\n\t\t\n#if DEBUG\n\t\tpublic override string ToString() => ((ITreeViewItem)this).DisplayText;\n#endif\n\t\t\n\t\tpublic bool IsEnabledOrHasEnabledChildren => IsFolder ? Children().Any(static o => o.IsEnabledOrHasEnabledChildren) : _isEnabled;\n\t\t\n\t\tpublic bool SetMarkerInDoc() {\n\t\t\tif (Parent.file.OpenDoc is { } doc) {\n\t\t\t\tif (markerHandle != 0) { doc.aaaMarkerDeleteHandle(markerHandle); markerHandle = 0; }\n\t\t\t\tint marker = !log.NE() ? SciTheme.Marker.BreakpointL : !condition.NE() ? SciTheme.Marker.BreakpointC : SciTheme.Marker.Breakpoint;\n\t\t\t\tif (!_isEnabled) marker++;\n\t\t\t\tvar h = doc.aaaMarkerAdd(marker, line);\n\t\t\t\tif (h != -1) { markerHandle = h; return true; }\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\t//ITreeViewItem\n\t\t\n\t\tpublic bool IsFolder => _isFolder;\n\t\t\n\t\tpublic bool IsExpanded => _isExpanded;\n\t\t\n\t\tpublic void SetIsExpanded(bool yes) { _isExpanded = yes; _view._SaveLater(5 * 60); }\n\t\t\n\t\tIEnumerable<ITreeViewItem> ITreeViewItem.Items => base.Children();\n\t\t\n\t\tstring ITreeViewItem.DisplayText => _isFolder ? (file.Parent.HasParent ? $\"{file.Name}  [{file.Parent.ItemPath}]\" : file.Name) : $\"{(line + 1).ToS()}  {name}\";\n\t\t//never mind: after moving the file should display the new path (just redraw).\n\t\t\n\t\tobject ITreeViewItem.Image => _isFolder ? EdIcons.FolderArrow(_isExpanded) : null;\n\t\t\n\t\tTVParts ITreeViewItem.NoParts => _isFolder ? TVParts.Checkbox : TVParts.Image;\n\t\t\n\t\tTVCheck ITreeViewItem.CheckState => _isEnabled ? TVCheck.Checked : TVCheck.Unchecked;\n\t\t\n\t\tint ITreeViewItem.TextColor(TVColorInfo ci) => !log.NE() ? 0x00A000 : !condition.NE() ? (ci.isHighContrastDark ? 0x8080FF : 0x0000FF) : -1;\n\t\t\n\t\tint ITreeViewItem.TooltipDelay => HasProperties ? 500 : 0;\n\t\t\n\t\tpublic void TooltipSetContent(ToolTip tt) {\n\t\t\tif (tt.Content is not TextBlock k) tt.Content = k = new TextBlock { FontFamily = new(\"Consolas\") };\n\t\t\tstring s1 = condition.NE() ? null : \"Condition: \", s2 = log.NE() ? null : condition.NE() ? \"Message: \" : \"\\nMessage: \";\n\t\t\tk.Text = $\"{s1}{condition}{s2}{log}\";\n\t\t}\n\t\t\n\t\t//IBreakpoint\n\t\t\n\t\tFileNode IBreakpoint.File => file ?? Parent.file;\n\t\t\n\t\tint IBreakpoint.Line => line;\n\t\t\n\t\tstring IBreakpoint.Condition => condition;\n\t\t\n\t\tstring IBreakpoint.Log => log;\n\t\t\n\t\tpublic bool LogExpression { get; set; }\n\t\t\n\t\tpublic bool HasProperties => !(condition.NE() && log.NE());\n\t\t\n\t\tpublic bool IsEnabled {\n\t\t\tget => _isEnabled;\n\t\t\tset {\n\t\t\t\tif (value == _isEnabled) return;\n\t\t\t\t_isEnabled = value;\n\t\t\t\tPanels.Debug.BreakpointAddedDeleted_(this, _isEnabled);\n\t\t\t}\n\t\t}\n\t\t\n\t\tint IBreakpoint.Id { get; set; }\n\t}\n}\n\ninterface IBreakpoint {\n\tFileNode File { get; }\n\tint Line { get; }\n\tstring Condition { get; }\n\tstring Log { get; }\n\tbool LogExpression { get; set; }\n\tbool HasProperties { get; }\n\tbool IsEnabled { get; }\n\tint Id { get; set; }\n}\n"
  },
  {
    "path": "Au.Editor/Debugger/PanelDebug.cs",
    "content": "using Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp;\nusing Microsoft.CodeAnalysis.CSharp.Syntax;\nusing Microsoft.CodeAnalysis.Text;\n\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Input;\nusing Au.Controls;\n\nnamespace LA;\n\npartial class PanelDebug {\n\t_Debugger _d;\n\t(Button debug, Button restart, Button next, Button step, Button stepOut, Button @continue, Button pause, Button end) _buttons;\n\tComboBox _cbThreads;\n\tKPanels.ILeaf _ipanel;\n\tbool _restart;\n\t_Session _s;\n\tstatic IntPtr s_event;\n\t\n\trecord class _Session(int processId, bool attachMode) {\n\t\tpublic FileNode file;\n\t\tpublic int threadId;\n\t\tpublic _FRAME frame;\n\t\tpublic bool attached, stepping, inStoppedEvent, stoppedOnException;\n\t\tpublic (int breakpointId, bool nonstop) runToHere;\n\t\tpublic _RthData restartToHere;\n\t\tpublic Process process;\n\t\tpublic string[] exceptionsT, exceptionsU;\n\t\tpublic _STOPPED thrownException;\n\t\tpublic string tePath;\n\t\tpublic TextSpan teSpan;\n\t\tpublic List<int> startedThreads;\n\t\tpublic Dictionary<string, string> modules = new();\n\t}\n\t\n\tpublic PanelDebug() {\n\t\tP.UiaSetName(\"Debug panel\");\n\t\t\n\t\tvar b = new wpfBuilder(P).Columns(-1).Brush(SystemColors.ControlBrush);\n\t\tb.Options(margin: new());\n\t\t\n\t\tvar tb = b.xAddToolBar(hideOverflow: true);\n\t\ttb.UiaSetName(\"Debug_toolbar\");\n\t\tconst string c_color = EdIcons.blue, c_color2 = EdIcons.green2, c_color3 = EdIcons.black;\n\t\t_buttons.debug = _TbButton(\"*Material.Bug\" + c_color2, _ => _Start(), \"Run with debugger.\\nWill stop (pause) at a breakpoint or exception line.\\nTo add a breakpoint, click the white margin in the code editor.\");\n\t\t_buttons.restart = _TbButton(\"*Codicons.DebugRestart\" + c_color2, _ => _Restart(), \"Restart\");\n\t\t_buttons.end = _TbButton(\"*Material.SquareOutline @14\" + c_color3, button => {\n\t\t\tif (_s == null) return;\n\t\t\tif (_s.attachMode) {\n\t\t\t\tvar m = new popupMenu();\n\t\t\t\tm.Add(\"Disconnect\", o => _Disconnect(), \"*Codicons.DebugDisconnect\").Tooltip = \"Continue without debugger\";\n\t\t\t\tm.Add(\"End task\", o => _End(), \"*Material.SquareOutline @14\").Tooltip = \"Stop debugging and end task\";\n\t\t\t\tvar r = button.RectInScreen();\n\t\t\t\tm.Show(xy: new(r.left, r.bottom), excludeRect: r, owner: button);\n\t\t\t} else {\n\t\t\t\t_End();\n\t\t\t}\n\t\t}, \"Stop debugging and end task.\\nRight click to disconnect the debugger only.\");\n\t\t_buttons.end.xContextMenu(m => {\n\t\t\tm.Add(\"Disconnect\", o => _Disconnect(), \"*Codicons.DebugDisconnect\", disable: !IsDebugging).Tooltip = \"Continue without debugger\";\n\t\t});\n\t\ttb.Items.Add(new Separator());\n\t\t_buttons.@continue = _TbButton(\"*Codicons.DebugContinue @14\" + c_color, _ => _Continue(), \"Continue\\n\\nF5\");\n\t\t_buttons.pause = _TbButton(\"*Codicons.DebugPause @14\" + c_color, _ => _Pause(), \"Pause\\n\\nF6\");\n\t\t_buttons.next = _TbButton(\"*Codicons.DebugStepOver @14\" + c_color, _ => _Next(), \"Step over\\n\\nF10\");\n\t\t_buttons.step = _TbButton(\"*Codicons.DebugStepInto\" + c_color, _ => _Step(), \"Step into\\n\\nF11\");\n\t\t_buttons.stepOut = _TbButton(\"*Codicons.DebugStepOut\" + c_color, _ => _StepOut(), \"Step out\\n\\nShift+F11\");\n\t\ttb.Items.Add(new Separator());\n\t\t//_TbButton(\"*BoxIcons.RegularMenu\" + c_color3, null,  \"More debugger commands\").xDropdownMenu(_CommandsMenu);\n\t\t_TbButton(\"*EvaIcons.Options2\" + EdIcons.green, null, \"Debugger options\").xDropdownMenu(_OptionsMenu);\n#if DEBUG\n\t\t_TbButton(\"*WeatherIcons.SnowWind #FF3300\", _ => { _Test(); }, \"Test\");\n#endif\n\t\t\n\t\tButton _TbButton(string icon, Action<Button> click, string tooltip/*, bool overflow = false*/) {\n\t\t\tvar v = tb.AddButton(icon, click, tooltip);\n\t\t\t//if (overflow) ToolBar.SetOverflowMode(v, OverflowMode.Always);\n\t\t\treturn v;\n\t\t}\n\t\t\n\t\tb.Options(margin: new());\n\t\t\n\t\tusing (_Header(\"Variables\", false)) {\n\t\t\t\n\t\t}\n\t\t_VariablesViewInit();\n\t\tb.Row((-Math.Max(1, App.Settings.debug.hVar), 1..)).xAddInBorder(_tvVariables, thickness: new(0, 0, 0, 1));\n\t\t\n\t\tusing (_Header(\"Call stack\", true, 0, 16, -1)) {\n\t\t\tb.Skip().Add(out _cbThreads).Tooltip(\"Threads.\\nThe text is thread id and name (Thread.Name).\");\n\t\t\t_cbThreads.SelectionChanged += (_, _) => { if (_cbThreads.SelectedItem is ComboBoxItem k && k.Tag is _THREAD t) _SelectedThread(t); };\n\t\t}\n\t\t_StackViewInit();\n\t\tb.Row((-Math.Max(1, App.Settings.debug.hStack), 1..)).Add(_tvStack);\n\t\t\n\t\tb.End();\n\t\t_UpdateUI(_UU.Init);\n\t\t\n\t\t_ipanel = Panels.PanelManager[\"Debug\"];\n\t\t_ipanel.DontActivateFloating = e => true;\n\t\t\n\t\tUsingEndAction _Header(string text, bool splitter, params WBGridLength[] cols) {\n\t\t\tb.Row(0);\n\t\t\tif (splitter) {\n\t\t\t\tb.Add(out GridSplitter k).Splitter(vertical: false, thickness: double.NaN).And(0);\n\t\t\t\tk.DragCompleted += (_, e) => { App.Settings.debug.hVar = _tvVariables.ActualHeight; App.Settings.debug.hStack = _tvStack.ActualHeight; };\n\t\t\t}\n\t\t\tb.StartGrid().Columns(cols);\n\t\t\tb.Add(out TextBlock t).FormatText($\"<b>{text}</b>\").Margin(\"2\").Align(y: VerticalAlignment.Bottom);\n\t\t\tt.IsHitTestVisible = false;\n\t\t\treturn new UsingEndAction(() => b.End());\n\t\t}\n\t}\n\t\n\tpublic UserControl P { get; } = new();\n\t\n\tpublic bool IsDebugging { get; private set; }\n\t\n\tpublic bool IsStopped { get; private set; }\n\t\n\tvoid _Start(FileNode restart = null, _RthData runToHere = null) {\n\t\tFileNode f;\n\t\tif (restart == null) {\n\t\t\tf = App.Model.CurrentFile;\n\t\t\tif (f == null) return;\n\t\t\tif (f.FindProject(out _, out var projMain)) f = projMain;\n\t\t} else {\n\t\t\tif (restart.IsAlien) return;\n\t\t\tf = restart;\n\t\t}\n\t\t\n\t\tif (IsDebugging) {\n\t\t\t_restart = true;\n\t\t\t_s.file = f;\n\t\t\t_s.restartToHere = runToHere;\n\t\t\t_End();\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tif (s_event == 0) s_event = Api.CreateEvent2(0, false, false, \"Au.event.Debugger\");\n\t\telse Api.ResetEvent(s_event);\n\t\t\n\t\tint processId = CompileRun.CompileAndRun(true, f, noDefer: true, runFromEditor: true, debugAttach: processId => _Attach(processId, false, f, runToHere));\n\t\tif (processId <= 0) {\n\t\t\tif (restart != null) _UpdateUI(_UU.Ended);\n\t\t}\n\t}\n\t\n\tvoid _Restart(_RthData runToHere = null) {\n\t\tif (_s?.file is { } f) _Start(f, runToHere);\n\t}\n\t\n\tbool _Attach(int processId, bool attachMode, FileNode file, _RthData runToHere = null) {\n\t\t//#if DEBUG\n\t\t//\t\tprint.clear(); print.qm2.clear();\n\t\t//#endif\n\t\t\n\t\tint arch = Cpp.Cpp_GetProcessArchitecture(processId);\n\t\tif(arch == 1) { _Print(\"Cannot debug x86 processes (platform x86).\"); return false; }\n\t\t\n\t\t_restart = false;\n\t\t_s = new(processId, attachMode) { file = file };\n\t\t_d = new _Debugger(_Events);\n\t\tif (!_d.Init(arch == 3)) return _Failed();\n\t\tIsDebugging = true;\n\t\t\n\t\t_SetOptions();\n\t\t_SetExceptions(0);\n\t\t_SetBreakpoints();\n\t\tif (runToHere != null) _RunToHere(runToHere.file, runToHere.line, runToHere.nonstop);\n\t\t\n\t\tif (!attachMode) _TempDisableAuDebugging(true);\n\t\t\n\t\tif (_d.SendSync(1, $\"-target-attach {processId}\") != \"^done\") {\n\t\t\t_Print(\"Failed to attach debugger.\"); //never mind: fails to attach to 32-bit process, error \"parameter incorrect\"\n\t\t\tIsDebugging = false;\n\t\t\t_d.Send($\"-gdb-exit\");\n\t\t\treturn _Failed();\n\t\t}\n\t\t\n\t\t_s.attached = true;\n\t\t_UpdateUI(_UU.Started);\n\t\t_PrintThread(null, true);\n\t\t_AutoShowHidePanel(true);\n\t\tRegHotkeys.RegisterDebug();\n\t\tprint.it(\"<><lc #C0C0FF>Debugging started<>\");\n\t\tApi.SetEvent(s_event); //let the process run\n\t\treturn true;\n\t\t\n\t\tbool _Failed() {\n\t\t\t_d.Dispose();\n\t\t\t_d = null;\n\t\t\t_UpdateUI(_UU.Ended);\n\t\t\t_s = null;\n\t\t\treturn false;\n\t\t}\n\t}\n\t\n\tpublic void Start() {\n\t\t_Start();\n\t}\n\t\n\tpublic bool Attach(int processId) {\n\t\tif (IsDebugging) return false;\n\t\tvar f = App.Tasks.TaskFromProcessId(processId)?.f;\n\t\tif (f == null) return false;\n\t\treturn _Attach(processId, true, f);\n\t}\n\t\n\tpublic bool EndIfDebugging(int processId) {\n\t\tif (!IsDebugging || processId != _s.processId) return false;\n\t\t_End();\n\t\treturn true;\n\t}\n\t\n\tinternal void WmHotkey_(RegHotkeys.Id id) {\n\t\tif (!IsDebugging) return;\n\t\tswitch (id) {\n\t\tcase RegHotkeys.Id.DebugNext: _Next(); break;\n\t\tcase RegHotkeys.Id.DebugStep: _Step(); break;\n\t\tcase RegHotkeys.Id.DebugStepOut: _StepOut(); break;\n\t\tcase RegHotkeys.Id.DebugContinue: _Continue(); break;\n\t\tcase RegHotkeys.Id.DebugPause: _Pause(); break;\n\t\t\t//case RegHotkeys.Id.DebugEnd: _End(); break;\n\t\t\t//case RegHotkeys.Id.DebugRestart: _Restart(); break;\n\t\t}\n\t}\n\t\n\t#region options, exceptions, breakpoints\n\t\n\tvoid _OptionsMenu(popupMenu m) {\n\t\tm[(App.Settings.debug.breakT & 9) switch { 1 => \"Exceptions...  (break when thrown)\", 9 => \"Exceptions...  (break when caught)\", _ => \"Exceptions...\" }] = _DExceptionTypes;\n\t\tm.AddCheck(\"Step into properties and operators\", App.Settings.debug.stepIntoAll, o => {\n\t\t\tApp.Settings.debug.stepIntoAll = o.IsChecked;\n\t\t\tif (IsDebugging) _d.Send(\"-gdb-set enable-step-filtering \" + (o.IsChecked ? \"0\" : \"1\"));\n\t\t});\n\t\tm.AddCheck(\"Debug optimized code\", App.Settings.debug.noJMC, o => { App.Settings.debug.noJMC = o.IsChecked; }, disable: IsDebugging);\n\t\tm.Separator();\n\t\tm.AddCheck(\"Print clicked variable in 1 line\", App.Settings.debug.printVarCompact, o => { App.Settings.debug.printVarCompact = o.IsChecked; });\n\t\tm.AddCheck(\"Print 'module loaded/unloaded'\", (App.Settings.debug.printEvents & 1) != 0, o => { App.Settings.debug.printEvents ^= 1; });\n\t\tm.AddCheck(\"Print 'thread started/ended'\", (App.Settings.debug.printEvents & 2) != 0, o => { App.Settings.debug.printEvents ^= 2; });\n\t\tm.Separator();\n\t\tm.AddCheck(\"Activate LA when stepping\", App.Settings.debug.activateLA, o => { App.Settings.debug.activateLA ^= true; });\n\t\t\n\t\tvoid _DExceptionTypes(PMItem mi) {\n\t\t\tvar w = new KDialogWindow();\n\t\t\tw.InitWinProp(\"Exception settings\", App.Wmain);\n\t\t\tvar b = new wpfBuilder(w).WinSize(450, 400).Columns(-1);\n\t\t\t\n\t\t\tb.StartStack();\n\t\t\tb.Add(out KCheckBox cThrown, \"Break when exception thrown\").Checked((App.Settings.debug.breakT & 1) != 0)\n\t\t\t\t.Add(out KCheckBox cCaught, \"when caught in user code\").Checked((App.Settings.debug.breakT & 8) != 0).xBindCheckedEnabled(cThrown);\n\t\t\tb.End();\n\t\t\tb.StartStack().Margin(\"TBL16\");\n\t\t\tb.Add(out KCheckBox cUseListT, \"If exception is\").Checked((App.Settings.debug.breakT & 2) != 0)\n\t\t\t\t.Add(out KCheckBox cNotT, \"not\").Checked((App.Settings.debug.breakT & 4) != 0).xBindCheckedEnabled(cUseListT);\n\t\t\tb.End();\n\t\t\tb.Row(-2).Add(out TextBox eListT, App.Settings.debug.breakListT).LabeledBy(cUseListT).Multiline(wrap: TextWrapping.NoWrap).Margin(\"B10\")\n\t\t\t\t.Tooltip(\"\"\"\nException types.\nIf the list is empty or 'if exception is' unchecked, will break on all exceptions.\nExample:\nSystem.DivideByZeroException\nSystem.IO.FileNotFoundException\n//comment\n\"\"\");\n\t\t\t\n\t\t\tb.Add(out KCheckBox cUU, \"Break when exception unhandled in user code is caught elsewhere\").Checked((App.Settings.debug.breakU & 1) != 0);\n\t\t\tb.Add<Label>(\"If exception is not\").Margin(\"TBL16\");\n\t\t\tb.Row(-1).Add(out TextBox eListU, App.Settings.debug.breakListU).LabeledBy().Multiline(wrap: TextWrapping.NoWrap)\n\t\t\t\t.Tooltip(\"\"\"\nException types.\nExample:\nSystem.OperationCanceledException\nSystem.Threading.Tasks.TaskCanceledException\n//comment\n\"\"\");\n\t\t\t\n\t\t\tb.R.AddOkCancel();\n\t\t\tif (!w.ShowAndWait()) return;\n\t\t\t\n\t\t\tApp.Settings.debug.breakListT = eListT.TextOrNull();\n\t\t\tApp.Settings.debug.breakListU = eListU.TextOrNull();\n\t\t\tApp.Settings.debug.breakT.SetFlag_(1, cThrown.IsChecked);\n\t\t\tApp.Settings.debug.breakT.SetFlag_(2, cUseListT.IsChecked);\n\t\t\tApp.Settings.debug.breakT.SetFlag_(4, cNotT.IsChecked);\n\t\t\tApp.Settings.debug.breakT.SetFlag_(8, cCaught.IsChecked);\n\t\t\tApp.Settings.debug.breakU.SetFlag_(1, cUU.IsChecked);\n\t\t\t_SetExceptions(3);\n\t\t\t\n\t\t\t//CONSIDER: allow to specify stack patterns where 'thrown' exceptions are ignored\n\t\t}\n\t}\n\t\n\tvoid _SetOptions() {\n\t\tif (App.Settings.debug.stepIntoAll) _d.Send(\"-gdb-set enable-step-filtering 0\");\n\t\tif (App.Settings.debug.noJMC) _d.Send(\"-gdb-set just-my-code 0\");\n\t}\n\t\n\t/// <param name=\"action\">0 init, 1 change 'throw', 2 change 'user-unhandled', 3 change both.</param>\n\tvoid _SetExceptions(int action) {\n\t\tif (!IsDebugging) return;\n\t\tif (action is 0 or 1 or 3) _Apply(\"throw\", App.Settings.debug.breakT, App.Settings.debug.breakListT, ref _s.exceptionsT);\n\t\tif (action is 0 or 2 or 3) _Apply(\"user-unhandled\", App.Settings.debug.breakU | 6, App.Settings.debug.breakListU, ref _s.exceptionsU);\n\t\t\n\t\tvoid _Apply(string tuu, int flags, string types, ref string[] ids) {\n\t\t\tif (action != 0 && ids != null) _d.SendSync(3, $\"-break-exception-delete {string.Join(' ', ids)}\");\n\t\t\tids = null;\n\t\t\tif ((flags & 1) != 0) {\n\t\t\t\tstring etypes = \"*\";\n\t\t\t\tif ((flags & 2) != 0 && !types.NE()) {\n\t\t\t\t\tusing (new StringBuilder_(out var sb)) {\n\t\t\t\t\t\tbool appendNot = (flags & 4) != 0;\n\t\t\t\t\t\tforeach (var v in types.Split_('\\n')) {\n\t\t\t\t\t\t\tif (v.Starts(\"//\")) continue;\n\t\t\t\t\t\t\tif (appendNot) { appendNot = false; sb.Append(\"--not\"); }\n\t\t\t\t\t\t\tsb.Append(' ').Append(v);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (sb.Length > 0) etypes = sb.ToString();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tvar s = _d.SendSync(2, $\"-break-exception-insert {tuu} {etypes}\");\n\t\t\t\tif (s.Starts(\"^done,bkpt={\")) { //^done,bkpt={number=\"1\"}\n\t\t\t\t\tids = [s.Split('\"')[1]];\n\t\t\t\t} else if (s.Starts(\"^done,bkpt=[\")) { //^done,bkpt=[{number=\"1\"},{number=\"2\"}]\n\t\t\t\t\tvar a = new _MiRecord(s).data[\"bkpt\"].AsArray();\n\t\t\t\t\tids = new string[a.Count];\n\t\t\t\t\tfor (int i = 0; i < ids.Length; i++) ids[i] = (string)a[i][\"number\"];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\t//With -break-exception-insert can be specified unhandled|user-unhandled|throw|throw+user-unhandled.\n\t\t//\tIt seems 'unhandled' is always on and cannot be changed.\n\t\t//\t'user-unhandled' means \"Break when handled exception was unhandled in user code\". Eg in Task.Run action when not awaited.\n\t}\n\t\n\tvoid _SetBreakpoints() {\n\t\tforeach (var b in Panels.Breakpoints.GetBreakpoints()) {\n\t\t\t_SetBreakpoint(b);\n\t\t}\n\t}\n\t\n\tvoid _SetBreakpoint(IBreakpoint b) {\n\t\tb.Id = _SetBreakpoint(b.File, b.Line);\n\t\tif (_s.runToHere.nonstop && b.Id != _s.runToHere.breakpointId) _d.Send($\"-break-activate false {b.Id}\");\n\t\tif (b.HasProperties) _SetBreakpointCondition(b);\n\t}\n\t\n\tint _SetBreakpoint(FileNode f, int line) {\n\t\tvar s = _d.SendSync(4, $\"-break-insert \\\"{f.FilePath.Replace(@\"\\\", @\"\\\\\")}:{line + 1}\\\"\");\n\t\tvar r = new _MiRecord(s);\n\t\tvar d = r.Data<_DONE_BKPT>().bkpt;\n\t\treturn d.number;\n\t}\n\t\n\tinternal void BreakpointAddedDeleted_(IBreakpoint b, bool added) {\n\t\tif (!IsDebugging) return;\n\t\tif (added) {\n\t\t\tApp.Dispatcher.InvokeAsync(() => { if (IsDebugging) _SetBreakpoint(b); });\n\t\t} else {\n\t\t\tint n = b.Id;\n\t\t\tb.Id = 0;\n\t\t\tDebug_.PrintIf(n == 0);\n\t\t\tif (n != 0 && n != _s.runToHere.breakpointId) _d.Send($\"-break-delete {n}\");\n\t\t}\n\t}\n\t\n\tinternal void BreakpointPropertiesChanged_(IBreakpoint b) {\n\t\tif (IsDebugging) _SetBreakpointCondition(b);\n\t}\n\t\n\tvoid _SetBreakpointCondition(IBreakpoint b) {\n\t\tstring s;\n\t\tif (b.HasProperties) {\n\t\t\tstring cond = b.Condition, log = b.Log;\n\t\t\tcond = cond?.RxReplace(@\"[\\r\\n]+\", \" \");\n\t\t\tif (log.NE()) s = cond;\n\t\t\telse {\n\t\t\t\tlog = log.RxReplace(@\"[\\r\\n]+\", \" \");\n\t\t\t\tif (!b.LogExpression) log = log.Escape(quote: true);\n\t\t\t\telse log = $\"({log}).ToString()\";\n\t\t\t\tif (cond.NE()) cond = \"true\";\n\t\t\t\ts = $\"Au.More.LaDebugger_.Logpoint({cond}, {log}, \\\"{b.File.IdStringWithWorkspace}|{b.Line + 1}\\\")\";\n\t\t\t}\n\t\t} else s = \"false\";\n\t\t_d.Send($\"-break-condition {b.Id} {s}\");\n\t\t//note: without \"\"\n\t}\n\t\n\t#endregion\n\t\n\tinternal void AddMarginMenuItems_(SciCode doc, popupMenu m, int line) {\n\t\tm[\"Run to here\", \"*JamIcons.ArrowCircleDownRight @14\" + EdIcons.blue] = o => _RunToHere(doc.EFile, line, false);\n\t\tm[\"Run to here non-stop\", \"*JamIcons.ArrowCircleDownRight @14\" + EdIcons.blue] = o => _RunToHere(doc.EFile, line, true);\n\t\tif (IsDebugging) m[\"Restart and run to here non-stop\", \"*Codicons.DebugRestart @14\" + EdIcons.green2] = o => _RunToHere(doc.EFile, line, true, true);\n\t\tif (IsStopped) m[\"Jump to here\", \"*Codicons.DebugStackframe @14\" + EdIcons.green2] = o => _JumpToHere(doc.EFile, line);\n\t}\n\t\n\tvoid _Step(string s) {\n\t\tif (!IsStopped) return;\n\t\tbool step = s[0] != 'c';\n\t\tif (step && _s.stoppedOnException) s = \"finish\"; //workaround for: if was stopped on user-unhandled exception, 'step' and 'next' behave like 'continue'.\n\t\t_s.stoppedOnException = false;\n\t\tif (_ExecStepL(step && _s.threadId != 0 ? $\"-exec-{s} --thread {_s.threadId}\" : $\"-exec-{s}\")) {\n\t\t\tIsStopped = false;\n\t\t\t_s.stepping = step;\n\t\t\t_s.frame = null;\n\t\t\t_UpdateUI(_UU.Resumed);\n\t\t} else if (step) { //eg when paused in [Native Frames]. Never seen after modifying netcoredbg.\n\t\t\t_Print(\"Can't step here. Try 'Continue' or 'Run to here'.\");\n\t\t}\n\t}\n\t\n\tvoid _Next() => _Step(\"next\");\n\t\n\tvoid _Step() => _Step(\"step\");\n\t\n\tvoid _StepOut() => _Step(\"finish\");\n\t\n\tvoid _Continue() => _Step(\"continue\");\n\t\n\tbool _ExecStepL(string s) {\n\t\t//return _d.SendSync(6, s) == \"^running\";\n\t\treturn _d.SendSync(6, s, o => {\n\t\t\tint i = 0; while (i < o.Length && o[i].IsAsciiDigit()) i++;\n\t\t\t//print.it($\"<><c blue>{o[i..]}<>\");\n\t\t\tif (o.Eq(i, \"^done,variables=\") || o.Eq(i, \"^done,stack=\")) return true; //old\n\t\t\treturn false;\n\t\t}) == \"^running\";\n\t}\n\t\n\tvoid _RunToHere(FileNode f, int line, bool nonstop, bool restart = false) {\n\t\tif (!IsDebugging) {\n\t\t\t_Start(runToHere: new(f, line, nonstop));\n\t\t} else if (restart) {\n\t\t\t_Restart(runToHere: new(f, line, nonstop));\n\t\t} else {\n\t\t\tif (_s.runToHere.breakpointId != 0) _RthEnd();\n\t\t\tif (_s.runToHere.nonstop = nonstop) _d.Send(\"-break-activate false\");\n\t\t\t_s.runToHere.breakpointId = _SetBreakpoint(f, line);\n\t\t\tif (nonstop && _IsEnabledBreakpoint(_s.runToHere.breakpointId)) _d.Send($\"-break-activate true {_s.runToHere.breakpointId}\");\n\t\t\tif (IsStopped) _Continue();\n\t\t}\n\t}\n\t\n\tvoid _RthEnd() {\n\t\tif (!_IsEnabledBreakpoint(_s.runToHere.breakpointId)) _d.Send($\"-break-delete {_s.runToHere.breakpointId}\");\n\t\t_s.runToHere.breakpointId = 0;\n\t\tif (_s.runToHere.nonstop) { _s.runToHere.nonstop = false; _d.Send(\"-break-activate true\"); }\n\t}\n\t\n\tvoid _JumpToHere(FileNode f, int line) {\n\t\t_s.stoppedOnException = false;\n\t\tvar s = _d.SendSync(7, $\"-jump \\\"{f.FilePath.Replace(@\"\\\", @\"\\\\\")}:{line + 1}\\\"\");\n\t\tif (s?.Starts(\"^done,sp=\") == true) {\n\t\t\tint i = 9;\n\t\t\tline = s.ToInt(i, out i) - 1;\n\t\t\tint endLine = s.ToInt(++i, out i) - 1;\n\t\t\tint col = s.ToInt(++i, out i) - 1;\n\t\t\tint endCol = s.ToInt(++i, out i) - 1;\n\t\t\tApp.Model.OpenAndGoTo(f, line, col, activateLA: App.Settings.debug.activateLA);\n\t\t\t_marker2.Delete();\n\t\t\t_marker.Add(line, col, endLine, endCol);\n\t\t\t_d.Send($\"-stack-list-frames --thread {_s.threadId}\");\n\t\t} else {\n\t\t\tif (s.Like(\"^error,msg=\\\"*\\\"\")) s = s[12..^1];\n\t\t\tif (s == \"SetIP cannot be done on any frame except the leaf frame.\") _Print(\"Cannot jump to another function.\");\n\t\t\telse _Print(\"Cannot jump to here. \" + s);\n\t\t}\n\t}\n\t\n\tbool _IsEnabledBreakpoint(int id) => Panels.Breakpoints.GetBreakpoints().Any(o => o.Id == id);\n\t\n\trecord class _RthData(FileNode file, int line, bool nonstop);\n\t\n\tvoid _Pause() {\n\t\tif (!IsDebugging || IsStopped) return;\n\t\tint tid = _s.threadId;\n\t\tif (tid == 0) { //pause main thread\n\t\t\ttry {\n\t\t\t\tif (_s.process == null) _s.process = Process.GetProcessById(_s.processId); else _s.process.Refresh();\n\t\t\t\ttid = _s.process.Threads[0].Id;\n\t\t\t}\n\t\t\tcatch (Exception e1) { Debug_.Print(e1); }\n\t\t}\n\t\t_d.Send($\"-exec-interrupt --thread {tid}\"); //note: netcoredbg code is modified, added --thread parameter. If --thread 0, works like without --thread.\n\t}\n\t\n\tvoid _End() {\n\t\tif (IsDebugging) _d.Send($\"-exec-abort\");\n\t}\n\t\n\tvoid _Disconnect() {\n\t\tif (IsDebugging) _d.Send($\"-gdb-exit\"); //detach and exit debugger\n\t}\n\t\n\tvoid _Events(string s) {\n\t\tif (_s == null) {\n\t\t\tDebug_.Print(\"_s==null\");\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tif (s.ToInt(out int token, 0, out int endToken)) s = s[endToken..]; //info: currently not using tokens\n\t\t\n#if DEBUG\n\t\tif (s.Starts(\"^error\")) print.it($\"<><c red>{s}<>\");\n\t\t//else if (0 == s.Starts(true, \"^done\", \"=message,\", \"=library-\", \"=thread-\")) print.it(\"EVENT\", s);\n#endif\n\t\t\n\t\tif (s == \"^exit\") {\n\t\t\t_d.Dispose();\n\t\t\t_d = null;\n\t\t\tIsDebugging = IsStopped = false;\n\t\t\t_UpdateUI(_UU.Ended);\n\t\t\tRegHotkeys.UnregisterDebug();\n\t\t\t_AutoShowHidePanel(false);\n\t\t\t\n\t\t\tif (_restart) {\n\t\t\t\t_restart = false;\n\t\t\t\tvar file = _s.file;\n\t\t\t\tvar rth = _s.restartToHere;\n\t\t\t\ttimer.after(100, _ => _Start(file, rth));\n\t\t\t}\n\t\t\t\n\t\t\t_s = null;\n\t\t\tprint.it(\"<><lc #C0C0FF><>\");\n\t\t} else if (s.Starts(\"*stopped\")) {\n\t\t\tif (s.Eq(17, \"exited\")) {\n\t\t\t\tIsDebugging = IsStopped = false;\n\t\t\t\t_d.Send($\"-gdb-exit\");\n\t\t\t\t_UpdateUI(_UU.Ended);\n\t\t\t\tif (s.RxMatch(@\"\\bexit-code=\"\"(.+?)\"\"\", 1, out string ec)) _Print($\"The process has exited with code {ec} (0x{ec.ToInt():X}).\");\n\t\t\t} else {\n\t\t\t\tIsStopped = true;\n\t\t\t\t\n\t\t\t\t_s.inStoppedEvent = true;\n\t\t\t\ttry { if (!_Stopped(s)) return; }\n\t\t\t\tfinally { _s.inStoppedEvent = false; }\n\t\t\t\t\n\t\t\t\t_UpdateUI(_UU.Paused);\n\t\t\t}\n\t\t} else if (s.Starts(\"^done,stack=\")) {\n\t\t\tif (!IsStopped) return;\n\t\t\t_FRAME[] a = new _MiRecord(s).Data<_DONE_STACK>().stack;\n\t\t\tif (a.Length == 0) return;\n\t\t\t\n\t\t\tint iSelect = Math.Max(0, Array.FindIndex(a, o => o.fullname?.Starts(App.Model.FilesDirectory) ?? false));\n\t\t\t\n\t\t\t_StackViewSetItems(a, iSelect);\n\t\t\t_s.frame = a[iSelect];\n\t\t\t_ListVariables();\n\t\t\t\n\t\t\tif (!_marker.Exists && !_marker2.Exists) {\n\t\t\t\tfor (int i = iSelect; i < a.Length; i++) {\n\t\t\t\t\tif (_GoToLine(a[i])) break;\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (s.Starts(\"^done,variables=\")) {\n\t\t\tif (!IsStopped) return;\n\t\t\t_VARIABLE[] a = new _MiRecord(s).Data<_DONE_VARIABLES>().variables;\n\t\t\t_VariablesViewSetItems(a);\n\t\t} else if (s.Starts('=')) {\n\t\t\tif (s.Starts(\"=library-\")) {\n\t\t\t\tvar r = new _MiRecord(s);\n\t\t\t\tstring id = (string)r.data[\"id\"], lib = (string)r.data[\"target_name\"];\n\t\t\t\tif (s.Starts(\"=library-loaded,\")) {\n\t\t\t\t\t_s.modules[id] = lib;\n\t\t\t\t\tif ((App.Settings.debug.printEvents & 1) != 0) _Print($\"Module loaded{((string)r.data[\"symbols_loaded\"] == \"1\" ? \" with symbols\" : \"\")}: {lib}\");\n\t\t\t\t} else if (s.Starts(\"=library-unloaded,\")) {\n\t\t\t\t\t_s.modules.Remove(id);\n\t\t\t\t\tif ((App.Settings.debug.printEvents & 1) != 0) _Print($\"Module unloaded: {lib}\");\n\t\t\t\t}\n\t\t\t} else if (s.Starts(\"=thread-created,id=\")) {\n\t\t\t\t_PrintThread(s, true);\n\t\t\t} else if (s.Starts(\"=thread-exited,id=\")) {\n\t\t\t\t_PrintThread(s, false);\n\t\t\t\tif (s.ToInt(19) == _s.threadId) _s.threadId = 0;\n\t\t\t} else if (s.Starts(\"=message,\")) { //Debug.Print etc, like =message,text=\"TEXT\\r\\n\",send-to=\"output-window\",source=\"Debugger.Log\"\n\t\t\t\tvar r = new _MiRecord(s);\n\t\t\t\tvar text = ((string)r.data[\"text\"]).TrimEnd();\n\t\t\t\t_Print(text.Starts(\"<>\") ? text[2..] : $\"<\\a>{text}</\\a>\");\n\t\t\t\t//BAD: not in sync with print.it.\n\t\t\t}\n\t\t}\n\t}\n\t\n\t//returns false to continue\n\tbool _Stopped(string s) {\n\t\tvar x = new _MiRecord(s).Data<_STOPPED>();\n\t\t_s.threadId = x.thread_id;\n\t\t_s.stoppedOnException = false;\n\t\tvar thrownException = _s.thrownException; _s.thrownException = null;\n\t\t\n\t\tswitch (x.reason) {\n\t\tcase \"breakpoint-hit\":\n\t\t\tif (_s.runToHere.breakpointId != 0 && x.bkptno == _s.runToHere.breakpointId) {\n\t\t\t\t_RthEnd();\n\t\t\t\t//never mind: does not delete the _runToHere breakpoint if stops there on exception\n\t\t\t} else {\n\t\t\t\tif (!x.exception.NE()) _Print($\"<c red>{x.exception}<>\"); //bad expression of condition or logpoint\n\t\t\t}\n\t\t\tbreak;\n\t\tcase \"exception-received\":\n\t\t\tif (_StoppedOnException(x)) return false;\n\t\t\t_s.stoppedOnException = true;\n\t\t\tbreak;\n\t\tcase \"end-stepping-range\":\n\t\t\tif (thrownException != null) if (!_DetectCatch(x, thrownException)) return false;\n\t\t\tbreak;\n\t\t\t//case \"signal-received\": //Pause or Debugger.Break\n\t\t\t\n\t\t\t//\tbreak;\n\t\t}\n\t\t\n\t\tif (_GetThreads() is _THREAD[] a) { //fast\n\t\t\ta = a.Where(t => t.id == _s.threadId || !_IsHiddenThreadName(t.name)).ToArray();\n\t\t\tbool same = a.Length == _cbThreads.Items.Count;\n\t\t\tif (same) for (int i = 0; i < a.Length; i++) if (!(same = _cbThreads.Items[i] is ComboBoxItem k && k.Tag is _THREAD t && t == a[i])) break;\n\t\t\tif (!same) {\n\t\t\t\t_cbThreads.Items.Clear();\n\t\t\t\tforeach (var t in a) _cbThreads.Items.Add(new ComboBoxItem { Content = $\"{t.id}  {t.name}\", Tag = t });\n\t\t\t}\n\t\t\t\n\t\t\t_GoToLine(x.frame);\n\t\t\t\n\t\t\tint iSel = Array.FindIndex(a, t => t.id == _s.threadId);\n\t\t\tif (iSel >= 0) {\n\t\t\t\tif (iSel != _cbThreads.SelectedIndex) _cbThreads.SelectedIndex = iSel; //_cbThreads.SelectionChanged -> _SelectedThread\n\t\t\t\telse _SelectedThread(a[iSel]);\n\t\t\t}\n\t\t}\n\t\t\n\t\treturn true;\n\t\t\n\t\t_THREAD[] _GetThreads() {\n\t\t\tif (_d.SendSync(5, \"-thread-info\") is string s && s.Starts(\"^done,threads=\")) {\n\t\t\t\t_THREAD[] a = new _MiRecord(s).Data<_DONE_THREADS>().threads;\n\t\t\t\tforeach (var t in a) {\n\t\t\t\t\tvar s1 = t.name;\n\t\t\t\t\t_GetThreadNameAndTime(t.id, out t.name, out t.time);\n\t\t\t\t\tif (t.name == null && s1 != \"<No name>\") t.name = s1;\n\t\t\t\t}\n\t\t\t\ta = a.OrderBy(o => o.time).ToArray();\n\t\t\t\ta[0].name ??= \"Main Thread\";\n\t\t\t\treturn a;\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\t\n\t\tbool _StoppedOnException(_STOPPED x) {\n\t\t\tif (_s.runToHere.nonstop && x.exception_stage != \"unhandled\") {\n\t\t\t\t_Continue();\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\t\n\t\t\tvar stage = x.exception_stage switch { \"throw\" => \"thrown\", \"user-unhandled\" => \"unhandled in user code\", _ => x.exception_stage };\n\t\t\t\n\t\t\tif ((App.Settings.debug.breakT & 9) == 9 && x.exception_stage == \"throw\") {\n\t\t\t\t_s.thrownException = x;\n\t\t\t\t_s.tePath = null;\n\t\t\t\tif (_ExecStepL(\"-exec-finish\")) return true;\n\t\t\t\tDebug_.Print(\"-exec-x failed on thrown exception\");\n\t\t\t\t_s.thrownException = null;\n\t\t\t}\n\t\t\t\n\t\t\t_PrintException(x, stage, false);\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\t//Returns: true - stop, false - continue.\n\t\tbool _DetectCatch(_STOPPED x, _STOPPED thrownException) {\n\t\t\tvar f = x.frame;\n\t\t\tif (!f.fullname.NE()) {\n\t\t\t\ttry {\n\t\t\t\t\tvar s = filesystem.loadText(f.fullname);\n\t\t\t\t\tfor (int i = 0, line = 0; i < s.Length; i++) {\n\t\t\t\t\t\tif (++line == f.line) {\n\t\t\t\t\t\t\tint pos = i + f.col - 1;\n\t\t\t\t\t\t\tif (s.Eq(pos, \"catch\") || s.Eq(pos, \"when\")) {\n\t\t\t\t\t\t\t\tvar tok = CiUtil.CreateSyntaxTree(s).FindToken(pos);\n\t\t\t\t\t\t\t\tvar cc = tok.Parent as CatchClauseSyntax;\n\t\t\t\t\t\t\t\tif (cc == null && tok.Parent is CatchFilterClauseSyntax) cc = tok.Parent.Parent as CatchClauseSyntax;\n\t\t\t\t\t\t\t\tif (cc != null) {\n\t\t\t\t\t\t\t\t\t_s.thrownException = thrownException;\n\t\t\t\t\t\t\t\t\t_s.tePath = f.fullname;\n\t\t\t\t\t\t\t\t\t_s.teSpan = cc.Block.Span;\n\t\t\t\t\t\t\t\t\tif (_ExecStepL(\"-exec-next\")) return false; //run until `{ }` or next `when`\n\t\t\t\t\t\t\t\t\tDebug_.Print(\"-exec-next failed on detected catch or when\");\n\t\t\t\t\t\t\t\t\t_s.thrownException = null;\n\t\t\t\t\t\t\t\t\t_s.tePath = null;\n\t\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else if (f.fullname == _s.tePath && _s.teSpan.Contains(pos)) {\n\t\t\t\t\t\t\t\t_PrintException(thrownException, \"caught\", true);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (_s.stepping) break;\n\t\t\t\t\t\t\t_Continue();\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t}\n\t\t\t\t\t\ti = s.IndexOf('\\n', i);\n\t\t\t\t\t\tif (i < 0) break;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (Exception e1) { Debug_.Print(e1); }\n\t\t\t} else Debug_.Print(\"non-user code\");\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\tvoid _PrintException(_STOPPED x, string stage, bool caught) {\n\t\t\tif (_VarCreateL(\"$exception.ToString()\") is { } v) {\n\t\t\t\tstring s = v.value.Trim('\"').Unescape(), color = caught ? \"#CC00FF\" : \"red\", append = null;\n\t\t\t\tif (_VarCreateL(\"$exception.InnerException\") is { } vi && vi.value.Starts('{')) {\n\t\t\t\t\tappend = \" See also: Debug panel > Variables > $exception > InnerException.\";\n\t\t\t\t}\n\t\t\t\t_Print($\"<c {color}>{stage.Upper(SUpper.FirstChar)}: {s}\\r\\n{append}<>\");\n\t\t\t}\n\t\t}\n\t}\n\t\n\tvoid _SelectedThread(_THREAD t) {\n\t\tif (!IsStopped) return;\n\t\t//if (!_s.inStoppedEvent) _VariablesViewChangedFrameOrThread();\n\t\tif (t.id != _s.threadId) {\n\t\t\t_s.threadId = t.id;\n\t\t\t_ClearTreeviewsAndMarkers();\n\t\t}\n\t\t_d.Send($\"-stack-list-frames --thread {_s.threadId}\");\n\t}\n\t\n\tbool _GoToLine(_FRAME f, bool keepMarkers = false) {\n\t\tif (f != null) {\n\t\t\tint line = f.line - 1, col = f.col - 1, line2 = f.end_line - 1, col2 = f.end_col - 1;\n\t\t\tif (App.Model.OpenAndGoTo(f.fullname, line, col, activateLA: App.Settings.debug.activateLA)) {\n\t\t\t\tif (f.level > 0) {\n\t\t\t\t\t_marker2.Add(line, col, line2, col2);\n\t\t\t\t} else {\n\t\t\t\t\t_marker2.Delete();\n\t\t\t\t\t_marker.Add(line, col, line2, col2);\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\tif (!keepMarkers) {\n\t\t\t_marker.Delete();\n\t\t\t_marker2.Delete();\n\t\t}\n\t\treturn false;\n\t}\n\t_Marker _marker = new(SciTheme.Marker.DebugLine, SciTheme.Indic.Debug), _marker2 = new(SciTheme.Marker.DebugLine2, SciTheme.Indic.Debug2);\n\t\n\tvoid _UpdateUI(_UU u) {\n\t\tif (u == _UU.Resumed) {\n\t\t\t_timerResumeUU ??= new(_ => _UpdateUI(_UU.Resumed2));\n\t\t\t_timerResumeUU.After(100);\n\t\t} else {\n\t\t\t_timerResumeUU?.Stop();\n\t\t\tbool deb = IsDebugging, stopped = IsStopped;\n\t\t\t_buttons.debug.IsEnabled = !deb;\n\t\t\t_buttons.restart.IsEnabled = deb && !_restart;\n\t\t\t_buttons.end.IsEnabled = deb;\n\t\t\t_buttons.next.IsEnabled = stopped;\n\t\t\t_buttons.step.IsEnabled = stopped;\n\t\t\t_buttons.stepOut.IsEnabled = stopped;\n\t\t\t_buttons.@continue.IsEnabled = stopped;\n\t\t\t_buttons.pause.IsEnabled = deb && !stopped;\n\t\t\tbool restart = deb || _restart;\n\t\t\t_buttons.debug.Visibility = !restart ? Visibility.Visible : Visibility.Collapsed;\n\t\t\t_buttons.restart.Visibility = restart ? Visibility.Visible : Visibility.Collapsed;\n\t\t\t_buttons.@continue.Visibility = !deb || stopped ? Visibility.Visible : Visibility.Collapsed;\n\t\t\t_buttons.pause.Visibility = deb && !stopped ? Visibility.Visible : Visibility.Collapsed;\n\t\t\tif (!stopped && u is _UU.Resumed2 or _UU.Ended) {\n\t\t\t\t_cbThreads.Items.Clear();\n\t\t\t\t_ClearTreeviewsAndMarkers();\n\t\t\t}\n\t\t\t\n\t\t\tif (u == _UU.Ended) _TempDisableAuDebugging(false);\n\t\t}\n\t}\n\t\n\tenum _UU { Init, Started, Ended, Paused, Resumed, Resumed2 }\n\t\n\ttimer _timerResumeUU;\n\t\n\tvoid _ClearTreeviewsAndMarkers() {\n\t\t_StackViewSetItems(null);\n\t\t_VariablesViewSetItems(null);\n\t\t_marker.Delete();\n\t\t_marker2.Delete();\n\t}\n\t\n\tvoid _AutoShowHidePanel(bool starting) {\n\t\tif (starting) {\n\t\t\tif (_hidePanelWhenEnds) _timerHidePanel?.Stop();\n\t\t\tif (!_ipanel.Visible) _hidePanelWhenEnds = true;\n\t\t\t_ipanel.Visible = true;\n\t\t} else if (_hidePanelWhenEnds && !_restart) {\n\t\t\t_timerHidePanel ??= new timer(t => {\n\t\t\t\tif (_ipanel.Visible) {\n\t\t\t\t\tif (_ipanel.Parent.Panel.IsMouseOver) return;\n\t\t\t\t\tvar w = wnd.fromMouse(WXYFlags.Raw);\n\t\t\t\t\tif (w == _tvVariables.Hwnd || w == _tvStack.Hwnd) return; //eg when ending/disconnecting through a context menu of a toolbar button\n\t\t\t\t\t_ipanel.Visible = false;\n\t\t\t\t}\n\t\t\t\tt.Stop();\n\t\t\t\t_hidePanelWhenEnds = false;\n\t\t\t});\n\t\t\t_timerHidePanel.Every(200);\n\t\t}\n\t}\n\tbool _hidePanelWhenEnds;\n\ttimer _timerHidePanel;\n\t\n\t#region util\n\t\n\tstatic void _Print(string s) {\n\t\tprint.it($\"<><lc #f8f8d0>{s}<>\");\n\t}\n\t\n\tstatic unsafe bool _GetThreadNameAndTime(int id, out string name, out long time) {\n\t\tname = null;\n\t\tusing var th = Api.OpenThread(Api.THREAD_QUERY_LIMITED_INFORMATION, false, id);\n\t\tif (!Api.GetThreadTimes(th, out time, out _, out _, out _)) {\n\t\t\ttime = long.MaxValue; //for sorting\n\t\t\treturn false;\n\t\t}\n\t\tif (osVersion.minWin10_1607) {\n\t\t\tif (0x10000000 == Api.GetThreadDescription(th, out var s1) && s1 != null) {\n\t\t\t\tif (s1[0] != 0) name = new string(s1);\n\t\t\t\tApi.LocalFree(s1);\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\t\n\tstatic bool _IsHiddenThreadName(string s) => s is \"Au.Aux\" or \".NET TP Gate\" or \".NET Tiered Compilation Worker\" or \".NET Counter Poller\" or \".NET Finalizer\" or \"Stylus Input\" or \"Au.JSettings\";\n\t\n\tstring _GetModuleName(_FRAME f) {\n\t\tif (f.clr_addr.module_id.NE()) return \"\"; //f.func \"[Native Frames]\"\n\t\tif (_s.modules.TryGetValue(f.clr_addr.module_id, out var r)) r = pathname.getName(r);\n\t\treturn r;\n\t}\n\t\n\tstring _FormatFrameString(_FRAME f/*, bool forPrint = false*/) {\n\t\tif (f.file.NE()) return $\"{_GetModuleName(f)}!{f.func}\";\n\t\t//if (forPrint) return $\"<open {f.file}|{f.line}><\\a>{f.func}  ::  {f.file} {f.line}</\\a><>\";\n\t\treturn $\"{f.func}  ::  {f.file} {f.line}\";\n\t}\n\t\n\tvoid _PrintThread(string s, bool started) {\n\t\tif ((App.Settings.debug.printEvents & 2) == 0) return;\n\t\tif (s == null) { //called when attached. Detects the main thread and prints threads ordered by the start time.\n\t\t\tif (_s.startedThreads is { } a) {\n\t\t\t\tList<(int id, string name, long time)> k = new();\n\t\t\t\tforeach (var v in a) if (_GetThreadNameAndTime(v, out var name, out var time) && !_IsHiddenThreadName(name)) k.Add((v, name, time));\n\t\t\t\tbool once = false;\n\t\t\t\tforeach (var v in k.OrderBy(o => o.time)) {\n\t\t\t\t\tstring name = v.name;\n\t\t\t\t\tif (!once) { once = true; name ??= \"Main Thread\"; }\n\t\t\t\t\t_Print($\"Thread started: {v.id} {name}\");\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tint id = s.ToInt(s.Find(\",id=\\\"\") + 5);\n\t\t\tif (_s.attached) {\n\t\t\t\tif (_GetThreadNameAndTime(id, out var name, out _) && !_IsHiddenThreadName(name))\n\t\t\t\t\t_Print($\"Thread {(started ? \"started\" : \"ended\")}: {id} {name}\");\n\t\t\t} else if (started) {\n\t\t\t\t(_s.startedThreads ??= new()).Add(id);\n\t\t\t}\n\t\t}\n\t}\n\t\n\t[Conditional(\"DEBUG\")]\n\tstatic void _TempDisableAuDebugging(bool disable) {\n\t\tif (disable == _tempDisableAuDebugging) return;\n\t\tif (disable) if (!App.IsAtHome || Debugger.IsAttached) return;\n\t\tstring from = folders.ThisAppBS + (disable ? \"Au.pdb\" : \"Au-.pdb\"), to = disable ? \"Au-.pdb\" : \"Au.pdb\";\n\t\ttry { filesystem.rename(from, to, FIfExists.Delete); }\n\t\tcatch (Exception e1) { Debug_.Print(e1); return; }\n\t\t_tempDisableAuDebugging = disable;\n\t}\n\tstatic bool _tempDisableAuDebugging;\n\t\n\t#endregion\n\t\n\tclass _Marker {\n\t\treadonly int _marker, _indic;\n\t\tSciCode _doc;\n\t\tint _line, _handle;\n\t\t\n\t\tpublic _Marker(int marker, int indic) {\n\t\t\t_marker = marker;\n\t\t\t_indic = indic;\n\t\t}\n\t\t\n\t\tpublic void Add(int line, int column, int line2, int column2) {\n\t\t\t//Column = column;\n\t\t\tvar doc = Panels.Editor.ActiveDoc;\n\t\t\t\n\t\t\tif (line != _line || doc != _doc) {\n\t\t\t\tDelete();\n\t\t\t\t_doc = doc;\n\t\t\t\t_line = line;\n\t\t\t\t_handle = _doc.aaaMarkerAdd(_marker, line);\n\t\t\t} else _doc.aaaIndicatorClear(_indic);\n\t\t\t\n\t\t\tint start = doc.aaaLineStart(false, line) + column;\n\t\t\tint end = doc.aaaLineStart(false, line2) + column2;\n\t\t\tdoc.aaaIndicatorAdd(_indic, false, start..end);\n\t\t}\n\t\t\n\t\tpublic void Delete() {\n\t\t\tif (_doc == null) return;\n\t\t\tif (!_doc.AaWnd.Is0) {\n\t\t\t\t_doc.aaaMarkerDeleteHandle(_handle);\n\t\t\t\t_doc.aaaIndicatorClear(_indic);\n\t\t\t}\n\t\t\t_doc = null;\n\t\t\t_line = 0;\n\t\t\t_handle = 0;\n\t\t}\n\t\t\n\t\tpublic bool Exists => _doc != null;\n\t\t\n\t\tpublic SciCode Doc => _doc;\n\t\t//public int Line => _line;\n\t\t//public int Column { get; private set; }\n\t}\n\t\n\t#region MI output record types. Generated by _MiRecord._PrintType.\n\t\n\trecord _BKPT(int number, string type, string disp, string enabled, string func, string file, string fullname, int line, string warning);\n\trecord _DONE_BKPT(_BKPT bkpt);\n\t\n\trecord _THREAD(int id, string state) { public string name; public long time; }\n\trecord _DONE_THREADS(_THREAD[] threads);\n\t\n\trecord struct _CLR_ADDR(string module_id, string method_token, int method_version, int il_offset, int native_offset);\n\trecord _FRAME(int level, string file, string fullname, int line, int col, int end_line, int end_col, _CLR_ADDR clr_addr, string func, string addr, string active_statement_flags);\n\trecord _STOPPED(string reason, int thread_id, string stopped_threads, int bkptno, int times, _FRAME frame, string exception_name, string exception, string exception_stage, string exception_category, string signal_name);\n\t\n\trecord _DONE_STACK(_FRAME[] stack);\n\t\n\trecord _VARIABLE(string name, string value);\n\trecord _DONE_VARIABLES(_VARIABLE[] variables);\n\t\n\trecord _VAR(string name, string value, string attributes, string exp, int numchild, string type, int thread_id);\n\t\n\trecord _DONE_CHILDREN(int numchild, _VAR[] children, int has_more);\n\t\n\t#endregion\n}\n\n//CONSIDER: later report these small bugs and suggestions, maybe consolidated into one github issue.\n//1. When stops because of exception, step commands don't work in some cases. Eg when fails to load a [DllImport] function.\n//\tFix: in `AsyncStepper::SetupStep` replace `if (pFrame == nullptr) return E_FAIL;` with `if (pFrame == nullptr) return S_FALSE;`.\n//2. -exec-run fails if I compiled.\n//3. Debugger crashes when evaluating expression like `c[0]` with evalFlags EVAL_NOFUNCEVAL.\n//\tCommand string: $\"-var-create - \\\"c[0]\\\" --frame 0 --evalFlags 128\"\n//\tHere c is a variable of a class with an indexer, eg List.\n//4. The used Roslyn dlls are very old. They don't support newer C# features.\n//5. The Roslyn scripting dlls are not used. Instead use <PackageReference Include=\"Microsoft.CodeAnalysis.CSharp\" Version=\"4.8.0\" />.\n//6. Both protocols (MI and VSCode) should support everything supported by the debugger. Examples:\n/*\nNow MI protocol supports setting options at any time (-gdb-set), but VSCode protocol only in the launch request (bot not in the attach request). Also it seems MI supports an additional option 'hot reload' (I didn't test).\nNow MI protocol does not support --thread in -exec-interrupt, although VSCode supports it.\nNow MI protocol does not support ExceptionBreakpoint.negativeCondition (prefix '!' in VSCode protocol).\n*/\n"
  },
  {
    "path": "Au.Editor/Default/Commands.xml",
    "content": "<!--\nCommand XML attributes:\n\tcolor - text color. Value: .NET color name or #RRGGBB or #RGB.\n\ttext - menu item text and toolbar button text. Insert _ before Alt-underlined character.\n\timage - icon name (see menu Tools > Icons) or image file (xaml, png). See ImageUtil.LoadWpfImageElement.\n\tkeys - keyboard or/and mouse shortcut(s), like \"Ctrl+E, Shift+M-click\". See keys.more.parseHotkeyString.\n\nThese attributes are used only for toolbar buttons:\n\tseparator - add separator before. Value: none.\n\thide - overflow mode. Value: always, never, asNeeded (default).\n\timageAt - show image + text; image at this side. Value: left, top, right, bottom. For submenu-items always left.\n\tbtext - button text. Use if it is different than menu item text.\n\nAll attributes are optional. The Example command contains all.\n<Example color=\"green\" text=\"Ab\" image=\"*Material.Forum #A6C639\" keys=\"Ctrl+H\" separator=\"\" hide=\"never\" imageAt=\"left\" btext=\"Bc\"/>\n-->\n<commands>\n\t<!-- Customized commands that maybe don't have a toolbar button. -->\n\t<menu>\n\t\t\n\t</menu>\n\t<!-- Toolbars. Commands added here will have a toolbar button.\n\tAttributes can be specified here or/and in <menu>.\n\tToolbars missing in the user file are automatically added from the default file.\n\tUnknown toolbars and commands in the user file are ignored.\n\t-->\n\t<File>\n\t\t<New />\n\t\t<Properties />\n\t\t<MultiSelect_files />\n\t\t<Collapse_all_folders hide=\"always\" />\n\t\t<Save_now hide=\"always\" />\n\t\t<Git hide=\"always\" />\n\t</File>\n\t<Run>\n\t\t<Compile />\n\t\t<Run_script />\n\t\t<End_task />\n\t\t<Debug_run />\n\t\t<Recent separator=\"\" />\n\t</Run>\n\t<Edit>\n\t\t<Undo />\n\t\t<Redo />\n\t\t<Cut hide=\"always\" separator=\"\" />\n\t\t<Copy hide=\"always\" />\n\t\t<Paste hide=\"always\" />\n\t\t<Format_document separator=\"\" />\n\t\t<Toggle_comment />\n\t\t<Surround />\n\t\t<Create_delegate />\n\t\t<Deduplicate_wnd_find />\n\t\t<Find separator=\"\" />\n\t\t<Find_references />\n\t\t<Find_symbol />\n\t\t<Go_back separator=\"\" />\n\t\t<Go_forward />\n\t\t<Next_bookmark hide=\"always\" />\n\t\t<Wrap_lines separator=\"\" />\n\t\t<Images_in_code />\n\t\t<WPF_preview />\n\t\t<Autocorrect_raw_enter />\n\t</Edit>\n\t<Tools>\n\t\t<Input_recorder />\n\t\t<wnd />\n\t\t<elm />\n\t\t<uiimage />\n\t\t<Keys />\n\t\t<New_trigger separator=\"\" />\n\t\t<Options separator=\"\" />\n\t\t<Icons />\n\t</Tools>\n\t<Custom1>\n\n\t</Custom1>\n\t<Custom2>\n\n\t</Custom2>\n</commands>"
  },
  {
    "path": "Au.Editor/Default/Layout.xml",
    "content": "<!--\nThe layout is a tree with 3 main types of nodes: stack, tab and leaf. Leaf has 3 subtypes: panel, toolbar and document.\nAll nodes can be docked (default), floating or hidden. Stacks are hidden only implicitly, when there are no more docked nodes.\nTags:\nstack - stack. Contains 2 or more nodes (stack, tab, leaf) and implicit splitters between them.\ntab - container for 1 or more leaf nodes displayed one at a time, with buttons to select the active node.\npanel - container for a control that often has child controls. Has header with text.\ntoolbar - container for a toolbar. Like panel, but header is small without text, and behavior is slightly different.\ndocument - placeholder for real document nodes that are added/removed later. Like panel, but without header, and behavior is slightly different.\nAttributes:\nname - name of the panel or toolbar. Displayed in the panel header.\no - stack orientation: v vertical, h horizontal.\nz - node size (width in horizontal stack or height in vertical). Star-sized if like \"100*\". Default for toolbar and stack: auto-sized. For others must be specified, cannot be auto.\ns - splitter size. Default 4. Min 1. Every node in stack except the first has a splitter before it.\nheaderAt - Left, Top, Right, Bottom. Default Left.\nstate - flags: 1 hidden, 2 floating.\nflags - misc flags:\n\t1 - splitter resizes the nearest node at each side instead of all star-sized nodes.\nactive - 0-based index of active node in tab. Default 0.\nAttributes valid in:\nname - panel, toolbar, document.\no - stack.\nz, s - any. Not used if in tab.\nheaderAt - any except stack. Not used if in tab.\nstate, flags - any.\nactive - tab.\n-->\n<stack o=\"h\">\n\t<stack o=\"v\" z=\"800*\">\n\t\t<stack o=\"h\">\n\t\t\t<toolbar name=\"Menu\" />\n\t\t\t<toolbar name=\"Tools\" />\n\t\t\t<toolbar name=\"Custom1\" z=\"100*\" />\n\t\t</stack>\n\t\t<stack o=\"h\" z=\"600*\" s=\"1\">\n\t\t\t<stack o=\"v\" z=\"200\">\n\t\t\t\t<toolbar name=\"File\" />\n\t\t\t\t<tab z=\"320*\" s=\"1\" headerAt=\"Top\">\n\t\t\t\t\t<panel name=\"Files\" headerAt=\"Top\" />\n\t\t\t\t\t<panel name=\"Outline\" headerAt=\"Top\" />\n\t\t\t\t</tab>\n\t\t\t\t<panel name=\"Open\" z=\"80\" />\n\t\t\t\t<tab z=\"140\" headerAt=\"Top\">\n\t\t\t\t\t<panel name=\"Find\" headerAt=\"Top\" />\n\t\t\t\t\t<panel name=\"Bookmarks\" headerAt=\"Top\" />\n\t\t\t\t\t<panel name=\"Breakpoints\" headerAt=\"Top\" />\n\t\t\t\t</tab>\n\t\t\t</stack>\n\t\t\t<stack o=\"v\" z=\"600*\">\n\t\t\t\t<stack o=\"h\">\n\t\t\t\t\t<toolbar name=\"Run\" />\n\t\t\t\t\t<toolbar name=\"Edit\" />\n\t\t\t\t\t<toolbar name=\"Custom2\" z=\"100*\" />\n\t\t\t\t</stack>\n\t\t\t\t<stack o=\"h\" z=\"500*\">\n\t\t\t\t\t<document name=\"documents\" z=\"300*\" headerAt=\"Right\" />\n\t\t\t\t\t<panel name=\"Read\" z=\"300*\" state=\"1\" />\n\t\t\t\t</stack>\n\t\t\t\t<tab z=\"140\">\n\t\t\t\t\t<panel name=\"Output\" />\n\t\t\t\t\t<panel name=\"Found\" />\n\t\t\t\t\t<panel name=\"Mouse\" />\n\t\t\t\t</tab>\n\t\t\t</stack>\n\t\t</stack>\n\t</stack>\n\t<stack o=\"v\" z=\"220\">\n\t\t<tab z=\"1000*\" headerAt=\"Top\">\n\t\t\t<panel name=\"Help\" headerAt=\"Top\" />\n\t\t\t<panel name=\"Debug\" headerAt=\"Top\" />\n\t\t</tab>\n\t\t<panel name=\"Tasks\" z=\"80\" headerAt=\"Right\" />\n\t</stack>\n</stack>"
  },
  {
    "path": "Au.Editor/Default/Snippets.xml",
    "content": "<snippets>\n\n\t<!--context=\"Function\"-->\n\n\t<snippet name=\"ifElseSnippet\" info=\"if { code } else { code }\">\n\t\t<![CDATA[if (${1:true}) {\n\t${SELECTED_TEXT}$0\n} else {\n\t\n}]]>\n\t</snippet>\n\t<snippet name=\"ifSurround\" info=\"if { code }\">\n\t\t<![CDATA[if (${1:true}) {\n\t${SELECTED_TEXT}$0\n}]]>\n\t</snippet>\n\t<snippet name=\"forLoopSnippet\" info=\"Repeat { code } count times.\" more=\"See also: Cookbook article &quot;for, foreach&quot;.\">\n\t\t<![CDATA[for (int ${1:i} = 0; $1 < ${2:count}; $1++) {\n\t${SELECTED_TEXT}$0\n}]]>\n\t</snippet>\n\t<snippet name=\"forReverseSnippet\" info=\"Repeat { code } count times.\" more=\"See also: Cookbook article &quot;for, foreach&quot;.\">\n\t\t<![CDATA[for (int ${1:i} = ${2:count}; --$1 >= 0; ) {\n\t${SELECTED_TEXT}$0\n}]]>\n\t</snippet>\n\t<snippet name=\"switchSnippet\" info=\"Go to a case that matches a variable, or default if none.\">\n\t\t<list item=\"switch statement\">\n\t\t\t<![CDATA[switch (${1:variable}) {\ncase ${2:C}:\n\t$0\n\tbreak;\ncase ${3:C}:\n\t\n\tbreak;\ncase ${4:C}:\n\t\n\tbreak;\ncase ${5:C}:\n\t\n\tbreak;\ncase ${6:C}:\n\t\n\tbreak;\ncase ${7:C}:\n\t\n\tbreak;\ncase ${8:C}:\n\t\n\tbreak;\ncase ${9:C}:\n\t\n\tbreak;\ncase ${10:C}:\n\t\n\tbreak;\ndefault:\n\t\n\tbreak;\n}]]>\n\t\t</list>\n\t\t<list item=\"switch &amp;expression, 1 line\">\n\t\t\t<![CDATA[var ${1:result} = ${2:variable} switch { ${3:C} => ${4:R}, ${5:C} => ${6:R}, ${7:C} => ${8:R}, _ => ${10:default} };]]>\n\t\t</list>\n\t\t<list item=\"switch expression, &amp;multiline\">\n\t\t\t<![CDATA[var ${1:result} = ${2:variable} switch {\n\t${3:C} => ${4:R},\n\t${5:C} => ${6:R},\n\t${7:C} => ${8:R},\n\t${9:C} => ${10:R},\n\t${11:C} => ${12:R},\n\t${13:C} => ${14:R},\n\t${15:C} => ${16:R},\n\t${17:C} => ${18:R},\n\t${19:C} => ${20:R},\n\t_ => ${21:default}\n};]]>\n\t\t</list>\n\t</snippet>\n\t<snippet name=\"trySnippet\" info=\"Exception handling.\" more=\"See also: Cookbook article &quot;Errors, exceptions&quot;.\">\n\t\t<list item=\"catch\">\n\t\t\t<![CDATA[try {\n\t${SELECTED_TEXT}\n}\ncatch(${1:Exception} ex) { $0 }]]>\n\t\t</list>\n\t\t<list item=\"&amp;finally\">\n\t\t\t<![CDATA[try {\n\t${SELECTED_TEXT}\n}\nfinally { $0 }]]>\n\t\t</list>\n\t\t<list item=\"c&amp;atch finally\">\n\t\t\t<![CDATA[try {\n\t${SELECTED_TEXT}\n}\ncatch(${1:Exception} ex) { $0 }\nfinally {  }]]>\n\t\t</list>\n\t</snippet>\n\t<snippet name=\"threadSnippet\" info=\"Run action in other thread.\">\n\t\t<list item=\"Start new thread\">\n\t\t\t<![CDATA[run.thread(() => { $0 });]]>\n\t\t</list>\n\t\t<list item=\"Use thread pool\">\n\t\t\t<![CDATA[Task.Run(() => { $0 });]]>\n\t\t</list>\n\t</snippet>\n\t<snippet name=\"dsDialogShowSnippet\" info=\"Standard dialog window.\" more=\"See also: wpfDialogSnippet.\">\n\t\t<list item=\"OK button\">\n\t\t\t<![CDATA[dialog.show(\"${1:header}\", $\"${2:text}\"$3);]]>\n\t\t</list>\n\t\t<list item=\"OK button and &amp;info icon\">\n\t\t\t<![CDATA[dialog.showInfo(\"${1:header}\", $\"${2:text}\"$3);]]>\n\t\t</list>\n\t\t<list item=\"OK button and &amp;warning icon\">\n\t\t\t<![CDATA[dialog.showWarning(\"${1:header}\", $\"${2:text}\"$3);]]>\n\t\t</list>\n\t\t<list item=\"OK button and &amp;error icon\">\n\t\t\t<![CDATA[dialog.showError(\"${1:header}\", $\"${2:text}\"$3);]]>\n\t\t</list>\n\t\t<list item=\"OK and &amp;Cancel buttons\">\n\t\t\t<![CDATA[if (!dialog.showOkCancel(\"${1:header}\", $\"${2:text}\"$3)) return;]]>\n\t\t</list>\n\t\t<list item=\"&amp;Yes and No buttons\">\n\t\t\t<![CDATA[if (!dialog.showYesNo(\"${1:header}\", $\"${2:text}\"$3)) return;]]>\n\t\t</list>\n\t\t<list item=\"Custom &amp;buttons\">\n\t\t\t<![CDATA[int button = dialog.show(\"${1:header}\", $\"${2:text}\", \"${3:1 OK|2 Cancel|3 Yes|4 No|5 Retry|6 Close|10 Button1|11 Button2}\", flags: DFlags.CommandLinks$4);\nswitch (button) {\ncase 1: $0 break;\ncase 3:  break;\ncase 4:  break;\ncase 5:  break;\ncase 6:  break;\ncase 10:  break;\ncase 11:  break;\ndefault: return;\n}]]>\n\t\t</list>\n\t\t<list item=\"&amp;List of buttons\">\n\t\t\t<![CDATA[int button = dialog.showList(\"${1:one|two|three}\", \"${2:header}\", $\"${3:text}\"$4);}]]>\n\t\t</list>\n\t\t<list item=\"&amp;Text input\">\n\t\t\t<![CDATA[if (!dialog.showInput(out string ${1:s}, \"${2:header}\", $\"${3:text}\"$4)) return;]]>\n\t\t</list>\n\t\t<list item=\"&amp;Number input\">\n\t\t\t<![CDATA[if (!dialog.showInputNumber(out int ${1:i}, \"${2:header}\", $\"${3:text}\"$4)) return;]]>\n\t\t</list>\n\t</snippet>\n\t<snippet name=\"winFindSnippet\" info=\"Find window.\">\n\t\t<![CDATA[var w = wnd.find(0, \"${1:*Name}\");]]>\n\t</snippet>\n\t<snippet name=\"osdSnippet\" info=\"Show OSD text (on-screen display) or tooltip.\">\n\t\t<list item=\"Transparent text\">\n\t\t\t<![CDATA[osdText.showTransparentText($0);]]>\n\t\t</list>\n\t\t<list item=\"Tooltip\">\n\t\t\t<![CDATA[osdText.showText($0);]]>\n\t\t</list>\n\t\t<list item=\"Transparent text, until current function ends\">\n\t\t\t<![CDATA[using var osd = osdText.showTransparentText($0, -1);]]>\n\t\t</list>\n\t\t<list item=\"Tooltip, until current function ends\">\n\t\t\t<![CDATA[using var osd = osdText.showText($0, -1);]]>\n\t\t</list>\n\t</snippet>\n\t<snippet name=\"triggerSnippet\" info=\"Add hotkey, autotext, mouse or window trigger.\" more=\"Add triggers in a file that contains triggers of this type. To open, use menu TT. Local triggers can be in any other script.\">\n\t\t<list item=\"Hotkey\">\n\t\t\t<![CDATA[hk[\"${1:Ctrl+E}\"] = o => { $0 };]]>\n\t\t</list>\n\t\t<list item=\"Autotext\">\n\t\t\t<![CDATA[tt[\"${1:text}\"] = o => { $0 };]]>\n\t\t</list>\n\t\t<list item=\"Autotext replace\">\n\t\t\t<![CDATA[tt[\"${1:text}\"] = o => o.Replace(\"${2:replacement}\");]]>\n\t\t</list>\n\t\t<list item=\"Autotext simple replace\">\n\t\t\t<![CDATA[tr[\"${1:text}\"] = \"${2:replacement}\";]]>\n\t\t</list>\n\t\t<list item=\"Autotext menu\">\n\t\t\t<![CDATA[tt[\"${1:text}\"] = o => o.Menu([\n\t\"${2:replacement1}\",\n\tnew(\"Label1\", \"replacement2\"),\n\tnew(\"Label2\", \"replacement3\", \"<b>html</b>\"),\n\t\"<tag>[[|]]</tag>\",\n\t]);]]>\n\t\t</list>\n\t\t<list item=\"Mouse click\">\n\t\t\t<![CDATA[Triggers.Mouse[TMClick.$1] = o => { $0 };]]>\n\t\t</list>\n\t\t<list item=\"Mouse wheel\">\n\t\t\t<![CDATA[Triggers.Mouse[TMWheel.$1] = o => { $0 };]]>\n\t\t</list>\n\t\t<list item=\"Mouse edge\">\n\t\t\t<![CDATA[Triggers.Mouse[TMEdge.$1] = o => { $0 };]]>\n\t\t</list>\n\t\t<list item=\"Mouse move\">\n\t\t\t<![CDATA[Triggers.Mouse[TMMove.$1] = o => { $0 };]]>\n\t\t</list>\n\t\t<list item=\"Window\">\n\t\t\t<![CDATA[Triggers.Window[TWEvent.ActiveNew, \"$1\"] = o => { $0 };]]>\n\t\t</list>\n\t\t<list item=\"Local triggers\" using=\"Au.Triggers\">\n\t\t\t<![CDATA[//Script-local triggers running all the time, as main script code.\nActionTriggers Triggers = new();\n\n//trigger examples\nvar hk = Triggers.Hotkey;\nhk[\"Ctrl+F1\"] = o => { print.it(o); };\nhk[\"Win+F1\"] = o => { Triggers.Stop(); };\n\nTriggers.Run(); //waits\n]]>\n\t\t</list>\n\t\t<list item=\"Local triggers thread\" using=\"Au.Triggers\">\n\t\t\t<![CDATA[//Script-local triggers running simultaneously with main script code until it ends.\nrun.thread(() => {\n\tActionTriggers Triggers = new();\n\t\n\t//trigger examples\n\tvar hk = Triggers.Hotkey;\n\thk[\"Ctrl+F1\"] = o => { print.it(o); };\n\t\n\t//Triggers.Mouse[TMEdge.Right] = o => { print.it(o); };\n\t//Triggers.Window[TWEvent.ActiveNew, \"* Notepad\"] = o => { print.it(o); };\n\t\n\tTriggers.Run();\n});\ndialog.show(\"Testing triggers thread\"); //replace this line with real script code\n]]>\n\t\t</list>\n\t</snippet>\n\t<snippet name=\"stringBuilderSnippet\" info=\"Create string with StringBuilder.\">\n\t\t<![CDATA[StringBuilder ${1:b} = new();\n$1.Append($0);\n$1.AppendLine();\n$1.AppendFormat(\"{0}, {1}\", 10, 20);\nvar s = $1.ToString();\nprint.it(s);]]>\n\t</snippet>\n\t<snippet name=\"ifActiveWindowSnippet\" info=\"If the active window is...\">\n\t\t<![CDATA[var ${1:w} = wnd.active;\nif ($1.IsMatch(\"${2:*Name}\")) {\n\t$0\n}]]>\n\t</snippet>\n\t<snippet name=\"ifKeySnippet\" info=\"If key is down (pressed).\">\n\t\t<list item=\"Ctrl\">\n\t\t\t<![CDATA[if (keys.isCtrl) { $0 }]]>\n\t\t</list>\n\t\t<list item=\"Shift\">\n\t\t\t<![CDATA[if (keys.isShift) { $0 }]]>\n\t\t</list>\n\t\t<list item=\"Alt\">\n\t\t\t<![CDATA[if (keys.isAlt) { $0 }]]>\n\t\t</list>\n\t\t<list item=\"Win\">\n\t\t\t<![CDATA[if (keys.isWin) { $0 }]]>\n\t\t</list>\n\t\t<list item=\"Ctrl, Shift, Alt or Win\">\n\t\t\t<![CDATA[if (keys.isMod()) { $0 }]]>\n\t\t</list>\n\t\t<list item=\"Specified key\">\n\t\t\t<![CDATA[if (keys.isPressed(KKey.$1)) { $0 }]]>\n\t\t</list>\n\t\t<list item=\"Caps Lock toggled\">\n\t\t\t<![CDATA[if (keys.isCapsLock) { $0 }]]>\n\t\t</list>\n\t\t<list item=\"Num Lock toggled\">\n\t\t\t<![CDATA[if (keys.isNumLock) { $0 }]]>\n\t\t</list>\n\t\t<list item=\"Scroll Lock toggled\">\n\t\t\t<![CDATA[if (keys.isScrollLock) { $0 }]]>\n\t\t</list>\n\t</snippet>\n\t<snippet name=\"speedOptSnippet\" info=\"Change sleep times used by mouse, keyboard and clipboard functions.\">\n\t\t<list item=\"All speed options\">\n\t\t\t<![CDATA[//all speed opt, with default values. Delete lines you don't need. Edit values.\nopt.key.KeySpeed = 1;\nopt.key.TextSpeed = 0;\nopt.key.SleepFinally = 10;\nopt.key.PasteSleep = 100;\nopt.key.KeySpeedClipboard = 5;\nopt.mouse.ClickSpeed = 20;\nopt.mouse.ClickSleepFinally = 10;\nopt.mouse.MoveSpeed = 0;\nopt.mouse.MoveSleepFinally = 10;\n]]>\n\t\t</list>\n\t\t<list item=\"For windows\">\n\t\t\t<![CDATA[//different options depending on the active window\nopt.key.Hook = k => {\n\tvar w = k.w.Window;\n\t//print.it(w);\n\tstring name = w.Name, cn = w.ClassName;\n\tif (name.Like(\"*Word Online - Google Chrome\")) { //example\n\t\tk.optk.TextHow = OKeyText.Paste;\n\t\tk.optk.PasteSleep = 400;\n\t}\n\t//else if (...) { ... }\n\t//else if (...) { ... }\n};\n]]>\n\t\t</list>\n\t\t<list item=\"For triggers\">\n\t\t\t<![CDATA[//options for trigger actions of triggers added afterwards\nTriggers.Options.BeforeAction = o => {\n\t//set opt options here\n};\n]]>\n\t\t</list>\n\t</snippet>\n\t<snippet name=\"perfSnippet\" info=\"Measure code speed.\">\n\t\t<list item=\"&amp;Static\">\n\t\t\t<![CDATA[perf.first();$0\nperf.next();\nperf.nw();]]>\n\t\t</list>\n\t\t<list item=\"&amp;Local, auto-NW\">\n\t\t\t<![CDATA[using var p1 = perf.local();$0\np1.Next();]]>\n\t\t</list>\n\t</snippet>\n\t<snippet name=\"menuSnippet\" info=\"Popup menu.\">\n\t\t<list item=\"Menu for automation scripts\">\n\t\t\t<![CDATA[var ${1:m} = new popupMenu(\"${GUID}\");\n\n$1[\"${2:Text}\"] = o => { $0 };\n$1[\"\"] = o => {  };\n$1.Submenu(\"\", $1 => {\n\t$1[\"\"] = o => {  };\n\t$1[\"\"] = o => {  };\n});\n$1.Separator();\n$1[\"Run program|Tooltip\"] = o => run.it(folders.System + @\"notepad.exe\");\n$1[\"Run script\"] = o => script.run(\"Script123456789.cs\");\n$1[\"Copy-paste\"] = o => {\n\tstring s = clipboard.copy();\n\ts = s.Upper();\n\tclipboard.paste(s);\n};\n\n$1.Show();]]>\n\t\t</list>\n\t\t<list item=\"Context menu for your program\">\n\t\t\t<![CDATA[var ${1:m} = new popupMenu();\n\n$1[\"${2:Text}\"] = o => { $0 };\n$1[\"\"] = o => {  };\n$1.Submenu(\"\", $1 => {\n\t$1[\"\"] = o => {  };\n\t$1[\"\"] = o => {  };\n});\n$1.Separator();\n$1[\"\"] = o => {  };\n$1[\"\"] = o => {  };\n\n$1.Show();]]>\n\t\t</list>\n\t\t<list item=\"Simple menu, returns item id\">\n\t\t\t<![CDATA[int ${1:id} = popupMenu.showSimple(\"${2:1 One|2 Two|3 Three||0 Cancel}\");\nswitch ($1) {\ncase 1: $0 break;\ncase 2:  break;\ncase 3:  break;\ndefault: return;\n}]]>\n\t\t</list>\n\t</snippet>\n\t<snippet name=\"menuItemSnippet\" info=\"Menu item.\" more=\"Other ways: drag-drop scripts, files, links; hotkey Ctrl+Shift+Q. More info in Cookbook.\" var=\"Au.popupMenu,m\">\n\t\t<![CDATA[${VAR}[\"${1:Text}\"] = o => { $0 };]]>\n\t</snippet>\n\t<snippet name=\"menuSubmenuSnippet\" info=\"Menu submenu.\" var=\"Au.popupMenu,m\">\n\t\t<![CDATA[${VAR}.Submenu(\"${1:Text}\", ${VAR} => {\n\t${VAR}[\"${2:Text}\"] = o => { $0 };\n\t${VAR}[\"\"] = o => {  };\n});]]>\n\t</snippet>\n\t<snippet name=\"tbToolbarButtonSnippet\" info=\"Toolbar button.\" more=\"Other ways: drag-drop scripts, files, links; hotkey Ctrl+Shift+Q. More info in Cookbook.\" var=\"Au.toolbar,t\">\n\t\t<![CDATA[${VAR}[\"${1:Text}\"] = o => { $0 };]]>\n\t</snippet>\n\t<snippet name=\"tbToolbarMenuSnippet\" info=\"Toolbar drop-down menu.\" var=\"Au.toolbar,t\">\n\t\t<![CDATA[${VAR}.Menu(\"${1:Text}\", ${VAR} => {\n\t${VAR}[\"${2:Text}\"] = o => { $0 };\n\t${VAR}[\"\"] = o => {  };\n});]]>\n\t</snippet>\n\t<snippet name=\"wpfDialogSnippet\" info=\"Create dialog window.\" more=\"See also: dialogSnippet, menu File &gt; New &gt; Dialogs.\" using=\"System.Windows;System.Windows.Controls;System.Windows.Controls.Primitives;System.Windows.Input;System.Windows.Media\">\n\t\t<![CDATA[var b = new wpfBuilder(\"${1:Window}\").WinSize(400);\nb.R.Add(\"Text\", out TextBox text1).Focus();\nb.R.Add(\"Combo\", out ComboBox combo1).Items(\"Zero|One|Two\");\nb.R.Add(out CheckBox c1, \"Check\");\nb.R.AddButton(\"Button\", _ => { print.it(\"Button clicked\"); });\nb.R.AddOkCancel();\nb.End();\n#if WPF_PREVIEW //menu Edit > View > WPF preview\nb.Window.Preview();\n#endif\nif (!b.ShowDialog()) return;\n//print.it(text1.Text, combo1.SelectedIndex, c1.IsChecked == true);]]>\n\t</snippet>\n\t<snippet name=\"wpfWndprocSnippet\" info=\"Set WPF window hook to receives messages.\" using=\"System.Windows;System.Windows.Interop\">\n\t\t<![CDATA[${1:window}.SourceInitialized += (o, _) => {\n\tvar hs = PresentationSource.FromVisual(o as Window) as HwndSource;\n\ths.AddHook(_WndProc);\n\tnint _WndProc(nint hwnd, int msg, nint wParam, nint lParam, ref bool handled) {\n\t\tvar w = (wnd)hwnd;\n\t\t//WndUtil.PrintMsg(w, msg, wParam, lParam);\n\t\t\n\t\tswitch (msg) {\n\t\t\n\t\t}\n\t\t\n\t\treturn 0;\n\t}\n};]]>\n\t</snippet>\n\t<snippet name=\"ssScriptSetupSnippet\" info=\"Set run-time options such as tray icon and emergency exit ways.\">\n\t\t<![CDATA[//.\nscript.setup(trayIcon: true, sleepExit: true);\n//..\n]]>\n\t</snippet>\n\t<snippet name=\"piPrintItSnippet\" info=\"Display text and variables in the output window.\">\n\t\t<![CDATA[print.it($0);]]>\n\t</snippet>\n\t<snippet name=\"outPrintItSnippet\" info=\"Display text and variables in the output window.\">\n\t\t<![CDATA[print.it($0);]]>\n\t</snippet>\n\t<snippet name=\"pcPrintClearSnippet\" info=\"Clear the output window.\">\n\t\t<![CDATA[print.clear();]]>\n\t</snippet>\n\t<snippet name=\"kkKeysSendSnippet\" info=\"Generate virtual keystrokes (keys, text).\">\n\t\t<![CDATA[keys.send(\"$0\");]]>\n\t</snippet>\n\t<snippet name=\"ktKeysSendtSnippet\" info=\"Send text to the active window using virtual keystrokes.\">\n\t\t<![CDATA[keys.sendt($0);]]>\n\t</snippet>\n\t<snippet name=\"pasteSnippet\" info=\"Paste text using the clipboard and Ctrl+V.\">\n\t\t<![CDATA[clipboard.paste($0);]]>\n\t</snippet>\n\t<snippet name=\"copySnippet\" info=\"Get the selected text using the clipboard and Ctrl+C.\" context=\"Function\">\n\t\t<list item=\"&amp;Copy\">\n\t\t\t<![CDATA[string ${1:s} = clipboard.copy();$0]]>\n\t\t</list>\n\t\t<list item=\"&amp;Try copy\">\n\t\t\t<![CDATA[if (!clipboard.tryCopy(out var ${1:s}, 500)) return;$0]]>\n\t\t</list>\n\t\t<list item=\"C&amp;ut\">\n\t\t\t<![CDATA[string ${1:s} = clipboard.copy(cut: true);$0]]>\n\t\t</list>\n\t</snippet>\n\t<snippet name=\"riRunItSnippet\" info=\"Run a program or open a document, folder, web page.\" more=\"Tip: drag and drop.\">\n\t\t<![CDATA[run.it(${1:folders.System + @\"notepad.exe\"});]]>\n\t</snippet>\n\t<snippet name=\"srScriptRunSnippet\" info=\"Start to execute a script.\" more=\"Tip: drag and drop.\">\n\t\t<![CDATA[script.run(@\"${1:\\Folder\\Script}.cs\");]]>\n\t</snippet>\n\t<snippet name=\"failedSnippet\" info=\"Throw 'failed' exception.\" more=\"Exception text is optional.\">\n\t\t<![CDATA[throw new ${2:AuException}($1);]]>\n\t</snippet>\n\t<snippet name=\"httpPostJsonSnippet\" info=\"Sends an HTTP POST request with JSON data and parses the JSON response.\" using=\"System.Text.Json.Nodes\">\n\t\t<![CDATA[var post = new { aString = \"A\", aNumber = 10, aBool = true, anArray = (object[])[new { x = \"X0\", y = \"Y0\" }, new { x = \"X1\", y = \"Y1\" }] }; //example input; will be sent as JSON; or you can use a raw JSON string instead\n//print.it(System.Text.Json.JsonSerializer.Serialize(post)); //debug-print the JSON that will be sent\nif (!internet.http.TryPost(out var r, \"$0\", internet.jsonContent(post), headers: [\"Authorization: Bearer \" + Environment.GetEnvironmentVariable(\"API_KEY\")], printError: true)) return;\n//var r = internet.http.Post(\"$0\", internet.jsonContent(post), headers: [\"Authorization: Bearer \" + Environment.GetEnvironmentVariable(\"API_KEY\")]);\n//if (!r.IsSuccessStatusCode) { print.it(r.StatusCode, r.ReasonPhrase, r.Text(ignoreError: true)); return; } //on error the response likely contains an error message\n//print.it(r.Text()); //debug-print the response JSON\nJsonNode j = r.Json();\n//var s1 = (string)j[\"a\"][\"b\"]; print.it(s1);\n//foreach (var v in j[\"c\"].AsArray()) { print.it(v[\"d\"]); }\n]]>\n\t</snippet>\n\n\t<!--context=\"Type\"-->\n\n\t<snippet name=\"propSnippet\" info=\"Property.\">\n\t\t<list item=\"{ get; set; }\">\n\t\t\t<![CDATA[public ${1:string} ${2:Property} { get; set; }]]>\n\t\t</list>\n\t\t<list item=\"{ get; &amp;private set; }\">\n\t\t\t<![CDATA[public ${1:string} ${2:Property} { get; private set; }]]>\n\t\t</list>\n\t\t<list item=\"{ g&amp;et {  } set {  } }\">\n\t\t\t<![CDATA[public ${1:string} ${2:Property} {\n\tget {\n\t\t$0\n\t}\n\tset {\n\t\t\n\t}\n}]]>\n\t\t</list>\n\t</snippet>\n\n\t<!--context=\"Namespace|Type\"-->\n\n\t<snippet name=\"nativeApiSnippet\" info=\"Adds class for Windows API declarations.\">\n\t\t<![CDATA[/// <summary>\n/// Add an empty class like this in scripts (at the end) and projects (in any file) where you want to use Windows API.\n/// Then, whenever you need an API function etc, type the class name and dot (api.). The completion list contains API names and adds declarations to this class.\n/// </summary>\n#pragma warning disable 649, 169 //field never assigned/used\nunsafe class ${1:api} : NativeApi {}\n#pragma warning restore 649, 169 //field never assigned/used]]>\n\t</snippet>\n\t<snippet name=\"settingsSnippet\" info=\"Adds a class for settings used by this script/program/library/etc.\">\n\t\t<![CDATA[/// <summary>\n/// Settings of this script/program/library/etc.\n/// Function <see cref=\"MySettings.Load\"/> loads settings from file or creates default; returns a new variable that lets you get or set settings, like <c>var v = sett.i; sett.i = 2;</c>.\n/// Settings are lazily auto-saved soon after changing.\n/// </summary>\ninternal record class MySettings : JSettings {\n\tpublic static readonly string File = folders.ThisAppDocuments + @\"${1:RenameMe}.json\";\n\n\tpublic static MySettings Load() => Load<MySettings>(File);\n\t\n\t//examples of settings. Most types are supported. Use public fields.\n\tpublic int i;\n\tpublic string s = \"default\";\n\tpublic string[] a = [];\n}\n\n//add this at class level:\n//internal static readonly MySettings sett = MySettings.Load();\n\n//or in script as local variable:\n//using var sett = MySettings.Load();]]>\n\t</snippet>\n\n\t<!--context=\"Namespace\"-->\n\n\t<!--rejected: programMainSurround.-->\n\n\t<!--context=\"Attributes\"-->\n\n\t<snippet name=\"marshalAsSnippet\" info=\"Adds MarshalAs attribute.\" more=\"Then press dot and select from list.\" context=\"Attributes\">\n\t\t<![CDATA[MarshalAs(UnmanagedType$0)]]>\n\t</snippet>\n\n\t<!--context=\"Any|Line\"-->\n\n\t<snippet name=\"#ifSnippet\" info=\"Enables or disables code depending on a preprocessor expression.\">\n\t\t<![CDATA[#if ${1:true}\n${SELECTED_TEXT}$0\n#else\n#endif]]>\n\t</snippet>\n\t<snippet name=\"#regionSnippet\" info=\"Fold (hide) code lines.\">\n\t\t<![CDATA[#region ${1}\n\t\t\t\t\n${SELECTED_TEXT}$0\n\n#endregion]]>\n\t</snippet>\n\n\t<!--context=\"Any\"-->\n\t\n\t<snippet name=\"//.Surround\" info=\"Fold (hide) code lines.\" more=\"In LibreAutomate it is the same as #region. Can be //. or //. text.\">\n\t\t<![CDATA[//.\n${SELECTED_TEXT}$0\n//..]]>\n\t</snippet>\n</snippets>"
  },
  {
    "path": "Au.Editor/Default/Themes/Material dark.csv",
    "content": "Font, Consolas, 9.75\nBackground, 0x263238\nNone, 0xEEFFFF\nComment, 0x7E99A5\nString, 0xC3E88D\nStringEscape, 0xFFD68F\nNumber, 0xF78C6C\nPunctuation, 0xEEFFFF\nOperator, 0x89DDFF\nKeyword, 0x89DDFF\nNamespace, 0xFFCB6B\nType, 0xFFCB6B\nFunction, 0x82AAFF, 1\nEvent, 0x82AAFF, 1\nLocalVariable, 0xEEFFFF\nField, 0xEEFFFF\nConstant, 0xEEFFFF\nLabel, 0xFFCB6B\nPreprocessor, 0x89DDFF\nExcluded, 0x9B9B9B\nXmlDocText, 0x7E99A5\nXmlDocTag, 0x546E7A\nRxText, 0xC3E88D\nRxMeta, 0x05C3BA\nRxChars, 0x2EABFE\nRxOption, 0x05C3BA\nRxEscape, 0xFFD68F\nRxCallout, 0xF979AE\nRxComment, 0x57A64A\nLineNumber, 0x808080\nLineNumberMargin, 0x1F445C\nMarkerMargin, 0x263238\nIndicRefs, 0x408000, 40\nIndicBraces, 0x80C000, 255\nIndicDebug, 0xC7CC75, 128\nIndicFound, 0x67370B, 255\nIndicSnippetField, 0xE0A000, 60\nIndicSnippetFieldActive, 0x33ADFF, 60\nSelColor, 0x466568, 160\nSelNofocusColor, 0x466568, 96\nCaret, 0xEEFFFF, 2\nCaretLine, 0x404040, 1\n"
  },
  {
    "path": "Au.Editor/Default/Themes/One Monokai dark.csv",
    "content": "Font, Consolas, 9.75\nBackground, 0x282C34\nNone, 0xBBBBBB\nComment, 0x939AA6\nString, 0xE5C07B\nStringEscape, 0x56B6C2\nNumber, 0xC678DD\nPunctuation, 0xBBBBBB\nOperator, 0xE06C75\nKeyword, 0xE06C75\nNamespace, 0x61AFEF\nType, 0x61AFEF\nFunction, 0x98C379, 1\nEvent, 0x98C379, 1\nLocalVariable, 0xBBBBBB\nField, 0xBBBBBB\nConstant, 0xBBBBBB\nLabel, 0xBBBBBB\nPreprocessor, 0xE06C75\nExcluded, 0xABB2BF\nXmlDocText, 0x939AA6\nXmlDocTag, 0x676F7D\nRxText, 0xE5C07B\nRxMeta, 0x05C3BA\nRxChars, 0x2EABFE\nRxOption, 0x05C3BA\nRxEscape, 0xFFD68F\nRxCallout, 0xF979AE\nRxComment, 0x57A64A\nLineNumber, 0x808080\nLineNumberMargin, 0x233858\nMarkerMargin, 0x263238\nIndicRefs, 0x408000, 40\nIndicBraces, 0x80C000, 255\nIndicDebug, 0xC7CC75, 128\nIndicFound, 0x67370B, 255\nIndicSnippetField, 0xE0A000, 60\nIndicSnippetFieldActive, 0x33ADFF, 60\nSelColor, 0x5A6376, 160\nSelNofocusColor, 0x5A6376, 96\nCaret, 0xF8F8F0, 2\nCaretLine, 0x404040, 1\n"
  },
  {
    "path": "Au.Editor/Default/Themes/Visual Studio dark.csv",
    "content": "Font, Consolas, 9.75\nBackground, 0x1E1E1E\nNone, 0xDCDCDC\nComment, 0x57A64A\nString, 0xD69D85\nStringEscape, 0xFFD68F\nNumber, 0xB5CEA8\nPunctuation, 0xDCDCDC\nOperator, 0xB4B4B4\nKeyword, 0x569CD6\nNamespace, 0xDCDCDC\nType, 0x4EC9B0\nFunction, 0xDCDCAA, 1\nEvent, 0xDCDCAA, 1\nLocalVariable, 0x9CDCFE\nField, 0x9CDCFE\nConstant, 0xDCDCDC\nLabel, 0xDCDCDC\nPreprocessor, 0x9B9B9B\nExcluded, 0x9B9B9B\nXmlDocText, 0x608B4E\nXmlDocTag, 0x808080\nRxText, 0xD69D85\nRxMeta, 0x05C3BA\nRxChars, 0x2EABFE\nRxOption, 0x05C3BA\nRxEscape, 0xFFD68F\nRxCallout, 0xF979AE\nRxComment, 0x57A64A\nLineNumber, 0xA0A0A0\nLineNumberMargin, 0x20384A\nMarkerMargin, 0x1E1E1E\nIndicRefs, 0x408000, 40\nIndicBraces, 0x80C000, 255\nIndicDebug, 0xC5C870, 128\nIndicFound, 0x653306, 255\nIndicSnippetField, 0xE0A000, 60\nIndicSnippetFieldActive, 0x33ADFF, 60\nSelColor, 0x2F70B0, 160\nSelNofocusColor, 0x2F70B0, 96\nCaret, 0xDCDCDC, 2\nCaretLine, 0x404040, 1\n"
  },
  {
    "path": "Au.Editor/Edit/Ci-types.cs",
    "content": "using Au.Controls;\n\nusing Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.Text;\nusing Microsoft.CodeAnalysis.Completion;\n\nnamespace LA;\n\nclass CiComplItem : ITreeViewItem {\n\tCompletionItem _ci;\n\tpublic readonly CiItemKind kind;\n\tpublic readonly CiItemAccess access;\n\treadonly CiComplProvider _provider;\n\tpublic CiComplItemHiddenBy hidden;\n\tpublic CiComplItemMoveDownBy moveDown;\n\tpublic ulong hilite; //bits for max 64 characters\n\tpublic int group;\n\tpublic int commentOffset;\n\tstring _dtext;\n\tobject _symbols; //ISymbol or List<ISymbol> or IReadonlyList<ISymbol> or null\n\t\n\tpublic CompletionItem GetCI([CallerMemberName] string m_ = null) {\n\t\tif (_ci == null) { //initially null if winapi provider\n\t\t\tSetCI(CompletionItem.Create(_dtext));\n\t\t\tDebug_.PrintIf(m_ != \"SelectBestMatch\", \"CompletionItem auto-created for a winapi provider item\");\n\t\t}\n\t\treturn _ci;\n\t}\n\t\n\tpublic void SetCI(CompletionItem c) {\n\t\t_ci = c;\n\t\t_ci.Attach = this;\n\t}\n\t\n\tpublic CiComplItem(CiComplProvider provider, CompletionItem ci) {\n\t\t_provider = provider;\n\t\t_symbols = ci.Symbols;\n\t\tSetCI(ci);\n\t\tCiUtil.TagsToKindAndAccess(ci.Tags, out kind, out access);\n\t\t//ci.DebugPrint();\n\t}\n\t\n\tpublic CiComplItem(CiComplProvider provider, string name, CiItemKind kind, CiItemAccess access = default) {\n\t\t_provider = provider;\n\t\tthis.kind = kind;\n\t\tthis.access = access;\n\t\tif (provider == CiComplProvider.Winapi) {\n\t\t\t_dtext = name;\n\t\t} else {\n\t\t\tSetCI(CompletionItem.Create(name));\n\t\t\t//_ci.Span = span; //FUTURE: may need this for our new providers. Current providers (Winapi, Snippets) don't use this.\n\t\t}\n\t}\n\t\n\tpublic IEnumerable<ISymbol> Symbols {\n\t\tget {\n\t\t\tif (_symbols is ISymbol sym) _symbols = new ISymbol[1] { sym };\n\t\t\treturn _symbols as IEnumerable<ISymbol>;\n\t\t}\n\t}\n\t\n\tpublic ISymbol FirstSymbol => _symbols switch { ISymbol sym => sym, IEnumerable<ISymbol> en => en.FirstOrDefault(), _ => null };\n\t\n\t/// <summary>\n\t/// Gets displayed text without prefix, suffix (eg generic) and green comments (group or inline description).\n\t/// In most cases it is simple name, but in some cases can be eg \"Namespace.Name\", \"Name(parameters)\", etc.\n\t/// </summary>\n\tpublic string Text => _ci?.DisplayText ?? _dtext;\n\t\n\tpublic string FilterText => _ci?.FilterText ?? _dtext;\n\t\n\tpublic string DisplayTextPrefix => _ci?.DisplayTextPrefix;\n\t\n\tpublic CiComplProvider Provider => _provider;\n\t\n\t#region ITreeViewItem\n\tstring ITreeViewItem.DisplayText => _dtext;\n\t\n\tobject ITreeViewItem.Image => ImageResource(kind);\n\t\n\t#endregion\n\t\n\tpublic void SetDisplayText(string comment) {\n\t\tif (_ci == null) return;\n\t\tvar desc = _ci.InlineDescription; if (desc.NE()) desc = comment;\n\t\tbool isComment = !desc.NE();\n\t\tif (_dtext != null && !isComment && commentOffset == 0) return;\n\t\t_dtext = _ci.DisplayText + _ci.DisplayTextSuffix + (isComment ? \"    //\" : null) + desc;\n\t\tif (_ci.DisplayTextPrefix is var dt && dt.Length > 0) {\n\t\t\t_dtext = dt + _dtext;\n\t\t\tDebug_.PrintIf(dt != \"(\", $\"{_dtext}, {dt}\"); //seen only of casts, eg \"(\" + \"int\" + \")\"\n\t\t}\n\t\tcommentOffset = isComment ? _dtext.Length - desc.Length - 6 : 0;\n\t}\n\t\n\tpublic static string ImageResource(CiItemKind kind) => kind switch {\n\t\tCiItemKind.Class => \"resources/ci/class.xaml\",\n\t\tCiItemKind.Constant => \"resources/ci/constant.xaml\",\n\t\tCiItemKind.Delegate => \"resources/ci/delegate.xaml\",\n\t\tCiItemKind.Enum => \"resources/ci/enum.xaml\",\n\t\tCiItemKind.EnumMember => \"resources/ci/enummember.xaml\",\n\t\tCiItemKind.Event => \"resources/ci/event.xaml\",\n\t\tCiItemKind.ExtensionMethod => \"resources/ci/extensionmethod.xaml\",\n\t\tCiItemKind.Field => \"resources/ci/field.xaml\",\n\t\tCiItemKind.Interface => \"resources/ci/interface.xaml\",\n\t\tCiItemKind.Keyword => \"resources/ci/keyword.xaml\",\n\t\tCiItemKind.Label => \"resources/ci/label.xaml\",\n\t\tCiItemKind.LocalVariable => \"resources/ci/localvariable.xaml\",\n\t\tCiItemKind.Method => \"resources/ci/method.xaml\",\n\t\tCiItemKind.Namespace => \"resources/ci/namespace.xaml\",\n\t\tCiItemKind.Operator => \"resources/ci/operator.xaml\",\n\t\tCiItemKind.Property => \"resources/ci/property.xaml\",\n\t\tCiItemKind.Snippet => \"resources/ci/snippet.xaml\",\n\t\tCiItemKind.Structure => \"resources/ci/structure.xaml\",\n\t\tCiItemKind.TypeParameter => \"resources/ci/typeparameter.xaml\",\n\t\tCiItemKind.LocalMethod => \"resources/ci/localmethod.xaml\",\n\t\tCiItemKind.Region => \"resources/ci/region.xaml\",\n\t\t_ => null\n\t};\n\t\n\tpublic string AccessImageSource => AccessImageResource(access);\n\t\n\tpublic static string AccessImageResource(CiItemAccess access) => access switch {\n\t\tCiItemAccess.Private => \"resources/ci/overlayprivate.xaml\",\n\t\tCiItemAccess.Protected => \"resources/ci/overlayprotected.xaml\",\n\t\tCiItemAccess.Internal => \"resources/ci/overlayinternal.xaml\",\n\t\t_ => null\n\t};\n\t\n\tpublic string ModifierImageSource => _ModifierImageResource(this);\n\t\n\tstatic string _ModifierImageResource(CiComplItem ci) {\n\t\tvar sym = ci.FirstSymbol;\n\t\tif (sym != null) {\n\t\t\tif (sym.IsStatic && ci.kind is not (CiItemKind.Constant or CiItemKind.EnumMember or CiItemKind.Namespace)) return \"resources/ci/overlaystatic.xaml\";\n\t\t\tif (ci.kind == CiItemKind.Class && sym.IsAbstract) return \"resources/ci/overlayabstract.xaml\";\n\t\t} else {\n\t\t\t//if (ci.Provider == CiComplProvider.Winapi && ci.kind == CiItemKind.Method) return \"resources/ci/overlaystatic.xaml\"; //no\n\t\t}\n\t\treturn null;\n\t}\n}\n\nenum CiComplProvider : byte {\n\tOther,\n\tSymbol,\n\tKeyword,\n\tCref,\n\tXmlDoc,\n\tEmbeddedLanguage, //Regex, DateTime format, maybe more\n\tOverride,\n\t//ExternAlias,\n\t//ObjectAndWithInitializer,\n\t//AttributeNamedParameter,\n\t\n\t//ours\n\tSnippet,\n\tWinapi,\n}\n\nenum CiComplResult {\n\t/// <summary>\n\t/// No completion.\n\t/// </summary>\n\tNone,\n\t\n\t/// <summary>\n\t/// Inserted text displayed in the popup list. Now caret is after it.\n\t/// </summary>\n\tSimple,\n\t\n\t/// <summary>\n\t/// Inserted more text than displayed in the popup list, eg \"(\" or \"{  }\" or override. Now caret probably is somewhere in middle of it. Also if regex.\n\t/// Only if ch == ' ', '\\n' (Enter) or default (Tab).\n\t/// </summary>\n\tComplex,\n}\n\n[Flags]\nenum CiComplItemHiddenBy : byte { FilterText = 1, Kind = 2, Always = 4 }\n\n[Flags]\nenum CiComplItemMoveDownBy : sbyte { Name = 1, Obsolete = 2, FilterText = 4 }\n\n//The order must match CiUtil.ItemKindNames. In this order are displayed group buttons in the completion popup. See also code in CiWinapi.cs: \" WHERE kind<=4\".\nenum CiItemKind : sbyte {\n\t//types\n\tClass, Structure, Interface, Enum, Delegate,\n\t//functions, events\n\tMethod, ExtensionMethod, Property, Operator, Event,\n\t//data\n\tField, LocalVariable, Constant, EnumMember,\n\t//other\n\tNamespace, Keyword, Label, Snippet, TypeParameter,\n\t//not in autocomplete. Not in CiUtil.ItemKindNames.\n\tLocalMethod, Region,\n\tNone\n}\n\n//don't reorder!\nenum CiItemAccess : sbyte { Public, Private, Protected, Internal }\n\nclass CiNamespaceSymbolEqualityComparer : IEqualityComparer<INamespaceSymbol> {\n\tpublic bool Equals(INamespaceSymbol x, INamespaceSymbol y) {\n\t\tfor (; ; ) {\n\t\t\tif (x.MetadataName != y.MetadataName) return false;\n\t\t\tx = x.ContainingNamespace;\n\t\t\ty = y.ContainingNamespace;\n\t\t\tif (x == null) return y == null;\n\t\t\tif (y == null) return false;\n\t\t}\n\t}\n\t\n\tpublic int GetHashCode(INamespaceSymbol obj) {\n\t\tfor (int r = obj.MetadataName.GetHashCode(); ;) {\n\t\t\tobj = obj.ContainingNamespace;\n\t\t\tif (obj == null) return r;\n\t\t\tr ^= obj.MetadataName.GetHashCode();\n\t\t}\n\t}\n}\n\nclass CiNamespaceOrTypeSymbolEqualityComparer : IEqualityComparer<INamespaceOrTypeSymbol> {\n\tpublic bool Equals(INamespaceOrTypeSymbol x, INamespaceOrTypeSymbol y) {\n\t\tfor (; ; ) {\n\t\t\tif (x.MetadataName != y.MetadataName) return false;\n\t\t\tif ((x is INamespaceSymbol) != (y is INamespaceSymbol)) return false;\n\t\t\tx = x.ContainingSymbol as INamespaceOrTypeSymbol;\n\t\t\ty = y.ContainingSymbol as INamespaceOrTypeSymbol;\n\t\t\tif (x == null) return y == null;\n\t\t\tif (y == null) return false;\n\t\t}\n\t}\n\t\n\tpublic int GetHashCode(INamespaceOrTypeSymbol obj) {\n\t\tfor (int r = obj.MetadataName.GetHashCode(); ;) {\n\t\t\tobj = obj.ContainingSymbol as INamespaceOrTypeSymbol;\n\t\t\tif (obj == null) return r;\n\t\t\tr ^= obj.MetadataName.GetHashCode();\n\t\t}\n\t}\n}\n\nstruct CiStringRange {\n\tpublic readonly string code;\n\tpublic readonly int start, end;\n\tpublic readonly bool verbatim;\n\t\n\tpublic CiStringRange(string code, int start, int end, bool verbatim) {\n\t\tthis.code = code; this.start = start; this.end = end; this.verbatim = verbatim;\n\t}\n\t\n\tpublic override string ToString() {\n\t\tvar s = code[start..end];\n\t\tif (!verbatim) s.Unescape(out s);\n\t\treturn s;\n\t}\n\t\n\tpublic int Length => end - start;\n}\n"
  },
  {
    "path": "Au.Editor/Edit/CiAutocorrect.cs",
    "content": "extern alias CAW;\n\nusing System.Windows.Input;\nusing Au.Controls;\n\nusing Microsoft.CodeAnalysis;\nusing CAW::Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp;\nusing Microsoft.CodeAnalysis.CSharp.Syntax;\nusing Microsoft.CodeAnalysis.Text;\nusing Microsoft.CodeAnalysis.Shared.Extensions;\nusing CAW::Microsoft.CodeAnalysis.Shared.Extensions;\nusing Microsoft.CodeAnalysis.CSharp.Extensions;\nusing Microsoft.CodeAnalysis.CSharp.Indentation;\nusing CAW::Microsoft.CodeAnalysis.Indentation;\n\nnamespace LA;\n\nclass CiAutocorrect {\n\t/// <summary>\n\t/// Call when added text with { } etc and want it behave like when the user types { etc.\n\t/// </summary>\n\tpublic void BracketsAdded(SciCode doc, int innerFrom, int innerTo, EBrackets operation) {\n\t\tvar r = doc.ETempRanges_Add(this, innerFrom, innerTo);\n\t\tif (operation == EBrackets.NewExpression) r.OwnerData = \"new\";\n\t\telse r.OwnerData = \"ac\";\n\t}\n\t\n\tpublic enum EBrackets {\n\t\t/// <summary>\n\t\t/// The same as when the user types '(' etc and is auto-added ')' etc. The user can overtype the ')' with the same character or delete '()' with Backspace.\n\t\t/// </summary>\n\t\tRegular,\n\t\t\n\t\t/// <summary>\n\t\t/// Like Regular, but also the user can overtype entire empty '()' with '[' or '{'. Like always, is auto-added ']' or '}' and the final result is '[]' or '{}'.\n\t\t/// </summary>\n\t\tNewExpression,\n\t}\n\t\n\t/// <summary>\n\t/// Called on Enter, Ctrl/Shift+Enter, Ctrl+;, Tab, Backspace and Delete, before passing it to Scintilla. Won't pass if returns true.\n\t/// Enter: adds new line. If need, completes statement (if before `)` etc) and/or adds indent. Always returns true.\n\t/// Ctrl/Shift+Enter: The same as above, but anywhere. The hotkey depends on App.Settings.ci_enterWith; if not the hotkey, can just add new line + indent.\n\t/// Ctrl+;: Like SciBeforeCharAdded(';'), but anywhere; and inserts semicolon now.\n\t/// Tab: calls/returns SciBeforeCharAdded, which skips auto-added ')' etc.\n\t/// Backspace: If inside an empty temp range, selects the '()' etc to erase and returns false.\n\t/// Backspace: If in blank line before `}` line, deletes the blank line and adds new line after the block.\n\t/// Delete, Backspace: If after deleting newline would be tabs after caret, deletes newline with tabs and returns true.\n\t/// </summary>\n\tpublic bool SciBeforeKey(SciCode doc, KKey key, ModifierKeys mod) {\n\t\tif (key is KKey.Enter) {\n\t\t\tswitch (mod) {\n\t\t\tcase 0:\n\t\t\t\t_OnEnter(-1);\n\t\t\t\treturn true;\n\t\t\tcase ModifierKeys.Control:\n\t\t\t\t_OnEnter(0);\n\t\t\t\treturn true;\n\t\t\tcase ModifierKeys.Shift:\n\t\t\t\t_OnEnter(1);\n\t\t\t\treturn true;\n\t\t\tcase ModifierKeys.Control | ModifierKeys.Shift:\n\t\t\t\t_OnEnter(2);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t} else {\n\t\t\tswitch ((key, mod)) {\n\t\t\tcase (KKey.OemSemicolon, ModifierKeys.Control):\n\t\t\t\t_OnSemicolon(anywhere: true);\n\t\t\t\treturn true;\n\t\t\tcase (KKey.Back, 0):\n\t\t\t\treturn _OnBackspaceOrDelete(doc, true) || SciBeforeCharAdded(doc, '\\b');\n\t\t\tcase (KKey.Delete, 0):\n\t\t\t\treturn _OnBackspaceOrDelete(doc, false);\n\t\t\tcase (KKey.Tab, 0):\n\t\t\t\treturn SciBeforeCharAdded(doc, '\\t');\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\t\n\t/// <summary>\n\t/// Called on WM_CHAR, before passing it to Scintilla. Won't pass if returns true. Not called if ch less than ' '.\n\t/// If ch is ')' etc, and at current position is ')' etc previously added on '(' etc, clears the temp range and returns true.\n\t/// If ch is ';' and current position is at '(...|)' and the statement etc must end with ';': adds ';' if missing, sets current position after ';', and returns true.\n\t/// If ch is '\"' after two '\"', may close raw string (add \"\"\"\") and return true.\n\t/// If ch is '.' after a statement that ends with `expression;`, replaces the ';' with `\\r\\n\\t.;` for method chain.\n\t/// Also called by SciBeforeKey on Backspace and Tab. Then ch is '\\b' or '\\t'.\n\t/// If a new statement can start here, may add semicolon.\n\t/// If at the very end, adds \"\\r\\n\" at the end.\n\t/// </summary>\n\tpublic bool SciBeforeCharAdded(SciCode doc, char ch) {\n\t\tvar (pos8, selEnd8) = doc.aaaSelection(false);\n\t\tbool hasSelection = selEnd8 > pos8, isBackspace = ch is '\\b', isOpenBrac = false;\n\t\t\n\t\tif (isBackspace) {\n\t\t\tif (hasSelection) return false;\n\t\t} else {\n\t\t\tif (selEnd8 == doc.aaaLen8 && !CodeInfo._compl.IsVisibleUI) { //if at the end of text, add newline\n\t\t\t\tdoc.aaaInsertText(false, selEnd8, \"\\r\\n\");\n\t\t\t\tif (hasSelection) doc.aaaSelect(false, pos8, selEnd8);\n\t\t\t}\n\t\t\t\n\t\t\tif (hasSelection) {\n\t\t\t\t_AutoSemicolon();\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t\n\t\t\tif (_AutoSemicolon()) return false;\n\t\t\t\n\t\t\tif (ch == ';') return _OnSemicolon(anywhere: false);\n\t\t\tif (ch == '.') return _OnDot();\n\t\t\t\n\t\t\tswitch (ch) {\n\t\t\tcase '\"' or '\\'' or ')' or ']' or '}' or '>' or '\\t': break; //skip auto-added char\n\t\t\tcase '[' or '{' or '(' or '<': isOpenBrac = true; break; //replace auto-added '()' when completing 'new Type' with '[]' or '{}'\n\t\t\tdefault: return false;\n\t\t\t}\n\t\t}\n\t\t\n\t\tvar r = doc.ETempRanges_Enum(pos8, this, endPosition: (ch == '\"' || ch == '\\''), utf8: true).FirstOrDefault();\n\t\tif (r == null) {\n\t\t\tif (ch == '\"') return _RawString();\n\t\t\treturn false;\n\t\t}\n\t\tif (isOpenBrac && (object)r.OwnerData is not (\"ac\" or \"new\")) return false;\n\t\tr.GetCurrentFromTo(out int from, out int to, utf8: true);\n\t\t\n\t\tif (isBackspace || isOpenBrac) {\n\t\t\tif (pos8 != from) return false;\n\t\t} else {\n\t\t\tif (ch != '\\t' && ch != doc.aaaCharAt8(to)) { //info: '\\0' if pos8 invalid\n\t\t\t\tif (ch == '\"') return _RawString();\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (ch == '\\t' && doc.aaaCharAt8(pos8 - 1) is '\\t' or '\\n') return false; //don't exit temp range if pos8 is at the start of line\n\t\t}\n\t\tfor (int i = pos8; i < to; i++) if (!(doc.aaaCharAt8(i) is ' ' or '\\t' or '\\r' or '\\n')) return false; //eg space before '}'\n\t\t\n\t\t//rejected: ignore user-typed '(' or '<' after auto-added '()' or '<>' by autocompletion. Probably more annoying than useful, because then may want to type (cast) or ()=>lambda or (tup, le).\n\t\t//if(isOpenBrac && (ch == '(' || ch == '<') && ch == doc.aaaCharAt(pos8 - 1)) {\n\t\t//\tr.OwnerData = null;\n\t\t//\treturn true;\n\t\t//}\n\t\tif (isOpenBrac && r.OwnerData != (object)\"new\") return false;\n\t\t\n\t\tif (ch is '\"' or '\\'') { //don't skip `\"` if escaped, eg `\"text\\\"\"`. Same for `'`.\n\t\t\tint i = pos8; while (doc.aaaCharAt8(i - 1) is '\\\\') i--;\n\t\t\tif (0 != ((pos8 - i) & 1)) {\n\t\t\t\tbool verbatim = ch is '\"' && doc.aaaCharAt8(i - 2) is var c2 && (c2 is '@' || (c2 is '$' && doc.aaaCharAt8(i - 3) is '@'));\n\t\t\t\tif (!verbatim) return false;\n\t\t\t}\n\t\t}\n\t\t\n\t\tr.Remove();\n\t\t\n\t\tif (isBackspace || isOpenBrac) {\n\t\t\tdoc.Call(Sci.SCI_SETSEL, pos8 - 1, to + 1); //select and pass to Scintilla, let it delete or overtype\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\tdoc.aaaCurrentPos8 = to + 1;\n\t\treturn true;\n\t\t\n\t\tbool _AutoSemicolon() {\n\t\t\tif (!App.Settings.ci_semicolon) return false;\n\t\t\t\n\t\t\t//is ch a statement-start character?\n\t\t\tif (!SyntaxFacts.IsIdentifierStartCharacter(ch)) {\n\t\t\t\tif (!(ch is (>= '0' and <= '9') or '@' or '$' or '*' or '(' or '\"' or '\\'' or '{')) return false;\n\t\t\t}\n\t\t\t\n\t\t\t//is caret at the end of line?\n\t\t\tfor (int i = selEnd8; ; i++) {\n\t\t\t\tchar c1 = doc.aaaCharAt8(i);\n\t\t\t\tif (c1 is '\\r' or '\\n' or '\\0' or '}') break;\n\t\t\t\tif (!(c1 is ' ' or '\\t')) return false;\n\t\t\t}\n\t\t\t//is caret at the start of line or after a statement-end or block-start character?\n\t\t\tfor (int i = pos8; --i >= 0;) {\n\t\t\t\tchar c1 = doc.aaaCharAt8(i);\n\t\t\t\tif (c1 is '\\n' or ';' or '{' or '}' or ':' or ')' or '/' or 'e' or 'o') break;\n\t\t\t\tif (!(c1 is ' ' or '\\t')) return false;\n\t\t\t}\n\t\t\t\n\t\t\t//can a statement start here?\n\t\t\tif (!CodeInfo.GetContextAndDocument(out var cd, doc.aaaPos16(pos8))) return false;\n\t\t\tvar tok = cd.syntaxRoot.FindTokenOnLeftOfPosition(cd.pos);\n\t\t\tvar node = tok.Parent;\n\t\t\tswitch (tok.Kind()) {\n\t\t\tcase 0: break;\n\t\t\tcase SyntaxKind.OpenBraceToken:\n\t\t\t\tif (node is not BlockSyntax) return false;\n\t\t\t\tbreak;\n\t\t\tcase SyntaxKind.CloseBraceToken:\n\t\t\t\tif (!(node is BlockSyntax { Parent: StatementSyntax or GlobalStatementSyntax or ElseClauseSyntax or CatchClauseSyntax or FinallyClauseSyntax or SwitchSectionSyntax } or SwitchStatementSyntax)) return false;\n\t\t\t\tbreak;\n\t\t\tcase SyntaxKind.SemicolonToken:\n\t\t\t\tif (!(node is StatementSyntax or UsingDirectiveSyntax or ExternAliasDirectiveSyntax) || cd.pos < node.Span.End) return false;\n\t\t\t\tif (IsSwitchSectionEndStatement_(node)) return false; //after `break;` etc in `switch`, probably starting to type `case` or `default`\n\t\t\t\tif (node is EmptyStatementSyntax && cd.pos == node.Span.End) {\n\t\t\t\t\tif (hasSelection) cd.sci.aaaReplaceSel(\"\");\n\t\t\t\t\tcd.sci.aaaGoToPos(true, node.SpanStart);\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase SyntaxKind.ColonToken:\n\t\t\t\tif (!(node is SwitchLabelSyntax or LabeledStatementSyntax)) return false;\n\t\t\t\tbreak;\n\t\t\tcase SyntaxKind.CloseParenToken:\n\t\t\t\tif (!(node is IfStatementSyntax or ForStatementSyntax or CommonForEachStatementSyntax or WhileStatementSyntax or FixedStatementSyntax or LockStatementSyntax or UsingStatementSyntax)) return false;\n\t\t\t\tbreak;\n\t\t\tcase SyntaxKind.ElseKeyword or SyntaxKind.DoKeyword:\n\t\t\t\tbreak;\n\t\t\tcase SyntaxKind.CloseBracketToken:\n\t\t\t\tif (!(node is AttributeListSyntax { Target: { } alst } && alst.Identifier.Kind() is SyntaxKind.ModuleKeyword or SyntaxKind.AssemblyKeyword)) return false;\n\t\t\t\tbreak;\n\t\t\tdefault: return false;\n\t\t\t}\n\t\t\tif (ch is '{') return false; //`{` can just replace empty statement `;` (see `case SyntaxKind.SemicolonToken`)\n\t\t\tif (CiUtil.IsPosInNonblankTrivia(cd.syntaxRoot, cd.pos, cd.code)) return false;\n\t\t\t\n\t\t\tdoc.aaaInsertText(false, selEnd8, \";\");\n\t\t\tif (hasSelection) doc.aaaSelect(false, pos8, selEnd8);\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\tbool _RawString() { //close raw string now if need. In SciCharAdded too late, code is invalid and cannot detect correctly.\n\t\t\tif (pos8 > 3 && doc.aaaCharAt8(pos8 - 1) == '\"' && doc.aaaCharAt8(pos8 - 2) == '\"' && doc.aaaCharAt8(pos8 - 3) != '@') {\n\t\t\t\tif (!CodeInfo.GetContextAndDocument(out var cd)) return false;\n\t\t\t\tvar pos16 = cd.pos;\n\t\t\t\tvar token = cd.syntaxRoot.FindToken(pos16 - 1);\n\t\t\t\tvar tkind = token.Kind();\n\t\t\t\tif (tkind is SyntaxKind.StringLiteralToken or SyntaxKind.InterpolatedStringEndToken) {\n\t\t\t\t\t//if pos is at \"\"|, make \"\"\"|\"\"\" and append ; if missing\n\t\t\t\t\tvar span = token.Span;\n\t\t\t\t\tif (pos16 == span.End && (tkind == SyntaxKind.StringLiteralToken ? span.Length == 2 : token.Parent.Span.Length == 3)) {\n\t\t\t\t\t\t//never mind: does not work if $$\"\". Then code is already invalid, and can't detect correctly. VS ignores it too.\n\t\t\t\t\t\tdoc.aaaInsertText(false, pos8, \"\\\"\");\n\t\t\t\t\t\tdoc.aaaAddUndoPoint();\n\t\t\t\t\t\tvar nt = token.GetNextToken(includeZeroWidth: true);\n\t\t\t\t\t\tbool semicolon = nt.IsKind(SyntaxKind.SemicolonToken) && nt.IsMissing;\n\t\t\t\t\t\tdoc.aaaInsertText(false, pos8 + 1, semicolon ? \"\\\"\\\"\\\";\" : \"\\\"\\\"\\\"\");\n\t\t\t\t\t\tdoc.aaaCurrentPos8 = pos8 + 1;\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t} else if (tkind is SyntaxKind.SingleLineRawStringLiteralToken or SyntaxKind.MultiLineRawStringLiteralToken or SyntaxKind.InterpolatedSingleLineRawStringStartToken or SyntaxKind.InterpolatedMultiLineRawStringStartToken) {\n\t\t\t\t\t//if pos it at \"\"\"|\"\"\", make \"\"\"\"|\"\"\"\"\n\t\t\t\t\tint q1 = 0, q2 = 0;\n\t\t\t\t\tfor (int i = pos8; doc.aaaCharAt8(--i) == '\"';) q1++;\n\t\t\t\t\tfor (int i = pos8; doc.aaaCharAt8(i++) == '\"';) q2++;\n\t\t\t\t\tif (q1 == q2 && q1 >= 3) {\n\t\t\t\t\t\tif (tkind is SyntaxKind.SingleLineRawStringLiteralToken or SyntaxKind.MultiLineRawStringLiteralToken && token.SpanStart != pos8 - q1) return false;\n\t\t\t\t\t\tdoc.aaaInsertText(false, pos8, \"\\\"\");\n\t\t\t\t\t\tdoc.aaaAddUndoPoint();\n\t\t\t\t\t\tdoc.aaaInsertText(false, pos8 + 1, \"\\\"\");\n\t\t\t\t\t\tdoc.aaaCurrentPos8 = pos8 + 1;\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t}\n\t\n\t//CONSIDER: insert missing `break;` in `switch` when completed a statement.\n\t\n\t/// <summary>\n\t/// Called on SCN_CHARADDED.\n\t/// If ch is '(' etc, adds ')' etc. For /* adds */.\n\t/// At the start of line corrects indent of '}' (or formats the block) or '{'. Removes that of '#'.\n\t/// Replaces code like 5s with 5.s(); etc.\n\t/// If ch is '/' in XML comments, may add the end tag.\n\t/// If ch is ';' or ':', may auto-format the completed statement or `case`.\n\t/// If ch is '{' or ':', may remove semicolon.\n\t/// </summary>\n\tpublic void SciCharAdded(CodeInfo.CharContext c) {\n\t\tchar ch = c.ch;\n\t\t\n\t\tif (ch is ';' or ':') {\n\t\t\tif (ch is ':') _ReplaceSemicolonAfterColon();\n\t\t\t_AutoFormat(ch);\n\t\t\treturn;\n\t\t\t//never mind: formats even if ';' added in a wrong place, eg `Func(...|...)`. In VS too.\n\t\t}\n\t\t\n\t\tstring replaceText = ch switch {\n\t\t\t'\"' => \"\\\"\",\n\t\t\t'\\'' => \"'\",\n\t\t\t'(' => \")\",\n\t\t\t'[' => \"]\",\n\t\t\t'{' => \"}\",\n\t\t\t'<' => \">\",\n\t\t\t'*' => \"*/\",\n\t\t\t's' or 't' or '}' or '#' or '/' => \"\",\n\t\t\t_ => null\n\t\t};\n\t\tif (replaceText == null) return;\n\t\t\n\t\tif (!CodeInfo.GetContextAndDocument(out var cd)) return;\n\t\tstring code = cd.code;\n\t\tint pos = cd.pos - 1; if (pos < 0) return;\n\t\t\n\t\tDebug.Assert(code[pos] == ch);\n\t\tif (code[pos] != ch) return;\n\t\t\n\t\tvar root = cd.syntaxRoot;\n\t\t//if(!root.ContainsDiagnostics) return; //no. Don't use errors. It can do more bad than good. Tested.\n\t\t\n\t\tif (ch is 's' or 't') {\n\t\t\tif (!SyntaxFacts.IsIdentifierPartCharacter(code.At_(cd.pos))) {\n\t\t\t\tif (ch == 's') { //when typed like `5s` or `500ms`, replace with `5.s();` or `500.ms();`\n\t\t\t\t\tif (pos > 0 && code[pos - 1] == 'm') { pos--; replaceText = \".ms();\"; } else replaceText = \".s();\";\n\t\t\t\t\tif (_IsNumericLiteralStatement(out _)) {\n\t\t\t\t\t\t//never mind: should ignore if not int s/ms or double s. Error if eg long or double ms.\n\t\t\t\t\t\t_ReplaceST(pos, false);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (ch == 't') { //when typed like `5t`, replace with snippet `for (int i = 0; i < 5; i++) {  }`\n\t\t\t\t\tif (_IsNumericLiteralStatement(out int spanStart)) {\n\t\t\t\t\t\treplaceText = $$\"\"\"for (int ${1:i} = 0; $1 < {{code[spanStart..pos]}}; $1++) { $0 }\"\"\";\n\t\t\t\t\t\t_ReplaceST(spanStart, true);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t\t\n\t\t\tbool _IsNumericLiteralStatement(out int spanStart) {\n\t\t\t\tif (pos > 0 && code[pos - 1].IsAsciiDigit()) {\n\t\t\t\t\tvar node = root.FindToken(pos - 1).Parent;\n\t\t\t\t\tif (node.IsKind(SyntaxKind.NumericLiteralExpression) && node.Parent is ExpressionStatementSyntax) {\n\t\t\t\t\t\tvar span = node.Span;\n\t\t\t\t\t\tspanStart = span.Start;\n\t\t\t\t\t\treturn span.Contains(pos - 1);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tspanStart = 0;\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\t\n\t\tint replaceLength = 0, tempRangeFrom = 0, tempRangeTo = 0, newPos = 0, replaceSemicolonAt = 0;\n\t\t\n\t\tif (ch == '#') { //#directive\n\t\t\tif (CiUtil.IsLineStart(code, pos, out int i) && i < pos) {\n\t\t\t\tif (root.FindTrivia(pos).IsDirective) {\n\t\t\t\t\tc.doc.aaaDeleteRange(true, i, pos);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t} else if (ch == '*') { /**/\n\t\t\tvar trivia = root.FindTrivia(pos);\n\t\t\tif (!trivia.IsKind(SyntaxKind.MultiLineCommentTrivia)) return;\n\t\t\tif (trivia.SpanStart != --pos) return;\n\t\t} else if (ch == '/') { //</tag>\n\t\t\tif (!(root.FindToken(pos, findInsideTrivia: true).Parent is XmlElementEndTagSyntax et && et.Name?.IsMissing != false && et.Parent is XmlElementSyntax e1)) return;\n\t\t\treplaceText = e1.StartTag.Name.ToString() + '>';\n\t\t} else {\n\t\t\tvar token = root.FindToken(pos);\n\t\t\tvar node = token.Parent;\n\t\t\t\n\t\t\tif (ch == '}') { //decrease indent\n\t\t\t\tif (!token.IsKind(SyntaxKind.CloseBraceToken) || pos != token.SpanStart) return;\n\t\t\t\tif (App.Settings.ci_formatAuto) {\n\t\t\t\t\tif (node.Parent is var p && p is (StatementSyntax and not BlockSyntax) or (MemberDeclarationSyntax and not GlobalStatementSyntax) or AccessorDeclarationSyntax) node = p;\n\t\t\t\t\tif (node is StatementSyntax or MemberDeclarationSyntax or AccessorDeclarationSyntax) _AutoFormat(node, cd);\n\t\t\t\t} else if (CiUtil.IsLineStart(code, pos, out int i) && i > 1) {\n\t\t\t\t\tif (node is not (BlockSyntax or SwitchStatementSyntax or MemberDeclarationSyntax or AccessorListSyntax or InitializerExpressionSyntax or AnonymousObjectCreationExpressionSyntax or SwitchExpressionSyntax or PropertyPatternClauseSyntax)) return;\n\t\t\t\t\tvar sInd = _GetIndent();\n\t\t\t\t\tif (!code.Eq(i..pos, sInd)) {\n\t\t\t\t\t\tc.doc.aaaReplaceRange(true, i, pos, sInd);\n\t\t\t\t\t\tc.ignoreChar = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\tstring _GetIndent() => new CiIndent(cd, token.SpanStart, false, useDefaultOptions: true).ToString();\n\t\t\t//info: without useDefaultOptions would be `if (true)\\r\\n\\t{  }` if not compact formatting. We need `if (true)\\r\\n{  }`.\n\t\t\t\n\t\t\tif (node is InterpolatedStringTextSyntax) node = node.Parent;\n\t\t\tvar kind = node.Kind();\n\t\t\t\n\t\t\tvar span = node.Span;\n\t\t\tif (span.Start > pos) return; // > if pos is in node's leading trivia, eg comments or #if-disabled block\n\t\t\t\n\t\t\ttempRangeFrom = tempRangeTo = cd.pos;\n\t\t\tif (ch == '\\'') {\n\t\t\t\tif (kind != SyntaxKind.CharacterLiteralExpression || span.Start != pos) return;\n\t\t\t} else if (ch == '\"') {\n\t\t\t\tbool isVerbatim, isInterpolated = false;\n\t\t\t\tswitch (kind) {\n\t\t\t\tcase SyntaxKind.StringLiteralExpression:\n\t\t\t\t\tisVerbatim = code[span.Start] == '@';\n\t\t\t\t\tbreak;\n\t\t\t\tcase SyntaxKind.InterpolatedStringExpression:\n\t\t\t\t\tisInterpolated = true;\n\t\t\t\t\tisVerbatim = code[span.Start] == '@' || code[span.Start + 1] == '@';\n\t\t\t\t\tbreak;\n\t\t\t\tdefault: return;\n\t\t\t\t}\n\t\t\t\tif (span.Start != pos - (isVerbatim ? 1 : 0) - (isInterpolated ? 1 : 0)) return;\n\t\t\t} else {\n\t\t\t\tswitch (kind) {\n\t\t\t\tcase SyntaxKind.CompilationUnit or SyntaxKind.CharacterLiteralExpression or SyntaxKind.StringLiteralExpression or SyntaxKind.Utf8StringLiteralExpression:\n\t\t\t\t\treturn;\n\t\t\t\tcase SyntaxKind.InterpolatedStringExpression:\n\t\t\t\t\t//after next typed { in interpolated string remove } added after first {\n\t\t\t\t\tif (ch == '{' && code.Eq(pos - 1, \"{{}\") && c.doc.ETempRanges_Enum(cd.pos, this, endPosition: true).Any()) {\n\t\t\t\t\t\treplaceLength = 1;\n\t\t\t\t\t\treplaceText = null;\n\t\t\t\t\t\ttempRangeFrom = 0;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\treturn;\n\t\t\t\tdefault:\n\t\t\t\t\tif (kind != SyntaxKind.Interpolation) {\n\t\t\t\t\t\tvar g = code.At_(cd.pos);\n\t\t\t\t\t\tif (g > ' ' && !(g is '/' && code.At_(cd.pos + 1) is '/' or '*')) {\n\t\t\t\t\t\t\tif (!(g is ';' or ',' or '.' or ':' or '?' or '{' or '}' or '(' or ')' or '[' or ']' or '#')) return;\n\t\t\t\t\t\t\tif ((ch, g) is ('(', '(')) return;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\tif (CiUtil.IsPosInNonblankTrivia(node, pos, code)) return;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tif (ch == '{') {\n\t\t\t\t\t\tif (kind == SyntaxKind.Interpolation) { //add } or }} etc if need\n\t\t\t\t\t\t\tint n = 0;\n\t\t\t\t\t\t\tfor (int i = pos; code[i] == '{'; i--) n++;\n\t\t\t\t\t\t\tif (n > 1) tempRangeFrom = 0; //raw string like $$\"\"\"...{{\n\t\t\t\t\t\t\tfor (int i = cd.pos; code.Eq(i, '}'); i++) n--;\n\t\t\t\t\t\t\tif (n <= 0) return;\n\t\t\t\t\t\t\tif (n > 1) replaceText = new('}', n);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\treplaceText = \"  }\";\n\t\t\t\t\t\t\tif (pos > 0 && !char.IsWhiteSpace(code[pos - 1])) {\n\t\t\t\t\t\t\t\treplaceText = \" {  }\";\n\t\t\t\t\t\t\t\ttempRangeFrom++;\n\t\t\t\t\t\t\t\tcd.pos--; replaceLength = 1; //replace the '{' too\n\t\t\t\t\t\t\t} else if (CiUtil.IsLineStart(code, pos, out int i) && i >= 4\n\t\t\t\t\t\t\t\t&& token.IsKind(SyntaxKind.OpenBraceToken) && pos == token.SpanStart) {\n\t\t\t\t\t\t\t\t//if { at the start of line (and maybe after an indent), correct indent\n\t\t\t\t\t\t\t\tvar sInd = _GetIndent();\n\t\t\t\t\t\t\t\tif (!code.Eq(i..pos, sInd)) {\n\t\t\t\t\t\t\t\t\treplaceText = sInd + \"{  }\";\n\t\t\t\t\t\t\t\t\ttempRangeFrom = i + sInd.Length + 1;\n\t\t\t\t\t\t\t\t\tcd.pos = i; replaceLength = pos - i + 1;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tnewPos = tempRangeFrom + 1;\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\treplaceSemicolonAt = _ReplaceSemicolonAfterBrace(token);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (ch == '<') {\n\t\t\t\t\t\tif (!_IsGenericLessThan()) return; //can be operators\n\t\t\t\t\t\t\n\t\t\t\t\t\tbool _IsGenericLessThan() {\n\t\t\t\t\t\t\tif (kind is SyntaxKind.TypeParameterList or SyntaxKind.TypeArgumentList) return true;\n\t\t\t\t\t\t\tif (kind != SyntaxKind.LessThanExpression) return false;\n\t\t\t\t\t\t\tvar tok2 = token.GetPreviousToken(); if (!tok2.IsKind(SyntaxKind.IdentifierToken)) return false;\n\t\t\t\t\t\t\tvar semo = cd.semanticModel;\n\t\t\t\t\t\t\tvar sa = semo.GetSymbolInfo(tok2).GetAllSymbols();\n\t\t\t\t\t\t\tif (!sa.IsEmpty) {\n\t\t\t\t\t\t\t\tforeach (var v in sa) {\n\t\t\t\t\t\t\t\t\tif (v is INamedTypeSymbol { IsGenericType: true } or IMethodSymbol { IsGenericMethod: true }) return true;\n\t\t\t\t\t\t\t\t\t//never mind: if eg IList and IList<T> are available, GetSymbolInfo gets only IList. Then no '>' completion. The same in VS.\n\t\t\t\t\t\t\t\t\t//\tOK if only IList<T> available. Methods OK.\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\t\n\t\tif (newPos > 0) { // `{ | }`\n\t\t\tusing var undo = cd.sci.ENewUndoAction();\n\t\t\tif (replaceSemicolonAt > 0) cd.sci.aaaReplaceRange(true, replaceSemicolonAt, replaceSemicolonAt + 1, \"\");\n\t\t\tc.doc.aaaReplaceRange(true, cd.pos, cd.pos + replaceLength, replaceText);\n\t\t\tc.doc.aaaCurrentPos16 = newPos;\n\t\t\t_AutoFormat(ch);\n\t\t\t//add temprange AFTER autoformatting. Else it may disappear.\n\t\t\tnewPos = c.doc.aaaCurrentPos16;\n\t\t\ttempRangeFrom = newPos - 1;\n\t\t\ttempRangeTo = newPos + 1;\n\t\t} else {\n\t\t\tc.doc.aaaReplaceRange(true, cd.pos, cd.pos + replaceLength, replaceText, moveCurrentPos: ch is ';' or '/');\n\t\t}\n\t\t\n\t\tif (tempRangeFrom > 0) c.doc.ETempRanges_Add(this, tempRangeFrom, tempRangeTo);\n\t\telse c.ignoreChar = true;\n\t\t\n\t\tstatic void _ReplaceSemicolonAfterColon() {\n\t\t\tif (App.Settings.ci_semicolon && CodeInfo.GetContextAndDocument(out var cd)) {\n\t\t\t\tvar tok = cd.syntaxRoot.FindTokenOnRightOfPosition(cd.pos);\n\t\t\t\tif (tok.Parent is EmptyStatementSyntax && tok.GetPreviousToken().Parent is LabeledStatementSyntax or CaseSwitchLabelSyntax) {\n\t\t\t\t\tvar (from, to) = tok.Span;\n\t\t\t\t\tcd.sci.aaaReplaceRange(true, from, to, \"\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tstatic int _ReplaceSemicolonAfterBrace(SyntaxToken braceToken) {\n\t\t\tif (App.Settings.ci_semicolon && braceToken.GetNextToken(includeSkipped: true) is { RawKind: (int)SyntaxKind.SemicolonToken } tok) {\n\t\t\t\tvar node = braceToken.Parent;\n\t\t\t\tif (node is BlockSyntax { Parent: StatementSyntax or ElseClauseSyntax or CatchClauseSyntax or FinallyClauseSyntax } or SwitchStatementSyntax or MemberDeclarationSyntax) {\n\t\t\t\t\treturn tok.SpanStart;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn 0;\n\t\t}\n\t\t\n\t\tvoid _ReplaceST(int from, bool snippet) {\n\t\t\tint to = cd.pos;\n\t\t\tif (App.Settings.ci_semicolon && CiUtil.SkipSpace(code, to) is int semicolonAt && code.Eq(semicolonAt, ';')) to = semicolonAt + 1;\n\t\t\tif (snippet) {\n\t\t\t\tc.doc.aaaReplaceRange(true, from, to, \"\");\n\t\t\t\tCiSnippets.Surround(replaceText, from..from);\n\t\t\t} else {\n\t\t\t\tc.doc.aaaReplaceRange(true, from, to, replaceText, moveCurrentPos: true);\n\t\t\t}\n\t\t\tc.ignoreChar = true;\n\t\t}\n\t}\n\t\n\t//Called by SciBeforeCharAdded on '.'.\n\t//Implements a quick way to continue a vertical method chain: type `.` on the next line after the `;`, and it will replace the `;\\r\\n` with `\\r\\n\\t.;` and show the completion list.\n\tstatic bool _OnDot() {\n\t\tif (!CodeInfo.GetContextWithoutDocument(out var cd)) return false;\n\t\tint i = CiUtil.SkipSpaceAndNewlineBack(cd.code, cd.pos);\n\t\tif (!cd.code.Eq(--i, ';')) return false;\n\t\tif (!cd.GetDocument()) return false;\n\t\tvar token = cd.syntaxRoot.FindToken(i);\n\t\tif (!token.IsKind(SyntaxKind.SemicolonToken) || token.SpanStart != i) return false;\n\t\tvar node = token.GetPreviousToken().Parent;\n\t\tif (node?.FirstAncestorOrSelf<ExpressionSyntax>() is not { } es) return false;\n\t\t\n\t\tusing var undo = cd.sci.ENewUndoAction();\n\t\tif (cd.code.AsSpan(i..cd.pos).Contains('\\n')) {\n\t\t\tCiIndent ind = new(cd, i, forNewLine: true);\n\t\t\tvar s = \"\\r\\n\" + ind.ToString() + \";\";\n\t\t\tcd.sci.aaaReplaceRange(true, i, cd.pos, s);\n\t\t\ti += s.Length - 1;\n\t\t}\n\t\tcd.sci.aaaCurrentPos16 = i;\n\t\tcd.sci.Call(Api.WM_CHAR, (nint)'.');\n\t\treturn true;\n\t}\n\t\n\t//anywhere true when Ctrl+;.\n\tstatic bool _OnSemicolon(bool anywhere) {\n\t\tif (!CodeInfo.GetDocumentAndFindToken(out var cd, out var token)) return false;\n\t\tvar node = token.Parent;\n\t\t\n\t\tif (!anywhere) {\n\t\t\tvar tk1 = token.Kind();\n\t\t\t\n\t\t\tif (tk1 == SyntaxKind.SemicolonToken) return _ReplaceSemicolon();\n\t\t\t\n\t\t\tif (!(tk1 is SyntaxKind.CloseParenToken or SyntaxKind.CloseBracketToken)) return false;\n\t\t\tif (!(node is BaseArgumentListSyntax or BaseParameterListSyntax or ArrayRankSpecifierSyntax\n\t\t\t\tor ParenthesizedExpressionSyntax or TupleExpressionSyntax or CollectionExpressionSyntax\n\t\t\t\tor TypeOfExpressionSyntax or SizeOfExpressionSyntax or DefaultExpressionSyntax or CheckedExpressionSyntax\n\t\t\t\tor DoStatementSyntax)\n\t\t\t\t) return false;\n\t\t\tif (cd.pos != token.SpanStart) return false;\n\t\t}\n\t\t\n\t\tnode = node.GetStatementEtc(cd.pos);\n\t\tif (node == null) return false;\n\t\t\n\t\tvar lastToken = node.GetLastToken(includeZeroWidth: true);\n\t\tif (!_GetLastTokenForSemicolonStatementCompletion(ref lastToken, out bool isComplete)) return false;\n\t\tif (isComplete) {\n\t\t\tcd.sci.aaaGoToPos(true, lastToken.SpanStart + 1);\n\t\t} else {\n\t\t\t_InsertNodeCompletionTextWithAutoformat(cd, node, \";\", lastToken.Span.End);\n\t\t}\n\t\t\n\t\treturn true;\n\t\t\n\t\t//CONSIDER: make similar feature for '{'. On '{': `if (...|)` -> `if (...) { | }`. `Func(...|)` -> `Func(...) { | }`.\n\t\t//\tRarely used. Much work to detect all possible cases where `(...{ })` is valid. Eg lambda/anonymous func, `is`, `switch`, `with`, `new`, `new X`, `new X()`, `new X[]`.\n\t\t\n\t\tbool _ReplaceSemicolon() {\n\t\t\tif (App.Settings.ci_semicolon && token.SpanStart == cd.pos && node.Span.End == cd.pos + 1) {\n\t\t\t\tcd.sci.aaaCurrentPos16 = cd.pos + 1;\n\t\t\t\tif (App.Settings.ci_formatAuto) _AutoFormat(node, cd);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t}\n\t\n\t/// <param name=\"lastToken\">\n\t/// In: last token of the statement etc node, found like `nodeStat.GetLastToken(includeZeroWidth: true)`.\n\t/// Out: if <i>isComplete</i> false - the existing token before missing `;`. Else the existing `;` token (unchanged).</param>\n\t/// <param name=\"isComplete\">`;` exists.</param>\n\t/// <returns>false if can't determine where `;` should be.</returns>\n\tstatic bool _GetLastTokenForSemicolonStatementCompletion(ref SyntaxToken lastToken, out bool isComplete) {\n\t\tisComplete = false;\n\t\tvar node = lastToken.Parent;\n\t\tvar tk2 = lastToken.Kind();\n\t\tbool isSemicolon = tk2 is SyntaxKind.SemicolonToken;\n\t\tif (isSemicolon || (tk2 is SyntaxKind.CloseBraceToken && node is TypeDeclarationSyntax tds && tds.ParameterList != null)) {\n\t\t\t//CiUtil.PrintNode(node, printErrors: true);\n\t\t\tvar diag = isSemicolon && node.ContainsDiagnostics ? node.GetDiagnostics() : null;\n\t\t\tif (isSemicolon && diag?.Any(o => o.Code is 1513 or 1026) == true) return false; //\"} expected\" or \") expected\". Eg in `timer.after(1, _=>{print.it(1));` after `(1)`.\n\t\t\tif (node.ContainsSkippedText) {\n\t\t\t\t//code examples (TLS):\n\t\t\t\t//\tvar k = (6, 9) void L() {}\n\t\t\t\t//\tvar k = (6, 9) void L() {int i;}\n\t\t\t\t//\tint i=5 print.it(1);\n\t\t\t\t//\tvar s=\"aaa bbb\" char c = 'a';\n\t\t\t\t//\tvar a = s.Lines() for (int i = 0; i < count; i++) { }\n\t\t\t\tif (isSemicolon && node is LocalDeclarationStatementSyntax && diag?.FirstOrDefault(static o => o.Code is 1003) is { } d) { //\", expected\"\n\t\t\t\t\tlastToken = node.FindTokenOnLeftOfPosition(d.Location.SourceSpan.Start);\n\t\t\t\t} else {\n\t\t\t\t\tDebug_.Print(\"ContainsSkippedText\");\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t} else if (lastToken.IsMissing) {\n\t\t\t\tlastToken = lastToken.GetPreviousToken();\n\t\t\t} else {\n\t\t\t\tisComplete = true;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\t\n\tstatic void _OnEnter(int mod) {\n\t\tvar doc = Panels.Editor.ActiveDoc;\n\t\tusing var undo = doc.ENewUndoAction();\n\t\tif (mod < 0 && doc.aaaHasSelection) doc.Call(Sci.SCI_DELETEBACK);\n\t\tif (!_OnEnter2(mod)) doc.Call(Sci.SCI_NEWLINE);\n\t}\n\t\n\t//mod: Ctrl 0, Shift 1, Ctrl+Shift 2, none -1, don't correct -2.\n\tstatic bool _OnEnter2(int mod) {\n\t\tif (!CodeInfo.GetContextAndDocument(out var cd)) return false;\n\t\tvar doc = cd.sci;\n\t\tvar code = cd.code;\n\t\tint pos = cd.pos;\n\t\tif (pos < 1) return false;\n\t\t\n\t\tbool anywhere = mod == App.Settings.ci_enterWith, canCorrect = anywhere, canCorrectOnEnterBeforeParenEtc = false, isEOF = false;\n\t\t\n\t\tvar tok1 = cd.syntaxRoot.FindToken(pos);\n\t\tif (tok1.IsKind(SyntaxKind.EndOfFileToken)) {\n\t\t\tif (!anywhere) if (1 == _InNonblankTriviaOrStringOrChar(cd, tok1)) return true;\n\t\t\ttok1 = tok1.GetPreviousToken();\n\t\t\tif (tok1.RawKind == 0) return false;\n\t\t\tisEOF = true;\n\t\t}\n\t\tvar tk1 = tok1.Kind();\n\t\tSyntaxNode nodeFromPos = tok1.Parent;\n\t\t\n\t\tif (!anywhere && mod != -2) {\n\t\t\tif (tk1 is SyntaxKind.CloseParenToken or SyntaxKind.CloseBracketToken && tok1.SpanStart == pos) {\n\t\t\t\tif (!App.Settings.ci_tempRawEnter && App.Settings.ci_enterBeforeParen && !(mod >= 0 || code[pos - 1] is ' ' or ',')) {\n\t\t\t\t\tcanCorrect = canCorrectOnEnterBeforeParenEtc = nodeFromPos\n\t\t\t\t\t\tis BaseArgumentListSyntax or BaseParameterListSyntax or ArrayRankSpecifierSyntax\n\t\t\t\t\t\tor ParenthesizedExpressionSyntax or TupleExpressionSyntax or CollectionExpressionSyntax\n\t\t\t\t\t\tor CatchDeclarationSyntax or CatchFilterClauseSyntax\n\t\t\t\t\t\tor TypeOfExpressionSyntax or SizeOfExpressionSyntax or DefaultExpressionSyntax or CheckedExpressionSyntax\n\t\t\t\t\t\tor (StatementSyntax and not BlockSyntax) || _IsSwitchCast(nodeFromPos);\n\t\t\t\t}\n\t\t\t} else if (tk1 is SyntaxKind.SemicolonToken && tok1.SpanStart == pos) {\n\t\t\t\tif (!App.Settings.ci_tempRawEnter && App.Settings.ci_enterBeforeSemicolon && !(mod >= 0 || code[pos - 1] is ' ' or ',')) {\n\t\t\t\t\tcanCorrect = canCorrectOnEnterBeforeParenEtc = !(nodeFromPos is ForStatementSyntax or EmptyStatementSyntax);\n\t\t\t\t}\n\t\t\t} else if (!isEOF) {\n\t\t\t\tint r = _InNonblankTriviaOrStringOrChar(cd, tok1);\n\t\t\t\tif (r == 1) return true; //yes and corrected\n\t\t\t\tif (r == 2) return false; //yes and not corrected\n\t\t\t\tif (r == 3) canCorrect = true; //string or char. Let's complete statement.\n\t\t\t}\n\t\t}\n\t\t\n\t\tSyntaxNode node = null;\n\t\tforeach (var v in nodeFromPos.AncestorsAndSelf()) {\n\t\t\tif (v is BlockSyntax) {\n\t\t\t\tif (v.Parent is BlockSyntax or GlobalStatementSyntax || v.Span.ContainsInside(pos)) break;\n\t\t\t\tcontinue;\n\t\t\t} else if (v is StatementSyntax) {\n\t\t\t} else if (v is MemberDeclarationSyntax) {\n\t\t\t\tif (v is GlobalStatementSyntax) break;\n\t\t\t} else if (v is AttributeListSyntax) {\n\t\t\t\tif (v.Parent is ParameterSyntax) continue;\n\t\t\t} else if (v is ExpressionSyntax) {\n\t\t\t\tif (!(v.Parent is InitializerExpressionSyntax)) continue;\n\t\t\t} else if (v is AccessorDeclarationSyntax or ElseClauseSyntax or FinallyClauseSyntax or CatchClauseSyntax or UsingDirectiveSyntax or ExternAliasDirectiveSyntax or CollectionElementSyntax) {\n\t\t\t} else continue;\n\t\t\t\n\t\t\tif (v is EnumMemberDeclarationSyntax or ExpressionSyntax or CollectionElementSyntax && pos >= v.Span.End) continue; //if `{ ... member| }`, move caret after `}`\n\t\t\t\n\t\t\tnode = v;\n\t\t\tbreak;\n\t\t}\n\t\tif (node == null || !node.Span.Contains(pos)) canCorrect = false;\n\t\t\n\t\tbool hasSemicolon = false;\n\t\tif (canCorrect) {\n\t\t\tbool needBraces = false, needSemicolon = false, needComma = false;\n\t\t\tSyntaxToken afterToken = default;\n\t\t\t\n\t\t\tswitch (node) {\n\t\t\t//StatementSyntax\n\t\t\tcase CommonForEachStatementSyntax k: _WithBlock(k.Statement, k.CloseParenToken); break;\n\t\t\tcase FixedStatementSyntax k: _WithBlock(k.Statement, k.CloseParenToken); break;\n\t\t\tcase ForStatementSyntax k: _WithBlock(k.Statement, k.CloseParenToken); break;\n\t\t\tcase IfStatementSyntax k: _WithBlock(k.Statement, k.CloseParenToken); break;\n\t\t\tcase LockStatementSyntax k: _WithBlock(k.Statement, k.CloseParenToken); break;\n\t\t\tcase UsingStatementSyntax k: _WithBlock(k.Statement, k.CloseParenToken); break;\n\t\t\tcase WhileStatementSyntax k: _WithBlock(k.Statement, k.CloseParenToken); break;\n\t\t\tcase TryStatementSyntax k: _WithBlock(k.Block, k.TryKeyword); break;\n\t\t\tcase LocalFunctionStatementSyntax k: _Function(k.Body, k.ExpressionBody, k.ParameterList.CloseParenToken, k.SemicolonToken); break;\n\t\t\tcase SwitchStatementSyntax k:\n\t\t\t\tvar obt = k.OpenBraceToken;\n\t\t\t\tif (needBraces = obt.IsMissing) {\n\t\t\t\t\tvar cpt = k.CloseParenToken;\n\t\t\t\t\tif (cpt.IsMissing) { //if 'switch(word) no {}', sometimes Roslyn thinks '(word)...' is a cast expression. See _IsSwitchCast.\n\t\t\t\t\t\tif (k.SwitchKeyword.GetNextToken().Parent is not CastExpressionSyntax ce) return false;\n\t\t\t\t\t\tcpt = ce.CloseParenToken;\n\t\t\t\t\t}\n\t\t\t\t\tafterToken = cpt;\n\t\t\t\t} else {\n\t\t\t\t\tif (pos <= obt.SpanStart) afterToken = obt;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t//MemberDeclarationSyntax\n\t\t\tcase NamespaceDeclarationSyntax k: _WithBraces(k.OpenBraceToken); break;\n\t\t\tcase BaseTypeDeclarationSyntax k: _WithBraces(k.OpenBraceToken); break;\n\t\t\tcase BaseMethodDeclarationSyntax k: _Function(k.Body, k.ExpressionBody, k.ParameterList.CloseParenToken, k.SemicolonToken); break;\n\t\t\tcase PropertyDeclarationSyntax k: _Property(k.AccessorList, k.ExpressionBody, k.Identifier, k.SemicolonToken); break;\n\t\t\tcase IndexerDeclarationSyntax k: _Property(k.AccessorList, k.ExpressionBody, k.ParameterList.CloseBracketToken, k.SemicolonToken); break;\n\t\t\tcase EventDeclarationSyntax k: _Property(k.AccessorList, null, k.Identifier, default); break;\n\t\t\tcase EnumMemberDeclarationSyntax k: _WithComma(k.EqualsValue); break;\n\t\t\t//other\n\t\t\tcase AccessorDeclarationSyntax k: _Function(k.Body, k.ExpressionBody, k.Keyword, k.SemicolonToken); break;\n\t\t\tcase ElseClauseSyntax k: _WithBlock(k.Statement, k.ElseKeyword); break;\n\t\t\tcase FinallyClauseSyntax k: _WithBlock(k.Block, k.FinallyKeyword); break;\n\t\t\tcase CatchClauseSyntax k: _WithBlock(k.Block, k.Filter?.CloseParenToken ?? k.Declaration?.CloseParenToken ?? k.CatchKeyword); break;\n\t\t\tcase ExpressionSyntax k: _WithComma(k); break;\n\t\t\tcase CollectionElementSyntax k: _WithComma(k); break;\n\t\t\tcase AttributeListSyntax: break;\n\t\t\tdefault: _WithSemicolon(node.GetLastToken(includeZeroWidth: true)); break;\n\t\t\t}\n\t\t\t//print.it($\"{{}}={needBraces}  ;={needSemicolon},  ,={needComma}\");\n\t\t\t\n\t\t\t//for nodes that can have a child block statement. Eg `if`, 'catch`.\n\t\t\tvoid _WithBlock(StatementSyntax statement, in SyntaxToken tokenBeforeBlock) {\n\t\t\t\tif (tokenBeforeBlock.IsMissing) return;\n\t\t\t\tif (statement is BlockSyntax bs && !bs.OpenBraceToken.IsMissing) afterToken = bs.OpenBraceToken;\n\t\t\t\telse { needBraces = true; afterToken = tokenBeforeBlock; }\n\t\t\t}\n\t\t\t\n\t\t\t//for nodes that have `{  }` but it isn't a block statement. Eg type declarations.\n\t\t\tvoid _WithBraces(in SyntaxToken openBraceToken) {\n\t\t\t\tif (needBraces = openBraceToken.IsMissing) afterToken = openBraceToken;\n\t\t\t\telse if (pos <= openBraceToken.SpanStart) afterToken = openBraceToken;\n\t\t\t}\n\t\t\t\n\t\t\t//for member and local functions\n\t\t\tvoid _Function(BlockSyntax block, ArrowExpressionClauseSyntax arrow, in SyntaxToken tokenBeforeBlock, in SyntaxToken semicolonToken) {\n\t\t\t\tif (tokenBeforeBlock.IsMissing) return;\n\t\t\t\tif (block != null) afterToken = block.OpenBraceToken;\n\t\t\t\telse if (needBraces = arrow is null) afterToken = tokenBeforeBlock;\n\t\t\t\telse needSemicolon = semicolonToken.IsMissing;\n\t\t\t}\n\t\t\t\n\t\t\t//for properties, indexers and events\n\t\t\tvoid _Property(AccessorListSyntax block, ArrowExpressionClauseSyntax arrow, in SyntaxToken tokenBeforeBlock, in SyntaxToken semicolonToken) {\n\t\t\t\tif (tokenBeforeBlock.IsMissing) return;\n\t\t\t\tif (block != null) afterToken = block.OpenBraceToken;\n\t\t\t\telse if (needBraces = arrow is null) afterToken = tokenBeforeBlock;\n\t\t\t\telse needSemicolon = semicolonToken.IsMissing;\n\t\t\t}\n\t\t\t\n\t\t\t//for statements that should end with semicolon\n\t\t\tvoid _WithSemicolon(in SyntaxToken semicolonToken) {\n\t\t\t\tif (!semicolonToken.IsKind(SyntaxKind.SemicolonToken)) { Debug_.Print(semicolonToken.Kind()); return; }\n\t\t\t\tafterToken = semicolonToken;\n\t\t\t\tif (_GetLastTokenForSemicolonStatementCompletion(ref afterToken, out hasSemicolon)) needSemicolon = !hasSemicolon;\n\t\t\t}\n\t\t\t\n\t\t\t//for enum members and object/collection initializer expression elements\n\t\t\tvoid _WithComma(SyntaxNode lastNode) {\n\t\t\t\tvar lastToken = lastNode.GetLastToken();\n\t\t\t\tvar nextToken = lastToken.GetNextToken();\n\t\t\t\tvar tk = nextToken.Kind();\n\t\t\t\tif (tk is SyntaxKind.CommaToken && nextToken.GetNextToken().IsKind(SyntaxKind.CloseBraceToken)) afterToken = nextToken;\n\t\t\t\telse { needComma = true; afterToken = lastToken; }\n\t\t\t}\n\t\t\t\n\t\t\tcanCorrect = needBraces || needSemicolon || needComma;\n\t\t\tif (afterToken.RawKind == 0) afterToken = node.GetLastToken();\n\t\t\tpos = CiUtil.SkipNewlineBack(code, canCorrect ? afterToken.Span.End : afterToken.FullSpan.End);\n\t\t\t\n\t\t\tif (canCorrect) {\n\t\t\t\tCiIndent ind = new(cd, node.SpanStart, forNewLine: false);\n\t\t\t\tstring s, sInd = ind.ToString();\n\t\t\t\tint replaceSemicolonAt = 0;\n\t\t\t\tif (needBraces) {\n\t\t\t\t\tstring sInd2 = node is SwitchStatementSyntax && App.Settings.ci_formatCompact ? sInd : (ind + 1).ToString();\n\t\t\t\t\ts = App.Settings.ci_formatCompact\n\t\t\t\t\t\t? \" {\" + \"\\r\\n\" + sInd2 + \"\\r\\n\" + sInd + \"}\"\n\t\t\t\t\t\t: \"\\r\\n\" + sInd + \"{\" + \"\\r\\n\" + sInd2 + \"\\r\\n\" + sInd + \"}\";\n\t\t\t\t\t\n\t\t\t\t\tif (App.Settings.ci_semicolon && afterToken.GetNextToken(includeSkipped: true) is { RawKind: (int)SyntaxKind.SemicolonToken } tok)\n\t\t\t\t\t\tif (node is StatementSyntax or ElseClauseSyntax or CatchClauseSyntax or FinallyClauseSyntax or SwitchStatementSyntax or MemberDeclarationSyntax)\n\t\t\t\t\t\t\treplaceSemicolonAt = code.Length - tok.SpanStart;\n\t\t\t\t} else {\n\t\t\t\t\ts = (needSemicolon ? \";\" : \",\") + \"\\r\\n\" + sInd;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t_InsertNodeCompletionTextWithAutoformat(cd, node, s, pos, needBraces ? 3 + sInd.Length : 0);\n\t\t\t\t\n\t\t\t\tif (replaceSemicolonAt > 0) {\n\t\t\t\t\treplaceSemicolonAt = cd.sci.aaaLen16 - replaceSemicolonAt;\n\t\t\t\t\tcd.sci.aaaReplaceRange(true, replaceSemicolonAt, replaceSemicolonAt + 1, \"\");\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (canCorrectOnEnterBeforeParenEtc) _OnUndoAddSimpleNewline();\n\t\t\t\treturn true;\n\t\t\t} else {\n\t\t\t\tdoc.aaaCurrentPos16 = pos;\n\t\t\t}\n\t\t}\n\t\t\n\t\t//auto-indent\n\t\tif (hasSemicolon && App.Settings.ci_formatAuto && App.Settings.ci_semicolon) { //probably auto-added semicolon\n\t\t\tCiIndent ind = new(cd, pos, forNewLine: true);\n\t\t\t_InsertNodeCompletionTextWithAutoformat(cd, node, \"\\r\\n\" + ind, pos);\n\t\t} else {\n\t\t\tvar (from, to) = CiUtil.SkipSpaceAround(code, pos); //remove spaces and tabs around the line break\n\t\t\tif (from == 0) return false;\n\t\t\tbool atStartOfLine = code[from - 1] == '\\n';\n\t\t\tbool atStartOfNonblankLine = atStartOfLine && !(to == code.Length || code[to] is '\\r' or '\\n');\n\t\t\t\n\t\t\tCiIndent ind = new(cd, from, forNewLine: !atStartOfNonblankLine);\n\t\t\tCiIndent indBefore = atStartOfNonblankLine ? new(cd, from - (code.Eq(from - 2, '\\r') ? 2 : 1), forNewLine: true) : default;\n\t\t\tbool noInd = ind == 0 && indBefore == 0;\n\t\t\tstring sInd = ind.ToString(), sBefore = indBefore.ToString(), sAfter = \"\";\n\t\t\t\n\t\t\t// `{ | }` -> `{\\r\\n\\t|\\r\\n}`\n\t\t\tif (!atStartOfNonblankLine && code.Eq(to, '}') && cd.syntaxRoot.FindToken(to) is { RawKind: (int)SyntaxKind.CloseBraceToken } cbt && cbt.SpanStart == to) {\n\t\t\t\tnode = cbt.Parent;\n\t\t\t\t\n\t\t\t\tbool same = App.Settings.ci_formatCompact && node is SwitchStatementSyntax;\n\t\t\t\t\n\t\t\t\tif (noInd && !same) return false;\n\t\t\t\t\n\t\t\t\tstring sInd2 = (same ? ind : ind - 1).ToString();\n\t\t\t\t\n\t\t\t\tif (!cbt.GetPreviousToken().IsKind(SyntaxKind.OpenBraceToken)) { //if `{ tokens | }`, don't add empty line, usually it is unwanted\n\t\t\t\t\tsInd = sInd2;\n\t\t\t\t} else {\n\t\t\t\t\tsAfter = \"\\r\\n\" + sInd2;\n\t\t\t\t\t\n\t\t\t\t\tif (!App.Settings.ci_formatCompact // `owner {` -> `owner\\r\\n{`\n\t\t\t\t\t\t&& code[from - 1] == '{'\n\t\t\t\t\t\t&& !(node is BlockSyntax && node.Parent is BlockSyntax or GlobalStatementSyntax)\n\t\t\t\t\t\t&& node is not InterpolationSyntax\n\t\t\t\t\t\t&& !CiUtil.IsLineStart(code, from - 1, out int i1)\n\t\t\t\t\t\t) {\n\t\t\t\t\t\tfrom = i1;\n\t\t\t\t\t\tsBefore = \"\\r\\n\" + (ind - 1).ToString() + \"{\";\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (noInd) return false;\n\t\t\t\tif (atStartOfLine && !atStartOfNonblankLine) sBefore = sInd;\n\t\t\t}\n\t\t\tvar s = sBefore + \"\\r\\n\" + sInd + sAfter;\n\t\t\t\n\t\t\tdoc.aaaReplaceRange(true, from, to, s);\n\t\t\tdoc.aaaGoToPos(true, from + s.Length - sAfter.Length);\n\t\t}\n\t\t\n\t\tif (canCorrectOnEnterBeforeParenEtc) _OnUndoAddSimpleNewline();\n\t\treturn true;\n\t\t\n\t\tvoid _OnUndoAddSimpleNewline() {\n\t\t\tint pos = cd.pos;\n\t\t\tcd.sci.AaTextChanged += _doc_AaTextChanged;\n\t\t\tvoid _doc_AaTextChanged(KScintilla.AaEventHandlerArgs e) {\n\t\t\t\te.c.AaTextChanged -= _doc_AaTextChanged;\n\t\t\t\tif (e.n.modificationType.Has(Sci.MOD.SC_PERFORMED_UNDO)) {\n\t\t\t\t\tApp.Dispatcher.InvokeAsync(() => {\n\t\t\t\t\t\tif (Panels.Editor.ActiveDoc != cd.sci) return;\n\t\t\t\t\t\tcd.sci.aaaCurrentPos16 = pos;\n\t\t\t\t\t\t_OnEnter(-2);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/// <returns>0 no, 1 yes and corrected, 2 yes and not corrected, 3 string or char.</returns>\n\tstatic int _InNonblankTriviaOrStringOrChar(CodeInfo.Context cd, SyntaxToken token) {\n\t\tstring code = cd.code, suffix = null;\n\t\tbool insertEmptyLine = false;\n\t\tint pos = cd.pos, posStart = pos, posEnd = pos;\n\t\tvar span = token.Span;\n\t\tif (pos < span.Start || pos > span.End) { //trivia\n\t\t\tvar trivia = token.FindTrivia(pos);\n\t\t\tspan = trivia.Span;\n\t\t\tvar kind = trivia.Kind();\n\t\t\tif (pos == span.Start && kind != SyntaxKind.MultiLineDocumentationCommentTrivia) return 0; //info: /** span starts after /**\n\t\t\tswitch (kind) {\n\t\t\tcase SyntaxKind.MultiLineCommentTrivia when span.Length == 4 && pos == span.Start + 2:\n\t\t\t\tinsertEmptyLine = true;\n\t\t\t\tbreak;\n\t\t\tcase SyntaxKind.MultiLineCommentTrivia or SyntaxKind.MultiLineDocumentationCommentTrivia:\n\t\t\t\treturn 2;\n\t\t\tcase SyntaxKind.SingleLineCommentTrivia:\n\t\t\t\tsuffix = \"//\";\n\t\t\t\tbreak;\n\t\t\tcase SyntaxKind.SingleLineDocumentationCommentTrivia:\n\t\t\t\tsuffix = \"/// \";\n\t\t\t\tif (CiUtil.IsLineStart(code, pos, out int sol)) pos = posStart = posEnd = CiUtil.SkipNewlineBack(code, sol);\n\t\t\t\tbreak;\n\t\t\tdefault: return 0;\n\t\t\t}\n\t\t\tif (suffix != null) { //trim spaces\n\t\t\t\twhile (posStart > span.Start && CiUtil.IsSpace(code[posStart - 1])) posStart--;\n\t\t\t\twhile (posEnd < span.End && CiUtil.IsSpace(code[posEnd])) posEnd++;\n\t\t\t\tif (kind is SyntaxKind.SingleLineDocumentationCommentTrivia && code.Eq(posStart - 3, \"/// \") && CiUtil.IsLineStart(code, posStart - 3)) posStart++; //let empty lines be `/// `, not '///`\n\t\t\t}\n\t\t} else {\n\t\t\tif (span.ContainsInside(pos) && token.IsKind(SyntaxKind.CharacterLiteralToken)) return 3;\n\t\t\tbool? isString = token.IsInString(pos, code, out var si, orU8: true);\n\t\t\tif (isString == false) return 0;\n\t\t\tif (si.isRawPrefixCenter) {\n\t\t\t\tcd.sci.aaaInsertText(true, pos, \"\\r\\n\\r\\n\"); //let Enter in raw string prefix like \"\"\"|\"\"\" add extra newline\n\t\t\t\tcd.sci.aaaCurrentPos16 = pos + 2;\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\tif (isString == null && !si.isRawMultilineBetweenStartQuotesAndText) return 2;\n\t\t\tif (si.isRawMultiline || si.isRawMultilineBetweenStartQuotesAndText) {\n\t\t\t\t(posStart, posEnd) = CiUtil.SkipSpaceAround(code, pos);\n\t\t\t\tvar sInd = new CiIndent(cd, posStart, forNewLine: true, rawString: true).ToString();\n\t\t\t\tif (sInd.Length == 0) return 2;\n\t\t\t\tstring sIndBefore = !si.isRawMultilineBetweenStartQuotesAndText && code[posStart - 1] == '\\n' ? sInd : null; //is start of line?\n\t\t\t\tcd.sci.aaaReplaceRange(true, posStart, posEnd, sIndBefore + \"\\r\\n\" + sInd, moveCurrentPos: true);\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\tif (si.isRaw && !si.stringNode.ContainsDiagnostics) {\n\t\t\t\tvar textSpan = si.stringNode is InterpolatedStringExpressionSyntax ises ? ises.Contents.Span : si.textSpan;\n\t\t\t\tif (pos != textSpan.Start) return 3;\n\t\t\t\t//\"\"\"|text\"\"\" -> \"\"\"|\\r\\ntext\\r\\n\"\"\"\n\t\t\t\tcd.sci.aaaInsertText(true, pos + textSpan.Length, \"\\r\\n\");\n\t\t\t\tcd.sci.aaaInsertText(true, pos, \"\\r\\n\");\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\tif (!si.isClassic) return 2;\n\t\t\treturn 3;\n\t\t\t//rejected: split string into \"abc\" + \"\" or \"abc\\r\\n\" + \"\". Rarely used. Better complete statement.\n\t\t}\n\t\t\n\t\tvar doc = cd.sci;\n\t\tint indent = doc.aaaLineIndentFromPos(true, posStart);\n\t\tif (indent < 1 /*&& prefix == null*/ && suffix == null && !insertEmptyLine) return 2;\n\t\t\n\t\tvar b = new StringBuilder();\n\t\tif (insertEmptyLine) {\n\t\t\tb.AppendLine().AppendLine().AppendIndent(indent);\n\t\t\t\n\t\t\tdoc.aaaInsertText(true, pos, b.ToString());\n\t\t\tdoc.aaaCurrentPos16 = pos + 2;\n\t\t} else {\n\t\t\tb.AppendLine();\n\t\t\tif (suffix != null) b.AppendIndent(indent).Append(suffix);\n\t\t\t\n\t\t\tdoc.aaaReplaceRange(true, posStart, posEnd, b.ToString(), moveCurrentPos: true);\n\t\t}\n\t\t\n\t\treturn 1;\n\t}\n\t\n\tstatic bool _OnBackspaceOrDelete(SciCode doc, bool back) {\n\t\t//when joining 2 non-empty lines with Delete or Backspace, remove indent from the second line\n\t\tif (doc.aaaHasSelection) return false;\n\t\tint i = doc.aaaCurrentPos16;\n\t\tvar code = doc.aaaText;\n\t\tRXGroup g;\n\t\tif (back) {\n\t\t\tif (_BlockCompletion()) return true;\n\t\t\tint i0 = i;\n\t\t\tif (code.Eq(i - 1, '\\n')) i--;\n\t\t\tif (code.Eq(i - 1, '\\r')) i--;\n\t\t\tif (i == i0) return false;\n\t\t} else {\n\t\t\t//if at the start of a line containing just tabs/spaces, let Delete delete entire line\n\t\t\tif (code.RxMatch(@\"(?m)^\\h+\\R\", 0, out g, RXFlags.ANCHORED, i..)) goto g1;\n\t\t}\n\t\tif (!code.RxMatch(@\"(?m)(?<!^)\\R\\h+\", 0, out g, RXFlags.ANCHORED, i..)) return false;\n\t\tg1: doc.aaaDeleteRange(true, g.Start, g.End);\n\t\treturn true;\n\t\t\n\t\t//On Backspace in the last blank line before `}` in multiline `{ block }` deleted that line and adds new line after the block.\n\t\t//Supports most `{ }` kinds and `[collection]`.\n\t\tbool _BlockCompletion() {\n\t\t\tvar (from, to) = CiUtil.SkipSpaceAround(code, i);\n\t\t\tint nextLineAt = CiUtil.SkipNewline(code, to);\n\t\t\tif ((nextLineAt > to && code.At_(from - 1) == '\\n')) {\n\t\t\t\tint braceAt = nextLineAt; while (CiUtil.IsSpace(code, braceAt)) braceAt++;\n\t\t\t\tif (code.At_(braceAt) is '}' or ']') {\n\t\t\t\t\tif (CodeInfo.GetDocumentAndFindToken(out var cd, out var tok, to) && tok.Kind() is var tk && tk is SyntaxKind.CloseBraceToken or SyntaxKind.CloseBracketToken) {\n\t\t\t\t\t\tvar node = tok.Parent;\n\t\t\t\t\t\tstring s = null, sInd = null;\n\t\t\t\t\t\tbool needSemicolon = false, format = false;\n\t\t\t\t\t\tint moveCaret = -2;\n\t\t\t\t\t\t\n\t\t\t\t\t\tif (node is ExpressionSyntax || (node is BlockSyntax && node.Parent is ExpressionSyntax)) {\n\t\t\t\t\t\t\t//note: API returns incorrect indent before `]` if no comma before\n\t\t\t\t\t\t\tif (tok.GetNextToken(includeZeroWidth: true) is { RawKind: (int)SyntaxKind.SemicolonToken } tSemicolon) {\n\t\t\t\t\t\t\t\tsInd = new CiIndent(cd, tSemicolon.Parent.GetStatementEtc(cd.pos).Span.End, true).ToString();\n\t\t\t\t\t\t\t\tif (needSemicolon = tSemicolon.IsMissing) to = tok.FullSpan.End; else to = tSemicolon.FullSpan.End;\n\t\t\t\t\t\t\t\tformat = true;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tcd.sci.aaaCurrentPos16 = braceAt + 1;\n\t\t\t\t\t\t\t\tcd.sci.aaaDeleteRange(true, from, nextLineAt);\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else if (node is StatementSyntax or BaseTypeDeclarationSyntax or NamespaceDeclarationSyntax or AccessorListSyntax) {\n\t\t\t\t\t\t\tvar ind = new CiIndent(cd, braceAt, false);\n\t\t\t\t\t\t\tif (ind.Spaces + 4 < cd.sci.aaaLineIndentFromPos(true, from, out int spaces) * 4 + spaces) return false; //maybe the coder just wants to delete extra indent\n\t\t\t\t\t\t\tsInd = ind.ToString();\n\t\t\t\t\t\t\tif (node is BlockSyntax && node.Parent is var p && !(p is BlockSyntax or GlobalStatementSyntax)) {\n\t\t\t\t\t\t\t\tif (p is DoStatementSyntax dss && dss.WhileKeyword.IsMissing) (to, s) = (braceAt + 1, \" while();\");\n\t\t\t\t\t\t\t\telse if (p is CatchClauseSyntax) to = p.Parent.FullSpan.End;\n\t\t\t\t\t\t\t\telse to = p.FullSpan.End;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tto = tok.FullSpan.End;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else return false;\n\t\t\t\t\t\t\n\t\t\t\t\t\tif (s == null) {\n\t\t\t\t\t\t\tif (code[to - 1] == '\\n') s = sInd + \"\\r\\n\"; else { s = \"\\r\\n\" + sInd + \"\\r\\n\" + sInd; moveCaret -= sInd.Length; }\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\tusing var undo = cd.sci.ENewUndoAction();\n\t\t\t\t\t\tcd.sci.aaaInsertText(true, to, s);\n\t\t\t\t\t\tcd.sci.aaaCurrentPos16 = to + s.Length + moveCaret;\n\t\t\t\t\t\tif (needSemicolon) cd.sci.aaaInsertText(true, braceAt + 1, \";\");\n\t\t\t\t\t\tcd.sci.aaaDeleteRange(true, from, nextLineAt);\n\t\t\t\t\t\tif (format) _AutoFormat(default);\n\t\t\t\t\t\t\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t}\n\t\n\tstatic void _AutoFormat(char ch) {\n\t\t//This func is called when text changed in these cases:\n\t\t//\t1. On ';' or ':' (from SciCharAdded). Then *pos* is after the char. Then *ch* is ';' or ':'.\n\t\t//\t2. On '{' when SciCharAdded added `{  }`. Then *pos* is after `{ `. Then *ch* is '{'.\n\t\t//\t3. On Backspace, if completed a `{ block };`. Then *pos* is in new line after ';'. Then *ch* is '\\0'.\n\t\t\n\t\tif (!App.Settings.ci_formatAuto && ch != ':') return;\n\t\t\n\t\tif (!CodeInfo.GetContextAndDocument(out var cd)) return;\n\t\tint pos = cd.pos;\n\t\t\n\t\tvar tok = cd.syntaxRoot.FindTokenOnLeftOfPosition(pos);\n\t\tif (tok.Parent is not { } n) return;\n\t\t\n\t\tif (ch is ';' or ':') if (pos != n.Span.End) return; //skip if in trivia, string, for(;;) etc\n\t\t\n\t\tint maxTo = -1;\n\t\tvar tk = tok.Kind();\n\t\tif (ch == ':') {\n\t\t\tif (n is not SwitchLabelSyntax sls) return;\n\t\t\tif (!App.Settings.ci_formatAuto) maxTo = sls.Keyword.Span.End; //just correct indent\n\t\t} else if (tk is SyntaxKind.SemicolonToken) {\n\t\t\tif (n is StatementSyntax && n.Parent is var p && p is StatementSyntax and not BlockSyntax) n = p; //eg format `if(...) statement;`, not just `statement;`\n\t\t} else if (tk is SyntaxKind.OpenBraceToken) {\n\t\t\t//blocks: BlockSyntax, SwitchStatementSyntax, BaseTypeDeclarationSyntax, NamespaceDeclarationSyntax, AccessorListSyntax\n\t\t\t//expressions etc: InitializerExpressionSyntax, AnonymousObjectCreationExpressionSyntax, SwitchExpressionSyntax, PropertyPatternClauseSyntax\n\t\t\t\n\t\t\tbool skipBlock = ch == '{'; //don't format `{  }`, because would make `{ }`\n\t\t\tif (n is BlockSyntax bs) { //eg `if(...) {  }` or `if(...) {\\r\\n\\t\\r\\n}` or `if(...) {\\r\\n\\t\\r\\n statements }`. Format `if(...) ` or `if(...) {\\r\\n\\t\\r\\n}`.\n\t\t\t\tif (n.Parent is BlockSyntax or GlobalStatementSyntax) return;\n\t\t\t\tif (skipBlock || bs.Statements.Any()) maxTo = n.SpanStart; //don't format `{  }` and `{\\r\\n\\t\\r\\n statements }`\n\t\t\t\tn = n.Parent;\n\t\t\t} else { //`class C {  }`, `switch(...) {  }`, `new C {  }`, etc\n\t\t\t\tif (n is ExpressionSyntax or PropertyPatternClauseSyntax) return;\n\t\t\t\tif (skipBlock || !tok.GetNextToken().IsKind(SyntaxKind.CloseBraceToken)) maxTo = tok.SpanStart; //don't format `{  }` and `{\\r\\n\\t\\r\\n tokens }`\n\t\t\t\tif (n is AccessorListSyntax) n = n.Parent;\n\t\t\t}\n\t\t}\n\t\t\n\t\t_AutoFormat(n, cd, maxTo);\n\t}\n\t\n\tstatic void _AutoFormat(SyntaxNode n, CodeInfo.Context cd, int maxTo = -1) {\n\t\tvar (from, to) = n.GetRealFullSpan(spanEnd: true);\n\t\tif (maxTo > from) to = maxTo;\n\t\t//CiUtil.DebugHiliteRange(from, to); wait.doEvents(); 500.ms(); CiUtil.DebugHiliteRange(0, 0);\n\t\tModifyCode.Format(cd, from, to);\n\t}\n\t\n\t//Used when pasting, dropping or inserting statement(s).\n\t//If inserting text before or after `;` and it is an empty statement, deletes the `;` if it is redundant.\n\tinternal class Pasting {\n\t\tSciCode _doc;\n\t\tint _insertedCount, _deleteSemicolonAt = -1;\n\t\t\n\t\tpublic Pasting(SciCode doc) {\n\t\t\tif (App.Settings.ci_semicolon) {\n\t\t\t\t_doc = doc;\n\t\t\t\t_doc.AaTextChanged += _doc_AaTextChanged;\n\t\t\t}\n\t\t}\n\t\t\n\t\tunsafe void _doc_AaTextChanged(KScintilla.AaEventHandlerArgs e) {\n\t\t\tif (e.n.modificationType.Has(Sci.MOD.SC_MOD_INSERTTEXT) && ++_insertedCount == 1) {\n\t\t\t\tint start8 = e.n.position, end8 = start8 + e.n.length;\n\t\t\t\tvar doc = e.c;\n\t\t\t\tbool semicolonBefore = doc.aaaCharAt8(start8 - 1) is ';', semicolonAfter = !semicolonBefore && doc.aaaCharAt8(end8) is ';';\n\t\t\t\tif (semicolonAfter) semicolonAfter = new RByte(e.n.textUTF8, e.n.length) is [.., (byte)';'] or [.., (byte)';', (byte)'\\r', (byte)'\\n'] or [.., (byte)';', (byte)'\\n'];\n\t\t\t\tif (semicolonBefore || semicolonAfter) _deleteSemicolonAt = semicolonBefore ? start8 - 1 : end8;\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic void After() {\n\t\t\tif (_doc != null) {\n\t\t\t\t_doc.AaTextChanged -= _doc_AaTextChanged;\n\t\t\t\tif (_insertedCount == 1 && _deleteSemicolonAt >= 0) {\n\t\t\t\t\tint pos8 = _deleteSemicolonAt;\n\t\t\t\t\tif (_doc.aaaCharAt8(pos8) != ';') {\n\t\t\t\t\t\tDebug_.Print(\"not ;\");\n\t\t\t\t\t} else {\n\t\t\t\t\t\tint pos = _doc.aaaPos16(pos8);\n\t\t\t\t\t\tif (CodeInfo.GetDocumentAndFindNode(out var cd, out var node, pos) && node is EmptyStatementSyntax && node.SpanStart == cd.pos) {\n\t\t\t\t\t\t\t_doc.aaaReplaceRange(false, pos8, pos8 + 1, \"\");\n\t\t\t\t\t\t\t//rejected: if inserted after `;`, and semicolon missing at the end of text, insert semicolon there. Unreliable etc.\n\t\t\t\t\t\t\t//bad: the user also may want to auto-delete or move the auto-added `;` when it isn't an empty statement. Eg when text dropped/pasted after `int i = ;`. But impossible to know.\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t\n\t#region util\n\t\n\t/// <summary>\n\t/// Inserts text after node, autoformats node if need, and sets final cursor position.\n\t/// Used for statement completion on Enter or ';'.\n\t/// Uses single text modification operation (insert or replace).\n\t/// </summary>\n\t/// <param name=\"cd\"></param>\n\t/// <param name=\"node\">Node to format. Does not format if null.</param>\n\t/// <param name=\"sInsert\">Text to insert. Eg \";\" or \" {  }\". Does not format it.</param>\n\t/// <param name=\"pos\">The end of the span of the last normal token of the node. Formats until it.</param>\n\t/// <param name=\"finalPosMinus\">Move the final caret position from the end of inserted text back by this count chars.</param>\n\t/// <returns>The end of the inserted text in new text.</returns>\n\tstatic int _InsertNodeCompletionTextWithAutoformat(CodeInfo.Context cd, SyntaxNode node, string sInsert, int pos, int finalPosMinus = 0) {\n\t\tif (App.Settings.ci_formatAuto && node != null) {\n\t\t\tint from = node.GetRealFullSpan(spanEnd: true).Start, to = pos, pos0 = pos;\n\t\t\tif (ModifyCode.Format(cd, ref from, ref to, ref pos, ref pos) is { } a) {\n\t\t\t\tusing var undo = cd.sci.ENewUndoAction();\n\t\t\t\tcd.sci.aaaInsertText(true, pos0, sInsert);\n\t\t\t\tfor (int i = a.Count; --i >= 0;) {\n\t\t\t\t\tvar c = a[i];\n\t\t\t\t\tif (c.Span.Start == pos0 && c.Span.IsEmpty) { pos -= c.NewText.Length; continue; } //don't insert space at the end, eg `if (true)  {  }`\n\t\t\t\t\tcd.sci.aaaReplaceRange(true, c.Span.Start, c.Span.End, c.NewText);\n\t\t\t\t}\n\t\t\t\tgoto g1;\n\t\t\t}\n\t\t}\n\t\tcd.sci.aaaInsertText(true, pos, sInsert);\n\t\tg1:\n\t\tint r = pos + sInsert.Length;\n\t\tcd.sci.aaaGoToPos(true, r - finalPosMinus);\n\t\treturn r;\n\t}\n\t\n\t/// <summary>\n\t/// If <c>switch(word)</c> without <c>{}</c> is followed by another statement, Roslyn thinks <c>(word)</c> is a cast expression.\n\t/// This function returns true if <i>node</i> is <b>CastExpressionSyntax</b> followed by <c>switch</c>.\n\t/// </summary>\n\t/// <param name=\"node\">Parent node of <c>)</c> token.</param>\n\t/// <returns></returns>\n\tstatic bool _IsSwitchCast(SyntaxNode node)\n\t\t=> node is CastExpressionSyntax ce && ce.OpenParenToken.GetPreviousToken() is { RawKind: (int)SyntaxKind.SwitchKeyword, Parent: SwitchStatementSyntax };\n\t\n\tinternal static bool IsSwitchSectionEndStatement_(SyntaxNode n)\n\t\t=> n.Parent is SwitchSectionSyntax && n is BreakStatementSyntax or ContinueStatementSyntax or GotoStatementSyntax or ReturnStatementSyntax or YieldStatementSyntax or ThrowStatementSyntax;\n\t\n\t#endregion\n}\n\n/// <summary>\n/// Gets indent for a code line.\n/// </summary>\nstruct CiIndent {\n\tint _spaces;\n\t\n\t/// <summary>\n\t/// Determines indent for line containing <i>pos</i>; if <i>forNewLine</i> - for new line that would be inserted at <i>pos</i>.\n\t/// Can be used to determine the indent of new line on <c>Enter</c>. Not for formatting of existing code.\n\t/// Ignores current indent of the line, but may use that of previous line.\n\t/// Does not have the formatter problems (bad comment alignment etc).\n\t/// </summary>\n\tpublic CiIndent(CodeInfo.Context cd, int pos, bool forNewLine, bool rawString = false, bool useDefaultOptions = false) {\n\t\tvar d = cd.document;\n\t\tvar root = cd.syntaxRoot;\n\t\tstring code = cd.code;\n\t\tSourceText stext;\n\t\t\n\t\tif (forNewLine) {\n\t\t\tcode = code.Insert(pos, \"\\r\\n\\r\\n\");\n\t\t\tstext = SourceText.From(code);\n\t\t\t//try faster way. Faster ~2 times if big file. And the speed is stable; the simple way is randomly much slower.\n\t\t\tbool fast = false;\n\t\t\tif (!rawString) {\n\t\t\t\tif (root.FindTrivia(pos) is { RawKind: not 0 } tri) { //the most common case. Eg when pos is at the end of a line or in `{  }`.\n\t\t\t\t\tif (fast = tri.SpanStart == pos) {\n\t\t\t\t\t\troot = root.InsertTriviaBefore(tri, new SyntaxTriviaList(SyntaxFactory.CarriageReturnLineFeed, SyntaxFactory.CarriageReturnLineFeed));\n\t\t\t\t\t\t//stext = root.GetText(); code = stext.ToString(); //much slower\n\t\t\t\t\t}\n\t\t\t\t} else if (root.FindToken(pos) is { RawKind: not 0 } tok) {\n\t\t\t\t\tif (fast = tok.SpanStart == pos && !tok.HasLeadingTrivia) {\n\t\t\t\t\t\troot = root.ReplaceToken(tok, tok.WithLeadingTrivia(SyntaxFactory.CarriageReturnLineFeed, SyntaxFactory.CarriageReturnLineFeed));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!fast) root = root.SyntaxTree.WithChangedText(stext).GetCompilationUnitRoot();\n\t\t\tpos += 2;\n\t\t} else {\n\t\t\tstext = root.SyntaxTree.GetText();\n\t\t}\n\t\t\n\t\tvar parsedDocument = new ParsedDocument(d.Id, stext, root, d.Project.GetExtendedLanguageServices());\n\t\tvar indenter = parsedDocument.LanguageServices.GetRequiredService<IIndentationService>();\n\t\tint line = parsedDocument.Text.Lines.GetLineFromPosition(pos).LineNumber;\n\t\tvar indOpt = useDefaultOptions ? IndentationOptionsProviders.GetDefault(parsedDocument.LanguageServices) : new IndentationOptions(ModifyCode.FormattingOptions);\n\t\tIndentationResult ind = indenter.GetIndentation(parsedDocument, line, indOpt, default);\n\t\t\n\t\t//IndentationResult -> _ind\n\t\t\n\t\tint to = ind.BasePosition, i = to;\n\t\twhile (i > 0 && code[i - 1] is '\\t' or ' ') i--;\n\t\tint k = 0;\n\t\tfor (; i < to; i++) {\n\t\t\tif (code[i] == ' ') k++;\n\t\t\telse k = k / 4 * 4 + 4;\n\t\t}\n\t\t\n\t\t_spaces = k + ind.Offset;\n\t\t\n\t\tif (forNewLine && !rawString) {\n\t\t\t//bad indent after `break;` etc in `switch{}`\n\t\t\tif (_spaces >= 4 && root.FindTokenOnLeftOfPosition(pos) is { RawKind: (int)SyntaxKind.SemicolonToken } ts && CiAutocorrect.IsSwitchSectionEndStatement_(ts.Parent)) {\n\t\t\t\t_spaces -= 4;\n\t\t\t}\n\t\t}\n\t}\n\t\n\tpublic static CiIndent operator ++(CiIndent i) { i._spaces += 4; return i; }\n\t\n\tpublic static CiIndent operator --(CiIndent i) { i._spaces = Math.Max(0, i._spaces - 4); return i; }\n\t\n\tpublic static CiIndent operator +(CiIndent i, int plus) => new() { _spaces = i._spaces + plus * 4 };\n\t\n\tpublic static CiIndent operator -(CiIndent i, int minus) => new() { _spaces = Math.Max(0, i._spaces - minus * 4) };\n\t\n\tpublic override string ToString() {\n\t\tif (_spaces == 0) return \"\";\n\t\tif (!App.Settings.ci_formatTabIndent) return new(' ', _spaces);\n\t\tvar (tabs, spaces) = Math.DivRem(_spaces, 4);\n\t\treturn spaces == 0 ? new('\\t', tabs)\n\t\t\t: tabs == 0 ? new(' ', spaces)\n\t\t\t: new string('\\t', tabs) + new string(' ', spaces);\n\t}\n\t\n\tpublic int Spaces => _spaces;\n\t\n\tpublic static implicit operator int(CiIndent i) => (i._spaces + 3) / 4;\n}\n"
  },
  {
    "path": "Au.Editor/Edit/CiCompletion.cs",
    "content": "extern alias CAW;\n\nusing System.Collections.Immutable;\nusing Au.Controls;\n\nusing Microsoft.CodeAnalysis;\nusing CAW::Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp;\nusing Microsoft.CodeAnalysis.CSharp.Syntax;\nusing Microsoft.CodeAnalysis.Text;\nusing Microsoft.CodeAnalysis.CSharp.Extensions;\nusing Microsoft.CodeAnalysis.Shared.Extensions;\nusing CAW::Microsoft.CodeAnalysis.Shared.Extensions;\nusing Microsoft.CodeAnalysis.Completion;\nusing Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;\n\n//PROBLEM: Roslyn bug: no popup list if first parameter of indexer setter is enum. Same in VS.\n//\tEven on Ctrl+Space does not select the enum in list. And does not add enum members like \"Enum.Member\".\n\n//CONSIDER: completion in keys string. But probably really useful only for keys like MediaNextTrack.\n\n//CONSIDER: if string starts with <>, on < show list of output tags. User suggestion. And useful for me. Maybe even show a list of standard colors and the color control.\n//\tAlso need a tool for wildcard expression.\n//\tNow users can in \"string\" press F1 or Ctrl+Space to open the help page.\n\nnamespace LA;\n\npartial class CiCompletion {\n\tCiPopupList _popupList;\n\t_Data _data; //not null while the popup list window is visible\n\tCancellationTokenSource _cancelTS;\n\t\n\tclass _Data {\n\t\tpublic CompletionService completionService;\n\t\tpublic Document document;\n\t\tpublic SemanticModel model;\n\t\tpublic List<CiComplItem> items;\n\t\tpublic int codeLength;\n\t\tpublic string filterText;\n\t\tpublic SciCode.ITempRange tempRange;\n\t\tpublic bool forced, noAutoSelect, isDot, unimported;\n\t\tpublic CiWinapi winapi;\n\t}\n\t\n\tpublic bool IsVisibleUI => _data != null;\n\t\n\tpublic void Cancel() {\n\t\t_cancelTS?.Cancel();\n\t\t_cancelTS = null;\n\t\t_CancelUI();\n\t}\n\t\n\tvoid _CancelUI(bool popupListHidden = false, bool tempRangeRemoved = false) {\n\t\t//print.it(\"_CancelUI\", _data != null);\n\t\tif (_data == null) return;\n\t\tif (!tempRangeRemoved) _data.tempRange.Remove();\n\t\t_data = null;\n\t\tif (!popupListHidden) _popupList.Hide();\n\t}\n\t\n\t/// <summary>\n\t/// Called before <see cref=\"CiAutocorrect.SciCharAdded\"/> and before passing the character to Scintilla.\n\t/// If showing popup list, synchronously commits the selected item if need (inserts text).\n\t/// Else inserts text at caret position and now caret is after the text.\n\t/// </summary>\n\tpublic CiComplResult SciCharAdding_Commit(SciCode doc, char ch) {\n\t\tCiComplResult R = CiComplResult.None;\n\t\tif (_data != null) {\n\t\t\tif (!SyntaxFacts.IsIdentifierPartCharacter(ch)) {\n\t\t\t\tvar ci = _popupList.SelectedItem;\n\t\t\t\tif (ci != null && _data.filterText.Length == 0 && ch != '.') ci = null;\n\t\t\t\tif (ci != null) R = _Commit(doc, ci, ch, default);\n\t\t\t\t_CancelUI();\n\t\t\t\t//return;\n\t\t\t}\n\t\t}\n\t\treturn R;\n\t}\n\t\n\t/// <summary>\n\t/// Asynchronously shows popup list if need.\n\t/// </summary>\n\tpublic void SciCharAdded_ShowList(CodeInfo.CharContext c) {\n\t\tif (_data == null) {\n\t\t\t_ShowList(c.ch);\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// If showing popup list, synchronously filters/selects items.\n\t/// </summary>\n\tpublic void SciModified(SciCode doc, in Sci.SCNotification n) {\n\t\tif (_data != null) {\n\t\t\tbool trValid = _data.tempRange.GetCurrentFromTo(out int from, out int to, utf8: true);\n\t\t\tDebug.Assert(trValid); if (!trValid) { Cancel(); return; }\n\t\t\tstring s = doc.aaaRangeText(false, from, to);\n\t\t\t\n\t\t\t//cancel if typed nonalpha if auto-showed by typing nonalpha (eg space in parameters or '(' after method name)\n\t\t\tif (!_data.forced && _data.filterText.NE() && s.Length == 1 && !SyntaxFacts.IsIdentifierStartCharacter(s[0]) && !(SyntaxFacts.IsIdentifierPartCharacter(s[0]) && _data.isDot)) {\n\t\t\t\tCancel();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\tforeach (var v in s) if (!SyntaxFacts.IsIdentifierPartCharacter(v)) return; //mostly because now is before SciCharAddedCommit, which commits (or cancels) if added invalid char\n\t\t\t_data.filterText = s;\n\t\t\t_FilterItems(_data);\n\t\t\t_popupList.UpdateVisibleItems(); //and calls SelectBestMatch\n\t\t}\n\t}\n\t\n\tpublic void ShowList(char ch = default) {\n\t\t_ShowList(ch);\n\t}\n\t\n\t//static bool s_workaround1;\n\t\n\tasync void _ShowList(char ch) {\n\t\t//using\n\t\t//var p1 = perf.local();\n\t\t\n\t\t//print.it(_cancelTS);\n\t\t//bool busy = _cancelTS != null;\n\t\tbool isCommand = ch == default;\n\t\t\n\t\tif (!CodeInfo.GetContextWithoutDocument(out var cd)) { //returns false if position is in meta comments\n\t\t\tCancel();\n\t\t\treturn;\n\t\t}\n\t\tSciCode doc = cd.sci;\n\t\tint position = cd.pos;\n\t\tstring code = cd.code;\n\t\t\n\t\tif (ch != default && position > 1 && SyntaxFacts.IsIdentifierPartCharacter(ch) && SyntaxFacts.IsIdentifierPartCharacter(code[position - 2])) { //in word\n\t\t\treturn;\n\t\t\t//never mind: does not hide Regex completions. Same in VS.\n\t\t}\n\t\t\n\t\tbool unimported = isCommand && IsVisibleUI;\n\t\t\n\t\tCancel();\n\t\t\n\t\t//CodeInfo.HideTextPopupAndTempWindows(); //no\n\t\tCodeInfo.HideTextPopup();\n\t\t\n\t\t//using var nogcr = keys.isScrollLock ? new Debug_.NoGcRegion(50_000_000) : default;\n\t\t\n\t\tif (!cd.GetDocument()) return; //returns false if fails (unlikely)\n\t\tvar document = cd.document;\n\t\t//p1.Next('d');\n\t\t\n\t\tif (ch == '/') {\n\t\t\tGenerateCode.DocComment(cd);\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tbool isDot = false, canGroup = false;\n\t\tPSFormat stringFormat = PSFormat.None;\n\t\tCiStringInfo stringInfo = default;\n\t\tCompletionService completionService = null;\n\t\tSemanticModel model = null;\n\t\tCompilationUnitSyntax root = null;\n\t\tCSharpSyntaxContext syncon = null;\n\t\tITypeSymbol typeL = null; //not null if X. where X is not type/namespace/unknown\n\t\tISymbol symL = null; //symbol at left of . etc\n\t\tint typenameStart = -1;\n\t\tINamedTypeSymbol nativeApiClass = null;\n\t\t\n\t\tvar cancelTS = _cancelTS = new CancellationTokenSource();\n\t\tvar cancelToken = cancelTS.Token;\n#if DEBUG\n\t\tif (Debugger.IsAttached) { cancelToken = default; _cancelTS = null; }\n#endif\n\t\t\n\t\ttry {\n\t\t\tCompletionList r = await Task.Run(async () => { //info: usually GetCompletionsAsync etc are not async\n\t\t\t\tmodel = await document.GetSemanticModelAsync(cancelToken).ConfigureAwait(false); //speed: does not make slower, because GetCompletionsAsync calls it too. Same speed if only GetSyntaxRootAsync.\n\t\t\t\troot = model.Root as CompilationUnitSyntax;\n\t\t\t\tvar tok = root.FindToken(position, findInsideTrivia: true);\n\t\t\t\t//CiUtil.PrintNode(tok);\n\t\t\t\t\n\t\t\t\t//return if in trivia, except if space in non-comments\n\t\t\t\tif (!isCommand && !tok.Span.ContainsOrTouches(position)) {\n\t\t\t\t\tbool good = false;\n\t\t\t\t\tif (ch == ' ') good = tok.FindTrivia(position - 1).IsKind(SyntaxKind.WhitespaceTrivia);\n\t\t\t\t\tif (ch == '<') good = tok.FindTrivia(position - 1).RawKind == 0; //in XML doc\n\t\t\t\t\tif (!good) return null;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ttry { syncon = CSharpSyntaxContext.CreateContext(document, model, position, cancelToken); }\n\t\t\t\tcatch (ArgumentException) { return null; } //may happen in invalid code, where probably don't need intellisense\n\t\t\t\t\n\t\t\t\tvar node = tok.Parent;\n\t\t\t\t//p1.Next('s');\n\t\t\t\t\n\t\t\t\t//in some cases show list when typing a character where GetCompletionsAsync works only on command\n\t\t\t\tif (ch == '[' && syncon.IsAttributeNameContext) ch = default;\n\t\t\t\tif (ch == ' ' && syncon.IsObjectCreationTypeContext) ch = default;\n\t\t\t\t//rejected. Then, if typed space, shows list 2 times. Not nice.\n\t\t\t\t//if (ch == '|') { //show on 'Enum.Member|'. Roslyn shows only on 'Enum.Member| ' (need space after |).\n\t\t\t\t//\tvar t2 = tok.GetPreviousToken();\n\t\t\t\t//\tif (t2.IsKind(SyntaxKind.BarToken) && t2.SpanStart == position - 1 && t2.Parent.IsKind(SyntaxKind.BitwiseOrExpression)) {\n\t\t\t\t//\t\tvar n2 = t2.GetPreviousToken().Parent;\n\t\t\t\t//\t\tif (n2 is IdentifierNameSyntax && (model.GetTypeInfo(n2).Type?.IsEnumType() ?? false)) ch = default;\n\t\t\t\t//\t}\n\t\t\t\t//}\n\t\t\t\t\n\t\t\t\tcompletionService = CompletionService.GetService(document);\n\t\t\t\tif (cancelToken.IsCancellationRequested) return null;\n\t\t\t\t\n\t\t\t\tvar options = CompletionOptions.Default with {\n\t\t\t\t\tTriggerInArgumentLists = false,\n\t\t\t\t\tShowNameSuggestions = false,\n\t\t\t\t\tSnippetsBehavior = SnippetsRule.NeverInclude,\n\t\t\t\t\tPerformSort = false,\n\t\t\t\t\tShowItemsFromUnimportedNamespaces = unimported,\n\t\t\t\t\tForceExpandedCompletionIndexCreation = unimported,\n\t\t\t\t\tExpandedCompletionBehavior = unimported ? ExpandedCompletionMode.ExpandedItemsOnly : ExpandedCompletionMode.AllItems,\n\t\t\t\t\t//TargetTypedCompletionFilter = true, //?\n\t\t\t\t};\n\t\t\t\t//print.it(options);\n\t\t\t\t//rejected: support /// <completionlist cref=\"ClassUsedLikeEnum\"/>. There is no option. It seems it's a legacy undocumented VS-only feature.\n\t\t\t\tvar trigger = ch == default ? default : CompletionTrigger.CreateInsertionTrigger(ch);\n\t\t\t\t\n\t\t\t\tCompletionList r1 = await completionService.GetCompletionsAsync(document, position, options, null, trigger, cancellationToken: cancelToken).ConfigureAwait(false);\n\t\t\t\tif (r1 != null && r1.ItemsList.Count == 0) r1 = null;\n\t\t\t\tif (unimported) return r1; //don't support grouping etc. The completion items don't have Symbol. It's OK.\n\t\t\t\tif (r1 != null) {\n\t\t\t\t\tcanGroup = true;\n\t\t\t\t\t//is it member access?\n\t\t\t\t\tbool propPatOrInit = node is PropertyPatternClauseSyntax or InitializerExpressionSyntax;\n\t\t\t\t\tif (propPatOrInit) {\n\t\t\t\t\t\tpropPatOrInit = node.Span.ContainsInside(position);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (position <= tok.SpanStart && tok.GetPreviousToken() is var t1 && t1.Kind() is SyntaxKind.CommaToken or SyntaxKind.OpenBraceToken)\n\t\t\t\t\t\t\tif (t1.Parent is PropertyPatternClauseSyntax or InitializerExpressionSyntax) (node, propPatOrInit) = (t1.Parent, true);\n\t\t\t\t\t}\n\t\t\t\t\tif (propPatOrInit) {\n\t\t\t\t\t\tif (node is InitializerExpressionSyntax) {\n\t\t\t\t\t\t\t//if only properties and/or fields, group by inheritance. Else group by namespace; it's a collection initializer list and contains everything.\n\t\t\t\t\t\t\tisDot = !r1.ItemsList.Any(o => o.Symbols?[0] is not (IPropertySymbol or IFieldSymbol));\n\t\t\t\t\t\t\tif (!isDot && ch == '{') return null; //eg 'new int[] {'\n\t\t\t\t\t\t} else isDot = true;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tisDot = syncon.IsRightOfNameSeparator;\n\t\t\t\t\t\tif (isDot) { //set canGroup = false if Namespace.X or alias::X\n\t\t\t\t\t\t\tif (syncon.IsInImportsDirective) {\n\t\t\t\t\t\t\t\tcanGroup = false;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tvar token = syncon.TargetToken; //not LeftToken, it seems they are swapped\n\t\t\t\t\t\t\t\tnode = token.Parent;\n\t\t\t\t\t\t\t\t//CiUtil.PrintNode(token);\n\t\t\t\t\t\t\t\t//CiUtil.PrintNode(node);\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tswitch (node) {\n\t\t\t\t\t\t\t\tcase MemberAccessExpressionSyntax s1: // . or ->\n\t\t\t\t\t\t\t\t\tnode = s1.Expression;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tcase MemberBindingExpressionSyntax s1: // ?.\n\t\t\t\t\t\t\t\t\tif (s1.Parent.Parent is ConditionalAccessExpressionSyntax caes1) node = caes1.Expression; else node = s1;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tcase QualifiedNameSyntax s1: // eg . outside functions\n\t\t\t\t\t\t\t\t\tnode = s1.Left;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tcase AliasQualifiedNameSyntax: // ::\n\t\t\t\t\t\t\t\tcase ExplicitInterfaceSpecifierSyntax: //Interface.X\n\t\t\t\t\t\t\t\tcase QualifiedCrefSyntax: //does not include base members\n\t\t\t\t\t\t\t\t\tcanGroup = false;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tcase RangeExpressionSyntax: //x..y (user may want to make x.Method().y)\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\t\tDebug_.Print(node.GetType());\n\t\t\t\t\t\t\t\t\tisDot = canGroup = false;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tif (canGroup) {\n#if true //need typeL\n\t\t\t\t\t\t\t\t\tvar ti = model.GetTypeInfo(node).Type;\n\t\t\t\t\t\t\t\t\tif (ti == null) {\n\t\t\t\t\t\t\t\t\t\tDebug_.PrintIf(model.GetSymbolInfo(node).Symbol is not INamespaceSymbol, node);\n\t\t\t\t\t\t\t\t\t\tcanGroup = false;\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tsymL = model.GetSymbolInfo(node).Symbol;\n\t\t\t\t\t\t\t\t\t\tDebug_.PrintIf(symL is INamespaceSymbol, node);\n\t\t\t\t\t\t\t\t\t\t//print.it(symL, symL is INamedTypeSymbol);\n\t\t\t\t\t\t\t\t\t\tif (symL is INamedTypeSymbol ints1) {\n\t\t\t\t\t\t\t\t\t\t\ttypenameStart = node.SpanStart;\n\t\t\t\t\t\t\t\t\t\t\tif (CiWinapi.IsWinapiClassSymbol(ints1)) nativeApiClass = ints1.BaseType;\n\t\t\t\t\t\t\t\t\t\t} else typeL = ti;\n\t\t\t\t\t\t\t\t\t}\n#else //need just canGroup\n\t\t\t\t\t\t\t\t\tif (model.GetSymbolInfo(node).Symbol is INamespaceSymbol) canGroup = false;\n#endif\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t//print.it(canGroup);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t//p1.Next('M');\n\t\t\t\t} else if (isCommand) {\n\t\t\t\t\tif (tok.IsInString(position, code, out stringInfo) == true) {\n\t\t\t\t\t\tvar tspan = stringInfo.textSpan;\n\t\t\t\t\t\tstringFormat = CiUtil.GetParameterStringFormat(stringInfo.stringNode, model, true);\n\t\t\t\t\t\tif (stringFormat == PSFormat.Wildex) { //is regex in wildex?\n\t\t\t\t\t\t\tif (code.RxMatch(@\"\\G(?:\\*\\*\\*\\w+ )?\\*\\*c?rc? \", 0, out RXGroup rg, 0, tspan.ToRange())\n\t\t\t\t\t\t\t\t&& position >= tspan.Start + rg.Length) stringFormat = PSFormat.Regexp;\n\t\t\t\t\t\t} else if (stringFormat == PSFormat.None) stringFormat = (PSFormat)100;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn r1;\n\t\t\t}); //await Task.Run\n\t\t\t\n\t\t\tif (cancelToken.IsCancellationRequested) return;\n\t\t\t\n\t\t\tif (r == null) {\n\t\t\t\tif (stringFormat == (PSFormat)100) {\n\t\t\t\t\tvar m = new popupMenu();\n\t\t\t\t\tm[\"Regex\"] = o => CodeInfo._tools.ShowForStringParameter(PSFormat.Regexp, cd, stringInfo);\n\t\t\t\t\tm[\"Keys\"] = o => CodeInfo._tools.ShowForStringParameter(PSFormat.Keys, cd, stringInfo);\n\t\t\t\t\tm.Separator();\n\t\t\t\t\tm.Submenu(\"Help\\tF1\", m => {\n\t\t\t\t\t\tm[\"C# strings\"] = o => run.itSafe(CiUtil.GoogleURL(\"C# strings\"));\n\t\t\t\t\t\tm[\"String formatting\"] = o => run.itSafe(CiUtil.GoogleURL(\"C# string formatting\"));\n\t\t\t\t\t\tm[\"Wildcard expression\"] = o => HelpUtil.AuHelp(\"articles/Wildcard expression\");\n\t\t\t\t\t\tm[\"Output tags\"] = o => HelpUtil.AuHelp(\"articles/Output tags\");\n\t\t\t\t\t});\n\t\t\t\t\tm.Show(PMFlags.ByCaret, owner: doc.AaWnd);\n\t\t\t\t} else if (stringFormat != 0) {\n\t\t\t\t\tCodeInfo._tools.ShowForStringParameter(stringFormat, cd, stringInfo);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\tDebug.Assert(doc == Panels.Editor.ActiveDoc); //when active doc changed, cancellation must be requested\n\t\t\tif (doc.aaaText != code) { //changed while awaiting\n\t\t\t\ttimer.after(55, _ => { if (doc == Panels.Editor.ActiveDoc) ShowList(); });\n\t\t\t\treturn;\n\t\t\t\t//TODO3: instead: when text changed, cancel and set timer.\n\t\t\t} else if (doc.aaaCurrentPos16 != position) return;\n\t\t\t//p1.Next('T');\n\t\t\t\n\t\t\tvar provider = _GetProvider(r.ItemsList[0]); //currently used only in cases when all completion items are from same provider\n\t\t\tif (!isDot) isDot = provider == CiComplProvider.Override;\n\t\t\t//print.it(provider, isDot, canGroup, r.Items[0].DisplayText);\n\t\t\t\n\t\t\tvar span = r.Span;\n\t\t\t\n\t\t\tvar d = new _Data {\n\t\t\t\tcompletionService = completionService,\n\t\t\t\tdocument = document,\n\t\t\t\tmodel = model,\n\t\t\t\tcodeLength = code.Length,\n\t\t\t\tfilterText = code.Substring(span.Start, span.Length),\n\t\t\t\titems = new(r.ItemsList.Count),\n\t\t\t\tforced = isCommand,\n\t\t\t\tnoAutoSelect = r.SuggestionModeItem != null,\n\t\t\t\tisDot = isDot,\n\t\t\t\tunimported = unimported\n\t\t\t};\n\t\t\t//if (r.SuggestionModeItem is {  } l) print.it(l.DisplayText, l.InlineDescription, l.Properties, l.ProviderName, l.Tags); //the item is not useful\n\t\t\t\n\t\t\t//ISymbol enclosing = null;\n\t\t\t//bool _IsAccessible(ISymbol symbol) {\n\t\t\t//\tenclosing ??= model.GetEnclosingNamedTypeOrAssembly(position, default);\n\t\t\t//\treturn enclosing != null && symbol.IsAccessibleWithin(enclosing);\n\t\t\t//}\n\t\t\t\n\t\t\t//info: some members of enum UnmanagedType are missing. Hidden with EditorBrowsableState.Never, don't know why.\n\t\t\t\n\t\t\t//var testInternal = CodeInfo.Meta.TestInternal;\n\t\t\tDictionary<INamespaceOrTypeSymbol, List<int>> groups = canGroup ? new(new CiNamespaceOrTypeSymbolEqualityComparer()) : null;\n\t\t\tList<int> keywordsGroup = null, etcGroup = null, snippetsGroup = null;\n\t\t\tuint kinds = 0; bool hasNamespaces = false;\n\t\t\tforeach (var ci_ in r.ItemsList) {\n\t\t\t\t//FUTURE: test with new Roslyn, maybe this fixed (in VS works well):\n\t\t\t\t//\tThe list contains only types if after `is`. Example: `bool ok = m is C.`. Here C is a class that contains inner types and int constants.\n\t\t\t\t\n\t\t\t\tvar ci = ci_;\n\t\t\t\tif (unimported) {\n\t\t\t\t\t//if (ci.Flags.Has(CompletionItemFlags.Expanded)) { //now instead used CompletionOptions.ExpandedCompletionBehavior\n\t\t\t\t\td.items.Add(new CiComplItem(provider, ci));\n\t\t\t\t\t//}\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tDebug.Assert(ci.Symbols == null || ci.Symbols.Count > 0); //we may use code ci?.Symbols[0]. Roslyn uses this code in CompletionItem ctor: var firstSymbol = symbols[0];\n\t\t\t\tvar sym = ci.Symbols?[0];\n\t\t\t\t\n\t\t\t\t//if (unimported && (ci.DisplayText == \"Activator\" || ci.DisplayText.Starts(\"Post\"))) {\n\t\t\t\t//\tprint.it(ci.Flags, ci.InlineDescription, ci.Properties, ci.Tags);\n\t\t\t\t//}\n\t\t\t\t\n\t\t\t\tif (sym != null) {\n\t\t\t\t\tif (sym is IAliasSymbol ia) ci.Symbols = ImmutableArray.Create(sym = ia.Target);\n\t\t\t\t\t\n\t\t\t\t\tif (provider == CiComplProvider.Cref) { //why it adds internals from other assemblies?\n\t\t\t\t\t\tswitch (sym.Kind) {\n\t\t\t\t\t\tcase SymbolKind.NamedType when sym.DeclaredAccessibility != Microsoft.CodeAnalysis.Accessibility.Public && !sym.IsInSource() && !model.IsAccessible(0, sym):\n\t\t\t\t\t\t\t//ci.DebugPrint();\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\tcase SymbolKind.Namespace:\n\t\t\t\t\t\t\t//print.it(sym, sym.ContainingAssembly?.Name, sym.IsInSource());\n\t\t\t\t\t\t\tswitch (sym.Name) {\n\t\t\t\t\t\t\tcase \"Internal\" when sym.ContainingAssembly?.Name == \"System.Core\":\n\t\t\t\t\t\t\tcase \"Windows\" when sym.ContainingAssembly?.Name == \"mscorlib\":\n\t\t\t\t\t\t\tcase \"MS\" when sym.ContainingAssembly?.Name == null:\n\t\t\t\t\t\t\tcase \"FxResources\" when sym.ContainingAssembly?.Name == \"System.Resources.Extensions\":\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (nativeApiClass != null && sym.ContainingType == nativeApiClass) {\n\t\t\t\t\t\t//d.items.Add(new CiComplItem(provider, ci) { moveDown = CiComplItemMoveDownBy.Name });\n\t\t\t\t\t\td.items.Add(new CiComplItem(provider, ci) { hidden = CiComplItemHiddenBy.Always });\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvar v = new CiComplItem(provider, ci);\n\t\t\t\tkinds |= 1u << ((int)v.kind);\n\t\t\t\t//print.it(ci.DisplayText, sym);\n\t\t\t\t//if(ci.SortText != ci.DisplayText) print.it($\"<>{ci.DisplayText}, sort={ci.SortText}\");\n\t\t\t\t//if(ci.FilterText != ci.DisplayText) print.it($\"<>{ci.DisplayText}, filter={ci.FilterText}\");\n\t\t\t\t//if(!ci.DisplayTextSuffix.NE()) print.it($\"<>{ci.DisplayText}, suf={ci.DisplayTextSuffix}\");\n\t\t\t\t//if(!ci.DisplayTextPrefix.NE()) print.it($\"<>{ci.DisplayText}, pre={ci.DisplayTextPrefix}\");\n\t\t\t\t//print.it(ci.Flags); //a new internal property. Always None.\n\t\t\t\t\n\t\t\t\tswitch (v.kind) {\n\t\t\t\tcase CiItemKind.Method:\n\t\t\t\t\tif (sym != null) {\n\t\t\t\t\t\tif (sym.IsStatic) {\n\t\t\t\t\t\t\tswitch (ci.DisplayText) {\n\t\t\t\t\t\t\tcase \"Equals\":\n\t\t\t\t\t\t\tcase \"ReferenceEquals\":\n\t\t\t\t\t\t\t\t//hide static members inherited from Object\n\t\t\t\t\t\t\t\tif (sym.ContainingType.BaseType == null) { //Object\n\t\t\t\t\t\t\t\t\tif (isDot && !(symL is INamedTypeSymbol ints1 && ints1.BaseType == null)) continue; //if not object\n\t\t\t\t\t\t\t\t\tv.moveDown = CiComplItemMoveDownBy.Name;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase \"Main\" when _IsOurScriptClass(sym.ContainingType):\n\t\t\t\t\t\t\t\tv.moveDown = CiComplItemMoveDownBy.Name;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tswitch (ci.DisplayText) {\n\t\t\t\t\t\t\tcase \"Equals\" or \"GetHashCode\" or \"GetType\" or \"ToString\":\n\t\t\t\t\t\t\tcase \"MemberwiseClone\":\n\t\t\t\t\t\t\tcase \"Deconstruct\": //record\n\t\t\t\t\t\t\tcase \"GetEnumerator\": //IEnumerable\n\t\t\t\t\t\t\tcase \"CompareTo\": //IComparable\n\t\t\t\t\t\t\tcase \"GetTypeCode\": //IConvertible\n\t\t\t\t\t\t\tcase \"Clone\" when sym.ContainingType.Name == \"String\": //this useless method would be the first in the list\n\t\t\t\t\t\t\t\tv.moveDown = CiComplItemMoveDownBy.Name;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t//var ct = sym.ContainingType;\n\t\t\t\t\t\t\t//print.it(ct.ToString(), ct.Name, ct.ContainingNamespace.ToString(), ct.BaseType);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase CiItemKind.Namespace when ci.Symbols != null: //null if extern alias\n\t\t\t\t\thasNamespaces = true;\n\t\t\t\t\tswitch (ci.DisplayText) {\n\t\t\t\t\tcase \"Accessibility\":\n\t\t\t\t\tcase \"UIAutomationClientsideProviders\":\n\t\t\t\t\t\tv.moveDown = CiComplItemMoveDownBy.Name;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"XamlGeneratedNamespace\": continue;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase CiItemKind.TypeParameter:\n\t\t\t\t\tif (sym == null && ci.DisplayText == \"T\") continue;\n\t\t\t\t\tbreak;\n\t\t\t\tcase CiItemKind.Class:\n\t\t\t\t\tif (!isDot && sym is INamedTypeSymbol ins && _IsOurScriptClass(ins)) v.moveDown = CiComplItemMoveDownBy.Name;\n\t\t\t\t\tbreak;\n\t\t\t\tcase CiItemKind.EnumMember when !isDot:\n\t\t\t\t\t//workaround for: if Enum.Member, members are sorted by value, not by name. Same in VS.\n\t\t\t\t\tif (ci.SortText != ci.DisplayText) {\n\t\t\t\t\t\tstring ss = ci.SortText, se = sym.ContainingType.Name;\n\t\t\t\t\t\tif (ss.Length == se.Length + 5 && ss.Starts(se) && ss[se.Length] == '_') //like \"EnumName_0001\"\n\t\t\t\t\t\t\tv.SetCI(ci = ci.WithSortText(se + \".\" + sym.Name));\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase CiItemKind.EnumMember when isDot:\n\t\t\t\tcase CiItemKind.Label:\n\t\t\t\t\tcanGroup = false;\n\t\t\t\t\tbreak;\n\t\t\t\tcase CiItemKind.LocalVariable:\n\t\t\t\t\tif (isDot) continue; //see the bug comment below\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tstatic bool _IsOurScriptClass(INamedTypeSymbol t) => t.Name is \"Program\" or \"Script\";\n\t\t\t\t\n\t\t\t\tif (sym != null && v.kind is not (CiItemKind.LocalVariable or CiItemKind.Namespace or CiItemKind.TypeParameter)) {\n\t\t\t\t\tif (ci.Symbols.All(sy => sy.IsObsolete())) v.moveDown = CiComplItemMoveDownBy.Obsolete; //can be several overloads, some obsolete but others not\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\td.items.Add(v);\n\t\t\t}\n\t\t\t//p1.Next('i');\n\t\t\t\n\t\t\tif (canGroup) {\n\t\t\t\tfor (int i = 0; i < d.items.Count; i++) {\n\t\t\t\t\tvar v = d.items[i];\n\t\t\t\t\tvar sym = v.FirstSymbol;\n\t\t\t\t\tif (sym == null) {\n\t\t\t\t\t\tif (v.kind == CiItemKind.Keyword) (keywordsGroup ??= []).Add(i);\n\t\t\t\t\t\telse (etcGroup ??= []).Add(i);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tINamespaceOrTypeSymbol nts;\n\t\t\t\t\t\tif (!isDot) {\n\t\t\t\t\t\t\tnts = sym.ContainingNamespace;\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t//put locals at the top\n\t\t\t\t\t\t\tif (nts?.ContainingNamespace != null && sym.ContainingSymbol is not INamespaceOrTypeSymbol) {\n\t\t\t\t\t\t\t\twhile (nts.ContainingNamespace is INamespaceSymbol n1) nts = n1; //global namespace\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t//rejected: also put members of enclosing type[s] (and the type itself) at the top.\n\t\t\t\t\t\t\t//\tNot so easy to implement without problems like \"Enum.Member\" in a different group than the \"Enum\".\n\t\t\t\t\t\t\t//\tIn some cases model.GetEnclosingNamedType returns other object although logically it is the same as sym.\n\t\t\t\t\t\t\t//\tAlso then may be more confusion. Now everything is clear: locals are at the top, as well as everything namespaceless.\n\t\t\t\t\t\t\t//\tSimple code that does not work well:\n\t\t\t\t\t\t\t//let only types be in namespace groups. Put locals and non-type members of enclosing type(s) at the top.\n\t\t\t\t\t\t\t//if (nts.ContainingNamespace != null && sym is not INamespaceOrTypeSymbol) {\n\t\t\t\t\t\t\t//\twhile (nts.ContainingNamespace is INamespaceSymbol n1) nts = n1; //global namespace\n\t\t\t\t\t\t\t//}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t//CONSIDER: put in different groups: namespaces, locals, members of enclosing type(s), other namespaceless symbols. Now grouped by kind.\n\t\t\t\t\t\t}\n\t\t\t\t\t\t//else if(sym is ReducedExtensionMethodSymbol em) nts = em.ReceiverType; //rejected. Didn't work well, eg with linq.\n\t\t\t\t\t\telse nts = sym.ContainingType;\n\t\t\t\t\t\t\n\t\t\t\t\t\t//Roslyn bug: sometimes adds some garbage items.\n\t\t\t\t\t\t//To reproduce: invoke global list. Then invoke list for a string variable. Adds String, Object, all local string variables, etc. Next time works well. After Enum dot adds the enum type, even in VS; in VS sometimes adds enum methods and extmethods.\n\t\t\t\t\t\t//Debug_.PrintIf(nts == null, sym.Name);\n\t\t\t\t\t\tif (nts == null) continue;\n\t\t\t\t\t\t\n\t\t\t\t\t\t//extension type (no Name) -> containing type\n\t\t\t\t\t\tif (nts is INamedTypeSymbol { IsExtension: true }) nts = nts.ContainingType;\n\t\t\t\t\t\t\n\t\t\t\t\t\tif (groups.TryGetValue(nts, out var list)) list.Add(i); else groups.Add(nts, [i]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t//snippets, winapi\n\t\t\t\tif (isDot) {\n\t\t\t\t\tif (typeL != null) { //eg variable.x\n\t\t\t\t\t} else if (nativeApiClass != null) { //api.x\n\t\t\t\t\t\tint i = d.items.Count;\n\t\t\t\t\t\tbool newExpr = syncon.TargetToken.Parent.Parent is ObjectCreationExpressionSyntax or StackAllocArrayCreationExpressionSyntax; //info: syncon.IsObjectCreationTypeContext false\n\t\t\t\t\t\td.winapi = CiWinapi.AddWinapi(symL as INamedTypeSymbol, d.items, typenameStart, newExpr);\n\t\t\t\t\t\tint n = d.items.Count - i;\n\t\t\t\t\t\tif (n > 0) {\n\t\t\t\t\t\t\tsnippetsGroup = new(n);\n\t\t\t\t\t\t\tfor (; i < d.items.Count; i++) snippetsGroup.Add(i);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else if (!d.noAutoSelect && (hasNamespaces || _OnlyCSharpKeywordsOrDirectives())) {\n\t\t\t\t\t//add snippets\n\t\t\t\t\tif (provider is not (CiComplProvider.Cref or CiComplProvider.XmlDoc)) {\n\t\t\t\t\t\tint i = d.items.Count;\n\t\t\t\t\t\tCiSnippets.AddSnippets(d.items, span, root, code, false, syncon);\n\t\t\t\t\t\tfor (; i < d.items.Count; i++) (snippetsGroup ??= []).Add(i);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tbool _OnlyCSharpKeywordsOrDirectives()\n\t\t\t\t\t=> kinds == 1u << (int)CiItemKind.Keyword && provider == CiComplProvider.Keyword;\n\t\t\t}\n\t\t\t//p1.Next('+');\n\t\t\t\n\t\t\tif (d.items.Count == 0) return;\n\t\t\t\n\t\t\tList<string> groupsList = null;\n\t\t\tif (canGroup && groups.Count + (keywordsGroup == null ? 0 : 1) + (etcGroup == null ? 0 : 1) + (snippetsGroup == null ? 0 : 1) > 1) {\n\t\t\t\tList<(string, List<int>)> g = null;\n\t\t\t\tif (isDot) {\n\t\t\t\t\tvar gs = groups.ToList();\n\t\t\t\t\tgs.Sort((k1, k2) => {\n\t\t\t\t\t\t//let extension methods be at bottom, sorted by type name\n\t\t\t\t\t\tint em1 = d.items[k1.Value[0]].kind == CiItemKind.ExtensionMethod ? 1 : 0;\n\t\t\t\t\t\tint em2 = d.items[k2.Value[0]].kind == CiItemKind.ExtensionMethod ? 1 : 0;\n\t\t\t\t\t\tint diff = em1 - em2;\n\t\t\t\t\t\tif (diff != 0) return diff;\n\t\t\t\t\t\tif (em1 == 1) return string.Compare(k1.Key.Name, k2.Key.Name, StringComparison.OrdinalIgnoreCase);\n#if true\n\t\t\t\t\t\t//sort non-extension members by inheritance or base interface\n\t\t\t\t\t\tvar t1 = k1.Key as INamedTypeSymbol; var t2 = k2.Key as INamedTypeSymbol;\n\t\t\t\t\t\tif (t1.InheritsFromOrImplementsOrEqualsIgnoringConstruction(t2)) return -1;\n\t\t\t\t\t\tif (t2.InheritsFromOrImplementsOrEqualsIgnoringConstruction(t1)) return 1;\n\t\t\t\t\t\t//interface and object? For both, BaseType returns null and InheritsFromOrImplementsOrEqualsIgnoringConstruction returns false.\n\t\t\t\t\t\tvar tk1 = t1.TypeKind; var tk2 = t2.TypeKind;\n\t\t\t\t\t\tif (tk1 == TypeKind.Class && t1.BaseType == null) return 1; //t1 is object\n\t\t\t\t\t\tif (tk2 == TypeKind.Class && t2.BaseType == null) return -1; //t2 is object\n\t\t\t\t\t\t//Debug_.Print($\"{t1}, {t2},    {t1.BaseType}, {t2.BaseType},    {tk1}, {tk2}\");\n#else\n\t\t\t\t\t\t//sort non-extension members by inheritance\n\t\t\t\t\t\tvar t1 = k1.Key as INamedTypeSymbol; var t2 = k2.Key as INamedTypeSymbol;\n\t\t\t\t\t\tif (_IsBase(t1, t2)) return -1;\n\t\t\t\t\t\tif (_IsBase(t2, t1)) return 1;\n\t\t\t\t\t\tstatic bool _IsBase(INamedTypeSymbol t, INamedTypeSymbol tBase) {\n\t\t\t\t\t\t\tfor (t = t.BaseType; t != null; t = t.BaseType) if (t == tBase) return true;\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t//can be both interfaces, or interface and object. For object and interfaces BaseType returns null.\n\t\t\t\t\t\tvar tk1 = t1.TypeKind; var tk2 = t2.TypeKind;\n\t\t\t\t\t\tif (tk1 == TypeKind.Class && t1.BaseType == null) return 1; //t1 is object\n\t\t\t\t\t\tif (tk2 == TypeKind.Class && t2.BaseType == null) return -1; //t2 is object\n\t\t\t\t\t\tif (tk1 == TypeKind.Interface && tk2 == TypeKind.Interface) {\n\t\t\t\t\t\t\tif (t2.AllInterfaces.Contains(t1)) return 1;\n\t\t\t\t\t\t\tif (t1.AllInterfaces.Contains(t2)) return -1;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t//fails for eg ObservableCollection<>. Uses 2 variables for t2 and t1.BaseType although it is the same type.\n\t\t\t\t\t\tDebug_.Print($\"{t1}, {t2}, {k1.Value.Count}, {k2.Value.Count}, {tk1}, {tk2}, {t1.BaseType}, {t2.BaseType}\"); //usually because of Roslyn bugs\n#endif\n\t\t\t\t\t\t\n\t\t\t\t\t\treturn 0;\n\t\t\t\t\t});\n\t\t\t\t\t//print.it(gs);\n\t\t\t\t\t\n#if true\n\t\t\t\t\tif (gs[0].Key.Name == \"String\") { //don't group Au extension methods\n\t\t\t\t\t\tfor (int i = gs.Count; --i > 0;) {\n\t\t\t\t\t\t\tif (d.items[gs[i].Value[0]].kind != CiItemKind.ExtensionMethod) continue;\n\t\t\t\t\t\t\tvar ns = gs[i].Key.ContainingNamespace;\n\t\t\t\t\t\t\tif (ns.Name == \"Types\" && ns.ContainingNamespace.Name == \"Au\") {\n\t\t\t\t\t\t\t\tgs[0].Value.AddRange(gs[i].Value);\n\t\t\t\t\t\t\t\tgs.RemoveAt(i);\n\t\t\t\t\t\t\t\t//break; //no. If testInternal Au, we have 2 Au.Types.\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n#else\n\t\t\t\t\tif(!App.Settings.ci_complGroupEM) { //don't group non-Linq extension methods\n\t\t\t\t\t\tfor(int i = 1; i < gs.Count; i++) {\n\t\t\t\t\t\t\tif(d.items[gs[i].Value[0]].kind != CiItemKind.ExtensionMethod) continue;\n\t\t\t\t\t\t\tvar ns = gs[i].Key.ContainingNamespace;\n\t\t\t\t\t\t\tif(ns.Name != \"Linq\") {\n\t\t\t\t\t\t\t\tgs[0].Value.AddRange(gs[i].Value);\n\t\t\t\t\t\t\t\tgs.RemoveAt(i--);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n#endif\n\t\t\t\t\t\n\t\t\t\t\tg = gs.Select(o => (o.Key.Name, o.Value)).ToList(); //list<(itype, list)> -> list<typeName, list>\n\t\t\t\t} else {\n\t\t\t\t\tg = groups.Select(o => (o.Key.QualifiedName(), o.Value)).ToList(); //dictionary<inamespace, list> -> list<namespaceName, list>\n\t\t\t\t\tg.Sort((e1, e2) => {\n\t\t\t\t\t\t//order: global, Au, my, others by name, Microsoft.*\n\t\t\t\t\t\tstring s1 = e1.Item1, s2 = e2.Item1;\n\t\t\t\t\t\tint k1 = s1.Length <= 2 ? (s1 switch { \"\" => 3, \"Au\" => 2, \"my\" => 1, _ => 0 }) : s1.Starts(\"Microsoft.\") ? -1 : 0;\n\t\t\t\t\t\tint k2 = s2.Length <= 2 ? (s2 switch { \"\" => 3, \"Au\" => 2, \"my\" => 1, _ => 0 }) : s2.Starts(\"Microsoft.\") ? -1 : 0;\n\t\t\t\t\t\tint kd = k2 - k1; if (kd != 0) return kd;\n\t\t\t\t\t\treturn string.Compare(s1, s2, StringComparison.OrdinalIgnoreCase);\n\t\t\t\t\t});\n\t\t\t\t\t//print.it(\"----\");\n\t\t\t\t\t//foreach(var v in g) print.it(v.Item1, v.Item2.Count);\n\t\t\t\t\t\n\t\t\t\t\tif (hasNamespaces && _GetFilters(model, out var filters)) {\n\t\t\t\t\t\tforeach (var (ns, a) in g) { //for each namespace in completion list\n\t\t\t\t\t\t\tif (ns.NE() || !filters.TryGetValue(ns, out var k)) continue;\n\t\t\t\t\t\t\tforeach (var i in a) { //for each type in that namespace in completion list\n\t\t\t\t\t\t\t\tvar sym = d.items[i].FirstSymbol;\n\t\t\t\t\t\t\t\tif (sym is not INamedTypeSymbol nt) {\n\t\t\t\t\t\t\t\t\tif (sym is IFieldSymbol fs) nt = fs.ContainingType; //enum member\n\t\t\t\t\t\t\t\t\telse continue;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tvar s = nt.Name;\n\t\t\t\t\t\t\t\tstring opt = k[0];\n\t\t\t\t\t\t\t\tbool found = k.Length == 1 || (k.Length == 2 && k[1] == \"*\");\n\t\t\t\t\t\t\t\tif (!found) {\n\t\t\t\t\t\t\t\t\tfor (int j = 1; j < k.Length; j++) { //for each type in filter, including additional options, like [-~ T1 T2 - T3 T4]\n\t\t\t\t\t\t\t\t\t\tvar t = k[j];\n\t\t\t\t\t\t\t\t\t\tif (t[0] is '+' or '-') { opt = t; continue; }\n\t\t\t\t\t\t\t\t\t\t//if (s.Like(u)) { found = true; break; }\n\t\t\t\t\t\t\t\t\t\tif (t[0] == '*') found = s.Ends(t.AsSpan(1..));\n\t\t\t\t\t\t\t\t\t\telse if (t[^1] == '*') found = s.Starts(t.AsSpan(..^1));\n\t\t\t\t\t\t\t\t\t\telse found = s == t;\n\t\t\t\t\t\t\t\t\t\tif (found) break;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (found == (opt[0] == '-')) {\n\t\t\t\t\t\t\t\t\tvar ci = d.items[i];\n\t\t\t\t\t\t\t\t\tif (opt.Eq(1, '~')) ci.moveDown |= CiComplItemMoveDownBy.Name;\n\t\t\t\t\t\t\t\t\telse ci.hidden |= CiComplItemHiddenBy.Always;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (keywordsGroup != null) g.Add((\"keywords\", keywordsGroup));\n\t\t\t\tif (snippetsGroup != null) g.Add((isDot ? \"\" : \"snippets\", snippetsGroup));\n\t\t\t\tif (etcGroup != null) g.Add((\"etc\", etcGroup));\n\t\t\t\tfor (int i = 0; i < g.Count; i++) {\n\t\t\t\t\tforeach (var v in g[i].Item2) d.items[v].group = i;\n\t\t\t\t}\n\t\t\t\tgroupsList = g.Select(o => o.Item1).ToList();\n\t\t\t}\n\t\t\t//p1.Next('g');\n\t\t\t\n\t\t\tif (!span.IsEmpty) _FilterItems(d);\n\t\t\t//p1.Next('F');\n\t\t\t\n\t\t\td.tempRange = doc.ETempRanges_Add(this, span.Start, span.End, () => {\n\t\t\t\t//print.it(\"leave\", _data==d);\n\t\t\t\tif (_data == d) _CancelUI(tempRangeRemoved: true);\n\t\t\t}, position == span.End ? SciCode.TempRangeFlags.LeaveIfPosNotAtEndOfRange : 0);\n\t\t\t\n\t\t\t_data = d;\n\t\t\tif (_popupList == null) {\n\t\t\t\t_popupList = new CiPopupList(this);\n\t\t\t\t_popupList.PopupWindow.Hidden += (_, _) => _CancelUI(popupListHidden: true);\n\t\t\t}\n\t\t\t_popupList.Show(doc, span.Start, _data.items, groupsList, winApi: nativeApiClass != null); //and calls SelectBestMatch\n\t\t}\n\t\tcatch (OperationCanceledException) { /*Debug_.Print(\"canceled\");*/ return; }\n\t\tfinally {\n\t\t\tif (_data == null) {\n\t\t\t\t//p1.Next('z');\n\t\t\t\t//print.it($\"{p1.ToString()}  |  ch='{(ch == default ? \"\" : ch.ToString())}', canceled={cancelTS.IsCancellationRequested}\");\n\t\t\t}\n\t\t\tcancelTS.Dispose();\n\t\t\tif (cancelTS == _cancelTS) _cancelTS = null;\n\t\t}\n\t\t\n\t\tstatic bool _GetFilters(SemanticModel model, out Dictionary<string, string[]> filters) {\n\t\t\t//using var p2 = perf.local(); //~50 mcs\n\t\t\tvar stCurrent = model.SyntaxTree;\n\t\t\tfilters = null;\n\t\t\tforeach (var st in model.Compilation.SyntaxTrees) {\n\t\t\t\t//print.it(st.FilePath);\n\t\t\t\tforeach (var u in st.GetCompilationUnitRoot().Usings) {\n\t\t\t\t\tif (u.GlobalKeyword.RawKind == 0 && st != stCurrent) break;\n\t\t\t\t\tif (u.Alias != null || u.StaticKeyword.RawKind != 0) continue;\n\t\t\t\t\t//print.it(u);\n\t\t\t\t\tvar tt = u.GetTrailingTrivia().FirstOrDefault(o => o.IsKind(SyntaxKind.SingleLineCommentTrivia));\n\t\t\t\t\tif (tt.RawKind != 0) {\n\t\t\t\t\t\tvar text = tt.ToString(); //print.it((object)text==tt.ToString()); //true, fast\n\t\t\t\t\t\tif (text[^1] == ']') {\n\t\t\t\t\t\t\tint i = text.LastIndexOf('[') + 1;\n\t\t\t\t\t\t\tif (i > 0 && text[i] is '+' or '-') {\n\t\t\t\t\t\t\t\tvar a = text[i..^1].Split(' ', StringSplitOptions.RemoveEmptyEntries);\n\t\t\t\t\t\t\t\tfilters ??= new();\n\t\t\t\t\t\t\t\tfilters[u.Name.ToString()] = a;\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tfilters?.Remove(u.Name.ToString());\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn filters != null;\n\t\t}\n\t}\n\t\n\tstatic void _FilterItems(_Data d) {\n\t\tvar filterText = d.filterText;\n\t\tforeach (var v in d.items) {\n\t\t\tv.hidden &= ~CiComplItemHiddenBy.FilterText;\n\t\t\tv.hilite = 0;\n\t\t\tv.moveDown &= ~CiComplItemMoveDownBy.FilterText;\n\t\t}\n\t\tif (!filterText.NE()) {\n\t\t\tstring textLower = filterText.Lower(), textUpper = filterText.Upper();\n\t\t\tchar c0Lower = textLower[0], c0Upper = textUpper[0];\n\t\t\tforeach (var v in d.items) {\n\t\t\t\tif (v.kind == CiItemKind.None) continue; //eg regex completion\n\t\t\t\tvar s = v.FilterText;\n\t\t\t\t//Debug_.PrintIf(v.FilterText != v.Text, $\"{v.FilterText}, {v.Text}\");\n\t\t\t\t//print.it(v.Text, v.FilterText, v.ci.SortText, v.ci.ToString());\n\t\t\t\tbool found = false;\n\t\t\t\tint iFirst = _FilterFindChar(s, 0, c0Lower, c0Upper), iFirstFirst = iFirst;\n\t\t\t\tif (iFirst >= 0) {\n\t\t\t\t\tif (filterText.Length == 1) {\n\t\t\t\t\t\t_HiliteChar(iFirst);\n\t\t\t\t\t} else {\n\t\t\t\t\t\twhile (!s.Eq(iFirst, filterText, true)) {\n\t\t\t\t\t\t\tiFirst = _FilterFindChar(s, iFirst + 1, c0Lower, c0Upper);\n\t\t\t\t\t\t\tif (iFirst < 0) break;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (iFirst >= 0) {\n\t\t\t\t\t\t\t_HiliteSubstring(iFirst);\n\t\t\t\t\t\t} else { //has all uppercase chars? Eg add OneTwoThree if text is \"ott\" or \"ot\" or \"tt\".\n\t\t\t\t\t\t\t_HiliteChar(iFirstFirst);\n\t\t\t\t\t\t\tfor (int i = 1, j = iFirstFirst + 1; i < filterText.Length; i++, j++) {\n\t\t\t\t\t\t\t\tj = _FilterFindChar(s, j, textLower[i], textUpper[i], camel: true);\n\t\t\t\t\t\t\t\tif (j < 0) { found = false; break; }\n\t\t\t\t\t\t\t\t_HiliteChar(j);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvoid _HiliteChar(int i) {\n\t\t\t\t\tfound = true;\n\t\t\t\t\tif (i < 64) v.hilite |= 1UL << i;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvoid _HiliteSubstring(int i) {\n\t\t\t\t\tfound = true;\n\t\t\t\t\tint to = Math.Min(i + filterText.Length, 64);\n\t\t\t\t\twhile (i < to) v.hilite |= 1UL << i++;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (found) {\n\t\t\t\t\tv.hilite <<= v.DisplayTextPrefix.Lenn();\n\t\t\t\t\t\n\t\t\t\t\t//if DisplayText != FilterText, correct or clear hilites. Eg cref.\n\t\t\t\t\tif (s != v.Text) {\n\t\t\t\t\t\tiFirst = v.Text.Find(s);\n\t\t\t\t\t\tif (iFirst < 0) v.hilite = 0; else v.hilite <<= iFirst;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif (filterText.Length > 1 && (iFirst = s.Find(filterText, true)) >= 0) {\n\t\t\t\t\t\tv.moveDown |= CiComplItemMoveDownBy.FilterText; //sort bottom\n\t\t\t\t\t\t_HiliteSubstring(iFirst);\n\t\t\t\t\t} else v.hidden |= CiComplItemHiddenBy.FilterText;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Finds character in s where it is one of: uppercase; lowercase after '_'/'@'; not uppercase/lowercase; any at i=0.\n\t/// </summary>\n\t/// <param name=\"s\"></param>\n\t/// <param name=\"i\">Start index.</param>\n\t/// <param name=\"cLower\">Lowercase version of character to find.</param>\n\t/// <param name=\"cUpper\">Uppercase version of character to find.</param>\n\t/// <param name=\"camel\">Uppercase must not be preceded and followed by uppercase.</param>\n\tstatic int _FilterFindChar(string s, int i, char cLower, char cUpper, bool camel = false) {\n\t\tfor (; i < s.Length; i++) {\n\t\t\tchar c = s[i];\n\t\t\tif (c == cUpper) { //any not lowercase\n\t\t\t\tif (!camel) return i;\n\t\t\t\tif (i == 0 || !char.IsUpper(c)) return i;\n\t\t\t\tif (!char.IsUpper(s[i - 1])) return i;\n\t\t\t\tif (i + 1 < s.Length && char.IsLower(s[i + 1])) return i;\n\t\t\t}\n\t\t\tif (c == cLower) { //lowercase\n\t\t\t\tif (i == 0) return i;\n\t\t\t\tswitch (s[i - 1]) { case '_': case '@': return i; }\n\t\t\t}\n\t\t}\n\t\treturn -1;\n\t}\n\t\n\tpublic void SelectBestMatch(List<CiComplItem> visibleListItems, bool grouped) {\n\t\tCiComplItem ci = null;\n\t\tvar filterText = _data.filterText;\n\t\t\n\t\tbool noSelect = visibleListItems.Count == 0 || filterText == \"_\";\n\t\tif (!noSelect && visibleListItems.Count > 10000 && filterText.Length <= 2) { //make the winapi list faster\n\t\t\tif (filterText.Length < 2) noSelect = true;\n\t\t\telse {\n\t\t\t\tci = visibleListItems.FirstOrDefault(o => o.Text.Starts(filterText));\n\t\t\t\tci ??= visibleListItems.FirstOrDefault(o => o.Text.Starts(filterText, true));\n\t\t\t\tnoSelect = ci != null;\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (!noSelect) {\n\t\t\t\n\t\t\t//rejected. Need FilterItems anyway, eg to select enum type or 'new' type.\n\t\t\t//if(filterText.NE()) {\n\t\t\t//\t_popupList.SelectFirstVisible();\n\t\t\t//\treturn;\n\t\t\t//}\n\t\t\t\n\t\t\tvar visible = visibleListItems.Select(o => o.GetCI()).ToImmutableArray();\n\t\t\tvar fi = _data.completionService.FilterItems(_data.document, visible, filterText);\n\t\t\t//if (filterText.Length > 0) print.it(\"-\", fi);\n\t\t\t//print.it(visible.Length, fi.Length);\n\t\t\tif (!fi.IsDefaultOrEmpty) {\n\t\t\t\tif (fi.Length < visible.Length || filterText.Length > 0 || visible.Length == 1) {\n\t\t\t\t\tvar v = fi[0];\n\t\t\t\t\tif (!v.DisplayTextPrefix.NE() && fi.Length > 1 && fi.FirstOrDefault(o => o.DisplayTextPrefix.NE()) is { } vv) v = vv; //eg \"(nint)\" -> \"Name\"\n\t\t\t\t\tci = v.Attach as CiComplItem;\n\t\t\t\t\t\n\t\t\t\t\t//rejected. Not sure it's better.\n\t\t\t\t\t//For normal priority items should ignore group and select by alphabet.\n\t\t\t\t\t//\tElse often selects eg an Au or System namespace item instead of keyword. Probably it is not what users like.\n\t\t\t\t\t//if (grouped && fi.Length > 1 && filterText.Length > 1 && ci.ci.SortText.Starts(filterText[0])) {\n\t\t\t\t\t//\tfor(int i = 1; i < fi.Length; i++) {\n\t\t\t\t\t//\t\tvar v = fi[i].Attach as CiComplItem;\n\t\t\t\t\t//\t\tif (v.moveDown != 0) break;\n\t\t\t\t\t//\t\tif (v.group == ci.group) continue;\n\t\t\t\t\t//\t\tstring s1 = v.ci.SortText, s2 = ci.ci.SortText;\n\t\t\t\t\t//\t\tif (s1.NE() || s2.NE() || s1[0] != s2[0]) continue;\n\t\t\t\t\t//\t\tif (string.CompareOrdinal(s1, s2) < 0) ci = v;\n\t\t\t\t\t//\t}\n\t\t\t\t\t//\t//foreach(var v in fi) print.it(v, v.Rules.MatchPriority, v.Rules.SelectionBehavior); //all 0, Default\n\t\t\t\t\t//}\n\t\t\t\t}\n\t\t\t} else if (filterText == \"\" && !_data.isDot && !_data.unimported) {\n\t\t\t\t//Workaround for bug in new Roslyn: does not select enum when the target type is enum.\n\t\t\t\t//\tThe same after keyword 'new'.\n\t\t\t\t//\tNever mind: does not prefer the target type when typed eg single letter and the list also contains static fields or props of that type like \"Type.Member\". It never worked.\n\t\t\t\t//\tVS works well in all cases. Maybe it does not use this Roslyn API, or uses different Roslyn version.\n\t\t\t\t//\tFUTURE: test after updating Roslyn, maybe fixed.\n\t\t\t\tforeach (var v in _data.items) {\n\t\t\t\t\t//if (v.ci is var j && j.DisplayText.Starts(\"DEdit\")) print.it(j.DisplayText, j.Flags, j.Span, j.Tags, j.Properties, j.IsPreferredItem());\n\t\t\t\t\t//if (j.ci is var j && j.Properties.ContainsKey(\"Symbols\")) print.it(j.DisplayText, j.Flags, j.Span, j.Tags, j.Properties, j.IsPreferredItem());\n\t\t\t\t\tif (v.kind is CiItemKind.Enum or CiItemKind.Class or CiItemKind.Structure or CiItemKind.Delegate && v.GetCI().Properties.ContainsKey(\"Symbols\")) { //TODO3: unreliable\n\t\t\t\t\t\tci = v;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (_data.noAutoSelect && ci != null) _popupList.SuggestedItem = ci;\n\t\telse _popupList.SelectedItem = ci;\n\t}\n\t//CONSIDER: when typed 1-2 lowercase chars, select keyword instead of type.\n\t//\tNow these types are selected first (but none when typed 3 chars):\n\t/*\n\telm else\n\tfolders for\n\tinputBlocker int\n\tregexp return/ref/readonly/record    //ref/record are rare and before return, but readonly...\n\ttrayIcon true/try\n\tuiimage uint\n\n\tRARE\n\tclipboard class\n\tfilesystem finally/fixed\n\tpathname params\n\tprint / process private/protected\n\n\t*/\n\t\n\tpublic System.Windows.Documents.Section GetDescriptionDoc(CiComplItem ci, int iSelect) {\n\t\tif (_data == null) return null;\n\t\tswitch (ci.Provider) {\n\t\tcase CiComplProvider.Snippet: return CiSnippets.GetDescription(ci);\n\t\tcase CiComplProvider.Winapi: return CiWinapi.GetDescription(ci);\n\t\t}\n\t\tswitch (ci.kind) {\n\t\tcase CiItemKind.Keyword: return CiText.FromKeyword(ci.Text);\n\t\tcase CiItemKind.Label: return CiText.FromLabel(ci.Text);\n\t\t}\n\t\tvar symbols = ci.Symbols;\n\t\tif (symbols != null) return CiText.FromSymbols(symbols, iSelect, _data.model, _data.tempRange.CurrentFrom);\n\t\tif (ci.kind == CiItemKind.Namespace) return null; //extern alias\n\t\tDebug_.PrintIf(!(ci.kind == CiItemKind.None || ci.GetCI().Flags.Has(CompletionItemFlags.Expanded)), ci.kind); //None if Regex\n\t\tvar r = _data.completionService.GetDescriptionAsync(_data.document, ci.GetCI()).Result_(); //fast if Regex, else not tested\n\t\treturn r == null ? null : CiText.FromTaggedParts(r.TaggedParts);\n\t}\n\t\n\t/// <summary>\n\t/// Inserts the replacement text of the completion item.\n\t/// ch == default if clicked or pressed Enter or Tab or a hotkey eg Ctrl+Enter.\n\t/// key == default if clicked or typed a character (except Tab and Enter). Does not include hotkey modifiers.\n\t/// </summary>\n\tCiComplResult _Commit(SciCode doc, CiComplItem item, char ch, KKey key) {\n\t\t//At first hide UI and set _data=null. Else modifying document text may cause bugs.\n\t\tvar data = _data;\n\t\tint currentFrom = data.tempRange.CurrentFrom, currentTo = data.tempRange.CurrentTo;\n\t\t_CancelUI();\n\t\t\n\t\tif (item.Provider == CiComplProvider.EmbeddedLanguage) { //can complete only on click or Tab\n\t\t\tif (ch != default || !(key == default || key == KKey.Tab)) return CiComplResult.None;\n\t\t} else if (ch == ':') { //don't complete if `label:`\n\t\t\tif (data.model.SyntaxTree.FindTokenOrEndToken(currentFrom, default).Parent is IdentifierNameSyntax node) {\n\t\t\t\tif ((node.Parent is ExpressionStatementSyntax ess && ess.Parent is BlockSyntax) || (node.Parent is VariableDeclarationSyntax vds && vds.Parent is LocalDeclarationStatementSyntax)) {\n\t\t\t\t\tif (!(item.Text == \"global\" || (item.FirstSymbol is INamespaceSymbol ns && ns.IsGlobalNamespace))) //eg `global::`\n\t\t\t\t\t\treturn CiComplResult.None;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tbool isSpace; if (isSpace = ch == ' ') ch = default;\n\t\tint codeLenDiff = doc.aaaLen16 - data.codeLength;\n\t\t\n\t\tif (item.Provider == CiComplProvider.Snippet) {\n#if true\n\t\t\tif (ch is not ('\\0' or '(')) return CiComplResult.None;\n\t\t\tCiSnippets.Commit(doc, item, codeLenDiff);\n\t\t\treturn CiComplResult.Complex;\n#else //rejected: support eg identifierSnippet.<new completion list>. Better use type alias.\n\t\t\tif (ch is not ('\\0' or '(' or '.')) return CiComplResult.None;\n\t\t\tvar snippet = CiSnippets.Commit(doc, item, codeLenDiff);\n\t\t\treturn ch != default && snippet != null && snippet.Split('.').All(o => SyntaxFacts.IsValidIdentifier(o)) ? CiComplResult.Simple //support eg identifierSnippet.<new completion list>\n\t\t\t\t: CiComplResult.Complex; //eat the char\n\t\t\t//rejected: if the snippet inserted code like `Type.Method` and completed with space, append `()`. Instead let use snippet `Type.Method($end$)`.\n#endif\n\t\t}\n\t\t\n\t\tstring s, displayTextSuffix = null;\n\t\tint startPos, len;\n\t\tbool isComplex = false;\n\t\tbool ourProvider = item.Provider is CiComplProvider.Winapi;\n\t\t\n\t\tif (ourProvider) {\n\t\t\ts = item.Text;\n\t\t\tstartPos = currentFrom;\n\t\t\tlen = currentTo - startPos;\n\t\t\t//info: item.ci is null\n\t\t} else {\n\t\t\tvar ci = item.GetCI();\n\t\t\tdisplayTextSuffix = ci.DisplayTextSuffix;\n\t\t\tvar change = data.completionService.GetChangeAsync(data.document, ci).Result_();\n\t\t\t//note: don't use the commitCharacter parameter. Some providers, eg XML doc, always set IncludesCommitCharacter=true, even when commitCharacter==null, but may include or not, and may include inside text or at the end.\n\t\t\t\n\t\t\tvar changes = change.TextChanges;\n\t\t\tvar provider = _GetProvider(ci);\n\t\t\tisComplex = changes.Length > 1 || change.NewPosition.HasValue || provider is CiComplProvider.Override or CiComplProvider.XmlDoc;\n\t\t\t\n\t\t\tvar lastChange = changes.Last();\n\t\t\ts = lastChange.NewText;\n\t\t\tif (s.NE()) return CiComplResult.None; //Roslyn bug: fails if there are parameters of type nint. Same in VS. Tried a workaround (modify ci.Properties[\"Symbols\"]), but unsuccessfully. Tried to find/modify the Roslyn code, but it's too deep.\n\t\t\tvar span = lastChange.Span;\n\t\t\tstartPos = span.Start;\n\t\t\tlen = span.Length + codeLenDiff;\n\t\t\tif (isComplex) { //xml doc, override, regex\n\t\t\t\tint newPos = change.NewPosition ?? -1;\n\t\t\t\tif (ch != default && newPos >= 0) return CiComplResult.None;\n\t\t\t\tswitch (provider) {\n\t\t\t\tcase CiComplProvider.Override:\n\t\t\t\t\tnewPos = -1;\n\t\t\t\t\tif (App.Settings.ci_formatTabIndent) {\n\t\t\t\t\t\t//Replace 4 spaces with tab. Make { in same line.\n\t\t\t\t\t\ts = s.Replace(\"    \", \"\\t\").RxReplace(@\"\\R\\t*\\{\", \" {\", 1);\n\t\t\t\t\t\t//Correct indent. \n\t\t\t\t\t\tint indent = s.FindNot(\"\\t\"), indent2 = doc.aaaLineIndentFromPos(true, currentFrom);\n\t\t\t\t\t\tif (indent > indent2) s = s.RxReplace(\"(?m)^\" + new string('\\t', indent - indent2), \"\");\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase CiComplProvider.XmlDoc:\n\t\t\t\t\tif (!s.Ends('>') && s.RxMatch(@\"^<?(\\w+)($| )\", 1, out string tag)) {\n\t\t\t\t\t\tif (CodeInfo.GetDocumentAndFindNode(out _, out var n1, span.Start, findInsideTrivia: true) && null == n1.GetAncestorOrThis<XmlNameAttributeSyntax>()) { //not in <tag attr=\"|\">\n\t\t\t\t\t\t\tstring lt = s.Starts('<') || doc.aaaText.Eq(span.Start - 1, '<') ? \"\" : \"<\";\n\t\t\t\t\t\t\tif (s == tag || (ci.Properties.TryGetValue(\"AfterCaretText\", out var s1) && s1.NE()) && newPos > 0) newPos += 1 + lt.Length;\n\t\t\t\t\t\t\ts = $\"{lt}{s}></{tag}>\";\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tusing var undo = doc.ENewUndoAction();\n\t\t\t\tbool last = true;\n\t\t\t\tfor (int j = changes.Length; --j >= 0; last = false) {\n\t\t\t\t\tvar v = changes[j];\n\t\t\t\t\tif (last) doc.aaaReplaceRange(true, v.Span.Start, v.Span.End + codeLenDiff, s, moveCurrentPos: newPos < 0);\n\t\t\t\t\telse doc.aaaReplaceRange(true, v.Span.Start, v.Span.End, v.NewText);\n\t\t\t\t}\n\t\t\t\tif (newPos >= 0) doc.aaaSelect(true, newPos, newPos, makeVisible: true);\n\t\t\t\t\n\t\t\t\treturn CiComplResult.Complex;\n\t\t\t}\n\t\t}\n\t\tDebug_.PrintIf(startPos != currentFrom && item.Provider != CiComplProvider.EmbeddedLanguage, $\"{currentFrom}, {startPos}\");\n\t\t\n\t\t//if typed space after method or keyword 'if' etc, replace the space with '(' etc. Also add if pressed Tab or Enter.\n\t\tCiAutocorrect.EBrackets bracketsOperation = default;\n\t\tint caretBack = 0, bracketsFrom = 0, bracketsLen = 0;\n\t\t//bool isEnter = key == KKey.Enter;\n\t\tstring sAppend = null;\n\t\t\n\t\tif (s.FindAny(\"({[<\") < 0) {\n\t\t\tif (ch == default) { //completed with Enter, Tab, Space or click\n\t\t\t\tswitch (item.kind) {\n\t\t\t\tcase CiItemKind.Method or CiItemKind.ExtensionMethod:\n\t\t\t\t\tch = '(';\n\t\t\t\t\tbreak;\n\t\t\t\tcase CiItemKind.Keyword:\n\t\t\t\t\tstring name = item.Text;\n\t\t\t\t\tswitch (name) {\n\t\t\t\t\tcase \"for\" or \"foreach\" or \"while\" or \"lock\" or \"catch\":\n\t\t\t\t\tcase \"if\" when !_IsDirective():\n\t\t\t\t\tcase \"fixed\" when _IsStartOfStatement(): //else in struct\n\t\t\t\t\t//case \"using\" when _IsStartOfStatement(): //else directive. But can be with or without `()`.\n\t\t\t\t\tcase \"when\" when _IsInAncestorNode(startPos, n => (n is CatchClauseSyntax, n is SwitchSectionSyntax)): //`catch(...) when`\n\t\t\t\t\t\t(ch, sAppend) = ('(', \" ()\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t//rejected: append ` {  }`. Too many confusing features isn't good. Probably rarely somebody uses or likes it. Also, would need to delete `;`.\n\t\t\t\t\t//case \"try\" or \"finally\":\n\t\t\t\t\t//case \"get\" or \"set\" or \"add\" or \"remove\" or \"do\" or \"unsafe\" or \"checked\" or \"unchecked\" when isEnter:\n\t\t\t\t\t//case \"else\" when isEnter && !_IsDirective():\n\t\t\t\t\t//\tch = '{';\n\t\t\t\t\t//\tbreak;\n\t\t\t\t\tcase \"switch\":\n\t\t\t\t\t\tif (_IsStartOfStatement()) (ch, sAppend) = ('(', \" ()\"); //else ch = '{';\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"nameof\" or \"sizeof\" or \"typeof\":\n\t\t\t\t\tcase \"checked\" or \"unchecked\" when !_IsStartOfStatement():\n\t\t\t\t\t\tch = '(';\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tif (_NeedParentheses()) ch = '(';\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase CiItemKind.Class or CiItemKind.Structure or CiItemKind.Interface or CiItemKind.Enum or CiItemKind.Delegate:\n\t\t\t\t\tif (displayTextSuffix == \"<>\") ch = '<';\n\t\t\t\t\telse if (_NeedParentheses()) ch = '(';\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tbool _IsDirective() => doc.aaaText.Eq(startPos - 1, \"#\"); //info: CompletionItem of 'if' and '#if' are identical. Nevermind: this code does not detect '# if'.\n\t\t\t\t\n\t\t\t\tif (isComplex = ch != default) {\n\t\t\t\t\t//if (ch == '{') {\n\t\t\t\t\t//\tif (isEnter) {\n\t\t\t\t\t//\t\tint indent = doc.aaaLineIndentFromPos(true, startPos);\n\t\t\t\t\t//\t\tvar b = new StringBuilder(\" {\\r\\n\");\n\t\t\t\t\t//\t\tb.AppendIndent(indent + 1);\n\t\t\t\t\t//\t\tb.AppendLine().AppendIndent(indent).Append('}');\n\t\t\t\t\t//\t\tsAppend = b.ToString();\n\t\t\t\t\t//\t\tcaretBack = indent + 3;\n\t\t\t\t\t//\t} else {\n\t\t\t\t\t//\t\tsAppend = \" {  }\";\n\t\t\t\t\t//\t\tcaretBack = 2;\n\t\t\t\t\t//\t}\n\t\t\t\t\t//\tbracketsFrom = startPos + s.Length + 2;\n\t\t\t\t\t//\tbracketsLen = sAppend.Length - 3;\n\t\t\t\t\t//} else\n\t\t\t\t\tif (App.Settings.ci_complParen switch { 0 => isSpace, 1 => true, _ => false } && !data.noAutoSelect && !doc.aaaText.Eq(startPos + len, ch)) { //info: noAutoSelect when lambda argument\n\t\t\t\t\t\tsAppend ??= ch == '(' ? \"()\" : \"<>\";\n\t\t\t\t\t\tcaretBack = 1;\n\t\t\t\t\t\tbracketsFrom = startPos + s.Length + sAppend.Length - 1;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tch = default;\n\t\t\t\t\t\tsAppend = null;\n\t\t\t\t\t\tisComplex = false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (!(ch is '(' or '<' or '[' or '{' || data.noAutoSelect)) { //completed with ;,.?- etc\n\t\t\t\tif (_NeedParentheses()) sAppend = \"()\";\n\t\t\t}\n\t\t\t\n\t\t\tbool _NeedParentheses() {\n\t\t\t\tif (item.kind is CiItemKind.Method or CiItemKind.ExtensionMethod) return true;\n\t\t\t\tif (ch == '.') return false; //if 'new Word.', often can be eg 'new Word.Word()' but rarely 'new Word().'\n\t\t\t\tswitch (item.kind) {\n\t\t\t\tcase CiItemKind.Class or CiItemKind.Structure or CiItemKind.Enum or CiItemKind.Delegate:\n\t\t\t\t//if (ci.Properties.TryGetValue(\"ShouldProvideParenthesisCompletion\", out var v1) && v1.Eqi(\"True\")) goto g1; //missing when eg 'new Namespace.Type'\n\t\t\t\t//break;\n\t\t\t\tcase CiItemKind.Keyword when item.Text is \"string\" or \"object\" or \"int\" or \"uint\" or \"nint\" or \"nuint\" or \"long\" or \"ulong\" or \"byte\" or \"sbyte\" or \"short\" or \"ushort\" or \"char\" or \"bool\" or \"double\" or \"float\" or \"decimal\":\n\t\t\t\t\tif (CodeInfo.GetDocumentAndFindNode(out _, out var node, startPos)) {\n\t\t\t\t\t\tnode = node.Parent;\n\t\t\t\t\t\tif (node is QualifiedNameSyntax) node = node.Parent;\n\t\t\t\t\t\tif (node is ObjectCreationExpressionSyntax) goto g1;\n\t\t\t\t\t\tif (node is AttributeSyntax && item.kind is CiItemKind.Class) return true;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t\tg1:\n\t\t\t\tbracketsOperation = CiAutocorrect.EBrackets.NewExpression;\n\t\t\t\treturn true;\n\t\t\t\t//If 'new Type', adds '()'.\n\t\t\t\t//If then coder types '[' for 'new Type[]' or '{' for 'new Type { initializers }', autocorrection will replace the '()' with '[]' or '{  }'.\n\t\t\t}\n\t\t\t\n\t\t\t//bool _IsGeneric()\n\t\t\t//\t=> ci.Properties.TryGetValue(\"IsGeneric\", out var v1) && v1.Eqi(\"True\");\n\t\t}\n\t\t\n\t\ttry {\n\t\t\tif (sAppend == null && s == data.filterText) return CiComplResult.None;\n\t\t\t\n\t\t\tif (!doc.aaaText.Eq(startPos..(startPos + len), s)) doc.aaaSetAndReplaceSel(true, startPos, startPos + len, s); else doc.aaaCurrentPos16 = startPos + len;\n\t\t\tif (sAppend != null) {\n\t\t\t\tdoc.aaaReplaceSel(sAppend);\n\t\t\t\tif (ch == ';' && caretBack == 0 && doc.aaaCharAt8(doc.aaaCurrentPos8) == ';') { caretBack = -1; isComplex = true; } //skip `;`\n\t\t\t}\n\t\t\tif (caretBack != 0) doc.aaaCurrentPos8 -= caretBack;\n\t\t\tif (bracketsFrom > 0) {\n\t\t\t\tCodeInfo._correct.BracketsAdded(doc, bracketsFrom, bracketsFrom + bracketsLen, bracketsOperation);\n\t\t\t}\n\t\t\t\n\t\t\treturn isComplex ? CiComplResult.Complex : CiComplResult.Simple;\n\t\t}\n\t\tfinally {\n\t\t\tif (ourProvider) {\n\t\t\t\tswitch (item.Provider) {\n\t\t\t\tcase CiComplProvider.Winapi: data.winapi.OnCommitInsertDeclaration(item); break;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (isComplex && ch is '(' or '<') {\n\t\t\t\tCodeInfo._signature.SciCharAdded(doc, ch, methodCompletion: ch == '(' && item.kind is CiItemKind.Method or CiItemKind.ExtensionMethod);\n\t\t\t\tbool methodCompletion = ch == '(' && item.kind is CiItemKind.Method or CiItemKind.ExtensionMethod; //may need to show enum list for the first method parameter, like when typed '('\n\t\t\t\tCodeInfo._signature.SciCharAdded(doc, ch, methodCompletion);\n\t\t\t}\n\t\t}\n\t\t\n\t\t//static bool _IsInAncestorNodeOfType<T>(int pos) where T : SyntaxNode\n\t\t//\t=> CodeInfo.GetDocumentAndFindNode(out _, out var node, pos) && null != node.GetAncestor<T>();\n\t\t\n\t\tstatic bool _IsInAncestorNode(int pos, Func<SyntaxNode, (bool yes, bool no)> f) {\n\t\t\tif (!CodeInfo.GetDocumentAndFindNode(out _, out var node, pos)) return false;\n\t\t\twhile ((node = node.Parent) != null) {\n\t\t\t\t//CiUtil.PrintNode(node);\n\t\t\t\tvar (yes, no) = f(node);\n\t\t\t\tif (yes) return true;\n\t\t\t\tif (no) return false;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\tbool _IsStartOfStatement() {\n\t\t\tif (!CodeInfo.GetDocumentAndFindToken(out _, out var tok, startPos, metaToo: true)) return false;\n\t\t\tfor (var node = tok.Parent; node?.SpanStart == startPos; node = node.Parent) {\n\t\t\t\tif (node is StatementSyntax or IncompleteMemberSyntax { Parent: CompilationUnitSyntax }) {\n\t\t\t\t\t//still can be an expression. Eg if `int j = i sw`, Roslyn assumes `int j = i` is a statement with missing `;`, and `sw` is another statement.\n\t\t\t\t\ttok = tok.GetPreviousToken();\n\t\t\t\t\treturn tok.Kind() is 0 or SyntaxKind.SemicolonToken or SyntaxKind.OpenBraceToken or SyntaxKind.CloseBraceToken or SyntaxKind.ColonToken;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Double-clicked item in list.\n\t/// </summary>\n\tpublic void Commit(SciCode doc, CiComplItem item) => _Commit(doc, item, default, default);\n\t\n\t/// <summary>\n\t/// Tab, Enter, Ctrl/Shift+Enter, Ctrl+;.\n\t/// </summary>\n\tpublic CiComplResult OnCmdKey_Commit(SciCode doc, KKey key) {\n\t\tvar R = CiComplResult.None;\n\t\tif (_data != null) {\n\t\t\tvar ci = _popupList.SelectedItem;\n\t\t\tif (key is KKey.Tab) ci ??= _popupList.SuggestedItem;\n\t\t\tif (ci != null) {\n\t\t\t\tR = _Commit(doc, ci, default, key);\n\t\t\t\tif (R == CiComplResult.None && key is KKey.Tab or KKey.Enter) R = CiComplResult.Simple; //always suppress Tab and Enter\n\t\t\t}\n\t\t\t_CancelUI();\n\t\t}\n\t\treturn R;\n\t}\n\t\n\t/// <summary>\n\t/// Esc, Arrow, Page, Home, End.\n\t/// </summary>\n\tpublic bool OnCmdKey_SelectOrHide(KKey key) => _data != null && _popupList.OnCmdKey(key);\n\t\n\tstatic CiComplProvider _GetProvider(CompletionItem ci) {\n\t\tvar s = ci.ProviderName;\n\t\tDebug_.PrintIf(s == null, \"ProviderName null\");\n\t\tif (s == null) return CiComplProvider.Other;\n\t\tint i = s.LastIndexOf('.') + 1;\n\t\tDebug.Assert(i > 0);\n\t\t//print.it(s[i..]);\n\t\tif (s.Eq(i, \"Symbol\")) return CiComplProvider.Symbol;\n\t\tif (s.Eq(i, \"Keyword\")) return CiComplProvider.Keyword;\n\t\tif (s.Eq(i, \"Cref\")) return CiComplProvider.Cref;\n\t\tif (s.Eq(i, \"XmlDoc\")) return CiComplProvider.XmlDoc;\n\t\tif (s.Find(\"EmbeddedLanguage\", i) >= 0) return CiComplProvider.EmbeddedLanguage;\n\t\tif (s.Eq(i, \"Override\")) return CiComplProvider.Override;\n\t\t//if (s.Eq(i, \"ExternAlias\")) return CiComplProvider.ExternAlias;\n\t\t//if (s.Eq(i, \"ObjectAndWith\")) return CiComplProvider.ObjectAndWithInitializer;\n\t\t//if (s.Eq(i, \"AttributeNamedParameter\")) return CiComplProvider.AttributeNamedParameter; //don't use because can be mixed with other symbols\n\t\treturn CiComplProvider.Other;\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Edit/CiErrors.cs",
    "content": "extern alias CAW;\n\nusing Au.Controls;\nusing System.Collections.Immutable;\n\nusing Microsoft.CodeAnalysis;\nusing CAW::Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp;\nusing Microsoft.CodeAnalysis.CSharp.Syntax;\nusing Microsoft.CodeAnalysis.Text;\nusing Microsoft.CodeAnalysis.Shared.Extensions;\nusing CAW::Microsoft.CodeAnalysis.Shared.Extensions;\nusing Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;\n\nnamespace LA;\n\nclass CiErrors {\n\trecord struct _Diagnostic(Diagnostic d, int start, int end) {\n\t\tpublic ErrorCode Code => (ErrorCode)d.Code;\n\t}\n\t\n\tSemanticModel _semo;\n\tList<_Diagnostic> _codeDiag;\n\treadonly List<StartEndText> _stringErrors = new();\n\treadonly List<StartEndText> _metaErrors = new();\n\tStartEnd _metaRange;\n\t\n\t/// <param name=\"start16\"></param>\n\t/// <param name=\"end16\"></param>\n\t/// <param name=\"pasting\">true on paste, drop or `InsertCode.Statements`</param>\n\t/// <param name=\"pastingSilent\">true if `InsertCode.Statements`</param>\n\tpublic void Indicators(int start16, int end16, bool pasting = false, bool pastingSilent = false) {\n\t\tbool pastedWithUsings = false;\n\t\tgStart:\n\t\t_semo = null;\n\t\t\n\t\tif (!CodeInfo.GetContextAndDocument(out var cd, 0, metaToo: true)) return;\n\t\tvar doc = cd.sci;\n\t\tvar code = cd.code;\n\t\tif (end16 < code.Length && code.Eq(end16 - 1, '\\n')) end16--; //don't include error from next line\n\t\tif (end16 <= start16) return;\n\t\tbool has = false;\n\t\tvar semo = cd.semanticModel;\n\t\t\n\t\tvar aDiag = semo.GetDiagnostics(TextSpan.FromBounds(start16, end16));\n\t\t\n\t\tif (!aDiag.IsDefaultOrEmpty) {\n\t\t\tif (pasting && !pastingSilent && !pastedWithUsings && _PastedWithUsings(cd, aDiag, ref start16, ref end16)) {\n\t\t\t\tpastedWithUsings = true;\n\t\t\t\tgoto gStart;\n\t\t\t}\n\t\t\t\n\t\t\t_codeDiag = new(aDiag.Length);\n\t\t\tforeach (var d_ in aDiag) {\n\t\t\t\tvar d = d_;\n\t\t\t\tif (d.IsSuppressed) continue;\n\t\t\t\tvar loc = d.Location; if (!loc.IsInSource) continue;\n\t\t\t\tvar span = loc.SourceSpan;\n\t\t\t\t//print.it(d.Severity, span, d.Id);\n\t\t\t\tint start = Math.Min(span.Start, code.Length), end = Math.Clamp(span.End, start, code.Length);\n\t\t\t\tif (end == start) {\n\t\t\t\t\tif (end < code.Length && code[end] is not ('\\r' or '\\n')) end++;\n\t\t\t\t\telse if (start > 0) start--;\n\t\t\t\t}\n\t\t\t\tvar ec = (ErrorCode)d.Code;\n\t\t\t\tif (start == 0 && ec == ErrorCode.WRN_UnprocessedXMLComment) continue; //XML comment at start\n\t\t\t\t\n\t\t\t\t//workaround for: when starting to type the last top-level statement, if it's a word (a C# type or non-keyword), error\n\t\t\t\t//\t\"A namespace cannot directly contain members such as fields or methods\". Users would think it's a bug of this program.\n\t\t\t\t//\tAnother workaround would be to add ';' after. But it does not work if it's a C# type eg 'int'.\n\t\t\t\t//\tLet's ignore it in editor. It's a lesser evil. And nobody will compile such unfinished code.\n\t\t\t\t//if (ec == ErrorCode.ERR_NamespaceUnexpected) continue;\n\t\t\t\tif (ec == ErrorCode.ERR_NamespaceUnexpected) {\n\t\t\t\t\t//If unknown name, convert to ERR_NameNotInContext. Then on mouse hover will display tooltip with links to add using directive etc.\n\t\t\t\t\tvar d2 = cd.document.WithText(SourceText.From(code.Insert(end, \";\")));\n\t\t\t\t\tvar m2 = d2.GetSemanticModelAsync().Result_();\n\t\t\t\t\t//print.it(m2.GetDiagnostics(span));\n\t\t\t\t\td = m2.GetDiagnostics(span).FirstOrDefault(o => (ErrorCode)o.Code == ErrorCode.ERR_NameNotInContext);\n\t\t\t\t\tif (d == null) continue;\n\t\t\t\t\tec = ErrorCode.ERR_NameNotInContext;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (!has) doc.EInicatorsDiag_(has = true);\n\t\t\t\tvar indic = d.Severity switch { DiagnosticSeverity.Error => SciTheme.Indic.Error, DiagnosticSeverity.Warning => SciTheme.Indic.Warning, DiagnosticSeverity.Info => SciTheme.Indic.Info, _ => SciTheme.Indic.DiagHidden };\n\t\t\t\tdoc.aaaIndicatorAdd(indic, true, start..end);\n\t\t\t\t_codeDiag.Add(new(d, start, end));\n\t\t\t\t\n\t\t\t\tif (d.Severity == DiagnosticSeverity.Error) {\n\t\t\t\t\tswitch (ec) {\n\t\t\t\t\tcase ErrorCode.ERR_NameNotInContext\n\t\t\t\t\tor ErrorCode.ERR_SingleTypeNameNotFound\n\t\t\t\t\tor ErrorCode.ERR_NoSuchMemberOrExtension\n\t\t\t\t\tor ErrorCode.ERR_NoSuchMemberOrExtensionNeedUsing\n\t\t\t\t\tor ErrorCode.ERR_UnimplementedInterfaceMember\n\t\t\t\t\tor ErrorCode.ERR_UnimplementedAbstractMethod\n\t\t\t\t\tor ErrorCode.ERR_BadBinaryOps:\n\t\t\t\t\t\t_semo = semo;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (_metaErrors.Count > 0) {\n\t\t\tint offs = 0; //!=0 if modified text before metacomments\n\t\t\tif (cd.meta.end - cd.meta.start == _metaRange.end - _metaRange.start) offs = cd.meta.start - _metaRange.start;\n\t\t\t\n\t\t\tforeach (var v in _metaErrors) {\n\t\t\t\tint from = v.start + offs, to = v.end + offs;\n\t\t\t\tif (to <= start16 || from >= end16) continue;\n\t\t\t\tif (!has) doc.EInicatorsDiag_(has = true);\n\t\t\t\tdoc.aaaIndicatorAdd(SciTheme.Indic.Error, true, from..to);\n\t\t\t}\n\t\t}\n\t\t_Strings(semo, cd, start16, end16);\n\t\tif (_stringErrors.Count > 0) {\n\t\t\tif (!has) doc.EInicatorsDiag_(has = true);\n\t\t\tforeach (var v in _stringErrors) {\n\t\t\t\tdoc.aaaIndicatorAdd(SciTheme.Indic.Warning, true, v.start..v.end);\n\t\t\t}\n\t\t}\n\t\tif (!has) {\n\t\t\tdoc.EInicatorsDiag_(false);\n\t\t\t_codeDiag = null;\n\t\t} else if (pasting && _semo != null) {\n\t\t\t//insert missing using directives\n\t\t\tList<_MissingUsingError> aMissingUsing = null;\n\t\t\tforeach (var v in _codeDiag) {\n\t\t\t\tif (_MissingUsingError.IsMissingUsingError(v.Code, out bool extMethod)) {\n\t\t\t\t\tvar mu = new _MissingUsingError(code, v.start, v.end, extMethod, _semo);\n\t\t\t\t\t_MissingUsingError.AddToList(ref aMissingUsing, mu);\n\t\t\t\t}\n\t\t\t}\n\t\t\t//var e1 = _codeDiag.Where(o => o.d.Severity == DiagnosticSeverity.Error); print.it(e1.Count(), e1);\n\t\t\t//print.it(amu.Lenn_(), amu);\n\t\t\t\n\t\t\tList<string> usings = _GetMissingUsings(aMissingUsing);\n\t\t\tif (usings != null) {\n\t\t\t\t//uncheck some usings that are rarely used and cause errors\n\t\t\t\tbool uncheckForms = usings.Count > 1 && usings.Any(o => o is \"System.Windows\" or \"System.Windows.Controls\" or \"System.Windows.Controls.Primitives\" or \"System.Windows.Input\");\n\t\t\t\tbool uncheckDrawing = usings.Count > 1 && usings.Contains(\"System.Windows.Media\");\n\t\t\t\tif (pastingSilent && uncheckForms) usings.Remove(\"System.Windows.Forms\");\n\t\t\t\tif (pastingSilent && uncheckDrawing) usings.Remove(\"System.Drawing\");\n\t\t\t\t\n\t\t\t\tif (!pastingSilent) {\n\t\t\t\t\tvar d = new CheckListDialog(\"Add missing using directives?\");\n\t\t\t\t\tforeach (var u in usings) {\n\t\t\t\t\t\tbool check = true;\n\t\t\t\t\t\tif (uncheckForms && u is \"System.Windows.Forms\") check = false;\n\t\t\t\t\t\tif (uncheckDrawing && u is \"System.Drawing\") check = false;\n\t\t\t\t\t\td.Add(u, check);\n\t\t\t\t\t}\n\t\t\t\t\tif (!d.ShowDialog(doc) || !d.ResultItems.Any()) return;\n\t\t\t\t\tusings = d.ResultItems.ToList();\n\t\t\t\t}\n\t\t\t\tif (usings.Count > 0) InsertCode.UsingDirective(string.Join(';', usings), true);\n\t\t\t\tif (!pastingSilent && usings.Count > 1) print.it(\"Info: multiple using directives have been added. If it causes 'ambiguous reference' errors, remove one of usings displayed in the error tooltip. If that does not work, undo and remove other using.\");\n\t\t\t}\n\t\t}\n\t}\n\t\n\tvoid _Strings(SemanticModel semo, CodeInfo.Context cd, int start16, int end16) {\n\t\t_stringErrors.Clear();\n\t\tvar code = cd.code;\n\t\tArgumentListSyntax keysArgs = null;\n\t\tforeach (var node in semo.Root.DescendantNodes(TextSpan.FromBounds(start16, end16))) {\n\t\t\tvar format = CiUtil.GetParameterStringFormat(node, semo, false, ignoreInterpolatedString: true);\n\t\t\tif (format is PSFormat.None or PSFormat.RegexpReplacement) continue;\n\t\t\tvar tok1 = node.GetFirstToken();\n\t\t\tvar s = tok1.ValueText; //replaced escape sequences\n\t\t\tif (s.Length == 0) continue;\n\t\t\tstring es = null;\n\t\t\ttry {\n\t\t\t\tswitch (format) {\n\t\t\t\tcase PSFormat.NetRegex:\n\t\t\t\t\tnew System.Text.RegularExpressions.Regex(s); //never mind: may have 'options' argument, eg ECMAScript or Compiled\n\t\t\t\t\tbreak;\n\t\t\t\tcase PSFormat.Regexp:\n\t\t\t\t\tnew regexp(s);\n\t\t\t\t\tbreak;\n\t\t\t\tcase PSFormat.Wildex:\n\t\t\t\t\tif (s.Starts(\"***\")) s = s[(s.IndexOf(' ') + 1)..]; //eg wnd.Child(\"***elmName ...\")\n\t\t\t\t\tif (s.Starts(\"**\")) new wildex(s);\n\t\t\t\t\tbreak;\n\t\t\t\tcase PSFormat.Keys:\n\t\t\t\t\tif (s[0] is '!' or '%') break;\n\t\t\t\t\t\n\t\t\t\t\t//if keys.send(\"arg1\", \"arg2\"), use single keys instance to validate all args.\n\t\t\t\t\t//\tElse possible false positives such as if second arg is \")\".\n\t\t\t\t\tvar args = node.GetAncestor<ArgumentListSyntax>();\n\t\t\t\t\tif (args == null || args == keysArgs) break;\n\t\t\t\t\tkeysArgs = args;\n\t\t\t\t\t\n\t\t\t\t\tvar k = new keys(null);\n\t\t\t\t\tforeach (var nk in args.DescendantNodes()) {\n\t\t\t\t\t\tif (nk is ArgumentSyntax) continue;\n\t\t\t\t\t\tif (nk is LiteralExpressionSyntax les && nk.Kind() == SyntaxKind.CharacterLiteralExpression) {\n\t\t\t\t\t\t\tif (les.Token.Value is char c1) k.AddChar(c1);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tif (CiUtil.GetParameterStringFormat(nk, semo, false) != PSFormat.Keys) continue;\n\t\t\t\t\t\t\ts = nk.GetFirstToken().ValueText;\n\t\t\t\t\t\t\tif (s.Length == 0 || s[0] is '!' or '%') continue; //never mind: can be \"keys\"+\"!keys\"\n\t\t\t\t\t\t\ttry { k.AddKeys(s); }\n\t\t\t\t\t\t\tcatch (ArgumentException ex) {\n\t\t\t\t\t\t\t\tvar e = ex.Message;\n\t\t\t\t\t\t\t\t//print.it(e); CiUtil.PrintNode(nk); CiUtil.PrintNode(nk.Parent);\n\t\t\t\t\t\t\t\t//detect some common valid cases like \"keys*\"+x or $\"keys*{x}\"\n\t\t\t\t\t\t\t\tif (((s.Ends('*') || s.Starts('*')) && e.Contains(\"<<<*\")) || (s.Ends('_') && e.Contains(\"<<<_>>>\"))) {\n\t\t\t\t\t\t\t\t\tif (nk.Parent is not ArgumentSyntax) continue; //eg like \"Tab*\"+5 or $\"Tab*{5}\"\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t_AddError(nk, e);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase PSFormat.Hotkey:\n\t\t\t\t\tif (!keys.more.parseHotkeyString(s, out _, out _))\n\t\t\t\t\t\t_AddError(node, \"Invalid hotkey string.\");\n\t\t\t\t\tbreak;\n\t\t\t\tcase PSFormat.HotkeyTrigger:\n\t\t\t\t\tif (!keys.more.parseTriggerString(s, out _, out _, out _, false))\n\t\t\t\t\t\t_AddError(node, \"Invalid hotkey string.\");\n\t\t\t\t\tbreak;\n\t\t\t\tcase PSFormat.TriggerMod:\n\t\t\t\t\tif (!keys.more.parseTriggerString(s, out _, out _, out _, true))\n\t\t\t\t\t\t_AddError(node, \"Invalid modifiers string.\");\n\t\t\t\t\tbreak;\n\t\t\t\tcase PSFormat.CodeFile or PSFormat.FileInWorkspace:\n\t\t\t\t\tif (null == App.Model.Find(s, format == PSFormat.CodeFile ? FNFind.CodeFile : FNFind.Any, silent: true)) {\n\t\t\t\t\t\tvar ae = App.Model.FoundMultiple;\n\t\t\t\t\t\tif (ae != null) {\n\t\t\t\t\t\t\tvar paths = string.Join('\\n', ae.Select(o => o.ItemPath));\n\t\t\t\t\t\t\t_AddError(node, \"Multiple found. Use path, or rename some.\\nPaths:\\n\" + paths);\n\t\t\t\t\t\t\t//never mind: should add links.\n\t\t\t\t\t\t} else _AddError(node, \"Not found.\");\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (ArgumentException ex) { es = ex.Message; }\n\t\t\tif (es != null) _AddError(node, es);\n\t\t\t\n\t\t\tvoid _AddError(SyntaxNode node, string es) {\n\t\t\t\tvar span = node.Span;\n\t\t\t\t_stringErrors.Add(new(span.Start, span.End, es));\n\t\t\t}\n\t\t}\n\t}\n\t\n\tpublic void ClearMetaErrors() => _metaErrors.Clear();\n\t\n\tpublic void AddMetaError(StartEnd metaRange, int from, int to, string s) {\n\t\t_metaRange = metaRange;\n\t\t_metaErrors.Add(new(from, to > from ? to : from + 1, s));\n\t}\n\t\n\tpublic void EraseIndicatorsInLine(SciCode doc, int pos8) {\n\t\tvar (_, start, end) = doc.aaaLineStartEndFromPos(false, pos8, withRN: true);\n\t\tdoc.aaaIndicatorClear(SciTheme.Indic.DiagHidden, false, start..end);\n\t\tdoc.aaaIndicatorClear(SciTheme.Indic.Info, false, start..end);\n\t\tdoc.aaaIndicatorClear(SciTheme.Indic.Warning, false, start..end);\n\t\tdoc.aaaIndicatorClear(SciTheme.Indic.Error, false, start..end);\n\t}\n\t\n\tpublic void SciModified(SciCode doc, in Sci.SCNotification n) {\n\t\t//clear arrays to prevent showing tooltip because positions changed. But don't clear indicators because we'll update them soon.\n\t\t_codeDiag = null;\n\t\t_stringErrors.Clear();\n\t\t\n\t\tif (_pasting.doc != null && n.modificationType.Has(Sci.MOD.SC_MOD_INSERTTEXT | Sci.MOD.SC_PERFORMED_USER)) {\n\t\t\tvar p = _pasting; _pasting = default;\n\t\t\tif (doc == p.doc && n.length > 3) {\n\t\t\t\tint start = doc.aaaPos16(n.position), end = doc.aaaPos16(n.position + n.length);\n\t\t\t\tstring text = doc.aaaText;\n\t\t\t\tdoc.Dispatcher.InvokeAsync(() => { //may need to modify code (can't do it in a scintilla modified notification)\n\t\t\t\t\tif (Panels.Editor.ActiveDoc != doc || doc.aaaText != text) return;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tIndicators(start, end, pasting: true, pastingSilent: p.silent);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (Exception ex) { Debug_.Print(ex); }\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\t(SciCode doc, bool silent) _pasting;\n\t\n\tpublic void Pasting(SciCode doc, bool silent) { _pasting = (doc, silent); }\n\t\n\tpublic System.Windows.Documents.Section GetPopupTextAt(SciCode doc, int pos8, int pos16, out Action<CiPopupText, string> onLinkClick) {\n\t\tonLinkClick = null;\n\t\tif (_codeDiag == null && _metaErrors.Count == 0 && _stringErrors.Count == 0) return null;\n\t\tif (pos8 < 0) return null;\n\t\tint all = doc.Call(Sci.SCI_INDICATORALLONFOR, pos8);\n\t\t//print.it(all);\n\t\tif (0 == (all & ((1 << SciTheme.Indic.Error) | (1 << SciTheme.Indic.Warning) | (1 << SciTheme.Indic.Info) | (1 << SciTheme.Indic.DiagHidden)))) return null;\n\t\t\n\t\tvar x = new CiText();\n\t\tx.StartParagraph();\n\t\t\n\t\tErrorCode ecPrev = 0;\n\t\tint implPos = -1;\n\t\tfor (int i = 0, n = _codeDiag?.Count ?? 0; i < n; i++) {\n\t\t\tvar v = _codeDiag[i];\n\t\t\tif (pos16 < v.start || pos16 > v.end) continue;\n\t\t\tvar d = v.d;\n\t\t\tvar s1 = d.Severity switch { DiagnosticSeverity.Error => \"Error\", DiagnosticSeverity.Warning => \"Warning\", _ => \"Info\" };\n\t\t\tx.LineBreak(s1, notIfFirstInParagraph: true);\n\t\t\tx.Append(\": \" + d.GetMessage());\n\t\t\t\n\t\t\tvar ec = (ErrorCode)d.Code;\n\t\t\tif (d.Severity == DiagnosticSeverity.Error) {\n\t\t\t\t//print.it(ec, d.Id);\n\t\t\t\tif (_semo == null) continue;\n\t\t\t\tif (_MissingUsingError.IsMissingUsingError(ec, out bool extMethod) || ec == ErrorCode.ERR_DottedTypeNameNotFoundInNS /*Namespace.NotFound*/) {\n\t\t\t\t\tif (ec == ecPrev && !extMethod) continue; //probably \"not found 'AbcAttribute'\" followed by \"not found 'Abc'\"\n\t\t\t\t\tecPrev = ec;\n\t\t\t\t\t_UsingsEtc(x, v, doc, extMethod);\n\t\t\t\t} else {\n\t\t\t\t\tswitch (ec) {\n\t\t\t\t\tcase ErrorCode.ERR_UnimplementedInterfaceMember or ErrorCode.ERR_UnimplementedAbstractMethod:\n\t\t\t\t\t\tDebug.Assert(implPos == -1 || implPos == v.start);\n\t\t\t\t\t\timplPos = v.start;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ErrorCode.ERR_BadBinaryOps:\n\t\t\t\t\t\t//New users may not know how to use multiple flags, and intuitively try operator +. Let's add more info.\n\t\t\t\t\t\tif (d.GetMessage().RxIsMatch(@\"^Operator '\\+' cannot be applied to operands of type '(\\w+)' and '\\1'$\")) {\n\t\t\t\t\t\t\tvar n1 = _semo.Root.FindToken(v.start).Parent;\n\t\t\t\t\t\t\tif (_semo.GetTypeInfo(n1).Type?.IsEnumType() ?? false) {\n\t\t\t\t\t\t\t\tx.Append(\". Use operator '|'.\");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (d.Severity == DiagnosticSeverity.Warning) {\n\t\t\t\tx.Append(\"\\nCopy: \");\n\t\t\t\tx.Hyperlink($\"^wi {d.Id}\", d.Id, \", \");\n\t\t\t\tx.Hyperlink($\"^wp {d.Id} {d.Descriptor.Title}\", $\"#pragma warning disable\");\n\t\t\t\tswitch (ec) {\n\t\t\t\tcase ErrorCode.WRN_MissingXMLComment:\n\t\t\t\t\tx.Append(\"\\nTo add XML comment, type /// above.\");\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (implPos >= 0) x.Hyperlink(\"^ii\" + implPos, \"\\nImplement\");\n\t\t\n\t\t_Also(_metaErrors, \"Error: \");\n\t\t_Also(_stringErrors, null);\n\t\tvoid _Also(List<StartEndText> a, string prefix) {\n\t\t\tforeach (var v in a) {\n\t\t\t\tif (pos16 < v.start || pos16 > v.end) continue;\n\t\t\t\tx.LineBreak(prefix, notIfFirstInParagraph: true);\n\t\t\t\tif (v.text.Starts(\"<>\") && v.text.RxFindAll(@\"(<\\+?\\w+ .+?)>(.+?)<>\", out var am)) {\n\t\t\t\t\tint i = 2;\n\t\t\t\t\tforeach (var m in am) {\n\t\t\t\t\t\tif (m.Start > i) x.Append(v.text[i..m.Start]);\n\t\t\t\t\t\tx.Hyperlink(\"^\" + m[1].Value, m[2].Value);\n\t\t\t\t\t\ti = m.End;\n\t\t\t\t\t}\n\t\t\t\t\tif (i < v.text.Length) x.Append(v.text[i..v.text.Length]);\n\t\t\t\t} else {\n\t\t\t\t\tx.Append(v.text);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tx.EndParagraph();\n\t\tonLinkClick = (ph, e) => _LinkClicked(e);\n\t\treturn x.Result;\n\t}\n\t\n\trecord _MissingUsingError {\n\t\tpublic static bool IsMissingUsingError(ErrorCode ec, out bool extMethod) {\n\t\t\textMethod = false;\n\t\t\treturn ec switch {\n\t\t\t\tErrorCode.ERR_NoSuchMemberOrExtension or ErrorCode.ERR_NoSuchMemberOrExtensionNeedUsing => extMethod = true, //these end with (are you missing a using directive...\n\t\t\t\tErrorCode.ERR_NameNotInContext or ErrorCode.ERR_SingleTypeNameNotFound => true,\n\t\t\t\t_ => false,\n\t\t\t};\n\t\t\t//not tested: ERR_GlobalSingleTypeNameNotFound, ERR_DottedTypeNameNotFoundInAgg, ERR_AliasNotFound, ERR_TypeNotFound\n\t\t}\n\t\t\n\t\tpublic static void AddToList(ref List<_MissingUsingError> a, _MissingUsingError mu) {\n\t\t\tif (mu.isEM && mu.emReceiverType == null) return; //unlikely\n\t\t\ta ??= new();\n\t\t\tforeach (var v in a) if (v.name == mu.name && v.emReceiverType == mu.emReceiverType && v.isGeneric == mu.isGeneric && v.isAttribute == mu.isAttribute) return;\n\t\t\ta.Add(mu);\n\t\t}\n\t\t\n\t\tpublic _MissingUsingError(string code, int start, int end, bool extMethod, SemanticModel semo) {\n\t\t\tint end2 = code.IndexOf('<', start, end - start);\n\t\t\tif (end2 < 0) end2 = end; else isGeneric = true;\n\t\t\tname = code[start..end2];\n\t\t\tisAttribute = !extMethod && _IsAttributeNameWithoutSuffix(name, start, semo);\n\t\t\tif (isEM = extMethod) emReceiverType = _GetExtensionMethodReceiverType(semo, start);\n\t\t\tDebug_.PrintIf(extMethod && emReceiverType == null, \"failed to get extension method receiver type\"); //unlikely\n\t\t}\n\t\t\n\t\tpublic readonly string name;\n\t\tpublic readonly bool isEM, isGeneric, isAttribute;\n\t\tpublic readonly ITypeSymbol emReceiverType;\n\t}\n\t\n\tList<string> _GetMissingUsings(List<_MissingUsingError> a) {\n\t\tif (a.NE_()) return null;\n\t\tList<string> usings = null;\n\t\tvar compilation = _semo.Compilation;\n\t\tvar stack = new List<string>();\n\t\tint need = 0; foreach (var v in a) if (v.isEM) need |= 2; else need |= 1;\n\t\t//var p1 = perf.local();\n\t\t//var p1 = new perf.Instance { Incremental = true };\n\t\t_EnumNamespace(compilation.GlobalNamespace);\n\t\t//p1.Write();\n\t\t//p1.NW();\n\t\t\n\t\t//CONSIDER: async, because slow. Or use AssemblyMetadata.CachedSymbols (internal), it's faster except first time.\n\t\tvoid _EnumNamespace(INamespaceSymbol ns) {\n\t\t\tbool found = false;\n\t\t\tforeach (var nt in ns.GetMembers()) {\n\t\t\t\tstring sn = nt.Name;\n\t\t\t\t//print.it(\"<>\" + new string(' ', stack.Count) + (nt is INamespaceSymbol ? \"<c blue>\" + nt.ToString() + \"<>\" : nt.ToString())/*, nt.ContainingAssembly?.Name*/);\n\t\t\t\tif (sn.NE() || sn[0] == '<') continue;\n\t\t\t\tif (nt is INamespaceSymbol ins) {\n\t\t\t\t\tstack.Add(sn);\n\t\t\t\t\t_EnumNamespace(ins);\n\t\t\t\t\tstack.RemoveAt(stack.Count - 1);\n\t\t\t\t} else if (!found) { //else continue to search in nested namespaces\n\t\t\t\t\tvar its = nt as INamedTypeSymbol;\n\t\t\t\t\tif (0 != (need & 1)) {\n\t\t\t\t\t\tforeach (var v in a) {\n\t\t\t\t\t\t\tif (v.isEM) continue;\n\t\t\t\t\t\t\tif (sn != v.name) {\n\t\t\t\t\t\t\t\tif (!(v.isAttribute && sn.Length == v.name.Length + 9 && sn.Starts(v.name) && sn.Ends(\"Attribute\"))) continue;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (v.isGeneric && !its.IsGenericType) continue;\n\t\t\t\t\t\t\tif (_AddNamespace(nt)) goto gNext;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (0 != (need & 2)) {\n\t\t\t\t\t\t//p1.First();\n\t\t\t\t\t\tif (its.IsStatic && its.MightContainExtensionMethods) { //fast, but without IsStatic slow first time\n\t\t\t\t\t\t\tforeach (var m in nt.GetMembers().OfType<IMethodSymbol>()) { //fast; slightly slower than nt.MemberNames.Contains(errName) which gets member types etc too\n\t\t\t\t\t\t\t\tforeach (var v in a) {\n\t\t\t\t\t\t\t\t\tif (!v.isEM) continue;\n\t\t\t\t\t\t\t\t\tif (m.Name == v.name && m.IsExtensionMethod) {\n\t\t\t\t\t\t\t\t\t\tif (null == m.ReduceExtensionMethod(v.emReceiverType)) { /*Debug_.Print(emReceiverType);*/ continue; }\n\t\t\t\t\t\t\t\t\t\tif (_AddNamespace(m)) goto gNext;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t//p1.Next();\n\t\t\t\t\t}\n\t\t\t\t\tgNext:;\n\t\t\t\t\t\n\t\t\t\t\tbool _AddNamespace(ISymbol sym) {\n\t\t\t\t\t\tif (!sym.IsAccessibleWithin(compilation.Assembly)) return false;\n\t\t\t\t\t\t(usings ??= new()).Add(string.Join('.', stack));\n\t\t\t\t\t\treturn found = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn usings;\n\t}\n\t\n\tvoid _UsingsEtc(CiText x, in _Diagnostic v, SciCode doc, bool extMethod) {\n\t\tvar d = v.d;\n\t\tvar ec = (ErrorCode)d.Code;\n\t\tvar mu = new _MissingUsingError(doc.aaaText, v.start, v.end, extMethod, _semo);\n\t\tList<_MissingUsingError> amu = null;\n\t\t_MissingUsingError.AddToList(ref amu, mu);\n\t\tList<string> usings = _GetMissingUsings(amu);\n\t\tif (usings != null) {\n\t\t\tvar sstart = doc.aaaPos8(v.start).ToString();\n\t\t\tx.Append(\"\\nAdd using \");\n\t\t\tfor (int i = 0; i < usings.Count; i++) {\n\t\t\t\tvar u = usings[i];\n\t\t\t\tif (i > 0) x.Append(\" or \");\n\t\t\t\tx.Hyperlink(\"^u\" + sstart + u, u);\n\t\t\t}\n\t\t\tif (!extMethod) {\n\t\t\t\tx.Append(\"\\nOr prefix \");\n\t\t\t\tfor (int i = 0; i < usings.Count; i++) {\n\t\t\t\t\tvar u = usings[i];\n\t\t\t\t\tif (i > 0) x.Append(\" or \");\n\t\t\t\t\tx.Hyperlink(\"^p\" + sstart + u, u);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tx.Hyperlink(\"^r\", \"\\nAdd assembly reference or class file...\");\n\t\t\tx.Hyperlink(\"^n\", \"\\nAdd NuGet package...\");\n\t\t\tif (!(mu.isEM | mu.isGeneric | mu.isAttribute || ec == ErrorCode.ERR_DottedTypeNameNotFoundInNS || _semo.GetEnclosingSymbol(v.start) is null or INamespaceSymbol))\n\t\t\t\tx.Hyperlink(\"^A\" + mu.name, \"\\nFind Windows API...\");\n\t\t}\n\t}\n\t\n\tvoid _LinkClicked(string s) {\n\t\tCodeInfo.HideTextPopup();\n\t\tchar action = s[1];\n\t\tif (action is 'u' or 'p') { //add 'using', prefix namespace\n\t\t\tint pos8 = s.ToInt(2, out int i);\n\t\t\ts = s[i..];\n\t\t\tvar doc = Panels.Editor.ActiveDoc;\n\t\t\tEraseIndicatorsInLine(doc, pos8);\n\t\t\tif (action == 'p') {\n\t\t\t\tdoc.aaaInsertText(false, pos8, s + \".\", addUndoPointAfter: true);\n\t\t\t} else {\n\t\t\t\tInsertCode.UsingDirective(s, true);\n\t\t\t}\n\t\t} else if (action == 'A') { //Windows API\n\t\t\tnew DWinapi(s[2..]).Show();\n\t\t} else if (action == 'r') { //Add reference\n\t\t\tMenus.File.Properties();\n\t\t} else if (action == 'n') { //Add NuGet package\n\t\t\tDNuget.ShowSingle();\n\t\t} else if (action == 'i') { //implement interface or abstract class\n\t\t\tGenerateCode.ImplementInterfaceOrAbstractClass(s.ToInt(3));\n\t\t} else if (action == '<') { //output tag\n\t\t\tint i = s.IndexOf(' ');\n\t\t\tPanels.Output.Scintilla.AaTags.OnLinkClick(s[2..i], s[++i..]);\n\t\t} else if (action == 'w') { //copy warning\n\t\t\tif (s[2] == 'i') s = s[4..]; else if (s.Split(' ', 3) is var a) s = $\"#pragma warning disable {a[1]} //{a[2]}\\r\\n#pragma warning restore {a[1]} //{a[2]}\\r\\n\";\n\t\t\tclipboard.text = s;\n\t\t}\n\t}\n\t\n\tstatic bool _IsAttributeNameWithoutSuffix(string name, int pos, SemanticModel semo) {\n\t\tif (name.Ends(\"Attribute\")) return false;\n\t\treturn semo.SyntaxTree.IsAttributeNameContext(pos, default);\n\t}\n\t\n\tstatic ITypeSymbol _GetExtensionMethodReceiverType(SemanticModel semo, int startOfMethodName) {\n\t\tITypeSymbol t = null;\n\t\tif (semo.SyntaxTree.GetRoot().FindToken(startOfMethodName).Parent.Parent is MemberAccessExpressionSyntax ma)\n\t\t\tt = semo.GetTypeInfo(ma.Expression).Type;\n\t\tDebug_.PrintIf(t == null, \"failed to get extension method receiver type\");\n\t\treturn t;\n\t}\n\t\n\tbool _PastedWithUsings(CodeInfo.Context cd, ImmutableArray<Diagnostic> aDiag, ref int start16, ref int end16) {\n\t\tint firstStart = int.MaxValue, lastEnd = 0;\n\t\tforeach (var d in aDiag) {\n\t\t\tif ((ErrorCode)d.Code != ErrorCode.ERR_UsingAfterElements) continue;\n\t\t\tfirstStart = Math.Min(firstStart, d.Location.SourceSpan.Start);\n\t\t\tlastEnd = Math.Max(lastEnd, d.Location.SourceSpan.End);\n\t\t}\n\t\tif (firstStart < lastEnd) {\n\t\t\tif (cd.syntaxRoot.FindTokenOnLeftOfPosition(lastEnd) is { RawKind: (int)SyntaxKind.SemicolonToken } t1) lastEnd = t1.FullSpan.End;\n\t\t\tint i = InsertCode.FindUsingsInsertPos(cd);\n\t\t\tif (i >= 0 && i <= firstStart) {\n\t\t\t\tvar sUsings = cd.code[firstStart..lastEnd];\n\t\t\t\tif (!sUsings.Ends('\\n')) sUsings += \"\\r\\n\";\n\t\t\t\t\n\t\t\t\t//don't add duplicate usings\n\t\t\t\tvar text2 = cd.code.Remove(firstStart..lastEnd).Insert(i, sUsings);\n\t\t\t\tvar document2 = cd.document.WithText(SourceText.From(text2));\n\t\t\t\tvar semo = document2.GetSemanticModelAsync().Result_();\n\t\t\t\tif (semo.GetDiagnostics(new(i, sUsings.Length)) is { IsDefaultOrEmpty: false } ad2) {\n\t\t\t\t\tStringBuilder b = null; int appended = i;\n\t\t\t\t\tforeach (var d in ad2) {\n\t\t\t\t\t\tif ((ErrorCode)d.Code is ErrorCode.WRN_DuplicateUsing or ErrorCode.HDN_DuplicateWithGlobalUsing) {\n\t\t\t\t\t\t\tb ??= new();\n\t\t\t\t\t\t\tint start = d.Location.SourceSpan.Start, end = d.Location.SourceSpan.End;\n\t\t\t\t\t\t\tif (semo.SyntaxTree.GetRoot().FindToken(start).Parent.GetAncestorOrThis<UsingDirectiveSyntax>() is { } nUsing) {\n\t\t\t\t\t\t\t\tstart = nUsing.SpanStart;\n\t\t\t\t\t\t\t\tend = nUsing.FullSpan.End;\n\t\t\t\t\t\t\t\tb.Append(text2, appended, start - appended);\n\t\t\t\t\t\t\t\tappended = end;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (b != null) {\n\t\t\t\t\t\tb.Append(text2, appended, i + sUsings.Length - appended);\n\t\t\t\t\t\tsUsings = b.ToString();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvar doc = cd.sci;\n\t\t\t\tusing var undo = doc.ENewUndoAction();\n\t\t\t\tdoc.aaaDeleteRange(true, firstStart, lastEnd);\n\t\t\t\tdoc.aaaInsertText(true, i, sUsings);\n\t\t\t\t\n\t\t\t\tstart16 = firstStart + sUsings.Length;\n\t\t\t\tend16 += firstStart - lastEnd+ sUsings.Length;\n\t\t\t\t\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Edit/CiFind.cs",
    "content": "extern alias CAW;\n\nusing System.Collections.Immutable;\nusing System.Windows;\nusing System.Windows.Controls;\n\nusing Microsoft.CodeAnalysis;\nusing CAW::Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp;\nusing Microsoft.CodeAnalysis.CSharp.Syntax;\nusing Microsoft.CodeAnalysis.Text;\nusing Microsoft.CodeAnalysis.CSharp.Extensions;\nusing Microsoft.CodeAnalysis.Shared.Extensions;\nusing CAW::Microsoft.CodeAnalysis.Shared.Extensions;\nusing CAW::Microsoft.CodeAnalysis.FindSymbols;\nusing CAW::Microsoft.CodeAnalysis.Rename;\nusing CAW::Microsoft.CodeAnalysis.Rename.ConflictEngine;\n\nusing Au.Controls;\nusing static Au.Controls.Sci;\n\nnamespace LA;\n\nstatic class CiFind {\n\tconst int c_markerSymbol = 0, c_markerInfo = 1, c_markerSeparator = 2, c_indicProject = 15;\n\tconst int c_markerRenameComment = 3, c_markerRenameDisabled = 4, c_markerRenameString = 5, c_markerRenameError = 6;\n\tconst int c_marginUsage = 1;\n\tconst int c_marginStyleBlack = 50, c_marginStyleWrite = 51, c_marginStyleRead = 52;\n\tstatic bool _working;\n\t\n\tpublic static async void FindReferencesOrImplementations(bool implementations) {\n\t\tif (_working) return;\n\t\t_working = true;\n\t\ttry {\n\t\t\tvar (sym, cd) = CiUtil.GetSymbolFromPos();\n\t\t\tif (sym == null) {\n\t\t\t\tPanels.Found.ClearResults(PanelFound.Found.SymbolReferences);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\tusing var workingState = Panels.Found.Prepare(PanelFound.Found.SymbolReferences, sym.JustName(), out var b);\n\t\t\tvar k = workingState.Scintilla;\n\t\t\tif (workingState.NeedToInitControl) {\n\t\t\t\tk.aaaMarkerDefine(c_markerSymbol, SC_MARK_BACKGROUND, backColor: 0xC0C0FF);\n\t\t\t\tk.aaaMarkerDefine(c_markerInfo, SC_MARK_BACKGROUND, backColor: 0xEEE8AA);\n\t\t\t\tk.aaaMarkerDefine(c_markerSeparator, SC_MARK_UNDERLINE, backColor: 0xe0e0e0);\n\t\t\t\tk.aaaIndicatorDefine(c_indicProject, INDIC_GRADIENT, 0xC0C0C0, alpha: 255, underText: true);\n\t\t\t\tk.aaaMarginSetType(c_marginUsage, SC_MARGIN_TEXT);\n\t\t\t\tk.aaaStyleBackColor(c_marginStyleBlack, 0xE0E0E0);\n\t\t\t\tk.aaaStyleForeColor(c_marginStyleBlack, 0);\n\t\t\t\tk.aaaStyleBackColor(c_marginStyleWrite, 0xE0E0E0);\n\t\t\t\tk.aaaStyleForeColor(c_marginStyleWrite, 0x0000ff);\n\t\t\t\tk.aaaStyleBackColor(c_marginStyleRead, 0xE0E0E0);\n\t\t\t\tk.aaaStyleForeColor(c_marginStyleRead, 0x008000);\n\t\t\t}\n\t\t\tk.aaaMarginSetWidth(c_marginUsage, 0, implementations ? 0 : 7);\n\t\t\t\n\t\t\t//perf.first();\n\t\t\tTestInternal.RefsStart();\n\t\t\tvar (solution, info) = await CiProjects.GetSolutionForFindReferences(sym, cd);\n\t\t\t\n\t\t\t//perf.next('s');\n\t\t\tbool multiProj = solution.ProjectIds.Count > 1;\n\t\t\t_LocationComparer locComp = new();\n\t\t\tvar symComp = new _SymbolComparer(locComp);\n\t\t\tList<(int pos, byte kind, ushort usage)> aUsage = new(); //kind: 1 TypeOrNamespaceUsageInfo, 2 ValueUsageInfo\n\t\t\tif (implementations) {\n\t\t\t\tif (sym is INamedTypeSymbol nts) {\n\t\t\t\t\tif (nts.TypeKind == TypeKind.Interface) {\n\t\t\t\t\t\t_AppendSymbols(\"Implementations\", await SymbolFinder.FindImplementationsAsync(nts, solution));\n\t\t\t\t\t\t_AppendSymbols(\"Derived interfaces\", await SymbolFinder.FindDerivedInterfacesAsync(nts, solution));\n\t\t\t\t\t} else {\n\t\t\t\t\t\t_AppendSymbols(\"Derived classes\", await SymbolFinder.FindDerivedClassesAsync(nts, solution));\n\t\t\t\t\t}\n\t\t\t\t} else if (sym.ContainingType is INamedTypeSymbol nts2) {\n\t\t\t\t\tif (nts2.TypeKind == TypeKind.Interface) {\n\t\t\t\t\t\t_AppendSymbols(\"Implementations\", await SymbolFinder.FindImplementationsAsync(sym, solution));\n\t\t\t\t\t} else {\n\t\t\t\t\t\t_AppendSymbols(\"Overrides\", await SymbolFinder.FindOverridesAsync(sym, solution));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (b.Length == 0) return;\n\t\t\t\t//never mind: Roslyn gives 1 result when implemented in 2 projects and same type name and assembly name.\n\t\t\t\t//\tSame in VS.\n\t\t\t\t//\tWe could generate unique assembly names, but it breaks [InternalsVisibleTo] and testInternal.\n\t\t\t\t//\tToo difficult to modify Roslyn code.\n\t\t\t\t\n\t\t\t\tvoid _AppendSymbols(string header, IEnumerable<ISymbol> e) {\n\t\t\t\t\te = e.Where(o => o.IsInSource());\n\t\t\t\t\tif (e.Any()) {\n\t\t\t\t\t\t//join duplicates added through meta c\n\t\t\t\t\t\tvar implSymbols = e.GroupBy(o => o, (k, e) => (sym: k, locs: e.SelectMany(o => o.Locations).Distinct(locComp)), symComp)\n\t\t\t\t\t\t\t.OrderBy(o => o.sym.Name);\n\t\t\t\t\t\t\n\t\t\t\t\t\t_Fold(true).Marker(c_markerSymbol).Text(header).NL();\n\t\t\t\t\t\tforeach (var v in implSymbols) _AppendSymbol(v.sym, false, v.locs);\n\t\t\t\t\t\t_Fold(false);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tHashSet<_Seen> seen = new();\n\t\t\t\tvar options = FindReferencesSearchOptions.GetFeatureOptionsForStartingSymbol(sym);\n\t\t\t\tvar rr = await SymbolFinder.FindReferencesAsync(sym, solution, options, default);\n\t\t\t\t//perf.next('f');\n\t\t\t\t\n\t\t\t\t//sort. Join duplicate definitions of logically same symbol added through meta c or in a case of partial method.\n\t\t\t\tvar refSymbols = rr.Where(o => o.ShouldShow(options))\n\t\t\t\t\t.GroupBy(o => o.Definition, (defSym, e) => (defSym, defLocs: e.SelectMany(k => k.Definition.Locations), refs: e.SelectMany(k => k.Locations)), symComp)\n\t\t\t\t\t.OrderByDescending(o => o.defSym.Kind)\n\t\t\t\t\t.ThenBy(o => o.defSym.ContainingAssembly?.Name != cd.document.Project.AssemblyName) //let current project be the first\n\t\t\t\t\t.ThenBy(o => o.defSym.ContainingAssembly?.Name);\n\t\t\t\t\n\t\t\t\tbool allInThisDoc = refSymbols.All(o => o.refs.All(oo => oo.Document == cd.document));\n\t\t\t\tif (allInThisDoc) multiProj = false;\n\t\t\t\t\n\t\t\t\tforeach (var (defSym, defLocs, refs) in refSymbols) {\n\t\t\t\t\tif (refs.Any()) {\n\t\t\t\t\t\t//definition\n\t\t\t\t\t\t\n\t\t\t\t\t\t_Fold(true).Marker(c_markerSymbol);\n\t\t\t\t\t\t//usage\n\t\t\t\t\t\tstring defInit = null;\n\t\t\t\t\t\tif (defSym is ILocalSymbol or IParameterSymbol or IFieldSymbol or IPropertySymbol\n\t\t\t\t\t\t\t&& defSym.IsFromSource()\n\t\t\t\t\t\t\t&& defSym.Locations[0].FindNode(default) is SyntaxNode n1) {\n\t\t\t\t\t\t\tEqualsValueClauseSyntax evc = n1 switch { VariableDeclaratorSyntax g => g.Initializer, PropertyDeclarationSyntax g => g.Initializer, ParameterSyntax g => g.Default, _ => null };\n\t\t\t\t\t\t\tif (evc != null) {\n\t\t\t\t\t\t\t\taUsage.Add((b.Length, 2, (ushort)ValueUsageInfo.Write));\n\t\t\t\t\t\t\t\tdefInit = evc.ToString();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\t_AppendSymbol(defSym, true, defLocs.Distinct(locComp), defInit);\n\t\t\t\t\t\tbool isCtor = defSym is IMethodSymbol ms && ms.IsConstructor();\n\t\t\t\t\t\t\n\t\t\t\t\t\t//references\n\t\t\t\t\t\t\n\t\t\t\t\t\tif (multiProj) _Fold(true);\n\t\t\t\t\t\tProjectId prevProjId = null;\n\t\t\t\t\t\tFileNode prevFile = null;\n\t\t\t\t\t\tvar refs2 = refs\n\t\t\t\t\t\t\t.OrderBy(o => o.Document.Project.Id != cd.document.Project.Id) //let current project be the first\n\t\t\t\t\t\t\t.ThenBy(o => o.Document.Project.Name)\n\t\t\t\t\t\t\t.ThenBy(o => o.Document.Name);\n\t\t\t\t\t\tforeach (var rloc in refs2) {\n\t\t\t\t\t\t\tvar f = CiProjects.FileOf(rloc.Document);\n\t\t\t\t\t\t\tvar span = rloc.Location.SourceSpan;\n\t\t\t\t\t\t\tif (!seen.Add(new(f, span.Start))) continue; //remove logical duplicates added because of meta c\n\t\t\t\t\t\t\tif (!rloc.Document.TryGetText(out var st)) { Debug_.Print(f); continue; }\n\t\t\t\t\t\t\tvar text = st.ToString();\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tif (f != prevFile) {\n\t\t\t\t\t\t\t\tif (prevFile != null) b.Marker(c_markerSeparator, prevLine: true);\n\t\t\t\t\t\t\t\tprevFile = f;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tif (multiProj && rloc.Document.Project.Id != prevProjId) {\n\t\t\t\t\t\t\t\tprevProjId = rloc.Document.Project.Id;\n\t\t\t\t\t\t\t\t_Fold(false);\n\t\t\t\t\t\t\t\t_Fold(true);\n\t\t\t\t\t\t\t\tb.Indic(c_indicProject).Text(\"Project \").B(rloc.Document.Project.Name).Indic_().NL();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t//usage\n\t\t\t\t\t\t\tvar sui = rloc.SymbolUsageInfo;\n\t\t\t\t\t\t\tif (sui.TypeOrNamespaceUsageInfoOpt != null) {\n\t\t\t\t\t\t\t\taUsage.Add((b.Length, 1, (ushort)sui.TypeOrNamespaceUsageInfoOpt.Value));\n\t\t\t\t\t\t\t} else if (sui.ValueUsageInfoOpt != null) {\n\t\t\t\t\t\t\t\tif (isCtor && sui.ValueUsageInfoOpt == ValueUsageInfo.Read) //for 'new()' of a ctor Roslyn gives ValueUsageInfo.Read\n\t\t\t\t\t\t\t\t\taUsage.Add((b.Length, 1, (ushort)TypeOrNamespaceUsageInfo.ObjectCreation));\n\t\t\t\t\t\t\t\telse if (!(defSym is IMethodSymbol && sui.ValueUsageInfoOpt is ValueUsageInfo.Read)) //'read' for methods makes no sense\n\t\t\t\t\t\t\t\t\taUsage.Add((b.Length, 2, (ushort)sui.ValueUsageInfoOpt.Value));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tPanelFound.AppendFoundLine(b, f, text, span.Start, span.End, workingState, displayFile: !allInThisDoc, indicHilite: PanelFound.Indicators.HiliteG);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (multiProj) _Fold(false);\n\t\t\t\t\t\t\n\t\t\t\t\t\t_Fold(false);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t//perf.next();\n\t\t\t\n\t\t\tif (!info.NE()) b.Marker(c_markerInfo).Text(info).NL();\n\t\t\t\n\t\t\tvoid _AppendSymbol(ISymbol sym, bool refDef = false, IEnumerable<Location> locations = null, string defInit = null) {\n\t\t\t\tvar sDef = sym is INamespaceSymbol\n\t\t\t\t\t? sym.ToString() /* with ancestor namespaces */\n\t\t\t\t\t: sym.ToDisplayString(SymbolDisplayFormat.CSharpShortErrorMessageFormat); /* no namespaces, no param names, no return type */\n\t\t\t\tbool isInSource = false;\n\t\t\t\tvar symName = sym.JustName();\n\t\t\t\tvar e = (locations ?? sym.Locations)\n\t\t\t\t\t.Where(o => o.IsInSource)\n\t\t\t\t\t.Select(o => (f: CiProjects.FileOf(o.SourceTree, solution), ts: o.SourceSpan))\n\t\t\t\t\t.OrderBy(o => StringUtil.LevenshteinDistance(o.f.DisplayName, symName));\n\t\t\t\tforeach (var (f, ts) in e) {\n\t\t\t\t\tif (isInSource) b.Text(\"   \");\n\t\t\t\t\tb.Link2(new PanelFound.CodeLink(f, ts.Start));\n\t\t\t\t\tif (!isInSource) {\n\t\t\t\t\t\tif (refDef) b.B(sDef); else b.Text(sDef);\n\t\t\t\t\t\tif (defInit != null) b.Text(\" \").Text(defInit.Limit(50).ReplaceLineEndings(\" \"));\n\t\t\t\t\t\tb.Text(\"      \");\n\t\t\t\t\t}\n\t\t\t\t\tb.Gray(f.Name);\n\t\t\t\t\tb.Link_();\n\t\t\t\t\tisInSource = true;\n\t\t\t\t\t//if (!refDef && multiProj) b.Text($\" ({solution.GetDocument(loc.SourceTree)?.Project.Name})\"); //bad if many partials\n\t\t\t\t}\n\t\t\t\tif (!isInSource) { if (refDef) b.B(sDef); else b.Text(sDef); }\n\t\t\t\tb.NL();\n\t\t\t}\n\t\t\t\n\t\t\tSciTextBuilder _Fold(bool start) => b.Fold(new(b.Length - (start ? 0 : 2), start));\n\t\t\t\n\t\t\tPanels.Found.SetResults(workingState, b);\n\t\t\t\n\t\t\t//margin text\n\t\t\tforeach (var v in aUsage) {\n\t\t\t\tstring s = null;\n\t\t\t\tint style = c_marginStyleBlack;\n\t\t\t\tif (v.kind == 1) {\n\t\t\t\t\tvar u = (TypeOrNamespaceUsageInfo)v.usage;\n\t\t\t\t\ts = u switch {\n\t\t\t\t\t\tTypeOrNamespaceUsageInfo.Base => \"base\",\n\t\t\t\t\t\tTypeOrNamespaceUsageInfo.Import => \"using\",\n\t\t\t\t\t\t//TypeOrNamespaceUsageInfo.NamespaceDeclaration => \"ns\",\n\t\t\t\t\t\tTypeOrNamespaceUsageInfo.ObjectCreation => \"new\",\n\t\t\t\t\t\t//TypeOrNamespaceUsageInfo.Qualified => \"A.B\", //weird and distracting\n\t\t\t\t\t\tTypeOrNamespaceUsageInfo.Qualified => \".\",\n\t\t\t\t\t\tTypeOrNamespaceUsageInfo.TypeArgument => \"<T>\",\n\t\t\t\t\t\tTypeOrNamespaceUsageInfo.TypeConstraint => \"where\",\n\t\t\t\t\t\t_ => null\n\t\t\t\t\t};\n\t\t\t\t} else {\n\t\t\t\t\tvar u = (ValueUsageInfo)v.usage;\n\t\t\t\t\ts = u switch {\n\t\t\t\t\t\tValueUsageInfo.Name => \"name\",\n\t\t\t\t\t\tValueUsageInfo.Read or ValueUsageInfo.ReadableReference => \"read\",\n\t\t\t\t\t\tValueUsageInfo.ReadableWritableReference => \"ref\",\n\t\t\t\t\t\tValueUsageInfo.ReadWrite => \"r w\",\n\t\t\t\t\t\tValueUsageInfo.WritableReference => \"out\",\n\t\t\t\t\t\tValueUsageInfo.Write => \"write\",\n\t\t\t\t\t\t_ => null\n\t\t\t\t\t};\n\t\t\t\t\tif (u.Has(ValueUsageInfo.Write)) style = c_marginStyleWrite; else if (u.Has(ValueUsageInfo.Read)) style = c_marginStyleRead;\n\t\t\t\t}\n\t\t\t\tint line = k.aaaLineFromPos(true, v.pos);\n\t\t\t\tk.Call(SCI_MARGINSETSTYLE, line, style);\n\t\t\t\tif (s != null) k.aaaSetString(SCI_MARGINSETTEXT, line, s);\n\t\t\t}\n\t\t\t\n\t\t\ttimer.after(1, _ => GC.Collect());\n\t\t\t//perf.nw();\n\t\t}\n\t\tfinally {\n\t\t\t_working = false;\n\t\t\tTestInternal.RefsEnd();\n\t\t}\n\t}\n\t\n\trecord struct _Seen(object file, int pos);\n\t\n\tclass _LocationComparer : IEqualityComparer<Location> {\n\t\tpublic bool Equals(Location x, Location y)\n\t\t\t=> x.IsInSource && y.IsInSource && x.SourceSpan == y.SourceSpan && x.SourceTree.FilePath == y.SourceTree.FilePath;\n\t\t\n\t\tpublic int GetHashCode(Location x)\n\t\t\t=> x.Kind == LocationKind.SourceFile ? x.SourceSpan.Start : x.GetHashCode();\n\t}\n\t\n\tclass _SymbolComparer : IEqualityComparer<ISymbol> {\n\t\t_LocationComparer _locComp;\n\t\t\n\t\tpublic _SymbolComparer(_LocationComparer locComp) { _locComp = locComp; }\n\t\t\n\t\t//public bool Equals(ISymbol x, ISymbol y) => x.Locations.Intersect(y.Locations, _locComp).Any();\n\t\tpublic bool Equals(ISymbol x, ISymbol y) { //faster, no garbage\n\t\t\tforeach (var xl in x.Locations)\n\t\t\t\tforeach (var yl in y.Locations)\n\t\t\t\t\tif (_locComp.Equals(xl, yl)) return true;\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\tpublic int GetHashCode(ISymbol x) => x.Name.GetHashCode();\n\t}\n\t\n\tpublic static void SciUpdateUI(SciCode doc, bool modified) {\n\t\tif (modified || 0 == doc.aaaIndicatorGetValue(SciTheme.Indic.Refs, doc.aaaCurrentPos8) || doc.aaaHasSelection)\n\t\t\tdoc.aaaIndicatorClear(SciTheme.Indic.Refs);\n\t\tdoc.aaaIndicatorClear(SciTheme.Indic.Braces);\n\t\t_cancelTS?.Cancel();\n\t\t_cancelTS = null;\n\t\t_doc = doc;\n\t\t//_timer1.After(modified ? 1000 : 200);\n\t\tif (!modified) _timer1.After(200);\n\t}\n\t\n\tstatic readonly timer _timer1 = new(_SciUpdateUI);\n\tstatic CancellationTokenSource _cancelTS;\n\tstatic SciCode _doc;\n\t\n\tstatic async void _SciUpdateUI(timer _1) {\n\t\tif (!CodeInfo.GetContextAndDocument(out var cd) || cd.sci != _doc) return;\n\t\tif (cd.sci.aaaHasSelection) return;\n\t\t\n\t\t//hilite symbol references\n\t\tif (CiUtil.GetSymbolFromPos(cd) is ISymbol sym) {\n\t\t\tif (sym is IAliasSymbol a1 && a1.Target is INamespaceSymbol ns1 && ns1.ContainingNamespace == null) return; //'global' keyword. Would hilite entire code.\n\t\t\tList<Range> ar = new();\n\t\t\tvar cancelTS = _cancelTS = new CancellationTokenSource();\n\t\t\tvar cancelToken = cancelTS.Token;\n\t\t\ttry {\n\t\t\t\tvar options = FindReferencesSearchOptions.GetFeatureOptionsForStartingSymbol(sym);\n\t\t\t\t\n\t\t\t\tvar solution = cd.document.Project.Solution;\n\t\t\t\tvar ihs = ImmutableHashSet.Create(cd.document);\n\t\t\t\tvar rr = await SymbolFinder.FindReferencesAsync(sym, solution, (IFindReferencesProgress)null, ihs, options, cancelToken);\n\t\t\t\t\n\t\t\t\tif (Panels.Editor.ActiveDoc != cd.sci) return;\n\t\t\t\t\n\t\t\t\tforeach (var v in rr) {\n\t\t\t\t\tif (!v.ShouldShow(options)) continue;\n\t\t\t\t\t\n\t\t\t\t\t//definition\n\t\t\t\t\tforeach (var loc in v.Definition.Locations) {\n\t\t\t\t\t\tif (loc.SourceTree == cd.syntaxRoot.SyntaxTree) {\n\t\t\t\t\t\t\tar.Add(loc.SourceSpan.ToRange());\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\t//references\n\t\t\t\t\tforeach (var rloc in v.Locations) {\n\t\t\t\t\t\tif (rloc.Document == cd.document) {\n\t\t\t\t\t\t\tvar span = rloc.Location.SourceSpan;\n\t\t\t\t\t\t\tif (span.Length == 0 && span.Start < cd.code.Length) span = new(span.Start, 1); //indexer\n\t\t\t\t\t\t\tar.Add(span.ToRange());\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tforeach (var v in ar) {\n\t\t\t\t\tif (cd.sci.SnippetMode_ != null && 0 != (cd.sci.aaaIndicatorGetAll(v.Start.Value, true) & 1 << SciTheme.Indic.SnippetFieldActive)) continue; //avoid mixed color\n\t\t\t\t\tcd.sci.aaaIndicatorAdd(SciTheme.Indic.Refs, true, v);\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (OperationCanceledException) { return; }\n\t\t\tcatch (Exception e1) { Debug_.Print(e1); } //Roslyn bug: when caret at '!=' in 'if (Sheet != App.ActiveSheet)' (COM). Also once \"Unexpected value 'PointerElementAccess'\".\n\t\t\tfinally {\n\t\t\t\tcancelTS.Dispose();\n\t\t\t\tif (cancelTS == _cancelTS) _cancelTS = null;\n\t\t\t}\n\t\t}\n\t\t//See also: Roslyn -> AbstractDocumentHighlightsService.cs.\n\t\t\n\t\t//CONSIDER: now brace hiliting is distracting and I as a user rarely need it.\n\t\t//\tMaybe hilite only when a single brace selected.\n\t\t//\tBut other IDEs hilite like this.\n\t\t_HighlightMatchingBracesOrDirectives(cd);\n\t}\n\t\n\tstatic void _HighlightMatchingBracesOrDirectives(CodeInfo.Context cd) {\n\t\tstring code = cd.code;\n\t\tint pos = cd.pos;\n\t\tchar chL = pos <= code.Length - 2 ? code[pos] : default, chR;\n\t\tif (chL is '(' or '[' or '{' or '<') {\n\t\t\tchR = chL switch { '(' => ')', '[' => ']', '{' => '}', _ => '>' };\n\t\t\t_Brace(false);\n\t\t} else if (chL == '#' && CiUtil.IsLineStart(code, pos)) {\n\t\t\t_Directive();\n\t\t}\n\t\t\n\t\tchR = --pos >= 0 ? code[pos] : default;\n\t\tif (chR is ')' or ']' or '}' or '>') {\n\t\t\tchL = chR switch { ')' => '(', ']' => '[', '}' => '{', _ => '<' };\n\t\t\t_Brace(true);\n\t\t} else if (chR == '#' && CiUtil.IsLineStart(code, pos)) {\n\t\t\t_Directive();\n\t\t}\n\t\t\n\t\tvoid _Brace(bool isPrevChar) {\n\t\t\tvar (kL, kR) = chL switch {\n\t\t\t\t'(' => (SyntaxKind.OpenParenToken, SyntaxKind.CloseParenToken),\n\t\t\t\t'[' => (SyntaxKind.OpenBracketToken, SyntaxKind.CloseBracketToken),\n\t\t\t\t'{' => (SyntaxKind.OpenBraceToken, SyntaxKind.CloseBraceToken),\n\t\t\t\t_ => (SyntaxKind.LessThanToken, SyntaxKind.GreaterThanToken)\n\t\t\t};\n\t\t\tvar token = cd.syntaxRoot.FindToken(pos);\n\t\t\tvar span = token.Span;\n\t\t\tif (span.Start != pos || span.Length != 1) return;\n\t\t\tif (!token.IsKind(isPrevChar ? kR : kL)) return;\n\t\t\tvar node = token.Parent;\n\t\t\tint posL = -1, posR = -1;\n\t\t\tforeach (var v in node.ChildTokens()) {\n\t\t\t\tif (posL < 0) {\n\t\t\t\t\tif (v.IsKind(kL) && v.Span.Length == 1) posL = v.SpanStart;\n\t\t\t\t} else if (posR < 0) {\n\t\t\t\t\tif (v.IsKind(kR) && v.Span.Length == 1) posR = v.SpanStart;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (posR < 0 || !(posL == pos || posR == pos)) return;\n\t\t\tif (posR - posL < 4) return; //don't hilite () etc\n\t\t\tcd.sci.aaaIndicatorAdd(SciTheme.Indic.Braces, true, posL..(posL + 1));\n\t\t\tcd.sci.aaaIndicatorAdd(SciTheme.Indic.Braces, true, posR..(posR + 1));\n\t\t}\n\t\t\n\t\tvoid _Directive() {\n\t\t\tif (cd.syntaxRoot.FindToken(pos, findInsideTrivia: true).Parent is not DirectiveTriviaSyntax node) return;\n\t\t\tIEnumerable<DirectiveTriviaSyntax> a = null;\n\t\t\tif (node is IfDirectiveTriviaSyntax or ElseDirectiveTriviaSyntax or ElifDirectiveTriviaSyntax or EndIfDirectiveTriviaSyntax) {\n\t\t\t\tvar m = node.GetMatchingConditionalDirectives(default);\n\t\t\t\tif (m.Length > 1) a = m;\n\t\t\t} else {\n\t\t\t\tvar node2 = node.GetMatchingDirective(default);\n\t\t\t\tif (node2 != null) a = [node, node2];\n\t\t\t}\n\t\t\tif (a == null) return;\n\t\t\tforeach (var v in a) cd.sci.aaaIndicatorAdd(SciTheme.Indic.Braces, true, v.HashToken.Span.ToRange());\n\t\t}\n\t}\n\t\n\tpublic static void RenameSymbol() {\n\t\tif (_working) return;\n\t\t_working = true;\n\t\ttry { new _Renamer().Rename(); }\n\t\tfinally { _working = false; }\n\t}\n\t\n\tclass _Renamer {\n\t\tstring _oldName, _newName, _info;\n\t\tbool _overloads, _comments, _disabled, _strings, _preview;\n\t\tList<_File> _a;\n\t\tFileNode _currentFile;\n\t\tKScintilla _sciPreview;\n\t\tstatic WeakReference<KScintilla> s_sciPreview;\n\t\t\n\t\tpublic async void Rename() {\n\t\t\tif (s_sciPreview?.TryGetTarget(out var sciPreview) ?? false) Panels.Found.Close(sciPreview);\n\t\t\ts_sciPreview = null;\n\t\t\t\n\t\t\tif (!CodeInfo.GetContextAndDocument(out var cd)) return;\n\t\t\t_currentFile = cd.sci.EFile;\n\t\t\t\n\t\t\tvar sym = await RenameUtilities.TryGetRenamableSymbolAsync(cd.document, cd.pos, default);\n\t\t\t\n\t\t\tif (sym == null || !sym.IsInSource() || sym.IsImplicitlyDeclared || !sym.Locations[0].FindToken(default).IsKind(SyntaxKind.IdentifierToken)) {\n\t\t\t\tdialog.show(\"This element cannot be renamed\", owner: App.Hmain);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\tif (!_Dialog(sym)) return;\n\t\t\t\n\t\t\t//using var p1 = perf.local();\n\t\t\tLightweightRenameLocations rlocs;\n\t\t\tTestInternal.RefsStart();\n\t\t\ttry {\n\t\t\t\t(var solution, _info) = await CiProjects.GetSolutionForFindReferences(sym, cd);\n\t\t\t\t//p1.Next('s');\n\t\t\t\t\n\t\t\t\tSymbolRenameOptions sro = new(_overloads, _strings, _comments | _disabled);\n\t\t\t\trlocs = await Renamer.FindRenameLocationsAsync(solution, sym, sro, default);\n\t\t\t}\n\t\t\tfinally { TestInternal.RefsEnd(); }\n\t\t\t//p1.Next('f');\n\t\t\t\n\t\t\tDictionary<FileNode, _File> df = new();\n\t\t\tHashSet<_Seen> seen = new();\n\t\t\tforeach (var v in rlocs.Locations) {\n\t\t\t\tvar f = CiProjects.FileOf(v.DocumentId);\n\t\t\t\tvar span = v.Location.SourceSpan;\n\t\t\t\tif (!seen.Add(new(f, span.Start))) continue; //remove logical duplicates added because of meta c\n\t\t\t\t\n\t\t\t\tint marker = 0;\n\t\t\t\tif (v.IsRenameInStringOrComment) {\n\t\t\t\t\tvar trivia = v.Location.SourceTree.GetRoot(default).FindTrivia(v.Location.SourceSpan.Start);\n\t\t\t\t\tmarker = trivia.Kind() switch {\n\t\t\t\t\t\tSyntaxKind.None => c_markerRenameString,\n\t\t\t\t\t\tSyntaxKind.DisabledTextTrivia => c_markerRenameDisabled,\n\t\t\t\t\t\t_ => c_markerRenameComment\n\t\t\t\t\t};\n\t\t\t\t\tif (marker == c_markerRenameComment && !_comments) continue;\n\t\t\t\t\tif (marker == c_markerRenameDisabled && !_disabled) continue;\n\t\t\t\t} else if (v.CandidateReason != CandidateReason.None) {\n\t\t\t\t\tmarker = c_markerRenameError;\n\t\t\t\t}\n\t\t\t\tif (marker > 0) _preview = true;\n\t\t\t\t\n\t\t\t\tif (!df.TryGetValue(f, out var x)) {\n\t\t\t\t\tif (!rlocs.Solution.GetDocument(v.DocumentId).TryGetText(out var st)) { Debug_.Print(f); continue; }\n\t\t\t\t\tdf.Add(f, x = new(f, st.ToString(), new()));\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tx.a.Add(new(span.Start, span.End, _newName, marker));\n\t\t\t}\n\t\t\t\n\t\t\t//Resolve conflicts.\n\t\t\t//\tWe use Renamer.FindRenameLocationsAsync (above) and ResolveConflictsAsync (below).\n\t\t\t//\tBad: ResolveConflictsAsync makes 4 times slower. Optional if don't need conflict resolution.\n\t\t\t//Could instead use Renamer.RenameSymbolAsync. Simpler, and same speed. Problems:\n\t\t\t//\t- Fails to rename some, eg tuple fields. Now fails only to resolve conflicts (that is why the try/catch).\n\t\t\t//\t- Skips #if-disabled code.\n\t\t\t//\t- Joins some changes, eg in doc comments. Then a change can span many lines. How to display it?\n\t\t\ttry {\n\t\t\t\tvar res = await rlocs.ResolveConflictsAsync(sym, _newName, default);\n\t\t\t\tif (res.IsSuccessful && res.RelatedLocations.Any(o => o.Type.HasAny(RelatedLocationType.UnresolvedConflict))) {\n\t\t\t\t\tif (!dialog.showOkCancel(\"Unresolved conflict\", \"Rename anyway?\", owner: App.Hmain)) return;\n\t\t\t\t\t//But does not find eg variable name conflicts in top-level statements.\n\t\t\t\t\t//In some places false positive unresolved conflict. Same in VS. Therefore don't show text like \"This name already exists\".\n\t\t\t\t}\n\t\t\t\tif (res.IsSuccessful && res.RelatedLocations.Any(o => o.Type is RelatedLocationType.ResolvedNonReferenceConflict or RelatedLocationType.ResolvedReferenceConflict)) {\n\t\t\t\t\tvar drloc = rlocs.Locations.ToDictionary(o => o.Location.SourceSpan.Start);\n\t\t\t\t\tforeach (var did in res.DocumentIds) {\n\t\t\t\t\t\tvar drels = res.GetRelatedLocationsForDocument(did);\n\t\t\t\t\t\tif (!drels.Any(o => o.Type is RelatedLocationType.ResolvedNonReferenceConflict or RelatedLocationType.ResolvedReferenceConflict)) continue;\n\t\t\t\t\t\tvar f = CiProjects.FileOf(did);\n\t\t\t\t\t\tvar doc1 = res.OldSolution.GetDocument(did);\n\t\t\t\t\t\tvar doc2 = res.NewSolution.GetDocument(did);\n\t\t\t\t\t\tforeach (var tc in await doc2.GetTextChangesAsync(doc1)) {\n\t\t\t\t\t\t\tif (tc.Span.Length == _oldName.Length && tc.NewText == _newName) continue; //not a conflict\n\t\t\t\t\t\t\tif (!seen.Add(new(did, tc.Span.End))) continue; //remove logical duplicates added because of meta c\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tif (!df.TryGetValue(f, out var x)) {\n\t\t\t\t\t\t\t\tif (!doc1.TryGetText(out var st)) { Debug_.Print(f); continue; }\n\t\t\t\t\t\t\t\tdf.Add(f, x = new(f, st.ToString(), new()));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t//Roslyn may add indent, and it is always spaces. Trim it.\n\t\t\t\t\t\t\tvar s = tc.NewText.TrimStart();\n\t\t\t\t\t\t\tint start = tc.Span.Start, end = tc.Span.End;\n\t\t\t\t\t\t\twhile (start < end && x.text[start] is '\\t' or ' ') start++;\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t//remove the overlapping change added before. Eg remove 'NewName' if this is 'PrependedNamespace.NewName'.\n\t\t\t\t\t\t\tif (end > start) x.a.RemoveAll(o => o.start < end && o.end > start);\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tx.a.Add(new(start, end, s, 0));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception e1) { Debug_.PrintIf(sym.Kind != SymbolKind.Field, e1); }\n\t\t\t\n\t\t\t_a = df.Values.OrderBy(o => o.f.Name).ToList();\n\t\t\tforeach (var v in _a) v.a.Sort((x, y) => x.end - y.end);\n\t\t\t//p1.Next();\n\t\t\t\n\t\t\tif (_preview) _Preview();\n\t\t\telse _Finish();\n\t\t}\n\t\t\n\t\tbool _Dialog(ISymbol sym) {\n\t\t\t_oldName = sym.JustName();\n\t\t\tbool hasOverloads = CAW::Microsoft.CodeAnalysis.Rename.RenameUtilities.GetOverloadedSymbols(sym).Any();\n\t\t\t\n\t\t\tvar b = new wpfBuilder(\"Rename symbol\").Width(300..);\n\t\t\tb.WinProperties(WindowStartupLocation.CenterOwner, showInTaskbar: false);\n\t\t\tb.R.Add(out KTextBox newName, _oldName).Font(\"Consolas\").Focus()\n\t\t\t\t.Validation(_ => SyntaxFacts.IsValidIdentifier(newName.Text.TrimStart('@')) ? null : \"Invalid name\");\n\t\t\tnewName.SelectAll();\n\t\t\tb.R.Add(out KCheckBox cOverloads, \"Include overloads\").Hidden(!hasOverloads).Checked(0 != (App.Settings.ci_rename & 1));\n\t\t\tb.R.Add(out KCheckBox cComments, \"Include comments\").Checked(0 != (App.Settings.ci_rename & 2));\n\t\t\tb.R.Add(out KCheckBox cDisabled, \"Include #if-disabled code\").Checked(0 != (App.Settings.ci_rename & 4));\n\t\t\tb.R.Add(out KCheckBox cStrings, \"Include strings\").Checked(0 != (App.Settings.ci_rename & 8));\n\t\t\t//b.R.Add(out KCheckBox cPreview, \"Preview\");\n\t\t\tb.R.AddOkCancel();\n\t\t\tb.End();\n\t\t\tif (!b.ShowDialog(App.Wmain)) return false;\n\t\t\t_overloads = cOverloads.IsChecked;\n\t\t\t_comments = cComments.IsChecked;\n\t\t\t_disabled = cDisabled.IsChecked;\n\t\t\t_strings = cStrings.IsChecked;\n\t\t\t//_preview = cPreview.IsChecked;\n\t\t\tApp.Settings.ci_rename = (_overloads ? 1 : 0) | (_comments ? 2 : 0) | (_disabled ? 4 : 0) | (_strings ? 8 : 0);\n\t\t\t_newName = newName.Text;\n\t\t\treturn _newName != _oldName;\n\t\t}\n\t\t\n\t\tvoid _Preview() {\n\t\t\tusing var workingState = Panels.Found.Prepare(PanelFound.Found.SymbolRename, \"Renaming\", out var b);\n\t\t\tif (workingState.NeedToInitControl) {\n\t\t\t\tvar k = workingState.Scintilla;\n\t\t\t\tk.aaaMarkerDefine(c_markerInfo, SC_MARK_BACKGROUND, backColor: 0xEEE8AA);\n\t\t\t\tk.aaaMarkerDefine(c_markerSeparator, SC_MARK_UNDERLINE, backColor: 0xe0e0e0);\n\t\t\t\tk.aaaMarginSetWidth(1, 12);\n\t\t\t\tk.aaaMarkerDefine(c_markerRenameComment, SC_MARK_SMALLRECT, backColor: 0x80C000);\n\t\t\t\tk.aaaMarkerDefine(c_markerRenameDisabled, SC_MARK_SMALLRECT, backColor: 0);\n\t\t\t\tk.aaaMarkerDefine(c_markerRenameString, SC_MARK_SMALLRECT, backColor: 0xC09060);\n\t\t\t\tk.aaaMarkerDefine(c_markerRenameError, SC_MARK_SMALLRECT, backColor: 0xFF0000);\n\t\t\t}\n\t\t\tb.Marker(c_markerInfo).Text(\"You may want to exclude some of these. Right-click.\\r\\n\");\n\t\t\t\n\t\t\tFileNode prevFile = null;\n\t\t\tforeach (var (f, text, a) in _a) {\n\t\t\t\tforeach (var v in a) {\n\t\t\t\t\t//if (v.marker > 0) b.Marker(v.marker);\n\t\t\t\t\tif (v.marker == 0) continue;\n\t\t\t\t\tb.Marker(v.marker);\n\t\t\t\t\tif (f != prevFile) { if (prevFile != null) b.Marker(c_markerSeparator, prevLine: true); prevFile = f; }\n\t\t\t\t\tPanelFound.AppendFoundLine(b, f, text, v.start, v.end, workingState, displayFile: true, indicHilite: PanelFound.Indicators.HiliteB);\n\t\t\t\t\t//rejected: in results display newName (hilited) as if already replaced. Or old red and new green, like in VSCode.\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tb.Marker(c_markerInfo)\n\t\t\t\t.B().Link(() => _Link(false), \"Rename\").B_()\n\t\t\t\t.Text(\"  \").Link(() => _Link(true), \"Cancel\").NL();\n\t\t\tb.Marker(c_markerInfo).Text(\"Markers: green - comment, black - #if, brown - string, red - error or ambiguous.\");\n\t\t\tif (!_info.NE()) b.NL().Marker(c_markerInfo).Text(_info);\n\t\t\t\n\t\t\tPanels.Found.SetResults(workingState, b);\n\t\t\ts_sciPreview = new(_sciPreview = workingState.Scintilla);\n\t\t}\n\t\t\n\t\tvoid _Link(bool cancel) {\n\t\t\t//async because cannot close Scintilla from link click notification\n\t\t\tApp.Dispatcher.InvokeAsync(() => {\n\t\t\t\tif (!cancel) _Finish();\n\t\t\t\tPanels.Found.Close(_sciPreview);\n\t\t\t});\n\t\t\ts_sciPreview = null;\n\t\t}\n\t\t\n\t\tvoid _Finish() {\n\t\t\tforeach (var (f, text, _) in _a) {\n\t\t\t\tif (f.GetCurrentText(out var t1, null) && t1 != text) {\n\t\t\t\t\tdialog.show(null, $\"Cannot rename symbol, because '{f.Name}' text changed in the meantime.\", owner: App.Hmain);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tif (_sciPreview != null) { //remove excluded items\n\t\t\t\tint line = 1;\n\t\t\t\tfor (int j = 0; j < _a.Count; j++) {\n\t\t\t\t\tvar a = _a[j].a;\n\t\t\t\t\tfor (int i = 0; i < a.Count; i++) {\n\t\t\t\t\t\tif (a[i].marker == 0) continue;\n\t\t\t\t\t\tif (0 != _sciPreview.aaaIndicatorGetValue(PanelFound.Indicators.Excluded, _sciPreview.aaaLineStart(false, line++)))\n\t\t\t\t\t\t\ta.RemoveAt(i--);\n\t\t\t\t\t}\n\t\t\t\t\tif (a.Count == 0) _a.RemoveAt(j--);\n\t\t\t\t}\n\t\t\t\tif (_a.Count == 0) return;\n\t\t\t}\n\t\t\t\n\t\t\t//var progress = App.Hmain.TaskbarButton;\n\t\t\tvar undoInFiles = SciUndo.OfWorkspace;\n\t\t\ttry {\n\t\t\t\tundoInFiles.StartReplaceInFiles();\n\t\t\t\tforeach (var (f, text, a1) in _a) {\n\t\t\t\t\tvar a = a1.Select(o => new StartEndText(o.start, o.end, o.text)).ToList();\n\t\t\t\t\tif (f.ReplaceAllInText(text, a, out var text2)) {\n\t\t\t\t\t\tif (f.OpenDoc != null) undoInFiles.RifAddFile(f.OpenDoc, text, text2, a);\n\t\t\t\t\t\telse undoInFiles.RifAddFile(f, text, text2, a);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tundoInFiles.FinishReplaceInFiles($\"rename symbol {_oldName} to {_newName}\");\n\t\t\t\t//progress.SetProgressState(WTBProgressState.NoProgress);\n\t\t\t\tCodeInfo.FilesChanged();\n\t\t\t}\n\t\t}\n\t\t\n\t\trecord struct _Change(int start, int end, string text, int marker);\n\t\trecord struct _File(FileNode f, string text, List<_Change> a);\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Edit/CiFindGo.cs",
    "content": "extern alias CAW;\n\nusing System.Collections.Immutable;\nusing System.Windows;\nusing System.Windows.Controls;\n\nusing Microsoft.CodeAnalysis;\nusing CAW::Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp;\nusing Microsoft.CodeAnalysis.CSharp.Syntax;\nusing Microsoft.CodeAnalysis.Text;\nusing Microsoft.CodeAnalysis.CSharp.Extensions;\nusing Microsoft.CodeAnalysis.Shared.Extensions;\nusing CAW::Microsoft.CodeAnalysis.Shared.Extensions;\nusing CAW::Microsoft.CodeAnalysis.FindSymbols;\nusing CAW::Microsoft.CodeAnalysis.PatternMatching;\nusing CAW::Microsoft.CodeAnalysis.Collections;\n\nusing Au.Controls;\nusing System.Windows.Input;\n\nnamespace LA;\n\nclass CiFindGo : KDialogWindow {\n\tKTextBox _tQuery;\n\tKCheckBox _cFuzzy, _cKeepOpen;\n\tKTreeView _tv;\n\tCancellationTokenSource _cancelTS;\n\t\n\tpublic static void ShowSingle() {\n\t\tif (Panels.Editor.ActiveDoc?.EFile.IsCodeFile != true) return;\n\t\tShowSingle(() => new CiFindGo());\n\t}\n\t\n\tCiFindGo() {\n\t\tInitWinProp(\"Find symbol\", App.Wmain);\n\t\tvar b = new wpfBuilder(this).WinSize(600, 600).Columns(-1, 0, 0, 0);\n\t\t\n\t\t_timer1 = new(_ => _Update());\n\t\t\n\t\tb.R.Add(out _tQuery, s_lastQuery).Font(\"Consolas\").Align(y: VerticalAlignment.Center).Focus()\n\t\t\t.Tooltip(\"\"\"\nSymbol name.\nCan be part, or part1 part2, or Type.Member, or camel.\nIf fuzzy, must be full name, 1 word.\nFilters: t Type, m Member, n Namespace.\n\"\"\");\n\t\t_tQuery.TextChanged += (_, _) => _timer1.After(200);\n\t\t\n\t\tb.Add(out _cFuzzy, \"Fuzzy\");\n\t\t_cFuzzy.CheckChanged += (_, _) => _timer1.After(200);\n\t\t\n\t\tb.xAddButtonIcon(\"*EvaIcons.Options2\" + EdIcons.green, _Options, \"Tool settings\");\n\t\tb.xAddCheckIcon(out _cKeepOpen, \"*MaterialLight.Pin\" + EdIcons.black, \"Keep this window open\");\n\t\t\n\t\tb.Row(-1).Add(out _tv);\n\t\t_tv.CustomDraw = new _TvDraw();\n\t\t_tv.CustomItemHeightAddPercent = 100;\n\t\t_tv.ItemMarginLeft = 8;\n\t\t_tv.HotTrack = true;\n\t\t_tv.SingleClickActivate = !App.Settings.ci_findgoDclick;\n\t\t_tv.ItemActivated += e => {\n\t\t\tif (!_cKeepOpen.IsChecked) Close();\n\t\t\t(e.Item as _TvItem).Clicked();\n\t\t};\n\t\t\n\t\tb.End();\n\t\t\n\t\tif (!s_lastQuery.NE()) {\n\t\t\t_tQuery.SelectAll();\n\t\t\t_timer1.After(1);\n\t\t}\n\t\t\n\t\tb.WinSaved(App.Settings.wndpos.symbol, o => App.Settings.wndpos.symbol = o);\n\t}\n\t\n\ttimer _timer1;\n\tstatic string s_lastQuery;\n\t\n\tasync void _Update() {\n\t\t_cancelTS?.Cancel(); //it seems the API always runs in this thread, but anyway\n\t\t\n\t\tvar query = _tQuery.Text.Trim();\n\t\tif (query.Length < 2) {\n\t\t\t_tv.SetItems(null);\n\t\t\treturn;\n\t\t}\n\t\ts_lastQuery = query;\n\t\t\n\t\tif (!CodeInfo.GetContextAndDocument(out var cd, metaToo: true)) return;\n\t\t\n\t\tvar a = new List<_TvItem>();\n\t\t\n\t\tvar filter = SymbolFilter.All;\n\t\tif (query.Length > 2 && query[1] is ' ' or ':' && query[0] is 't' or 'm' or 'n') {\n\t\t\tfilter = query[0] switch { 't' => SymbolFilter.Type, 'm' => SymbolFilter.Member, _ => SymbolFilter.Namespace };\n\t\t\tquery = query[2..].TrimStart();\n\t\t}\n\t\t\n\t\tbool fuzzy = _cFuzzy.IsChecked;\n\t\t\n\t\tvar cancelTS = _cancelTS = new CancellationTokenSource();\n\t\tvar cancelToken = cancelTS.Token;\n\t\t\n\t\tIEnumerable<ISymbol> e;\n\t\tvar solution = cd.document.Project.Solution;\n\t\ttry {\n\t\t\tif (fuzzy) {\n\t\t\t\tusing var sq = SearchQuery.CreateFuzzy(query);\n\t\t\t\te = await SymbolFinder.FindSourceDeclarationsWithCustomQueryAsync(solution, sq, filter, cancelToken);\n\t\t\t} else {\n\t\t\t\te = await SymbolFinder.FindSourceDeclarationsWithPatternAsync(solution, query, filter, cancelToken);\n\t\t\t}\n\t\t\t//BAD: skips ctors. Same in VSCode, but in VS ok.\n\t\t}\n\t\tcatch (OperationCanceledException) { return; }\n\t\tfinally {\n\t\t\tcancelTS.Dispose();\n\t\t\tif (cancelTS == _cancelTS) _cancelTS = null;\n\t\t}\n\t\t\n\t\tif (!e.Any()) {\n\t\t\t_tv.SetItems(null);\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tvar qname = PatternMatcher.GetNameAndContainer(query).name;\n\t\tusing var matcher = PatternMatcher.CreatePatternMatcher(qname, includeMatchedSpans: true, allowFuzzyMatching: fuzzy);\n\t\tusing var ta = TemporaryArray<PatternMatch>.Empty;\n\t\tvar b1 = new StringBuilder();\n\t\t\n\t\tforeach (var sym in e) {\n\t\t\tstring name = sym.JustName(), text;\n\t\t\tint nameStart = 0;\n\t\t\tif (sym is INamespaceSymbol) {\n\t\t\t\ttext = sym.ToString(); /* with ancestor namespaces */\n\t\t\t\tnameStart = text.Length - sym.Name.Length;\n\t\t\t} else {\n\t\t\t\ttext = sym.ToDisplayString(s_symbolFormat);\n\t\t\t\tif (name.Length < text.Length) {\n\t\t\t\t\tnameStart = text.FindWord(name, isWordChar: c => SyntaxFacts.IsIdentifierPartCharacter(c));\n\t\t\t\t\tDebug_.PrintIf(nameStart < 0, $\"{name}, {text}\");\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tImmutableArray<PatternMatch> matches = default;\n\t\t\tif (nameStart >= 0) {\n\t\t\t\tif (matcher.AddMatches(name, ref ta.AsRef())) {\n\t\t\t\t\tmatches = ta.ToImmutableAndClear();\n\t\t\t\t} else ta.Clear();\n\t\t\t}\n\t\t\t\n\t\t\tstring type = sym is INamespaceOrTypeSymbol ? null : sym.ContainingType?.ToDisplayString(s_symbolFormat);\n\t\t\t\n\t\t\tforeach (var loc in sym.Locations) {\n\t\t\t\tvar st = loc.SourceTree;\n\t\t\t\tif (st == null) continue;\n\t\t\t\tstring path = st.FilePath;\n\t\t\t\tstring text2 = type != null ? $\"in {type},  {path}\" : path;\n\t\t\t\tvar fn = CiProjects.FileOf(loc.SourceTree, solution);\n\t\t\t\t\n\t\t\t\ta.Add(new(sym, text, text2, matches, nameStart, fn, loc.SourceSpan.Start));\n\t\t\t}\n\t\t}\n\t\t\n\t\ta.Sort((x, y) => x.CompareTo(y));\n\t\t\n\t\t_tv.SetItems(a);\n\t\t_tv.Select(0, focus: true);\n\t}\n\t\n\tclass _TvItem : ITreeViewItem {\n\t\tstring _text, _text2, _kindImage, _accessImage;\n\t\tImmutableArray<PatternMatch> _matches;\n\t\tint _nameStart, _pos;\n\t\tFileNode _f;\n\t\t\n\t\tpublic _TvItem(ISymbol sym, string text, string text2, ImmutableArray<PatternMatch> matches, int nameStart, FileNode f, int pos) {\n\t\t\t_text = text;\n\t\t\t_text2 = text2;\n\t\t\t_matches = matches;\n\t\t\t_nameStart = nameStart;\n\t\t\t_f = f;\n\t\t\t_pos = pos;\n\t\t\t(_kindImage, _accessImage) = sym.ImageResource();\n\t\t}\n\t\t\n\t\t#region ITreeViewItem\n\t\t\n\t\tobject ITreeViewItem.Image => _kindImage;\n\t\t\n\t\tstring ITreeViewItem.DisplayText => _text + \"\\n\" + _text2; //for tooltip only\n\t\t\n\t\tint ITreeViewItem.MesureTextWidth(GdiTextRenderer tr) => Math.Max(tr.MeasureText(_text).width, tr.MeasureText(_text2).width);\n\t\t\n\t\tint ITreeViewItem.SelectedColor(TVColorInfo ci) => ci.isSelected ? 0xD5E1FF : -1;\n\t\t\n\t\tint ITreeViewItem.BorderColor(TVColorInfo ci) => ci.isSelected ? 0x91B1FF : -1;\n\t\t\n\t\t#endregion\n\t\t\n\t\tpublic void DrawText(TVDrawInfo d, GdiTextRenderer tr) {\n\t\t\tint y = d.yText;\n\t\t\ttr.MoveTo(d.xText, y);\n\t\t\t\n\t\t\tbool dark = d.colorInfo.isHighContrastDark && !d.colorInfo.isSelected;\n\t\t\tint textColor = dark ? 0xFFFFFF : 0;\n\t\t\tif (!_matches.IsDefault) {\n\t\t\t\tint i = 0;\n\t\t\t\tforeach (var v in _matches) {\n\t\t\t\t\tforeach (var t in v.MatchedSpans) {\n\t\t\t\t\t\tint from = _nameStart + t.Start, to = _nameStart + t.End;\n\t\t\t\t\t\tif (from > i) tr.DrawText(_text, textColor, i..from);\n\t\t\t\t\t\ttr.DrawText(_text, textColor, from..to, dark ? 0x0080A0 : 0x80F0FF);\n\t\t\t\t\t\ti = to;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (i < _text.Length) tr.DrawText(_text, textColor, i..);\n\t\t\t} else {\n\t\t\t\ttr.DrawText(_text, textColor);\n\t\t\t}\n\t\t\t\n\t\t\ty = d.rect.top + d.lineHeight;\n\t\t\ttr.MoveTo(d.xText, y);\n\t\t\ttr.DrawText(_text2, dark ? textColor : 0x808080);\n\t\t}\n\t\t\n\t\tpublic void DrawMarginLeft(TVDrawInfo d, GdiTextRenderer tr) {\n\t\t\tif (_accessImage == null) return;\n\t\t\tvar cxy = d.imageRect.Width;\n\t\t\tvar ri = new System.Drawing.Rectangle(d.imageRect.left - cxy / 2, d.imageRect.top + cxy / 4, cxy, cxy);\n\t\t\td.graphics.DrawImage(IconImageCache.Common.Get(_accessImage, d.dpi, isImage: true), ri);\n\t\t}\n\t\t\n\t\tpublic void Clicked() {\n\t\t\tApp.Model.OpenAndGoTo(_f, columnOrPos: _pos);\n\t\t}\n\t\t\n\t\tpublic int CompareTo(_TvItem other) {\n\t\t\tvar x = _matches;\n\t\t\tvar y = other._matches;\n\t\t\t\n\t\t\tif (x.IsDefault) return y.IsDefault ? 0 : 1;\n\t\t\tif (y.IsDefault) return -1;\n\t\t\t\n\t\t\t//Debug.Assert(x.Length == y.Length); //Once. After restarting could not reproduce. The text was \"MD5\" or \"MD5R\", while editing an Au file. Then added `else ta.Clear();` in _Update().\n\t\t\tDebug_.PrintIf(x.Length != y.Length, $\"x.Length={x.Length}, y.Length={y.Length}\");\n\t\t\tif (x.Length != y.Length) return 0;\n\t\t\t\n\t\t\tint r = 0;\n\t\t\tfor (int i = 0; i < x.Length; i++) r += x[i].CompareTo(y[i]);\n\t\t\treturn r;\n\t\t}\n\t}\n\t\n\tclass _TvDraw : ITVCustomDraw {\n\t\tTVDrawInfo _cd;\n\t\tGdiTextRenderer _tr;\n\t\t\n\t\t#region ITVCustomDraw\n\t\t\n\t\tpublic void Begin(TVDrawInfo cd, GdiTextRenderer tr) {\n\t\t\t_cd = cd;\n\t\t\t_tr = tr;\n\t\t}\n\t\t\n\t\t//public bool DrawBackground() {\n\t\t\n\t\t//\treturn default;\n\t\t//}\n\t\t\n\t\t//public bool DrawImage(System.Drawing.Bitmap image) {\n\t\t\n\t\t//\treturn default;\n\t\t//}\n\t\t\n\t\tpublic bool DrawText() {\n\t\t\t(_cd.item as _TvItem).DrawText(_cd, _tr);\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\tpublic void DrawMarginLeft() {\n\t\t\t(_cd.item as _TvItem).DrawMarginLeft(_cd, _tr);\n\t\t}\n\t\t\n\t\t//public void DrawMarginRight() {\n\t\t\n\t\t//}\n\t\t\n\t\t//public void End() {\n\t\t\n\t\t//}\n\t\t\n\t\t#endregion\n\t}\n\t\n\tconst SymbolDisplayMiscellaneousOptions s_miscDisplayOptions =\n\t\tSymbolDisplayMiscellaneousOptions.AllowDefaultLiteral\n\t\t| SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers\n\t\t//| SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier //?\n\t\t//| SymbolDisplayMiscellaneousOptions.IncludeNotNullableReferenceTypeModifier //!\n\t\t| SymbolDisplayMiscellaneousOptions.UseErrorTypeSymbolName\n\t\t| SymbolDisplayMiscellaneousOptions.UseSpecialTypes;\n\tconst SymbolDisplayParameterOptions s_parameterDisplayOptions =\n\t\tSymbolDisplayParameterOptions.IncludeType\n\t\t| SymbolDisplayParameterOptions.IncludeName\n\t\t| SymbolDisplayParameterOptions.IncludeParamsRefOut\n\t\t//| SymbolDisplayParameterOptions.IncludeOptionalBrackets\n\t\t;\n\t\n\tinternal static readonly SymbolDisplayFormat s_symbolFormat = new(\n\t\tSymbolDisplayGlobalNamespaceStyle.OmittedAsContaining,\n\t\tSymbolDisplayTypeQualificationStyle.NameAndContainingTypes,\n\t\tSymbolDisplayGenericsOptions.IncludeTypeParameters,\n\t\tSymbolDisplayMemberOptions.IncludeParameters,\n\t\tSymbolDisplayDelegateStyle.NameAndSignature,\n\t\tSymbolDisplayExtensionMethodStyle.Default,\n\t\ts_parameterDisplayOptions,\n\t\tSymbolDisplayPropertyStyle.NameOnly,\n\t\tSymbolDisplayLocalOptions.IncludeType,\n\t\tSymbolDisplayKindOptions.None,\n\t\ts_miscDisplayOptions\n\t\t);\n\t\n\tprotected override void OnPreviewKeyDown(KeyEventArgs e) {\n\t\tif (e.Source != _tv && e.Key is Key.Enter or Key.Down or Key.Up or Key.PageDown or Key.PageUp) {\n\t\t\t_tv.ProcessKey(e.Key);\n\t\t\te.Handled = true;\n\t\t} else if (e.Key is Key.Escape) {\n\t\t\tClose();\n\t\t\te.Handled = true;\n\t\t}\n\t\tbase.OnPreviewKeyDown(e);\n\t}\n\t\n\tprotected override void OnDeactivated(EventArgs e) {\n\t\tbase.OnDeactivated(e);\n\t\tif (IsVisible && !_cKeepOpen.IsChecked) {\n\t\t\t//need timer, else something activates wrong window, even with InvokeAsync\n\t\t\ttimer.after(25, _ => { if (!IsActive) Close(); });\n\t\t}\n\t}\n\t\n\tvoid _Options(WBButtonClickArgs e) {\n\t\tvar m = new popupMenu();\n\t\t\n\t\tm.AddCheck(\"Double-click\", App.Settings.ci_findgoDclick, _ => {\n\t\t\tApp.Settings.ci_findgoDclick ^= true;\n\t\t\t_tv.SingleClickActivate = !App.Settings.ci_findgoDclick;\n\t\t});\n\t\t\n\t\tm.Show(owner: this);\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Edit/CiFolding.cs",
    "content": "//#define PRINT\n\nextern alias CAW;\n\nusing Microsoft.CodeAnalysis;\nusing CAW::Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp;\nusing Microsoft.CodeAnalysis.CSharp.Syntax;\nusing Microsoft.CodeAnalysis.Text;\nusing Microsoft.CodeAnalysis.CSharp.Extensions;\nusing Microsoft.CodeAnalysis.Shared.Extensions;\nusing CAW::Microsoft.CodeAnalysis.Shared.Extensions;\n\nusing Au.Controls;\nusing static Au.Controls.Sci;\n\nnamespace LA;\n\nclass CiFolding {\n\t//Called from CiStyling._Work -> Task.Run when document opened or modified (250 ms timer).\n\t//We always set/update folding for entire code. Makes slower, but without it sometimes bad folding.\n\tpublic static List<SciFoldPoint> GetFoldPoints(SyntaxNode root, string code, CancellationToken cancelToken) {\n#if PRINT\n\t\tusing var p1 = perf.local();\n#endif\n\t\tvoid _PN(char ch = default) {\n#if PRINT\n\t\t\tp1.Next(ch);\n#endif\n\t\t}\n\t\t\n\t\tList<SciFoldPoint> af = null;\n\t\ts_foldPoints.Clear();\n\t\t\n\t\tvoid _AddFoldPoint(FoldKind what, int pos, bool start, bool trimNewline = false, ushort separator = 0) {\n\t\t\tif (trimNewline) {\n\t\t\t\tif (code[pos - 1] == '\\n') pos--;\n\t\t\t\tif (code[pos - 1] == '\\r') pos--;\n\t\t\t}\n\t\t\t(af ??= new()).Add(new(pos, start, separator));\n\t\t\tif (start) s_foldPoints.Add((pos, what));\n\t\t}\n\t\tvoid _AddFoldPoints(FoldKind what, int start, int end, ushort separator = 0) {\n\t\t\tif (separator == 0) {\n\t\t\t\tint k = code.IndexOf('\\n', start, end - start);\n\t\t\t\tif (k < 0 || k == end - 1) return;\n\t\t\t}\n\t\t\t_AddFoldPoint(what, start, true);\n\t\t\t_AddFoldPoint(what, end, false, true, separator);\n\t\t}\n\t\t\n\t\tIEnumerable<SyntaxNode> nodes;\n#if !true //slow\n\t\tif (code.Length > 200_000) { //fast, but does not fold anything inside functions and statements\n\t\t\tnodes = root.DescendantNodes(static o => {\n\t\t\t\tif (o is MemberDeclarationSyntax) return o is BaseNamespaceDeclarationSyntax or TypeDeclarationSyntax;\n\t\t\t\treturn o is CompilationUnitSyntax;\n\t\t\t});\n\t\t} else { //slow\n\t\t\tnodes = root.DescendantNodes();\n\t\t}\n#else //2-3 times faster\n\t\tif (code.Length > 500_000) { //fast, but does not fold anything inside functions and statements\n\t\t\tnodes = root.DescendantNodes(static o => {\n\t\t\t\tif (o is MemberDeclarationSyntax) return o is BaseNamespaceDeclarationSyntax or TypeDeclarationSyntax;\n\t\t\t\treturn o is CompilationUnitSyntax;\n\t\t\t});\n\t\t} else {\n\t\t\tnodes = root.DescendantNodes(n => {\n\t\t\t\t//note: all this code is just to make faster. It does not add any features.\n\t\t\t\tif (n is StatementSyntax) { //don't descend into statements that don't contain `{` etc\n\t\t\t\t\tvar nspan = n.Span;\n\t\t\t\t\tif (nspan.Length < 20) return false;\n\t\t\t\t\tvar s = code.AsSpan(nspan.ToRange());\n\t\t\t\t\tint i = s.IndexOf(1, '{');\n\t\t\t\t\tif (s[0] != '{') { //not BlockSyntax\n\t\t\t\t\t\tif (i > 0) {\n\t\t\t\t\t\t\tif (s[^1] == '}' || n is IfStatementSyntax { Statement: BlockSyntax } or DoStatementSyntax) i = s.IndexOf(i + 1, '{');\n\t\t\t\t\t\t} else if (s[^1] is ';' && s[^2] is ']' or '\"') { //maybe like `int[] a = [ multiline ];` or `var s = \"\"\"multiline\"\"\";`\n\t\t\t\t\t\t\treturn s.Contains('\\n');\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (i < 0 && s.Contains('\\n')) {\n\t\t\t\t\t\ti = s.IndexOf(\"\\\";\");\n\t\t\t\t\t\tif (i < 0) i = s.IndexOf(\"];\");\n\t\t\t\t\t}\n\t\t\t\t\treturn i >= 0;\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t});\n\t\t}\n#endif\n\t\tSyntaxNode prevStatementOrMember = null;\n\t\tforeach (var n in nodes) {\n\t\t\tif (n is GlobalStatementSyntax g) continue;\n\t\t\t//CiUtil.PrintNode(n, indent: true);\n\t\t\t\n\t\t\tFoldKind foldKind = FoldKind.Member;\n\t\t\tint foldStart = -1;\n\t\t\tbool separatorBefore = false;\n\t\t\tSyntaxNode prevSibling = null;\n\t\t\t\n\t\t\tswitch (n) {\n\t\t\tcase ExtensionBlockDeclarationSyntax d:\n\t\t\t\tfoldKind = FoldKind.Type;\n\t\t\t\tfoldStart = d.Keyword.SpanStart;\n\t\t\t\tbreak;\n\t\t\tcase BaseTypeDeclarationSyntax d: //class, struct, interface, enum\n\t\t\t\tfoldKind = FoldKind.Type;\n\t\t\t\tfoldStart = d.Identifier.SpanStart;\n\t\t\t\tseparatorBefore = _PrevSibling(n) is StatementSyntax and not LocalFunctionStatementSyntax;\n\t\t\t\tbreak;\n\t\t\tcase BaseMethodDeclarationSyntax d: //method, ctor, etc\n\t\t\t\tif (d.Body == null && d.ExpressionBody == null) continue; //extern, interface, partial\n\t\t\t\tfoldStart = d.ParameterList.SpanStart; //not perfect, but the best common property\n\t\t\t\tseparatorBefore = _PrevSibling(n) is BaseFieldDeclarationSyntax;\n\t\t\t\tbreak;\n\t\t\tcase BasePropertyDeclarationSyntax d: //property, indexer, event\n\t\t\t\tfoldStart = d.Type.FullSpan.End; //not perfect, but the best common property\n\t\t\t\tseparatorBefore = _PrevSibling(n) is BaseFieldDeclarationSyntax;\n\t\t\t\tbreak;\n\t\t\tcase LocalFunctionStatementSyntax d:\n\t\t\t\tif (d.Parent is GlobalStatementSyntax) separatorBefore = !(_PrevSibling(n) is null or LocalFunctionStatementSyntax);\n\t\t\t\telse if (_ManyLines(n, 3)) foldKind = FoldKind.Statement;\n\t\t\t\telse break;\n\t\t\t\tfoldStart = d.Identifier.SpanStart;\n\t\t\t\tbreak;\n\t\t\tcase AnonymousFunctionExpressionSyntax d when d.ExpressionBody == null: //lambda, delegate(){}\n\t\t\tcase InitializerExpressionSyntax or CollectionExpressionSyntax:\n\t\t\tcase LiteralExpressionSyntax { RawKind: (int)SyntaxKind.StringLiteralExpression } or InterpolatedStringExpressionSyntax:\n\t\t\t\tif (_ManyLines(n, n is AnonymousFunctionExpressionSyntax ? 3 : 5)) {\n\t\t\t\t\tfoldStart = n.SpanStart;\n\t\t\t\t\tfoldKind = FoldKind.Statement;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase NamespaceDeclarationSyntax d:\n\t\t\t\tfoldStart = d.Name.SpanStart;\n\t\t\t\tfoldKind = FoldKind.Namespace;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t\n\t\t\t//print.it(foldStart);\n\t\t\tif (foldStart >= 0) {\n\t\t\t\t_AddFoldPoints(foldKind, foldStart, n.Span.End, separator: (ushort)(foldKind == FoldKind.Statement ? 0 : 1));\n\t\t\t\t\n\t\t\t\t//add separator before top-level local function preceded by statement of other type.\n\t\t\t\t//\tAlso before other functions preceded by field or simple event.\n\t\t\t\tif (separatorBefore) {\n\t\t\t\t\tint i = _PrevSibling(n).Span.End; //add separator at the bottom of line containing position i\n\t\t\t\t\tif (n is LocalFunctionStatementSyntax) { //if there are empty lines, set i at the last empty line\n\t\t\t\t\t\tvar kPrev = SyntaxKind.EndOfLineTrivia;\n\t\t\t\t\t\tforeach (var t in n.GetLeadingTrivia()) {\n\t\t\t\t\t\t\tvar k = t.Kind();\n\t\t\t\t\t\t\tif (k == SyntaxKind.EndOfLineTrivia && k == kPrev) i = t.SpanStart;\n\t\t\t\t\t\t\tkPrev = k;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\ti = foldStart - i;\n\t\t\t\t\tif (i <= ushort.MaxValue) af[^2] = af[^2] with { separator = (ushort)i };\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (n is StatementSyntax or MemberDeclarationSyntax) prevStatementOrMember = n;\n\t\t\tif (cancelToken.IsCancellationRequested) return null;\n\t\t\t\n\t\t\tSyntaxNode _PrevSibling(SyntaxNode n) {\n\t\t\t\tif (prevSibling != null) return prevSibling;\n\t\t\t\tvar p = n.Parent;\n\t\t\t\tvar r = prevStatementOrMember;\n\t\t\t\twhile (r != null) {\n\t\t\t\t\tvar p2 = r.Parent;\n\t\t\t\t\tif (p2 == p || p2 is GlobalStatementSyntax) break;\n\t\t\t\t\tr = p2;\n\t\t\t\t}\n\t\t\t\treturn prevSibling = r;\n\t\t\t}\n\t\t\t\n\t\t\tbool _ManyLines(SyntaxNode n, int lines) {\n\t\t\t\tvar s = code.AsSpan(n.Span.ToRange());\n\t\t\t\tfor (int i = 0; ;) {\n\t\t\t\t\ti = s.IndexOf(i, '\\n') + 1;\n\t\t\t\t\tif (i == 0) return false;\n\t\t\t\t\tif (--lines <= 1) return true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t_PN('n');\n\t\t\n\t\tList<TextSpan> disabledRanges = null;\n\t\tvar dir = root.GetDirectives(o => o is RegionDirectiveTriviaSyntax or EndRegionDirectiveTriviaSyntax or BranchingDirectiveTriviaSyntax or EndIfDirectiveTriviaSyntax);\n\t\tforeach (var v in dir) {\n\t\t\tif (v is RegionDirectiveTriviaSyntax or EndRegionDirectiveTriviaSyntax) {\n\t\t\t\t_AddFoldPoint(FoldKind.Region, v.SpanStart, v is RegionDirectiveTriviaSyntax);\n\t\t\t} else if (v is BranchingDirectiveTriviaSyntax br && !br.BranchTaken) {\n\t\t\t\tvar rd = br.GetRelatedDirectives();\n\t\t\t\tfor (int i = 0; i < rd.Count - 1;) {\n\t\t\t\t\tif (rd[i++] == v) {\n\t\t\t\t\t\tint start = v.SpanStart, end = rd[i].SpanStart;\n\t\t\t\t\t\twhile (end > start && code[end - 1] is not ('\\n' or '\\r')) end--;\n\t\t\t\t\t\t_AddFoldPoints(FoldKind.Disabled, start, end);\n\t\t\t\t\t\t(disabledRanges ??= new()).Add(TextSpan.FromBounds(start, end));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t_PN('d');\n\t\t\n\t\t//Find comments that need to fold:\n\t\t//\t1. Blocks of //comments of >= 5 lines. Include empty lines, but split at the last empty line if last comments are followed by non-comments or other type of comments.\n\t\t//\t2. Blocks of ///comments of >= 2 lines.\n\t\t//\t3. /*...*/ comments of >= 2 lines. Same for /**...*/.\n\t\t//\t4. //. is like #region, but can be not at the start of line too. Must not be followed by a non-space character.\n\t\t//\t5. //.. is like #endregion, but can be not at the start of line too. Must not be followed by a non-space character.\n\t\t//\t\tRejected: //... unfolds 2 levels, //.... 3 levels and so on. Often //... comment used for \"more code\" or \"etc\". Also now not useful (was useful when C# did not have top-level statements).\n\t\t\n\t\t//Since root.DescendantTrivia is slow, we parse code\n\t\t//\tand then use Roslyn just to verify that the found // etc is at start of trivia, ie isn't inside a string or other comments or #directive.\n\t\t//We skip disabled code (!BranchingDirectiveTriviaSyntax.BranchTaken).\n\t\tfor (int ir = 0, nr = disabledRanges.Lenn_(), rangeStart = 0; ; rangeStart = disabledRanges[ir++].End) {\n\t\t\tint rangeEnd = ir == nr ? code.Length : disabledRanges[ir].Start;\n\t\t\t//print.it(rangeEnd-rangeStart);\n\t\t\tint rangeLength = rangeEnd - rangeStart;\n\t\t\tif (rangeLength > 2) { //possible at least //.\n\t\t\t\tvar s = code.AsSpan(rangeStart, rangeLength); //current non-disabled range in code\n\t\t\t\tfor (int i = 0; i <= s.Length - 4;) { //until last possible //..\n\t\t\t\t\tif (cancelToken.IsCancellationRequested) return null;\n\t\t\t\t\t\n\t\t\t\t\tint i0 = i = s.IndexOf(i, '/'); if ((uint)i > s.Length - 4) break;\n\t\t\t\t\tchar c = s[++i]; if (c is not ('/' or '*')) continue;\n\t\t\t\t\tif (c == '/' && _IsDotComment(s, ++i, out bool closing)) { //.\n\t\t\t\t\t\tif (!_IsStartOfTrivia(false)) continue;\n\t\t\t\t\t\t_AddFoldPoint(FoldKind.DotFold, rangeStart + i0, !closing);\n\t\t\t\t\t} else if (c == '*' || CiUtil.IsLineStart(s, i0)) {\n\t\t\t\t\t\ti = i0 + 2;\n\t\t\t\t\t\tbool isLineComment = c == '/', isDocComment = false;\n\t\t\t\t\t\tif (!isLineComment) {\n\t\t\t\t\t\t\t//ignore /*single line*/\n\t\t\t\t\t\t\tint k = s.IndexOf(i, '\\n');\n\t\t\t\t\t\t\tif (k < 0 || s[i..k].Contains(\"*/\", StringComparison.Ordinal)) continue;\n\t\t\t\t\t\t} else if (s.Eq(i, '/') && !s.Eq(++i, '/')) { //doc comment ///\n\t\t\t\t\t\t\tisLineComment = false;\n\t\t\t\t\t\t\ti0 = i; //somehow Span of doc comment trivia starts after ///\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t//ignore single-line doc comments\n\t\t\t\t\t\t\tint k = s.IndexOf(i, '\\n'); if (k++ < 0) continue;\n\t\t\t\t\t\t\twhile (k < s.Length && s[k] is '\\t' or ' ') k++;\n\t\t\t\t\t\t\tif (!s.Eq(k, \"///\")) continue;\n\t\t\t\t\t\t\tisDocComment = true;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tint nlines = 0, joinAt = 0, joinNlines = 0;\n\t\t\t\t\t\t\tfor (; ; nlines++) {\n\t\t\t\t\t\t\t\tif (nlines > 0) {\n\t\t\t\t\t\t\t\t\tint lineStart = i;\n\t\t\t\t\t\t\t\t\twhile (i < s.Length && s[i] is '\\t' or ' ') i++; //skip indent\n\t\t\t\t\t\t\t\t\tg1:\n\t\t\t\t\t\t\t\t\tbool ok = s.Eq(i, '/') && s.Eq(i + 1, '/');\n\t\t\t\t\t\t\t\t\tif (ok) {\n\t\t\t\t\t\t\t\t\t\ti += 2;\n\t\t\t\t\t\t\t\t\t\t//is same type of comment?\n\t\t\t\t\t\t\t\t\t\tif (s.Eq(i, '/')) ok = s.Eq(++i, '/'); //is /// ?\n\t\t\t\t\t\t\t\t\t\telse ok = !_IsDotComment(s, i, out _); //is //. ?\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t//join adjacent blocks of comments that end with empty line\n\t\t\t\t\t\t\t\t\t\tif (i == s.Length) joinAt = 0; //join\n\t\t\t\t\t\t\t\t\t\telse if (s[i] is '\\r' or '\\n') { //empty line. Continue, and later either join or split here.\n\t\t\t\t\t\t\t\t\t\t\tjoinAt = lineStart; joinNlines = nlines;\n\t\t\t\t\t\t\t\t\t\t\twhile (++i < s.Length && s[i] <= ' ') { } //skip empty lines and indents\n\t\t\t\t\t\t\t\t\t\t\tgoto g1;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tif (!ok) {\n\t\t\t\t\t\t\t\t\t\tif (joinAt > 0) { lineStart = joinAt; nlines = joinNlines; } //split\n\t\t\t\t\t\t\t\t\t\ti = lineStart;\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\twhile (i < s.Length && s[i] != '\\n') i++;\n\t\t\t\t\t\t\t\tif (i == s.Length) { nlines++; break; }\n\t\t\t\t\t\t\t\ti++;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (nlines < 5) continue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!_IsStartOfTrivia(isLineComment)) continue;\n\t\t\t\t\t\t_AddFoldPoints(isDocComment ? FoldKind.Doc : FoldKind.Comment, rangeStart + i0, rangeStart + i);\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tstatic bool _IsDotComment(RStr s, int j, out bool closing) {\n\t\t\t\t\t\tif (s.Eq(j, '.')) {\n\t\t\t\t\t\t\tif (closing = ++j < s.Length && s[j] == '.') j++;\n\t\t\t\t\t\t\tif (j == s.Length || s[j] <= ' ') return true; //must be at the end of line or followed by space (like //. comment). Else could be like //.Member\n\t\t\t\t\t\t} else closing = false;\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tbool _IsStartOfTrivia(bool isLineComment) {\n\t\t\t\t\t\t//p2.First();\n\t\t\t\t\t\tint start = rangeStart + i0;\n\t\t\t\t\t\tvar t = root.FindToken(start);\n\t\t\t\t\t\tvar span = t.Span;\n\t\t\t\t\t\t//p2.Next();\n\t\t\t\t\t\tif (span.Contains(start)) { i = span.End - rangeStart; return false; }\n#if true\n\t\t\t\t\t\tvar v = t.FindTrivia(start);\n\t\t\t\t\t\tspan = v.Span;\n\t\t\t\t\t\tif (span.End == 0) { Debug_.Print(start); return false; }\n\t\t\t\t\t\tbool ok = span.Start == start;\n\t\t\t\t\t\tif (!(ok && isLineComment)) i = span.End - rangeStart;\n\t\t\t\t\t\t//p2.Next();\n\t\t\t\t\t\treturn ok;\n#elif true //usually slightly slower, but in worst case could be much slower\n\t\t\t\t\t\tvar v = t.Parent.FindTrivia(start);\n\t\t\t\t\t\tspan = v.Span;\n\t\t\t\t\t\tif (span.End == 0) { Debug_.Print(start); return false; }\n\t\t\t\t\t\tbool ok = span.Start == start;\n\t\t\t\t\t\tif (!(ok && isLineComment)) i = span.End - rangeStart;\n\t\t\t\t\t\treturn ok;\n#else //slightly slower than above\n\t\t\t\t\t\tforeach (var v in t.GetAllTrivia()) {\n\t\t\t\t\t\t\tspan = v.Span;\n\t\t\t\t\t\t\tif (span.Start == start) { if (!isLineComment) i = span.End - rangeStart; return true; }\n\t\t\t\t\t\t\tif (span.Start > start) break;\n\t\t\t\t\t\t\tif (span.End > start) { i = span.End - rangeStart; return false; }\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn false;\n#endif\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (ir == nr) break;\n\t\t}\n\t\t_PN('t');\n\t\t\n\t\tif (af != null) {\n\t\t\taf.Sort((x, y) => x.pos - y.pos);\n\t\t\t//remove redundant fold end points\n\t\t\tfor (int i = 0, level = 0; i < af.Count; i++) {\n\t\t\t\tif (af[i].start) level++;\n\t\t\t\telse if (level > 0) level--;\n\t\t\t\telse af.RemoveAt(i--);\n\t\t\t}\n\t\t\t//print.it(af);\n\t\t}\n\t\treturn af;\n\t}\n\t\n\tpublic static void Fold(SciCode doc, List<SciFoldPoint> af) {\n\t\tdoc.aaaFoldingApply(af, SciTheme.Marker.Underline);\n\t\tdoc.ERestoreEditorData_();\n\t}\n\t\n\tpublic static void InitFolding(SciCode doc) {\n\t\tdoc.aaaFoldingInit(SciTheme.Margin.Fold, SciTheme.Marker.Underline);\n\t}\n\t\n\t[Flags]\n\tinternal enum FoldKind { Member = 1, Type = 2, Region = 4, Comment = 8, Doc = 16, DotFold = 32, Disabled = 64, Statement = 128, Namespace = 256 }\n\t\n\t/// <summary>\n\t/// Fold points (start position and kind) of the last <see cref=\"GetFoldPoints\"/>. Later used for the context menu.\n\t/// </summary>\n\tinternal static readonly List<(int pos, FoldKind kind)> s_foldPoints = new();\n}\n\npartial class SciCode {\n\tinternal void ERestoreEditorData_() {\n\t\t//print.it(_openState);\n\t\tif (_openState == _EOpenState.FoldingDone) return;\n\t\tvar os = _openState; _openState = _EOpenState.FoldingDone;\n\t\t\n\t\tif (os is _EOpenState.NewFileFromTemplate) {\n\t\t\tif (_fn.IsScript) {\n\t\t\t\tvar code = aaaText;\n\t\t\t\tif (!code.NE()) {\n\t\t\t\t\t//fold all //.\n\t\t\t\t\tfor (int i = base.aaaLineCount - 1; --i >= 0;) {\n\t\t\t\t\t\tif (aaaFoldingLevel(i).isHeader) {\n\t\t\t\t\t\t\tint j = aaaLineEnd(false, i);\n\t\t\t\t\t\t\tif (aaaCharAt8(j - 1) == '.' && aaaCharAt8(j - 2) == '/') Call(SCI_FOLDLINE, i);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tif (code.RxMatch(@\"//\\.\\.+\\R\\R?(?=\\z|\\R)\", 0, out RXGroup g)) {\n\t\t\t\t\t\taaaGoToPos(true, g.End);\n\t\t\t\t\t}\n\t\t\t\t\t//if (CodeInfo.GetContextAndDocument(out var cd, 0, true) && !cd.code.NE() && cd.document.GetSyntaxRootAsync().Result is CompilationUnitSyntax cu) {\n\t\t\t\t\t//\tprint.it(cu);\n\t\t\t\t\t//}\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (os == _EOpenState.NewFileNoTemplate) {\n\t\t} else {\n\t\t\t//TODO2: test SCI_SCROLLVERTICAL\n\t\t\t\n\t\t\t//restore saved folding, some markers, scroll position and caret position\n\t\t\tif (App.Model.State.EditorGet(_fn, out _sed)) {\n\t\t\t\tint cp = aaaCurrentPos8;\n\t\t\t\tif (_sed.fold != null) {\n\t\t\t\t\tfor (int i = _sed.fold.Length; --i >= 0;) EFoldLine(_sed.fold[i]);\n\t\t\t\t\tif (cp > 0) Call(SCI_ENSUREVISIBLEENFORCEPOLICY, aaaLineFromPos(false, cp));\n\t\t\t\t}\n\t\t\t\t//if (_sed.someMarker != null) {\n\t\t\t\t//\tforeach (var v in _sed.someMarker) Call(SCI_MARKERADD, v, c_markerX);\n\t\t\t\t//}\n\t\t\t\tif (os != _EOpenState.Reopen) {\n\t\t\t\t\tif (_sed.top != 0 || _sed.pos != 0) {\n\t\t\t\t\t\t_sed.top = _sed.pos = 0;\n\t\t\t\t\t\tApp.Model.State.EditorSave(_fn, _sed, true, false);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tint top = Math.Max(0, _sed.top), pos = Math.Clamp(_sed.pos, 0, aaaLen8);\n\t\t\t\t\tif (top + pos > 0 && cp == 0) {\n\t\t\t\t\t\t//workaround for:\n\t\t\t\t\t\t//\tWhen reopening a non-first document, scrollbar position remains at the top, although scrolls the view.\n\t\t\t\t\t\t//\tScintilla calls SetScrollInfo(pos), but it does not work because still didn't call SetScrollInfo(max).\n\t\t\t\t\t\t//\tAnother possible workaround would be a timer, but even 50 ms is too small.\n\t\t\t\t\t\tCall(SCI_SETVSCROLLBAR, false);\n\t\t\t\t\t\tCall(SCI_SETVSCROLLBAR, true);\n\t\t\t\t\t\t\n\t\t\t\t\t\tif (top > 0) Call(SCI_SETFIRSTVISIBLELINE, Call(SCI_VISIBLEFROMDOCLINE, top));\n\t\t\t\t\t\tif (pos <= aaaLen8) {\n\t\t\t\t\t\t\tApp.Model.EditGoBack.OnRestoringSavedPos();\n\t\t\t\t\t\t\t//aaaGoToPos(false, pos); //sometimes does not work well here (it also calls SCI_ENSUREVISIBLEENFORCEPOLICY)\n\t\t\t\t\t\t\tCall(SCI_GOTOPOS, pos);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t\n\tenum _EOpenState : byte { Open, Reopen, NewFileFromTemplate, NewFileNoTemplate, FoldingDone }\n\t_EOpenState _openState;\n\t\n\t/// <summary>\n\t/// Saves folding, markers, cursor position, etc.\n\t/// </summary>\n\tinternal void ESaveEditorData_(bool closingDoc) {\n\t\tif (_openState < _EOpenState.FoldingDone) return; //if did not have time to open editor data, better keep old data than delete. Also if not a code file.\n\t\t\n\t\tvar a = new List<int>();\n\t\tvar x = new WorkspaceState.Editor {\n\t\t\tfold = _GetLines(31),\n\t\t\t//someMarker = _GetLines(c_markerBreakpoint),\n\t\t};\n\t\tif (!closingDoc) {\n\t\t\tx.pos = aaaCurrentPos8;\n\t\t\tx.top = Call(SCI_GETFIRSTVISIBLELINE);\n\t\t\tif (x.top > 0) x.top = Call(SCI_DOCLINEFROMVISIBLE, x.top); //save document line, because visible line changes after changing wrap mode or resizing in wrap mode etc. Never mind: the top visible line may be not at the start of the document line.\n\t\t}\n\t\t\n\t\tif (x.Equals(_sed, out bool changedState, out bool changedFolding)) return;\n\t\t_sed = x;\n\t\tApp.Model.State.EditorSave(_fn, x, changedState, changedFolding);\n\t\t\n\t\t//Gets indices of lines containing markers or contracted folding points.\n\t\t//If marker 31, uses SCI_CONTRACTEDFOLDNEXT. Else uses SCI_MARKERNEXT; must be 0...24 (markers 25-31 are used for folding).\n\t\tint[] _GetLines(int marker/*, int skipLineFrom = 0, int skipLineTo = 0*/) {\n\t\t\ta.Clear();\n\t\t\tfor (int i = 0; ; i++) {\n\t\t\t\tif (marker == 31) i = Call(SCI_CONTRACTEDFOLDNEXT, i);\n\t\t\t\telse i = Call(SCI_MARKERNEXT, i, 1 << marker);\n\t\t\t\tif (i < 0) break;\n\t\t\t\ta.Add(i);\n\t\t\t}\n\t\t\treturn a.Count > 0 ? a.ToArray() : null;\n\t\t}\n\t}\n\tWorkspaceState.Editor _sed;\n\t\n\tpublic void EFoldLine(int line, bool unfold = false, bool andDescendants = false) {\n\t\tif (!aaaFoldingLevel(line).isHeader) return;\n\t\t\n\t\tif (unfold) {\n\t\t\tCall(andDescendants ? SCI_FOLDCHILDREN : SCI_FOLDLINE, line, 1);\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tif (0 != Call(SCI_GETFOLDEXPANDED, line)) {\n\t\t\t//get text for SCI_TOGGLEFOLDSHOWTEXT\n\t\t\tstring s = aaaLineText(line), s2 = null;\n\t\t\tint i = CiUtil.SkipSpace(s, 0);\n\t\t\tif (!s.Eq(i, '#')) {\n\t\t\t\tif (!s.Eq(i, \"//\")) {\n\t\t\t\t\tint last = Call(SCI_GETLASTCHILD, line, -1);\n\t\t\t\t\ts = aaaLineText(last);\n\t\t\t\t\tif (s.Length > 2) if (s.Ends(\"*/\") || (s.Ends(\"\\\";\") && s[^3] != '\\\"')) s = s[^2..]; //`text*/` -> `*/` or `text\";` -> `\";`\n\t\t\t\t\ts2 = \"... \" + s;\n\t\t\t\t} else if (s.Eq(i, \"///\")) { //let s2 = summary text\n\t\t\t\t\ts_rxFold.summary1 ??= new regexp(@\"\\h*<summary>\\h*$\", RXFlags.ANCHORED);\n\t\t\t\t\tif (s_rxFold.summary1.IsMatch(s, (i + 3)..)) {\n\t\t\t\t\t\ti = aaaLineStart(false, line + 1);\n\t\t\t\t\t\tint len = aaaLen8;\n\t\t\t\t\t\tif (i < len) {\n\t\t\t\t\t\t\tconst int maxLen = 100;\n\t\t\t\t\t\t\ts = aaaRangeText(false, i, Math.Min(len, i + maxLen + 10));\n\t\t\t\t\t\t\tint to = s.Find(\"</summary>\");\n\t\t\t\t\t\t\tbool limited = to < 0;\n\t\t\t\t\t\t\tif (limited) to = Math.Min(s.Length, maxLen); //Min to avoid partial </summary> at the end\n\t\t\t\t\t\t\ts_rxFold.summary2 ??= new regexp(@\"\\h*///\\h*\\K.+\");\n\t\t\t\t\t\t\tusing (new StringBuilder_(out var b)) {\n\t\t\t\t\t\t\t\tforeach (var g in s_rxFold.summary2.FindAllG(s, 0, 0..to)) {\n\t\t\t\t\t\t\t\t\tif (b.Length > 0) b.Append(' ');\n\t\t\t\t\t\t\t\t\tb.Append(s, g.Start, g.Length);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (limited) b.Append(\" …\");\n\t\t\t\t\t\t\t\ts2 = b.ToString();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tif (s2 != null) aaaSetString(SCI_TOGGLEFOLDSHOWTEXT, line, s2);\n\t\t\telse Call(SCI_FOLDLINE, line);\n\t\t}\n\t\t\n\t\tif (andDescendants) {\n\t\t\tfor (int last = Call(SCI_GETLASTCHILD, line, -1); ++line < last;) {\n\t\t\t\tEFoldLine(line);\n\t\t\t}\n\t\t\t//never mind: probably should not fold inside functions and statements.\n\t\t}\n\t}\n\tstatic (regexp summary1, regexp summary2) s_rxFold;\n\t\n\t/// <param name=\"modifiers\">\n\t/// 0 - toggle this.\n\t/// Shift (1) - expand this and descendants.\n\t/// Ctrl (2) - toggle this and descendants.\n\t/// </param>\n\tvoid _FoldOnMarginClick(int pos8, int modifiers) {\n\t\tif (modifiers > 2) return;\n\t\tint line = Call(SCI_LINEFROMPOSITION, pos8);\n\t\tif (!aaaFoldingLevel(line).isHeader) return;\n\t\tif (modifiers == 1) {\n\t\t\tCall(SCI_FOLDCHILDREN, line, 1);\n\t\t\treturn;\n\t\t}\n\t\tbool hide = 0 != Call(SCI_GETFOLDEXPANDED, line);\n\t\tEFoldLine(line, !hide, modifiers == 2);\n\t\tif (hide) {\n\t\t\t//move caret out of contracted region\n\t\t\tint pos = aaaCurrentPos8;\n\t\t\tif (pos > pos8) {\n\t\t\t\tint i = aaaLineEnd(false, Call(SCI_GETLASTCHILD, line, -1));\n\t\t\t\tif (pos <= i) aaaCurrentPos8 = pos8;\n\t\t\t}\n\t\t}\n\t}\n\t\n\tvoid _FoldContextMenu(int pos8 = -1) {\n\t\tif (!_fn.IsCodeFile) return;\n\t\t\n\t\t//get the fold kind for \"Fold all of this kind\"\n\t\tint headLine = aaaLineFromPos(false, pos8 < 0 ? aaaCurrentPos8 : pos8);\n\t\tif (!aaaFoldingLevel(headLine).isHeader) headLine = Call(SCI_GETFOLDPARENT, headLine);\n\t\tvar kind = headLine < 0 ? 0 : _Folds().FirstOrDefault(o => o.line == headLine).kind;\n\t\t\n\t\tvar m = new popupMenu();\n\t\tif (kind != default) {\n\t\t\tm[\"Fold all of this kind\"] = o => _Fold(false, kind);\n\t\t\tm[\"Unfold all of this kind\"] = o => _Fold(true, kind);\n\t\t\tm.Separator();\n\t\t}\n\t\tm[\"Fold functions, events\"] = o => _Fold(false, CiFolding.FoldKind.Member);\n\t\tm[\"Fold regions\"] = o => _Fold(false, CiFolding.FoldKind.Region);\n\t\tm[\"Fold //.\"] = o => _Fold(false, CiFolding.FoldKind.DotFold);\n\t\tm[\"Fold ///documentation\"] = o => _Fold(false, CiFolding.FoldKind.Doc);\n\t\tm[\"Fold comments, #if\"] = o => _Fold(false, CiFolding.FoldKind.Comment | CiFolding.FoldKind.Disabled);\n\t\tm[\"Fold all except types\"] = o => _Fold(false, CiFolding.FoldKind.Comment | CiFolding.FoldKind.Disabled | CiFolding.FoldKind.Doc | CiFolding.FoldKind.DotFold | CiFolding.FoldKind.Member | CiFolding.FoldKind.Region);\n\t\tm.Separator();\n\t\tm[\"Unfold all\"] = o => _Fold(true, 0); //not SCI_FOLDALL because need to skip meta\n\t\t\n\t\tm.Show(owner: AaWnd);\n\t\t\n\t\tvoid _Fold(bool expand, CiFolding.FoldKind kind) {\n\t\t\tvar meta = MetaComments.FindMetaComments(aaaText);\n\t\t\tint metaLine = meta.end == 0 ? -1 : aaaLineFromPos(true, meta.start);\n\t\t\tvar a = _Folds().ToArray();\n\t\t\tList<int> a2 = new();\n\t\t\tfor (int i = 0; i < a.Length; i++) {\n\t\t\t\tvar v = a[i];\n\t\t\t\tif (v.line == metaLine) continue;\n\t\t\t\tif (kind != 0) {\n\t\t\t\t\tif (!kind.Has(v.kind)) continue;\n\t\t\t\t\t//together with members and types also fold/unfold their ///documentation\n\t\t\t\t\tif (kind is CiFolding.FoldKind.Member or CiFolding.FoldKind.Type) {\n\t\t\t\t\t\tif (i > 0 && a[i - 1].kind == CiFolding.FoldKind.Doc) {\n\t\t\t\t\t\t\tint line2 = a[i - 1].line;\n\t\t\t\t\t\t\tif (v.line - 1 == Call(SCI_GETLASTCHILD, line2, -1)) a2.Add(line2);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\ta2.Add(v.line);\n\t\t\t}\n\t\t\t\n\t\t\t//In some cases something changes current pos. Also scrolls and unfolds. Sometimes then bad styling.\n\t\t\t//\tDifficult to find the reason.\n\t\t\t//\tWorkaround: hide/show in this order.\n\t\t\tif (expand) {\n\t\t\t\tfor (int i = 0; i < a2.Count; i++) {\n\t\t\t\t\tEFoldLine(a2[i], expand);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfor (int i = a2.Count; --i >= 0;) {\n\t\t\t\t\tEFoldLine(a2[i], expand);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t//move caret out of contracted region\n\t\t\t\tint line = aaaLineFromPos(false, aaaCurrentPos8), line0 = line;\n\t\t\t\twhile (0 == Call(SCI_GETLINEVISIBLE, line)) {\n\t\t\t\t\tint i = Call(SCI_GETFOLDPARENT, line);\n\t\t\t\t\tif ((uint)i >= line) break;\n\t\t\t\t\tline = i;\n\t\t\t\t}\n\t\t\t\tif (line < line0) aaaCurrentPos8 = aaaLineStart(false, line);\n\t\t\t}\n\t\t}\n\t\t\n\t\t//Gets an ordered copy of CiFolding.s_foldPoints with offsets converted to line indices.\n\t\t//\tGetFoldPoints does not do it because it would make slower there.\n\t\tIEnumerable<(int line, CiFolding.FoldKind kind)> _Folds()\n\t\t\t=> CiFolding.s_foldPoints.OrderBy(o => o.pos).Select(o => (line: aaaLineFromPos(true, o.pos), kind: o.kind));\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Edit/CiGoTo.cs",
    "content": "//#define SG_SYMBOL //sourcegraph type:symbol. Almost unusable (2023-07-04). Does not find many types, cannot search for \"member X in class Y\", cannot combine with nonsymbol search, incorrect and possibly unstable select:symbol.\n\nextern alias CAW;\n\nusing System.Net;\nusing System.Windows;\nusing System.Windows.Controls;\nusing Au.Controls;\n\nusing Microsoft.CodeAnalysis;\nusing CAW::Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp;\nusing Microsoft.CodeAnalysis.CSharp.Syntax;\nusing Microsoft.CodeAnalysis.Text;\nusing Microsoft.CodeAnalysis.Shared.Extensions;\nusing CAW::Microsoft.CodeAnalysis.Shared.Extensions;\nusing System.Text.RegularExpressions;\n\n//FUTURE: try source link. Like now VS.\n\nnamespace LA;\n\nclass CiGoTo {\n\tstruct _SourceLocation {\n\t\tpublic _SourceLocation(string file, int line, int column) {\n\t\t\tthis.file = file; this.line = line; this.column = column;\n\t\t}\n\t\tpublic string file;\n\t\tpublic int line, column;\n\t}\n\t\n\tbool _canGoTo, _inSource;\n\t//if in source\n\tList<_SourceLocation> _sourceLocations;\n\t//if in metadata\n\tstring _assembly, _repo, _type, _kind, _prefix, _member, _namespace, _alt;\n\tint _flags;\n\t//1 with github can use symbol: with member; eg github supports only methods.\n\t//2 can use \"public ... Member\"; eg cannot for interface and enum.\n\t//4 with github can use symbol: with type; eg github supports only class and struct.\n\t\n\t/// <summary>\n\t/// true if can go to the symbol source. Then caller eg can add link to HTML.\n\t/// May return true even if can't go to. Then on link click nothing happens. Next time will return false.\n\t/// </summary>\n\tpublic bool CanGoTo => _canGoTo;\n\t\n\tCiGoTo(bool inSource) { _canGoTo = true; _inSource = inSource; }\n\t\n\t/// <summary>\n\t/// Gets info required to go to symbol source file/line/position or website.\n\t/// This function is fast. The slower code is in the <b>GoTo</b> functions.\n\t/// </summary>\n\tpublic CiGoTo(ISymbol sym) {\n\t\tif (_inSource = sym.IsFromSource()) {\n\t\t\t_sourceLocations = new();\n\t\t\tforeach (var loc in sym.Locations) {\n\t\t\t\tDebug_.PrintIf(!loc.IsVisibleSourceLocation());\n\t\t\t\t//if (!loc.IsVisibleSourceLocation()) continue;\n\t\t\t\t\n\t\t\t\tvar v = loc.GetLineSpan();\n\t\t\t\t_sourceLocations.Add(new _SourceLocation(v.Path, v.StartLinePosition.Line, v.StartLinePosition.Character));\n\t\t\t}\n\t\t\t_canGoTo = _sourceLocations.Count > 0;\n\t\t} else {\n\t\t\tvar asm = sym.ContainingAssembly; if (asm == null) return;\n\t\t\tif (sym is INamespaceSymbol) return; //not useful\n\t\t\t_canGoTo = true;\n\t\t\t_assembly = asm.Name;\n\t\t\t\n\t\t\tif (asm.GetAttributes().FirstOrDefault(o => o.AttributeClass.Name == \"AssemblyMetadataAttribute\" && \"RepositoryUrl\" == o.ConstructorArguments[0].Value as string)?.ConstructorArguments[1].Value is string s && s.Length > 0) {\n\t\t\t\tif (s.Starts(\"git:\")) s = s.ReplaceAt(0, 3, \"https\"); //eg .NET\n\t\t\t\tif (s.Starts(\"https://github.com/\")) _repo = s.Ends(\".git\") ? s[19..^4] : s[19..];\n\t\t\t\tDebug_.PrintIf(_repo.NE(), s);\n\t\t\t}\n\t\t\t\n\t\t\t//Unfortunately the github search engine is so bad. Gives lots of garbage. Randomly returns not all results.\n\t\t\t//To remove some garbage, can include namespace, filename, path (can be partial, without filename).\n\t\t\t//There is no best way for all casses. GoTo() will show UI, and users can try several alternatives.\n\t\t\t//Also tried github API. Can get search results, but not always can find the match in the garbage.\n\t\t\t//\tThe returned code snippets are small and often don't contain the search words. Can download entire file, but it's too slow and dirty.\n\t\t\t//\tThe test code is in some script. Tested octokit too, but don't need it, it's just wraps the REST API, which is easy to use.\n\t\t\t//At first this class used referencesource, not github. Can jump directly to the class or method etc.\n\t\t\t//\tBut it seems it is now almost dead. Many API either don't exist or are obsolete (I guess) or only Unix version.\n\t\t\t//\tAnd it was only for .NET and Roslyn.\n\t\t\t//2023-07-02: GitHub search improved. Supports regex and partially symbol: (symbol definitions). It seems always shows all results.\n\t\t\t//\tTested: Supports symbol: for: class, interface, method.\n\t\t\t//\t\tNot for: property, event, delegate, enum, struct. For namespace only if NS{} but not if NS.NS{} or NS;.\n\t\t\t\n\t\t\tif (sym is not INamedTypeSymbol ts) {\n\t\t\t\tts = sym.ContainingType;\n\t\t\t\t//if (ts.IsExtension) ts = ts.ContainingType; //FUTURE\n\t\t\t\t_member = sym.Name;\n\t\t\t\tif (_member.Starts('.')) _member = null; //\".ctor\"\n\t\t\t\telse {\n\t\t\t\t\tif (sym is IMethodSymbol ims && ims.MethodKind is MethodKind.Ordinary or MethodKind.ReducedExtension) _flags |= 1;\n\t\t\t\t\tif (ts.TypeKind is TypeKind.Class or TypeKind.Struct) _flags |= 2;\n\t\t\t\t}\n\t\t\t}\n\t\t\tts = ts.OriginalDefinition; //eg List<int> -> List<T>\n\t\t\t\n\t\t\t_type = ts.Name; //get name like Int32 or List. GetShortName gets eg int, but we need Int32.\n\t\t\tif (ts.TypeKind is TypeKind.Class or TypeKind.Interface && !ts.IsRecord) _flags |= 4;\n\t\t\t\n#if SG_SYMBOL\n\t\t\tif (sym is INamedTypeSymbol) {\n\t\t\t\t_kind = ts.TypeKind switch {\n\t\t\t\t\tTypeKind.Class => \"class\",\n\t\t\t\t\tTypeKind.Struct => \"struct\",\n\t\t\t\t\tTypeKind.Enum => \"enum\",\n\t\t\t\t\tTypeKind.Interface => \"interface\",\n\t\t\t\t\tTypeKind.Delegate => \"method\",\n\t\t\t\t\t_ => null\n\t\t\t\t};\n\t\t\t} else {\n\t\t\t\t_kind = sym.Kind switch {\n\t\t\t\t\tSymbolKind.Event => \"event\",\n\t\t\t\t\tSymbolKind.Field => \"variable\",\n\t\t\t\t\tSymbolKind.Method => \"method\",\n\t\t\t\t\tSymbolKind.Property => \"property\",\n\t\t\t\t\t_ => null\n\t\t\t\t};\n\t\t\t\tDebug_.PrintIf(_kind == null, sym.Kind);\n\t\t\t}\n#endif\n\t\t\t\n\t\t\t_prefix = ts.TypeKind switch {\n\t\t\t\tTypeKind.Class => ts.IsRecord ? \"(record class|record)\" : \"class\",\n\t\t\t\tTypeKind.Struct => \"struct\",\n\t\t\t\tTypeKind.Enum => \"enum\",\n\t\t\t\tTypeKind.Interface => \"interface\",\n\t\t\t\t//TypeKind.Delegate => \"delegate \" + ts.DelegateInvokeMethod?.ReturnType.GetShortName(), //never mind: can be generic or qualified. Rare.\n\t\t\t\tTypeKind.Delegate => \"delegate .+?\",\n\t\t\t\t_ => null\n\t\t\t};\n\t\t\t\n\t\t\t_namespace = ts.ContainingNamespace.QualifiedName();\n\t\t\t\n\t\t\t//for source.dot.net need exact generic, preferably fully qualified, like Namespace.List<T>\n\t\t\t_alt = ts.ToDisplayString(s_sdnSymbolFormat);\n\t\t}\n\t}\n\t\n\tstatic SymbolDisplayFormat s_sdnSymbolFormat = new(\n\t\t   globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.OmittedAsContaining,\n\t\t   typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,\n\t\t   genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters);\n\t\n\tstring _GetLinkData() {\n\t\tif (!_canGoTo) return null;\n\t\tif (_inSource) {\n\t\t\tvar b = new StringBuilder();\n\t\t\tforeach (var v in _sourceLocations) b.AppendFormat(\"|{0}?{1},{2}\", v.file, v.line, v.column);\n\t\t\treturn b.ToString();\n\t\t} else {\n\t\t\treturn $\"|\\a{_assembly}\\a{_repo}\\a{_type}\\a{_member}\\a{_namespace}\\a{_kind}\\a{_prefix}\\a{_alt}\\a{_flags}\";\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Gets link data string for <see cref=\"LinkGoTo\"/>. Returns null if unavailable.\n\t/// This function is fast. The slower code is in the <b>GoTo</b> function.\n\t/// </summary>\n\tpublic static string GetLinkData(ISymbol sym) => new CiGoTo(sym)._GetLinkData();\n\t\n\t/// <summary>\n\t/// Opens symbol source file/line/position or shows github search UI. Used on link click.\n\t/// </summary>\n\t/// <param name=\"linkData\">String returned by <see cref=\"GetLinkData\"/>.</param>\n\tpublic static void LinkGoTo(string linkData) {\n\t\tif (linkData == null) return;\n\t\tbool inSource = !linkData.Starts(\"|\\a\");\n\t\tvar g = new CiGoTo(inSource);\n\t\tif (inSource) {\n\t\t\tvar a = linkData.Split('|');\n\t\t\tg._sourceLocations = new List<_SourceLocation>();\n\t\t\tfor (int i = 1; i < a.Length; i++) {\n\t\t\t\tvar s = a[i];\n\t\t\t\tint line = s.LastIndexOf('?'), column = s.LastIndexOf(',');\n\t\t\t\tg._sourceLocations.Add(new _SourceLocation(s.Remove(line), s.ToInt(line + 1), s.ToInt(column + 1)));\n\t\t\t}\n\t\t} else {\n\t\t\tvar a = linkData.Split('\\a');\n\t\t\tg._assembly = a[1];\n\t\t\tg._repo = a[2];\n\t\t\tg._type = a[3];\n\t\t\tg._member = a[4];\n\t\t\tg._namespace = a[5];\n\t\t\tg._kind = a[6];\n\t\t\tg._prefix = a[7];\n\t\t\tg._alt = a[8];\n\t\t\tg._flags = a[9].ToInt();\n\t\t}\n\t\tg.GoTo();\n\t}\n\t\n\t/// <summary>\n\t/// Opens symbol source file/line/position or website.\n\t/// If need to open website, runs async task.\n\t/// </summary>\n\tpublic void GoTo() {\n\t\tif (!_canGoTo) return;\n\t\tif (_inSource) {\n\t\t\tif (_sourceLocations.Count == 1) {\n\t\t\t\t_GoTo(_sourceLocations[0]);\n\t\t\t} else {\n\t\t\t\tint i = popupMenu.showSimple(_sourceLocations.Select(v => v.file + \", line \" + v.line.ToString()).ToArray(), rawText: true);\n\t\t\t\tif (i > 0) _GoTo(_sourceLocations[i - 1]);\n\t\t\t}\n\t\t\t\n\t\t\tstatic void _GoTo(_SourceLocation v) => App.Model.OpenAndGoTo(v.file, v.line, v.column);\n\t\t} else {\n\t\t\t_CodeSearchDialog();\n\t\t}\n\t}\n\t\n\tvoid _CodeSearchDialog() {\n\t\tAppSettings.gotoAsm_t asm = new(), asmSaved = null;\n\t\tApp.Settings.ci_gotoAsm?.TryGetValue(_assembly, out asmSaved);\n\t\tasmSaved ??= new();\n\t\tasmSaved.repo ??= _repo;\n\t\t\n\t\tvar bd = new wpfBuilder(\"Source code search\").WinSize(450).Columns(-1);\n\t\tbd.Window.UseLayoutRounding = true; //workaround for: text of OK etc buttons not centered vertically\n\t\tbd.Row(0).Add(out TabControl tc);\n\t\t\n\t\twpfBuilder _Page(string name, WBPanelType panelType = WBPanelType.Grid) {\n\t\t\tvar tp = new TabItem { Header = name };\n\t\t\ttc.Items.Add(tp);\n\t\t\treturn new wpfBuilder(tp, panelType).Options(bindLabelVisibility: true);\n\t\t}\n\t\t\n\t\tbd.R.StartGrid<KGroupBox>(\"Repository info of this assembly\").Columns(70, -1);\n\t\tbd.R.Add(\"Assembly\", out Label _, _assembly).And(50).Add(out KCheckBox cSharp, \"C#\").Checked(asmSaved.csharp);\n\t\tbd.R.Add(\"Repository\", out TextBox tRepo, asmSaved.repo)\n\t\t\t.Tooltip(\"Repository name, like owner/repo.\\nIt's the part of the github URL.\");\n\t\tbd.R.Add(\"Path\", out TextBox tPath, asmSaved.path).Tooltip(\"File paths must match this regex.\\nExample: src/libraries\");\n\t\tbd.R.Add(\"Context\", out TextBox tContext, asmSaved.context).Tooltip(@\"Let the sourcegraph query string start with this. Not used with github. Default: context:global repo:^github\\.com/\");\n\t\tbd.End();\n\t\t\n\t\tbd.AddOkCancel(apply: \"Search\");\n\t\tbd.End();\n\t\t\n\t\tbd.OkApply += _ => {\n\t\t\tasm.repo = tRepo.TextOrNull();\n\t\t\tasm.path = tPath.TextOrNull();\n\t\t\tasm.context = tContext.TextOrNull();\n\t\t\tasm.csharp = cSharp.IsChecked;\n\t\t\tif (asm.repo == _repo && asm.path == null && asm.context == null && asm.csharp) {\n\t\t\t\t//print.it(\"remove\");\n\t\t\t\tApp.Settings.ci_gotoAsm?.Remove(_assembly);\n\t\t\t} else if (asm != asmSaved) {\n\t\t\t\t//print.it(\"save\");\n\t\t\t\t(App.Settings.ci_gotoAsm ??= new())[_assembly] = new() { repo = asm.repo == _repo ? null : asm.repo, path = asm.path, context = asm.context, csharp = asm.csharp };\n\t\t\t\tasmSaved = asm with { };\n\t\t\t}\n\t\t\t\n\t\t\tApp.Settings.ci_gotoTab = tc.SelectedIndex;\n\t\t};\n\t\t\n#if SG_SYMBOL\n\t\tconst int c_sgSymbol = 0;\n\t\tconst int c_sgCode = 1;\n\t\tconst int c_github = 2;\n#else\n\t\tconst int c_sgCode = 0;\n\t\tconst int c_github = 1;\n#endif\n\t\t\n\t\tif (App.Settings.ci_gotoTab is int itab && itab > 0 && itab <= c_github) tc.SelectedIndex = itab;\n\t\t\n#if SG_SYMBOL\n\t\t_SourcegraphSymbol();\n#endif\n\t\t_Sourcegraph();\n\t\t_Github();\n\t\t_Sourcedotnet();\n\t\t\n\t\tbd.Window.Topmost = true;\n\t\tbd.WinSaved(s_wndpos, o => s_wndpos = o);\n\t\tbd.Window.Show();\n\t\t\n#if SG_SYMBOL //note: some parts of this code may be obsolete or little tested\n\t\tvoid _SourcegraphSymbol() {\n\t\t\tvar b = _Page(\"soucegraph symbol\");\n\t\t\t\n\t\t\t_AddInfo(b, \"Creates and opens a sourcegraph.com symbol search URL.  [\", new WBLink(\"syntax\", \"https://docs.sourcegraph.com/code_search/reference/queries\"), \"]\");\n\t\t\t\n\t\t\tb.R.Add(\"Symbol\", out TextBox tSymbol, $@\"\\b{_member.NE() ? _type : _member)}\\b\");\n\t\t\tTextBox tType = null; KCheckBox cType = null;\n\t\t\tif (!_member.NE()) {\n\t\t\t\tb.R.Add(out cType, \"Type\", out tType, $@\"\\b{_type}\\b\");\n\t\t\t\tcType.IsChecked = true;\n\t\t\t}\n\t\t\tb.R.Add(out KCheckBox cKind, \"Kind\", out TextBox tKind, _kind); cKind.IsChecked = _kind != null;\n\t\t\tb.R.Add(out KCheckBox cText, \"Text\", out TextBox tText, $@\"\\bnamespace {Regex.Escape(_namespace)}\\b\").Tooltip(\"The file must also contain this text or match this /regex/\"); ;\n\t\t\tb.R.Add(out KCheckBox cFile, \"File\", out TextBox tFile, _type).Tooltip(\"The file name or path must contain this text or match this /regex/\");\n\t\t\tb.R.Add(out KCheckBox cAlso, \"Also\", out TextBox tAlso, $\"NOT file:{_NotPath()}\").Tooltip(\"Append this to the query\");\n\t\t\tcAlso.IsChecked = true;\n\t\t\t\n\t\t\tb.End();\n\t\t\t\n\t\t\tbd.OkApply += _ => {\n\t\t\t\tif (tc.SelectedIndex != c_sgSymbol) return;\n\t\t\t\tvar q = new StringBuilder(@\"patterntype:regexp case:yes context:global repo:^github\\.com/\");\n\t\t\t\tif (!asm.repo.NE()) q.Append(Regex.Escape(asm.repo)).Append('$');\n\t\t\t\tif (asm.csharp) q.Append(\" lang:C#\");\n\t\t\t\tq.Append(\" type:symbol \").Append(tSymbol.Text);\n\t\t\t\tif (cType?.IsChecked == true && !tType.Text.NE()) q.Append(\" AND \").Append(tType.Text);\n\t\t\t\tif (cText.IsChecked && !tText.Text.NE()) q.Append(\" AND \").Append(tText.Text);\n\t\t\t\tif (cKind.IsChecked && !tKind.Text.NE()) q.Append(\" select:symbol.\" + tKind.Text);\n\t\t\t\tif (cFile.IsChecked && !tFile.Text.NE()) q.Append(\" file:\").Append(tFile.Text);\n\t\t\t\tif (asm.path != null) q.Append(\" file:\").Append(asm.path);\n\t\t\t\tif (cAlso.IsChecked && !tAlso.Text.NE()) q.Append(' ').Append(tAlso.Text);\n\t\t\t\t//print.it(q);\n\t\t\t\tvar url = $\"https://sourcegraph.com/search?q={WebUtility.UrlEncode(q.ToString())}\";\n\t\t\t\trun.itSafe(url);\n\t\t\t};\n\t\t}\n#endif\n\t\t\n\t\tvoid _Sourcegraph() {\n\t\t\tvar b = _Page(\"sourcegraph\");\n\t\t\t\n\t\t\tb.xAddInfoBlockF($\"Creates and opens a sourcegraph.com search URL.  [<a href='https://docs.sourcegraph.com/code_search/reference/queries'>syntax</a>]\");\n\t\t\t\n\t\t\tb.R.Add(\"Type\", out TextBox tType, $@\"\\b{_prefix}\\s+{_type}\\b\"); //note: don't use unescaped space. Then splits into too: \"\\b{_prefix}\" and \"{_type}\\b\"\n\t\t\tb.R.Add(\"Member\", out TextBox tMember);\n\t\t\tif (_member.NE()) b.Hidden(); else tMember.Text = 0 != (_flags & 2) ? $@\"\\bpublic\\s.+?\\s{_member}\\b\" : $@\"\\b{_member}\\b\";\n\t\t\tb.R.Add(out KCheckBox cText, \"Text\").Add(out TextBox tText, $@\"\\bnamespace\\s+{Regex.Escape(_namespace)}\\b\").LabeledBy().Tooltip(\"The code must also match this regex\"); ;\n\t\t\t//b.R.Add(out KCheckBox cText, \"Text\", out TextBox tText, $@\"\\bnamespace\\s+{Regex.Escape(_namespace)}\\b\").Tooltip(\"The code must also match this regex\"); ;\n\t\t\tb.R.Add(out KCheckBox cFile, \"File\").Add(out TextBox tFile, _type).LabeledBy().Tooltip(\"The file name or path must match this regex\");\n\t\t\tb.R.Add(out KCheckBox cAlso, \"Also\").Add(out TextBox tAlso, $\"NOT file:{_NotPath()}\").LabeledBy().Tooltip(\"Append this to the query\");\n\t\t\tcAlso.IsChecked = true;\n\t\t\t\n\t\t\tb.End();\n\t\t\t\n\t\t\tbd.OkApply += _ => {\n\t\t\t\tif (tc.SelectedIndex != c_sgCode) return;\n\t\t\t\tvar q = new StringBuilder(\"patterntype:regexp case:yes \"); //note: case:yes even if !asm.csharp\n\t\t\t\tq.Append(asm.context ?? @\"context:global repo:^github\\.com/\");\n\t\t\t\tif (!asm.repo.NE()) q.Append(Regex.Escape(asm.repo)).Append('$');\n\t\t\t\tif (asm.csharp) q.Append(\" lang:C#\");\n\t\t\t\tq.Append(' ');\n\t\t\t\tif (!tMember.Text.NE()) {\n\t\t\t\t\tq.Append(tMember.Text);\n\t\t\t\t\tif (!tType.Text.NE()) q.Append($\" file:has.content({tType.Text})\");\n\t\t\t\t\tif (cText.IsChecked && !tText.Text.NE()) q.Append($\" file:has.content({tText.Text})\");\n\t\t\t\t} else {\n\t\t\t\t\tq.Append(tType.Text);\n\t\t\t\t}\n\t\t\t\tif (cFile.IsChecked && !tFile.Text.NE()) q.Append(\" file:\").Append(_RxEscapeSpaces(tFile.Text));\n\t\t\t\tif (asm.path != null) q.Append(\" file:\").Append(_RxEscapeSpaces(asm.path));\n\t\t\t\tif (cAlso.IsChecked && !tAlso.Text.NE()) q.Append(' ').Append(tAlso.Text);\n\t\t\t\t//print.it(q);\n\t\t\t\tvar url = $\"https://sourcegraph.com/search?q={WebUtility.UrlEncode(q.ToString())}\";\n\t\t\t\trun.itSafe(url);\n\t\t\t\t\n\t\t\t\tstatic string _RxEscapeSpaces(string s) => s.RxReplace(@\"(?<!\\\\) \", @\"\\ \");\n\t\t\t};\n\t\t}\n\t\t\n\t\tvoid _Github() {\n\t\t\tvar b = _Page(\"github\");\n\t\t\t\n\t\t\tAction notes = () => dialog.show(\"GitHub code search notes\", \"Results may be incomplete. Try to reload the page.\\nSkips large source files.\", owner: bd.Window);\n\t\t\tb.xAddInfoBlockF($\"Creates and opens a github.com search URL.  [<a href='https://docs.github.com/en/search-github/github-code-search/understanding-github-code-search-syntax'>syntax</a>]  [<a {notes}>notes</a>]\");\n\t\t\t\n\t\t\tb.R.Add(\"Type\", out TextBox tType, 0 != (_flags & 4) && _member.NE() ? $@\"symbol:/(?-i)\\b{_type}\\b/ /(?-i)\\b{_prefix} {_type}\\b/\" : $@\"/(?-i)\\b{_prefix} {_type}\\b/\");\n\t\t\tb.R.Add(\"Member\", out TextBox tMember).Hidden(_member.NE());\n\t\t\tif (!_member.NE()) tMember.Text = 0 != (_flags & 1) ? $@\"symbol:/(?-i)\\b{_member}\\b/\"\n\t\t\t\t\t\t\t\t\t\t\t: 0 != (_flags & 2) ? $@\"/(?-i)\\bpublic .+? {_member}\\b/\" //member of class or struct\n\t\t\t\t\t\t\t\t\t\t\t: $@\"/(?-i)\\b{_member}\\b/\"; //member of interface or enum\n\t\t\tb.R.Add(out KCheckBox cText, \"Text\").Add(out TextBox tText, $\"\\\"namespace {_namespace}\\\"\").LabeledBy().Tooltip(\"Append this to the query\");\n\t\t\tb.R.Add(out KCheckBox cFile, \"File\").Add(out TextBox tFile, _type).LabeledBy().Tooltip(\"The file name or path must contain this text or match /regex/\");\n\t\t\tb.R.Add(out KCheckBox cAlso, \"Also\").Add(out TextBox tAlso, $\"NOT path:/{_NotPath()}/\").LabeledBy().Tooltip(\"Append this to the query\");\n\t\t\tcAlso.IsChecked = true;\n\t\t\t\n\t\t\tb.End();\n\t\t\t\n\t\t\tbd.OkApply += _ => {\n\t\t\t\tif (tc.SelectedIndex != c_github) return;\n\t\t\t\tvar q = new StringBuilder();\n\t\t\t\tif (!asm.repo.NE()) q.Append($\"repo:{asm.repo} \");\n\t\t\t\tif (asm.csharp) q.Append(\"lang:C# \");\n\t\t\t\tif (!tMember.Text.NE()) q.Append(tMember.Text).Append(' '); //must be before type for better results\n\t\t\t\tq.Append(tType.Text);\n\t\t\t\tif (cText.IsChecked && !tText.Text.NE()) q.Append(' ').Append(tText.Text);\n\t\t\t\tif (cFile.IsChecked && !tFile.Text.NE()) q.Append($\" path:{tFile.Text}\");\n\t\t\t\tif (asm.path != null) q.Append($\" path:/{asm.path.RxReplace(@\"(?<!\\\\)/\", @\"\\/\")}/\"); //tested: works well when cFile specified too (both use path:)\n\t\t\t\tif (cAlso.IsChecked && !tAlso.Text.NE()) q.Append(' ').Append(tAlso.Text);\n\t\t\t\t//print.it(q);\n\t\t\t\tvar url = $\"https://github.com/search?q={WebUtility.UrlEncode(q.ToString())}\";\n\t\t\t\t//print.it(url);\n\t\t\t\trun.itSafe(url);\n\t\t\t};\n\t\t}\n\t\t\n\t\tvoid _Sourcedotnet() {\n\t\t\tvar tp = new TabItem { Header = \"source.dot.net\" };\n\t\t\ttc.Items.Add(tp);\n\t\t\ttc.SelectionChanged += (_, e) => {\n\t\t\t\tif (tc.SelectedIndex == tc.Items.Count - 1) {\n\t\t\t\t\tvar s = \"https://source.dot.net/#q=\" + _alt;\n\t\t\t\t\tif (_member != null) s = s + \".\" + _member;\n\t\t\t\t\trun.itSafe(s);\n\t\t\t\t\tbd.Window.Close();\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t\t\n\t\tstring _NotPath() => @\"\\b[Tt]est|\\b[Gg]enerat|\\b[Uu]nix\\b|\\/ref\\/\" + (_repo == \"dotnet/wpf\" ? @\"|\\/cycle-breakers\\/\" : null); //note: sourcegraph does not support (?i) and (?-i)\n\t}\n\tstatic string s_wndpos;\n\t\n\t//This was used with referencesource. It seems don't need it now.\n\t///// <summary>\n\t///// If _filename or its ancestor type is forwarded to another assembly, replaces _assembly with the name of that assembly.\n\t///// For example initially we get that String is in System.Runtime. But actually it is in System.Private.CoreLib, and its name must be passed to https://source.dot.net.\n\t///// Speed: usually < 10 ms.\n\t///// </summary>\n\t//void _GetAssemblyNameOfForwardedType() {\n\t//\tvar path = folders.NetRuntimeBS + _assembly + \".dll\";\n\t//\tif (!(filesystem.exists(path).File || filesystem.exists(path = folders.NetRuntimeDesktopBS + _assembly + \".dll\").File)) return;\n\t\n\t//\tvar alc = new System.Runtime.Loader.AssemblyLoadContext(null, true);\n\t//\ttry {\n\t//\t\tvar asm = alc.LoadFromAssemblyPath(path);\n\t//\t\tvar ft = asm.GetForwardedTypes()?.FirstOrDefault(ty => ty.FullName == _filename);\n\t//\t\tif (ft != null) _assembly = ft.Assembly.GetName().Name;\n\t//\t}\n\t//\tcatch (Exception ex) { Debug_.Print(ex); }\n\t//\tfinally { alc.Unload(); }\n\t//}\n\t\n\tpublic static void GoToDefinition() {\n\t\tif (!CodeInfo.GetContextAndDocument(out var cd, metaToo: true)) return;\n\t\tvar (sym, _, helpKind, token) = CiUtil.GetSymbolEtcFromPos(cd);\n\t\tif (sym != null) {\n\t\t\tif (sym is IParameterSymbol or ITypeParameterSymbol && !sym.IsInSource()) return;\n\t\t\tif (_GetFoldersPath(token, out var fp, sym)) {\n\t\t\t\trun.itSafe(fp);\n\t\t\t} else {\n\t\t\t\tvar g = new CiGoTo(sym);\n\t\t\t\tif (g.CanGoTo) g.GoTo();\n\t\t\t}\n\t\t} else if (cd.sci.aaaHasSelection) {\n\t\t\t_Open(cd.sci.aaaSelectedText());\n\t\t} else if (helpKind == CiUtil.HelpKind.String && token.Parent.IsKind(SyntaxKind.StringLiteralExpression)) {\n\t\t\t_Open(token.ValueText, token);\n\t\t} else if (helpKind == CiUtil.HelpKind.None && cd != null) { //maybe path in comments or disabled code\n\t\t\tint pos = cd.pos;\n\t\t\tif (pos < cd.meta.end && pos > cd.meta.start) {\n\t\t\t\tforeach (var t in MetaComments.EnumOptions(cd.code, cd.meta)) {\n\t\t\t\t\tif (pos >= t.valueStart && pos <= t.valueEnd) {\n\t\t\t\t\t\t_Meta(t.Name, t.Value);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tvar root = cd.syntaxRoot;\n\t\t\t\tvar trivia = root.FindTrivia(pos); if (trivia.RawKind == 0) return;\n\t\t\t\tvar code = cd.code;\n\t\t\t\tvar span = trivia.Span;\n\t\t\t\tint from = pos, to = from;\n\t\t\t\tstatic bool _IsPathOrUrlChar(char c) => !pathname.isInvalidPathChar(c) || c == '?';\n\t\t\t\twhile (to < span.End && _IsPathOrUrlChar(code[to])) to++;\n\t\t\t\twhile (from > span.Start && _IsPathOrUrlChar(code[from - 1])) from--;\n\t\t\t\twhile (to > pos && code[to - 1] is <= ' ' or '.' or ';' or ',') to--; //trim right\n\t\t\t\twhile (from < pos && code[from] <= ' ') from++; //trim left\n\t\t\t\tif (to == from) return;\n\t\t\t\t//rejected: try to find path even if without \"\". How to know which text part it is? Better let the user select the text.\n\t\t\t\tif (!(code.Eq(from - 1, '\"') && code.Eq(to, '\"'))) return;\n\t\t\t\t_Open(code[from..to]);\n\t\t\t}\n\t\t}\n\t\t\n\t\t//if s is script, opens it. If file path, selects in Explorer. If folder path, opens the folder. If URL, opens the web page.\n\t\tstatic bool _Open(string s, SyntaxToken token = default) {\n\t\t\tif (s.NE()) return false;\n\t\t\tif (pathname.isUrl(s)) {\n\t\t\t\tif (0 == s.Starts(false, \"http:\", \"https:\")) return false;\n\t\t\t\trun.itSafe(s);\n\t\t\t} else {\n\t\t\t\tif (!pathname.isFullPathExpand(ref s, strict: false)) {\n\t\t\t\t\tvar f = App.Model.Find(s);\n\t\t\t\t\tif (f != null) { App.Model.SetCurrentFile(f); return true; }\n\t\t\t\t\tif (token.RawKind == 0 || !_GetFoldersPath(token, out var fp, null)) return false;\n\t\t\t\t\ts = pathname.combine(fp, s);\n\t\t\t\t}\n\t\t\t\tvar fe = filesystem.exists(s); if (!fe) return false;\n\t\t\t\tif (fe.Directory) run.itSafe(s);\n\t\t\t\telse run.selectInExplorer(s);\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\t//If t is \"string\" in 'folders.Folder + \"string\"' or Folder in 'folders.Folder', gets folder path and return true.\n\t\tstatic bool _GetFoldersPath(SyntaxToken t, out string s, ISymbol sym) {\n\t\t\ts = null;\n\t\t\tbool isString = sym is null;\n\t\t\tif (isString) {\n\t\t\t\tt = t.GetPreviousToken(); if (!t.IsKind(SyntaxKind.PlusToken)) return false;\n\t\t\t\tt = t.GetPreviousToken();\n\t\t\t}\n\t\t\tif (!t.IsKind(SyntaxKind.IdentifierToken)) return false;\n\t\t\tvar s2 = t.Text;\n\t\t\tt = t.GetPreviousToken(); if (!t.IsKind(SyntaxKind.DotToken)) return false;\n\t\t\tt = t.GetPreviousToken(); if (!t.IsKind(SyntaxKind.IdentifierToken) || t.Text != \"folders\") return false;\n\t\t\ts = folders.getFolder(s2);\n\t\t\tif (s is null) return false;\n\t\t\tif (true == sym?.IsInSource()) {\n\t\t\t\treturn 2 == popupMenu.showSimple(\"1 Go to definition|2 Open folder\");\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\tvoid _Meta(string name, string value) {\n\t\t\tFNFind? ffn = name switch { \"c\" => FNFind.Class, \"pr\" or \"preBuild\" or \"postBuild\" => FNFind.CodeFile, \"sign\" or \"manifest\" => FNFind.File, \"file\" or \"resource\" or \"icon\" => FNFind.Any, _ => null };\n\t\t\tif (ffn is { } ff) {\n\t\t\t\tvar f = MetaComments.FindFile(cd.sci.EFile, value, ff);\n\t\t\t\tif (f == null && name is \"c\") f = MetaComments.FindFile(cd.sci.EFile, value, FNFind.Folder);\n\t\t\t\tif (f != null) App.Model.SetCurrentFile(f);\n\t\t\t} else if (name == \"nuget\") {\n\t\t\t\tDNuget.ShowSingle(value);\n\t\t\t} else if (name == \"com\") {\n\t\t\t\tvar path = App.Model.WorkspaceDirectory + @\"\\.interop\\\" + value;\n\t\t\t\tif (filesystem.exists(path)) run.selectInExplorer(path);\n\t\t\t} else {\n\t\t\t\t_Open(value);\n\t\t\t}\n\t\t}\n\t}\n\t\n\tpublic static void GoToBase() {\n\t\tvar (sym, cd) = CiUtil.GetSymbolFromPos(andZeroLength: true);\n\t\tif (sym is null) return;\n\t\tList<ISymbol> a = new();\n\t\tif (sym is INamedTypeSymbol nt && nt.TypeKind is TypeKind.Class or TypeKind.Interface) {\n\t\t\tif (nt.TypeKind == TypeKind.Interface) {\n\t\t\t\ta.AddRange(nt.AllInterfaces);\n\t\t\t} else if (nt.BaseType != null) { //else object\n\t\t\t\ta.AddRange(nt.GetBaseTypes().SkipLast(1).Concat(nt.AllInterfaces));\n\t\t\t}\n\t\t} else if (sym.Kind is SymbolKind.Method or SymbolKind.Property or SymbolKind.Event) {\n\t\t\ta.AddRange(CAW::Microsoft.CodeAnalysis.FindSymbols.FindReferences.BaseTypeFinder.FindOverriddenAndImplementedMembers(sym, cd.document.Project.Solution, default));\n\t\t}\n\t\tif (a.Count == 0) return;\n\t\tif (a.Count == 1) sym = a[0];\n\t\telse {\n\t\t\tint i = popupMenu.showSimple(a.Select(o => o.Name).ToArray(), rawText: true);\n\t\t\tif (--i < 0) return;\n\t\t\tsym = a[i];\n\t\t}\n\t\tvar g = new CiGoTo(sym);\n\t\tif (g.CanGoTo) g.GoTo();\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Edit/CiPopupList.cs",
    "content": "using Au.Controls;\nusing System.Windows.Controls;\nusing System.Windows;\nusing System.Windows.Controls.Primitives;\nusing System.Windows.Automation;\n\nnamespace LA;\n\nclass CiPopupList {\n\tKPopup _popup;\n\tDockPanel _panel;\n\tKTreeView _tv;\n\tStackPanel _tb;\n\t\n\tSciCode _doc;\n\tCiCompletion _compl;\n\tList<CiComplItem> _a;\n\tList<CiComplItem> _av;\n\tKCheckBox[] _kindButtons;\n\tKCheckBox _groupButton;\n\tButton _unimportedButton;\n\tbool _groupsEnabled, _winApi;\n\tList<string> _groups;\n\tCiPopupText _textPopup;\n\ttimer _tpTimer;\n\t\n\tpublic KPopup PopupWindow => _popup;\n\t\n\t///// <summary>\n\t///// The child list control.\n\t///// </summary>\n\t//public KTreeView Control => _tv;\n\t\n\tpublic CiPopupList(CiCompletion compl) {\n\t\t_compl = compl;\n\t\t\n\t\t_tv = new KTreeView {\n\t\t\tItemMarginLeft = 20,\n\t\t\t//HotTrack = true, //no\n\t\t\tCustomDraw = new _CustomDraw(this),\n\t\t\t//test = true\n\t\t};\n\t\t_tv.ItemActivated += _tv_ItemActivated;\n\t\t_tv.SelectedSingle += _tv_SelectedSingle;\n\t\t\n\t\t_tb = new StackPanel { Background = SystemColors.ControlBrush };\n\t\t\n\t\tvar cstyle = Application.Current.FindResource(ToolBar.CheckBoxStyleKey) as Style;\n\t\tvar bstyle = Application.Current.FindResource(ToolBar.ButtonStyleKey) as Style;\n\t\t\n\t\tvar kindNames = CiUtil.ItemKindNames;\n\t\t_kindButtons = new KCheckBox[kindNames.Length];\n\t\tfor (int i = 0; i < kindNames.Length; i++) {\n\t\t\t_AddButton(_kindButtons[i] = new(), kindNames[i], CiComplItem.ImageResource((CiItemKind)i), _KindButton_Click);\n\t\t}\n\t\t\n\t\t_tb.Children.Add(new Separator());\n\t\t\n\t\t_AddButton(_groupButton = new() { IsChecked = App.Settings.ci_complGroup }, \"Group by namespace or inheritance\", \"resources/ci/groupby.xaml\", _GroupButton_Click);\n\t\t_AddButton(_unimportedButton = new(), \"Show more types or extension methods (Ctrl+Space)\", \"resources/ci/expandscope.xaml\", _UnimportedButton_Click);\n\t\t\n\t\t//var options = new Button();\n\t\t//options.Click += _Options_Click;\n\t\t//_AddButton(options, \"Options\", );\n\t\t\n\t\tvoid _AddButton(ButtonBase b, string text, string image, RoutedEventHandler click) {\n\t\t\tb.Style = (b is CheckBox) ? cstyle : bstyle;\n\t\t\tb.Content = ResourceUtil.GetWpfImageElement(image);\n\t\t\tb.ToolTip = text;\n\t\t\tAutomationProperties.SetName(b, text);\n\t\t\tb.Focusable = false; //would close popup\n\t\t\tif (b is KCheckBox c) c.CheckChanged += click; else b.Click += click;\n\t\t\t_tb.Children.Add(b);\n\t\t}\n\t\t\n\t\t_panel = new DockPanel { Background = SystemColors.WindowBrush };\n\t\tDockPanel.SetDock(_tb, Dock.Left);\n\t\t_panel.Children.Add(_tb);\n\t\t_panel.Children.Add(_tv);\n\t\t_popup = new KPopup {\n\t\t\tSize = (300, 360),\n\t\t\tContent = _panel,\n\t\t\tCloseHides = true,\n\t\t\tName = \"Ci.Completion\",\n\t\t\tWindowName = \"LA completion list\",\n\t\t};\n\t\t\n\t\t_textPopup = new CiPopupText(CiPopupText.UsedBy.PopupList);\n\t\t_tpTimer = new timer(_ShowTextPopup);\n\t}\n\t\n\tprivate void _KindButton_Click(object sender, RoutedEventArgs e) {\n\t\tif (_a == null) return;\n\t\tint kindsChecked = 0, kindsVisible = 0;\n\t\tfor (int i = 0; i < _kindButtons.Length; i++) {\n\t\t\tvar v = _kindButtons[i];\n\t\t\tif (v.IsVisible) {\n\t\t\t\tkindsVisible |= 1 << i;\n\t\t\t\tif (v.IsChecked == true) kindsChecked |= 1 << i;\n\t\t\t}\n\t\t}\n\t\tif (kindsChecked == 0) kindsChecked = kindsVisible;\n\t\tforeach (var v in _a) {\n\t\t\tif (0 != (kindsChecked & (1 << (int)v.kind))) v.hidden &= ~CiComplItemHiddenBy.Kind; else v.hidden |= CiComplItemHiddenBy.Kind;\n\t\t}\n\t\tUpdateVisibleItems();\n\t}\n\t\n\tprivate void _GroupButton_Click(object sender, RoutedEventArgs e) {\n\t\t_groupsEnabled = (sender as KCheckBox).IsChecked && _groups != null;\n\t\t_SortAndSetControlItems();\n\t\tthis.SelectedItem = null;\n\t\t_tv.Redraw(true);\n\t\tApp.Settings.ci_complGroup = _groupButton.IsChecked == true;\n\t}\n\t\n\tprivate void _UnimportedButton_Click(object sender, RoutedEventArgs e) {\n\t\tApp.Dispatcher.InvokeAsync(() => _compl.ShowList());\n\t}\n\t\n\t//private void _Options_Click(object sender, RoutedEventArgs e) {\n\t//\tvar m = new popupMenu();\n\t//\t//m[\"\"] = o => ;\n\t//}\n\t\n\tprivate void _tv_ItemActivated(TVItemEventArgs e) {\n\t\t_compl.Commit(_doc, _av[e.Index]);\n\t\tHide();\n\t}\n\t\n\tprivate void _tv_SelectedSingle(object sender, int index) {\n\t\tif ((uint)index < _av.Count) {\n\t\t\tvar ci = _av[index];\n\t\t\t//print.it(ci.ci.ProviderName, ci.Provider);\n\t\t\tif (ci.Provider == CiComplProvider.XmlDoc) return;\n\t\t\t_tpTimer.Tag = ci;\n\t\t\t_tpTimer.After(300);\n\t\t\t_textPopup.Text = null;\n\t\t} else {\n\t\t\t_textPopup.Hide();\n\t\t\t_tpTimer.Stop();\n\t\t}\n\t}\n\t\n\tvoid _ShowTextPopup(timer t) {\n\t\tvar ci = t.Tag as CiComplItem;\n\t\tvar text = _compl.GetDescriptionDoc(ci, 0);\n\t\tif (text == null) return;\n\t\t_textPopup.Text = text;\n\t\t_textPopup.OnLinkClick = (ph, e) => {\n\t\t\tif (e.ToInt(1) is int i && i != 0) {\n\t\t\t\tph.Text = _compl.GetDescriptionDoc(ci, e.ToInt(1));\n\t\t\t} else if (e.Starts(\"^snippet \")) {\n\t\t\t\tDSnippets.ShowSingle(e[9..]);\n\t\t\t}\n\t\t};\n\t\t_textPopup.Show(Panels.Editor.ActiveDoc, _popup.Hwnd.Rect, Dock.Right);\n\t}\n\t\n\tvoid _SortAndSetControlItems() {\n\t\tif (_winApi) {\n\t\t\tif (!_groupsEnabled) { //the database table is already grouped/sorted\n\t\t\t\t_av.Sort((c1, c2) => string.Compare(c1.Text, c2.Text, StringComparison.OrdinalIgnoreCase));\n\t\t\t}\n\t\t} else {\n\t\t\t_av.Sort((c1, c2) => {\n\t\t\t\tint diff = c1.moveDown - c2.moveDown;\n\t\t\t\tif (diff != 0) return diff;\n\t\t\t\t\n\t\t\t\tif (_groupsEnabled) {\n\t\t\t\t\tdiff = c1.group - c2.group;\n\t\t\t\t\tif (diff != 0) return diff;\n\t\t\t\t\t\n\t\t\t\t\tif (_groups[c1.group].NE()) {\n\t\t\t\t\t\tCiItemKind k1 = c1.kind, k2 = c2.kind;\n\t\t\t\t\t\t\n\t\t\t\t\t\t//group Enum and Enum.Member together\n\t\t\t\t\t\tif (k1 == CiItemKind.EnumMember) k1 = CiItemKind.Enum;\n\t\t\t\t\t\tif (k2 == CiItemKind.EnumMember) k2 = CiItemKind.Enum;\n\t\t\t\t\t\t\n\t\t\t\t\t\tdiff = k1 - k2;\n\t\t\t\t\t\tif (diff != 0) return diff;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tint r = CiUtil.SortComparer.Compare(c1.GetCI().SortText, c2.GetCI().SortText);\n\t\t\t\tif (r == 0) {\n\t\t\t\t\tr = CiSnippets.Compare(c1, c2); //custom snippet first\n\t\t\t\t}\n\t\t\t\treturn r;\n\t\t\t});\n\t\t}\n\t\t\n\t\tCiComplItem prev = null;\n\t\tforeach (var v in _av) {\n\t\t\tvar group = _groupsEnabled && (prev == null || v.group != prev.group) ? _groups[v.group] : null;\n\t\t\tv.SetDisplayText(group);\n\t\t\tprev = v;\n\t\t}\n\t\t\n\t\t_tv.SetItems(_av);\n\t}\n\t\n\tpublic void UpdateVisibleItems() {\n\t\tint n1 = 0; foreach (var v in _a) if (v.hidden == 0) n1++;\n\t\t_av = new(n1);\n\t\tforeach (var v in _a) if (v.hidden == 0) _av.Add(v);\n\t\t_SortAndSetControlItems();\n\t\t\n\t\t//Occasionally app used to crash without an error UI when typing a word and should show completions.\n\t\t//\tWindows event log shows exception with call stack, which shows that _av.Select called with _av=null.\n\t\t//\tThe reason (reproduced):\n\t\t//\t\tin _SortAndSetControlItems -> _tv.SetItems -> ... -> _Measure, probably when setting scrollbar properties,\n\t\t//\t\tWPF raises an UIA event and waits + dispatches messages. During that time is called Hide(). It sets _av=null.\n\t\t//\tWorkaround: return now if _aw null. Workaround 2: replace the WPF scrollbar with native scrollbar. Both done.\n\t\tif (_av == null) return;\n\t\t\n\t\t_compl.SelectBestMatch(_av, _groupsEnabled); //pass items sorted like in the visible list\n\t\t\n\t\t//Still _av and _a null sometimes (see above comments). It means, Roslyn code called by SelectBestMatch pumps messages.\n\t\t//\tIt started when started using the Windows \"Text cursor indicator\" feature. It aggressively sends many WM_GETOBJECT.\n\t\tif (_a == null) return;\n\t\t\n\t\tint kinds = 0;\n\t\tforeach (var v in _a) if ((v.hidden & ~CiComplItemHiddenBy.Kind) == 0) kinds |= 1 << (int)v.kind;\n\t\tfor (int i = 0; i < _kindButtons.Length; i++) _kindButtons[i].Visibility = 0 != (kinds & (1 << i)) ? Visibility.Visible : Visibility.Collapsed;\n\t}\n\t\n\tpublic void Show(SciCode doc, int position, List<CiComplItem> a, List<string> groups, bool winApi) {\n\t\tif (a.NE_()) {\n\t\t\tHide();\n\t\t\treturn;\n\t\t}\n\t\t\n\t\t_a = null; //let _KindButton_Click ignore the \"unchecked\" event triggered by the `v.IsChecked = false;`\n\t\tforeach (var v in _kindButtons) v.IsChecked = false;\n\t\t\n\t\t_a = a;\n\t\t_groups = groups;\n\t\t_groupsEnabled = _groups != null && _groupButton.IsChecked == true;\n\t\t_doc = doc;\n\t\t_winApi = winApi;\n\t\t\n\t\t_groupButton.Visibility = _groups != null ? Visibility.Visible : Visibility.Collapsed;\n\t\t_unimportedButton.Visibility = _groups != null ? Visibility.Visible : Visibility.Collapsed;\n\t\tUpdateVisibleItems();\n\t\t\n\t\tvar r = _doc.EGetCaretRectFromPos(position, inScreen: true);\n\t\tr.left -= Dpi.Scale(50, _doc);\n\t\t\n\t\t_popup.ShowByRect(_doc, Dock.Bottom, r);\n\t}\n\t\n\tpublic void Hide() {\n\t\t//Debug_.PrintIf(_debug, \"reenter, \" + new StackTrace());\n\t\tif (_a == null) return;\n\t\t_tv.SetItems(null);\n\t\t_popup.Close();\n\t\t_textPopup.Hide();\n\t\t_tpTimer.Stop();\n\t\t_a = null;\n\t\t_av = null;\n\t\t_groups = null;\n\t\t_suggestedItem = null;\n\t\t\n\t\tif (_winApi) Task.Run(() => GC.Collect());\n\t}\n\t\n\tpublic CiComplItem SelectedItem {\n\t\tget {\n\t\t\tint i = _tv.SelectedIndex;\n\t\t\treturn i >= 0 ? _av[i] : null;\n\t\t}\n\t\tset {\n\t\t\tint i = value == null ? -1 : _av.IndexOf(value);\n\t\t\tif (_tv.SelectedIndex == i) return;\n\t\t\tif (i >= 0) _tv.SelectSingle(i, andFocus: true, scrollTop: true); else _tv.UnselectAll();\n\t\t}\n\t}\n\t\n\tpublic CiComplItem SuggestedItem {\n\t\tget => _suggestedItem;\n\t\tset {\n\t\t\t_tv.UnselectAll();\n\t\t\tif (value != _suggestedItem) {\n\t\t\t\tvar old = _suggestedItem;\n\t\t\t\t_suggestedItem = value;\n\t\t\t\tif (value != null) {\n\t\t\t\t\t_tv.EnsureVisible(value, scrollTop: true);\n\t\t\t\t\t_tv.Redraw(value);\n\t\t\t\t}\n\t\t\t\tif (old != null) _tv.Redraw(old);\n\t\t\t}\n\t\t}\n\t}\n\tCiComplItem _suggestedItem;\n\t\n\tpublic bool OnCmdKey(KKey key) {\n\t\tif (_popup.IsVisible) {\n\t\t\tswitch (key) {\n\t\t\tcase KKey.Escape:\n\t\t\t\tHide();\n\t\t\t\treturn true;\n\t\t\tcase KKey.Down:\n\t\t\tcase KKey.Up:\n\t\t\tcase KKey.PageDown:\n\t\t\tcase KKey.PageUp:\n\t\t\tcase KKey.Home:\n\t\t\tcase KKey.End:\n\t\t\t\t_tv.ProcessKey(keys.more.KKeyToWpf(key));\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\t\n\tclass _CustomDraw : ITVCustomDraw {\n\t\tCiPopupList _p;\n\t\tTVDrawInfo _cd;\n\t\tGdiTextRenderer _tr;\n\t\tint _textColor;\n\t\t\n\t\tpublic _CustomDraw(CiPopupList list) {\n\t\t\t_p = list;\n\t\t}\n\t\t\n\t\tpublic void Begin(TVDrawInfo cd, GdiTextRenderer tr) {\n\t\t\t_cd = cd;\n\t\t\t_tr = tr;\n\t\t\t_textColor = Api.GetSysColor(Api.COLOR_WINDOWTEXT);\n\t\t}\n\t\t\n\t\t//public bool DrawBackground() {\n\t\t//}\n\t\t\n\t\t//public bool DrawCheckbox() {\n\t\t//}\n\t\t\n\t\t//public bool DrawImage(System.Drawing.Bitmap image) {\n\t\t//}\n\t\t\n\t\tpublic bool DrawText() {\n\t\t\tvar ci = _cd.item as CiComplItem;\n\t\t\t\n\t\t\tif (ci == _p.SuggestedItem) {\n\t\t\t\t_cd.graphics.DrawRectangleInset(System.Drawing.Pens.DodgerBlue, _cd.rect);\n\t\t\t}\n\t\t\t\n\t\t\tvar s = _cd.item.DisplayText;\n\t\t\tRange black, green;\n\t\t\tif (ci.commentOffset == 0) { black = ..s.Length; green = default; } else { black = ..ci.commentOffset; green = ci.commentOffset..; }\n\t\t\t\n\t\t\tint xEndOfText = 0;\n\t\t\tint color = ci.moveDown.HasAny(CiComplItemMoveDownBy.Name | CiComplItemMoveDownBy.FilterText) ? 0x808080 : _textColor;\n\t\t\t_tr.MoveTo(_cd.xText, _cd.yText);\n\t\t\tif (ci.hilite != 0) {\n\t\t\t\tulong h = ci.hilite;\n\t\t\t\tfor (int normalFrom = 0, boldFrom = 0, boldTo = 0, to = black.End.Value; normalFrom < to; normalFrom = boldTo) {\n\t\t\t\t\tfor (boldFrom = normalFrom; boldFrom < to && 0 == (h & 1); boldFrom++) h >>= 1;\n\t\t\t\t\t_tr.DrawText(s, color, normalFrom..boldFrom);\n\t\t\t\t\tif (boldFrom == to) break;\n\t\t\t\t\tfor (boldTo = boldFrom; boldTo < to && 0 != (h & 1); boldTo++) h >>= 1;\n\t\t\t\t\t_tr.FontBold(); _tr.DrawText(s, color, boldFrom..boldTo); _tr.FontNormal();\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t_tr.DrawText(s, color, black);\n\t\t\t}\n\t\t\t\n\t\t\tif (ci.moveDown.Has(CiComplItemMoveDownBy.Obsolete)) xEndOfText = _tr.GetCurrentPosition().x;\n\t\t\t\n\t\t\tif (ci.commentOffset > 0) _tr.DrawText(s, 0x00A040, green);\n\t\t\t\n\t\t\t//draw red line over obsolete items\n\t\t\tif (ci.moveDown.Has(CiComplItemMoveDownBy.Obsolete)) {\n\t\t\t\tint vCenter = _cd.rect.top + _cd.rect.Height / 2;\n\t\t\t\t_cd.graphics.DrawLine(System.Drawing.Pens.OrangeRed, _cd.xText, vCenter, xEndOfText, vCenter);\n\t\t\t}\n\t\t\t\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\tpublic void DrawMarginLeft() {\n\t\t\tvar ci = _cd.item as CiComplItem;\n\t\t\tvar g = _cd.graphics;\n\t\t\tvar r = _cd.rect;\n\t\t\t\n\t\t\t//draw images: access, static/abstract\n\t\t\tvar cxy = _cd.imageRect.Width;\n\t\t\tvar ri = new System.Drawing.Rectangle(_cd.imageRect.left - cxy, _cd.imageRect.top + cxy / 4, cxy, cxy);\n\t\t\tif (ci.AccessImageSource is string s1) g.DrawImage(IconImageCache.Common.Get(s1, _cd.dpi, isImage: true), ri);\n\t\t\tif (ci.ModifierImageSource is string s2) g.DrawImage(IconImageCache.Common.Get(s2, _cd.dpi, isImage: true), ri);\n\t\t\t\n\t\t\t//draw group separator\n\t\t\tif (_cd.index > 0) {\n\t\t\t\tvar cip = _p._av[_cd.index - 1];\n\t\t\t\tif (cip.moveDown != ci.moveDown || (_p._groupsEnabled && cip.group != ci.group)) {\n\t\t\t\t\t_cd.graphics.DrawLine(System.Drawing.Pens.YellowGreen, 0, r.top + 1, r.right, r.top + 1);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Edit/CiPopupText.cs",
    "content": "using Au.Controls;\nusing System.Windows;\nusing System.Windows.Controls;\n\nnamespace LA;\n\nclass CiPopupText {\n\tpublic enum UsedBy { PopupList, Signature, Info }\n\t\n\tKPopup _w;\n\tFlowDocumentControl _c;\n\tEventHandler _onHiddenOrDestroyed;\n\tUsedBy _usedBy;\n\tSystem.Windows.Documents.Section _section;\n\tbool _updateText;\n\t\n\tpublic CiPopupText(UsedBy usedy, EventHandler onHiddenOrDestroyed = null) {\n\t\t_usedBy = usedy;\n\t\t_onHiddenOrDestroyed = onHiddenOrDestroyed;\n\t}\n\t\n\t/// <summary>\n\t/// The top-level popup window.\n\t/// </summary>\n\tpublic KPopup PopupWindow => _CreateOrGet();\n\t\n\t/// <summary>\n\t/// Called when clicked a link with href prefix \"^\".\n\t/// </summary>\n\tpublic Action<CiPopupText, string> OnLinkClick { get; set; }\n\t\n\t/// <summary>\n\t/// Text to show. Set before calling Show.\n\t/// </summary>\n\tpublic System.Windows.Documents.Section Text {\n\t\tget => _section;\n\t\tset {\n\t\t\tif (value != _section) {\n\t\t\t\t_section = value;\n\t\t\t\tif (IsVisible) _SetText(); else _updateText = true;\n\t\t\t\t//TODO3: if visible, probably does not auto-size. But maybe now not used, because never noticed incorrect size.\n\t\t\t}\n\t\t}\n\t}\n\t\n\tKPopup _CreateOrGet() {\n\t\tif (_w == null) {\n\t\t\tbool ubInfo = _usedBy == UsedBy.Info;\n\t\t\t_w = new KPopup(ubInfo ? WS.POPUP | WS.BORDER : WS.POPUP | WS.THICKFRAME, shadow: ubInfo, sizeToContent: ubInfo ? SizeToContent.Height : default) {\n\t\t\t\tName = \"Ci.Info\",\n\t\t\t\tWindowName = _usedBy switch { UsedBy.PopupList => \"LA completion item info\", UsedBy.Signature => \"LA parameters info\", _ => \"LA quick info\" },\n\t\t\t\tCloseHides = true\n\t\t\t};\n\t\t\t_w.Size = (_usedBy == UsedBy.Signature ? 800 : 600, _usedBy == UsedBy.PopupList ? 360 : 300);\n\t\t\t//rejected: save size in app settings (if not info). Popup list too.\n\t\t\t\n\t\t\t_c = CiText.CreateControl();\n\t\t\t_w.Content = _c;\n\t\t\t\n\t\t\tif (_onHiddenOrDestroyed != null) _w.Hidden += _onHiddenOrDestroyed;\n\t\t\t_c.LinkClicked += (sender, s) => {\n\t\t\t\tif (s.Starts('#')) return; //anchor\n\t\t\t\tif (s.Starts('^')) {\n\t\t\t\t\tOnLinkClick?.Invoke(this, s);\n\t\t\t\t} else if (s.Starts('|')) { //go to symbol source file/position or web page\n\t\t\t\t\tCiGoTo.LinkGoTo(s);\n\t\t\t\t} else if (HelpUtil.IsAuHelp_(s)) {\n\t\t\t\t\tHelpUtil.AuHelp(s);\n\t\t\t\t} else {\n\t\t\t\t\trun.itSafe(s);\n\t\t\t\t}\n\t\t\t};\n\t\t\t\n\t\t\tif (ubInfo) _w.ClickInactiveActivateOwner = true;\n\t\t}\n\t\treturn _w;\n\t}\n\t\n\t/// <summary>\n\t/// Shows by anchorRect, owned by ownerControl.\n\t/// </summary>\n\t/// <param name=\"ownerControl\"></param>\n\t/// <param name=\"anchorRect\">Rectangle in screen coord. The popup window will be by it but not in it (in it if side = null).</param>\n\t/// <param name=\"hideIfOutside\">Hide when mouse moved outside anchorRect unless is in the popup window.</param>\n\tpublic void Show(SciCode ownerControl, RECT anchorRect, Dock? side, bool hideIfOutside = false) {\n\t\t_CreateOrGet();\n\t\tif (_updateText) _SetText();\n\t\t_w.ShowByRect(ownerControl, side, anchorRect);\n\t\t\n\t\tif (hideIfOutside) {\n\t\t\ttimer.every(100, t => {\n\t\t\t\tif (IsVisible) {\n\t\t\t\t\tvar p = mouse.xy;\n\t\t\t\t\tif (anchorRect.Contains(p) || _w.Hwnd.Rect.Contains(p) || _c.IsMouseCaptureWithin) return;\n\t\t\t\t\tHide();\n\t\t\t\t}\n\t\t\t\tt.Stop();\n\t\t\t});\n\t\t}\n\t}\n\t\n\tpublic void Show(SciCode ownerControl, int pos16, bool hideIfOutside = false, bool above = false) {\n\t\tvar r = ownerControl.EGetCaretRectFromPos(pos16, inScreen: true);\n\t\tr.Inflate(50, 0);\n\t\tShow(ownerControl, r, above ? Dock.Top : Dock.Bottom, hideIfOutside);\n\t}\n\t\n\tpublic bool Hide() {\n\t\tif (!IsVisible) return false;\n\t\t//print.it(new StackTrace());\n\t\t_w.Close();\n\t\t_section = null;\n\t\t_SetText();\n\t\treturn true;\n\t}\n\t\n\tpublic bool IsVisible => _w?.IsVisible ?? false;\n\t\n\tvoid _SetText() {\n\t\t_updateText = false;\n\t\t_c?.Clear();\n\t\tif (_section != null) _c.Document.Blocks.Add(_section);\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Edit/CiProjects.cs",
    "content": "extern alias CAW;\n\nusing Microsoft.CodeAnalysis;\nusing CAW::Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp;\nusing Microsoft.CodeAnalysis.CSharp.Syntax;\nusing Microsoft.CodeAnalysis.Text;\nusing Microsoft.CodeAnalysis.CSharp.Extensions;\nusing Microsoft.CodeAnalysis.Shared.Extensions;\nusing CAW::Microsoft.CodeAnalysis.Shared.Extensions;\nusing SymAcc = Microsoft.CodeAnalysis.Accessibility;\n\n\nnamespace LA;\n\nclass CiProjects {\n\t//public void Test() {\n\t\n\t//}\n\t\n\t/// <summary>\n\t/// From meta comments of all workspace files extracts pr and c.\n\t/// Slow, loads texts of all code files except those in excluded folders. At first saves everything.\n\t/// </summary>\n\t/// <returns>\n\t/// List containing <b>FileNode</b> of files that have valid pr or c, along with non-null <b>FileNode</b> arrays containing unique pr and c, including transitive c.\n\t/// All pr/c of a project are in a single list item where <b>f</b> is the project-main file.\n\t/// Random order if using parallel.\n\t/// </returns>\n\tstatic async Task<List<(FileNode f, FileNode[] pr, FileNode[] c, Dictionary<FileNode, FileNode> cPr)>> _GetAllMetaPrAndC() {\n\t\tvar model = App.Model;\n\t\tmodel.Save.AllNowIfNeed();\n\t\t\n\t\t//excluded folders\n\t\tHashSet<FileNode> hsEF = new();\n\t\tif (!model.WSSett.ci_skipFolders.NE()) {\n\t\t\tforeach (var v in model.WSSett.ci_skipFolders.Lines(noEmpty: true)) {\n\t\t\t\tvar f = model.Find(v, FNFind.Folder);\n\t\t\t\tif (f != null) hsEF.Add(f);\n\t\t\t}\n\t\t}\n\t\t\n\t\t//get file paths before loading async\n\t\tList<(FileNode f, string path)> a1 = new();\n\t\tFileNode projMain = null; //for all files in a project let f = project-main file\n#if DEBUG\n\t\t//if(keys.isScrollLock) _Folder(model.Find(\"\\\\Deps\", FNFind.Folder)); else\n#endif\n\t\t_Folder(model.Root);\n\t\t\n\t\tvoid _Folder(FileNode parent) {\n\t\t\tfor (var f = parent.FirstChild; f != null; f = f.Next) {\n\t\t\t\tif (f.IsCodeFile) {\n\t\t\t\t\tvar path = f.FilePath;\n\t\t\t\t\tif (projMain != null && f.IsClass) a1.Add((projMain, path)); else a1.Add((f, path));\n\t\t\t\t} else if (f.IsFolder && !hsEF.Contains(f)) {\n\t\t\t\t\tbool isProj = projMain == null && f.IsProjectFolder(out projMain);\n\t\t\t\t\t_Folder(f);\n\t\t\t\t\tif (isProj) projMain = null;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\t//load texts of all files. Find meta pr and c, and add to d.\n\t\tvar d = new Dictionary<FileNode, List<(int meta, string value)>>();\n\t\tawait Task.Run(() => {\n\t\t\tif (App.Settings.find_parallel) {\n\t\t\t\tParallel.For(0, a1.Count, i => _Meta(i, true));\n\t\t\t} else {\n\t\t\t\tfor (int i = 0; i < a1.Count; i++) _Meta(i, false);\n\t\t\t}\n\t\t\t\n\t\t\tvoid _Meta(int i, bool parallel) {\n\t\t\t\t//rejected: optimize: load only n bytes from the start of the file. Tested, similar speed.\n\t\t\t\tif (FileNode.GetFileTextLL(a1[i].path) is not string code || code.Length < 9) return;\n\t\t\t\tvar meta = MetaComments.FindMetaComments(code); if (meta.end == 0) return;\n\t\t\t\tbool locked = false;\n\t\t\t\tforeach (var v in MetaComments.EnumOptions(code, meta)) {\n\t\t\t\t\tint m; if (v.NameIs(\"pr\")) m = 1; else if (v.NameIs(\"c\")) m = 2; else continue;\n\t\t\t\t\tif (!locked && parallel) { locked = true; Monitor.Enter(d); }\n\t\t\t\t\tif (d.TryGetValue(a1[i].f, out var a2)) a2.Add((m, v.Value)); else d.Add(a1[i].f, new() { (m, v.Value) });\n\t\t\t\t}\n\t\t\t\tif (locked) Monitor.Exit(d);\n\t\t\t}\n\t\t});\n\t\t\n\t\t//find pr/c files and convert to list ar\n\t\tList<(FileNode f, FileNode[] pr, FileNode[] c, Dictionary<FileNode, FileNode> cPr)> ar = new();\n\t\tList<FileNode> aPR = new(), aC = new();\n\t\tforeach (var (f, a) in d) {\n\t\t\taPR.Clear(); aC.Clear();\n\t\t\tforeach (var v in a) {\n\t\t\t\tif (v.meta == 1) { //pr\n\t\t\t\t\tvar p = MetaComments.FindFile(f, v.value, FNFind.Class)?.GetProjectMainOrThis();\n\t\t\t\t\tif (p != null && p != f && !aPR.Contains(p)) aPR.Add(p);\n\t\t\t\t} else { //c\n\t\t\t\t\tif (MetaComments.FindFile(f, v.value, FNFind.Any) is FileNode ff) {\n\t\t\t\t\t\tif (ff.IsFolder) foreach (var c in ff.Descendants()) _AddC(c); else _AddC(ff);\n\t\t\t\t\t\tvoid _AddC(FileNode c) { if (c.IsClass && c != f && !aC.Contains(c)) aC.Add(c); }\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (aPR.Any() || aC.Any()) {\n\t\t\t\tar.Add((f, aPR.ToArray(), aC.ToArray(), null));\n\t\t\t\t//print.it(f, aPR, aC);\n\t\t\t}\n\t\t}\n\t\t\n\t\t//append transitive c and pr. Eg if file F1 contains c F2, and F2 contains c F3, add F3 to F1.\n\t\t//\tyes: F1 -> c F2 -> c F3\n\t\t//\tyes: F1 -> c F2 -> pr F3\n\t\t//\tyes: F1 -> pr F2 -> c F3\n\t\t//\tno: F1 -> pr F2 -> pr F3\n\t\t//\tno: F1 -> pr F2 -> c F3 -> pr F4\n\t\t_Transitive();\n\t\tvoid _Transitive() {\n\t\t\tvar td = ar.ToDictionary(o => o.f, o => (o.pr, o.c));\n\t\t\tList<FileNode> tap = new(), tac = new();\n\t\t\tforeach (ref var v in ar.AsSpan()) {\n\t\t\t\ttap.Clear(); tap.AddRange(v.pr);\n\t\t\t\ttac.Clear(); tac.AddRange(v.c);\n\t\t\t\tDictionary<FileNode, FileNode> cPr = null;\n\t\t\t\tforeach (var f in v.pr) _AddTransitive(f, true, f);\n\t\t\t\tforeach (var f in v.c) _AddTransitive(f, false, null);\n\t\t\t\tif (tap.Count > v.pr.Length) v.pr = tap.ToArray();\n\t\t\t\tif (tac.Count > v.c.Length) v.c = tac.ToArray();\n\t\t\t\tv.cPr = cPr;\n\t\t\t\t\n\t\t\t\tvoid _AddTransitive(FileNode fu, bool noPr, FileNode fPr) {\n\t\t\t\t\tif (!td.TryGetValue(fu, out var t)) return;\n\t\t\t\t\tvar (apr, ac) = t;\n\t\t\t\t\tif (!noPr) {\n\t\t\t\t\t\tforeach (var f in apr) {\n\t\t\t\t\t\t\tif (tap.Contains(f)) continue;\n\t\t\t\t\t\t\ttap.Add(f);\n\t\t\t\t\t\t\t_AddTransitive(f, true, f);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tforeach (var f in ac) {\n\t\t\t\t\t\tif (tac.Contains(f) || tap.Contains(f)) continue;\n\t\t\t\t\t\ttac.Add(f);\n\t\t\t\t\t\tif (fPr != null) (cPr ??= new()).Add(f, fPr);\n\t\t\t\t\t\t_AddTransitive(f, noPr, fPr);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\t//foreach (var v in ar.OrderBy(o => o.f.ItemPath)) print.it(v.f.ItemPath, v.pr, v.c);\n\t\t\n\t\treturn ar;\n\t}\n\t\n\t/// <summary>\n\t/// Gets all files that use some files in <i>d</i> through meta pr or c.\n\t/// For files in projects gets the project-main file.\n\t/// </summary>\n\tstatic async Task<List<FileNode>> _GetUsers(Dictionary<FileNode, Project> d, _FRContext x) {\n\t\tvar a = await _GetAllMetaPrAndC();\n\t\t\n\t\tList<FileNode> ar = new();\n\t\tvar defProjects = d\n\t\t\t.Where(o => o.Value.CompilationOptions.OutputKind == OutputKind.DynamicallyLinkedLibrary)\n\t\t\t.Select(o => FileOf(o.Value))\n\t\t\t.Distinct()\n\t\t\t.ToArray();\n\t\t//print.it(d.Keys, defProjects.Select(o => o.ItemPath));\n\t\t\n\t\tvar skipC = x.sol.Projects.SelectMany(o => o.DocumentIds.Select(FileOf)).ToHashSet();\n\t\tforeach (var user in a) {\n\t\t\tforeach (var f in defProjects)\n\t\t\t\tif (user.pr.Contains(f))\n\t\t\t\t\tif (!skipC.Contains(user.f)) goto g1;\n\t\t\tforeach (var f in d.Keys)\n\t\t\t\tif (user.c.Contains(f))\n\t\t\t\t\tif (!skipC.Contains(user.f)) {\n\t\t\t\t\t\tif (user.cPr != null && user.cPr.TryGetValue(f, out var cPr)) //if user uses this c through pr\n\t\t\t\t\t\t\tif (defProjects.Contains(cPr)) continue;\n\t\t\t\t\t\tgoto g1;\n\t\t\t\t\t}\n\t\t\tcontinue;\n\t\t\tg1:\n\t\t\tar.Add(user.f);\n\t\t}\n\t\t\n\t\treturn ar;\n\t}\n\t\n\t/// <summary>\n\t/// Gets all files that use <i>sym</i>.-\n\t/// For files in projects gets the project-main file.\n\t/// If <i>sym</i> is defined in class file(s), calls other <b>_GetUsers</b> overload and passes them.\n\t/// If <i>sym</i> is defined in metadata or global.cs, and current file is a class file, calls that <b>_GetUsers</b> overload and passes current file.\n\t/// </summary>\n\tstatic async Task<List<FileNode>> _GetUsers(ISymbol sym, _FRContext x) {\n\t\tif (sym is IAliasSymbol alias) sym = alias.Target;\n\t\t//if (sym is not (INamespaceSymbol or INamedTypeSymbol or IMethodSymbol or IPropertySymbol or IEventSymbol or IFieldSymbol)) return null;\n\t\tif (sym.DeclaredAccessibility is SymAcc.Private or SymAcc.NotApplicable) return null; //Internal and ProtectedAndInternal can be accessed through [InternalsVisible] or testInternal.\n\t\tDictionary<FileNode, Project> d = new();\n\t\tHashSet<FileNode> globals = null;\n\t\tbool isInMetadata = false, isInGlobal = false;\n\t\tforeach (var loc in sym.Locations) {\n\t\t\tFileNode f; bool currentFile = false;\n\t\t\tif (loc.IsInSource) {\n\t\t\t\tf = FileOf(loc.SourceTree, x.sol);\n\t\t\t\tif (f.IsClass && (globals ??= _GetGlobals()).Contains(f)) {\n\t\t\t\t\tisInGlobal = currentFile = true;\n\t\t\t\t\tf = x.cd.sci.EFile;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (isInMetadata) continue;\n\t\t\t\tisInMetadata = currentFile = true;\n\t\t\t\tf = x.cd.sci.EFile;\n\t\t\t}\n\t\t\t\n\t\t\tif (f.IsClass && !d.ContainsKey(f)) {\n\t\t\t\tvar doc = currentFile\n\t\t\t\t\t? x.sol.GetDocument(x.sol.GetDocumentIdsWithFilePath(f.ItemPath).First())\n\t\t\t\t\t: x.sol.GetDocument(loc.SourceTree);\n\t\t\t\td.Add(f, doc.Project);\n\t\t\t}\n\t\t}\n\t\tif (isInMetadata || isInGlobal) x.info = \"Searched only in this and related files, not in entire workspace.\";\n\t\treturn d.Any() ? await _GetUsers(d, x) : null;\n\t\t\n\t\t//rejected: append link 'Search in entire workspace'. Can be very slow if many files.\n\t\t\n\t\tstatic HashSet<FileNode> _GetGlobals() {\n\t\t\tHashSet<FileNode> h = new();\n\t\t\tvar g = App.Model.FindGlobalCs();\n\t\t\tif (g != null) _File(g);\n\t\t\tvoid _File(FileNode f) {\n\t\t\t\tif (!h.Add(f)) return;\n\t\t\t\tif (FileNode.GetFileTextLL(f.FilePath) is not string code || code.Length == 0) return;\n\t\t\t\tvar meta = MetaComments.FindMetaComments(code); if (meta.end == 0) return;\n\t\t\t\tforeach (var v in MetaComments.EnumOptions(code, meta)) {\n\t\t\t\t\tif (v.NameIs(\"c\")) {\n\t\t\t\t\t\tif (MetaComments.FindFile(f, v.Value, FNFind.Any) is FileNode ff) {\n\t\t\t\t\t\t\tif (ff.IsFolder)\n\t\t\t\t\t\t\t\tforeach (var c in ff.Descendants()) _File(c);\n\t\t\t\t\t\t\telse _File(ff);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn h;\n\t\t}\n\t}\n\t\n\tpublic static async Task<(Solution solution, string info)> GetSolutionForFindReferences(ISymbol sym, CodeInfo.Context cd) {\n\t\t_FRContext x = new() { sol = cd.document.Project.Solution, cd = cd };\n\t\tvar users = await _GetUsers(sym, x);\n\t\t\n\t\tif (!users.NE_()) {\n\t\t\t//print.it(\"USERS\", users);\n\t\t\t//never mind: if S -> c C -> pr L, adds C and S as users of L, although C is included in S.\n\t\t\t\n\t\t\t//create ProjectInfos for users\n\t\t\tList<(ProjectInfo pri, MetaComments meta)> ap = new();\n\t\t\tvar projects = x.sol.ProjectIds.ToDictionary(o => FileOf(o));\n\t\t\tDictionary<FileNode, string> dTexts = new();\n\t\t\tforeach (var f in users) {\n\t\t\t\tap.Add(_CreateProject(x, f, projects, dTexts));\n\t\t\t}\n\t\t\t\n\t\t\t//maybe some of these new projects have pr to other new projects\n\t\t\tif (ap.Count > 1) _AddNewPR();\n\t\t\tvoid _AddNewPR() {\n\t\t\t\tvar prNew = ap\n\t\t\t\t\t.Where(o => o.pri.CompilationOptions.OutputKind == OutputKind.DynamicallyLinkedLibrary)\n\t\t\t\t\t.ToDictionary(o => o.meta.MainFile.f, o => o.pri.Id);\n\t\t\t\tif (prNew.Count > 0) {\n\t\t\t\t\tList<ProjectReference> aPr = new();\n\t\t\t\t\tforeach (ref var p in ap.AsSpan()) {\n\t\t\t\t\t\tif (p.meta.ProjectReferences is { } a1) {\n\t\t\t\t\t\t\taPr.Clear();\n\t\t\t\t\t\t\tforeach (var v in a1) if (prNew.TryGetValue(v.f, out var p1)) aPr.Add(new ProjectReference(p1));\n\t\t\t\t\t\t\tif (aPr.Count > 0) p.pri = p.pri.WithProjectReferences(p.pri.ProjectReferences.Concat(aPr));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t//add these ProjectInfos to solution\n\t\t\tawait Task.Run(() => {\n\t\t\t\tforeach (var (pri, _) in ap) x.sol = x.sol.AddProject(pri);\n\t\t\t});\n\t\t}\n\t\t//print.it(x.sol.Projects.Count(), x.sol.Projects.Select(o => o.Name));\n\t\treturn (x.sol, x.info);\n\t}\n\t\n\tstatic (ProjectInfo pri, MetaComments meta) _CreateProject(_FRContext x, FileNode f, Dictionary<FileNode, ProjectId> projects, Dictionary<FileNode, string> dTexts) {\n\t\tif (!f.FindProject(out var projFolder, out var fmain)) fmain = f;\n\t\tvar m = new MetaComments(MCFlags.ForFindReferences, dTexts);\n\t\tm.Parse(fmain, projFolder);\n\t\t\n\t\tif (m.TestInternal is string[] testInternal) TestInternal.RefsAdd(m.Name, testInternal);\n\t\t\n\t\tvar projectId = ProjectId.CreateNewId();\n\t\tAttachFileOf(projectId, fmain);\n\t\tvar adi = new List<DocumentInfo>();\n\t\tforeach (var mf in m.CodeFiles) {\n\t\t\tvar docId = DocumentId.CreateNewId(projectId);\n\t\t\tAttachFileOf(docId, mf.f);\n\t\t\tvar tav = TextAndVersion.Create(SourceText.From(mf.code, Encoding.UTF8), VersionStamp.Default);\n\t\t\tadi.Add(DocumentInfo.Create(docId, mf.f.Name, null, SourceCodeKind.Regular, TextLoader.From(tav), mf.f.ItemPath));\n\t\t\t//note: 'filePath=mf.f.ItemPath' is important. If same path used in multiple projects, symbolfinder finds symbols\n\t\t\t//\twith same name in all projects, although technically it's not the same symbol. This is what we need.\n\t\t}\n\t\t\n\t\t//add project references that are in m.ProjectReferences and in current solution (intersection)\n\t\tList<ProjectReference> pr = null;\n\t\tif (m.ProjectReferences is { } a1) {\n\t\t\tfor (int i = 0; i < a1.Count; i++) {\n\t\t\t\tif (projects.TryGetValue(a1[i].f, out var p1)) {\n\t\t\t\t\t(pr ??= new()).Add(new ProjectReference(p1));\n\t\t\t\t\ta1.RemoveAt(i--);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tvar pri = ProjectInfo.Create(projectId, VersionStamp.Default, m.Name, m.Name, LanguageNames.CSharp, fmain.ItemPath, null,\n\t\t\tm.CreateCompilationOptions(),\n\t\t\tm.CreateParseOptions(),\n\t\t\tadi,\n\t\t\tpr,\n\t\t\tm.References.Refs);\n\t\treturn (pri, m);\n\t}\n\t\n\tclass _FRContext {\n\t\tpublic Solution sol;\n\t\tpublic CodeInfo.Context cd;\n\t\tpublic string info;\n\t}\n\t\n\tpublic static void AttachFileOf(DocumentId docId, FileNode f) { _cwtFO.Add(docId, f); }\n\tpublic static void AttachFileOf(ProjectId projId, FileNode f) { _cwtFO.Add(projId, f); }\n\t\n\tstatic readonly ConditionalWeakTable<object, FileNode> _cwtFO = new();\n\t\n\tpublic static FileNode FileOf(DocumentId id) => _cwtFO.TryGetValue(id, out var v) ? v : null;\n\tpublic static FileNode FileOf(Document doc) => FileOf(doc.Id);\n\tpublic static FileNode FileOf(SyntaxTree t, Solution sol) => FileOf(sol.GetDocument(t));\n\tpublic static FileNode FileOf(ProjectId id) => _cwtFO.TryGetValue(id, out var v) ? v : null;\n\tpublic static FileNode FileOf(Project proj) => FileOf(proj.Id);\n}\n"
  },
  {
    "path": "Au.Editor/Edit/CiQuickInfo.cs",
    "content": "using Microsoft.CodeAnalysis.QuickInfo;\n//using Microsoft.CodeAnalysis.CSharp.QuickInfo;\nusing System.Windows.Documents;\nusing Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp;\n\nnamespace LA;\n\nclass CiQuickInfo {\n\tpublic async Task<Section> GetTextAt(int pos16) {\n\t\t//using var p1 = perf.local();\n\t\tif (!CodeInfo.GetContextAndDocument(out var cd, pos16)) return null;\n\t\t//p1.Next();\n\t\t\n\t\tvar tok = cd.syntaxRoot.FindToken(pos16);\n\t\tvar tspan = tok.Span;\n\t\tbool isInToken = !tspan.IsEmpty && tspan.ContainsOrTouches(pos16);\n\t\tif (isInToken) {\n\t\t\t//print.it(tok.Kind());\n\t\t\t\n\t\t\t//CONSIDER: if over text like `0x123456` or `#123456` or `blue`, and it is a token or in a string, display quick info popup with the color (text and background). And add link \"Edit color\".\n\t\t\t//\tBut problem: `0x123456` can be RGB or BGR. Maybe then display both.\n\t\t\t\n\t\t\t//ignore literals\n\t\t\tif (cd.code[tspan.Start] is (>= '0' and <= '9') or '\\'' or '\"' or '@' or '$' or '{') return null;\n\t\t\tvar tk = tok.Kind(); if (tk is SyntaxKind.InterpolatedStringTextToken or SyntaxKind.TrueKeyword or SyntaxKind.FalseKeyword) return null;\n\t\t\t\n\t\t\t//make easier to get quick info for indexer\n\t\t\tif (tk is SyntaxKind.IdentifierToken && tspan.Length > 1 && pos16 >= tspan.End - 1)\n\t\t\t\tif (GetIndexerToken(cd, tok, out var tok2)) pos16 = tok2.SpanStart;\n\t\t\t\n\t\t\tPanels.Debug.SciMouseHover_(cd);\n\t\t}\n\t\t\n\t\tvar opt1 = QuickInfoOptions.Default with { IncludeNavigationHintsInQuickInfo = false };\n\t\tvar opt2 = new Microsoft.CodeAnalysis.LanguageService.SymbolDescriptionOptions { QuickInfoOptions = opt1 };\n\t\t\n\t\tvar service = QuickInfoService.GetService(cd.document);\n\t\tvar r = await Task.Run(async () => await service.GetQuickInfoAsync(cd.document, pos16, opt2, default));\n\t\t//p1.Next();\n\t\tif (r == null) return null;\n\t\t//this oveload is internal, but:\n\t\t//\t- The public overload does not have an options parameter. In old Roslyn could set options for workspace, but it stopped working.\n\t\t//\t- Roslyn in Debug config asserts \"don't use this function\".\n\t\t\n\t\t//ignore literals again. The above not always works, eg the service gets quickinfo when the mouse is over `}` in `\"string\"}` or `number}`.\n\t\tif (isInToken && r.Span.End <= tspan.Start) return null;\n\t\t\n\t\tvar x = new CiText();\n\t\t\n\t\tif (!r.Sections.IsDefaultOrEmpty) {\n\t\t\t//bool hasDocComm = false;\n\t\t\t//QuickInfoSection descr = null;\n\t\t\tvar a = r.Sections;\n\t\t\tfor (int i = 0; i < a.Length; i++) {\n\t\t\t\tvar se = a[i];\n\t\t\t\t//print.it(se.Kind, se.Text);\n\t\t\t\t\n\t\t\t\tx.StartParagraph();\n\t\t\t\t\n\t\t\t\tif (i == 0) { //image\n\t\t\t\t\tCiUtil.TagsToKindAndAccess(r.Tags, out var kind, out var access);\n\t\t\t\t\tif (kind != CiItemKind.None) {\n\t\t\t\t\t\tif (access != default) x.Image(access);\n\t\t\t\t\t\tx.Image(kind);\n\t\t\t\t\t\tx.Append(\" \");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvar tp = se.TaggedParts;\n\t\t\t\tif (tp[0].Tag == TextTags.LineBreak) { //remove/replace some line breaks in returns and exceptions\n\t\t\t\t\tint lessNewlines = se.Kind switch { QuickInfoSectionKinds.ReturnsDocumentationComments => 1, QuickInfoSectionKinds.Exception => 2, _ => 0 };\n\t\t\t\t\tvar k = new List<TaggedText>(tp.Length - 1);\n\t\t\t\t\tfor (int j = 1; j < tp.Length; j++) {\n\t\t\t\t\t\tvar v = tp[j];\n\t\t\t\t\t\tif (lessNewlines > 0 && j > 1) {\n\t\t\t\t\t\t\tif (v.Tag == TextTags.LineBreak) {\n\t\t\t\t\t\t\t\tif (j == 2) continue; //remove line break after \"Returns:\" etc\n\t\t\t\t\t\t\t\tif (lessNewlines == 2) { //in list of exceptions replace \"\\n  \" with \", \"\n\t\t\t\t\t\t\t\t\tif (++j == tp.Length || tp[j].Tag != TextTags.Space) { j--; continue; }\n\t\t\t\t\t\t\t\t\tv = new(TextTags.Text, \", \");\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tk.Add(v);\n\t\t\t\t\t}\n\t\t\t\t\tx.AppendTaggedParts(k, false);\n\t\t\t\t} else {\n\t\t\t\t\tx.AppendTaggedParts(tp);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tx.EndParagraph();\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (!r.RelatedSpans.IsDefaultOrEmpty) { //when mouse is on `}` or `#endx`, r.RelatedSpans contains spans of the `class etc` or `#if etc`\n\t\t\tforeach (var v in r.RelatedSpans) {\n\t\t\t\tx.StartParagraph();\n\t\t\t\tx.Append(cd.code[v.ToRange()]);\n\t\t\t\tx.EndParagraph();\n\t\t\t}\n\t\t}\n\t\t\n\t\treturn x.Result;\n\t}\n\t\n\tinternal static bool GetIndexerToken(CodeInfo.Context cd, in SyntaxToken tok, out SyntaxToken r) {\n\t\tr = default;\n\t\tDebug.Assert(tok.Kind() is SyntaxKind.IdentifierToken);\n\t\tif (tok.GetNextToken() is var tok2 && tok2.IsKind(SyntaxKind.OpenBracketToken)) {\n\t\t\tvar si = cd.semanticModel.GetSymbolInfo(tok.Parent);\n\t\t\tITypeSymbol t = si.Symbol switch {\n\t\t\t\tIPropertySymbol k => k.Type,\n\t\t\t\tIFieldSymbol k => k.Type,\n\t\t\t\tILocalSymbol k => k.Type,\n\t\t\t\tIParameterSymbol k => k.Type,\n\t\t\t\t_ => null\n\t\t\t};\n\t\t\tif (t != null) {\n\t\t\t\tif (t.TypeKind is TypeKind.Array || t.SpecialType is SpecialType.System_String or SpecialType.System_Array) return false; //indexer info not useful\n\t\t\t\tr = tok2;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Edit/CiSignature.cs",
    "content": "using Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp;\nusing Microsoft.CodeAnalysis.CSharp.Syntax;\nusing Microsoft.CodeAnalysis.Text;\nusing Microsoft.CodeAnalysis.SignatureHelp;\nusing Microsoft.CodeAnalysis.CSharp.SignatureHelp;\n\n//FUTURE: show for lambda parameters. Currently VS does not show too.\n\nnamespace LA;\n\nclass CiSignature {\n\tCiPopupText _textPopup;\n\t_Data _data; //not null while the popup window is visible\n\tCancellationTokenSource _cancelTS;\n\t\n\tclass _Data {\n\t\tpublic SignatureHelpItems r;\n\t\tpublic _Span span;\n\t\tpublic int iSelected, iUserSelected, iRoslynSelected;\n\t\tpublic SciCode sci;\n\t\t\n\t\tpublic bool IsSameSpan(_Span span2) {\n\t\t\treturn span2.start == span.start && span2.fromEnd == span.fromEnd;\n\t\t\t//never mind: we don't check whether text before and after is still the same. Not that important.\n\t\t}\n\t\t\n\t\tpublic bool IsSameArglist(_Span span2, SignatureHelpItems r2) {\n\t\t\tif (!IsSameSpan(span2) || r2.Items.Count != r.Items.Count) return false;\n\t\t\tfor (int i = 0; i < r.Items.Count; i++) {\n\t\t\t\tvar hi1 = r.Items[i] as AbstractSignatureHelpProvider.SymbolKeySignatureHelpItem;\n\t\t\t\tvar hi2 = r2.Items[i] as AbstractSignatureHelpProvider.SymbolKeySignatureHelpItem;\n\t\t\t\tDebug.Assert(!(hi1 == null || hi2 == null));\n\t\t\t\tif (hi1 == null || hi2 == null || hi2.Symbol != hi1.Symbol) return false;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t}\n\t\n\tstruct _Span {\n\t\tpublic int start, fromEnd;\n\t\tpublic _Span(int start, int fromEnd) { this.start = start; this.fromEnd = fromEnd; }\n\t\tpublic _Span(TextSpan span, string code) { this.start = span.Start; this.fromEnd = code.Length - span.End; }\n\t}\n\t\n\tpublic bool IsVisibleUI => _data != null;\n\t\n\tpublic void Cancel() {\n\t\t_cancelTS?.Cancel(); _cancelTS = null;\n\t\t_CancelUI();\n\t}\n\t\n\tvoid _CancelUI() {\n\t\tif (_data == null) return;\n\t\tforeach (var r in _data.sci.ETempRanges_Enum(this)) r.Remove();\n\t\t_data = null;\n\t\t_textPopup?.Hide();\n\t}\n\t\n\tpublic void SciPositionChanged(SciCode doc) {\n\t\tif (_afterCharAdded) { _afterCharAdded = false; return; }\n\t\tif (_data == null) return;\n\t\t_ShowSignature(doc, default);\n\t}\n\tbool _afterCharAdded;\n\t\n\tpublic void SciCharAdded(SciCode doc, char ch, bool methodCompletion = false) {\n\t\tswitch (ch) { case '(' or '[' or '<' or ')' or ']' or '>' or ',': break; default: return; }\n\t\t_ShowSignature(doc, ch, methodCompletion);\n\t\t_afterCharAdded = true;\n\t}\n\t\n\tpublic void ShowSignature(SciCode doc) {\n\t\t_ShowSignature(doc, default);\n\t}\n\t\n\tasync void _ShowSignature(SciCode doc, char ch, bool methodCompletion = false) {\n\t\t//using var p1 = perf.local();\n\t\tif (!CodeInfo.GetContextAndDocument(out var cd, -2) || cd.pos < 2) return; //returns false if position is in meta comments\n\t\t\n\t\t_cancelTS?.Cancel();\n\t\tvar cancelTS = _cancelTS = new CancellationTokenSource();\n\t\tvar cancelToken = cancelTS.Token;\n#if DEBUG\n\t\tif (Debugger.IsAttached) { cancelToken = default; _cancelTS = null; }\n#endif\n\t\t\n\t\tSyntaxNode root = null;\n\t\t//ISignatureHelpProvider provider = null;\n\t\tSignatureHelpItems r = null;\n\t\ttry {\n\t\t\t//could be sync, quite fast, but then sometimes reenters (GetItemsAsync waits/dispatches) and sometimes hangs\n\t\t\tr = await Task.Run(async () => {\n\t\t\t\t//p1.Next();\n\t\t\t\troot = cd.syntaxRoot;\n\t\t\t\t//p1.Next('r');\n\t\t\t\tvar providers = _SignatureHelpProviders;\n\t\t\t\t//print.it(providers);\n\t\t\t\tSignatureHelpItems r = null;\n\t\t\t\tvar trigger = new SignatureHelpTriggerInfo(ch == default ? SignatureHelpTriggerReason.InvokeSignatureHelpCommand : SignatureHelpTriggerReason.TypeCharCommand, ch);\n\t\t\t\tforeach (var p in providers) {\n\t\t\t\t\t//print.it(p);\n\t\t\t\t\tvar r2 = await p.GetItemsAsync(cd.document, cd.pos, trigger, default, cancelToken).ConfigureAwait(false);\n\t\t\t\t\t//never mind: GetItemsAsync may throw exception. Rare.\n\t\t\t\t\t\n\t\t\t\t\tif (cancelToken.IsCancellationRequested) { /*print.it(\"IsCancellationRequested\");*/ return null; } //often\n\t\t\t\t\tif (r2 == null) continue;\n\t\t\t\t\tif (r == null || r2.ApplicableSpan.Start > r.ApplicableSpan.Start) {\n\t\t\t\t\t\tr = r2;\n\t\t\t\t\t\t//provider = p;\n\t\t\t\t\t}\n\t\t\t\t\t//Example: 'print.it(new Something())'.\n\t\t\t\t\t//\tThe first provider probably is for Write (invocation).\n\t\t\t\t\t//\tThen the second is for Something (object creation).\n\t\t\t\t\t//\tWe need the innermost, in this case Something.\n\t\t\t\t}\n\t\t\t\treturn r;\n\t\t\t});\n\t\t}\n\t\tcatch (OperationCanceledException) { /*Debug_.Print(\"canceled\");*/ return; } //never noticed\n\t\tcatch (Exception e1) { Debug_.Print(e1); return; } //Roslyn bug: in certain code throws\n\t\tfinally {\n\t\t\tcancelTS.Dispose();\n\t\t\tif (cancelTS == _cancelTS) _cancelTS = null;\n\t\t}\n\t\t//print.it(r, cancelToken.IsCancellationRequested);\n\t\t\n\t\tif (cancelToken.IsCancellationRequested) return;\n\t\tif (r == null) {\n\t\t\t_CancelUI();\n\t\t\treturn;\n\t\t}\n\t\tDebug.Assert(doc == Panels.Editor.ActiveDoc); //when active doc changed, cancellation must be requested\n\t\tif (cd.pos != doc.aaaCurrentPos16 || (object)cd.code != doc.aaaText) return; //changed while awaiting\n\t\t//p1.Next('s');\n\t\t\n\t\t//print.it($\"<><c orange>pos={cd.pos}, span={r.ApplicableSpan},    nItems={r.Items.Count},  argCount={r.ArgumentCount}, argIndex={r.ArgumentIndex}, argName={r.ArgumentName}, sel={r.SelectedItemIndex},    provider={provider}<>\");\n\t\t\n\t\t//get span of the arglist. r.ApplicableSpan.Start is of the statement, not of the arglist. In chained methods it is the chain start.\n\t\tvar fullSpan = r.ApplicableSpan;\n\t\t//CiUtil.HiliteRange(fullSpan); wait.doEvents(500);\n\t\tvar start = fullSpan.Start;\n\t\tvar tok = root.FindToken(cd.pos);\n\t\tif (tok.Kind() is SyntaxKind.OpenParenToken or SyntaxKind.OpenBracketToken or SyntaxKind.LessThanToken) tok = tok.GetPreviousToken();\n\t\tvar argNode = tok.Parent;\n\t\twhile (argNode != null) {\n\t\t\tint i = argNode.SpanStart; if (i <= start) break;\n\t\t\tif (argNode is BaseArgumentListSyntax or AttributeArgumentListSyntax or TypeArgumentListSyntax) {\n\t\t\t\tstart = i + 1;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t//CiUtil.PrintNode(argNode);\n\t\t\targNode = argNode.Parent;\n\t\t}\n\t\tvar argSpan = new TextSpan(start, fullSpan.End - start);\n\t\t//CiUtil.PrintNode(argNode); CiUtil.HiliteRange(argSpan); //print.it(argSpan);\n\t\t\n\t\tvar span = new _Span(argSpan, cd.code);\n\t\tint iSel = 0, iSel2 = 0;\n\t\tif (r.Items.Count > 1) {\n\t\t\tiSel2 = r.SelectedItemIndex ?? -1;\n\t\t\tif (_data?.IsSameArglist(span, r) ?? false) {\n\t\t\t\tiSel = _data.iUserSelected; //preserve user selection in same session\n\t\t\t\tif (iSel2 < 0) iSel2 = _data.iRoslynSelected; //on error use last good Roslyn selection in same session, like in VS\n\t\t\t} else iSel = -1;\n\t\t}\n\t\t\n\t\t_data = new _Data {\n\t\t\tr = r,\n\t\t\tspan = span,\n\t\t\tiUserSelected = iSel,\n\t\t\tiRoslynSelected = iSel2,\n\t\t\tsci = doc,\n\t\t};\n\t\t\n\t\tif (iSel < 0) iSel = iSel2;\n\t\tif (iSel < 0) {\n\t\t\t//r.SelectedItemIndex is null when cannot resolve overloads, eg when arglist is partially typed. Example: wnd.find(1, );\n\t\t\tiSel = r.SelectedItemIndex ?? (r.SyntacticArgumentCount == 0 ? 0 : -1);\n\t\t\tif (iSel < 0) {\n\t\t\t\tfor (int i = 0; i < r.Items.Count; i++) if (r.Items[i].Parameters.Length >= r.SyntacticArgumentCount) { iSel = i; break; }\n\t\t\t\tif (iSel < 0) {\n\t\t\t\t\tfor (int i = 0; i < r.Items.Count; i++) if (r.Items[i].IsVariadic) { iSel = i; break; }\n\t\t\t\t\tif (iSel < 0) iSel = 0;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tdoc.ETempRanges_Add(this, argSpan.Start, argSpan.End, onLeave: () => {\n\t\t\tif (doc.ETempRanges_Enum(doc.aaaCurrentPos8, this, utf8: true).Any()) return;\n\t\t\t_CancelUI();\n\t\t}, SciCode.TempRangeFlags.NoDuplicate);\n\t\t\n\t\tvar rect = RECT.Union(doc.EGetCaretRectFromPos(fullSpan.Start), doc.EGetCaretRectFromPos(cd.pos));\n\t\tvar rclient = doc.AaWnd.ClientRect;\n\t\trect.left = Math.Clamp(rect.left, 0, rect.right = Math.Clamp(rect.right, 0, rclient.right));\n\t\tdoc.AaWnd.MapClientToScreen(ref rect);\n\t\trect.Width += Dpi.Scale(200, doc.AaWnd);\n\t\trect.left -= 6;\n\t\t\n\t\t_textPopup ??= new CiPopupText(CiPopupText.UsedBy.Signature, onHiddenOrDestroyed: (_, _) => _data = null) {\n\t\t\tOnLinkClick = (ph, e) => ph.Text = _FormatText(e.ToInt(1), userSelected: true)\n\t\t};\n\t\t_textPopup.Text = _FormatText(iSel, userSelected: false);\n\t\t\n\t\tif (!_textPopup.IsVisible) {\n\t\t\tCodeInfo.HideTextPopupAndTempWindows();\n\t\t\tif (CodeInfo._compl.IsVisibleUI) //without this does not show completions with selected enum when typed Function( when first parameter is enum\n\t\t\t\tCodeInfo._compl.Cancel();\n\t\t\tif (methodCompletion) CodeInfo._compl.ShowList(ch); //when autocompletion added (); may need to show enum list \n\t\t}\n\t\t\n\t\t_textPopup.Show(Panels.Editor.ActiveDoc, rect, System.Windows.Controls.Dock.Bottom);\n\t\t\n\t\t//also show Keys/Regex tool?\n\t\t//CiUtil.PrintNode(node);\n\t\tif (argNode is ArgumentListSyntax or BracketedArgumentListSyntax && cd.code.Eq(cd.pos - 1, \"\\\"\\\"\")) {\n\t\t\t//print.it(\"string\");\n\t\t\tvar semo = cd.semanticModel;\n\t\t\tvar token = root.FindToken(cd.pos);\n\t\t\tif (true == token.IsInString(cd.pos, cd.code, out var stringInfo)) {\n\t\t\t\tvar stringFormat = CiUtil.GetParameterStringFormat(stringInfo.stringNode, semo, true);\n\t\t\t\tif (stringFormat != 0)\n\t\t\t\t\tCodeInfo._tools.ShowForStringParameter(stringFormat, cd, stringInfo, _textPopup.PopupWindow.Hwnd);\n\t\t\t}\n\t\t}\n\t}\n\t\n\tSystem.Windows.Documents.Section _FormatText(int iSel, bool userSelected) {\n\t\t_data.iSelected = iSel;\n\t\tif (userSelected) _data.iUserSelected = iSel;\n\t\t\n\t\tvar r = _data.r;\n\t\tISymbol currentItem = null;\n\t\tSignatureHelpParameter currentParameter = null;\n\t\tvar x = new CiText();\n\t\t\n\t\t//print.clear();\n\t\tfor (int i = 0; i < r.Items.Count; i++) {\n\t\t\tvar sh = r.Items[i];\n\t\t\tif (sh is AbstractSignatureHelpProvider.SymbolKeySignatureHelpItem kk) {\n\t\t\t\tvar sym = kk.Symbol;\n\t\t\t\tif (i == iSel) currentItem = sym;\n\t\t\t\tx.StartOverload(i == iSel, i);\n#if false\n\t\t\t\tx.AppendTaggedParts(sh.PrefixDisplayParts); //works, but formats not as I like (too much garbage). Has bugs with tuples.\n#else\n\t\t\t\t//if(nt != null) {\n\t\t\t\t//\tprint.it(1, nt.IsGenericType, nt.IsTupleType, nt.IsUnboundGenericType, nt.Arity, nt.CanBeReferencedByName);\n\t\t\t\t//\tprint.it(2, nt.IsAnonymousType, nt.IsDefinition, nt.IsImplicitlyDeclared, nt.Kind, nt.TypeKind);\n\t\t\t\t//\tprint.it(3, nt.MemberNames);\n\t\t\t\t//\tprint.it(4, nt.Name, nt.MetadataName, nt.OriginalDefinition, nt.TupleUnderlyingType);\n\t\t\t\t//\tprint.it(\"TypeParameters:\");\n\t\t\t\t//\tprint.it(nt.TypeParameters);\n\t\t\t\t//\tprint.it(\"TypeArguments:\");\n\t\t\t\t//\tprint.it(nt.TypeArguments);\n\t\t\t\t//\tprint.it(\"TupleElements:\");\n\t\t\t\t//\ttry { var te = nt.TupleElements; if(!te.IsDefault) print.it(te); } catch(Exception e1) { print.it(e1.ToStringWithoutStack()); }\n\t\t\t\t//\tprint.it(\"---\");\n\t\t\t\t//}\n\t\t\t\t\n\t\t\t\tint isTuple = 0; //1 ValueTuple<...>, 2 (...)\n\t\t\t\tvar nt = sym as INamedTypeSymbol;\n\t\t\t\tif (nt != null && nt.IsTupleType) isTuple = nt.IsDefinition ? 1 : 2;\n\t\t\t\t\n\t\t\t\tif (isTuple == 1) x.Append(\"ValueTuple\"); //AppendSymbolWithoutParameters formats incorrectly\n\t\t\t\telse if (isTuple == 0) x.AppendSymbolWithoutParameters(sym);\n\t\t\t\tstring b1 = \"(\", b2 = \")\";\n\t\t\t\tif (nt != null) {\n\t\t\t\t\tif (nt.IsGenericType && isTuple != 2) { b1 = \"<\"; b2 = \">\"; }\n\t\t\t\t} else if (sym is IPropertySymbol) {\n\t\t\t\t\tb1 = \"[\"; b2 = \"]\";\n\t\t\t\t}\n\t\t\t\tx.Append(b1);\n#endif\n\t\t\t\tint iArg = r.SemanticParameterIndex, lastParam = sh.Parameters.Length - 1;\n\t\t\t\tint selParam = iArg <= lastParam ? iArg : (sh.IsVariadic ? lastParam : -1);\n\t\t\t\tif (!r.ArgumentName.NE()) {\n\t\t\t\t\tvar pa = sh.Parameters;\n\t\t\t\t\tfor (int pi = 0; pi < pa.Length; pi++) if (pa[pi].Name == r.ArgumentName) { selParam = pi; break; }\n\t\t\t\t}\n\t\t\t\tx.AppendParameters(sym, selParam, sh);\n\t\t\t\t//x.AppendParameters(sh, selParam); //works, but formats not as I like (too much garbage)\n#if false\n\t\t\t\tx.AppendTaggedParts(sh.SuffixDisplayParts);\n#else\n\t\t\t\tx.Append(b2);\n#endif\n\t\t\t\tif (i == iSel && selParam >= 0) currentParameter = sh.Parameters[selParam];\n\t\t\t\tx.EndOverload(i == iSel);\n\t\t\t} else {\n\t\t\t\tDebug_.Print(sh);\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (currentItem != null) {\n\t\t\tvar tt = r.Items[iSel].DocumentationFactory?.Invoke(default);\n\t\t\tbool haveDoc = tt?.Any() ?? false;\n\t\t\tstring helpUrl = CiUtil.GetSymbolHelpUrl(currentItem, out _);\n\t\t\tstring sourceUrl = CiGoTo.GetLinkData(currentItem);\n\t\t\tbool haveLinks = helpUrl != null || sourceUrl != null;\n\t\t\tif (haveDoc || haveLinks) {\n\t\t\t\tx.StartParagraph();\n\t\t\t\tif (haveDoc) x.AppendTaggedParts(tt, false);\n\t\t\t\tif (haveLinks) {\n\t\t\t\t\tif (haveDoc) x.Append(\" \");\n\t\t\t\t\tx.AppendSymbolLinks(helpUrl, sourceUrl);\n\t\t\t\t\tx.Append(\".\");\n\t\t\t\t}\n\t\t\t\tx.EndParagraph();\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (currentParameter != null && !currentParameter.Name.NE()) { //if tuple, Name is \"\" and then would be exception\n\t\t\tx.StartParagraph(\"parameter\");\n\t\t\tx.Bold(currentParameter.Name); x.Append(\":  \");\n\t\t\tvar tt = currentParameter.DocumentationFactory?.Invoke(default);\n\t\t\t//rejected. Use inheritdoc. This would cover only part of cases.\n\t\t\t//if (!tt.Any()) { //if this parameter of this overload is undocumented, look for parameter with same name in other overloads\n\t\t\t//\tfor (int i = 0; i < r.Items.Count; i++) {\n\t\t\t//\t\tif (i == iSel) continue;\n\t\t\t//\t\tvar p = r.Items[i].Parameters.FirstOrDefault(o => o.Name == currentParameter.Name);\n\t\t\t//\t\tif (p != null) {\n\t\t\t//\t\t\ttt = p.DocumentationFactory?.Invoke(default);\n\t\t\t//\t\t\tif (tt.Any()) break;\n\t\t\t//\t\t}\n\t\t\t//\t}\n\t\t\t//}\n\t\t\tx.AppendTaggedParts(tt, false);\n\t\t\tx.EndParagraph();\n\t\t}\n\t\t\n\t\treturn x.Result;\n\t}\n\t\n\tstatic List<ISignatureHelpProvider> _GetSignatureHelpProviders() {\n\t\tvar a = new List<ISignatureHelpProvider>();\n\t\tvar types = Assembly.GetAssembly(typeof(InvocationExpressionSignatureHelpProvider)).DefinedTypes;\n\t\tforeach (var t in types.Where(t =>\n\t\t\tt.Namespace == \"Microsoft.CodeAnalysis.CSharp.SignatureHelp\"\n\t\t\t&& t.IsDefined(typeof(ExportSignatureHelpProviderAttribute))\n\t\t\t//&& t.ImplementedInterfaces.Contains(typeof(ISignatureHelpProvider)) && !t.IsAbstract\n\t\t\t)) {\n\t\t\t//print.it(t);\n\t\t\tvar c = t.GetConstructor(Type.EmptyTypes);\n\t\t\tDebug_.PrintIf(c == null, t.ToString());\n\t\t\tif (c == null) continue;\n\t\t\tvar o = c.Invoke(null) as ISignatureHelpProvider; Debug.Assert(o != null); if (o == null) continue;\n\t\t\ta.Add(o);\n\t\t}\n\t\treturn a;\n\t}\n\t\n\tList<ISignatureHelpProvider> _SignatureHelpProviders => _shp ??= _GetSignatureHelpProviders();\n\tList<ISignatureHelpProvider> _shp;\n\t\n\tpublic bool OnCmdKey(KKey key) {\n\t\tif (_data != null) {\n\t\t\tswitch (key) {\n\t\t\tcase KKey.Escape:\n\t\t\t\tCancel();\n\t\t\t\treturn true;\n\t\t\tcase KKey.Down:\n\t\t\tcase KKey.Up:\n\t\t\t\tint i = _data.iSelected, n = _data.r.Items.Count;\n\t\t\t\tif (key == KKey.Down) {\n\t\t\t\t\tif (++i >= n) i = 0;\n\t\t\t\t} else {\n\t\t\t\t\tif (--i < 0) i = n - 1;\n\t\t\t\t}\n\t\t\t\tif (i != _data.iSelected) _textPopup.Text = _FormatText(i, userSelected: true);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Edit/CiSnippets.cs",
    "content": "extern alias CAW;\n\nusing Microsoft.CodeAnalysis;\nusing CAW::Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp;\nusing Microsoft.CodeAnalysis.CSharp.Syntax;\nusing Microsoft.CodeAnalysis.Text;\nusing Microsoft.CodeAnalysis.CSharp.Extensions;\nusing Microsoft.CodeAnalysis.Shared.Extensions;\nusing CAW::Microsoft.CodeAnalysis.Shared.Extensions;\nusing Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;\n\nusing System.Xml.Linq;\nusing Au.Controls;\nusing System.Windows.Input;\n\nnamespace LA;\n\nstatic class CiSnippets {\n\tclass _CiComplItemSnippet : CiComplItem {\n\t\tpublic readonly XElement x;\n\t\tpublic _Context context;\n\t\tpublic readonly string customFile, sortText;\n\t\t\n\t\tpublic _CiComplItemSnippet(string name, XElement x, string customFile) : base(CiComplProvider.Snippet, name, CiItemKind.Snippet) {\n\t\t\tthis.x = x;\n\t\t\tthis.customFile = customFile;\n\t\t\tsortText = name.Ends(\"Snippet\") ? name[..^7] : name.Ends(\"Surround\") ? name[..^8] : name;\n\t\t}\n\t}\n\t\n\tstatic List<_CiComplItemSnippet> s_items;\n\t\n\t[Flags]\n\tenum _Context {\n\t\tNone,\n\t\tNamespace = 1, //global, namespace{ }\n\t\tType = 2, //class{ }, struct{ }, interface{ }\n\t\tFunction = 4, //method{ }, lambda{ }\n\t\tArrow = 8, //lambda=>, function=>\n\t\tAttributes = 16, //[Attributes]\n\t\tUnknown = 32,\n\t\tAny = 0xffff,\n\t\tLine = 0x10000, //at start of line\n\t\t/*\n\t\t\n\t\tA context specifies where in code to add the snippets to the completion list. Several contexts can be combined.\n\t\t- **Function** - inside function body. Also in the main script code (top-level statements).\n\t\t- **Type** - inside a class, struct or interface but not inside functions. Use for snippets that insert entire methods, properties, etc.\n\t\t- **Namespace** - outside of types. Use for snippets that insert entire types.\n\t\t- **Attributes** - use for snippets that insert an `[attribute]`.\n\t\t- **Line** - at the start of a line. For example check **Line** and **Any** for snippets that insert a `#directive`.\n\t\t- **Any** - anywhere.\n\t\t- **None** - nowhere.\n\t\t\n\t\t*/\n\t}\n\t\n\tstatic _Context s_context;\n\t\n\tpublic static void AddSnippets(List<CiComplItem> items, TextSpan span, CompilationUnitSyntax root, string code, bool surround, CSharpSyntaxContext syncon = null) {\n\t\tif (syncon != null) {\n\t\t\t//CSharpSyntaxContext was discovered later and therefore almost not used here.\n\t\t\tif (syncon.IsObjectCreationTypeContext) return;\n\t\t\t//CiUtil.DebugGetContextType(syncon);\n\t\t}\n\t\t\n\t\t_Context context = _Context.Unknown;\n\t\tint pos = span.Start;\n\t\tbool inDirective = pos > 0 && code[pos - 1] == '#' && !surround; //when invoked the list after #, span does not include #\n\t\t\n\t\tif (inDirective) {\n\t\t} else if (pos < root.GetHeaderLength()) {\n\t\t} else {\n\t\t\t//get node from start\n\t\t\tvar token = root.FindToken(pos);\n\t\t\tvar node = token.Parent;\n\t\t\t\n\t\t\t//find ancestor/self that contains pos inside\n\t\t\twhile (node != null && !node.Span.ContainsInside(pos)) node = node.Parent;\n\t\t\t\n\t\t\tswitch (node) {\n\t\t\tcase BlockSyntax:\n\t\t\tcase SwitchSectionSyntax: //between case: and break;\n\t\t\tcase ElseClauseSyntax:\n\t\t\tcase LabeledStatementSyntax:\n\t\t\tcase IfStatementSyntax s1 when pos > s1.CloseParenToken.SpanStart:\n\t\t\tcase WhileStatementSyntax s2 when pos > s2.CloseParenToken.SpanStart:\n\t\t\tcase DoStatementSyntax s3 when pos < s3.WhileKeyword.SpanStart:\n\t\t\tcase ForStatementSyntax s4 when pos > s4.CloseParenToken.SpanStart:\n\t\t\tcase CommonForEachStatementSyntax s5 when pos > s5.CloseParenToken.SpanStart:\n\t\t\tcase LockStatementSyntax s6 when pos > s6.CloseParenToken.SpanStart:\n\t\t\tcase FixedStatementSyntax s7 when pos > s7.CloseParenToken.SpanStart:\n\t\t\tcase UsingStatementSyntax s8 when pos > s8.CloseParenToken.SpanStart:\n\t\t\t\tcontext = _Context.Function;\n\t\t\t\tbreak;\n\t\t\tcase TypeDeclarationSyntax td when pos > td.OpenBraceToken.Span.Start: //{ } of class, struct, interface, extension\n\t\t\t\tcontext = _Context.Type;\n\t\t\t\tbreak;\n\t\t\tcase NamespaceDeclarationSyntax ns when pos > ns.OpenBraceToken.Span.Start:\n\t\t\tcase FileScopedNamespaceDeclarationSyntax ns2 when pos >= ns2.SemicolonToken.Span.End:\n\t\t\t\tcontext = _Context.Namespace;\n\t\t\t\tbreak;\n\t\t\tcase CompilationUnitSyntax:\n\t\t\tcase null:\n\t\t\t\tcontext = _Context.Namespace | _Context.Function; //Function for top-level statements. TODO3: only if in correct place.\n\t\t\t\tbreak;\n\t\t\tcase LambdaExpressionSyntax:\n\t\t\tcase ArrowExpressionClauseSyntax: //like void F() =>here\n\t\t\t\tcontext = _Context.Arrow;\n\t\t\t\tbreak;\n\t\t\tcase AttributeListSyntax:\n\t\t\t\tcontext = _Context.Attributes;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tif (span.IsEmpty) { //if '=> here;' or '=> here)' etc, use =>\n\t\t\t\t\tvar t2 = token.GetPreviousToken();\n\t\t\t\t\tif (t2.IsKind(SyntaxKind.EqualsGreaterThanToken) && t2.Parent is LambdaExpressionSyntax) context = _Context.Arrow;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\ts_context = context;\n\t\t\n\t\tif (s_items == null) {\n\t\t\tvar a = new List<_CiComplItemSnippet>();\n\t\t\tforeach (var f in filesystem.enumFiles(AppSettings.DirBS, \"*Snippets.xml\")) _LoadFile(f.FullPath, true);\n\t\t\t_LoadFile(DefaultFile, false);\n\t\t\tif (a.Count == 0) return;\n\t\t\ta.Sort((x, y) => { //for the surround list\n\t\t\t\tint r = CiUtil.SortComparer.Compare(x.sortText, y.sortText);\n\t\t\t\tif (r == 0) return (x.customFile != null ? 0 : 1) - (y.customFile != null ? 0 : 1); //custom first\n\t\t\t\treturn r;\n\t\t\t});\n\t\t\t_DetectContextsOfSnippets(a);\n\t\t\ts_items = a;\n\t\t\t\n\t\t\tvoid _LoadFile(string file, bool custom) {\n\t\t\t\ttry {\n\t\t\t\t\tvar hidden = DSnippets.GetHiddenSnippets(custom ? pathname.getName(file) : \"default\");\n\t\t\t\t\tif (hidden?.Contains(\"\") == true) return;\n\t\t\t\t\t\n\t\t\t\t\tvar customFile = custom ? pathname.getName(file) : null;\n\t\t\t\t\tvar xroot = LoadSnippetsFile_(file);\n\t\t\t\t\tif (xroot == null) return;\n\t\t\t\t\tforeach (var xs in xroot.Elements(\"snippet\")) {\n\t\t\t\t\t\tvar name = xs.Attr(\"name\");\n\t\t\t\t\t\tif (hidden?.Contains(name) == true) continue;\n\t\t\t\t\t\ta.Add(new _CiComplItemSnippet(name, xs, customFile));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (Exception ex) { print.it(\"Failed to load snippets from \" + file + \"\\r\\n\\t\" + ex.ToStringWithoutStack()); }\n\t\t\t}\n\t\t}\n\t\t\n\t\tbool isLineStart = CiUtil.IsLineStart(code, pos - (inDirective ? 1 : 0));\n\t\t\n\t\tfor (int i = 0; i < s_items.Count; i++) {\n\t\t\tvar v = s_items[i];\n\t\t\tif (!v.context.HasAny(context)) continue;\n\t\t\tif (v.context.Has(_Context.Line) && !isLineStart) continue;\n\t\t\tif (!surround && v.Text.Ends(\"Surround\")) continue;\n\t\t\tif (inDirective) {\n\t\t\t\tif (v.Text[0] != '#') continue;\n\t\t\t\tv = new _CiComplItemSnippet(v.Text[1..], v.x, v.customFile); //like in VS. Else typing-filtering does not work.\n\t\t\t}\n\t\t\tv.group = 0; v.hidden = 0; v.hilite = 0; v.moveDown = 0;\n\t\t\tv.GetCI().Span = span;\n\t\t\titems.Add(v);\n\t\t}\n\t}\n\t\n\tinternal static XElement LoadSnippetsFile_(string file) {\n\t\tvar xroot = XmlUtil.LoadElem(file);\n\t\tif (xroot.Name != \"snippets\") {\n\t\t\tif (xroot.Name == \"Au.Snippets\") {\n\t\t\t\txroot = _ConvertOldFormat(xroot);\n\t\t\t\ttry { xroot.Save(file); } catch { }\n\t\t\t} else return null;\n\t\t}\n\t\treturn xroot;\n\t\t\n\t\tstatic XElement _ConvertOldFormat(XElement xRootOld) {\n\t\t\tvar xRootNew = new XElement(\"snippets\");\n\t\t\tforeach (var xg in xRootOld.Elements(\"group\")) {\n\t\t\t\tforeach (var xs in xg.Elements(\"snippet\")) {\n\t\t\t\t\t_ConvertSnippet(xs);\n\t\t\t\t\txRootNew.Add(xs);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn xRootNew;\n\t\t\t\n\t\t\tstatic void _ConvertSnippet(XElement xs) {\n\t\t\t\tif (!xs.HasElements) _ConvertCode(xs);\n\t\t\t\telse foreach (var x in xs.Elements(\"list\")) _ConvertCode(x);\n\t\t\t\t\n\t\t\t\tstatic void _ConvertCode(XElement x) {\n\t\t\t\t\tif (x.Value is string v) {\n\t\t\t\t\t\tif (v.Find(\"$end$\") is int i1 && i1 >= 0) { //$end$xxx$end$ -> ${1:xxx}, or $end$ -> $0\n\t\t\t\t\t\t\tint i1end = i1 + 5;\n\t\t\t\t\t\t\tif (v.Find(\"$end$\", i1end) is int i2 && i2 > 0) v = v.ReplaceAt(i1..(i2 + 5), \"${1:\" + v[i1end..i2] + \"}\");\n\t\t\t\t\t\t\telse v = v.ReplaceAt(i1..i1end, i1end < v.Length && v[i1end] is >= '0' and <= '9' ? \"${0}\" : \"$0\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\tv = v.Replace(\"$random$\", \"${RANDOM}\");\n\t\t\t\t\t\tv = v.Replace(\"$guid$\", \"${GUID}\");\n\t\t\t\t\t\tv = v.Replace(\"$var$\", \"${VAR}\");\n\t\t\t\t\t\tx.Value = v;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t\n\tstatic void _DetectContextsOfSnippets(List<_CiComplItemSnippet> a) {\n\t\tregexp rx1 = new(@\"\\$(?|(VAR|RANDOM|RANDOM_HEX|TM_FILENAME_BASE)\\b|\\{((?1))\\})\"),\n\t\t\trx2 = new(@\"\\$(?|([1-9]\\d*)(?!\\d)|\\{((?1))\\})\"),\n\t\t\trx3 = new(@\"\\$(?:\\{[A-Z_]+\\}|[A-Z_]+\\b|0|\\{0\\})\"),\n\t\t\trx4 = new(@\"\\$\\{\\d+:(.*?)\\}\");\n\t\t\n\t\tParallel.ForEach(a, v => {\n\t\t\tif (v.x.Attr(\"context\") is string s) {\n\t\t\t\tv.context = _GetFromAttr(s);\n\t\t\t} else {\n\t\t\t\tvar x = v.x.HasElements ? v.x.Elements().First() : v.x;\n\t\t\t\tv.context = _Detect(x.Value, v.Text);\n\t\t\t}\n\t\t});\n\t\t\n\t\t_Context _Detect(string code, string name) {\n\t\t\tif (code.NE()) return _Context.None;\n\t\t\t\n\t\t\tcode = rx1.Replace(code, \"$1\");\n\t\t\tcode = rx2.Replace(code, \"i\");\n\t\t\tcode = rx3.Replace(code, \"\");\n\t\t\tcode = rx4.Replace(code, \"$1\");\n\t\t\t\n\t\t\t//bool debug = name.Starts(\"ctor\");\n\t\t\t//if (debug) {\n\t\t\t//\tprint.it($\"<><lc #B3DF00>{name}<>\\r\\n<\\a>{code}</\\a>\");\n\t\t\t//}\n\t\t\t\n\t\t\ttry {\n\t\t\t\tvar cu = CiUtil.CreateSyntaxTree(code);\n\t\t\t\tif (cu.Usings.Any() || cu.Externs.Any() || cu.AttributeLists.Any()) return _Context.Namespace;\n\t\t\t\tif (!cu.Members.Any()) {\n\t\t\t\t\tif (cu.ContainsDirectives) return _Context.Any | _Context.Line;\n\t\t\t\t\tif (code.Starts(\"/// \")) return _Context.Namespace | _Context.Type | _Context.Line;\n\t\t\t\t\treturn _Context.Any;\n\t\t\t\t}\n\t\t\t\tif (cu.Members.Any(SyntaxKind.GlobalStatement)) {\n\t\t\t\t\tif (!cu.Members.All(o => o is GlobalStatementSyntax)) {\n\t\t\t\t\t\tif (cu.Members[0] is GlobalStatementSyntax) return _Context.Namespace; //TLS + types\n\t\t\t\t\t\t//Debug_.Print($\"{name}: GlobalStatement after non-global\");\n\t\t\t\t\t\tif (_TryInClass()) return _Context.Type;\n\t\t\t\t\t\treturn _Context.Function;\n\t\t\t\t\t}\n\t\t\t\t\tforeach (GlobalStatementSyntax gs in cu.Members) {\n\t\t\t\t\t\tvar stat = gs.Statement;\n\t\t\t\t\t\tif (stat is LocalFunctionStatementSyntax) { //can be local or member function\n\t\t\t\t\t\t\tif (_TryInClass()) return _Context.Type;\n\t\t\t\t\t\t} else if (stat is LocalDeclarationStatementSyntax lds) { //can be local or member variable\n\t\t\t\t\t\t\tif (lds.Declaration.Type.IsVar) return _Context.Function;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tif (_TryInClass()) return _Context.Type;\n\t\t\t\t\t\t\t//if (stat is ExpressionStatementSyntax ess && ess.SemicolonToken.IsMissing && ess.Expression is InvocationExpressionSyntax && cu.Members.Count == 1) return _Context.Attributes; //`Attribute(...)` //rejected\n\t\t\t\t\t\t\treturn _Context.Function;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn _Context.Type | _Context.Function;\n\t\t\t\t} else {\n\t\t\t\t\tif (cu.Members.Any(o => o is BaseNamespaceDeclarationSyntax)) return _Context.Namespace;\n\t\t\t\t\tif (cu.Members.All(o => o is BaseTypeDeclarationSyntax)) return _Context.Namespace | _Context.Type;\n\t\t\t\t\treturn _Context.Type;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tbool _TryInClass(bool debug = false) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tint n1 = cu.GetDiagnostics().Count();\n\t\t\t\t\t\tif (n1 == 0) return false;\n\t\t\t\t\t\tvar cu2 = CiUtil.CreateSyntaxTree(\"class C{\\r\\n\" + code + \"\\r\\n}\");\n\t\t\t\t\t\tvar d2 = cu2.GetDiagnostics();\n\t\t\t\t\t\tint n2 = d2.Count();\n\t\t\t\t\t\tif (n2 >= n1) return false;\n\t\t\t\t\t\tif (n2 > 0 && d2.Any(o => o.Code == 1519)) return false; //\"Invalid token 'token' in class, struct, or interface member declaration\". Eg elseSnippet.\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t\tcatch { return false; }\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch { return _Context.Any; }\n\t\t}\n\t\t\n\t\tstatic _Context _GetFromAttr(string s) {\n\t\t\t_Context r = 0;\n\t\t\tforeach (var se in s.SplitSE(.., '|')) {\n\t\t\t\tr |= s.AsSpan(se.Range) switch {\n\t\t\t\t\t\"Function\" => _Context.Function | _Context.Arrow,\n\t\t\t\t\t\"Type\" => _Context.Type,\n\t\t\t\t\t\"Namespace\" => _Context.Namespace,\n\t\t\t\t\t\"Attributes\" => _Context.Attributes,\n\t\t\t\t\t\"Any\" => _Context.Any,\n\t\t\t\t\t\"Line\" => _Context.Line,\n\t\t\t\t\t_ => 0\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn r;\n\t\t}\n\t}\n\t\n\tpublic static void Reload() => s_items = null;\n\t\n\tpublic static int Compare(CiComplItem i1, CiComplItem i2) {\n\t\tif (i1 is _CiComplItemSnippet x && i2 is _CiComplItemSnippet y) {\n\t\t\treturn (x.customFile != null ? 0 : 1) - (y.customFile != null ? 0 : 1); //sort custom first\n\t\t}\n\t\treturn 0;\n\t}\n\t\n\tpublic static System.Windows.Documents.Section GetDescription(CiComplItem item) {\n\t\tvar snippet = item as _CiComplItemSnippet;\n\t\tvar m = new CiText();\n\t\tm.StartParagraph();\n\t\tm.Append(\"Snippet \");\n\t\tm.StartBold(); m.Hyperlink($\"^snippet {snippet.Text}|{snippet.customFile ?? \"default\"}\", item.Text); m.EndBold(); //DSnippets.ShowSingle(snippet.Text);\n\t\tm.Append(\".\");\n\t\t_AppendInfo(snippet.x);\n\t\tbool isList = snippet.x.HasElements;\n\t\tif (isList) {\n\t\t\tforeach (var v in snippet.x.Elements(\"list\")) {\n\t\t\t\tm.Separator();\n\t\t\t\tm.StartParagraph();\n\t\t\t\tm.Append(StringUtil.RemoveUnderlineChar(v.Attr(\"item\")));\n\t\t\t\t_AppendInfo(v);\n\t\t\t\t_AppendCode(v);\n\t\t\t}\n\t\t} else {\n\t\t\t_AppendCode(snippet.x);\n\t\t}\n\t\tif (snippet.x.Attr(out string more, \"more\")) {\n\t\t\tif (isList) m.Separator();\n\t\t\tm.StartParagraph(); m.Append(more); m.EndParagraph();\n\t\t}\n\t\t\n\t\t//CONSIDER: add link \"Edit\". User suggestion.\n\t\t\n\t\treturn m.Result;\n\t\t\n\t\tvoid _AppendInfo(XElement x) {\n\t\t\tif (x.Attr(out string info, \"info\")) m.Append(\" \" + info);\n\t\t\tm.EndParagraph();\n\t\t}\n\t\t\n\t\tvoid _AppendCode(XElement x) {\n\t\t\tm.CodeBlock(x.Value.Replace(\"$end$\", \"\"));\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Surrounds a text range with a snippet.\n\t/// </summary>\n\t/// <param name=\"snippetXml\">If null, shows menu with all \"surround\" snippets that are valid at current place in code. Else can be either full snippet XML or just code; must contain <c>${SELECTED_TEXT}</c>.</param>\n\t/// <param name=\"range\">If null, uses the selected range, or current statement etc if there is no selection.</param>\n\t/// <remarks>\n\t/// Can also insert using directives etc where need.\n\t/// Formats the text and snippet.\n\t/// Can start snippet mode (Tab-navigation etc).\n\t/// </remarks>\n\tpublic static void Surround(string snippetXml = null, Range? range = null) {\n\t\tif (!CodeInfo.GetContextAndDocument(out var k)) return;\n\t\tvar (from, to) = range?.GetStartEnd(k.code.Length) ?? GetSurroundRange(k);\n\t\t\n\t\tXElement x;\n\t\tif (snippetXml != null) {\n\t\t\tif (!snippetXml.Starts('<')) snippetXml = \"<snippet><![CDATA[\" + snippetXml + \"]]></snippet>\";\n\t\t\tx = XElement.Parse(snippetXml);\n\t\t} else {\n\t\t\tList<CiComplItem> a = new();\n\t\t\tAddSnippets(a, new(from, 0), k.syntaxRoot, k.code, true);\n\t\t\tif (a.Count == 0) return;\n\t\t\t\n\t\t\tvar m = new popupMenu { RawText = true };\n\t\t\tList<XElement> list = new();\n\t\t\tforeach (_CiComplItemSnippet snippet in a) {\n\t\t\t\tstring name = null;\n\t\t\t\tif (snippet.x.HasElements) {\n\t\t\t\t\tlist.Clear();\n\t\t\t\t\tforeach (var v in snippet.x.Elements(\"list\")) if (_CanAdd(v)) list.Add(v);\n\t\t\t\t\tif (list.Count > 3) {\n\t\t\t\t\t\tvar sub = new popupMenu { RawText = true };\n\t\t\t\t\t\tforeach (var v in list) _Add(sub, v, true);\n\t\t\t\t\t\tm.Submenu(name, () => sub);\n\t\t\t\t\t} else if (list.Count > 0) {\n\t\t\t\t\t\tforeach (var v in list) _Add(m, v, true);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif (_CanAdd(snippet.x)) _Add(m, snippet.x, false);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tbool _CanAdd(XElement x) => x.Value.Contains(\"${SELECTED_TEXT}\");\n\t\t\t\t\n\t\t\t\tvoid _Add(popupMenu m, XElement x, bool listItem) {\n\t\t\t\t\tstring s = name;\n\t\t\t\t\tif (s == null) {\n\t\t\t\t\t\ts = snippet.Text;\n\t\t\t\t\t\tif (s.Like(\"*?Snippet\")) s = s[..^7];\n\t\t\t\t\t\telse if (s.Like(\"*?Surround\")) s = s[..^8];\n\t\t\t\t\t\tname = s;\n\t\t\t\t\t}\n\t\t\t\t\tif (listItem) s = s + \"  |  \" + StringUtil.RemoveUnderlineChar(x.Attr(\"item\"));\n\t\t\t\t\tvar v = m.Add(s);\n\t\t\t\t\tv.Tag = x;\n\t\t\t\t\tv.Tooltip = snippet.x.Attr(\"info\") + \"\\n\\n\" + x.Value;\n\t\t\t\t}\n\t\t\t\t//CONSIDER: hotkeys for surround snippets.\n\t\t\t}\n\t\t\tif (0 == m.Show()) return;\n\t\t\tx = m.Result.Tag as XElement;\n\t\t}\n\t\t\n\t\tif (to - from > 1 && k.code[to - 1] == '\\n') {\n\t\t\tif (k.code[--to - 1] == '\\r') to--;\n\t\t}\n\t\t\n\t\t_Commit(k.sci, from, to, x, k.code[from..to]);\n\t}\n\t\n\t/// <summary>\n\t/// Gets range for surround.\n\t/// If there is selection, returns the selected range.\n\t/// Else if caret is inside or touches a statement etc, gets its full span.\n\t/// Else returns empty range at caret position.\n\t/// </summary>\n\tpublic static (int start, int end) GetSurroundRange(CodeInfo.Context k) {\n\t\tvar (from, to) = k.sci.aaaSelection(true);\n\t\tif (from == to) {\n\t\t\tvar stat = k.syntaxRoot.FindToken(from).Parent.GetStatementEtc(from);\n\t\t\tif (stat is not (null or BlockSyntax)) {\n\t\t\t\tvar span = stat.GetRealFullSpan(minimalLeadingTrivia: !true);\n\t\t\t\tif (span.ContainsOrTouches(from)) (from, to) = span;\n\t\t\t}\n\t\t\tif (to == from && to > 0 && !(stat is BlockSyntax && stat.Span.ContainsInside(from))) from = to = k.code.LastIndexOf('\\n', to - 1) + 1;\n\t\t}\n\t\treturn (from, to);\n\t}\n\t\n\tpublic static void Commit(SciCode doc, CiComplItem item, int codeLenDiff) {\n\t\tdoc.SnippetMode_?.End();\n\t\t\n\t\tvar snippet = item as _CiComplItemSnippet;\n\t\tvar x = snippet.x;\n\t\t\n\t\t//list of snippets?\n\t\tif (x.HasElements) {\n\t\t\tvar a = x.Elements(\"list\").ToArray();\n\t\t\tvar m = new popupMenu();\n\t\t\tforeach (var v in a) m.Add(v.Attr(\"item\"));\n\t\t\tm.FocusedItem = m.Items.First();\n\t\t\tint g = m.Show(PMFlags.ByCaret | PMFlags.Underline);\n\t\t\tif (g == 0) return;\n\t\t\tx = a[g - 1];\n\t\t}\n\t\t\n\t\tvar span = item.GetCI().Span;\n\t\t_Commit(doc, span.Start, span.End + codeLenDiff, x);\n\t}\n\t\n\tstatic void _Commit(SciCode doc, int pos, int endPos, XElement x, string surroundText = null) {\n\t\tdoc.SnippetMode_?.End();\n\t\t\n\t\tvar xSnippet = x.Name == \"list\" ? x.Parent : x;\n\t\t\n\t\tstring s = x.Value;\n\t\t\n\t\t//##directive -> #directive\n\t\tif (s.Starts('#') && doc.aaaText.Eq(pos - 1, '#')) s = s[1..];\n\t\t\n\t\t//get variable name from code\n\t\tstring varName = null;\n\t\tif (_GetAttr(\"var\", out string attrVar)) {\n\t\t\tif (attrVar.RxMatch(@\"^(.+?), *(.+)$\", out var m)) {\n\t\t\t\ttry {\n\t\t\t\t\tvar t = CiUtil.GetNearestLocalVariableOfType(m[1].Value);\n\t\t\t\t\tvarName = t?.Name ?? m[2].Value;\n\t\t\t\t}\n\t\t\t\tcatch (ArgumentException ex1) { print.it($\"Error in {xSnippet.Attr(\"name\")}: {ex1.Message}\"); }\n\t\t\t}\n\t\t}\n\t\t\n\t\t//enclose in { } if in =>\n\t\tif (s_context == _Context.Arrow && !s.Starts(\"throw \")) {\n\t\t\tif (s.Contains('\\n')) {\n\t\t\t\ts = \"{\\r\\n\" + s.RxReplace(@\"(?m)^\", \"\\t\") + \"\\r\\n}\";\n\t\t\t} else {\n\t\t\t\ts = \"{ \" + s + \" }\";\n\t\t\t}\n\t\t\t//never mind: should add ; if missing\n\t\t}\n\t\t\n\t\t//rejected: ensure unique names of declared variables.\n\t\t\n\t\t//maybe need meta comments\n\t\tif (_GetAttr(\"meta\", out var attrMeta)) {\n\t\t\tint len1 = doc.aaaLen16;\n\t\t\tif (InsertCode.MetaComment(attrMeta)) {\n\t\t\t\tint lenDiff = doc.aaaLen16 - len1;\n\t\t\t\tpos += lenDiff;\n\t\t\t\tendPos += lenDiff;\n\t\t\t}\n\t\t}\n\t\t\n\t\t//maybe need using directives\n\t\tif (_GetAttr(\"using\", out var attrUsing)) {\n\t\t\tint len1 = doc.aaaLen16;\n\t\t\tif (InsertCode.UsingDirective(attrUsing)) {\n\t\t\t\tint lenDiff = doc.aaaLen16 - len1;\n\t\t\t\tpos += lenDiff;\n\t\t\t\tendPos += lenDiff;\n\t\t\t}\n\t\t}\n\t\t\n\t\tnew CiSnippetMode(pos, endPos, s, doc, varName, surroundText);\n\t\t\n\t\tif (_GetAttr(\"print\", out var attrPrint)) {\n\t\t\tprint.it(attrPrint.Insert(attrPrint.Starts(\"<>\") ? 2 : 0, \"Snippet \" + xSnippet.Attr(\"name\") + \" says: \"));\n\t\t}\n\t\t\n\t\tbool _GetAttr(string name, out string value) => x.Attr(out value, name) || (x != xSnippet && xSnippet.Attr(out value, name));\n\t}\n\t\n\tpublic static readonly string DefaultFile = folders.ThisApp + @\"Default\\Snippets.xml\";\n\tpublic static readonly string CustomFile = AppSettings.DirBS + \"Snippets.xml\";\n}\n\nclass CiSnippetMode {\n\t//$n/${n:text} info.\n\trecord struct _Dollar(int n, string text, int offset, int len);\n\t\n\t//$n/${n:text} info used in snippet mode.\n\tclass _Field {\n\t\tpublic int start, end, n;\n#if DEBUG\n\t\tpublic override string ToString() => $\"_Field {n} ({start}..{end})\";\n#endif\n\t}\n\t\n\tSciCode _doc;\n\t_Field[] _fields;\n\t_Field _activeField, _modifiedField;\n\tStartEnd _range;\n\tint _finalCaretPos;\n\tbool _ignoreModified, _ignorePosChanged;\n\tbool _isSurround;\n\t\n\tconst string c_markComment = \"/*\\f\\v*/\", c_markAlt = \"__\\f\\v__\";\n\t\n\tpublic CiSnippetMode(int pos, int endPos, string s, SciCode doc, string varName, string selectedText) {\n\t\t_doc = doc;\n\t\t_isSurround = !selectedText.NE();\n\t\t\n\t\t//replace escape sequences in an easy but not perfect way\n\t\tbool escaped = 0 != s.RxReplace(@\"\\\\[$}\\\\]\", m => m.Subject[m.End - 1] switch { '$' => \"\\uf100\", '}' => \"\\uf101\", _ => \"\\uf102\" }, out s); //Unicode private use area\n\t\t\n\t\t//in a `/* */` use c_markAlt instead of c_markComment\n\t\tvar blockComments = s.RxIsMatch(@\"/\\*.+?\\*/\") && CiUtil.CreateSyntaxTree(s) is { } cu ? cu.DescendantTrivia().Where(o => o.Kind() is SyntaxKind.MultiLineCommentTrivia or SyntaxKind.MultiLineDocumentationCommentTrivia).Select(o => o.Span).ToArray() : null;\n\t\t\n\t\tList<_Dollar> dollars = null;\n\t\tif (s.Contains('$')) {\n\t\t\tStringBuilder b = new();\n\t\t\tint i = 0;\n\t\t\tbool hasDollar0 = false;\n\t\t\tforeach (var m in s.RxFindAll(@\"(?s)\\$(?:(?|\\{(\\d+)(?::(.+?))?\\}|(\\d+))|\\{([A-Z].+?)\\})\")) {\n\t\t\t\tb.Append(s, i, m.Start - i);\n\t\t\t\ti = m.End;\n\t\t\t\tif (m[3].Exists) { //variable, eg ${SELECTED_TEXT}\n\t\t\t\t\tvar v = m[3].Value;\n\t\t\t\t\tb.Append(_Variable(v));\n\t\t\t\t} else { //field, eg ${1:i} or $1\n\t\t\t\t\tdollars ??= new();\n\t\t\t\t\t\n\t\t\t\t\tint n = m[1].Value.ToInt();\n\t\t\t\t\tif (n == 0) {\n\t\t\t\t\t\tif (hasDollar0) continue; //error in snippet. Multiple $0 have no sense.\n\t\t\t\t\t\thasDollar0 = true;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tstring v = _Trim(m[2].Value);\n\t\t\t\t\tif (v.NE()) {\n\t\t\t\t\t\tif (n != 0) foreach (var q in dollars) if (q.n == n && q.text != null) { v = q.text; break; } //eg `${n:s}, $n`. But ignore `$n, ${n:s}`, it's insane.\n\t\t\t\t\t} else if (v.Starts('$')) {\n\t\t\t\t\t\tv = _Variable(v[1..]);\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tv = v.NullIfEmpty_();\n\t\t\t\t\tstring v2 = v;\n\t\t\t\t\tv ??= blockComments?.Any(o => o.ContainsInside(m.Start)) == true ? c_markAlt : c_markComment;\n\t\t\t\t\t\n\t\t\t\t\tdollars.Add(new(n, v2, b.Length, v.Length));\n\t\t\t\t\tb.Append(v);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (b.Length > 0) {\n\t\t\t\tb.Append(s, i, s.Length - i);\n\t\t\t\ts = b.ToString();\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (escaped) s = s.Replace('\\uf100', '$').Replace('\\uf101', '}').Replace('\\uf102', '\\\\');\n\t\t\n\t\t_Start(pos, endPos, s, dollars);\n\t\t\n\t\tstring _Variable(string v) {\n\t\t\tstring def = null;\n\t\t\tint i = v.IndexOf(':');\n\t\t\tif (i > 0) { def = v[++i..]; v = v[..--i]; }\n\t\t\tv = v switch {\n\t\t\t\t\"SELECTED_TEXT\" => selectedText,\n\t\t\t\t\"VAR\" => varName ?? \"VAR\",\n\t\t\t\t\"GUID\" => Guid.NewGuid().ToString(),\n\t\t\t\t\"RANDOM\" => new Random().Next(0, 1000000).ToString(\"d6\"),\n\t\t\t\t\"RANDOM_HEX\" => new Random().Next(0, 0x1000000).ToString(\"x6\"),\n\t\t\t\t\"TM_FILENAME_BASE\" => v, //info: TM_FILENAME_BASE is used in VSCode snippets\n\t\t\t\t_ => null\n\t\t\t};\n\t\t\treturn v.NE() ? def : v;\n\t\t}\n\t\t\n\t\tstatic bool _IsSpace(char c) => SyntaxFacts.IsWhitespace(c) || SyntaxFacts.IsNewLine(c);\n\t\t\n\t\tstatic string _Trim(string s) {\n\t\t\tif (s != null) {\n\t\t\t\tint i = 0; while (i < s.Length && _IsSpace(s[i])) i++;\n\t\t\t\tint j = s.Length; while (j > i && _IsSpace(s[j - 1])) j--;\n\t\t\t\tif (i > 0 || j < s.Length) s = s[i..j];\n\t\t\t}\n\t\t\treturn s;\n\t\t}\n\t\t\n\t\t//not supported these rarely used features:\n\t\t//\tRarely used variables.\n\t\t//\t$VARIABLE. Supports only ${VARIABLE} and ${1:$VARIABLE}.\n\t\t//\tSupports variables in field default value only in the simplest form: ${1:$VARIABLE}.\n\t\t//\tNested fields, like ${1:a ${2:b}}.\n\t\t//\tChoice, like ${1|one,two,three|}.\n\t\t//\tVariable transforms (regex).\n\t\t//\tisFileTemplate.\n\t}\n\t\n\t//s - snippet code modified by ctor.\n\tvoid _Start(int pos, int endPos, string s, List<_Dollar> dollars) {\n\t\tint pos0 = pos;\n\t\tModifyCode.FormatForInsert(ref s, ref pos, endPos, dollars == null ? null : _ChangesCallback);\n\t\t\n\t\tvoid _ChangesCallback(IList<TextChange> a) {\n\t\t\tvar d = dollars.AsSpan();\n\t\t\tforeach (ref var v in d) v.offset += pos0 - pos;\n\t\t\tfor (int i = d.Length; --i >= 0;) {\n\t\t\t\tint dStart = d[i].offset + pos, dEnd = dStart + d[i].len;\n\t\t\t\t//print.it($\"dStart={dStart}, dEnd={dEnd}\");\n\t\t\t\tforeach (var v in a) {\n\t\t\t\t\tint cStart = v.Span.Start, cEnd = v.Span.End;\n\t\t\t\t\tif (cStart >= dEnd) break;\n\t\t\t\t\tDebug.Assert(!((cStart < dStart && cEnd > dStart) || (cStart < dEnd && cEnd > dEnd))); //a change must not span dStart or dEnd\n\t\t\t\t\tvar dif = v.NewText.Length - (cEnd - cStart);\n\t\t\t\t\tif (cStart <= dStart) d[i].offset += dif;\n\t\t\t\t\telse d[i].len += dif;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\t//delete semicolon?\n\t\tif (_doc.aaaText.Eq(endPos, ';') ) {\n\t\t\tvar cu = CiUtil.CreateSyntaxTree(s);\n\t\t\tvar tok = cu.GetLastToken(includeSkipped: true, includeDirectives: true);\n\t\t\tif (tok.Kind() is SyntaxKind.SemicolonToken or SyntaxKind.CloseBraceToken || tok.Parent is DirectiveTriviaSyntax || tok.Parent?.Parent is DirectiveTriviaSyntax) {\n\t\t\t\tendPos++;\n\t\t\t\tif (s.Ends(' ')) s = s[..^1]; //added when formatting\n\t\t\t}\n\t\t}\n\t\t\n\t\t//CodeInfo.Pasting(_doc, silent: true); //to auto-add missing using directives //rejected. Does not work well with EReplaceTextGently (because it makes multiple modifications). Namespaces can be specified in snippet.\n\t\tif (dollars == null) {\n\t\t\t_doc.aaaReplaceRange(true, pos, endPos, s, true);\n\t\t} else {\n\t\t\t//remove markers of empty fields\n\t\t\tfor (int i = dollars.Count; --i >= 0;) {\n\t\t\t\tif (dollars[i].text == null) {\n\t\t\t\t\tint len = dollars[i].len;\n\t\t\t\t\ts = s.Remove(dollars[i].offset, len);\n\t\t\t\t\tdollars.Ref(i).len = 0;\n\t\t\t\t\tfor (int j = i + 1; j < dollars.Count; j++) dollars.Ref(j).offset -= len;\n\t\t\t\t}\n\t\t\t}\n\t\t\t//never mind: the formatter splits line `/*mark*/code` -> `/*mark*/\\r\\ncode`\n\t\t\t\n\t\t\tif (_isSurround) _doc.EReplaceTextGently(pos, endPos, s);\n\t\t\telse _doc.aaaReplaceRange(true, pos, endPos, s);\n\t\t\t\n\t\t\tint nDollar0 = 0; //max 1\n\t\t\tforeach (ref var v in dollars.AsSpan()) {\n\t\t\t\tv.offset += pos;\n\t\t\t\tif (v.n == 0) nDollar0++;\n\t\t\t}\n\t\t\t\n\t\t\tif (dollars.Count == 1) {\n\t\t\t\tvar d = dollars[0];\n\t\t\t\t_doc.aaaSelect(true, d.offset, d.offset + d.len, makeVisible: true);\n\t\t\t\t\n\t\t\t\t//show signature if like `Method($0...``. Also may need to add temp range.\n\t\t\t\tif (d.len == 0) {\n\t\t\t\t\tint k = d.offset - pos; //$n offset in s\n\t\t\t\t\tbool showSignature = s.RxIsMatch(@\"\\w[([][^)\\]]*\"\"?$\", range: ..k);\n\t\t\t\t\t(int from, int to) tempRange = default;\n\t\t\t\t\tif (s.Eq(k - 1, \"()\") || s.Eq(k - 1, \"[]\") || s.Eq(k - 1, \"\\\"\\\"\")) tempRange = (d.offset, d.offset);\n\t\t\t\t\telse if (s.Eq(k - 2, \"{  }\")) tempRange = (d.offset - 1, d.offset + 1);\n\t\t\t\t\t\n\t\t\t\t\tif (tempRange.to > 0) CodeInfo._correct.BracketsAdded(_doc, tempRange.from, tempRange.to, default);\n\t\t\t\t\tif (showSignature) CodeInfo.ShowSignature();\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t_fields = new _Field[dollars.Count - nDollar0];\n\t\t\t\t_finalCaretPos = -1;\n\t\t\t\t\n\t\t\t\tint fi = 0, iStart = 0, nStart = int.MaxValue;\n\t\t\t\tforeach (var v in dollars) {\n\t\t\t\t\tif (v.n == 0) {\n\t\t\t\t\t\t_finalCaretPos = _doc.aaaPos8(v.offset);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (v.n < nStart) { nStart = v.n; iStart = fi; }\n\t\t\t\t\t\tvar f = new _Field { n = v.n, start = v.offset, end = v.offset + v.len };\n\t\t\t\t\t\t_doc.aaaNormalizeRange(true, ref f.start, ref f.end);\n\t\t\t\t\t\t_doc.aaaIndicatorAdd(SciTheme.Indic.SnippetField, false, f.start..f.end, f.n);\n\t\t\t\t\t\t_fields[fi++] = f;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t_range = new(pos, pos + s.Length);\n\t\t\t\t_doc.aaaNormalizeRange(true, ref _range.start, ref _range.end);\n\t\t\t\t\n\t\t\t\t_SetActiveField(_fields[iStart], select: true);\n\t\t\t\t\n\t\t\t\t_doc.SnippetMode_ = this;\n\t\t\t}\n\t\t}\n\t}\n\t\n\tpublic void End(bool goToFinal = false) {\n\t\tif (_doc.SnippetMode_ != this) return;\n\t\t_doc.SnippetMode_ = null;\n\t\t\n\t\t_doc.aaaIndicatorClear(SciTheme.Indic.SnippetField);\n\t\t\n\t\t_FieldLeaved(true);\n\t\t\n\t\tif (goToFinal) _doc.aaaGoToPos(false, _finalCaretPos >= 0 ? _finalCaretPos : _range.end);\n\t}\n\t\n\tvoid _SetActiveField(_Field field, bool select) {\n\t\tif (field == _activeField) return;\n\t\t_FieldLeaved();\n\t\t_activeField = field;\n\t\tforeach (var f in _fields) {\n\t\t\tif (f.n == field.n) {\n\t\t\t\t_doc.aaaIndicatorAdd(SciTheme.Indic.SnippetFieldActive, false, f.start..f.end, f.n);\n\t\t\t\t_doc.aaaIndicatorClear(SciTheme.Indic.SnippetField, false, f.start..f.end); //erase SciTheme.Indic.SnippetField to avoid mixed color\n\t\t\t}\n\t\t}\n\t\tif (select) {\n\t\t\t_ignorePosChanged = true;\n\t\t\t_doc.aaaSelect(false, field.start, field.end);\n\t\t\t_ignorePosChanged = false;\n\t\t}\n\t}\n\t\n\tvoid _FieldLeaved(bool ending = false) {\n\t\tif (_activeField == null) return;\n\t\tvar af = _activeField; _activeField = null;\n\t\t_doc.aaaIndicatorClear(SciTheme.Indic.SnippetFieldActive);\n\t\tif (!ending) { //restore SciTheme.Indic.SnippetField erased by _SetActiveField\n\t\t\tforeach (var v in _fields) {\n\t\t\t\tif (v.n == af.n) _doc.aaaIndicatorAdd(SciTheme.Indic.SnippetField, false, v.start..v.end, v.n);\n\t\t\t}\n\t\t}\n\t\t_ReplaceTextOfRelatedFields(ending);\n\t}\n\t\n\t_Field _FieldFromPos(int pos, int pos2) {\n\t\tforeach (var f in _fields) if (pos >= f.start && pos2 <= f.end) return f;\n\t\treturn null;\n\t}\n\t\n\t_Field _FieldFromSel() => _FieldFromPos(_doc.aaaSelectionStart8, _doc.aaaSelectionEnd8);\n\t\n\tpublic void SciModified(in Sci.SCNotification n) {\n\t\tif (_ignoreModified) return;\n\t\t\n\t\tbool deleted = n.modificationType.Has(Sci.MOD.SC_MOD_DELETETEXT);\n\t\tint pos = n.position, len = n.length, pos2 = deleted ? pos + len : pos;\n\t\t\n\t\tbool cancel = false, cantReplaceRelated = false;\n\t\tif (deleted) {\n\t\t\t//cancel etc if deleted entire snippet, or part of snippet together with other code, or any part of a field together with other code. Other cases detected by _FieldFromPos.\n\t\t\tif (pos <= _range.start && pos2 >= _range.end) { //probably Undo\n\t\t\t\tcancel = cantReplaceRelated = true;\n\t\t\t} else if (pos < _range.start) {\n\t\t\t\tcancel = true;\n\t\t\t\tcantReplaceRelated = pos2 > _range.start;\n\t\t\t} else if (pos2 > _range.end) {\n\t\t\t\tcancel = true;\n\t\t\t\tcantReplaceRelated = pos < _range.end;\n\t\t\t} else {\n\t\t\t\tforeach (var f in _fields) {\n\t\t\t\t\tif (pos < f.start) cancel = pos2 > f.start;\n\t\t\t\t\telse if (pos2 > f.end) cancel = pos < f.end;\n\t\t\t\t\tif (cancel) break;\n\t\t\t\t}\n\t\t\t\tif (cancel) cantReplaceRelated = true;\n\t\t\t\t//else cancel = pos < _finalCaretPos && pos2 > _finalCaretPos;\n\t\t\t}\n\t\t}\n\t\t\n\t\tvar field = cancel ? null : _FieldFromPos(pos, pos2);\n\t\tif (field == null) {\n\t\t\tif (cantReplaceRelated) _modifiedField = null;\n\t\t\tEnd();\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tif (deleted) len = -len;\n\t\tforeach (var f in _fields) {\n\t\t\tif (f.start > pos) f.start += len;\n\t\t\tif (f.end >= pos2) f.end += len;\n\t\t}\n\t\tif (_finalCaretPos >= pos2) _finalCaretPos += len;\n\t\t_range.end += len;\n\t\t\n\t\t_doc.aaaIndicatorAdd(_activeField != null ? SciTheme.Indic.SnippetFieldActive : SciTheme.Indic.SnippetField, false, field.start..field.end, field.n);\n\t\t\n\t\t_modifiedField = field;\n\t\t\n\t\t//_TestShowFields();\n\t}\n\t\n\tpublic void SciPosChanged() {\n\t\tif (_ignorePosChanged) return;\n\t\tif (_FieldFromSel() is { } field) {\n\t\t\t_SetActiveField(field, select: false);\n\t\t} else {\n\t\t\t_FieldLeaved();\n\t\t}\n\t}\n\t\n\tpublic bool SciKey(KKey key, ModifierKeys mod) {\n\t\tswitch ((key, mod)) {\n\t\tcase (KKey.Escape, 0):\n\t\t\tEnd();\n\t\t\treturn true;\n\t\tcase (KKey.Enter, 0):\n\t\t\tif (_FieldFromSel() == null) break;\n\t\t\tEnd(goToFinal: true);\n\t\t\treturn true;\n\t\tcase (KKey.Tab, 0):\n\t\t\treturn _Tab(false);\n\t\tcase (KKey.Tab, ModifierKeys.Shift):\n\t\t\treturn _Tab(true);\n\t\t}\n\t\treturn false;\n\t\t\n\t\tbool _Tab(bool shift) {\n\t\t\tif (_activeField == null) return false;\n\t\t\tint i = Array.IndexOf(_fields, _activeField), nNow = _activeField.n, nNext;\n\t\t\t_Field fNext = null;\n\t\t\tif (shift) {\n\t\t\t\tnNext = 0;\n\t\t\t\tforeach (var f in _fields) if (f.n < nNow && f.n > nNext) { nNext = f.n; fNext = f; } //find max n that is > nNow\n\t\t\t\tif (nNext == 0) return true;\n\t\t\t} else {\n\t\t\t\tnNext = int.MaxValue;\n\t\t\t\tforeach (var f in _fields) if (f.n > nNow && f.n < nNext) { nNext = f.n; fNext = f; } //find min n that is > nNow\n\t\t\t\tif (nNext == int.MaxValue) {\n\t\t\t\t\tEnd(goToFinal: true);\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t\t_SetActiveField(fNext, select: true);\n\t\t\treturn true;\n\t\t}\n\t}\n\t\n\tunsafe void _ReplaceTextOfRelatedFields(bool ending) {\n\t\tvar field = _modifiedField; if (field == null) return;\n\t\t_modifiedField = null;\n\t\tif (_fields.Any(o => o.n == field.n && o != field)) {\n\t\t\t\n\t\t\t//workaround for: Scintilla selects some text when clicked somewhere in the same line after a replacement.\n\t\t\t//if (Api.GetCapture() == _doc.AaWnd) Api.ReleaseCapture(); //works, but then caret stars to flicker. Scintilla does not listen for released capture.\n\t\t\tif (Api.GetCapture() == _doc.AaWnd && mouse.isPressed(MButtons.Left)) {\n\t\t\t\tvar xy = mouse.xy; _doc.AaWnd.MapScreenToClient(ref xy);\n\t\t\t\t_doc.AaWnd.Send(Api.WM_LBUTTONUP, 0, Math2.MakeLparam(xy));\n\t\t\t}\n\t\t\t\n\t\t\tint textLen = field.end - field.start;\n\t\t\tvar text = MemoryUtil.Alloc(textLen);\n\t\t\ttry {\n\t\t\t\tMemoryUtil.Copy(_doc.aaaRangePointer(field.start, field.end), text, textLen);\n\t\t\t\t_ignoreModified = true;\n\t\t\t\tusing var undo = _doc.ENewUndoAction();\n\t\t\t\tforeach (var f in _fields) {\n\t\t\t\t\tif (f.n == field.n && f != field) {\n\t\t\t\t\t\t_doc.Call(Sci.SCI_SETTARGETRANGE, f.start, f.end);\n\t\t\t\t\t\t_doc.Call(Sci.SCI_REPLACETARGET, textLen, text);\n\t\t\t\t\t\tint len = textLen - (f.end - f.start), oldStart = f.start, oldEnd = f.end;\n\t\t\t\t\t\tif (_finalCaretPos >= oldEnd) _finalCaretPos += len;\n\t\t\t\t\t\tforeach (var q in _fields) {\n\t\t\t\t\t\t\tif (q.start > oldStart) q.start += len;\n\t\t\t\t\t\t\tif (q.end >= oldEnd) q.end += len;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t_range.end += len;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tMemoryUtil.Free(text);\n\t\t\t\t_ignoreModified = false;\n\t\t\t}\n\t\t\tif (!ending) {\n\t\t\t\tforeach (var f in _fields) {\n\t\t\t\t\tif (f.n == field.n && f != field) {\n\t\t\t\t\t\t_doc.aaaIndicatorAdd(SciTheme.Indic.SnippetField, false, f.start..f.end, f.n);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t//_TestShowFields();\n\t\t}\n\t}\n\t\n\t//#if DEBUG\n\t//\tvoid _TestShowFields() {\n\t//\t\t_doc.aaaIndicatorClear(SciTheme.Indic.TestStrike);\n\t//\t\t_doc.aaaIndicatorClear(SciTheme.Indic.TestPoint);\n\t//\t\tforeach (var f in _fields) {\n\t//\t\t\t_doc.aaaIndicatorAdd(SciTheme.Indic.TestStrike, false, f.start..f.end);\n\t//\t\t}\n\t//\t\tif (_finalCaretPos >= 0) _doc.aaaIndicatorAdd(SciTheme.Indic.TestPoint, false, _finalCaretPos..(_finalCaretPos + 1));\n\t//\t}\n\t//#endif\n}\n"
  },
  {
    "path": "Au.Editor/Edit/CiStyling.cs",
    "content": "//#define PRINT\n\nextern alias CAW;\n\n//Code colors. Also calls functions of folding, images, errors.\n\nusing Au.Controls;\nusing static Au.Controls.Sci;\nusing static LA.SciTheme;\n\nusing Microsoft.CodeAnalysis;\nusing CAW::Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp;\nusing Microsoft.CodeAnalysis.CSharp.Syntax;\nusing Microsoft.CodeAnalysis.Text;\nusing CAW::Microsoft.CodeAnalysis.Classification;\n\nnamespace LA;\n\npartial class CiStyling {\n\tpublic void DocHandleDestroyed(SciCode doc) {\n\t\tif (doc == _doc) {\n\t\t\t_doc = null; //GC. Not important, but helps when trying to detect memory leaks.\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Called after setting editor control text when a document opened (not just switched active document).\n\t/// </summary>\n\tpublic static void DocTextAdded() => CodeInfo._styling._DocTextAdded();\n\tvoid _DocTextAdded() {\n\t\tif (CodeInfo.IsReadyForStyling) {\n\t\t\t_timerOpened ??= new(_ => _DocChanged());\n\t\t\t_timerOpened.After(10); //not Dispatcher.InvokeAsync\n\t\t} else { //at program startup\n\t\t\tCodeInfo.ReadyForStyling += () => _DocChanged();\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Sets timer to updates styling and folding from 0 to the end of the visible area.\n\t/// </summary>\n\tpublic void Update() => _update = true;\n\t\n\tSciCode _doc; //to detect when the active document changed\n\tbool _update;\n\tbool _folded;\n\tSci_VisibleRange _visibleLines;\n\ttimer _timerModified, _timerOpened;\n\tint _modStart;\n\tint _modFromEnd; //like SCI_GETENDSTYLED, but from end\n\tint _diagCounter;\n\tCancellationTokenSource _cancelTS;\n\t\n\tvoid _DocChanged(SciCode doc = null) {\n\t\t//bool opened = doc == null;\n\t\tdoc ??= Panels.Editor.ActiveDoc;\n\t\tif (doc == null) return;\n\t\t_doc = doc;\n\t\t_update = false;\n\t\t_folded = false;\n\t\t_visibleLines = default;\n\t\t_timerModified?.Stop();\n\t\t_modStart = _modFromEnd = int.MaxValue;\n\t\t_diagCounter = 0;\n\t\t_Work(doc, cancel: true);\n\t\t//if (opened) {\n\t\t//}\n\t}\n\t\n\t/// <summary>\n\t/// Called every 250 ms while editor is visible.\n\t/// </summary>\n\tpublic void Timer250msWhenVisibleAndWarm(SciCode doc) {\n\t\t//We can't use Scintilla styling notifications, mostly because of Roslyn slowness.\n\t\t//To detect when need styling and folding we use 'opened' and 'modified' events and 250 ms timer.\n\t\t//When modified, we do styling for the modified line(s). Redraws faster, but unreliable, eg does not update new/deleted identifiers.\n\t\t//The timer does styling and folding for all visible lines. Redraws with a bigger delay, but updates everything after modified, scrolled, resized, folded, etc.\n\t\t\n\t\tif (_cancelTS != null || (_timerModified?.IsRunning ?? false)) return;\n\t\tif (doc != _doc || _update) {\n\t\t\t_update = false;\n\t\t\tif (doc != _doc) _DocChanged(doc);\n\t\t\telse _Work(doc, cancel: true);\n\t\t} else {\n\t\t\tSci_GetVisibleRange(doc.AaSciPtr, out var vr); //fast\n\t\t\tif (vr != _visibleLines) {\n\t\t\t\t_Work(doc);\n\t\t\t} else if (_diagCounter > 0 && --_diagCounter == 0) {\n\t\t\t\tCodeInfo._diag.Indicators(doc.aaaPos16(vr.posFrom), doc.aaaPos16(vr.posTo));\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Called when editor text modified.\n\t/// </summary>\n\tpublic void SciModified(SciCode doc, in SCNotification n) {\n\t\t//Delay to avoid multiple styling/folding/cancelling on multistep actions (replace text range, find/replace all, autocorrection) and fast automated text input.\n\t\t_cancelTS?.Cancel(); _cancelTS = null;\n\t\t_modStart = Math.Min(_modStart, n.position);\n\t\t_modFromEnd = Math.Min(_modFromEnd, doc.aaaLen8 - n.FinalPosition);\n\t\t_folded = false;\n\t\t//using var p1 = perf.local();\n#if true\n\t\t_timerModified ??= new timer(_ModifiedTimer);\n\t\tif (!_timerModified.IsRunning) { _timerModified.Tag = doc; _timerModified.After(25); }\n#else\n\t\t_StylingAndFolding(doc, doc.aaaLineEndFromPos(false, doc.aaaLen8 - _modFromEnd, withRN: true));\n#endif\n\t\t//workaround for:\n\t\t//\tOn Undo, if the undo text contains hidden text, Scintilla it seems tries to show that unstyled text before styleneeded notification.\n\t\t//\tIf the hidden text is long, it adds horz scrollbar and scrolls.\n\t\t//\tNot if the undo text ends with newline.\n\t\tif (n.modificationType.Has(MOD.SC_LASTSTEPINUNDOREDO | MOD.SC_MOD_INSERTTEXT)) {\n\t\t\tdoc.EHideImages_(n.position, doc.aaaLineEndFromPos(false, n.position + n.length));\n\t\t\tdoc.aaaSetStyled();\n\t\t}\n\t}\n\t\n\tvoid _ModifiedTimer(timer t) {\n\t\t//var p1 = perf.local();\n\t\tvar doc = t.Tag as SciCode;\n\t\tif (doc != Panels.Editor.ActiveDoc) return;\n\t\tif (_cancelTS != null) return;\n\t\t_Work(doc, doc.aaaLineStartFromPos(false, _modStart), doc.aaaLineEndFromPos(false, doc.aaaLen8 - _modFromEnd, withRN: true));\n\t\t//p1.NW('a'); //we return without waiting for the async task to complete\n\t}\n\t\n\tasync void _Work(SciCode doc, int start8 = 0, int end8 = -1, bool cancel = false) {\n#if PRINT\n\t\tusing var p1 = perf.local();\n#endif\n\t\tvoid _PN(char ch = default) {\n#if PRINT\n\t\t\tp1.Next(ch);\n#endif\n\t\t}\n\t\t\n\t\tif (cancel) { _cancelTS?.Cancel(); _cancelTS = null; }\n\t\tDebug.Assert(_cancelTS == null);\n\t\tvar cancelTS = _cancelTS = new CancellationTokenSource();\n\t\tvar cancelToken = cancelTS.Token;\n\t\t\n\t\tvar cd = new CodeInfo.Context(0);\n\t\tDebug.Assert(doc == cd.sci);\n\t\tif (!cd.GetDocument()) return;\n\t\tvar document = cd.document;\n\t\tvar code = cd.code;\n\t\t_PN('d');\n\t\ttry {\n\t\t\tSci_GetVisibleRange(doc.AaSciPtr, out var vr);\n\t\t\t\n\t\t\tbool minimal = end8 >= 0;\n\t\t\tbool needFolding = !minimal && !_folded;\n\t\t\tList<SciFoldPoint> af = null;\n\t\t\t\n\t\t\tif (needFolding) {\n\t\t\t\tawait Task.Run(() => {\n\t\t\t\t\t_PN('s');\n\t\t\t\t\taf = CiFolding.GetFoldPoints(cd.syntaxRoot, code, cancelToken);\n\t\t\t\t});\n\t\t\t\tif (_Canceled()) return;\n\t\t\t}\n\t\t\t_PN('p');\n\t\t\t\n\t\t\tif (minimal) {\n\t\t\t\tstart8 = Math.Max(start8, vr.posFrom);\n\t\t\t\tend8 = Math.Min(end8, vr.posTo);\n\t\t\t} else {\n\t\t\t\tif (needFolding) {\n\t\t\t\t\tCiFolding.Fold(doc, af);\n\t\t\t\t\t_folded = true;\n\t\t\t\t\tSci_GetVisibleRange(doc.AaSciPtr, out vr);\n\t\t\t\t\t_PN('F');\n\t\t\t\t}\n\t\t\t\tstart8 = vr.posFrom;\n\t\t\t\tend8 = vr.posTo;\n\t\t\t}\n\t\t\tif (end8 <= start8) return;\n\t\t\t\n#if PRINT\n\t\t\t//print.it($\"<><c green>lines {doc.aaaLineFromPos(false, start8) + 1}-{doc.aaaLineFromPos(false, end8)}, range {start8}-{end8}, {vr}<>\");\n#endif\n\t\t\t\n\t\t\tvar ar8 = _GetVisibleRanges();\n\t\t\tList<StartEnd> _GetVisibleRanges() {\n\t\t\t\tList<StartEnd> a = new();\n\t\t\t\tStartEnd r = new(start8, end8);\n\t\t\t\tfor (int dline = doc.aaaLineFromPos(false, start8), dlinePrev = dline - 1, vline = doc.Call(SCI_VISIBLEFROMDOCLINE, dline); ; dline = doc.Call(SCI_DOCLINEFROMVISIBLE, ++vline)) {\n\t\t\t\t\tint i = doc.aaaLineStart(false, dline); if (i >= end8) break;\n\t\t\t\t\tif (dline > dlinePrev + 1) {\n\t\t\t\t\t\ta.Add(r);\n\t\t\t\t\t\tr.start = i;\n\t\t\t\t\t}\n\t\t\t\t\tr.end = i + doc.Call(SCI_LINELENGTH, dline);\n\t\t\t\t\tdlinePrev = dline;\n\t\t\t\t}\n\t\t\t\ta.Add(r);\n\t\t\t\treturn a;\n\t\t\t}\n\t\t\t\n\t\t\tvar ar = new (List<ClassifiedSpan> a, StartEnd r)[ar8.Count];\n\t\t\tfor (int i = 0; i < ar8.Count; i++) ar[i].r = new StartEnd(doc.aaaPos16(ar8[i].start), doc.aaaPos16(ar8[i].end));\n\t\t\tSemanticModel semo = null;\n\t\t\t\n\t\t\tawait Task.Run(async () => {\n\t\t\t\tsemo = await document.GetSemanticModelAsync(cancelToken).ConfigureAwait(false);\n\t\t\t\t_PN('m'); //BAD: slow when [re]opening a file in a large project\n\t\t\t\tfor (int i = 0; i < ar.Length; i++) {\n\t\t\t\t\tvar r = ar[i].r;\n\t\t\t\t\tar[i].a = CiUtil.GetClassifiedSpans(semo, document, r.start, r.end, cancelToken);\n\t\t\t\t}\n\t\t\t\t//info: GetClassifiedSpansAsync calls GetSemanticModelAsync and GetClassifiedSpans, like here.\n\t\t\t\t//GetSemanticModelAsync+GetClassifiedSpans are slow, ~ 90% of total time.\n\t\t\t\t//Tried to implement own \"GetClassifiedSpans\", but slow too, often slower, because GetSymbolInfo is slow.\n\t\t\t});\n\t\t\tif (_Canceled()) return;\n\t\t\t_PN('c');\n\t\t\t\n\t\t\tint start16 = doc.aaaPos16(start8), end16 = doc.aaaPos16(end8); //from now don't use UTF-8\n\t\t\tvar b = new byte[end16 - start16];\n\t\t\t\n\t\t\tchar prevPunctuation = default;\n\t\t\tforeach (var (a, r) in ar) {\n\t\t\t\tforeach (var v in a) {\n\t\t\t\t\tEStyle style = StyleFromClassifiedSpan(v);\n\t\t\t\t\t//print.it($\"<><c green>{v.ClassificationType}<> '{code[v.TextSpan.Start..v.TextSpan.End]}' style={style}\");\n\t\t\t\t\t\n\t\t\t\t\tif (style == EStyle.None) {\n#if DEBUG\n\t\t\t\t\t\tswitch (v.ClassificationType) {\n\t\t\t\t\t\tcase ClassificationTypeNames.Identifier or ClassificationTypeNames.PreprocessorText: break;\n\t\t\t\t\t\tdefault: Debug_.PrintIf(!v.ClassificationType.Starts(\"regex\"), $\"<c gray>{v.ClassificationType}, {v.TextSpan}<>\"); break;\n\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t} else {\n\t\t\t\t\t\tint spanStart = Math.Max(v.TextSpan.Start, r.start), spanEnd = Math.Min(v.TextSpan.End, r.end);\n\t\t\t\t\t\tb.AsSpan(spanStart - start16, spanEnd - spanStart).Fill((byte)style);\n\t\t\t\t\t\tif (style is EStyle.String && prevPunctuation is '(' or ',' or ':') _RegexString();\n\t\t\t\t\t\tprevPunctuation = style is EStyle.Punctuation ? code[v.TextSpan.End - 1] : default;\n\t\t\t\t\t\t\n\t\t\t\t\t\tvoid _RegexString() {\n\t\t\t\t\t\t\t//we need only verbatim and raw strings, and not interpolated\n\t\t\t\t\t\t\tbool verbatim = v.ClassificationType == ClassificationTypeNames.VerbatimStringLiteral;\n\t\t\t\t\t\t\tif (verbatim) {\n\t\t\t\t\t\t\t\tif (v.TextSpan.Length < 4 || !code.Eq(v.TextSpan.Start, \"@\\\"\")) return;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tif (v.TextSpan.Length < 7 || !code.Eq(v.TextSpan.Start, \"\\\"\\\"\\\"\")) return;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tvar tok = cd.syntaxRoot.FindToken(v.TextSpan.Start); //fast here\n\t\t\t\t\t\t\tif (tok.Parent is not LiteralExpressionSyntax { RawKind: (int)SyntaxKind.StringLiteralExpression } node) return;\n\t\t\t\t\t\t\tvar format = CiUtil.GetParameterStringFormat(tok.Parent, semo, true, ignoreInterpolatedString: true);\n\t\t\t\t\t\t\tif (!(format is PSFormat.Regexp or PSFormat.NetRegex or PSFormat.Wildex)) return;\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tvar (from, to) = v.TextSpan;\n\t\t\t\t\t\t\tif (verbatim) {\n\t\t\t\t\t\t\t\tfrom += 2; if (code[to - 1] is '\"') to--;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\twhile (from < to && code[from] is '\"') { from++; if (code[to - 1] is '\"') to--; }\n\t\t\t\t\t\t\t\tfrom = CiUtil.SkipNewline(code, CiUtil.SkipSpace(code, from));\n\t\t\t\t\t\t\t\tto = CiUtil.SkipNewlineBack(code, CiUtil.SkipSpaceBack(code, to));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tfrom = Math.Max(from, start16);\n\t\t\t\t\t\t\tto = Math.Min(to, end16);\n\t\t\t\t\t\t\tif (to <= from) return;\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tRegexParser.GetScintillaStylingBytes16(code.AsSpan(from..to), format, b.AsSpan((spanStart - start16 + from - v.TextSpan.Start)..));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tSpan<byte> b8 = KScintilla.aaaConvertStylingBytesToUtf8(b, code.AsSpan(start16..end16));\n\t\t\tdoc.EHideImages_(start8, end8, b8);\n\t\t\t_PN();\n\t\t\tdoc.aaaSetStyling(start8, b8);\n\t\t\tdoc.aaaSetStyled(minimal ? int.MaxValue : end8);\n\t\t\t\n\t\t\t_modStart = _modFromEnd = int.MaxValue;\n\t\t\t_visibleLines = minimal ? default : vr;\n\t\t\t_PN('S');\n\t\t\tif (!minimal) {\n\t\t\t\tif (!App.Settings.edit_noImages) doc.EImagesGet_(cd, ar.SelectMany(o => o.a).ToArray(), vr);\n\t\t\t\t_diagCounter = 4; //update diagnostics after 1 s\n\t\t\t} else {\n\t\t\t\tCodeInfo._diag.EraseIndicatorsInLine(doc, doc.aaaCurrentPos8);\n\t\t\t}\n\t\t}\n\t\tcatch (OperationCanceledException) { }\n\t\tcatch (Exception e1) { Debug_.Print(e1); } //InvalidOperationException when this code: wpfBuilder ... .Also(b=>b.Panel.for)\n\t\tfinally {\n\t\t\tcancelTS.Dispose();\n\t\t\tif (cancelTS == _cancelTS) _cancelTS = null;\n\t\t}\n\t\t\n\t\tbool _Canceled() {\n\t\t\tif (cancelToken.IsCancellationRequested) {\n\t\t\t\t_PN();\n#if PRINT\n\t\t\t\tprint.it($\"<><c orange>canceled.  {p1.ToString()}<>\");\n#endif\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (doc != Panels.Editor.ActiveDoc) {\n#if PRINT\n\t\t\t\tprint.it(\"<><c red>switched doc<>\");\n#endif\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n#if DEBUG\n\t\t//if(!s_debugPerf) { s_debugPerf = true; perf.nw('s'); }\n#endif\n\t}\n#if DEBUG\n\t//static bool s_debugPerf;\n#endif\n\t\n\tpublic static EStyle StyleFromClassifiedSpan(ClassifiedSpan cs) {\n\t\treturn cs.ClassificationType switch {\n\t\t\tClassificationTypeNames.ClassName => EStyle.Type,\n\t\t\tClassificationTypeNames.Comment => EStyle.Comment,\n\t\t\tClassificationTypeNames.ConstantName => EStyle.Constant,\n\t\t\tClassificationTypeNames.ControlKeyword => EStyle.Keyword,\n\t\t\tClassificationTypeNames.DelegateName => EStyle.Type,\n\t\t\tClassificationTypeNames.EnumMemberName => EStyle.Constant,\n\t\t\tClassificationTypeNames.EnumName => EStyle.Type,\n\t\t\tClassificationTypeNames.EventName => EStyle.Event,\n\t\t\tClassificationTypeNames.ExcludedCode => EStyle.Excluded,\n\t\t\tClassificationTypeNames.ExtensionMethodName => EStyle.Function,\n\t\t\tClassificationTypeNames.FieldName => EStyle.Field,\n\t\t\t//ClassificationTypeNames.Identifier => _TryResolveMethod(),\n\t\t\tClassificationTypeNames.InterfaceName => EStyle.Type,\n\t\t\tClassificationTypeNames.Keyword => EStyle.Keyword,\n\t\t\tClassificationTypeNames.LabelName => EStyle.Label,\n\t\t\tClassificationTypeNames.LocalName => EStyle.LocalVariable,\n\t\t\tClassificationTypeNames.MethodName => EStyle.Function,\n\t\t\tClassificationTypeNames.NamespaceName => EStyle.Namespace,\n\t\t\tClassificationTypeNames.NumericLiteral => EStyle.Number,\n\t\t\tClassificationTypeNames.Operator => EStyle.Operator,\n\t\t\tClassificationTypeNames.OperatorOverloaded => EStyle.Function,\n\t\t\tClassificationTypeNames.ParameterName => EStyle.LocalVariable,\n\t\t\tClassificationTypeNames.PreprocessorKeyword => EStyle.Preprocessor,\n\t\t\t//ClassificationTypeNames.PreprocessorText => EStyle.None,\n\t\t\tClassificationTypeNames.PropertyName => EStyle.Function,\n\t\t\tClassificationTypeNames.Punctuation => EStyle.Punctuation,\n\t\t\tClassificationTypeNames.RecordClassName or ClassificationTypeNames.RecordStructName => EStyle.Type,\n\t\t\tClassificationTypeNames.StringEscapeCharacter => EStyle.StringEscape,\n\t\t\tClassificationTypeNames.StringLiteral => EStyle.String,\n\t\t\tClassificationTypeNames.StructName => EStyle.Type,\n\t\t\t//ClassificationTypeNames.Text => EStyle.None,\n\t\t\tClassificationTypeNames.VerbatimStringLiteral => EStyle.String,\n\t\t\tClassificationTypeNames.TypeParameterName => EStyle.Type,\n\t\t\t//ClassificationTypeNames.WhiteSpace => EStyle.None,\n\t\t\t\n\t\t\tClassificationTypeNames.XmlDocCommentText => EStyle.XmlDocText,\n\t\t\tClassificationTypeNames.XmlDocCommentAttributeName => EStyle.XmlDocTag,\n\t\t\tClassificationTypeNames.XmlDocCommentAttributeQuotes => EStyle.XmlDocTag,\n\t\t\tClassificationTypeNames.XmlDocCommentAttributeValue => EStyle.XmlDocTag,\n\t\t\tClassificationTypeNames.XmlDocCommentCDataSection => EStyle.XmlDocTag,\n\t\t\tClassificationTypeNames.XmlDocCommentComment => EStyle.XmlDocTag,\n\t\t\tClassificationTypeNames.XmlDocCommentDelimiter => EStyle.XmlDocTag,\n\t\t\tClassificationTypeNames.XmlDocCommentEntityReference => EStyle.XmlDocTag,\n\t\t\tClassificationTypeNames.XmlDocCommentName => EStyle.XmlDocTag,\n\t\t\tClassificationTypeNames.XmlDocCommentProcessingInstruction => EStyle.XmlDocTag,\n\t\t\t\n\t\t\t//ClassificationTypeNames. => EStyle.,\n\t\t\t_ => EStyle.None\n\t\t};\n\t\t\n\t\t//it seems don't need this anymore\n\t\t//EStyle _TryResolveMethod() { //ClassificationTypeNames.Identifier. Possibly method name when there are errors in arguments.\n\t\t//\tvar node = semo.Root.FindNode(cs.TextSpan);\n\t\t//\tif (node?.Parent is InvocationExpressionSyntax && node.Span == cs.TextSpan && !semo.GetMemberGroup(node).IsDefaultOrEmpty) return EStyle.Function; //not too slow\n\t\t//\treturn EStyle.None;\n\t\t//}\n\t}\n\t\n\t/// <summary>\n\t/// Returns true if character at pos8 is in a hidden text.\n\t/// </summary>\n\tpublic static bool IsProtected(KScintilla sci, int pos8) => sci.aaaStyleGetAt(pos8) == STYLE_HIDDEN;\n\t\n\t/// <summary>\n\t/// Returns true if range from8..to8 intersects a hidden text, except when it is greater or equal than the hidden text range.\n\t/// It means the range should not be selected or modified.\n\t/// </summary>\n\tpublic static bool IsProtected(KScintilla sci, int from8, int to8) {\n\t\tbool p1 = IsProtected(sci, from8);\n\t\tif (to8 <= from8) return p1 && IsProtected(sci, from8 - 1);\n\t\tif (p1) return IsProtected(sci, from8 - 1) || (IsProtected(sci, to8 - 1) && IsProtected(sci, to8));\n\t\tif (IsProtected(sci, to8 - 1)) return IsProtected(sci, to8);\n\t\treturn false;\n\t}\n\t\n\tpublic static int SkipProtected(KScintilla sci, int pos8, bool back) {\n\t\tif (back) {\n\t\t\twhile (pos8 >= 0 && IsProtected(sci, pos8)) pos8--;\n\t\t\tif (pos8 < 0) return SkipProtected(sci, 0, false);\n\t\t} else {\n\t\t\twhile (IsProtected(sci, pos8)) pos8++;\n\t\t}\n\t\treturn pos8;\n\t}\n}\n\n"
  },
  {
    "path": "Au.Editor/Edit/CiText.cs",
    "content": "extern alias CAW;\n\nusing System.Windows.Documents;\nusing System.Windows;\nusing System.Windows.Media;\nusing System.Windows.Controls;\nusing System.Windows.Navigation;\nusing System.Windows.Input;\nusing System.Collections.Immutable;\n\nusing Microsoft.CodeAnalysis;\nusing CAW::Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp;\nusing Microsoft.CodeAnalysis.CSharp.Syntax;\nusing Microsoft.CodeAnalysis.Text;\nusing Microsoft.CodeAnalysis.Shared.Extensions;\nusing CAW::Microsoft.CodeAnalysis.Shared.Extensions;\nusing Microsoft.CodeAnalysis.DocumentationComments;\nusing Microsoft.CodeAnalysis.SignatureHelp;\n\nnamespace LA;\n\nclass CiText {\n\treadonly Stack<TextElement> _stack = new();\n\tTextElement _container = new Section();\n\tTextElement _last;\n\t\n\tpublic Section Result => (Section)_container;\n\t\n\t/// <summary>\n\t/// Starts Block or Span. Or adds empty.\n\t/// </summary>\n\t/// <param name=\"e\"></param>\n\t/// <param name=\"start\">Start and don't end now. If false, adds and ends.</param>\n\tvoid _Start(TextElement e, bool start) {\n\t\tswitch (e) {\n\t\tcase Block b: ((Section)_container).Blocks.Add(b); break;\n\t\tcase Span s: Append(s); break;\n\t\tdefault: throw new NotSupportedException();\n\t\t}\n\t\t_last = e;\n\t\tif (start) {\n\t\t\t_stack.Push(_container);\n\t\t\t_container = e;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Ends Block or Span.\n\t/// </summary>\n\tvoid _End() { _container = _stack.Pop(); }\n\t\n\tpublic Run Append(string text) {\n\t\tvar r = new Run(text);\n\t\tAppend(r);\n\t\treturn r;\n\t}\n\t\n\tpublic void Append(Inline i) {\n\t\t_Inlines().Add(i);\n\t\t_last = i;\n\t}\n\t\n\tpublic void Append(UIElement e) {\n\t\t_Inlines().Add(e);\n\t}\n\t\n\tInlineCollection _Inlines() => _container switch { Paragraph p => p.Inlines, Span s => s.Inlines, _ => throw new InvalidOperationException() };\n\t\n\tpublic Run Run(string style, string text, string append = null) {\n\t\tvar r = Append(text);\n\t\tStyle(style);\n\t\tif (append != null) Append(append);\n\t\treturn r;\n\t}\n\t\n\tpublic void Style(string style) {\n\t\t//_last.Style = (Style)Application.Current.Resources[style]; //finds slower\n\t\t_last.Style = (Style)Application.Current.FindResource(style);\n\t}\n\t\n\tpublic Paragraph StartParagraph(string style = null) {\n\t\tvar p = new Paragraph();\n\t\t_Start(p, true);\n\t\tif (style != null) Style(style);\n\t\treturn p;\n\t}\n\t\n\tpublic void EndParagraph() => _End();\n\t\n\t/// <summary>\n\t/// <c>StartParagraph(\"div\");</c>\n\t/// </summary>\n\tpublic Paragraph StartDiv() => StartParagraph(\"div\");\n\t\n\tpublic void EndDiv() => _End();\n\t\n\tpublic (Paragraph, Run) Header(string text) {\n\t\tvar p = StartParagraph(\"header\");\n\t\tvar r = Append(text);\n\t\tEndParagraph();\n\t\treturn (p, r);\n\t}\n\t\n\tpublic Separator Separator() {\n\t\tvar r = new Separator { Margin = new(4) };\n\t\tvar v = new BlockUIContainer(r);\n\t\t_Start(v, false);\n\t\treturn r;\n\t}\n\t\n\tpublic void LineBreak(string append = null, bool notIfFirstInParagraph = false) {\n\t\tif (!(notIfFirstInParagraph && _last is Paragraph)) Append(new LineBreak());\n\t\tif (append != null) Append(append);\n\t}\n\t\n\tpublic void StartOverload(bool selected, int index) {\n\t\tStartParagraph(selected ? \"overloadSelected\" : \"overload\");\n\t\tif (!selected) { StartHyperlink($\"^{index}\"); Style(\"divLink\"); }\n\t\tRun(selected ? \"dotSelected\" : \"dot\", \"● \");\n\t}\n\t\n\tpublic void EndOverload(bool selected) {\n\t\tif (!selected) EndHyperlink();\n\t\tEndParagraph();\n\t}\n\t\n\t/// <summary>\n\t/// Appends codeBlock paragraph with text.\n\t/// </summary>\n\t/// <param name=\"code\">Non-escaped text.</param>\n\tpublic void CodeBlock(string code) {\n\t\tStartParagraph(\"codeBlock\");\n\t\tAppend(code);\n\t\tEndParagraph();\n\t}\n\t\n\t/// <summary>\n\t/// Appends Span with a defined style (like HTML class).\n\t/// </summary>\n\t/// <param name=\"style\">A style defined in app resources, like \"keyword\".</param>\n\tpublic Span StartSpan(string style = null) {\n\t\tvar r = new Span();\n\t\t_Start(r, true);\n\t\tif (style != null) Style(style);\n\t\treturn r;\n\t}\n\t\n\tpublic void EndSpan() => _End();\n\t\n\tpublic void StartBold() => _Start(new Bold(), true);\n\t\n\tpublic void EndBold() => _End();\n\t\n\tpublic void Bold(string text) => _Start(new Bold(new Run(text)), false);\n\t\n\tpublic void StartItalic() => _Start(new Italic(), true);\n\t\n\tpublic void EndItalic() => _End();\n\t\n\tpublic void Italic(string text) => _Start(new Italic(new Run(text)), false);\n\t\n\tpublic Hyperlink StartHyperlink(string uri) {\n\t\tvar h = new Hyperlink { NavigateUri = new(uri, UriKind.RelativeOrAbsolute) };\n\t\th.RequestNavigate += (o, e) => FlowDocumentControl.OnLinkClicked_(o as Hyperlink, e);\n\t\t_Start(h, true);\n\t\treturn h;\n\t}\n\t\n\tpublic void EndHyperlink() => _End();\n\t\n\tpublic Hyperlink Hyperlink(string uri, string text, string append = null) {\n\t\tvar h = StartHyperlink(uri);\n\t\tif (WpfUtil_.IsHighContrastDark) h.Foreground = Brushes.RoyalBlue;\n\t\tAppend(text);\n\t\tEndHyperlink();\n\t\tif (append != null) Append(append);\n\t\treturn h;\n\t}\n\t\n\tpublic void Image(CiItemKind i) => Image(CiComplItem.ImageResource(i));\n\t\n\tpublic void Image(CiItemAccess i) => Image(CiComplItem.AccessImageResource(i));\n\t\n\t/// <summary>\n\t/// Adds xaml or png etc image from app resources.\n\t/// </summary>\n\t/// <param name=\"source\">Image resource, like \"resources/a.xaml\" or png etc.</param>\n\tpublic void Image(string source) {\n\t\tif (source.Ends(\".xaml\")) {\n\t\t\tvar c = new InlineUIContainer(ResourceUtil.GetWpfImageElement(source)) { BaselineAlignment = BaselineAlignment.Center };\n\t\t\tAppend(c);\n\t\t} else { //not used, not tested\n\t\t\tvar c = new Image { Source = ResourceUtil.GetWpfImage(source), Stretch = Stretch.None };\n\t\t\tAppend(c);\n\t\t}\n\t}\n\t\n\tpublic void AppendTaggedParts(IEnumerable<TaggedText> tags, bool? isParameters = null) {\n\t\tif (tags == null) return;\n\t\t//need IReadOnlyList to easier replace something. Usually it is ImmutableArray or List.\n\t\tDebug_.PrintIf(tags is not IReadOnlyList<TaggedText>, \"not IReadOnlyList\");\n\t\tvar a = tags as IReadOnlyList<TaggedText> ?? tags.ToArray();\n\t\t//print.it(a.Count);\n\t\tbool inParameters = isParameters == true;\n\t\tfor (int i = 0; i < a.Count; i++) {\n\t\t\tvar v = a[i];\n\t\t\t//print.it($\"{v.Tag}, '{v.Text}', {v.Style}\");\n\t\t\t//if(lessNewlines>1) print.it($\"{v.Tag}, '{v.Text}', {v.Style}\");\n\t\t\t//print.it($\"{v.Tag}, '{v.Text}', {v.Style}, navHint='{v.NavigationHint}', navTarget='{v.NavigationTarget}'\");\n\t\t\tstring s = v.Text, c = null;\n\t\t\tswitch (v.Tag) {\n\t\t\tcase TextTags.Struct:\n\t\t\t\tc = \"type\";\n\t\t\t\t\n\t\t\t\t//rejected: replace parameter type ReadOnlySpan<char> with Strinĝ, and in global.cs add: global using Strinĝ = System.ReadOnlySpan<char>;\n\t\t\t\t//\tBetter in XML doc tell it's a string.\n\t\t\t\t//if (inParameters) {\n\t\t\t\t//\tif (s == \"ReadOnlySpan\" && i < a.Count - 3 && a[i + 2].Text == \"char\" && a[i + 1].Text == \"<\" && a[i + 3].Text == \">\") {\n\t\t\t\t//\t\ts = \"Strinĝ\";\n\t\t\t\t//\t\ti += 3;\n\t\t\t\t//\t}\n\t\t\t\t//}\n\t\t\t\tbreak;\n\t\t\tcase TextTags.Class or TextTags.Enum or TextTags.Interface or TextTags.Delegate or TextTags.TypeParameter or TextTags.Record or TextTags.RecordStruct:\n\t\t\t\tc = \"type\";\n\t\t\t\tbreak;\n\t\t\tcase TextTags.Keyword:\n\t\t\t\tc = \"keyword\";\n\t\t\t\tbreak;\n\t\t\tcase TextTags.StringLiteral:\n\t\t\t\tc = \"string\";\n\t\t\t\tbreak;\n\t\t\tcase TextTags.NumericLiteral:\n\t\t\t\tc = \"number\";\n\t\t\t\tbreak;\n\t\t\tcase TextTags.Namespace:\n\t\t\t\tc = \"namespace\";\n\t\t\t\tbreak;\n\t\t\tcase TextTags.LineBreak:\n\t\t\t\tLineBreak();\n\t\t\t\tcontinue;\n\t\t\tcase TextTags.Punctuation:\n\t\t\t\tif (isParameters == null && i > 0 && s.Length == 1) { //auto-detect\n\t\t\t\t\tchar ch = s[0];\n\t\t\t\t\tif (!inParameters) {\n\t\t\t\t\t\tif (ch == '(') inParameters = a[i - 1].Tag is TextTags.Method or TextTags.ExtensionMethod;\n\t\t\t\t\t\tif (ch == '[') inParameters = a[i - 1].Tag is TextTags.Keyword && a[i - 1].Text == \"this\";\n\t\t\t\t\t\t//note: does not detect ctor.\n\t\t\t\t\t\t//\tThen a[i - 1] is type. But for cast operators it is type too; they must not be detected.\n\t\t\t\t\t\t//\tProbably it's better if ctor and cast are displayed with same types.\n\t\t\t\t\t} else if (ch == ')') inParameters = false;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase TextTags.Method: //eg operator <\n\t\t\tcase TextTags.Operator:\n\t\t\t\tbreak;\n\t\t\tcase TextTags.Text:\n\t\t\t\tif (v.Style == 0) _ProcessText();\n\t\t\t\tbreak;\n\t\t\tcase TextTags.CodeBlockStart or TextTags.CodeBlockEnd: continue;\n#if DEBUG\n\t\t\tcase TextTags.Space:\n\t\t\tcase TextTags.Constant:\n\t\t\tcase TextTags.EnumMember:\n\t\t\tcase TextTags.Event:\n\t\t\tcase TextTags.ExtensionMethod:\n\t\t\tcase TextTags.Field:\n\t\t\tcase TextTags.Local:\n\t\t\tcase TextTags.Parameter:\n\t\t\tcase TextTags.Property:\n\t\t\tcase TextTags.RangeVariable:\n\t\t\tcase TextTags.Alias:\n\t\t\tcase TextTags.Label:\n\t\t\tcase TextTags.ContainerStart:\n\t\t\tcase TextTags.ContainerEnd:\n\t\t\tcase TextTags.AnonymousTypeIndicator:\n\t\t\tcase TextTags.ErrorType:\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tDebug_.Print($\"{v.Tag}, '{v.Text}', {v.Style}\");\n\t\t\t\tbreak;\n#endif\n\t\t\t}\n\t\t\t\n\t\t\tswitch (v.Style) {\n\t\t\tcase TaggedTextStyle.Strong: StartBold(); break;\n\t\t\tcase TaggedTextStyle.Emphasis: StartItalic(); break;\n\t\t\tcase TaggedTextStyle.Code: StartSpan(\"codeSpan\"); break;\n\t\t\t}\n\t\t\t\n\t\t\tif (c != null) Run(c, s); else Append(s);\n\t\t\t\n\t\t\tswitch (v.Style) {\n\t\t\tcase TaggedTextStyle.Code: EndSpan(); break;\n\t\t\tcase TaggedTextStyle.Emphasis: EndItalic(); break;\n\t\t\tcase TaggedTextStyle.Strong: EndBold(); break;\n\t\t\t}\n\t\t\t\n\t\t\tvoid _ProcessText() {\n\t\t\t\t//replace [](xref:topic_id) with Hyperlink\n\t\t\t\tif (!s.Contains(\"](xref:\")) return;\n\t\t\t\tif (s_xrefmap == null) {\n\t\t\t\t\ts_xrefmap = new();\n\t\t\t\t\ttry {\n\t\t\t\t\t\tvar s1 = filesystem.loadText(folders.ThisApp + \"xrefmap.yml\"); //generated by DocFX project\n\t\t\t\t\t\tforeach (var k in s1.RxFindAll(@\"(?m)^- uid: (.+)\\R  name: (.+)\\R  href: (.+)$\")) {\n\t\t\t\t\t\t\ts_xrefmap.Add(k[1].Value, (k[3].Value, k[2].Value));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcatch { }\n\t\t\t\t}\n\t\t\t\tint i = 0;\n\t\t\t\tforeach (var m in s.RxFindAll(@\"\\[(.*?)\\]\\(xref:(.+?)\\)\")) {\n\t\t\t\t\tif (!s_xrefmap.TryGetValue(m[2].Value, out var v)) continue;\n\t\t\t\t\tif (m.Start > i) Append(s[i..m.Start]);\n\t\t\t\t\tHyperlink(v.url, m[1].Length > 0 ? m[1].Value : v.text);\n\t\t\t\t\ti = m.End;\n\t\t\t\t}\n\t\t\t\tif (i > 0) s = s[i..];\n\t\t\t}\n\t\t}\n\t}\n\t\n\tstatic Dictionary<string, (string url, string text)> s_xrefmap;\n\t\n\tpublic void AppendTaggedParts(ImmutableArray<SymbolDisplayPart> parts, bool? isParameters = null)\n\t\t=> AppendTaggedParts(parts.ToTaggedText(), isParameters);\n\t\n\tpublic static Section FromTaggedParts(IEnumerable<TaggedText> tags) {\n\t\tvar x = new CiText();\n\t\tx.StartParagraph();\n\t\tx.AppendTaggedParts(tags, false);\n\t\tx.EndParagraph();\n\t\treturn x.Result;\n\t}\n\t\n\tpublic static Section FromSymbols(IEnumerable<ISymbol> symbols, int iSelect, SemanticModel model, int position) {\n\t\t//perf.first();\n\t\tvar x = new CiText();\n\t\tx.AppendSymbols(symbols, iSelect, model, position);\n\t\treturn x.Result;\n\t}\n\t\n\tpublic void AppendSymbols(IEnumerable<ISymbol> symbols, int iSelect, SemanticModel model, int position) {\n\t\t//perf.first();\n\t\tISymbol sym = null;\n\t\tint i = -1;\n\t\tforeach (var v in symbols) {\n\t\t\tif (++i == iSelect) sym = v;\n\t\t\tStartOverload(i == iSelect, i);\n\t\t\tvar parts = v.ToDisplayParts(s_symbolFullFormat);\n\t\t\tAppendTaggedParts(parts);\n\t\t\tif (v.Kind == SymbolKind.Parameter) _AppendComment(\"parameter\");\n\t\t\tEndOverload(i == iSelect);\n\t\t}\n\t\t//perf.next();\n\t\t\n\t\tif (sym != null) {\n\t\t\t//print.it($\"<><c blue>{sym}<>\");\n\t\t\t//print.it(sym.GetType().GetInterfaces());\n\t\t\t\n\t\t\tvar parts = GetSymbolDescription(sym, model, position);\n\t\t\t//print.it(parts);\n\t\t\tif (parts.Any()) {\n\t\t\t\tStartParagraph();\n\t\t\t\tAppendTaggedParts(parts, false);\n\t\t\t\tEndParagraph();\n\t\t\t}\n\t\t\t\n\t\t\t//print.it(sym.GetDocumentationCommentXml());\n\t\t\t\n\t\t\tISymbol enclosing = null;\n\t\t\tswitch (sym.Kind) {\n\t\t\tcase SymbolKind.NamedType:\n\t\t\tcase SymbolKind.Event:\n\t\t\tcase SymbolKind.Field:\n\t\t\tcase SymbolKind.Method:\n\t\t\tcase SymbolKind.Property:\n\t\t\t\tSeparator();\n\t\t\t\t\n\t\t\t\t//HELP AND SOURCE LINKS\n\t\t\t\tStartDiv();\n\t\t\t\tAppendSymbolLinks(sym);\n\t\t\t\tEndDiv();\n\t\t\t\t\n\t\t\t\t//CONTAINER namespace/type and assembly\n\t\t\t\tStartDiv();\n\t\t\t\tAppend(\"In \");\n\t\t\t\tvar ctn = sym.ContainingType ?? sym.ContainingNamespace as INamespaceOrTypeSymbol;\n\t\t\t\tif (!(ctn is INamespaceSymbol ins && ins.IsGlobalNamespace)) {\n\t\t\t\t\tAppend((ctn is ITypeSymbol its) ? its.TypeKind.ToString().Lower() : \"namespace\"); Append(\" \");\n\t\t\t\t\tAppendTaggedParts(ctn.ToDisplayParts(s_qualifiedTypeFormat), false);\n\t\t\t\t\tAppend(\", \");\n\t\t\t\t}\n\t\t\t\tAppend(sym.IsFromSource() ? \"file \" : \"assembly \"); Append(sym.ContainingAssembly.Name);\n\t\t\t\tEndDiv();\n\t\t\t\t\n\t\t\t\t//MODIFIERS\n\t\t\t\tbool isReadonly = false, isConst = false, isVolatile = false, isAsync = false, isEnumMember = false;\n\t\t\t\tvar tKind = TypeKind.Unknown; //0\n\t\t\t\tINamedTypeSymbol baseType = null; string enumBaseType = null; ImmutableArray<INamedTypeSymbol> interfaces = default;\n\t\t\t\tswitch (sym) {\n\t\t\t\tcase IFieldSymbol ifs:\n\t\t\t\t\tif (isEnumMember = ifs.IsEnumMember()) {\n\t\t\t\t\t\tenumBaseType = ifs.ContainingType.EnumUnderlyingType.ToNameDisplayString();\n\t\t\t\t\t} else {\n\t\t\t\t\t\tisReadonly = ifs.IsReadOnly;\n\t\t\t\t\t\tisConst = ifs.IsConst;\n\t\t\t\t\t\tisVolatile = ifs.IsVolatile;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase INamedTypeSymbol ints:\n\t\t\t\t\tisReadonly = ints.IsReadOnly;\n\t\t\t\t\ttKind = ints.TypeKind;\n\t\t\t\t\tswitch (tKind) {\n\t\t\t\t\tcase TypeKind.Class:\n\t\t\t\t\t\tvar bt = ints.BaseType;\n\t\t\t\t\t\tif (bt != null && bt.BaseType != null) baseType = bt; //not object\n\t\t\t\t\t\tgoto case TypeKind.Interface;\n\t\t\t\t\tcase TypeKind.Interface:\n\t\t\t\t\tcase TypeKind.Struct:\n\t\t\t\t\t\tinterfaces = ints.AllInterfaces;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase TypeKind.Enum:\n\t\t\t\t\t\tenumBaseType = ints.EnumUnderlyingType.ToNameDisplayString();\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase IMethodSymbol ims:\n\t\t\t\t\tisAsync = ims.IsAsync;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (!isEnumMember) {\n\t\t\t\t\tStartDiv();\n\t\t\t\t\tstring s1 = sym.DeclaredAccessibility switch {\n\t\t\t\t\t\tMicrosoft.CodeAnalysis.Accessibility.Public => \"public\",\n\t\t\t\t\t\tMicrosoft.CodeAnalysis.Accessibility.Private => \"private\",\n\t\t\t\t\t\tMicrosoft.CodeAnalysis.Accessibility.Internal => \"internal\",\n\t\t\t\t\t\tMicrosoft.CodeAnalysis.Accessibility.Protected => \"protected\",\n\t\t\t\t\t\tMicrosoft.CodeAnalysis.Accessibility.ProtectedAndInternal => \"private protected\",\n\t\t\t\t\t\tMicrosoft.CodeAnalysis.Accessibility.ProtectedOrInternal => \"protected internal\",\n\t\t\t\t\t\t_ => null\n\t\t\t\t\t};\n\t\t\t\t\tif (s1 != null) _AppendKeyword(s1);\n\t\t\t\t\t\n\t\t\t\t\tif (isConst) _AppendSpaceKeyword(\"const\"); else if (sym.IsStatic) _AppendSpaceKeyword(\"static\");\n\t\t\t\t\tif (sym.IsAbstract && tKind != TypeKind.Interface) _AppendSpaceKeyword(\"abstract\");\n\t\t\t\t\tif (sym.IsSealed && (tKind == TypeKind.Class || tKind == TypeKind.Unknown)) _AppendSpaceKeyword(\"sealed\");\n\t\t\t\t\tif (sym.IsVirtual) _AppendSpaceKeyword(\"virtual\");\n\t\t\t\t\tif (sym.IsOverride) _AppendSpaceKeyword(\"override\");\n\t\t\t\t\tif (sym.IsExtern) _AppendSpaceKeyword(\"extern\");\n\t\t\t\t\tif (isReadonly) _AppendSpaceKeyword(\"readonly\");\n\t\t\t\t\tif (isAsync) _AppendSpaceKeyword(\"async\");\n\t\t\t\t\tif (isVolatile) _AppendSpaceKeyword(\"volatile\");\n\t\t\t\t\tEndDiv();\n\t\t\t\t\tvoid _AppendSpaceKeyword(string name) {\n\t\t\t\t\t\tif (_last is not Paragraph) Append(\" \");\n\t\t\t\t\t\tRun(\"keyword\", name);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t//BASE TYPES AND IMPLEMENTED INTERFACES\n\t\t\t\tif (baseType != null || enumBaseType != null || !interfaces.IsDefaultOrEmpty) {\n\t\t\t\t\tStartDiv();\n\t\t\t\t\tAppend(\": \");\n\t\t\t\t\tif (enumBaseType != null) {\n\t\t\t\t\t\t_AppendTypeName(enumBaseType, \"keyword\");\n\t\t\t\t\t} else if (baseType != null) {\n\t\t\t\t\t\t_AppendType(baseType);\n\t\t\t\t\t\tfor (baseType = baseType.BaseType; baseType.BaseType != null; baseType = baseType.BaseType) {\n\t\t\t\t\t\t\tAppend(\" : \");\n\t\t\t\t\t\t\t_AppendType(baseType);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (!interfaces.IsDefaultOrEmpty) {\n\t\t\t\t\t\tbool comma = baseType != null || enumBaseType != null;\n\t\t\t\t\t\tforeach (var v in interfaces) {\n\t\t\t\t\t\t\t_AppendComma(ref comma);\n\t\t\t\t\t\t\tbool hilite = v.Name == \"IDisposable\";\n\t\t\t\t\t\t\tif (hilite) StartSpan(\"hilite\");\n\t\t\t\t\t\t\t_AppendType(v);\n\t\t\t\t\t\t\tif (hilite) EndSpan();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tEndDiv();\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t//ATTRIBUTES\n\t\t\t\t_AppendAttributes(sym.GetAttributes());\n\t\t\t\tswitch (sym) {\n\t\t\t\tcase IMethodSymbol ims:\n\t\t\t\t\t_AppendAttributes(ims.GetReturnTypeAttributes(), \"return\");\n\t\t\t\t\tforeach (var v in ims.Parameters) _AppendAttributes(v.GetAttributes(), v.Name, isParameter: true);\n\t\t\t\t\tbreak;\n\t\t\t\tcase IPropertySymbol ips:\n\t\t\t\t\tvar ipgs = ips.GetMethod;\n\t\t\t\t\tif (ipgs != null) {\n\t\t\t\t\t\t_AppendAttributes(ipgs.GetAttributes(), \"get\");\n\t\t\t\t\t\t_AppendAttributes(ipgs.GetReturnTypeAttributes(), \"return\");\n\t\t\t\t\t}\n\t\t\t\t\tvar ipss = ips.SetMethod;\n\t\t\t\t\tif (ipss != null) _AppendAttributes(ipss.GetAttributes(), \"set\");\n\t\t\t\t\tvar ipfs = ips.GetBackingFieldIfAny();\n\t\t\t\t\tif (ipfs != null) _AppendAttributes(ipfs.GetAttributes(), \"field\");\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t//TYPE MEMBERS NOT SHOWN IN THE COMPLETION LIST: constructors, finalizers, operators\n\t\t\t\tif ((tKind == TypeKind.Class || tKind == TypeKind.Struct /*|| tKind == TypeKind.Interface*/) && sym is INamespaceOrTypeSymbol t) {\n\t\t\t\t\tList<ISymbol> a = null;\n\t\t\t\t\tforeach (var v in t.GetMembers()) {\n\t\t\t\t\t\tswitch (v) {\n\t\t\t\t\t\tcase IMethodSymbol m:\n\t\t\t\t\t\t\tswitch (m.MethodKind) {\n\t\t\t\t\t\t\tcase MethodKind.Ordinary:\n\t\t\t\t\t\t\tcase MethodKind.PropertyGet:\n\t\t\t\t\t\t\tcase MethodKind.PropertySet:\n\t\t\t\t\t\t\tcase MethodKind.EventAdd:\n\t\t\t\t\t\t\tcase MethodKind.EventRemove:\n\t\t\t\t\t\t\tcase MethodKind.ExplicitInterfaceImplementation:\n\t\t\t\t\t\t\tcase MethodKind.Destructor:\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t//case MethodKind.Destructor: goto g1; //don't check is accessible etc\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (m.IsImplicitlyDeclared) continue; //eg default ctor of struct\n\t\t\t\t\t\t\t//print.it(m.MethodKind, m);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t//case IPropertySymbol p when p.IsIndexer: //now indexers are in completion list\n\t\t\t\t\t\t//\tbreak;\n\t\t\t\t\t\tdefault: continue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!_IsAccessible(v) || v.IsObsolete()) continue;\n\t\t\t\t\t\t//g1:\n\t\t\t\t\t\t(a ??= new List<ISymbol>()).Add(v);\n\t\t\t\t\t}\n\t\t\t\t\tif (a != null) {\n\t\t\t\t\t\tSeparator();\n\t\t\t\t\t\t//string header = tKind switch { TypeKind.Class => \"Constructors, finalizers, operators, indexers\", TypeKind.Struct => \"Constructors, operators, indexers\", _ => \"Indexers\" };\n\t\t\t\t\t\t//string header = tKind == TypeKind.Interface ? \"Indexers\" : \"Constructors, operators, indexers\";\n\t\t\t\t\t\tstring header = \"Constructors, operators\";\n\t\t\t\t\t\tHeader(header);\n\t\t\t\t\t\ta.Sort((v1, v2) => {\n\t\t\t\t\t\t\t//var diff = v1.Kind - v2.Kind; //indexer is property, others are methods\n\t\t\t\t\t\t\t//if (diff != 0) return diff;\n\t\t\t\t\t\t\tif (v1 is IMethodSymbol m1 && v2 is IMethodSymbol m2) return _Sort(m1) - _Sort(m2);\n\t\t\t\t\t\t\tstatic int _Sort(IMethodSymbol sy) => sy.MethodKind switch { MethodKind.StaticConstructor => 0, MethodKind.Constructor => 1, /*MethodKind.Destructor => 2,*/ MethodKind.Conversion => 3, MethodKind.UserDefinedOperator => 4, _ => 0 };\n\t\t\t\t\t\t\treturn 0;\n\t\t\t\t\t\t});\n\t\t\t\t\t\tforeach (var v in a) {\n\t\t\t\t\t\t\tStartDiv();\n\t\t\t\t\t\t\tRun(\"dot\", \"• \");\n\t\t\t\t\t\t\tif (v is IMethodSymbol m && m.MethodKind == MethodKind.StaticConstructor) _AppendKeyword(\"static \");\n\t\t\t\t\t\t\tAppendTaggedParts(v.ToDisplayParts(s_symbolFullFormat));\n\t\t\t\t\t\t\tEndDiv();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t//never mind: also should add those of base types.\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t//perf.nw();\n\t\t\t\n\t\t\tbool _IsAccessible(ISymbol symbol) {\n\t\t\t\tenclosing ??= model.GetEnclosingNamedTypeOrAssembly(position, default);\n\t\t\t\treturn enclosing != null && symbol.IsAccessibleWithin(enclosing);\n\t\t\t}\n\t\t\tvoid _AppendComma(ref bool isComma) { if (!isComma) isComma = true; else Append(\", \"); }\n\t\t\tvoid _AppendKeyword(string name) { Run(\"keyword\", name); }\n\t\t\tvoid _AppendTypeName(string name, string cssClass = \"type\") => Run(cssClass, name);\n\t\t\tvoid _AppendType(INamedTypeSymbol t) {\n\t\t\t\tif (t.IsGenericType) AppendTaggedParts(t.ToDisplayParts(s_unqualifiedTypeFormat), false);\n\t\t\t\telse _AppendTypeName(t.Name);\n\t\t\t}\n\t\t\tvoid _AppendAttributes(ImmutableArray<AttributeData> a, string target = null, bool isParameter = false) {\n\t\t\t\tif (a.IsDefaultOrEmpty) return;\n\t\t\t\tbool attrComma = false, paramAdded = false;\n\t\t\t\tforeach (var att in a) {\n\t\t\t\t\tvar ac = att.AttributeClass; if (ac == null) continue;\n\t\t\t\t\t//if(!_IsAccessible(ac)) continue; //no, would not show attributes if code does not have 'using' for the attribute\n\t\t\t\t\tstring aname = ac.Name.RemoveSuffix(\"Attribute\");\n\t\t\t\t\tif (aname == \"CompilerGenerated\" || aname == \"IteratorStateMachine\") continue;\n\t\t\t\t\t//att.ToString(); //similar, but too much noise\n\t\t\t\t\tif (isParameter) {\n\t\t\t\t\t\tif (!paramAdded) {\n\t\t\t\t\t\t\tparamAdded = true;\n\t\t\t\t\t\t\tStartDiv();\n\t\t\t\t\t\t\tAppend(target + \": [\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\t_AppendComma(ref attrComma);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tStartDiv();\n\t\t\t\t\t\tAppend(\"[\");\n\t\t\t\t\t\tif (target != null) Run(\"keyword\", target, \": \");\n\t\t\t\t\t}\n\t\t\t\t\tbool hilite = aname == \"Obsolete\";\n\t\t\t\t\tif (hilite) StartSpan(\"hilite\");\n\t\t\t\t\t_AppendTypeName(aname);\n\t\t\t\t\tif (hilite) EndSpan();\n\t\t\t\t\tvar ca = att.CommonConstructorArguments;\n\t\t\t\t\tvar na = att.CommonNamedArguments;\n\t\t\t\t\tif (ca.Length + na.Length > 0) {\n\t\t\t\t\t\tAppend(\"(\");\n\t\t\t\t\t\tbool paramComma = false;\n\t\t\t\t\t\tforeach (var v in ca) {\n\t\t\t\t\t\t\t_AppendComma(ref paramComma);\n\t\t\t\t\t\t\tAppend(v.ToCSharpString()); //never mind: enum with namespace\n\t\t\t\t\t\t}\n\t\t\t\t\t\tforeach (var v in na) {\n\t\t\t\t\t\t\t_AppendComma(ref paramComma);\n\t\t\t\t\t\t\tAppend(v.Key + \" = \");\n\t\t\t\t\t\t\tAppend(v.Value.ToCSharpString());\n\t\t\t\t\t\t}\n\t\t\t\t\t\tAppend(\")\");\n\t\t\t\t\t}\n\t\t\t\t\tif (!isParameter) { Append(\"]\"); EndDiv(); }\n\t\t\t\t}\n\t\t\t\tif (isParameter && paramAdded) { Append(\"]\"); EndDiv(); }\n\t\t\t}\n\t\t}\n\t\tvoid _AppendComment(string s) {\n\t\t\tRun(\"comment\", \"   //\" + s);\n\t\t}\n\t}\n\t\n\tpublic static ImmutableArray<TaggedText> GetSymbolDescription(ISymbol sym, SemanticModel model, int position) {\n\t\treturn sym.GetDocumentationParts(model, position, _Formatter, default);\n\t}\n\t\n\t//public static IEnumerable<TaggedText> GetTaggedTextForXml(string xml, SemanticModel model, int position)\n\t//{\n\t//\tif(xml == null) return Enumerable.Empty<TaggedText>();\n\t//\treturn _Formatter.Format(xml, model, position, ISymbolExtensions2.CrefFormat); //error in new Roslyn\n\t//}\n\t\n\tstatic IDocumentationCommentFormattingService _Formatter => s_formatter ??= CodeInfo.CurrentWorkspace.Workspace.Services.GetLanguageServices(\"C#\").GetService<IDocumentationCommentFormattingService>();\n\tstatic IDocumentationCommentFormattingService s_formatter;\n\t\n\tconst SymbolDisplayMiscellaneousOptions s_miscDisplayOptions =\n\t\tSymbolDisplayMiscellaneousOptions.AllowDefaultLiteral\n\t\t| SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers\n\t\t| SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier //? //info: added 2024-11-21. Test more.\n\t\t//| SymbolDisplayMiscellaneousOptions.IncludeNotNullableReferenceTypeModifier //!\n\t\t| SymbolDisplayMiscellaneousOptions.RemoveAttributeSuffix\n\t\t| SymbolDisplayMiscellaneousOptions.UseErrorTypeSymbolName\n\t\t| SymbolDisplayMiscellaneousOptions.UseSpecialTypes;\n\tconst SymbolDisplayParameterOptions s_parameterDisplayOptions =\n\t\tSymbolDisplayParameterOptions.IncludeType\n\t\t| SymbolDisplayParameterOptions.IncludeName\n\t\t| SymbolDisplayParameterOptions.IncludeParamsRefOut\n\t\t| SymbolDisplayParameterOptions.IncludeOptionalBrackets\n\t\t| SymbolDisplayParameterOptions.IncludeDefaultValue\n\t\t| SymbolDisplayParameterOptions.IncludeExtensionThis;\n\t\n\tinternal static readonly SymbolDisplayFormat s_symbolFullFormat = new(\n\t\tSymbolDisplayGlobalNamespaceStyle.OmittedAsContaining,\n\t\tSymbolDisplayTypeQualificationStyle.NameAndContainingTypes,\n\t\tSymbolDisplayGenericsOptions.IncludeTypeParameters | SymbolDisplayGenericsOptions.IncludeVariance | SymbolDisplayGenericsOptions.IncludeTypeConstraints,\n\t\tSymbolDisplayMemberOptions.IncludeType | SymbolDisplayMemberOptions.IncludeParameters | SymbolDisplayMemberOptions.IncludeConstantValue | SymbolDisplayMemberOptions.IncludeRef | SymbolDisplayMemberOptions.IncludeExplicitInterface,\n\t\tSymbolDisplayDelegateStyle.NameAndSignature,\n\t\tSymbolDisplayExtensionMethodStyle.Default,\n\t\ts_parameterDisplayOptions,\n\t\tSymbolDisplayPropertyStyle.ShowReadWriteDescriptor,\n\t\tSymbolDisplayLocalOptions.IncludeType | SymbolDisplayLocalOptions.IncludeRef | SymbolDisplayLocalOptions.IncludeConstantValue,\n\t\tSymbolDisplayKindOptions.IncludeMemberKeyword | SymbolDisplayKindOptions.IncludeNamespaceKeyword | SymbolDisplayKindOptions.IncludeTypeKeyword,\n\t\ts_miscDisplayOptions\n\t\t);\n\tstatic SymbolDisplayFormat s_qualifiedTypeFormat = new(\n\t\tSymbolDisplayGlobalNamespaceStyle.OmittedAsContaining,\n\t\tSymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,\n\t\tSymbolDisplayGenericsOptions.IncludeTypeParameters | SymbolDisplayGenericsOptions.IncludeVariance,\n\t\tmiscellaneousOptions: s_miscDisplayOptions\n\t\t);\n\tstatic SymbolDisplayFormat s_unqualifiedTypeFormat = new(\n\t\tSymbolDisplayGlobalNamespaceStyle.Omitted,\n\t\tSymbolDisplayTypeQualificationStyle.NameOnly,\n\t\tSymbolDisplayGenericsOptions.IncludeTypeParameters | SymbolDisplayGenericsOptions.IncludeVariance,\n\t\tmiscellaneousOptions: s_miscDisplayOptions\n\t\t);\n\tstatic SymbolDisplayFormat s_parameterFormat = new(\n\t\tSymbolDisplayGlobalNamespaceStyle.Omitted,\n\t\tSymbolDisplayTypeQualificationStyle.NameAndContainingTypes,\n\t\tgenericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters | SymbolDisplayGenericsOptions.IncludeVariance,\n\t\tparameterOptions: s_parameterDisplayOptions,\n\t\tmiscellaneousOptions: s_miscDisplayOptions\n\t\t);\n\tstatic SymbolDisplayFormat s_symbolWithoutParametersFormat = new(\n\t\tSymbolDisplayGlobalNamespaceStyle.Omitted,\n\t\tSymbolDisplayTypeQualificationStyle.NameAndContainingTypes,\n\t\tSymbolDisplayGenericsOptions.IncludeTypeParameters | SymbolDisplayGenericsOptions.IncludeVariance,\n\t\tSymbolDisplayMemberOptions.IncludeType | SymbolDisplayMemberOptions.IncludeRef | SymbolDisplayMemberOptions.IncludeExplicitInterface,\n\t\tmiscellaneousOptions: s_miscDisplayOptions\n\t\t);\n\tstatic SymbolDisplayFormat s_nameWithoutTypeParametersFormat = new(\n\t\tSymbolDisplayGlobalNamespaceStyle.Omitted,\n\t\tSymbolDisplayTypeQualificationStyle.NameOnly,\n\t\tmiscellaneousOptions: s_miscDisplayOptions\n\t\t);\n\t\n\tpublic void AppendSymbolWithoutParameters(ISymbol sym) {\n\t\tAppendTaggedParts(sym.ToDisplayParts(sym is INamedTypeSymbol ? s_nameWithoutTypeParametersFormat : s_symbolWithoutParametersFormat), false);\n\t}\n\t\n\tpublic void AppendParameters(ISymbol sym, int iSel = -1, SignatureHelpItem sh = null) {\n\t\tImmutableArray<IParameterSymbol> ap;\n\t\tswitch (sym) {\n\t\tcase IMethodSymbol sy:\n\t\t\tap = sy.Parameters;\n\t\t\tbreak;\n\t\tcase IPropertySymbol sy: //indexer\n\t\t\tap = sy.Parameters;\n\t\t\tbreak;\n\t\tcase INamedTypeSymbol sy:\n\t\t\tif (sy.IsTupleType && !sy.IsDefinition) {\n\t\t\t\tvar te = sy.TupleElements;\n\t\t\t\tfor (int i = 0; i < te.Length; i++) {\n\t\t\t\t\t_Param(te[i], i, 1);\n\t\t\t\t}\n\t\t\t} else if (sy.IsGenericType) {\n\t\t\t\tvar tp = sy.TypeParameters;\n\t\t\t\tfor (int i = 0; i < tp.Length; i++) {\n\t\t\t\t\t_Param(sy.TypeParameters[i], i, 2);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tDebug_.Print(sy);\n\t\t\t}\n\t\t\treturn;\n\t\tdefault:\n\t\t\tDebug_.Print(sym);\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tfor (int i = 0; i < ap.Length; i++) _Param(ap[i], i, 0);\n\t\tif (sh != null) { //eg properties in [Attribute(params, properties)]. Can be used for params too, but formats not as I like.\n\t\t\tvar ap2 = sh.Parameters;\n\t\t\tfor (int i = ap.Length; i < ap2.Length; i++) {\n\t\t\t\tif (i > 0) Append(\", \");\n\t\t\t\tif (i == iSel) StartBold();\n\t\t\t\tAppendTaggedParts(ap2[i].DisplayParts);\n\t\t\t\tif (i == iSel) EndBold();\n\t\t\t}\n\t\t}\n\t\t\n\t\tvoid _Param(ISymbol p, int i, byte kind) { //kind: 0 param, 1 field, 2 typeparam\n\t\t\tif (i > 0) Append(\", \");\n\t\t\tif (i == iSel) StartBold();\n\t\t\tAppendTaggedParts(p.ToDisplayParts(kind == 1 ? s_symbolWithoutParametersFormat : s_parameterFormat), isParameters: kind == 0);\n\t\t\t//info: s_symbolWithoutParametersFormat here maybe is not the best, but it worked well with all tested tuple parameter types.\n\t\t\tif (i == iSel) EndBold();\n\t\t}\n\t}\n\t\n\t//public void AppendParameters(SignatureHelpItem sh, int iSel = -1)\n\t//{\n\t//\tvar ap = sh.Parameters;\n\t//\tfor(int i = 0; i < ap.Length; i++) {\n\t//\t\tif(i > 0) Append(\", \");\n\t//\t\tif(i == iSel) StartBold();\n\t//\t\tAppendTaggedParts(ap[i].DisplayParts);\n\t//\t\tif(i == iSel) EndBold();\n\t//\t}\n\t//}\n\t\n\tpublic bool AppendSymbolLinks(ISymbol sym) {\n\t\tstring helpUrl = CiUtil.GetSymbolHelpUrl(sym, out _);\n\t\tstring sourceUrl = CiGoTo.GetLinkData(sym);\n\t\tif (helpUrl == null && sourceUrl == null) return false;\n\t\tAppendSymbolLinks(helpUrl, sourceUrl);\n\t\treturn true;\n\t}\n\t\n\tpublic void AppendSymbolLinks(string helpUrl, string sourceUrl) {\n\t\tAppend(helpUrl != null && sourceUrl != null ? \"Links: \" : \"Link: \");\n\t\tif (helpUrl != null) Hyperlink(helpUrl, \"more info\");\n\t\tif (sourceUrl != null) {\n\t\t\tif (helpUrl != null) Append(\" , \");\n\t\t\tHyperlink(sourceUrl, \"source code\");\n\t\t}\n\t}\n\t\n\tpublic static Section FromKeyword(string name) {\n\t\t//var url = \"https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/\";\n\t\tvar url = App.Settings.internetSearchUrl + \"C%23+keyword\";\n\t\tvar x = new CiText();\n\t\tx.StartParagraph();\n\t\tx.Append(\"Keyword \");\n\t\tx.Run(\"keyword\", name);\n\t\tx.EndParagraph();\n\t\tx.Separator();\n\t\tx.StartParagraph();\n\t\tx.Append(\"Links: \");\n\t\tx.Hyperlink($\"{url} {name}\", \"more info\", \" , \");\n\t\tx.Hyperlink($\"{url}s\", \"C# keywords\", \".\");\n\t\tx.EndParagraph();\n\t\treturn x.Result;\n\t}\n\t\n\tpublic static Section FromLabel(string name) {\n\t\tvar x = new CiText();\n\t\tx.StartParagraph();\n\t\tx.Append(\"Label \"); x.Bold(name);\n\t\tx.EndParagraph();\n\t\treturn x.Result;\n\t}\n\t\n\tpublic static FlowDocumentControl CreateControl(int colorRGB = 0xfffff0) {\n\t\tvar ff = App.Wmain.FontFamily;\n\t\tdouble fs = App.Wmain.FontSize;\n\t\tif (ff.Source == \"Segoe UI\") { ff = new(\"Calibri\"); fs += 2; }\n\t\tvar d = new FlowDocument {\n\t\t\tFontFamily = ff,\n\t\t\tFontSize = fs,\n\t\t\tBackground = ColorInt.WpfBrush_(colorRGB),\n\t\t\tPagePadding = default,\n\t\t\tTextAlignment = TextAlignment.Left,\n\t\t};\n\t\tTextOptions.SetTextFormattingMode(d, TextFormattingMode.Display);\n\t\tvar c = new FlowDocumentControl {\n\t\t\tDocument = d,\n\t\t\tFocusVisualStyle = null,\n\t\t\tVerticalScrollBarVisibility = ScrollBarVisibility.Auto,\n\t\t\tIsInactiveSelectionHighlightEnabled = true,\n\t\t};\n\t\treturn c;\n\t}\n}\n\nclass FlowDocumentControl : FlowDocumentScrollViewer {\n\tpublic event Action<FlowDocumentControl, string> LinkClicked;\n\t\n\tinternal static void OnLinkClicked_(Hyperlink h, RequestNavigateEventArgs e) {\n\t\tvar s = e.Uri.OriginalString;\n\t\t//print.it(s);\n\t\tDependencyObject d = h;\n\t\twhile (null != (d = LogicalTreeHelper.GetParent(d))) if (d is FlowDocumentControl c) {\n\t\t\t\tc.LinkClicked?.Invoke(c, s);\n\t\t\t\tbreak;\n\t\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Clears text (removes all blocks) and applies a workaround for a WPF bug.\n\t/// </summary>\n\tpublic void Clear() {\n\t\tDocument.Blocks.Clear();\n\t\t_ = Selection?.IsEmpty; //workaround for WPF bug: if some text was selected, would select all new text\n\t}\n\t\n\tprotected override void OnPreviewMouseLeftButtonUp(MouseButtonEventArgs e) {\n\t\tif (!Selection.IsEmpty) this.Hwnd().ActivateL(); //user may want to copy text with Ctrl+C\n\t\tbase.OnPreviewMouseLeftButtonUp(e);\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Edit/CiTools.cs",
    "content": "using ToolLand;\nusing Au.Controls;\nusing Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp;\nusing Microsoft.CodeAnalysis.CSharp.Syntax;\nusing Microsoft.CodeAnalysis.Text;\nusing System.Windows.Controls;\n\n//FUTURE: Color tool. Eg to set toolbar colors.\n//\tNow can select in the Icons dialog or capture with Duiimage.\n\nnamespace LA;\n\nclass CiTools {\n\tpublic bool HideTempWindows() {\n\t\tbool v1 = _regexWindow?.IsVisible ?? false, v2 = _keysWindow?.IsVisible ?? false;\n\t\tif (v1) _regexWindow.Close();\n\t\tif (v2) _keysWindow.Close();\n\t\treturn v1 || v2;\n\t}\n\t\n\t#region regex\n\t\n\tRegexWindow _regexWindow;\n\tstring _regexTopic;\n\t\n\tvoid _RegexWindowShow(SciCode doc, string code, int pos16, in CiStringInfo si, bool replace, wnd dontCover = default) {\n\t\tvar (spanStart, spanEnd) = si.textSpan;\n\t\t\n\t\t_regexWindow ??= new();\n\t\t_ShowWindow(_regexWindow, doc, pos16, dontCover);\n\t\t\n\t\tif (!replace && si.isClassic) {\n\t\t\tdoc.aaaInsertText(true, si.stringNode.SpanStart, \"@\"); //never mind: user may want `new regexp(\"\"\"\"\"\")`\n\t\t\tspanStart++; spanEnd++;\n\t\t}\n\t\t\n\t\tvar s = _regexWindow.CurrentTopic;\n\t\tif (s == \"replace\") {\n\t\t\tif (!replace) _regexWindow.CurrentTopic = _regexTopic;\n\t\t} else if (replace) {\n\t\t\t_regexTopic = s;\n\t\t\t_regexWindow.CurrentTopic = \"replace\";\n\t\t}\n\t\tdoc.ETempRanges_Add(this, spanStart, spanEnd, onLeave: () => _regexWindow.Close());\n\t}\n\t\n\t//public bool RegexWindowIsVisible => _regexWindow?.Window.Visible ?? false;\n\t\n\t#endregion\n\t\n\t#region keys\n\t\n\tKeysWindow _keysWindow;\n\t\n\tvoid _KeysWindowShow(SciCode doc, string code, int pos16, in CiStringInfo si, PSFormat format, wnd dontCover = default) {\n\t\t_keysWindow ??= new KeysWindow();\n\t\t_ShowWindow(_keysWindow, doc, pos16, dontCover);\n\t\t_keysWindow.SetFormat(format);\n\t\tdoc.ETempRanges_Add(this, si.textSpan.Start, si.textSpan.End, onLeave: () => _keysWindow.Close());\n\t}\n\t\n\t#endregion\n\t\n\tstatic void _ShowWindow(InfoWindow w, SciCode doc, int position, wnd dontCover) {\n\t\tif (w.IsVisible) w.Hwnd.ZorderTop();\n\t\tvar r = doc.EGetCaretRectFromPos(position, inScreen: true);\n\t\tr.left -= Dpi.Scale(80, doc);\n\t\tbool above = !dontCover.Is0;\n\t\tif (above) r = RECT.Union(r, dontCover.Rect);\n\t\tw.ShowByRect(doc, above ? Dock.Top : Dock.Bottom, r, exactSize: true);\n\t\tw.InsertInControl = doc;\n\t}\n\t\n\tpublic static void CmdShowRegexWindow() => _ShowRegexOrKeysWindow(true);\n\tpublic static void CmdShowKeysWindow() => _ShowRegexOrKeysWindow(false);\n\t\n\tstatic void _ShowRegexOrKeysWindow(bool isRegex) {\n\t\tbool retry = false;\n\t\tg1:\n\t\tif (!CodeInfo.GetDocumentAndFindToken(out var cd, out var token)) return;\n\t\tvar pos16 = cd.pos;\n\t\tbool? inString = token.IsInString(pos16, cd.code, out var stri);\n\t\tif (inString == null) return;\n\t\tif (inString != true) {\n\t\t\tif (isRegex || retry) {\n\t\t\t\tdialog.showInfo(isRegex ? \"Regex\" : \"Keys\", \"The text cursor must be in a string.\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\t//is in keys.send argument list?\n\t\t\tvar node = token.Parent;\n\t\t\tif (node != null && node is not ArgumentListSyntax && !node.Span.ContainsInside(pos16)) {\n\t\t\t\tnode = node.Parent;\n\t\t\t\tif (node is ArgumentSyntax) node = node.Parent;\n\t\t\t}\n\t\t\tif (node is ArgumentListSyntax && node.Parent is InvocationExpressionSyntax ie && ie.Expression.ToString() == \"keys.send\") {\n\t\t\t\tSyntaxToken t1, t2;\n\t\t\t\tif (pos16 <= token.SpanStart) { t1 = token.GetPreviousToken(); t2 = token; } else { t1 = token; t2 = token.GetNextToken(); }\n\t\t\t\t//CiUtil.PrintNode(t1);\n\t\t\t\t//CiUtil.PrintNode(t2);\n\t\t\t\tSyntaxKind k1 = t1.Kind(), k2 = t2.Kind();\n\t\t\t\tbool good1 = k1 is SyntaxKind.OpenParenToken or SyntaxKind.CommaToken, good2 = k2 is SyntaxKind.CloseParenToken or SyntaxKind.CommaToken;\n\t\t\t\tstring s;\n\t\t\t\tif (good1 && good2) s = \"\\\"`|`\\\"\"; else if (good1) s = \"\\\"`|`\\\", \"; else if (good2) s = \", \\\"`|`\\\"\"; else s = \", \\\"`|`\\\", \";\n\t\t\t\tInsertCode.TextSimply(s);\n\t\t\t} else {\n\t\t\t\tInsertCode.Statements(new(\"keys.send(\\\"`|`\\\");\", goTo: true));\n\t\t\t}\n\t\t\tretry = true;\n\t\t\tgoto g1;\n\t\t}\n\t\t\n\t\tvar t = CodeInfo._tools;\n\t\tif (isRegex) {\n\t\t\tt._RegexWindowShow(cd.sci, cd.code, pos16, stri, replace: false);\n\t\t} else {\n\t\t\tPSFormat format = PSFormat.Keys;\n\t\t\tif (inString == true) {\n\t\t\t\tvar semo = cd.semanticModel;\n\t\t\t\tformat = CiUtil.GetParameterStringFormat(token.Parent, semo, true);\n\t\t\t}\n\t\t\tt._KeysWindowShow(cd.sci, cd.code, pos16, stri, format);\n\t\t}\n\t}\n\t\n\tpublic void ShowForStringParameter(PSFormat stringFormat, CodeInfo.Context cd, in CiStringInfo si, wnd dontCover = default) {\n\t\tswitch (stringFormat) {\n\t\tcase PSFormat.Regexp:\n\t\tcase PSFormat.RegexpReplacement:\n\t\t\t_RegexWindowShow(cd.sci, cd.code, cd.pos, si, replace: stringFormat == PSFormat.RegexpReplacement, dontCover);\n\t\t\tbreak;\n\t\tcase PSFormat.Keys or PSFormat.Hotkey or PSFormat.HotkeyTrigger or PSFormat.TriggerMod:\n\t\t\t_KeysWindowShow(cd.sci, cd.code, cd.pos, si, stringFormat, dontCover);\n\t\t\tbreak;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Edit/CiUtil.cs",
    "content": "extern alias CAW;\n\nusing Microsoft.CodeAnalysis;\nusing CAW::Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp;\nusing Microsoft.CodeAnalysis.CSharp.Syntax;\nusing Microsoft.CodeAnalysis.Text;\nusing Microsoft.CodeAnalysis.Shared.Extensions;\nusing CAW::Microsoft.CodeAnalysis.Shared.Extensions;\nusing Microsoft.CodeAnalysis.CSharp.Extensions;\nusing Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;\nusing CAW::Microsoft.CodeAnalysis.Classification;\nusing CAW::Microsoft.CodeAnalysis.Tags;\nusing CAW::Microsoft.CodeAnalysis.FindSymbols;\n\nusing System.Collections.Immutable;\nusing EStyle = LA.SciTheme.EStyle;\n\nnamespace LA;\n\nstatic class CiUtil {\n\t#region simple string util\n\t\n\t/// <summary>\n\t/// Returns <c>=> c is ' ' or '\\t';</c>\n\t/// </summary>\n\tpublic static bool IsSpace(char c) => c is ' ' or '\\t';\n\t\n\t/// <summary>\n\t/// Returns true if <i>pos</i> is in <i>code</i> and <c>code[pos] is ' ' or '\\t'</c>.\n\t/// </summary>\n\tpublic static bool IsSpace(string code, int pos) => (uint)pos < code.Length && code[pos] is ' ' or '\\t';\n\t\n\t/// <summary>\n\t/// Skips <c>' '</c> and <c>'\\t'</c> characters after <i>pos</i>.\n\t/// </summary>\n\tpublic static int SkipSpace(string code, int pos) {\n\t\twhile (IsSpace(code, pos)) pos++;\n\t\treturn pos;\n\t}\n\t\n\t/// <summary>\n\t/// Skips <c>' '</c> and <c>'\\t'</c> characters before <i>pos</i>.\n\t/// </summary>\n\tpublic static int SkipSpaceBack(string code, int pos) {\n\t\twhile (IsSpace(code, pos - 1)) pos--;\n\t\treturn pos;\n\t}\n\t\n\t/// <summary>\n\t/// Returns the start and end of the range consisting of <c>' '</c> and <c>'\\t'</c> characters around <i>pos</i>.\n\t/// </summary>\n\tpublic static (int start, int end) SkipSpaceAround(string code, int pos) {\n\t\tint start = pos, end = pos;\n\t\twhile (IsSpace(code, start - 1)) start--;\n\t\twhile (IsSpace(code, end)) end++;\n\t\treturn (start, end);\n\t}\n\t\n\t/// <summary>\n\t/// If <i>pos</i> is at the end of a line and not at the end of the string, returns the start of next line. Else returns <i>pos</i>.\n\t/// </summary>\n\tpublic static int SkipNewline(string code, int pos) {\n\t\tif (pos < code.Length && code[pos] == '\\r') pos++;\n\t\tif (pos < code.Length && code[pos] == '\\n') pos++;\n\t\treturn pos;\n\t}\n\t\n\t/// <summary>\n\t/// If <i>pos</i> is at the start of a line, returns the end of previous line. Else returns <i>pos</i>.\n\t/// </summary>\n\tpublic static int SkipNewlineBack(string code, int pos) {\n\t\tif (pos > 0 && code[pos - 1] == '\\n') pos--;\n\t\tif (pos > 0 && code[pos - 1] == '\\r') pos--;\n\t\treturn pos;\n\t}\n\t\n\t/// <summary>\n\t/// Skips <c>' '</c>, <c>'\\t'</c>, <c>'\\r'</c> and <c>'\\n'</c> characters after <i>pos</i>.\n\t/// </summary>\n\tpublic static int SkipSpaceAndNewline(string code, int pos) {\n\t\twhile (pos < code.Length && code[pos] is ' ' or '\\t' or '\\r' or '\\n') pos++;\n\t\treturn pos;\n\t}\n\t\n\t/// <summary>\n\t/// Skips <c>' '</c>, <c>'\\t'</c>, <c>'\\r'</c> and <c>'\\n'</c> characters before <i>pos</i>.\n\t/// </summary>\n\tpublic static int SkipSpaceAndNewlineBack(string code, int pos) {\n\t\twhile (pos > 0 && code[pos - 1] is ' ' or '\\t' or '\\r' or '\\n') pos--;\n\t\treturn pos;\n\t}\n\t\n\t/// <summary>\n\t/// Returns true if i is at a line start + any number of spaces and tabs.\n\t/// </summary>\n\tpublic static bool IsLineStart(RStr s, int i/*, out int startOfLine*/) {\n\t\twhile (i > 0 && s[i - 1] is '\\t' or ' ') i--;\n\t\t//startOfLine = j;\n\t\treturn i == 0 || s[i - 1] == '\\n';\n\t}\n\t\n\t/// <summary>\n\t/// Returns true if i is at a line start + any number of spaces and tabs.\n\t/// </summary>\n\t/// <param name=\"startOfLine\">Receives i or the start of horizontal whitespace before i.</param>\n\tpublic static bool IsLineStart(RStr s, int i, out int startOfLine) {\n\t\twhile (i > 0 && s[i - 1] is '\\t' or ' ') i--;\n\t\tstartOfLine = i;\n\t\treturn i == 0 || s[i - 1] == '\\n';\n\t}\n\t\n\t/// <summary>\n\t/// Creates string containing n tabs or n*4 spaces, depending on <b>App.Settings.ci_formatTabIndent</b>.\n\t/// See also <see cref=\"CiUtilExt.AppendIndent\"/>.\n\t/// </summary>\n\tpublic static string CreateIndentString(int n) {\n\t\tif (n < 1) return \"\";\n\t\tif (App.Settings.ci_formatTabIndent) return new('\\t', n);\n\t\treturn new(' ', n * 4);\n\t}\n\t\n\t/// <summary>\n\t/// Returns string with same indent as of the document line from pos.\n\t/// The string must not contain multiline raw/verbatim strings; this func ignores it.\n\t/// </summary>\n\tpublic static string IndentStringForInsertSimple(string s, SciCode doc, int pos, bool indentFirstLine = false, int indentPlus = 0) {\n\t\tif (s.Contains('\\n')) {\n\t\t\tint indent = doc.aaaLineIndentFromPos(true, pos) + indentPlus;\n\t\t\t//if (!App.Settings.ci_formatTabIndent) s = s.RxReplace(@\"(?m)^\\t+\", m => CreateIndentString(m.Length)); //rejected. This could be useful for snippets. But then also need to apply App.Settings.ci_formatCompact=false, eg move braces to new lines, indent switch block. Then also need to do all it in code inserted by tools. Better let users format code afterwards.\n\t\t\tif (indent > 0) s = s.RxReplace(indentFirstLine ? @\"(?m)^\" : @\"(?<=\\n)\", CreateIndentString(indent));\n\t\t}\n\t\treturn s;\n\t}\n\t\n\tpublic static string GoogleURL(string query) => App.Settings.internetSearchUrl + System.Net.WebUtility.UrlEncode(query);\n\t\n\t#endregion\n\t\n\t#region syntax\n\t\n\t/// <summary>\n\t/// Returns true if <i>pos</i> is in trivia where normal tokens are not allowed. For example in comment, doccomment, directive, disabled text.\n\t/// </summary>\n\tpublic static bool IsPosInNonblankTrivia(SyntaxNode node, int pos, string code) {\n\t\tif (pos > 0) {\n\t\t\tbool minus1 = (pos == code.Length || SyntaxFacts.IsNewLine(code[pos])) && !SyntaxFacts.IsNewLine(code[pos - 1]); //end of text or non-empty line. At the left can be eg `//comment`.\n\t\t\tif (minus1) pos--;\n\t\t\tvar trivia = node.FindTrivia(pos);\n\t\t\tvar tk = trivia.Kind();\n\t\t\tif (!(tk is 0 or SyntaxKind.WhitespaceTrivia or SyntaxKind.EndOfLineTrivia)) {\n\t\t\t\tvar ts = trivia.Span;\n\t\t\t\t//print.it($\"{trivia.Kind()}, {pos}, {trivia.FullSpan}, {ts}, '{trivia}'\");\n\t\t\t\tif (tk is SyntaxKind.DisabledTextTrivia or SyntaxKind.EndIfDirectiveTrivia or SyntaxKind.ElifDirectiveTrivia or SyntaxKind.ElseDirectiveTrivia or SyntaxKind.BadDirectiveTrivia) return true;\n\t\t\t\tif (minus1 && pos + 1 == ts.End && tk is SyntaxKind.MultiLineCommentTrivia or SyntaxKind.MultiLineDocumentationCommentTrivia) return false;\n\t\t\t\tif (pos > trivia.FullSpan.Start && pos < ts.End) return true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\t\n\t/// <summary>\n\t/// Returns true if <i>code</i> contains global statements or is empty or the first method of the first class is named \"Main\".\n\t/// </summary>\n\tpublic static bool IsScript(string code) {\n\t\tvar cu = CreateSyntaxTree(code);\n\t\tvar f = cu.Members.FirstOrDefault();\n\t\tif (f != null) {\n\t\t\tif (f is GlobalStatementSyntax) return true;\n\t\t\tif (f is BaseNamespaceDeclarationSyntax nd) f = nd.Members.FirstOrDefault();\n\t\t\tif (f is ClassDeclarationSyntax cd && cd.Members.OfType<MethodDeclarationSyntax>().FirstOrDefault()?.Identifier.Text == \"Main\") return true;\n\t\t} else {\n\t\t\tvar u = cu.Usings.FirstOrDefault();\n\t\t\tif (u != null && u.GlobalKeyword.RawKind != 0) return false; //global.cs?\n\t\t\treturn !cu.AttributeLists.Any(); //AssemblyInfo.cs?\n\t\t}\n\t\treturn false;\n\t}\n\t\n\t#endregion\n\t\n\t#region symbols\n\t\n\tpublic static (ISymbol symbol, CodeInfo.Context cd) GetSymbolFromPos(bool andZeroLength = false, bool preferVar = false) {\n\t\tif (!CodeInfo.GetContextAndDocument(out var cd)) return default;\n\t\treturn (GetSymbolFromPos(cd, andZeroLength, preferVar), cd);\n\t}\n\t\n\tpublic static ISymbol GetSymbolFromPos(CodeInfo.Context cd, bool andZeroLength = false, bool preferVar = false) {\n\t\tif (andZeroLength && _TryGetAltSymbolFromPos(cd) is ISymbol s1) return s1;\n\t\tvar sym = SymbolFinder.FindSymbolAtPositionAsync(cd.document, cd.pos).Result_();\n\t\tif (sym is IMethodSymbol ims) sym = ims.PartialImplementationPart ?? sym;\n\t\telse if (preferVar && sym is INamedTypeSymbol) { //for 'this' and 'base' SymbolFinder gets INamedTypeSymbol\n\t\t\tint i1 = cd.pos, i2 = i1;\n\t\t\twhile (i1 > 0 && SyntaxFacts.IsIdentifierPartCharacter(cd.code[i1 - 1])) i1--;\n\t\t\twhile (i2 < cd.code.Length && SyntaxFacts.IsIdentifierPartCharacter(cd.code[i2])) i2++;\n\t\t\tif (cd.code.Eq(i1..i2, \"this\") || cd.code.Eq(i1..i2, \"base\")) {\n\t\t\t\tvar node = cd.syntaxRoot.FindToken(cd.pos).Parent;\n\t\t\t\tvar semo = cd.semanticModel;\n\t\t\t\treturn semo.GetSymbolInfo(node).GetAnySymbol() ?? semo.GetDeclaredSymbolForNode(node);\n\t\t\t}\n\t\t}\n\t\treturn sym;\n\t}\n\t\n\tstatic ISymbol _TryGetAltSymbolFromPos(CodeInfo.Context cd) {\n\t\tif (cd.code.Eq(cd.pos, '[')) { //indexer?\n\t\t\tvar t = cd.syntaxRoot.FindToken(cd.pos, true);\n\t\t\tif (t.IsKind(SyntaxKind.OpenBracketToken) && t.Parent is BracketedArgumentListSyntax { Parent: ElementAccessExpressionSyntax es }) {\n\t\t\t\treturn cd.semanticModel.GetSymbolInfo(es).GetAnySymbol();\n\t\t\t}\n\t\t}\n\t\t//rejected: in the same way get cast operator if pos is before '('. Not very useful.\n\t\treturn null;\n\t}\n\t\n\tpublic static (ISymbol symbol, string keyword, HelpKind helpKind, SyntaxToken token) GetSymbolEtcFromPos(CodeInfo.Context cd, bool forHelp = false) {\n\t\tif (_TryGetAltSymbolFromPos(cd) is ISymbol s1) return (s1, null, default, default);\n\t\t\n\t\tint pos = cd.pos; if (pos > 0 && SyntaxFacts.IsIdentifierPartCharacter(cd.code[pos - 1])) pos--;\n\t\tif (!cd.syntaxRoot.FindTouchingToken(out var token, pos, findInsideTrivia: true)) return default;\n\t\t\n\t\tstring word = cd.code[token.Span.ToRange()];\n\t\t\n\t\tvar k = token.Kind();\n\t\tif (k == SyntaxKind.IdentifierToken) {\n\t\t\tswitch (word) {\n\t\t\tcase \"var\" when forHelp: //else get the inferred type\n\t\t\tcase \"dynamic\":\n\t\t\tcase \"nameof\":\n\t\t\tcase \"unmanaged\": //tested cases\n\t\t\t\treturn (null, word, HelpKind.ContextualKeyword, token);\n\t\t\t}\n\t\t\t\n\t\t\tif (GetSymbolFromPos(cd) is not { } sym) return (null, null, default, token);\n\t\t\t\n\t\t\t//if pressed F1 inside identifier in `identifier[...]`, it's very likely wants help for the indexer. Let's show menu.\n\t\t\tif (forHelp && CiQuickInfo.GetIndexerToken(cd, token, out var tok2)) {\n\t\t\t\tif (tok2.Parent is BracketedArgumentListSyntax { Parent: ElementAccessExpressionSyntax es }) {\n\t\t\t\t\tif (cd.semanticModel.GetSymbolInfo(es).GetAnySymbol() is { } sym2) {\n\t\t\t\t\t\tswitch (popupMenu.showSimple([sym.Name, \"[...]\"], PMFlags.ByCaret, rawText: true)) {\n\t\t\t\t\t\tcase 1: break;\n\t\t\t\t\t\tcase 2: sym = sym2; break;\n\t\t\t\t\t\tdefault: return (null, null, default, token);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\treturn (sym, null, default, token);\n\t\t} else if (token.Parent is ImplicitObjectCreationExpressionSyntax && (!forHelp || cd.pos == token.Span.End)) {\n\t\t\t//for 'new(' get the ctor or type\n\t\t} else if (k == SyntaxKind.BaseKeyword) {\n\t\t\t\n\t\t} else {\n\t\t\t//print.it(\n\t\t\t//\t//token.IsKeyword(), //IsReservedKeyword||IsContextualKeyword, but not IsPreprocessorKeyword\n\t\t\t//\tSyntaxFacts.IsReservedKeyword(k), //also true for eg #if\n\t\t\t//\tSyntaxFacts.IsContextualKeyword(k)\n\t\t\t//\t//SyntaxFacts.IsQueryContextualKeyword(k) //included in IsContextualKeyword\n\t\t\t//\t//SyntaxFacts.IsAccessorDeclarationKeyword(k),\n\t\t\t//\t//SyntaxFacts.IsPreprocessorKeyword(k), //true if #something or can be used in #something context. Also true for eg if without #.\n\t\t\t//\t//SyntaxFacts.IsPreprocessorContextualKeyword(k) //badly named. True only if #something.\n\t\t\t//\t);\n\t\t\t\n\t\t\tif (SyntaxFacts.IsReservedKeyword(k)) {\n\t\t\t\tbool pp = (word == \"if\" || word == \"else\") && token.GetPreviousToken().IsKind(SyntaxKind.HashToken);\n\t\t\t\tif (pp) word = \"#\" + word;\n\t\t\t\treturn (null, word, pp ? HelpKind.PreprocKeyword : HelpKind.ReservedKeyword, token);\n\t\t\t}\n\t\t\tif (SyntaxFacts.IsContextualKeyword(k)) {\n\t\t\t\treturn (null, word, SyntaxFacts.IsAttributeTargetSpecifier(k) ? HelpKind.AttributeTarget : HelpKind.ContextualKeyword, token);\n\t\t\t}\n\t\t\tif (SyntaxFacts.IsPreprocessorKeyword(k)) {\n\t\t\t\t//if(SyntaxFacts.IsPreprocessorContextualKeyword(k)) word = \"#\" + word; //better don't use this internal func\n\t\t\t\tif (token.GetPreviousToken().IsKind(SyntaxKind.HashToken)) word = \"#\" + word;\n\t\t\t\treturn (null, word, HelpKind.PreprocKeyword, token);\n\t\t\t}\n\t\t\tif (token.Parent is BaseArgumentListSyntax bals) {\n\t\t\t\tif (!GetFunctionSymbolInfoFromArgumentList(bals, cd.semanticModel, out var si)) return default;\n\t\t\t\treturn (si.GetAnySymbol(), null, default, token);\n\t\t\t}\n\t\t\tswitch (token.IsInString(cd.pos, cd.code, out _)) {\n\t\t\tcase true: return (null, null, HelpKind.String, token);\n\t\t\tcase null: return default;\n\t\t\t}\n\t\t}\n\t\t//note: don't pass contextual keywords to FindSymbolAtPositionAsync or GetSymbolInfo.\n\t\t//\tIt may get info for something other, eg 'new' -> ctor or type, or 'int' -> type 'Int32'.\n\t\t\n\t\treturn (GetSymbolFromPos(cd), null, default, token);\n\t}\n\t\n\t/// <summary>\n\t/// Gets SymbolInfo of invoked method, ctor or indexer from its argument list.\n\t/// </summary>\n\tpublic static bool GetFunctionSymbolInfoFromArgumentList(BaseArgumentListSyntax als, SemanticModel semo, out SymbolInfo si) {\n\t\tsi = default;\n\t\tvar pa = als.Parent;\n\t\tif (als is ArgumentListSyntax && pa is InvocationExpressionSyntax or BaseObjectCreationExpressionSyntax) {\n\t\t\tsi = semo.GetSymbolInfo(pa);\n\t\t} else if (als is BracketedArgumentListSyntax && pa is ElementAccessExpressionSyntax eacc) {\n\t\t\tsi = semo.GetSymbolInfo(eacc);\n\t\t} else return false;\n\t\treturn !si.IsEmpty;\n\t}\n\t\n\tpublic enum HelpKind {\n\t\tNone, ReservedKeyword, ContextualKeyword, AttributeTarget, PreprocKeyword, String\n\t}\n\t\n\tpublic static void OpenSymbolEtcFromPosHelp() {\n\t\tstring url = null;\n\t\tif (!CodeInfo.GetContextAndDocument(out var cd)) return;\n\t\tvar (sym, keyword, helpKind, _) = GetSymbolEtcFromPos(cd, forHelp: true);\n\t\tif (sym != null) {\n\t\t\turl = GetSymbolHelpUrl(sym, out bool isAu);\n\t\t\tif (isAu) {\n\t\t\t\tHelpUtil.AuHelp(url);\n\t\t\t\treturn;\n\t\t\t}\n\t\t} else if (keyword != null) {\n\t\t\tvar s = helpKind switch {\n\t\t\t\tHelpKind.PreprocKeyword => \"preprocessor directive\",\n\t\t\t\tHelpKind.AttributeTarget => \"attributes, \",\n\t\t\t\t_ => \"keyword\"\n\t\t\t};\n\t\t\ts = $\"C# {s} \\\"{keyword}\\\"\";\n\t\t\t//print.it(s); return;\n\t\t\turl = GoogleURL(s);\n\t\t} else if (helpKind == HelpKind.String) {\n\t\t\tint i = popupMenu.showSimple(\"1 C# strings|2 String formatting|3 Wildcard expression|4 Output tags|11 Regex tool (Ctrl+Space)|12 Keys tool (Ctrl+Space)\", PMFlags.ByCaret);\n\t\t\tswitch (i) {\n\t\t\tcase 1: url = \"C# strings\"; break;\n\t\t\tcase 2: url = \"C# string formatting\"; break;\n\t\t\tcase 3: HelpUtil.AuHelp(\"articles/Wildcard expression\"); break;\n\t\t\tcase 4: HelpUtil.AuHelp(\"articles/Output tags\"); break;\n\t\t\tcase 11: CiTools.CmdShowRegexWindow(); break;\n\t\t\tcase 12: CiTools.CmdShowKeysWindow(); break;\n\t\t\t}\n\t\t\tif (url != null) url = GoogleURL(url);\n\t\t}\n\t\tif (url != null) run.itSafe(url);\n\t}\n\t\n\t/// <summary>\n\t/// From position in code gets ArgumentSyntax and its IParameterSymbol.\n\t/// </summary>\n\t/// <param name=\"arg\">Not null if returns true and the argument list isn't empty.</param>\n\t/// <param name=\"ps\">If returns true, the array contains 1 or more elements. Multiple if cannot resolve overload.</param>\n\t/// <param name=\"filter\"></param>\n\tpublic static bool GetArgumentParameterFromPos(BaseArgumentListSyntax als, int pos, SemanticModel semo, out ArgumentSyntax arg, out IParameterSymbol[] ps, Func<IParameterSymbol, bool> filter = null) {\n\t\targ = null; ps = null;\n\t\tvar args = als.Arguments;\n\t\tvar index = args.Count == 0 ? 0 : args.IndexOf(o => pos <= o.FullSpan.End); //print.it(index);\n\t\tif (index < 0) return default;\n\t\tif (!CiUtil.GetFunctionSymbolInfoFromArgumentList(als, semo, out var si)) return default;\n\t\tstring name = null;\n\t\tif (args.Count > 0) {\n\t\t\targ = args[index];\n\t\t\tif (arg.NameColon is { } nc) name = nc.Name.Identifier.Text;\n\t\t}\n\t\tps = GetParameterSymbol(si, index, name, args.Count, filter)\n\t\t\t.DistinctBy(o => o.Type.ToString()) //tested with Task.Run. 8 overloads, 4 distinct parameter types.\n\t\t\t.ToArray();\n\t\treturn ps.Length > 0;\n\t}\n\t\n\t/// <summary>\n\t/// Gets IParameterSymbol of siFunction's parameter matching argument index or name.\n\t/// Can return multiple if cannot resolve overload.\n\t/// </summary>\n\t/// <param name=\"siFunction\">SymbolInfo of the method, ctor or indexer.</param>\n\t/// <param name=\"index\">Argument index. Not used if used name.</param>\n\t/// <param name=\"name\">Parameter name, if specified in the argument, else null.</param>\n\t/// <param name=\"argCount\">Count of arguments.</param>\n\t/// <param name=\"filter\"></param>\n\tpublic static IEnumerable<IParameterSymbol> GetParameterSymbol(SymbolInfo siFunction, int index, string name, int argCount, Func<IParameterSymbol, bool> filter = null) {\n\t\tif (argCount == 0 && siFunction.Symbol is IMethodSymbol { ContainingType: var ct } ims && ims.Parameters.Length == 0) { //GetAllSymbols would contain just ims, but we need overloads with parameters\n\t\t\tforeach (var v in ct.GetMembers(ims.Name)) {\n\t\t\t\tif (v is IMethodSymbol m && m != ims) {\n\t\t\t\t\tif (_Get(m) is { } r) yield return r;\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tforeach (var v in siFunction.GetAllSymbols()) {\n\t\t\t\tif (_Get(v) is { } r) yield return r;\n\t\t\t}\n\t\t}\n\t\t\n\t\tIParameterSymbol _Get(ISymbol fsym) {\n\t\t\tvar parms = fsym switch { IMethodSymbol ms => ms.Parameters, IPropertySymbol ps => ps.Parameters, _ => default };\n\t\t\tif (!parms.IsDefaultOrEmpty && parms.Length >= argCount) {\n\t\t\t\tvar ps = name != null ? parms.FirstOrDefault(o => o.Name == name) : index < parms.Length ? parms[index] : null;\n\t\t\t\tif (ps != null) if (filter == null || filter(ps)) return ps;\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Gets <b>ILocalSymbol</b> or <b>IParameterSymbol</b> of the nearest declared/accessible local variable or parameter of one of specified types.\n\t/// Uses current document and caret position.\n\t/// Returns null if not found.\n\t/// </summary>\n\t/// <param name=\"types\">Fully qualified type name. The type must be in an assembly, not in source.</param>\n\t/// <exception cref=\"ArgumentException\">Type not found.</exception>\n\tpublic static ISymbol GetNearestLocalVariableOfType(params string[] types) {\n\t\tif (!CodeInfo.GetContextAndDocument(out var cd)) return null;\n\t\tvar semo = cd.semanticModel;\n\t\tvar ats = types.Select(o => semo.Compilation.GetTypeByMetadataName(o) ?? throw new ArgumentException($\"Type not found: {o}.\"));\n\t\tvar a = GetLocalVariablesAt(semo, cd.pos, o => ats.Contains(o));\n\t\treturn a.Count > 0 ? a[^1] : null;\n\t}\n\t\n\t/// <summary>\n\t/// Gets <b>ILocalSymbol</b> or <b>IParameterSymbol</b> of local variables and parameters that can be used at position <i>pos</i>. The order is the same as declared in code.\n\t/// Not perfect.\n\t/// </summary>\n\tpublic static List<ISymbol> GetLocalVariablesAt(SemanticModel semo, int pos, Func<ITypeSymbol, bool> ofType = null) {\n\t\tvar a = new List<ISymbol>();\n\t\tvar scopes = _GetLocalScopes(semo, pos);\n\t\tif (scopes.Count > 0) {\n\t\t\tvar e = semo.GetAllDeclaredSymbols(scopes[^1], default);\n\t\t\t//var e=semo.GetAllDeclaredSymbols(scopes[^1], default, o => { CiUtil.PrintNode(o); return true; });\n\t\t\tforeach (var v in e) {\n\t\t\t\t//if (v is not (ILocalSymbol or IParameterSymbol)) print.it(v.Name, v.GetTypeDisplayName());\n\t\t\t\tif (v is not (ILocalSymbol or IParameterSymbol)) continue;\n\t\t\t\tif (ofType != null && !ofType(v.GetSymbolType())) continue;\n\t\t\t\tvar n2 = v.DeclaringSyntaxReferences.First().GetSyntax();\n\t\t\t\tvar s2 = _GetLocalScope(n2);\n\t\t\t\tif (!scopes.Contains(s2)) {\n\t\t\t\t\t//print.it($\"<>    <c #ff8080>{v.Name}, {v.GetSymbolType()}<>\");\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tvar span = n2 is ForEachStatementSyntax fe ? fe.Identifier.Span : n2.Span;\n\t\t\t\tif (pos <= span.End) {\n\t\t\t\t\t//print.it($\"<>    <c #c0c0c0>{v.Name}, {v.GetSymbolType()}<>\");\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t//print.it(v.Name, v.GetSymbolType());\n\t\t\t\ta.Add(v);\n\t\t\t}\n\t\t}\n\t\treturn a;\n\t}\n\t\n\t/// <summary>\n\t/// Enumerates properties and fields of the enclosing class/struct.\n\t/// Skips write-only properties, indexers and const fields. Enums both static and non-static.\n\t/// </summary>\n\tpublic static IEnumerable<ISymbol> EnumPropertiesAndFields(SemanticModel semo, int pos) {\n\t\tif (semo.GetEnclosingNamedType(pos, default) is { TypeKind: TypeKind.Class or TypeKind.Struct } t) {\n\t\t\tforeach (var v in t.GetMembers().Where(o => o is IFieldSymbol or IPropertySymbol)) {\n\t\t\t\tif (v is IPropertySymbol ips) {\n\t\t\t\t\tif (ips.IsIndexer || ips.IsWriteOnly) continue;\n\t\t\t\t} else if (v is IFieldSymbol ifs) {\n\t\t\t\t\tif (ifs.IsConst || ifs.AssociatedSymbol != null) continue;\n\t\t\t\t} else continue;\n\t\t\t\t\n\t\t\t\tyield return v;\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Gets ancestor scopes of local variables and parameters.\n\t/// For { block } gets its parent if there may be declared variables/parameters that are visible only in that block; eg function declaration or foreach statement.\n\t/// </summary>\n\t/// <param name=\"semo\"></param>\n\t/// <param name=\"pos\"></param>\n\tstatic List<SyntaxNode> _GetLocalScopes(SemanticModel semo, int pos) {\n\t\tvar a = new List<SyntaxNode>();\n\t\tvar node = semo.SyntaxTree.GetCompilationUnitRoot().FindToken(pos).Parent;\n\t\tbool inside = false;\n\t\tforeach (var v in node.AncestorsAndSelf()) {\n\t\t\tif (v is CompilationUnitSyntax) {\n\t\t\t\ta.Add(v);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (!(inside || (inside = v.Span.ContainsInside(pos)))) continue;\n\t\t\tif (_IsLocalScope(v)) {\n\t\t\t\ta.Add(v);\n\t\t\t\tif (v is BaseMethodDeclarationSyntax) break;\n\t\t\t\tif (v is LocalFunctionStatementSyntax lf && lf.Modifiers.Any(SyntaxKind.StaticKeyword)) break;\n\t\t\t\tif (v is AnonymousFunctionExpressionSyntax af && af.Modifiers.Any(SyntaxKind.StaticKeyword)) break;\n\t\t\t} else if (v is BaseTypeDeclarationSyntax or NamespaceDeclarationSyntax) {\n\t\t\t\ta.Clear();\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn a;\n\t}\n\t\n\t/// <summary>\n\t/// Returns true if local/parameter variables declared inside n aren't visible outside.\n\t/// </summary>\n\t/// <param name=\"n\"></param>\n\tstatic bool _IsLocalScope(SyntaxNode n) {\n\t\tif (n is BlockSyntax) return !_Is2(n.Parent);\n\t\treturn _Is2(n) || n is SwitchSectionSyntax or CompilationUnitSyntax;\n\t\t\n\t\tstatic bool _Is2(SyntaxNode n) => n is BaseMethodDeclarationSyntax\n\t\t\tor LocalFunctionStatementSyntax\n\t\t\tor AnonymousFunctionExpressionSyntax\n\t\t\tor ForStatementSyntax\n\t\t\tor CommonForEachStatementSyntax\n\t\t\tor CatchClauseSyntax\n\t\t\tor FixedStatementSyntax\n\t\t\tor UsingStatementSyntax\n\t\t\tor SwitchExpressionArmSyntax\n\t\t;\n\t}\n\t\n\tstatic SyntaxNode _GetLocalScope(SyntaxNode node) => node.FirstAncestorOrSelf<SyntaxNode>(o => _IsLocalScope(o));\n\t\n\t/// <summary>\n\t/// \n\t/// </summary>\n\t/// <param name=\"sym\"></param>\n\t/// <param name=\"isAu\">It's an Au symbol. Returns not URL but just HTML filename like <c>\"Au.Something\"</c> or <c>\"Au.Types.Generic-1\"</c>. Call <see cref=\"HelpUtil.AuHelp\"/>. Another way to detect it - <see cref=\"HelpUtil.IsAuHelp_\"/>.</param>\n\t/// <returns></returns>\n\tpublic static string GetSymbolHelpUrl(ISymbol sym, out bool isAu) {\n\t\tisAu = false;\n\t\t//print.it(sym);\n\t\t//print.it(sym.IsInSource(), sym.IsFromSource());\n\t\tif (sym is IParameterSymbol or ITypeParameterSymbol) return null;\n\t\tstring query;\n\t\tIModuleSymbol metadata = null;\n\t\tforeach (var loc in sym.Locations) {\n\t\t\tif ((metadata = loc.MetadataModule) != null) break;\n\t\t}\n\t\t\n\t\tisAu = metadata?.Name == \"Au.dll\";\n\t\tif (isAu && !sym.HasPublicResultantVisibility()) metadata = null; //no online doc for other than public, protected, protected internal. But the google search is useful eg if it's an Api class member visible because of meta testInternal.\n\t\t\n\t\tif (metadata != null) {\n\t\t\tif (isAu && sym.IsEnumMember()) sym = sym.ContainingType;\n\t\t\t//print.it(sym, sym.GetType(), sym.GetType().GetInterfaces());\n\t\t\tif (sym is INamedTypeSymbol nt && nt.IsGenericType) {\n\t\t\t\tvar qn = sym.QualifiedName(noDirectName: true);\n\t\t\t\tif (isAu) query = qn + \".\" + sym.MetadataName.Replace('`', '-');\n\t\t\t\telse query = $\"{qn}.{sym.Name}<{string.Join(\", \", nt.TypeParameters)}>\";\n\t\t\t} else {\n\t\t\t\tquery = sym.QualifiedName();\n\t\t\t}\n\t\t\t\n\t\t\tif (query.Ends(\"..ctor\")) query = query.ReplaceAt(^6.., isAu ? \".-ctor\" : \" constructor\");\n\t\t\telse if (query.Ends(\".this[]\")) query = query.ReplaceAt(^7.., \".Item\");\n\t\t\t\n\t\t\tif (isAu) return query;\n\t\t\tif (metadata.Name.Starts(\"Au.\")) return null;\n\t\t\t\n\t\t\tstring kind = (sym is INamedTypeSymbol ints) ? ints.TypeKind.ToString() : sym.Kind.ToString();\n\t\t\tquery = query + \" \" + kind.Lower();\n\t\t} else if (!sym.IsInSource() && !isAu) { //eg an operator of string etc\n\t\t\tif (!(sym is IMethodSymbol me && me.MethodKind == MethodKind.BuiltinOperator)) return null;\n\t\t\t//print.it(sym, sym.Kind, sym.QualifiedName());\n\t\t\t//query = \"C# \" + sym.ToString(); //eg \"string.operator +(string, string)\", and Google finds just Equality\n\t\t\t//query = \"C# \" + sym.QualifiedName(); //eg \"System.String.op_Addition\", and Google finds nothing\n\t\t\tquery = \"C# \" + sym.ToString().RxReplace(@\"\\(.+\\)$\", \"\", 1).Replace('.', ' '); //eg C# string operator +, not bad\n\t\t} else if (sym.IsExtern) { //[DllImport]\n\t\t\tquery = sym.Name + \" function\";\n\t\t} else if (sym is INamedTypeSymbol nt1 && nt1.IsComImport) { //[ComImport] interface or coclass\n\t\t\tquery = sym.Name + \" \" + nt1.TypeKind.ToString().Lower();\n\t\t} else if (sym.ContainingType?.IsComImport == true) { //[ComImport] interface method\n\t\t\tquery = sym.ContainingType.Name + \".\" + sym.Name;\n\t\t} else if (_IsNativeApiClass(sym.ContainingType)) {\n\t\t\tif (sym is INamedTypeSymbol nt2) query = sym.Name + \" \" + (nt2.TypeKind switch { TypeKind.Struct => \"structure\", TypeKind.Enum => \"enumeration\", TypeKind.Delegate => \"callback function\", _ => null });\n\t\t\telse query = sym.Name; //constant or Guid/etc\n\t\t} else if (sym is IFieldSymbol && _IsNativeApiClass(sym.ContainingType.ContainingType)) {\n\t\t\tquery = sym.ContainingType.Name + \".\" + sym.Name; //struct field or enum member\n\t\t} else {\n\t\t\treturn null;\n\t\t}\n\t\t\n\t\tstatic bool _IsNativeApiClass(INamedTypeSymbol t)\n\t\t\t=> t?.TypeKind is TypeKind.Class or TypeKind.Struct\n\t\t\t&& (t.BaseType?.Name == \"NativeApi\" || t.Name.Contains(\"Native\") || t.Name.Ends(\"Api\"));\n\t\t\n\t\treturn GoogleURL(query);\n\t}\n\t\n\tpublic static PSFormat GetParameterStringFormat(SyntaxNode node, SemanticModel semo, bool isString, bool ignoreInterpolatedString = false) {\n\t\tvar kind = node.Kind();\n\t\t//print.it(kind);\n\t\tSyntaxNode parent;\n\t\tif (isString || kind == SyntaxKind.StringLiteralExpression) parent = node.Parent;\n\t\telse if (kind == SyntaxKind.InterpolatedStringText && !ignoreInterpolatedString) parent = node.Parent.Parent;\n\t\telse return PSFormat.None;\n\t\t\n\t\twhile (parent is BinaryExpressionSyntax && parent.IsKind(SyntaxKind.AddExpression)) parent = parent.Parent; //\"string\"+\"string\"+...\n\t\t\n\t\tif (parent is ExpressionElementSyntax { Parent: CollectionExpressionSyntax colex }) parent = colex.Parent; //[\"\", \"\"]\n\t\t\n\t\tPSFormat format = PSFormat.None;\n\t\tif (parent is ArgumentSyntax asy) {\n\t\t\tif (parent.Parent is ArgumentListSyntax alis) {\n\t\t\t\tif (alis.Parent is ExpressionSyntax es && es is BaseObjectCreationExpressionSyntax or InvocationExpressionSyntax) {\n\t\t\t\t\tvar si = semo.GetSymbolInfo(es);\n\t\t\t\t\tif (si.Symbol is IMethodSymbol m) {\n\t\t\t\t\t\tformat = _GetFormat(m, alis);\n\t\t\t\t\t} else if (!si.CandidateSymbols.IsDefaultOrEmpty) {\n\t\t\t\t\t\tforeach (var v in si.CandidateSymbols.OfType<IMethodSymbol>()) {\n\t\t\t\t\t\t\tif ((format = _GetFormat(v, alis)) != 0) break;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (parent.Parent is BracketedArgumentListSyntax balis && balis.Parent is ElementAccessExpressionSyntax eacc) {\n\t\t\t\tif (semo.GetSymbolInfo(eacc).Symbol is IPropertySymbol ips && ips.IsIndexer) {\n\t\t\t\t\tvar ims = ips.SetMethod;\n\t\t\t\t\tif (ims != null) format = _GetFormat(ims, balis);\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tPSFormat _GetFormat(IMethodSymbol ims, BaseArgumentListSyntax alis) {\n\t\t\t\tIParameterSymbol p = null;\n\t\t\t\tvar pa = ims.Parameters;\n\t\t\t\tif (pa.Length > 0) {\n\t\t\t\t\tvar nc = asy.NameColon;\n\t\t\t\t\tif (nc != null) {\n\t\t\t\t\t\tvar name = nc.Name.Identifier.Text;\n\t\t\t\t\t\tforeach (var v in pa) if (v.Name == name) { p = v; break; }\n\t\t\t\t\t} else {\n\t\t\t\t\t\tint i; var aa = alis.Arguments;\n\t\t\t\t\t\tfor (i = 0; i < aa.Count; i++) if ((object)aa[i] == asy) break;\n\t\t\t\t\t\tif (i >= pa.Length && pa[^1].IsParams) i = pa.Length - 1;\n\t\t\t\t\t\tif (i < pa.Length) p = pa[i];\n\t\t\t\t\t}\n\t\t\t\t\tif (p != null) {\n\t\t\t\t\t\tforeach (var v in p.GetAttributes()) {\n\t\t\t\t\t\t\tswitch (v.AttributeClass.Name) {\n\t\t\t\t\t\t\tcase nameof(ParamStringAttribute): return v.GetConstructorArgument<PSFormat>(0, SpecialType.None);\n\t\t\t\t\t\t\tcase nameof(System.Diagnostics.CodeAnalysis.StringSyntaxAttribute) when v.GetConstructorArgument<string>(0, SpecialType.System_String) == System.Diagnostics.CodeAnalysis.StringSyntaxAttribute.Regex: return PSFormat.NetRegex; //note: the attribute also can be set on properties and fields. But Regex doesn't have.\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn PSFormat.None;\n\t\t\t}\n\t\t}\n\t\treturn format;\n\t}\n\t\n\t/// <summary>\n\t/// Gets \"global using Namespace;\" directives from all files of compilation. Skips aliases and statics.\n\t/// </summary>\n\tpublic static IEnumerable<UsingDirectiveSyntax> GetAllGlobalUsings(SemanticModel model) {\n\t\tforeach (var st in model.Compilation.SyntaxTrees) {\n\t\t\tforeach (var u in st.GetCompilationUnitRoot().Usings) {\n\t\t\t\tif (u.GlobalKeyword.RawKind == 0) break;\n\t\t\t\tif (u.Alias != null || u.StaticKeyword.RawKind != 0) continue;\n\t\t\t\tyield return u;\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Calls Classifier.GetClassifiedSpansAsync and corrects overlapped items etc.\n\t/// </summary>\n\tpublic static async Task<List<ClassifiedSpan>> GetClassifiedSpansAsync(Document document, int from, int to, CancellationToken cancellationToken = default) {\n\t\tvar e = await Classifier.GetClassifiedSpansAsync(document, TextSpan.FromBounds(from, to)).ConfigureAwait(false);\n\t\treturn _CorrectClassifiedSpans(e);\n\t}\n\t\n\t/// <summary>\n\t/// Calls Classifier.GetClassifiedSpans and corrects overlapped items etc.\n\t/// </summary>\n\tpublic static List<ClassifiedSpan> GetClassifiedSpans(SemanticModel semo, Document document, int from, int to, CancellationToken cancellationToken = default) {\n\t\tvar proj = document.Project;\n\t\tvar opt = new ClassificationOptions { ColorizeRegexPatterns = false, ColorizeJsonPatterns = true }; //default true true, but don't work; turn off regex anyway.\n\t\tvar e = Classifier.GetClassifiedSpans(proj.Solution.Services, proj, semo, TextSpan.FromBounds(from, to), opt, true, cancellationToken);\n\t\treturn _CorrectClassifiedSpans(e);\n\t}\n\t\n\tstatic List<ClassifiedSpan> _CorrectClassifiedSpans(IEnumerable<ClassifiedSpan> espans) {\n\t\tvar a = espans as List<ClassifiedSpan>;\n\t\t//print.clear(); foreach (var v in a) print.it(v.ClassificationType);\n\t\t\n\t\t//Order StringEscapeCharacter correctly. Now in $\"string\" they are randomly after or before the string. Must be after.\n\t\t//\tThis code ignores regex and json tokens, because Classifier.GetClassifiedSpans does not produce them (why?). And callers don't support it too.\n\t\tfor (int k = a.Count; --k > 0;) {\n\t\t\tif (a[k - 1].ClassificationType == ClassificationTypeNames.StringEscapeCharacter)\n\t\t\t\t//if (a[k].ClassificationType == ClassificationTypeNames.StringLiteral || a[k].ClassificationType == ClassificationTypeNames.VerbatimStringLiteral)\n\t\t\t\tif (a[k].TextSpan.Contains(a[k - 1].TextSpan))\n\t\t\t\t\tMath2.Swap(ref a.Ref(k), ref a.Ref(k - 1));\n\t\t}\n\t\t\n\t\t//remove StaticSymbol\n\t\tint i = 0, j = 0;\n\t\tfor (; i < a.Count; i++) {\n\t\t\tif (a[i].ClassificationType == ClassificationTypeNames.StaticSymbol) continue;\n\t\t\tif (j != i) a[j] = a[i];\n\t\t\tj++;\n\t\t}\n\t\tif ((i -= j) > 0) a.RemoveRange(a.Count - i, i);\n\t\t\n\t\treturn a;\n\t}\n\t\n\t/// <summary>\n\t/// For C# code gets style bytes that can be used with SCI_SETSTYLINGEX for UTF-8 text.\n\t/// Uses Classifier.GetClassifiedSpansAsync, like the code editor.\n\t/// Controls that use this should set styles like this example, probably when handle created:\n\t/// <c>var styles = new CiStyling.TTheme { FontSize = 9 };\n\t/// styles.ToScintilla(this);</c>\n\t/// <para>\n\t/// If called in LA tools process or in a non-main LA thread, runs in the main process/thread; returns <c>null</c> if failed.\n\t/// </para>\n\t/// </summary>\n\tpublic static byte[] GetScintillaStylingBytes8(string code) {\n\t\tif (!process.IsLaMainThread_) {\n\t\t\tif (process.IsLaProcess_) return App.Dispatcher.Invoke<byte[]>(() => GetScintillaStylingBytes8(code));\n\t\t\treturn WndCopyData.SendReceive<char>(ScriptEditor.WndMsg_, 15, code, out byte[] r1) ? r1 : null;\n\t\t}\n\t\t\n\t\tvar styles8 = new byte[Encoding.UTF8.GetByteCount(code)];\n\t\tvar map8 = styles8.Length == code.Length ? null : Convert2.Utf8EncodeAndGetOffsets_(code).offsets;\n\t\tusing var ws = new AdhocWorkspace();\n\t\tvar document = CreateDocumentFromCode(ws, code, needSemantic: true);\n\t\t//var semo = document.GetSemanticModelAsync().Result;\n\t\tvar a = GetClassifiedSpansAsync(document, 0, code.Length).Result_();\n\t\tforeach (var v in a) {\n\t\t\t//print.it(v.TextSpan, v.ClassificationType, code[v.TextSpan.Start..v.TextSpan.End]);\n\t\t\tEStyle style = CiStyling.StyleFromClassifiedSpan(v);\n\t\t\tif (style == EStyle.None) continue;\n\t\t\tvar (i, end) = v.TextSpan;\n\t\t\tif (map8 != null) { i = map8[i]; end = map8[end]; }\n\t\t\twhile (i < end) styles8[i++] = (byte)style;\n\t\t}\n\t\treturn styles8;\n\t}\n\t\n\t//not used\n\t///// <summary>\n\t///// For C# code gets style bytes that can be used with <see cref=\"KScintilla.aaaSetStyling\"/> (UTF-16).\n\t///// Uses Classifier.GetClassifiedSpansAsync, like the code editor.\n\t///// Controls that use this should set styles like this example, probably when handle created:\n\t///// <c>var styles = new CiStyling.TTheme { FontSize = 9 };\n\t///// styles.ToScintilla(this);</c>\n\t///// </summary>\n\t//public static byte[] GetScintillaStylingBytes16(string code) {\n\t//\tvar styles8 = new byte[code.Length];\n\t//\tusing var ws = new AdhocWorkspace();\n\t//\tvar document = CreateDocumentFromCode(ws, code, needSemantic: true);\n\t//\t//var semo = document.GetSemanticModelAsync().Result;\n\t//\tvar a = GetClassifiedSpansAsync(document, 0, code.Length).Result;\n\t//\tforeach (var v in a) {\n\t//\t\t//print.it(v.TextSpan, v.ClassificationType, code[v.TextSpan.Start..v.TextSpan.End]);\n\t//\t\tEStyle style = CiStyling.StyleFromClassifiedSpan(v);\n\t//\t\tif (style == EStyle.None) continue;\n\t//\t\tvar (i, end) = v.TextSpan;\n\t//\t\twhile (i < end) styles8[i++] = (byte)style;\n\t//\t}\n\t//\treturn styles8;\n\t//}\n\t\n\tpublic static CiItemKind MemberDeclarationToKind(MemberDeclarationSyntax m) {\n\t\treturn m switch {\n\t\t\tClassDeclarationSyntax or ExtensionBlockDeclarationSyntax => CiItemKind.Class,\n\t\t\tStructDeclarationSyntax => CiItemKind.Structure,\n\t\t\tRecordDeclarationSyntax rd => rd.IsKind(SyntaxKind.RecordStructDeclaration) ? CiItemKind.Structure : CiItemKind.Class,\n\t\t\tEnumDeclarationSyntax => CiItemKind.Enum,\n\t\t\tDelegateDeclarationSyntax => CiItemKind.Delegate,\n\t\t\tInterfaceDeclarationSyntax => CiItemKind.Interface,\n\t\t\tOperatorDeclarationSyntax or ConversionOperatorDeclarationSyntax or IndexerDeclarationSyntax => CiItemKind.Operator,\n\t\t\tBaseMethodDeclarationSyntax => CiItemKind.Method,\n\t\t\t// => CiItemKind.ExtensionMethod,\n\t\t\tPropertyDeclarationSyntax => CiItemKind.Property,\n\t\t\tEventDeclarationSyntax or EventFieldDeclarationSyntax => CiItemKind.Event,\n\t\t\tFieldDeclarationSyntax f => f.Modifiers.Any(o => o.Text == \"const\") ? CiItemKind.Constant : CiItemKind.Field,\n\t\t\tEnumMemberDeclarationSyntax => CiItemKind.EnumMember,\n\t\t\tBaseNamespaceDeclarationSyntax => CiItemKind.Namespace,\n\t\t\t_ => CiItemKind.None\n\t\t};\n\t}\n\t\n\tpublic static void TagsToKindAndAccess(ImmutableArray<string> tags, out CiItemKind kind, out CiItemAccess access) {\n\t\tkind = CiItemKind.None;\n\t\taccess = default;\n\t\tif (tags.IsDefaultOrEmpty) return;\n\t\tkind = tags[0] switch {\n\t\t\tWellKnownTags.Class => CiItemKind.Class,\n\t\t\tWellKnownTags.Structure => CiItemKind.Structure,\n\t\t\tWellKnownTags.Enum => CiItemKind.Enum,\n\t\t\tWellKnownTags.Delegate => CiItemKind.Delegate,\n\t\t\tWellKnownTags.Interface => CiItemKind.Interface,\n\t\t\tWellKnownTags.Method => CiItemKind.Method,\n\t\t\tWellKnownTags.ExtensionMethod => CiItemKind.ExtensionMethod,\n\t\t\tWellKnownTags.Property => CiItemKind.Property,\n\t\t\tWellKnownTags.Operator => CiItemKind.Operator,\n\t\t\tWellKnownTags.Event => CiItemKind.Event,\n\t\t\tWellKnownTags.Field => CiItemKind.Field,\n\t\t\tWellKnownTags.Local => CiItemKind.LocalVariable,\n\t\t\tWellKnownTags.Parameter => CiItemKind.LocalVariable,\n\t\t\tWellKnownTags.RangeVariable => CiItemKind.LocalVariable,\n\t\t\tWellKnownTags.Constant => CiItemKind.Constant,\n\t\t\tWellKnownTags.EnumMember => CiItemKind.EnumMember,\n\t\t\tWellKnownTags.Keyword => CiItemKind.Keyword,\n\t\t\tWellKnownTags.Namespace => CiItemKind.Namespace,\n\t\t\tWellKnownTags.Label => CiItemKind.Label,\n\t\t\tWellKnownTags.TypeParameter => CiItemKind.TypeParameter,\n\t\t\t//WellKnownTags.Snippet => CiItemKind.Snippet,\n\t\t\t_ => CiItemKind.None\n\t\t};\n\t\tif (tags.Length > 1) {\n\t\t\taccess = tags[1] switch {\n\t\t\t\tWellKnownTags.Private => CiItemAccess.Private,\n\t\t\t\tWellKnownTags.Protected => CiItemAccess.Protected,\n\t\t\t\tWellKnownTags.Internal => CiItemAccess.Internal,\n\t\t\t\t_ => default\n\t\t\t};\n\t\t}\n\t}\n\t\n\t//The order must match CiItemKind.\n\tpublic static string[] ItemKindNames { get; } = new[] {\n\t\t\"Class\",\n\t\t\"Structure\",\n\t\t\"Interface\",\n\t\t\"Enum\",\n\t\t\"Delegate\",\n\t\t\"Method\",\n\t\t\"ExtensionMethod\",\n\t\t\"Property\",\n\t\t\"Operator\",\n\t\t\"Event\",\n\t\t\"Field\",\n\t\t\"LocalVariable\",\n\t\t\"Constant\",\n\t\t\"EnumMember\",\n\t\t\"Namespace\",\n\t\t\"Keyword\",\n\t\t\"Label\",\n\t\t\"Snippet\",\n\t\t\"TypeParameter\"\n\t};\n\t\n\t/// <summary>\n\t/// Calls <b>string.Compare</b>. Moves items starting with an ASCII non-symbol char to the bottom.\n\t/// </summary>\n\tpublic class CompletionListSortComparer : IComparer<string> {\n\t\tpublic int Compare(string x, string y) {\n\t\t\tint r = _IsAsciiNonSymChar(x) - _IsAsciiNonSymChar(y);\n\t\t\tif (r == 0) r = string.Compare(x, y, StringComparison.OrdinalIgnoreCase);\n\t\t\treturn r;\n\t\t\t\n\t\t\tstatic int _IsAsciiNonSymChar(string s) => s.Length > 0 && s[0] is char c && (char.IsAsciiLetterOrDigit(c) || c == '_' || c > 127) ? 0 : 1;\n\t\t}\n\t}\n\tpublic static readonly CompletionListSortComparer SortComparer = new();\n\t\n\t#endregion\n\t\n\t#region create a syntax tree, document, etc\n\t\n\t/// <summary>\n\t/// From C# code creates a Roslyn workspace+project+document for code analysis.\n\t/// If <i>needSemantic</i>, adds default references and a document with default global usings (same as in default global.cs).\n\t/// </summary>\n\t/// <param name=\"ws\"><c>using var ws = new AdhocWorkspace(); //need to dispose</c></param>\n\t/// <param name=\"code\">Any C# code fragment, valid or not.</param>\n\t/// <param name=\"needSemantic\">Add default references (.NET and Au.dll) and global usings.</param>\n\tpublic static Document CreateDocumentFromCode(AdhocWorkspace ws, string code, bool needSemantic) {\n\t\tProjectId projectId = ProjectId.CreateNewId();\n\t\tDocumentId documentId = DocumentId.CreateNewId(projectId);\n\t\tvar pi = ProjectInfo.Create(projectId, VersionStamp.Default, \"l\", \"l\", LanguageNames.CSharp, null, null,\n\t\t\tnew CSharpCompilationOptions(OutputKind.WindowsApplication, allowUnsafe: true),\n\t\t\tnew CSharpParseOptions(LanguageVersion.Preview),\n\t\t\tmetadataReferences: needSemantic ? new MetaReferences().Refs : null //tested: does not make slower etc\n\t\t\t);\n\t\tvar sol = ws.CurrentSolution.AddProject(pi);\n\t\tif (needSemantic) {\n\t\t\tsol = sol.AddDocument(DocumentId.CreateNewId(projectId), \"g.cs\", c_globalUsingsText);\n\t\t}\n\t\treturn sol.AddDocument(documentId, \"l.cs\", code).GetDocument(documentId);\n\t\t\n\t\t//It seems it's important to dispose workspaces.\n\t\t//\tIn the docs project at first didn't dispose. After maybe 300_000 times: much slower, process memory 3 GB, sometimes hangs.\n\t}\n\t\n\tpublic const string c_globalUsingsText = \"\"\"\nglobal using Au;\nglobal using Au.Types;\nglobal using System;\nglobal using System.Collections.Generic;\nglobal using System.Linq;\nglobal using System.Collections.Concurrent;\nglobal using System.Diagnostics;\nglobal using System.Globalization;\nglobal using System.IO;\nglobal using System.IO.Compression;\nglobal using System.Runtime.CompilerServices;\nglobal using System.Runtime.InteropServices;\nglobal using System.Text;\nglobal using System.Text.RegularExpressions;\nglobal using System.Threading;\nglobal using System.Threading.Tasks;\nglobal using Microsoft.Win32;\nglobal using Au.More;\nglobal using Au.Triggers;\nglobal using System.Windows;\nglobal using System.Windows.Controls;\nglobal using System.Windows.Media;\n\"\"\";\n\t\n\t/// <summary>\n\t/// Calls <b>CSharpSyntaxTree.ParseText</b> and returns <b>CompilationUnitSyntax</b>.\n\t/// </summary>\n\tpublic static CompilationUnitSyntax CreateSyntaxTree(string code) {\n\t\treturn CSharpSyntaxTree.ParseText(code, new CSharpParseOptions(LanguageVersion.Preview)).GetCompilationUnitRoot();\n\t}\n\t\n\t#endregion\n\t\n\t#region DEBUG\n\t\n#if DEBUG\n\tpublic static void PrintNode(SyntaxNode x, int pos = 0, bool printNode = true, bool printErrors = false, bool indent = false) {\n\t\tif (x == null) { print.it(\"null\"); return; }\n\t\tif (printNode) {\n\t\t\tstring si = null;\n\t\t\tif (indent) {\n\t\t\t\tint i = x.Ancestors().Count();\n\t\t\t\tif (--i > 0) si = new('\\t', i);\n\t\t\t}\n\t\t\tprint.it($\"<>{si}<c blue>{pos}, {x.Span}, {x.FullSpan}, k={x.Kind()}, t={x.GetType().Name},<> '<c green><\\a>{(x is CompilationUnitSyntax ? null : x.ToString().Limit(10, middle: true, lines: true))}</\\a><>'\");\n\t\t}\n\t\tif (printErrors) foreach (var d in x.GetDiagnostics()) print.it(d.Code, d.Location.SourceSpan, d);\n\t}\n\t\n\tpublic static void PrintNode(SyntaxToken x, int pos = 0, bool printNode = true, bool printErrors = false) {\n\t\tif (printNode) print.it($\"<><c blue>{pos}, {x.Span}, {x.Kind()},<> '<c green><\\a>{x.ToString().Limit(10, middle: true, lines: true)}</\\a><>'\");\n\t\tif (printErrors) foreach (var d in x.GetDiagnostics()) print.it(d.Code, d.Location.SourceSpan, d);\n\t}\n\t\n\tpublic static void PrintNode(SyntaxTrivia x, int pos = 0, bool printNode = true, bool printErrors = false) {\n\t\tif (printNode) print.it($\"<><c blue>{pos}, {x.Span}, {x.Kind()},<> '<c green><\\a>{x.ToString().Limit(10, middle: true, lines: true)}</\\a><>'\");\n\t\tif (printErrors) foreach (var d in x.GetDiagnostics()) print.it(d.Code, d.Location.SourceSpan, d);\n\t}\n\t\n\tpublic static void DebugHiliteRange(int start, int end, int indic = SciTheme.Indic.TestBox) {\n\t\tvar doc = Panels.Editor.ActiveDoc;\n\t\tdoc.aaaIndicatorClear(indic);\n\t\tif (end > start) doc.aaaIndicatorAdd(indic, true, start..end);\n\t}\n\t\n\tpublic static void DebugHiliteRange(TextSpan span, int indic = SciTheme.Indic.TestBox) => DebugHiliteRange(span.Start, span.End, indic);\n\t\n\tpublic static void DebugHiliteRanges(List<Range> a, int indic = SciTheme.Indic.TestBox) {\n\t\tvar doc = Panels.Editor.ActiveDoc;\n\t\tdoc.aaaIndicatorClear(indic);\n\t\tint i = 0;\n\t\tforeach (var v in a) doc.aaaIndicatorAdd(indic, true, v, ++i);\n\t}\n\t\n\tstatic IEnumerable<string> DebugGetSymbolInterfaces(ISymbol sym) {\n\t\treturn sym.GetType().FindInterfaces((t, _) => t.Name.Ends(\"Symbol\") && t.Name != \"ISymbol\", null).Select(o => o.Name);\n\t}\n\t\n\t//unfinished. Just prints what we can get from CSharpSyntaxContext.\n\tpublic static /*CiContextType*/void DebugGetContextType(/*in CodeInfo.Context cd,*/ CSharpSyntaxContext c) {\n\t\t//print.it(\"--------\");\n\t\tprint.clear();\n\t\t//print.it(cd.pos);\n\t\t_Print(\"IsInNonUserCode\", c.IsInNonUserCode);\n\t\t_Print(\"IsGlobalStatementContext\", c.IsGlobalStatementContext);\n\t\t_Print(\"IsAnyExpressionContext\", c.IsAnyExpressionContext);\n\t\t//_Print(\"IsAtStartOfPattern\", c.IsAtStartOfPattern);\n\t\t//_Print(\"IsAtEndOfPattern\", c.IsAtEndOfPattern);\n\t\t_Print(\"IsAttributeNameContext\", c.IsAttributeNameContext);\n\t\t//_Print(\"IsCatchFilterContext\", c.IsCatchFilterContext);\n\t\t_Print(\"IsConstantExpressionContext\", c.IsConstantExpressionContext);\n\t\t_Print(\"IsCrefContext\", c.IsCrefContext);\n\t\t//_Print(\"IsDeclarationExpressionContext\", c.IsDeclarationExpressionContext); //removed from Roslyn\n\t\t//_Print(\"IsDefiniteCastTypeContext\", c.IsDefiniteCastTypeContext);\n\t\t//_Print(\"IsEnumBaseListContext\", c.IsEnumBaseListContext);\n\t\t//_Print(\"IsFixedVariableDeclarationContext\", c.IsFixedVariableDeclarationContext);\n\t\t//_Print(\"IsFunctionPointerTypeArgumentContext\", c.IsFunctionPointerTypeArgumentContext);\n\t\t//_Print(\"IsGenericTypeArgumentContext\", c.IsGenericTypeArgumentContext);\n\t\t//_Print(\"IsImplicitOrExplicitOperatorTypeContext\", c.IsImplicitOrExplicitOperatorTypeContext);\n\t\t_Print(\"IsInImportsDirective\", c.IsInImportsDirective);\n\t\t//_Print(\"IsInQuery\", c.IsInQuery);\n\t\t_Print(\"IsInstanceContext\", c.IsInstanceContext);\n\t\t//_Print(\"IsIsOrAsOrSwitchOrWithExpressionContext\", c.IsIsOrAsOrSwitchOrWithExpressionContext);\n\t\t//_Print(\"IsIsOrAsTypeContext\", c.IsIsOrAsTypeContext);\n\t\t_Print(\"IsLabelContext\", c.IsLabelContext);\n\t\t_Print(\"IsLocalVariableDeclarationContext\", c.IsLocalVariableDeclarationContext);\n\t\t//_Print(\"IsMemberAttributeContext\", c.IsMemberAttributeContext(new HashSet<SyntaxKind>(), default));\n\t\t_Print(\"IsMemberDeclarationContext\", c.IsMemberDeclarationContext());\n\t\t_Print(\"IsNameOfContext\", c.IsNameOfContext);\n\t\t_Print(\"IsNamespaceContext\", c.IsNamespaceContext);\n\t\t_Print(\"IsNamespaceDeclarationNameContext\", c.IsNamespaceDeclarationNameContext);\n\t\t_Print(\"IsNonAttributeExpressionContext\", c.IsNonAttributeExpressionContext);\n\t\t_Print(\"IsObjectCreationTypeContext\", c.IsObjectCreationTypeContext);\n\t\t_Print(\"IsOnArgumentListBracketOrComma\", c.IsOnArgumentListBracketOrComma);\n\t\t_Print(\"IsParameterTypeContext\", c.IsParameterTypeContext);\n\t\t_Print(\"IsPossibleLambdaOrAnonymousMethodParameterTypeContext\", c.IsPossibleLambdaOrAnonymousMethodParameterTypeContext);\n\t\t_Print(\"IsPossibleTupleContext\", c.IsPossibleTupleContext);\n\t\t_Print(\"IsPreProcessorDirectiveContext\", c.IsPreProcessorDirectiveContext);\n\t\t_Print(\"IsPreProcessorExpressionContext\", c.IsPreProcessorExpressionContext);\n\t\t_Print(\"IsPreProcessorKeywordContext\", c.IsPreProcessorKeywordContext);\n\t\t_Print(\"IsPrimaryFunctionExpressionContext\", c.IsPrimaryFunctionExpressionContext);\n\t\t_Print(\"IsRightOfNameSeparator\", c.IsRightOfNameSeparator);\n\t\t_Print(\"IsRightSideOfNumericType\", c.IsRightSideOfNumericType);\n\t\t_Print(\"IsStatementAttributeContext\", c.IsStatementAttributeContext());\n\t\t_Print(\"IsStatementContext\", c.IsStatementContext);\n\t\t_Print(\"IsTypeArgumentOfConstraintContext\", c.IsTypeArgumentOfConstraintContext);\n\t\t_Print(\"IsTypeAttributeContext\", c.IsTypeAttributeContext(default));\n\t\t_Print(\"IsTypeContext\", c.IsTypeContext);\n\t\t_Print(\"IsTypeDeclarationContext\", c.IsTypeDeclarationContext());\n\t\t_Print(\"IsTypeOfExpressionContext\", c.IsTypeOfExpressionContext);\n\t\t_Print(\"IsWithinAsyncMethod\", c.IsWithinAsyncMethod);\n\t\t//_Print(\"\", c.);\n\t\t//_Print(\"\", c.);\n\t\t//_Print(\"\", c.);\n\t\t//_Print(\"\", c.);\n\t\t\n\t\tstatic void _Print(string s, bool value) {\n\t\t\tif (value) print.it($\"<><c red>{s}<>\");\n\t\t\telse print.it(s);\n\t\t}\n\t\t\n\t\t//return CiContextType.Namespace;\n\t}\n\t\n\t//unfinished. Also does not support namespaces.\n\t//public static CiContextType DebugGetContextType(CompilationUnitSyntax t, int pos) {\n\t//\tvar members = t.Members;\n\t//\tvar ms = members.FullSpan;\n\t//\t//foreach(var v in members) print.it(v.GetType().Name, v); return 0;\n\t//\t//print.it(pos, ms);\n\t//\t//CiUtil.HiliteRange(ms);\n\t//\tif (ms == default) { //assume empty top-level statements\n\t//\t\tvar v = t.AttributeLists.FullSpan;\n\t//\t\tif (v == default) {\n\t//\t\t\tv = t.Usings.FullSpan;\n\t//\t\t\tif (v == default) v = t.Externs.FullSpan;\n\t//\t\t}\n\t//\t\tif (pos >= v.End) return CiContextType.Method;\n\t//\t} else if (pos < ms.Start) {\n\t//\t} else if (pos >= members.Span.End) {\n\t//\t\tif (members.Last() is GlobalStatementSyntax) return CiContextType.Method;\n\t//\t} else {\n\t//\t\tint i = members.IndexOf(o => o is not GlobalStatementSyntax);\n\t//\t\tif (i < 0 || pos <= members[i].SpanStart) return CiContextType.Method;\n\t\n\t//\t\t//now the difficult part\n\t//\t\tms = members[i].Span;\n\t//\t\tprint.it(pos, ms);\n\t//\t\tCiUtil.HiliteRange(ms);\n\t//\t\t//unfinished. Here should use CSharpSyntaxContext.\n\t//\t}\n\t//\treturn CiContextType.Namespace;\n\t//}\n\t\n\t//enum CiContextType\n\t//{\n\t//\t/// <summary>\n\t//\t/// Outside class/method/topLevelStatements. Eg before using directives or at end of file.\n\t//\t/// Completion list must not include types.\n\t//\t/// </summary>\n\t//\tNamespace,\n\t\n\t//\t/// <summary>\n\t//\t/// Inside class but outside method.\n\t//\t/// Completion list can include types but not functions and values.\n\t//\t/// </summary>\n\t//\tClass,\n\t\n\t//\t/// <summary>\n\t//\t/// Inside method/topLevelStatements.\n\t//\t/// Completion list can include all symbols.\n\t//\t/// </summary>\n\t//\tMethod\n\t//}\n\t\n#endif\n\t\n\t#endregion\n}\n"
  },
  {
    "path": "Au.Editor/Edit/CiUtilExt.cs",
    "content": "extern alias CAW;\n\nusing System.Collections.Immutable;\n\nusing Microsoft.CodeAnalysis;\nusing CAW::Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp;\nusing Microsoft.CodeAnalysis.CSharp.Syntax;\nusing Microsoft.CodeAnalysis.Text;\nusing Microsoft.CodeAnalysis.CSharp.Extensions;\nusing Microsoft.CodeAnalysis.Shared.Extensions;\nusing CAW::Microsoft.CodeAnalysis.Shared.Extensions;\nusing Microsoft.CodeAnalysis.Completion;\n//using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;\n//using Microsoft.CodeAnalysis.Tags;\n//using Microsoft.CodeAnalysis.DocumentationComments;\n//using Microsoft.CodeAnalysis.FindSymbols;\n//using Roslyn.Utilities;\n\nnamespace LA;\n\n/// <summary>\n/// Code info util extension methods.\n/// </summary>\nstatic class CiUtilExt {\n\t/// <summary>\n\t/// Detects whether <i>position</i> is inside a string (literal or interpolated, except when in an interpolation code).\n\t/// </summary>\n\t/// <returns>\n\t/// - false - not in a string; or is in a hole of an interpolated string.\n\t/// - true - in a string; or in a text part of an interpolated string.\n\t/// - null - inside of a prefix or suffix of a string or interpolation, eg @\", $\", @$\", \"\"\", $$\"\"\", {{, }}.\n\t/// </returns>\n\t/// <param name=\"t\">This token. It's ref; if <b>EndOfFileToken</b>, the function sets it = previous token.</param>\n\t/// <param name=\"position\">Where this token has been found using code like <c>var token = root.FindToken(position);</c>.</param>\n\t/// <param name=\"code\">All code. The function uses it to compare substrings easier and faster.</param>\n\t/// <param name=\"x\">If the function returns true, receives span etc.</param>\n\t/// <param name=\"orU8\">Support \"text\"u8 etc.</param>\n\tpublic static bool? IsInString(this ref SyntaxToken t, int position, string code, out CiStringInfo x, bool orU8 = false) {\n\t\tx = default;\n\t\tvar k = t.Kind();\n\t\tif (k == SyntaxKind.EndOfFileToken) {\n\t\t\tt = t.GetPreviousToken(includeSkipped: true, includeDirectives: true);\n\t\t\tk = t.Kind();\n\t\t}\n\t\t//CiUtil.PrintNode(t);\n\t\t\n\t\tvar span = t.Span;\n\t\tint start = span.Start, end = span.End;\n\t\tif (position < start || position > end) return false;\n\t\tbool isInterpolated = false, isVerbatim = false, isRaw = false, isRawPrefixCenter = false, isU8 = false;\n\t\tvar node = t.Parent;\n\t\t\n\t\tif (node.IsKind(SyntaxKind.StringLiteralExpression) || (orU8 && (isU8 = node.IsKind(SyntaxKind.Utf8StringLiteralExpression)))) {\n\t\t\tif (position == start) return false;\n\t\t\tif (k is not (SyntaxKind.StringLiteralToken or SyntaxKind.Utf8StringLiteralToken)) isRaw = true;\n\t\t\tif (!isRaw) {\n\t\t\t\tif (isVerbatim = code[start++] == '@') if (position == start++) return null; //inside @\"\n\t\t\t\tif (!isU8) {\n\t\t\t\t\tif (position < end) { end--; goto gTrue; }\n\t\t\t\t\tif (code[end - 1] != '\"' || end == start || node.NoClosingQuote()) goto gTrue; //no closing \"\n\t\t\t\t} else if (position < end) {\n\t\t\t\t\tend -= 3;\n\t\t\t\t\tif (position > end) return null;\n\t\t\t\t\tgoto gTrue;\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t} else {\n\t\t\t\twhile (start < end && code[start] == '\"') start++; //skip \"\"\"\n\t\t\t\tint nq = start - span.Start;\n\t\t\t\tif (position < start) { //inside \"\"\"\n\t\t\t\t\tif (!isU8 && _IsRawPrefixCenter(span.Start, nq)) goto gTrue;\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\tbool ml = k is SyntaxKind.MultiLineRawStringLiteralToken or SyntaxKind.Utf8MultiLineRawStringLiteralToken;\n\t\t\t\tif (ml) { //skip newline\n\t\t\t\t\twhile (start < end && code[start] is ' ' or '\\t') start++;\n\t\t\t\t\tif (code[start] == '\\r') start++;\n\t\t\t\t\tif (code[start++] != '\\n') return null;\n\t\t\t\t}\n\t\t\t\tx.isRawMultiline = ml;\n\t\t\t\tif (position < start) {\n\t\t\t\t\tx.isRawMultilineBetweenStartQuotesAndText = true;\n\t\t\t\t\treturn null; //before newline\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (position == end) {\n\t\t\t\t\tif (!isU8 && node.NoClosingQuote()) goto gTrue;\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (isU8) end -= 2;\n\t\t\t\twhile (nq > 0 && end > start && code[--end] == '\"') nq--;\n\t\t\t\tif (nq > 0) goto gTrue; //unterminated\n\t\t\t\tif (position > end) return null; //inside \"\"\", or \"\"\" not in its own line (error)\n\t\t\t\tif (ml) {\n\t\t\t\t\twhile (end > start && code[end - 1] is ' ' or '\\t') end--;\n\t\t\t\t\tif (code[--end] != '\\n') return null;\n\t\t\t\t\tif (code[end - 1] == '\\r') end--;\n\t\t\t\t\tif (position > end) return null;\n\t\t\t\t\t//never mind indent\n\t\t\t\t}\n\t\t\t\tgoto gTrue;\n\t\t\t}\n\t\t}\n\t\tisInterpolated = true;\n\t\t\n\t\tbool _IsRawPrefixCenter(int iq, int nq) {\n\t\t\t//most likely the user will write a raw string at \"\"\"|\"\"\" and not at \"\"\"\"\"\"|\n\t\t\tif (nq < 6 || 0 != (nq & 1)) return false;\n\t\t\tiq += nq / 2; if (iq != position) return false;\n\t\t\t//if (isRawPrefixCenter = node.NoClosingQuote()) start = end = iq; //not always works\n\t\t\t//return isRawPrefixCenter;\n\t\t\tstart = end = iq;\n\t\t\treturn isRawPrefixCenter = true;\n\t\t}\n\t\t\n\t\tif (k is SyntaxKind.InterpolatedSingleLineRawStringStartToken or SyntaxKind.InterpolatedMultiLineRawStringStartToken) {\n\t\t\tint iq = start, nq = 0; while (code[iq] == '$') iq++;\n\t\t\twhile (code.Eq(iq + nq, '\"')) nq++;\n\t\t\tif (_IsRawPrefixCenter(iq, nq)) goto gTrue;\n\t\t}\n\t\t\n\t\tswitch (k) {\n\t\tcase SyntaxKind.InterpolatedStringTextToken:\n\t\t\tgoto gTrue;\n\t\tcase SyntaxKind.InterpolatedStringEndToken:\n\t\tcase SyntaxKind.InterpolatedRawStringEndToken when position == start:\n\t\t\t_BackToTextToken(ref t);\n\t\t\tgoto gTrue;\n\t\tcase SyntaxKind.InterpolatedRawStringEndToken:\n\t\t\treturn position == end ? false : null;\n\t\tcase SyntaxKind.InterpolatedStringStartToken or SyntaxKind.InterpolatedVerbatimStringStartToken:\n\t\tcase SyntaxKind.InterpolatedSingleLineRawStringStartToken or SyntaxKind.InterpolatedMultiLineRawStringStartToken:\n\t\t\treturn position > start ? null : false;\n\t\tcase SyntaxKind.OpenBraceToken when t.Parent is InterpolationSyntax:\n\t\t\tif (position == start) {\n\t\t\t\tif (!_BackToTextToken(ref t)) return null;\n\t\t\t\tgoto gTrue;\n\t\t\t}\n\t\t\tif (code.Eq(position - 1, \"{{\")) return null;\n\t\t\tbreak;\n\t\tcase SyntaxKind.CloseBraceToken when t.Parent is InterpolationSyntax:\n\t\t\tif (code.Eq(position - 1, \"}}\")) return null;\n\t\t\tbreak;\n\t\t}\n\t\t\n\t\tbool _BackToTextToken(ref SyntaxToken t) {\n\t\t\tvar tt = t.GetPreviousToken();\n\t\t\tvar kk = tt.Kind();\n\t\t\tif (kk == SyntaxKind.OpenBraceToken) return false;\n\t\t\tif (kk == SyntaxKind.InterpolatedStringTextToken) (start, end) = tt.Span;\n\t\t\telse end = start;\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\treturn false;\n\t\tgTrue:\n\t\tx.textSpan = TextSpan.FromBounds(start, end);\n\t\tx.stringNode = t.Parent;\n\t\tif (isInterpolated && x.stringNode is not InterpolatedStringExpressionSyntax) x.stringNode = x.stringNode.Parent;\n\t\tDebug.Assert(x.stringNode is LiteralExpressionSyntax or InterpolatedStringExpressionSyntax);\n\t\tif (x.isInterpolated = isInterpolated) {\n\t\t\tvar k1 = x.stringNode.GetFirstToken().Kind();\n\t\t\tx.isRawMultiline = k1 == SyntaxKind.InterpolatedMultiLineRawStringStartToken;\n\t\t\tif (k1 is SyntaxKind.InterpolatedVerbatimStringStartToken) isVerbatim = true;\n\t\t\telse if (k1 is SyntaxKind.InterpolatedSingleLineRawStringStartToken or SyntaxKind.InterpolatedMultiLineRawStringStartToken) isRaw = true;\n\t\t}\n\t\tx.isVerbatim = isVerbatim;\n\t\tx.isRaw = isRaw;\n\t\tx.isClassic = !(isVerbatim | isRaw);\n\t\tx.isRawPrefixCenter = isRawPrefixCenter;\n\t\tx.isU8 = isU8;\n\t\treturn true;\n\t}\n\t\n\t//It seems these have no sense. Never noticed syntax warnings.\n\t///// <summary>\n\t///// Gets <b>IEnumerable</b> of <b>Diagnostic</b> where <b>Severity</b> is <b>Error</b>.\n\t///// </summary>\n\t///// <returns>Not null.</returns>\n\t//public static IEnumerable<Diagnostic> GetErrors(this SyntaxNode t) {\n\t//\tif (!t.ContainsDiagnostics) return Array.Empty<Diagnostic>(); //faster\n\t//\treturn t.GetDiagnostics().Where(static o => o.Severity is DiagnosticSeverity.Error);\n\t//}\n\t\n\t///// <summary>\n\t///// Gets <b>IEnumerable</b> of <b>Diagnostic</b> where <b>Severity</b> is <b>Error</b>.\n\t///// </summary>\n\t///// <returns>Not null.</returns>\n\t//public static IEnumerable<Diagnostic> GetErrors(this SyntaxToken t) {\n\t//\tif (!t.ContainsDiagnostics) return Array.Empty<Diagnostic>();\n\t//\treturn t.GetDiagnostics().Where(static o => o.Severity is DiagnosticSeverity.Error);\n\t//}\n\t\n\t/// <summary>\n\t/// Returns true if this token is a string, and the closing quote(s) is missing (contains diagnostics error).\n\t/// </summary>\n\tpublic static bool NoClosingQuote(this SyntaxNode t) //fast\n\t\t=> t.ContainsDiagnostics && t.GetDiagnostics().Any(o => o.Code is 1010 or 1039 or 8997);\n\t//Newline in constant, Unterminated string literal, Unterminated raw string literal.\n\t//note: SyntaxToken may contain the same diagnostics too, but not always, eg no if $$\"\"\"\"\"\". Same speed.\n\t\n\tpublic static void Deconstruct(this TextSpan t, out int Start, out int End) { Start = t.Start; End = t.End; }\n\t\n\tpublic static Range ToRange(this TextSpan t) => t.Start..t.End;\n\t\n\t/// <summary>\n\t/// <c>position &gt; t.Start &amp;&amp; position &lt; t.End;</c>\n\t/// </summary>\n\tpublic static bool ContainsInside(this TextSpan t, int position) => position > t.Start && position < t.End;\n\t\n\t/// <summary>\n\t/// <c>position &gt;= t.Start &amp;&amp; position &lt;= t.End;</c>\n\t/// </summary>\n\tpublic static bool ContainsOrTouches(this TextSpan t, int position) => position >= t.Start && position <= t.End;\n\t\n\t/// <summary>\n\t/// Finds child trivia of this token. Returns default if <i>position</i> is not in child trivia of this token. Does not descend into structured trivia.\n\t/// The code is from Roslyn source function FindTriviaByOffset. Roslyn has function to find trivia in SyntaxNode (recursive), but not in SyntaxToken.\n\t/// </summary>\n\t/// <param name=\"t\"></param>\n\t/// <param name=\"position\">Position in whole code.</param>\n\tpublic static SyntaxTrivia FindTrivia(in this SyntaxToken t, int position) {\n\t\tint textOffset = position - t.Position;\n\t\tif (textOffset >= 0) {\n\t\t\tvar leading = t.LeadingWidth;\n\t\t\tif (textOffset < leading) {\n\t\t\t\tforeach (var trivia in t.LeadingTrivia) {\n\t\t\t\t\tif (textOffset < trivia.FullWidth) return trivia;\n\t\t\t\t\ttextOffset -= trivia.FullWidth;\n\t\t\t\t}\n\t\t\t} else if (textOffset >= leading + t.Width) {\n\t\t\t\ttextOffset -= leading + t.Width;\n\t\t\t\tforeach (var trivia in t.TrailingTrivia) {\n\t\t\t\t\tif (textOffset < trivia.FullWidth) return trivia;\n\t\t\t\t\ttextOffset -= trivia.FullWidth;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn default;\n\t}\n\t\n\t/// <summary>\n\t/// Finds token at <i>position</i>. Returns true if its span contains or touches <i>position</i>.\n\t/// May return previous token in some cases, eg if the found token is EndOfFileToken.\n\t/// </summary>\n\tpublic static bool FindTouchingToken(this SyntaxNode t, out SyntaxToken token, int position, bool findInsideTrivia = false) {\n\t\ttoken = t.FindToken(position, findInsideTrivia);\n\t\tif (!token.IsKind(SyntaxKind.EndOfFileToken) && token.Span.ContainsOrTouches(position)) return true;\n\t\ttoken = token.GetPreviousToken();\n\t\tif (token.Span.End == position) return true;\n\t\ttoken = default;\n\t\treturn false;\n\t}\n\t\n\t/// <summary>\n\t/// Gets full span, not including leading trivia that is not comments/doccomments touching the declaration.\n\t/// </summary>\n\t/// <param name=\"minimalLeadingTrivia\">Get leading trivia just until the first newline when searching backwards. Usually it is indent whitespace or nothing.</param>\n\t/// <param name=\"spanEnd\">Get <c>Span.End</c> instead of <c>FullSpan.End</c>.</param>\n\tpublic static TextSpan GetRealFullSpan(this SyntaxNode t, bool minimalLeadingTrivia = false, bool spanEnd = false) {\n\t\tint from = t.SpanStart;\n\t\tvar a = t.GetLeadingTrivia();\n\t\tfor (int i = a.Count; --i >= 0;) {\n\t\t\tvar v = a[i];\n\t\t\tvar k = v.Kind();\n\t\t\tif (k == SyntaxKind.EndOfLineTrivia) {\n\t\t\t\tif (i == 0 || minimalLeadingTrivia) break;\n\t\t\t\tk = a[i - 1].Kind();\n\t\t\t\tif (k == SyntaxKind.EndOfLineTrivia) break;\n\t\t\t\tif (k == SyntaxKind.WhitespaceTrivia) if (i == 1 || a[i - 2].IsKind(SyntaxKind.EndOfLineTrivia)) break;\n\t\t\t} else if (k is SyntaxKind.SingleLineDocumentationCommentTrivia or SyntaxKind.MultiLineDocumentationCommentTrivia) {\n\t\t\t\tfrom = v.FullSpan.Start; //SpanStart does not include /// or /**\n\t\t\t\tcontinue;\n\t\t\t} else {\n\t\t\t\tif (k is not (SyntaxKind.WhitespaceTrivia or SyntaxKind.SingleLineCommentTrivia or SyntaxKind.MultiLineCommentTrivia)) break;\n\t\t\t}\n\t\t\tfrom = v.SpanStart;\n\t\t}\n\t\treturn TextSpan.FromBounds(from, spanEnd ? t.Span.End : t.FullSpan.End);\n\t}\n\t\n\t/// <summary>\n\t/// Gets the first ancestor-or-this that is a statement or member/accessor declaration or using directive etc.\n\t/// </summary>\n\t/// <param name=\"t\"></param>\n\t/// <param name=\"pos\">See remarks.</param>\n\t/// <param name=\"notAccessor\">Don't need <b>AccessorDeclarationSyntax</b> (get the property etc declaration).</param>\n\t/// <returns>null if the initial node is <b>CompilationUnitSyntax</b>, eg <i>pos</i> is at the end of file.</returns>\n\t/// <remarks>\n\t/// If the statement is `{ }` owned by another statement (eg `if`, but not `{ }`) or member/accessor declaration, gets the owner if <i>pos</i> is not inside `{ }`.\n\t/// If that owner is `{ }` in an expression (eg lambda), returns the ancestor statement.\n\t/// </remarks>\n\tpublic static SyntaxNode GetStatementEtc(this SyntaxNode t, int pos, bool notAccessor = false) {\n\t\tg1:\n\t\tvar n = t.FirstAncestorOrSelf<SyntaxNode>(notAccessor ? static o => o is StatementSyntax or MemberDeclarationSyntax : static o => o is StatementSyntax or MemberDeclarationSyntax or AccessorDeclarationSyntax);\n\t\tif (n == null) {\n\t\t\tn = t.FirstAncestorOrSelf<SyntaxNode>(static o => o.Parent is CompilationUnitSyntax); //using directive etc\n\t\t} else if (n is BlockSyntax) {\n\t\t\tvar p = n.Parent;\n\t\t\tif (!(p is BlockSyntax or GlobalStatementSyntax) && !n.Span.ContainsInside(pos)) {\n\t\t\t\tif (!(p is StatementSyntax or MemberDeclarationSyntax)) { t = p; goto g1; }\n\t\t\t\tn = p;\n\t\t\t}\n\t\t}\n\t\treturn n;\n\t}\n\t\n\tpublic static IEnumerable<SyntaxNode> PreviousSiblings(this SyntaxNode t, bool andThis = false) {\n\t\tif (t.Parent is { } pa) {\n\t\t\tif (pa is GlobalStatementSyntax) pa = (t = pa).Parent;\n\t\t\tbool found = false;\n\t\t\tforeach (var v in pa.ChildNodesAndTokens().Reverse()) {\n\t\t\t\tif (v.AsNode(out var n)) {\n\t\t\t\t\tif (!found) {\n\t\t\t\t\t\tif (n != t) continue;\n\t\t\t\t\t\tfound = true;\n\t\t\t\t\t\tif (!andThis) continue;\n\t\t\t\t\t}\n\t\t\t\t\tyield return n is GlobalStatementSyntax g ? g.Statement : n;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t\n\tpublic static bool Eq(this string t, TextSpan span, string s, bool ignoreCase = false)\n\t\t=> t.Eq(span.Start..span.End, s, ignoreCase);\n\t\n\t/// <summary>\n\t/// SyntaxFacts.IsNewLine(t[i]);\n\t/// </summary>\n\tpublic static bool IsCsNewlineChar(this string t, int i)\n\t\t=> SyntaxFacts.IsNewLine(t[i]);\n\t\n\t/// <summary>\n\t/// i == 0 || SyntaxFacts.IsNewLine(t[i - 1]);\n\t/// </summary>\n\tpublic static bool IsCsStartOfLine(this string t, int i)\n\t\t=> i == 0 || SyntaxFacts.IsNewLine(t[i - 1]);\n\t\n\t/// <summary>\n\t/// i == t.Length || SyntaxFacts.IsNewLine(t[i]);\n\t/// </summary>\n\tpublic static bool IsCsEndOfLine(this string t, int i)\n\t\t=> i == t.Length || SyntaxFacts.IsNewLine(t[i]);\n\t\n\t[Conditional(\"DEBUG\")]\n\tpublic static void DebugPrint(this CompletionItem t, string color = \"blue\") {\n\t\tprint.it($\"<><c {color}>{t.DisplayText},    {string.Join(\"|\", t.Tags)},    prefix={t.DisplayTextPrefix},    suffix={t.DisplayTextSuffix},    filter={t.FilterText},    sort={t.SortText},    inline={t.InlineDescription},    automation={t.AutomationText},    provider={t.ProviderName}<>\");\n\t\tprint.it(string.Join(\"\\n\", t.Properties));\n\t}\n\t\n\t[Conditional(\"DEBUG\")]\n\tpublic static void DebugPrintIf(this CompletionItem t, bool condition, string color = \"blue\") {\n\t\tif (condition) DebugPrint(t, color);\n\t}\n\t\n\t/// <summary>\n\t/// Gets symbol name as single word good for displaying etc.\n\t/// Known cases when the return value != <b>Name</b>:\n\t/// \".ctor\" -> \"TypeName\".\n\t/// \"Finalize\" -> \"~TypeName\".\n\t/// \"QualifiedInterface.Explicit\" -> \"Explicit\".\n\t/// </summary>\n\tpublic static string JustName(this ISymbol t) {\n\t\tvar s = t.Name;\n\t\tif (SyntaxFacts.IsValidIdentifier(s) && !t.IsDestructor()) return s;\n\t\treturn t.ToDisplayString(SymbolDisplayFormat.ShortFormat);\n\t}\n\t\n\tpublic static string QualifiedName(this ISymbol t, bool onlyNamespace = false, bool noDirectName = false) {\n\t\tvar g = t_qnStack ??= new Stack<string>();\n\t\tg.Clear();\n\t\tif (noDirectName) t = t.ContainingType ?? t.ContainingNamespace as ISymbol;\n\t\tif (!onlyNamespace) for (var k = t; k != null; k = k.ContainingType) g.Push(k.Name);\n\t\tfor (var n = t.ContainingNamespace; n != null && !n.IsGlobalNamespace; n = n.ContainingNamespace) g.Push(n.Name);\n\t\treturn string.Join(\".\", g);\n\t}\n\t[ThreadStatic] static Stack<string> t_qnStack;\n\t\n\t/// <summary>\n\t/// Cached, thread-safe.\n\t/// </summary>\n\tpublic static string QualifiedNameCached(this INamespaceSymbol t) { //only 2 times faster than QualifiedName, but no garbage. Same speed with Dictionary.\n\t\t//if (t.IsGlobalNamespace || t.ContainingNamespace.IsGlobalNamespace) return t.Name; //same speed. Few such namespaces.\n\t\tif (!_namespaceNames.TryGetValue(t, out var s)) {\n\t\t\ts = t.QualifiedName();\n\t\t\t_namespaceNames.AddOrUpdate(t, s); //OrUpdate for thread safety\n\t\t\t//print.it(s);\n\t\t}\n\t\treturn s;\n\t}\n\tstatic ConditionalWeakTable<INamespaceSymbol, string> _namespaceNames = new();\n\t\n\t/// <summary>\n\t/// Like <b>GetEnclosingNamedType</b>, but <i>pos</i> can be anywhere in the class/struct/interface/enum definition span, not just in { }.\n\t/// </summary>\n\tpublic static INamedTypeSymbol GetEnclosingNamedType2(this SemanticModel t, int pos, out SyntaxNode posNode, out BaseTypeDeclarationSyntax declNode) {\n\t\tposNode = t.Root.FindToken(pos).Parent;\n\t\tdeclNode = posNode?.GetAncestorOrThis<BaseTypeDeclarationSyntax>();\n\t\tif (declNode == null || !declNode.Span.Contains(pos) || declNode.CloseBraceToken.IsMissing) return null;\n\t\treturn t.GetEnclosingNamedType(declNode.OpenBraceToken.SpanStart + 1, default);\n\t}\n\t\n\t/// <summary>\n\t/// If the file code contains usings, externs, attributes or #define directives, returns position after all them. Else 0.\n\t/// </summary>\n\tpublic static int GetHeaderLength(this CompilationUnitSyntax t) {\n\t\tvar last = t.AttributeLists.LastOrDefault() as SyntaxNode\n\t\t\t?? t.Usings.LastOrDefault() as SyntaxNode\n\t\t\t?? t.Externs.LastOrDefault() as SyntaxNode\n\t\t\t?? t.GetDirectives(o => o is DefineDirectiveTriviaSyntax).LastOrDefault();\n\t\tif (last == null) return 0;\n\t\treturn last.FullSpan.End;\n\t}\n\t\n\tpublic static (string kind, string access) ImageResource(this ISymbol t) {\n\t\tvar glyph = t.GetGlyph();\n\t\treturn glyph switch {\n\t\t\tGlyph.ClassPublic => (\"resources/ci/class.xaml\", null),\n\t\t\tGlyph.ClassInternal => (\"resources/ci/class.xaml\", \"resources/ci/overlayinternal.xaml\"),\n\t\t\tGlyph.ClassPrivate => (\"resources/ci/class.xaml\", \"resources/ci/overlayprivate.xaml\"),\n\t\t\tGlyph.ClassProtected => (\"resources/ci/class.xaml\", \"resources/ci/overlayprotected.xaml\"),\n\t\t\t\n\t\t\tGlyph.ConstantPublic => (\"resources/ci/constant.xaml\", null),\n\t\t\tGlyph.ConstantInternal => (\"resources/ci/constant.xaml\", \"resources/ci/overlayinternal.xaml\"),\n\t\t\tGlyph.ConstantPrivate => (\"resources/ci/constant.xaml\", \"resources/ci/overlayprivate.xaml\"),\n\t\t\tGlyph.ConstantProtected => (\"resources/ci/constant.xaml\", \"resources/ci/overlayprotected.xaml\"),\n\t\t\t\n\t\t\tGlyph.DelegatePublic => (\"resources/ci/delegate.xaml\", null),\n\t\t\tGlyph.DelegateInternal => (\"resources/ci/delegate.xaml\", \"resources/ci/overlayinternal.xaml\"),\n\t\t\tGlyph.DelegatePrivate => (\"resources/ci/delegate.xaml\", \"resources/ci/overlayprivate.xaml\"),\n\t\t\tGlyph.DelegateProtected => (\"resources/ci/delegate.xaml\", \"resources/ci/overlayprotected.xaml\"),\n\t\t\t\n\t\t\tGlyph.EnumPublic => (\"resources/ci/enum.xaml\", null),\n\t\t\tGlyph.EnumInternal => (\"resources/ci/enum.xaml\", \"resources/ci/overlayinternal.xaml\"),\n\t\t\tGlyph.EnumPrivate => (\"resources/ci/enum.xaml\", \"resources/ci/overlayprivate.xaml\"),\n\t\t\tGlyph.EnumProtected => (\"resources/ci/enum.xaml\", \"resources/ci/overlayprotected.xaml\"),\n\t\t\t\n\t\t\tGlyph.EnumMemberPublic => (\"resources/ci/enummember.xaml\", null),\n\t\t\tGlyph.EnumMemberInternal => (\"resources/ci/enummember.xaml\", \"resources/ci/overlayinternal.xaml\"), //?\n\t\t\tGlyph.EnumMemberPrivate => (\"resources/ci/enummember.xaml\", \"resources/ci/overlayprivate.xaml\"),\n\t\t\tGlyph.EnumMemberProtected => (\"resources/ci/enummember.xaml\", \"resources/ci/overlayprotected.xaml\"),\n\t\t\t\n\t\t\tGlyph.EventPublic => (\"resources/ci/event.xaml\", null),\n\t\t\tGlyph.EventInternal => (\"resources/ci/event.xaml\", \"resources/ci/overlayinternal.xaml\"),\n\t\t\tGlyph.EventPrivate => (\"resources/ci/event.xaml\", \"resources/ci/overlayprivate.xaml\"),\n\t\t\tGlyph.EventProtected => (\"resources/ci/event.xaml\", \"resources/ci/overlayprotected.xaml\"),\n\t\t\t\n\t\t\tGlyph.ExtensionMethodPublic => (\"resources/ci/extensionmethod.xaml\", null),\n\t\t\tGlyph.ExtensionMethodInternal => (\"resources/ci/extensionmethod.xaml\", \"resources/ci/overlayinternal.xaml\"),\n\t\t\tGlyph.ExtensionMethodPrivate => (\"resources/ci/extensionmethod.xaml\", \"resources/ci/overlayprivate.xaml\"),\n\t\t\tGlyph.ExtensionMethodProtected => (\"resources/ci/extensionmethod.xaml\", \"resources/ci/overlayprotected.xaml\"),\n\t\t\t\n\t\t\tGlyph.FieldPublic => (\"resources/ci/field.xaml\", null),\n\t\t\tGlyph.FieldInternal => (\"resources/ci/field.xaml\", \"resources/ci/overlayinternal.xaml\"),\n\t\t\tGlyph.FieldPrivate => (\"resources/ci/field.xaml\", \"resources/ci/overlayprivate.xaml\"),\n\t\t\tGlyph.FieldProtected => (\"resources/ci/field.xaml\", \"resources/ci/overlayprotected.xaml\"),\n\t\t\t\n\t\t\tGlyph.InterfacePublic => (\"resources/ci/interface.xaml\", null),\n\t\t\tGlyph.InterfaceInternal => (\"resources/ci/interface.xaml\", \"resources/ci/overlayinternal.xaml\"),\n\t\t\tGlyph.InterfacePrivate => (\"resources/ci/interface.xaml\", \"resources/ci/overlayprivate.xaml\"),\n\t\t\tGlyph.InterfaceProtected => (\"resources/ci/interface.xaml\", \"resources/ci/overlayprotected.xaml\"),\n\t\t\t\n\t\t\tGlyph.MethodPublic => (\"resources/ci/method.xaml\", null),\n\t\t\tGlyph.MethodInternal => (\"resources/ci/method.xaml\", \"resources/ci/overlayinternal.xaml\"),\n\t\t\tGlyph.MethodPrivate => (\"resources/ci/method.xaml\", \"resources/ci/overlayprivate.xaml\"),\n\t\t\tGlyph.MethodProtected => (\"resources/ci/method.xaml\", \"resources/ci/overlayprotected.xaml\"),\n\t\t\t\n\t\t\tGlyph.OperatorPublic => (\"resources/ci/operator.xaml\", null),\n\t\t\tGlyph.OperatorInternal => (\"resources/ci/operator.xaml\", \"resources/ci/overlayinternal.xaml\"),\n\t\t\tGlyph.OperatorPrivate => (\"resources/ci/operator.xaml\", \"resources/ci/overlayprivate.xaml\"),\n\t\t\tGlyph.OperatorProtected => (\"resources/ci/operator.xaml\", \"resources/ci/overlayprotected.xaml\"),\n\n\t\t\tGlyph.PropertyPublic => (\"resources/ci/property.xaml\", null),\n\t\t\tGlyph.PropertyInternal => (\"resources/ci/property.xaml\", \"resources/ci/overlayinternal.xaml\"),\n\t\t\tGlyph.PropertyPrivate => (\"resources/ci/property.xaml\", \"resources/ci/overlayprivate.xaml\"),\n\t\t\tGlyph.PropertyProtected => (\"resources/ci/property.xaml\", \"resources/ci/overlayprotected.xaml\"),\n\t\t\t\n\t\t\tGlyph.StructurePublic => (\"resources/ci/structure.xaml\", null),\n\t\t\tGlyph.StructureInternal => (\"resources/ci/structure.xaml\", \"resources/ci/overlayinternal.xaml\"),\n\t\t\tGlyph.StructurePrivate => (\"resources/ci/structure.xaml\", \"resources/ci/overlayprivate.xaml\"),\n\t\t\tGlyph.StructureProtected => (\"resources/ci/structure.xaml\", \"resources/ci/overlayprotected.xaml\"),\n\t\t\t\n\t\t\tGlyph.Keyword => (\"resources/ci/keyword.xaml\", null), //implicit 'value' parameter in a property etc\n\t\t\tGlyph.Label => (\"resources/ci/label.xaml\", null),\n\t\t\tGlyph.Local or Glyph.Parameter or Glyph.RangeVariable => (\"resources/ci/localvariable.xaml\", null),\n\t\t\tGlyph.Namespace => (\"resources/ci/namespace.xaml\", null),\n\t\t\tGlyph.TypeParameter => (\"resources/ci/typeparameter.xaml\", null),\n\t\t\t_ => default\n\t\t};\n\t}\n\t\n\t/// <summary>\n\t/// Appends n tabs or n*4 spaces, depending on <b>App.Settings.ci_formatTabIndent</b>.\n\t/// </summary>\n\tpublic static StringBuilder AppendIndent(this StringBuilder t, int n) {\n\t\tif (App.Settings.ci_formatTabIndent) t.Append('\\t', n);\n\t\telse t.Append(' ', n * 4);\n\t\treturn t;\n\t}\n\t\n\t/// <summary>\n\t/// Appends C# code <i>s</i> to <i>b</i>.\n\t/// For each line adds <i>indent</i> tabs, except in multiline @\"string\" or \"\"\"string\"\"\" (same for u8).\n\t/// Ignores the last empty line of <i>s</i>. Appends newline at the end if <b>andNewline</b>.\n\t/// </summary>\n\tpublic static void AppendCodeWithIndent(this StringBuilder t, string s, int indent, bool andNewline) {\n\t\tif (indent > 0) {\n\t\t\tvar cu = CSharpSyntaxTree.ParseText(s, new CSharpParseOptions(LanguageVersion.Preview)).GetCompilationUnitRoot();\n\t\t\tvar a = s.Lines(..); int i = 0;\n\t\t\tforeach (var v in a) {\n\t\t\t\tbool canIndent = true;\n\t\t\t\tif (s[v.start] == '#' && cu.FindTrivia(v.start).IsDirective) canIndent = false;\n\t\t\t\telse {\n\t\t\t\t\tvar tok = cu.FindToken(v.start);\n\t\t\t\t\tcanIndent = tok.IsInString(v.start, s, out _, orU8: true) == false;\n\t\t\t\t}\n\t\t\t\tif (canIndent) t.AppendIndent(indent);\n\t\t\t\tt.Append(s, v.start, v.Length);\n\t\t\t\tif (++i < a.Length || andNewline) t.AppendLine();\n\t\t\t}\n\t\t} else {\n\t\t\tt.Append(s);\n\t\t\tif (!s.Ends('\\n')) t.AppendLine();\n\t\t}\n\t}\n}\n\nrecord struct CiStringInfo {\n\t/// <summary>\n\t/// The text part of string literal or a text part of interpolated string. Without enclosing and prefix.\n\t/// </summary>\n\tpublic TextSpan textSpan;\n\t\n\t/// <summary>\n\t/// Entire string node. Can be either <b>LiteralExpressionSyntax</b> or <b>InterpolatedStringExpressionSyntax</b>.\n\t/// </summary>\n\tpublic SyntaxNode stringNode;\n\t\n\tpublic bool isVerbatim, isRaw, isInterpolated, isU8;\n\t\n\t/// <summary>\n\t/// Not verbatim/raw. Can be \"...\", $\"...\" or \"...\"u8.\n\t/// </summary>\n\tpublic bool isClassic;\n\t\n\t/// <summary>\n\t/// At \"\"\"|\"\"\".\n\t/// </summary>\n\tpublic bool isRawPrefixCenter;\n\t\n\t/// <summary>\n\t/// In multiline raw string. Valid when returns true, but can be <c>true</c> regardless of the return value.\n\t/// </summary>\n\tpublic bool isRawMultiline;\n\t\n\t/// <summary>\n\t/// In multiline raw string after \"\"\" but before the start of next line. Can be <c>true</c> only if returned null.\n\t/// </summary>\n\tpublic bool isRawMultilineBetweenStartQuotesAndText;\n}"
  },
  {
    "path": "Au.Editor/Edit/CiWinapi.cs",
    "content": "extern alias CAW;\n\nusing Au.Controls;\n\nusing Microsoft.CodeAnalysis;\nusing CAW::Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp;\nusing Microsoft.CodeAnalysis.CSharp.Syntax;\nusing Microsoft.CodeAnalysis.Text;\nusing Microsoft.CodeAnalysis.Shared.Extensions;\nusing CAW::Microsoft.CodeAnalysis.Shared.Extensions;\n\nnamespace LA;\n\nclass CiWinapi {\n\tint _typenameStart;\n\tbool _canInsert;\n\tstatic string s_tempFile;\n\t\n\tpublic static bool IsWinapiClassSymbol(INamedTypeSymbol typeSym) => typeSym?.BaseType?.Name == \"NativeApi\";\n\t\n\tpublic static CiWinapi AddWinapi(INamedTypeSymbol typeSym, List<CiComplItem> items, int typenameStart, bool onlyTypes) {\n\t\tDebug.Assert(IsWinapiClassSymbol(typeSym));\n\t\t\n\t\t//At first read from database and write to a temp binary file. Reading from it is ~2 times faster than from database.\n\t\tif (s_tempFile == null || !filesystem.GetTime_(s_tempFile, out var t1) || !filesystem.GetTime_(EdDatabases.WinapiFile, out var t2) || t2 > t1) {\n\t\t\ts_tempFile = folders.ThisAppTemp + \"winapi.bin\";\n\t\t\tusing var w = new BinaryWriter(File.Create(s_tempFile));\n\t\t\tusing var db = EdDatabases.OpenWinapi();\n\t\t\tusing var stat = db.Statement(\"SELECT name, kind FROM api\");\n\t\t\twhile (stat.Step()) {\n\t\t\t\tw.Write((byte)stat.GetInt(1));\n\t\t\t\tw.Write(stat.GetText(0));\n\t\t\t}\n\t\t\t//note: don't use this FileStream for reading. Works but very slow.\n\t\t}\n\t\t\n\t\tvar fs = filesystem.loadStream(s_tempFile);\n\t\tint cap = (int)(fs.Length / 22); if (onlyTypes) cap /= 8;\n\t\titems.Capacity = items.Count + cap;\n\t\tusing var r = new BinaryReader(fs);\n\t\twhile (fs.Position < fs.Length) {\n\t\t\tvar kind = (CiItemKind)r.ReadByte();\n\t\t\tif (onlyTypes && (int)kind > 4) break; //sorted by kind, types first\n\t\t\tvar name = r.ReadString();\n\t\t\tvar ci = new CiComplItem(CiComplProvider.Winapi, name, kind/*, CiItemAccess.Internal*/);\n\t\t\titems.Add(ci);\n\t\t}\n\t\t\n\t\treturn new() { _typenameStart = typenameStart, _canInsert = typeSym.IsFromSource() };\n\t}\n\t\n\tpublic static System.Windows.Documents.Section GetDescription(CiComplItem item) {\n\t\tvar m = new CiText();\n\t\tm.StartParagraph();\n\t\tm.Append(item.kind.ToString() + \" \"); m.Bold(item.Text); m.Append(\".\");\n\t\tm.EndParagraph();\n\t\tif (_GetText(item, out string s)) m.CodeBlock(s);\n\t\treturn m.Result;\n\t}\n\t\n\tstatic bool _GetText(CiComplItem item, out string text) {\n\t\tusing var db = EdDatabases.OpenWinapi();\n\t\treturn db.Get(out text, $\"SELECT code FROM api WHERE name='{item.Text}'\");\n\t}\n\t\n\tpublic void OnCommitInsertDeclaration(CiComplItem item) {\n\t\tif (!_GetText(item, out string text)) return;\n\t\tif (_InsertDeclaration(item, text)) return;\n\t\tclipboard.text = text;\n\t\tprint.it(\"<>Clipboard:\\r\\n<code>\" + text + \"</code>\");\n\t}\n\t\n\tbool _InsertDeclaration(CiComplItem item, string text) {\n\t\tif (!_canInsert) return false;\n\t\tif (!CodeInfo.GetDocumentAndFindNode(out var cd, out var typenameNode, _typenameStart)) return false;\n\t\tvar semo = cd.semanticModel;\n\t\tvar sym = semo.GetSymbolInfo(typenameNode).Symbol;\n\t\tif (sym is not INamedTypeSymbol t || !t.IsFromSource()) return false;\n\t\tvar sr = t.DeclaringSyntaxReferences[0];\n\t\t\n\t\tSciCode doc = cd.sci;\n\t\tFileNode fSelect = null;\n\t\tif (sr.SyntaxTree != semo.SyntaxTree) {\n\t\t\tvar f = App.Model.Find(sr.SyntaxTree.FilePath, FNFind.CodeFile);\n\t\t\tif (!App.Model.SetCurrentFile(f, dontChangeTreeSelection: true)) return false;\n\t\t\tdoc = Panels.Editor.ActiveDoc;\n\t\t\tfSelect = cd.sci.EFile;\n\t\t}\n\t\t\n\t\tvar hs = new HashSet<string>();\n\t\tusing (doc.ENewUndoAction()) {\n\t\t\t_Insert(0, sr.GetSyntax(), text, item.Text, item.kind);\n\t\t}\n\t\t\n\t\tif (fSelect != null) App.Model.SetCurrentFile(fSelect);\n\t\treturn true;\n\t\t\n\t\tvoid _Insert(int level, SyntaxNode node, string text, string name, CiItemKind kind) {\n\t\t\tif (node is not ClassDeclarationSyntax nodeCD) return;\n\t\t\tint posClass = nodeCD.Keyword.SpanStart, posInsert = nodeCD.CloseBraceToken.SpanStart;\n\t\t\tstring emptyLine = \"\\r\\n\";\n\t\t\t\n\t\t\t//if constant, try to insert below the last existing constant with same prefix\n\t\t\tif (kind == CiItemKind.Constant) {\n\t\t\t\tint u = name.IndexOf('_') + 1;\n\t\t\t\tif (u > 1) {\n\t\t\t\t\tstring prefix = \" \" + name[..u], code = doc.aaaText;\n\t\t\t\t\tforeach (var v in nodeCD.ChildNodes().OfType<FieldDeclarationSyntax>()) {\n\t\t\t\t\t\tvar span = v.Span;\n\t\t\t\t\t\tint j = code.Find(prefix, span.Start..span.End);\n\t\t\t\t\t\tif (j > 0 && code.Find(\"const \", span.Start..j) > 0) {\n\t\t\t\t\t\t\tposInsert = v.FullSpan.End;\n\t\t\t\t\t\t\temptyLine = null;\n\t\t\t\t\t\t\t//break; //no, need the last\n\t\t\t\t\t\t} else if (emptyLine == null) break;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t//if (level == 0) { //insert missing usings first. Now in global.cs. Or CiErrors will add.\n\t\t\t//\tint len = doc.aaaLen16;\n\t\t\t//\tInsertCode.UsingDirective(\"Au;Au.Types;System;System.Runtime.InteropServices\"); //Au: wnd; Au.Types: RECT etc; System: IntPtr, Guid etc\n\t\t\t//\tint add = doc.aaaLen16 - len;\n\t\t\t//\tposInsert += add; posClass += add;\n\t\t\t//}\n\t\t\t\n\t\t\ttext = emptyLine + text + \"\\r\\n\";\n\t\t\tdoc.aaaInsertText(true, posInsert, text, addUndoPointAfter: true, restoreFolding: true);\n\t\t\t\n\t\t\t//recursively add declarations for unknown names found in now added declaration\n\t\t\tif (kind is CiItemKind.Constant or CiItemKind.Field or CiItemKind.Enum or CiItemKind.Class) return;\n\t\t\t//print.it(level, name);\n\t\t\tif (level > 30) return; //max seen 10. Tested: at level 10 uses ~40 KB of stack.\n\t\t\tif (!CodeInfo.GetDocumentAndFindNode(out var cd, out node, posClass)) return;\n\t\t\tif (node is not ClassDeclarationSyntax) return;\n\t\t\tvar semo = cd.semanticModel;\n\t\t\tvar newSpan = new TextSpan(posInsert, text.Length);\n\t\t\tvar da = semo.GetDiagnostics(newSpan); //the slowest part\n\t\t\tforeach (var d in da) {\n\t\t\t\tvar ec = (ErrorCode)d.Code;\n\t\t\t\tif (ec\n\t\t\t\t\tis ErrorCode.ERR_SingleTypeNameNotFound\n\t\t\t\t\tor ErrorCode.ERR_NameNotInContext //never seen\n\t\t\t\t\tor ErrorCode.ERR_BadAccess //eg \"'VARIANT' is inaccessible due to its protection level\", because defined as internal in some assembly\n\t\t\t\t\t) {\n\t\t\t\t\tvar loc = d.Location;\n\t\t\t\t\tif (!loc.IsInSource) {\n\t\t\t\t\t\tDebug_.Print(\"!insource\");\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tvar span = loc.SourceSpan;\n\t\t\t\t\tif (!newSpan.Contains(span)) {\n\t\t\t\t\t\tDebug_.Print(\"!contains\");\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tname = cd.code[span.Start..span.End];\n\t\t\t\t\t//print.it(name);\n\t\t\t\t\t\n\t\t\t\t\tif (!hs.Add(name)) { //same name again\n\t\t\t\t\t\t//print.it(\"same\", name);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\t//some parameter types add much garbage, but the parameters are rarely used.\n\t\t\t\t\t//\tIf parameter, it is usually null. If interface member parameter, the member is rarely used.\n\t\t\t\t\t//\tLet's add empty definition. It's easy to replace it with full definition when need.\n\t\t\t\t\tif (name == \"IBindCtx\") {\n\t\t\t\t\t\ttext = \"\"\"\n[ComImport, Guid(\"0000000e-0000-0000-C000-000000000046\"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\ninternal interface IBindCtx {}\n\"\"\";\n\t\t\t\t\t\tkind = CiItemKind.Interface;\n\t\t\t\t\t} else if (name == \"PROPVARIANT\") {\n\t\t\t\t\t\ttext = \"internal struct PROPVARIANT { int a, b; nint c, d; }\";\n\t\t\t\t\t\tkind = CiItemKind.Structure;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tusing var db = EdDatabases.OpenWinapi(); //fast, if compared with GetDiagnostics\n\t\t\t\t\t\tusing var stat = db.Statement(\"SELECT code, kind FROM api WHERE name=?\", name);\n\t\t\t\t\t\tif (!stat.Step()) {\n\t\t\t\t\t\t\tDebug_.Print(\"not in DB: \" + name);\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\ttext = stat.GetText(0);\n\t\t\t\t\t\tkind = (CiItemKind)stat.GetInt(1);\n\t\t\t\t\t\t//print.it(kind, text);\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\t_Insert(level + 1, node, text, name, kind);\n\t\t\t\t} else {\n\t\t\t\t\tif (ec == ErrorCode.ERR_ManagedAddr) continue; //possibly same name is an internal managed type in some assembly, but in our DB it may be unmanaged. This error is for for field; we'll catch its type later.\n\t\t\t\t\tif (ec == ErrorCode.WRN_NewNotRequired) continue; //when 'new' used with a repeated member of a base interface, the base is still not declared, therefore this warning\n\t\t\t\t\tDebug_.Print(d);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Edit/CiWorkspace.cs",
    "content": "extern alias CAW;\n\nusing Microsoft.CodeAnalysis;\nusing CAW::Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp;\nusing Microsoft.CodeAnalysis.CSharp.Syntax;\nusing Microsoft.CodeAnalysis.Text;\nusing Microsoft.CodeAnalysis.Shared.Extensions;\nusing CAW::Microsoft.CodeAnalysis.Shared.Extensions;\nusing Microsoft.CodeAnalysis.CSharp.Extensions;\n\n\nnamespace LA;\n\n/// <summary>\n/// Ctor creates new <b>AdhocWorkspace</b> (<see cref=\"Workspace\"/>) and adds projects and documents.\n/// Dtor disposes the <b>AdhocWorkspace</b>. Must be disposed soon.\n/// </summary>\nclass CiWorkspace : IDisposable {\n\tpublic AdhocWorkspace Workspace { get; }\n\t\n\t/// <summary>\n\t/// <c>=> Workspace.CurrentSolution</c>\n\t/// </summary>\n\tpublic Solution Solution => Workspace.CurrentSolution;\n\t\n\t/// <summary>\n\t/// <b>MetaComments</b> of the main project.\n\t/// </summary>\n\tpublic MetaComments Meta { get; }\n\t\n\t/// <summary>\n\t/// Files etc of the main project (the first) and pr projects.\n\t/// </summary>\n\tpublic ProjectData[] Projects { get; }\n\t\n\t/// <summary>\n\t/// Gets <b>Compilation</b> of the main project: <c>=> Solution.GetProject(Projects[0].projectId).GetCompilationAsync().Result</c>\n\t/// </summary>\n\tpublic Compilation GetCompilation() => Solution.GetProject(Projects[0].projectId).GetCompilationAsync().Result_();\n\t\n\t/// <summary>\n\t/// <b>DocumentId</b> of <i>fn</i>, or null.\n\t/// Searches only in the main project.\n\t/// </summary>\n\tpublic DocumentId GetDocId(FileNode fn) {\n\t\tforeach (var v in Projects[0].files) if (v.f.f == fn) return v.docId;\n\t\treturn null;\n\t}\n\t\n\tpublic CiWorkspace(FileNode fn, Caller caller) {\n\t\tWorkspace = new();\n\t\tList<ProjectData> projects = new();\n\t\t\n\t\tbool inEditor = caller is Caller.CodeInfoNormal or Caller.CodeInfoWpfPreview;\n\t\t\n\t\tif (!fn.FindProject(out var projFolder, out var projMain)) projMain = fn;\n\t\tDebug.Assert(projMain.IsCodeFile);\n\t\t\n\t\tif (inEditor) TestInternal.CiClear();\n\t\t\n\t\tList<ProjectInfo> aPI = new();\n\t\tDictionary<FileNode, ProjectReference> dPR = null;\n\t\tvar mcFlags = caller switch {\n\t\t\tCaller.CodeInfoNormal => MCFlags.ForCodeInfoInEditor,\n\t\t\tCaller.CodeInfoWpfPreview => MCFlags.ForCodeInfoInEditor | MCFlags.WpfPreview,\n\t\t\t_ => MCFlags.ForCodeInfo\n\t\t};\n\t\t(_, Meta) = _AddProject(projMain, projFolder, mcFlags);\n\t\tWorkspace.AddProjects(aPI);\n\t\tProjects = projects.ToArray();\n\t\t\n\t\t(ProjectId pid, MetaComments meta) _AddProject(FileNode projMain, FileNode projFolder, MCFlags mcFlags) {\n\t\t\tvar m = new MetaComments(mcFlags);\n\t\t\tm.Parse(projMain, projFolder);\n\t\t\t\n\t\t\tif (inEditor && m.TestInternal is string[] testInternal) TestInternal.CiAdd(m.Name, testInternal);\n\t\t\t\n\t\t\tvar projectId = ProjectId.CreateNewId();\n\t\t\t\n\t\t\tProjectData pd = new(m, projectId, new());\n\t\t\tprojects.Add(pd);\n\t\t\t\n\t\t\tif (inEditor) CiProjects.AttachFileOf(projectId, projMain);\n\t\t\tvar adi = new List<DocumentInfo>();\n\t\t\tforeach (var k in m.CodeFiles) {\n\t\t\t\tvar docId = DocumentId.CreateNewId(projectId);\n\t\t\t\tvar tav = TextAndVersion.Create(SourceText.From(k.code, Encoding.UTF8), VersionStamp.Default, k.f.FilePath);\n\t\t\t\tadi.Add(DocumentInfo.Create(docId, k.f.Name, null, SourceCodeKind.Regular, TextLoader.From(tav), k.f.ItemPath));\n\t\t\t\tif (inEditor) CiProjects.AttachFileOf(docId, k.f);\n\t\t\t\tpd.files.Add((k, docId));\n\t\t\t}\n\t\t\t//TODO3: reuse document+syntaxtree of global.cs and its meta c files if their text not changed.\n\t\t\t\n\t\t\tint iPI = aPI.Count; aPI.Add(null); //let this project be before pr projects in aPI\n\t\t\t\n\t\t\tList<ProjectReference> aPR = null;\n\t\t\tif (caller != Caller.Other && m.ProjectReferences is { } a1) {\n\t\t\t\tdPR ??= new();\n\t\t\t\tforeach (var v in a1) {\n\t\t\t\t\tif (!dPR.TryGetValue(v.f, out var pr)) {\n\t\t\t\t\t\tpr = new ProjectReference(_AddProject(v.f, v.f.Parent, mcFlags & ~MCFlags.WpfPreview).pid);\n\t\t\t\t\t\tdPR.Add(v.f, pr);\n\t\t\t\t\t}\n\t\t\t\t\t(aPR ??= new()).Add(pr);\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\taPI[iPI] = ProjectInfo.Create(projectId, VersionStamp.Default, m.Name, m.Name, LanguageNames.CSharp, null, null,\n\t\t\t\tm.CreateCompilationOptions(),\n\t\t\t\tm.CreateParseOptions(),\n\t\t\t\tadi,\n\t\t\t\taPR,\n\t\t\t\tm.References.Refs);\n\t\t\t\n\t\t\treturn (projectId, m);\n\t\t}\n\t}\n\t\n\tpublic void Dispose() {\n\t\tWorkspace.Dispose();\n\t}\n\t\n\tpublic enum Caller {\n\t\t/// <summary>Called by <b>CodeInfo</b>. Supports meta testInternal, <see cref=\"CiProjects.FileOf\"/>.</summary>\n\t\tCodeInfoNormal,\n\t\t\n\t\t/// <summary><b>CodeInfoNormal</b> + <see cref=\"MCFlags.WpfPreview\"/>.</summary>\n\t\tCodeInfoWpfPreview,\n\t\t\n\t\t/// <summary>Add project references (meta pr).</summary>\n\t\tOtherPR,\n\t\t\n\t\t/// <summary>Don't add project references (meta pr).</summary>\n\t\tOther\n\t}\n\t\n\tpublic record struct ProjectData(MetaComments meta, ProjectId projectId, List<(MCCodeFile f, DocumentId docId)> files);\n}\n"
  },
  {
    "path": "Au.Editor/Edit/CodeExporter.cs",
    "content": "extern alias CAW;\n\nusing Microsoft.CodeAnalysis;\nusing CAW::Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp;\nusing Microsoft.CodeAnalysis.CSharp.Syntax;\nusing Microsoft.CodeAnalysis.Text;\nusing Microsoft.CodeAnalysis.Classification;\nusing CAW::Microsoft.CodeAnalysis.Classification;\nusing static LA.SciTheme;\n\nnamespace LA;\n\nstatic class CodeExporter {\n\tpublic static EStyle[] GetStyles(string s) {\n\t\tusing var ws = new AdhocWorkspace();\n\t\tvar document = CiUtil.CreateDocumentFromCode(ws, s, needSemantic: true);\n\t\t//var semo = document.GetSemanticModelAsync().Result;\n\t\t\n\t\tvar a = new EStyle[s.Length];\n\t\tint prevEnd = 0; EStyle prevStyle = 0;\n\t\tforeach (var v in CiUtil.GetClassifiedSpansAsync(document, 0, s.Length).Result_()) {\n\t\t\tEStyle style = CiStyling.StyleFromClassifiedSpan(v);\n\t\t\tint start = v.TextSpan.Start, end = v.TextSpan.End;\n\t\t\t//print.it(style, s[start..end]);\n\t\t\tif (style == prevStyle && start > prevEnd && a[prevEnd] == 0) start = prevEnd; //join adjacent styles separated by whitespace\n\t\t\tprevEnd = end; prevStyle = style;\n\t\t\ta.AsSpan(start..end).Fill(style);\n\t\t}\n\t\t\n\t\treturn a;\n\t}\n\t\n\tpublic static string ExportForum(string s) {\n\t\tvar a = GetStyles(s);\n\t\tvar b = new StringBuilder();\n\t\tEStyle prevStyle = 0;\n\t\tfor (int i = 0; i < a.Length; i++) {\n\t\t\tvar style = a[i];\n\t\t\tif (style != prevStyle) {\n\t\t\t\tif (prevStyle != 0) b.Append(\"(`)\");\n\t\t\t\tif (style != 0) b.Append(\"(`\").Append(_StyleToString(style)).Append(\")\");\n\t\t\t\tprevStyle = style;\n\t\t\t}\n\t\t\tb.Append(s[i]);\n\t\t}\n\t\tif (prevStyle != 0) b.Append(\"(`)\");\n\t\treturn b.ToString();\n\t}\n\t\n\tpublic static string ExportHtml(string s, bool spanClass, bool withCss = false) {\n\t\tvar a = GetStyles(s);\n\t\tStringWriter t = new();\n\t\tif (withCss) t.Write(\"<style>\\r\\n{0}</style>\\r\\n\", ExportCss());\n\t\tt.Write(\"<pre\");\n\t\tif (spanClass) t.Write(\" class='au'\");\n\t\tif (!spanClass || withCss) t.Write(\" style='{0}'\", c_preStyle);\n\t\tt.Write(\">\\r\\n\");\n\t\tEStyle prevStyle = 0;\n\t\tint i = 0, ip = 0;\n\t\tfor (; i < a.Length; i++) {\n\t\t\tvar style = a[i];\n\t\t\tif (style != prevStyle) {\n\t\t\t\t_AppendText();\n\t\t\t\tif (style != 0) {\n\t\t\t\t\tif (spanClass) {\n\t\t\t\t\t\tt.Write(\"<span class='\");\n\t\t\t\t\t\tt.Write(_StyleToString(style));\n\t\t\t\t\t} else {\n\t\t\t\t\t\tvar k = _StyleToStruct(style);\n\t\t\t\t\t\tt.Write(\"<span style='color:#{0:X6}\", k.color);\n\t\t\t\t\t\tif (k.bold) t.Write(\";font-weight: bold\");\n\t\t\t\t\t\t//we use default styles, and there are no styles with italic or underline\n\t\t\t\t\t\t//if (k.italic) t.Write(\";font-style: italic\");\n\t\t\t\t\t\t//if (k.underline) t.Write(\";text-decoration: underline\");\n\t\t\t\t\t}\n\t\t\t\t\tt.Write(\"'>\");\n\t\t\t\t}\n\t\t\t\tprevStyle = style;\n\t\t\t}\n\t\t}\n\t\t_AppendText();\n\t\t\n\t\tvoid _AppendText() {\n\t\t\tif (i > ip) { System.Net.WebUtility.HtmlEncode(s[ip..i], t); ip = i; }\n\t\t\tif (prevStyle != 0) t.Write(\"</span>\");\n\t\t}\n\t\t\n\t\tt.Write(\"</pre>\\r\\n\");\n\t\treturn t.ToString();\n\t}\n\t\n\tconst string c_preStyle = \"background-color:#FFFFFF;border:#D1D7DC;border-style:solid;border-width:1px;padding-left:2px;line-height:normal;color:black;tab-size:4;font-family:\\\"Consolas\\\";\";\n\t\n\tpublic static string ExportCss() {\n\t\tvar b = new StringBuilder();\n\t\tb.Append(\"pre.au {\").Append(c_preStyle).AppendLine(\"}\");\n\t\t\n\t\t_Style(EStyle.Comment);\n\t\t_Style(EStyle.Constant);\n\t\t_Style(EStyle.Excluded);\n\t\t_Style(EStyle.Function);\n\t\t_Style(EStyle.Keyword);\n\t\t_Style(EStyle.Label);\n\t\t_Style(EStyle.LocalVariable);\n\t\t_Style(EStyle.Namespace);\n\t\t_Style(EStyle.Number);\n\t\t_Style(EStyle.Operator);\n\t\t_Style(EStyle.Preprocessor);\n\t\t_Style(EStyle.Punctuation);\n\t\t_Style(EStyle.String);\n\t\t_Style(EStyle.StringEscape);\n\t\t_Style(EStyle.Type);\n\t\t_Style(EStyle.XmlDocTag);\n\t\t_Style(EStyle.XmlDocText);\n\t\t\n\t\tvoid _Style(EStyle style) {\n\t\t\tvar k = _StyleToStruct(style);\n\t\t\tb.AppendFormat(\"pre.au .{0}{{color:#{1:X6};\", _StyleToString(style), k.color);\n\t\t\tif (k.bold) b.Append(\"font-weight: bold;\");\n\t\t\t//if (k.italic) b.Append(\"font-style: italic;\");\n\t\t\t//if (k.underline) b.Append(\"text-decoration: underline;\");\n\t\t\tb.Append(\"}\");\n\t\t}\n\t\t\n\t\treturn b.AppendLine().ToString();\n\t}\n\t\n\tstatic string _StyleToString(EStyle style)\n\t\t=> style switch {\n\t\t\tEStyle.Comment => \"cm\",\n\t\t\tEStyle.Constant => \"cn\",\n\t\t\tEStyle.Excluded => \"ex\",\n\t\t\tEStyle.Function or EStyle.Event => \"fn\",\n\t\t\tEStyle.Keyword => \"kw\",\n\t\t\tEStyle.Label => \"lb\",\n\t\t\tEStyle.LocalVariable or EStyle.Field => \"vr\",\n\t\t\tEStyle.Namespace => \"ns\",\n\t\t\tEStyle.Number => \"nr\",\n\t\t\tEStyle.Operator => \"op\",\n\t\t\tEStyle.Preprocessor => \"pd\",\n\t\t\tEStyle.Punctuation => \"pn\",\n\t\t\tEStyle.String => \"st\",\n\t\t\tEStyle.StringEscape => \"se\",\n\t\t\tEStyle.Type => \"tp\",\n\t\t\tEStyle.XmlDocTag => \"xt\",\n\t\t\tEStyle.XmlDocText => \"xd\",\n\t\t\t_ => \"st\" //Rx (unused)\n\t\t};\n\t\n\tstatic TStyle _StyleToStruct(EStyle style) => SciTheme.Default[style];\n}\n"
  },
  {
    "path": "Au.Editor/Edit/CodeInfo.cs",
    "content": "extern alias CAW;\n\n//#define NO_COMPL_CORR_SIGN\n\nusing System.Windows.Input;\nusing System.Windows;\nusing Au.Controls;\n\nusing Microsoft.CodeAnalysis;\nusing CAW::Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp;\nusing Microsoft.CodeAnalysis.CSharp.Syntax;\nusing Microsoft.CodeAnalysis.Text;\nusing Microsoft.CodeAnalysis.Shared.Extensions;\nusing CAW::Microsoft.CodeAnalysis.Shared.Extensions;\nusing Microsoft.CodeAnalysis.Classification;\nusing CAW::Microsoft.CodeAnalysis.Classification;\nusing Microsoft.CodeAnalysis.Completion;\n\nnamespace LA;\n\nstatic class CodeInfo {\n\tinternal static readonly CiCompletion _compl = new();\n\tinternal static readonly CiSignature _signature = new();\n\tinternal static readonly CiAutocorrect _correct = new();\n\tinternal static readonly CiQuickInfo _quickInfo = new();\n\tinternal static readonly CiStyling _styling = new();\n\tinternal static readonly CiErrors _diag = new();\n\tinternal static readonly CiTools _tools = new();\n\tinternal static readonly CiProjects _projects = new();\n\t\n\tstatic Solution _solution;\n\tstatic ProjectId _projectId;\n\tstatic DocumentId _documentId;\n\tstatic Document _document;\n\tstatic CompilationUnitSyntax _syntaxRoot;\n\t//static MetaComments _meta;\n\tstatic string _metaText;\n\tstatic bool _isWarm;\n\tstatic bool _isUI;\n\tstatic RECT _sciRect;\n\t\n\tpublic static void UiLoaded() {\n\t\t//This code warms up Roslyn. It can take several s.\n\t\t//\tDuring that time the window is visible (except document) but disabled.\n\t\t\n\t\t//perf.next('u');\n\t\t//don't allow users to make any changes until Roslyn loaded. It can be dangerous.\n\t\tApp.Hmain.Enable(false);\n\t\twnd[] aEnable = null;\n\t\tApp.Dispatcher.InvokeAsync(() => { //disable floating panels too\n\t\t\taEnable = wnd.getwnd.threadWindows(process.thisThreadId, onlyVisible: true);\n\t\t\t//print.it(aEnable);\n\t\t\tfor (int i = 0; i < aEnable.Length; i++)\n\t\t\t\tif (aEnable[i].IsEnabled()) aEnable[i].Enable(false); else aEnable[i] = default;\n\t\t});\n\t\tvar doc = Panels.Editor.ActiveDoc;\n\t\tif (doc != null) doc.Visibility = Visibility.Hidden; //hide document window. The black unfolded text is distracting. Does not have sense to show it.\n\t\t\n\t\t_warmupTask = Task.Run(() => {\n\t\t\t//using var p1 = perf.local();\n\t\t\ttry {\n\t\t\t\tvar code = @\"using Au; print.it(\"\"t\"\" + 1);\";\n\t\t\t\t\n\t\t\t\tvar refs = new MetaReferences().Refs;\n\t\t\t\tProjectId projectId = ProjectId.CreateNewId();\n\t\t\t\tDocumentId documentId = DocumentId.CreateNewId(projectId);\n\t\t\t\tusing var ws = new AdhocWorkspace();\n\t\t\t\tvar sol = ws.CurrentSolution\n\t\t\t\t\t.AddProject(projectId, \"p\", \"p\", LanguageNames.CSharp)\n\t\t\t\t\t.AddMetadataReferences(projectId, refs)\n\t\t\t\t\t.AddDocument(documentId, \"f.cs\", code);\n\t\t\t\tvar document = sol.GetDocument(documentId);\n\t\t\t\t//p1.Next();\n\t\t\t\t\n\t\t\t\tvar semo = document.GetSemanticModelAsync().Result_();\n\t\t\t\t//p1.Next('s');\n\t\t\t\t\n\t\t\t\t//let the coloring and folding in editor start working immediately\n\t\t\t\tCiUtil.GetClassifiedSpansAsync(document, 0, code.Length).Wait();\n\t\t\t\t//p1.Next('c');\n\t\t\t\t\n\t\t\t\tApp.Dispatcher.InvokeAsync(() => {\n\t\t\t\t\t_isWarm = true;\n\t\t\t\t\tReadyForStyling?.Invoke();\n\t\t\t\t\tReadyForStyling = null; //GC\n\t\t\t\t\tPanels.Editor.ActiveDocChanged += Stop;\n\t\t\t\t\tApp.Timer025sWhenVisible += _Timer025sWhenVisible;\n\t\t\t\t\t_Finally();\n\t\t\t\t});\n\t\t\t\t//p1.Next();\n\t\t\t\t\n\t\t\t\t500.ms();\n\t\t\t\t//p1.Next();\n\t\t\t\tvar compl = CompletionService.GetService(document);\n\t\t\t\tcompl.GetCompletionsAsync(document, code.Find(\".it\") + 1); //not necessary, but without it sometimes the first completion list is too slow if the user types fast\n\t\t\t\t//p1.Next('C');\n\t\t\t\t\n\t\t\t\tCompiler.Warmup(document); //not necessary, but it's better when the first compilation is 200 ms instead of 500\n\t\t\t\t\n\t\t\t\t//EdUtil.MinimizeProcessPhysicalMemory(500); //with this later significantly slower\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tprint.it(ex);\n\t\t\t\tApp.Dispatcher.InvokeAsync(_Finally);\n\t\t\t}\n\t\t});\n\t\t\n\t\tvoid _Finally() {\n\t\t\tif (doc != null) doc.Visibility = Visibility.Visible;\n\t\t\tApp.Hmain.Enable(true);\n\t\t\tif (aEnable != null) {\n\t\t\t\tfor (int i = 0; i < aEnable.Length; i++)\n\t\t\t\t\tif (!aEnable[i].Is0) aEnable[i].Enable(true);\n\t\t\t}\n\t\t\t//perf.nw('R');\n\t\t\t_warmupTask = null;\n\t\t\t\n\t\t\tIsReadyForEditing = true;\n\t\t\tif (ReadyForEditing != null) {\n\t\t\t\ttry { ReadyForEditing(); } catch (Exception e1) { print.it(e1); } //used in editorExtension scripts\n\t\t\t\tReadyForEditing = null; //GC\n\t\t\t}\n\t\t\tGit.AutoBackup(true);\n\t\t\t\n#if DEBUG\n\t\t\tRoslynMod.Print.PrintItCallback = o => print.it(o);\n#endif\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Code styling and folding already can work after program starts.\n\t/// </summary>\n\tpublic static bool IsReadyForStyling => _isWarm;\n\t\n\t/// <summary>\n\t/// When code styling and folding already can work after program starts.\n\t/// Runs in main thread.\n\t/// </summary>\n\tpublic static event Action ReadyForStyling;\n\t\n\t/// <summary>\n\t/// Main window already enabled after program starts.\n\t/// </summary>\n\tpublic static bool IsReadyForEditing { get; private set; }\n\t\n\t/// <summary>\n\t/// When main window enabled after program starts.\n\t/// Runs in main thread, after <b>ReadyForStyling</b>.\n\t/// </summary>\n\tpublic static event Action ReadyForEditing;\n\t\n\t/// <summary>\n\t/// If the warmup task is still running, which means that still not ready for styling etc and should not open files, waits until the task completes.\n\t/// </summary>\n\tpublic static void WaitUntilReadyForStyling() {\n\t\tif (_warmupTask == null) return;\n\t\t_warmupTask.Wait();\n\t\twait.doEvents(); //run Dispatcher.InvokeAsync(_Finally)\n\t}\n\tstatic Task _warmupTask;\n\t\n\tstatic bool _CanWork(SciCode doc) {\n\t\tif (!_isWarm) return false;\n\t\tif (doc == null) return false;\n\t\tif (!doc.EFile.IsCodeFile) return false;\n\t\tif (doc != Panels.Editor.ActiveDoc) { _Uncache(); return false; } //maybe changed an inactive file that participates in current compilation //FUTURE: what if isn't open?\n\t\treturn true;\n\t}\n\t\n\tstatic void _Uncache() {\n\t\t//print.it(\"_Uncache\");\n\t\tCurrentWorkspace?.Dispose();\n\t\tCurrentWorkspace = null;\n\t\t_solution = null;\n\t\t_projectId = null;\n\t\t_documentId = null;\n\t\t_document = null;\n\t\t_syntaxRoot = null;\n\t\t//_meta = null;\n\t\t_metaText = null;\n\t}\n\t\n\tpublic static void Stop() {\n\t\tCancel();\n\t\t_Uncache();\n\t}\n\t\n\tpublic static void Cancel() {\n\t\tHideTextPopupAndTempWindows();\n\t\t_compl.Cancel();\n\t\t_signature.Cancel();\n\t}\n\t\n\t/// <summary>\n\t/// Similar to <see cref=\"FilesChanged\"/>, but called when other conditions changed, eg a nuget package [un]installed.\n\t/// Not async, and does not fire an event.\n\t/// </summary>\n\tpublic static void StopAndUpdateStyling() {\n\t\tStop();\n\t\t_styling.Update();\n\t}\n\t\n\t/// <summary>\n\t/// Called when files added, deleted, moved, copied, imported, renamed, text replaced.\n\t/// Eg need to update styling and diagnostics when a meta c file became [un]available or when project folder structure changed.\n\t/// Will update async, eg single updating for multiple calls. Will call <see cref=\"StopAndUpdateStyling\"/> and <see cref=\"FilesChangedEvent\"/>.\n\t/// </summary>\n\tpublic static void FilesChanged() {\n\t\tif (_filesChangedAsync) return; _filesChangedAsync = true;\n\t\tApp.Dispatcher.InvokeAsync(() => {\n\t\t\t_filesChangedAsync = false;\n\t\t\tStopAndUpdateStyling();\n\t\t\tFilesChangedEvent?.Invoke();\n\t\t});\n\t\t//why async:\n\t\t//\t1. Easier to consolidate multiple events.\n\t\t//\t2. Easier to create files with custom text (the text is added after calling this).\n\t\t//\t3. In some cases may be better/safer.\n\t}\n\tstatic bool _filesChangedAsync;\n\t\n\t/// <summary>\n\t/// When files added, deleted, moved, copied, imported, renamed, text replaced.\n\t/// Called through Dispatcher.InvokeAsync and may consolidate multiple changes.\n\t/// </summary>\n\tpublic static event Action FilesChangedEvent;\n\t\n\tpublic static void SciKillFocus(SciCode doc) {\n\t\tif (!_CanWork(doc)) return;\n#if DEBUG\n\t\tif (Debugger.IsAttached) return;\n#endif\n\t\t//hide code info windows, except when a code info window is focused. Code info window names start with \"Ci.\".\n\t\tvar aw = wnd.thisThread.active;\n\t\tif (aw.Is0) Stop(); else if (!(KPopup.FromHwnd(aw) is KPopup p && p.Name.Starts(\"Ci.\"))) Cancel();\n\t}\n\t\n\tpublic static bool SciCmdKey(SciCode doc, KKey key, ModifierKeys mod) {\n#if NO_COMPL_CORR_SIGN\n\t\treturn false;\n#endif\n\t\tif (!_CanWork(doc)) return false;\n\t\t\n\t\tswitch ((key, mod)) {\n\t\tcase (KKey.Space, ModifierKeys.Control):\n\t\t\tShowCompletionList(doc);\n\t\t\treturn true;\n\t\tcase (KKey.Space, ModifierKeys.Control | ModifierKeys.Shift):\n\t\t\tShowSignature(doc);\n\t\t\treturn true;\n\t\tcase (KKey.Escape, 0):\n\t\tcase (KKey.Down, 0):\n\t\tcase (KKey.Up, 0):\n\t\tcase (KKey.PageDown, 0):\n\t\tcase (KKey.PageUp, 0):\n\t\tcase (KKey.Home, 0):\n\t\tcase (KKey.End, 0):\n\t\t\tif ((HideTextPopup() || _tools.HideTempWindows()) && key == KKey.Escape) return true;\n\t\t\t//never mind: on Esc, if several popups, should hide the top popup.\n\t\t\t//\tWe instead hide less-priority popups when showing a popup. Then Esc will hide the correct popup in most cases.\n\t\t\tif (_compl.OnCmdKey_SelectOrHide(key) || _signature.OnCmdKey(key)) return true;\n\t\t\tif (key == KKey.Escape) if (doc.SnippetMode_?.SciKey(key, mod) == true) return true;\n\t\t\tbreak;\n\t\tcase (KKey.Tab, 0):\n\t\tcase (KKey.Enter, 0):\n\t\t\tif (_compl.OnCmdKey_Commit(doc, key) != CiComplResult.None) return true;\n\t\t\tif (doc.SnippetMode_?.SciKey(key, mod) == true) return true;\n\t\t\tif (_correct.SciBeforeKey(doc, key, mod)) return true;\n\t\t\tbreak;\n\t\tcase (KKey.Tab, ModifierKeys.Shift):\n\t\t\tif (doc.SnippetMode_?.SciKey(key, mod) == true) return true;\n\t\t\tbreak;\n\t\tcase (KKey.Enter, ModifierKeys.Shift) or (KKey.Enter, ModifierKeys.Control) or (KKey.Enter, ModifierKeys.Control | ModifierKeys.Shift):\n\t\tcase (KKey.OemSemicolon, ModifierKeys.Control):\n\t\t\tvar complResult = _compl.OnCmdKey_Commit(doc, key);\n\t\t\tif (complResult == CiComplResult.Complex) return true;\n\t\t\tif (_correct.SciBeforeKey(doc, key, mod) | (complResult != CiComplResult.None)) return true;\n\t\t\tbreak;\n\t\tcase (KKey.Back, 0):\n\t\tcase (KKey.Delete, 0):\n\t\t\tif (_correct.SciBeforeKey(doc, key, mod)) return true;\n\t\t\tbreak;\n\t\t}\n\t\treturn false;\n\t}\n\t\n\tpublic static bool SciBeforeCharAdded(SciCode doc, char ch) {\n#if NO_COMPL_CORR_SIGN\n\t\treturn false;\n#endif\n\t\tif (!_CanWork(doc)) return false;\n\t\t\n\t\tif (_compl.IsVisibleUI) {\n\t\t\tif (CiComplResult.Complex == _compl.SciCharAdding_Commit(doc, ch)) return true;\n\t\t}\n\t\t\n\t\treturn _correct.SciBeforeCharAdded(doc, ch);\n\t}\n\t\n\tpublic static bool SciModified(SciCode doc, in Sci.SCNotification n) {\n\t\tif (!_CanWork(doc)) return false;\n\t\t_document = null;\n\t\t_compl.SciModified(doc, in n);\n\t\t_styling.SciModified(doc, in n);\n\t\t_diag.SciModified(doc, in n);\n\t\tPanels.Outline.SciModified();\n\t\treturn true;\n\t}\n\t\n\tpublic static void SciCharAdded(SciCode doc, char ch) {\n#if NO_COMPL_CORR_SIGN\n\t\treturn;\n#endif\n\t\tif (!_CanWork(doc)) return;\n\t\t\n\t\tusing var c = new CharContext(doc, ch);\n\t\t_correct.SciCharAdded(c); //sync adds or removes ')' etc if need.\n\t\tif (!c.ignoreChar) {\n\t\t\t_compl.SciCharAdded_ShowList(c); //async gets completions and shows popup list. If already showing, filters/selects items.\n\t\t\t_signature.SciCharAdded(c.doc, c.ch); //async shows signature help. Faster than _compl.\n\t\t}\n\t\t\n\t\t//Example: user types 'wri('.\n\t\t//\tWhen typed 'w', _compl.SciCharAdded_ShowList shows popup list (async).\n\t\t//\tWhile typing 'ri', _compl.SciModified in the list selects Write.\n\t\t//\tWhen typed '(':\n\t\t//\t\t_compl.SciCharAdded_Commit replaces 'wri(' with 'Write('. Caret is after '('.\n\t\t//\t\t_correct adds ')'. Caret is still after '('.\n\t\t//\t\t_signature shows signature help.\n\t\t//\tIf then user types 'tr)':\n\t\t//\t\t_compl on 't' shows popup list and on ')' replaces 'tr)' with 'true)'.\n\t\t//\t\t_correct deletes the ')' it added before.\n\t\t//\t\t_signature not called because discardChar==true. To hide signature help are used temp ranges.\n\t\t//\tFinally we have 'Write(true)', and caret is after it, and no double '))'.\n\t\t//\tIf instead types 'tr;':\n\t\t//\t\t_correct on ';' moves caret after ')', and finally we have 'Write(true);', and caret after ';'.\n\t}\n\t\n\tpublic static void SciUpdateUI(SciCode doc, Sci.UPDATE updated) {\n#if NO_COMPL_CORR_SIGN\n\t\treturn;\n#endif\n\t\t//print.it(\"SciUpdateUI\", modified, _tempNoAutoComplete);\n\t\tif (!_CanWork(doc)) return;\n\t\t\n\t\tif (updated.HasAny(Sci.UPDATE.SC_UPDATE_CONTENT | Sci.UPDATE.SC_UPDATE_SELECTION)) {\n\t\t\t//_compl.SciUpdateUI(doc);\n\t\t\t_signature.SciPositionChanged(doc);\n\t\t\tCiFind.SciUpdateUI(doc, updated.Has(Sci.UPDATE.SC_UPDATE_CONTENT));\n\t\t} else if (updated.HasAny(Sci.UPDATE.SC_UPDATE_V_SCROLL | Sci.UPDATE.SC_UPDATE_H_SCROLL)) {\n\t\t\tCancel();\n\t\t\t//if (0 != (updated & 4)) { //vertically\n\t\t\t//\t_styling.Timer250msWhenVisibleAndWarm(doc); //rejected. Uses much CPU. The 250 ms timer is OK.\n\t\t\t//}\n\t\t}\n\t}\n\t\n\tpublic static void ShowCompletionList(SciCode doc = null) {\n\t\tdoc ??= Panels.Editor.ActiveDoc;\n\t\tif (!_CanWork(doc)) return;\n\t\t_compl.ShowList();\n\t}\n\t\n\tpublic static void ShowSignature(SciCode doc = null) {\n\t\tdoc ??= Panels.Editor.ActiveDoc;\n\t\tif (!_CanWork(doc)) return;\n\t\t_signature.ShowSignature(doc);\n\t}\n\t\n\t/// <summary>\n\t/// Shows or hides quick info or/and error info.\n\t/// </summary>\n\tpublic static async void SciMouseDwellStarted(SciCode doc, int pos8) {\n\t\tif (!_CanWork(doc) || pos8 < 0) return;\n\t\t\n\t\tvar text0 = doc.aaaText;\n\t\tint pos16 = doc.aaaPos16(pos8);\n\t\tvar diag = _diag.GetPopupTextAt(doc, pos8, pos16, out var onLinkClick);\n\t\tvar quick = await _quickInfo.GetTextAt(pos16);\n\t\tif (doc != Panels.Editor.ActiveDoc || (object)text0 != doc.aaaText) return; //changed while awaiting\n\t\t\n\t\tif (diag == null && quick == null) {\n\t\t\tHideTextPopup();\n\t\t} else {\n\t\t\tvar text = diag ?? quick;\n\t\t\tif (quick != null && diag != null) {\n\t\t\t\ttext.Blocks.Add(new System.Windows.Documents.BlockUIContainer(new System.Windows.Controls.Separator { Margin = new(4) }));\n\t\t\t\ttext.Blocks.Add(quick);\n\t\t\t}\n\t\t\t_ShowTextPopup(doc, pos16, text, onLinkClick);\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Use with `using` before pasting, dropping or inserting text when may need special processing, eg auto-inserting 'using' directives.\n\t/// </summary>\n\tpublic class PastingEtc : IDisposable {\n\t\tSciCode _doc;\n\t\tCiAutocorrect.Pasting _ac;\n\t\t\n\t\t/// <param name=\"silent\">Insert missing usings without showing dialog.</param>\n\t\tpublic PastingEtc(SciCode doc, bool silent = false) {\n\t\t\tif (!_CanWork(doc)) return;\n\t\t\t_doc = doc;\n\t\t\t_diag.Pasting(_doc, silent);\n\t\t\t_ac = new(_doc);\n\t\t}\n\t\t\n\t\tpublic void Dispose() {\n\t\t\tif (_doc == null) return;\n\t\t\t_ac.After();\n\t\t}\n\t}\n\t\n\tpublic class Context {\n\t\tpublic readonly SciCode sci;\n\t\tpublic readonly string code;\n\t\tpublic readonly StartEnd meta;\n\t\tpublic int pos;\n\t\tpublic readonly bool isCodeFile;\n\t\t\n\t\tpublic Document document { get; private set; }\n\t\t\n\t\tpublic CompilationUnitSyntax syntaxRoot { get; private set; }\n\t\t\n\t\tpublic SemanticModel semanticModel => document.GetSemanticModelAsync().Result_(); //only first time slow\n\t\t\n\t\t/// <summary>\n\t\t/// Initializes all fields except document.\n\t\t/// For <b>sci</b> uses <b>Panels.Editor.aaActiveDoc</b>.\n\t\t/// </summary>\n\t\t/// <param name=\"pos\">If -1, gets current position. If -2, gets selection start.</param>\n\t\tpublic Context(int pos) {\n\t\t\tDebug.Assert(App.IsMainThread);\n\t\t\t\n\t\t\tsci = Panels.Editor.ActiveDoc;\n\t\t\tcode = sci.aaaText;\n\t\t\tthis.pos = pos switch { -1 => sci.aaaCurrentPos16, -2 => sci.aaaSelectionStart16, _ => pos };\n\t\t\tDebug.Assert((uint)this.pos <= code.Length);\n\t\t\tif (isCodeFile = sci.EFile.IsCodeFile) meta = MetaComments.FindMetaComments(code);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Initializes the document field.\n\t\t/// Creates or updates Solution if need.\n\t\t/// Returns false if fails, eg if code too big.\n\t\t/// </summary>\n\t\tpublic bool GetDocument() {\n\t\t\tif (_document != null) {\n\t\t\t\tdocument = _document;\n\t\t\t\tsyntaxRoot = _syntaxRoot;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\t//perf.first();\n\t\t\t\n\t\t\t//return false if code is too big. Eg Roslyn hangs if pasted 20 MB \"\"\"XML\"\"\".\n\t\t\tif (code.Length > 10_000_000) {\n\t\t\t\t_Uncache();\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t\n\t\t\tif (_solution != null && !code.Eq(meta.start..meta.end, _metaText)) {\n\t\t\t\t_Uncache();\n\t\t\t\t_styling.Update();\n\t\t\t}\n\t\t\tif (_solution == null) _metaText = code[meta.start..meta.end];\n\t\t\t\n\t\t\ttry {\n\t\t\t\tif (_solution == null) {\n\t\t\t\t\t_CreateWorkspace(sci);\n\t\t\t\t} else {\n\t\t\t\t\t_solution = _solution.WithDocumentText(_documentId, SourceText.From(code, Encoding.UTF8));\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\t//Debug_.Print(ex);\n\t\t\t\tprint.it(ex);\n\t\t\t\t_Uncache();\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t\n\t\t\tdocument = _document = _solution.GetDocument(_documentId);\n\t\t\t//perf.next();\n\t\t\t\n\t\t\t//syntaxRoot protects the syntax tree from GC. Creating it is expensive.\n\t\t\t//\tRoslyn keeps just a week reference, and could have to recompute it for every task.\n\t\t\t//\tNote: now I can't reproduce. It seems TryGetSyntaxRoot etc always succeeds.\n\t\t\tsyntaxRoot = _syntaxRoot = document.GetSyntaxRootSynchronously(default) as CompilationUnitSyntax;\n\t\t\t\n\t\t\t//When certain invalid code exists, the Roslyn's copy of file code (syntaxRoot.GetText()) may be different (usually much shorter).\n\t\t\t//\tThen calling various Roslyn functions would throw exception. Better return false now.\n\t\t\t//\tIt seems it was caused by _ModifyTLS, now removed. //FUTURE: remove this code?\n\t\t\tif (syntaxRoot.EndOfFileToken.SpanStart != code.Length) {\n#if DEBUG\n\t\t\t\tprint.clear();\n\t\t\t\tDebug_.Print(\"---- bad code ----\");\n\t\t\t\tforeach (var v in syntaxRoot.ChildNodes()) CiUtil.PrintNode(v);\n#endif\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t\n\t\t\treturn true;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Creates new Context and calls its GetDocument.\n\t/// Returns false if: 1. Not a code file; 2. position is in meta comments (unless metaToo==true); 3. Fails to create/update Solution (unlikely). Then r.document is null.\n\t/// If returns true, r.document is Document for Panels.Editor.ActiveDoc. If need, parses meta, creates Project, Solution, etc.\n\t/// Always sets other r fields.\n\t/// </summary>\n\t/// <param name=\"position\">If -1, gets current position. If -2, gets selection start.</param>\n\t/// <param name=\"metaToo\">Don't return false if position is in meta comments.</param>\n\tpublic static bool GetContextAndDocument(out Context r, int position = -1, bool metaToo = false) {\n\t\tif (!GetContextWithoutDocument(out r, position, metaToo)) return false;\n\t\treturn r.GetDocument();\n\t}\n\t\n\t/// <summary>\n\t/// Creates new Context with document=null. Even if returns false.\n\t/// Returns false if: 1. Not a code file; 2. position is in meta comments (unless metaToo==true).\n\t/// </summary>\n\t/// <param name=\"position\">If -1, gets current position. If -2, gets selection start.</param>\n\t/// <param name=\"metaToo\">Don't return false if position is in meta comments.</param>\n\tpublic static bool GetContextWithoutDocument(out Context r, int position = -1, bool metaToo = false) {\n\t\tif (Panels.Editor.ActiveDoc == null) { r = null; return false; }\n\t\tr = new Context(position);\n\t\tif (!r.isCodeFile) return false;\n\t\tif (!metaToo && r.pos < r.meta.end && r.pos > r.meta.start) return false;\n\t\treturn true;\n\t}\n\t\n\t/// <summary>\n\t/// Calls <see cref=\"GetContextAndDocument\"/>, gets its syntax root and finds node.\n\t/// </summary>\n\t/// <param name=\"position\">If -1, gets current position. If -2, gets selection start.</param>\n\t/// <param name=\"metaToo\">Don't return false if position is in meta comments.</param>\n\tpublic static bool GetDocumentAndFindNode(out Context r, out SyntaxNode node, int position = -1, bool metaToo = false, bool findInsideTrivia = false) {\n\t\tif (!GetContextAndDocument(out r, position, metaToo)) { node = null; return false; }\n\t\tnode = r.syntaxRoot.FindToken(r.pos, findInsideTrivia).Parent;\n\t\treturn true;\n\t}\n\t\n\t/// <summary>\n\t/// Calls <see cref=\"GetContextAndDocument\"/>, gets its syntax root and finds token.\n\t/// </summary>\n\t/// <param name=\"position\">If -1, gets current position. If -2, gets selection start.</param>\n\t/// <param name=\"metaToo\">Don't return false if position is in meta comments.</param>\n\tpublic static bool GetDocumentAndFindToken(out Context r, out SyntaxToken token, int position = -1, bool metaToo = false, bool findInsideTrivia = false) {\n\t\tif (!GetContextAndDocument(out r, position, metaToo)) { token = default; return false; }\n\t\ttoken = r.syntaxRoot.FindToken(r.pos, findInsideTrivia);\n\t\treturn true;\n\t}\n\t\n\tpublic static CiWorkspace CurrentWorkspace { get; private set; }\n\t\n\tstatic void _CreateWorkspace(SciCode sci) {\n\t\t//TODO3: use same workspace if project/solution not changed.\n\t\t//\tHere \"solution\" means when a project or file uses project references.\n\t\t//\tNow eg slow GetSemanticModelAsync when [re]opening a file in a large project/solution.\n\t\t\n\t\t_diag.ClearMetaErrors();\n\t\t\n\t\tvar fn = sci.EFile;\n\t\tvar ws = CurrentWorkspace = new(fn, sci.EIsWpfPreview ? CiWorkspace.Caller.CodeInfoWpfPreview : CiWorkspace.Caller.CodeInfoNormal);\n\t\t_solution = ws.Solution;\n\t\t_projectId = ws.Projects[0].projectId;\n\t\t_documentId = ws.GetDocId(fn);\n\t}\n\t\n\tprivate static void _Timer025sWhenVisible() {\n\t\tvar doc = Panels.Editor.ActiveDoc;\n\t\tif (!_CanWork(doc)) {\n\t\t\tPanels.Outline.Clear();\n\t\t\treturn;\n\t\t}\n\t\t\n\t\t//cancel if changed the screen rectangle of the document window\n\t\tif (_compl.IsVisibleUI || _signature.IsVisibleUI || _tpVisible) {\n\t\t\tvar r = Panels.Editor.ActiveDoc.AaWnd.Rect;\n\t\t\tif (!_isUI) {\n\t\t\t\t_isUI = true;\n\t\t\t\t_sciRect = r;\n\t\t\t} else if (r != _sciRect) { //moved/resized top-level window or eg moved some splitter\n\t\t\t\t_isUI = false;\n\t\t\t\tCancel();\n\t\t\t}\n\t\t} else if (_isUI) {\n\t\t\t_isUI = false;\n\t\t}\n\t\t\n\t\t_styling.Timer250msWhenVisibleAndWarm(doc);\n\t\tPanels.Outline.Timer025sWhenVisible();\n\t}\n\t\n\tstatic CiPopupText _textPopup;\n\tstatic bool _tpVisible;\n\t\n\tstatic void _ShowTextPopup(SciCode doc, int pos16, System.Windows.Documents.Section text, Action<CiPopupText, string> onLinkClick = null) {\n\t\t_textPopup ??= new CiPopupText(CiPopupText.UsedBy.Info, onHiddenOrDestroyed: (_, _) => _tpVisible = false);\n\t\t_textPopup.Text = text;\n\t\t_textPopup.OnLinkClick = onLinkClick;\n\t\t_textPopup.Show(doc, pos16, hideIfOutside: true);\n\t\t_tpVisible = true;\n\t}\n\t\n\t//CONSIDER: option to show tooltip: below mouse (like now), above mouse, top/bottom (which is farther), maybe above Output etc.\n\t//\tThis test version shows above Output.\n\t//static void _ShowTextPopup(SciCode doc, int pos16, System.Windows.Documents.Section text, Action<CiPopupText, string> onLinkClick = null) {\n\t//\t_textPopup ??= new CiPopupText(CiPopupText.UsedBy.Info, onHiddenOrDestroyed: (_, _) => _tpVisible = false);\n\t//\t_textPopup.Text = text;\n\t//\t_textPopup.OnLinkClick = onLinkClick;\n\t//\tif (keys.isScrollLock && Panels.Output.IsVisible) {\n\t//\t\tvar r = Panels.Output.RectInScreen();\n\t//\t\t_textPopup.Show(doc, r, null);\n\t//\t} else {\n\t//\t\t_textPopup.Show(doc, pos16, hideIfOutside: true);\n\t//\t}\n\t//\t_tpVisible = true;\n\t//}\n\t\n\tinternal static bool HideTextPopup() {\n\t\tif (_tpVisible) { _textPopup.Hide(); return true; }\n\t\treturn false;\n\t}\n\t\n\tinternal static void HideTextPopupAndTempWindows() {\n\t\tHideTextPopup();\n\t\t_tools.HideTempWindows();\n\t}\n\t\n\tpublic class CharContext : IDisposable {\n\t\tpublic readonly SciCode doc;\n\t\tpublic char ch;\n\t\t/// <summary>Don't show completions, signature, etc.</summary>\n\t\tpublic bool ignoreChar;\n\t\t//bool _undoStarted;\n\t\t\n\t\tpublic CharContext(SciCode doc, char ch) {\n\t\t\tthis.doc = doc;\n\t\t\tthis.ch = ch;\n\t\t}\n\t\t\n\t\t//public void BeginUndoAction()\n\t\t//{\n\t\t//\tif(!_undoStarted) {\n\t\t//\t\t_undoStarted = true;\n\t\t//\t\tdoc.Call(Sci.SCI_BEGINUNDOACTION);\n\t\t//\t}\n\t\t//}\n\t\t\n\t\tpublic void Dispose() {\n\t\t\t//if(_undoStarted) {\n\t\t\t//\t_undoStarted = false;\n\t\t\t//\tdoc.Call(Sci.SCI_ENDUNDOACTION);\n\t\t\t//}\n\t\t}\n\t}\n\t\n\t//public static void Test()\n\t//{\n\t\n\t//}\n}\n"
  },
  {
    "path": "Au.Editor/Edit/EditGoBack.cs",
    "content": "namespace LA;\n\nclass EditGoBack {\n\trecord struct _Location(FileNode fn, int pos);\n\t\n\tList<_Location> _a = new();\n\tint _i = -1;\n\tbool _canGoBack, _canGoForward;\n\tlong _time;\n\t\n\tinternal void OnPosChanged(SciCode doc) {\n\t\t//print.it(\"pos\", doc.aaaCurrentPos8);\n\t\t\n\t\tbool add;\n\t\tint pos = doc.aaaCurrentPos8;\n\t\tvar prev = _a.Count > 0 ? _a[_i] : default;\n\t\tif (prev.fn != doc.EFile) {\n\t\t\tadd = true;\n\t\t} else {\n\t\t\tif (pos == prev.pos) return; //eg on Back/Forward\n\t\t\tif (_recordNext) {\n\t\t\t\t_recordNext = false;\n\t\t\t\tadd = Environment.TickCount64 - _time > 250;\n\t\t\t} else {\n\t\t\t\tadd = Environment.TickCount64 - _time > 500\n\t\t\t\t\t&& Math.Abs(doc.aaaLineFromPos(false, pos) - doc.aaaLineFromPos(false, prev.pos)) >= 10;\n\t\t\t}\n\t\t}\n\t\t\n\t\tvar now = new _Location(doc.EFile, pos);\n\t\tif (add) {\n\t\t\tif (++_i < _a.Count) _a.RemoveRange(_i, _a.Count - _i); //after GoBack\n\t\t\telse if (_a.Count == 256) _a.RemoveAt(0);\n\t\t\t_i = _a.Count;\n\t\t\t_a.Add(now);\n\t\t} else _a[_i] = now;\n\t\t\n\t\t_time = Environment.TickCount64;\n\t\t_UpdateUI();\n\t\t\n\t\t//timer.after(1000, _ => {\n\t\t//\tprint.it(\"----\");\n\t\t//\tprint.it(_a);\n\t\t//});\n\t}\n\t\n\tinternal void SciModified(SciCode doc, bool deleted, int pos, int len) {\n\t\t//print.it(\"mod\", deleted, pos, len);\n\t\t_time = 0;\n\t\tif (_i < 0) return; //probably impossible, but anyway\n\t\tif (deleted) len = -len;\n\t\tvar fn = doc.EFile;\n\t\tfor (int i = _a.Count; --i >= 0;) {\n\t\t\tif (_a[i].fn == fn && _a[i].pos > pos) {\n\t\t\t\t_a[i] = new(fn, Math.Max(pos, _a[i].pos + len));\n\t\t\t}\n\t\t}\n\t\tif (deleted) { //remove adjacent duplicates\n\t\t\tfor (int i = _a.Count - 1; --i >= 0;) {\n\t\t\t\tif (_a[i].fn == fn && _a[i].pos == pos && _a[i + 1].fn == fn && _a[i + 1].pos == pos) {\n\t\t\t\t\t_a.RemoveAt(i);\n\t\t\t\t\tif (i <= _i) _i--;\n\t\t\t\t}\n\t\t\t}\n\t\t\t//for (int i = 0; i < _a.Count; i++) print.it(_a[i].pos);\n\t\t\t_UpdateUI();\n\t\t}\n\t}\n\t\n\t//when file modified externally. Currently not used.\n\t//internal void OnTextReplaced(FileNode fn) {\n\t//\t//print.it(\"replaced\", fn);\n\t//\t_time = 0;\n\t//\tif (_i < 0) return;\n\t//\tint iFirst = _a.FindIndex(o => o.fn == fn); if (iFirst < 0) return;\n\t//\t_a[iFirst] = new(fn, 0);\n\t//\tfor (int i = _a.Count; --i > iFirst;) {\n\t//\t\tif (_a[i].fn == fn) {\n\t//\t\t\t_a.RemoveAt(i);\n\t//\t\t\tif (i <= _i) _i--;\n\t//\t\t}\n\t//\t}\n\t//\t_UpdateUI();\n\t//}\n\t\n\tinternal void OnRestoringSavedPos() {\n\t\t_time = Environment.TickCount64;\n\t}\n\t\n\tinternal void OnFileDeleted(IEnumerable<FileNode> e) {\n\t\tbool removed = false;\n\t\tforeach (var f in e) {\n\t\t\tif (f.IsFolder) continue;\n\t\t\t//print.it($\"before: count={_a.Count}, _i={_i}, file={f}, _a={_a:print}\");\n\t\t\tfor (int i = _a.Count; --i >= 0;) {\n\t\t\t\tif (_a[i].fn == f) {\n\t\t\t\t\t_a.RemoveAt(i);\n\t\t\t\t\tif (i <= _i) _i--;\n\t\t\t\t\t//print.it(i, _i);\n\t\t\t\t\tremoved = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (removed) {\n\t\t\tif (_i > 0 && _a[_i] == _a[_i - 1]) _a.RemoveAt(_i--);\n\t\t\t//print.it($\"after:  count={_a.Count}, _i={_i}, _a={_a:print}\");\n\t\t\t_time = 0;\n\t\t\t_UpdateUI();\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Record next position change event, even if the position is near.\n\t/// </summary>\n\tpublic void RecordNext() { _recordNext = true; }\n\tbool _recordNext;\n\t\n\tpublic void GoBack() {\n\t\tif (_i < 1) return;\n\t\t_i--;\n\t\t_GoTo();\n\t}\n\t\n\tpublic void GoForward() {\n\t\tif (_i > _a.Count - 2) return;\n\t\t_i++;\n\t\t_GoTo();\n\t}\n\t\n\tvoid _GoTo() {\n\t\tvar v = _a[_i];\n\t\t_UpdateUI();\n\t\tif (!App.Model.SetCurrentFile(v.fn)) return;\n\t\tvar doc = Panels.Editor.ActiveDoc;\n\t\tdoc.aaaGoToPos(false, v.pos);\n\t\tdoc.Focus();\n\t}\n\t\n\tvoid _UpdateUI() {\n\t\tbool canGoBack = _i > 0, canGoForward = _i < _a.Count - 1;\n\t\tif (canGoBack != _canGoBack) App.Commands[nameof(Menus.Edit.Navigate.Go_back)].Enable(_canGoBack = canGoBack);\n\t\tif (canGoForward != _canGoForward) App.Commands[nameof(Menus.Edit.Navigate.Go_forward)].Enable(_canGoForward = canGoForward);\n\t}\n\t\n\tinternal static void DisableUI() {\n\t\tif (App.Commands == null) return;\n\t\tApp.Commands[nameof(Menus.Edit.Navigate.Go_back)].Enable(false);\n\t\tApp.Commands[nameof(Menus.Edit.Navigate.Go_forward)].Enable(false);\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Edit/GenerateCode.cs",
    "content": "extern alias CAW;\n\nusing Au.Controls;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Collections.Immutable;\n\nusing Microsoft.CodeAnalysis;\nusing CAW::Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp;\nusing Microsoft.CodeAnalysis.CSharp.Syntax;\nusing Microsoft.CodeAnalysis.Text;\nusing Microsoft.CodeAnalysis.CSharp.Extensions;\nusing Microsoft.CodeAnalysis.Shared.Extensions;\nusing CAW::Microsoft.CodeAnalysis.Shared.Extensions;\nusing CAW::Microsoft.CodeAnalysis.Rename;\nusing acc = Microsoft.CodeAnalysis.Accessibility;\n\nnamespace LA;\n\n/// <summary>\n/// Inserts various code in code editor. With correct indent etc.\n/// Some functions can insert in other controls too.\n/// </summary>\nstatic class GenerateCode {\n\t/// <summary>\n\t/// Called from CiCompletion._ShowList on char '/'. If need, inserts XML doc comment with empty summary, param and returns tags.\n\t/// </summary>\n\tpublic static void DocComment(CodeInfo.Context cd) {\n\t\tint pos = cd.pos;\n\t\tstring code = cd.code;\n\t\tSciCode doc = cd.sci;\n\t\t\n\t\tif (0 == code.Eq(pos - 3, false, \"///\\r\", \"///\\n\") || !CiUtil.IsLineStart(code, pos - 3)) return;\n\t\t\n\t\tvar root = cd.syntaxRoot;\n\t\tvar node = root.FindToken(pos).Parent;\n\t\tvar start = node.SpanStart;\n\t\tif (start < pos) return;\n\t\t\n\t\tnode = node.GetAncestorOrThis<MemberDeclarationSyntax>();\n\t\tif (node is null or GlobalStatementSyntax or ExtensionBlockDeclarationSyntax || node.SpanStart != start) return;\n\t\t\n\t\t//already has doc comment?\n\t\tforeach (var v in node.GetLeadingTrivia()) {\n\t\t\tif (v.IsDocumentationCommentTrivia) { //singleline (preceded by ///) or multiline (preceded by /**)\n\t\t\t\tvar span = v.Span;\n\t\t\t\tif (span.Start != pos || span.Length > 2) return; //when single ///, span includes only newline after ///\n\t\t\t}\n\t\t}\n\t\t//print.it(pos);\n\t\t//CiUtil.PrintNode(node);\n\t\t\n\t\tstring s = @\" <summary>\n/// \n/// </summary>\";\n\t\tBaseParameterListSyntax pl = node switch {\n\t\t\tBaseMethodDeclarationSyntax k => k.ParameterList,\n\t\t\tTypeDeclarationSyntax k => k.ParameterList,\n\t\t\tIndexerDeclarationSyntax k => k.ParameterList,\n\t\t\tDelegateDeclarationSyntax k => k.ParameterList,\n\t\t\t_ => null\n\t\t};\n\t\tif (pl != null) {\n\t\t\tvar b = new StringBuilder(s);\n\t\t\tforeach (var p in pl.Parameters) {\n\t\t\t\tb.Append(\"\\r\\n/// <param name=\\\"\").Append(p.Identifier.Text).Append(\"\\\"></param>\");\n\t\t\t}\n\t\t\tif ((node is MethodDeclarationSyntax met2 && !code.Eq(met2.ReturnType.Span, \"void\"))\n\t\t\t\t|| node is IndexerDeclarationSyntax\n\t\t\t\t|| (node is DelegateDeclarationSyntax del2 && !code.Eq(del2.ReturnType.Span, \"void\"))\n\t\t\t\t) b.Append(\"\\r\\n/// <returns></returns>\");\n\t\t\t\n\t\t\ts = b.ToString();\n\t\t\t//rejected: <typeparam name=\"TT\"></typeparam>. Rarely used.\n\t\t}\n\t\t\n\t\ts = CiUtil.IndentStringForInsertSimple(s, doc, pos);\n\t\t\n\t\tdoc.aaaInsertText(true, pos, s, true, true);\n\t\tdoc.aaaGoToPos(true, pos + s.Find(\"/ \") + 2);\n\t}\n\t\n\t/// <summary>\n\t/// Prints delegate code. Does not insert.\n\t/// </summary>\n\tpublic static void CreateDelegate() {\n\t\tif (!_CreateDelegate()) print.it(\"To create delegate code, the text cursor must be where a delegate can be used, for example after 'Event+=' or in a function argument list.\");\n\t}\n\t\n\tstatic bool _CreateDelegate() {\n\t\t//FUTURE: show tool to insert code, not just print.\n\t\t//\tWith name edit field. Options to insert as method or local func. Option to use `#region events`. Option to add `print.it(\"EventName\");`.\n\t\t\n\t\tif (!CodeInfo.GetContextAndDocument(out var cd)) return false;\n\t\tint pos = cd.pos;\n\t\tvar semo = cd.semanticModel;\n\t\tvar token = cd.syntaxRoot.FindTokenOnLeftOfPosition(cd.pos);\n\t\tSyntaxNode node = token.Parent, node0 = node;\n\t\t\n\t\tfor (; node != null; node = node.Parent) {\n\t\t\tif (node is AssignmentExpressionSyntax aes) {\n\t\t\t\tif (node.Kind() is SyntaxKind.SimpleAssignmentExpression or SyntaxKind.AddAssignmentExpression or SyntaxKind.SubtractAssignmentExpression)\n\t\t\t\t\treturn _GetTypeAndFormat(aes.Left, aes);\n\t\t\t} else if (node is BaseArgumentListSyntax als) {\n\t\t\t\tif (!node.Span.ContainsInside(pos)) continue;\n\t\t\t\tif (CiUtil.GetArgumentParameterFromPos(als, pos, semo, out _, out var aps, o => _GetDelegateType(o.Type) != null)) {\n\t\t\t\t\tbool ok = false;\n\t\t\t\t\tforeach (var ps in aps) {\n\t\t\t\t\t\tok |= _Format(ps.Type);\n\t\t\t\t\t}\n\t\t\t\t\treturn ok;\n\t\t\t\t\t\n\t\t\t\t\t//rejected: support generic, like `void M12<T>(T a) where T: Delegate`\n\t\t\t\t}\n\t\t\t} else if (node is VariableDeclarationSyntax vds) {\n\t\t\t\treturn _GetTypeAndFormat(vds.Type);\n\t\t\t} else if (node is ReturnStatementSyntax rss) {\n\t\t\t\treturn _GetTypeAndFormat(rss.GetAncestor<MethodDeclarationSyntax>()?.ReturnType);\n\t\t\t} else if (node is ArrowExpressionClauseSyntax ae) {\n\t\t\t\tswitch (node.Parent) {\n\t\t\t\tcase MethodDeclarationSyntax m: return _GetTypeAndFormat(m.ReturnType);\n\t\t\t\tcase PropertyDeclarationSyntax p: return _GetTypeAndFormat(p.Type);\n\t\t\t\t}\n\t\t\t} else if (node is LambdaExpressionSyntax les && node == node0 && token.IsKind(SyntaxKind.EqualsGreaterThanToken)) { //maybe the lambda returns a delegate\n\t\t\t\tif (semo.GetTypeInfo(les).ConvertedType is INamedTypeSymbol { DelegateInvokeMethod: { } dim }) return _Format(dim.ReturnType);\n\t\t\t} else if (node is StatementSyntax or AnonymousFunctionExpressionSyntax) {\n\t\t\t} else continue;\n\t\t\tbreak;\n\t\t}\n\t\t\n\t\treturn false;\n\t\t\n\t\tstatic INamedTypeSymbol _GetDelegateType(ITypeSymbol t) {\n\t\t\tif (t.TypeKind == TypeKind.Delegate || t.SpecialType == SpecialType.System_Delegate) return t as INamedTypeSymbol;\n\t\t\tif (t is IArrayTypeSymbol ats) return _GetDelegateType(ats.ElementType);\n\t\t\tif (t is INamedTypeSymbol { Arity: 1 } nt && t.CanBeEnumerated()) return _GetDelegateType(nt.TypeArguments[0]);\n\t\t\treturn null;\n\t\t}\n\t\t\n\t\tbool _GetTypeAndFormat(SyntaxNode sn, AssignmentExpressionSyntax aes = null) {\n\t\t\tif (sn == null) return false;\n\t\t\treturn _Format(semo.GetTypeInfo(sn).Type, aes);\n\t\t}\n\t\t\n\t\tbool _Format(ITypeSymbol type, AssignmentExpressionSyntax aes = null) {\n\t\t\tif (_GetDelegateType(type) is not INamedTypeSymbol t || t is IErrorTypeSymbol) return false;\n\t\t\tif (t.TypeKind != TypeKind.Delegate) {\n\t\t\t\tif (t.SpecialType == SpecialType.System_Delegate && t.ContainingAssembly.GetTypeByMetadataName(\"System.Action\") is { } tsa) t = tsa;\n\t\t\t\telse return false;\n\t\t\t}\n\t\t\tvar b = new StringBuilder(\"<><lc #A0C0A0>Delegate method and lambda<>\\r\\n<code>\");\n\t\t\tstring methodName = \"_RenameMe\";\n\t\t\tif (aes != null) {\n\t\t\t\tif (aes.Left is IdentifierNameSyntax ins) {\n\t\t\t\t\tmethodName = $\"_{ins.Identifier.Text}\";\n\t\t\t\t} else if (aes.Left is MemberAccessExpressionSyntax maes) {\n\t\t\t\t\tif (maes.Expression is IdentifierNameSyntax ins2) {\n\t\t\t\t\t\tmethodName = $\"_{ins2.Identifier.Text}_{maes.Name.Identifier.Text}\";\n\t\t\t\t\t} else {\n\t\t\t\t\t\tmethodName = $\"_{maes.Name.Identifier.Text}\";\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (methodName.Starts(\"__\")) methodName = methodName[1..];\n\t\t\t}\n\t\t\t_FormatDelegete(b, null, t, methodName, semo, pos);\n\t\t\tb.AppendLine(\"</code>\");\n\t\t\tprint.it(b);\n\t\t\treturn true;\n\t\t}\n\t}\n\t\n\tstatic void _FormatDelegete(StringBuilder b, bool? lambda, INamedTypeSymbol t, string methodName, SemanticModel semo, int pos, string printEvent = null) {\n\t\t_sdf1 ??= new SymbolDisplayFormat(\n\t\t\tgenericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,\n\t\t\tmemberOptions: SymbolDisplayMemberOptions.IncludeParameters | SymbolDisplayMemberOptions.IncludeType | SymbolDisplayMemberOptions.IncludeRef,\n\t\t\tmiscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes | SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers,\n\t\t\tparameterOptions: SymbolDisplayParameterOptions.IncludeType | SymbolDisplayParameterOptions.IncludeName | SymbolDisplayParameterOptions.IncludeParamsRefOut | SymbolDisplayParameterOptions.IncludeDefaultValue\n\t\t\t);\n\t\t\n\t\tvar m = t.DelegateInvokeMethod;\n\t\t\n\t\tif (lambda != true) {\n\t\t\tb.Append(m.ToMinimalDisplayString(semo, pos, _sdf1));\n\t\t\tb.Replace(\" Invoke(\", $\" {methodName}(\");\n\t\t\t_AppendBody(false);\n\t\t}\n\t\t\n\t\tif (lambda != false) {\n\t\t\tvar s = \"()\";\n\t\t\tif (m.Parameters.Any()) {\n\t\t\t\tvar format = _sdf1.WithMemberOptions(SymbolDisplayMemberOptions.IncludeParameters);\n\t\t\t\tbool withTypes = m.Parameters.Any(o => o.RefKind != 0 || o.IsParams);\n\t\t\t\tif (withTypes) format = format.RemoveParameterOptions(SymbolDisplayParameterOptions.IncludeDefaultValue);\n\t\t\t\telse format = format.WithParameterOptions(SymbolDisplayParameterOptions.IncludeName);\n\t\t\t\ts = m.ToMinimalDisplayString(semo, pos, format);\n\t\t\t\tif (m.Parameters.Length == 1 && !withTypes) s = s[7..^1]; else s = s[6..]; //remove 'Invoke' and maybe '()'\n\t\t\t}\n\t\t\tb.Append(s).Append(\" =>\");\n\t\t\tif (lambda == true) _AppendBody(true); else b.AppendLine(\" \");\n\t\t}\n\t\t\n\t\tvoid _AppendBody(bool oneLine) {\n\t\t\tb.Append(oneLine ? \" { \" : \" {\\r\\n\\t\");\n\t\t\tif (printEvent != null) b.AppendFormat(\"_PrintEvent(\\\"{0}\\\");\", printEvent).Append(oneLine ? \" \" : \"\\r\\n\\t\");\n\t\t\tif (!m.ReturnsVoid) b.Append(\"return default;\"); //never mind ref return\n\t\t\tb.AppendLine(oneLine ? \" };\" : \"\\r\\n}\");\n\t\t}\n\t}\n\tstatic SymbolDisplayFormat _sdf1;\n\t\n\tpublic static void CreateEventHandlers() {\n\t\tINamedTypeSymbol type = null;\n\t\tstring varName = null;\n\t\tbool staticEvents = false;\n\t\tvar (sym, cd) = CiUtil.GetSymbolFromPos(preferVar: true);\n\t\tif (sym != null) {\n\t\t\tif (sym is INamedTypeSymbol nts) {\n\t\t\t\ttype = nts;\n\t\t\t\tvarName = type.Name;\n\t\t\t\tstaticEvents = true;\n\t\t\t} else if (sym is ILocalSymbol or IParameterSymbol or IFieldSymbol or IPropertySymbol) {\n\t\t\t\ttype = sym.GetSymbolType() as INamedTypeSymbol;\n\t\t\t\tvarName = sym.Name;\n\t\t\t}\n\t\t}\n\t\tif (type == null) {\n\t\t\tdialog.showInfo(\"Create event handlers\", \"Please click or right-click a variable or type name in code.\\nThen retry.\\nIf type name, prints static events, else non-static.\");\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tstring typeString = type.ToString(); //X or X<T>\n\t\tbool lambda = false;\n\t\tint button = dialog.show(\"Create event handlers\", $\"Type: {typeString}\", \"1 Methods|2 Lambdas|0 Cancel\", flags: DFlags.CommandLinks, owner: App.Hmain);\n\t\tswitch (button) { case 1: break; case 2: lambda = true; break; default: return; }\n\t\t\n\t\tbool found = false, found2 = false;\n\t\tStringBuilder b1 = new(\"<><lc #A0C0A0>Event handlers. The text is in the clipboard.<>\\r\\n<code>\"), b2 = new();\n\t\tstring prefix = (varName[0] != '_' ? \"_\" : null) + varName + \"_\";\n\t\tforeach (var type2 in type.TypeKind is TypeKind.Interface ? type.GetAllInterfacesIncludingThis() : type.GetBaseTypesAndThis().SkipLast(1)) {\n\t\t\tbool once = false;\n\t\t\tforeach (var v in type2.GetMembers()) {\n\t\t\t\tif (v is IEventSymbol es) {\n\t\t\t\t\tif (es.IsStatic == staticEvents) {\n\t\t\t\t\t\tvar funcName = prefix + es.Name;\n\t\t\t\t\t\tif (!once) {\n\t\t\t\t\t\t\tvar s1 = $\"//{type2} events\";\n\t\t\t\t\t\t\tif (found) b1.AppendLine();\n\t\t\t\t\t\t\tb1.AppendLine(s1);\n\t\t\t\t\t\t\tif (!lambda) b2.AppendLine().AppendLine(s1);\n\t\t\t\t\t\t\tonce = found = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tb1.Append($\"{(staticEvents ? typeString : varName)}.{es.Name} += \");\n\t\t\t\t\t\tif (!lambda) {\n\t\t\t\t\t\t\tb1.AppendLine($\"{funcName};\");\n\t\t\t\t\t\t\tb2.AppendLine();\n\t\t\t\t\t\t}\n\t\t\t\t\t\t_FormatDelegete(lambda ? b1 : b2, lambda, es.Type as INamedTypeSymbol, funcName, cd.semanticModel, cd.pos, es.Name);\n\t\t\t\t\t} else found2 = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (!found) {\n\t\t\tprint.it(found2\n\t\t\t\t? $\"Type {typeString} does not have {(staticEvents ? \"static\" : \"non-static\")} events.\"\n\t\t\t\t: $\"Type {typeString} does not have events.\");\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tb1.Append(b2).Append(\"\\r\\n[Conditional(\\\"DEBUG\\\")]\\r\\nstatic void _PrintEvent(string s) { print.it($\\\"<><c green>event <_>{s}</_><>\\\"); }\\r\\n</code>\");\n\t\tvar text = b1.ToString();\n\t\tprint.it(text);\n\t\tclipboard.text = text[(text.Find(\"<code>\") + 6)..^7];\n\t}\n\t\n\tpublic static void CreateOverrides() {\n\t\tif (!CodeInfo.GetContextAndDocument(out var cd)) return;\n\t\tvar semo = cd.semanticModel;\n\t\tvar type = semo.GetEnclosingNamedType2(cd.pos, out _, out var declNode);\n\t\tif (type == null || type.TypeKind != TypeKind.Class) return;\n\t\tint pos2 = declNode.CloseBraceToken.Span.Start;\n\t\tvar b = new StringBuilder(\"<><lc #A0C0A0>Overrides. The text is in the clipboard.<>\\r\\n<code>\");\n\t\tvar format = CiText.s_symbolFullFormat.WithParameterOptions(CiText.s_symbolFullFormat.ParameterOptions & ~SymbolDisplayParameterOptions.IncludeOptionalBrackets);\n\t\tbool found = false;\n\t\tfor (var type2 = type.BaseType; type2?.BaseType != null; type2 = type2.BaseType) { //base types except object\n\t\t\t//print.it(type2);\n\t\t\tbool once = false;\n\t\t\tforeach (var v in type2.GetMembers()) {\n\t\t\t\tif ((v.IsVirtual || v.IsAbstract) && (v is IMethodSymbol { MethodKind: MethodKind.Ordinary } || v is IPropertySymbol)) {\n\t\t\t\t\tif (!once) {\n\t\t\t\t\t\tif (found) b.AppendLine();\n\t\t\t\t\t\tb.AppendLine($\"//{type2} overrides\");\n\t\t\t\t\t\tonce = found = true;\n\t\t\t\t\t}\n\t\t\t\t\tb.AppendLine().Append(_AccToString(v)).Append(\" override \");\n\t\t\t\t\tvar s = v.ToMinimalDisplayString(semo, pos2, format);\n\t\t\t\t\tif (v is IMethodSymbol ims) {\n\t\t\t\t\t\tb.Append(s);\n\t\t\t\t\t\tb.AppendLine(\" {\\r\\n\\t_PrintFunc();\\r\\n\\t\");\n\t\t\t\t\t\tif (v.IsAbstract) {\n\t\t\t\t\t\t\tif (!ims.ReturnsVoid) b.AppendLine(\"\\treturn default;\");\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tb.AppendLine($\"\\t{(ims.ReturnsVoid ? \"\" : \"return \")}base.{v.Name}({string.Join(\", \", ims.Parameters.Select(o => o.Name))});\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\tb.AppendLine(\"}\");\n\t\t\t\t\t} else if (v is IPropertySymbol ips) {\n\t\t\t\t\t\tif (v.IsAbstract) {\n\t\t\t\t\t\t\tif (!ips.IsWriteOnly) s = s.Replace(\" get;\", $\"\\r\\n\\tget {{ _PrintFunc($\\\"get {v.Name}\\\");  return default; }}\\r\\n\");\n\t\t\t\t\t\t\tif (!ips.IsReadOnly) s = s.Replace(\" set;\", $\"\\r\\n\\tset {{ _PrintFunc($\\\"set {v.Name}\\\");  }}\\r\\n\");\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tif (!ips.IsWriteOnly) s = s.Replace(\" get;\", $\"\\r\\n\\tget {{ _PrintFunc($\\\"get {v.Name}\\\");  return base.{v.Name}; }}\\r\\n\");\n\t\t\t\t\t\t\tif (!ips.IsReadOnly) s = s.Replace(\" set;\", $\"\\r\\n\\tset {{ _PrintFunc($\\\"set {v.Name}\\\");  base.{v.Name} = value; }}\\r\\n\");\n\t\t\t\t\t\t\tif (ips.IsIndexer) s = s.Replace(\" base.this[]\", $\" base[{string.Join(\", \", ips.Parameters.Select(o => o.Name))}]\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\ts = s.Replace(\"\\n }\", \"\\n}\");\n\t\t\t\t\t\tb.AppendLine(s);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (!found) {\n\t\t\tprint.it($\"Class {type} does not have a base class containing virtual or abstract functions.\");\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tb.Append(\"\\r\\n[Conditional(\\\"DEBUG\\\")]\\r\\nstatic void _PrintFunc([CallerMemberName] string m_ = null) { print.it($\\\"<><c green>override <_>{m_}</_><>\\\"); }\\r\\n</code>\");\n\t\tvar text = b.ToString();\n\t\tprint.it(text);\n\t\tclipboard.text = text[(text.Find(\"<code>\") + 6)..^7];\n\t}\n\t\n\tstatic string _AccToString(ISymbol v) => v.DeclaredAccessibility switch { acc.Public => \"public\", acc.Internal => \"internal\", acc.Protected => \"protected\", acc.ProtectedOrInternal => \"protected internal\", acc.ProtectedAndInternal => \"private protected\", _ => \"\" };\n\t\n\tpublic static void ImplementInterfaceOrAbstractClass(int position = -1) {\n\t\t//note: ignores interface members that have default implementation. The same as in VS. To add such a member, type like `IExample.` and select from the list; it creates member code.\n\t\t\n\t\tif (!CodeInfo.GetContextAndDocument(out var cd, position)) return;\n\t\tvar semo = cd.semanticModel;\n\t\t\n\t\tvar thisType = semo.GetEnclosingNamedType2(cd.pos, out var node, out var declNode);\n\t\tif (thisType == null || thisType.TypeKind is not (TypeKind.Class or TypeKind.Struct)) return;\n\t\t\n\t\tvar baseClass = thisType.BaseType.IsAbstract ? thisType.BaseType : null;\n\t\tif (baseClass == null && thisType.Interfaces.IsDefaultOrEmpty) return;\n\t\t\n\t\tvar baseFromPos = position < 0 && node.GetAncestorOrThis<BaseTypeSyntax>() is BaseTypeSyntax bts ? semo.GetTypeInfo(bts.Type).Type as INamedTypeSymbol : null;\n\t\t\n\t\tList<(INamedTypeSymbol type, List<ISymbol> members)> types = new();\n\t\tbool implementInterfaces = false;\n\t\tif (baseFromPos != null) {\n\t\t\t_GetOfType(baseFromPos);\n\t\t\t_Interfaces(baseFromPos);\n\t\t} else {\n\t\t\tif (baseClass != null) _GetOfType(baseClass);\n\t\t\t_Interfaces(thisType);\n\t\t}\n\t\t\n\t\tvoid _Interfaces(INamedTypeSymbol t) { //gets direct base interfases and their all base interfaces. Unlike AllInterfaces, skips base interfaces of base classes.\n\t\t\tforeach (var v in t.Interfaces) {\n\t\t\t\t_GetOfType(v);\n\t\t\t\t_Interfaces(v);\n\t\t\t}\n\t\t}\n\t\t\n\t\t//note: GetAllUnimplementedMembersInThis gets not all members. Eg for ITreeViewItem skips properties that have default impl (but includes such methods). Or gets some garbage.\n\t\tvoid _GetOfType(INamedTypeSymbol t) {\n\t\t\tList<ISymbol> members = null;\n\t\t\tbool isInterface = t.TypeKind == TypeKind.Interface;\n\t\t\tforeach (var m in t.GetMembers()) {\n\t\t\t\tif (!m.IsAbstract) if (!isInterface || m.IsStatic || m.DeclaredAccessibility == acc.Private) continue;\n\t\t\t\tif (m is not (IMethodSymbol { MethodKind: MethodKind.Ordinary or MethodKind.UserDefinedOperator or MethodKind.Conversion } or IPropertySymbol or IEventSymbol)) continue;\n\t\t\t\tISymbol k = isInterface ? thisType.FindImplementationForInterfaceMember(m) : thisType.FindImplementationForAbstractMember(m);\n\t\t\t\t//if (k != null && k.ContainingType != thisType) k = null;\n\t\t\t\tif (k == null) {\n\t\t\t\t\tif (members == null) types.Add((t, members = new()));\n\t\t\t\t\tmembers.Add(m);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (members != null) {\n\t\t\t\tmembers.Sort((x, y) => (y.IsAbstract ? 1 : 0) - (x.IsAbstract ? 1 : 0));\n\t\t\t\timplementInterfaces |= isInterface;\n\t\t\t}\n\t\t\t\n\t\t\tif (!isInterface && t.BaseType.IsAbstract) _GetOfType(t.BaseType);\n\t\t}\n\t\t\n\t\tif (types.Count == 0) {\n\t\t\tdialog.show(\"Found 0 unimplemented members\", owner: App.Hmain);\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tbool explicitly;\n\t\tstring buttons = implementInterfaces\n\t\t\t? \"1 Implement\\nLike 'public Type Member'|2 Implement explicitly\\nLike 'Type Interface.Member'|0 Cancel\"\n\t\t\t: \"1 Implement|0 Cancel\";\n\t\tswitch (dialog.show(\"Implement\", \"This will add members that were not implemented.\", buttons, flags: DFlags.CommandLinks, owner: App.Hmain)) {\n\t\tcase 1: explicitly = false; break;\n\t\tcase 2: explicitly = true; break;\n\t\tdefault: return;\n\t\t}\n\t\t\n\t\tposition = declNode.CloseBraceToken.Span.Start;\n\t\twhile (cd.code[position - 1] is ' ' or '\\t') position--;\n\t\t\n\t\tvar b = new StringBuilder();\n\t\tvar format = CiText.s_symbolFullFormat.WithParameterOptions(CiText.s_symbolFullFormat.ParameterOptions & ~SymbolDisplayParameterOptions.IncludeOptionalBrackets);\n\t\tvar formatExp = format.WithMemberOptions(SymbolDisplayMemberOptions.IncludeContainingType | SymbolDisplayMemberOptions.IncludeType | SymbolDisplayMemberOptions.IncludeParameters | SymbolDisplayMemberOptions.IncludeRef);\n\t\t\n\t\tvar hsMembers = thisType.MemberNames.ToHashSet(); //nevermind method overloads, it's rare\n\t\t\n\t\tforeach (var (type, members) in types) {\n\t\t\tb.AppendLine().AppendLine(\"//\" + type.ToMinimalDisplayString(semo, position, CiText.s_symbolFullFormat));\n\t\t\t\n\t\t\tbool isInterface = type.TypeKind == TypeKind.Interface;\n\t\t\tforeach (var v in members) {\n\t\t\t\tbool expl = false;\n\t\t\t\tif (isInterface) expl = explicitly || v.DeclaredAccessibility != acc.Public || !hsMembers.Add(v.Name);\n\t\t\t\t\n\t\t\t\tstring append = null;\n\t\t\t\tswitch (v) {\n\t\t\t\tcase IMethodSymbol ims:\n\t\t\t\t\tif (ims.MethodKind == MethodKind.Ordinary) {\n\t\t\t\t\t\tappend = ims.ReturnsVoid ? @\" {\n\n}\" : @\" {\n\n\treturn default;\n}\";\n\t\t\t\t\t} else append = \" => default;\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase IPropertySymbol ips:\n\t\t\t\t\tif (!expl && isInterface) {\n\t\t\t\t\t\tif (ips.GetMethod != null && ips.GetMethod.DeclaredAccessibility != acc.Public) expl = true;\n\t\t\t\t\t\tif (ips.SetMethod != null && ips.SetMethod.DeclaredAccessibility != acc.Public) expl = true;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase IEventSymbol:\n\t\t\t\t\tappend = !expl ? \";\" : @\" {\n\tadd {  }\n\tremove {  }\n}\";\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tb.AppendLine();\n\t\t\t\tif (isInterface) {\n\t\t\t\t\tif (!v.IsAbstract) b.AppendLine(\"//has default implementation\");\n\t\t\t\t\tif (!expl) b.Append(\"public \");\n\t\t\t\t\tif (v.IsStatic) b.Append(\"static \");\n\t\t\t\t} else {\n\t\t\t\t\tb.Append(_AccToString(v));\n\t\t\t\t\tb.Append(\" override \");\n\t\t\t\t}\n\t\t\t\tb.Append(v.ToMinimalDisplayString(semo, position, expl ? formatExp : format))\n\t\t\t\t\t.AppendLine(append);\n\t\t\t}\n\t\t}\n\t\t\n\t\tvar text = b.ToString();\n\t\ttext = text.Replace(\"] { get; set; }\", @\"] {\n\tget { return default; }\n\tset {  }\n}\"); //indexers\n\t\ttext = text.RxReplace(@\"[^\\]] \\{\\K set; \\}\", @\"\n\tset {  }\n}\"); //write-only properties\n\t\t\n\t\ttext = CiUtil.IndentStringForInsertSimple(text, cd.sci, position, true, 1);\n\t\t\n\t\tcd.sci.aaaInsertText(true, position, text, addUndoPointAfter: true);\n\t\tcd.sci.aaaGoToPos(true, position);\n\t\tcd.sci.aaaSelect(true, position + text.Length, position);\n\t\t\n\t\t//tested: Microsoft.CodeAnalysis.CSharp.ImplementInterface.CSharpImplementInterfaceService works but the result is badly formatted (without spaces, etc). Internal, undocumented.\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Edit/InsertCode.cs",
    "content": "extern alias CAW;\n\nusing Microsoft.CodeAnalysis;\nusing CAW::Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp;\nusing Microsoft.CodeAnalysis.CSharp.Syntax;\nusing Microsoft.CodeAnalysis.Text;\nusing Microsoft.CodeAnalysis.CSharp.Extensions;\nusing Microsoft.CodeAnalysis.Shared.Extensions;\nusing CAW::Microsoft.CodeAnalysis.Shared.Extensions;\nusing CAW::Microsoft.CodeAnalysis.Rename;\nusing acc = Microsoft.CodeAnalysis.Accessibility;\n\nusing Au.Controls;\nusing System.Windows;\nusing System.Windows.Controls;\nusing ToolLand;\n\nnamespace LA;\n\n/// <summary>\n/// Inserts various code in code editor. With correct indent etc.\n/// Some functions can insert in other controls too.\n/// </summary>\nstatic class InsertCode {\n\t/// <summary>\n\t/// Parameters for <c>InsertCode.Statements</c>.\n\t/// </summary>\n\t/// <param name=\"s\">Text. The function ignores \"\\r\\n\" at the end. Does nothing if null. If contains '\\n', prepends/appends empty line to separate from surrounding code.</param>\n\t/// <param name=\"goTo\">If text contains <c>`|`</c>, remove it and finally move caret there.</param>\n\t/// <param name=\"activateEditor\">Activate editor window.</param>\n\t/// <param name=\"noFocus\">Don't focus the editor control. Without this flag focuses it if window is active or activated.</param>\n\t/// <param name=\"selectNewCode\"></param>\n\t/// <param name=\"makeVarName1\"></param>\n\t/// <param name=\"renameVars\">Variable names to rename in <i>s</i>.</param>\n\tpublic record InsertCodeParams(string s, bool goTo = false, bool activateEditor = false, bool noFocus = false, bool selectNewCode = false, bool makeVarName1 = false, (string oldName, string newName)[] renameVars = null);\n\t\n\t/// <summary>\n\t/// Inserts one or more statements at current line. With correct position, indent, etc.\n\t/// If editor is null or readonly or not C# file, prints in output.\n\t/// <para>\n\t/// If called in LA tools process or in a non-main LA thread, runs async in the main process/thread.\n\t/// </para>\n\t/// </summary>\n\tpublic static void Statements(InsertCodeParams p) {\n\t\tif (p.s == null) return;\n\t\t\n\t\tif (!process.IsLaMainThread_) {\n\t\t\tif (process.IsLaProcess_) App.Dispatcher.InvokeAsync(() => Statements(p));\n\t\t\telse WndCopyData.Send<char>(ScriptEditor.WndMsg_, 17, System.Text.Json.JsonSerializer.Serialize(p));\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tstring s = p.s;\n\t\tbool separate = s.Contains('\\n');\n\t\t\n\t\tif (!App.Hmain.IsVisible) App.ShowWindow();\n\t\tif (!CodeInfo.GetContextAndDocument(out var k, metaToo: true)) {\n\t\t\tprint.it(s);\n\t\t\treturn;\n\t\t}\n\t\tvar root = k.syntaxRoot;\n\t\tvar code = k.code;\n\t\tvar pos = k.pos;\n\t\tvar token = root.FindToken(pos);\n\t\tvar node = token.Parent;\n\t\t\n\t\t//get the best valid insertion place\n\t\t\n\t\tbool havePos = false;\n\t\tvar last = root.AttributeLists.LastOrDefault() as SyntaxNode\n\t\t\t?? root.Usings.LastOrDefault() as SyntaxNode\n\t\t\t?? root.Externs.LastOrDefault() as SyntaxNode\n\t\t\t?? root.GetDirectives(o => o is DefineDirectiveTriviaSyntax).LastOrDefault();\n\t\tif (last != null) {\n\t\t\tint e1 = last.FullSpan.End;\n\t\t\tif (havePos = pos <= e1) pos = e1;\n\t\t}\n\t\t\n\t\tif (!havePos) {\n\t\t\tvar members = root.Members;\n\t\t\tif (members.Any()) {\n\t\t\t\tvar g = members.LastOrDefault(o => o is GlobalStatementSyntax);\n\t\t\t\tint posAfterTLS = g?.FullSpan.End ?? members.First().FullSpan.Start;\n\t\t\t\t\n\t\t\t\tbool done1 = false;\n\t\t\t\tif (node is BlockSyntax) {\n\t\t\t\t\tdone1 = node.Span.ContainsInside(pos);\n\t\t\t\t} else if (node is MemberDeclarationSyntax) {\n\t\t\t\t\tdone1 = true;\n\t\t\t\t\t//don't use posAfterTLS if before the first type\n\t\t\t\t\tbool here = node == members.FirstOrDefault(o => o is not GlobalStatementSyntax) && pos <= node.SpanStart;\n\t\t\t\t\tpos = Math.Min(pos, here ? node.SpanStart : posAfterTLS);\n\t\t\t\t} else if (node is CompilationUnitSyntax && g != members[^1]) { //after types\n\t\t\t\t\tdone1 = true;\n\t\t\t\t\tpos = Math.Min(pos, posAfterTLS);\n\t\t\t\t}\n\t\t\t\tif (!done1) {\n\t\t\t\t\tfor (; node is not CompilationUnitSyntax; node = node.Parent) {\n\t\t\t\t\t\t//CiUtil.PrintNode(node);\n\t\t\t\t\t\tif (node is StatementSyntax) {\n\t\t\t\t\t\t\tvar pa = node.Parent;\n\t\t\t\t\t\t\tif (node is BlockSyntax && pa is not (BlockSyntax or GlobalStatementSyntax)) continue;\n\t\t\t\t\t\t\tvar span = node.Span;\n\t\t\t\t\t\t\tif (havePos = pos >= span.End && token.IsKind(SyntaxKind.CloseBraceToken)) pos = node.FullSpan.End;\n\t\t\t\t\t\t\telse if (havePos = pos >= span.Start) pos = span.Start;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (node is MemberDeclarationSyntax) {\n\t\t\t\t\t\t\tpos = posAfterTLS;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\thavePos |= pos == posAfterTLS;\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (k.meta.end > 0 && pos <= k.meta.end) {\n\t\t\thavePos = true;\n\t\t\tpos = k.meta.end;\n\t\t\tif (code.Eq(pos, \"\\r\\n\")) pos += 2; else if (code.Eq(pos, '\\n')) pos++;\n\t\t}\n\t\t\n\t\tif (!havePos) { //if in comments or directive or disabled code, move to the start of trivia or line\n\t\t\tvar trivia = root.FindTrivia(pos);\n\t\t\tvar tk = trivia.Kind();\n\t\t\tif (tk is not (SyntaxKind.EndOfLineTrivia or SyntaxKind.None)) {\n\t\t\t\tif (tk == SyntaxKind.DisabledTextTrivia) {\n\t\t\t\t\twhile (pos > 0 && code[pos - 1] != '\\n') pos--;\n\t\t\t\t} else {\n\t\t\t\t\tpos = trivia.FullSpan.Start;\n\t\t\t\t}\n\t\t\t\t//rejected: move to the start of entire #if ... block.\n\t\t\t\t//\tRare, not easy, may be far, and maybe user wants to insert into disabled code.\n\t\t\t}\n\t\t}\n\t\t\n\t\t//rename symbols in s if need\n\t\t\n\t\ttry { Util.RenameNewSymbols(ref s, k, node, pos, p.makeVarName1, p.renameVars); }\n\t\tcatch (Exception e1) { Debug_.Print(e1); }\n\t\t\n\t\t//indent, newlines\n\t\t\n\t\tstring breakLine = null;\n\t\tfor (; pos > 0 && code[pos - 1] != '\\n'; pos--) if (code[pos - 1] is not (' ' or '\\t')) { breakLine = \"\\r\\n\"; break; }\n\t\tint replTo = CiUtil.SkipSpace(code, pos);\n\t\t\n\t\tvar d = k.sci;\n\t\t\n\t\tvar t2 = root.FindToken(pos); if (t2.SpanStart >= pos) t2 = t2.GetPreviousToken();\n\t\tbool afterOpenBrace = t2.IsKind(SyntaxKind.OpenBraceToken);\n\t\tbool beforeCloseBrace = replTo < code.Length && code[replTo] == '}';\n\t\t\n\t\tint indent = d.aaaLineIndentFromPos(true, pos);\n\t\tif (afterOpenBrace && breakLine != null && !(t2.Parent is BlockSyntax bs1 && bs1.Parent is GlobalStatementSyntax)) indent++;\n\t\telse if (beforeCloseBrace) indent++;\n\t\t\n\t\tvar b = new StringBuilder(breakLine);\n\t\tif (separate && !afterOpenBrace && !s.Starts(\"{\\r\\n\") && pos > 0) {\n\t\t\tint nn = 0; for (int i = pos; --i >= 0 && code[i] <= ' ';) if (code[i] == '\\n' && ++nn == 2) break;\n\t\t\tif (nn < 2) b.AppendIndent(indent).AppendLine();\n\t\t}\n\t\t\n\t\tb.AppendCodeWithIndent(s, indent, andNewline: true);\n\t\t\n\t\tif (separate && !s.Ends(\"\\n}\") && replTo < code.Length && code[replTo] is not ('\\r' or '}')) {\n\t\t\tb.AppendIndent(indent).AppendLine();\n\t\t}\n\t\tif (indent > 0) b.AppendIndent(beforeCloseBrace ? indent - 1 : indent);\n\t\ts = b.ToString();\n\t\t\n\t\t//insert\n\t\t\n\t\tint go = -1;\n\t\tif (p.goTo) {\n\t\t\tgo = s.Find(\"`|`\");\n\t\t\tif (go >= 0) s = s.Remove(go, 3);\n\t\t}\n\t\t\n\t\td.aaaSelect(true, pos, replTo);\n\t\tusing (new CodeInfo.PastingEtc(d, silent: true)) {\n\t\t\td.aaaReplaceSel(s);\n\t\t\t\n\t\t\tif (go >= 0) d.aaaGoToPos(true, pos + go);\n\t\t\telse if (p.selectNewCode) d.aaaSelect(true, pos + s.TrimEnd('\\t').Length, pos, true);\n\t\t\t//else if (p.goToStart)) d.aaaGoToPos(true, pos);\n\t\t}\n\t\t\n\t\tvar w = d.AaWnd.Window;\n\t\tif (p.activateEditor) w.ActivateL();\n\t\tif (!p.noFocus && w.IsActive) d.Focus();\n\t}\n\t\n\t/// <summary>\n\t/// Inserts text in code editor at current position, not as new line, replaces selection.\n\t/// If editor is null or readonly, does nothing.\n\t/// </summary>\n\t/// <param name=\"s\">If contains <c>`|`</c>, removes it and moves caret there; must be single line.</param>\n\tpublic static void TextSimply(string s) {\n\t\tDebug.Assert(App.IsMainThread);\n\t\tvar d = Panels.Editor.ActiveDoc;\n\t\tif (d == null || d.aaaIsReadonly) return;\n\t\tTUtil.InsertTextIn(d, s);\n\t}\n\t\n\t/// <summary>\n\t/// Inserts code 'using ns;\\r\\n' in correct place in editor text, unless it is already exists.\n\t/// Returns true if inserted.\n\t/// </summary>\n\t/// <param name=\"ns\">Namespace, eg \"System.Diagnostics\". Can be multiple, separated with semicolon (can be whitespce around).</param>\n\t/// <param name=\"missing\">Don't check whether the usings exist. Caller knows they don't exist.</param>\n\tpublic static bool UsingDirective(string ns, bool missing = false) {\n\t\tDebug.Assert(App.IsMainThread);\n\t\tif (!CodeInfo.GetContextAndDocument(out var k, 0, metaToo: true)) return false;\n\t\tvar namespaces = ns.Split(';', StringSplitOptions.TrimEntries);\n\t\tint i = FindUsingsInsertPos(k, missing ? null : namespaces);\n\t\tif (i < 0) return false;\n\t\t\n\t\tvar b = new StringBuilder();\n\t\tif (i > 0 && k.code[i - 1] != '\\n') b.AppendLine();\n\t\tforeach (var v in namespaces) {\n\t\t\tif (v != null) b.Append(\"using \").Append(v).AppendLine(\";\");\n\t\t}\n\t\tif (i == k.code.Length || k.code[i] is not ('\\r' or '\\n')) b.AppendLine();\n\t\t\n\t\tk.sci.aaaInsertText(true, i, b.ToString(), addUndoPointAfter: true, restoreFolding: true);\n\t\t\n\t\treturn true;\n\t\t\n\t\t//tested: CompilationUnitSyntax.AddUsings isn't better than this code. Does not skip existing. Does not add newlines. Does not skip comments etc. Did't test #directives.\n\t}\n\t\n\t/// <summary>\n\t/// Finds where new using directives can be inserted:\n\t/// <br/>• after existing using directives\n\t/// <br/>• or at 0\n\t/// <br/>• or after extern aliases\n\t/// <br/>• or after #directives\n\t/// <br/>• or after meta\n\t/// <br/>• or after doc comments\n\t/// <br/>• or after 1 comments line.\n\t/// If namespaces!=null, clears existing namespaces in it (sets =null); if all cleared, returns -1.\n\t/// </summary>\n\tpublic static int FindUsingsInsertPos(CodeInfo.Context k, string[] namespaces = null) {\n\t\t//In namespaces clears elements that exist in e. If all cleared, sets namespaces=null and returns true.\n\t\tbool _ClearExistingUsings(IEnumerable<UsingDirectiveSyntax> e) {\n\t\t\tint n = namespaces.Count(o => o != null); //if (n == 0) return;\n\t\t\tforeach (var u in e) {\n\t\t\t\tint i = Array.IndexOf(namespaces, u.Name.ToString());\n\t\t\t\tif (i >= 0) {\n\t\t\t\t\tnamespaces[i] = null;\n\t\t\t\t\tif (--n == 0) { namespaces = null; return true; }\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\t//at first look for \"global using\"\n\t\tvar semo = k.semanticModel;\n\t\tif (namespaces != null && _ClearExistingUsings(CiUtil.GetAllGlobalUsings(semo))) return -1;\n\t\t\n\t\tint end = -1;\n\t\tvar cu = k.syntaxRoot;\n\t\t\n\t\t//then look in current namespace, ancestor namespaces, compilation unit\n\t\tint pos = k.sci.aaaCurrentPos16;\n\t\tfor (var node = cu.FindToken(pos).Parent; node != null; node = node.Parent) {\n\t\t\tSyntaxList<UsingDirectiveSyntax> usings; SyntaxList<ExternAliasDirectiveSyntax> externs;\n\t\t\tif (node is NamespaceDeclarationSyntax ns) {\n\t\t\t\tif (pos <= ns.OpenBraceToken.SpanStart || pos > ns.CloseBraceToken.SpanStart) continue;\n\t\t\t\tusings = ns.Usings; externs = ns.Externs;\n\t\t\t} else if (node is CompilationUnitSyntax) {\n\t\t\t\tusings = cu.Usings; externs = cu.Externs;\n\t\t\t} else continue;\n\t\t\t\n\t\t\tif (usings.Any()) {\n\t\t\t\tif (namespaces != null && _ClearExistingUsings(usings)) return -1;\n\t\t\t\tif (end < 0) end = usings[^1].FullSpan.End;\n\t\t\t} else if (externs.Any()) {\n\t\t\t\tif (end < 0) end = externs[^1].FullSpan.End;\n\t\t\t}\n\t\t\tif (end >= 0 && namespaces == null) break;\n\t\t}\n\t\t\n\t\tif (end < 0) { //insert at the start but after #directives and certain comments\n\t\t\tint end2 = -1;\n\t\t\tforeach (var v in cu.GetLeadingTrivia()) if (v.IsDirective) end2 = v.FullSpan.End; //skip directives\n\t\t\tif (end2 < 0) {\n\t\t\t\tend2 = k.meta.end; //skip meta\n\t\t\t\tif (end2 == 0) if (k.code.RxMatch(@\"^(///.*\\R)+(?=\\R|$)\", 0, out RXGroup g1)) end2 = g1.End; //skip ///comments\n\t\t\t\tif (k.code.RxMatch(@\"\\s*//\\.(?: .*)?\\R\", 0, out RXGroup g2, RXFlags.ANCHORED, end2..)) end2 = g2.End; //skip //. used for folding\n\t\t\t}\n\t\t\tend = end2;\n\t\t}\n\t\treturn end;\n\t}\n\t\n\t/// <summary>\n\t/// Inserts meta comments.\n\t/// </summary>\n\t/// <param name=\"s\">One or more meta options, like <c>\"c A; r B;\"</c>.</param>\n\t/// <returns>true if changed documnt text.</returns>\n\tpublic static bool MetaComment(string s) {\n\t\tDebug.Assert(App.IsMainThread);\n\t\tif (Panels.Editor.ActiveDoc is not { EFile.IsCodeFile: true } doc) return false;\n\t\tvar meta = new MetaCommentsParser(doc.aaaText);\n\t\tvar meta2 = new MetaCommentsParser($\"/*/ {s} /*/\");\n\t\tmeta.Merge(meta2);\n\t\treturn meta.Apply();\n\t}\n\t\n\t//rejected\n\t//public static void AddFileDescription() {\n\t//\tvar doc = Panels.Editor.ActiveDoc;\n\t//\tif (!doc.EFile.IsCodeFile) return;\n\t//\tdoc.aaaInsertText(false, 0, \"/// Description\\r\\n\\r\\n\");\n\t//\tdoc.aaaSelect(false, 4, 15, makeVisible: true);\n\t//}\n\t\n\tpublic static void SurroundPragmaWarningFormat() {\n\t\tCiSnippets.Surround(\"\"\"\n#pragma warning disable format\n${SELECTED_TEXT}$0\n#pragma warning restore format\n\"\"\");\n\t}\n\t\n\tpublic static void AddClassProgram() {\n\t\tif (!CodeInfo.GetContextAndDocument(out var cd) /*|| !cd.sci.EFile.IsScript*/) return;\n\t\tint start, end = cd.code.Length;\n\t\tvar members = cd.syntaxRoot.Members;\n\t\tif (members.Any()) {\n\t\t\tstart = _FindRealStart(false, members[0]);\n\t\t\tif (members[0] is not GlobalStatementSyntax) end = start;\n\t\t\telse if (members.FirstOrDefault(v => v is not GlobalStatementSyntax) is SyntaxNode sn) end = _FindRealStart(true, sn);\n\t\t\t\n\t\t\tint _FindRealStart(bool needEnd, SyntaxNode sn) {\n\t\t\t\tint start = sn.SpanStart;\n\t\t\t\t//find first empty line in comments before\n\t\t\t\tvar t = sn.GetLeadingTrivia();\n\t\t\t\tfor (int i = t.Count; --i >= 0;) {\n\t\t\t\t\tvar v = t[i];\n\t\t\t\t\tint ss = v.SpanStart;\n\t\t\t\t\tif (ss < cd.meta.end) break;\n\t\t\t\t\t//if (needEnd) { print.it($\"{v.Kind()}, '{v}'\"); continue; }\n\t\t\t\t\tvar k = v.Kind();\n\t\t\t\t\tif (k == SyntaxKind.EndOfLineTrivia) {\n\t\t\t\t\t\twhile (i > 0 && t[i - 1].IsKind(SyntaxKind.WhitespaceTrivia)) i--;\n\t\t\t\t\t\tif (i == 0 || t[i - 1].IsKind(k)) return needEnd ? ss : v.Span.End;\n\t\t\t\t\t} else if (k == SyntaxKind.SingleLineCommentTrivia) {\n\t\t\t\t\t\tif (cd.code.Eq(ss, \"//.\") && char.IsWhiteSpace(cd.code[ss + 3])) break;\n\t\t\t\t\t} else if (k is not (SyntaxKind.WhitespaceTrivia or SyntaxKind.MultiLineCommentTrivia or SyntaxKind.SingleLineDocumentationCommentTrivia or SyntaxKind.MultiLineDocumentationCommentTrivia)) {\n\t\t\t\t\t\tbreak; //eg #directive\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn start;\n\t\t\t}\n\t\t} else start = end;\n\t\t//CiUtil.HiliteRange(start, end); return;\n\t\t\n\t\tCiSnippets.Surround(\"\"\"\nclass Program {\n\tstatic void Main(string[] a) => new Program(a);\n\tProgram(string[] args) {\n${SELECTED_TEXT}$0\n\t}\n}\n\"\"\", start..end);\n\t}\n\t\n\t/// <summary>\n\t/// Called from Dicons.\n\t/// </summary>\n\t/// <param name=\"icon\">Like \"*Pack.Name #color\".</param>\n\tpublic static void SetMenuToolbarItemIcon(string icon) {\n\t\tif (!CodeInfo.GetDocumentAndFindNode(out var cd, out var node, -2)) return;\n\t\tvar semo = cd.semanticModel;\n\t\t\n\t\t//find nearest argumentlist and its method symbol\n\t\tBaseArgumentListSyntax arglist = null; IMethodSymbol method = null;\n\t\tvar es = node.FirstAncestorOrSelf<ExpressionStatementSyntax>()?.Expression;\n\t\tif (es is InvocationExpressionSyntax ies) { //m.Add(\"name\", \"image\"); or m.Submenu(\"name\", , \"image\"); etc\n\t\t\targlist = ies.ArgumentList;\n\t\t\tmethod = semo.GetSymbolInfo(ies.Expression).Symbol as IMethodSymbol;\n\t\t} else if (es is AssignmentExpressionSyntax aes && aes.Left is ElementAccessExpressionSyntax eaes) { //m[\"name\", \"image\"] = ;\n\t\t\targlist = eaes.ArgumentList;\n\t\t\tif (semo.GetSymbolInfo(eaes).Symbol is IPropertySymbol ips && ips.IsIndexer) method = ips.SetMethod;\n\t\t} else return;\n\t\tif (method == null) return;\n\t\t\n\t\t//get index of parameter of type MTImage\n\t\tvar timage = semo.Compilation.GetTypeByMetadataName(\"Au.Types.\" + nameof(MTImage)); if (timage == null) return;\n\t\tint paramIndex = 0; string paramName = null;\n\t\tforeach (var p in method.Parameters) {\n\t\t\tif (p.Type == timage) { paramName = p.Name; break; }\n\t\t\tparamIndex++;\n\t\t}\n\t\tif (paramIndex == method.Parameters.Length) return;\n\t\t\n\t\t//find image argument\n\t\tArgumentSyntax arg = null; int i = 0;\n\t\tforeach (var a in arglist.Arguments) {\n\t\t\tvar nc = a.NameColon;\n\t\t\tif (nc != null) {\n\t\t\t\tif (nc.Name.Identifier.Text == paramName) arg = a;\n\t\t\t} else {\n\t\t\t\tif (i == paramIndex) arg = a;\n\t\t\t}\n\t\t\tif (arg != null) break;\n\t\t\ti++;\n\t\t}\n\t\t\n\t\t//replace or insert\n\t\tint replFrom, replTo; string prefix = null, suffix = null;\n\t\tif (arg != null) {\n\t\t\tvar span = arg.Span;\n\t\t\treplFrom = span.Start;\n\t\t\treplTo = span.End;\n\t\t} else if (paramIndex < arglist.Arguments.Count) {\n\t\t\treplFrom = replTo = arglist.Arguments[paramIndex].SpanStart;\n\t\t\tsuffix = \", \";\n\t\t} else {\n\t\t\treplFrom = replTo = arglist.Span.End - 1;\n\t\t\tif (arglist.Arguments.Count > 0) prefix = \", \";\n\t\t}\n\t\ticon = $\"{prefix}{paramName}: \\\"{icon}\\\"{suffix}\";\n\t\t//print.it(cd.pos, replFrom, replTo, icon);\n\t\t\n\t\tcd.sci.aaaReplaceRange(true, replFrom, replTo, icon);\n\t}\n\t\n\tpublic static class Util {\n\t\t/// <summary>\n\t\t/// If <i>s</i> contains local variable declarations, replaces the variable names if they exist in current document in scope at caret position.\n\t\t/// </summary>\n\t\tpublic static void RenameNewSymbols(ref string s, int pos, bool makeVarName1 = false) {\n\t\t\tif (!CodeInfo.GetContextAndDocument(out var k)) return;\n\t\t\tvar token = k.syntaxRoot.FindToken(k.pos);\n\t\t\tvar node = token.Parent;\n\t\t\ttry { RenameNewSymbols(ref s, k, node, pos, makeVarName1); }\n\t\t\tcatch (Exception e1) { Debug_.Print(e1); }\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// If <i>s</i> contains local variable declarations, replaces the variable names if they exist in <i>k.document</i> in scope at <i>node</i>/<i>pos</i>.\n\t\t/// </summary>\n\t\t/// <param name=\"makeVarName1\">Also rename local variables declared like `var v` with `var v1` at root level.</param>\n\t\t/// <param name=\"rename\">Explicit renamings.</param>\n\t\tpublic static void RenameNewSymbols(ref string s, CodeInfo.Context k, SyntaxNode node, int pos, bool makeVarName1, (string oldName, string newName)[] rename = null) {\n\t\t\t//find the function or TLS where to look for declared symbols in editor code\n\t\t\tvar scope = node;\n\t\t\tfor (; scope is not CompilationUnitSyntax; scope = scope.Parent) {\n\t\t\t\tif (scope is BaseMethodDeclarationSyntax or LocalFunctionStatementSyntax or AnonymousFunctionExpressionSyntax) {\n\t\t\t\t\tif (scope.Span.ContainsInside(pos)) break;\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t//modified Roslyn's GetAllDeclaredSymbols. Would be difficult to use it.\n\t\t\tstatic void _GetDeclaredSymbols(SemanticModel semo, SyntaxNode node, List<ISymbol> symbols, HashSet<string> names, HashSet<string> hVar1, int level = 0) {\n\t\t\t\tif (node is not CompilationUnitSyntax && semo.GetDeclaredSymbol(node) is ISymbol sym) {\n\t\t\t\t\tsymbols?.Add(sym);\n\t\t\t\t\tnames?.Add(sym.Name);\n\t\t\t\t\tif (hVar1 != null) if (level == 4 && node.Parent.Parent is LocalDeclarationStatementSyntax && sym.Name is var s && !s.Ends('1')) hVar1.Add(s);\n\t\t\t\t}\n\t\t\t\tif (level > 0 && node is LocalFunctionStatementSyntax or BaseTypeDeclarationSyntax) return;\n\t\t\t\tforeach (var n in node.ChildNodes()) {\n\t\t\t\t\tif (n is AnonymousFunctionExpressionSyntax or AnonymousObjectCreationExpressionSyntax or TupleExpressionSyntax) continue; //lambda etc\n\t\t\t\t\t_GetDeclaredSymbols(semo, n, symbols, names, hVar1, level + 1);\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tHashSet<string> h1 = new(); //names of symbols declared in scope. If makeVarName1, also names of local variables declared in s like `var v` at root level.\n\t\t\t\n\t\t\t//get symbols declared in s\n\t\t\tList<ISymbol> a2 = new();\n\t\t\tHashSet<string> h2 = new();\n\t\t\tusing var ws2 = new AdhocWorkspace();\n\t\t\tvar doc2 = CiUtil.CreateDocumentFromCode(ws2, s, false);\n\t\t\tvar semo2 = doc2.GetSemanticModelAsync().Result_();\n\t\t\t_GetDeclaredSymbols(semo2, semo2.Root, a2, h2, makeVarName1 ? h1 : null);\n\t\t\t//print.it(\"---- h2 ----\"); foreach (var v in h2) print.it(v);\n\t\t\tif (h2.Count == 0) return;\n\t\t\t\n\t\t\t//get names of symbols declared in scope (editor code)\n\t\t\tvar semo1 = k.document.GetSemanticModelAsync().Result_();\n\t\t\t_GetDeclaredSymbols(semo1, scope, null, h1, null);\n\t\t\t//print.it(\"---- h1 ----\"); foreach (var v in h1) print.it(v);\n\t\t\tif (h1.Count == 0) return;\n\t\t\t\n\t\t\tbool renamed = false;\n\t\t\tvar sol = doc2.Project.Solution;\n\t\t\t\n\t\t\t//in s rename symbols that exist in scope (editor code) or renameVars\n\t\t\tforeach (var sym in a2) {\n\t\t\t\tstring name = sym.Name, name2 = null;\n\t\t\t\t\n\t\t\t\tif (rename != null) {\n\t\t\t\t\tforeach (var v in rename)\n\t\t\t\t\t\tif (v.oldName == name) {\n\t\t\t\t\t\t\tname = name2 = v.newName;\n\t\t\t\t\t\t\tif (!makeVarName1 && !h1.Contains(name)) goto g2;\n\t\t\t\t\t\t\tgoto g1;\n\t\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (!h1.Contains(name)) continue;\n\t\t\t\tg1:\n\t\t\t\t//create unique name and add to hs1\n\t\t\t\tint i = 1;\n\t\t\t\tif (name.RxMatch(@\"\\d+$\", 0, out RXGroup g)) {\n\t\t\t\t\ti = Math.Max(i, name.ToInt(g.Start) + 1);\n\t\t\t\t\tname = name[..g.Start];\n\t\t\t\t}\n\t\t\t\twhile (h2.Contains(name2 = name + i) || !h1.Add(name2)) i++;\n\t\t\t\t//print.it(sym.Name, name2);\n\t\t\t\tg2:\n\t\t\t\tvar opt1 = new SymbolRenameOptions();\n\t\t\t\tsol = Renamer.RenameSymbolAsync(sol, sym, opt1, name2).Result_();\n\t\t\t\th2.Remove(sym.Name);\n\t\t\t\trenamed = true;\n\t\t\t}\n\t\t\tif (renamed) s = sol.GetDocument(doc2.Id).GetTextAsync().Result_().ToString();\n\t\t\t\n\t\t\t//rejected: don't rename if variables in both codes are in unrelated { blocks }.\n\t\t\t//\tTested: in most cases better like now.\n\t\t\t\n\t\t\t//rejected: also replace names that match reserved keywords.\n\t\t\t//\tThis program will not create such names.\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Edit/ModifyCode.cs",
    "content": "extern alias CAW;\n\nusing Microsoft.CodeAnalysis;\nusing CAW::Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp;\nusing Microsoft.CodeAnalysis.CSharp.Syntax;\nusing Microsoft.CodeAnalysis.Text;\nusing Microsoft.CodeAnalysis.Shared.Extensions;\nusing CAW::Microsoft.CodeAnalysis.Shared.Extensions;\nusing Microsoft.CodeAnalysis.CSharp.Extensions;\nusing Microsoft.CodeAnalysis.Shared.Utilities;\nusing CAW::Microsoft.CodeAnalysis.Shared.Utilities;\nusing CAW::Microsoft.CodeAnalysis.FindSymbols;\nusing Microsoft.CodeAnalysis.Formatting;\nusing CAW::Microsoft.CodeAnalysis.Formatting;\nusing Microsoft.CodeAnalysis.CSharp.Formatting;\n\nnamespace LA;\n\nstatic class ModifyCode {\n\t/// <summary>\n\t/// Comments out (adds // or /**/) or uncomments selected text or current line.\n\t/// </summary>\n\t/// <param name=\"comment\">Comment out (true), uncomment (false) or toggle (null).</param>\n\t/// <param name=\"notSlashStar\">Comment out lines, even if there is not-full-line selection.</param>\n\tpublic static void Comment(bool? comment, bool notSlashStar = false) {\n\t\t//how to comment/uncomment: // or /**/\n\t\tif (!CodeInfo.GetContextAndDocument(out var cd, -2, metaToo: true)) return;\n\t\tvar doc = cd.sci;\n\t\tint selStart = cd.pos, selEnd = doc.aaaSelectionEnd16, replStart = selStart, replEnd = selEnd;\n\t\tbool com, slashStar = false, isSelection = selEnd > selStart;\n\t\tstring code = cd.code, s = null;\n\t\tvar root = cd.syntaxRoot;\n\t\t\n\t\tif (!notSlashStar) {\n\t\t\tvar trivia = root.FindTrivia(selStart);\n\t\t\tif (trivia.IsMultiLineComment()) {\n\t\t\t\tif (selStart > cd.meta.start && selEnd < cd.meta.end) {\n\t\t\t\t\tif (slashStar = !isSelection) {\n\t\t\t\t\t\tvar t = MetaComments.EnumOptions(code, new(cd.meta.start, cd.meta.end)).FirstOrDefault(o => selStart >= o.nameStart && selEnd <= o.valueEnd);\n\t\t\t\t\t\tif (t.code == null) return;\n\t\t\t\t\t\tif (comment is null) comment = !t.IsDisabled; else if (comment == t.IsDisabled) return;\n\t\t\t\t\t\treplStart = replEnd = t.nameStart;\n\t\t\t\t\t\tif (comment == true) s = \"//\"; else replEnd += code.Eq(replStart, \"//\") ? 2 : 1;\n\t\t\t\t\t} else notSlashStar = true;\n\t\t\t\t} else {\n\t\t\t\t\tvar span = trivia.Span;\n\t\t\t\t\tif (slashStar = comment != true && selEnd <= trivia.Span.End) {\n\t\t\t\t\t\t(replStart, replEnd) = span;\n\t\t\t\t\t\ts = code[(replStart + 2)..(replEnd - 2)];\n\t\t\t\t\t} else notSlashStar = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (!slashStar) {\n\t\t\t//get the start and end of lines containing selection\n\t\t\twhile (replStart > 0 && code[replStart - 1] != '\\n') replStart--;\n\t\t\tif (!(replEnd > replStart && code[replEnd - 1] == '\\n')) {\n\t\t\t\twhile (replEnd < code.Length && code[replEnd] is not ('\\r' or '\\n')) replEnd++;\n\t\t\t\tif (replEnd > replStart && code.Eq(replEnd, \"\\r\\n\")) replEnd += 2; //prevent on Undo moving the caret to the end of line and possibly hscrolling\n\t\t\t}\n\t\t\tif (replEnd == replStart) return;\n\t\t\t\n\t\t\t//are all lines //comments ?\n\t\t\tbool allLinesComments = true;\n\t\t\tfor (int i = replStart; i < replEnd; i++) {\n\t\t\t\ti = CiUtil.SkipSpace(code, i);\n\t\t\t\tif (!(code.At_(i) is '\\r' or '\\n')) {\n\t\t\t\t\tif (root.FindTrivia(i).Kind() is var tk && !(tk is SyntaxKind.SingleLineCommentTrivia || (tk is SyntaxKind.DisabledTextTrivia or SyntaxKind.MultiLineCommentTrivia or SyntaxKind.MultiLineDocumentationCommentTrivia && code.Eq(i, \"//\")))) {\n\t\t\t\t\t\tallLinesComments = false;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\ti = code.IndexOf('\\n', i, replEnd - i); if (i < 0) break;\n\t\t\t}\n\t\t\tif (allLinesComments) {\n\t\t\t\tcom = comment ?? false;\n\t\t\t} else {\n\t\t\t\tif (comment == false) return;\n\t\t\t\tcom = true;\n\t\t\t\tslashStar = isSelection && !notSlashStar && (selStart > replStart || selEnd < replEnd);\n\t\t\t\tif (slashStar) { replStart = selStart; replEnd = selEnd; }\n\t\t\t}\n\t\t\t\n\t\t\ts = code[replStart..replEnd];\n\t\t\tif (slashStar) {\n\t\t\t\ts = \"/*\" + s + \"*/\";\n\t\t\t} else {\n\t\t\t\tif (com) {\n\t\t\t\t\ts.RxFindAll(@\"(?m)^[\\t ]*(.*)\\R?\", out RXMatch[] a);\n\t\t\t\t\t//find smallest common indentation\n\t\t\t\t\tint indent = 0; //tabs*4 or spaces*1\n\t\t\t\t\tforeach (var m in a) {\n\t\t\t\t\t\tif (m[1].Length == 0) continue;\n\t\t\t\t\t\tint n = 0; for (int i = m.Start; i < m[1].Start; i++) if (s[i] == ' ') n++; else n = (n & ~3) + 4;\n\t\t\t\t\t\tindent = indent == 0 ? n : Math.Min(indent, n);\n\t\t\t\t\t\tif (indent == 0) break;\n\t\t\t\t\t}\n\t\t\t\t\t//insert // in lines containing code\n\t\t\t\t\tvar b = new StringBuilder();\n\t\t\t\t\tforeach (var m in a) {\n\t\t\t\t\t\tif (m[1].Length == 0) {\n\t\t\t\t\t\t\tb.Append(s, m.Start, m.Length);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tint i = m.Start; for (int n = 0; n < indent; i++) if (s[i] == ' ') n++; else n = (n & ~3) + 4;\n\t\t\t\t\t\t\tb.Append(s, m.Start, i - m.Start).Append(\"//\").Append(s, i, m.End - i);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\ts = b.ToString();\n\t\t\t\t} else { //remove single // from all lines\n\t\t\t\t\ts = s.RxReplace(@\"(?m)^([ \\t]*)//\", \"$1\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tbool caretAtEnd = isSelection && doc.aaaCurrentPos16 == selEnd;\n\t\tdoc.EReplaceTextGently(replStart, replEnd, s);\n\t\tif (isSelection) {\n\t\t\tint i = replStart, j = replStart + s.Length;\n\t\t\tdoc.aaaSelect(true, caretAtEnd ? i : j, caretAtEnd ? j : i);\n\t\t}\n\t}\n\t\n\t//public static string Format(ref int from, ref int to) {\n\t//\tif (!CodeInfo.GetContextAndDocument(out var cd, from, metaToo: true)) return null;\n\t//\treturn Format(cd, ref from, ref to);\n\t//}\n\t\n\tpublic static void Format(bool selection) {\n\t\tif (!CodeInfo.GetContextAndDocument(out var cd, -2, metaToo: true)) return;\n\t\t\n\t\tvar doc = cd.sci;\n\t\tint from, to, selStart = cd.pos, selEnd = doc.aaaSelectionEnd16;\n\t\tif (selection) {\n\t\t\t(from, to) = (selStart, selEnd);\n\t\t\tif (from == to) {\n\t\t\t\tvar node = cd.syntaxRoot.FindToken(from).Parent.GetStatementEtc(from);\n\t\t\t\tif (node == null) return;\n\t\t\t\t(from, to) = node.FullSpan;\n\t\t\t}\n\t\t} else {\n\t\t\t(from, to) = (0, cd.code.Length);\n\t\t}\n\t\t\n\t\tif (to > from && Format(cd, ref from, ref to, ref selStart, ref selEnd) is { } a) {\n\t\t\t_FormatReplace(cd, a);\n\t\t\tdoc.aaaSelect(true, selStart, selEnd);\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Formats text of the specified range.\n\t/// </summary>\n\tpublic static void Format(CodeInfo.Context cd, int from, int to) {\n\t\tint pos = cd.sci.aaaCurrentPos16;\n\t\tif (Format(cd, ref from, ref to, ref pos, ref pos) is { } a) {\n\t\t\t_FormatReplace(cd, a);\n\t\t\tif (pos >= from || pos <= to) cd.sci.aaaCurrentPos16 = pos;\n\t\t}\n\t}\n\t\n\tstatic void _FormatReplace(CodeInfo.Context cd, List<TextChange> a) {\n\t\tusing var undo = a.Count < 2 ? default : cd.sci.ENewUndoAction();\n\t\tfor (int i = a.Count; --i >= 0;) {\n\t\t\tvar c = a[i];\n\t\t\tcd.sci.aaaReplaceRange(true, c.Span.Start, c.Span.End, c.NewText);\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Formats text of the specified range.\n\t/// </summary>\n\t/// <param name=\"cd\"></param>\n\t/// <param name=\"from\">Start of range. The function may adjust it to span horizontal whitespace before.</param>\n\t/// <param name=\"to\">End of range. The function may adjust it to exclude newline after.</param>\n\t/// <param name=\"selStart\">Selection start. The function adjusts it to match the formatted text. Can be -1 if don't need.</param>\n\t/// <param name=\"selEnd\">Selection end. The function adjusts it to match the formatted text. Can be -1 if don't need. Can be the same variable as <i>selStart</i>.</param>\n\t/// <returns>Text changes in the final range. Or null if no changes.</returns>\n\tpublic static List<TextChange> Format(CodeInfo.Context cd, ref int from, ref int to, ref int selStart, ref int selEnd) {\n\t\tstring code = cd.code;\n\t\tDebug.Assert(code.AsSpan(..to).Eq(cd.sci.aaaText.AsSpan(..to)));\n\t\t\n\t\t//exclude newline at the end. Else formats entire leading trivia of next token.\n\t\t//\tNever mind: anyway formats if the last selected line is //comment.\n\t\tif (to < code.Length) {\n\t\t\tif (to - from > 0 && code[to - 1] == '\\n') to--;\n\t\t\tif (to - from > 0 && code[to - 1] == '\\r') to--;\n\t\t}\n\t\tif (to == from) return null;\n\t\t\n\t\t//include whitespace before. Else _Format can't detect \\r\\n before when indented.\n\t\twhile (from > 0 && code[from - 1] is '\\t' or ' ') from--;\n\t\t\n\t\tif (!_Format(cd, from, to, out var a)) return null;\n\t\t\n\t\tint i1 = from, caret1 = selStart, caret2 = selEnd, moveCaret1 = 0, moveCaret2 = 0;\n\t\tfor (int i = 0; i < a.Count; i++) {\n\t\t\tvar v = a[i];\n\t\t\tvar newText = v.NewText;\n\t\t\tvar (cStart, cEnd) = v.Span;\n\t\t\t\n\t\t\t_Caret(caret1, ref moveCaret1);\n\t\t\t_Caret(caret2, ref moveCaret2);\n\t\t\t\n\t\t\tvoid _Caret(int caret, ref int moveCaret) {\n\t\t\t\tif (caret >= cEnd) {\n\t\t\t\t\tif (caret == cEnd && cStart == cEnd && caret == caret1 && caret2 > caret) return; //inserting text at caret1. Eg adding indent when caret1 is at SOL. Let selStart remain at SOL.\n\t\t\t\t\tmoveCaret += cStart - cEnd + newText.Length;\n\t\t\t\t} else if (caret > cStart) moveCaret += cStart - caret + newText.Length;\n\t\t\t}\n\t\t}\n\t\t\n\t\tcaret1 += moveCaret1;\n\t\tif (caret2 >= 0) caret2 = Math.Max(caret2 + moveCaret2, caret1);\n\t\tselStart = caret1;\n\t\tselEnd = caret2;\n\t\t\n\t\treturn a;\n\t}\n\t\n\tstatic bool _Format(CodeInfo.Context cd, int from, int to, out List<TextChange> ac, string code = null) {\n\t\tac = null;\n\t\tif ((uint)(to - from) > 500_000) return false; //too slow\n\t\t\n\t\tbool changedCode = code != null;\n\t\tcode ??= cd.code;\n\t\tstring code0 = code;\n\t\tvar root = cd.syntaxRoot;\n\t\t\n\t\t//workaround for some nasty Roslyn features that can't be changed with options:\n\t\t//\tRemoves tabs from empty lines.\n\t\t//\tIf next line after code//comment1 is //comment2, aligns //comment2 with //comment1.\n\t\t//Before formatting, in blank lines add a marker (doc comment). The same in lines containing only //comment.\n\t\t//Other ways: 1. Modify Roslyn code; too difficult etc. 2. Fix formatted code; not 100% reliable.\n\t\tconst string c_mark = \"///\\a\\b\"; const int c_markLen = 5;\n\t\tint nw = (s_rx1 ??= new(@\"(?m)^\\h*\\K(?=\\R|//(?!/(?!/)))\")).Replace(code, c_mark, out code, range: from..to);\n\t\tif (nw > 0 || changedCode) {\n\t\t\troot = root.SyntaxTree.WithChangedText(SourceText.From(code)).GetCompilationUnitRoot();\n\t\t\tif (root.GetText().Length != code.Length) { Debug_.Print(\"bad new code\"); return false; }\n\t\t}\n\t\t\n\t\t//include \\r\\n before. Else may not correct indent.\n\t\t//\tnever mind: then formats all trivia before. We'll skip it.\n\t\tif (from > 0 && code[from - 1] == '\\n') from--;\n\t\tif (from > 0 && code[from - 1] == '\\r') from--;\n\t\t//never mind: does not add space before statement when *from* is after eg `{` or `}` or `;`.\n\t\t//\tVS adds space or newline when using \"Format selection\", but not when auto-format.\n\t\t\n\t\ttry { ac = Formatter.GetFormattedTextChanges(root, TextSpan.FromBounds(from, to + nw * c_markLen), cd.document.Project.Solution.Services, FormattingOptions) as List<TextChange>; }\n\t\tcatch (Exception e1) { Debug_.Print(e1); return false; } //https://www.libreautomate.com/forum/showthread.php?tid=7622\n\t\tif (ac.Count == 0) return false;\n\t\t\n\t\t//part 2 of the workaround. Remove marker traces from ac. Then ac will match the original code (the caller's version).\n\t\tif (nw > 0) {\n\t\t\t//_PrintFormattingTextChanges(\"BEFORE\", code, ac);\n\t\t\t\n\t\t\tvar aInserted = new int[nw];\n\t\t\tfor (int i = 0, j = 0; i < nw; j += c_markLen) aInserted[i++] = j = code.Find(\"///\\a\\b\", j);\n\t\t\t\n\t\t\tfor (int i = 0; i < ac.Count; i++) {\n\t\t\t\tvar v = ac[i];\n\t\t\t\t\n\t\t\t\t//some changes contain marks\n\t\t\t\tvar s = v.NewText; int lenRemoved = 0;\n\t\t\t\tif (s.Length >= c_markLen) { s = s.Replace(c_mark, null); lenRemoved = v.NewText.Length - s.Length; }\n\t\t\t\t\n\t\t\t\tint startOfChange = v.Span.Start, nInsertedBefore = 0;\n\t\t\t\twhile (nInsertedBefore < nw && aInserted[nInsertedBefore] < startOfChange) nInsertedBefore++;\n\t\t\t\t\n\t\t\t\tif (nInsertedBefore > 0 || lenRemoved > 0) ac[i] = new(new(startOfChange - nInsertedBefore * c_markLen, v.Span.Length - lenRemoved), s);\n\t\t\t}\n\t\t\t\n\t\t\tcode = code0;\n\t\t\t//_PrintFormattingTextChanges(\"AFTER\", code, ac);\n\t\t}\n\t\t\n\t\t//remove fake changes. Can be many.\n\t\tfor (int i = 0; i < ac.Count; i++) {\n\t\t\tvar v = ac[i];\n\t\t\tif (code.AsSpan(v.Span.ToRange()).Eq(v.NewText)) ac[i] = default;\n\t\t}\n\t\tac.RemoveAll(static o => o.NewText == null);\n\t\tif (ac.Count == 0) return false;\n\t\t\n\t\t//_PrintFormattingTextChanges(\"CHANGES\", code, ac);\n\t\t\n\t\tfor (int i = 0; i < ac.Count; i++) {\n\t\t\tvar v = ac[i];\n\t\t\tif (v.Span.IsEmpty || v.NewText.Length == 0) continue;\n\t\t\t\n\t\t\t//Some changes contain unchanged text at the start or end. Eg comments and/or newlines.\n\t\t\t//\tIf a change contain newlines, it would delete markers etc. Also may span the start or end of the formatting range.\n\t\t\t//\tRemove such unchanged text from the change.\n\t\t\tRStr sp1 = code.AsSpan(v.Span.ToRange()), sp2 = v.NewText;\n\t\t\tint commonStart = StringUtil.CommonPrefix(sp1, sp2);\n\t\t\tif (commonStart > 0) { sp1 = sp1[commonStart..]; sp2 = sp2[commonStart..]; }\n\t\t\tint commonEnd = StringUtil.CommonSuffix(sp1, sp2);\n\t\t\tif (commonEnd > 0) { sp1 = sp1[..^commonEnd]; sp2 = sp2[..^commonEnd]; }\n\t\t\tif (commonStart + commonEnd > 0) ac[i] = v = new(TextSpan.FromBounds(v.Span.Start + commonStart, v.Span.End - commonEnd), sp2.ToString());\n\t\t\t\n\t\t\t//Some changes can be multiline. Eg when code contains `//comment\\r\\n\\r\\n//comment\\r\\n\\r\\n`. Would delete markers etc. Split.\n\t\t\tif (sp1.Contains('\\n')) {\n\t\t\t\tvar a1 = code.Lines(v.Span.ToRange(), preferMore: true);\n\t\t\t\tvar a2 = v.NewText.Lines(.., preferMore: true);\n\t\t\t\tif (a1.Length != a2.Length) {\n\t\t\t\t\t//Debug_.Print(v); //usually it's OK. Eg `foo\\n{` -> `foo {`.\n\t\t\t\t} else {\n\t\t\t\t\tbool insert = false;\n\t\t\t\t\tfor (int j = 0; j < a1.Length; j++) {\n\t\t\t\t\t\tif (a1[j].Length == a2[j].Length && code.Eq(a1[j].Range, v.NewText.AsSpan(a2[j].Range))) continue;\n\t\t\t\t\t\tTextChange k = new(new(a1[j].start, a1[j].Length), v.NewText[a2[j].Range]);\n\t\t\t\t\t\tif (!insert) { insert = true; ac[i] = k; } else ac.Insert(++i, k);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\t//remove changes that are not in the range. Most likely after.\n\t\tfor (int i = ac.Count; --i >= 0;) {\n\t\t\tvar v = ac[i];\n\t\t\tvar (cStart, cEnd) = v.Span;\n\t\t\t\n\t\t\tif (cStart < from || cEnd > to || (cEnd == to && v.NewText.Length == 0)) {\n\t\t\t\t//Debug_.Print($\"a TextChange not in the formatting range {from..to}: {v}\");\n\t\t\t\tDebug_.PrintIf(cStart < from && cEnd > from);\n\t\t\t\tDebug_.PrintIf(cEnd > to && cStart < to);\n\t\t\t\tac.RemoveAt(i--);\n\t\t\t}\n\t\t}\n\t\t\n\t\treturn ac.Count > 0;\n\t\t\n\t\t//BAD: Roslyn does not format multiline collection initializers.\n\t\t//\thttps://github.com/dotnet/roslyn/issues/8269\n\t}\n\tstatic regexp s_rx1;\n\t\n#if DEBUG\n\tstatic void _PrintFormattingTextChanges(string header, string code, IList<TextChange> a) {\n\t\tprint.it(\"----\", header);\n\t\tforeach (var v in a) {\n\t\t\tif (code.AsSpan(v.Span.ToRange()).Eq(v.NewText)) print.it($\"<><c gray><\\a>{v}</\\a><>\");\n\t\t\telse print.it($\"<><c green><\\a>{v}</\\a><>, <c blue>\\\"<\\a>{code[v.Span.ToRange()]}</\\a>\\\"<>\");\n\t\t}\n\t}\n#endif\n\t\n\t/// <summary>\n\t/// Formats code for inserting in current document at <i>start</i>.\n\t/// If end &gt; <i>start</i> - for replacing text at <c>start..end</c>.\n\t/// May decrease <i>start</i>; does it before calling <i>changes</i>.\n\t/// </summary>\n\t/// <returns><c>true</c> if formatted.</returns>\n\tpublic static bool FormatForInsert(ref string s, ref int start, int end, Action<IList<TextChange>> changes = null) {\n\t\tif (!CodeInfo.GetContextAndDocument(out var cd, start, metaToo: true) || s.NE()) return false;\n\t\t\n\t\tvar code = cd.code.ReplaceAt(start..end, s);\n\t\tint end2 = start + s.Length;\n\t\tint start2 = start; while (start2 > 0 && cd.code[start2 - 1] is '\\t' or ' ') start2--; //include whitespace before. Else _Format can't detect \\r\\n before when indented.\n\t\t\n\t\tif (!_Format(cd, start2, end2, out var a, code)) return false;\n\t\t\n\t\tfor (int i = a.Count; --i >= 0;) {\n\t\t\tvar v = a[i];\n\t\t\tint ss = v.Span.Start, se = v.Span.End;\n\t\t\tif (ss < start2 && se >= start2 && se < end2\n\t\t\t\t&& !(se == start2 && v.NewText == \" \" && cd.code.Eq(se - 1, '\\n')) //formatter bug: in TLS replaces `A\\n{B` with `A {B`\n\t\t\t\t) {\n\t\t\t\tint n = v.NewText.AsSpan().TrimEnd(\"\\t \").Length;\n\t\t\t\ta[i] = new(TextSpan.FromBounds(start2, se), v.NewText[n..]);\n\t\t\t} else {\n\t\t\t\tif (ss < start2 || se > end2 || code.Eq(ss..se, v.NewText)) a.RemoveAt(i);\n\t\t\t\telse if (v.NewText.NE() && se - ss == 1 && code.Eq((ss - 2)..(ss + 2), \"{  }\")) a.RemoveAt(i); //don't replace `{  }` with `{ }` eg in snippet\n\t\t\t}\n\t\t}\n\t\tif (a.Count == 0) return false;\n\t\t\n\t\t//_PrintFormattingTextChanges(\"CHANGES\", code, a);\n\t\t\n\t\tstart = start2;\n\t\tif (changes != null) {\n\t\t\t//if (start2 < start) s = string.Concat(cd.code.AsSpan(start2..start), s); //currently not used\n\t\t\tchanges(a);\n\t\t}\n\t\t\n\t\tvar b = new StringBuilder();\n\t\tint k = start;\n\t\tforeach (var v in a) {\n\t\t\tb.Append(code, k, v.Span.Start - k).Append(v.NewText);\n\t\t\tk = v.Span.End;\n\t\t}\n\t\tb.Append(code, k, end2 - k);\n\t\ts = b.ToString();\n\t\t\n\t\treturn true;\n\t}\n\t\n\t/// <summary>\n\t/// Can be replaced by editorExtension scripts.\n\t/// </summary>\n\tpublic static CSharpSyntaxFormattingOptions FormattingOptions {\n\t\tget {\n\t\t\tif (_formattingOptions == null) {\n\t\t\t\tswitch (App.Settings.ci_formatCompact, App.Settings.ci_formatTabIndent) {\n\t\t\t\tcase (true, true): //default in this program\n\t\t\t\t\t_formattingOptions = new() {\n\t\t\t\t\t\tLineFormatting = new() { UseTabs = true },\n\t\t\t\t\t\tIndentation = IndentationPlacement.BlockContents | IndentationPlacement.SwitchCaseContents | IndentationPlacement.SwitchCaseContentsWhenBlock,\n\t\t\t\t\t\tNewLines = NewLinePlacement.BeforeCatch | NewLinePlacement.BeforeFinally | NewLinePlacement.BetweenQueryExpressionClauses,\n\t\t\t\t\t\tLabelPositioning = LabelPositionOptionsInternal.NoIndent,\n\t\t\t\t\t};\n\t\t\t\t\tbreak;\n\t\t\t\tcase (false, true):\n\t\t\t\t\t_formattingOptions = new() {\n\t\t\t\t\t\tLineFormatting = new() { UseTabs = true },\n\t\t\t\t\t};\n\t\t\t\t\tbreak;\n\t\t\t\tcase (true, false):\n\t\t\t\t\t_formattingOptions = new() {\n\t\t\t\t\t\tIndentation = IndentationPlacement.BlockContents | IndentationPlacement.SwitchCaseContents | IndentationPlacement.SwitchCaseContentsWhenBlock,\n\t\t\t\t\t\tNewLines = NewLinePlacement.BeforeCatch | NewLinePlacement.BeforeFinally | NewLinePlacement.BetweenQueryExpressionClauses,\n\t\t\t\t\t\tLabelPositioning = LabelPositionOptionsInternal.NoIndent,\n\t\t\t\t\t};\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\t_formattingOptions = CSharpSyntaxFormattingOptions.Default;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn _formattingOptions;\n\t\t}\n\t\tset { _formattingOptions = value; }\n\t}\n\tstatic CSharpSyntaxFormattingOptions _formattingOptions;\n\t\n\tpublic static void CleanupWndFind() {\n\t\tif (!CodeInfo.GetContextAndDocument(out var cd, metaToo: true)) return;\n\t\tvar doc = cd.sci;\n\t\tvar (from, to) = doc.aaaHasSelection ? doc.aaaSelection(true) : (0, cd.code.Length);\n\t\tDictionary<string, List<LocalDeclarationStatementSyntax>> d = new();\n\t\t\n\t\tforeach (var n in cd.syntaxRoot.DescendantNodes(TextSpan.FromBounds(from, to))) {\n\t\t\tif (n is not LocalDeclarationStatementSyntax lds) continue;\n\t\t\tvar ds = lds.Declaration;\n\t\t\tif (!(ds.Type.ToString() is \"var\" or \"wnd\" or \"Au.wnd\")) continue;\n\t\t\tif (ds.Variables.Count != 1) continue;\n\t\t\tvar ds2 = ds.Variables[0];\n\t\t\tif (ds2.Initializer?.Value is not InvocationExpressionSyntax ies) continue;\n\t\t\tif (ies.Expression.ToString() != \"wnd.find\") {\n\t\t\t\t//maybe wnd.find(...).Activate()\n\t\t\t\tif (!ies.ToString().Like(\"wnd.find(*).Activate()\")) continue;\n\t\t\t\tif (ies.Expression is not MemberAccessExpressionSyntax mas) continue;\n\t\t\t\ties = mas.Expression as InvocationExpressionSyntax;\n\t\t\t\tif (ies?.Expression.ToString() != \"wnd.find\") continue;\n\t\t\t}\n\t\t\tvar a = ies.ArgumentList.Arguments;\n\t\t\tif (a.Count > 0) {\n\t\t\t\tvar k0 = a[0].Expression.Kind();\n\t\t\t\tif (k0 is SyntaxKind.NumericLiteralExpression || (a[0].Expression is PrefixUnaryExpressionSyntax pues && pues.Operand.Kind() is SyntaxKind.NumericLiteralExpression)) a = a.RemoveAt(0);\n\t\t\t\telse if (!(k0 is SyntaxKind.StringLiteralExpression or SyntaxKind.NullLiteralExpression)) continue; //probably Seconds\n\t\t\t}\n\t\t\tif (a.Count == 0) continue;\n\t\t\tvar s = a.ToString();\n\t\t\tif (d.TryGetValue(s, out var list)) list.Add(lds); else d[s] = new() { lds };\n\t\t\t\n\t\t\t//CONSIDER: if found with similar name (eg \"Doc1 - App\" -> \"Doc2 - App\"), ask whether to remove these too. Eg UI with checkboxes.\n\t\t\t//\tAlso the UI should offer to replace these with `w.WaitForName`.\n\t\t\t//\tBut maybe in many cases the user would do it manually faster than with all this confusing UI. Or just leave it as is.\n\t\t}\n\t\t\n\t\tList<(int start, int end, string repl)> aRepl = new();\n\t\tforeach (var (k, a) in d) {\n\t\t\tif (a.Count < 2) continue;\n\t\t\tstatic SyntaxNode _Scope(SyntaxNode n) {\n\t\t\t\tn = n.Parent;\n\t\t\t\tif (n is GlobalStatementSyntax) n = n.Parent;\n\t\t\t\treturn n;\n\t\t\t}\n\t\t\tg1:\n\t\t\tvar ds0 = a[0];\n\t\t\tvar repl = ds0.Declaration.Variables[0].Identifier.Text;\n\t\t\tvar scope0 = _Scope(ds0);\n\t\t\ta.RemoveAt(0);\n\t\t\tfor (int i = 0; i < a.Count; i++) {\n\t\t\t\tvar lds = a[i];\n\t\t\t\tvar scope = _Scope(lds);\n\t\t\t\tif (scope != scope0) {\n\t\t\t\t\tbool ok = false;\n\t\t\t\t\tforeach (var v in scope.Ancestors()) {\n\t\t\t\t\t\tif (ok = v == scope0) break;\n\t\t\t\t\t\tif (v is MemberDeclarationSyntax && v is not GlobalStatementSyntax) break;\n\t\t\t\t\t\tif (v is AnonymousFunctionExpressionSyntax or LocalFunctionStatementSyntax) break;\n\t\t\t\t\t}\n\t\t\t\t\tif (!ok) continue;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t//remove the duplicate wnd.find statement\n\t\t\t\tif (lds.Declaration.Variables[0].Initializer.Value is InvocationExpressionSyntax ies && ies.Expression is MemberAccessExpressionSyntax mas && mas.Name.Identifier.Text == \"Activate\") { //.Activate()\n\t\t\t\t\tvar span = lds.Span;\n\t\t\t\t\taRepl.Add((span.Start, mas.Expression.Span.End, repl));\n\t\t\t\t} else {\n\t\t\t\t\tvar span = lds.GetRealFullSpan(true);\n\t\t\t\t\taRepl.Add((span.Start, span.End, \"\"));\n\t\t\t\t}\n\t\t\t\ta.RemoveAt(i--);\n\t\t\t\t\n\t\t\t\t//find references of the variable declared in the duplicate wnd.find statement\n\t\t\t\tif (cd.semanticModel.GetDeclaredSymbol(lds.Declaration.Variables[0]) is ISymbol sym) {\n\t\t\t\t\tif (SymbolFinder.FindReferencesAsync(sym, cd.document.Project.Solution, [cd.document]).Result_().SingleOrDefault() is { } rs) {\n\t\t\t\t\t\tforeach (var loc in rs.Locations) {\n\t\t\t\t\t\t\tvar span = loc.Location.SourceSpan;\n\t\t\t\t\t\t\taRepl.Add((span.Start, span.End, repl));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (a.Count > 1) goto g1;\n\t\t}\n\t\t\n\t\tif (aRepl.Count == 0) {\n\t\t\tdialog.showInfo(\"Deduplicate wnd.find\", $\"No duplicate wnd.find statements found{(doc.aaaHasSelection ? \" in the selected text\" : \"\")}.\", owner: doc.AaWnd, secondsTimeout: 5);\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tdoc.EInicatorsFound_(aRepl.Select(o => o.start..o.end).ToList());\n\t\twait.doEvents(1000);\n\t\tif (doc != Panels.Editor.ActiveDoc || doc.aaaText != cd.code) return;\n\t\tusing (doc.ENewUndoAction()) {\n\t\t\tforeach (var v in aRepl.OrderByDescending(o => o.end)) {\n\t\t\t\tif (v.start > to) continue; to = v.start; //a variable in a replaced statement\n\t\t\t\tdoc.aaaReplaceRange(true, v.start, v.end, v.repl);\n\t\t\t}\n\t\t}\n\t}\n\t\n\tpublic static void ConvertInterfaceMethodPreserveSig() {\n\t\tif (!CodeInfo.GetContextAndDocument(out var cd, metaToo: true)) return;\n\t\tvar doc = cd.sci;\n\t\tvar code = cd.code;\n\t\tvar (selStart, selEnd) = doc.aaaSelection(true);\n\t\tvar tok1 = cd.syntaxRoot.FindToken(selStart);\n\t\tSyntaxNode firstNode = _ToMemberDecl(tok1.Parent), lastNode = firstNode;\n\t\tif (firstNode == null) return;\n\t\tif (selEnd > selStart) {\n\t\t\tvar tok2 = cd.syntaxRoot.FindTokenOnLeftOfPosition(selEnd);\n\t\t\tif (tok2.SpanStart > tok1.SpanStart) lastNode = _ToMemberDecl(tok2.Parent) ?? firstNode;\n\t\t}\n\t\t\n\t\t//if last is `get_X`, try to include next `put_X` (or vice versa)\n\t\tif (_IsProp(lastNode, out var r1) && _ToMemberDecl(lastNode.GetLastToken().GetNextToken().Parent) is { } n1 && _IsProp(n1, out var u1) && u1.put != r1.put && u1.name[4..] == r1.name[4..]) {\n\t\t\tlastNode = n1;\n\t\t}\n\t\t//if first is `put_X`, try to include previous `get_X` (or vice versa)\n\t\tif (_IsProp(firstNode, out var r2) && _ToMemberDecl(firstNode.GetFirstToken().GetPreviousToken().Parent) is { } n2 && _IsProp(n2, out var u2) && u2.put != r2.put && u2.name[4..] == r2.name[4..]) {\n\t\t\tfirstNode = n2;\n\t\t}\n\t\tbool _IsProp(SyntaxNode n, out (string name, bool put) r) {\n\t\t\tr = default;\n\t\t\tif (n is MethodDeclarationSyntax m) { r.name = m.Identifier.Text; if (r.name.Starts(\"get_\") || (r.put = r.name.Starts(\"put_\"))) return true; }\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\t//CiUtil.PrintNode(firstNode);\n\t\t//CiUtil.PrintNode(lastNode);\n\t\t\n\t\tStringBuilder b = new();\n\t\tvar replRange = (start: firstNode.SpanStart, end: lastNode.Span.End);\n\t\tint written = replRange.start;\n\t\t\n\t\tif (firstNode is MethodDeclarationSyntax mds) {\n\t\t\tif (lastNode is not MethodDeclarationSyntax || firstNode.Parent != lastNode.Parent || firstNode.Parent is not InterfaceDeclarationSyntax ids2) { _PrintErrorBadSelection(); return; }\n\t\t\tif (firstNode == lastNode) _Methods([mds]);\n\t\t\telse _Methods(ids2.Members.OfType<MethodDeclarationSyntax>().SkipWhile(o => o != firstNode).TakeWhile(o => { if (lastNode == null) return false; if (o == lastNode) lastNode = null; return true; }).ToArray());\n\t\t} else if (firstNode is InterfaceDeclarationSyntax ids) {\n\t\t\tif (lastNode is not InterfaceDeclarationSyntax || firstNode.Parent != lastNode.Parent) { _PrintErrorBadSelection(); return; }\n\t\t\tif (firstNode == lastNode) {\n\t\t\t\t_Interface(ids);\n\t\t\t} else {\n\t\t\t\tbool found = false;\n\t\t\t\tforeach (var v in _GetDescendantInterfaces(firstNode.Parent)) {\n\t\t\t\t\tif (!found) { if (v == firstNode) found = true; else continue; }\n\t\t\t\t\t_Interface(v);\n\t\t\t\t\tif (v == lastNode) break;\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (firstNode is TypeDeclarationSyntax or BaseNamespaceDeclarationSyntax) {\n\t\t\tif (lastNode != firstNode) { _PrintErrorBadSelection(); return; }\n\t\t\tforeach (var v in _GetDescendantInterfaces(firstNode)) {\n\t\t\t\t_Interface(v);\n\t\t\t}\n\t\t} else {\n\t\t\t_PrintErrorBadSelection();\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tif (b.Length > 0) {\n\t\t\tb.Append(code, written, replRange.end - written);\n\t\t\t//print.it(b);\n\t\t\tdoc.EReplaceTextGently(replRange.start, replRange.end, b.ToString());\n\t\t}\n\t\t\n\t\tstatic void _PrintErrorBadSelection() {\n\t\t\tprint.it(\"\"\"\nTo convert COM interface methods, please click or select one of:\n- One or more methods in a COM interface definition.\n- One or more COM interface definitions; they can be in a class, struct or namespace.\n- A class, struct or namespace that contains COM interface definitions.\n\"\"\");\n\t\t}\n\t\t\n\t\tstatic MemberDeclarationSyntax _ToMemberDecl(SyntaxNode n) {\n\t\t\treturn n?.FirstAncestorOrSelf<MemberDeclarationSyntax>();\n\t\t}\n\t\t\n\t\tstatic IEnumerable<InterfaceDeclarationSyntax> _GetDescendantInterfaces(SyntaxNode parent) {\n\t\t\treturn parent.DescendantNodes(o => o is TypeDeclarationSyntax or BaseNamespaceDeclarationSyntax or CompilationUnitSyntax).OfType<InterfaceDeclarationSyntax>();\n\t\t}\n\t\t\n\t\tvoid _Interface(InterfaceDeclarationSyntax ids) {\n\t\t\t_Methods(ids.Members.OfType<MethodDeclarationSyntax>().ToArray());\n\t\t}\n\t\t\n\t\tvoid _Methods(MethodDeclarationSyntax[] a) {\n\t\t\tfor (int i = 0; i < a.Length; i++) {\n\t\t\t\tvar m = a[i];\n\t\t\t\tif (!_IsPreserveSig(m)) continue;\n\t\t\t\tb.Append(code, written, m.SpanStart - written);\n\t\t\t\tstring name = m.Identifier.Text, modifiers = m.Modifiers.ToString();\n\t\t\t\t//is property?\n\t\t\t\tif (_IsProperty(m, out bool isPut, out string type)) {\n\t\t\t\t\t//is read-write property?\n\t\t\t\t\tMethodDeclarationSyntax m2 = null;\n\t\t\t\t\tbool isPair = i < a.Length - 1 && _IsPreserveSig(m2 = a[i + 1])\n\t\t\t\t\t\t&& m2.Identifier.Text is { } name2 && name2.Starts(isPut ? \"get_\" : \"put_\") && name2.AsSpan(4).Eq(name.AsSpan(4))\n\t\t\t\t\t\t&& _IsProperty(m2, out bool isPut2, out string type2) && isPut2 != isPut\n\t\t\t\t\t\t&& type2 == type\n\t\t\t\t\t\t&& m2.Modifiers.ToString() == modifiers\n\t\t\t\t\t\t&& m2.GetFirstToken().GetPreviousToken().Parent == m;\n\t\t\t\t\t\n\t\t\t\t\tif (!modifiers.NE()) b.Append(modifiers).Append(' ');\n\t\t\t\t\tname = name[4..]; if (SyntaxFacts.IsReservedKeyword(SyntaxFacts.GetKeywordKind(name))) name = '@' + name;\n\t\t\t\t\tb.Append(type).Append(' ').Append(name).Append(isPut ? \" { set; \" : \" { get; \");\n\t\t\t\t\t\n\t\t\t\t\tif (isPair) {\n\t\t\t\t\t\tb.Append(isPut ? \"get; }\" : \"set; }\");\n\t\t\t\t\t\twritten = m2.Span.End;\n\t\t\t\t\t\ti++;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tb.Append('}');\n\t\t\t\t\t\twritten = m.Span.End;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t//is last parameter `out`?\n\t\t\t\t\tvar ap = m.ParameterList.Parameters;\n\t\t\t\t\tif (ap.Any() && ap.Last() is var p && p.Modifiers.ToString() is \"out\" && _ParamAttr(p, out string marshal)) {\n\t\t\t\t\t\tif (marshal != null) b.Append($\"[return: {marshal}] \");\n\t\t\t\t\t\tif (!modifiers.NE()) b.Append(modifiers).Append(' ');\n\t\t\t\t\t\tb.Append(_ParamType(p)).Append(' ');\n\t\t\t\t\t\tint n = m.ParameterList.Parameters.Count;\n\t\t\t\t\t\tvar span = n == 1 ? m.ParameterList.OpenParenToken.Span : m.ParameterList.Parameters[n - 2].Span;\n\t\t\t\t\t\tb.Append(code.AsSpan(m.Identifier.SpanStart..span.End)).Append(\");\");\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (!modifiers.NE()) b.Append(modifiers).Append(' ');\n\t\t\t\t\t\tb.Append(\"void \").Append(code.AsSpan(m.Identifier.SpanStart..m.Span.End));\n\t\t\t\t\t}\n\t\t\t\t\twritten = m.Span.End;\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tstatic bool _IsPreserveSig(MethodDeclarationSyntax m) {\n\t\t\t\treturn m.ReturnType.ToString() == \"int\"\n\t\t\t\t\t&& m.AttributeLists.Count == 1 && m.AttributeLists[0].Attributes.ToString() is \"PreserveSig\" or \"PreserveSigAttribute\"\n\t\t\t\t\t&& m.Body == null && m.ExpressionBody == null && m.Arity == 0;\n\t\t\t}\n\t\t\t\n\t\t\tstatic bool _IsProperty(MethodDeclarationSyntax m, out bool put, out string type) {\n\t\t\t\tput = false; type = null;\n\t\t\t\tif (m.ParameterList.Parameters.Count == 1) {\n\t\t\t\t\tstring name = m.Identifier.Text;\n\t\t\t\t\tif (name.Starts(\"get_\") || (put = name.Starts(\"put_\"))) {\n\t\t\t\t\t\tvar p = m.ParameterList.Parameters.Last();\n\t\t\t\t\t\tif (put ? p.Modifiers.Count == 0 : p.Modifiers.ToString() is \"out\" or \"ref\") {\n\t\t\t\t\t\t\tif (_ParamAttr(p, out var marshal) && marshal == null) { //info: C# does not allow to set [MarshalAs] on properties and `set`/`get`; with `return` too.\n\t\t\t\t\t\t\t\ttype = _ParamType(p);\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t\n\t\t\tstatic string _ParamType(ParameterSyntax p) => p.Type.ToString().Replace(\" \", \"\");\n\t\t\t\n\t\t\tstatic bool _ParamAttr(ParameterSyntax p, out string marshal) {\n\t\t\t\tmarshal = null;\n\t\t\t\tforeach (var v in p.AttributeLists) {\n\t\t\t\t\tif (v.Attributes.Count != 1) return false;\n\t\t\t\t\tvar s = v.Attributes[0].ToString();\n\t\t\t\t\tif (s.Starts(\"MarshalAs\")) marshal = s;\n\t\t\t\t\telse if (!(s is \"In\" or \"Out\")) return false;\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n}\n\n\npartial class SciCode {\n\t/// <summary>\n\t/// Replaces text without losing markers, expanding folded code, etc.\n\t/// </summary>\n\tpublic void EReplaceTextGently(string s) => _ReplaceTextGently(0, aaaLen16, s, false);\n\t\n\t/// <summary>\n\t/// Replaces range text without losing markers, expanding folded code, etc.\n\t/// </summary>\n\tpublic void EReplaceTextGently(int from, int to, string s) => _ReplaceTextGently(from, to, s, true);\n\t\n\tvoid _ReplaceTextGently(int rFrom, int rTo, string s, bool isRange) {\n\t\tint len = s.Lenn(); if (len == 0) goto gRaw;\n\t\tstring old = isRange ? aaaRangeText(true, rFrom, rTo) : aaaText;\n\t\tif (len > 5_000_000 || old.Length > 5_000_000 || old.Length == 0) goto gRaw;\n\t\tvar dmp = new Libs.DiffMatchPatch.diff_match_patch();\n\t\tvar a = dmp.diff_main(old, s, true); //the slowest part. Timeout 1 s; then a valid but smaller.\n\t\tdmp.diff_cleanupEfficiency(a);\n\t\tusing (ENewUndoAction(onUndoDontChangeCaretPos: !isRange)) {\n\t\t\tfor (int i = a.Count - 1, j = old.Length; i >= 0; i--) {\n\t\t\t\tvar d = a[i];\n\t\t\t\tif (d.operation == Libs.DiffMatchPatch.Operation.INSERT) {\n\t\t\t\t\taaaInsertText(true, j + rFrom, d.text);\n\t\t\t\t} else {\n\t\t\t\t\tj -= d.text.Length;\n\t\t\t\t\tif (d.operation == Libs.DiffMatchPatch.Operation.DELETE)\n\t\t\t\t\t\taaaDeleteRange(true, j + rFrom, j + d.text.Length + rFrom);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn;\n\t\tgRaw:\n\t\tif (isRange) aaaReplaceRange(true, rFrom, rTo, s);\n\t\telse aaaText = s;\n\t\t\n\t\t//never mind: then Undo sets position at the first replaced part (in the document it's the last, because replaces in reverse order).\n\t\t//\tAnd then Redo sets position at the last replaced part.\n\t\t//\tCould try SCI_ADDUNDOACTION.\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Edit/PanelEdit.cs",
    "content": "//#define TEST_COPYPASTE\n\nusing System.Windows;\nusing System.Windows.Controls;\n\nusing Au.Controls;\nusing static Au.Controls.Sci;\n\nnamespace LA;\n\nclass PanelEdit {\n\treadonly List<SciCode> _docs = new();\n\tSciCode _activeDoc;\n\t\n\tpublic PanelEdit() {\n\t\tP.Background = SystemColors.AppWorkspaceBrush;\n\t\tApp.Commands.BindKeysTarget(P, \"Edit\");\n\t\t_UpdateUI_IsOpen();\n\t\tUpdateUI_EditView_();\n\t}\n\t\n\tpublic Grid P { get; } = new();\n\t\n\tpublic SciCode ActiveDoc => _activeDoc;\n\t\n\t/// <summary>\n\t/// When opened new document, or switched to an open document, or closed current or all documents.\n\t/// When closing, <b>ActiveDoc</b> is null when calling event handlers.\n\t/// </summary>\n\tpublic event Action ActiveDocChanged;\n\t\n\t/// <summary>\n\t/// true if 1 or more documents are open.\n\t/// </summary>\n\tpublic bool IsOpen => _activeDoc != null;\n\t\n\t/// <summary>\n\t/// Documents that currently are open in editor.\n\t/// See also <see cref=\"FilesModel.OpenFiles\"/> (it contains these and more).\n\t/// </summary>\n\tpublic IReadOnlyList<SciCode> OpenDocs => _docs;\n\t\n\t/// <summary>\n\t/// If f is already open, unhides its control.\n\t/// Else loads f text and creates control. If fails, does not change anything.\n\t/// Hides current file's control.\n\t/// Returns false if failed to read file.\n\t/// Does not save text of previously active document.\n\t/// </summary>\n\t/// <param name=\"f\"></param>\n\t/// <param name=\"newFile\">Should be true if opening the file first time after creating.</param>\n\t/// <param name=\"focusEditor\">If null, focus later, when mouse enters editor. Ignored if editor was focused (sets focus). Also depends on <i>newFile</i>.</param>\n\t/// <param name=\"noTemplate\">New file was created with custom text (option 'replaceTemplate').</param>\n\tpublic bool Open(FileNode f, bool newFile, bool? focusEditor, bool noTemplate) {\n\t\tDebug.Assert(!App.Model.IsAlien(f));\n\t\t\n\t\tif (f == _activeDoc?.EFile) return true;\n\t\t\n\t\t//print.it(focusEditor, new StackTrace(true));\n\t\tbool focusNow = !newFile && (focusEditor == true || (_activeDoc?.AaWnd.IsFocused ?? false));\n\t\t\n\t\tvoid _ShowHideActiveDoc(bool show) {\n\t\t\tif (show) {\n\t\t\t\t_activeDoc.Visibility = Visibility.Visible;\n\t\t\t\t//Children.Add(_activeDoc);\n\t\t\t} else if (_activeDoc != null) {\n\t\t\t\t_activeDoc.Visibility = Visibility.Hidden;\n\t\t\t\t//Children.Remove(_activeDoc);\n\t\t\t\t_activeDoc.ETempRanges_HidingOrClosingActiveDoc_();\n\t\t\t}\n\t\t}\n\t\t\n\t\tvar doc = f.OpenDoc;\n\t\tif (doc != null) {\n\t\t\t_ShowHideActiveDoc(false);\n\t\t\t_activeDoc = doc;\n\t\t\t_ShowHideActiveDoc(true);\n\t\t\tdoc.EOpenDocActivated();\n\t\t\t_UpdateUI_IsOpen();\n\t\t\tUpdateUI_EditEnabled_();\n\t\t\tActiveDocChanged?.Invoke();\n\t\t} else {\n\t\t\tvar path = f.FilePath;\n\t\t\tKScintilla.aaaFileLoaderSaver fls = new();\n\t\t\ttry { fls.Load(path); }\n\t\t\tcatch (Exception ex) { print.it(\"Failed to open file. \" + ex.Message); return false; }\n\t\t\t\n\t\t\t_ShowHideActiveDoc(false);\n\t\t\tdoc = new SciCode(f, fls);\n\t\t\t_docs.Add(doc);\n\t\t\tf.OpenDoc = _activeDoc = doc;\n\t\t\tP.Children.Add(doc);\n\t\t\tdoc.EInit_(newFile, noTemplate);\n\t\t\t_UpdateUI_IsOpen();\n\t\t\tUpdateUI_EditEnabled_();\n\t\t\tActiveDocChanged?.Invoke();\n\t\t\t//CodeInfo.FileOpened(doc);\n\t\t}\n\t\t\n\t\tif (focusNow) _activeDoc.Focus();\n\t\telse if (focusEditor == null || (newFile && focusEditor == true)) {\n\t\t\t//if opens on single click, focus later, when mouse is in doc.\n\t\t\t//\tElse eg user clicks and presses Del to delete file but instead deletes char in doc text. Or wants to rename but F2 does nothing.\n\t\t\t\n\t\t\tint count = 60 * 4; //60 s timeout\n\t\t\tApp.Timer025sWhenVisible += _Timer;\n\t\t\tvoid _Timer() {\n\t\t\t\t//print.it(\"timer\");\n\t\t\t\tif (--count > 0 && f == _activeDoc?.EFile && Panels.Files.TreeControl.IsFocused) {\n\t\t\t\t\tif (wnd.fromMouse() != doc.AaWnd\n\t\t\t\t\t\t|| !Panels.Files.TreeControl.IsKeyboardFocused //editing item label\n\t\t\t\t\t\t) return;\n\t\t\t\t\tdoc.Focus();\n\t\t\t\t}\n\t\t\t\tApp.Timer025sWhenVisible -= _Timer;\n\t\t\t}\n\t\t}\n\t\t\n\t\tPanels.Find.UpdateQuickResults();\n\t\treturn true;\n\t}\n\t\n\t/// <summary>\n\t/// If f is open, closes its document and destroys its control.\n\t/// f can be any, not necessary the active document.\n\t/// Saves text before closing the active document.\n\t/// Does not show another document when closed the active document.\n\t/// </summary>\n\t/// <param name=\"f\"></param>\n\tpublic void Close(FileNode f) {\n\t\tDebug.Assert(f != null);\n\t\tSciCode doc;\n\t\tif (f == _activeDoc?.EFile) {\n\t\t\t_activeDoc.ETempRanges_HidingOrClosingActiveDoc_();\n\t\t\tApp.Model.Save.TextNowIfNeed(closingDoc: true);\n\t\t\tdoc = _activeDoc;\n\t\t\t_activeDoc = null;\n\t\t\tActiveDocChanged?.Invoke();\n\t\t} else {\n\t\t\tdoc = f.OpenDoc;\n\t\t\tif (doc == null) return;\n\t\t}\n\t\t_Close(doc);\n\t\t_docs.Remove(doc);\n\t\t_UpdateUI_IsOpen();\n\t}\n\t\n\tvoid _Close(SciCode doc) {\n\t\tClosingDoc?.Invoke(doc);\n\t\tif (doc.IsFocused) doc.IsEnabled = false; //prevent focusing the scintilla control after hiding/parking\n\t\tP.Children.Remove(doc);\n\t\t//CodeInfo.FileClosed(doc);\n\t\tdoc.EFile.OpenDoc = null;\n\t\tdoc.Dispose();\n\t}\n\t\n\t/// <summary>\n\t/// Closes all documents and destroys controls.\n\t/// Called by FilesModel.UnloadingWorkspace_.\n\t/// Note: does not update the files model and treeview; to close normally use FilesModel.CloseX.\n\t/// </summary>\n\tinternal void CloseAll_(bool saveTextIfNeed) {\n\t\t_activeDoc?.ETempRanges_HidingOrClosingActiveDoc_();\n\t\tif (saveTextIfNeed) App.Model.Save.TextNowIfNeed();\n\t\t_activeDoc = null;\n\t\tActiveDocChanged?.Invoke();\n\t\tforeach (var doc in _docs) _Close(doc);\n\t\t_docs.Clear();\n\t\t_UpdateUI_IsOpen();\n\t}\n\t\n\t/// <summary>\n\t/// When closing a document (active or not).\n\t/// Called after setting <b>ActiveDoc</b> = null, invoking <b>ActiveDocChanged</b> and saving changes (event handlers must not make new changes).\n\t/// </summary>\n\tpublic event Action<SciCode> ClosingDoc;\n\t\n\tpublic bool SaveText() {\n\t\treturn _activeDoc?.ESaveText_(false) ?? true;\n\t}\n\t\n\tpublic void SaveEditorData(bool closingDoc) {\n\t\t_activeDoc?.ESaveEditorData_(closingDoc);\n\t}\n\t\n\t//public bool IsModified => _activeDoc?.IsModified ?? false;\n\t\n\tinternal void OnAppActivated_() {\n\t\tforeach (var doc in _docs) {\n\t\t\tdoc.EFile._CheckModifiedExternally(doc);\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// If the specified file is open in <see cref=\"ActiveDoc\"/>: if <i>save</i>, saves text if need; else reloads if need.\n\t/// </summary>\n\tpublic void SyncEditorTextIfFileIs(string file, bool save) {\n\t\tif (ActiveDoc is { } doc && filesystem.more.isSameFile(file, doc.EFile.FilePath)) {\n\t\t\tif (save) {\n\t\t\t\tApp.Model.Save.TextNowIfNeed();\n\t\t\t} else {\n\t\t\t\tdoc.EFile._CheckModifiedExternally(doc);\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Enables/disables Edit and Run toolbars/menus and some other UI parts depending on whether a document is open in editor.\n\t/// </summary>\n\tvoid _UpdateUI_IsOpen() {\n\t\tbool enable = _activeDoc != null;\n\t\tif (enable != _uiDisabled_IsOpen) return;\n\t\t_uiDisabled_IsOpen = !enable;\n\t\t_editDisabled = 0;\n\t\t\n\t\tstring[] a1 = [\n\t\t\tnameof(Menus.Edit),\n\t\t\tnameof(Menus.Code),\n\t\t\tnameof(Menus.TT.Toolbar_trigger),\n\t\t\tnameof(Menus.TT.Script_launchers),\n\t\t\tnameof(Menus.TT.Find_triggers),\n\t\t\tnameof(Menus.Run.Run_script),\n\t\t\tnameof(Menus.Run.Compile),\n\t\t\tnameof(Menus.Run.Debug_run),\n\t\t\tnameof(Menus.Run.Publish),\n\t\t\tnameof(Menus.Run.Run_in_PiP),\n\t\t\t//nameof(Menus.Run.Debugger),\n\t\t\t//and not Properties, Rename, Delete, More, because users can right-click in the Files panel\n\t\t\t];\n\t\tforeach (string v in a1) if (v!=null) App.Commands[v].Enable(enable);\n\t}\n\tbool _uiDisabled_IsOpen;\n\t\n\t/// <summary>\n\t/// Enables/disables commands (toolbar buttons, menu items) depending on document state such as \"can undo\".\n\t/// Called on SCN_UPDATEUI.\n\t/// </summary>\n\tinternal void UpdateUI_EditEnabled_() {\n\t\t_EUpdateUI disable = 0;\n\t\tvar d = _activeDoc;\n\t\tif (d == null) return; //we disable the toolbar and menu\n\t\tif (0 == d.Call(SCI_CANUNDO)) disable |= _EUpdateUI.Undo;\n\t\tif (0 == d.Call(SCI_CANREDO)) disable |= _EUpdateUI.Redo;\n\t\tif (!d.aaaHasSelection) disable |= _EUpdateUI.Copy;\n\t\tif (disable.Has(_EUpdateUI.Copy) || d.aaaIsReadonly) disable |= _EUpdateUI.Cut;\n\t\t//if(0 == d.Call(SCI_CANPASTE)) disable |= EUpdateUI.Paste; //rejected. Often slow. Also need to see on focused etc.\n\t\t\n\t\tvar dif = disable ^ _editDisabled;\n\t\t//print.it(dif);\n\t\tif (dif == 0) return;\n\t\t\n\t\t_editDisabled = disable;\n\t\tif (dif.Has(_EUpdateUI.Undo)) App.Commands[nameof(Menus.Edit.UndoRedo.Undo)].Enable(!disable.Has(_EUpdateUI.Undo));\n\t\tif (dif.Has(_EUpdateUI.Redo)) App.Commands[nameof(Menus.Edit.UndoRedo.Redo)].Enable(!disable.Has(_EUpdateUI.Redo));\n\t\tif (dif.Has(_EUpdateUI.Cut)) App.Commands[nameof(Menus.Edit.Clipboard.Cut)].Enable(!disable.Has(_EUpdateUI.Cut));\n\t\tif (dif.Has(_EUpdateUI.Copy)) App.Commands[nameof(Menus.Edit.Clipboard.Copy)].Enable(!disable.Has(_EUpdateUI.Copy));\n\t\t//if(dif.Has(EUpdateUI.Paste)) App.Commands[nameof(Menus.Edit.Paste)].Enable(!disable.Has(EUpdateUI.Paste));\n\t\t\n\t}\n\t_EUpdateUI _editDisabled;\n\t\n\tinternal void UpdateUI_EditView_() {\n\t\tApp.Commands[nameof(Menus.Edit.View.Wrap_lines)].Checked = App.Settings.edit_wrap;\n\t\tApp.Commands[nameof(Menus.Edit.View.Images_in_code)].Checked = !App.Settings.edit_noImages;\n\t}\n\t\n\t[Flags]\n\tenum _EUpdateUI {\n\t\tUndo = 1,\n\t\tRedo = 2,\n\t\tCut = 4,\n\t\tCopy = 8,\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Edit/Sci-DD.cs",
    "content": "using static Au.Controls.Sci;\nusing System.Windows;\nusing ToolLand;\n\nnamespace LA;\n\npartial class SciCode {\n\tvoid _InitDragDrop() {\n\t\tApi.RevokeDragDrop(AaWnd); //of Scintilla\n\t\tApi.RegisterDragDrop(AaWnd, _ddTarget = new _DragDrop(this));\n\t\t//Scintilla will call RevokeDragDrop when destroying window\n\t}\n\t\n\t_DragDrop _ddTarget;\n\t\n\tclass _DragDrop : Api.IDropTarget {\n\t\treadonly SciCode _sci;\n\t\tDDData _data;\n\t\tbool _canDrop, _justText;\n\t\t\n\t\tpublic _DragDrop(SciCode sci) { _sci = sci; }\n\t\t\n\t\tvoid Api.IDropTarget.DragEnter(System.Runtime.InteropServices.ComTypes.IDataObject d, int grfKeyState, POINT pt, ref int effect) {\n\t\t\t_data = default;\n\t\t\t_justText = false;\n\t\t\tif (_canDrop = _data.GetData(d, getFileNodes: true)) {\n\t\t\t\t_justText = _data.text != null && _data.linkName == null;\n\t\t\t}\n\t\t\teffect = _GetEffect(effect, grfKeyState);\n\t\t\tCodeInfo.Cancel();\n\t\t}\n\t\t\n\t\tunsafe void Api.IDropTarget.DragOver(int grfKeyState, POINT p, ref int effect) {\n\t\t\tif ((effect = _GetEffect(effect, grfKeyState)) != 0) {\n\t\t\t\t_GetDropPos(ref p, out _);\n\t\t\t\tvar z = new Sci_DragDropData { x = p.x, y = p.y };\n\t\t\t\t_sci.Call(SCI_DRAGDROP, 1, &z);\n\t\t\t}\n\t\t}\n\t\t\n\t\tvoid Api.IDropTarget.DragLeave() {\n\t\t\tif (_canDrop) {\n\t\t\t\t_canDrop = false;\n\t\t\t\t_sci.Call(SCI_DRAGDROP, 3);\n\t\t\t}\n\t\t}\n\t\t\n\t\tvoid Api.IDropTarget.Drop(System.Runtime.InteropServices.ComTypes.IDataObject d, int grfKeyState, POINT pt, ref int effect) {\n\t\t\tif ((effect = _GetEffect(effect, grfKeyState)) != 0) _Drop(pt, effect);\n\t\t\t_canDrop = false;\n\t\t}\n\t\t\n\t\tint _GetEffect(int effect, int grfKeyState) {\n\t\t\tif (!_canDrop) return 0;\n\t\t\tif (_sci.aaaIsReadonly) return 0;\n\t\t\tDragDropEffects r, ae = (DragDropEffects)effect;\n\t\t\tvar ks = (DragDropKeyStates)grfKeyState;\n\t\t\tswitch (ks & (DragDropKeyStates.ShiftKey | DragDropKeyStates.ControlKey | DragDropKeyStates.AltKey)) {\n\t\t\tcase 0: r = DragDropEffects.Move; break;\n\t\t\tcase DragDropKeyStates.ControlKey: r = DragDropEffects.Copy; break;\n\t\t\tdefault: return 0;\n\t\t\t}\n\t\t\tif (_data.text != null) r = 0 != (ae & r) ? r : ae;\n\t\t\telse if (0 != (ae & DragDropEffects.Link)) r = DragDropEffects.Link;\n\t\t\telse if (0 != (ae & DragDropEffects.Copy)) r = DragDropEffects.Copy;\n\t\t\telse r = ae;\n\t\t\treturn (int)r;\n\t\t}\n\t\t\n\t\tvoid _GetDropPos(ref POINT p, out int pos) {\n\t\t\t_sci.AaWnd.MapScreenToClient(ref p);\n\t\t\tif (!_justText) { //if files etc, drop as lines, not anywhere\n\t\t\t\tpos = _sci.Call(SCI_POSITIONFROMPOINT, p.x, p.y);\n\t\t\t\tpos = _sci.aaaLineStartFromPos(false, pos);\n\t\t\t\tp.x = _sci.Call(SCI_POINTXFROMPOSITION, 0, pos);\n\t\t\t\tp.y = _sci.Call(SCI_POINTYFROMPOSITION, 0, pos);\n\t\t\t} else pos = 0;\n\t\t}\n\t\t\n\t\tvoid _Drop(POINT xy, int effect) {\n\t\t\t//activate the window. Most apps activate self on drop. Also eg the Windows 11 Drag Tray may activate it and close the menu.\n\t\t\tvar wAct = _sci.AaWnd.Window;\n\t\t\tif (!wAct.IsActive) wAct.ActivateL();\n\t\t\t\n\t\t\t_GetDropPos(ref xy, out _);\n\t\t\tvar z = new Sci_DragDropData { x = xy.x, y = xy.y };\n\t\t\tstring s = null;\n\t\t\tvar b = new StringBuilder();\n\t\t\tbool isCodeFile = _sci.EFile.IsCodeFile;\n\t\t\tAction finalAction = null;\n\t\t\t\n\t\t\tif (_justText) {\n\t\t\t\ts = _data.text;\n\t\t\t} else {\n\t\t\t\tunsafe { _sci.Call(SCI_DRAGDROP, 2, &z); } //just hides the drag indicator and sets caret position\n\t\t\t\t\n\t\t\t\tif (_data.scripts) {\n\t\t\t\t\tif (Panels.Files.TreeControl.DragDropFiles is { } a) {\n\t\t\t\t\t\tint what = 0;\n\t\t\t\t\t\tif (isCodeFile) {\n\t\t\t\t\t\t\tvar sm = \"1 string s = name;|2 string s = path;\";\n\t\t\t\t\t\t\tif (a.All(o => o.IsCodeFile)) {\n\t\t\t\t\t\t\t\tsm += \"|3 script.run(name);|4 t[name] = o => script.run(name);\";\n\t\t\t\t\t\t\t\tif (a.All(o => o.IsClass)) {\n\t\t\t\t\t\t\t\t\tsm += a.All(o => o.GetClassFileRole() == FNClassFileRole.Library) ? \"|101 /* pr name; */\" : \"|100 /* c name; */\";\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tsm += \"|102 /* resource path; */\";\n\t\t\t\t\t\t\t\tsm += \"|103 /* file path; */\";\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\twhat = popupMenu.showSimple(sm);\n\t\t\t\t\t\t\tif (what == 0) return;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (what >= 100) { //meta comment\n\t\t\t\t\t\t\tMetaCommentsParser m = new(_sci.EFile) { };\n\t\t\t\t\t\t\tvar list = what switch { 100 => m.c, 101 => m.pr, 102 => m.resource, _ => m.file };\n\t\t\t\t\t\t\tforeach (var v in a) list.Add(v.ItemPathOrName(relativeTo: _sci.EFile));\n\t\t\t\t\t\t\tm.Apply();\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfor (int i = 0; i < a.Length; i++)\n\t\t\t\t\t\t\t_AppendScriptOrLink(what, what is 3 or 4 ? a[i].ItemPathOrName() : a[i].ItemPath, a[i].Name, i + 1, a[i]);\n\t\t\t\t\t}\n\t\t\t\t} else if (_data.files != null || _data.shell != null) {\n\t\t\t\t\tstring[] files = null, names = null;\n\t\t\t\t\tfiles = _data.shell != null ? _GetShell(_data.shell, out names) : _data.files;\n\t\t\t\t\tif (isCodeFile) {\n\t\t\t\t\t\tvar what = PathInfo.InsertCodeMenu(files, _sci.AaWnd);\n\t\t\t\t\t\tif (what == 0) return;\n\t\t\t\t\t\tfor (int i = 0; i < files.Length; i++) {\n\t\t\t\t\t\t\tif (i > 0) b.AppendLine();\n\t\t\t\t\t\t\tvar k = new PathInfo(files[i], names?[i]);\n\t\t\t\t\t\t\tb.Append(k.FormatCode(what, i + 1));\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tfor (int i = 0; i < files.Length; i++) {\n\t\t\t\t\t\t\tif (i > 0) b.AppendLine();\n\t\t\t\t\t\t\tb.Append(files[i]);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else if (_data.linkName != null) {\n\t\t\t\t\tint what = 0;\n\t\t\t\t\tif (isCodeFile) {\n\t\t\t\t\t\twhat = popupMenu.showSimple(\"11 string s = URL;|12 run.it(URL);|13 t[name] = o => run.it(URL);\");\n\t\t\t\t\t\tif (what == 0) return;\n\t\t\t\t\t}\n\t\t\t\t\tfinalAction = _AppendScriptOrLink(what, _data.text, _GetLinkName(_data.linkName));\n\t\t\t\t}\n\t\t\t\ts = b.ToString();\n\t\t\t}\n\t\t\t\n\t\t\tif (!s.NE()) {\n\t\t\t\tif (_justText) { //a simple drag-drop inside scintilla or text-only from outside\n\t\t\t\t\tvar s8 = Encoding.UTF8.GetBytes(s);\n\t\t\t\t\tunsafe {\n\t\t\t\t\t\tfixed (byte* p8 = s8) {\n\t\t\t\t\t\t\tz.text = p8;\n\t\t\t\t\t\t\tz.len = s8.Length;\n\t\t\t\t\t\t\tif (0 == ((DragDropEffects)effect & DragDropEffects.Move)) z.copy = 1;\n\t\t\t\t\t\t\tusing (new CodeInfo.PastingEtc(_sci))\n\t\t\t\t\t\t\t\t_sci.Call(SCI_DRAGDROP, 2, &z);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else { //file, script or URL\n\t\t\t\t\tif (isCodeFile) InsertCode.Statements(new(s, noFocus: true));\n\t\t\t\t\telse if (!_sci.aaaIsReadonly) _sci.aaaReplaceSel(s + \"\\r\\n\");\n\t\t\t\t\telse print.it(s);\n\t\t\t\t}\n\t\t\t\tif (!_sci.IsFocused && _sci.AaWnd.Window.IsActive) { //note: don't activate window; let the drag source do it, eg Explorer activates on drag-enter.\n\t\t\t\t\t_sci._noModelEnsureCurrentSelected = true; //don't scroll treeview to currentfile\n\t\t\t\t\t_sci.Focus();\n\t\t\t\t\t_sci._noModelEnsureCurrentSelected = false;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (_justText) _sci.Call(SCI_DRAGDROP, 3);\n\t\t\t}\n\t\t\t\n\t\t\tfinalAction?.Invoke();\n\t\t\t\n\t\t\tAction _AppendScriptOrLink(int what, string path, string name, int index = 0, FileNode fn = null) {\n\t\t\t\tif (b.Length > 0) b.AppendLine();\n\t\t\t\tif (what == 0) {\n\t\t\t\t\tb.Append(path);\n\t\t\t\t} else {\n\t\t\t\t\tif (what == 4) name = name.RemoveSuffix(\".cs\");\n\t\t\t\t\tname = name.Escape();\n\t\t\t\t\t\n\t\t\t\t\tif (what is 4 or 13) {\n\t\t\t\t\t\tvar t = CiUtil.GetNearestLocalVariableOfType(\"Au.toolbar\", \"Au.popupMenu\");\n\t\t\t\t\t\tb.Append($\"{t?.Name ?? \"t\"}[\\\"{name}\\\"] = o => \");\n\t\t\t\t\t} else if (what is 1 or 2 or 11) {\n\t\t\t\t\t\tb.Append($\"string s{index} = \");\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tb.Append(what switch {\n\t\t\t\t\t\t1 => $\"\\\"{name}\\\";\",\n\t\t\t\t\t\t2 or 11 => $\"@\\\"{path}\\\";\",\n\t\t\t\t\t\t3 or 4 => $\"script.run(@\\\"{path}\\\");\",\n\t\t\t\t\t\t_ => $\"run.it(@\\\"{path}\\\");\"\n\t\t\t\t\t});\n\t\t\t\t\t\n\t\t\t\t\tif (what == 13) { //favicon\n\t\t\t\t\t\tint dropPos = _sci.aaaCurrentPos16;\n\t\t\t\t\t\treturn async () => { //this code will run after inserting the statement\n\t\t\t\t\t\t\tstring text1 = _sci.aaaText, url = $\"https://www.google.com/s2/favicons?domain={path}\";\n\t\t\t\t\t\t\tvar pngBytes = await Task.Run(() => internet.http.TryGet(out var r, url) && r.Content.Headers.ContentType.MediaType == \"image/png\" ? r.Bytes() : null);\n\t\t\t\t\t\t\tif (pngBytes != null && Panels.Editor.ActiveDoc == _sci && _sci.aaaText == text1 && text1.RxMatch(@\"\\G.+?\"\"(\\]) = o => run\\.it\\(\", 1, out RXGroup g, range: dropPos..)) {\n\t\t\t\t\t\t\t\t_sci.aaaInsertText(true, g.Start, $\", @\\\"image:{Convert.ToBase64String(pngBytes)}\\\"\");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\t\n\t\t\tstatic unsafe string[] _GetShell(byte[] b, out string[] names) {\n\t\t\t\tfixed (byte* p = b) {\n\t\t\t\t\tint* pi = (int*)p;\n\t\t\t\t\tint n = *pi++;\n\t\t\t\t\tvar paths = new string[n];\n\t\t\t\t\tnames = new string[n];\n\t\t\t\t\tif (n > 0) {\n\t\t\t\t\t\tIntPtr pidlFolder = (IntPtr)(p + *pi++);\n\t\t\t\t\t\tfor (int i = 0; i < n; i++) {\n\t\t\t\t\t\t\tusing var pidl = new Pidl(pidlFolder, (IntPtr)(p + pi[i]));\n\t\t\t\t\t\t\t//if from Winstore apps folder, get \"shell:...\"\n\t\t\t\t\t\t\tif (folders.shell.pidlApps_Win8.ValueEquals(pidlFolder)) {\n\t\t\t\t\t\t\t\t//var s11 = pidl.ToShellString(SIGDN.DESKTOPABSOLUTEPARSING); //same as PARENTRELATIVEPARSING, not full\n\t\t\t\t\t\t\t\tvar s1 = pidl.ToShellString(SIGDN.PARENTRELATIVEPARSING);\n\t\t\t\t\t\t\t\tif (s1 != null && !s1.Starts('{') && s1.Contains('!')) paths[i] = @\"shell:AppsFolder\\\" + s1;\n\t\t\t\t\t\t\t\t//if (s1 != null) {\n\t\t\t\t\t\t\t\t//\tif (s1.Starts('{')) paths[i] = s1; //like \"{1AC14E77-02E7-4E5D-B744-2EB1AE5198B7}\\charmap.exe\". run.it fails etc. Cannot get path. The ITEMIDLIST is insanely long.\n\t\t\t\t\t\t\t\t//\telse if (s1.Contains('!')) paths[i] = @\"shell:AppsFolder\\\" + s1;\n\t\t\t\t\t\t\t\t//}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tpaths[i] ??= pidl.ToString();\n\t\t\t\t\t\t\tnames[i] = pidl.ToShellString(SIGDN.NORMALDISPLAY);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn paths;\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tstatic unsafe string _GetLinkName(byte[] b) {\n\t\t\t\tif (b.Length != 596) return null; //sizeof(FILEGROUPDESCRIPTORW) with 1 FILEDESCRIPTORW\n\t\t\t\tfixed (byte* p = b) { //FILEGROUPDESCRIPTORW\n\t\t\t\t\tif (*(int*)p != 1) return null; //count of FILEDESCRIPTORW\n\t\t\t\t\tvar s = new string((char*)(p + 76));\n\t\t\t\t\tif (!s.Ends(\".url\", true)) return null;\n\t\t\t\t\treturn s[..^4];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Edit/Sci-TR.cs",
    "content": "//#define TRACE_TEMP_RANGES\n\nusing Au.Controls;\nusing static Au.Controls.Sci;\n\nnamespace LA;\n\npartial class SciCode : KScintilla {\n\t[Flags]\n\tpublic enum TempRangeFlags {\n\t\t/// <summary>\n\t\t/// Call onLeave etc when current position != current end of range.\n\t\t/// </summary>\n\t\tLeaveIfPosNotAtEndOfRange = 1,\n\t\t\n\t\t/// <summary>\n\t\t/// Call onLeave etc when range text modified.\n\t\t/// </summary>\n\t\tLeaveIfRangeTextModified = 2,\n\t\t\n\t\t/// <summary>\n\t\t/// Don't add new range if already exists a range with same current from, to, owner and flags. Then returns that range.\n\t\t/// </summary>\n\t\tNoDuplicate = 4,\n\t}\n\t\n\tpublic interface ITempRange {\n\t\t/// <summary>\n\t\t/// Removes this range from the collection of ranges of the document.\n\t\t/// Optional. Temp ranges are automatically removed sooner or later.\n\t\t/// Does nothing if already removed.\n\t\t/// </summary>\n\t\tvoid Remove();\n\t\t\n\t\t/// <summary>\n\t\t/// Gets current start and end positions of this range added with <see cref=\"ETempRanges_Add\"/>.\n\t\t/// Returns false if the range is removed; then sets from = to = -1.\n\t\t/// </summary>\n\t\tbool GetCurrentFromTo(out int from, out int to, bool utf8 = false);\n\t\t\n\t\t/// <summary>\n\t\t/// Gets current start position of this range added with <see cref=\"ETempRanges_Add\"/>. UTF-16.\n\t\t/// Returns -1 if the range is removed.\n\t\t/// </summary>\n\t\tint CurrentFrom { get; }\n\t\t\n\t\t/// <summary>\n\t\t/// Gets current end position of this range added with <see cref=\"ETempRanges_Add\"/>. UTF-16.\n\t\t/// Returns -1 if the range is removed.\n\t\t/// </summary>\n\t\tint CurrentTo { get; }\n\t\t\n\t\tobject Owner { get; }\n\t\t\n\t\t/// <summary>\n\t\t/// Any data. Not used by temp range functions.\n\t\t/// </summary>\n\t\tobject OwnerData { get; set; }\n\t}\n\t\n\tclass _TempRange : ITempRange {\n\t\tSciCode _doc;\n\t\treadonly object _owner;\n\t\treadonly int _fromUtf16;\n\t\tinternal readonly int from;\n\t\tinternal int to;\n\t\tinternal readonly Action onLeave;\n\t\treadonly TempRangeFlags _flags;\n\t\t\n\t\tinternal _TempRange(SciCode doc, object owner, int fromUtf16, int fromUtf8, int toUtf8, Action onLeave, TempRangeFlags flags) {\n\t\t\t_doc = doc;\n\t\t\t_owner = owner;\n\t\t\t_fromUtf16 = fromUtf16;\n\t\t\tfrom = fromUtf8;\n\t\t\tto = toUtf8;\n\t\t\tthis.onLeave = onLeave;\n\t\t\t_flags = flags;\n\t\t}\n\t\t\n\t\tpublic void Remove() {\n\t\t\t_TraceTempRange(\"remove\", _owner);\n\t\t\tif (_doc != null) {\n\t\t\t\t_doc._tempRanges.Remove(this);\n\t\t\t\t_doc = null;\n\t\t\t}\n\t\t}\n\t\t\n\t\tinternal void Leaved() => _doc = null;\n\t\t\n\t\tpublic bool GetCurrentFromTo(out int from, out int to, bool utf8 = false) {\n\t\t\tif (_doc == null) { from = to = -1; return false; }\n\t\t\tif (utf8) {\n\t\t\t\tfrom = this.from;\n\t\t\t\tto = this.to;\n\t\t\t} else {\n\t\t\t\tfrom = _fromUtf16;\n\t\t\t\tto = CurrentTo;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\tpublic int CurrentFrom => _doc != null ? _fromUtf16 : -1;\n\t\t\n\t\tpublic int CurrentTo => _doc?.aaaPos16(to) ?? -1;\n\t\t\n\t\tpublic object Owner => _owner;\n\t\t\n\t\tpublic object OwnerData { get; set; }\n\t\t\n\t\tinternal bool MustLeave(int pos, int pos2, int modLen) {\n\t\t\treturn pos < from || pos2 > to\n\t\t\t\t|| (0 != (_flags & TempRangeFlags.LeaveIfPosNotAtEndOfRange) && pos2 != to)\n\t\t\t\t|| (0 != (_flags & TempRangeFlags.LeaveIfRangeTextModified) && modLen != 0);\n\t\t}\n\t\t\n\t\tinternal bool Contains(int pos, object owner, bool endPosition)\n\t\t\t=> (endPosition ? (pos == to) : (pos >= from || pos <= to)) && (owner == null || ReferenceEquals(owner, _owner));\n\t\t\n\t\tinternal bool Equals(int from2, int to2, object owner2, TempRangeFlags flags2) {\n\t\t\tif (from2 != from || to2 != to || flags2 != _flags\n\t\t\t\t//|| onLeave2 != onLeave //delegate always different if captured variables\n\t\t\t\t//|| !ReferenceEquals(onLeave2?.Method, onLeave2?.Method) //can be used but slow. Also tested Target, always different.\n\t\t\t\t) return false;\n\t\t\treturn ReferenceEquals(owner2, _owner);\n\t\t}\n\t\t\n\t\tpublic override string ToString() => $\"({CurrentFrom}, {CurrentTo}), owner={_owner}\";\n\t}\n\t\n\tList<_TempRange> _tempRanges = new();\n\t\n\t/// <summary>\n\t/// Marks a temporary working range of text and later notifies when it is leaved.\n\t/// Will automatically update range bounds when editing text inside it.\n\t/// Supports many ranges, possibly overlapping.\n\t/// The returned object can be used to get range info or remove it.\n\t/// Used mostly for code info, eg to cancel the completion list or signature help.\n\t/// </summary>\n\t/// <param name=\"owner\">Owner of the range. See also <see cref=\"ITempRange.OwnerData\"/>.</param>\n\t/// <param name=\"from\">Start of range, UTF-16.</param>\n\t/// <param name=\"to\">End of range, UTF-16. Can be = from.</param>\n\t/// <param name=\"onLeave\">\n\t/// Called when current position changed and is outside this range (before from or after to) or text modified outside it. Then also forgets the range.\n\t/// Called after removing the range.\n\t/// If leaved several ranges, called in LIFO order.\n\t/// Can be null.\n\t/// </param>\n\t/// <param name=\"flags\"></param>\n\tpublic ITempRange ETempRanges_Add(object owner, int from, int to, Action onLeave = null, TempRangeFlags flags = 0) {\n\t\tint fromUtf16 = from;\n\t\taaaNormalizeRange(true, ref from, ref to);\n\t\t//print.it(fromUtf16, from, to, aaaCurrentPos8);\n#if DEBUG\n\t\tif (!(aaaCurrentPos8 >= from && (flags.Has(TempRangeFlags.LeaveIfPosNotAtEndOfRange) ? aaaCurrentPos8 == to : aaaCurrentPos8 <= to))) {\n\t\t\tDebug_.Print(\"bad\");\n\t\t\tCiUtil.DebugHiliteRange(from, to);\n\t\t}\n#endif\n\t\t\n\t\tif (flags.Has(TempRangeFlags.NoDuplicate)) {\n\t\t\tfor (int i = _tempRanges.Count; --i >= 0;) {\n\t\t\t\tvar t = _tempRanges[i];\n\t\t\t\tif (t.Equals(from, to, owner, flags)) return t;\n\t\t\t}\n\t\t}\n\t\t\n\t\t_TraceTempRange(\"ADD\", owner);\n\t\tvar r = new _TempRange(this, owner, fromUtf16, from, to, onLeave, flags);\n\t\t_tempRanges.Add(r);\n\t\treturn r;\n\t}\n\t\n\t/// <summary>\n\t/// Gets ranges containing the specified position and optionally of the specified owner, in LIFO order.\n\t/// It's safe to remove the retrieved ranges while enumerating.\n\t/// </summary>\n\t/// <param name=\"position\"></param>\n\t/// <param name=\"owner\">If not null, returns only ranges where ReferenceEquals(owner, range.owner).</param>\n\t/// <param name=\"endPosition\">position must be at the end of the range.</param>\n\t/// <param name=\"utf8\"></param>\n\tpublic IEnumerable<ITempRange> ETempRanges_Enum(int position, object owner = null, bool endPosition = false, bool utf8 = false) {\n\t\tif (!utf8) position = aaaPos8(position);\n\t\tfor (int i = _tempRanges.Count; --i >= 0;) {\n\t\t\tvar r = _tempRanges[i];\n\t\t\tif (r.Contains(position, owner, endPosition)) yield return r;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Gets ranges of the specified owner, in LIFO order.\n\t/// It's safe to remove the retrieved ranges while enumerating.\n\t/// </summary>\n\t/// <param name=\"owner\">Returns only ranges where ReferenceEquals(owner, range.owner).</param>\n\tpublic IEnumerable<ITempRange> ETempRanges_Enum(object owner) {\n\t\tfor (int i = _tempRanges.Count; --i >= 0;) {\n\t\t\tvar r = _tempRanges[i];\n\t\t\tif (ReferenceEquals(owner, r.Owner)) yield return r;\n\t\t}\n\t}\n\t\n\tinternal void ETempRanges_HidingOrClosingActiveDoc_() {\n\t\tif (_tempRanges.Count == 0) return;\n\t\t//_TraceTempRange(\"CLEAR\", null);\n\t\tfor (int i = _tempRanges.Count; --i >= 0;) {\n\t\t\tvar r = _tempRanges[i];\n\t\t\t_TraceTempRange(\"leave\", r.Owner);\n\t\t\tr.Leaved();\n\t\t\tr.onLeave?.Invoke();\n\t\t}\n\t\t_tempRanges.Clear();\n\t}\n\t\n\tvoid _TempRangeOnModifiedOrPosChanged(MOD mod, int pos, int len) {\n\t\tif (_tempRanges.Count == 0) return;\n\t\tif (mod == 0) pos = aaaCurrentPos8;\n\t\tint pos2 = pos;\n\t\tif (mod.Has(MOD.SC_MOD_DELETETEXT)) { pos2 += len; len = -len; }\n\t\tList<Action> aOL = null;\n\t\tfor (int i = _tempRanges.Count; --i >= 0;) {\n\t\t\tvar r = _tempRanges[i];\n\t\t\tif (r.MustLeave(pos, pos2, len)) {\n\t\t\t\t_TraceTempRange(\"leave\", r.Owner);\n\t\t\t\t_tempRanges.RemoveAt(i);\n\t\t\t\tr.Leaved();\n\t\t\t\tif (r.onLeave is { } ol) (aOL ??= new()).Add(ol);\n\t\t\t} else {\n\t\t\t\tr.to += len;\n\t\t\t\tDebug.Assert(r.to >= r.from);\n\t\t\t}\n\t\t}\n\t\tif (aOL != null) foreach (var ol in aOL) ol();\n\t}\n\t\n\t[Conditional(\"TRACE_TEMP_RANGES\")]\n\tstatic void _TraceTempRange(string action, object owner) => print.it(action, owner);\n}\n"
  },
  {
    "path": "Au.Editor/Edit/Sci-images.cs",
    "content": "extern alias CAW;\n\n//#define SMALLER_SCREENSHOTS //smaller if /*image:...*/\n\nusing static Au.Controls.Sci;\nusing System.Drawing;\nusing System.Buffers;\nusing static Au.Controls.KImageUtil;\n\nusing Microsoft.CodeAnalysis;\nusing CAW::Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp;\nusing Microsoft.CodeAnalysis.CSharp.Syntax;\nusing Microsoft.CodeAnalysis.Text;\nusing Microsoft.CodeAnalysis.Shared.Extensions;\nusing CAW::Microsoft.CodeAnalysis.Shared.Extensions;\nusing Microsoft.CodeAnalysis.Classification;\nusing CAW::Microsoft.CodeAnalysis.Classification;\nusing CT = CAW::Microsoft.CodeAnalysis.Classification.ClassificationTypeNames;\nusing Au.Controls;\n\nnamespace LA;\n\npartial class SciCode {\n\tstruct _Image {\n\t\tpublic Bitmap image;\n\t\tpublic bool isImage; //draw frame; else icon, draw without frame\n\t\tpublic bool isComment; /*image:...*/\n\t}\n\t\n\t//fields for drawing images in margin\n\tstruct _Images {\n\t\tpublic List<_Image> a; //images retrieved by _ImagesGet on styling\n\t\tpublic Dictionary<string, Bitmap> cache; //image cache of this document. Used only for non-icon images; for icons we use the common cache.\n\t\tpublic Sci_MarginDrawCallback callback;\n\t\tpublic IntPtr callbackPtr;\n\t}\n\t_Images _im;\n\t\n\t//Called by CiStyling._StylingAndFolding.\n\tinternal void EImagesGet_(CodeInfo.Context cd, ClassifiedSpan[] a, in Sci_VisibleRange vr) {\n\t\t//if (App.Settings.edit_noImages) return; //caller does it, because it has some more work to do if need images\n\t\t//using var p1 = perf.local(); //fast when bitmaps loaded/cached\n\t\t\n\t\tif (a.Length > 0) aaaIndicatorClear(SciTheme.Indic.Images, true, a[0].TextSpan.Start..a[^1].TextSpan.End);\n\t\t\n\t\tstring code = cd.code;\n\t\tint maxWidth = 0;\n\t\tint nextLineStart = 0;\n\t\t\n\t\t//CONSIDER: prefer /*image:...*/? Now, if before is eg \"file path\", displays file icon. Or draw both somehow.\n\t\t\n\t\tfor (int i = 0; i < a.Length; i++) {\n\t\t\tif (a[i].TextSpan.Start < nextLineStart) continue; //max 1 image/line\n\t\t\tstring s;\n\t\t\tImageType imType = 0;\n\t\t\tbool isComment = false;\n\t\t\tif (_IsString(a[i], out var sr)) {\n\t\t\t\timType = _ImageTypeFromString(false, code.AsSpan(sr.start, sr.Length));\n\t\t\t\tif (imType == 0) continue;\n\t\t\t\ts = sr.ToString();\n\t\t\t} else if (i < a.Length && a[i].ClassificationType == CT.Comment) {\n\t\t\t\tvar ts = a[i].TextSpan;\n\t\t\t\tif (!code.Eq(ts.Start, \"/*\")) continue;\n\t\t\t\tint j = ts.Start + 2;\n\t\t\t\twhile (j < ts.End && code[j] <= ' ') j++;\n\t\t\t\tif (!code.Eq(j, \"image:\")) continue;\n\t\t\t\tint k = code.Find(\"*/\", j..ts.End); if (k <= j) continue;\n\t\t\t\ts = code[j..k];\n\t\t\t\timType = ImageType.Base64Image;\n\t\t\t\tisComment = true;\n\t\t\t} else if (null != (s = _IsFoldersEtc(a[i], ref i, out imType))) {\n\t\t\t\tif (imType == 0) imType = _ImageTypeFromString(true, s);\n\t\t\t}\n\t\t\tif (imType == 0) continue;\n\t\t\tBitmap b;\n\t\t\tbool isImage = imType is ImageType.Base64Image or ImageType.PngGifJpg or ImageType.Bmp or ImageType.Xaml;\n\t\t\tif (isImage) {\n\t\t\t\tif (!(_im.cache ??= new()).TryGetValue(s, out b)) {\n\t\t\t\t\ttry { b = ImageUtil.LoadGdipBitmap(s, (_dpi, null)); }\n\t\t\t\t\tcatch { b = null; }\n\t\t\t\t\t_im.cache[s] = b;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tb = IconImageCache.Common.Get(s, _dpi, imType == ImageType.XamlIconName);\n\t\t\t}\n\t\t\tif (b == null) continue;\n\t\t\t\n\t\t\tnextLineStart = code.IndexOf('\\n', a[i].TextSpan.End) + 1;\n\t\t\tif (nextLineStart == 0) nextLineStart = code.Length;\n\t\t\t\n\t\t\tint start = a[i].TextSpan.Start;\n\t\t\tint line = aaaLineFromPos(true, start), vi = Call(SCI_VISIBLEFROMDOCLINE, line);\n\t\t\tif (vi >= vr.vlineFrom && vi < vr.vlineTo && 0 != Call(SCI_GETLINEVISIBLE, line))\n\t\t\t\tmaxWidth = Math.Max(maxWidth, _ImageDisplaySize(!isImage, b, isComment).Width);\n\t\t\t\n\t\t\tif (_im.a == null) {\n\t\t\t\t_im.a = new();\n\t\t\t\tCall(SCI_INDICSETSTYLE, SciTheme.Indic.Images, INDIC_HIDDEN);\n\t\t\t\tint descent = Dpi.Scale(16, _dpi) - Call(SCI_TEXTHEIGHT) + Call(SCI_GETEXTRADESCENT) + Call(SCI_GETEXTRAASCENT);\n\t\t\t\tif (descent > 0) {\n\t\t\t\t\tbool caretVisible = AaWnd.ClientRect.Contains(0, Call(SCI_POINTYFROMPOSITION, 0, aaaCurrentPos8));\n\t\t\t\t\tCall(SCI_SETEXTRAASCENT, descent / 2);\n\t\t\t\t\tCall(SCI_SETEXTRADESCENT, descent - descent / 2);\n\t\t\t\t\t//note: later don't restore when no visible images. Then bad scrolling and can start to repeat.\n\t\t\t\t\tif (caretVisible) Call(SCI_SCROLLCARET);\n\t\t\t\t}\n\t\t\t\tif (_im.callback == null) _im.callbackPtr = Marshal.GetFunctionPointerForDelegate(_im.callback = _ImagesMarginDrawCallback);\n\t\t\t\tCall(SCI_SETMARGINDRAWCALLBACK, 1 << SciTheme.Margin.Images, _im.callbackPtr);\n\t\t\t}\n\t\t\tvar ab = _im.a;\n\t\t\tint ii;\n\t\t\tfor (ii = 0; ii < ab.Count; ii++) if (ab[ii].image == b) break;\n\t\t\tif (ii == ab.Count) ab.Add(new() { image = b, isImage = isImage, isComment = isComment });\n\t\t\t//print.it(ii, s);\n\t\t\t\n\t\t\taaaIndicatorAdd(SciTheme.Indic.Images, true, start..(start + 1), ii + 1);\n\t\t}\n\t\t\n\t\t//maxWidth is 0 if no images or if all images are in folded regions.\n\t\tif (maxWidth > 0) maxWidth = Math.Min(maxWidth, Dpi.Scale(100, _dpi)) + 8;\n\t\tvar (left, right) = aaaMarginGetX(SciTheme.Margin.Images);\n\t\t_ImagesMarginAutoWidth(right - left, maxWidth);\n\t\tif (maxWidth > 0) Api.InvalidateRect(AaWnd, new RECT(left, 0, maxWidth, short.MaxValue));\n\t\t//TODO3: draw only when need, ie when new indicators are different than old.\n\t\t//\tNow draws on each text change, eg added character, unless changes are frequent. But not too slow.\n\t\t//\tAnd probably then also draws all other margins.\n\t\t\n\t\t#region local util\n\t\t\n\t\tbool _Eq(int i, string ctype, string text = null)\n\t\t\t=> (uint)i < a.Length && a[i].ClassificationType == ctype && (text == null || code.Eq(a[i].TextSpan, text));\n\t\t\n\t\tbool _IsString(ClassifiedSpan v, out CiStringRange r) {\n\t\t\tr = default;\n\t\t\tbool verbatim = false;\n\t\t\tvar ct = v.ClassificationType;\n\t\t\tif (!(ct == CT.StringLiteral || (verbatim = ct == CT.VerbatimStringLiteral))) return false;\n\t\t\t//skip short strings and $\"string\" parts\n\t\t\tint start = v.TextSpan.Start, end = v.TextSpan.End - 1;\n\t\t\tif (verbatim && code[start++] != '@') return false;\n\t\t\tif (end - start < 3 || code[end] != '\"' || code[start++] != '\"') return false;\n\t\t\tr = new(code, start, end, verbatim);\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\tstring _IsFoldersEtc(ClassifiedSpan v, ref int i, out ImageType imageType) {\n\t\t\timageType = 0;\n\t\t\tif (_Eq(i, CT.ClassName, \"folders\")) {\n\t\t\t\tif (_Eq(++i, CT.Operator, \".\")) {\n\t\t\t\t\tint i1 = ++i;\n\t\t\t\t\tif (_Eq(i, CT.PropertyName)\n\t\t\t\t\t\t|| (_Eq(i, CT.ClassName, \"shell\") && _Eq(++i, CT.Operator, \".\") && _Eq(++i, CT.PropertyName))\n\t\t\t\t\t\t) {\n\t\t\t\t\t\tvar fp = folders.getFolder(code[a[i1].TextSpan.Start..a[i].TextSpan.End]);\n\t\t\t\t\t\tif (!fp.IsNull) return _Plus(fp, ref i);\n\t\t\t\t\t} else if (_Eq(i = i1, CT.MethodName) && _Eq(i + 1, CT.Punctuation, \"(\") && _Eq(i + 2, CT.Punctuation, \")\")) {\n\t\t\t\t\t\ti += 2;\n\t\t\t\t\t\tif (code[a[i1].TextSpan.ToRange()] == \"sourceCode\") return _Plus(folders.sourceCode(_fn.FilePath), ref i);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (_Eq(i, CT.EnumName, \"StockIcon\")) {\n\t\t\t\tif (_Eq(++i, CT.Operator, \".\") && _Eq(++i, CT.EnumMemberName))\n\t\t\t\t\tif (Enum.TryParse(code.AsSpan(a[i].TextSpan.ToRange()), out StockIcon si))\n\t\t\t\t\t\tif (icon.GetStockIconLocation_(si, out string path, out int index)) {\n\t\t\t\t\t\t\timageType = ImageType.IconLib;\n\t\t\t\t\t\t\treturn path + \",\" + index.ToS();\n\t\t\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t\t\n\t\t\tstring _Plus(FolderPath fp, ref int i) {\n\t\t\t\t//print.it(\"FOLDERS\", fp.Path);\n\t\t\t\tif (i < a.Length - 2 && (_Eq(i + 1, CT.OperatorOverloaded, \"+\") || _Eq(i + 1, CT.Operator, \"+\")) && _IsString(a[i + 2], out var r)) {\n\t\t\t\t\ti += 2;\n\t\t\t\t\treturn fp + r.ToString();\n\t\t\t\t}\n\t\t\t\treturn fp.Path;\n\t\t\t}\n\t\t}\n\t\t\n\t\tstatic ImageType _ImageTypeFromString(bool folders, RStr s/*, out int prefixLength*/) {\n\t\t\t//prefixLength = 0;\n\t\t\tif (s.Length < 2) return default;\n\t\t\t\n\t\t\t//special strings\n\t\t\tswitch (s[0]) {\n\t\t\tcase 'i' when s.StartsWith(\"image:\"):\n\t\t\t\t//prefixLength = 6;\n\t\t\t\treturn !folders && s.Length >= 10 ? ImageType.Base64Image : default;\n\t\t\tcase 'i' when s.StartsWith(\"imagefile:\"):\n\t\t\t\tif (folders) return default;\n\t\t\t\ts = s[10..];\n\t\t\t\t//prefixLength = 10;\n\t\t\t\tbreak;\n\t\t\tcase '<':\n\t\t\t\tif (s.Contains(\"xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'\", StringComparison.Ordinal)) {\n\t\t\t\t\tif (s.Contains(\"<Path \", StringComparison.Ordinal) || s.Contains(\"<GeometryDrawing \", StringComparison.Ordinal)) return ImageType.Xaml;\n\t\t\t\t}\n\t\t\t\treturn default;\n\t\t\tcase '*':\n\t\t\t\tif (DIcons.PossiblyIconName(s)) return ImageType.XamlIconName;\n\t\t\t\treturn default;\n\t\t\tcase '.':\n\t\t\t\treturn pathname.IsExtension_(s) ? ImageType.ShellIcon : default;\n\t\t\tcase ':':\n\t\t\t\treturn s[1] == ':' ? ImageType.ShellIcon : default;\n\t\t\t}\n\t\t\t\n\t\t\t//file path or URL\n\t\t\t//string expanded = null;\n\t\t\t//if (s[0] == '%') {\n\t\t\t//\texpanded = pathname.expand(s.ToString(), strict: false);\n\t\t\t//\tif (expanded.Length < 8 || expanded[0] == '%') return default;\n\t\t\t//\ts = expanded;\n\t\t\t//}\n\t\t\tif (s is ['/' or '\\\\', '/' or '\\\\', ..]) return default; //would hang for several s if string like \"//bad/network/path\", eg xpath. String like @\"\\\\...\" is dangerous here too. Also can be a mapped drive like \"N:\\path\", but at least it's probably a path string.\n\t\t\tif (pathname.isFullPath(s, orEnvVar: true)) { //is image file path?\n\t\t\t\tif (s.Length >= 8) { //can be image file. Else can be eg \"C:\\\" or \"C:\\A\".\n\t\t\t\t\tif (s[^4] == '.') {\n\t\t\t\t\t\tvar ext = s[^3..];\n\t\t\t\t\t\tif (ext.Eqi(\"png\") || ext.Eqi(\"gif\") || ext.Eqi(\"jpg\")) return ImageType.PngGifJpg;\n\t\t\t\t\t\tif (ext.Eqi(\"bmp\")) return ImageType.Bmp;\n\t\t\t\t\t\tif (ext.Eqi(\"ico\")) return ImageType.Ico;\n\t\t\t\t\t\tif (ext.Eqi(\"cur\") || ext.Eqi(\"ani\")) return ImageType.Cur;\n\t\t\t\t\t} else if (s[^1].IsAsciiDigit() && s.Contains(',')) { //can be like C:\\x.dll,10\n\t\t\t\t\t\tif (icon.parsePathIndex(s.ToString(), out _, out _)) return ImageType.IconLib;\n\t\t\t\t\t}\n\t\t\t\t} else if (s.Length < 4 && s[1] != ':') return default; //eg \"\\\\\"\n\t\t\t} else {\n\t\t\t\tif (s.Length < 4) return default;\n\t\t\t\tif (pathname.isUrl(s)) {\n\t\t\t\t\t//display icon only for known protocols that can be used with run.it(). Ignore \"web:LINK\" etc and protocols with common icon (eg \"http:\").\n\t\t\t\t\tif (!(s.Starts(\"shell:\") || s.Starts(\"ms-settings:\"))) return default;\n\t\t\t\t} else if (!s.EndsWith(\".cs\", StringComparison.OrdinalIgnoreCase)) return default;\n\t\t\t}\n\t\t\t\n\t\t\treturn ImageType.ShellIcon;\n\t\t}\n\t\t\n\t\t#endregion\n\t}\n\t\n\tunsafe void _ImagesMarginDrawCallback(ref Sci_MarginDrawCallbackData c) {\n\t\t//print.it(c.rect, c.firstLine, c.lastLine);\n\t\t//using var p1 = perf.local();\n\t\t\n\t\tint pos = aaaLineStart(false, c.firstLine) - 1, posEnd = aaaLineEnd(false, c.lastLine);\n\t\tint topVisibleLine = Call(SCI_GETFIRSTVISIBLELINE), lineH = Call(SCI_TEXTHEIGHT);\n\t\tint maxWidth = 0;\n\t\tGraphics g = null;\n\t\ttry {\n\t\t\tfor (; ; pos++) {\n\t\t\t\tpos = Call(SCI_INDICATOREND, SciTheme.Indic.Images, pos); //skip non-indicator range\n\t\t\t\tif (pos <= 0 || pos >= posEnd) break; //after the visible range or at the end of text\n\t\t\t\tint i = Call(SCI_INDICATORVALUEAT, SciTheme.Indic.Images, pos) - 1;\n\t\t\t\tif ((uint)i >= _im.a.Count) break; //should never\n\t\t\t\tint line = aaaLineFromPos(false, pos);\n\t\t\t\tif (0 == Call(SCI_GETLINEVISIBLE, line)) continue; //folded?\n\t\t\t\t\n\t\t\t\t//print.it(pos, i, line);\n\t\t\t\tvar v = _im.a[i];\n#if SMALLER_SCREENSHOTS\n\t\t\t\tbool smaller = v.isComment;\n\t\t\t\tvar z = _ImageDisplaySize(!v.isImage, v.image, smaller);\n#else\n\t\t\t\tvar z = _ImageDisplaySize(!v.isImage, v.image, false);\n#endif\n\t\t\t\tmaxWidth = Math.Max(maxWidth, z.Width);\n\t\t\t\tint x = c.rect.CenterX - z.Width / 2;\n\t\t\t\tint y = (Call(SCI_VISIBLEFROMDOCLINE, line) - topVisibleLine) * lineH;\n\t\t\t\t\n\t\t\t\tRECT r = new(x, y, z.Width, z.Height);\n\t\t\t\tif (!c.rect.IntersectsWith(r)) continue;\n\t\t\t\tif (g == null) {\n\t\t\t\t\tg = Graphics.FromHdc(c.hdc);\n\t\t\t\t\tg.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tg.IntersectClip(c.rect); //limit image width, because not clipped\n\t\t\t\t\n\t\t\t\tif (v.isImage) g.DrawRectangleInset(Color.Green, 1, r, outset: true);\n\t\t\t\tg.DrawImage(v.image, r);\n\t\t\t}\n\t\t}\n\t\tcatch (Exception ex) { Debug_.Print(ex); }\n\t\tfinally {\n\t\t\tg?.Dispose();\n\t\t}\n\t\t\n\t\t//auto-correct margin width, in case _ImagesGet not called because styling is valid.\n\t\t//\tNeed it eg after expanding/collapsing a folding containing images. Also sometimes after resizing the control or after zoom changed.\n\t\tif (maxWidth > 0) maxWidth = Math.Min(maxWidth, Dpi.Scale(100, _dpi)) + 8;\n\t\tint oldWidth = c.rect.Width;\n\t\tif (maxWidth != oldWidth)\n\t\t\tif (maxWidth > oldWidth || (c.rect.top == 0 && c.rect.bottom == AaWnd.ClientRect.bottom))\n\t\t\t\t_ImagesMarginAutoWidth(oldWidth, maxWidth);\n\t}\n\t\n\tvoid _ImagesMarginAutoWidth(int oldWidth, int width) {\n\t\tif (width == oldWidth) return;\n\t\t//when shrinking, in wrap mode could start autorepeating, when makes less lines wrapped and it uncovers wider images at the bottom and need to expand again.\n\t\t//\tTried to delay or to not change if changed recently, but not good. Never mind.\n\t\tif (width < oldWidth && App.Settings.edit_wrap) return;\n\t\tAaWnd.Post(SCI_SETMARGINWIDTHN, SciTheme.Margin.Images, width);\n\t}\n\t\n\tvoid _ImagesOnOff() {\n\t\tif (App.Settings.edit_noImages == (_im.a == null)) return;\n\t\tif (_im.a != null) {\n\t\t\tCall(SCI_SETMARGINDRAWCALLBACK);\n\t\t\tCall(SCI_SETMARGINWIDTHN, SciTheme.Margin.Images, 0);\n\t\t\tCall(SCI_SETEXTRAASCENT, 0);\n\t\t\tCall(SCI_SETEXTRADESCENT, 1);\n\t\t\taaaIndicatorClear(SciTheme.Indic.Images);\n\t\t\t_im.a = null;\n\t\t} else {\n\t\t\tif (this == Panels.Editor.ActiveDoc) CodeInfo._styling.Update();\n\t\t}\n\t}\n\t\n\tSize _ImageDisplaySize(bool isIcon16, Bitmap b, bool smaller) {\n\t\tvar z = b.Size;\n\t\tif (isIcon16) {\n\t\t\tvar k = Dpi.Scale(16, _dpi);\n\t\t\tz = new(k, k);\n\t\t} else {\n\t\t\tint hr = b.HorizontalResolution.ToInt(), vr = b.VerticalResolution.ToInt();\n\t\t\tif (hr >= 96 && vr >= 96) z = new(Math2.MulDiv(z.Width, _dpi, hr), Math2.MulDiv(b.Height, _dpi, vr));\n\t\t}\n#if SMALLER_SCREENSHOTS\n\t\tif (smaller) return new(z.Width * 3 / 4, z.Height * 3 / 4);\n#endif\n\t\treturn z;\n\t}\n\t\n\t/// <summary>\n\t/// Finds all /*image:Base64*/ and @\"image:Base64\" in scintilla text range from8..to8 (UTF-8) and sets style STYLE_HIDDEN for the Base64.\n\t/// If <i>styles</i> != null, writes STYLE_HIDDEN in <i>styles</i>, else uses SCI_STARTSTYLING/SCI_SETSTYLING.\n\t/// </summary>\n\t/// <remarks>\n\t/// Called on SCN_STYLENEEDED (to avoid bad things like briefly visible and added horizontal scrollbar) and then by CiStyling._Work (async).\n\t/// </remarks>\n\tinternal unsafe void EHideImages_(int from8, int to8, Span<byte> styles = default, [CallerMemberName] string caller = null) {\n\t\tif (styles.IsNull()) from8 = aaaLineStartFromPos(false, from8);\n\t\tif (to8 - from8 < 49) return;\n\t\t\n\t\tvar r = new Au.Controls.SciDirectRange(this, from8, to8);\n\t\tfor (int j = from8 + 2, to2 = to8 - 47; j <= to2;) {\n\t\t\tint i = j;\n\t\t\tfor (; ; i++) {\n\t\t\t\tif (i > to2) return;\n\t\t\t\tif (r[i] == 'i' && r[i + 1] == 'm' && r[i + 2] == 'a' && r[i + 3] == 'g' && r[i + 4] == 'e' && r[i + 5] == ':') break;\n\t\t\t}\n\t\t\tj = i + 6;\n\t\t\t\n\t\t\tchar c1 = r[i - 1], c2 = r[i - 2];\n\t\t\tif (!((c1 == '*' && c2 == '/') || (c1 == '\"' && c2 == '@'))) continue;\n\t\t\t\n\t\t\tint j2 = to8 - (c1 == '\"' ? 1 : 2);\n\t\t\twhile (j < j2 && r[j] is (>= 'A' and <= 'Z') or (>= 'a' and <= 'z') or (>= '0' and <= '9') or '+' or '/') j++;\n\t\t\tif (j - i < 46) continue; //match regex image:[A-Za-z0-9/+]{40,}\n\t\t\twhile (j < j2 && r[j] == '=') j++;\n\t\t\t\n\t\t\tif (r[j] != c1 || (c1 == '*' && '/' != r[j + 1])) continue;\n\t\t\tif (c1 == '\"') i += 6; else { i -= 2; j += 2; }\n\t\t\t\n\t\t\tif (!styles.IsNull()) {\n\t\t\t\tstyles.Slice(i - from8, j - i).Fill(STYLE_HIDDEN);\n\t\t\t} else {\n\t\t\t\tCall(SCI_STARTSTYLING, i);\n\t\t\t\tCall(SCI_SETSTYLING, j - i, STYLE_HIDDEN);\n\t\t\t}\n\t\t}\n\t\t\n\t\t//note: don't use SCI_SETTARGETRANGE here. In some cases scintilla does not work well with it. It may create bugs.\n\t}\n\t//Not easy to use hidden style because:\n\t//\t1. Scintilla bug: in wrap mode sometimes draws as many lines as with big font. Even caret is large and spans all lines.\n\t//\t\tPlus other anomalies, eg when scrolling.\n\t//\t\tWorkaround: at first hide all on SCN_STYLENEEDED.\n\t//\t2. User cannot delete text containing hidden text.\n\t//\t\tWorkaround: modify scintilla source in Editor::RangeContainsProtected.\n\t\n\tbool _ImageDeleteKey(KKey key) {\n\t\tif (key is KKey.Delete or KKey.Back && !base.aaaHasSelection) {\n\t\t\tint pos = base.aaaCurrentPos8, to = pos;\n\t\t\tif (key == KKey.Back) {\n\t\t\t\twhile (aaaStyleGetAt(pos - 1) == STYLE_HIDDEN) pos--;\n\t\t\t} else {\n\t\t\t\twhile (aaaStyleGetAt(to) == STYLE_HIDDEN) to++;\n\t\t\t}\n\t\t\tif (to > pos) {\n\t\t\t\tbool ok = s_imageDeleteAlways;\n\t\t\t\tif (!ok) {\n\t\t\t\t\tvar s = base.aaaRangeText(false, pos, to).Limit(50);\n\t\t\t\t\tvar c = new DControls { Checkbox = \"Remember\" };\n\t\t\t\t\tok = 1 == dialog.show(\"Delete hidden text?\", s, \"OK|Cancel\", controls: c, owner: this);\n\t\t\t\t\ts_imageDeleteAlways = ok && c.IsChecked;\n\t\t\t\t}\n\t\t\t\tif (ok) {\n\t\t\t\t\taaaDeleteRange(false, pos, to);\n\t\t\t\t\taaaAddUndoPoint();\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\tstatic bool s_imageDeleteAlways;\n\t\n\tstatic string _ImageRemoveScreenshots(string s, bool onCopy) {\n\t\t//s = s.RxReplace(@\"/\\*image:[A-Za-z0-9/+]{40,}=*\\*/\", \"\");\n\t\t//var s2 = s.RxReplace(@\"\"\"image:[A-Za-z0-9/+]{40,}=*\"\"\", @\"\"\"image:\"\"\");\n\t\t//if (s2 != s)\n\t\t//\tif (dialog.showYesNo(\"Remove all images?\", \"This will replace strings \\\"image:<hidden image data>\\\" with \\\"image:\\\" in the copied text.\")) s = s2;\n\t\t\n\t\tint n = s.RxReplace(@\"/\\*image:[A-Za-z0-9/+]{40,}=*\\*/\", \"\", out s);\n\t\tif (onCopy && n > 0) print.it($\"Info: in the copied code have been removed {n} images embedded in hidden comments. The script can run without them.\");\n\t\tn = s.RxReplace(@\"\"\"image:[A-Za-z0-9/+]{40,}=*\"\"\", @\"\"\"image:\"\"\", out s);\n\t\tif (onCopy && n > 0) print.it($\"Info: in the copied code have been removed {n} images embedded in strings \\\"image:<hidden image data>\\\". You can copy-paste the strings separately if necessary.\");\n\t\treturn s;\n\t}\n\t\n\tpublic void EImageRemoveScreenshots() {\n\t\tif (aaaIsReadonly || !_fn.IsCodeFile) return;\n\t\tbool isSel = aaaHasSelection;\n\t\tstring s = isSel ? aaaSelectedText() : aaaText;\n\t\tvar s2 = _ImageRemoveScreenshots(s, false);\n\t\tif (s2 == s) return;\n\t\tif (isSel) EReplaceTextGently(aaaSelectionStart8, aaaSelectionEnd8, s2);\n\t\telse EReplaceTextGently(s2);\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Edit/SciCode.cs",
    "content": "using Au.Controls;\nusing static Au.Controls.Sci;\nusing System.Windows;\nusing System.Windows.Input;\nusing System.Windows.Controls;\n\nusing Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp.Syntax;\n\nnamespace LA;\n\npartial class SciCode : KScintilla {\n\treadonly aaaFileLoaderSaver _fls;\n\treadonly FileNode _fn;\n\t\n\tpublic FileNode EFile => _fn;\n\t\n\tpublic override string ToString() => _fn.ToString();\n\t\n\tinternal SciCode(FileNode file, aaaFileLoaderSaver fls) {\n\t\t_fn = file;\n\t\t_fls = fls;\n\t\t\n\t\tif (fls.IsBinary) AaInitReadOnlyAlways = true;\n\t\tif (fls.IsImage) {\n\t\t\tAaInitImages = true;\n\t\t\tAaInitTagsStyle = AaTagsStyle.User;\n\t\t}\n\t\t\n\t\tName = \"document\";\n\t}\n\t\n\tprotected override void AaOnHandleCreated() {\n\t\tCall(SCI_SETMODEVENTMASK, (int)(MOD.SC_MOD_INSERTTEXT | MOD.SC_MOD_DELETETEXT | MOD.SC_MOD_BEFOREDELETE /*| MOD.SC_MOD_INSERTCHECK | MOD.SC_MOD_BEFOREINSERT*/\n\t\t\t//| MOD.SC_MOD_CHANGEFOLD //only when text modified, but not when user clicks +-\n\t\t\t));\n\t\t\n\t\taaaMarginSetType(SciTheme.Margin.Images, SC_MARGIN_COLOUR, markersMask: 0);\n\t\tCall(SCI_SETMARGINBACKN, SciTheme.Margin.Images, Api.GetSysColor(Api.COLOR_BTNFACE));\n\t\taaaMarginSetWidth(SciTheme.Margin.Images, 0);\n\t\t\n\t\taaaMarginSetType(SciTheme.Margin.Markers, SC_MARGIN_COLOUR, markersMask: 0x1FFFFFF, sensitive: true, cursorArrow: true);\n\t\taaaMarginSetWidth(SciTheme.Margin.Markers, 16);\n\t\t\n\t\taaaMarginSetType(SciTheme.Margin.LineNumbers, SC_MARGIN_NUMBER);\n\t\t\n\t\taaaMarginSetWidth(SciTheme.Margin.Changes, 10);\n\t\taaaMarginSetWidth(-1, 2);\n\t\t\n\t\tCall(SCI_SETWRAPMODE, App.Settings.edit_wrap ? SC_WRAP_WORD : 0);\n\t\tCall(SCI_SETINDENTATIONGUIDES, SC_IV_REAL);\n\t\tCall(SCI_ASSIGNCMDKEY, Math2.MakeLparam(SCK_RETURN, SCMOD_CTRL | SCMOD_SHIFT), SCI_NEWLINE);\n\t\tCall(SCI_SETEXTRADESCENT, 1); //eg to avoid drawing fold separator lines on text\n\t\tCall(SCI_SETMOUSEDWELLTIME, 500);\n\t\tCall(SCI_SETUNDOSELECTIONHISTORY, 1); //BAD: uses too much memory. Documented: >=150 / action. Tested: 270 (same when 1-char and when coalesced 10 char). Without this somehow the heap does not grow. It seems there is no easy workaround; temporarily turning off this feature clears the selection undo history.\n\t\t\n\t\tCall(SCI_SETLAYOUTTHREADS, Environment.ProcessorCount);\n//#if IDE_LA //works if don't need to draw images, else crashes because there is no HDC. Need to reimplement the drawing functions, to use ID2D1RenderTarget instead of HDC. Much work. The benefit would be antialiased drawing of Scintilla's markers etc, but we don't use them. Text looks differently. Uses + 25 MB of memory.\n//\t\tCall(SCI_SETTECHNOLOGY, SC_TECHNOLOGY_DIRECTWRITEDC);\n//#endif\n\t\t\n\t\tSciTheme.Current.ToScintilla(this);\n\t\t\n\t\t_OnHandleCreatedOrDpiChanged();\n\t\tif (_fn.IsCodeFile) CiFolding.InitFolding(this);\n\t\t_InitDragDrop();\n\t\t\n\t\t//C# interprets Unicode newline characters NEL, LS and PS as newlines.\n\t\t//\tIf editor does not support it, line numbers in errors/warnings/stacktraces may be incorrect.\n\t\t//\tVisual Studio supports it. VSCode no; when pasted, suggests to replace to normal.\n\t\t//\tScintilla supports it (SCI_SETLINEENDTYPESALLOWED) only if using C++ lexer.\n\t\t//\t\tModified: now supports by default.\n\t\t//\tAscii VT and FF are not interpreted as newlines by C# and Scintilla.\n\t}\n\t\n\tvoid _OnHandleCreatedOrDpiChanged() {\n\t\t_IndicatorsInit();\n\t\t_DefineIconMarkers();\n\t\tCall(SCI_SETXCARETPOLICY, CARET_STRICT | CARET_EVEN | CARET_SLOP, _dpi / 2);\n\t}\n\t\n\tprotected override void OnDpiChanged(DpiScale oldDpi, DpiScale newDpi) {\n\t\tbase.OnDpiChanged(oldDpi, newDpi);\n\t\tif (!AaWnd.Is0) _OnHandleCreatedOrDpiChanged();\n\t}\n\t\n\tprotected override void DestroyWindowCore(HandleRef hwnd) {\n\t\tCodeInfo._styling.DocHandleDestroyed(this);\n\t\tbase.DestroyWindowCore(hwnd);\n\t}\n\t\n\t//~SciCode() { print.it(\"~SciCode\"); } //note: never called, because Dispose (called by PanelEdit) calls GC.SuppressFinalize. To test this, call GC.ReRegisterForFinalize(this) in DestroyWindowCore.\n\t\n\t//Called by PanelEdit.Open.\n\tinternal void EInit_(bool newFile, bool noTemplate) {\n\t\tDebug.Assert(!AaWnd.Is0);\n\t\t\n\t\tbool editable = _fls.SetText(this);\n\t\tif (!EIsBinary) {\n\t\t\t_fn._UpdateFileModTime();\n\t\t\t//if (_fn.DontSave) aaaIsReadonly = true; //rejected. Never make editor readonly when opened a text file. Would need too many `if (doc.aaaIsReadonly) return;` etc everywhere.\n\t\t\tif (_fn.DontSave) print.it($\"<>Warning: Don't edit {_fn.SciLink()}. It will not be saved. It will be replaced when updating this app.\");\n\t\t}\n\t\tESetLineNumberMarginWidth_();\n\t\t\n\t\tif (newFile) _openState = noTemplate ? _EOpenState.NewFileNoTemplate : _EOpenState.NewFileFromTemplate;\n\t\telse if (App.Model.OpenFiles.Contains(_fn)) _openState = _EOpenState.Reopen;\n\t\t\n\t\tif (_fn.IsCodeFile) CiStyling.DocTextAdded();\n\t\tPanels.Bookmarks.SciLoaded(this);\n\t\tPanels.Breakpoints.SciLoaded(this);\n\t\tApp.Model.EditGoBack.OnPosChanged(this);\n\t\t\n\t\t//detect \\r without '\\n', because it is not well supported. Also NEL, LS, PS.\n\t\tif (editable) {\n\t\t\tif (_fls.DetectBadNewlines()) {\n\t\t\t\tprint.it($@\"<>Note: text of {_fn.Name} contains unusual line end characters. <+badNL s>Show<>, <+badNL h>hide<>, <+badNL f>fix<>.\");\n\t\t\t\tif (!s_badNewlines) {\n\t\t\t\t\ts_badNewlines = true;\n\t\t\t\t\tPanels.Output.Scintilla.AaTags.AddLinkTag(\"+badNL\", static s1 => {\n\t\t\t\t\t\tvar doc = Panels.Editor.ActiveDoc;\n\t\t\t\t\t\tif (doc != null) {\n\t\t\t\t\t\t\tif (s1.Starts('f')) {\n\t\t\t\t\t\t\t\tif (!doc.aaaIsReadonly) doc.aaaText = doc.aaaText.ReplaceLineEndings();\n\t\t\t\t\t\t\t\t//info: ReplaceLineEndings also replaces single \\n and FF.\n\t\t\t\t\t\t\t\t//\tSCI_CONVERTEOLS ignores NEL, LS, PS.\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tdoc.Call(SCI_SETVIEWEOL, s1.Starts('s') ? 1 : 0);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\t_fls.FinishedLoading();\n\t}\n\tstatic bool s_badNewlines;\n\t\n\tinternal void EOpenDocActivated() {\n\t\t_fn._CheckModifiedExternally(this);\n\t\tApp.Model.EditGoBack.OnPosChanged(this);\n\t}\n\t\n\tprotected override void AaOnSciNotify(ref SCNotification n) {\n\t\tbool isActive = this == Panels.Editor.ActiveDoc;\n\t\t//if (isActive) {\n\t\t//\t//if (test_) {\n\t\t//\t\tswitch (n.nmhdr.code) {\n\t\t//\t\tcase NOTIF.SCN_UPDATEUI:\n\t\t//\t\tcase NOTIF.SCN_NEEDSHOWN:\n\t\t//\t\tcase NOTIF.SCN_PAINTED:\n\t\t//\t\tcase NOTIF.SCN_FOCUSIN:\n\t\t//\t\tcase NOTIF.SCN_FOCUSOUT:\n\t\t//\t\tcase NOTIF.SCN_DWELLSTART:\n\t\t//\t\tcase NOTIF.SCN_DWELLEND:\n\t\t//\t\t\tbreak;\n\t\t//\t\tcase NOTIF.SCN_MODIFIED:\n\t\t//\t\t\tprint.it(n.nmhdr.code, n.modificationType);\n\t\t//\t\t\tbreak;\n\t\t//\t\tdefault:\n\t\t//\t\t\tprint.it(n.nmhdr.code);\n\t\t//\t\t\tbreak;\n\t\t//\t\t}\n\t\t//\t//}\n\t\t//} else {\n\t\t//\t//#if DEBUG\n\t\t//\t//\t\t\tif (n.code is not (NOTIF.SCN_FOCUSOUT or NOTIF.SCN_DWELLEND\n\t\t//\t//\t\t\t\tor NOTIF.SCN_SAVEPOINTLEFT or NOTIF.SCN_SAVEPOINTREACHED or NOTIF.SCN_MODIFIED or NOTIF.SCN_UPDATEUI or NOTIF.SCN_STYLENEEDED\n\t\t//\t//\t\t\t\t)) Debug_.Print($\"AaOnSciNotify in background, {_fn}, {n.nmhdr.code}\");\n\t\t//\t//#endif\n\t\t//}\n\t\t\n\t\tswitch (n.code) {\n\t\tcase NOTIF.SCN_MODIFIED:\n\t\t\t//print.it(\"SCN_MODIFIED\", n.modificationType, n.position, n.FinalPosition, aaaCurrentPos8, n.Text);\n\t\t\tif (n.modificationType.HasAny(MOD.SC_MOD_INSERTTEXT | MOD.SC_MOD_DELETETEXT)) {\n\t\t\t\tApp.Model.Save.TextLater(); //just compares/sets a field. Note: don't use SCN_SAVEPOINTLEFT. No SCN_SAVEPOINTLEFT if text modified externally. Then would not save subsequent changes in editor.\n\t\t\t\t_modified = true;\n\t\t\t\t_TempRangeOnModifiedOrPosChanged(n.modificationType, n.position, n.length);\n\t\t\t\t_ManageUndoOnModified(n.modificationType);\n\t\t\t\tSnippetMode_?.SciModified(n);\n\t\t\t\tif (isActive) {\n\t\t\t\t\tif (CodeInfo.SciModified(this, n)) _CodeModifiedAndCodeinfoOK(); //WPF preview\n\t\t\t\t\tPanels.Find.UpdateQuickResults();\n\t\t\t\t}\n\t\t\t\tPanels.Bookmarks.SciModified(this, n);\n\t\t\t\tPanels.Breakpoints.SciModified(this, n);\n\t\t\t\tPanels.Found.SciModified(this, n);\n\t\t\t\tApp.Model.EditGoBack.SciModified(this, n.modificationType.Has(MOD.SC_MOD_DELETETEXT), n.position, n.length);\n\t\t\t\tif (n.linesAdded != 0) ESetLineNumberMarginWidth_(onModified: true);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase NOTIF.SCN_UPDATEUI:\n\t\t\t//note: SCN_UPDATEUI is async. Can be isActive true in SCN_MODIFIED but false in SCN_UPDATEUI. Or vice versa.\n\t\t\t//print.it(_modified, n.updated);\n\t\t\tif (n.updated.Has(UPDATE.SC_UPDATE_CONTENT)) {\n\t\t\t\tif (_modified) _modified = false;\n\t\t\t\telse if ((n.updated &= ~UPDATE.SC_UPDATE_CONTENT) == 0) break; //ignore when changed styling or markers\n\t\t\t}\n\t\t\tif (n.updated.HasAny(UPDATE.SC_UPDATE_CONTENT | UPDATE.SC_UPDATE_SELECTION)) {\n\t\t\t\tif (!n.updated.Has(UPDATE.SC_UPDATE_CONTENT)) _TempRangeOnModifiedOrPosChanged(0, 0, 0);\n\t\t\t\tif (isActive) {\n\t\t\t\t\tif (n.updated.Has(UPDATE.SC_UPDATE_SELECTION)) App.Model.EditGoBack.OnPosChanged(this);\n\t\t\t\t\tPanels.Editor.UpdateUI_EditEnabled_();\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (isActive) {\n\t\t\t\tif ((n.updated & (UPDATE.SC_UPDATE_CONTENT | UPDATE.SC_UPDATE_SELECTION)) == UPDATE.SC_UPDATE_SELECTION) SnippetMode_?.SciPosChanged();\n\t\t\t\tCodeInfo.SciUpdateUI(this, n.updated);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase NOTIF.SCN_CHARADDED when isActive:\n\t\t\t//print.it($\"SCN_CHARADDED  {n.ch}  '{(char)n.ch}'\");\n\t\t\tif (n.ch == '\\n' /*|| n.ch == ';'*/) { //split scintilla Undo\n\t\t\t\taaaAddUndoPoint();\n\t\t\t}\n\t\t\tif (n.ch != '\\r' && n.ch <= 0xffff) { //on Enter we receive notifications for '\\r' and '\\n'\n\t\t\t\tCodeInfo.SciCharAdded(this, (char)n.ch);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase NOTIF.SCN_DWELLSTART when isActive:\n\t\t\tCodeInfo.SciMouseDwellStarted(this, n.position);\n\t\t\tPanels.Breakpoints.SciMouseDwell_(true, this, n);\n\t\t\tbreak;\n\t\tcase NOTIF.SCN_DWELLEND when isActive:\n\t\t\tPanels.Breakpoints.SciMouseDwell_(false, this, n);\n\t\t\tbreak;\n\t\tcase NOTIF.SCN_MARGINCLICK when isActive:\n\t\t\tif (_fn.IsCodeFile) {\n\t\t\t\tCodeInfo.Cancel();\n\t\t\t\tif (n.margin == SciTheme.Margin.Fold) {\n\t\t\t\t\t_FoldOnMarginClick(n.position, n.modifiers);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (n.margin == SciTheme.Margin.Markers) {\n\t\t\t\tif (n.modifiers is 0) _MarkersMarginClicked(false, n.position);\n\t\t\t}\n\t\t\tbreak;\n\t\t//case NOTIF.SCN_MARGINRIGHTCLICK: break; //can't use it because: 1. Need to handle WM_RBUTTONDOWN. 2. Need notification on button up.\n\t\tcase NOTIF.SCN_STYLENEEDED:\n\t\t\t//print.it(\"SCN_STYLENEEDED\");\n\t\t\tif (isActive && _fn.IsCodeFile) {\n\t\t\t\tEHideImages_(Call(SCI_GETENDSTYLED), n.position);\n\t\t\t\tCall(SCI_STARTSTYLING, n.position); //need this even if would not hide images\n\t\t\t} else {\n\t\t\t\taaaSetStyled();\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\t\n\t\tbase.AaOnSciNotify(ref n);\n\t}\n\tbool _modified;\n\t\n\tprotected override nint WndProc(wnd w, int msg, nint wp, nint lp) {\n\t\t//WndUtil.PrintMsg(w, msg, wp, lp, new(Api.WM_TIMER, Api.WM_PAINT, Api.WM_MOUSEMOVE, Api.WM_NCHITTEST, Api.WM_SETCURSOR));\n\t\t\n\t\tswitch (msg) {\n\t\tcase Api.WM_CHAR: {\n\t\t\t\tint c = (int)wp;\n\t\t\t\tif (c < 32) {\n\t\t\t\t\tif (c is not (9 or 10 or 13)) return 0;\n\t\t\t\t} else {\n\t\t\t\t\tif (CodeInfo.SciBeforeCharAdded(this, (char)c)) return 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\t//rejected. Possibly can be bad in some cases. I can't learn and test everything.\n\t\t//case Api.WM_IME_COMPOSITION: //to add missing newline, because no WM_CHAR when using IME\n\t\t//\tif (0 != (lp & 0x800)) //GCS_RESULTSTR; Scintilla uses it.\n\t\t//\t\tCodeInfo.SciBeforeCharAdded(this, default);\n\t\t//\tbreak;\n\t\tcase Api.WM_RBUTTONDOWN or Api.WM_MBUTTONDOWN: {\n\t\t\t\tPOINT p = Math2.NintToPOINT(lp);\n\t\t\t\tint margin = aaaMarginFromPoint(p);\n\t\t\t\tif (margin >= 0) {\n\t\t\t\t\tif (msg == Api.WM_RBUTTONDOWN) {\n\t\t\t\t\t\t//prevent changing the caret/selection when rclicked some margins\n\t\t\t\t\t\tif (margin is SciTheme.Margin.Fold or SciTheme.Margin.Markers) return 0;\n\t\t\t\t\t\t\n\t\t\t\t\t\t//prevent changing the caret/selection if it is in the rclicked line\n\t\t\t\t\t\tvar selStart = aaaSelectionStart8;\n\t\t\t\t\t\tvar (_, start, end) = aaaLineStartEndFromPos(false, aaaPosFromXY(false, p, false));\n\t\t\t\t\t\tif (selStart >= start && selStart <= end) return 0;\n\t\t\t\t\t\t//do vice versa if the end of non-empty selection is at the start of the rclicked line, to avoid comment/uncomment wrong lines\n\t\t\t\t\t\tif (margin is SciTheme.Margin.LineNumbers or SciTheme.Margin.Images or SciTheme.Margin.Changes) {\n\t\t\t\t\t\t\tif (aaaSelectionEnd8 == start) aaaGoToPos(false, start); //clear selection above start\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (margin == SciTheme.Margin.Markers) {\n\t\t\t\t\t\tint pos = aaaPosFromXY(false, p, false);\n\t\t\t\t\t\tif (pos >= 0) {\n\t\t\t\t\t\t\tint line = aaaLineFromPos(false, pos);\n\t\t\t\t\t\t\tif (!Panels.Breakpoints.SciMiddleClick_(this, line)) Panels.Bookmarks.SciMiddleClick_(this, line);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\tcase Api.WM_RBUTTONUP: {\n\t\t\t\tPOINT p = Math2.NintToPOINT(lp);\n\t\t\t\tint margin = aaaMarginFromPoint(p);\n\t\t\t\tif (margin >= 0) {\n\t\t\t\t\tswitch (margin) {\n\t\t\t\t\tcase SciTheme.Margin.LineNumbers or SciTheme.Margin.Images or SciTheme.Margin.Changes:\n\t\t\t\t\t\tModifyCode.Comment(null, notSlashStar: true);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase SciTheme.Margin.Markers:\n\t\t\t\t\t\t_MarkersMarginClicked(true, base.aaaPosFromXY(false, p, false));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase SciTheme.Margin.Fold:\n\t\t\t\t\t\t_FoldContextMenu(aaaPosFromXY(false, p, false));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\tcase Api.WM_CONTEXTMENU: {\n\t\t\t\tbool kbd = (int)lp == -1;\n\t\t\t\tvar m = new KWpfMenu();\n\t\t\t\tDCustomizeContextMenu.AddToMenu(m, \"Edit\");\n\t\t\t\tApp.Commands[nameof(Menus.Edit)].CopyToMenu(m);\n\t\t\t\tm.Show(this, byCaret: kbd);\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t}\n\t\t\n\t\tvar R = base.WndProc(w, msg, wp, lp);\n\t\t\n\t\tswitch (msg) {\n\t\tcase Api.WM_KILLFOCUS:\n\t\t\tCodeInfo.SciKillFocus(this);\n\t\t\tbreak;\n\t\t}\n\t\t\n\t\treturn R;\n\t}\n\t\n\tvoid _MarkersMarginClicked(bool rclick, int pos8) {\n\t\tint line = aaaLineFromPos(false, pos8);\n\t\tvar m = new popupMenu();\n#if !true //breakpoints if left click, bookmarks if right click\n\t\tif (rclick) {\n\t\t\tPanels.Bookmarks.AddMarginMenuItems_(this, m, pos8);\n\t\t} else if (EFile.IsCodeFile) {\n\t\t\tPanels.Breakpoints.AddMarginMenuItems_(this, m, line, pos8);\n\t\t\tPanels.Debug.AddMarginMenuItems_(this, m, line);\n\t\t}\n#else //breakpoints and bookmarks in single menu, but bookmarks first if rclick\n\t\tif (rclick) Panels.Bookmarks.AddMarginMenuItems_(this, m, pos8);\n\t\tif (EFile.IsCodeFile) {\n\t\t\tif (rclick) m.Separator();\n\t\t\tPanels.Breakpoints.AddMarginMenuItems_(this, m, line, pos8);\n\t\t\tPanels.Debug.AddMarginMenuItems_(this, m, line);\n\t\t\tif (!rclick) m.Separator();\n\t\t}\n\t\tif (!rclick) Panels.Bookmarks.AddMarginMenuItems_(this, m, pos8);\n#endif\n\t\tvar xy = mouse.xy; xy.Offset(-_dpi / 2, -_dpi / 8);\n\t\tm.Show(xy: xy, owner: AaWnd);\n\t}\n\t\n\tprotected override bool TranslateAcceleratorCore(ref System.Windows.Interop.MSG msg, ModifierKeys mod) {\n\t\tif (msg.message is Api.WM_KEYDOWN or Api.WM_SYSKEYDOWN) {\n\t\t\tvar key = (KKey)msg.wParam;\n\t\t\tif (_ImageDeleteKey(key)) return true;\n\t\t\tif (CodeInfo.SciCmdKey(this, key, mod)) return true;\n\t\t\tswitch ((key, mod)) {\n\t\t\tcase (KKey.Enter, 0):\n\t\t\tcase (KKey.Enter, ModifierKeys.Control | ModifierKeys.Shift):\n\t\t\t\taaaAddUndoPoint();\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn base.TranslateAcceleratorCore(ref msg, mod);\n\t}\n\t\n\tprotected override void OnGotFocus(RoutedEventArgs e) {\n\t\tif (!_noModelEnsureCurrentSelected) App.Model.EnsureCurrentSelected();\n\t\tbase.OnGotFocus(e);\n\t}\n\tbool _noModelEnsureCurrentSelected;\n\t\n\tinternal bool EIsUnsaved_ {\n\t\tget => _isUnsaved || 0 != Call(SCI_GETMODIFY);\n\t\tset {\n\t\t\tif (_isUnsaved = value) App.Model.Save.TextLater(1);\n\t\t}\n\t}\n\tbool _isUnsaved;\n\t\n\tpublic bool EIsBinary => _fls.IsBinary;\n\t\n\t//Called by PanelEdit.SaveText.\n\tinternal bool ESaveText_(bool force) {\n\t\tDebug.Assert(!EIsBinary); if (EIsBinary) return false;\n\t\tif (_fn.DontSave) return true;\n\t\tif (force || EIsUnsaved_) {\n\t\t\t//print.qm2.write(\"saving\");\n\t\t\tif (!App.Model.TryFileOperation(FOSync.UserFileWrite, () => _fls.Save(this, _fn.FilePath))) return false;\n\t\t\t_isUnsaved = false;\n\t\t\tCall(SCI_SETSAVEPOINT); //note: splits Undo. Maybe it's good. But I don't remember what is the primary purpose of this. VS splits Undo on save too.\n\t\t\t_fn._UpdateFileModTime();\n\t\t\tif (this != Panels.Editor.ActiveDoc) CodeInfo.FilesChanged();\n\t\t}\n\t\treturn true;\n\t}\n\t\n\t//Called by FileNode.CheckModifiedExternally_.\n\tinternal void EFileModifiedExternally_() {\n\t\tDebug.Assert(!EIsBinary); if (EIsBinary) return; //caller must check it\n\t\tif (!_fn.GetFileText(out var text) || text == this.aaaText) return;\n\t\tEReplaceTextGently(text);\n\t\tCall(SCI_SETSAVEPOINT);\n\t\t\n\t\t//rejected: print info. VS and VSCode reload silently.\n\t\t//if (this == Panels.Editor.ActiveDoc) print.it($\"<>Info: file {_fn.SciLink()} has been reloaded because modified outside. You can Undo.\");\n\t}\n\t\n\t//never mind: not called when zoom changes.\n\tinternal void ESetLineNumberMarginWidth_(bool onModified = false) {\n\t\tint c = 4;\n\t\tint lines = aaaLineCount;\n\t\twhile (lines > 999) { c++; lines /= 10; }\n\t\tif (!onModified || c != _prevLineNumberMarginWidth) aaaMarginSetWidth(SciTheme.Margin.LineNumbers, 3, _prevLineNumberMarginWidth = c);\n\t}\n\tint _prevLineNumberMarginWidth;\n\t\n\tprotected override void AaOnDeletingLineWithMarkers(int line, uint markers) {\n\t\tif ((markers & 3 << SciTheme.Marker.Bookmark) != 0) Panels.Bookmarks.SciDeletingLineWithMarker(this, line);\n\t\tif ((markers & 63 << SciTheme.Marker.Breakpoint) != 0) Panels.Breakpoints.SciDeletingLineWithMarker(this, line);\n\t\tbase.AaOnDeletingLineWithMarkers(line, markers);\n\t}\n\t\n\tinternal CiSnippetMode SnippetMode_;\n\t\n\t#region copy paste\n\t\n\t/// <summary>\n\t/// Called when copying (menu or Ctrl+C).\n\t/// Caller must not copy text to clipboard, and must not pass the event to Scintilla.\n\t/// </summary>\n\tpublic void ECopy(ECopyAs copyAs = ECopyAs.Text) {\n\t\tint i1 = aaaSelectionStart8, i2 = aaaSelectionEnd8, textLen = aaaLen8;\n\t\tif (textLen == 0) return;\n\t\tif (copyAs == ECopyAs.Text) {\n\t\t\tif (i2 != i1) Call(SCI_COPY);\n\t\t} else {\n\t\t\tbool isCS = _fn.IsCodeFile, isFragment = i2 != i1 && !(i1 == 0 && i2 == textLen);\n\t\t\tstring s = isFragment ? aaaRangeText(false, i1, i2) : aaaText;\n\t\t\tif (isCS) s = _ImageRemoveScreenshots(s, true);\n\t\t\tswitch (copyAs) {\n\t\t\tcase ECopyAs.Forum:\n\t\t\t\tvar b = new StringBuilder(\"[code]\");\n\t\t\t\tif (isCS) {\n\t\t\t\t\tif (!isFragment) {\n\t\t\t\t\t\tvar name = _fn.Name; if (name.RxIsMatch(_fn.IsScript ? @\"(?i)^Script\\d*\\.cs$\" : @\"(?i)^Class\\d*\\.cs$\")) name = null;\n\t\t\t\t\t\tif (!(name == null && _fn.IsScript)) b.AppendFormat(\"// {0} \\\"{1}\\\"\\r\\n\", _fn.IsScript ? \"script\" : \"class\", name);\n\t\t\t\t\t}\n\t\t\t\t\ts = CodeExporter.ExportForum(s);\n\t\t\t\t}\n\t\t\t\ts = b.Append(s).AppendLine(\"[/code]\").ToString();\n\t\t\t\tbreak;\n\t\t\tcase ECopyAs.HtmlSpanStyle or ECopyAs.HtmlSpanClass or ECopyAs.HtmlSpanClassCss:\n\t\t\t\ts = CodeExporter.ExportHtml(s, spanClass: copyAs != ECopyAs.HtmlSpanStyle, withCss: copyAs == ECopyAs.HtmlSpanClassCss);\n\t\t\t\tnew clipboardData().AddText(s).AddHtml(s).SetClipboard();\n\t\t\t\treturn;\n\t\t\tcase ECopyAs.Markdown:\n\t\t\t\ts = $\"```csharp\\r\\n{s}\\r\\n```\\r\\n\";\n\t\t\t\tbreak;\n\t\t\t\t//case ECopyAs.TextWithoutScreenshots:\n\t\t\t}\n\t\t\tclipboard.text = s;\n\t\t}\n\t}\n\t\n\tpublic enum ECopyAs { Text, Forum, HtmlSpanStyle, HtmlSpanClassCss, HtmlSpanClass, Markdown, TextWithoutScreenshots }\n\t\n\t/// <summary>\n\t/// Called when pasting (menu or Ctrl+V). Inserts text, possibly with processed forum bbcode etc.\n\t/// Caller must not insert text, and must not pass the event to Scintilla.\n\t/// </summary>\n\tpublic void EPaste() {\n\t\tvar s1 = clipboard.text; if (s1.NE()) return;\n\t\t\n\t\tvar (isFC, text, name, isClass) = EIsForumCode_(s1, false);\n\t\tif (isFC) {\n\t\t\tstring buttons = _fn.FileType != (isClass ? FNType.Class : FNType.Script) || aaaIsReadonly\n\t\t\t\t? \"1 Create new file|0 Cancel\"\n\t\t\t\t: \"1 Create new file|2 Replace all text|3 Paste|0 Cancel\";\n\t\t\tswitch (dialog.show(\"Import C# file text from clipboard\", \"Source file: \" + name, buttons, DFlags.CommandLinks, owner: AaWnd)) {\n\t\t\tcase 1: //Create new file\n\t\t\t\t_NewFileFromForumCode(text, name, isClass);\n\t\t\t\tbreak;\n\t\t\tcase 2: //Replace all text\n\t\t\t\taaaSetText(text);\n\t\t\t\tbreak;\n\t\t\tcase 3: //Paste\n\t\t\t\tusing (new CodeInfo.PastingEtc(this))\n\t\t\t\t\taaaReplaceSel(text);\n\t\t\t\tbreak;\n\t\t\t} //rejected: option to rename this file\n\t\t} else {\n\t\t\tusing (new CodeInfo.PastingEtc(this))\n\t\t\t\tCall(SCI_PASTE); //not aaaReplaceSel, because can be SCI_SETMULTIPASTE etc\n\t\t}\n\t}\n\t\n\tinternal static (bool yes, string text, string filename, bool isClass) EIsForumCode_(string s, bool newFile) {\n\t\tif (!s.RxMatch(@\"^// (script|class) \"\"(.*?)\"\"( |\\R)\", out var m)) return default;\n\t\t\n\t\tbool isClass = s[3] == 'c';\n\t\ts = s[m.End..];\n\t\tvar name = m[2].Length > 0 ? m[2].Value : (isClass ? \"Class1.cs\" : \"Script1.cs\");\n\t\t\n\t\tif (newFile && dialog.showOkCancel(\"Import C# file from clipboard?\", \"Source file: \" + name, owner: App.Hmain))\n\t\t\t_NewFileFromForumCode(s, name, isClass);\n\t\t\n\t\treturn (true, s, name, isClass);\n\t}\n\t\n\tstatic void _NewFileFromForumCode(string text, string name, bool isClass) {\n\t\tApp.Model.NewItem(isClass ? \"Class.cs\" : \"Script.cs\", null, name, text: new NewFileText(replaceTemplate: true, text));\n\t}\n\t\n\t#endregion\n\t\n\t#region indicators\n\t\n\tvoid _IndicatorsInit() {\n\t\tif (!_fn.IsCodeFile) return;\n\t\t\n\t\t//workaround for: indicators too small if high DPI\n\t\tint style = _dpi < 144 ? INDIC_SQUIGGLEPIXMAP : INDIC_SQUIGGLE,\n\t\t\tstrokeWidth = _dpi < 144 ? 100 : 200;\n\t\t//strokeWidth = _dpi < 144 ? 100 : _dpi < 192 ? 150 : 200;\n\t\t\n\t\taaaIndicatorDefine(SciTheme.Indic.Error, style, 0xff0000, strokeWidth: strokeWidth);\n\t\taaaIndicatorDefine(SciTheme.Indic.Warning, style, 0x008000, strokeWidth: strokeWidth); //dark green\n\t\taaaIndicatorDefine(SciTheme.Indic.Info, INDIC_DIAGONAL, 0xc0c0c0, strokeWidth: strokeWidth);\n\t\taaaIndicatorDefine(SciTheme.Indic.DiagHidden, INDIC_DOTS, 0xc0c0c0, strokeWidth: strokeWidth);\n\t}\n\t\n\tbool _indicHaveFound, _indicHaveDiag;\n\t\n\tinternal void EInicatorsFound_(List<Range> a) {\n\t\tif (_indicHaveFound) {\n\t\t\t_indicHaveFound = false;\n\t\t\taaaIndicatorClear(SciTheme.Indic.Found);\n\t\t}\n\t\tif (a.NE_()) return;\n\t\t_indicHaveFound = true;\n\t\t\n\t\tforeach (var v in a) aaaIndicatorAdd(SciTheme.Indic.Found, true, v);\n\t}\n\t\n\tinternal void EInicatorsDiag_(bool has) {\n\t\tif (_indicHaveDiag) {\n\t\t\t_indicHaveDiag = false;\n\t\t\taaaIndicatorClear(SciTheme.Indic.DiagHidden);\n\t\t\taaaIndicatorClear(SciTheme.Indic.Info);\n\t\t\taaaIndicatorClear(SciTheme.Indic.Warning);\n\t\t\taaaIndicatorClear(SciTheme.Indic.Error);\n\t\t}\n\t\tif (!has) return;\n\t\t_indicHaveDiag = true;\n\t}\n\t\n\t#endregion\n\t\n\t#region view\n\t\n\t[Flags]\n\tpublic enum EView { Wrap = 1, Images = 2 }\n\t\n\tinternal static void EToggleView_call_from_menu_only_(EView what) {\n\t\tif (what.Has(EView.Wrap)) {\n\t\t\tApp.Settings.edit_wrap ^= true;\n\t\t\tforeach (var v in Panels.Editor.OpenDocs) v.Call(SCI_SETWRAPMODE, App.Settings.edit_wrap ? SC_WRAP_WORD : 0);\n\t\t}\n\t\tif (what.Has(EView.Images)) {\n\t\t\tApp.Settings.edit_noImages ^= true;\n\t\t\tforeach (var v in Panels.Editor.OpenDocs) v._ImagesOnOff();\n\t\t}\n\t\t\n\t\t//should not need this, because this func called from menu commands only.\n\t\t//\tBut somehow KMenuCommands does not auto change menu/toolbar checked state for Edit menu. Need to fix it.\n\t\tPanels.Editor.UpdateUI_EditView_();\n\t}\n\t\n\tvoid _CodeModifiedAndCodeinfoOK() {\n\t\tif (!_wpfPreview) return;\n\t\ts_timer1 ??= new(static t => {\n\t\t\tvar doc = Panels.Editor.ActiveDoc;\n\t\t\tif (doc == t.Tag) doc._WpfPreviewRun(false);\n\t\t});\n\t\ts_timer1.Tag = this;\n\t\ts_timer1.After(500);\n\t}\n\tstatic timer s_timer1;\n\tstatic bool s_wpfPreviewInited;\n\tbool _wpfPreview;\n\tinternal bool EIsWpfPreview => _wpfPreview;\n\t\n\tvoid _WpfPreviewRun(bool starting) {\n\t\tif (!_wpfPreview) return;\n\t\tCompileRun.RunWpfPreview(_fn, k => {\n\t\t\tbool hasWPF_PREVIEW = false;\n\t\t\tfor (int i = k.m.GlobalCount; i < k.trees.Length; i++) {\n\t\t\t\t//print.it(m.CodeFiles[i]);\n\t\t\t\tif (!k.m.CodeFiles[i].code.Contains(\"WPF_PREVIEW\")) continue;\n\t\t\t\tvar cu = k.trees[i].GetCompilationUnitRoot();\n\t\t\t\tif (!cu.GetDirectives(d => d is IfDirectiveTriviaSyntax di && di.Condition.ToString() == \"WPF_PREVIEW\").Any()) continue;\n\t\t\t\thasWPF_PREVIEW = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (!hasWPF_PREVIEW) {\n\t\t\t\tif (starting) {\n\t\t\t\t\tprint.it(\"\"\"\n<>To enable <help editor/Code editor>WPF preview<>, add #if WPF_PREVIEW with code that calls Preview(). Examples: <fold>\n<code>\n//code before b.ShowDialog\n#if WPF_PREVIEW\nb.Window.Preview();\n#endif\n\n//code near the start of the script file, when using dialog class DialogClass\n#if WPF_PREVIEW\nnew DialogClass().Preview();\n#endif\n\n//code before dialog class DialogClass, when not using a script file\n#if WPF_PREVIEW\nclass Program { static void Main() { new DialogClass().Preview(); }}\n#endif\n</code>\n</fold>\n\"\"\");\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t//print.it(k.compilation.GetDiagnostics());\n\t\t\treturn !k.compilation.GetDiagnostics().Any(o => o.Severity == DiagnosticSeverity.Error);\n\t\t});\n\t}\n\t\n\tpublic static void WpfPreviewStartStop(MenuItem mi) {\n\t\tvar doc = Panels.Editor.ActiveDoc; if (doc == null) return;\n\t\tbool start = mi.IsChecked;\n\t\tif (start == doc._wpfPreview) return;\n\t\tdoc._wpfPreview = start;\n\t\t\n\t\tif (start) doc._WpfPreviewRun(true);\n\t\t\n\t\tif (!s_wpfPreviewInited) {\n\t\t\ts_wpfPreviewInited = true;\n\t\t\tPanels.Editor.ActiveDocChanged += () => {\n\t\t\t\tmi.IsChecked = Panels.Editor.ActiveDoc?._wpfPreview ?? false;\n\t\t\t};\n\t\t}\n\t\t\n\t\t//update #if WPF_PREVIEW: styling, errors.\n\t\tCodeInfo.StopAndUpdateStyling();\n\t}\n\t\n\t#endregion\n\t\n\t#region acc\n\t\n\tprotected override ERole AaAccessibleRole => ERole.DOCUMENT;\n\t\n\tprotected override string AaAccessibleName => \"document - \" + _fn.DisplayName;\n\t\n\tprotected override string AaAccessibleDescription => _fn.FilePath;\n\t\n\t#endregion\n\t\n\t#region util\n\t\n\tvoid _DefineIconMarkers() {\n\t\tif (s_markerBitmaps.dpi != _dpi) {\n\t\t\ts_markerBitmaps.dpi = _dpi;\n\t\t\t_Bitmap(ref s_markerBitmaps.bookmark, \"*Material.Bookmark #EABB00 @16\", 14);\n\t\t\t_Bitmap(ref s_markerBitmaps.bookmark2, \"*Material.BookmarkOutline #EABB00 @16\", 14);\n\t\t\t_Bitmap(ref s_markerBitmaps.breakpoint, \"*Material.Circle #EE3000\", 8);\n\t\t\t_Bitmap(ref s_markerBitmaps.breakpointD, \"*Material.Circle #A0A0A0\", 8);\n\t\t\t_Bitmap(ref s_markerBitmaps.breakpointC, \"*Codicons.DebugBreakpointConditional #EE3000\", 8);\n\t\t\t_Bitmap(ref s_markerBitmaps.breakpointCD, \"*Codicons.DebugBreakpointConditional #A0A0A0\", 8);\n\t\t\t_Bitmap(ref s_markerBitmaps.breakpointL, \"*BootstrapIcons.DiamondFill #40B000\", 10);\n\t\t\t_Bitmap(ref s_markerBitmaps.breakpointLD, \"*BootstrapIcons.DiamondFill #A0A0A0\", 10);\n\t\t\t_Bitmap(ref s_markerBitmaps.debugLine, \"*Codicons.DebugStackframe #40B000 @16\", 14);\n\t\t\t_Bitmap(ref s_markerBitmaps.debugLine2, \"*Codicons.DebugStackframe #808080 @16\", 14);\n\t\t}\n\t\t_Marker(SciTheme.Marker.Bookmark, s_markerBitmaps.bookmark);\n\t\t_Marker(SciTheme.Marker.BookmarkInactive, s_markerBitmaps.bookmark2);\n\t\t_Marker(SciTheme.Marker.Breakpoint, s_markerBitmaps.breakpoint);\n\t\t_Marker(SciTheme.Marker.BreakpointD, s_markerBitmaps.breakpointD);\n\t\t_Marker(SciTheme.Marker.BreakpointC, s_markerBitmaps.breakpointC);\n\t\t_Marker(SciTheme.Marker.BreakpointCD, s_markerBitmaps.breakpointCD);\n\t\t_Marker(SciTheme.Marker.BreakpointL, s_markerBitmaps.breakpointL);\n\t\t_Marker(SciTheme.Marker.BreakpointLD, s_markerBitmaps.breakpointLD);\n\t\t_Marker(SciTheme.Marker.DebugLine, s_markerBitmaps.debugLine);\n\t\t_Marker(SciTheme.Marker.DebugLine2, s_markerBitmaps.debugLine2);\n\t\t\n\t\tunsafe void _Bitmap(ref (int size, nint data) mb, string icon, int size) {\n\t\t\tif (mb.data != 0) { MemoryUtil.Free((uint*)mb.data); mb.data = 0; }\n\t\t\tusing var b = ImageUtil.LoadGdipBitmapFromXaml(icon, _dpi, new(size, size));\n\t\t\tusing var v = b.Data(System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);\n\t\t\tsize = v.Width;\n\t\t\tvar m = MemoryUtil.Alloc<uint>(size * size);\n\t\t\tMemoryUtil.Copy((uint*)v.Scan0, m, size * size * 4);\n\t\t\tfor (uint* p = m, pe = p + size * size; p < pe; p++) *p = ColorInt.SwapRB(*p);\n\t\t\tmb = new(size, (nint)m);\n\t\t}\n\t\t\n\t\tunsafe void _Marker(int marker, (int size, nint data) b) {\n\t\t\taaaMarkerDefine(marker, Sci.SC_MARK_RGBAIMAGE);\n\t\t\tCall(SCI_RGBAIMAGESETWIDTH, b.size);\n\t\t\tCall(SCI_RGBAIMAGESETHEIGHT, b.size);\n\t\t\tCall(SCI_MARKERDEFINERGBAIMAGE, marker, b.data);\n\t\t}\n\t}\n\t\n\tstruct _MarkerBitmaps {\n\t\tpublic int dpi;\n\t\tpublic (int size, nint data) bookmark, bookmark2, breakpoint, breakpointD, breakpointC, breakpointCD, breakpointL, breakpointLD, debugLine, debugLine2;\n\t}\n\tstatic _MarkerBitmaps s_markerBitmaps;\n\t\n\t/// <summary>\n\t/// Gets rectangle of caret if it was at the specified UTF-16 position.\n\t/// If <i>pos16</i> less than 0, uses current caret position.\n\t/// </summary>\n\tpublic RECT EGetCaretRectFromPos(int pos16 = -1, bool inScreen = false) {\n\t\tint pos8 = pos16 < 0 ? aaaCurrentPos8 : aaaPos8(pos16);\n\t\tint x = Call(Sci.SCI_POINTXFROMPOSITION, 0, pos8), y = Call(Sci.SCI_POINTYFROMPOSITION, 0, pos8);\n\t\tvar r = new RECT(x, y, 1, Call(Sci.SCI_TEXTHEIGHT, aaaLineFromPos(false, pos8)) + 2);\n\t\tif (inScreen) AaWnd.MapClientToScreen(ref r);\n\t\treturn r;\n\t}\n\t\n\t#endregion\n}\n"
  },
  {
    "path": "Au.Editor/Edit/SciTheme.cs",
    "content": "using Au.Controls;\n\nusing static Au.Controls.Sci;\n\nnamespace LA;\n//note: used in ToolLand too.\n\nrecord class SciTheme { //info: record class because need `with` and synthesized ==\n\tpublic record struct TStyle(int color, bool back = false, bool bold = false, bool italic = false, bool underline = false) {\n\t\tpublic static implicit operator TStyle(int color) => new(color);\n\t}\n\t\n\tpublic record struct TIndicator(int color, int alpha);\n\t\n\tpublic string FontName = \"Consolas\";\n\tpublic double FontSize = 9.75;\n\tpublic int Background = 0xffffff;\n\t\n\tpublic TStyle None; //black\n\tpublic TStyle Comment = 0x60A000; //green-yellow\n\tpublic TStyle String = 0xA07040; //brown-green\n\tpublic TStyle StringEscape = 0xFF60FF; //pink\n\tpublic TStyle Number = 0x804000; //brown-red\n\tpublic TStyle Punctuation; //black\n\tpublic TStyle Operator = 0x0000ff; //blue\n\tpublic TStyle Keyword = 0x0000ff; //blue\n\tpublic TStyle Namespace = 0x808000; //dark yellow\n\tpublic TStyle Type = 0x0080c0; //teal-blue\n\tpublic TStyle Function = new(0, bold: true); //black bold\n\tpublic TStyle Event = new(0, bold: true);\n\tpublic TStyle LocalVariable = 0x204020; //dark gray-green\n\tpublic TStyle Field = 0x204020;\n\tpublic TStyle Constant = 0x204020;\n\tpublic TStyle Label = 0xff00ff; //magenta\n\tpublic TStyle Preprocessor = 0xff3300; //red\n\tpublic TStyle Excluded = 0x808080; //gray\n\tpublic TStyle XmlDocText = 0x408000; //green\n\tpublic TStyle XmlDocTag = 0x808080; //gray\n\tpublic TStyle RxText = 0xA07040;\n\tpublic TStyle RxMeta = new(0x0060FF, bold: true);\n\tpublic TStyle RxChars = new(0x60A000, bold: true);\n\tpublic TStyle RxOption = new(0xFF4040, bold: true);\n\tpublic TStyle RxEscape = 0xFF60FF;\n\tpublic TStyle RxCallout = new(0xFF8060, back: true);\n\tpublic TStyle RxComment = 0x808080;\n\t\n\tpublic TStyle LineNumber = 0x808080;\n\tpublic int LineNumberMargin = 0xE0E0E0;\n\tpublic int MarkerMargin = 0xFFFFFF;\n\t\n\tpublic TIndicator IndicRefs = new(0x80C000, 40);\n\tpublic TIndicator IndicBraces = new(0x80C000, 255);\n\tpublic TIndicator IndicDebug = new(0xFFF181, 255);\n\tpublic TIndicator IndicFound = new(0xffff00, 255);\n\tpublic TIndicator IndicSnippetField = new(0xe0a000, 60);\n\tpublic TIndicator IndicSnippetFieldActive = new(0x33ADFF, 60);\n\t\n\tpublic int SelColor = unchecked((int)0xA0A0A0A0);\n\tpublic int SelNofocusColor = 0x60A0A0A0;\n\tpublic int Caret = 0x2000000; //alpha = thickness\n\tpublic int CaretLine = 0x1E0E0E0; //alpha = frame thickness\n\t\n\t//note: these must be before the static properties, else would be null when the properties use them.\n\tpublic static readonly string ThemesDirCustomizedBS = AppSettings.DirBS + @\"Themes\\\";\n\tpublic static readonly string ThemesDirDefaultBS = folders.ThisAppBS + @\"Default\\Themes\\\";\n\t\n\t//public\n\t\n\t/// <summary>\n\t/// Default theme without customizations.\n\t/// Use as immutable. If need theme with some values changed, use code `SciTheme.Default with { ... }`.\n\t/// </summary>\n\tpublic static SciTheme Default { get; } = new();\n\t\n\t/// <summary>\n\t/// Current theme (with or without customizations).\n\t/// Use as immutable. If need theme with some values changed, use code `SciTheme.Current with { ... }`.\n\t/// </summary>\n\tpublic static SciTheme Current {\n\t\tget => field ??= new(\"\");\n\t\tset { field = value; }\n\t}\n\t\n\tclass _ThemeInfo {\n\t\tpublic string\n\t\t\tname, //not null if can load file; without suffix\n\t\t\tdefPath, //not null if can load a default file\n\t\t\tcustPath; //not null, even if file does not exist\n\t\tpublic bool\n\t\t\tcustomized, //true if using a customized file\n\t\t\tcustExists; //true if customized file exists, even if not using it\n\t\t\n\t\t/// <param name=\"theme\">\"\" - current. null - default (\"LA\").</param>\n\t\tpublic _ThemeInfo(string theme) {\n\t\t\tif (theme is \"\") theme = App.Settings.edit_theme.NullIfEmpty_();\n\t\t\tif (theme is null or \"LA\") {\n\t\t\t\t_Cust();\n\t\t\t\t\n\t\t\t\t//In the past LA did not support multiple themes...\n\t\t\t\tif (!custExists && theme is null) {\n\t\t\t\t\tvar sOld = AppSettings.DirBS + \"Font.csv\";\n\t\t\t\t\tif (filesystem.exists(sOld, true).File) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tvar s = filesystem.loadText(sOld);\n\t\t\t\t\t\t\ts = s.RxReplace(@\"(?m)^Function\\b(.+)\", \"Function$1\\r\\nEvent$1\");\n\t\t\t\t\t\t\ts = s.RxReplace(@\"(?m)^Variable\\b(.+)\", \"LocalVariable$1\\r\\nField$1\");\n\t\t\t\t\t\t\tfilesystem.saveText(custPath, s);\n\t\t\t\t\t\t\tApp.Settings.edit_theme = \"LA [customized]\";\n\t\t\t\t\t\t\tname = \"LA\";\n\t\t\t\t\t\t\tcustomized = custExists = true;\n\t\t\t\t\t\t\tfilesystem.rename(sOld, \"Font.csv.bak\", FIfExists.RenameNew);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcatch (Exception e1) { Debug_.Print(e1); }\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tbool cust = theme.Ends(\" [customized]\");\n\t\t\t\tif (cust) theme = theme[..^13];\n\t\t\t\tif (cust && theme is \"LA\") {\n\t\t\t\t\t_Cust();\n\t\t\t\t\tif (customized = custExists) name = theme;\n\t\t\t\t} else {\n\t\t\t\t\tvar dp = ThemesDirDefaultBS + theme + \".csv\";\n\t\t\t\t\tif (filesystem.exists(dp, true).File) {\n\t\t\t\t\t\tname = theme;\n\t\t\t\t\t\tdefPath = dp;\n\t\t\t\t\t\tcustPath = ThemesDirCustomizedBS + theme + \".csv\";\n\t\t\t\t\t\tcustExists = filesystem.exists(custPath, true).File;\n\t\t\t\t\t\tcustomized = cust && custExists;\n\t\t\t\t\t} else {\n\t\t\t\t\t\t_Cust();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tvoid _Cust() {\n\t\t\t\tcustPath = ThemesDirCustomizedBS + \"LA.csv\";\n\t\t\t\tcustExists = filesystem.exists(custPath, true).File;\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Gets theme name like <c>\"Name\"</c> or <c>\"Name [customized]\"</c>.\n\t/// </summary>\n\tpublic string Name { get; init; } = \"LA\";\n\t\n\tSciTheme() { }\n\t\n\t/// <summary>\n\t/// Loads current or specified theme (default or customized).\n\t/// </summary>\n\t/// <param name=\"theme\">Theme name, or <c>\"\"</c> to load current theme. If ends with <c>\" [customized]\"</c>, loads customized file if exists.</param>\n\tpublic SciTheme(string theme) {\n\t\tvar t = new _ThemeInfo(theme);\n\t\tif (t.name != null) {\n\t\t\tif (t.defPath != null && _Load(t.defPath)) Name = t.name; //need even if will load customized, because it may not have some items or may fail to load\n\t\t\tif (t.customized && _Load(t.custPath)) Name = t.name + \" [customized]\";\n\t\t}\n\t}\n\t\n\tbool _Load(string path) {\n\t\tcsvTable csv;\n\t\ttry { csv = csvTable.load(path); }\n\t\tcatch (Exception e1) { print.it(e1); return false; }\n\t\tif (csv.ColumnCount < 2) return false;\n\t\t\n\t\tforeach (var a in csv.Rows) {\n\t\t\tswitch (a[0]) {\n\t\t\tcase \"Font\":\n\t\t\t\tif (!a[1].NE()) FontName = a[1];\n\t\t\t\tif (a.Length > 2) { var fs = a[2].ToNumber(); if (fs >= 5 && fs <= 100) FontSize = fs; }\n\t\t\t\tbreak;\n\t\t\tcase nameof(Background): _Int6(ref Background); break;\n\t\t\tcase nameof(None): _Style(ref None); break;\n\t\t\tcase nameof(Comment): _Style(ref Comment); break;\n\t\t\tcase nameof(String): _Style(ref String); break;\n\t\t\tcase nameof(StringEscape): _Style(ref StringEscape); break;\n\t\t\tcase nameof(Number): _Style(ref Number); break;\n\t\t\tcase nameof(Punctuation): _Style(ref Punctuation); break;\n\t\t\tcase nameof(Operator): _Style(ref Operator); break;\n\t\t\tcase nameof(Keyword): _Style(ref Keyword); break;\n\t\t\tcase nameof(Namespace): _Style(ref Namespace); break;\n\t\t\tcase nameof(Type): _Style(ref Type); break;\n\t\t\tcase nameof(Function): _Style(ref Function); break;\n\t\t\tcase nameof(Event): _Style(ref Event); break;\n\t\t\tcase nameof(LocalVariable): _Style(ref LocalVariable); break;\n\t\t\tcase nameof(Field): _Style(ref Field); break;\n\t\t\tcase nameof(Constant): _Style(ref Constant); break;\n\t\t\tcase nameof(Label): _Style(ref Label); break;\n\t\t\tcase nameof(Preprocessor): _Style(ref Preprocessor); break;\n\t\t\tcase nameof(Excluded): _Style(ref Excluded); break;\n\t\t\tcase nameof(XmlDocText): _Style(ref XmlDocText); break;\n\t\t\tcase nameof(XmlDocTag): _Style(ref XmlDocTag); break;\n\t\t\tcase nameof(RxText): _Style(ref RxText, true); break;\n\t\t\tcase nameof(RxMeta): _Style(ref RxMeta, true); break;\n\t\t\tcase nameof(RxChars): _Style(ref RxChars, true); break;\n\t\t\tcase nameof(RxOption): _Style(ref RxOption, true); break;\n\t\t\tcase nameof(RxEscape): _Style(ref RxEscape, true); break;\n\t\t\tcase nameof(RxCallout): _Style(ref RxCallout, true); break;\n\t\t\tcase nameof(RxComment): _Style(ref RxComment, true); break;\n\t\t\tcase nameof(LineNumber): _Style(ref LineNumber); break;\n\t\t\tcase nameof(LineNumberMargin): _Int6(ref LineNumberMargin); break;\n\t\t\tcase nameof(MarkerMargin): _Int6(ref MarkerMargin); break;\n\t\t\tcase nameof(IndicRefs): _Indic(ref IndicRefs); break;\n\t\t\tcase nameof(IndicBraces): _Indic(ref IndicBraces); break;\n\t\t\tcase nameof(IndicDebug): _Indic(ref IndicDebug); break;\n\t\t\tcase nameof(IndicFound): _Indic(ref IndicFound); break;\n\t\t\tcase nameof(IndicSnippetField): _Indic(ref IndicSnippetField); break;\n\t\t\tcase nameof(IndicSnippetFieldActive): _Indic(ref IndicSnippetFieldActive); break;\n\t\t\tcase nameof(SelColor): _Int6Int(ref SelColor); break;\n\t\t\tcase nameof(SelNofocusColor): _Int6Int(ref SelNofocusColor); break;\n\t\t\tcase nameof(Caret): _Int6Int(ref Caret); break;\n\t\t\tcase nameof(CaretLine): _Int6Int(ref CaretLine); break;\n\t\t\t}\n\t\t\t\n\t\t\tvoid _Style(ref TStyle r, bool hasBack = false) {\n\t\t\t\tif (!a[1].NE() && a[1].ToInt(out int i)) r.color = i;\n\t\t\t\tif (a.Length > 2 && !a[2].NE() && a[2].ToInt(out int i2)) {\n\t\t\t\t\tr.bold = 0 != (i2 & 1);\n\t\t\t\t\tr.italic = 0 != (i2 & 2);\n\t\t\t\t\tr.underline = 0 != (i2 & 4);\n\t\t\t\t\tif (hasBack) r.back = 0 != (i2 & 8);\n\t\t\t\t} else r.bold = r.italic = r.underline = r.back = false;\n\t\t\t}\n\t\t\t\n\t\t\tvoid _Indic(ref TIndicator r) {\n\t\t\t\tint i = r.color; _Int6(ref i); r.color = i;\n\t\t\t\ti = r.alpha; _Alpha(ref i); r.alpha = i;\n\t\t\t}\n\t\t\t\n\t\t\tvoid _Int6(ref int value) {\n\t\t\t\tif (!a[1].NE() && a[1].ToInt(out int i)) value = i;\n\t\t\t\tvalue &= 0xffffff;\n\t\t\t}\n\t\t\t\n\t\t\tvoid _Alpha(ref int value) {\n\t\t\t\tif (a.Length > 2 && !a[2].NE() && a[2].ToInt(out int i)) value = Math.Clamp(i, 0, 255);\n\t\t\t}\n\t\t\t\n\t\t\tvoid _Int6Int(ref int r) {\n\t\t\t\tint i = r; _Int6(ref i);\n\t\t\t\tint j = r >>> 24; _Alpha(ref j);\n\t\t\t\tr = i | (j << 24);\n\t\t\t}\n\t\t}\n\t\t\n\t\treturn true;\n\t}\n\t\n\tpublic void Save() {\n\t\tvar b = new StringBuilder(); //don't need csvTable for such simple values\n\t\tb.AppendLine($\"Font, {FontName}, {FontSize.ToS()}\");\n\t\t_Int6(Background);\n\t\t_Style(None);\n\t\t_Style(Comment);\n\t\t_Style(String);\n\t\t_Style(StringEscape);\n\t\t_Style(Number);\n\t\t_Style(Punctuation);\n\t\t_Style(Operator);\n\t\t_Style(Keyword);\n\t\t_Style(Namespace);\n\t\t_Style(Type);\n\t\t_Style(Function);\n\t\t_Style(Event);\n\t\t_Style(LocalVariable);\n\t\t_Style(Field);\n\t\t_Style(Constant);\n\t\t_Style(Label);\n\t\t_Style(Preprocessor);\n\t\t_Style(Excluded);\n\t\t_Style(XmlDocText);\n\t\t_Style(XmlDocTag);\n\t\t_Style(RxText);\n\t\t_Style(RxMeta);\n\t\t_Style(RxChars);\n\t\t_Style(RxOption);\n\t\t_Style(RxEscape);\n\t\t_Style(RxCallout);\n\t\t_Style(RxComment);\n\t\t_Style(LineNumber);\n\t\t_Int6(LineNumberMargin);\n\t\t_Int6(MarkerMargin);\n\t\t_Indic(IndicRefs);\n\t\t_Indic(IndicBraces);\n\t\t_Indic(IndicDebug);\n\t\t_Indic(IndicFound);\n\t\t_Indic(IndicSnippetField);\n\t\t_Indic(IndicSnippetFieldActive);\n\t\t_Int6Int(SelColor);\n\t\t_Int6Int(SelNofocusColor);\n\t\t_Int6Int(Caret);\n\t\t_Int6Int(CaretLine);\n\t\t\n\t\tvoid _Style(TStyle r, [CallerArgumentExpression(\"r\")] string name = null) {\n\t\t\tb.Append($\"{name}, 0x{r.color:X6}\");\n\t\t\tif (((r.bold ? 1 : 0) | (r.italic ? 2 : 0) | (r.underline ? 4 : 0) | (r.back ? 8 : 0)) is int i && i > 0) b.Append($\", {i.ToS()}\");\n\t\t\tb.AppendLine();\n\t\t}\n\t\t\n\t\tvoid _Indic(TIndicator i, [CallerArgumentExpression(\"i\")] string name = null)\n\t\t\t=> b.AppendLine($\"{name}, 0x{i.color:X6}, {i.alpha.ToS()}\");\n\t\t\n\t\tvoid _Int6(int i, [CallerArgumentExpression(\"i\")] string name = null)\n\t\t\t=> b.AppendLine($\"{name}, 0x{i:X6}\");\n\t\t\n\t\tvoid _Int6Int(int i, [CallerArgumentExpression(\"i\")] string name = null)\n\t\t\t=> b.AppendLine($\"{name}, 0x{i & 0xffffff:X6}, {(i >>> 24).ToS()}\");\n\t\t\n\t\tvar t = new _ThemeInfo(\"\");\n\t\tfilesystem.saveText(t.custPath, b.ToString());\n\t}\n\t\n\t/// <param name=\"multiFont\">Set font only for code styles, not for STYLE_DEFAULT.</param>\n\tpublic void ToScintilla(KScintilla sci, bool multiFont = false, string fontName = null, double? fontSize = null) {\n\t\t//print.it(sci.GetType(), sci.Name);\n\t\t\n\t\tif (!multiFont) sci.aaaStyleFont(STYLE_DEFAULT, fontName ?? FontName, fontSize ?? FontSize);\n\t\tsci.aaaStyleBackColor(STYLE_DEFAULT, Background);\n\t\t//if(None.color != 0) sci.aaaStyleForeColor(STYLE_DEFAULT, None.color); //also would need bold and in ctor above\n\t\tsci.aaaStyleClearAll();\n\t\t\n\t\tvoid _Set(EStyle k, TStyle sty) {\n\t\t\tif (sty.back) {\n\t\t\t\tsci.aaaStyleBackColor((int)k, sty.color);\n\t\t\t\tsci.aaaStyleForeColor((int)k, None.color);\n\t\t\t} else sci.aaaStyleForeColor((int)k, sty.color);\n\t\t\t\n\t\t\tif (sty.bold) sci.aaaStyleBold((int)k, true);\n\t\t\tif (sty.italic) sci.aaaStyleItalic((int)k, true);\n\t\t\tif (sty.underline) sci.aaaStyleUnderline((int)k, true);\n\t\t\tif (multiFont) sci.aaaStyleFont((int)k, fontName ?? FontName, fontSize ?? FontSize);\n\t\t}\n\t\t\n\t\t_Set(EStyle.None, None);\n\t\t_Set(EStyle.Comment, Comment);\n\t\t_Set(EStyle.String, String);\n\t\t_Set(EStyle.StringEscape, StringEscape);\n\t\t_Set(EStyle.Number, Number);\n\t\t_Set(EStyle.Punctuation, Punctuation);\n\t\t_Set(EStyle.Operator, Operator);\n\t\t_Set(EStyle.Keyword, Keyword);\n\t\t_Set(EStyle.Namespace, Namespace);\n\t\t_Set(EStyle.Type, Type);\n\t\t_Set(EStyle.Function, Function);\n\t\t_Set(EStyle.Event, Event);\n\t\t_Set(EStyle.LocalVariable, LocalVariable);\n\t\t_Set(EStyle.Field, Field);\n\t\t_Set(EStyle.Constant, Constant);\n\t\t_Set(EStyle.Label, Label);\n\t\t_Set(EStyle.Preprocessor, Preprocessor);\n\t\t_Set(EStyle.Excluded, Excluded);\n\t\t_Set(EStyle.XmlDocText, XmlDocText);\n\t\t_Set(EStyle.XmlDocTag, XmlDocTag);\n\t\t_Set(EStyle.RxText, RxText);\n\t\t_Set(EStyle.RxMeta, RxMeta);\n\t\t_Set(EStyle.RxChars, RxChars);\n\t\t_Set(EStyle.RxOption, RxOption);\n\t\t_Set(EStyle.RxEscape, RxEscape);\n\t\t_Set(EStyle.RxCallout, RxCallout);\n\t\t_Set(EStyle.RxComment, RxComment);\n\t\t\n\t\t_Set((EStyle)STYLE_LINENUMBER, LineNumber);\n\t\tsci.aaaStyleBackColor(STYLE_LINENUMBER, LineNumberMargin); //documented: sets color of all margins except of type SC_MARGIN_COLOUR and the folding margin\n\t\t\n\t\tsci.aaaStyleForeColor(STYLE_INDENTGUIDE, 0xcccccc);\n\t\t\n\t\tif (sci is SciCode || sci.Name == \"styles\") { //main code editor or Options > Font, colors\n\t\t\t_Indic(Indic.Refs, IndicRefs.color, IndicRefs.alpha, INDIC_FULLBOX);\n\t\t\t_Indic(Indic.Braces, IndicBraces.color, IndicBraces.alpha, INDIC_GRADIENT);\n\t\t\t_Indic(Indic.Debug, IndicDebug.color, IndicDebug.alpha, INDIC_FULLBOX);\n\t\t\t_Indic(Indic.Debug2, IndicDebug.color, 128 + IndicDebug.alpha / 2, INDIC_GRADIENTCENTRE);\n\t\t\t_Indic(Indic.Found, IndicFound.color, IndicFound.alpha, INDIC_FULLBOX);\n\t\t\t_Indic(Indic.SnippetField, IndicSnippetField.color, IndicSnippetField.alpha, INDIC_FULLBOX);\n\t\t\t_Indic(Indic.SnippetFieldActive, IndicSnippetFieldActive.color, IndicSnippetFieldActive.alpha, INDIC_FULLBOX);\n#if DEBUG\n\t\t\t_Indic(Indic.TestBox, 0xff0000, 60, INDIC_FULLBOX);\n\t\t\tsci.aaaIndicatorDefine(Indic.TestStrike, INDIC_STRIKE, 0xff0000);\n\t\t\tsci.aaaIndicatorDefine(Indic.TestPoint, INDIC_POINT, 0xff00ff);\n#endif\n\t\t\t\n\t\t\tvoid _Indic(int indic, int color, int alpha, int style) {\n\t\t\t\tsci.aaaIndicatorDefine(indic, style, color, alpha, 255, underText: true);\n\t\t\t}\n\t\t\t//void _Indic(int indic, int color, int alpha, bool gradient) {\n\t\t\t//\tsci.aaaIndicatorDefine(indic, gradient ? INDIC_GRADIENT : INDIC_FULLBOX, color, alpha, 255, underText: true);\n\t\t\t//}\n\t\t}\n\t\t\n\t\tsci.aaaSetElementColor(SC_ELEMENT_SELECTION_BACK, SelColor);\n\t\tsci.aaaSetElementColor(SC_ELEMENT_SELECTION_INACTIVE_BACK, SelNofocusColor);\n\t\tsci.aaaSetElementColor(SC_ELEMENT_CARET, Caret & 0xffffff);\n\t\tsci.Call(SCI_SETCARETWIDTH, Math.Clamp(Caret >>> 24, 1, 4)); //not DPI-scaled\n\t\tif (sci is SciCode) {\n\t\t\tsci.aaaSetElementColor(SC_ELEMENT_CARET_LINE_BACK, CaretLine & 0xffffff);\n\t\t\tsci.Call(SCI_SETCARETLINEFRAME, Math.Clamp(CaretLine >>> 24, 1, 4)); //not DPI-scaled\n\t\t\tsci.Call(SCI_SETCARETLINEVISIBLEALWAYS, 1);\n\t\t\t\n\t\t\tsci.Call(SCI_SETMARGINBACKN, Margin.Markers, ColorInt.SwapRB(MarkerMargin));\n\t\t}\n\t\tsci.aaaSetElementColor(SC_ELEMENT_WHITE_SPACE, 0x808080); //space/tab visuals and wrap visuals\n\t}\n\t\n\tpublic ref TStyle this[EStyle style] {\n\t\tget {\n\t\t\tswitch (style) {\n\t\t\tcase EStyle.None: return ref None;\n\t\t\tcase EStyle.Comment: return ref Comment;\n\t\t\tcase EStyle.String: return ref String;\n\t\t\tcase EStyle.StringEscape: return ref StringEscape;\n\t\t\tcase EStyle.Number: return ref Number;\n\t\t\tcase EStyle.Punctuation: return ref Punctuation;\n\t\t\tcase EStyle.Operator: return ref Operator;\n\t\t\tcase EStyle.Keyword: return ref Keyword;\n\t\t\tcase EStyle.Namespace: return ref Namespace;\n\t\t\tcase EStyle.Type: return ref Type;\n\t\t\tcase EStyle.Function: return ref Function;\n\t\t\tcase EStyle.Event: return ref Event;\n\t\t\tcase EStyle.LocalVariable: return ref LocalVariable;\n\t\t\tcase EStyle.Field: return ref Field;\n\t\t\tcase EStyle.Constant: return ref Constant;\n\t\t\tcase EStyle.Label: return ref Label;\n\t\t\tcase EStyle.Preprocessor: return ref Preprocessor;\n\t\t\tcase EStyle.Excluded: return ref Excluded;\n\t\t\tcase EStyle.XmlDocText: return ref XmlDocText;\n\t\t\tcase EStyle.XmlDocTag: return ref XmlDocTag;\n\t\t\tcase EStyle.RxText: return ref RxText;\n\t\t\tcase EStyle.RxMeta: return ref RxMeta;\n\t\t\tcase EStyle.RxChars: return ref RxChars;\n\t\t\tcase EStyle.RxOption: return ref RxOption;\n\t\t\tcase EStyle.RxEscape: return ref RxEscape;\n\t\t\tcase EStyle.RxCallout: return ref RxCallout;\n\t\t\tcase EStyle.RxComment: return ref RxComment;\n\t\t\tcase EStyle.LineNumber: return ref LineNumber;\n\t\t\t}\n\t\t\tthrow new InvalidEnumArgumentException();\n\t\t}\n\t}\n\t\n\t/// <param name=\"indicator\">ScConst.indicX.</param>\n\tpublic ref TIndicator Indicator(int indicator) {\n\t\tswitch (indicator) {\n\t\tcase Indic.Refs: return ref IndicRefs;\n\t\tcase Indic.Braces: return ref IndicBraces;\n\t\tcase Indic.Debug: return ref IndicDebug;\n\t\tcase Indic.Found: return ref IndicFound;\n\t\tcase Indic.SnippetField: return ref IndicSnippetField;\n\t\tcase Indic.SnippetFieldActive: return ref IndicSnippetFieldActive;\n\t\t}\n\t\tthrow new InvalidEnumArgumentException();\n\t}\n\t\n\tpublic ref int Element(int element) {\n\t\tswitch (element) {\n\t\tcase Sci.SC_ELEMENT_SELECTION_BACK: return ref SelColor;\n\t\tcase Sci.SC_ELEMENT_SELECTION_INACTIVE_BACK: return ref SelNofocusColor;\n\t\tcase Sci.SC_ELEMENT_CARET: return ref Caret;\n\t\tcase Sci.SC_ELEMENT_CARET_LINE_BACK: return ref CaretLine;\n\t\tcase -1: return ref LineNumberMargin;\n\t\tcase -2: return ref MarkerMargin;\n\t\t}\n\t\tthrow new InvalidEnumArgumentException();\n\t}\n\t\n\t/// <summary>\n\t/// Scintilla style indices of token kinds.\n\t/// </summary>\n\tpublic enum EStyle : byte {\n\t\tNone,\n\t\tComment,\n\t\tString,\n\t\tStringEscape,\n\t\tNumber,\n\t\tPunctuation,\n\t\tOperator,\n\t\tKeyword,\n\t\tNamespace,\n\t\tType,\n\t\tFunction,\n\t\tEvent,\n\t\tLocalVariable,\n\t\tField,\n\t\tConstant,\n\t\tLabel,\n\t\tPreprocessor,\n\t\tExcluded,\n\t\tXmlDocText,\n\t\tXmlDocTag, //tags, CDATA, ///, etc\n\t\tRxText,\n\t\tRxMeta,\n\t\tRxChars,\n\t\tRxOption,\n\t\tRxEscape,\n\t\tRxCallout,\n\t\tRxComment,\n\t\t\n\t\tcountUserDefined,\n\t\t\n\t\tImage = countUserDefined,\n\t\t\n\t\t//STYLE_HIDDEN=31,\n\t\t//STYLE_DEFAULT=32,\n\t\t\n\t\tLineNumber = 33, //STYLE_LINENUMBER\n\t}\n\t\n#pragma warning disable CS1591 //Missing XML comment for publicly visible type or member\n\t/// <summary>\n\t/// Indicator indices.\n\t/// </summary>\n\tpublic static class Indic {\n\t\t//We can use 8-31. KScintilla can use 0-7. Draws indicators from smaller to bigger, eg error on warning.\n\t\tpublic const int\n\t\t\tImages = 8,\n\t\t\tRefs = 9,\n\t\t\tBraces = 10,\n\t\t\tDebug = 11,\n\t\t\tDebug2 = 12,\n\t\t\tFound = 13,\n\t\t\tSnippetField = 14,\n\t\t\tSnippetFieldActive = 15,\n\t\t\tDiagHidden = 20,\n\t\t\tInfo = 21,\n\t\t\tWarning = 22,\n\t\t\tError = 23,\n\t\t\tTestBox = 29,\n\t\t\tTestStrike = 30,\n\t\t\tTestPoint = 31\n\t\t\t;\n\t}\n\t\n\t/// <summary>\n\t/// Marker indices.\n\t/// </summary>\n\tpublic static class Marker {\n\t\t//We can use 0-20. Changes 21-24. Folding 25-31.\n\t\tpublic const int\n\t\t\tUnderline = 0,\n\t\t\tBookmark = 1, BookmarkInactive = 2,\n\t\t\tBreakpoint = 3, BreakpointD = 4, BreakpointC = 5, BreakpointCD = 6, BreakpointL = 7, BreakpointLD = 8,\n\t\t\tDebugLine = 19, DebugLine2 = 20;\n\t}\n\t\n\t/// <summary>\n\t/// Margin indices.\n\t/// </summary>\n\tpublic static class Margin {\n\t\t//Initially 0-4. We can add more with SCI_SETMARGINS.\n\t\tpublic const int\n\t\t\tFold = 0,\n\t\t\tImages = 1,\n\t\t\tMarkers = 2,\n\t\t\tLineNumbers = 3,\n\t\t\tChanges = 4; //currently not impl, just adds some space between line numbers and text\n\t}\n#pragma warning restore CS1591 //Missing XML comment for publicly visible type or member\n}\n"
  },
  {
    "path": "Au.Editor/Edit/SciUndo.cs",
    "content": "using Au.Controls;\nusing static Au.Controls.Sci;\n\nnamespace LA;\n\nclass SciUndo : IDisposable {\n\tpublic static SciUndo OfWorkspace => (App.Model.UndoContext_ ??= new SciUndo()) as SciUndo;\n\t\n\tsqlite _db;\n\tSLTransaction _transaction; //of current record\n\tint _id; //of current record\n\tint _nFiles; //in current record\n\tSciCode _lastDoc; //in current record\n\tint _idToUndo;\n\tHashSet<int> _invalidIds = [];\n\t\n\tvoid IDisposable.Dispose() {\n\t\tif (_db != null) {\n\t\t\t_db.Dispose();\n\t\t\t_db = null;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Starts a multi-file replace undo record.\n\t/// </summary>\n\tpublic void StartReplaceInFiles() {\n\t\t_nFiles = 0;\n\t\t_lastDoc = null;\n\t\ttry {\n\t\t\t//temp database. Uses a temp file when exceeds 2 MB.\n\t\t\t_db ??= new sqlite(@\"\", sql: \"\"\"\nCREATE TABLE records (id INTEGER, descr TEXT);\nCREATE TABLE files (id INTEGER, fileId INTEGER, oldHash BLOB, newHash BLOB, undo BLOB, redo BLOB, find TEXT, repl TEXT);\n\"\"\");\n\t\t}\n\t\tcatch (Exception e1) { Debug_.Print(e1); return; }\n\t\t_transaction = _db.Transaction();\n\t\t_id++;\n\t}\n\t\n\t/// <summary>\n\t/// Commits the multi-file replace undo record started with <see cref=\"StartReplaceInFiles\"/>.\n\t/// </summary>\n\tpublic void FinishReplaceInFiles(string operationDescription) {\n\t\tif (_db == null) return;\n\t\tif (_nFiles == 1 && Panels.Editor.ActiveDoc is SciCode ad && _lastDoc == ad) { //if replaced only in Panels.Editor.ActiveDoc\n\t\t\t_nFiles--;\n\t\t\tad.ESetUndoMark_(-1);\n\t\t}\n\t\tif (_nFiles > 0) {\n\t\t\ttry {\n\t\t\t\t_db.Execute(\"INSERT INTO records VALUES (?, ?)\", _id, operationDescription);\n\t\t\t\t_transaction.Commit();\n\t\t\t\t\n\t\t\t\t//if was undone, that range of ids becomes invalid. Let UndoRedoMultiFileReplace skip them.\n\t\t\t\tfor (int i = _idToUndo + 1; i < _id; i++) _invalidIds.Add(i); //never mind: also should remove from the database\n\t\t\t\t\n\t\t\t\t_idToUndo = _id;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcatch (Exception e1) { print.it(e1); }\n\t\t}\n\t\t_transaction.Rollback();\n\t\t_id--;\n\t}\n\t\n\t/// <summary>\n\t/// Adds an open file to the current record.\n\t/// </summary>\n\tpublic void RifAddFile(SciCode doc, string oldText, string newText, List<StartEndText> changes) {\n\t\tif (_db == null) return;\n\t\tint n = _nFiles;\n\t\tRifAddFile(doc.EFile, oldText, newText, changes);\n\t\tif (_nFiles > n) (_lastDoc = doc).ESetUndoMark_(_id);\n\t}\n\t\n\t/// <summary>\n\t/// Adds a closed file to the current record.\n\t/// </summary>\n\tpublic void RifAddFile(FileNode f, string oldText, string newText, List<StartEndText> changes) {\n\t\tif (_db == null) return;\n\t\t\n\t\t//optimization: don't store full texts of files. Store only hashes and changes. Can make 100 times smaller.\n\t\t\n\t\t//optimization: don't store full info if all \"find\" or/and \"replace\" texts are same. They can be different only if used regex. Can make 4 times smaller.\n\t\tstring sFind = oldText[changes[0].Range]; for (int i = 1; i < changes.Count; i++) if (!oldText.Eq(changes[i].Range, sFind)) { sFind = null; break; }\n\t\tstring sRepl = changes[0].text; for (int i = 1; i < changes.Count; i++) if (changes[i].text != sRepl) { sRepl = null; break; }\n\t\t\n\t\tvar ms = new MemoryStream();\n\t\tvar bw = new BinaryWriter(ms);\n\t\ttry {\n\t\t\tusing var p = _db.Statement(\"INSERT INTO files VALUES (?, ?, ?, ?, ?, ?, ?, ?)\");\n\t\t\tp.Bind(1, _id)\n\t\t\t\t.Bind(2, f.Id)\n\t\t\t\t.BindStruct(3, Hash.MD5(oldText))\n\t\t\t\t.BindStruct(4, Hash.MD5(newText))\n\t\t\t\t.Bind(5, _TextToBlob(false))\n\t\t\t\t.Bind(6, _TextToBlob(true))\n\t\t\t\t.Bind(7, sFind)\n\t\t\t\t.Bind(8, sRepl)\n\t\t\t\t.Step();\n\t\t}\n\t\tcatch (Exception e1) { print.it(e1); return; }\n\t\t_nFiles++;\n\t\t\n\t\tSpan<byte> _TextToBlob(bool redo) {\n\t\t\tms.SetLength(0);\n\t\t\tint offset = 0;\n\t\t\tforeach (var v in changes) {\n\t\t\t\tif (redo) {\n\t\t\t\t\tbw.Write7BitEncodedInt(v.start);\n\t\t\t\t\tif (sFind == null) bw.Write7BitEncodedInt(v.Length);\n\t\t\t\t\tif (sRepl == null) bw.Write(v.text);\n\t\t\t\t} else {\n\t\t\t\t\tbw.Write7BitEncodedInt(v.start + offset);\n\t\t\t\t\toffset += v.text.Length - v.Length;\n\t\t\t\t\tif (sRepl == null) bw.Write7BitEncodedInt(v.text.Length);\n\t\t\t\t\tif (sFind == null) bw.Write(oldText[v.start..v.end]); //not bw.Write(oldText.AsSpan(v.start..v.end));\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn ms.GetBuffer().AsSpan(0, (int)ms.Position);\n\t\t}\n\t}\n\t\n\tbool _RifUndoRedo(bool redo, int id) {\n\t\tif (id < 1 || id > _id) return false;\n\t\t_db.Get(out string descr, \"SELECT descr FROM records WHERE id=?\", id);\n\t\t_db.Get(out int count, \"SELECT COUNT(*) FROM files WHERE id=?\", id);\n\t\tif (!dialog.showOkCancel($\"{(redo ? \"Redo\" : \"Undo\")} in {count} files\", \"It was:\\n\" + descr, DFlags.CenterMouse, owner: App.Hmain))\n\t\t\treturn false; //rejected: option to undo/redo only in this file. Confusing and probably not useful.\n\t\t\n\t\tStringBuilder skipped = null;\n\t\t//these will be reused to make less allocations\n\t\tStringBuilder sb = null;\n\t\tList<StartEndText> aset = new();\n\t\tMemoryStream ms = new();\n\t\tBinaryReader br = new(ms);\n\t\t\n\t\tusing var x = _db.Statement(\"SELECT fileId, oldHash, newHash, undo, redo, find, repl FROM files WHERE id=?\", id);\n\t\twhile (x.Step()) {\n\t\t\tif (App.Model.FindById(x.GetInt(0)) is not FileNode f) continue;\n\t\t\t//print.it(f);\n\t\t\tif (f.OpenDoc is SciCode doc) {\n\t\t\t\tint mark = doc.EGetUndoMark_(redo);\n\t\t\t\tif (mark == id) {\n\t\t\t\t\tDebug_.PrintIf(Hash.MD5(doc.aaaText) != _HashBefore());\n\t\t\t\t\t_UndoRedo(doc, redo, mark);\n\t\t\t\t\tDebug_.PrintIf(Hash.MD5(doc.aaaText) != _HashAfter());\n\t\t\t\t} else if (0 == doc.Call(SCI_CANUNDO) && 0 == doc.Call(SCI_CANREDO) && Hash.MD5(doc.aaaText) == _HashBefore()) { //opened later\n\t\t\t\t\tdoc.EReplaceTextGently(_TextFromBlob(doc.aaaText));\n\t\t\t\t\tdoc.Call(SCI_EMPTYUNDOBUFFER);\n\t\t\t\t} else {\n\t\t\t\t\t_Skipped(f);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tdoc.ESaveText_(true);\n\t\t\t} else {\n\t\t\t\ttry {\n\t\t\t\t\tvar textNow = filesystem.loadText(f.FilePath);\n\t\t\t\t\tif (Hash.MD5(textNow) == _HashBefore()) {\n\t\t\t\t\t\tf.SaveNewTextOfClosedFile(_TextFromBlob(textNow));\n\t\t\t\t\t} else {\n\t\t\t\t\t\t_Skipped(f);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (Exception e1) { _Skipped(f, e1.ToString()); }\n\t\t\t}\n\t\t\t\n\t\t\tHash.MD5Result _HashBefore() => x.GetStruct<Hash.MD5Result>(redo ? 1 : 2);\n\t\t\tHash.MD5Result _HashAfter() => x.GetStruct<Hash.MD5Result>(redo ? 2 : 1);\n\t\t\t\n\t\t\tunsafe string _TextFromBlob(string textNow) {\n\t\t\t\tstring sFind = x.GetText(5), sRepl = x.GetText(6);\n\t\t\t\t\n\t\t\t\taset.Clear();\n\t\t\t\tvar blob = x.GetBlob(redo ? 4 : 3, out int len1);\n\t\t\t\tms.SetLength(0); ms.Write(new(blob, len1)); ms.Position = 0;\n\t\t\t\t\n\t\t\t\twhile (ms.Position < len1) {\n\t\t\t\t\tint i = br.Read7BitEncodedInt();\n\t\t\t\t\tint len = (redo && sFind != null) ? sFind.Length : (!redo && sRepl != null) ? sRepl.Length : br.Read7BitEncodedInt();\n\t\t\t\t\tstring s = (redo && sRepl != null) ? sRepl : (!redo && sFind != null) ? sFind : br.ReadString();\n\t\t\t\t\taset.Add(new(i, i + len, s));\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tStartEndText.ReplaceAll(textNow, aset, ref sb);\n\t\t\t\treturn sb.ToString();\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (skipped != null) print.it(skipped);\n\t\t\n\t\tvoid _Skipped(FileNode f, string s = null) {\n\t\t\tskipped ??= new($\"<>The multi-file {(redo ? \"redo\" : \"undo\")} operation skipped these files:\\r\\n\");\n\t\t\tskipped.AppendLine($\"\\t{f.SciLink(true)}. {s ?? \"Text modified later.\"}\");\n\t\t}\n\t\t\n\t\t_idToUndo = redo ? id : id - 1;\n\t\treturn true;\n\t}\n\t\n\tpublic void UndoRedo(bool redo) {\n\t\tvar doc = Panels.Editor.ActiveDoc; if (doc == null) return;\n\t\tint mark = doc.EGetUndoMark_(redo);\n\t\tif (mark > 0)\n\t\t\t_RifUndoRedo(redo, mark);\n\t\telse\n\t\t\t_UndoRedo(doc, redo, mark);\n\t}\n\t\n\t/// <summary>\n\t/// Calls SCI_UNDO or SCI_REDO.\n\t/// If <i>mark</i> != 0, does not change current position at the end of the operation.\n\t/// To set and get mark use <see cref=\"SciCode.ESetUndoMark_\"/> and <see cref=\"SciCode.EGetUndoMark_\"/>.\n\t/// </summary>\n\tstatic void _UndoRedo(SciCode doc, bool redo, int mark) {\n\t\tdoc.Call(redo ? SCI_REDO : SCI_UNDO, mark != 0);\n\t}\n\t\n\tpublic void UndoRedoMultiFileReplace(bool redo) {\n\t\tint id = _idToUndo; if (redo) id++;\n\t\twhile (_invalidIds.Contains(id)) id += redo ? 1 : -1;\n\t\t_RifUndoRedo(redo, id);\n\t}\n}\n\npartial class SciCode {\n\tList<(int u, int mark)> _undoMarks;\n\t\n\tinternal void ESetUndoMark_(int mark) {\n\t\t_undoMarks ??= [];\n\t\tint u = Call(SCI_GETUNDOCURRENT) - 1;\n\t\tif (u < 0) return;\n\t\tif (0 != (256 & Call(SCI_GETUNDOACTIONTYPE, u))) throw new InvalidOperationException(\"Cannot set Undo mark for a possibly not full Undo action\"); //has flag \"can coalesce\"\n\t\tfor (int i = _undoMarks.Count; --i >= 0;) {\n\t\t\tif (_undoMarks[i].u == u) { _undoMarks[i] = (u, mark); return; }\n\t\t}\n\t\t_undoMarks.Add((u, mark));\n\t}\n\t\n\tinternal int EGetUndoMark_(bool redo) {\n\t\tif (!_undoMarks.NE_()) {\n\t\t\tint u = Call(SCI_GETUNDOCURRENT);\n\t\t\tif (redo) {\n\t\t\t\tint n = Call(SCI_GETUNDOACTIONS);\n\t\t\t\twhile (u < n && 0 != (256 & Call(SCI_GETUNDOACTIONTYPE, u))) u++; //if multiple actions coalesced, get the last, because only it can be marked\n\t\t\t\tif (u >= n) return 0;\n\t\t\t} else {\n\t\t\t\tif (--u < 0) return 0;\n\t\t\t}\n\t\t\t//print.it(u, _GetUndoStack(), _undoMarks);\n\t\t\tvar a = _undoMarks;\n\t\t\tfor (int i = a.Count; --i >= 0;) {\n\t\t\t\tif (a[i].u == u) return a[i].mark;\n\t\t\t\tif (a[i].u < u) break;\n\t\t\t}\n\t\t}\n\t\treturn 0;\n\t}\n\t\n\tvoid _ManageUndoOnModified(MOD mod) {\n\t\tif (mod.Has(MOD.SC_PERFORMED_USER)) {\n\t\t\t//from _undoMarks remove items for Scintilla Undo actions that have been removed by SCI_EMPTYUNDOBUFFER or when user-modified after an Undo or non-last Redo\n\t\t\tif (_undoMarks is { } a) {\n\t\t\t\tint n = Call(SCI_GETUNDOACTIONS) - 1; //the last action in the Scintilla's Undo stack. If the Undo stack previously had >= actions than now, they were removed and added this action.\n\t\t\t\twhile (a.Count > 0 && a[^1].u >= n) a.RemoveAt(a.Count - 1);\n\t\t\t}\n\t\t}\n\t}\n\t\n\t//#if DEBUG\n\t//\tint[] _GetUndoStack() {\n\t//\t\tint n = Call(SCI_GETUNDOACTIONS);\n\t//\t\tvar a = new int[n];\n\t//\t\tfor (int i = 0; i < n; i++) {\n\t//\t\t\ta[i] = Call(SCI_GETUNDOACTIONTYPE, i);\n\t//\t\t}\n\t//\t\treturn a;\n\t//\t}\n\t//#endif\n\t\n\t/// <summary>\n\t/// <c>=> new EUndoAction(this, onUndoDontChangeCaretPos);</c>\n\t/// </summary>\n\tpublic EUndoAction ENewUndoAction(bool onUndoDontChangeCaretPos = false) => new EUndoAction(this, onUndoDontChangeCaretPos);\n\t\n\t/// <summary>\n\t/// Ctor calls <see cref=\"KScintilla.aaaBeginUndoAction\"/>. Dispose() calls <see cref=\"KScintilla.aaaEndUndoAction\"/>.\n\t/// Does nothing if it's a nested undo action.\n\t/// </summary>\n\tpublic struct EUndoAction : IDisposable {\n\t\tSciCode _sci;\n\t\tbool _onUndoDontChangeCaretPos;\n\t\t\n\t\t/// <summary>\n\t\t/// Calls SCI_BEGINUNDOACTION.\n\t\t/// </summary>\n\t\t/// <param name=\"sci\">Can be null, then does nothing.</param>\n\t\t/// <param name=\"onUndoDontChangeCaretPos\"></param>\n\t\tpublic EUndoAction(SciCode sci, bool onUndoDontChangeCaretPos = false) {\n\t\t\t_onUndoDontChangeCaretPos = onUndoDontChangeCaretPos;\n\t\t\t_sci = sci;\n\t\t\t_sci?.aaaBeginUndoAction();\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Calls SCI_ENDUNDOACTION and clears this variable.\n\t\t/// </summary>\n\t\tpublic void Dispose() {\n\t\t\tif (_sci != null) {\n\t\t\t\tif (0 == _sci.aaaEndUndoAction()) {\n\t\t\t\t\tif (_onUndoDontChangeCaretPos) _sci.ESetUndoMark_(-1);\n\t\t\t\t}\n\t\t\t\t_sci = null;\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Files/DProperties.cs",
    "content": "//TODO2: UI to set r etc alias and noCopy\n\nusing System.Windows;\nusing System.Windows.Controls;\nusing Au.Controls;\nusing Microsoft.Win32;\nusing System.Drawing;\nusing System.Windows.Documents;\nusing ToolLand;\n\nnamespace LA;\n\nclass DProperties : KDialogWindow {\n\tpublic static void ShowFor(FileNode f) {\n\t\tf.SingleDialog(() => new DProperties(f));\n\t}\n\t\n\treadonly FileNode _f;\n\treadonly MetaCommentsParser _meta;\n\treadonly bool _isClass;\n\tMCRole _role;\n\tint _miscFlags;\n\t\n\t//controls\n\treadonly KSciInfoBox info;\n\treadonly ComboBox role, ifRunning, uac, warningLevel, nullable, platform;\n\treadonly TextBox testScript, define, noWarnings, testInternal, preBuild, postBuild, findInLists;\n\treadonly ComboBox outputPath, icon, manifest, sign;\n\treadonly KCheckBox xmlDoc, console, optimize, cMultiline;\n\treadonly KGroupBoxSeparator gAssembly, gCompile;\n\treadonly Panel pRun;\n\treadonly Button addNuget, addLibrary, addComRegistry, addComBrowse, addProject, addClassFile, addResource, addFile;\n\t\n\tDProperties(FileNode f) {\n\t\t_f = f;\n\t\t_isClass = f.IsClass;\n\t\t_meta = new MetaCommentsParser(_f);\n\t\t\n\t\tInitWinProp(\"Properties of \" + _f.Name, App.Wmain);\n\t\t\n\t\tvar b = new wpfBuilder(this).WinSize(800).Columns(-1, 20, -1, 20, 0);\n\t\tb.Options(bindLabelVisibility: true);\n\t\tb.R.Add(out info).Height(60).Margin(\"B8\");\n\t\t\n\t\t//. left column\n\t\tb.R.StartGrid();\n\t\t\n\t\tb.R.StartGrid<KGroupBoxSeparator>(\"Role\");\n\t\tb.R.Add(\"role\", out role);\n\t\tb.End();\n\t\t\n\t\tb.R.StartGrid(out gCompile, \"Compile\").Columns(0, -1, 20, 0, -2);\n\t\tb.R.Add(out optimize, \"optimize\");\n\t\tb.R.Add(\"define\", out define);\n\t\tb.R.Add(\"warningLevel\", out warningLevel).Editable();\n\t\tb.Skip().Add(\"nullable\", out nullable);\n\t\tb.R.Add(\"noWarnings\", out noWarnings);\n\t\tb.R.Add(\"testInternal\", out testInternal);\n\t\tb.R.Add(\"preBuild\", out preBuild);\n\t\tb.R.Add(\"postBuild\", out postBuild);\n\t\tb.End();\n\t\t\n\t\tb.End();\n\t\t//..\n\t\t\n\t\t//. center column\n\t\tb.Skip(1).StartGrid();\n\t\t\n\t\tb.R.StartGrid<KGroupBoxSeparator>(\"Run\");\n\t\tb.StartGrid(); pRun = b.Panel;\n\t\tb.R.Add(\"ifRunning\", out ifRunning);\n\t\tb.R.Add(\"uac\", out uac);\n\t\tb.End();\n\t\tb.R.Add(\"testScript\", out testScript)\n\t\t\t.Validation(_ => testScript.IsVisible && testScript.IsEnabled && testScript.Text is string s1 && s1.Length > 0 && null == _f.FindRelative(true, s1, FNFind.CodeFile) ? \"testScript not found\" : null);\n\t\tb.End();\n\t\t\n\t\tb.R.StartGrid(out gAssembly, \"Assembly\");\n\t\tb.Add(\"outputPath\", out outputPath).Editable();\n\t\toutputPath.DropDownOpened += _OutputPath_DropDownOpened;\n\t\tb.R.Add(\"icon\", out icon).Editable()\n\t\t\t.Add(\"manifest\", out manifest).Editable();\n\t\tb.R.Add(\"sign\", out sign).Editable();\n\t\ticon.DropDownOpened += _IconManifestSign_DropDownOpened;\n\t\tmanifest.DropDownOpened += _IconManifestSign_DropDownOpened;\n\t\tsign.DropDownOpened += _IconManifestSign_DropDownOpened;\n\t\tb.R.Add(out console, \"console\")\n\t\t\t.And(0).Add(out xmlDoc, \"xmlDoc\");\n\t\tb.R.Add(\"platform\", out platform).Width(100, \"L\");\n\t\tb.End();\n\t\t\n\t\tb.End();\n\t\t//..\n\t\t\n\t\t//. right column\n\t\tb.Skip(1).StartStack(vertical: true);\n\t\t\n\t\tb.StartGrid<KGroupBoxSeparator>(\"Add reference\");\n\t\tb.R.AddButton(out addLibrary, \"Library...\", _ButtonClick_addLibrary);\n\t\tb.R.AddButton(out addNuget, \"NuGet ▾\", _ButtonClick_addNuget);\n\t\tb.R.AddButton(out addComRegistry, \"COM ▾\", _bAddComRegistry_Click)\n\t\t\t.AddButton(out addComBrowse, \"...\", _bAddComBrowse_Click).Width(30);\n\t\tb.AddButton(out addProject, \"Project ▾\", _ButtonClick_addProject);\n\t\tb.End();\n\t\t\n\t\tb.StartStack<KGroupBoxSeparator>(\"Add file\", vertical: true);\n\t\tb.AddButton(out addClassFile, \"Class file ▾\", _ButtonClick_addClass);\n\t\tb.AddButton(out addResource, \"Resource ▾\", _ButtonClick_addFile);\n\t\tb.AddButton(out addFile, \"Other file ▾\", _ButtonClick_addFile);\n\t\tb.End();\n\t\t\n\t\tb.Add<AdornerDecorator>().Child().Add(out findInLists).Watermark(\"Find in lists\")\n\t\t\t.Tooltip(\"In button drop-down lists show only items containing this text.\\n\\nTip: to hide garbage files, put them in folder(s) named \\\"Garbage\\\".\");\n\t\t\n\t\tb.End();\n\t\t//..\n\t\t\n\t\tb.R.AddSeparator();\n\t\t\n\t\tb.R.StartGrid().Columns(-1, 0, 0);\n\t\tb.Add(out cMultiline, \"/*/ multiple lines /*/\").Checked(_meta.Multiline);\n\t\tb.Options(modifyPadding: false); //workaround for: OK/Cancel text incorrectly vcentered. Only on Win11, only in this dialog, only when this dialog is not in the secondary screen with DPI 125%.\n\t\tb.AddOkCancel();\n\t\tb.xAddDialogHelpButtonAndF1(\"editor/File properties\");\n\t\tb.End();\n\t\t\n\t\tb.End();\n\t\t\n\t\t_role = _meta.role switch {\n\t\t\t\"miniProgram\" => MCRole.miniProgram,\n\t\t\t\"exeProgram\" => MCRole.exeProgram,\n\t\t\t\"editorExtension\" => MCRole.editorExtension,\n\t\t\t\"classLibrary\" when _isClass => MCRole.classLibrary,\n\t\t\t\"classFile\" when _isClass => MCRole.classFile,\n\t\t\t_ => _isClass ? MCRole.classFile : MCRole.miniProgram,\n\t\t};\n\t\t_InitCombo(role, _isClass ? \"miniProgram|exeProgram|editorExtension|classLibrary|classFile\" : \"miniProgram|exeProgram|editorExtension\", null, (int)_role);\n\t\ttestScript.Text = _f.TestScript?.ItemPath;\n\t\t_miscFlags = _meta.miscFlags.ToInt();\n\t\t//Run\n\t\t_InitCombo(ifRunning, \"warn_restart|warn|cancel_restart|cancel|wait_restart|wait|run_restart|run|restart|end|end_restart\", _meta.ifRunning);\n\t\t_InitCombo(uac, \"inherit|user|admin\", _meta.uac);\n\t\t//Assembly\n\t\toutputPath.Text = _meta.outputPath;\n\t\tvoid _OutputPath_DropDownOpened(object sender, EventArgs e) {\n\t\t\toutputPath.IsDropDownOpen = false;\n\t\t\tvar m = new popupMenu();\n\t\t\tm[_GetOutputPath(getDefault: true)] = o => outputPath.Text = o.ToString();\n\t\t\tbool isLibrary = _role == MCRole.classLibrary;\n\t\t\tif (isLibrary) m[@\"%folders.ThisApp%\\Libraries\"] = o => outputPath.Text = o.ToString();\n\t\t\tm[\"Browse...\"] = o => {\n\t\t\t\tvar initf = _GetOutputPath(getDefault: false, expandEnvVar: true);\n\t\t\t\tif (!isLibrary && !filesystem.exists(initf)) initf = pathname.getDirectory(initf);\n\t\t\t\tfilesystem.createDirectory(initf);\n\t\t\t\tvar d = new FileOpenSaveDialog(isLibrary ? \"{4D1F3AFB-DA1A-45AC-8C12-41DDA5C51CDD}\" : \"{4D1F3AFB-DA1A-45AC-8C12-51DDA5C51CDD}\") {\n\t\t\t\t\tInitFolderFirstTime = initf,\n\t\t\t\t};\n\t\t\t\tif (d.ShowOpen(out string s, this, selectFolder: true)) outputPath.Text = folders.unexpandPath(s);\n\t\t\t};\n\t\t\tm.Show(owner: this);\n\t\t}\n\t\ticon.Text = _meta.icon;\n\t\tmanifest.Text = _meta.manifest;\n\t\tsign.Text = _meta.sign;\n\t\tif (_meta.console is \"true\" or \"!false\") console.IsChecked = true;\n\t\t_InitCombo(platform, \"Default|x64|arm64|x86\", _meta.platform);\n\t\tif (_meta.xmlDoc == \"true\") xmlDoc.IsChecked = true;\n\t\t//Compile\n\t\tif (_meta.optimize is \"true\" or \"!false\") optimize.IsChecked = true;\n\t\tdefine.Text = _meta.define;\n\t\t_InitCombo(warningLevel, \"8|7|6|5|4|3|2|1|0\", _meta.warningLevel, 0);\n\t\tnoWarnings.Text = _meta.noWarnings;\n\t\t_InitCombo(nullable, \"disable|enable|warnings|annotations\", _meta.nullable);\n\t\ttestInternal.Text = _meta.testInternal;\n\t\tpreBuild.Text = _meta.preBuild;\n\t\tpostBuild.Text = _meta.postBuild;\n\t\t\n\t\tstatic void _InitCombo(ComboBox c, string items, string meta, int index = 0) {\n\t\t\tvar a = items.Split('|');\n\t\t\tif (meta != null) index = Array.IndexOf(a, meta);\n\t\t\tforeach (var v in a) c.Items.Add(v);\n\t\t\tc.SelectedIndex = Math.Max(0, index);\n\t\t}\n\t\t\n\t\t_ChangedRole();\n\t\trole.SelectionChanged += (_, _) => {\n\t\t\t_role = (MCRole)role.SelectedIndex;\n\t\t\t_ChangedRole();\n\t\t};\n\t\tvoid _ChangedRole() {\n\t\t\tbool? ts = _role is MCRole.classLibrary or MCRole.classFile ? (_f.FindProject(out _, out var pmain) && _f != pmain ? null : true) : false;\n\t\t\t_ShowCollapse(testScript, ts != false);\n\t\t\tif (ts == null) { testScript.IsEnabled = false; testScript.Text = \"<the first C# file of this project>\"; }\n\t\t\t_ShowCollapse(_role is MCRole.miniProgram or MCRole.exeProgram, pRun, console, icon);\n\t\t\t_ShowCollapse(_role is MCRole.exeProgram or MCRole.classLibrary, outputPath);\n\t\t\t_ShowCollapse(_role is MCRole.exeProgram, manifest, platform);\n\t\t\t_ShowCollapse(_role == MCRole.classLibrary, xmlDoc);\n\t\t\t_ShowCollapse(_role != MCRole.classFile, gAssembly, gCompile);\n\t\t}\n\t\t\n\t\t//rejected. Will display error in code editor. Rarely used. For some would need to remove /suffix.\n\t\t//string _ValidateFile(FrameworkElement e, string name, FNFind kind) =>\n\t\t\n\t\tb.OkApply += _OkApply;\n\t}\n\t\n\tprotected override void OnSourceInitialized(EventArgs e) {\n\t\t_InitInfo();\n\t\tbase.OnSourceInitialized(e);\n\t}\n\t\n\tvoid _GetMeta() {\n\t\t//info: _Get returns null if hidden\n\t\t\n\t\t_f.TestScript = _Get(testScript) is string sts && testScript.IsEnabled ? _f.FindRelative(true, sts, FNFind.CodeFile) : null; //validated\n\t\t\n\t\t_meta.ifRunning = _Get(ifRunning, defaultIndex: 0);\n\t\t_meta.uac = _Get(uac, defaultIndex: 0);\n\t\t_meta.platform = _Get(platform, defaultIndex: 0);\n\t\t\n\t\t_meta.console = _Get(console);\n\t\t_meta.icon = _Get(icon);\n\t\t_meta.manifest = _Get(manifest);\n\t\t//_meta.resFile = _Get(resFile);\n\t\t_meta.sign = _Get(sign);\n\t\t_meta.xmlDoc = _Get(xmlDoc);\n\t\t\n\t\t_meta.optimize = _Get(optimize);\n\t\t_meta.define = _Get(define);\n\t\t_meta.warningLevel = _Get(warningLevel, defaultIndex: 0);\n\t\t_meta.noWarnings = _Get(noWarnings);\n\t\t_meta.nullable = _Get(nullable, defaultIndex: 0);\n\t\t_meta.testInternal = _Get(testInternal);\n\t\t_meta.preBuild = _Get(preBuild);\n\t\t_meta.postBuild = _Get(postBuild);\n\t\t\n\t\tvar oldRole = _meta.role;\n\t\t_meta.role = null;\n\t\t_meta.outputPath = null;\n\t\tif (_role != MCRole.classFile) {\n\t\t\tif (_isClass || _role != MCRole.miniProgram) _meta.role = _role.ToString();\n\t\t\tswitch (_role) {\n\t\t\tcase MCRole.exeProgram:\n\t\t\tcase MCRole.classLibrary:\n\t\t\t\t_meta.outputPath = _GetOutputPath(getDefault: false);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\t\n\t\t_meta.miscFlags = _miscFlags == 0 ? null : _miscFlags.ToS();\n\t\t\n\t\tif (_isClass && oldRole is null or \"classLibrary\" && !(_meta.role is null or \"classLibrary\")) {\n\t\t\tprint.it($$\"\"\"\n<>Info: Now <help editor/Class files, projects>class file<> '{{_f.Name}}' can be executed directly. Just add code that calls a class function. Example:\n<code>/*/ role {{_meta.role}}; define TEST; /*/\n#if TEST //allows to use this class file elsewhere via /*/ c {{_f.Name}}; /*/\nClass1.Function1();\n#endif\nclass Class1 {\n\tpublic static void Function1() {\n\t\tprint.it(1);\n\t}\n}\n</code>\n\"\"\");\n\t\t}\n\t}\n\t\n\tvoid _OkApply(WBButtonClickArgs e) {\n\t\tif (App.Model.CurrentFile != _f && !App.Model.SetCurrentFile(_f)) return;\n\t\t_GetMeta();\n\t\t_meta.Multiline = cMultiline.IsChecked;\n\t\t_meta.Apply();\n\t}\n\t\n\tvoid _ButtonClick_addLibrary(WBButtonClickArgs e) {\n\t\tstring dir1 = App.Model.DllDirectory, dir2 = folders.ThisAppBS + \"Libraries\", initDir = null;\n\t\tbool exists1 = filesystem.exists(dir1).Directory, exists2 = filesystem.exists(dir2).Directory;\n\t\tif (exists1 || exists2) {\n\t\t\tvar m = new popupMenu { CheckDontClose = true };\n\t\t\tif (exists1) m.Add(1, @\"%folders.Workspace%\\dll\");\n\t\t\tif (exists2) m.Add(2, @\"%folders.ThisApp%\\Libraries\");\n\t\t\tm.Add(3, \"Last used folder\");\n\t\t\tm.Separator();\n\t\t\tm.AddCheck(\"Unexpand path (option)\", App.Settings.tools_pathUnexpand, o => App.Settings.tools_pathUnexpand ^= true);\n\t\t\tint r = m.Show(owner: this); if (r == 0) return;\n\t\t\tif (r == 1) initDir = dir1; else if (r == 2) initDir = dir2;\n\t\t}\n\t\tvar d = new FileOpenSaveDialog(\"{4D1F3AFB-DA1A-47AC-8C12-41DDA5C51CDB}\") {\n\t\t\tFileTypes = \"Dll|*.dll|All files|*.*\",\n\t\t\tInitFolderNow = initDir\n\t\t};\n\t\tif (!d.ShowOpen(out string[] a, this)) return;\n\t\t\n\t\tif (!TUtil2.UnexpandPathsMetaR(a, this)) return;\n\t\t\n\t\t_meta.r.AddRange(a);\n\t\t_ShowInfo_Added(e.Button, _meta.r);\n\t}\n\t\n\tvoid _ButtonClick_addNuget(WBButtonClickArgs e) {\n\t\tvar a = DNuget.GetInstalledPackages();\n\t\tif (a == null) {\n\t\t\tdialog.showInfo(null, \"There are no NuGet packages installed in this workspace.\\nTo install NuGet packages, use menu Tools > NuGet.\", owner: this);\n\t\t\treturn;\n\t\t}\n\t\tvar sFind = findInLists.Text;\n\t\tif (!sFind.NE()) {\n\t\t\ta = a.Where(s => s.Contains(sFind, StringComparison.OrdinalIgnoreCase)).ToArray();\n\t\t\tif (!a.Any()) return;\n\t\t}\n\t\tint i = popupMenu.showSimple(a, owner: this, rawText: true) - 1; if (i < 0) return;\n\t\t_meta.nuget.Add(a[i]);\n\t\t_ShowInfo_Added(e.Button, _meta.nuget);\n\t}\n\t\n\tvoid _ButtonClick_addProject(WBButtonClickArgs e)\n\t\t=> _AddFromWorkspace(\n\t\t\tf => (f != _f && f.GetClassFileRole() == FNClassFileRole.Library) ? f : null,\n\t\t\t_meta.pr, false, e.Button);\n\t\n\tvoid _ButtonClick_addClass(WBButtonClickArgs e) {\n\t\tFileNode prFolder1 = null;\n\t\tif (_f.IsScript && _f.FindProject(out prFolder1, out var prMain1, ofAnyScript: true) && _f == prMain1) prFolder1 = null;\n\t\t\n\t\tbool _Include(FileNode f) {\n\t\t\tif (!f.IsClass || f == _f) return false;\n\t\t\tif (f.FindProject(out var prFolder, out var prMain) && !prFolder.Name.Starts(\"@@\")) { //exclude class files that are in projects, except if project name starts with @@\n\t\t\t\tif (prFolder != prFolder1) return false; //but if _f is a non-project script in a project folder, include local classes\n\t\t\t}\n\t\t\treturn f.GetClassFileRole() == FNClassFileRole.Class;\n\t\t}\n\t\t\n\t\t_AddFromWorkspace(f => _Include(f) ? f : null, _meta.c, false, e.Button);\n\t}\n\t\n\tvoid _ButtonClick_addFile(WBButtonClickArgs e) {\n\t\tbool isResource = e.Button == addResource;\n\t\tvar a = isResource ? _meta.resource : _meta.file;\n\t\tvar m = new popupMenu();\n\t\tif (_f.FindProject(out var proj, out _, ofAnyScript: true)) m.Submenu(\"Project\", m => _AddFW(m, proj));\n\t\tm.Submenu(\"All\", m => _AddFW(m));\n\t\tm.Submenu(\"By type\", m => _AddFW(m, sortByType: true));\n\t\tif (isResource) {\n\t\t\tm.Submenu(\"Options\", m => {\n\t\t\t\tm.AddCheck(\"Add XAML icons from code strings like \\\"*name color\\\"\",\n\t\t\t\t\tcheck: _role is not (MCRole.editorExtension or MCRole.classFile) && 0 == (_miscFlags & 1),\n\t\t\t\t\tclick: o => { if (o.IsChecked) _miscFlags &= ~1; else _miscFlags |= 1; });\n\t\t\t});\n\t\t}\n\t\tm.Show(owner: this);\n\t\t\n\t\tvoid _AddFW(popupMenu m, FileNode proj = null, bool sortByType = false) {\n\t\t\t_AddFromWorkspace(\n\t\t\t\tf => {\n\t\t\t\t\tif (f.IsCodeFile) return null;\n\t\t\t\t\tif (f.IsFolder) { //add if contains non-code files and does not contain code files\n\t\t\t\t\t\tif (proj == null) return null;\n\t\t\t\t\t\tbool has = false;\n\t\t\t\t\t\tforeach (var v in f.Descendants()) { if (v.IsCodeFile) return null; has |= !v.IsFolder; }\n\t\t\t\t\t\tif (!has) return null;\n\t\t\t\t\t}\n\t\t\t\t\treturn f;\n\t\t\t\t}, a, true, e.Button, proj, m, sortByType);\n\t\t}\n\t}\n\t\n\tvoid _AddFromWorkspace(Func<FileNode, FileNode> filter, List<string> metaList, bool withIcons, UIElement clicked, FileNode folder = null, popupMenu pm = null, bool sortByType = false, bool noInfo = false) {\n\t\tvar sFind = findInLists.Text;\n\t\tList<(FileNode f, string s, bool near)> a = new();\n\t\tfolder ??= App.Model.Root;\n\t\tforeach (var f in folder.DescendantsExceptGarbage()) {\n\t\t\tif (filter(f) is not FileNode f2) continue;\n\t\t\t\n\t\t\tvar path = f2.ItemPath;\n\t\t\tif (sFind.Length > 0 && path.Find(sFind, true) < 0) continue;\n\t\t\t\n\t\t\t//if (_f.Parent.Parent != null && f.IsDescendantOf(_f.Parent)) path = \".\" + f.ItemPathIn(_f.Parent); //rejected. Bad with export-import.\n\t\t\t\n\t\t\tif (!metaList.Contains(path, StringComparer.OrdinalIgnoreCase)) {\n\t\t\t\tbool near = _f.Parent.Parent != null && f.IsDescendantOf(_f.Parent);\n\t\t\t\ta.Add((f2, path, near));\n\t\t\t}\n\t\t}\n\t\tif (a.Count == 0) {\n\t\t\tif (pm == null) _ShowInfo_ListEmpty(clicked, sFind);\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tif (sortByType) {\n\t\t\ta = a.OrderBy(o => o.f.FileExt).ThenBy(o => !o.near).ThenBy(o => o.s).ToList();\n\t\t} else {\n\t\t\ta = a.OrderBy(o => !o.near).ThenBy(o => o.s).ToList();\n\t\t}\n\t\t\n\t\tvar m = pm ?? new popupMenu();\n\t\tstring prevExt = null;\n\t\tforeach (var (f, s, near) in a) {\n\t\t\tif (sortByType) {\n\t\t\t\tvar ext = f.FileExt;\n\t\t\t\tif (prevExt != null && !ext.Eqi(prevExt)) m.Separator();\n\t\t\t\tprevExt = ext;\n\t\t\t}\n\t\t\tvar v = m.Add(s.Limit(80, middle: true), o => {\n\t\t\t\t//metaList.Add(s[0] == '.' ? s : f.ItemPathOrName());\n\t\t\t\tmetaList.Add(f.ItemPathOrName(relativeTo: _f));\n\t\t\t\tif (!noInfo) _ShowInfo_Added(clicked, metaList);\n\t\t\t}, withIcons ? f.FilePath : null);\n\t\t\t//if (s[0] == '.') v.TextColor = 0x0080ff;\n\t\t\tif (near) v.TextColor = 0x0080ff;\n\t\t}\n\t\tif (pm == null) m.Show(owner: this);\n\t}\n\t\n\tvoid _IconManifestSign_DropDownOpened(object sender, EventArgs e) {\n\t\tvar cb = sender as ComboBox;\n\t\tcb.IsDropDownOpen = false;\n\t\t\n\t\tvar ext = cb == icon ? \".ico\" : cb == manifest ? \".manifest\" : \".snk\";\n\t\tList<string> r = new();\n\t\t_AddFromWorkspace(f => f.FileType == FNType.Other && f.FileExt.Eqi(ext) ? f : null, r, cb == icon, cb, noInfo: true);\n\t\tif (r.Count > 0) cb.Text = r[0];\n\t}\n\t\n\t#region COM\n\t\n\tvoid _bAddComBrowse_Click(WBButtonClickArgs e) {\n\t\tvar m = new popupMenu();\n\t\tm[\"Select and convert a COM library...\"] = _ => {\n\t\t\tvar d = new FileOpenSaveDialog(\"{4D1F3AFB-DA1A-45AC-8C12-41DDA5C51CDC}\") {\n\t\t\t\tFileTypes = \"Type library|*.dll;*.tlb;*.olb;*.ocx;*.exe|All files|*.*\"\n\t\t\t};\n\t\t\tif (d.ShowOpen(out string s, this))\n\t\t\t\t_ConvertTypeLibrary(s, e.Button);\n\t\t};\n\t\tvar dir = folders.Workspace + @\".interop\";\n\t\tif (filesystem.exists(dir)) {\n\t\t\tm.Submenu(\"Use converted\", m => {\n\t\t\t\tforeach (var f in filesystem.enumFiles(dir, \"*.dll\")) {\n\t\t\t\t\tm[f.Name] = o => {\n\t\t\t\t\t\tvar s = o.Text;\n\t\t\t\t\t\tif (!_meta.com.Contains(s)) _meta.com.Add(s);\n\t\t\t\t\t\t_ShowInfo_Added(e.Button, _meta.com);\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t});\n\t\t\tm[\"Delete converted...\"] = _ => run.itSafe(dir);\n\t\t}\n\t\tm.Show(owner: this);\n\t}\n\t\n\tvoid _bAddComRegistry_Click(WBButtonClickArgs e) {\n\t\t//HKCU\\TypeLib\\typelibGuid\\version\\\n\t\tvar sFind = findInLists.Text;\n\t\tvar rx = new regexp(@\"(?i) (?:Type |Object )?Library[ \\d\\.]*$\");\n\t\tvar a = new List<EdComUtil.RegTypelib>(1000);\n\t\tusing (var tlKey = Registry.ClassesRoot.OpenSubKey(\"TypeLib\")) { //guids\n\t\t\tforeach (var sGuid in tlKey.GetSubKeyNames()) {\n\t\t\t\tif (sGuid.Length != 38) continue;\n\t\t\t\t//print.it(sGuid);\n\t\t\t\tusing var guidKey = tlKey.OpenSubKey(sGuid);\n\t\t\t\tforeach (var sVer in guidKey.GetSubKeyNames()) {\n\t\t\t\t\tusing var verKey = guidKey.OpenSubKey(sVer);\n\t\t\t\t\tif (verKey.GetValue(\"\") is string description) {\n\t\t\t\t\t\tif (rx.Match(description, 0, out RXGroup g)) description = description.Remove(g.Start);\n\t\t\t\t\t\tif (sFind.Length > 0 && description.Find(sFind, true) < 0) continue;\n\t\t\t\t\t\ta.Add(new(description.Limit(80, middle: true) + \", \" + sVer, sGuid, sVer));\n\t\t\t\t\t} //else print.it(sGuid); //some Microsoft typelibs. VS does not show these too.\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (a.Count == 0) { _ShowInfo_ListEmpty(e.Button, sFind); return; }\n\t\ta.Sort((x, y) => string.Compare(x.text, y.text, true));\n\t\t\n\t\tvar m = new popupMenu();\n\t\tforeach (var v in a) {\n\t\t\tm[v.text] = o => _ConvertTypeLibrary(v, e.Button);\n\t\t}\n\t\tm.Show(owner: this);\n\t}\n\t\n\tasync void _ConvertTypeLibrary(object tlDef, Button button) {\n\t\tif (await EdComUtil.ConvertTypeLibrary(tlDef, this) is { } converted) {\n\t\t\tforeach (var v in converted) if (!_meta.com.Contains(v)) _meta.com.Add(v);\n\t\t\t_ShowInfo_Added(button, _meta.com);\n\t\t}\n\t}\n\t\n\t#endregion\n\t\n\t#region util\n\t\n\tstatic void _ShowHide(FrameworkElement e, bool show) => e.Visibility = show ? Visibility.Visible : Visibility.Hidden;\n\t\n\tstatic void _ShowCollapse(FrameworkElement e, bool show) => e.Visibility = show ? Visibility.Visible : Visibility.Collapsed;\n\t\n\tstatic void _ShowHide(bool show, params FrameworkElement[] a) {\n\t\tforeach (var v in a) _ShowHide(v, show);\n\t}\n\t\n\tstatic void _ShowCollapse(bool show, params FrameworkElement[] a) {\n\t\tforeach (var v in a) _ShowCollapse(v, show);\n\t}\n\t\n\tstatic bool _IsHidden(FrameworkElement t) {\n\t\tif (t.IsVisible) return false;\n\t\tif (t.Visibility != Visibility.Visible) return true;\n\t\treturn true;\n\t}\n\t\n\tstatic string _Get(TextBox t, bool nullIfHidden = true) {\n\t\tif (nullIfHidden && _IsHidden(t)) return null;\n\t\tvar r = t.Text.Trim();\n\t\treturn r == \"\" ? null : r;\n\t}\n\t\n\tstatic string _Get(ComboBox t, bool nullIfHidden = true, int? defaultIndex = null) {\n\t\tif (defaultIndex.HasValue && t.SelectedIndex == defaultIndex.Value) return null;\n\t\tif (nullIfHidden && _IsHidden(t)) return null;\n\t\treturn t.IsEditable ? t.Text : t.SelectedItem as string; //note: t.Text changes after t.SelectionChanged event\n\t}\n\t\n\tstatic string _Get(KCheckBox t, bool nullIfHidden = true) {\n\t\tif (nullIfHidden && _IsHidden(t)) return null;\n\t\treturn t.IsChecked ? \"true\" : null;\n\t}\n\t\n\tstatic bool _IsChecked(KCheckBox t, bool falseIfHidden = true) {\n\t\tif (falseIfHidden && _IsHidden(t)) return false;\n\t\treturn t.IsChecked;\n\t}\n\t\n\tstring _GetOutputPath(bool getDefault, bool expandEnvVar = false) {\n\t\tif (!getDefault && _Get(outputPath) is string r) {\n\t\t\tif (expandEnvVar) r = pathname.expand(r);\n\t\t} else {\n\t\t\tr = MetaComments.GetDefaultOutputPath(_f, _role, withEnvVar: !expandEnvVar);\n\t\t}\n\t\treturn r;\n\t}\n\t\n\tvoid _ShowInfo_ListEmpty(UIElement by, string sFind) {\n\t\t_ShowInfoTooltip(by, sFind.Length > 0 ? \"There are no items containing \" + sFind : \"The list is empty\");\n\t}\n\t\n\tvoid _ShowInfo_Added(UIElement by, List<string> metaList) {\n\t\t_ShowInfoTooltip(by, string.Join(\"\\r\\n\", metaList) + \"\\r\\n\\r\\nFinally click OK to save.\");\n\t}\n\t\n\tvoid _ShowInfoTooltip(UIElement by, string s) {\n\t\tToolLand.TUtil.InfoTooltip(ref _tt, by, s, Dock.Right);\n\t}\n\tKPopup _tt;\n\t\n\t#endregion\n\t\n\t#region info\n\t\n\tvoid _InitInfo() {\n\t\tinfo.AaTags.AddStyleTag(\".c\", new() { backColor = 0xF0F0F0, monospace = true }); //inline code\n\t\tinfo.AaTags.AddLinkTag(\"+changeFileType\", _ => {\n\t\t\tif (!dialog.showOkCancel($\"Change file type to: {(_f.IsScript ? \"class\" : \"script\")}\", \"This also will close the Properties dialog as if clicked Cancel.\", owner: this)) return;\n\t\t\t_f.FileType = _f.IsScript ? FNType.Class : FNType.Script;\n\t\t\tClose();\n\t\t});\n\t\t\n\t\tvar dialogInfo = $\"\"\"\nFile type: <help editor/{(_isClass ? \"Class files, projects>C# class file\" : \"Scripts>C# script\")}<>  (<+changeFileType>change...<>)\nFile path: <explore>{_f.FilePath}<>\n\"\"\";\n\t\tinfo.aaaText = dialogInfo;\n\t\tinfo.AaAddElem(this, dialogInfo);\n\t\t\n\t\tinfo.AaAddElem(role, \"\"\"\n<b>role<> - the purpose of this C# code file. What type of assembly to create and how to execute.\n • <.c>miniProgram<> - execute in a separate host process started from editor.\n • <.c>exeProgram<> - create/execute .exe file, which can run on any computer, without editor installed.\n • <.c>editorExtension<> - execute in the editor's UI thread. Rarely used. Incorrect code can kill editor.\n • <.c>classLibrary<> - create .dll file, which can be used in C# scripts and other .NET-based programs.\n • <.c>classFile<> - don't create/execute. The file can be used by other C# files, either as part of the project or added explicitly.\n\"\"\");\n\t\t\n\t\t#region Run\n\t\tinfo.AaAddElem(testScript, \"\"\"\n<b>testScript<> - a script to run when you click the <b>Run<> button.\n\nPress F1 for more info.\n\"\"\");\n\t\tinfo.AaAddElem(ifRunning, \"\"\"\n<b>ifRunning<> - when trying to start this script, what to do if it is already running.\n • <.c>warn<> - print warning and don't run.\n • <.c>cancel<> - don't run.\n • <.c>wait<> - run later, when it ends.\n • <.c>run<> - run simultaneously.\n • <.c>restart<> - end it and run.\n • <.c>end<> - end it and don't run.\n\nSuffix <.c>_restart<> means restart if starting the script with the <b>Run<> button.\n\"\"\");\n\t\tinfo.AaAddElem(uac, \"\"\"\n<b>uac<> - <help articles/UAC>UAC<> integrity level (IL) of the task process.\n • <.c>inherit<> (default) - the same as of the editor process.\n • <.c>user<> - Medium IL, like most applications. The task cannot automate some windows etc.\n • <.c>admin<> - High IL, aka \"administrator\", \"elevated\".\n\"\"\");\n\t\t#endregion\n\t\t\n\t\t#region Compile\n\t\tinfo.AaAddElem(optimize, \"\"\"\n<b>optimize<> - whether to make the compiled code as fast as possible.\n • <.c>false<> (default) - don't optimize. Define <.c>DEBUG<> and <.c>TRACE<>. Aka \"Debug configuration\".\n • <.c>true<> (checked) - optimize. Aka \"Release configuration\".\n\"\"\");\n\t\tinfo.AaAddElem(define, \"\"\"\n<b>define<> - symbols that can be used with <.c>#if<>.\n\nExample: <.c>ONE,TWO,r:THREE,d:FOUR<>\nPrefix <.c>r:<> - if <.c>optimize true<>. Prefix <.c>d:<> - if no <.c>optimize true<>.\n\nSee also <google C# #define>#define<>.\n\"\"\");\n\t\tinfo.AaAddElem(warningLevel, $\"\"\"\n<b>warningLevel<> - <google C# Compiler Options, WarningLevel>warning level<>.\n0 - no warnings.\n1 - only severe warnings.\n2 - level 1 plus some less-severe warnings.\n3 - most warnings.\n4 - all warnings of C# 1-8.\n5-9999 - level 4 plus warnings added in C# 9+.\n\"\"\");\n\t\tinfo.AaAddElem(noWarnings, $\"\"\"\n<b>noWarnings<> - don't show these warnings.\n\nExample: <.c>151,3001,CS1234<>\n\nSee also <google C# #pragma warning>#pragma warning<>.\n\"\"\");\n\t\tinfo.AaAddElem(nullable, \"\"\"\n<b>nullable<> - <google C# Nullable reference types>nullable context<>.\ndisable - no warnings; code does not use nullable syntax (<.c>Type? variable<>).\nenable - print warnings; code uses nullable syntax.\nwarnings - print warnings; code does not use nullable syntax.\nannotations - no warnings; code uses nullable syntax.\n\"\"\");\n\t\tinfo.AaAddElem(testInternal, \"\"\"\n<b>testInternal<> - can use internal members of these assemblies, like with <.c>InternalsVisibleToAttribute<>.\n\nExample: <.c>Assembly1,Assembly2<>\n\"\"\");\n\t\tinfo.AaAddElem(preBuild, \"\"\"\n<b>preBuild<> - a script to run before compiling.\n\nTo create new preBuild script: menu <b>File > New > More<>.\nPress F1 for more info.\n\"\"\");\n\t\tinfo.AaAddElem(postBuild, \"\"\"\n<b>postBuild<> - a script to run after compiling successfully.\n\nTo create new postBuild script: menu <b>File > New > More<>.\nPress F1 for more info.\n\"\"\");\n\t\t#endregion\n\t\t\n\t\t#region Assembly\n\t\tinfo.AaAddElem(outputPath, \"\"\"\n<b>outputPath<> - directory for the output files (exe, dll etc).\n\nPress F1 for more info.\n\"\"\");\n\t\tinfo.AaAddElem(icon, \"\"\"\n<b>icon<> - icon(s) of the output exe file.\n\nPress F1 for more info.\n\"\"\");\n\t\tinfo.AaAddElem(manifest, \"\"\"\n<b>manifest<> - <google manifest file site:microsoft.com>manifest<> of the output exe file.\n\nPress F1 for more info.\n\"\"\");\n\t\tinfo.AaAddElem(sign, \"\"\"\n<b>sign<> - strong-name signing key file, to sign the output assembly.\n\nPress F1 for more info.\n\"\"\");\n\t\tinfo.AaAddElem(console, \"\"\"\n<b>console<> - let the program run with console.\n\"\"\");\n\t\tinfo.AaAddElem(platform, $\"\"\"\n<b>platform<> - CPU instruction set.\n\nDefault on this computer: {(osVersion.isArm64Process ? \"arm64\" : \"x64\")}.\nPress F1 for more info.\n\"\"\");\n\t\t\n\t\tinfo.AaAddElem(xmlDoc, \"\"\"\n<b>xmlDoc<> - create XML documentation file from <.c>/// comments<>. And print errors in <.c>/// comments<>.\n\"\"\");\n\t\t#endregion\n\t\t\n\t\t#region Add reference\n\t\tinfo.AaAddElem(addLibrary, \"\"\"\n<b>Library<> - add a .NET assembly reference.\nAdds meta comment <c green>r DllFile<>.\n\nPress F1 for more info.\n\"\"\");\n\t\tinfo.AaAddElem(addNuget, \"\"\"\n<b>NuGet<> - use a NuGet package reference (see menu <b>Tools > NuGet</b>).\nAdds meta comment <c green>nuget Folder\\Package<>.\n\nPress F1 for more info.\n\"\"\");\n\t\t\n\t\tconst string c_com = \"\"\"\n<b>COM<>, <b>...<> - convert (now) a COM component's type library to an <i>interop assembly<>, and use it.\nAdds meta comment <c green>com FileName.dll<>.\nSaves the assembly file in <link>%folders.Workspace%\\.interop<>.\n\nPress F1 for more info.\n\"\"\";\n\t\tinfo.AaAddElem(addComRegistry, c_com);\n\t\tinfo.AaAddElem(addComBrowse, c_com);\n\t\tinfo.AaAddElem(addProject, \"\"\"\n<b>Project<> - add a reference to a class library created in this workspace.\nAdds meta comment <c green>pr File.cs<>.\n\nPress F1 for more info.\n\"\"\");\n\t\t#endregion\n\t\t\n\t\t#region Add file\n\t\tinfo.AaAddElem(addClassFile, \"\"\"\n<b>Class file<> - add a C# code file that contains some classes/functions used by this file.\nAdds meta comment <c green>c File.cs<>.\n\nPress F1 for more info.\n\"\"\");\n\t\t//FUTURE: add UI to append resource suffix\n\t\tinfo.AaAddElem(addResource, \"\"\"\n<b>Resource<> - add image etc file(s) as managed resources.\nAdds meta comment <c green>resource File<>.\n\nPress F1 for more info.\n\"\"\");\n\t\tinfo.AaAddElem(addFile, \"\"\"\n<b>Other file<> - make a file available at run time. Eg an unmanaged dll.\nAdds meta comment <c green>file File<>.\n\nPress F1 for more info.\n\"\"\");\n\t\t#endregion\n\t}\n\t\n\t#endregion\n}\n"
  },
  {
    "path": "Au.Editor/Files/FileNode.cs",
    "content": "using Au.Controls;\nusing System.Xml;\nusing System.Xml.Linq;\n\nnamespace LA;\n\npartial class FileNode : TreeBase<FileNode>, ITreeViewItem {\n\t#region types\n\t\n\t//Not saved in file.\n\t[Flags]\n\tenum _State : byte {\n\t\tDeleted = 1,\n\t}\n\t\n\t//Saved in file.\n\t[Flags]\n\tenum _Flags : byte {\n\t\t_obsolete_Symlink = 1,\n\t}\n\t\n\t#endregion\n\t\n\t#region fields, ctors, load/save\n\t\n\treadonly FilesModel _model;\n\tstring _name;\n\tstring _displayName;\n\tuint _id;\n\tFNType _type;\n\t_State _state;\n\t_Flags _flags;\n\tstring _linkTarget;\n\tstring _icon;\n\tuint _testScriptId;\n\t\n\t/// <summary>\n\t/// This ctor is used when creating new item of known type in current workspace. Not when exporting.\n\t/// </summary>\n\tpublic FileNode(FilesModel model, string name, FNType type) {\n\t\t_model = model;\n\t\t_type = type;\n\t\t_SetName(name);\n\t\t_id = _model.AddGetId(this);\n\t}\n\t\n\t/// <summary>\n\t/// This ctor is used when importing items from files etc.\n\t/// </summary>\n\t/// <param name=\"model\"></param>\n\t/// <param name=\"name\"></param>\n\t/// <param name=\"filePath\">Used to detect type (when !isDir). If isLink, sets link target.</param>\n\t/// <param name=\"isDir\"></param>\n\t/// <param name=\"isLink\"></param>\n\tpublic FileNode(FilesModel model, string name, string filePath, bool isDir, bool isLink = false) {\n\t\t_model = model;\n\t\t_type = isDir ? FNType.Folder : _DetectFileType(filePath);\n\t\t_SetName(name);\n\t\t_id = _model.AddGetId(this);\n\t\tif (isLink) _linkTarget = filePath;\n\t}\n\t\n\t/// <summary>\n\t/// This ctor is used when copying an item or importing a workspace.\n\t/// Deep-copies fields from f, except _model, _name, _id (generates new) and _testScriptId.\n\t/// </summary>\n\tFileNode(FilesModel model, FileNode f, string name) {\n\t\t_model = model;\n\t\t_SetName(name);\n\t\t_type = f._type;\n\t\t_state = f._state;\n\t\t_flags = f._flags;\n\t\t_linkTarget = f._linkTarget;\n\t\t_icon = f.CustomIconName;\n\t\t_id = _model.AddGetId(this);\n\t}\n\t\n\t/// <summary>\n\t/// This ctor is used when loading workspace (reading files.xml).\n\t/// </summary>\n\tFileNode(XmlReader x, FileNode parent, FilesModel model) {\n\t\t_model = model;\n\t\tif (parent == null) { //the root node\n\t\t\tif (x.Name != \"files\") throw new ArgumentException(\"XML root element name must be 'files'\");\n\t\t} else {\n\t\t\t_type = XmlTagToFileType(x.Name, canThrow: true);\n\t\t\twhile (x.MoveToNextAttribute()) _ReadXmlAttribute(x.Name, x.Value);\n\t\t\tif (_name == null) throw new ArgumentException(\"no 'n' attribute in XML\");\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// This ctor is used when syncing workspace (reading files.xml modified by another LA process).\n\t/// </summary>\n\tinternal FileNode(XElement x, FilesModel model) {\n\t\t_model = model;\n\t\t_type = XmlTagToFileType(x.Name.LocalName, canThrow: true);\n\t\tforeach (var attr in x.Attributes()) _ReadXmlAttribute(attr.Name.LocalName, attr.Value);\n\t\tif (_name == null) throw new ArgumentException(\"no 'n' attribute in XML\");\n\t}\n\t\n\tvoid _ReadXmlAttribute(string name, string value) {\n\t\tif (value.NE()) return;\n\t\tswitch (name) {\n\t\tcase \"n\": _SetName(value); break;\n\t\tcase \"i\": value.ToInt(out _id); break;\n\t\tcase \"f\": _flags = (_Flags)value.ToInt(); break;\n\t\tcase \"path\": _linkTarget = value.Starts(@\".\\\") ? _model.WorkspaceDirectory + value[1..] : value; break;\n\t\tcase \"icon\": _icon = value; break;\n\t\tcase \"run\": value.ToInt(out _testScriptId); break;\n\t\t}\n\t}\n\t\n\tinternal void _WorkspaceLoaded(uint id, bool importing) {\n\t\t_id = id; //if id in XML was invalid, the caller generated new id\n\t\t\n\t\tif (_flags.Has(_Flags._obsolete_Symlink)) { //before v0.17 for folder links used symlinks. Bad idea.\n\t\t\t_flags &= ~_Flags._obsolete_Symlink;\n\t\t\tvar linkPath = FilePath;\n\t\t\tfilesystem.more.getFinalPath(linkPath, out _linkTarget);\n\t\t\tif (!importing) {\n\t\t\t\tApi.RemoveDirectory(linkPath);\n\t\t\t\t_model.Save.WorkspaceAsync();\n\t\t\t}\n\t\t}\n\t}\n\t\n\tpublic static FileNode LoadWorkspace(string file, FilesModel model) {\n\t\tvar root = XmlLoad(file, (x, p) => new FileNode(x, p, model));\n\t\troot._isExpanded = true;\n\t\treturn root;\n\t}\n\t\n\tpublic void SaveWorkspace(string file) => XmlSave(file, (x, n) => n._XmlWrite(x));\n\t\n\tvoid _XmlWrite(XmlWriter x) {\n\t\tif (x.WriteState is WriteState.Prolog or WriteState.Start) {\n\t\t\tx.WriteStartElement(\"files\");\n\t\t} else {\n\t\t\tx.WriteStartElement(_type switch { FNType.Folder => \"d\", FNType.Script => \"s\", FNType.Class => \"c\", _ => \"n\" });\n\t\t\tx.WriteAttributeString(\"n\", _name);\n\t\t\tif (_id != 0) x.WriteAttributeString(\"i\", _id.ToString());\n\t\t\tif (_flags != 0) x.WriteAttributeString(\"f\", ((int)_flags).ToString());\n\t\t\tif (IsLink) x.WriteAttributeString(\"path\", _model != null && _linkTarget.PathStarts(_model.WorkspaceDirectory) ? @\".\" + _linkTarget[_model.WorkspaceDirectory.Length..] : _linkTarget);\n\t\t\tif (CustomIconName is { } ico) x.WriteAttributeString(\"icon\", ico);\n\t\t\tif (_testScriptId != 0) x.WriteAttributeString(\"run\", _testScriptId.ToString());\n\t\t\t\n\t\t\t//These are not set when exporting: _id, _model, _testScriptId.\n\t\t}\n\t}\n\t\n\tpublic static void Export(FileNode fRoot, string file) {\n\t\tfRoot.XmlSave(file, (x, n) => n._XmlWrite(x));\n\t}\n\t\n\tFileNode() { }\n\t\n\t/// <summary>\n\t/// Creates the root folder for exporting.\n\t/// </summary>\n\tpublic static FileNode CreateForExport() {\n\t\treturn new() { _type = FNType.Folder };\n\t}\n\t\n\t/// <summary>\n\t/// Creates a folder for exporting, eg folder \"Classes\" for meta c files.\n\t/// </summary>\n\t/// <param name=\"folderName\"></param>\n\tpublic static FileNode CreateForExport(string folderName) {\n\t\treturn new() { _type = FNType.Folder, _name = folderName };\n\t}\n\t\n\t/// <summary>\n\t/// Creates a clone of an item for exporting.\n\t/// </summary>\n\t/// <param name=\"f\">The original item to get properties from.</param>\n\t/// <param name=\"exportLinkTarget\">Don't set <b>_linkTarget</b>.</param>\n\tpublic static FileNode CreateForExport(FileNode f, bool exportLinkTarget) {\n\t\treturn new() {\n\t\t\t_type = f._type,\n\t\t\t_name = f._name,\n\t\t\t_flags = f._flags,\n\t\t\t_linkTarget = exportLinkTarget ? null : f._linkTarget,\n\t\t\t_icon = f._icon,\n\t\t};\n\t}\n\t\n\t#endregion\n\t\n\t#region properties\n\t\n\t/// <summary>\n\t/// Panels.Files.TreeControl.\n\t/// </summary>\n\tpublic static KTreeView TreeControl => Panels.Files.TreeControl;\n\t\n\t/// <summary>\n\t/// Gets workspace that contains this file.\n\t/// </summary>\n\tpublic FilesModel Model => _model;\n\t\n\t/// <summary>\n\t/// Gets the root node (Model.Root).\n\t/// </summary>\n\tpublic FileNode Root => _model.Root;\n\t\n\t/// <summary>\n\t/// File type.\n\t/// Setter can change Script to Class or vice versa; call when closing DProperties.\n\t/// </summary>\n\tpublic FNType FileType {\n\t\tget => _type;\n\t\tset {\n\t\t\tDebug.Assert(IsCodeFile);\n\t\t\tif (value == _type) return;\n\t\t\t_type = value;\n\t\t\t_testScriptId = 0;\n\t\t\t\n\t\t\t_model.Save.WorkspaceAsync();\n\t\t\tCompiler.Uncache(this);\n\t\t\tCodeInfo.FilesChanged();\n\t\t\tFilesModel.Redraw(this);\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// true if folder or root.\n\t/// </summary>\n\tpublic bool IsFolder => _type == FNType.Folder;\n\t\n\t/// <summary>\n\t/// true if script file.\n\t/// </summary>\n\tpublic bool IsScript => _type == FNType.Script;\n\t\n\t/// <summary>\n\t/// true if class file.\n\t/// </summary>\n\tpublic bool IsClass => _type == FNType.Class;\n\t\n\t/// <summary>\n\t/// true if script or class file.\n\t/// false if folder or not a code file.\n\t/// </summary>\n\tpublic bool IsCodeFile => _type is FNType.Script or FNType.Class;\n\t\n\t/// <summary>\n\t/// true if not script/class/folder.\n\t/// </summary>\n\tpublic bool IsOtherFileType => _type == FNType.Other;\n\t\n\t/// <summary>\n\t/// File name with extension.\n\t/// </summary>\n\tpublic string Name => _name;\n\t\n\t/// <summary>\n\t/// File name. Without extension if ends with \".cs\".\n\t/// </summary>\n\tpublic string DisplayName => _displayName ??= IsCodeFile ? _name.RemoveSuffix(\".cs\", true) : _name;\n\t\n\tvoid _SetName(string name) {\n\t\tif (_name != null) _model._nameMap.MultiRemove_(_name, this); //renaming\n\t\t_model._nameMap.MultiAdd_(_name = name, this);\n\t\t_displayName = null;\n\t}\n\t\n\t/// <summary>\n\t/// Unique id in this workspace. To find faster, with database, etc.\n\t/// Root id is 0.\n\t/// Ids of deleted items are not reused.\n\t/// </summary>\n\tpublic uint Id => _id;\n\t\n\t/// <summary>\n\t/// <see cref=\"Id\"/> as string.\n\t/// </summary>\n\tpublic string IdString => _id.ToString();\n\t\n\t/// <summary>\n\t/// Formats string like \":0x10000000A\", with <see cref=\"Id\"/> in low-order uint and <see cref=\"FilesModel.WorkspaceSN\"/> in high-order int.\n\t/// Such string can be passed to <see cref=\"FilesModel.Find\"/>.\n\t/// </summary>\n\tpublic string IdStringWithWorkspace => \":0x\" + (_id | ((long)_model.WorkspaceSN << 32)).ToString(\"X\");\n\t\n\t/// <summary>\n\t/// Formats SciTags &lt;open&gt; link tag to open this file.\n\t/// </summary>\n\t/// <param name=\"path\">In link name use <see cref=\"ItemPath\"/> instead of name.</param>\n\t/// <param name=\"lineOrPos\">If not null: if >0, appends |lineOrPos (1-based line index); else appends ||-lineOrPos (0-based position).</param>\n\tpublic string SciLink(bool path = false, int? lineOrPos = null) {\n\t\tstring text = path ? ItemPath : _name;\n\t\tif (lineOrPos == null) return $\"<open {IdStringWithWorkspace}>{text}<>\";\n\t\tint i = lineOrPos.Value;\n\t\tif (i > 0) return $\"<open {IdStringWithWorkspace}|{i}>{text}<>\";\n\t\treturn $\"<open {IdStringWithWorkspace}||{-i}>{text}<>\";\n\t}\n\t\n\t/// <summary>\n\t/// true if is a link to an external file or folder.\n\t/// See also IsExternal.\n\t/// </summary>\n\tpublic bool IsLink => _linkTarget != null;\n\t\n\t/// <summary>\n\t/// If <see cref=\"IsLink\"/>, returns target path, else null.\n\t/// </summary>\n\tpublic string LinkTarget => _linkTarget;\n\t\n\t/// <summary>\n\t/// true if this or an ancestor is <see cref=\"IsLink\"/>.\n\t/// </summary>\n\tpublic bool IsExternal {\n\t\tget {\n\t\t\tfor (var v = this; v != null; v = v.Parent) if (v.IsLink) return true;\n\t\t\treturn false;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Gets the filename extension of the target file, like \".cs\". Returns \"\" if folder.\n\t/// </summary>\n\tpublic string FileExt => IsFolder ? \"\" : pathname.getExtension(IsLink ? LinkTarget : Name);\n\t\n\t/// <summary>\n\t/// Gets or sets custom icon name (like \"*Pack.Icon color\") or null.\n\t/// </summary>\n\t/// <remarks>\n\t/// The setter will save workspace.\n\t/// User can set custom icon: menu Tools > Icons.\n\t/// Currently editor does not support item icons as .ico files etc. Not difficult to add, but probably don't need when we have 25000 XAML icons. For .exe files can use any icons.\n\t/// </remarks>\n\tpublic string CustomIconName {\n\t\tget => _icon;\n\t\tset {\n\t\t\t_icon = value;\n\t\t\t_model.Save.WorkspaceAsync();\n\t\t\tCompiler.Uncache(this);\n\t\t\tFilesModel.Redraw(this);\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Gets custom or default icon name or file path.\n\t/// </summary>\n\tpublic string IconString => CustomIconName ?? (IsOtherFileType ? FilePath : GetFileTypeImageSource(FileType));\n\t\n\t/// <summary>\n\t/// Gets or sets other item to run instead of this. None if null.\n\t/// The setter will save workspace.\n\t/// </summary>\n\tpublic FileNode TestScript {\n\t\tget {\n\t\t\tif (_testScriptId != 0) {\n\t\t\t\tvar f = _model.FindById(_testScriptId); if (f != null) return f;\n\t\t\t\tTestScript = null;\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\tset {\n\t\t\tuint id = value?._id ?? 0;\n\t\t\tif (_testScriptId == id) return;\n\t\t\t_testScriptId = id;\n\t\t\t_model.Save.WorkspaceAsync();\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Gets or sets 'Delete' flag. Does nothing more.\n\t/// </summary>\n\tpublic bool IsDeleted {\n\t\tget => 0 != (_state & _State.Deleted);\n\t\tset { Debug.Assert(value); _state |= _State.Deleted; }\n\t}\n\t\n\t/// <summary>\n\t/// true if is deleted or is not in current workspace.\n\t/// </summary>\n\tpublic bool IsAlien => IsDeleted || _model != App.Model;\n\t\n\t/// <summary>\n\t/// Returns item path in workspace, like @\"\\Folder\\Name.cs\" or @\"\\Name.cs\".\n\t/// Returns null if this item is deleted.\n\t/// </summary>\n\tpublic string ItemPath => _ItemPath();\n\t\n\t/// <summary>\n\t/// Returns item path relative to <i>ancestor</i>, like @\"\\Folder\\Name.cs\" or @\"\\Name.cs\".\n\t/// Returns null if this item is deleted or not in <i>ancestor</i>.\n\t/// If <i>ancestor</i> is null or <b>Root</b>, the result is the same as <b>ItemPath</b>.\n\t/// </summary>\n\tpublic string ItemPathIn(FileNode ancestor) => _ItemPath(null, ancestor);\n\t\n\t[SkipLocalsInit]\n\tunsafe string _ItemPath(string prefix = null, FileNode ancestor = null) {\n\t\tif (!App.IsMainThread) {\n\t\t\tDebug_.Print(\"_ItemPath called in wrong thread\");\n\t\t\treturn App.Dispatcher.Invoke(() => _ItemPath(prefix, ancestor));\n\t\t}\n\t\tint len = prefix.Lenn();\n\t\tvar root = ancestor ?? Root;\n\t\tfor (var f = this; f != root; f = f.Parent) {\n\t\t\tif (f == null) { Debug.Assert(IsDeleted); throw new InvalidOperationException(); }\n\t\t\tlen += f._name.Length + 1;\n\t\t}\n\t\tvar p = stackalloc char[len];\n\t\tchar* e = p + len;\n\t\tfor (var f = this; f != root; f = f.Parent) {\n\t\t\tf._name.CopyTo_(e -= f._name.Length);\n\t\t\t*(--e) = '\\\\';\n\t\t}\n\t\tif (e > p) prefix.CopyTo_(p);\n\t\treturn new string(p, 0, len);\n\t}\n\t\n\t/// <summary>\n\t/// Gets full path of the file.\n\t/// If this is a link, it is the link target.\n\t/// </summary>\n\tpublic string FilePath {\n\t\tget {\n\t\t\tif (this == Root) return _model.FilesDirectory;\n\t\t\tif (IsDeleted) return null;\n\t\t\t\n\t\t\tif (IsLink) return LinkTarget;\n\t\t\tFileNode link = Parent; while (link != null && !link.IsLink) link = link.Parent;\n\t\t\tif (link != null) return _ItemPath(link.LinkTarget, link);\n\t\t\t\n\t\t\treturn _ItemPath(_model.FilesDirectory);\n\t\t}\n\t}\n\t//rejected: cache item path and file path.\n\t//\tWhen an item renamed or moved, reset cached paths of that item and of its descendants.\n\t//\tBut creating paths is fast and not so much garbage. And not so frequently used. Keeping paths in memory isn't good too.\n\t\n\t/// <summary>\n\t/// Returns <b>ItemPath</b> if exist multiple items with <b>Name</b>. Else returns <b>Name</b>.\n\t/// Can be used for inserting code, like <c>script.run</c>.\n\t/// </summary>\n\tpublic string ItemPathOrName() {\n\t\t//if (!_name.RxIsMatch(@\"(?i)^(?:Script\\d*\\.cs|Class\\d*\\.cs|File\\d*\\..+|Folder\\d*)$\")) { //rejected\n\t\tif (Model.Find(_name, silent: true) == this) return _name; //else found multiple\n\t\t//}\n\t\treturn ItemPath;\n\t}\n\t\n\t/// <summary>\n\t/// Returns <b>ItemPath</b> if exist multiple items with <b>Name</b>, unless single exists in <i>relativeTo</i>'s subfolders or in an ancestor folder. Else returns <b>Name</b>.\n\t/// Uses <see cref=\"FindRelative\"/>.\n\t/// </summary>\n\t/// <param name=\"relativeTo\">Search relative to this folder or to the parent folder of this file.</param>\n\tpublic string ItemPathOrName(FileNode relativeTo, FNFind kind = FNFind.Any) {\n\t\tif (relativeTo.FindRelative(true, _name, kind) == this) return _name;\n\t\treturn ItemPath;\n\t}\n\t\n\t/// <summary>\n\t/// If is open (active or not), returns the <b>SciCode</b>, else null.\n\t/// </summary>\n\tpublic SciCode OpenDoc { get; internal set; }\n\t\n\t/// <summary>\n\t/// Returns Name.\n\t/// </summary>\n\tpublic override string ToString() => _name;\n\t\n\t///// <summary>\n\t///// Data used by code info classes.\n\t///// </summary>\n\t//public CiFileData ci;\n\t\n\t#endregion\n\t\n\t#region text\n\t\n\t/// <summary>\n\t/// Gets text from editor (if this is the active open document) or file (<see cref=\"GetFileText\"/>).\n\t/// </summary>\n\t/// <param name=\"text\">\"\" if failed (eg file not found) or is folder.</param>\n\t/// <param name=\"silent\">Don't print warning when failed. If null, silent only if file not found.</param>\n\t/// <returns>(true, null) if got text or is folder. (false, error) if failed.</returns>\n\tpublic BoolError GetCurrentText(out string text, bool? silent = false) {\n\t\tif (this == _model.CurrentFile) { text = Panels.Editor.ActiveDoc.aaaText; return true; }\n\t\treturn GetFileText(out text, silent);\n\t}\n\t\n\t/// <summary>\n\t/// Gets text from file.\n\t/// Does not get editor text like <see cref=\"GetCurrentText\"/>.\n\t/// </summary>\n\t/// <param name=\"text\">\"\" if failed (eg file not found) or is folder.</param>\n\t/// <param name=\"silent\">Don't print warning when failed. If null, silent only if file not found.</param>\n\t/// <returns>(true, null) if got text or is folder. (false, error) if failed.</returns>\n\tpublic BoolError GetFileText(out string text, bool? silent = false) {\n\t\ttext = \"\";\n\t\tif (IsFolder) return true;\n\t\t\n\t\t//rejected: cache text. OS caching isn't too slow.\n\t\t\n\t\tstring path = FilePath, es = null;\n\t\tif (path == null) { //IsDeleted\n\t\t\tes = \"Deleted.\";\n\t\t\tpath = \"unknown\";\n\t\t} else {\n\t\t\ttry {\n\t\t\t\tusing var sr = filesystem.waitIfLocked(() => new StreamReader(path));\n\t\t\t\t\n\t\t\t\tif (sr.BaseStream.Length > 100_000_000) es = \"File too big, > 100_000_000.\";\n\t\t\t\telse text = sr.ReadToEnd();\n\t\t\t\t\n\t\t\t\t//rejected: update _fileModTime. Would need to call OnAppActivatedAndThisIsOpen if changed.\n\t\t\t\t//var fs = (FileStream)sr.BaseStream;\n\t\t\t\t//if (fs.Length > 100_000_000) es = \"File too big, > 100_000_000.\";\n\t\t\t\t//else {\n\t\t\t\t//\ttext = sr.ReadToEnd();\n\t\t\t\t//\tif (!silent) _fileModTime = Api.GetFileInformationByHandle(fs.SafeFileHandle.DangerousGetHandle(), out var fi) ? fi.ftLastWriteTime : 0;\n\t\t\t\t//}\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tbool notFound = ex is FileNotFoundException or DirectoryNotFoundException;\n\t\t\t\tDebug_.PrintIf(!notFound && silent == true, ex);\n\t\t\t\tif (notFound) silent ??= true;\n\t\t\t\tes = ex.ToStringWithoutStack();\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (es == null) return true;\n\t\tes = $\"Failed to get text of <open>{ItemPath}<>, file <explore>{path}<>\\r\\n\\t{es}\";\n\t\tif (silent != true) print.warning(es, -1);\n\t\treturn new(false, es);\n\t}\n\t\n\t/// <summary>\n\t/// Loads text of any file like <see cref=\"GetFileText\"/>.\n\t/// Thread-safe. Silent when file not found or too big.\n\t/// </summary>\n\t/// <param name=\"filePath\">Full path, to pass directly to .NET <b>File</b> function.</param>\n\t/// <returns>null if failed or file size more than 100_000_000.</returns>\n\tpublic static string GetFileTextLL(string filePath) {\n\t\t//TODO3: option to load only text files. If unknown extension, try to load part and find \\0.\n\t\ttry {\n\t\t\tusing var sr = filesystem.waitIfLocked(() => new StreamReader(filePath));\n\t\t\tif (sr.BaseStream.Length > 100_000_000) return null;\n\t\t\treturn sr.ReadToEnd();\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tif (ex is not (FileNotFoundException or DirectoryNotFoundException)) print.warning(ex.ToStringWithoutStack(), -1);\n\t\t}\n\t\treturn null;\n\t}\n\t\n\tlong _fileModTime;\n\t\n\t//called when SciDoc loaded or saved the file\n\tinternal void _UpdateFileModTime() {\n\t\tfilesystem.GetTime_(FilePath, out _fileModTime);\n\t}\n\t\n\tinternal void _CheckModifiedExternally(SciCode doc) {\n\t\tif (doc.EIsBinary) return;\n\t\tDebug_.PrintIf(_fileModTime == 0);\n\t\tif (!filesystem.GetTime_(FilePath, out var t) || t == _fileModTime) return;\n\t\t_fileModTime = t;\n\t\tdoc.EFileModifiedExternally_(); //calls GetFileText\n\t}\n\t\n\t/// <summary>\n\t/// Replaces all specified text ranges with specified texts.\n\t/// If the file is open in editor, replaces it there; saves if it isn't the active document. Else replaces file text.\n\t/// </summary>\n\t/// <param name=\"text\">Current text.</param>\n\t/// <param name=\"a\">Text ranges and replacement texts. Must be sorted by range. Ranges must not overlap.</param>\n\t/// <returns>false if a is empty or failed to save.</returns>\n\t/// <param name=\"newText\"></param>\n\t/// <exception cref=\"ArgumentException\">Ranges are overlapped or not sorted. Only #if DEBUG.</exception>\n\tpublic bool ReplaceAllInText(string text, List<StartEndText> a, out string newText) {\n\t\tnewText = null;\n\t\tif (a.Count > 0) {\n\t\t\tvar doc = OpenDoc;\n\t\t\tif (doc != null) {\n\t\t\t\tDebug.Assert(!doc.aaaIsReadonly);\n\t\t\t\tStartEndText.ThrowIfNotSorted(a);\n\t\t\t\t\n\t\t\t\tusing (doc.ENewUndoAction()) {\n\t\t\t\t\tfor (int i = a.Count; --i >= 0;) {\n\t\t\t\t\t\tvar (from, to, s) = a[i];\n\t\t\t\t\t\tdoc.aaaNormalizeRange(true, ref from, ref to);\n\t\t\t\t\t\tif (CiStyling.IsProtected(doc, from, to)) continue; //hidden text (embedded image)\n\t\t\t\t\t\tdoc.aaaReplaceRange(false, from, to, s);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tnewText = doc.aaaText;\n\t\t\t\tif (doc != Panels.Editor.ActiveDoc) doc.ESaveText_(true);\n\t\t\t\treturn true;\n\t\t\t} else {\n\t\t\t\tnewText = StartEndText.ReplaceAll(text, a);\n\t\t\t\ttry {\n\t\t\t\t\tSaveNewTextOfClosedFile(newText);\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tcatch (Exception e1) { print.warning($\"Failed to save {SciLink()}. {e1.ToStringWithoutStack()}\"); }\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\t\n\t/// <exception cref=\"Exception\"></exception>\n\tpublic void SaveNewTextOfClosedFile(string text) {\n\t\tDebug.Assert(OpenDoc == null);\n\t\tif (DontSave) throw new AuException(\"This file should not be modified.\");\n\t\tstring path = FilePath;\n\t\t_model.FileOperation(FOSync.UserFileWrite, () => { filesystem.saveText(path, text); });\n\t\t_UpdateFileModTime();\n\t\tCodeInfo.FilesChanged();\n\t}\n\t\n\t/// <summary>\n\t/// true if is link to a file in folders.ThisAppBS + \"Default\" or \"Templates\".\n\t/// #if DEBUG, always returns false.\n\t/// </summary>\n#if DEBUG\n\tpublic bool DontSave => false;\n#else\n\tpublic bool DontSave => IsLink && FilePath is var s && s.Starts(folders.ThisAppBS) && 0 != s.Eq(folders.ThisAppBS.Length, true, @\"Default\\\", @\"Templates\\\");\n#endif\n\t\n\t#endregion\n\t\n\t#region ITreeViewItem\n\t\n\tIEnumerable<ITreeViewItem> ITreeViewItem.Items => Children();\n\t\n\t/// <summary>\n\t/// Gets or sets expanded state.\n\t/// The setter sets to save later but does not update the control (for it use <see cref=\"KTreeView.Expand\"/> instead, it calls the setter).\n\t/// </summary>\n\tpublic bool IsExpanded => _isExpanded;\n\tbool _isExpanded;\n\t\n\tpublic void SetIsExpanded(bool yes) { if (yes != _isExpanded) { _isExpanded = yes; _model.Save.StateLater(); } }\n\t\n\tstring ITreeViewItem.DisplayText => DisplayName;\n\t\n\tvoid ITreeViewItem.SetNewText(string text) { FileRename(text); }\n\t\n\tpublic static string GetFileTypeImageSource(FNType ft, bool openFolder = false)\n\t\t=> ft switch {\n\t\t\tFNType.Script => App.Settings.icons.ft_script ?? EdIcons.Script,\n\t\t\tFNType.Class => App.Settings.icons.ft_class ?? EdIcons.Class,\n\t\t\tFNType.Folder => openFolder\n\t\t\t\t? App.Settings.icons.ft_folderOpen ?? EdIcons.FolderOpen\n\t\t\t\t: App.Settings.icons.ft_folder ?? EdIcons.Folder,\n\t\t\t_ => null\n\t\t};\n\t\n\tpublic object Image {\n\t\tget {\n\t\t\tvar s = CustomIconName ?? (IsOtherFileType ? FilePath : GetFileTypeImageSource(FileType, _isExpanded));\n\t\t\tif (IsLink) return new object[] { s, \"link:\" };\n\t\t\treturn s;\n\t\t}\n\t}\n\t\n\tint ITreeViewItem.Color(TVColorInfo ci) => ci.isTextBlack && !IsFolder && _model.OpenFiles.Contains(this) ? 0x1fafad2 : -1;\n\t\n\tint ITreeViewItem.TextColor(TVColorInfo ci) => _model.IsCut(this) ? 0xff0000 : -1;\n\t\n\tint ITreeViewItem.BorderColor(TVColorInfo ci) => this == _model.CurrentFile ? (ci.isTextBlack ? 0x1A0A0FF : 0xFF8000) : -1;\n\t\n\t#endregion\n\t\n\t#region tree view\n\t\n\t/// <summary>\n\t/// Unselects all and selects this. Ensures visible. Does not open document.\n\t/// If this is root, just unselects all.\n\t/// </summary>\n\tpublic void SelectSingle() {\n\t\tif (this == Root) TreeControl.UnselectAll();\n\t\telse if (!IsAlien) TreeControl.SelectSingle(this);\n\t}\n\t\n\tpublic bool IsSelected {\n\t\tget => TreeControl.IsSelected(this);\n\t\tset => TreeControl.Select(this, value);\n\t}\n\t\n\t/// <summary>\n\t/// Call this to update/redraw control row view when changed its data (text, image, checked, color, etc).\n\t/// Redraws only this control; to update all, call <see cref=\"FilesModel.Redraw\"/> instead.\n\t/// </summary>\n\tpublic void UpdateControlRow() => TreeControl.Redraw(this);\n\t\n\t#endregion\n\t\n\t#region find, enum\n\t\n\t/// <summary>\n\t/// Finds descendant file or folder by name or @\"\\relative path\".\n\t/// Returns null if not found; also if name is null/\"\".\n\t/// </summary>\n\t/// <param name=\"name\">Name like \"name.cs\" or relative path like @\"\\name.cs\" or @\"\\subfolder\\name.cs\".</param>\n\t/// <param name=\"kind\"></param>\n\tpublic FileNode FindDescendant(string name, FNFind kind = FNFind.Any) {\n\t\tif (name.NE()) return null;\n\t\tif (name[0] == '\\\\') return _FindRelative(name, kind);\n\t\treturn _FindIn(Descendants(), name, kind, true);\n\t}\n\t\n\tstatic FileNode _FindIn(IEnumerable<FileNode> e, RStr name, FNFind kind, bool preferFile) {\n\t\tFileNode folder = null;\n\t\tforeach (var f in e) {\n\t\t\tif (!name.Eqi(f._name)) continue;\n\t\t\tif (!FilesModel.KindFilter_(f, kind)) continue;\n\t\t\tif (preferFile && f.IsFolder) { folder ??= f; continue; }\n\t\t\treturn f;\n\t\t}\n\t\treturn folder;\n\t}\n\t\n\tFileNode _FindRelative(string name, FNFind kind, bool orAnywhere = false) {\n\t\tModel.FoundMultiple = null;\n\t\tbool retry = false; gRetry:\n\t\tint i = name.LastIndexOf('\\\\');\n\t\tvar lastName = i < 0 ? name : name[(i + 1)..]; //never mind: allocation. To avoid allocation would need to enumerate without dictionary, and in big workspace it can be 100 times slower.\n\t\tif (_model._nameMap.MultiGet_(lastName, out FileNode single, out var multiple)) {\n\t\t\tif (multiple != null) {\n\t\t\t\tforeach (var f in multiple) if (_KindOk(f) && _PathOk(f)) return f;\n\t\t\t\tif (orAnywhere && i < 0) { //try to find single descendant in this folder or in an ancestor folder\n\t\t\t\t\tfor (var p = this; p.HasParent; p = p.Parent) {\n\t\t\t\t\t\tFileNode found = null;\n\t\t\t\t\t\tforeach (var f in multiple) if (_KindOk(f) && p.IsAncestorOf(f)) { if (found == null) found = f; else goto gMulti; }\n\t\t\t\t\t\tif (found != null) return found;\n\t\t\t\t\t}\n\t\t\t\t\tgMulti:\n\t\t\t\t\tModel.FoundMultiple = multiple;\n\t\t\t\t}\n\t\t\t} else if (_KindOk(single)) {\n\t\t\t\tif ((orAnywhere && i < 0) || _PathOk(single)) return single;\n\t\t\t}\n\t\t}\n\t\tif (!retry) {\n\t\t\tretry = kind is FNFind.CodeFile or FNFind.Class ? !lastName.Ends(\".cs\", true) : kind is FNFind.Folder ? false : !lastName.Contains('.');\n\t\t\tif (retry) { name += \".cs\"; goto gRetry; }\n\t\t}\n\t\treturn null;\n\t\t\n\t\tbool _KindOk(FileNode f) => FilesModel.KindFilter_(f, kind);\n\t\t\n\t\tbool _PathOk(FileNode f) {\n\t\t\tf = f.Parent;\n\t\t\tfor (int j = i; j > 0 && f != null; f = f.Parent) {\n\t\t\t\tint k = name.LastIndexOf('\\\\', j - 1);\n\t\t\t\tif (!name.Eq((k + 1)..j, f.Name ?? \"\", true)) return false;\n\t\t\t\tj = k;\n\t\t\t}\n\t\t\treturn f == this;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Finds file or folder by name or path relative to: this folder, parent folder (if this is file) or root (if relativePath starts with @\"\\\").\n\t/// Returns null if not found; also if name is null/\"\".\n\t/// </summary>\n\t/// <param name=\"relativePath\">Examples: \"name.cs\", @\"subfolder\\name.cs\", @\".\\subfolder\\name.cs\", @\"..\\parent\\name.cs\", @\"\\root path\\name.cs\".</param>\n\t/// <param name=\"kind\"></param>\n\t/// <param name=\"orAnywhere\">If <i>relativePath</i> is filename and does not exist in this folder: if single such file exists anywhere or in subfolders or in an ancestor folder, return that file.</param>\n\tpublic FileNode FindRelative(bool orAnywhere, string relativePath, FNFind kind = FNFind.Any) {\n\t\tif (!IsFolder) return Parent.FindRelative(orAnywhere, relativePath, kind);\n\t\tvar s = relativePath;\n\t\tif (s.NE()) return null;\n\t\tFileNode p = this;\n\t\tif (s[0] == '\\\\') p = Root;\n\t\telse if (s[0] == '.') {\n\t\t\tint i = 0;\n\t\t\tfor (; s.Eq(i, @\"..\\\"); i += 3) { p = p.Parent; if (p == null) return null; }\n\t\t\tif (i == 0 && s.Starts(@\".\\\")) i = 2;\n\t\t\tif (i != 0) {\n\t\t\t\tif (i == s.Length) return (p == Root || !(kind is FNFind.Any or FNFind.Folder)) ? null : p;\n\t\t\t\ts = s[i..];\n\t\t\t}\n\t\t\torAnywhere = false;\n\t\t}\n\t\treturn p._FindRelative(s, kind, orAnywhere);\n\t}\n\t\n\t/// <summary>\n\t/// Finds all descendant files (and not folders) that have the specified name.\n\t/// Returns empty array if not found.\n\t/// </summary>\n\t/// <param name=\"name\">File name. If starts with backslash, works like <see cref=\"FindDescendant\"/>.</param>\n\tpublic FileNode[] FindAllDescendantFiles(string name) {\n\t\tif (!name.NE()) {\n\t\t\tif (name[0] == '\\\\') {\n\t\t\t\tvar f1 = _FindRelative(name, FNFind.File);\n\t\t\t\tif (f1 != null) return [f1];\n\t\t\t} else {\n\t\t\t\treturn Descendants().Where(k => !k.IsFolder && k._name.Eqi(name)).ToArray();\n\t\t\t}\n\t\t}\n\t\treturn [];\n\t}\n\t\n\t/// <summary>\n\t/// Gets all descendant nodes, except descendants of folders named \"Garbage\".\n\t/// </summary>\n\tpublic IEnumerable<FileNode> DescendantsExceptGarbage() {\n\t\tint level = 0;\n\t\tforeach (var f in Descendants()) {\n\t\t\tif (level == 0) {\n\t\t\t\tif (f.IsFolder) if (f.Name.Eqi(\"Garbage\")) level = f.Level;\n\t\t\t} else {\n\t\t\t\tif (f.Level > level) continue;\n\t\t\t\tlevel = 0;\n\t\t\t}\n\t\t\tyield return f;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Finds ancestor (including self) project folder and its main file.\n\t/// </summary>\n\t/// <returns>true if this is in a project folder and found its main file.</returns>\n\t/// <param name=\"folder\">If the function returns true, receives the project folder, else null.</param>\n\t/// <param name=\"main\">If the function returns true, receives the project-main file, else null.</param>\n\t/// <param name=\"ofAnyScript\">Get project even if this is a non-main script in project folder.</param>\n\tpublic bool FindProject(out FileNode folder, out FileNode main, bool ofAnyScript = false) {\n\t\tfor (FileNode r = Root, f = IsFolder ? this : Parent; f != r && f != null; f = f.Parent) {\n\t\t\tif (!f.IsProjectFolder(out var fm)) continue;\n\t\t\tif (this.IsScript && this != fm && !ofAnyScript) break; //non-main scripts are not part of project\n\t\t\tmain = fm;\n\t\t\tfolder = f;\n\t\t\treturn true;\n\t\t}\n\t\tfolder = main = null;\n\t\treturn false;\n\t}\n\t//CONSIDER: multiscript project. Each script in it is compiled together with cs files.\n\t\n\t/// <summary>\n\t/// If this is in a project, returns the project-main file. Else returns this.\n\t/// This must be a code file (asserts).\n\t/// </summary>\n\t/// <param name=\"ofAnyScript\">Get project even if this is a non-main script in project folder.</param>\n\tpublic FileNode GetProjectMainOrThis(bool ofAnyScript = false) {\n\t\tDebug.Assert(IsCodeFile);\n\t\treturn FindProject(out _, out var pm, ofAnyScript) ? pm : this;\n\t}\n\t\n\t//public FileNode GetProjectMainOrThis\n\t\n\t/// <summary>\n\t/// Returns true if this is a folder and Name starts with '@' and contains main code file.\n\t/// </summary>\n\t/// <param name=\"main\">Receives the main code file. It is the first direct child code file.</param>\n\tpublic bool IsProjectFolder(out FileNode main) {\n\t\tmain = null;\n\t\tif (IsFolder && _name[0] == '@') {\n\t\t\tfor (var f = FirstChild; f != null; f = f.Next) {\n\t\t\t\tif (f.IsCodeFile) { main = f; return true; }\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\t\n\tpublic IEnumerable<FileNode> EnumProjectClassFiles(FileNode fSkip = null) {\n\t\tforeach (var f in Descendants()) {\n\t\t\tif (f._type == FNType.Class && f != fSkip) yield return f;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Gets class file role from metacomments.\n\t/// Note: can be slow, because loads file text if this is a class file.\n\t/// </summary>\n\t/// <param name=\"preferApp\">If it's a test-runnable class file, return <b>App</b> instead of <b>Class</b>.</param>\n\tpublic FNClassFileRole GetClassFileRole(bool preferApp = false) {\n\t\tif (_type != FNType.Class) return FNClassFileRole.None;\n\t\tvar r = FNClassFileRole.Class;\n\t\tif (GetCurrentText(out var code, silent: null)) {\n\t\t\tvar meta = MetaComments.FindMetaComments(code);\n\t\t\tif (meta.end > 0) {\n\t\t\t\tint i = -1;\n\t\t\t\tbool findDefineTest = false;\n\t\t\t\tforeach (var v in MetaComments.EnumOptions(code, meta)) {\n\t\t\t\t\ti++;\n\t\t\t\t\tif (findDefineTest) {\n\t\t\t\t\t\tif (v.NameIs(\"define\")) return FNClassFileRole.Class;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (!v.NameIs(\"role\")) continue;\n\t\t\t\t\t\tif (v.ValueIs(\"classLibrary\")) return FNClassFileRole.Library;\n\t\t\t\t\t\tif (v.ValueIs(\"classFile\")) break;\n\t\t\t\t\t\tr = FNClassFileRole.App;\n\t\t\t\t\t\tif (!preferApp) if (findDefineTest = i == 0) continue; //maybe using feature \"test-run without a test script\", eg `/*/ role miniProgram; define TEST; /*/ ... #if TEST ...`\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn r;\n\t}\n\t\n\t/// <summary>\n\t/// Returns true if this is a code file that can be executed directly (not via project main or test script).\n\t/// </summary>\n\tpublic bool IsExecutableDirectly() {\n\t\tif (!IsCodeFile) return false;\n\t\tif (IsClass) {\n\t\t\tif (GetProjectMainOrThis() != this) return false;\n\t\t\tif (GetClassFileRole(preferApp: true) != FNClassFileRole.App) return false;\n\t\t}\n\t\treturn true;\n\t}\n\t\n\t#endregion\n\t\n\t#region new item\n\t\n\tpublic static string CreateNameUniqueInFolder(FileNode folder, string fromName, bool forFolder, bool autoGenerated = false, bool moving = false, FileNode renaming = null) {\n\t\tbool isGlobal = !forFolder && !moving && fromName.Eqi(\"global.cs\");\n\t\tif (isGlobal) {\n\t\t\tif (null == folder.Model.FindGlobalCs(silent: true)) return fromName;\n\t\t} else {\n\t\t\tif (!_Exists(fromName)) return fromName;\n\t\t}\n\t\t\n\t\tstring oldName = fromName, ext = null;\n\t\tif (!forFolder) {\n\t\t\tint i = fromName.LastIndexOf('.');\n\t\t\tif (i >= 0) { ext = fromName[i..]; fromName = fromName[..i]; }\n\t\t}\n\t\tfromName = fromName.RxReplace(@\"\\d+$\", \"\");\n\t\tfor (int i = 2; ; i++) {\n\t\t\tvar s = fromName + i + ext;\n\t\t\tif (!_Exists(s)) {\n\t\t\t\tif (!autoGenerated) print.it($\"Info: name \\\"{oldName}\\\" has been changed to \\\"{s}\\\"{(isGlobal ? null : \", to make it unique in the folder\")}.\");\n\t\t\t\treturn s;\n\t\t\t}\n\t\t}\n\t\t\n\t\tbool _Exists(string s) {\n\t\t\tvar f = _FindIn(folder.Children(), s, FNFind.Any, false);\n\t\t\tif (f != null) return f != renaming;\n\t\t\tif (filesystem.exists(folder.FilePath + \"\\\\\" + s)) return true; //orphaned file?\n\t\t\treturn false;\n\t\t}\n\t}\n\t\n\tpublic static class Templates {\n\t\tpublic static readonly string DefaultDirBS = folders.ThisAppBS + @\"Templates\\files\\\";\n\t\tpublic static readonly string UserDirBS = AppSettings.DirBS + @\"Templates\\\";\n\t\t\n\t\tpublic static string FileName(ETempl templ) => templ switch { ETempl.Class => \"Class.cs\", _ => \"Script.cs\" };\n\t\t\n\t\tpublic static string FilePathRaw(ETempl templ, bool user) => (user ? UserDirBS : DefaultDirBS) + FileName(templ);\n\t\t\n\t\tpublic static string FilePathReal(ETempl templ, bool? user = null) {\n\t\t\tbool u = user ?? ((ETempl)App.Settings.templ_use).Has(templ);\n\t\t\tvar file = FilePathRaw(templ, u);\n\t\t\tif (u && !filesystem.exists(file, true)) file = FilePathRaw(templ, false);\n\t\t\treturn file;\n\t\t}\n\t\t\n\t\tpublic static string Load(ETempl templ, bool? user = null) {\n\t\t\treturn filesystem.loadText(FilePathReal(templ, user));\n\t\t}\n\t\t\n\t\tpublic static bool IsStandardTemplateName(string template, out ETempl result, bool ends = false) {\n\t\t\tint i = ends ? template.Ends(false, s_names) : template.Eq(false, s_names);\n\t\t\tif (i-- == 0) { result = 0; return false; }\n\t\t\tresult = (ETempl)(1 << i);\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\treadonly static string[] s_names = { \"Script.cs\", \"Class.cs\" };\n\t\t\n\t\t/// <summary>\n\t\t/// Loads Templates\\files.xml and optionally finds a template in it.\n\t\t/// Returns null if template not found. Exception if fails to load file.\n\t\t/// Uses caching to avoid loading file each time, but reloads if file modified; don't modify the XML DOM.\n\t\t/// </summary>\n\t\t/// <param name=\"template\">null or relative path of template in Templates\\files. Case-insensitive.</param>\n\t\tpublic static XElement LoadXml(string template = null) {\n\t\t\t//load files.xml first time, or reload if file modified\n\t\t\tfilesystem.GetTime_(s_xmlFilePath, out var time);\n\t\t\tif (s_xml == null || time != s_xmlFileTime) {\n\t\t\t\ts_xml = XmlUtil.LoadElem(s_xmlFilePath);\n\t\t\t\ts_xmlFileTime = time;\n\t\t\t}\n\t\t\t\n\t\t\tvar x = s_xml;\n\t\t\tif (template != null) {\n\t\t\t\tvar a = template.Split('\\\\');\n\t\t\t\tfor (int i = 0; i < a.Length; i++) x = x?.Elem(i < a.Length - 1 ? \"d\" : null, \"n\", a[i], ignoreCase: true);\n\t\t\t\tDebug.Assert(x != null);\n\t\t\t}\n\t\t\treturn x;\n\t\t}\n\t\tstatic XElement s_xml;\n\t\tstatic readonly string s_xmlFilePath = folders.ThisAppBS + @\"Templates\\files.xml\";\n\t\tstatic long s_xmlFileTime;\n\t\t\n\t\tpublic static bool IsInDefault(XElement x) => x.Ancestors().Any(o => o.Attr(\"n\") == \"Default\");\n\t}\n\t\n\t[Flags]\n\tpublic enum ETempl { Script = 1, Class = 2 }\n\t\n\t#endregion\n\t\n\t#region rename, move, copy\n\t\n\t/// <summary>\n\t/// Changes Name of this object and renames its file (if not link).\n\t/// Returns false if name is empty or fails to rename its file.\n\t/// </summary>\n\t/// <param name=\"name\">\n\t/// Name, like \"New name.cs\" or \"New name\".\n\t/// If not folder, adds previous extension if no extension or changed code file extension.\n\t/// If invalid filename, replaces invalid characters etc.\n\t/// </param>\n\t/// <param name=\"syncing\">Called by the filesystem sync. The filesystem file is renamed. Need just to set item name and update everything.</param>\n\tpublic bool FileRename(string name, bool syncing = false) {\n\t\tif (syncing) {\n\t\t\t_SetName(name);\n\t\t} else {\n\t\t\tname = pathname.correctName(name);\n\t\t\tif (!IsFolder) {\n\t\t\t\tvar ext = FileExt;\n\t\t\t\tif (ext.Length > 0) if (name.IndexOf('.') < 0 || (IsCodeFile && !name.Ends(ext, true))) name += ext;\n\t\t\t}\n\t\t\tif (name == _name) return true;\n\t\t\tname = CreateNameUniqueInFolder(Parent, name, IsFolder, renaming: this);\n\t\t\t\n\t\t\tif (!RenameL_(name)) return false;\n\t\t}\n\t\t\n\t\tif (IsFolder) _model.ChangedFolderItemPath_();\n\t\t_model.Save.WorkspaceAsync();\n\t\tFilesModel.Redraw(this, remeasure: true, renamed: true);\n\t\tCompiler.Uncache(this, andDescendants: true);\n\t\tCodeInfo.FilesChanged();\n\t\treturn true;\n\t}\n\t\n\tinternal bool RenameL_(string name) {\n\t\tif (!IsLink) {\n\t\t\tif (!_model.TryFileOperation(FOSync.UserFileOp, () => filesystem.rename(FilePath, name, FIfExists.Fail))) return false;\n\t\t}\n\t\t_SetName(name);\n\t\treturn true;\n\t\t\n\t\t//CONSIDER: when renaming or moving, search all code files (except in \"Garbage*\" folders) and replace the old name or path in strings.\n\t\t//\tAlso doc the garbage folders feature.\n\t}\n\t\n\t/// <summary>\n\t/// Returns true if can move the tree node into the specified position.\n\t/// For example, cannot move parent into child etc.\n\t/// Does not check whether can move the file.\n\t/// </summary>\n\tpublic bool CanMove(FNInsertPos ipos) {\n\t\t//cannot move into self or descendants\n\t\tif (ipos.f == this || ipos.f.IsDescendantOf(this)) return false;\n\t\t\n\t\t//cannot move into a non-folder or before/after self\n\t\tswitch (ipos.pos) {\n\t\tcase FNInsert.Before:\n\t\t\tif (Next == ipos.f) return false;\n\t\t\tbreak;\n\t\tcase FNInsert.After:\n\t\t\tif (Previous == ipos.f) return false;\n\t\t\tbreak;\n\t\tdefault: //inside\n\t\t\tif (!ipos.f.IsFolder) return false;\n\t\t\tbreak;\n\t\t}\n\t\treturn true;\n\t}\n\t\n\t/// <summary>\n\t/// Moves this into, before or after target.\n\t/// Also moves file if need.\n\t/// </summary>\n\t/// <param name=\"ipos\">If f null, uses Root, Last.</param>\n\tpublic bool FileMove(FNInsertPos ipos) {\n\t\tif (ipos.f == null) ipos = new(Root, FNInsert.Last);\n\t\tif (!CanMove(ipos)) return false;\n\t\t\n\t\tvar newParent = ipos.ParentFolder;\n\t\tif (newParent != Parent) {\n\t\t\tvar name = CreateNameUniqueInFolder(newParent, _name, IsFolder, moving: true);\n\t\t\t\n\t\t\tif (!IsLink) {\n\t\t\t\tstring path2 = newParent.FilePath + \"\\\\\" + name;\n\t\t\t\tif (!_model.TryFileOperation(FOSync.UserFileOp, () => filesystem.move(FilePath, path2, FIfExists.Fail))) return false;\n\t\t\t}\n\t\t\t\n\t\t\tif (name != _name) _SetName(name);\n\t\t}\n\t\t\n\t\t//move tree node\n\t\tRemove();\n\t\tCommon_MoveCopyNew(ipos, true);\n\t\t\n\t\treturn true;\n\t}\n\t\n\tpublic void Common_MoveCopyNew(FNInsertPos ipos, bool moving) {\n\t\tAddToTree(ipos);\n\t\t_model.Save.WorkspaceAsync();\n\t\tif (moving) {\n\t\t\tif (IsFolder) _model.ChangedFolderItemPath_();\n\t\t\tCompiler.Uncache(this, andDescendants: true);\n\t\t}\n\t\tCodeInfo.FilesChanged();\n\t}\n\t\n\t/// <summary>\n\t/// Adds this to the tree, updates control, optionally sets to save workspace.\n\t/// </summary>\n\tpublic void AddToTree(FNInsertPos ipos) {\n\t\tif (ipos.Inside) ipos.f.AddChild(this, first: ipos.pos == FNInsert.First); else ipos.f.AddSibling(this, after: ipos.pos == FNInsert.After);\n\t\t_model.UpdateControlItems();\n\t}\n\t\n\t/// <summary>\n\t/// Copies this into, before or after target.\n\t/// Also copies file if need.\n\t/// Returns the copy, or null if fails.\n\t/// </summary>\n\t/// <param name=\"ipos\">If f null, uses Root, Last.</param>\n\t/// <param name=\"model\">Copy into this FileModel. It is != _model when importing workspace.</param>\n\t/// <param name=\"copyLinkTarget\">For links, copy the link target into the workspace.</param>\n\tinternal FileNode _FileCopy(FNInsertPos ipos, FilesModel model, bool copyLinkTarget = false) {\n\t\t_model.Save?.TextNowIfNeed(onlyText: true);\n\t\tif (ipos.f == null) ipos = new(Root, FNInsert.Last);\n\t\t\n\t\t//create unique name\n\t\tvar newParent = ipos.ParentFolder;\n\t\tstring name = CreateNameUniqueInFolder(newParent, _name, IsFolder);\n\t\t\n\t\t//copy file or directory\n\t\tif (!IsLink || copyLinkTarget) {\n\t\t\tstring path2 = newParent.FilePath + \"\\\\\" + name;\n\t\t\tif (!_model.TryFileOperation(FOSync.UserFileOp, () => filesystem.copy(FilePath, path2, FIfExists.Fail))) return null;\n\t\t}\n\t\t\n\t\t//create new FileNode with descendants\n\t\tvar f = new FileNode(model, this, name);\n\t\tif (copyLinkTarget) f._linkTarget = null;\n\t\t_CopyChildren(this, f);\n\t\t\n\t\tvoid _CopyChildren(FileNode from, FileNode to) {\n\t\t\tif (!from.IsFolder) return;\n\t\t\tforeach (var v in from.Children()) {\n\t\t\t\tvar t = new FileNode(model, v, v._name);\n\t\t\t\tto.AddChild(t);\n\t\t\t\t_CopyChildren(v, t);\n\t\t\t}\n\t\t}\n\t\t\n\t\t//insert at the specified place and set to save\n\t\tf.Common_MoveCopyNew(ipos, false);\n\t\treturn f;\n\t}\n\t\n\t#endregion\n\t\n\t#region util\n\t\n\t//public bool ContainsName(string name, bool printInfo = false) {\n\t//\tif (null == _FindIn(Children(), name, null, false)) return false;\n\t//\tif (printInfo) print.it(name + \" exists in this folder.\");\n\t//\treturn true;\n\t//}\n\t\n\t/// <summary>\n\t/// Gets file type from XML tag which should be \"d\", \"s\", \"c\" or \"n\".\n\t/// If none, throws ArgumentException if canThrow, else returns FNType.Other.\n\t/// </summary>\n\tpublic static FNType XmlTagToFileType(string tag, bool canThrow) => tag switch {\n\t\t\"d\" => FNType.Folder,\n\t\t\"s\" => FNType.Script,\n\t\t\"c\" => FNType.Class,\n\t\t\"n\" => FNType.Other,\n\t\t_ => !canThrow ? FNType.Other : throw new ArgumentException(\"XML element name must be 'd', 's', 'c' or 'n'\")\n\t};\n\t\n\t/// <summary>\n\t/// Detects file type from extension and code.\n\t/// If .cs, returns Class or Script, else Other.\n\t/// Must be not folder.\n\t/// </summary>\n\tstatic FNType _DetectFileType(string path) {\n\t\tvar type = FNType.Other;\n\t\tif (path.Ends(\".cs\", true)) {\n\t\t\ttype = FNType.Class;\n\t\t\ttry {\n\t\t\t\tvar code = filesystem.loadText(path);\n\t\t\t\tif (CiUtil.IsScript(code)) type = FNType.Script;\n\t\t\t}\n\t\t\tcatch (Exception ex) { Debug_.Print(ex); }\n\t\t}\n\t\treturn type;\n\t}\n\t\n\t#endregion\n}\n"
  },
  {
    "path": "Au.Editor/Files/Files+.cs",
    "content": "using System.Windows;\nusing System.Windows.Controls;\nusing Au.Controls;\n\nnamespace LA;\n\n/// <summary>\n/// File type of a <see cref=\"FileNode\"/>.\n/// Saved in XML as tag name: d folder, s script, c class, n other.\n/// </summary>\nenum FNType : byte {\n\tFolder, //must be 0\n\tScript,\n\tClass,\n\tOther,\n}\n\nenum FNInsert { Last, First, Before, After }\n\nrecord struct FNInsertPos(FileNode f, FNInsert pos) {\n\tpublic bool Inside => pos is FNInsert.First or FNInsert.Last;\n\tpublic FileNode ParentFolder => Inside ? f : f.Parent;\n}\n\nenum FNFind {\n\tAny, File, Folder, CodeFile, Class/*, Script*/ //Script not useful because class files can be executable too\n}\n\nenum FNClassFileRole {\n\t/// <summary>Not a class file.</summary>\n\tNone,\n\t/// <summary>Has meta role miniProgram/exeProgram/editorExtension.</summary>\n\tApp,\n\t/// <summary>Has meta role classLibrary.</summary>\n\tLibrary,\n\t/// <summary>Has meta role classFile, or no meta role.</summary>\n\tClass,\n}\n\nclass NewFileText {\n\tpublic bool replaceTemplate;\n\tpublic string text, meta;\n\t\n\tpublic NewFileText() { }\n\t\n\tpublic NewFileText(bool replaceTemplate, string text, string meta = null) {\n\t\tthis.replaceTemplate = replaceTemplate;\n\t\tthis.text = text;\n\t\tthis.meta = meta;\n\t}\n}\n\nclass DNewWorkspace : KDialogWindow {\n\tstring _name, _location;\n\tpublic string ResultPath { get; private set; }\n\t\n\tpublic DNewWorkspace(string name, string location) {\n\t\t_name = name;\n\t\t_location = location;\n\t\t\n\t\tTitle = \"New workspace\";\n\t\t\n\t\tvar b = new wpfBuilder(this).WinSize(600);\n\t\tTextBox tName, tLocation = null;\n\t\tb.R.Add(\"Folder name\", out tName, _name).Validation(_Validate).Focus();\n\t\tb.R.Add(\"Parent folder\", out tLocation, _location).Validation(_Validate);\n\t\tb.R.AddButton(\"Browse...\", _Browse).Width(70, \"L\");\n\t\tb.R.AddOkCancel();\n\t\tb.End();\n\t\t\n\t\tvoid _Browse(WBButtonClickArgs e) {\n\t\t\tvar d = new FileOpenSaveDialog(\"{4D1F3AFB-DA1A-45AC-8C12-41DDA5C51CDA}\") {\n\t\t\t\tInitFolderNow = filesystem.exists(tLocation.Text).Directory ? tLocation.Text : folders.ThisAppDocuments,\n\t\t\t};\n\t\t\tif (d.ShowOpen(out string s, this, selectFolder: true)) tLocation.Text = s;\n\t\t}\n\t\t\n\t\tstring _Validate(FrameworkElement e) {\n\t\t\tvar s = (e as TextBox).Text.Trim();\n\t\t\tif (e == tLocation) {\n\t\t\t\tif (!filesystem.exists(s).Directory) return \"Folder does not exist\";\n\t\t\t} else {\n\t\t\t\tif (pathname.isInvalidName(s) || s[0] is '.' or '_') return \"Invalid filename\";\n\t\t\t\tResultPath = pathname.combine(tLocation.Text, s); //validation is when OK clicked\n\t\t\t\tif (filesystem.exists(ResultPath)) return s + \" already exists\";\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\t\n\t\t//b.OkApply += e => {\n\t\t//\tprint.it(ResultPath); e.Cancel = true;\n\t\t//};\n\t}\n}\n\nclass RepairWorkspace {\n\tconst int c_markerInfo = 0;\n\t\n\tpublic static void Repair() {\n\t\tvar rootFolder = App.Model.Root;\n\t\t\n\t\tusing var workingState = Panels.Found.Prepare(PanelFound.Found.Repair, \"Repair\", out var b);\n\t\tif (workingState.NeedToInitControl) {\n\t\t\tvar k = workingState.Scintilla;\n\t\t\tk.aaaMarkerDefine(c_markerInfo, Sci.SC_MARK_BACKGROUND, backColor: 0xEEE8AA);\n\t\t}\n\t\t\n\t\t//broken links\n\t\t\n\t\tb.Marker(c_markerInfo).Text(\"Broken links (missing target). You may want to delete them from the Files panel.\").NL();\n\t\tvar links = rootFolder.Descendants().Where(o => o.IsLink && !filesystem.exists(o.FilePath)).ToArray();\n\t\tif (links.Length > 0) {\n\t\t\tforeach (var f in links) {\n\t\t\t\tb.NL().Link(f, f.ItemPath); if (f.IsFolder) b.Text(\" (folder)\");\n\t\t\t\tb.Text(\" -> \").Text(f.FilePath);\n\t\t\t}\n\t\t\tb.NL();\n\t\t} else {\n\t\t\tb.Text(\"\\r\\nNone.\\r\\n\");\n\t\t}\n\t\t\n\t\t//biggest files\n\t\t\n\t\tb.NL().Marker(c_markerInfo).Text(\"Big files. You may want to delete some files if not used. Size in KB.\").NL();\n\t\tList<(long size, FileNode f)> biggest = new();\n\t\t_Biggest(rootFolder);\n\t\tvoid _Biggest(FileNode folder) {\n\t\t\tforeach (var f in folder.Children()) {\n\t\t\t\tif (f.IsLink) continue;\n\t\t\t\tif (f.IsFolder) {\n\t\t\t\t\t_Biggest(f);\n\t\t\t\t} else if (filesystem.GetProp_(f.FilePath, out var p) && p.size >= 50 * 1024) {\n\t\t\t\t\tbiggest.Add((p.size, f));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (biggest.Any()) {\n\t\t\tforeach (var (size, f) in biggest.OrderByDescending(o => o.size)) {\n\t\t\t\tb.NL().Text($\"{size / 1024} \").Link(f, f.ItemPath);\n\t\t\t}\n\t\t} else {\n\t\t\tb.Text(\"\\r\\nNone.\\r\\n\");\n\t\t}\n\t\t\n\t\t//--------------\n\t\t\n\t\tPanels.Found.SetResults(workingState, b);\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Files/FilesModel.cs",
    "content": "//TODO2: idea: in each directory auto-create-update a hidden file containing a copy of files.xml branch for that directory. Benefits:\n//\tCould recreate the ordering etc when the folder imported into another workspace.\n//\tCould recreate deleted or corrupt files.xml.\n\nusing System.Xml.Linq;\nusing System.IO.Compression;\nusing System.Windows.Input;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Controls.Primitives;\nusing System.Windows.Media;\nusing Au.Controls;\n\nnamespace LA;\n\nusing static Menus.File;\n\npartial class FilesModel {\n\tpublic readonly FileNode Root;\n\tpublic readonly int WorkspaceSN; //sequence number of workspace opened in this process: 1, 2...\n\tstatic int s_workspaceSN;\n\tpublic readonly string WorkspaceName;\n\tpublic readonly string WorkspaceDirectory;\n\tpublic readonly string WorkspaceFile; //.xml file containing the tree of .cs etc files\n\tpublic readonly string FilesDirectory; //.cs etc files\n\tpublic readonly string TempDirectory; //any temporary files\n\tpublic readonly string NugetDirectory; //NuGet libraries\n\tpublic readonly string NugetDirectoryBS; //NugetDirectory\\\n\tpublic readonly string DllDirectory; //user-created or downloaded libraries\n\tpublic readonly string DllDirectoryBS; //LibrariesDirectory\\\n\tpublic readonly AutoSave Save;\n\treadonly Dictionary<uint, FileNode> _idMap;\n\tinternal readonly Dictionary<string, object> _nameMap;\n\tpublic readonly WorkspaceSettings WSSett;\n\tpublic readonly WorkspaceState State;\n\treadonly bool _importing;\n\treadonly bool _initedFully;\n\tinternal object CompilerContext_;\n\tinternal IDisposable UndoContext_;\n\t//internal event Action<bool> TreeLoaded_;\n\t\n\t/// <param name=\"file\">Path of workspace tree file (files.xml).</param>\n\t/// <exception cref=\"ArgumentException\">Invalid or not full path.</exception>\n\t/// <exception cref=\"Exception\">XElement.Load exceptions. And possibly more.</exception>\n\tpublic FilesModel(string file, bool importing) {\n\t\t_importing = importing;\n\t\tWorkspaceFile = pathname.normalize(file);\n\t\tWorkspaceDirectory = pathname.getDirectory(WorkspaceFile);\n\t\tWorkspaceName = pathname.getName(WorkspaceDirectory);\n\t\tFilesDirectory = WorkspaceDirectory + @\"\\files\";\n\t\t//CacheDirectory = WorkspaceDirectory + @\"\\.cache\";\n\t\tTempDirectory = WorkspaceDirectory + @\"\\.temp\";\n\t\tNugetDirectory = WorkspaceDirectory + @\"\\.nuget\";\n\t\tNugetDirectoryBS = NugetDirectory + @\"\\\";\n\t\tDllDirectory = WorkspaceDirectory + @\"\\dll\";\n\t\tDllDirectoryBS = DllDirectory + @\"\\\";\n\t\t//ExeDirectory = WorkspaceDirectory + @\"\\exe\";\n\t\tif (!_importing) {\n\t\t\tWorkspaceSN = ++s_workspaceSN;\n\t\t\tfilesystem.createDirectory(FilesDirectory);\n\t\t\tSave = new AutoSave(this);\n\t\t}\n\t\t_idMap = new();\n\t\t_nameMap = new(StringComparer.OrdinalIgnoreCase);\n\t\t\n\t\tRoot = FileNode.LoadWorkspace(WorkspaceFile, this); //recursively creates whole model tree; caller handles exceptions\n\t\t\n\t\tList<FileNode> bad = null;\n\t\tforeach (var f in Root.Descendants()) {\n\t\t\tuint id = f.Id;\n\t\t\tif (id == 0 || !_idMap.TryAdd(id, f)) (bad ??= new()).Add(f); //missing or duplicate id in xml file\n\t\t\telse f._WorkspaceLoaded(id, _importing);\n\t\t}\n\t\tif (bad != null) {\n\t\t\tforeach (var f in bad) {\n\t\t\t\tif (!_importing) print.warning($\"Invalid id of {f.SciLink(true)}. Creating new.\");\n\t\t\t\tf._WorkspaceLoaded(AddGetId(f), _importing);\n\t\t\t}\n\t\t\tif (!_importing) {\n\t\t\t\tfilesystem.delete(WorkspaceDirectory + @\"\\.compiled\", FDFlags.CanFail);\n\t\t\t\tSave.WorkspaceAsync();\n\t\t\t}\n\t\t}\n\t\t\n\t\t//TreeLoaded_?.Invoke(_importing);\n\t\t\n\t\tif (!_importing) {\n\t\t\tWSSett = WorkspaceSettings.Load(WorkspaceDirectory + @\"\\settings.json\");\n\t\t\tState = new WorkspaceState(this);\n\t\t\tfolders.Workspace = new FolderPath(WorkspaceDirectory);\n\t\t\tEnvironment.SetEnvironmentVariable(\"dll\", DllDirectory);\n\t\t\t\n\t\t\tvar dirDel = TempDirectory + @\"\\delete\";\n\t\t\tif (filesystem.exists(dirDel, true)) filesystem.delete(dirDel, FDFlags.CanFail);\n\t\t}\n\t\t_initedFully = true;\n\t}\n\t\n\tpublic void Dispose() {\n\t\tif (_importing) return;\n\t\tif (_initedFully) {\n\t\t\tApp.Tasks.OnWorkspaceClosed();\n\t\t\tRecentTT.Clear();\n\t\t\t//Save.AllNowIfNeed(); //owner FilesPanel calls this before calling this func. Because may need more code in between.\n\t\t}\n\t\tSave?.Dispose();\n\t\tState?.Dispose();\n\t\tUndoContext_?.Dispose();\n\t\t_syncWatchers?.Dispose();\n\t\tWSSett?.Dispose();\n\t\tEditGoBack.DisableUI();\n\t}\n\t\n\t#region tree control\n\t\n\tpublic static FilesView TreeControl => Panels.Files?.TreeControl;\n\t\n\t/// <summary>\n\t/// Updates control when changed number or order of visible items (added, removed, moved, etc).\n\t/// </summary>\n\tpublic void UpdateControlItems() { TreeControl.SetItems(Root.Children(), true); }\n\t\n\t/// <summary>\n\t/// When need to redraw an item in controls that display it.\n\t/// If the parameter is null, redraw all items.\n\t/// </summary>\n\tpublic static event Action<RedrawEventData> NeedRedraw;\n\t\n\tpublic record class RedrawEventData(FileNode f, bool remeasure, bool renamed);\n\t\n\t/// <summary>\n\t/// Raises <see cref=\"NeedRedraw\"/> event.\n\t/// </summary>\n\tpublic static void Redraw(FileNode f = null, bool remeasure = false, bool renamed = false) {\n\t\tNeedRedraw?.Invoke(new(f, remeasure, renamed));\n\t}\n\t\n\t#endregion\n\t\n\t#region load workspace\n\t\n\tpublic static void LoadWorkspace(string wsDir = null) {\n\t\twsDir ??= App.Settings.workspace;\n\t\tif (wsDir.NE()) wsDir = folders.ThisAppDocuments + \"Main\"; else wsDir = pathname.normalize(wsDir);\n\t\t\n\t\tvar xmlFile = wsDir + @\"\\files.xml\";\n\t\tvar oldModel = App.Model;\n\t\tFilesModel m = null;\n\t\tg1:\n\t\ttry {\n\t\t\t//TODO3: if editor runs as admin, the workspace directory should be write-protected from non-admin processes.\n\t\t\t\n\t\t\tif (s_isNewWorkspace = !filesystem.exists(xmlFile).File) {\n\t\t\t\tfilesystem.copy(folders.ThisAppBS + @\"Default\\Workspace\", wsDir);\n\t\t\t}\n\t\t\t\n\t\t\toldModel?.UnloadingWorkspace_(); //saves all, closes documents, sets current file = null\n\t\t\t\n\t\t\tm = new FilesModel(xmlFile, importing: false);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tm?.Dispose();\n\t\t\tm = null;\n\t\t\t//print.it($\"Failed to load '{wsDir}'. {ex.Message}\");\n\t\t\tswitch (dialog.showError(\"Failed to load workspace\", wsDir,\n\t\t\t\t\"1 Retry|2 Load another|3 Create new|0 Cancel\",\n\t\t\t\towner: TreeControl, expandedText: ex.ToString())) {\n\t\t\tcase 1: goto g1;\n\t\t\tcase 2: OpenWorkspaceUI(); break;\n\t\t\tcase 3: NewWorkspaceUI(); break;\n\t\t\t}\n\t\t\tif (App.Model == null) Environment.Exit(1);\n\t\t\treturn;\n\t\t}\n\t\t\n\t\toldModel?.Dispose();\n\t\tApp.Model = m;\n\t\t\n\t\t//this code is important for portable\n\t\tfilesystem.more.getFinalPath(wsDir, out wsDir);\n\t\tfilesystem.more.getFinalPath(folders.ThisAppDocuments, out var tad);\n\t\tvar unexpanded = folders.unexpandPath(wsDir, (tad, \"%folders.ThisAppDocuments%\"));\n\t\t\n\t\tif (unexpanded != App.Settings.workspace) {\n\t\t\tif (App.Settings.workspace != null) {\n\t\t\t\tstring[] ar = App.Settings.recentWS ?? [];\n\t\t\t\tint i = Array.IndexOf(ar, unexpanded);\n\t\t\t\tif (i < 0) i = Array.IndexOf(ar, wsDir);\n\t\t\t\tif (i >= 0) ar = ar.RemoveAt(i);\n\t\t\t\tApp.Settings.recentWS = ar.InsertAt(0, App.Settings.workspace);\n\t\t\t}\n\t\t\tApp.Settings.workspace = unexpanded;\n\t\t}\n\t\t\n\t\tif (App.Loaded == AppState.LoadedUI) m.WorkspaceLoadedWithUI(onUiLoaded: false);\n\t}\n\t\n\t[MethodImpl(MethodImplOptions.NoInlining)] //avoid loading WPF at startup before loading UI\n\tpublic void WorkspaceLoadedWithUI(bool onUiLoaded) {\n\t\tif (!s_isNewWorkspace) LoadState(expandFolders: true);\n\t\tTreeControl.SetItems();\n\t\tif (s_isNewWorkspace) {\n\t\t\ts_isNewWorkspace = false;\n\t\t\tAddMissingDefaultFiles(true, true);\n\t\t\tSetCurrentFile(Root.FirstChild, newFile: true);\n\t\t} else {\n\t\t\tLoadState(openFiles: true);\n\t\t\tApp.Dispatcher.InvokeAsync(() => { SyncWithFilesystem_(); });\n\t\t}\n\t\tThisWorkspaceLoadedAndDocumentsOpened?.Invoke();\n\t\tAnyWorkspaceLoadedAndDocumentsOpened?.Invoke();\n\t\tif (!onUiLoaded) RunStartupScripts(true);\n\t}\n\t\n\tstatic bool s_isNewWorkspace;\n\t\n\tpublic event Action ThisWorkspaceLoadedAndDocumentsOpened;\n\tpublic static event Action AnyWorkspaceLoadedAndDocumentsOpened;\n\t\n\t/// <summary>\n\t/// Shows \"Open workspace\" dialog. On OK loads the selected workspace.\n\t/// </summary>\n\tpublic static void OpenWorkspaceUI() {\n\t\tvar d = new FileOpenSaveDialog(\"{4D1F3AFB-DA1A-45AC-8C12-41DDA5C51CDA}\") { Title = \"Open workspace\" };\n\t\tif (!d.ShowOpen(out string s, App.Hmain, selectFolder: true)) return;\n\t\tif (!filesystem.exists(s + @\"\\files.xml\").File) dialog.showError(null, \"The folder must contain file files.xml\", owner: TreeControl);\n\t\telse LoadWorkspace(s);\n\t}\n\t\n\t/// <summary>\n\t/// Shows dialog to create new workspace. On OK creates and loads new workspace.\n\t/// </summary>\n\tpublic static void NewWorkspaceUI() {\n\t\tvar path = GetDirectoryPathForNewWorkspace();\n\t\tif (path != null) LoadWorkspace(path);\n\t}\n\t\n\t#endregion\n\t\n\t#region find, id\n\t\n\t/// <summary>\n\t/// Finds file or folder by name or @\"\\relative path\" or id or full path.\n\t/// </summary>\n\t/// <param name=\"name\">\n\t/// Can be:\n\t/// Name, like \"name.cs\" or just \"name\".\n\t/// Relative path like @\"\\name.cs\" or @\"\\subfolder\\name.cs\".\n\t/// Full path in this workspace or of a linked external file.\n\t/// :id - <see cref=\"FileNode.IdString\"/> with prefix \":\", or <see cref=\"FileNode.IdStringWithWorkspace\"/>; can be followed by any text.\n\t/// \n\t/// Case-insensitive.\n\t/// If just \"name\" and not found and name does not end with \".cs\", tries to find name + \".cs\".\n\t/// </param>\n\t/// <param name=\"kind\">Ignored if <i>name</i> is id.</param>\n\t/// <param name=\"silent\">Don't print warning \"Found multiple...\".</param>\n\tpublic FileNode Find(string name, FNFind kind = FNFind.Any, bool silent = false) {\n\t\tFoundMultiple = null;\n\t\tif (name.NE()) return null;\n\t\tif (name[0] == ':') {\n\t\t\tname.ToInt(out long id, 1);\n\t\t\treturn FindById(id);\n\t\t}\n\t\tif (pathname.isFullPath(name)) return FindByFilePath(name, kind);\n\t\tif (name[0] == '\\\\') return Root.FindDescendant(name, kind);\n\t\treturn _FindByName(name, kind);\n\t\t\n\t\tFileNode _FindByName(string name, FNFind kind) {\n\t\t\tif (_nameMap.MultiGet_(name, out FileNode v, out var a)) {\n\t\t\t\tif (v != null) return KindFilter_(v, kind) ? v : null;\n\t\t\t\tFileNode first = null;\n\t\t\t\tforeach (var f in a) {\n\t\t\t\t\tif (!KindFilter_(f, kind)) continue;\n\t\t\t\t\tif (first == null) first = f; else (FoundMultiple ??= new() { first }).Add(f);\n\t\t\t\t}\n\t\t\t\tif (FoundMultiple == null) return first; //note: don't return the first if found multiple. Unsafe.\n\t\t\t\tif (!silent) {\n\t\t\t\t\tvar paths = string.Join(\", \", FoundMultiple.Select(o => o.SciLink(path: true)));\n\t\t\t\t\tprint.warning($\"Found multiple '{name}'. Use path if possible, or rename.\\r\\n\\tPaths: {paths}.\"/*, -1*/);\n\t\t\t\t}\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tif (kind != FNFind.Folder && !name.Ends(\".cs\", true)) return _FindByName(name + \".cs\", kind);\n\t\t\treturn null;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// When <see cref=\"Find\"/> returns null because exists multiple items with the specified name/kind, this property contains them. Else null.\n\t/// </summary>\n\tpublic List<FileNode> FoundMultiple;\n\t\n\tinternal static bool KindFilter_(FileNode f, FNFind kind) => kind switch {\n\t\tFNFind.File => !f.IsFolder,\n\t\tFNFind.Folder => f.IsFolder,\n\t\tFNFind.CodeFile => f.IsCodeFile,\n\t\tFNFind.Class => f.IsClass,\n\t\t//FNFind.Script => f.IsScript,\n\t\t_ => true, //FNFind.Any\n\t};\n\t\n\t/// <summary>\n\t/// Calls <see cref=\"Find(string, FNFind, bool)\"/>(name, FNFind.CodeFile).\n\t/// </summary>\n\t/// <inheritdoc cref=\"Find(string, FNFind, bool)\"/>\n\tpublic FileNode FindCodeFile(string name, bool silent = false) => Find(name, FNFind.CodeFile, silent);\n\t\n\t/// <summary>\n\t/// Finds file or folder by its file path (<see cref=\"FileNode.FilePath\"/>).\n\t/// </summary>\n\t/// <param name=\"path\">Full path of a file in this workspace or of a linked external file.</param>\n\t/// <param name=\"kind\"></param>\n\tpublic FileNode FindByFilePath(string path, FNFind kind = FNFind.Any) {\n\t\tvar d = FilesDirectory;\n\t\tif (path.PathStarts(d)) return Root.FindDescendant(path[d.Length..], kind); //is in workspace folder\n\t\t\n\t\tforeach (var f in Root.Descendants()) {\n\t\t\tif (f.IsLink) {\n\t\t\t\tif (path.Eqi(f.LinkTarget)) return KindFilter_(f, kind) ? f : null;\n\t\t\t\tif (f.IsFolder) {\n\t\t\t\t\td = f.LinkTarget;\n\t\t\t\t\tif (path.PathStarts(d)) return f.FindDescendant(path[d.Length..], kind);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\t\n\t///// <summary>\n\t///// If path starts with <see cref=\"FilesDirectory\"/> and '\\\\', removes the FilesDirectory part and returns true.\n\t///// </summary>\n\t///// <param name=\"path\">Full or relative path or name.</param>\n\t//bool _FullPathToRelative(ref string path) {\n\t//\tvar d = FilesDirectory;\n\t//\tbool full = path.Starts(d, true) && path.Eq(d.Length, '\\\\');\n\t//\tif (full) path = path[d.Length..];\n\t//\treturn full;\n\t//}\n\t\n\t/// <summary>\n\t/// Finds file or folder by exact <c>@\"\\relative path\"</c>.\n\t/// </summary>\n\t/// <param name=\"path\">Must start with <c>@\"\\\"</c> (asserts).</param>\n\t/// <remarks>\n\t/// Use only with paths created by the program, and not by users.\n\t/// Does not retry with \".cs\" suffix.\n\t/// </remarks>\n\t/// <returns></returns>\n\tpublic FileNode FindByItemPath(string path) {\n\t\tDebug.Assert(path.Starts('\\\\'));\n\t\tint i = path.LastIndexOf('\\\\');\n\t\tvar lastName = path[(i + 1)..];\n\t\tif (_nameMap.MultiGet_(lastName, out FileNode single, out var multiple)) {\n\t\t\tif (multiple != null) {\n\t\t\t\tforeach (var f in multiple) if (_PathOk(f)) return f;\n\t\t\t} else {\n\t\t\t\tif (_PathOk(single)) return single;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t\t\n\t\tbool _PathOk(FileNode f) {\n\t\t\tf = f.Parent;\n\t\t\tfor (int j = i; j > 0 && f != null; f = f.Parent) {\n\t\t\t\tint k = path.LastIndexOf('\\\\', j - 1);\n\t\t\t\tif (!path.Eq((k + 1)..j, f.Name ?? \"\", true)) return false;\n\t\t\t\tj = k;\n\t\t\t}\n\t\t\treturn f == Root;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Generates new id for f, and adds both to the dictionary that is used by <see cref=\"FindById\"/> etc.\n\t/// Returns <i>id</i> or the generated id.\n\t/// </summary>\n\tpublic uint AddGetId(FileNode f) {\n\t\tuint id = (_idMap.Count > 0 ? _idMap.Keys.Max() : 0) + 1; //normally we don't reuse ids of deleted items\n\t\tif (id != 0) _idMap.Add(id, f);\n\t\telse while (!_idMap.TryAdd(++id, f)) { } //unlikely, but anyway. If new item created every 8 s, we have 1000 years.\n\t\treturn id;\n\t}\n\t\n\t/// <summary>\n\t/// Finds file or folder by its <see cref=\"FileNode.Id\"/>.\n\t/// Returns null if id is 0 or not found.\n\t/// id can contain <see cref=\"WorkspaceSN\"/> in high-order int.\n\t/// </summary>\n\tpublic FileNode FindById(long id) {\n\t\tint idc = (int)(id >> 32); if (idc != 0 && idc != WorkspaceSN) return null;\n\t\tuint idf = (uint)id;\n\t\tif (idf == 0) return null;\n\t\tif (_idMap.TryGetValue(idf, out var f)) {\n\t\t\tDebug_.PrintIf(f == null, \"deleted: \" + idf);\n\t\t\treturn f;\n\t\t}\n\t\tDebug_.Print(\"id not found: \" + idf);\n\t\treturn null;\n\t}\n\t\n\t/// <summary>\n\t/// Finds file or folder by its <see cref=\"FileNode.IdString\"/> or <see cref=\"FileNode.IdStringWithWorkspace\"/>.\n\t/// </summary>\n\tpublic FileNode FindById(string id) {\n\t\tid.ToInt(out long n, id.Starts(':') ? 1 : 0);\n\t\treturn FindById(n);\n\t}\n\t\n\t/// <summary>\n\t/// Finds all files (and not folders) with the specified name.\n\t/// Returns empty array if not found.\n\t/// </summary>\n\t/// <param name=\"name\">File name, like \"name.cs\". If starts with backslash, works like <see cref=\"Find\"/>. Does not support <see cref=\"FileNode.IdStringWithWorkspace\"/> string and filename without extension.</param>\n\tpublic FileNode[] FindAllFiles(string name) {\n\t\treturn Root.FindAllDescendantFiles(name);\n\t}\n\t\n\t/// <summary>\n\t/// Finds class file \"global.cs\".\n\t/// If multiple exist, ignores external files and prefers \"\\Classes\\global.cs\".\n\t/// </summary>\n\t/// <param name=\"silent\">Don't print warnings when not found or found multiple.</param>\n\t/// <returns>null if not found or if found multiple and could not pick the best.</returns>\n\tpublic FileNode FindGlobalCs(bool silent = false) {\n\t\tFoundMultiple = null;\n\t\tif (_nameMap.MultiGet_(\"global.cs\", out FileNode g, out var a)) {\n\t\t\tif (g != null) {\n\t\t\t\tif (!g.IsClass) g = null;\n\t\t\t} else {\n\t\t\t\tfor (int i = a.Count; --i >= 0;) if (!a[i].IsClass) a.RemoveAt(i);\n\t\t\t\tif (a.Count == 1) g = a[0];\n\t\t\t\telse if (a.Count > 0) {\n\t\t\t\t\tforeach (var v in a) if (v.Parent.Parent == Root && v.Parent.Name.Eqi(\"Classes\")) { g = v; break; } //prefer default location\n\t\t\t\t\tif (g is null) { //find single non-external\n\t\t\t\t\t\tforeach (var v in a) {\n\t\t\t\t\t\t\tif (v.IsExternal) continue;\n\t\t\t\t\t\t\tif (g is null) g = v; else { g = null; break; }\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (g is null) FoundMultiple = a;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (g != null) {\n\t\t\t_noGlobalCsWarning = false;\n\t\t\treturn g;\n\t\t}\n\t\tif (!silent) {\n\t\t\tif (FoundMultiple != null) {\n\t\t\t\tprint.warning(@\"Cannot use class file 'global.cs', because multiple exist, and none of them is \\Classes\\global.cs (default location).\", -1);\n\t\t\t} else if (!_noGlobalCsWarning) {\n\t\t\t\t_noGlobalCsWarning = true;\n\t\t\t\tPanels.Output.Scintilla.AaTags.AddLinkTag(\"+restoreGlobal\", _ => App.Model.AddMissingDefaultFiles(globalCs: true));\n\t\t\t\tprint.warning(\"Missing class file \\\"global.cs\\\". <+restoreGlobal>Create<>.\", -1, \"<>\");\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\tbool _noGlobalCsWarning;\n\t\n\t#endregion\n\t\n\t#region open/close, select, current, selected, context menu\n\t\n\t/// <summary>\n\t/// Returns true if f is null or isn't in this workspace or is deleted.\n\t/// </summary>\n\tpublic bool IsAlien(FileNode f) => f?.Model != this || f.IsDeleted;\n\t\n\t/// <summary>\n\t/// Closes f if open.\n\t/// Saves text if need, removes from OpenItems, deselects in treeview.\n\t/// </summary>\n\t/// <param name=\"f\">Can be any item or null. Does nothing if it is null, folder or not open.</param>\n\t/// <param name=\"activateOther\">When closing current file, if there are more open files, activate another open file.</param>\n\t/// <param name=\"selectOther\">Select the activated file.</param>\n\tpublic bool CloseFile(FileNode f, bool activateOther = true, bool selectOther = false) {\n\t\tif (IsAlien(f)) return false;\n\t\tif (!_openFiles.Remove(f)) return false;\n\t\t\n\t\tbool isCurrent = f == _currentFile;\n\t\tbool setFocus = isCurrent && activateOther && f.OpenDoc.IsFocused;\n\t\t\n\t\tPanels.Editor.Close(f);\n\t\tf.IsSelected = false;\n\t\t\n\t\t//_StateCleanup();\n\t\tState.Cleanup();\n\t\t\n\t\tif (isCurrent) {\n\t\t\tif (activateOther) {\n\t\t\t\tif (_openFiles.Count > 0) {\n\t\t\t\t\tvar ff = _openFiles[0];\n\t\t\t\t\tif (selectOther) ff.SelectSingle();\n\t\t\t\t\tif (_SetCurrentFile(ff, focusEditor: setFocus)) return true;\n\t\t\t\t} else if (setFocus) {\n\t\t\t\t\tApp.Wmain.Focus(); //else would not work any hotkeys and even Alt+menu keys until something focused\n\t\t\t\t}\n\t\t\t}\n\t\t\t_currentFile = null;\n\t\t}\n\t\tf.UpdateControlRow();\n\t\t\n\t\t_UpdateOpenFiles(_currentFile);\n\t\tSave.StateLater();\n\t\t\n\t\treturn true;\n\t}\n\t\n\t/// <summary>\n\t/// Closes specified files that are open.\n\t/// </summary>\n\t/// <param name=\"files\">Any <b>IEnumerable</b> except <b>OpenFiles</b>.</param>\n\t/// <param name=\"dontClose\">null or <b>FileNode</b> or <b>BitArray</b> to not close.</param>\n\tpublic void CloseFiles(IEnumerable<FileNode> files, object dontClose = null) {\n\t\tif (files == _openFiles) files = _openFiles.ToArray();\n\t\tbool closeCurrent = false;\n\t\tint i = 0;\n\t\tforeach (var f in files) {\n\t\t\tif (dontClose is System.Collections.BitArray ba) {\n\t\t\t\tif (i < ba.Length && ba[i++]) continue;\n\t\t\t} else if (f == dontClose) continue;\n\t\t\tif (f == _currentFile) closeCurrent = true; else CloseFile(f, activateOther: false);\n\t\t}\n\t\tif (closeCurrent) CloseFile(_currentFile);\n\t}\n\t\n\t/// <summary>\n\t/// Closes the file. If folder, closes/collapses its descendants.\n\t/// </summary>\n\tpublic void CloseFiles(FileNode f) {\n\t\tif (f.IsFolder) {\n\t\t\tCloseFiles(f.Descendants());\n\t\t\tCollapseAll(folder: f);\n\t\t\t//note: does not collapse the folder. Let the user see what happened.\n\t\t} else CloseFile(f);\n\t}\n\t\n\t/// <summary>\n\t/// Updates PanelOpen, enables/disables Previous command.\n\t/// </summary>\n\tvoid _UpdateOpenFiles(FileNode current) {\n\t\tPanels.Open.UpdateList();\n\t\tApp.Commands[nameof(Menus.Edit.Navigate.Previous_document)].Enable(_openFiles.Count > 1);\n\t}\n\t\n\t/// <summary>\n\t/// Called by <see cref=\"LoadWorkspace\"/> before opening another workspace and disposing this.\n\t/// Saves all, raises <see cref=\"UnloadingThisWorkspace\"/> event, closes documents, sets _currentFile = null.\n\t/// </summary>\n\tinternal void UnloadingWorkspace_() {\n\t\tSave.AllNowIfNeed();\n\t\tEditorExtension.ClosingWorkspace_(onExit: false);\n\t\tUnloadingThisWorkspace?.Invoke(); //closes dialogs that contain workspace-specific data, eg Properties\n\t\tUnloadingAnyWorkspace?.Invoke();\n\t\t_currentFile = null;\n\t\tPanels.Editor.CloseAll_(saveTextIfNeed: false);\n\t\t_openFiles.Clear();\n\t\t_UpdateOpenFiles(null);\n\t}\n\t\n\t/// <summary>\n\t/// Note: unsubscribe to avoid memory leaks.\n\t/// </summary>\n\tpublic event Action UnloadingThisWorkspace;\n\tpublic static event Action UnloadingAnyWorkspace;\n\t\n\t//rejected. Let unsubscribe in OnClosed: App.Model.UnloadingWorkspaceEvent -= Close;\n\t///// <summary>\n\t///// Closes window <i>w</i> when unloading workspace.\n\t///// </summary>\n\t//public void UnloadingWorkspaceCloseWindow(Window w) {\n\t//\tAction aClose = w.Close;\n\t//\tUnloadingWorkspaceEvent += aClose;\n\t//\tEventHandler closed = null;\n\t//\tclosed = (_, _) => { UnloadingWorkspaceEvent -= aClose; w.Closed -= closed; };\n\t//\tw.Closed += closed;\n\t//}\n\t\n\t/// <summary>\n\t/// Files that are displayed in the Open panel.\n\t/// Some of them are open in editor (see <see cref=\"PanelEdit.OpenDocs\"/>, <see cref=\"FileNode.OpenDoc\"/>), others just were open when closing this workspace the last time.\n\t/// </summary>\n\tpublic IReadOnlyList<FileNode> OpenFiles => _openFiles;\n\tList<FileNode> _openFiles = new();\n\t\n\t/// <summary>\n\t/// Gets the current file. It is open/active in the code editor.\n\t/// </summary>\n\tpublic FileNode CurrentFile => _currentFile;\n\tFileNode _currentFile;\n\t\n\t/// <summary>\n\t/// Selects the node. If not folder, opens its file in the code editor.\n\t/// Returns false if failed to open, for example if <i>f</i> is a folder.\n\t/// </summary>\n\tpublic bool SetCurrentFile(FileNode f, bool dontChangeTreeSelection = false, bool newFile = false, bool? focusEditor = true, bool noTemplate = false) {\n\t\tif (IsAlien(f)) return false;\n\t\tif (!dontChangeTreeSelection) f.SelectSingle();\n\t\tif (_currentFile != f) _SetCurrentFile(f, newFile, focusEditor, noTemplate);\n\t\treturn _currentFile == f;\n\t}\n\t\n\t/// <summary>\n\t/// If f!=_currentFile and not folder:\n\t/// - Opens it in editor, adds to OpenFiles, sets _currentFile, saves state later, updates UI.\n\t/// - Saves and hides current document.\n\t/// Returns false if fails to read file or if f is folder.\n\t/// </summary>\n\t/// <param name=\"f\"></param>\n\t/// <param name=\"newFile\">Should be true if opening the file first time after creating.</param>\n\t/// <param name=\"focusEditor\">If null, focus later, when mouse enters editor. Ignored if editor was focused (sets focus). Also depends on <i>newFile</i>.</param>\n\tbool _SetCurrentFile(FileNode f, bool newFile = false, bool? focusEditor = true, bool noTemplate = false) {\n\t\tDebug.Assert(!IsAlien(f));\n\t\tif (f == _currentFile) return true;\n\t\t//print.it(f);\n\t\tif (f.IsFolder) return false;\n\t\t\n\t\tCodeInfo.WaitUntilReadyForStyling();\n\t\t\n\t\tif (_currentFile != null) Save.TextNowIfNeed();\n\t\t\n\t\tvar fPrev = _currentFile;\n\t\t_currentFile = f;\n\t\t\n\t\tif (!Panels.Editor.Open(f, newFile, focusEditor, noTemplate)) {\n\t\t\t_currentFile = fPrev;\n\t\t\tif (_openFiles.Contains(f)) _UpdateOpenFiles(_currentFile); //?\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\tfPrev?.UpdateControlRow();\n\t\t_currentFile.UpdateControlRow();\n\t\t\n\t\t_openFiles.Remove(f);\n\t\t_openFiles.Insert(0, f);\n\t\t_UpdateOpenFiles(f);\n\t\tSave.StateLater();\n\t\t\n\t\treturn true;\n\t}\n\t\n\tvoid _ItemRightClicked(FileNode f) { //Dispatcher.InvokeAsync\n\t\tif (IsAlien(f)) return;\n\t\tif (!f.IsSelected) f.SelectSingle();\n\t\t_ContextMenu();\n\t}\n\t\n\tvoid _ContextMenu() {\n\t\tif (s_inContextMenu) return; //workaround for: sometimes, when dying mouse generates >1 rclick, somehow the menu is at screen 0 0\n\t\tif (s_contextMenu == null) {\n\t\t\tvar m = s_contextMenu = new ContextMenu { PlacementTarget = TreeControl };\n\t\t\t//m.ItemsSource = App.Commands[nameof(Menus.File)].MenuItem.Items; //shows menu but then closes on mouse over\n\t\t\tApp.Commands[nameof(Menus.File)].CopyToMenu(m);\n\t\t\t\n\t\t\t//unset s_inContextMenu when the menu closed and the command ended executing (if can show a modal dialog that closes the menu)\n\t\t\tm.Closed += static (_, _) => s_inContextMenu = false;\n\t\t\tApp.Commands.ExecutingStartedEnded += started => s_inContextMenuCommand = started ? s_inContextMenu : false;\n\t\t}\n\t\ts_contextMenu.IsOpen = true;\n\t\ts_inContextMenu = s_contextMenu.IsOpen;\n\t}\n\tstatic ContextMenu s_contextMenu;\n\tstatic bool s_inContextMenu, s_inContextMenuCommand;\n\t\n\t/// <summary>\n\t/// Gets the place where item should be added in operations such as new, paste, import.\n\t/// If s_inContextMenuCommand or atSelection (true when pasting), uses the focused item. Else top.\n\t/// </summary>\n\tFNInsertPos _GetInsertPos(bool atSelection = false) {\n\t\tif ((atSelection || s_inContextMenuCommand) && TreeControl.FocusedItem is FileNode target) {\n\t\t\tvar pos = FNInsert.Before;\n\t\t\tbool isFolder = target.IsFolder && target.IsSelected && TreeControl.SelectedIndices.Count == 1;\n\t\t\tif (isFolder && !target.HasChildren) pos = FNInsert.Last;\n\t\t\telse if (target.Next == null) pos = FNInsert.After; //usually users want to add after the last, not before\n\t\t\t\n\t\t\treturn new(target, pos);\n\t\t} else { //top\n\t\t\treturn new(Root, FNInsert.First);\n\t\t}\n\t}\n\t\n\t//Called when editor control focused, etc.\n\tpublic void EnsureCurrentSelected() {\n\t\t//if(_currentFile != null && !_currentFile.IsSelected && _control.SelectedIndices.Count < 2) _currentFile.SelectSingle();\n\t\tif (_currentFile != null && !_currentFile.IsSelected) _currentFile.SelectSingle();\n\t}\n\t\n\t/// <summary>\n\t/// Selects the node, opens its file in the code editor, optionally goes to the specified position or line or line/column.\n\t/// Returns false if failed to open, for example if it is a folder (then just selects folder).\n\t/// </summary>\n\t/// <param name=\"f\"></param>\n\t/// <param name=\"line\">If not negative, goes to this 0-based line.</param>\n\t/// <param name=\"columnOrPos\">If not negative, goes to this 0-based position in text (if line negative) or to this 0-based column in line.</param>\n\t/// <param name=\"findText\">If not null, finds this text (<b>FindWord</b>), and goes there if found. Then <i>line</i> and <i>columnPos</i> not used.</param>\n\t/// <param name=\"activateLA\">Always activate the LA window. If false - only if wasn't visible.</param>\n\tpublic bool OpenAndGoTo(FileNode f, int line = -1, int columnOrPos = -1, string findText = null, bool activateLA = true) {\n\t\tif (activateLA || !App.Wmain.IsLoaded || !App.Wmain.IsVisible) App.Wmain.AaShowAndActivate();\n\t\tbool wasOpen = _currentFile == f;\n\t\tif (!SetCurrentFile(f)) return false;\n\t\tvar doc = Panels.Editor.ActiveDoc;\n\t\tif (findText != null) {\n\t\t\tline = -1;\n\t\t\tcolumnOrPos = doc.aaaText.FindWord(findText);\n\t\t}\n\t\tif (line >= 0 || columnOrPos >= 0) {\n\t\t\tif (line >= 0) {\n\t\t\t\tint i = doc.aaaLineStart(true, line);\n\t\t\t\tif (columnOrPos > 0) i = Math.Min(i + columnOrPos, doc.aaaLen16); //not SCI_FINDCOLUMN, it calculates tabs\n\t\t\t\tcolumnOrPos = i;\n\t\t\t}\n\t\t\tif (!wasOpen) wait.doEvents(); //else scrolling does not work well if now opened the file. Can't async, because caller may use the new pos immediately.\n\t\t\tdoc.aaaGoToPos(true, columnOrPos);\n\t\t} else {\n\t\t\tif (!wasOpen) wait.doEvents(); //caller then may call aaaGoToPos or aaaSelect etc\n\t\t}\n\t\tdoc.Focus();\n\t\treturn true;\n\t}\n\t\n\t/// <summary>\n\t/// Finds file and calls <see cref=\"OpenAndGoTo(FileNode, int, int, string, bool)\"/>. Does nothing if not found. Note: default kind is CodeFile.\n\t/// </summary>\n\tpublic bool OpenAndGoTo(string file, int line = -1, int columnOrPos = -1, string findText = null, bool activateLA = true, FNFind kind = FNFind.CodeFile) {\n\t\tvar f = Find(file, kind); if (f == null) return false;\n\t\treturn OpenAndGoTo(f, line, columnOrPos, findText, activateLA);\n\t}\n\t\n\t/// <summary>\n\t/// Finds file or folder and selects the node. If not folder, opens its file in the code editor, optionally goes to the specified position or line or line/column.\n\t/// Returns false if failed to find or select.\n\t/// </summary>\n\t/// <param name=\"fileOrFolder\">See <see cref=\"Find\"/>.</param>\n\t/// <param name=\"line1Based\">If not empty, goes to this 1-based line. Or can be \"expand\" to find and expand a folder.</param>\n\t/// <param name=\"column1BasedOrPos\">If not empty, goes to this 0-based position in text (if line empty) or to this 1-based column in line.</param>\n\t/// <param name=\"activateLA\"></param>\n\t/// <remarks>\n\t/// If column1BasedOrPos or line1Based not empty, searches only files, not folders.\n\t/// </remarks>\n\tpublic bool OpenAndGoTo2(string fileOrFolder, string line1Based = null, string column1BasedOrPos = null, bool activateLA = true) {\n\t\tbool expand = line1Based == \"expand\"; if (expand) line1Based = \"\";\n\t\tvar f = Find(fileOrFolder, expand ? FNFind.Folder : line1Based.NE() && column1BasedOrPos.NE() ? FNFind.Any : FNFind.CodeFile);\n\t\tif (f == null) return false;\n\t\tif (f.IsFolder) {\n\t\t\tf.SelectSingle();\n\t\t\tif (expand) TreeControl.Expand(f, true);\n\t\t\treturn true;\n\t\t}\n\t\tint line = line1Based.NE() ? -1 : line1Based.ToInt() - 1;\n\t\tint columnOrPos = -1; if (!column1BasedOrPos.NE()) columnOrPos = column1BasedOrPos.ToInt() - (line < 0 ? 0 : 1);\n\t\treturn OpenAndGoTo(f, line, columnOrPos, activateLA: activateLA);\n\t}\n\t\n\t/// <summary>\n\t/// Finds code file, selects the node, opens in the code editor, searches for the specified text. If finds, goes there.\n\t/// Returns false if failed to find file or select.\n\t/// </summary>\n\t/// <param name=\"fileOrFolder\">See <see cref=\"Find\"/>.</param>\n\t/// <param name=\"findText\"></param>\n\t/// <param name=\"activateLA\"></param>\n\tpublic bool OpenAndGoTo3(string fileOrFolder, string findText, bool activateLA = true) {\n\t\tvar f = FindCodeFile(fileOrFolder);\n\t\tif (f == null) return false;\n\t\treturn OpenAndGoTo(f, findText: findText, activateLA: activateLA);\n\t}\n\t\n\t#endregion\n\t\n\t#region rename, delete, open/close (menu commands), properties\n\t\n\tpublic void RenameSelected(bool newFile = false) {\n\t\tPanels.PanelManager[Panels.Files.P].Visible = true; //exception if not visible\n\t\tTreeControl.EditLabel(ended: newFile ? ok => { if (ok && Keyboard.IsKeyDown(Key.Enter)) Panels.Editor.ActiveDoc?.Focus(); } : null);\n\t}\n\t\n\tpublic void RenameNewProject(FileNode folder, FileNode main) {\n\t\tPanels.PanelManager[Panels.Files.P].Visible = true; //exception if not visible\n\t\tfolder.SelectSingle();\n\t\tTreeControl.EditLabel(ended: ok => {\n\t\t\tif (ok && Keyboard.IsKeyDown(Key.Enter)) {\n\t\t\t\tmain.SelectSingle();\n\t\t\t\tRenameSelected(newFile: true);\n\t\t\t}\n\t\t});\n\t}\n\t\n\tpublic void DeleteSelected() {\n\t\tvar a = TreeControl.SelectedItems; if (a.Length < 1) return;\n\t\t\n\t\t//confirmation\n\t\tvar text = string.Join(\"\\n\", a.Select(f => f.Name));\n\t\tbool hasLinks = a.Any(o => o.Descendants(andSelf: true).Any(u => u.IsLink));\n\t\tbool hasNonlinks = a.Any(o => !o.IsLink);\n\t\tvar expandedText = (hasLinks, hasNonlinks) switch {\n\t\t\t(true, true) => \"Files will be deleted. Will use the Recycle Bin, if possible.\\nLink targets will NOT be deleted.\",\n\t\t\t(true, false) => \"The link will be deleted. Its target will NOT be deleted.\",\n\t\t\t(false, true) => \"The file will be deleted. Will use the Recycle Bin, if possible.\",\n\t\t\t_ => null\n\t\t};\n\t\tvar r = dialog.show(\"Deleting\", text, \"1 OK|0 Cancel\", owner: TreeControl, expandedText: expandedText);\n\t\tif (r == 0) return;\n\t\t\n\t\tforeach (var f in a) {\n\t\t\tif (f.IsDeleted) continue; //deleted together with the parent folder\n\t\t\t_Delete(f); //info: and saves everything, now and/or later\n\t\t}\n\t\t\n\t\tSave.WorkspaceAsync();\n\t\tCodeInfo.FilesChanged();\n\t}\n\t\n\t/// <param name=\"syncing\">0 - normal; 1 - syncing all a startup; 2 - filesystem watchers detected a deleted file or folder.</param>\n\tbool _Delete(FileNode f, bool recycleBin = true, int syncing = 0) {\n\t\tvar e = f.Descendants(true);\n\t\t\n\t\tCloseFiles(e);\n\t\tUncut();\n\t\t\n\t\tif (syncing == 0) {\n\t\t\tif (f.IsLink) {\n\t\t\t\tprint.it($\"<>Info: The deleted item was a link to <explore>{f.FilePath}<>\");\n\t\t\t} else {\n\t\t\t\tif (!TryFileOperation(FOSync.UserFileOp, () => filesystem.delete(f.FilePath, recycleBin ? FDFlags.RecycleBin : 0))) return false;\n\t\t\t\t//CONSIDER: add all paths to List, and delete finally in single call.\n\t\t\t\t//CONSIDER: move to folder '.deleted'. Moving to RB is very slow. No RB if in removable drive etc.\n\t\t\t}\n\t\t}\n\t\t\n\t\tEditGoBack.OnFileDeleted(e);\n\t\tPanels.Bookmarks.FileDeleted(e);\n\t\tPanels.Breakpoints.FileDeleted(e);\n\t\tforeach (var k in e) {\n\t\t\tif (!k.IsFolder) {\n\t\t\t\tState.EditorDelete(k);\n\t\t\t\tif (k.IsCodeFile) Compiler.Uncache(k);\n\t\t\t}\n\t\t\tif (k.IsLink && k.IsFolder) {\n\t\t\t\t//_rootLF?.Remove(k);\n\t\t\t\t_syncWatchers?.Remove(k);\n\t\t\t}\n\t\t\t\n\t\t\t_idMap[k.Id] = null;\n\t\t\t_nameMap.MultiRemove_(k.Name, k);\n\t\t\tk.IsDeleted = true;\n\t\t}\n\t\t\n\t\tf.Remove();\n\t\t\n\t\tif (syncing != 1) UpdateControlItems();\n\t\tif (syncing == 2) {\n\t\t\tSave.WorkspaceAsync();\n\t\t\tCodeInfo.FilesChanged();\n\t\t}\n\t\t\n\t\treturn true;\n\t}\n\t\n\t//TODO3: once (2 times) crashed when deleting folder \"@Triggers and toolbars\".\n\t//\tBoth times in editor was opened a class file from the folder.\n\t//\tFirst time: messagebox \"exception processing message, unexpected parameters\".\n\t//\t\tAfter restarting were deleted files and tree items. Just several warnings about not found file id.\n\t//\tSecond time with debugger: access violation exception somewhere in DispatchMessage -> COM message processing.\n\t//\t\tAfter restarting were deleted files but not tree items. Tried to reopen the file, but failed, and no editor control was created.\n\t//\tCOM wasn't used explicitly. Maybe because of Recycle Bin.\n\t//\tThen could not reproduce (after recompiling same code).\n\t\n\t/// <summary>\n\t/// Opens the selected item(s) in our editor or in default app or selects in Explorer.\n\t/// </summary>\n\t/// <param name=\"how\">1 open, 2 open in new window (not impl), 3 open in default app, 4 select in Explorer.</param>\n\tpublic void OpenSelected(int how) {\n\t\tvar a = TreeControl.SelectedItems; if (a.Length == 0) return;\n\t\t\n\t\tforeach (var f in a) {\n\t\t\tvar path = f.FilePath;\n\t\t\tif (how is 3 or 4) {\n\t\t\t\tvar e = filesystem.exists(path, useRawPath: true);\n\t\t\t\tif (!e) { print.it(f.IsFolder ? \"The folder does not exist\" : \"The file does not exist\"); continue; }\n\t\t\t}\n\t\t\t\n\t\t\tswitch (how) {\n\t\t\tcase 1:\n\t\t\t\tif (f.IsFolder) TreeControl.Expand(f, true);\n\t\t\t\telse SetCurrentFile(f);\n\t\t\t\tbreak;\n\t\t\t//case 2:\n\t\t\t//\tif(f.IsFolder) continue;\n\t\t\t//\t//FUTURE\n\t\t\t//\tbreak;\n\t\t\tcase 3:\n\t\t\t\trun.itSafe(path);\n\t\t\t\tbreak;\n\t\t\tcase 4:\n\t\t\t\trun.selectInExplorer(path);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Closes selected or all items, or collapses folders.\n\t/// Used to implement menu File > Open/Close.\n\t/// </summary>\n\tpublic void CloseEtc(ECloseCmd how, FileNode dontClose = null) {\n\t\tswitch (how) {\n\t\tcase ECloseCmd.CloseSelectedOrCurrent:\n\t\t\tvar a = TreeControl.SelectedItems;\n\t\t\tif (a.Length == 0) CloseFile(_currentFile);\n\t\t\telse if (a.Length == 1) CloseFiles(a[0]);\n\t\t\telse CloseFiles(a);\n\t\t\tbreak;\n\t\tcase ECloseCmd.CloseAll:\n\t\t\tCloseFiles(_openFiles, dontClose);\n\t\t\tCollapseAll();\n\t\t\tif (dontClose != null) TreeControl.EnsureVisible(dontClose);\n\t\t\tbreak;\n\t\tcase ECloseCmd.CollapseAllFolders:\n\t\t\tCollapseAll();\n\t\t\tbreak;\n\t\tcase ECloseCmd.CollapseInactiveFolders:\n\t\t\tCollapseAll(exceptWithOpenFiles: true);\n\t\t\tbreak;\n\t\t}\n\t}\n\t\n\tpublic void CollapseAll(bool exceptWithOpenFiles = false, FileNode folder = null) {\n\t\tbool update = false;\n\t\tfolder ??= Root;\n\t\tforeach (var v in folder.Descendants()) {\n\t\t\tif (v.IsExpanded) {\n\t\t\t\tif (exceptWithOpenFiles && v.Descendants().Any(o => _openFiles.Contains(o))) continue;\n\t\t\t\tupdate = true;\n\t\t\t\tv.SetIsExpanded(false);\n\t\t\t}\n\t\t}\n\t\tif (update) UpdateControlItems();\n\t}\n\t\n\tpublic enum ECloseCmd {\n\t\t/// <summary>\n\t\t/// Closes selected files. If there are no selected files, closes current file. Does not collapse selected folders.\n\t\t/// </summary>\n\t\tCloseSelectedOrCurrent,\n\t\tCloseAll,\n\t\tCollapseAllFolders,\n\t\tCollapseInactiveFolders,\n\t}\n\t\n\tpublic void Properties() {\n\t\tFileNode f = null;\n\t\tif (s_inContextMenuCommand) {\n\t\t\tvar a = TreeControl.SelectedItems;\n\t\t\tif (a.Length == 1) f = a[0];\n\t\t} else {\n\t\t\tEnsureCurrentSelected();\n\t\t\tf = _currentFile;\n\t\t}\n\t\tif (f == null) return;\n\t\tif (f.IsCodeFile) DProperties.ShowFor(f);\n\t\t//else if(f.IsFolder) DFolderProperties.ShowFor(f);\n\t\t//else DOtherFileProperties.ShowFor(f);\n\t}\n\t\n\t#endregion\n\t\n\t#region new item\n\t\n\t/// <summary>\n\t/// Creates new item.\n\t/// Opens file, or selects folder, or opens main file of project folder. Optionally begins renaming.\n\t/// Loads files.xml, finds template's element and calls <see cref=\"NewItemX\"/>; it calls <see cref=\"NewItemLX\"/>.\n\t/// </summary>\n\t/// <param name=\"template\">\n\t/// Relative path of a file or folder in the Templates\\files folder. Case-sensitive, as in workspace.\n\t/// Examples: \"File.cs\", \"File.txt\", \"Subfolder\", \"Subfolder\\File.cs\".\n\t/// Special names: null (creates folder), \"Script.cs\", \"Class.cs\".\n\t/// If folder and not null, adds descendants too; removes '!' from the start of template folder name.\n\t/// </param>\n\t/// <param name=\"ipos\">If null, adds at the context menu position or top.</param>\n\t/// <param name=\"name\">If not null, creates with this name (eg \"name.cs\"). Else gets name from template. In any case makes unique name.</param>\n\tpublic FileNode NewItem(string template, FNInsertPos? ipos = null, string name = null, bool beginRenaming = false, NewFileText text = null) {\n\t\tXElement x = null;\n\t\tif (template != null) {\n\t\t\tx = FileNode.Templates.LoadXml(template); if (x == null) return null;\n\t\t}\n\t\treturn NewItemX(x, ipos, name, beginRenaming, text);\n\t}\n\t\n\t/// <summary>\n\t/// Creates new item.\n\t/// Returns the new item, or null if fails.\n\t/// Does not open/select/startRenaming.\n\t/// </summary>\n\t/// <param name=\"template\">See <see cref=\"NewItem\"/>.</param>\n\t/// <param name=\"ipos\">If null, adds at the context menu position or top.</param>\n\t/// <param name=\"name\">If not null, creates with this name (eg \"name.cs\"). Else gets name from template. In any case makes unique name.</param>\n\tpublic FileNode NewItemL(string template, FNInsertPos? ipos = null, string name = null) {\n\t\tXElement x = null;\n\t\tif (template != null) {\n\t\t\tx = FileNode.Templates.LoadXml(template); if (x == null) return null;\n\t\t}\n\t\treturn NewItemLX(x, ipos, name);\n\t}\n\t\n\t/// <summary>\n\t/// Creates new item.\n\t/// Opens file, or selects folder, or opens main file of project folder. Optionally begins renaming.\n\t/// Calls <see cref=\"NewItemLX\"/>.\n\t/// <param name=\"template\">An XElement of files.xml of the Templates workspace. If null, creates folder.</param>\n\t/// <param name=\"ipos\">If null, adds at the context menu position or top.</param>\n\t/// <param name=\"name\">If not null, creates with this name (eg \"name.cs\"). Else gets name from template. In any case makes unique name.</param>\n\t/// </summary>\n\tpublic FileNode NewItemX(XElement template, FNInsertPos? ipos = null, string name = null, bool beginRenaming = false, NewFileText text = null) {\n\t\tstring s = null;\n\t\tif (text != null && text.replaceTemplate) {\n\t\t\ts = text.meta.NE() ? text.text : _MetaPlusText(text.text);\n\t\t\ttext = null;\n\t\t}\n\t\t\n\t\tvar f = NewItemLX(template, ipos, name, s);\n\t\tif (f == null) return null;\n\t\tvar f0 = f;\n\t\t\n\t\tif (beginRenaming && template != null && FileNode.Templates.IsInDefault(template)) beginRenaming = false;\n\t\t\n\t\tif (f.IsFolder) {\n\t\t\tif (f.IsProjectFolder(out var main) && main != null) SetCurrentFile(f = main, newFile: true); //open the main file of the new project folder\n\t\t\telse f.SelectSingle(); //select the new folder\n\t\t} else {\n\t\t\tSetCurrentFile(f, newFile: true, noTemplate: text?.replaceTemplate ?? false); //open the new file\n\t\t}\n\t\t\n\t\tif (text != null && f == CurrentFile) {\n\t\t\tDebug.Assert(f.IsScript);\n\t\t\tf.GetCurrentText(out s);\n\t\t\tvar me = MetaComments.FindMetaComments(s).end;\n\t\t\tif (!text.meta.NE()) {\n\t\t\t\tif (me == 0) s = _MetaPlusText(s); //never mind: should skip script doc comments at start. Rare and not important.\n\t\t\t\telse s = s.Insert(me - 3, (s[me - 4] == ' ' ? \"\" : \" \") + text.meta + \" \");\n\t\t\t}\n\t\t\tif (!text.text.NE()) {\n\t\t\t\tif (s.NE()) s = text.text;\n\t\t\t\telse if (s.RxMatch(@\"\\R\\R\", 0, out RXGroup g, range: me..)) s = s.Insert(g.End, text.text);\n\t\t\t\telse if (s.RxMatch(@\"\\R\\z\", 0, out g, range: me..)) s = s + \"\\r\\n\" + text.text;\n\t\t\t}\n\t\t\tPanels.Editor.ActiveDoc.aaaSetText(s);\n\t\t}\n\t\t\n\t\tif (beginRenaming && f.IsSelected) {\n\t\t\tif (f != f0) RenameNewProject(f0, f);\n\t\t\telse RenameSelected(newFile: !f.IsFolder);\n\t\t}\n\t\treturn f;\n\t\t\n\t\tstring _MetaPlusText(string t) => $\"/*/ {text.meta} /*/{(t.Starts(\"//.\") ? \" \" : \"\\r\\n\")}{t}\";\n\t}\n\t\n\t/// <summary>\n\t/// Creates new item.\n\t/// Returns the new item, or null if fails.\n\t/// Does not open/select/startRenaming.\n\t/// </summary>\n\t/// <param name=\"template\">An XElement of files.xml of the Templates workspace. If null, creates folder.</param>\n\t/// <param name=\"ipos\">If null, adds at the context menu position or top.</param>\n\t/// <param name=\"name\">If not null, creates with this name (eg \"name.cs\"). Else gets name from template. In any case makes unique name.</param>\n\t/// <param name=\"text\">If not null, sets this text. If null, sets default text (template etc). Not used for folders.</param>\n\tpublic FileNode NewItemLX(XElement template, FNInsertPos? ipos = null, string name = null, string text = null) {\n\t\tvar ip = ipos ?? _GetInsertPos();\n\t\t\n\t\t//create unique name\n\t\tbool isFolder = template == null || template.Name.LocalName == \"d\";\n\t\tif (name == null) {\n\t\t\tbool append1 = true;\n\t\t\tif (template == null) {\n\t\t\t\tname = \"Folder\";\n\t\t\t} else {\n\t\t\t\tname = template.Attr(\"n\");\n\t\t\t\tif (isFolder && name.Starts('!')) name = name[1..];\n\t\t\t\tappend1 = !FileNode.Templates.IsInDefault(template);\n\t\t\t}\n\t\t\t//let unique names start from 1\n\t\t\tif (append1) {\n\t\t\t\tint i;\n\t\t\t\tif (!isFolder && (i = name.LastIndexOf('.')) > 0) name = name.Insert(i, \"1\"); else name += \"1\";\n\t\t\t}\n\t\t}\n\t\tname = FileNode.CreateNameUniqueInFolder(ip.ParentFolder, name, isFolder, autoGenerated: true);\n\t\t\n\t\treturn _NewItem(ip, template, name, text);\n\t}\n\t\n\tFileNode _NewItem(FNInsertPos ipos, XElement template, string name, string text) {\n\t\tvar fileType = template == null ? FNType.Folder : FileNode.XmlTagToFileType(template.Name.LocalName, canThrow: false);\n\t\tDebug.Assert(fileType is not (FNType.Script or FNType.Class) || name.Ends(\".cs\"));\n\t\t\n\t\tif (text == null && fileType != FNType.Folder) {\n\t\t\tstring relPath = template.Attr(\"n\");\n\t\t\tfor (var p = template; (p = p.Parent).Name.LocalName != \"files\";) relPath = p.Attr(\"n\") + \"\\\\\" + relPath;\n\t\t\tif (fileType == FNType.Other) {\n\t\t\t\ttext = filesystem.loadText(FileNode.Templates.DefaultDirBS + relPath);\n\t\t\t} else if (FileNode.Templates.IsStandardTemplateName(relPath, out var tt)) {\n\t\t\t\ttext = FileNode.Templates.Load(tt);\n\t\t\t\t//if (tt == FileNode.ETempl.Script) text = text.RxReplace(@\"\\bScript\\s*\\{\", \"Script {\", 1); //no. The user will see warning when compiling, and let update custom template.\n\t\t\t} else {\n\t\t\t\ttext = filesystem.loadText(FileNode.Templates.DefaultDirBS + relPath);\n\t\t\t\tif (text.Length < 20 && text.Starts(\"//#\")) { //load default or custom template?\n\t\t\t\t\ttt = text switch { \"//#script\" => FileNode.ETempl.Script, \"//#class\" => FileNode.ETempl.Class, _ => 0 };\n\t\t\t\t\tif (tt != 0) text = FileNode.Templates.Load(tt);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tFileNode parent = ipos.ParentFolder;\n\t\tvar path = parent.FilePath + \"\\\\\" + name;\n\t\tif (!TryFileOperation(FOSync.UserFileOp, () => {\n\t\t\tif (fileType == FNType.Folder) filesystem.createDirectory(path);\n\t\t\telse filesystem.saveText(path, text);\n\t\t})) return null;\n\t\t\n\t\tvar f = new FileNode(this, name, fileType);\n\t\tf.Common_MoveCopyNew(ipos, false);\n\t\t\n\t\tif (template != null) {\n\t\t\tif (template.Attr(out string icon, \"icon\")) f.CustomIconName = icon;\n\t\t\t\n\t\t\tif (fileType == FNType.Folder) {\n\t\t\t\tforeach (var x in template.Elements()) {\n\t\t\t\t\t_NewItem(new(f, FNInsert.Last), x, x.Attr(\"n\"), null);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\treturn f;\n\t}\n\t\n\t#endregion\n\t\n\t#region clipboard\n\t\n\tstruct _Clipboard {\n\t\tpublic FileNode[] items;\n\t\tpublic bool cut;\n\t\tpublic uint clipSN;\n\t\t\n\t\tpublic bool IsCut(FileNode f) => cut && items.Contains(f);\n\t}\n\t_Clipboard _clipboard;\n\t\n\tpublic void CutCopySelected(bool cut) {\n\t\tUncut();\n\t\tvar a = TreeControl.SelectedItems; if (a.NE_()) return;\n\t\t_clipboard = new _Clipboard { items = a, cut = cut };\n\t\tif (cut) {\n\t\t\t//we don't support cut to outside this workspace. Much work, rarely used, can copy/delete.\n\t\t\t//\tThe same with copy/paste between workspaces.\n\t\t\tclipboard.clear();\n\t\t\tTreeControl.Redraw();\n\t\t} else {\n\t\t\tvar d = new clipboardData();\n\t\t\td.AddFiles(a.Select(o => o.FilePath).ToArray());\n\t\t\td.AddText(string.Join(\"\\r\\n\", a.Select(o => o.Name)));\n\t\t\td.SetClipboard();\n\t\t}\n\t\t_clipboard.clipSN = Api.GetClipboardSequenceNumber();\n\t}\n\t\n\tpublic void Paste() {\n\t\tif (_clipboard.items != null && _clipboard.clipSN != Api.GetClipboardSequenceNumber()) Uncut();\n\t\tvar ipos = _GetInsertPos(atSelection: true);\n\t\tif (_clipboard.items != null) {\n\t\t\t_MultiCopyMove(!_clipboard.cut, _clipboard.items, ipos);\n\t\t\tUncut();\n\t\t} else {\n\t\t\tusing (new clipboard.OpenClipboard_(false)) {\n\t\t\t\tvar h = Api.GetClipboardData(Api.CF_HDROP);\n\t\t\t\tif (h != default) {\n\t\t\t\t\tvar a = clipboardData.HdropToFiles_(h);\n\t\t\t\t\t_DroppedOrPasted(null, a, ipos, false);\n\t\t\t\t} else if (clipboardData.GetText_(0) is string s && s.Length > 0) {\n\t\t\t\t\tSciCode.EIsForumCode_(s, newFile: true);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t\n\tpublic void Uncut() {\n\t\tbool cut = _clipboard.cut;\n\t\tif (!cut && _clipboard.items != null && _clipboard.clipSN == Api.GetClipboardSequenceNumber()) clipboard.clear();\n\t\t_clipboard = default;\n\t\tif (cut) TreeControl.Redraw();\n\t}\n\t\n\tpublic bool IsCut(FileNode f) => _clipboard.IsCut(f);\n\t\n\tpublic void SelectedCopyPath(bool full) {\n\t\tvar a = TreeControl.SelectedItems; if (a.Length == 0) return;\n\t\tclipboard.text = string.Join(\"\\r\\n\", a.Select(f => full ? f.FilePath : f.ItemPath));\n\t}\n\t\n\t#endregion\n\t\n\t#region import, move, copy\n\t\n\tvoid _MultiCopyMove(bool copy, FileNode[] a, FNInsertPos ipos) {\n\t\tbool copyLinkTarget = copy && a.Any(o => o.IsLink) && 2 == popupMenu.showSimple(\"1 Copy link|2 Copy link target\", owner: TreeControl);\n\t\tif (copy) TreeControl.UnselectAll();\n\t\ttry {\n\t\t\tbool movedCurrentFile = false;\n\t\t\tvar a2 = new List<FileNode>(a.Length);\n\t\t\t//foreach (var f in (ipos.pos == FNInsert.After) ? a.Reverse() : a) { //broken in .NET 9\n\t\t\tforeach (var f in (ipos.pos == FNInsert.After) ? Enumerable.Reverse(a) : a) {\n\t\t\t\tif (!this.IsMyFileNode(f)) continue; //deleted?\n\t\t\t\tif (a.Contains(f.Parent)) continue;\n\t\t\t\tif (copy) {\n\t\t\t\t\tvar fCopied = f._FileCopy(ipos, this, copyLinkTarget);\n\t\t\t\t\tif (fCopied != null) a2.Add(fCopied);\n\t\t\t\t} else {\n\t\t\t\t\tif (!f.FileMove(ipos)) continue;\n\t\t\t\t\tif (!movedCurrentFile && _currentFile != null) {\n\t\t\t\t\t\tif (f == _currentFile || (f.IsFolder && _currentFile.IsDescendantOf(f))) movedCurrentFile = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (movedCurrentFile) TreeControl.EnsureVisible(_currentFile);\n\t\t\tif (copy && !(ipos.Inside && !ipos.f.IsExpanded)) {\n\t\t\t\tbool focus = true;\n\t\t\t\tforeach (var f in a2) {\n\t\t\t\t\tf.IsSelected = true;\n\t\t\t\t\tif (focus) { focus = false; TreeControl.SetFocusedItem(f); }\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcatch (Exception e1) { print.it(e1); }\n\t\t\n\t\t//info: don't need to schedule saving here. FileCopy and FileMove did it.\n\t}\n\t\n\tvoid _DroppedOrPasted(FileNode[] nodes, string[] files, FNInsertPos ipos, bool copy) {\n\t\tif (nodes != null) {\n\t\t\t_MultiCopyMove(copy, nodes, ipos);\n\t\t} else {\n\t\t\tif (ipos.f == null) ipos = new(Root, FNInsert.Last);\n\t\t\tif (files.Length == 1 && IsWorkspaceDirectoryOrZip_ShowDialogOpenImport(files[0], out int dialogResult)) {\n\t\t\t\tswitch (dialogResult) {\n\t\t\t\tcase 1: timer.after(1, _ => LoadWorkspace(files[0])); break;\n\t\t\t\tcase 2: ImportWorkspace(files[0], ipos); break;\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tImportFiles(files, ipos, copy ? ImportFlags.Copy : 0);\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Imports one or more files or folders into the workspace. Shows dialog to select.\n\t/// </summary>\n\tpublic void ImportFiles(bool folder) {\n\t\tvar d = new FileOpenSaveDialog(\"{4D1F3AFB-DA1A-45AC-8C12-41DDA5C51CDA}\") { Title = folder ? \"Import folder\" : \"Import files\" };\n\t\tif (d.ShowOpen(out string[] a, owner: TreeControl, selectFolder: folder))\n\t\t\tImportFiles(a);\n\t}\n\t\n\t/// <summary>\n\t/// Imports one or more files or folders into the workspace.\n\t/// </summary>\n\tpublic void ImportFiles(string[] a, ImportFlags flags = 0) {\n\t\tImportFiles(a, _GetInsertPos(), flags);\n\t}\n\t\n\t/// <summary>\n\t/// Imports one or more files or/and folders.\n\t/// </summary>\n\t/// <param name=\"a\"></param>\n\t/// <param name=\"ipos\"></param>\n\t/// <param name=\"flags\"></param>\n\t/// <returns>The first imported item, or null if failed.</returns>\n\tpublic FileNode ImportFiles(string[] a, FNInsertPos ipos, ImportFlags flags = 0) {\n\t\tFileNode fReturn = null;\n\t\ttry {\n\t\t\ta = a.Select(s => filesystem.more.getFinalPath(s, out s, format: FPFormat.PrefixNever) ? s : null).OfType<string>().ToArray();\n\t\t\tif (a.Length == 0) return null;\n\t\t\tvar newParent = ipos.ParentFolder;\n\t\t\t\n\t\t\t//need to detect files coming from the workspace. Get path of the workspace files dir and target paths of all link folders.\n\t\t\tvar wsDirs = Root.Descendants()\n\t\t\t\t.Where(o => o.IsLink && o.IsFolder)\n\t\t\t\t.Select(o => filesystem.more.getFinalPath(o.LinkTarget, out var s, format: FPFormat.PrefixNever) ? s : null)\n\t\t\t\t.OfType<string>() //where not null\n\t\t\t\t.Prepend(FilesDirectory)\n\t\t\t\t.ToArray();\n\t\t\t\n\t\t\tvar action = flags & (ImportFlags.Copy | ImportFlags.Move | ImportFlags.Link);\n\t\t\tbool dontPrint = flags.Has(ImportFlags.DontPrint);\n\t\t\tint fromWorkspaceDir = 0;\n\t\t\tbool isLinkToWsFile = false;\n\t\t\t\n\t\t\tfor (int i = 0; i < a.Length; i++) {\n\t\t\t\tvar s = a[i];\n\t\t\t\tif (s.Find(@\"\\$RECYCLE.BIN\\\", true) > 0) {\n\t\t\t\t\tprint.it($\"<>Cannot import files directly from Recycle Bin.\");\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\tif (wsDirs.FirstOrDefault(o => o.PathStarts(s, orEquals: true)) is { } s2) {\n\t\t\t\t\tprint.it($\"<>Cannot import. The folder {(s2.Eqi(s) ? \"already is\" : \"contains a folder that is\")} in the workspace. {FindByFilePath(s2)?.SciLink(true)}\");\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\tif (wsDirs.Any(o => s.PathStarts(o))) {\n\t\t\t\t\tif (action != 0) return null; //unlikely\n\t\t\t\t\tvar f1 = FindByFilePath(s);\n\t\t\t\t\tif (f1 != null) {\n\t\t\t\t\t\tvar sff = f1.IsFolder ? \"folder\" : \"file\";\n\t\t\t\t\t\tif (a.Length > 1) {\n\t\t\t\t\t\t\tprint.it($\"<>Cannot import. The {sff} already is in the workspace. {f1.SciLink(true)}. Try to import single file.\");\n\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tint dr = dialog.show(\"Import files\", $\"The {sff} already is in the workspace.\\n\\n{f1.ItemPath}\", \"2 Open the existing|1 Create link|0 Cancel\", DFlags.CommandLinks, owner: TreeControl);\n\t\t\t\t\t\tif (dr != 1) {\n\t\t\t\t\t\t\tif (dr == 2) f1.Model.SetCurrentFile(f1);\n\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t}\n\t\t\t\t\t\taction = ImportFlags.Link;\n\t\t\t\t\t\tdontPrint = true;\n\t\t\t\t\t\tisLinkToWsFile = true;\n\t\t\t\t\t} else {\n\t\t\t\t\t\t//repair workspace: import file that is in a workspace folder but not in the Files panel\n\t\t\t\t\t\tfromWorkspaceDir++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (fromWorkspaceDir > 0 && fromWorkspaceDir < a.Length) return null; //some files from workspace dir and some not. Unlikely.\n\t\t\t\n\t\t\tif (action == 0) {\n\t\t\t\tif (fromWorkspaceDir > 0) {\n\t\t\t\t\taction = ImportFlags.Move;\n\t\t\t\t} else {\n\t\t\t\t\tvar ab = new[] { \"2 Copy to the workspace\", \"3 Move to the workspace\", \"1 Create link\", \"0 Cancel\" };\n\t\t\t\t\tint dr = dialog.show(\"Import files\", string.Join(\"\\n\", a), ab, DFlags.CommandLinks, owner: TreeControl, footer: GetSecurityInfo(\"v|\"));\n\t\t\t\t\taction = dr switch { 1 => ImportFlags.Link, 2 => ImportFlags.Copy, 3 => ImportFlags.Move, _ => 0 };\n\t\t\t\t\tif (action == 0) return null;\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tbool select = !flags.Has(ImportFlags.DontSelect) && !(ipos.Inside && !ipos.f.IsExpanded), focus = select;\n\t\t\tif (select) TreeControl.UnselectAll();\n\t\t\t\n\t\t\t_ImportRename iren = new(this);\n\t\t\t\n\t\t\ttry {\n\t\t\t\tvar newParentPath = newParent.FilePath + \"\\\\\";\n\t\t\t\tvar (nf1, nd1, nc1) = dontPrint ? default : _CountFilesFolders();\n\t\t\t\t\n\t\t\t\tforeach (var path in a) {\n\t\t\t\t\tvar g = filesystem.exists(path, true);\n\t\t\t\t\tif (!g.Exists || g.IsNtfsLink) continue;\n\t\t\t\t\tbool isDir = g.Directory;\n\t\t\t\t\t\n\t\t\t\t\tFileNode k;\n\t\t\t\t\tvar name = pathname.getName(path);\n\t\t\t\t\tif (fromWorkspaceDir == 0) {\n\t\t\t\t\t\tif (isLinkToWsFile) name = \"Link to \" + name; //prevent duplicate name. Never mind: when isDir, we'll have duplicate names inside the dir; cannot rename.\n\t\t\t\t\t\tname = FileNode.CreateNameUniqueInFolder(newParent, name, isDir);\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tstring path2;\n\t\t\t\t\tif (action == ImportFlags.Link) {\n\t\t\t\t\t\tk = new FileNode(this, name, path2 = path, isDir, isLink: true);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tpath2 = newParentPath + name;\n\t\t\t\t\t\tif (action == ImportFlags.Copy) {\n\t\t\t\t\t\t\tif (!TryFileOperation(FOSync.UserFileOp, () => { filesystem.copy(path, path2, FIfExists.Fail); })) continue;\n\t\t\t\t\t\t} else if (path2 != path) {\n\t\t\t\t\t\t\tif (!TryFileOperation(FOSync.UserFileOp, () => { filesystem.move(path, path2, FIfExists.Fail); })) continue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tk = new FileNode(this, name, path2, isDir);\n\t\t\t\t\t}\n\t\t\t\t\tk.AddToTree(ipos);\n\t\t\t\t\tif (isDir) {\n\t\t\t\t\t\t_AddDir(path2, k);\n\t\t\t\t\t\tforeach (var desc in k.Descendants()) _UnsafeAdd(desc, action == ImportFlags.Link);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t_UnsafeAdd(k);\n\t\t\t\t\t}\n\t\t\t\t\tif (select) {\n\t\t\t\t\t\tk.IsSelected = true;\n\t\t\t\t\t\tif (focus) { focus = false; TreeControl.SetFocusedItem(k); }\n\t\t\t\t\t}\n\t\t\t\t\tif (k.IsLink && isDir) {\n\t\t\t\t\t\t//_rootLF?.Add(k);\n\t\t\t\t\t\t_syncWatchers?.Add(k);\n\t\t\t\t\t}\n\t\t\t\t\tfReturn ??= k;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (!dontPrint) {\n\t\t\t\t\tvar (nf2, nd2, nc2) = _CountFilesFolders();\n\t\t\t\t\tint nf = nf2 - nf1, nd = nd2 - nd1, nc = nc2 - nc1;\n\t\t\t\t\tif (nf + nd > 0) print.it($\"Info: Imported {nf} files{(nd > 0 ? $\" and {nd} folders\" : null)}.{(nc > 0 ? GetSecurityInfo(\"\\r\\n\\t\") : null)}\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception ex) { print.it(ex); }\n\t\t\tSave.WorkspaceAsync();\n\t\t\tCodeInfo.FilesChanged();\n\t\t\t\n\t\t\tvoid _UnsafeAdd(FileNode f, bool inDir = false) {\n\t\t\t\tif (f.IsCodeFile && fromWorkspaceDir == 0 && !isLinkToWsFile && !flags.Has(ImportFlags.DontPrint)) iren.Imported(f, inDir);\n\t\t\t}\n\t\t\t\n\t\t\t(int nf, int nd, int nc) _CountFilesFolders() {\n\t\t\t\tint nf = 0, nd = 0, nc = 0;\n\t\t\t\tforeach (var v in Root.Descendants()) if (v.IsFolder) nd++; else { nf++; if (v.IsCodeFile) nc++; }\n\t\t\t\treturn (nf, nd, nc);\n\t\t\t}\n\t\t}\n\t\tcatch (Exception e1) { print.it(e1); }\n\t\treturn fReturn;\n\t}\n\t\n\tclass _ImportRename {\n\t\tHashSet<string> _hs;\n\t\tFilesModel _model;\n\t\t\n\t\tpublic _ImportRename(FilesModel model) {\n\t\t\t_model = model;\n\t\t}\n\t\t\n\t\t//When importing an unknown file that has a known auto-executed file name or \"global.cs\", renames it.\n\t\tpublic void Imported(FileNode f, bool inLinkDir) {\n\t\t\tif (_hs == null) {\n\t\t\t\t_hs = new(StringComparer.OrdinalIgnoreCase) { \"global.cs\" };\n\t\t\t\tif (_model._GetStartupScripts() is { } x) foreach (var row in x.Rows) _hs.Add(row[0]); //and never mind if \\path or //comment\n\t\t\t}\n\t\t\tvar name = f.Name;\n\t\t\tif (_hs.Contains(name)) {\n\t\t\t\tvar oldName = name;\n\t\t\t\tname = name.Insert(^3, \"-renamed\");\n\t\t\t\tfor (int i = 2, j = name.Length - 3; ; i++) {\n\t\t\t\t\tif (_hs.Contains(name) || filesystem.exists(f.Parent.FilePath + \"\\\\\" + name)) name = name.ReplaceAt(j..^3, i.ToS());\n\t\t\t\t\telse break;\n\t\t\t\t}\n\t\t\t\tconst string s1 = \" to prevent unintended execution or duplicate name problems\";\n\t\t\t\tif (inLinkDir || !f.RenameL_(name)) print.it($\"<><c red>Consider to rename or delete {f.SciLink(true)}{s1}.<>\");\n\t\t\t\telse print.it($\"<>File {f.SciLink(false)} has been renamed{s1}. Original name: \\\"{oldName}\\\".\");\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Finds a file or directory by path in the workspace. If not found, imports as link. Opens/selects.\n\t/// </summary>\n\t/// <param name=\"file\">Full path.</param>\n\t/// <param name=\"ipos\">If default, inserts at the top.</param>\n\t/// <returns>The imported item, or null if failed.</returns>\n\tpublic FileNode ImportLinkOrOpen(string file, FNInsertPos ipos = default) {\n\t\tvar k = filesystem.exists(file); if (!k) return null;\n\t\tvar f = FindByFilePath(file, k.Directory ? FNFind.Folder : FNFind.File);\n\t\tif (f is null) {\n\t\t\tif (ipos.f is null) ipos = new(Root, FNInsert.First);\n\t\t\tf = ImportFiles([file], ipos, ImportFlags.Link | ImportFlags.DontPrint | ImportFlags.DontSelect);\n\t\t\tif (f is null) return null;\n\t\t}\n\t\treturn SetCurrentFile(f) ? f : null;\n\t}\n\t\n\t/// <summary>\n\t/// Adds to workspace 1 file (not folder) that exists in workspace folder in filesystem.\n\t/// </summary>\n\tpublic FileNode ImportFileFromWorkspaceDir(string path, FNInsertPos ipos) {\n\t\ttry { //probably nothing can throw here, but anyway\n\t\t\tif (!filesystem.exists(path, useRawPath: true).File) return null;\n\t\t\tif (FindByFilePath(path) is { } f1) return f1.IsFolder ? null : f1;\n\t\t\tvar R = new FileNode(this, pathname.getName(path), path, isDir: false);\n\t\t\tR.AddToTree(ipos);\n\t\t\tSave.WorkspaceAsync();\n\t\t\tCodeInfo.FilesChanged();\n\t\t\treturn R;\n\t\t}\n\t\tcatch (Exception ex) { print.warning(ex); return null; }\n\t}\n\t\n\tFileNode _SyncImportFromWorkspaceDir(string path, string itemPath) {\n\t\ttry { //probably nothing can throw here, but anyway\n\t\t\tvar exists = filesystem.exists(path, true);\n\t\t\tif (exists && !exists.Attributes.Has(FileAttributes.Hidden | FileAttributes.System) && FindByItemPath(itemPath) is null) {\n\t\t\t\tvar parentItemPath = pathname.getDirectory(itemPath);\n\t\t\t\tvar parent = parentItemPath.Length > 0 ? FindByItemPath(parentItemPath) : Root;\n\t\t\t\tif (parent != null) {\n\t\t\t\t\tFNInsertPos ipos = new(parent, parent.Id == 0 ? FNInsert.First : FNInsert.Last);\n\t\t\t\t\tvar R = new FileNode(this, pathname.getName(path), path, isDir: exists.Directory);\n\t\t\t\t\tR.AddToTree(ipos);\n\t\t\t\t\t\n\t\t\t\t\t//if folder, add descendants.\n\t\t\t\t\t//\tNote: when copying, some descendants may be still not copied; it's OK, we'll receive 'created' events for those. When moving, there are no 'created' events for descendants.\n\t\t\t\t\tif (exists.Directory) _AddDir(path, R);\n\t\t\t\t\t\n\t\t\t\t\tSave.WorkspaceAsync();\n\t\t\t\t\tCodeInfo.FilesChanged();\n\t\t\t\t\treturn R;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcatch (Exception ex) { print.warning(ex); }\n\t\treturn null;\n\t}\n\t\n\tvoid _AddDir(string path, FileNode parent) {\n\t\tforeach (var u in filesystem.enumerate(path, FEFlags.UseRawPath | FEFlags.SkipHiddenSystem)) {\n\t\t\tif (_IsPathIgnored(parent.ItemPath + \"\\\\\" + u.Name)) continue;\n\t\t\tbool isDir = u.IsDirectory;\n\t\t\tvar k = new FileNode(this, u.Name, u.FullPath, isDir);\n\t\t\tparent.AddChild(k);\n\t\t\tif (isDir) _AddDir(u.FullPath, k);\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Imports another workspace folder or zip file (workspace or not) into this workspace.\n\t/// </summary>\n\t/// <param name=\"wsDirOrZip\">Workspace directory or any .zip file.</param>\n\t/// <param name=\"ipos\">If null, calls _GetInsertPos.</param>\n\tpublic void ImportWorkspace(string wsDirOrZip = null, FNInsertPos? ipos = null) {\n\t\ttry {\n\t\t\tstring wsDir, folderName;\n\t\t\tbool isZip = wsDirOrZip.Ends(\".zip\", true) && filesystem.exists(wsDirOrZip).File, notWorkspace = false;\n\t\t\t\n\t\t\tif (isZip) {\n\t\t\t\tfolderName = pathname.getNameNoExt(wsDirOrZip);\n\t\t\t\twsDir = folders.ThisAppTemp + folderName;\n\t\t\t\tfilesystem.delete(wsDir);\n\t\t\t\tZipFile.ExtractToDirectory(wsDirOrZip, wsDir);\n\t\t\t\tnotWorkspace = !IsWorkspaceDirectoryOrZip(wsDir, out _);\n\t\t\t} else {\n\t\t\t\twsDir = wsDirOrZip;\n\t\t\t\tfolderName = pathname.getName(wsDir);\n\t\t\t}\n\t\t\t\n\t\t\t//create new folder for workspace's items\n\t\t\tvar folder = NewItemLX(null, ipos, folderName);\n\t\t\tif (folder == null) return;\n\t\t\t\n\t\t\tif (notWorkspace) {\n\t\t\t\tImportFiles(Directory.GetFileSystemEntries(wsDir), new(folder, FNInsert.Last), ImportFlags.Copy);\n\t\t\t} else {\n\t\t\t\tvar m = new FilesModel(wsDir + @\"\\files.xml\", importing: true);\n\t\t\t\tFNInsertPos ipos2 = new(folder, FNInsert.Last);\n\t\t\t\tforeach (var f in m.Root.Children()) {\n\t\t\t\t\tf._FileCopy(ipos2, this);\n\t\t\t\t}\n\t\t\t\tm.Dispose(); //currently does nothing\n\t\t\t\t\n\t\t\t\t_ImportRename iren = new(this);\n\t\t\t\tforeach (var f in folder.Descendants()) {\n\t\t\t\t\tif (f.IsCodeFile) iren.Imported(f, false);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tprint.it($\"Info: Imported '{wsDirOrZip}' to folder '{folder.Name}'.\\r\\n\\t{GetSecurityInfo()}\");\n\t\t\t}\n\t\t\t\n\t\t\tfolder.SelectSingle();\n\t\t\tif (isZip) filesystem.delete(wsDir);\n\t\t}\n\t\tcatch (Exception ex) { print.it(ex); }\n\t}\n\t\n\t#endregion\n\t\n\t#region export\n\t\n\t/// <summary>\n\t/// Shows dialog to get path for new or exporting workspace.\n\t/// Returns workspace's directory path.\n\t/// Does not create any files/directories.\n\t/// </summary>\n\t/// <param name=\"name\">Default name of the workspace.</param>\n\t/// <param name=\"location\">Default parent directory of the main directory of the workspace.</param>\n\tpublic static string GetDirectoryPathForNewWorkspace(string name = null, string location = null) {\n\t\tvar d = new DNewWorkspace(name, location ?? folders.ThisAppDocuments) { Owner = App.Wmain, ShowInTaskbar = false };\n\t\tif (d.ShowDialog() != true) return null;\n\t\treturn d.ResultPath;\n\t}\n\t\n\tpublic bool ExportSelected(string location = null) {\n\t\tvar aSel = TreeControl.SelectedItems; if (aSel.Length < 1) return false;\n\t\t\n\t\t//if selected a file in a project, export the project folder instead\n\t\tfor (int i = 0; i < aSel.Length; i++) if (aSel[i].FindProject(out var folder, out _)) aSel[i] = folder;\n\t\taSel = aSel.Distinct().ToArray();\n\t\t\n\t\tvar b = new wpfBuilder(\"Export selected files\").WinSize(550);\n\t\tb.WinProperties(showInTaskbar: false);\n\t\tb.R.StartGrid<KGroupBox>(\"Files\");\n\t\tb.R.xAddInfoBlockF($\"Selected: {string.Join(\", \", aSel.AsEnumerable())}\");\n\t\tb.R.Add(out KCheckBox cDeps, \"And files used via /*/ c, resource, file, preBuild, postBuild, icon, manifest, sign /*/\")\n\t\t\t.Checked(0 != (App.Settings.export & 4));\n\t\tb.R.Add(out KCheckBox cGlobal, \"And global.cs\")\n\t\t\t.Margin(20).xBindCheckedEnabled(cDeps)\n\t\t\t.Checked(0 != (App.Settings.export & 8))\n\t\t\t.Tooltip(\"File global.cs and its /*/ c etc /*/ files.\\nNote: when importing, this global.cs probably will be renamed to global2.cs because global.cs already exists. Users can rename, edit and include it where need like /*/ c renamed.cs /*/.\");\n\t\tb.R.Add(out KCheckBox cPR, \"And library projects used via /*/ pr /*/\")\n\t\t\t.Margin(20).xBindCheckedEnabled(cDeps)\n\t\t\t.Checked(0 != (App.Settings.export & 16));\n\t\tb.End();\n\t\tb.R.StartGrid<KGroupBox>(\"Options\");\n\t\tb.R.Add(out KCheckBox cZip, \"Zip file\")\n\t\t\t.Checked(0 != (App.Settings.export & 1))\n\t\t\t.Tooltip(\"Export as .zip file.\\nIf unchecked, exports as folder. In any case it's a LibreAutomate workspace.\");\n\t\tb.R.Add(out KCheckBox cLinks, \"Add link target\")\n\t\t\t.Checked(0 != (App.Settings.export & 2))\n\t\t\t.Tooltip(\"How to export links:\\nChecked - export the link target instead.\\nUnchecked - export the link; on other computers it probably will be invalid.\");\n\t\tb.End();\n\t\tb.R.AddOkCancel();\n\t\tb.End();\n\t\tif (!b.ShowDialog(App.Wmain)) return false;\n\t\tbool zip = cZip.IsChecked, exportLinkTarget = cLinks.IsChecked, exportDeps = cDeps.IsChecked, exportGlobal = cGlobal.IsChecked, exportPR = cPR.IsChecked;\n\t\tApp.Settings.export = (zip ? 1 : 0) | (exportLinkTarget ? 2 : 0) | (exportDeps ? 4 : 0) | (exportGlobal ? 8 : 0) | (exportPR ? 16 : 0);\n\t\t\n\t\tSave.AllNowIfNeed();\n\t\t\n\t\tstring name = aSel[0].Name; if (!aSel[0].IsFolder) name = pathname.getNameNoExt(name);\n\t\t\n\t\t//if selected single folder, export its children without the folder\n\t\tif (aSel.Length == 1 && aSel[0].IsFolder && aSel[0].HasChildren) aSel = aSel[0].Children().ToArray();\n\t\t\n\t\tList<FileNode> aDeps = new();\n\t\tif (exportDeps && !_Deps()) return false;\n\t\t\n\t\tvar fRoot = FileNode.CreateForExport();\n\t\t_Enum1(fRoot, aSel);\n\t\tvoid _Enum1(FileNode parent, IEnumerable<FileNode> e) {\n\t\t\tforeach (var f in e) {\n\t\t\t\tvar fe = FileNode.CreateForExport(f, exportLinkTarget);\n\t\t\t\tparent.AddChild(fe);\n\t\t\t\tif (f.IsFolder) _Enum1(fe, f.Children());\n\t\t\t}\n\t\t}\n\t\t\n\t\tstring wsDir;\n\t\tif (zip) {\n\t\t\tvar d = new FileOpenSaveDialog(\"{4D1F3AFB-DA1A-45AC-8C12-41DDA5C51CDA}\") {\n\t\t\t\tFileTypes = \"Zip files|*.zip\",\n\t\t\t\tDefaultExt = \"zip\",\n\t\t\t\tInitFolderFirstTime = location ?? folders.ThisAppDocuments,\n\t\t\t\tFileNameText = name + \".zip\",\n\t\t\t};\n\t\t\tif (!d.ShowSave(out location, App.Hmain, overwritePrompt: false)) return false;\n\t\t\twsDir = folders.ThisAppTemp + \"Workspace zip\";\n\t\t\tfilesystem.delete(wsDir);\n\t\t} else {\n\t\t\twsDir = GetDirectoryPathForNewWorkspace(name, location);\n\t\t\tif (wsDir == null) return false;\n\t\t}\n\t\t\n\t\tstring filesDir = wsDir + @\"\\files\";\n\t\ttry {\n\t\t\tfilesystem.createDirectory(filesDir);\n\t\t\tforeach (var f in aSel) {\n\t\t\t\tif (!(f.IsLink && !exportLinkTarget)) filesystem.copyTo(f.FilePath, filesDir);\n\t\t\t}\n\t\t\t\n\t\t\tif (exportLinkTarget) {\n\t\t\t\tforeach (var f in aSel) {\n\t\t\t\t\tif (f.IsFolder && !f.IsLink) {\n\t\t\t\t\t\tforeach (var v in f.Descendants()) {\n\t\t\t\t\t\t\tif (v.IsLink) filesystem.copy(v.FilePath, filesDir + \"\\\\\" + f.Name + v.ItemPathIn(f));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tif (aDeps.Count > 0) {\n\t\t\t\tvar depsDirName = _GetUniqueNameForDepsFolder();\n\t\t\t\tvar depsDir = filesDir + @\"\\\" + depsDirName;\n\t\t\t\tfilesystem.createDirectory(depsDir);\n\t\t\t\tFileNode fDeps = FileNode.CreateForExport(depsDirName);\n\t\t\t\tforeach (var f in aDeps) {\n\t\t\t\t\tif (!(f.IsLink && !exportLinkTarget)) filesystem.copyTo(f.FilePath, depsDir);\n\t\t\t\t\tvar fe = FileNode.CreateForExport(f, exportLinkTarget);\n\t\t\t\t\tfDeps.AddChild(fe);\n\t\t\t\t\tif (f.IsFolder) _Enum1(fe, f.Children());\n\t\t\t\t}\n\t\t\t\tfRoot.AddChild(fDeps);\n\t\t\t}\n\t\t\t\n\t\t\tFileNode.Export(fRoot, wsDir + @\"\\files.xml\");\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tprint.it(ex);\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\tif (zip) {\n\t\t\tfilesystem.delete(location);\n\t\t\tZipFile.CreateFromDirectory(wsDir, location);\n\t\t\tfilesystem.delete(wsDir);\n\t\t\twsDir = location;\n\t\t}\n\t\t\n\t\tprint.it($\"<>Exported to <explore>{wsDir}<>\");\n\t\tif (aDeps.Count > 0) print.it($\"<>\\tAdditional files: {string.Join(\", \", aDeps.Select(o => o.SciLink()))}\");\n\t\treturn true;\n\t\t\n\t\t//adds meta c etc files\n\t\tbool _Deps() {\n\t\t\tHashSet<FileNode> hsAll = aSel.SelectMany(o => o.Descendants(andSelf: true)).ToHashSet(), hsParsed = new();\n\t\t\tHashSet<string> hsAllNames = new(hsAll.Select(o => o.Name), StringComparer.OrdinalIgnoreCase);\n\t\t\tbool failed = false;\n\t\t\ttry { _Enum2(aSel); }\n\t\t\tcatch (Exception ex) { failed = true; print.it(\"Can't export. Error: \" + ex.Message); }\n\t\t\treturn !failed;\n\t\t\t\n\t\t\tvoid _Enum2(IEnumerable<FileNode> e) {\n\t\t\t\tforeach (var f in e) {\n\t\t\t\t\tif (f.FindProject(out var fFolder, out var fMain)) {\n\t\t\t\t\t\t_ParseMeta(fMain, fFolder);\n\t\t\t\t\t} else if (f.IsCodeFile) {\n\t\t\t\t\t\t_ParseMeta(f);\n\t\t\t\t\t} else if (f.IsFolder) {\n\t\t\t\t\t\t_Enum2(f.Children());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvoid _ParseMeta(FileNode f, FileNode projFolder = null) {\n\t\t\t\t\tif (hsParsed.Contains(f)) return;\n\t\t\t\t\tvar m = new MetaComments(MCFlags.Export | (exportGlobal ? 0 : MCFlags.ExportNoGlobal) | (exportPR ? MCFlags.ExportPR : 0));\n\t\t\t\t\texportGlobal = false;\n\t\t\t\t\tif (!m.Parse(f, projFolder)) {\n\t\t\t\t\t\tprint.it($\"<><lc #F0E080>Can't export additional files. Errors:<>\\r\\n{m.Errors}\");\n\t\t\t\t\t\tfailed = true;\n\t\t\t\t\t\thsParsed.Add(f);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tforeach (var c in m.CodeFiles) hsParsed.Add(c.f);\n\t\t\t\t\tif (m.ExportC_ is { } ac) { //meta `c fileOrFolder` (m.CodeFiles - only files)\n\t\t\t\t\t\tforeach (var c in ac) _Add(c);\n\t\t\t\t\t}\n\t\t\t\t\tif (m.Resources != null) foreach (var c in m.Resources) _Add(c.f); //meta `resource fileOrFolder`\n\t\t\t\t\tif (m.OtherFiles != null) foreach (var c in m.OtherFiles) _Add(c.f); //meta `file fileOrFolder`\n\t\t\t\t\t_Add(m.IconFile);\n\t\t\t\t\t_Add(m.ManifestFile);\n\t\t\t\t\t_Add(m.SignFile);\n\t\t\t\t\t_Add(m.PreBuild.f, true);\n\t\t\t\t\t_Add(m.PostBuild.f, true);\n\t\t\t\t\tif (m.ProjectReferences is { } pr) {\n\t\t\t\t\t\tforeach (var v in pr) _AddPR(v.f);\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tvoid _Add(FileNode f, bool parseMeta = false) {\n\t\t\t\t\t\tif (f != null && hsAll.Add(f)) {\n\t\t\t\t\t\t\tif (!hsAllNames.Add(f.Name)) throw new InvalidOperationException(\"Not unique name of additional file: \" + f.Name); //don't allow duplicate names. Users would fail to compile.\n\t\t\t\t\t\t\taDeps.Add(f);\n\t\t\t\t\t\t\tif (parseMeta) _ParseMeta(f);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tvoid _AddPR(FileNode f) {\n\t\t\t\t\t\tvar folder = f.Parent.Name is ['@', ..] ? f.Parent : null;\n\t\t\t\t\t\tvar ff = folder ?? f;\n\t\t\t\t\t\tif (hsAll.Add(ff)) {\n\t\t\t\t\t\t\tif (!hsAllNames.Add(ff.Name)) throw new InvalidOperationException(\"Not unique name of additional file: \" + ff.Name);\n\t\t\t\t\t\t\taDeps.Add(ff);\n\t\t\t\t\t\t\t_ParseMeta(f, folder);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tstring _GetUniqueNameForDepsFolder() {\n\t\t\tfor (int i = 1; ; i++) {\n\t\t\t\tvar s = i == 1 ? \"Classes\" : \"Classes\" + i;\n\t\t\t\tif (!aSel.Any(o => o.Name.Eqi(s))) return s;\n\t\t\t}\n\t\t}\n\t}\n\t\n\t#endregion\n\t\n\t#region fill menu\n\t\n\t/// <summary>\n\t/// Adds recent workspaces to submenu File > Workspace.\n\t/// </summary>\n\tpublic static void FillMenuRecentWorkspaces(MenuItem sub) {\n\t\tvar mi = sub.Items[0] as MenuItem;\n\t\tmi.Header = App.Model.WorkspaceDirectory.Replace(\"_\", \"__\");\n\t\tmi.FontWeight = FontWeights.Bold;\n\t\t\n\t\tsub.RemoveAllCustom();\n\t\tvar ar = App.Settings.recentWS;\n\t\tint j = 0, i = 0, n = ar?.Length ?? 0;\n\t\tfor (; i < n; i++) {\n\t\t\tif (sub.Items.Count >= 15 || !filesystem.exists(ar[i]).Directory) ar[i] = null;\n\t\t\telse sub.InsertCustom(++j, pathname.expand(ar[i]), LoadWorkspace);\n\t\t}\n\t\tif (j < i) App.Settings.recentWS = ar.Where(o => o != null).ToArray();\n\t}\n\t\n\t/// <summary>\n\t/// Adds templates to File > New.\n\t/// </summary>\n\tpublic static void FillMenuNew(MenuItem sub) {\n\t\t//rejected: call once, or if Templates\\files.xml modified. Fast, don't need caching.\n\t\t\n\t\tvar xroot = FileNode.Templates.LoadXml();\n\t\tsub.RemoveAllCustom();\n\t\t\n\t\tvar isContextMenu = sub.Parent is ContextMenu;\n\t\tvar templDir = FileNode.Templates.DefaultDirBS;\n\t\t_CreateMenu(sub, xroot, null, 0);\n\t\t\n\t\tvoid _CreateMenu(MenuItem mParent, XElement xParent, string dir, int level) {\n\t\t\tforeach (var x in xParent.Elements()) {\n\t\t\t\tstring tag = x.Name.LocalName, name = x.Attr(\"n\");\n\t\t\t\tint isFolder = tag == \"d\" ? 1 : 0;\n\t\t\t\tif (isFolder == 1) {\n\t\t\t\t\tisFolder = name[0] switch { '@' => 2, '!' => 3, _ => 1 }; //@ project, ! simple folder\n\t\t\t\t} else if (level == 0) {\n\t\t\t\t\tif (FileNode.Templates.IsStandardTemplateName(name, out _) || name == \"File.txt\") continue;\n\t\t\t\t}\n\t\t\t\tstring relPath = dir + name;\n\t\t\t\tif (isFolder == 3) name = name[1..];\n\t\t\t\tvar item = new MenuItem { Header = name.Replace(\"_\", \"__\") };\n\t\t\t\tif (isFolder == 1) {\n\t\t\t\t\t_CreateMenu(item, x, relPath + \"\\\\\", level + 1);\n\t\t\t\t} else {\n\t\t\t\t\titem.Click += (_, e) => {\n\t\t\t\t\t\ts_inContextMenuCommand = isContextMenu;\n\t\t\t\t\t\tApp.Model.NewItemX(x, beginRenaming: true);\n\t\t\t\t\t\ts_inContextMenuCommand = false;\n\t\t\t\t\t};\n\t\t\t\t\tvar ft = FileNode.XmlTagToFileType(tag, canThrow: false);\n\t\t\t\t\titem.Icon = ft == FNType.Other\n\t\t\t\t\t\t? new Image { Source = icon.of(templDir + relPath)?.ToWpfImage() }\n\t\t\t\t\t\t: ImageUtil.LoadWpfImageElement(FileNode.GetFileTypeImageSource(ft));\n\t\t\t\t}\n\t\t\t\tmParent.InsertCustom(-1, item);\n\t\t\t}\n\t\t}\n\t}\n\t\n\t#endregion\n\t\n\t#region other\n\t\n\t//public List<FileNode> RootAndLinkFolders {\n\t//\tget {\n\t//\t\tif (_rootLF == null) {\n\t//\t\t\t_rootLF = [];\n\t//\t\t\t_rootLF.Add(Root);\n\t//\t\t\tforeach (var f in Root.Descendants()) {\n\t//\t\t\t\tif (f.IsLink && f.IsFolder) _rootLF.Add(f);\n\t//\t\t\t}\n\t//\t\t}\n\t//\t\treturn _rootLF;\n\t//\t}\n\t//}\n\t//List<FileNode> _rootLF;\n\t\n\tinternal void ChangedFolderItemPath_() {\n\t\t_syncWatchers?.UpdatePaths();\n\t}\n\t\n\t/// <summary>\n\t/// Adds some default files if missing.\n\t/// </summary>\n\t/// <param name=\"scriptForNewWorkspace\">If empty workspace, creates new empty script from current template.</param>\n\t/// <param name=\"globalCs\">If class file \"global.cs\" not found, creates it in existing or new folder \"Classes\".</param>\n\tpublic void AddMissingDefaultFiles(bool scriptForNewWorkspace = false, bool globalCs = false) {\n\t\tif (scriptForNewWorkspace && Root.FirstChild == null) {\n\t\t\tNewItem(@\"Script.cs\");\n\t\t}\n\t\tif (globalCs && null == FindGlobalCs(silent: true) && FoundMultiple == null) {\n\t\t\tvar folder = Find(@\"\\Classes\", FNFind.Folder) ?? NewItemL(null, new(Root, FNInsert.Last), \"Classes\");\n\t\t\tNewItemL(@\"Default\\global.cs\", new(folder, FNInsert.Last));\n\t\t}\n\t\t//example of importing a default file as a link, as readonly\n\t\t//if (git && null == FindCodeFile(\"Git script.cs\") && FoundMultiple == null) {\n\t\t//\tvar folder = Find(@\"\\Classes\", FNFind.Folder) ?? NewItemL(null, new(Root, FNInsert.Last), \"Classes\");\n\t\t//\tImportFiles(new[] { folders.ThisAppBS + @\"Templates\\files\\Default\\Git script.cs\" }, new(folder, FNInsert.Last), ImportFlags.Link | ImportFlags.DontPrint | ImportFlags.DontSelect);\n\t\t//}\n\t}\n\t\n\tpublic WorkspaceSettings.User UserSettings => WSSett.CurrentUser;\n\t\n\tpublic void RunStartupScripts(bool startAsync) {\n\t\tforeach (var (row, f) in GetStartupScriptsExceptDisabled(printNotFound: true)) {\n\t\t\tint delay = 0;\n\t\t\tif (row.Length > 1) {\n\t\t\t\tvar sd = row[1];\n\t\t\t\tdelay = sd.ToInt(0, out int end);\n\t\t\t\tif (end > 0 && !sd.Ends(\"ms\", true)) delay = (int)Math.Min(delay * 1000L, int.MaxValue);\n\t\t\t}\n\t\t\tif (startAsync && delay < 10) delay = 10;\n\t\t\tif (delay > 0) {\n\t\t\t\ttimer.after(delay, t => {\n\t\t\t\t\tCompileRun.CompileAndRun(true, f);\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tCompileRun.CompileAndRun(true, f);\n\t\t\t}\n\t\t}\n\t\tGit.AutoBackup(true);\n\t}\n\t\n\tcsvTable _GetStartupScripts() {\n\t\tif (UserSettings.startupScripts is var csv && !csv.NE()) {\n\t\t\ttry { return csvTable.parse(csv); }\n\t\t\tcatch (FormatException e1) { print.warning(e1); }\n\t\t}\n\t\treturn null;\n\t}\n\t\n\t/// <summary>\n\t/// Gets startup scripts of this workspace, except those commented out or missing.\n\t/// </summary>\n\t/// <param name=\"printNotFound\">Print not found scripts.</param>\n\t/// <returns>CSV row and the <b>FileNode</b> (null if not found).</returns>\n\tpublic IEnumerable<(string[] row, FileNode f)> GetStartupScriptsExceptDisabled(bool printNotFound = false) {\n\t\tif (_GetStartupScripts() is { } csv) {\n\t\t\tforeach (var row in csv.Rows) {\n\t\t\t\tstring file = row[0];\n\t\t\t\tif (file.NE() || file.Starts(\"//\")) continue;\n\t\t\t\tif (FindCodeFile(file) is { } f) yield return (row, f);\n\t\t\t\telse if (printNotFound) print.it(\"<>Startup script not found: \" + file + \". Please edit <+options Workspace>Options > Workspace > Startup scripts<>.\");\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Finds <i>f</i> in startup scripts. If not found, adds to startup scripts.\n\t/// </summary>\n\tpublic EISSResult EnsureIsInStartupScripts(FileNode f, bool printAdded, bool usePath = false) {\n\t\tstring itemPath = f.ItemPath;\n\t\tstring itemPathOrName = usePath ? itemPath : f.ItemPathOrName();\n\t\tvar ss = App.Model.UserSettings.startupScripts;\n\t\tif (ss.NE()) {\n\t\t\tss = itemPathOrName;\n\t\t} else {\n\t\t\tif (_GetStartupScripts() is not { } csv) return EISSResult.Failed;\n\t\t\tstring name = f.Name, name2 = f.DisplayName;\n\t\t\tif (csv.Rows.FirstOrDefault(a => a[0].AsSpan(a[0].Starts(\"//\") ? 2 : 0) is var s && (s.Eqi(itemPath) || s.Eqi(name) || s.Eqi(name2))) is { } a) {\n\t\t\t\tif (!a[0].Starts(\"//\")) return EISSResult.FoundEnabled;\n\t\t\t\t//rejected. It may be set to run from other startup script.\n\t\t\t\t//if (printDisabled) print.it($\"<>Note: script \\\"{itemPathOrName}\\\" is not set to run at startup. In <+options Workspace>Options > Workspace > Startup scripts<> remove the // prefix.\");\n\t\t\t\treturn EISSResult.FoundDisabled;\n\t\t\t}\n\t\t\tcsv.AddRow(itemPathOrName);\n\t\t\tss = csv.ToString();\n\t\t}\n\t\t\n\t\t//if Options is open...\n\t\tif (KDialogWindow.GetSingle<DOptions>(out var dOptions)) {\n\t\t\tDOptions.AaShow(DOptions.EPage.Workspace);\n\t\t\tclipboard.text = itemPathOrName;\n\t\t\tdialog.show(null, $\"Please add this to Options > Workspace > Startup scripts.\\nIt's now in the clipboard.\\n\\n{itemPathOrName}\", owner: dOptions);\n\t\t\treturn EISSResult.Failed;\n\t\t}\n\t\t\n\t\tApp.Model.UserSettings.startupScripts = ss;\n\t\tif (printAdded) print.it($\"<>Info: script \\\"{itemPathOrName}\\\" has been added to <+options Workspace>Options > Workspace > Startup scripts<>. If unwanted, disable the line (prefix //).\");\n\t\treturn EISSResult.Added;\n\t}\n\tpublic enum EISSResult { FoundEnabled, FoundDisabled, Added, Failed }\n\t\n\t//Used mostly by SciCode, but owned by workspace because can go to any file.\n\tinternal readonly EditGoBack EditGoBack = new();\n\t\n\t#endregion\n\t\n\t#region util\n\t\n\t/// <summary>\n\t/// Calls <i>action</i> in try/catch, and manages filesystem sync. On exception prints message and returns false.\n\t/// </summary>\n\tpublic bool TryFileOperation(FOSync fos, Action action) {\n\t\t_syncWatchers?.FileOperationStarted(fos);\n\t\ttry { action(); return true; }\n\t\tcatch (Exception ex) { print.warning(ex); return false; }\n\t\tfinally { _syncWatchers?.FileOperationEnded(fos); }\n\t}\n\t\n\t/// <summary>\n\t/// Calls <i>action</i> and manages filesystem sync. Does not handle exceptions.\n\t/// </summary>\n\t/// <exception cref=\"Exception\"></exception>\n\tpublic void FileOperation(FOSync fos, Action action) {\n\t\t_syncWatchers?.FileOperationStarted(fos);\n\t\ttry { action(); }\n\t\tfinally { _syncWatchers?.FileOperationEnded(fos); }\n\t}\n\t\n\t/// <summary>\n\t/// Returns true if FileNode f is not null and belongs to this FilesModel and is not deleted.\n\t/// </summary>\n\tpublic bool IsMyFileNode(FileNode f) { return Root.IsAncestorOf(f); }\n\t\n\t/// <summary>\n\t/// Returns true if s is path of a workspace directory or .zip file.\n\t/// </summary>\n\tpublic static bool IsWorkspaceDirectoryOrZip(string path, out bool zip) {\n\t\tzip = false;\n\t\tswitch (filesystem.exists(path)) {\n\t\tcase 2:\n\t\t\tstring xmlFile = path + @\"\\files.xml\";\n\t\t\tif (filesystem.exists(xmlFile).File && filesystem.exists(path + @\"\\files\").Directory) {\n\t\t\t\ttry { return XmlUtil.LoadElem(xmlFile).Name == \"files\"; } catch { }\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 1 when path.Ends(\".zip\", true):\n\t\t\treturn zip = true;\n\t\t}\n\t\treturn false;\n\t}\n\t\n\t/// <summary>\n\t/// If s is path of a workspace directory or .zip file, shows \"Open/import\" dialog and returns true.\n\t/// dialogResult receives: 1 Open, 2 Import, 0 Cancel.\n\t/// </summary>\n\tpublic static bool IsWorkspaceDirectoryOrZip_ShowDialogOpenImport(string path, out int dialogResult) {\n\t\tdialogResult = 0;\n\t\tif (!IsWorkspaceDirectoryOrZip(path, out bool zip)) return false;\n\t\tvar text1 = zip ? \"Import files from zip\" : \"Workspace\";\n\t\tvar buttons = zip ? \"2 Import|0 Cancel\" : \"1 Open|2 Import|0 Cancel\";\n\t\tdialogResult = dialog.show(text1, path, buttons, footer: GetSecurityInfo(\"v|\"), owner: TreeControl);\n\t\treturn true;\n\t}\n\t\n\t/// <summary>\n\t/// Security info string.\n\t/// </summary>\n\tpublic static string GetSecurityInfo(string prefix = null) {\n\t\treturn prefix + \"Security info: Unknown C# script files can contain malicious code - virus, spyware, etc. It is safe to import, open and edit C# files if you don't run them. Triggers don't work until run.\";\n\t}\n\t\n\t#endregion\n}\n\n[Flags]\nenum ImportFlags {\n\tCopy = 1,\n\tMove = 2,\n\tLink = 4,\n\tDontSelect = 8,\n\tDontPrint = 16,\n}\n"
  },
  {
    "path": "Au.Editor/Files/FilesView.cs",
    "content": "using Au.Controls;\nusing System.Windows.Input;\nusing System.Windows;\nusing System.Windows.Controls;\n\nnamespace LA;\n\npartial class FilesModel {\n\tpublic class FilesView : KTreeView {\n\t\tpublic FilesView() {\n\t\t\tSetMultiSelect(toggle: false);\n\t\t\tFullRowExpand = true;\n\t\t\tAllowDrop = true;\n\n\t\t\tItemActivated += _ItemActivated;\n\t\t\tItemClick += _ItemClick;\n\n\t\t\tItemDragStart += _ItemDragStart;\n\n\t\t\tFilesModel.NeedRedraw += v => { if (v.f != null) Redraw(v.f, v.remeasure); else Redraw(v.remeasure); };\n\n\t\t\tApp.Commands.BindKeysTarget(this, \"Files\");\n\t\t}\n\n\t\tpublic void SetItems() {\n\t\t\tbase.SetItems(App.Model.Root.Children());\n\t\t}\n\n\t\tpublic void SetMultiSelect(bool toggle) {\n\t\t\tbool multi = App.Settings.files_multiSelect;\n\t\t\tif (toggle) App.Settings.files_multiSelect = (multi ^= true);\n\t\t\tMultiSelect = multi;\n\t\t\tSingleClickActivate = !multi;\n\t\t\tApp.Commands[nameof(Menus.File.CopyPaste.MultiSelect_files)].Checked = multi;\n\t\t}\n\n\t\tprivate void _ItemActivated(TVItemEventArgs e) {\n\t\t\tvar f = e.Item as FileNode;\n\t\t\tif (f.IsFolder) {\n\t\t\t\tif (e.ClickCount == 0) TreeControl.Expand(f, true);\n\t\t\t} else {\n\t\t\t\tvar m = App.Model;\n\t\t\t\tif (e.ClickCount == 0 && f == m.CurrentFile) Panels.Editor.ActiveDoc?.Focus(); //let Enter set focus = active doc\n\t\t\t\telse m._SetCurrentFile(f, focusEditor: e.ClickCount switch { 1 => null, 2 => true, _ => false });\n\t\t\t}\n\t\t}\n\n\t\tprivate void _ItemClick(TVItemEventArgs e) {\n\t\t\tif (e.Mod != 0) return;\n\t\t\tvar f = e.Item as FileNode;\n\t\t\tswitch (e.Button) {\n\t\t\tcase MouseButton.Right:\n\t\t\t\tDispatcher.InvokeAsync(() => App.Model._ItemRightClicked(f));\n\t\t\t\tbreak;\n\t\t\tcase MouseButton.Middle:\n\t\t\t\tApp.Model.CloseFiles(f);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tprotected override void OnKeyDown(KeyEventArgs e) {\n\t\t\tbase.OnKeyDown(e);\n\t\t\tif (e.Handled || base.EditingLabel) return;\n\t\t\tvar m = App.Model;\n\t\t\tswitch ((e.KeyboardDevice.Modifiers, e.Key)) {\n\t\t\tcase (0, Key.Delete): m.DeleteSelected(); break;\n\t\t\tcase (ModifierKeys.Control, Key.X): m.CutCopySelected(true); break;\n\t\t\tcase (ModifierKeys.Control, Key.C): m.CutCopySelected(false); break;\n\t\t\tcase (ModifierKeys.Control, Key.V): m.Paste(); break;\n\t\t\tcase (0, Key.Escape): m.Uncut(); break;\n\t\t\tdefault: return;\n\t\t\t}\n\t\t\te.Handled = true;\n\t\t}\n\n\t\tprotected override void OnContextMenuOpening(ContextMenuEventArgs e) {\n\t\t\tApp.Model._ContextMenu();\n\t\t}\n\n\t\tpublic new FileNode[] SelectedItems => base.SelectedItems.Cast<FileNode>().ToArray();\n\n\t\t#region drag-drop\n\n\t\tprivate void _ItemDragStart(TVItemEventArgs e) {\n\t\t\tif (e.Button != MouseButton.Left) return;\n\t\t\t//if(e.Item.IsFolder && e.Item.IsExpanded) Expand(e.Index, false);\n\t\t\tvar a = IsSelected(e.Index) ? SelectedItems : new FileNode[] { e.Item as FileNode };\n\t\t\tDragDropFiles = a;\n\t\t\tDragDrop.DoDragDrop(this, new DataObject(typeof(FileNode[]), a), DragDropEffects.Move | DragDropEffects.Copy);\n\t\t\tDragDropFiles = null;\n\t\t}\n\n\t\tpublic FileNode[] DragDropFiles { get; private set; }\n\n\t\tprotected override void OnDragOver(DragEventArgs e) {\n\t\t\te.Handled = true;\n\t\t\tbool can = _DragDrop(e, false);\n\t\t\tOnDragOver2(can);\n\t\t\tbase.OnDragOver(e);\n\t\t}\n\n\t\tprotected override void OnDrop(DragEventArgs e) {\n\t\t\te.Handled = true;\n\t\t\t_DragDrop(e, true);\n\t\t\tbase.OnDrop(e);\n\t\t}\n\n\t\tbool _DragDrop(DragEventArgs e, bool drop) {\n\t\t\tbool can;\n\t\t\tFileNode[] nodes = null;\n\t\t\tif (can = e.Data.GetDataPresent(typeof(FileNode[]))) {\n\t\t\t\tnodes = e.Data.GetData(typeof(FileNode[])) as FileNode[];\n\t\t\t\tGetDropInfo(out var d);\n\t\t\t\tif (d.targetItem is FileNode target) {\n\t\t\t\t\tbool no = false;\n\t\t\t\t\tforeach (FileNode v in nodes) {\n\t\t\t\t\t\tif (d.intoFolder) {\n\t\t\t\t\t\t\tno = v == target || target.IsDescendantOf(v);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tno = target.IsDescendantOf(v);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (no) break;\n\t\t\t\t\t}\n\t\t\t\t\tcan = !no;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tcan = e.Data.GetDataPresent(DataFormats.FileDrop);\n\t\t\t}\n\n\t\t\tif (can) {\n\t\t\t\t//convert multiple effects to single\n\t\t\t\tswitch (e.KeyStates & (DragDropKeyStates.ControlKey | DragDropKeyStates.ShiftKey)) {\n\t\t\t\tcase DragDropKeyStates.ControlKey: e.Effects &= DragDropEffects.Copy; break;\n\t\t\t\tcase DragDropKeyStates.ShiftKey: e.Effects &= DragDropEffects.Move; break;\n\t\t\t\tcase DragDropKeyStates.ControlKey | DragDropKeyStates.ShiftKey: e.Effects &= DragDropEffects.Link; break;\n\t\t\t\tdefault:\n\t\t\t\t\tif (e.Effects.Has(DragDropEffects.Move)) e.Effects = DragDropEffects.Move;\n\t\t\t\t\telse if (e.Effects.Has(DragDropEffects.Link)) e.Effects = DragDropEffects.Link;\n\t\t\t\t\telse if (e.Effects.Has(DragDropEffects.Copy)) e.Effects = DragDropEffects.Copy;\n\t\t\t\t\telse e.Effects = 0;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\te.Effects = 0;\n\t\t\t}\n\t\t\tif (e.Effects == 0) return false;\n\n\t\t\tif (drop) {\n\t\t\t\tvar files = nodes == null ? e.Data.GetData(DataFormats.FileDrop) as string[] : null;\n\t\t\t\tGetDropInfo(out var d);\n\t\t\t\tvar pos = d.intoFolder ? FNInsert.Last : (d.insertAfter ? FNInsert.After : FNInsert.Before);\n\t\t\t\tDispatcher.InvokeAsync(() => App.Model._DroppedOrPasted(nodes, files, new(d.targetItem as FileNode, pos), e.Effects == DragDropEffects.Copy));\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\t#endregion\n\t}\n}\n\n//Tested Jdenticon library that generates random icons. Works well, but the icons are too abstract and monotonic.\n//partial class FileNode\n//{\n//\tSystem.Drawing.Bitmap ITreeViewItem.Image {\n//\t\tget {\n//\t\t\tif (_bmp == null && 0 != Name.Like(true, \"Script14?.cs\", \"Script13?.cs\")) {\n//\t\t\t\tvar v = Identicon.FromValue(Name, size: 16);\n//\t\t\t\tv.Style = new IdenticonStyle { BackColor = Jdenticon.Rendering.Color.Transparent/*, ColorSaturation=0.7f, ColorLightness = new(0.1f, 0.5f)*/ };\n//\t\t\t\tusing var stream = v.SaveAsPng();\n//\t\t\t\t_bmp = System.Drawing.Bitmap.FromStream(stream) as System.Drawing.Bitmap;\n//\t\t\t}\n//\t\t\treturn _bmp;\n//\t\t}\n//\t}\n//\tSystem.Drawing.Bitmap _bmp;\n//}\n"
  },
  {
    "path": "Au.Editor/Files/Git.cs",
    "content": "using System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Media;\nusing System.Windows.Documents;\nusing System.Text.Json.Nodes;\nusing Au.Controls;\nusing System.Net.Http;\n\nnamespace LA;\n\nstatic partial class Git {\n\tstatic string _gitExe, //git.exe path\n\t\t_dir, //workspace dir\n\t\t_go; //git output. If failed, it is \"[exitcode] output\".\n\tstatic bool _isPrivateGit;\n\tconst string c_defaultGitignore = \"\"\"\n/.nuget/*/*\n/.interop/\n/exe/\n/dll/\n\n# Don't change these defaults\n!/.nuget/*/*.csproj\n/.compiled/\n/.temp/\n\n\"\"\";\n\t\n\tstatic bool _Start(bool setup = false) {\n\t\tif (_gitExe == null || setup || _dir != App.Model.WorkspaceDirectory) {\n\t\t\t_dir = App.Model.WorkspaceDirectory;\n\t\t\tif (!setup) if (!filesystem.exists(_dir + @\"\\.git\").Directory) return false;\n\t\t\tif (!_FindGit(out _gitExe, out _isPrivateGit)) return false;\n\t\t}\n\t\treturn true;\n\t}\n\t\n\tstatic bool _FindGit(out string path, out bool isPrivate) {\n\t\tvar gitDir = folders.ThisAppBS + \"Git\";\n\t\tpath = gitDir + @\"\\cmd\\git.exe\"; //or @\"\\mingw64\\bin\\git.exe\"\n\t\tif (isPrivate = filesystem.exists(path) && !Downloader.IsDirectoryInvalid(gitDir)) return true;\n\t\treturn null != (path = filesystem.searchPath(\"git.exe\") ?? filesystem.searchPath(folders.ProgramFiles + @\"Git\\cmd\\git.exe\"));\n\t}\n\t\n\t#region commands\n\t\n\tstatic bool _InitCommand() {\n\t\tbool ok = _Start();\n\t\tif (ok) {\n\t\t\tif (!_EnsureMain()) return false;\n\t\t\tApp.Model.Save.AllNowIfNeed();\n\t\t\t_CheckIgnored();\n\t\t} else if (t_autoBackup) {\n\t\t\tprint.it(\"Auto-backup error. To fix it use menu File > Git > Git setup.\");\n\t\t} else {\n\t\t\tSetup();\n\t\t}\n\t\treturn ok;\n\t\t\n\t\tstatic bool _EnsureMain() {\n\t\t\tif (!gits(\"branch --show-current\")) return false;\n\t\t\tif (_go != \"main\") {\n\t\t\t\tif (t_autoBackup) { print.it(\"Auto-backup error: not main branch.\"); return false; }\n\t\t\t\tif (!dialog.showOkCancel(\"Switch to main branch?\", $\"The Git command can work only with the main branch.\\nCurrent branch is '{_go}'.\", owner: App.Hmain)) return false;\n\t\t\t\tif (!git(\"switch main\")) return false;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\tstatic void _CheckIgnored() {\n\t\t\tbool i1 = false, i2 = false, i3 = false;\n\t\t\tif (gits(@\"check-ignore .compiled/* .temp/* .nuget/*/*.csproj\")) {\n\t\t\t\ti1 = _go.Contains(\".comp\");\n\t\t\t\ti2 = _go.Contains(\".temp\");\n\t\t\t\ti3 = _go.Contains(\".nuge\");\n\t\t\t}\n\t\t\tif (!i1 || !i2 || i3) {\n\t\t\t\tstring file = _dir + @\"\\.gitignore\", s;\n\t\t\t\tif (filesystem.exists(file)) {\n\t\t\t\t\ts = filesystem.loadText(file) + \"\\r\\n\";\n\t\t\t\t\tif (!i1) s += \"/./compiled\\r\\n\";\n\t\t\t\t\tif (!i2) s += \"/.temp/\\r\\n\";\n\t\t\t\t\tif (i3) s += \"!/.nuget/*/*.csproj\\r\\n\";\n\t\t\t\t} else {\n\t\t\t\t\ts = c_defaultGitignore;\n\t\t\t\t}\n\t\t\t\tfilesystem.saveText(file, s);\n\t\t\t}\n\t\t}\n\t}\n\t\n\tstatic void _Thread(Action func) {\n\t\tif (_InitCommand()) run.thread(func);\n\t}\n\t\n\tstatic bool _NotThread => App.IsMainThread;\n\t\n#if DEBUG\n\tpublic static void Test() {\n\t\tif (_NotThread) { _Thread(Test); return; }\n\t\t\n\t\t//gita(\"\");\n\t}\n#endif\n\t\n\tpublic static void Status() {\n\t\tif (_NotThread) { _Thread(Status); return; }\n\t\t\n\t\tprint.it(_GetStatus(true));\n\t}\n\t\n\tstatic bool _Commit(string m = null) {\n\t\tif (m == null) {\n\t\t\tm = \"backup\";\n\t\t\tdo {\n\t\t\t\tif (!dialog.showInput(out m, \"Git commit\", \"Message\", editText: m, owner: App.Hmain,\n\t\t\t\t\tfooter: new(\"Print:    <a href=\\\"status\\\">status</a>\", _Link)\n\t\t\t\t\t)) return false;\n\t\t\t} while (m.NE());\n\t\t}\n\t\tm = m.Replace(\"\\\"\", \"''\");\n\t\t\n\t\tif (!git(\"add .\")) return false;\n\t\tif (!git($\"commit -m \\\"{m}\\\"\")) return false;\n\t\treturn true;\n\t}\n\t\n\tpublic static void Commit(bool autoBackup = false, int autoBackupWorkspaceSN = 0) {\n\t\tif (_NotThread) { _Thread(() => Commit(autoBackup, autoBackupWorkspaceSN)); return; }\n\t\t\n\t\tif (autoBackup && App.Model.WorkspaceSN != autoBackupWorkspaceSN) return; //this code runs async with a delay and in new thread. During that time can be loaded another workspace.\n\t\tt_autoBackup = autoBackup;\n\t\ttry {\n\t\t\tvar gs = _GetStatus();\n\t\t\tif (!gs.CanCommit) _Print(\"Nothing to commit.\");\n\t\t\telse if (_Commit(autoBackup ? \"auto-backup\" : null)) _Print(\"==== DONE ====\");\n\t\t\telse if (autoBackup) print.it(\"Auto-backup failed. Try menu File > Git > Commit.\"); //else git() prints errors\n\t\t}\n\t\tcatch (Exception e1) { print.it((autoBackup ? \"Auto-backup: \" : null) + \"Git commit failed\", e1); }\n\t\tfinally { t_autoBackup = false; }\n\t\t\n\t\tstatic void _Print(string s) {\n\t\t\tif (!t_autoBackup) print.it(s);\n\t\t}\n\t}\n\t[ThreadStatic] static bool t_autoBackup;\n\t\n\tpublic static void Push() {\n\t\tif (_NotThread) { _Thread(Push); return; }\n\t\t\n\t\tvar gs = _GetStatus(); //note: don't use _GetStatus(true) to detect whether need to push. In some cases it can't detect local and remote differences, eg after modifying the local commit history. Also makes slower.\n\t\tif (gs.CanCommit) if (!_Commit()) return;\n\t\tif (!git(\"push\")) {\n\t\t\tgs = _GetStatus(true);\n\t\t\tstring s1, s2;\n\t\t\tif (gs.behind > 0) {\n\t\t\t\ts1 = $\"Exist {gs.ahead} local and {gs.behind} remote new different commits.\";\n\t\t\t\ts2 = \"0 Cancel|1 Overwrite remote\\ngit push --force|2 Pull instead...\\nYou can keep local or remote commits\";\n\t\t\t} else { //eg after modifying the local commit history\n\t\t\t\ts1 = \"The safe 'git push' command failed.\";\n\t\t\t\ts2 = \"0 Cancel|1 Try 'git push --force'\\nIt overwrites remote commits.\";\n\t\t\t}\n\t\t\tswitch (dialog.show(\"Git push\", s1, s2, DFlags.CommandLinks, DIcon.Warning, owner: App.Hmain)) {\n\t\t\tcase 1:\n\t\t\t\tif (!git(\"push --force\")) return;\n\t\t\t\tbreak;\n\t\t\tcase 2:\n\t\t\t\tPull();\n\t\t\t\treturn;\n\t\t\tdefault: return;\n\t\t\t}\n\t\t} else if (_go == \"Everything up-to-date\") {\n\t\t\tprint.it(\"==== Nothing to push ====\");\n\t\t\treturn;\n\t\t}\n\t\tprint.it(\"==== DONE ====\");\n\t}\n\t\n\tpublic static void Pull() {\n\t\tif (_NotThread) { _Thread(Pull); return; }\n\t\t\n\t\tif (!git(\"fetch\")) return;\n\t\tvar gs = _GetStatus();\n\t\tif (gs.behind == 0) { print.it(\"==== No new remote commits ====\"); return; }\n\t\t\n\t\t//refuse to pull if there are uncommitted changes\n\t\tbool stashed = false;\n\t\tif (gs.CanCommit) {\n\t\t\t//changes in the .state dir are likely. They are not important and can be dropped. The .toolbars dir is similar.\n\t\t\t//\tIf uncommitted changes are only in these dirs: Now stash. Finally drop if successful, else restore.\n\t\t\tif (!gs.changes.Any(o => !(o.Eq(3, \".state/\", true) || o.Eq(3, \".toolbars/\", true)))) stashed = gits(\"stash\");\n\t\t\tif (!stashed) {\n\t\t\t\tif (1 != dialog.show(\"Before git pull\", \"Need to commit, else new local changes would be lost. Commit now?\",\n\t\t\t\t\t\"1 OK|Cancel\", owner: App.Hmain,\n\t\t\t\t\tfooter: new(\"Print:    <a href=\\\"status\\\">status</a>\", _Link)\n\t\t\t\t\t)) return;\n\t\t\t\tif (!_Commit()) return;\n\t\t\t\tgs.ahead++;\n\t\t\t}\n\t\t}\n\t\t\n\t\tbool stashDrop = false, push = false;\n\t\ttry {\n\t\t\tif (gs.ahead > 0) {\n\t\t\t\t//note: don't merge. It can damage files, eg files.xml.\n\t\t\t\tint button = dialog.show(\"Git pull\", $\"Exist {gs.ahead} local and {gs.behind} remote new different commits.                                              \",\n\t\t\t\t\t\"0 Cancel|1 Keep remote\\nDiscards new local changes and applies remote changes.\\nAt first backups the new local commits in a new branch.\\nFinally reloads the workspace.|2 Keep local\\nDoes not modify the workspace.\\nInserts the new remote commits before the new local commits.\",\n\t\t\t\t\tflags: DFlags.CommandLinks | DFlags.ExpandDown, icon: DIcon.Warning, owner: App.Hmain,\n\t\t\t\t\texpandedText: \"You can click Cancel and use eg GitHubDesktop to pull. It allows to merge remote changes and resolve possible conflicts. Be careful, it can damage some files.\\n\\nTo avoid this, always pull before making changes in the workspace.\",\n\t\t\t\t\tfooter: new(\"Print:    <a href=\\\"diff\\\">diff for 'keep remote'</a>    <a href=\\\"diffR\\\">diff for 'keep local'</a>\", _Link)\n\t\t\t\t\t);\n\t\t\t\t\n\t\t\t\tif (button == 1) {\n\t\t\t\t\tif (!git($\"branch {DateTime.Now.ToString(\"yyyy-MM-dd--HH-mm-ss\")}\")) return;\n\t\t\t\t\tif (!git(\"reset --hard origin/main\")) return;\n\t\t\t\t} else if (button == 2) {\n\t\t\t\t\tif (!git(\"merge -s ours\")) return;\n\t\t\t\t\tpush = true;\n\t\t\t\t} else return;\n\t\t\t} else {\n\t\t\t\tDControls dc = new() { Checkbox = \"Backup workspace files\", IsChecked = !ScriptEditor.IsPortable };\n\t\t\t\tif (1 != dialog.show(\"Git pull\", \"Will update your local repo and workspace files to match the remote repo.\\nFinally will reload the workspace.\",\n\t\t\t\t\t\"1 OK|Cancel\", owner: App.Hmain, controls: dc)) return;\n\t\t\t\tif (dc.IsChecked) _BackupWorkspace();\n\t\t\t\tif (!git(\"merge --ff-only\")) return;\n\t\t\t\tif (_go.Starts(\"Already up to date\")) { print.it(\"==== No new remote commits ====\"); return; }\n\t\t\t}\n\t\t\tstashDrop = !push;\n\t\t}\n\t\tfinally {\n\t\t\tif (stashed) {\n\t\t\t\tif (stashDrop) gits(\"stash drop\"); else gits(\"stash pop\");\n\t\t\t}\n\t\t}\n\t\tprint.it(\"==== DONE ====\");\n\t\t\n\t\tif (push) {\n\t\t\t//print.it(\"Now you can push to GitHub. It will add the new local commits to the GitHub repo.\");\n\t\t\tif (dialog.showYesNo(\"Now push to GitHub?\", \"It will add the new local commits to the GitHub repo.\\nOr you can click No and push later.\", owner: App.Hmain)) {\n\t\t\t\tif (!git(\"push\")) return;\n\t\t\t\tprint.it(\"==== DONE ====\");\n\t\t\t}\n\t\t} else {\n\t\t\tApp.Dispatcher.InvokeAsync(ReloadWorkspace);\n\t\t}\n\t}\n\t\n\tpublic static void RunGui() {\n\t\tif (!_InitCommand()) return;\n\t\t\n\t\tvar s = Microsoft.Win32.Registry.GetValue(@\"HKEY_CLASSES_ROOT\\x-github-client\\shell\\open\\command\", \"\", null) as string;\n\t\tif (s != null) {\n\t\t\ts = s.Split(' ')[0].Trim('\"');\n\t\t\tif (!filesystem.exists(s)) s = null;\n\t\t}\n\t\ts ??= folders.LocalAppData + @\"GitHubDesktop\\GitHubDesktop.exe\";\n\t\tif (filesystem.exists(s)) run.itSafe(s, null, RFlags.InheritAdmin); else print.it(\"<>GitHubDesktop not found. <link https://desktop.github.com/>Download<>.\");\n\t}\n\t\n\tpublic static void RunCmd() {\n\t\tif (!_InitCommand()) return;\n\t\t\n\t\tstring oldPath = null;\n\t\tif (_isPrivateGit) Environment.SetEnvironmentVariable(\"PATH\", pathname.getDirectory(_gitExe) + \";\" + (oldPath = Environment.GetEnvironmentVariable(\"PATH\")));\n\t\trun.itSafe(folders.System + \"cmd.exe\", null, RFlags.InheritAdmin, _dir);\n\t\tif (_isPrivateGit) Environment.SetEnvironmentVariable(\"PATH\", oldPath);\n\t}\n\t\n\tpublic static void WorkspaceFolder() {\n\t\trun.itSafe(App.Model.WorkspaceDirectory);\n\t}\n\t\n\tpublic static void ReloadWorkspace() {\n\t\tFilesModel.LoadWorkspace(App.Model.WorkspaceDirectory);\n\t}\n\t\n\tpublic static void Signout() {\n\t\tif (_NotThread) { _Thread(Signout); return; }\n\t\t\n\t\tif (!gits(\"config --get remote.origin.url\")) return;\n\t\t_go.RxMatch(@\"^https://github.com/(.+?)/\", 1, out string user);\n\t\tif (!git(\"credential reject\", stdin: $\"protocol=https\\nhost=github.com\\nusername={user}\\n\\n\")) return;\n\t\tprint.it(\"==== DONE ====\");\n\t}\n\t\n\tpublic static void Maintenance() {\n\t\tif (!_InitCommand()) return;\n\t\t\n\t\tvar w = new KDialogWindow() { Title = \"Git repository maintenance\", ShowInTaskbar = false, Owner = App.Wmain };\n\t\tvar b = new wpfBuilder(w).WinSize(400);\n\t\t\n\t\tvar info = new System.Windows.Documents.Run();\n\t\t_Info();\n\t\tb.R.xAddInfoBlockF($\"\"\"\nThe local repository is folder <a {() => { var s1 = _dir + @\"\\.git\"; filesystem.setAttributes(s1, FileAttributes.Hidden, false); run.selectInExplorer(s1); }}>.git</a> in the workspace folder.\n{info}\nUse this tool to make it smaller or delete old commits or branches.\nThis tool modifies only the .git folder, not workspace files.\n\"\"\");\n\t\t\n\t\tb.R.xAddGroupSeparator(\"Safe tasks\");\n\t\tb.R.AddButton(\"Run 'git gc' to compact the folder\", async _ => {\n\t\t\tw.IsEnabled = false;\n\t\t\tawait Task.Run(() => _Gc());\n\t\t\t_Info();\n\t\t\tw.IsEnabled = true;\n\t\t}).Tooltip(\"Collects garbage. Packs and compresses files.\\nDoes not change the history, branches, etc.\");\n\t\t\n\t\tb.R.xAddGroupSeparator(\"Destructive tasks\");\n\t\tb.R.AddButton(\"Delete branch...\", _ => _Branches());\n\t\tb.R.AddButton(\"Delete old commits...\", _ => _DeleteOldCommits());\n\t\t\n\t\tb.R.xAddGroupSeparator(\"Info\");\n\t\tb.R.AddButton(\"Print workspace folder sizes\", _ => _PrintFolderSizes()).Tooltip(\"Print sizes of workspace folders except .git, .compiled and .temp.\\nThis info can be useful when editing file .gitignore.\");\n\t\t\n\t\tb.End();\n\t\tw.Show(); //not ShowDialog\n\t\t\n\t\tvoid _Info() {\n\t\t\tint nFiles = 0, nDirs = 0;\n\t\t\tlong size = filesystem.enumerate(_dir + \"\\\\.git\", FEFlags.AllDescendants | FEFlags.IgnoreInaccessible).Sum(o => { if (o.IsDirectory) { nDirs++; return 0; } nFiles++; return o.Size; });\n\t\t\tgits(\"rev-list --count HEAD\");\n\t\t\tinfo.Text = $\"Size {size / 1024 / 1024} MB. Contains {nFiles} files and {nDirs} folders.\\nThe main branch contains {_go} commits.\";\n\t\t}\n\t\t\n\t\tstatic void _Gc() {\n\t\t\t//something adds readonly attribute to many dirs in .git/objects. Then git gc cannot delete them.\n\t\t\tforeach (var v in filesystem.enumDirectories(_dir + @\"\\.git\\objects\")) {\n\t\t\t\t//print.it(v.Attributes, v.Name);\n\t\t\t\tif (v.Attributes.Has(FileAttributes.ReadOnly)) {\n\t\t\t\t\ttry { File.SetAttributes(v.FullPath, FileAttributes.Directory); } catch { }\n\t\t\t\t}\n\t\t\t}\n\t\t\tgit(\"reflog expire --all --expire=now\"); //need after deleting old commits, else does not make smaller\n\t\t\tgit(\"gc --aggressive --prune=now\");\n\t\t\t//run.it(\"cmd.exe\", $\"/c \\\"{gitExe}\\\" gc --aggressive --prune=now\", RFlags.InheritAdmin | RFlags.WaitForExit, _dir);\n\t\t\tprint.it(\"==== DONE ====\");\n\t\t}\n\t\t\n\t\tvoid _Branches() {\n\t\t\tif (!gits(\"branch\")) return;\n\t\t\tvar m = new popupMenu();\n\t\t\tforeach (var v in _go.Lines()) {\n\t\t\t\tif (v.Starts('*')) m.Add(v).IsDisabled = true;\n\t\t\t\telse m[v.TrimStart()] = _DeleteBranch;\n\t\t\t}\n\t\t\tm.Show();\n\t\t\t\n\t\t\tvoid _DeleteBranch(PMItem mi) {\n\t\t\t\tvar s = mi.Text;\n\t\t\t\tif (1 != dialog.show(\"Delete branch?\", s + \"\\n\\nThis action cannot be undone.\", \"0 Cancel|1 Delete\", icon: DIcon.Warning, owner: w)) return;\n\t\t\t\tif (!git($\"branch -D \\\"{s}\\\"\")) return;\n\t\t\t\t_Info();\n\t\t\t\tprint.it(\"==== DONE ====\");\n\t\t\t}\n\t\t}\n\t\t\n\t\tasync void _DeleteOldCommits() {\n\t\t\tif (1 != dialog.show(\"Deleting old commits\", \"This will delete all commits except the last.\\nThen will push to GitHub.\\n\\nThis action cannot be undone.\", \"0 Cancel|1 Delete\", icon: DIcon.Warning, owner: w)) return;\n\t\t\tw.IsEnabled = false;\n\t\t\tawait Task.Run(() => {\n\t\t\t\tif (!git(\"checkout --orphan m7j9u447352k\")) return;\n\t\t\t\tif (!git(\"add .\")) return;\n\t\t\t\tif (!git($\"commit -m \\\"Deleted old commits\\\"\")) return;\n\t\t\t\tif (!git(\"branch -D main\")) return;\n\t\t\t\tif (!git(\"branch -m main\")) return;\n\t\t\t\tgit(\"push --force\");\n\t\t\t\t_Gc();\n\t\t\t});\n\t\t\t_Info();\n\t\t\tw.IsEnabled = true;\n\t\t\t\n\t\t\t//Also tried to delete old commits older than a selected commit. But could not find how to do it correctly.\n\t\t\t//\tGit rebase fails (errors etc).\n\t\t\t//\tGit clone shallow works, but does not delete remote commits.\n\t\t}\n\t\t\n\t\tstatic void _PrintFolderSizes() {\n\t\t\tFileTree.PrintSizes(_dir, true, .1, dirFilter: o => !(o.Level is 0 && o.Name.Lower() is \".git\" or \".compiled\" or \".temp\"));\n\t\t}\n\t}\n\t\n\tpublic static void AutoBackup(bool now) {\n\t\tif (!App.Model.UserSettings.gitBackup) return;\n\t\tif (!CodeInfo.IsReadyForEditing) return;\n\t\tif (!now && _autoBackupTime != 0 && Environment.TickCount64 - _autoBackupTime < 1000 * 60 * 60 * 4) return; //4 hours\n\t\t_autoBackupTime = Environment.TickCount64;\n\t\tvar wsSN = App.Model.WorkspaceSN;\n\t\ttimer.after(500, _ => {\n\t\t\t//print.it(\"auto-backup\", DateTime.Now);\n\t\t\tCommit(autoBackup: true, autoBackupWorkspaceSN: wsSN);\n\t\t});\n\t}\n\tstatic long _autoBackupTime;\n\t\n\t#endregion\n\t\n\t#region setup\n\t\n\tpublic static bool IsReady => _Start();\n\t\n\tpublic static void Setup() {\n\t\tnew _DSetup().ShowDialog();\n\t}\n\t\n\tclass _DSetup : KDialogWindow {\n\t\tpublic _DSetup() {\n\t\t\t_Start(setup: true);\n\t\t\t\n\t\t\tInitWinProp(\"Git setup\", App.Wmain);\n\t\t\tvar b = new wpfBuilder(this).WinSize(500).Columns(0, -1, 0);\n\t\t\tvar panel = b.Panel;\n\t\t\tTextBox tUrl = null;\n\t\t\t\n\t\t\tb.R.StartGrid();\n\t\t\tAction lHelp = () => HelpUtil.AuHelp(\"editor/Git, backup, sync\"),\n\t\t\t\tlGithub = () => run.itSafe(tUrl.TextOrNull() ?? \"https://github.com\");\n\t\t\tb.Add<TextBlock>().FormatText($\"<a {lHelp}>Help</a>    <a href='{_dir}'>Workspace folder</a>    <a {lGithub}>GitHub</a>\").Align(y: VerticalAlignment.Center);\n\t\t\tb.End();\n\t\t\t\n\t\t\tb.R.xAddGroupSeparator(\"GitHub repository\");\n\t\t\tvar url = _GetURL();\n\t\t\tb.R.Add<AdornerDecorator>(\"URL\", out _).Child().Add(out tUrl, url).Watermark(\"https://github.com/owner/repo\")\n\t\t\t\t.Validation(o => !_ParseURL(tUrl.Text, out _, out _) ? (tUrl.Text.NE() ? \"URL empty\" : \"URL must be like https://github.com/owner/repo\") : null);\n\t\t\t\n\t\t\tb.R.xAddGroupSeparator(\"Git\");\n\t\t\tCancellationTokenSource cts = null;\n\t\t\tButton bGitInstall = null;\n\t\t\tb.R.Add(out TextBlock tGitStatus).Span(2).AddButton(out bGitInstall, \"\", _InstallGit)\n\t\t\t\t.Validation(o => !_FindGit(out _, out _) ? \"Git not installed\" : null);\n\t\t\t_SetGitControlText();\n\t\t\tb.Window.Closed += (_, _) => { cts?.Cancel(); };\n\t\t\t\n\t\t\tb.R.AddSeparator();\n\t\t\tb.R.Add(\"OK\", out ComboBox cbOK).Items(filesystem.exists(_dir + @\"\\.git\").Directory ? \"Update local settings\" : \"Create local repository linked with the still empty GitHub repository\", \"Clone (download) the GitHub repository to new workspace\");\n\t\t\tb.R.AddOkCancel();\n\t\t\tb.End();\n\t\t\t\n\t\t\tb.OkApply += async e => {\n\t\t\t\te.Cancel = true;\n\t\t\t\tApp.Model.UserSettings.gitUrl = tUrl.Text;\n\t\t\t\t_Start(setup: true); //may need to set _gitExe\n\t\t\t\tvar w = b.Window.Hwnd();\n\t\t\t\tb.Window.IsEnabled = false; //disables controls but allows to close the window, unlike b.Window.Hwnd().Enable(false);. Git may hang, eg when auth fails on my vmware Win7.\n\t\t\t\tbool ok = false, clone = cbOK.SelectedIndex == 1;\n\t\t\t\ttry {\n\t\t\t\t\tok = await Task.Run(() => _OkTask(clone));\n\t\t\t\t}\n\t\t\t\tcatch (Exception e1) { print.it(e1); }\n\t\t\t\tif (ok) b.Window.Close(); else b.Window.IsEnabled = true;\n\t\t\t};\n\t\t\t\n\t\t\tasync void _InstallGit(WBButtonClickArgs k) {\n\t\t\t\tk.Button.IsEnabled = false;\n\t\t\t\ttry {\n\t\t\t\t\tusing var dl = new Downloader();\n\t\t\t\t\tif (dl.PrepareDirectory(folders.ThisAppBS + \"Git\")) {\n\t\t\t\t\t\t//get URL of the latest mingit zip\n\t\t\t\t\t\ttGitStatus.Text = \"Getting the download URL (GitHub)\";\n\t\t\t\t\t\tvar r = await _GithubGetAsync($\"repos/git-for-windows/git/releases/latest\");\n\t\t\t\t\t\tr = r[\"assets\"].AsArray().First(o => ((string)o[\"name\"]).RxIsMatch(@\"(?i)MinGit-[\\d\\.]+-64-bit.zip\")); //note: not busybox; it's smaller and worked, but now fails.\n\t\t\t\t\t\tstring url = (string)r[\"browser_download_url\"];\n\t\t\t\t\t\t\n\t\t\t\t\t\tcts = new();\n\t\t\t\t\t\tawait dl.DownloadAndExtract(url, tGitStatus, cts.Token);\n\t\t\t\t\t\tcts = null;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (Exception ex) { print.warning(ex); }\n\t\t\t\t_SetGitControlText();\n\t\t\t\tk.Button.IsEnabled = true;\n\t\t\t}\n\t\t\t\n\t\t\tvoid _SetGitControlText() {\n\t\t\t\tbool found = _FindGit(out _, out bool isPrivate);\n\t\t\t\ttGitStatus.Text = !found ? \"Git not installed\" : isPrivate ? \"Private Git is installed and will be used\" : \"Shared Git found and will be used\";\n\t\t\t\tbGitInstall.Content = isPrivate ? \"Update private Git\" : \"Install private Git\";\n\t\t\t}\n\t\t}\n\t\t\n\t\tprotected override void OnSourceInitialized(EventArgs e) {\n\t\t\tApp.Model.UnloadingThisWorkspace += Close;\n\t\t\tbase.OnSourceInitialized(e);\n\t\t}\n\t\t\n\t\tprotected override void OnClosed(EventArgs e) {\n\t\t\tApp.Model.UnloadingThisWorkspace -= Close;\n\t\t\tbase.OnClosed(e);\n\t\t}\n\t\t\n\t\tstatic bool _OkTask(bool clone) {\n\t\t\tvar url = App.Model.UserSettings.gitUrl;\n\t\t\tif (!url.Ends(\".git\")) url += \".git\";\n\t\t\t\n\t\t\tif (clone) {\n\t\t\t\tvar dir2 = pathname.makeUnique(_dir, isDirectory: true);\n\t\t\t\tbool cloned = git($\"clone \\\"{url}\\\" \\\"{dir2}\\\"\");\n\t\t\t\tif (cloned && !(cloned = FilesModel.IsWorkspaceDirectoryOrZip(dir2, out _))) print.it(\"Error. The GitHub repository does not contain a workspace.\");\n\t\t\t\tif (!cloned) { filesystem.delete(dir2); return false; }\n\t\t\t\t\n\t\t\t\tvar dir3 = _dir; _dir = dir2;\n\t\t\t\ttry { _Config(); }\n\t\t\t\tfinally { _dir = dir3; }\n\t\t\t\t\n\t\t\t\tPanels.Output.Scintilla.AaTags.AddLinkTag(\"+openWorkspace\", s => FilesModel.LoadWorkspace(dir2));\n\t\t\t\tprint.it($\"<>The GitHub repository has been cloned to <link {dir2}>new workspace folder<>. Now you can <+openWorkspace {dir2}>open the new workspace<>. Also you may want to copy some files from <link {_dir}>old workspace<>.\");\n\t\t\t} else {\n\t\t\t\tvar file1 = _dir + \"\\\\.gitignore\";\n\t\t\t\tif (!filesystem.exists(file1)) filesystem.saveText(file1, c_defaultGitignore);\n\t\t\t\t\n\t\t\t\tvar file2 = _dir + \"\\\\.gitattributes\";\n\t\t\t\tif (!filesystem.exists(file2)) filesystem.saveText(file2, \"\"\"\n* -text\n/.state/* binary\n\n\"\"\");\n\t\t\t\t\n\t\t\t\tbool localExists = filesystem.exists(_dir + @\"\\.git\").Directory;\n\t\t\t\tif (localExists && gits(\"remote get-url origin\") && _go == url) return true;\n\t\t\t\t\n\t\t\t\t//repo exists?\n\t\t\t\tif (!git($\"ls-remote {url}\")) return false;\n\t\t\t\t//rejected: try to create repo now.\n\t\t\t\t//\tTo get token, see the commented out _GithubGet overload.\n\t\t\t\t\n\t\t\t\tif (localExists) {\n\t\t\t\t\tif (!git(\"remote set-url origin \" + url)) return false;\n\t\t\t\t} else {\n\t\t\t\t\t//repo empty?\n\t\t\t\t\tif (!_go.NE()) {\n\t\t\t\t\t\tprint.it(\"Error. The GitHub repository must be empty (0 commits, no readme etc). Else it can only be cloned.\");\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\t//repo private?\n\t\t\t\t\t_ParseURL(url, out var owner, out var repo); //info: the dialog validates the URL format\n\t\t\t\t\tvar r = _GithubGet($\"users/{owner}/repos\");\n\t\t\t\t\tif (r.AsArray().Any(v => ((string)v[\"name\"]).Eqi(repo))) {\n\t\t\t\t\t\tprint.it(\"Error. The GitHub repository must be private. You can make it public later if really need.\");\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tif (!git(\"init -b main\")) return false;\n\t\t\t\t\tif (!git(\"remote add origin \" + url)) return false;\n\t\t\t\t\tif (!git(\"config push.autoSetupRemote true\")) return false;\n\t\t\t\t\t_Config();\n\t\t\t\t\tprint.it(\"<>Local Git repository has been created. Now you can use the Git menu to make backups etc.  [<help editor/git, backup, sync>Help<>]\");\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t\t\n\t\t\tvoid _Config() {\n\t\t\t\tgit(\"config core.autocrlf false\");\n\t\t\t\tgit(\"config core.quotepath off\");\n\t\t\t\t\n\t\t\t\tif (!gits(\"config --get user.name\") || _go.NE()) git(\"config user.name \\\"unknown\\\"\");\n\t\t\t\tif (!gits(\"config --get user.email\") || _go.NE()) git(\"config user.email \\\"unknown@unknown.com\\\"\");\n\t\t\t}\n\t\t}\n\t\t\n\t\tstatic bool _ParseURL(string url, out string owner, out string repo) {\n\t\t\tif (!url.RxMatch(@\"^https://github.com/([[:alnum:]\\-]+)/([\\w\\-\\.]+?)(?:\\.git)?$\", out var m)) { owner = repo = null; return false; }\n\t\t\towner = m[1].Value;\n\t\t\trepo = m[2].Value;\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\tstatic string _GetURL() {\n\t\t\tif (_gitExe != null && gits(\"config --get remote.origin.url\") && !_go.NE()) return _go.Ends(\".git\", true) ? _go[..^4] : _go;\n\t\t\treturn App.Model.UserSettings.gitUrl;\n\t\t}\n\t\t\n\t\tstatic JsonNode _GithubGet(string endpoint) {\n\t\t\tvar r = internet.http.Get($\"https://api.github.com/{endpoint}\", headers: [\"Accept: application/vnd.github+json\", \"X-GitHub-Api-Version: 2022-11-28\"]);\n\t\t\treturn r.Json();\n\t\t}\n\t\t\n\t\tstatic async Task<JsonNode> _GithubGetAsync(string endpoint) {\n\t\t\tvar m = new HttpRequestMessage(HttpMethod.Get, $\"https://api.github.com/{endpoint}\");\n\t\t\tm.Headers.AddMany([\"Accept: application/vnd.github+json\", \"X-GitHub-Api-Version: 2022-11-28\"]);\n\t\t\tvar r = await internet.http.SendAsync(m);\n\t\t\treturn r.Json();\n\t\t}\n\t\t\n\t\t//not used, not finished, just shows how to get oauth token to use GitHub features where need it\n\t\t//static JsonNode _GithubGet(string endpoint, string user) {\n\t\t//\tif (!gits(\"credential fill\", stdin: $\"protocol=https\\nhost=github.com\\nusername={user}\\n\\n\")) throw new AuException();\n\t\t//\tif (!_go.RxMatch(@\"\\Rpassword=(.+)\", 1, out string token)) throw new AuException();\n\t\t\n\t\t//\tvar ah = new[] { \"Accept: application/vnd.github+json\", \"X-GitHub-Api-Version: 2022-11-28\", $\"Authorization: Bearer {token}\" };\n\t\t//\tvar r = internet.http.Get($\"https://api.github.com/{endpoint}\", headers: ah);\n\t\t//\treturn r.Json();\n\t\t//}\n\t}\n\t\n\t#endregion\n\t\n\t#region util\n\t\n\t/// <summary>\n\t/// What should <b>git</b> print.\n\t/// </summary>\n\tenum _GP {\n\t\t/// <summary>Print blue and error. Default.</summary>\n\t\tError,\n\t\t/// <summary>Print everything.</summary>\n\t\tAll,\n\t\t/// <summary>Print blue only.</summary>\n\t\tBlue,\n\t\t/// <summary>Print nothing.</summary>\n\t\tSilent\n\t}\n\t\n\tstatic bool git(string s, _GP gp = _GP.Error, string stdin = null) {\n\t\tif (t_autoBackup) gp = _GP.Silent;\n\t\tbool silent = gp == _GP.Silent;\n\t\tif (!silent) print.it($\"<><c #4040FF>git {s}<>\");\n\t\tusing var c = new consoleProcess(_gitExe, s, _dir);\n\t\tif (stdin != null) c.Write(stdin);\n\t\t\n\t\tbool slow = !silent && 0 != s.Starts(false, \"clone\", \"add\", \"commit\", \"push\", \"fetch\", \"gc\", \"ls-remote\");\n\t\tvar t = slow ? timer2.after(3000, static o => { if (o.Tag == null) print.it(\"wait...\"); }) : null;\n\t\t\n\t\t_go = c.ReadAllText().Trim(\"\\r\\n\");\n\t\t\n\t\tif (t != null) { t.Tag = \"\"; t.Stop(); }\n\t\t\n\t\tint ec = c.ExitCode;\n\t\tbool ok = ec == 0;\n\t\tif (!ok) _go = $\"[{ec}] {_go}\";\n\t\tif (!_go.NE()) if (gp == _GP.All || (!ok && gp == _GP.Error)) _Print(_go, !ok);\n\t\treturn ok;\n\t\t\n\t\tstatic void _Print(string s, bool error) {\n\t\t\tvar color = error ? \"FF3300\" : \"206000\";\n\t\t\tprint.it($\"<><c #{color}><_>{s}</_><>\");\n\t\t}\n\t}\n\t\n\tstatic bool gits(string s, string stdin = null) => git(s, _GP.Silent, stdin);\n\t\n\tstatic bool gita(string s, string stdin = null) => git(s, _GP.All, stdin);\n\t\n\tstatic _Status _GetStatus(bool remoteUpdate = false) {\n\t\tif (remoteUpdate) git(\"remote update\"); //can be \"fetch\", but somehow I see \"remote update\" everywhere. Same speed (> 1.5 s). Both download everything including blobs, even \"fetch --dry-run\".\n\t\tgit(\"status -z --branch --no-renames\");\n\t\t\n\t\tint j = _go.IndexOf('\\0');\n\t\tstring s1 = _go[..j], s2 = j < _go.Length - 1 ? _go[++j..] : null;\n\t\tif (!s1.RxMatch(@\"^##[^\\[]+(?:\\[(?:ahead (\\d+))?(?:, )?(?:behind (\\d+))?\\])?\", out var m)) throw new AuException(\"Unexpected 'git status' output:\\r\\n\" + _go);\n\t\t\n\t\treturn new(m[1].Exists ? m[1].Value.ToInt() : 0, m[2].Exists ? m[2].Value.ToInt() : 0, s2?.Split('\\0', StringSplitOptions.RemoveEmptyEntries));\n\t}\n\t\n\trecord struct _Status(int ahead, int behind, string[] changes) {\n\t\tpublic bool CanCommit => changes != null;\n\t\t\n\t\tpublic override string ToString() {\n\t\t\tvar b = new StringBuilder($\"New commits: {ahead} local, {behind} remote.\\r\\n\");\n\t\t\tif (changes == null) {\n\t\t\t\tb.Append(\"No changes to be committed.\");\n\t\t\t} else {\n\t\t\t\tvar a = changes;\n\t\t\t\tfor (int i = 0; i < a.Length; i++) {\n\t\t\t\t\tvar s2 = a[i][..2];\n\t\t\t\t\tstring s = s2 switch {\n\t\t\t\t\t[_, 'D'] or ['D', ' '] => \"  deleted:  \",\n\t\t\t\t\t[_, '?'] or ['A', _] => \"  added:    \",\n\t\t\t\t\t[_, 'M'] or ['M', ' '] => \"  modified: \",\n\t\t\t\t\t\t_ => \"  \" + s2 + \":       \"\n\t\t\t\t\t};\n\t\t\t\t\ta[i] = s + a[i][2..];\n\t\t\t\t}\n\t\t\t\tb.AppendLine(\"Changes to be committed:\").AppendJoin(\"\\r\\n\", a.OrderBy(o => o));\n\t\t\t}\n\t\t\treturn b.ToString();\n\t\t}\n\t}\n\t\n\tstatic void _Link(DEventArgs e) {\n\t\tswitch (e.LinkHref) {\n\t\tcase \"status\": Status(); break;\n\t\tcase \"diff\": _Diff(true); break;\n\t\tcase \"diffR\": _Diff(false); break;\n\t\t}\n\t\t\n\t\tvoid _Diff(bool R) {\n\t\t\tvar s1 = R ? \"-R \" : \"\";\n\t\t\tgit($\"diff {s1}--compact-summary origin/main\");\n\t\t\tprint.it(_go);\n\t\t\tgit($\"diff {s1}origin/main\");\n\t\t\t_go = _go.RxReplace(@\"\\Rindex .+\", \"\", 1).RxReplace(@\"\\R--- .(.+)\\R\\+\\+\\+ .\\1\", \"\", 1);\n\t\t\tprint.it(_go);\n\t\t}\n\t}\n\t\n\tstatic bool _BackupWorkspace() {\n\t\tif (!gits(\"ls-tree -r HEAD --name-only\")) goto ge;\n\t\t\n\t\tvar zipFile = _dir + @\"\\.temp\\git backup.7z\";\n\t\tif (filesystem.exists(zipFile)) filesystem.delete(zipFile); else filesystem.createDirectoryFor(zipFile);\n\t\tusing (var tf = new TempFile()) {\n\t\t\tfilesystem.saveText(tf, _go);\n\t\t\tif (0 != run.console(out _, folders.ThisAppBS + @\"32\\7za.exe\", $@\"a \"\"{zipFile}\"\" -iw-@\"\"{tf}\"\"\", _dir)) goto ge;\n\t\t}\n\t\t\n\t\tprint.it($\"<>Created <explore {zipFile}>temporary backup<> of workspace files.\");\n\t\treturn true;\n\t\tge: print.it(\"Failed to backup workspace files.\");\n\t\treturn false;\n\t}\n\t\n\t#endregion\n}\n"
  },
  {
    "path": "Au.Editor/Files/Save.cs",
    "content": "namespace LA;\n\npartial class FilesModel {\n\tpublic class AutoSave {\n\t\tFilesModel _model;\n\t\tint _workspaceAfterS, _stateAfterS, _textAfterS;\n\t\tinternal bool LoadingState;\n\t\t\n\t\tpublic AutoSave(FilesModel model) {\n\t\t\t_model = model;\n\t\t\tApp.Timer1s += _Program_Timer1s;\n\t\t}\n\t\t\n\t\tpublic void Dispose() {\n\t\t\t_model = null;\n\t\t\tApp.Timer1s -= _Program_Timer1s;\n\t\t\t\n\t\t\t//must be all saved or unchanged\n\t\t\tDebug.Assert(_workspaceAfterS == 0);\n\t\t\tDebug.Assert(_stateAfterS == 0);\n\t\t\tDebug.Assert(_textAfterS == 0);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Async-saves workspace (files.xml).\n\t\t/// </summary>\n\t\tpublic void WorkspaceAsync() {\n\t\t\tif (PauseSaveWorkspace) { _workspaceAfterS = 0; return; }\n\t\t\tint afterS = 1;\n\t\t\tif (_workspaceAfterS < 1 || _workspaceAfterS > afterS) {\n\t\t\t\t_workspaceAfterS = afterS;\n\t\t\t\tApp.Dispatcher.InvokeAsync(WorkspaceNowIfNeed);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Used by filesystem sync.\n\t\t/// </summary>\n\t\tpublic bool PauseSaveWorkspace { get; set { field = value; _workspaceAfterS = 0; } }\n\t\t\n\t\t/// <summary>\n\t\t/// Sets timer to save state later, if not already set.\n\t\t/// </summary>\n\t\t/// <param name=\"afterS\">Timer time, seconds.</param>\n\t\tpublic void StateLater(int afterS = 30) {\n\t\t\tif (LoadingState) return;\n\t\t\tif (_stateAfterS < 1 || _stateAfterS > afterS) _stateAfterS = afterS;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Sets timer to save editor text later, if not already set.\n\t\t/// </summary>\n\t\t/// <param name=\"afterS\">Timer time, seconds.</param>\n\t\tpublic void TextLater(int afterS = 60) {\n\t\t\tif (_textAfterS < 1 || _textAfterS > afterS) _textAfterS = afterS;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// If files.xml is set to save (WorkspaceLater), saves it now.\n\t\t/// </summary>\n\t\tpublic void WorkspaceNowIfNeed() {\n\t\t\tif (_workspaceAfterS > 0) _SaveWorkspaceNow();\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// If state is set to save (StateLater), saves it now.\n\t\t/// </summary>\n\t\tpublic void StateNowIfNeed() {\n\t\t\tif (_stateAfterS > 0) _SaveStateNow();\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// If editor text is set to save (TextLater), saves it now.\n\t\t/// Also saves markers, folding, etc, unless onlyText is true.\n\t\t/// </summary>\n\t\tpublic void TextNowIfNeed(bool onlyText = false, bool closingDoc = false) {\n\t\t\tif (_textAfterS > 0) _SaveTextNow();\n\t\t\tif (onlyText) return;\n\t\t\tPanels.Editor?.SaveEditorData(closingDoc);\n\t\t}\n\t\t\n\t\tvoid _SaveWorkspaceNow() {\n\t\t\t_workspaceAfterS = 0;\n\t\t\tDebug.Assert(_model != null); if (_model == null) return;\n\t\t\tif (!_model._SaveWorkspaceNow()) _workspaceAfterS = 5; //if fails, retry later\n\t\t}\n\t\t\n\t\tvoid _SaveStateNow() {\n\t\t\t_stateAfterS = 0;\n\t\t\tDebug.Assert(_model != null);\n\t\t\t_model?._SaveStateNow();\n\t\t}\n\t\t\n\t\tvoid _SaveTextNow() {\n\t\t\t_textAfterS = 0;\n\t\t\tDebug.Assert(_model != null); if (_model == null) return;\n\t\t\tDebug.Assert(Panels.Editor.IsOpen);\n\t\t\tif (!Panels.Editor.SaveText()) _textAfterS = 300; //if fails, retry later\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Calls WorkspaceNowIfNeed, TextNowIfNeed, StateNowIfNeed, Panels.Bookmarks.SaveNowIfNeed, Panels.Debugg.SaveNowIfNeed.\n\t\t/// </summary>\n\t\tpublic void AllNowIfNeed() {\n\t\t\tWorkspaceNowIfNeed();\n\t\t\t_model.State.SuspendSave(true);\n\t\t\tTextNowIfNeed();\n\t\t\tStateNowIfNeed();\n\t\t\t_model.State.SuspendSave(false);\n\t\t\tPanels.Bookmarks.SaveNowIfNeed();\n\t\t\tPanels.Breakpoints.SaveNowIfNeed();\n\t\t}\n\t\t\n\t\tvoid _Program_Timer1s() {\n\t\t\tif (_workspaceAfterS > 0 && --_workspaceAfterS == 0) _SaveWorkspaceNow();\n\t\t\tif (_stateAfterS > 0 && --_stateAfterS == 0) _SaveStateNow();\n\t\t\tif (_textAfterS > 0 && --_textAfterS == 0) _SaveTextNow();\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Used only by the Save class.\n\t/// </summary>\n\tbool _SaveWorkspaceNow() {\n\t\treturn TryFileOperation(FOSync.FilesXml, () => { Root.SaveWorkspace(WorkspaceFile); });\n\t\t//XElement.Save exceptions are undocumented\n\t}\n\t\n\t/// <summary>\n\t/// Used only by the Save class.\n\t/// </summary>\n\tvoid _SaveStateNow() {\n\t\tState.FilesSave(_openFiles, Root.Descendants().Where(n => n.IsExpanded));\n\t}\n\t\n\t/// <summary>\n\t/// Called at the end of opening this workspace.\n\t/// </summary>\n\tpublic void LoadState(bool expandFolders = false, bool openFiles = false) {\n\t\ttry {\n\t\t\tSave.LoadingState = true;\n\t\t\t\n\t\t\tif (expandFolders) {\n\t\t\t\tforeach (var id in State.FilesGet(1))\n\t\t\t\t\tif (FindById(id) is { } f)\n\t\t\t\t\t\tf.SetIsExpanded(true);\n\t\t\t}\n\t\t\t\n\t\t\tif (openFiles) {\n\t\t\t\tforeach (var id in State.FilesGet(0)) {\n\t\t\t\t\tif (FindById(id) is { } f) _openFiles.Add(f);\n\t\t\t\t}\n\t\t\t\tif (_openFiles.Count == 0 || !SetCurrentFile(_openFiles[0])) _UpdateOpenFiles(null); //disable Previous command\n\t\t\t}\n\t\t}\n\t\tcatch (Exception ex) { Debug_.Print(ex); }\n\t\tfinally { Save.LoadingState = false; }\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Files/SyncWithFilesystem.cs",
    "content": "//#define USE_CHANGED_EVENTS\n\nusing System.Xml;\nusing System.Xml.Linq;\n\nnamespace LA;\n\npartial class FilesModel {\n\tWildcardList _ignoredPaths, _ignoredPaths2;\n\t_FileWatchers _syncWatchers;\n\t\n\tbool _IsPathIgnored(RStr itemPath, bool orAncestorDir = false) {\n\t\t_ignoredPaths ??= new(WSSett.syncfs_skip);\n\t\tif (_ignoredPaths.IsMatch(itemPath, true)) return true;\n\t\tif (orAncestorDir) {\n\t\t\t_ignoredPaths2 ??= new(_ignoredPaths.ListOfStrings.Where(o => !o.Ends('*')).Select(o => o + @\"\\*\").ToArray());\n\t\t\tif (_ignoredPaths2.IsMatch(itemPath, true)) return true;\n\t\t}\n\t\treturn false;\n\t}\n\t\n\t//CONSIDER: call when workspace loaded, even if LA hidden. Now does not sync until shown (_syncWatchers too). But is it important?\n\t/// <summary>\n\t/// Called when: 1. WorkspaceLoadedWithUI; 2. WSSett.syncfs_skip changed in Options.\n\t/// </summary>\n\tinternal async void SyncWithFilesystem_() {\n\t\t_ignoredPaths = new(WSSett.syncfs_skip);\n\t\t_ignoredPaths2 = null;\n\t\t\n\t\t//get the root and all folder links\n\t\t_RootDir[] rootDirs = Root.Descendants(andSelf: true)\n\t\t\t.Where(o => o.Id == 0 || (o.IsLink && o.IsFolder && filesystem.exists(o.FilePath).Directory))\n\t\t\t.Select(o => new _RootDir(o, o.ItemPath, o.FilePath))\n\t\t\t.ToArray();\n\t\t\n\t\t//gather all filesystem files and dirs. Can be slow. Async to avoid blocking the main thread.\n\t\tawait Task.Run(() => {\n\t\t\tforeach (var rd in rootDirs) {\n\t\t\t\tRelativePath rel = new(rd.itemPath);\n\t\t\t\trd.tree = _Dir(new(pathname.expand(rd.fullPath)));\n\t\t\t\t\n\t\t\t\t_DirTree _Dir(DirectoryInfo parent) {\n\t\t\t\t\t_DirTree t = new(parent.Name) { children = new() };\n\t\t\t\t\ttry {\n\t\t\t\t\t\tforeach (var v in parent.EnumerateFileSystemInfos()) {\n\t\t\t\t\t\t\tif (v.Attributes.Has(FileAttributes.Hidden | FileAttributes.System)) continue;\n\t\t\t\t\t\t\tvar relPath = rel.GetRelativePath(v.FullName);\n\t\t\t\t\t\t\tif (_IsPathIgnored(relPath)) continue;\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tif (v is DirectoryInfo di) {\n\t\t\t\t\t\t\t\tt.children.Add(_Dir(di));\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tt.children.Add(v.Name);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcatch (Exception ex) { //eg symlink target dir does not exist\n\t\t\t\t\t\tt.children = null; //don't remove descendant filenodes, because the symlink target may be restored later\n\t\t\t\t\t\tDebug_.Print(ex);\n\t\t\t\t\t}\n\t\t\t\t\treturn t;\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tDebug.Assert(App.IsMainThread);\n\t\tif (App.Model != this) return;\n\t\t\n\t\t//get added and deleted files\n\t\tforeach (var rd in rootDirs) {\n\t\t\t_DirItemComparer comparer = new();\n\t\t\tList<Dictionary<object, _DirTree>> adict = new();\n\t\t\t_Dir(rd.fn, rd.tree, 0);\n\t\t\tvoid _Dir(FileNode parentNode, _DirTree parentTree, int level) {\n\t\t\t\tif (parentTree.children == null) return; //failed to enum this dir; don't remove filenodes\n\t\t\t\t\n\t\t\t\tif (level == adict.Count) adict.Add(new(comparer));\n\t\t\t\tDictionary<object, _DirTree> dict = adict[level];\n\t\t\t\tdict.Clear();\n\t\t\t\t\n\t\t\t\tforeach (var v in parentTree.children) {\n\t\t\t\t\tdict.Add(v, v as _DirTree);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tforeach (var f in parentNode.Children()) {\n\t\t\t\t\tif (f.IsLink) continue;\n\t\t\t\t\tif (!dict.Remove(f.Name, out var t)) {\n\t\t\t\t\t\t(rd.delete ??= new()).Add(f);\n\t\t\t\t\t} else if (f.IsFolder != (t != null)) {\n\t\t\t\t\t\t(rd.delete ??= new()).Add(f);\n\t\t\t\t\t\tdict.Add((object)t ?? f.Name, t);\n\t\t\t\t\t} else if (f.IsFolder) {\n\t\t\t\t\t\t_Dir(f, t, level + 1);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (dict.Count > 0) (rd.add ??= new()).Add((parentNode, dict.Keys.ToArray()));\n\t\t\t}\n\t\t}\n\t\t\n\t\t//update filenodes\n\t\tStringBuilder bAdd = null;\n\t\tbool deleted = false;\n\t\tforeach (var rd in rootDirs) {\n\t\t\t//print.it(rd.fn, rd.delete?.Select(o => o.ItemPath), rd.add?.Select(o => (o.parent.ItemPath, \"[\" + print.util.toString(o.files, compact: true) + \"]\")));\n\t\t\tif (rd.delete != null) {\n\t\t\t\tforeach (var f in rd.delete) {\n\t\t\t\t\tif (f.IsDeleted) continue; //was in a now deleted folder\n\t\t\t\t\t//bDel ??= new(\"Missing or <+options Workspace>excluded<> files were removed from the <b>Files<> panel:\"); //probably not useful info. Just distracts and scares people.\n\t\t\t\t\t//bDel.Append(\"\\r\\n  \").Append(f.ItemPath);\n\t\t\t\t\t//if (f.IsFolder) bDel.Append(\"  (folder)\");\n\t\t\t\t\tdeleted = true;\n\t\t\t\t\t_Delete(f, syncing: 1);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (rd.add != null) {\n\t\t\t\tforeach (var v in rd.add) {\n\t\t\t\t\t_AddDirItems(v.parent, v.files, 0);\n\t\t\t\t}\n\t\t\t}\n\t\t\t//rejected: if renamed or moved inside workspace, preserve file id etc. If folder, preserve the order of items.\n\t\t\t//\tCannot make it reliable. It would be too dirty/heavy. Possible side effects. Rarely used/useful.\n\t\t}\n\t\t\n\t\tif (bAdd != null || deleted) {\n\t\t\tUpdateControlItems();\n\t\t\tSave.WorkspaceAsync();\n\t\t\tCodeInfo.FilesChanged();\n\t\t\tif (bAdd != null) {\n\t\t\t\tbAdd.Append(\"\\r\\nYou may want to review added files and: change the order; change some properties; delete unused files; hide unused files (Options > Workspace).<>\");\n\t\t\t\tprint.it(bAdd);\n\t\t\t}\n\t\t}\n\t\t\n\t\t_syncWatchers ??= new(this);\n\t\t\n\t\tvoid _AddDirItems(FileNode parent, IEnumerable<object> files, int indent) {\n\t\t\tvar parentPath = parent.FilePath + \"\\\\\";\n\t\t\t\n\t\t\tforeach (var v in files) {\n\t\t\t\tvar name = v.ToString();\n\t\t\t\tvar f = new FileNode(this, name, parentPath + name, v is _DirTree);\n\t\t\t\tparent.AddChild(f, first: parent == parent.Root);\n\t\t\t\tbAdd ??= new(\"<><lc #FFFFB9>New files were added:\");\n\t\t\t\tbAdd.AppendLine().Append(' ', indent * 2).Append(\"  \").Append(f.SciLink(true));\n\t\t\t\tif (f.IsFolder) bAdd.Append(\"  (folder)\");\n\t\t\t\t\n\t\t\t\tif (v is _DirTree dt) _AddDirItems(f, dt.children, indent + 1);\n\t\t\t}\n\t\t}\n\t}\n}\n\nfile record class _RootDir(FileNode fn, string itemPath, string fullPath) {\n\tpublic _DirTree tree;\n\tpublic List<FileNode> delete;\n\tpublic List<(FileNode parent, object[] files)> add;\n}\n\nfile record class _DirTree(string name) {\n\tpublic List<object> children;\n\tpublic override string ToString() => name;\n}\n\nfile class _DirItemComparer : IEqualityComparer<object> {\n\tbool IEqualityComparer<object>.Equals(object x, object y) {\n\t\treturn x.ToString().Eqi(y.ToString());\n\t}\n\t\n\tint IEqualityComparer<object>.GetHashCode(object obj) {\n\t\treturn obj.ToString().GetHashCode();\n\t}\n}\n\npartial class FilesModel {\n\tclass _FileWatchers {\n\t\tclass _Watcher : FileSystemWatcher {\n\t\t\tpublic _Watcher(string path, FileNode fn) : base(path) {\n\t\t\t\tFN = fn;\n\t\t\t\tSetFolderItemPathBS();\n\t\t\t}\n\t\t\tpublic FileNode FN { get; }\n\t\t\tpublic string FolderItemPathBS { get; private set; }\n\t\t\t\n\t\t\tpublic void SetFolderItemPathBS() {\n\t\t\t\tif (FN is { IsFolder: true } f) FolderItemPathBS = f == f.Root ? \"\\\\\" : f.ItemPath + \"\\\\\";\n\t\t\t}\n\t\t}\n\t\t\n\t\treadonly FilesModel _model;\n\t\treadonly Dictionary<string, _Watcher> _dDir;\n\t\t\n\t\tpublic _FileWatchers(FilesModel model) {\n\t\t\t_model = model;\n\t\t\t_dDir = new(StringComparer.OrdinalIgnoreCase);\n\t\t\t_guid = Guid.NewGuid().ToString(\"N\");\n\t\t\t_syncFilePath = _model.FilesDirectory + \"\\\\\" + _guid + \".~!~\";\n\t\t\t\n\t\t\t//_Add(null, _model.WorkspaceDirectory);\n\t\t\t_Add(_model.Root, _model.FilesDirectory);\n\t\t\tforeach (var f in _model.Root.Descendants()) {\n\t\t\t\tif (f.IsLink && f.IsFolder) _Add(f, f.LinkTarget);\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic void Dispose() {\n\t\t\tforeach (var v in _dDir.Values) v.Dispose();\n\t\t\t_dDir.Clear();\n\t\t}\n\t\t\n\t\tvoid _Add(FileNode fn, string path) {\n\t\t\tif (_dDir.ContainsKey(path)) {\n\t\t\t\tDebug_.Print(path);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!filesystem.exists(path).Directory) return;\n\t\t\t\n\t\t\t_Watcher fw = null;\n\t\t\ttry {\n\t\t\t\t//print.it(\"add\", path);\n\t\t\t\tfw = new _Watcher(path, fn);\n\t\t\t\t//if (fn == null) { //workspace dir\n\t\t\t\t//\tfw.Filters.Add(\"files.xml\");\n\t\t\t\t//\tfw.Filters.Add(\"settings.json\");\n\t\t\t\t//} else {\n\t\t\t\tfw.IncludeSubdirectories = true;\n\t\t\t\tif (fn == fn.Root) fw.InternalBufferSize = 64 * 1024;\n\t\t\t\t//}\n\t\t\t\t\n\t\t\t\tfw.Created += _EventInTpThread;\n\t\t\t\tfw.Deleted += _EventInTpThread;\n\t\t\t\tfw.Renamed += _EventInTpThread;\n#if USE_CHANGED_EVENTS\n\t\t\t\tfw.Changed += _Event;\n#else\n\t\t\t\t//if (fn == null) fw.Changed += _EventInTpThread;\n#endif\n#if DEBUG\n\t\t\t\tfw.Error += static (o, e) => { Debug_.Print(e.GetException()); }; //never noticed. Never mind, will update at the next startup.\n#endif\n\t\t\t\tfw.EnableRaisingEvents = true;\n\t\t\t\t_dDir[path] = fw;\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tDebug_.Print(ex);\n\t\t\t\tfw?.Dispose();\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic void Add(FileNode f) {\n\t\t\t_Add(f, f.LinkTarget);\n\t\t}\n\t\t\n\t\tpublic void Remove(FileNode f) {\n\t\t\tstring path = f.LinkTarget;\n\t\t\t//print.it(\"remove\", path);\n\t\t\tif (_dDir.Remove(path, out var f1)) f1.Dispose();\n\t\t\telse Debug_.Print(path);\n\t\t}\n\t\t\n\t\tpublic void UpdatePaths() {\n\t\t\tforeach (var v in _dDir.Values) v.SetFolderItemPathBS();\n\t\t}\n\t\t\n\t\tpublic void FileOperationStarted(FOSync fos) {\n\t\t\tif (fos != FOSync.UserFileOp) return;\n\t\t\t//print.it(\"started\", fos);\n\t\t\t_CreateSyncFile();\n\t\t}\n\t\t\n\t\tpublic void FileOperationEnded(FOSync fos) {\n\t\t\tif (fos != FOSync.FilesXml) return;\n\t\t\t//print.it(\"ended\", fos);\n\t\t\t_CreateSyncFile();\n\t\t\t_hSyncFile.Dispose();\n\t\t}\n\t\t\n\t\tvoid _CreateSyncFile() {\n\t\t\tif (_hSyncFile.Is0) {\n\t\t\t\t_hSyncFile = Api.CreateFile(_syncFilePath, Api.GENERIC_WRITE, Api.FILE_SHARE_DELETE | Api.FILE_SHARE_READ, Api.CREATE_NEW, Api.FILE_ATTRIBUTE_TEMPORARY | Api.FILE_FLAG_DELETE_ON_CLOSE | Api.FILE_ATTRIBUTE_HIDDEN | Api.FILE_ATTRIBUTE_SYSTEM);\n\t\t\t}\n\t\t}\n\t\t\n\t\treadonly string _guid, _syncFilePath;\n\t\tHandle_ _hSyncFile;\n\t\tvolatile int _paused;\n\t\t\n\t\tvoid _EventInTpThread(object sender, FileSystemEventArgs e) {\n\t\t\tvar fw = sender as _Watcher;\n\t\t\tif (e.Name[^1] == '~') { //atomic save (LA, VS), Rider temp backup, or a sync file\n\t\t\t\tif (e.Name.Ends(\".~!~\") && e.Name.Length == _guid.Length + 4 && e.ChangeType is WatcherChangeTypes.Created or WatcherChangeTypes.Deleted) { //a sync file of this or another LA process\n\t\t\t\t\tif (e.ChangeType is WatcherChangeTypes.Created) {\n\t\t\t\t\t\tInterlocked.Increment(ref _paused);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tint paused = Interlocked.Decrement(ref _paused);\n\t\t\t\t\t\tif (paused == 0) {\n\t\t\t\t\t\t\tif (e.Name.Starts(_guid)) {\n\t\t\t\t\t\t\t\t//print.it(\"own\");\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tApp.Dispatcher.InvokeAsync(_SyncFromAnotherLaProcess);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else if (paused < 0) { //unlikely but possible. We did not receive the 'created' event because either: 1. Another LA process created its sync file before we started watching; 2. After a watcher error.\n\t\t\t\t\t\t\t_paused = 0;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (_paused > 0) return;\n\t\t\tif (e.Name.Ends(true, [\".TMP\", \".bak\"]) > 0) return; //VS atomic save, etc\n#if !USE_CHANGED_EVENTS\n\t\t\tif (e.ChangeType == WatcherChangeTypes.Renamed && ((RenamedEventArgs)e).OldName.Ends('~') && fw.FN != null) return; //atomic save\n#endif\n\t\t\t//note: we ignore renaming from/to a filtered filename. Eg if renamed `file.txt` -> `file.bak` or `bind` -> `bin`.\n\t\t\t//\tIt could conflict with the atomic save detection. Rare. Will correct at the next startup.\n\t\t\t\n\t\t\tApp.Dispatcher.InvokeAsync(() => _EventInMainThread(fw, e));\n\t\t}\n\t\t\n\t\tvoid _SyncFromAnotherLaProcess() {\n\t\t\tif (App.Model != _model) return;\n\t\t\t_timer?.Stop();\n\t\t\t_ae.Clear();\n\t\t\t_model._SyncFromAnotherLaProcess();\n\t\t}\n\t\t\n\t\tvoid _EventInMainThread(_Watcher fw, FileSystemEventArgs e) {\n\t\t\tif (App.Model != _model) return;\n\t\t\t\n\t\t\tstring itemPath = null;\n\t\t\tvar fnRootOrLinkFolder = fw.FN;\n\t\t\tif (fnRootOrLinkFolder != null) {\n\t\t\t\tif (fnRootOrLinkFolder.IsDeleted) return;\n#if USE_CHANGED_EVENTS\n\t\t\t\tif (e.ChangeType == WatcherChangeTypes.Changed && filesystem.exists(e.FullPath, true).Directory) return;\n#endif\n\t\t\t\titemPath = fw.FolderItemPathBS + e.Name;\n\t\t\t\tif (_model._IsPathIgnored(itemPath, true)) return;\n\t\t\t}\n\t\t\t\n\t\t\t//print.it($\"<><c green>{e.ChangeType}, {e.Name}, {(e as RenamedEventArgs)?.OldName}<>\");\n\t\t\t\n\t\t\t//delay to filter out unusual replace-file sequences etc or join multiple events when copying a folder (and in some cases moving or deleting)\n\t\t\t_ae.Add(new(fw, e, itemPath)); //queue events. Without it, eg on copy folder, the printed items are not all and in random order, although imports without errors.\n\t\t\t_timer ??= new(_Timer);\n\t\t\t_timer.After(250);\n\t\t}\n\t\t\n\t\ttimer _timer;\n\t\tList<_EventInfo> _ae = [];\n\t\t\n\t\trecord class _EventInfo(_Watcher fw, FileSystemEventArgs e, string itemPath);\n\t\t\n\t\tvoid _Timer(timer t_) {\n\t\t\tif (App.Model != _model) return;\n\t\t\tforeach (var x in _ae) {\n\t\t\t\tif (x.fw.FN != null) {\n\t\t\t\t\tswitch (x.e.ChangeType) {\n\t\t\t\t\tcase WatcherChangeTypes.Renamed:\n\t\t\t\t\t\tif (x.e is RenamedEventArgs re) {\n\t\t\t\t\t\t\tvar itemPathOld = x.fw.FolderItemPathBS + re.OldName;\n\t\t\t\t\t\t\tif (_model.FindByItemPath(itemPathOld) is { } fr && _model.FindByItemPath(x.itemPath) is null && !filesystem.exists(re.OldFullPath, true) && filesystem.exists(re.FullPath, true)) {\n\t\t\t\t\t\t\t\tfr.FileRename(pathname.getName(re.Name), syncing: true);\n\t\t\t\t\t\t\t\tprint.it($\"<><lc #FFFFB9>Renamed: {fr.SciLink(true)} (was {pathname.getName(re.OldName)})<>\");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase WatcherChangeTypes.Deleted:\n\t\t\t\t\t\tif (_model.FindByItemPath(x.itemPath) is { } fd && !filesystem.exists(x.e.FullPath, true)) {\n\t\t\t\t\t\t\tprint.it($\"<><lc #FFFFB9>Deleted: {fd.ItemPath}{(fd.IsFolder ? \"  (folder)\" : null)}<>\");\n\t\t\t\t\t\t\t_model._Delete(fd, syncing: 2);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase WatcherChangeTypes.Created:\n\t\t\t\t\t\tif (_model._SyncImportFromWorkspaceDir(x.e.FullPath, x.itemPath) is { } fa) {\n\t\t\t\t\t\t\tforeach (var v in fa.Descendants(true)) print.it($\"<><lc #FFFFB9>Added: {v.SciLink(true)}{(v.IsFolder ? \"  (folder)\" : null)}<>\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\t//} else if (x.e.ChangeType != WatcherChangeTypes.Deleted) {\n\t\t\t\t\t//\tprint.it(x.e.Name);\n\t\t\t\t}\n\t\t\t}\n\t\t\t_ae.Clear();\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Called by a filesystem watcher when files.xml modified by another LA process.\n\t/// Normally it happens when using PiP (child session). One LA process runs in main session, and other in child session. Same user, same workspace.\n\t/// </summary>\n\tvoid _SyncFromAnotherLaProcess() {\n\t\tXElement newRoot;\n\t\ttry {\n\t\t\tnewRoot = filesystem.waitIfLocked(() => XElement.Load(WorkspaceFile)); //the slowest part\n\t\t}\n\t\tcatch (Exception ex) { Debug_.Print(ex); return; }\n\t\t\n\t\tSave.PauseSaveWorkspace = true; //nothing here tries to save, but would be bad if somehow it would happen without this protection. Also it cancels async save.\n\t\ttry {\n\t\t\t_SyncFromAnotherLaProcess(newRoot);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tdialog.showError(\"Failed to sync workspace\", ex.ToString(), owner: App.Hmain);\n\t\t\tEnvironment.Exit(1);\n\t\t}\n\t\tSave.PauseSaveWorkspace = false;\n\t}\n\t\n\tvoid _SyncFromAnotherLaProcess(XElement newRoot) {\n\t\t//Algorithm: remove all our filenodes from their parents, and then re-add like in the new XML.\n\t\t//Then easy to find added, deleted, moved and changed nodes.\n\t\t\n\t\tvar aOld = Root.Descendants().ToArray();\n\t\tvar dOld = aOld.ToDictionary(f => f.Id);\n\t\tuint[] aOldParent = new uint[aOld.Length]; for (int i = 0; i < aOld.Length; i++) aOldParent[i] = aOld[i].Parent.Id;\n\t\tList<FileNode> aAdded = [];\n\t\t\n\t\tforeach (var f in aOld) f.Remove();\n\t\t\n\t\t_Folder(Root, newRoot);\n\t\tDebug.Assert(Root.Descendants().Count() == newRoot.Descendants().Count());\n\t\tvoid _Folder(FileNode oldFolder, XElement newFolder) {\n\t\t\tforeach (var x in newFolder.Elements()) {\n\t\t\t\tif (!x.Attribute(\"i\").Value.ToInt(out uint id)) throw new ArgumentException();\n\t\t\t\tif (!dOld.Remove(id, out var f)) {\n\t\t\t\t\tf = new(x, this);\n\t\t\t\t\t_idMap.Add(id, f); //exception if duplicate\n\t\t\t\t\taAdded.Add(f);\n\t\t\t\t}\n\t\t\t\toldFolder.AddChild(f);\n\t\t\t\tif (x.HasElements) _Folder(f, x);\n\t\t\t}\n\t\t}\n\t\t\n\t\t//DELETED\n\t\tforeach (var f in dOld.Values) {\n\t\t\t//print.it($\"Deleted {f.Name}\");\n\t\t\tif (!f.IsDeleted) _Delete(f, syncing: 1);\n\t\t}\n\t\t\n\t\t//ADDED\n\t\t//foreach (var f in aAdded) {\n\t\t//\tprint.it($\"Added {f}\");\n\t\t//}\n\t\t\n\t\t//CHANGED ATTRIBUTES OR TAG\n\t\tusing (var oldEnum = Root.Descendants().GetEnumerator())\n\t\tusing (var newEnum = newRoot.Descendants().GetEnumerator()) {\n\t\t\twhile (oldEnum.MoveNext() && newEnum.MoveNext()) {\n\t\t\t\tvar f = oldEnum.Current;\n\t\t\t\tDebug.Assert(newEnum.Current.Attribute(\"i\").Value.ToInt(out uint u) && u == f.Id);\n\t\t\t\tif (aAdded.Contains(f)) continue;\n\t\t\t\tf.SyncAttributes(newEnum.Current);\n\t\t\t}\n\t\t}\n\t\t\n\t\t//CHANGED PARENT\n\t\tfor (int i = 0; i < aOld.Length; i++) {\n\t\t\tvar f = aOld[i];\n\t\t\tif (f.IsDeleted) continue;\n\t\t\tif (f.Parent.Id != aOldParent[i]) {\n\t\t\t\t//print.it($\"Moved {f.Name}, {f.Descendants():print}\");\n\t\t\t\tCompiler.Uncache(f, andDescendants: true);\n\t\t\t}\n\t\t}\n\t\t\n\t\tChangedFolderItemPath_();\n\t\tUpdateControlItems();\n\t\tCodeInfo.FilesChanged();\n\t}\n}\n\npartial class FileNode {\n\tinternal void SyncAttributes(XElement x) {\n\t\tvar type = XmlTagToFileType(x.Name.LocalName, canThrow: true);\n\t\tif (type != _type) {\n\t\t\tif (!(IsCodeFile && type is FNType.Script or FNType.Class)) throw new ArgumentException();\n\t\t\t_type = type;\n\t\t\t_testScriptId = 0;\n\t\t\tCompiler.Uncache(this);\n\t\t}\n\t\t\n\t\tstring icon = null;\n\t\tuint testScriptId = 0;\n\t\t//_Flags flags = 0;\n\t\tforeach (var attr in x.Attributes()) {\n\t\t\tstring name = attr.Name.LocalName, value = attr.Value;\n\t\t\tif (value.NE()) continue;\n\t\t\tswitch (name) {\n\t\t\t//case \"i\": break;\n\t\t\t//case \"path\": break; //never changes\n\t\t\t//case \"f\": flags = (_Flags)value.ToInt(); break; //currently there are no flags\n\t\t\tcase \"n\" when value != _name:\n\t\t\t\t_SetName(value);\n\t\t\t\tCompiler.Uncache(this, andDescendants: true);\n\t\t\t\tbreak;\n\t\t\tcase \"icon\": icon = value; break;\n\t\t\tcase \"run\": value.ToInt(out testScriptId); break;\n\t\t\t}\n\t\t}\n\t\t\n\t\t//if (flags != _flags) {\n\t\t//\t_flags = flags;\n\t\t//}\n\t\t\n\t\tif (icon != _icon) {\n\t\t\t_icon = icon;\n\t\t\tCompiler.Uncache(this);\n\t\t}\n\t\t\n\t\tif (testScriptId != _testScriptId) {\n\t\t\t_testScriptId = testScriptId;\n\t\t}\n\t}\n}\n\nenum FOSync { UserFileOp, UserFileWrite, FilesXml, PrivateFileWrite }\n"
  },
  {
    "path": "Au.Editor/Files/WorkspaceState.cs",
    "content": "//We save workspace state data in folder \"%folders.Workspace%\\.state\", in files:\n//\tstate - top/pos of open files, open files, expanded folders, maybe some markers.\n//\t\tIt's a text file containing 3 compact lines like:\n//\t\t\teditor|id1=t1p2|id2=p2h1,2,3\n//\t\t\topen|id1|id2\n//\t\t\texpanded|id1|id2\n//\tfolding - contracted fold points of all files.\n//\t\tIt's a binary file, as compact as possible. Managed by struct WorkspaceState._Folding.\n//In child session separately, in subfolder \"pip\".\n\nnamespace LA;\n\n/// <summary>\n/// Workspace state, such as open files, expanded folders, editor caret position, folded lines.\n/// WorkspaceDirectory + @\"\\.state\\state.json\"\n/// </summary>\nclass WorkspaceState : IDisposable {\n\treadonly FilesModel _model;\n\treadonly _State _s;\n\t\n\tpublic WorkspaceState(FilesModel model) {\n\t\t_model = model;\n\t\t_s = new _State(model);\n\t}\n\t\n\tpublic void Dispose() {\n\t\t_s.SaveIfNeed();\n\t}\n\t\n\tvoid _Save() {\n\t\tif (_suspendSave == 0) _s.SaveIfNeed();\n\t\telse _suspendSave |= 2;\n\t}\n\tbyte _suspendSave; //flags: 1 suspended, 2 save when resumed\n\t\n\tpublic void SuspendSave(bool suspend) {\n\t\tif (suspend) _suspendSave |= 1;\n\t\telse {\n\t\t\tif ((_suspendSave & 2) != 0) _s.SaveIfNeed();\n\t\t\t_suspendSave = 0;\n\t\t}\n\t}\n\t\n\t#region editor\n\t\n\t//Contains parsed editor state.\n\tpublic record struct Editor {\n\t\tpublic int top, pos;\n\t\tpublic int[] fold/*, someMarker*/;\n\t\t\n\t\tpublic bool Equals(in Editor e, out bool changedState, out bool changedFolding) {\n\t\t\tchangedState = !(top == e.top && pos == e.pos /*&& someMarker.AsSpan().SequenceEqual(e.someMarker)*/);\n\t\t\tchangedFolding = !fold.AsSpan().SequenceEqual(e.fold);\n\t\t\treturn !(changedState || changedFolding);\n\t\t}\n\t}\n\t\n\tpublic bool EditorGet(FileNode f, out Editor x) {\n\t\tx = default;\n\t\tif (_s.Find(f, out var t)) {\n\t\t\tvar s = _s.editor;\n\t\t\tfor (int start = t.valueStart, end; start < t.end; start = end) {\n\t\t\t\tfor (end = ++start; end < t.end && s[end] is not (>= 'g' and <= 'z');) end++;\n\t\t\t\tint what = s[start - 1];\n\t\t\t\tswitch (what) {\n\t\t\t\tcase 'p' or 't': //int\n\t\t\t\t\tvar k = s.ToInt(start, STIFlags.IsHexWithout0x);\n\t\t\t\t\tif (what == 't') x.top = k; else x.pos = k;\n\t\t\t\t\tbreak;\n\t\t\t\t\t//case 'h': //int[]\n\t\t\t\t\t//\tint n = 1; for (int j = start; j < end; j++) if (s[j] == ',') n++;\n\t\t\t\t\t//\tvar a = new int[n];\n\t\t\t\t\t//\ta[0] = start; for (int i = 0, j = start; j < end; j++) if (s[j] == ',') a[++i] = j + 1;\n\t\t\t\t\t//\tfor (int i = 0, j = 0; i < n; i++) a[i] = j += s.ToInt(a[i], STIFlags.IsHexWithout0x); //deltas\n\t\t\t\t\t//\tif (what == 'h') x.someMarker = a;\n\t\t\t\t\t//\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (_Folding.Get(f) is { } af) x.fold = af.ToArray();\n\t\t\n\t\treturn x != default;\n\t}\n\t\n\tpublic void EditorSave(FileNode f, in Editor x, bool changedState, bool changedFolding) {\n\t\tif (changedState) _EditorSave(x);\n\t\tif (changedFolding) _Folding.Save(f, x.fold);\n\t\t\n\t\tvoid _EditorSave(in Editor x) {\n\t\t\tstring s = _EditorFormat(f, x);\n\t\t\tif (_s.Find(f, out var t)) {\n\t\t\t\tif (s != null && _s.editor.Eq(t.start..t.end, s)) return;\n\t\t\t\t_s.editor = string.Concat(s, _s.editor.AsSpan(..t.start), _s.editor.AsSpan(t.end..));\n\t\t\t} else {\n\t\t\t\tif (s == null) return;\n\t\t\t\t_s.editor = s + _s.editor;\n\t\t\t}\n\t\t\t_Save();\n\t\t}\n\t\t\n\t\tstatic string _EditorFormat(FileNode f, in Editor x) {\n\t\t\tif (x.top == 0 && x.pos == 0 /*&& x.someMarker == null*/) return null;\n\t\t\tusing (new StringBuilder_(out var b)) {\n\t\t\t\tb.Append('|').AppendHex(f.Id).Append('=');\n\t\t\t\tif (x.top > 0) b.Append('t').AppendHex(x.top);\n\t\t\t\tif (x.pos > 0) b.Append('p').AppendHex(x.pos);\n\t\t\t\t//_AppendLines(x.someMarker, 'h');\n\t\t\t\treturn b.ToString();\n\t\t\t\t\n\t\t\t\t//void _AppendLines(int[] a, char c) {\n\t\t\t\t//\tif (a == null) return;\n\t\t\t\t//\tb.Append(c);\n\t\t\t\t//\tfor (int i = 0, j = 0; i < a.Length; j = a[i++]) {\n\t\t\t\t//\t\tif (i > 0) b.Append(',');\n\t\t\t\t//\t\tb.AppendHex(a[i] - j); //delta\n\t\t\t\t//\t}\n\t\t\t\t//}\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// When deleting a file, called to delete its saved states.\n\t/// </summary>\n\tpublic void EditorDelete(FileNode f) {\n\t\tif (_s.Find(f, out var v)) {\n\t\t\t_s.editor = _s.editor.Remove(v.start..v.end);\n\t\t\t_Save();\n\t\t}\n\t\t\n\t\t_Folding.Delete(f);\n\t}\n\t\n\t//called by FilesModel.CloseFile\n\tpublic void Cleanup() {\n\t\tif (_cleanup++ != 0) return; //only the first and every 256-th time\n\t\t\n\t\tvar hs = _model.OpenFiles.Select(o => o.Id).ToHashSet();\n\t\tvar b = new StringBuilder(_s.editor.Length);\n\t\tint n1 = 0, n2 = 0;\n\t\tforeach (var t in _s.editor.SplitSE(.., '|', StringSplitOptions.RemoveEmptyEntries)) {\n\t\t\tn1++;\n\t\t\tif (!_s.editor.ToInt(out uint id, t.start, out int i, STIFlags.IsHexWithout0x)) continue; //remove if _js.editor corrupt\n\t\t\tif (!hs.Contains(id)) { //the file isn't in OpenFiles\n\t\t\t\tif (_model.FindById(id) == null) continue; //remove if file deleted\n\t\t\t\tif (_s.editor.AsSpan(++i..t.end).IndexOfAny(\"gh\") < 0) continue; //remove if no markers etc\n\t\t\t}\n\t\t\tb.Append(_s.editor, t.start - 1, t.Length + 1);\n\t\t\tn2++;\n\t\t}\n\t\tif (n2 < n1) {\n\t\t\t_s.editor = b.ToString();\n\t\t\t_Save();\n\t\t}\n\t\t\n\t\t//CONSIDER: also cleanup folding? Normally don't need; unless some files edited manually, or an exception prevented deleting data, etc.\n\t}\n\tbyte _cleanup;\n\t\n\t#endregion\n\t\n\t#region files\n\t\n\t//These functions set/get the 'open' and 'expanded' lines in the state file.\n\t\n\t/// <param name=\"what\">0 open, 1 expanded.</param>\n\tpublic IEnumerable<uint> FilesGet(int what) {\n\t\tvar s = what switch { 0 => _s.open, 1 => _s.expanded, _ => throw null };\n\t\tforeach (var v in s.SplitSE(.., '|')) {\n\t\t\tyield return (uint)s.ToInt(v.start, STIFlags.IsHexWithout0x);\n\t\t}\n\t}\n\t\n\tpublic void FilesSave(IEnumerable<FileNode> open, IEnumerable<FileNode> expanded) {\n\t\tusing (new StringBuilder_(out var b)) {\n\t\t\t_s.open = _FormatIds(b, open);\n\t\t\t_s.expanded = _FormatIds(b, expanded);\n\t\t}\n\t\t_Save();\n\t\t\n\t\tstatic string _FormatIds(StringBuilder b, IEnumerable<FileNode> e) {\n\t\t\tb.Clear();\n\t\t\tforeach (var f in e) {\n\t\t\t\tif (b.Length > 0) b.Append('|');\n\t\t\t\tb.AppendHex(f.Id);\n\t\t\t}\n\t\t\treturn b.ToString();\n\t\t}\n\t}\n\t\n\t#endregion\n\t\n\t//Loads/saves the state file. Contains its lines as public string fields.\n\tclass _State {\n\t\tFilesModel _model;\n\t\tstring _file;\n\t\tpublic string editor, open, expanded;\n\t\tstring _editor, _open, _expanded;\n\t\t\n\t\tpublic _State(FilesModel model) {\n\t\t\t_model = model;\n\t\t\t_file = $@\"{_model.WorkspaceDirectory}\\.state{(miscInfo.isChildSession ? @\"\\pip\" : null)}\\state\";\n\t\t\ttry {\n\t\t\t\tvar s = filesystem.loadText(_file);\n\t\t\t\tforeach (var line in s.Lines(..)) {\n\t\t\t\t\tint what = s.Eq(line.start, false, \"editor|\", \"open|\", \"expanded|\");\n\t\t\t\t\tif (what == 0) continue;\n\t\t\t\t\tswitch (what) {\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\teditor = s[(line.start + 6)..line.end];\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\topen = s[(line.start + 5)..line.end];\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\texpanded = s[(line.start + 9)..line.end];\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception e1) {\n\t\t\t\tif (!filesystem.exists(_file)) _ConvertOldDb();\n\t\t\t\telse print.it(e1);\n\t\t\t}\n\t\t\t\n\t\t\t_editor = editor ??= \"\";\n\t\t\t_open = open ??= \"\";\n\t\t\t_expanded = expanded ??= \"\";\n\t\t\t\n\t\t\t//convert from old SQLite database\n\t\t\tvoid _ConvertOldDb() {\n\t\t\t\tif (miscInfo.isChildSession) return;\n\t\t\t\tvar dbFile = _model.WorkspaceDirectory + @\"\\state.db\";\n\t\t\t\tif (filesystem.exists(dbFile, true)) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tusing (var db = new sqlite(dbFile, SLFlags.SQLITE_OPEN_READONLY)) {\n\t\t\t\t\t\t\t//using (var p = db.Statement(\"SELECT * FROM _misc\")) { } //never mind open/expanded/top/pos. Convert just folding.\n\t\t\t\t\t\t\t_Folding.ConvertOldDb(db, _model);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfilesystem.delete(dbFile);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (Exception e1) { Debug_.Print(e1); }\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic void SaveIfNeed() {\n\t\t\tif (editor == _editor && open == _open && expanded == _expanded) return;\n\t\t\t\n\t\t\t//print.it(\"saved\");\n\t\t\t//if (editor != _editor) print.it($\"\\teditor: old={_editor}, new={editor}\");\n\t\t\t//if (open != _open) print.it($\"\\topen: old={_open}, new={open}\");\n\t\t\t//if (expanded != _expanded) print.it($\"\\texpanded: old={_expanded}, new={expanded}\");\n\t\t\t\n\t\t\tvar s = $\"editor{editor}\\nopen|{open}\\nexpanded|{expanded}\\n\";\n\t\t\tif (!_model.TryFileOperation(FOSync.PrivateFileWrite, () => { filesystem.saveText(_file, s); })) return;\n\t\t\t\n\t\t\t_editor = editor;\n\t\t\t_open = open;\n\t\t\t_expanded = expanded;\n\t\t}\n\t\t\n\t\tpublic bool Find(FileNode f, out (int start, int valueStart, int end) r) {\n\t\t\tvar s = $\"|{f.Id:X}=\";\n\t\t\tint i = editor.Find(s);\n\t\t\tif (i < 0) { r = default; return false; }\n\t\t\tint j = editor.IndexOf('|', i + s.Length); if (j < 0) j = editor.Length;\n\t\t\tr = new(i, i + s.Length, j);\n\t\t\treturn true;\n\t\t}\n\t}\n\t\n\t//Contains folding data of a source file.\n\t//The static functions load/save the folding file and get/set/delete folding data of a source file.\n\trecord struct _Folding(string path, byte[] bytes, int start, int valueStart, int end) {\n\t\t//Loads the folding file if exists, and finds f data in it.\n\t\t//Returns false if: file does not exist; failed to load; did not find f data.\n\t\t//If returns false: sets path; sets bytes if loaded.\n\t\t//If returns true: sets all fields.\n\t\tstatic bool _Load(FileNode f, out _Folding r) {\n\t\t\tuint id = f.Id;\n\t\t\tbyte[] b = null;\n\t\t\tvar file = $@\"{f.Model.WorkspaceDirectory}\\.state{(miscInfo.isChildSession ? @\"\\pip\" : null)}\\folding\";\n\t\t\tif (filesystem.exists(file, useRawPath: true)) {\n\t\t\t\ttry {\n\t\t\t\t\tb = filesystem.loadBytes(file);\n\t\t\t\t\t//find byte 0 followed by 7-bit-encoded id. There are no other byte 0, because: an id cannot be 0; a line delta cannot be 0.\n\t\t\t\t\tSpan<byte> sid = stackalloc byte[8];\n\t\t\t\t\tsid = sid[.._Encode7Bit(id, sid, 1)];\n\t\t\t\t\tint start = b.AsSpan().IndexOf(sid);\n\t\t\t\t\tif (start >= 0) {\n\t\t\t\t\t\tint vstart = start + sid.Length, end = vstart;\n\t\t\t\t\t\twhile (end < b.Length && b[end] != 0) end++;\n\t\t\t\t\t\tif (end > start) { //else corrupt\n\t\t\t\t\t\t\tr = new(file, b, start, vstart, end);\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (Exception e1) { Debug_.Print(e1); }\n\t\t\t}\n\t\t\tr = new(file, b, 0, 0, 0);\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\t//7-bit encodes id.\n\t\t//span - memory for writing. Must be at least 5 bytes + offset.\n\t\t//offset - offset in <i>span</i> where to start writing.\n\t\t//Returns the end offset.\n\t\tstatic int _Encode7Bit(uint id, Span<byte> span, int offset = 0) {\n\t\t\twhile (id > 0x7Fu) { span[offset++] = (byte)(id | 0x80u); id >>= 7; }\n\t\t\tspan[offset++] = (byte)id;\n\t\t\treturn offset;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets <i>f</i> data from the folding file.\n\t\t/// </summary>\n\t\t/// <returns>[+] lines. Returns null if: no file; no data; failed.</returns>\n\t\tpublic static List<int> Get(FileNode f) {\n\t\t\tif (_Load(f, out var x)) {\n\t\t\t\ttry {\n\t\t\t\t\tvar a = new List<int>();\n\t\t\t\t\t//read array of 7-bit encoded line deltas\n\t\t\t\t\tvar ms = new MemoryStream(x.bytes, x.valueStart, x.end - x.valueStart, false);\n\t\t\t\t\tusing var r = new BinaryReader(ms);\n\t\t\t\t\tint line = -1;\n\t\t\t\t\twhile (ms.Position < ms.Length) {\n\t\t\t\t\t\tint delta = r.Read7BitEncodedInt();\n\t\t\t\t\t\ta.Add(line += delta);\n\t\t\t\t\t}\n\t\t\t\t\treturn a;\n\t\t\t\t}\n\t\t\t\tcatch (Exception e1) { Debug_.Print(e1); }\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// If <i>a</i> null, deletes <i>f</i> data from file if exists. Else saves <i>f</i> data in file, unless same data is already saved.\n\t\t/// </summary>\n\t\t/// <param name=\"a\">[+] lines or null</param>\n\t\tpublic static void Save(FileNode f, int[] a) {\n\t\t\tif (a == null) {\n\t\t\t\tDelete(f);\n\t\t\t} else {\n\t\t\t\tvar ms = new MemoryStream();\n\t\t\t\tusing var w = new BinaryWriter(ms);\n\t\t\t\tWrite(w, f.Id, a);\n\t\t\t\tvar b = ms.ToArray();\n\t\t\t\t\n\t\t\t\tbool exists = _Load(f, out var x);\n\t\t\t\tif (exists && x.bytes.AsSpan(x.start..x.end).SequenceEqual(b)) return;\n\t\t\t\t\n\t\t\t\tf.Model.TryFileOperation(FOSync.PrivateFileWrite, () => {\n\t\t\t\t\tfilesystem.save(x.path, temp => {\n\t\t\t\t\t\tusing var fs = File.Create(temp);\n\t\t\t\t\t\tfs.Write(b);\n\t\t\t\t\t\tif (exists) {\n\t\t\t\t\t\t\tfs.Write(x.bytes, 0, x.start);\n\t\t\t\t\t\t\tfs.Write(x.bytes, x.end, x.bytes.Length - x.end);\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic static void Write(BinaryWriter w, uint id, int[] a) {\n\t\t\tw.Write((byte)0); //record separator, to find the record later. Other data does not contain 0. The minimal possible delta is 1 (see 'line = -1').\n\t\t\tw.Write7BitEncodedInt((int)id);\n\t\t\tfor (int i = 0, line = -1; i < a.Length; line = a[i++]) {\n\t\t\t\tint delta = a[i] - line;\n\t\t\t\tDebug.Assert(delta > 0);\n\t\t\t\tw.Write7BitEncodedInt(delta);\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic static void Delete(FileNode f) {\n\t\t\tif (_Load(f, out var x)) {\n\t\t\t\tvar b = x.bytes.RemoveAt(x.start, x.end - x.start);\n\t\t\t\tf.Model.TryFileOperation(FOSync.PrivateFileWrite, () => { filesystem.saveBytes(x.path, b); });\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic static void ConvertOldDb(sqlite db, FilesModel model) {\n\t\t\tvar ms = new MemoryStream();\n\t\t\tusing var w = new BinaryWriter(ms);\n\t\t\tusing var p = db.Statement(\"SELECT id,lines FROM _editor ORDER BY id\");\n\t\t\twhile (p.Step()) {\n\t\t\t\tuint id = (uint)p.GetInt(0);\n\t\t\t\tif (model.FindById(id) != null && p.GetArray<int>(1) is { } a) {\n\t\t\t\t\tfor (int i = 0; i < a.Length; i++) a[i] &= 0x7FFFFFF;\n\t\t\t\t\t_Folding.Write(w, id, a);\n\t\t\t\t}\n\t\t\t}\n\t\t\tfilesystem.saveBytes(model.WorkspaceDirectory + @\"\\.state\\folding\", ms.ToArray());\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/LibreAutomate.iss",
    "content": "﻿#define MyAppName \"LibreAutomate\"\n#define MyAppNameShort \"LibreAutomate\"\n#define MyAppVersion \"1.15.0\"\n#define MyAppPublisher \"Gintaras Didžgalvis\"\n#define MyAppURL \"https://www.libreautomate.com/\"\n#define MyAppExeName \"Au.Editor.exe\"\n\n[Setup]\n//note: the \" C#\" here is inherited from the old LA full name \"LibreAutomate C#\". Can't change AppId easily. Docs: not used for display anywhere.\nAppId=LibreAutomate C#\nAppName={#MyAppName}\nAppVersion={#MyAppVersion}\nUninstallDisplayName={#MyAppName}\nAppPublisher={#MyAppPublisher}\nAppPublisherURL={#MyAppURL}\nAppSupportURL={#MyAppURL}\nAppUpdatesURL={#MyAppURL}\nDefaultDirName={commonpf}\\{#MyAppNameShort}\nSourceDir=..\\_\nOutputDir=.\nOutputBaseFilename=LibreAutomateSetup\nCompression=lzma/normal\nSolidCompression=yes\nArchitecturesAllowed=x64 arm64\nArchitecturesInstallIn64BitMode=x64 arm64\nMinVersion=0,6.1sp1\nDisableProgramGroupPage=yes\nAppMutex=Au.Editor.Mutex.m3gVxcTJN02pDrHiQ00aSQ\nUsePreviousGroup=False\nDisableDirPage=no\nSetupIconFile=..\\Au.Editor\\resources\\ico\\app.ico\nUninstallDisplayIcon={app}\\{#MyAppExeName}\n\n[Languages]\nName: \"english\"; MessagesFile: \"compiler:Default.isl\"\n\n[Files]\n#if true\nSource: \"Au.Editor.exe\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"Au.Editor-arm.exe\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"Au.Editor.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"Au.Editor.xml\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"Au.Task-x64.exe\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"Au.Task-arm.exe\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"Au.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"Au.xml\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"Au.Controls.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"Au.Controls.xml\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"Au.Net4.exe\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"AxMSTSCLib.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"MSTSCLib.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"Microsoft.Web.WebView2.Core.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"Microsoft.Web.WebView2.Wpf.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"runtimes\\win-x64\\native\\WebView2Loader.dll\"; DestDir: \"{app}\\runtimes\\win-x64\\native\"; Flags: ignoreversion\nSource: \"runtimes\\win-arm64\\native\\WebView2Loader.dll\"; DestDir: \"{app}\\runtimes\\win-arm64\\native\"; Flags: ignoreversion\nSource: \"NuGet.*.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\n\nSource: \"64\\Au.AppHost.exe\"; DestDir: \"{app}\\64\"; Flags: ignoreversion\nSource: \"64\\ARM\\Au.AppHost.exe\"; DestDir: \"{app}\\64\\ARM\"; Flags: ignoreversion\nSource: \"32\\Au.AppHost.exe\"; DestDir: \"{app}\\32\"; Flags: ignoreversion\n\nSource: \"64\\AuCpp.dll\"; DestDir: \"{app}\\64\"; Flags: ignoreversion\nSource: \"64\\ARM\\AuCpp.dll\"; DestDir: \"{app}\\64\\ARM\"; Flags: ignoreversion\nSource: \"32\\AuCpp.dll\"; DestDir: \"{app}\\32\"; Flags: ignoreversion\n\nSource: \"64\\Au.DllHost.exe\"; DestDir: \"{app}\\64\"; Flags: ignoreversion\nSource: \"64\\ARM\\Au.DllHost.exe\"; DestDir: \"{app}\\64\\ARM\"; Flags: ignoreversion\nSource: \"32\\Au.DllHost.exe\"; DestDir: \"{app}\\32\"; Flags: ignoreversion\n\nSource: \"64\\Scintilla.dll\"; DestDir: \"{app}\\64\"; Flags: ignoreversion\nSource: \"64\\ARM\\Scintilla.dll\"; DestDir: \"{app}\\64\\ARM\"; Flags: ignoreversion\n\nSource: \"32\\7za.exe\"; DestDir: \"{app}\\32\"; Flags: ignoreversion\n\nSource: \"Debugger\\*.dll\"; DestDir: \"{app}\\Debugger\"; Flags: ignoreversion recursesubdirs\nSource: \"Debugger\\*.exe\"; DestDir: \"{app}\\Debugger\"; Flags: ignoreversion recursesubdirs\n\nSource: \"Roslyn\\*.dll\"; DestDir: \"{app}\\Roslyn\"; Flags: ignoreversion\n\nSource: \"Default\\*\"; DestDir: \"{app}\\Default\"; Flags: ignoreversion\nSource: \"Default\\Workspace\\files\\*\"; DestDir: \"{app}\\Default\\Workspace\\files\"; Flags: ignoreversion recursesubdirs\nSource: \"Default\\Workspace\\files.xml\"; DestDir: \"{app}\\Default\\Workspace\"; Flags: ignoreversion\nSource: \"Default\\Themes\\*\"; DestDir: \"{app}\\Default\\Themes\"; Flags: ignoreversion\nSource: \"Templates\\files\\*\"; DestDir: \"{app}\\Templates\\files\"; Flags: ignoreversion recursesubdirs\nSource: \"Templates\\files.xml\"; DestDir: \"{app}\\Templates\"; Flags: ignoreversion\n\nSource: \"doc.db\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"doc-ai.db\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"doc-html.db\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"icons.db\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"ref.db\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"winapi.db\"; DestDir: \"{app}\"; Flags: ignoreversion\n\nSource: \"default.exe.manifest\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"toc.json\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"toc-ai.yml\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"xrefmap.yml\"; DestDir: \"{app}\"; Flags: ignoreversion\n#endif\n\n//CONSIDER: don't include big not frequently updated files. Auto-download on demand.\n//\tMost .db, maybe Roslyn folder. Makes smaller: 40 MB -> 5 MB.\n\n[Dirs]\nName: \"{commonappdata}\\LibreAutomate\"; Flags: uninsalwaysuninstall; Permissions: authusers-modify\nName: \"{app}\\Git\"; Flags: uninsalwaysuninstall; Permissions: authusers-modify\nName: \"{app}\\SDK\"; Flags: uninsalwaysuninstall; Permissions: authusers-modify\nName: \"{app}\\Roslyn\\.exeProgram\"; Attribs: hidden\n;why Inno stops here when debugging?\n\n[InstallDelete]\n;Type: files; Name: \"{app}\\file.ext\"\n;Type: filesandordirs; Name: \"{app}\\dir\"\nType: filesandordirs; Name: \"{app}\\Roslyn\"\nType: filesandordirs; Name: \"{app}\\64\"\nType: filesandordirs; Name: \"{app}\\32\"\nType: filesandordirs; Name: \"{app}\\Default\"\nType: filesandordirs; Name: \"{app}\\Templates\"\n\n//FUTURE: remove this code\nType: files; Name: \"{app}\\Au.Task.exe\"\nType: files; Name: \"{autoprograms}\\LibreAutomate C#.lnk\"\n\n[Icons]\nName: \"{autoprograms}\\{#MyAppName}\"; Filename: \"{app}\\{#MyAppExeName}\"\n\n[Registry]\n;register app path\nRoot: HKLM; Subkey: Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\Au.Editor.exe; ValueType: string; ValueData: {app}\\Au.Editor.exe; Flags: uninsdeletevalue uninsdeletekeyifempty\n\n//FUTURE: remove this. It's to fix a result of a bug in the setup of LA 1.13.0, 2025/07/06, fixed in 1.13.1.\nRoot: HKLM; Subkey: Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\LibreAutomate_is1; Flags: deletekey\n\n;rejected: set environment variable Au.Path, to find unmanaged dlls used by Au.dll when it is used in various scripting environments etc that copy it somewhere (they don't copy unmanaged dlls)\n; Rarely used. Overwrites mine. Let users learn it, then they can use the dlls on any computer without this program installed.\n;Root: HKLM; Subkey: SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment; ValueType: string; ValueData: {app}; Flags: uninsdeletevalue\n\n[Run]\nFilename: \"{app}\\{#MyAppExeName}\"; Flags: nowait postinstall skipifsilent; Description: \"{cm:LaunchProgram,{#MyAppName}}\"\n\n[UninstallRun]\nFilename: \"{sys}\\schtasks.exe\"; Parameters: \"/delete /tn \\Au\\Au.Editor /f\"; Flags: nowait skipifdoesntexist runhidden; RunOnceId: \"schtasks-delete\"\n\n;rejected because of AV false positives.\n;[Files]\n;Source: \"Setup32.dll\"; DestDir: \"{app}\"; Flags: ignoreversion\n\n[Code]\n//procedure Cpp_Install(step: Integer; dir: String);\n//external 'Cpp_Install@files:Setup32.dll cdecl setuponly delayload';\n\n//procedure Cpp_Uninstall(step: Integer);\n//external 'Cpp_Uninstall@{app}\\Setup32.dll cdecl uninstallonly delayload';\n\nfunction FindWindowEx(w1, w2: Integer; cn, name: String): Integer;\nexternal 'FindWindowExW@user32.dll';\n\nfunction SendMessageTimeout(w, msg, wp, lp, flags, timeout: Integer; var res: Integer): Integer;\nexternal 'SendMessageTimeoutW@user32.dll';\n\nconst nmax = 25000;\n\nvar\n  IsDotNetInstalledCalled, IsDotNetInstalledSaidYes: Boolean;\n\n//The same as Cpp_Unload. Maybe possible to call it, but difficult and may trigger AV FP.\nprocedure UnloadAuCppDll(unins: Boolean);\nvar\n  a :Array of Integer;\n  i, n, w, res :Integer;\n  silent: Boolean;\nbegin\n  //if not FileExists(ExpandConstant('{app}\\64\\AuCpp.dll')) then exit; //error, app still not set\n  \n  if unins then silent:=UninstallSilent() else silent:=WizardSilent();\n  \n  SetArrayLength(a, nmax);\n  \n  //close agent windows\n  for i:=0 to nmax-1 do begin\n    w:=FindWindowEx(-3, w, 'AuCpp_IPA_1', ''); //HWND_MESSAGE //if '', Pascal passes null, not \"\"\n    if w=0 then break;\n    a[i]:=w;\n    inc(n);\n  end;\n  if n>0 then begin\n    for i:=0 to n-1 do SendMessageTimeout(a[i], 16, 0, 0, 2, 5000, res); //WM_CLOSE, SMTO_ABORTIFHUNG\n    if silent then Sleep(n * 50);\n  end;\n  //Log(IntToStr(i));\n  \n  //unload from processes where loaded by the clipboard hook\n  if silent then SendMessageTimeout($ffff, 0, 0, 0, 2, 500, res) else SendBroadcastNotifyMessage(0, 0, 0); //HWND_BROADCAST, SMTO_ABORTIFHUNG\n  n:=0; w:=0;\n  for i:=0 to nmax-1 do begin\n    w:=FindWindowEx(-3, w, '', ''); //HWND_MESSAGE\n    if w=0 then break;\n    a[i]:=w;\n    inc(n);\n  end;\n  if silent then for i:=0 to n-1 do SendMessageTimeout(a[i], 0, 0, 0, 2, 500, res)\n  else for i:=0 to n-1 do SendNotifyMessage(a[i], 0, 0, 0);\n  if silent then Sleep(500);\nend;\n\nfunction _IsDotNetInstalled(): Boolean;\nvar\n  args: string;\n  fileName: string;\n  output: AnsiString;\n  resultCode: Integer;\nbegin\n  Result := false;\n  //exit;\n  fileName := ExpandConstant('{tmp}\\dotnet.txt');\n  args := '/C dotnet --list-runtimes > \"' + fileName + '\" 2>&1';\n  if Exec(ExpandConstant('{cmd}'), args, '', SW_HIDE, ewWaitUntilTerminated, resultCode) and (resultCode = 0) then\n  begin\n    if LoadStringFromFile(fileName, output) then Result := Pos('Microsoft.WindowsDesktop.App 10.', output) > 0;\n  end;\n  DeleteFile(fileName);\n\t\n\t//tested: If installed dotnet for multiple platforms (eg ARM64, x64, x86), only the runtime of the OS platform adds its path to PATH. It is what we need.\nend;\n\nfunction IsDotNetInstalled(): Boolean;\nbegin\n  if IsDotNetInstalledCalled then\n  begin\n    Result := IsDotNetInstalledSaidYes;\n\tend else begin\n\t\tResult := _IsDotNetInstalled;\n\t\tIsDotNetInstalledSaidYes := Result;\n\t\tIsDotNetInstalledCalled := True;\n  end;\nend;\n\nfunction InstallDotNet(): Boolean;\nvar\n  ResultCode: Integer;\n  page: TDownloadWizardPage;\n  url, setupFileName, setupFilePath, txtFileName: string;\n  urls: TArrayOfString;\nbegin\n  Result := true;\n  \n  page := CreateDownloadPage('Installing .NET 10 Desktop Runtime', 'If stopped or failed now, download/install it manually. Size ~60 MB.', nil);\n  page.Clear;\n  page.Msg1Label.Caption := 'Getting the download URL of the current version.';\n\ttxtFileName := 'net-10-url.txt';\n  page.Add('https://www.libreautomate.com/download/' + txtFileName, txtFileName, '');\n  page.Show;\n\ttry\n\t\tpage.Download;\n\t\t//Get the download URL of the latest .NET Desktop Runtime.\n\t\t//  Info: Script \"Check for new .NET version\" runs every day. If a new .NET build version available, updates the URLs here and in https://www.libreautomate.com/download/net-x-url.txt.\n\t\t//\tBad: may be inaccessible and hang, eg in China. Never mind: instead can exec curl with timeout.\n\t\tLoadStringsFromFile(ExpandConstant('{tmp}\\' + txtFileName), urls);\n\texcept\n\t\t//MsgBox('Failed: ' + 'https://www.libreautomate.com/download/' + txtFileName, mbInformation, MB_OK);\n\tend;\n  \n  //If the above failed, use these hardcoded URLs.\n  //  Script \"Check for new .NET version\" runs every day. If a new .NET build version available, updates these strings here.\n  if (Length(urls) < 2) then\n  begin\n    SetLength(urls, 2);\n    urls[0] := 'https://builds.dotnet.microsoft.com/dotnet/WindowsDesktop/10.0.1/windowsdesktop-runtime-10.0.1-win-x64.exe';\n    urls[1] := 'https://builds.dotnet.microsoft.com/dotnet/WindowsDesktop/10.0.1/windowsdesktop-runtime-10.0.1-win-arm64.exe';\n  end;\n\t\n\tif IsArm64 then url := urls[1] else url := urls[0];\n  setupFileName := ExtractFileName(url);\n    \n  page.Clear;\n\tpage.Msg1Label.Caption := 'Downloading.';\n  page.Add(url, setupFileName, '');\n\ttry\n\t\tpage.Download;\n\t\t\n\t\tpage.Msg1Label.Caption := 'Running the .NET installer.';\n\t\tpage.Msg2Label.Caption := setupFileName;\n\t\tpage.ProgressBar.Style := npbstMarquee;\n\t\tpage.AbortButton.Hide;\n\t\t\n\t\tsetupFilePath := ExpandConstant('{tmp}\\' + setupFileName);\n\t\tResultCode:=-111;\n\t\tResult := Exec(setupFilePath, '/install /quiet /norestart', '', SW_HIDE, ewWaitUntilTerminated, ResultCode) and (ResultCode = 0);\n\t\t//never mind: if the .NET installer in UI mode shows \"Modify Setup\" (eg if dotnet dir deleted), in silent mode does nothing and returns 0.\n\t\t//\tCould call _IsDotNetInstalled and retry in UI mode, but now it does not work because this process uses old PATH env var (without dotnet path).\n\t\t//info: in Windows Sandbox .NET 9.0.3+ setup hangs for ~10 minutes. Same with UI. Used to work normally. See https://github.com/microsoft/Windows-Sandbox/issues/68\n\t\tDeleteFile(setupFilePath);\n\texcept\n\t\tif page.AbortedByUser then Log('Aborted by user.') else Log(GetExceptionMessage);\n\t\tResult := false;\n\tfinally\n\t\tpage.Hide;\n\tend;\nend;\n\nprocedure InstallExeForCurrentArch(fileName: String);\nvar\n  s, s64: String;\nbegin\n  if IsArm64 then\n  begin\n\t\ts := ExpandConstant('{app}\\') + fileName + '.exe';\n\t\ts64 := ExpandConstant('{app}\\') + fileName + '-x64.exe';\n\t\tDeleteFile(s64);\n    RenameFile(s, s64);\n    RenameFile(ExpandConstant('{app}\\') + fileName + '-arm.exe', s);\n\t\t//info: rename both, not rename+delete. Need both x64 and ARM64 files for portable LA.\n  end;\nend;\n\n\nfunction InitializeSetup(): Boolean;\nbegin\n  //Cpp_Install(0, '');\n  UnloadAuCppDll(false);\n  Result:=true;\nend;\n\nfunction InitializeUninstall(): Boolean;\nbegin\n  //Cpp_Uninstall(0);\n  UnloadAuCppDll(true);\n  Result:=true;\nend;\n\nprocedure CurStepChanged(CurStep: TSetupStep);\nbegin\n  case CurStep of\n    ssInstall:\n    begin\n      //Cpp_Install(1, ExpandConstant('{app}\\'));\n    end;\n    ssPostInstall:\n    begin\n      //Cpp_Install(2, ExpandConstant('{app}\\'));\n      InstallExeForCurrentArch('Au.Editor');\n      if not IsDotNetInstalled() then InstallDotNet();\n    end;\n  end;\nend;\n\nprocedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);\nvar\n  s1: String;\nbegin\n  case CurUninstallStep of\n//     usUninstall:\n//     begin\n//       Cpp_Uninstall(1);\n//       UnloadDLL(ExpandConstant('{app}\\Setup32.dll'));\n//     end;\n    usPostUninstall:\n    begin\n      s1:=ExpandConstant('{app}');\n      if DirExists(s1) and not RemoveDir(s1) then begin RestartReplace(s1, ''); end;\n    end;\n  end;\nend;\n\nprocedure CurPageChanged(CurPageID: Integer);\nbegin\n  if CurPageID = wpReady then\n  begin\n    if not IsDotNetInstalled then\n    begin\n      WizardForm.ReadyMemo.Lines.Add('');\n      WizardForm.ReadyMemo.Lines.Add('Additional tasks:');\n      WizardForm.ReadyMemo.Lines.Add('      Install .NET 10 Desktop Runtime (~60 MB download)');\n    end;\n  end;\nend;"
  },
  {
    "path": "Au.Editor/Panels/PanelBookmarks.cs",
    "content": "using System.Windows.Controls;\nusing System.Windows.Input;\nusing Au.Controls;\nusing Microsoft.CodeAnalysis.CSharp.Syntax;\nusing Microsoft.CodeAnalysis;\nusing System.Windows;\n\nnamespace LA;\n\nclass PanelBookmarks {\n\tKTreeView _tv;\n\t_Item _root;\n\tstring _file;\n\tbool _initOnce;\n\tint _save;\n\t\n\tpublic PanelBookmarks() {\n\t\t_tv = new() { Name = \"Bookmarks_list\", SingleClickActivate = true, SmallIndent = true };\n\t\tP.Children.Add(_tv);\n\t\t\n\t\tFilesModel.AnyWorkspaceLoadedAndDocumentsOpened += _LoadIfNeed;\n\t\t\n\t\tPanels.PanelManager[\"Bookmarks\"].DontActivateFloating = e => e == _tv;\n\t}\n\t\n\tpublic DockPanel P { get; } = new();\n\t\n\tvoid _LoadIfNeed() {\n\t\tif (_root != null) return;\n\t\t_root = new(null, true);\n\t\t\n\t\t_file = App.Model.WorkspaceDirectory + (miscInfo.isChildSession ? @\"\\bookmarks-pip.csv\" : @\"\\bookmarks.csv\");\n\t\tif (filesystem.exists(_file).File) {\n\t\t\ttry {\n\t\t\t\tvar csv = csvTable.load(_file);\n\t\t\t\t_Item folder = null;\n\t\t\t\tforeach (var a in csv.Rows) {\n\t\t\t\t\tif (a[0][0] == '#') {\n\t\t\t\t\t\tif (a[0].ToInt(out uint u, 1) && App.Model.FindById(u) is { } f) {\n\t\t\t\t\t\t\tint flags = a[1].ToInt();\n\t\t\t\t\t\t\t_root.AddChild(folder = new(f, 0 != (flags & 1)));\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tfolder = null;\n\t\t\t\t\t\t\t_SaveLater(1);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (folder != null) {\n\t\t\t\t\t\tfolder.AddChild(new(a[0].ToInt(), 0, a[1]));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception e1) { print.it(e1); }\n\t\t}\n\t\t_TvSetItems();\n\t\t\n\t\tif (!_initOnce) {\n\t\t\t_initOnce = true;\n\t\t\t\n\t\t\tFilesModel.UnloadingAnyWorkspace += () => {\n\t\t\t\tif (_root == null) return;\n\t\t\t\tSaveNowIfNeed();\n\t\t\t\t_root = null;\n\t\t\t\t_TvSetItems();\n\t\t\t};\n\t\t\t\n\t\t\tFilesModel.NeedRedraw += v => {\n\t\t\t\tif (!v.renamed) return;\n\t\t\t\tvar f = _FindItemOfFile(v.f);\n\t\t\t\tif (f != null) _tv.Redraw(f, v.remeasure);\n\t\t\t};\n\t\t\t\n\t\t\t_tv.ItemActivated += e => {\n\t\t\t\tif (e.Mod != 0) return;\n\t\t\t\tif (e.Item is _Item b && !b.IsFolder) {\n\t\t\t\t\t_SetActive(b, true);\n\t\t\t\t\tApp.Model.OpenAndGoTo(b.Parent.file, b.line);\n\t\t\t\t}\n\t\t\t};\n\t\t\t\n\t\t\t_tv.ItemClick += e => {\n\t\t\t\tvar b = e.Item as _Item;\n\t\t\t\tswitch (e.Button) {\n\t\t\t\tcase MouseButton.Left:\n\t\t\t\t\tswitch (e.Mod) {\n\t\t\t\t\tcase ModifierKeys.Control: _DeleteItem(b); break;\n\t\t\t\t\tcase ModifierKeys.Shift when !b.IsFolder: _Rename(b); break;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase MouseButton.Right:\n\t\t\t\t\t_ContextMenu(b);\n\t\t\t\t\tbreak;\n\t\t\t\tcase MouseButton.Middle:\n\t\t\t\t\t_SetActive(b, !b.IsActiveOrHasActiveChildren);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t};\n\t\t\t\n\t\t\t_tv.PreviewKeyDown += (_, e) => {\n\t\t\t\tif (e.Key is Key.Up or Key.Down && e.KeyboardDevice.Modifiers == ModifierKeys.Shift && _tv.FocusedItem is _Item { IsFolder: true } f) {\n\t\t\t\t\te.Handled = true;\n\t\t\t\t\t_MoveFolder(f, e.Key is Key.Up);\n\t\t\t\t}\n\t\t\t};\n\t\t\t\n\t\t\t_tv.EditLabelStarted += e => {\n\t\t\t\te.Text = (e.item as _Item).name; //edit without line number\n\t\t\t\te.SelectText();\n\t\t\t};\n\t\t\t\n\t\t\t_tv.RightClickInEmptySpace += () => _ContextMenu(null);\n\t\t\t\n\t\t\tPanels.Editor.ClosingDoc += doc => {\n\t\t\t\tif (_FindItemOfFile(doc) is { } f) {\n\t\t\t\t\tforeach (var b in f.Children()) _SetActive(b, false);\n\t\t\t\t}\n\t\t\t};\n\t\t\t\n\t\t\tApp.Timer1sWhenVisible += () => { if (_save != 0 && --_save <= 0) _SaveNow(); };\n\t\t}\n\t}\n\t\n\tpublic void SaveNowIfNeed() {\n\t\tif (_save != 0) _SaveNow();\n\t}\n\t\n\tvoid _SaveNow() {\n\t\tAction saveAction;\n\t\tif (_root.HasChildren) {\n\t\t\tvar csv = new csvTable { ColumnCount = 2 };\n\t\t\tfor (var f = _root.FirstChild; f != null; f = f.Next) {\n\t\t\t\tcsv[^0, 0] = \"#\" + f.file.IdString;\n\t\t\t\tif (f.IsExpanded) csv[^1, 1] = \"1\";\n\t\t\t\tfor (var b = f.FirstChild; b != null; b = b.Next) {\n\t\t\t\t\tcsv.Set(^0, 0, b.line);\n\t\t\t\t\tcsv[^1, 1] = b.name;\n\t\t\t\t}\n\t\t\t}\n\t\t\tsaveAction = () => { csv.Save(_file); };\n\t\t} else {\n\t\t\tsaveAction = () => { filesystem.saveText(_file, \"\"); };\n\t\t}\n\t\tif (!App.Model.TryFileOperation(FOSync.PrivateFileWrite, saveAction)) return;\n\t\t_save = 0;\n\t\t//print.it(\"saved\");\n\t}\n\t\n\tvoid _SaveLater(int afterS = 30) {\n\t\t//print.it(new StackTrace(true));\n\t\t_save = _save == 0 ? afterS : Math.Min(_save, afterS);\n\t}\n\t\n\tinternal void SciLoaded(SciCode doc) {\n\t\t_LoadIfNeed();\n\t\tvar f = _FindItemOfFile(doc); if (f == null) return;\n\t\tbool removed = false;\n\t\tfor (var b = f.FirstChild; b != null;) {\n\t\t\tint h = doc.aaaMarkerAdd(b.isActive ? SciTheme.Marker.Bookmark : SciTheme.Marker.BookmarkInactive, b.line);\n\t\t\tif (h == -1) {\n\t\t\t\tvar bb = b; b = b.Next;\n\t\t\t\tbb.Remove();\n\t\t\t\tremoved = true;\n\t\t\t\t_SaveLater(1);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tb.markerHandle = h;\n\t\t\tb = b.Next;\n\t\t}\n\t\tif (removed) _TvSetItems(true);\n\t}\n\t\n\t//_Item _FindItemOfFile(FileNode fn) => _root.Children().FirstOrDefault(o => o.file == fn); //slow, garbage\n\t_Item _FindItemOfFile(FileNode fn) {\n\t\tif (_root != null && fn != null)\n\t\t\tfor (var f = _root.FirstChild; f != null; f = f.Next)\n\t\t\t\tif (f.file == fn) return f;\n\t\treturn null;\n\t}\n\t_Item _FindItemOfFile(SciCode doc) => _FindItemOfFile(doc?.EFile);\n\t\n\tpublic void ToggleBookmark(int pos8 = -1) {\n\t\tvar doc = Panels.Editor.ActiveDoc;\n\t\tif (doc == null) return;\n\t\tbool useSelStart = pos8 < 0;\n\t\tint line = useSelStart ? doc.aaaLineFromPos() : doc.aaaLineFromPos(false, pos8);\n\t\tif (_IsBookmark(doc, line)) {\n\t\t\t_DeleteBookmark(doc, line);\n\t\t} else {\n\t\t\tif (!useSelStart) if (doc.aaaLineFromPos() == line) useSelStart = true;\n\t\t\t\n\t\t\tint h = doc.aaaMarkerAdd(SciTheme.Marker.Bookmark, line); if (h < 0) return;\n\t\t\tvar folder = _FindItemOfFile(doc);\n\t\t\tif (folder == null) {\n\t\t\t\t_root.AddChild(folder = new(doc.EFile, true), true);\n\t\t\t} else folder.SetIsExpanded(true);\n\t\t\t\n\t\t\tvar name = GetMarkerName_(doc, line, useSelStart) ?? \"Bookmark\";\n\t\t\t_Item b = new(line, h, name) { isActive = true };\n\t\t\tif (folder.Children().FirstOrDefault(o => o.line > line) is { } b2) b2.AddSibling(b, false);\n\t\t\telse folder.AddChild(b);\n\t\t\t\n\t\t\t_TvSetItems(true);\n\t\t\t_tv.SelectSingle(b);\n\t\t\t_SaveLater();\n\t\t}\n\t}\n\t\n\tinternal static string GetMarkerName_(SciCode doc, int line, bool useSelStart) {\n\t\tif (CodeInfo.GetDocumentAndFindNode(out var cd, out var node, useSelStart ? -2 : doc.aaaLineStart(true, line))) {\n\t\t\tvar code = cd.code;\n\t\t\tif (_Node(node) is string s1) return s1 + \"  \" + _Line();\n\t\t\treturn _Line();\n\t\t\t\n\t\t\tstring _Line() => \"¦  \" + doc.aaaLineText(line).Trim().Limit(50);\n\t\t\t\n\t\t\tstring _Node(SyntaxNode node) {\n\t\t\t\tfor (; node != null; node = node.Parent) {\n\t\t\t\t\tif (node is LocalFunctionStatementSyntax or BaseMethodDeclarationSyntax or BasePropertyDeclarationSyntax or EventFieldDeclarationSyntax) {\n\t\t\t\t\t\tswitch (node) {\n\t\t\t\t\t\tcase LocalFunctionStatementSyntax k:\n\t\t\t\t\t\t\tstring s1 = k.Identifier.Text + \"()\", s2 = _Node(k.Parent);\n\t\t\t\t\t\t\treturn s2 == null ? s1 : $\"{s1} in {s2}\";\n\t\t\t\t\t\tcase MethodDeclarationSyntax k:\n\t\t\t\t\t\t\treturn k.Identifier.Text + \"()\";\n\t\t\t\t\t\tcase ConstructorDeclarationSyntax k:\n\t\t\t\t\t\t\treturn k.Identifier.Text + \"()\";\n\t\t\t\t\t\tcase DestructorDeclarationSyntax k:\n\t\t\t\t\t\t\treturn \"~\" + k.Identifier.Text + \"()\";\n\t\t\t\t\t\tcase OperatorDeclarationSyntax k:\n\t\t\t\t\t\t\treturn \"operator \" + k.OperatorToken.Text;\n\t\t\t\t\t\tcase ConversionOperatorDeclarationSyntax k:\n\t\t\t\t\t\t\treturn \"operator \" + k.Type;\n\t\t\t\t\t\tcase PropertyDeclarationSyntax k:\n\t\t\t\t\t\t\treturn k.Identifier.Text;\n\t\t\t\t\t\tcase IndexerDeclarationSyntax k:\n\t\t\t\t\t\t\treturn \"this[]\";\n\t\t\t\t\t\tcase EventDeclarationSyntax k:\n\t\t\t\t\t\t\treturn \"event \" + k.Identifier.Text;\n\t\t\t\t\t\tcase EventFieldDeclarationSyntax k:\n\t\t\t\t\t\t\treturn \"event \" + k.Declaration.Variables;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\t\n\tpublic void NextBookmark(bool up) {\n\t\t//If there are no active bookmarks, go to next/prev bookmark in this file.\n\t\t//Else if there is an active bookmark below or above (depending on *up*) current line in this file, go to it.\n\t\t//Else go to next/prev active bookmark in any file. Start searching from (not including):\n\t\t//\tIf this file contains bookmarks - from next/prev file in _root.Children.\n\t\t//\tElse from the focused bookmark in _tv.\n\t\t\n\t\tif (_root?.HasChildren != true) return;\n\t\t\n\t\t_Item go = null;\n\t\tvar doc = Panels.Editor.ActiveDoc;\n\t\tvar file = _FindItemOfFile(doc);\n\t\t\n\t\tif (_root.Descendants().Any(static o => o.isActive)) {\n\t\t\tif (file != null) {\n\t\t\t\tvar (prev, next) = _GetPrevNextInDoc(true);\n\t\t\t\tgo = up ? prev : next;\n\t\t\t}\n\t\t\tif (go == null) {\n\t\t\t\tvar a = _root.Descendants().ToArray();\n\t\t\t\tvar from = file != null ? (up ? file.FirstChild : file.LastChild) : _tv.FocusedItem as _Item;\n\t\t\t\tfrom ??= up ? a[^1] : a[0];\n\t\t\t\tint i = Array.IndexOf(a, from);\n\t\t\t\tfor (; ; ) {\n\t\t\t\t\tif (up) i = (i > 0 ? i : a.Length) - 1; else i = ++i < a.Length ? i : 0;\n\t\t\t\t\tif (a[i].isActive) { go = a[i]; break; }\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (file != null) {\n\t\t\tvar (prev, next) = _GetPrevNextInDoc(false);\n\t\t\tif (prev == null && next == null) return;\n\t\t\tgo = up ? prev ?? file.LastChild : next ?? file.FirstChild;\n\t\t}\n\t\t\n\t\tif (go != null) {\n\t\t\tApp.Model.OpenAndGoTo(go.Parent.file, go.line);\n\t\t\t_tv.SelectSingle(go);\n\t\t}\n\t\t\n\t\t(_Item prev, _Item next) _GetPrevNextInDoc(bool active) {\n\t\t\tint line = doc.aaaLineFromPos();\n\t\t\t_Item prev = null;\n\t\t\tfor (var v = file.FirstChild; v != null; v = v.Next) {\n\t\t\t\tif (active && !v.isActive) continue;\n\t\t\t\tif (v.line < line) prev = v; else if (v.line > line) return (prev, v);\n\t\t\t}\n\t\t\treturn (prev, null);\n\t\t}\n\t}\n\t\n\tvoid _SetActive(_Item b, bool active) {\n\t\tif (b.IsFolder) {\n\t\t\tforeach (var v in b.Children()) _SetActive(v, active);\n\t\t} else if (b.isActive != active) {\n\t\t\tb.isActive = active;\n\t\t\t_tv.Redraw(b);\n\t\t\t\n\t\t\tif (b.Parent.file.OpenDoc is { } doc) {\n\t\t\t\tdoc.aaaMarkerDeleteHandle(b.markerHandle);\n\t\t\t\tb.markerHandle = doc.aaaMarkerAdd(b.isActive ? SciTheme.Marker.Bookmark : SciTheme.Marker.BookmarkInactive, b.line);\n\t\t\t}\n\t\t}\n\t}\n\t\n\tvoid _TvSetItems(bool modified = false) {\n\t\tif (_root?.Count > 0) {\n\t\t\t_tv.SetItems(_root.Children(), modified);\n\t\t} else {\n\t\t\t_tv.SetItems(null);\n\t\t}\n\t}\n\t\n\tvoid _ContextMenu(_Item b) {\n\t\tvar m = new popupMenu();\n\t\t\n\t\tif (b != null) {\n\t\t\t_tv.SelectSingle(b);\n\t\t\t\n\t\t\tif (b.IsFolder) {\n\t\t\t\tm[\"Move up\\tShift+Up\", disable: b.Previous == null] = o => _MoveFolder(b, true);\n\t\t\t\tm[\"Move down\\tShift+Down\", disable: b.Next == null] = o => _MoveFolder(b, false);\n\t\t\t\tm.Separator();\n\t\t\t\tm[\"Delete these bookmarks\\tCtrl+click\"] = o => _DeleteItem(b);\n\t\t\t\tm.AddCheck(\"Active bookmarks\\tM-click\", b.IsActiveOrHasActiveChildren, o => _SetActive(b, o.IsChecked));\n\t\t\t} else {\n\t\t\t\tm[\"Delete\\tCtrl+click\"] = o => _DeleteItem(b);\n\t\t\t\tm[\"Rename\\tShift+click\"] = o => _Rename(b);\n\t\t\t\tm.AddCheck(\"Active\\tM-click\", b.isActive, o => _SetActive(b, o.IsChecked));\n\t\t\t}\n\t\t\tm.Separator();\n\t\t} else if (!(_root?.Count > 0)) {\n\t\t\tprint.it(\"To add a bookmark, click the white margin in the code editor.\");\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tm[\"Deactivate all\"] = o => { foreach (var v in _root.Children()) _SetActive(v, false); };\n\t\tm[\"Collapse all\"] = o => { foreach (var v in _root.Children()) _tv.Expand(v, false); };\n\t\tm.Submenu(\"Delete all\", m => {\n\t\t\tm[\"Delete all inactive bookmarks\"] = o => {\n\t\t\t\t//if (1 != dialog.show(\"Delete all inactive bookmarks\", \"Are you sure?\", \"2 No|1 Yes\", icon: DIcon.Warning, owner: _tv)) return;\n\t\t\t\tforeach (var v in _root.Descendants().ToArray()) if (!v.IsFolder && !v.isActive) _DeleteItem(v);\n\t\t\t};\n\t\t\tm[\"Delete all bookmarks\"] = o => {\n\t\t\t\t//if (1 != dialog.show(\"Delete all bookmarks\", \"Are you sure?\", \"2 No|1 Yes\", icon: DIcon.Warning, owner: _tv)) return;\n\t\t\t\tforeach (var v in _root.Children().ToArray()) _DeleteItem(v);\n\t\t\t};\n\t\t});\n\t\t\n\t\tm.Show(owner: _tv);\n\t}\n\t\n\tbool _IsBookmark(SciCode doc, int line) => (doc.Call(Sci.SCI_MARKERGET, line) & 3 << SciTheme.Marker.Bookmark) != 0;\n\t\n\t_Item _BookmarkFromLine(SciCode doc, int line) {\n\t\tif (_FindItemOfFile(doc) is { } folder) {\n\t\t\tfor (int i = 0; ; i++) {\n\t\t\t\tint h = doc.Call(Sci.SCI_MARKERHANDLEFROMLINE, line, i);\n\t\t\t\tif (h < 0) return null;\n\t\t\t\tfor (var b = folder.FirstChild; b != null; b = b.Next) if (b.markerHandle == h) return b;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\t\n\tvoid _DeleteBookmark(SciCode doc, int line, bool sciDelete = true) {\n\t\tif (_BookmarkFromLine(doc, line) is { } b) {\n\t\t\tif (sciDelete) doc.aaaMarkerDeleteHandle(b.markerHandle);\n\t\t\t_DeleteBookmarkL(b);\n\t\t}\n\t}\n\t\n\tvoid _DeleteBookmarkL(_Item b) {\n\t\tvar folder = b.Parent;\n\t\tb.Remove();\n\t\tif (!folder.HasChildren) folder.Remove();\n\t\t_TvSetItems(true);\n\t\t_SaveLater();\n\t}\n\t\n\tvoid _DeleteItem(_Item b) {\n\t\tif (b.IsFolder) {\n\t\t\tforeach (var v in b.Children().ToArray()) _DeleteItem(v);\n\t\t} else {\n\t\t\tvar folder = b.Parent;\n\t\t\tif (b.markerHandle != 0 && folder.file.OpenDoc is { } doc) {\n\t\t\t\tdoc.aaaMarkerDeleteHandle(b.markerHandle);\n\t\t\t}\n\t\t\t_DeleteBookmarkL(b);\n\t\t}\n\t}\n\t\n\tvoid _Rename(_Item b) {\n\t\tPanels.PanelManager[P].Visible = true;\n\t\t_tv.SelectSingle(b);\n\t\t_tv.EditLabel(b);\n\t}\n\t\n\tvoid _MoveFolder(_Item f, bool up) {\n\t\tif (up && f == _root.FirstChild) return;\n\t\tvar ff = up ? f.Previous : f.Next;\n\t\tif (ff == null) return;\n\t\tf.Remove();\n\t\tff.AddSibling(f, after: !up);\n\t\t_TvSetItems(true);\n\t\t_SaveLater();\n\t}\n\t\n\tinternal void SciDeletingLineWithMarker(SciCode doc, int line) {\n\t\t_DeleteBookmark(doc, line, false);\n\t}\n\t\n\tinternal void SciModified(SciCode doc, in Sci.SCNotification n) {\n\t\tif (n.linesAdded != 0) {\n\t\t\t//update the line field, and display\n\t\t\tvar folder = _FindItemOfFile(doc);\n\t\t\tif (folder == null) return;\n\t\t\tbool redraw = false;\n\t\t\tfor (var b = folder.FirstChild; b != null; b = b.Next) {\n\t\t\t\tint line = doc.Call(Sci.SCI_MARKERLINEFROMHANDLE, b.markerHandle);\n\t\t\t\tif (line != b.line) {\n\t\t\t\t\tb.line = line;\n\t\t\t\t\tredraw = true;\n\t\t\t\t\t_SaveLater(2 * 60);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (redraw && folder.IsExpanded) _tv.Redraw();\n\t\t\t//never mind: don't need to redraw if the Bookmarks panel isn't visible. It's fast, just invalidates.\n\t\t}\n\t}\n\t\n\tinternal void AddMarginMenuItems_(SciCode doc, popupMenu m, int pos8) {\n\t\tif (_BookmarkFromLine(doc, doc.aaaLineFromPos(false, pos8)) is { } b) {\n\t\t\tm[\"Delete bookmark\", \"*Material.BookmarkMinus @16\" + EdIcons.darkYellow] = o => ToggleBookmark(pos8);\n\t\t\tm[\"Rename bookmark\"/*, \"*Modern.Edit @14\" + EdIcons.darkYellow*/] = o => _Rename(b);\n\t\t\tm.AddCheck(\"Active bookmark\\tM-click\", b.isActive, o => _SetActive(b, o.IsChecked));\n\t\t} else {\n\t\t\tm[\"Add bookmark\", \"*Material.Bookmark @16\" + EdIcons.darkYellow] = o => ToggleBookmark(pos8);\n\t\t}\n\t\tm[\"Previous bookmark\", \"*JamIcons.ArrowSquareUp\" + EdIcons.black] = o => NextBookmark(true);\n\t\tm[\"Next bookmark\", \"*JamIcons.ArrowSquareDown\" + EdIcons.black] = o => NextBookmark(false);\n\t}\n\t\n\tinternal void SciMiddleClick_(SciCode doc, int line) {\n\t\tif (_BookmarkFromLine(doc, line) is { } b) {\n\t\t\t_SetActive(b, !b.isActive);\n\t\t}\n\t}\n\t\n\tinternal void FileDeleted(IEnumerable<FileNode> e) {\n\t\tforeach (var v in e) {\n\t\t\tif (_FindItemOfFile(v) is { } folder) {\n\t\t\t\tfolder.Remove();\n\t\t\t\t_TvSetItems(true);\n\t\t\t\t_SaveLater();\n\t\t\t}\n\t\t}\n\t}\n\t\n\tclass _Item : TreeBase<_Item>, ITreeViewItem {\n\t\tpublic readonly FileNode file; //if folder\n\t\tpublic int line, markerHandle; //if bookmark\n\t\tpublic string name;\n\t\treadonly bool _isFolder;\n\t\tbool _isExpanded;\n\t\tpublic bool isActive;\n\t\t\n\t\tpublic _Item(FileNode file, bool isExpanded) {\n\t\t\tthis.file = file;\n\t\t\t_isFolder = true;\n\t\t\t_isExpanded = isExpanded;\n\t\t}\n\t\t\n\t\tpublic _Item(int line, int markerHandle, string name) {\n\t\t\tthis.line = line;\n\t\t\tthis.markerHandle = markerHandle;\n\t\t\tthis.name = name;\n\t\t}\n\t\t\n#if DEBUG\n\t\tpublic override string ToString() => ((ITreeViewItem)this).DisplayText;\n#endif\n\t\t\n\t\t#region ITreeViewItem\n\t\t\n\t\tpublic bool IsFolder => _isFolder;\n\t\t\n\t\tpublic bool IsExpanded => _isExpanded;\n\t\t\n\t\tpublic void SetIsExpanded(bool yes) { _isExpanded = yes; Panels.Bookmarks._SaveLater(5 * 60); }\n\t\t\n\t\tIEnumerable<ITreeViewItem> ITreeViewItem.Items => base.Children();\n\t\t\n\t\tstring ITreeViewItem.DisplayText => _isFolder ? (file.Parent.HasParent ? $\"{file.Name}  [{file.Parent.ItemPath}]\" : file.Name) : $\"{(line + 1).ToS()}  {name}\";\n\t\t//never mind: after moving the file should display the new path (just redraw).\n\t\t\n\t\tvoid ITreeViewItem.SetNewText(string text) { name = text; Panels.Bookmarks._SaveLater(); }\n\t\t\n\t\tobject ITreeViewItem.Image => _isFolder ? EdIcons.FolderArrow(_isExpanded)\n\t\t\t: isActive ? \"*Material.Bookmark @14\" + EdIcons.darkYellow\n\t\t\t: \"*Material.BookmarkOutline @14\" + EdIcons.darkYellow;\n\t\t\n\t\t#endregion\n\t\t\n\t\tpublic bool IsActiveOrHasActiveChildren => IsFolder ? Children().Any(static o => o.isActive) : isActive;\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Panels/PanelFiles.cs",
    "content": "using System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Input;\nusing System.Windows.Documents;\nusing Au.Controls;\nusing ToolLand;\n\nnamespace LA;\n\npartial class PanelFiles {\n\tFilesModel.FilesView _tv;\n\tKTextBox _tFind;\n\ttimer _timerFind;\n\tFileNode _firstFoundFile;\n\tKPopup _ttError;\n\t\n\tpublic PanelFiles() {\n\t\tP.UiaSetName(\"Files panel\");\n\t\tP.Background = SystemColors.ControlBrush;\n\t\t\n\t\tvar b = new wpfBuilder(P).Columns(-1).Options(margin: new());\n\t\tb.Row(-1).Add(out _tv).Name(\"Files_list\", true);\n\t\t\n\t\tb.Row(2) //maybe 4 would look better, but then can be confused with a splitter\n\t\t\t.Add<Border>().Border(thickness2: new(0, 1, 0, 1));\n\t\t\n\t\t_tFind = new() { BorderThickness = default };\n\t\tb.R.Add<AdornerDecorator>().Child().Add(_tFind).Name(\"Find_file\", true)\n\t\t\t.Watermark(\"Find file\").Tooltip(@\"Part of file name, or wildcard expression.\nExamples: part, start*, *end.cs, **r regex, **m green.cs||blue.cs.\");\n\t\t\n\t\t//CONSIDER: File bookmarks. And/or tags. Probably not very useful. Unless many users will want it.\n\t\t\n\t\tb.End();\n\t\t\n\t\t_tFind.TextChanged += (_, _) => { (_timerFind ??= new(_ => _Find())).After(_tFind.Text.Length switch { 1 => 1200, 2 => 600, _ => 300 }); };\n\t\t_tFind.GotKeyboardFocus += (_, _) => P.Dispatcher.InvokeAsync(() => _tFind.SelectAll());\n\t\t_tFind.KeyDown += (_, e) => { if (e.Key is Key.Enter && _firstFoundFile != null) App.Model.OpenAndGoTo(_firstFoundFile); };\n\t\t\n\t\t_tv.EditLabelStarted += e => {\n\t\t\tvar f = e.item as FileNode;\n\t\t\tvar s = e.Text;\n\t\t\tif (f.IsFolder) {\n\t\t\t\tif (s[0] == '@') e.SelectText(1, s.Length);\n\t\t\t} else if (f.IsOtherFileType) {\n\t\t\t\tint i = pathname.findExtension(s);\n\t\t\t\tif (i > 0) e.SelectText(0, i);\n\t\t\t}\n\t\t};\n\t\t\n\t\tEditGoBack.DisableUI();\n\t}\n\t\n\tpublic UserControl P { get; } = new();\n\t\n\tpublic FilesModel.FilesView TreeControl => _tv;\n\t\n\tprivate void _Find() {\n\t\t_firstFoundFile = null;\n\t\tvar s = _tFind.Text;\n\t\t\n\t\twildex wild = null; string err = null;\n\t\tif (wildex.hasWildcardChars(s)) {\n\t\t\ttry { wild = new wildex(s); }\n\t\t\tcatch (Exception ex1) {\n\t\t\t\terr = ex1.Message;\n\t\t\t\tif (err.Starts(\"Invalid \\\"**options\") && !s.Contains(' ')) err = null;\n\t\t\t\ts = null;\n\t\t\t}\n\t\t}\n\t\tif (err != null) TUtil.InfoTooltip(ref _ttError, _tFind, err); else _ttError?.Close();\n\t\t\n\t\tif (s.NE()) {\n\t\t\tPanels.Found.ClearResults(PanelFound.Found.Files);\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tvar workingState = Panels.Found.Prepare(PanelFound.Found.Files, s, out var b);\n\t\t\n\t\tforeach (var f in App.Model.Root.Descendants()) {\n\t\t\tvar name = f.Name;\n\t\t\tint i = -1;\n\t\t\tif (wild != null) {\n\t\t\t\tif (!wild.Match(name)) continue;\n\t\t\t} else {\n\t\t\t\ti = name.Find(s, true);\n\t\t\t\tif (i < 0) continue;\n\t\t\t}\n\t\t\t\n\t\t\tvar path = f.ItemPath;\n\t\t\tint i1 = path.Length - name.Length;\n\t\t\tb.Link2(f).Gray(path.AsSpan(0, i1)).Text(name);\n\t\t\tif (i >= 0) {\n\t\t\t\ti += b.Length - name.Length;\n\t\t\t\tb.Indic(PanelFound.Indicators.HiliteY, i, i + s.Length);\n\t\t\t}\n\t\t\tb.Link_();\n\t\t\tif (f.IsFolder) b.Green(\"    //folder\");\n\t\t\tb.NL();\n\t\t\t_firstFoundFile ??= f;\n\t\t}\n\t\t\n\t\tif (b.Length == 0) return;\n\t\t\n\t\tPanels.Found.SetResults(workingState, b);\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Panels/PanelFind.cs",
    "content": "//TODO2: once crashed when replacing in files.\n//\tCan't reproduce.\n//\tReplaced </msdn> with </ms> in Au project. Crashed in the middle.\n//\tProbably crashed while saving, because left one ~backup~ file. The normal file was saved OK.\n//\tProcess ended quickly. No exception, no log event, no dump file.\n\nusing Au.Controls;\nusing ToolLand;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Input;\nusing System.Media;\nusing System.Windows.Data;\n\n//CONSIDER: right-click \"Find\" - search backward. The same for \"Replace\" (reject \"find next\"). Rarely used.\n//CONSIDER: option to replace and don't find next until next click. Eg Eclipse has buttons \"Replace\" and \"Replace/Find\". Or maybe delay to preview.\n\nnamespace LA;\n\nclass PanelFind {\n\tKScintilla _tFind, _tReplace;\n\tKCheckNoBox _cCase, _cWord, _cRegex;\n\tButton _bInFiles, _bFilter;\n\tKPopup _ttRegex, _ttNext;\n\t\n\tpublic PanelFind() {\n\t\tP.UiaSetName(\"Find panel\");\n\t\t\n\t\tvar b = new wpfBuilder(P).Columns(-1).Brush(SystemColors.ControlBrush);\n\t\tb.Options(modifyPadding: false, margin: new Thickness(2));\n\t\tb.AlsoAll((b, _) => { if (b.Last is Button k) k.Padding = new(1, 0, 1, 1); });\n\t\t\n\t\tKScintilla _AddTextbox(string name) {\n\t\t\tvar k = new KScintilla { AaWrapLines = true };\n\t\t\tvar border = b.Row(-1).xAddInBorder(k);\n\t\t\tborder.Margin = new(2, 0, 2, 0);\n\t\t\tb.Name(name, true);\n\t\t\tk.AaHandleCreated += k => _tFindReplace_HandleCreated(k);\n\t\t\treturn k;\n\t\t}\n\t\t\n\t\tb.Row(3);\n\t\t_tFind = _AddTextbox(\"Find_text\");\n\t\tb.xAddSplitterH();\n\t\t_tReplace = _AddTextbox(\"Replace_text\");\n\t\t\n\t\tb.R.StartGrid().Columns((-1, ..80), (-1, ..80), (-1, ..80), 0).Margin(top: 3);\n\t\tb.R.AddButton(out var bFind, \"Find\", _ => _FindNextInEditor()).Tooltip(\"Find next in editor.\\nRight click - find previous.\");\n\t\tbFind.MouseRightButtonUp += (_, _) => FindNextInEditor(true);\n\t\tb.AddButton(out var bReplace, \"Replace\", _ => _FindNextInEditor(replace: true)).Tooltip(\"Replace current found text in editor and find next.\\nRight click - find next.\");\n\t\tbReplace.MouseRightButtonUp += (_, _) => _FindNextInEditor();\n\t\tb.AddButton(\"Repl. all\", _bReplaceAll_Click).Tooltip(\"Replace all in editor\");\n\t\t\n\t\tb.R.AddButton(out _bInFiles, \"In files\", _ => _FindAllInFiles()).Tooltip(\"Find text in files\");\n\t\tb.StartStack();\n\t\tb.xAddButtonIcon(out _bFilter, \"*Material.FolderSearchOutline\" + EdIcons.green, _FilterMenu, \"Let 'In files' search only in current project or root folder\").Padding(1, 0, 1, 1);\n\t\tb.xAddButtonIcon(\"*EvaIcons.Options2\" + EdIcons.green, _ => _Options(), \"More options\");\n\t\t\n\t\tvar cmd1 = App.Commands[nameof(Menus.Edit.Navigate.Go_back)];\n\t\tb.xAddButtonIcon(EdIcons.Back, _ => Menus.Edit.Navigate.Go_back(), \"Go back\").Disabled(!cmd1.Enabled);\n\t\tvar bBack = b.Last;\n\t\tcmd1.CanExecuteChanged += (o, e) => bBack.IsEnabled = cmd1.Enabled;\n\t\t\n\t\tb.xAddButtonIcon(\"*MaterialDesign.SavedSearch\" + EdIcons.black, _SavedSearches, \"Saved searches\");\n\t\t\n\t\tb.End();\n\t\t\n\t\tb.R.StartStack();\n\t\tb.Add(out _cCase, \"Case\").Tooltip(\"Match case\").Margin(right: 0).Checked(App.Settings.find_case);\n\t\tb.Add(out _cWord, \"Word\").Tooltip(\"Whole word\").Margin(right: 0).Checked(App.Settings.find_word);\n\t\t\n\t\tb.Add(out _cRegex).Tooltip(\"Regular expression\");\n\t\tb.StartGrid(true).Columns(0, 0);\n\t\tb.Add<TextBlock>(\"Regex\").Margin(\"0\");\n\t\tb.Add(out KButtonRoundNoBorder rxTool, ImageUtil.LoadWpfImageElement(\"*FileIcons.Regex @10\" + EdIcons.blue)).Margin(2, 0, -3, -1).Tooltip(\"Regex tool\");\n\t\trxTool.Click += (_, _) => { _cRegex.IsChecked = true; _ShowRegexInfo(_tReplace.IsFocused ? _tReplace : _tFind); };\n\t\tb.End();\n\t\tb.End().End().End();\n\t\t\n\t\t_cCase.CheckChanged += _CheckedChanged;\n\t\t_cWord.CheckChanged += _CheckedChanged;\n\t\t_cRegex.CheckChanged += _CheckedChanged;\n\t\t\n\t\tP.IsVisibleChanged += (_, _) => {\n\t\t\tif (P.IsVisible) UpdateQuickResults();\n\t\t\telse {\n\t\t\t\tforeach (var d in Panels.Editor.OpenDocs) d.EInicatorsFound_(null);\n\t\t\t\t\n\t\t\t\tApp.Settings.find_case = _cCase.IsChecked;\n\t\t\t\tApp.Settings.find_word = _cWord.IsChecked;\n\t\t\t}\n\t\t};\n\t}\n\t\n\tpublic UserControl P { get; } = new();\n\t\n\tinternal void CodeStylesChanged_() {\n\t\tif (!_tFind.IsLoaded) return;\n\t\t_SetCodeStyles(_tFind);\n\t\t_SetCodeStyles(_tReplace);\n\t}\n\t\n\tvoid _SetCodeStyles(KScintilla k) {\n\t\tSciTheme.Current.ToScintilla(k, fontName: App.Settings.font_find.name, fontSize: App.Settings.font_find.size);\n\t\tk.aaaStyleForeColor(255, 0xa0a0a0); //watermark\n\t\t\n\t\tif (k.Parent is Border { Parent: Grid g } b) {\n\t\t\tdouble h = Dpi.Unscale(k.aaaLineHeight(), k._dpi);\n\t\t\tg.RowDefinitions[Grid.GetRow(b)].MinHeight = h + 3;\n\t\t}\n\t}\n\t\n\t#region control events\n\t\n\tvoid _tFindReplace_HandleCreated(KScintilla k) {\n\t\t_SetCodeStyles(k);\n\t\t\n\t\tk.aaaMarginSetWidth(1, 0);\n\t\tk.aaaMarginSetWidth(-1, 2);\n\t\t\n\t\tif (k == _tFind) k.AaTextChanged += _ => { _RegexStyling(); UpdateQuickResults(); };\n\t\t\n\t\t_SetWatermark(true);\n\t\t\n\t\tvoid _SetWatermark(bool set) {\n\t\t\tif (set) {\n\t\t\t\tk.aaaMarginSetWidth(-1, -4);\n\t\t\t\tk.Call(Sci.SCI_EOLANNOTATIONSETSTYLE, 0, 255);\n\t\t\t\tk.aaaSetString(Sci.SCI_EOLANNOTATIONSETTEXT, 0, k == _tFind ? \"Find\"u8 : \"Replace\"u8);\n\t\t\t\tk.Call(Sci.SCI_EOLANNOTATIONSETVISIBLE, 1);\n\t\t\t} else {\n\t\t\t\tk.Call(Sci.SCI_EOLANNOTATIONSETVISIBLE);\n\t\t\t\tk.aaaMarginSetWidth(-1, 2);\n\t\t\t}\n\t\t}\n\t\t\n\t\tk.MessageHook += (nint hwnd, int msg, nint wp, nint lp, ref bool handled) => {\n\t\t\tvar w = (wnd)hwnd;\n\t\t\tif (msg == Api.WM_CONTEXTMENU) {\n\t\t\t\tvar m = new popupMenu();\n\t\t\t\tm[\"Undo\\tCtrl+Z\", disable: 0 == k.Call(Sci.SCI_CANUNDO)] = o => k.Call(Sci.SCI_UNDO);\n\t\t\t\tm[\"Redo\\tCtrl+Y\", disable: 0 == k.Call(Sci.SCI_CANREDO)] = o => k.Call(Sci.SCI_REDO);\n\t\t\t\tm[\"Cut\\tCtrl+X\", disable: !k.aaaHasSelection] = o => k.Call(Sci.SCI_CUT);\n\t\t\t\tm[\"Copy\\tCtrl+C\", disable: !k.aaaHasSelection] = o => k.Call(Sci.SCI_COPY);\n\t\t\t\tm[\"Paste\\tCtrl+V\", disable: 0 == k.Call(Sci.SCI_CANPASTE)] = o => k.Call(Sci.SCI_PASTE);\n\t\t\t\tm[\"Select all\\tCtrl+A\"] = o => k.Call(Sci.SCI_SELECTALL);\n\t\t\t\tm[\"Clear\\tM-click\"] = o => k.aaaClearText();\n\t\t\t\tm[\"Recent\\tM-click\"] = o => _RecentPopupList(k);\n\t\t\t\tm.Show(owner: w);\n\t\t\t} else if (msg == Api.WM_SETFOCUS || msg == Api.WM_KILLFOCUS) {\n\t\t\t\tbool focus = msg == Api.WM_SETFOCUS;\n\t\t\t\tif (focus) _SetWatermark(false); else if (k.aaaLen8 == 0) _SetWatermark(true);\n\t\t\t\tif (_cRegex.IsChecked) {\n\t\t\t\t\tif (focus) {\n\t\t\t\t\t\t//use timer to avoid temporary focus problems, for example when tabbing quickly or closing active Regex window\n\t\t\t\t\t\ttimer.after(70, _ => { if (k.AaWnd.IsFocused) _ShowRegexInfo(k, onFocus: true); });\n\t\t\t\t\t} else if (_regexWindow?.IsVisible is true) {\n\t\t\t\t\t\ttimer.after(70, _ => {\n\t\t\t\t\t\t\tif (_regexWindow?.IsVisible is true && !_regexWindow.Hwnd.IsActive) {\n\t\t\t\t\t\t\t\tvar c = Api.GetFocus();\n\t\t\t\t\t\t\t\tif (c != _tFind.AaWnd && c != _tReplace.AaWnd) _regexWindow.Hwnd.ShowL(false);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (msg == Api.WM_MBUTTONUP) {\n\t\t\t\tif (k.aaaLen8 > 0) k.aaaClearText(); else _RecentPopupList(k);\n\t\t\t}\n\t\t\treturn 0;\n\t\t};\n\t}\n\t\n\tRegexWindow _regexWindow;\n\tstring _regexTopic;\n\t\n\tvoid _ShowRegexInfo(KScintilla k, bool onFocus = false) {\n\t\tif (onFocus) {\n\t\t\tif (_regexWindow == null || _regexWindow.UserClosed) return;\n\t\t} else {\n\t\t\t_regexWindow ??= new();\n\t\t\t_regexWindow.UserClosed = false;\n\t\t}\n\t\t\n\t\tif (_regexWindow.Hwnd.Is0) {\n\t\t\tvar r = P.RectInScreen();\n\t\t\tr.Offset(0, -20);\n\t\t\t_regexWindow.ShowByRect(App.Wmain, Dock.Right, r, true);\n\t\t} else _regexWindow.Hwnd.ShowL(true);\n\t\t\n\t\t_regexWindow.InsertInControl = k;\n\t\t\n\t\tbool replace = k == _tReplace;\n\t\tvar s = _regexWindow.CurrentTopic;\n\t\tif (s == \"replace\") {\n\t\t\tif (!replace) _regexWindow.CurrentTopic = _regexTopic;\n\t\t} else if (replace) {\n\t\t\t_regexTopic = s;\n\t\t\t_regexWindow.CurrentTopic = \"replace\";\n\t\t}\n\t}\n\t\n\tunsafe void _RegexStyling() {\n\t\tif (!_cRegex.IsChecked) return;\n\t\tvar s = _tFind.aaaText;\n\t\tif (s.NE()) return;\n\t\tvar b = new byte[s.Length];\n\t\tRegexParser.GetScintillaStylingBytes16(s, PSFormat.Regexp, b);\n\t\t_tFind.aaaSetStyling(0, KScintilla.aaaConvertStylingBytesToUtf8(b, s));\n\t}\n\t\n\tvoid _CheckedChanged(object sender, RoutedEventArgs e) {\n\t\tif (sender == _cWord) {\n\t\t\tif (_cWord.IsChecked) _cRegex.IsChecked = false;\n\t\t} else if (sender == _cRegex) {\n\t\t\tif (_cRegex.IsChecked) {\n\t\t\t\t_cWord.IsChecked = false;\n\t\t\t\t\n\t\t\t\t_RegexStyling();\n\t\t\t} else {\n\t\t\t\t_regexWindow?.Close();\n\t\t\t\t_regexWindow = null;\n\t\t\t\t\n\t\t\t\t_tFind.Call(Sci.SCI_STARTSTYLING);\n\t\t\t\t_tFind.Call(Sci.SCI_SETSTYLING, _tFind.aaaLen8);\n\t\t\t}\n\t\t}\n\t\tUpdateQuickResults();\n\t}\n\t\n\tvoid _Options() {\n\t\tvar b = new wpfBuilder(\"Find text options\").WinSize(420);\n\t\tb.R.StartGrid<KGroupBox>(\"Find in files\");\n\t\tb.R.Add(\"Search in\", out ComboBox cbFileType, true).Items(\"All files|C# files (*.cs)|Other files\").Select(_SearchIn);\n\t\tb.R.Add(\n\t\t\tnew TextBlock() { TextWrapping = TextWrapping.Wrap, Text = \"Skip files whose workspace paths match a wildcard from this list\" },\n\t\t\tout TextBox tSkip,\n\t\t\tApp.Settings.find_skip,\n\t\t\trow2: 0)\n\t\t\t.Multiline(100, TextWrapping.NoWrap)\n\t\t\t.Tooltip(@\"Example:\n*.exe\n\\FolderA\\*\n*\\FolderB\\*\n//Comment\");\n\t\tb.R.Add(out KCheckBox cParallel, \"Load files in parallel threads\").Checked(App.Settings.find_parallel)\n\t\t\t.Tooltip(\"\"\"\nLoad files in multiple threads simultaneously.\nMakes much faster if disk is SSD, but much slower if HDD. Always faster if files are in the OS file cache. This feature can be useful if the workspace contains >1000 files.\nTo see maximal speed difference, before searching clear the OS file cache or restart the computer. The search time is displayed at the bottom of results if there are slow files; the 'Slow files' list does not reflect the actual speed gain.\nThis setting also is used by 'Find references' etc.\n\"\"\");\n\t\tint iSlow = App.Settings.find_printSlow; string sSlow = iSlow > 0 ? iSlow.ToS() : null;\n\t\tb.R.StartStack().Add(\"Print file load+search times >=\", out TextBox tSlow, sSlow).Width(50).Add<Label>(\"ms\").End();\n\t\tb.End();\n\t\tb.R.AddOkCancel();\n\t\tb.End();\n\t\tb.Window.ShowInTaskbar = false;\n\t\tif (!b.ShowDialog(App.Wmain)) return;\n\t\tApp.Settings.find_searchIn = cbFileType.SelectedIndex;\n\t\tApp.Settings.find_skip = tSkip.Text; _skip = null;\n\t\tApp.Settings.find_parallel = cParallel.IsChecked;\n\t\tApp.Settings.find_printSlow = tSlow.Text.ToInt();\n\t\t\n\t\t//rejected: support wildex in 'skip'. Not useful.\n\t}\n\t\n\tvoid _FilterMenu(WBButtonClickArgs e) {\n\t\tint f = _filter;\n\t\tvar m = new popupMenu();\n\t\tm.AddRadio(\"Search in entire workspace\", f == 0, _ => f = 0, image: _FilterImage(0));\n\t\tm.AddRadio(\"Search in current root folder\", f == 1, _ => f = 1, image: _FilterImage(1));\n\t\tm.AddRadio(\"Search in current @Project\", f == 2, _ => f = 2, image: _FilterImage(2));\n\t\tm.Show();\n\t\t_SetFilter(f);\n\t}\n\tint _filter; //0 workspace, 1 root folder, 2 project or root folder\n\t\n\tstatic string _FilterImage(int f) => \"*Material.FolderSearchOutline\" + (f == 0 ? EdIcons.green : f == 1 ? EdIcons.orange : EdIcons.red);\n\t\n\tvoid _SetFilter(int f) {\n\t\tif (f != _filter) {\n\t\t\t_filter = f;\n\t\t\t_bFilter.Content = ImageUtil.LoadWpfImageElement(_FilterImage(f));\n\t\t\t_bFilter.ToolTip = f switch { 0 => \"Search in entire workspace\", 1 => \"Search in current root folder\", _ => \"Search in current @Project\" };\n\t\t}\n\t}\n\t\n\t#endregion\n\t\n\t#region common\n\t\n\t/// <summary>\n\t/// Makes visible and sets find text = s (should be selected text of a control; can be null/\"\").\n\t/// </summary>\n\tpublic void CtrlF(string s/*, bool findInFiles = false*/) {\n\t\tPanels.PanelManager[P].Visible = true;\n\t\t_tFind.Focus();\n\t\tif (s.NE()) {\n\t\t\t_tFind.Call(Sci.SCI_SELECTALL); //often user wants to type new text\n\t\t\treturn;\n\t\t}\n\t\t_tFind.aaaText = s;\n\t}\n\t\n\t/// <summary>\n\t/// Makes visible and sets find text = selected text of e.\n\t/// Supports KScintilla and TextBox. If other type or null or no selected text, just makes visible etc.\n\t/// </summary>\n\tpublic void CtrlF(FrameworkElement e) {\n\t\tstring s = null;\n\t\tswitch (e) {\n\t\tcase KScintilla c:\n\t\t\ts = c.aaaSelectedText();\n\t\t\tbreak;\n\t\tcase TextBox c:\n\t\t\ts = c.SelectedText;\n\t\t\tbreak;\n\t\t}\n\t\tCtrlF(s);\n\t}\n\t\n\t/// <summary>\n\t/// Called when changed find text or options. Also when activated another document.\n\t/// Async-updates find-hiliting in editor.\n\t/// </summary>\n\tpublic void UpdateQuickResults() {\n\t\tif (!P.IsVisible) return;\n\t\t\n\t\t_timerUQR ??= new timer(_ => {\n\t\t\tPanels.Editor.ActiveDoc?.EInicatorsFound_(_FindAllInEditor());\n\t\t});\n\t\t\n\t\t_timerUQR.After(150);\n\t}\n\ttimer _timerUQR;\n\t\n\tinternal class TextToFind_ {\n\t\tpublic string findText;\n\t\tpublic string replaceText;\n\t\tpublic regexp rx;\n\t\tpublic bool wholeWord;\n\t\tpublic bool matchCase;\n\t\tpublic int filter;\n\t\t\n\t\tpublic bool IsSameFindTextAndOptions(TextToFind_ ttf)\n\t\t\t=> ttf.findText == findText\n\t\t\t&& ttf.matchCase == matchCase\n\t\t\t&& ttf.wholeWord == wholeWord\n\t\t\t&& (ttf.rx != null) == (rx != null);\n\t\t//ignore filter\n\t\t\n\t\tpublic int OptionsInt {\n\t\t\tget {\n\t\t\t\tint r = matchCase ? 1 : 0;\n\t\t\t\tif (wholeWord) r |= 2; else if (rx != null) r |= 4;\n\t\t\t\treturn r | (filter << 8);\n\t\t\t}\n\t\t}\n\t\t\n\t\treadonly static (int flag, char letter)[] s_map = [(1, 'c'), (2, 'w'), (4, 'r'), (1 << 8, 'F'), (2 << 8, 'P'), (0x10000, 'S')];\n\t\t\n\t\tpublic string OptionsString(int other) => StringUtil.FlagsToLetters(OptionsInt | (other << 16), s_map);\n\t\t\n\t\tpublic static int OptionsStringToInt(string s) => StringUtil.FlagsFromLetters(s, s_map);\n\t}\n\t\n\tbool _GetTextToFind(out TextToFind_ ttf, bool forReplace = false, bool noRecent = false, bool noErrorTooltip = false) {\n\t\t_ttRegex?.Close();\n\t\tstring text = _tFind.aaaText; if (text.NE()) { ttf = null; return false; }\n\t\tttf = new() { findText = text, matchCase = _cCase.IsChecked, filter = _filter };\n\t\ttry {\n\t\t\tif (_cRegex.IsChecked) {\n\t\t\t\tvar fl = RXFlags.MULTILINE;\n\t\t\t\tif (!ttf.matchCase) fl |= RXFlags.CASELESS;\n\t\t\t\tttf.rx = new regexp(ttf.findText, flags: fl);\n\t\t\t} else {\n\t\t\t\tttf.wholeWord = _cWord.IsChecked;\n\t\t\t}\n\t\t}\n\t\tcatch (ArgumentException e) { //regexp ctor throws if invalid\n\t\t\tif (!noErrorTooltip) TUtil.InfoTooltip(ref _ttRegex, _tFind, e.Message);\n\t\t\treturn false;\n\t\t}\n\t\tif (forReplace) ttf.replaceText = _tReplace.aaaText;\n\t\t\n\t\tif (!noRecent) _AddToRecent(ttf);\n\t\t\n\t\tif (forReplace && (Panels.Editor.ActiveDoc?.aaaIsReadonly ?? true)) return false;\n\t\treturn true;\n\t}\n\t\n\tstatic void _FindAllInString(string text, TextToFind_ ttf, Action<int, int> found) {\n\t\t_SkipImages si = new(text);\n\t\t\n\t\tif (ttf.rx != null) {\n\t\t\tforeach (var g in ttf.rx.FindAllG(text, 0)) {\n\t\t\t\tif (si.Skip(g.Start, g.End)) continue;\n\t\t\t\tfound(g.Start, g.End);\n\t\t\t}\n\t\t} else {\n\t\t\tfor (int i = 0; i < text.Length; i += ttf.findText.Length) {\n\t\t\t\ti = ttf.wholeWord ? text.FindWord(ttf.findText, i.., !ttf.matchCase, \"_\") : text.Find(ttf.findText, i, !ttf.matchCase);\n\t\t\t\tif (i < 0) break;\n\t\t\t\tint to = i + ttf.findText.Length;\n\t\t\t\tif (si.Skip(i, to)) continue;\n\t\t\t\tfound(i, to);\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Finds hidden images and determines whether a found text range is in an image.\n\t/// </summary>\n\tstruct _SkipImages {\n\t\tstring _text;\n\t\tint _imageStart, _imageEnd;\n\t\t\n\t\tpublic _SkipImages(string text) { _text = text; }\n\t\t\n\t\t/// <summary>\n\t\t/// Returns true if <i>start</i> or <i>end</i> is inside a hidden @\"image:Base64\" or /*image:Base64*/.\n\t\t/// </summary>\n\t\tpublic bool Skip(int start, int end) {\n\t\t\twhile (start >= _imageEnd) _FindImage();\n\t\t\tif (end > _imageStart) {\n\t\t\t\tif (end < _imageEnd || start > _imageStart) return true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\tvoid _FindImage() {\n\t\t\tfor (int i = _imageEnd + 2; i < _text.Length; i += 6) {\n\t\t\t\ti = _text.Find(\"image:\", i);\n\t\t\t\tif (i < 0) break;\n\t\t\t\tif (s_rx.Match(_text, 0, out RXGroup g, (i - 2)..)) {\n\t\t\t\t\tbool isString = _text[i - 1] == '\"';\n\t\t\t\t\t_imageStart = i + (isString ? 6 : -2);\n\t\t\t\t\t_imageEnd = g.End - (isString ? 1 : 0);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\t_imageStart = _imageEnd = int.MaxValue;\n\t\t}\n\t\t\n\t\tstatic regexp s_rx = new(@\"@\"\"image:[A-Za-z0-9/+]{40,}=*\"\"|/\\*image:[A-Za-z0-9/+]{40,}=*\\*/\", RXFlags.ANCHORED);\n\t}\n\t\n\t#endregion\n\t\n\t#region in editor\n\t\n\tpublic void FindNextInEditor(bool back) {\n\t\tif (back) {\n\t\t\t//this was added later. Since PCRE can't search backward, let's use _FindAllInEditor.\n\t\t\t_ttNext?.Close();\n\t\t\tvar doc = Panels.Editor.ActiveDoc; if (doc == null) return;\n\t\t\tvar a = _FindAllInEditor();\n\t\t\tint pos = doc.aaaSelectionStart8;\n\t\t\tfor (int i = a.Count; --i >= 0;) {\n\t\t\t\tif (a[i].End.Value <= pos) {\n\t\t\t\t\t_SelectFound(true, doc, a[i].Start.Value, a[i].End.Value);\n\t\t\t\t\t_rxReplPos = default;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tSystemSounds.Asterisk.Play();\n\t\t} else {\n\t\t\t_FindNextInEditor();\n\t\t}\n\t}\n\t\n\tvoid _FindNextInEditor(bool replace = false) {\n\t\tif (!_GetTextToFind(out var ttf, forReplace: replace)) return;\n\t\t_FindNextInEditor2(ttf, replace);\n\t}\n\t\n\tvoid _FindNextInEditor2(TextToFind_ ttf, bool replace) {\n\t\t_ttNext?.Close();\n\t\tvar doc = Panels.Editor.ActiveDoc; if (doc == null) return;\n\t\tvar text = doc.aaaText; if (text.Length == 0) return;\n\t\tint i, to, len = 0, from8 = replace ? doc.aaaSelectionStart8 : doc.aaaSelectionEnd8, from = doc.aaaPos16(from8), to8 = doc.aaaSelectionEnd8;\n\t\tRXMatch rm = null;\n\t\tbool retryFromStart = false, retryRx = false;\n\t\tg1:\n\t\tif (ttf.rx != null) {\n\t\t\t//this code solves this problem: now would not match if the regex contains \\K etc, because 'from' is different\n\t\t\tif (replace && _rxReplPos.doc == doc && _rxReplPos.from8 == from8 && _rxReplPos.to8 == to8 && _rxReplPos.text == text && ttf.IsSameFindTextAndOptions(_rxReplPos.ttf)) {\n\t\t\t\ti = from8; to = to8; rm = _rxReplPos.rm;\n\t\t\t\tgoto g2;\n\t\t\t}\n\t\t\t\n\t\t\tif (ttf.rx.Match(text, out rm, from..)) {\n\t\t\t\ti = rm.Start;\n\t\t\t\tlen = rm.Length;\n\t\t\t\tif (i == from && len == 0 && !(replace | retryRx | retryFromStart)) {\n\t\t\t\t\tif (++i > text.Length) i = -1;\n\t\t\t\t\telse {\n\t\t\t\t\t\tif (i < text.Length) if (text.Eq(i - 1, \"\\r\\n\") || char.IsSurrogatePair(text, i - 1)) i++;\n\t\t\t\t\t\tfrom = i; retryRx = true; goto g1;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (len == 0) doc.Focus();\n\t\t\t} else i = -1;\n\t\t} else {\n\t\t\ti = ttf.wholeWord ? text.FindWord(ttf.findText, from.., !ttf.matchCase, \"_\") : text.Find(ttf.findText, from, !ttf.matchCase);\n\t\t\tlen = ttf.findText.Length;\n\t\t}\n\t\tif (i < 0) {\n\t\t\tSystemSounds.Asterisk.Play();\n\t\t\t_rxReplPos = default;\n\t\t\tif (retryFromStart || from8 == 0) return;\n\t\t\tfrom = 0; retryFromStart = true; replace = false;\n\t\t\tgoto g1;\n\t\t}\n\t\tif (retryFromStart) TUtil.InfoTooltip(ref _ttNext, _tFind, \"Info: searching from start.\");\n\t\tto = doc.aaaPos8(i + len);\n\t\ti = doc.aaaPos8(i);\n\t\tg2:\n\t\tif (replace && i == from8 && to == to8) {\n\t\t\tvar repl = ttf.replaceText;\n\t\t\tif (rm != null) if (!_TryExpandRegexReplacement(rm, repl, out repl)) return;\n\t\t\t//doc.aaaReplaceRange(i, to, repl); //also would need to set caret pos = to\n\t\t\tdoc.aaaReplaceSel(repl);\n\t\t\t_FindNextInEditor2(ttf, false);\n\t\t} else if (_SelectFound(false, doc, i, to)) {\n\t\t\t_rxReplPos = (ttf, doc, text, i, to, rm);\n\t\t}\n\t}\n\t\n\tbool _SelectFound(bool back, SciCode doc, int start, int end) {\n\t\tif (CiStyling.IsProtected(doc, start, end)) {\n\t\t\tint skip = back ? CiStyling.SkipProtected(doc, start, true) : CiStyling.SkipProtected(doc, end, false);\n\t\t\tdoc.aaaGoToPos(false, skip);\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\tApp.Model.EditGoBack.RecordNext();\n\t\tdoc.aaaSelect(false, start, end, true);\n\t\treturn true;\n\t}\n\t\n\t(TextToFind_ ttf, SciCode doc, string text, int from8, int to8, RXMatch rm) _rxReplPos;\n\t\n\tvoid _bReplaceAll_Click(WBButtonClickArgs e) {\n\t\tif (!_GetTextToFind(out var ttf, forReplace: true)) return;\n\t\t_ReplaceAllInEditor(ttf);\n\t}\n\t\n\t//Can replace in inactive SciCode too.\n\tvoid _ReplaceAllInEditor(TextToFind_ ttf, SciCode doc = null, SciUndo undoInFiles = null) {\n\t\tdoc ??= Panels.Editor.ActiveDoc;\n\t\tif (doc.aaaIsReadonly) return;\n\t\tvar text = doc.aaaText;\n\t\tvar a = _FindReplacements(ttf, text);\n\t\tif (doc.EFile.ReplaceAllInText(text, a, out var text2))\n\t\t\tundoInFiles?.RifAddFile(doc, text, text2, a);\n\t}\n\t\n\tvoid _ReplaceAllInClosedFile(TextToFind_ ttf, FileNode f, SciUndo undoInFiles) {\n\t\tif (!f.GetCurrentText(out string text, silent: null)) return;\n\t\tvar a = _FindReplacements(ttf, text);\n\t\tif (f.ReplaceAllInText(text, a, out var text2))\n\t\t\tundoInFiles?.RifAddFile(f, text, text2, a);\n\t}\n\t\n\tList<StartEndText> _FindReplacements(TextToFind_ ttf, string text) {\n\t\tList<StartEndText> a = new();\n\t\tvar repl = ttf.replaceText;\n\t\tif (ttf.rx != null) {\n\t\t\tif (ttf.rx.FindAll(text, out var ma)) {\n\t\t\t\t_SkipImages si = new(text);\n\t\t\t\tforeach (var m in ma) {\n\t\t\t\t\tif (si.Skip(m.Start, m.End)) continue;\n\t\t\t\t\tif (!_TryExpandRegexReplacement(m, repl, out var r)) break;\n\t\t\t\t\ta.Add(new(m.Start, m.End, r));\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t_FindAllInString(text, ttf, (start, end) => a.Add(new(start, end, repl)));\n\t\t}\n\t\treturn a;\n\t}\n\t\n\tbool _TryExpandRegexReplacement(RXMatch m, string repl, out string result) {\n\t\ttry {\n\t\t\tresult = m.ExpandReplacement(repl);\n\t\t\treturn true;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tTUtil.InfoTooltip(ref _ttRegex, _tReplace, e.Message);\n\t\t\tresult = null;\n\t\t\treturn false;\n\t\t}\n\t}\n\t\n\tinternal bool ValidateReplacement_(TextToFind_ ttf/*, FileNode file*/) {\n\t\t//FUTURE: add regexp.IsValidReplacement and use it here.\n\t\t//if (ttf.rx != null\n\t\t//\t&& file.GetCurrentText(out var s, silent: true)\n\t\t//\t&& ttf.rx.Match(s, out RXMatch m)\n\t\t//\t&& !_TryExpandRegexReplacement(m, _tReplace.Text, out _)\n\t\t//\t) return false;\n\t\treturn true;\n\t}\n\t\n\tList<Range> _FindAllInEditor() {\n\t\tif (!_GetTextToFind(out var ttf, noRecent: true, noErrorTooltip: true)) return null;\n\t\tvar text = Panels.Editor.ActiveDoc?.aaaText; if (text.NE()) return null;\n\t\tList<Range> a = new(); //all found in editor text\n\t\t_FindAllInString(text, ttf, (start, end) => a.Add(start..end));\n\t\treturn a;\n\t}\n\t\n\t#endregion\n\t\n\t#region in files\n\t\n\tint _SearchIn => Math.Clamp(App.Settings.find_searchIn, 0, 2);\n\t\n\tWildcardList _skip;\n\treadonly string[] _aSkipImagesEtc = [\".png\", \".bmp\", \".jpg\", \".jpeg\", \".gif\", \".tif\", \".tiff\", \".ico\", \".cur\", \".ani\", \".snk\", \".dll\"];\n\t\n\tasync void _FindAllInFiles() {\n\t\tif (_cancelTS != null) {\n\t\t\t_cancelTS.Cancel();\n\t\t\treturn;\n\t\t}\n\t\t\n\t\t//using var p1 = perf.local();\n\t\tif (!_GetTextToFind(out var ttf)) return;\n\t\t\n\t\tApp.Model.Save.AllNowIfNeed(); //save text of current document\n\t\t\n\t\t_bInFiles.IsEnabled = false;\n\t\tvar cancelTimer = timer.after(1000, _ => { if (_cancelTS != null) { _bInFiles.Content = \"Stop\"; _bInFiles.IsEnabled = true; } });\n\t\ttry {\n\t\t\tusing var workingState = Panels.Found.Prepare(PanelFound.Found.Text, ttf.findText, out var b);\n\t\t\t\n\t\t\tconst int c_markerFile = 0, c_markerInfo = 1;\n\t\t\tif (workingState.NeedToInitControl) {\n\t\t\t\tvar k = workingState.Scintilla;\n\t\t\t\tk.aaaMarkerDefine(c_markerFile, Sci.SC_MARK_BACKGROUND, backColor: 0xC0E0A0);\n\t\t\t\tk.aaaMarkerDefine(c_markerInfo, Sci.SC_MARK_BACKGROUND, backColor: 0xEEE8AA);\n\t\t\t}\n\t\t\t\n\t\t\tFileNode folder = App.Model.Root;\n\t\t\tif (_filter > 0 && Panels.Editor.ActiveDoc?.EFile is FileNode fn) {\n\t\t\t\tif (_filter == 2 && fn.FindProject(out var proj, out _, ofAnyScript: true)) folder = proj;\n\t\t\t\telse folder = fn.AncestorsFromRoot(noRoot: true).FirstOrDefault() ?? folder;\n\t\t\t}\n\t\t\t\n\t\t\tList<(FileNode f, string s, int time, int len)> aSearchInFiles = new();\n\t\t\tint searchIn = _SearchIn;\n\t\t\tforeach (var v in folder.Descendants()) {\n\t\t\t\tif (v.IsFolder) continue;\n\t\t\t\tif (v.IsCodeFile) {\n\t\t\t\t\tif (searchIn == 2) continue; //0 all, 1 C#, 2 other\n\t\t\t\t} else {\n\t\t\t\t\tif (searchIn == 1) continue;\n\t\t\t\t\tif (v.Name.Ends(true, _aSkipImagesEtc) > 0) continue;\n\t\t\t\t}\n\t\t\t\tif ((_skip ??= new(App.Settings.find_skip)).IsMatch(v.ItemPath, true)) continue;\n\t\t\t\taSearchInFiles.Add((v, v.FilePath, 0, 0));\n\t\t\t}\n\t\t\t\n\t\t\tList<(FileNode f, Range[] ar, string text, int i)> aResults = new();\n\t\t\tlong timeStarted = perf.ms;\n\t\t\t\n\t\t\t//p1.Next();\n\t\t\t_cancelTS = new CancellationTokenSource();\n\t\t\tawait Task.Run(() => {\n\t\t\t\tvar ctoken = _cancelTS.Token;\n\t\t\t\tvar po = new ParallelOptions { CancellationToken = ctoken };\n\t\t\t\tif (App.Settings.find_parallel) {\n\t\t\t\t\tParallel.For(0, aSearchInFiles.Count, po, static () => new List<Range>(),\n\t\t\t\t\t\t(j, ps, ar) => {\n\t\t\t\t\t\t\tref var v = ref aSearchInFiles.Ref(j);\n\t\t\t\t\t\t\tlong t1 = perf.mcs;\n\t\t\t\t\t\t\tvar text = FileNode.GetFileTextLL(v.s);\n\t\t\t\t\t\t\t_File(v.f, text, ar, j);\n\t\t\t\t\t\t\tv.time += (int)((perf.mcs - t1) / 100);\n\t\t\t\t\t\t\tv.len = text.Lenn();\n\t\t\t\t\t\t\treturn ar;\n\t\t\t\t\t\t}, static _ => { });\n\t\t\t\t} else {\n\t\t\t\t\tfor (int from = 0, i = 0; i < aSearchInFiles.Count; from = i) {\n\t\t\t\t\t\tfor (int sumLength = 0; i < aSearchInFiles.Count && sumLength < 4_000_000; i++) { //load max 8 MB of text\n\t\t\t\t\t\t\tref var v = ref aSearchInFiles.Ref(i);\n\t\t\t\t\t\t\tlong t1 = perf.mcs;\n\t\t\t\t\t\t\tv.s = FileNode.GetFileTextLL(v.s);\n\t\t\t\t\t\t\tv.time = (int)((perf.mcs - t1) / 100);\n\t\t\t\t\t\t\tsumLength += v.len = v.s.Lenn();\n\t\t\t\t\t\t\tctoken.ThrowIfCancellationRequested();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tParallel.For(from, i, po, static () => new List<Range>(),\n\t\t\t\t\t\t\t(j, ps, ar) => {\n\t\t\t\t\t\t\t\tref var v = ref aSearchInFiles.Ref(j);\n\t\t\t\t\t\t\t\tlong t1 = perf.mcs;\n\t\t\t\t\t\t\t\t_File(v.f, v.s, ar, j);\n\t\t\t\t\t\t\t\tv.time += (int)((perf.mcs - t1) / 100);\n\t\t\t\t\t\t\t\tv.s = null; //GC\n\t\t\t\t\t\t\t\treturn ar;\n\t\t\t\t\t\t\t}, static _ => { });\n\t\t\t\t\t\t//need the Parallel in case of a very slow regex. Makes faster even if not regex.\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvoid _File(FileNode f, string text, List<Range> ar, int i) {\n\t\t\t\t\tif (text.NE() || text.Contains('\\0')) return;\n\t\t\t\t\t\n\t\t\t\t\tar.Clear();\n\t\t\t\t\t_FindAllInString(text, ttf, (start, end) => ar.Add(start..end));\n\t\t\t\t\t\n\t\t\t\t\tif (ar.Count > 0) {\n\t\t\t\t\t\tlock (aResults) {\n\t\t\t\t\t\t\taResults.Add((f, ar.ToArray(), text, i));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t\t//p1.Next();\n\t\t\t\n\t\t\tint nFound = aResults.Sum(o => o.ar.Length);\n\t\t\tbool limited = nFound > 100_000;\n\t\t\tforeach (var (f, ar, text, _) in aResults.OrderBy(o => o.i)) {\n\t\t\t\t//file\n\t\t\t\tvar path = f.ItemPath;\n\t\t\t\tb.Marker(c_markerFile)\n\t\t\t\t\t.Link2(new PanelFound.CodeLink(f, ar[0].Start.Value, isHeading: true))\n\t\t\t\t\t.Gray(path.AsSpan(0..^f.Name.Length))\n\t\t\t\t\t.B(f.Name);\n\t\t\t\tint ns = 120 - path.Length * 7 / 4; if (ns > 0) b.Text(new string(' ', ns));\n\t\t\t\tb.Link_();\n\t\t\t\tif (!limited) b.Text(\"    \").Link(new PanelFound.ReplaceInFileLink(f), \"Replace\");\n\t\t\t\tif (f.IsExternal) b.Green(\"    //external\");\n\t\t\t\tb.NL();\n\t\t\t\t//found text instances in that file\n\t\t\t\tif (limited) continue;\n\t\t\t\tfor (int i = 0; i < ar.Length; i++) {\n\t\t\t\t\tvar range = ar[i];\n\t\t\t\t\tPanelFound.AppendFoundLine(b, f, text, range.Start.Value, range.End.Value, workingState, displayFile: false);\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tb.Marker(c_markerInfo);\n\t\t\tif (aResults.Count > 0) {\n\t\t\t\tb.Text($\"Found {nFound} in {aResults.Count} files.    \");\n\t\t\t\tif (!limited) b.Link(\"RAIF\", \"Replace all...\");\n\t\t\t} else b.Text(\"Not found.\");\n\t\t\tb.NL();\n\t\t\t\n\t\t\tif (folder != App.Model.Root) b.Marker(c_markerInfo).Text($\"Searched only in folder '{folder.Name}'.\").NL();\n\t\t\tif (searchIn > 0) b.Marker(c_markerInfo).Link2(new Action(_Options), $\"Searched only in {(searchIn == 1 ? \"C#\" : \"non-C#\")} files.\").NL();\n\t\t\t\n\t\t\tif (App.Settings.find_printSlow > 0) {\n\t\t\t\tbool once = false;\n\t\t\t\tforeach (var v in aSearchInFiles) {\n\t\t\t\t\tint t = v.time / 10;\n\t\t\t\t\tif (t < App.Settings.find_printSlow) continue;\n\t\t\t\t\tif (!once) {\n\t\t\t\t\t\tonce = true;\n\t\t\t\t\t\tb.Marker(c_markerInfo)\n\t\t\t\t\t\t\t.Link2(new Action(_Options), $\"Searched in {aSearchInFiles.Count} files. Time: {perf.ms - timeStarted} ms. Slow files:\").NL();\n\t\t\t\t\t}\n\t\t\t\t\tb.Text($\"{t} ms \").Link(v.f, v.f.ItemPath).Text($\" , length {v.len}\\r\\n\");\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t//p1.Next();\n\t\t\tPanels.Found.SetFindInFilesResults(workingState, b, ttf, aResults.Select(o => o.f).ToList());\n\t\t}\n\t\tcatch (OperationCanceledException) { }\n\t\tfinally {\n\t\t\t_cancelTS?.Dispose();\n\t\t\t_cancelTS = null;\n\t\t\tcancelTimer.Stop();\n\t\t\t_bInFiles.Content = \"In files\";\n\t\t\t_bInFiles.IsEnabled = true;\n\t\t}\n\t}\n\tCancellationTokenSource _cancelTS;\n\t\n\tinternal void ReplaceAllInEditorFromFoundPanel_(TextToFind_ ttf) {\n\t\tif (!_CanReplaceFromFoundPanel(ttf)) return;\n\t\t_ReplaceAllInEditor(ttf);\n\t}\n\t\n\tbool _CanReplaceFromFoundPanel(TextToFind_ ttf) {\n\t\tbool ok = ttf.findText == _tFind.aaaText\n\t\t\t&& ttf.matchCase == _cCase.IsChecked\n\t\t\t&& ttf.wholeWord == _cWord.IsChecked\n\t\t\t&& (ttf.rx != null) == _cRegex.IsChecked\n\t\t\t&& ttf.filter == _filter;\n\t\tif (!ok) {\n\t\t\tdialog.show(null, \"Please click 'In files' to update the Found panel.\", owner: P);\n\t\t\treturn false;\n\t\t}\n\t\tttf.replaceText = _tReplace.aaaText;\n\t\t_AddToRecent(ttf, onlyRepl: true);\n\t\treturn true;\n\t}\n\t\n\tinternal void ReplaceAllInFilesFromFoundPanel_(TextToFind_ ttf, List<FileNode> files) {\n\t\tif (!ValidateReplacement_(ttf)) return; //avoid opening files in editor when invalid regex replacement\n\t\tif (!_CanReplaceFromFoundPanel(ttf)) return;\n\t\t\n\t\tbool haveExternal = files.Any(o => o.IsExternal);\n\t\t\n\t\tswitch (dialog.show(\"Replace text in files\", \"Replaces text in all files displayed in the Found panel.\",\n\t\t\thaveExternal ? \"1 Replace all|2 Replace all except external|0 Cancel\" : \"1 Replace all|0 Cancel\",\n\t\t\tflags: /*DFlags.CommandLinks |*/ DFlags.CenterMouse,\n\t\t\towner: App.Hmain)) {\n\t\tcase 1:\n\t\t\tif (haveExternal) { //remove duplicate files (two links pointing to the same file). Else possible confusion eg disabled Undo.\n\t\t\t\tHashSet<FileId> hs = new();\n\t\t\t\tfiles = files.Where(o => !o.IsExternal || !filesystem.more.getFileId(o.FilePath, out var u) || hs.Add(u)).ToList();\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 2:\n\t\t\tfiles = files.Where(o => !o.IsExternal).ToList();\n\t\t\tbreak;\n\t\tdefault: return;\n\t\t}\n\t\t\n\t\tvar progress = App.Hmain.TaskbarButton;\n\t\tvar undoInFiles = SciUndo.OfWorkspace;\n\t\ttry {\n\t\t\tundoInFiles.StartReplaceInFiles();\n\t\t\tprogress.SetProgressState(WTBProgressState.Normal);\n\t\t\t\n\t\t\tfor (int i = 0; i < files.Count; i++) {\n\t\t\t\tprogress.SetProgressValue(i, files.Count);\n\t\t\t\tvar f = files[i];\n\t\t\t\tif (f.OpenDoc != null) {\n\t\t\t\t\t_ReplaceAllInEditor(ttf, f.OpenDoc, undoInFiles);\n\t\t\t\t} else if (!f.DontSave) {\n\t\t\t\t\t_ReplaceAllInClosedFile(ttf, f, undoInFiles);\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t}\n\t\tfinally {\n\t\t\tundoInFiles.FinishReplaceInFiles($\"replace text '{ttf.findText.Limit(50)}' with '{ttf.replaceText.Limit(50)}'\");\n\t\t\tprogress.SetProgressState(WTBProgressState.NoProgress);\n\t\t\tCodeInfo.FilesChanged();\n\t\t}\n\t}\n\t\n\t#endregion\n\t\n\t#region recent, saved\n\t\n\tstring _recentPrevFind, _recentPrevReplace;\n\tint _recentPrevOptions;\n\t\n\t//temp is false when clicked a button, true when changed the find text or a checkbox.\n\tvoid _AddToRecent(TextToFind_ ttf, bool onlyRepl = false) {\n\t\tif (!onlyRepl) {\n\t\t\tint options = ttf.OptionsInt;\n\t\t\tif (ttf.findText != _recentPrevFind || options != _recentPrevOptions) _Add(false, _recentPrevFind = ttf.findText, _recentPrevOptions = options);\n\t\t}\n\t\tif (!ttf.replaceText.NE() && ttf.replaceText != _recentPrevReplace) _Add(true, _recentPrevReplace = ttf.replaceText, 0);\n\t\t\n\t\tstatic void _Add(bool replace, string text, int options) {\n\t\t\tif (text.Length > 3000) return;\n\t\t\tvar ri = new _RecentItem(text, options);\n\t\t\tvar a = _RecentLoad(replace);\n\t\t\tif (a.NE_()) a = new _RecentItem[] { ri };\n\t\t\telse if (a[0].text == text) a[0] = ri;\n\t\t\telse {\n\t\t\t\tfor (int i = a.Length; --i > 0;) if (a[i].text == text) a = a.RemoveAt(i); //no duplicates\n\t\t\t\tif (a.Length > 29) a = a[0..29]; //limit count\n\t\t\t\ta = a.InsertAt(0, ri);\n\t\t\t}\n\t\t\t_RecentSave(replace, a);\n\t\t}\n\t}\n\t\n\tvoid _RecentPopupList(KScintilla k) {\n\t\tbool replace = k == _tReplace;\n\t\tvar a = _RecentLoad(replace);\n\t\tif (a.NE_()) return;\n\t\tvar p = new KPopupListBox { PlacementTarget = k };\n\t\tvar c = p.Control;\n\t\tforeach (var v in a) c.Items.Add(v);\n\t\tp.OK += o => _RecentUse(o as _RecentItem, k);\n\t\tP.Dispatcher.InvokeAsync(() => p.IsOpen = true);\n\t}\n\t\n\tvoid _RecentUse(_RecentItem r, KScintilla k) {\n\t\tk.aaaText = r.text;\n\t\tif (k == _tFind) {\n\t\t\tint g = r.options;\n\t\t\t_cCase.IsChecked = 0 != (g & 1);\n\t\t\t_cWord.IsChecked = 0 != (g & 2);\n\t\t\t_cRegex.IsChecked = 0 != (g & 4);\n\t\t\t_SetFilter(g >> 8 & 3);\n\t\t}\n\t}\n\t\n\tstatic _RecentItem[] _RecentLoad(bool replace) {\n\t\tvar file = _RecentFile(replace);\n\t\tvar x = filesystem.exists(file, true).File ? csvTable.load(file) : null;\n\t\tif (x == null) return null;\n\t\tvar a = new _RecentItem[x.RowCount];\n\t\tfor (int i = 0; i < a.Length; i++) {\n\t\t\ta[i] = new(x[i][1], x[i][0].ToInt());\n\t\t}\n\t\treturn a;\n\t}\n\t\n\tstatic void _RecentSave(bool replace, _RecentItem[] a) {\n\t\tvar x = new csvTable { ColumnCount = 2, RowCount = a.Length };\n\t\tfor (int i = 0; i < a.Length; i++) {\n\t\t\tx[i][0] = a[i].options.ToS();\n\t\t\tx[i][1] = a[i].text;\n\t\t}\n\t\tx.Save(_RecentFile(replace));\n\t}\n\t\n\tstatic string _RecentFile(bool replace) => AppSettings.DirBS + (replace ? \"Recent replace.csv\" : \"Recent find.csv\");\n\t\n\trecord _RecentItem(string text, int options) {\n\t\tpublic override string ToString() => text.Limit(200); //ListBox item display text\n\t}\n\t\n\trecord _SavedItem(string name, string text, int options, string repl) : _RecentItem(text, options) {\n\t\tpublic override string ToString() => name; //ListBox item display text\n\t}\n\t\n\tvoid _SavedSearches(WBButtonClickArgs ba) {\n\t\tstring file = AppSettings.DirBS + \"Saved searches.csv\";\n\t\t\n\t\tPanels.Editor.SyncEditorTextIfFileIs(file, true);\n\t\t\n\t\tvar p = new KPopupListBox { PlacementTarget = ba.Button };\n\t\tvar c = p.Control;\n\t\t\n\t\tc.Items.Add(\"Save this search...\");\n\t\tc.Items.Add(\"Edit saved searches\");\n\t\tc.Items.Add(new Separator());\n\t\t\n\t\tif (filesystem.exists(file, true).File) {\n\t\t\tvar x = csvTable.load(file); //note: no try/catch. If bad format, shows an exception mesage box with error description.\n\t\t\tif (x.ColumnCount < 4) x.ColumnCount = 4;\n\t\t\tforeach (var r in x.Rows) if (r[0].NE()) r[0] = r[2]?.Limit(200);\n\t\t\tforeach (var r in x.Rows.OrderBy(o => o[0], StringComparer.OrdinalIgnoreCase)) {\n\t\t\t\tif (!r[1].NE()) c.Items.Add(new _SavedItem(r[0], r[2], TextToFind_.OptionsStringToInt(r[1]), r[3]));\n\t\t\t}\n\t\t}\n\t\t\n\t\t(c.ItemContainerStyle = new(typeof(ListBoxItem))).Setters.Add(new Setter(ListBoxItem.ToolTipProperty, new Binding(\"text\")));\n\t\t\n\t\tp.OK += o => {\n\t\t\tif (o is _SavedItem g) {\n\t\t\t\tif (g.repl is string repl) { _tReplace.Focus(); _tReplace.aaaText = repl; }\n\t\t\t\t_tFind.Focus();\n\t\t\t\t_RecentUse(g, _tFind);\n\t\t\t\tif (0 != (g.options & 0x10000)) _FindAllInFiles();\n\t\t\t} else if (o == c.Items[0]) { //Save this\n\t\t\t\tif (!_GetTextToFind(out var ttf, noRecent: true)) return; //info: returns false if text empty or regex invalid (shows tooltip)\n\t\t\t\tstring replace = _tReplace.aaaText.NullIfEmpty_();\n\t\t\t\t\n\t\t\t\tvar b = new wpfBuilder(\"Save this search\").WinSize(400);\n\t\t\t\tb.R.Add(\"Name (optional)\", out TextBox tName).Focus();\n\t\t\t\tb.R.Add(out KCheckBox cAndReplace, \"Include the Replace field\").Disabled(replace.NE());\n\t\t\t\tb.R.Add(out KCheckBox cInFiles, \"Search in files when selected\");\n\t\t\t\tb.R.AddOkCancel();\n\t\t\t\tb.Window.ShowInTaskbar = false;\n\t\t\t\tif (!b.ShowDialog(App.Wmain)) return;\n\t\t\t\t\n\t\t\t\tvar x = filesystem.exists(file, true).File ? csvTable.load(file) : new();\n\t\t\t\tif (x.ColumnCount < 4) x.ColumnCount = 4;\n\t\t\t\t//if (x.RowCount == 0) { x.AddRow(); x.AddRow(\"CSV: name, options, findText, replaceText\"); } //no, just adds noise\n\t\t\t\tstring name = tName.Text.NullIfEmpty_(), text = ttf.findText, options = ttf.OptionsString(cInFiles.IsChecked ? 1 : 0);\n\t\t\t\tif (!cAndReplace.IsChecked) replace = null;\n\t\t\t\tstring[] dup = null;\n\t\t\t\tvar dupName = name is null ? null : x.Rows.FirstOrDefault(o => o[0]?.Eqi(name) == true);\n\t\t\t\tvar dupText = x.Rows.FirstOrDefault(o => o[2]?.Eqi(text) == true);\n\t\t\t\tif (dupName != null || dupText != null) {\n\t\t\t\t\tswitch (dialog.show(\"Replace existing?\", $\"A search with this {(dupName is null ? \"text\" : \"name\")} is already saved.\", \"1 Replace|2 Add new|0 Cancel\", owner: App.Hmain)) {\n\t\t\t\t\tcase 1: dup = dupName ?? dupText; break;\n\t\t\t\t\tcase 2: break;\n\t\t\t\t\tdefault: return;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (dup != null) (dup[0], dup[1], dup[2], dup[3]) = (name, options, text, replace);\n\t\t\t\telse x.InsertRow(0, name, options, text, replace);\n\t\t\t\tx.Save(file);\n\t\t\t\tPanels.Editor.SyncEditorTextIfFileIs(file, false);\n\t\t\t} else if (o == c.Items[1]) { //Edit saved searches\n\t\t\t\tApp.Model.ImportLinkOrOpen(file);\n\t\t\t}\n\t\t};\n\t\t\n\t\tp.IsOpen = true;\n\t}\n\t\n\t#endregion\n}\n"
  },
  {
    "path": "Au.Editor/Panels/PanelFound.cs",
    "content": "using System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Input;\nusing Au.Controls;\nusing static Au.Controls.Sci;\n\nnamespace LA;\n\nclass PanelFound {\n\t_LbItem _li;\n\t_KScintilla _sci;\n\tGrid _grid;\n\tListBox _lb;\n\t\n\tpublic PanelFound() {\n\t\tP.UiaSetName(\"Found panel\");\n\t\t\n\t\tvar b = new wpfBuilder(P).Brush(SystemColors.ControlBrush);\n\t\tb.Options(modifyPadding: false, margin: new());\n\t\t\n\t\tvar tb = b.xAddToolBar(hideOverflow: true, controlBrush: true);\n\t\ttb.UiaSetName(\"Found_toolbar\");\n\t\t\n\t\tvar cKeep = tb.AddCheckbox(\"*RemixIcon.Lock2Line\" + EdIcons.black, \"Keep results\", enabled: false);\n\t\tcKeep.CheckChanged += (_, _) => { if (_sci != null) _sci.isLocked = cKeep.IsChecked; };\n\t\t\n\t\tvar bCloseOF = tb.AddButton(\"*Codicons.CloseAll\" + EdIcons.black, _ => _sci?.CloseOpenedFiles(), \"Close opened files\", enabled: false);\n\t\t\n\t\tb.Add<Border>().Border(thickness2: new(1, 0, 0, 0)).SpanRows(2);\n\t\tb.Child().Add(out _grid);\n\t\t\n\t\tb.Row(-1).Add(out _lb).Span(1).Border(thickness2: new(0, 1, 0, 0)).UiaName(\"Found_pages\");\n\t\t_lb.SelectionChanged += (_, _) => {\n\t\t\tif (_sci != null) _sci.Visibility = Visibility.Hidden;\n\t\t\t_li = _lb.SelectedItem as _LbItem;\n\t\t\t_sci = _li?.sci;\n\t\t\tif (_sci != null) {\n\t\t\t\t_sci.Visibility = Visibility.Visible;\n\t\t\t\tcKeep.IsChecked = _sci.isLocked;\n\t\t\t} else {\n\t\t\t\tcKeep.IsChecked = false;\n\t\t\t}\n\t\t\tcKeep.IsEnabled = _sci != null && _sci.kind != Found.SymbolRename;\n\t\t\tbCloseOF.IsEnabled = _sci != null;\n\t\t};\n\t\t\n\t\tb.End();\n\t\t\n\t\tFilesModel.UnloadingAnyWorkspace += _CloseAll;\n\t\t\n\t\tPanels.PanelManager[\"Found\"].DontActivateFloating = e => true;\n\t}\n\t\n\tpublic UserControl P { get; } = new();\n\t\n\tpublic WorkingState Prepare(Found kind, string text, out SciTextBuilder builder) {\n\t\tPanels.PanelManager[P].Visible = true;\n\t\t\n\t\tif (_sci == null || _sci.kind != kind || _sci.isLocked) {\n\t\t\tvar li = _lb.Items.Cast<_LbItem>().FirstOrDefault(o => o.sci.kind == kind && !o.sci.isLocked);\n\t\t\tif (li == null) {\n\t\t\t\tvar c = new _KScintilla(kind);\n\t\t\t\t_grid.Children.Add(c);\n\t\t\t\tli = new _LbItem(c, kind switch {\n\t\t\t\t\tFound.Files => \"*FeatherIcons.File\" + EdIcons.black,\n\t\t\t\t\t//Found.Text => \"*Material.Text\" + EdIcons.black,\n\t\t\t\t\tFound.Text => \"*Material.FindReplace\" + EdIcons.black,\n\t\t\t\t\tFound.SymbolReferences => EdIcons.References,\n\t\t\t\t\tFound.SymbolRename => \"*PicolIcons.Edit\" + EdIcons.red,\n\t\t\t\t\tFound.Repair => \"*RPGAwesome.Repair\" + EdIcons.black,\n\t\t\t\t\t_ => null\n\t\t\t\t}, null);\n\t\t\t\tli.ContextMenuOpening += (_, _) => {\n\t\t\t\t\tvar m = new popupMenu();\n\t\t\t\t\tm[\"Close\\tM-click\"] = o => _Close(li);\n\t\t\t\t\tm.Show();\n\t\t\t\t};\n\t\t\t\tli.MouseDown += (_, e) => { if (e.ChangedButton == MouseButton.Middle) _Close(li); };\n\t\t\t\t_lb.Items.Add(li);\n\t\t\t}\n\t\t\t_lb.SelectedItem = li;\n\t\t}\n\t\t\n\t\t_Clear();\n\t\t_li.SetText(text.Limit(15).RxReplace(@\"\\R\", \" \"), text);\n\t\t\n\t\tbuilder = new SciTextBuilder() {\n\t\t\tBoldStyle = Styles.Bold,\n\t\t\tGrayStyle = Styles.Gray,\n\t\t\tGreenStyle = Styles.Green,\n\t\t\tLinkIndic = Indicators.Link,\n\t\t\tLink2Indic = Indicators.Link2,\n\t\t\tuser = (0, _sci)\n\t\t};\n\t\t\n\t\tif (kind is Found.Files) return new(_sci, disable: false);\n\t\treturn new(_sci, disable: true);\n\t}\n\t\n\tpublic void ClearResults(Found kind) {\n\t\tif (_sci == null || _sci.kind != kind || _sci.isLocked) return;\n\t\t_Clear();\n\t\t_li.SetText(null, null);\n\t}\n\t\n\tvoid _Clear() {\n\t\t_sci.Clear();\n\t\t_docLinks = default;\n\t}\n\t\n\tbool _IsSciOk(in WorkingState ws) {\n\t\treturn _sci == ws.Scintilla;\n\t}\n\t\n\tpublic void SetResults(in WorkingState ws, SciTextBuilder b) {\n\t\tif (!_IsSciOk(ws)) return;\n\t\tb.Apply(_sci);\n\t}\n\t\n\tpublic void SetFindInFilesResults(in WorkingState ws, SciTextBuilder b, PanelFind.TextToFind_ ttf, List<FileNode> files) {\n\t\tif (!_IsSciOk(ws)) return;\n\t\tb.Apply(_sci);\n\t\t_sci.ttf = ttf;\n\t\t_sci.files = files;\n\t}\n\t\n\t/// <summary>\n\t/// Appends limited text of line of text range <i>start..end</i>, as a link that opens file <i>f</i> and select text <i>start..end</i>, with highlighted range <i>start..end</i>.\n\t/// </summary>\n\t/// <param name=\"text\">Text of file <i>f</i>.</param>\n\tpublic static void AppendFoundLine(SciTextBuilder b, FileNode f, string text, int start, int end, WorkingState workingState, bool displayFile, int indicHilite = Indicators.HiliteY) {\n\t\tif (b.user.i == 0) {\n\t\t\tvar k = b.user.o as KScintilla;\n\t\t\tb.user.i = Math.Max((int)k.ActualWidth - k.aaaMarginGetX(4, dpiUnscale: true).right - 20, 1); //logical pixels\n\t\t}\n\t\tvar fileName = displayFile ? f.Name.Limit(30) : null;\n\t\tint wid = Math.Clamp(b.user.i / 10 - (displayFile ? fileName.Length + 4 : 0), 20, 100); //chars\n\t\tint lineStart = start, lineEnd = end;\n\t\tint lsMax = Math.Max(start - wid, 0), leMax = Math.Min(end + 200, text.Length); //start/end limits like in VS\n\t\twhile (lineStart > lsMax && !text.IsCsNewlineChar(lineStart - 1)) lineStart--;\n\t\tbool limitStart = lineStart > 0 && !text.IsCsNewlineChar(lineStart - 1);\n\t\twhile (lineStart < start && text[lineStart] is ' ' or '\\t') lineStart++;\n\t\twhile (lineEnd < leMax && !text.IsCsNewlineChar(lineEnd)) lineEnd++;\n\t\tbool limitEnd = lineEnd < text.Length && !text.IsCsNewlineChar(lineEnd);\n\t\t\n\t\tb.Link2(new CodeLink(f, start));\n\t\tif (displayFile) b.Gray(fileName).Text(\"        \");\n\t\tif (limitStart) b.Text(\"…\");\n\t\tb.Text(text.AsSpan(lineStart..start)).Indic(indicHilite, text.AsSpan(start..end)).Text(text.AsSpan(end..lineEnd));\n\t\tif (limitEnd) b.Text(\"…\");\n\t\tb.Link_().NL();\n\t\t\n\t\t//CONSIDER: comments green.\n\t\t//\tNow users don't see '//' if replaced with '...'.\n\t\t//\tBut for me it never was a problem.\n\t}\n\t\n\tinternal void SciModified(SciCode doc, in Sci.SCNotification n) {\n\t\tvar f = doc.EFile;\n\t\t\n\t\tif (_docLinks.doc != doc) { //cache links to this document, to avoid enumerating all links to all documents on each modification (slow)\n\t\t\t_docLinks.doc = doc;\n\t\t\t_docLinks.links = null;\n\t\t\tforeach (_LbItem li in _lb.Items) {\n\t\t\t\tif (li.sci.kind is Found.Files or Found.Repair) continue;\n\t\t\t\tforeach (var v in li.sci.AaRangeDataEnum<CodeLink>()) {\n\t\t\t\t\tif (v.file == f) (_docLinks.links ??= new()).Add(v);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (_docLinks.links == null) return;\n\t\t\n\t\tint pos = n.position, len = n.length;\n\t\tif (n.modificationType.Has(MOD.SC_MOD_DELETETEXT)) len = -len;\n\t\tforeach (var v in _docLinks.links) {\n\t\t\tv.TextChanged_(pos, len);\n\t\t}\n\t}\n\t\n\t(SciCode doc, List<CodeLink> links) _docLinks;\n\t\n\tpublic void Close(KScintilla sci) {\n\t\tvar li = _lb.Items.OfType<_LbItem>().FirstOrDefault(o => o.sci == sci);\n\t\tif (li != null) _Close(li);\n\t}\n\t\n\tvoid _Close(_LbItem li) {\n\t\tif (li == _li && _lb.Items.Count is int n && n > 1) {\n\t\t\tint i = _lb.Items.IndexOf(li);\n\t\t\t_lb.SelectedIndex = i == n - 1 ? i - 1 : i + 1;\n\t\t}\n\t\t_lb.Items.Remove(li);\n\t\t_CloseSci(li.sci);\n\t}\n\t\n\tvoid _CloseSci(_KScintilla sci) {\n\t\t_grid.Children.Remove(sci);\n\t\tsci.Dispose();\n\t\tif (sci.IsFocused) Keyboard.Focus(_lb);\n\t}\n\t\n\tvoid _CloseAll() {\n\t\tvar a = _lb.Items.OfType<_LbItem>().ToArray();\n\t\t_lb.Items.Clear();\n\t\tforeach (var v in a) _CloseSci(v.sci);\n\t}\n\t\n\tpublic void NextFound(bool previous) {\n\t\t_sci?.NextFound(previous);\n\t}\n\t\n\tclass _KScintilla : KScintilla {\n\t\tpublic readonly Found kind;\n\t\tpublic bool isLocked, once;\n\t\tpublic PanelFind.TextToFind_ ttf;\n\t\tpublic List<FileNode> files;\n\t\tHashSet<FileNode> _openedFiles;\n\t\t\n\t\tpublic _KScintilla(Found kind) {\n\t\t\tthis.kind = kind;\n\t\t\tName = \"Found_\" + kind;\n\t\t\tAaInitReadOnlyAlways = true;\n\t\t\tAaNoMouseSetFocus = MButtons.Right | MButtons.Middle;\n\t\t\tAaInitTagsStyle = AaTagsStyle.AutoAlways;\n\t\t}\n\t\t\n\t\tprotected override void AaOnHandleCreated() {\n\t\t\taaaMarginSetWidth(1, 0);\n\t\t\taaaStyleFont(STYLE_DEFAULT, App.Wmain);\n\t\t\taaaStyleClearAll();\n\t\t\tCall(SCI_SETCARETSTYLE, CARETSTYLE_INVISIBLE);\n\t\t\tCall(SCI_SETEXTRAASCENT, 1);\n\t\t\tCall(SCI_SETEXTRADESCENT, 1);\n\t\t\t\n\t\t\t//indicators\n\t\t\taaaIndicatorDefine(Indicators.HiliteY, INDIC_STRAIGHTBOX, 0xffff00, alpha: 255, borderAlpha: 255, underText: true);\n\t\t\taaaIndicatorDefine(Indicators.HiliteG, INDIC_STRAIGHTBOX, 0xC0FF60, alpha: 255, borderAlpha: 255, underText: true);\n\t\t\taaaIndicatorDefine(Indicators.HiliteB, INDIC_GRADIENT, 0xA0A0FF, alpha: 255, underText: true);\n\t\t\taaaIndicatorDefine(Indicators.FocusRect, INDIC_FULLBOX, 0x4169E1, alpha: 25, borderAlpha: 255, strokeWidth: _dpi / 96); //better than SC_ELEMENT_CARET_LINE_BACK/SCI_SETCARETLINEFRAME etc\n\t\t\taaaIndicatorDefine(Indicators.Excluded, INDIC_STRIKE, 0xFF0000);\n\t\t\t\n\t\t\t//link indicators\n\t\t\taaaIndicatorDefine(-Indicators.Link, INDIC_COMPOSITIONTHIN, 0x0080ff, hoverColor: 0x8000ff);\n\t\t\taaaIndicatorDefine(-Indicators.Link + 1, INDIC_TEXTFORE, 0x0080ff, hoverColor: 0x8000ff);\n\t\t\taaaIndicatorDefine(Indicators.Link2, INDIC_HIDDEN, hoverColor: 1);\n\t\t\t\n\t\t\t//styles\n\t\t\taaaStyleBold(Styles.Bold, true);\n\t\t\taaaStyleForeColor(Styles.Gray, 0x808080);\n\t\t\taaaStyleForeColor(Styles.Green, 0x008000);\n\t\t\t\n\t\t\tif (kind == Found.SymbolReferences) aaaFoldingInit(0, autoFold: true);\n\t\t\t\n\t\t\tbase.AaOnHandleCreated();\n\t\t}\n\t\t\n\t\tprotected override nint WndProc(wnd w, int msg, nint wp, nint lp) {\n\t\t\tswitch (msg) {\n\t\t\tcase Api.WM_MBUTTONDOWN: //close file\n\t\t\t\tint pos = Call(SCI_POSITIONFROMPOINTCLOSE, Math2.LoShort(lp), Math2.HiShort(lp));\n\t\t\t\tif (pos >= 0 && AaRangeDataGet(false, pos, out object o)) {\n\t\t\t\t\tvar f = o switch { FileNode f1 => f1, CodeLink cl => cl.file, _ => null };\n\t\t\t\t\tif (f != null && (_openedFiles?.Contains(f) ?? false)) //close only if opened from this panel. Else may accidentally close (eg confuse middle/right button) and lose the undo history etc.\n\t\t\t\t\t\tApp.Model.CloseFile(f, selectOther: true);\n\t\t\t\t}\n\t\t\t\treturn 0;\n\t\t\tcase Api.WM_CONTEXTMENU:\n\t\t\t\tif (kind == Found.SymbolRename) {\n\t\t\t\t\tint i = aaaCurrentPos8;\n\t\t\t\t\tif (0 != aaaIndicatorGetValue(Indicators.Link2, i)) {\n\t\t\t\t\t\tvar v = aaaLineStartEndFromPos(false, i);\n\t\t\t\t\t\tif (0 == aaaIndicatorGetValue(Indicators.Excluded, i)) aaaIndicatorAdd(Indicators.Excluded, false, v.start..v.end);\n\t\t\t\t\t\telse aaaIndicatorClear(Indicators.Excluded, false, v.start..v.end);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\t\n\t\t\treturn base.WndProc(w, msg, wp, lp);\n\t\t}\n\t\t\n\t\tprotected override void AaOnSciNotify(ref SCNotification n) {\n\t\t\t//if (n.code == NOTIF.SCN_MODIFIED) print.it(n.code, n.modificationType, n.position, n.length); else if (n.code is not (NOTIF.SCN_PAINTED or NOTIF.SCN_STYLENEEDED)) print.it(n.code);\n\t\t\tswitch (n.code) {\n\t\t\tcase NOTIF.SCN_INDICATORRELEASE:\n\t\t\t\tif (n.modifiers == 0) _OnClickIndicLink(n.position);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbase.AaOnSciNotify(ref n);\n\t\t}\n\t\t\n\t\tpublic void Clear() {\n\t\t\taaaClearText();\n\t\t\tttf = null;\n\t\t\tfiles = null;\n\t\t\t_openedFiles = null;\n\t\t}\n\t\t\n\t\tpublic void CloseOpenedFiles() {\n\t\t\tif (_openedFiles != null) {\n\t\t\t\tApp.Model.CloseFiles(_openedFiles);\n\t\t\t\tApp.Model.CollapseAll(exceptWithOpenFiles: true);\n\t\t\t\t_openedFiles = null;\n\t\t\t}\n\t\t}\n\t\t\n\t\tvoid _OnClickIndicLink(int pos8) {\n\t\t\tif (AaRangeDataGet(false, pos8, out object o)) {\n\t\t\t\tswitch (o) {\n\t\t\t\tcase FileNode f:\n\t\t\t\t\t_OpenLinkClicked(pos8, f);\n\t\t\t\t\tbreak;\n\t\t\t\tcase CodeLink k:\n\t\t\t\t\tif (_OpenLinkClicked(pos8, k.file)) {\n\t\t\t\t\t\ttimer.after(10, _ => {\n\t\t\t\t\t\t\tvar doc = Panels.Editor.ActiveDoc;\n\t\t\t\t\t\t\tif (doc?.EFile != k.file || k.start >= doc.aaaLen16) return;\n\t\t\t\t\t\t\tApp.Model.EditGoBack.RecordNext();\n\t\t\t\t\t\t\tdoc.aaaGoToPos(true, k.start);\n\t\t\t\t\t\t\tdoc.Focus();\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t//rejected: briefly show a marker, or hilite the line, or change caret color/width.\n\t\t\t\t\t\t\t//\tMore distracting than useful.\n\t\t\t\t\t\t\t//\tThe default blinking caret + default highlighting are easy to notice.\n\t\t\t\t\t\t});\n\t\t\t\t\t\t//info: scrolling works better with async when now opened the file\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase ReplaceInFileLink k:\n\t\t\t\t\tif (_OpenLinkClicked(pos8, k.file, replaceAll: true)) {\n\t\t\t\t\t\ttimer.after(10, _ => {\n\t\t\t\t\t\t\tif (Panels.Editor.ActiveDoc?.EFile != k.file) return;\n\t\t\t\t\t\t\tPanels.Find.ReplaceAllInEditorFromFoundPanel_(ttf);\n\t\t\t\t\t\t});\n\t\t\t\t\t\t//info: without timer sometimes does not set cursor pos correctly\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase string s when s == \"RAIF\": //Replace all in all files\n\t\t\t\t\tPanels.Find.ReplaceAllInFilesFromFoundPanel_(ttf, files);\n\t\t\t\t\tbreak;\n\t\t\t\tcase Action k:\n\t\t\t\t\tk.Invoke();\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tDebug_.Print(o);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t} else Debug_.Print(pos8);\n\t\t}\n\t\t\n\t\tbool _OpenLinkClicked(int pos8, FileNode f, bool replaceAll = false) {\n\t\t\tif (App.Model.IsAlien(f)) return false;\n\t\t\tif (f.IsFolder) f.SelectSingle();\n\t\t\telse {\n\t\t\t\t//avoid opening the file in editor when invalid regex replacement\n\t\t\t\tif (replaceAll && !Panels.Find.ValidateReplacement_(ttf)) return false;\n\t\t\t\t\n\t\t\t\tif (!App.Model.OpenFiles.Contains(f)) (_openedFiles ??= new()).Add(f);\n\t\t\t\tif (!App.Model.SetCurrentFile(f)) return false;\n\t\t\t}\n\t\t\t//add indicator to help the user to find this line later\n\t\t\taaaIndicatorClear(Indicators.FocusRect);\n\t\t\tvar v = aaaLineStartEndFromPos(false, pos8);\n\t\t\taaaIndicatorAdd(Indicators.FocusRect, false, v.start..v.end);\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\tpublic void NextFound(bool previous) {\n\t\t\tfor (int line = aaaIndicatorFindFirst(Indicators.FocusRect, out var r1) ? aaaLineFromPos(false, r1.start) : -1; ;) {\n\t\t\t\tline += (previous ? -1 : 1);\n\t\t\t\tif ((uint)line >= aaaLineCount) break;\n\t\t\t\tint pos = aaaLineStart(false, line);\n\t\t\t\tif (!AaRangeDataGet(false, pos, out CodeLink k) || k.isHeading) continue;\n\t\t\t\t_OnClickIndicLink(pos);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\t\n\tclass _LbItem : KListBoxItemWithImage {\n\t\tpublic readonly _KScintilla sci;\n\t\tpublic _LbItem(_KScintilla sci, object image, string text) : base(image, text, 1) { this.sci = sci; }\n\t}\n\t\n\tpublic enum Found {\n\t\tFiles,\n\t\tText,\n\t\tSymbolReferences,\n\t\tSymbolRename,\n\t\tRepair\n\t}\n\t\n\t//CONSIDER: instead of taskbar button progress:\n\t//\tWhile searching hide scintilla (or don't create until finished) and in its place show a WPF progressbar.\n\t//\tBut then need to dispatch messages, eg async-await.\n\tpublic struct WorkingState : IDisposable {\n\t\tpublic KScintilla Scintilla { get; private set; }\n\t\t\n\t\tpublic WorkingState(KScintilla sci, bool disable) {\n\t\t\tScintilla = sci;\n\t\t\tif (disable) Panels.Found.P.IsEnabled = false;\n\t\t}\n\t\t\n\t\tpublic void Dispose() {\n\t\t\tPanels.Found.P.IsEnabled = true;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Returns true when called first time for current Scintilla control; let the caller set Scintilla styles, markers, etc. Later returns false.\n\t\t/// </summary>\n\t\tpublic bool NeedToInitControl {\n\t\t\tget {\n\t\t\t\tvar k = Scintilla as _KScintilla;\n\t\t\t\tif (k.once) return false;\n\t\t\t\tk.once = true;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\t\n\tpublic class CodeLink {\n\t\tFileNode _file;\n\t\tint _start;\n\t\tint _deletedAt;\n\t\t\n\t\tpublic FileNode file => _file;\n\t\tpublic int start => _start;\n\t\tpublic bool isHeading { get; private set; }\n\t\t\n\t\tpublic CodeLink(FileNode file, int start, bool isHeading = false) {\n\t\t\t_file = file;\n\t\t\t_start = start;\n\t\t\tthis.isHeading = isHeading;\n\t\t\t_deletedAt = -1;\n\t\t}\n\t\t\n\t\t//len < 0 when deleting\n\t\tinternal void TextChanged_(int pos, int len) {\n\t\t\tif (pos == _start) {\n\t\t\t\tif (len < 0) { _deletedAt = pos; return; } //detect when replacing the link target text. Eg replacing found substrings, or renaming found symbol references.\n\t\t\t\tif (pos != _deletedAt) _start += len;\n\t\t\t} else {\n\t\t\t\tif (pos < _start) _start += len;\n\t\t\t\t//never mind: will not work well in all cases. Eg when the deleted range includes _start.\n\t\t\t}\n\t\t\t_deletedAt = -1;\n\t\t}\n\t}\n\t\n\tpublic record class ReplaceInFileLink(FileNode file);\n\t\n\t/// <summary>\n\t/// Indices of indicators defined by this control.\n\t/// </summary>\n\tpublic static class Indicators {\n\t\tpublic const int HiliteY = 8, HiliteG = 9, HiliteB = 10, FocusRect = 11, Excluded = 12, Link = -16, Link2 = 18;\n\t}\n\t\n\t/// <summary>\n\t/// Indices of styles defined by this control.\n\t/// </summary>\n\tpublic static class Styles {\n\t\tpublic const int Bold = 30, Gray = 29, Green = 28; //31 STYLE_HIDDEN\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Panels/PanelHelp.cs",
    "content": "using System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Input;\nusing System.Xml.Linq;\nusing Au.Controls;\nusing System.Security.Authentication;\nusing System.Text.Json.Nodes;\nusing System.Net;\nusing System.Windows.Media;\nusing ToolLand;\n\n//CONSIDER: Add a menu-button. Menu:\n//\tItem \"Request a recipe for this search query (uses internet)\".\n\nnamespace LA;\n\nclass PanelHelp {\n\tKTreeView _tv;\n\tKTextBox _search;\n\t_Item _root;\n\t_Item _selectedItem;\n\tList<_Item> _aiResults;\n\tbool _showingResults;\n\tbool _openingItem;\n\tinternal (Button aiSearch, Button copyResults, ToolBarTray toolbar, Button back, Button forward, Button openInBrowser, Button toggleReadPanel) buttons_;\n\t\n\tpublic PanelHelp() {\n\t\tP = new();\n\t\tP.UiaSetName(\"Help panel\");\n\t\t\n\t\tvar b = new wpfBuilder(P).Columns(-1, 0, 2).Brush(SystemColors.ControlBrush);\n\t\t\n\t\tb.R.Add(out _search).Tooltip(\"Find documentation.\\n\\nMiddle-click to clear.\").UiaName(\"Find documentation\"); //rejected: watermark\n\t\t_search.TextChanged += (_, _) => _SearchInNameOnSearchTextChanged();\n\t\t_search.PreviewKeyDown += (_, e) => { if (e.Key == Key.Enter) _AiSearch(); };\n\t\t\n\t\tb.Options(modifyPadding: false, margin: new());\n\t\t\n\t\tb.xAddButtonIcon(out buttons_.aiSearch, EdIcons.AiSearch, _ => _AiSearch(), \"AI search\\n\\nEnter\");\n\t\tb.And(0).xAddButtonIcon(out buttons_.copyResults, \"*Material.ContentCopy\" + EdIcons.black, _CopyResultsForAiChat, \"Copy results for AI chat\\n\\nYou can paste it in ChatGPT, Gemini, etc.\\nThen the AI can answer your question better.\\nPaste it anywhere in your message.\").Hidden(null);\n\t\t\n\t\t_tv = new() { Name = \"Help_TOC\", SingleClickActivate = true, HotTrack = true, BackgroundColor = 0xf0f8e8, SmallIndent = true };\n\t\tb.Row(-1).Add(_tv);\n\t\t\n\t\tvar tb = b.R.xAddToolBar(hideOverflow: true);\n\t\tbuttons_.toolbar = tb.Parent as ToolBarTray;\n\t\tbuttons_.toolbar.Visibility = Visibility.Collapsed; //will add buttons and show when creating web browser control\n\t\ttb.UiaSetName(\"Help_navigation_toolbar\");\n\t\t\n\t\tb.End();\n\t\t\n\t\tPanels.PanelManager[\"Help\"].DontActivateFloating = e => e == _tv;\n\t\t\n\t\tP.IsVisibleChanged += (_, e) => {\n\t\t\tif ((bool)e.NewValue && _root == null) {\n\t\t\t\t_Load();\n\t\t\t\t_tv.ItemActivated += e => _OpenItem(e.Item as _Item, false);\n\t\t\t}\n\t\t};\n\t\t\n\t\t//#if DEBUG\n\t\t//\t\t_tv.ItemClick += e => {\n\t\t//\t\t\tif (e.Button == MouseButton.Right) {\n\t\t//\t\t\t\tvar m = new popupMenu();\n\t\t//\t\t\t\tm.Add(\"DEBUG\", disable: true);\n\t\t//\t\t\t\t//m[\"Print name words\"] = o => _DebugGetWords();\n\t\t//\t\t\t\tm.Show();\n\t\t//\t\t\t}\n\t\t//\t\t};\n\t\t//#endif\n\t}\n\t\n\tpublic UserControl P { get; }\n\t\n\tvoid _Load() {\n\t\ttry {\n\t\t\t_root = new _Item(null, _DocKind.Folder);\n\t\t\t_TOC(folders.ThisAppBS + \"toc.json\", _root);\n\t\t\t\n\t\t\tvar cookbookRoot = _root.FirstChild;\n\t\t\tvar first = cookbookRoot.FirstChild;\n\t\t\tfirst.Remove();\n\t\t\t_root.AddChild(first, first: true);\n\t\t\t\n\t\t\tcookbookRoot.isExpanded = true;\n\t\t\tcookbookRoot.Next.isExpanded = true; //API\n\t\t\t\n\t\t\tstatic void _TOC(string jsonFile, _Item root) {\n\t\t\t\tvar json = filesystem.loadText(jsonFile);\n\t\t\t\tvar jRoot = JsonNode.Parse(json).AsArray();\n\t\t\t\tforeach (JsonObject j in jRoot) {\n\t\t\t\t\tvar docKind = (string)j[\"name\"] switch { \"Cookbook\" => _DocKind.Cookbook, \"Articles\" => _DocKind.Article, \"Editor\" => _DocKind.Editor, _ => _DocKind.Api };\n\t\t\t\t\t_Add(j, root, docKind);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tstatic void _Add(JsonNode j, _Item ip, _DocKind docKind) {\n\t\t\t\t\tstring name = (string)j[\"name\"], href = (string)j[\"href\"], symKind = null;\n\t\t\t\t\tif (docKind == _DocKind.Api) symKind = (string)j[\"kind\"];\n\t\t\t\t\tif (j[\"items\"] is JsonArray { Count: > 0 } ja) {\n\t\t\t\t\t\tvar i = new _Item(name, _DocKind.Folder, symKind, href);\n\t\t\t\t\t\tip.AddChild(i);\n\t\t\t\t\t\tforeach (var v in ja) {\n\t\t\t\t\t\t\t_Add(v, i, docKind);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tip.AddChild(new _Item(name, docKind, symKind, href));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t_tv.SetItems(_root.Children());\n\t\t}\n\t\tcatch (Exception e1) { print.it(e1); }\n\t\t\n\t\t//_Test();\n\t}\n\t\n\tvoid _OpenItem(_Item item, bool select) {\n\t\tif (item == null) return;\n\t\tif (item.dir) {\n\t\t\tif (item.href == null) {\n\t\t\t\t_tv.Expand(item, null);\n\t\t\t\treturn;\n\t\t\t} else if (!item.isExpanded) {\n\t\t\t\t_tv.Expand(item, true);\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (_showingResults) {\n\t\t\t_selectedItem = item.clonedFrom;\n\t\t} else {\n\t\t\tif (select) {\n\t\t\t\t_openingItem = true;\n\t\t\t\t_search.Text = \"\";\n\t\t\t\t_openingItem = false;\n\t\t\t\t_tv.Select(item);\n\t\t\t}\n\t\t\t\n\t\t\t_selectedItem = item;\n\t\t}\n\t\t\n\t\tvar s1 = item.docKind switch { _DocKind.Cookbook => \"cookbook/\", _DocKind.Editor => \"editor/\", _DocKind.Article => \"articles/\", _ => \"api/\" };\n\t\tvar href = item.docKind == _DocKind.Cookbook ? Uri.EscapeDataString(item.name) + \".html\" : item.href;\n\t\tHelpUtil.AuHelp($\"{s1}{href}\"); //opens in the Read panel or in the web browser, depending on user settings\n\t}\n\t\n\tvoid _SearchInNameOnSearchTextChanged() {\n\t\tif (_aiResults != null) {\n\t\t\t_aiResults = null;\n\t\t\tbuttons_.aiSearch.Visibility = Visibility.Visible;\n\t\t\tbuttons_.copyResults.Visibility = Visibility.Collapsed;\n\t\t}\n\t\t\n\t\tvar s = _search.Text.Trim();\n\t\tif (s.Length < 2) {\n\t\t\t_tv.SetItems(_root.Children());\n\t\t\t_showingResults = false;\n\t\t\tif (!_openingItem && _selectedItem != null) {\n\t\t\t\t_tv.Select(_selectedItem);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\t\n\t\t//print.clear();\n\t\t\n\t\t//CONSIDER: use Lucene.\n\t\t//rejected: use SQLite FTS5. Tried but didn't like. Lucene is much better.\n\t\t\n\t\tvar root = _SearchContains(_root);\n\t\t_Item _SearchContains(_Item parent) {\n\t\t\t_Item R = null;\n\t\t\tfor (var n = parent.FirstChild; n != null; n = n.Next) {\n\t\t\t\t_Item r = null;\n\t\t\t\tif (n.dir) {\n\t\t\t\t\tr = _SearchContains(n);\n\t\t\t\t}\n\t\t\t\tif (!n.dir || n.href != null) {\n\t\t\t\t\tif (n.name.Contains(s, StringComparison.OrdinalIgnoreCase)) r ??= n.Clone();\n\t\t\t\t}\n\t\t\t\tif (r != null) {\n\t\t\t\t\tif (R == null) {\n\t\t\t\t\t\tR = parent.Clone();\n\t\t\t\t\t\tR.isExpanded = true;\n\t\t\t\t\t}\n\t\t\t\t\tR.AddChild(r);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn R;\n\t\t}\n\t\t\n\t\t//try stemmed fuzzy. Max Levenshtein distance 1 for a word.\n\t\t//\trejected: use FuzzySharp. For max distance 1 don't need it.\n\t\tbool fuzzy = root == null && s.Length >= 3;\n\t\tif (fuzzy) {\n\t\t\tstring[] a1 = _Stem(s);\n\t\t\troot = _SearchFuzzy(_root);\n\t\t\t_Item _SearchFuzzy(_Item parent) {\n\t\t\t\t_Item R = null;\n\t\t\t\tfor (var n = parent.FirstChild; n != null; n = n.Next) {\n\t\t\t\t\t_Item r = null;\n\t\t\t\t\tif (n.dir) {\n\t\t\t\t\t\tr = _SearchFuzzy(n);\n\t\t\t\t\t}\n\t\t\t\t\tif (!n.dir || n.href != null) {\n\t\t\t\t\t\tn.stemmedName ??= _Stem(n.name);\n\t\t\t\t\t\tbool allFound = true;\n\t\t\t\t\t\tforeach (var v1 in a1) {\n\t\t\t\t\t\t\tbool found = false;\n\t\t\t\t\t\t\tforeach (var v2 in n.stemmedName) {\n\t\t\t\t\t\t\t\tif (found = _Match(v1, v2)) break;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (!(allFound &= found)) break;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (allFound) r = n.Clone();\n\t\t\t\t\t}\n\t\t\t\t\tif (r != null) {\n\t\t\t\t\t\tif (R == null) {\n\t\t\t\t\t\t\tR = parent.Clone();\n\t\t\t\t\t\t\tR.isExpanded = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tR.AddChild(r);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn R;\n\t\t\t}\n\t\t}\n\t\t\n\t\t_tv.SetItems(root?.Children());\n\t\t_showingResults = true;\n\t\t\n\t\tstatic bool _Match(string s1, string s2) {\n\t\t\tif (s1[0] != s2[0] || Math.Abs(s1.Length - s2.Length) > 1) return false; //the first char must match\n\t\t\tif (s1.Length > s2.Length) Math2.Swap(ref s1, ref s2); //let s1 be the shorter\n\t\t\t\n\t\t\tint ib = 0, ie1 = s1.Length, ie2 = s2.Length;\n\t\t\twhile (ib < s1.Length && s1[ib] == s2[ib]) ib++; //skip common prefix\n\t\t\twhile (ie1 > ib && s1[ie1 - 1] == s2[--ie2]) ie1--; //skip common suffix\n\t\t\t\n\t\t\tint n = ie1 - ib;\n\t\t\tif (n == 1) return s1.Length == s2.Length || ib == ie1;\n\t\t\treturn n == 0;\n\t\t}\n\t}\n\t\n\tstring[] _Stem(string s) {\n\t\tif (_stem.stemmer == null) _stem = (new(), new(), new regexp(@\"(*UCP)[^\\W_]+\"));\n\t\t_stem.a.Clear();\n\t\tforeach (var v in _stem.rx.FindAll(s.Lower(), 0)) {\n\t\t\t_stem.a.Add(_stem.stemmer.Stem(v));\n\t\t}\n\t\treturn _stem.a.ToArray();\n\t}\n\t(Libs.Porter2Stemmer.EnglishPorter2Stemmer stemmer, List<string> a, regexp rx) _stem;\n\t\n\tasync void _AiSearch() {\n\t\tvar query = _search.Text.Trim();\n\t\tif (query.Length < 2) return;\n\t\t\n\t\tAI.AiModel.ApiKeys = App.Settings.ai_ak;\n\t\tvar emModel = AI.AiModel.GetModel<AI.AiEmbeddingModel>(App.Settings.ai_modelEmbed, displayName: true);\n\t\tif (emModel == null) {\n\t\t\t_AiSettingsError($\"Please go to Options > AI and select models for documentation search.\");\n\t\t\treturn;\n\t\t}\n\t\tvar rrModel = AI.AiModel.GetModel<AI.AiRerankModel>(App.Settings.ai_modelRerank, displayName: true);\n\t\tif (rrModel == null) AI.AiModel.RerankerModelWarning();\n\t\t\n\t\ttry {\n\t\t\t_ctsAiSearch?.Cancel();\n\t\t\t_ctsAiSearch?.Dispose();\n\t\t\t_ctsAiSearch = new();\n\t\t\tvar cancel = _ctsAiSearch.Token;\n\t\t\t\n\t\t\tvar em = new AI.Embeddings(emModel);\n\t\t\tvar ems = await Task.Run(() => em.GetDocsEmbeddings(cancel));\n\t\t\t\n\t\t\tvar r1 = _search.RectInScreen();\n\t\t\tusing var osd = osdText.showText(\"Searching.\\nClick to cancel.\", -1, new(r1.right, r1.bottom), showMode: OsdMode.ThisThread);\n\t\t\tosd.Clicked += (_, _) => { _ctsAiSearch?.Cancel(); };\n\t\t\t\n\t\t\tint takePlus = Math.Min(40, query.Count(c => c is <= ' ' or ',' or '.' or ';' or '?'));\n\t\t\tint take = 15 + takePlus;\n\t\t\t\n\t\t\tvar queryVector = await Task.Run(() => em.CreateEmbedding(query, cancel));\n\t\t\tvar topAll = em.GetTopMatches(queryVector, ems, rrModel == null ? 50 : 150);\n\t\t\tif (topAll.Count == 0) return;\n\t\t\t\n\t\t\tDictionary<string, (float score, bool summary)> dTop = [];\n\t\t\tforeach (var v in topAll) {\n\t\t\t\tvar name = v.f.name;\n\t\t\t\tbool isSum = name[0] == '+';\n\t\t\t\tif (isSum) name = name[1..];\n\t\t\t\tdTop.TryAdd(name, (v.score, isSum));\n\t\t\t}\n\t\t\tvar aTop = dTop.Select(o => (name: o.Key, v: o.Value)).OrderByDescending(o => o.v.score).ToArray();\n\t\t\t\n\t\t\tList<_Item> a = [];\n\t\t\tif (rrModel != null) {\n\t\t\t\tosd.Text = \"Reranking.\\nClick to cancel.\";\n\t\t\t\tawait Task.Run(() => {\n\t\t\t\t\tvar names = aTop.Select(o => o.name).ToArray();\n\t\t\t\t\tvar texts = em.GetDocsTexts(names);\n\t\t\t\t\tvar headers = rrModel.GetHeaders();\n\t\t\t\t\tvar post = rrModel.GetPostData(query, texts);\n\t\t\t\t\tvar j = rrModel.Post(post, headers, cancel).Json();\n\t\t\t\t\t//print.it(j.ToJsonString(new() { WriteIndented = true }));\n\t\t\t\t\tvar ar = rrModel.GetResults(j);\n\t\t\t\t\tint i = 0;\n\t\t\t\t\tfloat firstScore = 0;\n\t\t\t\t\tforeach (var v in ar) {\n\t\t\t\t\t\tif (i == 0) firstScore = v.score;\n\t\t\t\t\t\tif (i++ > take || firstScore - v.score > .3f || v.score < .4f) break;\n\t\t\t\t\t\t_FindAdd(names[v.index]);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tfloat firstScore = aTop[0].v.score;\n\t\t\t\tforeach (var v in aTop) {\n\t\t\t\t\tif (v.v.score < firstScore - .2f) break;\n\t\t\t\t\t_FindAdd(v.name);\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tvoid _FindAdd(string s) {\n\t\t\t\tif (s.Starts(\"[cookbook]\")) {\n\t\t\t\t\ts = s[11..];\n\t\t\t\t\tif (_FindRecipe(s, true) is { } r) {\n\t\t\t\t\t\ta.Add(r.Clone());\n\t\t\t\t\t} else {\n\t\t\t\t\t\tDebug_.Print(s);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tbool isAPI = s[0] != '[';\n\t\t\t\t\tvar r = _root.Children().ElementAt(isAPI ? 2 : s[1] == 'a' ? 3 : 4);\n\t\t\t\t\tif (isAPI) {\n\t\t\t\t\t\tint i = s.Starts(\"Au.More\") ? 7\n\t\t\t\t\t\t\t: s.Starts(\"Au.Types\") ? 8\n\t\t\t\t\t\t\t: s.Starts(\"Au.Triggers\") ? 11\n\t\t\t\t\t\t\t: 2;\n\t\t\t\t\t\tvar ns = s[..i];\n\t\t\t\t\t\tr = r.Children().First(o => o.name == ns);\n\t\t\t\t\t\tr = r.Descendants().First(o => o.ApiFullNameEquals(s));\n\t\t\t\t\t\ts = s[(i + 1)..];\n\t\t\t\t\t\tr = r.Clone(s);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tstring name = s = s[(s.IndexOf(' ') + 1)..], section = null;\n\t\t\t\t\t\tint i = s.Find(\" | \");\n\t\t\t\t\t\tif (i > 0) (section, name) = (name[(i + 3)..], name[..i]);\n\t\t\t\t\t\tif (section == \"other\") section = null;\n\t\t\t\t\t\t\n\t\t\t\t\t\tr = r.Children().FirstOrDefault(o => o.name == name);\n\t\t\t\t\t\tif (r == null) {\n\t\t\t\t\t\t\tDebug_.Print(name);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\tstring href = r.href;\n\t\t\t\t\t\tif (section != null) {\n\t\t\t\t\t\t\ts = $\"{r.name} | {section}\";\n\t\t\t\t\t\t\tsection = section.Lower().Replace(' ', '-').RxReplace(@\"[^[:alnum:]\\-]\", \"\");\n\t\t\t\t\t\t\thref = $\"{href}#{section}\";\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\ts = r.name;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\tr = new(s, r.docKind, null, href, r);\n\t\t\t\t\t}\n\t\t\t\t\ta.Add(r);\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t_tv.SetItems(a);\n\t\t\t_aiResults = a;\n\t\t\t_showingResults = true;\n\t\t\tbuttons_.aiSearch.Visibility = Visibility.Collapsed;\n\t\t\tbuttons_.copyResults.Visibility = Visibility.Visible;\n\t\t}\n\t\tcatch (OperationCanceledException etc) { if (etc.InnerException is TimeoutException) print.it(etc.Message); }\n\t\tcatch (InvalidCredentialException) {\n\t\t\tvar api = emModel.api;\n\t\t\t_AiSettingsError($\"Please go to Options > AI and set the API key for {api}.\\nYou can create an API key in your account on the {api} website.\");\n\t\t}\n\t\tcatch (Exception e1) { print.it(e1); }\n\t\t\n\t\tvoid _AiSettingsError(string text) {\n\t\t\tif (!dialog.showOkCancel(\"AI search error\", text, owner: P)) return;\n\t\t\tDOptions.AaShow(DOptions.EPage.AI);\n\t\t}\n\t}\n\tCancellationTokenSource _ctsAiSearch;\n\t\n\tvoid _CopyResultsForAiChat(WBButtonClickArgs ba) {\n\t\tif (!(_aiResults?.Count > 0)) return;\n\t\tusing var db = new sqlite(folders.ThisAppBS + \"doc-ai.db\", SLFlags.SQLITE_OPEN_READONLY);\n\t\tvar b = new StringBuilder($$\"\"\"\n\n\n--- Context ---\n\nBelow are LibreAutomate documentation articles that likely contain information to answer the user question.\nThey are retrieved and ordered using AI embedding.\nThe search phrase was: {{_search.Text}}\nThe user question is above or below this context data. If it's missing, assume the search phrase is the question.\n\nArticles are separated by `--- Article kind: KIND ---` lines, where KIND is one of:\n- API reference - library API member reference (method, class, etc)\n- library conceptual article - a library documentation article other than API member reference\n- cookbook - a how-to article with code examples\n- application help - LibreAutomate IDE documentation\n\n\"\"\");\n\t\tforeach (var v_ in _aiResults) {\n\t\t\tvar v = v_.clonedFrom;\n\t\t\tvar name = v.name;\n\t\t\tvar dbName = v.docKind switch {\n\t\t\t\t_DocKind.Cookbook => \"[cookbook] \" + name,\n\t\t\t\t_DocKind.Editor => \"[editor] \" + name,\n\t\t\t\t_DocKind.Article => \"[articles] \" + name,\n\t\t\t\t_ => v.Level == 3 ? $\"{v.Parent.name}.{name}\" : $\"{v.Parent.Parent.name}.{v.Parent.name}.{name}\"\n\t\t\t};\n\t\t\t\n\t\t\tif (!db.Get(out string text, \"SELECT text FROM doc WHERE name=?\", dbName)) { Debug_.Print(dbName); continue; }\n\t\t\tvar docKind = v.docKind switch { _DocKind.Cookbook => \"cookbook\", _DocKind.Editor => \"application help\", _DocKind.Article => \"library conceptual article\", _ => \"API reference\" };\n\t\t\tb.AppendFormat(\"\"\"\n\n\n--- Article kind: {0} ---\n\n{1}\n\"\"\", docKind, text);\n\t\t}\n\t\tb.Append(\"\"\"\n\n\n--- End of context ---\n\n\n\"\"\");\n\t\t\n\t\t//print.it(b.ToString());\n\t\tclipboard.text = b.ToString();\n\t}\n\t\n\t//#if DEBUG\n\t//\tvoid _Test() {\n\t//\t\tprint.clear();\n\t//\t\tusing var db = new sqlite(folders.ThisAppBS + \"doc-ai.db\", SLFlags.SQLITE_OPEN_READONLY);\n\t//\t\t//foreach (var v in _root.Children().ElementAt(2).Descendants().Where(o => o.Level > 2)) {\n\t//\t\t//\tvar s = v.Level == 3 ? $\"{v.Parent.name}.{v.name}\" : $\"{v.Parent.Parent.name}.{v.Parent.name}.{v.name}\";\n\t//\t\t//\t//print.it(s);\n\t//\t\t//\tif (!db.Get(out string text, \"SELECT text FROM doc WHERE name=?\", s)) { Debug_.Print(s); } //fields are not included in the DB for AI\n\t//\t\t//}\n\t//\t\t//foreach (var v in _root.Children().ElementAt(1).Descendants().Where(o => o.Level > 2 && !o.dir)) {\n\t//\t\t//\tvar s = \"[cookbook] \" + v.name;\n\t//\t\t//\t//print.it(s);\n\t//\t\t//\tif (!db.Get(out string text, \"SELECT text FROM doc WHERE name=?\", s)) { Debug_.Print(s); } //very small articles are not included in the DB for AI\n\t//\t\t//}\n\t//\t\t//foreach (var v in _root.Children().ElementAt(3).Descendants().Where(o => o.Level > 2 && !o.dir)) {\n\t//\t\t//\tvar s = \"[articles] \" + v.name;\n\t//\t\t//\t//print.it(s);\n\t//\t\t//\tif (!db.Get(out string text, \"SELECT text FROM doc WHERE name=?\", s)) { Debug_.Print(s); }\n\t//\t\t//}\n\t//\t\t//foreach (var v in _root.Children().ElementAt(4).Descendants().Where(o => o.Level > 2 && !o.dir)) {\n\t//\t\t//\tvar s = \"[editor] \" + v.name;\n\t//\t\t//\t//print.it(s);\n\t//\t\t//\tif (!db.Get(out string text, \"SELECT text FROM doc WHERE name=?\", s)) { Debug_.Print(s); }\n\t//\t\t//}\n\t//\t}\n\t//#endif\n\t\n\t/// <summary>\n\t/// Finds and opens a recipe.\n\t/// <para>\n\t/// If called in LA tools process or in a non-main LA thread, runs async in the main process/thread.\n\t/// </para>\n\t/// </summary>\n\t/// <param name=\"name\">Wildcard or start or any substring of recipe name.</param>\n\tpublic static void OpenRecipe(string name) {\n\t\tif (process.IsLaMainThread_) Panels.Help._OpenRecipe(name);\n\t\telse if (process.IsLaProcess_) App.Dispatcher.InvokeAsync(() => OpenRecipe(name));\n\t\telse WndCopyData.Send<char>(ScriptEditor.WndMsg_, 18, name);\n\t}\n\t\n\tvoid _OpenRecipe(string name) {\n\t\tPanels.PanelManager[P].Visible = true;\n\t\t_OpenItem(_FindRecipe(name), true);\n\t}\n\t\n\t_Item _FindRecipe(string s, bool exact = false) {\n\t\tvar d = _root.Descendants().Where(o => o.docKind is _DocKind.Cookbook);\n\t\tif (exact) return d.FirstOrDefault(o => !o.dir && o.name == s);\n\t\treturn d.FirstOrDefault(o => !o.dir && o.name.Like(s, true))\n\t\t\t?? d.FirstOrDefault(o => !o.dir && o.name.Starts(s, true))\n\t\t\t?? d.FirstOrDefault(o => !o.dir && o.name.Find(s, true) >= 0);\n\t}\n\t\n\tpublic void MenuCommand() {\n\t\tPanels.PanelManager[P].Visible = true;\n\t\t_search.Focus();\n\t\t\n\t\tif (_selectedItem == null && !App.Settings.doc_web) {\n\t\t\t_OpenItem(_root.FirstChild, true);\n\t\t}\n\t}\n\t\n\t//#if DEBUG\n\t//\tvoid _DebugGetWords() {\n\t//\t\tprint.clear();\n\t//\t\tvar hs = new HashSet<string>();\n\t//\t\tforeach (var recipe in _root.Descendants().Where(o => o.docKind is _DocKind.Cookbook)) {\n\t//\t\t\tvar a = _Stem(recipe.name);\n\t//\t\t\tforeach (var s in a)\n\t//\t\t\t\tif (s.Length > 2 && !s[0].IsAsciiDigit()) hs.Add(s);\n\t//\t\t}\n\t//\t\tprint.it(hs.OrderBy(o => o, StringComparer.OrdinalIgnoreCase));\n\t//\t}\n\t//#endif\n\t\n\tenum _DocKind : byte { Folder, Cookbook, Api, Editor, Article }\n\t\n\tclass _Item : TreeBase<_Item>, ITreeViewItem {\n\t\tinternal readonly string name;\n\t\tinternal readonly _DocKind docKind;\n\t\tinternal readonly string symKind;\n\t\tinternal readonly string href;\n\t\tinternal readonly _Item clonedFrom;\n\t\tinternal bool isExpanded;\n\t\tinternal string[] stemmedName;\n\t\t\n\t\tpublic _Item(string name, _DocKind docKind, string symKind = null, string href = null, _Item clonedFrom = null) {\n\t\t\tthis.name = name;\n\t\t\tthis.docKind = docKind;\n\t\t\tthis.symKind = symKind;\n\t\t\tthis.href = href;\n\t\t\tthis.clonedFrom = clonedFrom;\n\t\t}\n\t\t\n\t\tpublic _Item Clone(string newName = null) => new(newName ?? name, docKind, symKind, href, this);\n\t\t\n\t\tinternal bool dir => docKind == _DocKind.Folder;\n\t\t\n\t\t#region ITreeViewItem\n\t\t\n\t\tstring ITreeViewItem.DisplayText => name;\n\t\t\n\t\tobject ITreeViewItem.Image\n\t\t\t=> docKind switch {\n\t\t\t\t_DocKind.Folder => symKind != null ? _KindIcon : EdIcons.FolderArrow(isExpanded),\n\t\t\t\t_DocKind.Cookbook => name == \"Documentation\" ? EdIcons.Help : \"*BoxIcons.RegularCookie\" + EdIcons.darkYellow,\n\t\t\t\t_DocKind.Api => _KindIcon,\n\t\t\t\t_DocKind.Editor => \"*Material.ApplicationOutline\" + EdIcons.blue,\n\t\t\t\t_DocKind.Article => \"*PhosphorIcons.Article\" + EdIcons.black,\n\t\t\t\t_ => null\n\t\t\t};\n\t\t\n\t\tstring _KindIcon => symKind switch {\n\t\t\t\"Namespace\" => \"resources/ci/namespace.xaml\",\n\t\t\t\"Class\" => \"resources/ci/class.xaml\",\n\t\t\t\"Struct\" => \"resources/ci/structure.xaml\",\n\t\t\t\"Enum\" => \"resources/ci/enum.xaml\",\n\t\t\t\"Interface\" => \"resources/ci/interface.xaml\",\n\t\t\t\"Delegate\" => \"resources/ci/delegate.xaml\",\n\t\t\t\"Method\" or \"Constructor\" => \"resources/ci/method.xaml\",\n\t\t\t\"Property\" or \"Indexer\" => \"resources/ci/property.xaml\",\n\t\t\t\"Event\" => \"resources/ci/event.xaml\",\n\t\t\t\"Field\" => \"resources/ci/field.xaml\",\n\t\t\t\"Operator\" => \"resources/ci/operator.xaml\",\n\t\t\t_ => null\n\t\t};\n\t\t\n\t\tvoid ITreeViewItem.SetIsExpanded(bool yes) { isExpanded = yes; }\n\t\t\n\t\tbool ITreeViewItem.IsExpanded => isExpanded;\n\t\t\n\t\tIEnumerable<ITreeViewItem> ITreeViewItem.Items => base.Children();\n\t\t\n\t\tbool ITreeViewItem.IsFolder => dir;\n\t\t\n\t\t#endregion\n\t\t\n\t\tpublic bool ApiFullNameEquals(string s) {\n\t\t\tif (!s.Ends(name)) return false;\n\t\t\tint i1 = s.Length - name.Length - 1;\n\t\t\tif (s[i1] != '.') return false;\n\t\t\tint level = Level; if (!(level is 3 or 4)) return false;\n\t\t\tvar t = this;\n\t\t\tif (level == 4) { //member of a type\n\t\t\t\tt = Parent; //type\n\t\t\t\tint i2 = i1 - t.name.Length;\n\t\t\t\tif (!s.Eq(i2, t.name) || !s.Eq(--i2, '.')) return false;\n\t\t\t\ti1 = i2;\n\t\t\t}\n\t\t\tt = t.Parent; //namespace\n\t\t\tif (i1 != t.name.Length || !s.Starts(t.name)) return false;\n\t\t\treturn true;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Panels/PanelMouse.cs",
    "content": "using Au.Controls;\nusing System.Windows;\nusing System.Windows.Controls;\n\nnamespace LA;\n\nclass PanelMouse {\n\tKScintilla _sci;\n\tPOINT _prevXY;\n\twnd _prevWnd;\n\tstring _prevWndName;\n\tint _prevCounter;\n\t\n\tpublic PanelMouse() {\n\t\t//P.UiaSetName(\"Mouse panel\"); //no UIA element for Panel\n\t\t\n\t\t_sci = new KScintilla_(this) { Name = \"Mouse_info\" };\n\t\tP.Children.Add(_sci);\n\t}\n\t\n\tpublic Grid P { get; } = new();\n\t\n\tvoid _MouseInfo() {\n\t\t//using var p1 = perf.local();\n\t\tif (!P.IsVisible) return;\n\t\t\n\t\tvar p = mouse.xy;\n\t\tif (p == _prevXY && ++_prevCounter < 4) return; _prevCounter = 0; //use less CPU. c and wName rarely change when same p.\n\t\tvar c = wnd.fromXY(p);\n\t\t//p1.Next();\n\t\tvar w = c.Window;\n\t\tstring wName = w.Name;\n\t\tif (p == _prevXY && c == _prevWnd && wName == _prevWndName) return;\n\t\t_prevXY = p;\n\t\t_prevWnd = c;\n\t\t_prevWndName = wName;\n\t\t\n\t\tstring lineSep = App.Settings.mouse_singleLine ? \"    ..    \" : \"\\r\\n\";\n\t\tint limit = Math.Clamp(App.Settings.mouse_limitText, 20, 10000);\n\t\t\n\t\t//p1.Next();\n\t\tusing (new StringBuilder_(out var b)) {\n\t\t\tvar cn = w.ClassName;\n\t\t\tif (cn != null) {\n\t\t\t\tvar pc = p; w.MapScreenToClient(ref pc);\n\t\t\t\tb.AppendFormat(\"<b>Mouse</b> {0,5} {1,5}  .  <b>in window</b> {2,5} {3,5}    ..    <b>Program</b>  {4}\",\n\t\t\t\t\tp.x, p.y, pc.x, pc.y, w.ProgramName?.Escape(limit));\n\t\t\t\tif (c.UacAccessDenied) b.Append(\" <c red>(admin)<>\");\n\t\t\t\tb.AppendFormat(\"{0}<b>Window   \", lineSep);\n\t\t\t\tvar name = wName?.Escape(limit); if (!name.NE()) b.AppendFormat(\"</b>{0}  .  <b>\", name);\n\t\t\t\tb.Append(\"cn</b>  \").Append(cn.Escape(limit));\n\t\t\t\tif (c != w) {\n\t\t\t\t\tb.AppendFormat(\"{0}<b>Control   id</b>  {1}  .  <b>cn</b>  {2}\",\n\t\t\t\t\t\tlineSep, c.ControlId, c.ClassName?.Escape(limit));\n\t\t\t\t\tvar ct = c.Name;\n\t\t\t\t\tif (!ct.NE()) b.Append(\"  .  <b>name</b>  \").Append(ct.Escape(limit));\n\t\t\t\t} else if (cn == \"#32768\") {\n\t\t\t\t\tvar m = MenuItemInfo.FromXY(p, w, 50);\n\t\t\t\t\tif (m != null) {\n\t\t\t\t\t\tb.AppendFormat(\"{0}<b>Menu   id</b>  {1}\", lineSep, m.ItemId);\n\t\t\t\t\t\tif (m.IsSystem) b.Append(\" (system)\");\n\t\t\t\t\t\t//print.it(m.GetText(true, true));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t//rejected. Makes this func 5 times slower.\n\t\t\t\t//var color = CaptureScreen.Pixel(p);\n\t\t\t}\n\t\t\tvar s = b.ToString();\n\t\t\t//p1.Next();\n\t\t\t_sci.aaaSetText(s);\n\t\t}\n\t}\n\t\n\t//public void SetMouseInfoText(string text)\n\t//{\n\t//\tif(Dispatcher.Thread == Thread.CurrentThread) _SetMouseInfoText(text);\n\t//\telse Dispatcher.InvokeAsync(() => _SetMouseInfoText(text));\n\t//\n\t//\tvoid _SetMouseInfoText(string text) { _sci.aaaSetText(text); }\n\t//}\n\t\n\tinternal class KScintilla_ : KScintilla {\n\t\tPanelMouse _p;\n\t\t\n\t\tinternal KScintilla_(PanelMouse panel) {\n\t\t\t_p = panel;\n\t\t\t\n\t\t\tAaInitReadOnlyAlways = true;\n\t\t\tAaInitTagsStyle = KScintilla.AaTagsStyle.AutoAlways;\n\t\t}\n\t\t\n\t\tprotected override void AaOnHandleCreated() {\n\t\t\taaaStyleBackColor(Sci.STYLE_DEFAULT, 0xF0F0F0);\n\t\t\taaaStyleFont(Sci.STYLE_DEFAULT, App.Wmain);\n\t\t\taaaMarginSetWidth(1, 4);\n\t\t\taaaStyleClearAll();\n\t\t\tCall(Sci.SCI_SETHSCROLLBAR);\n\t\t\tCall(Sci.SCI_SETVSCROLLBAR);\n\t\t\tCall(Sci.SCI_SETWRAPMODE, Sci.SC_WRAP_WORD);\n\t\t\t\n\t\t\tApp.Timer025sWhenVisible += _p._MouseInfo;\n\t\t}\n\t\t\n\t\tprotected override IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) {\n\t\t\t//WndUtil.PrintMsg(out var s, default, msg, wParam, lParam); print.qm2.write(s);\n\t\t\tswitch (msg) {\n\t\t\tcase Api.WM_CONTEXTMENU:\n\t\t\t\tvar m = new popupMenu();\n\t\t\t\tm.AddCheck(\"Single line\", App.Settings.mouse_singleLine, o => { App.Settings.mouse_singleLine = o.IsChecked; });\n\t\t\t\tm[\"Text length...\"] = o => { if (dialog.showInputNumber(out int i, \"Mouse panel\", \"Maximal length of name, cn and program strings.\", editText: App.Settings.mouse_limitText, owner: Handle)) App.Settings.mouse_limitText = i > 0 ? i : 100; };\n\t\t\t\tm.Show(owner: Handle);\n\t\t\t\treturn default;\n\t\t\t}\n\t\t\treturn base.WndProc(hwnd, msg, wParam, lParam, ref handled);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Panels/PanelOpen.cs",
    "content": "using Au.Controls;\nusing System.Windows.Controls;\nusing System.Windows.Input;\n\nnamespace LA;\n\nclass PanelOpen {\n\tKTreeView _tv;\n\tbool _updatedOnce;\n\tbool _closing;\n\n\tpublic PanelOpen() {\n\t\t//P.UiaSetName(\"Open panel\"); //no UIA element for Panel\n\n\t\t_tv = new() { Name = \"Open_list\" };\n\t\tP.Children.Add(_tv);\n\t\t\n\t\tPanels.PanelManager[\"Open\"].DontActivateFloating = e => true;\n\t}\n\n\tpublic DockPanel P { get; } = new();\n\n\tpublic KTreeView TreeControl => _tv;\n\n\tpublic void UpdateList() {\n\t\tvar e = App.Model.OpenFiles.Select(o => new _Item { f = o });\n\t\tif (_Sort) e = e.OrderBy(o => o.f.Name, StringComparer.OrdinalIgnoreCase)\n\t\t\t\t.ThenBy(o => o.f.Id); //to make stable when there are multiple files with same name\n\t\t_tv.SetItems(e, _updatedOnce);\n\t\t_SelectCurrent(true);\n\t\tif (!_updatedOnce) {\n\t\t\t_updatedOnce = true;\n\t\t\tFilesModel.NeedRedraw += v => {\n\t\t\t\tif (v.f != null && !App.Model.OpenFiles.Contains(v.f)) return;\n\t\t\t\tif (v.renamed && _Sort) UpdateList();\n\t\t\t\telse _tv.Redraw(v.remeasure);\n\t\t\t};\n\t\t\t_tv.ItemClick += _tv_ItemClick;\n\t\t\t//_tv.ContextMenuOpening += (_,_) => //never mind\n\t\t}\n\t}\n\n\tprivate void _tv_ItemClick(TVItemEventArgs e) {\n\t\tif (e.Mod != 0 || e.ClickCount != 1) return;\n\t\tvar f = (e.Item as _Item).f;\n\t\tswitch (e.Button) {\n\t\tcase MouseButton.Left:\n\t\t\tApp.Model.SetCurrentFile(f);\n\t\t\tbreak;\n\t\tcase MouseButton.Right:\n\t\t\t_tv.SelectSingle(e.Item);\n\t\t\t_ContextMenu(f);\n\t\t\tbreak;\n\t\tcase MouseButton.Middle:\n\t\t\t_CloseFile(f);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tvoid _CloseFile(FileNode f) {\n\t\t_closing = true; //prevent scrolling to top when closing an item near the bottom\n\t\tApp.Model.CloseFile(f, selectOther: true);\n\t\t_closing = false;\n\t}\n\n\tvoid _ContextMenu(FileNode f) {\n\t\tvar m = new popupMenu();\n\t\tm.Add(1, \"Close\\tM-click\");\n\t\tm.Add(2, \"Close all other\");\n\t\tm.Add(3, \"Close all\");\n\t\tm.Separator();\n\t\tm.AddCheck(\"Sort alphabetically\", _Sort).Id = 4;\n\t\tvar r = m.Show();\n\t\tswitch (r) {\n\t\tcase 1:\n\t\t\t_CloseFile(f);\n\t\t\treturn;\n\t\tcase 2:\n\t\t\tApp.Model.CloseEtc(FilesModel.ECloseCmd.CloseAll, dontClose: f);\n\t\t\treturn;\n\t\tcase 3:\n\t\t\tApp.Model.CloseEtc(FilesModel.ECloseCmd.CloseAll);\n\t\t\treturn;\n\t\tcase 4:\n\t\t\tApp.Settings.openFiles_flags ^= 1;\n\t\t\tUpdateList();\n\t\t\treturn;\n\t\t}\n\t\t_SelectCurrent(false);\n\t}\n\n\tvoid _SelectCurrent(bool focus) {\n\t\tif (_tv.CountVisible == 0) return;\n\t\tint i = _Sort ? _tv.IndexOf(o => (o as _Item).f == App.Model.CurrentFile) : 0;\n\t\tif (focus) _tv.SetFocusedItem(i, _closing ? 0 : TVFocus.EnsureVisible);\n\t\t_tv.SelectSingle(i, andFocus: false);\n\t}\n\n\tstatic bool _Sort => 0 != (1 & App.Settings.openFiles_flags);\n\n\tclass _Item : ITreeViewItem {\n\t\tpublic FileNode f;\n\n\t\t#region ITreeViewItem\n\n\t\tstring ITreeViewItem.DisplayText => f.DisplayName;\n\n\t\tobject ITreeViewItem.Image => f.Image;\n\n\t\t#endregion\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Panels/PanelOutline.cs",
    "content": "using System.Windows.Controls;\nusing Au.Controls;\n\nusing Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp;\nusing Microsoft.CodeAnalysis.CSharp.Syntax;\nusing Microsoft.CodeAnalysis.CSharp.Extensions;\n\nnamespace LA;\n\nclass PanelOutline {\n\tKTreeView _tv;\n\tSciCode _activeDoc;\n\tbool _modified;\n\tbool _once;\n\t_Item _oldTree;\n\n\tpublic PanelOutline() {\n\t\t//P.UiaSetName(\"Outline panel\");\n\n\t\t_tv = new KTreeView { Name = \"Outline_list\", SingleClickActivate = true, HotTrack = true };\n\t\t_tv.ItemClick += e => { if (e.Button == System.Windows.Input.MouseButton.Right) _ContextMenu(); };\n\t\t_tv.ContextMenuOpening += (_, _) => _ContextMenu(); //right-click in empty space\n\t\tP.Children.Add(_tv);\n\t\t\n\t\tPanels.PanelManager[\"Outline\"].DontActivateFloating = e => e == _tv;\n\t}\n\n\tpublic DockPanel P { get; } = new();\n\n\tpublic void Timer025sWhenVisible() {\n\t\tif (_activeDoc == null || _modified) Update(); else if (!P.IsVisible) Clear();\n\t\t//if (_oldTree != null) if (0 != (4 & App.Settings.outline_flags)) _Sync();\n\t}\n\n\tpublic void SciModified() {\n\t\tif (_activeDoc != null) _modified = true;\n\t}\n\n\tpublic void Update() {\n\t\tif (P.IsVisible) {\n\t\t\ttry { if (_Update()) return; }\n\t\t\tcatch (Exception e1) { print.it(e1); }\n\t\t}\n\t\tClear();\n\t}\n\n\tbool _Update() {\n\t\t//print.it(\"update\");\n\t\t//using var p1 = perf.local();\n\t\tif (!CodeInfo.GetContextAndDocument(out var cd, 0, metaToo: true)) return false;\n\t\tvar cu = cd.syntaxRoot;\n\n\t\tvar root = new _Item();\n\n\t\t//at first get regions\n\t\tif (cu.ContainsDirectives) {\n\t\t\t_Item regions = null;\n\t\t\tfor (var d = cu.GetFirstDirective(); d != null; d = d.GetNextDirective()) {\n\t\t\t\tif (!d.IsKind(SyntaxKind.RegionDirectiveTrivia) || !d.IsActive) continue;\n\t\t\t\tvar s = d.EndOfDirectiveToken.LeadingTrivia.ToString(); if (s.NE()) continue;\n\t\t\t\tif (regions == null) root.AddChild(regions = new(\"#region\", CiItemKind.Region, 0));\n\t\t\t\tregions.AddChild(new(s, CiItemKind.Region, d.SpanStart));\n\t\t\t}\n\t\t}\n\n\t\t//then get member declarations\n\t\tvar members = cu.Members;\n\t\twhile (members.Count == 1) {\n\t\t\tvar f = members.First();\n\t\t\tif (f is BaseNamespaceDeclarationSyntax nd) members = nd.Members;\n\t\t\telse if (f is TypeDeclarationSyntax td) members = td.Members;\n\t\t\telse break;\n\t\t}\n\t\t//p1.Next('d');\n\t\t_Members(root, members, 0);\n\t\t//p1.Next('m');\n\n\t\tvoid _Members(_Item parent, SyntaxList<MemberDeclarationSyntax> members, int level) {\n\t\t\tint sort = App.Settings.outline_flags & 3;\n\t\t\tList<_Item> a = sort != 0 ? new() : null;\n\t\t\t_Item locals = null;\n\t\t\tforeach (var m in members) {\n\t\t\t\tif (level == 0 && m is GlobalStatementSyntax g) {\n\t\t\t\t\tif (g.Statement is not LocalFunctionStatementSyntax d) continue;\n\t\t\t\t\tif (locals == null) parent.AddChild(locals = new(\"Local functions\", CiItemKind.LocalMethod, 0));\n\t\t\t\t\tvar k = new _Item(d);\n\t\t\t\t\tlocals.AddChild(k);\n\t\t\t\t} else {\n\t\t\t\t\tvar k = new _Item(m);\n\t\t\t\t\tif (a != null) a.Add(k); else parent.AddChild(k);\n\t\t\t\t\tswitch (m) {\n\t\t\t\t\tcase BaseNamespaceDeclarationSyntax d:\n\t\t\t\t\t\t_Members(k, d.Members, level + 1);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase TypeDeclarationSyntax d:\n\t\t\t\t\t\t_Members(k, d.Members, level + 1);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (a != null) {\n\t\t\t\ta.Sort((i1, i2) => {\n\t\t\t\t\tif (sort >= 2) {\n\t\t\t\t\t\tint k1 = (int)i1._kind, k2 = (int)i2._kind;\n\t\t\t\t\t\tif (k1 != k2) return k1 - k2;\n\t\t\t\t\t\t//if (sort == 2) return i1._pos - i2._pos;\n\t\t\t\t\t}\n\t\t\t\t\tstring s1 = i1._text, s2 = i2._text;\n\t\t\t\t\tbool a1 = s1[0].IsAsciiAlpha(), a2 = s2[0].IsAsciiAlpha();\n\t\t\t\t\tif (a1 != a2) return a1 ? -1 : 1;\n\t\t\t\t\treturn string.CompareOrdinal(s1, s2);\n\t\t\t\t});\n\t\t\t\tforeach (var v in a) parent.AddChild(v);\n\t\t\t}\n\t\t}\n\n\t\t_modified = false;\n\t\t_activeDoc = cd.sci;\n\t\t//_oldDocument = cd.document;\n\n\t\tint changed = 4;\n\t\tif (_oldTree != null) {\n\t\t\tchanged = _TreeChanged(_oldTree, root);\n\t\t\t//print.it(changed);\n\t\t\tif (changed == 0) {\n\t\t\t\t_UpdatePos(_oldTree, root);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\t_oldTree = root;\n\t\t//p1.Next('c');\n\n\t\tif (!_once) {\n\t\t\t_once = true;\n\t\t\t_tv.ItemActivated += e => {\n\t\t\t\t_activeDoc.Focus();\n\t\t\t\tvar v = e.Item as _Item;\n\t\t\t\t_activeDoc.aaaGoToPos(true, v._pos);\n\t\t\t};\n\t\t\tPanels.Editor.ActiveDocChanged += Clear;\n\t\t}\n\n\t\t_tv.SetItems(root.Children(), modified: changed != 4);\n\t\treturn true;\n\n\t\t//0 same, 1 changed\n\t\tint _TreeChanged(_Item old, _Item now) {\n\t\t\tint R = 0;\n\t\t\tif (old.HasChildren || now.HasChildren) {\n\t\t\t\tfor (_Item o = old.FirstChild, n = now.FirstChild; ; o = o.Next, n = n.Next) {\n\t\t\t\t\tif ((o == null) != (n == null)) return 1; //added or removed at the end\n\t\t\t\t\tif (o == null) break;\n\t\t\t\t\tif (!n.Eq(o)) {\n\t\t\t\t\t\t//is just changed text of this, or something added or removed in the middle? If added/removed, need to update _isExpanded.\n\t\t\t\t\t\tR |= 1;\n\t\t\t\t\t\t_Item o1 = o.Next, n1 = n.Next;\n\t\t\t\t\t\tif (o1 != null && n1 != null) { //else added or removed at the end\n\t\t\t\t\t\t\tif (!n1.Eq(o1)) { //else just changed text\n\t\t\t\t\t\t\t\tfor (; n1 != null; n1 = n1.Next) if (n1.Eq(o)) { n = n1; goto g1; } //inserted 1 or more\n\t\t\t\t\t\t\t\tfor (; o1 != null; o1 = o1.Next) if (o1.Eq(n)) { o = o1; goto g1; } //removed 1 or more\n\t\t\t\t\t\t\t\treturn 1;\n\t\t\t\t\t\t\t\tg1:;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (o.HasChildren || n.HasChildren) {\n\t\t\t\t\t\tR |= _TreeChanged(o, n);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tnow._isExpanded = old._isExpanded;\n\t\t\t}\n\t\t\treturn R;\n\t\t}\n\n\t\tvoid _UpdatePos(_Item old, _Item now) {\n\t\t\tfor (_Item o = old.FirstChild, n = now.FirstChild; o != null; o = o.Next, n = n.Next) {\n\t\t\t\to._pos = n._pos;\n\t\t\t\tif (o.HasChildren) _UpdatePos(o, n);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void Clear() {\n\t\tif (_activeDoc == null) return;\n\t\t_activeDoc = null;\n\t\t_oldTree = null;\n\t\t_modified = false;\n\t\t_tv.SetItems(null);\n\t}\n\n\t//unfinished, rejected, maybe in the future. Can't use just _pos. Need to get and update full spans.\n\t//\tMaybe better add \"manual sync\" button instead of \"auto sync\" option.\n\t//void _Sync() {\n\t//\tDebug.Assert(this.IsVisible && _oldTree != null);\n\t//\t//print.it(\"sync\");\n\n\t//\tint pos = Panels.Editor.ActiveDoc.aaaCurrentPos16; //todo: return if didn't change (use the position changed notification).\n\t//\t_Item found = null;\n\t//\tif (0 == (3 & App.Settings.outline_flags)) { //not sorted\n\t//\t\tfound = _Find(_oldTree);\n\n\t//\t\t_Item _Find(_Item parent) {\n\t//\t\t\tfor (var v = parent.FirstChild; v != null; v = v.Next) {\n\t//\t\t\t\tif (v._pos >= pos) return v;\n\t//\t\t\t\tif (v.HasChildren) {\n\t//\t\t\t\t\tvar r = _Find(v);\n\t//\t\t\t\t\tif (r != null) return r;\n\t//\t\t\t\t}\n\t//\t\t\t}\n\t//\t\t\treturn null;\n\t//\t\t}\n\t//\t}\n\t//\tif (found != null) {\n\t//\t\tprint.it(found._text);\n\n\t//\t}\n\t//}\n\n\tvoid _ContextMenu() {\n\t\tvar m = new popupMenu();\n\t\tint flags = App.Settings.outline_flags, sort = flags & 3;\n\t\tm.AddRadio(\"Don't sort\", sort == 0).Id = 1;\n\t\tm.AddRadio(\"Sort by name\", sort == 1).Id = 2;\n\t\tm.AddRadio(\"Sort by kind and name\", sort == 2).Id = 3;\n\t\t//m.AddRadio(\"Sort by kind and name\", sort == 3).Id = 4; //sorting by kind+position probably not useful. Always sort by kind+name.\n\t\t//m.Separator();\n\t\t//m.AddCheck(\"Sync\", 0 != (flags & 4), _ => App.Settings.outline_flags ^= 4);\n\t\tint i = m.Show(owner: P);\n\t\tif (i is >= 1 and <= 3) {\n\t\t\tApp.Settings.outline_flags = (byte)((App.Settings.outline_flags & ~3) | (i - 1));\n\t\t\tif (_oldTree != null) {\n\t\t\t\t_oldTree = null;\n\t\t\t\tUpdate();\n\t\t\t}\n\t\t}\n\t}\n\n\tclass _Item : TreeBase<_Item>, ITreeViewItem {\n\t\tinternal string _text;\n\t\tinternal int _pos;\n\t\tinternal CiItemKind _kind;\n\t\tinternal bool _isExpanded = true;\n\n\t\tpublic _Item() { } //root\n\n\t\tpublic _Item(string text, CiItemKind kind, int pos) {\n\t\t\t_text = text;\n\t\t\t_pos = pos;\n\t\t\t_kind = kind;\n\t\t}\n\n\t\tpublic _Item(MemberDeclarationSyntax m) {\n\t\t\t//CiUtil.PrintNode(m);\n\t\t\tstring name;\n\t\t\tif (m is BaseFieldDeclarationSyntax fd) { //field, event\n\t\t\t\tvar vd = fd.Declaration;\n\t\t\t\tvar a = vd.Variables;\n\t\t\t\tif (a.Count == 1) name = a[0].Identifier.Text + \" : \" + vd.Type;\n\t\t\t\telse name = string.Join(\", \", a.Select(o => o.Identifier.ToString())) + \" : \" + vd.Type;\n\t\t\t} else if (m is BaseNamespaceDeclarationSyntax nd) {\n\t\t\t\tname = nd.Name.ToString();\n\t\t\t} else if (m is ExtensionBlockDeclarationSyntax ed) {\n\t\t\t\tname = $\"extension{ed.TypeParameterList}{ed.ParameterList}\";\n\t\t\t} else if (m is TypeDeclarationSyntax td) {\n\t\t\t\tname = td.Identifier.Text + td.TypeParameterList;\n\t\t\t} else {\n\t\t\t\tname = m switch {\n\t\t\t\t\tDestructorDeclarationSyntax dd => \"~\" + dd.Identifier.Text,\n\t\t\t\t\tConversionOperatorDeclarationSyntax od => $\"{od.ImplicitOrExplicitKeyword.Text} {od.Type}\",\n\t\t\t\t\t_ => m.GetNameToken().Text\n\t\t\t\t};\n\n\t\t\t\tif (m is MethodDeclarationSyntax md) name = $\"{name}{md.TypeParameterList}({_Function(md.ParameterList.Parameters)}) : {md.ReturnType}\";\n\t\t\t\telse if (m is OperatorDeclarationSyntax od) name = $\"{name}({_Function(od.ParameterList.Parameters)}) : {od.ReturnType}\";\n\t\t\t\telse if (m is BaseMethodDeclarationSyntax bmd) name = $\"{name}({_Function(bmd.ParameterList.Parameters)})\";\n\t\t\t\telse if (m is IndexerDeclarationSyntax id) name = $\"{name}[{_Function(id.ParameterList.Parameters)}]\";\n\t\t\t\telse if (m is DelegateDeclarationSyntax odd) name = $\"{name}{odd.TypeParameterList}({_Function(odd.ParameterList.Parameters)}) : {odd.ReturnType}\";\n\n\t\t\t\tif (m is BasePropertyDeclarationSyntax pd) name = $\"{name} : {pd.Type}\"; //property, indexer, event\n\n\t\t\t\tstatic string _Function(SeparatedSyntaxList<ParameterSyntax> a) {\n\t\t\t\t\tint n = a.Count; if (n == 0) return null;\n\t\t\t\t\tif (n == 1) return a[0].Type.ToString();\n\t\t\t\t\treturn string.Join(\", \", a.Select(o => o.Type));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t_text = name;\n\t\t\t_kind = CiUtil.MemberDeclarationToKind(m);\n\t\t\t_pos = m.SpanStart;\n\t\t}\n\n\t\tpublic _Item(LocalFunctionStatementSyntax m) {\n\t\t\t_text = m.Identifier.Text;\n\t\t\t_kind = CiItemKind.Method;\n\t\t\t_pos = m.SpanStart;\n\t\t}\n\n\t\tpublic bool Eq(_Item b) => _text == b._text && _kind == b._kind;\n\n\t\t#region ITreeViewItem\n\n\t\tstring ITreeViewItem.DisplayText => _text;\n\n\t\tobject ITreeViewItem.Image => CiComplItem.ImageResource(_kind);\n\n\t\tvoid ITreeViewItem.SetIsExpanded(bool yes) { _isExpanded = yes; }\n\n\t\tbool ITreeViewItem.IsExpanded => _isExpanded;\n\n\t\tIEnumerable<ITreeViewItem> ITreeViewItem.Items => base.Children();\n\n\t\tbool ITreeViewItem.IsFolder => _IsFolder;\n\t\tbool _IsFolder => base.HasChildren;\n\n\t\t//bool ITreeViewItem.IsBold => _IsFolder;\n\n\t\t#endregion\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Panels/PanelOutput.cs",
    "content": "//TODO2: multiple output controls in the Output panel. Eg compiler output should be separated, and cleared without clearing common output text.\n//\tAlternatively, allow to clear certain parts of output text, eg compiler output.\n\nusing System.Windows.Controls;\nusing Au.Controls;\nusing static Au.Controls.Sci;\n\nnamespace LA;\n\nclass PanelOutput {\n\treadonly KScintilla_ _c;\n\treadonly KPanels.ILeaf _leaf;\n\treadonly Queue<PrintServerMessage> _history;\n\t\n\tpublic KScintilla_ Scintilla => _c;\n\t\n\tpublic PanelOutput() {\n\t\t//P.UiaSetName(\"Output panel\"); //no UIA element for Panel\n\t\t\n\t\t_c = new KScintilla_(this) { Name = \"Output_text\" };\n\t\tP.Children.Add(_c);\n\t\t_history = new Queue<PrintServerMessage>();\n\t\tApp.Commands.BindKeysTarget(P, \"Output\");\n\t\t_leaf = Panels.PanelManager[\"Output\"];\n\t}\n\t\n\tpublic DockPanel P { get; } = new();\n\t\n\tpublic void Clear() { _c.aaaClearText(); _c.Call(SCI_SETSCROLLWIDTH, 1); }\n\t\n\tpublic void Copy() { _c.Call(SCI_COPY); }\n\t\n\t//public void Find() { Panels.Find.CtrlF(_c); }\n\t\n\tpublic void History() {\n\t\tvar p = new KPopupListBox { PlacementTarget = P, Placement = System.Windows.Controls.Primitives.PlacementMode.MousePoint };\n\t\tp.Control.ItemsSource = _history;\n\t\tp.OK += o => print.it((o as PrintServerMessage).Text);\n\t\tP.Dispatcher.InvokeAsync(() => p.IsOpen = true);\n\t}\n\t\n\tvoid _c_HandleCreated() {\n\t\t_inInitSettings = true;\n\t\tif (WrapLines) WrapLines = true;\n\t\tif (WhiteSpace) WhiteSpace = true;\n\t\t_c.AaNoMouseSetFocus = MButtons.Middle;\n\t\t_inInitSettings = false;\n\t}\n\tbool _inInitSettings;\n\t\n\tpublic bool WrapLines {\n\t\tget => App.Settings.output_wrap;\n\t\tset {\n\t\t\tDebug.Assert(!_inInitSettings || value);\n\t\t\tif (!_inInitSettings) App.Settings.output_wrap = value;\n\t\t\t_c.Call(SCI_SETWRAPMODE, value ? SC_WRAP_WORD : 0);\n\t\t\tApp.Commands[nameof(Menus.Tools.Output.Output_wrap_lines)].Checked = value;\n\t\t}\n\t}\n\t\n\tpublic bool WhiteSpace {\n\t\tget => App.Settings.output_white;\n\t\tset {\n\t\t\tDebug.Assert(!_inInitSettings || value);\n\t\t\tif (!_inInitSettings) App.Settings.output_white = value;\n\t\t\t_c.Call(SCI_SETVIEWWS, value);\n\t\t\tApp.Commands[nameof(Menus.Tools.Output.Output_white_space)].Checked = value;\n\t\t}\n\t}\n\t\n\tinternal class KScintilla_ : KScintilla {\n\t\tPanelOutput _p;\n\t\tStringBuilder _sb;\n\t\t\n\t\tinternal KScintilla_(PanelOutput panel) {\n\t\t\t_p = panel;\n\t\t\t\n\t\t\tAaInitReadOnlyAlways = true;\n\t\t\tAaInitTagsStyle = AaTagsStyle.AutoWithPrefix;\n\t\t\tAaInitImages = true;\n\t\t\t\n\t\t\t//App.Commands[nameof(Menus.Tools.Output)].SetKeysTarget(this);\n\t\t}\n\t\t\n\t\tprotected override void AaOnHandleCreated() {\n\t\t\taaaMarginSetWidth(1, 3);\n\t\t\tAaSetStyles();\n\t\t\t_p._c_HandleCreated();\n\t\t\t\n\t\t\tAaTags.CodeStylesProvider = CiUtil.GetScintillaStylingBytes8;\n\t\t\t\n\t\t\tSciTags.AddCommonLinkTag(\"open\", _OpenLink);\n\t\t\tSciTags.AddCommonLinkTag(\"script\", _RunScript);\n\t\t\tSciTags.AddCommonLinkTag(\"google\", _Google);\n\t\t\tSciTags.AddCommonLinkTag(\"+recipe\", PanelHelp.OpenRecipe);\n\t\t\tSciTags.AddCommonLinkTag(\"+nuget\", DNuget.ShowSingle);\n\t\t\tSciTags.AddCommonLinkTag(\"+options\", s => { DOptions.AaShow(s.NE() ? null : Enum.Parse<DOptions.EPage>(s)); });\n\t\t\tAaTags.AddLinkTag(\"+properties\", fid => {\n\t\t\t\tvar f = App.Model.FindCodeFile(fid);\n\t\t\t\tif (f == null || !App.Model.SetCurrentFile(f)) return;\n\t\t\t\tMenus.File.Properties();\n\t\t\t});\n\t\t\tAaTags.AddLinkTag(\"+DCustomize\", DCustomize.ShowSingle);\n\t\t\t\n\t\t\tApp.PrintServer.SetNotifications(AaWnd, Api.WM_APP);\n\t\t\t\n\t\t\tbase.AaOnHandleCreated();\n\t\t}\n\t\t\n\t\tpublic void AaSetStyles() {\n\t\t\tvar t = SciTheme.Default with {\n\t\t\t\tFontName = App.Settings.font_output.name,\n\t\t\t\tFontSize = App.Settings.font_output.size,\n\t\t\t\tBackground = 0xF7F7F7,\n\t\t\t};\n\t\t\tt.ToScintilla(this);\n\t\t\tCall(SCI_SETWHITESPACESIZE, 2); //not DPI-scaled\n\t\t}\n\t\t\n\t\tprotected override IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) {\n\t\t\t//WndUtil.PrintMsg(out var s, default, msg, wParam, lParam); print.qm2.write(s);\n\t\t\tswitch (msg) {\n\t\t\tcase Api.WM_APP:\n\t\t\t\tAaTags.PrintServerProcessMessages(App.PrintServer, _onServerMessage ??= _OnServerMessage);\n\t\t\t\treturn default;\n\t\t\tcase Api.WM_MBUTTONDOWN:\n\t\t\t\t_p.Clear();\n\t\t\t\treturn default;\n\t\t\tcase Api.WM_CONTEXTMENU:\n\t\t\t\tvar m = new ContextMenu { PlacementTarget = this };\n\t\t\t\tApp.Commands[nameof(Menus.Tools.Output)].CopyToMenu(m);\n\t\t\t\tm.IsOpen = true;\n\t\t\t\treturn default;\n\t\t\t}\n\t\t\treturn base.WndProc(hwnd, msg, wParam, lParam, ref handled);\n\t\t}\n\t\t\n\t\tAction<PrintServerMessage> _onServerMessage;\n\t\tvoid _OnServerMessage(PrintServerMessage m) {\n\t\t\tif (m.Type != PrintServerMessageType.Write) {\n\t\t\t\tif (m.Type == PrintServerMessageType.TaskEvent) RecentTT.TriggerEvent(m);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\t//create links in compilation errors/warnings or run-time stack trace\n\t\t\tvar s = m.Text; int i;\n\t\t\tif (s.Length >= 22) {\n\t\t\t\tif (s.Starts(\"<><lc #\") && (s.Eq(13, \">Compilation: \") || s.Eq(13, \">Can't export \"))) { //compilation. Or error in meta while exporting.\n\t\t\t\t\ts_rxCompError ??= new regexp(@\"(?m)^\\[(.+?)(\\((\\d+),(\\d+)\\))?\\]: ((?:error|warning) \\w+)\");\n\t\t\t\t\tm.Text = s_rxCompError.Replace(s, x => {\n\t\t\t\t\t\tvar f = App.Model?.FindByFilePath(x[1].Value);\n\t\t\t\t\t\tif (f == null) return x[0].Value;\n\t\t\t\t\t\tvar sEW = x[5].Value;\n\t\t\t\t\t\treturn $\"<open {f.IdStringWithWorkspace}|{x[3].Value}|{x[4].Value}>{f.Name}{x[2].Value}<>: <c {(sEW[0] == 'e' ? \"red\" : \"green\")}>{sEW}<>\";\n\t\t\t\t\t});\n\t\t\t\t} else if ((i = s.Find(\"   at \") + 1) >= 0 && s.Find(\":line \", i) > 0) { //stack trace with source file info\n\t\t\t\t\t_ModifyStackTraces(m);\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tif (s.Length <= 10_000) { //* 50 = 1 MB\n\t\t\t\tif (!ReferenceEquals(s, m.Text)) m = new PrintServerMessage(PrintServerMessageType.Write, s, m.TimeUtc, m.Caller);\n\t\t\t\tvar h = _p._history;\n\t\t\t\th.Enqueue(m);\n\t\t\t\tif (h.Count > 50) h.Dequeue();\n\t\t\t}\n\t\t\t\n\t\t\tif (!_p.P.IsVisible) _p._leaf.Visible = true;\n\t\t}\n\t\tstatic regexp s_rxCompError;\n\t\t\n\t\tstatic void _OpenLink(string s) {\n\t\t\tvar a = s.Split('|');\n\t\t\tif (a.Length > 3) App.Model.OpenAndGoTo3(a[0], a[3]);\n\t\t\telse App.Model.OpenAndGoTo2(a[0], a.Length > 1 ? a[1] : null, a.Length > 2 ? a[2] : null);\n\t\t}\n\t\t\n\t\tstatic void _RunScript(string s) {\n\t\t\tvar a = s.Split('|');\n\t\t\tvar f = App.Model.FindCodeFile(a[0]); if (f == null) return;\n\t\t\tCompileRun.CompileAndRun(true, f, a.Length == 1 ? null : a.RemoveAt(0));\n\t\t}\n\t\t\n\t\tstatic void _Google(string s) {\n\t\t\tvar a = s.Split('|');\n\t\t\tstring s1 = a[0], s2 = a.Length > 1 ? a[1] : null;\n\t\t\trun.itSafe(App.Settings.internetSearchUrl + System.Net.WebUtility.UrlEncode(s1) + s2);\n\t\t}\n\t\t\n\t\tvoid _ModifyStackTraces(PrintServerMessage psm) {\n\t\t\tstring s = psm.Text;\n\t\t\t//print.qm2.write(\"`\" + s + \"`\");\n\t\t\tvar b = _sb ??= new StringBuilder(s.Length + 2000);\n\t\t\tb.Clear();\n\t\t\tbool wasFormatted = s.Starts(\"<>\");\n\t\t\tRXGroup[] aLit = null;\n\t\t\tif (wasFormatted) (s_rxLiteral ??= new regexp(@\"(?s)<([\\a_])>.+?</\\1>\")).FindAllG(s, 0, out aLit);\n\t\t\telse b.Append(\"<><\\a>\");\n\t\t\tint appendFrom = 0;\n\t\t\t\n\t\t\t//for each stack trace\n\t\t\ts_rxStack ??= new regexp(@\"(?m)(^   at .+(?:\\R|\\z))+(^ *--.*\\R(?1)+)*\");\n\t\t\tforeach (var g1 in s_rxStack.FindAllG(s, 0)) {\n\t\t\t\tint start = g1.Start, end = g1.End;\n\t\t\t\tif (s[end - 1] == '\\n' && s[--end - 1] == '\\r') end--;\n\t\t\t\tbool inLiteral = true;\n\t\t\t\tif (wasFormatted) {\n\t\t\t\t\tif (s[end - 1] == '>') //don't include eg `<\\a><>` at the end of the last line\n\t\t\t\t\t\tif ((s_rxClosingTags ??= new regexp(@\"(?:<(?:/.+?)?>)+$\")).Match(s, 0, out RXGroup g2, s.LastIndexOf('\\n', end - 1)..end)) end = g2.Start;\n\t\t\t\t\tinLiteral = aLit?.Any(o => o.Start < start && o.End > end) ?? false;\n\t\t\t\t}\n\t\t\t\tif (s.Find(\"<\\a>\", start..end) >= 0 || s.Find(\"</\\a>\", start..end) >= 0 || s.Find(\"<_>\", start..end) >= 0 || s.Find(\"</_>\", start..end) >= 0) continue;\n\t\t\t\t\n\t\t\t\tb.Append(s, appendFrom, start - appendFrom); appendFrom = end;\n\t\t\t\t\n\t\t\t\t//for each line that has source file info\n\t\t\t\tbool modified = false;\n\t\t\t\ts_rxAtLine ??= new regexp(@\"(?m)^   at (.+?\\)) in ([^\\r\\n<]+):line (\\d+)$\");\n\t\t\t\tforeach (var m in s_rxAtLine.FindAll(s, start..end)) {\n\t\t\t\t\tvar f = App.Model?.FindByFilePath(m[2].Value); if (f == null) continue;\n\t\t\t\t\tb.AppendFormat(\"{3}   at <open {0}|{1}>line {1}<> in <bc #FAFAD2>{2}<>{4}\", f.IdStringWithWorkspace, m[3].Value, f.Name, inLiteral ? \"</\\a>\" : \"\", inLiteral ? \"<\\a>\" : \"\");\n\t\t\t\t\tbool isMain = f.IsScript && 0 != s.Eq(m[1].Start, false, \"Program.<Main>$(String[] args)\", \"Program..ctor(String[] args)\", \"Script..ctor(String[] args)\");\n\t\t\t\t\tif (!isMain) {\n\t\t\t\t\t\tvar method = m[1].Value;\n\t\t\t\t\t\tbool lit = !inLiteral && method.Contains('<');\n\t\t\t\t\t\tb.AppendFormat(\"{1}, {0}{2}\", method, lit ? \"<\\a>\" : \"\", lit ? \"</\\a>\" : \"\");\n\t\t\t\t\t}\n\t\t\t\t\tb.AppendLine();\n\t\t\t\t\tmodified = true;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (modified) { //append `<fold>raw stack trace</fold>`\n\t\t\t\t\tif (b[b.Length - 1] != '\\n') b.AppendLine();\n\t\t\t\t\tif (inLiteral) b.Append(\"</\\a>\");\n\t\t\t\t\tb.Append(\"   <fold><\\a>   --- Raw stack trace ---\\r\\n\").Append(s, start, end - start);\n\t\t\t\t\tif (b[b.Length - 1] != '\\n') b.AppendLine();\n\t\t\t\t\tb.Append(\"</\\a></fold>\");\n\t\t\t\t\tif (inLiteral) b.Append(\"<\\a>\");\n\t\t\t\t} else { //append raw stack trace\n\t\t\t\t\tif (!inLiteral) b.Append(\"<\\a>\");\n\t\t\t\t\tb.Append(s, start, end - start);\n\t\t\t\t\tif (!inLiteral) b.Append(\"</\\a>\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (appendFrom > 0) {\n\t\t\t\tb.Append(s, appendFrom, s.Length - appendFrom);\n\t\t\t\tif (!wasFormatted) b.Append(\"</\\a>\");\n\t\t\t\t//print.qm2.write(\"+\" + b);\n\t\t\t\tpsm.Text = b.ToString();\n\t\t\t}\n\t\t\tif (_sb.Capacity > 10_000) _sb = null; //let GC free it. Usually < 4000.\n\t\t}\n\t\tstatic regexp s_rxStack, s_rxClosingTags, s_rxAtLine, s_rxLiteral;\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Panels/PanelRead.cs",
    "content": "extern alias CAW;\n\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Media;\nusing Microsoft.Win32;\nusing System.Runtime.Loader;\nusing Au.Controls;\nusing Microsoft.Web.WebView2.Wpf;\nusing Microsoft.Web.WebView2.Core;\nusing System.Text.Json.Nodes;\n\nnamespace LA;\n\nclass PanelRead {\n\tWebView2 _wv;\n\tstring _localBaseUri;\n\t\n\t//public PanelRead() {\n\t//\t//P.UiaSetName(\"Read panel\"); //no UIA element for Panel\n\t//}\n\t\n\tpublic Grid P { get; } = new();\n\t\n\tvoid _BeforeOpeningArticle() {\n\t\tvar p = Panels.PanelManager[P];\n\t\tp.Visible = true;\n\t\tif (p.Floating) {\n\t\t\twnd w = p.Content.Hwnd(), wmain = App.Hmain;\n\t\t\tif (w.IsMinimized) {\n\t\t\t\tw.ShowNotMinimized();\n\t\t\t} else if (!w.IsOwnedBy(wmain, 0) && !w.IsTopmost) {\n\t\t\t\tif (w.ClientRectInScreen.IntersectsWith(wmain.ClientRectInScreen)) w.ActivateL(true);\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Opens a LA documentation page in this panel.\n\t/// Opens the local version of the page.\n\t/// If cannot open (eg WebView2 unavailable on current computer), opens the specified URL in default web browser app.\n\t/// </summary>\n\t/// <param name=\"url\">Like <c>\"https://www.libreautomate.com/articles/name.html\"</c>.</param>\n\tpublic void OpenDocUrl(string url) {\n\t\tvar baseUri = DocsHttpServer.LocalBaseUri;\n\t\tif (baseUri == null || !_IsDocsUrl(url, c_laWebsiteBaseUri) || !_IsWebViewAvailable) {\n\t\t\trun.itSafe(url);\n\t\t\treturn;\n\t\t}\n\t\t_localBaseUri = baseUri; //save because DocsHttpServer.LocalBaseUri may become null\n\t\turl = string.Concat(baseUri, url.AsSpan(30));\n\t\t\n\t\t_BeforeOpeningArticle();\n\t\tif (_wv == null) _CreateWebView();\n\t\t_wv.Source = new(url);\n\t}\n\t\n\tconst string c_laWebsiteBaseUri = \"https://www.libreautomate.com/\";\n\t\n\tstatic bool _IsDocsUrl(string url, string baseUri)\n\t\t=> url.Starts(baseUri) && url.Eq(baseUri.Length, false, \"api\", \"editor\", \"articles\", \"cookbook\") > 0;\n\t\n\tstatic bool _IsWebViewAvailable {\n\t\tget {\n\t\t\tif (s_wvAvailable == null) {\n\t\t\t\ttry {\n\t\t\t\t\tCoreWebView2Environment.SetLoaderDllFolderPath(folders.ThisAppBS + $@\"runtimes\\win-{(osVersion.isArm64Process ? \"arm64\" : \"x64\")}\\native\"); //else would fail or use PATH etc\n\t\t\t\t\t\n\t\t\t\t\tstring ver = CoreWebView2Environment.GetAvailableBrowserVersionString();\n\t\t\t\t\ts_wvAvailable = !ver.NE();\n\t\t\t\t}\n\t\t\t\tcatch { s_wvAvailable = false; }\n\t\t\t\tif (s_wvAvailable == false) print.it(\"<>Info: To show documentation in <b>Read<> panel, download/install <google WebView2 site:microsoft.com>WebView2<>. Then restart this app. See <b>Options > Other<>.\");\n\t\t\t}\n\t\t\treturn s_wvAvailable == true;\n\t\t}\n\t}\n\tstatic bool? s_wvAvailable;\n\t\n\tvoid _CreateWebView() {\n\t\tvar tb = Panels.Help.buttons_.toolbar.ToolBars[0];\n\t\tPanels.Help.buttons_.back = tb.AddButton(\"*EvaIcons.ArrowBack\" + EdIcons.black, _ => { try { _wv.GoBack(); } catch { } }, \"Back\", enabled: false);\n\t\tPanels.Help.buttons_.forward = tb.AddButton(\"*EvaIcons.ArrowForward\" + EdIcons.black, _ => { try { _wv.GoForward(); } catch { } }, \"Forward\", enabled: false);\n\t\tPanels.Help.buttons_.openInBrowser = tb.AddButton(\"*Modern.Browser\" + EdIcons.black, _ => { _OpenInWebBrowser(); }, \"Open in web browser\", enabled: false);\n\t\ttb.Items.Add(new Separator());\n\t\tPanels.Help.buttons_.toggleReadPanel = tb.AddButton(\"*PixelartIcons.Article\" + EdIcons.black, _ => { Panels.PanelManager[P].Visible ^= true; }, \"Show/hide the Read panel\");\n\t\t\n\t\tstring udf = folders.ThisAppTemp + \"wv\";\n\t\tfilesystem.delete(udf, FDFlags.CanFail); //would fail to init if it's corrupt. And it grows.\n\t\t_wv = new() {\n\t\t\tCreationProperties = new() { UserDataFolder = udf }\n\t\t};\n\t\tP.Children.Add(_wv);\n\t\t\n\t\t_wv.CoreWebView2InitializationCompleted += (_, e) => {\n\t\t\tif (!e.IsSuccess) {\n\t\t\t\tprint.warning(\"Failed to initialize WebView2. Can't show LA documentation in the Read panel. See <+options Other>Options > Other<>. \" + e.InitializationException);\n\t\t\t\ts_wvAvailable = false;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\tPanels.Help.buttons_.toolbar.Visibility = Visibility.Visible;\n\t\t\t\n\t\t\tvar core = _wv.CoreWebView2;\n\t\t\t\n\t\t\tDebug.Assert(!core.Environment.UserDataFolder.Starts(folders.ThisApp)); //ignores CreationProperties if it was set too early\n\t\t\t\n\t\t\tcore.HistoryChanged += (_, _) => {\n\t\t\t\tPanels.Help.buttons_.back.IsEnabled = _wv.CanGoBack;\n\t\t\t\tPanels.Help.buttons_.forward.IsEnabled = _wv.CanGoForward;\n\t\t\t\tPanels.Help.buttons_.openInBrowser.IsEnabled = true;\n\t\t\t};\n\t\t\t\n\t\t\tcore.NavigationStarting += (_, e) => {\n\t\t\t\tvar url = e.Uri;\n\t\t\t\tif (url.Starts(\"nuget:\")) {\n\t\t\t\t\te.Cancel = true;\n\t\t\t\t\tDNuget.ShowSingle(Uri.UnescapeDataString(url[6..]));\n\t\t\t\t} else if (!_IsDocsUrl(url, _localBaseUri)) {\n\t\t\t\t\te.Cancel = true;\n\t\t\t\t\trun.itSafe(url);\n\t\t\t\t}\n\t\t\t};\n\t\t\t\n\t\t\tcore.ContextMenuRequested += _ContextMenuRequested;\n\t\t\t\n#if DEBUG\n\t\t\t_PreviewCurrentRecipeScript();\n#endif\n\t\t};\n\t}\n\t\n\t/// <summary>\n\t/// Opens the currently displayed page in the default web browser app.\n\t/// </summary>\n\tvoid _OpenInWebBrowser() {\n\t\tif (_wv.Source is { } uri) {\n\t\t\tvar url = uri.ToString();\n\t\t\tif (_IsDocsUrl(url, _localBaseUri)) url = c_laWebsiteBaseUri + url[_localBaseUri.Length..];\n\t\t\trun.itSafe(url);\n\t\t}\n\t}\n\t\n\tasync void _ContextMenuRequested(object sender, CoreWebView2ContextMenuRequestedEventArgs e) {\n\t\tvar defer = e.GetDeferral();\n\t\ttry {\n\t\t\tvar items = e.MenuItems;\n\t\t\t\n\t\t\tfor (int i = items.Count; --i >= 0;) {\n\t\t\t\tif (items[i].Kind == CoreWebView2ContextMenuItemKind.Command && items[i].Name is \"inspectElement\" or \"webCapture\" or \"webSelect\" or \"createQrCode\")\n\t\t\t\t\titems.RemoveAt(i);\n\t\t\t\t//if need Inspect, Ctrl+Shit+I still works\n\t\t\t}\n\t\t\t\n\t\t\tvar core = _wv.CoreWebView2;\n\t\t\t\n\t\t\t//detect <pre> and get its text\n\t\t\tstring preText = null;\n\t\t\tvar cmt = e.ContextMenuTarget;\n\t\t\tif (cmt.Kind == CoreWebView2ContextMenuTargetKind.Page && !(cmt.HasLinkUri || cmt.IsEditable)) {\n\t\t\t\tstring js = $$\"\"\"\n(function() {\n    var el = document.elementFromPoint({{e.Location.X}}, {{e.Location.Y}});\n    while(el && el.nodeType === 1) {\n        if(el.tagName === 'PRE') return el.textContent;\n        el = el.parentElement;\n    }\n    return null;\n})();\n\"\"\";\n\t\t\t\ttry {\n\t\t\t\t\tpreText = await core.ExecuteScriptAsync(js);\n\t\t\t\t\tif (preText != null) preText = (string)JsonValue.Parse(preText);\n\t\t\t\t}\n\t\t\t\tcatch (Exception ex) { print.it(ex); }\n\t\t\t}\n\t\t\t\n\t\t\tvar mCC = core.Environment.CreateContextMenuItem(\"Copy code\", null, CoreWebView2ContextMenuItemKind.Command);\n\t\t\tvar mNS = core.Environment.CreateContextMenuItem(\"New script\", null, CoreWebView2ContextMenuItemKind.Command);\n\t\t\tif (preText != null) {\n\t\t\t\tmCC.CustomItemSelected += (_, __) => { clipboard.text = preText; };\n\t\t\t\tmNS.CustomItemSelected += (_, __) => {\n\t\t\t\t\tvar name = pathname.getNameNoExt(Uri.UnescapeDataString(_wv.Source.AbsolutePath));\n\t\t\t\t\tApp.Model.NewItem(\"Script.cs\", null, name + \".cs\", true, new(true, preText));\n\t\t\t\t};\n\t\t\t} else {\n\t\t\t\tmCC.IsEnabled = mNS.IsEnabled = false;\n\t\t\t}\n\t\t\titems.Insert(0, mCC);\n\t\t\titems.Insert(1, mNS);\n\t\t\ttry { items.Insert(2, core.Environment.CreateContextMenuItem(null, null, CoreWebView2ContextMenuItemKind.Separator)); } catch { } //throws on Win7\n\t\t\t\n\t\t\tvar mOB = core.Environment.CreateContextMenuItem(\"Open in browser\", null, CoreWebView2ContextMenuItemKind.Command);\n\t\t\tmOB.CustomItemSelected += (_, __) => { _OpenInWebBrowser(); };\n\t\t\ttry { items.Add(core.Environment.CreateContextMenuItem(null, null, CoreWebView2ContextMenuItemKind.Separator)); } catch { }\n\t\t\titems.Add(mOB);\n\t\t}\n\t\tfinally {\n\t\t\tdefer.Complete();\n\t\t}\n\t}\n\t\n#if DEBUG\n\t/// <summary>\n\t/// Starts a timer that displays HTML (preview) of the current C# script, if it's a cookbook recipe source script.\n\t/// It makes editing cookbook recipes easy.\n\t/// </summary>\n\tvoid _PreviewCurrentRecipeScript() {\n\t\tstring prevText = null;\n\t\tSciCode prevDoc = null;\n\t\t\n\t\tApp.Timer1sWhenVisible += () => {\n\t\t\tif (!P.IsVisible || DocsHttpServer.LocalBaseUri == null) return;\n\t\t\tif (App.Model.WorkspaceName != \"Cookbook\") {\n\t\t\t\tif (!(App.Model.WorkspaceName == \"ok\" && App.Model.CurrentFile is { IsExternal: true, IsScript: true } cf && cf.Ancestors().Any(o => o.Name is \"cookbook_files\"))) return;\n\t\t\t}\n\t\t\tvar doc = Panels.Editor.ActiveDoc;\n\t\t\tif (doc == null || !doc.EFile.IsScript || doc.EFile.Parent.Name == \"-\") return;\n\t\t\t\n\t\t\tstring text = doc.aaaText;\n\t\t\tif (text == prevText) return;\n\t\t\tif (_IsCaretInPossiblyUnfinishedTag(doc, text)) return; //avoid printing debug info for invalid links etc\n\t\t\t\n\t\t\tif (_wv.Source.AbsolutePath != \"/cookbook/preview.html\") {\n\t\t\t\tprevText = null;\n\t\t\t\tprevDoc = doc;\n\t\t\t\t_wv.Source = new(DocsHttpServer.LocalBaseUri + \"cookbook/preview.html\");\n\t\t\t\t//DocsHttpServer will call GetPreviewHtmlTemplate_() and the control will display it (empty page).\n\t\t\t\t//In next timer call (the `else` code) its <article>'s inner HTML will be replaced with the HTML returned by _GetPreviewHtml.\n\t\t\t} else {\n\t\t\t\tvar html = _GetPreviewHtml();\n\t\t\t\tvar json = JsonValue.Create(html).ToJsonString();\n\t\t\t\t_wv.CoreWebView2.ExecuteScriptAsync($\"window.updatePreview({json});{(doc != prevDoc ? \"scrollToTop()\" : null)}\");\n\t\t\t\tprevDoc = doc;\n\t\t\t\tprevText = text;\n\t\t\t}\n\t\t};\n\t\t\n\t\tbool _IsCaretInPossiblyUnfinishedTag(SciCode doc, string text) {\n\t\t\tint i = doc.aaaCurrentPos16;\n\t\t\ti = i == 0 ? -1 : text.LastIndexOfAny(['<', '>', '\\n'], i - 1);\n\t\t\tif (i > 0 && text[i] == '<' && text.Eq(text.LastIndexOf('\\n', i) + 1, \"///\")) {\n\t\t\t\t//print.it(\"tag\");\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Converts text of the current C# script to HTML. It must be a cookbook recipe source script.\n\t/// Uses the same converter as the \"Au docs\" script (it creates HTML etc files for the website).\n\t/// </summary>\n\tstatic string _GetPreviewHtml() {\n\t\ttry {\n\t\t\tvar doc = Panels.Editor.ActiveDoc;\n\t\t\tstring name = doc.EFile.DisplayName, code = doc.Dispatcher.Invoke(() => doc.aaaText);\n\t\t\t\n\t\t\tif (_RecipeCodeToHtml == null) {\n\t\t\t\tAssemblyLoadContext.Default.LoadFromAssemblyPath(@\"C:\\code\\ok\\.nuget\\-\\Markdig.dll\");\n\t\t\t\tvar asm = AssemblyLoadContext.Default.LoadFromAssemblyPath(@\"C:\\code\\ok\\dll\\AuDocsLib.dll\");\n\t\t\t\t_RecipeCodeToHtml = asm.GetType(\"ADL.AuDocsShared\").GetMethod(\"RecipeCodeToHtml\").CreateDelegate<Func<string, string, string>>();\n\t\t\t}\n\t\t\treturn _RecipeCodeToHtml(name, code);\n\t\t}\n\t\tcatch (Exception ex) { print.it(ex); throw; }\n\t}\n\t\n\tstatic Func<string, string, string> _RecipeCodeToHtml;\n\t\n\t/// <summary>\n\t/// Called by the HTTP server. The code is here because it's better to keep most of the preview code in one place.\n\t/// </summary>\n\tinternal static string GetPreviewHtmlTemplate_() {\n\t\treturn \"\"\"\n<!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"utf-8\">\n      <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge,chrome=1\">\n      <title>Constants, enum | LibreAutomate </title>\n      <meta name=\"viewport\" content=\"width=device-width\">\n      <link rel=\"stylesheet\" href=\"../styles/docfx.vendor.min.css\">\n      <link rel=\"stylesheet\" href=\"../styles/docfx.css\">\n      <link rel=\"stylesheet\" href=\"../styles/main.css\">\n      <link rel=\"stylesheet\" href=\"../styles/code.css\">\n  <link rel=\"stylesheet\" href=\"../styles/la.css\">\n</head>\n  <body>\n    <a name=\"top\"></a>\n    <div id=\"wrapper\">\n      <div role=\"main\" class=\"container-fluid body-content hide-when-search\">\n          <div class=\"col-md-12\">\n            <article class=\"content wrap\" id=\"_content\"/>\n          </div>\n      </div>\n    </div>\n<script>\nwindow.updatePreview = function(html) {\n    document.getElementById('_content').innerHTML = html;\n};\nwindow.scrollToTop = function() {\n    window.scrollTo(0, 0);\n};\n</script>\n</body>\n</html>\n\"\"\";\n\t}\n#endif\n}\n\n/// <summary>\n/// Used by the Read panel. Retrieves local LA documentation files.\n/// Probably it's the best way to display local HTML files. Other ways have problems.\n/// </summary>\nclass DocsHttpServer : HttpServerSession {\n\tstatic bool s_running;\n\tstatic int s_port;\n\t\n\tpublic static void StartOrSwitch() {\n\t\tif (App.Settings.doc_web || s_running) {\n\t\t\t_Switch();\n\t\t} else {\n\t\t\ts_running = true;\n\t\t\trun.thread(() => {\n\t\t\t\ttry {\n\t\t\t\t\tListen<DocsHttpServer>(0, \"127.0.0.1\", listener => {\n\t\t\t\t\t\ts_port = ((System.Net.IPEndPoint)listener.LocalEndpoint).Port;\n\t\t\t\t\t\t_Switch();\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tcatch (Exception ex) {\n\t\t\t\t\tprint.warning(ex);\n\t\t\t\t}\n\t\t\t\tApp.Settings.doc_web = true;\n\t\t\t\t_Switch();\n\t\t\t\ts_running = false;\n\t\t\t}, sta: false);\n\t\t}\n\t}\n\t\n\tpublic static string LocalBaseUri { get; private set; }\n\t\n\tstatic void _Switch() {\n\t\tif (App.Settings.doc_web) {\n\t\t\tLocalBaseUri = null;\n\t\t\tHelpUtil.AuHelpEvent_ -= _AuHelpEvent;\n\t\t} else {\n\t\t\tLocalBaseUri = $\"http://127.0.0.1:{s_port}/\";\n\t\t\tHelpUtil.AuHelpEvent_ += _AuHelpEvent;\n\t\t}\n\t\t\n\t\tstatic void _AuHelpEvent(HelpUtil.AuHelpEventArgs_ e) {\n\t\t\te.Cancel = true;\n\t\t\tPanels.Read.OpenDocUrl(e.Url);\n\t\t}\n\t}\n\t\n\tprotected override void MessageReceived(HSMessage m, HSResponse r) {\n\t\tif (m.Method != \"GET\") { r.Status = System.Net.HttpStatusCode.MethodNotAllowed; return; }\n\t\t\n\t\tvar path = m.TargetPath; if (path.Ends('/')) path += \"index.html\";\n\t\tpath = path[1..];\n\t\t//print.it(path);\n\t\t\n#if DEBUG\n\t\tif (path == \"cookbook/preview.html\") {\n\t\t\tr.SetContentText(PanelRead.GetPreviewHtmlTemplate_(), \"text/html; charset=utf-8\");\n\t\t\treturn;\n\t\t}\n\t\t//this can be used when editing/testing a CSS file\n\t\t//if (path == \"styles/docfx.vendor.min.css\") {\n\t\t//\tr.SetContentText(filesystem.loadText(@\"C:\\Temp\\Au\\DocFX\\site\\styles\\docfx.vendor.min.css\"), \"text/css\");\n\t\t//\tr.Headers[\"Cache-Control\"] = \"no-cache, no-store, must-revalidate\";\n\t\t//\treturn;\n\t\t//}\n#endif\n\t\t\n\t\tif (path == \"favicon.ico\" || path.Ends(\"/com.chrome.devtools.json\")) { r.Status = System.Net.HttpStatusCode.NotFound; return; }\n\t\t\n\t\tlock (typeof(DocsHttpServer)) { //faster than the SQLite's busy timeout implementation\n\t\t\tusing var db = new sqlite(folders.ThisAppBS + \"doc-html.db\", SLFlags.SQLITE_OPEN_READONLY); //fast, don't keep open\n\t\t\tif (db.Get(out byte[] content, \"SELECT text FROM doc WHERE name=?\", path)) {\n\t\t\t\tr.Content = content;\n\t\t\t\tvar ext = pathname.getExtension(path).Lower();\n\t\t\t\tvar ct = ext switch {\n\t\t\t\t\t\".html\" => \"text/html; charset=utf-8\",\n\t\t\t\t\t\".css\" => \"text/css\",\n\t\t\t\t\t\".js\" => \"text/javascript\",\n\t\t\t\t\t\".png\" => \"image/png\",\n\t\t\t\t\t_ => null\n\t\t\t\t};\n\t\t\t\tif (ct != null) r.Headers[\"Content-Type\"] = ct; else Debug_.PrintIf(!(ext is \".eot\"), path);\n\t\t\t\tif (ext != \".html\") r.Headers[\"Cache-Control\"] = \"max-age=86400\"; //1 day or until process exit\n\t\t\t} else {\n\t\t\t\tDebug_.PrintIf(\n\t\t\t\t\t!(path is \"styles/docfx.vendor.min.css.map\" /*size ~500kb, requested when showing devtools, works well without*/),\n\t\t\t\t\t\"NOT FOUND: \" + path);\n\t\t\t\tr.Status = System.Net.HttpStatusCode.NotFound;\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Panels/PanelTasks.cs",
    "content": "using Au.Controls;\nusing System.Windows.Controls;\nusing System.Windows.Input;\n\nnamespace LA;\n\nclass PanelTasks {\n\tKTreeView _tv;\n\tbool _updatedOnce;\n\n\tpublic PanelTasks() {\n\t\t//P.UiaSetName(\"Tasks panel\"); //no UIA element for Panel\n\n\t\t_tv = new KTreeView { Name = \"Tasks_list\" };\n\t\tP.Children.Add(_tv);\n\t\t\n\t\tPanels.PanelManager[\"Tasks\"].DontActivateFloating = e => true;\n\t}\n\n\tpublic DockPanel P { get; } = new();\n\n\tpublic void UpdateList() {\n\t\t_tv.SetItems(App.Tasks.Items, _updatedOnce);\n\t\tif (!_updatedOnce) {\n\t\t\t_updatedOnce = true;\n\t\t\tFilesModel.NeedRedraw += v => { _tv.Redraw(v.remeasure); };\n\t\t\t_tv.ItemClick += _tv_ItemClick;\n\t\t}\n\t}\n\n\tprivate void _tv_ItemClick(TVItemEventArgs e) {\n\t\tif (e.Mod != 0 || e.ClickCount != 1) return;\n\t\tvar t = e.Item as RunningTask;\n\t\tvar f = t.f;\n\t\tswitch (e.Button) {\n\t\tcase MouseButton.Left:\n\t\t\tApp.Model.SetCurrentFile(f);\n\t\t\tbreak;\n\t\tcase MouseButton.Right:\n\t\t\t_tv.Select(t);\n\t\t\tvar name = f.DisplayName;\n\t\t\tvar m = new popupMenu { RawText = true };\n\t\t\tm[\"End task  '\" + name + \"'\"] = _ => App.Tasks.EndTask(t);\n\t\t\tm[\"End all  '\" + name + \"'\"] = _ => App.Tasks.EndTasksOf(f);\n\t\t\tm.Separator();\n\t\t\tm[\"Attach debugger\"] = _ => Panels.Debug.Attach(t.processId);\n\t\t\tm.Separator();\n\t\t\tm[\"Close\\tM-click\", disable: f.OpenDoc == null] = _ => App.Model.CloseFile(f, selectOther: true);\n\t\t\t//m.Separator();\n\t\t\t//m[\"Recent tasks and triggers...\"] = _ => RecentTT.Show(); //rejected. It is in menu Run. Or would also need to show context menu when rclicked in empty space.\n\t\t\tm.Show();\n\t\t\tbreak;\n\t\tcase MouseButton.Middle:\n\t\t\tApp.Model.CloseFile(f, selectOther: true);\n\t\t\tbreak;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Panels/Panels.cs",
    "content": "using Au.Controls;\nusing System.Windows.Controls;\nusing System.Windows;\nusing System.Windows.Input;\nusing System.Xml.Linq;\nusing System.Xml.XPath;\nusing System.Windows.Media;\n\nnamespace LA;\n\nstatic class Panels {\n\tpublic static KPanels PanelManager;\n\t//panels\n\tpublic static PanelEdit Editor;\n\tpublic static PanelFiles Files;\n\tpublic static PanelOutline Outline;\n\tpublic static PanelHelp Help;\n\tpublic static PanelOpen Open;\n\tpublic static PanelTasks Tasks;\n\tpublic static PanelOutput Output;\n\tpublic static PanelFind Find;\n\tpublic static PanelFound Found;\n\tpublic static PanelMouse Mouse;\n\tpublic static PanelRead Read;\n\tpublic static PanelBookmarks Bookmarks;\n\tpublic static PanelBreakpoints Breakpoints;\n\tpublic static PanelDebug Debug;\n\t//menu and toolbars\n\tpublic static Menu Menu;\n\tpublic static ToolBar TFile, TEdit, TRun, TTools, TCustom1, TCustom2;\n\t\n\tpublic static void LoadAndCreateToolbars() {\n\t\tvar pm = PanelManager = new KPanels();\n\t\t\n\t\tvar customLayoutPath = AppSettings.DirBS + \"Layout.xml\";\n\t\tif (filesystem.exists(customLayoutPath).File) {\n\t\t\ttry {\n\t\t\t\tvar x = XmlUtil.LoadElem(customLayoutPath);\n\t\t\t\tif (x.XPathSelectElement(\"//panel[@name='Outline']\") == null) { //v0.4 added several new panels etc, and users would not know the best place for them, or even how to move\n\t\t\t\t\tfilesystem.delete(customLayoutPath, FDFlags.RecycleBin);\n\t\t\t\t\tprint.it(\"Info: The window layout has been reset, because several new panels have been added in this app version.\\r\\n\\tIf you want to undo it: 1. Exit the program. 2. Restore file Layout.xml from the Recycle Bin (replace the existing file). 3. Run the program. 4. Move panels from the bottom of the window to a better place.\");\n\t\t\t\t} else if (x.XPathSelectElement(\"//panel[@name='Help']\") == null) { //in v1.15 renamed some panels and removed the Help toolbar\n\t\t\t\t\tx.XPathSelectElement(\"//toolbar[@name='Help']\")?.Remove();\n\t\t\t\t\tx.XPathSelectElement(\"//panel[@name='Cookbook']\")?.SetAttributeValue(\"name\", \"Help\");\n\t\t\t\t\tx.XPathSelectElement(\"//panel[@name='Recipe']\")?.SetAttributeValue(\"name\", \"Read\");\n\t\t\t\t\t\n\t\t\t\t\t//also renamed some attributes\n\t\t\t\t\tforeach (var v in x.Descendants()) {\n\t\t\t\t\t\tif (v.Attribute(\"captionAt\") is { } k) { v.SetAttributeValue(\"headerAt\", k.Value); k.Remove(); }\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tx.SaveElem(customLayoutPath);\n\t\t\t\t\t\n\t\t\t\t\t//also remove the Help toolbar from toolbar customizations\n\t\t\t\t\tvar customCommandsPath = AppSettings.DirBS + \"Commands.xml\";\n\t\t\t\t\tif (filesystem.exists(customCommandsPath).File) {\n\t\t\t\t\t\tvar xc = XmlUtil.LoadElem(customCommandsPath);\n\t\t\t\t\t\tif (xc.XPathSelectElement(\"//Help\") is { } x1) {\n\t\t\t\t\t\t\tx1.Remove();\n\t\t\t\t\t\t\txc.Save(customCommandsPath);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception e1) { Debug_.Print(e1); }\n\t\t}\n\t\t\n\t\tpm.BorderBrush = SystemColors.ActiveBorderBrush;\n\t\t//pm.Load(folders.ThisAppBS + @\"Default\\Layout.xml\", null);\n\t\tpm.Load(folders.ThisAppBS + @\"Default\\Layout.xml\", customLayoutPath);\n\t\t\n\t\tint saveCounter = 0;\n\t\tApp.Timer1sWhenVisible += () => {\n\t\t\tif (++saveCounter >= 60) {\n\t\t\t\tsaveCounter = 0;\n\t\t\t\tpm.Save();\n\t\t\t}\n\t\t};\n\t\t\n\t\tpm[\"Menu\"].Content = Menu = new Menu();\n\t\tTFile = _CreateToolbar(\"File\");\n\t\tTEdit = _CreateToolbar(\"Edit\");\n\t\tTRun = _CreateToolbar(\"Run\");\n\t\tTTools = _CreateToolbar(\"Tools\");\n\t\tTCustom1 = _CreateToolbar(\"Custom1\");\n\t\tTCustom2 = _CreateToolbar(\"Custom2\");\n\t}\n\t\n\tstatic ToolBar _CreateToolbar(string name, Func<DockPanel, Dock> dockPanel = null) {\n\t\tvar c = new ToolBar { Name = name/*, Background = SystemColors.ControlBrush*/ };\n\t\tc.UiaSetName(name);\n\t\tc.HideGripAndOverflow(false);\n\t\tvar tt = new ToolBarTray { IsLocked = true/*, Background = SystemColors.ControlBrush*/ }; //because ToolBar looks bad if parent is not ToolBarTray\n\t\ttt.ToolBars.Add(c);\n\t\tFrameworkElement content = tt;\n\t\tif (dockPanel != null) {\n\t\t\tvar p = new DockPanel { Background = tt.Background };\n\t\t\tp.Children.Add(tt);\n\t\t\tDockPanel.SetDock(tt, dockPanel(p));\n\t\t\tcontent = p;\n\t\t}\n\t\tPanelManager[name].Content = content;\n\t\t\n\t\tc.ContextMenuOpening += DCustomize.ToolbarContextMenuOpening;\n\t\tc.PreviewMouseRightButtonDown += (o, e) => { //prevent closing the overflow panel on right mouse button down\n\t\t\tif ((e.OriginalSource as UIElement).VisualAncestors(true).Any(o => o is System.Windows.Controls.Primitives.ToolBarOverflowPanel)) e.Handled = true;\n\t\t};\n\t\t\n\t\treturn c;\n\t}\n\t\n\tpublic static void CreatePanels() {\n\t\tvar pm = PanelManager;\n\t\t\n\t\tKPanels.ILeaf _AddDontFocus(string panel, FrameworkElement content) {\n\t\t\tvar p = pm[panel];\n\t\t\tp.Content = content;\n\t\t\tp.DontFocusTab = () => {\n\t\t\t\tvar doc = Panels.Editor.ActiveDoc;\n\t\t\t\tif (doc != null) doc.Focus(); else Keyboard.ClearFocus();\n\t\t\t};\n\t\t\treturn p;\n\t\t}\n\t\t\n\t\tpm[\"documents\"].Content = (Editor = new()).P;\n\t\t\n\t\tpm[\"Files\"].Content = (Files = new()).P;\n\t\t_AddDontFocus(\"Outline\", (Outline = new()).P);\n\t\tpm[\"Help\"].Content = (Help = new()).P;\n\t\t_AddDontFocus(\"Debug\", (Debug = new()).P);\n\t\t_AddDontFocus(\"Open\", (Open = new()).P);\n\t\t_AddDontFocus(\"Tasks\", (Tasks = new()).P);\n\t\tpm[\"Find\"].Content = (Find = new()).P;\n\t\t_AddDontFocus(\"Bookmarks\", (Bookmarks = new()).P);\n\t\t_AddDontFocus(\"Breakpoints\", (Breakpoints = new()).P);\n\t\t_AddDontFocus(\"Output\", (Output = new()).P);\n\t\t_AddDontFocus(\"Mouse\", (Mouse = new()).P);\n\t\t_AddDontFocus(\"Found\", (Found = new()).P);\n\t\tvar pRead = _AddDontFocus(\"Read\", (Read = new()).P);\n\t\tpRead.Visible = false;\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Properties/launchSettings.json",
    "content": "{\n  \"profiles\": {\n    \"Au.Editor\": {\n      \"commandName\": \"Project\",\n      \"nativeDebugging\": false\n    }\n  }\n}"
  },
  {
    "path": "Au.Editor/Test.cs",
    "content": "#if DEBUG || IDE_LA\nextern alias CAW;\n\nusing Microsoft.CodeAnalysis;\nusing CAW::Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp;\nusing Microsoft.CodeAnalysis.CSharp.Syntax;\nusing Microsoft.CodeAnalysis.Text;\nusing Microsoft.CodeAnalysis.Shared.Extensions;\nusing CAW::Microsoft.CodeAnalysis.Shared.Extensions;\nusing Microsoft.CodeAnalysis.CSharp.Extensions;\nusing Microsoft.CodeAnalysis.Shared.Utilities;\nusing CAW::Microsoft.CodeAnalysis.Shared.Utilities;\nusing CAW::Microsoft.CodeAnalysis.FindSymbols;\n\nusing Au.Triggers;\nusing Au.Controls;\nusing System.Windows.Controls;\n//using System.Windows.Forms;\nusing System.Text.RegularExpressions;\nusing System.Diagnostics.CodeAnalysis;\n\nusing static Au.Controls.Sci;\n\nnamespace LA;\n\nstatic class Test {\n\t/// <summary>\n\t/// \n\t/// </summary>\n\tpublic static void FromMenubar() {\n\t\t//print.clear();\n\t\t\n\t\tprint.it(Panels.Editor.ActiveDoc.aaaCurrentPos16);\n\t\t\n\t\t//var query = Panels.Editor.ActiveDoc.aaaText.Lines()[0];\n\t\t//var query = Panels.Editor.ActiveDoc.aaaText;\n\t\t//Task.Run(() => {\n\t\t//\ttry {\n\t\t//\t\tMcpTools _tools = new();\n\t\t//\t\tvar s = _tools.find_la_docs(query, \"\");\n\t\t//\t\tprint.scrollToTop();\n\t\t//\t}\n\t\t//\tcatch (Exception ex) { print.it(ex); }\n\t\t//});\n\t\t//print.it(s);\n\t\t\n\t\t//timer2.every(500, _=> { GC.Collect(); });\n\t\t\n\t\t//Cpp.Cpp_Test();\n\t\t\n#if !IDE_LA\n#endif\n\t}\n\t\n\tpublic static void MonitorGC() {\n\t\t//if(!s_debug2) {\n\t\t//\ts_debug2 = true;\n\t\t//\tnew TestGC();\n\t\t\n\t\t//\t//timer.every(50, _ => {\n\t\t//\t//\tif(!s_debug) {\n\t\t//\t//\t\ts_debug = true;\n\t\t//\t//\t\ttimer.after(100, _ => new TestGC());\n\t\t//\t//\t}\n\t\t//\t//});\n\t\t//}\n\t}\n\t//static bool s_debug2;\n\t\n\tclass TestGC {\n\t\t~TestGC() {\n\t\t\tif (Environment.HasShutdownStarted) return;\n\t\t\tif (AppDomain.CurrentDomain.IsFinalizingForUnload()) return;\n\t\t\tprint.it(\"GC\", GC.CollectionCount(0), GC.CollectionCount(1), GC.CollectionCount(2));\n\t\t\t//timer.after(1, _ => new TestGC());\n\t\t\t//var f = App.Wmain; if(!f.IsHandleCreated) return;\n\t\t\t//f.BeginInvoke(new Action(() => new TestGC()));\n\t\t\tnew TestGC();\n\t\t}\n\t}\n}\n#endif\n"
  },
  {
    "path": "Au.Editor/Tools/CapturingWithHotkey.cs",
    "content": "using Au.Controls;\nusing System.Windows.Interop;\nusing System.Windows;\nusing System.Windows.Controls;\n\nnamespace ToolLand;\n\nstatic partial class TUtil {\n\t/// <summary>\n\t/// Common code for tools that capture UI objects with F3.\n\t/// </summary>\n\tpublic class CapturingWithHotkey {\n\t\tpublic record struct Hothey(string hotkey, Action a);\n\t\t\n\t\treadonly bool _isElm;\n\t\treadonly wnd _wDialog;\n\t\treadonly KCheckBox _captureCheckbox;\n\t\treadonly Func<GetRectArgs, bool> _getRect;\n\t\treadonly Hothey _hkCapture, _hkInsert, _hkSmaller;\n\t\tHwndSource _hs;\n\t\ttimer _timer;\n\t\tosdRect _osr;\n\t\tosdText _ost; //TODO3: draw rect and text in same OsdWindow\n\t\tbool _capturing;\n\t\t\n\t\tconst string c_propName = \"Au.Capture\";\n\t\treadonly static int s_stopMessage = Api.RegisterWindowMessage(c_propName);\n\t\tconst int c_hotkeyCapture = 1623031890, c_hotkeyInsert = 1623031891, c_hotkeySmaller = 1623031892;\n\t\t\n\t\t/// <param name=\"captureCheckbox\">Checkbox that turns on/off capturing.</param>\n\t\t/// <param name=\"getRect\">Called to get rectangle of object from mouse. Receives mouse position. Can return default to hide the rectangle.</param>\n\t\tpublic CapturingWithHotkey(KCheckBox captureCheckbox, Func<GetRectArgs, bool> getRect, Hothey capture, Hothey insert = default, Hothey smaller = default) {\n\t\t\t_wDialog = captureCheckbox.Hwnd();\n\t\t\t_captureCheckbox = captureCheckbox;\n\t\t\t_getRect = getRect;\n\t\t\t_hkCapture = capture;\n\t\t\t_hkInsert = insert;\n\t\t\t_hkSmaller = smaller;\n\t\t\t_isElm = smaller.a != null;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Starts or stops capturing.\n\t\t/// Does nothing if already in that state.\n\t\t/// </summary>\n\t\tpublic bool Capturing {\n\t\t\tget => _capturing;\n\t\t\tset {\n\t\t\t\tif (value == _capturing) return;\n\t\t\t\tif (value) _StartCapturing(); else _StopCapturing();\n\t\t\t}\n\t\t}\n\t\t\n\t\tvoid _StopCapturing() {\n\t\t\t_capturing = false;\n\t\t\t_timer.Stop();\n\t\t\t_HideRect();\n\t\t\t_hs.RemoveHook(_WndProc);\n\t\t\tApi.UnregisterHotKey(_wDialog, c_hotkeyCapture);\n\t\t\tif (_hkInsert.hotkey != null) Api.UnregisterHotKey(_wDialog, c_hotkeyInsert);\n\t\t\tif (_hkSmaller.hotkey != null) Api.UnregisterHotKey(_wDialog, c_hotkeySmaller);\n\t\t\t_wDialog.Prop.Remove(c_propName);\n\t\t}\n\t\t\n\t\tvoid _StartCapturing() {\n\t\t\t//let other tools stop capturing. Any process.\n\t\t\twnd.find(null, \"HwndWrapper[*\", flags: WFlags.HiddenToo | WFlags.CloakedToo, also: o => {\n\t\t\t\tif (o != _wDialog && o.Prop[c_propName] == 1) o.SendTimeout(3000, out _, s_stopMessage);\n\t\t\t\treturn false;\n\t\t\t});\n\t\t\t_wDialog.Prop.Set(c_propName, 1);\n\t\t\t\n\t\t\t//register hotkeys\n\t\t\tbool _RegisterHotkey(int id, string hotkey) {\n\t\t\t\tstring es = null;\n\t\t\t\ttry {\n\t\t\t\t\tvar (mod, key) = RegisteredHotkey.Normalize_(hotkey);\n\t\t\t\t\tif (Api.RegisterHotKey(_wDialog, id, mod, key)) return true;\n\t\t\t\t\tes = \"Failed to register.\";\n\t\t\t\t}\n\t\t\t\tcatch (Exception e1) { es = e1.Message; }\n\t\t\t\tdialog.showError(\"Hotkey \" + hotkey, es + \"\\nClick the hotkey link to set another hotkey.\", owner: _wDialog);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (!_RegisterHotkey(c_hotkeyCapture, _hkCapture.hotkey)) return;\n\t\t\tif (_hkInsert.hotkey != null) _RegisterHotkey(c_hotkeyInsert, _hkInsert.hotkey);\n\t\t\tif (_hkSmaller.hotkey != null) _RegisterHotkey(c_hotkeySmaller, _hkSmaller.hotkey);\n\t\t\t\n\t\t\t//hook wndproc\n\t\t\tif (_hs == null) {\n\t\t\t\t_hs = PresentationSource.FromDependencyObject(_captureCheckbox) as HwndSource;\n\t\t\t\t_hs.Disposed += (_, _) => {\n\t\t\t\t\tCapturing = false;\n\t\t\t\t\t_osr?.Dispose();\n\t\t\t\t\t_ost?.Dispose();\n\t\t\t\t};\n\t\t\t}\n\t\t\t_hs.AddHook(_WndProc);\n\t\t\t\n\t\t\t//set timer to show rectangle of UI element from mouse\n\t\t\tif (_timer == null) {\n\t\t\t\t_osr = CreateOsdRect(2);\n\t\t\t\t_timer = new timer(_ => {\n\t\t\t\t\tif (!_capturing) return;\n\t\t\t\t\tPOINT p = mouse.xy;\n\t\t\t\t\tif (_isElm) {\n\t\t\t\t\t\t_elmTimer.Timer(p);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t_ShowRect(p, wnd.fromXY(p, WXYFlags.NeedWindow));\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (_isElm) _elmTimer = new(this);\n\t\t\t_timer.Every(_isElm ? 50 : 200);\n\t\t\t\n\t\t\t_capturing = true;\n\t\t}\n\t\t_ElmTimer _elmTimer;\n\t\t\n\t\tclass _ElmTimer {\n\t\t\tCapturingWithHotkey _capt;\n\t\t\tPOINT _pMM;\n\t\t\tbool _mouseMoved;\n\t\t\tlong _timeMM, _timeUpdated;\n\t\t\twnd _w;\n\t\t\t\n\t\t\tpublic _ElmTimer(CapturingWithHotkey capt) {\n\t\t\t\t_capt = capt;\n\t\t\t\t_pMM.x = int.MinValue;\n\t\t\t}\n\t\t\t\n\t\t\tpublic void Timer(POINT p) {\n\t\t\t\tif (mouse.isPressed()) {\n\t\t\t\t\t_capt._HideRect();\n\t\t\t\t\tif (p == _pMM) return;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t//get rect when mouse stops. With a delay if stops in a new window.\n\t\t\t\tif (p != _pMM) {\n\t\t\t\t\tif (_capt._osr.Visible && !_capt._osr.Rect.Contains(p)) _capt._HideRect();\n\t\t\t\t\t_pMM = p;\n\t\t\t\t\t_mouseMoved = true;\n\t\t\t\t\t_timeMM = Environment.TickCount64;\n\t\t\t\t} else {\n\t\t\t\t\twnd w = wnd.fromXY(p, WXYFlags.NeedWindow);\n\t\t\t\t\tbool sameWindow = !_w.Is0 && (w == _w || w.ThreadId == _w.ThreadId);\n\t\t\t\t\tbool update = false;\n\t\t\t\t\tbool idle = sameWindow && !_mouseMoved;\n\t\t\t\t\tif (idle) { //repeat _ShowRect anyway, because may scroll, close window, etc\n\t\t\t\t\t\tlong timeNow = Environment.TickCount64;\n\t\t\t\t\t\tlong liTime = Api.GetLastInputTime();\n\t\t\t\t\t\tif (timeNow - liTime > 700) {\n\t\t\t\t\t\t\tlong timeNotUpdated = timeNow - _timeUpdated;\n\t\t\t\t\t\t\tupdate = timeNotUpdated > 1000 || (liTime > _timeUpdated && timeNotUpdated > 300);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tupdate = sameWindow || Environment.TickCount64 - _timeMM >= 230;\n\t\t\t\t\t}\n\t\t\t\t\tif (update) {\n\t\t\t\t\t\t//print.it(\"update rect\");\n\t\t\t\t\t\tlong t1 = Environment.TickCount64;\n\t\t\t\t\t\t_capt._ShowRect(p, _w = w);\n\t\t\t\t\t\tlong t2 = Environment.TickCount64;\n\t\t\t\t\t\t_timeUpdated = t2 + (t2 - t1) * 2;\n\t\t\t\t\t}\n\t\t\t\t\t_mouseMoved = false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tvoid _ShowRect(POINT p, wnd w) {\n\t\t\tbool ok = false; RECT r = default; string text = null;\n\t\t\tif (!(w.Is0 || w == _wDialog || (w.IsOfThisThread && w.ZorderIsAbove(_wDialog)))) {\n\t\t\t\tvar k = new GetRectArgs(p, w);\n\t\t\t\tif (ok = _getRect(k)) (r, text) = (k.resultRect, k.resultText);\n\t\t\t\t\n\t\t\t\t//F3 does not work if this process has lower UAC IL than the foreground process.\n\t\t\t\t//\tNormally editor is admin, but if portable etc...\n\t\t\t\t//\tShift+F3 too. But Ctrl+F3 works.\n\t\t\t\t//if (w!=wPrev && w.IsActive) {\n\t\t\t\t//\tw = wPrev;\n\t\t\t\t//\tif(w.UacAccessDenied)print.it(\"F3 \");\n\t\t\t\t//}\n\t\t\t}\n\t\t\tif (ok && !t_hideCapturingRect) {\n\t\t\t\tr.Inflate(1, 1); //1 pixel inside, 1 outside\n\t\t\t\t_LimitInsaneRect(ref r);\n\t\t\t\t_osr.Rect = r;\n\t\t\t\t_osr.Show();\n\t\t\t\tif (!text.NE()) {\n\t\t\t\t\t_ost ??= new() { Font = new(8), Shadow = false, ShowMode = OsdMode.ThisThread, SecondsTimeout = -1 };\n\t\t\t\t\t_ost.Text = text;\n\t\t\t\t\tvar ro = _ost.Measure();\n\t\t\t\t\tvar rs = screen.of(r).Rect;\n\t\t\t\t\tint x = r.left, y = r.top + 8;\n\t\t\t\t\tif (r.top - rs.top >= ro.Height) y = r.top - ro.Height; else if (r.Height < 200) y = r.bottom; else x += 8;\n\t\t\t\t\t_ost.XY = new(x, y, false);\n\t\t\t\t\t_ost.Show();\n\t\t\t\t} else _ost?.Visible = false;\n\t\t\t} else {\n\t\t\t\t_HideRect();\n\t\t\t}\n\t\t}\n\t\t\n\t\tvoid _HideRect() {\n\t\t\t_osr.Hide();\n\t\t\t_ost?.Hide();\n\t\t}\n\t\t\n\t\tpublic record class GetRectArgs(POINT p, wnd wTL) {\n\t\t\tpublic RECT resultRect;\n\t\t\tpublic string resultText;\n\t\t}\n\t\t\n\t\tnint _WndProc(nint hwnd, int msg, nint wParam, nint lParam, ref bool handled) {\n\t\t\tif (msg == s_stopMessage) {\n\t\t\t\thandled = true;\n\t\t\t\t_captureCheckbox.IsChecked = false;\n\t\t\t} else if (msg == Api.WM_HOTKEY && (wParam is c_hotkeyCapture or c_hotkeyInsert or c_hotkeySmaller)) {\n\t\t\t\thandled = true;\n\t\t\t\tif (wParam == c_hotkeyInsert) _hkInsert.a();\n\t\t\t\telse if (wParam == c_hotkeySmaller) _hkSmaller.a();\n\t\t\t\telse _hkCapture.a();\n\t\t\t}\n\t\t\treturn default;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Adds link +hotkey that shows dialog \"Hotkeys\" and updates LA.App.Settings.delm.hk_x.\n\t\t/// </summary>\n\t\tpublic static void RegisterLink_DialogHotkey(KSciInfoBox sci) {\n\t\t\tif (sci.AaTags.HasLinkTag(\"+hotkey\")) return;\n\t\t\tsci.AaTags.AddLinkTag(\"+hotkey\", _ => {\n\t\t\t\tvar b = new wpfBuilder(\"Hotkey\");\n\t\t\t\tb.R.xAddGroupSeparator(\"In wnd and elm tools\");\n\t\t\t\tb.R.Add(\"Capture\", out TextBox capture, LA.App.Settings.delm.hk_capture).xValidateHotkey(errorIfEmpty: true).Focus();\n\t\t\t\tb.R.xAddGroupSeparator(\"In elm tool\");\n\t\t\t\tb.R.Add(\"Insert code\", out TextBox insert, LA.App.Settings.delm.hk_insert).xValidateHotkey();\n\t\t\t\tb.R.Add(\"Capturing method\", out TextBox smaller, LA.App.Settings.delm.hk_smaller).xValidateHotkey();\n\t\t\t\tb.R.AddSeparator(false);\n\t\t\t\tb.R.xAddInfoBlockT(\"After changing hotkeys please restart the tool window.\");\n\t\t\t\tif (!uacInfo.isAdmin) b.R.xAddInfoBlockT(\"Hotkeys don't work when the active window is admin,\\nbecause this process isn't admin.\");\n\t\t\t\tb.R.AddOkCancel();\n\t\t\t\tb.End();\n\t\t\t\tif (b.ShowDialog(Window.GetWindow(sci))) {\n\t\t\t\t\tLA.App.Settings.delm.hk_capture = capture.Text;\n\t\t\t\t\tLA.App.Settings.delm.hk_insert = insert.TextOrNull();\n\t\t\t\t\tLA.App.Settings.delm.hk_smaller = smaller.TextOrNull();\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t\t\n\t\tpublic UsingEndAction TempHideRect() {\n\t\t\tbool v1, v2;\n\t\t\tif (v1 = _osr.Visible) _osr.Hwnd.ShowL(false);\n\t\t\tif (v2 = _ost?.Visible == true) _ost.Hwnd.ShowL(false);\n\t\t\treturn new(() => {\n\t\t\t\tif (v1) _osr.Hwnd.ShowL(true);\n\t\t\t\tif (v2) _ost.Hwnd.ShowL(true);\n\t\t\t});\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Tools/ColorQuantizer.cs",
    "content": "using System.Drawing;\nusing System.Drawing.Imaging;\n\nnamespace ToolLand;\n\n/// <summary>\n/// Reduces image color depth with better quality than GDI/GDI+. It makes image smaller.\n/// Uses color quantizer algorithm of Xiaolin Wu.\n/// Same instance can be reused to quantize multiple images and avoid 0.7 MB of garbage for each; the static functions use this.\n/// </summary>\nunsafe class ColorQuantizer {\n\t/// <summary>\n\t/// Takes screenshot of specified rectangle in screen, quantizes colors to make smaller, compresses, Base64 encodes and returns comment string like <c>\" /*image:...*/\"</c>.\n\t/// If fails, prints warning and returns null.\n\t/// </summary>\n\t/// <param name=\"r\">Rectangle in screen. Must be not DPI-scaled. This function inflates it if need for DPI of that screen.</param>\n\tpublic static string MakeScreenshotComment(RECT r) {\n\t\tint dpi = screen.of(r).Dpi;\n\t\tif (dpi != 96) {\n\t\t\tr.Inflate((Dpi.Scale(r.Width, dpi) - r.Width) / 2, (Dpi.Scale(r.Height, dpi) - r.Height) / 2);\n\t\t}\n\t\ttry {\n\t\t\tvar b = CaptureScreen.Image(r);\n\t\t\tvar a = Quantize(b, 16, dpi);\n\t\t\tvar z = Convert2.BrotliCompress(a);\n\t\t\treturn \"/*image:\"\n\t\t\t\t//+ \"\\r\\n\" //rejected\n\t\t\t\t+ \"WkJN\" + Convert.ToBase64String(z) + \"*/\";\n\t\t}\n\t\tcatch (Exception e1) { print.warning(\"MakeScreenshotComment() failed. \" + e1.ToStringWithoutStack()); return null; }\n\t}\n\n\t[ThreadStatic] static WeakReference<ColorQuantizer> t_wr;\n\n\t/// <summary>\n\t/// Quantizes bitmap.\n\t/// </summary>\n\t/// <param name=\"b\">Bitmap with pixel bit count 32 or 24. If 32, alpha is ignored.</param>\n\t/// <param name=\"nColors\">Max count of colors desired. Must be 2 to 256 inclusive. For example a 4-bit bitmap can have max 16 colors; 8-bit - max 256 colors.</param>\n\t/// <param name=\"dpi\">Image resolution as DPI. If more than 96, this function writes this info to the bitmap header.</param>\n\t/// <returns>.bmp file data (BITMAPFILEHEADER, BITMAPINFOHEADER, color table, pixel bits) of quantized bitmap.</returns>\n\t/// <exception cref=\"ArgumentException\"></exception>\n\tpublic static byte[] Quantize(Bitmap b, int nColors, int dpi = 0) {\n\t\tt_wr ??= new(null); if (!t_wr.TryGetTarget(out var q)) t_wr.SetTarget(q = new()); //avoid 0.7 MB of garbage each time\n\n\t\tusing var d = b.Data(ImageLockMode.ReadOnly);\n\t\tvar p = (byte*)d.Scan0;\n\t\tbool topDown = d.Stride >= 0;\n\t\tif (!topDown) p -= -d.Stride * (d.Height - 1);\n\t\treturn q.Quantize(d.Width, d.Height, Image.GetPixelFormatSize(d.PixelFormat), p, ref nColors, topDown, dpi);\n\t}\n\n\t/// <summary>\n\t/// Quantizes bitmap.\n\t/// </summary>\n\t/// <param name=\"b\">Bitmap with pixel bit count 32 or 24. If 32, alpha is ignored.</param>\n\t/// <param name=\"nColors\">Max count of colors desired. Must be 2 to 256 inclusive. For example a 4-bit bitmap can have max 16 colors; 8-bit - max 256 colors.</param>\n\t/// <param name=\"dpi\">Image resolution as DPI. If more than 96, this function writes this info to the bitmap header.</param>\n\t/// <returns>Quantized bitmap.</returns>\n\t/// <exception cref=\"ArgumentException\"></exception>\n\tpublic static Bitmap QuantizeB(Bitmap b, int nColors, int dpi = 0) => new(new MemoryStream(Quantize(b, nColors, dpi)));\n\n\t// DIB data\n\tint _width, _height, _lineSize, _colorBytes;\n\tbyte* _bits;\n\n\tstruct Box { public int r0, r1, g0, g1, b0, b1, vol; }\n\n\tconst int FI_RGBA_RED = 2, FI_RGBA_GREEN = 1, FI_RGBA_BLUE = 0;\n\tconst int FIQ_SIZE_3D = 33 * 33 * 33;\n\n\tfloat[] _gm2;\n\tint[] _wt, _mr, _mg, _mb;\n\tushort[] Qadd;\n\tbyte[] _tag;\n\n\t/// <summary>\n\t/// Quantizes bitmap.\n\t/// </summary>\n\t/// <param name=\"width\"></param>\n\t/// <param name=\"height\"></param>\n\t/// <param name=\"bitCount\">Pixel bit count of the input bitmap. Must be 32 or 24. If 32, alpha is ignored.</param>\n\t/// <param name=\"bits\">Pixels.</param>\n\t/// <param name=\"nColors\">Input - max count of colors desired. Must be 2 to 256 inclusive. For example a 4-bit bitmap can have max 16 colors; 8-bit - max 256 colors. Output - actual count of colors in color table of the returned bitmap.</param>\n\t/// <param name=\"topDown\">Top-down bitmap. For example GDI+ (System.Drawing) bitmaps usually are top-down.</param>\n\t/// <param name=\"dpi\">Image resolution as DPI. If more than 96, this function writes this info to the bitmap header.</param>\n\t/// <returns>.bmp file data (BITMAPFILEHEADER, BITMAPINFOHEADER, color table, pixel bits) of quantized bitmap.</returns>\n\t/// <exception cref=\"ArgumentException\"></exception>\n\tpublic byte[] Quantize(int width, int height, int bitCount, byte* bits, ref int nColors, bool topDown, int dpi = 0) {\n\t\tif (!(bits != null && bitCount is (32 or 24) && width > 0 && height > 0 && nColors >= 2 && nColors <= 256)) throw new ArgumentException();\n\n\t\t_width = width;\n\t\t_height = height;\n\t\t_lineSize = _LineSize(bitCount);\n\t\t_colorBytes = bitCount / 8;\n\t\t_bits = bits;\n\n\t\t//Debug_.MemorySetAnchor_();\n\t\tstatic void _Array3D<T>(ref T[] a) { if (a == null) a = new T[FIQ_SIZE_3D]; else Array.Clear(a, 0, a.Length); }\n\t\t_Array3D(ref _gm2);\n\t\t_Array3D(ref _wt);\n\t\t_Array3D(ref _mr);\n\t\t_Array3D(ref _mg);\n\t\t_Array3D(ref _mb);\n\t\t_Array3D(ref _tag);\n\t\tQadd = new ushort[_width * _height];\n\n\t\tvar cube = stackalloc Box[256];\n\t\tint next;\n\t\tint i, weight;\n\t\tint k;\n\t\tvar vv = stackalloc float[256]; float temp;\n\n\t\t// Compute 3D histogram\n\n\t\tHist3D(_wt, _mr, _mg, _mb, _gm2);\n\n\t\t// Compute moments\n\n\t\tM3D(_wt, _mr, _mg, _mb, _gm2);\n\n\t\tcube[0].r0 = cube[0].g0 = cube[0].b0 = 0;\n\t\tcube[0].r1 = cube[0].g1 = cube[0].b1 = 32;\n\t\tnext = 0;\n\n\t\tfor (i = 1; i < nColors; i++) {\n\t\t\tif (Cut(ref cube[next], ref cube[i])) {\n\t\t\t\t// volume test ensures we won't try to cut one-cell box\n\t\t\t\tvv[next] = (cube[next].vol > 1) ? Var(cube[next]) : 0;\n\t\t\t\tvv[i] = (cube[i].vol > 1) ? Var(cube[i]) : 0;\n\t\t\t} else {\n\t\t\t\tvv[next] = 0f;   // don't try to split this box again\n\t\t\t\ti--;              // didn't create box i\n\t\t\t}\n\n\t\t\tnext = 0; temp = vv[0];\n\n\t\t\tfor (k = 1; k <= i; k++) {\n\t\t\t\tif (vv[k] > temp) {\n\t\t\t\t\ttemp = vv[k]; next = k;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (temp <= 0.0) {\n\t\t\t\tnColors = i + 1;\n\n\t\t\t\t// Error: \"Only got 'nColors' boxes\"\n\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t// Partition done\n\n\t\t// Allocate a new dib\n\n\t\tint bpp = nColors > 16 ? 8 : 4, lineSize = _LineSize(bpp), paletteSize = nColors * 4, bitsSize = _height * lineSize;\n\t\tint headersSize = sizeof(BITMAPFILEHEADER) + sizeof(Api.BITMAPINFOHEADER), bitsOffset = headersSize + paletteSize;\n\t\tvar ret = new byte[bitsOffset + bitsSize];\n\t\tfixed (byte* pret = ret) {\n\n\t\t\t// create an optimized palette\n\n\t\t\tvar new_pal = (RGBQUAD*)(pret + headersSize);\n\n\t\t\tfor (k = 0; k < nColors; k++) {\n\t\t\t\tMark(cube[k], k, _tag);\n\t\t\t\tweight = Vol(cube[k], _wt);\n\n\t\t\t\tif (weight != 0) {\n\t\t\t\t\tnew_pal[k].rgbRed = (byte)(((float)Vol(cube[k], _mr) / (float)weight) + 0.5f);\n\t\t\t\t\tnew_pal[k].rgbGreen = (byte)(((float)Vol(cube[k], _mg) / (float)weight) + 0.5f);\n\t\t\t\t\tnew_pal[k].rgbBlue = (byte)(((float)Vol(cube[k], _mb) / (float)weight) + 0.5f);\n\t\t\t\t} else {\n\t\t\t\t\t// Error: bogus box 'k'\n\n\t\t\t\t\tnew_pal[k].rgbRed = new_pal[k].rgbGreen = new_pal[k].rgbBlue = 0;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// create pixel bits\n\n\t\t\tvar newBits = pret + bitsOffset;\n\t\t\tfor (int y = 0; y < _height; y++) {\n\t\t\t\tbyte* new_bits = newBits + (y * lineSize);\n\t\t\t\tint yy = topDown ? _height - y - 1 : y; //always save as bottom-up\n\t\t\t\tint lineStart = yy * _width;\n\n\t\t\t\tif (bpp == 8) {\n\t\t\t\t\tfor (int x = 0; x < _width; x++)\n\t\t\t\t\t\tnew_bits[x] = _tag[Qadd[lineStart + x]];\n\t\t\t\t} else {\n\t\t\t\t\tint x, n = _width & ~1; //because using x+1\n\t\t\t\t\tfor (x = 0; x < n; x += 2)\n\t\t\t\t\t\tnew_bits[x / 2] = (byte)((_tag[Qadd[lineStart + x]] << 4) | (_tag[Qadd[lineStart + x + 1]] & 0xF));\n\t\t\t\t\tif (n != _width) new_bits[x / 2] = (byte)(_tag[Qadd[lineStart + x]] << 4);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// output 'new_pal' as color look-up table contents,\n\t\t\t// 'new_bits' as the quantized image (array of table addresses).\n\t\t\tvar f2 = (BITMAPFILEHEADER*)pret;\n\t\t\tf2->bfType = Math2.MakeWord('B', 'M'); f2->bfSize = ret.Length; f2->bfOffBits = bitsOffset;\n\t\t\tvar h2 = (Api.BITMAPINFOHEADER*)(f2 + 1);\n\t\t\th2->biSize = sizeof(Api.BITMAPINFOHEADER); h2->biPlanes = 1;\n\t\t\th2->biBitCount = (ushort)bpp; h2->biClrUsed = nColors;\n\t\t\th2->biWidth = _width; h2->biHeight = _height;\n\t\t\tif (dpi > 96) h2->biXPelsPerMeter = h2->biYPelsPerMeter = (dpi * 39.370079).ToInt();\n\t\t}\n\n\t\t//Debug_.MemoryPrint_();\n\t\treturn ret;\n\n\t\tint _LineSize(int bitCount) => Math2.AlignUp(_width * bitCount, 32) / 8;\n\t}\n\n\t// 3D array indexation\n\t[MethodImpl(MethodImplOptions.AggressiveInlining)]\n\tstatic int FIQ_INDEX(int r, int g, int b) => (r << 10) + (r << 6) + r + (g << 5) + g + b;\n\n\t// Histogram is in elements 1..HISTSIZE along each axis,\n\t// element 0 is for base or marginal value\n\t// NB: these must start out 0!\n\n\t// Build 3-D color histogram of counts, r/g/b, c^2\n\tvoid Hist3D(int[] vwt, int[] vmr, int[] vmg, int[] vmb, float[] m2) {\n\t\tint ind = 0;\n\t\tint inr, ing, inb;\n\t\tvar table = stackalloc int[256];\n\t\tint i, y, x;\n\n\t\tfor (i = 0; i < 256; i++)\n\t\t\ttable[i] = i * i;\n\n\t\tfor (y = 0; y < _height; y++) {\n\t\t\tvar bits = _bits + y * _lineSize;\n\n\t\t\tfor (x = 0; x < _width; x++) {\n\t\t\t\tinr = (bits[FI_RGBA_RED] >> 3) + 1;\n\t\t\t\ting = (bits[FI_RGBA_GREEN] >> 3) + 1;\n\t\t\t\tinb = (bits[FI_RGBA_BLUE] >> 3) + 1;\n\t\t\t\tind = FIQ_INDEX(inr, ing, inb);\n\t\t\t\tQadd[y * _width + x] = (ushort)ind;\n\t\t\t\t// [inr][ing][inb]\n\t\t\t\tvwt[ind]++;\n\t\t\t\tvmr[ind] += bits[FI_RGBA_RED];\n\t\t\t\tvmg[ind] += bits[FI_RGBA_GREEN];\n\t\t\t\tvmb[ind] += bits[FI_RGBA_BLUE];\n\t\t\t\tm2[ind] += (float)(table[bits[FI_RGBA_RED]] + table[bits[FI_RGBA_GREEN]] + table[bits[FI_RGBA_BLUE]]);\n\t\t\t\tbits += _colorBytes;\n\t\t\t}\n\t\t}\n\t}\n\t//___________________________________________\n\n\t// At conclusion of the histogram step, we can interpret\n\t// wt[r][g][b] = sum over voxel of P(c)\n\t// mr[r][g][b] = sum over voxel of r*P(c)  ,  similarly for mg, mb\n\t// m2[r][g][b] = sum over voxel of c^2*P(c)\n\t// Actually each of these should be divided by 'ImageSize' to give the usual\n\t// interpretation of P() as ranging from 0 to 1, but we needn't do that here.\n\n\t// We now convert histogram into moments so that we can rapidly calculate\n\t// the sums of the above quantities over any desired box.\n\n\t// Compute cumulative moments\n\tstatic void M3D(int[] vwt, int[] vmr, int[] vmg, int[] vmb, float[] m2) {\n\t\tint ind1, ind2;\n\t\tbyte i, r, g, b;\n\t\tint line, line_r, line_g, line_b;\n\t\tint* area = stackalloc int[33], area_r = stackalloc int[33], area_g = stackalloc int[33], area_b = stackalloc int[33];\n\t\tfloat line2; var area2 = stackalloc float[33];\n\n\t\tfor (r = 1; r <= 32; r++) {\n\t\t\tfor (i = 0; i <= 32; i++) {\n\t\t\t\tarea2[i] = 0;\n\t\t\t\tarea[i] = area_r[i] = area_g[i] = area_b[i] = 0;\n\t\t\t}\n\t\t\tfor (g = 1; g <= 32; g++) {\n\t\t\t\tline2 = 0;\n\t\t\t\tline = line_r = line_g = line_b = 0;\n\t\t\t\tfor (b = 1; b <= 32; b++) {\n\t\t\t\t\tind1 = FIQ_INDEX(r, g, b); // [r][g][b]\n\t\t\t\t\tline += vwt[ind1];\n\t\t\t\t\tline_r += vmr[ind1];\n\t\t\t\t\tline_g += vmg[ind1];\n\t\t\t\t\tline_b += vmb[ind1];\n\t\t\t\t\tline2 += m2[ind1];\n\t\t\t\t\tarea[b] += line;\n\t\t\t\t\tarea_r[b] += line_r;\n\t\t\t\t\tarea_g[b] += line_g;\n\t\t\t\t\tarea_b[b] += line_b;\n\t\t\t\t\tarea2[b] += line2;\n\t\t\t\t\tind2 = ind1 - 1089; // [r-1][g][b]\n\t\t\t\t\tvwt[ind1] = vwt[ind2] + area[b];\n\t\t\t\t\tvmr[ind1] = vmr[ind2] + area_r[b];\n\t\t\t\t\tvmg[ind1] = vmg[ind2] + area_g[b];\n\t\t\t\t\tvmb[ind1] = vmb[ind2] + area_b[b];\n\t\t\t\t\tm2[ind1] = m2[ind2] + area2[b];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t//___________________________________________\n\n\t// Compute sum over a box of any given statistic\n\tstatic int Vol(in Box cube, int[] mmt) {\n\t\treturn mmt[FIQ_INDEX(cube.r1, cube.g1, cube.b1)]\n\t\t\t  - mmt[FIQ_INDEX(cube.r1, cube.g1, cube.b0)]\n\t\t\t  - mmt[FIQ_INDEX(cube.r1, cube.g0, cube.b1)]\n\t\t\t  + mmt[FIQ_INDEX(cube.r1, cube.g0, cube.b0)]\n\t\t\t  - mmt[FIQ_INDEX(cube.r0, cube.g1, cube.b1)]\n\t\t\t  + mmt[FIQ_INDEX(cube.r0, cube.g1, cube.b0)]\n\t\t\t  + mmt[FIQ_INDEX(cube.r0, cube.g0, cube.b1)]\n\t\t\t  - mmt[FIQ_INDEX(cube.r0, cube.g0, cube.b0)];\n\t}\n\t//___________________________________________\n\n\t// The next two routines allow a slightly more efficient calculation\n\t// of Vol() for a proposed subbox of a given box.  The sum of Top()\n\t// and Bottom() is the Vol() of a subbox split in the given direction\n\t// and with the specified new upper bound.\n\n\t// Compute part of Vol(cube, mmt) that doesn't depend on r1, g1, or b1\n\t// (depending on dir)\n\tstatic int Bottom(in Box cube, byte dir, int[] mmt) {\n\t\tswitch (dir) {\n\t\tcase FI_RGBA_RED:\n\t\t\treturn -mmt[FIQ_INDEX(cube.r0, cube.g1, cube.b1)]\n\t\t\t\t\t+ mmt[FIQ_INDEX(cube.r0, cube.g1, cube.b0)]\n\t\t\t\t\t+ mmt[FIQ_INDEX(cube.r0, cube.g0, cube.b1)]\n\t\t\t\t\t- mmt[FIQ_INDEX(cube.r0, cube.g0, cube.b0)];\n\t\tcase FI_RGBA_GREEN:\n\t\t\treturn -mmt[FIQ_INDEX(cube.r1, cube.g0, cube.b1)]\n\t\t\t\t\t+ mmt[FIQ_INDEX(cube.r1, cube.g0, cube.b0)]\n\t\t\t\t\t+ mmt[FIQ_INDEX(cube.r0, cube.g0, cube.b1)]\n\t\t\t\t\t- mmt[FIQ_INDEX(cube.r0, cube.g0, cube.b0)];\n\t\tcase FI_RGBA_BLUE:\n\t\t\treturn -mmt[FIQ_INDEX(cube.r1, cube.g1, cube.b0)]\n\t\t\t\t\t+ mmt[FIQ_INDEX(cube.r1, cube.g0, cube.b0)]\n\t\t\t\t\t+ mmt[FIQ_INDEX(cube.r0, cube.g1, cube.b0)]\n\t\t\t\t\t- mmt[FIQ_INDEX(cube.r0, cube.g0, cube.b0)];\n\t\t}\n\n\t\treturn 0;\n\t}\n\t//___________________________________________\n\n\t// Compute remainder of Vol(cube, mmt), substituting pos for\n\t// r1, g1, or b1 (depending on dir)\n\tstatic int Top(in Box cube, byte dir, int pos, int[] mmt) {\n\t\tswitch (dir) {\n\t\tcase FI_RGBA_RED:\n\t\t\treturn mmt[FIQ_INDEX(pos, cube.g1, cube.b1)]\n\t\t\t\t   - mmt[FIQ_INDEX(pos, cube.g1, cube.b0)]\n\t\t\t\t   - mmt[FIQ_INDEX(pos, cube.g0, cube.b1)]\n\t\t\t\t   + mmt[FIQ_INDEX(pos, cube.g0, cube.b0)];\n\t\tcase FI_RGBA_GREEN:\n\t\t\treturn mmt[FIQ_INDEX(cube.r1, pos, cube.b1)]\n\t\t\t\t   - mmt[FIQ_INDEX(cube.r1, pos, cube.b0)]\n\t\t\t\t   - mmt[FIQ_INDEX(cube.r0, pos, cube.b1)]\n\t\t\t\t   + mmt[FIQ_INDEX(cube.r0, pos, cube.b0)];\n\t\tcase FI_RGBA_BLUE:\n\t\t\treturn mmt[FIQ_INDEX(cube.r1, cube.g1, pos)]\n\t\t\t\t   - mmt[FIQ_INDEX(cube.r1, cube.g0, pos)]\n\t\t\t\t   - mmt[FIQ_INDEX(cube.r0, cube.g1, pos)]\n\t\t\t\t   + mmt[FIQ_INDEX(cube.r0, cube.g0, pos)];\n\t\t}\n\n\t\treturn 0;\n\t}\n\t//___________________________________________\n\n\t// Compute the weighted variance of a box \n\t// NB: as with the raw statistics, this is really the variance * ImageSize \n\tfloat Var(in Box cube) {\n\t\tfloat dr = (float)Vol(cube, _mr);\n\t\tfloat dg = (float)Vol(cube, _mg);\n\t\tfloat db = (float)Vol(cube, _mb);\n\t\tfloat xx = _gm2[FIQ_INDEX(cube.r1, cube.g1, cube.b1)]\n\t\t\t\t- _gm2[FIQ_INDEX(cube.r1, cube.g1, cube.b0)]\n\t\t\t\t - _gm2[FIQ_INDEX(cube.r1, cube.g0, cube.b1)]\n\t\t\t\t + _gm2[FIQ_INDEX(cube.r1, cube.g0, cube.b0)]\n\t\t\t\t - _gm2[FIQ_INDEX(cube.r0, cube.g1, cube.b1)]\n\t\t\t\t + _gm2[FIQ_INDEX(cube.r0, cube.g1, cube.b0)]\n\t\t\t\t + _gm2[FIQ_INDEX(cube.r0, cube.g0, cube.b1)]\n\t\t\t\t - _gm2[FIQ_INDEX(cube.r0, cube.g0, cube.b0)];\n\n\t\treturn (xx - (dr * dr + dg * dg + db * db) / (float)Vol(cube, _wt));\n\t}\n\t//___________________________________________\n\n\t// We want to minimize the sum of the variances of two subboxes.\n\t// The sum(c^2) terms can be ignored since their sum over both subboxes\n\t// is the same (the sum for the whole box) no matter where we split.\n\t// The remaining terms have a minus sign in the variance formula,\n\t// so we drop the minus sign and MAXIMIZE the sum of the two terms.\n\tfloat Maximize(in Box cube, byte dir, int first, int last, out int cut, int whole_r, int whole_g, int whole_b, int whole_w) {\n\t\tint half_r, half_g, half_b, half_w;\n\t\tint i;\n\t\tfloat temp;\n\n\t\tint base_r = Bottom(cube, dir, _mr);\n\t\tint base_g = Bottom(cube, dir, _mg);\n\t\tint base_b = Bottom(cube, dir, _mb);\n\t\tint base_w = Bottom(cube, dir, _wt);\n\n\t\tfloat max = 0f;\n\n\t\tcut = -1;\n\n\t\tfor (i = first; i < last; i++) {\n\t\t\thalf_r = base_r + Top(cube, dir, i, _mr);\n\t\t\thalf_g = base_g + Top(cube, dir, i, _mg);\n\t\t\thalf_b = base_b + Top(cube, dir, i, _mb);\n\t\t\thalf_w = base_w + Top(cube, dir, i, _wt);\n\n\t\t\t// now half_x is sum over lower half of box, if split at i\n\n\t\t\tif (half_w == 0) {      // subbox could be empty of pixels!\n\t\t\t\tcontinue;           // never split into an empty box\n\t\t\t} else {\n\t\t\t\ttemp = ((float)half_r * half_r + (float)half_g * half_g + (float)half_b * half_b) / half_w;\n\t\t\t}\n\n\t\t\thalf_r = whole_r - half_r;\n\t\t\thalf_g = whole_g - half_g;\n\t\t\thalf_b = whole_b - half_b;\n\t\t\thalf_w = whole_w - half_w;\n\n\t\t\tif (half_w == 0) {      // subbox could be empty of pixels!\n\t\t\t\tcontinue;           // never split into an empty box\n\t\t\t} else {\n\t\t\t\ttemp += ((float)half_r * half_r + (float)half_g * half_g + (float)half_b * half_b) / half_w;\n\t\t\t}\n\n\t\t\tif (temp > max) {\n\t\t\t\tmax = temp;\n\t\t\t\tcut = i;\n\t\t\t}\n\t\t}\n\n\t\treturn max;\n\t}\n\t//___________________________________________\n\n\tbool Cut(ref Box set1, ref Box set2) {\n\t\tbyte dir;\n\t\tint cutr, cutg, cutb;\n\n\t\tint whole_r = Vol(set1, _mr);\n\t\tint whole_g = Vol(set1, _mg);\n\t\tint whole_b = Vol(set1, _mb);\n\t\tint whole_w = Vol(set1, _wt);\n\n\t\tfloat maxr = Maximize(set1, FI_RGBA_RED, set1.r0 + 1, set1.r1, out cutr, whole_r, whole_g, whole_b, whole_w);\n\t\tfloat maxg = Maximize(set1, FI_RGBA_GREEN, set1.g0 + 1, set1.g1, out cutg, whole_r, whole_g, whole_b, whole_w);\n\t\tfloat maxb = Maximize(set1, FI_RGBA_BLUE, set1.b0 + 1, set1.b1, out cutb, whole_r, whole_g, whole_b, whole_w);\n\n\t\tif ((maxr >= maxg) && (maxr >= maxb)) {\n\t\t\tdir = FI_RGBA_RED;\n\n\t\t\tif (cutr < 0) {\n\t\t\t\treturn false; // can't split the box\n\t\t\t}\n\t\t} else if ((maxg >= maxr) && (maxg >= maxb)) {\n\t\t\tdir = FI_RGBA_GREEN;\n\t\t} else {\n\t\t\tdir = FI_RGBA_BLUE;\n\t\t}\n\n\t\tset2.r1 = set1.r1;\n\t\tset2.g1 = set1.g1;\n\t\tset2.b1 = set1.b1;\n\n\t\tswitch (dir) {\n\t\tcase FI_RGBA_RED:\n\t\t\tset2.r0 = set1.r1 = cutr;\n\t\t\tset2.g0 = set1.g0;\n\t\t\tset2.b0 = set1.b0;\n\t\t\tbreak;\n\n\t\tcase FI_RGBA_GREEN:\n\t\t\tset2.g0 = set1.g1 = cutg;\n\t\t\tset2.r0 = set1.r0;\n\t\t\tset2.b0 = set1.b0;\n\t\t\tbreak;\n\n\t\tcase FI_RGBA_BLUE:\n\t\t\tset2.b0 = set1.b1 = cutb;\n\t\t\tset2.r0 = set1.r0;\n\t\t\tset2.g0 = set1.g0;\n\t\t\tbreak;\n\t\t}\n\n\t\tset1.vol = (set1.r1 - set1.r0) * (set1.g1 - set1.g0) * (set1.b1 - set1.b0);\n\t\tset2.vol = (set2.r1 - set2.r0) * (set2.g1 - set2.g0) * (set2.b1 - set2.b0);\n\n\t\treturn true;\n\t}\n\t//___________________________________________\n\n\tstatic void Mark(in Box cube, int label, byte[] tag) {\n\t\tfor (int r = cube.r0 + 1; r <= cube.r1; r++) {\n\t\t\tfor (int g = cube.g0 + 1; g <= cube.g1; g++) {\n\t\t\t\tfor (int b = cube.b0 + 1; b <= cube.b1; b++) {\n\t\t\t\t\ttag[FIQ_INDEX(r, g, b)] = (byte)label;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t[StructLayout(LayoutKind.Sequential, Pack = 2)]\n\tinternal struct BITMAPFILEHEADER {\n\t\tpublic ushort bfType;\n\t\tpublic int bfSize;\n\t\tpublic ushort bfReserved1;\n\t\tpublic ushort bfReserved2;\n\t\tpublic int bfOffBits;\n\t}\n\n#pragma warning disable CS0649 //field never assigned\n\n\tinternal struct RGBQUAD {\n\t\tpublic byte rgbBlue;\n\t\tpublic byte rgbGreen;\n\t\tpublic byte rgbRed;\n\t\tpublic byte rgbReserved;\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Tools/DCustomize.cs",
    "content": "using System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Controls.Primitives;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing Au.Controls;\nusing ToolLand;\nusing System.Xml.Linq;\n\n#if SCRIPT\nnamespace Script;\n#endif\n\nnamespace LA;\n\nclass DCustomize : KDialogWindow {\n\tpublic static void ShowSingle(string commandName = null) {\n\t\tvar d = ShowSingle(() => new DCustomize());\n\t\tif (commandName != null) d._SelectAndOpen(commandName);\n\t}\n\t\n\t/// <summary>\n\t/// If the dialog is open, sets its Image field text and returns true. Called from the Icons dialog.\n\t/// </summary>\n\tpublic static bool AaSetImage(string s) {\n\t\tif (!GetSingle(out DCustomize d)) return false;\n\t\tif (d._panelProp.IsVisible) d._tImage.Text = s;\n\t\treturn true;\n\t}\n\t\n\tList<_Custom> _tree;\n\tKSciInfoBox _info;\n\t\n\tContextMenu _menu;\n\tDictionary<string, _Default> _dict = new();\n\t\n\tKTreeView _tv;\n\tPanel _panelProp;\n\tTextBox _tColor, _tText, _tImage, _tKeys, _tBtext;\n\tTextBox _tDefText, _tDefImage, _tDefKeys;\n\tKCheckBox _cSeparator;\n\tComboBox _cbHide, _cbImageAt;\n\tLabel _lCommandPath;\n\t\n\t_Custom _ti; //current item\n\t_Custom _clip; //cut/copy item\n\tbool _ignoreEvents;\n\t\n\tDCustomize() {\n\t\tInitWinProp(\"Customize\", App.Wmain);\n\t\t\n\t\tvar b = new wpfBuilder(this).WinSize(700, 700).Columns(220, 0, -1);\n\t\tb.Row(-1);\n\t\t\n\t\tb.xAddInBorder(out _tv);\n\t\tb.Add<GridSplitter>().Splitter(vertical: true);\n\t\t\n\t\tb.StartGrid().Columns(-1); //right side\n\t\t\n\t\tb.Row(84).Add(out _info);\n\t\tb.AddSeparator(vertical: false);\n\t\t\n\t\tb.Row(-1).StartStack(vertical: true).Hidden();\n\t\t_panelProp = b.Panel;\n\t\t\n\t\tb.Add(out _lCommandPath).Padding(\"4\").Brush(Brushes.LightBlue, Brushes.Black);\n\t\tb.StartGrid<KGroupBox>(\"Properties common to menu item and toolbar button\").Columns(0, -1, 30, 30);\n\t\tb.R.Add(\"Text\", out _tText).Tooltip(\"Text.\\nInsert _ before Alt-underlined character.\");\n\t\tb.R.Add(\"Color\", out _tColor).Tooltip(\"Text color.\\nCan be a .NET color name or #RRGGBB or #RGB.\")\n\t\t\t.xAddButtonIcon(EdIcons.Color, _ => KColorPicker.ColorTool(s => { _tColor.Text = s; }, b.Window, modal: true, add0xRgbButton: false, addBgrButton: false), \"Colors\").Span(1);\n\t\tb.R.Add(\"Image\", out _tImage).Tooltip(\"Icon name etc.\\nSee ImageUtil.LoadWpfImageElement.\")\n\t\t\t.xAddButtonIcon(EdIcons.Icons, _ => { _tImage.SelectAll(); DIcons.ShowSingle(expandMenuIcon: true); }, \"Icons tool.\\nSelect an icon and click button 'Menu or toolbar item'.\").Span(1);\n\t\tb.R.Add(\"Keys\", out _tKeys).Tooltip(\"Keyboard or/and mouse shortcut(s), like Ctrl+E, Shift+M-click.\\nSee keys.more.parseHotkeyString.\")\n\t\t\t.xAddButtonIcon(EdIcons.Keys, _ => _KeysTool(), \"Keys tool\");\n\t\tb.xAddButtonIcon(\"*FeatherIcons.Eye\" + EdIcons.black, _ => _KeysList(), \"Existing hotkeys\");\n\t\tb.StartGrid<Expander>(\"Default\");\n\t\tb.R.Add(\"Text\", out _tDefText); _Readonly(_tDefText);\n\t\tb.R.Add(\"Image\", out _tDefImage); _Readonly(_tDefImage);\n\t\tb.R.Add(\"Keys\", out _tDefKeys); _Readonly(_tDefKeys);\n\t\tstatic void _Readonly(TextBox k) {\n\t\t\tk.IsReadOnly = true;\n\t\t\tk.IsReadOnlyCaretVisible = true;\n\t\t\tk.Background = SystemColors.ControlBrush;\n\t\t}\n\t\tb.End();\n\t\tb.End();\n\t\t\n\t\tb.StartGrid<KGroupBox>(\"Toolbar button properties\").Columns(0, 100, -1);\n\t\tb.R.Add(\"Text\", out _tBtext).Multiline(wrap: TextWrapping.NoWrap).Tooltip(\"Button text, if different than menu item text.\");\n\t\tb.R.Add(\"Image at\", out _cbImageAt).Items(\"\", \"left\", \"top\", \"right\", \"bottom\").Span(1).Tooltip(\"Display image + text and put image at this side.\\nFor submenu-items always left.\");\n\t\tb.R.Add(out _cSeparator, \"Separator before\");\n\t\tb.R.Add(\"Hide\", out _cbHide).Items(\"\", \"always\", \"never\").Span(1).Tooltip(\"When to move the button to the overflow dropdown.\\nIf empty - when the toolbar is too small.\");\n\t\tb.End();\n\t\t\n\t\tb.StartGrid<KGroupBox>(\"Move\");\n\t\tb.R.AddButton(\"Up\", _ => _Move(_ti, true)).Tooltip(\"Shift+Up\");\n\t\tb.R.AddButton(\"Down\", _ => _Move(_ti, false)).Tooltip(\"Shift+Down\");\n\t\tb.End().Width(90).Align(\"L\");\n\t\t\n\t\tb.End();\n\t\t\n\t\tb.R.AddSeparator(vertical: false);\n\t\tb.R.StartOkCancel()\n\t\t\t.AddButton(out var bOK, \"Save\", _ => { _Save(); }).Width(70).Tooltip(\"Saves changes. Will be applied when the program starts next time.\")\n\t\t\t.AddButton(out var bOK2, \"Save and restart\", _ => { _Save(); Close(); App.Restart(); }).Width(120).Tooltip(\"Saves changes and restarts the program.\")\n\t\t\t.AddButton(out var bCancel, \"Cancel\", _ => Close()).Width(70)\n\t\t\t.End();\n\t\t\n\t\tb.End(); //right side\n\t\tb.End();\n\t\t\n\t\t_FillMenu();\n\t\t\n\t\t_tv.SingleClickActivate = true;\n\t\t_tv.ItemActivated += _tv_ItemActivated;\n\t\t_tv.ItemClick += _tv_ItemClick;\n\t\t_tv.KeyDown += _tv_KeyDown;\n\t\t_FillTree();\n\t\t\n\t\tforeach (var v in new Control[] { _tText, _tColor, _tImage, _tKeys, _tBtext, _cbImageAt, _cbHide, _cSeparator }) {\n\t\t\tvoid _Update() { if (!_ignoreEvents) { _GetControlValues(); _tv.Redraw(); } }\n\t\t\tif (v is TextBox tb) tb.TextChanged += (_, _) => _Update();\n\t\t\telse if (v is ComboBox cb) cb.SelectionChanged += (_, _) => _Update();\n\t\t\telse if (v is KCheckBox ch) ch.CheckChanged += (_, _) => _Update();\n\t\t}\n\t\t\n\t\tb.Loaded += () => {\n\t\t\t_info.aaaText = $\"\"\"\nHere you can edit menus, toolbars and hotkeys of the main window.\n\n<b>menu</b> - customized menu items. Right-click...\n<b>File, etc</b> - toolbars. Right-click to add a button.\n\nMenu items cannot be removed or reordered. Default toolbar buttons cannot be removed, but you can edit, reorder and hide.\n\nText color in the list: blue - customized; gray - hidden button; red - cut.\n\nYou also can edit the <explore {App.Commands.UserFile}>file<> in an XML editor. To reset everything, delete the file. To reset an item or toolbar, remove it from XML.\n\"\"\";\n\t\t};\n\t\t\n\t\tvoid _KeysTool() {\n\t\t\t_tKeys.SelectAll();\n\t\t\t_tKeys.Focus();\n\t\t\tvar k = new KeysWindow { InsertInControl = _tKeys, ClickClose = KPopup.CC.Outside, CloseHides = true };\n\t\t\tk.SetFormat(PSFormat.Hotkey);\n\t\t\tk.ShowByRect(_tKeys, Dock.Bottom);\n\t\t}\n\t\t\n\t\tvoid _KeysList() {\n\t\t\tvar m = new popupMenu();\n\t\t\tvar a = new Dictionary<_Default, string>();\n\t\t\t//add default hotkeys to a\n\t\t\tforeach (var v in _dict) {\n\t\t\t\tif (v.Value.attr.keys is string k) a.Add(v.Value, k);\n\t\t\t}\n\t\t\t//add custom hotkeys to a\n\t\t\tforeach (var v in _tree.SelectMany(o => o.Children())) {\n\t\t\t\tif (v.keys is string k) {\n\t\t\t\t\tif (k == \"\") a.Remove(v.def); else a[v.def] = k;\n\t\t\t\t}\n\t\t\t}\n\t\t\t//add all to menu\n\t\t\tforeach (var v in a.OrderBy(o => o.Value, StringComparer.OrdinalIgnoreCase)) {\n\t\t\t\tvar u = m.Add(v.Key.text + \"\\t\" + v.Value, o => {\n\t\t\t\t\tvar d = o.Tag as _Default;\n\t\t\t\t\tvar c = _tree.SelectMany(o => o.Children()).FirstOrDefault(o => o.def == d);\n\t\t\t\t\tif (c == null) {\n\t\t\t\t\t\tif (!dialog.showOkCancel(\"Customize this menu item?\", d.text, owner: this)) return;\n\t\t\t\t\t\t_AddToCustomized(d, _tree.Find(o => o.isMenu));\n\t\t\t\t\t} else {\n\t\t\t\t\t\t_SelectAndOpen(c);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tu.Tag = v.Key;\n\t\t\t\tif (v.Value != v.Key.attr.keys) u.TextColor = Colors.Blue; //customized\n\t\t\t}\n\t\t\tm.Show(owner: this);\n\t\t}\n\t}\n\t\n\tvoid _tv_ItemActivated(TVItemEventArgs e) => _Open(e.Item as _Custom);\n\t\n\tvoid _Open(_Custom c) {\n\t\t//print.it(c.name);\n\t\tif (c.Level == 0) {\n\t\t\t_panelProp.Visibility = Visibility.Hidden;\n\t\t\treturn;\n\t\t}\n\t\t_ti = c;\n\t\t_ignoreEvents = true;\n\t\t_panelProp.Visibility = Visibility.Visible;\n\t\t\n\t\tvar path = new Stack<string>();\n\t\tfor (var v = c.def; v != null; v = v.parent) path.Push(v.text);\n\t\t_lCommandPath.Content = string.Join(\" > \", path);\n\t\t\n\t\t//common\n\t\t_tText.Text = c.ctext ?? c.def.text;\n\t\t_tColor.Text = c.color;\n\t\t_tImage.Text = c.image ?? c.def.attr.image;\n\t\t_tKeys.Text = c.keys ?? c.def.attr.keys;\n\t\t_tDefText.Text = c.def.text;\n\t\t_tDefImage.Text = c.def.attr.image;\n\t\t_tDefKeys.Text = c.def.attr.keys;\n\t\t//button\n\t\t_tBtext.Text = c.btext;\n\t\t_cbImageAt.SelectedIndex = c.imageAt switch { \"left\" => 1, \"top\" => 2, \"right\" => 3, \"bottom\" => 4, _ => 0 };\n\t\t_cSeparator.IsChecked = c.separator;\n\t\t_cbHide.SelectedIndex = c.hide switch { \"always\" => 1, \"never\" => 2, _ => 0 };\n\t\t\n\t\t_ignoreEvents = false;\n\t}\n\t\n\tvoid _GetControlValues() {\n\t\tvar c = _ti;\n\t\tc.ctext = _Text(_tText, c.def.text);\n\t\tc.color = _Text(_tColor);\n\t\tc.image = _Text(_tImage, c.def.attr.image);\n\t\tc.keys = _Text(_tKeys, c.def.attr.keys);\n\t\tc.btext = _Text(_tBtext);\n\t\tc.imageAt = _Text(_cbImageAt);\n\t\tc.separator = _cSeparator.IsChecked;\n\t\tc.hide = _Text(_cbHide);\n\t\t\n\t\tstring _Text(Control tbcb, string def = null) {\n\t\t\tvar s = tbcb switch { TextBox tb => tb.Text, ComboBox cb => cb.SelectedValue as string, _ => null };\n\t\t\ts = s.NullIfEmpty_();\n\t\t\tif (def == null) return s;\n\t\t\tif (s == def) return null;\n\t\t\treturn s ?? \"\";\n\t\t}\n\t}\n\t\n\tvoid _SelectAndOpen(_Custom t) {\n\t\t_tv.SelectSingle(t);\n\t\tif (t != _ti) _Open(t);\n\t}\n\t\n\tvoid _SelectAndOpen(string commandName) {\n\t\tvar c = _tree.SelectMany(o => o.Children()).LastOrDefault(o => o.def.name == commandName); //Last to prefer toolbar button and not menu item\n\t\tif (c != null) _SelectAndOpen(c);\n\t}\n\t\n\tvoid _tv_ItemClick(TVItemEventArgs e) {\n\t\tvar t = e.Item as _Custom;\n\t\tif (e.Button == MouseButton.Right) {\n\t\t\tvar m = new popupMenu();\n\t\t\t\n\t\t\tvar c0 = t.Level == 0 ? t : t.Parent;\n\t\t\tm[c0.isMenu ? \"Customize menu item...\" : \"Add button...\"] = o => { _menu.PlacementTarget = this; _menu.IsOpen = true; };\n\t\t\tif (t.Level == 1) {\n\t\t\t\tm.Separator();\n\t\t\t\tm[\"Cut\"] = o => _Cut(t);\n\t\t\t\t_MiPaste(false);\n\t\t\t\tm.AddCheck(\"Hide\", t.hide == \"always\", o => _Hide(t, o.IsChecked));\n\t\t\t\tif (!_IsDefaultButton(t)) {\n\t\t\t\t\tm.Submenu(\"Delete\", m => {\n\t\t\t\t\t\tm[\"Delete\"] = o => _Delete(t, ask: false);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t_MiPaste(true);\n\t\t\t}\n\t\t\t\n\t\t\tvoid _MiPaste(bool into) {\n\t\t\t\tif (_CanPaste(t)) m[\"Paste\"] = o => _Paste(t);\n\t\t\t}\n\t\t\t\n\t\t\t_contextMenuOwner = t;\n\t\t\tm.Show(owner: this);\n\t\t}\n\t}\n\t\n\t_Custom _contextMenuOwner;\n\t\n\tvoid _Cut(_Custom t) {\n\t\tif (_clip != null) _tv.Redraw(_clip);\n\t\t_clip = t;\n\t\t_tv.Redraw(t); //red etc\n\t}\n\t\n\tbool _CanPaste(_Custom where) {\n\t\tif (_clip == null || _clip == where) return false;\n\t\tif (_IsDefaultButton(_clip) && where != _clip.Parent && where.Parent != _clip.Parent) return false; //don't move to another toolbar\n\t\treturn true;\n\t}\n\t\n\tvoid _Paste(_Custom where) {\n\t\t_clip.Remove();\n\t\tif (where.Level == 0) where.AddChild(_clip); else where.AddSibling(_clip, after: false);\n\t\t_clip = null;\n\t\t_tv.SetItems(_tree, modified: true);\n\t}\n\t\n\tvoid _AddToCustomized(_Default def, _Custom where) {\n\t\tvar c0 = where.Level == 0 ? where : where.Parent;\n\t\tvar c = c0.Children().FirstOrDefault(o => o.def == def);\n\t\tif (c == null) {\n\t\t\tc = new _Custom(this, def, c0.isMenu);\n\t\t\tif (c0 == where) where.AddChild(c); else where.AddSibling(c, after: false);\n\t\t\t\n\t\t\t_tv.SetItems(_tree, modified: true);\n\t\t}\n\t\t\n\t\t_SelectAndOpen(c);\n\t}\n\t\n\tvoid _Move(_Custom t, bool up) {\n\t\tvar where = up ? t.Previous : t.Next;\n\t\tif (where == null) {\n\t\t\tif (_IsDefaultButton(t)) return; //don't move to another toolbar\n\t\t\tint i = _tree.IndexOf(t.Parent) + (up ? -1 : 1);\n\t\t\tif ((uint)i >= _tree.Count) return;\n\t\t\twhere = _tree[i];\n\t\t}\n\t\tt.Remove();\n\t\tif (where.Level == 0) where.AddChild(t, first: !up); else where.AddSibling(t, after: !up);\n\t\t_tv.SetItems(_tree, modified: true);\n\t\t_tv.EnsureVisible(t);\n\t}\n\t\n\tvoid _Delete(_Custom t, bool ask) {\n\t\tif (!_IsDefaultButton(t)) {\n\t\t\tif (ask && !dialog.showOkCancel(\"Delete?\", t.displayText, owner: this)) return;\n\t\t\tif (t == _ti) _panelProp.Visibility = Visibility.Hidden;\n\t\t\t_clip = null;\n\t\t\tt.Remove();\n\t\t\t_tv.SetItems(_tree, true);\n\t\t} else if (t == _ti) {\n\t\t\t_cbHide.SelectedIndex = 1;\n\t\t} else {\n\t\t\tt.hide = \"always\";\n\t\t\t_tv.Redraw(t);\n\t\t}\n\t}\n\t\n\tvoid _Hide(_Custom t, bool hide) {\n\t\tif (t == _ti) {\n\t\t\t_cbHide.SelectedIndex = hide ? 1 : 0;\n\t\t} else {\n\t\t\tt.hide = hide ? \"always\" : null;\n\t\t\t_tv.Redraw(t);\n\t\t}\n\t}\n\t\n\tbool _IsDefaultButton(_Custom t) {\n\t\tif (t.Level > 0 && !t.isMenu) {\n\t\t\t_xdefault ??= XmlUtil.LoadElem(App.Commands.DefaultFile);\n\t\t\treturn _xdefault.Element(t.Parent.displayText)?.Element(t.def.name) != null;\n\t\t}\n\t\treturn false;\n\t}\n\tXElement _xdefault;\n\t\n\tvoid _FillMenu() {\n\t\t_Menu(Panels.Menu, _menu = new(), null, 0);\n\t\t\n\t\tvoid _Menu(ItemsControl sourceMenu, ItemsControl destMenu, _Default parentCommand, int level) {\n\t\t\tforeach (var o in sourceMenu.Items) {\n\t\t\t\tif (o is not MenuItem ms || ms.Command is not KMenuCommands.Command c) continue;\n\t\t\t\tstring name = c.Name;\n\t\t\t\t\n\t\t\t\tvar def = new _Default(name, c.Attribute, c.Text, parentCommand);\n\t\t\t\t_dict.Add(name, def);\n\t\t\t\t\n\t\t\t\tvar m = new MenuItem { Name = name, Header = c.Text };\n\t\t\t\tdestMenu.Items.Add(m);\n\t\t\t\t\n\t\t\t\tif (ms.Role is MenuItemRole.SubmenuHeader or MenuItemRole.TopLevelHeader) {\n\t\t\t\t\tif (level > 0) m.PreviewMouseLeftButtonUp += (o, e) => { if (e.Source == o) { this.Focus(); _AddToCustomized(def, _contextMenuOwner); }; };\n\t\t\t\t\t\n\t\t\t\t\t_Menu(ms, m, def, level + 1);\n\t\t\t\t} else {\n\t\t\t\t\tm.Click += (o, _) => _AddToCustomized(def, _contextMenuOwner);\n\t\t\t\t}\n\t\t\t}\n\t\t\t//}\n\t\t}\n\t}\n\t\n\tvoid _FillTree() {\n\t\t_tree = new();\n\t\tvar a = App.Commands.LoadFiles(); if (a == null) return;\n\t\t\n\t\tforeach (var xx in a) {\n\t\t\tvar s1 = xx.Name.LocalName;\n\t\t\tbool isMenu = s1 == \"menu\";\n\t\t\tvar vv = new _Custom(this, null, isMenu, s1);\n\t\t\t_tree.Add(vv);\n\t\t\tforeach (var x in xx.Elements()) {\n\t\t\t\tif (!_dict.TryGetValue(x.Name.LocalName, out var def)) continue;\n\t\t\t\tvar c = new _Custom(this, def, isMenu) {\n\t\t\t\t\tcolor = x.Attr(\"color\"),\n\t\t\t\t\tctext = x.Attr(\"text\"),\n\t\t\t\t\timage = x.Attr(\"image\"),\n\t\t\t\t\tkeys = x.Attr(\"keys\"),\n\t\t\t\t\thide = x.Attr(\"hide\"),\n\t\t\t\t\timageAt = x.Attr(\"imageAt\"),\n\t\t\t\t\tseparator = x.HasAttr(\"separator\"),\n\t\t\t\t\tbtext = x.Attr(\"btext\"),\n\t\t\t\t};\n\t\t\t\tvv.AddChild(c);\n\t\t\t}\n\t\t}\n\t\t\n\t\t_tv.SetItems(_tree);\n\t}\n\t\n\tvoid _Save() {\n\t\tvar xr = new XElement(\"commands\");\n\t\tforeach (var t in _tree) {\n\t\t\tvar xt = new XElement(t.displayText);\n\t\t\txr.Add(xt);\n\t\t\tforeach (var c in t.Children()) {\n\t\t\t\tif (c.isMenu && !c.IsCustomized()) continue;\n\t\t\t\tvar x = new XElement(c.def.name);\n\t\t\t\txt.Add(x);\n\t\t\t\tif (c.separator) x.SetAttributeValue(\"separator\", \"\");\n\t\t\t\t_Set(\"text\", c.ctext);\n\t\t\t\t_Set(\"color\", c.color);\n\t\t\t\t_Set(\"image\", c.image);\n\t\t\t\t_Set(\"keys\", c.keys);\n\t\t\t\t_Set(\"btext\", c.btext);\n\t\t\t\t_Set(\"imageAt\", c.imageAt);\n\t\t\t\t_Set(\"hide\", c.hide);\n\t\t\t\t\n\t\t\t\tvoid _Set(string attr, string s) {\n\t\t\t\t\tif (s != null) x.SetAttributeValue(attr, s);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t//print.clear();\n\t\t//print.it(xr);\n\t\txr.SaveElem(App.Commands.UserFile);\n\t}\n\t\n\t//default properties (Menus member name and attributes)\n\trecord _Default(string name, CommandAttribute attr, string text, _Default parent);\n\t\n\t//customized properties (in XML file)\n\tclass _Custom : TreeBase<_Custom>, ITreeViewItem {\n\t\treadonly DCustomize _d;\n\t\tpublic readonly _Default def;\n\t\tpublic string displayText;\n\t\tpublic readonly bool isMenu;\n\t\tbool _isExpanded;\n\t\t//customized properties\n\t\tpublic bool separator;\n\t\tpublic string ctext, color, image, keys, btext, imageAt, hide;\n\t\t\n\t\tpublic _Custom(DCustomize d, _Default def, bool isMenu, string displayText = null) {\n\t\t\t_d = d;\n\t\t\tthis.def = def;\n\t\t\tthis.isMenu = isMenu;\n\t\t\tthis.displayText = displayText ?? StringUtil.RemoveUnderlineChar(def.text, '_');\n\t\t\t_isExpanded = def == null;\n\t\t}\n\t\t\n\t\t#region ITreeViewItem\n\t\t\n\t\tvoid ITreeViewItem.SetIsExpanded(bool yes) { _isExpanded = yes; }\n\t\t\n\t\tpublic bool IsExpanded => _isExpanded;\n\t\t\n\t\tIEnumerable<ITreeViewItem> ITreeViewItem.Items => base.Children();\n\t\t\n\t\tpublic bool IsFolder => base.HasChildren;\n\t\t\n\t\tstring ITreeViewItem.DisplayText => displayText;\n\t\t\n\t\tobject ITreeViewItem.Image\n\t\t\t=> Level == 0 ? EdIcons.FolderArrow(_isExpanded) : (image ?? def.attr.image);\n\t\t\n\t\tint ITreeViewItem.Color(TVColorInfo ci)\n\t\t\t=> Level > 0 ? -1 : isMenu ? 0xF0C080 : 0xC0E0A0;\n\t\t\n\t\tint ITreeViewItem.TextColor(TVColorInfo ci)\n\t\t\t=> this == _d._clip ? 0xFF0000\n\t\t\t: hide == \"always\" ? 0x808080\n\t\t\t: IsCustomized() ? (ci.isHighContrastDark ? 0xFFFF00 : 0x0000FF)\n\t\t\t: Level == 0 ? 0\n\t\t\t: -1;\n\t\t\n\t\t#endregion\n\t\t\n\t\tpublic bool IsCustomized() {\n\t\t\tif (def == null) return false;\n\t\t\tif (separator && isMenu) return true; //never mind: or may be specified in the default XML file, but that info now is lost.\n\t\t\treturn ctext != null || color != null || image != null || keys != null || btext != null || imageAt != null || hide != null;\n\t\t}\n\t}\n\t\n\tvoid _tv_KeyDown(object sender, KeyEventArgs e) {\n\t\tvar k = (e.KeyboardDevice.Modifiers, e.Key);\n\t\tswitch (k) {\n\t\tcase (0, Key.Escape):\n\t\t\tif (_clip != null) {\n\t\t\t\t_clip = null;\n\t\t\t\t_tv.Redraw();\n\t\t\t}\n\t\t\tgoto gh;\n\t\t}\n\t\tif (_tv.FocusedItem is _Custom t) {\n\t\t\tswitch (k) {\n\t\t\tcase (0, Key.Delete):\n\t\t\t\tif (t.Level > 0) _Delete(t, ask: true);\n\t\t\t\tbreak;\n\t\t\tcase (ModifierKeys.Control, Key.X):\n\t\t\t\tif (t.Level > 0) _Cut(t);\n\t\t\t\tbreak;\n\t\t\tcase (ModifierKeys.Control, Key.V):\n\t\t\t\tif (_CanPaste(t)) _Paste(t);\n\t\t\t\tbreak;\n\t\t\tcase (ModifierKeys.Shift, Key.Up):\n\t\t\t\tif (t.Level > 0) _Move(t, up: true);\n\t\t\t\tbreak;\n\t\t\tcase (ModifierKeys.Shift, Key.Down):\n\t\t\t\tif (t.Level > 0) _Move(t, up: false);\n\t\t\t\tbreak;\n\t\t\tdefault: return;\n\t\t\t}\n\t\t}\n\t\tgh:\n\t\te.Handled = true;\n\t}\n\t\n\tpublic static void ToolbarContextMenuOpening(object sender, ContextMenuEventArgs _1) {\n\t\tif (GetSingle<DCustomize>(out _)) return;\n\t\tvar toolbar = sender as ToolBar;\n\t\tFrameworkElement e = null;\n\t\tICommand ic = null;\n\t\tint index = 0;\n\t\t\n\t\t//WPF does not have a better way to get the right-clicked element when it is disabled. Mouse.DirectlyOver etc return the container.\n\t\tvar xy = mouse.xy;\n\t\tforeach (FrameworkElement v in toolbar.Items) {\n\t\t\tif (v.IsVisible && v is System.Windows.Controls.Primitives.ButtonBase or Border && v.RectInScreen().Contains(xy)) {\n\t\t\t\te = v;\n\t\t\t\tif (v is ButtonBase b1) ic = b1.Command;\n\t\t\t\telse if (v is Border b2 && b2.Child is Menu m2) ic = m2.Items.OfType<MenuItem>().SingleOrDefault()?.Command;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tindex++;\n\t\t}\n\t\tif (ic is not KMenuCommands.Command c) return;\n\t\t\n\t\tOverflowMode hide1 = ToolBar.GetOverflowMode(e);\n\t\tbool separatorBefore = index > 0 && toolbar.Items[index - 1] is Separator;\n\t\t\n\t\tvar m = new popupMenu();\n\t\tm[\"Customize...\"] = o => DCustomize.ShowSingle(c.Name);\n\t\tm.Separator();\n\t\tm.AddCheck(\"Hide\", hide1 == OverflowMode.Always, _ => _Hide(hide1 == OverflowMode.Always ? OverflowMode.AsNeeded : OverflowMode.Always));\n\t\tm.AddCheck(\"Never hide\", hide1 == OverflowMode.Never, _ => _Hide(hide1 == OverflowMode.Never ? OverflowMode.AsNeeded : OverflowMode.Never));\n\t\tif (!ToolBar.GetIsOverflowItem(e)) {\n\t\t\tm.Separator();\n\t\t\tm.AddCheck(\"Separator before\", separatorBefore, o => _Separator());\n\t\t}\n\t\tm.Show();\n\t\t\n\t\tvoid _Hide(OverflowMode hide2) {\n\t\t\tif (!_ModifyUserXmlButton(x => { x.SetAttributeValue(\"hide\", hide2 switch { OverflowMode.Always => \"always\", OverflowMode.Never => \"never\", _ => null }); })) return;\n\t\t\t\n\t\t\tToolBar.SetOverflowMode(e, hide2);\n\t\t\tif (separatorBefore) ToolBar.SetOverflowMode(toolbar.Items[index - 1] as Separator, hide2);\n\t\t}\n\t\t\n\t\tvoid _Separator() {\n\t\t\tif (!_ModifyUserXmlButton(x => { x.SetAttributeValue(\"separator\", separatorBefore ? null : \"\"); })) return;\n\t\t\t\n\t\t\tif (separatorBefore) {\n\t\t\t\ttoolbar.Items.RemoveAt(index - 1);\n\t\t\t} else {\n\t\t\t\tvar sep = new Separator();\n\t\t\t\tif (hide1 != default) ToolBar.SetOverflowMode(sep, hide1);\n\t\t\t\ttoolbar.Items.Insert(index, sep);\n\t\t\t}\n\t\t}\n\t\t\n\t\tbool _ModifyUserXmlButton(Action<XElement> modify) {\n\t\t\tif (App.Commands.LoadFiles() is not { } toolbars) return false;\n\t\t\tif (toolbars.FirstOrDefault(o => o.Name == toolbar.Name)?.Element(c.Name) is not { } x) return false;\n\t\t\tmodify(x);\n\t\t\tnew XElement(\"commands\", toolbars).SaveElem(App.Commands.UserFile);\n\t\t\treturn true;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Tools/DCustomizeContextMenu.cs",
    "content": "using Au.Controls;\nusing System.Windows.Controls;\nusing System.Windows;\nusing System.Windows.Controls.Primitives;\nusing System.Windows.Input;\nusing System.Windows.Media;\n\nnamespace LA;\n\nstatic class DCustomizeContextMenu {\n\t\n\tpublic static void Dialog(string menuName, string ownerName) {\n\t\tvar (file, text) = _GetFilePathAndText(menuName);\n\t\t\n\t\tvar b = new wpfBuilder(\"Customize context menu\").WinSize(550, 550).Columns(-1, -1);\n\t\tb.WinProperties(showInTaskbar: false);\n\t\t\n\t\tb.Row(40).Add(out KSciInfoBox info);\n\t\tinfo.aaaText = $\"\"\"\nLeft - all menu commands. Select a line and drag or copy to the right.\nRight - commands to add to the context menu of the {ownerName}. Separator -.\n\"\"\";\n\t\t\n\t\tb.Row(-1).xAddInBorder(out KScintilla t1);\n\t\tt1.AaInitReadOnlyAlways = true;\n\t\tb.Loaded += () => {\n\t\t\tt1.aaaText = _GetAllCommands(menuName, out int goTo);\n\t\t\tif (goTo > 0) t1.Call(Sci.SCI_LINESCROLL, 0, t1.aaaLineFromPos(false, goTo));\n\t\t};\n\t\t\n\t\tb.xAddInBorder(out KScintilla t2);\n\t\tt2.AaInitUseDefaultContextMenu = true;\n\t\tt2.aaaText = text;\n\t\t//b.Validation(o => never mind);\n\t\tt2.AaNotify += e => {\n\t\t\tunsafe {\n\t\t\t\tif (e.n.code == Sci.NOTIF.SCN_MODIFIED) {\n\t\t\t\t\t//trim tabs\n\t\t\t\t\tif (e.n.modificationType.Has(Sci.MOD.SC_MOD_INSERTCHECK) && e.n.length > 1 && e.n.textUTF8[0] == 9) {\n\t\t\t\t\t\tvar s = e.n.Text.RxReplace(@\"(?m)^\\t+\", \"\");\n\t\t\t\t\t\te.c.aaaSetString(Sci.SCI_CHANGEINSERTION, s);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t\t\n\t\tb.R.AddOkCancel();\n\t\tb.End();\n\t\tif (!b.ShowDialog(App.Wmain)) return;\n\t\t\n\t\tfilesystem.saveText(file, t2.aaaText);\n\t}\n\t\n\tstatic string _GetAllCommands(string menuName, out int goTo) {\n\t\tint go = -1;\n\t\tvar b = new StringBuilder();\n\t\t_Menu(Panels.Menu, 0);\n\t\tgoTo = go;\n\t\treturn b.ToString();\n\t\t\n\t\tvoid _Menu(ItemsControl sourceMenu, int level) {\n\t\t\tforeach (var o in sourceMenu.Items) {\n\t\t\t\tif (o is not MenuItem ms || ms.Command is not KMenuCommands.Command c) continue;\n\t\t\t\tbool isMenu = ms.Role is MenuItemRole.SubmenuHeader or MenuItemRole.TopLevelHeader;\n\t\t\t\tif (isMenu && go < 0 && c.Name == menuName) go = b.Length;\n\t\t\t\tb.Append('\\t', level).AppendLine(c.Name);\n\t\t\t\tif (isMenu) _Menu(ms, level + 1);\n\t\t\t}\n\t\t}\n\t}\n\t\n\tstatic (string path, string text) _GetFilePathAndText(string menuName) {\n\t\tstring file = AppSettings.DirBS + menuName + \" context menu.txt\", text = null;\n\t\tif (filesystem.exists(file)) text = filesystem.loadText(file);\n\t\telse if (menuName == \"Edit\") text = \"\"\"\nCut\nCopy\nPaste\n-\nSurround\nToggle_bookmark\n\"\"\";\n\t\treturn (file, text);\n\t}\n\t\n\tpublic static void AddToMenu(KWpfMenu m, string menuName) {\n\t\ttry {\n\t\t\tif (_GetFilePathAndText(menuName).text is not string text) return;\n\t\t\tvar a = text.Lines();\n\t\t\tbool needSeparator = false;\n\t\t\tforeach (var line in a) {\n\t\t\t\tvar s = line.Trim();\n\t\t\t\tif (s.Length == 0 || s.Starts('/')) continue;\n\t\t\t\tif (s == \"-\") {\n\t\t\t\t\tm.Separator();\n\t\t\t\t\tneedSeparator = false;\n\t\t\t\t} else if (App.Commands.TryFind(s, out var c)) {\n\t\t\t\t\tvar k = new MenuItem();\n\t\t\t\t\tc.CopyToMenu(k);\n\t\t\t\t\tm.Items.Add(k);\n\t\t\t\t\tneedSeparator = true;\n\t\t\t\t} else {\n\t\t\t\t\t//print.it($\"<>Unknown menu command: {s}.\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (needSeparator) m.Items.Add(new Separator());\n\t\t}\n\t\tcatch (Exception e1) { Debug_.Print(e1); }\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Tools/DEnumFiles.cs",
    "content": "using System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Controls.Primitives;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing System.Windows.Data;\nusing Au.Controls;\nusing ToolLand;\n\nnamespace LA;\n\nclass DEnumDir : KDialogWindow {\n\tpublic static void Dialog(string initFolder = null) {\n\t\tnew DEnumDir(initFolder).Show();\n\t}\n\t\n\tKTextBoxFile _tFolder;\n\tComboBox _cbGet;\n\tKTextExpressionBox _tFilter;\n\tKCheckBox _cArray, _cForeach, _cFileFilter, _cDirFilter, _cRecurse, _cIgnore, _cSymlink, _cRelative, _cSkipHidden, _cSkipHiddenSystem, _cWhere, _cOrderBy, _cThenBy, _cSelectPath, _cSelectOther/*, _cNet*/;\n\tPanel _p1, _p2;\n\tKSciCodeBox _code;\n\t\n\tconst string c_Where = \".Where(o => o.EditMe > EditMe)\",\n\t\tc_OrderBy = \".OrderBy(o => o.EditMe)\",\n\t\tc_ThenBy = \".ThenBy(o => o.EditMe)\",\n\t\tc_Select = \".Select(o => o.EditMe)\";\n\t\n\tDEnumDir(string initFolder = null) {\n\t\tInitWinProp(\"Get files in folder\", App.Wmain);\n\t\t\n\t\t_noeventValueChanged = true;\n\t\tvar b = new wpfBuilder(this).WinSize(600).Columns(0, 90, 90, -1);\n\t\t\n\t\tb.R.StartGrid().Columns(76, 76, 0, 0, -1);\n\t\tCancellationTokenSource cts = null;\n\t\tb.AddButton(\"Test\", async e => {\n\t\t\tif (cts == null) {\n\t\t\t\tvar s = _FormatCode(true);\n\t\t\t\tprint.clear();\n\t\t\t\te.Button.Content = \"Stop\";\n\t\t\t\tcts = new();\n\t\t\t\tvar r = await TUtil.RunTestCodeAsync(s, cts.Token);\n\t\t\t\tcts = null;\n\t\t\t\tif (!IsLoaded) return;\n\t\t\t\te.Button.Content = \"Test\";\n\t\t\t\tif (r?.isError == true) print.it($\"<><b>{r.header}<>\\r\\n{r.text}\");\n\t\t\t} else {\n\t\t\t\tcts.Cancel();\n\t\t\t}\n\t\t});\n\t\tClosing += (_, _) => { cts?.Cancel(); };\n\t\tb.AddButton(\"OK\", _ => {\n\t\t\tClose();\n\t\t\tvar s = _FormatCode();\n\t\t\tInsertCode.Statements(new(s, makeVarName1: true));\n\t\t});\n\t\t\n\t\tb.Add(out _cArray, \"Get array\").Checked().Tooltip(\"Store results in an array variable before starting to use them.\\nThen it is safe to delete/rename/move/copy the files.\\nBut then cannot start using results until all retrieved from the file system.\\nCheck if going to delete/rename/move/copy files.\\nUncheck if going to search in a potentially large directory.\")\n\t\t\t.Add(out _cForeach, \"Use foreach\").Checked();\n\t\t//b.Add(out _cNet, \"Use only .NET classes\");\n\t\tb.End();\n\t\tb.R.AddSeparator();\n\t\t\n\t\tb.R.Add(\"Folder\", out _tFolder, initFolder).Tooltip(\"Folder path.\\nPaste, drop or right-click.\").Focus();\n\t\t_tFolder.IsFolder = true;\n\t\t_tFolder.Unexpand = App.Settings.tools_pathUnexpand;\n\t\t\n\t\tb.R.Add(\"Get\", out _cbGet).Items(\"files|folders|all\").Margin(\"R12\");\n\t\t_cbGet.SelectionChanged += (_, _) => {\n\t\t\t_p1.Visibility = _cbGet.SelectedIndex == 2 ? Visibility.Hidden : Visibility.Visible;\n\t\t\t_p2.Visibility = _cbGet.SelectedIndex == 2 ? Visibility.Visible : Visibility.Hidden;\n\t\t};\n\t\t\n\t\tb.StartGrid().Columns(0, -1, 0);\n\t\t_p1 = b.Panel;\n\t\tb.Add(\"Name wildex\", out _tFilter).Tooltip(\"Optional file name wildex.\\nExamples:\\n*.txt\\n**m *.png||*.jpg\")\n\t\t\t.xAddControlHelpButton(\"articles/Wildcard expression\", \"Wildcard expression help\");\n\t\tb.End().Span(2);\n\t\t\n\t\tb.And(0).StartStack();\n\t\t_p2 = b.Panel;\n\t\tb.Add(out _cFileFilter, \"Use file filter\").Add(out _cDirFilter, \"Use folder filter\").Margin(\"L8\");\n\t\tb.End().Hidden();\n\t\t\n\t\tb.R.StartGrid<KGroupBox>(\"Flags\");\n\t\tb.R.Add(out _cRecurse, \"Include subfolders\");\n\t\tb.R.Add(out _cIgnore, \"Ignore inaccessible\").Margin(\"L22\").xBindCheckedVisible(_cRecurse);\n\t\tb.R.Add(out _cSymlink, \"Follow NTFS links\").Margin(\"L22\").xBindCheckedVisible(_cRecurse).Tooltip(\"Enumerate the target folder of NTFS links (symbolic links, volume mount points, etc)\");\n\t\tb.R.Add(out _cRelative, \"Get relative path\").Margin(\"L22\").xBindCheckedVisible(_cRecurse).Tooltip(\"Store relative path in the Name property\");\n\t\tb.R.Add(out _cSkipHidden, \"Skip hidden\");\n\t\tb.R.Add(out _cSkipHiddenSystem, \"Skip hidden system\");\n\t\tb.End().Span(3);\n\t\t\n\t\tb.StartGrid<KGroupBox>(\"Results\");\n\t\tb.R.Add(out _cWhere, \"Where\")\n\t\t\t.Tooltip($\"Add code '{c_Where}' to filter results by size, date, etc.\\nThen in the code editor you'll edit it.\");\n\t\tb.R.StartStack();\n\t\tb.Add(out _cOrderBy, \"OrderBy\")\n\t\t\t.Tooltip($\"Add code '{c_OrderBy}' to sort results by some property.\\nThen in the code editor you'll edit it. You can replace OrderBy with OrderByDescending.\");\n\t\tb.Add(out _cThenBy, \"ThenBy\").xBindCheckedVisible(_cOrderBy)\n\t\t\t.Tooltip($\"Adds code '{c_ThenBy}' to sort results by another property.\\nThen in the code editor you'll edit it. You can replace ThenBy with ThenByDescending, and append more ThenBy/ThenByDescending if need.\");\n\t\tb.End();\n\t\tb.R.StartStack();\n\t\tb.Add(out _cSelectPath, \"Select path\")\n\t\t\t.Tooltip($\"Get only paths, not all properties.\");\n\t\tb.Add(out _cSelectOther, \"Select other\")\n\t\t\t.Tooltip($\"Add code '{c_Select}' to change the type of results.\\nFor example select a property or several properties (tuple).\\nThen in the code editor you'll edit it.\");\n\t\tb.End();\n\t\tb.End();\n\t\t\n\t\tb.R.AddSeparator();\n\t\tb.Row(150..).xAddInBorder(out _code);\n\t\t\n\t\tb.End();\n\t\t_noeventValueChanged = false;\n\t\t\n\t\tb.Loaded += () => {\n\t\t\t_FormatCode();\n\t\t};\n\t\t_tFolder.UnexpandChanged += () => {\n\t\t\tApp.Settings.tools_pathUnexpand = _tFolder.Unexpand;\n\t\t\t_FormatCode();\n\t\t};\n\t}\n\t\n\tstatic DEnumDir() {\n\t\tTUtil.OnAnyCheckTextBoxValueChanged<DEnumDir>((d, o) => d._AnyCheckTextBoxComboValueChanged(o), comboToo: true);\n\t}\n\t\n\t//when checked/unchecked any checkbox, and when text changed of any textbox or combobox\n\tvoid _AnyCheckTextBoxComboValueChanged(object source) {\n\t\tif (!_noeventValueChanged) {\n\t\t\t_noeventValueChanged = true;\n\t\t\tif (source == _cSkipHidden && _cSkipHidden.IsChecked) _cSkipHiddenSystem.IsChecked = false;\n\t\t\tif (source == _cSkipHiddenSystem && _cSkipHiddenSystem.IsChecked) _cSkipHidden.IsChecked = false;\n\t\t\t_noeventValueChanged = false;\n\t\t\t\n\t\t\t_FormatCode();\n\t\t}\n\t}\n\tbool _noeventValueChanged;\n\t\n\tstring _FormatCode(bool forTest = false, bool onOK = false) {\n\t\tint getWhat = _cbGet.SelectedIndex;\n\t\tstring filter = _tFilter.Text;\n\t\tbool hasFilter = getWhat != 2 && !filter.NE();\n\t\t\n\t\tvar b = new StringBuilder();\n\t\tb.AppendLine($\"var folder = {_tFolder.GetCode()};\");\n\t\tb.Append(\"var a = \");\n\t\t//if (_cNet.IsChecked) {\n\t\t\n\t\t//} else {\n\t\tb.Append(\"filesystem.\").Append(getWhat switch { 0 => \"enumFiles\", 1 => \"enumDirectories\", _ => \"enumerate\" });\n\t\tb.Append(\"(folder\");\n\t\tif (hasFilter) b.AppendStringArg(filter);\n\t\tbool rec = _cRecurse.IsChecked;\n\t\tif (rec || _cSkipHidden.IsChecked || _cSkipHiddenSystem.IsChecked) {\n\t\t\tif (TUtil.FormatFlags(out var s1,\n\t\t\t\t(rec, FEFlags.AllDescendants),\n\t\t\t\t(rec && _cIgnore.IsChecked, FEFlags.IgnoreInaccessible),\n\t\t\t\t(rec && _cSymlink.IsChecked, FEFlags.RecurseNtfsLinks),\n\t\t\t\t(rec && _cRelative.IsChecked, FEFlags.NeedRelativePaths),\n\t\t\t\t(_cSkipHidden.IsChecked, FEFlags.SkipHidden),\n\t\t\t\t(_cSkipHiddenSystem.IsChecked, FEFlags.SkipHiddenSystem)\n\t\t\t\t)) b.AppendOtherArg(s1, hasFilter ? null : \"flags\");\n\t\t}\n\t\tif (getWhat == 2 && !forTest) {\n\t\t\tif (_cFileFilter.IsChecked) b.AppendOtherArg(\"\"\"f => f.Name.Ends(\".example\", true)\"\"\");\n\t\t\tif (_cDirFilter.IsChecked) b.AppendOtherArg(\"\"\"d => d.Name.Eqi(\"Example\") ? 0 : 3\"\"\");\n\t\t\t//rejected: errorHandler parameter.\n\t\t}\n\t\tb.Append(')');\n\t\tif (!forTest) {\n\t\t\tint len1 = b.Length;\n\t\t\tif (_cWhere.IsChecked) b.Append(\"\\r\\n\\t\").Append(c_Where);\n\t\t\tif (_cOrderBy.IsChecked) { b.Append(\"\\r\\n\\t\").Append(c_OrderBy); if (_cThenBy.IsChecked) b.Append(c_ThenBy); }\n\t\t\tif (_cSelectOther.IsChecked) b.Append(\"\\r\\n\\t\").Append(_cSelectPath.IsChecked ? \".Select(o => (path: o.FullPath, other: o.EditMe))\" : c_Select);\n\t\t\telse if (_cSelectPath.IsChecked) b.Append(\"\\r\\n\\t.Select(o => o.FullPath)\");\n\t\t\tif (b.Length > len1) b.Append(\"\\r\\n\\t\");\n\t\t}\n\t\t//}\n\t\t\n\t\tb.Append(_cArray.IsChecked && !forTest ? \".ToArray();\" : \";\");\n\t\tif (_cForeach.IsChecked || forTest) {\n\t\t\tb.Append(\"\\r\\nforeach \");\n\t\t\tif (!forTest && (_cSelectPath.IsChecked || _cSelectOther.IsChecked)) b.Append(\"(var f in a) {\\r\\n\\tprint.it(f);\");\n\t\t\telse b.Append(\"(var f in a) {\\r\\n\\tvar path = f.FullPath;\\r\\n\\tprint.it(path);\");\n\t\t\tb.Append(\"\\r\\n\\t\");\n\t\t\tif (forTest) b.Append(\"if (cancel.IsCancellationRequested) break;\");\n\t\t\tb.Append(\"\\r\\n}\");\n\t\t}\n\t\t\n\t\tvar R = b.ToString();\n\t\t\n\t\tif (!(forTest | onOK)) _code.AaSetText(R);\n\t\t\n\t\treturn R;\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Tools/DIcons.cs",
    "content": "using System.Security.Authentication;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Input;\nusing Au.Controls;\n\nnamespace LA;\n\nclass DIcons : KDialogWindow {\n\tpublic static void ShowSingle(string find = null, bool expandFileIcon = false, bool expandMenuIcon = false) {\n\t\tvar d = ShowSingle(() => new DIcons(randomizeColors: find == null, expandFileIcon, expandMenuIcon));\n\t\tif (find != null) d._tName.Text = find;\n\t}\n\t\n\tenum _Action {\n\t\tFileIcon = 1,\n\t\tMenuIcon,\n\t\t//InsertXamlVar,\n\t\t//InsertXamlField,\n\t\tCopyName,\n\t\tCopyXaml,\n\t\tExportXaml,\n\t\tExportIcon,\n\t\t_DebugCopyPng,\n\t}\n\t\n\tList<_Item> _a;\n\tstring _color = \"#000000\";\n\tRandom _random;\n\tint _dpi;\n\tCancellationTokenSource _ctsWindow = new(), _ctsTask;\n\tKTreeView _tv;\n\tKTextBox _tName;\n\tKScintilla _tCustom;\n\t\n\tDIcons(bool randomizeColors, bool expandFileIcon, bool expandMenuIcon) {\n\t\tInitWinProp(\"Icons\", App.Wmain);\n\t\t\n\t\tvar b = new wpfBuilder(this).WinSize(600, 600);\n\t\tb.Columns(-1, 0);\n\t\t\n\t\t//left - edit control and tree view\n\t\tb.Row(-1).StartGrid().Columns(-1, 0);\n\t\tb.Add(out _tName).Focus().Tooltip(@\"Search.\nPart of icon name, or wildcard expression.\nExamples: part, Part (match case), start*, *end, **rc regex case-sensitive.\nCan be Pack.Icon, like Material.Folder.\");\n\t\t_tName.PreviewKeyDown += (_, e) => { if (e.Key == Key.Enter) _AiSearch(); };\n\t\t\n\t\tb.xAddButtonIcon(EdIcons.AiSearch, _ => _AiSearch(), \"Use AI to find icons.\\nCan search by name or/and image.\\nType what you want in the text box or/and copy an icon image (for example in web browser, PNG format). Then click this button.\");\n\t\tb.Row(-1).xAddInBorder(out _tv);\n\t\t_tv.ImageBrush = System.Drawing.Brushes.White;\n\t\tb.End();\n\t\t\n\t\t//right - color picker, buttons, etc\n\t\tb.StartGrid().Columns(-1);\n\t\tb.Add(out KColorPicker colors).Align(HorizontalAlignment.Left);\n\t\tcolors.ColorChanged += color => {\n\t\t\t_random = null;\n\t\t\t_color = _ColorToString(color);\n\t\t\t_tv.Redraw();\n\t\t};\n\t\tb.StartStack();\n\t\t\n\t\tTextBox randFromTo = null, iconSizes = null;\n\t\t\n\t\tb.AddButton(\"Randomize colors\", _ => _RandomizeColors());\n\t\tb.Add(\"L %\", out randFromTo, \"30-70\").Width(50);\n\t\tb.End();\n\t\tb.AddSeparator().Margin(\"B20\");\n\t\t\n\t\t//rejected: double-clicking an icon clicks the last clicked button. Unclear and not so useful.\n\t\t\n\t\tb.AlsoAll((b, e) => { if (b.Last is Expander er) er.Header = new TextBlock { Text = er.Header as string, FontWeight = FontWeights.Bold }; });\n\t\t\n\t\tb.StartGrid<Expander>(out var exp1, \"Set file icon\").Columns(0, 70, 70, -1);\n\t\tb.R.Add(\"File\", out ComboBox cbIconOf).Items(\"Selected file(s)\", \"Script files\", \"Class files\", \"Folders\", \"Open folders\").Span(2);\n\t\tb.R.Add<Label>(\"Icon\");\n\t\tb.AddButton(out var bThis, \"This\", _ => { _SetFileIcon(true); /*lastAction = _Action.FileIcon;*/ }).Disabled();\n\t\tb.AddButton(\"Default\", _ => _SetFileIcon(false));\n\t\t//b.AddButton(\"Random\", null); //idea: set random icons for multiple selected files. Probably too crazy.\n\t\tb.AddButton(\"Show current\", _ => _ShowCurrent()).Margin(\"L20\");\n\t\tb.End();\n\t\tif (expandFileIcon) exp1.IsExpanded = true;\n\t\t\n\t\tb.StartGrid<Expander>(out var exp2, \"Menu/toolbar/etc icon\").Columns(0, 70, 70, -1);\n\t\tb.R.Add<Label>(\"Set icon of: \");\n\t\tb.AddButton(out var bMenuItem, \"Menu or toolbar item\", _ => _InsertCodeOrExport(_Action.MenuIcon)).Span(2).Disabled()\n\t\t\t.Tooltip(\"To assign the selected icon to a toolbar button or menu item,\\nin the code editor click its line (anywhere except action code)\\nand then click this button.\\n\\nIf the 'Customize' window is open, this button sets its Image text.\");\n\t\tb.R.Add<Label>(\"Copy text: \");\n\t\tb.AddButton(out var bCodeName, \"Name\", _ => _InsertCodeOrExport(_Action.CopyName)).Span(1).Disabled()\n\t\t\t.Tooltip(\"Shorter string than XAML.\\nCan be used with custom menus and toolbars,\\neditor menus and toolbars (edit Commands.xml),\\nScriptEditor.GetIcon, IconImageCache, ImageUtil,\\noutput tag <image>.\");\n\t\tb.AddButton(out var bCodeXaml, \"XAML\", _ => _InsertCodeOrExport(_Action.CopyXaml)).Span(1).Disabled();\n\t\tb.End();\n\t\tif (expandMenuIcon) exp2.IsExpanded = true;\n\t\t\n\t\tb.StartGrid<Expander>(\"Export to current workspace folder\").Columns(70, 70, 0, -1);\n\t\tb.AddButton(out var bExportXaml, \".xaml\", _ => _InsertCodeOrExport(_Action.ExportXaml)).Disabled();\n\t\tb.AddButton(out var bExportIco, \".ico\", _ => _InsertCodeOrExport(_Action.ExportIcon)).Disabled();\n\t\tb.Add(\"sizes\", out iconSizes, \"16,20,24,28,32,48,64\");\n\t\t//#if DEBUG\n\t\t//\t\tb.R.AddButton(\"[debug] Copy PNG\", _ => _InsertCodeOrExport(_Action._DebugCopyPng));\n\t\t//#endif\n\t\tb.End();\n\t\t\n\t\tb.StartStack<Expander>(\"Custom icon string\", vertical: true);\n\t\tb.StartGrid().Columns(30, 30, -1, 24, 40);\n\t\tb.AddButton(out var bCustomAppend, \"+\", _ => {\n\t\t\tif (_tv.SelectedItem is _Item k) _tCustom.aaaAppendText((_tCustom.aaaText is \"\" or [.., '\\n'] ? \"\" : \"\\r\\n\") + _GetSelectedIconString(), true, true);\n\t\t}).Tooltip(\"Append selected icon\").Align(y: VerticalAlignment.Bottom).Disabled();\n\t\tb.AddButton(\"?\", _ => HelpUtil.AuHelp(\"Au.More.ImageUtil.LoadWpfImageElement\")).Align(y: VerticalAlignment.Bottom);\n\t\tb.Skip().Add(out Border customPreview).Size(18, 18).Border().Align(y: VerticalAlignment.Bottom).Add(out Border customPreview2).Size(34, 34).Border().Margin(\"T-8\");\n\t\tb.End();\n\t\tb.xAddInBorder(out _tCustom); b.Height(60);\n\t\t_tCustom.AaHandleCreated += k => {\n\t\t\tk.aaaMarginSetWidth(1, 0, 1);\n\t\t};\n\t\t_tCustom.AaTextChanged += e => {\n\t\t\tFrameworkElement c = null, c2 = null;\n\t\t\ttry {\n\t\t\t\tif (_GetCustomIconString() is string s) {\n\t\t\t\t\tc = ImageUtil.LoadWpfImageElement(s);\n\t\t\t\t\tc2 = ImageUtil.LoadWpfImageElement(s);\n\t\t\t\t\tc2.Width = c2.Height = 32;\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch { }\n\t\t\tcustomPreview.Child = c;\n\t\t\tcustomPreview2.Child = c2;\n\t\t\t_EnableControls();\n\t\t};\n\t\tb.End();\n\t\t\n\t\tb.StartGrid<Expander>(\"Options\");\n\t\t\n\t\tvar darkContrast = b.R.xAddCheckText(\"High contrast color\", App.Settings.dicons_contrastColor, check: App.Settings.dicons_contrastUse);\n\t\tb.Tooltip(\"Append this color, like \\\"*Pack.Name selectedColor|thisColor\\\". This color is for high contrast dark theme. Can be #RRGGBB or color name.\");\n\t\tdarkContrast.c.CheckChanged += (_, _) => App.Settings.dicons_contrastUse = darkContrast.c.IsChecked;\n\t\tdarkContrast.t.TextChanged += (_, _) => {\n\t\t\tvar s = darkContrast.t.TextOrNull();\n\t\t\tif (s != null) for (int i = 0; i < 2; i++) { try { System.Windows.Media.ColorConverter.ConvertFromString(s); } catch { s = i == 0 ? \"#\" + s : null; } }\n\t\t\tApp.Settings.dicons_contrastColor = s;\n\t\t};\n\t\t\n\t\tb.R.Add(\"List background\", out ComboBox cBackground).Items(\"White|Black|Dark|Control\").Select(Math.Clamp(App.Settings.dicons_listColor, 0, 3));\n\t\tcBackground.SelectionChanged += (o, e) => {\n\t\t\tApp.Settings.dicons_listColor = cBackground.SelectedIndex;\n\t\t\t_SetListIconBrush();\n\t\t\t_tv.Redraw();\n\t\t};\n\t\t_SetListIconBrush();\n\t\tvoid _SetListIconBrush() {\n\t\t\t_tv.ImageBrush = App.Settings.dicons_listColor switch { 0 => System.Drawing.Brushes.White, 1 => System.Drawing.Brushes.Black, 2 => System.Drawing.Brushes.DimGray, _ => System.Drawing.SystemBrushes.Control }; ;\n\t\t}\n\t\t\n\t\tb.R.Add(\"List image size\", out KTextBox tIconSize, \"16\");\n\t\ttIconSize.TextChanged += (_, _) => {\n\t\t\tint k = tIconSize.Text.ToInt();\n\t\t\tif (k < 7 || k > 256) return;\n\t\t\t_tv.ImageSize = k;\n\t\t};\n\t\t\n\t\tb.End();\n\t\t\n\t\tb.Row(-1);\n\t\tb.R.xAddDialogHelpButtonAndF1(\"editor/Icons\").Align(\"R\");\n\t\tb.End();\n\t\t\n\t\tb.End();\n\t\t\n\t\tb.Loaded += () => {\n\t\t\t_dpi = Dpi.OfWindow(this);\n\t\t\t_OpenDB();\n\t\t\t\n\t\t\t_a = new(60000);\n\t\t\tforeach (var (table, _) in s_tables) {\n\t\t\t\tusing var stat = s_db.Statement(\"SELECT name FROM \" + table);\n\t\t\t\twhile (stat.Step()) {\n\t\t\t\t\tvar k = new _Item(this, table, stat.GetText(0));\n\t\t\t\t\t_a.Add(k);\n\t\t\t\t}\n\t\t\t}\n\t\t\t_a.Sort((a, b) => string.Compare(a._name, b._name, StringComparison.OrdinalIgnoreCase));\n\t\t\tif (randomizeColors) _RandomizeColors();\n\t\t\t_tv.SetItems(_a);\n\t\t};\n\t\t\n\t\t_tName.TextChanged += (_, _) => {\n\t\t\tstring name = _tName.Text, table = null;\n\t\t\tFunc<_Item, bool> f = null;\n\t\t\tbool select = false;\n\t\t\tif (!name.NE()) {\n\t\t\t\tif (select = IconString_.Parse(name, out var x)) {\n\t\t\t\t\ttable = x.pack;\n\t\t\t\t\tname = x.name;\n\t\t\t\t\tf = o => o._name == name && o._table == table;\n\t\t\t\t\tcolors.Color = x.color is ['#', ..] ? x.color[1..].ToInt(0, STIFlags.IsHexWithout0x) : 0;\n\t\t\t\t} else {\n\t\t\t\t\tif (name.RxMatch(@\"^(\\w+)\\.(.+)\", out var m)) (table, name) = (m[1].Value, m[2].Value);\n\t\t\t\t\twildex wild = null;\n\t\t\t\t\tStringComparison comp = StringComparison.OrdinalIgnoreCase;\n\t\t\t\t\tbool matchCase = name.RxIsMatch(\"[A-Z]\");\n\t\t\t\t\tif (wildex.hasWildcardChars(name)) {\n\t\t\t\t\t\ttry { wild = new wildex(name, matchCase && !name.Starts(\"**\")); }\n\t\t\t\t\t\tcatch { name = null; }\n\t\t\t\t\t} else if (matchCase) comp = StringComparison.Ordinal;\n\t\t\t\t\t\n\t\t\t\t\tif (name != null) f = o => (table == null || o._table.Eqi(table)) && (wild?.Match(o._name) ?? o._name.Contains(name, comp));\n\t\t\t\t}\n\t\t\t}\n\t\t\tvar e = f == null ? _a : _a.Where(f);\n\t\t\t_tv.SetItems(e);\n\t\t\tif (select && (select = e.Count() == 1)) _tv.Select(0);\n\t\t\t_EnableControls();\n\t\t};\n\t\t\n\t\t_tv.SelectedSingle += (o, i) => _EnableControls();\n\t\t\n\t\tb.WinSaved(App.Settings.wndpos.icons, o => App.Settings.wndpos.icons = o);\n\t\t\n\t\tvoid _EnableControls() {\n\t\t\tbool enable = _UseCustom ? customPreview.Child != null : _tv.SelectedIndex >= 0;\n\t\t\tbThis.IsEnabled = enable;\n\t\t\tbMenuItem.IsEnabled = enable;\n\t\t\t//bCodeVar.IsEnabled = enable;\n\t\t\t//bCodeField.IsEnabled = enable;\n\t\t\tbCodeXaml.IsEnabled = enable;\n\t\t\tbCodeName.IsEnabled = enable;\n\t\t\tbExportXaml.IsEnabled = enable;\n\t\t\tbExportIco.IsEnabled = enable;\n\t\t\tbCustomAppend.IsEnabled = _tv.SelectedIndex >= 0;\n\t\t}\n\t\t\n\t\tvoid _SetFileIcon(bool set) {\n\t\t\tstring icon = set ? _GetSelectedOrCustomIconString() : null;\n\t\t\tint si = cbIconOf.SelectedIndex;\n\t\t\tif (si == 0) {\n\t\t\t\tforeach (var v in FilesModel.TreeControl.SelectedItems) v.CustomIconName = icon;\n\t\t\t} else {\n\t\t\t\tswitch (si) {\n\t\t\t\tcase 1: App.Settings.icons.ft_script = icon; break;\n\t\t\t\tcase 2: App.Settings.icons.ft_class = icon; break;\n\t\t\t\tcase 3: App.Settings.icons.ft_folder = icon; break;\n\t\t\t\tcase 4: App.Settings.icons.ft_folderOpen = icon; break;\n\t\t\t\t}\n\t\t\t\tFilesModel.Redraw();\n\t\t\t}\n\t\t}\n\t\t\n\t\tvoid _ShowCurrent() {\n\t\t\t_tName.Text = cbIconOf.SelectedIndex switch {\n\t\t\t\t1 => App.Settings.icons.ft_script,\n\t\t\t\t2 => App.Settings.icons.ft_class,\n\t\t\t\t3 => App.Settings.icons.ft_folder,\n\t\t\t\t4 => App.Settings.icons.ft_folderOpen,\n\t\t\t\t_ => FilesModel.TreeControl.SelectedItems.FirstOrDefault()?.CustomIconName\n\t\t\t};\n\t\t}\n\t\t\n\t\tvoid _InsertCodeOrExport(_Action what) {\n\t\t\t//lastAction = what;\n\t\t\tbool useCustom = _UseCustom;\n\t\t\tvar k = useCustom ? null : _tv.SelectedItem as _Item;\n\t\t\tif (k == null && !useCustom) return;\n\t\t\t//string code = null;\n\t\t\tif (what == _Action.MenuIcon) {\n\t\t\t\tif (_GetSelectedOrCustomIconString() is string s) {\n\t\t\t\t\tif (DCustomize.AaSetImage(s)) return;\n\t\t\t\t\tInsertCode.SetMenuToolbarItemIcon(s);\n\t\t\t\t}\n\t\t\t} else if (what == _Action.CopyName) {\n\t\t\t\tif (_GetSelectedOrCustomIconString() is string s) {\n\t\t\t\t\tclipboard.text = s;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tstring xaml;\n\t\t\t\tif (useCustom) {\n\t\t\t\t\tif (!_GetIconFromDB(_GetCustomIconString(), out xaml)) return;\n\t\t\t\t} else {\n\t\t\t\t\tif (!_GetIconViewboxXamlFromDB(out xaml, k._table, k._name, _ItemColor(k))) return;\n\t\t\t\t}\n\t\t\t\txaml = xaml.Replace('\"', '\\'').RxReplace(@\"\\R\\s*\", \"\");\n\t\t\t\tswitch (what) {\n\t\t\t\t//case _Action.InsertXamlVar: code = $\"string icon{k._name} = \\\"{xaml}\\\";\"; break;\n\t\t\t\t//case _Action.InsertXamlField: code = $\"public const string {k._name} = \\\"{xaml}\\\";\"; break;\n\t\t\t\tcase _Action.CopyXaml: clipboard.text = xaml; break;\n\t\t\t\tcase _Action.ExportXaml: _Export(false); break;\n\t\t\t\tcase _Action.ExportIcon: _Export(true); break;\n\t\t\t\tcase _Action._DebugCopyPng: _DebugCopyPng(); break;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvoid _Export(bool ico) {\n\t\t\t\t\tvar folder = App.Model.CurrentFile?.Parent ?? App.Model.Root;\n\t\t\t\t\tstring name = useCustom && IconString_.Parse(_GetCustomIconString(), out var ics) ? ics.name : k?._name ?? \"name\";\n\t\t\t\t\tvar path = $\"{folder.FilePath}\\\\{name}{(ico ? \".ico\" : \".xaml\")}\";\n\t\t\t\t\tif (ico) {\n\t\t\t\t\t\tvar sizes = iconSizes.Text.Split_(',').Select(o => o.ToInt()).ToArray();\n\t\t\t\t\t\tvar e = ImageUtil.LoadWpfImageElement(xaml);\n\t\t\t\t\t\tImageUtil.ConvertWpfImageElementToIcon_(path, e, sizes);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tfilesystem.saveText(path, xaml);\n\t\t\t\t\t}\n\t\t\t\t\tvar fn = App.Model.ImportFileFromWorkspaceDir(path, new(folder, folder.Parent == null ? FNInsert.First : FNInsert.Last));\n\t\t\t\t\tif (fn == null) print.it(\"failed\");\n\t\t\t\t\telse print.it($\"<>Exported to <open>{fn.ItemPath}<>\");\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvoid _DebugCopyPng() { //note: this is only for debug. Ignores DPI.\n\t\t\t\t\tint size = _tv.ImageSize;\n\t\t\t\t\tvar path = xaml.RxReplace(@\"^.+?(<Path.+)</Viewbox>\", \"$1\");\n\t\t\t\t\tvar conv = AI.XamlIconConverter_.GetConverter(path);\n\t\t\t\t\tvar png = conv.ToPng(size, 96, System.Windows.Media.Brushes.White);\n\t\t\t\t\tnew clipboardData().AddBinary(png, ClipFormats.Png).SetClipboard();\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t//if (code != null) {\n\t\t\t//\tif (what is _Action.CopyName or _Action.CopyXaml) clipboard.text = code;\n\t\t\t//\telse if (what is _Action.InsertXamlVar or _Action.InsertXamlField) InsertCode.Statements(code);\n\t\t\t//\telse InsertCode.TextSimply(code);\n\t\t\t//}\n\t\t}\n\t\t\n\t\tvoid _RandomizeColors() {\n\t\t\t_random = new();\n\t\t\t//perf.first();\n\t\t\t//if (keys.isScrollLock) { //generate random HLS. Colors look not as good, maybe because the green-yellow range is too narrow and red-purple range too wide.\n\t\t\t//\tforeach (var v in _a) {\n\t\t\t//\t\tint L = _random.Next(40, 200);\n\t\t\t//\t\tdouble k = _random.NextDouble() * 15d; int S = 240 - (int)(k * k); //print.it(S); //generate less low-saturation colors\n\t\t\t//\t\tif (S < 60 && L > 60) S += 60; //avoid light-gray, because looks like disabled\n\t\t\t//\t\tv._color = ColorInt.FromHLS(_random.Next(0, 240), L, S, false);\n\t\t\t//\t}\n\t\t\t//} else {\n\t\t\tint iFrom = 0, iTo = 100; if (randFromTo.Text.RxMatch(@\"^(\\d+) *- *(\\d+)\", out var m)) { iFrom = m[1].Value.ToInt(); iTo = m[2].Value.ToInt(); }\n\t\t\tfloat briFrom = Math.Clamp(iFrom / 100f, 0f, 0.9f), briTo = Math.Clamp(iTo / 100f, briFrom + 0.05f, 1f);\n\t\t\tint middleL = ((briTo + briFrom) * 120f).ToInt();\n\t\t\tforeach (var v in _a) {\n\t\t\t\tint c = _random.Next(0, 0xffffff);\n\t\t\t\tvar (H, L, S) = ColorInt.ToHLS(c, false);\n\t\t\t\tif (S < 60 && L > 60) c = ColorInt.FromHLS(H, L, S += 60, false); //avoid light-gray, because looks like disabled\n\t\t\t\tvar bri = ColorInt.GetPerceivedBrightness(c, false);\n\t\t\t\tif (bri < briFrom || bri > briTo) c = ColorInt.FromHLS(H, middleL, S, false);\n\t\t\t\tv._color = c;\n\t\t\t}\n\t\t\t//}\n\t\t\t//perf.nw(); //4 ms\n\t\t\t_tv.Redraw();\n\t\t}\n\t\t\n\t\tstring _GetSelectedOrCustomIconString() {\n\t\t\tif (_GetCustomIconString() is string s) {\n\t\t\t\tif (null == IconString_.ParseAll(s)) { dialog.show(null, \"Invalid icon string.\", owner: this); return null; }\n\t\t\t\treturn s;\n\t\t\t}\n\t\t\treturn _GetSelectedIconString();\n\t\t}\n\t\t\n\t\tstring _GetSelectedIconString() {\n\t\t\tif (_tv.SelectedItem is not _Item k) return null;\n\t\t\tvar s = $\"*{k._table}.{k._name} {_ItemColor(k)}\";\n\t\t\tif (App.Settings.dicons_contrastUse && !App.Settings.dicons_contrastColor.NE()) s = s + \"|\" + App.Settings.dicons_contrastColor;\n\t\t\treturn s;\n\t\t}\n\t\t\n\t\tstring _GetCustomIconString() => _UseCustom ? _tCustom.aaaText.RxReplace(@\";?\\R\", \"; \").TrimEnd(\"; \") : null;\n\t}\n\t\n\tstatic string _ColorToString(int c) => \"#\" + c.ToS(\"X6\");\n\t\n\tstring _ItemColor(_Item k) => _random == null ? _color : _ColorToString(k._color);\n\t\n\tbool _UseCustom => !_tCustom.AaWnd.Is0 && _tCustom.aaaLen8 > 0;\n\t\n\tasync void _AiSearch() {\n\t\tstring query = _tName.Text;\n\t\tbyte[] png = _GetPngFromClipboard();\n\t\t\n\t\tif (query.NE() && png == null) {\n\t\t\tdialog.show(\"How to use AI to find icons\", \"Can search by icon name or/and image. Type what you want in the text box or/and copy an icon image (for example in web browser, PNG format). Then click the AI search button.\\n\\nAI embedding model: voyage-multimodal-3.\", owner: this);\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tif (!query.NE() && png != null) {\n\t\t\tswitch (popupMenu.showSimple(\"1 Text|2 Image (clipboard)|3 Text and image||0 Cancel\", owner: this)) {\n\t\t\tcase 1: png = null; break;\n\t\t\tcase 2: query = null; break;\n\t\t\tcase 3: break;\n\t\t\tdefault: return;\n\t\t\t}\n\t\t}\n\t\t\n\t\tstatic byte[] _GetPngFromClipboard() {\n\t\t\tbyte[] png;\n\t\t\tif (clipboardData.getBinary(ClipFormats.Png) is { } b) png = b;\n\t\t\telse if (clipboardData.getFiles() is { Length: 1 } a && a[0].Ends(\".png\", true)) png = filesystem.loadBytes(a[0]);\n\t\t\telse return null;\n\t\t\t\n\t\t\t//remove alpha. The Voyage model does not support it.\n\t\t\tusing var msIn = new MemoryStream(png);\n\t\t\tusing var src = new System.Drawing.Bitmap(msIn);\n\t\t\tusing var dst = new System.Drawing.Bitmap(src.Width, src.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);\n\t\t\tusing (var g = System.Drawing.Graphics.FromImage(dst)) {\n\t\t\t\tg.Clear(System.Drawing.Color.White);\n\t\t\t\tg.DrawImage(src, 0, 0, src.Width, src.Height);\n\t\t\t}\n\t\t\tusing var msOut = new MemoryStream();\n\t\t\tdst.Save(msOut, System.Drawing.Imaging.ImageFormat.Png);\n\t\t\treturn msOut.ToArray();\n\t\t}\n\t\t\n\t\tAI.AiModel.ApiKeys = App.Settings.ai_ak;\n\t\t\n\t\tvar emModel = new AI.ModelVoyageEmbedM();\n\t\t//var emModel = AI.AiModel.Models.OfType<AI.AiEmbeddingModel>().First(o => o.isMultimodal /*&& o.DisplayName == App.Settings.ai_modelIconSearch*/);\n\t\t//if (emModel == null) {\n\t\t//\t_AiSettingsError($\"Please go to Options > AI and select model for icon search.\");\n\t\t//\treturn;\n\t\t//}\n\t\t\n\t\tthis.IsEnabled = false;\n\t\ttry {\n\t\t\t_ctsTask?.Cancel();\n\t\t\t_ctsTask?.Dispose();\n\t\t\t_ctsTask = CancellationTokenSource.CreateLinkedTokenSource(_ctsWindow.Token);\n\t\t\tvar cancel = _ctsTask.Token;\n\t\t\t\n\t\t\tvar em = new AI.Embeddings(emModel);\n\t\t\tvar ems = await Task.Run(() => em.GetIconsEmbeddings(true, cancel));\n\t\t\t\n\t\t\tvar r1 = _tName.RectInScreen();\n\t\t\tusing var osd = osdText.showText(\"Searching.\\nClick to cancel.\", -1, new(r1.right, r1.bottom), showMode: OsdMode.ThisThread);\n\t\t\tosd.Clicked += (_, _) => { _ctsTask?.Cancel(); };\n\t\t\t\n\t\t\tAI.EmInput input = new(png == null ? [query] : query.NE() ? [png] : [query, png]);\n\t\t\tvar queryVector = await Task.Run(() => em.CreateEmbedding(input, cancel));\n\t\t\tvar a = em.GetTopMatches(queryVector, ems, take: 300).Select(o => new _Item(this, o.f.name)).ToArray();\n\t\t\t_tv.SetItems(a);\n\t\t}\n\t\tcatch (OperationCanceledException etc) { if (etc.InnerException is TimeoutException) print.it(etc.Message); }\n\t\tcatch (InvalidCredentialException) {\n\t\t\tvar api = emModel.api;\n\t\t\t_AiSettingsError($\"Please go to Options > AI and set the API key for {api}.\\nYou can create an API key in your account on the {api} website.\");\n\t\t}\n\t\tcatch (Exception e1) { print.it(e1); }\n\t\tfinally {\n\t\t\tthis.IsEnabled = true;\n\t\t\t_ = Task.Delay(500).ContinueWith(t => { GC.Collect(); 1.s(); GC.Collect(); });\n\t\t}\n\t\t\n\t\tvoid _AiSettingsError(string text) {\n\t\t\tif (!dialog.showOkCancel(\"AI search error\", text, owner: this)) return;\n\t\t\tDOptions.AaShow(DOptions.EPage.AI);\n\t\t}\n\t}\n\t\n\tprotected override void OnClosed(EventArgs e) {\n\t\t_ctsWindow.Cancel();\n\t\t_ctsWindow.Dispose();\n\t\t_ctsTask?.Dispose();\n\t\tbase.OnClosed(e);\n\t}\n\t\n\tprotected override void OnDpiChanged(DpiScale oldDpi, DpiScale newDpi) {\n\t\t_dpi = newDpi.PixelsPerInchX.ToInt();\n\t\tbase.OnDpiChanged(oldDpi, newDpi);\n\t}\n\t\n\tclass _Item : ITreeViewItem {\n\t\tDIcons _dialog;\n\t\tpublic string _table, _name;\n\t\tpublic int _color;\n\t\t\n\t\tpublic _Item(DIcons dialog, string table, string name) {\n\t\t\t_dialog = dialog;\n\t\t\t_table = table; _name = name;\n\t\t}\n\t\t\n\t\tpublic _Item(DIcons dialog, string tableDotName) {\n\t\t\t_dialog = dialog;\n\t\t\tint i = tableDotName.IndexOf('.');\n\t\t\t_table = tableDotName[..i]; _name = tableDotName[++i..];\n\t\t}\n\t\t\n\t\t//string ITreeViewItem.DisplayText => s_dialog._withCollection ? (_name + new string(' ', Math.Max(8, 40 - _name.Length * 2)) + \"(\" + _table + \")\") : _name;\n\t\tstring ITreeViewItem.DisplayText => _name + new string(' ', Math.Max(8, 40 - _name.Length * 2)) + \"(\" + _table + \")\";\n\t\t\n\t\tobject ITreeViewItem.Image {\n\t\t\t//note: don't store UIElement or Bitmap. They can use hundreds MB of memory and it does not make faster/better. Let GC dispose unused objects asap.\n\t\t\tget {\n\t\t\t\ttry {\n\t\t\t\t\t//using var p1 = perf.local();\n\t\t\t\t\tif (_GetIconViewboxXamlFromDB(out string xaml, _table, _name, _dialog._ItemColor(this))) {\n\t\t\t\t\t\t//p1.Next('d');\n\t\t\t\t\t\tint size = _dialog._tv.ImageSize;\n\t\t\t\t\t\treturn ImageUtil.LoadGdipBitmapFromXaml(xaml, _dialog._dpi, (size, size));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (Exception ex) { Debug_.Print(ex); }\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t}\n\t\n\tstatic sqlite s_db;\n\tstatic Dictionary<string, string> s_tables; //table->template, ~30 tables\n\t\n\t/// <summary>\n\t/// Returns true if <i>s</i> starts with \"*pack.name\" (possibly with library) and the pack exists.\n\t/// Fast, does not use the database (may use only the first time).\n\t/// </summary>\n\tpublic static bool PossiblyIconName(RStr s) {\n\t\tif (!IconString_.Detect(s, out var d)) return false;\n\t\t_OpenDB();\n\t\treturn s_tables.ContainsKey(s[d.pack.Range].ToString());\n\t}\n\t\n\tstatic void _OpenDB() {\n\t\tif (s_db == null) {\n\t\t\tvar db = s_db = new sqlite(folders.ThisAppBS + \"icons.db\", SLFlags.SQLITE_OPEN_READONLY);\n\t\t\tprocess.thisProcessExit += _ => db.Dispose();\n\t\t\ts_tables = new(StringComparer.OrdinalIgnoreCase);\n\t\t\tusing var st = s_db.Statement(\"SELECT * FROM _tables\");\n\t\t\twhile (st.Step()) {\n\t\t\t\t//print.it($\"--- {st.GetText(0)}\\r\\n{st.GetText(1)}\");\n\t\t\t\ts_tables.Add(st.GetText(0), st.GetText(1));\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/// <exception cref=\"Exception\">Failed to open database.</exception>\n\tstatic bool _GetIconPathXamlFromDB(out string xaml, string table, string name, string color, bool setColor = true) {\n\t\txaml = null;\n\t\t_OpenDB();\n\t\tif (!s_tables.TryGetValue(table, out var templ)) return false;\n\t\tif (!s_db.Get(out string data, $\"SELECT data FROM {table} WHERE name='{name}'\")) return false;\n\t\t//the SELECT is the slowest part. With prepared statement just slightly faster.\n\t\t\n\t\ttempl = s_rxColor.Replace(templ, setColor ? IconString_.NormalizeColor(color) : null, 1);\n\t\t\n\t\tint i = templ.Find(\" Data=\\\"{x:Null}\\\"\");\n\t\txaml = templ.ReplaceAt(i + 7, 8, data);\n\t\t\n\t\tif (xaml.Contains(\"\\\"{\")) return false;\n\t\treturn true;\n\t\t\n\t\t//Gets data directly from the big DB. Slightly slower than with a small DB file for used icons, but simpler.\n\t\t//Maybe not good to use an 18 MB DB directly, but I didn't notice something bad.\n\t\t//An alternative version could store XAML of used icons in a small DB file. When missing, gets from the big DB and copies to the small.\n\t\t//\tFaster 2 times (when the small DB is WAL). But both versions much faster than converting XAML to GDI+ bitmap. Same memory usage.\n\t\t//note: the big DB must have PRIMARY KEY. Don't need it with other version.\n\t}\n\tstatic regexp s_rxColor = new(@\"(?:Fill|Stroke)=\"\"\\K[^\"\"]*\");\n\t\n\t/// <exception cref=\"Exception\">Failed to open database.</exception>\n\tstatic bool _GetIconViewboxXamlFromDB(out string xaml, string table, string name, string color, bool setColor = true) {\n\t\tif (!_GetIconPathXamlFromDB(out xaml, table, name, color, setColor)) return false;\n\t\txaml = $\"{c_viewbox}{xaml}</Viewbox>\";\n\t\treturn true;\n\t}\n\t\n\tconst string c_viewbox = \"<Viewbox Width='16' Height='16' xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'>\";\n\t\n\t/// <exception cref=\"Exception\">Failed to open database.</exception>\n\tstatic bool _GetIconFromDB(string icon, out string xaml, bool forResource = false) {\n\t\txaml = null;\n\t\tif (IconString_.ParseAll(icon) is not { } a) return false;\n\t\t\n\t\tbool simple = false;\n\t\tif (a.Length == 1) {\n\t\t\tref IconString_ r = ref a[0];\n\t\t\tsimple = !(r.HasSize || r.HasMargin);\n\t\t\tif (!_GetIconViewboxXamlFromDB(out xaml, r.pack, r.name, r.color, simple && !forResource)) return false;\n\t\t} else {\n\t\t\tvar b = new StringBuilder(c_viewbox).AppendLine(\"<Grid>\");\n\t\t\tfor (int i = 0; i < a.Length; i++) {\n\t\t\t\tref IconString_ r = ref a[i];\n\t\t\t\tif (!_GetIconPathXamlFromDB(out xaml, r.pack, r.name, null, false)) return false;\n\t\t\t\tb.AppendLine(xaml);\n\t\t\t}\n\t\t\txaml = b.Append(\"</Grid></Viewbox>\").ToString();\n\t\t}\n\t\t\n\t\tbool ok = simple || forResource || IconString_.XamlSetColorSizeMargin(ref xaml, a);\n\t\t\n\t\t//print.qm2.write($\"\\r\\n---- {icon}\\r\\n{xaml}\");\n\t\t\n\t\treturn ok;\n\t}\n\t\n\t/// <param name=\"icon\">\n\t/// Icon name, like <c>\"*Pack.Icon color\"</c>, where color is like <c>#RRGGBB</c> or color name.\n\t/// Full supported format: <c>\"[*&lt;library&gt;]*pack.name[ color][ @size][ %margin][;more icons]\"</c>.\n\t/// If no color, sets black.\n\t/// </param>\n\t/// <returns>false if <i>icon</i> is null or invalid format or the icon does not exist.</returns>\n\tpublic static bool TryGetIconFromDB(string icon, out string xaml, bool forResource = false) {\n\t\t//using var p1 = perf.local();\n\t\ttry { if (_GetIconFromDB(icon, out xaml, forResource)) return true; }\n\t\tcatch (Exception e1) { Debug_.Print(e1); }\n\t\txaml = null;\n\t\treturn false;\n\t}\n\t\n\tpublic static string GetIconString(string s, EGetIcon what) {\n\t\tif (what != EGetIcon.IconNameToXaml) {\n\t\t\ts = App.Model.Find(s, silent: true)?.Image switch {\n\t\t\t\tstring v => v,\n\t\t\t\tIEnumerable<object> v => v.First() as string,\n\t\t\t\t_ => null\n\t\t\t};\n\t\t}\n\t\tif (what != EGetIcon.PathToIconName && s != null) TryGetIconFromDB(s, out s);\n\t\treturn s;\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Tools/DInputRecorder.cs",
    "content": "using System.ComponentModel;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Controls.Primitives;\nusing System.Windows.Media;\nusing Au.Controls;\nusing System.Windows.Threading;\n\n//FUTURE: record \"wait for UAC consent and then for normal desktop\".\n\nusing ToolLand;\n\nnamespace LA;\n\nclass DInputRecorder : KDialogWindow {\n\tpublic static void ShowRecorder() {\n\t\tif (s_showing) return;\n\t\t\n\t\tint t = WindowsHook.LowLevelHooksTimeout;\n\t\tif (t < 300) { //default 300, max 1000\n\t\t\tprint.it($\"Warning: incorrect Windows settings. The keyboard/mouse hook timeout is {t} ms. Should be 1000. Set it in Options -> OS, and restart computer. Now recording and triggers are unreliable.\");\n\t\t}\n\t\t\n\t\tvar ir = new DInputRecorder();\n#if SCRIPT\n\t\tir.ShowDialog();\n#else\n\t\tir.Show();\n#endif\n\t}\n\tstatic bool s_showing;\n\t\n\twnd _wThis, _wFore;\n\tbool _recordKeys, _recordText, _recordText2, _recordMouse, _recordWheel, _recordDrag, _recordMove;\n\tint _xyIn;\n\tListBox _list;\n\tScrollViewer _scroller;\n\tTextBox _tSpeed;\n\t\n\tconst string c_iconPause = \"*Material.PauseCircleOutline\" + EdIcons.blue,\n\t\tc_iconRetry = \"*BoxIcons.RegularReset\" + EdIcons.red,\n\t\tc_iconUndo = EdIcons.Undo;\n\t\n\tconst int c_xyControl = 1, c_xyScreen = 2;\n\t\n\tDInputRecorder() {\n\t\tTitle = \"Input recorder\";\n\t\tvar b = new wpfBuilder(this).WinSize((300, 200..400), (284, 270..)).Columns(80, -1);\n\t\tb.WinProperties(WindowStartupLocation.Manual, showActivated: false, showInTaskbar: false, topmost: true, style: WindowStyle.ToolWindow);\n\t\tb.Window.SizeToContent = 0;\n\t\t\n\t\tb.Row(-1).StartGrid().Columns(-1);\n\t\tb.Options(margin: new(1));\n\t\tb.Add(out KCheckBox cKeys, \"Keys\").Tooltip(\"Record keyboard\");\n\t\tb.Add(out KCheckBox cText, \"Text\").Margin(\"L8\").Tooltip(\"Record text keys as text, not as key names\");\n\t\tb.Add(out KCheckBox cText2, \"Text+\").Margin(\"L16\").Tooltip(\"Record Alt+Ctrl+key as text if possible\");\n\t\tb.Add(out KCheckBox cMouse, \"Mouse\").Tooltip(\"Record mouse clicks and optionally wheel and movements\");\n\t\tb.Add(out KCheckBox cWheel, \"Wheel\").Margin(\"L8\").Tooltip(\"Record mouse wheel\");\n\t\tb.Add(out KCheckBox cDrag, \"Drag\").Margin(\"L8\").Tooltip(\"Record mouse movements while a button is pressed\");\n\t\tb.Add(out KCheckBox cMove, \"Move\").Margin(\"L8\").Tooltip(\"Record mouse movements while buttons aren't pressed\");\n\t\tb.Add(out ComboBox cbIn).Items(\"Window|Control|Screen\").Tooltip(\"Mouse position relative to\").Margin(\"2\");\n\t\tcbIn.SelectionChanged += (_, _) => { _xyIn = cbIn.SelectedIndex; };\n\t\tcbIn.SelectedIndex = Math.Clamp(App.Settings.recorder.xyIn, 0, 2);\n\t\tb.Add<Label>(\"Speed\").And(34).Add(out _tSpeed, App.Settings.recorder.speed).Tooltip(\"If not empty, adds code that sets mouse/keys/text speed = this value\");\n\t\tb.Options(margin: new(2));\n\t\t\n\t\tcKeys.CheckChanged += (_, _) => {\n\t\t\t_recordKeys = cKeys.IsChecked;\n\t\t\t_recordText = _recordKeys ? cText.IsChecked : false;\n\t\t\tcText.IsEnabled = _recordKeys;\n\t\t\t_recordText2 = _recordKeys && _recordText ? cText2.IsChecked : false;\n\t\t\tcText2.IsEnabled = _recordKeys && _recordText;\n\t\t};\n\t\tcText.CheckChanged += (_, _) => {\n\t\t\t_recordText = _recordKeys && cText.IsChecked;\n\t\t\tcText2.IsEnabled = _recordKeys && _recordText;\n\t\t};\n\t\tcText2.CheckChanged += (_, _) => { _recordText2 = _recordKeys && _recordText && cText2.IsChecked; };\n\t\tcMouse.CheckChanged += (_, _) => {\n\t\t\t_recordMouse = cMouse.IsChecked;\n\t\t\t_recordWheel = _recordMouse ? cWheel.IsChecked : false;\n\t\t\t_recordDrag = _recordMouse ? cDrag.IsChecked : false;\n\t\t\t_recordMove = _recordMouse ? cMove.IsChecked : false;\n\t\t\tcWheel.IsEnabled = _recordMouse;\n\t\t\tcDrag.IsEnabled = _recordMouse;\n\t\t\tcMove.IsEnabled = _recordMouse;\n\t\t};\n\t\tcWheel.CheckChanged += (_, _) => { _recordWheel = _recordMouse && cWheel.IsChecked; };\n\t\tcDrag.CheckChanged += (_, _) => { _recordDrag = _recordMouse && cDrag.IsChecked; };\n\t\tcMove.CheckChanged += (_, _) => { _recordMove = _recordMouse && cMove.IsChecked; };\n\t\t\n\t\tif (App.Settings.recorder.keys) cKeys.IsChecked = true; else { cText.IsEnabled = false; cText2.IsEnabled = false; }\n\t\tif (App.Settings.recorder.text) cText.IsChecked = true; else cText2.IsEnabled = false;\n\t\tif (App.Settings.recorder.text2) cText2.IsChecked = true;\n\t\tif (App.Settings.recorder.mouse) cMouse.IsChecked = true; else { cWheel.IsEnabled = false; cDrag.IsEnabled = false; cMove.IsEnabled = false; }\n\t\tif (App.Settings.recorder.wheel) cWheel.IsChecked = true;\n\t\tif (App.Settings.recorder.drag) cDrag.IsChecked = true;\n\t\tif (App.Settings.recorder.move) cMove.IsChecked = true;\n\t\tthis.Closing += (_, _) => {\n\t\t\tApp.Settings.recorder.keys = cKeys.IsChecked;\n\t\t\tApp.Settings.recorder.text = cText.IsChecked;\n\t\t\tApp.Settings.recorder.text2 = cText2.IsChecked;\n\t\t\tApp.Settings.recorder.mouse = cMouse.IsChecked;\n\t\t\tApp.Settings.recorder.wheel = cWheel.IsChecked;\n\t\t\tApp.Settings.recorder.drag = cDrag.IsChecked;\n\t\t\tApp.Settings.recorder.move = cMove.IsChecked;\n\t\t\tApp.Settings.recorder.xyIn = _xyIn;\n\t\t\tApp.Settings.recorder.speed = _tSpeed.Text;\n\t\t};\n\t\t\n\t\tb.Row(-1).StartStack();\n\t\tb.xAddCheckIcon(out _, c_iconPause, \"Pause recording\", checkChanged: _Pause);\n\t\tb.xAddButtonIcon(c_iconRetry, _ => _Clear(), \"Clear the recorded data\");\n\t\tb.xAddButtonIcon(c_iconUndo, _ => _Undo(), \"Remove the last recorded event, or restore cleared data\");\n\t\tb.End();\n\t\t\n\t\tb.AddButton(out var bOK, \"OK\", _ => _OK(false)).Tooltip(\"Insert the code and close.\\nRight-click to copy to the clipboard.\");\n\t\tbOK.MouseRightButtonUp += (_, _) => _OK(true);\n\t\t//b.AddButton(\"Copy\", _=>_OK(true)).Tooltip(\"Copy the code to the clipboard.\\nStops recording and closes this window.\");\n\t\t//b.AddButton(\"Cancel\", _=>Close()); //can instead use x or Copy\n\t\t\n\t\tb.End();\n\t\t\n\t\tb.Add(out _list).Margin(5, 2, 2, 2);\n\t\t\n\t\tb.End();\n\t\t\n\t\tb.WinSaved(App.Settings.wndpos.recorder, o => App.Settings.wndpos.recorder = o);\n\t}\n\t\n\tstatic DInputRecorder() {\n\t\t//JIT some code that is called in hook proc\n\t\tvar w = App.Hmain;\n\t\tvar r1 = new _RecoWinFind { w = w, varName = 1, waitS = 1 };\n\t\tr1.FormatCode();\n\t\tvar r2 = new _RecoWinChild { w = w.Get.FirstChild, varName = 1, windowVarName = 1 };\n\t\tr2.FormatCode(w);\n\t\t//print.it(r1.code); print.it(r2.code);\n\t}\n\t\n\tprotected override void OnSourceInitialized(EventArgs e) {\n\t\tbase.OnSourceInitialized(e);\n\t\t\n\t\t_wThis = this.Hwnd();\n\t\t_scroller = _list.FindVisualDescendant(o => o is ScrollViewer) as ScrollViewer;\n\t\t//_wThis.MoveInScreen(^1, ^1);\n\t\ts_showing = true;\n\t\tApp.Hmain.ShowMinimized();\n\t\t\n\t\tDispatcher.InvokeAsync(() => { //set hooks when fully loaded\n\t\t\tif (_closed) return;\n\t\t\t_keyHook = WindowsHook.Keyboard(_KeyHook);\n\t\t\t_mouseHook = WindowsHook.Mouse(_MouseHook);\n\t\t\t//_weHook=new WinEventHook(new EEvent[] { EEvent.OBJECT_SHOW, EEvent.OBJECT_HIDE }, _WeHook, flags: EHookFlags.SKIPOWNTHREAD);\n\t\t}, DispatcherPriority.ApplicationIdle);\n\t}\n\t\n\tprotected override void OnClosed(EventArgs e) {\n\t\tbase.OnClosed(e);\n\t\t\n\t\t_closed = true;\n\t\t_keyHook.Dispose();\n\t\t_mouseHook.Dispose();\n\t\t//_weHook.Dispose();\n\t\ts_showing = false;\n\t\tApp.Hmain.ShowNotMinimized(); //BAD: flickers. With animation not so bad.\n\t\tApp.Hmain.ActivateL();\n\t}\n\tbool _closed;\n\t\n\tprotected override void OnActivated(EventArgs e) {\n\t\tif (wnd.active == _wThis) {\n\t\t\tif (_Last is _RecoMouseMoveBy) _Remove(^1);\n\t\t}\n\t\tbase.OnActivated(e);\n\t}\n\t\n\tenum _Event { Key, MButton, MMove }\n\t\n\tabstract record _Reco {\n\t\tpublic wnd w;\n\t\tpublic int time;\n\t}\n\t\n\trecord _RecoKey : _Reco {\n\t\tpublic KKey key;\n\t\tpublic KMod mod;\n\t\tpublic sbyte down; //1 down, -1 up, 0 down+up\n\t\tpublic int count;\n\t\t\n\t\tpublic override string ToString() {\n\t\t\tusing (new StringBuilder_(out var b)) {\n\t\t\t\tkeys.more.hotkeyToString(b, mod, key);\n\t\t\t\tif (down > 0) b.Append(\"*down\"); else if (down < 0) b.Append(\"*up\");\n\t\t\t\tif (count > 1) b.Append('*').Append(count);\n\t\t\t\treturn b.ToString();\n\t\t\t}\n\t\t}\n\t}\n\t\n\trecord _RecoChar : _Reco {\n\t\tpublic char c;\n\t\tpublic bool alt;\n\t\tpublic int count;\n\t\tpublic string s;\n\t\t\n\t\tpublic override string ToString() => (alt ? \"Alt+\" : null) + (s ?? c.ToString()) + (count > 1 ? (\"*\" + count) : null);\n\t\t\n\t\tpublic void FormatCode(StringBuilder b, bool first) {\n\t\t\tif (alt) {\n\t\t\t\tif (!first) b.Append(\"\\\", \\\"\");\n\t\t\t\tb.Append(\"Alt+\").Append(count > 1 ? '_' : '^');\n\t\t\t} else {\n\t\t\t\tif (first) b.Append(count > 1 ? '_' : '^');\n\t\t\t\telse if (count > 1) b.Append(\"\\\", \\\"_\");\n\t\t\t}\n\t\t\tFormatCodeSimple(b);\n\t\t}\n\t\t\n\t\tpublic void FormatCodeSimple(StringBuilder b) {\n\t\t\tif (s != null) b.Append(s.Escape());\n\t\t\telse {\n\t\t\t\tif (c is '\"' or '\\\\') b.Append('\\\\');\n\t\t\t\tb.Append(c);\n\t\t\t\tif (count > 1) b.Append('*').Append(count);\n\t\t\t}\n\t\t}\n\t}\n\t\n\trecord _RecoMouse : _Reco //mouse.move() and base of _RecoMouseX\n\t{\n\t\tpublic POINT p, pw; //in screen and in window\n\t\tpublic int varName; //0 if in screen, >0 if in window (_RecoWinFind with this varName), <0 if in control (_RecoWinChild with minus this varName)\n\t\t\n\t\tprotected string _VarName => varName == 0 ? null : (varName > 0 ? (\" in w\" + varName) : (\" in c\" + -varName));\n\t\t\n\t\tpublic override string ToString() => \"Mouse move\" + _VarName;\n\t\t\n\t\tpublic virtual void FormatCode(StringBuilder b) {\n\t\t\tb.Append(\"mouse.move(\");\n\t\t\t_FormatMoveArgs(b);\n\t\t\tb.Append(\");\");\n\t\t}\n\t\t\n\t\tprotected void _FormatMoveArgs(StringBuilder b) {\n\t\t\tif (varName != 0) {\n\t\t\t\tb.AppendFormat(\"{0}{1}, {2}, {3}\", varName > 0 ? 'w' : 'c', Math.Abs(varName), pw.x, pw.y);\n\t\t\t} else {\n\t\t\t\tb.AppendFormat(\"{0}, {1}\", p.x, p.y);\n\t\t\t}\n\t\t}\n\t}\n\t\n\trecord _RecoMouseButton : _RecoMouse {\n\t\tpublic MButton button;\n\t\tpublic sbyte down; //1 down, -1 up, 0 down+up\n\t\tpublic bool two, noXY, activated;\n\t\tpublic string image, comment;\n\t\t\n\t\tpublic override string ToString() => $\"Mouse {button.ToString().Lower()}{(down switch { 1 => \" down\", -1 => \" up\", _ => null })}{(two ? \"*2\" : null)}{_VarName}\"; //not useful:, {p.x}, {p.y}\n\t\t\n\t\tpublic override void FormatCode(StringBuilder b) {\n\t\t\tb.Append(\"mouse.\");\n\t\t\tbool ex = false; if (two) ex = button != MButton.Left; else ex = button is not (MButton.Left or MButton.Right);\n\t\t\tstring s;\n\t\t\tif (ex) s = \"clickEx\";\n\t\t\telse if (two) s = \"doubleClick\";\n\t\t\telse if (down > 0) s = button == MButton.Left ? \"leftDown\" : \"rightDown\";\n\t\t\telse if (down < 0) s = button == MButton.Left ? \"leftUp\" : \"rightUp\";\n\t\t\telse s = button == MButton.Left ? \"click\" : \"rightClick\";\n\t\t\tb.Append(s).Append('(');\n\t\t\tif (ex) {\n\t\t\t\tb.Append(\"MButton.\").Append(button);\n\t\t\t\tif (two) b.Append(\" | MButton.DoubleClick\"); else if (down > 0) b.Append(\" | MButton.Down\"); else if (down < 0) b.Append(\" | MButton.Up\");\n\t\t\t\tif (!noXY) b.Append(\", \");\n\t\t\t}\n\t\t\tif (!noXY) _FormatMoveArgs(b);\n\t\t\tb.Append(\");\");\n\t\t}\n\t\t\n\t\tpublic void FormatDrag(StringBuilder b, object dxy/*, KMod mod*/) {\n\t\t\tb.Append(\"mouse.drag(\");\n\t\t\t_FormatMoveArgs(b);\n\t\t\tswitch (dxy) {\n\t\t\tcase (int dx, int dy): b.AppendFormat(\", {0}, {1}\", dx, dy); break;\n\t\t\tcase string s: b.AppendFormat(\", \\\"{0}\\\"\", s); break;\n\t\t\t}\n\t\t\tif (button != MButton.Left) b.Append(\", MButton.\").Append(button);\n\t\t\t//never mind: could also format modifier keys, but it's quite difficult, eg can be pressed before or/and after the mouse button.\n\t\t\tb.Append(\");\");\n\t\t}\n\t}\n\t\n\trecord _RecoMouseWheel : _RecoMouse {\n\t\tpublic int ticks;\n\t\tpublic bool move;\n\t\t\n\t\tpublic override string ToString() => move && varName != 0 ? $\"Mouse wheel {_TicksToS}{_VarName}\" : $\"Mouse wheel {_TicksToS}\";\n\t\t\n\t\tstring _TicksToS => (ticks / 120d).ToS();\n\t\t\n\t\tpublic override void FormatCode(StringBuilder b) {\n\t\t\tif (move) { base.FormatCode(b); b.AppendLine(); }\n\t\t\tb.AppendFormat(\"mouse.wheel({0});\", _TicksToS);\n\t\t}\n\t}\n\t\n\trecord _RecoMouseMoveBy : _RecoMouse {\n\t\t//p - the last move coord\n\t\t//a - all relative coords\n\t\tpublic List<uint> a;\n\t\tpublic bool drag;\n\t\tstring _ostring;\n\t\t\n\t\tpublic override string ToString() => \"Mouse \" + (drag ? \"drag\" : \"move\");\n\t\t\n\t\tpublic string OffsetsString => _ostring ??= _GetOString();\n\t\t\n\t\tstring _GetOString() {\n\t\t\t//remove some points to make shorter and faster\n\t\t\t//in n1=a.Count;\n\t\t\tdouble prevAngle = 400, dist = 0;\n\t\t\tfor (int i = a.Count - 1; --i > 0;) {\n\t\t\t\tint dx = Math2.LoShort((nint)a[i]), dy = Math2.HiShort((nint)a[i]);\n\t\t\t\tdouble angle = Math2.AngleFromXY(dx, dy);\n\t\t\t\tdist += Math.Sqrt(dx * dx + dy * dy);\n\t\t\t\t//print.it(dx, dy, angle, dist);\n\t\t\t\tif (angle == prevAngle && dist < 30) {\n\t\t\t\t\ta.RemoveAt(i);\n\t\t\t\t\tint dx2 = Math2.LoShort((nint)a[i]), dy2 = Math2.HiShort((nint)a[i]);\n\t\t\t\t\ta[i] = (uint)Math2.MakeLparam(dx2 + dx, dy2 + dy);\n\t\t\t\t} else {\n\t\t\t\t\tprevAngle = angle;\n\t\t\t\t\tdist = 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\t//print.it(n1, a.Count);\n\t\t\treturn RecordingUtil.MouseToString(a, withSleepTimes: false);\n\t\t}\n\t\t\n\t\tpublic override void FormatCode(StringBuilder b) {\n\t\t\tb.AppendFormat(\"mouse.moveBy(\\\"{0}\\\");\", OffsetsString);\n\t\t}\n\t}\n\t\n\trecord _RecoWin : _Reco { //wnd.Activate, wnd.WaitForName, base of _RecoWinFind\n\t\tpublic int varName;\n\t\tpublic int waitS;\n\t\tpublic bool activate;\n\t\tpublic string code;\n\t\t\n\t\tpublic override string ToString() {\n\t\t\tusing (new StringBuilder_(out var b)) {\n\t\t\t\tb.AppendFormat(\"wnd w{0}: \", varName);\n\t\t\t\tif (waitS != 0) {\n\t\t\t\t\tb.Append(\"new name\");\n\t\t\t\t\tif (activate) b.Append(\", activate\");\n\t\t\t\t} else {\n\t\t\t\t\tb.Append(\"activate\");\n\t\t\t\t}\n\t\t\t\treturn b.ToString();\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic void FormatCode(string name = null) {\n\t\t\tusing (new StringBuilder_(out var b)) {\n\t\t\t\tif (waitS != 0) {\n\t\t\t\t\tb.AppendFormat(\"w{0}.WaitForName({1}\", varName, waitS).AppendStringArg(TUtil.EscapeWindowName(name, true)).Append(\");\");\n\t\t\t\t\tif (activate) b.AppendLine();\n\t\t\t\t}\n\t\t\t\tif (activate) b.AppendFormat(\"w{0}.Activate();\", varName);\n\t\t\t\tcode = b.ToString();\n\t\t\t}\n\t\t}\n\t}\n\t\n\trecord _RecoWinFind : _RecoWin {\n\t\tpublic string name;\n\t\t\n\t\tpublic void FormatCode(int ownerVar) {\n\t\t\tvar f = new TUtil.WindowFindCodeFormatter { VarWindow = \"w\" + varName };\n\t\t\tf.RecordWindowFields(w, waitS, activate, ownerVar > 0 ? \"w\" + ownerVar : null);\n\t\t\tcode = f.Format();\n\t\t\t//print.it(code);\n\t\t}\n\t\t\n\t\tpublic override string ToString() {\n\t\t\tusing (new StringBuilder_(out var b)) {\n\t\t\t\tb.AppendFormat(\"wnd w{0}: \", varName);\n\t\t\t\tb.Append(\"find\"); if (waitS != 0) b.Append(\"/wait\");\n\t\t\t\tif (activate) b.Append(\", activate\");\n\t\t\t\treturn b.ToString();\n\t\t\t}\n\t\t}\n\t}\n\t\n\trecord _RecoWinChild : _Reco {\n\t\tpublic string code;\n\t\tpublic int varName, windowVarName;\n\t\t\n\t\tpublic void FormatCode(wnd window) {\n\t\t\tvar f = new TUtil.WindowFindCodeFormatter { NeedWindow = false, VarWindow = \"w\" + windowVarName, VarControl = \"c\" + varName, Throw = true, waitW = \"1\" };\n\t\t\tf.RecordControlFields(window, w);\n\t\t\tcode = f.Format();\n\t\t\t//print.it(code);\n\t\t}\n\t\t\n\t\tpublic override string ToString() {\n\t\t\tusing (new StringBuilder_(out var b)) {\n\t\t\t\tb.AppendFormat(\"Find control c{0} in w{1}\", varName, windowVarName);\n\t\t\t\treturn b.ToString();\n\t\t\t}\n\t\t}\n\t}\n\t\n\trecord _RecoSleep : _Reco {\n\t}\n\t\n\tWindowsHook _keyHook, _mouseHook;\n\t//WinEventHook _weHook;\n\tList<_Reco> _a = new(), _aUndo;\n\tbool _paused;\n\tbool _canMove;\n\t\n\tvoid _Pause() {\n\t\tif (_paused ^= true) _keyToText.Clear();\n\t}\n\t\n\tvoid _Clear() {\n\t\tif (!_a.Any()) return;\n\t\t_aUndo = _a;\n\t\t_a = new();\n\t\t_keyToText.Clear();\n\t\t_list.Items.Clear();\n\t}\n\t\n\tvoid _Undo() {\n\t\tif (_a.Any()) {\n\t\t\tint i = _a.Count - 1;\n\t\t\tif (_a[i] is _RecoKey k && k.down < 0) { int j = _FindModDown(k.key, i); if (j >= 0) i = j; } //if last is Mod*up, remove all starting from Mod*down\n\t\t\tfor (int u = _a.Count; --u >= i;) _list.Items.RemoveAt(u);\n\t\t\t_a.RemoveRange(i, _a.Count - i);\n\t\t} else if (_aUndo != null) {\n\t\t\t_a = _aUndo;\n\t\t\t_aUndo = null;\n\t\t\tforeach (var r in _a) _list.Items.Add(_NewListItem(r));\n\t\t\t_scroller.ScrollToEnd();\n\t\t}\n\t}\n\t\n\tvoid _Add(_Reco r, int time, wnd w, wnd wTL = default) {\n\t\tr.w = w;\n\t\tr.time = time != 0 ? time : Environment.TickCount;\n\t\tint varName = _AddWinIfNeed(r, w, wTL);\n\t\t\n\t\tif (r is _RecoMouse m) {\n\t\t\tm.varName = varName;\n\t\t\tvar p = m.p;\n\t\t\tif (_xyIn != c_xyScreen && w.MapScreenToClient(ref p)) m.pw = p;\n\t\t}\n\t\t\n\t\t_a.Add(r);\n\t\t_list.Items.Add(_NewListItem(r));\n\t\t_scroller.ScrollToEnd();\n\t\t_canMove = true;\n\t}\n\t\n\tstatic ListBoxItem _NewListItem(_Reco r) {\n\t\tvar v = new ListBoxItem { Content = r.ToString() };\n\t\tvar b = r switch { _RecoMouse => Brushes.Blue, _RecoKey or _RecoChar { alt: true } => Brushes.Green, _RecoChar => Brushes.DarkOrange, _ => null };\n\t\tif (b != null) v.Foreground = b;\n\t\treturn v;\n\t}\n\t\n\tint _AddWinIfNeed(_Reco r, wnd w, wnd wTL) { //if _RecoMouse, w may be control, and wTL is always the top-level window; else w is top-level window, and wTL not used\n\t\tif (r is not (_RecoKey or _RecoChar or _RecoMouse) || w.Is0) return 0;\n\t\twnd wChild = default;\n\t\tbool activate = false;\n\t\tif (r is _RecoMouse) { //button, wheel or move\n\t\t\tif (_xyIn == c_xyScreen || (r is _RecoMouseMoveBy) || (r is _RecoMouseWheel mw && !mw.move)) return 0;\n\t\t\tif (r is _RecoMouseButton mb && mb.down < 0 && _Last is _RecoMouseMoveBy mm && mm.drag && _a[_a.Count - 2] is _RecoMouseButton mb2 && mb2.down > 0 && mb2.w == w) { mb.noXY = true; return 0; } //if was drag, just release button (no window, x, y)\n\t\t\tif (wTL != w) { wChild = w; w = wTL; }\n\t\t\tactivate = (r is not _RecoMouseButton || !_a.Any()) && w.IsActive;\n\t\t} else { //key or char\n\t\t\tactivate = true;\n\t\t}\n\t\tif (activate && _IsWinActivated(w)) activate = false;\n\t\t\n\t\tint varName = _AddWin(w, activate, r is not _RecoMouse, r.time);\n\t\t\n\t\tif (!wChild.Is0) {\n\t\t\tvar rc = _FindWinChild(wChild);\n\t\t\tif (rc == null) {\n\t\t\t\tint vi = (_LastOfType<_RecoWinChild>()?.varName ?? 0) + 1;\n\t\t\t\tvar z = new _RecoWinChild { varName = vi, windowVarName = varName };\n\t\t\t\t_Add(z, r.time, wChild);\n\t\t\t\tTask.Run(() => z.FormatCode(w)).Wait(30); //may use ***elmName (slow and may fail in hook proc)\n\t\t\t\tvarName = -vi;\n\t\t\t} else {\n\t\t\t\tvarName = -rc.varName;\n\t\t\t}\n\t\t}\n\t\t\n\t\treturn varName;\n\t}\n\t\n\t/// <summary>\n\t/// If w is new, adds wnd.find and optionally Activate.\n\t/// Else if name changed, adds w.WaitForName and optionally Activate.\n\t/// Else if activate, adds Activate.\n\t/// Else just returns 0.\n\t/// If onKey, does nothing if !activate and name not changed.\n\t/// </summary>\n\t/// <returns>Variable name.</returns>\n\tint _AddWin(wnd w, bool activate, bool onKey = false, int time = 0) {\n\t\tvar name = w.Name;\n\t\t_RecoWin g = null;\n\t\tvar rw = _FindWinFind(w);\n\t\tint varName = rw?.varName ?? 0;\n\t\tif (rw == null) { //find\n\t\t\tif (!activate && onKey) return 0;\n\t\t\tvarName = (_LastOfType<_RecoWinFind>()?.varName ?? 0) + 1;\n\t\t\tvar z = new _RecoWinFind { w = w, name = name, varName = varName, activate = activate, waitS = _a.Any() ? 10 : 0 };\n\t\t\tint owner = 0; if (!w.Get.Owner.Is0 || w.IsTopmost) owner = _FindWinFind(o => w.IsOwnedBy(o, 2))?.varName ?? 0;\n\t\t\tz.FormatCode(owner);\n\t\t\tg = z;\n\t\t} else if (name.Trim('*') != rw.name.Trim('*')) { //wait for new name\n\t\t\trw.name = name;\n\t\t\tg = new _RecoWin { activate = activate, varName = rw.varName, waitS = 30 };\n\t\t\tg.FormatCode(name);\n\t\t} else if (activate) { //just activate\n\t\t\tg = new _RecoWin { activate = true, varName = rw.varName };\n\t\t\tg.FormatCode();\n\t\t} else {\n\t\t\tif (onKey) return 0;\n\t\t}\n\t\tif (g != null) _Add(g, time, w);\n\t\treturn varName;\n\t}\n\t\n\tvoid _Remove(Index i) {\n\t\tint j = i.GetOffset(_a.Count);\n\t\t_a.RemoveAt(j);\n\t\t_list.Items.RemoveAt(j);\n\t}\n\t\n\t/// <summary>\n\t/// Redraws _list.Items[i]. If r not null, replaces _a[i].\n\t/// </summary>\n\tvoid _Replace(Index i, _Reco r = null) {\n\t\tint j = i.GetOffset(_a.Count);\n\t\tif (r != null) _a[j] = r; else r = _a[j];\n\t\t_list.Items[j] = _NewListItem(r);\n\t}\n\t\n\t_Reco _Last => _a.Count > 0 ? _a[^1] : null;\n\t\n\tT _LastOfType<T>() where T : _Reco {\n\t\tfor (int i = _a.Count; --i >= 0;) if (_a[i] is T r) return r;\n\t\treturn null;\n\t}\n\t\n\t/// <summary>\n\t/// Finds the last modifier down event for modKey key. Searches in _a from i-1, reverse.\n\t/// Returns -1 if not found or if found modKey non-down event.\n\t/// </summary>\n\tint _FindModDown(KKey modKey, int i) {\n\t\twhile (--i >= 0) if (_a[i] is _RecoKey rk && rk.key == modKey) return rk.down > 0 ? i : -1;\n\t\treturn -1;\n\t}\n\t\n\t_RecoWinFind _FindWinFind(wnd w) {\n\t\tfor (int i = _a.Count; --i >= 0;) if (_a[i] is _RecoWinFind f && f.w == w) return f;\n\t\treturn null;\n\t}\n\t\n\t_RecoWinFind _FindWinFind(Func<wnd, bool> func) {\n\t\tfor (int i = _a.Count; --i >= 0;) if (_a[i] is _RecoWinFind f && func(f.w)) return f;\n\t\treturn null;\n\t}\n\t\n\t_RecoWinChild _FindWinChild(wnd w) {\n\t\tfor (int i = _a.Count; --i >= 0;) if (_a[i] is _RecoWinChild f && f.w == w) return f;\n\t\treturn null;\n\t}\n\t\n\tbool _IsWinActivated(wnd w) {\n\t\tfor (int i = _a.Count; --i >= 0;) {\n\t\t\tswitch (_a[i]) {\n\t\t\tcase _RecoWin r:\n\t\t\t\tif (r.activate) return r.w == w;\n\t\t\t\tbreak;\n\t\t\tcase _RecoMouseButton r:\n\t\t\t\tif (r.activated) return r.varName > 0 ? r.w == w : r.w.Window == w;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\t\n\tKeyToTextConverter _keyToText = new();\n\t\n\tvoid _KeyHook(HookData.Keyboard k) {\n\t\tif (_paused || !_recordKeys) return;\n\t\tvar w = wnd.active;\n\t\tif (w == _wThis) return;\n\t\t\n\t\tif (k.Mod != 0) {\n\t\t\tif (k.IsUp) {\n\t\t\t\tif (!keys.isPressed(k.Key)) return; //eg Ctrl up when switching windows with different keyboard layouts\n\t\t\t\tif (_RemoveMod(k.Key, k.Mod)) return;\n\t\t\t} else {\n\t\t\t\tif (keys.isPressed(k.Key)) return; //auto-repeated. If AltGr, auto-repeats pairs, like Alt Ctrl Alt Ctrl.\n\t\t\t}\n\t\t\t_Add(new _RecoKey { key = k.Key, down = (sbyte)(k.IsUp ? -1 : 1), count = 1 }, k.time, w);\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tif (k.IsUp) return;\n\t\t\n\t\tif (k.Key == KKey.Packet) {\n\t\t\tif (!_recordText) return;\n\t\t\t_Add(new _RecoChar { c = (char)k.scanCode, count = 1 }, k.time, w);\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tif (_recordText && _Text()) return;\n\t\t\n\t\tif (_Last is _RecoKey rk && rk.key == k.Key && rk.mod == 0 && rk.down == 0 && rk.count < 1000) { //*count\n\t\t\trk.count++;\n\t\t\t_Replace(^1);\n\t\t\treturn;\n\t\t}\n\t\t\n\t\t_Add(new _RecoKey { key = k.Key, count = 1 }, k.time, w);\n\t\t\n\t\tbool _Text() {\n\t\t\tvar mod = keys.getMod();\n\t\t\tif (!KeyToTextConverter.IsPossiblyChar_(mod, k.Key) || k.Key is KKey.Enter or KKey.Tab) return false;\n\t\t\tvar wf = wnd.focused; if (wf.Is0) wf = w;\n\t\t\tif (!_keyToText.Convert(out var t, k.Key, k.scanCode, mod, wf.ThreadId)) return false;\n\t\t\tif (t == default) return true; //dead-key\n\t\t\tif (t.c == ' ' && k.Key == KKey.Space && _Last is not _RecoChar) return false; //record Space as key if it isn't followed by a char\n\t\t\tvar mod2 = mod & ~KMod.Shift;\n\t\t\tif (!_recordText2 && mod2 == (KMod.Alt | KMod.Ctrl)) if (!keys.isPressed(KKey.RAlt) || keys.isPressed(KKey.RCtrl)) return false; //AltGr = RAlt+LCtrl\n\t\t\tbool alt = mod2 == KMod.Alt;\n\t\t\t\n\t\t\tif (_a.Count > 0 && t.c != default) { //*count\n\t\t\t\tif (_IsSame(^1)) {\n\t\t\t\t\tvar rc = _Last as _RecoChar;\n\t\t\t\t\tif (rc.count > 1) {\n\t\t\t\t\t\trc.count++;\n\t\t\t\t\t\t_Replace(^1);\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t} else if (_a.Count > 3 && _IsSame(^2) && _IsSame(^3) && _IsSame(^4)) {\n\t\t\t\t\t\t_Remove(^2); _Remove(^2); _Remove(^2);\n\t\t\t\t\t\trc.count = 5;\n\t\t\t\t\t\t_Replace(^1);\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbool _IsSame(Index i) {\n\t\t\t\t\treturn _a[i] is _RecoChar rc && rc.c == t.c && rc.alt == alt && rc.count < 1000;\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t_Add(new _RecoChar { c = t.c, s = t.s, alt = alt, count = 1 }, k.time, w);\n\t\t\t\n\t\t\t//why Text+ checkbox exists when we can detect AltGr:\n\t\t\t//\t1. OS bugs. Eg AltGr stops working after an Open/Save dialog etc.\n\t\t\t//\t2. Possibly not all keyboards have RAlt.\n\t\t\t//\t3. Somebody may want to use Ctrl+Alt even if AltGr exists and works.\n\t\t\t//\t4. There must be a reason why Alt+Ctrl is an alternative for AltGr.\n\t\t\treturn true;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// On modifier up converts sequence like {Ctrl*down, Alt*down, K, Alt*up, Ctrl*Up} to {Ctrl+Alt+K} or {text}.\n\t/// If returns false, the caller does not record the Mod*up event.\n\t/// </summary>\n\tbool _RemoveMod(KKey key, KMod mod) {\n\t\t//print.it(\"----\", key, mod);\n\t\t//print.it(_a);\n\t\tint nKeys = 0, nChars = 0, iKey = -1;\n\t\tfor (int i = _a.Count; --i >= 0;) {\n\t\t\tswitch (_a[i]) {\n\t\t\tcase _RecoKey d:\n\t\t\t\t//print.it(d);\n\t\t\t\tif (d.key == key) {\n\t\t\t\t\tif (d.down != 1) return false; //Mod*up without Mod*down\n\t\t\t\t\tif (nKeys + nChars == 0) { //modifier(s) pressed-released without a key etc in between. Replace Mod*down with Mod.\n\t\t\t\t\t\tif (i == _a.Count - 1) {\n\t\t\t\t\t\t\td.down = 0;\n\t\t\t\t\t\t\t_Replace(i);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tfor (int j = _a.Count; --j > i;) if (_a[j] is _RecoKey u && u.down != -1) _AddMod(j);\n\t\t\t\t\t\t\t_Remove(i);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else { //remove Mod*down if Mod+key or Mod+characters\n\t\t\t\t\t\tif (nKeys > 0) { //else all characters\n\t\t\t\t\t\t\tif (nKeys + nChars > 1) { //Mod + multiple keys or Mod + keys and chars. Now display {Mod*down, A, B, Mod*up}. Later will convert to {Mod+(A B)}.\n\t\t\t\t\t\t\t\tif (mod == KMod.Alt) while (++i < _a.Count && _a[i] is _RecoChar rc) { rc.alt = false; _Replace(i); }\n\t\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (nKeys == 1 && iKey >= 0) _AddMod(iKey); //Mod + key\n\t\t\t\t\t\t}\n\t\t\t\t\t\t_Remove(i);\n\t\t\t\t\t}\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tif (d.down == 0) {\n\t\t\t\t\tnKeys++; iKey = i;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase _RecoChar d:\n\t\t\t\tif (d.alt) nKeys++; else nChars++;\n\t\t\t\tbreak;\n\t\t\tdefault: return false;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t\t\n\t\tvoid _AddMod(int i) {\n\t\t\tvar r = _a[i] as _RecoKey;\n\t\t\tif (r.key != key) r.mod |= mod;\n\t\t\t_Replace(i);\n\t\t}\n\t}\n\t\n\tvoid _MouseHook(HookData.Mouse k) {\n\t\t//deactivate this window when mouse leaves it\n\t\tvar wa = wnd.active;\n\t\tif (k.IsMove) {\n\t\t\tif (wa != _wThis) _wFore = wa;\n\t\t\telse if (_wFore.IsVisible && !_wThis.Rect.Contains(k.pt) && Api.GetCapture().Is0) {\n\t\t\t\tApi.SetForegroundWindow(_wFore);\n\t\t\t\t_wFore = default;\n\t\t\t\t_canMove = false; //don't record mouse move until something new recorded\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (_paused || !_recordMouse || wa == _wThis) return;\n\t\t\n\t\tif (k.IsButton) {\n\t\t\t//using var p1=perf.local();\n\t\t\t\n\t\t\tif (!_WinFromXY(out wnd w, out wnd wTL)) return;\n\t\t\t\n\t\t\tif (k.IsButtonUp) {\n\t\t\t\tif (_Last is _RecoMouseButton m && m.button == k.Button && m.down > 0 && !_PointIsDrag(m.p, k.pt)) {\n\t\t\t\t\tif (_a.Count > 1 && _a[^2] is _RecoMouseButton u && u.button == m.button && u.down == 0 && !u.two && _PointIsDouble(u.p, m.p) && m.time - u.time <= Api.GetDoubleClickTime()) {\n\t\t\t\t\t\tu.two = true;\n\t\t\t\t\t\t_Remove(^1);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tm.down = 0;\n\t\t\t\t\t\t\n\t\t\t\t\t\t//if clicked a taskbar and it activates a window, replace the mouse.click with wnd.Activate()\n\t\t\t\t\t\tif (m.button == MButton.Left && wTL.ClassNameIs(\"Shell_*TrayWnd\") && wTL.ProgramName.Eqi(\"explorer.exe\")) {\n\t\t\t\t\t\t\ttimer.after(25, _ => {\n\t\t\t\t\t\t\t\tvar w1 = wnd.active;\n\t\t\t\t\t\t\t\tif (w1 != wTL && !w1.Is0 && _Last == m) {\n\t\t\t\t\t\t\t\t\t_Remove(^1); //remove mouse.click(w)\n\t\t\t\t\t\t\t\t\tif (_Last is _RecoWinChild rc && rc.w == w) _Remove(^1); //remove wnd.Child(w)\n\t\t\t\t\t\t\t\t\tif (_Last is _RecoWin rw && rw.w == wTL) _Remove(^1); //remove wnd.find(wTL)\n\t\t\t\t\t\t\t\t\t_AddWin(w1, true);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t_Replace(^1);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tvar r = new _RecoMouseButton { button = k.Button, down = (sbyte)(k.IsButtonDown ? 1 : -1), p = k.pt };\n\t\t\t_Add(r, k.time, w, wTL);\n\t\t\t\n\t\t\tif (k.IsButtonDown) {\n\t\t\t\t//don't record w.Activate() on next key etc if this click activates w\n\t\t\t\tvar ww = w.Window;\n\t\t\t\tif (!ww.Is0 && !ww.IsActive) {\n\t\t\t\t\ttimer.after(10, _ => r.activated = ww.IsActive);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t//comments\n\t\t\t\t//p1.Next();\n\t\t\t\tvar p = k.pt;\n\t\t\t\tvar ta = new Task[2] {\n\t\t\t\t\tTask.Run(() => r.image=TUtil.MakeScreenshot(p)),\n\t\t\t\t\tTask.Run(_Comment)\n\t\t\t\t};\n\t\t\t\tTask.WaitAll(ta, Math.Clamp(WindowsHook.LowLevelHooksTimeout - 150, 30, 150));\n\t\t\t\tvoid _Comment() {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tvar e = elm.fromXY(p, EXYFlags.PreferLink /*| EXYFlags.NotInProc*/); //with NotInProc in some cases works not as good, eg no Name of 32-bit classic toolbar buttons\n\t\t\t\t\t\tvar role = e?.Role; if (role.NE()) return;\n\t\t\t\t\t\tvar name = e.Name;\n\t\t\t\t\t\tif (!name.NE()) name = name.Escape(limit: 30, quote: true);\n\t\t\t\t\t\telse if (role is \"CLIENT\" or \"WINDOW\") return;\n\t\t\t\t\t\tr.comment = $\" /* {role.Lower()} {name} */\";\n\t\t\t\t\t}\n\t\t\t\t\tcatch (Exception e1) { Debug_.Print(e1); }\n\t\t\t\t}\n\t\t\t\t//CONSIDER: also screenshot on button up, if not joined with down and not in same point in window.\n\t\t\t\t//\tBut then usually records single line (mouse.drag).\n\t\t\t}\n\t\t} else if (k.IsWheel) {\n\t\t\tif (!_recordWheel) return;\n\t\t\tif (!_WinFromXY(out wnd w, out wnd wTL)) return;\n\t\t\t\n\t\t\tvar rm = _LastOfType<_RecoMouse>();\n\t\t\tbool move = rm == null || rm.p != k.pt || rm is _RecoMouseMoveBy;\n\t\t\t_Add(new _RecoMouseWheel { ticks = k.WheelValue, p = k.pt, move = move }, k.time, w, wTL);\n\t\t} else if (_canMove) { //IsMove\n\t\t\tbool drag = mouse.isPressed(MButtons.Left | MButtons.Right | MButtons.Middle);\n\t\t\tif (!(drag ? _recordDrag : _recordMove)) return;\n\t\t\tif (_a.LastOrDefault(o => o is _RecoMouse) is _RecoMouse rm) {\n\t\t\t\tif (rm.p == k.pt) return;\n\t\t\t\tif (_Last is _RecoMouseMoveBy mm) { //add to the mouse.moveBy\n\t\t\t\t\tmm.a.Add((uint)Math2.MakeLparam(k.pt.x - mm.p.x, k.pt.y - mm.p.y));\n\t\t\t\t\tmm.p = k.pt;\n\t\t\t\t} else { //start mouse.moveBy\n\t\t\t\t\t_Add(new _RecoMouseMoveBy { a = new() { (uint)Math2.MakeLparam(k.pt.x - rm.p.x, k.pt.y - rm.p.y) }, p = k.pt, drag = drag }, k.time, default);\n\t\t\t\t}\n\t\t\t} else { //this is the first mouse event, therefore before mouse.moveBy need mouse.move(x, y)\n\t\t\t\tif (!_WinFromXY(out wnd w, out wnd wTL)) return;\n\t\t\t\t_Add(new _RecoMouse { p = k.pt }, k.time, w, wTL);\n\t\t\t}\n\t\t}\n\t\t\n\t\tbool _WinFromXY(out wnd w, out wnd wTL) {\n\t\t\tbool ctl = _xyIn == c_xyControl;\n\t\t\tw = wnd.fromXY(k.pt, ctl ? 0 : WXYFlags.NeedWindow);\n\t\t\twTL = ctl ? w.Window : w;\n\t\t\tif (wTL == _wThis || wTL.Is0) { w = wTL = default; return false; }\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\t//tested: SM_CXDOUBLECLK=4 and SM_CXDRAG=4 regardless of DPI (using Dpi.GetSystemMetrics), and can't be changed in Control Panel etc.\n\t\t//static bool _PointIsDrag(POINT p1, POINT p2, wnd w) {\n\t\t//\tif(p1==p2) return false;\n\t\t//\tint dpi=Dpi.OfWindow(w);\n\t\t//\treturn Math.Abs(p1.x-p2.x)>=Dpi.GetSystemMetrics(Api.SM_CXDRAG, dpi) || Math.Abs(p1.y-p2.y)>=Dpi.GetSystemMetrics(Api.SM_CYDRAG, dpi);\n\t\t//}\n\t\tstatic bool _PointIsDrag(POINT a, POINT b) => Math.Abs(a.x - b.x) >= 4 || Math.Abs(a.y - b.y) >= 4;\n\t\t\n\t\tstatic bool _PointIsDouble(POINT a, POINT b) => Math.Abs(a.x - b.x) <= 2 && Math.Abs(a.y - b.y) <= 2;\n\t}\n\t\n\t//void _WeHook(HookData.WinEvent k) {\n\t//\tif (_paused) return;\n\t//\tswitch (k.event_) {\n\t//\tcase EEvent.OBJECT_SHOW:\n\t//\t\tif (k.idObject != EObjid.WINDOW || k.idChild != 0 || k.w.IsChild) break;\n\t//\t\t//\tprint.it(k.idObject, k.idChild, k.w);\n\t//\t\tvar w = k.w;\n\t//\t\tbreak;\n\t//\tcase EEvent.OBJECT_HIDE:\n\t\n\t//\t\tbreak;\n\t//\t\t//default:\n\t//\t\t//\tprint.it(k.event_);\n\t//\t\t//\tbreak;\n\t//\t}\n\t//}\n\t\n\tstring _GetCode() {\n\t\tif (_Last is _RecoMouseMoveBy) _a.RemoveAt(_a.Count - 1);\n\t\tif (!_a.Any()) return null;\n\t\tvar b = new StringBuilder();\n\t\tstring speed = _tSpeed.TextOrNull();\n\t\tif (speed != null) b.Append(\"using (opt.scope.all()) \");\n\t\tb.AppendLine(\"{ //recorded\");\n\t\tif (speed != null) b.AppendLine($\"opt.mouse.MoveSpeed = opt.key.KeySpeed = opt.key.TextSpeed = {speed};\");\n\t\t//bool addEmptyLine = false;\n\t\tfor (int i = 0, n = _a.Count; i < n; i++) {\n\t\t\t////rejected: if mouse button with screenshot, add empty lines before and after, and add comment and screenshot before. Better just add empty line after.\n\t\t\t//if(_a[i] is _RecoMouseButton mb0 && mb0.image != null) {\n\t\t\t//\tb.AppendLine().Append(mb0.comment, 1, mb0.comment.Length - 1).AppendLine(mb0.image);\n\t\t\t//\taddEmptyLine = true;\n\t\t\t//} else if (addEmptyLine) {\n\t\t\t//\tif (_a[i] is not _RecoSleep) b.AppendLine();\n\t\t\t//\taddEmptyLine = false;\n\t\t\t//}\n\t\t\t\n\t\t\tswitch (_a[i]) {\n\t\t\tcase _RecoWin r: //and _RecoWinFind\n\t\t\t\tb.Append(r.code);\n\t\t\t\tbreak;\n\t\t\tcase _RecoWinChild r:\n\t\t\t\tb.Append(r.code);\n\t\t\t\tbreak;\n\t\t\tcase _RecoMouse r:\n\t\t\t\t//if drag in same window, format mouse.drag relative\n\t\t\t\tif (r is _RecoMouseButton mb && mb.down > 0 && i < n - 1) {\n\t\t\t\t\tif (_a[i + 1] is _RecoMouseButton mb2 && mb2.down < 0 && !mb2.noXY && mb2.w == mb.w) {\n\t\t\t\t\t\tmb.FormatDrag(b, (mb2.p.x - mb.p.x, mb2.p.y - mb.p.y));\n\t\t\t\t\t\ti++;\n\t\t\t\t\t\tgoto g1;\n\t\t\t\t\t} else if (_a[i + 1] is _RecoMouseMoveBy mm && _a[i + 2] is _RecoMouseButton mb3 && mb3.down < 0 && mb3.noXY) {\n\t\t\t\t\t\tmb.FormatDrag(b, mm.OffsetsString);\n\t\t\t\t\t\ti += 2;\n\t\t\t\t\t\tgoto g1;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tr.FormatCode(b);\n\t\t\t\tg1:\n\t\t\t\t//if (r is _RecoMouseButton mbb && mbb.image == null) b.Append(mbb.comment);\n\t\t\t\tif (r is _RecoMouseButton mbb) {\n\t\t\t\t\tb.Append(mbb.comment);\n\t\t\t\t\tif (mbb.image != null) {\n\t\t\t\t\t\tb.Append(mbb.image);\n\t\t\t\t\t\tif (i < n - 1) b.AppendLine(); //make space for image\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase _RecoKey or _RecoChar:\n\t\t\t\tb.Append(\"keys.send(\\\"\");\n\t\t\t\tfor (int prev = 0; i < n; i++) { //prev: 0 none, 1 keys, 2 chars\n\t\t\t\t\tvar r = _a[i];\n\t\t\t\t\tif (r is _RecoKey rk) {\n\t\t\t\t\t\tif (prev == 1) b.Append(' '); else if (prev == 2) b.Append(\"\\\", \\\"\");\n\t\t\t\t\t\tprev = 1;\n\t\t\t\t\t\t\n\t\t\t\t\t\t//Mod*down A B Mod*up -> Mod+(A B)\n\t\t\t\t\t\t//\tFUTURE: keys.send(\"Ctrl+\", click);\n\t\t\t\t\t\tif (rk.down > 0) {\n\t\t\t\t\t\t\tKMod modDown = 0, modUp = 0;\n\t\t\t\t\t\t\tint j = i;\n\t\t\t\t\t\t\tfor (; j < n && _a[j] is _RecoKey k2 && k2.down > 0; j++) modDown |= _KeyToMod(k2.key);\n\t\t\t\t\t\t\tif (modDown != 0) {\n\t\t\t\t\t\t\t\tint iKeys = j;\n\t\t\t\t\t\t\t\twhile (j < n && _a[j] is _RecoKey { down: 0, mod: 0 } or _RecoChar { s: null }) j++;\n\t\t\t\t\t\t\t\tint iModUp = j;\n\t\t\t\t\t\t\t\tfor (; j < n && _a[j] is _RecoKey k2 && k2.down < 0; j++) modUp |= _KeyToMod(k2.key);\n\t\t\t\t\t\t\t\tif (modUp == modDown) {\n\t\t\t\t\t\t\t\t\tkeys.more.hotkeyToString(b, modDown, 0);\n\t\t\t\t\t\t\t\t\tb.Append('(');\n\t\t\t\t\t\t\t\t\tfor (i = iKeys; i < iModUp; i++) {\n\t\t\t\t\t\t\t\t\t\tswitch (_a[i]) {\n\t\t\t\t\t\t\t\t\t\tcase _RecoKey k:\n\t\t\t\t\t\t\t\t\t\t\tif (i > iKeys) b.Append(' ');\n\t\t\t\t\t\t\t\t\t\t\tb.Append(k);\n\t\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t\tcase _RecoChar k:\n\t\t\t\t\t\t\t\t\t\t\tif (i > iKeys && _a[i - 1] is _RecoKey) b.Append(' ');\n\t\t\t\t\t\t\t\t\t\t\tb.Append('_');\n\t\t\t\t\t\t\t\t\t\t\tk.FormatCodeSimple(b);\n\t\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tb.Append(')');\n\t\t\t\t\t\t\t\t\ti = j - 1;\n\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tstatic KMod _KeyToMod(KKey k) => k switch { KKey.Ctrl => KMod.Ctrl, KKey.Shift => KMod.Shift, KKey.Alt => KMod.Alt, KKey.Win => KMod.Win, _ => 0 };\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\tb.Append(rk);\n\t\t\t\t\t} else if (r is _RecoChar rc) {\n\t\t\t\t\t\tif (prev == 1) b.Append(' ');\n\t\t\t\t\t\trc.FormatCode(b, prev != 2);\n\t\t\t\t\t\tprev = rc.count > 1 ? 1 : 2;\n\t\t\t\t\t} else break;\n\t\t\t\t}\n\t\t\t\tb.Append(\"\\\");\");\n\t\t\t\ti--;\n\t\t\t\tbreak;\n\t\t\tcase _RecoSleep r:\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (i < n - 1) b.AppendLine();\n\t\t}\n\t\t//if (speed != null)\n\t\tb.Replace(\"\\n\", \"\\n\\t\");\n\t\tb.Append(\"\\r\\n}\");\n\t\t\n\t\treturn b.ToString();\n\t}\n\t\n\tvoid _OK(bool copy) {\n\t\tClose();\n\t\tvar s = _GetCode(); if (s.NE()) return;\n\t\tif (copy) {\n\t\t\tclipboard.text = s;\n\t\t} else {\n#if SCRIPT\n\t\t\tprint.it(\"<><code>\" + s + \"</code>\");\n\t\t\t//ScriptEditor.Open(\"recorded.cs\");\n\t\t\t//keys.send(\"Ctrl+End\");\n\t\t\t//clipboard.paste(s);\n#else\n\t\t\t//InsertCode.Statements(s); //flickers\n\t\t\t//timer.after(100, _ => InsertCode.Statements(s));\n\t\t\tDispatcher.InvokeAsync(() => InsertCode.Statements(new(s)), DispatcherPriority.ApplicationIdle); //35 ms\n#endif\n\t\t}\n\t}\n}\n\n"
  },
  {
    "path": "Au.Editor/Tools/DPortable.cs",
    "content": "using System.Text.Json.Nodes;\nusing System.Windows;\nusing System.Windows.Controls;\nusing Au.Controls;\nusing System.IO.Compression;\n\nnamespace LA;\n\nclass DPortable : KDialogWindow {\n\tpublic static void ShowSingle() {\n\t\tif (App.IsPortable) {\n\t\t\tdialog.showError(\"Portable mode\", \"Please restart this program in non-portable mode.\", owner: App.Wmain);\n\t\t\treturn;\n\t\t}\n\t\tShowSingle(() => new DPortable());\n\t}\n\t\n\tstring _dirNet = folders.NetRuntime, _dirNetDesktop = folders.NetRuntimeDesktop;\n\tbool _exists;\n\t\n\trecord class _Dir(string local, string portableRelative, int iSettings, string[] skipAlways) {\n\t\tpublic string portable => portableRelative.NE() ? App.Settings.portable_dir : App.Settings.portable_dir + \"\\\\\" + portableRelative;\n\t\tpublic bool copy;\n\t\tpublic KCheckBox cCopy;\n\t\tpublic TextBox tSkip;\n\t\t\n\t\tpublic string Skip {\n\t\t\tget => field;\n\t\t\tset {\n\t\t\t\tvar s = value;\n\t\t\t\tif (skipAlways != null) {\n\t\t\t\t\tvar a = (s ?? \"\").Lines(true);\n\t\t\t\t\tforeach (var v in skipAlways) if (!a.Contains(v, StringComparer.OrdinalIgnoreCase)) s = v + \"\\r\\n\" + s;\n\t\t\t\t}\n\t\t\t\tfield = s;\n\t\t\t\tApp.Settings.portable_skip[iSettings] = s;\n\t\t\t}\n\t\t}\n\t}\n\t\n\t_Dir _dApp, _dWs, _dSett, _dScript, _dDoc, _dRoaming;\n\t\n\tDPortable() {\n\t\tif (App.Settings.portable_dir.NE() && folders.RemovableDrive0.Path is string drive) App.Settings.portable_dir = drive + @\"PortableApps\\LibreAutomate\";\n\t\tif (filesystem.more.comparePaths(folders.ThisApp, _dirNet) is CPResult.Same or CPResult.AContainsB) _dirNet = _dirNetDesktop = null;\n\t\tif (App.Settings.portable_skip.Lenn_() != 6) App.Settings.portable_skip = [\"\\\\SDK\\r\\n\\\\Git\", \".git\\r\\n\\\\exe\", \"\", \"\", \".git\", \"\"];\n\t\t\n\t\tInitWinProp(\"Portable LibreAutomate setup\", App.Wmain);\n\t\tvar b = new wpfBuilder(this);\n\t\t\n\t\tb.R.Add(\"Install in folder\", out TextBox tDir).Focus().Validation(_ => _ValidateFolder());\n\t\t\n\t\tb.R.xAddInfoBlockT(out var tbInfoExists);\n\t\t\n\t\tint iDir = 0;\n\t\tb.R.StartGrid().Span(2).Columns(0, -1);\n\t\tb.R.Add<TextBlock>(\"Copy folder\").Font(bold: true).Add<TextBlock>(\"Skip subfolders\").Font(bold: true);\n\t\t_dApp = _AddDir(folders.ThisApp, \"\", \"Program files (LA, .NET)\", skipAlways: [@\"\\SDK\"]);\n\t\t_dWs = _AddDir(App.Model.WorkspaceDirectory, @\"data\\doc\\\" + pathname.getName(App.Model.WorkspaceDirectory), \"Workspace (scripts etc)\");\n\t\t_dSett = _AddDir(folders.ThisAppDocuments + \".settings\", @\"data\\doc\\.settings\", @\"Settings\");\n\t\t_dScript = _AddDir(folders.ThisAppDocuments + \"_script\", @\"data\\doc\\_script\", @\"Script data\");\n\t\t_dDoc = _AddDir(folders.ThisAppDocuments, @\"data\\doc\", @\"Documents (except the above 3 subfolders)\");\n\t\t_dRoaming = _AddDir(folders.ThisAppDataRoaming, @\"data\\AppRoaming\", @\"AppData\");\n\t\tb.R.AddSeparator(false);\n\t\tb.End();\n\t\t\n\t\t_Dir _AddDir(string local, string portableRelative, string label, string[] skipAlways = null) {\n\t\t\tint i = iDir++;\n\t\t\t\n\t\t\tif (!filesystem.exists(local).Directory) filesystem.createDirectory(local);\n\t\t\t_Dir d = new(local, portableRelative, i, skipAlways) { Skip = App.Settings.portable_skip[i] };\n\t\t\t\n\t\t\tb.R.AddSeparator(false);\n\t\t\t\n\t\t\tb.R.StartStack(vertical: true);\n\t\t\tb.Add(out d.cCopy, label).Checked(d.copy = 0 != (App.Settings.portable_check & 1 << i));\n\t\t\td.cCopy.CheckChanged += (_, _) => App.Settings.portable_check.SetFlag_(1 << i, d.copy = d.cCopy.IsChecked);\n\t\t\tb.xAddInfoBlockF($\"<a href=\\\"{local}\\\">{local.Limit(70, middle: true)}</a>{(i == 0 ? \"\" : \" -> \\\\\")}{portableRelative}\").Padding(\"T1 B2\");\n\t\t\tb.End();\n\t\t\t\n\t\t\tb.Add(out d.tSkip, d.Skip).Multiline(wrap: TextWrapping.NoWrap).Size(..500, ..120)\n\t\t\t\t.Tooltip(\"These folders will not be copied. Existing folders will not be deleted or updated.\\r\\nExamples:\\r\\nDescendantFolderName\\r\\n\\\\DirectChildFolderName\\r\\n\\\\Folder1\\\\Folder2\\r\\n//comment\");\n\t\t\td.tSkip.TextChanged += (o, _) => { d.Skip = (o as TextBox).Text; };\n\t\t\tif (label.Starts(\"Workspace\")) b.Validation(o => d.tSkip.Text.Lines(noEmpty: true) is var a1 && (a1.Contains(@\"\\files\", StringComparer.OrdinalIgnoreCase) || a1.Contains(@\"files\", StringComparer.OrdinalIgnoreCase)) ? \"can't skip the files subfolder\" : null);\n\t\t\t\n\t\t\treturn d;\n\t\t}\n\t\t\n\t\tb.R.StartOkCancel();\n\t\tb.AddButton(\"Print details\", _ => _PrintDetails()).Width(100);\n\t\tb.AddButton(\"Install\", null, WBBFlags.OK);\n\t\tb.AddButton(\"Cancel\", null, WBBFlags.Cancel);\n\t\tb.xAddDialogHelpButtonAndF1(\"editor/Portable app\");\n\t\tb.End();\n\t\t\n\t\tb.End();\n\t\t\n\t\ttDir.TextChanged += (_, _) => {\n\t\t\tvar s = App.Settings.portable_dir = tDir.Text.Trim().TrimEnd(\"\\\\/\");\n\t\t\t_exists = filesystem.exists(s).Directory;\n\t\t\ttbInfoExists.Text = _exists ? \"The portable folder already exists.\\nChecked portable folders will be updated.\" : s == \"\" ? \"\" : \"The folder still does not exist; it will be created.\\nCheck all 'Copy folder'. Or only those you want to copy.\\nIn the future you can use this tool to update portable files.\";\n\t\t\tif (_exists && filesystem.exists(_dApp.portable).Directory) _dApp.cCopy.IsEnabled = true; else (_dApp.cCopy.IsEnabled, _dApp.cCopy.IsChecked) = (false, true);\n\t\t};\n\t\ttDir.Text = App.Settings.portable_dir;\n\t\t\n\t\tb.OkApply += e => {\n\t\t\tTask.Run(() => {\n\t\t\t\ttry { _InstallThread(); }\n\t\t\t\tcatch (Exception e1) { dialog.showError(@\"Failed\", e1.ToString()); }\n\t\t\t});\n\t\t};\n\t}\n\t\n\tvoid _InstallThread() {\n\t\tprint.it($\"<><lc YellowGreen>{(_exists ? \"Updating\" : \"Installing\")} portable LibreAutomate. Please wait until DONE.<>\");\n\t\t\n\t\tAction portableWsPathSetting = _WsPathInPortableSettings();\n\t\t\n\t\tif (_dApp.copy || !_exists) {\n\t\t\t_Copy(_dApp, \"/mir /xf unins* /xd dotnet data\");\n\t\t\t\n\t\t\tbool isArm = osVersion.isArm64Process;\n\t\t\tif (isArm) _RenameArm64();\n\t\t\t\n\t\t\tif (_dirNet != null) {\n\t\t\t\tvar dotnet = App.Settings.portable_dir + (isArm ? @\"\\dotnetARM\" : @\"\\dotnet\");\n\t\t\t\t_Copy2(_dirNet, dotnet, \"/e\");\n\t\t\t\t_Copy2(_dirNetDesktop, dotnet, \"/e\");\n\t\t\t}\n\t\t\t\n\t\t\ttry {\n\t\t\t\tDotnetUtil.DownloadNetRuntimesForOtherArch(_dApp.portable, true);\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tstring arch = isArm ? \"x64\" : \"Arm64\";\n\t\t\t\tprint.warning($\"Failed to download .NET Runtime {arch}. Only .NET Runtime {(isArm ? \"Arm64\" : \"x64\")} has been copied to the portable folder. On Windows {arch} the portable app will run only if .NET Runtime {arch} is installed there. Exception:\\r\\n{ex}\");\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (_dDoc.copy) _Copy(_dDoc, $\"\"\"/mir /xd \"{_dWs.local}\" \"{_dWs.portable}\" \"{_dSett.local}\" \"{_dSett.portable}\" \"{_dScript.local}\" \"{_dScript.portable}\" \"\"\");\n\t\telse filesystem.createDirectory(_dDoc.portable); //need subdir \"data\" to detect portable mode\n\t\tif (_dWs.copy) _Copy(_dWs, $\"\"\"/mir /xd \"{_dWs.local}\\.compiled\" \"{_dWs.local}\\.temp\" \"\"\");\n\t\tif (_dSett.copy) _Copy(_dSett);\n\t\tif (_dScript.copy) _Copy(_dScript);\n\t\tif (_dRoaming.copy) _Copy(_dRoaming);\n\t\t\n\t\tportableWsPathSetting?.Invoke();\n\t\tif (_dWs.copy) {\n\t\t\tvar file = _dWs.portable + @\"\\settings.json\";\n\t\t\tvar s = filesystem.loadText(file);\n\t\t\ts = s.Replace(\"\\\"gitBackup\\\": true\", \"\\\"gitBackup\\\": false\");\n\t\t\tfilesystem.saveText(file, s);\n\t\t}\n\t\t\n\t\tprint.it($\"<>DONE. Installed in <link>{App.Settings.portable_dir}<>.\");\n\t\t\n\t\tstatic void _Copy(_Dir d, string how = \"/mir\") => _Copy2(d.local, d.portable, how, d.Skip);\n\t\t\n\t\tstatic void _Copy2(string dir, string dirTo, string how, string skipDirs = null) {\n\t\t\tprint.it($\"Copying {dir}\");\n\t\t\tvar s = _RobocopyArgs(false, dir, dirTo, how, skipDirs);\n\t\t\t//info: the `/r:0` turns off the robocopy's slow and unreliable retries. Use this loop instead.\n\t\t\t//tested: with `/mt` (multithreaded) faster eg 20 -> 17 s or 49 -> 31 s. Faster even if HDD.\n\t\t\t\n\t\t\tfor (int i = 0; ;) {\n\t\t\t\tint r = run.console(out var so, \"robocopy.exe\", s);\n\t\t\t\t//print.it($\"<><c red>{r}<>\"); print.it(so);\n\t\t\t\tif ((uint)r < 8) break;\n\t\t\t\tif (++i == 5) {\n\t\t\t\t\tprint.it($\"<><c red>Failed to copy '{dir}' to '{dirTo}'.<>\\r\\n<\\a>{so}</\\a>\");\n\t\t\t\t\tif (r == 16) throw new AuException();\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\t100.ms();\n\t\t\t}\n\t\t\t\n\t\t\t//CONSIDER: options:\n\t\t\t//\t1. Keep new and newer portable files. (instead of /mir use /e /xo; or /mir /xo /xx)\n\t\t\t//\t\tBad: if both files modified...\n\t\t\t//\t2. Copy symbolic link target. And workspace link target.\n\t\t}\n\t\t\n\t\tAction _WsPathInPortableSettings() {\n\t\t\tif (!_dSett.copy && !_dWs.copy) return null;\n\t\t\t\n\t\t\tstring wsPath = null, file = _dSett.portable + @\"\\Settings.json\";\n\t\t\tif (!_dWs.copy) { //preserve the portable workspace path setting when replacing the portable settings file\n\t\t\t\ttry { wsPath = (string)JsonNode.Parse(filesystem.loadBytes(file))[\"workspace\"]; }\n\t\t\t\tcatch { }\n\t\t\t}\n\t\t\t\n\t\t\treturn () => {\n\t\t\t\tvar j = filesystem.exists(file) ? JsonNode.Parse(filesystem.loadBytes(file)) : new JsonObject();\n\t\t\t\tj[\"workspace\"] = wsPath ?? $@\"%folders.ThisAppDocuments%\\{pathname.getName(_dWs.local)}\";\n\t\t\t\tfilesystem.saveText(file, j.ToJsonString());\n\t\t\t};\n\t\t}\n\t\t\n\t\tvoid _RenameArm64() {\n\t\t\tvar dir = _dApp.portable;\n\t\t\t_Rename(\"Au.Editor.exe\");\n\t\t\t\n\t\t\tvoid _Rename(string fileName) {\n\t\t\t\tstring s1 = dir + \"\\\\\" + fileName, s2 = s1.Insert(^4, \"-x64\");\n\t\t\t\tif (filesystem.exists(s2)) {\n\t\t\t\t\tfilesystem.rename(s1, fileName.Insert(^4, \"-arm\"));\n\t\t\t\t\tfilesystem.rename(s2, fileName);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t\n\tstatic string _RobocopyArgs(bool log, string dir, string dirTo, string how, string skipDirs) {\n\t\tdir = pathname.unprefixLongPath(dir); //robocopy unaware about \"\\\\?\\\"\n\t\tdirTo = pathname.unprefixLongPath(dirTo);\n\t\t\n\t\tStringBuilder b = new($\"\"\"\n\"{dir}\" \"{dirTo}\" {how} /xj /r:0 /w:1 /np /mt\n\"\"\");\n\t\t\n\t\tif (!skipDirs.NE() && skipDirs.Lines(noEmpty: true) is var a) {\n\t\t\tbool once = false;\n\t\t\tforeach (var s in a) {\n\t\t\t\tif (s.Starts(\"//\")) continue;\n\t\t\t\tif (!once) { once = true; b.Append(\" /xd\"); }\n\t\t\t\t\n\t\t\t\t//workaround for: robocopy does not support relative paths. Can be either filename or full path.\n\t\t\t\t//\tIf filename, skips all source and dest descendants with that name.\n\t\t\t\t//\tTo achieve the same with a relative path, specify full paths in both source and dest dir.\n\t\t\t\tif (s[0] is '\\\\' or '/') b.Append($\" \\\"{dir}{s}\\\" \\\"{dirTo}{s}\\\"\");\n\t\t\t\telse b.Append($\" \\\"{s}\\\"\");\n\t\t\t}\n\t\t\t\n\t\t\t//TODO2: allow to reinclude subfolders and files, like <+>\\.nuget\\subfolder.\n\t\t}\n\t\t\n\t\tif (log) b.Append(\" /l /bytes /nfl /njh\");\n\t\t\n\t\treturn b.ToString();\n\t}\n\t\n\tstring _RobocopyArgs(bool log, _Dir d, string how) => _RobocopyArgs(log, d.local, d.portable, how, d.Skip);\n\t\n\tasync void _PrintDetails() {\n\t\tprint.clear();\n\t\t\n\t\tstatic async Task<long> _GetDirSize2(string dir, string how = \"/mir\", string skipDirs = null) {\n\t\t\treturn await Task.Run(() => {\n\t\t\t\tvar s = _RobocopyArgs(true, dir, folders.Temp + \"06e5f7d0-13ec-438a-a415-53b624d3c308\", how, skipDirs);\n\t\t\t\trun.console(out var so, \"robocopy.exe\", s);\n\t\t\t\tif (so.RxMatch(@\"(?m)^\\h*Bytes\\h*:\\h*\\K\\d+\", 0, out so) && so.ToNumber(out long k)) return k;\n\t\t\t\treturn -1;\n\t\t\t});\n\t\t}\n\t\t\n\t\tstatic async Task<long> _GetDirSize(_Dir d, string how = \"/mir\") => await _GetDirSize2(d.local, how, d.Skip);\n\t\t\n\t\tlong sizeApp = await _GetDirSize(_dApp);\n\t\tlong sizeNet = _dirNet == null ? 0 : await _GetDirSize2(_dirNet) + await _GetDirSize2(_dirNetDesktop);\n\t\tlong sizeWs = await _GetDirSize(_dWs);\n\t\tlong sizeSett = await _GetDirSize(_dSett);\n\t\tlong sizeScript = await _GetDirSize(_dScript);\n\t\tlong sizeDoc = await _GetDirSize(_dDoc, $\"\"\"/mir /xd \"{_dWs.local}\" \"{_dSett.local}\" \"{_dScript.local}\" \"\"\");\n\t\tlong sizeRoaming = await _GetDirSize(_dRoaming);\n\t\tbool isArm = osVersion.isArm64Process;\n\t\t\n\t\tvar b = new StringBuilder();\n\t\tb.AppendLine($\"\"\"\n<><lc YellowGreen>Portable LibreAutomate setup details. The 'Skip subfolders' setting is applied.<>\nTotal size: {_MB(sizeApp + sizeNet + sizeWs + sizeSett + sizeScript + sizeDoc + sizeRoaming)} MB.\nFolder sizes (MB):\n\t{\"Program\",-20} {_MB(sizeApp)}\n\t{$\".NET Runtime {(isArm ? \"Arm64\" : \"x64\")}\",-20} {_MB(sizeNet)}\n\t{$\".NET Runtime {(isArm ? \"x64\" : \"Arm64\")}\",-20} ~{_MB(sizeNet)} (download)\n\t{\"Workspace\",-20} {_MB(sizeWs)}\n\t{\"Settings\",-20} {_MB(sizeSett)}\n\t{\"Script data\",-20} {_MB(sizeScript)}\n\t{\"Documents\",-20} {_MB(sizeDoc)} (except the above 3 subfolders)\n\t{\"AppData\",-20} {_MB(sizeRoaming)}\n\"\"\");\n\t\t\n\t\t_Links(b);\n\t\t\n\t\tprint.it(b.ToString());\n\t\tprint.scrollToTop();\n\t\t\n\t\tstatic string _MB(long size) {\n\t\t\tconst int MB = 1024 * 1024;\n\t\t\tif (size < 0) return \"<failed>\";\n\t\t\tif (size == 0) return \"0\";\n\t\t\tif (size < MB) return \"<1\";\n\t\t\treturn (size / MB).ToString();\n\t\t}\n\t\t\n\t\tvoid _Links(StringBuilder b) {\n\t\t\tint n = 0;\n\t\t\tforeach (var f in App.Model.Root.Descendants()) {\n\t\t\t\tif (f.IsLink) {\n\t\t\t\t\tif (n++ == 0) b.AppendLine(\"Links to external files and folders will be invalid on other computers:\");\n\t\t\t\t\tb.Append($\"\\t{(f.IsFolder ? \"Folder \" : null)}{f.SciLink(path: true)}  ->  <explore>{f.FilePath}<>\\r\\n\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t\n\tstring _ValidateFolder() {\n\t\tif (App.Settings.portable_dir.NE()) return \"Where to install?\";\n\t\t\n\t\t//error if workspace is in portable folder (shared). Or part of it (link target).\n\t\tif (filesystem.exists(App.Settings.portable_dir)) {\n\t\t\tbool bad = filesystem.more.comparePaths(App.Settings.portable_dir, _dWs.local) is CPResult.AContainsB or CPResult.BContainsA or CPResult.Same;\n\t\t\tif (!bad) {\n\t\t\t\tforeach (var f in App.Model.Root.Descendants()) {\n\t\t\t\t\tif (f.IsLink) {\n\t\t\t\t\t\tvar s = f.FilePath;\n\t\t\t\t\t\tif (filesystem.more.comparePaths(App.Settings.portable_dir, s) is CPResult.AContainsB or CPResult.BContainsA or CPResult.Same) {\n\t\t\t\t\t\t\tif (!bad) print.it(\"Links to portable:\");\n\t\t\t\t\t\t\tbad = true;\n\t\t\t\t\t\t\tprint.it($\"<>{f.SciLink(path: true)}  ->  <explore>{s}<>\");\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (bad) return \"Current workspace or part of it is in the portable folder, or vice versa.\";\n\t\t}\n\t\t\n\t\treturn null;\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Tools/DPwnd.cs",
    "content": "using Au.Controls;\nusing System.Windows;\nusing System.Windows.Controls;\n\nnamespace ToolLand;\n\n/// <summary>\n/// Dialog page for capturing a top-level window and editing its properties.\n/// The result is <b>wnd.find</b> arguments string.\n/// </summary>\nclass DPwnd : UserControl {\n\twnd _wnd;\n\tstring _wndName;\n\t\n\tKSciInfoBox _info;\n\tButton _bTest;\n\tKCheckBox _cCapture;\n\tControls _k;\n\t\n\tpublic DPwnd() : this(null) { }\n\t\n\tpublic DPwnd(string header) {\n\t\tvar b = new wpfBuilder(this).Columns(-1);\n\t\tBackground = SystemColors.ControlBrush; //no tooltip etc without a brush\n\t\t\n\t\tif (header != null) b.xAddGroupSeparator(header);\n\t\tb.Row(50).Add(out _info);\n\t\t\n\t\tb.R.StartGrid().Columns(0, 76, -1);\n\t\tb.xAddCheckIcon(out _cCapture, \"*Unicons.Capture\" + LA.EdIcons.red, $\"Enable capturing ({LA.App.Settings.delm.hk_capture}) and show window rectangles\");\n\t\tb.AddButton(out _bTest, \"Test\", _bTest_Click).Span(1).Disabled().Tooltip(\"Find the window and show the rectangle\");\n\t\tb.End();\n\t\t\n\t\tb.R.xStartPropertyGrid(\"L2 T3 R2 B1\");\n\t\t_k = new(b, true);\n\t\tb.xEndPropertyGrid();\n\t\t\n\t\tb.End();\n\t\t\n\t\tbool loadedOnce = false;\n\t\tthis.Loaded += (_, e) => {\n\t\t\tif (!loadedOnce) {\n\t\t\t\tloadedOnce = true;\n\t\t\t\t_InitInfo();\n\t\t\t}\n\t\t\t_cCapture.IsChecked = true;\n\t\t};\n\t\t\n\t\tIsVisibleChanged += (_, e) => {\n\t\t\tif (!(bool)e.NewValue) _cCapture.IsChecked = false;\n\t\t};\n\t}\n\t\n\tstatic DPwnd() {\n\t\tTUtil.OnAnyCheckTextBoxValueChanged<DPwnd>((d, o) => d._AnyCheckTextBoxValueChanged(o));\n\t}\n\t\n\tpublic void SetWnd(wnd w) {\n\t\tbool newWindow = w != _wnd;\n\t\t_wnd = w;\n\t\t_noeventValueChanged = true;\n\t\t\n\t\tstring wndName = _wnd.NameTL_;\n\t\tif (newWindow) _k.Fill(_wnd, wndName, _wnd.ClassName, _wnd.ProgramName);\n\t\telse if (wndName != _wndName) _k.FillChangedName(wndName);\n\t\t_wndName = wndName;\n\t\t\n\t\t_noeventValueChanged = false;\n\t}\n\t\n\tpublic void Clear() {\n\t\t_wnd = default;\n\t\t_wndName = null;\n\t\t_noeventValueChanged = true;\n\t\t_k.Clear();\n\t\t_noeventValueChanged = false;\n\t}\n\t\n\t//when checked/unchecked any checkbox, and when text changed of any textbox\n\tvoid _AnyCheckTextBoxValueChanged(object source) {\n\t\tif (source == _cCapture) {\n\t\t\t_cCapture_CheckedChanged();\n\t\t} else if (!_noeventValueChanged) {\n\t\t\t_noeventValueChanged = true;\n\t\t\tif (source is TextBox t && t.Tag is KCheckTextBox k) {\n\t\t\t\tk.CheckIfTextNotEmpty();\n\t\t\t}\n\t\t\t_noeventValueChanged = false;\n\t\t}\n\t\t\n\t\tif (source is KCheckBox c && _k.IsMyCheckbox(c)) _bTest.IsEnabled = _k.HasResults;\n\t}\n\tbool _noeventValueChanged;\n\t\n\tstring _FormatCode(bool forTest = false) {\n\t\tvar f = new TUtil.WindowFindCodeFormatter { Test = forTest };\n\t\t_k.GetResults(f);\n\t\treturn f.Format();\n\t}\n\t\n\t#region capture\n\t\n\tTUtil.CapturingWithHotkey _capt;\n\t\n\tvoid _cCapture_CheckedChanged() {\n\t\t_capt ??= new(\n\t\t\t_cCapture,\n\t\t\to => o.wTL.GetRect(out o.resultRect),\n\t\t\tnew(LA.App.Settings.delm.hk_capture, _Capture)\n\t\t\t);\n\t\t_capt.Capturing = _cCapture.IsChecked;\n\t}\n\t\n\tvoid _Capture() {\n\t\tvar c = wnd.fromMouse(WXYFlags.NeedWindow); if (c.Is0) return;\n\t\tSetWnd(c);\n\t\tvar w = this.Hwnd();\n\t\tif (w.IsMinimized) {\n\t\t\tw.ShowNotMinMax();\n\t\t\tw.ActivateL();\n\t\t}\n\t}\n\t\n\t#endregion\n\t\n\t#region Result, Test\n\t\n\t/// <summary>\n\t/// true if any checkbox checked.\n\t/// </summary>\n\tpublic bool HasResult => _bTest.IsEnabled;\n\t\n\t///// <summary>\n\t///// The captured window. Or default if not captured.\n\t///// </summary>\n\t//public wnd AaResultWindow => _wnd;\n\t\n\t/// <summary>\n\t/// <b>wnd.find</b> arguments string, like <c>\"\\\"name\\\", \\\"class\\\"...\"</c>. Or null if not specified.\n\t/// </summary>\n\tpublic string AaResultCode {\n\t\tget {\n\t\t\tvar s = _FormatCode();\n\t\t\ts = TUtil.ArgsFromWndFindCode(s);\n\t\t\tif (s.Starts(\"null, null, \\\"\")) s = s.ReplaceAt(0, 13, \"of: \\\"\");\n\t\t\treturn s == \"null\" ? null : s;\n\t\t}\n\t}\n\t\n\tprivate void _bTest_Click(WBButtonClickArgs ea) {\n\t\tvar code = _FormatCode(true);\n\t\tvar rr = TUtil.RunTestFindObject(this, code, \"w\", _wnd, getRect: o => {\n\t\t\tvar w = (wnd)o;\n\t\t\tvar r = w.Rect;\n\t\t\tif (w.IsMaximized) {\n\t\t\t\tvar k = w.Screen.Rect; k.Inflate(-2, -2);\n\t\t\t\tr.Intersect(k);\n\t\t\t}\n\t\t\treturn r;\n\t\t});\n\t\t_info.InfoErrorOrInfo(rr.info);\n\t}\n\t\n\t#endregion\n\t\n\t#region info\n\t\n\tTUtil.CommonInfos _commonInfos;\n\tvoid _InitInfo() {\n\t\t_commonInfos = new TUtil.CommonInfos(_info);\n\t\tstring s1 = _wnd.Is0 ? \"C\" : \"You can c\", s = $@\"{s1}apture a window with <+hotkey>hotkey<> <b>{LA.App.Settings.delm.hk_capture}<>.\";\n\t\t_info.aaaText = s;\n\t\t_info.AaAddElem(this, s);\n\t\tTUtil.CapturingWithHotkey.RegisterLink_DialogHotkey(_info);\n\t\t_k.InitInfo(_info);\n\t}\n\t\n\t#endregion\n\t\n\t//used by Dwnd too\n\tpublic class Controls {\n\t\tpublic KCheckTextBox name, cn, program, contains, also;\n\t\t\n\t\tpublic Controls(wpfBuilder b, bool addAlso) {\n\t\t\tb.Columns(70, -1);\n\t\t\tname = b.xAddCheckText<KTextExpressionBox>(\"name\");\n\t\t\tcn = b.xAddCheckText<KTextExpressionBox>(\"class\");\n\t\t\tprogram = b.xAddCheckTextDropdown<KTextExpressionBox>(\"program\");\n\t\t\tcontains = b.xAddCheckTextDropdown<KTextExpressionBox>(\"contains\");\n\t\t\tif (addAlso) also = b.xAddCheckText(\"also\", \"o=>true\");\n\t\t}\n\t\t\n\t\tpublic void InitInfo(KSciInfoBox ib) {\n\t\t\tib.InfoCT(name, \"Window name.\", true);\n\t\t\tib.InfoCT(cn, \"Window class name.\", true);\n\t\t\tib.InfoCT(program, \"Program.\", true);\n\t\t\tib.InfoCT(contains, \"\"\"\nA UI element in the window. Format: e 'role' name.\nOr a control in the window. Format: c 'class' text.\n\"\"\", true, \"name/class/text\");\n\t\t\tib.InfoCT(also, \"<help>wnd.find<> \" + TUtil.CommonInfos.c_alsoParameter);\n\t\t}\n\t\t\n\t\tpublic void Fill(wnd w, string name_, string cn_, string program_) {\n\t\t\tname.Set(true, TUtil.EscapeWindowName(name_, true));\n\t\t\tcn.Set(true, TUtil.StripWndClassName(cn_, true));\n\t\t\tvar ap = new List<string> { program_, \"WOwner.Process(processId)\", \"WOwner.Thread(threadId)\" }; if (!w.Get.Owner.Is0) ap.Add(\"WOwner.Window(ow)\");\n\t\t\tprogram.Set(name_.NE(), program_, ap);\n\t\t\tcontains.Set(false, null, _ContainsCombo_DropDown);\n\t\t\t\n\t\t\tList<string> _ContainsCombo_DropDown() {\n\t\t\t\ttry {\n\t\t\t\t\tvar a1 = new List<string>();\n\t\t\t\t\t//child\n\t\t\t\t\tforeach (var c in w.Get.Children(onlyVisible: true)) {\n\t\t\t\t\t\tvar cn = c.Name; if (cn.NE()) continue;\n\t\t\t\t\t\tcn = \"c '\" + TUtil.StripWndClassName(c.ClassName, true) + \"' \" + TUtil.EscapeWildex(cn);\n\t\t\t\t\t\tif (!a1.Contains(cn)) a1.Add(cn);\n\t\t\t\t\t}\n\t\t\t\t\t//elm\n\t\t\t\t\tvar a2 = new List<string>();\n\t\t\t\t\tvar a3 = w.Elm[name: \"?*\", prop: \"notin=SCROLLBAR\\0maxcc=100\", flags: EFFlags.ClientArea].FindAll(); //all that have a name\n\t\t\t\t\tstring prevName = null;\n\t\t\t\t\tfor (int i = a3.Length; --i >= 0;) {\n\t\t\t\t\t\tif (!a3[i].GetProperties(\"Rn\", out var prop)) continue;\n\t\t\t\t\t\tif (prop.Name == prevName && prop.Role == \"WINDOW\") continue; prevName = prop.Name; //skip parent WINDOW\n\t\t\t\t\t\tstring rn = \"e '\" + prop.Role + \"' \" + TUtil.EscapeWildex(prop.Name);\n\t\t\t\t\t\tif (!a2.Contains(rn)) a2.Add(rn);\n\t\t\t\t\t}\n\t\t\t\t\ta2.Reverse();\n\t\t\t\t\ta1.AddRange(a2);\n\t\t\t\t\t\n\t\t\t\t\treturn a1;\n\t\t\t\t\t//rejected: sort\n\t\t\t\t}\n\t\t\t\tcatch (Exception ex) { Debug_.Print(ex); return null; }\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic void FillChangedName(string name_) {\n\t\t\tif (TUtil.ShouldChangeTextBoxWildex(name.t.Text, name_))\n\t\t\t\tname.Set(true, TUtil.EscapeWindowName(name_, true));\n\t\t}\n\t\t\n\t\tpublic void GetResults(TUtil.WindowFindCodeFormatter f) {\n\t\t\tname.GetText(out f.nameW, emptyToo: true);\n\t\t\tcn.GetText(out f.classW);\n\t\t\tprogram.GetText(out f.programW);\n\t\t\talso.GetText(out f.alsoW);\n\t\t\tcontains.GetText(out f.containsW);\n\t\t}\n\t\t\n\t\tpublic bool IsMyCheckbox(KCheckBox c) => c == name.c || c == cn.c || c == program.c || c == also.c || c == contains.c;\n\t\t\n\t\tpublic bool HasResults => name.c.IsChecked || cn.c.IsChecked || program.c.IsChecked || also.c.IsChecked || contains.c.IsChecked;\n\t\t\n\t\tpublic void Clear() {\n\t\t\tname.Clear();\n\t\t\tcn.Clear();\n\t\t\tprogram.Clear();\n\t\t\tcontains.Clear();\n\t\t\talso.Clear(\"o=>true\");\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Tools/DSnippets.cs",
    "content": "extern alias CAW;\n\nusing Microsoft.CodeAnalysis;\nusing CAW::Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp;\nusing Microsoft.CodeAnalysis.CSharp.Syntax;\nusing Microsoft.CodeAnalysis.Text;\nusing Microsoft.CodeAnalysis.CSharp.Extensions;\nusing Microsoft.CodeAnalysis.Shared.Extensions;\nusing CAW::Microsoft.CodeAnalysis.Shared.Extensions;\nusing CAW::Microsoft.CodeAnalysis.Rename;\n\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Controls.Primitives;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing Au.Controls;\nusing System.Xml.Linq;\nusing System.Windows.Documents;\nusing System.Text.Json.Nodes;\nusing ToolLand;\n\nnamespace LA;\n\nclass DSnippets : KDialogWindow {\n\t/// <param name=\"openSnippet\">Open this snippet. Must be <c>\"snippetName|fileName.ext\"</c>.</param>\n\tpublic static void ShowSingle(string openSnippet = null) {\n\t\tvar d = ShowSingle(() => new DSnippets());\n\t\tif (openSnippet != null) d._SelectAndOpen(openSnippet);\n\t}\n\t\n\tList<_Item> _files;\n\t\n\tPanel _panelSnippet, _panelFile;\n\tKTreeView _tv;\n\tKSciCodeBox _code;\n\tTextBox _tName, _tContext, _tInfo, _tMore, _tPrint, _tUsing, _tMeta, _tVar;\n\tTextBlock _tbFile, _tbDefaultFileInfo;\n\t\n\t_Item _ti; //current item\n\t_Item _clip; //cut/copy item\n\tbool _cut;\n\tbool _readonly;\n\tbool _ignoreEvents;\n\t\n\tDSnippets() {\n\t\tInitWinProp(\"Snippets\", App.Wmain);\n\t\t\n\t\tvar b = new wpfBuilder(this).WinSize(800, 600).Columns(250, 0, -1);\n\t\tb.Row(-1);\n\t\t\n\t\tb.xAddInBorder(out _tv);\n\t\tb.Add<GridSplitter>().Splitter(vertical: true);\n\t\t\n\t\tb.StartGrid().Columns(-1); //right side\n\t\tb.Row(-1);\n\t\t\n\t\t//snippet\n\t\t\n\t\tb.StartGrid().Hidden(null).Columns(0, -1, 30, 0, -1);\n\t\t_panelSnippet = b.Panel;\n\t\tb.R.Add(\"Name\", out _tName).Tooltip(\"Snippet name. Single word.\\nIf ends with \\\"Surround\\\", the snippet can be used only for surround.\");\n\t\t_tName.TextChanged += (_, _) => { if (!_ignoreEvents && _ti.Level == 1) _TvSetText(_tName.TextOrNull()); };\n\t\tb.Skip().Add(\"Context\", out AdornerDecorator _).Child().Add(out _tContext).Watermark(\"Auto-detect\");\n\t\t_tContext.TextChanged += (_, _) => { if (!_ignoreEvents) _ti.context = _tContext.TextOrNull(); };\n\t\tb.R.Add(\"Info\", out _tInfo);\n\t\t_tInfo.TextChanged += (_, _) => { if (!_ignoreEvents) { _ti.info = _tInfo.TextOrNull(); if (_ti.Level == 2) _TvSetText(_ti.info); } };\n\t\tb.R.Add(\"Info+\", out _tMore).Multiline(40);\n\t\t_tMore.TextChanged += (_, _) => { if (!_ignoreEvents) _ti.more = _tMore.TextOrNull(); };\n\t\tb.R.Add(\"Print\", out _tPrint).Multiline(40).Tooltip(\"Print this text when inserting the snippet.\\nCan contain output tags if starts with <>.\");\n\t\t_tPrint.TextChanged += (_, _) => { if (!_ignoreEvents) _ti.print_ = _tPrint.TextOrNull(); };\n\t\tb.R.Add(\"using\", out _tUsing).Tooltip(\"If the snippet code requires using directives, add the namespace names here.\\nExample: System.Windows;System.Windows.Controls\");\n\t\t_tUsing.TextChanged += (_, _) => { if (!_ignoreEvents) _ti.using_ = _tUsing.TextOrNull(); };\n\t\tb.R.Add(\"Meta\", out _tMeta).Tooltip(\"If the snippet code requires /*/ meta comments /*/, add them here.\\nExample: c Example.cs; nuget -\\\\Example\");\n\t\t_tMeta.TextChanged += (_, _) => { if (!_ignoreEvents) _ti.meta_ = _tMeta.TextOrNull(); };\n\t\tb.R.Add(\"${VAR}\", out _tVar).Tooltip(\"${VAR} variable type and name. Example: Au.toolbar,t\");\n\t\t_tVar.TextChanged += (_, _) => { if (!_ignoreEvents) _ti.var_ = _tVar.TextOrNull(); };\n\t\tforeach (var v in _panelSnippet.Children) if (v is TextBox t1) t1.IsReadOnlyCaretVisible = true;\n\t\t\n\t\tb.R.Add<Label>(\"Code\");\n\t\tb.StartGrid().Columns(70, 70, 70, 70, 40).Align(\"R\");\n\t\tb.AddButton(\"Paste\", _ => _PasteCode(clipboard.text)).Tooltip(\"Replaces code with the clipboard text without indent tabs\");\n\t\tb.AddButton(\"${n:text}\", _ => _InsertVar(\"${1:}\")).Tooltip(\"Tab stop with text\");\n\t\tb.AddButton(\"$n\", _ => _InsertVar(\"$1\")).Tooltip(\"Tab stop without text or with text of the first ${n:text}\");\n\t\tb.AddButton(\"$0\", _ => _InsertVar(\"$0\")).Tooltip(\"Final text cursor position\");\n\t\tb.AddButton(\"...\", _ => _InsertVar(null));\n\t\tb.End();\n\t\tb.Row(-1).xAddInBorder(out _code);\n\t\t_code.AaTextChanged += _ => { if (!_ignoreEvents) _ti.code = _code.aaaText; };\n\t\t_code.AaNotify += e => {\n\t\t\tif (e.n.code == Sci.NOTIF.SCN_MODIFYATTEMPTRO && _readonly) dialog.showInfo(null, \"Default snippets are read-only, but you can clone a default snippet (right click, copy, paste) and edit the clone. Uncheck the default snippet.\", owner: e.c);\n\t\t};\n\t\tb.End();\n\t\t\n\t\t//file\n\t\t\n\t\tb.And(0).StartStack(true).Hidden(null);\n\t\t_panelFile = b.Panel;\n\t\tb.Add(out _tbFile);\n\t\tb.Add(out _tbDefaultFileInfo, \"Don't edit this file. The setup program replaces it. Only uncheck some snippets if need.\").Wrap();\n\t\tb.End();\n\t\t\n\t\tb.R.AddSeparator(vertical: false);\n\t\tb.R.StartOkCancel().AddOkCancel(out var bOK, out var bCancel, out _, apply: \"Apply\");\n\t\tbOK.IsDefault = false; bCancel.IsCancel = false;\n\t\tb.AddButton(\"Import\", _ => _ImportMenu()).Width(70);\n\t\tb.xAddDialogHelpButtonAndF1(\"editor/Snippets\");\n\t\tb.End();\n\t\t\n\t\tb.End(); //right side\n\t\tb.End();\n\t\t\n\t\t_tv.HasCheckboxes = true;\n\t\t_tv.SingleClickActivate = true;\n\t\t_tv.ItemActivated += _tv_ItemActivated;\n\t\t_tv.ItemClick += _tv_ItemClick;\n\t\t_tv.KeyDown += _tv_KeyDown;\n\t\t_FillTree();\n\t\t\n\t\t//b.Loaded += () => {\n\t\t//};\n\t\t\n\t\tb.OkApply += e => {\n\t\t\t_Save();\n\t\t};\n\t}\n\t\n\tvoid _tv_ItemActivated(TVItemEventArgs e) => _Open(e.Item as _Item);\n\t\n\tvoid _Open(_Item t) {\n\t\t_ignoreEvents = true;\n\t\t_ti = t;\n\t\t_readonly = t.IsReadonly;\n\t\tint level = t.Level;\n\t\t_panelFile.Visibility = level == 0 ? Visibility.Visible : Visibility.Collapsed;\n\t\t_panelSnippet.Visibility = level > 0 ? Visibility.Visible : Visibility.Collapsed;\n\t\t\n\t\tif (level == 0) {\n\t\t\t_tbFile.Inlines.Clear();\n\t\t\tvar h = new Hyperlink(new Run(t.filePath));\n\t\t\th.Click += (_, _) => run.selectInExplorer(t.filePath);\n\t\t\t_tbFile.Inlines.Add(h);\n\t\t\t_tbDefaultFileInfo.Visibility = _readonly ? Visibility.Visible : Visibility.Collapsed;\n\t\t} else {\n\t\t\tbool isMenu = level == 1 && t.IsFolder;\n\t\t\tif (!isMenu) {\n\t\t\t\tvar s = t.code;\n\t\t\t\t_code.AaSetText(s, _readonly ? 0 : -1);\n\t\t\t}\n\t\t\t\n\t\t\t_tName.Text = level == 1 ? t.text : t.Parent.text;\n\t\t\t_tContext.Text = level == 1 ? t.context : t.Parent.context;\n\t\t\t_tInfo.Text = level == 1 ? t.info : t.text;\n\t\t\t_tMore.Text = level == 1 ? t.more : t.Parent.more;\n\t\t\t_tPrint.Text = t.print_;\n\t\t\t_tUsing.Text = t.using_;\n\t\t\t_tMeta.Text = t.meta_;\n\t\t\t_tVar.Text = t.var_;\n\t\t\t\n\t\t\t_code.Visibility = isMenu ? Visibility.Hidden : Visibility.Visible;\n\t\t\t//don't disable textboxes. Instead make read-only, to allow scroll/select/copy text.\n\t\t\t_tName.IsReadOnly = level == 2 || _readonly;\n\t\t\t_tContext.IsReadOnly = level == 2 || _readonly;\n\t\t\t_tInfo.IsReadOnly = _readonly;\n\t\t\t_tMore.IsReadOnly = level == 2 || _readonly;\n\t\t\t_tPrint.IsReadOnly = _readonly;\n\t\t\t_tUsing.IsReadOnly = _readonly;\n\t\t\t_tMeta.IsReadOnly = _readonly;\n\t\t\t_tVar.IsReadOnly = _readonly;\n\t\t\tforeach (var v in _panelSnippet.Children) {\n\t\t\t\tswitch (v is AdornerDecorator ad ? ad.Child : v) {\n\t\t\t\tcase TextBox t1: //if readonly, display like disabled\n\t\t\t\t\tif (t1.IsReadOnly) t1.Background = SystemColors.ControlBrush; else t1.ClearValue(TextBox.BackgroundProperty);\n\t\t\t\t\tbreak;\n\t\t\t\tcase Panel p1:\n\t\t\t\t\tp1.IsEnabled = !(_readonly || isMenu);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t_ignoreEvents = false;\n\t}\n\t\n\tvoid _tv_ItemClick(TVItemEventArgs e) {\n\t\tvar t = e.Item as _Item;\n\t\tint level = t.Level;\n\t\tif (e.Button == MouseButton.Left) {\n\t\t\t//print.it(e.Part);\n\t\t\tif (e.Part == TVParts.Checkbox) {\n\t\t\t\tt.isChecked ^= true;\n\t\t\t\t_tv.Redraw(t);\n\t\t\t} else if (e.Part == TVParts.Text) {\n\t\t\t\tif (level == 1 && t.IsFolder) _tv.Expand(t, true);\n\t\t\t}\n\t\t} else if (e.Button == MouseButton.Right) {\n\t\t\tvar m = new popupMenu();\n\t\t\t\n\t\t\tif (!t.IsReadonly) {\n\t\t\t\tint clipLevel = _clip?.Level ?? -1;\n\t\t\t\tif (level == 0) {\n\t\t\t\t\tm[\"Add new snippet\"] = o => _New(true);\n\t\t\t\t\tif (clipLevel == 1) _MiPaste(true);\n\t\t\t\t} else {\n\t\t\t\t\tif (level == 1) m[\"Add new snippet\"] = o => _New(false);\n\t\t\t\t\tm[\"Add new menu item\"] = o => _New(level == 1);\n\t\t\t\t\tm.Separator();\n\t\t\t\t\tm[\"Cut\"] = o => _Copy(t, true);\n\t\t\t\t\tm[\"Copy\"] = o => _Copy(t);\n\t\t\t\t\tif (clipLevel > 0) {\n\t\t\t\t\t\t_MiPaste(false);\n\t\t\t\t\t\tif (level == 1) _MiPaste(true);\n\t\t\t\t\t}\n\t\t\t\t\tm.Submenu(\"Delete\", m => {\n\t\t\t\t\t\tm[$\"Delete {(level == 1 ? \"snippet\" : \"menu item\")} '{t.text}'\"] = o => _Remove(t);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvoid _New(bool into) { _AddNewOrPaste(false, t, into); }\n\t\t\t\t\n\t\t\t\tvoid _MiPaste(bool into) {\n\t\t\t\t\tif (!_CanPaste(t, into)) return;\n\t\t\t\t\tint le = level; if (into) le++;\n\t\t\t\t\tm[le == 1 ? \"Paste snippet\" : into ? \"Paste as menu item\" : \"Paste menu item\"] = o => _AddNewOrPaste(true, t, into);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (level == 0) {\n\t\t\t\t\tm.Separator();\n\t\t\t\t\tm[\"Sort snippets\", disable: t.Count < 2] = o => _Sort(t);\n\t\t\t\t\tif (t.isImportedFile || (t.Count == 0 && !t.text.Eqi(\"Snippets.xml\"))) {\n\t\t\t\t\t\tm.Separator();\n\t\t\t\t\t\tm[$\"Delete this {(t.isImportedFile ? \"imported\" : \"empty\")} file...\"] = o => _ImportDeleteFile(t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (level > 0) {\n\t\t\t\tm[\"Copy\"] = o => _Copy(t);\n\t\t\t}\n\t\t\t\n\t\t\tm.Show(owner: this);\n\t\t}\n\t}\n\t\n\tvoid _Copy(_Item t, bool cut = false) {\n\t\tif (_clip != null) _tv.Redraw(_clip);\n\t\t_clip = t;\n\t\t_cut = cut;\n\t\t_tv.Redraw(t); //red etc\n\t}\n\t\n\tbool _CanPaste(_Item t, bool into) {\n\t\tint level = t.Level + (into ? 1 : 0), clipLevel = _clip?.Level ?? -1;\n\t\tif (clipLevel != level && !(level == 2 && clipLevel == 1 && !_clip.IsFolder)) return false;\n\t\tif (_clip == t.Parent || (_cut && _clip == t)) return false;\n\t\treturn true;\n\t}\n\t\n\t//rejected: select/copy multiple. Rarely need. Can edit file text.\n\t\n\tvoid _AddNewOrPaste(bool paste, _Item anchor, bool into) {\n\t\tint level = anchor.Level + (into ? 1 : 0);\n\t\t_Item t;\n\t\tif (paste) {\n\t\t\tif (_cut) {\n\t\t\t\tt = _clip;\n\t\t\t\tif (level == 2 && t.Level == 1) {\n\t\t\t\t\tif (!t.info.NE()) t.text = t.info;\n\t\t\t\t\tif (!t.more.NE()) print.it($\"{t.text} Info+ was:\\r\\n{t.more}\");\n\t\t\t\t\tif (!t.context.NE()) print.it($\"{t.text} Context was:\\r\\n{t.context}\");\n\t\t\t\t\tt.info = t.more = t.context = null;\n\t\t\t\t}\n\t\t\t\t_Remove(t, cut: true);\n\t\t\t} else {\n\t\t\t\tt = new _Item(this, level, _clip.text);\n\t\t\t\tt.code = _clip.code;\n\t\t\t\tif (level == 1) {\n\t\t\t\t\tt.info = _clip.info;\n\t\t\t\t\tt.more = _clip.more;\n\t\t\t\t\tt.context = _clip.context;\n\t\t\t\t} else {\n\t\t\t\t\tif (_clip.Level == 1 && !_clip.info.NE()) t.text = _clip.info;\n\t\t\t\t}\n\t\t\t\tt.print_ = _clip.print_;\n\t\t\t\tt.using_ = _clip.using_;\n\t\t\t\tt.meta_ = _clip.meta_;\n\t\t\t\tt.var_ = _clip.var_;\n\t\t\t\tt.isChecked = _clip.isChecked;\n\t\t\t\tforeach (var v in _clip.Children()) {\n\t\t\t\t\tvar c = new _Item(this, 2, v.text) { code = v.code, print_ = v.print_, using_ = v.using_, meta_ = v.meta_, var_ = v.var_ };\n\t\t\t\t\tt.AddChild(c);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tt = new _Item(this, level, level == 1 ? \"Snippet\" : \"\");\n\t\t\tt.isChecked = level == 1;\n\t\t}\n\t\t\n\t\tif (level == 2 && into && !anchor.IsFolder) { //convert anchor to menu\n\t\t\tvar u = new _Item(this, level, anchor.info?.TrimEnd('.')) { code = anchor.code };\n\t\t\tanchor.code = null;\n\t\t\tanchor.AddChild(u);\n\t\t}\n\t\t\n\t\tif (into) anchor.AddChild(t); else anchor.AddSibling(t, false);\n\t\t\n\t\t_tv.SetItems(_files, true);\n\t\t\n\t\tif (paste && _cut) {\n\t\t\t\n\t\t} else {\n\t\t\tif (into) _tv.Expand(anchor, true);\n\t\t\t_SelectAndOpen(t);\n\t\t\tvar c = level == 1 ? _tName : _tInfo;\n\t\t\tc.Focus();\n\t\t\tif (paste) c.Select(0, t.text.Length - (t.text.Ends(\"Snippet\") ? 7 : t.text.Ends(\"Surround\") ? 8 : 0));\n\t\t}\n\t}\n\t\n\tvoid _SelectAndOpen(_Item t) {\n\t\t_tv.SelectSingle(t);\n\t\tif (t != _ti) _Open(t);\n\t}\n\t\n\tvoid _SelectAndOpen(string s) {\n\t\tif (!s.Split2_('|', out var snippet, out var file, 1, 1)) return;\n\t\tforeach (var f in _files) {\n\t\t\tif (f.text.Eqi(file)) {\n\t\t\t\tif (f.Children().FirstOrDefault(o => o.text == snippet) is { } t) {\n\t\t\t\t\t_SelectAndOpen(t);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\t\n\tvoid _Remove(_Item t, bool cut = false) {\n\t\t_clip = null;\n\t\tvar p = t.Parent;\n\t\tt.Remove();\n\t\t\n\t\tif (p.Level == 1 && p.Count == 1) { //convert p to non-menu\n\t\t\tvar u = p.FirstChild;\n\t\t\tp.code = u.code;\n\t\t\tp.info ??= u.text;\n\t\t\tp.more ??= u.more;\n\t\t\tp.context ??= u.context;\n\t\t\tp.print_ ??= u.print_;\n\t\t\tp.using_ ??= u.using_;\n\t\t\tp.meta_ ??= u.meta_;\n\t\t\tp.var_ ??= u.var_;\n\t\t\tu.Remove();\n\t\t}\n\t\t\n\t\tif (!cut) _tv.SetItems(_files, true);\n\t\t\n\t\tif (p.Level == 1 && !cut) {\n\t\t\t_SelectAndOpen(p);\n\t\t} else if (_ti == t) {\n\t\t\t_SelectNone();\n\t\t}\n\t}\n\t\n\tvoid _SelectNone() {\n\t\t_ti = null;\n\t\t_clip = null;\n\t\t_panelFile.Visibility = Visibility.Collapsed;\n\t\t_panelSnippet.Visibility = Visibility.Collapsed;\n\t}\n\t\n\t//rejected: a main menu command or hotkey here to quickly add new snippet from selected text or clipboard text.\n\t//\tCannot know where and what snippet type (snippet or menu item) to add.\n\t//void _QuickAddSnippet() {\n\t\n\t//}\n\t\n\tvoid _Sort(_Item t) {\n\t\tvar a = t.Children().OrderBy(o => o.text, CiUtil.SortComparer).ToArray();\n\t\tforeach (var v in a) v.Remove();\n\t\tforeach (var v in a) t.AddChild(v);\n\t\t_tv.SetItems(_files, modified: true);\n\t}\n\t\n\tvoid _InsertVar(string s) {\n\t\tvar text = _code.aaaText;\n\t\tvar (selStart, selEnd) = _code.aaaSelection(true);\n\t\tbool inPlaceholder = selStart >= 4 && selEnd < text.Length && text[selEnd] == '}' && text[selStart - 1] == ':' && text.LastIndexOf('$', selStart - 2) is int i1 && i1 >= 0 && text.RxIsMatch(@\"\\G\\$\\{\\d+:\\z\", range: i1..selStart);\n\t\t\n\t\tif (s != null) {\n\t\t\tif (inPlaceholder) return;\n\t\t\tif (s == \"${1:}\") {\n\t\t\t\tint n = 1; while (text.RxIsMatch($@\"\\$(?:{{{n}[:}}]|{n}(?!\\d))\")) n++;\n\t\t\t\ts = $\"${{{n}:{_code.aaaSelectedText()}}}\";\n\t\t\t}\n\t\t\t_code.aaaReplaceSel(s);\n\t\t\tif (s.RxIsMatch(@\"^\\$\\{\\d+:\\}$\")) _code.Call(Sci.SCI_CHARLEFT);\n\t\t\telse if (s == \"$1\") _code.Call(Sci.SCI_CHARLEFTEXTEND);\n\t\t\t_code.Focus();\n\t\t} else {\n\t\t\tvar m = new popupMenu();\n\t\t\tAction<PMItem> aIns = o => {\n\t\t\t\tvar s = o.Text;\n\t\t\t\tif (inPlaceholder) s = \"$\" + s[2..^1];\n\t\t\t\t_code.aaaReplaceSel(s);\n\t\t\t\t_code.Focus();\n\t\t\t};\n\t\t\tm[\"${SELECTED_TEXT}\"] = aIns;\n\t\t\tm[\"${GUID}\"] = aIns;\n\t\t\tm[\"${RANDOM}\"] = aIns;\n\t\t\tm[\"${RANDOM_HEX}\"] = aIns;\n\t\t\tm[\"${VAR}\"] = aIns;\n\t\t\tm.Show();\n\t\t}\n\t}\n\t\n\tvoid _PasteCode(string s) {\n\t\tif (s.NE()) return;\n\t\tvar a = s.Lines();\n\t\tint indent = (int)a.Min(o => (uint)o.FindNot(\"\\t\"));\n\t\ts = string.Join(\"\\r\\n\", a.Select(o => o[indent..]));\n\t\t_code.aaaReplaceRange(false, 0, -1, s);\n\t}\n\t\n\tvoid _TvSetText(string s) {\n\t\t_ti.text = s;\n\t\t_tv.Redraw(_ti, true);\n\t}\n\t\n\tstatic HashSet<string> _GetHiddenSnippets(string fileName, bool orAdd = false) {\n\t\tvar files = App.Settings.ci_hiddenSnippets ??= (orAdd ? new() : null);\n\t\tif (files == null) return null;\n\t\tfileName = fileName.Lower();\n\t\tif (files.TryGetValue(fileName, out var r)) return r;\n\t\tif (orAdd) files.Add(fileName, r = new());\n\t\treturn r;\n\t}\n\t\n\tpublic static HashSet<string> GetHiddenSnippets(string fileName) => _GetHiddenSnippets(fileName);\n\t\n\t/// <summary>\n\t/// Saves hidden snippets of a snippets file in <c>App.Settings.ci_hiddenSnippets</c>. Removes if <i>hs</i> is null or empty.\n\t/// </summary>\n\tstatic void _SaveHiddenSnippets(string fileName, HashSet<string> hs = null) {\n\t\tif (hs?.Count > 0) {\n\t\t\t(App.Settings.ci_hiddenSnippets ??= new())[fileName.Lower()] = hs;\n\t\t} else {\n\t\t\tApp.Settings.ci_hiddenSnippets?.Remove(fileName.Lower());\n\t\t}\n\t}\n\t\n\tvoid _FillTree() {\n\t\tstring snippetsDir = AppSettings.DirBS, defSnippets = CiSnippets.DefaultFile;\n\t\tif (!filesystem.exists(CiSnippets.CustomFile).File) {\n\t\t\ttry { filesystem.saveText(CiSnippets.CustomFile, \"<snippets></snippets>\"); }\n\t\t\tcatch { }\n\t\t}\n\t\t_files = new();\n\t\t\n\t\tforeach (var f in filesystem.enumFiles(snippetsDir, \"*Snippets.xml\")) _File(f.FullPath, f.Name, false);\n\t\t_File(defSnippets, \"default\", true);\n\t\t\n\t\t_tv.SetItems(_files);\n\t\t\n\t\tvoid _File(string path, string name, bool isDefault) {\n\t\t\ttry {\n\t\t\t\tvar hidden = _GetHiddenSnippets(name);\n\t\t\t\tvar xf = CiSnippets.LoadSnippetsFile_(path);\n\t\t\t\tif (xf == null) return;\n\t\t\t\tvar tf = new _Item(this, 0, name) { filePath = path, isImportedFile = !isDefault && xf.HasAttr(\"imported\") };\n\t\t\t\tif (hidden == null || !hidden.Contains(\"\")) tf.isChecked = true;\n\t\t\t\t_files.Add(tf);\n\t\t\t\tvar e = xf.Elements(\"snippet\");\n\t\t\t\tif (isDefault) e = e.OrderBy(o => o.Attr(\"name\"), CiUtil.SortComparer); //rejected: order in all files. Instead there is a context menu command \"Sort\".\n\t\t\t\tforeach (var xs in e) {\n\t\t\t\t\tvar ts = new _Item(this, 1, xs.Attr(\"name\")) {\n\t\t\t\t\t\tinfo = xs.Attr(\"info\"),\n\t\t\t\t\t\tmore = xs.Attr(\"more\"),\n\t\t\t\t\t\tcontext = xs.Attr(\"context\"),\n\t\t\t\t\t\tprint_ = xs.Attr(\"print\"),\n\t\t\t\t\t\tusing_ = xs.Attr(\"using\"),\n\t\t\t\t\t\tmeta_ = xs.Attr(\"meta\"),\n\t\t\t\t\t\tvar_ = xs.Attr(\"var\")\n\t\t\t\t\t};\n\t\t\t\t\tif (hidden == null || !hidden.Contains(ts.text)) ts.isChecked = true;\n\t\t\t\t\ttf.AddChild(ts);\n\t\t\t\t\tif (xs.HasElements) {\n\t\t\t\t\t\tforeach (var xi in xs.Elements(\"list\")) {\n\t\t\t\t\t\t\tvar ti = new _Item(this, 2, xi.Attr(\"item\")) {\n\t\t\t\t\t\t\t\tcode = xi.Value,\n\t\t\t\t\t\t\t\tprint_ = xi.Attr(\"print\"),\n\t\t\t\t\t\t\t\tusing_ = xi.Attr(\"using\"),\n\t\t\t\t\t\t\t\tmeta_ = xi.Attr(\"meta\"),\n\t\t\t\t\t\t\t\tvar_ = xi.Attr(\"var\")\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\tts.AddChild(ti);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tts.code = xs.Value;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\ttf.fileXml = _ToXml(tf).ToString(); //to detect when need to save\n\t\t\t}\n\t\t\tcatch (Exception e1) { print.it(e1.ToStringWithoutStack()); }\n\t\t}\n\t}\n\t\n\tXElement _ToXml(_Item f) {\n\t\tvar xf = new XElement(\"snippets\");\n\t\tforeach (var s in f.Children()) {\n\t\t\tvar xs = new XElement(\"snippet\", new XAttribute(\"name\", s.text ?? \"\"), new XAttribute(\"info\", s.info ?? \"\"));\n\t\t\txf.Add(xs);\n\t\t\tif (s.context != null) xs.SetAttributeValue(\"context\", s.context);\n\t\t\tif (s.more != null) xs.SetAttributeValue(\"more\", s.more);\n\t\t\t_Snippet(s, xs, false);\n\t\t\tforeach (var i in s.Children()) {\n\t\t\t\tvar xi = new XElement(\"list\", new XAttribute(\"item\", i.text ?? \"\"));\n\t\t\t\txs.Add(xi);\n\t\t\t\t_Snippet(i, xi, true);\n\t\t\t}\n\t\t}\n\t\treturn xf;\n\t\t\n\t\tstatic void _Snippet(_Item t, XElement x, bool li) {\n\t\t\tif (t.code != null) {\n\t\t\t\tx.Add(\"\\n\");\n\t\t\t\tx.Add(new XCData(t.code));\n\t\t\t\tx.Add(li ? \"\\n      \" : \"\\n    \");\n\t\t\t}\n\t\t\tif (t.print_ != null) x.SetAttributeValue(\"print\", t.print_);\n\t\t\tif (t.using_ != null) x.SetAttributeValue(\"using\", t.using_);\n\t\t\tif (t.meta_ != null) x.SetAttributeValue(\"meta\", t.meta_);\n\t\t\tif (t.var_ != null) x.SetAttributeValue(\"var\", t.var_);\n\t\t}\n\t}\n\t\n\tvoid _Save() {\n\t\tforeach (var f in _files) {\n\t\t\tif (f.text != \"default\") {\n\t\t\t\tvar xf = _ToXml(f);\n\t\t\t\tvar xml = xf.ToString();\n\t\t\t\tif (xml != f.fileXml) {\n\t\t\t\t\tf.fileXml = xml;\n\t\t\t\t\tfilesystem.saveText(f.filePath, xml);\n\t\t\t\t}\n\t\t\t}\n\t\t\tvar hs = f.Descendants().Where(o => o.Level == 1 && !o.isChecked).Select(o => o.text).ToHashSet();\n\t\t\tif (!f.isChecked) hs.Add(\"\");\n\t\t\t_SaveHiddenSnippets(f.text, hs);\n\t\t}\n\t\tCiSnippets.Reload();\n\t}\n\t\n\tclass _Item : TreeBase<_Item>, ITreeViewItem {\n\t\tDSnippets _d;\n\t\tpublic string text; //displayed text. Depends on level: 0 filename or \"default\", 1 snippet name, 2 snippet item info\n\t\tpublic string filePath, fileXml; //level 0\n\t\tpublic string code, context, info, more, print_, using_, meta_, var_; //level 1 or 2\n\t\tpublic bool isChecked, isImportedFile;\n\t\tbool _isExpanded;\n\t\t\n\t\tpublic _Item(DSnippets d, int level, string text) {\n\t\t\t_d = d;\n\t\t\tthis.text = text;\n\t\t\t_isExpanded = level == 0;\n\t\t}\n\t\t\n\t\tpublic bool IsReadonly => RootAncestor.text == \"default\";\n\t\t\n\t\t#region ITreeViewItem\n\t\t\n\t\tvoid ITreeViewItem.SetIsExpanded(bool yes) { _isExpanded = yes; }\n\t\t\n\t\tpublic bool IsExpanded => _isExpanded;\n\t\t\n\t\tIEnumerable<ITreeViewItem> ITreeViewItem.Items => base.Children();\n\t\t\n\t\tpublic bool IsFolder => base.HasChildren;\n\t\t\n\t\tstring ITreeViewItem.DisplayText => text;\n\t\t\n\t\tobject ITreeViewItem.Image => Level switch {\n\t\t\t0 => \"*Modern.PageXml\" + EdIcons.black,\n\t\t\t1 => IsFolder ? s_snippetMenuIcon : \"*Codicons.SymbolSnippet\" + EdIcons.blue,\n\t\t\t_ => \"*Material.Asterisk @12\" + EdIcons.blue,\n\t\t};\n\t\tstatic string[] s_snippetMenuIcon = [\"*Codicons.SymbolSnippet\" + EdIcons.blue, \"*Material.Asterisk @8\" + EdIcons.blue];\n\t\t\n\t\tTVCheck ITreeViewItem.CheckState => Level == 2 ? TVCheck.None : isChecked ? TVCheck.Checked : TVCheck.Unchecked;\n\t\t\n\t\tint ITreeViewItem.TextColor(TVColorInfo ci) => this == _d._clip ? (_d._cut ? 0xFF0000 : 0x00A000) : -1;\n\t\t\n\t\t#endregion\n\t}\n\t\n\tvoid _tv_KeyDown(object sender, KeyEventArgs e) {\n\t\tvar k = (e.KeyboardDevice.Modifiers, e.Key);\n\t\tswitch (k) {\n\t\tcase (0, Key.Escape):\n\t\t\tif (_clip != null) {\n\t\t\t\t_clip = null;\n\t\t\t\t_tv.Redraw();\n\t\t\t}\n\t\t\tgoto gh;\n\t\t}\n\t\tif (_tv.FocusedItem is _Item t) {\n\t\t\tswitch (k) {\n\t\t\tcase (0, Key.Delete):\n\t\t\t\tif (!t.IsReadonly && t.Level > 0 && dialog.showOkCancel(\"Delete?\", t.text, owner: this)) _Remove(t);\n\t\t\t\tbreak;\n\t\t\tcase (ModifierKeys.Control, Key.X):\n\t\t\t\tif (!t.IsReadonly && t.Level > 0) _Copy(t, true);\n\t\t\t\tbreak;\n\t\t\tcase (ModifierKeys.Control, Key.C):\n\t\t\t\tif (t.Level > 0) _Copy(t);\n\t\t\t\tbreak;\n\t\t\tcase (ModifierKeys.Control, Key.V):\n\t\t\t\tif (!t.IsReadonly && _clip != null) {\n\t\t\t\t\tbool into = _clip.Level > t.Level;\n\t\t\t\t\tif (_CanPaste(t, into)) _AddNewOrPaste(true, t, into);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tdefault: return;\n\t\t\t}\n\t\t}\n\t\tgh:\n\t\te.Handled = true;\n\t}\n\t\n\t#region import\n\t\n\tvoid _ImportMenu() {\n\t\tvar m = new popupMenu();\n\t\tm[\"Import VS snippets...\"] = o => _ImportVS();\n\t\tm[\"Import VSCode snippets...\"] = o => _ImportJson();\n\t\tm.Show(owner: this);\n\t}\n\t\n\tvoid _ImportSave(XElement x) {\n\t\tx.SetAttributeValue(\"imported\", \"\");\n\t\tstring dir = AppSettings.DirBS, file = null;\n\t\tfor (int i = 1; ; i++) { //unique filename\n\t\t\tfile = AppSettings.DirBS + \"Imported\" + i + \" snippets.xml\";\n\t\t\tif (!filesystem.exists(file)) {\n\t\t\t\tx.Save(file);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\t_SelectNone();\n\t\t_FillTree();\n\t\tif (_files.FirstOrDefault(o => o.filePath == file) is { } f) {\n\t\t\t_tv.SelectSingle(f, scrollTop: true);\n\t\t\t_Open(f);\n\t\t}\n\t\tCiSnippets.Reload();\n\t}\n\t\n\tvoid _ImportVS() {\n\t\tvar d = new FileOpenSaveDialog(\"fc3a7c73-3be0-43f2-af26-7d4d6b6c381e\") { FileTypes = \"VS snippets|*.snippet\", Title = \"Import snippet files\", FileNameLabel = \"Selected files:\" };\n\t\tif (!d.ShowOpen(out string[] files, owner: this)) return;\n\t\ttry {\n\t\t\tvar x = _ImportVS(files);\n\t\t\t_ImportSave(x);\n\t\t}\n\t\tcatch (Exception ex) { dialog.showError(\"Failed to import snippets.\", ex.ToString(), owner: this); }\n\t}\n\t\n\tvoid _ImportJson() {\n\t\tvar d = new FileOpenSaveDialog(\"89bf0c8f-f42b-49ca-8644-649acecfe92f\") { FileTypes = \"VSCode snippets|*.json\", Title = \"Import snippets file\" };\n\t\tif (!d.ShowOpen(out string file, owner: this)) return;\n\t\ttry {\n\t\t\tvar x = _ImportJson(file);\n\t\t\t_ImportSave(x);\n\t\t}\n\t\tcatch (Exception ex) { dialog.showError(\"Failed to import snippets.\", ex.ToString(), owner: this); }\n\t}\n\t\n\tstatic XElement _ImportVS(IEnumerable<string> files) {\n\t\tvar xRoot = new XElement(\"snippets\");\n\t\tforeach (var f in files) {\n\t\t\ttry { _AddFile(f); }\n\t\t\tcatch (Exception ex) { print.it($\"Failed to convert {pathname.getName(f)}. {ex}\"); }\n\t\t}\n\t\treturn xRoot;\n\t\t\n\t\tvoid _AddFile(string file) {\n\t\t\tvar xSnippets = XElement.Load(file);\n\t\t\t//print.it($\"<><bc green>{file}<>\\r\\n{xSnippets}\");\n\t\t\tvar ns = xSnippets.Name.Namespace;\n\t\t\tforeach (var xSnippet in xSnippets.Elements(ns + \"CodeSnippet\")) {\n\t\t\t\tif (xSnippet.Attr(\"Format\") is string format && !format.Starts(\"1.\")) { print.warning($\"Snippet format {format} not supported.\"); continue; }\n\t\t\t\tvar xs = xSnippet.Element(ns + \"Snippet\");\n\t\t\t\tvar s = xs.Elem(ns + \"Code\", \"Language\", \"csharp\", true)?.Value;\n\t\t\t\tif (_ImportSkip(s)) continue;\n\t\t\t\t\n\t\t\t\tList<(string id, string def)> ad = null;\n\t\t\t\tif (xs.Element(ns + \"Declarations\") is { } xDecls) {\n\t\t\t\t\tforeach (var xl in xDecls.Elements(ns + \"Literal\")) {\n\t\t\t\t\t\tvar def = xl.Element(ns + \"Default\")?.Value;\n\t\t\t\t\t\tif (def == null) {\n\t\t\t\t\t\t\tif (xl.Element(ns + \"Function\")?.Value is string k && k.Like(\"SimpleTypeName(*)\")) {\n\t\t\t\t\t\t\t\tk = k[15..^1];\n\t\t\t\t\t\t\t\tif (k.Starts(\"global::\")) k = k[8..];\n\t\t\t\t\t\t\t\tif (k.Starts(\"System.\") && k.IndexOf('.', 7) < 0) k = k[7..];\n\t\t\t\t\t\t\t\tdef = k;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tvar id = xl.Element(ns + \"ID\").Value;\n\t\t\t\t\t\t//if (xl.Attr(\"Editable\") == \"false\") print.it(xl);\n\t\t\t\t\t\tif (xl.Attr(\"Editable\") == \"false\" && !def.NE()) s = s.Replace($\"${id}$\", def);\n\t\t\t\t\t\telse (ad ??= new()).Add((id, def ?? \"EDIT\"));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\ts = s.Replace(\"$selected$ $end$\", \"$selected$$end$\");\n\t\t\t\tif (s.Find(\"$end$\") is int i1 && i1 >= 0 && i1 + 5 is int i1end) s = s.ReplaceAt(i1..i1end, s.Length > i1end && s[i1end] is >= '0' and <= '9' ? \"${0}\" : \"$0\");\n\t\t\t\ts = s.Replace(\"$selected$\", \"${SELECTED_TEXT}\");\n\t\t\t\tif (ad != null) {\n\t\t\t\t\tint n = 1;\n\t\t\t\t\tforeach (var v in ad) s = s.Replace($\"${v.id}$\", $\"${{{n++}:{v.def}}}\");\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvar xh = xSnippet.Element(ns + \"Header\");\n\t\t\t\t\n\t\t\t\tvar x = _ImportAddXElement(xRoot, s, xh.Element(ns + \"Shortcut\").Value, xh.Element(ns + \"Description\")?.Value);\n\t\t\t\tif (xs.Element(ns + \"Imports\") is { } xImps) {\n\t\t\t\t\tStringBuilder bu = new();\n\t\t\t\t\tforeach (var xi in xImps.Elements(ns + \"Import\")) {\n\t\t\t\t\t\tif (xi.Element(ns + \"Namespace\") is { } xns) bu.Append(bu.Length > 0 ? \"; \" : null).Append(xns.Value);\n\t\t\t\t\t}\n\t\t\t\t\tif (bu.Length > 0) x.SetAttributeValue(\"using\", bu.ToString());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t\n\tstatic XElement _ImportJson(string file) {\n\t\tvar xRoot = new XElement(\"snippets\");\n\t\t\n\t\tvar jRoot = JsonNode.Parse(filesystem.loadText(file));\n\t\t//print.it(jRoot.ToJsonString(new() { WriteIndented = true }));\n\t\tforeach (var jSnippet in jRoot as JsonObject) {\n\t\t\tvar info = jSnippet.Key;\n\t\t\ttry {\n\t\t\t\tvar j = jSnippet.Value.AsObject();\n\t\t\t\tvar code = _ToString(j[\"body\"]);\n\t\t\t\tcode = code.Replace(\"${TM_SELECTED_TEXT}\", \"${SELECTED_TEXT}\").Replace(\"${UUID}\", \"${GUID}\");\n\t\t\t\tif (_ImportSkip(code)) continue;\n\t\t\t\tvar more = _ToString(j[\"description\"]);\n\t\t\t\tvar n = j[\"prefix\"];\n\t\t\t\tif (n is JsonArray ap) {\n\t\t\t\t\tforeach (var v in ap) _Add((string)v);\n\t\t\t\t} else {\n\t\t\t\t\t_Add((string)n);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvoid _Add(string prefix) {\n\t\t\t\t\t_ImportAddXElement(xRoot, code, prefix, info, more);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tstatic string _ToString(JsonNode n) {\n\t\t\t\t\tif (n is JsonArray a) return string.Join(\"\\r\\n\", a.GetValues<string>());\n\t\t\t\t\treturn (string)n;\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception ex) { print.it($\"Failed to convert {info}. {ex}\"); }\n\t\t}\n\t\t\n\t\treturn xRoot;\n\t}\n\t\n\tstatic bool _ImportSkip(string code) => code.NE() || 0 != code.Starts(false, \"<\", \"Microsoft Visual Studio Solution File\", \"@\"); //project or solution file, XML, razor\n\t\n\tstatic XElement _ImportAddXElement(XElement xRoot, string code, string namePrefix, string info, string more = null) {\n\t\tvar x = new XElement(\"snippet\", \"\\n\", new XCData(code ?? \"\"), \"\\n    \");\n\t\tx.SetAttributeValue(\"name\", namePrefix/*.RxReplace(@\"[^\\p{Xan}_#]+\", \"_\")*/ + \"Snippet\");\n\t\tx.SetAttributeValue(\"info\", info);\n\t\tif (!more.NE() && more != info) x.SetAttributeValue(\"more\", more);\n\t\txRoot.Add(x);\n\t\treturn x;\n\t}\n\t\n\tvoid _ImportDeleteFile(_Item f) {\n\t\tif (!dialog.showOkCancel(\"Delete this snippets file?\", $\"{f.filePath}\", owner: this)) return;\n\t\tif (false == filesystem.delete(f.filePath, FDFlags.CanFail | FDFlags.RecycleBin)) return;\n\t\t_SaveHiddenSnippets(f.text);\n\t\t_SelectNone();\n\t\t_FillTree();\n\t\tCiSnippets.Reload();\n\t}\n\t\n\t#endregion\n}\n"
  },
  {
    "path": "Au.Editor/Tools/DWinapi.cs",
    "content": "using System.Windows;\nusing System.Windows.Controls;\nusing Au.Controls;\nusing ToolLand;\n\nnamespace LA;\n\nclass DWinapi : KDialogWindow {\n\tsqlite _db;\n\tTextBox tName;\n\tKSciCodeBox code;\n\n\tpublic DWinapi(string name = null) {\n\t\tvar doc = Panels.Editor.ActiveDoc;\n\t\tInitWinProp(\"Find Windows API\", App.Wmain);\n\n\t\tif (name == null) {\n\t\t\tname = doc.aaaSelectedText();\n\t\t\tif (!name.NE()) {\n\t\t\t\tname = name.RxReplace(@\"\\W+\", \" \");\n\t\t\t}\n\t\t}\n\n\t\tvar b = new wpfBuilder(this).WinSize(800, 500);\n\t\tb.R.Add(\"Name\", out tName, name);\n\t\tb.Row(-1).Add(out code); code.AaInitBorder = true;\n\t\tb.R.StartOkCancel().AddOkCancel(\"OK, copy to clipboard\").xAddDialogHelpButtonAndF1(_Help).End();\n\t\tb.End();\n\n\t\tb.OkApply += o => {\n\t\t\tstring s = code.aaaText;\n\t\t\tif (s.NE()) return;\n\t\t\tClipboard.SetText(s);\n\t\t};\n\n\t\ttName.TextChanged += (_, _) => _TextChanged();\n\n\t\tvoid _Help() {\n\t\t\tvar s = @\"/**\nHere you can find Windows API declarations: function, struct, enum, interface, delegate, constant, GUID.\nEnter API name in the above field. Case-sensitive. Wildcard examples: Start*, *End, *Part*.\nOr multiple full names, like Name1 Name2 Name3.\nYou can in editor select text with one or more names and open this window. Or use error tooltips.\nIf script is without a class, create new class (or convert script to class) and paste declarations there.\n\nAlso try snippet nativeApiSnippet (select it from the completion list when text cursor is where classes can be,\nfor example at the end of script). It adds a special class. Then anywhere in script just type class name, dot,\nand select from the list. It adds the declaration to the class, and more declarations if need.\n\nThe database contains ~200_000 declarations. They are not perfect. You can edit.\nData source: https://github.com/microsoft/win32metadata\n\nSee also: menu Edit > Generate > Convert [PreserveSig] methods.\n*/\";\n\t\t\tcode.AaSetText(s);\n\t\t}\n\t}\n\n\tprotected override void OnSourceInitialized(EventArgs e) {\n\t\t_db = EdDatabases.OpenWinapi();\n\t\tif (!tName.Text.NE()) _TextChanged();\n\n\t\tbase.OnSourceInitialized(e);\n\t}\n\n\tprotected override void OnClosed(EventArgs e) {\n\t\t_db?.Dispose();\n\t\t_db = null;\n\t\tbase.OnClosed(e);\n\t}\n\n\tvoid _TextChanged() {\n\t\tstring name = tName.Text;\n\t\tvar a = new List<(string name, string code)>();\n\n\t\tint nWild = 0; for (int i = 0; i < name.Length; i++) { switch (name[i]) { case '*': case '?': nWild++; break; } }\n\t\tif (name.Length > 0 && (nWild == 0 || name.Length - nWild >= 2)) {\n\t\t\tstring sql;\n\t\t\tif (name.Contains(' ')) sql = $\"in ('{string.Join(\"', '\", name.RxFindAll(@\"\\b[A-Za-z_]\\w\\w+\", (RXFlags)0))}')\";\n\t\t\telse if (name.FindAny(\"*?\") >= 0) sql = $\"GLOB '{name}'\";\n\t\t\telse sql = $\"= '{name}'\";\n\t\t\ttry {\n\t\t\t\tusing var stat = _db.Statement(\"SELECT name, code FROM api WHERE name \" + sql);\n\t\t\t\t//perf.first();\n\t\t\t\twhile (stat.Step()) a.Add((stat.GetText(0), stat.GetText(1)));\n\t\t\t\t//perf.nw(); //30 ms cold, 10 ms warm. Without index.\n\t\t\t}\n\t\t\tcatch (SLException ex) { Debug_.Print(ex); }\n\t\t}\n\n\t\tstring s = \"\";\n\t\tif (a.Count != 0) {\n\t\t\ts = a[0].code;\n\t\t\tif (a.Count > 1) s = string.Join(s.Starts(\"internal const\") ? \"\\r\\n\" : \"\\r\\n\\r\\n\", a.Select(o => o.code));\n\t\t\ts += \"\\r\\n\";\n\t\t}\n\t\tcode.AaSetText(s);\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Tools/Delm.cs",
    "content": "using Au.Controls;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Input;\n\nnamespace ToolLand;\n\n//TODO3: if checked 'state', activate window before test. Else different FOCUSED etc.\n\n//CONSIDER: here and in Dwnd: UI for \"contains image\".\n//\tAlso then need to optimize \"elm containing image\". Now with image finder in 'also' gets pixels multiple times.\n\n//PROBLEM: hangs when trying to get a property while the target window does not respond. Only MSAA, not UIA.\n//\tEg when the Windows search UI hidden after capturing. Then its process is suspended?\n\n//note: don't use access keys (_ in control names). It presses the button without Alt, eg when the user forgets to activate editor and starts typing.\n\n//TEST: CoCancelCall/CoTestCancel.\n\n//TODO3: auto detect when should use Invoke and when WebInvoke, and suggest to switch.\n//\tIf now Invoke, and role web:, and after executing action soon changed window title, suggest WebInvoke.\n//\tIf now WebInvoke, and role not web:, and after executing action does not change window title for eg 5 s, suggest to stop waiting and use Invoke.\n//\tOr just add pseudo action Auto. If \"web:\", use WebInvoke, else if has action, use Invoke, else MouseClick.\n\n//TODO3: on Win11 capturing and finding in some places doesn't work because an element in the path is an invisible control.\n//\tExamples: taskbar, Paint.\n//\tNow by default auto-switches to UIA; it works, but then broken Invoke (in taskbar only).\n\n//CONSIDER: action \"screenshot\".\n\n//note: in System Informer window all UIA API hang for 2 s. Mixed acc/UIA capturing too. Pure acc OK. Same if notinproc. Other tested apps too (Inspect, PAD, UiPath).\n\nclass Delm : KDialogWindow {\n\telm _elm;\n\twnd _wnd, _con;\n\tbool _useCon;\n\tbool _wndNoActivate;\n\tstring _wndName;\n\tstring _screenshot;\n\t\n\tKSciInfoBox _info;\n\tButton _bTest, _bInsert, _bWindow;\n\tComboBox _cbAction;\n\tKCheckBox _cCapture, _cAutoTestAction, _cAutoInsert, _cControl, _cException, _cScroll;\n\tCheckBox _cUIA;\n\tKCheckTextBox _wait, _xy;\n\t_PropPage _page, _commonPage;\n\tBorder _pageBorder;\n\tKSciCodeBoxWnd _code;\n\tKTreeView _tree;\n\t\n\tpartial class _PropPage {\n\t\tDelm _dlg;\n\t\t\n\t\tpublic KCheckTextBox roleA, nameA, uiaidA, uiacnA, idA, classA, valueA, descriptionA, actionA, keyA, helpA, urlA, elemA, stateA, rectA, alsoA, skipA, navigA, notinA, maxccA, levelA;\n\t\tpublic KCheckBox inPath, hiddenTooA, reverseA, uiaA, notInprocA, clientAreaA, menuTooA;\n\t\tpublic Border htmlAttr;\n\t\tpublic Grid panel;\n\t\tpublic _TreeItem ti;\n\t\t\n\t\t//elm properties, other parameters, search settings\n\t\tpublic _PropPage(Delm dlg) {\n\t\t\t//print.it(\"_PropPage\");\n\t\t\t_dlg = dlg;\n\t\t\t\n\t\t\tvar b = new wpfBuilder().Height(180).Columns(-2, 0, -1);\n\t\t\tpanel = b.Panel as Grid;\n\t\t\t//elm properties (left side)\n\t\t\tb.R.xStartPropertyGrid(\"L2 TRB\").Height = panel.Height;\n\t\t\troleA = b.xAddCheckText<KTextExpressionBox>(\"role\");\n\t\t\tnameA = b.xAddCheckText<KTextExpressionBox>(\"name\");\n\t\t\t//note: these must be == prop property names\n\t\t\tuiaidA = b.xAddCheckText<KTextExpressionBox>(\"uiaid\");\n\t\t\tuiacnA = b.xAddCheckText<KTextExpressionBox>(\"uiacn\");\n\t\t\tidA = b.xAddCheckText<KTextExpressionBox>(\"id\");\n\t\t\tclassA = b.xAddCheckText<KTextExpressionBox>(\"class\");\n\t\t\tvalueA = b.xAddCheckText<KTextExpressionBox>(\"value\");\n\t\t\tdescriptionA = b.xAddCheckText<KTextExpressionBox>(\"desc\");\n\t\t\tactionA = b.xAddCheckText<KTextExpressionBox>(\"action\");\n\t\t\tkeyA = b.xAddCheckText<KTextExpressionBox>(\"key\");\n\t\t\thelpA = b.xAddCheckText<KTextExpressionBox>(\"help\");\n\t\t\turlA = b.xAddCheckText<KTextExpressionBox>(\"url\");\n\t\t\tb.R.Add(out htmlAttr); //HTML attributes will be added with another builder\n\t\t\telemA = b.xAddCheckText<KTextExpressionBox>(\"item\");\n\t\t\tstateA = b.xAddCheckText<KTextExpressionBox>(\"state\");\n\t\t\trectA = b.xAddCheckText<KTextExpressionBox>(\"rect\");\n\t\t\tb.xEndPropertyGrid();\n\t\t\tb.SpanRows(3);\n\t\t\tb.xAddSplitterV(span: 4, thickness: 12);\n\t\t\t//right side\n\t\t\tb.xStartPropertyGrid(\"R2 LTB\").Height = 140;\n\t\t\t//other parameters\n\t\t\talsoA = b.xAddCheckText(\"also\", \"o=>true\");\n\t\t\tskipA = b.xAddCheckText(\"skip\");\n\t\t\tnavigA = b.xAddCheckText<KTextExpressionBox>(\"navig\");\n\t\t\t//search settings\n\t\t\tb.xAddCheck(out hiddenTooA, \"Find hidden too\");\n\t\t\tb.xAddCheck(out reverseA, \"Reverse order\");\n\t\t\tb.xAddCheck(out uiaA, \"UI Automation\");\n\t\t\tb.xAddCheck(out notInprocA, \"Not in-process\");\n\t\t\tb.xAddCheck(out clientAreaA, \"Only client area\");\n\t\t\tb.xAddCheck(out menuTooA, \"Can be in menu\");\n\t\t\tnotinA = b.xAddCheckText<KTextExpressionBox>(\"notin\"); //note: these must be == prop property names\n\t\t\tmaxccA = b.xAddCheckText<KTextExpressionBox>(\"maxcc\");\n\t\t\tlevelA = b.xAddCheckText<KTextExpressionBox>(\"level\");\n\t\t\tb.xEndPropertyGrid();\n\t\t\tb.R.Skip(2).AddSeparator(vertical: false).Margin(\"T9 B9\");\n\t\t\tb.Row(-1).Skip(2).Add(out inPath, \"Add to path\").Margin(\"1\");\n\t\t\tb.End();\n\t\t\t\n\t\t\t_InitInfo();\n\t\t}\n\t}\n\t\n\tpublic Delm(POINT? p = null) {\n\t\tTitle = \"Find UI element\";\n\t\t\n\t\tvar b = new wpfBuilder(this).WinSize((500, 440..), (600, 500..)).Columns(-1, 0);\n\t\tb.R.Add(out _info).Height(60);\n\t\tb.R.StartGrid().Columns(76, 76, 130, 0, 70, -1);\n\t\t//row 1\n\t\tb.R.StartStack();\n\t\tb.xAddCheckIcon(out _cCapture, \"*Unicons.Capture\" + LA.EdIcons.red, $\"Enable capturing (hotkey {LA.App.Settings.delm.hk_capture}, and {LA.App.Settings.delm.hk_insert} to insert) and show UI element rectangles\");\n\t\tb.xAddCheckIcon(out _cAutoTestAction, \"*Material.CursorDefaultClickOutline\" + LA.EdIcons.red, \"Auto test action when captured.\\r\\nIf no action selected, will show menu.\");\n\t\tb.xAddCheckIcon(out _cAutoInsert, \"*VaadinIcons.Insert\" + LA.EdIcons.brown, \"Auto insert code when captured\");\n\t\tb.AddSeparator(true);\n\t\t//b.xAddButtonIcon(LA.EdIcons.iconUndo, _ => App.Dispatcher.InvokeAsync(() => SciUndo.OfWorkspace.UndoRedo(false)), \"Undo in editor\");\n\t\t//b.xAddButtonIcon(\"*Material.SquareEditOutline\" + LA.EdIcons.blue, _ => App.Hmain.ActivateL(true), \"Activate editor window\");\n\t\tb.xAddButtonIcon(\"*FontAwesome.WindowMaximizeRegular\" + LA.EdIcons.blue, _ => _wnd.ActivateL(true), \"Activate captured window\");\n\t\tb.AddSeparator(true);\n\t\tb.xAddButtonIcon(\"*Material.CursorDefaultClickOutline\" + LA.EdIcons.black, _ => _Test(testAction: true), \"Test action\");\n\t\tb.xAddButtonIcon(\"*Material.CursorDefaultClickOutline\" + LA.EdIcons.blue, _ => _Test(testAction: true, actWin: true), \"Activate window and test action\");\n\t\tb.AddSeparator(true);\n\t\tb.xAddButtonIcon(\"*EvaIcons.Options2\" + LA.EdIcons.green, _ => _ToolSettings(), \"Tool settings\");\n\t\tb.AddSeparator(true);\n\t\tb.Add(out _cUIA, \"UIA\").Checked(LA.App.Settings.delm.def_UIA, threeState: true).Tooltip(\"What API to use to capture UI elements:\\nChecked - UI Automation\\nUnchecked - MSAA\\nIndeterminate - auto\");\n\t\t\n\t\tb.End();\n\t\t//row 2\n\t\tb.R.AddButton(out _bTest, \"Test\", _ => _Test()).Tooltip(\"Execute the 'find' part of the code now and show the rectangle.\\r\\nRight-click for more options.\");\n\t\t_bTest.ContextMenuOpening += _bTest_ContextMenuOpening;\n\t\tb.AddButton(out _bInsert, \"Insert\", _ => _Insert(hotkey: false)).Tooltip($\"Insert code in editor.\\nHotkey {LA.App.Settings.delm.hk_insert} (while capturing).\");\n\t\tb.Add(out _cbAction).Tooltip(\"Action. Call this function when found. Or instead of Find call FindAll or create new elmFinder.\");\n\t\t_xy = b.xAddCheckText(\"x, y\", noR: true, check: _Opt.Has(_EOptions.MouseXY)); b.Span(1).Height(18); _xy.t.HorizontalScrollBarVisibility = ScrollBarVisibility.Hidden;\n\t\tb.Add(out _cScroll, \"Scroll\").Checked(_Opt.Has(_EOptions.MouseScroll));\n\t\t//row 3\n\t\tb.R.StartStack();\n\t\tb.AddButton(out _bWindow, \"Window...\", _bWnd_Click).Width(70);\n\t\tb.xAddCheck(out _cControl, \"Control\").Margin(\"R30\");\n\t\t_wait = b.xAddCheckText(\"Wait\", LA.App.Settings.delm.def_wait ?? \"1\", check: !_Opt.Has(_EOptions.NoWait)); b.Width(48);\n\t\tb.xAddCheck(out _cException, \"Fail if not found\").Checked();\n\t\tb.End();\n\t\t\n\t\tb.End();\n\t\t_ActionInit();\n\t\t_EnableDisableTopControls(false);\n\t\t\n\t\tb.R.AddSeparator(false);\n\t\t_commonPage = new(this);\n\t\tb.R.Add(out _pageBorder).Margin(\"LR\").Height(_commonPage.panel.Height);\n\t\tb.R.AddSeparator(false);\n\t\t\n\t\t//code\n\t\tb.Row(80).xAddInBorder(out _code, \"B\");\n\t\t\n\t\t//tree\n\t\tb.xAddSplitterH(span: -1);\n\t\tb.Row(-1).xAddInBorder(out _tree, \"T\");\n\t\t\n\t\tb.End();\n\t\t\n\t\t_InitTree();\n\t\t\n\t\tif (p != null) _ElmFromPoint(p.Value, ctor: true); //will call _SetElm in OnSourceInitialized\n\t\t\n\t\tb.WinProperties(\n\t\t\ttopmost: true,\n\t\t\tshowActivated: _elm != null ? false : null //eg if captured a popup menu item, activating this window closes the menu and we cannot get properties\n\t\t\t);\n\t\t\n\t\tWndSavedRect.Restore(this, LA.App.Settings.wndpos.elm, o => LA.App.Settings.wndpos.elm = o);\n\t}\n\t\n\tstatic Delm() {\n\t\tTUtil.OnAnyCheckTextBoxValueChanged<Delm>((d, o) => d._AnyCheckTextBoxValueChanged(o), comboToo: true);\n\t}\n\t\n\tprotected override void OnSourceInitialized(EventArgs e) {\n\t\tbase.OnSourceInitialized(e);\n\t\t\n\t\tif (_elm != null) _SetElm(false);\n\t\t_InitInfo();\n\t\t_InitCapturingWithHotkey();\n\t}\n\t\n\tprotected override void OnClosing(CancelEventArgs e) {\n\t\t_cCapture.IsChecked = false;\n\t\tbase.OnClosing(e);\n\t}\n\t\n\tprotected override void OnClosed(EventArgs e) {\n\t\tbase.OnClosed(e);\n\t\t\n\t\t//release COM objects. Else later can't unload the C++ dll from processes where used UIA etc.\n\t\t_tree.SetItems(null);\n\t\tforeach (var f in typeof(Delm).GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)) {\n\t\t\tif (!f.FieldType.IsValueType) f.SetValue(this, null);\n\t\t}\n\t\tGC.Collect();\n\t\tGC.WaitForPendingFinalizers();\n\t}\n\t\n\tvoid _SetElm(bool captured) {\n\t\twnd c = _elm.WndContainer, w = c.Window;\n\t\tif (w.Is0) return;\n\t\t\n\t\tstring wndName = w.NameTL_;\n\t\tbool sameWnd = captured && w == _wnd && wndName == _wndName;\n\t\t_wndName = wndName;\n\t\t\n\t\tbool useCon = _useCon && captured && sameWnd && c == _con;\n\t\t//search in control by default in these cases:\n\t\t//\t1. It is in other thread. Then would be slow because cannot use inproc. Except in known windows.\n\t\t//\t2. If Java. Can find in any case if control class name specified, but now cannot get tree of entire window.\n\t\tif (!useCon && c != w) {\n\t\t\tif (c.ThreadId != w.ThreadId) useCon = 0 == c.ClassNameIs(Api.string_IES, \"Windows.UI.Core.CoreWindow\");\n\t\t\telse useCon = w.ClassNameIs(\"SunAwt*\") && c.ClassNameIs(\"SunAwt*\");\n\t\t}\n\t\t\n\t\t_SetWndCon(w, c, useCon);\n\t\t\n\t\tif (!_FillPropertiesTreeAndCode(true, sameWnd)) return;\n\t\t\n\t\tif (_IsAutoTest) timer.after(1, _ => _Test(captured: true, testAction: _cAutoTestAction.IsChecked));\n\t}\n\t\n\tvoid _SetWndCon(wnd w, wnd con, bool useCon = false) {\n\t\t_wnd = w;\n\t\t_con = con == w ? default : con;\n\t\t_useCon = useCon && !_con.Is0;\n\t\t_wndNoActivate = w.IsNoActivateStyle_();\n\t\tusing var nevc = new _NoeventValueChanged(this);\n\t\t_cControl.IsChecked = _useCon; _cControl.IsEnabled = !_con.Is0;\n\t}\n\t\n\t//Called when: 1. _SetElm. 2. The Window button changed window. 3. The Control checkbox checked/unchecked.\n\tbool _FillPropertiesTreeAndCode(bool setElm = false, bool sameWnd = false) {\n\t\t//_nodeCaptured = null;\n\t\t\n\t\t//perf.first();\n\t\t_TreeItem ti = null;\n\t\tbool sameTree = sameWnd && _TrySelectInSameTree(out ti);\n\t\t//perf.next();\n\t\t\n\t\tif (!sameTree) _ClearTree();\n\t\telse if (ti != null && _PathSetPageWhenTreeItemSelected(ti)) return true;\n\t\t\n\t\tif (!_FillProperties(out var p)) return false;\n\t\tif (!sameTree) {\n\t\t\tMouse.SetCursor(Cursors.Wait);\n\t\t\t_FillTree(p);\n\t\t\tMouse.SetCursor(Cursors.Arrow);\n\t\t}\n\t\t_FormatCode();\n\t\t\n\t\tif (p.Role == \"CLIENT\" && _wnd.ClassNameIs(\"SunAwt*\") && !_elm.MiscFlags.Has(EMiscFlags.Java) /*&& !osVersion.is32BitOS*/) {\n\t\t\ttimer.every(50, t => {\n\t\t\t\tif (_testing) return;\n\t\t\t\tt.Stop();\n\t\t\t\tif (_info.AaElemsSuspended) { //eg showing test result\n\t\t\t\t\tstring s1 = c_infoJava, s2 = _info.aaaText; if (!s2.NE() && !s2.Ends('\\n')) s1 = \"\\r\\n\" + s1;\n\t\t\t\t\t_info.aaaAppendText(s1, false, false);\n\t\t\t\t} else _info.aaaText = c_infoJava;\n\t\t\t});\n\t\t}\n\t\t//_info.aaaText = c_infoJava;\n\t\t\n\t\t//_nodeCaptured = _tree.SelectedItem as _TreeItem; //print.it(_nodeCaptured?.e);\n\t\t//perf.nw();\n\t\treturn true;\n\t}\n\t\n\t//Called when: 1. Captured, or window/control changed (_FillPropertiesTreeAndCode). 2. A tree item clicked.\n\tbool _FillProperties(out EProperties p) {\n\t\tbool propOK = _elm.GetProperties(\"Rnuvdakh@srwU\", out var pr);\n\t\tvar (browser, propUrl) = propOK ? _IsVisibleWebPage(_elm, _con.Is0 ? _wnd : _con) : default;\n\t\tp = pr;\n\t\tif (propOK != _bTest.IsEnabled) _EnableDisableTopControls(propOK);\n\t\tif (!propOK) {\n\t\t\t_pageBorder.Child = null;\n\t\t\t_page = null;\n\t\t\t_info.InfoError(\"Failed to get UI element properties\", lastError.message);\n\t\t\treturn false;\n\t\t}\n\t\t_page ??= _commonPage ??= new(this);\n\t\t_pageBorder.Child = _page.panel;\n\t\t\n\t\tusing var nevc = new _NoeventValueChanged(this);\n\t\t\n\t\tbool isWeb = browser != 0;\n\t\t_isWebIE = browser == _BrowserEnum.IE;\n\t\t\n\t\tvar role = p.Role; if (isWeb) role = \"web:\" + role;\n\t\t_SetHideIfEmpty(_page.roleA, role, check: true, escape: false);\n\t\t//CONSIDER: path too. But maybe don't encourage, because then the code depends on window/page structure.\n\t\tbool noName = !_SetHideIfEmpty(_page.nameA, p.Name, check: true, escape: true, dontHide: true);\n\t\t\n\t\tbool haveCon = !isWeb && !_con.Is0;\n\t\tbool uncheckCon = haveCon && !_con.IsVisible; //eg in Photos window\n\t\tif (haveCon && !uncheckCon && p.UiaId.ToInt(out int i1) && i1 == _con.ControlId) { //don't use uiaid if == control id\n\t\t\tif (_con.GetWindowAndClientRectInScreen(out var rw1, out var rc1))\n\t\t\t\tif (rc1 == p.Rect || rw1 == p.Rect) p.UiaId = null;\n\t\t\t//never mind: possibly incorrect p.Rect of DPI-scaled window.\n\t\t}\n\t\tif (_SetHideIfEmpty(_page.uiaidA, p.UiaId, check: noName, escape: true)) noName = false;\n\t\t\n\t\tif (haveCon && !uncheckCon && !p.UiaCN.NE() && p.UiaCN.Eqi(_con.ClassName)) { //don't use uiacn if == control class\n\t\t\tif (_con.GetWindowAndClientRectInScreen(out var rw1, out var rc1))\n\t\t\t\tif (rc1 == p.Rect || rw1 == p.Rect) p.UiaCN = null;\n\t\t}\n\t\tif (_SetHideIfEmpty(_page.uiacnA, p.UiaCN, check: false, escape: true)) noName = false;\n\t\t\n\t\t//control\n\t\tbool isClassId = haveCon && !_useCon;\n\t\t_page.idA.Visible = isClassId;\n\t\t_page.classA.Visible = isClassId;\n\t\tif (isClassId) {\n\t\t\tstring sId = TUtil.GetUsefulControlId(_con, _wnd, out int id) ? id.ToString() : _con.NameWinforms;\n\t\t\tbool hasId = _SetHideIfEmpty(_page.idA, sId, check: true, escape: false);\n\t\t\t_Set(_page.classA, TUtil.StripWndClassName(_con.ClassName, true), check: !hasId);\n\t\t\tif (uncheckCon) _page.classA.c.IsChecked = _page.idA.c.IsChecked = false;\n\t\t}\n\t\t\n\t\t_SetHideIfEmpty(_page.valueA, p.Value, check: false, escape: true);\n\t\tif (_SetHideIfEmpty(_page.descriptionA, p.Description, check: noName, escape: true)) noName = false;\n\t\t_SetHideIfEmpty(_page.actionA, p.DefaultAction, check: false, escape: true);\n\t\tif (_SetHideIfEmpty(_page.keyA, p.KeyboardShortcut, check: noName, escape: true)) noName = false;\n\t\tif (_SetHideIfEmpty(_page.helpA, p.Help, check: noName, escape: true)) noName = false;\n\t\t_SetHideIfEmpty(_page.urlA, propUrl, check: true, escape: false);\n\t\t\n\t\tif (p.HtmlAttributes.Count > 0) {\n\t\t\tvar b = new wpfBuilder(_page.htmlAttr).Columns((0, ..100), -1).Options(modifyPadding: false, margin: new());\n\t\t\tforeach (var attr in p.HtmlAttributes) {\n\t\t\t\tstring na = attr.Key, va = attr.Value;\n\t\t\t\tbool check = noName && (na == \"id\" || na == \"name\") && va.Length > 0;\n\t\t\t\tvar k = b.xAddCheckText<KTextExpressionBox>(\"@\" + na, TUtil.EscapeWildex(va));\n\t\t\t\tif (check) { k.c.IsChecked = true; noName = false; }\n\t\t\t\tvar info = TUtil.CommonInfos.AppendWildexInfo(TUtil.CommonInfos.PrependName(na, \"HTML attribute.\"));\n\t\t\t\t_info.AaAddElem(k.c, info);\n\t\t\t\t_info.AaAddElem(k.t, info);\n\t\t\t}\n\t\t\tb.End();\n\t\t} else _page.htmlAttr.Child = null;\n\t\t\n\t\tint item = _elm.Item;\n\t\t_page.elemA.Visible = item != 0;\n\t\tif (item != 0) {\n\t\t\t_Set(_page.elemA, item.ToS());\n\t\t\tif (noName && role is \"BUTTON\" or \"CHECKBOX\") { noName = false; _page.elemA.c.IsChecked = true; } //buttons in some toolbars don't have Name\n\t\t}\n\t\t\n\t\t_Set(_page.stateA, p.State.ToString());\n\t\t_Set(_page.rectA, $\"{{W={p.Rect.Width} H={p.Rect.Height}}}\");\n\t\t\n\t\t_page.alsoA.c.IsChecked = false;\n\t\t_page.skipA.c.IsChecked = false;\n\t\t_page.navigA.c.IsChecked = false;\n\t\tif (isWeb && !_waitAutoCheckedOnce) _wait.c.IsChecked = _waitAutoCheckedOnce = true;\n\t\t_page.uiaA.IsChecked = _elm.MiscFlags.Has(EMiscFlags.UIA);\n\t\t\n\t\treturn true;\n\t\t\n\t\tvoid _Set(KCheckTextBox ct, string value, bool check = false) {\n\t\t\tct.t.Text = value;\n\t\t\tct.c.IsChecked = check;\n\t\t}\n\t\t\n\t\tbool _SetHideIfEmpty(KCheckTextBox ct, string value, bool check, bool escape, bool dontHide = false, bool hideIfNull = false) {\n\t\t\tbool empty = hideIfNull ? value == null : value.NE();\n\t\t\tct.Visible = !empty || dontHide;\n\t\t\tif (empty) check = false;\n\t\t\telse if (escape) value = TUtil.EscapeWildex(value);\n\t\t\tct.t.Text = value;\n\t\t\tct.c.IsChecked = check;\n\t\t\treturn !empty;\n\t\t}\n\t}\n\t\n\tprivate void _bWnd_Click(WBButtonClickArgs e) {\n\t\tvar wPrev = _WndSearchIn;\n\t\tbool captCheck = _cCapture.IsChecked;\n\t\tvar r = _code.AaShowWndTool(this, _wnd, _con, checkControl: _useCon);\n\t\tif (captCheck) _cCapture.IsChecked = true;\n\t\tif (!r.ok) return;\n\t\t_SetWndCon(r.w, r.con, r.useCon);\n\t\tif (_WndSearchIn != wPrev) _FillPropertiesTreeAndCode();\n\t}\n\t\n\t//when checked/unchecked any checkbox, and when text changed of any textbox\n\tvoid _AnyCheckTextBoxValueChanged(object source) {\n\t\tif (source == _cCapture) {\n\t\t\t_capt.Capturing = _cCapture.IsChecked;\n\t\t} else if (_noeventValueChanged < 1 && _page != null) {\n\t\t\tusing var nevc = new _NoeventValueChanged(this);\n\t\t\tif (source is KCheckBox c) {\n\t\t\t\tif (c == _cControl) {\n\t\t\t\t\t_useCon = c.IsChecked;\n\t\t\t\t\t_FillPropertiesTreeAndCode();\n\t\t\t\t\treturn;\n\t\t\t\t} else if (c == _page.inPath) {\n\t\t\t\t\t_PathAddRemove();\n\t\t\t\t} else if (c == _page.uiaA) {\n\t\t\t\t\t_ClearTree();\n\t\t\t\t\t_cCapture.IsChecked = true;\n\t\t\t\t\t_cUIA.IsChecked = c.IsChecked;\n\t\t\t\t\tTUtil.InfoTooltip(ref _ttRecapture, c, \"Please capture the UI element again.\");\n\t\t\t\t}\n\t\t\t} else if (source is TextBox t && t.Tag is KCheckTextBox k) {\n\t\t\t\tk.CheckIfTextNotEmpty();\n\t\t\t}\n\t\t\t\n\t\t\t_FormatCode();\n\t\t}\n\t}\n\t\n\tint _noeventValueChanged;\n\tref struct _NoeventValueChanged {\n\t\tDelm _d;\n\t\tpublic _NoeventValueChanged(Delm d) { _d = d; _d._noeventValueChanged++; }\n\t\tpublic void Dispose() { _d._noeventValueChanged--; }\n\t}\n\t\n\tKPopup _ttRecapture;\n\t\n\t(string code, string wndVar) _FormatCode(bool test = false) {\n\t\tif (_page == null) return default; //failed to get UI element props\n\t\t\n\t\tvar action = _CurrentAction;\n\t\tbool isFinder = !test && action.IsFinder;\n\t\tbool isFindAll = !test && action.IsFindAll;\n\t\tint iFindAll = isFindAll ? action.name.Ends(false, \"foreach\", \"for\", \"Select\", \"Select 2\", \"table\") : 0;\n\t\tbool orThrow = !(test | isFinder | isFindAll) && _cException.IsChecked;\n\t\tbool isAction = !test && action.code != null;\n\t\tbool isVar = !(test | isFinder | isFindAll /*|| (isAction && orThrow && _Opt.Has(_EOptions.Compact))*/);\n\t\t\n\t\tvar b = new StringBuilder();\n\t\tstring wndCode = null, wndVar = null;\n\t\tif (isFinder) {\n\t\t\tb.Append(\"var f = elm.path\");\n\t\t} else {\n\t\t\t(wndCode, wndVar) = _code.AaGetWndFindCode(test, _wnd, _useCon ? _con : default);\n\t\t\tb.AppendLine(wndCode);\n\t\t\tif (isVar) b.Append(\"var e = \");\n\t\t\telse if (isFindAll) b.Append(iFindAll switch { 1 => \"foreach (var e in \", 5 => \"var rows = \", _ => \"var a = \" });\n\t\t\tb.Append(wndVar).Append(\".Elm\");\n\t\t}\n\t\t\n\t\tbool isInPath = _page != _commonPage;\n\t\tint nPathPages = isInPath ? _path.Count : 1;\n\t\tfor (int iPathPage = 0; iPathPage < nPathPages; iPathPage++) {\n\t\t\tvar page = isInPath ? _path[iPathPage].page : _page;\n\t\t\tb.Append('[');\n\t\t\tpage.roleA.GetText(out var role, emptyToo: true);\n\t\t\tif (iPathPage > 0 && !role.NE()) role = role[(role.IndexOf(':') + 1)..];\n\t\t\tb.AppendStringArg(role);\n\t\t\t\n\t\t\tbool isName = page.nameA.GetText(out var name, emptyToo: true);\n\t\t\tif (isName) b.AppendStringArg(name);\n\t\t\t\n\t\t\tint nProp = 0, propStart = 0;\n\t\t\tvoid _AppendProp(KCheckTextBox k, bool emptyToo = false) {\n\t\t\t\tif (!k.GetText(out var va, emptyToo)) return;\n\t\t\t\tif (k == page.levelA && va == \"0 1000\") return;\n\t\t\t\tif (k == page.maxccA && va == \"10000\") return;\n\t\t\t\tif (nProp++ == 0) {\n\t\t\t\t\tb.Append(\", \");\n\t\t\t\t\tif (!isName) b.Append(\"prop: \");\n\t\t\t\t\tpropStart = b.Length;\n\t\t\t\t\tb.Append(\"[\");\n\t\t\t\t} else b.Append(\", \");\n\t\t\t\tint j = b.Length;\n\t\t\t\tb.Append('\"').Append(k.c.Content as string).Append('=');\n\t\t\t\tif (!va.NE()) {\n\t\t\t\t\tif (va.Starts(\"@@\")) {\n\t\t\t\t\t\tb.Insert(j, '$').Append('{').Append(va[2..]).Append('}');\n\t\t\t\t\t} else if (TUtil.IsVerbatim(va, out int prefixLen)) {\n\t\t\t\t\t\tb.Insert(j, va[..prefixLen++]).Append(va, prefixLen, va.Length - prefixLen);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tva = va.Escape();\n\t\t\t\t\t\tb.Append(va);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tb.Append('\"');\n\t\t\t}\n\t\t\t\n\t\t\t_AppendProp(page.uiaidA, true);\n\t\t\t_AppendProp(page.uiacnA, true);\n\t\t\tif (iPathPage == 0) {\n\t\t\t\t_AppendProp(page.idA, true);\n\t\t\t\t_AppendProp(page.classA);\n\t\t\t}\n\t\t\t_AppendProp(page.valueA, true);\n\t\t\t_AppendProp(page.descriptionA, true);\n\t\t\t_AppendProp(page.actionA, true);\n\t\t\t_AppendProp(page.keyA, true);\n\t\t\t_AppendProp(page.helpA, true);\n\t\t\t_AppendProp(page.urlA, true);\n\t\t\tif (page.htmlAttr.Child is Grid g) foreach (var c in g.Children.OfType<KCheckBox>()) _AppendProp(c.Tag as KCheckTextBox, true);\n\t\t\t_AppendProp(page.elemA);\n\t\t\t_AppendProp(page.stateA);\n\t\t\t_AppendProp(page.rectA);\n\t\t\t_AppendProp(page.notinA);\n\t\t\t_AppendProp(page.maxccA);\n\t\t\t_AppendProp(page.levelA);\n\t\t\tif (nProp > 0) b.Append(']');\n\t\t\t\n\t\t\tif (TUtil.FormatFlags(out var s1,\n\t\t\t\t(page.hiddenTooA, EFFlags.HiddenToo),\n\t\t\t\t(page.reverseA, EFFlags.Reverse),\n\t\t\t\t(iPathPage == 0 ? page.uiaA : null, EFFlags.UIA),\n\t\t\t\t(iPathPage == 0 ? page.notInprocA : null, EFFlags.NotInProc),\n\t\t\t\t(iPathPage == 0 ? page.clientAreaA : null, EFFlags.ClientArea),\n\t\t\t\t(page.menuTooA, EFFlags.MenuToo)\n\t\t\t\t)) b.AppendOtherArg(s1, (isName && nProp > 0) ? null : \"flags\");\n\t\t\t\n\t\t\tif (page.alsoA.GetText(out var also)) b.AppendOtherArg(also, \"also\");\n\t\t\tif (page.skipA.GetText(out var skip)) b.AppendOtherArg(skip, \"skip\");\n\t\t\tif (page.navigA.GetText(out var navig)) b.AppendStringArg(navig, \"navig\");\n\t\t\tb.Append(']');\n\t\t}\n\t\t\n\t\tif (isFinder) {\n\t\t\tb.Append(';');\n\t\t\tb.Append(_screenshot);\n\t\t} else if (isFindAll) {\n\t\t\tb.Append(\".FindAll()\").Append(iFindAll switch {\n\t\t\t\t1 => \"\"\"\n) {\n\tprint.it(e);\n}\n\"\"\",\n\t\t\t\t2 => \"\"\"\n;\nfor (int i = 0; i < a.Length; i++) {\n\tprint.it(a[i]);\n}\n\"\"\",\n\t\t\t\t3 => \"\"\"\n\n\t.Select(o => o.Name).ToArray();\nforeach (var v in a) print.it(v);\n//for (int i = 0; i < a.Length; i++) print.it(a[i]);\n\"\"\",\n\t\t\t\t4 => \"\"\"\n\n\t.Select(o => (name: o.Name, value: o.Value)).ToArray();\nforeach (var v in a) print.it(v.name, v.value);\n//for (int i = 0; i < a.Length; i++) print.it(a[i].name, a[i].value);\n\"\"\",\n\t\t\t\t_ => \"\"\"\n;\nfor (int ir = 0; ir < rows.Length; ir++) { //for each row\n\t//if (ir == 0) continue; //header?\n\tvar a = rows[ir].Elm[prop: \"level=0\"].FindAll(); //cells in this row. You may want to change level or/and use role etc.\n\tprint.it(rows[ir]); print.it(a.Select(o => $\"\\t{o.Role},  {o.Name,-30},  {o.Rect}\")); //debug\n\t//print.it(a[0].Name, a[1].Name); //example\n\t//print.it(a[1].Navigate(\"fi\").Name, a[2].Elm[\"LINK\"].Find(0).Name); //examples: get or find an element inside the cell\n}\n\"\"\",\n\t\t\t});\n\t\t} else {\n\t\t\tb.Append(test && action.IsFindAll ? \".FindAll(\" : \".Find(\");\n\t\t\tif (!test && _wait.GetText(out var waitTime, emptyToo: true)) b.AppendWaitTime(waitTime, orThrow); else if (orThrow) b.Append('0');\n#if true\n\t\t\tb.Append(\");\");\n\t\t\tif (!test) b.Append(_screenshot);\n\t\t\tif (isVar && !orThrow) b.Append(\"\\r\\nif(e == null) { print.it(\\\"not found\\\"); }\");\n\t\t\tif (isAction) {\n\t\t\t\tb.Append(\"\\r\\ne\");\n\t\t\t\tif (!orThrow) b.Append('?');\n\t\t\t\tb.Append('.').Append(_ActionGetCode(test: false)).Append(';');\n\t\t\t}\n#else\n//rejected: _EOptions.Compact - instead of 2 lines \"elm e=find\" and \"e.Action()\" format 1 line \"find.Action()\".\n//\tOften I forget to uncheck it and have to edit the code if need with variable. Also then .Action() often is far.\n\n\t\t\tb.Append(')');\n\t\t\tif (isAction && !isVar) _AppendAction(); else b.Append(';');\n\t\t\tif (!forTest) b.Append(_screenshot);\n\t\t\tif (isVar && !orThrow) b.Append(\"\\r\\nif(e == null) { print.it(\\\"not found\\\"); }\");\n\t\t\tif (isAction && isVar) { b.Append(\"\\r\\ne\"); _AppendAction(); }\n\n\t\t\tvoid _AppendAction() {\n\t\t\t\tif (!orThrow) b.Append('?');\n\t\t\t\tb.Append('.').Append(_ActionGetCode(test: false)).Append(';');\n\t\t\t}\n#endif\n\t\t}\n\t\t\n\t\tvar R = b.ToString();\n\t\t\n\t\tif (!test) _code.AaSetText(R, wndCode.Lenn());\n\t\t\n\t\treturn (R, wndVar);\n\t}\n\t\n\t#region capture\n\t\n\tTUtil.CapturingWithHotkey _capt;\n\t\n\tvoid _InitCapturingWithHotkey() {\n\t\t_capt = new(_cCapture,\n\t\t\t_GetCapturingRect,\n\t\t\tnew(LA.App.Settings.delm.hk_capture, _Capture),\n\t\t\tnew(LA.App.Settings.delm.hk_insert, () => _Insert(hotkey: true)),\n\t\t\tnew(LA.App.Settings.delm.hk_smaller, _CaptureSmallerToggle));\n\t\t_cCapture.IsChecked = true;\n\t}\n\t\n\t//Called repeatedly while capturing to display rectangles of elements from mouse.\n\tbool _GetCapturingRect(TUtil.CapturingWithHotkey.GetRectArgs k) {\n\t\t//don't show rects when a mouse button is pressed. With some apps then hangs. Eg Word > Insert > Symbol, click a symbol; notinproc too, but not UIA.\n\t\tif (mouse.isPressed()) return false;\n\t\t\n\t\tif (_smaller is { } sm && sm.Window == k.wTL) {\n\t\t\treturn sm.GetRect(k);\n\t\t}\n\t\t\n\t\tvar p = k.p;\n\t\t\n\t\tvar flags = _GetXYFlags();\n\t\t//using var pe1 = perf.local();\n\t\tvar (e_, w) = _ElmFromPointRaw(p, flags);\n\t\t//pe1.Next('e');\n\t\tif (e_ == null) return false;\n\t\t\n\t\tusing var e = e_;\n\t\tvar r = e.Rect;\n\t\t//pe1.Next('r');\n\t\t\n\t\tif (_CaptureSmallerChromeWorkaround(e, w, r)) {\n\t\t\treturn _smaller.GetRect(k);\n\t\t}\n\t\t\n\t\tk.resultRect = r;\n\t\tk.resultText = e.Role;\n\t\treturn true;\n\t}\n\t\n\tvoid _Capture() {\n\t\t_ttRecapture?.Close();\n\t\t_info.aaaText = _IsAutoTest ? \"\" : _dialogInfo; //clear error/info from previous test etc. If with auto-test options, make empty to reduce flickering.\n\t\t\n\t\tif (!_ElmFromPoint(mouse.xy)) return;\n\t\t\n\t\t_testedAction = _aActions[0];\n\t\t_SetElm(true);\n\t\tvar w = this.Hwnd();\n\t\tif (w.IsMinimized) {\n\t\t\tw.ShowNotMinMax();\n\t\t\tw.ActivateL();\n\t\t}\n\t}\n\t\n\tEXYFlags _GetXYFlags() {\n\t\tvar flags = EXYFlags.PreferLink;\n\t\tswitch (_cUIA.IsChecked) { case true: flags |= EXYFlags.UIA; break; case null: flags |= EXYFlags.OrUIA; break; }\n\t\tif (_page != null) {\n\t\t\tif (_page.notInprocA.IsChecked) flags |= EXYFlags.NotInProc;\n\t\t}\n\t\treturn flags;\n\t}\n\t\n\tstatic EXYFlags _AdjustXYFlagsForWindow(wnd wFP, wnd wTL, EXYFlags flags) {\n\t\tif (flags.HasAny(EXYFlags.OrUIA | EXYFlags.UIA)) {\n\t\t\tif (!flags.Has(EXYFlags.UIA)) {\n\t\t\t\t//UIA was faster by ~20% in all tested Store apps.\n\t\t\t\t//\tAlso, MSAA Invoke does not work with many controls, eg in Settings and XAML Gallery. The UIA wrapper tries various patterns (Toggle, Expand, Select) and usually it works.\n\t\t\t\t//\tAlso, when eg window minimized, the Store process is suspended, and MSAA hangs when trying to get properties etc; UIA fails immediately (good).\n\t\t\t\tif (0 != wTL.IsUwpApp || wTL.IsWindows8MetroStyle) return flags | EXYFlags.UIA;\n\t\t\t\tif (0 != wTL.ClassNameIs(\n\t\t\t\t\t\"WinUIDesktopWin32WindowClass\", //WinUI (UIA faster/better). Eg WinUI3 Gallery, Microsoft PowerToys.\n\t\t\t\t\t\"GlassWndClass*\" //JavaFX (no MSAA)\n\t\t\t\t\t)) return flags | EXYFlags.UIA;\n\t\t\t\t\n\t\t\t\tif (!wTL.Child(cn: \"Windows.UI.Input.InputSite.WindowClass\", flags: WCFlags.HiddenToo).Is0) return flags | EXYFlags.UIA; //eg Win11 taskbar, terminal, taskmanager. MSAA bug: intermediate hidden child window; from-point API does not work; for find/enum may need hiddentoo flag.\n\t\t\t\tif (wFP != wTL) { //maybe need UIA flag only for part of window, eg Win11 explorer's ribbon. With UIA much slower to find.\n\t\t\t\t\tif (wFP.ClassNameIs(\"Microsoft.UI.Content.DesktopChildSiteBridge\")) flags |= EXYFlags.UIA; //eg Win11 mspaint, explorer's ribbon, notepad. No MSAA.\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!flags.Has(EXYFlags.NotInProc)) {\n\t\t\t\tif (wTL.ClassNameIs(\"Notepad\") && osVersion.minWin11) return flags | EXYFlags.UIA | EXYFlags.NotInProc; //need uia, but then inproc very slow find/enum\n\t\t\t}\n\t\t}\n\t\treturn flags;\n\t}\n\t\n\tstatic (elm e, wnd w) _ElmFromPointRaw(POINT p, EXYFlags flags) {\n\t\twnd w = default;\n\t\telm e = elm.fromXY_(p, flags, (flags, wFP, wTL) => {\n\t\t\tw = wTL;\n\t\t\treturn _AdjustXYFlagsForWindow(wFP, wTL, flags);\n\t\t});\n\t\treturn (e, w);\n\t}\n\t\n\t//Called only when capturing, not to display rectangles of elements from mouse.\n\tbool _ElmFromPoint(POINT p, bool ctor = false) {\n\t\t_capturingMenu?.Close();\n\t\t\n\t\tvar flags = _GetXYFlags();\n\t\t\n\t\tvar a = _smaller is { } sm && sm.Window == wnd.fromXY(p, WXYFlags.NeedWindow)\n\t\t\t? sm.GetElm(p)\n\t\t\t: _GetElm(p, flags, ctor);\n\t\tif (a.NE_()) return false;\n\t\tvar e = a[0];\n\t\t\n\t\t_screenshot = TUtil.MakeScreenshot(p, _capt);\n\t\t\n\t\tif (a.Length > 1) {\n\t\t\tvar m = new popupMenu();\n\t\t\tfor (int i = 0; i < a.Length;) {\n\t\t\t\tvar v = a[i++];\n\t\t\t\tvar s = v.role.NullIfEmpty_() ?? i.ToS();\n\t\t\t\ts = $\"&{i}. {s}\";\n\t\t\t\tvar mi = m.Add(i, s);\n\t\t\t\tmi.Tooltip = v.tt;\n\t\t\t\tmi.Tag = v.rect;\n\t\t\t}\n\t\t\t_capturingMenu = m;\n\t\t\tint k = _ShowMenu(m, e.rect, osd: true) - 1;\n\t\t\t_capturingMenu = null;\n\t\t\tif (k < 0) return false;\n\t\t\t\n\t\t\t//some objects become invalid while showing the menu (or in any case when mouse moved out). Eg STATICTEXT in icons in VSCode left toolbar.\n\t\t\twhile (!a[k].e.GetRect(out _, raw: true)) if (++k == a.Length) return false;\n\t\t\t\n\t\t\te = a[k];\n\t\t}\n\t\t\n\t\t//set x y field always, not only when a mouse action selected, because may select a mouse action afterwards\n\t\t_noeventValueChanged++;\n\t\t_xy.t.Text = $\"{p.x - e.rect.left}, {p.y - e.rect.top}\";\n\t\t_noeventValueChanged--;\n\t\t\n\t\t_elm = e.e;\n\t\treturn true;\n\t\t\n\t\t_CapturedElm[] _GetElm(POINT p, EXYFlags flags, bool ctor) {\n\t\t\tif (ctor) {\n\t\t\t\t//workaround for: Chrome may give wrong element at first. Eg youtube right list -> \"x months ago\".\n\t\t\t\t//\tPossibly this can be useful with some other apps too.\n\t\t\t\t//\tIf not ctor, capturing works well because _ElmFromPointRaw is called to display element rectangles.\n\t\t\t\t//\tEven with this delay, may be no HTML attributes at first. Never mind.\n\t\t\t\t_ElmFromPointRaw(p, flags);\n\t\t\t\t100.ms();\n\t\t\t}\n\t\t\t\n\t\t\tvar (e, w) = _ElmFromPointRaw(p, flags);\n\t\t\tif (!uacInfo.isAdmin && w.UacAccessDenied) print.warning(\"The target process is admin; this process isn't. Can't use its UI elements.\", -1); //not _info.InfoError, it's unreliable here, even with timer\n\t\t\tif (e == null) return null;\n\t\t\t\n\t\t\t//the same workaround as in _GetCapturingRect\n\t\t\tif (ctor && _CaptureSmallerChromeWorkaround(e, w)) {\n\t\t\t\treturn _smaller.GetElm(p);\n\t\t\t}\n\t\t\t\n\t\t\t//If e probably does not support Invoke or Focus, show menu with e and the first ancestor that supports it.\n\t\t\t//\tIn some cases use the ancestor without a menu (if LINK or BUTTON etc).\n\t\t\t//\tThis code is similar to the C++ code in _FromPoint_GetLink, but covers more cases.\n\t\t\t//\tCannot show menu in this thread. Pass e2 and strings to the UI thread, let it show menu if e2 not null.\n\t\t\telm e2 = null; string tt1 = null, tt2 = null;\n\t\t\tif (!e.MiscFlags.HasAny(EMiscFlags.UIA | EMiscFlags.Java)) { //TODO3: UIA too\n\t\t\t\tif (!_IsInteractive(e, true, out bool stop1) && !stop1) {\n\t\t\t\t\tbool found = false;\n\t\t\t\t\ttt1 = \"This element probably does not support Invoke or Focus\";\n\t\t\t\t\ttt2 = \"This parent element probably supports Invoke\";\n\t\t\t\t\telm ep = e.Parent, ep0 = ep;\n\t\t\t\t\tfor (int i = 20; i-- > 0 && ep != null; ep = ep.Parent) {\n\t\t\t\t\t\tif ((found = _IsInteractive(ep, false, out bool stop2)) || stop2) break;\n\t\t\t\t\t\t//print.it(ep.ChildCount);\n\t\t\t\t\t\ti -= ep.ChildCount; //sometimes the subtree is <div><div><div>..., 1-2 children at every level\n\t\t\t\t\t}\n\t\t\t\t\tif (!found && ep != null && e.MiscFlags.Has(EMiscFlags.InProc)) { //detect when e is not a child of its parent\n\t\t\t\t\t\tvar ee = ep0.Elm[e.Role, prop: $\"level=0\\0rect={e.RectRawDpi_}\", flags: EFFlags.MenuToo].Find();\n\t\t\t\t\t\t//print.it(ee);\n\t\t\t\t\t\tif (found = ee == null) {\n\t\t\t\t\t\t\tDebug_.Print(\"e is not a child of its parent: \" + e);\n\t\t\t\t\t\t\tep = ep0;\n\t\t\t\t\t\t\ttt1 = \"Possibly disconnected from the tree\";\n\t\t\t\t\t\t\ttt2 = \"Parent element\";\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (found && ep.Name.NE() && !e.Name.NE()) found = false;\n\t\t\t\t\tif (found && ep.WndContainer.Is0) found = false; //see bug comment in C++\n\t\t\t\t\tif (found) {\n\t\t\t\t\t\tbool use = _RoleIsLinkOrButton(ep.RoleInt)\n\t\t\t\t\t\t\t//|| role ERole.MENUITEM //will need EFFlags.MenuToo. But then cannot capture child elements in some menu items, and cannot select in tree because it closes the menu.\n\t\t\t\t\t\t\t;\n\t\t\t\t\t\tif (use) e = ep; else e2 = ep;\n\t\t\t\t\t}\n\t\t\t\t} else if (!stop1) {\n\t\t\t\t\t//is in COMBOBOX?\n\t\t\t\t\tif (e.RoleInt is ERole.TEXT or ERole.BUTTON && e.Parent is elm ep) {\n\t\t\t\t\t\tvar r1 = ep.RoleInt;\n\t\t\t\t\t\tif (r1 == ERole.COMBOBOX || (r1 == ERole.WINDOW && ((ep = ep.Parent)?.RoleInt ?? 0) == ERole.COMBOBOX)) e2 = ep;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tstatic bool _IsInteractive(elm e, bool orig, out bool stop) {\n\t\t\t\t\tstop = false;\n\t\t\t\t\tif (e.Item != 0) return true;\n\t\t\t\t\tvar role = e.RoleInt;\n\t\t\t\t\tif (_RoleIsLinkOrButton(role)) return true;\n\t\t\t\t\tif (stop = role is ERole.WINDOW or ERole.DOCUMENT or ERole.PROPERTYPAGE or ERole.PAGETABLIST\n\t\t\t\t\t\tor ERole.TABLE or ERole.LIST or ERole.TREE\n\t\t\t\t\t\t//or ERole.CLIENT or ERole.PANE //no, sometimes used for \"no role\" elements\n\t\t\t\t\t\tor ERole.DIALOG //action=\"OK\"\n\t\t\t\t\t\t) return false;\n\t\t\t\t\tif (stop = role == ERole.PAGETAB) if (e.ChildCount > 2) return false; /*PAGETAB can be used either as button (eg in web browsers) or as button + page with all controls (eg WPF tab control)*/\n\t\t\t\t\t//if static text or image, even if has action, it may just throw \"not implemented\". Noticed in Firefox somewhere.\n\t\t\t\t\tif (role is ERole.STATICTEXT or ERole.IMAGE\n\t\t\t\t\t\tor ERole.DIAGRAM //IMAGE in Firefox\n\t\t\t\t\t\tor ERole.GROUPING //often has action, but rarely useful\n\t\t\t\t\t\t) return false;\n\t\t\t\t\tvar state = e.State;\n\t\t\t\t\tif (state.Has(EState.FOCUSABLE)) return true; //eg editable TEXT. It could be eg in a WPF EXPANDER with action. Not in 'if (orig)' because eg TEXT may have children.\n\t\t\t\t\tif (orig) {\n\t\t\t\t\t\tif (role == ERole.TEXT) return state.Has(EState.DISABLED); //probably TEXT used instead of STATICTEXT, eg in Firefox\n\t\t\t\t\t} else if (state.Has(EState.INVISIBLE)) return false;\n\t\t\t\t\treturn e.DefaultAction is not (null or \"\" or \"click ancestor\" /*Chrome*/ or \"Collapse\" /*WPF expander*/);\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tif (e2 == null) return [new(e, null, null, e.Rect)];\n\t\t\treturn [new(e, e.Role, tt1, e.Rect), new(e2, e2.Role, tt2, e2.Rect)];\n\t\t}\n\t}\n\tbool _waitAutoCheckedOnce; //if user unchecks, don't check next time\n\tpopupMenu _capturingMenu;\n\t\n\trecord class _CapturedElm(elm e, string role, string tt, RECT rect);\n\t\n\tclass _CaptureSmaller {\n\t\tDelm _d;\n\t\twnd _w;\n\t\t(elm e, RECT rect)[] _a;\n\t\tlong _timeUpdated, _updatePeriod;\n\t\t\n\t\tpublic _CaptureSmaller(Delm d, wnd w) {\n\t\t\t_d = d;\n\t\t\t_w = w;\n\t\t}\n\t\t\n\t\tpublic wnd Window => _w;\n\t\t\n\t\tpublic bool Update() {\n\t\t\tif (_d._capturingMenu != null || _d._actionMenu != null) return false;\n\t\t\t\n\t\t\tEFFlags flags = 0;\n\t\t\tif (_d._page is { } page) {\n\t\t\t\tif (page.notInprocA.IsChecked) flags |= EFFlags.NotInProc;\n\t\t\t\tif (page.hiddenTooA.IsChecked) flags |= EFFlags.HiddenToo;\n\t\t\t}\n\t\t\tEXYFlags xyFlags = _AdjustXYFlagsForWindow(wnd.fromMouse(WXYFlags.Raw), _w, _d._GetXYFlags());\n\t\t\tif (xyFlags.Has(EXYFlags.UIA)) flags |= EFFlags.UIA;\n\t\t\tif (xyFlags.Has(EXYFlags.NotInProc)) flags |= EFFlags.NotInProc;\n\t\t\t\n\t\t\t_timeUpdated = long.MaxValue;\n\t\t\ttry {\n\t\t\t\tvar aOld = _a;\n\t\t\t\tlong t1 = Environment.TickCount64;\n\t\t\t\t_a = elmFinder.GetAllWithRect_(_w, flags).ToArray();\n\t\t\t\tlong t2 = Environment.TickCount64;\n\t\t\t\tlong t3 = t2 - t1;\n\t\t\t\t\n\t\t\t\tif (t3 < 3000) (_timeUpdated, _updatePeriod) = (t2, 2000 + t3 * 2);\n\t\t\t\t\n\t\t\t\t//Release old COM objects.\n\t\t\t\t//\tOld test results, now can't repro:\n\t\t\t\t//\t\tElse may become slower, and the target process memory grows, until next GC.\n\t\t\t\t//\t\tEg Chrome the \"get all\" time normally is ~32 ms, but without this grows until 80-120 ms.\n\t\t\t\tif (aOld != null) Task.Delay(1000).ContinueWith(t => { GC.Collect(); });\n\t\t\t\t\n\t\t\t\t//rejected: auto switch to UIA when need. Eg if CLIENT empty.\n\t\t\t\t//\tDifficult to reliably detect whether it's better.\n\t\t\t\t//\tAnyway does not improve all cases. Eg when need UIA only for a single child window, eg HtmlHelp tree.\n\t\t\t}\n\t\t\tcatch (Exception e1) { Debug_.Print(e1); return false; }\n\t\t\t\n\t\t\t//Task.Run(() => { using (osdText.showTransparentText(\"updated\", 1, PopupXY.Mouse)) 500.ms(); });\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\tpublic bool GetRect(TUtil.CapturingWithHotkey.GetRectArgs k) {\n\t\t\tlong timeNow = Environment.TickCount64;\n\t\t\tbool update = timeNow - _timeUpdated >= _updatePeriod;\n\t\t\tif (!update) {\n\t\t\t\tlong liTime = Api.GetLastInputTime();\n\t\t\t\tupdate = liTime > _timeUpdated && timeNow - liTime > 600;\n\t\t\t}\n\t\t\tif (update) Update();\n\t\t\t\n\t\t\tvar a = GetElm(k.p);\n\t\t\tif (a.Length == 0) return false;\n\t\t\tk.resultRect = a[0].rect;\n\t\t\tk.resultText = a[0].role + \" *\";\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\tpublic _CapturedElm[] GetElm(POINT p) {\n\t\t\t//get objects whose rect contains p. Get only tree leaves.\n\t\t\tList<_CapturedElm> r = [];\n\t\t\tint skipLevel = 0;\n\t\t\tfor (int i = _a.Length; --i >= 0;) {\n\t\t\t\tint level = _a[i].e.Level;\n\t\t\t\tif (level < skipLevel) { skipLevel = level; continue; }\n\t\t\t\tif (_a[i].rect.Contains(p)) {\n\t\t\t\t\tr.Add(new(_a[i].e, _a[i].e.Role, null, _a[i].rect));\n\t\t\t\t\tskipLevel = level;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (r.Count == 0) return [];\n\t\t\tif (r.Count == 1) return [r[0]];\n\t\t\tvar a = r.OrderBy(o => o.rect.NoArea ? long.MaxValue : o.rect.Area_).ToArray();\n\t\t\treturn a;\n\t\t}\n\t}\n\t_CaptureSmaller _smaller;\n\twnd _noAutoSmaller;\n\t\n\tvoid _CaptureSmallerToggle() {\n\t\tvar w = wnd.fromMouse(WXYFlags.NeedWindow);\n\t\t//var w = wnd.fromMouse(); //no, it can be a ghost child window. Eg \"Intermediate D3D Window\" in Chrome; GetAll hangs if UIA.\n\t\t\n\t\tif (w.IsOfThisThread) {\n\t\t\tdialog.showInfo(null, \"Use this hotkey when the mouse is in the window where you want to capture an element.\", flags: DFlags.CenterMouse, owner: this);\n\t\t\treturn;\n\t\t}\n\t\t\n\t\t_CaptureSmallerToggle(w);\n\t}\n\t\n\tvoid _CaptureSmallerToggle(wnd w) {\n\t\tbool smaller = _smaller?.Window == w;\n\t\tsmaller = !smaller && !w.Is0;\n\t\t_smaller = null;\n\t\t\n\t\tif (smaller) {\n\t\t\tvar sm = new _CaptureSmaller(this, w);\n\t\t\tif (sm.Update()) _smaller = sm; else smaller = false;\n\t\t} else {\n\t\t\t_noAutoSmaller = w;\n\t\t}\n\t\t\n\t\tosdText.showText(smaller ? \"Using alternative elm capturing method in this window\" : \"Using default elm capturing method\", smaller ? 3 : 2, PopupXY.Mouse);\n\t}\n\t\n\t//workaround for Chromium bug (now fixed in browsers): can't capture links etc in acc mode.\n\t//\tOn mouse-move over a link, Chrome shows the URL at the bottom. While the URL is visible, acc elm-from-point API doesn't work.\n\t//\tSimilar in some other cases, eg when shows a tab thumbnail. Then uia API don't work too (never mind).\n\t//\tIn next Chrome and Edge version they restored the old element-from-point behavior: acc OK, UIA dead in Chrome.\n\t//\tStill need this for VSCode (2025-09-27); for any element, not just links.\n\tbool _CaptureSmallerChromeWorkaround(elm e, wnd w, RECT? rIfHave = null) {\n\t\tif (w == _noAutoSmaller) return false;\n\t\tif (e == null || (e.MiscFlags & (EMiscFlags.InProc | EMiscFlags.UIA | EMiscFlags.Java)) != EMiscFlags.InProc) return false;\n\t\tif (e.RoleInt_ is ERole.PANE && w.ClassNameIs(\"Chrome_WidgetWin_1\")) {\n\t\t\tvar r = rIfHave ?? e.Rect;\n\t\t\tif (r == w.ClientRectInScreen) {\n\t\t\t\t_CaptureSmallerToggle(w);\n\t\t\t\treturn _smaller != null;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t\t\n\t\t//CONSIDER: auto-switch to smaller mode in any window. Or just inform about it. To detect:\n\t\t//\tIf acc rect == window rect or control rect (client):\n\t\t//\tEnum direct acc children to find one at that point. If found, switch.\n\t\t//\tCan detect it in C++ acc-from-point code and return a flag \"has a child at that point\". Then in the role tooltip display \"ROLE (Shift+F3 to show smaller elements)\".\n\t\t\n\t\t//[old] rejected: if big etc, inform about 'Smaller'\n\t\t//if (r.Width * r.Height > 5000) {\n\t\t//\tvar ri = e.RoleInt;\n\t\t//\tif (!(_RoleIsLinkOrButton(ri) || ri is ERole.Custom or ERole.TEXT or ERole.STATICTEXT or ERole.IMAGE or ERole.DIAGRAM)) {\n\t\t//\t\tint wid = (int)Dpi.Unscale(r.Width, r), hei = Math2.MulDiv(r.Height, wid, r.Width);\n\t\t//\t\t//print.it(wid, hei, wid * hei);\n\t\t//\t\tbool big = wid * hei > 10000;\n\t\t//\t\tif (!big && !m.flags.Has(EXYFlags.UIA)) {\n\t\t//\t\t\tvar ee = _ElmFromPointRaw(m.p, m.flags | EXYFlags.UIA);\n\t\t//\t\t\tif (ee != null && ee.RoleInt != ri) {\n\t\t//\t\t\t\tvar rr = ee.Rect;\n\t\t//\t\t\t\tif (rr.Width * rr.Height < r.Width * r.Height / 2) big = true;\n\t\t//\t\t\t}\n\t\t//\t\t}\n\t\t//\t\tif (big) s += \"\\nTry the hotkey\";\n\t\t//\t}\n\t\t//}\n\t}\n\t\n\t#endregion\n\t\n\t#region Insert, Test\n\t\n\t///// <summary>\n\t///// When OK clicked, contains C# code. Else null.\n\t///// </summary>\n\t//public string aaResultCode { get; private set; }\n\t\n\tvoid _Insert(bool hotkey) {\n\t\tif (_close && !hotkey) {\n\t\t\tbase.Close();\n\t\t} else if (_code.aaaText.NullIfEmpty_() is string s) {\n\t\t\tif (_Opt.Has(_EOptions.Activate) && !_wndNoActivate && !_CurrentAction.IsFinder) {\n\t\t\t\ts = s.RxReplace(@\"^.+?\\bwnd\\.find\\(.+[^(]\\)\\K;\\r\", \".Activate();\\r\", 1);\n\t\t\t}\n\t\t\t\n\t\t\t(string oldName, string newName)[] rename_e = null;\n\t\t\tif (_Opt.Has(_EOptions.VarRole) && !(_CurrentAction.IsFinder || _CurrentAction.IsFindAll)) {\n\t\t\t\tif (s.RxMatch(@\"(?m)^var e = w\\.Elm\\[\"\"(?:[a-z]+:)?([a-zA-Z][\\w ]+\\w)\"\"\", out var m)) rename_e = [(\"e\", m[1].Value.Lower().Replace(' ', '_'))];\n\t\t\t}\n\t\t\t\n\t\t\tLA.InsertCode.Statements(new(s, makeVarName1: true, renameVars: rename_e));\n\t\t\tif (!hotkey) {\n\t\t\t\t_close = true;\n\t\t\t\t_bInsert.Content = \"Close\";\n\t\t\t\t_bInsert.MouseLeave += (_, _) => {\n\t\t\t\t\t_close = !true;\n\t\t\t\t\t_bInsert.Content = \"Insert\";\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t}\n\tbool _close;\n\t\n\tvoid _Test(bool captured = false, bool testAction = false, bool actWin = false) {\n\t\tif (_page == null) return;\n\t\t\n\t\tvar (code, wndVar) = _FormatCode(true); if (code.NE()) return;\n\t\tvar elmSelected = _page == _commonPage ? _elm : _path[^1].ti.e;\n\t\t\n\t\tif (testAction) testAction = !_CurrentAction.NoTest;\n\t\tbool autoInsert = captured && _cAutoInsert.IsChecked;\n\t\t\n\t\t_testing = true;\n\t\tvar restoreOwner = new int[1];\n\t\ttry {\n\t\t\telmFinder.t_navigResult = (true, null, null);\n\t\t\tvar rr = TUtil.RunTestFindObject(this.Hwnd(), code, wndVar, _WndSearchIn, o => (o as elm).Rect, actWin, restoreOwner, this.Dispatcher);\n\t\t\telm elmFound = rr.obj as elm, elmFoundBN = null;\n\t\t\t//need elm found before navig\n\t\t\tif (elmFound != null) elmFoundBN = elmFinder.t_navigResult.after == elmFound ? elmFinder.t_navigResult.before : elmFound;\n\t\t\telmFinder.t_navigResult = default;\n\t\t\tbool bad = false;\n\t\t\tif (elmFound != null) {\n\t\t\t\tRECT r1 = elmFoundBN.Rect, r2 = elmSelected.Rect;\n\t\t\t\t//print.it(r1, r2); //in DPI-scaled windows can be slightly different if different inproc of elmSelected and elmFoundBN. Would be completely different if using raw rect.\n\t\t\t\tint diff = elmFoundBN.MiscFlags.Has(EMiscFlags.InProc) == elmSelected.MiscFlags.Has(EMiscFlags.InProc) ? 0 : 2;\n\t\t\t\tbad = (!RECT.EqualFuzzy_(r1, r2, diff) || elmFoundBN.Role != elmSelected.Role);\n\t\t\t}\n\t\t\t\n\t\t\tif (elmFound == null) {\n\t\t\t\tstring osd;\n\t\t\t\tif (rr.info == null) _info.InfoError(osd = \"Timeout\", \"Not found in 10 s.\");\n\t\t\t\telse if (rr.speed < 0) { //error\n\t\t\t\t\t_info.InfoErrorOrInfo(rr.info);\n\t\t\t\t\tosd = rr.info.header;\n\t\t\t\t} else { //not found\n\t\t\t\t\tosd = \"Not found\";\n\t\t\t\t\tstring s2 = \"Try: check <b>Find hidden too<>; check/uncheck/edit other controls.\";\n\t\t\t\t\tint n = _page.maxccA.GetText(out _) ? 0 : (_elm.Parent?.ChildCount ?? 0);\n\t\t\t\t\tif (n > 10000) { //never mind: may be an indirect ancestor. Rare.\n\t\t\t\t\t\ts2 = $\"The parent element has {n} children. Need to specify maxcc.\";\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (_PathIsIntermediate()) s2 += \"\\r\\nTry <b>skip<> -1 to search for next path element in all matching intermediate elements.\";\n\t\t\t\t\t\tif (_page.navigA.GetText(out _)) s2 += \"\\r\\nTry <b>skip<> -1 to retry failed <b>navig<>ation with all matching intermediate elements.\";\n\t\t\t\t\t\tif (!_wnd.IsActive) {\n\t\t\t\t\t\t\tif (_page.actionA.GetText(out _)) s2 += \"\\r\\nNote: <b>action<> often is unavailable in inactive window.\";\n\t\t\t\t\t\t\tif (_page.keyA.GetText(out _)) s2 += \"\\r\\nNote: <b>key<> sometimes is unavailable in inactive window.\";\n\t\t\t\t\t\t\tif (_page.stateA.GetText(out _)) s2 += \"\\r\\nNote: <b>state<> often is different in inactive window.\";\n\t\t\t\t\t\t\ts2 += \"\\r\\n<+actTest>Activate window and test find<>\";\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t_info.InfoError(\"Not found\", s2, rr.info.headerSmall);\n\t\t\t\t}\n\t\t\t\t_Osd(osd, true);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (bad && !_CurrentAction.IsFindAll) {\n\t\t\t\tvar s2 = \"Try: <b>Add to path<> or/and <b>skip<>; check/uncheck/edit other controls.\\r\\nIf this element cannot be uniquely identified (no name etc), try another element and use <b>navig<>.\";\n\t\t\t\tif (_PathIsIntermediate()) s2 += \"\\r\\nTry <b>skip<> -1 to search for next path element in all matching intermediate elements.\";\n\t\t\t\tstring s1 = \"Found wrong element\";\n\t\t\t\t_info.InfoError(s1, s2, rr.info.headerSmall);\n\t\t\t\t_Osd(s1, true);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t_info.InfoErrorOrInfo(rr.info);\n\t\t\t\n\t\t\t//if (quickOK && r.speed < 1_000_000) {\n\t\t\t//\t//timer.after(1000, _ => _bOK.RaiseEvent(new RoutedEventArgs(ButtonBase.ClickEvent)));\n\t\t\t//\treturn;\n\t\t\t//}\n\t\t\t\n\t\t\tif (testAction) {\n\t\t\t\tstring aCode = _ActionGetCode(test: true);\n\t\t\t\tif (aCode != null) {\n\t\t\t\t\tApi.AllowSetForegroundWindow();\n\t\t\t\t\tvar re = TUtil.RunTestAction(elmFound, aCode);\n\t\t\t\t\tif (re != null) {\n\t\t\t\t\t\t_info.InfoErrorOrInfo(re);\n\t\t\t\t\t\t_Osd(re.header, true);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\trestoreOwner[0] = 1000;\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tif (autoInsert) {\n\t\t\t\t_Insert(hotkey: true);\n\t\t\t\t_Osd(\"Inserted\", false);\n\t\t\t}\n\t\t}\n\t\tfinally {\n\t\t\tif (restoreOwner[0] == 0) restoreOwner[0] = 1;\n\t\t\t_testing = false;\n\t\t}\n\t\t\n\t\tvoid _Osd(string s, bool error) {\n\t\t\tif (!captured) return;\n\t\t\tosdText.showTransparentText(s, 2, PopupXY.Mouse, error ? 0xFF6040 : 0x00C000, showMode: OsdMode.ThisThread);\n\t\t}\n\t}\n\tbool _testing;\n\t\n\tbool _IsAutoTest => _Opt.HasAny(_EOptions.AutoTest) || _cAutoTestAction.IsChecked || _cAutoInsert.IsChecked;\n\t\n\tvoid _bTest_ContextMenuOpening(object sender, ContextMenuEventArgs e) {\n\t\tvar m = new popupMenu();\n\t\t//bool isAction = _page != null && _ActionCanTest();\n\t\t//m[\"Test action\", disable: !isAction] = _ => _Test(testAction: true);\n\t\t//m[\"Activate window and test action\", disable: !isAction] = _ => _Test(testAction: true, actWin: true);\n\t\t//m[\"Activate window and test find\", disable: _page == null] = _ => _Test(actWin: true);\n\t\tm[\"Activate window and test\", disable: _page == null] = _ => _Test(actWin: true);\n\t\tm.Show(owner: this);\n\t}\n\t\n\t#endregion\n\t\n\t#region tree\n\t\n\t_TreeItem _treeRoot;\n\t//_TreeItem _nodeCaptured;\n\tbool _isWebIE; //_FillProperties sets it; then _FillTree uses it.\n\t\n\tvoid _InitTree() {\n\t\t_tree.SingleClickActivate = true;\n\t\t_tree.ItemActivated += e => _ItemActivated(e.Item as _TreeItem);\n\t\t\n\t\tvoid _ItemActivated(_TreeItem ti) {\n\t\t\t_elm = ti.e;\n\t\t\t//_screenshot = null;\n\t\t\t_SetWndCon(_wnd, _elm.WndContainer, _useCon);\n\t\t\tif (!_PathSetPageWhenTreeItemSelected(ti)) {\n\t\t\t\tif (!_FillProperties(out _)) return;\n\t\t\t}\n\t\t\t_FormatCode();\n\t\t\tTUtil.ShowOsdRect(_elm.Rect);\n\t\t}\n\t\t\n\t\t_tree.ItemClick += e => {\n\t\t\tif (e.Button == MouseButton.Right) {\n\t\t\t\tvar ti = e.Item as _TreeItem;\n\t\t\t\tvar m = new popupMenu();\n\t\t\t\tm[\"Navigate to this from the selected\", disable: ti == _tree.SelectedItem] = _ => _NavigateTo(ti);\n\t\t\t\tif (!_path.NE_() && !_path.Any(o => o.ti == ti)) {\n\t\t\t\t\tm[\"Navigate to this from the last in path\"] = _ => {\n\t\t\t\t\t\tvar tiPath = _path[^1].ti;\n\t\t\t\t\t\t_tree.SelectSingle(tiPath);\n\t\t\t\t\t\t_ItemActivated(tiPath);\n\t\t\t\t\t\t_NavigateTo(ti);\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\tm.Show(owner: this);\n\t\t\t}\n\t\t};\n\t}\n\t\n\tvoid _ClearTree() {\n\t\t_tree.SetItems(null);\n\t\t_treeRoot = null;\n\t\t_PathClear();\n\t}\n\t\n\t(_TreeItem xRoot, _TreeItem xSelect) _CreateTreeModel(wnd w, EProperties p, bool skipWINDOW) {\n\t\tEFFlags flags = Enum_.EFFlags_Mark | EFFlags.HiddenToo | EFFlags.MenuToo;\n\t\tif (_page.uiaA.IsChecked) flags |= EFFlags.UIA;\n\t\tif (!_elm.MiscFlags.Has(EMiscFlags.InProc)) flags |= EFFlags.NotInProc; //if captured notinproc in DPI-scaled, would fail to select in tree if tree elems retrieved inproc, because would compare with non-scaled rects\n\t\t\n\t\tvar us = (uint)p.State;\n\t\tvar prop = $\"rect={p.Rect}\\0state=0x{us:X},!0x{~us:X}\";\n\t\tif (skipWINDOW) prop += $\"\\0notin=WINDOW\";\n\t\tvar role = p.Role.NullIfEmpty_();\n\t\t\n\t\t_TreeItem xRoot = new(this), xSelect = null;\n\t\tvar stack = new Stack<_TreeItem>(); stack.Push(xRoot);\n\t\tint level = 0;\n\t\t\n\t\ttry {\n\t\t\tw.Elm[role, \"**tc \" + p.Name, prop, flags, also: o => {\n\t\t\t\t_TreeItem x = new(this);\n\t\t\t\tint lev = o.Level;\n\t\t\t\tif (lev != level) {\n\t\t\t\t\tif (lev > level) {\n\t\t\t\t\t\tDebug.Assert(lev - level == 1);\n\t\t\t\t\t\tstack.Push(stack.Peek().LastChild);\n\t\t\t\t\t} else {\n\t\t\t\t\t\twhile (level-- > lev) stack.Pop();\n\t\t\t\t\t}\n\t\t\t\t\tlevel = lev;\n\t\t\t\t}\n\t\t\t\tx.e = o;\n\t\t\t\tif (o.MiscFlags.Has(Enum_.EMiscFlags_Marked)) {\n\t\t\t\t\tif (xSelect == null) xSelect = x;\n\t\t\t\t}\n\t\t\t\tstack.Peek().AddChild(x);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t].Exists();\n\t\t\t\n\t\t\treturn (xRoot, xSelect);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\t_info.InfoError(\"Failed to get UI element tree.\", ex.Message);\n\t\t\treturn default;\n\t\t}\n\t}\n\t\n\tvoid _FillTree(EProperties p) {\n\t\tDebug.Assert(_treeRoot == null); Debug.Assert(_path == null); //_ClearTree must be called before\n\t\t\n\t\tvar w = _WndSearchIn;\n\t\tif (_isWebIE && !_useCon && !_con.Is0) w = _con; //if IE, don't display whole tree. Could be very slow, because cannot use in-proc for web pages (and there may be many tabs with large pages), because its control is in other thread.\n\t\tvar (xRoot, xSelect) = _CreateTreeModel(w, p, false);\n\t\tif (xRoot == null) return;\n\t\t\n\t\tif (xSelect == null && w.IsAlive) {\n\t\t\t//IAccessible of some controls are not connected to the parent.\n\t\t\t//\tAlso, WndContainer then may get the top-level window.\n\t\t\t//\tWorkaround: enum child controls and look for _elm in one them. Then add \"class\" row if need.\n\t\t\tDebug_.Print(\"broken IAccessible branch\");\n\t\t\tforeach (var c in w.Get.Children(onlyVisible: true)) {\n\t\t\t\tvar m = _CreateTreeModel(c, p, true);\n\t\t\t\tif (m.xSelect != null) {\n\t\t\t\t\t//m.xRoot.a = elm.fromWindow(c, flags: EWFlags.NoThrow);\n\t\t\t\t\t//if(m.xRoot.a != null) model.xRoot.Add(m.xRoot);\n\t\t\t\t\t//else model.xRoot = m.xRoot;\n\t\t\t\t\t(xRoot, xSelect) = m;\n\t\t\t\t\tif (!_page.classA.Visible) {\n\t\t\t\t\t\t_page.classA.t.Text = TUtil.StripWndClassName(c.ClassName, true);\n\t\t\t\t\t\t_page.classA.c.IsChecked = true;\n\t\t\t\t\t\t_page.classA.Visible = true;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\t_tree.SetItems(xRoot.Children());\n\t\t_treeRoot = xRoot;\n\t\tif (xSelect != null) _SelectTreeItem(xSelect);\n\t\t\n\t\tGC.Collect(1); //release old COM objects. Else may become slower.\n\t}\n\t\n\tvoid _SelectTreeItem(_TreeItem x) {\n\t\t_tree.SelectSingle(x);\n\t}\n\t\n\t//Tries to find and select _elm in current tree when captured from same window.\n\t//Usually faster than recreating tree, but in some cases can be slower. Slower when fails to find.\n\tbool _TrySelectInSameTree(out _TreeItem ti) {\n\t\tti = null;\n\t\tif (_treeRoot == null) return false;\n\t\tvar a = _treeRoot.Descendants().ToArray();\n\t\tif (a.Length == 0) return false;\n\t\t\n\t\tif (_elm.MiscFlags.Has(EMiscFlags.UIA) != _page.uiaA.IsChecked //different tree\n\t\t\t|| _elm.MiscFlags.Has(EMiscFlags.InProc) != a[0].e.MiscFlags.Has(EMiscFlags.InProc) //different rects if DPI-scaled window\n\t\t\t) {\n\t\t\t_ClearTree();\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\tti = _Find(_elm, a);\n\t\tstatic _TreeItem _Find(elm e, _TreeItem[] a) {\n\t\t\tif (!e.GetProperties(\"rn\", out var p)) return null;\n\t\t\tint item = e.Item;\n\t\t\tvar ri = e.RoleInt;\n\t\t\tstring rs = ri == ERole.Custom ? e.Role : null;\n\t\t\t//CONSIDER: to make faster, run all this code inproc. Or at least switch context once for each element.\n\t\t\tforeach (var v in a) {\n\t\t\t\te = v.e;\n\t\t\t\tif (e.Item != item) continue;\n\t\t\t\tif (e.RoleInt != ri) continue;\n\t\t\t\tif (rs != null && e.Role != rs) continue;\n\t\t\t\tif (!e.GetRect(out var rr, raw: true) || rr != p.Rect) continue;\n\t\t\t\tif (e.Name != p.Name) continue;\n\t\t\t\treturn v;\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\t\n\t\tif (ti != null) _SelectTreeItem(ti);\n\t\telse Debug_.Print(\"recreating tree of same window\");\n\t\treturn ti != null;\n\t\t\n\t\t//Other ways to compare elm:\n\t\t//IAccIdentity. Unavailable in web pages.\n\t\t//IUIAutomationElement. Very slow ElementFromIAccessible. In Firefox can be 30 ms.\n\t}\n\t\n\tclass _TreeItem : TreeBase<_TreeItem>, ITreeViewItem {\n\t\treadonly Delm _dlg;\n\t\tpublic elm e;\n\t\tstring _displayText;\n\t\tbool _isFailed;\n\t\tbool _isInvisible;\n\t\tbool _isExpanded;\n\t\t\n\t\tpublic _TreeItem(Delm dlg) {\n\t\t\t_dlg = dlg;\n\t\t}\n\t\t\n\t\t#region ITreeViewItem\n\t\t\n\t\tstring ITreeViewItem.DisplayText {\n\t\t\tget {\n\t\t\t\tif (_displayText == null) {\n\t\t\t\t\tbool isWINDOW = e.RoleInt == ERole.WINDOW;\n\t\t\t\t\tstring props = isWINDOW ? \"Rnsw\" : \"Rns\";\n\t\t\t\t\tif (!e.GetProperties(props, out var p)) {\n\t\t\t\t\t\t_displayText = \"Failed: \" + lastError.message;\n\t\t\t\t\t\t(_isInvisible, _isFailed) = (false, true);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (isWINDOW) {\n\t\t\t\t\t\t\tusing (new StringBuilder_(out var b)) {\n\t\t\t\t\t\t\t\tb.Append(p.Role).Append(\"  (\").Append(p.WndContainer.ClassName).Append(')');\n\t\t\t\t\t\t\t\tif (p.Name.Length > 0) b.Append(\"  \\\"\").Append(p.Name).Append('\"');\n\t\t\t\t\t\t\t\t_displayText = b.ToString();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else if (p.Name.Length == 0) {\n\t\t\t\t\t\t\t_displayText = p.Role;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t_displayText = p.Role + \" \\\"\" + p.Name.Escape(limit: 250) + \"\\\"\";\n\t\t\t\t\t\t}\n\t\t\t\t\t\t(_isInvisible, _isFailed) = (e.IsInvisible_(p.State), false);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn _displayText;\n\t\t\t}\n\t\t}\n\t\t\n\t\tvoid ITreeViewItem.SetIsExpanded(bool yes) { _isExpanded = yes; }\n\t\t\n\t\tbool ITreeViewItem.IsExpanded => _isExpanded;\n\t\t\n\t\tIEnumerable<ITreeViewItem> ITreeViewItem.Items => base.Children();\n\t\t\n\t\tbool ITreeViewItem.IsFolder => _IsFolder;\n\t\tbool _IsFolder => base.HasChildren;\n\t\t\n\t\tobject ITreeViewItem.Image => _IsFolder ? LA.EdIcons.FolderArrow(_isExpanded) : null;\n\t\t\n\t\tint ITreeViewItem.TextColor(TVColorInfo ci)\n\t\t\t=> _isFailed ? 0xff0000\n\t\t\t: _isInvisible ? ColorInt.SwapRB(Api.GetSysColor(Api.COLOR_GRAYTEXT))\n\t\t\t: -1;\n\t\t\n\t\tint ITreeViewItem.BorderColor(TVColorInfo ci)\n\t\t\t=> _dlg._PathFind(this) >= 0 ? 0x00C000 : -1;\n\t\t\n\t\t#endregion\n\t}\n\t\n\t#endregion\n\t\n\t#region path\n\t\n\tList<(_TreeItem ti, _PropPage page)> _path;\n\t\n\tvoid _PathAddRemove() {\n\t\tif (_tree.SelectedItem is not _TreeItem ti) return;\n\t\tbool add = _page.inPath.IsChecked;\n\t\tif (add) {\n\t\t\tif (_path == null) {\n\t\t\t\t_path = new() { (ti, _commonPage) };\n\t\t\t} else {\n\t\t\t\t//find index where to insert\n\t\t\t\tint i = -1;\n\t\t\t\tforeach (var v in ti.Ancestors()) if (v == _path[^1].ti) { i = _path.Count; break; } //is ti after current path?\n\t\t\t\tif (i < 0) { //is ti before current path?\n\t\t\t\t\tfor (int j = 0; j < _path.Count; j++) {\n\t\t\t\t\t\tif (_path[j].ti.Ancestors().Contains(ti)) { i = j; break; }\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (i < 0) { //isn't straight path. Eg the user wants to use navig.\n\t\t\t\t\tif (!dialog.showInputNumber(out i, \"Index in path\", \"This element isn't an ancestor or descendant of a path element,\\r\\ntherefore its index in path cannot be determined automatically.\\r\\n\\r\\n0-based index:\", 0, owner: this)) {\n\t\t\t\t\t\t_page.inPath.IsChecked = false;\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\ti = Math.Clamp(i, 0, _path.Count);\n\t\t\t\t}\n\t\t\t\t_path.Insert(i, (ti, _commonPage));\n\t\t\t}\n\t\t\t_page = _commonPage;\n\t\t\t_commonPage = null;\n\t\t\t_page.ti = ti;\n\t\t} else {\n\t\t\tint i = _PathFind(ti); if (i < 0) return;\n\t\t\tif (_path.Count > 1) _path.RemoveAt(i); else _path = null;\n\t\t\t_page.ti = null;\n\t\t\t_commonPage = _page;\n\t\t}\n\t\t\n\t\t_tree.Redraw();\n\t\t//then _FormatCode will be called\n\t}\n\t\n\tbool _PathSetPageWhenTreeItemSelected(_TreeItem ti) {\n\t\t//print.it(ti.e);\n\t\tif (_path == null || _page == null) return false;\n\t\tif (_page.ti == ti) return false;\n\t\tint i = _PathFind(ti);\n\t\t_page = i < 0 ? _commonPage : _path[i].page;\n\t\tif (i >= 0) _pageBorder.Child = _page.panel;\n\t\treturn i >= 0;\n\t}\n\t\n\tint _PathFind(_TreeItem ti) {\n\t\tif (_path != null) {\n\t\t\tfor (int i = 0; i < _path.Count; i++) if (_path[i].ti == ti) return i;\n\t\t}\n\t\treturn -1;\n\t}\n\t\n\tbool _PathIsIntermediate() {\n\t\tif (_path == null || _page == null) return false;\n\t\treturn (uint)_PathFind(_page.ti) < _path.Count - 1;\n\t}\n\t\n\t//int _PathFind() {\n\t//\tif (_path != null && _tree.SelectedItem is _TreeItem ti) return _PathFind(ti);\n\t//\treturn -1;\n\t//}\n\t\n\tvoid _PathClear() {\n\t\tif (_path == null) return;\n\t\tif (_page?.ti != null) {\n\t\t\t_page.ti = null;\n\t\t\tusing var nevc = new _NoeventValueChanged(this);\n\t\t\t_page.inPath.IsChecked = false;\n\t\t\t_commonPage = _page;\n\t\t}\n\t\t_path = null;\n\t}\n\t\n\t#endregion\n\t\n\t#region action\n\t\n\tconst string c_actions = \"\"\"\nNo action\nInvoke | Invoke()\nWebInvoke | WebInvoke()\nMouse\n\tMouse click | MouseClick(%)\n\tMouse 2*click | MouseClickD(%)\n\tMouse right click | MouseClickR(%)\n\tMouse move | MouseMove(%)\n\t-\n\tPost click | PostClick(%)\n\tPost 2*click | PostClickD(%)\n\tPost right click | PostClickR(%)\nKeys, text\n\tSend keys | SendKeys(\"\")\n\tReplace text | SendKeys(\"Ctrl+A\", \"!text\")\n\tAppend text | SendKeys(\"Ctrl+End\", \"!text\")\n\t-\n\tClick, send keys | MouseClick(); keys.send(\"\")\n\tClick, replace text | MouseClick(); keys.send(\"Ctrl+A\", \"!text\")\n\tClick, append text | MouseClick(); keys.send(\"Ctrl+End\", \"!text\")\nFocus, select\n\tFocus | Focus()\n\tSelect | Select()\n\tSelect and focus | Focus(true)\nCheck\n\tCheck | Check(true)\n\tCheck (keys) | Check(true, \"\")\n\tCheck (click) | Check(true, e => e.MouseClick(%))\n\tCheck (post) | Check(true, e => e.PostClick(%))\n\t-\n\tUncheck | Check(false)\n\tUncheck (keys) | Check(false, \"\")\n\tUncheck (click) | Check(false, e => e.MouseClick(%))\n\tUncheck (post) | Check(false, e => e.PostClick(%))\nComboSelect\n\tComboSelect | ComboSelect(^)\n\tComboSelect (invoke) | ComboSelect(^, \"i\")\n\tComboSelect (keys) | ComboSelect(^, \"k\")\n\tComboSelect (mouse) | ComboSelect(^, \"m\")\n\tItem... | #cs\nExpand\n\tExpand | Expand(true)\n\tExpand (keys) | Expand(true, \"\")\n\tExpand (click) | Expand(true, e => e.MouseClick(%))\n\tExpand (2*click) | Expand(true, e => e.MouseClickD(%))\n\tExpand (post) | Expand(true, e => e.PostClick(%))\n\tExpand (2*post) | Expand(true, e => e.PostClickD(%))\n\tPath... | #ep\n\t-\n\tCollapse | Expand(false)\n\tCollapse (keys) | Expand(false, \"\")\n\tCollapse (click) | Expand(false, e => e.MouseClick(%))\n\tCollapse (2*click) | Expand(false, e => e.MouseClickD(%))\n\tCollapse (post) | Expand(false, e => e.PostClick(%))\n\tCollapse (2*post) | Expand(false, e => e.PostClickD(%))\nScrollTo | ScrollTo()\nWaitFor | WaitFor(0, o => !o.IsDisabled) | 1\n-\nFindAll\n\tFindAll, foreach || 3\n\tFindAll, for || 3\n\tFindAll, Select || 3\n\tFindAll, Select 2 || 3\n\tFindAll, table || 3\n\t-\n\tHelp - table | ?table | 3\nnew elmFinder || 5\n\"\"\";\n\t\n\trecord class _Action(string name, string code, int _flags, bool isSubmenu, bool inSubmenu, bool separatorBefore, bool isNone) {\n\t\tpublic bool NoTest => 0 != (_flags & 1);\n\t\tpublic bool IsFindAll => 0 != (_flags & 2);\n\t\tpublic bool IsFinder => 0 != (_flags & 4);\n\t\tpublic bool IsMouse => code?.Contains('%') == true;\n\t}\n\t\n\tList<_Action> _aActions; //for menu\n\t_Action _selectedAction, _testedAction;\n\t\n\t_Action _CurrentAction => !_selectedAction.isNone ? _selectedAction : _testedAction;\n\t\n\t_Action _ActionFind(string name) => _aActions.FirstOrDefault(o => o.name == name && !o.isSubmenu) ?? _aActions[0];\n\t\n\tvoid _ActionInit() {\n\t\t_cbAction.Items.Add(\"\");\n\t\t_cbAction.DropDownOpened += (o, e) => {\n\t\t\t_cbAction.IsDropDownOpen = false;\n\t\t\t_ActionMenu(false);\n\t\t};\n\t\t\n\t\t_aActions = new();\n\t\tvar a = c_actions.Lines();\n\t\tbool separatorBefore = false;\n\t\tfor (int i = 0; i < a.Length; i++) {\n\t\t\tvar k = a[i].Split('|', StringSplitOptions.TrimEntries);\n\t\t\tvar name = k[0];\n\t\t\tbool inSubmenu = a[i][0] == '\\t';\n\t\t\tif (k.Length == 1) {\n\t\t\t\tif (name == \"-\") { separatorBefore = true; continue; }\n\t\t\t\tvar x = new _Action(name, null, 0, i > 0, false, separatorBefore, i == 0);\n\t\t\t\t_aActions.Add(x);\n\t\t\t} else {\n\t\t\t\tvar x = new _Action(name, k[1], k.Length < 3 ? 0 : k[2].ToInt(), false, inSubmenu, separatorBefore, false);\n\t\t\t\t_aActions.Add(x);\n\t\t\t}\n\t\t\tseparatorBefore = false;\n\t\t}\n\t\t\n\t\t_testedAction = _aActions[0];\n\t\t_ActionChange(_ActionFind(LA.App.Settings.delm.def_action));\n\t}\n\t\n\tvoid _ActionChange(_Action action) {\n\t\t_selectedAction = action;\n\t\t_cbAction.Items[0] = action.name;\n\t\t_cbAction.SelectedIndex = 0;\n\t\t_ActionSetControlsVisibility();\n\t\tif (_page != null) _FormatCode();\n\t}\n\t\n\t_Action _ActionMenu(bool test) {\n\t\tvar m = new popupMenu();\n\t\tfor (int i = test ? 1 : 0; i < _aActions.Count; i++) {\n\t\t\tvar x = _aActions[i];\n\t\t\tif (test && x.NoTest) break;\n\t\t\tif (x.isSubmenu) {\n\t\t\t\tif (x.separatorBefore) m.Separator();\n\t\t\t\tint from = ++i, to = i;\n\t\t\t\twhile (to < _aActions.Count && _aActions[to].inSubmenu) i = to++;\n\t\t\t\tm.Submenu(x.name, m => { for (int i = from; i < to; i++) _Add(m, i); });\n\t\t\t} else {\n\t\t\t\t_Add(m, i);\n\t\t\t}\n\t\t\t\n\t\t\tvoid _Add(popupMenu m, int i) {\n\t\t\t\tvar x = _aActions[i];\n\t\t\t\tif (test && x.code is ['#' or '?', ..]) return;\n\t\t\t\tif (x.separatorBefore) m.Separator();\n\t\t\t\tm.Add(i + 1, x.name);\n\t\t\t}\n\t\t}\n\t\t\n\t\t_actionMenu?.Close(); _actionMenu = m; //prevent multiple instances on capture+test\n\t\tint ia;\n\t\tif (test) { //called when testing if no action selected\n\t\t\tia = m.Show(owner: this);\n\t\t} else {\n\t\t\tvar r = _cbAction.RectInScreen();\n\t\t\tia = m.Show(PMFlags.AlignRectBottomTop, new POINT(r.left, r.bottom), r, owner: this);\n\t\t}\n\t\t_actionMenu = null;\n\t\tif (--ia < 0) return _aActions[0];\n\t\tvar action = _aActions[ia];\n\t\t\n\t\tif (!test) {\n\t\t\t_testedAction = _aActions[0];\n\t\t\t\n\t\t\tif (action.code is string s1) {\n\t\t\t\tif (s1.Starts('#')) {\n\t\t\t\t\tif (!_ActionInputStringArg(s1, out string selectAction)) return _aActions[0];\n\t\t\t\t\t//select the first action in this submenu, but don't change if was selected an action in this submenu\n\t\t\t\t\taction = _selectedAction.name.Starts(selectAction) ? _CurrentAction : _ActionFind(selectAction);\n\t\t\t\t\tif (test && ReferenceEquals(action, _testedAction)) _FormatCode();\n\t\t\t\t} else if (s1.Starts('?')) {\n\t\t\t\t\tif (s1 == \"?table\") {\n\t\t\t\t\t\tLA.PanelHelp.OpenRecipe(\"Extract table*UI*\");\n\t\t\t\t\t\taction = _ActionFind(\"FindAll, table\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (!test) {\n\t\t\t_ActionChange(action);\n\t\t} else if (!ReferenceEquals(action, _testedAction)) {\n\t\t\t_testedAction = action;\n\t\t\t_ActionSetControlsVisibility();\n\t\t\t_FormatCode();\n\t\t}\n\t\treturn action;\n\t}\n\tpopupMenu _actionMenu;\n\t\n\tstring _ActionGetCode(bool test) {\n\t\tvar action = !test ? _CurrentAction : !_selectedAction.isNone ? _selectedAction : _ActionMenu(true);\n\t\t\n\t\tif (test && action.name == \"WebInvoke\") action = _ActionFind(\"Invoke\"); //avoid waiting\n\t\t\n\t\tvar s = action.code;\n\t\tif (s != null) {\n\t\t\tint j = s.IndexOf('%'); //mouse x y placeholder\n\t\t\tif (j > 0) {\n\t\t\t\tbool scroll = _cScroll.IsChecked;\n\t\t\t\tif (_xy.GetText(out var xy)) {\n\t\t\t\t\tif (s[j - 1] != '(') xy = \", \" + xy;\n\t\t\t\t\t//if (s[j + 1] != ')') xy += \", \";\n\t\t\t\t\tif (scroll) xy += \", scroll: 50\";\n\t\t\t\t\ts = s.ReplaceAt(j, 1, xy);\n\t\t\t\t} else {\n\t\t\t\t\ts = s.Remove(j, 1);\n\t\t\t\t\tif (scroll) s = s.Insert(j, \"scroll: 50\");\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tif (s.Starts(\"Expand(true\")) {\n\t\t\t\tif (_actionExpandPath != null) s = s.ReplaceAt(7, 4, _actionExpandPath.Escape(quote: true));\n\t\t\t} else if (s.Starts(\"ComboSelect(^\")) {\n\t\t\t\tif (test) {\n\t\t\t\t\tif (_actionComboSelectItem == null || popupMenu.showSimple(new(_actionComboSelectItem, \"8492 Other...\")) == 8492) {\n\t\t\t\t\t\tif (!_ActionInputStringArg(\"#cs\", out _) || _actionComboSelectItem == null) return null;\n\t\t\t\t\t\t_FormatCode();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\ts = s.ReplaceAt(12, 1, (_actionComboSelectItem ?? \"Item\").Escape(quote: true));\n\t\t\t}\n\t\t}\n\t\treturn s;\n\t}\n\tstring _actionExpandPath, _actionComboSelectItem;\n\t\n\tbool _ActionInputStringArg(string what, out string selectAction) {\n\t\tselectAction = null;\n\t\tswitch (what) {\n\t\tcase \"#cs\":\n\t\t\tselectAction = \"ComboSelect\";\n\t\t\treturn _Dialog(ref _actionComboSelectItem, \"ComboSelect item\", \"Combo box item name. Wildcard expression.\");\n\t\tcase \"#ep\":\n\t\t\tselectAction = \"Expand\";\n\t\t\treturn _Dialog(ref _actionExpandPath, \"Expand path\", \"Tree node path for Expand actions.\\nExample: Folder1|Folder2|Folder3\\nPath parts are wildcard expressions.\");\n\t\t}\n\t\treturn false; //impossible\n\t\t\n\t\tbool _Dialog(ref string r, string text1, string text2) {\n\t\t\tif (!dialog.showInput(out string s, text1, text2, editText: r, owner: this)) return false;\n\t\t\tr = s.NullIfEmpty_();\n\t\t\treturn true;\n\t\t}\n\t}\n\t\n\tvoid _ActionSetControlsVisibility() {\n\t\tvar action = _CurrentAction;\n\t\tbool isMouse = action.IsMouse;\n\t\t_xy.Visible = isMouse;\n\t\t_cScroll.Visibility = isMouse ? Visibility.Visible : Visibility.Hidden;\n\t\tbool isFind = !(action.IsFindAll || action.IsFinder);\n\t\t_wait.Visible = isFind;\n\t\t_cException.Visibility = isFind ? Visibility.Visible : Visibility.Hidden;\n\t}\n\t\n\t#endregion\n\t\n\t#region misc\n\t\n\tvoid _ToolSettings() {\n\t\tvar m = new popupMenu();\n\t\tvar cAT = m.AddCheck(\"Auto test find\", _Opt.Has(_EOptions.AutoTest)); cAT.Tooltip = \"Test find when captured\";\n\t\tvar cWA = m.AddCheck(\".Activate()\", _Opt.Has(_EOptions.Activate)); cWA.Tooltip = \"Append .Activate() to wnd.find(...), unless the window looks like does not like to be activated\";\n\t\tvar cVR = m.AddCheck(\"var role\", _Opt.Has(_EOptions.VarRole)); cVR.Tooltip = \"Use role in elm variable name\";\n\t\t//var cCC = m.AddCheck(\"Compact code\", _Opt.Has(_EOptions.Compact)); cNS.Tooltip = \"Insert code without { } and don't use elm e with action\";\n\t\tm.Separator();\n\t\tm.Add(\"Save action\", _ => {\n\t\t\tLA.App.Settings.delm.def_action = _selectedAction.name;\n\t\t\t_SetOpt(_EOptions.MouseXY, _xy.c.IsChecked);\n\t\t\t_SetOpt(_EOptions.MouseScroll, _cScroll.IsChecked);\n\t\t}).Tooltip = \"Let the tool start with current action and its settings\";\n\t\tm.Add(\"Save wait\", _ => {\n\t\t\tLA.App.Settings.delm.def_wait = _wait.t.Text;\n\t\t\t_SetOpt(_EOptions.NoWait, !_wait.c.IsChecked);\n\t\t}).Tooltip = \"Let the tool start with current wait settings\";\n\t\tm.Add(\"Save UIA\", _ => {\n\t\t\tLA.App.Settings.delm.def_UIA = _cUIA.IsChecked;\n\t\t}).Tooltip = \"Let the tool start with current UIA checkbox state\";\n\t\t//if (Java.GetJavaPath(out _)) { //moved to Options -> OS. It isn't a tool setting.\n\t\t//\tm.Separator();\n\t\t//\tm[\"Java...\"] = o => Java.EnableDisableJabUI(this);\n\t\t//}\n\t\tm.Show(owner: this);\n\t\t_SetOpt(_EOptions.AutoTest, cAT.IsChecked);\n\t\t//bool format = _SetOpt(_EOptions.Compact, cCC.IsChecked);\n\t\t//if (format) _FormatCode();\n\t\t_SetOpt(_EOptions.Activate, cWA.IsChecked);\n\t\t_SetOpt(_EOptions.VarRole, cVR.IsChecked);\n\t}\n\t\n\t[Flags]\n\tenum _EOptions {\n\t\tAutoTest = 1,\n\t\t//NoScope = 1 << 1, //rejected\n\t\tMouseXY = 1 << 2,\n\t\tNoWait = 1 << 3,\n\t\tMouseScroll = 1 << 4,\n\t\tActivate = 1 << 5,\n\t\tVarRole = 1 << 6,\n\t\t//and don't save autotestaction, autoinsert\n\t}\n\t\n\tstatic _EOptions _Opt {\n\t\tget => (_EOptions)LA.App.Settings.delm.flags;\n\t\tset => LA.App.Settings.delm.flags = (int)value;\n\t}\n\t\n\tstatic bool _SetOpt(_EOptions opt, bool on) {\n\t\t_EOptions f = _Opt;\n\t\tif (f.Has(opt) == on) return false;\n\t\tf.SetFlag(opt, on);\n\t\t_Opt = f;\n\t\treturn true;\n\t}\n\t\n\t//Builds navig path from the selected tree node to *to*. Sets control text and checkbox.\n\tvoid _NavigateTo(_TreeItem to) {\n\t\tif (_tree.SelectedItem is not _TreeItem from) return;\n\t\t//print.it(from.e); print.it(to.e);\n\t\tvar a = new List<(string s, int n)>();\n\t\tif (from.Parent == to.Parent) {\n\t\t\t_AppendNePr(from, to);\n\t\t} else {\n\t\t\tvar aFrom = from.AncestorsFromRoot(andSelf: true);\n\t\t\tint i = Array.IndexOf(aFrom, to);\n\t\t\tif (i >= 0) { //'to' is ancestor of 'from'\n\t\t\t\t_AppendPa();\n\t\t\t} else {\n\t\t\t\t//find common ancestor\n\t\t\t\tvar aTo = to.AncestorsFromRoot(andSelf: true);\n\t\t\t\tfor (i = Math.Min(aFrom.Length, aTo.Length); --i >= 0;) if (aFrom[i] == aTo[i]) break;\n\t\t\t\t\n\t\t\t\tif (++i < aFrom.Length) {\n\t\t\t\t\t_AppendPa();\n\t\t\t\t\t_AppendNePr(aFrom[i], aTo[i]);\n\t\t\t\t} else i--;\n\t\t\t\t\n\t\t\t\twhile (++i < aTo.Length) {\n\t\t\t\t\tvar v = aTo[i]; var p = aTo[i].Parent;\n\t\t\t\t\tvar s = v == p.FirstChild ? \"fi\" : v == p.LastChild ? \"la\" : null;\n\t\t\t\t\tif (s == null) a.Add((\"ch\", v.Index + 1));\n\t\t\t\t\telse if (a.Count > 0 && a[^1].s == s) a[^1] = (s, a[^1].n + 1);\n\t\t\t\t\telse a.Add((s, 1));\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tvoid _AppendPa() {\n\t\t\t\tint n = aFrom.Length - i - 1;\n\t\t\t\tif (n > 0) a.Add((\"pa\", n)); //else common parent with a 'to' ancestor\n\t\t\t}\n\t\t}\n\t\t\n\t\tvoid _AppendNePr(_TreeItem from, _TreeItem to) {\n\t\t\tif (to == from) return;\n\t\t\tint n = to.Index - from.Index;\n\t\t\ta.Add((n > 0 ? \"ne\" : \"pr\", Math.Abs(n)));\n\t\t\t//never mind: n may be incorrect because some UI elements skip invisible siblings. Eg standard WINDOW elements.\n\t\t\t//\tWe could detect it and either display a warning or use pa ch instead (can be unreliable).\n\t\t}\n\t\t\n\t\tvar b = new StringBuilder();\n\t\tforeach (var (s, n) in a) {\n\t\t\tif (b.Length > 0) b.Append(' ');\n\t\t\tb.Append(s);\n\t\t\tif (n > 1) b.Append(n);\n\t\t}\n\t\tvar navig = b.ToString();\n\t\t\n\t\t_page.navigA.Set(navig.Length > 0, navig);\n\t}\n\t\n\tpublic static class Java {\n\t\t/// <summary>\n\t\t/// Calls <see cref=\"EnableDisableJab\"/>. Before it shows dialog \"enable/disable\". After it shows dialog with results.\n\t\t/// </summary>\n\t\tpublic static void EnableDisableJabUI(AnyWnd owner) {\n\t\t\tbool enable;\n\t\t\tswitch (dialog.show(\"Enable or disable Java Access Bridge\", $\"If enabled, scripts and programs can find UI elements in Java apps.\", \"1 Enable|2 Disable|Cancel\", owner: owner, flags: DFlags.CenterOwner)) {\n\t\t\tcase 1: enable = true; break;\n\t\t\tcase 2: enable = false; break;\n\t\t\tdefault: return;\n\t\t\t}\n\t\t\tvar (ok, results) = EnableDisableJab(enable);\n\t\t\tdialog.show(null, results, icon: ok ? DIcon.Info : DIcon.Error, owner: owner, flags: DFlags.CenterOwner);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Enables or disables Java Access Bridge for current user.\n\t\t/// Returns: ok = false if failed or canceled. results = null if canceled.\n\t\t/// </summary>\n\t\tpublic static (bool ok, string results) EnableDisableJab(bool enable) {\n\t\t\tif (!GetJavaPath(out var path)) return (false, $\"Cannot find Java {RuntimeInformation.ProcessArchitecture} (JRE or JDK). Make sure it is installed and in PATH. Need the {RuntimeInformation.ProcessArchitecture} version, not 32-bit or {(osVersion.isArm64Process ? \"x64\" : \"ARM64\")}. If you have other Java versions (32-bit etc), keep them too.\");\n\t\t\t\n\t\t\tstring sout = null;\n\t\t\tstring jabswitch = path + @\"\\jabswitch.exe\";\n\t\t\tif (!filesystem.exists(jabswitch).File) return (false, \"Cannot find jabswitch.exe.\");\n\t\t\ttry {\n\t\t\t\trun.console(out sout, jabswitch, enable ? \"-enable\" : \"-disable\");\n\t\t\t\tsout = sout?.Trim();\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\treturn (false, ex.ToStringWithoutStack());\n\t\t\t}\n\t\t\t\n\t\t\tsout += \"\\r\\nAlso may need to restart Java apps and this app.\";\n\t\t\t\n\t\t\treturn (true, sout);\n\t\t\t\n\t\t\t//tested: the checkbox in CP does not disable JAB. Works only enabling.\n\t\t\t//\tTested on Win 10 (installed Java 64 and 32) and 7 (installed Java 64).\n\t\t\t//\tTested 64-bit and 32-bit processes.\n\t\t\t//\t\\lib\\accessibility.properties is not modified, ie not enabled for all users.\n\t\t\t//\tThis function works.\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Gets path of the bin folder of installed Java JRE or JDK. Only of same x64/AMD64 architecture as of this process.\n\t\t/// </summary>\n\t\tpublic static bool GetJavaPath(out string path) {\n\t\t\tpath = null;\n\t\t\ttry {\n\t\t\t\tif (0 == run.console(out string where, \"where\", \"java\")) {\n\t\t\t\t\tforeach (var javaExe in where.Lines()) {\n\t\t\t\t\t\tif (!javaExe.Ends(\".exe\", true)) continue;\n\t\t\t\t\t\trun.console(out string s, javaExe, \"-XshowSettings:properties\");\n\t\t\t\t\t\t\n\t\t\t\t\t\t//print.it($\"<><lc yellow>{javaExe}<>\");\n\t\t\t\t\t\t//print.it(s);\n\t\t\t\t\t\t\n\t\t\t\t\t\tvar arch = osVersion.isArm64Process ? \"aarch64\" : \"amd64\";\n\t\t\t\t\t\tif (s.RxIsMatch($@\"(?m)^\\h*os.arch *= *{arch}\\b\") && s.RxMatch(@\"(?m)^\\h*java.home *= *(.+)\", 1, out s)) {\n\t\t\t\t\t\t\tpath = s + \"\\\\bin\";\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch { }\n\t\t\treturn false;\n\t\t}\n\t}\n\t\n\t#endregion\n\t\n\t#region util\n\t\n\twnd _WndSearchIn => _useCon ? _con : _wnd;\n\t\n\t//Returns nonzero browser if e is in visible web page in a browser, and not UIA.\n\t//If Chrome document URL is not https/http/file, also returns the URL as wildcard.\n\tstatic (_BrowserEnum browser, string propUrl) _IsVisibleWebPage(elm e, wnd wContainer) {\n\t\tif (e.MiscFlags.HasAny(EMiscFlags.UIA | EMiscFlags.Java)) return default;\n\t\t\n\t\tvar browser = wContainer.HasStyle(WS.CHILD)\n\t\t\t? wContainer.ClassNameIs(Api.string_IES, \"Chrome_RenderWidgetHostHWND\") switch { 1 => _BrowserEnum.IE, 2 => _BrowserEnum.Chrome, _ => 0 }\n\t\t\t: (_BrowserEnum)wContainer.ClassNameIs(\"Chrome*\", \"Mozilla*\");\n\t\t\n\t\tif (browser is _BrowserEnum.Chrome or _BrowserEnum.FF) {\n\t\t\telm eDoc = null;\n\t\t\tdo {\n\t\t\t\tif (e.RoleInt == ERole.DOCUMENT) eDoc = e;\n\t\t\t\te = e.Parent;\n\t\t\t} while (e != null);\n\t\t\tif (eDoc == null || eDoc.IsInvisible) return default;\n\t\t\t\n\t\t\tif (browser == _BrowserEnum.Chrome) { //see _FindDocumentCallback\n\t\t\t\tvar url = eDoc.Value;\n\t\t\t\tif (url.NE()) return default;\n\t\t\t\tif (0 == url.Starts(false, \"https:\", \"http:\", \"file:\")) {\n\t\t\t\t\tif (url.Starts(\"devtools:\")) return (browser, \"devtools:*\");\n\t\t\t\t\tint i = url.FindAny(\"?*\"); if (i >= 0) url = url[..i] + \"*\"; //\"https://example?a=1\" -> \"https://example*\"\n\t\t\t\t\treturn (browser, url);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\treturn (browser, null);\n\t}\n\tenum _BrowserEnum { Chrome = 1, FF, IE }\n\t\n\tvoid _EnableDisableTopControls(bool enable) {\n\t\t_bTest.IsEnabled = enable; _bInsert.IsEnabled = enable;\n\t\t_ActionSetControlsVisibility();\n\t\tvar vis = enable ? Visibility.Visible : Visibility.Hidden;\n\t\t//_cbAction.Visibility = vis; _topRow2.Visibility = vis; //no, may want to change before capturing if 'Auto test action'\n\t\t_bWindow.Visibility = vis;\n\t\t_cControl.Visibility = vis;\n\t}\n\t\n\t//Shows popup menu m by RECT r or by the mouse cursor, depending on r size.\n\t//If r small, shows by r so that the menu does not cover the rectangle. Else shows by the mouse (else the user would not notice the menu easily).\n\t//If ods, shows OSD rects for menu items with Tag = RECT.\n\tint _ShowMenu(popupMenu m, RECT r, bool osd = false) {\n\t\tbool byMouse = r.Height > 200 || r.Is0;\n\t\tvar mf = byMouse ? PMFlags.Underline : PMFlags.Underline | PMFlags.AlignRectBottomTop | PMFlags.AlignCenterH;\n\t\tif (osd) _RectTimer(m);\n\t\treturn m.Show(mf, excludeRect: byMouse ? null : r, owner: this.IsLoaded ? this : default);\n\t\t\n\t\tstatic void _RectTimer(popupMenu m) {\n\t\t\tvar osd = new osdRect { Color = 0x80C000, Thickness = 3, TopmostWorkaround_ = true };\n\t\t\tPMItem pmi = null;\n\t\t\ttimer.every(100, t => {\n\t\t\t\tif (m.IsOpen) {\n\t\t\t\t\tvar mi = m.FocusedItem;\n\t\t\t\t\tif (mi != pmi) {\n\t\t\t\t\t\tpmi = mi;\n\t\t\t\t\t\tif (mi?.Tag is RECT r) {\n\t\t\t\t\t\t\tr.Inflate(1, 1);\n\t\t\t\t\t\t\tosd.Rect = r;\n\t\t\t\t\t\t\tosd.Visible = true;\n\t\t\t\t\t\t} else osd.Visible = false;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tt.Stop();\n\t\t\t\t\tosd.Dispose();\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t}\n\t\n\tstatic bool _RoleIsLinkOrButton(ERole role) => role is ERole.LINK\n\t\t\tor ERole.BUTTON or ERole.BUTTONMENU or ERole.BUTTONDROPDOWN or ERole.BUTTONDROPDOWNGRID\n\t\t\tor ERole.CHECKBOX or ERole.RADIOBUTTON;\n\t\n\t#endregion\n\t\n\t#region info\n\t\n\tTUtil.CommonInfos _commonInfos;\n\tvoid _InitInfo() {\n\t\t_commonInfos = new TUtil.CommonInfos(_info);\n\t\t\n\t\t_info.aaaText = _dialogInfo;\n\t\t_info.AaAddElem(this, _dialogInfo);\n\t\t_info.AaTags.AddLinkTag(\"+jab\", _ => Java.EnableDisableJabUI(this));\n\t\t_info.AaTags.AddLinkTag(\"+actTest\", _ => { if (_wnd.ActivateL()) _Test(); });\n\t\tTUtil.CapturingWithHotkey.RegisterLink_DialogHotkey(_info);\n\t\t\n\t\t//note: for Test button etc it's better to use tooltip, not _info.\n\t\t\n\t\t_info.InfoCT(_xy,\n@\"Mouse x y in the UI element. Empty = center.\nSee <help Au.Types.Coord>Coord<>. Examples:\n<mono>10, 10\n^10, .9f<>\");\n\t\t_info.InfoC(_cScroll, @\"At first call ScrollTo\");\n\t\t_info.InfoC(_cControl,\n@\"Find first matching control and search in it, not in all matching controls.\nTo change window or/and control name etc, click <b>Window...<> or edit it in the code field.\");\n\t\t_info.InfoCT(_wait,\n@\"The wait timeout, seconds.\nThe function waits max this time interval. On timeout throws exception if <b>Exception<> checked, else returns null. If empty, uses 8e88 (infinite).\");\n\t\t_info.InfoC(_cException,\n@\"Throw exception if not found.\nIf unchecked, returns null.\");\n\t\t\n\t\t_info.Info(_tree, \"Tree view\",\n@\"All UI elements in the window.\");\n\t\t\n\t\t//TODO3: now no info for HwndHost\n\t\t//\t\t_info.Info(_code, \"Code\",\n\t\t//@\"Code to find the UI element.\n\t\t//The \"\"find window\"\" part can be edited directly.\");\n\t}\n\t\n\tstring _dialogInfo = $@\"This tool creates code to find <help elm>UI element<> in <help wnd.find>window<>.\n1. Move the mouse to a UI element. Press <+hotkey>hotkey<> <b>{LA.App.Settings.delm.hk_capture}<>.\n2. Click the Test button to see how the 'find' code works.\n3. If need, change some fields or select another element.\n4. Click Insert. Click Close, or capture/insert again.\n5. If need, edit the code in editor. For example rename variables, delete duplicate wnd.find lines, replace part of window name with *, add code to click the UI element. <help elm>Examples<>.\n\nHow to find UI elements that don't have a name or other property with unique constant value? Capture another UI element near it, and use <b>navig<> to get it. Or try <b>skip<>. Or path.\n\nIf the wanted element is \"\"behind\"\" a bigger element, try <+hotkey>hotkey<> <b>{LA.App.Settings.delm.hk_smaller}<>.\";\n\t\n\tconst string c_infoJava = \"If there are no UI elements in this window, need to <+jab>enable<> Java Access Bridge etc. More info in <help>elm<> help.\";\n\t\n\tpartial class _PropPage {\n\t\tvoid _InitInfo() {\n\t\t\tvar _info = _dlg._info;\n\t\t\t_info.InfoCT(roleA,\n@\"Role. Prefix <b>web:<> means \"\"in web page\"\".\nRead more in <help>elmFinder[]<> help.\");\n\t\t\t_info.InfoCT(nameA, \"Name.\", true);\n\t\t\t_info.InfoCT(uiaidA, \"UIA AutomationId.\", true);\n\t\t\t_info.InfoCT(uiacnA, \"UIA ClassName.\", true);\n\t\t\t_info.InfoCT(idA, \"Control id. Will search only in controls that have it.\");\n\t\t\t_info.InfoCT(classA, \"Control class name. Will search only in controls that have it.\", true);\n\t\t\t_info.InfoCT(valueA, \"Value.\", true);\n\t\t\t_info.InfoCT(descriptionA, \"Description.\", true);\n\t\t\t_info.InfoCT(actionA, \"Default action.\", true);\n\t\t\t_info.InfoCT(keyA, \"Keyboard shortcut.\", true);\n\t\t\t_info.InfoCT(helpA, \"Help.\", true);\n\t\t\t_info.InfoCT(urlA, \"Chrome DOCUMENT URL (Value).\", true);\n\t\t\t_info.InfoCT(elemA,\n@\"Simple element id.\");\n\t\t\t_info.InfoCT(stateA,\n@\"State. List of <help Au.Types.EState>states<> this UI element must have and/or not have.\nExample: CHECKED, !DISABLED\nNote: states can change. Use only states you need. Remove others from the list.\");\n\t\t\t_info.InfoCT(rectA,\n@\"Raw rectangle. Can be specified width (W) and/or height (H).\nExample: {W=100 H=20}\");\n\t\t\t\n\t\t\t_info.InfoCT(alsoA,\n@\"<help>elmFinder[]<> <i>also<> \" + TUtil.CommonInfos.c_alsoParameter);\n\t\t\t_info.InfoCT(skipA,\n@\"0-based index of matching UI element.\nFor example, if 1, gets the second matching element.\n-1 means any matching intermediate element when used path or <b>navig<>.\");\n\t\t\t_info.InfoCT(navigA,\n@\"Get another UI element using this tree path from the found element.\nSee <help>elm.Navigate<>. Tool: in the tree view right click that element...\nOne or several words: <u><i>parent<> <i>child<> <i>first<> <i>last<> <i>next<> <i>previous<><>. Or 2 letters, like <i>ne<>.\nExample: pa ne2 ch3. The 2 means 2 times (ne ne). The 3 means 3-rd child; -3 would be 3-rd from end.\nNote: ne/pr may skip invisible siblings.\nSome elements also support <u><i>up<> <i>down<> <i>left<> <i>right<><>.\");\n\t\t\t\n\t\t\t_info.InfoC(hiddenTooA, \"Flag <help>Au.Types.EFFlags<>.HiddenToo.\");\n\t\t\t_info.InfoC(reverseA, \"Flag <help>Au.Types.EFFlags<>.Reverse (search bottom to top).\");\n\t\t\t_info.InfoC(uiaA,\n@\"Flag <help>Au.Types.EFFlags<>.UIA.\nThis checkbox may change when capturing, depending on what is usually better in that case; it also depends on the above UIA checkbox.\");\n\t\t\t_info.InfoC(notInprocA, @\"Flag <help>Au.Types.EFFlags<>.NotInProc.\nIf checked, the tool also captures elements not in-proc.\");\n\t\t\t_info.InfoC(clientAreaA, \"Flag <help>Au.Types.EFFlags<>.ClientArea.\");\n\t\t\t_info.InfoC(menuTooA,\n@\"Flag <help>Au.Types.EFFlags<>.MenuToo.\nCheck this if the UI element is in a menu and its role is not MENUITEM or MENUPOPUP.\");\n\t\t\t_info.InfoCT(notinA,\n@\"Don't search in UI elements that have these roles. Can make faster.\nExample: LIST,TREE,TITLEBAR,SCROLLBAR\");\n\t\t\t_info.InfoCT(maxccA, \"Don't search in UI elements that have more direct children. Default 10000, min 1, max 1000000.\");\n\t\t\t_info.InfoCT(levelA,\n@\"0-based level of the UI element in the tree of UI elements. Or min and max levels. Default 0 1000.\nRelative to the window, control (if used <b>class<> or <b>id<>) or web page (role prefix <b>web:<> etc).\");\n\t\t\t_info.InfoC(inPath,\n@\"Adds this element to a path like [element1][element2][wantedElement].\nUse path when Test finds a similar element but in another ancestor element. Then need to find the correct ancestor element at first.\n1. Check this checkbox for the wanted element. 2. In the tree select an ancestor element and check this too. 3. Click Test. It should find the wanted element. If it doesn't, try <b>skip<> -1 for the ancestor element. If the ancestor cannot be uniquely identified (no name etc), you can try to find an element near it in the tree and use <b>navig<> to navigate to it.\n\nThis tool remembers edited properties of the element if this checkbox is checked. Also draws a green border in the tree.\");\n\t\t}\n\t}\n\t#endregion\n}\n"
  },
  {
    "path": "Au.Editor/Tools/Dnuget.cs",
    "content": "//For NuGet management we use dotnet.exe and full or minimal .NET SDK.\n//Also some API from the NuGet client SDK. But it does not have API for installing dlls etc.\n//We don't install packages for each script that uses them. We install all in current workspace, and let scripts use them.\n//\tTo avoid conflicts, packages can be installed in separate folders.\n//\tMore info in nuget.md.\n//Rejected: UI to search for packages and display package info. Why to duplicate the NuGet website.\n\nusing System.Text.Json.Nodes;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Input;\nusing System.Xml.Linq;\nusing System.Xml.XPath;\nusing Au.Controls;\nusing NGC = NuGet.Configuration;\nusing System.Collections.ObjectModel;\n\nnamespace LA;\n\nclass DNuget : KDialogWindow {\n\t/// <param name=\"package\">null or package name or folder\\name.</param>\n\tpublic static void ShowSingle(string package = null) {\n\t\tvar d = ShowSingle(() => new DNuget());\n\t\tif (package != null) {\n\t\t\tif (package.Split('\\\\', 2) is var a && a.Length == 2 && a[0].Length > 0 && a[1].Length > 0) { //folder\\name\n\t\t\t\tpackage = a[1];\n\t\t\t\td._cbFolder.Text = a[0];\n\t\t\t}\n\t\t\td._tPackage.Text = package;\n\t\t}\n\t}\n\t\n\treadonly TextBox _tPackage;\n\treadonly ComboBox _cbFolder, _cbSource;\n\treadonly KTreeView _tv;\n\treadonly Panel _panelManage;\n\treadonly KGroupBox _groupInstall, _groupInstalled;\n\treadonly Button _bMenu;\n\treadonly TextBlock _tStatus;\n\t\n\treadonly string _nugetDir = App.Model.NugetDirectory;\n\treadonly ObservableCollection<string> _folders;\n\treadonly WindowDisabler _disabler;\n\t\n\tDNuget() {\n\t\tInitWinProp(\"NuGet packages\", App.Wmain);\n\t\tvar b = new wpfBuilder(this).WinSize(550, 600).Columns(-1, 30, 0);\n\t\t\n\t\tb.R.StartGrid(out _groupInstall, \"Install\").Columns(0, -1, 0);\n\t\tb.R.Add(wpfBuilder.formattedText($\"<a href='https://www.nuget.org'>NuGet</a> package\"), out _tPackage)\n\t\t\t.Tooltip(\"Examples:\\nPackageName (will get the latest version)\\nPackageName --version 1.2.3\\ndotnet add package PackageName --version 1.2.3 (copied from the package's web page)\")\n\t\t\t.Focus();\n\t\tb.xAddButtonIcon(EdIcons.Paste, _ => { _tPackage.SelectAll(); _tPackage.Paste(); }, \"Paste\");\n\t\t\n\t\tb.R.StartGrid().Columns(76, 0, -1, 20, 0, -1);\n\t\t\n\t\tb.R.AddButton(out var bInstall, \"Install\", _ => _Install()).Disabled();\n\t\tbInstall.IsDefault = true;\n\t\t\n\t\tb.Add(\"into folder\", out _cbFolder).Editable().Tooltip(@\"Press F1 if need help with this. Or just use the default selected folder.\");\n\t\t_cbFolder.MaxDropDownHeight = 600;\n\t\t_cbFolder.ShouldPreserveUserEnteredPrefix = true;\n\t\tfilesystem.createDirectory(_nugetDir);\n\t\t_folders = new(filesystem.enumDirectories(_nugetDir).Select(o => o.Name));\n\t\tif (_folders.Count == 0) _folders.Add(\"-\");\n\t\t_cbFolder.ItemsSource = _folders;\n\t\t_cbFolder.SelectedIndex = 0; //probably \"-\"\n\t\t\n\t\tb.Window.AddHandler(TextBox.TextChangedEvent, new TextChangedEventHandler((_, e) => {\n\t\t\tbInstall.IsEnabled = !_tPackage.Text.Trim().NE() && !pathname.isInvalidName(_cbFolder.Text);\n\t\t\tif (e.Source == _tPackage) _PackageFieldTextChanged();\n\t\t}));\n\t\t\n\t\tb.Skip().Add(\"Source\", out _cbSource).Tooltip(\"Package source.\\nThe list contains nuget.org and sources specified in nuget.config files.\");\n\t\tb.End();\n\t\tb.End();\n\t\t\n\t\tb.Row(-1).StartGrid(out _groupInstalled, \"Installed\").Columns(-1, 76);\n\t\t\n\t\tb.Row(-1).xAddInBorder(out _tv);\n\t\t\n\t\tb.StartGrid().Columns(-1); //right\n\t\tb.Row(-1).StartStack(vertical: true).Disabled(); //buttons\n\t\tb.AddButton(\"Add code\", _ => _AddMeta()).Margin(\"B20\").Tooltip(\"Use the package in the current C# file.\\nAdds /*/ nuget Package; /*/.\");\n\t\tb.AddButton(\"→ NuGet\", _ => run.itSafe(\"https://www.nuget.org/packages/\" + _Selected.Name)).Tooltip(\"Open the package's NuGet webpage\");\n\t\tb.AddButton(\"→ Folder\", _ => run.itSafe(_FolderPath(_Selected.Parent.Name))).Margin(\"B20\").Tooltip(\"Open the folder\");\n\t\tb.AddButton(out var bUpdate, \"Update\", _ => _Update(false)).Tooltip(\"Replace this version with the latest version\");\n\t\tb.AddButton(out var bUpdateTo, \"Update ▾\", _ => _Update(true)).Tooltip(\"Replace this version with another version\");\n\t\tbUpdate.ContextMenuOpening += (_, e) => _Update(true);\n\t\tb.AddButton(\"Move to ▾\", _ => _Move()).Tooltip(\"Uninstall from this folder and install in another folder\");\n\t\tb.AddButton(\"Uninstall\", _ => _Uninstall()).Tooltip(\"Remove the package and its files from the folder\");\n\t\t\n\t\t_panelManage = b.Panel;\n\t\t_tv.SelectionChanged += (_, _) => _panelManage.IsEnabled = !_tv.SelectedItem?.IsFolder ?? false;\n\t\t_tv.ItemClick += _tv_ItemClick;\n\t\t\n\t\tb.End(); //buttons\n\t\tb.AddButton(\"Updates...\", _ => _CheckForUpdates()).Tooltip(\"Check for updates\");\n\t\tb.End(); //right\n\t\tb.End(); //group \"Installed\"\n\t\t\n\t\tb.R.Add(out _tStatus)\n\t\t\t.xAddButtonIcon(out _bMenu, \"*MaterialDesign.MoreHorizRound\" + EdIcons.black, _ => _Menu(), \"Menu\")\n\t\t\t.xAddDialogHelpButtonAndF1(\"editor/NuGet\");\n\t\t\n\t\tb.End();\n\t\t\n\t\tbool missingSdk = DotnetUtil.MissingSdkUI(b.Window, [_groupInstall, _groupInstalled, _bMenu], () => { _InitSources(); _FillTree(); });\n\t\tif (!missingSdk) _InitSources();\n\t\t\n\t\tLoaded += (_, _) => {\n\t\t\tApp.Model.UnloadingThisWorkspace += Close;\n\t\t\t_FillTree(); //note: fill now even if missingSdk, or the user might have a heart attack when seeing an empty list. When installed, _FillTree will be called again, because need to set _TreeItem.Source.\n\t\t};\n\t\t\n\t\t_disabler = new(this);\n\t}\n\t\n\tprotected override void OnClosed(EventArgs e) {\n\t\tApp.Model.UnloadingThisWorkspace -= Close;\n\t\tbase.OnClosed(e);\n\t}\n\t\n\tasync void _Install(string folder = null) {\n\t\tfolder ??= _cbFolder.Text.Trim();\n\t\tvar package = _tPackage.Text.Trim();\n\t\t_NormalizeCopiedPackageString(ref package);\n\t\t\n\t\tif (!App.Settings.nuget_noPrerelease) if (!package.RxIsMatch(\"(?i) --version | -v | --prerelease\\b\")) package += \" --prerelease\";\n\t\tif (_cbSource.SelectedItem is _Source source && !package.RxIsMatch(\"(?i) --source | -s \")) package += $\" --source \\\"{source.UrlList}\\\"\";\n\t\t\n\t\tusing var _ = _disabler.Disable();\n\t\tif (!await _InstallWhenInstallingUpdatingOrMoving(package, folder)) return;\n\t\tprint.it(\"========== Finished ==========\");\n\t\tCodeInfo.StopAndUpdateStyling();\n\t}\n\t\n\t/// <param name=\"packageString\">Package name, possibly with --version and other options.</param>\n\tasync Task<bool> _InstallWhenInstallingUpdatingOrMoving(string packageString, string folder, _TreeItem updating = null, bool moving = false) {\n\t\tvar projPath = _ProjPath(folder);\n\t\tvar package = packageString.RxReplace(@\"^\\s*(\\S+).*\", \"$1\");\n\t\t\n\t\tusing var um = new _UndoManager(_FolderPath(folder));\n\t\tif (!um.Init()) return false;\n\t\tum.printError = $\"Failed to {(updating != null ? \"update\" : \"install\")} {package}.\";\n\t\t\n\t\tif (!_CreateProjectFileIfNeed(projPath)) return false;\n\t\t\n\t\tvar sAdd = $@\"add \"\"{projPath}\"\" package {packageString}\";\n\t\t//var sAdd = $@\"package add {packageString} --project \"\"{proj}\"\"\"; //new syntax in .NET SDK 10\n\t\t\n\t\tbool _CanAddPrereleaseOption() => !sAdd.Contains(\" --prerelease\") && !sAdd.Contains(\" --version\");\n\t\t\n\t\tList<string> errorsAndWarnings = [];\n\t\tbool retryPrerelease = false, cancel = false;\n\t\tgRetry1:\n\t\tif (!await _RunDotnet(_Operation.Add, sAdd, errorsAndWarnings: errorsAndWarnings)) {\n\t\t\tretryPrerelease = errorsAndWarnings.Any(o => o.Like(\"error: There are no stable versions*--prerelease*\")) && _CanAddPrereleaseOption();\n\t\t\tif (!retryPrerelease) return false;\n\t\t} else if (errorsAndWarnings.Any(o => o.Starts(\"error:\")) && updating is null) {\n\t\t\tswitch (dialog.show(\"Errors detected\", \"Try to install anyway?\\n\\nMore info in the Output.\", \"1 No|2 Yes\", icon: DIcon.Warning, owner: this)) {\n\t\t\tcase 1: cancel = true; break;\n\t\t\t}\n\t\t} else if (errorsAndWarnings.Any(o => o.Starts(\"warn : NU1701:\")) && updating is null && !moving) {\n\t\t\tvar buttons = \"1 Cancel|2 OK\" + (_CanAddPrereleaseOption() ? \"|3 Retry with option --prerelease\\nMaybe a compatible prerelease version exists...\" : null);\n\t\t\tswitch (dialog.show(\"This package may be incompatible\", \"Install anyway?\\n\\nMore info in the Output.\", buttons, DFlags.CommandLinks, DIcon.Warning, owner: this)) {\n\t\t\tcase 1: cancel = true; break;\n\t\t\tcase 3: retryPrerelease = true; break;\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (retryPrerelease) {\n\t\t\tprint.it(\"info : Added option --prerelease\");\n\t\t\tsAdd += \" --prerelease\";\n\t\t\tgoto gRetry1;\n\t\t}\n\t\tif (cancel) {\n\t\t\tprint.it(\"========== Canceled ==========\");\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\tif (!await _Build(folder, package)) return false;\n\t\tum.success = true;\n\t\t\n\t\t_AddToTreeOrUpdate(folder, package, updating);//TODO: review and test how when failed\n\t\t\n\t\treturn true;\n\t\t\n\t\tbool _CreateProjectFileIfNeed(string path) {\n\t\t\ttry {\n\t\t\t\tstring writeProjText = null;\n\t\t\t\tstring c_targetFramework = $\"<TargetFramework>net{Environment.Version.ToString(2)}-windows</TargetFramework>\";\n\t\t\t\tconst string c_assemblyName = \"<AssemblyName>___</AssemblyName>\"; //without this, usually error if the folder name == a dll name, because the main dll name was the same.\n\t\t\t\tconst string c_allowMissingPacksDir = \"<AllowMissingPrunePackageData>true</AllowMissingPrunePackageData>\"; //.NET 10 SDK requires the `packs` dir by default. The error description says add this if the dir is missing.\n\t\t\t\tif (!filesystem.exists(path, true)) {\n\t\t\t\t\twriteProjText = $\"\"\"\n<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    {c_targetFramework}\n    <UseWPF>true</UseWPF>\n    <UseWindowsForms>true</UseWindowsForms>\n    <ProduceReferenceAssembly>False</ProduceReferenceAssembly>\n    <DebugType>none</DebugType>\n    <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>\n    <NuGetAudit>False</NuGetAudit>\n    {c_assemblyName}\n    {c_allowMissingPacksDir}\n  </PropertyGroup>\n\n  <!-- Copy XML files -->\n  <Target Name=\"_ResolveCopyLocalNuGetPkgXmls\" AfterTargets=\"ResolveReferences\">\n    <ItemGroup>\n      <ReferenceCopyLocalPaths Include=\"@(ReferenceCopyLocalPaths->'%(RootDir)%(Directory)%(Filename).xml')\" Condition=\"'%(ReferenceCopyLocalPaths.NuGetPackageId)'!='' and Exists('%(RootDir)%(Directory)%(Filename).xml')\" />\n    </ItemGroup>\n  </Target>\n\n</Project>\n\"\"\";\n\t\t\t\t\tif (!_folders.Any(o => o.Eqi(folder))) _folders.Add(folder);\n\t\t\t\t} else { //may need to update something\n\t\t\t\t\tstring s = filesystem.loadText(path), s0 = s;\n\t\t\t\t\t\n\t\t\t\t\tif (!s.Contains(c_targetFramework)) s = s.RxReplace(@\"<TargetFramework>.+?</TargetFramework>\", c_targetFramework, 1);\n\t\t\t\t\tif (!s.Contains(c_targetFramework)) s = s.RxReplace(@\"<TargetFramework>.+?</TargetFramework>\", c_targetFramework, 1);\n\t\t\t\t\t\n\t\t\t\t\t_AppendPropIfMissing(c_assemblyName);\n\t\t\t\t\t_AppendPropIfMissing(c_allowMissingPacksDir);\n\t\t\t\t\t\n\t\t\t\t\tvoid _AppendPropIfMissing(string prop) {\n\t\t\t\t\t\tif (!s.Contains(prop) && s.RxMatch(@\"\\R\\h*</PropertyGroup>\", 0, out RXGroup g)) s = s.Insert(g.Start, $\"\\r\\n    {prop}\");\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tif (s != s0) writeProjText = s;\n\t\t\t\t}\n\t\t\t\tif (writeProjText != null) filesystem.saveText(path, writeProjText);\n\t\t\t}\n\t\t\tcatch (Exception e1) {\n\t\t\t\tprint.warning(e1);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Creates temporary backup of current packages folder.\n\t/// Also it allows to update files in use, eg dlls used by script processes.\n\t/// </summary>\n\tclass _UndoManager : IDisposable {\n\t\tstring _folderPath, _undoPath;\n\t\tbool _dirExists, _failedMove;\n\t\t\n\t\tpublic _UndoManager(string folderPath) {\n\t\t\t_folderPath = folderPath;\n\t\t\t_dirExists = filesystem.exists(_folderPath);\n\t\t\t_undoPath = _dirExists ? pathname.makeUnique(_folderPath + \"~old\", true) : null;\n\t\t}\n\t\t\n\t\tpublic bool Init() {\n\t\t\tif (_dirExists) {\n\t\t\t\ttry {\n\t\t\t\t\tfilesystem.move(_folderPath, _undoPath);\n\t\t\t\t}\n\t\t\t\tcatch (Exception ex) { //note: succeeds when a dll file is used, but may fail for other reasons, eg the dir itself is locked\n\t\t\t\t\t//TODO2: then try to copy/delete folder contents; OK if deletes all files but not all empty folders.\n\t\t\t\t\t\n\t\t\t\t\t_failedMove = true;\n\t\t\t\t\tprint.warning(\"Failed to create a temporary backup of the NuGet folder. \" + ex);\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\tfilesystem.createDirectory(_folderPath);\n\t\t\tif (_dirExists) {\n\t\t\t\tforeach (var v in filesystem.enumerate(_undoPath)) {\n\t\t\t\t\tif (v.Attributes.Has(FileAttributes.ReadOnly) /*user-added files*/ || (!v.IsDirectory && v.Name.Ends(\".csproj\", true))) {\n\t\t\t\t\t\tfilesystem.copyTo(v.FullPath, _folderPath);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\tpublic bool success;\n\t\t\n\t\tpublic string printError;\n\t\t\n\t\tpublic void Dispose() {\n\t\t\tif (_failedMove) return;\n\t\t\tif (success) {\n\t\t\t\tif (_dirExists) {\n\t\t\t\t\tif (false == filesystem.delete(_undoPath, FDFlags.CanFail)) {\n\t\t\t\t\t\tvar dir3 = App.Model.TempDirectory + @\"\\delete\"; //will be deleted when loading the workspace next time\n\t\t\t\t\t\tfilesystem.moveTo(_undoPath, dir3, FIfExists.RenameNew);\n\t\t\t\t\t\t\n\t\t\t\t\t\t//If failed, probably some dlls are used by script processes.\n\t\t\t\t\t\t//good: we can update (or delete) locked dlls.\n\t\t\t\t\t\t//bad: these dlls later may try to load updated dlls as dependencies, and may fail if different version. Never mind, it's rare.\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfilesystem.delete(_folderPath);\n\t\t\t\tif (_dirExists) filesystem.move(_undoPath, _folderPath);\n\t\t\t\t\n\t\t\t\tif (printError != null) print.it(printError + $\" No changes have been made in folder {pathname.getName(_folderPath)}.\");\n\t\t\t}\n\t\t}\n\t}\n\t\n\tasync Task<bool> _Build(string folder, string package = null) {\n\t\tvar folderPath = _FolderPath(folder);\n\t\tvar proj = _ProjPath(folder);\n\t\tbool installing = package != null;\n\t\t\n\t\tvar noRestore = installing ? \"--no-restore \" : null; //`package add` restores, `package remove` doesn't\n\t\tvar sBuild = $@\"build \"\"{proj}\"\" {noRestore}--nologo -v m -o \"\"{folderPath}\"\"\";\n\t\tif (!await _RunDotnet(_Operation.Build, sBuild)) return false;\n\t\t\n\t\tif (installing) {\n\t\t\t//we need a list of installed files (managed dll, unmanaged dll, maybe more).\n\t\t\t//\tWhen compiling miniProgram or editorExtension, will need dll paths to resolve at run time.\n\t\t\t//\tWhen compiling exeProgram, will need to copy them to the output directory.\n\t\t\t\n\t\t\t//at first create a copy of the csproj file with only this PackageReference (remove others)\n\t\t\tvar dirProj2 = folderPath + @\"\\single\";\n\t\t\tfilesystem.createDirectory(dirProj2);\n\t\t\tvar proj2 = dirProj2 + @\"\\~.csproj\";\n\t\t\tvar dirBin2 = dirProj2 + @\"\\bin\";\n\t\t\tvar xp = XElement.Load(proj);\n\t\t\tvar axp = xp.XPathSelectElements($\"/ItemGroup/PackageReference[@Include]\").ToArray();\n\t\t\tforeach (var v in axp) if (!v.Attr(\"Include\").Eqi(package)) v.Remove();\n\t\t\txp.Save(proj2);\n\t\t\t\n\t\t\t//then build it, using a temp output directory\n\t\t\tsBuild = $@\"build \"\"{proj2}\"\" --nologo -v m -o \"\"{dirBin2}\"\"\"; //note: no --no-restore\n\t\t\tif (!await _RunDotnet(_Operation.Build, sBuild, s => { })) { //try silent, but print errors if fails (unlikely)\n\t\t\t\tDebug_.Print(\"FAILED\");\n\t\t\t\tif (!await _RunDotnet(_Operation.Build, sBuild)) return false;\n\t\t\t}\n\t\t\t//#if DEBUG\n\t\t\t//run.it(dirBin2);\n\t\t\t//dialog.show(\"Debug\", \"single build done\"); //to inspect files before deleting\n\t\t\t//#endif\n\t\t\t\n\t\t\t//delete runtimes of unsupported OS or CPU. It seems cannot specify it in project file.\n\t\t\t_DeleteOtherRuntimes(folderPath);\n\t\t\t_DeleteOtherRuntimes(dirBin2);\n\t\t\tvoid _DeleteOtherRuntimes(string dir) {\n\t\t\t\tdir += @\"\\runtimes\";\n\t\t\t\tif (filesystem.exists(dir)) {\n\t\t\t\t\tforeach (var v in filesystem.enumDirectories(dir)) {\n\t\t\t\t\t\tvar n = v.Name;\n\t\t\t\t\t\tif (!n.Starts(\"win\", true) || (n.Contains('-') && 0 == n.Ends(true, \"-x64\", \"-x86\", \"-arm64\"))) {\n\t\t\t\t\t\t\tfilesystem.delete(v.FullPath);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t//save relative paths etc of output files in file \"nuget.xml\"\n\t\t\t//\tDon't use ___.deps.json. It contains only used dlls, but may also need other files, eg exe.\n\t\t\t//\tFor testing can be used NuGet package Microsoft.PowerShell.SDK. It has dlls for testing almost all cases.\n\t\t\t\n\t\t\tvar npath = _nugetDir + @\"\\nuget.xml\";\n\t\t\tvar xn = XmlUtil.LoadElemIfExists(npath, \"nuget\");\n\t\t\tvar packagePath = folder + \"\\\\\" + package;\n\t\t\txn.Elem(\"package\", \"path\", packagePath, true)?.Remove();\n\t\t\tvar xx = new XElement(\"package\", new XAttribute(\"path\", packagePath), new XAttribute(\"format\", \"1\"));\n\t\t\txn.AddFirst(xx);\n\t\t\t\n\t\t\tvar dCompile = _GetCompileAssembliesFromAssetsJson(dirProj2 + @\"\\obj\\project.assets.json\", folderPath);\n\t\t\t\n\t\t\t//get lists of .NET dlls, native dlls and other files\n\t\t\tList<(FEFile f, int r)> aDllNet = new(); //r: 0 r (ref and run time), 1 ro (ref only), 2 rt (run time only)\n\t\t\tList<FEFile> aDllNative = new(), aOther = new();\n\t\t\tvar feFlags = FEFlags.AllDescendants | FEFlags.OnlyFiles | FEFlags.UseRawPath | FEFlags.NeedRelativePaths;\n\t\t\tforeach (var f in filesystem.enumFiles(dirBin2, flags: feFlags).OrderBy(o => o.Level)) {\n\t\t\t\tvar s = f.Name; //like @\"\\file\" or @\"\\dir\\file\"\n\t\t\t\tbool runtimes = false;\n\t\t\t\tif (f.Level == 0) {\n\t\t\t\t\tif (s.Starts(@\"\\___.\")) continue;\n\t\t\t\t} else {\n\t\t\t\t\truntimes = s.Starts(@\"\\runtimes\\win\", true);\n\t\t\t\t\tDebug_.PrintIf(!(runtimes || s.Ends(\".resources.dll\") || 0 != s.Starts(false, @\"\\ref\\\", @\"\\.playwright\\\")), s); //ref is used by Microsoft.PowerShell.SDK as data files\n\t\t\t\t}\n\t\t\t\tif (s.Ends(\".dll\", true) && (f.Level == 0 || runtimes)) {\n\t\t\t\t\tif (CompilerUtil.IsNetAssembly(f.FullPath, out bool refOnly)) {\n\t\t\t\t\t\taDllNet.Add((f, refOnly ? 1 : runtimes ? 2 : 0));\n\t\t\t\t\t} else {\n\t\t\t\t\t\taDllNative.Add(f);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\taOther.Add(f);\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t//.NET dlls\n\t\t\tHashSet<string> hsLib = new(StringComparer.OrdinalIgnoreCase);\n\t\t\tforeach (var group in aDllNet.ToLookup(o => pathname.getName(o.f.Name), StringComparer.OrdinalIgnoreCase)) {\n\t\t\t\t//print.it($\"<><lc #BBE3FF>{group.Key}<>\");\n\t\t\t\tvar filename = group.Key;\n\t\t\t\tint count = group.Count();\n\t\t\t\tbool haveRO = dCompile.Remove(filename);\n\t\t\t\tif (haveRO) xx.Add(new XElement(\"ro\", @\"\\_ref\\\" + filename));\n\t\t\t\tXElement xGroup = null;\n\t\t\t\tforeach (var (f, r) in group) {\n\t\t\t\t\tvar s = f.Name; //like @\"\\file\" or @\"\\dir\\file\"\n\t\t\t\t\thsLib.Add(s);\n\t\t\t\t\tbool refOnly = r == 1 || (r == 0 && f.Level == 0 && count > 1); //if count>1, this is X.dll from [X.dll, sub\\X.dll, ...]\n\t\t\t\t\tif (refOnly) {\n\t\t\t\t\t\tif (haveRO) continue;\n\t\t\t\t\t\txx.Add(new XElement(\"ro\", s));\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (r == 2 && s[13] != '\\\\') { //\\runtimes\\win... but not \\runtimes\\win\\...\n\t\t\t\t\t\t\tif (xGroup == null) xx.Add(xGroup = new(\"group\"));\n\t\t\t\t\t\t\txGroup.Add(new XElement(\"rt\", s));\n\t\t\t\t\t\t} else if (!haveRO && r == 0) {\n\t\t\t\t\t\t\txx.Add(new XElement(\"r\", s));\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\txx.Add(new XElement(\"rt\", s));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t//print.it(s, f.Size, refOnly, haveRO);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t//XML tags:\n\t\t\t\t//\t\"r\" - .NET dll used at compile time and run time. Not ref-only.\n\t\t\t\t//\t\"ro\" - .NET dll used only at compile time. Can be ref-only or not.\n\t\t\t\t//\t\"rt\" - .NET dll used only at run time.\n\t\t\t\t//\t\"native\" - unmanaged dll\n\t\t\t\t//\t\"other\" - all other (including dlls in folders other than root and runtimes)\n\t\t\t\t//\t\"group\" - group of \"rt\" dlls. Same dll for different OS versions/platforms.\n\t\t\t\t//\t\"natives\" - group of \"native\" dlls. Same dll for different OS versions/platforms.\n\t\t\t\t//native dlls usually are in \\runtimes\\win-x64\\native\\x.dll etc, but also can be in \\runtimes\\win10-x64\\native\\x.dll etc.\n\t\t\t}\n\t\t\t\n\t\t\tif (dCompile.Any()) { //ref-only dlls that were not copied to dirBin2\n\t\t\t\tforeach (var (k, v) in dCompile) {\n\t\t\t\t\txx.Add(new XElement(\"ro\", @\"\\_ref\\\" + k));\n\t\t\t\t\thsLib.Add(k);\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t//native dlls\n\t\t\tforeach (var group in aDllNative.ToLookup(o => pathname.getName(o.Name), StringComparer.OrdinalIgnoreCase)) {\n\t\t\t\tXElement xGroup = null;\n\t\t\t\tforeach (var f in group) {\n\t\t\t\t\tvar s = f.Name;\n\t\t\t\t\tif (f.Level > 0 && s[13] != '\\\\') {\n\t\t\t\t\t\tif (xGroup == null) xx.Add(xGroup = new(\"natives\"));\n\t\t\t\t\t\txGroup.Add(new XElement(\"native\", s));\n\t\t\t\t\t} else {\n\t\t\t\t\t\txx.Add(new XElement(\"native\", s));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t//print.it(xx);\n\t\t\t\n\t\t\t//other files\n\t\t\tforeach (var f in aOther) {\n\t\t\t\tvar s = f.Name;\n\t\t\t\t\n\t\t\t\t//skip XML doc. When compiling exeProgram, other xml files will be copied to the output.\n\t\t\t\tif (s.Ends(\".xml\", true) && hsLib.Contains(s.ReplaceAt(^3..^1, \"dl\"))) continue;\n\t\t\t\t\n\t\t\t\txx.Add(new XElement(\"other\", s));\n\t\t\t}\n\t\t\t\n\t\t\txn.SaveElem(npath, backup: true);\n\t\t\t\n\t\t\t//finally delete temp files\n\t\t\ttry { filesystem.delete(dirProj2); }\n\t\t\tcatch (Exception e1) { Debug_.Print(e1); }\n\t\t}\n\t\t\n\t\ttry {\n\t\t\tfilesystem.delete($@\"{folderPath}\\___.dll\");\n\t\t\tforeach (var v in Directory.GetFiles(folderPath, \"*.json\")) filesystem.delete(v);\n\t\t\tfilesystem.delete($@\"{folderPath}\\obj\");\n\t\t}\n\t\tcatch (Exception e1) { Debug_.Print(e1); }\n\t\t\n\t\treturn true;\n\t\t\n\t\tstatic Dictionary<string, string> _GetCompileAssembliesFromAssetsJson(string file, string folderPath) {\n\t\t\tstring refDir = null;\n\t\t\tvar hsDotnet = _GetDotnetAssemblies();\n\t\t\tDictionary<string, string> d = new(StringComparer.OrdinalIgnoreCase);\n\t\t\tvar j = JsonNode.Parse(File.ReadAllBytes(file));\n\t\t\tvar packages = j[\"packageFolders\"].AsObject().First().Key;\n\t\t\tforeach (var (nameVersion, v1) in j[\"targets\"].AsObject().First().Value.AsObject()) {\n\t\t\t\tvar k = v1.AsObject();\n\t\t\t\tif (!k.TryGetPropertyValue(\"compile\", out var v2)) continue;\n\t\t\t\tforeach (var (s, _) in v2.AsObject()) {\n\t\t\t\t\tif (s.NE() || s.Ends(\"/_._\")) continue;\n\t\t\t\t\tvar name = pathname.getName(s);\n\t\t\t\t\tif (hsDotnet.Contains(name)) continue;\n\t\t\t\t\tvar path = (packages + nameVersion + \"\\\\\" + s).Replace('/', '\\\\');\n\t\t\t\t\tif (!filesystem.exists(path)) {\n\t\t\t\t\t\tDebug_.Print($\"<c red>{path}<>\");\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (d.TryGetValue(name, out var ppath)) {\n#if DEBUG\n\t\t\t\t\t\tfilesystem.GetProp_(ppath, out var u1);\n\t\t\t\t\t\tfilesystem.GetProp_(path, out var u2);\n\t\t\t\t\t\tif (u2.size != u1.size) Debug_.Print($\"<c orange>\\t{name}<>\\n\\t\\t{u1.size}  {ppath}\\n\\t\\t{u2.size}  {path}\");\n\t\t\t\t\t\tbool e1 = filesystem.exists(ppath.ReplaceAt(^3..^1, \"xm\")), e2 = filesystem.exists(path.ReplaceAt(^3..^1, \"xm\"));\n\t\t\t\t\t\tDebug_.PrintIf(e2 != e1, \"no xml\");\n#endif\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tvar path2 = folderPath + \"\\\\\" + name;\n\t\t\t\t\tif (filesystem.GetProp_(path2, out var p1) && filesystem.GetProp_(path, out var p2) && p1 == p2) continue;\n\t\t\t\t\td.Add(name, path);\n\t\t\t\t\tif (refDir == null) filesystem.createDirectory(refDir = folderPath + @\"\\_ref\");\n\t\t\t\t\tfilesystem.copyTo(path, refDir);\n\t\t\t\t\tvar docPath = path.ReplaceAt(^3..^1, \"xm\");\n\t\t\t\t\tif (filesystem.exists(docPath, useRawPath: true)) filesystem.copyTo(docPath, refDir);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn d;\n\t\t}\n\t\t\n\t\tstatic HashSet<string> _GetDotnetAssemblies() {\n\t\t\tvar s = AppContext.GetData(\"TRUSTED_PLATFORM_ASSEMBLIES\") as string;\n\t\t\tvar a = s.Split(';', StringSplitOptions.RemoveEmptyEntries);\n\t\t\tvar h = new HashSet<string>(a.Length, StringComparer.OrdinalIgnoreCase);\n\t\t\tstring net1 = folders.NetRuntimeBS, net2 = folders.NetRuntimeDesktopBS;\n\t\t\tforeach (var v in a) {\n\t\t\t\tif (v.Starts(net1, true) || v.Starts(net2, true)) h.Add(pathname.getName(v));\n\t\t\t}\n\t\t\treturn h;\n\t\t}\n\t}\n\t\n\tasync void _Uninstall() {\n\t\tusing var _ = _disabler.Disable();\n\t\tif (!await _UninstallWhenUninstallingOrMoving(_Selected)) return;\n\t\tprint.it(\"========== Finished ==========\");\n\t\tCodeInfo.StopAndUpdateStyling();\n\t}\n\t\n\tasync Task<bool> _UninstallWhenUninstallingOrMoving(_TreeItem t) {\n\t\tvar folder = t.Parent.Name;\n\t\tvar package = t.Name;\n\t\t//if (uninstalling) if (!dialog.showOkCancel(\"Uninstall package\", package, owner: this)) return; //more annoying than useful\n\t\t\n\t\tusing var um = new _UndoManager(_FolderPath(folder));\n\t\tif (!um.Init()) return false;\n\t\tum.printError = $\"Failed to uninstall {package}.\";\n\t\t\n\t\tif (!await _RunDotnet(_Operation.Other, $@\"remove \"\"{_ProjPath(folder)}\"\" package {package}\")) return false;\n\t\t//if (!await _RunDotnet($@\"package remove {package} --project \"\"{_ProjPath()}\"\"\")) return false; //new syntax in .NET SDK 10\n\t\t\n\t\tif (!await _Build(folder)) return false;\n\t\tum.success = true;\n\t\t\n\t\tvar npath = _nugetDir + @\"\\nuget.xml\";\n\t\tif (filesystem.exists(npath)) {\n\t\t\tvar xn = XmlUtil.LoadElem(npath);\n\t\t\tvar xx = xn.Elem(\"package\", \"path\", folder + \"\\\\\" + package, true);\n\t\t\tif (xx != null) {\n\t\t\t\txx.Remove();\n\t\t\t\txn.SaveElem(npath);\n\t\t\t}\n\t\t}\n\t\t\n\t\tt.Remove();\n\t\t_tv.SetItems(_tvroot.Children(), true);\n\t\tif (_Selected is null) _panelManage.IsEnabled = false;\n\t\t\n\t\treturn true;\n\t}\n\t\n\tasync void _Update(bool showVersionsMenu) {\n\t\tvar t = _Selected;\n\t\tvar s = t.Name;\n\t\t\n\t\tif (showVersionsMenu) {\n\t\t\tif (await _GetVersions(t) is not { } versions) return;\n\t\t\tint i = popupMenu.showSimple(versions, owner: this, rawText: true) - 1; if (i < 0) return;\n\t\t\ts = $\"{s} --version {versions[i]}\";\n\t\t} else {\n\t\t\tif (!App.Settings.nuget_noPrerelease || t.Version.Contains('-')) s += \" --prerelease\";\n\t\t}\n\t\tif (t.Source is { } so) s += $\" --source \\\"{so.UrlList}\\\"\";\n\t\t\n\t\tusing var _ = _disabler.Disable();\n\t\tif (!await _InstallWhenInstallingUpdatingOrMoving(s, t.Parent.Name, t)) return;\n\t\tprint.it(\"========== Finished ==========\");\n\t\tCodeInfo.StopAndUpdateStyling();\n\t}\n\t\n\tasync void _Move() {\n\t\tvar t = _Selected;\n\t\t\n\t\tvar otherFolders = _folders.ToList();\n\t\tif (_cbFolder.Text.Trim() is var newFolder && !pathname.isInvalidName(newFolder) && !otherFolders.Contains(newFolder, StringComparer.OrdinalIgnoreCase)) otherFolders.Add(newFolder);\n\t\tif (otherFolders.FindIndex(o => o.Eqi(t.Parent.Name)) is int i1 && i1 >= 0) otherFolders.RemoveAt(i1);\n\t\t\n\t\tint i = popupMenu.showSimple(otherFolders, owner: this, rawText: true) - 1; if (i < 0) return;\n\t\tvar folder = otherFolders[i];\n\t\t\n\t\t//can't move to a folder that already contains a package with this name\n\t\tforeach (var f in _tvroot.Children()) {\n\t\t\tif (f.Name == folder) {\n\t\t\t\tif (f.Children().Any(o => o.Name.Eqi(t.Name))) {\n\t\t\t\t\tprint.it(\"Can't move to a folder that contains a package with this name.\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\t\n\t\tvar s = $\"{t.Name} --version {t.Version}\";\n\t\tif (t.Source is { } so) s += $\" --source \\\"{so.UrlList}\\\"\";\n\t\t\n\t\tusing var _ = _disabler.Disable();\n\t\tif (!await _InstallWhenInstallingUpdatingOrMoving(s, folder, moving: true)) return;\n\t\tawait _UninstallWhenUninstallingOrMoving(t);\n\t\tprint.it(\"========== Finished ==========\");\n\t\tCodeInfo.StopAndUpdateStyling();\n\t}\n\t\n\tvoid _AddMeta() {\n\t\tvar doc = Panels.Editor.ActiveDoc; if (doc == null) return;\n\t\tvar meta = new MetaCommentsParser(doc.aaaText);\n\t\tvar t = _Selected;\n\t\tmeta.nuget.Add($@\"{t.Parent.Name}\\{t.Name}\");\n\t\tmeta.Apply();\n\t}\n\t\n\tpublic static string[] GetInstalledPackages() {\n\t\tvar xn = XmlUtil.LoadElemIfExists(App.Model.NugetDirectoryBS + \"nuget.xml\");\n\t\tif (xn == null) return null;\n\t\tvar a = xn.Elements(\"package\").Select(o => o.Attr(\"path\")).ToArray();\n\t\tif (!a.Any()) return null;\n\t\tArray.Sort(a, StringComparer.OrdinalIgnoreCase);\n\t\treturn a;\n\t}\n\t\n\tvoid _Menu() {\n\t\tvar m = new popupMenu();\n\t\tm.AddCheck(\"Avoid prerelease versions\", App.Settings.nuget_noPrerelease, o => { App.Settings.nuget_noPrerelease = o.IsChecked; });\n\t\tm.Last.Tooltip = \"When installing or updating, if version not specified, prefer the latest stable version\";\n\t\tm.Submenu(\"nuget.config\", m => {\n\t\t\tforeach (var v in _Config.GetConfigFilePaths()) m[v.Limit(150, middle: true)] = o => { run.selectInExplorer(v); };\n\t\t});\n\t\tm.Submenu(\"NuGet cache\", m => {\n\t\t\tm[\"Open packages folder\"] = o => { if (_GetCacheDir() is { } s) run.itSafe(s); };\n\t\t\tm.Submenu(\"Clear all caches\", m => {\n\t\t\t\tm[\"Clear\"] = o => { _ = _RunDotnet(_Operation.Other, \"nuget locals all --clear\"); };\n\t\t\t});\n\t\t});\n\t\tm.Show();\n\t}\n\t\n\tvoid _tv_ItemClick(TVItemEventArgs e) {\n\t\tif (e.Button != MouseButton.Right) return;\n\t\tvar t = e.Item as _TreeItem;\n\t\t_tv.Select(t, focus: true);\n\t\tif (t.IsFolder) {\n\t\t\tvar path = _FolderPath(t.Name);\n\t\t\tvar m = new popupMenu();\n\t\t\tm[\"Install into this folder\", disable: _tPackage.Text.Trim().NE()] = o => { _Install(t.Name); };\n\t\t\tm[\"Rename folder\"] = o => { _tv.EditLabel(t); };\n\t\t\tm[\"Delete unused folder\", disable: !_CanDeleteFolder()] = o => {\n\t\t\t\tif (false == filesystem.delete(path, FDFlags.CanFail)) return;\n\t\t\t\tt.Remove();\n\t\t\t\t_tv.SetItems(_tvroot.Children(), true);\n\t\t\t\t_folders.Remove(t.Name);\n\t\t\t};\n\t\t\tm.Show();\n\t\t\t\n\t\t\tbool _CanDeleteFolder() {\n\t\t\t\tif (t.HasChildren) return false;\n\t\t\t\tif (!filesystem.exists(path).Directory) return false;\n\t\t\t\tvar a = filesystem.enumerate(path, FEFlags.UseRawPath).ToArray();\n\t\t\t\tif (a.Length > 1 || (a.Length == 1 && (a[0].IsDirectory || !a[0].Name.Ends(\".csproj\", true)))) return false;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\t\n\tenum _Operation { Add, Build, Other }\n\t\n\tasync Task<bool> _RunDotnet(_Operation op, string cl, Action<string> printer = null, List<string> errorsAndWarnings = null) {\n\t\terrorsAndWarnings?.Clear();\n\t\ttry {\n\t\t\tif (printer == null) {\n\t\t\t\tvar clPrint = cl.RxReplace(@\" \"\"[^\"\"]+\\.csproj\"\"\", \"\", 1).Replace(\" --nologo -v m\", \"\"); //try to print shorter command line\n\t\t\t\tprint.it($\"<><c blue>dotnet {clPrint}<>\");\n\t\t\t\tbool skip = false;\n\t\t\t\tprinter = s => {\n\t\t\t\t\tif (!skip) skip = s.Starts(\"Usage:\");\n\t\t\t\t\tif (skip) return;\n\t\t\t\t\tif (s.Starts(\"error\") || s.Contains(\": error \")) {\n\t\t\t\t\t\terrorsAndWarnings?.Add(s);\n\t\t\t\t\t\ts = $\"<><c red>{s}<>\";\n\t\t\t\t\t} else if (s.Starts(\"warn\") || s.Contains(\": warning \")) {\n\t\t\t\t\t\terrorsAndWarnings?.Add(s);\n\t\t\t\t\t\ts = $\"<><c DarkOrange>{s}<>\";\n\t\t\t\t\t} else if (op == _Operation.Add) {\n\t\t\t\t\t\tif (s.Like(false,\n\t\t\t\t\t\t\t\"info :   OK http*\",\n\t\t\t\t\t\t\t\"info : Generating MSBuild file*\",\n\t\t\t\t\t\t\t\"info : Writing assets file*\",\n\t\t\t\t\t\t\t\"info : X.* certificate chain validation will*\",\n\t\t\t\t\t\t\t\"  Determining projects to restore*\",\n\t\t\t\t\t\t\t\"log  : Restored*\",\n\t\t\t\t\t\t\t\"  Writing *\",\n\t\t\t\t\t\t\t\"info : PackageReference for package*\",\n\t\t\t\t\t\t\t\"*is compatible with all the specified frameworks in project*\"\n\t\t\t\t\t\t\t) > 0) s = null;\n\t\t\t\t\t} else if (op == _Operation.Build) {\n\t\t\t\t\t\tif (s.Like(false,\n\t\t\t\t\t\t\t\"  Determining projects to restore*\",\n\t\t\t\t\t\t\t\"Time Elapsed *\",\n\t\t\t\t\t\t\t\"    0 Warning*\",\n\t\t\t\t\t\t\t\"    0 Error*\",\n\t\t\t\t\t\t\t@\"*\\___.dll\"\n\t\t\t\t\t\t\t) > 0) s = null;\n\t\t\t\t\t}\n\t\t\t\t\tif (!s.NE()) print.it(s);\n\t\t\t\t};\n\t\t\t}\n\t\t\t\n\t\t\treturn await Task.Run(() => 0 == run.console(printer, DotnetUtil.DotnetExe, cl));\n\t\t}\n\t\tcatch (Exception e1) {\n\t\t\tvar s = e1.ToStringWithoutStack();\n\t\t\tdialog.showError(\"Failed to run dotnet.exe\", s, owner: this);\n\t\t}\n\t\treturn false;\n\t}\n\t\n\t/// <summary>\n\t/// Gets our <b>NGC.ISettings</b>.\n\t/// </summary>\n\t/// <exception cref=\"Exception\"></exception>\n\tNGC.ISettings _Config => field ??= NGC.Settings.LoadDefaultSettings(_nugetDir);\n\t\n\tstring _GetCacheDir() {\n\t\ttry { return NGC.SettingsUtility.GetGlobalPackagesFolder(_Config)?.TrimEnd('\\\\'); }\n\t\tcatch { return null; }\n\t}\n\t\n\tclass _Source {\n\t\tpublic _Source() {\n\t\t\tUrl = UrlList = \"https://api.nuget.org/v3/index.json\";\n\t\t\tName = \"nuget.org\";\n\t\t\tPackageBaseUrl = \"https://api.nuget.org/v3-flatcontainer/\";\n\t\t\t_packageBaseUrlInited = true;\n\t\t}\n\t\t\n\t\tpublic _Source(NGC.PackageSource ps) {\n\t\t\tUrl = UrlList = ps.Source;\n\t\t\tName = ps.Name;\n\t\t\tIsLocal = ps.IsLocal;\n\t\t\tif (ps.Credentials is { } cred) _auth = $\"{cred.Username}:{cred.Password}\"; //tested: auto-decrypts password\n\t\t}\n\t\t\n\t\tpublic _Source(string url) {\n\t\t\tUrl = UrlList = Name = url;\n\t\t\tIsLocal = !url.Starts(\"http\", true);\n\t\t}\n\t\t\n\t\tpublic string Url { get; }\n\t\tpublic string Name { get; }\n\t\tpublic bool IsLocal { get; }\n\t\treadonly string _auth;\n\t\tpublic override string ToString() => Name;\n\t\t\n\t\t/// <summary>\n\t\t/// Gets the base URL of packages, or null if local or <see cref=\"InitPackageBaseUrl\"/> not called or failed.\n\t\t/// </summary>\n\t\tpublic string PackageBaseUrl { get; private set; }\n\t\t\n\t\t/// <summary>\n\t\t/// List of URLs for <c>dotnet add package --sources</c>, like <c>\"Url;other_url\"</c>.\n\t\t/// </summary>\n\t\tpublic string UrlList { get; set; }\n\t\t\n\t\t/// <summary>\n\t\t/// Downloads <c>index.json</c> of the source and gets the base URL of packages.\n\t\t/// Does nothing if local or already called.\n\t\t/// Not thread-safe.\n\t\t/// </summary>\n\t\tpublic void InitPackageBaseUrl() {\n\t\t\tif (_packageBaseUrlInited || IsLocal) return;\n\t\t\ttry {\n\t\t\t\tif (!Url.Ends(\".json\", true)) return;\n\t\t\t\tvar j = internet.http.Get(Url, auth: _auth).Json();\n\t\t\t\tvar ver = (string)j[\"version\"];\n\t\t\t\tif (!ver.Starts(\"3.0.\")) { Debug_.Print(ver); return; }\n\t\t\t\tforeach (var v in j[\"resources\"].AsArray()) {\n\t\t\t\t\tif (((string)v[\"@type\"])?.Starts(\"PackageBaseAddress/3.0.\") == true) {\n\t\t\t\t\t\tif ((string)v[\"@id\"] is string s1) {\n\t\t\t\t\t\t\tif (!s1.Ends('/')) s1 += \"/\";\n\t\t\t\t\t\t\tPackageBaseUrl = s1;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception ex) { Debug_.Print(ex); }\n\t\t\tfinally { _packageBaseUrlInited = true; }\n\t\t}\n\t\tbool _packageBaseUrlInited;\n\t\t\n\t\t/// <summary>\n\t\t/// Downloads <c>index.json</c> of a package.\n\t\t/// </summary>\n\t\t/// <returns>null if local or failed or <see cref=\"InitPackageBaseUrl\"/> not called or failed.</returns>\n\t\tpublic JsonNode GetPackageIndexJson(string package) {\n\t\t\tif (PackageBaseUrl is string pbu) {\n\t\t\t\tvar url = $\"{pbu}{package.Lower()}/index.json\";\n\t\t\t\ttry {\n\t\t\t\t\tvar r = internet.http.Get(url, auth: _auth);\n\t\t\t\t\tif (r.IsSuccessStatusCode) return r.Json();\n\t\t\t\t\tDebug_.PrintIf(r.StatusCode != System.Net.HttpStatusCode.NotFound, r);\n\t\t\t\t}\n\t\t\t\tcatch (Exception ex) { Debug_.Print(ex); }\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\t\n\t\tpublic string[] GetPackageVersions(string package) {\n\t\t\tif (GetPackageIndexJson(package) is JsonNode j) {\n\t\t\t\tif (j[\"versions\"]?.AsArray() is { } versions) {\n\t\t\t\t\tvar a = versions.Select(o => (string)o).ToArray();\n\t\t\t\t\tif (a.Length > 1 && NuGet.Versioning.NuGetVersion.Parse(a[0]) > NuGet.Versioning.NuGetVersion.Parse(a[^1])) Array.Reverse(a); //eg in GitHub Packages the list is in reverse order than in nuget.org\n\t\t\t\t\treturn a;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\t\n\t\tpublic byte[] GetPackageIcon(_TreeItem t) {\n\t\t\tif (PackageBaseUrl is string pbu) {\n\t\t\t\tvar url = $\"{pbu}{t.Name.Lower()}/{t.Version.Lower()}/icon\";\n\t\t\t\ttry {\n\t\t\t\t\tvar r = internet.http.Get(url, auth: _auth);\n\t\t\t\t\tif (r.IsSuccessStatusCode) return r.Bytes();\n\t\t\t\t\tif (r.StatusCode == System.Net.HttpStatusCode.NotFound) return [];\n\t\t\t\t\tDebug_.Print(r);\n\t\t\t\t}\n\t\t\t\tcatch (Exception ex) { Debug_.Print(ex); }\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t}\n\t\n\tvoid _InitSources() {\n\t\t_sources = [new()]; //nuget.org\n\t\t_dSources = new(StringComparer.OrdinalIgnoreCase) { { _sources[0].Url, _sources[0] } };\n\t\tint nNonlocal = 0;\n\t\ttry {\n\t\t\tvar provider = new NGC.PackageSourceProvider(_Config);\n\t\t\tforeach (var v in provider.LoadPackageSources()) {\n\t\t\t\tif (!v.IsEnabled) continue;\n\t\t\t\tif (v.Source == _sources[0].Url) continue;\n\t\t\t\t_Source so = new(v);\n\t\t\t\tif (!_dSources.TryAdd(so.Url, so)) continue; //note: no warning\n\t\t\t\t_sources.Add(so);\n\t\t\t\tif (!v.IsLocal) nNonlocal++;\n\t\t\t}\n\t\t}\n\t\tcatch (Exception ex) { Debug_.Print(ex); }\n\t\t\n\t\tif (nNonlocal > 0) { //create UrlList for each\n\t\t\tvar b = new StringBuilder();\n\t\t\tforeach (var so in _sources.Skip(1)) {\n\t\t\t\tif (so.IsLocal) continue;\n\t\t\t\tb.Clear();\n\t\t\t\tb.Append(so.Url);\n\t\t\t\tforeach (var k in _sources) if (k != so && !k.IsLocal) b.Append(';').Append(k.Url);\n\t\t\t\tso.UrlList = b.ToString();\n\t\t\t}\n\t\t}\n\t\t\n\t\tforeach (var v in _sources) _cbSource.Items.Add(v);\n\t\t_cbSource.Items.Add(\"Try all listed sources\");\n\t\t_cbSource.SelectedIndex = 0;\n\t}\n\t\n\tList<_Source> _sources;\n\tDictionary<string, _Source> _dSources;\n\t\n\t_Source _SourceFromUrl(string url) {\n\t\tif (_sources is null) return null; //SDK not installed\n\t\tif (url.NE()) return null;\n\t\tif (_dSources.TryGetValue(url, out var r)) return r;\n\t\tDebug_.Print(url);\n\t\treturn new(url);\n\t}\n\t\n\tasync Task<string[]> _GetVersions(_TreeItem t) {\n\t\tList<_Source> sources;\n\t\tif (t.Source is { } so) {\n\t\t\tif (so.IsLocal) {\n\t\t\t\tdialog.show(\"Not supported\", \"Cannot get package versions from local sources.\\nSource: \" + so.Name, owner: this);\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tsources = [so];\n\t\t} else {\n\t\t\tsources = _sources;\n\t\t}\n\t\tvar r = await Task.Run(() => {\n\t\t\tforeach (var source in sources) {\n\t\t\t\tsource.InitPackageBaseUrl();\n\t\t\t\tif (source.GetPackageVersions(t.Name) is { } a) {\n\t\t\t\t\tArray.Reverse(a);\n\t\t\t\t\t//remove prereleases older than the latest stable version\n\t\t\t\t\tint i = 0; while (i < a.Length) if (!a[i++].Contains('-')) break;\n\t\t\t\t\tfor (; i < a.Length; i++) if (a[i].Contains('-')) a[i] = null;\n\t\t\t\t\treturn a.Where(o => o != null).ToArray();\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t});\n\t\treturn r;\n\t}\n\t\n\tasync void _CheckForUpdates() {\n\t\tusing var _ = _disabler.Disable();\n\t\t_tStatus.Text = \"Checking for updates...\";\n\t\t\n\t\tvar a = _tvroot.Descendants().Where(o => o.Level == 2).ToArray();\n\t\tint nStable = 0, nPrerelease = 0;\n\t\t\n\t\tawait Task.Run(() => {\n\t\t\tforeach (var v in _sources) v.InitPackageBaseUrl();\n\t\t\t\n\t\t\tParallel.ForEach(a, t => {\n\t\t\t\tt.Updates = null;\n\t\t\t\ttry {\n#if true\n\t\t\t\t\tstring[] versions = null;\n\t\t\t\t\tif (t.Source is { } so) {\n\t\t\t\t\t\tversions = so.GetPackageVersions(t.Name);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tforeach (var v in _sources) {\n\t\t\t\t\t\t\tversions = v.GetPackageVersions(t.Name);\n\t\t\t\t\t\t\tif (versions != null) break;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n#else\n\t\t\t\t\tvar so = t.Source ?? _sources[0];\n\t\t\t\t\tvar versions = so.GetPackageVersions(t.Name);\n#endif\n\t\t\t\t\tif (versions.NE_()) return;\n\t\t\t\t\t\n\t\t\t\t\tstring latest = versions[^1], latestPrerelease = null, s;\n\t\t\t\t\tif (latest.Contains('-')) {\n\t\t\t\t\t\tlatestPrerelease = latest;\n\t\t\t\t\t\tlatest = versions.LastOrDefault(o => !o.Contains('-'));\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tif (latestPrerelease != null) {\n\t\t\t\t\t\tif (t.Version == latestPrerelease) return;\n\t\t\t\t\t\tbool onlyPR = latest is null || t.Version == latest;\n\t\t\t\t\t\ts = onlyPR ? latestPrerelease : $\"{latest},  {latestPrerelease}\";\n\t\t\t\t\t\tnPrerelease++; if (!onlyPR) nStable++;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (t.Version == latest) return;\n\t\t\t\t\t\ts = latest;\n\t\t\t\t\t\tnStable++;\n\t\t\t\t\t}\n\t\t\t\t\tt.Updates = s;\n\t\t\t\t}\n\t\t\t\tcatch (Exception ex) {\n\t\t\t\t\tDebug_.Print($\"Failed to check {t.Name}: {ex}\");\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\t\t\n\t\twpfBuilder.formatTextOf(_tStatus, $\"Updates: <s c='green'>{nStable} stable</s>, <s c='blue'>{nPrerelease} prerelease</s>\");\n\t\t_tv.Redraw(remeasure: true);\n\t}\n\t\n\tstatic bool _NormalizeCopiedPackageString(ref string s) {\n\t\tif (s.Starts(true, \"dotnet add package \", \"dotnet package add \") > 0) s = s[19..];\n\t\telse if (s.RxReplace(@\"^.+?\\bInstall-Package \", \"\", out s) > 0) s = s.Replace(\"-Version \", \"--version \");\n\t\telse return false;\n\t\treturn true;\n\t}\n\t\n\tvoid _PackageFieldTextChanged() {\n\t\t_TreeItem select = null;\n\t\tvar s = _tPackage.Text;\n\t\tif (s.Length > 1) {\n\t\t\tif (_NormalizeCopiedPackageString(ref s)) { _tPackage.Text = s; return; } //calls this func again when text changed\n\t\t\tint i = s.IndexOf(' ');\n\t\t\tif (i > 0) s = s[..i];\n\t\t\tselect = _tvroot.Descendants().FirstOrDefault(o => !o.IsFolder && o.Name.Find(s, true) >= 0);\n\t\t}\n\t\t\n\t\tif (select != null) _tv.SelectSingle(select);\n\t\telse _tv.UnselectAll();\n\t}\n\t\n\tbool _RenameFolder(_TreeItem t, string name) {\n\t\tif (name != t.Name) {\n\t\t\ttry {\n\t\t\t\tvar dir = _FolderPath(t.Name);\n\t\t\t\tfilesystem.createDirectory(dir); //ensure exists\n\t\t\t\tfilesystem.rename(dir, name);\n\t\t\t\t_folders[_folders.IndexOf(t.Name)] = name;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tcatch (Exception ex) { print.it(ex); }\n\t\t}\n\t\treturn false;\n\t}\n\t\n\t_TreeItem _Selected => _tv.SelectedItem as _TreeItem;\n\t\n\tstring _FolderPath(string folder) => $@\"{_nugetDir}\\{folder}\";\n\t\n\tstring _ProjPath(string folder) => $@\"{_nugetDir}\\{folder}\\{folder}.csproj\";\n\t\n\tXElement _LoadProject(string folder) {\n\t\tvar path = _ProjPath(folder);\n\t\tif (filesystem.exists(path))\n\t\t\ttry { return XElement.Load(path); }\n\t\t\tcatch (Exception ex) { print.warning(ex); }\n\t\treturn null;\n\t}\n\t\n\tIEnumerable<(string name, string version, string source)> _GetProjectPackages(XElement xr) {\n\t\tforeach (var x in xr.XPathSelectElements(\"/ItemGroup/PackageReference[@Include]\")) {\n\t\t\tyield return (x.Attr(\"Include\"), x.Attr(\"Version\"), x.Attr(\"la-source\"));\n\t\t}\n\t}\n\t\n\t(XElement x, string name, string version, string source) _GetProjectPackage(XElement xr, string name) {\n\t\tforeach (var x in xr.XPathSelectElements(\"/ItemGroup/PackageReference[@Include]\")) {\n\t\t\tstring s = x.Attr(\"Include\");\n\t\t\tif (s.Eqi(name)) return (x, s, x.Attr(\"Version\"), x.Attr(\"la-source\"));\n\t\t}\n\t\treturn default;\n\t}\n\t\n\tvoid _FillTree() {\n\t\tList<_TreeItem> a = [];\n\t\t_tvroot = new(this, null);\n\t\tforeach (var folder in _folders) {\n\t\t\tvar k = new _TreeItem(this, folder);\n\t\t\tif (_LoadProject(folder) is { } xr) {\n\t\t\t\tforeach (var (name, version, source) in _GetProjectPackages(xr)) {\n\t\t\t\t\tvar t = new _TreeItem(this, name, version, _SourceFromUrl(source));\n\t\t\t\t\tk.AddChild(t);\n\t\t\t\t\ta.Add(t);\n\t\t\t\t}\n\t\t\t}\n\t\t\t_tvroot.AddChild(k);\n\t\t}\n\t\t_tv.SetItems(_tvroot.Children());\n\t\tif (_sources != null && a.Count > 0) _DisplayIcons(a, useCache: true); //if SDK installed\n\t}\n\t//CONSIDER: display transitive packages too, as child nodes.\n\t\n\tasync void _DisplayIcons(List<_TreeItem> a, bool useCache) {\n\t\tconst string c_defaultIcon = \"*SimpleIcons.NuGet #55A7DD\";\n\t\tvar cacheDir = folders.ThisAppDataLocal + \"nugetIcons\";\n\t\t\n\t\ttry {\n\t\t\tif (filesystem.exists(cacheDir, true).Directory) {\n\t\t\t\tif (useCache) {\n\t\t\t\t\tvar df = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);\n\t\t\t\t\tforeach (var v in filesystem.enumFiles(cacheDir, \"**m *.png||*.jpg||*.ico||*.a\")) df[pathname.getNameNoExt(v.Name)] = v.FullPath;\n\t\t\t\t\tfor (int i = a.Count; --i >= 0;) {\n\t\t\t\t\t\tif (df.TryGetValue(a[i].Name, out string file)) {\n\t\t\t\t\t\t\tvar ext = pathname.getExtension(file);\n\t\t\t\t\t\t\ta[i].Icon = ext.Eqi(\".a\") ? c_defaultIcon : ext.Eqi(\".ico\") ? file : \"imagefile:\" + file;\n\t\t\t\t\t\t\ta.RemoveAt(i);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfilesystem.createDirectory(cacheDir);\n\t\t\t}\n\t\t}\n\t\tcatch { return; }\n\t\t\n\t\tif (a.Count > 0) await Task.Run(_GetIcons);\n\t\t_tv.Redraw();\n\t\t\n\t\tvoid _GetIcons() {\n\t\t\tforeach (var v in _sources) v.InitPackageBaseUrl();\n\t\t\t\n\t\t\tParallel.ForEach(a, t => {\n\t\t\t\ttry {\n\t\t\t\t\tt.Icon = c_defaultIcon;\n\t\t\t\t\tvar so = t.Source ?? _sources[0];\n\t\t\t\t\tif (so.GetPackageIcon(t) is { } bytes) {\n\t\t\t\t\t\tvar fileType = bytes switch { //note: Content-Type unreliable, eg sometimes \"application/octet-stream\"\n\t\t\t\t\t\t\t[0x89, 0x50, 0x4E, 0x47, ..] => \".png\",\n\t\t\t\t\t\t\t[0xFF, 0xD8, 0xFF, ..] => \".jpg\",\n\t\t\t\t\t\t\t[0, 0, 1, 0, ..] => \".ico\",\n\t\t\t\t\t\t\t_ => \".a\" //no icon or unsupported filetype. Why \".a\": if will be \"file.a\" and \"file.png\", the `df[pathname.getNameNoExt(v.Name)] = v.FullPath` will choose \"file.png\".\n\t\t\t\t\t\t};\n\t\t\t\t\t\tvar file = $@\"{cacheDir}\\{t.Name}{fileType}\";\n\t\t\t\t\t\tfilesystem.saveBytes(file, bytes);\n\t\t\t\t\t\tif (fileType != \".a\") t.Icon = fileType == \".ico\" ? file : \"imagefile:\" + file;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (Exception ex) {\n\t\t\t\t\tDebug_.Print($\"Failed to get icon of {t.Name}: {ex}\");\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t}\n\t\n\tvoid _AddToTreeOrUpdate(string folder, string package, _TreeItem updating) {\n\t\tif (_LoadProject(folder) is not { } xr) return;\n\t\tvar pr = _GetProjectPackage(xr, package);\n\t\t\n\t\t//from nuget cache get source and correct-cased name\n\t\tif (_GetCacheDir() is string dir) {\n\t\t\tdir = $@\"{dir}\\{package}\\{pr.version}\\\";\n\t\t\tstring name0 = pr.name, source0 = pr.source;\n\t\t\ttry { pr.source = (string)JsonNode.Parse(filesystem.loadText(dir + \".nupkg.metadata\"))[\"source\"]; } catch { }\n\t\t\ttry { pr.name = XmlUtil.LoadElem(out var ns, dir + package + \".nuspec\").Element(ns + \"metadata\").Element(ns + \"id\").Value; } catch { }\n\t\t\tif (pr.name != name0 || pr.source != source0) {\n\t\t\t\tif (pr.name != name0) pr.x.SetAttributeValue(\"Include\", pr.name);\n\t\t\t\tif (pr.source != source0) pr.x.SetAttributeValue(\"la-source\", pr.source);\n\t\t\t\ttry { xr.SaveElem(_ProjPath(folder)); } catch { }\n\t\t\t}\n\t\t}\n\t\t\n\t\tbool isNew = false;\n\t\t_TreeItem t = updating;\n\t\tif (t is null) {\n\t\t\tvar tFolder = _tvroot.Children().FirstOrDefault(o => o.Name == folder);\n\t\t\tif (tFolder is null) {\n\t\t\t\ttFolder = new(this, folder);\n\t\t\t\tvar tBefore = _tvroot.Children().FirstOrDefault(o => o.Name.CompareTo(folder) > 0);\n\t\t\t\tif (tBefore != null) tBefore.AddSibling(tFolder, false); else _tvroot.AddChild(tFolder);\n\t\t\t}\n\t\t\tt = tFolder.Children().FirstOrDefault(o => o.Name.Eqi(pr.name));\n\t\t\tif (isNew = t is null) {\n\t\t\t\tt = new(this, pr.name, pr.version, _SourceFromUrl(pr.source));\n\t\t\t\ttFolder.AddChild(t);\n\t\t\t\t_tv.SetItems(_tvroot.Children(), true);\n\t\t\t}\n\t\t}\n\t\tif (!isNew) {\n\t\t\tt.Update(pr.name, pr.version, _SourceFromUrl(pr.source));\n\t\t\t_tv.Redraw(t, true);\n\t\t}\n\t\t_tv.SelectSingle(t);\n\t\t_DisplayIcons([t], useCache: false);\n\t}\n\t\n\t_TreeItem _tvroot;\n\t\n\tclass _TreeItem : TreeBase<_TreeItem>, ITreeViewItem {\n\t\tDNuget _d;\n\t\tbool _isExpanded;\n\t\t\n\t\tpublic _TreeItem(DNuget d, string folder) {\n\t\t\t_d = d;\n\t\t\tIsFolder = _isExpanded = true;\n\t\t\tName = NameVersion = folder;\n\t\t}\n\t\t\n\t\tpublic _TreeItem(DNuget d, string name, string version, _Source source) {\n\t\t\t_d = d;\n\t\t\tUpdate(name, version, source);\n\t\t}\n\t\t\n\t\tpublic string Name { get; private set; }\n\t\tpublic string Version { get; private set; }\n\t\tpublic _Source Source { get; private set; }\n\t\tpublic string NameVersion { get; private set; }\n\t\tpublic string Updates { get; set; }\n\t\tpublic object Icon { get; set; }\n\t\tpublic override string ToString() => NameVersion;\n\t\t\n\t\tpublic void Update(string name, string version, _Source source) {\n\t\t\tName = name;\n\t\t\tVersion = version;\n\t\t\tSource = source;\n\t\t\tNameVersion = version == null ? name : $\"{name} {version}\";\n\t\t\tUpdates = null;\n\t\t}\n\t\t\n\t\t#region ITreeViewItem\n\t\t\n\t\tvoid ITreeViewItem.SetIsExpanded(bool yes) { _isExpanded = yes; }\n\t\tbool ITreeViewItem.IsExpanded => _isExpanded;\n\t\tIEnumerable<ITreeViewItem> ITreeViewItem.Items => base.Children();\n\t\tpublic bool IsFolder { get; }\n\t\tstring ITreeViewItem.DisplayText => Updates is null ? NameVersion : NameVersion + \"  ->  \" + Updates;\n\t\tobject ITreeViewItem.Image => IsFolder ? EdIcons.FolderIcon(_isExpanded) : Icon;\n\t\t\n\t\tint ITreeViewItem.TextColor(TVColorInfo ci) {\n\t\t\tif (Updates is string s) {\n\t\t\t\tif (s.Contains('-') && !s.Contains(\", \")) return 0xFF;\n\t\t\t\treturn 0x9000;\n\t\t\t}\n\t\t\treturn -1;\n\t\t}\n\t\t\n\t\tvoid ITreeViewItem.SetNewText(string text) { if (_d._RenameFolder(this, text)) Name = NameVersion = text; }\n\t\t\n\t\t#endregion\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Tools/Docr.cs",
    "content": "using System.Windows.Controls;\nusing Au.Controls;\nusing System.Windows.Documents;\n\nnamespace ToolLand;\n\nclass Docr : KDialogWindow {\n\twnd _wnd, _con;\n\tbool _useCon;\n\n\tKSciInfoBox _info;\n\tButton _bTest, _bInsert;\n\tComboBox _cbAction;\n\tconst int c_waitnot = 6, c_finder = 7;\n\tKSciCodeBoxWnd _code;\n\tTextBox _text;\n\n\tKCheckBox exceptionC, exceptionnoC;\n\tKCheckTextBox rectC, scaleC, skipC, waitC, waitnoC;\n\tKCheckComboBox wiflagsC, engineC;\n\tKTextExpressionBox textC;\n\t//CONSIDER: add wnd Activate if pixels from screen.\n\n\tpublic Docr() {\n\t\tTitle = \"Find OCR text\";\n\n\t\t_noeventValueChanged = true;\n\t\tvar b = new wpfBuilder(this).WinSize((460, 440..), (500, 400..)).Columns(160, 20, -1);\n\t\tb.R.Add(out _info).Height(80);\n\t\tb.R.StartGrid().Columns(76, 76, 76, -1);\n\t\t//row 1\n\t\tb.R.AddButton(\"Window...\", _ => _bWnd_Click());\n\t\tb.AddButton(out _bTest, \"Test\", _Test).Disabled().Tooltip(\"Execute the code now (except wait/fail/mouse) and show the rectangle\");\n\t\tb.AddButton(out _bInsert, \"Insert\", _Insert).Disabled();\n\t\tb.Add(out _cbAction).Align(\"L\").Width(140).Items(\"|MouseMove|MouseClick|MouseClickD|MouseClickR|PostClick|waitNot|new ocrFinder\").Select(2);\n\t\t//row 2\n\t\tb.StartStack();\n\t\twaitC = b.xAddCheckText(\"Wait\", \"1\", check: true); b.Width(53);\n\t\t(waitnoC = b.xAddCheckText(\"Timeout\", \"5\")).Visible = false; b.Width(53);\n\t\tb.xAddCheck(out exceptionC, \"Fail if not found\").Checked();\n\t\tb.xAddCheck(out exceptionnoC, \"Fail on timeout\").Checked().Hidden(null);\n\t\tb.End();\n\t\tb.End();\n\t\t//row 3\n\t\t//left\n\t\tb.R.StartGrid();\n\t\tb.R.Add(\"Find text:\", out textC, row2: 0);\n\t\tskipC = b.R.xAddCheckText(\"Skip\");\n\t\tb.End();\n\t\t//right\n\t\tb.Skip().xStartPropertyGrid();\n\t\trectC = b.xAddCheckText(\"Rectangle\", \"0, 0, ^0, ^0\");\n\t\tb.And(21).AddButton(\"···\", _bRect_Click).Tooltip(\"Select rectangle in window\").Height(19); //info: the '·' in \"···\" is U+00B7, because '.' is too low\n\t\twiflagsC = b.xAddCheckCombo(\"Flags\", \"WindowDC|PrintWindow\");\n\t\tscaleC = b.xAddCheckText(\"Scale\");\n\t\tengineC = b.xAddCheckCombo(\"OCR engine\", \"Win10|Tesseract|GoogleCloud|MicrosoftAzure\");\n\t\tb.And(21).AddButton(\"···\", _bEngine_Click).Tooltip(\"OCR engine parameters\").Height(19);\n\t\tb.xEndPropertyGrid();\n\n\t\tb.Row(100).xAddInBorder(out _code);\n\t\tb.xAddSplitterH(span: -1);\n\t\tb.Row(-1).Add(out _text).Margin(\"T\").Multiline().Readonly();\n\t\tb.End();\n\t\t_noeventValueChanged = false;\n\n\t\tWndSavedRect.Restore(this, LA.App.Settings.wndpos.ocr, o => LA.App.Settings.wndpos.ocr = o);\n\t}\n\n\tstatic Docr() {\n\t\tTUtil.OnAnyCheckTextBoxValueChanged<Docr>((d, o) => d._AnyCheckTextBoxComboValueChanged(o), comboToo: true);\n\t}\n\n\tprotected override void OnSourceInitialized(EventArgs e) {\n\t\tbase.OnSourceInitialized(e);\n\n\t\t_InitInfo();\n\t\t//_bWnd_Click(); //rejected. Confusing.\n\t}\n\n\tvoid _bWnd_Click() {\n\t\tvar r = _code.AaShowWndTool(this, _wnd, _con, checkControl: _wnd.Is0 || _useCon);\n\t\tif (r.ok) _SetWndCon(r.w, r.con, r.useCon);\n\t}\n\n\tvoid _bRect_Click(WBButtonClickArgs e) {\n\t\tif (_wnd.Is0) return;\n\t\tvar m = new popupMenu();\n\t\tm[\"Select rectangle...\"] = o => { if (_CaptureRect(out var r)) rectC.Set(true, r.rect.ToStringFormat(\"({0}, {1}, {4}, {5})\")); };\n\t\tm.Show(owner: this);\n\t}\n\n\tvoid _SetWndCon(wnd w, wnd con, bool useCon) {\n\t\tvar wPrev = _AreaWnd;\n\t\t_wnd = w;\n\t\t_con = con == w ? default : con;\n\n\t\t_noeventValueChanged = true;\n\t\t_useCon = useCon && !_con.Is0;\n\t\tif (_AreaWnd != wPrev) rectC.c.IsChecked = false;\n\t\t_noeventValueChanged = false;\n\t\t_FormatCode();\n\t}\n\n\t//when checked/unchecked any checkbox, and when text changed of any textbox\n\tvoid _AnyCheckTextBoxComboValueChanged(object source) {\n\t\tif (!_noeventValueChanged) {\n\t\t\t_noeventValueChanged = true;\n\t\t\t//print.it(\"source\", source);\n\t\t\tif (source is KCheckBox c) {\n\t\t\t\t//bool on = c.IsChecked;\n\t\t\t} else if (source is TextBox t && t.Tag is KCheckTextBox k) {\n\t\t\t\t_noeventValueChanged = _formattedOnValueChanged = false; //allow auto-check but prevent formatting twice\n\t\t\t\tk.CheckIfTextNotEmpty();\n\t\t\t\tif (_formattedOnValueChanged) return;\n\t\t\t} else if (source is ComboBox u && u.Tag is KCheckComboBox m) {\n\t\t\t\t_noeventValueChanged = _formattedOnValueChanged = false; //allow auto-check but prevent formatting twice\n\t\t\t\tm.c.IsChecked = true;\n\t\t\t\tif (_formattedOnValueChanged) return;\n\t\t\t} else if (source == _cbAction) {\n\t\t\t\tint i = _cbAction.SelectedIndex;\n\t\t\t\twaitnoC.Visible = i == c_waitnot;\n\t\t\t\texceptionnoC.Visibility = i == c_waitnot ? System.Windows.Visibility.Visible : System.Windows.Visibility.Collapsed;\n\t\t\t\twaitC.Visible = i < c_waitnot;\n\t\t\t\texceptionC.Visibility = i < c_waitnot ? System.Windows.Visibility.Visible : System.Windows.Visibility.Collapsed;\n\t\t\t}\n\t\t\t_noeventValueChanged = false;\n\t\t\t_formattedOnValueChanged = true;\n\t\t\t//print.it(\"format\");\n\t\t\t_FormatCode();\n\t\t}\n\t}\n\tbool _noeventValueChanged, _formattedOnValueChanged;\n\n\t(string code, string wndVar) _FormatCode(bool forTest = false) {\n\t\t//print.it(\"_FormatCode\");\n\t\tStringBuilder b = new(), bb = new(); //b for the find/finder function line and everything after it; bb for everything before it.\n\n\t\tstring waitTime = null;\n\t\tbool finder = false, waitNot = false, wait = false, orThrow = false, notTimeout = false;\n\t\tint iAction = 0;\n\t\tif (!forTest) {\n\t\t\tiAction = _cbAction.SelectedIndex;\n\t\t\tif (finder = iAction == c_finder) {\n\n\t\t\t} else if (waitNot = iAction == c_waitnot) {\n\t\t\t\tnotTimeout = waitnoC.GetText(out waitTime, emptyToo: true);\n\t\t\t\torThrow = notTimeout && exceptionnoC.IsChecked;\n\t\t\t\tif (waitTime.NE()) waitTime = \"0\";\n\t\t\t} else {\n\t\t\t\twait = waitC.GetText(out waitTime, emptyToo: true);\n\t\t\t\torThrow = exceptionC.IsChecked;\n\t\t\t}\n\t\t}\n\n\t\tstring wndCode = null, wndVar = null;\n\t\tif (finder) {\n\t\t\tb.Append(\"var f = new ocrFinder(\");\n\t\t} else {\n\t\t\tb.Append(waitNot ? \"ocr.waitNot(\" : \"ocr.find(\");\n\t\t\tif (wait || waitNot || orThrow) if (b.AppendWaitTime(waitTime ?? \"0\", orThrow, appendAlways: waitNot)) b.Append(\", \");\n\n\t\t\t(wndCode, wndVar) = _code.AaGetWndFindCode(forTest, _wnd, _useCon ? _con : default);\n\t\t\tbb.AppendLine(wndCode);\n\n\t\t\tif (rectC.GetText(out var sRect)) b.AppendFormat(\"new({0}, {1})\", wndVar, sRect);\n\t\t\telse b.Append(wndVar);\n\t\t}\n\n\t\tbool isEngine = engineC.GetIndex(out var ieng);\n\t\tif (isEngine) {\n\t\t\tvar g = LA.App.Settings.ocr ?? new();\n\t\t\tbb.AppendFormat(\"{0}engine = new Ocr{1}(\", forTest ? \"var \" : \"ocr.\", engineC.t.SelectedItem);\n\t\t\tif (ieng == 3) bb.AppendStringArg(g.mUrl).AppendStringArg(g.mKey);\n\t\t\telse if (ieng == 2) bb.AppendStringArg(g.gKey);\n\t\t\tbb.Append(\")\");\n\t\t\tif (ieng switch { 0 => !g.wLang.NE(), 1 => !g.tLang.NE() || !g.tCL.NE(), 2 => !g.gFeat.NE() || !g.gIC.NE(), _ => false }) {\n\t\t\t\tbb.Append(\" { \");\n\t\t\t\tswitch (ieng) {\n\t\t\t\tcase 0:\n\t\t\t\t\tbb.Append(\"Language = \").AppendStringArg(g.wLang, noComma: true);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 1:\n\t\t\t\t\tif (!g.tLang.NE()) bb.Append(\"Language = \").AppendStringArg(g.tLang, noComma: true);\n\t\t\t\t\tif (!g.tCL.NE()) bb.Append(g.tLang.NE() ? \"\" : \", \").Append(\"CommandLine = \").AppendStringArg(g.tCL, noComma: true);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 2:\n\t\t\t\t\tif (!g.gFeat.NE()) bb.Append(\"Features = \").AppendStringArg(g.gFeat, noComma: true);\n\t\t\t\t\tif (!g.gIC.NE()) bb.Append(g.gFeat.NE() ? \"\" : \", \").Append(\"ImageContext = \").AppendStringArg(g.gIC, noComma: true);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tbb.Append(\" }\");\n\t\t\t}\n\t\t\tbb.AppendLine(\";\");\n\t\t}\n\n\t\tb.AppendStringArg(textC.Text);\n\n\t\tif (wiflagsC.GetText(out var wiFlag)) b.AppendOtherArg(\"OcrFlags.\" + wiFlag);\n\n\t\tif (scaleC.GetText(out var scale) && scale != \"0\") b.AppendOtherArg(scale, \"scale\");\n\n\t\tif (forTest) b.AppendOtherArg(isEngine ? \"engine\" : \"new OcrWin10()\", \"engine\"); //don't set/use ocr.engine when testing\n\n\t\tif (skipC.GetText(out var skip)) b.AppendOtherArg(skip, \"skip\");\n\n\t\tb.Append(\");\");\n\n\t\tif (!forTest) {\n\t\t\tif (waitNot) {\n\t\t\t\tif (!orThrow && notTimeout) bb.Append(\"bool ok = \");\n\t\t\t} else if (!finder) {\n\t\t\t\tstring mouse = iAction switch {\n\t\t\t\t\t1 => \"t.MouseMove();\",\n\t\t\t\t\t2 => \"t.MouseClick();\",\n\t\t\t\t\t3 => \"t.MouseClickD();\",\n\t\t\t\t\t4 => \"t.MouseClickR();\",\n\t\t\t\t\t5 => \"t.PostClick();\",\n\t\t\t\t\t_ => null\n\t\t\t\t};\n\t\t\t\tbb.Append(\"var t = \");\n\t\t\t\tif (!orThrow || mouse != null) b.AppendLine();\n\t\t\t\tif (!orThrow) b.Append(\"if(t != null) { \");\n\t\t\t\tif (mouse != null) b.Append(mouse);\n\t\t\t\tif (!orThrow) b.Append(\" } else { print.it(\\\"not found\\\"); }\");\n\t\t\t}\n\t\t}\n\n\t\tvar R = bb.Append(b).ToString();\n\n\t\tif (!forTest) {\n\t\t\t_code.AaSetText(R, wndCode.Lenn());\n\t\t\t_bTest.IsEnabled = true; _bInsert.IsEnabled = true;\n\t\t}\n\n\t\treturn (R, wndVar);\n\t}\n\n\t#region util, misc\n\n\twnd _AreaWnd => _useCon ? _con : _wnd;\n\n\tbool _CaptureRect(out CIUResult r) {\n\t\tif (!CaptureScreen.ImageColorRectUI(out r, CIUFlags.Rectangle, this)) return false;\n\n\t\tvar w2 = _useCon ? r.w : r.w.Window;\n\t\tstring es = null;\n\t\tbool otherWindow = w2 != _AreaWnd;\n\t\tif (otherWindow) es = \"Whole rectangle must be in the client area of the captured image's window or control.\";\n\t\tif (es != null) {\n\t\t\tdialog.showError(null, es, owner: this);\n\t\t\treturn false;\n\t\t}\n\n\t\tw2.MapScreenToClient(ref r.rect);\n\t\treturn true;\n\t}\n\n\t#endregion\n\n\t#region Insert, Test\n\n\t///// <summary>\n\t///// When OK clicked, contains C# code. Else null.\n\t///// </summary>\n\t//public string aaResultCode { get; private set; }\n\n\tvoid _Insert(WBButtonClickArgs _1) {\n\t\tif (_close) {\n\t\t\tbase.Close();\n\t\t} else if (_code.aaaText.NullIfEmpty_() is string s) {\n\t\t\tLA.InsertCode.Statements(new(s, makeVarName1: true));\n\t\t\t//if (_Opt.Has(_EOptions.InsertClose)) {\n\t\t\t//\tbase.Close();\n\t\t\t//} else {\n\t\t\t_close = true;\n\t\t\t_bInsert.Content = \"Close\";\n\t\t\t_bInsert.MouseLeave += (_, _) => {\n\t\t\t\t_close = !true;\n\t\t\t\t_bInsert.Content = \"Insert\";\n\t\t\t};\n\t\t\t//}\n\n\t\t\tif (rectC.GetText(out var sRect)) TUtil.InfoRectCoord(_AreaWnd, sRect);\n\t\t}\n\t}\n\tbool _close;\n\n\tvoid _Test(WBButtonClickArgs _1) {\n\t\tvar (code, wndVar) = _FormatCode(true); if (code.NE()) return;\n\t\tocrFinder.testing_ = (true, null);\n\t\tvar rr = TUtil.RunTestFindObject(this, code, wndVar, _AreaWnd, getRect: o => (o as ocr).GetRect(inScreen: true), activateWindow: true);\n\t\t_text.Text = ocrFinder.testing_.result?.Text;\n\t\tocrFinder.testing_ = default;\n\t\t_info.InfoErrorOrInfo(rr.info);\n\t}\n\n\t#endregion\n\n\t#region info\n\n\t//TUtil.CommonInfos _commonInfos;\n\tvoid _InitInfo() {\n\t\t//_commonInfos = new TUtil.CommonInfos(_info);\n\n\t\t_info.aaaText = c_dialogInfo;\n\t\t_info.AaAddElem(this, c_dialogInfo);\n\n\t\t_info.InfoCT(rectC,\n@\"Limit the area to this rectangle in the client area of the window or control. Smaller = faster.\nCan be <b>RECT<>: <code>(left, top, width, height)</code>. Or 4 <help Au.Types.Coord>Coord<>: <code>left, top, right, bottom</code>; for example <code>^0</code> is right/bottom edge, <code>0.5f</code> is center.\nIf action is ocrFinder, this is used only for testing.\");\n\t\t_info.InfoCO(wiflagsC,\n@\"Get pixels from window, not from screen.\nThe window can be in the background. Also with WindowDC faster.\nWorks not with all windows. Try WindowDC, then PrintWindow if fails.\");\n\t\t_info.InfoCT(scaleC,\n@\"Scale factor. Value 2 or 3 may improve OCR results.\nMore info: <help>ocr.find<>.\");\n\t\t_info.InfoCO(engineC,\n@\"OCR engine.\n• <i>OcrWin10</i> - fast, poor accuracy, available on Windows 10 and later.\n• <i>OcrTesseract</i> - slower, poor accuracy, available if Tesseract is installed, supports more languages.\n• <i>OcrGoogleCloud</i> - slow, good accuracy, uses internet, need Google Cloud account, may be not free.\n• <i>OcrMicrosoftAzure</i> - slowest, good accuracy, uses internet, need Microsoft Azure account, may be not free.\n\nNormally you specify OCR engine once in script. If unchecked, will use the previously specified engine, or OcrWin10 if unspecified.\");\n\t\t_info.Info(textC, \"Find text\", @\"Text to find in OCR results. Can have prefix:\n• <mono>**r<> - PCRE regular expression. Example: <mono>@\"\"**r \\bwhole words\\b\"\"<>\n• <mono>**R<> - .NET regular expression.\n• <mono>**i<> - case-insensitive.\n• <mono>**t<> - case-sensitive (default).\n\nSeparate words with single space.\");\n\t\t_info.InfoCT(skipC,\n@\"0-based index of matching text.\nFor example, if 1, finds the second matching text.\");\n\t\t_info.Info(_cbAction, \"Action\", \"Call this function when found. Or instead of <b>find<> call <b>waitNot<> or create new <b>ocrFinder<>.\");\n\t\t_info.InfoCT(waitC, @\"The wait timeout, seconds.\nThe function waits max this time interval. On timeout throws exception if <b>Fail...<> checked, else returns null. If empty, uses 8e88 (infinite).\");\n\t\t_info.InfoCT(waitnoC, @\"The wait timeout, seconds.\nThe function waits max this time interval. On timeout throws exception if <b>Fail...<> checked, else returns false. No timeout if unchecked or 0 or empty.\");\n\t\t_info.InfoC(exceptionC,\n@\"Throw exception if not found.\nIf unchecked, returns null.\");\n\t\t_info.InfoC(exceptionnoC,\n@\"Throw exception on timeout.\nIf unchecked, returns false.\");\n\t}\n\n\tconst string c_dialogInfo =\n@\"This tool creates <help>ocr.find<> code (OCR-extract <help wnd.find>window<> text and find).\n1. Select a window or control. Test, OK.\n2. Enter text to find.\n3. Click the Test button to see how the 'find' code works.\n4. If need, change some fields.\n5. Click Insert. Click Close, or repeat 1-5 to create new code.\n6. If need, edit the code in editor. For example rename variables, delete duplicate wnd.find lines, replace part of window name with *, add code to click the text.\";\n\n\t#endregion\n\n\t#region OCR engine properties\n\n\tprivate void _bEngine_Click(WBButtonClickArgs e) {\n\t\tvar g = LA.App.Settings.ocr ?? new();\n\n\t\tvar b = new wpfBuilder(\"OCR engine properties\").WinSize(500);\n\t\tb.Window.UseLayoutRounding = true;\n\n\t\t_StartGroupBox(\"OcrWin10\");\n\t\tb.R.Add(\"Language\", out KComboExpressionBox wLang, g.wLang).Editable().Tooltip(\"An installed language that supports OCR.\\nIf empty, uses the default OCR language.\");\n\t\tif (osVersion.minWin10) b.Items(true, cb => { try { cb.ItemsSource = new OcrWin10().AvailableLanguages.Select(o => o.tag); } catch { } });\n\t\tb.End();\n\n\t\t_StartGroupBox(\"OcrTesseract\");\n\t\t//b.R.AddButton(\"Download\", _=> run.itSafe(\"https://github.com/UB-Mannheim/tesseract/wiki\")); //the link is in doc\n\t\tb.R.Add(\"Language\", out KComboExpressionBox tLang, g.tLang).Editable().Tooltip(\"A language installed with Tesseract.\\nIf empty, uses eng. Can be sevaral, like eng+deu.\");\n\t\tb.Items(true, cb => { try { cb.ItemsSource = new OcrTesseract().AvailableLanguages; } catch { } });\n\t\tb.R.Add(\"Command line\", out KTextExpressionBox tCL, g.tCL).Tooltip(\"Additional tesseract.exe command line arguments.\");\n\t\tb.End();\n\n\t\t_StartGroupBox(\"OcrGoogleCloud\");\n\t\tb.R.Add(\"API key *\", out KComboExpressionBox gKey, g.gKey).Items(\"\"\"@@Environment.GetEnvironmentVariable(\"API_KEY_1\")\"\"\");\n\t\tb.R.Add(\"Features\", out KTextExpressionBox gFeat, g.gFeat).Multiline().Tooltip(\"Feature type, like TEXT_DETECTION, or JSON of features array content.\\nIf empty, uses DOCUMENT_TEXT_DETECTION.\");\n\t\tb.R.Add(\"Image context\", out KTextExpressionBox gIC, g.gIC).Multiline().Tooltip(\"JSON of imageContext.\\nFor example can specify language (usually don't need).\");\n\t\tb.End();\n\n\t\t_StartGroupBox(\"OcrMicrosoftAzure\");\n\t\tb.R.Add(\"Endpoint URL *\", out KComboExpressionBox mUrl, g.mUrl).Items(\"https://replacethis.cognitiveservices.azure.com/\");\n\t\tb.R.Add(\"API key *\", out KComboExpressionBox mKey, g.mKey).Items(\"\"\"@@Environment.GetEnvironmentVariable(\"API_KEY_2\")\"\"\");\n\t\tb.End();\n\n\t\tb.R.StartGrid().Columns(-1, 0);\n\t\tb.Add<TextBlock>(\"* required\");\n\t\tb.AddOkCancel();\n\t\tb.End();\n\t\tb.End();\n\t\t\n\t\tif (!b.ShowDialog(this)) return;\n\n\t\tg.wLang = _Text(wLang.Text);\n\t\tg.tLang = _Text(tLang.Text);\n\t\tg.tCL = _Text(tCL.Text);\n\t\tg.gKey = _Text(gKey.Text);\n\t\tg.gFeat = _Text(gFeat.Text);\n\t\tg.gIC = _Text(gIC.Text);\n\t\tg.mUrl = _Text(mUrl.Text);\n\t\tg.mKey = _Text(mKey.Text);\n\n\t\tstatic string _Text(string s) => s.Trim().NullIfEmpty_();\n\n\t\tLA.App.Settings.ocr = g;\n\t\t_FormatCode();\n\n\t\tvoid _StartGroupBox(string engine) {\n\t\t\tvar hl = new Hyperlink(new Run(engine));\n\t\t\thl.Click += (_, _) => HelpUtil.AuHelp(\"Au.More.\" + engine);\n\t\t\tb.R.StartGrid<KGroupBox>(hl).Columns(100, -1);\n\t\t}\n\t}\n\n\t#endregion\n}\n"
  },
  {
    "path": "Au.Editor/Tools/Duiimage.cs",
    "content": "using System.Windows.Controls;\nusing Au.Controls;\nusing System.Windows.Input;\nusing System.Windows.Interop;\nusing System.Drawing;\n\n//FUTURE: add image tools: eraser (to draw mask, ie transparent areas), crop.\n\n//FUTURE: if there are screens with different DPI, suggest to capture on each screen. Then code:\n//string image = screen.of(w).Dpi switch {\n//\t120 => @\"image:\",\n//\t_ => @\"image:\"\n//};\n\nnamespace ToolLand;\n\nclass Duiimage : KDialogWindow {\n\twnd _wnd, _con;\n\tbool _useCon;\n\tBitmap _image;\n\tRECT _rect;\n\tbool _isColor;\n\tuint _color;\n\tstring _imageFile;\n\n\tKSciInfoBox _info;\n\tKPopup _ttInfo;\n\tButton _bTest, _bInsert, _bMore, _bCapture;\n\tComboBox _cbAction;\n\tconst int c_waitnot = 8, c_finder = 9;\n\t_PictureBox _pict;\n\tKSciCodeBoxWnd _code;\n\n\tKCheckBox controlC, allC, exceptionC, exceptionnoC;\n\tKCheckTextBox rectC, diffC, skipC, waitC, waitnoC;\n\tKCheckComboBox wiflagsC;\n\t//CONSIDER: add mouse xy like in Delm.\n\t//CONSIDER: add wnd Activate if pixels from screen.\n\n\tpublic Duiimage(wnd wCapture = default) {\n\t\tTitle = \"Find image or color in window\";\n\n\t\t_noeventValueChanged = true;\n\t\tvar b = new wpfBuilder(this).WinSize((450, 400..), (380, 330..)).Columns(160, -1);\n\t\tb.R.Add(out _info).Height(60);\n\t\tb.R.StartGrid().Columns(76, 76, 76, -1);\n\t\t//row 1\n\t\tb.R.AddButton(out _bCapture, \"Capture\", _ => _Capture());\n\t\tb.AddButton(out _bTest, \"Test\", _Test).Disabled().Tooltip(\"Execute the code now (except wait/fail/mouse) and show the rectangle\");\n\t\tb.AddButton(out _bInsert, \"Insert\", _Insert).Disabled();\n\t\tb.Add(out _cbAction).Align(\"L\").Width(140).Items(\"|MouseMove|MouseClick|MouseClickD|MouseClickR|PostClick|PostClickD|PostClickR|waitNot|new uiimageFinder\").Select(2);\n\t\t//row 3\n\t\tb.R.AddButton(out _bMore, \"More ▾\", _bEtc_Click).Align(\"L\");\n\t\tb.StartStack();\n\t\twaitC = b.xAddCheckText(\"Wait\", \"1\", check: true); b.Width(53);\n\t\t(waitnoC = b.xAddCheckText(\"Timeout\", \"5\")).Visible = false; b.Width(53);\n\t\tb.xAddCheck(out exceptionC, \"Fail if not found\").Checked();\n\t\tb.xAddCheck(out exceptionnoC, \"Fail on timeout\").Checked().Hidden(null);\n\t\tb.End();\n\t\tb.End();\n\t\t//row 4\n\t\tb.R.AddButton(\"Window...\", _bWnd_Click).And(-70).Add(out controlC, \"Control\").Disabled();\n\t\tb.xStartPropertyGrid();\n\t\trectC = b.xAddCheckText(\"Rectangle\", \"0, 0, ^0, ^0\");\n\t\tb.And(21).AddButton(\"···\", _bRect_Click).Height(19);\n\t\twiflagsC = b.xAddCheckCombo(\"Flags\", \"WindowDC|PrintWindow\");\n\t\tdiffC = b.xAddCheckText(\"Color diff\", \"10\");\n\t\tb.And(60).AddButton(\"Detect\", _bDiff_Click).Tooltip(\"Detects the smallest diff value that allows to find the image\");\n\t\tskipC = b.xAddCheckText(\"Skip\");\n\t\tb.xAddCheck(out allC, \"Get all\", noR: true);\n\t\tb.xEndPropertyGrid(); b.SpanRows(2);\n\n\t\tb.Row(80).xAddInBorder(out _pict); b.Span(1);\n\t\tb.Row(-1).xAddInBorder(out _code);\n\t\tb.End();\n\t\t_noeventValueChanged = false;\n\n\t\tWndSavedRect.Restore(this, LA.App.Settings.wndpos.uiimage, o => LA.App.Settings.wndpos.uiimage = o);\n\n\t\tif (!wCapture.Is0) {\n\t\t\tWindowState = System.Windows.WindowState.Minimized;\n\t\t\twiflagsC.c.IsChecked = !(wCapture.HasExStyle(WSE.NOREDIRECTIONBITMAP) || wCapture.ChildAll().Any(o => o.HasExStyle(WSE.NOREDIRECTIONBITMAP)));\n\t\t\tb.Loaded += () => {\n\t\t\t\t_Capture(wCapture);\n\t\t\t};\n\t\t}\n\t}\n\n\tstatic Duiimage() {\n\t\tTUtil.OnAnyCheckTextBoxValueChanged<Duiimage>((d, o) => d._AnyCheckTextBoxComboValueChanged(o), comboToo: true);\n\t}\n\n\tprotected override void OnSourceInitialized(EventArgs e) {\n\t\tbase.OnSourceInitialized(e);\n\n\t\t_InitInfo();\n\t}\n\n\tprotected override void OnClosed(EventArgs e) {\n\t\tif (_image != null) _pict.Image = _image = null;\n\n\t\tbase.OnClosed(e);\n\t}\n\n\tvoid _Capture(wnd wCapture = default) {\n\t\tif (!_CaptureImageOrRect(false, out var r, wCapture)) return;\n\t\t_imageFile = null;\n\t\t_SetImage(r);\n\n\t\tif (r.possiblyWrongWindow) {\n\t\t\tdialog.showWarning(\"Possibly wrong window\", \"The window that contained the captured image possibly disappeared while capturing. Please review the code. If the window is wrong, click the [Window...] button and capture the correct window; or capture later with the 'Find window' tool or 'Quick capturing' hotkey.\", owner: this);\n\t\t}\n\n\t\tif (_Flags is 0 or CIUFlags.PrintWindow && Dpi.IsWindowVirtualized(r.w)) {\n\t\t\tTUtil.InfoTooltip(ref _ttInfo, _bCapture, \"\"\"\nNote: The window is DPI-scaled. Its pixel colors will change after resizing, and the code may stop working.\nTo avoid it, capture with flag WindowDC. Or try to move the window to another screen.\n\"\"\");\n\t\t}\n\t}\n\n\tvoid _bRect_Click(WBButtonClickArgs e) {\n\t\tif (_wnd.Is0) return;\n\t\tvar m = new popupMenu();\n\t\tm[\"Select rectangle...\"] = o => { if (_CaptureImageOrRect(true, out var r)) _SetRect(r.rect); };\n\t\tif (_image != null) m[\"Rectangle of the captured image\"] = o => _SetRect(_rect);\n\t\tm.Show(owner: this);\n\t\tvoid _SetRect(RECT k) => rectC.Set(true, k.ToStringFormat(\"({0}, {1}, {4}, {5})\"));\n\t}\n\n\tvoid _bDiff_Click(WBButtonClickArgs e) {\n\t\tif (_image == null || _wnd.Is0) return;\n\t\t_wnd.ActivateL();\n\t\t300.ms();\n\t\tstring es = null;\n\t\ttry {\n\t\t\tusing var b = CaptureScreen.Image(_AreaWnd, flags: _Flags.ToCIFlags_());\n\t\t\tvar im = _isColor ? (IFImage)_color : _image;\n\t\t\tint maxFound = 0, minNotfound = 0;\n\t\t\tif (!new uiimageFinder(im).Exists(b)) {\n\t\t\t\tfor (maxFound = 101; maxFound - minNotfound > 1;) {\n\t\t\t\t\tint i = (minNotfound + maxFound) / 2;\n\t\t\t\t\tif (new uiimageFinder(im, diff: i).Exists(b)) maxFound = i; else minNotfound = i;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (maxFound <= 100) {\n\t\t\t\t_noeventValueChanged = true;\n\t\t\t\tdiffC.t.Text = maxFound.ToS();\n\t\t\t\tdiffC.c.IsChecked = maxFound > 0;\n\t\t\t\t_noeventValueChanged = false;\n\t\t\t} else es = \"Can't find with any diff.\";\n\t\t}\n\t\tcatch (Exception e1) { es = e1.ToStringWithoutStack(); }\n\t\tfinally { this.Hwnd().ActivateL(); }\n\t\tif (es != null) TUtil.InfoTooltip(ref _ttInfo, e.Button, es);\n\t}\n\n\tCIUFlags _Flags => !wiflagsC.c.IsChecked ? 0 : wiflagsC.t.SelectedIndex switch { 1 => CIUFlags.PrintWindow, _ => CIUFlags.WindowDC };\n\n\tbool _CaptureImageOrRect(bool rect, out CIUResult r, wnd wCapture = default) {\n\t\t_ttInfo?.Close();\n\n\t\tvar fl = rect ? CIUFlags.Rectangle : _Flags;\n\t\tif (!CaptureScreen.ImageColorRectUI(out r, fl, this, wCapture)) return false;\n\n\t\tvar w2 = (!rect || _useCon) ? r.w : r.w.Window;\n\t\tstring es = null;\n\t\tif (rect) {\n\t\t\tbool otherWindow = w2 != _AreaWnd;\n\t\t\tif (otherWindow) es = \"Whole rectangle must be in the client area of the captured image's window or control.\";\n\t\t} else if (r.w.Is0) {\n\t\t\tr.image?.Dispose(); r.image = null;\n\t\t\tes = \"Whole image must be in the client area of a single window.\";\n\t\t}\n\t\tif (es != null) {\n\t\t\tdialog.showError(null, es, owner: this);\n\t\t\treturn false;\n\t\t}\n\n\t\tw2.MapScreenToClient(ref r.rect);\n\t\treturn true;\n\t}\n\n\t//Use r on Capture. Use image on Open or Paste.\n\tvoid _SetImage(CIUResult r = null, Bitmap image = null) {\n\t\tif (r != null) { //on Capture\n\t\t\tvar w = r.w.Window; if (w.Is0) return;\n\t\t\t_SetWndCon(w, r.w, true, false);\n\t\t\tif (_isColor = (r.image == null)) {\n\t\t\t\tusing var g = Graphics.FromImage(r.image = new Bitmap(16, 16));\n\t\t\t\tg.Clear(Color.FromArgb((int)r.color));\n\t\t\t}\n\t\t\t_color = r.color & 0xffffff;\n\t\t\t_image = r.image;\n\t\t\t_rect = r.rect;\n\t\t\tif (r.dpiScale != 1 && !_isColor) _rect.Inflate(2, 2);\n\t\t} else { //on Open or Paste\n\t\t\t_isColor = false;\n\t\t\t_color = 0;\n\t\t\t_image = image;\n\t\t\t_rect = new RECT(0, 0, image.Width, image.Height);\n\t\t}\n\n\t\t//set _pict\n\t\t_pict.Image = _image;\n\n\t\t//set _code\n\t\t_FormatCode();\n\n\t\t_bTest.IsEnabled = true; _bInsert.IsEnabled = true;\n\n\t\tif (_MultiIsActive /*&& dialog.showYesNo(\"Add to array?\", owner: this)*/) _MultiAdd();\n\t}\n\n\tvoid _SetWndCon(wnd w, wnd con, bool useCon, bool updateCodeIfNeed) {\n\t\tvar wPrev = _AreaWnd;\n\t\t_wnd = w;\n\t\t_con = con == w ? default : con;\n\n\t\t_noeventValueChanged = !updateCodeIfNeed;\n\t\t_useCon = useCon && !_con.Is0;\n\t\tcontrolC.IsChecked = _useCon; controlC.IsEnabled = !_con.Is0;\n\t\tif (_AreaWnd != wPrev) rectC.c.IsChecked = false;\n\t\t_noeventValueChanged = false;\n\t}\n\n\tprivate void _bWnd_Click(WBButtonClickArgs e) {\n\t\tvar r = _code.AaShowWndTool(this, _wnd, _con, checkControl: _useCon);\n\t\tif (r.ok) _SetWndCon(r.w, r.con, r.useCon, true);\n\t}\n\n\t//when checked/unchecked any checkbox, and when text changed of any textbox or combobox\n\tvoid _AnyCheckTextBoxComboValueChanged(object source) {\n\t\tif (!_noeventValueChanged) {\n\t\t\t_noeventValueChanged = true;\n\t\t\t//print.it(\"source\", source);\n\t\t\tif (source is KCheckBox c) {\n\t\t\t\tbool on = c.IsChecked;\n\t\t\t\tif (c == controlC) {\n\t\t\t\t\tif (_useCon = on) _wnd.MapClientToClientOf(_con, ref _rect); else _con.MapClientToClientOf(_wnd, ref _rect);\n\t\t\t\t\trectC.c.IsChecked = false;\n\t\t\t\t} else if (c == wiflagsC.c) {\n\t\t\t\t\tif (_image != null) TUtil.InfoTooltip(ref _ttInfo, c, \"After changing flags may need to capture again.\\nClick Test. If not found, click Capture.\");\n\t\t\t\t} else if (c == skipC.c) {\n\t\t\t\t\tif (on) allC.IsChecked = false;\n\t\t\t\t} else if (c == allC) {\n\t\t\t\t\tif (on) skipC.c.IsChecked = false;\n\t\t\t\t}\n\t\t\t} else if (source is TextBox t && t.Tag is KCheckTextBox k) {\n\t\t\t\t_noeventValueChanged = _formattedOnValueChanged = false; //allow auto-check but prevent formatting twice\n\t\t\t\tk.CheckIfTextNotEmpty();\n\t\t\t\tif (_formattedOnValueChanged) return;\n\t\t\t} else if (source is ComboBox u && u.Tag is KCheckComboBox m) {\n\t\t\t\t_noeventValueChanged = _formattedOnValueChanged = false; //allow auto-check but prevent formatting twice\n\t\t\t\tm.c.IsChecked = true;\n\t\t\t\tif (_formattedOnValueChanged) return;\n\t\t\t} else if (source == _cbAction) {\n\t\t\t\tint i = _cbAction.SelectedIndex;\n\t\t\t\twaitnoC.Visible = i == c_waitnot;\n\t\t\t\texceptionnoC.Visibility = i == c_waitnot ? System.Windows.Visibility.Visible : System.Windows.Visibility.Collapsed;\n\t\t\t\twaitC.Visible = i < c_waitnot;\n\t\t\t\texceptionC.Visibility = i < c_waitnot ? System.Windows.Visibility.Visible : System.Windows.Visibility.Collapsed;\n\t\t\t\tallC.Visibility = i == c_waitnot ? System.Windows.Visibility.Hidden : System.Windows.Visibility.Visible;\n\t\t\t}\n\t\t\t_noeventValueChanged = false;\n\t\t\t_formattedOnValueChanged = true;\n\t\t\t//print.it(\"format\");\n\t\t\t_FormatCode();\n\t\t}\n\t}\n\tbool _noeventValueChanged, _formattedOnValueChanged;\n\n\t(string code, string wndVar) _FormatCode(bool forTest = false) {\n\t\t//print.it(\"_FormatCode\");\n\t\tif (_image == null) return default;\n\t\tstring wndCode = null, wndVar = null;\n\t\tStringBuilder b = new(), bb = new(); //b for the find/finder function line and everything after it; bb for everything before it.\n\n\t\tstring waitTime = null;\n\t\tbool finder = false, waitNot = false, wait = false, orThrow = false, notTimeout = false, findAll = false, isMulti = false;\n\t\tint iAction = 0;\n\t\tif (!forTest) {\n\t\t\tiAction = _cbAction.SelectedIndex;\n\t\t\tif (finder = iAction == c_finder) {\n\n\t\t\t} else if (waitNot = iAction == c_waitnot) {\n\t\t\t\tnotTimeout = waitnoC.GetText(out waitTime, emptyToo: true);\n\t\t\t\torThrow = notTimeout && exceptionnoC.IsChecked;\n\t\t\t\tif (waitTime.NE()) waitTime = \"0\";\n\t\t\t} else {\n\t\t\t\twait = waitC.GetText(out waitTime, emptyToo: true);\n\t\t\t\torThrow = exceptionC.IsChecked;\n\t\t\t}\n\t\t\tfindAll = !waitNot && allC.IsChecked;\n\t\t\tisMulti = _multi != null && _multi.Count != 0;\n\t\t}\n\t\tbool isColor = _isColor && !isMulti;\n\n\t\tif (finder) {\n\t\t\tb.Append(\"var f = new uiimageFinder(\");\n\t\t} else {\n\t\t\tb.Append(waitNot ? \"uiimage.waitNot(\" : \"uiimage.find(\");\n\t\t\tif (wait || waitNot || orThrow) if (b.AppendWaitTime(waitTime ?? \"0\", orThrow, appendAlways: waitNot)) b.Append(\", \");\n\n\t\t\t(wndCode, wndVar) = _code.AaGetWndFindCode(forTest, _wnd, _useCon ? _con : default);\n\t\t\tbb.AppendLine(wndCode);\n\n\t\t\tif (rectC.GetText(out var sRect)) b.AppendFormat(\"new({0}, {1})\", wndVar, sRect);\n\t\t\telse b.Append(wndVar);\n\t\t}\n\n\t\t//string imageVar = isColor ? \"0x\" : (\"image\" + Environment.TickCount.ToS(\"X\"));\n\t\tstring imageVar = isColor ? \"0x\" : \"image\";\n\t\tb.AppendOtherArg(imageVar);\n\t\tif (isColor) b.Append(_color.ToString(\"X6\"));\n\n\t\tif (wiflagsC.GetText(out var wiFlag)) b.AppendOtherArg(\"IFFlags.\" + wiFlag);\n\n\t\tif (diffC.GetText(out var diff) && diff != \"0\") b.AppendOtherArg(diff, \"diff\");\n\n\t\tstring also = null;\n\t\tif (findAll) {\n\t\t\talso = \"o => { a.Add(o); return IFAlso.OkFindMore; }\";\n\t\t} else if (skipC.GetText(out var skip)) {\n\t\t\talso = \"o => o.Skip(\" + skip + \")\";\n\t\t}\n\t\tif (also != null) b.AppendOtherArg(also, \"also\");\n\n\t\tb.Append(\");\");\n\n\t\tif (!isColor) {\n\t\t\tif (isMulti) {\n\t\t\t\tbb.AppendLine($\"IFImage[] {imageVar} = {{\");\n\t\t\t\tforeach (var v in _multi) bb.Append('\\t').Append(v).AppendLine(\",\");\n\t\t\t\tbb.Append('}');\n\t\t\t} else {\n\t\t\t\tbb.Append($\"string {imageVar} = \").Append(_CurrentImageString());\n\t\t\t}\n\t\t\tbb.AppendLine(\";\");\n\t\t}\n\n\t\tif (!forTest) {\n\t\t\tif (findAll) bb.AppendLine(\"var a = new List<uiimage>();\");\n\t\t\tif (waitNot) {\n\t\t\t\tif (!orThrow && notTimeout) bb.Append(\"bool ok = \");\n\t\t\t} else if (!finder) {\n\t\t\t\tstring mouse = iAction switch {\n\t\t\t\t\t1 => \"im.MouseMove();\",\n\t\t\t\t\t2 => \"im.MouseClick();\",\n\t\t\t\t\t3 => \"im.MouseClickD();\",\n\t\t\t\t\t4 => \"im.MouseClickR();\",\n\t\t\t\t\t5 => \"im.PostClick();\",\n\t\t\t\t\t6 => \"im.PostClickD();\",\n\t\t\t\t\t7 => \"im.PostClickR();\",\n\t\t\t\t\t_ => null\n\t\t\t\t};\n\t\t\t\tif (findAll) {\n\t\t\t\t\tb.Append(\"\\r\\nforeach(var im in a) { \");\n\t\t\t\t\tif (mouse != null) b.Append(mouse).Append(\" 250.ms(); \");\n\t\t\t\t\tb.Append('}');\n\t\t\t\t} else {\n\t\t\t\t\tbb.Append(\"var im = \");\n\t\t\t\t\tif (!orThrow || mouse != null) b.AppendLine();\n\t\t\t\t\tif (!orThrow) b.Append(\"if(im != null) { \");\n\t\t\t\t\tif (mouse != null) b.Append(mouse);\n\t\t\t\t\tif (!orThrow) b.Append(\" } else { print.it(\\\"not found\\\"); }\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tvar R = bb.Append(b).ToString();\n\n\t\tif (!forTest) _code.AaSetText(R, wndCode.Lenn());\n\n\t\treturn (R, wndVar);\n\t}\n\n\t#region util, misc\n\n\twnd _AreaWnd => _useCon ? _con : _wnd;\n\n\tstring _CurrentImageString() {\n\t\tif (_isColor) return \"0x\" + _color.ToString(\"X6\");\n\t\tif (_imageFile != null) return \"@\\\"\" + _imageFile + \"\\\"\";\n\t\tvar ms = new MemoryStream();\n\t\t_image.Save(ms, System.Drawing.Imaging.ImageFormat.Png);\n\t\treturn \"@\\\"image:\" + Convert.ToBase64String(ms.GetBuffer(), 0, (int)ms.Length) + \"\\\"\";\n\t}\n\n\t#endregion\n\n\t#region menu, open, save, multi\n\n\tvoid _bEtc_Click(WBButtonClickArgs e) {\n\t\tbool isImage = _image != null && !_isColor;\n\n\t\tvar m = new popupMenu();\n\t\tm[\"Copy image\", disable: !isImage] = o => { new clipboardData().AddImage(_image).SetClipboard(); };\n\t\tm[\"Paste image\", disable: !clipboardData.contains(ClipFormats.Image)] = o => _PasteImage(e.Button);\n\t\tm[\"Use file...\"] = o => _OpenFile(false, e.Button);\n\t\tm[\"Embed file...\"] = o => _OpenFile(true, e.Button);\n\t\tm[\"Save as file...\", disable: !isImage] = o => _SaveFile();\n\t\tm.Separator();\n\t\tm[\"Add to array\", disable: _image == null] = o => _MultiMenuAdd();\n\t\tm[\"Remove from array\", disable: !_MultiIsActive] = o => _MultiRemove();\n\t\tm.Show(owner: this);\n\t}\n\n\tbool _RequireWindow(Button button) {\n\t\tif (!_wnd.Is0) return true;\n\t\tTUtil.InfoTooltip(ref _ttInfo, button, \"At first please select a window with button 'Capture' or 'Window'.\");\n\t\treturn false;\n\t}\n\n\tvoid _OpenFile(bool embed, Button button) {\n\t\tif (!_RequireWindow(button)) return;\n\t\tif (!_FileDialog().ShowOpen(out string f, this)) return;\n\t\tvar im = new Bitmap(f);\n\t\t_imageFile = embed ? null : f;\n\t\t_SetImage(null, im);\n\t}\n\n\tstatic FileOpenSaveDialog _FileDialog() => new(\"{4D1F3AFB-DA1A-45AC-8C12-41DDA5C51CDE}\") { FileTypes = \"png, bmp|*.png;*.bmp\", DefaultExt = \"png\" };\n\n\tbool _SaveFile() {\n\t\tif (!_FileDialog().ShowSave(out string f, this)) return false;\n\t\t_image.Save(f, f.Ends(\".bmp\", true) ? System.Drawing.Imaging.ImageFormat.Bmp : System.Drawing.Imaging.ImageFormat.Png);\n\t\t_MultiRemove(true);\n\t\t_imageFile = f;\n\t\t_MultiAdd();\n\t\t_FormatCode();\n\t\treturn true;\n\t}\n\n\tvoid _PasteImage(Button button) {\n\t\tif (!_RequireWindow(button)) return;\n\t\tvar im = clipboardData.getImage();\n\t\tif (im != null) _SetImage(null, im);\n\t}\n\n\tvoid _MultiMenuAdd() {\n\t\t_multi ??= new HashSet<string>();\n\t\t_MultiAdd();\n\t}\n\n\tbool _MultiIsActive => _multi != null && _image != null;\n\n\tvoid _MultiAdd() {\n\t\tif (!_MultiIsActive) return;\n\t\t_multi.Add(_CurrentImageString());\n\t\t_FormatCode();\n\t}\n\tHashSet<string> _multi;\n\n\tvoid _MultiRemove(bool noUpdateCode = false) {\n\t\tif (!_MultiIsActive) return;\n\t\t_multi.Remove(_CurrentImageString());\n\t\tif (!noUpdateCode) _FormatCode();\n\t}\n\n\t//FUTURE: make transparent.\n\t//\tThis code is not useful because images are often alpha-blended with background. Need to make transparent near-color pixels too.\n\t//\tLet the user set color difference with preview. Maybe even draw or flood-fill clicked areas.\n\t//void _MakeTransparent(int corner)\n\t//{\n\t//\tint x = 0, y = 0;\n\n\t//\t_image.MakeTransparent(_image.GetPixel(x, y));\n\t//\t_pict.Invalidate();\n\t//}\n\n\t#endregion\n\n\t#region Insert, Test\n\n\t///// <summary>\n\t///// When OK clicked, contains C# code. Else null.\n\t///// </summary>\n\t//public string aaResultCode { get; private set; }\n\n\tvoid _Insert(WBButtonClickArgs _1) {\n\t\tif (_close) {\n\t\t\tbase.Close();\n\t\t} else if (_code.aaaText.NullIfEmpty_() is string s) {\n\t\t\tLA.InsertCode.Statements(new(s, makeVarName1: true));\n\t\t\t_close = true;\n\t\t\t_bInsert.Content = \"Close\";\n\t\t\t_bInsert.MouseLeave += (_, _) => {\n\t\t\t\t_close = !true;\n\t\t\t\t_bInsert.Content = \"Insert\";\n\t\t\t};\n\n\t\t\tif (rectC.GetText(out var sRect)) TUtil.InfoRectCoord(_AreaWnd, sRect);\n\t\t}\n\t}\n\tbool _close;\n\n\tvoid _Test(WBButtonClickArgs _1) {\n\t\tvar (code, wndVar) = _FormatCode(true); if (code.NE()) return;\n\t\tvar rr = TUtil.RunTestFindObject(this, code, wndVar, _AreaWnd, getRect: o => (o as uiimage).RectInScreen, activateWindow: true);\n\t\t_info.InfoErrorOrInfo(rr.info);\n\n\t\t//CONSIDER: don't activate the window if it wasn't active when captured\n\t}\n\n\t#endregion\n\n\t#region info\n\n\t//TUtil.CommonInfos _commonInfos;\n\tvoid _InitInfo() {\n\t\t//_commonInfos = new TUtil.CommonInfos(_info);\n\n\t\t_info.aaaText = c_dialogInfo;\n\t\t_info.AaAddElem(this, c_dialogInfo);\n\n\t\t_info.InfoC(controlC,\n@\"Search only in control (if captured), not in whole window.\nTo change window or/and control name etc, click 'Window...' or edit it in the code field.\nWith uiimageFinder this is used only for testing.\");\n\t\t_info.InfoCT(rectC,\n@\"Limit the search area to this rectangle in the client area of the window or control. Smaller = faster.\nCan be <b>RECT<>: <code>(left, top, width, height)</code>. Or 4 <help Au.Types.Coord>Coord<>: <code>left, top, right, bottom</code>; for example <code>^0</code> is right/bottom edge, <code>0.5f</code> is center.\nIf action is uiimageFinder, this is used only for testing.\");\n\t\t_info.InfoCO(wiflagsC,\n@\"Get pixels from window, not from screen.\nThe window can be in the background. Also with WindowDC faster.\nWorks not with all windows. Try WindowDC, then PrintWindow if fails.\");\n\t\t_info.InfoCT(diffC,\n@\"Maximal allowed color difference.\nValid values: 0 - 100.\");\n\t\t_info.InfoCT(skipC,\n@\"0-based index of matching image.\nFor example, if 1, gets the second matching image.\");\n\t\t_info.Info(_cbAction, \"Action\", \"Call this function when found. Or instead of <b>find<> call <b>waitNot<> or create new <b>uiimageFinder<>.\");\n\t\t_info.Info(_bMore, \"More\",\n@\"Manage images now.\n • <i>Copy image</i> - copy the image to the clipboard. For example then you can paste it in image editing software.\n • <i>Paste image</i> - paste image from the clipboard. For example from image editing software or Snipping Tool.\n • <b>Use file</b> - use an image file instead of captured image string.\n • <b>Embed file</b> - add image file data to the code as string.\n • <b>Save as file</b> - save the image.\n • <b>Add to array</b> - add this image to a list of images to find.\");\n\t\t_info.InfoC(allC, \"Find all matching images.\");\n\t\t_info.InfoCT(waitC, @\"The wait timeout, seconds.\nThe function waits max this time interval. On timeout throws exception if <b>Fail...<> checked, else returns null. If empty, uses 8e88 (infinite).\");\n\t\t_info.InfoCT(waitnoC, @\"The wait timeout, seconds.\nThe function waits max this time interval. On timeout throws exception if <b>Fail...<> checked, else returns false. No timeout if unchecked or 0 or empty.\");\n\t\t_info.InfoC(exceptionC,\n@\"Throw exception if not found.\nIf unchecked, returns null.\");\n\t\t_info.InfoC(exceptionnoC,\n@\"Throw exception on timeout.\nIf unchecked, returns false.\");\n\t}\n\n\tconst string c_dialogInfo =\n@\"This tool creates code to find <help uiimage.find>image or color<> in <help wnd.find>window<>.\n1. Click the Capture button. Mouse-drag-select the image.\n2. Click the Test button to see how the 'find' code works.\n3. If need, change some fields.\n4. Click Insert. Click Close, or capture/insert again.\n5. If need, edit the code in editor. For example rename variables, delete duplicate wnd.find lines, replace part of window name with *, add code to click the image.\";\n\n\tprotected override void OnPreviewKeyDown(KeyEventArgs e) {\n\t\t_ttInfo?.Close();\n\t\tbase.OnPreviewKeyDown(e);\n\t}\n\n\t#endregion\n\n\t//display image in HwndHost.\n\t//\tTried WPF Image, but blurry when high DPI, even if bitmap's DPI is set to match window's DPI.\n\t//\tUseLayoutRounding does not help in this case.\n\t//\tRenderOptions.SetBitmapScalingMode(NearestNeighbor) helps it seems. But why it tries to scale the image, and how many times?\n\t//\tTried winforms PictureBox, bust sometimes very slowly starts.\n\tclass _PictureBox : HwndHost {\n\t\twnd _w;\n\n\t\tprotected override HandleRef BuildWindowCore(HandleRef hwndParent) {\n\t\t\tvar wParent = (wnd)hwndParent.Handle;\n\t\t\t_w = WndUtil.CreateWindow(_wndProc = _WndProc, false, \"Static\", null, WS.CHILD | WS.CLIPCHILDREN, 0, 0, 0, 10, 10, wParent);\n\n\t\t\treturn new HandleRef(this, _w.Handle);\n\t\t}\n\n\t\tprotected override void DestroyWindowCore(HandleRef hwnd) {\n\t\t\tApi.DestroyWindow(_w);\n\t\t}\n\n\t\tpublic Bitmap Image {\n\t\t\tget => _b;\n\t\t\tset {\n\t\t\t\t_b?.Dispose();\n\t\t\t\t_b = value;\n\t\t\t\tApi.InvalidateRect(_w);\n\t\t\t}\n\t\t}\n\t\tBitmap _b;\n\n\t\tWNDPROC _wndProc;\n\t\tnint _WndProc(wnd w, int msg, nint wParam, nint lParam) {\n\t\t\t//var pmo = new PrintMsgOptions(Api.WM_NCHITTEST, Api.WM_SETCURSOR, Api.WM_MOUSEMOVE, Api.WM_NCMOUSEMOVE, 0x10c1);\n\t\t\t//if (WndUtil.PrintMsg(out string s, _w, msg, wParam, lParam, pmo)) print.it(\"<><c green>\" + s + \"<>\");\n\n\t\t\tswitch (msg) {\n\t\t\tcase Api.WM_NCHITTEST:\n\t\t\t\treturn Api.HTTRANSPARENT;\n\t\t\tcase Api.WM_PAINT:\n\t\t\t\tvar dc = Api.BeginPaint(w, out var ps);\n\t\t\t\t_WmPaint(dc);\n\t\t\t\tApi.EndPaint(w, ps);\n\t\t\t\treturn default;\n\t\t\t}\n\n\t\t\treturn Api.DefWindowProc(w, msg, wParam, lParam);\n\t\t}\n\n\t\tvoid _WmPaint(IntPtr dc) {\n\t\t\tusing var g = Graphics.FromHdc(dc);\n\t\t\tg.Clear(System.Drawing.SystemColors.AppWorkspace);\n\t\t\tif (_b == null) return;\n\n\t\t\tg.DrawImage(_b, 0, 0);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Tools/Dwnd.cs",
    "content": "using Au.Controls;\nusing System.Windows;\nusing System.Windows.Controls;\n\n//FUTURE: add UI to format code 'w = w.Get.Right();' etc.\n//FUTURE: init from code string.\n//TODO3: try to find and select control in current tree when captured from same window. Like in Delm.\n\nnamespace ToolLand;\n\n[Flags]\nenum DwndFlags {\n\tDontInsert = 1,\n\tNoControl = 2,\n\tCheckControl = 4,\n}\n\nclass Dwnd : KDialogWindow {\n\twnd _wnd, _con;\n\tbool _dontInsert, _noControl, _checkControl;\n\tstring _wndName;\n\t\n\tKSciInfoBox _info, _winInfo;\n\tButton _bTest, _bInsert;\n\tKCheckBox _cCapture, _cControl, _cActivate, _cException;\n\tComboBox _cbFunc;\n\tSeparator _sepControl;\n\tScrollViewer _scroller;\n\tKSciCodeBoxWnd _code;\n\tKTreeView _tree;\n\t\n\tGrid _gCon1, _gCon2;\n\tDPwnd.Controls _k;\n\tKCheckTextBox idC, nameC, classC, alsoC, waitW, skipC;\n\tKCheckBox cHiddenTooW, cCloakedTooW, cHiddenTooC;\n\t\n\tpublic Dwnd(wnd w = default, DwndFlags flags = 0, string title = \"Find window\") {\n\t\t_dontInsert = flags.Has(DwndFlags.DontInsert);\n\t\t_noControl = flags.Has(DwndFlags.NoControl);\n\t\t_checkControl = flags.Has(DwndFlags.CheckControl);\n\t\t\n\t\tTitle = title;\n\t\t\n\t\tvar b = new wpfBuilder(this).WinSize((500, 450..), (600, 430..)).Columns(-1);\n\t\tb.R.Add(out _info).Height(60);\n\t\tb.R.StartGrid().Columns(0, 76, 76, 0, 0, -1);\n\t\tb.xAddCheckIcon(out _cCapture, \"*Unicons.Capture\" + LA.EdIcons.red, $\"Enable capturing ({LA.App.Settings.delm.hk_capture}) and show window/control rectangles\");\n\t\tb.AddButton(out _bTest, \"Test\", _bTest_Click).Disabled().Tooltip(\"Execute the 'find' part of the code now and show the rectangle\");\n\t\tb.AddButton(out _bInsert, _dontInsert ? \"OK\" : \"Insert\", _Insert).Disabled(); if (!_dontInsert) b.Tooltip(\"Insert code in editor\");\n\t\tb.Add(out _cbFunc).Items(\"find|findOrRun|runAndFind|finder\").Tooltip(\"Function\").Width(90);\n\t\t_cbFunc.SelectionChanged += _cbFunc_SelectionChanged;\n\t\tb.Add(out _cActivate, \"Activate\").Tooltip(\"Activate the found window\");\n\t\tb.Add(out _cException, \"Fail if not found\").Checked(true).Tooltip(\"Throw exception if not found\");\n\t\t//cActivate.CheckChanged += (_, _) => { cException.Visibility = cActivate.IsChecked ? Visibility.Hidden : Visibility.Visible; }; //no, need for control too\n\t\tb.End();\n\t\t\n\t\t//window and control properties and search settings\n\t\tb.R.AddSeparator(false).Margin(\"B\");\n\t\tb.Row(0); //auto height, else adds v scrollbar when textbox height changes when a textbox text is multiline or too long (with h scrollbar)\n\t\t_scroller = b.xStartPropertyGrid(\"L2 T3 R2 B1\"); //actually never shows scrollbar because of row auto height, but sets some options etc\n\t\t_scroller.Visibility = Visibility.Hidden;\n\t\tb.Columns(-3, 0, -1.2);\n\t\t//window\n\t\tb.R.Add<TextBlock>(\"Window\").Margin(\"T1 B3\").xSetHeaderProp(); //rejected: vertical headers. Tested, looks not good, too small for vertical Control checkbox.\n\t\tb.Row(0).StartGrid();\n\t\t_k = new(b, false);\n\t\tb.End();\n\t\tb.xAddSplitterV(span: 4, thickness: 12);\n\t\tb.StartGrid().Columns(44, -1);\n\t\tb.xAddCheck(out cHiddenTooW, \"Find hidden too\");\n\t\tb.xAddCheck(out cCloakedTooW, \"Find cloaked too\");\n\t\t_k.also = b.xAddCheckText(\"also\", \"o=>true\");\n\t\twaitW = b.xAddCheckText(\"wait\", \"1\", check: true);\n\t\tb.End();\n\t\t//control\n\t\tb.R.AddSeparator(false).Margin(\"T4 B0\"); _sepControl = b.Last as Separator;\n\t\tb.R.Add(out _cControl, \"Control\").Margin(\"T5 B3\").xSetHeaderProp();\n\t\tb.Row(0).StartGrid().Columns(70, -1); _gCon1 = b.Panel as Grid;\n\t\tnameC = b.xAddCheckTextDropdown<KTextExpressionBox>(\"name\");\n\t\tclassC = b.xAddCheckText<KTextExpressionBox>(\"class\");\n\t\tidC = b.xAddCheckText(\"id\");\n\t\tb.End().Skip();\n\t\tb.StartGrid().Columns(44, -1); _gCon2 = b.Panel as Grid;\n\t\tb.xAddCheck(out cHiddenTooC, \"Find hidden too\");\n\t\talsoC = b.xAddCheckText(\"also\", \"o=>true\");\n\t\tskipC = b.xAddCheckText(\"skip\");\n\t\tb.End();\n\t\tb.xEndPropertyGrid();\n\t\tb.R.AddSeparator(false);\n\t\t\n\t\t//code\n\t\tb.Row(64).xAddInBorder(out _code, \"B\");\n\t\t\n\t\t//tree and window info\n\t\tb.xAddSplitterH(span: -1);\n\t\tb.Row(-1).StartGrid().Columns(-1, 0, -1);\n\t\tb.Row(-1).xAddInBorder(out _tree, \"TR\");\n\t\tb.xAddSplitterV();\n\t\tb.xAddInBorder(out _winInfo, \"TL\"); _winInfo.AaWrapLines = false; _winInfo.Name = \"window_info\";\n\t\tb.End();\n\t\t\n\t\tb.End();\n\t\t\n\t\t_InitTree();\n\t\t\n\t\t_con = w;\n\t\t\n\t\tb.WinProperties(\n\t\t\ttopmost: true,\n\t\t\tshowActivated: _dontInsert || w.Is0 ? null : false //eg if captured a popup menu, activating this window closes the menu and we cannot get properties\n\t\t\t);\n\t\t\n\t\tWndSavedRect.Restore(this, LA.App.Settings.wndpos.wnd, o => LA.App.Settings.wndpos.wnd = o);\n\t}\n\t\n\tstatic Dwnd() {\n\t\tTUtil.OnAnyCheckTextBoxValueChanged<Dwnd>((d, o) => d._AnyCheckTextBoxValueChanged(o));\n\t}\n\t\n\tprotected override void OnSourceInitialized(EventArgs e) {\n\t\tbase.OnSourceInitialized(e);\n\t\t\n\t\tif (!_con.Is0) _SetWnd(false);\n\t\t_InitInfo();\n\t\t_cCapture.IsChecked = true;\n\t}\n\t\n\tprotected override void OnClosing(CancelEventArgs e) {\n\t\t_cCapture.IsChecked = false;\n\t\t\n\t\tbase.OnClosing(e);\n\t}\n\t\n\tvoid _SetWnd(bool captured) {\n\t\t_bTest.IsEnabled = true; _bInsert.IsEnabled = true;\n\t\t\n\t\tvar wndOld = _wnd;\n\t\t_wnd = _con.Window;\n\t\tif (_wnd == _con || _noControl) _con = default;\n\t\tbool newWindow = _wnd != wndOld;\n\t\t\n\t\t_ClearTree();\n\t\tif (!_FillProperties(newWindow)) return;\n\t\t_FormatCode(false);\n\t\t_FillTree();\n\t}\n\t\n\tbool _FillProperties(bool newWindow) {\n\t\tbool isCon = !_con.Is0;\n\t\t\n\t\t_WinInfo f = default;\n\t\t\n\t\tif (!_GetClassName(_wnd, out f.wClass)) return false; //note: get even if !newWindow, to detect closed window\n\t\tif (isCon && !_GetClassName(_con, out f.cClass)) return false;\n\t\t\n\t\tbool _GetClassName(wnd w, out string cn) {\n\t\t\tcn = w.ClassName; if (cn != null) return true;\n\t\t\t_winInfo.aaaText = \"Failed to get \" + (w == _wnd ? \"window\" : \"control\") + \" properties: \\r\\n\" + lastError.message;\n\t\t\t_scroller.Visibility = Visibility.Hidden;\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\t_noeventValueChanged = true;\n\t\t\n\t\tvar wndName = _wnd.NameTL_;\n\t\tif (newWindow) _k.Fill(_wnd, wndName, f.wClass, f.wProg = _wnd.ProgramName);\n\t\telse if (wndName != _wndName) _k.FillChangedName(wndName);\n\t\tf.wName = _wndName = wndName;\n\t\t\n\t\tif (isCon) {\n\t\t\t//name combo\n\t\t\tf.cName = _con.Name;\n\t\t\tint iSel = f.cName.NE() ? -1 : 0;\n\t\t\tvar an = new List<string> { TUtil.EscapeWildex(f.cName) };\n\t\t\t_ConNameAdd(\"***wfName \", f.cWF = _con.NameWinforms);\n\t\t\t/*bool isElm =*/\n\t\t\t_ConNameAdd(\"***elmName \", f.cElm = _con.NameElm);\n\t\t\t//bool isLabel = _ConNameAdd(\"***label \", f.cLabel = _con.NameLabel);\n\t\t\t//if(isElm && isLabel && iSel == an.Count - 2 && f.cElm == f.cLabel) iSel++; //if label == elmName, prefer label\n\t\t\tif (iSel < 0) iSel = 0; //never select text, even if all others unavailable\n\t\t\t_ConNameAdd(\"***text \", f.cText = _con.ControlText);\n\t\t\tbool _ConNameAdd(string prefix, string value) {\n\t\t\t\tif (value.NE()) return false;\n\t\t\t\tif (iSel < 0) iSel = an.Count;\n\t\t\t\tan.Add(prefix + TUtil.EscapeWildex(value));\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\t\n\t\t\tbool idUseful = TUtil.GetUsefulControlId(_con, _wnd, out f.cId);\n\t\t\t//idC.Visible = idUseful;\n\t\t\tidC.Set(idUseful, f.cId.ToS() + (idUseful ? null : \" /*probably not useful*/\"));\n\t\t\tstring sName = an[iSel], sClass = TUtil.StripWndClassName(f.cClass, true);\n\t\t\tnameC.Set(!idUseful, sName, an);\n\t\t\tclassC.Set(!idUseful, sClass);\n\t\t\tbool hiddenToo = !_con.IsVisible;\n\t\t\tcHiddenTooC.IsChecked = hiddenToo;\n\t\t\tvar skip = idUseful ? null : TUtil.GetControlSkip(_wnd, _con, sName, sClass, hiddenToo);\n\t\t\tskipC.Set(skip != null, skip);\n\t\t}\n\t\t\n\t\tbool checkControl = isCon && _checkControl;\n\t\t_cControl.IsChecked = checkControl;\n\t\t_ShowControlProperties(showGrid: checkControl, showAll: isCon);\n\t\t\n\t\t_noeventValueChanged = false;\n\t\t\n\t\t_scroller.Visibility = Visibility.Visible;\n\t\t_FillWindowInfo(f);\n\t\treturn true;\n\t}\n\t\n\tprivate void _cbFunc_SelectionChanged(object sender, SelectionChangedEventArgs e) {\n\t\tint iFunc = _cbFunc.SelectedIndex;\n\t\t_noeventValueChanged = true;\n\t\tvar vis = iFunc == 3 ? Visibility.Hidden : Visibility.Visible;\n\t\t_cActivate.Visibility = vis;\n\t\t_cException.Visibility = vis;\n\t\tif (iFunc is 1 or 2) _cActivate.IsChecked = true;\n\t\twaitW.Visible = iFunc is 0 or 2;\n\t\tif (iFunc == 2) {\n\t\t\tif (waitW.t.Text == \"1\") waitW.t.Text = \"30\";\n\t\t\twaitW.c.IsChecked = true;\n\t\t}\n\t\t_noeventValueChanged = false;\n\t\t_FormatCode();\n\t}\n\t\n\t//when checked/unchecked any checkbox, and when text changed of any textbox\n\tvoid _AnyCheckTextBoxValueChanged(object source) {\n\t\tif (source == _cCapture) {\n\t\t\t_cCapture_CheckedChanged();\n\t\t} else if (!_noeventValueChanged) {\n\t\t\t_noeventValueChanged = true;\n\t\t\tif (source is KCheckBox c) {\n\t\t\t\tbool on = c.IsChecked;\n\t\t\t\tif (c == _cControl) {\n\t\t\t\t\t_checkControl = on;\n\t\t\t\t\t_ShowControlProperties(on, null);\n\t\t\t\t}\n\t\t\t} else if (source is TextBox t && t.Tag is KCheckTextBox k) {\n\t\t\t\t_noeventValueChanged = _formattedOnValueChanged = false; //allow auto-check but prevent formatting twice\n\t\t\t\tk.CheckIfTextNotEmpty();\n\t\t\t\tif (_formattedOnValueChanged) return;\n\t\t\t}\n\t\t\t_noeventValueChanged = false;\n\t\t\t_formattedOnValueChanged = true;\n\t\t\t_FormatCode();\n\t\t}\n\t}\n\tbool _noeventValueChanged = true, _formattedOnValueChanged;\n\t\n\tvoid _ShowControlProperties(bool showGrid, bool? showAll) {\n\t\tif (showAll != null) {\n\t\t\tvar vis = showAll.Value ? Visibility.Visible : Visibility.Hidden;\n\t\t\t_cControl.Visibility = vis;\n\t\t\t_sepControl.Visibility = vis;\n\t\t\t_gCon1.Visibility = vis;\n\t\t\t_gCon2.Visibility = vis;\n\t\t}\n\t\t_gCon1.IsEnabled = showGrid;\n\t\t_gCon2.IsEnabled = showGrid;\n\t}\n\t\n\t(string code, string wndVar) _FormatCode(bool forTest = false) {\n\t\tif (!_scroller.IsVisible) return default; //still not captured, or failed to get window props\n\t\tint iFunc = _cbFunc.SelectedIndex;\n\t\t\n\t\tvar f = new TUtil.WindowFindCodeFormatter {\n\t\t\tTest = forTest,\n\t\t\tFinder = iFunc == 3,\n\t\t\tNeedControl = !_con.Is0 && _cControl.IsChecked,\n\t\t\tThrow = _cException.IsChecked,\n\t\t\tActivate = _cActivate.IsChecked,\n\t\t\thiddenTooW = cHiddenTooW.IsChecked,\n\t\t\tcloakedTooW = cCloakedTooW.IsChecked,\n\t\t\thiddenTooC = cHiddenTooC.IsChecked,\n\t\t};\n\t\t\n\t\tif (!forTest && iFunc is 1 or 2) {\n\t\t\tif (WndCopyData.SendReceive<char>(ScriptEditor.WndMsg_, 19, _wnd.Handle.ToS(), out string s1)) {\n\t\t\t\tif (iFunc == 1) f.orRunW = s1; else f.andRunW = s1;\n\t\t\t}\n\t\t}\n\t\t\n\t\t_k.GetResults(f);\n\t\tif (!forTest) waitW.GetText(out f.waitW, emptyToo: true);\n\t\t\n\t\tif (f.NeedControl) {\n\t\t\tnameC.GetText(out f.nameC, emptyToo: true);\n\t\t\tclassC.GetText(out f.classC);\n\t\t\tidC.GetText(out f.idC);\n\t\t\talsoC.GetText(out f.alsoC);\n\t\t\tskipC.GetText(out f.skipC);\n\t\t\tf.nameC_comments = nameC.t.Text;\n\t\t\tf.classC_comments = classC.t.Text;\n\t\t}\n\t\t\n\t\tvar R = f.Format();\n\t\t\n\t\tif (!forTest) {\n\t\t\t_code.AaSetText(R);\n\t\t}\n\t\t\n\t\treturn (R, \"w\");\n\t}\n\t\n\t#region capture\n\t\n\tTUtil.CapturingWithHotkey _capt;\n\t\n\tvoid _cCapture_CheckedChanged() {\n\t\t_capt ??= new(\n\t\t\t_cCapture,\n\t\t\to => (_noControl ? o.wTL : wnd.fromXY(o.p)).GetRect(out o.resultRect),\n\t\t\tnew(LA.App.Settings.delm.hk_capture, _Capture)\n\t\t\t);\n\t\t_capt.Capturing = _cCapture.IsChecked;\n\t}\n\t\n\tvoid _Capture() {\n\t\tvar c = wnd.fromMouse(); if (c.Is0) return;\n\t\t_con = c;\n\t\t_SetWnd(true);\n\t\tvar w = this.Hwnd();\n\t\tif (w.IsMinimized) {\n\t\t\tw.ShowNotMinMax();\n\t\t\tw.ActivateL();\n\t\t}\n\t}\n\t\n\t#endregion\n\t\n\t#region Insert, Test\n\t\n\t/// <summary>\n\t/// When OK clicked, the top-level window (even when <see cref=\"AaResultUseControl\"/> is true).\n\t/// </summary>\n\tpublic wnd AaResultWindow => _wnd;\n\t\n\t/// <summary>\n\t/// When OK clicked, the control (even when <see cref=\"AaResultUseControl\"/> is false) or default(wnd).\n\t/// </summary>\n\tpublic wnd AaResultControl => _con;\n\t\n\t/// <summary>\n\t/// When OK clicked, true if a control was selected and the 'Control' checkbox checked.\n\t/// Use <see cref=\"AaResultWindow\"/> or <see cref=\"AaResultControl\"/>, depending on this property.\n\t/// </summary>\n\tpublic bool AaResultUseControl { get; private set; }\n\t\n\t/// <summary>\n\t/// When OK clicked, contains C# code. Else null.\n\t/// </summary>\n\tpublic string AaResultCode { get; private set; }\n\t\n\t//rejected. Can use Closed event; then aaResultCode not null if OK.\n\t///// <summary>\n\t///// When closed with OK button.\n\t///// </summary>\n\t//public event Action OK;\n\t\n\tprivate void _Insert(WBButtonClickArgs e) {\n\t\tvar s = _code.aaaText.NullIfEmpty_();\n\t\t\n\t\tif (_dontInsert) {\n\t\t\tAaResultCode = s;\n\t\t\tif (s == null) e.Cancel = true;\n\t\t\telse AaResultUseControl = !_con.Is0 && _cControl.IsChecked;\n\t\t\t\n\t\t\tif (this.IsModal_() == false) Close();\n\t\t\telse DialogResult = s != null;\n\t\t} else if (_close) {\n\t\t\tClose();\n\t\t} else if (s != null) {\n\t\t\tLA.InsertCode.Statements(new(s, makeVarName1: true));\n\t\t\t_close = true;\n\t\t\t_bInsert.Content = \"Close\";\n\t\t\t_bInsert.MouseLeave += (_, _) => {\n\t\t\t\t_close = !true;\n\t\t\t\t_bInsert.Content = \"Insert\";\n\t\t\t};\n\t\t}\n\t}\n\tbool _close;\n\t\n\tprivate void _bTest_Click(WBButtonClickArgs ea) {\n\t\tvar (code, wndVar) = _FormatCode(true); if (code.NE()) return;\n\t\tvar rr = TUtil.RunTestFindObject(this, code, wndVar, _wnd, getRect: o => {\n\t\t\tvar w = (wnd)o;\n\t\t\tvar r = w.Rect;\n\t\t\tif (w.IsMaximized && !w.IsChild) {\n\t\t\t\tvar k = w.Screen.Rect; k.Inflate(-2, -2);\n\t\t\t\tr.Intersect(k);\n\t\t\t}\n\t\t\treturn r;\n\t\t});\n\t\t_info.InfoErrorOrInfo(rr.info);\n\t}\n\t\n\t#endregion\n\t\n\t#region tree\n\t\n\tvoid _InitTree() {\n\t\t_tree.SingleClickActivate = true;\n\t\t_tree.ItemActivated += e => {\n\t\t\tvar x = e.Item as _TreeItem;\n\t\t\t_con = x.c == _wnd ? default : x.c;\n\t\t\tif (!_FillProperties(false)) return;\n\t\t\t_FormatCode();\n\t\t\tif (!_con.Is0) TUtil.ShowOsdRect(_con.Rect);\n\t\t};\n\t}\n\t\n\tvoid _ClearTree() {\n\t\t_tree.SetItems(null);\n\t}\n\t\n\tvoid _FillTree() {\n\t\t_TreeItem xWindow = new() { c = _wnd }, xSelect = _con.Is0 ? xWindow : null;\n\t\t_AddChildren(_wnd, xWindow);\n\t\tvoid _AddChildren(wnd wParent, _TreeItem nParent) {\n\t\t\tfor (wnd t = wParent.Get.FirstChild; !t.Is0; t = t.Get.Next()) {\n\t\t\t\tvar x = new _TreeItem() { c = t };\n\t\t\t\tnParent.AddChild(x);\n\t\t\t\tif (t == _con && xSelect == null) xSelect = x;\n\t\t\t\t_AddChildren(t, x);\n\t\t\t}\n\t\t}\n\t\t\n\t\t_tree.SetItems(new _TreeItem[1] { xWindow });\n\t\t\n\t\t//print.it(xWindow.Descendants().Select(o=>o.c));\n\t\t\n\t\tif (xSelect != null) _tree.SelectSingle(xSelect);\n\t}\n\t\n\tclass _TreeItem : TreeBase<_TreeItem>, ITreeViewItem {\n\t\tpublic wnd c;\n\t\tstring _displayText;\n\t\tbool _isExpanded;\n\t\tbool _isFailed;\n\t\t\n\t\t#region ITreeViewItem\n\t\t\n\t\tstring ITreeViewItem.DisplayText {\n\t\t\tget {\n\t\t\t\tif (_displayText == null) {\n\t\t\t\t\tvar cn = c.ClassName;\n\t\t\t\t\tif (cn == null) {\n\t\t\t\t\t\t_isFailed = true;\n\t\t\t\t\t\treturn _displayText = \"Failed: \" + lastError.message;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tvar name = c.Name;\n\t\t\t\t\tif (name.NE()) _displayText = cn;\n\t\t\t\t\telse {\n\t\t\t\t\t\tusing (new StringBuilder_(out var b)) {\n\t\t\t\t\t\t\tname = name.Escape(limit: 250);\n\t\t\t\t\t\t\tb.Append(cn).Append(\"  \\\"\").Append(name).Append('\"');\n\t\t\t\t\t\t\t_displayText = b.ToString();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn _displayText;\n\t\t\t}\n\t\t}\n\t\t\n\t\tvoid ITreeViewItem.SetIsExpanded(bool yes) { _isExpanded = yes; }\n\t\t\n\t\tbool ITreeViewItem.IsExpanded => _isExpanded;\n\t\t\n\t\tIEnumerable<ITreeViewItem> ITreeViewItem.Items => base.Children();\n\t\t\n\t\tbool ITreeViewItem.IsFolder => _IsFolder;\n\t\tbool _IsFolder => base.HasChildren;\n\t\t\n\t\tobject ITreeViewItem.Image => _IsFolder ? LA.EdIcons.FolderArrow(_isExpanded) : null;\n\t\t\n\t\tint ITreeViewItem.TextColor(TVColorInfo ci)\n\t\t\t=> _isFailed ? 0xff\n\t\t\t: !c.IsVisible ? ColorInt.SwapRB(Api.GetSysColor(Api.COLOR_GRAYTEXT))\n\t\t\t: -1;\n\t\t\n\t\t#endregion\n\t}\n\t\n\t#endregion\n\t\n\t#region info\n\t\n\tstruct _WinInfo {\n\t\tpublic string wClass, wName, wProg, cClass, cName, cText, /*cLabel,*/ cElm, cWF;\n\t\tpublic int cId;\n\t\t\n\t\tpublic string Format(wnd w, wnd c, int wcp) {\n\t\t\tvar b = new StringBuilder();\n\t\t\t\n\t\t\tif (wcp == 2 && c.Is0) wcp = 1;\n\t\t\tif (!w.IsAlive) return \"\";\n\t\t\tif (wcp == 1) { //window\n\t\t\t\tb.AppendLine(\"<lc #B0E0B0><b>Window<>    <+switch 2>Control<>    <+switch 3>Process<><>\");\n\t\t\t\tif (wClass == null) {\n\t\t\t\t\twClass = w.ClassName;\n\t\t\t\t\twName = w.Name;\n\t\t\t\t}\n\t\t\t\t_Common(false, b, w, wName, wClass);\n\t\t\t} else if (wcp == 2) { //control\n\t\t\t\tb.AppendLine(\"<lc #B0E0B0><+switch 1>Window<>    <b>Control<>    <+switch 3>Process<><>\");\n\t\t\t\tif (c.IsAlive) {\n\t\t\t\t\tif (cClass == null) {\n\t\t\t\t\t\tcClass = c.ClassName;\n\t\t\t\t\t\tcName = c.Name;\n\t\t\t\t\t\tcText = c.ControlText;\n\t\t\t\t\t\t//cLabel = c.NameLabel;\n\t\t\t\t\t\tcElm = c.NameElm;\n\t\t\t\t\t\tcWF = c.NameWinforms;\n\t\t\t\t\t\tcId = c.ControlId;\n\t\t\t\t\t}\n\t\t\t\t\t_Common(true, b, c, cName, cClass);\n\t\t\t\t}\n\t\t\t} else { //program\n\t\t\t\tb.AppendLine(\"<lc #B0E0B0><+switch 1>Window<>    <+switch 2>Control<>    <b>Process<><>\");\n\t\t\t\tg1:\n\t\t\t\tif (wProg == null) {\n\t\t\t\t\twProg = w.ProgramName;\n\t\t\t\t}\n\t\t\t\tb.Append(\"<i>ProgramName<>:    \").AppendLine(wProg);\n\t\t\t\tb.Append(\"<i>ProgramPath<>:    \").AppendLine(w.ProgramPath);\n\t\t\t\tb.Append(\"<i>ProgramDescription<>:    \").AppendLine(w.ProgramDescription);\n\t\t\t\tint pid = w.ProcessId, tid = w.ThreadId;\n\t\t\t\tb.Append(\"<i>ProcessId<>:    \").AppendLine(pid.ToString());\n\t\t\t\tb.Append(\"<i>ThreadId<>:    \").AppendLine(tid.ToString());\n\t\t\t\tb.Append(\"<i>Is32Bit<>:    \").AppendLine(w.Is32Bit ? \"true\" : \"false\");\n\t\t\t\tusing (var uac = uacInfo.ofProcess(pid)) {\n\t\t\t\t\tb.Append(\"<i><help articles/UAC>UAC<> IL, elevation<>:    \")\n\t\t\t\t\t\t.Append(uac.IntegrityLevel.ToString())\n\t\t\t\t\t\t.Append(\", \").AppendLine(uac.Elevation.ToString());\n\t\t\t\t}\n\t\t\t\tb.Append(\"<i>CommandLine<>:    \").AppendLine(process.getCommandLine(pid));\n\t\t\t\t\n\t\t\t\t//if control's process or thread is different...\n\t\t\t\tif (!c.Is0) {\n\t\t\t\t\tint pid2 = c.ProcessId;\n\t\t\t\t\tif (pid2 != pid && pid2 != 0) {\n\t\t\t\t\t\tb.AppendLine(\"\\r\\n<c red>Control is in other process:<>\");\n\t\t\t\t\t\tw = c; wProg = null;\n\t\t\t\t\t\tgoto g1;\n\t\t\t\t\t}\n\t\t\t\t\tint tid2 = c.ThreadId;\n\t\t\t\t\tif (tid2 != tid && tid2 != 0) {\n\t\t\t\t\t\tb.AppendLine(\"\\r\\n<c red>Control is in other thread:<>\");\n\t\t\t\t\t\tb.Append(\"<i>ThreadId<>:    \").AppendLine(tid2.ToString());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\treturn b.ToString();\n\t\t}\n\t\t\n\t\tvoid _Common(bool isCon, StringBuilder b, wnd w, string name, string className) {\n\t\t\tstring s, sh = w.Handle.ToString();\n\t\t\tb.Append(\"<i>Handle<>:    \").AppendLine(sh);\n\t\t\tb.Append(\"<i>ClassName<>:    \").AppendLine(className);\n\t\t\tif (!isCon || !name.NE()) b.Append(\"<i>Name<>:    \").AppendLine(name);\n\t\t\tif (isCon) {\n\t\t\t\t//if(!cLabel.NE()) b.Append(\"<i>NameLabel<>:    \").AppendLine(cLabel);\n\t\t\t\tif (!cElm.NE()) b.Append(\"<i>NameElm<>:    \").AppendLine(cElm);\n\t\t\t\tif (!cWF.NE()) b.Append(\"<i>NameWinForms<>:    \").AppendLine(cWF);\n\t\t\t\tif (!cText.NE()) b.Append(\"<i>ControlText<>:    \").Append(\"<\\a>\").Append(cText.Escape(10000, true)).AppendLine(\"</\\a>\");\n\t\t\t\tb.Append(\"<i>ControlId<>:    \").AppendLine(cId.ToString());\n\t\t\t\tb.AppendFormat(\"<+rect {0}><i>RectInWindow<><>:    \", sh).AppendLine(w.RectInWindow.ToString());\n\t\t\t} else {\n\t\t\t\tvar wo = w.Get.Owner;\n\t\t\t\tif (!wo.Is0) b.AppendFormat(\"<+rect {0}><i>Owner<><>:    \", wo.Handle.ToString()).AppendLine(wo.ToString());\n\t\t\t\tb.AppendFormat(\"<+rect {0}><i>Rect<><>:    \", sh).AppendLine(w.Rect.ToString());\n\t\t\t}\n\t\t\tb.AppendFormat(\"<+rect {0} 1><i>ClientRect<><>:    \", sh).AppendLine(w.ClientRect.ToString());\n\t\t\tvar style = w.Style;\n\t\t\ts = (style & (WS)0xffff0000).ToString();\n\t\t\tif (isCon) s = s.Replace(\"MINIMIZEBOX\", \"GROUP\").Replace(\"MAXIMIZEBOX\", \"TABSTOP\"); else s = s.Replace(\"GROUP\", \"MINIMIZEBOX\").Replace(\"TABSTOP\", \"MAXIMIZEBOX\");\n\t\t\tuint style2 = ((uint)style) & 0xffff; //unknown styles of that class\n\t\t\tb.Append(\"<i>Style<>:  0x\").Append(((uint)style).ToString(\"X8\")).Append(\" (\").Append(s);\n\t\t\tif (style2 != 0) b.Append(\", 0x\").Append(style2.ToString(\"X4\"));\n\t\t\tb.AppendLine(\")\");\n\t\t\tvar estyle = w.ExStyle;\n\t\t\tb.Append(\"<i>ExStyle<>:  0x\").Append(((uint)estyle).ToString(\"X8\")).Append(\" (\").Append(estyle.ToString()).AppendLine(\")\");\n\t\t\t//b.Append(\"<i>Class style<>:  0x\").AppendLine(((uint)WndUtil.GetClassLong(w, GCL.STYLE)).ToString(\"X8\"));\n\t\t\tif (!isCon) {\n\t\t\t\tb.Append(\"<i>Is...<>:    \");\n\t\t\t\t_AppendIs(w.IsPopupWindow, \"IsPopupWindow\");\n\t\t\t\t_AppendIs(w.IsToolWindow, \"IsToolWindow\");\n\t\t\t\t_AppendIs(w.IsTopmost, \"IsTopmost\");\n\t\t\t\t_AppendIs(w.IsFullScreen, \"IsFullScreen\");\n\t\t\t\t_AppendIs(0 != w.IsUwpApp, \"IsUwpApp\");\n\t\t\t\t_AppendIs(w.IsWindows8MetroStyle, \"IsWindows8MetroStyle\");\n\t\t\t\tb.AppendLine();\n\t\t\t}\n\t\t\tb.Append(\"<i>Prop[\\\"...\\\"]<>:    \"); bool isProp = false;\n\t\t\tforeach (var p in w.Prop.GetList()) {\n\t\t\t\tif (p.Key.Starts('#')) continue;\n\t\t\t\tif (!isProp) isProp = true; else b.Append(\", \");\n\t\t\t\tb.Append(p.Key).Append(\" = \").Append(p.Value.ToString());\n\t\t\t}\n\t\t\tb.AppendLine();\n\t\t\t\n\t\t\tvoid _AppendIs(bool yes, string prop) {\n\t\t\t\tif (b[^1] != ' ') b.Append(\", \");\n\t\t\t\tif (!yes) b.Append('!');\n\t\t\t\tb.Append(prop);\n\t\t\t}\n\t\t}\n\t}\n\t\n\tvoid _FillWindowInfo(in _WinInfo f) {\n\t\tif (_wiWCP == 0) {\n\t\t\t_wiWCP = 1;\n\t\t\t_winInfo.AaTags.AddLinkTag(\"+switch\", s => {\n\t\t\t\t_wiWCP = s.ToInt();\n\t\t\t\t_SetText(default);\n\t\t\t});\n\t\t\t_winInfo.AaTags.AddLinkTag(\"+rect\", s => {\n\t\t\t\tvar w = (wnd)s.ToInt(0, out int e);\n\t\t\t\tint client = s.ToInt(e);\n\t\t\t\tvar r = client == 1 ? w.ClientRectInScreen : w.Rect;\n\t\t\t\tTUtil.ShowOsdRect(r, limitToScreen: w.IsMaximized);\n\t\t\t});\n\t\t}\n\t\t_SetText(f);\n\t\t\n\t\tvoid _SetText(in _WinInfo wi) {\n\t\t\tvar s1 = wi.Format(_wnd, _con, _wiWCP);\n\t\t\t_winInfo.aaaText = s1.TrimEnd(\"\\r\\n\");\n\t\t}\n\t}\n\tint _wiWCP; //0 not inited, 1 window, 2 control, 3 program\n\t\n\tTUtil.CommonInfos _commonInfos;\n\tvoid _InitInfo() {\n\t\t_commonInfos = new TUtil.CommonInfos(_info);\n\t\t\n\t\t_info.aaaText = _dialogInfo;\n\t\t_info.AaAddElem(this, _dialogInfo);\n\t\tTUtil.CapturingWithHotkey.RegisterLink_DialogHotkey(_info);\n\t\t\n\t\t_k.InitInfo(_info);\n\t\t_info.InfoCT(idC, \"Control id.\");\n\t\t_info.InfoCT(nameC, \"Control name.\", true);\n\t\t_info.InfoCT(classC, \"Control class name.\", true);\n\t\t\n\t\t_info.InfoC(cHiddenTooW, \"Flag <help>Au.Types.WFlags<>.HiddenToo.\");\n\t\t_info.InfoC(cCloakedTooW, \"Flag <help>Au.Types.WFlags<>.CloakedToo.\");\n\t\t_info.InfoCT(waitW,\n@\"The wait timeout, seconds.\nThe function waits for such window max this time interval. On timeout throws exception if <b>Fail...<> checked, else returns default(wnd). If empty, uses 8e88 (infinite).\");\n\t\t_info.InfoC(cHiddenTooC, \"Flag <help>Au.Types.WCFlags<>.HiddenToo.\");\n\t\t_info.InfoCT(alsoC,\n@\"<help>wnd.Child<> \" + TUtil.CommonInfos.c_alsoParameter);\n\t\t_info.InfoCT(skipC,\n@\"0-based index of matching control.\nFor example, if 1, gets the second matching control.\");\n\t\t\n\t\t_info.Info(_tree, \"Tree view\", \"All child/descendant controls in the window.\");\n\t\t\n\t\t//TODO3: now no info for HwndHost\n\t\t//\t\t\t_Info(_code, \"Code\",\n\t\t//@\"Created code to find the window or control.\n\t\t//Some parts can be edited directly.\n\t\t//\");\n\t\t//\t\t\t_Info(_winInfo, \"Window info\",\n\t\t//@\"Various properties of the selected window, control and process.\n\t\t//For example can be useful when creating <i>also<> function.\n\t\t//\");\n\t}\n\t\n\tstring _dialogInfo =\n$@\"This tool creates code to find <help wnd.find>window<> or <help wnd.Child>control<>.\n1. Move the mouse to a window or control. Press <+hotkey>hotkey<> <b>{LA.App.Settings.delm.hk_capture}<>.\n2. Click the Test button to see how the 'find' code works.\n3. If need, change some fields or select another window/control.\n4. Click Insert. Click Close, or capture/insert again.\n5. If need, edit the code in editor. For example rename variables, delete duplicate wnd.find lines, replace part of window name with *. Add code to use the window or control. Examples: w.Activate(); var s = w.Name;.\n\n{(uacInfo.isAdmin ? \"\" : \"If the hotkey does not work when the target window is active, probably its process is admin and this process isn't.\")}\";\n\t\n\t#endregion\n}\n"
  },
  {
    "path": "Au.Editor/Tools/InfoWindow.cs",
    "content": "using Au.Controls;\nusing System.Windows.Controls;\nusing System.Windows;\n\nnamespace ToolLand;\n\n/// <summary>\n/// <see cref=\"KPopup\"/>-based info window with 1 or 2 scintilla controls (<see cref=\"KSciInfoBox\"/>) with output tags etc.\n/// You can set text, resize and show/hide/dispose it many times.\n/// User can middle-click to hide.\n/// </summary>\nclass InfoWindow : KPopup {\n\tDockPanel _panel;\n\t_InfoBox _c, _c2;\n\n\t/// <param name=\"split\">If not 0, sets <b>Control1.Width</b>=<i>split</i> and adds <b>Control2</b>.</param>\n\t/// <param name=\"titleBar\">With title bar.</param>\n\tpublic InfoWindow(int split, bool titleBar = true) : base(titleBar ? WS.THICKFRAME | WS.POPUP | WS.CAPTION | WS.SYSMENU : WS.THICKFRAME | WS.POPUP) {\n\t\tContent = _panel = new();\n\t\t_panel.Children.Add(_c = new());\n\t\tif (split > 0) {\n\t\t\t_c.Width = split;\n\t\t\t_panel.Children.Add(_c2 = new() { Name = \"info_2\" });\n\t\t}\n\t}\n\n\t/// <summary>\n\t/// The child control. Displays text.\n\t/// </summary>\n\tpublic KSciInfoBox Control1 => _c;\n\n\t/// <summary>\n\t/// The second child control. Displays text.\n\t/// </summary>\n\tpublic KSciInfoBox Control2 => _c2;\n\n\t/// <summary>\n\t/// Text with output tags.\n\t/// </summary>\n\tpublic string Text {\n\t\tget => _c?.aaaText;\n\t\tset => Control1.aaaText = value;\n\t}\n\n\t/// <summary>\n\t/// Text of second control with output tags.\n\t/// </summary>\n\tpublic string Text2 {\n\t\tget => _c2?.aaaText;\n\t\tset => Control2.aaaText = value;\n\t}\n\n\t/// <summary>\n\t/// A text control in which to insert the link text when clicked.\n\t/// If null, uses the focused control.\n\t/// </summary>\n\tpublic FrameworkElement InsertInControl { get; set; }\n\n\tclass _InfoBox : KSciInfoBox {\n\t\tpublic _InfoBox() {\n\t\t\t//_t = t;\n\t\t\tthis.AaInitUseSystemFont = true;\n\t\t\tthis.AaInitBlankMargins = (4, 4);\n\t\t}\n\n\t\t//protected override void aaOnHandleCreated() {\n\t\t//\tbase.aaOnHandleCreated();\n\t\t//\t//base.NoMouseLeftSetFocus = true; //no, then cannot scroll with wheel on Win7-8.1\n\t\t//\t//base.NoMouseRightSetFocus = true;\n\t\t//}\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Tools/KSciCodeBox.cs",
    "content": "using Au.Controls;\n\nnamespace ToolLand;\n\n/// <summary>\n/// Scintilla-based control that shows colored C# code.\n/// Also can be used anywhere to edit styled C# code. To make editable and set text use <see cref=\"AaSetText\"/> with readonlyFrom=-1.\n/// </summary>\nclass KSciCodeBox : KScintilla {\n\tpublic KSciCodeBox() {\n\t\tAaInitUseDefaultContextMenu = true;\n\t\t//aaInitBorder = true; //no, the native border is of different color and thickness (high DPI) than other WPF controls\n\t\tName = \"code\";\n\t}\n\t\n\tprotected override void AaOnHandleCreated() {\n\t\tbase.AaOnHandleCreated();\n\t\t\n\t\taaaMarginSetWidth(1, 0);\n\t\taaaIsReadonly = true;\n\t\tLA.SciTheme.Current.ToScintilla(this, fontSize: 9);\n\t}\n\t\n\tprotected override void AaOnSciNotify(ref Sci.SCNotification n) {\n\t\t//switch(n.nmhdr.code) {\n\t\t//case Sci.NOTIF.SCN_PAINTED: case Sci.NOTIF.SCN_UPDATEUI: break;\n\t\t//default: print.it(n.nmhdr.code, n.modificationType); break;\n\t\t//}\n\t\t\n\t\tswitch (n.code) {\n\t\tcase Sci.NOTIF.SCN_UPDATEUI:\n\t\t\t//make text after _ReadonlyStartUtf8 readonly\n\t\t\tif (n.updated.Has(Sci.UPDATE.SC_UPDATE_SELECTION)) { //selection changed\n\t\t\t\tif (_readonlyLenUtf8 > 0) {\n\t\t\t\t\tint i = Call(Sci.SCI_GETSELECTIONEND);\n\t\t\t\t\taaaIsReadonly = i > _ReadonlyStartUtf8 || _LenUtf8 == 0; //small bug: if caret is at the boundary, allows to delete readonly text, etc.\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\tcase Sci.NOTIF.SCN_STYLENEEDED:\n\t\t\t_Styling();\n\t\t\tbreak;\n\t\t}\n\t\t\n\t\tbase.AaOnSciNotify(ref n);\n\t}\n\t\n\t/// <summary>\n\t/// Sets text and makes all or part of it readonly.\n\t/// </summary>\n\t/// <param name=\"s\"></param>\n\t/// <param name=\"readonlyFrom\">If 0, makes all text readonly. If s.Length or -1, makes all text editable. If between 0 and s.Length, makes readonly from this position.</param>\n\tpublic void AaSetText(string s, int readonlyFrom = 0) {\n\t\taaaIsReadonly = false;\n\t\taaaSetText(s, SciSetTextFlags.NoUndoNoNotify);\n\t\tif (readonlyFrom > 0) {\n\t\t\t_readonlyLenUtf8 = _LenUtf8 - aaaPos8(readonlyFrom);\n\t\t} else if (readonlyFrom < 0) {\n\t\t\t_readonlyLenUtf8 = 0;\n\t\t} else {\n\t\t\taaaIsReadonly = true;\n\t\t\t_readonlyLenUtf8 = -1;\n\t\t}\n\t}\n\t\n\tpublic int AaReadonlyStart => _readonlyLenUtf8 < 0 ? 0 : aaaPos16(_ReadonlyStartUtf8);\n\t\n\tprotected int _readonlyLenUtf8;\n\t\n\tprotected int _ReadonlyStartUtf8 => _readonlyLenUtf8 < 0 ? 0 : _LenUtf8 - _readonlyLenUtf8;\n\t\n\tprotected int _LenUtf8 => Call(Sci.SCI_GETTEXTLENGTH);\n\t\n\tunsafe void _Styling() {\n\t\tif (LA.CiUtil.GetScintillaStylingBytes8(aaaText) is not { } styles8) return;\n\t\tCall(Sci.SCI_STARTSTYLING);\n\t\tfixed (byte* p = styles8) Call(Sci.SCI_SETSTYLINGEX, styles8.Length, p);\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Tools/KSciCodeBoxWnd.cs",
    "content": "using Au.Controls;\nusing System.Windows;\nusing Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp;\nusing Microsoft.CodeAnalysis.CSharp.Syntax;\n\nnamespace ToolLand;\n\n/// <summary>\n/// Scintilla-based control that shows colored C# code. Based on <see cref=\"KSciCodeBox\"/> and adds methods to get code for wnd.find.\n/// </summary>\nclass KSciCodeBoxWnd : KSciCodeBox {\n\t/// <summary>\n\t/// Returns code to find window w and optionally control con in it. Without newline at the end.\n\t/// If w/con is same as previous and code of this control is modified and valid, gets code from this code control, from the start to aaReadonlyStart.\n\t/// Else creates code \"var w = wnd.find(...);\". If w is invalid, creates code \"wnd w = default;\".\n\t/// The returned wndVar is final wnd variable name (of window or control).\n\t/// </summary>\n\tpublic (string code, string wndVar) AaGetWndFindCode(bool test, wnd w, wnd con = default, bool private1 = false) {\n\t\tif (test) { //remove 'wait' and 'activate' from wnd.find and wnd.Child. If no 'wait', insert 0 to throw notfoundexception.\n\t\t\tvar k = AaGetWndFindCode(false, w, con, private1: true);\n\t\t\tvar s = k.code;\n\t\t\tvar p = _ParseWndFind(s, test: true);\n\t\t\tif (p?.wVar != null) {\n\t\t\t\tvoid _Replace(int end, int argsStart, int argsEnd, int nameStart, bool orRun = false, bool orRunReplace = false, int funcNameEnd = 0) {\n\t\t\t\t\tif (orRun && !orRunReplace) return;\n\t\t\t\t\ts = s.ReplaceAt(argsEnd..end, \");\"); //remove '.Activate()' etc. If orRun, removes run etc arguments.\n\t\t\t\t\ts = s.ReplaceAt(argsStart..nameStart, nameStart < argsEnd ? \"0, \" : \"0\"); //remove 'wait, ' and add '0, ' (to throw NotFoundException)\n\t\t\t\t\tif (orRun) s = s.Remove(funcNameEnd - 5, 5); //findOrRun -> find\n\t\t\t\t}\n\t\t\t\tif (p.cVar != null) _Replace(p.cEnd, p.cArgsStart, p.cArgsEnd, p.cNameStart);\n\t\t\t\t_Replace(p.wEnd, p.wArgsStart, p.wArgsEnd, p.wNameStart, p.orRun, p.orRunReplace, p.wFuncNameEnd);\n\t\t\t}\n\t\t\t//print.it(s);\n\t\t\tk.code = s;\n\t\t\treturn k;\n\t\t}\n\n\t\tstring R = null, sCode = null, wndVar = \"w\", conVar = \"c\";\n\n\t\tif (w != _wnd) _userModified = false; else if (!_userModified) _userModified = 0 != Call(Sci.SCI_GETMODIFY);\n\t\tif (!w.Is0) {\n\t\t\tif (_userModified) {\n\t\t\t\tsCode = aaaRangeText(false, 0, _ReadonlyStartUtf8);\n\t\t\t\tvar p = _ParseWndFind(sCode, test: false);\n\t\t\t\tif (p?.wVar != null) {\n\t\t\t\t\tbool isConCode = p.cVar != null;\n\t\t\t\t\t//print.it(isConCode);\n\t\t\t\t\tif (con == _con && !con.Is0 == isConCode) {\n\t\t\t\t\t\t//print.it(isConCode ? \"same control\" : \"no control\");\n\t\t\t\t\t\tif (!private1 && p.wName != null) {\n\t\t\t\t\t\t\t//if window name changed and does not match the name in code, change it in code\n\t\t\t\t\t\t\tvar name = w.NameTL_;\n\t\t\t\t\t\t\tif (name != null && !new wildex(p.wName, noException: true).Match(name)) {\n\t\t\t\t\t\t\t\tvar s = TUtil.EscapeWindowName(name, true);\n\t\t\t\t\t\t\t\tif (!(TUtil.IsVerbatim(s, out _) || TUtil.MakeVerbatim(ref s))) s = s.Escape(quote: true);\n\t\t\t\t\t\t\t\tsCode = sCode.ReplaceAt(p.wNameStart..p.wNameEnd, s);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn (sCode, p.cVar ?? p.wVar);\n\t\t\t\t\t}\n\t\t\t\t\twndVar = p.wVar;\n\t\t\t\t\tif (isConCode) sCode = sCode[..p.cStart];\n\t\t\t\t\tif (con.Is0) {\n\t\t\t\t\t\t//print.it(\"remove control\");\n\t\t\t\t\t\t_con = default;\n\t\t\t\t\t\treturn (sCode, wndVar);\n\t\t\t\t\t}\n\t\t\t\t\tif (isConCode) conVar = p.cVar;\n\t\t\t\t\t//print.it(isConCode ? \"replace control\" : \"add control\");\n\t\t\t\t} else sCode = null;\n\t\t\t}\n\n\t\t\tvar f = new TUtil.WindowFindCodeFormatter {\n\t\t\t\tThrow = true,\n\t\t\t\twaitW = \"1\",\n\t\t\t\tVarWindow = wndVar,\n\t\t\t\tVarControl = conVar,\n\t\t\t};\n\n\t\t\tif (sCode != null) {\n\t\t\t\tf.CodeBefore = sCode;\n\t\t\t\tf.NeedWindow = false;\n\t\t\t} else if (w.ClassName is string cls) {\n\t\t\t\tf.nameW = TUtil.EscapeWindowName(w.NameTL_, true);\n\t\t\t\tf.classW = TUtil.StripWndClassName(cls, true);\n\t\t\t\tif (f.nameW.NE()) f.programW = w.ProgramName;\n\t\t\t\tf.hiddenTooW = !w.IsVisible;\n\t\t\t\tf.cloakedTooW = w.IsCloaked;\n\t\t\t} else {\n\t\t\t\tcon = default;\n\t\t\t\tf.NeedWindow = false;\n\t\t\t}\n\n\t\t\tif (!con.Is0) {\n\t\t\t\tbool isId = TUtil.GetUsefulControlId(con, w, out int id);\n\t\t\t\tstring cls = con.ClassName;\n\t\t\t\tif (isId || cls != null) {\n\t\t\t\t\tf.NeedControl = true;\n\t\t\t\t\twndVar = conVar;\n\t\t\t\t\tif (isId) {\n\t\t\t\t\t\tf.idC = id.ToS();\n\t\t\t\t\t\tf.classC_comments = cls;\n\t\t\t\t\t\tf.nameC_comments = con.Name;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tf.classC = TUtil.StripWndClassName(cls, true);\n\t\t\t\t\t\tstring name = con.Name, prefix = null;\n\t\t\t\t\t\tif (name.NE()) {\n\t\t\t\t\t\t\tname = con.NameWinforms;\n\t\t\t\t\t\t\tif (!name.NE()) prefix = \"***wfName \";\n\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\tvar nameElm = con.NameElm;\n\t\t\t\t\t\t\t\t//var nameLabel = con.NameLabel;\n\t\t\t\t\t\t\t\tif (!nameElm.NE()/* || !nameLabel.NE()*/) {\n\t\t\t\t\t\t\t\t\t//if(nameAcc.NE() || nameLabel == nameAcc) {\n\t\t\t\t\t\t\t\t\t//\tname = nameLabel; prefix = \"***label \";\n\t\t\t\t\t\t\t\t\t//} else {\n\t\t\t\t\t\t\t\t\tname = nameElm; prefix = \"***elmName \";\n\t\t\t\t\t\t\t\t\t//}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (wildex.hasWildcardChars(name)) name = \"**t \" + name;\n\n\t\t\t\t\t\tf.nameC = prefix + name;\n\t\t\t\t\t\tf.hiddenTooC = !con.IsVisible;\n\t\t\t\t\t\tf.SetSkipC(w, con);\n\t\t\t\t\t}\n\t\t\t\t} else con = default;\n\t\t\t}\n\n\t\t\tR = f.Format();\n\t\t}\n\n\t\tif (R == null) {\n\t\t\t_wnd = default; _con = default;\n\t\t\treturn (\"wnd w = default;\", \"w\");\n\t\t}\n\t\t_wnd = w; _con = con;\n\t\treturn (R, wndVar);\n\t}\n\twnd _wnd, _con;\n\tbool _userModified;\n\n\trecord _WndFindParsing {\n\t\t//var w = wnd.find...\n\t\tpublic string wVar, wName;\n\t\tpublic int wEnd, wArgsStart, wArgsEnd, wNameStart, wNameEnd, wFuncNameEnd;\n\t\tpublic bool orRun, orRunReplace;\n\t\t//var c = w.Child...\n\t\tpublic string cVar;\n\t\tpublic int cStart, cEnd, cArgsStart, cArgsEnd, cNameStart;\n\t}\n\n\t//Parses var w = wnd.find(...)..., and var c = w.Child(...)... if exists.\n\t//Gets only strings and offsets needed for replacements.\n\tstatic _WndFindParsing _ParseWndFind(string code, bool test) {\n\t\tif (code.NE()) return null;\n\t\tvar p = new _WndFindParsing();\n\t\tvar cu = CSharpSyntaxTree.ParseText(code, new CSharpParseOptions(LanguageVersion.Preview)).GetCompilationUnitRoot();\n\t\tforeach (var g1 in cu.Members) {\n\t\t\tif (g1 is not GlobalStatementSyntax g) break;\n\t\t\tif (g.Statement is LocalDeclarationStatementSyntax lds && lds.Declaration.Type.ToString() is \"var\" or \"wnd\") {\n\t\t\t\tvar v = lds.Declaration.Variables[0];\n\t\t\t\tif (v.ArgumentList != null) continue; //array\n\t\t\t\tif (v.Initializer.Value is InvocationExpressionSyntax ies && ies.Expression is MemberAccessExpressionSyntax mae) {\n\t\t\t\t\twhile (mae.Expression is InvocationExpressionSyntax ies2 && ies2.Expression is MemberAccessExpressionSyntax mae2) { ies = ies2; mae = mae2; } //eg when with '.Activate()'\n\t\t\t\t\tstring s1 = mae.Expression.ToString(), s2 = mae.Name.ToString();\n\t\t\t\t\tbool isW = p.wVar == null;\n\t\t\t\t\tif (isW ? (s1 == \"wnd\" && s2 == \"find\" || (p.orRun = s2 == \"findOrRun\")) : (s1 == p.wVar && s2 == \"Child\")) {\n\t\t\t\t\t\tvar al = ies.ArgumentList;\n\t\t\t\t\t\tint argsStart = al.OpenParenToken.Span.End, argsEnd = al.CloseParenToken.SpanStart, nameStart = argsStart;\n\t\t\t\t\t\tvar a = al.Arguments;\n\t\t\t\t\t\tif (a.Count > 0) {\n\t\t\t\t\t\t\tint iName = 0;\n\t\t\t\t\t\t\t//wait. Never mind: also can be +, ~, (cast), double.Constant, etc.\n\t\t\t\t\t\t\tif (!p.orRun && a[0].Expression.Kind() is SyntaxKind.NumericLiteralExpression or SyntaxKind.UnaryMinusExpression) {\n\t\t\t\t\t\t\t\tnameStart = a.Count > 1 ? a[1].SpanStart : argsEnd;\n\t\t\t\t\t\t\t\tiName = a.Count > 1 ? 1 : -1;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (isW && iName >= 0 && a[iName].Expression is LiteralExpressionSyntax les && les.IsKind(SyntaxKind.StringLiteralExpression)) {\n\t\t\t\t\t\t\t\tp.wName = les.Token.ValueText;\n\t\t\t\t\t\t\t\tp.wNameEnd = les.Span.End;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tvar varName = v.Identifier.ToString();\n\t\t\t\t\t\tint end = lds.Span.End;\n\t\t\t\t\t\tif (isW) { //wnd.find\n\t\t\t\t\t\t\tp.wVar = varName;\n\t\t\t\t\t\t\tp.wEnd = end;\n\t\t\t\t\t\t\tp.wArgsStart = argsStart;\n\t\t\t\t\t\t\tp.wArgsEnd = argsEnd;\n\t\t\t\t\t\t\tp.wNameStart = nameStart;\n\t\t\t\t\t\t\tp.wFuncNameEnd = mae.Name.Span.End;\n\t\t\t\t\t\t\tif (p.orRun && test) {\n\t\t\t\t\t\t\t\tfor (int i = 1, n = a.Count; i < n; i++) {\n\t\t\t\t\t\t\t\t\tif (a[i].NameColon?.Name.Identifier.Text == \"run\") {\n\t\t\t\t\t\t\t\t\t\tp.orRunReplace = true;\n\t\t\t\t\t\t\t\t\t\tp.wArgsEnd = a[i - 1].Span.End;\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else { //w.Child\n\t\t\t\t\t\t\tp.cVar = varName;\n\t\t\t\t\t\t\tp.cEnd = end;\n\t\t\t\t\t\t\tp.cArgsStart = argsStart;\n\t\t\t\t\t\t\tp.cArgsEnd = argsEnd;\n\t\t\t\t\t\t\tp.cNameStart = nameStart;\n\t\t\t\t\t\t\tp.cStart = lds.FullSpan.Start; while (code[p.cStart - 1] <= ' ') p.cStart--;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t//print.it(p);\n\t\treturn p;\n\t}\n\n\t/// <summary>\n\t/// Shows <see cref=\"Dwnd\"/> and updates text.\n\t/// </summary>\n\tpublic (bool ok, wnd w, wnd con, bool useCon) AaShowWndTool(Window owner, wnd w, wnd con, bool checkControl) {\n\t\tvar flags = DwndFlags.DontInsert; if (checkControl) flags |= DwndFlags.CheckControl;\n\t\tvar d = new Dwnd(con.Is0 ? w : con, flags);\n\t\td.ShowAndWait(owner, hideOwner: true);\n\t\tvar code = d.AaResultCode; if (code == null) return default;\n\t\t_wnd = d.AaResultWindow;\n\t\t_con = d.AaResultUseControl ? d.AaResultControl : default;\n\t\tint i = _ReadonlyStartUtf8;\n\t\tvar code2 = aaaRangeText(false, i, i + _readonlyLenUtf8);\n\t\taaaIsReadonly = false;\n\t\taaaSetText(code + code2);\n\t\treturn (true, d.AaResultWindow, d.AaResultControl, d.AaResultUseControl);\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Tools/KTextExpressionBox.cs",
    "content": "using System.Windows.Controls;\nusing Au.Controls;\nusing System.Windows;\n\nnamespace ToolLand;\n\n/// <summary>\n/// <see cref=\"KTextBox\"/> that supports C# expression in text.\n/// Just adds context menu item \"Expressions...\" that shows how to enter an expression; see <see cref=\"SetExpressionContextMenu\"/>.\n/// </summary>\nclass KTextExpressionBox : KTextBox {\n\tprotected override void OnContextMenuOpening(ContextMenuEventArgs e) {\n\t\tSetExpressionContextMenu(this);\n\t\tbase.OnContextMenuOpening(e);\n\t}\n\t\n\t/// <summary>\n\t/// Replaces the standard context menu. Adds standard items + item \"Expressions...\" that shows how to enter an expression.\n\t/// Does nothing if <c>t.ContextMenu?.HasItems == true</c>.\n\t/// </summary>\n\tpublic static void SetExpressionContextMenu(TextBox t) {\n\t\tif (t.ContextMenu?.HasItems == true) return;\n\t\tvar m = t.xAddCutCopyPasteToContextMenu(addClear: t is KTextBox, setStateNow: true);\n\t\tm.xAddSeparator();\n\t\tm.xAdd(\"Expressions...\", null, (_, _) => { dialog.show(\"Expressions\", \"\"\"\nIn this text field you can enter literal text or a C# expression.\nLiteral text in code will be enclosed in \"\" or @\"\" and escaped if need.\nExpression will be added to code without changes.\n\nExamples of all supported expressions:\n\n@@expression\n@\"verbatim string\"\n$\"interpolated string like {variable} text {expression} text\"\n$@\"interpolated verbatim string like {variable} text {expression} text\"\n\nReal expression examples:\n\n@\"\\bregular expression\\b\"\n@@Environment.GetEnvironmentVariable(\"API_KEY\")\n@@\"line1\\r\\nline2\"\n\"\"\", owner: t); });\n\t}\n}\n\n/// <summary>\n/// Editable <b>ComboBox</b> that supports C# expression in text.\n/// Just adds context menu item \"Expressions...\" that shows how to enter an expression.\n/// </summary>\nclass KComboExpressionBox : ComboBox {\n\tpublic KComboExpressionBox() {\n\t\tIsEditable = true;\n\t}\n\t\n\tpublic override void OnApplyTemplate() {\n\t\tbase.OnApplyTemplate();\n\t\tif (GetTemplateChild(\"PART_EditableTextBox\") is TextBox t) {\n\t\t\tt.ContextMenu = new();\n\t\t\tt.ContextMenuOpening += (_, _) => KTextExpressionBox.SetExpressionContextMenu(t);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Tools/KeysWindow.cs",
    "content": "using Au.Controls;\nusing ToolLand;\nusing System.Windows;\nusing System.Windows.Controls;\n\nnamespace LA;\n\nclass KeysWindow : InfoWindow { //KPopup\n\tpublic KeysWindow() : base(0) {\n\t\tSize = (500, 240);\n\t\tWindowName = \"Keys\";\n\t\tName = \"Ci.Keys\"; //prevent hiding when activated\n\t\tCloseHides = true;\n\t}\n\t\n\tprotected override void OnHandleCreated() {\n\t\tvar c = Control1;\n\t\tc.AaTags.AddLinkTag(\"+a\", o => _Insert(o), new() { textColor = 0x0060E0, monospace = true }); //link that inserts a key etc\n\t\tc.AaNotify += _c_AaNotify;\n\t\tc.Call(Sci.SCI_SETMOUSEDWELLTIME, 500);\n\t\t\n\t\t_SetText();\n\t\t\n\t\tbase.OnHandleCreated();\n\t}\n\t\n\tvoid _SetText() {\n\t\tvar s = c_contentText.RxReplace(@\"\\{(.+?)\\}(?!\\})\", \"<+a>$1<>\");\n\t\tif (_format != 0) {\n\t\t\tbool mod = _format == PSFormat.TriggerMod;\n\t\t\tint i = s.Find(mod ? \"\\n\" : \"\\n<b>Operator\") + 1;\n\t\t\tif (i > 0) s = s.ReplaceAt(i.., \"\");\n\t\t\ts = s.Replace(\"  <+a>right ▾<>\", \"\");\n\t\t}\n\t\tthis.Text = s;\n\t}\n\t\n\t/// <summary>\n\t/// Inserts s in InsertInControl, which can be either Panels.Editor.ActiveDoc or a TextBox (only for a hotkey).\n\t/// </summary>\n\tvoid _Insert(string s) {\n\t\tFrameworkElement con = InsertInControl;\n\t\tif (con is System.Windows.Controls.TextBox tb) {\n\t\t\tif (!_GetTrueKeyName()) return;\n\t\t\tint pos = tb.CaretIndex;\n\t\t\tif (pos > 0 && s[0] != '+') { //insert space between keys, even if used for a hotkey\n\t\t\t\tvar code = tb.Text;\n\t\t\t\tchar k1 = code[pos - 1];\n\t\t\t\tif (k1 > ' ' && k1 != '(' && !(k1 == '+' && !code.Eq(pos - 2, '#'))) s = \" \" + s;\n\t\t\t}\n\t\t\tif (s.Ends(false, \"Alt\", \"Ctrl\", \"Shift\", \"Win\") > 0) s += \"+\";\n\t\t} else {\n\t\t\tDebug.Assert(con == Panels.Editor.ActiveDoc);\n\t\t\tif (!CodeInfo.GetDocumentAndFindToken(out var cd, out var token)) return;\n\t\t\tif (true != token.IsInString(cd.pos, cd.code, out var si)) return;\n\t\t\tint pos = cd.pos, from = si.textSpan.Start, to = si.textSpan.End;\n\t\t\t\n\t\t\tswitch (s) {\n\t\t\tcase \"text\": _AddArg(\", \\\"!`|`\\\"\"); return;\n\t\t\tcase \"html\": _AddArg(\", \\\"%`|`\\\"\"); return;\n\t\t\tcase \"sleepMs\": _AddArg(\", 100\"); return;\n\t\t\tcase \"keyCode\": _AddArg(\", KKey.Left\"); return;\n\t\t\tcase \"scanCode\": _AddArg(\", new KKeyScan(1, false)\"); return;\n\t\t\tcase \"action\": _AddArg(\", new Action(() => { mouse.rightClick(); })\"); return;\n\t\t\t}\n\t\t\t\n\t\t\tvar code = cd.code;\n\t\t\tbool addArg = code[from] is '!' or '%' || code[from..cd.pos].Contains('^');\n\t\t\tstring prefix = null, suffix = null;\n\t\t\tchar k1 = code[pos - 1], k2 = code[pos];\n\t\t\tif (!addArg) {\n\t\t\t\tif (s[0] is '*' or '+') {\n\t\t\t\t\tif (k1 is '*' or '+') cd.sci?.aaaSelect(true, pos - 1, pos); //eg remove + from Alt+ if now selected *down\n\t\t\t\t} else {\n\t\t\t\t\tif (pos > from && k1 > ' ' && k1 != '(' && !(k1 == '+' && !code.Eq(pos - 2, '#'))) prefix = \" \"; //insert space between keys\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (s.Ends(false, \"Alt\", \"Ctrl\", \"Shift\", \"Win\") > 0) suffix = \"+\";\n\t\t\telse if (!addArg && pos < to && k2 > ' ' && k2 is not (')' or '+' or '*')) suffix = \"`|` \";\n\t\t\t\n\t\t\tif (!_GetTrueKeyName()) return;\n\t\t\ts = prefix + s + suffix;\n\t\t\t\n\t\t\tif (addArg) {\n\t\t\t\t_AddArg($\", \\\"{s}`|`\\\"\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\tvoid _AddArg(string s) {\n\t\t\t\tint stringEnd = si.stringNode.Span.End;\n\t\t\t\tif (to == stringEnd) s = \"\\\"\" + s;\n\t\t\t\tcd.sci.aaaGoToPos(true, stringEnd);\n\t\t\t\tTUtil.InsertTextIn(cd.sci, s);\n\t\t\t}\n\t\t}\n\t\t\n\t\tTUtil.InsertTextIn(con, s);\n\t\t\n\t\tbool _GetTrueKeyName() {\n\t\t\tbool ok = true;\n\t\t\tif (s.Length == 2 && s[0] != '#' && !s[0].IsAsciiAlpha()) s = s[0] == '\\\\' ? \"|\" : s[..1]; //eg 2@ or /? or \\|\n\t\t\telse if (s.Starts(\"right\")) ok = _Menu(\"RAlt\", \"RCtrl\", \"RShift\", \"RWin\");\n\t\t\telse if (s.Starts(\"lock\")) ok = _Menu(\"CapsLock\", \"NumLock\", \"ScrollLock\");\n\t\t\telse if (s.Starts(\"other\")) ok = _Menu(s_rare);\n\t\t\treturn ok;\n\t\t\t\n\t\t\tbool _Menu(params string[] a) {\n\t\t\t\tint j = popupMenu.showSimple(a, owner: this.Hwnd, rawText: true) - 1;\n\t\t\t\tif (j < 0) return false;\n\t\t\t\ts = a[j];\n\t\t\t\tj = s.IndexOf(' '); if (j > 0) s = s[..j];\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\t\n\tstatic string[] s_rare = {\n\"BrowserBack\", \"BrowserForward\", \"BrowserRefresh\", \"BrowserStop\", \"BrowserSearch\", \"BrowserFavorites\", \"BrowserHome\",\n\"LaunchMail\", \"LaunchMediaSelect\", \"LaunchApp1\", \"LaunchApp2\",\n\"MediaNextTrack\", \"MediaPrevTrack\", \"MediaStop\", \"MediaPlayPause\",\n\"VolumeMute\", \"VolumeDown\", \"VolumeUp\",\n\"IMEKanaMode\", \"IMEHangulMode\", \"IMEJunjaMode\", \"IMEFinalMode\", \"IMEHanjaMode\", \"IMEKanjiMode\", \"IMEConvert\", \"IMENonconvert\", \"IMEAccept\", \"IMEModeChange\", \"IMEProcessKey\",\n\"Break  //Ctrl+Pause\", \"Clear  //Shift+#5\", \"Sleep\",\n//\"F13\", \"F14\", \"F15\", \"F16\", \"F17\", \"F18\", \"F19\", \"F20\", \"F21\", \"F22\", \"F23\", \"F24\", //rejected\n  };\n\t\n\tpublic void SetFormat(PSFormat format) {\n\t\tif (format == PSFormat.Keys) format = 0;\n\t\tif (format == _format) return;\n\t\t_format = format;\n\t\t_SetText();\n\t}\n\tPSFormat _format;\n\t\n\tvoid _c_AaNotify(KScintilla.AaEventHandlerArgs e) {\n\t\t//show tooltips for some links\n\t\tvar c = e.c;\n\t\tif (e.n.code == Sci.NOTIF.SCN_DWELLSTART) {\n\t\t\tif (_linkStyle == 0) {\n\t\t\t\t_linkStyle = c.aaaStyleGetAt(c.aaaFindText(false, \"Alt\"));\n\t\t\t\tc.Call(Sci.SCI_CALLTIPSETPOSITION, true); //above\n\t\t\t\tthis.Hidden += (_, _) => { Control1.Call(Sci.SCI_CALLTIPCANCEL); };\n\t\t\t}\n\t\t\tif (e.n.position > 0 && _GetRange(e.n.position, out var r)) {\n\t\t\t\tif (r == _linkRange && 0 != c.Call(Sci.SCI_CALLTIPACTIVE)) return;\n\t\t\t\tvar s = c.aaaRangeText(false, r.start, r.end);\n\t\t\t\ts = s switch {\n\t\t\t\t\t\"code\" => \"Virtual-key code, like VK65 or VK0x42\",\n\t\t\t\t\t\"*nTimes\" => \"Press the key many times.\\nExamples:\\nkeys.send(\\\"Left*3\\\");\\nint n = 3; keys.send($\\\"Left*{n}\\\");\",\n\t\t\t\t\t\"*down\" => \"Press the key but don't release now.\\nExample:\\nkeys.send(\\\"Ctrl*down\\\");\",\n\t\t\t\t\t\"*up\" => \"Release the key.\\nExample:\\nkeys.send(\\\"Ctrl*up\\\");\",\n\t\t\t\t\t\"+key\" => \"Press multiple keys simultaneously.\\nExamples:\\nkeys.send(\\\"Ctrl+Shift+A\\\");\\nkeys.send(\\\"Alt+E+P\\\");\",\n\t\t\t\t\t\"+(keys)\" => \"Press multiple keys while holding down the modifier key.\\nExample:\\nkeys.send(\\\"Alt+(E P)\\\");\",\n\t\t\t\t\t\"_char\" => \"Next character is text, not a key name.\\nExample:\\nkeys.send(\\\"Alt+_e_a\\\");\",\n\t\t\t\t\t\"^chars\" => \"The remaining part of the string is text, not key names.\\nExample:\\nkeys.send(\\\"Alt+^ea\\\");\",\n\t\t\t\t\t\"text\" => \"Literal text.\\nExamples:\\nkeys.send(\\\"Tab\\\", \\\"!Text\\\", \\\"Tab\\\");\\nstring s = \\\"Text\\\"; keys.send(\\\"Tab\\\", \\\"!\\\" + s, \\\"Tab\\\");\\nkeys.sendt(\\\"Text\\\");\",\n\t\t\t\t\t\"html\" => \"HTML (if the active window supports it).\\nExamples:\\nkeys.send(\\\"%<b>Text</b>\\\");\\nkeys.sendt(\\\"Text\\\", \\\"<b>Text</b>\\\");\",\n\t\t\t\t\t\"sleepMs\" => \"Sleep milliseconds.\\nExample:\\nkeys.send(\\\"Left\\\", 500, \\\"Right\\\");\",\n\t\t\t\t\t\"keyCode\" => \"A key from enum KKey.\",\n\t\t\t\t\t\"scanCode\" => \"Scan code etc.\",\n\t\t\t\t\t\"action\" => \"Callback function.\\nExample:\\nAction click = () => mouse.click();\\nkeys.send(\\\"Shift+\\\", click);\",\n\t\t\t\t\t_ => null\n\t\t\t\t};\n\t\t\t\tif (s != null) {\n\t\t\t\t\tc.aaaSetString(Sci.SCI_CALLTIPSHOW, r.start, s);\n\t\t\t\t\t_linkRange = r;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tc.Call(Sci.SCI_CALLTIPCANCEL);\n\t\t}\n\t\t\n\t\tbool _GetRange(int pos, out StartEnd r) {\n\t\t\tr = default;\n\t\t\tif (c.aaaStyleGetAt(pos) != _linkStyle) return false;\n\t\t\tint start = pos, end = start + 1;\n\t\t\twhile (c.aaaStyleGetAt(start - 1) == _linkStyle) start--;\n\t\t\twhile (c.aaaStyleGetAt(end) == _linkStyle) end++;\n\t\t\tr = new(start, end);\n\t\t\treturn true;\n\t\t}\n\t}\n\tint _linkStyle;\n\tStartEnd _linkRange;\n\t\n\tconst string c_contentText = \"\"\"\n<b>Modifier:<><mono>  {Alt}  {Ctrl}  {Shift}  {Win}  {right ▾}<>\n<b>Navigate:<><mono>   {Down}  {Left}  {Right}  {Up}  {End}  {Home}  {PgDn}  {PgUp}<>\n<b>Other:<><mono>   {Back}  {Del}  {Enter}  {Esc}  {Space}  {Tab}<>\n<b>Function:<><mono>   {F1}  {F2}  {F3}  {F4}  {F5}  {F6}  {F7}  {F8}  {F9}  {F10}  {F11}  {F12}<>\n<b>Letter:<><mono>   {A} {B} {C} {D} {E} {F} {G} {H} {I} {J} {K} {L} {M} {N} {O} {P} {Q} {R} {S} {T} {U} {V} {W} {X} {Y} {Z}<>\n<b>Number:<><mono>   {1!}  {2@}  {3#}  {4$}  {5%}  {6^}  {7&}  {8*}  {9(}  {0)}<>\n<b>Punctuation etc:<><mono>   {`~}  {-_}  {=+}  {[{}  {]}}  {\\|}  {;:}  {'\"}  {,<}  {.>}  {/?}<>\n<b>Numpad:<><mono>   {#1} {#2} {#3} {#4} {#5} {#6} {#7} {#8} {#9} {#0} {#/} {#*} {#-} {#+} {#.}<>\n<b>Rare:<><mono>   {Apps}  {Ins}  {Pause}  {PrtSc}  {lock ▾}  {other ▾}  <+a VK>code<><>\n<b>Operator:<><mono>   <+a *>*nTimes<>  {*down}  {*up}  <+a +>+key<>  <+a +(`|`)>+(keys)<>  <+a _>_char<>  <+a ^>^chars<><>\n<b>Argument:<><mono>   {text}  {html}  {sleepMs}  {keyCode}  {scanCode}  {action}<>\n\n<help keys.send>Help<>    Example:  <code>keys.send(\"Ctrl+A Del Tab*3\", \"!text\", 100, \"Enter\");</code>\n\"\"\";\n}\n"
  },
  {
    "path": "Au.Editor/Tools/QuickCapture.cs",
    "content": "//CONSIDER: disable hotkeys when editor hidden.\n\nusing ToolLand;\n\nnamespace LA;\n\nstatic class QuickCapture {\n\tstatic popupMenu _m;\n\n\tpublic static void Info() {\n\t\tprint.it($@\"<>Hotkeys for quick capturing (<+options Hotkeys>Options > Hotkeys<>):\n\t{App.Settings.hotkeys.tool_quick} - capture window from mouse and show menu to insert code to find it etc.\n\t{App.Settings.hotkeys.tool_wnd} - capture window from mouse and show tool 'Find window'.\n\t{App.Settings.hotkeys.tool_elm} - capture UI element from mouse and show tool 'Find UI element'.\n\t{App.Settings.hotkeys.tool_uiimage} - capture image in window from mouse and show tool 'Find image'.\n\");\n\t}\n\n\t//CONSIDER: while showing menu, show on-screen rectangles of the window, control and elm.\n\t//CONSIDER: the 'click' items could show a dialog to select Coord format. Or add tool in editor.\n\n\tpublic static void Menu() {\n\t\t_m?.Close();\n\n\t\tvar p = mouse.xy;\n\t\twnd w0 = wnd.fromXY(p), w = w0.Window, c = w == w0 ? default : w0;\n\t\tuint color = CaptureScreen.Pixel(p) & 0xFFFFFF;\n\t\tvar screenshot = TUtil.MakeScreenshot(p);\n\n\t\tvar m = new popupMenu();\n\t\tm[\"Find window\"] = _ => _Insert(_Wnd_Find(w));\n\t\tm.Submenu(\"Find+\", m => {\n\t\t\tm[\"Find and activate\"] = _ => _Insert(_Wnd_Find(w, activate: true));\n\t\t\tm[\"Find or run\"] = _ => {\n\t\t\t\tvar k = PathInfo.FromWindow(w);\n\t\t\t\tif (k != null) _Insert(_Wnd_Find(w, activate: true, orRun: k.FormatCode(PathCode.Run)));\n\t\t\t};\n\t\t\tm[\"Run and find\"] = _ => {\n\t\t\t\tvar k = PathInfo.FromWindow(w);\n\t\t\t\tif (k != null) _Insert(_Wnd_Find(w, activate: true, andRun: k.FormatCode(PathCode.Run)));\n\t\t\t};\n\t\t\tm[\"wndFinder\"] = _ => _Insert(\"var f = new wndFinder(\" + TUtil.ArgsFromWndFindCode(_Wnd_Find(w)) + \");\");\n\t\t\tm[\"Find control\"] = _ => _Insert(_Wnd_Find(w, c));\n\t\t\tm.Last.IsDisabled = c.Is0;\n\t\t});\n\t\tm.Submenu(\"Mouse\", m => {\n\t\t\tstring _Click(wnd w, string v) {\n\t\t\t\tw.MapScreenToClient(ref p);\n\t\t\t\treturn $\"\\r\\nmouse.click({v}, {p.x}, {p.y});{screenshot}\";\n\t\t\t}\n\t\t\tm[\"Click window\"] = _ => _Insert(_Wnd_Find(w) + _Click(w, \"w\"));\n\t\t\tm[\"Click control\"] = _ => _Insert(_Wnd_Find(w, c) + _Click(c, \"c\"));\n\t\t\tm.Last.IsDisabled = c.Is0;\n\t\t\t//CONSIDER: UI element\n\t\t\tm[\"Click screen\"] = _ => _Insert($\"mouse.click({p.x}, {p.y});{screenshot}\");\n\t\t});\n\t\tm.Submenu(\"Triggers\", m => {\n\t\t\tm[\"Window trigger...\"] = _ => TriggersAndToolbars.QuickWindowTrigger(w, 0);\n\t\t\tm[\"Trigger scope...\"] = _ => TriggersAndToolbars.QuickWindowTrigger(w, 3);\n\t\t\tm[\"Trigger scope - window\"] = _ => TriggersAndToolbars.QuickWindowTrigger(w, 1);\n\t\t\tm.Last.Tooltip = \"Hotkey/autotext/mouse triggers added afterwards will work only when this window is active\";\n\t\t\tm[\"Trigger scope - program\"] = _ => TriggersAndToolbars.QuickWindowTrigger(w, 2);\n\t\t\tm.Last.Tooltip = \"Hotkey/autotext/mouse triggers added afterwards will work only when a window of this program is active\";\n\t\t\t\n\t\t\t//CONSIDER: add options for trigger scope:\n\t\t\t//1. `using Triggers.Of.Window(...) { ... }`\n\t\t\t//2. `#region ... #endregion`. With regions can find scopes in the Outline panel.\n\t\t});\n\t\tm.Submenu(\"Program\", m => {\n\t\t\tvar k = PathInfo.FromWindow(w);\n\t\t\tif (k != null) {\n\t\t\t\tPathInfo.QuickCaptureMenu(m, o => _Insert(k.FormatCode(o)));\n\t\t\t\tm.Separator();\n\t\t\t\tm[\"Copy path\"] = _ => clipboard.text = k.fileRaw;\n\t\t\t\tm[\"Copy @\\\"path\\\"\"] = _ => clipboard.text = k.fileString;\n\t\t\t}\n\t\t\tif (w.ProgramName is string pn) m[\"Copy filename\"] = _ => clipboard.text = pn;\n\t\t\tif (k != null) {\n\t\t\t\tm.Separator();\n\t\t\t\tm[\"Select in Explorer\"] = _ => run.selectInExplorer(k.fileRaw);\n\t\t\t}\n\t\t});\n\t\tm.Submenu(\"Color\", m => {\n\t\t\tstring s0 = color.ToString(\"X6\"), s1 = \"#\" + s0, s2 = $\"0x\" + s0, s3 = $\"0x\" + ColorInt.SwapRB(color).ToString(\"X6\");\n\t\t\tm[\"Copy #RRGGBB:  \" + s1] = _ => clipboard.text = s1;\n\t\t\tm[\"Copy 0xRRGGBB:  \" + s2] = _ => clipboard.text = s2;\n\t\t\tm[\"Copy 0xBBGGRR:  \" + s3] = _ => clipboard.text = s3;\n\t\t});\n\t\t//\tm.Submenu(\"Get color\", m => {\n\t\t//\t\tm[\"Window\"] = _=> {\n\t\t//\t\t\t\n\t\t//\t\t};\n\t\t//\t\tm[\"Control\"] = _=> {\n\t\t//\t\t\t\n\t\t//\t\t};\n\t\t//\t\tm.Last.IsDisabled=c.Is0;\n\t\t//\t\tm[\"Screen\"] = _=> {\n\t\t//\t\t\t\n\t\t//\t\t};\n\t\t//\t});\n\t\tm.Separator();\n\t\tm[\"About\"] = _ => Info();\n\t\tm.Add(\"Cancel\");\n\n\t\t_m = m;\n\t\tm.Show();\n\t\t_m = null;\n\t}\n\n\tstatic string _Wnd_Find(wnd w, wnd c = default, bool activate = false, string orRun = null, string andRun = null) {\n\t\tvar f = new TUtil.WindowFindCodeFormatter();\n\t\tf.RecordWindowFields(w, andRun != null ? 30 : 1, activate);\n\t\tf.orRunW = orRun;\n\t\tf.andRunW = andRun;\n\t\tif (!c.Is0) f.RecordControlFields(w, c);\n\t\treturn f.Format();\n\t}\n\n\tstatic void _Insert(string s) {\n\t\t//print.it(s);\n\t\tInsertCode.Statements(new(s, makeVarName1: true));\n\t}\n\n\tpublic static void AoolDwnd() {\n\t\tToolProcess.StartDwnd(wnd.fromMouse());\n\t}\n\n\tpublic static void ToolDelm() {\n\t\tToolProcess.StartDelm(mouse.xy);\n\t}\n\n\tpublic static void ToolDuiimage() {\n\t\tToolProcess.StartDuiimage(wnd.fromMouse(WXYFlags.NeedWindow));\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Tools/RegexWindow.cs",
    "content": "using Au.Controls;\n\nnamespace ToolLand;\n\nclass RegexWindow : InfoWindow { //KPopup\n\tpublic RegexWindow() : base(250) {\n\t\tSize = (800, 220);\n\t\tWindowName = \"Regex\";\n\t\tName = \"Ci.Regex\"; //prevent hiding when activated\n\t\tCloseHides = true;\n\t}\n\t\n\tprotected override void OnHandleCreated() {\n\t\tfor (int i = 0; i < 2; i++) {\n\t\t\tvar c = i == 0 ? this.Control1 : this.Control2;\n\t\t\tc.AaTags.AddStyleTag(\".r\", new() { textColor = 0xf08080 }); //red regex\n\t\t\tc.AaTags.AddLinkTag(\"+p\", o => CurrentTopic = o); //link to a local info topic\n\t\t\tc.Call(Sci.SCI_SETWRAPSTARTINDENT, 4);\n\t\t}\n\t\tthis.Control2.AaTags.AddStyleTag(\".h\", new() { backColor = 0xC0E0C0, bold = true, eolFilled = true }); //topic header\n\t\tthis.Control2.AaTags.AddLinkTag(\"+a\", o => TUtil.InsertTextIn(InsertInControl, o), new() { textColor = 0x0060E0, monospace = true }); //link that inserts a regex token\n\t\tthis.Control1.AaTags.DefaultLinkStyle = new() { textColor = 0x0080FF, underline = false }; //remove underline from links\n\t\t\n\t\t_SetTocText();\n\t\tCurrentTopic = @\"help\";\n\t\t\n\t\tbase.OnHandleCreated();\n\t}\n\t\n\tvoid _SetTocText() {\n\t\tthis.Text = c_contentText.Remove(c_contentText.Find(\"\\r\\n\\r\\n-- \"));\n\t}\n\t\n\t/// <summary>\n\t/// Opens an info topic or gets current topic name.\n\t/// </summary>\n\tpublic string CurrentTopic {\n\t\tget => _topic;\n\t\tset {\n\t\t\tif (value == _topic) return;\n\t\t\t_topic = value;\n\t\t\tstring s;\n\t\t\tif (value.Starts(\"Note:\")) {\n\t\t\t\ts = value;\n\t\t\t} else {\n\t\t\t\ts = c_contentText;\n\t\t\t\tif (!s.RxMatch($@\"(?ms)^-- {_topic} --\\R\\R(.+?)\\R-- \", 1, out s)) s = \"\";\n\t\t\t\telse s = s.Replace(\"{App.Settings.internetSearchUrl}\", LA.App.Settings.internetSearchUrl);\n\t\t\t}\n\t\t\tthis.Text2 = s;\n\t\t}\n\t}\n\tstring _topic;\n\t\n\tpublic void Refresh() {\n\t\t_SetTocText();\n\t\tvar s = _topic;\n\t\t_topic = null;\n\t\tCurrentTopic = s;\n\t}\n\t\n\tconst string c_contentText = \"\"\"\n<+p help>Help<>\n<+p options>Options<> <.r>(?imsnxUJ)<> and <.r>(*...)<>\n<+p meta>Metacharacters<> <.r>^ $ . [ | ( ) * + ? { \\<>\n<+p escape>Escaped chars<> <.r>\\^ \\n<> etc and text <.r>\\Q...\\E<>\n<+p charTypes>Character types, any char<> <.r>\\d \\w \\s \\R .<>\n<+p charClasses>Character classes<> <.r>[abc]<> and <.r>(?[ext])<>\n<+p repetition>Repetition quantifiers<> <.r>* + ? {n} {n,m}<>\n<+p startEnd>Start/end of string/line/word<> <.r>^ $ \\b<> etc\n<+p or>OR operator<> <.r>A|B|C<>\n<+p groups>Groups<> <.r>(...)<> and backreferences <.r>\\1<>\n<+p assertions>Follows/precedes<> <.r>(?=...)<> etc and <.r>\\K<>\n\n<b>Advanced and rarely used<>\n<+p extended>Extended syntax<> <.r>(?x)<> and <.r>#comments<>\n<+p conditional>Conditional groups<> <.r>(?(if)yes|no)<>\n<+p subroutines>Subroutines, recursion<> <.r>(?&name) (?1) (?R)<>\n<+p callouts>Callouts<> <.r>(?C) (?C0) (?C'text')<> etc\n<+p backtracking>Backtracking control<> <.r>(*...)<>\n<+p charProp>Unicode character properties<> <.r>\\p{Xx}<>\n<+p scripts>Unicode scripts<> <.r>\\p{name}<> and <.r>(*sr:...)<>\n\n<b>Replacement<>\n<+p replace>Replacement<> <.r>$1 ${2} ${name} $$<> etc\n\n-- help --\n\n<.h>Help<>\n\n<link https://www.pcre.org/current/doc/html/pcre2pattern.html>PCRE regex syntax<>\n<link https://www.pcre.org/current/doc/html/pcre2syntax.html>PCRE regex syntax summary<>\n<link https://www.pcre.org/current/doc/html/pcre2api.html#SEC20>PCRE2_ flags<>\n<link https://www.pcre.org/current/doc/html/index.html>PCRE regex library reference<>\n<link>http://www.rexegg.com/<>\n<link>https://www.regular-expressions.info/<>\n<link {App.Settings.internetSearchUrl}regular+expression+tester,+PCRE>Find regex test tools<>\n\n-- options --\n\n<.h><link https://www.pcre.org/current/doc/html/pcre2pattern.html#SEC15>Options<><>\n\n<+a>(?i)<>  -  case-insensitive. In the Find panel this is default when 'Case' unchecked.\n<+a>(?m)<>  -  <.r>^<> and <.r>$<> are start/end of a line. Default in the Find panel. Known as \"multiline\".\n<+a>(?s)<>  -  <.r>.<> matches newlines (<.r>\\r<>, <.r>\\n<>) too. Known as \"single-line\" or \"dotall\".\n<+a>(?n)<>  -  unnamed <.r>(...)<> groups are non-capturing, like <.r>(?:...)<>. Named still capturing: <.r>(?'name'...)<>.\n<+a>(?x)<>  -  extended. Support <.r>#comments<> and ignore spaces/newlines, except in <.r>[...]<> and after <.r>\\<>.\n<+a>(?xx)<>  -  extended more. Like <.r>(?x)<> but also ignore spaces in <.r>[...]<> (but not newlines).\n<+a>(?U)<>  -  ungreedy (lazy) quantifiers <.r>+ * ? {n}<> etc. For example makes <.r>.+<> the same as <.r>.+?<>.\n<+a>(?J)<>  -  allow duplicate group names.\n\n<b>How to remove options, set multiple, etc<>\n<+a (?`|`)>(?msi)<>  -  set multiple options.\n<+a (?-`|`)>(?-im)<>  -  remove options.\n<+a (?`|`-)>(?si-m)<>  -  set and remove options.\n<+a>(?^)<>  -  remove options <.r>imsnx<>.\n<+a (?^`|`)>(?^im)<>  -  remove <.r>imsnx<> and add <.r>im<>.\n<+a (?`|`:)>(?i:...)<>  -  another way to set options in non-capturing groups. The same as <.r>(?:(?i)...)<>.\n\nThese options can be set anywhere in regular expression and are applied until its end or until the end of the <.r>(...)<> group/assertion/etc or until unset.\n\nExample regex: <.r>(?mi)^ab((?s).+?)cd(?-i)ef<>. Here option <.r>m<> is set for whole regex, option <.r>i<> until <.r>(?-i)<>, and option <.r>s<> added only in group <.r>(.+?)<>.\n\n<.h><link https://www.pcre.org/current/doc/html/pcre2pattern.html#SEC3>Start-of-regex options<><>\n<+a>(*BSR_UNICODE)<>  -  <.r>\\R<> matches any Unicode newline characters, not only <.r>\\n<>, <.r>\\r<> and <.r>\\r\\n<>.\n<+a>(*ANY)<>  -  newline includes any Unicode newline characters, not only <.r>\\n<>, <.r>\\r<> and <.r>\\r\\n<>.\n<+a>(*LF)<>  -  newline is only <.r>\\n<>.\n<+a>(*CR)<>  -  newline is only <.r>\\r<>.\n<+a>(*CRLF)<>  -  newline is only <.r>\\r\\n<>.\n<+a>(*NOTEMPTY)<>  -  set this flag. See the links below.\n<+a>(*NOTEMPTY_ATSTART)<>  -  set this flag.\n<+a>(*NO_AUTO_POSSESS)<>  -  set this flag.\n<+a>(*NO_DOTSTAR_ANCHOR)<>  -  set this flag.\n<+a>(*NO_START_OPT)<>  -  set this flag.\n<+a>(*UTF)<>  -  set this flag.\n<+a>(*UCP)<>  -  set this flag.\n<+a>(*EC)<>  -  enable UTS#18 extended character classes\n\nThese options can be set only at the start of regular expression. Example: <.r>(*ANY)(*UCP)regex<>.\n\nWith regular expression functions you can also use flags <help>Au.Types.RXFlags<> and <help>Au.Types.RXMatchFlags<>. There are flags for most <.r>(?...)<> and <.r>(*...)<> options and more. Options <.r>(?...)<> set in regular expression override function's flags.\n\n<b>Links<>\n<link https://www.pcre.org/current/doc/html/pcre2api.html#SEC20>API flags<>\n<link https://www.pcre.org/current/doc/html/pcre2unicode.html>Unicode, (*UTF), (*UCP)<>\n\n-- meta --\n\n<.h><link https://www.pcre.org/current/doc/html/pcre2pattern.html#SEC4>Metacharacters<><>\n\n<.r>^<>  -  start of string (or line, in multiline mode).\n<.r>$<>  -  end of string (or line, in multiline mode).\n<.r>.<>  -  match any character except newline (by default).\n<.r>[<>  -  start character class definition.\n<.r>|<>  -  start of alternative branch.\n<.r>(<>  -  start group or control verb.\n<.r>)<>  -  end group or control verb.\n<.r>*<>  -  0 or more quantifier.\n<.r>+<>  -  1 or more quantifier; also \"possessive quantifier\".\n<.r>?<>  -  0 or 1 quantifier; also quantifier minimizer.\n<.r>{<>  -  start min/max quantifier.\n<.r>\\<>  -  general escape character with several uses.\n\nIn extended mode also:\n<.r>#<>  -  starts a line comment.\n\nIn a character class <.r>[...]<> the only metacharacters are:\n<.r>^<>  -  negate the class, but only if the first character.\n<.r>-<>  -  indicates character range.\n<.r>]<>  -  terminates the character class.\n<.r>[:class:]<>  -  POSIX character class.\n<.r>\\<>  -  general escape character.\n\n-- escape --\n\n<.h><link https://www.pcre.org/current/doc/html/pcre2pattern.html#SEC5>Escape sequences and literal text<><>\n\n<.r>\\<> makes the next metacharacter a literal character. It is an <i>escape sequence<>.\n\n<+a>\\^<>, <+a>\\$<>, <+a>\\.<>, <+a>\\[<>, <+a>\\|<>, <+a>\\(<>, <+a>\\)<>, <+a>\\*<>, <+a>\\+<>, <+a>\\?<>, <+a>\\{<>, <+a>\\\\<>  -  escape sequences for metacharacters.\n<+a>\\#<>, <+a>\\ <> (space)  -  in extended mode also need these.\n<+a>\\^<>, <+a>\\-<>, <+a>\\]<>, <+a>\\[<>, <+a>\\\\<>  -  in character classes <.r>[...]<> need only these.\n\n<.r>\\<> before any other ASCII non-alphanumeric character is ignored (removed).\n\nEscape sequences for non-printing characters:\n\n<+a>\\n<>  -  linefeed (character code 10).\n<+a>\\r<>  -  carriage return (character code 13).\n<+a>\\r\\n<>  -  Windows newline sequence. See also \"any newline\" <+p charTypes>\\R<>.\n<+a>\\t<>  -  tab (character code 9).\n<+a \\x`|`>\\xhh<>  -  character with hex character code hh.\n<+a \\x{`|`}>\\x{hhhh}<>  -  character with hex character code hhhh.\n<+a \\N{U+`|`}>\\N{U+hhhh}<>  -  character with Unicode hex code point hhhh.\n\n<+a \\Q`|`\\E>\\Q...\\E<>  -  literal text.\n\nMetacharacter <.r>\\<> also has other uses. With regex functions use <c green>@\"verbatim string\"<> to avoid C# escape sequences for every <.r>\\<>.\n\n-- charTypes --\n\n<.h><link https://www.pcre.org/current/doc/html/pcre2pattern.html#SEC7>Any character<>, <link https://www.pcre.org/current/doc/html/pcre2pattern.html#newlineseq>newlines<> and <link https://www.pcre.org/current/doc/html/pcre2pattern.html#genericchartypes>character types<><>\n\n<+a>.<>  -  any character except newlines (<.r>\\r<>, <.r>\\n<>). Any character if option <.r>(?s)<>.\n<+a>\\N<>  -  any character except newlines, regardless of option <.r>(?s)<>.\n<+a>\\R<>  -  newline sequence <.r>\\r\\n<> or <.r>\\n<> or <.r>\\r<>. Not allowed in <.r>[...]<>.\n<+a>\\d<>  -  digit (ASCII 0-9).\n<+a>\\D<>  -  non-digit.\n<+a>\\w<>  -  word character (ASCII 0-9, A-Z, a-z, _).\n<+a>\\W<>  -  non-word character.\n<+a>\\s<>  -  white space character (space, tab, newlines \\r \\n, vertical tab, form feed).\n<+a>\\S<>  -  character that is not a white space character.\n<+a>\\h<>  -  horizontal white space character (space, tab).\n<+a>\\H<>  -  character that is not a horizontal white space character.\n<+a>\\v<>  -  vertical white space character (newlines).\n<+a>\\V<>  -  character that is not a vertical white space character.\n<+a \\p{`|`}>\\p{Xx}<>  -  character with the Xx <+p charProp>property<> or in <+p scripts>script<>.\n<+a \\P{`|`}>\\P{Xx}<>  -  character without the Xx <+p charProp>property<> or not in <+p scripts>script<>.\n<+a>\\X<>  -  Unicode extended grapheme cluster.\n\n<b>Options<>  (must be at the start of regular expression)\n<+a>(*UCP)<>  -  <.r>\\d<>, <.r>\\w<> and <.r>\\s<> match not only ASCII characters. Flag <help Au.Types.RXFlags>RXFlags.UCP<>.\n<+a>(*BSR_UNICODE)<>  -  <.r>\\R<> matches any Unicode newline characters, not only <.r>\\n<>, <.r>\\r<> and <.r>\\r\\n<>.\n<+a>(*ANY)<>, <+a>(*LF)<>, <+a>(*CR)<>, <+a>(*CRLF)<>  -  <+p>options<> for <.r>.<> and <.r>\\N<>.\n\n-- charClasses --\n\n<.h><link https://www.pcre.org/current/doc/html/pcre2pattern.html#SEC9>Character classes<><>\n\n<+a [`|`]>[abc]<>  -  character a, b or c.\n<+a [^`|`]>[^abc]<>  -  any character except a, b, c.\n<+a [^\\r\\n`|`]>[^abc\\r\\n]<>  -  any character except a, b, c and newlines.\n<+a [`|`-]>[a-z]<>  -  one of characters in range a to z.\n<+a [^`|`-]>[^a-z]<>  -  any character not in range a to z.\n\nCan include several ranges, characters plus ranges, character types like <.r>\\d<> (digit), escaped characters like <.r>\\n<>, POSIX character classes like <.r>[:alpha:]<>. For literal <.r>^ - ] \\<> use <.r>\\^ \\- \\] \\\\<>.\n\n<b>Examples<>\n<+a>[_\\d]<>  -  character _ and digits 0-9.\n<+a>[^\"\\r\\n\\-]<>  -  any character except \", newlines and -.\n<+a>[0-9A-Fa-f]<> or <+a>[[:xdigit:]]<>  -  a hexadecimal digit.\n<+a>[A-Za-z_]\\w+<> or <+a>[[:alpha:]_]\\w+<>  -  a C# identifier.\n\n<.h><link https://www.pcre.org/current/doc/html/pcre2pattern.html#SEC12>POSIX character classes<> in <.r>[...]<><>\n<+a>[:alnum:]<>  -  letters and digits.\n<+a>[:alpha:]<>  -  letters.\n<+a>[:ascii:]<>  -  character codes 0 - 127.\n<+a>[:blank:]<>  -  space, tab.\n<+a>[:cntrl:]<>  -  control characters.\n<+a>[:digit:]<>  -  decimal digits (same as <.r>\\d<>).\n<+a>[:graph:]<>  -  printing characters, excluding space.\n<+a>[:lower:]<>  -  lower case letters.\n<+a>[:print:]<>  -  printing characters, including space.\n<+a>[:punct:]<>  -  printing characters, except letters and digits and space.\n<+a>[:space:]<>  -  white space (same as <.r>\\s<>).\n<+a>[:upper:]<>  -  upper case letters.\n<+a>[:word:]<>  -  word characters (same as <.r>\\w<>).\n<+a>[:xdigit:]<>  -  hexadecimal digits.\n\nSee also: <+p charProp>Unicode character properties<> and <+p scripts>scripts<>.\n\n<b>Options<>  (must be at the start of regular expression)\n<+a>(*UCP)<>  -  include non-ASCII characters in POSIX classes and <.r>\\w<> etc. Flag <help Au.Types.RXFlags>RXFlags.UCP<>. Also there are options and flags to exclude some.\n\n<.h><link https://www.pcre.org/current/doc/html/pcre2pattern.html#SEC10>Extended character classes<><>\n<+a (?[`|`])>(?[...])<>  -  extended character class. Can contain multiple simple classes like <.r>\\d<> or <.r>[...]<>, operators like <.r>-<>, and <.r>()<> for grouping.\n\n<b>Examples<>\n<+a>(?[\\w -[xy]])<>  -  word characters except x and y.\n<+a>(?[\\w -[:upper:]])<>  -  word characters except uppercase letters.\n\n<.h><link https://www.pcre.org/current/doc/html/pcre2pattern.html#SEC11>UTS#18 extended character classes<><>\n<+a>(*EC)<>  -  enable UTS#18 extended character classes. This must be at the start of regular expression.\n\n<b>Examples<>\n<+a>[\\w--[xy]]<>  -  word characters except x and y.\n<+a>[.\\-\\w--[:upper:]]<>  -  dot, hyphen and word characters except uppercase letters.\n\n-- repetition --\n\n<.h><link https://www.pcre.org/current/doc/html/pcre2pattern.html#SEC19>Repetition quantifiers<><>\n\n<+a>?<>  -  0 or 1. Lazy <+a>??<>, possessive <+a>?+<>.\n<+a>*<>  -  0 or more. Lazy <+a>*?<>, possessive <+a>*+<>.\n<+a>+<>  -  1 or more. Lazy <+a>+?<>, possessive <+a>++<>.\n<+a {`|`}>{n}<>  -  exactly n.\n<+a {`|`,}>{n,m}<>  -  at least n, no more than m. Lazy <+a {`|`,}?>{n,m}?<>, possessive <+a {`|`,}+>{n,m}+<>.\n<+a {`|`,}>{n,}<>  -  n or more. Lazy <+a {`|`,}?>{n,}?<>, possessive <+a {`|`,}+>{n,}+<>.\n\nA quantifier after a character/group/etc specifies how many times it must match.\nExamples: <.r><.r>.*<><> - 0 or more characters; <.r><.r>(\\d{3}-)+<><> - 1 or more groups of 3 digits and -.\n\nQuantifiers can be placed after:\n  a character, such as <.r>c<> or <.r>\\t<> (tab);\n  <.r>.<> (any character except newline, or any character if option <.r>(?s)<>);\n  a character type such as <.r>\\d<> (digit) or <.r>\\pL<> that matches a single character;\n  <.r>\\R<> (any newline sequence), <.r>\\C<>, <.r>\\X<>;\n  a character class <.r>[...]<>;\n  a group like <.r>(...)<> or <.r>(?:...)<> or assertion like <.r>(?=...)<> etc;\n  a backreference, like <.r>\\1<>;\n  a subroutine call, like <.r>(?1)<> or <.r>(?R)<> or <.r>(?&name)<>.\n\nQuantifiers can be greedy (default), lazy (with <.r>?<> or option <.r>(?U)<>) or possessive (with <.r>+<>).\n• Greedy - as many as possible. For example regex <.r><.+><> matches <c green><ab> cd <ef><> in string <c green><ab> cd <ef> gh<>.\n• Lazy - as few as possible. For example regex <.r><.+?><> matches <c green><ab><> in the above string.\n• <link https://www.pcre.org/current/doc/html/pcre2pattern.html#SEC20>Possessive<> - once the regex part matched, it cannot be backtracked (retried smaller parts of it) when next regex part does not match; it can make faster. Greedy, ignores <.r>(?U)<>. The same as <+p groups>atomic groups<>, just simpler syntax.\n\n-- startEnd --\n\n<.h><link https://www.pcre.org/current/doc/html/pcre2pattern.html#smallassertions>Start/end of string/line/word<><>\n\n<+a>\\b<>  -  word boundary.\n<+a>\\B<>  -  not a word boundary.\n<+a>^<>  -  start of string; also start of a line in multiline mode (<+p options>option<> <.r>(?m)<>; default in Find panel).\n<+a>$<>  -  end of string; also before newline at end of string; also end of a line in multiline mode.\n<+a>\\A<>  -  start of string. This and others below don't depend on multiline mode.\n<+a>\\Z<>  -  end of string; also before newline at end of string.\n<+a>\\z<>  -  end of string.\n<+a>\\G<>  -  when using a non-zero start index in string - at the start index; else same as <.r>\\A<>. With 'find all' and 'replace all' functions it also is previous match's end.\n\nA word boundary is between two characters that match <.r>\\w<> (letters, digits and _) and <.r>\\W<> (other characters), or between <.r>\\w<> and start/end. By default only ASCII characters are considered word characters, therefore for example <.r>\\b<> matches between an ASCII letter and non-ASCII letter. If used option <.r>(*UCP)<> or flag <help Au.Types.RXFlags>RXFlags.UCP<>, <.r>\\w<> etc match not only ASCII characters.\n\nDefault newlines are any of <.r>\\r\\n<>, <.r>\\n<> and <.r>\\r<>. There are <+p>options<> to change it, and thus change the behaviour of <.r>^<>, <.r>$<> and <.r>\\Z<>.\n\n-- or --\n\n<.h><link https://www.pcre.org/current/doc/html/pcre2pattern.html#SEC14>OR operator<><>\n\n<.r>red|green|blue<>  -  red or green or blue.\n<.r>red|green|<>  -  red or green or empty.\n\nOften used in a group like <.r>(one|two)<> or in a \"followed/preceded by\" assertion like <.r>(?=one|two)<> etc.\n\n-- groups --\n\n<.h><link https://www.pcre.org/current/doc/html/pcre2pattern.html#SEC16>Groups<> and backreferences<>\n\n<+a (`|`)>(...)<>  -  capturing group.\n<+a (?'`|`')>(?'name'...)<>  -  named capturing group. Or <+a \"(?<`|`>)\">(?<name>...)<>, <+a \"(?P<`|`>)\">(?P<name>...)<>.\n<+a (?:`|`)>(?:...)<>  -  non-capturing group.\n<+a (?|`|`)>(?|...)<>  -  nested groups like A and B in <.r>(?|(A)s|(B))<> have the same number. Non-capturing.\n<+a \"(?>`|`)\">(?>...)<> or <+a (*atomic:`|`)>(*atomic:...)<>  -  <link https://www.pcre.org/current/doc/html/pcre2pattern.html#SEC20>atomic non-capturing group<>.\n\nGroups are used:\n1. To localize/isolate several <+p or>alternatives<> like <.r>(one|two)<> or <+p>options<> like <.r>((?i)...)<>.\n2. To get the matched substring with a regex function such as <help>regexp.Match<>.\n3. To use the matched substring as a backreference, like <.r>\\g{1}<>.\n4. To use the matched substring in the <+p replace>replacement<>, like <.r>$1<> or <.r>${name}<>.\n5. To reuse the group in regex as a <+p subroutines>subroutine<> or recursive pattern.\n6. To match a group n times, like <.r>(...)+<> (1 or more) or <.r>(...)?<> (0 or 1).\n7. And more.\n\nCapturing groups have numbers starting from 1. Non-capturing groups don't have numbers and cannot be used to get substring, for backreferences, replacements, subroutines.\n\nGroups can be nested, like <.r>(group1(group2))<>.\n\n<link https://www.pcre.org/current/doc/html/pcre2pattern.html#SEC20>Atomic groups<> can be used to avoid unnecessary backtracking and thus improve performance. Once the group matched, it cannot be backtracked (retried to find alternative matches) when next regex part does not match. For simple cases can instead be used <+p repetition>possessive quantifiers<>, which is the same just simpler syntax.\n\n<.h><link https://www.pcre.org/current/doc/html/pcre2pattern.html#SEC21>Backreferences<><>\n<+a \\g`|`>\\gn<>  -  reference to group n (1, 2, ...). Or <+a \\`|`>\\n<>, but can be ambiguous.\n<+a \\g{`|`}>\\g{n}<>  -  reference to group n. Use when followed by a digit.\n<+a \\g-`|`>\\g-n<> or <+a \\g{-`|`}>\\g{-n}<>  -  relative reference to a group started at the left.\n<+a \\g+`|`>\\g+n<> or <+a \\g{+`|`}>\\g{+n}<>  -  relative reference to a group at the right.\n<+a \\k'`|`'>\\k'name'<>  -  reference by name. Or <+a \"\\k<`|`>\">\\k<name><>, <+a \\g{`|`}>\\g{name}<>, <+a \\k{`|`}>\\k{name}<>, <+a (?P=`|`)>(?P=name)<>.\n\nA backreference matches the same substring as the referenced group matched most recently.\n\n-- assertions --\n\n<.h><link https://www.pcre.org/current/doc/html/pcre2pattern.html#SEC22>Follows/precedes assertions<> and <link https://www.pcre.org/current/doc/html/pcre2pattern.html#resetmatchstart>setting the start of match<><>\n\n<+a (?=`|`)>(?=...)<>  -  positive lookahead assertion. It means \"followed by ...\".\n<+a (*pla:`|`)>(*pla:...)<> or <+a (*positive_lookahead:`|`)>(*positive_lookahead:...)<>  -  the same.\n<+a (?!`|`)>(?!...)<>  -  negative lookahead assertion. It means \"not followed by ...\".\n<+a (*nla:`|`)>(*nla:...)<> or <+a (*negative_lookahead:`|`)>(*negative_lookahead:...)<>  -  the same.\n<+a (?<=`|`)>(?<=...)<>  -  positive lookbehind assertion. It means \"preceded by ...\".\n<+a (*plb:`|`)>(*plb:...)<> or <+a (*positive_lookbehind:`|`)>(*positive_lookbehind:...)<>  -  the same.\n<+a (?<!`|`)>(?<!...)<>  -  negative lookbehind assertion. It means \"not preceded by ...\".\n<+a (*nlb:`|`)>(*nlb:...)<> or <+a (*negative_lookbehind:`|`)>(*negative_lookbehind:...)<>  -  the same.\n<+a (*scs:(1)`|`)>(*scs:(group)...)<> or <+a (*scan_substring:(1)`|`)>(*scan_substring:(group)...)<>  -  (<link https://www.pcre.org/current/doc/html/pcre2pattern.html#SEC24>docs<>) the matched substring of group must also match .... Group: group number or +-relative or <.r>'name'<> or list. The ... regex must match at the start of the substring; use <.r>\\z<> to also match at its end.\n\nAn assertion is a test on the string part following or preceding the current matching point that does not consume any characters. For example, regex <.r>ab(?=cd)<> matches ab followed by cd, but does not include cd in the reported match.\n\nAn assertion can contain several local \"or\" parts like <.r>(?=ab|cd)<>, local options like <.r>(?=(?i)...)<>, nested assertions. Several assertions (of any sort) may occur in succession. For example, <.r>(?<=\\d{3})(?<!999)foo<> matches foo preceded by three digits that are not 999.\n\nLookbehind assertions (\"preceded by\") must be of a known length (max 255), for example <.r>(?<=abc)<> or <.r>(?<=abc|defgh)<> or <.r>(?<=\\d{1,9})<>, but not <.r>(?<=.*)<> because <.r>.*<> can match a substring of any length.\n\nOften instead of a lookbehind assertion can be used <.r>\\K<>. It sets the reported start of match; the string part matched before <.r>\\K<> is not included in the reported match. For example, instead of invalid regex <.r>(?<=\\w*)\\d<> use <.r>\\w*\\K\\d<>.\n\n-- extended --\n\n<.h>Extended syntax and comments<>\n\nIf option <.r>(?x)<> is set, most white space characters (spaces, newlines, etc) in the regular expression are ignored, except when escaped (preceded by <.r>\\<>) or inside a character class <.r>[...]<>. Option <.r>(?xx)<> allows spaces in <.r>[...]<> too. The <.r>#<> character starts a line comment. It makes easier to write and read big regular expressions: you can split it into multiple lines.\n\nExample in normal mode:\n<.r>one #two(?#comment)three<>\n\nThe same in extended mode can be written in multiple lines:\n<.r>(?x)\none\n\\ \\#two\n\n#comment\nthree<>\n\n<b>Comments<>\n<+a #`|`>#comment<>  -  comment in extended mode.\n<+a (?#`|`)>(?#comment)<>  -  comment in any mode.\n\n<b>Links<>\n<link https://www.pcre.org/current/doc/html/pcre2api.html#SEC20>Extended syntax (flag PCRE2_EXTENDED)<>\n\n-- conditional --\n\n<.h><link https://www.pcre.org/current/doc/html/pcre2pattern.html#SEC26>Conditional groups<><>\n\n<+a (?(`|`))>(?(condition)yes-pattern)<>  -  \"if condition is true then use yes-pattern\".\n<+a (?(`|`)|)>(?(condition)yes-pattern|no-pattern)<>  -  \"if condition is true then use yes-pattern, else no-pattern\".\n\nConditions:\n<.r>n<>  -  reference to group n (1, 2, ...). True if the group has already been matched.\n<+a -`|`>-n<>  -  relative reference to a group started at the left.\n<+a +`|`>+n<>  -  relative reference to a group at the right.\n<+a \"'`|`'\">'name'<>  -  reference to a named group. Or <+a \"<`|`>\"><name><>.\n<+a>R<>  -  overall recursion.\n<+a R`|`>Rn<>  -  specific numbered group recursion.\n<+a R&`|`>R&name<>  -  specific named group recursion.\n<+a ?`|`>assertion<>  -  <+p assertions>assertion<>. Example: <.r>(?(?=\\d)yes|no)<>.\n<+a VERSION=`|`.>VERSION=n.m<>  -  test PCRE2 version.\n<+a \"VERSION>=`|`.\">VERSION>=n.m<>  -  test PCRE2 version.\n\n-- subroutines --\n\n<.h><link https://www.pcre.org/current/doc/html/pcre2pattern.html#SEC28>Subroutines and recursion<><>\n\n<+a \"(?`|`)\">(?n)<>  -  call subroutine by group number n (1, 2, ...). Or <+a \"\\g'`|`'\">\\g'n'<>, <+a \"\\g<`|`>\"><_>\\g<n></_><>.\n<+a \"(?-`|`)\">(?-n)<>  -  call subroutine by relative number. Or <+a \"\\g'-`|`'\">\\g'-n'<>, <+a \"\\g<-`|`>\"><_>\\g<-n></_><>.\n<+a \"(?+`|`)\">(?+n)<>  -  call subroutine by relative number. Or <+a \"\\g'+`|`'\">\\g'+n'<>, <+a \"\\g<+`|`>\"><_>\\g<+n></_><>.\n<+a \"(?&`|`)\">(?&name)<>  -  call subroutine by group name. Or <+a \"\\g'`|`'\">\\g'name'<>, <+a \"\\g<`|`>\"><_>\\g<name></_><>, <+a \"(?P>`|`)\">(?P>name)<>.\n<+a>(?R)<>  -  recurse whole pattern.\n<+a (?(DEFINE)(`|`))>(?(DEFINE)(subroutine))<>  -  define a subroutine; don't evaluate it now.\n\nRecursive regex examples:\n<+a>\\(([^()]++|(?R))*\\)<>  -  match a string enclosed in ( ) that may contain any number of enclosed substrings, like <c green>(...)<> or <c green>(..(..)+(..))<> or <c green>(..((..)..)..)<>.\n<+a>(\\(([^()]++|(?-2))*\\))<>  -  the same as above when it is part of a larger regular expression.\n\nA capturing group <.r>(...)<> can be reused in other parts of regular expression. It is like a subroutine that can be called like a function by its number or name. It also can be called recursively (from itself). Unlike a backreference (the substring captured by the referenced group), a subroutine call is like a copy of the group in the regular expression. For example, regex <.r>(\\w+)-(?1)<> is the same as <.r>(\\w+)-(?:\\w+)<>; as well as <.r>(?(DEFINE)(\\w+))(?1)-(?1)<> and <.r>(?(DEFINE)(?'word'\\w+))(?&word)-(?&word)<>.\n\n-- callouts --\n\n<.h><link https://www.pcre.org/current/doc/html/pcre2pattern.html#SEC29>Callouts<><>\n\n<+a>(?C)<>  -  callout (assumed number 0).\n<+a (?C`|`)>(?Cn)<>  -  callout with numerical data n.\n<+a (?C'`|`')>(?C'text')<>  -  callout with string data.\n\n<b>Links<>\n<help>regexp.Callout<>\n\n-- backtracking --\n\n<.h><link https://www.pcre.org/current/doc/html/pcre2pattern.html#SEC30>Backtracking control<><>\n\nAll backtracking control verbs may be in the form <.r>(*VERB:NAME)<>. For <.r>(*MARK)<> the name is mandatory, for the others it is optional. <.r>(*SKIP)<> changes its behaviour if <.r>:NAME<> is present. The others just set a name for passing back to the caller, but this is not a name that <.r>(*SKIP)<> can see. The following act immediately they are reached:\n\n<+a>(*ACCEPT)<>  -  force successful match.\n<+a>(*FAIL)<>  -  force backtrack; synonym <+a>(*F)<>.\n<+a (*MARK:`|`)>(*MARK:NAME)<>  -  set name to be passed back; synonym <+a (*:`|`)>(*:NAME)<>.\n\nThe following act only when a subsequent match failure causes a backtrack to reach them. They all force a match failure, but they differ in what happens afterwards. Those that advance the start-of-match point do so only if the pattern is not anchored.\n\n<+a>(*COMMIT)<>  -  overall failure, no advance of starting point.\n<+a>(*PRUNE)<>  -  advance to next starting character.\n<+a>(*SKIP)<>  -  advance to current matching position.\n<+a (*SKIP:`|`)>(*SKIP:NAME)<>  -  advance to position corresponding to an earlier <.r>(*MARK:NAME)<>; if not found, the <.r>(*SKIP)<> is ignored.\n<+a>(*THEN)<>  -  local failure, backtrack to next alternation.\n\nThe effect of one of these verbs in a group called as a subroutine is confined to the subroutine call.\n\n-- charProp --\n\n<.h><link https://www.pcre.org/current/doc/html/pcre2pattern.html#uniextseq>Unicode character properties<><>\n\n<+a>\\pC<>  -  Other.\n<+a>\\p{Cc}<>  -  Control.\n<+a>\\p{Cf}<>  -  Format.\n<+a>\\p{Cn}<>  -  Unassigned.\n<+a>\\p{Co}<>  -  Private use.\n<+a>\\p{Cs}<>  -  Surrogate.\n\n<+a>\\pL<>  -  Letter.\n<+a>\\p{Ll}<>  -  Lower case letter.\n<+a>\\p{Lm}<>  -  Modifier letter.\n<+a>\\p{Lo}<>  -  Other letter.\n<+a>\\p{Lt}<>  -  Title case letter.\n<+a>\\p{Lu}<>  -  Upper case letter.\n<+a>\\p{L&}<>  -  Ll, Lu, or Lt.\n\n<+a>\\pM<>  -  Mark.\n<+a>\\p{Mc}<>  -  Spacing mark.\n<+a>\\p{Me}<>  -  Enclosing mark.\n<+a>\\p{Mn}<>  -  Non-spacing mark.\n\n<+a>\\pN<>  -  Number.\n<+a>\\p{Nd}<>  -  Decimal number.\n<+a>\\p{Nl}<>  -  Letter number.\n<+a>\\p{No}<>  -  Other number.\n\n<+a>\\pP<>  -  Punctuation.\n<+a>\\p{Pc}<>  -  Connector punctuation.\n<+a>\\p{Pd}<>  -  Dash punctuation.\n<+a>\\p{Pe}<>  -  Close punctuation.\n<+a>\\p{Pf}<>  -  Final punctuation.\n<+a>\\p{Pi}<>  -  Initial punctuation.\n<+a>\\p{Po}<>  -  Other punctuation.\n<+a>\\p{Ps}<>  -  Open punctuation.\n\n<+a>\\pS<>  -  Symbol.\n<+a>\\p{Sc}<>  -  Currency symbol.\n<+a>\\p{Sk}<>  -  Modifier symbol.\n<+a>\\p{Sm}<>  -  Mathematical symbol.\n<+a>\\p{So}<>  -  Other symbol.\n\n<+a>\\pZ<>  -  Separator.\n<+a>\\p{Zl}<>  -  Line separator.\n<+a>\\p{Zp}<>  -  Paragraph separator.\n<+a>\\p{Zs}<>  -  Space separator.\n\n<+a>\\p{Xan}<>  -  Alphanumeric: union of properties L and N.\n<+a>\\p{Xps}<>  -  POSIX space: property Z or tab, NL, VT, FF, CR.\n<+a>\\p{Xsp}<>  -  Perl space: property Z or tab, NL, VT, FF, CR.\n<+a>\\p{Xuc}<>  -  Univerally-named character.\n<+a>\\p{Xwd}<>  -  Perl word: property Xan or underscore.\n\n-- scripts --\n\n<.h><link https://www.pcre.org/current/doc/html/pcre2pattern.html#uniextseq>Unicode scripts<><>\n\nSome characters belong to certain scripts, like Greek, Arabic or Han.\n\n<+a \\p{`|`}>\\p{Xxx}<>  -  a character in script Xxx.\n<+a \\P{`|`}>\\P{Xxx}<>  -  a character not in script Xxx.\n\n<b>Script names<>\n\nAdlam, Ahom, Anatolian_Hieroglyphs, Arabic, Armenian, Avestan, Balinese, Bamum, Bassa_Vah, Batak, Bengali, Bhaiksuki, Bopomofo, Brahmi, Braille, Buginese, Buhid, Canadian_Aboriginal, Carian, Caucasian_Albanian, Chakma, Cham, Cherokee, Common, Coptic, Cuneiform, Cypriot, Cyrillic, Deseret, Devanagari, Dogra, Duployan, Egyptian_Hieroglyphs, Elbasan, Ethiopic, Georgian, Glagolitic, Gothic, Grantha, Greek, Gujarati, Gunjala_Gondi, Gurmukhi, Han, Hangul, Hanifi_Rohingya, Hanunoo, Hatran, Hebrew, Hiragana, Imperial_Aramaic, Inherited, Inscriptional_Pahlavi, Inscriptional_Parthian, Javanese, Kaithi, Kannada, Katakana, Kayah_Li, Kharoshthi, Khmer, Khojki, Khudawadi, Lao, Latin, Lepcha, Limbu, Linear_A, Linear_B, Lisu, Lycian, Lydian, Mahajani, Makasar, Malayalam, Mandaic, Manichaean, Marchen, Masaram_Gondi, Medefaidrin, Meetei_Mayek, Mende_Kikakui, Meroitic_Cursive, Meroitic_Hieroglyphs, Miao, Modi, Mongolian, Mro, Multani, Myanmar, Nabataean, New_Tai_Lue, Newa, Nko, Nushu, Ogham, Ol_Chiki, Old_Hungarian, Old_Italic, Old_North_Arabian, Old_Permic, Old_Persian, Old_Sogdian, Old_South_Arabian, Old_Turkic, Oriya, Osage, Osmanya, Pahawh_Hmong, Palmyrene, Pau_Cin_Hau, Phags_Pa, Phoenician, Psalter_Pahlavi, Rejang, Runic, Samaritan, Saurashtra, Sharada, Shavian, Siddham, SignWriting, Sinhala, Sogdian, Sora_Sompeng, Soyombo, Sundanese, Syloti_Nagri, Syriac, Tagalog, Tagbanwa, Tai_Le, Tai_Tham, Tai_Viet, Takri, Tamil, Tangut, Telugu, Thaana, Thai, Tibetan, Tifinagh, Tirhuta, Ugaritic, Unknown, Vai, Warang_Citi, Yi, Zanabazar_Square.\n\n<.h><link https://www.pcre.org/current/doc/html/pcre2pattern.html#SEC25>Script runs<><>\n\nA script run is a sequence of characters that are all from the same script.\n\n<+a (*sr:`|`)>(*sr:...)<> - script run. Matches only if <.r>...<> belongs to a single script.\n<+a (*asr:`|`)>(*asr:...)<> - atomic script run (backtracking is not allowed into the <.r>...<>).\n\n-- replace --\n\n<.h>Replacement<>\n\n<+a>$0<>  -  whole match.\n<+a>$1<>  <+a>$2<>  <+a>$3<>  <+a>$4<>  <+a>$5<>  <+a>$6<>  <+a>$7<>  <+a>$8<>  <+a>$9<>  <+a>$10<>  -  string part that matches a capturing group <.r>(...)<>.\n<+a ${`|`}>${n}<>  -  the same as above. Here n is a group number. Example: <.r>${10}<>.\n<+a ${`|`}>${name}<>  -  string part that matches a named group like <.r>(?'name'...)<>.\n<+a>$+<>  -  string part that matches the last group.\n<+a>$`<>  -  string part before match.\n<+a>$'<>  -  string part after match.\n<+a>$_<>  -  whole string.\n<+a>$*<>  -  the name of the last reached mark or other named <+p backtracking>verb<> like <.r>(*MARK:NAME)<>.\n<+a ${+`|`}>${+func}<>  -  call a function added with <help>regexp.addReplaceFunc<> and use its return value.\n<+a ${+`|`}>${+func(group)}<>  -  call function with group number or name.\n<+a ${+`|`}>${+func(group, string)}<>  -  call function with some string.\n<+a>$$<>  -  $ character.\n\nThese are used in the replacement text, not in the regular expression.\n\n<b>Example<>\nString: <c green>10/08/2020<>\nRegex: <.r>^(\\d+)/(\\d+)/(\\d+)$<>\nReplacement: <.r>$3-$1-$2<>\nResult: <c green>2020-10-08<>\n\n<b>Links<>\n<help>regexp.Replace<>\n\n-- end --\n\"\"\";\n}\n"
  },
  {
    "path": "Au.Editor/Tools/Scripting.cs",
    "content": "using System.Runtime.Loader;\n\nusing Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp;\nusing System.Reflection;\n\nnamespace ToolLand;\n\nstatic class Scripting {\n\t/// <summary>\n\t/// Compiles C# code. Optionally loads in-memory assembly and gets MethodInfo of the first static method for executing.\n\t/// Returns false if there are errors in code.\n\t/// </summary>\n\t/// <param name=\"code\">C# code.</param>\n\t/// <param name=\"r\">Receives results when compiled successfully.</param>\n\t/// <param name=\"addUsings\">Add usings Au, Au.Types, System, generic, LINQ.</param>\n\t/// <param name=\"addGlobalCs\">Add \"global.cs\" to the compilation if exists. It includes [module: DefaultCharSet(CharSet.Unicode)].</param>\n\t/// <param name=\"wrapInClass\">Wrap code in public class __script__.</param>\n\t/// <param name=\"dll\">Create dll, ie without entry method.</param>\n\t/// <param name=\"load\">If not null, load in-memory assembly and get MethodInfo of the first static method for executing from public class <i>load</i>. If \"\", gets entry method (<i>dll</i> must be false).</param>\n\t/// <remarks>\n\t/// Also adds default references.\n\t/// \n\t/// Function's code does not throw exceptions, but the CodeAnalysis API may throw, although undocumented and never noticed.\n\t/// \n\t/// Thread-safe.\n\t/// </remarks>\n\tpublic static bool Compile(string code, out Result r, bool addUsings, bool addGlobalCs, bool wrapInClass, bool dll, string load = \"__script__\") {\n\t\tif (addUsings || wrapInClass) {\n\t\t\tvar sb = new StringBuilder();\n\t\t\tif (addUsings) sb.AppendLine(\"using Au; using Au.Types; using System; using System.Collections.Generic; using System.Linq;\");\n\t\t\tif (wrapInClass) sb.AppendLine(\"public class __script__ {\");\n\t\t\tif (!code.Contains(\"#line \")) sb.AppendLine(\"#line 1\");\n\t\t\tsb.AppendLine(code);\n\t\t\tif (wrapInClass) sb.AppendLine(\"}\");\n\t\t\tcode = sb.ToString();\n\t\t\t//print.it(code);\n\t\t}\n\t\t\n\t\tvar parseOpt = new CSharpParseOptions(LanguageVersion.Preview);\n\t\t\n\t\tSyntaxTree treeGlobal = null;\n\t\tif (addGlobalCs) {\n\t\t\tif (WndCopyData.SendReceive<char>(ScriptEditor.WndMsg_, 16, \"global.cs\", out string gcode)) { //never mind: in LA main process/thread could call directly.\n\t\t\t\ttreeGlobal = CSharpSyntaxTree.ParseText(gcode, parseOpt);\n\t\t\t}\n\t\t\t//TODO3: also recursively add files etc specified in meta c, r, etc.\n\t\t\t//\tNow it is not important, because this func used only in \"find UI object\" tools, and global.cs can be useful only in 'also' field.\n\t\t}\n\t\t\n\t\tvar tree = CSharpSyntaxTree.ParseText(code, parseOpt);\n\t\tvar trees = treeGlobal != null ? new SyntaxTree[] { treeGlobal, tree } : new SyntaxTree[] { tree };\n\t\tvar compOpt = new CSharpCompilationOptions(dll ? OutputKind.DynamicallyLinkedLibrary : OutputKind.WindowsApplication, allowUnsafe: true);\n\t\tvar compilation = CSharpCompilation.Create(\"script\", trees, s_refs, compOpt);\n\t\tvar memStream = new MemoryStream(4000 + code.Length * 2);\n\t\tvar emitResult = compilation.Emit(memStream);\n\t\t\n\t\tr = new Result();\n\t\tif (!emitResult.Success) {\n\t\t\tr.errors = string.Join(\"\\r\\n\", emitResult.Diagnostics.Where(d => d.Severity == DiagnosticSeverity.Error).Select(d => d.ToString()));\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\tmemStream.Position = 0;\n\t\tif (load != null) {\n\t\t\tvar alc = new AssemblyLoadContext(null, isCollectible: true);\n\t\t\tr.assembly = alc.LoadFromStream(memStream);\n\t\t\t//print.it(AppDomain.CurrentDomain.GetAssemblies().Where(o => o.GetName().Name == \"script\").Count());\n\t\t\t\n\t\t\tif (load.NE()) r.method = r.assembly.EntryPoint;\n\t\t\telse r.method = r.assembly.GetType(load).GetMethods(BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static)[0];\n\t\t} else {\n\t\t\tr.stream = memStream;\n\t\t}\n\t\treturn true;\n\t}\n\t\n\t/// <summary>\n\t/// Creates <b>MetadataReference</b> for all .NET assemblies and Au.dll.\n\t/// </summary>\n\tstatic List<MetadataReference> _GetRefs() {\n\t\tvar r = new List<MetadataReference>();\n\t\tvar rdb = folders.ThisAppBS + \"ref.db\";\n\t\tusing var db = new sqlite(rdb, SLFlags.SQLITE_OPEN_READONLY);\n\t\tusing var stat = db.Statement(\"SELECT * FROM ref\");\n\t\twhile (stat.Step()) r.Add(MetadataReference.CreateFromImage(stat.GetArray<byte>(1), filePath: stat.GetText(0)));\n\t\tr.Add(MetadataReference.CreateFromFile(folders.ThisAppBS + \"Au.dll\"));\n\t\treturn r;\n\t}\n\tstatic List<MetadataReference> s_refs = _GetRefs();\n\t\n\tpublic class Result {\n\t\t/// <summary>\n\t\t/// Receives errors when fails to compile.\n\t\t/// </summary>\n\t\tpublic string errors;\n\t\t\n\t\t/// <summary>\n\t\t/// When load is false, receives assembly bytes in stream (position=0).\n\t\t/// </summary>\n\t\tpublic MemoryStream stream;\n\t\t\n\t\t/// <summary>\n\t\t/// When load is true, receives loaded assembly.\n\t\t/// </summary>\n\t\tpublic Assembly assembly;\n\t\t\n\t\t/// <summary>\n\t\t/// When load is true, receives MethodInfo of the first static method for executing.\n\t\t/// </summary>\n\t\tpublic MethodInfo method;\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Tools/TUtil-main-process.cs",
    "content": "using System.Windows;\nusing System.Windows.Controls;\nusing Au.Controls;\nusing System.Windows.Interop;\nusing System.Windows.Media;\nusing System.Windows.Controls.Primitives;\nusing System.Windows.Threading;\n\nusing ToolLand;\n\nnamespace LA;\n\nstatic class TUtil2 {\n\t//Some tool dialogs normally run in new thread. If started from another such dialog - in its thread.\n\t//Currently not using this. Tools like Delm run in separate process instead.\n\tpublic static void ShowDialogInNonmainThread(Func<KDialogWindow> newDialog) {\n\t\tif (!App.IsMainThread) {\n\t\t\t_Show(false);\n\t\t} else {\n\t\t\trun.thread(() => { _Show(true); }).Name = \"Au.Tool\";\n\t\t}\n\t\t\n\t\tbool _Show(bool dialog) {\n\t\t\ttry { //unhandled exception kills process if in nonmain thread\n\t\t\t\tvar d = newDialog();\n\t\t\t\tif (dialog) {\n\t\t\t\t\tbool ok = true == d.ShowDialog();\n\t\t\t\t\td.Dispatcher.InvokeShutdown(); //without this would be huge memory leaks, random crashes etc. From Dispatcher class docs: If you create a Dispatcher on a background thread, be sure to shut down the dispatcher before exiting the thread.\n\t\t\t\t\treturn ok;\n\t\t\t\t}\n\t\t\t\td.Show();\n\t\t\t}\n\t\t\tcatch (Exception e1) { print.it(e1); }\n\t\t\treturn false;\n\t\t}\n\t}\n\t\n\tpublic static void CloseDialogsInNonmainThreads() {\n\t\t//close tool windows running in other threads. Elso they would not save their rect etc.\n\t\tvar aw = wnd.findAll(null, \"HwndWrapper[*\", WOwner.Process(process.thisProcessId), WFlags.CloakedToo | WFlags.HiddenToo,\n\t\t\talso: o => o.Prop[\"close me on exit\"] == 1);\n\t\tforeach (var v in aw) v.SendTimeout(1000, out _, Api.WM_CLOSE);\n\t}\n\t\n\tpublic static bool UnexpandPathsMetaR(string[] a, AnyWnd errDlgOwner) {\n\t\tforeach (var v in a) {\n\t\t\tif (CompilerUtil.IsNetAssembly(v)) continue;\n\t\t\tdialog.showError(\"Not a .NET assembly.\", v, owner: errDlgOwner);\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\tstring appDir = folders.ThisAppBS, dllDir = App.Model.DllDirectoryBS;\n\t\tif (a[0].Starts(dllDir, true)) { //note: in portable LA it is in app dir\n\t\t\tfor (int i = 0; i < a.Length; i++) a[i] = @\"%dll%\\\" + a[i][dllDir.Length..];\n\t\t} else if (a[0].Starts(appDir, true) && !(App.IsPortable && a[0].Starts(appDir + @\"data\\\", true))) {\n\t\t\tfor (int i = 0; i < a.Length; i++) a[i] = a[i][appDir.Length..];\n\t\t} else if (App.Settings.tools_pathUnexpand) { //unexpand path\n\t\t\tfor (int i = 0; i < a.Length; i++) a[i] = folders.unexpandPath(a[i]);\n\t\t}\n\t\t\n\t\treturn true;\n\t}\n}\n\n/// <summary>\n/// From path gets name and various path formats (raw, @\"string\", unexpanded, shortcut) for inserting in code.\n/// If shortcut, also gets arguments.\n/// Supports \":: ITEMIDLIST\".\n/// </summary>\nclass PathInfo {\n\tpublic readonly string fileRaw, lnkRaw, fileString, lnkString, fileUnexpanded, lnkUnexpanded;\n\treadonly string _name, _name2, _args;\n\treadonly bool _elevated, _argsComment;\n\t\n\tpublic PathInfo(string path, string name = null, string args = null, bool elevated = false, bool argsComment = false) {\n\t\tfileRaw = path;\n\t\t_name = name ?? _Name(path);\n\t\t_args = args;\n\t\t_elevated = elevated;\n\t\t_argsComment = argsComment;\n\t\tif (path.Ends(\".lnk\", true)) {\n\t\t\ttry {\n\t\t\t\tvar g = shortcutFile.open(path);\n\t\t\t\tstring target = g.TargetAnyType;\n\t\t\t\tif (target.Starts(\"::\")) {\n\t\t\t\t\tusing var pidl = Pidl.FromString(target);\n\t\t\t\t\t_name2 = pidl.ToShellString(SIGDN.NORMALDISPLAY);\n\t\t\t\t} else {\n\t\t\t\t\t_args ??= g.Arguments.NullIfEmpty_();\n\t\t\t\t\tif (name == null)\n\t\t\t\t\t\tif (!target.Ends(\".exe\", true) || _name.Contains(\"Shortcut\"))\n\t\t\t\t\t\t\t_name2 = _Name(target);\n\t\t\t\t}\n\t\t\t\tlnkRaw = path;\n\t\t\t\tfileRaw = target;\n\t\t\t}\n\t\t\tcatch { }\n\t\t}\n\t\t\n\t\t_Format(fileRaw, out fileString, out fileUnexpanded);\n\t\tif (lnkRaw != null) _Format(lnkRaw, out lnkString, out lnkUnexpanded);\n\t\tif (_args != null) _args = _Str(_args);\n\t\t\n\t\tstatic void _Format(string raw, out string str, out string unexpanded) {\n\t\t\tstr = _Str(raw);\n\t\t\tif (folders.unexpandPath(raw, out unexpanded, out var sn) && !sn.NE()) unexpanded = unexpanded + \" + \" + _Str(sn);\n\t\t}\n\t\t\n\t\tstatic string _Name(string path) {\n\t\t\tif (path.Starts(\"shell:\") || path.Starts(\"::\")) return \"\";\n\t\t\tvar s = pathname.getNameNoExt(path);\n\t\t\tif (s.Length == 0) {\n\t\t\t\ts = pathname.getName(path); //eg some folders are like \".name\"\n\t\t\t\tif (s.Length == 0 && path.Like(\"?:\\\\\")) s = path[..2]; //eg \"C:\\\"\n\t\t\t}\n\t\t\treturn s;\n\t\t}\n\t}\n\t\n\tstatic string _Str(string s) {\n\t\tif (s == null) return \"null\";\n\t\tif (!TUtil.MakeVerbatim(ref s)) s = s.Escape(quote: true);\n\t\treturn s;\n\t}\n\t\n\t/// <summary>\n\t/// Gets path of window's program for <see cref=\"run.it\"/>. Supports appid, folder, mmc, itemidlist.\n\t/// Returns null if failed to get path or app id.\n\t/// </summary>\n\tpublic static PathInfo FromWindow(wnd w) {\n\t\tvar path = WndUtil.GetWindowsStoreAppId(w, true, true);\n\t\tif (path == null) return null;\n\t\tstring name = null, args = null;\n\t\tbool elevated = w.Uac.Elevation == UacElevation.Full;\n\t\tbool argsComment = false;\n\t\t//if folder window, try to get folder path\n\t\tif (path.Starts(\"shell:\")) {\n\t\t\tname = w.Name;\n\t\t} else if (path.Ends(@\"\\explorer.exe\", true) && w.ClassNameIs(\"CabinetWClass\")) {\n\t\t\tvar s1 = ExplorerFolder.Of(w)?.GetFolderPath();\n\t\t\tif (!s1.NE()) path = s1;\n\t\t} else {\n\t\t\tvar s = process.getCommandLine(w.ProcessId, removeProgram: true);\n\t\t\tif (!s.NE()) {\n\t\t\t\tif (path.Ends(@\"\\javaw.exe\", true)) {\n\t\t\t\t\targs = s;\n\t\t\t\t} else if (path.Ends(@\"\\mmc.exe\", true)\n\t\t\t\t\t&& path[..^8].Eqi(folders.System)\n\t\t\t\t\t&& s.RxMatch($@\"^(\"\".+?\\.msc\"\"|\\S+\\.msc)(?: (.+))?$\", out RXMatch m)) {\n\t\t\t\t\ts = m[1].Value.Trim('\"');\n\t\t\t\t\tif (!pathname.isFullPath(s)) s = filesystem.searchPath(s);\n\t\t\t\t\telse if (!filesystem.exists(s)) s = null;\n\t\t\t\t\tif (s != null) { path = s; args = m[2].Value; name = w.Name; }\n\t\t\t\t\t;\n\t\t\t\t} else {\n\t\t\t\t\targs = s;\n\t\t\t\t\targsComment = args != null;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t}\n\t\t}\n\t\tvar r = new PathInfo(path, name, args, elevated, argsComment);\n\t\treturn r;\n\t}\n\t\n\t/// <summary>\n\t/// Gets path/name/args code for inserting in editor.\n\t/// path is escaped/enclosed and may be unexpanded (depends on settings), like <c>@\"x:\\a\\b.c\"</c> or <c>folders.Example + @\"a\\b.c\"</c>.\n\t/// If args not null, it is escaped/enclosed is like \", args\" or \"/*, args*/\".\n\t/// </summary>\n\tpublic (string path, string name, string args) GetStringsForCode() {\n\t\tbool lnk = App.Settings.tools_pathLnk && lnkString != null;\n\t\tvar path = lnk\n\t\t\t? (App.Settings.tools_pathUnexpand && lnkUnexpanded != null ? lnkUnexpanded : lnkString)\n\t\t\t: (App.Settings.tools_pathUnexpand && fileUnexpanded != null ? fileUnexpanded : fileString);\n\t\tvar name = lnk ? _name : (_name2 ?? _name);\n\t\tvar args = lnk ? null : _args;\n\t\tif (args != null) args = _argsComment ? \"/*, \" + args + \"*/\" : \", \" + args;\n\t\treturn (path, name, args);\n\t}\n\t\n\t/// <summary>\n\t/// Calls <see cref=\"GetStringsForCode\"/> and returns code like <c>run.it(path);</c>.\n\t/// </summary>\n\t/// <param name=\"what\">Var, Run or RunMenu.</param>\n\t/// <param name=\"varIndex\">If not 0, appends to s in `var s = path`.</param>\n\tpublic string FormatCode(PathCode what, int varIndex = 0) {\n\t\tvar (path, name, args) = GetStringsForCode();\n\t\tstring nameComment = (what is PathCode.Var or PathCode.Run && (path.Starts(\"\\\":: \") || path.Like(\"folders.shell.*\\\"\")) && !name.NE()) ? $\"/* {name} */ \" : null;\n\t\tif (what is PathCode.Var) {\n\t\t\tstring si = varIndex > 0 ? varIndex.ToS() : null;\n\t\t\treturn $\"string s{si} = {nameComment}{path};\"; //not var s, because may be FolderPath\n\t\t} else if (what is PathCode.Run or PathCode.RunMenu) {\n\t\t\tvar b = new StringBuilder();\n\t\t\tif (what is PathCode.RunMenu) {\n\t\t\t\tvar t = CiUtil.GetNearestLocalVariableOfType(\"Au.toolbar\", \"Au.popupMenu\");\n\t\t\t\tb.Append($\"{t?.Name ?? \"t\"}[{_Str(name)}] = o => \");\n\t\t\t}\n\t\t\tb.Append(\"run.it(\").Append(nameComment).Append(path).Append(args);\n\t\t\tif (_elevated) b.Append(\", flags: RFlags.Admin\");\n\t\t\tb.Append(\");\");\n\t\t\treturn b.ToString();\n\t\t}\n\t\treturn null;\n\t}\n\t\n\t/// <summary>\n\t/// Shows \"insert file path code\" menu and returns its result.\n\t/// Called for drag-dropped files or shell items.\n\t/// </summary>\n\tpublic static PathCode InsertCodeMenu(string[] paths, AnyWnd owner) {\n\t\tvar m = new popupMenu { CheckDontClose = true };\n\t\t\n\t\tvoid _Add(PathCode what, string text) { m.Add((int)what, text); }\n\t\t\n\t\t_Add(PathCode.Var, \"string s = path;\");\n\t\t_Add(PathCode.Run, \"run.it(path);\");\n\t\t_Add(PathCode.RunMenu, \"t[name] = o => run.it(path);\");\n\t\tif (paths.All(o => filesystem.exists(o).Directory)) {\n\t\t\t_Add(PathCode.EnumDir, \"Get files in folder...\");\n\t\t} else if (paths.All(o => filesystem.exists(o).File && o.Ends(\".dll\", true))) {\n\t\t\t_Add(PathCode.MetaR, \"/*/ r path; /*/\");\n\t\t}\n\t\tm.Separator();\n\t\tm.AddCheck(\"Unexpand path (option)\", App.Settings.tools_pathUnexpand, o => App.Settings.tools_pathUnexpand ^= true);\n\t\tif (paths.Any(o => o.Ends(\".lnk\", true)))\n\t\t\tm.AddCheck(\"Shortcut path (option)\", App.Settings.tools_pathLnk, o => App.Settings.tools_pathLnk ^= true);\n\t\t\n\t\tvar R = (PathCode)m.Show(owner: owner);\n\t\t\n\t\tif (R is PathCode.EnumDir) {\n\t\t\tDEnumDir.Dialog(paths[0]);\n\t\t\treturn 0;\n\t\t}\n\t\t\n\t\tif (R is PathCode.MetaR) {\n\t\t\tif (TUtil2.UnexpandPathsMetaR(paths, owner)) {\n\t\t\t\tvar p = new MetaCommentsParser(App.Model.CurrentFile);\n\t\t\t\tp.r.AddRange(paths);\n\t\t\t\tp.Apply();\n\t\t\t}\n\t\t\treturn 0;\n\t\t}\n\t\t\n\t\treturn R;\n\t\t\n\t\t//rejected\n\t\t//static bool _IsConsole(string path) {\n\t\t//\tif (!path.Ends(\".exe\", true)) return path.Ends(\".bat\", true);\n\t\t//\treturn 0x4550 == Api.SHGetFileInfo(path, out _, Api.SHGFI_EXETYPE);\n\t\t//}\n\t}\n\t\n\t/// <summary>\n\t/// Adds \"insert file path code\" items to the quick capture menu.\n\t/// </summary>\n\t/// <param name=\"m\">Submenu \"Program\".</param>\n\t/// <param name=\"click\"></param>\n\tpublic static void QuickCaptureMenu(popupMenu m, Action<PathCode> click) {\n\t\tm.CheckDontClose = true;\n\t\t\n\t\tvoid _Add(PathCode what, string text) { m[text] = o => click(what); }\n\t\t\n\t\t_Add(PathCode.Var, \"string s = path;\");\n\t\t_Add(PathCode.Run, \"run.it(path);\");\n\t\t_Add(PathCode.RunMenu, \"t[name] = o => run.it(path);\");\n\t\tm.Separator();\n\t\tm.AddCheck(\"Unexpand path (option)\", App.Settings.tools_pathUnexpand, o => App.Settings.tools_pathUnexpand ^= true);\n\t}\n}\n\nenum PathCode { None, Var, Run, RunMenu, EnumDir, MetaR }\n"
  },
  {
    "path": "Au.Editor/Tools/TUtil.cs",
    "content": "using Au.Controls;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Interop;\nusing System.Windows.Media;\nusing System.Windows.Controls.Primitives;\nusing System.Windows.Threading;\n\nnamespace ToolLand;\n\nstatic partial class TUtil {\n\t#region text\n\t\n\t/// <summary>\n\t/// Appends `, ` and string argument.\n\t/// </summary>\n\t/// <param name=\"t\"></param>\n\t/// <param name=\"s\">\n\t/// Argument value.\n\t/// If null, appends `null`.\n\t/// Else if like `@@expression`, appends `expression`.\n\t/// Else if matches `@\"*\"` or `$\"*\"` or `$@\"*\"` or `@$\"*\"`, appends `s`.\n\t/// Else appends `\"escaped s\"`; can make verbatim.\n\t/// </param>\n\t/// <param name=\"param\">If not null, appends `param: s`. By default appends only `s`. If \"null\", appends `null, s`.</param>\n\t/// <param name=\"noComma\">Don't append `, `. If false, does not append if b.Length is less than 2 or if b ends with one of: <c>,([{&lt;</c>.</param>\n\tpublic static StringBuilder AppendStringArg(this StringBuilder t, string s, string param = null, bool noComma = false) {\n\t\t_AppendArgPrefix(t, param, noComma);\n\t\tif (s == null) t.Append(\"null\");\n\t\telse if (s.Starts(\"@@\")) t.Append(s[2..]);\n\t\telse if (IsVerbatim(s, out _) || MakeVerbatim(ref s)) t.Append(s);\n\t\telse t.Append(s.Escape(quote: true));\n\t\treturn t;\n\t}\n\t\n\t/// <summary>\n\t/// Appends `, ` and non-string argument.\n\t/// </summary>\n\t/// <param name=\"t\"></param>\n\t/// <param name=\"s\">Argument value. Must not be empty.</param>\n\t/// <param name=\"param\">If not null, appends `param: s`. By default appends only `s`. If \"null\", appends `null, s`.</param>\n\t/// <param name=\"noComma\">Don't append `, `. Use for the first parameter. If false, does not append only if b.Length is less than 2.</param>\n\tpublic static StringBuilder AppendOtherArg(this StringBuilder t, string s, string param = null, bool noComma = false) {\n\t\tDebug.Assert(!s.NE());\n\t\t_AppendArgPrefix(t, param, noComma);\n\t\tt.Append(s);\n\t\treturn t;\n\t}\n\t\n\tstatic void _AppendArgPrefix(StringBuilder t, string param, bool noComma) {\n\t\tif (!noComma && t.Length > 1 && t[^1] is not ('(' or '[' or '{' or '<' or ',')) t.Append(\", \");\n\t\tif (param != null) t.Append(param).Append(param == \"null\" ? \", \" : \": \");\n\t}\n\t\n\t/// <summary>\n\t/// Appends waitTime. If !orThrow, appends \"-\" if need.\n\t/// If !orThrow and !appendAlways and waitTime == \"0\", appends nothing and returns false.\n\t/// </summary>\n\tpublic static bool AppendWaitTime(this StringBuilder t, string waitTime, bool orThrow, bool appendAlways = false) {\n\t\tif (waitTime.NE()) waitTime = \"8e88\";\n\t\tif (!orThrow) {\n\t\t\tif (!appendAlways && waitTime == \"0\") return false;\n\t\t\tif (!waitTime.Starts('-') && waitTime != \"0\") t.Append('-');\n\t\t}\n\t\tt.Append(waitTime);\n\t\treturn true;\n\t}\n\t\n\t/// <summary>\n\t/// If some <i>use</i> are true, formats a flags argument like \"Enum.Flag1 | Enum.Flag2\" and returns true.\n\t/// </summary>\n\tpublic static bool FormatFlags<T>(out string s, params (bool use, T flag)[] af) where T : unmanaged, Enum {\n\t\tif (!af.Any(o => o.use)) { s = null; return false; }\n\t\ts = string.Join(\" | \", af.Where(o => o.use).Select(o => typeof(T).Name + \".\" + o.flag));\n\t\treturn true;\n\t}\n\t\n\t/// <summary>\n\t/// If some <i>use</i> checkboxes are checked or indeterminate, formats a flags argument like \"Enum.Flag1 | Enum.Flag2\" and returns true. Ignores null controls.\n\t/// </summary>\n\tpublic static bool FormatFlags<T>(out string s, params (CheckBox use, T flag)[] af) where T : unmanaged, Enum {\n\t\treturn FormatFlags(out s, af.Select(o => (o.use != null && o.use.IsChecked != false, o.flag)).ToArray());\n\t}\n\t\n\t/// <summary>\n\t/// Formats flags like \"Enum.Flag1 | Enum.Flag2\".\n\t/// </summary>\n\t/// <param name=\"flags\"></param>\n\tpublic static string FormatFlags<T>(T flags) where T : unmanaged, Enum {\n\t\tvar s = flags.ToString();\n\t\tif (!char.IsAsciiDigit(s[0])) s = s.Replace(\", \", \" | \").RxReplace(@\"\\b(?=\\w)\", typeof(T).Name + \".\");\n\t\treturn s;\n\t}\n\t\n\t/// <summary>\n\t/// Returns true if s is like `@\"*\"` or `$\"*\"` or `$@\"*\"` or `@$\"*\"`.\n\t/// s can be null.\n\t/// </summary>\n\tpublic static bool IsVerbatim(string s, out int prefixLength) {\n\t\tprefixLength = (s.Like(false, \"@\\\"*\\\"\", \"$\\\"*\\\"\", \"$@\\\"*\\\"\", \"@$\\\"*\\\"\") + 1) / 2;\n\t\treturn prefixLength > 0;\n\t}\n\t\n\t/// <summary>\n\t/// If s contains \\ and no newlines/controlchars: replaces all \" with \"\", prepends @\", appends \" and returns true.\n\t/// </summary>\n\t/// <param name=\"s\"></param>\n\t/// <returns></returns>\n\tpublic static bool MakeVerbatim(ref string s) {\n\t\tif (!s.Contains('\\\\') || s.RxIsMatch(@\"[\\x00-\\x1F\\x85\\x{2028}\\x{2029}]\")) return false;\n\t\ts = \"@\\\"\" + s.Replace(\"\\\"\", \"\\\"\\\"\") + \"\\\"\";\n\t\treturn true;\n\t}\n\t\n\t/// <summary>\n\t/// If s has *? characters, prepends \"**t \".\n\t/// s can be null.\n\t/// </summary>\n\tpublic static string EscapeWildex(string s) {\n\t\tif (wildex.hasWildcardChars(s)) s = \"**t \" + s;\n\t\treturn s;\n\t}\n\t\n\t/// <summary>\n\t/// If s has *? characters, prepends \"**t \".\n\t/// If <i>canMakeVerbatim</i>, finally calls <see cref=\"MakeVerbatim\"/>.\n\t/// s can be null.\n\t/// </summary>\n\tpublic static string EscapeWindowName(string s, bool canMakeVerbatim) {\n\t\tif (s != null) {\n\t\t\tif (wildex.hasWildcardChars(s)) s = \"**t \" + s;\n\t\t\tif (canMakeVerbatim) MakeVerbatim(ref s);\n\t\t}\n\t\treturn s;\n\t}\n\t//rejected: if name has * at the start or end, make regex like @\"**r \\*?\\QUntitled - Notepad\\E\", so that would find with or without *.\n\t//\tUgly. Most users will not know what it is. Anyway in most cases need to replace the document part with *.\n\t///// <summary>\n\t///// If s has *? characters, prepends \"**t \".\n\t///// But if s has single * character, converts to \"**r regex\" that ignores it. Because single * often is used to indicate unsaved state.\n\t///// If canMakeVerbatim, finally calls <see cref=\"MakeVerbatim\"/>.\n\t///// s can be null.\n\t///// </summary>\n\t//public static string EscapeWindowName(string s, bool canMakeVerbatim) {\n\t//\tif (s == null) return s;\n\t//\tif (wildex.hasWildcardChars(s)) {\n\t//\t\tint i = s.IndexOf('*');\n\t//\t\tif (i >= 0 && s.IndexOf('*', i + 1) < 0) {\n\t//\t\t\ts = \"**r \" + regexp.escapeQE(s[..i]) + @\"\\*?\" + regexp.escapeQE(s[++i..]);\n\t//\t\t} else s = \"**t \" + s;\n\t//\t}\n\t//\tif (canMakeVerbatim) MakeVerbatim(ref s);\n\t//\treturn s;\n\t//}\n\t\n\t/// <summary>\n\t/// Returns true if newRawValue does not match wildex tbValue, unless contains is like $\"...\" or $@\"...\".\n\t/// </summary>\n\t/// <param name=\"tbValue\">A wildex string, usually from a TextBox control. Can be raw or verbatim. Can be null.</param>\n\t/// <param name=\"newRawValue\">New raw string, not wildex. Can be null.</param>\n\tpublic static bool ShouldChangeTextBoxWildex(string tbValue, string newRawValue) {\n\t\ttbValue ??= \"\";\n\t\tif (newRawValue == null) newRawValue = \"\";\n\t\tif (IsVerbatim(tbValue, out _)) {\n\t\t\tif (tbValue[0] == '$') return false;\n\t\t\ttbValue = tbValue[2..^1].Replace(\"\\\"\\\"\", \"\\\"\");\n\t\t}\n\t\tvar x = new wildex(tbValue, noException: true);\n\t\treturn !x.Match(newRawValue);\n\t}\n\t\n\t/// <summary>\n\t/// Replaces known non-constant window class names with wildcard. Eg \"WindowsForms10.EDIT...\" with \"*.EDIT.*\".\n\t/// </summary>\n\t/// <param name=\"s\">Can be null.</param>\n\t/// <param name=\"escapeWildex\">If didn't replace, call <see cref=\"EscapeWildex\"/>.</param>\n\tpublic static string StripWndClassName(string s, bool escapeWildex) {\n\t\tif (!s.NE()) {\n\t\t\tint n = s.RxReplace(@\"^WindowsForms\\d+(\\..+?\\.).+\", \"*$1*\", out s);\n\t\t\tif (n == 0) n = s.RxReplace(@\"^(HwndWrapper\\[.+?;|Afx:).+\", \"$1*\", out s);\n\t\t\tif (escapeWildex && n == 0) s = EscapeWildex(s);\n\t\t}\n\t\treturn s;\n\t}\n\t\n\tpublic static string ArgsFromWndFindCode(string wndFind) {\n\t\tif (wndFind.RxMatch(@\"\\bwnd.find\\((?:-?\\d+, )?(.+)\\);\", 1, out RXGroup g)) return g.Value;\n\t\treturn null;\n\t}\n\t\n\t#endregion\n\t\n\t#region misc\n\t\n\t/// <summary>\n\t/// Gets Keyboard.FocusedElement. If null, and a HwndHost-ed control is focused, returns the HwndHost.\n\t/// Slow if HwndHost-ed control.\n\t/// </summary>\n\tpublic static FrameworkElement FocusedElement {\n\t\tget {\n\t\t\tvar v = System.Windows.Input.Keyboard.FocusedElement;\n\t\t\tif (v != null) return v as FrameworkElement;\n\t\t\treturn wnd.Internal_.ToWpfElement(Api.GetFocus());\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Inserts text in specified or focused control.\n\t/// At current position, not as new line, replaces selection.\n\t/// </summary>\n\t/// <param name=\"c\">If null, uses the focused control, else sets focus.</param>\n\t/// <param name=\"s\">If contains <c>`|`</c>, removes it and moves caret there; must be single line.</param>\n\tpublic static void InsertTextIn(FrameworkElement c, string s) {\n\t\tif (c == null) {\n\t\t\tc = FocusedElement;\n\t\t\tif (c == null) return;\n\t\t} else {\n\t\t\tDebug.Assert(Environment.CurrentManagedThreadId == c.Dispatcher.Thread.ManagedThreadId);\n\t\t\tif (c != FocusedElement) //be careful with HwndHost\n\t\t\t\tc.Focus();\n\t\t}\n\t\t\n\t\tint i = s.Find(\"`|`\");\n\t\tif (i >= 0) {\n\t\t\tDebug.Assert(!s.Contains('\\n'));\n\t\t\ts = s.Remove(i, 3);\n\t\t\ti = s.Length - i;\n\t\t}\n\t\t\n\t\tif (c is KScintilla sci) {\n\t\t\tif (sci.aaaIsReadonly) return;\n\t\t\tsci.aaaReplaceSel(s);\n\t\t\twhile (i-- > 0) sci.Call(Sci.SCI_CHARLEFT);\n\t\t} else if (c is TextBox tb) {\n\t\t\tif (tb.IsReadOnly) return;\n\t\t\ttb.SelectedText = s;\n\t\t\ttb.CaretIndex = tb.SelectionStart + tb.SelectionLength - Math.Max(i, 0);\n\t\t} else {\n\t\t\tDebug_.Print(c);\n\t\t\tif (!c.Hwnd().Window.ActivateL()) return;\n\t\t\tTask.Run(() => {\n\t\t\t\tvar k = new keys(null);\n\t\t\t\tk.AddText(s);\n\t\t\t\tif (i > 0) k.AddKey(KKey.Left).AddRepeat(i);\n\t\t\t\tk.SendNow();\n\t\t\t});\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Gets control id. Returns true if it can be used to identify the control in window wWindow.\n\t/// </summary>\n\tpublic static bool GetUsefulControlId(wnd wControl, wnd wWindow, out int id) {\n\t\tid = wControl.ControlId;\n\t\tif (id == 0 || id == -1 || id > 0xffff || id < -0xffff) return false;\n\t\t//info: some apps use negative ids, eg FileZilla. Usually >= -0xffff. Id -1 is often used for group buttons and separators.\n\t\t//if(id == (int)wControl) return false; //.NET forms, delphi. //possible coincidence //rejected, because window handles are > 0xffff\n\t\tDebug.Assert((int)wControl > 0xffff);\n\t\t\n\t\t//if(wWindow.Child(id: id) != wControl) return false; //problem with combobox child Edit that all have id 1001\n\t\tif (wWindow.ChildAll(id: id).Length != 1) return false; //note: searching only visible controls; else could find controls with same id in hidden pages of tabbed dialog.\n\t\treturn true;\n\t}\n\t\n\t/// <summary>\n\t/// Returns <b>wnd.Child</b> parameter <i>skip</i> if <i>c</i> is not the first found <i>w</i> child control with <i>name</i> and <i>cn</i>.\n\t/// </summary>\n\tpublic static string GetControlSkip(wnd w, wnd c, string name, string cn, bool hiddenToo) {\n\t\tif (!c.Is0) {\n\t\t\tvar a = w.ChildAll(name, cn, hiddenToo ? WCFlags.HiddenToo : 0);\n\t\t\tif (a.Length > 1 && a[0] != c) {\n\t\t\t\tint skip = Array.IndexOf(a, c);\n\t\t\t\tif (skip > 0) return skip.ToS();\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\t\n\t/// <summary>\n\t/// Calls EventManager.RegisterClassHandler for CheckBox.CheckedEvent, CheckBox.UncheckedEvent, TextBox.TextChangedEvent and optionally ComboBox.SelectionChangedEvent.\n\t/// Call from static ctor of KDialogWindow-based or ContentControl-based classes.\n\t/// The specified event handler will be called on events of any of these controls in all dialogs/containers of T type.\n\t/// </summary>\n\t/// <typeparam name=\"T\"></typeparam>\n\t/// <param name=\"changed\">Called on event.</param>\n\t/// <param name=\"comboToo\">Register for ComboBox too.</param>\n\tpublic static void OnAnyCheckTextBoxValueChanged<T>(Action<T, object> changed, bool comboToo = false) where T : ContentControl {\n\t\tvar h = new RoutedEventHandler((sender, e) => {\n\t\t\t//print.it(sender, e.Source, e.OriginalSource);\n\t\t\tvar source = e.OriginalSource; //e.Source can't be used; it == e.OriginalSource if T is KDialogWindow, but == sender if T is UserControl.\n\t\t\tif (source is CheckBox or TextBox or ComboBox) changed(sender as T, source);\n\t\t});\n\t\tEventManager.RegisterClassHandler(typeof(T), ToggleButton.CheckedEvent, h);\n\t\tEventManager.RegisterClassHandler(typeof(T), ToggleButton.UncheckedEvent, h);\n\t\tEventManager.RegisterClassHandler(typeof(T), TextBoxBase.TextChangedEvent, h);\n\t\tif (comboToo) EventManager.RegisterClassHandler(typeof(T), Selector.SelectionChangedEvent, h);\n\t}\n\t\n\t/// <summary>\n\t/// Takes screenshot of standard size to display in editor's margin.\n\t/// Color-quantizes, compresses and converts to a comment string to embed in code.\n\t/// Returns null if <b>LA.App.Settings.edit_noImages</b>.\n\t/// </summary>\n\t/// <param name=\"p\">Point in center of screenshot rectangle.</param>\n\t/// <param name=\"capt\">If used, temporarily hides its on-screen rect etc.</param>\n\tpublic static string MakeScreenshot(POINT p, CapturingWithHotkey capt = null) {\n\t\tif (LA.App.Settings.edit_noImages) return null;\n\t\tusing var thr = capt?.TempHideRect();\n\t\tconst int sh = 30;\n\t\treturn ColorQuantizer.MakeScreenshotComment(new(p.x - sh, p.y - sh / 2, sh * 2, sh));\n\t}\n\t\n\t#endregion\n\t\n\t#region OnScreenRect\n\t\n\t/// <summary>\n\t/// Creates standard <see cref=\"osdRect\"/>.\n\t/// </summary>\n\tpublic static osdRect CreateOsdRect(int thickness = 4) => new() { Color = 0xFF0000, Thickness = thickness, TopmostWorkaround_ = true }; //red\n\t\n\t/// <summary>\n\t/// Briefly shows standard blinking on-screen rectangle.\n\t/// If disp, shows async in its thread.\n\t/// </summary>\n\tpublic static void ShowOsdRect(RECT r, bool error = false, bool limitToScreen = false, Dispatcher disp = null) {\n\t\tif (disp != null) {\n\t\t\tdisp.InvokeAsync(() => ShowOsdRect(r, error, limitToScreen));\n\t\t} else {\n\t\t\tint thick = error ? 6 : 2;\n\t\t\tvar osr = new osdRect { Color = error ? 0xFF0000 : 0x0000FF, Thickness = thick * 2, TopmostWorkaround_ = true };\n\t\t\tr.Inflate(thick, thick); //2 pixels inside, 2 outside\n\t\t\tif (limitToScreen) {\n\t\t\t\tvar k = screen.of(r).Rect;\n\t\t\t\tr.Intersect(k);\n\t\t\t} else _LimitInsaneRect(ref r);\n\t\t\tosr.Rect = r;\n\t\t\t_OsdRectShow(osr, error);\n\t\t}\n\t}\n\t\n\tstatic void _OsdRectShow(osdRect osr, bool error = false) {\n\t\tt_hideCapturingRect = true;\n\t\tosr.Show();\n\t\tint i = 0;\n\t\ttimer.every(250, t => {\n\t\t\tif (i++ < 4) {\n\t\t\t\tosr.Hwnd.ZorderTopRaw_();\n\t\t\t\tosr.Color = (i & 1) != 0 ? 0xFFFF00 : error ? 0xFF0000 : 0x0000FF;\n\t\t\t} else {\n\t\t\t\tt.Stop();\n\t\t\t\tosr.Dispose();\n\t\t\t\tt_hideCapturingRect = false;\n\t\t\t}\n\t\t});\n\t}\n\t\n\t[ThreadStatic] static bool t_hideCapturingRect;\n\t\n\t//eg VS Code code editor and output are {W=1000000 H=1000000}. Then the rect drawing code would fail.\n\tstatic void _LimitInsaneRect(ref RECT r) {\n\t\tif (r.Width > 2000 || r.Height > 1200) {\n\t\t\tvar rs = screen.virtualScreen; rs.Inflate(100, 100);\n\t\t\tr.Intersect(rs);\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Briefly shows multiple on-screen rectangles.\n\t/// If disp, shows async in its thread.\n\t/// Can modify <i>a</i> elements.\n\t/// </summary>\n\tpublic static void ShowOsdRects(RECT[] a, Dispatcher disp = null) {\n\t\tif (disp != null) {\n\t\t\tdisp.InvokeAsync(() => ShowOsdRects(a));\n\t\t} else {\n\t\t\tvar osr = new osdRect { Color = 0x0000FF, Thickness = 2, TopmostWorkaround_ = true };\n\t\t\tfor (int i = 0; i < a.Length; i++) _LimitInsaneRect(ref a[i]);\n\t\t\tosr.SetRects(a);\n\t\t\t_OsdRectShow(osr);\n\t\t}\n\t}\n\t\n\t#endregion\n\t\n\t#region test\n\t\n\tpublic record RunTestFindResult(object obj, long speed, InfoStrings info);\n\t\n\t/// <summary>\n\t/// Executes test code that finds an object in window.\n\t/// Returns the found object, speed and info strings to display. On error speed negative.\n\t/// </summary>\n\t/// <param name=\"owner\">Owner dialog.</param>\n\t/// <param name=\"code\">\n\t/// Must start with one or more lines that find window or control and set wnd variable named <i>wndVar</i>. Can be any code.\n\t/// The last line must be a \"find object\" or \"find all objects\" function call, like <c>uiimage.find(...);</c>. No `var x = `, no \"not found\" exception, no wait, no action.\n\t/// If \"find all objects\", will display rectangles of all found objects and return the first found object.\n\t/// </param>\n\t/// <param name=\"wndVar\">Name of wnd variable of the window or control in which to search.</param>\n\t/// <param name=\"w\">Window or control in which to search. For a wnd tool can be 0.</param>\n\t/// <param name=\"getRect\">Callback function that returns object's rectangle in screen. Called when object has been found.</param>\n\t/// <param name=\"activateWindow\">Between finding window and object in it, activate the found window and wait 200 ms.</param>\n\t/// <param name=\"restoreOwner\">If this func minimizes or deactivates the owner window, it sets a timer to restore it after eg ~2 seconds. If <i>restoreOwner</i> not null, the timer will delay restoring until restoreOwner[0] != 0, after restoreOwner[0] ms.</param>\n\t/// <param name=\"rectDisp\">Use this dispatcher to show rectangles. For example if calling this in a non-UI thread and want to show in UI thread.</param>\n\t/// <example>\n\t/// <code><![CDATA[\n\t/// var rr = TUtil.RunTestFindObject(this, code, wndVar, _wnd, o => (o as elm).Rect);\n\t/// _info.InfoErrorOrInfo(rr.info);\n\t/// ]]></code>\n\t/// </example>\n\tpublic static RunTestFindResult RunTestFindObject(\n\t\tAnyWnd owner, string code, string wndVar, wnd w,\n\t\tFunc<object, RECT> getRect = null,\n\t\t/*\n\t\t/// <param name=\"invoke\">Callback that executes the code. Let it call/return MethodInfo.Invoke(null, null). For example if wants to execute in other thread. If null, the code is executed in this thread.</param>\n\t\tFunc<MethodInfo, object> invoke = null\n\t\t*/\n\t\tbool activateWindow = false, int[] restoreOwner = null, Dispatcher rectDisp = null) {\n\t\t\n\t\tDebug.Assert(!code.NE());\n\t\t//print.it(code);\n\t\t//perf.first();\n\t\t\n\t\tvar code0 = code;\n\t\tvar b = new StringBuilder();\n\t\tb.AppendLine(@\"static object[] __TestFunc__() {\");\n\t\tif (activateWindow) b.Append(\"((wnd)\").Append(w.Window.Handle).Append(\").ActivateL(); 300.ms(); \");\n\t\tb.AppendLine(\"var _p_ = perf.local();\");\n\t\tb.AppendLine(\"#line 1\");\n\t\tvar lines = code.Lines(true);\n\t\tint lastLine = lines.Length - 1;\n\t\tfor (int i = 0; i < lastLine; i++) b.AppendLine(lines[i]);\n\t\tb.AppendLine(\"_p_.Next(); var _a_ =\");\n\t\tb.AppendLine(\"#line \" + (lastLine + 1));\n\t\tb.AppendLine(lines[lastLine]);\n\t\tb.AppendLine(\"_p_.Next();\");\n\t\tb.AppendLine($\"return new object[] {{ _p_.ToArray(), _a_, {wndVar} }};\");\n\t\tb.AppendLine(\"\\r\\n}\");\n\t\tcode = b.ToString(); //print.it(code);\n\t\t\n\t\t(long[] speed, object obj, wnd w) r = default;\n\t\twnd dlg = owner.Hwnd;\n\t\tbool dlgWasActive = dlg.IsActive, dlgMinimized = false;\n\t\ttry {\n\t\t\ttry {\n\t\t\t\tif (!Scripting.Compile(code, out var c, addUsings: true, addGlobalCs: true, wrapInClass: true, dll: true)) {\n\t\t\t\t\tDebug_.Print(\"---- CODE ----\\r\\n\" + code + \"--------------\");\n\t\t\t\t\t//shows code too, because it may be different than in the code box\n\t\t\t\t\treturn new(null, -1, new(true, \"Errors:\", $\"{c.errors}\\r\\n\\r\\n<lc #C0C0C0><b>Code:<><>\\r\\n<code>{code0}</code>\"));\n\t\t\t\t}\n\t\t\t\t//object ro = invoke?.Invoke(c.method) ?? c.method.Invoke(null, null);\n\t\t\t\tobject ro = c.method.Invoke(null, null);\n\t\t\t\tvar rr = (object[])ro; //use array because fails to cast tuple, probably because in that assembly it is new type\n\t\t\t\tr = ((long[])rr[0], rr[1], (wnd)rr[2]);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tif (e is TargetInvocationException tie) e = tie.InnerException;\n\t\t\t\tstring s1, s2;\n\t\t\t\tif (e is NotFoundException) { //info: throws only when window not found\n\t\t\t\t\ts1 = \"Window not found\";\n\t\t\t\t\ts2 = \"Tip: If part of window name changes, replace it with *\";\n\t\t\t\t} else {\n\t\t\t\t\ts1 = e.GetType().Name;\n\t\t\t\t\ts2 = e.Message.RxReplace(@\"^Exception of type '.+?' was thrown. \", \"\");\n\t\t\t\t\tif (e.StackTrace.RxMatch(@\"(?m)^\\s*( at .+?)\\(.+\\R\\s+\\Qat __script__.__TestFunc__()\\E\", 1, out string s3)) s1 += s3;\n\t\t\t\t}\n\t\t\t\treturn new(null, -2, new(true, s1, s2));\n\t\t\t}\n\t\t\t\n\t\t\t//perf.nw();\n\t\t\t//print.it(r);\n\t\t\t\n\t\t\tstatic double _SpeedMcsToMs(long tn) => Math.Round(tn / 1000d, tn < 1000 ? 2 : (tn < 10000 ? 1 : 0));\n\t\t\tdouble t0 = _SpeedMcsToMs(r.speed[0]), t1 = _SpeedMcsToMs(r.speed[1]); //times of wnd.find and Object.Find\n\t\t\tstring sSpeed;\n\t\t\tif (lastLine == 1 && lines[0] == \"wnd w;\") sSpeed = t1.ToS() + \" ms\"; //only wnd.find: \"wnd w;\\r\\nw = wnd.find(...);\"\n\t\t\telse sSpeed = t0.ToS() + \" + \" + t1.ToS() + \" ms\";\n\t\t\t\n\t\t\tif (r.obj is wnd w1 && w1.Is0) r.obj = null;\n\t\t\t\n\t\t\t//FindAll used instead of Find?\n\t\t\tvar en = r.obj as IEnumerable<object>;\n\t\t\tif (en != null && !en.Any()) r.obj = null;\n\t\t\t\n\t\t\tif (r.obj != null) {\n\t\t\t\tRECT re;\n\t\t\t\tif (en != null) {\n\t\t\t\t\tvar ar = en.Select(o => getRect(o)).ToArray();\n\t\t\t\t\tre = RECT.FromLTRB(ar.Min(o => o.left), ar.Min(o => o.top), ar.Max(o => o.right), ar.Max(o => o.bottom));\n\t\t\t\t\tr.obj = en.First();\n\t\t\t\t\tShowOsdRects(ar, disp: rectDisp);\n\t\t\t\t} else {\n\t\t\t\t\tre = getRect(r.obj);\n\t\t\t\t\tShowOsdRect(re, disp: rectDisp);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t//if dlg covers the found object, temporarily minimize it (may be always-on-top) and activate object's window. Never mind owners.\n\t\t\t\tvar wTL = r.w.Window;\n\t\t\t\tif (dlgMinimized = dlg.Rect.IntersectsWith(re) && !r.w.IsOfThisThread && !dlg.IsMinimized) {\n\t\t\t\t\tdlg.ShowMinimized(1);\n\t\t\t\t\twTL.ActivateL();\n\t\t\t\t\twait.doEvents(1000);\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tif (r.w != w && !r.w.Is0 && !w.Is0) {\n\t\t\t\tShowOsdRect(r.w.Rect, error: true, limitToScreen: true, disp: rectDisp);\n\t\t\t\t//FUTURE: show list of objects inside the wanted window, same as in the Dwnd 'contains' combo. Let user choose. Then update window code quickly.\n\t\t\t\t//string wndCode = null;\n\t\t\t\t//wndCode = \"wnd w = wnd.find(\\\"Other\\\");\";\n\t\t\t\treturn new(null, -3, new(true, \"Finds another \" + (r.w.IsChild ? \"control\" : \"window\"), $\"<i>Need:<>  {w}\\r\\n<i>Found:<>  {r.w}\"));\n\t\t\t}\n\t\t\t\n\t\t\treturn new(r.obj, r.speed[1], new(r.obj == null, r.obj != null ? \"Found\" : \"Not found\", null, \",  speed \" + sSpeed));\n\t\t}\n\t\tfinally {\n\t\t\tif (dlgWasActive || dlgMinimized) {\n\t\t\t\tint after = activateWindow && !dlgMinimized && r.w == w ? 1500 : 500;\n\t\t\t\ttimer.after(after, t => {\n\t\t\t\t\tif (!dlg.IsAlive) return;\n\t\t\t\t\tif (restoreOwner == null) {\n\t\t\t\t\t\tif (dlgMinimized) dlg.ShowNotMinimized(1);\n\t\t\t\t\t\tif (dlgWasActive) dlg.ActivateL();\n\t\t\t\t\t} else if (restoreOwner[0] == 0) {\n\t\t\t\t\t\tt.After(100);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tt.After(restoreOwner[0]);\n\t\t\t\t\t\trestoreOwner = null;\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\t\n\tpublic record InfoStrings(bool isError, string header, string text, string headerSmall = null) {\n\t\t//public string text2 { get; set; }\n\t}\n\t\n\t/// <summary>\n\t/// Executes action code for a found UI element etc.\n\t/// </summary>\n\t/// <returns>Error strings to display, or null if no error.</returns>\n\t/// <param name=\"obj\">The element. The function passes it to the test script.</param>\n\t/// <param name=\"code\">Code like \"Method(arguments)\". The function prepends \"obj.\" and appends \";\".</param>\n\tpublic static InfoStrings RunTestAction(object obj, string code) {\n\t\tvar code0 = code;\n\t\tcode = $@\"static void __TestFunc__({obj.GetType()} obj) {{\n#line 1\nobj.{code};\n}}\";\n\t\t//print.it(code);\n\t\t\n\t\ttry {\n\t\t\tif (!Scripting.Compile(code, out var c, addUsings: true, addGlobalCs: true, wrapInClass: true, dll: true))\n\t\t\t\treturn new(true, \"Errors:\", $\"{c.errors}\\r\\n\\r\\n<b>Code:<>\\r\\n<code>obj.{code0};</code>\");\n\t\t\tc.method.Invoke(null, new object[] { obj });\n\t\t\treturn null;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tif (e is TargetInvocationException tie) e = tie.InnerException;\n\t\t\treturn new(true, \"Action failed\", e.GetType().Name + \". \" + e.Message.RxReplace(@\"^Exception of type '.+?' was thrown. \", \"\"));\n\t\t}\n\t}\n\t\n\tpublic static async Task<InfoStrings> RunTestCodeAsync(string code, CancellationToken cancel = default) {\n\t\tvar code0 = code;\n\t\tcode = $@\"static void __TestFunc__(CancellationToken cancel) {{\n#line 1\n{code}\n}}\";\n\t\t//print.it(code);\n\t\t\n\t\ttry {\n\t\t\tif (!Scripting.Compile(code, out var c, addUsings: true, addGlobalCs: true, wrapInClass: true, dll: true)) {\n\t\t\t\tDebug_.Print(\"---- CODE ----\\r\\n\" + code + \"--------------\");\n\t\t\t\treturn new(true, \"Errors:\", $\"{c.errors}\\r\\n\\r\\n<b>Code:<>\\r\\n<code>{code0};</code>\");\n\t\t\t}\n\t\t\tawait Task.Run(() => { c.method.Invoke(null, new object[] { cancel }); });\n\t\t\treturn null;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tif (e is TargetInvocationException tie) e = tie.InnerException;\n\t\t\treturn new(true, \"Failed\", e.GetType().Name + \". \" + e.Message.RxReplace(@\"^Exception of type '.+?' was thrown. \", \"\"));\n\t\t}\n\t}\n\t\n\t#endregion\n\t\n\t#region info\n\t\n\tpublic static void InfoError(this KSciInfoBox t, string header, string text, string headerSmall = null) {\n\t\tt.aaaText = $\"<lc #F0E080><b>{header}<>{headerSmall}<>\\r\\n{text}\";\n\t\tt.AaSuspendElems();\n\t}\n\t\n\tpublic static void InfoInfo(this KSciInfoBox t, string header, string text, string headerSmall = null) {\n\t\tt.aaaText = $\"<lc #C0E0C0><b>{header}<>{headerSmall}<>\\r\\n{text}\";\n\t\tt.AaSuspendElems();\n\t}\n\t\n\tpublic static void InfoErrorOrInfo(this KSciInfoBox t, InfoStrings info) {\n\t\tvar text = info.text /*?? info.text2*/;\n\t\tif (info.isError) InfoError(t, info.header, text, info.headerSmall);\n\t\telse InfoInfo(t, info.header, text, info.headerSmall);\n\t}\n\t\n\tpublic static void Info(this KSciInfoBox t, FrameworkElement e, string name, string text) {\n\t\ttext = CommonInfos.PrependName(name, text);\n\t\tt.AaAddElem(e, text);\n\t}\n\t\n\tpublic static void InfoC(this KSciInfoBox t, ContentControl k, string text) => Info(t, k, _ControlName(k), text);\n\t\n\tpublic static void InfoCT(this KSciInfoBox t, KCheckTextBox k, string text, bool isWildex = false, string wildexPart = null) {\n\t\ttext = CommonInfos.PrependName(_ControlName(k.c), text);\n\t\tif (isWildex) text = CommonInfos.AppendWildexInfo(text, wildexPart);\n\t\tt.AaAddElem(k.c, text);\n\t\tt.AaAddElem(k.t, text);\n\t}\n\t\n\tpublic static void InfoCO(this KSciInfoBox t, KCheckComboBox k, string text) {\n\t\ttext = CommonInfos.PrependName(_ControlName(k.c), text);\n\t\tt.AaAddElem(k.c, text);\n\t\tt.AaAddElem(k.t, text);\n\t}\n\t\n\t/// <summary>\n\t/// Returns k text without '_' character used for Alt+underline.\n\t/// </summary>\n\tstatic string _ControlName(ContentControl k) => StringUtil.RemoveUnderlineChar(k.Content as string, '_');\n\t\n\t/// <summary>\n\t/// Can be used by tool dialogs to display common info in <see cref=\"KSciInfoBox\"/> control.\n\t/// </summary>\n\tpublic class CommonInfos {\n\t\tKSciInfoBox _control;\n\t\tRegexWindow _regexWindow;\n\t\t\n\t\tpublic CommonInfos(KSciInfoBox control) {\n\t\t\t_control = control;\n\t\t\t_control.AaTags.AddLinkTag(\"+regex\", o => _Regex(o));\n\t\t}\n\t\t\n\t\tvoid _Regex(string _) {\n\t\t\t_regexWindow ??= new();\n\t\t\tif (_regexWindow.Hwnd.Is0) {\n\t\t\t\t_regexWindow.ShowByRect(_control.Hwnd().Window, Dock.Bottom);\n\t\t\t} else _regexWindow.Hwnd.ShowL(true);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Formats \"name - text\" string, where name is bold: <![CDATA[\"<b>\" + name + \"<> - \" + text]]>.\n\t\t/// </summary>\n\t\tpublic static string PrependName(string name, string text) => \"<b>\" + name + \"<> - \" + text;\n\t\t\n\t\tpublic static string AppendWildexInfo(string s, string part = null) => s + \"\\r\\n\" + (part ?? \"The text\") +\n@\" is <help articles/Wildcard expression>wildcard expression<>. Can be <+regex>regex<>, like <mono>**rc regex<>.\nExamples:\n<mono>whole text\n*end\nstart*\n*middle*\ntime ??:??\n**t literal text\n**c case-sensitive text\n**tc case-sensitive literal\n**r regular expression\n**rc case-sensitive regex\n**n not this\n**m this||or this||**r or this regex||**n and not this\n**m(^^^) this^^^or this^^^or this\n@\"\"C# verbatim string\"\"\n<>\";\n\t\t\n\t\tpublic const string c_alsoParameter = @\"<i>also<> lambda.\nCan be multiline.\nCan use global usings and classes/functions from file \"\"global.cs\"\".\";\n\t}\n\t\n\t/// <summary>\n\t/// Auto-creates and shows click-closed system-colored tooltip below element e.\n\t/// </summary>\n\tpublic static void InfoTooltip(ref KPopup p, UIElement e, string text, Dock side = Dock.Bottom) {\n\t\tif (p == null) {\n\t\t\tp = new(WS.POPUP | WS.BORDER, shadow: true) { Content = new Label(), ClickClose = KPopup.CC.Anywhere };\n\t\t\tp.Border.Background = SystemColors.InfoBrush;\n\t\t}\n\t\t(p.Content as Label).Content = text;\n\t\tp.ShowByRect(e, side);\n\t}\n\t\n\t///// <summary>\n\t///// Auto-creates and shows tooltip below element e. The tooltip has system colors, not WPF colors.\n\t///// </summary>\n\t//public static void InfoTooltip(ref ToolTip tt, UIElement e, string text) {\n\t//\ttt ??= new ToolTip { StaysOpen = false, Placement = PlacementMode.Bottom, Background = SystemColors.InfoBrush, Foreground = SystemColors.InfoTextBrush };\n\t//\ttt.PlacementTarget = e;\n\t//\ttt.Content = text;\n\t//\ttt.IsOpen = false;\n\t//\ttt.IsOpen = true;\n\t//}\n\t\n\tpublic static void InfoRectCoord(wnd w, string rect) {\n\t\tif (!rect.RxMatch(@\"^ *\\( *(\\d+) *, *(\\d+) *, *(\\d+) *, *(\\d+) *\\) *\", out var m)) return;\n\t\tInfoRectCoord(w.ClientRect, new RECT(m[1].Value.ToInt(), m[2].Value.ToInt(), m[3].Value.ToInt(), m[4].Value.ToInt()));\n\t}\n\t\n\tpublic static void InfoRectCoord(RECT rOuter, RECT rInner) {\n\t\tvar b = new StringBuilder();\n\t\tb.AppendFormat(\"<><help Au.Types.IFArea>IFArea<> <help Au.Types.Coord>Coord<> values equivalent to RECT ({0}, {1}, {2}, {3}) for window size ({4}, {5}): <fold>\\r\\n\", rInner.left, rInner.top, rInner.Width, rInner.Height, rOuter.Width, rOuter.Height);\n\t\tb.AppendFormat(\"           {0,-8} {1,-8} {2,-8} {3}\\r\\n\", \"left\", \"top\", \"right\", \"bottom\");\n\t\tfor (int i = 0; i < 3; i++) {\n\t\t\tb.AppendFormat(\"{0,-11}\", i switch { 0 => \"Simple\", 1 => \"Reverse\", _ => \"Fraction\" });\n\t\t\tfor (int j = 0; j < 4; j++) {\n\t\t\t\tvar (e, s1) = j switch { 0 => (rInner.left, \"left\"), 1 => (rInner.top, \"top\"), 2 => (rInner.right, \"right\"), _ => (rInner.bottom, \"bottom\") };\n\t\t\t\tint wh = ((j & 1) == 0 ? rOuter.Width : rOuter.Height);\n\t\t\t\tb.AppendFormat(\"{0,-9}\", i switch { 0 => e.ToS(), 1 => \"^\" + (wh - e).ToS(), _ => wh == 0 ? \"0\" : ((double)e / wh).ToS(\"0.###\") + \"f\" });\n\t\t\t}\n\t\t\tb.AppendLine();\n\t\t}\n\t\tb.Append(\"</fold>\");\n\t\tprint.it(b);\n\t}\n\t\n\t#endregion\n}\n"
  },
  {
    "path": "Au.Editor/Tools/ToolProcess.cs",
    "content": "using System.Runtime.Loader;\nusing System.Windows;\n\nnamespace ToolLand;\n\n/// <summary>\n/// Tools like \"Find UI element\" run in a separate Au.Editor.exe process. They are implemented in Au.Controls.dll.\n/// The <c>StartX</c> functions are used by the main Au.Editor.exe process to start a tool process.\n/// Then <c>Run</c> is called in a tool process.\n/// </summary>\nstatic class ToolProcess {\n\tpublic static void StartDwnd(wnd w = default, DwndFlags flags = 0) {\n\t\t_Start($\"Dwnd {w.Handle} {(int)flags}\");\n\t}\n\t\n\tpublic static void StartDelm(POINT? p = null) {\n\t\t_Start($\"Delm {p?.x} {p?.y}\");\n\t}\n\t\n\tpublic static void StartDuiimage(wnd wCapture = default) {\n\t\t_Start($\"Duiimage {wCapture.Handle}\");\n\t}\n\t\n\tpublic static void StartDocr() {\n\t\t_Start($\"Docr\");\n\t}\n\t\n\tstatic void _Start(string commandline) {\n\t\tvar ps = new ProcessStarter_(process.thisExePath, $\"/tool {LA.App.Hmain.Handle} {commandline}\", rawExe: true);\n\t\tps.Start();\n\t}\n\t\n\tpublic static void Run(ReadOnlySpan<string> args) {\n\t\t//perf.first();\n\t\t\n\t\ttry {\n\t\t\tvar poDir = folders.ThisAppDataLocal + \"optimization\";\n\t\t\tif (!Directory.Exists(poDir)) Directory.CreateDirectory(poDir);\n\t\t\tAssemblyLoadContext.Default.SetProfileOptimizationRoot(poDir);\n\t\t\tDebug.Assert(args[1][0] == 'D'); //Dwnd etc\n\t\t\tAssemblyLoadContext.Default.StartProfileOptimization(args[1]);\n\t\t}\n\t\tcatch (Exception ex) { Debug_.Print(ex); }\n\t\t\n\t\tTask task1 = Task.Run(() => { LA.AppSettings.Load(); }); //makes startup faster, although with PO not so much\n\t\t\n\t\t_ToolProcess(args[1..], (wnd)args[0].ToInt(), task1);\n\t}\n\t\n\tstatic void _ToolProcess(ReadOnlySpan<string> args, wnd wMain, Task task1) {\n\t\t//perf.next();\n\t\tprocess.ThisThreadSetComApartment_(ApartmentState.STA);\n\t\tprocess.thisProcessCultureIsInvariant = true;\n\t\tLA.App.InitThisAppFoldersEtc_();\n\t\tAssemblyLoadContext.Default.Resolving += (alc, an) => alc.LoadFromAssemblyPath($@\"{folders.ThisAppBS}\\Roslyn\\{an.Name}.dll\");\n\t\t\n\t\tnew System.Windows.Window { Content = new System.Windows.Controls.Button() };\n\t\t//perf.next();\n\t\ttask1.Wait();\n\t\t//perf.next('t');\n\t\t//timer.after(1, _ => { perf.nw(); });\n\t\t\n\t\ttimer.after(1000, _1 => {\n\t\t\tTask.Run(() => { //warm-up compiler\n\t\t\t\ttry { if (!Scripting.Compile(\"static void M() { print.it(1); }\", out _, true, true, true, true)) throw null; }\n\t\t\t\tcatch (Exception ex) { Debug_.Print(ex); }\n\t\t\t});\n\t\t\t\n\t\t\tif (!wMain.Is0) {\n\t\t\t\tvar w = s_wTool.Hwnd();\n\t\t\t\ttimer2.every(200, _ => { //exit when main window closed\n\t\t\t\t\tif (!wMain.IsAlive) {\n\t\t\t\t\t\tw.Close(noWait: true);\n\t\t\t\t\t\tif (wait.until(-2, () => !w.IsAlive)) 500.ms();\n\t\t\t\t\t\tApi.ExitProcess(0); //Environment.Exit not always works when main thread is hung\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\t\t\n\t\tif (args[0] is \"Dwnd\") {\n\t\t\ts_wTool = new Dwnd((wnd)args[1].ToInt(), (DwndFlags)args[2].ToInt());\n\t\t\ts_wTool.ShowDialog();\n\t\t} else if (args[0] is \"Delm\") {\n\t\t\ts_wTool = new Delm(args.Length > 2 ? new POINT(args[1].ToInt(), args[2].ToInt()) : null);\n\t\t\ts_wTool.ShowDialog();\n\t\t} else if (args[0] is \"Duiimage\") {\n\t\t\ts_wTool = new Duiimage((wnd)args[1].ToInt());\n\t\t\ts_wTool.ShowDialog();\n\t\t} else if (args[0] is \"Docr\") {\n\t\t\ts_wTool = new Docr();\n\t\t\ts_wTool.ShowDialog();\n\t\t}\n\t}\n\t\n\tstatic Window s_wTool;\n}\n"
  },
  {
    "path": "Au.Editor/Tools/WindowFindCodeFormatter.cs",
    "content": "namespace ToolLand;\n\nstatic partial class TUtil {\n\tpublic record WindowFindCodeFormatter {\n\t\tpublic string nameW, classW, programW, containsW, alsoW, waitW, orRunW, andRunW;\n\t\tpublic bool hiddenTooW, cloakedTooW, programNotStringW;\n\t\tpublic string idC, nameC, classC, alsoC, skipC, nameC_comments, classC_comments;\n\t\tpublic bool hiddenTooC;\n\t\tpublic bool NeedWindow = true, NeedControl, Throw, Activate, Test, Finder;\n\t\tpublic string CodeBefore, VarWindow = \"w\", VarControl = \"c\";\n\t\t\n\t\tpublic string Format() {\n\t\t\tif (!(NeedWindow || NeedControl)) return CodeBefore;\n\t\t\tvar b = new StringBuilder(CodeBefore);\n\t\t\tif (CodeBefore != null && !CodeBefore.Ends('\\n')) b.AppendLine();\n\t\t\t\n\t\t\tbool orThrow = false, orRun = false, andRun = false, activate = false, needWndFinder = false, needChildFinder = false;\n\t\t\tif (!(Test || Finder)) {\n\t\t\t\torThrow = Throw;\n\t\t\t\torRun = orRunW != null;\n\t\t\t\tandRun = andRunW != null;\n\t\t\t\tactivate = Activate;\n\t\t\t}\n\t\t\tif (Finder && !Test) {\n\t\t\t\tneedChildFinder = NeedControl;\n\t\t\t\tneedWndFinder = !needChildFinder;\n\t\t\t}\n\t\t\t\n\t\t\tif (NeedWindow && !needChildFinder) {\n\t\t\t\tbool orThrowW = orThrow || NeedControl;\n\t\t\t\t\n\t\t\t\tif (needWndFinder) {\n\t\t\t\t\tb.Append(\"wndFinder f = new(\");\n\t\t\t\t} else {\n\t\t\t\t\tb.Append(Test ? \"wnd \" : \"var \").Append(VarWindow);\n\t\t\t\t\tif (Test) b.AppendLine(\";\").Append(VarWindow);\n\t\t\t\t\t\n\t\t\t\t\tif (orRun) {\n\t\t\t\t\t\tb.Append(\" = wnd.findOrRun(\");\n\t\t\t\t\t} else if (andRun) {\n\t\t\t\t\t\tb.Append(\" = wnd.runAndFind(() => { \").Append(andRunW).Append(\" }, \");\n\t\t\t\t\t\tb.AppendWaitTime(waitW, orThrowW);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tb.Append(\" = wnd.find(\");\n\t\t\t\t\t\tif (waitW != null && !Test) b.AppendWaitTime(waitW, orThrowW);\n\t\t\t\t\t\telse if (orThrowW) b.Append('0');\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tb.AppendStringArg(nameW);\n\t\t\t\tint m = 0;\n\t\t\t\tif (classW != null) m |= 1;\n\t\t\t\tif (programW != null) m |= 2;\n\t\t\t\tif (m != 0) b.AppendStringArg(classW);\n\t\t\t\tif (programW != null) {\n\t\t\t\t\tif (!(programNotStringW || programW.Starts(\"WOwner.\"))) b.AppendStringArg(programW);\n\t\t\t\t\telse if (!Test) b.AppendOtherArg(programW);\n\t\t\t\t\telse m &= ~2;\n\t\t\t\t}\n\t\t\t\tif (FormatFlags(out var s1, (hiddenTooW, WFlags.HiddenToo), (cloakedTooW, WFlags.CloakedToo))) b.AppendOtherArg(s1, m < 2 ? \"flags\" : null);\n\t\t\t\tif (alsoW != null) b.AppendOtherArg(alsoW, \"also\");\n\t\t\t\tif (containsW != null) b.AppendStringArg(containsW, \"contains\");\n\t\t\t\t\n\t\t\t\tif (orRun) {\n\t\t\t\t\tb.Append(\", run: () => { \").Append(orRunW).Append(\" }\");\n\t\t\t\t\tif (!orThrowW) b.Append(\", wait: -60\");\n\t\t\t\t}\n\t\t\t\tif (orRun || andRun) {\n\t\t\t\t\tif (!activate) b.Append(\", activate: !true\");\n\t\t\t\t\tactivate = false;\n\t\t\t\t}\n\t\t\t\tb.Append(')');\n\t\t\t\tif (activate && orThrowW) { b.Append(\".Activate()\"); activate = false; }\n\t\t\t\tb.Append(';');\n\t\t\t}\n\t\t\t\n\t\t\tif (NeedControl) {\n\t\t\t\tif (needChildFinder) {\n\t\t\t\t\tb.Append(\"wndChildFinder cf = new(\");\n\t\t\t\t} else {\n\t\t\t\t\tif (NeedWindow) b.AppendLine();\n\t\t\t\t\tif (!Test) b.Append(\"var \").Append(VarControl).Append(\" = \");\n\t\t\t\t\tb.Append(VarWindow).Append(\".Child(\");\n\t\t\t\t\tif (!Test) {\n\t\t\t\t\t\tif (waitW is not (null or \"0\") || orRun || andRun) b.Append(orThrow ? \"1\" : \"-1\");\n\t\t\t\t\t\telse if (orThrow) b.Append('0');\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (nameC != null) b.AppendStringArg(nameC);\n\t\t\t\tif (classC != null) b.AppendStringArg(classC, nameC == null ? \"cn\" : null);\n\t\t\t\tif (FormatFlags(out var s1, (hiddenTooC, WCFlags.HiddenToo))) b.AppendOtherArg(s1, nameC == null || classC == null ? \"flags\" : null);\n\t\t\t\tif (idC != null) b.AppendOtherArg(idC, \"id\");\n\t\t\t\tif (alsoC != null) b.AppendOtherArg(alsoC, \"also\");\n\t\t\t\tif (skipC != null) b.AppendOtherArg(skipC, \"skip\");\n\t\t\t\tb.Append(\");\");\n\t\t\t\t\n\t\t\t\tif (!Test && nameC == null) { //if no control name, append // classC_comments nameC_comments\n\t\t\t\t\tstring sn = nameC == null ? nameC_comments : null, sc = classC == null ? classC_comments : null;\n\t\t\t\t\tint m = 0; if (!sn.NE()) m |= 1; if (!sc.NE()) m |= 2;\n\t\t\t\t\tif (m != 0) {\n\t\t\t\t\t\tb.Append(\" // \");\n\t\t\t\t\t\tif (0 != (m & 2)) b.Append(sc.Limit(70));\n\t\t\t\t\t\tif (0 != (m & 1)) {\n\t\t\t\t\t\t\tif (0 != (m & 2)) b.Append(' ');\n\t\t\t\t\t\t\tb.AppendStringArg(sn.Limit(100).RxReplace(@\"^\\*\\*\\*\\w+ (.+)\", \"$1\"), noComma: true);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tif (!orThrow && !Test && !Finder) {\n\t\t\t\tb.Append(\"\\r\\nif(\").Append(NeedControl ? VarControl : VarWindow).Append(\".Is0) { print.it(\\\"not found\\\"); }\");\n\t\t\t\tif (activate) b.Append(\" else { \").Append(VarWindow).Append(\".Activate(); }\");\n\t\t\t}\n\t\t\t\n\t\t\treturn b.ToString();\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Sets <b>skipC</b> if <i>c</i> is not the first found <i>w</i> child control with <b>nameC</b>/<b>classC</b>/<b>hiddenTooC</b>.\n\t\t/// </summary>\n\t\tpublic void SetSkipC(wnd w, wnd c) {\n\t\t\tskipC = GetControlSkip(w, c, nameC, classC, hiddenTooC);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Fills top-level window fields.\n\t\t/// Call once, because does not clear unused fields.\n\t\t/// </summary>\n\t\tpublic void RecordWindowFields(wnd w, int waitS, bool activate, string owner = null) {\n\t\t\tNeedWindow = true;\n\t\t\tThrow = true;\n\t\t\tActivate = activate;\n\t\t\twaitW = waitS.ToS();\n\t\t\tstring name = w.Name;\n\t\t\tnameW = EscapeWindowName(name, true);\n\t\t\tclassW = StripWndClassName(w.ClassName, true);\n\t\t\tif (programNotStringW = owner != null) programW = owner; else if (name.NE()) programW = w.ProgramName;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Fills control fields.\n\t\t/// Call once, because does not clear unused fields.\n\t\t/// </summary>\n\t\t/// <param name=\"w\">Top-level window.</param>\n\t\t/// <param name=\"c\">Control.</param>\n\t\tpublic void RecordControlFields(wnd w, wnd c) {\n\t\t\tNeedControl = true;\n\t\t\t\n\t\t\tstring name = null, cn = StripWndClassName(c.ClassName, true);\n\t\t\tif (cn == null) return;\n\t\t\t\n\t\t\tbool _Name(string prefix, string value) {\n\t\t\t\tif (value.NE()) return false;\n\t\t\t\tname = prefix + EscapeWildex(value);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\t\n\t\t\tif (GetUsefulControlId(c, w, out int id)) {\n\t\t\t\tidC = id.ToS();\n\t\t\t\tclassC_comments = cn;\n\t\t\t\t_ = _Name(null, c.Name) || _Name(null, c.NameWinforms) || _Name(null, c.NameElm);\n\t\t\t\tnameC_comments = name;\n\t\t\t} else {\n\t\t\t\t_ = _Name(null, c.Name) || _Name(\"***wfName \", c.NameWinforms);\n\t\t\t\tnameC = name;\n\t\t\t\tclassC = cn;\n\t\t\t\t\n#if true\n\t\t\t\tSetSkipC(w, c);\n\t\t\t\tif (name == null && _Name(\"***elmName \", c.NameElm)) {\n\t\t\t\t\tif (skipC == null) nameC = name;\n\t\t\t\t\telse nameC_comments = name;\n\t\t\t\t\t//SetSkipC(w, c); //can be too slow\n\t\t\t\t}\n#else\n\t\t\t\tbool setSkip = true;\n\t\t\t\tif (name == null && _Name(\"***elmName \", c.NameElm)) {\n\t\t\t\t\tnameC = name;\n\t\t\t\t\tsetSkip = false; //can be too slow\n\t\t\t\t}\n\t\t\t\tif (setSkip) SetSkipC(w, c);\n#endif\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Triggers and toolbars/DCommandline.cs",
    "content": "using System.Windows;\nusing System.Windows.Controls;\nusing Au.Controls;\n\nnamespace LA;\n\nstatic class DCommandline {\n\tstatic FileNode _CurrentFile() {\n\t\tvar f = App.Model.CurrentFile;\n\t\tif (f != null) {\n\t\t\tif (f.IsExecutableDirectly()) return f;\n\t\t\tdialog.showInfo(null, \"This file isn't runnable as a script.\", owner: App.Wmain);\n\t\t}\n\t\treturn null;\n\t}\n\t\n\tstatic string _Quot(string s) {\n\t\tvar q = s.Contains(' ') ? \"\\\"\" : \"\";\n\t\treturn $\"{q}{s}{q}\";\n\t}\n\t\n\tpublic class Commandline : KDialogWindow {\n\t\tpublic static void ShowForCurrentFile() {\n\t\t\tif (_CurrentFile() is { } f) f.SingleDialog(() => new Commandline(f));\n\t\t}\n\t\t\n\t\tCommandline(FileNode f) {\n\t\t\tInitWinProp(\"Command line - \" + f.Name, App.Wmain);\n\t\t\tvar b = new wpfBuilder(this).WinSize(500);\n\t\t\tb.R.xAddInfoBlockT(\"This tool creates a command line string to run this script from other programs (cmd, PowerShell etc). More info in Cookbook folder \\\"Script\\\".\");\n\t\t\tb.R.Add(out KCheckBox cEditorNoPath, \"Editor program name without path\").Tooltip(\"Makes the string shorter. Not all programs support it.\");\n\t\t\tb.R.Add(out KCheckBox cWait, \"Wait until script ends\").Tooltip(\"Unchecked:  start the script and return its process id.\\nChecked: run the script and return its exit code. The caller can get script.writeResult text.\");\n\t\t\tb.R.Add(\"Script arguments\", out TextBox tArgs).Tooltip(\"Optional\");\n\t\t\tb.R.AddSeparator();\n\t\t\tb.R.StartStack();\n\t\t\tb.AddButton(\"Copy to clipboard\", _ => {\n\t\t\t\tvar sb = new StringBuilder();\n\t\t\t\tsb.Append(_Quot(cEditorNoPath.IsChecked ? process.thisExeName : process.thisExePath)).Append(' ');\n\t\t\t\tsb.Append(_Quot((cWait.IsChecked ? \"*\" : \"\") + f.ItemPathOrName()));\n\t\t\t\tif (tArgs.Text.NullIfEmpty_() is { } sa) sb.Append(' ').Append(sa);\n\t\t\t\t\n\t\t\t\tclipboard.text = sb.ToString();\n\t\t\t});\n\t\t\tb.AddOkCancel(null, \"Close\");\n\t\t\tb.End();\n\t\t\tb.End();\n\t\t}\n\t}\n\t\n\tpublic class Shortcut : KDialogWindow {\n\t\tpublic static void ShowForCurrentFile() {\n\t\t\tif (_CurrentFile() is { } f) f.SingleDialog(() => new Shortcut(f));\n\t\t}\n\t\t\n\t\tShortcut(FileNode f) {\n\t\t\tInitWinProp(\"Shortcut - \" + f.Name, App.Wmain);\n\t\t\tvar b = new wpfBuilder(this).WinSize(500);\n\t\t\tb.R.xAddInfoBlockT(\"This tool creates a shortcut (.lnk file) to run this script.\");\n\t\t\tb.R.Add(\"Script arguments\", out TextBox tArgs).Tooltip(\"Optional\");\n\t\t\tb.R.AddSeparator();\n\t\t\tb.R.StartStack();\n\t\t\tb.AddButton(\"Create shortcut...\", _ => {\n\t\t\t\tif (App.IsPortable && 1 != dialog.showWarning(\"Portable mode warning\", \"This will create a shortcut file. Portable apps should not create files on host computer.\\r\\n\\r\\nDo you want to continue?\", \"1 Yes|2 No\", owner: this)) return;\n\t\t\t\tvar d = new FileOpenSaveDialog(\"38939d61-1971-45f5-8ce5-0c405aab792c\") {\n\t\t\t\t\tFileNameText = App.Model.CurrentFile.DisplayName + \".lnk\",\n\t\t\t\t\tFileTypes = \"Shortcut|*.lnk\",\n\t\t\t\t\tInitFolderFirstTime = folders.Desktop,\n\t\t\t\t\tTitle = \"Create shortcut\"\n\t\t\t\t};\n\t\t\t\tif (!d.ShowSave(out var lnk, this)) return;\n\t\t\t\tvar s = _Quot(f.ItemPathOrName());\n\t\t\t\tif (tArgs.Text.NullIfEmpty_() is { } sa) s = s + \" \" + sa;\n\t\t\t\ttry {\n\t\t\t\t\tusing var sh = shortcutFile.create(lnk);\n\t\t\t\t\tsh.TargetPath = process.thisExePath;\n\t\t\t\t\tsh.Arguments = s;\n\t\t\t\t\tsh.Save();\n\t\t\t\t\tClose();\n\t\t\t\t}\n\t\t\t\tcatch (Exception e1) { dialog.showError(\"Failed\", e1.ToStringWithoutStack(), owner: this); }\n\t\t\t});\n\t\t\tb.AddOkCancel(null);\n\t\t\tb.End();\n\t\t\tb.End();\n\t\t}\n\t}\n\t\n\tpublic class ShellMenu : KDialogWindow {\n\t\tpublic static void ShowForCurrentFile() {\n\t\t\tif (_CurrentFile() is { } f) f.SingleDialog(() => new ShellMenu(f));\n\t\t}\n\t\t\n\t\t[Flags] enum _Type { file = 1, directory = 2, drive = 4, back = 8, desktop = 16, taskbar = 32 }\n\t\t\n\t\tconst string infoUrl = \"https://www.libreautomate.com/forum/showthread.php?tid=7819\";\n\t\t\n\t\tShellMenu(FileNode f) {\n\t\t\tInitWinProp(\"Shell menu - \" + f.Name, App.Wmain);\n\t\t\tvar b = new wpfBuilder(this).WinSize(550);\n\t\t\tb.Options(bindLabelVisibility: true);\n\t\t\tb.R.xAddInfoBlockF($\"\"\"\nThis tool creates <a href='{infoUrl}'>Nilesoft Shell</a> code for adding a menu item to run this script (or submenu etc). Paste in a nss file. To apply: Ctrl+right-click in a folder window or desktop.\n\"\"\");\n\t\t\t\n\t\t\tb.R.xAddGroupSeparator(\"Menu item properties\");\n\t\t\tb.R.Add(\"Title\", out KTextBox tTitle, f.DisplayName);\n\t\t\tb.R.Add(\"Image\", out KTextBox tImage).Tooltip(@\"A font icon, like \\uE025 or image.glyph(\\uE025, #00e000)\nOr path of a file with icons, like c:\\dir\\file.ico, c:\\dir\\file.exe, c:\\dir\\file.dll,3\nOr color, like #c0ff80\");\n\t\t\t//b.And(26).xAddButtonIcon(\"*Material.HelpCircleOutline #79AEFF\" + EdIcons.blue, _ => _ImageButton(), \"Image reference\");\n\t\t\tb.And(26).xAddControlHelpButton(_ => _ImageButton(), \"Image reference\");\n\t\t\tb.R.Add(\"Tooltip\", out KTextBox tTip).Multiline();\n\t\t\tb.R.Add<Label>(\"Separator\"); b.StartStack().Add(out KCheckBox cSepBefore, \"before\").Add(out KCheckBox cSepAfter, \"after\").End();\n\t\t\t//b.R.Add(out KCheckBox cMenu, \"Add to an existing submenu\"); //rejected. Rarely used. Also would need pos.\n\t\t\t\n\t\t\tb.R.xAddGroupSeparator(\"Show if\");\n\t\t\tb.R.Add(\"Selection\", out ComboBox cbMode).Items(\n\t\t\t\t\"default setting (inherit from parent menu, or single)\",\n\t\t\t\t\"single (selected single item)\",\n\t\t\t\t\"multiple (selected one or more items)\",\n\t\t\t\t\"multi_unique (selected one or more items of the same object type)\",\n\t\t\t\t\"multi_single (selected one or more items of the same file type)\",\n\t\t\t\t\"none (no selection)\");\n\t\t\t\n\t\t\tb.R.Add<Label>(\"Object type\").StartPanel(new WrapPanel()).Margin(\"T3B3\");\n\t\t\tvar ty = new EnumUI<_Type>(b.Panel, _Type.file);\n\t\t\tb.End();\n\t\t\t\n\t\t\tb.R.Add(\"File types\", out KTextBox tFileTypes).Tooltip(\"Examples:\\n.exe\\n.png|.jpg|.ico\\n!.exe (not .exe)\");\n\t\t\tb.R.Add(out KCheckBox cShift, \"+Shift\");\n\t\t\t\n\t\t\tb.R.xAddGroupSeparator(\"Action\");\n\t\t\tb.R.Add(\"Action\", out ComboBox cbAction).Items(\"Run this script|Run program|Expression|Submenu\");\n\t\t\tb.R.Add(\"Program\", out KTextBox tProgram).Hidden(null);\n\t\t\tb.R.Add(\"Expression\", out KTextBox tExpression).Hidden(null);\n\t\t\tb.R.Add(\"Arguments\", out KTextBox tArgs, \"@sel(true)\")\n\t\t\t\t.Tooltip(\"@sel(true) means \\\"paths of selected files\\\".\\nYou can add more arguments before or after.\");\n\t\t\tb.And(26).xAddButtonIcon(out var bArgs, \"*Modern.LanguageCsharp\" + EdIcons.blue, _ => _ArgsButton(), \"Copy C# code\");\n\t\t\tcbAction.SelectionChanged += (_, _) => {\n\t\t\t\tint i = cbAction.SelectedIndex;\n\t\t\t\ttProgram.Visibility = i == 1 ? Visibility.Visible : Visibility.Collapsed;\n\t\t\t\ttExpression.Visibility = i == 2 ? Visibility.Visible : Visibility.Collapsed;\n\t\t\t\ttArgs.Visibility = bArgs.Visibility = i < 2 ? Visibility.Visible : Visibility.Collapsed;\n\t\t\t};\n\t\t\t\n\t\t\tb.R.AddSeparator().Margin(\"T8\");\n\t\t\tb.R.StartGrid().Columns(130, 0, -1, 100, 100);\n\t\t\tb.AddButton(\"Copy to clipboard\", _1 => {\n\t\t\t\tstatic string _SA(string prop, string s) => s.NE() ? null : _SA2(prop, s);\n\t\t\t\tstatic string _SA2(string prop, string s) => $\"{prop}='{s?.Replace(\"'\", \"@\\\"'\\\"\")}'\";\n\t\t\t\tstatic string _SQ(string prop, string s) => s.NE() ? null : $\"{prop}=\\\"{s.Escape()}\\\"\";\n\t\t\t\t\n\t\t\t\tvar sImage = tImage.Text;\n\t\t\t\tif (!sImage.NE()) {\n\t\t\t\t\tif (pathname.isFullPath(sImage, orEnvVar: true) || sImage.RxIsMatch(@\"\\.\\w{3}(?:, *-?\\d+)?$\")) {\n\t\t\t\t\t\tsImage = _SA(\"image\", sImage);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (sImage.RxMatch(@\"(?i)^(\\\\u)?([[:xdigit:]]{4})$\", out var m)) {\n\t\t\t\t\t\t\tstring u = m[1].Value ?? \"\\\\u\", h = m[2].Value;\n\t\t\t\t\t\t\tif (h[1] is >= '7') sImage = $\"image.segoe({u}{h}, 14)\"; //Windows icon font. DPI problems. 14 best, but not perfect. `image.glyph` bad.\n\t\t\t\t\t\t\telse sImage = u + h; //NS embedded glyphs\n\t\t\t\t\t\t}\n\t\t\t\t\t\tsImage = $\"image={sImage}\";\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tvar sCommon = $\"\"\"\n({_SA2(\"title\", tTitle.Text)}\n\t{sImage}\n\t{_SQ(\"tip\", tTip.Text)}\n\t{_SA(\"sep\", (cSepBefore.IsChecked, cSepAfter.IsChecked) switch { (true, false) => \"before\", (false, true) => \"after\", (true, true) => \"both\", _ => null })}\n\t{(cbMode.SelectedIndex == 0 ? \"\" : _SA(\"mode\", (cbMode.SelectedItem as string).Split(' ', 2)[0]))}\n\t{(ty.Result == 0 ? \"\" : _SA(\"type\", ty.Result.ToString().Replace(\", \", \"|\")))}\n\t{_SA(\"find\", tFileTypes.Text)}\n\t{(cShift.IsChecked ? \"vis=key.shift()\" : \"\")}\n\"\"\".RxReplace(@\"(?m)(\\R\\t)+$\", \"\");\n\t\t\t\tstring s;\n\t\t\t\tint i = cbAction.SelectedIndex;\n\t\t\t\tif (i == 3) { //submenu\n\t\t\t\t\ts = $$\"\"\"\nmenu{{sCommon}})\n{\n\n}\n\n\"\"\";\n\t\t\t\t} else {\n\t\t\t\t\tvar sca = i switch {\n\t\t\t\t\t\t0 => $\"cmd='{process.thisExeName}' {_SA(\"args\", $\"\\\"{f.ItemPathOrName()}\\\" {tArgs.Text}\")}\",\n\t\t\t\t\t\t1 => $\"{_SA2(\"cmd\", tProgram.Text)} {_SA2(\"args\", tArgs.Text)}\",\n\t\t\t\t\t\t_ => $\"cmd={tExpression.Text}\"\n\t\t\t\t\t};\n\t\t\t\t\ts = $\"\"\"\nitem{sCommon}\n\t{sca})\n\n\"\"\";\n\t\t\t\t\t//rejected: add UI for other command parameters: invoke, window, dir, admin, verb, wait.\n\t\t\t\t}\n\t\t\t\tclipboard.text = s;\n\t\t\t});\n\t\t\tb.AddOkCancel(null, \"Close\");\n\t\t\tb.Skip().AddButton(\"Open nss file\", _ => _OpenNSS());\n\t\t\tb.AddButton(\"On error ▾\", _ => _OnErrorButton());\n\t\t\tb.End();\n\t\t\tb.End();\n\t\t\t\n\t\t\tvoid _ImageButton() {\n\t\t\t\tvar m = new popupMenu();\n\t\t\t\tm[\"Glyph icons\"] = o => { run.it(\"https://nilesoft.org/gallery/glyphs\"); };\n\t\t\t\tm[\"Win11 font icons (Fluent)\"] = o => { run.it(\"https://learn.microsoft.com/en-us/windows/apps/design/style/segoe-fluent-icons-font\"); };\n\t\t\t\tm[\"Win10 font icons (MDL2)\"] = o => { run.it(\"https://learn.microsoft.com/en-us/windows/apps/design/style/segoe-ui-symbol-font\"); };\n\t\t\t\tm.Separator();\n\t\t\t\tm[\"Image function\"] = o => { run.it(\"https://nilesoft.org/docs/functions/image\"); };\n\t\t\t\tm[\"Image property\"] = o => { run.it(\"https://nilesoft.org/docs/configuration/properties#image\"); };\n\t\t\t\tm.Show(owner: this);\n\t\t\t}\n\t\t\t\n\t\t\tvoid _ArgsButton() {\n\t\t\t\tvar m = new popupMenu();\n\t\t\t\tm[\"Copy C# code \\\"foreach selected path\\\"\"] = o => { clipboard.text = \"\"\"\nforeach (string path in args) {\n\tprint.it(path);\n}\n\n\"\"\"; };\n\t\t\t\tm[\"Copy C# code \\\"foreach selected path\\\" when there are more arguments\"] = o => { clipboard.text = \"\"\"\nforeach (string path in args.Skip(1)) {\n\tprint.it(path);\n}\n\n\"\"\"; };\n\t\t\t\tm.Show(owner: this);\n\t\t\t}\n\t\t\t\n\t\t\tvoid _OnErrorButton() {\n\t\t\t\tvar m = new popupMenu();\n\t\t\t\tm[\"Print last error\"] = o => { if (_GetNsDir(out var nsDir)) try { print.it(File.ReadLines(nsDir + @\"\\shell.log\").Where(o => o.Contains(\"[error]\")).Last()); } catch { } };\n\t\t\t\tm[\"Restart Explorer\"] = o => { if (_GetNsDir(out var nsDir)) run.itSafe(nsDir + @\"\\shell.exe\", \"-restart\"); };\n\t\t\t\tm.Show(owner: this);\n\t\t\t}\n\t\t\t\n\t\t\tvoid _OpenNSS() {\n\t\t\t\tif (!_GetNsDir(out var nsDir)) return;\n\t\t\t\tvar nssFile = nsDir + @\"\\shell.nss\";\n\t\t\t\ttry {\n\t\t\t\t\tvar text = filesystem.loadText(nssFile);\n\t\t\t\t\tif (text.RxFindAll(@\"(?m)^\\h*import +'(?!imports[/\\\\])(.+?)'\", 1, out string[] a)) {\n\t\t\t\t\t\tnssFile = a[0];\n\t\t\t\t\t\tif (a.Length > 1) {\n\t\t\t\t\t\t\tint i = dialog.showList(a.Select(o => pathname.getName(o)).ToArray(), owner: this) - 1;\n\t\t\t\t\t\t\tif (i < 0) return;\n\t\t\t\t\t\t\tnssFile = a[i];\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (1 != dialog.show(\"Your nss file still not imported into shell.nss\", \"Open shell.nss?\", buttons: \"1 OK|0 Cancel\", footer: new($\"<a>Setup info</a>\", _NssLink), owner: this)) return;\n\t\t\t\t\t}\n\t\t\t\t\tif (!App.Model.OpenAndGoTo(nssFile, kind: FNFind.File)) run.itSafe(nssFile);\n\t\t\t\t}\n\t\t\t\tcatch { }\n\t\t\t}\n\t\t\t\n\t\t\tbool _GetNsDir(out string r) {\n\t\t\t\tr = App.Settings.nilesoftShellDir;\n\t\t\t\tif (!filesystem.exists(r).Directory) {\n\t\t\t\t\tr = folders.ProgramFiles + \"Nilesoft Shell\";\n\t\t\t\t\twhile (!filesystem.exists(r).Directory) {\n\t\t\t\t\t\tif (!dialog.showInput(out r, \"Where is Nilesoft Shell installed?\", $\"Nilesoft Shell folder path\", footer: new($\"<a>Setup info</a>\", _NssLink), owner: this)) return false;\n\t\t\t\t\t\tApp.Settings.nilesoftShellDir = r;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\t\n\t\t\tstatic void _NssLink(DEventArgs _) {\n\t\t\t\trun.itSafe(infoUrl);\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Triggers and toolbars/DSchedule.cs",
    "content": "using Au.Controls;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Controls.Primitives;\nusing System.Xml;\nusing System.Xml.Linq;\n\nnamespace LA;\n\nusing api = WinSchedulerApi;\nusing TT = WinSchedulerApi.TASK_TRIGGER_TYPE2;\nusing TTS = WinSchedulerApi.TASK_SESSION_STATE_CHANGE_TYPE;\n\nclass DSchedule : KDialogWindow {\n\tpublic static void ShowFor(FileNode f, int triggerIndex1Based = 0) {\n\t\tif (f == null) return;\n#if TEST\n\t\tvar d = new DSchedule(f, triggerIndex1Based);\n\t\td.ShowDialog();\n\t\t//d.ShowAndWait();\n\t\t//d.Show();\n\t\t//dialog.show(\"DSchedule\");\n#else\n\t\tif (!f.IsExecutableDirectly()) {\n\t\t\tdialog.showInfo(null, \"This file isn't runnable as a script.\", owner: App.Wmain);\n\t\t\treturn;\n\t\t}\n\t\tf.SingleDialog(() => new DSchedule(f, triggerIndex1Based) { Owner = App.Wmain, ShowInTaskbar = false });\n#endif\n\t}\n\t\n\tFileNode _f;\n\twpfBuilder _b;\n\tListBox _lbPages;\n\tGrid _gPages;\n\tevent Action<WBButtonClickArgs> _apply;\n\t\n\tapi.ITaskDefinition _td;\n\tstring _taskName;\n\t\n\tstatic readonly string s_taskFolder = @\"\\Au\\\" + Environment.UserName;\n\t\n\tDSchedule(FileNode f, int selectTrigger) {\n\t\t_f = f;\n\t\t\n\t\tTitle = \"Schedule - \" + _f.Name;\n\t\t\n\t\tvar b = _b = new wpfBuilder(this).WinSize(700, 420).Columns(-1.2, 8, -2);\n\t\tb.Options(bindLabelVisibility: true);\n\t\t\n\t\t//left column\n\t\t\n\t\tButton bDeleteTrigger = null;\n\t\t\n\t\tb.Row(-1).StartGrid().Columns(-1);\n\t\t\n\t\tb.Row(-1).Add(out _lbPages);\n\t\tScrollViewer.SetHorizontalScrollBarVisibility(_lbPages, ScrollBarVisibility.Disabled); //to wrap item text\n\t\t\n\t\tb.StartGrid().Columns(-1, -1);\n\t\tb.AddButton(\"New trigger\", _ => _NewTrigger(true));\n\t\tb.AddButton(out bDeleteTrigger, \"Delete trigger\", _ => _DeleteTrigger()).Disabled();\n\t\tb.End();\n\t\tb.R.Add(out KCheckBox cTaskEnabled, \"Task enabled\").Margin(\"T10B10\");\n\t\t\n\t\tb.End();\n\t\t\n\t\t//right column\n\t\t\n\t\tb.Skip(1).Add(out _gPages).Margin(\"0\");\n\t\t\n\t\tb.R.AddSeparator().Span(-1);\n\t\t\n\t\tb.StartGrid().Columns(-1, 0);\n\t\tb.R.xAddInfoBlockT(out var tExistsInfo, \"OK/Apply will create a new scheduled task.\");\n\t\tb.StartOkCancel();\n\t\tb.AddOkCancel(out var bOK, out _, out _, apply: \"Apply\");\n\t\tb.AddButton(\"Task Scheduler ▾\", _ => {\n\t\t\tvar m = new popupMenu();\n\t\t\tm[\"OK, open task in Task Scheduler\"] = o => {\n\t\t\t\tbOK.RaiseEvent(new RoutedEventArgs(Button.ClickEvent));\n\t\t\t\tif (!IsVisible) _EditTask(); //may be not closed if validation error\n\t\t\t};\n\t\t\tbool disabled = !_TaskExists();\n\t\t\tm[\"Cancel, open task in Task Scheduler\", disable: disabled] = o => { Close(); _EditTask(); };\n\t\t\tm.Submenu(\"Delete task\", m => {\n\t\t\t\tm[\"Delete the task and close this window\"] = o => {\n\t\t\t\t\tWinScheduler.DeleteTask(s_taskFolder, _taskName);\n\t\t\t\t\tClose();\n\t\t\t\t};\n\t\t\t}, disable: disabled);\n\t\t\tm[\"Open Task Scheduler\"] = o => { _EditTask(true); };\n\t\t\tm.Show(owner: this);\n\t\t\t\n\t\t\tvoid _EditTask(bool justOpenFolder = false) {\n\t\t\t\tWinScheduler.EditTask(s_taskFolder, justOpenFolder ? null : _taskName);\n\t\t\t}\n\t\t\t\n\t\t\tbool _TaskExists() => WinScheduler.TaskExists(s_taskFolder, _taskName);\n\t\t});\n\t\tb.End();\n\t\tb.End();\n\t\tb.End();\n\t\t\n\t\tb.OkApply += o => {\n\t\t\t//print.clear();\n\t\t\t\n\t\t\t_td.Settings.Enabled = cTaskEnabled.IsChecked;\n\t\t\t_apply(o);\n\t\t\t\n\t\t\t//update trigger strings in listbox\n\t\t\tvar tc = _td.Triggers;\n\t\t\tfor (int i = 1; i < _lbPages.Items.Count - 2; i++) {\n\t\t\t\tif (0 == tc.get_Item(i, out var v)) {\n\t\t\t\t\tvar li = _lbPages.Items[i] as ListBoxItem;\n\t\t\t\t\tli.Content = _CreatePageListItemContent(v.FormatTriggerString(), true);\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t//print.it(_td.XmlText); print.scrollToTop();\n\t\t\t\n\t\t\tint hr = WinScheduler.CreateTaskFromDefinition(s_taskFolder, _taskName, _td, UacIL.High, api.TASK_CREATION.TASK_CREATE_OR_UPDATE);\n\t\t\tif (hr != 0) {\n\t\t\t\tstring s = lastError.messageFor(hr);\n\t\t\t\tif (hr == Api.E_ACCESSDENIED && !uacInfo.isAdmin) s += \"\\n\\nRestart this program as administrator.\";\n\t\t\t\tdialog.showError(\"Failed to register scheduled task\", s, owner: this);\n\t\t\t\to.Cancel = true;\n\t\t\t} else {\n\t\t\t\ttExistsInfo.Hide_(true);\n\t\t\t}\n\t\t};\n\t\t\n\t\tbool exists = WinScheduler.FindScriptTask(_f.Name, _f.ItemPath, out var r);\n\t\tif (exists) {\n\t\t\t_taskName = r.rt.Name;\n\t\t\t_td = r.td;\n\t\t} else {\n\t\t\t_taskName = _f.Name[..^3];\n\t\t\tif (!WinScheduler.Connect(out var ts)) throw new AuException(\"*get task service\");\n\t\t\t\n\t\t\tvar sArgs = \"*\" + _f.ItemPathOrName();\n\t\t\tif (0 != ts.NewTask(0, out _td)) throw new AuException(\"*create task definition\");\n\t\t\t_td.XmlText = WinScheduler.CreateXmlForNewTask(UacIL.High, WinScheduler.EditorPath, sArgs);\n\t\t\t\n\t\t\t_NewTrigger(false);\n\t\t}\n\t\ttExistsInfo.Hide_(exists);\n\t\t//print.it(_td.XmlText);\n\t\t\n\t\tcTaskEnabled.IsChecked = _td.Settings.Enabled;\n\t\t\n\t\t_AddPage(\"Triggers\", null);\n\t\t_AddPage(\"Conditions\", _ConditionsPage());\n\t\t_AddPage(\"Settings\", _SettingsPage(r.scriptArgs));\n\t\t\n\t\tforeach (var t in _td.EnumTriggers()) {\n\t\t\t_CreateTriggerPage(t, false);\n\t\t}\n\t\t\n\t\t_lbPages.SelectionChanged += (_, e) => {\n\t\t\tint i = _lbPages.SelectedIndex, n = _lbPages.Items.Count;\n\t\t\tif (i == 0) {\n\t\t\t\t_lbPages.SelectedIndex = n > 3 ? 1 : -1;\n\t\t\t} else {\n\t\t\t\tif (e.RemovedItems is [ListBoxItem { Tag: UIElement p1 }]) p1.Visibility = Visibility.Hidden;\n\t\t\t\tif (e.AddedItems is [ListBoxItem { Tag: UIElement p2 }]) p2.Visibility = Visibility.Visible;\n\t\t\t\tbDeleteTrigger.IsEnabled = i < n - 2;\n\t\t\t}\n\t\t};\n\t\t\n#if TEST\n\t\t//_lbPages.SelectedIndex = _lbPages.Items.Count - 2;\n\t\t_lbPages.SelectedIndex = 1;\n#else\n\t\tint nTriggers = _lbPages.Items.Count - 3;\n\t\tif (selectTrigger > 0 && selectTrigger <= nTriggers) _lbPages.SelectedIndex = selectTrigger;\n\t\telse if (nTriggers > 0) _lbPages.SelectedIndex = 1;\n#endif\n\t\t\n#if WPF_PREVIEW\n\t\tb.Window.Preview();\n#endif\n\t}\n\t\n\tvoid _AddPage(string name, _Page page) {\n\t\tvar li = new ListBoxItem { Content = _CreatePageListItemContent(name, page is _TriggerPage), Tag = page };\n\t\t_lbPages.Items.Insert(_lbPages.Items.Count - (page is _TriggerPage ? 2 : 0), li);\n\t\tif (page != null) {\n\t\t\tpage.li = li;\n\t\t\tpage.Visibility = Visibility.Collapsed;\n\t\t\t_gPages.Children.Add(page);\n\t\t}\n\t}\n\t\n\tobject _CreatePageListItemContent(string text, bool isTrigger) {\n\t\tif (!isTrigger) return text;\n\t\tvar r = new DockPanel();\n\t\tvar img = ImageUtil.LoadWpfImageElement(\"*Codicons.SymbolEvent #4066FF @14\");\n\t\timg.VerticalAlignment = VerticalAlignment.Top;\n\t\timg.Margin = new(-3, 0, 0, 2);\n\t\tr.Children.Add(img);\n\t\tvar t = new TextBlock { Text = text, TextWrapping = TextWrapping.Wrap };\n\t\tr.Children.Add(t);\n\t\treturn r;\n\t}\n\t\n\tclass _Page : UserControl {\n\t\tpublic ListBoxItem li;\n\t\t\n\t\tpublic int Index => ((ListBox)li.Parent).Items.IndexOf(li);\n\t}\n\t\n\t_Page _ConditionsPage() {\n\t\tvar sett = _td.Settings;\n\t\t\n\t\t_Page uc = new();\n\t\tvar b = new wpfBuilder(uc);\n\t\tb.Options(bindLabelVisibility: true);\n\t\t\n\t\t//print.it(sett.);\n\t\t\n\t\tb.R.xAddGroupSeparator(\"Idle\");\n\t\t\n\t\t_TimeIntervalControls kIdle = new(this, b, \"Start only if the computer is idle for:\", \"1 m|10 m|1 h\", 1, (\"PT1M\", \"P31D\"));\n\t\t_TimeIntervalControls kIdleWait = new(this, b, \"Wait for idle for\", \"0 s|1 m|10 m|1 h\", 3, cOther: kIdle.c);\n\t\tb.Tooltip(\"Max time interval to wait for the above condition\");\n\t\tb.Last2.HorizontalAlignment = HorizontalAlignment.Right;\n\t\tb.R.Add(out KCheckBox cIdleStop, \"Stop if the computer ceases to be idle\").Margin(20).xBindCheckedEnabled(kIdle.c)\n\t\t\t.Tooltip(\"Note: triggers of type \\\"Computer idle\\\" may stop the task even if this isn't checked.\");\n\t\tb.R.Add(out KCheckBox cIdleRestart, \"Restart when idle again\").Margin(40).xBindCheckedEnabled(kIdle.c);\n\t\tif (sett.RunOnlyIfIdle) {\n\t\t\tvar idle = sett.IdleSettings;\n\t\t\tkIdle.Value = idle.IdleDuration;\n\t\t\tkIdleWait.Value0 = idle.WaitTimeout;\n\t\t\tcIdleStop.IsChecked = idle.StopOnIdleEnd;\n\t\t\tcIdleRestart.IsChecked = idle.RestartOnIdle;\n\t\t}\n\t\t\n\t\tb.R.xAddGroupSeparator(\"Power\");\n\t\t\n\t\tb.R.Add(out KCheckBox cDisallowStartIfOnBatteries, \"Start only if the computer is on AC power\").Checked(sett.DisallowStartIfOnBatteries);\n\t\tb.R.Add(out KCheckBox cStopIfGoingOnBatteries, \"Stop if the computer switches to battery power\").Margin(20).Checked(sett.StopIfGoingOnBatteries).xBindCheckedEnabled(cDisallowStartIfOnBatteries);\n\t\tb.R.Add(out KCheckBox cWakeToRun, \"Wake the computer to run this task\").Checked(sett.WakeToRun);\n\t\t\n\t\tb.R.xAddGroupSeparator(\"Network\");\n\t\t\n\t\tb.R.Add(out KCheckBox cNetwork, \"Required network connection:\").Checked(sett.RunOnlyIfNetworkAvailable);\n\t\tb.Add(out TextBox tNetwork).xBindCheckedEnabled(cNetwork, true);\n\t\tif (sett.RunOnlyIfNetworkAvailable && sett.NetworkSettings is { } ns) tNetwork.Text = ns.Name;\n\t\t\n\t\tb.End();\n\t\t\n\t\t_apply += o => {\n\t\t\ttry {\n\t\t\t\tif (sett.RunOnlyIfIdle = kIdle.c.IsChecked) {\n\t\t\t\t\tvar idle = sett.IdleSettings;\n\t\t\t\t\tidle.IdleDuration = kIdle.Value;\n\t\t\t\t\tidle.WaitTimeout = kIdleWait.Value;\n\t\t\t\t\tidle.StopOnIdleEnd = cIdleStop.IsChecked;\n\t\t\t\t\tidle.RestartOnIdle = cIdleRestart.IsChecked;\n\t\t\t\t}\n\t\t\t\tsett.DisallowStartIfOnBatteries = cDisallowStartIfOnBatteries.IsChecked;\n\t\t\t\tsett.StopIfGoingOnBatteries = cStopIfGoingOnBatteries.IsChecked;\n\t\t\t\tsett.WakeToRun = cWakeToRun.IsChecked;\n\t\t\t\tif (sett.RunOnlyIfNetworkAvailable = cNetwork.IsChecked) sett.NetworkSettings.Name = tNetwork.TextOrNull();\n\t\t\t}\n\t\t\tcatch (Exception ex) { dialog.showError(\"Failed to apply Conditions\", ex.ToString()); } //unlikely (all validated)\n\t\t};\n\t\t\n\t\treturn uc;\n\t}\n\t\n\t_Page _SettingsPage(string scriptArgs) {\n\t\tvar sett = _td.Settings;\n\t\t\n\t\t_Page uc = new();\n\t\tvar b = new wpfBuilder(uc);\n\t\tb.Options(bindLabelVisibility: true);\n\t\t\n\t\tb.R.Add(out KCheckBox cRunAfterMissed, \"Run task later if a scheduled start is missed\").Checked(sett.StartWhenAvailable);\n\t\t\n\t\t_TimeIntervalControls kRetry = new(this, b, \"If can't start the task, retry every:\", \"1 m|10 m|1 h\", 0, (\"PT1M\", \"P31D\"));\n\t\tkRetry.c.ToolTip = \"When Task Scheduler can't start the task when it should. This does not include failures in LibreAutomate or script, such as \\\"LibreAutomate can't find or start the script\\\" or \\\"the script process crashed or returned not 0\\\".\";\n\t\tvar v1 = (n: sett.RestartCount, t: sett.RestartInterval);\n\t\tbool restart = v1.n > 0 && v1.t != null;\n\t\tif (restart) kRetry.Value = v1.t;\n\t\t\n\t\tb.R.Add(\"Max times\", out TextBox tFailedRetryTimes, restart ? v1.n : 3).xBindCheckedEnabled(kRetry.c)\n\t\t\t.Validation(o => _ValidateNumber(o, 1, 999), _ValidationLinkClick);\n\t\tb.Last2.HorizontalAlignment = HorizontalAlignment.Right;\n\t\t\n\t\t_TimeIntervalControls kStop = new(this, b, \"Stop the task if it runs longer than:\", \"30 s|5 m|1 h|3 d\", 3) { Value = sett.ExecutionTimeLimit };\n\t\t_TimeIntervalControls kDelete = new(this, b, \"Delete expired task after:\", \"0 s|30 d|365 d\", 1, checkboxValidation: _ValidationForDeleteExpired) { Value0 = sett.DeleteExpiredTaskAfter };\n\t\t\n\t\tb.StartDock()\n\t\t\t.Add(\"If the task is already running\", out ComboBox cbIfRunning)\n\t\t\t.Items(\"Start a new instance|Queue a new instance|Don't start a new instance|Stop the existing instance\")\n\t\t\t.Select(Math.Clamp((int)sett.MultipleInstances, 0, 3))\n\t\t\t.End();\n\t\t\n\t\tb.R.AddSeparator().Margin(\"T8B8\");\n\t\tb.R.StartDock().Add(\"Script arguments\", out TextBox tArgs, scriptArgs)\n\t\t\t.Tooltip(\"Optional string to pass to the script process in command line. The script receives parsed arguments in the args variable.\")\n\t\t\t.End();\n\t\t\n\t\tb.R.AddSeparator().Margin(\"T8\");\n\t\tb.Row(-1).xAddInfoBlockT(\"\"\"\nThis tool creates or updates a Windows Task Scheduler task that starts LibreAutomate with a command to run this script. You can edit the task here or in Task Scheduler. All info about Task Scheduler is on the internet.\n\nTo run a script without LibreAutomate, instead create .exe program from the script, and in Task Scheduler create a task to run it.\n\"\"\", scrollViewer: true);\n\t\t\n\t\tb.End();\n\t\t\n\t\t_apply += o => {\n\t\t\ttry {\n\t\t\t\tsett.StartWhenAvailable = cRunAfterMissed.IsChecked;\n\t\t\t\tsett.RestartInterval = kRetry.Value;\n\t\t\t\tsett.RestartCount = kRetry.c.IsChecked ? tFailedRetryTimes.Text.ToInt() : 0;\n\t\t\t\tsett.ExecutionTimeLimit = kStop.Value ?? \"PT0S\"; //if null, will be the default 3 days\n\t\t\t\tsett.DeleteExpiredTaskAfter = kDelete.Value;\n\t\t\t\tsett.MultipleInstances = cbIfRunning.SelectedIndex;\n\t\t\t\t\n\t\t\t\tvar x = _td.Actions.GetExecAction(1);\n\t\t\t\tvar s = $\"\\\">{_f.ItemPathOrName()}\\\"\";\n\t\t\t\tif (tArgs.TextOrNull() is { } sa) s = s + \" \" + sa;\n\t\t\t\tx.Arguments = s;\n\t\t\t}\n\t\t\tcatch (Exception ex) { dialog.showError(\"Failed to apply Settings\", ex.ToString()); } //unlikely (all validated)\n\t\t};\n\t\t\n\t\treturn uc;\n\t\t\n\t\tstring _ValidationForDeleteExpired(FrameworkElement e) {\n\t\t\tif (!((KCheckBox)e).IsChecked) return null;\n\t\t\tif (_lbPages.Items.OfType<ListBoxItem>().Any(o => o.Tag is _TriggerPage { cExpire: { IsChecked: true } })) return null;\n\t\t\treturn \"Please also check Expire for a trigger\";\n\t\t}\n\t}\n\t\n\tconst TT c_unsupportedTT = (TT)(-1);\n\treadonly TT[] _ttMap = [TT.TIME, TT.DAILY, TT.WEEKLY, TT.MONTHLY, TT.MONTHLYDOW, TT.LOGON, TT.SESSION, TT.EVENT, TT.IDLE, TT.REGISTRATION, c_unsupportedTT];\n\t\n\tvoid _CreateTriggerPage(api.ITrigger t, bool select) {\n\t\t_TriggerPage page = new() { trigger = t };\n\t\tvar b = new wpfBuilder(page).Columns(60, -1);\n\t\tb.Options(bindLabelVisibility: true);\n\t\t\n\t\tb.R.Add(\"Schedule\", out ComboBox cbTriggerType).Span(1)\n\t\t\t.Items(\"Once|Daily|Weekly|Month days|Month week days|Log on|User session event|Event log|Computer idle|This task created or modified\").Select(-1);\n\t\t\n\t\tvar tt = t.Type;\n\t\tif (!_ttMap.Contains(tt) || tt == c_unsupportedTT) {\n\t\t\ttt = c_unsupportedTT;\n\t\t\tcbTriggerType.Items.Add(\"<unsupported trigger type>\");\n\t\t}\n\t\t\n\t\tvar dtf = CultureInfo.InvariantCulture.DateTimeFormat;\n\t\tvar aWeekDayNames = dtf.DayNames;\n\t\tvar aMonthNames = dtf.MonthNames.Take(12).ToArray();\n\t\tstring defaultUserId = $@\"{Environment.MachineName}\\{Environment.UserName}\";\n\t\t_TimeIntervalControls kDelay = null;\n\t\t\n\t\tvar ap = new (Panel panel, Action<WBButtonClickArgs> apply)[11];\n\t\tvar ab = new Action<int>[11] { _Once, _Daily, _Weekly, _Month, _MonthDOW, _Logon, _Session, _Eventlog, _Idle, _Registration, _Unsupported };\n\t\t\n\t\twpfBuilder _BuilderProlog(int iType, params WBGridLength[] widths) {\n\t\t\tvar b = new wpfBuilder();\n\t\t\tb.Options(bindLabelVisibility: true);\n\t\t\tif (widths.Length > 0) b.Columns(widths); else b.Columns(60, -1);\n\t\t\tap[iType].panel = b.Panel;\n\t\t\treturn b;\n\t\t}\n\t\t\n\t\tT _ApplyProlog<T>(TT type) where T : class {\n\t\t\tif (tt != type) {\n\t\t\t\t//There is no API \"replace trigger type\" or \"replace trigger at\" or \"insert trigger at\".\n\t\t\t\t//\tTo change trigger type, need to remove the trigger and add new trigger.\n\t\t\t\t//\tBut it adds to the end. Workaround: remove/add the listbox item too. Nevermind, it's rare.\n\t\t\t\t\n\t\t\t\tvar old = t;\n\t\t\t\tpage.trigger = t = _td.Triggers.Create(tt = type);\n\t\t\t\tt.Id = old.Id;\n\t\t\t\t\n\t\t\t\tint i = page.Index;\n\t\t\t\t_td.Triggers.Remove(i);\n\t\t\t\t\n\t\t\t\tif (i < _td.Triggers.Count) {\n\t\t\t\t\tbool select = page.li == _lbPages.SelectedItem;\n\t\t\t\t\t_lbPages.Items.Remove(page.li);\n\t\t\t\t\t_lbPages.Items.Insert(_lbPages.Items.Count - 2, page.li);\n\t\t\t\t\tif (select) {\n\t\t\t\t\t\t_lbPages.SelectedItem = page.li;\n\t\t\t\t\t\t_lbPages.ScrollIntoView(page.li);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn t as T;\n\t\t}\n\t\t\n\t\t//- trigger types\n\t\tb.Row((0, 100..)).StartGrid().Columns(60, -1, 0);\n\t\t\n\t\t_DateTimeControls dtStart = new(this, b, 1, \"Start\") { Value = _IsDateTrigger() ? t.StartBoundary : null };\n\t\t\n\t\tb.R.Add(out ContentControl cc).Margin(\"0\");\n\t\t\n\t\tvoid _Once(int iType) {\n\t\t\tif (tt == TT.TIME && t is api.ITimeTrigger m) {\n\t\t\t\tkDelay.Value = m.RandomDelay;\n\t\t\t}\n\t\t\t\n\t\t\tap[iType].apply = o => {\n\t\t\t\tvar m = _ApplyProlog<api.ITimeTrigger>(TT.TIME);\n\t\t\t\tm.RandomDelay = kDelay.Value;\n\t\t\t};\n\t\t}\n\t\t\n\t\tvoid _Daily(int iType) {\n\t\t\tvar b = _BuilderProlog(iType, 60, 50, -1);\n\t\t\tb.R.Add(\"Every\", out TextBox tEvery, \"1\")\n\t\t\t\t.Validation(o => _ValidateNumber(tEvery, 1, 365), _ValidationLinkClick)\n\t\t\t\t.Add<Label>(\"days\");\n\t\t\tb.End();\n\t\t\tif (tt == TT.DAILY && t is api.IDailyTrigger m) {\n\t\t\t\ttEvery.Text = m.DaysInterval.ToString();\n\t\t\t\tkDelay.Value = m.RandomDelay;\n\t\t\t}\n\t\t\t\n\t\t\tap[iType].apply = o => {\n\t\t\t\tvar m = _ApplyProlog<api.IDailyTrigger>(TT.DAILY);\n\t\t\t\tm.DaysInterval = (short)tEvery.Text.ToInt();\n\t\t\t\tm.RandomDelay = kDelay.Value;\n\t\t\t};\n\t\t}\n\t\t\n\t\tvoid _Weekly(int iType) {\n\t\t\tvar b = _BuilderProlog(iType, 60, 50, -1);\n\t\t\tb.R.Add(\"Every\", out TextBox tEvery, \"1\")\n\t\t\t\t.Validation(o => _ValidateNumber(tEvery, 1, 99), _ValidationLinkClick)\n\t\t\t\t.Add<Label>(\"weeks\");\n\t\t\tvar cdWeeks = new KCheckDropdownBox { Checkboxes = aWeekDayNames, TextWrapping = TextWrapping.Wrap };\n\t\t\tb.R.Add(\"On\", cdWeeks)\n\t\t\t\t.Validation(_ValidateBits, _ValidationLinkClick);\n\t\t\tif (tt == TT.WEEKLY) {\n\t\t\t\tvar m = (api.IWeeklyTrigger)t;\n\t\t\t\ttEvery.Text = m.WeeksInterval.ToString();\n\t\t\t\tcdWeeks.Checked = m.DaysOfWeek;\n\t\t\t\tkDelay.Value = m.RandomDelay;\n\t\t\t}\n\t\t\tb.End();\n\t\t\t\n\t\t\tap[iType].apply = o => {\n\t\t\t\tvar m = _ApplyProlog<api.IWeeklyTrigger>(TT.WEEKLY);\n\t\t\t\tm.WeeksInterval = (short)tEvery.Text.ToInt();\n\t\t\t\tm.DaysOfWeek = (ushort)cdWeeks.Checked;\n\t\t\t\tm.RandomDelay = kDelay.Value;\n\t\t\t};\n\t\t}\n\t\t\n\t\tvoid _Month(int iType) {\n\t\t\tvar b = _BuilderProlog(iType);\n\t\t\tvar cdMonths = new KCheckDropdownBox { Checkboxes = aMonthNames, AllItem = \"All months\", AllText = \"All months\", TextWrapping = TextWrapping.Wrap };\n\t\t\tb.R.Add(\"Months\", cdMonths)\n\t\t\t\t.Validation(_ValidateBits, _ValidationLinkClick);\n\t\t\tvar cdDays = new KCheckDropdownBox { DropdownSettings = () => new(new UniformGrid()), Checkboxes = Enumerable.Range(1, 31).Cast<object>().Append(\"Last\").ToArray(), TextWrapping = TextWrapping.Wrap };\n\t\t\tb.R.Add(\"Days\", cdDays)\n\t\t\t\t.Validation(_ValidateBits, _ValidationLinkClick);\n\t\t\tif (tt == TT.MONTHLY) {\n\t\t\t\tvar m = (api.IMonthlyTrigger)t;\n\t\t\t\tcdMonths.Checked = m.MonthsOfYear;\n\t\t\t\tcdDays.Checked = m.DaysOfMonth | (m.RunOnLastDayOfMonth ? 1u << 31 : 0);\n\t\t\t\tkDelay.Value = m.RandomDelay;\n\t\t\t}\n\t\t\tb.End();\n\t\t\t\n\t\t\tap[iType].apply = o => {\n\t\t\t\tvar m = _ApplyProlog<api.IMonthlyTrigger>(TT.MONTHLY);\n\t\t\t\tm.MonthsOfYear = (ushort)cdMonths.Checked;\n\t\t\t\tm.DaysOfMonth = (uint)(cdDays.Checked & 0x7fff_ffff);\n\t\t\t\tm.RunOnLastDayOfMonth = 0 != (cdDays.Checked & 0x8000_0000);\n\t\t\t\tm.RandomDelay = kDelay.Value;\n\t\t\t};\n\t\t}\n\t\t\n\t\tvoid _MonthDOW(int iType) {\n\t\t\tvar b = _BuilderProlog(iType, 60, 100, -1);\n\t\t\tvar cdMonths = new KCheckDropdownBox { Checkboxes = aMonthNames, AllItem = \"All months\", AllText = \"All months\", TextWrapping = TextWrapping.Wrap };\n\t\t\tb.R.Add(\"Months\", cdMonths)\n\t\t\t\t.Validation(_ValidateBits, _ValidationLinkClick);\n\t\t\tvar cdWeeks = new KCheckDropdownBox { Checkboxes = [\"first\", \"second\", \"third\", \"fourth\", \"last\"], AllText = \"every\", TextWrapping = TextWrapping.Wrap };\n\t\t\tb.R.Add(\"On\", cdWeeks)\n\t\t\t\t.Validation(_ValidateBits, _ValidationLinkClick);\n\t\t\tvar dDays = new KCheckDropdownBox { Checkboxes = aWeekDayNames, AllItem = \"All week days\", AllText = \"{0} (all days)\", TextWrapping = TextWrapping.Wrap };\n\t\t\tb.Add(dDays)\n\t\t\t\t.Validation(_ValidateBits, _ValidationLinkClick);\n\t\t\tif (tt == TT.MONTHLYDOW) {\n\t\t\t\tvar m = (api.IMonthlyDOWTrigger)t;\n\t\t\t\tcdMonths.Checked = m.MonthsOfYear;\n\t\t\t\tcdWeeks.Checked = m.WeeksOfMonth | (m.RunOnLastWeekOfMonth ? 1u << 4 : 0);\n\t\t\t\tdDays.Checked = m.DaysOfWeek;\n\t\t\t\tkDelay.Value = m.RandomDelay;\n\t\t\t}\n\t\t\tb.End();\n\t\t\t\n\t\t\tap[iType].apply = o => {\n\t\t\t\tvar m = _ApplyProlog<api.IMonthlyDOWTrigger>(TT.MONTHLYDOW);\n\t\t\t\tm.MonthsOfYear = (ushort)cdMonths.Checked;\n\t\t\t\tm.WeeksOfMonth = (ushort)(cdWeeks.Checked & 0xf);\n\t\t\t\tm.RunOnLastWeekOfMonth = 0 != (cdWeeks.Checked & 0x10);\n\t\t\t\tm.DaysOfWeek = (ushort)dDays.Checked;\n\t\t\t\tm.RandomDelay = kDelay.Value;\n\t\t\t};\n\t\t}\n\t\t\n\t\tvoid _Logon(int iType) {\n\t\t\tvar b = _BuilderProlog(iType);\n\t\t\tb.R.Add(\"User\", out ComboBox cbUser).Editable();\n\t\t\tstring user = null;\n\t\t\tif (tt == TT.LOGON) {\n\t\t\t\tvar m = (api.ILogonTrigger)t;\n\t\t\t\tuser = m.UserId;\n\t\t\t\tkDelay.Value = m.Delay;\n\t\t\t}\n\t\t\t_InitUserCombo(cbUser, user);\n\t\t\tb.End();\n\t\t\t\n\t\t\tap[iType].apply = o => {\n\t\t\t\tvar m = _ApplyProlog<api.ILogonTrigger>(TT.LOGON);\n\t\t\t\tvar s = cbUser.Text; if (s is \"\" or \"<any>\") s = null;\n\t\t\t\tm.UserId = s;\n\t\t\t\tm.Delay = kDelay.Value;\n\t\t\t};\n\t\t}\n\t\t\n\t\tvoid _Session(int iType) {\n\t\t\tTTS[] ttsMap = [TTS.TASK_CONSOLE_CONNECT, TTS.TASK_CONSOLE_DISCONNECT, TTS.TASK_REMOTE_CONNECT, TTS.TASK_REMOTE_DISCONNECT, TTS.TASK_SESSION_LOCK, TTS.TASK_SESSION_UNLOCK];\n\t\t\t\n\t\t\tvar b = _BuilderProlog(iType);\n\t\t\tb.R.Add(\"Event\", out ComboBox cbEvent).Items(\"Local connection to user session|Local disconnect from user session|Remote connection to user session|Remote disconnect from user session|Workstation lock|Workstation unlock\").Select(-1);\n\t\t\tb.R.Add(\"User\", out ComboBox cbUser).Editable();\n\t\t\tstring user = null;\n\t\t\tif (tt == TT.SESSION) {\n\t\t\t\tvar m = (api.ISessionStateChangeTrigger)t;\n\t\t\t\tcbEvent.SelectedIndex = Array.IndexOf(ttsMap, m.StateChange);\n\t\t\t\tuser = m.UserId;\n\t\t\t\tkDelay.Value = m.Delay;\n\t\t\t}\n\t\t\t_InitUserCombo(cbUser, user);\n\t\t\tb.End();\n\t\t\t\n\t\t\tap[iType].apply = o => {\n\t\t\t\tvar m = _ApplyProlog<api.ISessionStateChangeTrigger>(TT.SESSION);\n\t\t\t\tvar s = cbUser.Text; if (s is \"\" or \"<any>\") s = null;\n\t\t\t\tm.UserId = s;\n\t\t\t\tm.StateChange = ttsMap[cbEvent.SelectedIndex];\n\t\t\t\tm.Delay = kDelay.Value;\n\t\t\t};\n\t\t}\n\t\t\n\t\tvoid _Eventlog(int iType) {\n\t\t\tbool custom = false;\n\t\t\tvar b = _BuilderProlog(iType, 60, 80, 10, 0, 100, -1, 0);\n\t\t\tb.R.Add(\"Log\", out TextBox tLog)\n\t\t\t\t.Validation(o => tLog.Visibility == Visibility.Visible && tLog.Text.NE() ? \"Log not specified\" : null, _ValidationLinkClick);\n\t\t\tb.R.Add(\"Source\", out TextBox tSource);\n\t\t\tb.R.Add(\"Event ID\", out TextBox tID)\n\t\t\t\t.Validation(o => _ValidateNumber(o, 0, ushort.MaxValue, allowEmpty: true), _ValidationLinkClick);\n\t\t\tb.Skip().Add(\"Level\", out ComboBox cbLevel).Items(\"<any>|Critical|Error|Warning|Information|Verbose\");\n\t\t\tb.Skip().AddButton(out var bMore, \"More ▾\", null);\n\t\t\tb.R.Add(\"Query\", out TextBox tQuery).Multiline(60..75).Hidden(null)\n\t\t\t\t.Validation(o => { if (custom) try { XElement.Parse(tQuery.Text); } catch { return \"Invalid XML\"; } return null; }, _ValidationLinkClick);\n\t\t\tif (tt == TT.EVENT) {\n\t\t\t\tvar m = (api.IEventTrigger)t;\n\t\t\t\tvar (q, log, source, id, level) = m.GetQuery();\n\t\t\t\tif (q != null) {\n\t\t\t\t\tif (log != null) {\n\t\t\t\t\t\ttLog.Text = log;\n\t\t\t\t\t\ttSource.Text = source;\n\t\t\t\t\t\ttID.Text = id;\n\t\t\t\t\t\tif (level is >= 1 and <= 5) cbLevel.SelectedIndex = level;\n\t\t\t\t\t} else {\n\t\t\t\t\t\t_ToggleCustom();\n\t\t\t\t\t\ttQuery.Text = q;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tkDelay.Value = m.Delay;\n\t\t\t}\n\t\t\tb.End();\n\t\t\t\n\t\t\tap[iType].apply = o => {\n\t\t\t\tvar m = _ApplyProlog<api.IEventTrigger>(TT.EVENT);\n\t\t\t\tstring q;\n\t\t\t\tif (custom) {\n\t\t\t\t\tq = tQuery.Text;\n\t\t\t\t} else {\n\t\t\t\t\tstring s = \"*\", log = tLog.Text, source = tSource.TextOrNull(), id = tID.TextOrNull(), level = cbLevel.SelectedIndex is var si && si > 0 ? si.ToS() : null;\n\t\t\t\t\tif ((source ?? id ?? level) != null) {\n\t\t\t\t\t\ts = \"*[System[\";\n\t\t\t\t\t\tstring sep = null;\n\t\t\t\t\t\t_Append(source, \"Provider[@Name='\", \"']\");\n\t\t\t\t\t\t_Append(id, \"EventID=\");\n\t\t\t\t\t\t_Append(level, \"Level=\");\n\t\t\t\t\t\ts += \"]]\";\n\t\t\t\t\t\t\n\t\t\t\t\t\tvoid _Append(string v, string pref, string suf = null) {\n\t\t\t\t\t\t\tif (v != null) {\n\t\t\t\t\t\t\t\ts = s + sep + pref + v + suf;\n\t\t\t\t\t\t\t\tsep = \" and \";\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tvar x = new XElement(\"QueryList\",\n\t\t\t\t\t\tnew XElement(\"Query\",\n\t\t\t\t\t\t\tnew XAttribute(\"Id\", \"0\"),\n\t\t\t\t\t\t\tnew XAttribute(\"Path\", log),\n\t\t\t\t\t\t\tnew XElement(\"Select\",\n\t\t\t\t\t\t\t\tnew XAttribute(\"Path\", log),\n\t\t\t\t\t\t\t\ts\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t)\n\t\t\t\t\t);\n\t\t\t\t\tq = x.ToString();\n\t\t\t\t}\n\t\t\t\tm.Subscription = q;\n\t\t\t\tm.Delay = kDelay.Value;\n\t\t\t};\n\t\t\t\n\t\t\tbMore.Click += (_, _) => {\n\t\t\t\tvar m = new popupMenu();\n\t\t\t\tm[\"Open Event Viewer\"] = o => { run.it(@\"eventvwr.msc\"); };\n\t\t\t\tm[\"Paste event details\", disable: !tLog.IsVisible] = o => {\n\t\t\t\t\tif (clipboard.text is { } s && s.Starts(\"Log Name:\")) {\n\t\t\t\t\t\tforeach (var line in s.Lines()) {\n\t\t\t\t\t\t\tif (line.RxMatch(@\"^(Log Name|Source|Event ID|Level):\\h*(.+)\", out var m)) {\n\t\t\t\t\t\t\t\tvar v = m[2].Value;\n\t\t\t\t\t\t\t\tvar c = line[m[1].Start + 2] switch { 'g' => tLog, 'u' => tSource, 'e' => tID, _ => null };\n\t\t\t\t\t\t\t\tif (c != null) c.Text = v;\n\t\t\t\t\t\t\t\telse if (cbLevel.SelectedIndex > 0) cbLevel.Text = v; //rejected: paste Level. But need to change if incorrect selected.\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tdialog.showInfo(null, \"In Event Viewer select an event and copy its details as text.\", owner: this);\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t\tm.Separator();\n\t\t\t\tm.AddCheck(\"Custom\", custom, _ => _ToggleCustom());\n\t\t\t\tm.Show(owner: this);\n\t\t\t};\n\t\t\t\n\t\t\tvoid _ToggleCustom() {\n\t\t\t\tcustom ^= true;\n\t\t\t\ttLog.Collapse_(custom);\n\t\t\t\ttSource.Collapse_(custom);\n\t\t\t\ttID.Collapse_(custom);\n\t\t\t\tcbLevel.Collapse_(custom);\n\t\t\t\ttQuery.Collapse_(!custom);\n\t\t\t}\n\t\t}\n\t\t\n\t\tvoid _Idle(int iType) {\n\t\t\tvar b = _BuilderProlog(iType);\n\t\t\tb.R.xAddInfoBlockT(\"To change idle conditions, use the Conditions page.\");\n\t\t\tb.End();\n\t\t\t\n\t\t\tap[iType].apply = o => {\n\t\t\t\t_ApplyProlog<api.ITrigger>(TT.IDLE);\n\t\t\t};\n\t\t}\n\t\t\n\t\tvoid _Registration(int iType) {\n\t\t\tif (tt == TT.REGISTRATION && t is api.IRegistrationTrigger m) {\n\t\t\t\tkDelay.Value = m.Delay;\n\t\t\t}\n\t\t\t\n\t\t\tap[iType].apply = o => {\n\t\t\t\tvar m = _ApplyProlog<api.IRegistrationTrigger>(TT.REGISTRATION);\n\t\t\t\tm.Delay = kDelay.Value;\n\t\t\t};\n\t\t}\n\t\t\n\t\tvoid _Unsupported(int iType) {\n\t\t\tvar b = _BuilderProlog(iType);\n\t\t\tb.R.xAddInfoBlockT(t.Type == TT.BOOT ? \"LibreAutomate cannot run at system startup. Delete this trigger.\\nInstead create .exe program from this script, and in Task Scheduler create a task to run it.\" : \"Cannot edit this trigger.\");\n\t\t\tb.End();\n\t\t}\n\t\t\n\t\tvoid _InitUserCombo(ComboBox cb, string user) {\n\t\t\tif (!user.NE()) cb.Items.Add(user);\n\t\t\tcb.Items.Add(\"<any>\");\n\t\t\tif (user != defaultUserId) cb.Items.Add(defaultUserId);\n\t\t\tcb.SelectedIndex = 0;\n\t\t}\n\t\t\n\t\tb.End();\n\t\t//-\n\t\t\n\t\tb.R.xAddGroupSeparator(\"Trigger settings\");\n\t\t\n\t\t//- Trigger settings\n\t\tb.Row(-1).Add(new ScrollViewer { VerticalScrollBarVisibility = ScrollBarVisibility.Auto }).Margin(\"0\")\n\t\t\t.StartGrid(childOfLast: true);\n\t\t\n\t\tkDelay = new _TimeIntervalControls(this, b, \"\", \"30 s|1 m|1 h|1 d\", 2, (\"PT1S\", \"P31D\"));\n\t\t\n\t\tb.R.StartGrid().Columns(0, -1, 0, -1);\n\t\t\n\t\t_TimeIntervalControls kRepeat = new(this, b, \"Repeat every:\", \"5 m|1 h\", 1, (\"PT1M\", \"P31D\"));\n\t\t_TimeIntervalControls kRepeatDuration = new(this, b, \"for a duration of:\", \"|15 m|1 h|1 d\", 3,\n\t\t\tvalidation: o => ((ComboBox)o).Text is \"\" ? null : _ValidateTimeInterval(o, /*\"PT1M\"*/kRepeat.Value, \"P31D\"),\n\t\t\tcOther: kRepeat.c, noRow: true);\n\t\tb.R.Add(out KCheckBox cRepeatStop, \"Stop all running tasks at end of repetition duration\").Margin(22).xBindCheckedEnabled(kRepeat.c);\n\t\tif (t.Repetition is { } rep && rep.Interval is string s1) {\n\t\t\tkRepeat.Value = s1;\n\t\t\tif (rep.Duration is string s2) kRepeatDuration.Value = rep.Duration; else kRepeatDuration.cb.SelectedIndex = 0;\n\t\t\tcRepeatStop.IsChecked = rep.StopAtDurationEnd;\n\t\t}\n\t\tb.End();\n\t\t\n\t\t_TimeIntervalControls kStop = new(this, b, \"Stop task if it runs longer than:\", \"30 s|5 m|1 h|3 d\", 2) { Value = t.ExecutionTimeLimit };\n\t\t\n\t\tb.R.StartGrid().Columns(0, -1, 0);\n\t\t_DateTimeControls dtActivate = new(this, b, 2, \"Activate\") { Value = _IsDateTrigger() ? null : t.StartBoundary };\n\t\t_DateTimeControls dtExpire = new(this, b, 3, \"Expire\") { Value = t.EndBoundary };\n\t\tdtExpire.dt.ValidationDateMustBeAfter = () => (_IsDateTrigger() ? dtStart : dtActivate.c.IsChecked ? dtActivate : null)?.dt.Value;\n\t\tpage.cExpire = dtExpire.c;\n\t\tb.End();\n\t\t\n\t\tb.R.Add(out KCheckBox cEnabled, \"Trigger enabled\").Checked(t.Enabled);\n\t\t\n\t\tb.End();\n\t\t//-\n\t\t\n\t\tb.End();\n\t\t\n\t\t_apply += page.apply = o => {\n\t\t\ttry {\n\t\t\t\tint i = cbTriggerType.SelectedIndex;\n\t\t\t\tap[i].apply?.Invoke(o);\n\t\t\t\t\n\t\t\t\tif (kRepeat.c.IsChecked) {\n\t\t\t\t\tvar rep = t.Repetition;\n\t\t\t\t\trep.Interval = kRepeat.Value;\n\t\t\t\t\trep.Duration = kRepeatDuration.Value;\n\t\t\t\t\trep.StopAtDurationEnd = cRepeatStop.IsChecked;\n\t\t\t\t} else {\n\t\t\t\t\tt.Repetition = null;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tt.ExecutionTimeLimit = kStop.Value;\n\t\t\t\tt.StartBoundary = _IsDateTrigger() ? dtStart.Value : dtActivate.Value;\n\t\t\t\tt.EndBoundary = dtExpire.Value;\n\t\t\t\tt.Enabled = cEnabled.IsChecked;\n\t\t\t}\n\t\t\tcatch (Exception ex) { dialog.showError(\"Failed to apply trigger #\" + page.Index, ex.ToString()); } //unlikely (all validated)\n\t\t};\n\t\t\n\t\tcbTriggerType.SelectionChanged += (_, e) => {\n\t\t\tint i = cbTriggerType.SelectedIndex;\n\t\t\t\n\t\t\tif (ap[i].panel == null) ab[i](i);\n\t\t\tcc.Content = ap[i].panel;\n\t\t\t\n\t\t\tbool isDateTrigger = i <= 4;\n\t\t\tdtStart.Collapse(!isDateTrigger);\n\t\t\tdtActivate.Collapse(isDateTrigger);\n\t\t\tkDelay.c.Content = isDateTrigger ? \"Delay task for up to (random delay):\" : \"Delay task for:\";\n\t\t\tkDelay.Collapse(_ttMap[i] == TT.IDLE);\n\t\t};\n\t\t\n\t\tcbTriggerType.SelectedIndex = Array.IndexOf(_ttMap, tt);\n\t\t\n\t\t_AddPage(page.ToString(), page);\n\t\t\n\t\tif (select) {\n\t\t\t_lbPages.SelectedItem = page.li;\n\t\t\t_lbPages.ScrollIntoView(page.li);\n\t\t}\n\t\t\n\t\t//note: gets the saved trigger type. It is different if changed and still not applied.\n\t\tbool _IsDateTrigger() => tt is >= TT.TIME and <= TT.MONTHLYDOW;\n\t}\n\t\n\tvoid _NewTrigger(bool createPage) {\n\t\tvar trigger = _td.Triggers.Create(TT.TIME);\n\t\tvar dt = DateTime.Now;\n\t\ttrigger.StartBoundary = dt.ToString(\"s\");\n\t\tif (createPage) _CreateTriggerPage(trigger, true);\n\t}\n\t\n\tvoid _DeleteTrigger() {\n\t\tif (_lbPages.SelectedItem is ListBoxItem { Tag: _TriggerPage page } li) {\n\t\t\tif (0 != _td.Triggers.Remove(_lbPages.SelectedIndex)) return;\n\t\t\t_lbPages.Items.Remove(li);\n\t\t\t_apply -= page.apply;\n\t\t}\n\t}\n\t\n\tclass _TriggerPage : _Page {\n\t\tpublic api.ITrigger trigger;\n\t\tpublic Action<WBButtonClickArgs> apply;\n\t\tpublic KCheckBox cExpire;\n\t\t\n\t\tpublic override string ToString() {\n\t\t\treturn trigger.FormatTriggerString();\n\t\t}\n\t}\n\t\n\tstatic string _DecodeTimeInterval(string s, string returnIf0 = null) {\n\t\tif (s.NE()) return null;\n\t\tif (s[0] != 'P') return s;\n\t\ttry {\n\t\t\tvar t = XmlConvert.ToTimeSpan(s);\n\t\t\tif (t.Ticks == 0) return returnIf0;\n\t\t\tStringBuilder b = new();\n\t\t\t_Append(t.Days, 'd');\n\t\t\t_Append(t.Hours, 'h');\n\t\t\t_Append(t.Minutes, 'm');\n\t\t\t_Append(t.Seconds, 's');\n\t\t\treturn b.ToString();\n\t\t\t\n\t\t\tvoid _Append(int n, char c) {\n\t\t\t\tif (n > 0) {\n\t\t\t\t\tif (b.Length > 0) b.Append(\" \");\n\t\t\t\t\tb.Append(n).Append(' ').Append(c);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcatch { return s; }\n\t}\n\t\n\tstatic string _EncodeTimeInterval(string s) {\n\t\tif (s.NE() || !s.RxMatch(@\"(?i)^ *P?(?:(\\d+) *d(?:ays?)?,?)? *T?(?:(\\d+) *h(?:ours?)?,?)? *(?:(\\d+) *m(?:in(?:utes?)?)?,?)? *(?:(\\d+) *s(?:ec(?:onds?)?)?)? *$\", out var m)) return null;\n\t\tbool dExists = m[1].Exists, hmsExists = m[2].Exists || m[3].Exists || m[4].Exists;\n\t\tif (!(dExists || hmsExists)) return null;\n\t\tvar b = new StringBuilder(\"P\");\n\t\tif (dExists) b.Append(m[1].Value).Append('D');\n\t\tif (hmsExists) {\n\t\t\tb.Append('T');\n\t\t\tif (m[2].Exists) b.Append(m[2].Value).Append('H');\n\t\t\tif (m[3].Exists) b.Append(m[3].Value).Append('M');\n\t\t\tif (m[4].Exists) b.Append(m[4].Value).Append('S');\n\t\t}\n\t\treturn b.ToString();\n\t}\n\t\n\tstatic string _ValidateTimeInterval(FrameworkElement e, string min = null, string max = null, Func<string> also = null) {\n\t\tvar c = (ComboBox)e;\n\t\tif (c.IsEnabled && c.Visibility == Visibility.Visible) {\n\t\t\tvar s = c.Text.Trim();\n\t\t\tif (s.NE()) return \"Empty\";\n\t\t\tif (_EncodeTimeInterval(s) is not string k) return \"Invalid duration format\";\n\t\t\t\n\t\t\ttry {\n\t\t\t\tvar t = XmlConvert.ToTimeSpan(k);\n\t\t\t\t//print.it(k, t);\n\t\t\t\tif (min != null && t < XmlConvert.ToTimeSpan(min)) return \"Duration min \" + _DecodeTimeInterval(min);\n\t\t\t\tmax ??= \"P24855D\"; //int.MaxValue seconds\n\t\t\t\tif (t > XmlConvert.ToTimeSpan(max)) return \"Duration max \" + _DecodeTimeInterval(max);\n\t\t\t\t\n\t\t\t\tif (also is { } vp) return vp();\n\t\t\t}\n\t\t\tcatch { return \"Invalid duration format\"; }\n\t\t}\n\t\treturn null;\n\t}\n\t\n\tstatic string _ValidateNumber(FrameworkElement e, int min, int max, bool allowEmpty = false) {\n\t\tvar c = (TextBox)e;\n\t\tif (c.IsEnabled && c.Visibility == Visibility.Visible) {\n\t\t\tvar s = c.Text.Trim();\n\t\t\tif (s.NE()) return allowEmpty ? null : \"Empty\";\n\t\t\tif (!s.ToInt(out long r, 0, out int end, STIFlags.NoHex) || end < s.Length) return \"Invalid number format\";\n\t\t\tif (r < min) return \"Min \" + min;\n\t\t\tif (r > max) return \"Max \" + max;\n\t\t}\n\t\treturn null;\n\t}\n\t\n\tstatic string _ValidateBits(FrameworkElement e) {\n\t\tvar c = (KCheckDropdownBox)e;\n\t\tif (c.IsEnabled && c.Visibility == Visibility.Visible) {\n\t\t\tif (c.Checked == 0) return \"Empty\";\n\t\t}\n\t\treturn null;\n\t}\n\t\n\tvoid _ValidationLinkClick(FrameworkElement e) {\n\t\tif (!e.IsVisible) {\n\t\t\tvar page = e.FindVisualAncestor<_Page>(false, null, false) as _Page;\n\t\t\t_lbPages.SelectedIndex = page.Index;\n\t\t}\n\t}\n\t\n\tclass _TimeIntervalControls {\n\t\tpublic ComboBox cb;\n\t\tpublic KCheckBox c;\n\t\tLabel _label;\n\t\tKCheckBox _cOther;\n\t\t\n\t\tpublic _TimeIntervalControls(DSchedule dialog, wpfBuilder b, string label, string cbItems, int cbIndex,\n\t\t\t(string min, string max) valid = default, Func<FrameworkElement, string> validation = null, Func<FrameworkElement, string> checkboxValidation = null,\n\t\t\tKCheckBox cOther = null, bool noRow = false) {\n\t\t\t\n\t\t\tvalidation ??= o => _ValidateTimeInterval(o, valid.min, valid.max);\n\t\t\t\n\t\t\tif (!noRow) b.Row(0);\n\t\t\tif ((_cOther = cOther) == null) {\n\t\t\t\tb.Add(out c, label);\n\t\t\t\tif (checkboxValidation != null) b.Validation(checkboxValidation, dialog._ValidationLinkClick);\n\t\t\t} else b.Add(out _label, label);\n\t\t\t\n\t\t\tb.Add(out cb).Editable().LabeledBy()\n\t\t\t\t.Items(cbItems).Select(cbIndex)\n\t\t\t\t.Validation(validation, dialog._ValidationLinkClick);\n\t\t\tb.xBindCheckedEnabled(c ?? _cOther, true);\n\t\t}\n\t\t\n\t\tpublic string Value {\n\t\t\tget => (c ?? _cOther).IsChecked ? _EncodeTimeInterval(cb.Text) : null;\n\t\t\tset {\n\t\t\t\tif (_DecodeTimeInterval(value) is string s) {\n\t\t\t\t\tcb.Text = s;\n\t\t\t\t\tif (c != null) c.IsChecked = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic string Value0 {\n\t\t\tset {\n\t\t\t\tif (_DecodeTimeInterval(value, \"0 s\") is string s) {\n\t\t\t\t\tcb.Text = s;\n\t\t\t\t\tif (c != null) c.IsChecked = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic void Collapse(bool collapse) {\n\t\t\t_label?.Collapse_(collapse);\n\t\t\tc?.Collapse_(collapse);\n\t\t\tcb.Collapse_(collapse);\n\t\t}\n\t}\n\t\n\tclass _DateTimeControls {\n\t\tpublic KDateTime dt;\n\t\tpublic KCheckBox c;\n\t\tLabel _label;\n\t\tKCheckBox _cSync;\n\t\tbool _expire;\n\t\t\n\t\t/// <param name=\"id\">1 Start, 2 Activate, 3 Expire.</param>\n\t\tpublic _DateTimeControls(DSchedule dialog, wpfBuilder b, int id, string label, Func<FrameworkElement, string> validation = null) {\n\t\t\t_expire = id == 3;\n\t\t\tbool checkbox = id is 2 or 3;\n\t\t\t\n\t\t\tb.Row(0);\n\t\t\tif (checkbox) b.Add(out c, label); else b.Add(out _label, label);\n\t\t\t\n\t\t\tb.Add(out dt).LabeledBy(bindVisibility: false)\n\t\t\t\t.Validation(validation ?? KDateTime.Validation, dialog._ValidationLinkClick);\n\t\t\tif (checkbox) b.xBindCheckedEnabled(c);\n\t\t\t\n\t\t\tb.Add(out _cSync, \"Sync across time zones\");\n\t\t\tif (checkbox) b.xBindCheckedEnabled(c);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Note: getter throws if the date control text is invalid. Must be validated.\n\t\t/// </summary>\n\t\tpublic string Value {\n\t\t\tget => c?.IsChecked == false ? null : XmlConvert.ToString(dt.Value.Value, _cSync.IsChecked ? XmlDateTimeSerializationMode.Local : XmlDateTimeSerializationMode.Unspecified);\n\t\t\tset {\n\t\t\t\tif (!value.NE()) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tdt.Value = XmlConvert.ToDateTime(value, XmlDateTimeSerializationMode.Local);\n\t\t\t\t\t\tif (c != null) c.IsChecked = true;\n\t\t\t\t\t\t_cSync.IsChecked = value.RxIsMatch(@\"(?:Z|[+\\-][\\d:]+)$\");\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tcatch { }\n\t\t\t\t}\n\t\t\t\tvar d = DateTime.Now;\n\t\t\t\tif (_expire) d = d.AddYears(1);\n\t\t\t\tdt.Value = d;\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic void Collapse(bool collapse) {\n\t\t\t_label?.Collapse_(collapse);\n\t\t\tc?.Collapse_(collapse);\n\t\t\tdt.Collapse_(collapse);\n\t\t\t_cSync.Collapse_(collapse);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Triggers and toolbars/TT-tb.cs",
    "content": "extern alias CAW;\n\nusing System.Windows;\nusing System.Windows.Controls;\n\nusing Microsoft.CodeAnalysis;\nusing CAW::Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp;\nusing Microsoft.CodeAnalysis.CSharp.Syntax;\nusing Microsoft.CodeAnalysis.Text;\nusing Microsoft.CodeAnalysis.Shared.Extensions;\nusing CAW::Microsoft.CodeAnalysis.Shared.Extensions;\nusing Microsoft.CodeAnalysis.CSharp.Extensions;\nusing CAW::Microsoft.CodeAnalysis.FindSymbols;\n\nusing Au.Triggers;\nusing ToolLand;\nusing Au.Controls;\n\nnamespace LA;\n\npartial class TriggersAndToolbars {\n\t_Toolbar[] _toolbars;\n\tMetaComments _meta;\n\tSolution _sln;\n\tCompilation _compilation;\n\tINamedTypeSymbol _programSym;\n\tDictionary<FileNode, SyntaxTree> _fnToSt;\n\tDictionary<SyntaxTree, FileNode> _fnFromSt;\n\t\n\tTriggersAndToolbars() {\n\t\t_Update();\n\t}\n\t\n\tvoid _NewToolbar() {\n\t\tvar w = new KDialogWindow { Title = \"New toolbar\" };\n\t\tvar b = new wpfBuilder(w).WinSize(500).Columns(50, -1);\n\t\tb.WinProperties(resizeMode: ResizeMode.NoResize, showInTaskbar: false);\n\t\t\n\t\tb.R.Add(\"Name\", out TextBox tName, \"Toolbar_\").Focus()\n\t\t\t.Validation(_ => tName.Text is \"\" or \"Toolbar_\" ? \"No name\" : !SyntaxFacts.IsValidIdentifier(tName.Text) ? \"Invalid function name\" : null);\n\t\t\n\t\tb.R.Add(\"In file\", out ComboBox cbFile);\n\t\tcbFile.ShouldPreserveUserEnteredPrefix = true;\n\t\tcbFile.Items.Add(\"<new file>\");\n\t\tforeach (var st in _EnumToolbarTriggersFunctions().Select(o => o.Locations[0].SourceTree).Distinct()) {\n\t\t\tcbFile.Items.Add(_fnFromSt[st]);\n\t\t}\n\t\tcbFile.SelectedIndex = 0;\n\t\t\n\t\tb.R.Add(\"Code\", out ComboBox cbTrigger).Items(\"Window trigger, attach to window|Window trigger, attach, auto-hide at screen edge|Show at startup, auto-hide at screen edge|Mouse trigger, auto-hide at screen edge|No trigger\");\n\t\tb.R.Skip().Add(out KCheckBox cLaterName, \"Show/hide whenever window name changes\");\n\t\tb.R.StartGrid().Columns(50, -1, 20, 0, -1).Hidden(null)\n\t\t\t.R.Add(\"Edge\", out ComboBox cbEdge).Items(typeof(TMEdge).GetEnumNames()).Select(1)\n\t\t\t.Skip();\n\t\tvar cbScreen = _ScreenComboBoxInit(b);\n\t\tb.End();\n\t\tvar pScreen = b.Last;\n\t\tvar windowTriggerPage = _AddWindowPage(b);\n\t\t\n\t\tcbTrigger.SelectionChanged += (_, _) => {\n\t\t\tint si = cbTrigger.SelectedIndex;\n\t\t\tpScreen.Visibility = si is 1 or 2 or 3 ? Visibility.Visible : Visibility.Collapsed;\n\t\t\tcLaterName.Visibility = si is 0 or 1 ? Visibility.Visible : Visibility.Collapsed;\n\t\t\twindowTriggerPage.Visibility = si is 0 or 1 ? Visibility.Visible : Visibility.Collapsed;\n\t\t};\n\t\t\n\t\tb.R.AddOkCancel();\n\t\tb.End();\n\t\t\n\t\tb.Loaded += () => { tName.CaretIndex = tName.Text.Length; };\n\t\t\n\t\tif (!w.ShowAndWait(App.Wmain)) return;\n\t\t\n\t\tstring sName = _GetUniqueNameInProgram(tName.Text);\n\t\t\n\t\tint iTrigger = cbTrigger.SelectedIndex;\n\t\tstring sAutoHide = null;\n\t\tif (iTrigger is 0 or 4) { //window, none\n\t\t\tsAutoHide = \"\"\"\n\t\t\n\t\t////auto-hide. Above is the auto-hide part. Below is the always-visible part.\n\t\t//t = t.AutoHide();\n\t\t//if (t.FirstTime) {\n\t\t\t\n\t\t//}\n\t\t\n\"\"\";\n\t\t\tif (iTrigger == 0) sAutoHide += \"\"\"\n\n\t\t//t.MaximizedWindowTopPlus = 6.7;\n\t\t\n\"\"\";\n\t\t} else if (iTrigger is 1 or 2) { //window+screen or screen\n\t\t\tstring sScreen = _ScreenComboBoxResult(cbScreen, false) ?? \"default\";\n\t\t\tsAutoHide = $$\"\"\"\n\t\t\n\t\t//auto-hide at the specified screen edge. Above is the auto-hide part. Below is the always-visible part.\n\t\tt = t.AutoHideScreenEdge(TMEdge.{{cbEdge.SelectedItem}}, {{sScreen}}, 5, ^5, 2);\n\t\tt.BorderColor = System.Drawing.Color.Orange;\n\t\t//if (t.FirstTime) {\n\t\t\t\n\t\t//}\n\t\t\n\"\"\";\n\t\t} else if (iTrigger == 3) { //screen+mouse\n\t\t\tsAutoHide = \"\"\"\n\t\t\n\t\t//auto-hide at the screen edge of the mouse trigger. Above is the auto-hide part. Below is the always-visible part.\n\t\tt = t.AutoHideScreenEdge(ta, 5, ^5, 2);\n\t\tt.BorderColor = System.Drawing.Color.Orange;\n\t\t//if (t.FirstTime) {\n\t\t\t\n\t\t//}\n\t\t\n\"\"\";\n\t\t}\n\t\t\n\t\tvar sArg = iTrigger switch {\n\t\t\t0 or 1 => \"WindowTriggerArgs ta\",\n\t\t\t3 => \"MouseTriggerArgs ta\",\n\t\t\t_ => \"TriggerArgs ta = null\"\n\t\t};\n\t\t\n\t\tbool laterName = iTrigger is 0 or 1 && cLaterName.IsChecked;\n\t\tstring sRetType = laterName ? \"toolbar\" : \"void\", sRetCode = laterName ? \"\\r\\n\\t\\t\\r\\n\\t\\treturn t;\" : null;\n\t\t\n\t\tvar text = $$\"\"\"\n\n\t{{sRetType}} {{sName}}({{sArg}}) {\n\t\tvar t = new toolbar();\n\t\tif (t.FirstTime) {\n\t\t\t//t.DisplayText = !true; //display only icon or 2 characters, unless button text is like \"Text\\a\"\n\t\t\t\n\t\t}\n\t\t\n\t\t//Add buttons here, like in examples. Hints: toolbarButtonSnippet, drag-drop, Ctrl+Shift+Q.\n\t\t//More info: 1. Cookbook > Floating toolbars. 2. toolbar class help (click word toolbar above and press F1).\n\t\t\n\t\tt[\"Example\"] = o => { print.it(\"button clicked\"); };\n\t\tt[\"|Tooltip1\", image: \"*Modern.TreeLeaf #73BF00\"] = o => {  }; //to set icon use the Icons dialog\n\t\tt[\"Text\\a\"] = o => {  }; //this button always displays text. The above button never.\n\t\tt.Menu(\"Menu1\", t => {\n\t\t\tt[\"A\"] = o => {  };\n\t\t\tt[\"B|Tooltip3\"] = o => {  };\n\t\t});\n\t\tt.Separator();\n\t\tt[\"Text2\\a|Tooltip2\"] = o => {  };\n{{sAutoHide}}\n\t\tt.Show(ta);\n\t\t\n\t\t////this code is the same as t.Show(ta), but you can specify more Show parameters, for example attach to a control\n\t\t//if (ta is WindowTriggerArgs wta) {\n\t\t//\tt.Show(wta.Window); //attach to the trigger window\n\t\t//} else {\n\t\t//\tt.Show();\n\t\t//\tta?.DisableTriggerUntilClosed(t); //single instance\n\t\t//}{{sRetCode}}\n\t}\n\n\"\"\";\n\t\t\n\t\tif (cbFile.SelectedItem is not FileNode f) { //new file\n\t\t\ttext = $$\"\"\"\nusing Au.Triggers;\n\npartial class Program {\n\t[Toolbars]\n\tvoid {{_GetUniqueNameInProgram(sName + \"_Triggers\")}}() {\n\t}\n\n\"\"\"\n+ text\n+ @\"}\n\";\n\t\t\tvar folder = App.Model.Find(@\"\\@Triggers and toolbars\\Toolbars\", FNFind.Folder);\n\t\t\tf = App.Model.NewItem(\"Class.cs\", new(folder, FNInsert.Last), sName + \".cs\", text: new(true, text));\n\t\t} else {\n\t\t\tvar programNode = _ProgramClassNodeFromST(_fnToSt[f]); if (programNode == null) return;\n\t\t\t_OpenSourceFile(f).aaaInsertText(true, programNode.CloseBraceToken.SpanStart, text);\n\t\t}\n\t\t\n\t\t_Update();\n\t\tvar t = _toolbars[Array.FindIndex(_toolbars, o => o.Name == sName)];\n\t\t\n\t\t//trigger\n\t\tif (iTrigger != 4) {\n\t\t\twait.doEvents(30); //workaround for bad scrolling (mouse/screen) etc\n\t\t\tif (iTrigger is 0 or 1) { //window\n\t\t\t\t_AddTriggerWindow(t, windowTriggerPage);\n\t\t\t} else if (iTrigger == 2) { //startup\n\t\t\t\t_AddTriggerStartup(t);\n\t\t\t} else { //mouse\n\t\t\t\t_AddTriggerMouse(t, cbEdge.SelectedItem as string, cbScreen);\n\t\t\t}\n\t\t\t_Update();\n\t\t\tif (!_StillExists(ref t)) return;\n\t\t}\n\t\t\n\t\t//go to the toolbar function\n\t\tint i = t.location.SourceSpan.Start;\n\t\ttimer.after(iTrigger != 4 ? 500 : 10, _ => { //workaround for bad scrolling. Also briefly shows the trigger.\n\t\t\t_OpenSourceFile(f)?.aaaGoToPos(true, i);\n\t\t});\n\t\t\n\t\t//maybe a settings file exists with this name, probably orphaned\n\t\tvar jsFolder = folders.Workspace + \".toolbars\";\n\t\tvar jsPath = jsFolder + \"\\\\\" + sName + \".json\";\n\t\tif (filesystem.exists(jsPath)) {\n\t\t\t//rejected: show a dialog box.\n\t\t\t//CONSIDER: for new toolbar names use name+GUID.\n\t\t\tif (true == filesystem.delete(jsPath, FDFlags.RecycleBin | FDFlags.CanFail))\n\t\t\t\tprint.it($\"<>Note: A toolbar settings file with this name ({sName}) has been found, and moved to the Recycle Bin to avoid confusion.\\r\\n\\tInfo: Each toolbar has a settings file with the same name, saved <link {jsFolder}>here<>. The program does not delete settings files of deleted or renamed toolbars. You can delete unused files. Deleting a used file resets the position, size and context menu settings of that toolbar.\");\n\t\t}\n\t}\n\t\n\tstatic DPwnd _AddWindowPage(wpfBuilder b) {\n\t\tDPwnd p = new(\"Window\");\n\t\tb.R.Add(p).Margin(\"LR\")\n\t\t\t.Validation(o => p.IsVisible && !p.HasResult ? \"Window not specified\" : null);\n\t\treturn p;\n\t}\n\t\n\tvoid _SetToolbarTrigger() {\n\t\tvar t = _ToolbarFromCurrentPos(); if (t == null) return;\n\t\t\n\t\tvar w = new KDialogWindow { Title = \"New trigger for \" + t.Name, ShowInTaskbar = false };\n\t\tvar b = new wpfBuilder(w).WinSize(500);\n\t\t\n\t\tComboBox cbReplace = null, cbEdge = null, cbScreen = null;\n\t\tDPwnd windowTriggerPage = null;\n\t\t\n\t\tif (t.triggers.Length > 0) {\n\t\t\tb.R.Add(\"Replace trigger\", out cbReplace);\n\t\t\tcbReplace.Items.Add(\"Don't replace\");\n\t\t\tforeach (var v in t.triggers) cbReplace.Items.Add(v);\n\t\t\tcbReplace.SelectedIndex = 0;\n\t\t}\n\t\t\n\t\tint iTrigger = -1; //0 window, 1 startup, 2 mouse\n\t\tif (t.method.Parameters.Length > 0) {\n\t\t\tvar pt = t.method.Parameters[0].Type;\n\t\t\tif (pt == _compilation.GetTypeByMetadataName(\"Au.Triggers.WindowTriggerArgs\")) iTrigger = 0;\n\t\t\telse if (pt == _compilation.GetTypeByMetadataName(\"Au.Triggers.MouseTriggerArgs\")) iTrigger = 2;\n\t\t}\n\t\t\n\t\tb.R.Add(\"Trigger type\", out ComboBox cbTrigger).Disabled(iTrigger >= 0)\n\t\t\t.Items(iTrigger switch { 0 => \"Window\", 2 => \"Mouse\", _ => \"Window|Show at startup\" });\n\t\tif (iTrigger == 2) {\n\t\t\tb.R.Add(\"Screen edge\", out cbEdge).Items(typeof(TMEdge).GetEnumNames()).Select(1)\n\t\t\t\t.And(170).StartGrid();\n\t\t\tcbScreen = _ScreenComboBoxInit(b);\n\t\t\tb.End();\n\t\t} else if (iTrigger <= 0) {\n\t\t\twindowTriggerPage = _AddWindowPage(b);\n\t\t\tcbTrigger.SelectionChanged += (_, _) => { windowTriggerPage.Visibility = cbTrigger.SelectedIndex == 0 ? Visibility.Visible : Visibility.Collapsed; };\n\t\t}\n\t\t//b.R.Add(out KCheckBox cRestart, \"Restart TT script\").Checked(); //rejected\n\t\t\n\t\tb.R.AddOkCancel();\n\t\tb.End();\n\t\t\n\t\tif (!w.ShowAndWait(App.Wmain)) return;\n\t\tif (!_StillExists(ref t)) return;\n\t\t\n\t\tint pos = -1;\n\t\tif (cbReplace?.SelectedItem is _Trigger u && _GetTriggerStatementFullRange2(u, out var span, replacing: true)) {\n\t\t\tvar doc = _OpenSourceFile(t.fn, span.Start);\n\t\t\tusing var undo = doc.ENewUndoAction();\n\t\t\tdoc.aaaDeleteRange(true, span.Start, span.End);\n\t\t\tpos = span.Start;\n\t\t\t_Add();\n\t\t} else {\n\t\t\t_Add();\n\t\t}\n\t\t_Update();\n\t\t\n\t\tvoid _Add() {\n\t\t\tif (iTrigger < 0) iTrigger = cbTrigger.SelectedIndex;\n\t\t\tif (iTrigger == 0) {\n\t\t\t\t_AddTriggerWindow(t, windowTriggerPage, pos);\n\t\t\t} else if (iTrigger == 1) {\n\t\t\t\t_AddTriggerStartup(t, pos);\n\t\t\t} else if (iTrigger == 2) {\n\t\t\t\t_AddTriggerMouse(t, cbEdge.SelectedItem as string, cbScreen, pos);\n\t\t\t}\n\t\t}\n\t}\n\t\n\t_Toolbar _ToolbarFromCurrentPos() {\n\t\tvar doc = Panels.Editor.ActiveDoc; if (doc == null) return null;\n\t\tint pos = doc.aaaCurrentPos16;\n\t\tvar f = doc.EFile;\n\t\t//is pos in a toolbar function?\n\t\tvar t = _toolbars.FirstOrDefault(o => o.fn == f && o.method.DeclaringSyntaxReferences[0].Span.ContainsOrTouches(pos));\n\t\tif (t != null) return t;\n\t\t//is pos in a name of a toolbar function?\n\t\tforeach (var v in _toolbars) {\n\t\t\tforeach (var tr in v.triggers) if (tr.fn == f && tr.location.SourceSpan.ContainsOrTouches(pos)) return v;\n\t\t}\n\t\t//get the first toolbar in the file\n\t\treturn _toolbars.FirstOrDefault(o => o.fn == f);\n\t}\n\t\n\t//void _EditToolbar(_Toolbar t) {\n\t//\t_OpenSourceFile(t, t.location.SourceSpan);\n\t//}\n\t\n\t//rejected. It's better if the user reviews that code and deletes manually.\n\t//void _DeleteToolbar(_Toolbar t, bool commentOut) {}\n\t\n\tvoid _AddTriggerWindow(_Toolbar t, DPwnd p, int pos = -1) {\n\t\tstring sTrigger = p.AaResultCode, sAction = t.Name, sSep = \" \";\n\t\tif (!t.method.ReturnsVoid) {\n\t\t\tsTrigger += \", later: TWLater.Name\";\n\t\t\tsAction = $\"ta => ta.ShowToolbarWhenWindowName({sAction}, \\\"*window_name_when_the_toolbar_is_visible*\\\")\";\n\t\t\tsSep = \"\\r\\n\\t\";\n\t\t\tprint.it(\"\"\"\nPlease edit window name strings in the toolbar trigger code.\n\tThe first should contain only the non-changing part, for example \"*Program\".\n\tThe second - when the toolbar must be visible, for example \"Document -*\"\n\"\"\");\n\t\t}\n\t\t_AddTrigger(t, $\"Triggers.Window[TWEvent.ActiveOnce, {sTrigger}] ={sSep}{sAction};\", pos);\n\t}\n\t\n\tvoid _AddTriggerStartup(_Toolbar t, int pos = -1) {\n\t\t_AddTrigger(t, $\"{t.Name}();\", pos);\n\t}\n\t\n\tvoid _AddTriggerMouse(_Toolbar t, string edge, ComboBox cbScreen, int pos = -1) {\n\t\tstring s = _ScreenComboBoxResult(cbScreen, true);\n\t\tif (s != null) s = \", screen: \" + s;\n\t\t_AddTrigger(t, $\"Triggers.Mouse[TMEdge.{edge}{s}] = {t.Name};\", pos);\n\t}\n\t\n\tvoid _AddTrigger(_Toolbar t, string s, int pos) {\n\t\tif (pos < 0) pos = _FindToolbarTriggersFunction(t).node.Body.CloseBraceToken.SpanStart;\n\t\tif (null == _OpenSourceFile(t.fn, pos)) return;\n\t\tInsertCode.Statements(new(s, selectNewCode: true));\n\t}\n\t\n\tstatic void _EditTrigger(_Trigger t) {\n\t\t_OpenSourceFile(t.fn, t.location.SourceSpan);\n\t}\n\t\n\t//bool _DeleteTrigger(_Trigger t/*, bool commentOut = false*/) {\n\t//\tif (!_GetTriggerStatementFullRange2(t, out var span, replacing: false)) return false;\n\t//\tvar doc = _OpenSourceFile(t.fn, span.Start);\n\t//\t//doc.aaaSelect(true, span.Start, span.End, true); return -1;\n\t//\t//if (commentOut) {\n\t//\t//\tdoc.aaaSelect(true, span.Start, span.End, true);\n\t//\t//\tModifyCode.CommentLines(true);\n\t//\t//} else {\n\t//\tdoc.aaaDeleteRange(true, span.Start, span.End);\n\t//\t//}\n\t//\t_Update();\n\t//\treturn true;\n\t//}\n\t\n\tComboBox _ScreenComboBoxInit(wpfBuilder b) {\n\t\tb.Add(\"Screen\", out ComboBox cb);\n\t\tcb.Items.Add(\"Primary\");\n\t\tcb.SelectedIndex = 0;\n\t\t\n\t\tvar a = screen.all;\n\t\tif (a.Length > 1) {\n\t\t\t//add functions of screen.at\n\t\t\tforeach (var v in typeof(screen.at).GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public)) {\n\t\t\t\tcb.Items.Add(\"screen.at.\" + v.Name);\n\t\t\t}\n\t\t\t\n\t\t\t//if defined type \"screens\", add its public static properties that return screen\n\t\t\t//\tThis code works, but probably this feature would be rarely used. Now undocumented.\n\t\t\t//if (_compilation.GetSymbolsWithName(\"screens\", SymbolFilter.Type).FirstOrDefault() is INamedTypeSymbol screens) {\n\t\t\t//\tforeach (var v in screens.GetMembers()) {\n\t\t\t//\t\tif (v is not IPropertySymbol p || !v.IsStatic || v.DeclaredAccessibility is not Microsoft.CodeAnalysis.Accessibility.Public) continue;\n\t\t\t//\t\tif (p.Type.ToString() != \"Au.screen\") continue;\n\t\t\t//\t\tcb.Items.Add(\"screens.\" + v.Name);\n\t\t\t//\t}\n\t\t\t//}\n\t\t\t\n\t\t\t//if (a.Length == 2) cb.Items.Add(\"screen.index(1)\"); //no. More screens may be added in the future, and indices may change then.\n\t\t}\n\t\t\n\t\treturn cb;\n\t}\n\t\n\tstatic string _ScreenComboBoxResult(ComboBox cbScreen, bool trigger) {\n\t\tint iScreen = cbScreen.SelectedIndex;\n\t\tif (iScreen == 0) return null;\n\t\tvar s = cbScreen.Items[iScreen] as string;\n\t\t//if (s.Starts(\"screens.\")) return s;\n\t\t//if (s.Starts(\"screen.at.\"))\n\t\treturn s + (trigger ? \"(true)\" : \"()\");\n\t\t//return trigger ? s.Insert(^1, \", lazy: true\") : s;\n\t}\n\t\n\tstatic bool _GetTriggerStatementFullRange2(_Trigger t, out TextSpan span, bool replacing) {\n\t\tif (_GetTriggerStatementFullRange(t, out span, replacing)) return true;\n\t\tprint.it(\"This trigger should be deleted manually: \" + t.text + \"\\r\\n\\tIt depends on other code which should be edited, deleted or reviewed.\");\n\t\tif (!replacing) _EditTrigger(t);\n\t\treturn false;\n\t}\n\t\n\tstatic bool _GetTriggerStatementFullRange(_Trigger t, out TextSpan span, bool replacing) {\n\t\tspan = default;\n\t\tvar node = t.location.FindNode(default);\n\t\tg1:\n\t\tvar ss = node.GetAncestor<StatementSyntax>();\n\t\tif (ss == null) return false;\n\t\t//print.clear(); CiUtil.PrintNode(ss); CiUtil.PrintNode(ss.Parent);\n\t\tvar pa = ss.Parent;\n\t\tif (pa is not BlockSyntax bs) return false;\n\t\tif (t.isTrigger && bs.Parent is SimpleLambdaExpressionSyntax) {\n\t\t\tif (bs.Statements.Count == 1 && bs.ToString().RxIsMatch(@\"^\\{\\s*\\w.+;\\s*\\}$\")) { //lambda { single statement }\n\t\t\t\tnode = bs; goto g1;\n\t\t\t}\n\t\t\tif (replacing) return false;\n\t\t}\n\t\tvar from = ss.SpanStart;\n\t\tif (ss.HasLeadingTrivia) {\n\t\t\tvar u = ss.GetLeadingTrivia()[^1];\n\t\t\tif (u.Kind() == SyntaxKind.WhitespaceTrivia) from = u.SpanStart;\n\t\t}\n\t\tspan = TextSpan.FromBounds(from, ss.FullSpan.End);\n\t\treturn true;\n\t}\n\t\n\t/// <summary>\n\t/// Finds the toolbar in current _toolbars.\n\t/// If _toolbars changed and does not contain t, tries to find in the new _toolbars and updates t; returns false if not found (the toolbar code has been deleted).\n\t/// </summary>\n\tbool _StillExists(ref _Toolbar t) {\n\t\tvar tt = t;\n\t\tt = _toolbars.FirstOrDefault(o => o.EqualsMethodQName(tt));\n\t\treturn t != null && !t.fn.IsDeleted;\n\t}\n\t\n\tstatic SciCode _OpenSourceFile(FileNode f, int pos = -1) {\n\t\tif (App.Model.OpenAndGoTo(f, columnOrPos: pos)) return Panels.Editor.ActiveDoc;\n\t\treturn null;\n\t}\n\t\n\tstatic SciCode _OpenSourceFile(FileNode f, TextSpan span) {\n\t\tif (!App.Model.OpenAndGoTo(f)) return null;\n\t\tvar doc = Panels.Editor.ActiveDoc;\n\t\tdoc.aaaSelect(true, span.End, span.Start, true);\n\t\treturn doc;\n\t}\n\t\n\tIEnumerable<IMethodSymbol> _EnumToolbarTriggersFunctions() {\n\t\tvar at = _programSym.GetTypeMembers(\"ToolbarsAttribute\")[0];\n\t\tforeach (var m in _programSym.GetMembers().OfType<IMethodSymbol>()) {\n\t\t\tforeach (var a in m.GetAttributes()) if (a.AttributeClass == at) yield return m;\n\t\t}\n\t}\n\t\n\tClassDeclarationSyntax _ProgramClassNodeFromST(SyntaxTree tree)\n\t\t=> _programSym.Locations.FirstOrDefault(o => o.SourceTree == tree)?.FindNode(default) as ClassDeclarationSyntax;\n\t\n\tstring _GetUniqueNameInProgram(string name) {\n\t\tif (_programSym.MemberNames.Contains(name)) {\n\t\t\tfor (int i = 2; ; i++) {\n\t\t\t\tvar n = name + i;\n\t\t\t\tif (!_programSym.MemberNames.Contains(n)) return n;\n\t\t\t}\n\t\t}\n\t\treturn name;\n\t}\n\t\n\t(IMethodSymbol sym, MethodDeclarationSyntax node) _FindToolbarTriggersFunction(_Toolbar t) {\n\t\tbool retry = false;\n\t\tg1:\n\t\tforeach (var m in _EnumToolbarTriggersFunctions()) {\n\t\t\tvar loc = m.Locations[0];\n\t\t\tif (loc.SourceTree == t.tree) return (m, loc.FindNode(default) as MethodDeclarationSyntax);\n\t\t}\n\t\tif (retry) return default; retry = true;\n\t\t\n\t\t//create the function\n\t\tstring name = _GetUniqueNameInProgram(\"Toolbars\");\n\t\tvar programNode = _ProgramClassNodeFromST(t.tree);\n\t\t_OpenSourceFile(t.fn, programNode.OpenBraceToken.FullSpan.End);\n\t\tvar s = $$\"\"\"\n\n[Toolbars]\nvoid {{name}}() {\n\t\n}\n\n\n\"\"\";\n\t\tInsertCode.TextSimply(s);\n\t\t_Update();\n\t\tt = _toolbars.First(o => o.EqualsMethodQName(t));\n\t\tgoto g1;\n\t}\n\t\n\tvoid _Update() {\n\t\tvar a = new List<_Toolbar>();\n\t\tvar at = new List<_Trigger>();\n\t\tvar proj = TriggersAndToolbars.GetProject(create: true);\n\t\tusing var ws = new CiWorkspace(proj, CiWorkspace.Caller.Other);\n\t\t_meta = ws.Meta;\n\t\t_sln = ws.Solution;\n\t\t_compilation = ws.GetCompilation();\n\t\tvar ntToolbar = _compilation.GetTypeByMetadataName(\"Au.toolbar\");\n\t\t\n\t\t_fnToSt = new();\n\t\t_fnFromSt = new();\n\t\tint iTree = 0;\n\t\tforeach (var tree in _compilation.SyntaxTrees) {\n\t\t\tDebug.Assert(tree.FilePath == _meta.CodeFiles[iTree].f.ItemPath);\n\t\t\tvar f = _meta.CodeFiles[iTree++].f;\n\t\t\t_fnToSt[f] = tree;\n\t\t\t_fnFromSt[tree] = f;\n\t\t\t\n\t\t\tvar semo = _compilation.GetSemanticModel(tree);\n\t\t\tvar cu = semo.SyntaxTree.GetCompilationUnitRoot();\n\t\t\tvar k = semo.GetAllDeclaredSymbols(cu, default);\n\t\t\tIMethodSymbol mPrev = null;\n\t\t\tforeach (var v in k) {\n\t\t\t\tif (v is ILocalSymbol loc && loc.Type == ntToolbar) {\n\t\t\t\t\tif (loc.DeclaringSyntaxReferences[0].GetSyntax() is VariableDeclaratorSyntax vd && vd.Initializer is EqualsValueClauseSyntax evc && evc.Value is BaseObjectCreationExpressionSyntax or InvocationExpressionSyntax) {\n\t\t\t\t\t\tvar m = loc.ContainingSymbol as IMethodSymbol;\n\t\t\t\t\t\tif (m == mPrev) continue; mPrev = m; //get single toolbar in function\n\t\t\t\t\t\tvar t = new _Toolbar { method = m, location = m.Locations[0], Name = m.Name, fn = f, tree = tree, variable = loc, };\n\t\t\t\t\t\t\n\t\t\t\t\t\tat.Clear();\n\t\t\t\t\t\tforeach (var x in SymbolFinder.FindCallersAsync(m, _sln).Result_()) {\n\t\t\t\t\t\t\tforeach (var y in x.Locations) {\n\t\t\t\t\t\t\t\tvar node = y.FindNode(default);\n\t\t\t\t\t\t\t\tstring tt = \"?\";\n\t\t\t\t\t\t\t\tbool isTrigger = false;\n\t\t\t\t\t\t\t\tif (node.GetAncestor<AssignmentExpressionSyntax>()?.Left is ElementAccessExpressionSyntax ea) {\n\t\t\t\t\t\t\t\t\tvar semo2 = y.SourceTree == tree ? semo : _compilation.GetSemanticModel(y.SourceTree);\n\t\t\t\t\t\t\t\t\tvar ty = semo2.GetTypeInfo(ea.Expression).Type;\n\t\t\t\t\t\t\t\t\tif (isTrigger = ty.ContainingNamespace.ToString() == \"Au.Triggers\") {\n\t\t\t\t\t\t\t\t\t\ttt = ea.ArgumentList.ToString();\n\t\t\t\t\t\t\t\t\t\tif (ty.Name == nameof(WindowTriggers)) tt = tt.RxReplace(@\"^\\[\\s*TWEvent\\.\\w+\\s*,\\s*\", \"[\");\n\t\t\t\t\t\t\t\t\t\telse tt = ty.Name[..^8] + tt;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (!isTrigger) {\n\t\t\t\t\t\t\t\t\tforeach (var p in node.Ancestors()) {\n\t\t\t\t\t\t\t\t\t\tif (p is LocalFunctionStatementSyntax lf) { tt = /*\"Called from \" +*/ lf.Identifier.Text; break; }\n\t\t\t\t\t\t\t\t\t\tif (p is MethodDeclarationSyntax met) { tt = /*\"Called from \" +*/ met.Identifier.Text; break; }\n\t\t\t\t\t\t\t\t\t\tif (p is MemberDeclarationSyntax mem && p is not BaseTypeDeclarationSyntax) { tt = mem.ToString(); break; } //field, property, method\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\ttt = tt.Replace(\"\\t\", \"\").RxReplace(@\"\\R\", \" \");\n\t\t\t\t\t\t\t\tat.Add(new(_fnFromSt[y.SourceTree], y, tt, isTrigger));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tt.triggers = at.ToArray();\n\t\t\t\t\t\tif (at.Any()) t.TriggerText = string.Join('\\n', at.Select(o => o.text));\n\t\t\t\t\t\t\n\t\t\t\t\t\ta.Add(t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\t_toolbars = a.ToArray();\n\t\t_programSym = _compilation.GlobalNamespace.GetTypeMembers(\"Program\")[0];\n\t}\n\t\n\tclass _Toolbar {\n\t\tpublic FileNode fn;\n\t\tpublic SyntaxTree tree;\n\t\tpublic IMethodSymbol method;\n\t\tpublic Location location;\n\t\tpublic ILocalSymbol variable; //currently not used. In the future may be used for adding toolbar buttons.\n\t\tpublic _Trigger[] triggers;\n\t\t\n\t\tpublic string Name { get; set; }\n\t\tpublic string TriggerText { get; set; }\n\t\t\n\t\t/// <summary>Equals method qualified name.</summary>\n\t\tpublic bool EqualsMethodQName(_Toolbar t) => Name == t.Name && method.ToString() == t.method.ToString();\n\t\t\n\t\t/// <summary>Equals SourceTree and SourceSpan.</summary>\n\t\tpublic bool EqualsMethodLocation(Location loc) => loc.SourceTree.FilePath == location.SourceTree.FilePath && loc.SourceSpan.Start == location.SourceSpan.Start;\n\t}\n\t\n\trecord _Trigger(FileNode fn, Location location, string text, bool isTrigger) {\n\t\tpublic override string ToString() => text;\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Triggers and toolbars/TT-triggers.cs",
    "content": "using System.Windows;\nusing System.Windows.Controls;\nusing Au.Controls;\nusing ToolLand;\nusing Au.Triggers;\n\nnamespace LA;\n\npartial class TriggersAndToolbars {\n\tpublic enum TriggersType { None, Hotkey, Autotext, Mouse, Window }\n\t\n\t/// <summary>\n\t/// Tool dialog \"New trigger\".\n\t/// </summary>\n\t/// <param name=\"selectTriggersType\">If not 0, selects trigger type.</param>\n\t/// <param name=\"windowTriggerWnd\">Window for window trigger.</param>\n\tpublic static void NewTrigger(TriggersType selectTriggersType = 0, wnd windowTriggerWnd = default) {\n\t\tvar owner = selectTriggersType > 0 ? null : App.Wmain;\n\t\tvar w = new KDialogWindow { Title = \"New trigger\", ShowInTaskbar = owner == null, Topmost = owner == null };\n\t\tvar b = new wpfBuilder(w).WinSize(500, 400);\n\t\tb.Options(bindLabelVisibility: true);\n\t\t\n\t\tUserControl[][] pages = [new UserControl[2], new UserControl[2], new UserControl[2], new UserControl[3]];\n\t\tint iPage = 0;\n\t\tButton bBack = null, bNext = null;\n\t\t_HotkeyTriggerPage hotkeyTriggerPage = null;\n\t\t_AutotextTriggerPage autotextTriggerPage = null;\n\t\t_MouseTriggerPage mouseTriggerPage = null;\n\t\t_WindowTriggerSettingsPage windowTriggerSettingsPage = null;\n\t\tDPwnd windowTriggerWindowPage = null;\n\t\t\n\t\tb.Row(-1).Add(out ContentControl ccPages).Margin(\"0\");\n\t\tb.Child().Add(out UserControl firstPage).StartGrid(childOfLast: true);\n\t\tfor (int i = 0; i < 4; i++) pages[i][0] = firstPage;\n\t\t\n\t\tTriggersType tType = selectTriggersType != 0 ? selectTriggersType : _CodeAnalysis.GetTriggersType();\n\t\tvar fn = App.Model.CurrentFile;\n\t\tbool canRunThisScript = fn.IsExecutableDirectly() && !IsTtScipt(fn);\n\t\t\n\t\tb.R.Add(\"Trigger\", out ToolBar tb).Margin(\"LRT\").Brush(SystemColors.ControlBrush);\n\t\ttb.HideGripAndOverflow();\n\t\tstring[] aTriggersTypeStrings = { \"Hotkey\", \"Autotext\", \"Mouse\", \"Window\" };\n\t\tvar abc = new RadioButton[aTriggersTypeStrings.Length];\n\t\tfor (int i = 0; i < abc.Length; i++) {\n\t\t\ttb.Items.Add(abc[i] = new RadioButton { Content = aTriggersTypeStrings[i], Width = 60, Margin = new(0, 0, 3, 0), BorderBrush = SystemColors.ActiveBorderBrush });\n\t\t}\n\t\t\n\t\t//var bMore = new Button { Content = \"...\", Width = 24, BorderBrush = SystemColors.ActiveBorderBrush };\n\t\t//bMore.Click += (_, _) => {\n\t\t//\tvar m = new popupMenu();\n\t\t//\tm[\"Command line\"] = o => { b.Window.Close(); Menus.TT.Script_launchers.Command_line(); };\n\t\t//\tm[\"Schedule\"] = o => { b.Window.Close(); Menus.TT.Script_launchers.Schedule(); };\n\t\t//\tm[\"Shortcut\"] = o => { b.Window.Close(); Menus.TT.Script_launchers.Shortcut(); };\n\t\t//\tm[\"Shell menu\"] = o => { b.Window.Close(); Menus.TT.Script_launchers.Shell_menu(); };\n\t\t//\tm[\"Run at startup\"] = o => { b.Window.Close(); Menus.TT.Script_launchers.Run_at_startup(); };\n\t\t//\tm[\"Open file \\\"Other triggers\\\"\"] = o => { b.Window.Close(); Menus.TT.Other_triggers(); };\n\t\t//\tm.Show(owner: b.Window);\n\t\t//};\n\t\t//tb.Items.Add(bMore);\n\t\t\n\t\tb.Row(-1).Add(\"Action\", out ListBox lbAction);\n\t\tScrollViewer.SetHorizontalScrollBarVisibility(lbAction, ScrollBarVisibility.Disabled);\n\t\t\n\t\tb.Row(80).Add(out KSciInfoBox info);\n\t\t\n\t\tb.End(); //of firstPage\n\t\t\n\t\tb.R.StartOkCancel();\n\t\tb.AddButton(out bBack, ImageUtil.LoadWpfImageElement(\"*EvaIcons.ArrowBack @12\" + EdIcons.black), k => {\n\t\t\tccPages.Content = pages[(int)tType - 1][--iPage];\n\t\t\tbBack.IsEnabled = iPage > 0;\n\t\t\tbNext.Content = \"Next\";\n\t\t}).Padding(\"L3R3\").Disabled();\n\t\t\n\t\tb.AddButton(out bNext, \"Next\", k => { k.Cancel = _NextPage(); }, WBBFlags.OK);\n\t\t\n\t\tbool _NextPage() {\n\t\t\tint lastPage = tType == TriggersType.Window ? 2 : 1;\n\t\t\tif (iPage == lastPage) return false;\n\t\t\tiPage++;\n\t\t\tif (tType == TriggersType.Hotkey) {\n\t\t\t\tif (iPage == 1) pages[(int)tType - 1][iPage] ??= hotkeyTriggerPage = new();\n\t\t\t\tif (iPage == 1) timer.after(1, _ => { hotkeyTriggerPage.FocusHotkey(); }); //does not work now (after `ccPages.Content = ...`), and even with Dispatcher\n\t\t\t} else if (tType == TriggersType.Autotext) {\n\t\t\t\tif (iPage == 1) pages[(int)tType - 1][iPage] ??= autotextTriggerPage = new();\n\t\t\t\tif (iPage == 1) timer.after(1, _ => { autotextTriggerPage.FocusText(); });\n\t\t\t} else if (tType == TriggersType.Mouse) {\n\t\t\t\tif (iPage == 1) pages[(int)tType - 1][iPage] ??= mouseTriggerPage = new();\n\t\t\t} else if (tType == TriggersType.Window) {\n\t\t\t\tif (iPage == 1) {\n\t\t\t\t\tif (windowTriggerWindowPage == null) {\n\t\t\t\t\t\tpages[(int)tType - 1][iPage] = windowTriggerWindowPage = new();\n\t\t\t\t\t\tif (windowTriggerWnd.IsAlive) windowTriggerWindowPage.SetWnd(windowTriggerWnd);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (iPage == 2) pages[(int)tType - 1][iPage] ??= windowTriggerSettingsPage = new();\n\t\t\t}\n\t\t\tccPages.Content = pages[(int)tType - 1][iPage];\n\t\t\tbBack.IsEnabled = true;\n\t\t\tbNext.Content = iPage < lastPage ? \"Next\" : \"OK\";\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\tb.AddButton(\"Cancel\", null, WBBFlags.Cancel);\n\t\tb.End();\n\t\t\n\t\tb.End();\n\t\t\n\t\tif (tType > 0) abc[(int)tType - 1].IsChecked = true;\n\t\tif (selectTriggersType == 0) _SetTriggerType(tType, true);\n\t\t\n\t\tfor (int i = 0; i < abc.Length; i++) {\n\t\t\tint tt = i + 1;\n\t\t\tabc[i].Checked += (o, _) => _SetTriggerType((TriggersType)tt, false);\n\t\t}\n\t\t\n\t\tif (selectTriggersType > 0) {\n\t\t\t_SetTriggerType(tType, false);\n\t\t\t//_NextPage();\n\t\t}\n\t\t\n\t\tvoid _SetTriggerType(TriggersType tt, bool startup) {\n\t\t\tif (!startup) {\n\t\t\t\tif (!_CodeAnalysis.IsNonStandardFileWithTriggers())\n\t\t\t\t\tTriggersAndToolbars.Edit($@\"Triggers\\{aTriggersTypeStrings[(int)tt - 1]} triggers.cs\");\n\t\t\t\ttType = tt;\n\t\t\t}\n\t\t\tbool enable = tType > 0;\n\t\t\tif (enable) {\n\t\t\t\tinfo.aaaText = tType switch {\n\t\t\t\t\tTriggersType.Window => $\"\"\"\nNow in code click where to insert the new trigger.\nThen click Next, capture window, Next, set trigger properties, OK.\n\"\"\",\n\t\t\t\t\tTriggersType.Autotext => $\"\"\"\nNow in code click where to insert the new trigger.\nThen click Next, set trigger properties, OK.\nIn code edit the replacement text. Can use [[|]] to move the caret.\nTo set trigger scope window can be used {App.Settings.hotkeys.tool_quick}.\n\"\"\",\n\t\t\t\t\t_ => $\"\"\"\nNow in code click where to insert the new trigger.\nThen click Next, set trigger properties, OK.\nTo set trigger scope window can be used {App.Settings.hotkeys.tool_quick}.\n\"\"\"\n\t\t\t\t} + \"\\r\\nFinally click Run to [re]start the triggers script.\";\n\t\t\t\t\n\t\t\t\tvar scriptRun = $\"o => script.run(@\\\"{(canRunThisScript ? fn.ItemPathOrName() : \"script.cs\")}\\\")\";\n\t\t\t\tstring[] a = tType switch {\n\t\t\t\t\tTriggersType.Autotext => [\n\t\t\t\t\t\t\"\"\"o => o.Replace(\"replacement\")\"\"\",\n\t\t\t\t\t\t\"\"\"\"o => o.Replace(\"\"\"multiline replacement\"\"\")\"\"\"\",\n\t\t\t\t\t\t//\"\\\"replacement\\\"\", \"\\\"\\\"\\\"multiline replacement\\\"\\\"\\\"\", //rejected. Just another way to do the same, which is better to avoid. It has sense only when users manually write code.\n\t\t\t\t\t\t\"\"\"o => o.Menu(\"one\", \"two\", new(\"Label3\", \"three\"))\"\"\",\n\t\t\t\t\t\t\"o => { print.it(o); }\",\n\t\t\t\t\t\tscriptRun],\n\t\t\t\t\tTriggersType.Window => [\n\t\t\t\t\t\t\"\"\"o => { print.it(\"Trigger\", o.Window); }\"\"\",\n\t\t\t\t\t\t\"\"\"o => { <close window> }\"\"\",\n\t\t\t\t\t\tscriptRun],\n\t\t\t\t\t_ => [\n\t\t\t\t\t\t\"o => { print.it(o); }\",\n\t\t\t\t\t\tscriptRun]\n\t\t\t\t};\n\t\t\t\tlbAction.ItemsSource = a;\n\t\t\t\tlbAction.SelectedIndex = 0;\n\t\t\t}\n\t\t\tinfo.Visibility = enable ? Visibility.Visible : Visibility.Hidden;\n\t\t\tlbAction.Visibility = enable ? Visibility.Visible : Visibility.Hidden;\n\t\t\tbNext.IsEnabled = enable;\n\t\t}\n\t\t\n\t\tif (!w.ShowAndWait(owner)) return;\n\t\t\n\t\t_CodeAnalysis.MoveCaretForNewTriggerOrScope();\n\t\tstring ttVar = _CodeAnalysis.GetTriggersVar(tType);\n\t\t\n\t\tstring s = null, sAction = ((string)lbAction.SelectedItem)[5..];\n\t\t\n\t\tif (tType == TriggersType.Hotkey) {\n\t\t\ts = hotkeyTriggerPage.FormatCode(sAction, ttVar);\n\t\t} else if (tType == TriggersType.Autotext) {\n\t\t\tsAction = sAction.Replace(\"multiline replacement\", \"\\r\\n\\r\\n\");\n\t\t\ts = autotextTriggerPage.FormatCode(sAction, ttVar);\n\t\t} else if (tType == TriggersType.Mouse) {\n\t\t\ts = mouseTriggerPage.FormatCode(sAction, ttVar);\n\t\t} else if (tType == TriggersType.Window) {\n\t\t\tsAction = sAction.Replace(\" <close window> \", \"\"\"\n\n\tprint.it(\"Closing window\", o.Window);\n\t//examples:\n\t//o.Window.Close();\n\t//o.Window.ButtonClick(\"Save\");\n\t//keys.send(\"Enter\");\n\t//keys.send(\"Esc\");\n\t//keys.send(\"Alt+S\");\n\n\"\"\");\n\t\t\ts = windowTriggerWindowPage.AaResultCode ?? \"\\\"`|`\\\"\";\n\t\t\ts = windowTriggerSettingsPage.FormatCode(s, sAction, ttVar);\n\t\t}\n\t\t\n\t\tInsertCode.Statements(new(s, goTo: true));\n\t\tif (s.Contains(\"`|`\")) {\n\t\t\tCodeInfo.ShowSignature();\n\t\t\tif (tType == TriggersType.Mouse) CodeInfo.ShowCompletionList();\n\t\t}\n\t}\n\t\n\tclass _HotkeyTriggerPage : UserControl {\n\t\tKHotkeyControl _hk;\n\t\tEnumUI<TKFlags> _eFlags;\n\t\tKCheckBox _cAlwaysEnabled;\n\t\t\n\t\tpublic _HotkeyTriggerPage() {\n\t\t\tvar b = new wpfBuilder(this);\n\t\t\tb.R.Add(_hk = new(true));\n\t\t\tb.R.StartStack<KGroupBox>(EdWpf.TextAndHelp<TKFlags>(\"<b>Flags</b>\"), true);\n\t\t\t_eFlags = new(b.Panel, items: [\n\t\t\t\t(TKFlags.ShareEvent, \"ShareEvent - let other apps receive the key\"),\n\t\t\t\t(TKFlags.KeyModUp, \"KeyModUp - delay until the trigger keys are released\"),\n\t\t\t\t(TKFlags.NoModOff, \"NoModOff - don't release modifier keys\"),\n\t\t\t\t(TKFlags.LeftMod, \"LeftMod - left-side modifier keys\"),\n\t\t\t\t(TKFlags.RightMod, \"RightMod - right-side modifier keys\"),\n\t\t\t\t(TKFlags.Numpad, \"NumpadOnly - only if the numpad key\"),\n\t\t\t\t(TKFlags.NumpadNot, \"NumpadNot - not if the numpad key\"),\n\t\t\t]);\n\t\t\t\n\t\t\tvar (cNumpadOnly, cNumpadNot) = (b.Panel.Children[b.Panel.Children.Count - 2] as CheckBox, b.Panel.Children[b.Panel.Children.Count - 1] as CheckBox);\n\t\t\tcNumpadOnly.Visibility = cNumpadNot.Visibility = Visibility.Collapsed;\n\t\t\t_hk.Changed += k_ => {\n\t\t\t\tif (k_ is { } k) {\n\t\t\t\t\tcNumpadOnly.IsChecked = cNumpadNot.IsChecked = false;\n\t\t\t\t\tcNumpadOnly.Visibility = cNumpadNot.Visibility = Visibility.Collapsed;\n\t\t\t\t\tif (k.Key is KKey.Enter or KKey.Home or KKey.End or KKey.PageUp or KKey.PageDown or KKey.Left or KKey.Right or KKey.Up or KKey.Down or KKey.Insert or KKey.Delete) {\n\t\t\t\t\t\tbool isNumpad = k.IsExtended == k.Key is KKey.Enter;\n\t\t\t\t\t\tvar c = isNumpad ? cNumpadOnly : cNumpadNot;\n\t\t\t\t\t\tc.IsChecked = true;\n\t\t\t\t\t\tc.Visibility = Visibility.Visible;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t};\n\t\t\t\n\t\t\tb.End();\n\t\t\tb.R.Add(out _cAlwaysEnabled, \"Always enabled\");\n\t\t\t\n\t\t\t//rejected. Can be confusing. Despite all info, probably often woud be used incorrectly. Let use the scope tool separately.\n\t\t\t//b.R.AddButton(\"Scope...\", o => {\n\t\t\t//\tif (_TriggerScopeDialog(out var s, true, Window.GetWindow(this))) _scope = s;\n\t\t\t//}).Width(70);\n\t\t}\n\t\t\n\t\tpublic string FormatCode(string actionCode, string ttVar) {\n\t\t\tStringBuilder b = new();\n\t\t\tb.Append($\"{ttVar}[\\\"{(_hk.Result ?? \"`|`\")}\\\"\");\n\t\t\t\n\t\t\tvar flags = _eFlags.Result;\n\t\t\tif (flags.Has(TKFlags.ExtendedYes | TKFlags.ExtendedNo)) flags &= ~(TKFlags.ExtendedYes | TKFlags.ExtendedNo);\n\t\t\tif (flags.Has(TKFlags.LeftMod | TKFlags.RightMod)) flags &= ~(TKFlags.LeftMod | TKFlags.RightMod);\n\t\t\tif (flags != 0) b.Append(\", flags: \").Append(TUtil.FormatFlags(flags));\n\t\t\t\n\t\t\tb.Append($\"] = o => {actionCode};\");\n\t\t\tif (_cAlwaysEnabled.IsChecked) b.Append($\"\\r\\n{ttVar}.Last.EnabledAlways = true;\");\n\t\t\treturn b.ToString();\n\t\t}\n\t\t\n\t\tpublic void FocusHotkey() {\n\t\t\t_hk.Focus();\n\t\t}\n\t}\n\t\n\tclass _AutotextTriggerPage : UserControl {\n\t\tTextBox _tText;\n\t\tEnumUI<TAFlags> _eFlags;\n\t\tEnumUI<TAPostfix> _ePostfix;\n\t\tComboBox _useFlags;\n\t\t(ComboBox use, ComboBox cb) _postfixType;\n\t\t(ComboBox use, TextBox t) _postfixChars;\n\t\tKCheckBox _cAlwaysEnabled;\n\t\t\n\t\tpublic _AutotextTriggerPage() {\n\t\t\tvar b = new wpfBuilder(this).Columns(180, -1);\n\t\t\tb.R.Add<System.Windows.Documents.AdornerDecorator>().Child().Add(out _tText).Watermark(\"Trigger text\");\n\t\t\t\n\t\t\tb.R.Add(out _useFlags).Span(1).Items(\"Use DefaultFlags|Use flags parameter|Set DefaultFlags\");\n\t\t\tb.R.StartStack(out KGroupBox gFlags, EdWpf.TextAndHelp<TAFlags>(\"<b>Flags</b>\"), true);\n\t\t\t_useFlags.SelectionChanged += (_, _) => { gFlags.Visibility = _useFlags.SelectedIndex > 0 ? Visibility.Visible : Visibility.Collapsed; };\n\t\t\t_eFlags = new EnumUI<TAFlags>(b.Panel, items: [\n\t\t\t\t(TAFlags.MatchCase, \"MatchCase - case-sensitive\"),\n\t\t\t\t(TAFlags.DontErase, \"DontErase - don't erase the text when replacing\"),\n\t\t\t\t(TAFlags.ReplaceRaw, \"ReplaceRaw - don't modify the replacement text\"),\n\t\t\t\t(TAFlags.RemovePostfix, \"RemovePostfix - erase the postfix character when replacing\"),\n\t\t\t\t(TAFlags.Confirm, \"Confirm - show confirmation UI when replacing\"),\n\t\t\t\t(TAFlags.ShiftLeft, \"ShiftLeft - select text with Shift+Left when replacing\"),\n\t\t\t]);\n\t\t\tb.End().Margin(\"L20 T\").Hidden(null);\n\t\t\t\n\t\t\tb.R.Add(out _postfixType.use).Items(\"Use DefaultPostfixType|Use postfixType parameter|Set DefaultPostfixType\");\n\t\t\tvar help2 = EdWpf.TextAndHelp<TAPostfix>(null);\n\t\t\tb.Add(out _postfixType.cb).Hidden().And(16).Add(help2).Hidden();\n\t\t\t_postfixType.use.SelectionChanged += (_, _) => { var v = _postfixType.use.SelectedIndex > 0 ? Visibility.Visible : Visibility.Hidden; _postfixType.cb.Visibility = help2.Visibility = v; };\n\t\t\t_ePostfix = new(_postfixType.cb);\n\t\t\t\n\t\t\tb.R.Add(out _postfixChars.use).Items(\"Use DefaultPostfixChars|Use postfixChars parameter|Set DefaultPostfixChars\");\n\t\t\tb.Add(out _postfixChars.t).Hidden().Tooltip(\"Postfix characters used when postfix type is Char or CharOrKey (default). Default - non-word characters.\\nUse \\\\t and \\\\r for Tab and Enter.\");\n\t\t\t_postfixChars.use.SelectionChanged += (_, _) => { _postfixChars.t.Visibility = _postfixChars.use.SelectedIndex > 0 ? Visibility.Visible : Visibility.Hidden; };\n\t\t\t\n\t\t\tb.R.Add(out _cAlwaysEnabled, \"Always enabled\").Span(-1);\n\t\t\t\n\t\t\tb.R.xAddInfoBlockF($\"\"\"\n<a {() => HelpUtil.AuHelp(\"Au.Triggers.AutotextTriggers.Item\")}>Trigger help</a>\nAlso in code you can set PostfixKey, WordCharsPlus and MenuOptions.\n\"\"\").Span(-1);\n\t\t}\n\t\t\n\t\tpublic string FormatCode(string actionCode, string ttVar) {\n\t\t\tStringBuilder b = new($\"{ttVar}[\\\"\");\n\t\t\tvar s = _tText.Text;\n\t\t\tif (s.Length == 0) s = \"`|`\"; else s = s.Escape();\n\t\t\tb.Append(s).Append('\"');\n\t\t\t\n\t\t\tif (_useFlags.SelectedIndex is int useFlags && useFlags > 0) {\n\t\t\t\ts = TUtil.FormatFlags(_eFlags.Result);\n\t\t\t\tif (useFlags == 2) b.Insert(0, $\"{ttVar}.DefaultFlags = {s};\\r\\n\");\n\t\t\t\telse if (s == \"0\") b.Append(\", flags: \").Append(s);\n\t\t\t\telse b.Append(\", \").Append(s);\n\t\t\t}\n\t\t\t\n\t\t\tif (_postfixType.use.SelectedIndex is int usePT && usePT > 0) {\n\t\t\t\ts = \"TAPostfix.\" + _postfixType.cb.Text;\n\t\t\t\tif (usePT == 1) b.Append(\", postfixType: \").Append(s);\n\t\t\t\telse b.Insert(0, $\"{ttVar}.DefaultPostfixType = {s};\\r\\n\");\n\t\t\t}\n\t\t\t\n\t\t\tif (_postfixChars.use.SelectedIndex is int usePC && usePC > 0) {\n\t\t\t\ts = _postfixChars.t.Text;\n\t\t\t\tif (s.Contains(\"\\\\n\") && !s.Contains(\"\\\\r\")) s = s.Replace(\"\\\\n\", \"\\\\r\"); //for newline need \\r, not \\n\n\t\t\t\ts = s.Unescape().Escape(quote: true);\n\t\t\t\tif (usePC == 1) b.Append(\", postfixChars: \").Append(s);\n\t\t\t\telse b.Insert(0, $\"{ttVar}.DefaultPostfixChars = {s};\\r\\n\");\n\t\t\t}\n\t\t\t\n\t\t\tb.Append($\"] = o => {actionCode};\");\n\t\t\tif (_cAlwaysEnabled.IsChecked) b.Append($\"\\r\\n{ttVar}.Last.EnabledAlways = true;\");\n\t\t\treturn b.ToString();\n\t\t}\n\t\t\n\t\tpublic void FocusText() {\n\t\t\t_tText.Focus();\n\t\t}\n\t}\n\t\n\tclass _MouseTriggerPage : UserControl {\n\t\tint _kind;\n\t\tEnumUI<TMKind> _eKind;\n\t\tComboBox[] _acb = new ComboBox[4];\n\t\tKScreenComboBox _cbScreen;\n\t\tKHotkeyControl _hkMod;\n\t\tEnumUI<TMFlags> _eFlags;\n\t\tKCheckBox _cAlwaysEnabled;\n\t\t\n\t\tpublic _MouseTriggerPage() {\n\t\t\tvar b = new wpfBuilder(this).Columns(100, 150, 0, 10, -1);\n\t\t\t\n\t\t\tb.R.Add(out ComboBox cbKind);\n\t\t\t_eKind = new(cbKind);\n\t\t\tcbKind.SelectedIndex = _kind = -1;\n\t\t\t\n\t\t\tfor (int i = 0; i < 4; i++) {\n\t\t\t\tType t = i switch { 0 => typeof(TMClick), 1 => typeof(TMWheel), 2 => typeof(TMEdge), _ => typeof(TMMove) };\n\t\t\t\t_acb[i] = new() { Padding = cbKind.Padding, ItemsSource = t.GetEnumNames() };\n\t\t\t}\n\t\t\tb.Add(out ContentControl cc);\n\t\t\tvar tHelp1 = EdWpf.TextAndHelp(null, () => (_kind switch { /*0 => typeof(TMClick), 1 => typeof(TMWheel),*/ 2 => typeof(TMEdge), _ => typeof(TMMove) }).FullName);\n\t\t\tb.Add(tHelp1).Margin(0).Hidden();\n\t\t\tb.Skip().Add(out _cbScreen).Hidden();\n\t\t\t_cbScreen.Items.Add(\"screen.ofMouse\");\n\t\t\t\n\t\t\tcbKind.SelectionChanged += (_, _) => {\n\t\t\t\t_kind = cbKind.SelectedIndex;\n\t\t\t\tcc.Content = _acb[_kind];\n\t\t\t\tvar vis = _kind >= 2 ? Visibility.Visible : Visibility.Hidden;\n\t\t\t\ttHelp1.Visibility = vis; //don't need help for click and wheel enums\n\t\t\t\t_cbScreen.Visibility = vis;\n\t\t\t};\n\t\t\t\n\t\t\tb.R.Add(_hkMod = new(true, true));\n\t\t\tb.R.StartStack<KGroupBox>(EdWpf.TextAndHelp<TMFlags>(\"<b>Flags</b>\"), true);\n\t\t\t_eFlags = new EnumUI<TMFlags>(b.Panel, items: [\n\t\t\t\t(TMFlags.ShareEvent, \"ShareEvent - let other apps receive the click/wheel event\"),\n\t\t\t\t(TMFlags.ButtonModUp, \"ButtonModUp - delay until the button and keys are released\"),\n\t\t\t\t(TMFlags.LeftMod, \"LeftMod - left-side modifier keys\"),\n\t\t\t\t(TMFlags.RightMod, \"RightMod - right-side modifier keys\"),\n\t\t\t]);\n\t\t\tb.End();\n\t\t\t\n\t\t\tb.R.Add(out _cAlwaysEnabled, \"Always enabled\").Span(2);\n\t\t}\n\t\t\n\t\tpublic string FormatCode(string actionCode, string ttVar) {\n\t\t\tStringBuilder b = new($\"{ttVar}[TM\");\n\t\t\tif (_kind < 0) {\n\t\t\t\tb.Append(\"`|`\");\n\t\t\t} else {\n\t\t\t\tvar kind = _eKind.Result;\n\t\t\t\tb.Append(kind).Append('.');\n\t\t\t\tvar s = _acb[_kind].Text;\n\t\t\t\tb.Append(s.NE() ? \"`|`\" : s);\n\t\t\t}\n\t\t\t\n\t\t\tif (_hkMod.Result is string sMod) b.Append(\", \\\"\").Append(sMod).Append('\"');\n\t\t\t\n\t\t\tvar flags = _eFlags.Result;\n\t\t\tif (flags.Has(TMFlags.LeftMod | TMFlags.RightMod)) flags &= ~(TMFlags.LeftMod | TMFlags.RightMod);\n\t\t\tif (flags != 0) b.Append(\", flags: \").Append(TUtil.FormatFlags(flags));\n\t\t\t\n\t\t\tif (_kind >= 2 && _cbScreen.Result(true) is string sScreen) b.Append(\", screen: \").Append(sScreen);\n\t\t\t\n\t\t\tb.Append($$\"\"\"] = o => {{actionCode}};\"\"\");\n\t\t\tif (_cAlwaysEnabled.IsChecked) b.Append($\"\\r\\n{ttVar}.Last.EnabledAlways = true;\");\n\t\t\treturn b.ToString();\n\t\t}\n\t}\n\t\n\tclass _WindowTriggerSettingsPage : UserControl {\n\t\tListBox _lbEvent, _lbWhen;\n\t\tKCheckBox _cAtStartup, _cAlwaysEnabled;\n\t\tPanel _pLater;\n\t\tEnumUI<TWLater> _later;\n\t\t\n\t\tpublic _WindowTriggerSettingsPage() {\n\t\t\tvar b = new wpfBuilder(this).Columns(0, -1, 10, 0, -1);\n\t\t\t\n\t\t\tb.R.Add(\"Event\", out _lbEvent).Items(\"Window active|Window visible\");\n\t\t\tb.Skip().Add(\"When\", out _lbWhen).Items(\"New window|Once per window|Every time\").Select(0);\n\t\t\tb.R.Add(out _cAtStartup);\n\t\t\t_lbEvent.SelectionChanged += (o, e) => _cAtStartup.Content = $\"Run the action at startup if the window then is {(_lbEvent.SelectedIndex == 0 ? \"active\" : \"visible\")}\";\n\t\t\t_lbEvent.SelectedIndex = 0;\n\t\t\tb.R.Add(out _cAlwaysEnabled, \"Always enabled\");\n\t\t\t\n\t\t\tb.R.StartGrid<KGroupBox>(EdWpf.TextAndHelp<TWLater>(\"<b>Events later in that window</b>\")).Columns(0, 0, 0, -1);\n\t\t\t_later = new EnumUI<TWLater>(_pLater = b.Panel);\n\t\t\tb.End();\n\t\t\t\n\t\t\tb.End();\n\t\t}\n\t\t\n\t\tpublic string FormatCode(string wndFindArgs, string actionCode, string ttVar) {\n\t\t\tStringBuilder b = new($\"{ttVar}[TWEvent.\");\n\t\t\tb.Append(_lbEvent.SelectedIndex == 0 ? \"Active\" : \"Visible\").Append(_lbWhen.SelectedIndex switch { 0 => \"New\", 1 => \"Once\", _ => null });\n\t\t\tb.Append(\", \").Append(wndFindArgs);\n\t\t\tif (_cAtStartup.IsChecked) b.Append(\", flags: TWFlags.RunAtStartup\");\n\t\t\tvar later = _later.Result;\n\t\t\tif (later != 0) b.Append(\", later: \").Append(TUtil.FormatFlags(later));\n\t\t\tb.Append(\"] = o => \");\n\t\t\t\n\t\t\tif (later == 0) {\n\t\t\t\tb.Append(actionCode).Append(';');\n\t\t\t} else {\n\t\t\t\tb.Append($$\"\"\"\n{\n\tif (o.Later is 0) {\n\t\t{{actionCode.Trim(\" {};\")}};\n\t}\n\"\"\");\n\t\t\t\tforeach (CheckBox c in _pLater.Children) {\n\t\t\t\t\tif (c.IsChecked == true) b.Append($$\"\"\"\n else if (o.Later is TWLater.{{c.Content}}) {\n\t\tprint.it(\"later: {{c.Content}}\");\n\t}\n\"\"\");\n\t\t\t\t}\n\t\t\t\tb.Append(\"\\r\\n};\");\n\t\t\t}\n\t\t\t\n\t\t\tif (_cAlwaysEnabled.IsChecked) b.Append($\"\\r\\n{ttVar}.Last.EnabledAlways = true;\");\n\t\t\treturn b.ToString();\n\t\t}\n\t}\n\t\n\tstatic bool _TriggerScopeDialog(out string result, wnd wnd) {\n\t\tresult = null;\n\t\t\n\t\tvar tt = _CodeAnalysis.GetTriggersType();\n\t\tif (tt == TriggersType.Window) {\n\t\t\tdialog.showInfo(\"Trigger scope\", \"Scopes are not used with window triggers. Only with hotkey, autotext and mouse triggers.\");\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\tvar w = new KDialogWindow() { Title = \"Trigger scope\", Topmost = true };\n\t\tvar b = new wpfBuilder(w).WinSize(500, 350).Columns(0, 0, -1);\n\t\tb.R.Add(out DPwnd pwnd).Margin(\"L T R B6\");\n\t\tList<string> a = new();\n\t\tb.R.AddButton(\"Add more windows\", _ => {\n\t\t\tif (pwnd.HasResult) a.Add(pwnd.AaResultCode);\n\t\t\tpwnd.Clear();\n\t\t}).Tooltip(\"Add another window to the scope\");\n\t\tb.Add(out KCheckBox cNot, \"Not\").Tooltip(\"Triggers will NOT work when the window is active\");\n\t\tb.StartOkCancel();\n\t\tif (tt == 0) {\n\t\t\tb.Add(\"File\", out ComboBox cbOpen).Width(80).Items(\"|Hotkey|Autotext|Mouse\");\n\t\t\tcbOpen.SelectionChanged += (_, _) => {\n\t\t\t\tint i = cbOpen.SelectedIndex;\n\t\t\t\tif (i > 0) {\n\t\t\t\t\tTriggersAndToolbars.Edit($@\"Triggers\\{(TriggersType)i} triggers.cs\");\n\t\t\t\t\tb.Window.Hwnd().ActivateL();\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t\tb.AddOkCancel();\n\t\tb.End();\n\t\tb.Row(-1).Add(out KSciInfoBox info).Margin(\"T6\");\n\t\tinfo.aaaText = \"\"\"\nThis tool will add a new <+recipe>trigger scope<> statement in code. Triggers defined below it will work only when the window is active.\n\nPlease make sure the text cursor is in correct place. The new scope statement will be inserted there. Or you can move it afterwards.\n\"\"\";\n\t\tb.End();\n\t\t\n\t\tif (!wnd.Is0) pwnd.SetWnd(wnd);\n\t\t\n\t\tif (!w.ShowAndWait()) return false;\n\t\t\n\t\tvar triggersVar = _CodeAnalysis.GetActionTriggersVar();\n\t\tvar s = pwnd.HasResult ? pwnd.AaResultCode : null;\n\t\tif (s == null && a.Count == 0) {\n\t\t\tresult = $\"{triggersVar}.Of.AllWindows();\";\n\t\t} else {\n\t\t\tif (a.Count > 0) {\n\t\t\t\tif (s != null) a.Add(s);\n\t\t\t\ta = a.Distinct().ToList();\n\t\t\t\tif (a.Count == 1) { s = a[0]; a.Clear(); }\n\t\t\t}\n\t\t\tStringBuilder f = new($\"{triggersVar}.Of.\");\n\t\t\tif (cNot.IsChecked) f.Append(\"Not\");\n\t\t\tif (a.Count > 0) {\n\t\t\t\tf.Append(\"Windows([\");\n\t\t\t\tfor (int i = 0; i < a.Count; i++) {\n\t\t\t\t\tf.Append(\"\\r\\n\\t\").Append(\"new(\").Append(a[i]).Append(\"),\");\n\t\t\t\t}\n\t\t\t\tf.Append(\"\\r\\n]);\");\n\t\t\t} else {\n\t\t\t\tf.Append(\"Window(\").Append(s).Append(\");\");\n\t\t\t}\n\t\t\tresult = f.ToString();\n\t\t}\n\t\treturn true;\n\t}\n\t\n\t/// <summary>\n\t/// Inserts code statement for a window trigger or trigger scope.\n\t/// </summary>\n\t/// <param name=\"w\"></param>\n\t/// <param name=\"action\">0 trigger tool, 1 window scope, 2 program scope, 3 scope tool.</param>\n\tpublic static void QuickWindowTrigger(wnd w, int action) {\n\t\tif (action == 0) {\n\t\t\tNewTrigger(TriggersType.Window, w);\n\t\t} else {\n\t\t\tstring s;\n\t\t\tif (action is 1 or 2) {\n\t\t\t\tvar triggersVar = _CodeAnalysis.GetActionTriggersVar();\n\t\t\t\tif (action == 1) s = $\"{triggersVar}.Of.Window({_WndFindArgs(w)});\";\n\t\t\t\telse s = $\"{triggersVar}.Of.Window(of: \\\"{w.ProgramName.Escape()}\\\");\";\n\t\t\t} else {\n\t\t\t\tif (!_TriggerScopeDialog(out s, w)) return;\n\t\t\t}\n\t\t\t_CodeAnalysis.MoveCaretForNewTriggerOrScope();\n\t\t\tInsertCode.Statements(new(s));\n\t\t}\n\t}\n\t\n\tstatic string _WndFindArgs(wnd w) {\n\t\tvar f = new TUtil.WindowFindCodeFormatter();\n\t\tf.RecordWindowFields(w, 0, false);\n\t\treturn TUtil.ArgsFromWndFindCode(f.Format());\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Triggers and toolbars/TT._CodeAnalysis.cs",
    "content": "extern alias CAW;\n\nusing Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp.Syntax;\nusing CAW::Microsoft.CodeAnalysis.Shared.Extensions;\n\nusing CAW::Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp;\nusing Microsoft.CodeAnalysis.Text;\nusing Microsoft.CodeAnalysis.Shared.Extensions;\nusing Microsoft.CodeAnalysis.CSharp.Extensions;\n\nusing Au.Controls;\nusing System.Windows;\nusing System.Windows.Controls;\n\nnamespace LA;\n\npartial class TriggersAndToolbars {\n#if DEBUG\n\tpublic static void Test() {\n\t\t//var tt = _CodeAnalysis.GetTriggersType();\n\t\t//print.it(tt);\n\t\t//if (tt == 0) return;\n\t\t//print.it(\"-----\");\n\t\t//print.it(_CodeAnalysis.GetTriggersVar(tt));\n\t\t\n\t\t//print.it(_CodeAnalysis.GetActionTriggersVar());\n\t}\n#endif\n\t\n\tstatic class _CodeAnalysis {\n\t\t/// <summary>\n\t\t/// Called either to get triggers type (with tt 0) or triggers variable (with tt not 0). But always gets both, because not always can get just one.\n\t\t/// In current document looks for a variable of a triggers type. Or in current class looks for a field/property. Also can detect type if this is a standard triggers file.\n\t\t/// </summary>\n\t\t/// <returns>\n\t\t/// tType: if tt 0 - the detected type, or 0 if cannot detect; else tt.\n\t\t/// tVar: variable name (or like `triggers.Hotkey`), or null if tVar 0.\n\t\t/// onlyActionTriggers: true if tt 0 and found only an ActionTriggers variable.\n\t\t/// </returns>\n\t\tstatic (TriggersType tType, string tVar, bool onlyActionTriggers) _GetTriggersTypeOrVar(TriggersType tt) {\n\t\t\tif (!CodeInfo.GetContextAndDocument(out var cd, metaToo: true)) return default;\n\t\t\tvar semo = cd.semanticModel;\n\t\t\t\n\t\t\t//Find a local variable of a triggers type (like HotkeyTriggers).\n\t\t\t//\tEither nearest declaration, or a trigger like `variable[...]...`.\n\t\t\t//\tSearch upwards, enumerating sibling statements of current statement and its ancestors.\n\t\t\t//\tIf tt==0, find any type. Else find tt type.\n\t\t\t//\tIf not found, find an ActionTriggers variable declaration (for tVar like `triggers.Hotkey`).\n\t\t\t\n\t\t\tvar tok = cd.syntaxRoot.FindToken(cd.pos);\n\t\t\tif (tok.IsKind(SyntaxKind.EndOfFileToken)) tok = tok.GetPreviousToken();\n\t\t\tvar node = tok.Parent;\n\t\t\tif (node is MemberDeclarationSyntax and not GlobalStatementSyntax && cd.pos <= tok.SpanStart) node = (tok = tok.GetPreviousToken()).Parent;\n\t\t\tbool? andThis = null;\n\t\t\tstring typeName = tt == 0 ? null : $\"Au.Triggers.{tt}Triggers\", actionTriggersVar = null;\n\t\t\tif (node is BlockSyntax) {\n\t\t\t\tSyntaxNode n1 = null;\n\t\t\t\tif (node.Span.ContainsInside(cd.pos)) n1 = tok.IsKind(SyntaxKind.CloseBraceToken) ? node.ChildNodes().LastOrDefault() : node.ChildNodes().FirstOrDefault();\n\t\t\t\tnode = n1 ?? node.GetAncestor<StatementSyntax>();\n\t\t\t} else {\n\t\t\t\tnode = node?.GetAncestorOrThis<StatementSyntax>();\n\t\t\t\tandThis = node is ExpressionStatementSyntax && (node.SpanStart <= cd.pos || tok.GetPreviousToken().IsKind(SyntaxKind.OpenBraceToken));\n\t\t\t}\n\t\t\twhile (node != null) {\n\t\t\t\tforeach (var n in node.PreviousSiblings(andThis: andThis ?? node is ExpressionStatementSyntax)) {\n\t\t\t\t\t//CiUtil.PrintNode(n);\n\t\t\t\t\tif (n is LocalDeclarationStatementSyntax lds) {\n\t\t\t\t\t\tif (lds.Declaration.Variables.Count > 0 && _IsTriggersTypeVarNode(lds.Declaration.Type, out var varType)) {\n\t\t\t\t\t\t\tvar s = lds.Declaration.Variables[0].Identifier.Text;\n\t\t\t\t\t\t\tif (varType != 0) return (varType, s, false);\n\t\t\t\t\t\t\tactionTriggersVar ??= s;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (n is ExpressionStatementSyntax { Expression: AssignmentExpressionSyntax { RawKind: (int)SyntaxKind.SimpleAssignmentExpression } aes }) {\n\t\t\t\t\t\tif (aes.Left is ElementAccessExpressionSyntax ea && _IsTriggersTypeVarNode(ea.Expression, out var varType)) {\n\t\t\t\t\t\t\tif (varType != 0) return (varType, ea.Expression.ToString(), false);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tnode = node.Parent;\n\t\t\t\tif (node is BlockSyntax || node is not StatementSyntax) node = node?.GetAncestor<StatementSyntax>();\n\t\t\t\tandThis = null;\n\t\t\t\t\n\t\t\t\t//never mind: can be static func.\n\t\t\t\t//never mind: other kinds of local var declaration, eg out.\n\t\t\t\t\n\t\t\t\tbool _IsTriggersTypeVarNode(SyntaxNode varOrTypeExpr, out TriggersType varType) {\n\t\t\t\t\tif (semo.GetTypeInfo(varOrTypeExpr).Type is { } ty) {\n\t\t\t\t\t\t//print.it(ty, n);\n\t\t\t\t\t\treturn _IsTriggersType(ty, out varType);\n\t\t\t\t\t}\n\t\t\t\t\tvarType = 0;\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t//If standard triggers file, use the filename, and skip the \"find field/property\" code.\n\t\t\tif (tt == 0 || actionTriggersVar == null) {\n\t\t\t\tif (_IsStdTriggersFile(cd.sci.EFile) is var tts && tts != 0) {\n\t\t\t\t\tif (tt == 0) tt = tts;\n\t\t\t\t\tactionTriggersVar ??= \"Triggers\";\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t//Find a field or property.\n\t\t\tactionTriggersVar ??= _FindActionTriggersPropertyOrField(semo, cd.pos);\n\t\t\t\n\t\t\treturn (\n\t\t\t\ttt,\n\t\t\t\ttt == 0 ? null : (actionTriggersVar ?? \"Triggers\") + \".\" + tt,\n\t\t\t\ttt == 0 && actionTriggersVar != null\n\t\t\t\t);\n\t\t\t\n\t\t\tbool _IsTriggersType(ITypeSymbol ty, out TriggersType varType) {\n\t\t\t\tvarType = 0;\n\t\t\t\tvar s = ty.ToString();\n\t\t\t\tif (s.Like(\"Au.Triggers.*Triggers\")) {\n\t\t\t\t\tif (tt == 0) {\n\t\t\t\t\t\tswitch (s[12..^8]) {\n\t\t\t\t\t\tcase \"Hotkey\": varType = TriggersType.Hotkey; return true;\n\t\t\t\t\t\tcase \"Autotext\": varType = TriggersType.Autotext; return true;\n\t\t\t\t\t\tcase \"Mouse\": varType = TriggersType.Mouse; return true;\n\t\t\t\t\t\tcase \"Window\": varType = TriggersType.Window; return true;\n\t\t\t\t\t\tcase \"Action\": return true;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (s == typeName) { varType = tt; return true; }\n\t\t\t\t\t\tif (s.Eq(12..^8, \"Action\")) return true;\n\t\t\t\t\t}\n\t\t\t\t} else if (s == \"Au.Triggers.TASimpleReplace\") {\n\t\t\t\t\tif (tt == 0) { varType = TriggersType.Autotext; return true; } //note: the variable will be incorrect, but we need only type\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\t\n\t\tstatic TriggersType _IsStdTriggersFile(FileNode fn) {\n\t\t\tif (fn?.IsClass == true) {\n\t\t\t\tint i = fn.Name.Eq(true, \"Hotkey triggers.cs\", \"Autotext triggers.cs\", \"Mouse triggers.cs\", \"Window triggers.cs\");\n\t\t\t\tif (i > 0 && fn.ItemPath.Eqi(@\"\\@Triggers and toolbars\\Triggers\\\" + fn.Name)) return (TriggersType)i;\n\t\t\t}\n\t\t\treturn 0;\n\t\t}\n\t\t\n\t\tstatic string _FindActionTriggersPropertyOrField(SemanticModel semo, int pos) {\n\t\t\treturn CiUtil.EnumPropertiesAndFields(semo, pos).FirstOrDefault(o => o.GetMemberType().ToString() == \"Au.Triggers.ActionTriggers\")?.Name;\n\t\t}\n\t\t\n\t\tpublic static TriggersType GetTriggersType() {\n\t\t\treturn _GetTriggersTypeOrVar(0).tType;\n\t\t}\n\t\t\n\t\tpublic static string GetTriggersVar(TriggersType tt) {\n\t\t\tDebug.Assert(tt != 0);\n\t\t\treturn _GetTriggersTypeOrVar(tt).tVar ?? \"Triggers.\" + tt;\n\t\t}\n\t\t\n\t\tpublic static string GetActionTriggersVar() {\n\t\t\tif (CodeInfo.GetContextAndDocument(out var cd)) {\n\t\t\t\tvar semo = cd.semanticModel;\n\t\t\t\tif (CiUtil.GetLocalVariablesAt(semo, cd.pos, o => o.ToString() == \"Au.Triggers.ActionTriggers\").FirstOrDefault() is { } localSym) return localSym.Name;\n\t\t\t\tif (_FindActionTriggersPropertyOrField(semo, cd.pos) is { } pf) return pf;\n\t\t\t}\n\t\t\treturn \"Triggers\";\n\t\t}\n\t\t\n\t\tpublic static bool IsNonStandardFileWithTriggers() {\n\t\t\tif (App.Model.CurrentFile is not { } f) return false;\n\t\t\tif (_IsStdTriggersFile(f) != 0) return false;\n\t\t\tvar v = _GetTriggersTypeOrVar(0);\n\t\t\treturn v.tType != 0 || v.onlyActionTriggers;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// If current document is a standard triggers file, moves caret to a place where a new trigger can be inserted (if need).\n\t\t/// </summary>\n\t\t/// <returns>true if caret was in a correct place or if moved. false if not a standard triggers file.</returns>\n\t\tpublic static bool MoveCaretForNewTriggerOrScope() {\n\t\t\tif (!CodeInfo.GetContextAndDocument(out var cd, metaToo: true)) return false;\n\t\t\tvar semo = cd.semanticModel;\n\t\t\t\n\t\t\tvar programSym = semo.Compilation.GlobalNamespace.GetTypeMembers(\"Program\").FirstOrDefault();\n\t\t\tvar attrSym = programSym?.GetTypeMembers(\"TriggersAttribute\").FirstOrDefault();\n\t\t\tif (attrSym == null) return false;\n\t\t\t\n\t\t\tvar m = semo.GetEnclosingSymbol<IMethodSymbol>(cd.pos, default);\n\t\t\twhile (m != null && !m.IsOrdinaryMethod()) m = m.ContainingSymbol as IMethodSymbol;\n\t\t\t\n\t\t\tif (m != null && _HasTriggersAttribute(m)) return true;\n\t\t\tm = programSym.GetMembers().OfType<IMethodSymbol>().FirstOrDefault(o => o.Locations[0].SourceTree == semo.SyntaxTree && _HasTriggersAttribute(o));\n\t\t\tif (m == null) return false; //this file has no methods with [Triggers]\n\t\t\t\n\t\t\tif (m.Locations[0].FindNode(default) is MethodDeclarationSyntax md) {\n\t\t\t\tvar last = md.Body.Statements.OfType<ExpressionStatementSyntax>().LastOrDefault(o => o.Expression is AssignmentExpressionSyntax { Left: ElementAccessExpressionSyntax eae });\n\t\t\t\tif (last != null) cd.pos = last.FullSpan.End;\n\t\t\t\telse if (md.Body.Statements.OfType<IfStatementSyntax>().FirstOrDefault() is { } firstIf) cd.pos = firstIf.FullSpan.Start;\n\t\t\t\telse cd.pos = md.Body.CloseBraceToken.SpanStart;\n\t\t\t\t\n\t\t\t\tcd.sci.aaaGoToPos(true, cd.pos);\n\t\t\t}\n\t\t\t\n\t\t\treturn true;\n\t\t\t\n\t\t\tbool _HasTriggersAttribute(IMethodSymbol m) => m.GetAttributes().Any(o => o.AttributeClass == attrSym);\n\t\t}\n\t\t\n\t}\n\t\n\tpublic record struct FoundTrigger(FileNode file, int pos, string type, string trigger, string scope);\n\t\n\tpublic static async Task<List<FoundTrigger>> FindTriggersInCodeAsync(FileNode thisFile) {\n\t\tif (thisFile?.IsCodeFile != true) return null;\n\t\t\n\t\tvar ttFolder = GetProject(create: false);\n\t\tif (ttFolder == null) return null;\n\t\t\n\t\tList<FoundTrigger> aResults = new();\n\t\tstring nameCs = thisFile.Name, nameNoCs = thisFile.DisplayName, parentDir = thisFile.ItemPath[..^nameCs.Length];\n\t\t\n\t\tusing var ws = new CiWorkspace(ttFolder, CiWorkspace.Caller.OtherPR);\n\t\tawait Task.Run(_Work);\n\t\treturn aResults;\n\t\t\n\t\tvoid _Work() {\n\t\t\tvar comp = ws.GetCompilation();\n\t\t\t\n\t\t\tINamedTypeSymbol\n\t\t\t\tntHotkey = comp.GetTypeByMetadataName(\"Au.Triggers.HotkeyTriggers\"),\n\t\t\t\tntAutotext = comp.GetTypeByMetadataName(\"Au.Triggers.AutotextTriggers\"),\n\t\t\t\tntMouse = comp.GetTypeByMetadataName(\"Au.Triggers.MouseTriggers\"),\n\t\t\t\tntWindow = comp.GetTypeByMetadataName(\"Au.Triggers.WindowTriggers\"),\n\t\t\t\tntToolbar = comp.GetTypeByMetadataName(\"Au.toolbar\"),\n\t\t\t\tntMenu = comp.GetTypeByMetadataName(\"Au.popupMenu\"),\n\t\t\t\tntTriggerArgs = comp.GetTypeByMetadataName(\"Au.Triggers.TriggerArgs\"),\n\t\t\t\tntMTItem = comp.GetTypeByMetadataName(\"Au.Types.MTItem\"),\n\t\t\t\tntScopes = comp.GetTypeByMetadataName(\"Au.Triggers.TriggerScopes\");\n\t\t\t\n\t\t\tINamedTypeSymbol ntTriggersAttribute = null, ntToolbarsAttribute = null;\n\t\t\tif (comp.GlobalNamespace.GetTypeMembers(\"Program\").FirstOrDefault() is { } symProgram) {\n\t\t\t\tntTriggersAttribute = symProgram.GetTypeMembers(\"TriggersAttribute\").FirstOrDefault();\n\t\t\t\tntToolbarsAttribute = symProgram.GetTypeMembers(\"ToolbarsAttribute\").FirstOrDefault();\n\t\t\t}\n\t\t\t\n\t\t\t//var files = ws.Meta.CodeFiles.Zip(comp.SyntaxTrees, (cf, tree) => (cf: cf, tree: tree, semo: comp.GetSemanticModel(tree))).ToArray();\n\t\t\tvar files = ws.Meta.CodeFiles.Zip(comp.SyntaxTrees, (f, tree) => (cf: f, tree: tree, semo: f.isC ? null : comp.GetSemanticModel(tree))).Where(o => !o.cf.isC).ToArray(); //skip meta c and global.cs\n\t\t\t\n\t\t\tforeach (var (cf, tree, semo) in files) {\n\t\t\t\t//print.it($\"<><lc greenyellow>{cf.f}<>\");\n\t\t\t\tvar root = tree.GetCompilationUnitRoot();\n\t\t\t\t\n\t\t\t\t//look for strings equal to the script name or path\n\t\t\t\tforeach (var tok in root.DescendantTokens()) {\n\t\t\t\t\tvar tk = tok.Kind();\n\t\t\t\t\tif (tk is SyntaxKind.StringLiteralToken or SyntaxKind.SingleLineRawStringLiteralToken or SyntaxKind.InterpolatedStringTextToken) {\n\t\t\t\t\t\tvar s = tok.ValueText;\n\t\t\t\t\t\tstring sn;\n\t\t\t\t\t\tif (s.Ends(sn = nameCs, true) || s.Ends(sn = nameNoCs, true)) {\n\t\t\t\t\t\t\tif (s.Length - sn.Length is int j && j > 0) if (j != parentDir.Length || !s.Starts(parentDir, true)) continue;\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tint found = 0;\n\t\t\t\t\t\t\t_GetTrigger(tok.Parent, semo, cf, 0, false, ref found);\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t//if trigger not found, add as string\n\t\t\t\t\t\t\tif (found == 0) {\n\t\t\t\t\t\t\t\tvar n = tok.Parent; if (tk == SyntaxKind.InterpolatedStringTextToken) n = n.Parent;\n\t\t\t\t\t\t\t\tif (n.GetAncestors().FirstOrDefault(o => o is StatementSyntax or ArrowExpressionClauseSyntax) is { } ss) {\n\t\t\t\t\t\t\t\t\t//var scope = semo.GetEnclosingSymbol(ss.SpanStart)?.Name; //empty if lambda\n\t\t\t\t\t\t\t\t\tvar scope = ss.GetAncestor<MemberDeclarationSyntax>() switch { MethodDeclarationSyntax k => k.Identifier.Text, PropertyDeclarationSyntax k => k.Identifier.Text, EventDeclarationSyntax k => k.Identifier.Text, _ => null };\n\t\t\t\t\t\t\t\t\taResults.Add(new(cf.f, n.SpanStart, null, ss.ToString(), scope));\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t//If node is in a trigger action, finds the trigger statement and gets the triggers type.\n\t\t\t//If found, adds to the _FindTriggerResult list.\n\t\t\tvoid _GetTrigger(SyntaxNode node, SemanticModel semo, MCCodeFile cf, int level, bool inFuncOfTriggersActionType, ref int result) {\n\t\t\t\tfor (var n = node; n != null; n = n.Parent) {\n\t\t\t\t\tif (n is LocalFunctionStatementSyntax) {\n\t\t\t\t\t\tif (level < 5 && semo.GetDeclaredSymbol(n) is IMethodSymbol ims && n.GetAncestor<BlockSyntax>() is { } scope) {\n\t\t\t\t\t\t\t_FuncReferences(ims, scope, semo, cf, level, ref result);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t} else if (n is MethodDeclarationSyntax) {\n\t\t\t\t\t\tif (level < 5 && semo.GetDeclaredSymbol(n) is IMethodSymbol ims) {\n\t\t\t\t\t\t\tif (ntTriggersAttribute != null && ims.GetAttributes().Any(o => o.AttributeClass == ntTriggersAttribute || o.AttributeClass == ntToolbarsAttribute)) break;\n\t\t\t\t\t\t\tforeach (var v in files) {\n\t\t\t\t\t\t\t\t_FuncReferences(ims, v.tree.GetCompilationUnitRoot(), v.semo, v.cf, level, ref result);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t} else if (n is AnonymousFunctionExpressionSyntax) {\n\t\t\t\t\t\tinFuncOfTriggersActionType = _IsFuncOfTriggersActionType(semo.GetSymbolInfo(n).Symbol as IMethodSymbol);\n\t\t\t\t\t} else if (!inFuncOfTriggersActionType) {\n\t\t\t\t\t\t//is it a toolbar/menu label?\n\t\t\t\t\t\tbool isLabel = n is BracketedArgumentListSyntax;\n\t\t\t\t\t\tif (!isLabel && n is InvocationExpressionSyntax { Expression: MemberAccessExpressionSyntax maes }) {\n\t\t\t\t\t\t\t//probably `script.run(\"theString\")`, but can be eg `toolbarOrMenu.Add(\"theString\")`\n\t\t\t\t\t\t\tvar ty = semo.GetTypeInfo(maes.Expression).Type;\n\t\t\t\t\t\t\tisLabel = ty == ntToolbar || ty == ntMenu || ty == ntHotkey || ty == ntAutotext || ty == ntMouse || ty == ntWindow;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (isLabel) { result |= 2; break; }\n\t\t\t\t\t} else if (n is InvocationExpressionSyntax or AssignmentExpressionSyntax { Left: ElementAccessExpressionSyntax }) {\n\t\t\t\t\t\tExpressionSyntax expr = null;\n\t\t\t\t\t\tBaseArgumentListSyntax arglist = null;\n\t\t\t\t\t\tif (n is AssignmentExpressionSyntax { Left: ElementAccessExpressionSyntax eaes }) { //`t[...] = ...`\n\t\t\t\t\t\t\texpr = eaes.Expression;\n\t\t\t\t\t\t\targlist = eaes.ArgumentList;\n\t\t\t\t\t\t} else if (n is InvocationExpressionSyntax { Expression: MemberAccessExpressionSyntax maes } ies) { //maybe `t.Add(...)` or `t.Menu(...)`\n\t\t\t\t\t\t\texpr = maes.Expression;\n\t\t\t\t\t\t\targlist = ies.ArgumentList;\n\t\t\t\t\t\t} else continue;\n\t\t\t\t\t\t\n\t\t\t\t\t\tif (semo.GetTypeInfo(expr).Type is { } ty) {\n\t\t\t\t\t\t\tstring triggersType = null;\n\t\t\t\t\t\t\tif (ty == ntHotkey || ty == ntAutotext || ty == ntMouse || ty == ntWindow) {\n\t\t\t\t\t\t\t\ttriggersType = ty.Name[..^8];\n\t\t\t\t\t\t\t} else if (ty == ntToolbar) {\n\t\t\t\t\t\t\t\ttriggersType = \"Toolbar\";\n\t\t\t\t\t\t\t} else if (ty == ntMenu) { //a submenu of a toolbar?\n\t\t\t\t\t\t\t\tforeach (var v in n.GetAncestors<InvocationExpressionSyntax>()) {\n\t\t\t\t\t\t\t\t\tif (v.Expression is MemberAccessExpressionSyntax maes && semo.GetTypeInfo(maes.Expression).Type is { } ty2) {\n\t\t\t\t\t\t\t\t\t\tif (ty2 == ntToolbar) { triggersType = \"Toolbar\"; break; }\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tif (triggersType != null) {\n\t\t\t\t\t\t\t\tvar args = arglist.Arguments;\n\t\t\t\t\t\t\t\tstring arguments = null, scope = null;\n\t\t\t\t\t\t\t\tif (ty == ntToolbar || ty == ntMenu) {\n\t\t\t\t\t\t\t\t\tif (n.GetAncestor<MethodDeclarationSyntax>() is { } mds) arguments = mds.Identifier.Text;\n\t\t\t\t\t\t\t\t\telse arguments = args[0].ToString();\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\targuments = args.ToString();\n\t\t\t\t\t\t\t\t\tif (ty != ntWindow) scope = _GetScope(n, semo);\n\t\t\t\t\t\t\t\t\t//funcOf = _GetFuncOf(n, semo);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\taResults.Add(new(cf.f, args[0].SpanStart, triggersType, arguments, scope));\n\t\t\t\t\t\t\t\tresult |= 1;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tvoid _FuncReferences(IMethodSymbol ims, SyntaxNode scope, SemanticModel semo, MCCodeFile cf, int level, ref int found) {\n\t\t\t\tvar range = scope.Span;\n\t\t\t\tstring name = ims.Name, code = cf.code;\n\t\t\t\tbool? isOfTriggersActionType = null;\n\t\t\t\tfor (int i = range.Start; ; i += name.Length) {\n\t\t\t\t\ti = code.Find(name, i..range.End);\n\t\t\t\t\tif (i < 0) break;\n\t\t\t\t\tif (!SyntaxFacts.IsIdentifierPartCharacter(code.At_(i - 1)) && !SyntaxFacts.IsIdentifierPartCharacter(code.At_(i + name.Length))) {\n\t\t\t\t\t\tvar tok = scope.FindToken(i);\n\t\t\t\t\t\tif (tok.SpanStart != i || tok.Parent is not IdentifierNameSyntax) continue; //info: also skips the declaration, because it's not IdentifierNameSyntax\n\t\t\t\t\t\tvar sym2 = semo.GetSymbolInfo(tok.Parent).Symbol;\n\t\t\t\t\t\tif (sym2 != ims) continue;\n\t\t\t\t\t\tvar node = tok.Parent.Parent;\n\t\t\t\t\t\tif (node is InvocationExpressionSyntax or AssignmentExpressionSyntax or ArgumentSyntax) {\n\t\t\t\t\t\t\tbool tat = node is InvocationExpressionSyntax ? false : (isOfTriggersActionType ??= _IsFuncOfTriggersActionType(ims));\n\t\t\t\t\t\t\t_GetTrigger(node, semo, cf, level + 1, tat, ref found);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tbool _IsFuncOfTriggersActionType(IMethodSymbol ims) {\n\t\t\t\tif (ims != null && ims.ReturnsVoid && ims.Parameters.Length == 1 && !ims.IsGenericMethod && ims.Parameters[0] is { RefKind: RefKind.None, Type: { BaseType: var t2 } t1 }) {\n\t\t\t\t\tif (t2 == ntTriggerArgs || t1 == ntTriggerArgs || t2 == ntMTItem || t1 == ntMTItem) return true;\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t\n\t\t\tstring _GetScope(SyntaxNode node, SemanticModel semo) {\n\t\t\t\tforeach (var stat in _EnumStatementsUp(node)) {\n\t\t\t\t\tif (stat is ExpressionStatementSyntax { Expression: InvocationExpressionSyntax { Expression: MemberAccessExpressionSyntax maes } ies }) {\n\t\t\t\t\t\tif (maes.Name.Identifier.Text is string s1 && s1 is \"Window\" or \"Windows\" or \"Again\" or \"AllWindows\") {\n\t\t\t\t\t\t\tif (semo.GetTypeInfo(maes.Expression).Type == ntScopes) {\n\t\t\t\t\t\t\t\tif (s1 == \"AllWindows\") return null;\n\t\t\t\t\t\t\t\treturn s1 + ies.ArgumentList;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\t\n\t\t\t//rejected. Need much more code to detect whether NextTrigger would be applied to this trigger. Can be unreliable.\n\t\t\t//string _GetFuncOf(SyntaxNode node, SemanticModel semo) {\n\t\t\t//\tforeach (var stat in _EnumStatementsUp(node)) {\n\t\t\t//\t\tif (stat is ExpressionStatementSyntax { Expression: AssignmentExpressionSyntax { Left: MemberAccessExpressionSyntax maes } aes }) {\n\t\t\t//\t\t\tif (maes.Name.Identifier.Text is string s1 && s1 is \"FollowingTriggers\" or \"FollowingTriggersBeforeWindow\" or \"NextTrigger\" or \"NextTriggerBeforeWindow\") {\n\t\t\t//\t\t\t\tif (semo.GetTypeInfo(maes.Expression).Type == ntFuncs) {\n\t\t\t//\t\t\t\t\tif (aes.Right is LiteralExpressionSyntax) return null; // `= null`\n\t\t\t//\t\t\t\t\treturn aes.Right.ToString();\n\t\t\t//\t\t\t\t}\n\t\t\t//\t\t\t}\n\t\t\t//\t\t}\n\t\t\t//\t}\n\t\t\t//\treturn null;\n\t\t\t//}\n\t\t\t\n\t\t\tIEnumerable<StatementSyntax> _EnumStatementsUp(SyntaxNode n) {\n\t\t\t\twhile (n.GetAncestorOrThis<StatementSyntax>() is { } stat) {\n\t\t\t\t\tforeach (var v in stat.PreviousSiblings()) {\n\t\t\t\t\t\tif (v is StatementSyntax ss) {\n\t\t\t\t\t\t\tyield return ss;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tDebug_.Print(v);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tn = stat.Parent;\n\t\t\t\t\tif (n is BlockSyntax { Parent: not BlockSyntax }) n = n.Parent;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Returns true if <i>f</i> is <c>@\"\\@Triggers and toolbars\\Triggers and toolbars.cs\"</c>.\n\t/// </summary>\n\tpublic static bool IsTtScipt(FileNode f) {\n\t\treturn f != null && f.IsCodeFile && f.Name.Eqi(\"Triggers and toolbars.cs\") && f.Parent.Name.Eqi(\"@Triggers and toolbars\") && f.Parent.Parent.Parent == null;\n\t}\n\t\n\tpublic static async Task<List<FoundTrigger>> FindAllTriggersAsync(FileNode thisFile) {\n\t\tif (!thisFile.IsExecutableDirectly()) return null;\n\t\t\n\t\t//action triggers\n\t\tvar ar = await FindTriggersInCodeAsync(thisFile) ?? new();\n\t\t\n\t\t//scheduler\n\t\tif (await WinScheduler.GetScriptTriggersAsync(thisFile) is { } ast) {\n\t\t\tforeach (var v in ast) {\n\t\t\t\tar.Add(new(null, -1, \"Scheduled\", v.trigger, v.task));\n\t\t\t}\n\t\t}\n\t\t\n\t\t//startup script\n\t\tforeach (var v in App.Model.GetStartupScriptsExceptDisabled()) {\n\t\t\tif (v.f == thisFile) {\n\t\t\t\tar.Add(new(null, -1, \"Startup\", \"When this workspace loaded\", null));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\t\n\t\t//test script\n\t\tforeach (var v in App.Model.Root.Descendants()) {\n\t\t\tif (v.TestScript == thisFile) {\n\t\t\t\tar.Add(new(v, -1, \"Test script\", v.Name, null));\n\t\t\t}\n\t\t}\n\t\t\n\t\t//rejected: preBuid/postBuild, shortcut\n\t\t\n\t\treturn ar;\n\t}\n\t\n\tpublic static async void AllTriggersMenu(FileNode thisFile) {\n\t\tif (thisFile == null) return;\n\t\t//if (thisFile.IsClass) thisFile = thisFile.GetProjectMainOrThis();\n\t\tvar a = await FindAllTriggersAsync(thisFile);\n\t\t\n\t\tvar p = new KPopupListBox { Placement = System.Windows.Controls.Primitives.PlacementMode.Mouse, PlacementTarget = App.Wmain };\n\t\tp.OK += o => {\n\t\t\tif (o is ListBoxItem { Tag: Action click }) click();\n\t\t};\n\t\t\n\t\tvoid _Add(string s, Action click = null) {\n\t\t\tvar li = new ListBoxItem { Content = s };\n\t\t\tif (click != null) li.Tag = click; else li.IsEnabled = false;\n\t\t\tp.Control.Items.Add(li);\n\t\t}\n\t\t\n\t\tif (a == null) {\n\t\t\t_Add(\"This file isn't runnable as a script\");\n\t\t} else if (a.Count == 0) {\n\t\t\t_Add(\"No triggers found\");\n\t\t} else {\n\t\t\tint schedTrigger = 0;\n\t\t\tforeach (var v in a) {\n\t\t\t\tstatic string _Limit(string s) {\n\t\t\t\t\tif (s != null) {\n\t\t\t\t\t\ts = s.Replace(\"\\t\", \"    \");\n\t\t\t\t\t\t//s = string.Join('\\n', s.Lines(noEmpty: true).Select(o => o.Limit(200))); //don't need. WPF limits the window width to the work area and adds hscrollbar.\n\t\t\t\t\t}\n\t\t\t\t\treturn s;\n\t\t\t\t}\n\t\t\t\tstring trigger = _Limit(v.trigger), scope = _Limit(v.scope);\n\t\t\t\tif (v.file != null) {\n\t\t\t\t\tif (v.type == \"Test script\") {\n\t\t\t\t\t\t_Add($\"{v.type} of {trigger}\", () => App.Model.OpenAndGoTo(v.file));\n\t\t\t\t\t} else if (v.type == null) {\n\t\t\t\t\t\t_Add($\"Found in {scope}:\\n    {trigger}\", () => App.Model.OpenAndGoTo(v.file, columnOrPos: v.pos));\n\t\t\t\t\t} else {\n\t\t\t\t\t\tvar s = $\"{v.type} {trigger}\";\n\t\t\t\t\t\tif (scope != null) s += \"\\n    Scope: \" + scope;\n\t\t\t\t\t\t_Add(s, () => App.Model.OpenAndGoTo(v.file, columnOrPos: v.pos));\n\t\t\t\t\t}\n\t\t\t\t} else if (v.type == \"Scheduled\") {\n\t\t\t\t\tint i = ++schedTrigger;\n\t\t\t\t\t_Add(trigger, () => DSchedule.ShowFor(thisFile, i));\n\t\t\t\t} else if (v.type == \"Startup\") {\n\t\t\t\t\t_Add(trigger, () => DOptions.AaShow(DOptions.EPage.Workspace));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tp.IsOpen = true;\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Triggers and toolbars/TriggersAndToolbars.cs",
    "content": "using System.Xml.Linq;\nusing Au.Triggers;\n\nnamespace LA;\n\npartial class TriggersAndToolbars {\n\tpublic static bool Edit(string file) {\n\t\tvar f = _GetFile(file, create: true);\n\t\tif (f == null) return false;\n\t\treturn App.Model.OpenAndGoTo(f);\n\t}\n\t\n\tpublic static void GoToToolbars() {\n\t\tvar folder = GetProject(create: true);\n\t\tfolder = folder.Children().First(f => f.IsFolder && f.Name.Eqi(\"Toolbars\"));\n\t\t\n\t\tfolder.SelectSingle();\n\t\tvar tv = Panels.Files.TreeControl;\n\t\tif (!folder.IsExpanded) tv.Expand(folder, true);\n\t\ttv.EnsureVisible(folder, scrollTop: true);\n\t\t\n\t\t//var m = new popupMenu();\n\t\t//foreach (var f in folder.Children()) {\n\t\t//\tif (!f.IsClass) continue;\n\t\t//\tm[f.DisplayName] = _ => App.Model.OpenAndGoTo(f);\n\t\t//}\n\t\t//m.Show();\n\t}\n\t\n\t/// <summary>\n\t/// Finds or creates project @\"\\@Triggers and toolbars\".\n\t/// </summary>\n\t/// <param name=\"create\">Create if does not exist.</param>\n\t/// <returns>The project folder. Returns null if does not exist and <i>create</i> false.</returns>\n\tpublic static FileNode GetProject(bool create) {\n\t\tvar fProject = App.Model.Find(@\"\\@Triggers and toolbars\", FNFind.Folder);\n\t\tif (create) {\n\t\t\tif (fProject == null) {\n\t\t\t\tfProject = App.Model.NewItemL(s_templPath);\n\t\t\t\tprint.it(\"Info: folder \\\"@Triggers and toolbars\\\" has been created.\");\n\t\t\t} else { //create missing files. Note: don't cache, because files can be deleted at any time. Fast enough.\n\t\t\t\tvar xTempl = FileNode.Templates.LoadXml(s_templPath); //fast, does not load the xml file each time\n\t\t\t\t_Folder(xTempl, fProject);\n\t\t\t\tvoid _Folder(XElement xParent, FileNode fParent) {\n\t\t\t\t\tforeach (var x in xParent.Elements()) {\n\t\t\t\t\t\tbool isFolder = x.Name.LocalName == \"d\";\n\t\t\t\t\t\tstring name = x.Attr(\"n\");\n\t\t\t\t\t\tif (isFolder && (name == \"Scripts\" || name == \"Functions\")) continue;\n\t\t\t\t\t\tvar ff = fParent.Children().FirstOrDefault(o => o.Name.Eqi(name));\n\t\t\t\t\t\tif (ff == null) {\n\t\t\t\t\t\t\tff = App.Model.NewItemLX(x, new(fParent, FNInsert.Last));\n\t\t\t\t\t\t} else if (isFolder) {\n\t\t\t\t\t\t\t_Folder(x, ff);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t//set run at startup\n\t\t\tif (_GetFile(@\"Triggers and toolbars.cs\", create: false) is { } f) {\n\t\t\t\tif (App.Model.EnsureIsInStartupScripts(f, printAdded: false, usePath: true) is FilesModel.EISSResult.Added) {\n\t\t\t\t\tprint.it($\"<>Info: script \\\"{f.Name}\\\" has been added to <+options Workspace>Options > Workspace > Startup scripts<>. If unwanted, disable the line (prefix //).\"); //disable, not delete, else next time would auto-add again\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn fProject;\n\t}\n\tconst string s_templPath = @\"Default\\@Triggers and toolbars\";\n\t\n\tstatic FileNode _GetFile(string file, bool create, FNFind kind = FNFind.File) {\n\t\tvar f = GetProject(create: create);\n\t\treturn f?.FindRelative(false, file, kind);\n\t}\n\t\n\tpublic static void Restart() {\n\t\tvar f = _GetFile(@\"Triggers and toolbars.cs\", create: false);\n\t\tif (f != null) CompileRun.CompileAndRun(true, f, runFromEditor: true);\n\t}\n\t\n\t/// <summary>\n\t/// Disables, enables or toggles triggers in all processes. See <see cref=\"ActionTriggers.DisabledEverywhere\"/>.\n\t/// Also updates UI: changes tray icon and checks/unchecks the menu item.\n\t/// </summary>\n\t/// <param name=\"disable\">If null, toggles.</param>\n\tpublic static void DisableTriggers(bool? disable) {\n\t\tbool dis = disable switch { true => true, false => false, _ => !ActionTriggers.DisabledEverywhere };\n\t\tif (dis == ActionTriggers.DisabledEverywhere) return;\n\t\tActionTriggers.DisabledEverywhere = dis; //notifies us to update tray icon etc\n\t}\n\t\n\t//from ActionTriggers.DisabledEverywhere through our message-only window\n\tinternal static void OnDisableTriggers() {\n\t\tbool dis = ActionTriggers.DisabledEverywhere;\n\t\tApp.TrayIcon.Disabled = dis;\n\t\tif (App.Commands is { } ac) ac[nameof(Menus.TT.Disable_triggers)].Checked = dis;\n\t}\n\t\n\t//rejected\n\t//public static void ShowActiveTriggers() {\n\t//\tvar w = wnd.findFast(cn: \"Au.Triggers.Hooks\", messageOnly: true);\n\t//\tif (!w.Is0) w.Post(Api.WM_USER + 2, -1);\n\t//}\n\t\n\tpublic static void ShowActiveToolbars() {\n\t\tforeach (var w in wnd.getwnd.allWindows().Where(o => o.ClassNameIs(\"Au.toolbar\")).DistinctBy(o => o.ThreadId)) {\n\t\t\tApi.AllowSetForegroundWindow(w.ProcessId);\n\t\t\tw.Post(Api.WM_USER + 51);\n\t\t}\n\t}\n\t\n\tpublic static void NewToolbar() {\n\t\tvar tt = new TriggersAndToolbars();\n\t\ttt._NewToolbar();\n\t}\n\t\n\tpublic static void SetToolbarTrigger() {\n\t\tvar tt = new TriggersAndToolbars();\n\t\ttt._SetToolbarTrigger();\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/Triggers and toolbars/WinScheduler.cs",
    "content": "using System.Security.Principal;\nusing System.Xml.Linq;\n\nnamespace LA;\n\nusing api = WinSchedulerApi;\nusing TT = WinSchedulerApi.TASK_TRIGGER_TYPE2;\n\nstatic class WinScheduler {\n\tstatic string _SidCurrentUser => WindowsIdentity.GetCurrent().User.ToString();\n\t//static string _SddlCurrentUserReadExecute => \"D:AI(A;;FA;;;SY)(A;;FA;;;BA)(A;;GRGX;;;\" + _SidCurrentUser + \")\";\n\tstatic string _SddlCurrentUserReadExecute => \"D:AI(A;;FA;;;SY)(A;;FA;;;BA)(A;;FA;;;\" + _SidCurrentUser + \")\";\n\tstatic string c_sddlEveryoneReadExecute = \"D:AI(A;;FA;;;SY)(A;;FA;;;BA)(A;;GRGX;;;WD)\";\n\t\n\tinternal static bool Connect(out api.ITaskService ts) {\n\t\ttry {\n\t\t\tts = new api.TaskScheduler() as api.ITaskService;\n\t\t\treturn 0 == ts.Connect();\n\t\t}\n\t\tcatch { ts = null; return false; }\n\t}\n\t\n\t/// <summary>\n\t/// Creates or updates a trigerless task that executes a program as system, admin or user.\n\t/// Creates the folder (and ancestors) if does not exist.\n\t/// This process must be admin.\n\t/// You can use <see cref=\"RunTask\"/> to run the task.\n\t/// </summary>\n\t/// <param name=\"IL\">Can be System, High or Medium. If System, runs in SYSTEM account. Else in creator's account.</param>\n\t/// <param name=\"programFile\">Full path of an exe file. This function does not normalize it.</param>\n\t/// <param name=\"args\">Command line arguments. Can contain literal substrings $(Arg0), $(Arg1), ..., $(Arg32) that will be replaced by <see cref=\"RunTask\"/>.</param>\n\t/// <returns>HRESULT.</returns>\n\t/// <inheritdoc cref=\"RunTask\" path=\"/param\"/>\n\tpublic static int CreateTaskWithoutTriggers(string taskFolder, string taskName, UacIL IL, string programFile, string args = null, string author = \"Au\", api.TASK_CREATION creation = api.TASK_CREATION.TASK_CREATE) {\n\t\tvar xml = CreateXmlForNewTask(IL, programFile, args, author);\n\t\treturn CreateTaskFromXml(taskFolder, taskName, xml, IL, creation);\n\t}\n\t\n\t/// <summary>\n\t/// Creates or updates a task using XML.\n\t/// Creates the folder (and ancestors) if does not exist.\n\t/// This process must be admin.\n\t/// You can use <see cref=\"RunTask\"/> to run the task.\n\t/// </summary>\n\t/// <param name=\"IL\">Can be System, High or Medium. If System, runs in SYSTEM account. Else in creator's account.</param>\n\t/// <returns>HRESULT.</returns>\n\t/// <inheritdoc cref=\"RunTask\" path=\"/param\"/>\n\tpublic static int CreateTaskFromXml(string taskFolder, string taskName, string xml, UacIL IL, api.TASK_CREATION creation = api.TASK_CREATION.TASK_CREATE) {\n\t\treturn _CreateTask(taskFolder, taskName, xml, IL, creation);\n\t}\n\t\n\t/// <summary>\n\t/// Creates or updates a task using XML.\n\t/// Creates the folder (and ancestors) if does not exist.\n\t/// This process must be admin.\n\t/// You can use <see cref=\"RunTask\"/> to run the task.\n\t/// </summary>\n\t/// <param name=\"IL\">Can be System, High or Medium. If System, runs in SYSTEM account. Else in creator's account.</param>\n\t/// <returns>HRESULT.</returns>\n\t/// <inheritdoc cref=\"RunTask\" path=\"/param\"/>\n\tinternal static int CreateTaskFromDefinition(string taskFolder, string taskName, api.ITaskDefinition td, UacIL IL, api.TASK_CREATION creation = api.TASK_CREATION.TASK_CREATE) {\n\t\treturn _CreateTask(taskFolder, taskName, td, IL, creation);\n\t}\n\t\n\tstatic int _CreateTask(string taskFolder, string taskName, object tdOrXml, UacIL IL, api.TASK_CREATION creation = api.TASK_CREATION.TASK_CREATE) {\n\t\tif (!(IL is UacIL.High or UacIL.Medium or UacIL.System)) throw new ArgumentException();\n\t\tif (!Connect(out var ts)) return Api.E_FAIL;\n\t\tif (0 != ts.GetFolder(taskFolder, out var tf)) {\n\t\t\tint hr = ts.GetFolder(null, out tf);\n\t\t\tif (hr == 0) hr = tf.CreateFolder(taskFolder, c_sddlEveryoneReadExecute, out tf);\n\t\t\tif (hr != 0) return hr;\n\t\t} else if ((creation & api.TASK_CREATION.TASK_CREATE_OR_UPDATE) == api.TASK_CREATION.TASK_CREATE) {\n\t\t\ttf.DeleteTask(taskName, 0); //delete if exists. Note: TASK_CREATE_OR_UPDATE does not update task file's security.\n\t\t}\n\t\tvar logonType = IL == UacIL.System ? api.TASK_LOGON_TYPE.TASK_LOGON_SERVICE_ACCOUNT : api.TASK_LOGON_TYPE.TASK_LOGON_INTERACTIVE_TOKEN;\n\t\tvar sddl = IL == UacIL.System ? c_sddlEveryoneReadExecute : _SddlCurrentUserReadExecute;\n\t\tif (tdOrXml is api.ITaskDefinition td) {\n\t\t\treturn tf.RegisterTaskDefinition(taskName, td, creation, null, null, logonType, sddl, out _);\n\t\t} else {\n\t\t\treturn tf.RegisterTask(taskName, (string)tdOrXml, creation, null, null, logonType, sddl, out _);\n\t\t}\n\t\t\n\t\t//note: cannot create a task that runs only in current interactive session, regardless of user.\n\t\t//\tTried INTERACTIVE: userId \"S-1-5-4\", logonType TASK_LOGON_GROUP. But then runs in all logged in sessions.\n\t}\n\t\n\t/// <summary>\n\t/// Creates XML of a new trigerless task.\n\t/// </summary>\n\t/// <param name=\"IL\">Can be System, High or Medium. If System, runs in SYSTEM account. Else in creator's account.</param>\n\t/// <param name=\"programFile\">Full path of an exe file. This function does not normalize it.</param>\n\t/// <param name=\"args\">Command line arguments. Can contain literal substrings $(Arg0), $(Arg1), ..., $(Arg32) that will be replaced by <see cref=\"RunTask\"/>.</param>\n\t/// <param name=\"author\"></param>\n\tpublic static string CreateXmlForNewTask(UacIL IL, string programFile, string args = null, string author = \"Au\") {\n\t\tif (!(IL is UacIL.High or UacIL.Medium or UacIL.System)) throw new ArgumentException();\n\t\tvar userId = IL == UacIL.System ? \"<UserId>S-1-5-18</UserId>\\r\\n\" : null;\n\t\tvar runLevel = IL switch { UacIL.System => null, UacIL.High => \"<RunLevel>HighestAvailable</RunLevel>\", _ => \"<RunLevel>LeastPrivilege</RunLevel>\" };\n\t\treturn $\"\"\"\n<?xml version='1.0' encoding='UTF-16'?>\n<Task version='1.{(osVersion.minWin10 ? \"4\" : \"3\")}' xmlns='http://schemas.microsoft.com/windows/2004/02/mit/task'>\n\n<RegistrationInfo>\n<Author>{author}</Author>\n</RegistrationInfo>\n\n<Principals>\n<Principal id='Author'>\n{userId}{runLevel}\n</Principal>\n</Principals>\n\n<Settings>\n<MultipleInstancesPolicy>Parallel</MultipleInstancesPolicy>\n<DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>\n<StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>\n<ExecutionTimeLimit>PT0S</ExecutionTimeLimit>\n<Priority>5</Priority>\n</Settings>\n\n<Actions Context='Author'>\n<Exec>\n<Command>{programFile}</Command>\n<Arguments>{args}</Arguments>\n</Exec>\n</Actions>\n\n</Task>\n\"\"\";\n\t}\n\t\n\t/// <summary>\n\t/// Runs task. Does not wait.\n\t/// </summary>\n\t/// <returns>Process id. Returns 0 if failed, eg if the task does not exist or is disabled.</returns>\n\t/// <param name=\"taskFolder\">Can be like <c>@\"\\Folder\"</c> or <c>@\"\\A\\B\"</c> or <c>\"Folder\"</c> or <c>@\"\\\"</c> or <c>\"\"</c> or null.</param>\n\t/// <param name=\"taskName\">Can be like <c>\"Name\"</c> or <c>@\"\\Folder\\Name\"</c> or <c>@\"Folder\\Name\"</c>.</param>\n\t/// <param name=\"pathMustBe\">If not null, don't run if the task action's path does not match this.</param>\n\t/// <param name=\"joinArgs\">Join args into single arg for $(Arg0).</param>\n\t/// <param name=\"args\">Replacement values for substrings $(Arg0), $(Arg1), ..., $(Arg32) in 'create task' args. See <ms>IRegisteredTask.Run</ms>.</param>\n\tpublic static (int processId, RResult result) RunTask(string taskFolder, string taskName, string pathMustBe, bool joinArgs, params string[] args) {\n\t\tif (!Connect(out var ts)) return (0, RResult.CantConnect);\n\t\tif (0 != ts.GetFolder(taskFolder, out var tf) || 0 != tf.GetTask(taskName, out var t)) return (0, RResult.TaskNotFound);\n\t\t\n\t\tif (!t.Enabled) return (0, RResult.TaskDisabled);\n\t\t\n\t\tif (pathMustBe != null) {\n\t\t\tif (t.Definition.Actions.GetExecAction(1) is not { } action) return (0, RResult.BadTask);\n\t\t\tif (!filesystem.more.isSameFile(pathMustBe, action.Path)) return (0, RResult.BadPath);\n\t\t}\n\t\t\n\t\tobject a; if (args.NE_()) a = null; else if (joinArgs) a = StringUtil.CommandLineFromArray(args); else a = args;\n\t\tif (0 != t.Run(a, out var rt)) return (0, RResult.RunFailed);\n\t\trt.get_EnginePID(out int pid);\n\t\treturn (pid, RResult.Success);\n\t}\n\t\n\tpublic enum RResult { None, Success, CantConnect, TaskNotFound, TaskDisabled, BadTask, BadPath, RunFailed, ArgN }\n\t\n\t/// <summary>\n\t/// Returns true if the task exists.\n\t/// </summary>\n\t/// <inheritdoc cref=\"RunTask\" path=\"/param\"/>\n\tpublic static bool TaskExists(string taskFolder, string taskName) {\n\t\tif (!Connect(out var ts)) return false;\n\t\treturn 0 == ts.GetFolder(taskFolder, out var tf) && 0 == tf.GetTask(taskName, out _);\n\t}\n\t\n\t/// <summary>\n\t/// Deletes task if exists.\n\t/// This process must be admin.\n\t/// </summary>\n\t/// <inheritdoc cref=\"RunTask\" path=\"/param\"/>\n\tpublic static void DeleteTask(string taskFolder, string taskName) {\n\t\tif (!Connect(out var ts)) return;\n\t\tif (0 == ts.GetFolder(taskFolder, out var tf)) tf.DeleteTask(taskName, 0);\n\t}\n\t\n\tpublic static string EditorPath { get; set; } = process.thisExePath;\n\t\n\tpublic record struct FSTResult(api.IRegisteredTask rt, api.ITaskDefinition td, api.IExecAction action, string scriptArgs);\n\t\n\tpublic static bool FindScriptTask(string fileName, string itemPath, out FSTResult r) {\n\t\tr = default;\n\t\tif (Connect(out var ts)) {\n\t\t\tstring user = Environment.UserName, thisExePath = EditorPath;\n\t\t\tif (0 == ts.GetFolder(@\"Au\\\" + user, out var tf) && 0 == tf.GetTasks(api.TASK_ENUM_HIDDEN, out var tasks) && tasks.Count is int nTasks) {\n\t\t\t\tfor (int i = 1; i <= nTasks; i++) {\n\t\t\t\t\tif (0 == tasks.get_Item(i, out var rtask) && rtask.Definition is { } td) {\n\t\t\t\t\t\tif (td.Actions is { Count: int nActions } actions) {\n\t\t\t\t\t\t\tfor (int j = 1; j <= nActions; j++) {\n\t\t\t\t\t\t\t\tif (actions.GetExecAction(j) is { Arguments: var s } action && !s.NE()) {\n\t\t\t\t\t\t\t\t\tint start = 0, end;\n\t\t\t\t\t\t\t\t\tif (s[0] == '\"') {\n\t\t\t\t\t\t\t\t\t\tend = s.IndexOf('\"', start = 1);\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tend = s.IndexOf(' '); if (end < 0) end = s.Length;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tif (s.At_(start) is '>' or '*') start++;\n\t\t\t\t\t\t\t\t\tif (end > start) {\n\t\t\t\t\t\t\t\t\t\tRange r1 = start..end;\n\t\t\t\t\t\t\t\t\t\tif (s.Eq(r1, fileName, true) || s.Eq(r1, itemPath, true)) {\n\t\t\t\t\t\t\t\t\t\t\tif (action.Path is string sp && filesystem.more.isSameFile(thisExePath, sp)) {\n\t\t\t\t\t\t\t\t\t\t\t\tstring scriptArgs = null;\n\t\t\t\t\t\t\t\t\t\t\t\tif (end < s.Length) {\n\t\t\t\t\t\t\t\t\t\t\t\t\tif (s[end] == '\"') end++;\n\t\t\t\t\t\t\t\t\t\t\t\t\twhile (end < s.Length && s[end] == ' ') end++;\n\t\t\t\t\t\t\t\t\t\t\t\t\tif (end < s.Length) scriptArgs = s[end..];\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\tr = new(rtask, td, action, scriptArgs);\n\t\t\t\t\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\t\n\t/// <summary>\n\t/// Returns trigger strings of all found scheduler tasks that are set to run script <i>fn</i>.\n\t/// For a multi-trigger task returns multiple items.\n\t/// </summary>\n\tpublic static async Task<List<(string task, string trigger)>> GetScriptTriggersAsync(FileNode fn) {\n\t\tstring name = fn.Name, itemPath = fn.ItemPath;\n\t\tList<(string task, string trigger)> ar = null;\n\t\tawait Task.Run(_Work);\n\t\treturn ar;\n\t\t\n\t\tvoid _Work() {\n\t\t\tStringBuilder b = null;\n\t\t\tif (FindScriptTask(name, itemPath, out var r)) {\n\t\t\t\tstring taskName = r.rt.Name;\n\t\t\t\tforeach (var t in r.td.EnumTriggers()) {\n\t\t\t\t\t(b ??= new()).Clear();\n\t\t\t\t\tt.FormatTriggerString(b);\n\t\t\t\t\t(ar ??= new()).Add((taskName, b.ToString()));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Opens Task Scheduler UI for task editing.\n\t/// This process must be admin.\n\t/// Starts task and returns.\n\t/// </summary>\n\t/// <param name=\"taskFolder\">Task folder, like <c>@\"A\\B\"</c>.</param>\n\t/// <param name=\"taskName\">Task name (without path). If null, just opens the Task Scheduler folder.</param>\n\tpublic static void EditTask(string taskFolder, string taskName) {\n\t\tTask.Run(() => {\n\t\t\t//run Task Scheduler UI\n\t\t\tvar w = wnd.runAndFind(\n\t\t\t\t() => run.it(folders.System + \"mmc.exe\", folders.System + \"taskschd.msc\", flags: RFlags.InheritAdmin),\n\t\t\t\t60, cn: \"MMCMainFrame\");\n\t\t\t\n\t\t\t//expand folder \"Task Scheduler Library\"\n\t\t\tvar tv = w.Child(id: 12785);\n\t\t\ttv.Focus();\n\t\t\tvar htvi = wait.until(5, () => tv.Send(api.TVM_GETNEXTITEM, api.TVGN_CHILD, tv.Send(api.TVM_GETNEXTITEM)));\n\t\t\twait.until(10, () => 0 != tv.Send(api.TVM_EXPAND, api.TVE_EXPAND, htvi)); //note: don't wait for TVM_GETITEMSTATE TVIS_EXPANDED\n\t\t\t\n\t\t\t//open the specified folder\n\t\t\tvar e = elm.fromWindow(tv, EObjid.CLIENT);\n\t\t\te.Item = 2;\n\t\t\ttaskFolder = taskFolder.Trim('\\\\').Replace('\\\\', '|');\n\t\t\te.Expand(taskFolder, waitS: 10, notLast: true).Select();\n\t\t\t\n\t\t\tif (taskName == null) return;\n\t\t\t\n\t\t\t//open Properties dialog of the specified task\n\t\t\tvar lv = w.Child(30, \"***wfName listViewMain\", \"*.SysListView32.*\"); //the slowest part, ~1 s\n\t\t\tlv.Elm[\"LISTITEM\", taskName, flags: EFFlags.ClientArea | EFFlags.HiddenToo].Find(10).Select();\n\t\t\tlv.Post(Api.WM_KEYDOWN, (int)KKey.Enter);\n\t\t\t\n\t\t\t//wait for task Properties dialog and select tab \"Trigger\"\n\t\t\tvar wp = wnd.find(10, taskName + \"*\", \"*.Window.*\", WOwner.Process(w.ProcessId));\n\t\t\twp.Activate();\n\t\t\tvar tc = wp.Child(5, cn: \"*.SysTabControl32.*\");\n\t\t\ttc.Send(api.TCM_SETCURFOCUS, 1);\n\t\t\t\n\t\t\t//never mind: the script may fail at any step, although on my computers never failed.\n\t\t\t//\tLet it do as much as it can. It's better than nothing.\n\t\t\t//\tTask.Run silently handles exceptions.\n\t\t});\n\t}\n}\n\nstatic class ExtScheduler {\n\t\n\tpublic static IEnumerable<api.ITrigger> EnumTriggers(this api.ITaskDefinition t) {\n\t\tif (t.Triggers is { Count: int nTriggers } triggers && nTriggers > 0) {\n\t\t\tfor (int k = 1; k <= nTriggers; k++) {\n\t\t\t\tif (0 == triggers.get_Item(k, out var r)) yield return r;\n\t\t\t}\n\t\t}\n\t}\n\t\n\tpublic static string FormatTriggerString(this api.ITrigger t) {\n\t\tStringBuilder sb = new();\n\t\tFormatTriggerString(t, sb);\n\t\treturn sb.ToString();\n\t}\n\t\n\tpublic static void FormatTriggerString(this api.ITrigger t, StringBuilder b) {\n\t\tif (!t.Enabled) b.Append(\"(disabled) \");\n\t\tvar ttype = t.Type;\n\t\tif ((int)ttype is >= 1 and <= 5) {\n\t\t\tstring sStart = t.StartBoundary;\n\t\t\tvar (sStartDate, sStartTime) = SplitDateTime(sStart);\n\t\t\tb.Append($\"At {sStartTime} \");\n\t\t\t\n\t\t\tswitch (ttype) {\n\t\t\tcase TT.TIME:\n\t\t\t\tb.Append($\"on {sStartDate}\");\n\t\t\t\tbreak;\n\t\t\tcase TT.DAILY when t is api.IDailyTrigger g:\n\t\t\t\tshort daysInterval = g.DaysInterval;\n\t\t\t\tb.Append(\"every \").Append(daysInterval == 1 ? \"day\" : $\"{daysInterval} day\");\n\t\t\t\tbreak;\n\t\t\tcase TT.WEEKLY when t is api.IWeeklyTrigger g:\n\t\t\t\tushort daysOfWeek1 = g.DaysOfWeek;\n\t\t\t\tshort weeksInterval = g.WeeksInterval;\n\t\t\t\tif ((daysOfWeek1 & 0x7f) == 0x7f) b.Append(\"every day of the week,\"); else { b.Append(\"on \"); _DaysOfWeek(daysOfWeek1); }\n\t\t\t\tif (weeksInterval == 1) b.Append(\" every week\"); else b.AppendFormat(\" every {0} weeks\", weeksInterval);\n\t\t\t\tbreak;\n\t\t\tcase TT.MONTHLY when t is api.IMonthlyTrigger g:\n\t\t\t\tb.Append(\"on day \");\n\t\t\t\t_DaysOfMonth(g.DaysOfMonth, g.RunOnLastDayOfMonth);\n\t\t\t\tb.Append(\" of \");\n\t\t\t\tushort monthsOfYear1 = g.MonthsOfYear;\n\t\t\t\tif ((monthsOfYear1 & 0xfff) == 0xfff) b.Append(\"every month\"); else _MonthsOfYear(monthsOfYear1);\n\t\t\t\tbreak;\n\t\t\tcase TT.MONTHLYDOW when t is api.IMonthlyDOWTrigger g:\n\t\t\t\tushort weeksOfMonth = g.WeeksOfMonth;\n\t\t\t\tif (g.RunOnLastWeekOfMonth) weeksOfMonth |= 0x10;\n\t\t\t\tif ((weeksOfMonth & 0x1f) == 0x1f) b.Append(\"every\"); else { b.Append(\"on \"); _WeeksOfMonth(weeksOfMonth); }\n\t\t\t\tb.Append(\" \");\n\t\t\t\t_DaysOfWeek(g.DaysOfWeek);\n\t\t\t\tushort monthsOfYear2 = g.MonthsOfYear;\n\t\t\t\tif ((monthsOfYear2 & 0xfff) == 0xfff) b.Append(\" every month\"); else { b.Append(\" each \"); _MonthsOfYear(monthsOfYear2); }\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif ((int)ttype >= 3) b.Append(\", starting \").Append(sStartDate);\n\t\t} else {\n\t\t\tswitch (ttype) {\n\t\t\tcase TT.BOOT:\n\t\t\t\tb.Append(\"At system startup\");\n\t\t\t\tbreak;\n\t\t\tcase TT.EVENT:\n\t\t\t\tif (t is api.IEventTrigger et) {\n\t\t\t\t\tvar v = et.GetQuery();\n\t\t\t\t\tb.Append(\"On event: \").Append(v.log == null ? v.query : $\"log={v.log}, source={v.source}, id={v.id}\");\n\t\t\t\t\tif (v.level != 0) b.Append(\", level=\").Append(v.level);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase TT.IDLE:\n\t\t\t\tb.Append(\"When computer is idle\");\n\t\t\t\tbreak;\n\t\t\tcase TT.LOGON when t is api.ILogonTrigger g:\n\t\t\t\tb.Append(\"At log on of \").Append(g.UserId ?? \"any user\");\n\t\t\t\tbreak;\n\t\t\tcase TT.REGISTRATION:\n\t\t\t\tb.Append(\"When the task is created or modified\");\n\t\t\t\tbreak;\n\t\t\tcase TT.SESSION when t is api.ISessionStateChangeTrigger g:\n\t\t\t\tb.Append(\"On \");\n\t\t\t\tb.AppendFormat(g.StateChange switch {\n\t\t\t\t\tapi.TASK_SESSION_STATE_CHANGE_TYPE.TASK_CONSOLE_CONNECT => \"local connection to {0} session\",\n\t\t\t\t\tapi.TASK_SESSION_STATE_CHANGE_TYPE.TASK_CONSOLE_DISCONNECT => \"local disconnect from {0} session\",\n\t\t\t\t\tapi.TASK_SESSION_STATE_CHANGE_TYPE.TASK_REMOTE_CONNECT => \"remote connection to {0} session\",\n\t\t\t\t\tapi.TASK_SESSION_STATE_CHANGE_TYPE.TASK_REMOTE_DISCONNECT => \"remote disconnect from {0} session\",\n\t\t\t\t\tapi.TASK_SESSION_STATE_CHANGE_TYPE.TASK_SESSION_LOCK => \"workstation lock of {0}\",\n\t\t\t\t\tapi.TASK_SESSION_STATE_CHANGE_TYPE.TASK_SESSION_UNLOCK => \"workstation unlock of {0}\",\n\t\t\t\t\t_ => \"\"\n\t\t\t\t}, g.UserId ?? \"any user\");\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tb.Append(\"Custom trigger\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tb.Append('.');\n\t\t\n\t\tif (t.Repetition is { } rep && rep.Interval is string repInterval) {\n\t\t\tb.Append(\" Then repeat every \"); _Time(repInterval);\n\t\t\tif (rep.Duration is string repDuration) { b.Append(\" for a duration of \"); _Time(repDuration); }\n\t\t\tb.Append('.');\n\t\t}\n\t\t\n\t\tif (t.EndBoundary is string sEnd) {\n\t\t\tvar (sd, st) = SplitDateTime(sEnd);\n\t\t\tb.Append($\" Expires {sd} {st}.\");\n\t\t}\n\t\t\n\t\tstatic (string date, string time) SplitDateTime(string s) {\n\t\t\tif (s?.IndexOf('T') is int i && i > 0) {\n\t\t\t\tvar sd = s[..i++];\n\t\t\t\tint j = s.FindAny(\"+-\", i..); if (j < 0) j = s.Length;\n\t\t\t\treturn (sd, s[i..j]);\n\t\t\t} else return default;\n\t\t}\n\t\t\n\t\tvoid _Time(string s) {\n\t\t\tif (s.RxMatch(@\"^P(?:(\\d+)D)?(?:T(?:(\\d+)H)?(?:(\\d+)M)?)?$\", out var m)) {\n\t\t\t\tfor (int i = 1, n = 0; i <= 3; i++) {\n\t\t\t\t\tif (m[i].Value is string k) {\n\t\t\t\t\t\tif (n++ > 0) b.Append(' ');\n\t\t\t\t\t\tb.Append(k).Append(' ');\n\t\t\t\t\t\tb.Append(s[m[i].End] switch { 'D' => \"day\", 'H' => \"hour\", _ => \"minute\" });\n\t\t\t\t\t\tif (k != \"1\") b.Append('s');\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else b.Append(s);\n\t\t}\n\t\t\n\t\tvoid _DaysOfWeek(uint days) {\n\t\t\tstring sep = null;\n\t\t\tfor (int i = 0; i < 7; i++) {\n\t\t\t\tif ((days >> i & 1) != 0) {\n\t\t\t\t\tb.Append(sep); sep ??= \"|\";\n\t\t\t\t\tb.Append(i switch { 0 => \"Sunday\", 1 => \"Monday\", 2 => \"Tuesday\", 3 => \"Wednesday\", 4 => \"Thursday\", 5 => \"Friday\", 6 => \"Saturday\", _ => null });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tvoid _DaysOfMonth(uint days, bool lastDay) {\n\t\t\tstring sep = null;\n\t\t\tfor (int i = 0; i < 31; i++) {\n\t\t\t\tif ((days >> i & 1) != 0) {\n\t\t\t\t\tb.Append(sep); sep ??= \"|\";\n\t\t\t\t\tb.Append(i + 1);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (lastDay) b.Append(sep).Append(\"last\");\n\t\t}\n\t\t\n\t\tvoid _MonthsOfYear(uint months) {\n\t\t\tstring sep = null;\n\t\t\tfor (int i = 0; i < 12; i++) {\n\t\t\t\tif ((months >> i & 1) != 0) {\n\t\t\t\t\tb.Append(sep); sep ??= \"|\";\n\t\t\t\t\tb.Append((i + 1) switch { 1 => \"January\", 2 => \"February\", 3 => \"March\", 4 => \"April\", 5 => \"May\", 6 => \"June\", 7 => \"July\", 8 => \"August\", 9 => \"September\", 10 => \"October\", 11 => \"November\", 12 => \"December\", _ => null });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tvoid _WeeksOfMonth(uint weeks) {\n\t\t\tstring sep = null;\n\t\t\tfor (int i = 0; i < 5; i++) {\n\t\t\t\tif ((weeks >> i & 1) != 0) {\n\t\t\t\t\tb.Append(sep); sep ??= \"|\";\n\t\t\t\t\tb.Append((i + 1) switch { 1 => \"first\", 2 => \"second\", 3 => \"third\", 4 => \"fourth\", 5 => \"last\", _ => null });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Calls <b>get_Item</b> and casts to <b>IExecAction</b>.\n\t/// </summary>\n\t/// <param name=\"t\"></param>\n\t/// <param name=\"index\">1-based.</param>\n\t/// <returns>null if fails.</returns>\n\tpublic static api.IExecAction GetExecAction(this api.IActionCollection t, int index) {\n\t\tif (0 == t.get_Item(1, out var v)) return v as api.IExecAction;\n\t\treturn null;\n\t}\n\t\n\t/// <summary>\n\t/// Calls <b>Subscription</b> and parses the query.\n\t/// If it's a standard simple query, sets <b>log</b>, <b>source</b> and <b>id</b>. Else they will be null. Always sets <b>query</b>.\n\t/// </summary>\n\tpublic static (string query, string log, string source, string id, int level) GetQuery(this api.IEventTrigger t) {\n\t\tvar s = t.Subscription;\n\t\ttry {\n\t\t\tvar x = XElement.Parse(s);\n\t\t\tif (x is { Name.LocalName: \"QueryList\" }\n\t\t\t\t&& x.Element(\"Query\") is { NextNode: null } xq && xq.Attr(\"Path\") is { } log\n\t\t\t\t&& xq.Element(\"Select\") is { NextNode: null, Value: ['*', ..] v } xs && xs.Attr(\"Path\") == log) {\n\t\t\t\t\n\t\t\t\tstring source = null, id = null, level = null;\n\t\t\t\tbool ok = false;\n\t\t\t\tif (v == \"*\") ok = true;\n\t\t\t\telse if (v.Starts(\"*[System[\") && v.Ends(\"]]\")) {\n\t\t\t\t\tforeach (var k in v[9..^2].Split(\" and \")) {\n\t\t\t\t\t\tif (k.Like(\"Provider[@Name='*']\")) source = k[16..^2];\n\t\t\t\t\t\telse if (k.Starts(\"EventID=\")) id = k[8..];\n\t\t\t\t\t\telse if (k.Like(\"(EventID=*)\") && !k.Contains(' ')) id = k[9..^1];\n\t\t\t\t\t\telse if (k.Starts(\"Level=\")) level = k[6..];\n\t\t\t\t\t\telse if (k.Like(\"(Level=*)\") && !k.Contains(' ')) level = k[7..^1];\n\t\t\t\t\t\telse { ok = false; break; }\n\t\t\t\t\t\tok = true;\n\t\t\t\t\t}\n\t\t\t\t\tif (ok) return (s, log, source, id, level?.ToInt() ?? 0);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcatch { }\n\t\treturn (s, null, null, null, 0);\n\t}\n\t\n\t//not used\n\t//public static void SetPrincipal(this api.ITaskDefinition t, UacIL IL, string author = \"Au\") {\n\t//\tif (!(IL is UacIL.High or UacIL.Medium or UacIL.System)) throw new ArgumentException();\n\t//\tt.get_Principal(out var p);\n\t//\tp.put_Id(\"Author\");\n\t//\tp.put_LogonType(api.TASK_LOGON_TYPE.TASK_LOGON_INTERACTIVE_TOKEN);\n\t//\tif (IL == UacIL.System) p.put_UserId(\"<UserId>S-1-5-18</UserId>\\r\\n\");\n\t//\telse p.put_RunLevel(IL == UacIL.Medium ? api.TASK_RUNLEVEL_TYPE.TASK_RUNLEVEL_LUA : api.TASK_RUNLEVEL_TYPE.TASK_RUNLEVEL_HIGHEST);\n\t//}\n}\n\n#pragma warning disable 649, 169 //field never assigned/used\nunsafe class WinSchedulerApi : NativeApi {\n\t[ComImport, Guid(\"0f87369f-a4e5-4cfc-bd3e-73e6154572dd\"), ClassInterface(ClassInterfaceType.None)]\n\tinternal class TaskScheduler { }\n\t\n\t[ComImport, Guid(\"2faba4c7-4da9-4013-9697-20cc3fd40f85\")] //dual\n\tinternal interface ITaskService {\n\t\t[PreserveSig] int GetFolder(string path, out ITaskFolder ppFolder);\n\t\t[PreserveSig] int GetRunningTasks(int flags, out IRunningTaskCollection ppRunningTasks);\n\t\t[PreserveSig] int NewTask(uint flags, out ITaskDefinition ppDefinition);\n\t\t[PreserveSig] int Connect(object serverName = null, object user = null, object domain = null, object password = null);\n\t\tbool Connected { get; }\n\t\tstring TargetServer { get; }\n\t\tstring ConnectedUser { get; }\n\t\tstring ConnectedDomain { get; }\n\t\tuint HighestVersion { get; }\n\t}\n\t\n\t[ComImport, Guid(\"8cfac062-a080-4c15-9a88-aa7c2af80dfc\")] //dual\n\tinternal interface ITaskFolder {\n\t\tstring Name { get; }\n\t\tstring Path { get; }\n\t\t[PreserveSig] int GetFolder(string path, out ITaskFolder ppFolder);\n\t\t[PreserveSig] int GetFolders(int flags, out ITaskFolderCollection ppFolders);\n\t\t[PreserveSig] int CreateFolder(string subFolderName, object sddl, out ITaskFolder ppFolder);\n\t\t[PreserveSig] int DeleteFolder(string subFolderName, int flags);\n\t\t[PreserveSig] int GetTask(string path, out IRegisteredTask ppTask);\n\t\t[PreserveSig] int GetTasks(int flags, out IRegisteredTaskCollection ppTasks);\n\t\t[PreserveSig] int DeleteTask(string name, int flags);\n\t\t[PreserveSig] int RegisterTask(string path, string xmlText, TASK_CREATION flags, object userId, object password, TASK_LOGON_TYPE logonType, object sddl, out IRegisteredTask ppTask);\n\t\t[PreserveSig] int RegisterTaskDefinition(string path, ITaskDefinition pDefinition, TASK_CREATION flags, object userId, object password, TASK_LOGON_TYPE logonType, object sddl, out IRegisteredTask ppTask);\n\t\t[PreserveSig] int GetSecurityDescriptor(int securityInformation, out string pSddl);\n\t\t[PreserveSig] int SetSecurityDescriptor(string sddl, int flags);\n\t}\n\t\n\t[Flags]\n\tinternal enum TASK_CREATION : uint {\n\t\tTASK_VALIDATE_ONLY = 0x1,\n\t\tTASK_CREATE,\n\t\tTASK_UPDATE = 0x4,\n\t\tTASK_CREATE_OR_UPDATE = 0x6,\n\t\tTASK_DISABLE = 0x8,\n\t\tTASK_DONT_ADD_PRINCIPAL_ACE = 0x10,\n\t\tTASK_IGNORE_REGISTRATION_TRIGGERS = 0x20\n\t}\n\t\n\t[ComImport, Guid(\"86627eb4-42a7-41e4-a4d9-ac33a72f2d52\")] //dual\n\tinternal interface IRegisteredTaskCollection {\n\t\tint Count { get; }\n\t\t[PreserveSig] int get_Item(object index, out IRegisteredTask ppRegisteredTask);\n\t}\n\t\n\t[ComImport, Guid(\"9c86f320-dee3-4dd1-b972-a303f26b061e\")] //dual\n\tinternal interface IRegisteredTask {\n\t\tstring Name { get; }\n\t\tstring Path { get; }\n\t\tTASK_STATE State { get; }\n\t\tbool Enabled { get; set; }\n\t\t[PreserveSig] int Run(object @params, out IRunningTask ppRunningTask);\n\t\t[PreserveSig] int RunEx(object @params, int flags, int sessionID, string user, out IRunningTask ppRunningTask);\n\t\t[PreserveSig] int GetInstances(int flags, out IRunningTaskCollection ppRunningTasks);\n\t\tDateTime LastRunTime { get; }\n\t\tint LastTaskResult { get; }\n\t\tint NumberOfMissedRuns { get; }\n\t\tDateTime NextRunTime { get; }\n\t\tITaskDefinition Definition { get; }\n\t\tstring Xml { get; }\n\t\t[PreserveSig] int GetSecurityDescriptor(int securityInformation, out string pSddl);\n\t\t[PreserveSig] int SetSecurityDescriptor(string sddl, int flags);\n\t\t[PreserveSig] int Stop(int flags);\n\t\t[PreserveSig] int GetRunTimes(in SYSTEMTIME pstStart, in SYSTEMTIME pstEnd, ref uint pCount, out SYSTEMTIME* pRunTimes);\n\t}\n\t\n\t[ComImport, Guid(\"79184a66-8664-423f-97f1-637356a5d812\")] //dual\n\tinternal interface ITaskFolderCollection {\n\t\tint Count { get; }\n\t\t[PreserveSig] int get_Item(object index, out ITaskFolder ppFolder);\n\t}\n\t\n\tinternal struct SYSTEMTIME {\n\t\tpublic ushort wYear;\n\t\tpublic ushort wMonth;\n\t\tpublic ushort wDayOfWeek;\n\t\tpublic ushort wDay;\n\t\tpublic ushort wHour;\n\t\tpublic ushort wMinute;\n\t\tpublic ushort wSecond;\n\t\tpublic ushort wMilliseconds;\n\t}\n\t\n\t[ComImport, Guid(\"f5bc8fc5-536d-4f77-b852-fbc1356fdeb6\")] //dual\n\tinternal interface ITaskDefinition {\n\t\tIRegistrationInfo RegistrationInfo { get; set; }\n\t\tITriggerCollection Triggers { get; set; }\n\t\tITaskSettings Settings { get; set; }\n\t\tstring Data { get; set; }\n\t\tIPrincipal Principal { get; set; }\n\t\tIActionCollection Actions { get; set; }\n\t\tstring XmlText { get; set; }\n\t}\n\t\n\t[ComImport, Guid(\"6a67614b-6828-4fec-aa54-6d52e8f1f2db\")] //dual\n\tinternal interface IRunningTaskCollection {\n\t\tint Count { get; }\n\t\t[PreserveSig] int get_Item(object index, out IRunningTask ppRunningTask);\n\t}\n\t\n\t[ComImport, Guid(\"653758fb-7b9a-4f1e-a471-beeb8e9b834e\")] //dual\n\tinternal interface IRunningTask {\n\t\t[PreserveSig] int get_Name(out string pName);\n\t\t[PreserveSig] int get_InstanceGuid(out string pGuid);\n\t\t[PreserveSig] int get_Path(out string pPath);\n\t\t[PreserveSig] int get_State(out TASK_STATE pState);\n\t\t[PreserveSig] int get_CurrentAction(out string pName);\n\t\t[PreserveSig] int Stop();\n\t\t[PreserveSig] int Refresh();\n\t\t[PreserveSig] int get_EnginePID(out int pPID);\n\t}\n\t\n\tinternal enum TASK_STATE {\n\t\tTASK_STATE_UNKNOWN,\n\t\tTASK_STATE_DISABLED,\n\t\tTASK_STATE_QUEUED,\n\t\tTASK_STATE_READY,\n\t\tTASK_STATE_RUNNING\n\t}\n\t\n\t[ComImport, Guid(\"02820E19-7B98-4ed2-B2E8-FDCCCEFF619B\")] //dual\n\tinternal interface IActionCollection {\n\t\tint Count { get; }\n\t\t[PreserveSig] int get_Item(int index, out IAction ppAction);\n\t\tvoid __NewEnum();\n\t\tstring XmlText { get; set; }\n\t\t[PreserveSig] int Create(TASK_ACTION_TYPE type, out IAction ppAction);\n\t\t[PreserveSig] int Remove(object index);\n\t\t[PreserveSig] int Clear();\n\t\tstring Context { get; set; }\n\t}\n\t\n\t[ComImport, Guid(\"D98D51E5-C9B4-496a-A9C1-18980261CF0F\")] //dual\n\tinternal interface IPrincipal {\n\t\tstring Id { get; set; }\n\t\tstring DisplayName { get; set; }\n\t\tstring UserId { get; set; }\n\t\tTASK_LOGON_TYPE LogonType { get; set; }\n\t\tstring GroupId { get; set; }\n\t\tTASK_RUNLEVEL_TYPE RunLevel { get; set; }\n\t}\n\t\n\t[ComImport, Guid(\"8FD4711D-2D02-4c8c-87E3-EFF699DE127E\")] //dual\n\tinternal interface ITaskSettings {\n\t\tbool AllowDemandStart { get; set; }\n\t\tstring RestartInterval { get; set; }\n\t\tint RestartCount { get; set; }\n\t\tint MultipleInstances { get; set; }\n\t\tbool StopIfGoingOnBatteries { get; set; }\n\t\tbool DisallowStartIfOnBatteries { get; set; }\n\t\tbool AllowHardTerminate { get; set; }\n\t\tbool StartWhenAvailable { get; set; }\n\t\tstring XmlText { get; set; }\n\t\tbool RunOnlyIfNetworkAvailable { get; set; }\n\t\tstring ExecutionTimeLimit { get; set; }\n\t\tbool Enabled { get; set; }\n\t\tstring DeleteExpiredTaskAfter { get; set; }\n\t\tint Priority { get; set; }\n\t\tint Compatibility { get; set; }\n\t\tbool Hidden { get; set; }\n\t\tIIdleSettings IdleSettings { get; set; }\n\t\tbool RunOnlyIfIdle { get; set; }\n\t\tbool WakeToRun { get; set; }\n\t\tINetworkSettings NetworkSettings { get; set; }\n\t} //info: ITaskSettings2/3 not useful\n\t\n\t[ComImport, Guid(\"416D8B73-CB41-4ea1-805C-9BE9A5AC4A74\")] //dual\n\tinternal interface IRegistrationInfo {\n\t\tstring Description { get; set; }\n\t\tstring Author { get; set; }\n\t\tstring Version { get; set; }\n\t\tstring Date { get; set; }\n\t\tstring Documentation { get; set; }\n\t\tstring XmlText { get; set; }\n\t\tstring URI { get; set; }\n\t\tobject SecurityDescriptor { get; set; }\n\t\tstring Source { get; set; }\n\t}\n\t\n\t[ComImport, Guid(\"9F7DEA84-C30B-4245-80B6-00E9F646F1B4\")] //dual\n\tinternal interface INetworkSettings {\n\t\tstring Name { get; set; }\n\t\tstring Id { get; set; }\n\t}\n\t\n\t[ComImport, Guid(\"84594461-0053-4342-A8FD-088FABF11F32\")] //dual\n\tinternal interface IIdleSettings {\n\t\tstring IdleDuration { get; set; }\n\t\tstring WaitTimeout { get; set; }\n\t\tbool StopOnIdleEnd { get; set; }\n\t\tbool RestartOnIdle { get; set; }\n\t}\n\t\n\tinternal enum TASK_COMPATIBILITY {\n\t\tTASK_COMPATIBILITY_AT,\n\t\tTASK_COMPATIBILITY_V1,\n\t\tTASK_COMPATIBILITY_V2,\n\t\tTASK_COMPATIBILITY_V2_1,\n\t\tTASK_COMPATIBILITY_V2_2,\n\t\tTASK_COMPATIBILITY_V2_3,\n\t\tTASK_COMPATIBILITY_V2_4\n\t}\n\t\n\tinternal enum TASK_INSTANCES_POLICY {\n\t\tTASK_INSTANCES_PARALLEL,\n\t\tTASK_INSTANCES_QUEUE,\n\t\tTASK_INSTANCES_IGNORE_NEW,\n\t\tTASK_INSTANCES_STOP_EXISTING\n\t}\n\t\n\tinternal enum TASK_RUNLEVEL_TYPE {\n\t\tTASK_RUNLEVEL_LUA,\n\t\tTASK_RUNLEVEL_HIGHEST\n\t}\n\t\n\tinternal enum TASK_LOGON_TYPE {\n\t\tTASK_LOGON_NONE,\n\t\tTASK_LOGON_PASSWORD,\n\t\tTASK_LOGON_S4U,\n\t\tTASK_LOGON_INTERACTIVE_TOKEN,\n\t\tTASK_LOGON_GROUP,\n\t\tTASK_LOGON_SERVICE_ACCOUNT,\n\t\tTASK_LOGON_INTERACTIVE_TOKEN_OR_PASSWORD\n\t}\n\t\n\t[ComImport, Guid(\"BAE54997-48B1-4cbe-9965-D6BE263EBEA4\")] //dual\n\tinternal interface IAction {\n\t\tstring Id { get; set; }\n\t\tTASK_ACTION_TYPE Type { get; }\n\t}\n\t\n\tinternal enum TASK_ACTION_TYPE {\n\t\tTASK_ACTION_EXEC,\n\t\tTASK_ACTION_COM_HANDLER = 5,\n\t\tTASK_ACTION_SEND_EMAIL,\n\t\tTASK_ACTION_SHOW_MESSAGE\n\t}\n\t\n\t[ComImport, Guid(\"4c3d624d-fd6b-49a3-b9b7-09cb3cd3f047\")] //dual\n\tinternal interface IExecAction {\n\t\tvoid _0(); void _1(); void _2(); //IAction\n\t\tstring Path { get; set; }\n\t\tstring Arguments { get; set; }\n\t\tstring WorkingDirectory { get; set; }\n\t}\n\t\n\tinternal const int TASK_ENUM_HIDDEN = 0x1;\n\t\n\tinternal enum TASK_TRIGGER_TYPE2 {\n\t\tEVENT,\n\t\tTIME,\n\t\tDAILY,\n\t\tWEEKLY,\n\t\tMONTHLY,\n\t\tMONTHLYDOW,\n\t\tIDLE,\n\t\tREGISTRATION,\n\t\tBOOT,\n\t\tLOGON,\n\t\tSESSION = 11,\n\t\tCUSTOM_TRIGGER_01\n\t}\n\t\n\t[ComImport, Guid(\"85df5081-1b24-4f32-878a-d9d14df4cb77\")] //dual\n\tinternal interface ITriggerCollection {\n\t\tint Count { get; }\n\t\t[PreserveSig] int get_Item(int index, out ITrigger ppTrigger);\n\t\tvoid __NewEnum();\n\t\tITrigger Create(TASK_TRIGGER_TYPE2 type);\n\t\t[PreserveSig] int Remove(object index);\n\t\tvoid Clear();\n\t}\n\t\n\t[ComImport, Guid(\"09941815-ea89-4b5b-89e0-2a773801fac3\")] //dual\n\tinternal interface ITrigger {\n\t\tTASK_TRIGGER_TYPE2 Type { get; }\n\t\tstring Id { get; set; }\n\t\tIRepetitionPattern Repetition { get; set; }\n\t\tstring ExecutionTimeLimit { get; set; }\n\t\tstring StartBoundary { get; set; }\n\t\tstring EndBoundary { get; set; }\n\t\tbool Enabled { get; set; }\n\t}\n\t\n\t[ComImport, Guid(\"7FB9ACF1-26BE-400e-85B5-294B9C75DFD6\")] //dual\n\tinternal interface IRepetitionPattern {\n\t\tstring Interval { get; set; }\n\t\tstring Duration { get; set; }\n\t\tbool StopAtDurationEnd { get; set; }\n\t}\n\t\n\t[ComImport, Guid(\"b45747e0-eba7-4276-9f29-85c5bb300006\")]\n\tinternal interface ITimeTrigger {\n\t\tvoid _0();void _1();void _2();void _3();void _4();void _5();void _6();void _7();void _8();void _9();void _10();void _11();void _12();\n\t\tstring RandomDelay { get; set; }\n\t}\n\t\n\t[ComImport, Guid(\"126c5cd8-b288-41d5-8dbf-e491446adc5c\")]\n\tinternal interface IDailyTrigger {\n\t\tvoid _0(); void _1(); void _2(); void _3(); void _4(); void _5(); void _6(); void _7(); void _8(); void _9(); void _10(); void _11(); void _12();\n\t\tshort DaysInterval { get; set; }\n\t\tstring RandomDelay { get; set; }\n\t}\n\t\n\t[ComImport, Guid(\"5038fc98-82ff-436d-8728-a512a57c9dc1\")]\n\tinternal interface IWeeklyTrigger {\n\t\tvoid _0(); void _1(); void _2(); void _3(); void _4(); void _5(); void _6(); void _7(); void _8(); void _9(); void _10(); void _11(); void _12();\n\t\tushort DaysOfWeek { get; set; }\n\t\tshort WeeksInterval { get; set; }\n\t\tstring RandomDelay { get; set; }\n\t}\n\t\n\t[ComImport, Guid(\"97c45ef1-6b02-4a1a-9c0e-1ebfba1500ac\")]\n\tinternal interface IMonthlyTrigger {\n\t\tvoid _0(); void _1(); void _2(); void _3(); void _4(); void _5(); void _6(); void _7(); void _8(); void _9(); void _10(); void _11(); void _12();\n\t\tuint DaysOfMonth { get; set; }\n\t\tushort MonthsOfYear { get; set; }\n\t\tbool RunOnLastDayOfMonth { get; set; }\n\t\tstring RandomDelay { get; set; }\n\t}\n\t\n\t[ComImport, Guid(\"77d025a3-90fa-43aa-b52e-cda5499b946a\")]\n\tinternal interface IMonthlyDOWTrigger {\n\t\tvoid _0(); void _1(); void _2(); void _3(); void _4(); void _5(); void _6(); void _7(); void _8(); void _9(); void _10(); void _11(); void _12();\n\t\tushort DaysOfWeek { get; set; }\n\t\tushort WeeksOfMonth { get; set; }\n\t\tushort MonthsOfYear { get; set; }\n\t\tbool RunOnLastWeekOfMonth { get; set; }\n\t\tstring RandomDelay { get; set; }\n\t}\n\t\n\t//[ComImport, Guid(\"2a9c35da-d357-41f4-bbc1-207ac1b1f3cb\")]\n\t//internal interface IBootTrigger {\n\t//\tvoid _0();void _1();void _2();void _3();void _4();void _5();void _6();void _7();void _8();void _9();void _10();void _11();void _12();\n\t//\tstring Delay { get; set; }\n\t//}\n\t\n\t[ComImport, Guid(\"d45b0167-9653-4eef-b94f-0732ca7af251\")]\n\tinternal interface IEventTrigger {\n\t\tvoid _0(); void _1(); void _2(); void _3(); void _4(); void _5(); void _6(); void _7(); void _8(); void _9(); void _10(); void _11(); void _12();\n\t\tstring Subscription { get; set; }\n\t\tstring Delay { get; set; }\n\t\tITaskNamedValueCollection ValueQueries { get; set; }\n\t}\n\t\n\t[ComImport, Guid(\"b4ef826b-63c3-46e4-a504-ef69e4f7ea4d\")]\n\tinternal interface ITaskNamedValueCollection {\n\t\tint Count { get; }\n\t\t[PreserveSig] int get_Item(int index, out ITaskNamedValuePair ppPair);\n\t\tvoid __NewEnum();\n\t\t[PreserveSig] int Create(string name, string value, out ITaskNamedValuePair ppPair);\n\t\t[PreserveSig] int Remove(int index);\n\t\t[PreserveSig] int Clear();\n\t}\n\t\n\t[ComImport, Guid(\"39038068-2b46-4afd-8662-7bb6f868d221\")]\n\tinternal interface ITaskNamedValuePair {\n\t\tstring Name { get; set; }\n\t\tstring Value { get; set; }\n\t}\n\t\n\t[ComImport, Guid(\"72dade38-fae4-4b3e-baf4-5d009af02b1c\")]\n\tinternal interface ILogonTrigger {\n\t\tvoid _0(); void _1(); void _2(); void _3(); void _4(); void _5(); void _6(); void _7(); void _8(); void _9(); void _10(); void _11(); void _12();\n\t\tstring Delay { get; set; }\n\t\tstring UserId { get; set; }\n\t}\n\t\n\t[ComImport, Guid(\"4c8fec3a-c218-4e0c-b23d-629024db91a2\")]\n\tinternal interface IRegistrationTrigger {\n\t\tvoid _0();void _1();void _2();void _3();void _4();void _5();void _6();void _7();void _8();void _9();void _10();void _11();void _12();\n\t\tstring Delay { get; set; }\n\t}\n\t\n\t[ComImport, Guid(\"754da71b-4385-4475-9dd9-598294fa3641\")]\n\tinternal interface ISessionStateChangeTrigger {\n\t\tvoid _0(); void _1(); void _2(); void _3(); void _4(); void _5(); void _6(); void _7(); void _8(); void _9(); void _10(); void _11(); void _12();\n\t\tstring Delay { get; set; }\n\t\tstring UserId { get; set; }\n\t\tTASK_SESSION_STATE_CHANGE_TYPE StateChange { get; set; }\n\t}\n\t\n\tinternal struct MONTHLYDOW {\n\t\tpublic ushort wWhichWeek;\n\t\tpublic ushort rgfDaysOfTheWeek;\n\t\tpublic ushort rgfMonths;\n\t}\n\t\n\tinternal struct MONTHLYDATE {\n\t\tpublic uint rgfDays;\n\t\tpublic ushort rgfMonths;\n\t}\n\t\n\tinternal struct WEEKLY {\n\t\tpublic ushort WeeksInterval;\n\t\tpublic ushort rgfDaysOfTheWeek;\n\t}\n\t\n\tinternal struct DAILY {\n\t\tpublic ushort DaysInterval;\n\t}\n\t\n\tinternal enum TASK_SESSION_STATE_CHANGE_TYPE {\n\t\tTASK_CONSOLE_CONNECT = 1,\n\t\tTASK_CONSOLE_DISCONNECT,\n\t\tTASK_REMOTE_CONNECT,\n\t\tTASK_REMOTE_DISCONNECT,\n\t\tTASK_SESSION_LOCK = 7,\n\t\tTASK_SESSION_UNLOCK\n\t}\n\t\n\tinternal const int TVM_GETNEXTITEM = 0x110A;\n\tinternal const int TVM_EXPAND = 0x1102;\n\t\n\tinternal const int TVGN_CHILD = 0x4;\n\t\n\tinternal const int TVE_EXPAND = 0x2;\n\t\n\tinternal const int TCM_SETCURFOCUS = 0x1330;\n}\n#pragma warning restore 649, 169 //field never assigned/used\n"
  },
  {
    "path": "Au.Editor/_prePostBuild.cs",
    "content": "/*/ role editorExtension; /*/\n\nif (args[0] == \"post\") {\n\tvar outputPath = args[1];\n\t\n\tvar source = @\"C:\\code\\au\\_\";\n\trun.console(out var so, \"robocopy.exe\", $\"\\\"{source}\\\" \\\"{outputPath}\\\" /e /sj /sl /r:2 /w:1 /xd Default Git SDK /xf *.pdb Au.dll Au.Controls.dll Au.Editor.dll LibreAutomateSetup.exe\");\n\t\n\t//compiler always copies the true Au.dll to outputPath. Let's replace it with our Au.dll.\n\tfilesystem.copyTo(@\"C:\\code\\ok\\dll\\Au.dll\", outputPath, FIfExists.Delete);\n} else {\n\tforeach (var w in wnd.findAll(\"**m LibreAutomate||Find window||Find UI element\", \"HwndWrapper[Au.Editor;*\", also: o => o.ProcessId != process.thisProcessId)) {\n\t\tw.Close(noWait: true);\n\t\tw.WaitForClosed(3, waitUntilProcessEnds: true);\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/resources/AssemblyInfo.cs",
    "content": "using System.Windows;\n\n[assembly: System.Resources.NeutralResourcesLanguage(\"en-US\")]\n\n[assembly: ThemeInfo(\n\tResourceDictionaryLocation.None, //where theme specific resource dictionaries are located\n\t\t\t\t\t\t\t\t\t //(used if a resource is not found in the page,\n\t\t\t\t\t\t\t\t\t // or application resource dictionaries)\n\tResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located\n\t\t\t\t\t\t\t\t\t\t\t  //(used if a resource is not found in the page,\n\t\t\t\t\t\t\t\t\t\t\t  // app, or any theme specific resource dictionaries)\n)]\n"
  },
  {
    "path": "Au.Editor/resources/Au.manifest",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\" manifestVersion=\"1.0\" xmlns:asmv3=\"urn:schemas-microsoft-com:asm.v3\">\n\t<assemblyIdentity\n\t\tversion=\"1.0.0.0\"\n\t\tprocessorArchitecture=\"*\"\n\t\tname=\"Company.Product.Filename\"\n\t\ttype=\"win32\"\n/>\n\t<description>File Description</description>\n\t<dependency>\n\t\t<dependentAssembly>\n\t\t\t<assemblyIdentity\n\t\t\t\ttype=\"win32\"\n\t\t\t\tname=\"Microsoft.Windows.Common-Controls\"\n\t\t\t\tversion=\"6.0.0.0\"\n\t\t\t\tprocessorArchitecture=\"*\"\n\t\t\t\tpublicKeyToken=\"6595b64144ccf1df\"\n\t\t\t\tlanguage=\"*\"\n        />\n\t\t</dependentAssembly>\n\t</dependency>\n\t<trustInfo xmlns=\"urn:schemas-microsoft-com:asm.v2\">\n\t\t<security>\n\t\t\t<requestedPrivileges>\n\t\t\t\t<requestedExecutionLevel level=\"asInvoker\" uiAccess=\"false\"/>\n\t\t\t</requestedPrivileges>\n\t\t</security>\n\t</trustInfo>\n\t<compatibility xmlns=\"urn:schemas-microsoft-com:compatibility.v1\">\n\t\t<application>\n\t\t\t<!-- Windows 7 -->\n\t\t\t<supportedOS Id=\"{35138b9a-5d96-4fbd-8e2d-a2440225f93a}\"/>\n\t\t\t<!-- Windows 8 -->\n\t\t\t<supportedOS Id=\"{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}\"/>\n\t\t\t<!-- Windows 8.1 -->\n\t\t\t<supportedOS Id=\"{1f676c76-80e1-4239-95bb-83d0f6d0da78}\"/>\n\t\t\t<!-- Windows 10 -->\n\t\t\t<supportedOS Id=\"{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}\"/>\n\t\t\t<maxversiontested Id=\"10.0.22631.0\"/>\n\t\t</application>\n\t</compatibility>\n\t<asmv3:application>\n\t\t<asmv3:windowsSettings xmlns=\"http://schemas.microsoft.com/SMI/2005/WindowsSettings\">\n\t\t\t<dpiAwareness xmlns=\"http://schemas.microsoft.com/SMI/2016/WindowsSettings\">PerMonitorV2</dpiAwareness>\n\t\t\t<dpiAware>True/PM</dpiAware>\n\t\t</asmv3:windowsSettings>\n\t\t<asmv3:windowsSettings xmlns=\"http://schemas.microsoft.com/SMI/2011/WindowsSettings\">\n\t\t\t<disableWindowFiltering>true</disableWindowFiltering>\n\t\t</asmv3:windowsSettings>\n\t</asmv3:application>\n</assembly>\n<!--\nThis manifest is used in Au.Editor and Au.Task.\nAdded to the C++ apphost (from template Au.AppHost) in post-build event.\nNote: VS does not recompile project after modifying this file, unless some other file is modified of \"Copy if newer\" is set.\n-->"
  },
  {
    "path": "Au.Editor/resources/ci/Class.xaml",
    "content": "﻿<!-- This file was generated by the AiToXaml tool.-->\n<!-- Tool Version: 14.0.22307.0 -->\n<Viewbox Width=\"16\" Height=\"16\" xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">\n  <Rectangle Width=\"16\" Height=\"16\">\n    <Rectangle.Fill>\n      <DrawingBrush>\n        <DrawingBrush.Drawing>\n          <DrawingGroup>\n            <DrawingGroup.Children>\n              <GeometryDrawing Brush=\"#00FFFFFF\" Geometry=\"F1M16,16L0,16 0,0 16,0z\" />\n              <GeometryDrawing Brush=\"#FFF6F6F6\" Geometry=\"F1M5.5863,-0.000199999999999534L0.000299999999999301,5.5858 0.000299999999999301,6.4138 3.9993,10.4138 6.4143,7.9998 7.0003,7.9998 7.0003,9.9998 7.0003,13.0008 8.5863,13.0008 11.5853,15.9998 12.4133,15.9998 16.0003,12.4148 16.0003,11.5858 13.9143,9.4998 16.0003,7.4138 16.0003,6.5858 12.9993,3.5868 11.5853,4.9998 10.0003,4.9998 9.4143,4.9998 10.4143,4.0008 6.4143,-0.000199999999999534z\" />\n              <GeometryDrawing Brush=\"#FFC17C1A\" Geometry=\"F1M13,10L15,12 12,15 10,13 11,12 8,12 8,7 6,7 4,9 1,6 6,1 9,4 7,6 12,6 13,5 15,7 12,10 10,8 11,7 9,7 9,11 11.999,11.002z\" />\n            </DrawingGroup.Children>\n          </DrawingGroup>\n        </DrawingBrush.Drawing>\n      </DrawingBrush>\n    </Rectangle.Fill>\n  </Rectangle>\n</Viewbox>"
  },
  {
    "path": "Au.Editor/resources/ci/Constant.xaml",
    "content": "﻿<!-- This file was generated by the AiToXaml tool.-->\n<!-- Tool Version: 14.0.22307.0 -->\n<Viewbox Width=\"16\" Height=\"16\" xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">\n  <Rectangle Width=\"16\" Height=\"16\">\n    <Rectangle.Fill>\n      <DrawingBrush>\n        <DrawingBrush.Drawing>\n          <DrawingGroup>\n            <DrawingGroup.Children>\n              <GeometryDrawing Brush=\"#00FFFFFF\" Geometry=\"F1M16,16L0,16 0,0 16,0z\" />\n              <GeometryDrawing Brush=\"#FFF6F6F6\" Geometry=\"F1M2.8789,14L0.9999,12.121 0.9999,3.879 2.8789,2 13.1209,2 14.9999,3.879 14.9999,12.121 13.1209,14z\" />\n              <GeometryDrawing Brush=\"#FFEFEFF0\" Geometry=\"F1M11,7L5,7 5,6 11,6z M11,10L5,10 5,9 11,9z M12.293,4L3.707,4 3,4.707 3,11.293 3.707,12 12.293,12 13,11.293 13,4.707z\" />\n              <GeometryDrawing Brush=\"#FF424242\" Geometry=\"F1M3.707,12L12.293,12 13,11.293 13,4.707 12.293,4 3.707,4 3,4.707 3,11.293z M12.707,13L3.293,13 2,11.707 2,4.293 3.293,3 12.707,3 14,4.293 14,11.707z\" />\n              <GeometryDrawing Brush=\"#FF00529C\" Geometry=\"F1M11,9L5,9 5,10 11,10z M11,7L5,7 5,6 11,6z\" />\n            </DrawingGroup.Children>\n          </DrawingGroup>\n        </DrawingBrush.Drawing>\n      </DrawingBrush>\n    </Rectangle.Fill>\n  </Rectangle>\n</Viewbox>"
  },
  {
    "path": "Au.Editor/resources/ci/Delegate.xaml",
    "content": "﻿<!-- This file was generated by the AiToXaml tool.-->\n<!-- Tool Version: 14.0.22307.0 -->\n<Viewbox Width=\"16\" Height=\"16\" xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">\n  <Rectangle Width=\"16\" Height=\"16\">\n    <Rectangle.Fill>\n      <DrawingBrush>\n        <DrawingBrush.Drawing>\n          <DrawingGroup>\n            <DrawingGroup.Children>\n              <GeometryDrawing Brush=\"#00FFFFFF\" Geometry=\"F1M16,16L0,16 0,0 16,0z\" />\n              <GeometryDrawing Brush=\"#FFF6F6F6\" Geometry=\"F1M2.25,13C1.009,13,0,11.99,0,10.75L0,5.25C0,4.009,1.009,3,2.25,3L3.014,3C3.139,1.876,4.094,1,5.25,1L6.75,1C7.906,1,8.861,1.876,8.986,3L9.75,3C10.99,3,12,4.009,12,5.25L12,10.75C12,11.99,10.99,13,9.75,13z\" />\n              <GeometryDrawing Brush=\"#FF642D90\" Geometry=\"F1M10,11L9,11 9,5 10,5z M7,4L5,4 5,3.25C5,3.112,5.112,3,5.25,3L6.75,3C6.888,3,7,3.112,7,3.25z M3,11L2,11 2,5 3,5z M9.75,4L8,4 8,3.25C8,2.561,7.439,2,6.75,2L5.25,2C4.561,2,4,2.561,4,3.25L4,4 2.25,4C1.561,4,1,4.561,1,5.25L1,10.75C1,11.44,1.561,12,2.25,12L9.75,12C10.44,12,11,11.44,11,10.75L11,5.25C11,4.561,10.44,4,9.75,4\" />\n              <GeometryDrawing Brush=\"#FFEFEFF0\" Geometry=\"F1M10,11L9,11 9,5 10,5z M3,11L2,11 2,5 3,5z\" />\n            </DrawingGroup.Children>\n          </DrawingGroup>\n        </DrawingBrush.Drawing>\n      </DrawingBrush>\n    </Rectangle.Fill>\n  </Rectangle>\n</Viewbox>"
  },
  {
    "path": "Au.Editor/resources/ci/Enum.xaml",
    "content": "﻿<!-- This file was generated by the AiToXaml tool.-->\n<!-- Tool Version: 14.0.22307.0 -->\n<Viewbox Width=\"16\" Height=\"16\" xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">\n  <Rectangle Width=\"16\" Height=\"16\">\n    <Rectangle.Fill>\n      <DrawingBrush>\n        <DrawingBrush.Drawing>\n          <DrawingGroup>\n            <DrawingGroup.Children>\n              <GeometryDrawing Brush=\"#00FFFFFF\" Geometry=\"F1M16,16L0,16 0,0 16,0z\" />\n              <GeometryDrawing Brush=\"#FFF6F6F6\" Geometry=\"F1M7.5861,0.999700000000001L6.0001,2.5867 6.0001,5.9997 1.5861,5.9997 9.99999999997669E-05,7.5867 9.99999999997669E-05,13.4157 1.5861,14.9997 8.4141,14.9997 10.0001,13.4157 10.0001,9.9997 14.4141,9.9997 16.0001,8.4147 16.0001,2.5867 14.4141,0.999700000000001z\" />\n              <GeometryDrawing Brush=\"#FFEFEFF0\" Geometry=\"F1M13,5L9,5 9,4 13,4z M14,6L14,3 8,3 8,6 8.414,6 9,6.586 9,6 13,6 13,7 9.414,7 10,7.586 10,8 14,8z M3,11L7,11 7,12 3,12z M3,9L7,9 7,10 3,10z M2,13L8,13 8,11 8,8 2,8z\" />\n              <GeometryDrawing Brush=\"#FFC17C1A\" Geometry=\"F1M14,2.0005L8,2.0005 7,3.0005 7,6.0005 8,6.0005 8,3.0005 14,3.0005 14,6.0005 14,8.0005 10,8.0005 10,9.0005 14,9.0005 15,8.0005 15,3.0005z M9,5.0005L13,5.0005 13,4.0005 9,4.0005z M9,6.0005L10,7.0005 13,7.0005 13,6.0005z M8,8.0005L2,8.0005 2,13.0005 8,13.0005 8,11.0005z M9,8.0005L9,13.0005 8,14.0005 2,14.0005 1,13.0005 1,8.0005 2,7.0005 8,7.0005z M3,10.0005L7.001,10.0005 7.001,9.0005 3,9.0005z M3,11.0005L7.001,11.0005 7.001,12.0005 3,12.0005z\" />\n            </DrawingGroup.Children>\n          </DrawingGroup>\n        </DrawingBrush.Drawing>\n      </DrawingBrush>\n    </Rectangle.Fill>\n  </Rectangle>\n</Viewbox>"
  },
  {
    "path": "Au.Editor/resources/ci/EnumMember.xaml",
    "content": "﻿<!-- This file was generated by the AiToXaml tool.-->\n<!-- Tool Version: 14.0.22307.0 -->\n<Viewbox Width=\"16\" Height=\"16\" xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">\n  <Rectangle Width=\"16\" Height=\"16\">\n    <Rectangle.Fill>\n      <DrawingBrush>\n        <DrawingBrush.Drawing>\n          <DrawingGroup>\n            <DrawingGroup.Children>\n              <GeometryDrawing Brush=\"#00FFFFFF\" Geometry=\"F1M16,16L0,16 0,0 16,0z\" />\n              <GeometryDrawing Brush=\"#FFF6F6F6\" Geometry=\"F1M7.5852,0.999700000000001L6.0002,2.5867 6.0002,5.9997 0.000200000000000422,5.9997 0.000200000000000422,14.9997 10.0002,14.9997 10.0002,9.9997 14.4142,9.9997 16.0002,8.4147 16.0002,2.5867 14.4142,0.999700000000001z\" />\n              <GeometryDrawing Brush=\"#FFEFEFF0\" Geometry=\"F1M7,11L3,11 3,10 7,10z M2,13L8,13 8,8 2,8z M13,5L9,5 9,4 13,4z M8,3L8,6 9,6 10,6 13,6 13,7 10,7 10,8 14,8 14,6 14,3z\" />\n              <GeometryDrawing Brush=\"#FF00529C\" Geometry=\"F1M8,8L2,8 2,13 8,13z M9,14L1,14 1,7 9,7z M7,10L3,10 3,11 7,11z M14,2L8,2 7,3 7,6 8,6 8,3 14,3 14,6 14,8 10,8 10,9 14,9 15,8 15,3z M9,5L13,5 13,4 9,4z M10,6L13,6 13,7 10,7z\" />\n            </DrawingGroup.Children>\n          </DrawingGroup>\n        </DrawingBrush.Drawing>\n      </DrawingBrush>\n    </Rectangle.Fill>\n  </Rectangle>\n</Viewbox>"
  },
  {
    "path": "Au.Editor/resources/ci/Event.xaml",
    "content": "﻿<!-- This file was generated by the AiToXaml tool.-->\n<!-- Tool Version: 14.0.22307.0 -->\n<Viewbox Width=\"16\" Height=\"16\" xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">\n  <Rectangle Width=\"16\" Height=\"16\">\n    <Rectangle.Fill>\n      <DrawingBrush>\n        <DrawingBrush.Drawing>\n          <DrawingGroup>\n            <DrawingGroup.Children>\n              <GeometryDrawing Brush=\"#00FFFFFF\" Geometry=\"F1M16,16L0,16 0,0 16,0z\" />\n              <GeometryDrawing Brush=\"#FFF6F6F6\" Geometry=\"F1M3.3822,1.9996L0.000199999999999978,8.7636 0.000199999999999978,9.9996 2.3812,9.9996 0.3812,14.0006 2.9142,14.0006 9.5002,7.4136 9.5002,6.0006 6.4142,6.0006 9.0002,3.4146 9.0002,1.9996z\" />\n              <GeometryDrawing Brush=\"#FFC17C1A\" Geometry=\"F1M8.5176,7L2.5176,13 1.9996,13 4.0176,9 0.9996,9 3.9996,3 7.9996,3 3.9996,7z\" />\n            </DrawingGroup.Children>\n          </DrawingGroup>\n        </DrawingBrush.Drawing>\n      </DrawingBrush>\n    </Rectangle.Fill>\n  </Rectangle>\n</Viewbox>"
  },
  {
    "path": "Au.Editor/resources/ci/ExpandScope.xaml",
    "content": "<Viewbox Width=\"16 \" Height=\"16\" xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\" xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" xmlns:System=\"clr-namespace:System;assembly=mscorlib\">\n  <Rectangle Width=\"16 \" Height=\"16\">\n    <Rectangle.Resources>\n      <SolidColorBrush x:Key=\"canvas\" Opacity=\"0\" />\n      <SolidColorBrush x:Key=\"light-defaultgrey\" Color=\"#212121\" Opacity=\"1\" />\n      <SolidColorBrush x:Key=\"light-green\" Color=\"#1f801f\" Opacity=\"1\" />\n    </Rectangle.Resources>\n    <Rectangle.Fill>\n      <DrawingBrush Stretch=\"None\">\n        <DrawingBrush.Drawing>\n          <DrawingGroup>\n            <DrawingGroup x:Name=\"canvas\">\n              <GeometryDrawing Brush=\"{DynamicResource canvas}\" Geometry=\"F1M16,0V16H0V0Z\" />\n            </DrawingGroup>\n            <DrawingGroup x:Name=\"level_1\">\n              <GeometryDrawing Brush=\"{DynamicResource light-defaultgrey}\" Geometry=\"F1M1.5,1H5V2H2V5H1V1.5Zm12,0H10V2h3V5h1V1.5ZM14,13.5V10H13v3H10v1h3.5ZM1.5,14H5V13H2V10H1v3.5Z\" />\n              <GeometryDrawing Brush=\"{DynamicResource light-green}\" Geometry=\"F1M12,8H8v4H7V8H3V7H7V3H8V7h4Z\" />\n            </DrawingGroup>\n          </DrawingGroup>\n        </DrawingBrush.Drawing>\n      </DrawingBrush>\n    </Rectangle.Fill>\n  </Rectangle>\n</Viewbox>\n"
  },
  {
    "path": "Au.Editor/resources/ci/ExtensionMethod.xaml",
    "content": "﻿<!-- This file was generated by the AiToXaml tool.-->\n<!-- Tool Version: 14.0.22307.0 -->\n<Viewbox Width=\"16\" Height=\"16\" xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">\n  <Rectangle Width=\"16\" Height=\"16\">\n    <Rectangle.Fill>\n      <DrawingBrush>\n        <DrawingBrush.Drawing>\n          <DrawingGroup>\n            <DrawingGroup.Children>\n              <GeometryDrawing Brush=\"#00FFFFFF\" Geometry=\"F1M16,16L0,16 0,0 16,0z\" />\n              <GeometryDrawing Brush=\"#FFF6F6F6\" Geometry=\"F1M0.1674,2.8564L0.1674,8.4254 5.2914,11.6534 9.7554,8.3804 9.7554,2.8684 5.0504,0.386400000000001z M9.9994,5.0004L9.9994,9.5254 9.5304,9.0554 8.0004,10.5854 8.0004,12.4744 11.5254,16.0004 12.4744,16.0004 16.0004,12.4744 16.0004,10.5854 14.4694,9.0554 14.0004,9.5254 14.0004,5.0004z\" />\n              <GeometryDrawing Brush=\"#FFEFEFF0\" Geometry=\"F1M8.1904,4.4258L5.6144,5.9918 5.6144,9.4548 8.1904,7.5988z M4.9954,9.5438L4.9954,5.9708 1.7864,4.1778 1.7864,7.4968 4.9534,9.5618 4.9534,9.5768 4.9614,9.5698 4.9704,9.5768 4.9704,9.5618z M4.9954,2.1878L8.0654,3.7008 5.2014,5.4198 2.1064,3.6548z\" />\n              <GeometryDrawing Brush=\"#FF642D90\" Geometry=\"F1M8.1904,7.5986L5.6144,9.4546 5.6144,5.9926 8.1904,4.4256z M4.9704,9.5616L4.9704,9.5776 4.9614,9.5696 4.9534,9.5776 4.9534,9.5616 1.7864,7.4966 1.7864,4.1776 4.9954,5.9706 4.9954,9.5436z M4.9954,2.1876L8.0654,3.7016 5.2014,5.4196 2.1064,3.6546z M8.7544,7.8736L8.7544,3.4706 5.0414,1.5116 1.1674,3.4706 1.1674,7.8736 5.2484,10.4446z\" />\n              <GeometryDrawing Brush=\"#FF424242\" Geometry=\"F1M11,7L13,7 13,6 11,6z M11,9L13,9 13,8 11,8z M11,10L11,11.939 9.53,10.469 8.47,11.531 12,15.06 15.53,11.531 14.469,10.469 13,11.939 13,10z\" />\n            </DrawingGroup.Children>\n          </DrawingGroup>\n        </DrawingBrush.Drawing>\n      </DrawingBrush>\n    </Rectangle.Fill>\n  </Rectangle>\n</Viewbox>"
  },
  {
    "path": "Au.Editor/resources/ci/Field.xaml",
    "content": "﻿<!-- This file was generated by the AiToXaml tool.-->\n<!-- Tool Version: 14.0.22307.0 -->\n<Viewbox Width=\"16\" Height=\"16\" xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">\n  <Rectangle Width=\"16\" Height=\"16\">\n    <Rectangle.Fill>\n      <DrawingBrush>\n        <DrawingBrush.Drawing>\n          <DrawingGroup>\n            <DrawingGroup.Children>\n              <GeometryDrawing Brush=\"#00FFFFFF\" Geometry=\"F1M16,16L0,16 0,0 16,0z\" />\n              <GeometryDrawing Brush=\"#FFF6F6F6\" Geometry=\"F1M9,-0.000199999999999534L0,4.4998 0,10.7358 7,14.2358 16,9.7358 16,3.4998z\" />\n              <GeometryDrawing Brush=\"#FF00529C\" Geometry=\"F1M7,6.8818L3.236,4.9998 9,2.1178 12.764,3.9998z M9,0.9998L1,4.9998 1,9.9998 7,12.9998 15,8.9998 15,3.9998z\" />\n              <GeometryDrawing Brush=\"#FFEFEFF0\" Geometry=\"F1M9,2.1182L12.764,4.0002 7,6.8822 3.236,5.0002z\" />\n            </DrawingGroup.Children>\n          </DrawingGroup>\n        </DrawingBrush.Drawing>\n      </DrawingBrush>\n    </Rectangle.Fill>\n  </Rectangle>\n</Viewbox>"
  },
  {
    "path": "Au.Editor/resources/ci/GroupBy.xaml",
    "content": "﻿<!-- This file was generated by the AiToXaml tool.-->\n<!-- Tool Version: 14.0.22307.0 -->\n<Viewbox Width=\"16\" Height=\"16\" xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">\n  <Rectangle Width=\"16\" Height=\"16\">\n    <Rectangle.Fill>\n      <DrawingBrush>\n        <DrawingBrush.Drawing>\n          <DrawingGroup>\n            <DrawingGroup.Children>\n              <GeometryDrawing Brush=\"#00FFFFFF\" Geometry=\"F1M16,16L0,16 0,0 16,0z\" />\n              <GeometryDrawing Brush=\"#FFF6F6F6\" Geometry=\"F1M0,-0.000199999999999534L0,15.9998 5,15.9998 5,14.9998 9,14.9998 9,13.9998 16,13.9998 16,8.9998 9,8.9998 9,6.9998 16,6.9998 16,1.9998 9,1.9998 9,0.9998 5,0.9998 5,-0.000199999999999534z\" />\n              <GeometryDrawing Brush=\"#FF424242\" Geometry=\"F1M10,13L15,13 15,12 10,12z M10,11L15,11 15,10 10,10z M10,6L15,6 15,5 10,5z M10,4L15,4 15,3 10,3z M1,15L4,15 4,14 3,14 3,2 4,2 4,1 1,1z M5,9L5,14 8,14 8,13 7,13 7,10 8,10 8,9z M8,2L8,3 7,3 7,6 8,6 8,7 5,7 5,2z\" />\n            </DrawingGroup.Children>\n          </DrawingGroup>\n        </DrawingBrush.Drawing>\n      </DrawingBrush>\n    </Rectangle.Fill>\n  </Rectangle>\n</Viewbox>"
  },
  {
    "path": "Au.Editor/resources/ci/Interface.xaml",
    "content": "﻿<!-- This file was generated by the AiToXaml tool.-->\n<!-- Tool Version: 14.0.22307.0 -->\n<Viewbox Width=\"16\" Height=\"16\" xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">\n  <Rectangle Width=\"16\" Height=\"16\">\n    <Rectangle.Fill>\n      <DrawingBrush>\n        <DrawingBrush.Drawing>\n          <DrawingGroup>\n            <DrawingGroup.Children>\n              <GeometryDrawing Brush=\"#00FFFFFF\" Geometry=\"F1M16,16L0,16 0,0 16,0z\" />\n              <GeometryDrawing Brush=\"#FFF6F6F6\" Geometry=\"F1M11.5,12C9.585,12,7.898,10.759,7.272,9L5.862,9C5.326,9.985 4.29,10.625 3.125,10.625 1.402,10.625 0,9.223 0,7.5 0,5.777 1.402,4.375 3.125,4.375 4.29,4.375 5.326,5.014 5.862,6L7.272,6C7.898,4.241 9.585,3 11.5,3 13.981,3 16,5.019 16,7.5 16,9.981 13.981,12 11.5,12\" />\n              <GeometryDrawing Brush=\"#FFEFEFF0\" Geometry=\"F1M11.5,9C10.673,9 10,8.326 10,7.5 10,6.672 10.673,6 11.5,6 12.327,6 13,6.672 13,7.5 13,8.326 12.327,9 11.5,9\" />\n              <GeometryDrawing Brush=\"#FF00529C\" Geometry=\"F1M11.5,9C10.673,9 10,8.327 10,7.5 10,6.673 10.673,6 11.5,6 12.327,6 13,6.673 13,7.5 13,8.327 12.327,9 11.5,9 M11.5,4C9.738,4,8.295,5.306,8.05,7L5.185,7C4.959,6.069 4.125,5.375 3.125,5.375 1.951,5.375 1,6.326 1,7.5 1,8.674 1.951,9.625 3.125,9.625 4.125,9.625 4.959,8.931 5.185,8L8.05,8C8.295,9.694 9.738,11 11.5,11 13.433,11 15,9.433 15,7.5 15,5.567 13.433,4 11.5,4\" />\n            </DrawingGroup.Children>\n          </DrawingGroup>\n        </DrawingBrush.Drawing>\n      </DrawingBrush>\n    </Rectangle.Fill>\n  </Rectangle>\n</Viewbox>"
  },
  {
    "path": "Au.Editor/resources/ci/Keyword.xaml",
    "content": "﻿<!-- This file was generated by the AiToXaml tool.-->\n<!-- Tool Version: 14.0.22307.0 -->\n<Viewbox Width=\"16\" Height=\"16\" xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">\n  <Rectangle Width=\"16\" Height=\"16\">\n    <Rectangle.Fill>\n      <DrawingBrush>\n        <DrawingBrush.Drawing>\n          <DrawingGroup>\n            <DrawingGroup.Children>\n              <GeometryDrawing Brush=\"#00FFFFFF\" Geometry=\"F1M16,16L0,16 0,0 16,0z\" />\n              <GeometryDrawing Brush=\"#FFF6F6F6\" Geometry=\"F1M0,1.0006L0,6.0006 0,8.9996 0,12.0006 0,15.0006 13,15.0006 13,12.0006 16,12.0006 16,8.9996 15,8.9996 15,6.0006 11,6.0006 9,6.0006 9,5.0006 16,5.0006 16,2.0006 9,2.0006 9,1.0006z M8,8.9996L9,8.9996 9,12.0006 8,12.0006z\" />\n              <GeometryDrawing Brush=\"#FFEFEFF0\" Geometry=\"F1M2,4L7,4 7,3 2,3z\" />\n              <GeometryDrawing Brush=\"#FF424242\" Geometry=\"F1M7,3L2,3 2,4 7,4z M8,5L1,5 1,2 8,2z M15,10L10,10 10,11 15,11z M7,10L1,10 1,11 7,11z M12,13L1,13 1,14 12,14z M10,7L1,7 1,8 10,8z M14,7L12,7 12,8 14,8z M15,4L10,4 10,3 15,3z\" />\n            </DrawingGroup.Children>\n          </DrawingGroup>\n        </DrawingBrush.Drawing>\n      </DrawingBrush>\n    </Rectangle.Fill>\n  </Rectangle>\n</Viewbox>"
  },
  {
    "path": "Au.Editor/resources/ci/Label.xaml",
    "content": "﻿<!-- This file was generated by the AiToXaml tool.-->\n<!-- Tool Version: 14.0.22307.0 -->\n<Viewbox Width=\"16\" Height=\"16\" xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">\n  <Rectangle Width=\"16\" Height=\"16\">\n    <Rectangle.Fill>\n      <DrawingBrush>\n        <DrawingBrush.Drawing>\n          <DrawingGroup>\n            <DrawingGroup.Children>\n              <GeometryDrawing Brush=\"#00FFFFFF\" Geometry=\"F1M16,16L0,16 0,0 16,0z\" />\n              <GeometryDrawing Brush=\"#FFF6F6F6\" Geometry=\"F1M6.2793,-0.000199999999999534L0.9453,15.9998 5.0533,15.9998 6.0533,13.0008 9.9463,13.0008 10.9463,15.9998 15.0543,15.9998 9.7213,-0.000199999999999534z\" />\n              <GeometryDrawing Brush=\"#FF424242\" Geometry=\"F1M8,4L10,10 6,10z M4.333,15L5.333,12 10.667,12 11.667,15 13.667,15 9,1 7,1 2.333,15z\" />\n            </DrawingGroup.Children>\n          </DrawingGroup>\n        </DrawingBrush.Drawing>\n      </DrawingBrush>\n    </Rectangle.Fill>\n  </Rectangle>\n</Viewbox>"
  },
  {
    "path": "Au.Editor/resources/ci/LocalMethod.xaml",
    "content": "﻿<!-- This file was generated by the AiToXaml tool.-->\n<!-- Tool Version: 14.0.22307.0 -->\n<Viewbox Width=\"16\" Height=\"16\" xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">\n  <Rectangle Width=\"16\" Height=\"16\">\n    <Rectangle.Fill>\n      <DrawingBrush>\n        <DrawingBrush.Drawing>\n          <DrawingGroup>\n            <DrawingGroup.Children>\n              <GeometryDrawing Brush=\"#00FFFFFF\" Geometry=\"F1M16,16L0,16 0,0 16,0z\" />\n              <GeometryDrawing Brush=\"#FFF6F6F6\" Geometry=\"F1M3.9996,4.6141L3.9996,3.0001 -0.000399999999999956,3.0001 -0.000399999999999956,13.0001 3.9996,13.0001 3.9996,11.5411 8.0006,13.8511 11.9996,11.5411 11.9996,13.0001 16.0006,13.0001 16.0006,3.0001 11.9996,3.0001 11.9996,4.6141 8.0006,2.3041z\" />\n              <GeometryDrawing Brush=\"#FF424242\" Geometry=\"F1M13,11L13,12 15,12 15,4 13,4 13,5 14,5 14,11z M2,5L2,11 3,11 3,12 1,12 1,4 3,4 3,5z\" />\n              <GeometryDrawing Brush=\"#FF642D90\" Geometry=\"F1M11,9.8086L8.5,11.2526 8.5,8.2986 11,6.8556z M5.558,6.0226L8,4.6136 10.442,6.0226 8,7.4336z M7.5,11.2526L5,9.8086 5,6.8556 7.5,8.2986z M8,3.4586L4,5.7676 4,10.3866 8,12.6966 12,10.3866 12,5.7676z\" />\n            </DrawingGroup.Children>\n          </DrawingGroup>\n        </DrawingBrush.Drawing>\n      </DrawingBrush>\n    </Rectangle.Fill>\n  </Rectangle>\n</Viewbox>"
  },
  {
    "path": "Au.Editor/resources/ci/LocalVariable.xaml",
    "content": "﻿<!-- This file was generated by the AiToXaml tool.-->\n<!-- Tool Version: 14.0.22307.0 -->\n<Viewbox Width=\"16\" Height=\"16\" xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">\n  <Rectangle Width=\"16\" Height=\"16\">\n    <Rectangle.Fill>\n      <DrawingBrush>\n        <DrawingBrush.Drawing>\n          <DrawingGroup>\n            <DrawingGroup.Children>\n              <GeometryDrawing Brush=\"#00FFFFFF\" Geometry=\"F1M16,16L0,16 0,0 16,0z\" />\n              <GeometryDrawing Brush=\"#FFF6F6F6\" Geometry=\"F1M5.0004,4.7482L5.0004,3.0002 0.000399999999999956,3.0002 0.000399999999999956,13.0002 5.0004,13.0002 5.0004,11.1412 7.1564,12.2182 10.9994,10.2952 10.9994,13.0002 16.0004,13.0002 16.0004,3.0002 10.9994,3.0002 10.9994,4.0142 8.7334,2.8812z\" />\n              <GeometryDrawing Brush=\"#FF424242\" Geometry=\"F1M12,11L12,12 15,12 15,4 12,4 12,5 14,5 14,11z M2,5L2,11 4,11 4,12 1,12 1,4 4,4 4,5z\" />\n              <GeometryDrawing Brush=\"#FFEFEFF0\" Geometry=\"F1M7.1558,7.1558L5.5778,6.3668 8.7338,4.7888 10.3118,5.5778z\" />\n              <GeometryDrawing Brush=\"#FF00529C\" Geometry=\"F1M7.1558,7.1558L5.5778,6.3668 8.7338,4.7888 10.3118,5.5778z M8.7338,3.9998L3.9998,6.3668 3.9998,9.5228 7.1558,11.0998 11.8888,8.7328 11.8888,5.5778z\" />\n            </DrawingGroup.Children>\n          </DrawingGroup>\n        </DrawingBrush.Drawing>\n      </DrawingBrush>\n    </Rectangle.Fill>\n  </Rectangle>\n</Viewbox>"
  },
  {
    "path": "Au.Editor/resources/ci/Method.xaml",
    "content": "﻿<!-- This file was generated by the AiToXaml tool.-->\n<!-- Tool Version: 14.0.22307.0 -->\n<Viewbox Width=\"16\" Height=\"16\" xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">\n  <Rectangle Width=\"16\" Height=\"16\">\n    <Rectangle.Fill>\n      <DrawingBrush>\n        <DrawingBrush.Drawing>\n          <DrawingGroup>\n            <DrawingGroup.Children>\n              <GeometryDrawing Brush=\"#00FFFFFF\" Geometry=\"F1M16,16L0,16 0,0 16,0z\" />\n              <GeometryDrawing Brush=\"#FFF6F6F6\" Geometry=\"F1M7.5951,-0.000199999999999534L1.0001,3.3268 1.0001,11.5818 8.0701,15.9998 8.9751,15.9998 15.0001,11.7518 15.0001,3.3488 8.7131,-0.000199999999999534z\" />\n              <GeometryDrawing Brush=\"#FFEFEFF0\" Geometry=\"F1M9,13.5361L9,7.8781 13,5.3981 13,10.7161z M3,5.1021L8,7.8941 8,13.5981 3,10.4731z M12.715,4.3981L8.487,7.0201 3.565,4.2721 8.144,1.9631z\" />\n              <GeometryDrawing Brush=\"#FF642D90\" Geometry=\"F1M1.9998,3.9427L1.9998,11.0277 8.5168,15.0997 14.0008,11.2337 14.0008,3.9497 8.1558,0.8367z M3.5658,4.2717L8.1428,1.9627 12.7148,4.3977 8.4868,7.0197z M2.9998,10.4727L2.9998,5.1017 7.9998,7.8937 7.9998,13.5977z M8.9998,7.8787L12.9998,5.3977 12.9998,10.7157 8.9998,13.5357z\" />\n            </DrawingGroup.Children>\n          </DrawingGroup>\n        </DrawingBrush.Drawing>\n      </DrawingBrush>\n    </Rectangle.Fill>\n  </Rectangle>\n</Viewbox>"
  },
  {
    "path": "Au.Editor/resources/ci/Namespace.xaml",
    "content": "﻿<!-- This file was generated by the AiToXaml tool.-->\n<!-- Tool Version: 14.0.22307.0 -->\n<Viewbox Width=\"16\" Height=\"16\" xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">\n  <Rectangle Width=\"16\" Height=\"16\">\n    <Rectangle.Fill>\n      <DrawingBrush>\n        <DrawingBrush.Drawing>\n          <DrawingGroup>\n            <DrawingGroup.Children>\n              <GeometryDrawing Brush=\"#00FFFFFF\" Geometry=\"F1M16,16L0,16 0,0 16,0z\" />\n              <GeometryDrawing Brush=\"#FFF6F6F6\" Geometry=\"F1M5.7388,15.0156C2.6498,14.9806,2.3168,12.8516,2.3168,11.9376L2.3168,9.9206C2.3168,9.5936,2.2498,9.4896,2.2498,9.4886L1.3128,9.4286 1.2498,8.4986 1.2498,6.5626C1.2498,6.5626 2.1898,6.5036 2.1918,6.5036 2.2498,6.5036 2.3168,6.3896 2.3168,6.0516L2.3168,4.0896C2.3168,2.1406,3.5638,1.0086,5.7388,0.984599999999999L6.7498,0.973599999999999 6.7498,4.0196 5.7748,4.0436C5.7298,4.0446 5.6998,4.0486 5.6818,4.0526 5.6878,4.0746 5.6628,4.1676 5.6628,4.3496L5.6628,6.2776C5.6628,6.9526 5.4778,7.5306 5.1298,7.9816 5.4778,8.4316 5.6628,9.0096 5.6628,9.6886L5.6628,11.5956C5.6628,11.7706 5.6768,11.8866 5.6898,11.9586 5.7128,11.9606 6.7498,11.9846 6.7498,11.9846L6.7498,15.0276z M9.2598,11.9846L10.2378,11.9626C10.2738,11.9616 10.3038,11.9596 10.3268,11.9566 10.3378,11.8936 10.3538,11.7786 10.3538,11.5956L10.3538,9.6886C10.3538,9.0096 10.5378,8.4316 10.8838,7.9816 10.5378,7.5306 10.3538,6.9526 10.3538,6.2776L10.3538,4.3496C10.3538,4.1826 10.3328,4.0906 10.3198,4.0476 10.3178,4.0476 9.2598,4.0196 9.2598,4.0196L9.2598,0.973599999999999 10.2708,0.984599999999999C12.4378,1.0086,13.6798,2.1406,13.6798,4.0896L13.6798,6.0516C13.6798,6.4026,13.7508,6.5136,13.7518,6.5146L14.6878,6.5746 14.7398,7.5006 14.7398,9.4366 13.8048,9.4966C13.7288,9.5126,13.6798,9.6436,13.6798,9.9206L13.6798,11.9376C13.6798,12.8516,13.3478,14.9806,10.2718,15.0156L9.2598,15.0276z\" />\n              <GeometryDrawing Brush=\"#FF424242\" Geometry=\"F1M13.7397,8.499C13.0337,8.544,12.6797,9.019,12.6797,9.921L12.6797,11.937C12.6797,13.305,11.8737,13.997,10.2607,14.016L10.2607,12.963C10.6567,12.954 10.9377,12.845 11.1037,12.635 11.2707,12.425 11.3537,12.079 11.3537,11.596L11.3537,9.688C11.3537,8.763,11.8027,8.201,12.7007,8L12.7007,7.979C11.8027,7.765,11.3537,7.198,11.3537,6.277L11.3537,4.35C11.3537,3.498,10.9897,3.062,10.2607,3.044L10.2607,1.984C11.8737,2.002,12.6797,2.705,12.6797,4.09L12.6797,6.052C12.6797,6.972,13.0337,7.456,13.7397,7.501z M5.7497,14.016C4.1277,13.997,3.3167,13.305,3.3167,11.937L3.3167,9.921C3.3167,9.019,2.9607,8.544,2.2497,8.499L2.2497,7.501C2.9607,7.456,3.3167,6.972,3.3167,6.052L3.3167,4.09C3.3167,2.705,4.1277,2.002,5.7497,1.984L5.7497,3.044C5.0257,3.062,4.6627,3.498,4.6627,4.35L4.6627,6.277C4.6627,7.198,4.2097,7.765,3.3027,7.979L3.3027,8C4.2097,8.201,4.6627,8.763,4.6627,9.688L4.6627,11.596C4.6627,12.083 4.7437,12.431 4.9057,12.638 5.0677,12.846 5.3487,12.954 5.7497,12.963z\" />\n            </DrawingGroup.Children>\n          </DrawingGroup>\n        </DrawingBrush.Drawing>\n      </DrawingBrush>\n    </Rectangle.Fill>\n  </Rectangle>\n</Viewbox>"
  },
  {
    "path": "Au.Editor/resources/ci/Operator.xaml",
    "content": "﻿<!-- This file was generated by the AiToXaml tool.-->\n<!-- Tool Version: 14.0.22307.0 -->\n<Viewbox Width=\"16\" Height=\"16\" xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">\n  <Rectangle Width=\"16\" Height=\"16\">\n    <Rectangle.Fill>\n      <DrawingBrush>\n        <DrawingBrush.Drawing>\n          <DrawingGroup>\n            <DrawingGroup.Children>\n              <GeometryDrawing Brush=\"#00FFFFFF\" Geometry=\"F1M16,16L0,16 0,0 16,0z\" />\n              <GeometryDrawing Brush=\"#FFF6F6F6\" Geometry=\"F1M16,8C16,12.418 12.418,16 8,16 3.582,16 0,12.418 0,8 0,3.582 3.582,0 8,0 12.418,0 16,3.582 16,8\" />\n              <GeometryDrawing Brush=\"#FF424242\" Geometry=\"F1M10.1064,11.95L8.9184,11.925 11.0004,8 12.1604,8z\" />\n              <GeometryDrawing Brush=\"#FF424242\" Geometry=\"F1M8.0331,1C4.1671,1 1.0331,4.134 1.0331,8 1.0331,11.866 4.1671,15 8.0331,15 11.8991,15 15.0331,11.866 15.0331,8 15.0331,4.134 11.8991,1 8.0331,1 M8.0331,2C11.3411,2 14.0331,4.692 14.0331,8 14.0331,11.308 11.3411,14 8.0331,14 4.7251,14 2.0331,11.308 2.0331,8 2.0331,4.692 4.7251,2 8.0331,2\" />\n              <GeometryDrawing Brush=\"#FF424242\" Geometry=\"F1M8,5L6,5 6,3 5,3 5,5 3,5 3,6 5,6 5,8 6,8 6,6 8,6z\" />\n              <GeometryDrawing Brush=\"#FF424242\" Geometry=\"F1M13,6L9,6 9,5 13,5z\" />\n              <GeometryDrawing Brush=\"#FF424242\" Geometry=\"F1M8,10L4,10 4,9 8,9z\" />\n              <GeometryDrawing Brush=\"#FF424242\" Geometry=\"F1M8,12L4,12 4,11 8,11z\" />\n              <GeometryDrawing Brush=\"#FFEFEFF0\" Geometry=\"F1M9,5L13,5 13,6 9,6z M10.106,11.95L8.918,11.924 11,8 12.16,8z M8,6L6,6 6,8 5,8 5,6 3,6 3,5 5,5 5,3 6,3 6,5 8,5z M8,10L4,10 4,9 8,9z M8,12L4,12 4,11 8,11z M8.033,2C4.725,2 2.033,4.692 2.033,8 2.033,11.308 4.725,14 8.033,14 11.341,14 14.033,11.308 14.033,8 14.033,4.692 11.341,2 8.033,2\" />\n            </DrawingGroup.Children>\n          </DrawingGroup>\n        </DrawingBrush.Drawing>\n      </DrawingBrush>\n    </Rectangle.Fill>\n  </Rectangle>\n</Viewbox>"
  },
  {
    "path": "Au.Editor/resources/ci/OverlayAbstract.xaml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--This file is compatible with Silverlight-->\n<Canvas xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\" Name=\"svg11\" Width=\"16\" Height=\"16\">\n  <Canvas.RenderTransform>\n    <TranslateTransform X=\"0\" Y=\"0\"/>\n  </Canvas.RenderTransform>\n  <Canvas.Resources/>\n  <!--Unknown tag: metadata-->\n  <!--Unknown tag: sodipodi:namedview-->\n  <TextBlock xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" FontSize=\"9.33333302\" FontWeight=\"Normal\" FontFamily=\"Verdana\" FontStyle=\"normal\" Foreground=\"#FF0046FD\" Canvas.Left=\"7.4077458\" Canvas.Top=\"-3.2\" Name=\"text18\">\n    <Span FontSize=\"9.33333302\">a</Span>\n  </TextBlock>\n</Canvas>\n"
  },
  {
    "path": "Au.Editor/resources/ci/OverlayInternal.xaml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--This file is compatible with Silverlight-->\n<Canvas xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\" Name=\"svg11\" Width=\"16\" Height=\"16\">\n  <Canvas.RenderTransform>\n    <TranslateTransform X=\"0\" Y=\"0\"/>\n  </Canvas.RenderTransform>\n  <Canvas.Resources/>\n  <!--Unknown tag: metadata-->\n  <!--Unknown tag: sodipodi:namedview-->\n  <Canvas Name=\"outline\">\n    <Canvas.RenderTransform>\n      <TranslateTransform X=\"-8.8813559\" Y=\"-8.9491525\"/>\n    </Canvas.RenderTransform>\n    <Path xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" Name=\"path4\" Fill=\"#FFF6F6F6\" Opacity=\"0\" Data=\"M 0 0 H 16 V 16 H 0 Z\"/>\n    <Path xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" Name=\"path6\" Fill=\"#FFF6F6F6\" Data=\"M 9.5 4 H 6.414 L 9 1.414 V 0 H 3.382 L 0 6.764 V 8 h 2.382 l -2 4 H 2.914 L 9.5 5.414 Z m 4.071 5 C 13.295 9 12.894 9.055 12.5 9.305 A 1.996 1.996 0 0 0 11.429 9 C 10.112 9 9 10.137 9 11.482 c 0 0.594 0.196 1.188 0.561 1.656 0.293 0.355 2.246 2.529 2.246 2.529 L 12.104 16 h 0.909 l 0.3 -0.352 c 0 0 1.836 -2.159 2.148 -2.535 0.343 -0.442 0.54 -1.037 0.54 -1.631 C 16 10.137 14.888 9 13.571 9 Z\"/>\n  </Canvas>\n  <Path xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" Name=\"color_x5F_importance\" Fill=\"#FFC27D1A\" Data=\"m -4.8813559 -3.9491525 3.99999997 -4 H -4.8813559 l -3 6 h 3.018 l -2.018 4 h 0.518 l 5.99999997 -6 z\"/>\n  <Path xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" Name=\"not_x5F_bg\" Fill=\"#FF424242\" Data=\"m 4.6896441 1.0508475 c -0.527 0 -0.86 0.747 -1.071 1.25 -0.211 -0.503 -0.545 -1.25 -1.071 -1.25 -0.728 0 -1.429 0.664 -1.429 1.482 0 0.375 0.127 0.756 0.329 1.018 0.29 0.35 2.222 2.5 2.222 2.5 0 0 1.829 -2.15 2.119 -2.5 0.203 -0.262 0.33 -0.643 0.33 -1.018 0 -0.818 -0.7 -1.482 -1.429 -1.482 z\"/>\n</Canvas>\n"
  },
  {
    "path": "Au.Editor/resources/ci/OverlayPrivate.xaml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--This file is compatible with Silverlight-->\n<Canvas xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\" Name=\"svg14\" Width=\"16\" Height=\"16\">\n  <Canvas.RenderTransform>\n    <TranslateTransform X=\"0\" Y=\"0\"/>\n  </Canvas.RenderTransform>\n  <Canvas.Resources/>\n  <!--Unknown tag: metadata-->\n  <!--Unknown tag: sodipodi:namedview-->\n  <Canvas Name=\"outline\">\n    <Canvas.RenderTransform>\n      <TranslateTransform X=\"-8.9491525\" Y=\"-8\"/>\n    </Canvas.RenderTransform>\n    <Path xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" Name=\"path4\" Fill=\"#FFF6F6F6\" Data=\"M 6.414 4 9 1.414 V 0 H 3.382 L 0 6.764 V 8 h 2.382 l -2 4 H 2.914 L 9.5 5.414 V 4 Z M 9 16 9.005 12.016 c 0 0 -0.225 -3.984 3.589 -3.984 1.532 0 3.406 0.967 3.406 3.976 V 16 Z\"/>\n    <Path xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" Name=\"path6\" Fill=\"#FFF0EFF1\" Data=\"m 13 14 h -1 v -0.959 h 1 z\"/>\n  </Canvas>\n  <Path xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" Name=\"color_x5F_importance\" Fill=\"#FFC27D1A\" Data=\"m -4.9491525 -3 3.99999996 -4 H -4.9491525 l -3 6 h 3.018 l -2.018 4 h 0.518 l 5.99999996 -6 z\"/>\n  <Canvas Name=\"not_x5F_bg\">\n    <Canvas.RenderTransform>\n      <TranslateTransform X=\"-8.9491525\" Y=\"-8\"/>\n    </Canvas.RenderTransform>\n    <Path xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" Name=\"path10\" Fill=\"#FF424242\" Data=\"m 11.019 11.563 c 0.059 -0.729 0.334 -1.541 1.575 -1.541 1.249 0 1.391 0.795 1.404 1.541 h 0.966 c -0.085 -1.119 -0.144 -2.531 -2.37 -2.531 -2.396 0 -2.445 1.406 -2.576 2.531 z M 14 12 h -4 v 3 h 5 v -3 z m -1 2 h -1 v -1 h 1 z\"/>\n  </Canvas>\n  <Path xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" Name=\"not_x5F_fg\" Fill=\"#FFF0EFF1\" Data=\"m 3.0508475 5 h 1 v 1 h -1 z\"/>\n</Canvas>\n"
  },
  {
    "path": "Au.Editor/resources/ci/OverlayProtected.xaml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--This file is compatible with Silverlight-->\n<Canvas xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\" Name=\"svg11\" Width=\"16\" Height=\"16\">\n  <Canvas.RenderTransform>\n    <TranslateTransform X=\"0\" Y=\"0\"/>\n  </Canvas.RenderTransform>\n  <Canvas.Resources/>\n  <!--Unknown tag: metadata-->\n  <!--Unknown tag: sodipodi:namedview-->\n  <Canvas Name=\"outline\">\n    <Canvas.RenderTransform>\n      <MatrixTransform Matrix=\"1.2 0 0 1.2 -10.740369 -10.713749\"/>\n    </Canvas.RenderTransform>\n    <Path xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" Name=\"path4\" Fill=\"#FFF6F6F6\" Opacity=\"0\" Data=\"M 0 0 H 16 V 16 H 0 Z\"/>\n    <Path xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" Name=\"path6\" Fill=\"#FFF6F6F6\" Data=\"M 6.414 4 9 1.414 V 0 H 3.382 L 0 6.764 V 8 h 2.382 l -2 4 H 2.914 L 9.5 5.414 V 4 Z M 14.607 13.545 16 12.5 V 11 h -2.156 l -0.6 -2 h -1.488 l -0.6 2 H 9 v 1.5 L 10.393 13.545 9.656 16 h 1.677 L 12.5 15.125 13.667 16 h 1.677 z\"/>\n  </Canvas>\n  <Path xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" Name=\"color_x5F_importance\" Fill=\"#FFC27D1A\" StrokeThickness=\"1.20000005\" Data=\"m -5.0331559 -4.1491525 4.79999997 -4.8 H -5.0331559 l -3.6 7.2 h 3.6216 l -2.4216 4.8 h 0.6216 l 7.19999997 -7.2 z\"/>\n  <Path xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" Name=\"not_x5F_bg\" Fill=\"#FF424242\" StrokeThickness=\"1.20000005\" Data=\"m 5.3676135 5.024414 1.8612 -1.3956 H 4.9488136 l -0.72 -2.4 -0.72 2.4 h -2.28 l 1.8612 1.3956 -0.6612 2.2044 1.8 -1.35 1.7999999 1.35 z\"/>\n</Canvas>\n"
  },
  {
    "path": "Au.Editor/resources/ci/OverlayStatic.xaml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--This file is compatible with Silverlight-->\n<Canvas xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\" Name=\"svg11\" Width=\"16\" Height=\"16\">\n  <Canvas.RenderTransform>\n    <TranslateTransform X=\"0\" Y=\"0\"/>\n  </Canvas.RenderTransform>\n  <Canvas.Resources/>\n  <!--Unknown tag: metadata-->\n  <!--Unknown tag: sodipodi:namedview-->\n  <TextBlock xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" FontSize=\"9.33333302\" FontWeight=\"Normal\" FontFamily=\"Verdana\" FontStyle=\"normal\" Foreground=\"#FF0046FD\" Canvas.Left=\"7.3898301\" Canvas.Top=\"-3.2\" Name=\"text18\">\n    <Span FontSize=\"9.33333302\">s</Span>\n  </TextBlock>\n</Canvas>\n"
  },
  {
    "path": "Au.Editor/resources/ci/Property.xaml",
    "content": "﻿<!-- This file was generated by the AiToXaml tool.-->\n<!-- Tool Version: 14.0.22307.0 -->\n<Viewbox Width=\"16\" Height=\"16\" xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">\n  <Rectangle Width=\"16\" Height=\"16\">\n    <Rectangle.Fill>\n      <DrawingBrush>\n        <DrawingBrush.Drawing>\n          <DrawingGroup>\n            <DrawingGroup.Children>\n              <GeometryDrawing Brush=\"#00FFFFFF\" Geometry=\"F1M16,16L0,16 0,0 16,0z\" />\n              <GeometryDrawing Brush=\"#FFF6F6F6\" Geometry=\"F1M16,5.5C16,8.538 13.538,11 10.5,11 10.225,11 9.957,10.973 9.693,10.934 9.667,10.93 9.641,10.927 9.615,10.922 9.337,10.877 9.066,10.816 8.804,10.731L4.268,15.268C3.795,15.74 3.167,16 2.5,16 1.833,16 1.205,15.74 0.731999999999999,15.268 -0.242000000000001,14.293 -0.242000000000001,12.707 0.731999999999999,11.732L5.269,7.196C5.184,6.934 5.123,6.662 5.078,6.384 5.073,6.359 5.07,6.333 5.066,6.307 5.027,6.043 5,5.775 5,5.5 5,2.462 7.462,0 10.5,0 13.538,0 16,2.462 16,5.5\" />\n              <GeometryDrawing Brush=\"#FF424242\" Geometry=\"F1M15,5.5C15,7.985 12.985,10 10.5,10 9.807,10 9.158,9.83 8.571,9.55L3.561,14.561C3.268,14.854 2.884,15 2.5,15 2.116,15 1.732,14.854 1.439,14.561 0.853999999999999,13.975 0.853999999999999,13.025 1.439,12.439L6.45,7.429C6.17,6.842 6,6.193 6,5.5 6,3.015 8.015,1 10.5,1 11.193,1 11.842,1.17 12.429,1.45L9.636,4.243 11.757,6.364 14.55,3.571C14.83,4.158,15,4.807,15,5.5\" />\n            </DrawingGroup.Children>\n          </DrawingGroup>\n        </DrawingBrush.Drawing>\n      </DrawingBrush>\n    </Rectangle.Fill>\n  </Rectangle>\n</Viewbox>"
  },
  {
    "path": "Au.Editor/resources/ci/Region.xaml",
    "content": "﻿<!-- This file was generated by the AiToXaml tool.-->\n<!-- Tool Version: 14.0.22307.0 -->\n<Viewbox Width=\"16\" Height=\"16\" xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">\n  <Rectangle Width=\"16\" Height=\"16\">\n    <Rectangle.Fill>\n      <DrawingBrush>\n        <DrawingBrush.Drawing>\n          <DrawingGroup>\n            <DrawingGroup.Children>\n              <GeometryDrawing Brush=\"#00FFFFFF\" Geometry=\"F1M16,16L0,16 0,0 16,0z\" />\n              <GeometryDrawing Brush=\"#FFF6F6F6\" Geometry=\"F1M1.9998,-0.000199999999999534L1.9998,15.9998 15.0008,15.9998 15.0008,4.3788 10.6208,-0.000199999999999534z\" />\n              <GeometryDrawing Brush=\"#FF424242\" Geometry=\"F1M14,8L13,8 13,9 14,9z M12,8L11,8 11,9 12,9z M10,8L9,8 9,9 10,9z M4,8L3,8 3,9 4,9z M8,8L7,8 7,9 8,9z M6,8L5,8 5,9 6,9z M10,5L12.793,5 10,2.207z M3,7L3,1 10.207,1 14,4.793 14,7 13,7 13,6 9,6 9,2 4,2 4,7z M14,10L14,15 3,15 3,10 4,10 4,14 13,14 13,10z\" />\n              <GeometryDrawing Brush=\"#FFEFEFF0\" Geometry=\"F1M10,2.207L10,5 12.793,5z\" />\n              <GeometryDrawing Brush=\"#FFEFEFF0\" Geometry=\"F1M4,14L13,14 13,10 4,10z\" />\n              <GeometryDrawing Brush=\"#FFEFEFF0\" Geometry=\"F1M13,7L13,6 9,6 9,2 4,2 4,7z\" />\n              <GeometryDrawing Brush=\"#FFF0EFF1\" Geometry=\"F1M10,2.207L10,5 12.793,5z\" />\n              <GeometryDrawing Brush=\"#FFF0EFF1\" Geometry=\"F1M4,14L13,14 13,10 4,10z\" />\n              <GeometryDrawing Brush=\"#FFF0EFF1\" Geometry=\"F1M13,7L13,6 9,6 9,2 4,2 4,7z\" />\n            </DrawingGroup.Children>\n          </DrawingGroup>\n        </DrawingBrush.Drawing>\n      </DrawingBrush>\n    </Rectangle.Fill>\n  </Rectangle>\n</Viewbox>"
  },
  {
    "path": "Au.Editor/resources/ci/Snippet.xaml",
    "content": "﻿<!-- This file was generated by the AiToXaml tool.-->\n<!-- Tool Version: 14.0.22307.0 -->\n<Viewbox Width=\"16\" Height=\"16\" xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">\n  <Rectangle Width=\"16\" Height=\"16\">\n    <Rectangle.Fill>\n      <DrawingBrush>\n        <DrawingBrush.Drawing>\n          <DrawingGroup>\n            <DrawingGroup.Children>\n              <GeometryDrawing Brush=\"#00FFFFFF\" Geometry=\"F1M16,16L0,16 0,0 16,0z\" />\n              <GeometryDrawing Brush=\"#FFF6F6F6\" Geometry=\"F1M2,0L15,0 15,15 2,15z\" />\n              <GeometryDrawing Brush=\"#FF414141\" Geometry=\"F1M7,14L8,14 8,13 7,13z\" />\n              <GeometryDrawing Brush=\"#FF414141\" Geometry=\"F1M5,14L6,14 6,13 5,13z\" />\n              <GeometryDrawing Brush=\"#FF414141\" Geometry=\"F1M3,14L4,14 4,13 3,13z\" />\n              <GeometryDrawing Brush=\"#FF414141\" Geometry=\"F1M9,14L10,14 10,13 9,13z\" />\n              <GeometryDrawing Brush=\"#FF414141\" Geometry=\"F1M11,14L12,14 12,13 11,13z\" />\n              <GeometryDrawing Brush=\"#FF414141\" Geometry=\"F1M13,14L14.001,14 14.001,13 13,13z\" />\n              <GeometryDrawing Brush=\"#FF414141\" Geometry=\"F1M3,1L3,12 4,12 4,2 13,2 13,12 14,12 14,1z\" />\n              <GeometryDrawing Brush=\"#FFF0EFF1\" Geometry=\"F1M13,12L4,12 4,2 13,2z\" />\n            </DrawingGroup.Children>\n          </DrawingGroup>\n        </DrawingBrush.Drawing>\n      </DrawingBrush>\n    </Rectangle.Fill>\n  </Rectangle>\n</Viewbox>"
  },
  {
    "path": "Au.Editor/resources/ci/Structure.xaml",
    "content": "﻿<!-- This file was generated by the AiToXaml tool.-->\n<!-- Tool Version: 14.0.22307.0 -->\n<Viewbox Width=\"16\" Height=\"16\" xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">\n  <Rectangle Width=\"16\" Height=\"16\">\n    <Rectangle.Fill>\n      <DrawingBrush>\n        <DrawingBrush.Drawing>\n          <DrawingGroup>\n            <DrawingGroup.Children>\n              <GeometryDrawing Brush=\"#00FFFFFF\" Geometry=\"F1M16,16L0,16 0,0 16,0z\" />\n              <GeometryDrawing Brush=\"#FFF6F6F6\" Geometry=\"F1M1,15L1,6 5,6 5,1 15,1 15,10 11,10 11,15z\" />\n              <GeometryDrawing Brush=\"#FF00539C\" Geometry=\"F1M14,9L11,9 11,6 6,6 6,2 14,2z M10,14L2,14 2,7 10,7z\" />\n            </DrawingGroup.Children>\n          </DrawingGroup>\n        </DrawingBrush.Drawing>\n      </DrawingBrush>\n    </Rectangle.Fill>\n  </Rectangle>\n</Viewbox>"
  },
  {
    "path": "Au.Editor/resources/ci/TypeParameter.xaml",
    "content": "﻿<!-- This file was generated by the AiToXaml tool.-->\n<!-- Tool Version: 14.0.22307.0 -->\n<Viewbox Width=\"16\" Height=\"16\" xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">\n  <Rectangle Width=\"16\" Height=\"16\">\n    <Rectangle.Fill>\n      <DrawingBrush>\n        <DrawingBrush.Drawing>\n          <DrawingGroup>\n            <DrawingGroup.Children>\n              <GeometryDrawing Brush=\"#00FFFFFF\" Geometry=\"F1M16,16L0,16 0,0 16,0z\" />\n              <GeometryDrawing Brush=\"#FFF6F6F6\" Geometry=\"F1M2.9997,1.9996L2.9997,5.0006 -0.000300000000000189,5.0006 -0.000300000000000189,13.0006 8.9997,13.0006 8.9997,9.9996 11.9997,9.9996 11.9997,1.9996z\" />\n              <GeometryDrawing Brush=\"#FFEFEFF0\" Geometry=\"F1M2,11L7,11 7,7 2,7z M10,4L10,8 8,8 8,6 5,6 5,4z\" />\n              <GeometryDrawing Brush=\"#FF00529C\" Geometry=\"F1M10,8L8,8 8,6 5,6 5,4 10,4z M7,11L2,11 2,7 7,7z M4,3L4,6 1,6 1,12 8,12 8,9 11,9 11,3z\" />\n            </DrawingGroup.Children>\n          </DrawingGroup>\n        </DrawingBrush.Drawing>\n      </DrawingBrush>\n    </Rectangle.Fill>\n  </Rectangle>\n</Viewbox>"
  },
  {
    "path": "Au.Editor/xAI/AI search.cs",
    "content": "using System.Text.Json.Nodes;\nusing System.Net.Http;\nusing System.Buffers.Text;\nusing LA;\nusing System.Text.Json;\nusing System.Text.Json.Serialization;\nusing System.Windows.Media;\nusing System.Windows.Media.Imaging;\nusing System.Windows;\n\nnamespace AI;\n\nclass Embeddings(AiEmbeddingModel model) {\n\t/// <summary>\n\t/// Calls AI embeddings API to get embedding vectors for any number of strings or multimodal data.\n\t/// </summary>\n\t/// <param name=\"input\">0 or more.</param>\n\t/// <param name=\"forDatabase\">true if for a vector database. false if for a query. Some API have optimization parameters for it.</param>\n\t/// <param name=\"getInt8\"><c>true</c> - get list of <c>sbyte[]</c>. <c>false</c> - get list of <c>float[]</c>.</param>\n\t/// <exception cref=\"OperationCanceledException\"></exception>\n\t/// <exception cref=\"Exception\"></exception>\n\tpublic List<Array> CreateEmbeddings(IList<EmInput> input, bool forDatabase, bool getInt8 = false, CancellationToken cancel = default) {\n\t\tif (input.Count == 0) return [];\n\t\t\n\t\tvar headers = model.GetHeaders();\n\t\tvar allVectors = new List<Array>();\n\t\tlong downloadSize = 0;\n\t\tdialog pd = null;\n\t\tusing CancellationTokenSource cts = cancel != default ? CancellationTokenSource.CreateLinkedTokenSource(cancel) : new();\n\t\tif (input.Count == 1) {\n\t\t\t_GetBatch(input);\n\t\t} else {\n\t\t\ttry {\n\t\t\t\tvar batch = new List<EmInput>();\n\t\t\t\tint sizeSum = 0;\n\t\t\t\tint tokensToDisplay = 0;\n\t\t\t\tvar lim = model.limits;\n\t\t\t\t\n\t\t\t\tforeach (var (i, data) in input.Index()) {\n\t\t\t\t\tint nextSize = sizeSum + data.Size;\n\t\t\t\t\tif ((lim.maxTokens > 0 && nextSize / 2 >= lim.maxTokens) || (lim.maxInputs > 0 && batch.Count == lim.maxInputs) || (lim.maxSize > 0 && nextSize >= lim.maxSize)) {\n\t\t\t\t\t\tif (batch.Count == 0) batch.Add(data); //try anyway\n\t\t\t\t\t\tif (pd == null) {\n\t\t\t\t\t\t\tvar url = string.Concat(model.url.Chunk(50).Select(o => { var s = new string(o); return s.Length < 50 ? s : s.Insert(s.LastIndexOfAny('/', ':') + 1, \"\\n  \"); }));\n\t\t\t\t\t\t\tpd = dialog.showProgress(false, \"Creating AI search vectors\", flags: DFlags.ExpandDown, expandedText: $\"API: {model.api}\\nModel: {model.model}\\nURL: {url}\", footer: \" \");\n\t\t\t\t\t\t\tpd.Destroyed += _pd_Destroyed;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tint batchTokens = _GetBatch(batch);\n\t\t\t\t\t\tbool noTokens = batchTokens == 0;\n\t\t\t\t\t\ttokensToDisplay += noTokens ? sizeSum / 3 : batchTokens;\n\t\t\t\t\t\tsizeSum = 0;\n\t\t\t\t\t\tbatch.Clear();\n\t\t\t\t\t\tif (i < input.Count - 1) {\n\t\t\t\t\t\t\tif (!pd.IsOpen) { pd = null; throw new OperationCanceledException(); } //clicked Cancel\n\t\t\t\t\t\t\tpd.Send.Progress(Math2.PercentFromValue(input.Count, allVectors.Count));\n\t\t\t\t\t\t\tpd.Send.ChangeFooterText($\"Tokens: {(noTokens ? \"~\" : null)}{tokensToDisplay.ToString(\"N0\")}.  Downloaded: {downloadSize / (1024 * 1024)} MB.\", false);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbatch.Add(data);\n\t\t\t\t\tsizeSum += data.Size;\n\t\t\t\t}\n\t\t\t\tif (batch.Count > 0) _GetBatch(batch);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tpd?.Destroyed -= _pd_Destroyed;\n\t\t\t\tpd?.Send.Close();\n\t\t\t}\n\t\t\t\n\t\t\tvoid _pd_Destroyed(DEventArgs _) { cts.Cancel(); }\n\t\t}\n\t\treturn allVectors;\n\t\t\n\t\tint _GetBatch(IList<EmInput> input) {\n\t\t\tobject post = model.GetPostData(input, isQuery: !forDatabase);\n\t\t\t//print.it(JsonSerializer.Serialize(post)); throw null;\n\t\t\tvar r = model.Post(post, headers: headers, cts.Token);\n\t\t\tvar bytes = r.Bytes();\n\t\t\tdownloadSize += bytes.Length; //never mind: probably downloads less, because of compression\n\t\t\tvar json = JsonNode.Parse(bytes);\n\t\t\t\n\t\t\t//print.it(json.ToJsonString(new(System.Text.Json.JsonSerializerDefaults.Web) { WriteIndented = true }));\n\t\t\t//print.it(json[\"usage\"].ToJsonString(new(System.Text.Json.JsonSerializerDefaults.Web) { WriteIndented = true }));\n\t\t\t//print.it(json[\"data\"].ToJsonString(new(System.Text.Json.JsonSerializerDefaults.Web) { WriteIndented = true }));\n\t\t\t\n\t\t\tvar ja = model.GetVectors(json);\n\t\t\tforeach (var v in ja) {\n\t\t\t\tfloat[] f = null; sbyte[] b = null;\n\t\t\t\tif (v is JsonArray aj) {\n\t\t\t\t\tif (aj.Count != model.dimensions) throw new InvalidOperationException($\"Returned array length ({aj.Count} elements) does not match model's dimensions ({model.dimensions}).\");\n\t\t\t\t\tif (aj.All(o => o.AsValue().TryGetValue(out int i_))) {\n\t\t\t\t\t\tb = new sbyte[aj.Count];\n\t\t\t\t\t\tfor (int j = 0; j < aj.Count; j++) b[j] = (sbyte)(int)aj[j];\n\t\t\t\t\t} else {\n\t\t\t\t\t\tf = new float[aj.Count];\n\t\t\t\t\t\tfor (int j = 0; j < aj.Count; j++) f[j] = (float)aj[j];\n\t\t\t\t\t}\n\t\t\t\t} else { //OpenAI, Voyage and Cohere can return float[] as base64 string\n\t\t\t\t\tvar base64 = v.GetValue<string>();\n\t\t\t\t\tvar t = Convert.FromBase64String(base64);\n\t\t\t\t\tif (t.Length == model.dimensions) b = MemoryMarshal.Cast<byte, sbyte>(t).ToArray();\n\t\t\t\t\telse if (t.Length == model.dimensions * 4) f = MemoryMarshal.Cast<byte, float>(t).ToArray();\n\t\t\t\t\telse throw new InvalidOperationException($\"Returned data length ({t.Length} bytes) does not match model's dimensions ({model.dimensions}).\");\n\t\t\t\t}\n\t\t\t\tif (getInt8) {\n\t\t\t\t\tif (b is null) {\n\t\t\t\t\t\tb = new sbyte[f.Length];\n\t\t\t\t\t\tfor (int j = 0; j < f.Length; j++) b[j] = (sbyte)Math.Round(f[j] * 127);\n#if DEBUG\n\t\t\t\t\t\tsbyte min = b.Min(), max = b.Max();\n\t\t\t\t\t\tif (min < -100 || max > 100) Debug_.Print($\"{min}, {max}\");\n#endif\n\t\t\t\t\t}\n\t\t\t\t\tallVectors.Add(b);\n\t\t\t\t} else {\n\t\t\t\t\tif (f is null) {\n\t\t\t\t\t\tf = new float[b.Length];\n\t\t\t\t\t\tfor (int j = 0; j < b.Length; j++) f[j] = b[j] / 127f;\n\t\t\t\t\t}\n\t\t\t\t\tallVectors.Add(f);\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\treturn model.GetTokens(json);\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Calls AI embeddings API to get embedding vector for single string or multimodal data used for a query.\n\t/// </summary>\n\t/// <exception cref=\"OperationCanceledException\"></exception>\n\t/// <exception cref=\"Exception\"></exception>\n\tpublic float[] CreateEmbedding(EmInput input, CancellationToken cancel = default) => CreateEmbeddings([input], forDatabase: false, cancel: cancel)[0] as float[];\n\t\n\tpublic static string VectorDir { get; } = folders2.LaDataRoaming + @\"AI\\Embedding\"; //note: don't use the common app data folder\n\t\n\tList<EmVector> _GetEmbeddings(string dbPath, bool compact, Func<(List<string> names, List<EmInput> datas)> getData, CancellationToken cancel) {\n\t\t_EmHash newHash = _Hash(dbPath), oldHash = default;\n\t\tstring emPath = VectorDir + $@\"\\{model.GetType()}-{pathname.getNameNoExt(dbPath)}.bin\";\n\t\tvar emFile = new _EmStorageFile(emPath);\n\t\tList<EmVector> ems = null;\n\t\tbool retried = false; gRetry:\n\t\tif (filesystem.exists(emPath).File) {\n\t\t\ttry { ems = emFile.Load(out oldHash); }\n\t\t\tcatch (Exception ex) when (emFile.Reading && ex is not OutOfMemoryException) { print.warning(ex); }\n\t\t\t\n\t\t\t//emFile.PrintUploadIfAtHome(model, newHash);\n\t\t}\n\t\tbool exists = ems != null && oldHash.modelParams == newHash.modelParams;\n\t\tif (exists && oldHash.hash == newHash.hash) return ems;\n\t\t\n\t\tif (!exists && !retried) {\n\t\t\tretried = true;\n\t\t\tif (emFile.TryDownload(model, newHash)) goto gRetry;\n\t\t}\n\t\t\n\t\tvar (names, datas) = getData();\n\t\tList<(string name, EmInput data, Array vec)> aSame = null;\n\t\t\n\t\tif (exists) { //get embeddings only for new and changed texts\n\t\t\tvar dOld = emFile.LoadForUpdate();\n\t\t\taSame = new(dOld.Count);\n\t\t\tint j = 0;\n\t\t\tfor (int i = 0; i < names.Count; i++) {\n\t\t\t\tstring name = names[i];\n\t\t\t\tEmInput data = datas[i];\n\t\t\t\tif (dOld.TryGetValue(name, out var old) && old.hash == data.GetHash()) {\n\t\t\t\t\taSame.Add((name, data, old.vec));\n\t\t\t\t} else {\n\t\t\t\t\tnames[j] = name;\n\t\t\t\t\tdatas[j++] = data;\n\t\t\t\t}\n\t\t\t}\n\t\t\tnames.RemoveRange(j, names.Count - j);\n\t\t\tdatas.RemoveRange(j, datas.Count - j);\n\t\t}\n\t\t//print.it(names.Count);\n\t\t\n\t\tif (names.Count > 250 && !retried) {\n\t\t\tretried = true;\n\t\t\tif (emFile.TryDownload(model, newHash)) goto gRetry;\n\t\t}\n\t\t\n\t\tvar vectors = CreateEmbeddings(datas, forDatabase: true, getInt8: compact, cancel);\n\t\t\n\t\tif (exists) {\n\t\t\tforeach (var v in aSame) { names.Add(v.name); datas.Add(v.data); vectors.Add(v.vec); }\n\t\t}\n\t\t\n\t\temFile.Save(names, vectors, datas, newHash);\n\t\t\n\t\temFile.PrintUploadIfAtHome(model, newHash);\n\t\t\n\t\treturn emFile.Load(out _);\n\t\t\n\t\t_EmHash _Hash(string file) {\n\t\t\tvar hash = System.Security.Cryptography.SHA256.HashData(filesystem.loadBytes(file));\n\t\t\treturn new(Base64Url.EncodeToString(hash), $\"{model.model};{model.dimensions};{model.emType}\");\n\t\t}\n\t}\n\t\n\t#region docs\n\t\n\tstatic string s_dbFileDocs = folders2.La + \"doc-ai.db\";\n\t\n\t/// <summary>\n\t/// Loads or creates/updates embeddings for docs.\n\t/// </summary>\n\t/// <exception cref=\"OperationCanceledException\"></exception>\n\t/// <exception cref=\"Exception\"></exception>\n\tpublic List<EmVector> GetDocsEmbeddings(CancellationToken cancel = default) {\n\t\treturn _GetEmbeddings(s_dbFileDocs, false, _GetData, cancel);\n\t\t\n\t\tstatic (List<string> names, List<EmInput> texts) _GetData() {\n\t\t\tList<string> names = [];\n\t\t\tList<EmInput> texts = [];\n\t\t\tusing var db = new sqlite(s_dbFileDocs, SLFlags.SQLITE_OPEN_READONLY);\n\t\t\tusing var sta = db.Statement(\"SELECT name,summary,text FROM doc\");\n\t\t\twhile (sta.Step()) {\n\t\t\t\tstring name = sta.GetText(0), sum = sta.GetText(1), text = sta.GetText(2);\n\t\t\t\tnames.Add(name);\n\t\t\t\ttexts.Add(text);\n\t\t\t\tif (!sum.NE()) {\n\t\t\t\t\tnames.Add(\"+\" + name);\n\t\t\t\t\ttexts.Add(sum);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn (names, texts);\n\t\t}\n\t}\n\t\n\tpublic string[] GetDocsTexts(IEnumerable<string> names) {\n\t\tstring[] an = names.ToArray();\n\t\tvar a = new string[an.Length];\n\t\tusing var db = new sqlite(s_dbFileDocs, SLFlags.SQLITE_OPEN_READONLY);\n\t\tusing var sta = db.Statement($\"SELECT name,text FROM doc WHERE name IN ({string.Join(',', an.Select(_ => \"?\"))})\");\n\t\tsta.BindAll(an);\n\t\twhile (sta.Step()) {\n\t\t\tstring name = sta.GetText(0), text = sta.GetText(1);\n\t\t\ta[an.IndexOf(name)] = text;\n\t\t}\n\t\tif (a.Contains(null)) throw new ArgumentException();\n\t\treturn a;\n\t}\n\t\n\t#endregion\n\t\n\t#region icons\n\t\n\tstatic string s_dbFileIcons = folders2.La + \"icons.db\";\n\t\n\t/// <summary>\n\t/// Loads or creates/updates embeddings for icons.\n\t/// </summary>\n\t/// <exception cref=\"OperationCanceledException\"></exception>\n\t/// <exception cref=\"Exception\"></exception>\n\tpublic List<EmVector> GetIconsEmbeddings(bool withImages, CancellationToken cancel = default) {\n\t\treturn _GetEmbeddings(s_dbFileIcons, true, _GetData, cancel);\n\t\t\n\t\t(List<string> names, List<EmInput> texts) _GetData() {\n\t\t\tvar rx1 = new regexp(@\"([a-z0-9])([A-Z])\");\n\t\t\tvar rx2 = new regexp(@\"([A-Z])([A-Z][a-z])\");\n\t\t\t\n\t\t\tif (withImages) {\n\t\t\t\tusing var db = new sqlite(s_dbFileIcons, SLFlags.SQLITE_OPEN_READONLY);\n\t\t\t\t\n\t\t\t\tint _TotalCount() {\n\t\t\t\t\tdb.Get(out string sql, \"\"\"SELECT 'SELECT ' || group_concat('(SELECT COUNT(*) FROM \"' || name || '\")', ' + ') || ';' AS sql FROM _tables;\"\"\");\n\t\t\t\t\tdb.Get(out int n, sql);\n\t\t\t\t\treturn n;\n\t\t\t\t}\n\t\t\t\tint nTotal = _TotalCount();\n\t\t\t\tint nn = 0, percent = 0;\n\t\t\t\tvar dp = dialog.showProgress(false, \"Preparing data for AI search\");\n\t\t\t\t\n\t\t\t\tList<string> names = new(nTotal);\n\t\t\t\tList<EmInput> a = new(nTotal);\n\t\t\t\tusing var stTables = db.Statement(\"SELECT * FROM _tables\");\n\t\t\t\twhile (stTables.Step()) {\n\t\t\t\t\tvar table = stTables.GetText(0);\n\t\t\t\t\t//if (table!=\"Material\") continue;\n\t\t\t\t\tvar templ = stTables.GetText(1);\n\t\t\t\t\tvar tc = XamlIconConverter_.GetConverter(templ) ?? throw new NotImplementedException(\"Unknown XAML: \" + templ);\n\t\t\t\t\t\n\t\t\t\t\tusing var stIcons = db.Statement(\"SELECT name,data FROM \" + table);\n\t\t\t\t\twhile (stIcons.Step()) {\n\t\t\t\t\t\tvar name = stIcons.GetText(0);\n\t\t\t\t\t\tnames.Add($\"{table}.{name}\");\n\t\t\t\t\t\tstring text = _SplitName(name);\n\t\t\t\t\t\t\n\t\t\t\t\t\ttc.PathData = stIcons.GetText(1);\n\t\t\t\t\t\tvar png = tc.ToPng(64, 96, Brushes.White);\n\t\t\t\t\t\t\n\t\t\t\t\t\ttext = $\"Icon named \\\"{text}\\\"\";\n\t\t\t\t\t\ta.Add((object[])[text, png]);\n\t\t\t\t\t\t\n\t\t\t\t\t\tif (!dp.IsOpen) throw new OperationCanceledException();\n\t\t\t\t\t\tint percent2 = Math2.PercentFromValue(nTotal, ++nn);\n\t\t\t\t\t\tif (percent2 > percent) dp.Send.Progress(percent = percent2);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tdp.Send.Close();\n\t\t\t\t\n\t\t\t\treturn (names, a);\n\t\t\t} else {\n\t\t\t\tList<string> names = _GetUniqueIconNames();\n\t\t\t\tList<EmInput> texts = new(names.Count);\n\t\t\t\tforeach (var name in names) {\n\t\t\t\t\ttexts.Add(_SplitName(name));\n\t\t\t\t}\n\t\t\t\treturn (names, texts);\n\t\t\t\t\n\t\t\t\tstatic List<string> _GetUniqueIconNames() {\n\t\t\t\t\tHashSet<string> h = new(60000, StringComparer.OrdinalIgnoreCase);\n\t\t\t\t\t\n\t\t\t\t\tusing var db = new sqlite(s_dbFileIcons, SLFlags.SQLITE_OPEN_READONLY);\n\t\t\t\t\tusing var stTables = db.Statement(\"SELECT name FROM _tables\");\n\t\t\t\t\twhile (stTables.Step()) {\n\t\t\t\t\t\tvar table = stTables.GetText(0);\n\t\t\t\t\t\tusing var stNames = db.Statement(\"SELECT name FROM \" + table);\n\t\t\t\t\t\twhile (stNames.Step()) {\n\t\t\t\t\t\t\tvar s = stNames.GetText(0);\n\t\t\t\t\t\t\th.Add(s);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t//print.it(h.Count);\n\t\t\t\t\treturn h.ToList();\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tstring _SplitName(string s) {\n\t\t\t\ts = s.Trim('_');\n\t\t\t\t//split PascalCase etc into words\n\t\t\t\ts = rx1.Replace(s, \"$1 $2\");\n\t\t\t\ts = rx2.Replace(s, \"$1 $2\");\n\t\t\t\treturn s;\n\t\t\t}\n\t\t}\n\t}\n\t\n\t#endregion\n\t\n\tpublic List<(EmVector f, float score)> GetTopMatches(float[] queryVector, List<EmVector> ems, int take) {\n\t\tvar a = ems\n\t\t\t.Select(f => (f, score: CosineSimilarity(queryVector, f.vec)))\n\t\t\t.OrderByDescending(x => x.score)\n\t\t\t.Take(take)\n\t\t\t.ToList();\n\t\treturn a;\n\t}\n\t\n\t[MethodImpl(MethodImplOptions.AggressiveOptimization)]\n\tpublic static float CosineSimilarity(float[] query, Array saved) {\n\t\tfloat dot = 0, normA = 0, normB = 0;\n\t\tswitch (saved) {\n\t\tcase float[] b:\n\t\t\tfor (int i = 0; i < query.Length; i++) {\n\t\t\t\tdot += query[i] * b[i];\n\t\t\t\tnormA += query[i] * query[i];\n\t\t\t\tnormB += b[i] * b[i];\n\t\t\t}\n\t\t\tbreak;\n\t\tcase sbyte[] b:\n\t\t\tfor (int i = 0; i < query.Length; i++) {\n\t\t\t\tfloat f = b[i] / 127f;\n\t\t\t\tdot += query[i] * f;\n\t\t\t\tnormA += query[i] * query[i];\n\t\t\t\tnormB += f * f;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\treturn dot / (MathF.Sqrt(normA) * MathF.Sqrt(normB));\n\t}\n}\n\nrecord struct EmVector(string name, Array vec);\n\nfile class _EmStorageFile(string file) {\n\tpublic void Save(List<string> names, List<Array> ems, List<EmInput> datas, _EmHash hash) {\n\t\tfilesystem.createDirectoryFor(file);\n\t\tusing var w = new BinaryWriter(File.Create(file));\n\t\tw.Write((byte)(ems[0] is float[]? 4 : 1));\n\t\tw.Write(ems[0].Length);\n\t\tw.Write(hash.hash);\n\t\tw.Write(hash.modelParams);\n\t\tfor (int i = 0; i < names.Count; i++) {\n\t\t\tw.Write(names[i]);\n\t\t\tswitch (ems[i]) {\n\t\t\tcase float[] a: w.Write(MemoryMarshal.AsBytes(a)); break;\n\t\t\tcase sbyte[] a: w.Write(MemoryMarshal.AsBytes(a)); break;\n\t\t\t}\n\t\t\tvar md5 = datas[i].GetHash();\n\t\t\tw.Write(md5.r1);\n\t\t\tw.Write(md5.r2);\n\t\t}\n\t}\n\t\n\tBinaryReader _Load(out int type, out int vectorLen, out _EmHash hash) {\n\t\tvar r = new BinaryReader(filesystem.loadStream(file));\n\t\ttype = r.ReadByte();\n\t\tReading = true; //for the caller's exception handler to detect whether the exception is likely because of corrupt file\n\t\tif (!(type is 1 or 4)) throw new FileFormatException();\n\t\tvectorLen = r.ReadInt32();\n\t\thash = new(r.ReadString(), r.ReadString());\n\t\treturn r;\n\t}\n\t\n\tpublic bool Reading { get; private set; }\n\t\n\tpublic List<EmVector> Load(out _EmHash hash) {\n\t\tusing var r = _Load(out int type, out int vectorLen, out hash);\n\t\tList<EmVector> a = new((int)(r.BaseStream.Length / vectorLen / type));\n\t\twhile (r.BaseStream.Position < r.BaseStream.Length) {\n\t\t\tvar name = r.ReadString();\n\t\t\tif (type == 4) {\n\t\t\t\tvar vec = GC.AllocateUninitializedArray<float>(vectorLen, pinned: true);\n\t\t\t\tr.Read(MemoryMarshal.AsBytes(vec.AsSpan()));\n\t\t\t\ta.Add(new(name, vec));\n\t\t\t} else {\n\t\t\t\tvar vec = GC.AllocateUninitializedArray<sbyte>(vectorLen, pinned: true);\n\t\t\t\tr.Read(MemoryMarshal.AsBytes(vec.AsSpan()));\n\t\t\t\ta.Add(new(name, vec));\n\t\t\t}\n\t\t\tr.ReadInt64(); r.ReadInt64(); //hash\n\t\t}\n\t\treturn a;\n\t\t\n\t\t//With AllocateUninitializedArray faster. When pinned, less work for GC, which runs simultaneously because we allocate large amount of memory.\n\t}\n\t\n\tpublic Dictionary<string, (Array vec, Hash.MD5Result hash)> LoadForUpdate() {\n\t\tDictionary<string, (Array vec, Hash.MD5Result hash)> d = new();\n\t\tusing var r = _Load(out int type, out int vectorLen, out _);\n\t\twhile (r.BaseStream.Position < r.BaseStream.Length) {\n\t\t\tvar name = r.ReadString();\n\t\t\tArray vec;\n\t\t\tif (type == 4) {\n\t\t\t\tvar f = new float[vectorLen];\n\t\t\t\tr.Read(MemoryMarshal.AsBytes(f.AsSpan()));\n\t\t\t\tvec = f;\n\t\t\t} else {\n\t\t\t\tvar b = new sbyte[vectorLen];\n\t\t\t\tr.Read(MemoryMarshal.AsBytes(b.AsSpan()));\n\t\t\t\tvec = b;\n\t\t\t}\n\t\t\tHash.MD5Result md5 = new(r.ReadInt64(), r.ReadInt64());\n\t\t\td.Add(name, (vec, md5));\n\t\t}\n\t\treturn d;\n\t}\n\t\n\tpublic bool TryDownload(AiEmbeddingModel model, _EmHash hash) {\n\t\t//#if !SCRIPT\n\t\t//\t\tif (App.IsAtHome) return false;\n\t\t//#endif\n\t\tif (_TryGetZipName(model, hash) is not { } zipName) return false;\n\t\tstring zipFile = file + \".7z\";\n\t\ttry {\n\t\t\tvar r = internet.http.Get($\"https://www.libreautomate.com/download/ai/embedding/{zipName}\", dontWait: true);\n\t\t\tif (!r.IsSuccessStatusCode) return false;\n\t\t\tr.Download(zipFile, progressText1: \"Downloading data for AI search\");\n\t\t\tvar dir = pathname.getDirectory(file);\n\t\t\tif (!SevenZip.Extract(out var errors, zipFile, dir)) { Debug_.Print(errors); return false; }\n\t\t}\n\t\tcatch (Exception ex) { Debug_.Print(ex); }\n\t\tfinally { filesystem.delete(zipFile, FDFlags.CanFail); }\n\t\treturn true;\n\t}\n\t\n\tstring _TryGetZipName(AiEmbeddingModel model, _EmHash hash) {\n\t\tif (!(file.Ends(\"-icons.bin\") && model.isCompact && model.GetType().Assembly == GetType().Assembly)) return null;\n\t\t\n\t\tvar md5 = new Hash.MD5Context();\n\t\tmd5.Add(hash.hash);\n\t\tmd5.Add(hash.modelParams);\n\t\treturn $\"{pathname.getNameNoExt(file)}-{md5.Hash.ToStringBase64Url()}.zip\";\n\t}\n\t\n\tpublic void PrintUploadIfAtHome(AiEmbeddingModel model, _EmHash hash) {\n#if !SCRIPT\n\t\tif (!App.IsAtHome) return;\n#endif\n\t\tif (_TryGetZipName(model, hash) is not { } zipName) return;\n\t\tprint.it($\"<><script Upload AI embeddings.cs|{file}|{zipName}>Upload<> AI embedding vectors.\");\n\t}\n}\n\nfile record struct _EmHash(string hash, string modelParams);\n\n[JsonConverter(typeof(_JsonConverter))]\nrecord struct EmInput {\n\tobject _o;\n\t\n\tpublic EmInput(string text) { _o = text; }\n\t\n\tpublic EmInput(object[] multimodal) { _o = multimodal; }\n\t\n\t//public bool IsMultimodal => _o is object[];\n\t\n\tpublic object[] Multimodal => _o as object[];\n\t\n\tpublic string Text => _o as string;\n\t\n\tpublic static implicit operator EmInput(string s) => new(s);\n\t\n\tpublic static implicit operator EmInput(object[] a) => new(a);\n\t\n\tpublic int Size {\n\t\tget {\n\t\t\tif (_o is object[] a) {\n\t\t\t\tint r = 0;\n\t\t\t\tforeach (var v in a) {\n\t\t\t\t\tif (v is byte[] b) r += b.Length / 3 * 4 + 4; //Base64 of image\n\t\t\t\t\telse if (v is string g) r += g.Length;\n\t\t\t\t}\n\t\t\t\treturn r;\n\t\t\t} else if (_o is string s) {\n\t\t\t\treturn s.Length;\n\t\t\t} else return 0;\n\t\t}\n\t}\n\t\n\tpublic Hash.MD5Result GetHash() {\n\t\tif (_o is object[] a) {\n\t\t\tvar h = new Hash.MD5Context();\n\t\t\tforeach (var v in a) {\n\t\t\t\tif (v is byte[] b) h.Add(b);\n\t\t\t\telse if (v is string g) h.Add(g);\n\t\t\t}\n\t\t\treturn h.Hash;\n\t\t} else if (_o is string s) {\n\t\t\treturn Hash.MD5(s);\n\t\t} else return default;\n\t}\n\t\n\tclass _JsonConverter : JsonConverter<EmInput> {\n\t\tpublic override EmInput Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) {\n\t\t\tthrow new NotImplementedException();\n\t\t}\n\t\t\n\t\tpublic override void Write(Utf8JsonWriter writer, EmInput value, JsonSerializerOptions options) {\n\t\t\tJsonSerializer.Serialize(writer, value.Text, options);\n\t\t}\n\t}\n}\n\n/// <summary>\n/// Converts XAML icons (those from the LA database) to various formats.\n/// 2 or more times faster than <see cref=\"ImageUtil.LoadGdipBitmapFromXaml\"/>, but supports only raw Path XAML from the database. No *Pack.Icon, Viewbox, multiple layers, margin etc.\n/// </summary>\nclass XamlIconConverter_ {\n\tbool _flip, _stroke2;\n\t\n\tpublic string PathData { get; set; }\n\tpublic Brush Brush { get; set; }\n\t\n\t//Each icon pack uses the same Path XAML for all its icons. Currently icon packs use one of 3 XAML versions. After updating the package may need to edit the regex.\n\tstatic regexp s_rx1 = new regexp(\"\"\"\n^<Path Data=.(.+?). Stretch=.Uniform. (?|Fill=.(.*?). UseLayoutRounding=.False. SnapsToDevicePixels=.False.(?: />|>\\s*<Path\\.LayoutTransform>\\s*<ScaleTransform ScaleY=.-1. />\\s*</Path\\.LayoutTransform>\\s*</Path>)|Stroke=.(.+?). StrokeThickness=.2. StrokeStartLineCap=.Round. StrokeEndLineCap=.Round. StrokeLineJoin=.Round. UseLayoutRounding=.False. Width=.24. Height=.24. SnapsToDevicePixels=.False. />)$\n\"\"\");\n\t\n\t/// <summary>\n\t/// Gets a converter for the specified path XAML.\n\t/// </summary>\n\t/// <param name=\"pathXaml\">Icon <c>Path</c> XAML. Can be with or without data (ignores it).</param>\n\t/// <returns><c>null</c> if <i>pathXaml</i> does not match one of icon templates from the LA icon database.</returns>\n\tpublic static XamlIconConverter_ GetConverter(string pathXaml) {\n\t\tif (!pathXaml.Starts(\"<Path Data=\")) return null;\n\t\tif (!s_rx1.Match(pathXaml, out var m)) return null;\n\t\tstring data = m[1].Value, color = m[2].Value;\n\t\tXamlIconConverter_ r = new() { _stroke2 = pathXaml[m[1].Start - 6] == 'S', _flip = pathXaml.Ends(\"</Path>\") };\n\t\tif (data.Length > 0 && data[0] != '{') r.PathData = data;\n\t\tif (color.Length > 0 && color[0] != '{') { r.Brush = (Brush)new BrushConverter().ConvertFromString(color); r.Brush.Freeze(); }\n\t\treturn r;\n\t}\n\t\n\tpublic RenderTargetBitmap ToWpfBitmap(int size, int dpi, Brush background = null) {\n\t\tvar geo = Geometry.Parse(PathData ?? throw new InvalidOperationException(\"PathData null\"));\n\t\tvar bounds = _stroke2 ? new(0, 0, 24, 24) : geo.Bounds;\n\t\tdouble scale = Math.Min(size / bounds.Width, size / bounds.Height);\n\t\t\n\t\tvar tg = new TransformGroup();\n\t\ttg.Children.Add(new TranslateTransform(-bounds.X, -bounds.Y));\n\t\tif (_flip) {\n\t\t\ttg.Children.Add(new ScaleTransform(scale, -scale));\n\t\t\ttg.Children.Add(new TranslateTransform(0, bounds.Height * scale));\n\t\t} else {\n\t\t\ttg.Children.Add(new ScaleTransform(scale, scale));\n\t\t}\n\t\tdouble size0 = Math.Max(bounds.Width, bounds.Height), centerX = (size0 - bounds.Width) * scale / 2, centerY = (size0 - bounds.Height) * scale / 2;\n\t\tif (centerX > 1 || centerY > 1) tg.Children.Add(new TranslateTransform(centerX, centerY));\n\t\t\n\t\tvar brush = Brush ?? SystemColors.ControlTextBrush;\n\t\tPen pen = null;\n\t\tif (_stroke2) {\n\t\t\tpen = new(brush, 2) { StartLineCap = PenLineCap.Round, EndLineCap = PenLineCap.Round, LineJoin = PenLineJoin.Round };\n\t\t}\n\t\t\n\t\tvar dv = new DrawingVisual();\n\t\tusing (var dc = dv.RenderOpen()) {\n\t\t\tif (background != null) dc.DrawRectangle(background, null, new(0, 0, size, size)); //note: AI does not support PNG with transparent background\n\t\t\tdc.PushTransform(tg);\n\t\t\tdc.DrawGeometry(pen == null ? brush : null, pen, geo);\n\t\t\tdc.Pop();\n\t\t}\n\t\t\n\t\tvar rtb = new RenderTargetBitmap(size, size, dpi, dpi, PixelFormats.Pbgra32);\n\t\trtb.Render(dv);\n\t\treturn rtb;\n\t}\n\t\n\tpublic byte[] ToPng(int size, int dpi, Brush background = null) {\n\t\tusing var ms = new MemoryStream();\n\t\tToPng(ms, size, dpi, background);\n\t\treturn ms.ToArray();\n\t}\n\t\n\tpublic void ToPng(Stream stream, int size, int dpi, Brush background = null) {\n\t\tBitmapSource bs = ToWpfBitmap(size, dpi, background);\n\t\t\n\t\tvar encoder = new PngBitmapEncoder();\n\t\tencoder.Frames.Add(BitmapFrame.Create(bs));\n\t\tencoder.Save(stream);\n\t}\n\t\n#if false //unused\n\tpublic System.Drawing.Bitmap ToGdipBitmap(int size, int dpi, Brush background = null) {\n\t\tvar rtb = ToWpfBitmap(size, dpi, background);\n\t\t\n\t\tint stride = size * 4, msize = size * stride;\n\t\tvar bmp = new System.Drawing.Bitmap(size, size, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);\n\t\tusing var d = bmp.Data(System.Drawing.Imaging.ImageLockMode.ReadWrite);\n\t\trtb.CopyPixels(new System.Windows.Int32Rect(0, 0, size, size), d.Scan0, msize, stride);\n\t\tbmp.SetResolution(dpi, dpi);\n\t\treturn bmp;\n\t}\n\t\n\tpublic unsafe void ToIcon(Stream stream, int[] sizes) {\n\t\tif (sizes is not { Length: >= 1 and <= 1000 }) throw new ArgumentOutOfRangeException(nameof(sizes));\n\t\tstream.Position = Math2.AlignUp(sizeof(Api.NEWHEADER) + sizeof(Api.ICONDIRENTRY) * sizes.Length, 4);\n\t\tvar a = stackalloc Api.ICONDIRENTRY[sizes.Length];\n\t\tfor (int i = 0; i < sizes.Length; i++) {\n\t\t\tint size = sizes[i];\n\t\t\tif (size < 1 || size > 256) throw new ArgumentOutOfRangeException();\n\t\t\tlong pos = stream.Position;\n\t\t\tToPng(stream, size, 96);\n\t\t\tbyte bsize = (byte)size;\n\t\t\ta[i] = new() { bWidth = bsize, bHeight = bsize, wBitCount = 32, dwBytesInRes = (int)(stream.Position - pos), dwImageOffset = (int)pos };\n\t\t}\n\t\tvar posEnd = stream.Position;\n\t\tstream.Position = 0;\n\t\tvar h = new Api.NEWHEADER { wResType = 1, wResCount = (ushort)sizes.Length };\n\t\tstream.Write(new(&h, sizeof(Api.NEWHEADER)));\n\t\tstream.Write(new(a, sizeof(Api.ICONDIRENTRY) * sizes.Length));\n\t\tstream.Position = posEnd;\n\t}\n\t\n\tpublic void ToIcon(string icoFile, FrameworkElement e, int[] sizes) {\n\t\ticoFile = pathname.NormalizeMinimally_(icoFile);\n\t\tusing var stream = File.OpenWrite(icoFile);\n\t\tToIcon(stream, sizes);\n\t}\n\t\n\tpublic System.Drawing.Icon ToGdipIcon(int size) {\n\t\tusing var ms = new MemoryStream();\n\t\tToIcon(ms, [size]);\n\t\tms.Position = 0;\n\t\treturn new System.Drawing.Icon(ms);\n\t}\n#endif\n}\n"
  },
  {
    "path": "Au.Editor/xAI/AiModel.cs",
    "content": "//#define ADD_ALL_COMPACT_EMBEDDING_MODELS\n#if DEBUG\n#define ADD_CHAT_MODELS //currently not using chat models in LA, but likely in the future\n#endif\n\nusing System.Text.Json.Nodes;\nusing System.Security.Authentication;\nusing System.Net.Http;\nusing LA;\n\nnamespace AI;\n\nrecord AMLimits(int maxTokens, int maxInputs, int requestPeriod = 0, int maxSize = 0);\n\nabstract record class AiModel(string api, string url, string model, AMLimits limits, string apiSuffix = null) {\n\t#region static\n\t\n\tstatic AiModel() {\n\t\tModels = [\n//\t\t\tnew ModelOpenaiEmbed(), //don't add, because other models are much better\n//#if ADD_ALL_COMPACT_EMBEDDING_MODELS\n//\t\t\tnew ModelOpenaiEmbed2(),\n//#endif\n#if ADD_CHAT_MODELS\n\t\t\tnew ModelOpenaiChat(\"gpt-5\"),\n\t\t\tnew ModelOpenaiChat(\"gpt-5-mini\"),\n\t\t\t//new ModelOpenaiCompletionsChat(\"gpt-5\"),\n\t\t\t//new ModelOpenaiCompletionsChat(\"gpt-5-mini\"),\n#endif\n\t\t\t\n\t\t\tnew ModelGeminiEmbed(),\n#if ADD_ALL_COMPACT_EMBEDDING_MODELS\n\t\t\tnew ModelGeminiEmbed2(),\n#endif\n#if ADD_CHAT_MODELS\n\t\t\tnew ModelGeminiChat(\"gemini-2.5-flash\"),\n\t\t\tnew ModelGeminiChat(\"gemini-2.5-flash-lite\"),\n#endif\n\t\t\t\n\t\t\tnew ModelVoyageEmbed(),\n#if ADD_ALL_COMPACT_EMBEDDING_MODELS\n\t\t\tnew ModelVoyageEmbed2(),\n#endif\n\t\t\tnew ModelVoyageRerank(\"rerank-2.5\"),\n\t\t\tnew ModelVoyageRerank(\"rerank-2.5-lite\"),\n\t\t\t\n#if ADD_CHAT_MODELS\n\t\t\tnew ModelClaudeChat(\"claude-opus-4-1\"),\n\t\t\tnew ModelClaudeChat(\"claude-sonnet-4-0\"),\n\t\t\t\n\t\t\tnew ModelDeepseekChat(),\n#endif\n\t\t\t\n#if MISTRAL\n\t\t\tnew ModelMistralEmbed(),\n#if ADD_ALL_COMPACT_EMBEDDING_MODELS\n\t\t\tnew ModelMistralEmbed2(),\n#endif\n#if ADD_CHAT_MODELS\n\t\t\tnew ModelMistralChat(\"mistral-medium-latest\"),\n\t\t\tnew ModelMistralChat(\"mistral-large-latest\"),\n\t\t\tnew ModelMistralChat(\"codestral-latest\"),\n#endif\n#endif\n\t\t\t\n#if COHERE\n\t\t\tnew ModelCohereEmbed(),\n#if ADD_ALL_COMPACT_EMBEDDING_MODELS\n\t\t\tnew ModelCohereEmbed2(),\n#endif\n#if ADD_CHAT_MODELS\n\t\t\tnew ModelCohereChat(\"command-a-03-2025\"),\n\t\t\tnew ModelCohereRerank(\"rerank-v3.5\"),\n\t\t\tnew ModelCohereRerank(\"rerank-english-v3.0\"),\n#endif\n#endif\n\t\t];\n\t}\n\t\n\tpublic static List<AiModel> Models { get; }\n\t\n\t/// <summary>\n\t/// Finds model by type.\n\t/// </summary>\n\tpublic static T GetModel<T>() where T : AiModel => Models.OfType<T>().First();\n\t\n\t/// <summary>\n\t/// Finds model by type and name.\n\t/// </summary>\n\t/// <param name=\"model\"></param>\n\t/// <param name=\"displayName\"><i>model</i> is like <c>\"API model\"</c>.</param>\n\t/// <returns>null if not <i>model</i> found.</returns>\n\tpublic static T GetModel<T>(string model, bool displayName = false) where T : AiModel => Models.OfType<T>().FirstOrDefault(o => (displayName ? o.DisplayName : o.model) == model);\n\t\n\tpublic static void RerankerModelWarning() {\n\t\tif (!s_onceWarning1) s_onceWarning1 = true; else return;\n\t\tprint.it(\"<>Note: Select an AI reranker model in <+options AI>Options > AI<>. It improves AI search results.\");\n\t}\n\tstatic bool s_onceWarning1;\n\t\n\t#endregion\n\t\n\tpublic string DisplayName => $\"{api}{apiSuffix} {model}\";\n\t\n\t/// <exception cref=\"Exception\"></exception>\n\tpublic IEnumerable<string> GetHeaders() {\n\t\tif (ApiKeys is not { } ak) throw new InvalidOperationException(\"Property ApiKeys not set\");\n\t\tif (!ak.TryGetValue(api, out string apiKey) || apiKey.NE()) throw new InvalidCredentialException(\"AI settings error: no API key\");\n\t\t\n\t\tif (apiKey is ['%', _, .., '%']) {\n\t\t\tapiKey = Environment.GetEnvironmentVariable(apiKey = apiKey[1..^1]) ?? throw new InvalidCredentialException($\"AI settings error: missing environment variable {apiKey}\");\n\t\t} else {\n\t\t\tapiKey = EdProtectedData.Unprotect(apiKey);\n\t\t}\n\t\t\n\t\treturn _GetHeaders(apiKey);\n\t}\n\t\n\tprotected private virtual IEnumerable<string> _GetHeaders(string apiKey) => [\"Authorization: Bearer \" + apiKey];\n\t\n\tpublic static Dictionary<string, string> ApiKeys { get; set; }\n\t\n\t/// <summary>\n\t/// HTTP-posts data. Manages <c>limits.requestPeriod</c> (waits if need) and error \"too many requests\" (waits/retries with UI and cancellation).\n\t/// </summary>\n\t/// <exception cref=\"OperationCanceledException\"></exception>\n\t/// <exception cref=\"Exception\"></exception>\n\tpublic HttpResponseMessage Post(object data, IEnumerable<string> headers, CancellationToken cancel = default) {\n\t\t_ApiPostTimes ppt = s_dpl.GetOrAdd(api, o => new());\n\t\tbool retried = false;\n\t\tgRetry:\n\t\tif (limits.requestPeriod > 0) {\n\t\t\tlong sleep = limits.requestPeriod - (Environment.TickCount64 - ppt.lastRequestTime);\n\t\t\tif (sleep > 0) Task.Delay((int)sleep, cancel).GetAwaiter().GetResult();\n\t\t\tppt.lastRequestTime = Environment.TickCount64;\n\t\t}\n\t\tvar r = internet.http.Send(internet.message(HttpMethod.Post, url, headers: headers, internet.jsonContent(data)), cancel);\n\t\tif (r.IsSuccessStatusCode) return r;\n\t\tif (r.StatusCode == System.Net.HttpStatusCode.TooManyRequests) {\n\t\t\tif (r.Headers.RetryAfter is { Delta: not null } ra) ppt.waitRetryS = Math.Max(1, (int)ra.Delta.Value.TotalSeconds);\n\t\t\tif (retried) ppt.waitRetryS += 10; else retried = true;\n\t\t\tusing var osd = osdText.showText(\"\", -1, icon: icon.stock(StockIcon.WARNING), showMode: OsdMode.ThisThread, dontShow: true);\n\t\t\tfor (int i = 0; i < ppt.waitRetryS; i++) {\n\t\t\t\tosd.Text = $\"Too many AI requests.\\nWaiting {ppt.waitRetryS - i} s and will retry.\\nYou can click here to cancel.\";\n\t\t\t\tosd.Visible = true;\n\t\t\t\twait.doEvents(1000);\n\t\t\t\tif (!osd.Visible || cancel.IsCancellationRequested) {\n\t\t\t\t\tprint.it(\"Too many AI requests\", r.Text(ignoreError: true));\n\t\t\t\t\tthrow new OperationCanceledException();\n\t\t\t\t}\n\t\t\t}\n\t\t\tgoto gRetry;\n\t\t}\n\t\tthrow new HttpRequestException($\"{r.StatusCode}. {r.Text(ignoreError: true)}\");\n\t}\n\t\n\tclass _ApiPostTimes {\n\t\tpublic long lastRequestTime;\n\t\tpublic int waitRetryS = 10;\n\t}\n\tstatic ConcurrentDictionary<string, _ApiPostTimes> s_dpl = [];\n}\n\nabstract record class AiEmbeddingModel(string api, string url, string model, int dimensions, string emType, AMLimits limits)\n\t: AiModel(api, url, model, limits) {\n\t\n\tpublic bool isCompact { get; protected set; }\n\t\n\tpublic bool isMultimodal { get; protected set; }\n\t\n\tpublic abstract object GetPostData(IList<EmInput> input, bool isQuery);\n\t\n\tpublic abstract IEnumerable<JsonNode> GetVectors(JsonNode j);\n\t\n\tpublic abstract int GetTokens(JsonNode j);\n}\n\nabstract record class AiChatModel(string api, string url, string model, AMLimits limits)\n\t: AiModel(api, url, model, limits) {\n\t\n\tpublic abstract object GetPostData(string systemInstruction, List<AiChatMessage> messages, double? temperature = null);\n\t\n\tpublic abstract AiChatMessage GetAnswer(JsonNode j);\n}\n\nrecord class AiChatMessage(ACMRole role, string text, JsonNode json = null);\nenum ACMRole { user, assistant, tool }\n\nabstract record class AiRerankModel(string api, string url, string model, AMLimits limits)\n\t: AiModel(api, url, model, limits) {\n\t\n\tpublic abstract object GetPostData(string query, IList<string> documents, int top_n = 0);\n\t\n\tpublic abstract IEnumerable<AiRerankResult> GetResults(JsonNode j);\n}\n\nrecord struct AiRerankResult(int index, float score);\n\n#region Mistral\n#if MISTRAL //waekar chat model. Embedding good, but not as good as Gemini and Voyage.\nrecord class ModelMistralEmbed : AiEmbeddingModel {\n\tpublic ModelMistralEmbed() : base(\"Mistral\", \"https://api.mistral.ai/v1/embeddings\", \"codestral-embed\", 1024, \"float\", new(32000, 256, requestPeriod: 1100)) { }\n\t//\"mistral-embed\" (only 1024 dim float), \"codestral-embed\"\n\t\n\tpublic override object GetPostData(IList<EmInput> input, bool isQuery)\n\t\t=> new { model, input, output_dimension = dimensions, output_dtype = emType };\n\t\n\tpublic override IEnumerable<JsonNode> GetVectors(JsonNode j)\n\t\t=> j[\"data\"].AsArray().Select(o => o[\"embedding\"]);\n\t\n\tpublic override int GetTokens(JsonNode j)\n\t\t=> (int)j[\"usage\"][\"total_tokens\"];\n}\n\n#if ADD_ALL_COMPACT_EMBEDDING_MODELS\nrecord class ModelMistralEmbed2 : ModelMistralEmbed {\n\tpublic ModelMistralEmbed2() { isCompact = true; dimensions = 384; emType = \"int8\"; }\n}\n#endif\n\nrecord class ModelMistralChat : AiChatModel {\n\tpublic ModelMistralChat(string model) : base(\"Mistral\", \"https://api.mistral.ai/v1/chat/completions\", model, new(32000, 256, requestPeriod: 1500)) { }\n\t//\"mistral-small-latest\", \"mistral-medium-latest\", \"mistral-large-latest\", \"codestral-latest\"\n\t\n\tpublic override object GetPostData(string systemInstruction, List<AiChatMessage> messages, double? temperature = null)\n\t\t=> new {\n\t\t\tmodel,\n\t\t\tmessages = (object[])[new { role = \"system\", content = systemInstruction }, .. messages.Select(o => new { role = o.role.ToString(), content = o.text })],\n\t\t\ttemperature,\n\t\t};\n\t\n\tpublic override AiChatMessage GetAnswer(JsonNode j)\n\t\t=> new AiChatMessage(ACMRole.assistant, (string)j[\"choices\"][0][\"message\"][\"content\"]);\n}\n#endif\n#endregion\n\n#region OpenAI\n\nrecord class ModelOpenaiEmbed : AiEmbeddingModel {\n\tpublic ModelOpenaiEmbed() : base(\"OpenAI\", \"https://api.openai.com/v1/embeddings\", \"text-embedding-3-small\", 1024, null, new(100000, 2048)) { }\n\t\n\tpublic override object GetPostData(IList<EmInput> input, bool isQuery)\n\t\t=> new { model, input, dimensions, encoding_format = \"base64\" };\n\t\n\tpublic override IEnumerable<JsonNode> GetVectors(JsonNode j)\n\t\t=> j[\"data\"].AsArray().Select(o => o[\"embedding\"]);\n\t\n\tpublic override int GetTokens(JsonNode j)\n\t\t=> (int)j[\"usage\"][\"total_tokens\"];\n}\n\n#if ADD_ALL_COMPACT_EMBEDDING_MODELS\nrecord class ModelOpenaiEmbed2 : ModelOpenaiEmbed {\n\tpublic ModelOpenaiEmbed2() { isCompact = true; dimensions = 384; }\n}\n#endif\n\nrecord class ModelOpenaiChat : AiChatModel {\n\tpublic ModelOpenaiChat(string model) : base(\"OpenAI\", \"https://api.openai.com/v1/responses\", model, new(100000, 2048)) { apiSuffix = \" responses\"; }\n\t//\"gpt-5\", \"gpt-5-mini\", \"gpt-5-nano\"\n\t\n\t/// <summary>\n\t/// high, medium (default), low, minimal\n\t/// </summary>\n\tpublic string reasoning { get; init; } = \"medium\";\n\t\n\tpublic override object GetPostData(string instructions, List<AiChatMessage> messages, double? temperature = null) {\n\t\tList<object> input = [];\n\t\tforeach (var m in messages) {\n\t\t\tif (m.role is ACMRole.user) input.Add(new { role = \"user\", content = m.text });\n\t\t\telse input.AddRange(m.json.AsArray());\n\t\t}\n\t\t\n\t\tif (model.Starts(\"gpt-4\"))\n\t\t\treturn new {\n\t\t\t\tmodel,\n\t\t\t\tinstructions,\n\t\t\t\tinput,\n\t\t\t\ttemperature,\n\t\t\t\tstore = false,\n\t\t\t};\n\t\t\n\t\treturn new {\n\t\t\tmodel,\n\t\t\tinstructions,\n\t\t\tinput,\n\t\t\t//temperature, //not supported\n\t\t\tstore = false,\n\t\t\treasoning = new { effort = reasoning }\n\t\t};\n\t}\n\t\n\tpublic override AiChatMessage GetAnswer(JsonNode j) {\n\t\tvar a = j[\"output\"].AsArray();\n\t\tforeach (var v in a) {\n\t\t\tif ((string)v[\"type\"] == \"message\") { //doc: always \"message\"\n\t\t\t\tvar m = v[\"content\"][0];\n\t\t\t\tif ((string)m[\"type\"] != \"output_text\") continue; //refusal\n\t\t\t\treturn new AiChatMessage(ACMRole.assistant, (string)m[\"text\"], a);\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\t\n\t//tokens: (int)json[\"usage\"][\"total_tokens\"]\n}\n\n//record class ModelOpenaiCompletionsChat : AiChatModel {\n//\tpublic ModelOpenaiCompletionsChat(string model) : base(\"OpenAI\", \"https://api.openai.com/v1/chat/completions\", model, new(100000, 2048)) { apiSuffix = \" completions\"; }\n\n//\tpublic override object GetPostData(string systemInstruction, List<AiChatMessage> messages, double? temperature = null)\n//\t\t=> new {\n//\t\t\tmodel,\n//\t\t\tmessages = (object[])[new { role = \"developer\", content = systemInstruction }, .. messages.Select(o => new { role = o.role.ToString(), content = o.text })],\n//\t\t\ttemperature,\n//\t\t};\n\n//\tpublic override AiChatMessage GetAnswer(JsonNode j)\n//\t\t=> new AiChatMessage(ACMRole.assistant, (string)j[\"choices\"][0][\"message\"][\"content\"]);\n\n//\t//tokens: (int)json[\"usage\"][\"total_tokens\"]\n//}\n\n#endregion\n\n#region Gemini\n\nrecord class ModelGeminiEmbed : AiEmbeddingModel {\n\tconst string c_model = \"gemini-embedding-001\";\n\t\n\tpublic ModelGeminiEmbed() : base(\"Gemini\", $\"https://generativelanguage.googleapis.com/v1beta/models/{c_model}:batchEmbedContents\", c_model, 1024, null, new(8000, 100, requestPeriod: 2000)) { }\n\t\n\tprivate protected override IEnumerable<string> _GetHeaders(string apiKey) => [\"x-goog-api-key: \" + apiKey];\n\t\n\tpublic override object GetPostData(IList<EmInput> input, bool isQuery)\n\t\t=> new {\n\t\t\trequests = (object[])[input.Select(o => new {\n\t\t\t\tmodel = \"models/\" + model,\n\t\t\t\tcontent = new { parts = (object[])[new { text = o.Text }] },\n\t\t\t\toutputDimensionality = dimensions,\n\t\t\t\ttask_type = isQuery ? \"RETRIEVAL_QUERY\" : \"RETRIEVAL_DOCUMENT\"\n\t\t\t})]\n\t\t};\n\t\n\tpublic override IEnumerable<JsonNode> GetVectors(JsonNode j)\n\t\t=> j[\"embeddings\"].AsArray().Select(o => o[\"values\"]);\n\t\n\tpublic override int GetTokens(JsonNode j) => 0;\n}\n\n#if ADD_ALL_COMPACT_EMBEDDING_MODELS\nrecord class ModelGeminiEmbed2 : ModelGeminiEmbed {\n\tpublic ModelGeminiEmbed2() { isCompact = true; dimensions = 384; }\n}\n#endif\n\nrecord class ModelGeminiChat : AiChatModel {\n\tpublic ModelGeminiChat(string model) : base(\"Gemini\", $\"https://generativelanguage.googleapis.com/v1beta/models/{model}:generateContent\", model, new(8000, 100)) { }\n\t//\"gemini-2.5-flash\", \"gemini-2.5-flash-lite\"\n\t\n\tprivate protected override IEnumerable<string> _GetHeaders(string apiKey) => [\"x-goog-api-key: \" + apiKey];\n\t\n\tpublic override object GetPostData(string systemInstruction, List<AiChatMessage> messages, double? temperature = null) {\n\t\treturn new {\n\t\t\tsystem_instruction = new { parts = (object[])[new { text = systemInstruction }] },\n\t\t\tcontents = messages.Select(o => new { role = o.role is ACMRole.user ? \"user\" : \"model\", parts = (object[])[new { text = o.text }] }),\n\t\t\tgenerationConfig = new { temperature }\n\t\t};\n\t}\n\t\n\tpublic override AiChatMessage GetAnswer(JsonNode j)\n\t\t=> new AiChatMessage(ACMRole.assistant, (string)j[\"candidates\"][0][\"content\"][\"parts\"][0][\"text\"]);\n\t\n\t//tokens: (int)json[\"usageMetadata\"][\"totalTokenCount\"]\n}\n\n#endregion\n\n#region Voyage\n\nrecord class ModelVoyageEmbed : AiEmbeddingModel {\n\t//public ModelVoyageEmbed() : base(\"Voyage\", \"https://api.voyageai.com/v1/embeddings\", \"voyage-3.5\", 1024, \"float\", new(3300, 1000, requestPeriod: 20500)) { } //free tier rate: 10000 TPM, 3 RPM\n\tpublic ModelVoyageEmbed() : base(\"Voyage\", \"https://api.voyageai.com/v1/embeddings\", \"voyage-3.5\", 1024, \"float\", new(32000, 1000, requestPeriod: 1000)) { } //rate: 2000000 TPM, 2000 RPM\n\t\n\tpublic override object GetPostData(IList<EmInput> input, bool isQuery)\n\t\t=> new {\n\t\t\tmodel, //voyage-3.5, voyage-3.5-lite\n\t\t\tinput,\n\t\t\toutput_dimension = dimensions, //2048, 1024 (default), 512, and 256\n\t\t\toutput_dtype = emType,\n\t\t\tencoding_format = \"base64\",\n\t\t\tinput_type = isQuery ? \"query\" : \"document\"\n\t\t};\n\t\n\tpublic override IEnumerable<JsonNode> GetVectors(JsonNode j)\n\t\t=> j[\"data\"].AsArray().Select(o => o[\"embedding\"]);\n\t\n\tpublic override int GetTokens(JsonNode j)\n\t\t=> (int)j[\"usage\"][\"total_tokens\"];\n}\n\n#if ADD_ALL_COMPACT_EMBEDDING_MODELS\nrecord class ModelVoyageEmbed2 : ModelVoyageEmbed {\n\tpublic ModelVoyageEmbed2() { isCompact = true; dimensions = 512; emType = \"int8\"; }\n}\n#endif\n\nrecord class ModelVoyageEmbedM : AiEmbeddingModel {\n\tpublic ModelVoyageEmbedM() : base(\"Voyage\", \"https://api.voyageai.com/v1/multimodalembeddings\", \"voyage-multimodal-3\", 1024, null, new(32000, 1000, requestPeriod: 1000)) { isMultimodal = isCompact = true; }\n\t//With every 560 pixels of an image being counted as a token, each input in the list must not exceed 32,000 tokens, and the total number of tokens across all inputs must not exceed 320,000.\n\t\n\tpublic override object GetPostData(IList<EmInput> input, bool isQuery) {\n\t\tList<object> a = new(input.Count);\n\t\tList<object> aItem = [];\n\t\tforeach (var d in input) {\n\t\t\taItem.Clear();\n\t\t\tforeach (var o in d.Multimodal) {\n\t\t\t\tif (o is string s) aItem.Add(new { type = \"text\", text = s });\n\t\t\t\telse if (o is byte[] png) aItem.Add(new { type = \"image_base64\", image_base64 = \"data:image/png;base64,\" + Convert.ToBase64String(png) });\n\t\t\t\telse throw new ArgumentException();\n\t\t\t}\n\t\t\ta.Add(new { content = aItem.ToArray() });\n\t\t}\n\t\t\n\t\treturn new {\n\t\t\tmodel, //voyage-multimodal-3\n\t\t\tinput = a,\n\t\t\toutput_encoding = \"base64\",\n\t\t\tinput_type = isQuery ? \"query\" : \"document\"\n\t\t};\n\t}\n\t\n\tpublic override IEnumerable<JsonNode> GetVectors(JsonNode j)\n\t\t=> j[\"data\"].AsArray().Select(o => o[\"embedding\"]);\n\t\n\tpublic override int GetTokens(JsonNode j)\n\t\t=> (int)j[\"usage\"][\"total_tokens\"];\n}\n\nrecord class ModelVoyageRerank : AiRerankModel {\n\t//public ModelVoyageRerank(string model) : base(\"Voyage\", \"https://api.voyageai.com/v1/rerank\", model, new(3300, 1000, requestPeriod: 20500)) { } //free tier\n\tpublic ModelVoyageRerank(string model) : base(\"Voyage\", \"https://api.voyageai.com/v1/rerank\", model, new(8000, 1000, requestPeriod: 1000)) { }\n\t\n\tpublic override object GetPostData(string query, IList<string> documents, int top_n = 0)\n\t\t=> new {\n\t\t\tmodel, //rerank-2.5, rerank-2.5-lite\n\t\t\tquery,\n\t\t\tdocuments,\n\t\t\ttop_k = Math.Min(top_n > 0 ? top_n : int.MaxValue, documents.Count),\n\t\t\t//truncation //true (default) - truncates at 8000 tok; false - error if > 8000 tok.\n\t\t};\n\t\n\tpublic override IEnumerable<AiRerankResult> GetResults(JsonNode j)\n\t\t=> j[\"data\"].AsArray().Select(o => new AiRerankResult((int)o[\"index\"], (float)o[\"relevance_score\"]));\n}\n\n#endregion\n\n#region Claude\n\n//no embedding API\n\nrecord class ModelClaudeChat : AiChatModel {\n\tpublic ModelClaudeChat(string model) : base(\"Claude\", \"https://api.anthropic.com/v1/messages\", model, new(200000, 100000, 1250)) { } //200K tokens, 50 requests/minute, 30000 input tokens/minute, 8000 output tokens/minute\n\t\n\tprivate protected override IEnumerable<string> _GetHeaders(string apiKey) => [\"x-api-key: \" + apiKey, \"anthropic-version: 2023-06-01\"];\n\t\n\tpublic override object GetPostData(string systemInstruction, List<AiChatMessage> messages, double? temperature = null)\n\t\t=> new {\n\t\t\tmodel,\n\t\t\tsystem = systemInstruction,\n\t\t\tmessages = messages.Select(o => new { role = o.role.ToString(), content = o.text }),\n\t\t\tmax_tokens = 32000, //opus-4.1 32000, sonnet-4 64000\n\t\t\ttemperature = temperature ?? 1,\n\t\t};\n\t\n\tpublic override AiChatMessage GetAnswer(JsonNode j)\n\t\t=> new AiChatMessage(ACMRole.assistant, (string)j[\"content\"][0][\"text\"]);\n}\n\n#endregion\n\n#region DeepSeek\n\n//no embedding API\n\nrecord class ModelDeepseekChat : AiChatModel {\n\tpublic ModelDeepseekChat(string model = \"deepseek-chat\") : base(\"DeepSeek\", \"https://api.deepseek.com/chat/completions\", model, new(200000, 100000)) { }//TODO2: limits\n\t\n\tpublic override object GetPostData(string systemInstruction, List<AiChatMessage> messages, double? temperature = null)\n\t\t=> new {\n\t\t\tmodel, //deepseek-chat, deepseek-reasoner\n\t\t\tmessages = (object[])[new { role = \"system\", content = systemInstruction }, .. messages.Select(o => new { role = o.role.ToString(), content = o.text })],\n\t\t\ttemperature = temperature ?? 1, //1-2\n\t\t};\n\t\n\tpublic override AiChatMessage GetAnswer(JsonNode j)\n\t\t=> new AiChatMessage(ACMRole.assistant, (string)j[\"choices\"][0][\"message\"][\"content\"]);\n}\n\n#endregion\n\n#region Cohere\n#if COHERE\nrecord class ModelCohereEmbed : AiEmbeddingModel {\n\tpublic ModelCohereEmbed() : base(\"Cohere\", \"https://api.cohere.ai/v2/embed\", \"embed-v4.0\", 1024, \"base64\", new(100000, 96, requestPeriod: 1000, maxSize: 256000)) { }\n\t\n\tpublic override object GetPostData(IList<EmInput> texts, bool isQuery)\n\t\t=> new { model, texts, output_dimension = dimensions, embedding_types = (string[])[emType], input_type = isQuery ? \"search_query\" : \"search_document\" };\n\t\n\tpublic override IEnumerable<JsonNode> GetVectors(JsonNode j)\n\t\t=> j[\"embeddings\"][emType].AsArray();\n\t\n\tpublic override int GetTokens(JsonNode j)\n\t\t=> (int)j[\"meta\"][\"billed_units\"][\"input_tokens\"];\n}\n\n#if ADD_ALL_COMPACT_EMBEDDING_MODELS\nrecord class ModelCohereEmbed2 : ModelCohereEmbed {\n\tpublic ModelCohereEmbed2() { isCompact = true; dimensions = 512; emType = \"int8\"; }\n}\n#endif\n\nrecord class ModelCohereChat : AiChatModel {\n\tpublic ModelCohereChat(string model = \"command-a-03-2025\") : base(\"Cohere\", \"https://api.cohere.com/v2/chat\", model, new(0, 96, maxSize: 256000)) { } //doc: max content length 256k (chars or tokens?)\n\t\n\tpublic override object GetPostData(string systemInstruction, List<AiChatMessage> messages, double? temperature = null)\n\t\t=> new {\n\t\t\tmodel,\n\t\t\tmessages = (object[])[new { role = \"assistant\", content = systemInstruction }, .. messages.Select(o => new { role = o.role.ToString(), content = o.text })],\n\t\t\ttemperature,\n\t\t};\n\t\n\tpublic override AiChatMessage GetAnswer(JsonNode j)\n\t\t=> new AiChatMessage(ACMRole.assistant, (string)j[\"message\"][\"content\"][0][\"text\"]);\n}\n\nrecord class ModelCohereRerank : AiRerankModel {\n\tpublic ModelCohereRerank(string model) : base(\"Cohere\", \"https://api.cohere.com/v2/rerank\", model, new(4096, 1000)) { }\n\t\n\tpublic override object GetPostData(string query, IList<string> documents, int top_n = 0)\n\t\t=> new {\n\t\t\tmodel, //rerank-v3.5, rerank-english-v3.0\n\t\t\tquery,\n\t\t\tdocuments,\n\t\t\ttop_n = Math.Min(top_n > 0 ? top_n : int.MaxValue, documents.Count),\n\t\t\t//max_tokens_per_doc //4096 is default and max\n\t\t};\n\t\n\tpublic override IEnumerable<AiRerankResult> GetResults(JsonNode j)\n\t\t=> j[\"results\"].AsArray().Select(o => new AiRerankResult((int)o[\"index\"], (float)o[\"relevance_score\"]));\n}\n#endif\n#endregion\n\n#region Jina\n#if false //much worse for this task\nrecord class ModelJinaRerank : AiRerankModel {\n\tpublic ModelJinaRerank(string model = \"jina-reranker-v2-base-multilingual\") : base(\"Jina\", \"https://api.jina.ai/v1/rerank\", model, new(8000, 1000, requestPeriod: 1000)) { } //todo: limits\n\t\n\tpublic override object GetPostData(string query, IList<string> documents, int top_n = 0)\n\t\t=> new {\n\t\t\tmodel, //jina-reranker-v3 (error; paid only?), jina-reranker-v2-base-multilingual\n\t\t\tquery,\n\t\t\tdocuments,\n\t\t\ttop_n = Math.Min(top_n > 0 ? top_n : int.MaxValue, documents.Count),\n\t\t\treturn_documents = false,\n\t\t};\n\t\n\tpublic override IEnumerable<AiRerankResult> GetResults(JsonNode j)\n\t\t=> j[\"results\"].AsArray().Select(o => new AiRerankResult((int)o[\"index\"], (float)o[\"relevance_score\"]));\n}\n#endif\n#endregion\n"
  },
  {
    "path": "Au.Editor/xAI/McpServer.cs",
    "content": "using System.Text.Json;\n\nnamespace LA;\n\nclass McpServer {\n\tpublic static void Run(ReadOnlySpan<string> args) {\n#if !SCRIPT\n\t\tprocess.thisProcessCultureIsInvariant = true;\n\t\tApp.InitThisAppFoldersEtc_();\n\t\tAppSettings.Load();\n#endif\n\t\t\n\t\tvar stdin = Console.OpenStandardInput();\n\t\tvar stdout = Console.OpenStandardOutput();\n\t\tusing var reader = new StreamReader(stdin);\n\t\tusing var writer = new StreamWriter(stdout) { AutoFlush = true };\n\t\t\n\t\tvar mcpServer = new McpServer();\n\t\t\n\t\twhile (reader.ReadLine() is string json) {\n\t\t\t//print.it(json);\n\t\t\tif (mcpServer.MessageReceived(json) is string response) {\n\t\t\t\twriter.WriteLine(response);\n\t\t\t}\n\t\t}\n\t}\n\t\n\tMcpTools _tools = new();\n\t\n\t/// <summary>\n\t/// Processes a MCP message. Calls a tool if need, etc.\n\t/// </summary>\n\t/// <param name=\"json\">JSON of the received message.</param>\n\t/// <returns>Response JSON. <c>null</c> it's a notification.</returns>\n\tpublic string MessageReceived(string json) {\n\t\tvar msg = JsonDocument.Parse(json).RootElement;\n\t\t\n\t\tint id;\n\t\tif (!(msg.TryGetProperty(\"id\", out var eId) && eId.TryGetInt32(out id))) { //notification\n\t\t\treturn null;\n\t\t}\n\t\t\n\t\ttry {\n\t\t\tstring method = msg.GetProperty(\"method\").GetString();\n\t\t\t\n\t\t\tif (method == \"initialize\") {\n\t\t\t\treturn _Send(new {\n\t\t\t\t\tprotocolVersion = \"2025-06-18\",\n\t\t\t\t\tserverInfo = new { name = \"LibreAutomate\", version = \"1.0\" },\n\t\t\t\t\tcapabilities = new {\n\t\t\t\t\t\ttools = new { },\n\t\t\t\t\t\t//resources = new { },\n\t\t\t\t\t\t//prompts = new { },\n\t\t\t\t\t\t//completions = new { },\n\t\t\t\t\t\t//logging = new { },\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t} else if (method == \"ping\") {\n\t\t\t\treturn _Send(new { });\n\t\t\t} else if (method == \"tools/list\") {\n\t\t\t\treturn _Send(new { tools = _GenerateToolList() });\n\t\t\t} else if (method == \"tools/call\") {\n\t\t\t\t//print.it($\"MCP: process={process.thisProcessId}\");\n\t\t\t\t//print.it(json);\n\t\t\t\tvar p = msg.GetProperty(\"params\");\n\t\t\t\tvar text = _CallTool(p.GetProperty(\"name\").GetString(), p.GetProperty(\"arguments\"));\n\t\t\t\treturn _ReturnContent(new { type = \"text\", text });\n\t\t\t} else {\n\t\t\t\treturn _Error(-32601, \"Method not found\");\n\t\t\t}\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tif (ex is TargetInvocationException tie) ex = tie.InnerException;\n\t\t\tprint.warning(ex, \"<>Exception in LA MCP server: \");\n//#if DEBUG\n//\t\t\tDebugger.Launch();\n//#endif\n\t\t\treturn _Error(-32603, ex.ToStringWithoutStack());\n\t\t}\n\t\t\n\t\tstring _Send(object result) => JsonSerializer.Serialize(new { jsonrpc = \"2.0\", id, result });\n\t\t\n\t\tstring _ReturnContent(params object[] content) => _Send(new { content });\n\t\t\n\t\tstring _Error(int code, string message) => JsonSerializer.Serialize(new { jsonrpc = \"2.0\", id, error = new { code, message } });\n\t}\n\t\n\tstatic List<object> _GenerateToolList() {\n\t\tList<object> tools = [];\n\t\t\n\t\tforeach (var mi in typeof(McpTools).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly)) {\n\t\t\tvar toolAttr = mi.GetCustomAttribute<McpAttribute>();\n\t\t\tif (toolAttr == null) continue;\n\t\t\t\n\t\t\tDictionary<string, object> properties = [];\n\t\t\tList<string> required = [];\n\t\t\t\n\t\t\tforeach (var p in mi.GetParameters()) {\n\t\t\t\tstring type = _GetJsonType(p.ParameterType);\n\t\t\t\tDictionary<string, object> propDef = [];\n\t\t\t\tpropDef[\"type\"] = type;\n\t\t\t\tif (p.GetCustomAttribute<McpAttribute>() is { } attr) {\n\t\t\t\t\tpropDef[\"description\"] = attr.Description;\n\t\t\t\t\tif (attr.EnumValues is string sv) propDef[\"enum\"] = sv.Split('|');\n\t\t\t\t}\n\t\t\t\tif (p.ParameterType == typeof(string[])) propDef[\"items\"] = new { type = \"string\" };\n\t\t\t\tif (p.HasDefaultValue) propDef[\"default\"] = p.DefaultValue; else required.Add(p.Name);\n\t\t\t\tproperties[p.Name] = propDef;\n\t\t\t}\n\t\t\t\n\t\t\tvar inputSchema = new {\n\t\t\t\ttype = \"object\",\n\t\t\t\tproperties,\n\t\t\t\trequired\n\t\t\t};\n\t\t\t\n\t\t\ttools.Add(new {\n\t\t\t\tname = mi.Name,\n\t\t\t\t//title = method.Name,\n\t\t\t\tdescription = toolAttr.Description,\n\t\t\t\tinputSchema\n\t\t\t});\n\t\t}\n\t\t\n\t\t//print.it(JsonSerializer.Serialize(tools, new JsonSerializerOptions() { WriteIndented = true }));\n\t\treturn tools;\n\t\t\n\t\tstatic string _GetJsonType(Type t) {\n\t\t\tif (t.IsByRef) throw new NotSupportedException(\"ByRef parameter\");\n\t\t\tif (t == typeof(string)) return \"string\";\n\t\t\tif (t == typeof(bool)) return \"boolean\";\n\t\t\tif (t == typeof(int) || t == typeof(long)) return \"integer\";\n\t\t\tif (t == typeof(double)) return \"number\";\n\t\t\tif (t == typeof(string[])) return \"array\";\n\t\t\tthrow new NotSupportedException($\"MCP tool parameter type '{t.FullName}' is not supported. Expected string, bool, int, long, double or string[].\");\n\t\t}\n\t}\n\t\n\tstring _CallTool(string name, JsonElement jArgs) {\n\t\tvar mi = typeof(McpTools).GetMethod(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly) ?? throw new ArgumentException($\"Tool `{name}` does not exist\");\n\t\tvar ap = mi.GetParameters();\n\t\tvar ao = ap.Length > 0 ? new object[ap.Length] : null;\n\t\tif (ap.Length > 0) {\n\t\t\tfor (int i = 0; i < ap.Length; i++) {\n\t\t\t\tvar p = ap[i];\n\t\t\t\tvar t = p.ParameterType;\n\t\t\t\tobject v = null;\n\t\t\t\tif (jArgs.TryGetProperty(p.Name, out var jArg)) {\n\t\t\t\t\t//print.it(jArg.ValueKind);\n\t\t\t\t\tif (t == typeof(string)) v = jArg.GetString();\n\t\t\t\t\telse if (t == typeof(bool)) v = jArg.GetBoolean();\n\t\t\t\t\telse if (t == typeof(int)) v = jArg.GetInt32();\n\t\t\t\t\telse if (t == typeof(long)) v = jArg.GetInt64();\n\t\t\t\t\telse if (t == typeof(double)) v = jArg.GetDouble();\n\t\t\t\t\telse if (t == typeof(string[])) {\n\t\t\t\t\t\tvar a1 = new string[jArg.GetArrayLength()];\n\t\t\t\t\t\tint j = 0;\n\t\t\t\t\t\tforeach (var jElem in jArg.EnumerateArray()) {\n\t\t\t\t\t\t\ta1[j++] = jElem.GetString();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tv = a1;\n\t\t\t\t\t} else throw new ArgumentException($\"Bad type of argument `{p.Name}`.\");\n\t\t\t\t} else {\n\t\t\t\t\tif (p.HasDefaultValue) v = p.DefaultValue;\n\t\t\t\t\telse throw new ArgumentException($\"Missing argument `{p.Name}`.\");\n\t\t\t\t}\n\t\t\t\tao[i] = v;\n\t\t\t}\n\t\t}\n\t\treturn mi.Invoke(_tools, ao) as string;\n\t}\n}\n\n[AttributeUsage(AttributeTargets.Method | AttributeTargets.Parameter)]\nclass McpAttribute : Attribute {\n\tpublic McpAttribute(string description) => Description = description;\n\t\n\t/// <summary>\n\t/// JSON <c>\"description\"</c>.\n\t/// </summary>\n\tpublic string Description { get; }\n\t\n\t/// <summary>\n\t/// JSON <c>\"enum\"</c> like <c>\"red|green|blue\"</c>.\n\t/// </summary>\n\tpublic string EnumValues { get; init; }\n}\n"
  },
  {
    "path": "Au.Editor/xAI/McpTools.cs",
    "content": "using AI;\nusing System.Security.Authentication;\n\nnamespace LA;\n\nclass McpTools {\n\treadonly string _appDir = LA.folders2.La;\n\t\n\tpublic McpTools() {\n\t\tAiModel.ApiKeys = App.Settings.ai_ak;\n\t}\n\t\n#if !true\n\t[Mcp(\"Echoes the input string\")]\n\tpublic string Echo([Mcp(\"Any text\")] string text, [Mcp(\"Make lowercase\")] bool lowerCase = false) {\n\t\treturn lowerCase ? text.ToLowerInvariant() : text;\n\t}\n\t\n\t[Mcp(\"Test enum\")]\n\tpublic string TestEnum([Mcp(\"Color\", EnumValues = \"red|green|blue\")] string color) {\n\t\treturn color;\n\t}\n\t\n\t[Mcp(\"Test numbers\")]\n\tpublic string TestNumbers([Mcp(\"Int\")] int a, [Mcp(\"Long\")] long b, [Mcp(\"Double\")] double c) {\n\t\treturn (a + b + c).ToS();\n\t}\n\t\n\t[Mcp(\"Test array\")]\n\tpublic string TestArray([Mcp(\"Colors\")] string[] a) {\n\t\treturn string.Join('|', a);\n\t}\n#else\n\t[Mcp(\"Gets the table of contents of the LibreAutomate documentation.\")]\n\tpublic string get_la_docs_toc() {\n\t\treturn filesystem.loadText($@\"{_appDir}\\toc-ai.yml\");\n\t}\n\t\n\t[Mcp(\"Gets specified articles from the LibreAutomate documentation.\")]\n\tpublic string get_la_docs(\n\t\t[Mcp(\"\"\"\nNames of articles.\nNames of API articles are like \"Namespace.Type.Member\", \"Namespace.Type\", \"Namespace.Type<T>.Member\".\nNames of non-API articles are like \"[articles] name\", \"[editor] name\", \"[cookbook] name\".\n\"\"\")]\n\t\tstring[] names\n\t\t) {\n\t\tif (App.Settings.ai_mcp_print) print.it($\"<><lc yellowgreen><\\a>MCP {nameof(get_la_docs)}:\\n{string.Join('\\n', names)}</\\a><>\");\n\t\t\n\t\tusing var db = new sqlite($@\"{_appDir}\\doc-ai.db\", SLFlags.SQLITE_OPEN_READONLY);\n\t\tusing var sta = db.Statement($\"SELECT name,text FROM doc WHERE name IN ({string.Join(',', names.Select(_ => \"?\"))})\");\n\t\tsta.BindAll(names);\n\t\tvar texts = new string[names.Length];\n\t\twhile (sta.Step()) {\n\t\t\tstring name = sta.GetText(0), text = sta.GetText(1);\n\t\t\ttexts[names.IndexOf(name)] = text;\n\t\t}\n\t\tDebug_.PrintIf(texts.Contains(null));\n\t\t\n\t\tvar sb = new StringBuilder();\n\t\tsb.AppendLine(\"Below are the articles you requested.\").AppendLine(c_listFormatInfo);\n\t\tfor (int i = 0; i < texts.Length; i++) {\n\t\t\t_AppendArticle(sb, i, names[i], texts[i]);\n\t\t}\n\t\t\n\t\treturn sb.ToString();\n\t}\n\t\n\tvoid _AppendArticle(StringBuilder sb, int i, string name, string text) {\n\t\tsb.AppendFormat(\"\\r\\n--- {0}. {1} ---\\r\\n\\r\\n{2}\\r\\n\", i + 1, name, text);\n\t}\n\t\n\tconst string c_listFormatInfo = \"\"\"\n\nArticle separator line format: `--- N. Name ---`, where N is 1-based index of the article.\n\nNames of library API member articles are like `Namespace.Type.Member`. Namespaces: `Au`, `Au.More`, `Au.Triggers`, `Au.Types`.\nNames of other articles have a prefix:\n- `[articles]` - a library documentation article other than API member reference\n- `[cookbook]` - a how-to article with code examples\n- `[editor]` - LibreAutomate IDE documentation\n\"\"\";\n\t\n\t[Mcp(\"\"\"\nReturns requested information about LibreAutomate API or IDE.\nWhenever you need information about LibreAutomate API or IDE, create one or more short queries and call this tool.\nThis tool uses semantic search (AI embedding) to find the requested information.\nIf this tool receives a list of queries (one per line), it processes them paralelley (much faster) and optimizes results.\n\"\"\")]\n\tpublic string find_la_docs(\n\t\t[Mcp(\"\"\"\nOne or more short queries for semantic search, one per line.\nIMPORTANT: each query must be focused on a **single** task or concept.\nKeyword lists are discouraged. Use meaningful phrases.\nExamples of good queries: \"activate window\", \"send keys\", \"key names and syntax\".\nExample of a BAD query: \"activate Chrome window send keys Ctrl+L LibreAutomate\". What's bad here: it's not a meaningful single-task phrase but a soup of search keywords for two tasks; it includes names of windows and keys; redundant context info \"LibreAutomate\".\n\"\"\")]\n\t\tstring query,\n\t\t[Mcp(\"The `description` text of the `query` parameter of this tool, as specified in the MCP tool definition. This is to ensure you know how to use this parameter.\")]\n\t\tstring passPhrase\n\t\t) {\n#if DEBUG\n\t\tprint.it(\"--- passPhrase:\");\n\t\tprint.it(passPhrase);\n\t\tprint.it(\"---\");\n#endif\n\t\t\n\t\tif (App.Settings.ai_mcp_print) print.it($\"<><lc yellowgreen>MCP {nameof(find_la_docs)}:\\r\\n<><lc LemonChiffon><\\a>{query}</\\a><>\");\n\t\t\n\t\tvar lines = query.Lines(noEmpty: true);\n\t\tvar aResults = new List<(string name, string text)>[lines.Length];\n\t\t\n\t\tvar emModel = AiModel.GetModel<AiEmbeddingModel>(App.Settings.ai_modelEmbed, displayName: true) ?? throw new AuException(\"Missing settings in LibreAutomate. Please go to Options > AI and select models for documentation search.\");\n\t\tvar rrModel = AiModel.GetModel<AiRerankModel>(App.Settings.ai_modelRerank, displayName: true);\n\t\tif (rrModel == null) AiModel.RerankerModelWarning();\n\t\t\n\t\ttry {\n\t\t\tvar em = new Embeddings(emModel);\n\t\t\tvar ems = em.GetDocsEmbeddings();\n\t\t\t\n\t\t\tParallel.For(0, lines.Length, i => _QueryLine(lines[i], aResults[i] = []));\n\t\t\t\n\t\t\tvoid _QueryLine(string query, List<(string name, string text)> results) {\n\t\t\t\tint takePlus = Math.Min(20, query.Count(c => c is <= ' ' or ',' or '.' or ';' or '?'));\n\t\t\t\tint take = 15 + takePlus;\n\t\t\t\t\n\t\t\t\tvar queryVector = em.CreateEmbedding(query);\n\t\t\t\tvar topAll = em.GetTopMatches(queryVector, ems, rrModel == null ? 30 : 100);\n\t\t\t\tif (topAll.Count == 0) return;\n\t\t\t\t\n\t\t\t\tDictionary<string, (float score, bool summary)> dTop = [];\n\t\t\t\tforeach (var v in topAll) {\n\t\t\t\t\tvar name = v.f.name;\n\t\t\t\t\tbool isSum = name[0] == '+';\n\t\t\t\t\tif (isSum) name = name[1..];\n\t\t\t\t\tdTop.TryAdd(name, (v.score, isSum));\n\t\t\t\t}\n\t\t\t\tvar aTop = dTop.Select(o => (name: o.Key, v: o.Value)).OrderByDescending(o => o.v.score).ToArray();\n\t\t\t\t\n\t\t\t\tstring[] names = aTop.Select(o => o.name).ToArray();\n\t\t\t\tstring[] texts = em.GetDocsTexts(names);\n\t\t\t\t\n\t\t\t\tif (rrModel != null) {\n\t\t\t\t\tvar headers = rrModel.GetHeaders();\n\t\t\t\t\tvar post = rrModel.GetPostData(query, texts);\n\t\t\t\t\tvar j = rrModel.Post(post, headers).Json();\n\t\t\t\t\t//print.it(j.ToJsonString(new() { WriteIndented = true }));\n\t\t\t\t\tvar ar = rrModel.GetResults(j).ToArray();\n\t\t\t\t\tfloat maxScore = ar[0].score, minScore = maxScore - (.3f + takePlus / 200f);\n\t\t\t\t\t//print.it(take, maxScore, minScore, maxScore - minScore);\n\t\t\t\t\tint i = 0;\n\t\t\t\t\tforeach (var v in ar) {\n\t\t\t\t\t\tif (i++ > take || v.score < minScore || v.score < .4f) break;\n\t\t\t\t\t\tresults.Add((names[v.index], texts[v.index]));\n\t\t\t\t\t\t//print.it(v.score, names[v.index]);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tint i = 0;\n\t\t\t\t\tfloat minScore = aTop[0].v.score - (.2f + takePlus / 200f);\n\t\t\t\t\tforeach (var v in aTop) {\n\t\t\t\t\t\tif (v.v.score < minScore) break;\n\t\t\t\t\t\tresults.Add((v.name, texts[i++]));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcatch (Exception ex) when (ex is InvalidCredentialException or AggregateException { InnerException: InvalidCredentialException }) {\n\t\t\tvar api = emModel.api;\n\t\t\tthrow new AuException($\"Missing settings in LibreAutomate. Please go to Options > AI and set the API key for {api}.\\nYou can create an API key in your account on the {api} website.\");\n\t\t}\n\t\t\n\t\tDictionary<string, string> dResults = [];\n\t\tfor (int i = 0, n = aResults.Max(o => o.Count); i < n; i++) {\n\t\t\tforeach (var list in aResults) {\n\t\t\t\tif (i < list.Count) dResults.TryAdd(list[i].name, list[i].text);\n\t\t\t}\n\t\t}\n\t\t\n\t\tvar sb = new StringBuilder();\n\t\t\n\t\tif (passPhrase.Length < 30 && lines.Length == 1 && query.Length > 30) {\n\t\t\tsb.AppendLine(\"Warning: the `passPhrase` argument is too short. Likely you (AI) ignore the description of the `query` parameter and use an incorrect query. Then the quality of results is bad.\\r\\n\");\n\t\t\t//Some models pass exact descriptions. Some models pass summaries. Some smaller models pass eg the parameter name.\n\t\t}\n\t\t\n\t\tsb.AppendLine($\"\"\"\nFound {dResults.Count} articles that likely contain the requested information.\n{c_listFormatInfo}\n\nList of article names:\n\"\"\");\n\t\t\n\t\tsb.AppendLine();\n\t\tforeach (var (i, name) in dResults.Keys.Index()) {\n\t\t\tsb.AppendLine($\"{i + 1}. {name}\");\n\t\t\tif (App.Settings.ai_mcp_print) print.it($\"{i + 1}. {name}\");\n\t\t}\n\t\t\n\t\tsb.AppendLine(\"\\r\\nArticles:\");\n\t\t\n\t\tforeach (var (i, v) in dResults.Index()) {\n\t\t\t_AppendArticle(sb, i, v.Key, v.Value);\n\t\t}\n\t\t\n\t\t//print.it(sb); print.scrollToTop();\n\t\t\n\t\treturn sb.ToString();\n\t}\n#endif\n}\n"
  },
  {
    "path": "Au.Editor/xMisc/EnvVarUpdater.cs",
    "content": "using System.Collections;\nusing DictSS = System.Collections.Generic.Dictionary<string, string>;\n\nnamespace LA;\n\nclass EnvVarUpdater {\n\tDictSS _r;\n\t\n\t//Called at startup.\n\tpublic EnvVarUpdater() {\n\t\t_r = _GetVars();\n\t\t//print.it(_r);\n\t\t\n\t\tvar s2 = Environment.GetEnvironmentVariable(\"Path\");\n\t\tif (_r.TryGetValue(\"Path\", out var s1) && s1 != s2) {\n\t\t\t//Debug_.PrintIf( //no, some VS versions change PATH, then always warning when this process started by VS\n\t\t\t//\ts2.Trim(';').Replace(\";;\", \";\") != s1, //somehow s2 ends with ';' if not admin, but no ';' if admin. Once was \";;\".\n\t\t\t//\t$\"PATH env var changed at startup:\\n{s1}\\n{s2}\");\n\t\t\tEnvironment.SetEnvironmentVariable(\"Path\", s1);\n\t\t}\n\t}\n\t\n\t//Called on WM_SETTINGCHANGE(\"Environment\").\n\tpublic void WmSettingchange() {\n\t\tcsvTable csv = new();\n\t\t\n\t\tvar rOld = _r;\n\t\t_r = _GetVars();\n\t\tvar p = _GetVars(EnvironmentVariableTarget.Process);\n\t\t\n\t\t//for each env var deleted from registry\n\t\tforeach (var k in rOld.Keys.Except(_r.Keys, StringComparer.OrdinalIgnoreCase)) {\n\t\t\tif (p.TryGetValue(k, out var s1) && s1 == rOld[k]) {\n\t\t\t\tDebug_.Print($\"Env var deleted: {k}\");\n\t\t\t\tEnvironment.SetEnvironmentVariable(k, null);\n\t\t\t\tcsv.AddRow(k, null);\n\t\t\t}\n\t\t}\n\t\t\n\t\t//for each env var added or changed in registry\n\t\tforeach (var (k, v) in _r.Except(rOld)) {\n\t\t\tif (p.TryGetValue(k, out var pv)) {\n\t\t\t\tif (pv == v) continue;\n\t\t\t\t//print.it(rOld.ContainsKey(k));\n\t\t\t\tif (!rOld.TryGetValue(k, out var old) || pv != old) continue;\n\t\t\t}\n\t\t\tDebug_.Print(pv == null ? $\"Env var added: {k} = \\\"{v}\\\"\" : $\"Env var changed: {k} = \\\"{v}\\\",        was \\\"{pv}\\\"\");\n\t\t\tEnvironment.SetEnvironmentVariable(k, v);\n\t\t\tcsv.AddRow(k, v);\n\t\t}\n\t\t\n\t\t//pass to script processes\n\t\tif (csv.RowCount > 0) {\n\t\t\tvar s = csv.ToString();\n\t\t\tfor (var w = wnd.findFast(null, script.c_auxWndClassName, true); !w.Is0; w = wnd.findFast(null, script.c_auxWndClassName, true, w))\n\t\t\t\tif (!w.IsOfThisProcess) w.SendTimeout(200, out _, Api.WM_SETTEXT, script.c_msg_wmsettext_UpdateEnvVar, s);\n\t\t}\n\t}\n\t\n\tDictSS _GetVars(EnvironmentVariableTarget target) {\n\t\tvar id = Environment.GetEnvironmentVariables(target);\n\t\tDictSS r = new(id.Count, StringComparer.OrdinalIgnoreCase);\n\t\tforeach (DictionaryEntry e in id) r.TryAdd(e.Key as string, e.Value as string);\n\t\treturn r;\n\t}\n\t\n\tDictSS _GetVars() {\n\t\t//Join registry env vars user + machine.\n\t\t//\tJoin PATH machine+\";\"+user. Trim ';'.\n\t\t//\tIgnore other machine vars that exist in user.\n\t\t\n\t\tvar r = _GetVars(EnvironmentVariableTarget.User);\n\t\tvar m = Environment.GetEnvironmentVariables(EnvironmentVariableTarget.Machine);\n\t\tbool pathOK = false;\n\t\tforeach (DictionaryEntry e in m) {\n\t\t\tstring k = e.Key as string, v = e.Value as string;\n\t\t\tif (!r.TryAdd(k, v)) {\n\t\t\t\tif (!pathOK && (pathOK = k.Eqi(\"Path\"))) r[\"Path\"] = (v + (v.Ends(';') ? null : \";\") + r[\"Path\"]).Trim(';');\n\t\t\t}\n\t\t}\n\t\tif (!pathOK && r.TryGetValue(\"Path\", out var s1)) r[\"Path\"] = s1.Trim(';');\n\t\treturn r;\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/xMisc/Pip.cs",
    "content": "//AxMSTSCLib.dll and MSTSCLib.dll were created with this cmd in Visual Studio: aximp.exe c:\\windows\\system32\\mstscax.dll.\n//\tSee https://learn.microsoft.com/en-us/windows/win32/termserv/calling-non-scriptable-interfaces.\n\nusing AxMSTSCLib;\nusing MSTSCLib;\nusing System.Windows.Forms;\nusing Microsoft.Win32;\nusing System.Runtime.Loader;\nusing wpfc = System.Windows.Controls;\n\nnamespace LA;\n\nstatic class Pip {\n\tpublic static bool noActivate;\n\tstatic wnd _wMsg;\n\t\n\t/// <summary>\n\t/// Called at startup in non-admin process when command line starts with /pip.\n\t/// </summary>\n\t/// <param name=\"args\"><c>args</c> without the first.</param>\n\t/// <returns>Process exit code (non-0 on error).</returns>\n\tpublic static int Run(ReadOnlySpan<string> args) {\n\t\tprocess.ThisThreadSetComApartment_(ApartmentState.STA);\n\t\tprocess.thisProcessCultureIsInvariant = true;\n\t\tApp.InitThisAppFoldersEtc_();\n\t\t\n\t\tif (args.Length > 0) {\n\t\t\tswitch (args[0].Lower()) {\n\t\t\tcase \"/enablecs\":\n\t\t\t\treturn api.WTSEnableChildSessions(true) ? 0 : 1;\n\t\t\tcase \"/disablecs\":\n\t\t\t\treturn api.WTSEnableChildSessions(false) ? 0 : 1;\n\t\t\tcase \"/settings\":\n\t\t\t\tPipWindow.SettingsDialog();\n\t\t\t\treturn 0;\n\t\t\tcase \"/noactivate\":\n\t\t\t\tnoActivate = true;\n\t\t\t\tbreak;\n\t\t\tdefault: return 2;\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (!script.TrySingle_(@\"Global\\Au.PiP-mutex\")) {\n\t\t\tif (!noActivate) {\n\t\t\t\tvar w = wnd.find(\"PiP session\", \"*.Window.*\", process.thisExeName);\n\t\t\t\tif (!w.Is0) w.ActivateL(true);\n\t\t\t\telse print.it(\"A PiP already exists on this computer. Single PiP allowed.\");\n\t\t\t}\n\t\t\treturn 3;\n\t\t}\n\t\t\n\t\t_wMsg = WndUtil.CreateMessageOnlyWindow(\"#32770\", \"Au.PiP-msg\");\n\t\t\n\t\tif (!osVersion.minWin8_1) return _Error(\"Requires Windows 8.1 or later.\"); //tested: on 8.0 error \"class not registered\"\n\t\tif (miscInfo.isChildSession) return _Error(\"Can't start another PiP session from PiP session.\");\n\t\tif (Registry.GetValue(@\"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\", \"ProductName\", null) is string sPN && sPN.Contains(\" Home\"))\n\t\t\treturn _Error($\"PiP does not work on Windows Home editions. Your OS is {sPN}.\");\n\t\t\n\t\tif (api.WTSIsChildSessionsEnabled(out bool y) && !y) {\n\t\t\tbool isAdmin = uacInfo.isAdmin;\n\t\t\tstring sInfo = $\"PiP uses Windows feature \\\"Child Sessions\\\". Click OK to enable it.{(isAdmin ? \"\" : \"\\n\\nAdministrator rights required to enable this feature.\")}\";\n\t\t\tif (!dialog.showOkCancel(\"Picture-in-picture setup\", sInfo, icon: isAdmin ? 0 : DIcon.Shield)) return 1;\n\t\t\tbool enabled = isAdmin ? api.WTSEnableChildSessions(true) : run.it(process.thisExePath, \"/pip /enablecs\", RFlags.Admin | RFlags.WaitForExit).ProcessExitCode == 0;\n\t\t\tif (!enabled) return _Error(\"Failed to enable Child Sessions.\");\n\t\t}\n\t\t\n\t\tstatic int _Error(string s) {\n\t\t\tdialog.showError(\"PiP error\", s);\n\t\t\treturn 1;\n\t\t}\n\t\t\n\t\tAssemblyLoadContext.Default.Resolving += static (alc, an) => alc.LoadFromAssemblyPath(folders.ThisAppBS + an.Name + \".dll\");\n\t\tApplication.Run(new PipWindow());\n\t\treturn 0;\n\t}\n\t\n\tinternal static void SetConnected_(int connected) {\n\t\t_wMsg.SetWindowLong(GWL.DWL.USER, connected);\n\t}\n}\n\nfile class PipWindow : Form {\n\tAxMsRdpClient9NotSafeForScripting _axRdp;\n\tIMsRdpClient9 _rdp;\n\twnd _w;\n\tFullScreenWindow _fullScreen;\n\tlong _reconnecting;\n\t\n\tpublic PipWindow() {\n\t\tText = \"PiP session\";\n\t\tIcon = icon.trayIcon(Api.IDI_APPLICATION + 2).ToGdipIcon();\n\t\t/*\n*PhosphorIcons.RectangleFill #BBE3FF %.5,2.5,.5,2.5,f\n*PhosphorIcons.PictureInPictureLight #909090\n\t\t*/\n\t\t\n\t\tControls.Add(_axRdp = new() { Dock = DockStyle.Fill });\n\t\t\n\t\tStartPosition = FormStartPosition.Manual;\n\t\tif (_sett.wndPos == null) {\n\t\t\tvar rm = screen.primary.WorkArea;\n\t\t\tbase.SetDesktopBounds(rm.left + rm.Width / 3, rm.top, rm.Width * 2 / 3, rm.Height * 3 / 4);\n\t\t}\n\t\tWndSavedRect.Restore(this, _sett.wndPos, o => _sett.wndPos = o);\n\t}\n\t\n\tprotected override void OnHandleCreated(EventArgs e) {\n\t\t_w = this.Hwnd();\n\t\t\n\t\tif (osVersion.minWin11) unsafe {\n\t\t\t\tvar rbp = api.DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_DONOTROUND;\n\t\t\t\tapi.DwmSetWindowAttribute(_w, api.DWMWA_WINDOW_CORNER_PREFERENCE, &rbp, 4);\n\t\t\t}\n\t\t\n\t\tbase.OnHandleCreated(e);\n\t}\n\t\n\tprotected override void OnLoad(EventArgs e) {\n\t\t_rdp = (IMsRdpClient9)_axRdp.GetOcx();\n\t\tvar advSett = _rdp.AdvancedSettings9;\n\t\tvar exSett = (IMsRdpExtendedSettings)_rdp;\n\t\t\n\t\t_rdp.Server = \"localhost\";\n\t\texSett.set_Property(\"ConnectToChildSession\", true);\n\t\tadvSett.EnableCredSspSupport = true;\n\t\t\n\t\t//_rdp.UserName = Environment.UserName;\n\t\t//advSett.ClearTextPassword = \"?\";\n\t\t\n\t\ttry {\n\t\t\tint scale = Dpi.Scale(100, _w);\n\t\t\tif (scale > 100) {\n\t\t\t\texSett.set_Property(\"DesktopScaleFactor\", (uint)Math.Min(500, scale));\n\t\t\t\texSett.set_Property(\"DeviceScaleFactor\", 100u);\n\t\t\t}\n\t\t\t\n\t\t\t//advSett.SmartSizing = true; //does not work; not useful\n\t\t\tadvSett.ContainerHandledFullScreen = 1; //less trouble\n\t\t\tadvSett.PinConnectionBar = false;\n\t\t\t((IMsRdpClientNonScriptable3)_rdp).ConnectionBarText = this.Text; //tested: other members of \"NonScriptable\" interfaces are not interesting.\n\t\t\tif (!_sett.redirectClipboard) advSett.RedirectClipboard = false;\n\t\t\t//if (osVersion.minWin10_1803) _exSett.set_Property(\"ManualClipboardSyncEnabled\", true); //exception when trying to get the `Clipboard` property. Bug in native method. Code: if (_rdp is IMsRdpClientNonScriptable7 rdpns7) timer.every(2000, _=> { print.it(rdpns7.Clipboard); });\n\t\t\t//advSett.EnableWindowsKey = 1; //default 1, but does not work\n\t\t\tadvSett.Compress = 0;\n\t\t\tadvSett.RedirectSmartCards = true;\n\t\t\t//timer.after(3000, _ => { es.set_Property(\"ShowSessionDiagnostics\", true); }); //does not work\n\t\t\t\n\t\t\tif (Api.WTSGetChildSessionId(out int csid)) {\n\t\t\t\t//print.it(csid);\n\t\t\t} else {\n\t\t\t\t//if editor not set to run at startup, set to run at startup once\n\t\t\t\tbool editorAutoRun = Registry.GetValue(@\"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run\", \"Au.Editor\", null) is string s1 && s1.RxIsMatch($\"(?i)^\\\".+?Au.Editor.exe\\\"\");\n\t\t\t\tif (!editorAutoRun) Registry.SetValue(@\"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce\", \"Au.Editor (PiP)\", $\"\\\"{process.thisExePath}\\\" /a\");\n\t\t\t}\n\t\t}\n\t\tcatch (Exception ex) { print.warning(ex); }\n\t\t\n\t\t_Events();\n\t\t\n\t\t_rdp.Connect();\n\t\t\n\t\t_axRdp.SizeChanged += (_, _) => _UpdateDesktopSize();\n\t\tAutoScaleMode = AutoScaleMode.Dpi; //not in ctor, to avoid DPI-scaling of the window size at startup\n\t\t\n\t\tif (Pip.noActivate) {\n\t\t\t_w.SetWindowPos(SWPFlags.NOACTIVATE | SWPFlags.NOMOVE | SWPFlags.NOSIZE, zorderAfter: SpecHWND.BOTTOM);\n\t\t\t//var wa = wnd.active;\n\t\t\t//if (!wa.Is0 && wa != _w) {\n\t\t\t//\t_w.SetWindowPos(SWPFlags.NOACTIVATE | SWPFlags.NOMOVE | SWPFlags.NOSIZE, zorderAfter: wa);\n\t\t\t//\t//_w.TaskbarButton.Flash(1); //no\n\t\t\t//}\n\t\t} else {\n\t\t\t_w.ActivateL(); //window in background after UAC consent\n\t\t}\n\t\t\n\t\tbase.OnLoad(e);\n\t}\n\t\n\tprotected override bool ShowWithoutActivation => Pip.noActivate;\n\t\n\tprotected override CreateParams CreateParams {\n\t\tget {\n\t\t\tvar p = base.CreateParams;\n\t\t\tif (Pip.noActivate) p.ExStyle |= (int)WSE.NOACTIVATE; //workaround for: the ActiveX control activates the window, although ShowWithoutActivation returns true\n\t\t\treturn p;\n\t\t}\n\t}\n\n\tprotected override void OnFormClosing(FormClosingEventArgs e) {\n\t\t_fullScreen?.SetFullScreen(false);\n\t\tPip.SetConnected_(0);\n\t\tbase.OnFormClosing(e);\n\t}\n\t\n\tvoid _Events() {\n\t\t_axRdp.OnConnected += (sender, e) => {\n\t\t\t_PrintEvent(\"OnConnected\");\n\t\t\tPip.SetConnected_(1);\n\t\t\t_reconnecting = 0;\n\t\t\tif (Pip.noActivate) _w.SetExStyle(WSE.NOACTIVATE, WSFlags.Remove);\n\t\t};\n\t\t_axRdp.OnDisconnected += (sender, e) => {\n\t\t\t_PrintEvent(\"OnDisconnected\", _rdp.ExtendedDisconnectReason);\n\t\t\tPip.SetConnected_(0);\n\t\t\tif (_reconnecting != 0 && Environment.TickCount64 - _reconnecting < 5000) {\n\t\t\t\ttimer.after(100, _ => {\n\t\t\t\t\t//print.it(\"reconnecting\"); //tested: succeeds after 400 ms\n\t\t\t\t\t_rdp.AdvancedSettings9.RedirectClipboard = _sett.redirectClipboard;\n\t\t\t\t\t_rdp.Connect();\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tClose();\n\t\t\t}\n\t\t};\n\t\t_axRdp.OnRequestGoFullScreen += (sender, e) => {\n\t\t\t_PrintEvent(\"OnRequestGoFullScreen\");\n\t\t\t_SetFullScreen(true);\n\t\t};\n\t\t_axRdp.OnRequestLeaveFullScreen += (sender, e) => {\n\t\t\t_PrintEvent(\"OnRequestLeaveFullScreen\");\n\t\t\t_SetFullScreen(false);\n\t\t};\n\t\t_axRdp.OnRequestContainerMinimize += (sender, e) => {\n\t\t\t_PrintEvent(\"OnRequestContainerMinimize\");\n\t\t\t_w.ShowMinimized();\n\t\t};\n\t\t_axRdp.OnConnectionBarPullDown += (sender, e) => {\n\t\t\t_PrintEvent(\"OnConnectionBarPullDown\");\n\t\t\t_rdp.FullScreen = false;\n\t\t\tif (mouse.isPressed(MButtons.Left) && _w.GetWindowAndClientRectInScreen(out var r, out var rc) && rc.top > r.top) {\n\t\t\t\tr.bottom = rc.top;\n\t\t\t\tApi.SetCursorPos(r.CenterX, r.CenterY);\n\t\t\t\t_w.Send(Api.WM_SYSCOMMAND, Api.SC_MOVE);\n\t\t\t}\n\t\t};\n\t\t_axRdp.OnAuthenticationWarningDisplayed += (sender, e) => {\n\t\t\t_PrintEvent(\"OnAuthenticationWarningDisplayed\");\n\t\t\tprint.it(\"Info: If PiP prompts for credentials every time: sign out of the PiP session, then sign out of your main session and sign in again.\");\n\t\t\tif (Pip.noActivate) {\n\t\t\t\t_w.SetExStyle(WSE.NOACTIVATE, WSFlags.Remove);\n\t\t\t\t_w.ActivateL();\n\t\t\t}\n\t\t};\n#if DEBUG\n\t\t_axRdp.OnConnecting += (sender, e) => { _PrintEvent(\"OnConnecting\"); };\n\t\t_axRdp.OnLoginComplete += (sender, e) => { _PrintEvent(\"OnLoginComplete\"); };\n\t\t_axRdp.OnChannelReceivedData += (sender, e) => { _PrintEvent(\"OnChannelReceivedData\"); };\n\t\t_axRdp.OnFatalError += (sender, e) => { _PrintEvent(\"OnFatalError\", e.errorCode); };\n\t\t_axRdp.OnWarning += (sender, e) => { _PrintEvent(\"OnWarning\", e.warningCode); };\n\t\t_axRdp.OnEnterFullScreenMode += (sender, e) => { _PrintEvent(\"OnEnterFullScreenMode \"); };\n\t\t_axRdp.OnLeaveFullScreenMode += (sender, e) => { _PrintEvent(\"OnLeaveFullScreenMode \"); };\n\t\t_axRdp.OnRemoteDesktopSizeChange += (sender, e) => { _PrintEvent(\"OnRemoteDesktopSizeChange\", e.width, e.height); };\n\t\t_axRdp.OnIdleTimeoutNotification += (sender, e) => { _PrintEvent(\"OnIdleTimeoutNotification\"); };\n\t\t_axRdp.OnConfirmClose += (sender, e) => { _PrintEvent(\"OnConfirmClose\"); e.pfAllowClose = true; };\n\t\t//_axRdp.OnReceivedTSPublicKey += (sender, e) => { _PrintEvent(\"OnReceivedTSPublicKey\"); };\n\t\t_axRdp.OnAutoReconnecting += (sender, e) => { _PrintEvent(\"OnAutoReconnecting\"); };\n\t\t_axRdp.OnAuthenticationWarningDismissed += (sender, e) => { _PrintEvent(\"OnAuthenticationWarningDismissed\"); };\n\t\t//_axRdp.OnRemoteProgramResult += (sender, e) => { _PrintEvent(\"OnRemoteProgramResult\"); };\n\t\t//_axRdp.OnRemoteProgramDisplayed += (sender, e) => { _PrintEvent(\"OnRemoteProgramDisplayed\"); };\n\t\t//_axRdp.OnRemoteWindowDisplayed += (sender, e) => { _PrintEvent(\"OnRemoteWindowDisplayed\"); };\n\t\t_axRdp.OnLogonError += (sender, e) => { _PrintEvent(\"OnLogonError\", e.lError); };\n\t\t_axRdp.OnFocusReleased += (sender, e) => { _PrintEvent(\"OnFocusReleased\"); };\n\t\t//_axRdp.OnUserNameAcquired += (sender, e) => { _PrintEvent(\"OnUserNameAcquired\", e.bstrUserName); };\n\t\t//_axRdp.OnMouseInputModeChanged += (sender, e) => { _PrintEvent(\"OnMouseInputModeChanged\"); };\n\t\t_axRdp.OnServiceMessageReceived += (sender, e) => { _PrintEvent(\"OnServiceMessageReceived\"); };\n\t\t//_axRdp.OnNetworkStatusChanged += (sender, e) => { _PrintEvent(\"OnNetworkStatusChanged\"); };\n\t\t_axRdp.OnDevicesButtonPressed += (sender, e) => { _PrintEvent(\"OnDevicesButtonPressed\"); };\n\t\t_axRdp.OnAutoReconnected += (sender, e) => { _PrintEvent(\"OnAutoReconnected\"); };\n\t\t_axRdp.OnAutoReconnecting2 += (sender, e) => { _PrintEvent(\"OnAutoReconnecting2\"); };\n#endif\n\t}\n\t\n\t[Conditional(\"DEBUG\")]\n\tstatic void _PrintEvent(string s) {\n\t\t//print.it($\"<><c green>event <_>{s}</_><>\");\n\t}\n\t\n\t[Conditional(\"DEBUG\")]\n\tstatic void _PrintEvent(object value1, object value2, params object[] more) {\n\t\t//print.it($\"<><c green>event <_>{print.util.toList(\", \", value1, value2, more)}</_><>\");\n\t}\n\t\n\tvoid _UpdateDesktopSize() {\n\t\tif (_dontUpdateDesktopSize) return;\n\t\tvar cs = _axRdp.ClientSize; int wid = cs.Width, hei = cs.Height;\n\t\tif (wid < 200 || hei < 200) return; //eg minimized\n\t\tif (wid == _rdp.DesktopWidth && hei == _rdp.DesktopHeight) return; //eg restored from minimized\n\t\t\n\t\t//print.it(\"_UpdateDesktopSize\", wid, hei, _rdp.DesktopWidth, _rdp.DesktopHeight);\n\t\t\n\t\tint scale = Dpi.Scale(100, _w);\n\t\ttry { _rdp.UpdateSessionDisplaySettings((uint)wid, (uint)hei, 0, 0, 0, (uint)scale, 100); }\n\t\tcatch { } //eg soon after starting. Never mind.\n\t}\n\t\n\tvoid _SetFullScreen(bool on) {\n\t\t_fullScreen ??= new(_w);\n\t\t_dontUpdateDesktopSize = true; //because the control resized 2 times: when changing window style and size; it makes PiP desktop resizing slow.\n\t\t_fullScreen.SetFullScreen(on);\n\t\t_dontUpdateDesktopSize = false;\n\t\t_UpdateDesktopSize();\n\t}\n\tbool _dontUpdateDesktopSize;\n\t\n\tbool IsConnected => _rdp?.Connected is 1;\n\t//bool IsConnectedOrConnecting => _rdp?.Connected is 1 or 2;\n\t\n\tprotected override void WndProc(ref Message m) {\n\t\tswitch (m.Msg) {\n\t\tcase Api.WM_SYSCOMMAND:\n\t\t\tswitch (m.WParam.ToInt32() & 0xFFF0) {\n\t\t\tcase Api.SC_MAXIMIZE:\n\t\t\t\t_rdp.FullScreen = true;\n\t\t\t\treturn;\n\t\t\tcase Api.SC_MOUSEMENU:\n\t\t\t\t_SystemIconMenu();\n\t\t\t\treturn;\n\t\t\t\t//case Api.SC_CLOSE: //rejected. Inconsistent with the full-screen Close button behavior (and the button cannot be intercepted or removed).\n\t\t\t\t//\tswitch (popupMenu.showSimple(\"1 Disconnect|2 Sign out\", owner: this)) {\n\t\t\t\t//\tcase 1: _rdp.Disconnect(); break;\n\t\t\t\t//\tcase 2: _ = PipIPC.SendAsync(\"logoff\"); break;\n\t\t\t\t//\t}\n\t\t\t\t//\treturn;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase Api.WM_ACTIVATE when IsConnected:\n\t\t\t_ = PipIPC.SendAsync(Math2.LoWord(m.WParam) != 0 ? \"WM_ACTIVATE 1\" : \"WM_ACTIVATE 0\", 1000);\n\t\t\tbreak;\n\t\t}\n\t\t\n\t\tbase.WndProc(ref m);\n\t}\n\t\n\tvoid _SystemIconMenu() {\n\t\tif (!IsConnected) return;\n#if true\n\t\tvar m = new popupMenu();\n\t\tm.Add(\"SETTINGS (will reconnect to change)\", disable: true);\n\t\tm.AddCheck(\"Clipboard sync enabled\", _sett.redirectClipboard, o => {\n\t\t\t_sett.redirectClipboard = o.IsChecked;\n\t\t\t_reconnecting = Environment.TickCount64;\n\t\t\t_rdp.Disconnect();\n\t\t\t//note: Reconnect() does not work.\n\t\t});\n#else\n\t\tif (!api.WTSGetChildSessionId(out int csid)) return;\n\t\tint rdpclip = process.allProcesses().FirstOrDefault(o => o.Name.Eqi(\"rdpclip.exe\") && o.SessionId == csid).Id;\n\t\tvar m = new popupMenu();\n\t\tm.AddCheck(\"Enable clipboard sync\", rdpclip != 0, o => {\n\t\t\tif (o.IsChecked) PipAgent.Send(\"clipboard\"); //run rdpclip.exe in PiP session\n\t\t\telse process.terminate(rdpclip);\n\t\t});\n#endif\n\t\tm.Separator();\n\t\tm[\"Help\"] = o => HelpUtil.AuHelp(\"editor/PiP session\");\n#if DEBUG\n\t\tm.Separator();\n\t\tm[\"Test 1\"] = o => {\n\t\t\tprint.it(PipIPC.SendSync(\"test\"));\n\t\t};\n\t\tm[\"Test 2\"] = async o => {\n\t\t\tif (await PipIPC.SendAsync(\"test\") is string r) {\n\t\t\t\tprint.it(r);\n\t\t\t}\n\t\t};\n#endif\n\t\tm.Show(owner: this);\n\t}\n\t\n\tpublic static void SettingsDialog() {\n\t\tvar b = new wpfBuilder(\"PiP settings\").WinSize(400);\n\t\tb.R.Add(out wpfc.CheckBox cSepClip, \"Clipboard sync enabled\").Checked(_sett.redirectClipboard);\n\t\tb.R.AddOkCancel();\n\t\tb.End();\n\t\tb.Loaded += () => { b.Window.Hwnd().ActivateL(); };\n\t\tif (!b.ShowDialog()) return;\n\t\t\n\t\t_sett.redirectClipboard = cSepClip.IsChecked.Value;\n\t}\n\t\n\trecord class _Settings : JSettings {\n\t\tpublic static readonly string File = AppSettings.DirBS + @\"PiP.json\";\n\t\t\n\t\tpublic static _Settings Load() => Load<_Settings>(File);\n\t\t\n\t\tpublic string wndPos;\n\t\tpublic bool redirectClipboard = true;\n\t}\n\tstatic _Settings _sett = _Settings.Load();\n}\n\nfile class FullScreenWindow {\n\twnd _w;\n\tRECT _r;\n\tWS _style;\n\t//WSE _exStyle; //never mind\n\tbool _isFS, _maximized;\n\t\n\tpublic FullScreenWindow(wnd w) {\n\t\t_w = w;\n\t}\n\t\n\tpublic void SetFullScreen(bool on) {\n\t\tif (on) {\n\t\t\tif (_isFS) return;\n\t\t\t_maximized = _w.IsMaximized;\n\t\t\t_w.ShowNotMinMax();\n\t\t\t_r = _w.Rect;\n\t\t\t_style = _w.Style;\n\t\t\t_w.SetStyle(_style & ~(WS.CAPTION | WS.THICKFRAME));\n\t\t\t_isFS = true; //before setting the final rect; then eg on wm_size IsFullScreen will return true\n\t\t\t_w.MoveL(screen.of(_w).Rect, SWPFlags.FRAMECHANGED); //info: it automatically makes taskbar non-topmost\n\t\t} else if (_isFS) {\n\t\t\t_isFS = false;\n\t\t\t_w.MoveL(_r, SWPFlags.FRAMECHANGED);\n\t\t\t_w.SetStyle(_style);\n\t\t\tif (_maximized) _w.ShowMaximized();\n\t\t}\n\t}\n\t\n\tpublic bool IsFullScreen => _isFS;\n}\n\n#pragma warning disable 649, 169 //field never assigned/used\nunsafe file class api : NativeApi {\n\t[DllImport(\"wtsapi32.dll\")]\n\tinternal static extern bool WTSIsChildSessionsEnabled(out bool pbEnabled);\n\t\n\t[DllImport(\"wtsapi32.dll\", SetLastError = true)]\n\tinternal static extern bool WTSEnableChildSessions(bool bEnable);\n\t\n\tinternal enum DWM_WINDOW_CORNER_PREFERENCE {\n\t\tDWMWCP_DEFAULT,\n\t\tDWMWCP_DONOTROUND,\n\t\tDWMWCP_ROUND,\n\t\tDWMWCP_ROUNDSMALL\n\t}\n\t\n\t[DllImport(\"dwmapi.dll\")]\n\tinternal static extern int DwmSetWindowAttribute(wnd hwnd, uint dwAttribute, void* pvAttribute, uint cbAttribute);\n\t\n\tinternal const int DWMWA_WINDOW_CORNER_PREFERENCE = 33;\n}\n"
  },
  {
    "path": "Au.Editor/xMisc/PipIPC.cs",
    "content": "using System.Security.AccessControl;\nusing System.Security.Principal;\nusing System.IO.Pipes;\nusing System.Text.Json.Nodes;\nusing Microsoft.Win32;\nusing System.Text.Json;\n\nnamespace LA;\n\n/// <summary>\n/// Used by all PiP processes.\n/// </summary>\n/// <remarks>\n/// When PiP is active, 3 Au.Editor.exe processes are running:\n/// <br/>• 1. Normal process in main session. Calls <see cref=\"StartPip\"/> or <see cref=\"RunScriptInPip\"/>. They use the <b>SendX</b> functions to communicate with process 3.\n/// <br/>• 2. Process in main session that shows PiP window and starts child session. Can use the <b>SendX</b> functions to communicate with process 3.\n/// \t\tNormally started by process 1 (the above functions). Or can be started manually, using command line <c>/pip</c>. Can run without process 1 or/and 3.\n/// <br/>• 3. Normal process in child session. Calls <see cref=\"StartPipeServerThread\"/>, which starts scripts in child session and performs other tasks requested by processes 1 and 2.\n/// \t\tNormally started via the registry key (process 2 sets RunOnce if need). Or can be [re] started manually. If not running, processes 1 and 2 can't start scripts etc in child session.\n/// </remarks>\nclass PipIPC {\n\tconst string c_pipeName = \"Au.PiP-pipe\";\n\t\n\t/// <summary>\n\t/// Sends a message from the main session to the LA pipe server running in the PiP session.\n\t/// </summary>\n\t/// <param name=\"message\">A single-line message. Can be like <c>\"message\"</c> or <c>\"message parameter\"</c>.</param>\n\t/// <param name=\"timeoutMS\">Timeout for <see cref=\"NamedPipeClientStream.Connect\"/>.</param>\n\t/// <returns>Response of the pipe server. Single line. Returns null if failed (eg connection timeout).</returns>\n\tpublic static string SendSync(string message, int timeoutMS = 60_000) {\n\t\ttry {\n\t\t\tusing var client = new NamedPipeClientStream(\".\", c_pipeName, PipeDirection.InOut);\n\t\t\tclient.Connect(timeoutMS);\n\t\t\t\n\t\t\tvar writer = new StreamWriter(client) { AutoFlush = true };\n\t\t\tvar reader = new StreamReader(client);\n\t\t\t\n\t\t\twriter.WriteLine(message);\n\t\t\tvar r = reader.ReadLine();\n\t\t\treturn r;\n\t\t}\n\t\tcatch (Exception ex) { Debug_.Print($\"{message}.  {ex}\"); return null; }\n\t}\n\t\n\t/// <inheritdoc cref=\"SendSync\"/>\n\tpublic static async Task<string> SendAsync(string message, int timeoutMS = 60_000) {\n\t\treturn await Task.Run(() => SendSync(message, timeoutMS));\n\t}\n\t\n\tpublic static void StartPipeServerThread() {\n\t\trun.thread(_PipeServer, sta: false);\n\t}\n\t\n\tstatic void _PipeServer() {\n\t\ttry {\n\t\t\tvar security = new PipeSecurity();\n\t\t\tsecurity.AddAccessRule(new PipeAccessRule(new SecurityIdentifier(WellKnownSidType.AuthenticatedUserSid, null), PipeAccessRights.FullControl, AccessControlType.Allow));\n\t\t\tusing var server = NamedPipeServerStreamAcl.Create(c_pipeName, PipeDirection.InOut, 1, PipeTransmissionMode.Message, 0, 0, 0, security);\n\t\t\t\n\t\t\tfor (; ; ) {\n\t\t\t\tserver.WaitForConnection();\n\t\t\t\t\n\t\t\t\tvar reader = new StreamReader(server);\n\t\t\t\tvar writer = new StreamWriter(server) { AutoFlush = true };\n\t\t\t\t\n\t\t\t\tstring message = reader.ReadLine(), param = null, ret = \"\\a\";\n\t\t\t\tint i = message.IndexOf(' ');\n\t\t\t\tif (i > 0) (param, message) = (message[(i + 1)..], message[..i]);\n\t\t\t\t//print.it(message, param);\n\t\t\t\t\n\t\t\t\tswitch (message) {\n\t\t\t\tcase \"runScript\":\n\t\t\t\t\t//ret = _RunScript(param);\n\t\t\t\t\tTask.Run(() => _RunScript(param));\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"logoff\":\n\t\t\t\t\tTask.Run(() => computer.logoff());\n\t\t\t\t\tbreak;\n\t\t\t\t//case \"clipboard\":\n\t\t\t\t//\tTask.Run(() => Process.Start(\"rdpclip.exe\"));\n\t\t\t\t//\tbreak;\n\t\t\t\t//case \"syncFiles\": //no\n\t\t\t\t//\tif (App.Dispatcher is { } disp) {\n\t\t\t\t//\t\tdisp.InvokeAsync(() => {\n\t\t\t\t//\t\t\tPanels.Editor.OnAppActivated_();\n\t\t\t\t//\t\t\tApp.Model?.SyncWithFilesystem_();\n\t\t\t\t//\t\t});\n\t\t\t\t//\t}\n\t\t\t\t//\tbreak;\n\t\t\t\tcase \"WM_ACTIVATE\":\n\t\t\t\t\tif (App.Hmain.IsActive) App.Dispatcher.InvokeAsync(() => MainWindow.OnActivatedDeactivatedAppOrPip_(param == \"1\"));\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tret = \"Bad: \" + message;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\twriter.WriteLine(ret);\n\t\t\t\tserver.Disconnect();\n\t\t\t}\n\t\t}\n\t\tcatch (Exception ex) { print.it(ex); }\n\t}\n\t\n\tstatic void _RunScript(string param) {\n\t\ttry {\n\t\t\tbool laActive = App.Hmain.IsActive;\n\t\t\tif (laActive) App.Dispatcher.Invoke(Panels.Editor.OnAppActivated_);\n\t\t\t\n\t\t\tvar j = JsonNode.Parse(param);\n\t\t\tstring file = (string)j[\"file\"];\n\t\t\tstring[] args = j[\"args\"] is JsonArray ja ? ja.Select(o => (string)o).ToArray() : [];\n\t\t\t\n\t\t\tif (!laActive) if (!wait.until(-60, () => miscInfo.isInputDesktop(true))) throw new InputDesktopException(\"Now cannot run scripts in PiP session.\"); //1-2 s when connecting to existing session\n\t\t\t\n\t\t\t//script.run(file, args); //works, but I like more low-level code here\n\t\t\tApp.Dispatcher.InvokeAsync(() => {\n\t\t\t\tif (App.Model?.FindCodeFile(file) is { } f) {\n\t\t\t\t\tCompileRun.CompileAndRun(true, f, args);\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t\tcatch (Exception ex) { print.it(ex); }\n\t}\n\t\n\t///// <summary>\n\t///// Returns true if child sessions supported (Win8.1+, not Home edition, not in child session).\n\t///// </summary>\n\t//public static bool CanUsePip => s_canUsePip ??= osVersion.minWin8_1 && !miscInfo.isChildSession && !(Registry.GetValue(@\"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\", \"ProductName\", null) is string sPN && sPN.Contains(\" Home\"));\n\t//static bool? s_canUsePip;\n\t\n\tstatic bool _CanUsePip() {\n\t\tif (!osVersion.minWin8_1) return _Error(\"Requires Windows 8.1 or later.\"); //tested: on 8.0 error \"class not registered\"\n\t\tif (miscInfo.isChildSession) return _Error(\"Can't start another PiP session from PiP session.\");\n\t\tif (Registry.GetValue(@\"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\", \"ProductName\", null) is string sPN && sPN.Contains(\" Home\"))\n\t\t\treturn _Error($\"PiP does not work on Windows Home editions. Your OS is {sPN}.\");\n\t\treturn true;\n\t\t//the pip exe will auto-enable child sessions\n\t\t\n\t\tstatic bool _Error(string s) {\n\t\t\tdialog.showError(\"PiP error\", s, owner: App.Hmain);\n\t\t\treturn false;\n\t\t}\n\t}\n\t\n\tpublic static void StartPip() {\n\t\tApp.Model.Save.AllNowIfNeed();\n\t\tif (!_CanUsePip()) return;\n\t\t_PipProcessStarter(\"/pip\").Start();\n\t}\n\t\n\tstatic ProcessStarter_ _PipProcessStarter(string args) => new(process.thisExePath, args, rawExe: true);\n\t\n\tpublic static async void RunScriptInPip(FileNode f, params string[] args) {\n\t\tApp.Model.Save.AllNowIfNeed();\n\t\tif (!_CanUsePip()) return;\n\t\tif (!CompileRun.Compile(true, ref f, out _)) return;\n\t\tstring itemPath = f.ItemPath;\n\t\t\n\t\tstring _Run() {\n\t\t\tlock (\"9j8+It9PbUaiHCFoo9QaSA\") {\n\t\t\t\tstatic bool _EnsurePipConnected() {\n\t\t\t\t\twnd wMsg = _FindWndMsg();\n\t\t\t\t\tif (wMsg.Is0) {\n\t\t\t\t\t\tvar v = _PipProcessStarter(\"/pip /noactivate\").Start(ProcessStarter_.Result.Need.WaitHandle);\n\t\t\t\t\t\twait.until(0, () => !(wMsg = _FindWndMsg()).Is0 || v.waitHandle.WaitOne(0)); //wait until the pip message-only window created or the pip process ended\n\t\t\t\t\t\tv.waitHandle.Dispose();\n\t\t\t\t\t\tif (wMsg.Is0) return false;\n\t\t\t\t\t}\n\t\t\t\t\treturn wait.until(0, () => wMsg.GetWindowLong(GWL.DWL.USER) == 1 || !wMsg.IsAlive) && wMsg.IsAlive; //wait until connected\n\t\t\t\t\t\n\t\t\t\t\tstatic wnd _FindWndMsg() => wnd.findFast(\"Au.PiP-msg\", \"#32770\", messageOnly: true); //created in pip process at startup, just after entering the single-instance mutex\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tfor (int i = 2; --i >= 0;) { //retry if the pip process is closing etc\n\t\t\t\t\tif (_EnsurePipConnected()) break;\n\t\t\t\t\tif (i == 0) return \"Failed to start PiP session.\";\n\t\t\t\t\tDebug_.Print(\"retrying PiP-connect\");\n\t\t\t\t\t2.s();\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\treturn SendSync(\"runScript \" + JsonSerializer.Serialize(new { file = itemPath, args = args }));\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (await Task.Run(_Run) is string r) {\n\t\t\tif (!r.Starts(\"\\a\")) print.warning($\"Failed to run script {f.SciLink()} in PiP session. {r}\", -1);\n\t\t} else {\n\t\t\tprint.warning($\"Failed to run script {f.SciLink()} in PiP session. Make sure LibreAutomate is running in PiP session too.\", -1);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/xMisc/RegHotkeys.cs",
    "content": "namespace LA;\n\nstatic class RegHotkeys {\n\tstatic RegisteredHotkey[] _a = new RegisteredHotkey[9];\n\t\n\tpublic enum Id {\n\t\tQuickCaptureMenu, QuickCaptureDwnd, QuickCaptureDelm, QuickCaptureDuiimage,\n\t\tDebugNext, DebugStep, DebugStepOut, DebugContinue, DebugPause,\n\t}\n\tconst Id _DebugFirst = Id.DebugNext, _DebugLast = Id.DebugPause;\n\t\n\tpublic static void RegisterPermanent() {\n#if !IDE_LA\n\t\t(string keys, string menu)[] g = [\n\t\t\t(App.Settings.hotkeys.tool_quick, nameof(Menus.Code.Quick_capturing_info)),\n\t\t\t(App.Settings.hotkeys.tool_wnd, nameof(Menus.Code.wnd)),\n\t\t\t(App.Settings.hotkeys.tool_elm, nameof(Menus.Code.elm)),\n\t\t\t(App.Settings.hotkeys.tool_uiimage, nameof(Menus.Code.uiimage)),\n\t\t];\n\t\tfor (int i = 0; i < (int)_DebugFirst; i++) {\n\t\t\tstring keys = g[i].keys;\n\t\t\tif (!keys.NE()) {\n\t\t\t\ttry {\n\t\t\t\t\tif (!_a[i].Register(i, keys, App.Hmain, noRepeat: true)) {\n\t\t\t\t\t\tprint.warning($\"Failed to register hotkey {keys}. Look in <+options Hotkeys>Options > Hotkeys<>.\", -1);\n\t\t\t\t\t\tkeys = null;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (Exception ex) { print.it(ex); keys = null; }\n\t\t\t}\n\t\t\tif (i > 0) App.Commands[g[i].menu].MenuItem.InputGestureText = keys;\n\t\t}\n#endif\n\t}\n\t\n\tpublic static void UnregisterPermanent() {\n\t\tfor (int i = 0; i < (int)_DebugFirst; i++) _a[i].Unregister();\n\t}\n\t\n\tpublic static void RegisterDebug() {\n\t\tfor (int i = (int)_DebugFirst; i <= (int)_DebugLast; i++) {\n\t\t\tstring keys = (Id)i switch { Id.DebugNext => \"F10\", Id.DebugStep => \"F11\", Id.DebugStepOut => \"Shift+F11\", Id.DebugContinue => \"F5\", Id.DebugPause => \"F6\", _ => null };\n\t\t\tif (!_a[i].Register(i, keys, App.Hmain)) print.warning($\"Failed to register hotkey {keys}.\", -1);\n\t\t}\n\t}\n\t\n\tpublic static void UnregisterDebug() {\n\t\tfor (int i = (int)_DebugFirst; i <= (int)_DebugLast; i++) _a[i].Unregister();\n\t}\n\t\n\tinternal static void WmHotkey_(nint wParam) {\n\t\tvar id = (Id)(int)wParam;\n\t\tswitch (id) {\n\t\tcase Id.QuickCaptureMenu: QuickCapture.Menu(); break;\n\t\tcase Id.QuickCaptureDwnd: QuickCapture.AoolDwnd(); break;\n\t\tcase Id.QuickCaptureDelm: QuickCapture.ToolDelm(); break;\n\t\tcase Id.QuickCaptureDuiimage: QuickCapture.ToolDuiimage(); break;\n\t\tcase >= _DebugFirst and <= _DebugLast: Panels.Debug.WmHotkey_(id); break;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/xMisc/UacDragDrop.cs",
    "content": "using System.Runtime.InteropServices.ComTypes;\nusing System.Windows.Interop;\n\nnamespace LA;\n\nclass UacDragDrop {\n\tpublic class AdminProcess {\n\t\tWinEventHook _hook; //SYSTEM_CAPTURESTART\n\t\ttimer _timer; //tracks mouse etc\n\t\tbool _isDragMode; //is in drag-drop\n\t\tbool _isProcess2; //is our non-admin process started\n\t\tint _endCounter; //delays ending drag mode\n\t\t\n\t\tstatic AdminProcess s_inst;\n\t\t\n\t\t//Called with on=true when main window becomes visible.\n\t\t//Called with on=false when hidden or closed.\n\t\tpublic static void Enable(bool on) {\n\t\t\tif (on == (s_inst != null)) return;\n\t\t\tif (on) {\n\t\t\t\tif (uacInfo.ofThisProcess.Elevation != UacElevation.Full) return;\n\t\t\t\ts_inst = new AdminProcess();\n\t\t\t} else {\n\t\t\t\ts_inst._Dispose();\n\t\t\t\ts_inst = null;\n\t\t\t}\n\t\t}\n\t\t\n\t\tAdminProcess() {\n\t\t\t_timer = new timer(_Timer);\n\t\t\t\n\t\t\t//use hook to detect when drag-drop started\n\t\t\t_hook = new WinEventHook(EEvent.SYSTEM_CAPTURESTART, 0, d => {\n\t\t\t\t//print.it(\"SYSTEM_CAPTURESTART\", d.w, d.w.IsVisible, d.w.IsMessageOnly);\n\t\t\t\tvar isClass = d.w.ClassNameIs(\"CLIPBRDWNDCLASS\", \"DragWindow\", \"LiftedDMITCursorRecalculateClass\");\n\t\t\t\tif (isClass == 3) return; //probably related to the Windows feature \"Drag Tray\". It we just end drag mode, drag-drop does not work first time after Windows startup if the feature is not turned off.\n\t\t\t\t_EndedDragMode();\n\t\t\t\tif (isClass == 0 && d.w.IsVisible) return; //all known inter-process drag-drop capturing windows are invisible; some also message-only.\n\t\t\t\t_StartedDragMode();\n\t\t\t}, flags: EHookFlags.SKIPOWNPROCESS);\n\t\t\t\n\t\t\t//note: we don't use SYSTEM_CAPTUREEND. It's too early and sometimes missing.\n\t\t\t//tested: no EVENT_SYSTEM_DRAGDROPSTART events.\n\t\t\t//info: classname \"DragWindow\" is of Windows Store.\n\t\t\t//rejected: int pid = d.w.ProcessId; using(var u = uacInfo.ofProcess(pid)) { if(u == null || u.Elevation == UacElevation.Full) return; }\n\t\t}\n\t\t\n\t\tvoid _Dispose() {\n\t\t\t_hook.Dispose();\n\t\t\t_EndedDragMode();\n\t\t}\n\t\t\n\t\tvoid _StartedDragMode() {\n\t\t\t_isDragMode = true;\n\t\t\t_endCounter = 0;\n\t\t\t_wTransparent = default;\n\t\t\t_wWindow = default;\n\t\t\t_wTargetControl = default;\n\t\t\t_data = null;\n\t\t\t_timer.Every(30);\n\t\t}\n\t\t\n\t\tvoid _EndedDragMode() {\n\t\t\tif (!_isDragMode) return;\n\t\t\t_isDragMode = _isProcess2 = false;\n\t\t\t_timer.Stop();\n\t\t\tif (!_wTransparent.Is0) _wTransparent.Post(Api.WM_USER); //let it close self\n\t\t}\n\t\t\n\t\t//Every 30 ms while in drag mode.\n\t\tvoid _Timer(timer t) {\n\t\t\tif (!_isDragMode) return;\n\t\t\t\n\t\t\t//when mouse released, end drag mode with ~100 ms delay\n\t\t\tif (!mouse.isPressed(MButtons.Left | MButtons.Right)) { //calls GetKeyStateAsync. GetKeyState unreliable when in drag mode.\n\t\t\t\tif (++_endCounter == 4) _EndedDragMode();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t_endCounter = 0;\n\t\t\t//end drag mode if _wTransparent died, eg did not find useful data on drag enter\n\t\t\tif (!_wTransparent.Is0 && !_wTransparent.IsAlive) {\n\t\t\t\t_EndedDragMode();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\tvar w = wnd.fromMouse(WXYFlags.NeedWindow);\n\t\t\tif (!_isProcess2) {\n\t\t\t\tif (!w.IsOfThisProcess) return;\n\t\t\t\t_isProcess2 = true;\n\t\t\t\t_wWindow = w;\n\t\t\t\tnew ProcessStarter_(\"Au.Editor.exe\", \"/dd \" + CommandLine.MsgWnd.Handle.ToString()).StartUserIL();\n\t\t\t} else if (!_wTransparent.Is0 && w != _wTransparent) {\n\t\t\t\t_wWindow = w;\n\t\t\t\t_SetTransparentSizeZorder();\n\t\t\t}\n\t\t}\n\t\t\n\t\twnd _wTransparent; //our transparent non-admin window\n\t\twnd _wWindow; //current our top-level admin window covered by _wTransparent\n\t\t\n\t\t//Called in admin process. Non-admin process may not be able to zorder its window above admin windows.\n\t\tvoid _SetTransparentSizeZorder() {\n\t\t\tif (_wWindow.IsOfThisProcess) {\n\t\t\t\tvar r = _wWindow.Rect;\n\t\t\t\t_wTransparent.MoveL(r.left, r.top, r.Width, r.Height);\n\t\t\t\tbool ok = _wTransparent.ZorderL_(_wWindow, before: true);\n\t\t\t\tDebug_.PrintIf(!ok, $\"ZorderL_ failed. {lastError.message}. _wWindow={_wWindow}\");\n\t\t\t} else {\n\t\t\t\t_wTransparent.MoveL(0, 0, 0, 0);\n\t\t\t}\n\t\t}\n\t\t\n\t\t//SendMessage from _wTransparent on WM_CREATE.\n\t\tpublic static void OnTransparentWindowCreated(wnd wTransparent) {\n\t\t\tvar x = s_inst;\n\t\t\tif (x?._isDragMode ?? false) {\n\t\t\t\tx._wTransparent = wTransparent;\n\t\t\t\tx._SetTransparentSizeZorder();\n\t\t\t} else wTransparent.Post(Api.WM_USER); //let it close self\n\t\t}\n\t\t\n\t\tpublic static int DragEvent(int event_, byte[] b) => s_inst?._DragEvent(event_, b) ?? 0;\n\t\t\n\t\tint _DragEvent(int event_, byte[] b) {\n\t\t\tif (!_isDragMode) return 0;\n\t\t\tDDEvent ev = (DDEvent)event_;\n\t\t\tif (ev == DDEvent.Leave) {\n\t\t\t\tif (!_wTargetControl.Is0) {\n\t\t\t\t\t_InvokeDT(_wTargetControl, ev, 0, 0, default);\n\t\t\t\t\t_wTargetControl = default;\n\t\t\t\t}\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tvar a = Serializer_.Deserialize(b);\n\t\t\tint effect = a[0], keyState = a[1]; POINT pt = new(a[2], a[3]);\n\t\t\tif (ev == DDEvent.Enter) {\n\t\t\t\t_data = new System.Windows.DataObject();\n\t\t\t\tvar t = new DDData { files = a[4], shell = a[5], text = a[6], linkName = a[7] };\n\t\t\t\tif (t.files != null) _data.SetData(\"FileDrop\", t.files);\n\t\t\t\tif (t.shell != null) _SetDataBytes(\"Shell IDList Array\", t.shell);\n\t\t\t\tif (t.text != null) _data.SetData(\"UnicodeText\", t.text);\n\t\t\t\tif (t.linkName != null) _SetDataBytes(\"FileGroupDescriptorW\", t.linkName);\n\t\t\t\t\n\t\t\t\t//workaround for: SetData writes byte[] in wrong format, probably serialized\n\t\t\t\tvoid _SetDataBytes(string name, byte[] a) => _data.SetData(name, new MemoryStream(a), false);\n\t\t\t}\n\t\t\t\n\t\t\tint ef = 0;\n\t\t\tvar w = _wWindow.ChildFromXY(pt, WXYCFlags.ScreenXY);\n\t\t\tif (w.Is0) w = _wWindow;\n\t\t\tif (w != _wTargetControl && !_wTargetControl.Is0) {\n\t\t\t\t_InvokeDT(_wTargetControl, DDEvent.Leave, 0, 0, default);\n\t\t\t\t_wTargetControl = default;\n\t\t\t}\n\t\t\tif (!w.Is0 && w.IsOfThisProcess && w.IsEnabled(true)) {\n\t\t\t\tif (ev != 0 && _wTargetControl.Is0) {\n\t\t\t\t\tif (ev == DDEvent.Over) ev = 0;\n\t\t\t\t\telse _InvokeDT(w, DDEvent.Enter, effect, keyState, pt);\n\t\t\t\t}\n\t\t\t\tef = _InvokeDT(_wTargetControl = w, ev, effect, keyState, pt);\n\t\t\t}\n\t\t\t\n\t\t\tif (ev == DDEvent.Drop) { _wTargetControl = default; }\n\t\t\t\n\t\t\tint _InvokeDT(wnd w, DDEvent ev, int effect, int keyState, POINT pt) {\n\t\t\t\tif (w.IsOfThisThread) return _InvokeDropTarget(w, ev, effect, keyState, pt);\n\t\t\t\tvar d = HwndSource.FromHwnd(w.Window.Handle)?.Dispatcher;\n\t\t\t\treturn d?.Invoke(() => _InvokeDropTarget(w, ev, effect, keyState, pt)) ?? 0;\n\t\t\t}\n\t\t\t\n\t\t\treturn ef;\n\t\t}\n\t\t\n\t\t//data etc sent by _wTransparent on OnDragEnter. Then used here on enter/over/drop.\n\t\tSystem.Windows.DataObject _data;\n\t\twnd _wTargetControl; //control or window from mouse\n\t\t\n\t\tunsafe int _InvokeDropTarget(wnd w, DDEvent ev, int effect, int keyState, POINT pt) {\n\t\t\tnint dt = w.Prop[\"OleDropTargetInterface\"];\n\t\t\tif (dt == 0 && w != _wWindow) { //if w is of a HwndHost that does not register drop target, use that of the main window\n\t\t\t\tw = _wWindow;\n\t\t\t\tdt = w.Prop[\"OleDropTargetInterface\"];\n\t\t\t}\n\t\t\tif (dt == 0) return 0;\n\t\t\t\n#pragma warning disable CS0168 // Variable is declared but never used\n\t\t\ttry {\n\t\t\t\tnint* vtbl = *(nint**)dt + 3;\n\t\t\t\tint hr = ev switch {\n\t\t\t\t\tDDEvent.Enter or DDEvent.Drop => ((delegate* unmanaged<nint, IDataObject, int, POINT, ref int, int>)vtbl[ev == DDEvent.Drop ? 3 : 0])(dt, _data, keyState, pt, ref effect),\n\t\t\t\t\tDDEvent.Over => ((delegate* unmanaged<nint, int, POINT, ref int, int>)vtbl[1])(dt, keyState, pt, ref effect),\n\t\t\t\t\t_ => ((delegate* unmanaged<nint, int>)vtbl[2])(dt)\n\t\t\t\t};\n\t\t\t\tif (hr != 0) effect = 0;\n\t\t\t}\n\t\t\tcatch (Exception ex) { //once: exception \"access violation\". Can't repro. To call IDropTarget methods easily, then was used a C++ helper function, not C# delegate*.\n#if DEBUG\n\t\t\t\tprint.it(\"_InvokeDropTarget\", dt, _data, w);\n\t\t\t\tprint.it(ex);\n#endif\n\t\t\t\treturn 0;\n\t\t\t}\n#pragma warning restore CS0168 // Variable is declared but never used\n\t\t\treturn effect;\n\t\t}\n\t}\n\t\n\t//Drag-drop events.\n\tpublic enum DDEvent { Enter, Over, Drop, Leave } //don't change, used in C++\n\t\n\t//A window in non-admin process that accepts drag-drop events and relays to the admin process.\n\t//Covers our admin window. Almost transparent.\n\tpublic static class NonAdminProcess {\n\t\tstatic wnd _w; //our transparent window\n\t\tstatic wnd _msgWnd; //message-only IPC window in admin process\n\t\tstatic _DropTarget _dt; //GC\n\t\tstatic bool _enteredOnce;\n\t\t\n\t\t//Called at startup in non-admin process when command line starts with /dd.\n\t\tpublic static void MainDD(ReadOnlySpan<string> args) {\n\t\t\tprocess.thisProcessCultureIsInvariant = true;\n\t\t\t\n\t\t\t_msgWnd = (wnd)args[0].ToInt();\n\t\t\t\n\t\t\tWndUtil.RegisterWindowClass(\"Au.Editor.DD\", _WndProc);\n\t\t\t_w = WndUtil.CreateWindow(\"Au.Editor.DD\", null, WS.POPUP | WS.DISABLED, WSE.LAYERED | WSE.NOACTIVATE | WSE.TOOLWINDOW | WSE.TOPMOST);\n\t\t\tApi.SetLayeredWindowAttributes(_w, 0, 1, 2);\n\t\t\t\n\t\t\tThread.CurrentThread.TrySetApartmentState(ApartmentState.Unknown); //uninit MTA\n\t\t\tApi.OleInitialize(default); //somehow RDD fails if process.ThisThreadSetComApartment_(ApartmentState.STA);\n\t\t\tApi.RegisterDragDrop(_w, _dt = new _DropTarget());\n\t\t\t_msgWnd.Send(Api.WM_USER, 10, (nint)_w);\n\t\t\tApi.SetTimer(_w, 1, 1000, null);\n\t\t\t\n\t\t\t_w.ShowL(true);\n\t\t\twhile (Api.GetMessage(out var m)) Api.DispatchMessage(m);\n\t\t\tApi.OleUninitialize();\n\t\t}\n\t\t\n\t\tstatic nint _WndProc(wnd w, int msg, nint wParam, nint lParam) {\n\t\t\tswitch (msg) {\n\t\t\tcase Api.WM_DESTROY:\n\t\t\t\tApi.RevokeDragDrop(w);\n\t\t\t\tApi.PostQuitMessage(0);\n\t\t\t\tbreak;\n\t\t\tcase Api.WM_TIMER:\n\t\t\t\tif (!_msgWnd.IsAlive) _Exit();\n\t\t\t\tbreak;\n\t\t\tcase Api.WM_USER: //admin posts it when ended drag mode\n\t\t\t\t_Exit();\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t\n\t\t\treturn Api.DefWindowProc(w, msg, wParam, lParam);\n\t\t}\n\t\t\n\t\tstatic void _Exit() { Api.DestroyWindow(_w); }\n\t\t\n\t\tclass _DropTarget : Api.IDropTarget {\n\t\t\tvoid Api.IDropTarget.DragEnter(IDataObject d, int grfKeyState, POINT pt, ref int effect) {\n\t\t\t\tif (_enteredOnce) {\n\t\t\t\t\t(this as Api.IDropTarget).DragOver(grfKeyState, pt, ref effect);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tDDData r = default;\n\t\t\t\tif (_enteredOnce = r.GetData(d)) {\n\t\t\t\t\tvar b = Serializer_.Serialize(effect, grfKeyState, pt.x, pt.y, r.files, r.shell, r.text, r.linkName);\n\t\t\t\t\teffect = (int)WndCopyData.Send<byte>(_msgWnd, 110, b, (int)DDEvent.Enter);\n\t\t\t\t} else {\n\t\t\t\t\t_Exit();\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tvoid Api.IDropTarget.DragOver(int grfKeyState, POINT pt, ref int effect) {\n\t\t\t\tif (!_enteredOnce) { effect = 0; return; }\n\t\t\t\tvar b = Serializer_.Serialize(effect, grfKeyState, pt.x, pt.y);\n\t\t\t\teffect = (int)WndCopyData.Send<byte>(_msgWnd, 110, b, (int)DDEvent.Over);\n\t\t\t}\n\t\t\t\n\t\t\tvoid Api.IDropTarget.Drop(IDataObject d, int grfKeyState, POINT pt, ref int effect) {\n\t\t\t\tif (!_enteredOnce) { effect = 0; return; }\n\t\t\t\t_Exit();\n\t\t\t\tvar b = Serializer_.Serialize(effect, grfKeyState, pt.x, pt.y);\n\t\t\t\teffect = (int)WndCopyData.Send<byte>(_msgWnd, 110, b, (int)DDEvent.Drop);\n\t\t\t}\n\t\t\t\n\t\t\tvoid Api.IDropTarget.DragLeave() {\n\t\t\t\tif (_enteredOnce) WndCopyData.Send<byte>(_msgWnd, 110, Serializer_.Serialize(), (int)DDEvent.Leave);\n\t\t\t}\n\t\t}\n\t}\n\t\n}\n\nstruct DDData {\n\tpublic string[] files;\n\tpublic byte[] shell, linkName;\n\tpublic string text;\n\tpublic bool scripts;\n\t\n\tpublic unsafe bool GetData(IDataObject d, bool getFileNodes = false) {\n\t\ttry {\n\t\t\tFORMATETC fHdrop = default, fShell = default, fText = default, fDesc = default;\n\t\t\tvar afe = new FORMATETC[1]; var fe = new int[1];\n\t\t\tvar e = d.EnumFormatEtc(DATADIR.DATADIR_GET);\n\t\t\twhile (e.Next(1, afe, fe) == 0 && fe[0] == 1) {\n\t\t\t\t//print.it(ClipFormats.GetName((ushort)afe[0].cfFormat));\n\t\t\t\tif (afe[0].tymed != TYMED.TYMED_HGLOBAL) continue;\n\t\t\t\tint cf = (ushort)afe[0].cfFormat;\n\t\t\t\tif (cf == Api.CF_HDROP) fHdrop = afe[0];\n\t\t\t\telse if (cf == ClipFormats.ShellIDListArray_) fShell = afe[0];\n\t\t\t\telse if (cf == Api.CF_UNICODETEXT) fText = afe[0];\n\t\t\t\telse if (cf == ClipFormats.FileGroupDescriptorW_) fDesc = afe[0];\n\t\t\t\telse if (getFileNodes && cf >= 0xC000 && ClipFormats.GetName(cf) == \"LA.FileNode[]\") return scripts = true;\n\t\t\t}\n\t\t\tif (fHdrop.cfFormat != 0) files = _GetFiles(ref fHdrop);\n\t\t\telse if (fShell.cfFormat != 0) shell = _GetBytes(ref fShell);\n\t\t\telse if (fText.cfFormat != 0) {\n\t\t\t\ttext = _GetText(ref fText);\n\t\t\t\tif (fDesc.cfFormat != 0) linkName = _GetBytes(ref fDesc); //text is URL\n\t\t\t} else return false;\n\t\t\treturn true;\n\t\t\t\n\t\t\tbyte[] _GetBytes(ref FORMATETC fe) {\n\t\t\t\td.GetData(ref fe, out var m);\n\t\t\t\tint n = (int)Api.GlobalSize(m.unionmember);\n\t\t\t\tvar t = Api.GlobalLock(m.unionmember);\n\t\t\t\ttry { return new Span<byte>((void*)t, n).ToArray(); }\n\t\t\t\tfinally { Api.GlobalUnlock(m.unionmember); Api.ReleaseStgMedium(ref m); }\n\t\t\t}\n\t\t\t\n\t\t\tstring _GetText(ref FORMATETC fe) {\n\t\t\t\td.GetData(ref fe, out var m);\n\t\t\t\tvar t = (char*)Api.GlobalLock(m.unionmember);\n\t\t\t\tint n = (int)Api.GlobalSize(m.unionmember) / 2; if (n > 0 && t[--n] != 0) n++;\n\t\t\t\ttry { return new string(t, 0, n); }\n\t\t\t\tfinally { Api.GlobalUnlock(m.unionmember); Api.ReleaseStgMedium(ref m); }\n\t\t\t}\n\t\t\t\n\t\t\tstring[] _GetFiles(ref FORMATETC fe) {\n\t\t\t\td.GetData(ref fe, out var m);\n\t\t\t\ttry { return clipboardData.HdropToFiles_(m.unionmember); }\n\t\t\t\tfinally { Api.ReleaseStgMedium(ref m); }\n\t\t\t}\n\t\t}\n\t\tcatch (Exception ex) { Debug_.Print(ex); } //info: if from IE, IDataObject.GetData fails for all formats.\n\t\treturn false;\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/xUtil/Downloader.cs",
    "content": "using System.Windows.Controls;\n\nnamespace LA;\n\n/// <summary>\n/// Downloads and extracts a 7z or zip file containing multiple files into a directory. Deletes all old files. Shows progress.\n/// Can be used in UI thread.\n/// Must be disposed (it deletes a sentinel file if succeeded).\n/// To extract uses the LA's installed 7za.exe.\n/// </summary>\nsealed class Downloader : IDisposable {\n\tstring _dirExtract;\n\tbool _deleteOldFiles;\n\tstring _sentinel;\n\t\n\t/// <summary>\n\t/// Prepares to extract files in given directory.\n\t/// </summary>\n\t/// <param name=\"dirExtract\">Path of directory where to extract files (later). This func creates new empty directory if does not exist; else just detects whether can create files there.</param>\n\t/// <param name=\"failedWarningPrefix\">Prefix text for <see cref=\"print.warning\"/> to use when fails.</param>\n\t/// <returns><c>false</c> if can't install in this directory (prints warning).</returns>\n\tpublic bool PrepareDirectory(string dirExtract, string failedWarningPrefix = \"<>Warning: \") {\n\t\t_dirExtract = null;\n\t\t_deleteOldFiles = false;\n\t\ttry {\n\t\t\tif (filesystem.exists(dirExtract, true).Directory) {\n\t\t\t\tusing (File.Create(dirExtract + @\"\\~\", 0, FileOptions.DeleteOnClose)) { } //can create files there?\n\t\t\t\t_deleteOldFiles = true;\n\t\t\t} else {\n\t\t\t\tfilesystem.createDirectory(dirExtract);\n\t\t\t}\n\t\t\t_dirExtract = dirExtract;\n\t\t}\n\t\tcatch (Exception e1) {\n\t\t\tprint.warning(e1, failedWarningPrefix);\n\t\t\tif (!uacInfo.isAdmin) print.it(\"\\tRestart this program as administrator.\");\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\t\n\t/// <summary>\n\t/// Downloads compressed file from given URL and extracts to the directory specified in a call to <see cref=\"PrepareDirectory\"/>. Deletes all old files.\n\t/// </summary>\n\t/// <param name=\"url\">Direct download URL. Must end with <c>\".zip\"</c> or <c>\".7z\"</c>.</param>\n\t/// <param name=\"progress\">This func will set text like <c>\"Downloading, 20%\"</c>. Can be null.</param>\n\t/// <param name=\"ct\"></param>\n\t/// <param name=\"progressText\">Progress text. While downloading, this function appends \", N%\".</param>\n\t/// <param name=\"failedWarningPrefix\">Prefix text for <see cref=\"print.warning\"/> to use when fails.</param>\n\t/// <returns><c>false</c> if failed (prints warning); <c>null</c> if canceled (no warning).</returns>\n\tpublic async Task<bool?> DownloadAndExtract(string url, TextBlock progress, CancellationToken ct, string progressText = \"Downloading\", string failedWarningPrefix = \"<>Warning: \") {\n\t\tif (_dirExtract is null) throw new InvalidOperationException();\n\t\tvar ext = pathname.getExtension(url).Lower();\n\t\tif (!(ext is \".zip\" or \".7z\")) throw new NotSupportedException(\"Bad file type\");\n\t\t\n\t\ttry {\n\t\t\tprogress?.Visibility = System.Windows.Visibility.Visible;\n\t\t\tusing var zip = new TempFile(ext);\n\t\t\t\n\t\t\tprogress?.Text = progressText + \". Connecting...\";\n\t\t\tvar rm = await Task.Run(() => internet.http.Get(url, dontWait: true)); //not GetAsync because it blocks for 7 s on my VMWare Win7\n\t\t\tif (!await rm.DownloadAsync(zip, p => { progress?.Text = $\"{progressText}, {p.Percent}%\"; }, ct)) return null;\n\t\t\t\n\t\t\tprogress?.Text = \"Extracting\";\n\t\t\tawait Task.Run(() => {\n\t\t\t\tvar aDel = _deleteOldFiles ? Directory.GetFileSystemEntries(_dirExtract) : null;\n\t\t\t\t\n\t\t\t\tstring sentinel = _dirExtract + c_sentinel;\n\t\t\t\tfilesystem.saveText(sentinel, \"\");\n\t\t\t\t\n\t\t\t\tif (_deleteOldFiles) filesystem.delete(aDel);\n\t\t\t\t\n\t\t\t\tvar sevenzip = folders.ThisAppBS + @\"32\\7za.exe\";\n\t\t\t\tif (0 != run.console(out var s, sevenzip, $@\"x -aoa -o\"\"{_dirExtract}\"\" \"\"{zip}\"\"\")) throw new AuException($\"*extract files. {s}\");\n\t\t\t\t\n\t\t\t\t_sentinel = sentinel;\n\t\t\t});\n\t\t}\n\t\tcatch (Exception e1) {\n\t\t\tprint.warning(e1, failedWarningPrefix);\n\t\t\treturn false;\n\t\t}\n\t\tfinally {\n\t\t\tprogress?.Text = \"\";\n\t\t}\n\t\treturn true;\n\t}\n\t\n\tconst string c_sentinel = @\"\\.la-extract-sentinel\";\n\t\n\tpublic void Dispose() {\n\t\tif (_sentinel != null) filesystem.delete(_sentinel);\n\t}\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if <see cref=\"DownloadAndExtract\"/> was killed during the \"delete existing files and extract\" operation. Ie the directory content is invalid.\n\t/// </summary>\n\t/// <param name=\"dirExtract\">The same directory as used with <b>DownloadAndExtract</b>.</param>\n\tpublic static bool IsDirectoryInvalid(string dirExtract) => filesystem.exists(dirExtract + c_sentinel, true);\n}\n"
  },
  {
    "path": "Au.Editor/xUtil/Ed util shared.cs",
    "content": "//This file also can be used in scripts (meta c).\n\nusing System.Security.Cryptography;\n\nnamespace LA;\n\n/// <summary>\n/// Gets paths of LA folders, regardless whether this code runs in LA or in a script, even when launched not by LA.\n/// In script must be defined SCRIPT.\n/// </summary>\nstatic class folders2 {\n\t/// <summary>\n\t/// ThisApp\n\t/// </summary>\n\tpublic static FolderPath La\n#if SCRIPT\n\t\t=> new(Environment.GetEnvironmentVariable(\"au_\") ?? throw null);\n#else\n\t\t=> folders.ThisApp;\n#endif\n\t\n\t/// <summary>\n\t/// ThisAppDataLocal\n\t/// </summary>\n\tpublic static FolderPath LaDataLocal\n#if SCRIPT\n\t\t=> new(folders.LocalAppData + @\"LibreAutomate\");\n#else\n\t\t=> folders.ThisAppDataLocal;\n#endif\n\t\n\t/// <summary>\n\t/// ThisAppDataRoaming\n\t/// </summary>\n\tpublic static FolderPath LaDataRoaming\n#if SCRIPT\n\t\t=> new(folders.RoamingAppData + @\"LibreAutomate\");\n#else\n\t\t=> folders.ThisAppDataRoaming;\n#endif\n}\n\n/// <summary>\n/// Calls <see cref=\"ProtectedData\"/> <c>Protect</c> or <c>Unprotect</c> with LA's entropy.\n/// </summary>\nstatic class EdProtectedData {\n\tstatic byte[] _entropy = [212, 71, 168, 115, 1, 83, 144, 90];\n\t\n\t/// <returns>Protected data as Base64 string. On exception prints warning and returns <c>null</c>.</returns>\n\tpublic static string Protect(string s) {\n\t\ttry { return Convert.ToBase64String(ProtectedData.Protect(s.ToUTF8(), _entropy, DataProtectionScope.CurrentUser)); }\n\t\tcatch (Exception ex) { print.warning(ex); return null; }\n\t}\n\t\n\t/// <returns>Unprotected data as string. On exception prints warning and returns <c>null</c>.</returns>\n\tpublic static string Unprotect(string s) {\n\t\ttry { return ProtectedData.Unprotect(Convert.FromBase64String(s), _entropy, DataProtectionScope.CurrentUser).ToStringUTF8(); }\n\t\tcatch (Exception ex) { print.warning(ex); return null; }\n\t}\n}\n\n/// <summary>\n/// Compresses and extracts files using the LA's installed 7za.exe.\n/// </summary>\nstatic class SevenZip {\n\tstatic string _Sevenzip => folders2.La + @\"32\\7za.exe\";\n\t\n\t/// <summary>\n\t/// Compresses a file or directory.\n\t/// </summary>\n\t/// <param name=\"output\">7za.exe output text.</param>\n\t/// <param name=\"zipFile\">The compressed file to create. If exists, deletes at first; else creates directory for it if need.</param>\n\t/// <param name=\"fileOrDir\">File, directory or wildcard. Full path or path in the current directory. If directory like <c>@\"dir\\*\"</c>, adds its contents to the root.</param>\n\t/// <param name=\"curDir\">Current directory path to pass to 7za.exe.</param>\n\t/// <param name=\"type7z\">Create 7z archive regardless of <i>zipFile</i> filename extension.</param>\n\t/// <param name=\"switches\">Optional 7za.exe command line switches/arguments.</param>\n\t/// <returns>false if returned an error code.</returns>\n\t/// <exception cref=\"Exception\">Exceptions of used functions. Unlikely.</exception>\n\tpublic static bool Compress(out string output, string zipFile, string fileOrDir, string curDir = null, bool type7z = false, string switches = null) {\n\t\tif (filesystem.exists(zipFile)) filesystem.delete(zipFile); else filesystem.createDirectoryFor(zipFile);\n\t\treturn 0 == run.console(out output, _Sevenzip, $@\"a \"\"{zipFile}\"\" \"\"{fileOrDir}\"\" {(type7z ? \"-t7z \" : \"\")}{switches}\", curDir);\n\t}\n\t\n\t/// <summary>\n\t/// Compresses a multiple files/directories.\n\t/// </summary>\n\t/// <param name=\"output\">7za.exe output text.</param>\n\t/// <param name=\"zipFile\">The compressed file to create. If exists, deletes at first; else creates directory for it if need.</param>\n\t/// <param name=\"files\">List of files/directories/wildcards. Full paths or paths in <i>inDir</i>. If a directory is like <c>@\"dir\\*\"</c>, adds its contents to the root.</param>\n\t/// <param name=\"curDir\">Current directory path to pass to 7za.exe. It will be the base directory of non-full-path files specified in <i>files</i>.</param>\n\t/// <param name=\"type7z\">Create 7z archive regardless of <i>zipFile</i> filename extension.</param>\n\t/// <param name=\"switches\">Optional 7za.exe command line switches/arguments.</param>\n\t/// <returns>false if returned an error code.</returns>\n\t/// <exception cref=\"Exception\">Exceptions of used functions. Unlikely.</exception>\n\tpublic static bool Compress(out string output, string zipFile, IEnumerable<string> files, string curDir = null, bool type7z = false, string switches = null) {\n\t\tif (filesystem.exists(zipFile)) filesystem.delete(zipFile); else filesystem.createDirectoryFor(zipFile);\n\t\tusing var tf = new TempFile();\n\t\tfilesystem.saveText(tf, string.Join(\"\\n\", files));\n\t\treturn 0 == run.console(out output, _Sevenzip, $@\"a \"\"{zipFile}\"\" @\"\"{tf}\"\" {(type7z ? \"-t7z \" : \"\")}{switches}\", curDir);\n\t}\n\t\n\t/// <summary>\n\t/// Extracts a compressed file to a directory.\n\t/// </summary>\n\t/// <param name=\"output\">7za.exe output text.</param>\n\t/// <param name=\"zipFile\">The compressed file.</param>\n\t/// <param name=\"outDir\">The directory where to extract. If does not exist, creates; else does not delete files before extracting.</param>\n\t/// <param name=\"switches\">Optional 7za.exe command line switches/arguments. The default \"-aoa\" means overwrite all existing files without prompt.</param>\n\t/// <returns>false if returned an error code.</returns>\n\tpublic static bool Extract(out string output, string zipFile, string outDir, string switches = \"-aoa\") {\n\t\treturn 0 == run.console(out output, _Sevenzip, $@\"x \"\"{zipFile}\"\" -o\"\"{outDir}\"\" {switches}\");\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/xUtil/Ed util.cs",
    "content": "using Microsoft.Win32;\nusing System.Windows.Controls;\nusing System.Windows;\nusing System.Collections;\nusing System.IO.Compression;\nusing System.Runtime.Loader;\nusing System.Security.Cryptography;\n\nnamespace LA;\n\n/// <summary>\n/// .NET SDK etc.\n/// </summary>\nstatic class DotnetUtil {\n\tstatic DotnetUtil() {\n\t\tEnvironment.SetEnvironmentVariable(\"DOTNET_CLI_TELEMETRY_OPTOUT\", \"1\");\n\t}\n\t\n\t/// <summary>\n\t/// Call this when showing a window that uses .NET SDK.\n\t/// If SDK missing (full or minimal, depending on <see cref=\"AppSettings.minimalSDK\"/>), disables specified UI elements and adds an install link at the top.\n\t/// </summary>\n\t/// <param name=\"installedNow\">Called later when SDK is installed if was missing. Called only if then <c>w.IsLoaded</c>.</param>\n\t/// <returns>true if missing.</returns>\n\tpublic static bool MissingSdkUI(Window w, FrameworkElement[] disable, Action installedNow = null) {\n\t\tif (InitSdk()) return false;\n\t\t\n\t\tvar content = w.Content as UIElement;\n\t\tw.Content = null;\n\t\tif (w.Width < 400) w.Width = 400;\n\t\t_DisableEnableAll(enable: false);\n\t\tvar panel = new DockPanel();\n\t\t\n\t\tTextBlock t = new() { Height = 18, Margin = new(5) };\n\t\tif (App.Settings.minimalSDK == false) wpfBuilder.formatTextOf(t, $\"Missing .NET SDK. See <b>Options > Other > Use minimal .NET SDK</b>.\");\n\t\telse wpfBuilder.formatTextOf(t, $\"<b><a {_InstallMinimalSdk}>Download required components</a></b> to use this feature (~30 MB)\");\n\t\t\n\t\tUIElement[] a = [t, new Separator(), content];\n\t\tforeach (var e in a) {\n\t\t\tif (e != content) DockPanel.SetDock(e, Dock.Top);\n\t\t\tpanel.Children.Add(e);\n\t\t}\n\t\tw.Content = panel;\n\t\t\n\t\treturn true;\n\t\t\n\t\tasync void _InstallMinimalSdk() {\n\t\t\tif (!await EnsureSdkAsync(t)) return;\n\t\t\t_DisableEnableAll(enable: true);\n\t\t\tpanel.Children.Remove(content);\n\t\t\tw.Content = content;\n\t\t\tif (installedNow is { } k && w.IsLoaded) k();\n\t\t}\n\t\tvoid _DisableEnableAll(bool enable) { foreach (var v in disable) v.IsEnabled = enable; }\n\t}\n\t\n\t/// <summary>\n\t/// Ensures that the full or minimal .NET SDK is installed, depending on <see cref=\"AppSettings.minimalSDK\"/>.\n\t/// If not installed, downloads/installs the minimal SDK (without confirmation, just displays progress).\n\t/// In any case sets <see cref=\"DotnetExe\"/> and <see cref=\"SdkDir\"/>.\n\t/// Not thread-safe. Call in main thread.\n\t/// </summary>\n\t/// <param name=\"progress\">This func will set text like <c>\"Downloading minimal SDK, 20%\"</c>. Also uses it to cancel when the window closed.</param>\n\t/// <returns>false if failed to install or canceled.</returns>\n\tpublic static async Task<bool> EnsureSdkAsync(TextBlock progress) {\n\t\tif (_InitSdk(out bool installMiniSdk)) return true;\n\t\tif (!installMiniSdk) return false;\n\t\tif (!await _InstallMinimalSDK(progress)) return false;\n\t\treturn _InitSdk(out _, true);\n\t\t\n\t\tstatic async Task<bool> _InstallMinimalSDK(TextBlock progress) {\n\t\t\tvar window = Window.GetWindow(progress);\n\t\t\t\n\t\t\tstring filename = $\"sdk-{Environment.Version.ToString(2)}-{(osVersion.isArm64OS ? \"arm64\" : \"x64\")}.zip\";\n\t\t\tstring url = $\"https://www.libreautomate.com/download/sdk/{filename}\";\n\t\t\tstring dotnetDir = folders.ThisAppBS + \"SDK\";\n\t\t\tusing var dl = new Downloader();\n\t\t\tif (!dl.PrepareDirectory(dotnetDir, failedWarningPrefix: c_errorInstallMinimalSDK)) return false;\n\t\t\t\n\t\t\tCancellationTokenSource cts = new();\n\t\t\tprogress.Unloaded += (_, _) => { cts?.Cancel(); };\n\t\t\tbool? ok = await dl.DownloadAndExtract(url, progress, cts.Token, progressText: \"Downloading required components\", failedWarningPrefix: c_errorInstallMinimalSDK.Replace(\"to install\", \"to download\"));\n\t\t\tcts = null;\n\t\t\treturn ok == true;\n\t\t}\n\t\t\n\t\t//note: the minimal SDK the first time installs 3 extra packages (16 MB download). The normal SDK doesn't.\n\t\t//\tIt's OK. It's because the minimal SDK doesn't have the `packs` folder. Auto-downloads what's missing.\n\t\t//\tAlso creates empty dir `metadata` in the minimal SDK dir.\n\t}\n\t\n\t/// <summary>\n\t/// Returns <c>true</c> if the full or minimal .NET SDK is installed (depending on <see cref=\"AppSettings.minimalSDK\"/>).\n\t/// Sets <see cref=\"DotnetExe\"/> and <see cref=\"SdkDir\"/> (null if returns <c>false</c>).\n\t/// Not thread-safe. Call in main thread.\n\t/// </summary>\n\tpublic static bool InitSdk() => _InitSdk(out _);\n\t\n\tstatic bool _InitSdk(out bool installMiniSdk, bool afterInstall = false) {\n\t\tinstallMiniSdk = false;\n\t\t\n\t\tif (!afterInstall) {\n\t\t\tDebug_.PrintIf(!App.IsMainThread);\n\t\t\tif (App.IsPortable) {\n\t\t\t\tif (_IsFullSdkInstalled()) return true;\n\t\t\t\tprint.warning(\"This feature in portable mode requires the .NET SDK.\", 1);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (App.Settings.minimalSDK != true) {\n\t\t\t\tif (_IsFullSdkInstalled()) return true;\n\t\t\t\tif (App.Settings.minimalSDK == false) {\n\t\t\t\t\t(DotnetExe, SdkDir) = (null, null);\n\t\t\t\t\tprint.warning(\"This feature requires the .NET SDK. Install it or don't uncheck Options > Other > Use minimal SDK.\", 1);\n\t\t\t\t\tDebug_.Print(new StackTrace(true));\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (s_minimalSdkDir is null) {\n\t\t\tstring dotnetDir = folders.ThisAppBS + @\"SDK\", sdkDir = dotnetDir + @\"\\sdk\", sdkVerDir = null;\n\t\t\tif (filesystem.exists(sdkDir, true).Directory && !Downloader.IsDirectoryInvalid(dotnetDir)) {\n\t\t\t\tsdkVerDir = Directory.EnumerateDirectories(sdkDir, Environment.Version.ToString(2) + \".*\").FirstOrDefault();\n\t\t\t}\n\t\t\tif (sdkVerDir is null) {\n\t\t\t\tinstallMiniSdk = true;\n\t\t\t} else {\n\t\t\t\tif (sdkVerDir != null && !(_EnsureLink(\"host\") && _EnsureLink(\"shared\"))) sdkVerDir = null; //not just when installing, because the folder may be copied and the links lost\n\t\t\t\t\n\t\t\t\tif (sdkVerDir != null) (s_minimalDotnetExe, s_minimalSdkDir) = (dotnetDir + @\"\\dotnet.exe\", sdkVerDir);\n\t\t\t}\n\t\t}\n\t\t\n\t\tDotnetExe = s_minimalDotnetExe;\n\t\tSdkDir = s_minimalSdkDir;\n\t\treturn s_minimalSdkDir != null;\n\t\t\n\t\tstatic bool _IsFullSdkInstalled() {\n\t\t\tif (!s_fullSdkDirOnce) {\n\t\t\t\tif (filesystem.searchPath(\"dotnet.exe\") is string dotnetExe) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (0 == run.console(out string v, dotnetExe, \"--version\", curDir: folders.ThisApp)) { //fast. Use `curDir: folders.ThisApp` because it searches for global.json (and SDK version in it) in this and ancestor dirs.\n\t\t\t\t\t\t\tvar dir = pathname.getDirectory(dotnetExe) + @\"\\sdk\\\" + v.Trim();\n\t\t\t\t\t\t\tif (filesystem.exists(dir, true).Directory) (s_fullDotnetExe, s_fullSdkDir) = (dotnetExe, dir);\n\t\t\t\t\t\t\tDebug_.PrintIf(s_fullSdkDir is null, dir);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcatch { }\n\t\t\t\t}\n\t\t\t\ts_fullSdkDirOnce = true;\n\t\t\t}\n\t\t\tDotnetExe = s_fullDotnetExe;\n\t\t\tSdkDir = s_fullSdkDir;\n\t\t\treturn s_fullSdkDir != null;\n\t\t}\n\t\t\n\t\tstatic bool _EnsureLink(string name) {\n\t\t\tvar link = folders.ThisAppBS + @\"SDK\\\" + name;\n\t\t\tif (!filesystem.exists(link, true).IsNtfsLink) {\n\t\t\t\tvar fullDotnetDir = folders.NetRuntime.Path.RxReplace(@\"(\\\\[^\\\\]+){3}$\", \"\", 1);\n\t\t\t\ttry { filesystem.more.createSymbolicLink(link, fullDotnetDir + @\"\\\" + name, CSLink.Junction, deleteOld: true); }\n\t\t\t\tcatch (Exception ex) { print.warning(ex, c_errorInstallMinimalSDK); return false; }\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\t//note: the minimal SDK the first time installs 3 extra packages (16 MB download). The normal SDK doesn't.\n\t\t//\tIt's OK. It's because the minimal SDK doesn't have the `packs` folder. Auto-downloads what's missing.\n\t\t//\tAlso creates empty dir `metadata` in the minimal SDK dir.\n\t}\n\tstatic string s_minimalDotnetExe, s_fullDotnetExe, s_minimalSdkDir, s_fullSdkDir;\n\tstatic bool s_fullSdkDirOnce;\n\tconst string c_errorInstallMinimalSDK = \"<>Failed to install required component (minimal .NET SDK). You can retry, or manually install the full .NET 10 SDK (~200 MB download). See also Options > Other > Use minimal SDK. Issues: https://github.com/qgindi/LibreAutomate/issues.\";\n\t\n\t/// <summary>\n\t/// Path of \"dotnet.exe\" set by <see cref=\"EnsureSdkAsync\"/>. Full or minimal SDK.\n\t/// </summary>\n\tpublic static string DotnetExe { get; private set; }\n\t\n\t/// <summary>\n\t/// Path of the SDK directory with dlls, set by <see cref=\"EnsureSdkAsync\"/>. Full or minimal SDK.\n\t/// </summary>\n\tpublic static string SdkDir { get; private set; }\n\t\n\t/// <summary>\n\t/// Downloads and extracts both .NET runtimes (core and desktop) for CPU architecture x64/ARM64 other than of this process.\n\t/// Run in a background thread.\n\t/// </summary>\n\t/// <param name=\"extractDir\">Extract both to this directory.</param>\n\t/// <param name=\"portable\">Extract to <i>dir</i> subdirectory <c>\"dotnet\"</c> (if x64) or <c>\"dotnetARM\"</c> (if ARM64). Delete old subdirectory.</param>\n\t/// <exception cref=\"OperationCanceledException\">User-canceled.</exception>\n\t/// <exception cref=\"Exception\">Failed.</exception>\n\tpublic static void DownloadNetRuntimesForOtherArch(string extractDir, bool portable) {\n\t\tbool forArm = !osVersion.isArm64Process;\n\t\t\n\t\tif (portable) {\n\t\t\textractDir = extractDir + \"\\\\dotnet\" + (forArm ? \"ARM\" : null);\n\t\t\tfilesystem.delete(extractDir);\n\t\t\tfilesystem.createDirectory(extractDir);\n\t\t}\n\t\t\n\t\tstring arch = forArm ? \"arm64\" : \"x64\";\n\t\tstring version = Environment.Version.ToString();\n\t\t\n\t\t_ClearOld();\n\t\t_DownloadAndExtract(false);\n\t\t_DownloadAndExtract(true);\n\t\t\n\t\tvoid _DownloadAndExtract(bool desktop) {\n\t\t\t//\"https://builds.dotnet.microsoft.com/dotnet/Runtime/9.0.6/dotnet-runtime-9.0.6-win-arm64.zip\"\n\t\t\t//\"https://builds.dotnet.microsoft.com/dotnet/WindowsDesktop/9.0.6/windowsdesktop-runtime-9.0.6-win-arm64.zip\"\n\t\t\tvar filename = $\"{(desktop ? \"windowsdesktop\" : \"dotnet\")}-runtime-{version}-win-{arch}.zip\";\n\t\t\tvar zip = $@\"{folders.ThisAppTemp}\\download\\{filename}\";\n\t\t\tif (!filesystem.exists(zip)) {\n\t\t\t\tprint.it(\"Downloading \" + filename);\n\t\t\t\tvar url = $\"https://builds.dotnet.microsoft.com/dotnet/{(desktop ? \"WindowsDesktop\" : \"Runtime\")}/{version}/{filename}\";\n\t\t\t\tvar r = internet.http.Get(url, dontWait: true);\n\t\t\t\tif (!r.Download(zip + \"~\")) throw new OperationCanceledException();\n\t\t\t\tfilesystem.rename(zip + \"~\", filename);\n\t\t\t}\n\t\t\t\n\t\t\tprint.it(\"Extracting \" + filename);\n\t\t\tvar starts = $\"shared/Microsoft.{(desktop ? \"WindowsDesktop\" : \"NETCore\")}.App/{version}/\";\n\t\t\tusing var z = ZipFile.OpenRead(zip);\n\t\t\tforeach (var e in z.Entries) {\n\t\t\t\tvar relPath = e.FullName;\n\t\t\t\tif (!relPath.Starts(starts, true)) continue;\n\t\t\t\trelPath = relPath[starts.Length..];\n\t\t\t\tvar s = extractDir + \"\\\\\" + relPath;\n\t\t\t\tif (relPath.Contains('/')) filesystem.createDirectoryFor(s);\n\t\t\t\te.ExtractToFile(s, overwrite: true);\n\t\t\t}\n\t\t}\n\t\t\n\t\tvoid _ClearOld() {\n\t\t\ttry {\n\t\t\t\tvar rx = new regexp(@\"(?i)^(?:dotnet|windowsdesktop)-runtime-(.+?)-.+\\.zip$\");\n\t\t\t\tforeach (var f in filesystem.enumFiles(folders.ThisAppTemp + \"download\")) {\n\t\t\t\t\tif (!rx.Match(f.Name, out var m)) continue;\n\t\t\t\t\tif (m[1].Value.Eqi(version)) continue;\n\t\t\t\t\tApi.DeleteFile(f.FullPath);\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch { }\n\t\t}\n\t}\n}\n\n/// <summary>\n/// Can be used to return bool and error text. Has implicit conversions from/to bool.\n/// </summary>\nrecord struct BoolError(bool ok, string error) {\n\tpublic static implicit operator bool(BoolError x) => x.ok;\n\tpublic static implicit operator BoolError(bool ok) => new(ok, ok ? null : \"Failed.\");\n}\n\n#if DEBUG\n\nstatic class EdDebug {\n}\n\n#endif\n\nstatic class EdWpf {\n\tpublic static TextBlock TextAndHelp(string text, string helpTopic) {\n\t\tvar img = ImageUtil.LoadWpfImageElement(\"*Entypo.HelpWithCircle #008EEE @14\");\n\t\tvar s1 = text.NE() ? \"\" : \"  \";\n\t\treturn wpfBuilder.formattedText($\"{text}{s1}<a {() => HelpUtil.AuHelp(helpTopic)}>{img}</a>\");\n\t}\n\t\n\tpublic static TextBlock TextAndHelp<T>(string text) => TextAndHelp(text, typeof(T).ToString());\n\t\n\tpublic static TextBlock TextAndHelp(string text, Func<string> helpTopic) {\n\t\tvar img = ImageUtil.LoadWpfImageElement(\"*Entypo.HelpWithCircle #008EEE @14\");\n\t\tvar s1 = text.NE() ? \"\" : \"  \";\n\t\treturn wpfBuilder.formattedText($\"{text}{s1}<a {() => HelpUtil.AuHelp(helpTopic())}>{img}</a>\");\n\t}\n}\n\n/// <summary>\n/// Opens databases ref.db, doc.db (created by project DatabasesEtc) or winapi.db (created by script \"SDK create database\").\n/// </summary>\nstatic class EdDatabases {\n\tpublic static sqlite OpenRef() => _Open(\"ref.db\");\n\t\n\tpublic static sqlite OpenDoc() => _Open(\"doc.db\");\n\t\n\tpublic static sqlite OpenWinapi() => _Open(\"winapi.db\");\n\t\n\tstatic sqlite _Open(string name) {\n\t\tvar path = folders.ThisAppBS + name;\n\t\t//if (App.IsAuHomePC) { //no. Instead exit editor before running DatabasesEtc project. And it does not lock winapi.db.\n\t\t//\tvar pathNew = path + \".new\";\n\t\t//\tif (filesystem.exists(pathNew)) filesystem.move(pathNew, path, FIfExists.Delete);\n\t\t//}\n\t\treturn new sqlite(path, SLFlags.SQLITE_OPEN_READONLY);\n\t}\n\t\n\tpublic static string WinapiFile => folders.ThisAppBS + \"winapi.db\";\n}\n\n/// <summary>\n/// Temporarily disables window redrawing.\n/// Ctor sends WM_SETREDRAW(0) if visible.\n/// If was visible, Dispose sends WM_SETREDRAW(1) and calls RedrawWindow.\n/// </summary>\nstruct WndSetRedraw : IDisposable {\n\twnd _w;\n\t\n\tpublic WndSetRedraw(wnd w) {\n\t\t_w = w;\n\t\tif (_w.IsVisible) _w.Send(Api.WM_SETREDRAW, 0); else _w = default;\n\t}\n\t\n\tpublic unsafe void Dispose() {\n\t\tif (_w.Is0) return;\n\t\t_w.Send(Api.WM_SETREDRAW, 1);\n\t\tApi.RedrawWindow(_w, flags: Api.RDW_ERASE | Api.RDW_FRAME | Api.RDW_INVALIDATE | Api.RDW_ALLCHILDREN);\n\t\t_w = default;\n\t}\n}\n\nrecord struct StartEndText(int start, int end, string text) {\n\tpublic int Length => end - start;\n\tpublic Range Range => start..end;\n\t\n\t/// <inheritdoc cref=\"ReplaceAll(string, List{StartEndText}, ref StringBuilder)\"/>\n\tpublic static string ReplaceAll(string s, List<StartEndText> a) {\n\t\tStringBuilder b = null;\n\t\tReplaceAll(s, a, ref b);\n\t\treturn b.ToString();\n\t}\n\t\n\t/// <summary>\n\t/// Replaces all text ranges specified in <i>a</i> with strings specified in <i>a</i>.\n\t/// </summary>\n\t/// <param name=\"a\">Text ranges and replacement texts. Must be sorted by range. Ranges must not overlap.</param>\n\t/// <param name=\"b\">Receives new text. If null, the function creates new, else at first calls <c>b.Clear()</c>.</param>\n\t/// <exception cref=\"ArgumentException\">Ranges are overlapped or not sorted. Only #if DEBUG.</exception>\n\tpublic static void ReplaceAll(string s, List<StartEndText> a, ref StringBuilder b) {\n\t\tThrowIfNotSorted(a);\n\t\tint cap = s.Length - a.Sum(o => o.Length) + a.Sum(o => o.text.Length);\n\t\tif (b == null) b = new(cap);\n\t\telse {\n\t\t\tb.Clear();\n\t\t\tb.EnsureCapacity(cap);\n\t\t}\n\t\t\n\t\tint i = 0;\n\t\tforeach (var v in a) {\n\t\t\tb.Append(s, i, v.start - i).Append(v.text);\n\t\t\ti = v.end;\n\t\t}\n\t\tb.Append(s, i, s.Length - i);\n\t}\n\t\n\t/// <exception cref=\"ArgumentException\">Ranges are overlapped or not sorted. [Conditional(\"DEBUG\")].</exception>\n\t[Conditional(\"DEBUG\")]\n\tinternal static void ThrowIfNotSorted(List<StartEndText> a) {\n\t\tfor (int i = 1; i < a.Count; i++) if (a[i].start < a[i - 1].end) throw new ArgumentException(\"ranges must be sorted and not overlapped\");\n\t}\n}\n\nstatic class EdComUtil {\n\t\n\t//To convert a COM type library we use TypeLibConverter class. However .NET Core+ does not have it.\n\t//Workaround: the code is in Au.Net4.exe. It uses .NET Framework 4.x. We call it through run.console.\n\t//We don't use tlbimp.exe:\n\t//\t1. If some used interop assemblies are in GAC (eg MS Office PIA), does not create files for them. But we cannot use GAC in a Core+ app.\n\t//\t2. Does not tell what files created.\n\t//\t3. My PC somehow has MS Office PIA installed and there is no uninstaller. After deleting the GAC files tlbimp.exe created all files, but it took several minutes.\n\t//Tested: impossible to convert .NET Framework TypeLibConverter code. Part of it is in extern methods.\n\t//Tested: cannot use .NET Framework dll for it. Fails at run time because uses Core+ assemblies, and they don't have the class. Need exe.\n\tpublic static async Task<List<string>> ConvertTypeLibrary(object tlDef, Window owner) {\n\t\tstring comDll = null;\n\t\tswitch (tlDef) {\n\t\tcase string path:\n\t\t\tcomDll = path;\n\t\t\tbreak;\n\t\tcase RegTypelib r:\n\t\t\t//can be several locales\n\t\t\tvar aloc = new List<string>(); //registry keys like \"0\" or \"409\"\n\t\t\tvar aloc2 = new List<string>(); //locale names for display in the list dialog\n\t\t\tusing (var verKey = Registry.ClassesRoot.OpenSubKey($@\"TypeLib\\{r.guid}\\{r.version}\")) {\n\t\t\t\tforeach (var s1 in verKey.GetSubKeyNames()) {\n\t\t\t\t\tint lcid = s1.ToInt(0, out int iEnd, STIFlags.IsHexWithout0x);\n\t\t\t\t\tif (iEnd != s1.Length) continue; //\"FLAGS\" etc; must be hex number without 0x\n\t\t\t\t\taloc.Add(s1);\n\t\t\t\t\tvar s2 = \"Neutral\";\n\t\t\t\t\tif (lcid > 0) {\n\t\t\t\t\t\ttry { s2 = new System.Globalization.CultureInfo(lcid).DisplayName; } catch { s2 = s1; }\n\t\t\t\t\t}\n\t\t\t\t\taloc2.Add(s2);\n\t\t\t\t}\n\t\t\t}\n\t\t\tstring locale;\n\t\t\tif (aloc.Count == 1) locale = aloc[0];\n\t\t\telse {\n\t\t\t\tint i = dialog.showList(aloc2, \"COM type library locale\", owner: owner);\n\t\t\t\tif (i == 0) return null;\n\t\t\t\tlocale = aloc[i - 1];\n\t\t\t}\n\t\t\tcomDll = r.GetPath(locale);\n\t\t\tif (comDll == null /*|| !filesystem.exists(comDll).File*/) { //can be \"filepath/resource\"\n\t\t\t\tprint.it(\"Failed to get file path.\");\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\t\n\t\tprint.it(\"<><lc YellowGreen>Converting COM type library to .NET assembly.<>\");\n\t\tList<string> converted = [];\n\t\tint rr = -1;\n\t\towner.IsEnabled = false;\n\t\ttry {\n\t\t\tawait Task.Run(() => {\n\t\t\t\tvar dir = folders.Workspace + @\".interop\\\";\n\t\t\t\tfilesystem.createDirectory(dir);\n\t\t\t\tvoid _Callback(string s) {\n\t\t\t\t\t//skip some useless warnings, eg \"can't convert some internal type\"\n\t\t\t\t\tbool mute = s.RxIsMatch(@\"Warning: .+ (?:could not convert the signature for the member '(?:\\w*_|tag|[A-Z]+\\.)|as a pointer and may require unsafe code to manipulate.|to import this property as a method instead.)\");\n\t\t\t\t\tif (!mute) print.it(s);\n\t\t\t\t\tif (s.Starts(\"<>Converted: \")) {\n\t\t\t\t\t\ts.RxMatch(@\"<explore .+?>(.+?)<>$\", 1, out s);\n\t\t\t\t\t\tconverted.Add(s);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\trr = run.console(_Callback, folders.ThisAppBS + \"Au.Net4.exe\", $\"/typelib \\\"{dir}|{comDll}\\\"\");\n\t\t\t});\n\t\t}\n\t\tcatch (Exception ex) { print.it(\"Failed to convert type library\", ex.ToStringWithoutStack()); }\n\t\towner.IsEnabled = true;\n\t\tif (rr != 0) return null;\n\t\tprint.it(\"==== DONE ====\");\n\t\treturn converted;\n\t}\n\t\n\tpublic record class RegTypelib(string text, string guid, string version) {\n\t\tpublic override string ToString() => text;\n\t\t\n\t\tpublic string GetPath(string locale) {\n\t\t\tvar k0 = $@\"TypeLib\\{guid}\\{version}\\{locale}\\win\";\n\t\t\tfor (int i = 0; i < 2; i++) {\n\t\t\t\tvar bits = osVersion.is32BitProcess == (i == 1) ? \"32\" : \"64\";\n\t\t\t\tusing var hk = Registry.ClassesRoot.OpenSubKey(k0 + bits);\n\t\t\t\tif (hk?.GetValue(\"\") is string path) return path.Trim('\"');\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t}\n\t\n}\n\n/// <summary>\n/// Gets relative path from full path when recursively enumerating a directory. Optionally with a prefix. Almost no garbage.\n/// </summary>\nclass RelativePath {\n\tchar[] _a;\n\tint _fullPathBaseLen;\n\tstring _prefix;\n\t\n\tpublic RelativePath(string prefix) {\n\t\t_prefix = prefix ?? \"\";\n\t\t_a = new char[_prefix.Length + 300];\n\t\tprefix.CopyTo(_a);\n\t}\n\t\n\tpublic RStr GetRelativePath(string fullPath) {\n\t\tif (_fullPathBaseLen == 0) _fullPathBaseLen = fullPath.LastIndexOf('\\\\');\n\t\tint len1 = fullPath.Length - _fullPathBaseLen, len2 = len1 + _prefix.Length;\n\t\tif (_a.Length < len2) Array.Resize(ref _a, len2 + 300);\n\t\tfullPath.CopyTo(_fullPathBaseLen, _a, _prefix.Length, len1);\n\t\treturn _a.AsSpan(0, len2);\n\t}\n}\n\n/// <summary>\n/// Wildcard-compares paths with a list of strings.\n/// </summary>\nclass WildcardList {\n\treadonly string[] _a;\n\t\n\t/// <summary>\n\t/// Creates a list of strings from a multiline string.\n\t/// </summary>\n\t/// <param name=\"list\">Multiline string. Use <c>//</c> to comment out a line.</param>\n\t/// <param name=\"replaceSlash\">Replace <c>'/'</c> with <c>'\\\\'</c> (to match file paths).</param>\n\t/// <param name=\"matchWithoutBackslashAt0\">Let `\\folder` match `folder`; and let `*\\folder` match both `folder` and `ancestors\\folder`.</param>\n\tpublic WildcardList(string list, bool replaceSlash = true, bool matchWithoutBackslashAt0 = false) {\n\t\tif (list.NE()) {\n\t\t\t_a = [];\n\t\t} else {\n\t\t\tList<string> a = [];\n\t\t\tforeach (var s_ in list.Lines(noEmpty: true)) {\n\t\t\t\tstring s = s_;\n\t\t\t\tif (s.Starts(\"//\")) continue;\n\t\t\t\tif (replaceSlash) s = s.Replace('/', '\\\\');\n\t\t\t\tif (s.FindNot(replaceSlash ? @\"*\\\" : \"*\") < 0) continue; //wildcard like \"*\" or \"\\*\" or \"\\\" etc makes no sense. Either matches always or never. Prevent accidentally excluding all files etc.\n\t\t\t\tif (matchWithoutBackslashAt0 && (s[0] == '\\\\' || s.Starts(@\"*\\\"))) {\n\t\t\t\t\tif (s[0] == '\\\\') { //`\\folder`: match `folder`\n\t\t\t\t\t\ta.Add(s[1..]);\n\t\t\t\t\t} else { //`*\\folder`: match both `folder` and `ancestors\\folder`\n\t\t\t\t\t\ta.Add(s[2..]);\n\t\t\t\t\t\ta.Add(s);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\ta.Add(s);\n\t\t\t\t}\n\t\t\t}\n\t\t\t_a = a.ToArray();\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Sets <c>ListOfStrings = list</c>.\n\t/// </summary>\n\tpublic WildcardList(string[] list) {\n\t\t_a = list;\n\t}\n\t\n\t/// <summary>\n\t/// Wildcard-compares <i>s</i> with the list of wildcard strings.\n\t/// </summary>\n\t/// <returns>true if a <i>s</i> matches a wildcard string.</returns>\n\tpublic bool IsMatch(RStr s, bool ignoreCase) {\n\t\tforeach (var v in _a) if (s.Like(v, ignoreCase)) return true;\n\t\treturn false;\n\t}\n\t\n\tpublic string[] ListOfStrings => _a;\n}\n\n/// <summary>\n/// Disables/reenables a <b>Window</b>. Supports nested disabling (uses reference counting).\n/// Example:\n/// <c>WindowDisabler _disabler;\n/// ...\n/// _disabler = new(this);\n/// ...\n/// using var _ = _disabler.Disable();</c>\n/// </summary>\nclass WindowDisabler {\n\treadonly Window _window;\n\tint _count;\n\t\n\tpublic WindowDisabler(Window window) => _window = window;\n\t\n\tpublic IDisposable Disable() {\n\t\tif (_count++ == 0) _window.IsEnabled = false;\n\t\treturn new ReenableDisposable(this);\n\t}\n\t\n\tclass ReenableDisposable : IDisposable {\n\t\treadonly WindowDisabler _d;\n\t\tbool _disposed;\n\t\t\n\t\tpublic ReenableDisposable(WindowDisabler d) => _d = d;\n\t\t\n\t\tpublic void Dispose() {\n\t\t\tif (_disposed) return;\n\t\t\t_disposed = true;\n\t\t\tif (--_d._count == 0)\n\t\t\t\t_d._window.IsEnabled = true;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/xUtil/EdExt.cs",
    "content": "using System.Windows;\nusing System.Windows.Controls;\nusing Au.Controls;\n\nnamespace LA;\n\n/// <summary>\n/// Misc extension methods.\n/// </summary>\nstatic class EdExt {\n\t/// <summary>\n\t/// Returns true if this starts with <i>s</i> and then follows '\\\\' or '/'.\n\t/// Case-insensitive.\n\t/// </summary>\n\t/// <param name=\"orEquals\">Also return true if this equals <i>s</i>.</param>\n\tpublic static bool PathStarts(this string t, RStr s, bool orEquals = false) {\n\t\tif (!t.Starts(s, true)) return false;\n\t\tif (t.Length > s.Length) return t[s.Length] is '\\\\' or '/';\n\t\treturn orEquals;\n\t}\n\t\n\t/// <summary>\n\t/// Appends hex like \"1A\".\n\t/// Like <c>t.Append(x.ToString(\"X\"))</c> or <c>t.AppendFormat(...)</c>, but faster and no garbage.\n\t/// </summary>\n\tpublic static StringBuilder AppendHex(this StringBuilder t, uint x) {\n\t\tif (x == 0) {\n\t\t\tt.Append('0');\n\t\t} else {\n\t\t\tSpan<char> p = stackalloc char[8];\n\t\t\tint i = 8;\n\t\t\tfor (; x != 0; x >>= 4) {\n\t\t\t\tuint h = x & 15;\n\t\t\t\tp[--i] = (char)(h + (h < 10 ? 48 : 55));\n\t\t\t}\n\t\t\tt.Append(p[i..]);\n\t\t}\n\t\treturn t;\n\t}\n\t\n\t/// <inheritdoc cref=\"AppendHex(StringBuilder, uint)\"/>\n\tpublic static StringBuilder AppendHex(this StringBuilder t, int x) => AppendHex(t, (uint)x);\n\t\n\t/// <summary>\n\t/// Inserts <i>e</i> as menu item.\n\t/// If <i>e</i> is null, creates and inserts separator.\n\t/// </summary>\n\t/// <inheritdoc cref=\"InsertCustom(MenuItem, int, string, RoutedEventHandler, bool)\"/>\n\tpublic static void InsertCustom(this MenuItem t, int i, FrameworkElement e = null) {\n\t\te ??= new Separator();\n\t\te.Tag = e;\n\t\tif (i < 0) t.Items.Add(e); else t.Items.Insert(i, e);\n\t}\n\t\n\t/// <summary>\n\t/// Creates and inserts menu item.\n\t/// </summary>\n\t/// <param name=\"i\">Insert at this index. If -1, adds at the end.</param>\n\t/// <param name=\"text\">Item text.</param>\n\t/// <param name=\"click\"><b>Click</b> event handler or null.</param>\n\t/// <param name=\"escapeUnderscore\">Replace all \"_\" with \"__\".</param>\n\t/// <remarks>\n\t/// Sets <b>Tag</b> so that <see cref=\"RemoveAllCustom\"/> will recognize items added by this function.\n\t/// </remarks>\n\tpublic static MenuItem InsertCustom(this MenuItem t, int i, string text, RoutedEventHandler click, bool escapeUnderscore = true) {\n\t\tif (escapeUnderscore) text = text.Replace(\"_\", \"__\");\n\t\tvar mi = new MenuItem { Header = text };\n\t\tif (click != null) mi.Click += click;\n\t\tInsertCustom(t, i, mi);\n\t\treturn mi;\n\t}\n\t\n\t/// <param name=\"click\"><b>Click</b> event handler or null. Receives unescaped <i>text</i>.</param>\n\t/// <inheritdoc cref=\"InsertCustom(MenuItem, int, string, RoutedEventHandler, bool)\"/>\n\tpublic static MenuItem InsertCustom(this MenuItem t, int i, string text, Action<string> click, bool escapeUnderscore = true)\n\t\t=> InsertCustom(t, i, text, (o, _) => click(text), escapeUnderscore);\n\t\n\t/// <summary>\n\t/// Removes all menu items inserted by <see cref=\"InsertCustom\"/>.\n\t/// </summary>\n\t/// <param name=\"t\"></param>\n\tpublic static void RemoveAllCustom(this MenuItem t) {\n\t\tfor (int j = t.Items.Count; --j >= 0;) if (t.Items[j] is FrameworkElement e && e.Tag == e) t.Items.RemoveAt(j);\n\t}\n\t\n\t/// <summary>\n\t/// Shows window of type T and ensures that there is single window of that type for this <b>FileNode</b> at a time. \n\t/// If a window of type T for this <b>FileNode</b> already exists (created with this function), activates it. Else calls <i>fNew</i> and <b>Show</b>. Auto-closes that window when unloading this workspace.\n\t/// Example: <c>f.SingleDialog(() => new DThisClass());</c>\n\t/// </summary>\n\t/// <param name=\"f\">Can be null.</param>\n\t/// <param name=\"fNew\">Called to create new T if there is no T window.</param>\n\t/// <returns>The window (new or existing), or null if <i>f</i> null.</returns>\n\tpublic static T SingleDialog<T>(this FileNode f, Func<T> fNew) where T : KDialogWindow {\n\t\tif (f == null) return null;\n\t\tvar v = s_singleDialog.FirstOrDefault(o => o.f == f && o.d is T);\n\t\tif (v.f != null) {\n\t\t\tv.d.Hwnd().ActivateL(true);\n\t\t\treturn v.d as T;\n\t\t} else {\n\t\t\tvar d = fNew();\n\t\t\ts_singleDialog.Add((f, d));\n\t\t\t\n\t\t\tApp.Model.UnloadingThisWorkspace += d.Close;\n\t\t\td.Closed += (_, _) => {\n\t\t\t\tApp.Model.UnloadingThisWorkspace -= d.Close;\n\t\t\t\ts_singleDialog.Remove((f, d));\n\t\t\t};\n\t\t\t\n\t\t\td.Show();\n\t\t\treturn d;\n\t\t}\n\t}\n\tstatic List<(FileNode f, KDialogWindow d)> s_singleDialog = new();\n}\n"
  },
  {
    "path": "Au.Editor/xUtil/EdIcons.cs",
    "content": "namespace LA;\n\nstatic class EdIcons {\n\tpublic const string\n\t\tScript = \"*Material.ScriptOutline #73BF00|#87E100\",\n\t\t//Script = \"*Material.Square white %4,1,4,1,f;*Material.ScriptOutline #73BF00|#87E100\", //white-filled. In some places looks not good.\n\t\tClass = \"*Codicons.SymbolClass #4080FF|#84ACFF\",\n\t\tFolder = \"*Material.Folder\" + darkYellow,\n\t\tFolderOpen = \"*Material.FolderOpen\" + darkYellow,\n\t\tBack = \"*EvaIcons.ArrowBack\" + black,\n\t\tTrigger = \"*Codicons.SymbolEvent\" + blue,\n\t\tIcons = \"*FontAwesome.IconsSolid\" + blue,\n\t\tKeys = \"*Material.KeyboardOutline\" + blue,\n\t\tColor = \"*MaterialDesign.ColorLens\" + blue,\n\t\tUndo = \"*Ionicons.UndoiOS\" + brown,\n\t\tPaste = \"*Material.ContentPaste\" + brown,\n\t\tReferences = \"*Material.MapMarkerMultiple\" + blue,\n\t\tRegex = \"*FileIcons.Regex @12\" + blue,\n\t\tHelp = \"*Lucide.CircleHelp\" + darkYellow,\n\t\tAiSearch = $\"*FontAwesome.SearchSolid %,7,7,-1{blue}; *RemixIcon.GeminiFill %4,-1,-1,4{blue}\"\n\t\t;\n\t\n\tpublic const string\n\t\tblack = \" #505050|#EEEEEE\",\n\t\tblue = \" #4080FF|#99CCFF\",\n\t\t//darkBlue = \" #5060FF|#7080FF\",\n\t\t//lightBlue = \" #B0C0FF|#D0E0FF\",\n\t\tgreen = \" #99BF00|#A7D000\",\n\t\tgreen2 = \" #40B000|#4FD200\",\n\t\tbrown = \" #9F5300|#EEEEEE\",\n\t\tpurple = \" #A040FF|#D595FF\",\n\t\tdarkYellow = \" #EABB00\",\n\t\torange = \" #FFA500\",\n\t\tred = \" #FF4040|#FF9595\"\n\t\t;\n\t\n\tpublic static string FolderIcon(bool open) => open ? FolderOpen : Folder;\n\t\n\tpublic static string FolderArrow(bool open) => open ? \"*Material.ChevronDown @9 #404040\" : \"*Material.ChevronRight @9 #404040\";\n}\n"
  },
  {
    "path": "Au.Editor/xUtil/Libs/DiffMatchPatch.cs",
    "content": "/*\n * Diff Match and Patch\n * Copyright 2018 The diff-match-patch Authors.\n * https://github.com/google/diff-match-patch\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//Au:\n//\tRemoved System.Web dependency. Now delta escaping is C#-style, not URL.\n//\tRemoved System.Linq dependency.\n//\tReplaced DateTime.Now with GetTickCount.\n//\tSome other small changes.\n\n#pragma warning disable 1591 //no XML comment\n\nusing System.Text.RegularExpressions;\n\nnamespace Libs.DiffMatchPatch\n{\n\tinternal static class CompatibilityExtensions\n\t{\n\t\t// JScript splice function\n\t\tpublic static List<T> Splice<T>(this List<T> input, int start, int count,\n\t\t\tparams T[] objects)\n\t\t{\n\t\t\tList<T> deletedRange = input.GetRange(start, count);\n\t\t\tinput.RemoveRange(start, count);\n\t\t\tinput.InsertRange(start, objects);\n\n\t\t\treturn deletedRange;\n\t\t}\n\n\t\t// Java substring function\n\t\tpublic static string JavaSubstring(this string s, int begin, int end)\n\t\t{\n\t\t\treturn s.Substring(begin, end - begin);\n\t\t}\n\n\t\tpublic static T First<T>(this List<T> t) => t[0];\n\n\t\tpublic static T Last<T>(this List<T> t) => t[t.Count - 1];\n\t}\n\n\t/**-\n\t * The data structure representing a diff is a List of Diff objects:\n\t * {Diff(Operation.DELETE, \"Hello\"), Diff(Operation.INSERT, \"Goodbye\"),\n\t *  Diff(Operation.EQUAL, \" world.\")}\n\t * which means: delete \"Hello\", add \"Goodbye\" and keep \" world.\"\n\t */\n\tpublic enum Operation\n\t{\n\t\tDELETE, INSERT, EQUAL\n\t}\n\n\n\t/**\n\t * Class representing one diff operation.\n\t */\n\tpublic class Diff\n\t{\n\t\tpublic Operation operation;\n\t\t// One of: INSERT, DELETE or EQUAL.\n\t\tpublic string text;\n\t\t// The text associated with this diff operation.\n\n\t\t/**\n\t\t * Constructor.  Initializes the diff with the provided values.\n\t\t * @param operation One of INSERT, DELETE or EQUAL.\n\t\t * @param text The text being applied.\n\t\t */\n\t\tpublic Diff(Operation operation, string text)\n\t\t{\n\t\t\t// Construct a diff with the specified operation and text.\n\t\t\tthis.operation = operation;\n\t\t\tthis.text = text;\n\t\t}\n\n\t\t/**\n\t\t * Display a human-readable version of this Diff.\n\t\t * @return text version.\n\t\t */\n\t\tpublic override string ToString()\n\t\t{\n\t\t\tstring prettyText = this.text.Replace('\\n', '\\u00b6');\n\t\t\treturn \"Diff(\" + this.operation + \",\\\"\" + prettyText + \"\\\")\";\n\t\t}\n\n\t\t/**\n\t\t * Is this Diff equivalent to another Diff?\n\t\t * @param d Another Diff to compare against.\n\t\t * @return true or false.\n\t\t */\n\t\tpublic override bool Equals(Object obj)\n\t\t{\n\t\t\t// If parameter is null return false.\n\t\t\tif(obj == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// If parameter cannot be cast to Diff return false.\n\t\t\tDiff p = obj as Diff;\n\t\t\tif((System.Object)p == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// Return true if the fields match.\n\t\t\treturn p.operation == this.operation && p.text == this.text;\n\t\t}\n\n\t\tpublic bool Equals(Diff obj)\n\t\t{\n\t\t\t// If parameter is null return false.\n\t\t\tif(obj == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// Return true if the fields match.\n\t\t\treturn obj.operation == this.operation && obj.text == this.text;\n\t\t}\n\n\t\tpublic override int GetHashCode()\n\t\t{\n\t\t\treturn text.GetHashCode() ^ operation.GetHashCode();\n\t\t}\n\t}\n\n\n\t/**\n\t * Class representing one patch operation.\n\t */\n\tpublic class Patch\n\t{\n\t\tpublic List<Diff> diffs = new List<Diff>();\n\t\tpublic int start1;\n\t\tpublic int start2;\n\t\tpublic int length1;\n\t\tpublic int length2;\n\n\t\t/**\n\t\t * Emulate GNU diff's format.\n\t\t * Header: @@ -382,8 +481,9 @@\n\t\t * Indices are printed as 1-based, not 0-based.\n\t\t * @return The GNU diff string.\n\t\t */\n\t\tpublic override string ToString()\n\t\t{\n\t\t\tstring coords1, coords2;\n\t\t\tif(this.length1 == 0) {\n\t\t\t\tcoords1 = this.start1 + \",0\";\n\t\t\t} else if(this.length1 == 1) {\n\t\t\t\tcoords1 = Convert.ToString(this.start1 + 1);\n\t\t\t} else {\n\t\t\t\tcoords1 = (this.start1 + 1) + \",\" + this.length1;\n\t\t\t}\n\t\t\tif(this.length2 == 0) {\n\t\t\t\tcoords2 = this.start2 + \",0\";\n\t\t\t} else if(this.length2 == 1) {\n\t\t\t\tcoords2 = Convert.ToString(this.start2 + 1);\n\t\t\t} else {\n\t\t\t\tcoords2 = (this.start2 + 1) + \",\" + this.length2;\n\t\t\t}\n\t\t\tStringBuilder text = new StringBuilder();\n\t\t\ttext.Append(\"@@ -\").Append(coords1).Append(\" +\").Append(coords2)\n\t\t\t\t.Append(\" @@\\n\");\n\t\t\t// Escape the body of the patch with %xx notation.\n\t\t\tforeach(Diff aDiff in this.diffs) {\n\t\t\t\tswitch(aDiff.operation) {\n\t\t\t\tcase Operation.INSERT:\n\t\t\t\t\ttext.Append('+');\n\t\t\t\t\tbreak;\n\t\t\t\tcase Operation.DELETE:\n\t\t\t\t\ttext.Append('-');\n\t\t\t\t\tbreak;\n\t\t\t\tcase Operation.EQUAL:\n\t\t\t\t\ttext.Append(' ');\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\ttext.Append(diff_match_patch.encodeURI(aDiff.text)).Append(\"\\n\");\n\t\t\t}\n\t\t\treturn text.ToString();\n\t\t}\n\t}\n\n\n\t/**\n\t * Class containing the diff, match and patch methods.\n\t * Also contains the behaviour settings.\n\t */\n\tpublic class diff_match_patch\n\t{\n\t\t// Defaults.\n\t\t// Set these on your diff_match_patch instance to override the defaults.\n\n\t\t// Number of seconds to map a diff before giving up (0 for infinity).\n\t\tpublic float Diff_Timeout = 1.0f;\n\t\t// Cost of an empty edit operation in terms of edit characters.\n\t\tpublic short Diff_EditCost = 4;\n\t\t// At what point is no match declared (0.0 = perfection, 1.0 = very loose).\n\t\tpublic float Match_Threshold = 0.5f;\n\t\t// How far to search for a match (0 = exact location, 1000+ = broad match).\n\t\t// A match this many characters away from the expected location will add\n\t\t// 1.0 to the score (0.0 is a perfect match).\n\t\tpublic int Match_Distance = 1000;\n\t\t// When deleting a large block of text (over ~64 characters), how close\n\t\t// do the contents have to be to match the expected contents. (0.0 =\n\t\t// perfection, 1.0 = very loose).  Note that Match_Threshold controls\n\t\t// how closely the end points of a delete need to match.\n\t\tpublic float Patch_DeleteThreshold = 0.5f;\n\t\t// Chunk size for context length.\n\t\tpublic short Patch_Margin = 4;\n\n\t\t// The number of bits in an int.\n\t\tconst short Match_MaxBits = 32;\n\t\tint _startTime, _timeout;\n\n\n\t\t//  DIFF FUNCTIONS\n\n\n\t\t/**\n\t\t * Find the differences between two texts.\n\t\t * @param text1 Old string to be diffed.\n\t\t * @param text2 New string to be diffed.\n\t\t * @param checklines Speedup flag.  If false, then don't run a\n\t\t *     line-level diff first to identify the changed areas.\n\t\t *     If true, then run a faster slightly less optimal diff.\n\t\t * @return List of Diff objects.\n\t\t */\n\t\tpublic List<Diff> diff_main(string text1, string text2, bool checklines = true)\n\t\t{\n\t\t\t// Set a deadline by which time the diff must be complete.\n\t\t\tif(this.Diff_Timeout <= 0 || this.Diff_Timeout > int.MaxValue / 1000) {\n\t\t\t\t_timeout = 0;\n\t\t\t} else {\n\t\t\t\t_startTime = Environment.TickCount;\n\t\t\t\t_timeout = (int)(this.Diff_Timeout * 1000d);\n\t\t\t}\n\t\t\treturn _diff_main(text1, text2, checklines);\n\t\t}\n\n\t\t/**\n\t\t * Find the differences between two texts.  Simplifies the problem by\n\t\t * stripping any common prefix or suffix off the texts before diffing.\n\t\t * @param text1 Old string to be diffed.\n\t\t * @param text2 New string to be diffed.\n\t\t * @param checklines Speedup flag.  If false, then don't run a\n\t\t *     line-level diff first to identify the changed areas.\n\t\t *     If true, then run a faster slightly less optimal diff.\n\t\t *     internally for recursive calls.  Users should set DiffTimeout\n\t\t *     instead.\n\t\t * @return List of Diff objects.\n\t\t */\n\t\tprivate List<Diff> _diff_main(string text1, string text2, bool checklines)\n\t\t{\n\t\t\t// Check for null inputs not needed since null can't be passed in C#.\n\n\t\t\t// Check for equality (speedup).\n\t\t\tList<Diff> diffs;\n\t\t\tif(text1 == text2) {\n\t\t\t\tdiffs = new List<Diff>();\n\t\t\t\tif(text1.Length != 0) {\n\t\t\t\t\tdiffs.Add(new Diff(Operation.EQUAL, text1));\n\t\t\t\t}\n\t\t\t\treturn diffs;\n\t\t\t}\n\n\t\t\t// Trim off common prefix (speedup).\n\t\t\tint commonlength = diff_commonPrefix(text1, text2);\n\t\t\tstring commonprefix = text1.Substring(0, commonlength);\n\t\t\ttext1 = text1.Substring(commonlength);\n\t\t\ttext2 = text2.Substring(commonlength);\n\n\t\t\t// Trim off common suffix (speedup).\n\t\t\tcommonlength = diff_commonSuffix(text1, text2);\n\t\t\tstring commonsuffix = text1.Substring(text1.Length - commonlength);\n\t\t\ttext1 = text1.Substring(0, text1.Length - commonlength);\n\t\t\ttext2 = text2.Substring(0, text2.Length - commonlength);\n\n\t\t\t// Compute the diff on the middle block.\n\t\t\tdiffs = diff_compute(text1, text2, checklines);\n\n\t\t\t// Restore the prefix and suffix.\n\t\t\tif(commonprefix.Length != 0) {\n\t\t\t\tdiffs.Insert(0, (new Diff(Operation.EQUAL, commonprefix)));\n\t\t\t}\n\t\t\tif(commonsuffix.Length != 0) {\n\t\t\t\tdiffs.Add(new Diff(Operation.EQUAL, commonsuffix));\n\t\t\t}\n\n\t\t\tdiff_cleanupMerge(diffs);\n\t\t\treturn diffs;\n\t\t}\n\n\t\t/**\n\t\t * Find the differences between two texts.  Assumes that the texts do not\n\t\t * have any common prefix or suffix.\n\t\t * @param text1 Old string to be diffed.\n\t\t * @param text2 New string to be diffed.\n\t\t * @param checklines Speedup flag.  If false, then don't run a\n\t\t *     line-level diff first to identify the changed areas.\n\t\t *     If true, then run a faster slightly less optimal diff.\n\t\t * @return List of Diff objects.\n\t\t */\n\t\tprivate List<Diff> diff_compute(string text1, string text2, bool checklines)\n\t\t{\n\t\t\tList<Diff> diffs = new List<Diff>();\n\n\t\t\tif(text1.Length == 0) {\n\t\t\t\t// Just add some text (speedup).\n\t\t\t\tdiffs.Add(new Diff(Operation.INSERT, text2));\n\t\t\t\treturn diffs;\n\t\t\t}\n\n\t\t\tif(text2.Length == 0) {\n\t\t\t\t// Just delete some text (speedup).\n\t\t\t\tdiffs.Add(new Diff(Operation.DELETE, text1));\n\t\t\t\treturn diffs;\n\t\t\t}\n\n\t\t\tstring longtext = text1.Length > text2.Length ? text1 : text2;\n\t\t\tstring shorttext = text1.Length > text2.Length ? text2 : text1;\n\t\t\tint i = longtext.IndexOf(shorttext, StringComparison.Ordinal);\n\t\t\tif(i != -1) {\n\t\t\t\t// Shorter text is inside the longer text (speedup).\n\t\t\t\tOperation op = (text1.Length > text2.Length) ?\n\t\t\t\t\tOperation.DELETE : Operation.INSERT;\n\t\t\t\tdiffs.Add(new Diff(op, longtext.Substring(0, i)));\n\t\t\t\tdiffs.Add(new Diff(Operation.EQUAL, shorttext));\n\t\t\t\tdiffs.Add(new Diff(op, longtext.Substring(i + shorttext.Length)));\n\t\t\t\treturn diffs;\n\t\t\t}\n\n\t\t\tif(shorttext.Length == 1) {\n\t\t\t\t// Single character string.\n\t\t\t\t// After the previous speedup, the character can't be an equality.\n\t\t\t\tdiffs.Add(new Diff(Operation.DELETE, text1));\n\t\t\t\tdiffs.Add(new Diff(Operation.INSERT, text2));\n\t\t\t\treturn diffs;\n\t\t\t}\n\n\t\t\t// Check to see if the problem can be split in two.\n\t\t\tstring[] hm = diff_halfMatch(text1, text2);\n\t\t\tif(hm != null) {\n\t\t\t\t// A half-match was found, sort out the return data.\n\t\t\t\tstring text1_a = hm[0];\n\t\t\t\tstring text1_b = hm[1];\n\t\t\t\tstring text2_a = hm[2];\n\t\t\t\tstring text2_b = hm[3];\n\t\t\t\tstring mid_common = hm[4];\n\t\t\t\t// Send both pairs off for separate processing.\n\t\t\t\tList<Diff> diffs_a = _diff_main(text1_a, text2_a, checklines);\n\t\t\t\tList<Diff> diffs_b = _diff_main(text1_b, text2_b, checklines);\n\t\t\t\t// Merge the results.\n\t\t\t\tdiffs = diffs_a;\n\t\t\t\tdiffs.Add(new Diff(Operation.EQUAL, mid_common));\n\t\t\t\tdiffs.AddRange(diffs_b);\n\t\t\t\treturn diffs;\n\t\t\t}\n\n\t\t\tif(checklines && text1.Length > 100 && text2.Length > 100) {\n\t\t\t\treturn diff_lineMode(text1, text2);\n\t\t\t}\n\n\t\t\treturn diff_bisect(text1, text2);\n\t\t}\n\n\t\t/**\n\t\t * Do a quick line-level diff on both strings, then rediff the parts for\n\t\t * greater accuracy.\n\t\t * This speedup can produce non-minimal diffs.\n\t\t * @param text1 Old string to be diffed.\n\t\t * @param text2 New string to be diffed.\n\t\t * @return List of Diff objects.\n\t\t */\n\t\tprivate List<Diff> diff_lineMode(string text1, string text2)\n\t\t{\n\t\t\t// Scan the text on a line-by-line basis first.\n\t\t\tObject[] a = diff_linesToChars(text1, text2);\n\t\t\ttext1 = (string)a[0];\n\t\t\ttext2 = (string)a[1];\n\t\t\tList<string> linearray = (List<string>)a[2];\n\n\t\t\tList<Diff> diffs = _diff_main(text1, text2, false);\n\n\t\t\t// Convert the diff back to original text.\n\t\t\tdiff_charsToLines(diffs, linearray);\n\t\t\t// Eliminate freak matches (e.g. blank lines)\n\t\t\tdiff_cleanupSemantic(diffs);\n\n\t\t\t// Rediff any replacement blocks, this time character-by-character.\n\t\t\t// Add a dummy entry at the end.\n\t\t\tdiffs.Add(new Diff(Operation.EQUAL, string.Empty));\n\t\t\tint pointer = 0;\n\t\t\tint count_delete = 0;\n\t\t\tint count_insert = 0;\n\t\t\tstring text_delete = string.Empty;\n\t\t\tstring text_insert = string.Empty;\n\t\t\twhile(pointer < diffs.Count) {\n\t\t\t\tswitch(diffs[pointer].operation) {\n\t\t\t\tcase Operation.INSERT:\n\t\t\t\t\tcount_insert++;\n\t\t\t\t\ttext_insert += diffs[pointer].text;\n\t\t\t\t\tbreak;\n\t\t\t\tcase Operation.DELETE:\n\t\t\t\t\tcount_delete++;\n\t\t\t\t\ttext_delete += diffs[pointer].text;\n\t\t\t\t\tbreak;\n\t\t\t\tcase Operation.EQUAL:\n\t\t\t\t\t// Upon reaching an equality, check for prior redundancies.\n\t\t\t\t\tif(count_delete >= 1 && count_insert >= 1) {\n\t\t\t\t\t\t// Delete the offending records and add the merged ones.\n\t\t\t\t\t\tdiffs.RemoveRange(pointer - count_delete - count_insert,\n\t\t\t\t\t\t\tcount_delete + count_insert);\n\t\t\t\t\t\tpointer = pointer - count_delete - count_insert;\n\t\t\t\t\t\tList<Diff> subDiff =\n\t\t\t\t\t\t\tthis._diff_main(text_delete, text_insert, false);\n\t\t\t\t\t\tdiffs.InsertRange(pointer, subDiff);\n\t\t\t\t\t\tpointer = pointer + subDiff.Count;\n\t\t\t\t\t}\n\t\t\t\t\tcount_insert = 0;\n\t\t\t\t\tcount_delete = 0;\n\t\t\t\t\ttext_delete = string.Empty;\n\t\t\t\t\ttext_insert = string.Empty;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tpointer++;\n\t\t\t}\n\t\t\tdiffs.RemoveAt(diffs.Count - 1);  // Remove the dummy entry at the end.\n\n\t\t\treturn diffs;\n\t\t}\n\n\t\t/**\n\t\t * Find the 'middle snake' of a diff, split the problem in two\n\t\t * and return the recursively constructed diff.\n\t\t * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations.\n\t\t * @param text1 Old string to be diffed.\n\t\t * @param text2 New string to be diffed.\n\t\t * @return List of Diff objects.\n\t\t */\n\t\tprotected List<Diff> diff_bisect(string text1, string text2)\n\t\t{\n\t\t\t// Cache the text lengths to prevent multiple calls.\n\t\t\tint text1_length = text1.Length;\n\t\t\tint text2_length = text2.Length;\n\t\t\tint max_d = (text1_length + text2_length + 1) / 2;\n\t\t\tint v_offset = max_d;\n\t\t\tint v_length = 2 * max_d;\n\t\t\tint[] v1 = new int[v_length];\n\t\t\tint[] v2 = new int[v_length];\n\t\t\tfor(int x = 0; x < v_length; x++) {\n\t\t\t\tv1[x] = -1;\n\t\t\t\tv2[x] = -1;\n\t\t\t}\n\t\t\tv1[v_offset + 1] = 0;\n\t\t\tv2[v_offset + 1] = 0;\n\t\t\tint delta = text1_length - text2_length;\n\t\t\t// If the total number of characters is odd, then the front path will\n\t\t\t// collide with the reverse path.\n\t\t\tbool front = (delta % 2 != 0);\n\t\t\t// Offsets for start and end of k loop.\n\t\t\t// Prevents mapping of space beyond the grid.\n\t\t\tint k1start = 0;\n\t\t\tint k1end = 0;\n\t\t\tint k2start = 0;\n\t\t\tint k2end = 0;\n\t\t\tfor(int d = 0; d < max_d; d++) {\n\t\t\t\t// Bail out if deadline is reached.\n\t\t\t\tif(_timeout != 0 && Environment.TickCount - _startTime > _timeout) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\t// Walk the front path one step.\n\t\t\t\tfor(int k1 = -d + k1start; k1 <= d - k1end; k1 += 2) {\n\t\t\t\t\tint k1_offset = v_offset + k1;\n\t\t\t\t\tint x1;\n\t\t\t\t\tif(k1 == -d || k1 != d && v1[k1_offset - 1] < v1[k1_offset + 1]) {\n\t\t\t\t\t\tx1 = v1[k1_offset + 1];\n\t\t\t\t\t} else {\n\t\t\t\t\t\tx1 = v1[k1_offset - 1] + 1;\n\t\t\t\t\t}\n\t\t\t\t\tint y1 = x1 - k1;\n\t\t\t\t\twhile(x1 < text1_length && y1 < text2_length\n\t\t\t\t\t\t  && text1[x1] == text2[y1]) {\n\t\t\t\t\t\tx1++;\n\t\t\t\t\t\ty1++;\n\t\t\t\t\t}\n\t\t\t\t\tv1[k1_offset] = x1;\n\t\t\t\t\tif(x1 > text1_length) {\n\t\t\t\t\t\t// Ran off the right of the graph.\n\t\t\t\t\t\tk1end += 2;\n\t\t\t\t\t} else if(y1 > text2_length) {\n\t\t\t\t\t\t// Ran off the bottom of the graph.\n\t\t\t\t\t\tk1start += 2;\n\t\t\t\t\t} else if(front) {\n\t\t\t\t\t\tint k2_offset = v_offset + delta - k1;\n\t\t\t\t\t\tif(k2_offset >= 0 && k2_offset < v_length && v2[k2_offset] != -1) {\n\t\t\t\t\t\t\t// Mirror x2 onto top-left coordinate system.\n\t\t\t\t\t\t\tint x2 = text1_length - v2[k2_offset];\n\t\t\t\t\t\t\tif(x1 >= x2) {\n\t\t\t\t\t\t\t\t// Overlap detected.\n\t\t\t\t\t\t\t\treturn diff_bisectSplit(text1, text2, x1, y1);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Walk the reverse path one step.\n\t\t\t\tfor(int k2 = -d + k2start; k2 <= d - k2end; k2 += 2) {\n\t\t\t\t\tint k2_offset = v_offset + k2;\n\t\t\t\t\tint x2;\n\t\t\t\t\tif(k2 == -d || k2 != d && v2[k2_offset - 1] < v2[k2_offset + 1]) {\n\t\t\t\t\t\tx2 = v2[k2_offset + 1];\n\t\t\t\t\t} else {\n\t\t\t\t\t\tx2 = v2[k2_offset - 1] + 1;\n\t\t\t\t\t}\n\t\t\t\t\tint y2 = x2 - k2;\n\t\t\t\t\twhile(x2 < text1_length && y2 < text2_length\n\t\t\t\t\t\t&& text1[text1_length - x2 - 1]\n\t\t\t\t\t\t== text2[text2_length - y2 - 1]) {\n\t\t\t\t\t\tx2++;\n\t\t\t\t\t\ty2++;\n\t\t\t\t\t}\n\t\t\t\t\tv2[k2_offset] = x2;\n\t\t\t\t\tif(x2 > text1_length) {\n\t\t\t\t\t\t// Ran off the left of the graph.\n\t\t\t\t\t\tk2end += 2;\n\t\t\t\t\t} else if(y2 > text2_length) {\n\t\t\t\t\t\t// Ran off the top of the graph.\n\t\t\t\t\t\tk2start += 2;\n\t\t\t\t\t} else if(!front) {\n\t\t\t\t\t\tint k1_offset = v_offset + delta - k2;\n\t\t\t\t\t\tif(k1_offset >= 0 && k1_offset < v_length && v1[k1_offset] != -1) {\n\t\t\t\t\t\t\tint x1 = v1[k1_offset];\n\t\t\t\t\t\t\tint y1 = v_offset + x1 - k1_offset;\n\t\t\t\t\t\t\t// Mirror x2 onto top-left coordinate system.\n\t\t\t\t\t\t\tx2 = text1_length - v2[k2_offset];\n\t\t\t\t\t\t\tif(x1 >= x2) {\n\t\t\t\t\t\t\t\t// Overlap detected.\n\t\t\t\t\t\t\t\treturn diff_bisectSplit(text1, text2, x1, y1);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Diff took too long and hit the deadline or\n\t\t\t// number of diffs equals number of characters, no commonality at all.\n\t\t\tList<Diff> diffs = new List<Diff>();\n\t\t\tdiffs.Add(new Diff(Operation.DELETE, text1));\n\t\t\tdiffs.Add(new Diff(Operation.INSERT, text2));\n\t\t\treturn diffs;\n\t\t}\n\n\t\t/**\n\t\t * Given the location of the 'middle snake', split the diff in two parts\n\t\t * and recurse.\n\t\t * @param text1 Old string to be diffed.\n\t\t * @param text2 New string to be diffed.\n\t\t * @param x Index of split point in text1.\n\t\t * @param y Index of split point in text2.\n\t\t * @return LinkedList of Diff objects.\n\t\t */\n\t\tprivate List<Diff> diff_bisectSplit(string text1, string text2, int x, int y)\n\t\t{\n\t\t\tstring text1a = text1.Substring(0, x);\n\t\t\tstring text2a = text2.Substring(0, y);\n\t\t\tstring text1b = text1.Substring(x);\n\t\t\tstring text2b = text2.Substring(y);\n\n\t\t\t// Compute both diffs serially.\n\t\t\tList<Diff> diffs = _diff_main(text1a, text2a, false);\n\t\t\tList<Diff> diffsb = _diff_main(text1b, text2b, false);\n\n\t\t\tdiffs.AddRange(diffsb);\n\t\t\treturn diffs;\n\t\t}\n\n\t\t/**\n\t\t * Split two texts into a list of strings.  Reduce the texts to a string of\n\t\t * hashes where each Unicode character represents one line.\n\t\t * @param text1 First string.\n\t\t * @param text2 Second string.\n\t\t * @return Three element Object array, containing the encoded text1, the\n\t\t *     encoded text2 and the List of unique strings.  The zeroth element\n\t\t *     of the List of unique strings is intentionally blank.\n\t\t */\n\t\tprotected Object[] diff_linesToChars(string text1, string text2)\n\t\t{\n\t\t\tList<string> lineArray = new List<string>();\n\t\t\tDictionary<string, int> lineHash = new Dictionary<string, int>();\n\t\t\t// e.g. linearray[4] == \"Hello\\n\"\n\t\t\t// e.g. linehash.get(\"Hello\\n\") == 4\n\n\t\t\t// \"\\x00\" is a valid character, but various debuggers don't like it.\n\t\t\t// So we'll insert a junk entry to avoid generating a null character.\n\t\t\tlineArray.Add(string.Empty);\n\n\t\t\t// Allocate 2/3rds of the space for text1, the rest for text2.\n\t\t\tstring chars1 = diff_linesToCharsMunge(text1, lineArray, lineHash, 40000);\n\t\t\tstring chars2 = diff_linesToCharsMunge(text2, lineArray, lineHash, 65535);\n\t\t\treturn new Object[] { chars1, chars2, lineArray };\n\t\t}\n\n\t\t/**\n\t\t * Split a text into a list of strings.  Reduce the texts to a string of\n\t\t * hashes where each Unicode character represents one line.\n\t\t * @param text String to encode.\n\t\t * @param lineArray List of unique strings.\n\t\t * @param lineHash Map of strings to indices.\n\t\t * @param maxLines Maximum length of lineArray.\n\t\t * @return Encoded string.\n\t\t */\n\t\tprivate string diff_linesToCharsMunge(string text, List<string> lineArray,\n\t\t\tDictionary<string, int> lineHash, int maxLines)\n\t\t{\n\t\t\tint lineStart = 0;\n\t\t\tint lineEnd = -1;\n\t\t\tstring line;\n\t\t\tStringBuilder chars = new StringBuilder();\n\t\t\t// Walk the text, pulling out a Substring for each line.\n\t\t\t// text.split('\\n') would would temporarily double our memory footprint.\n\t\t\t// Modifying text would create many large strings to garbage collect.\n\t\t\twhile(lineEnd < text.Length - 1) {\n\t\t\t\tlineEnd = text.IndexOf('\\n', lineStart);\n\t\t\t\tif(lineEnd == -1) {\n\t\t\t\t\tlineEnd = text.Length - 1;\n\t\t\t\t}\n\t\t\t\tline = text.JavaSubstring(lineStart, lineEnd + 1);\n\n\t\t\t\tif(lineHash.ContainsKey(line)) {\n\t\t\t\t\tchars.Append(((char)(int)lineHash[line]));\n\t\t\t\t} else {\n\t\t\t\t\tif(lineArray.Count == maxLines) {\n\t\t\t\t\t\t// Bail out at 65535 because char 65536 == char 0.\n\t\t\t\t\t\tline = text.Substring(lineStart);\n\t\t\t\t\t\tlineEnd = text.Length;\n\t\t\t\t\t}\n\t\t\t\t\tlineArray.Add(line);\n\t\t\t\t\tlineHash.Add(line, lineArray.Count - 1);\n\t\t\t\t\tchars.Append(((char)(lineArray.Count - 1)));\n\t\t\t\t}\n\t\t\t\tlineStart = lineEnd + 1;\n\t\t\t}\n\t\t\treturn chars.ToString();\n\t\t}\n\n\t\t/**\n\t\t * Rehydrate the text in a diff from a string of line hashes to real lines\n\t\t * of text.\n\t\t * @param diffs List of Diff objects.\n\t\t * @param lineArray List of unique strings.\n\t\t */\n\t\tprotected void diff_charsToLines(ICollection<Diff> diffs,\n\t\t\t\t\t\tIList<string> lineArray)\n\t\t{\n\t\t\tStringBuilder text;\n\t\t\tforeach(Diff diff in diffs) {\n\t\t\t\ttext = new StringBuilder();\n\t\t\t\tfor(int j = 0; j < diff.text.Length; j++) {\n\t\t\t\t\ttext.Append(lineArray[diff.text[j]]);\n\t\t\t\t}\n\t\t\t\tdiff.text = text.ToString();\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * Determine the common prefix of two strings.\n\t\t * @param text1 First string.\n\t\t * @param text2 Second string.\n\t\t * @return The number of characters common to the start of each string.\n\t\t */\n\t\tpublic static int diff_commonPrefix(string text1, string text2)\n\t\t{\n\t\t\t// Performance analysis: https://neil.fraser.name/news/2007/10/09/\n\t\t\tint n = Math.Min(text1.Length, text2.Length);\n\t\t\tfor(int i = 0; i < n; i++) {\n\t\t\t\tif(text1[i] != text2[i]) {\n\t\t\t\t\treturn i;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn n;\n\t\t}\n\n\t\t/**\n\t\t * Determine the common suffix of two strings.\n\t\t * @param text1 First string.\n\t\t * @param text2 Second string.\n\t\t * @return The number of characters common to the end of each string.\n\t\t */\n\t\tpublic static int diff_commonSuffix(string text1, string text2)\n\t\t{\n\t\t\t// Performance analysis: https://neil.fraser.name/news/2007/10/09/\n\t\t\tint text1_length = text1.Length;\n\t\t\tint text2_length = text2.Length;\n\t\t\tint n = Math.Min(text1.Length, text2.Length);\n\t\t\tfor(int i = 1; i <= n; i++) {\n\t\t\t\tif(text1[text1_length - i] != text2[text2_length - i]) {\n\t\t\t\t\treturn i - 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn n;\n\t\t}\n\n\t\t/**\n\t\t * Determine if the suffix of one string is the prefix of another.\n\t\t * @param text1 First string.\n\t\t * @param text2 Second string.\n\t\t * @return The number of characters common to the end of the first\n\t\t *     string and the start of the second string.\n\t\t */\n\t\tprotected int diff_commonOverlap(string text1, string text2)\n\t\t{\n\t\t\t// Cache the text lengths to prevent multiple calls.\n\t\t\tint text1_length = text1.Length;\n\t\t\tint text2_length = text2.Length;\n\t\t\t// Eliminate the null case.\n\t\t\tif(text1_length == 0 || text2_length == 0) {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\t// Truncate the longer string.\n\t\t\tif(text1_length > text2_length) {\n\t\t\t\ttext1 = text1.Substring(text1_length - text2_length);\n\t\t\t} else if(text1_length < text2_length) {\n\t\t\t\ttext2 = text2.Substring(0, text1_length);\n\t\t\t}\n\t\t\tint text_length = Math.Min(text1_length, text2_length);\n\t\t\t// Quick check for the worst case.\n\t\t\tif(text1 == text2) {\n\t\t\t\treturn text_length;\n\t\t\t}\n\n\t\t\t// Start by looking for a single character match\n\t\t\t// and increase length until no match is found.\n\t\t\t// Performance analysis: https://neil.fraser.name/news/2010/11/04/\n\t\t\tint best = 0;\n\t\t\tint length = 1;\n\t\t\twhile(true) {\n\t\t\t\tstring pattern = text1.Substring(text_length - length);\n\t\t\t\tint found = text2.IndexOf(pattern, StringComparison.Ordinal);\n\t\t\t\tif(found == -1) {\n\t\t\t\t\treturn best;\n\t\t\t\t}\n\t\t\t\tlength += found;\n\t\t\t\tif(found == 0 || text1.Substring(text_length - length) ==\n\t\t\t\t\ttext2.Substring(0, length)) {\n\t\t\t\t\tbest = length;\n\t\t\t\t\tlength++;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * Do the two texts share a Substring which is at least half the length of\n\t\t * the longer text?\n\t\t * This speedup can produce non-minimal diffs.\n\t\t * @param text1 First string.\n\t\t * @param text2 Second string.\n\t\t * @return Five element String array, containing the prefix of text1, the\n\t\t *     suffix of text1, the prefix of text2, the suffix of text2 and the\n\t\t *     common middle.  Or null if there was no match.\n\t\t */\n\n\t\tprotected string[] diff_halfMatch(string text1, string text2)\n\t\t{\n\t\t\tif(this.Diff_Timeout <= 0) {\n\t\t\t\t// Don't risk returning a non-optimal diff if we have unlimited time.\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tstring longtext = text1.Length > text2.Length ? text1 : text2;\n\t\t\tstring shorttext = text1.Length > text2.Length ? text2 : text1;\n\t\t\tif(longtext.Length < 4 || shorttext.Length * 2 < longtext.Length) {\n\t\t\t\treturn null;  // Pointless.\n\t\t\t}\n\n\t\t\t// First check if the second quarter is the seed for a half-match.\n\t\t\tstring[] hm1 = diff_halfMatchI(longtext, shorttext,\n\t\t\t\t\t\t\t\t\t\t   (longtext.Length + 3) / 4);\n\t\t\t// Check again based on the third quarter.\n\t\t\tstring[] hm2 = diff_halfMatchI(longtext, shorttext,\n\t\t\t\t\t\t\t\t\t\t   (longtext.Length + 1) / 2);\n\t\t\tstring[] hm;\n\t\t\tif(hm1 == null && hm2 == null) {\n\t\t\t\treturn null;\n\t\t\t} else if(hm2 == null) {\n\t\t\t\thm = hm1;\n\t\t\t} else if(hm1 == null) {\n\t\t\t\thm = hm2;\n\t\t\t} else {\n\t\t\t\t// Both matched.  Select the longest.\n\t\t\t\thm = hm1[4].Length > hm2[4].Length ? hm1 : hm2;\n\t\t\t}\n\n\t\t\t// A half-match was found, sort out the return data.\n\t\t\tif(text1.Length > text2.Length) {\n\t\t\t\treturn hm;\n\t\t\t\t//return new string[]{hm[0], hm[1], hm[2], hm[3], hm[4]};\n\t\t\t} else {\n\t\t\t\treturn new string[] { hm[2], hm[3], hm[0], hm[1], hm[4] };\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * Does a Substring of shorttext exist within longtext such that the\n\t\t * Substring is at least half the length of longtext?\n\t\t * @param longtext Longer string.\n\t\t * @param shorttext Shorter string.\n\t\t * @param i Start index of quarter length Substring within longtext.\n\t\t * @return Five element string array, containing the prefix of longtext, the\n\t\t *     suffix of longtext, the prefix of shorttext, the suffix of shorttext\n\t\t *     and the common middle.  Or null if there was no match.\n\t\t */\n\t\tprivate string[] diff_halfMatchI(string longtext, string shorttext, int i)\n\t\t{\n\t\t\t// Start with a 1/4 length Substring at position i as a seed.\n\t\t\tstring seed = longtext.Substring(i, longtext.Length / 4);\n\t\t\tint j = -1;\n\t\t\tstring best_common = string.Empty;\n\t\t\tstring best_longtext_a = string.Empty, best_longtext_b = string.Empty;\n\t\t\tstring best_shorttext_a = string.Empty, best_shorttext_b = string.Empty;\n\t\t\twhile(j < shorttext.Length && (j = shorttext.IndexOf(seed, j + 1,\n\t\t\t\tStringComparison.Ordinal)) != -1) {\n\t\t\t\tint prefixLength = diff_commonPrefix(longtext.Substring(i),\n\t\t\t\t\t\t\t\t\t\t\t\t\t shorttext.Substring(j));\n\t\t\t\tint suffixLength = diff_commonSuffix(longtext.Substring(0, i),\n\t\t\t\t\t\t\t\t\t\t\t\t\t shorttext.Substring(0, j));\n\t\t\t\tif(best_common.Length < suffixLength + prefixLength) {\n\t\t\t\t\tbest_common = shorttext.Substring(j - suffixLength, suffixLength)\n\t\t\t\t\t\t+ shorttext.Substring(j, prefixLength);\n\t\t\t\t\tbest_longtext_a = longtext.Substring(0, i - suffixLength);\n\t\t\t\t\tbest_longtext_b = longtext.Substring(i + prefixLength);\n\t\t\t\t\tbest_shorttext_a = shorttext.Substring(0, j - suffixLength);\n\t\t\t\t\tbest_shorttext_b = shorttext.Substring(j + prefixLength);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(best_common.Length * 2 >= longtext.Length) {\n\t\t\t\treturn new string[]{best_longtext_a, best_longtext_b,\n\t\t\tbest_shorttext_a, best_shorttext_b, best_common};\n\t\t\t} else {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * Reduce the number of edits by eliminating semantically trivial\n\t\t * equalities.\n\t\t * @param diffs List of Diff objects.\n\t\t */\n\t\tpublic void diff_cleanupSemantic(List<Diff> diffs)\n\t\t{\n\t\t\tbool changes = false;\n\t\t\t// Stack of indices where equalities are found.\n\t\t\tStack<int> equalities = new Stack<int>();\n\t\t\t// Always equal to equalities[equalitiesLength-1][1]\n\t\t\tstring lastEquality = null;\n\t\t\tint pointer = 0;  // Index of current position.\n\t\t\t\t\t\t\t  // Number of characters that changed prior to the equality.\n\t\t\tint length_insertions1 = 0;\n\t\t\tint length_deletions1 = 0;\n\t\t\t// Number of characters that changed after the equality.\n\t\t\tint length_insertions2 = 0;\n\t\t\tint length_deletions2 = 0;\n\t\t\twhile(pointer < diffs.Count) {\n\t\t\t\tif(diffs[pointer].operation == Operation.EQUAL) {  // Equality found.\n\t\t\t\t\tequalities.Push(pointer);\n\t\t\t\t\tlength_insertions1 = length_insertions2;\n\t\t\t\t\tlength_deletions1 = length_deletions2;\n\t\t\t\t\tlength_insertions2 = 0;\n\t\t\t\t\tlength_deletions2 = 0;\n\t\t\t\t\tlastEquality = diffs[pointer].text;\n\t\t\t\t} else {  // an insertion or deletion\n\t\t\t\t\tif(diffs[pointer].operation == Operation.INSERT) {\n\t\t\t\t\t\tlength_insertions2 += diffs[pointer].text.Length;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tlength_deletions2 += diffs[pointer].text.Length;\n\t\t\t\t\t}\n\t\t\t\t\t// Eliminate an equality that is smaller or equal to the edits on both\n\t\t\t\t\t// sides of it.\n\t\t\t\t\tif(lastEquality != null && (lastEquality.Length\n\t\t\t\t\t\t<= Math.Max(length_insertions1, length_deletions1))\n\t\t\t\t\t\t&& (lastEquality.Length\n\t\t\t\t\t\t\t<= Math.Max(length_insertions2, length_deletions2))) {\n\t\t\t\t\t\t// Duplicate record.\n\t\t\t\t\t\tdiffs.Insert(equalities.Peek(),\n\t\t\t\t\t\t\t\t\t new Diff(Operation.DELETE, lastEquality));\n\t\t\t\t\t\t// Change second copy to insert.\n\t\t\t\t\t\tdiffs[equalities.Peek() + 1].operation = Operation.INSERT;\n\t\t\t\t\t\t// Throw away the equality we just deleted.\n\t\t\t\t\t\tequalities.Pop();\n\t\t\t\t\t\tif(equalities.Count > 0) {\n\t\t\t\t\t\t\tequalities.Pop();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tpointer = equalities.Count > 0 ? equalities.Peek() : -1;\n\t\t\t\t\t\tlength_insertions1 = 0;  // Reset the counters.\n\t\t\t\t\t\tlength_deletions1 = 0;\n\t\t\t\t\t\tlength_insertions2 = 0;\n\t\t\t\t\t\tlength_deletions2 = 0;\n\t\t\t\t\t\tlastEquality = null;\n\t\t\t\t\t\tchanges = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tpointer++;\n\t\t\t}\n\n\t\t\t// Normalize the diff.\n\t\t\tif(changes) {\n\t\t\t\tdiff_cleanupMerge(diffs);\n\t\t\t}\n\t\t\tdiff_cleanupSemanticLossless(diffs);\n\n\t\t\t// Find any overlaps between deletions and insertions.\n\t\t\t// e.g: <del>abcxxx</del><ins>xxxdef</ins>\n\t\t\t//   -> <del>abc</del>xxx<ins>def</ins>\n\t\t\t// e.g: <del>xxxabc</del><ins>defxxx</ins>\n\t\t\t//   -> <ins>def</ins>xxx<del>abc</del>\n\t\t\t// Only extract an overlap if it is as big as the edit ahead or behind it.\n\t\t\tpointer = 1;\n\t\t\twhile(pointer < diffs.Count) {\n\t\t\t\tif(diffs[pointer - 1].operation == Operation.DELETE &&\n\t\t\t\t\tdiffs[pointer].operation == Operation.INSERT) {\n\t\t\t\t\tstring deletion = diffs[pointer - 1].text;\n\t\t\t\t\tstring insertion = diffs[pointer].text;\n\t\t\t\t\tint overlap_length1 = diff_commonOverlap(deletion, insertion);\n\t\t\t\t\tint overlap_length2 = diff_commonOverlap(insertion, deletion);\n\t\t\t\t\tif(overlap_length1 >= overlap_length2) {\n\t\t\t\t\t\tif(overlap_length1 >= deletion.Length / 2.0 ||\n\t\t\t\t\t\t\toverlap_length1 >= insertion.Length / 2.0) {\n\t\t\t\t\t\t\t// Overlap found.\n\t\t\t\t\t\t\t// Insert an equality and trim the surrounding edits.\n\t\t\t\t\t\t\tdiffs.Insert(pointer, new Diff(Operation.EQUAL,\n\t\t\t\t\t\t\t\tinsertion.Substring(0, overlap_length1)));\n\t\t\t\t\t\t\tdiffs[pointer - 1].text =\n\t\t\t\t\t\t\t\tdeletion.Substring(0, deletion.Length - overlap_length1);\n\t\t\t\t\t\t\tdiffs[pointer + 1].text = insertion.Substring(overlap_length1);\n\t\t\t\t\t\t\tpointer++;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif(overlap_length2 >= deletion.Length / 2.0 ||\n\t\t\t\t\t\t\toverlap_length2 >= insertion.Length / 2.0) {\n\t\t\t\t\t\t\t// Reverse overlap found.\n\t\t\t\t\t\t\t// Insert an equality and swap and trim the surrounding edits.\n\t\t\t\t\t\t\tdiffs.Insert(pointer, new Diff(Operation.EQUAL,\n\t\t\t\t\t\t\t\tdeletion.Substring(0, overlap_length2)));\n\t\t\t\t\t\t\tdiffs[pointer - 1].operation = Operation.INSERT;\n\t\t\t\t\t\t\tdiffs[pointer - 1].text =\n\t\t\t\t\t\t\t\tinsertion.Substring(0, insertion.Length - overlap_length2);\n\t\t\t\t\t\t\tdiffs[pointer + 1].operation = Operation.DELETE;\n\t\t\t\t\t\t\tdiffs[pointer + 1].text = deletion.Substring(overlap_length2);\n\t\t\t\t\t\t\tpointer++;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tpointer++;\n\t\t\t\t}\n\t\t\t\tpointer++;\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * Look for single edits surrounded on both sides by equalities\n\t\t * which can be shifted sideways to align the edit to a word boundary.\n\t\t * e.g: The c<ins>at c</ins>ame. -> The <ins>cat </ins>came.\n\t\t * @param diffs List of Diff objects.\n\t\t */\n\t\tpublic void diff_cleanupSemanticLossless(List<Diff> diffs)\n\t\t{\n\t\t\tint pointer = 1;\n\t\t\t// Intentionally ignore the first and last element (don't need checking).\n\t\t\twhile(pointer < diffs.Count - 1) {\n\t\t\t\tif(diffs[pointer - 1].operation == Operation.EQUAL &&\n\t\t\t\t  diffs[pointer + 1].operation == Operation.EQUAL) {\n\t\t\t\t\t// This is a single edit surrounded by equalities.\n\t\t\t\t\tstring equality1 = diffs[pointer - 1].text;\n\t\t\t\t\tstring edit = diffs[pointer].text;\n\t\t\t\t\tstring equality2 = diffs[pointer + 1].text;\n\n\t\t\t\t\t// First, shift the edit as far left as possible.\n\t\t\t\t\tint commonOffset = diff_commonSuffix(equality1, edit);\n\t\t\t\t\tif(commonOffset > 0) {\n\t\t\t\t\t\tstring commonString = edit.Substring(edit.Length - commonOffset);\n\t\t\t\t\t\tequality1 = equality1.Substring(0, equality1.Length - commonOffset);\n\t\t\t\t\t\tedit = commonString + edit.Substring(0, edit.Length - commonOffset);\n\t\t\t\t\t\tequality2 = commonString + equality2;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Second, step character by character right,\n\t\t\t\t\t// looking for the best fit.\n\t\t\t\t\tstring bestEquality1 = equality1;\n\t\t\t\t\tstring bestEdit = edit;\n\t\t\t\t\tstring bestEquality2 = equality2;\n\t\t\t\t\tint bestScore = diff_cleanupSemanticScore(equality1, edit) +\n\t\t\t\t\t\tdiff_cleanupSemanticScore(edit, equality2);\n\t\t\t\t\twhile(edit.Length != 0 && equality2.Length != 0\n\t\t\t\t\t\t&& edit[0] == equality2[0]) {\n\t\t\t\t\t\tequality1 += edit[0];\n\t\t\t\t\t\tedit = edit.Substring(1) + equality2[0];\n\t\t\t\t\t\tequality2 = equality2.Substring(1);\n\t\t\t\t\t\tint score = diff_cleanupSemanticScore(equality1, edit) +\n\t\t\t\t\t\t\tdiff_cleanupSemanticScore(edit, equality2);\n\t\t\t\t\t\t// The >= encourages trailing rather than leading whitespace on\n\t\t\t\t\t\t// edits.\n\t\t\t\t\t\tif(score >= bestScore) {\n\t\t\t\t\t\t\tbestScore = score;\n\t\t\t\t\t\t\tbestEquality1 = equality1;\n\t\t\t\t\t\t\tbestEdit = edit;\n\t\t\t\t\t\t\tbestEquality2 = equality2;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif(diffs[pointer - 1].text != bestEquality1) {\n\t\t\t\t\t\t// We have an improvement, save it back to the diff.\n\t\t\t\t\t\tif(bestEquality1.Length != 0) {\n\t\t\t\t\t\t\tdiffs[pointer - 1].text = bestEquality1;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tdiffs.RemoveAt(pointer - 1);\n\t\t\t\t\t\t\tpointer--;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tdiffs[pointer].text = bestEdit;\n\t\t\t\t\t\tif(bestEquality2.Length != 0) {\n\t\t\t\t\t\t\tdiffs[pointer + 1].text = bestEquality2;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tdiffs.RemoveAt(pointer + 1);\n\t\t\t\t\t\t\tpointer--;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tpointer++;\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * Given two strings, compute a score representing whether the internal\n\t\t * boundary falls on logical boundaries.\n\t\t * Scores range from 6 (best) to 0 (worst).\n\t\t * @param one First string.\n\t\t * @param two Second string.\n\t\t * @return The score.\n\t\t */\n\t\tprivate int diff_cleanupSemanticScore(string one, string two)\n\t\t{\n\t\t\tif(one.Length == 0 || two.Length == 0) {\n\t\t\t\t// Edges are the best.\n\t\t\t\treturn 6;\n\t\t\t}\n\n\t\t\t// Each port of this function behaves slightly differently due to\n\t\t\t// subtle differences in each language's definition of things like\n\t\t\t// 'whitespace'.  Since this function's purpose is largely cosmetic,\n\t\t\t// the choice has been made to use each language's native features\n\t\t\t// rather than force total conformity.\n\t\t\tchar char1 = one[one.Length - 1];\n\t\t\tchar char2 = two[0];\n\t\t\tbool nonAlphaNumeric1 = !Char.IsLetterOrDigit(char1);\n\t\t\tbool nonAlphaNumeric2 = !Char.IsLetterOrDigit(char2);\n\t\t\tbool whitespace1 = nonAlphaNumeric1 && Char.IsWhiteSpace(char1);\n\t\t\tbool whitespace2 = nonAlphaNumeric2 && Char.IsWhiteSpace(char2);\n\t\t\tbool lineBreak1 = whitespace1 && Char.IsControl(char1);\n\t\t\tbool lineBreak2 = whitespace2 && Char.IsControl(char2);\n\t\t\tbool blankLine1 = lineBreak1 && BLANKLINEEND.IsMatch(one);\n\t\t\tbool blankLine2 = lineBreak2 && BLANKLINESTART.IsMatch(two);\n\n\t\t\tif(blankLine1 || blankLine2) {\n\t\t\t\t// Five points for blank lines.\n\t\t\t\treturn 5;\n\t\t\t} else if(lineBreak1 || lineBreak2) {\n\t\t\t\t// Four points for line breaks.\n\t\t\t\treturn 4;\n\t\t\t} else if(nonAlphaNumeric1 && !whitespace1 && whitespace2) {\n\t\t\t\t// Three points for end of sentences.\n\t\t\t\treturn 3;\n\t\t\t} else if(whitespace1 || whitespace2) {\n\t\t\t\t// Two points for whitespace.\n\t\t\t\treturn 2;\n\t\t\t} else if(nonAlphaNumeric1 || nonAlphaNumeric2) {\n\t\t\t\t// One point for non-alphanumeric.\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\treturn 0;\n\t\t}\n\n\t\t// Define some regex patterns for matching boundaries.\n\t\tprivate Regex BLANKLINEEND = new Regex(\"\\\\n\\\\r?\\\\n\\\\Z\");\n\t\tprivate Regex BLANKLINESTART = new Regex(\"\\\\A\\\\r?\\\\n\\\\r?\\\\n\");\n\n\t\t/**\n\t\t * Reduce the number of edits by eliminating operationally trivial\n\t\t * equalities.\n\t\t * @param diffs List of Diff objects.\n\t\t */\n\t\tpublic void diff_cleanupEfficiency(List<Diff> diffs)\n\t\t{\n\t\t\tbool changes = false;\n\t\t\t// Stack of indices where equalities are found.\n\t\t\tStack<int> equalities = new Stack<int>();\n\t\t\t// Always equal to equalities[equalitiesLength-1][1]\n\t\t\tstring lastEquality = string.Empty;\n\t\t\tint pointer = 0;  // Index of current position.\n\t\t\t\t\t\t\t  // Is there an insertion operation before the last equality.\n\t\t\tbool pre_ins = false;\n\t\t\t// Is there a deletion operation before the last equality.\n\t\t\tbool pre_del = false;\n\t\t\t// Is there an insertion operation after the last equality.\n\t\t\tbool post_ins = false;\n\t\t\t// Is there a deletion operation after the last equality.\n\t\t\tbool post_del = false;\n\t\t\twhile(pointer < diffs.Count) {\n\t\t\t\tif(diffs[pointer].operation == Operation.EQUAL) {  // Equality found.\n\t\t\t\t\tif(diffs[pointer].text.Length < this.Diff_EditCost\n\t\t\t\t\t\t&& (post_ins || post_del)) {\n\t\t\t\t\t\t// Candidate found.\n\t\t\t\t\t\tequalities.Push(pointer);\n\t\t\t\t\t\tpre_ins = post_ins;\n\t\t\t\t\t\tpre_del = post_del;\n\t\t\t\t\t\tlastEquality = diffs[pointer].text;\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Not a candidate, and can never become one.\n\t\t\t\t\t\tequalities.Clear();\n\t\t\t\t\t\tlastEquality = string.Empty;\n\t\t\t\t\t}\n\t\t\t\t\tpost_ins = post_del = false;\n\t\t\t\t} else {  // An insertion or deletion.\n\t\t\t\t\tif(diffs[pointer].operation == Operation.DELETE) {\n\t\t\t\t\t\tpost_del = true;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tpost_ins = true;\n\t\t\t\t\t}\n\t\t\t\t\t/*\n\t\t\t\t\t * Five types to be split:\n\t\t\t\t\t * <ins>A</ins><del>B</del>XY<ins>C</ins><del>D</del>\n\t\t\t\t\t * <ins>A</ins>X<ins>C</ins><del>D</del>\n\t\t\t\t\t * <ins>A</ins><del>B</del>X<ins>C</ins>\n\t\t\t\t\t * <ins>A</del>X<ins>C</ins><del>D</del>\n\t\t\t\t\t * <ins>A</ins><del>B</del>X<del>C</del>\n\t\t\t\t\t */\n\t\t\t\t\tif((lastEquality.Length != 0)\n\t\t\t\t\t\t&& ((pre_ins && pre_del && post_ins && post_del)\n\t\t\t\t\t\t|| ((lastEquality.Length < this.Diff_EditCost / 2)\n\t\t\t\t\t\t&& ((pre_ins ? 1 : 0) + (pre_del ? 1 : 0) + (post_ins ? 1 : 0)\n\t\t\t\t\t\t+ (post_del ? 1 : 0)) == 3))) {\n\t\t\t\t\t\t// Duplicate record.\n\t\t\t\t\t\tdiffs.Insert(equalities.Peek(),\n\t\t\t\t\t\t\t\t\t new Diff(Operation.DELETE, lastEquality));\n\t\t\t\t\t\t// Change second copy to insert.\n\t\t\t\t\t\tdiffs[equalities.Peek() + 1].operation = Operation.INSERT;\n\t\t\t\t\t\tequalities.Pop();  // Throw away the equality we just deleted.\n\t\t\t\t\t\tlastEquality = string.Empty;\n\t\t\t\t\t\tif(pre_ins && pre_del) {\n\t\t\t\t\t\t\t// No changes made which could affect previous entry, keep going.\n\t\t\t\t\t\t\tpost_ins = post_del = true;\n\t\t\t\t\t\t\tequalities.Clear();\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tif(equalities.Count > 0) {\n\t\t\t\t\t\t\t\tequalities.Pop();\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tpointer = equalities.Count > 0 ? equalities.Peek() : -1;\n\t\t\t\t\t\t\tpost_ins = post_del = false;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tchanges = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tpointer++;\n\t\t\t}\n\n\t\t\tif(changes) {\n\t\t\t\tdiff_cleanupMerge(diffs);\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * Reorder and merge like edit sections.  Merge equalities.\n\t\t * Any edit section can move as long as it doesn't cross an equality.\n\t\t * @param diffs List of Diff objects.\n\t\t */\n\t\tpublic void diff_cleanupMerge(List<Diff> diffs)\n\t\t{\n\t\t\t// Add a dummy entry at the end.\n\t\t\tdiffs.Add(new Diff(Operation.EQUAL, string.Empty));\n\t\t\tint pointer = 0;\n\t\t\tint count_delete = 0;\n\t\t\tint count_insert = 0;\n\t\t\tstring text_delete = string.Empty;\n\t\t\tstring text_insert = string.Empty;\n\t\t\tint commonlength;\n\t\t\twhile(pointer < diffs.Count) {\n\t\t\t\tswitch(diffs[pointer].operation) {\n\t\t\t\tcase Operation.INSERT:\n\t\t\t\t\tcount_insert++;\n\t\t\t\t\ttext_insert += diffs[pointer].text;\n\t\t\t\t\tpointer++;\n\t\t\t\t\tbreak;\n\t\t\t\tcase Operation.DELETE:\n\t\t\t\t\tcount_delete++;\n\t\t\t\t\ttext_delete += diffs[pointer].text;\n\t\t\t\t\tpointer++;\n\t\t\t\t\tbreak;\n\t\t\t\tcase Operation.EQUAL:\n\t\t\t\t\t// Upon reaching an equality, check for prior redundancies.\n\t\t\t\t\tif(count_delete + count_insert > 1) {\n\t\t\t\t\t\tif(count_delete != 0 && count_insert != 0) {\n\t\t\t\t\t\t\t// Factor out any common prefixies.\n\t\t\t\t\t\t\tcommonlength = diff_commonPrefix(text_insert, text_delete);\n\t\t\t\t\t\t\tif(commonlength != 0) {\n\t\t\t\t\t\t\t\tif((pointer - count_delete - count_insert) > 0 &&\n\t\t\t\t\t\t\t\t  diffs[pointer - count_delete - count_insert - 1].operation\n\t\t\t\t\t\t\t\t\t  == Operation.EQUAL) {\n\t\t\t\t\t\t\t\t\tdiffs[pointer - count_delete - count_insert - 1].text\n\t\t\t\t\t\t\t\t\t\t+= text_insert.Substring(0, commonlength);\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tdiffs.Insert(0, new Diff(Operation.EQUAL,\n\t\t\t\t\t\t\t\t\t\ttext_insert.Substring(0, commonlength)));\n\t\t\t\t\t\t\t\t\tpointer++;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\ttext_insert = text_insert.Substring(commonlength);\n\t\t\t\t\t\t\t\ttext_delete = text_delete.Substring(commonlength);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// Factor out any common suffixies.\n\t\t\t\t\t\t\tcommonlength = diff_commonSuffix(text_insert, text_delete);\n\t\t\t\t\t\t\tif(commonlength != 0) {\n\t\t\t\t\t\t\t\tdiffs[pointer].text = text_insert.Substring(text_insert.Length\n\t\t\t\t\t\t\t\t\t- commonlength) + diffs[pointer].text;\n\t\t\t\t\t\t\t\ttext_insert = text_insert.Substring(0, text_insert.Length\n\t\t\t\t\t\t\t\t\t- commonlength);\n\t\t\t\t\t\t\t\ttext_delete = text_delete.Substring(0, text_delete.Length\n\t\t\t\t\t\t\t\t\t- commonlength);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Delete the offending records and add the merged ones.\n\t\t\t\t\t\tpointer -= count_delete + count_insert;\n\t\t\t\t\t\tdiffs.Splice(pointer, count_delete + count_insert);\n\t\t\t\t\t\tif(text_delete.Length != 0) {\n\t\t\t\t\t\t\tdiffs.Splice(pointer, 0,\n\t\t\t\t\t\t\t\tnew Diff(Operation.DELETE, text_delete));\n\t\t\t\t\t\t\tpointer++;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif(text_insert.Length != 0) {\n\t\t\t\t\t\t\tdiffs.Splice(pointer, 0,\n\t\t\t\t\t\t\t\tnew Diff(Operation.INSERT, text_insert));\n\t\t\t\t\t\t\tpointer++;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tpointer++;\n\t\t\t\t\t} else if(pointer != 0\n\t\t\t\t\t\t&& diffs[pointer - 1].operation == Operation.EQUAL) {\n\t\t\t\t\t\t// Merge this equality with the previous one.\n\t\t\t\t\t\tdiffs[pointer - 1].text += diffs[pointer].text;\n\t\t\t\t\t\tdiffs.RemoveAt(pointer);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tpointer++;\n\t\t\t\t\t}\n\t\t\t\t\tcount_insert = 0;\n\t\t\t\t\tcount_delete = 0;\n\t\t\t\t\ttext_delete = string.Empty;\n\t\t\t\t\ttext_insert = string.Empty;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(diffs[diffs.Count - 1].text.Length == 0) {\n\t\t\t\tdiffs.RemoveAt(diffs.Count - 1);  // Remove the dummy entry at the end.\n\t\t\t}\n\n\t\t\t// Second pass: look for single edits surrounded on both sides by\n\t\t\t// equalities which can be shifted sideways to eliminate an equality.\n\t\t\t// e.g: A<ins>BA</ins>C -> <ins>AB</ins>AC\n\t\t\tbool changes = false;\n\t\t\tpointer = 1;\n\t\t\t// Intentionally ignore the first and last element (don't need checking).\n\t\t\twhile(pointer < (diffs.Count - 1)) {\n\t\t\t\tif(diffs[pointer - 1].operation == Operation.EQUAL &&\n\t\t\t\t  diffs[pointer + 1].operation == Operation.EQUAL) {\n\t\t\t\t\t// This is a single edit surrounded by equalities.\n\t\t\t\t\tif(diffs[pointer].text.EndsWith(diffs[pointer - 1].text,\n\t\t\t\t\t\tStringComparison.Ordinal)) {\n\t\t\t\t\t\t// Shift the edit over the previous equality.\n\t\t\t\t\t\tdiffs[pointer].text = diffs[pointer - 1].text +\n\t\t\t\t\t\t\tdiffs[pointer].text.Substring(0, diffs[pointer].text.Length -\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  diffs[pointer - 1].text.Length);\n\t\t\t\t\t\tdiffs[pointer + 1].text = diffs[pointer - 1].text\n\t\t\t\t\t\t\t+ diffs[pointer + 1].text;\n\t\t\t\t\t\tdiffs.Splice(pointer - 1, 1);\n\t\t\t\t\t\tchanges = true;\n\t\t\t\t\t} else if(diffs[pointer].text.StartsWith(diffs[pointer + 1].text,\n\t\t\t\t\t\tStringComparison.Ordinal)) {\n\t\t\t\t\t\t// Shift the edit over the next equality.\n\t\t\t\t\t\tdiffs[pointer - 1].text += diffs[pointer + 1].text;\n\t\t\t\t\t\tdiffs[pointer].text =\n\t\t\t\t\t\t\tdiffs[pointer].text.Substring(diffs[pointer + 1].text.Length)\n\t\t\t\t\t\t\t+ diffs[pointer + 1].text;\n\t\t\t\t\t\tdiffs.Splice(pointer + 1, 1);\n\t\t\t\t\t\tchanges = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tpointer++;\n\t\t\t}\n\t\t\t// If shifts were made, the diff needs reordering and another shift sweep.\n\t\t\tif(changes) {\n\t\t\t\tthis.diff_cleanupMerge(diffs);\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * loc is a location in text1, compute and return the equivalent location in\n\t\t * text2.\n\t\t * e.g. \"The cat\" vs \"The big cat\", 1->1, 5->8\n\t\t * @param diffs List of Diff objects.\n\t\t * @param loc Location within text1.\n\t\t * @return Location within text2.\n\t\t */\n\t\tpublic int diff_xIndex(List<Diff> diffs, int loc)\n\t\t{\n\t\t\tint chars1 = 0;\n\t\t\tint chars2 = 0;\n\t\t\tint last_chars1 = 0;\n\t\t\tint last_chars2 = 0;\n\t\t\tDiff lastDiff = null;\n\t\t\tforeach(Diff aDiff in diffs) {\n\t\t\t\tif(aDiff.operation != Operation.INSERT) {\n\t\t\t\t\t// Equality or deletion.\n\t\t\t\t\tchars1 += aDiff.text.Length;\n\t\t\t\t}\n\t\t\t\tif(aDiff.operation != Operation.DELETE) {\n\t\t\t\t\t// Equality or insertion.\n\t\t\t\t\tchars2 += aDiff.text.Length;\n\t\t\t\t}\n\t\t\t\tif(chars1 > loc) {\n\t\t\t\t\t// Overshot the location.\n\t\t\t\t\tlastDiff = aDiff;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tlast_chars1 = chars1;\n\t\t\t\tlast_chars2 = chars2;\n\t\t\t}\n\t\t\tif(lastDiff != null && lastDiff.operation == Operation.DELETE) {\n\t\t\t\t// The location was deleted.\n\t\t\t\treturn last_chars2;\n\t\t\t}\n\t\t\t// Add the remaining character length.\n\t\t\treturn last_chars2 + (loc - last_chars1);\n\t\t}\n\n\t\t/**\n\t\t * Convert a Diff list into a pretty HTML report.\n\t\t * @param diffs List of Diff objects.\n\t\t * @return HTML representation.\n\t\t */\n\t\tpublic string diff_prettyHtml(List<Diff> diffs)\n\t\t{\n\t\t\tStringBuilder html = new StringBuilder();\n\t\t\tforeach(Diff aDiff in diffs) {\n\t\t\t\tstring text = aDiff.text.Replace(\"&\", \"&amp;\").Replace(\"<\", \"&lt;\")\n\t\t\t\t  .Replace(\">\", \"&gt;\").Replace(\"\\n\", \"&para;<br>\");\n\t\t\t\tswitch(aDiff.operation) {\n\t\t\t\tcase Operation.INSERT:\n\t\t\t\t\thtml.Append(\"<ins style=\\\"background:#e6ffe6;\\\">\").Append(text)\n\t\t\t\t\t\t.Append(\"</ins>\");\n\t\t\t\t\tbreak;\n\t\t\t\tcase Operation.DELETE:\n\t\t\t\t\thtml.Append(\"<del style=\\\"background:#ffe6e6;\\\">\").Append(text)\n\t\t\t\t\t\t.Append(\"</del>\");\n\t\t\t\t\tbreak;\n\t\t\t\tcase Operation.EQUAL:\n\t\t\t\t\thtml.Append(\"<span>\").Append(text).Append(\"</span>\");\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn html.ToString();\n\t\t}\n\n\t\t/**\n\t\t * Compute and return the source text (all equalities and deletions).\n\t\t * @param diffs List of Diff objects.\n\t\t * @return Source text.\n\t\t */\n\t\tpublic string diff_text1(List<Diff> diffs)\n\t\t{\n\t\t\tStringBuilder text = new StringBuilder();\n\t\t\tforeach(Diff aDiff in diffs) {\n\t\t\t\tif(aDiff.operation != Operation.INSERT) {\n\t\t\t\t\ttext.Append(aDiff.text);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn text.ToString();\n\t\t}\n\n\t\t/**\n\t\t * Compute and return the destination text (all equalities and insertions).\n\t\t * @param diffs List of Diff objects.\n\t\t * @return Destination text.\n\t\t */\n\t\tpublic string diff_text2(List<Diff> diffs)\n\t\t{\n\t\t\tStringBuilder text = new StringBuilder();\n\t\t\tforeach(Diff aDiff in diffs) {\n\t\t\t\tif(aDiff.operation != Operation.DELETE) {\n\t\t\t\t\ttext.Append(aDiff.text);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn text.ToString();\n\t\t}\n\n\t\t/**\n\t\t * Compute the Levenshtein distance; the number of inserted, deleted or\n\t\t * substituted characters.\n\t\t * @param diffs List of Diff objects.\n\t\t * @return Number of changes.\n\t\t */\n\t\tpublic int diff_levenshtein(List<Diff> diffs)\n\t\t{\n\t\t\tint levenshtein = 0;\n\t\t\tint insertions = 0;\n\t\t\tint deletions = 0;\n\t\t\tforeach(Diff aDiff in diffs) {\n\t\t\t\tswitch(aDiff.operation) {\n\t\t\t\tcase Operation.INSERT:\n\t\t\t\t\tinsertions += aDiff.text.Length;\n\t\t\t\t\tbreak;\n\t\t\t\tcase Operation.DELETE:\n\t\t\t\t\tdeletions += aDiff.text.Length;\n\t\t\t\t\tbreak;\n\t\t\t\tcase Operation.EQUAL:\n\t\t\t\t\t// A deletion and an insertion is one substitution.\n\t\t\t\t\tlevenshtein += Math.Max(insertions, deletions);\n\t\t\t\t\tinsertions = 0;\n\t\t\t\t\tdeletions = 0;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tlevenshtein += Math.Max(insertions, deletions);\n\t\t\treturn levenshtein;\n\t\t}\n\n\t\t/**\n\t\t * Crush the diff into an encoded string which describes the operations\n\t\t * required to transform text1 into text2.\n\t\t * E.g. =3\\t-2\\t+ing  -> Keep 3 chars, delete 2 chars, insert 'ing'.\n\t\t * Operations are tab-separated.  Inserted text is escaped using %xx\n\t\t * notation.\n\t\t * @param diffs Array of Diff objects.\n\t\t * @return Delta text.\n\t\t */\n\t\tpublic string diff_toDelta(List<Diff> diffs)\n\t\t{\n\t\t\tStringBuilder text = new StringBuilder();\n\t\t\tforeach(Diff aDiff in diffs) {\n\t\t\t\tswitch(aDiff.operation) {\n\t\t\t\tcase Operation.INSERT:\n\t\t\t\t\ttext.Append(\"+\").Append(encodeURI(aDiff.text)).Append(\"\\t\");\n\t\t\t\t\tbreak;\n\t\t\t\tcase Operation.DELETE:\n\t\t\t\t\ttext.Append(\"-\").Append(aDiff.text.Length).Append(\"\\t\");\n\t\t\t\t\tbreak;\n\t\t\t\tcase Operation.EQUAL:\n\t\t\t\t\ttext.Append(\"=\").Append(aDiff.text.Length).Append(\"\\t\");\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(text.Length != 0) text.Length--;\n\t\t\treturn text.ToString();\n\t\t}\n\n\t\t/**\n\t\t * Given the original text1, and an encoded string which describes the\n\t\t * operations required to transform text1 into text2, compute the full diff.\n\t\t * @param text1 Source string for the diff.\n\t\t * @param delta Delta text.\n\t\t * @return Array of Diff objects or null if invalid.\n\t\t * @throws ArgumentException If invalid input.\n\t\t */\n\t\tpublic List<Diff> diff_fromDelta(string text1, string delta)\n\t\t{\n\t\t\tList<Diff> diffs = new List<Diff>();\n\t\t\tint pointer = 0;  // Cursor in text1\n\t\t\tstring[] tokens = delta.Split('\\t');\n\t\t\tforeach(string token in tokens) {\n\t\t\t\tif(token.Length == 0) {\n\t\t\t\t\t// Blank tokens are ok (from a trailing \\t).\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t// Each token begins with a one character parameter which specifies the\n\t\t\t\t// operation of this token (delete, insert, equality).\n\t\t\t\tstring param = token.Substring(1);\n\t\t\t\tswitch(token[0]) {\n\t\t\t\tcase '+':\n\t\t\t\t\tparam = decodeURI(param);\n\t\t\t\t\tdiffs.Add(new Diff(Operation.INSERT, param));\n\t\t\t\t\tbreak;\n\t\t\t\tcase '-':\n\t\t\t\t// Fall through.\n\t\t\t\tcase '=':\n\t\t\t\t\tint n;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tn = Convert.ToInt32(param);\n\t\t\t\t\t}\n\t\t\t\t\tcatch(FormatException e) {\n\t\t\t\t\t\tthrow new ArgumentException(\n\t\t\t\t\t\t\t\"Invalid number in diff_fromDelta: \" + param, e);\n\t\t\t\t\t}\n\t\t\t\t\tif(n < 0) {\n\t\t\t\t\t\tthrow new ArgumentException(\n\t\t\t\t\t\t\t\"Negative number in diff_fromDelta: \" + param);\n\t\t\t\t\t}\n\t\t\t\t\tstring text;\n\t\t\t\t\ttry {\n\t\t\t\t\t\ttext = text1.Substring(pointer, n);\n\t\t\t\t\t\tpointer += n;\n\t\t\t\t\t}\n\t\t\t\t\tcatch(ArgumentOutOfRangeException e) {\n\t\t\t\t\t\tthrow new ArgumentException(\"Delta length (\" + pointer\n\t\t\t\t\t\t\t+ \") larger than source text length (\" + text1.Length\n\t\t\t\t\t\t\t+ \").\", e);\n\t\t\t\t\t}\n\t\t\t\t\tif(token[0] == '=') {\n\t\t\t\t\t\tdiffs.Add(new Diff(Operation.EQUAL, text));\n\t\t\t\t\t} else {\n\t\t\t\t\t\tdiffs.Add(new Diff(Operation.DELETE, text));\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\t// Anything else is an error.\n\t\t\t\t\tthrow new ArgumentException(\n\t\t\t\t\t\t\"Invalid diff operation in diff_fromDelta: \" + token[0]);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(pointer != text1.Length) {\n\t\t\t\tthrow new ArgumentException(\"Delta length (\" + pointer\n\t\t\t\t\t+ \") smaller than source text length (\" + text1.Length + \").\");\n\t\t\t}\n\t\t\treturn diffs;\n\t\t}\n\n\n\t\t//  MATCH FUNCTIONS\n\n\n\t\t/**\n\t\t * Locate the best instance of 'pattern' in 'text' near 'loc'.\n\t\t * Returns -1 if no match found.\n\t\t * @param text The text to search.\n\t\t * @param pattern The pattern to search for.\n\t\t * @param loc The location to search around.\n\t\t * @return Best match index or -1.\n\t\t */\n\t\tpublic int match_main(string text, string pattern, int loc)\n\t\t{\n\t\t\t// Check for null inputs not needed since null can't be passed in C#.\n\n\t\t\tloc = Math.Max(0, Math.Min(loc, text.Length));\n\t\t\tif(text == pattern) {\n\t\t\t\t// Shortcut (potentially not guaranteed by the algorithm)\n\t\t\t\treturn 0;\n\t\t\t} else if(text.Length == 0) {\n\t\t\t\t// Nothing to match.\n\t\t\t\treturn -1;\n\t\t\t} else if(loc + pattern.Length <= text.Length\n\t\t\t  && text.Substring(loc, pattern.Length) == pattern) {\n\t\t\t\t// Perfect match at the perfect spot!  (Includes case of null pattern)\n\t\t\t\treturn loc;\n\t\t\t} else {\n\t\t\t\t// Do a fuzzy compare.\n\t\t\t\treturn match_bitap(text, pattern, loc);\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * Locate the best instance of 'pattern' in 'text' near 'loc' using the\n\t\t * Bitap algorithm.  Returns -1 if no match found.\n\t\t * @param text The text to search.\n\t\t * @param pattern The pattern to search for.\n\t\t * @param loc The location to search around.\n\t\t * @return Best match index or -1.\n\t\t */\n\t\tprotected int match_bitap(string text, string pattern, int loc)\n\t\t{\n\t\t\t// assert (Match_MaxBits == 0 || pattern.Length <= Match_MaxBits)\n\t\t\t//    : \"Pattern too long for this application.\";\n\n\t\t\t// Initialise the alphabet.\n\t\t\tDictionary<char, int> s = match_alphabet(pattern);\n\n\t\t\t// Highest score beyond which we give up.\n\t\t\tdouble score_threshold = Match_Threshold;\n\t\t\t// Is there a nearby exact match? (speedup)\n\t\t\tint best_loc = text.IndexOf(pattern, loc, StringComparison.Ordinal);\n\t\t\tif(best_loc != -1) {\n\t\t\t\tscore_threshold = Math.Min(match_bitapScore(0, best_loc, loc,\n\t\t\t\t\tpattern), score_threshold);\n\t\t\t\t// What about in the other direction? (speedup)\n\t\t\t\tbest_loc = text.LastIndexOf(pattern,\n\t\t\t\t\tMath.Min(loc + pattern.Length, text.Length),\n\t\t\t\t\tStringComparison.Ordinal);\n\t\t\t\tif(best_loc != -1) {\n\t\t\t\t\tscore_threshold = Math.Min(match_bitapScore(0, best_loc, loc,\n\t\t\t\t\t\tpattern), score_threshold);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Initialise the bit arrays.\n\t\t\tint matchmask = 1 << (pattern.Length - 1);\n\t\t\tbest_loc = -1;\n\n\t\t\tint bin_min, bin_mid;\n\t\t\tint bin_max = pattern.Length + text.Length;\n\t\t\t// Empty initialization added to appease C# compiler.\n\t\t\tint[] last_rd = new int[0];\n\t\t\tfor(int d = 0; d < pattern.Length; d++) {\n\t\t\t\t// Scan for the best match; each iteration allows for one more error.\n\t\t\t\t// Run a binary search to determine how far from 'loc' we can stray at\n\t\t\t\t// this error level.\n\t\t\t\tbin_min = 0;\n\t\t\t\tbin_mid = bin_max;\n\t\t\t\twhile(bin_min < bin_mid) {\n\t\t\t\t\tif(match_bitapScore(d, loc + bin_mid, loc, pattern)\n\t\t\t\t\t\t<= score_threshold) {\n\t\t\t\t\t\tbin_min = bin_mid;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tbin_max = bin_mid;\n\t\t\t\t\t}\n\t\t\t\t\tbin_mid = (bin_max - bin_min) / 2 + bin_min;\n\t\t\t\t}\n\t\t\t\t// Use the result from this iteration as the maximum for the next.\n\t\t\t\tbin_max = bin_mid;\n\t\t\t\tint start = Math.Max(1, loc - bin_mid + 1);\n\t\t\t\tint finish = Math.Min(loc + bin_mid, text.Length) + pattern.Length;\n\n\t\t\t\tint[] rd = new int[finish + 2];\n\t\t\t\trd[finish + 1] = (1 << d) - 1;\n\t\t\t\tfor(int j = finish; j >= start; j--) {\n\t\t\t\t\tint charMatch;\n\t\t\t\t\tif(text.Length <= j - 1 || !s.ContainsKey(text[j - 1])) {\n\t\t\t\t\t\t// Out of range.\n\t\t\t\t\t\tcharMatch = 0;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tcharMatch = s[text[j - 1]];\n\t\t\t\t\t}\n\t\t\t\t\tif(d == 0) {\n\t\t\t\t\t\t// First pass: exact match.\n\t\t\t\t\t\trd[j] = ((rd[j + 1] << 1) | 1) & charMatch;\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Subsequent passes: fuzzy match.\n\t\t\t\t\t\trd[j] = ((rd[j + 1] << 1) | 1) & charMatch\n\t\t\t\t\t\t\t| (((last_rd[j + 1] | last_rd[j]) << 1) | 1) | last_rd[j + 1];\n\t\t\t\t\t}\n\t\t\t\t\tif((rd[j] & matchmask) != 0) {\n\t\t\t\t\t\tdouble score = match_bitapScore(d, j - 1, loc, pattern);\n\t\t\t\t\t\t// This match will almost certainly be better than any existing\n\t\t\t\t\t\t// match.  But check anyway.\n\t\t\t\t\t\tif(score <= score_threshold) {\n\t\t\t\t\t\t\t// Told you so.\n\t\t\t\t\t\t\tscore_threshold = score;\n\t\t\t\t\t\t\tbest_loc = j - 1;\n\t\t\t\t\t\t\tif(best_loc > loc) {\n\t\t\t\t\t\t\t\t// When passing loc, don't exceed our current distance from loc.\n\t\t\t\t\t\t\t\tstart = Math.Max(1, 2 * loc - best_loc);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t// Already passed loc, downhill from here on in.\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif(match_bitapScore(d + 1, loc, loc, pattern) > score_threshold) {\n\t\t\t\t\t// No hope for a (better) match at greater error levels.\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tlast_rd = rd;\n\t\t\t}\n\t\t\treturn best_loc;\n\t\t}\n\n\t\t/**\n\t\t * Compute and return the score for a match with e errors and x location.\n\t\t * @param e Number of errors in match.\n\t\t * @param x Location of match.\n\t\t * @param loc Expected location of match.\n\t\t * @param pattern Pattern being sought.\n\t\t * @return Overall score for match (0.0 = good, 1.0 = bad).\n\t\t */\n\t\tprivate double match_bitapScore(int e, int x, int loc, string pattern)\n\t\t{\n\t\t\tfloat accuracy = (float)e / pattern.Length;\n\t\t\tint proximity = Math.Abs(loc - x);\n\t\t\tif(Match_Distance == 0) {\n\t\t\t\t// Dodge divide by zero error.\n\t\t\t\treturn proximity == 0 ? accuracy : 1.0;\n\t\t\t}\n\t\t\treturn accuracy + (proximity / (float)Match_Distance);\n\t\t}\n\n\t\t/**\n\t\t * Initialise the alphabet for the Bitap algorithm.\n\t\t * @param pattern The text to encode.\n\t\t * @return Hash of character locations.\n\t\t */\n\t\tprotected Dictionary<char, int> match_alphabet(string pattern)\n\t\t{\n\t\t\tDictionary<char, int> s = new Dictionary<char, int>();\n\t\t\tchar[] char_pattern = pattern.ToCharArray();\n\t\t\tforeach(char c in char_pattern) {\n\t\t\t\tif(!s.ContainsKey(c)) {\n\t\t\t\t\ts.Add(c, 0);\n\t\t\t\t}\n\t\t\t}\n\t\t\tint i = 0;\n\t\t\tforeach(char c in char_pattern) {\n\t\t\t\tint value = s[c] | (1 << (pattern.Length - i - 1));\n\t\t\t\ts[c] = value;\n\t\t\t\ti++;\n\t\t\t}\n\t\t\treturn s;\n\t\t}\n\n\n\t\t//  PATCH FUNCTIONS\n\n\n\t\t/**\n\t\t * Increase the context until it is unique,\n\t\t * but don't let the pattern expand beyond Match_MaxBits.\n\t\t * @param patch The patch to grow.\n\t\t * @param text Source text.\n\t\t */\n\t\tprotected void patch_addContext(Patch patch, string text)\n\t\t{\n\t\t\tif(text.Length == 0) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tstring pattern = text.Substring(patch.start2, patch.length1);\n\t\t\tint padding = 0;\n\n\t\t\t// Look for the first and last matches of pattern in text.  If two\n\t\t\t// different matches are found, increase the pattern length.\n\t\t\twhile(text.IndexOf(pattern, StringComparison.Ordinal)\n\t\t\t\t!= text.LastIndexOf(pattern, StringComparison.Ordinal)\n\t\t\t\t&& pattern.Length < Match_MaxBits - Patch_Margin - Patch_Margin) {\n\t\t\t\tpadding += Patch_Margin;\n\t\t\t\tpattern = text.JavaSubstring(Math.Max(0, patch.start2 - padding),\n\t\t\t\t\tMath.Min(text.Length, patch.start2 + patch.length1 + padding));\n\t\t\t}\n\t\t\t// Add one chunk for good luck.\n\t\t\tpadding += Patch_Margin;\n\n\t\t\t// Add the prefix.\n\t\t\tstring prefix = text.JavaSubstring(Math.Max(0, patch.start2 - padding),\n\t\t\t  patch.start2);\n\t\t\tif(prefix.Length != 0) {\n\t\t\t\tpatch.diffs.Insert(0, new Diff(Operation.EQUAL, prefix));\n\t\t\t}\n\t\t\t// Add the suffix.\n\t\t\tstring suffix = text.JavaSubstring(patch.start2 + patch.length1,\n\t\t\t\tMath.Min(text.Length, patch.start2 + patch.length1 + padding));\n\t\t\tif(suffix.Length != 0) {\n\t\t\t\tpatch.diffs.Add(new Diff(Operation.EQUAL, suffix));\n\t\t\t}\n\n\t\t\t// Roll back the start points.\n\t\t\tpatch.start1 -= prefix.Length;\n\t\t\tpatch.start2 -= prefix.Length;\n\t\t\t// Extend the lengths.\n\t\t\tpatch.length1 += prefix.Length + suffix.Length;\n\t\t\tpatch.length2 += prefix.Length + suffix.Length;\n\t\t}\n\n\t\t/**\n\t\t * Compute a list of patches to turn text1 into text2.\n\t\t * A set of diffs will be computed.\n\t\t * @param text1 Old text.\n\t\t * @param text2 New text.\n\t\t * @return List of Patch objects.\n\t\t */\n\t\tpublic List<Patch> patch_make(string text1, string text2)\n\t\t{\n\t\t\t// Check for null inputs not needed since null can't be passed in C#.\n\t\t\t// No diffs provided, compute our own.\n\t\t\tList<Diff> diffs = diff_main(text1, text2, true);\n\t\t\tif(diffs.Count > 2) {\n\t\t\t\tdiff_cleanupSemantic(diffs);\n\t\t\t\tdiff_cleanupEfficiency(diffs);\n\t\t\t}\n\t\t\treturn patch_make(text1, diffs);\n\t\t}\n\n\t\t/**\n\t\t * Compute a list of patches to turn text1 into text2.\n\t\t * text1 will be derived from the provided diffs.\n\t\t * @param diffs Array of Diff objects for text1 to text2.\n\t\t * @return List of Patch objects.\n\t\t */\n\t\tpublic List<Patch> patch_make(List<Diff> diffs)\n\t\t{\n\t\t\t// Check for null inputs not needed since null can't be passed in C#.\n\t\t\t// No origin string provided, compute our own.\n\t\t\tstring text1 = diff_text1(diffs);\n\t\t\treturn patch_make(text1, diffs);\n\t\t}\n\n\t\t/**\n\t\t * Compute a list of patches to turn text1 into text2.\n\t\t * text2 is ignored, diffs are the delta between text1 and text2.\n\t\t * @param text1 Old text\n\t\t * @param text2 Ignored.\n\t\t * @param diffs Array of Diff objects for text1 to text2.\n\t\t * @return List of Patch objects.\n\t\t * @deprecated Prefer patch_make(string text1, List&lt;Diff&gt; diffs).\n\t\t */\n\t\tpublic List<Patch> patch_make(string text1, string text2,\n\t\t\tList<Diff> diffs)\n\t\t{\n\t\t\treturn patch_make(text1, diffs);\n\t\t}\n\n\t\t/**\n\t\t * Compute a list of patches to turn text1 into text2.\n\t\t * text2 is not provided, diffs are the delta between text1 and text2.\n\t\t * @param text1 Old text.\n\t\t * @param diffs Array of Diff objects for text1 to text2.\n\t\t * @return List of Patch objects.\n\t\t */\n\t\tpublic List<Patch> patch_make(string text1, List<Diff> diffs)\n\t\t{\n\t\t\t// Check for null inputs not needed since null can't be passed in C#.\n\t\t\tList<Patch> patches = new List<Patch>();\n\t\t\tif(diffs.Count == 0) {\n\t\t\t\treturn patches;  // Get rid of the null case.\n\t\t\t}\n\t\t\tPatch patch = new Patch();\n\t\t\tint char_count1 = 0;  // Number of characters into the text1 string.\n\t\t\tint char_count2 = 0;  // Number of characters into the text2 string.\n\t\t\t\t\t\t\t\t  // Start with text1 (prepatch_text) and apply the diffs until we arrive at\n\t\t\t\t\t\t\t\t  // text2 (postpatch_text). We recreate the patches one by one to determine\n\t\t\t\t\t\t\t\t  // context info.\n\t\t\tstring prepatch_text = text1;\n\t\t\tstring postpatch_text = text1;\n\t\t\tforeach(Diff aDiff in diffs) {\n\t\t\t\tif(patch.diffs.Count == 0 && aDiff.operation != Operation.EQUAL) {\n\t\t\t\t\t// A new patch starts here.\n\t\t\t\t\tpatch.start1 = char_count1;\n\t\t\t\t\tpatch.start2 = char_count2;\n\t\t\t\t}\n\n\t\t\t\tswitch(aDiff.operation) {\n\t\t\t\tcase Operation.INSERT:\n\t\t\t\t\tpatch.diffs.Add(aDiff);\n\t\t\t\t\tpatch.length2 += aDiff.text.Length;\n\t\t\t\t\tpostpatch_text = postpatch_text.Insert(char_count2, aDiff.text);\n\t\t\t\t\tbreak;\n\t\t\t\tcase Operation.DELETE:\n\t\t\t\t\tpatch.length1 += aDiff.text.Length;\n\t\t\t\t\tpatch.diffs.Add(aDiff);\n\t\t\t\t\tpostpatch_text = postpatch_text.Remove(char_count2,\n\t\t\t\t\t\taDiff.text.Length);\n\t\t\t\t\tbreak;\n\t\t\t\tcase Operation.EQUAL:\n\t\t\t\t\tif(aDiff.text.Length <= 2 * Patch_Margin\n\t\t\t\t\t\t&& patch.diffs.Count != 0 && aDiff != diffs.Last()) {\n\t\t\t\t\t\t// Small equality inside a patch.\n\t\t\t\t\t\tpatch.diffs.Add(aDiff);\n\t\t\t\t\t\tpatch.length1 += aDiff.text.Length;\n\t\t\t\t\t\tpatch.length2 += aDiff.text.Length;\n\t\t\t\t\t}\n\n\t\t\t\t\tif(aDiff.text.Length >= 2 * Patch_Margin) {\n\t\t\t\t\t\t// Time for a new patch.\n\t\t\t\t\t\tif(patch.diffs.Count != 0) {\n\t\t\t\t\t\t\tpatch_addContext(patch, prepatch_text);\n\t\t\t\t\t\t\tpatches.Add(patch);\n\t\t\t\t\t\t\tpatch = new Patch();\n\t\t\t\t\t\t\t// Unlike Unidiff, our patch lists have a rolling context.\n\t\t\t\t\t\t\t// https://github.com/google/diff-match-patch/wiki/Unidiff\n\t\t\t\t\t\t\t// Update prepatch text & pos to reflect the application of the\n\t\t\t\t\t\t\t// just completed patch.\n\t\t\t\t\t\t\tprepatch_text = postpatch_text;\n\t\t\t\t\t\t\tchar_count1 = char_count2;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\t// Update the current character count.\n\t\t\t\tif(aDiff.operation != Operation.INSERT) {\n\t\t\t\t\tchar_count1 += aDiff.text.Length;\n\t\t\t\t}\n\t\t\t\tif(aDiff.operation != Operation.DELETE) {\n\t\t\t\t\tchar_count2 += aDiff.text.Length;\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Pick up the leftover patch if not empty.\n\t\t\tif(patch.diffs.Count != 0) {\n\t\t\t\tpatch_addContext(patch, prepatch_text);\n\t\t\t\tpatches.Add(patch);\n\t\t\t}\n\n\t\t\treturn patches;\n\t\t}\n\n\t\t/**\n\t\t * Given an array of patches, return another array that is identical.\n\t\t * @param patches Array of Patch objects.\n\t\t * @return Array of Patch objects.\n\t\t */\n\t\tpublic List<Patch> patch_deepCopy(List<Patch> patches)\n\t\t{\n\t\t\tList<Patch> patchesCopy = new List<Patch>();\n\t\t\tforeach(Patch aPatch in patches) {\n\t\t\t\tPatch patchCopy = new Patch();\n\t\t\t\tforeach(Diff aDiff in aPatch.diffs) {\n\t\t\t\t\tDiff diffCopy = new Diff(aDiff.operation, aDiff.text);\n\t\t\t\t\tpatchCopy.diffs.Add(diffCopy);\n\t\t\t\t}\n\t\t\t\tpatchCopy.start1 = aPatch.start1;\n\t\t\t\tpatchCopy.start2 = aPatch.start2;\n\t\t\t\tpatchCopy.length1 = aPatch.length1;\n\t\t\t\tpatchCopy.length2 = aPatch.length2;\n\t\t\t\tpatchesCopy.Add(patchCopy);\n\t\t\t}\n\t\t\treturn patchesCopy;\n\t\t}\n\n\t\t/**\n\t\t * Merge a set of patches onto the text.  Return a patched text, as well\n\t\t * as an array of true/false values indicating which patches were applied.\n\t\t * @param patches Array of Patch objects\n\t\t * @param text Old text.\n\t\t * @return Two element Object array, containing the new text and an array of\n\t\t *      bool values.\n\t\t */\n\t\tpublic Object[] patch_apply(List<Patch> patches, string text)\n\t\t{\n\t\t\tif(patches.Count == 0) {\n\t\t\t\treturn new Object[] { text, new bool[0] };\n\t\t\t}\n\n\t\t\t// Deep copy the patches so that no changes are made to originals.\n\t\t\tpatches = patch_deepCopy(patches);\n\n\t\t\tstring nullPadding = this.patch_addPadding(patches);\n\t\t\ttext = nullPadding + text + nullPadding;\n\t\t\tpatch_splitMax(patches);\n\n\t\t\tint x = 0;\n\t\t\t// delta keeps track of the offset between the expected and actual\n\t\t\t// location of the previous patch.  If there are patches expected at\n\t\t\t// positions 10 and 20, but the first patch was found at 12, delta is 2\n\t\t\t// and the second patch has an effective expected position of 22.\n\t\t\tint delta = 0;\n\t\t\tbool[] results = new bool[patches.Count];\n\t\t\tforeach(Patch aPatch in patches) {\n\t\t\t\tint expected_loc = aPatch.start2 + delta;\n\t\t\t\tstring text1 = diff_text1(aPatch.diffs);\n\t\t\t\tint start_loc;\n\t\t\t\tint end_loc = -1;\n\t\t\t\tif(text1.Length > Match_MaxBits) {\n\t\t\t\t\t// patch_splitMax will only provide an oversized pattern\n\t\t\t\t\t// in the case of a monster delete.\n\t\t\t\t\tstart_loc = match_main(text,\n\t\t\t\t\t\ttext1.Substring(0, Match_MaxBits), expected_loc);\n\t\t\t\t\tif(start_loc != -1) {\n\t\t\t\t\t\tend_loc = match_main(text,\n\t\t\t\t\t\t\ttext1.Substring(text1.Length - Match_MaxBits),\n\t\t\t\t\t\t\texpected_loc + text1.Length - Match_MaxBits);\n\t\t\t\t\t\tif(end_loc == -1 || start_loc >= end_loc) {\n\t\t\t\t\t\t\t// Can't find valid trailing context.  Drop this patch.\n\t\t\t\t\t\t\tstart_loc = -1;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tstart_loc = this.match_main(text, text1, expected_loc);\n\t\t\t\t}\n\t\t\t\tif(start_loc == -1) {\n\t\t\t\t\t// No match found.  :(\n\t\t\t\t\tresults[x] = false;\n\t\t\t\t\t// Subtract the delta for this failed patch from subsequent patches.\n\t\t\t\t\tdelta -= aPatch.length2 - aPatch.length1;\n\t\t\t\t} else {\n\t\t\t\t\t// Found a match.  :)\n\t\t\t\t\tresults[x] = true;\n\t\t\t\t\tdelta = start_loc - expected_loc;\n\t\t\t\t\tstring text2;\n\t\t\t\t\tif(end_loc == -1) {\n\t\t\t\t\t\ttext2 = text.JavaSubstring(start_loc,\n\t\t\t\t\t\t\tMath.Min(start_loc + text1.Length, text.Length));\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttext2 = text.JavaSubstring(start_loc,\n\t\t\t\t\t\t\tMath.Min(end_loc + Match_MaxBits, text.Length));\n\t\t\t\t\t}\n\t\t\t\t\tif(text1 == text2) {\n\t\t\t\t\t\t// Perfect match, just shove the Replacement text in.\n\t\t\t\t\t\ttext = text.Substring(0, start_loc) + diff_text2(aPatch.diffs)\n\t\t\t\t\t\t\t+ text.Substring(start_loc + text1.Length);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Imperfect match.  Run a diff to get a framework of equivalent\n\t\t\t\t\t\t// indices.\n\t\t\t\t\t\tList<Diff> diffs = diff_main(text1, text2, false);\n\t\t\t\t\t\tif(text1.Length > Match_MaxBits\n\t\t\t\t\t\t\t&& this.diff_levenshtein(diffs) / (float)text1.Length\n\t\t\t\t\t\t\t> this.Patch_DeleteThreshold) {\n\t\t\t\t\t\t\t// The end points match, but the content is unacceptably bad.\n\t\t\t\t\t\t\tresults[x] = false;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tdiff_cleanupSemanticLossless(diffs);\n\t\t\t\t\t\t\tint index1 = 0;\n\t\t\t\t\t\t\tforeach(Diff aDiff in aPatch.diffs) {\n\t\t\t\t\t\t\t\tif(aDiff.operation != Operation.EQUAL) {\n\t\t\t\t\t\t\t\t\tint index2 = diff_xIndex(diffs, index1);\n\t\t\t\t\t\t\t\t\tif(aDiff.operation == Operation.INSERT) {\n\t\t\t\t\t\t\t\t\t\t// Insertion\n\t\t\t\t\t\t\t\t\t\ttext = text.Insert(start_loc + index2, aDiff.text);\n\t\t\t\t\t\t\t\t\t} else if(aDiff.operation == Operation.DELETE) {\n\t\t\t\t\t\t\t\t\t\t// Deletion\n\t\t\t\t\t\t\t\t\t\ttext = text.Remove(start_loc + index2, diff_xIndex(diffs,\n\t\t\t\t\t\t\t\t\t\t\tindex1 + aDiff.text.Length) - index2);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif(aDiff.operation != Operation.DELETE) {\n\t\t\t\t\t\t\t\t\tindex1 += aDiff.text.Length;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tx++;\n\t\t\t}\n\t\t\t// Strip the padding off.\n\t\t\ttext = text.Substring(nullPadding.Length, text.Length\n\t\t\t\t- 2 * nullPadding.Length);\n\t\t\treturn new Object[] { text, results };\n\t\t}\n\n\t\t/**\n\t\t * Add some padding on text start and end so that edges can match something.\n\t\t * Intended to be called only from within patch_apply.\n\t\t * @param patches Array of Patch objects.\n\t\t * @return The padding string added to each side.\n\t\t */\n\t\tpublic string patch_addPadding(List<Patch> patches)\n\t\t{\n\t\t\tshort paddingLength = this.Patch_Margin;\n\t\t\tstring nullPadding = string.Empty;\n\t\t\tfor(short x = 1; x <= paddingLength; x++) {\n\t\t\t\tnullPadding += (char)x;\n\t\t\t}\n\n\t\t\t// Bump all the patches forward.\n\t\t\tforeach(Patch aPatch in patches) {\n\t\t\t\taPatch.start1 += paddingLength;\n\t\t\t\taPatch.start2 += paddingLength;\n\t\t\t}\n\n\t\t\t// Add some padding on start of first diff.\n\t\t\tPatch patch = patches.First();\n\t\t\tList<Diff> diffs = patch.diffs;\n\t\t\tif(diffs.Count == 0 || diffs.First().operation != Operation.EQUAL) {\n\t\t\t\t// Add nullPadding equality.\n\t\t\t\tdiffs.Insert(0, new Diff(Operation.EQUAL, nullPadding));\n\t\t\t\tpatch.start1 -= paddingLength;  // Should be 0.\n\t\t\t\tpatch.start2 -= paddingLength;  // Should be 0.\n\t\t\t\tpatch.length1 += paddingLength;\n\t\t\t\tpatch.length2 += paddingLength;\n\t\t\t} else if(paddingLength > diffs.First().text.Length) {\n\t\t\t\t// Grow first equality.\n\t\t\t\tDiff firstDiff = diffs.First();\n\t\t\t\tint extraLength = paddingLength - firstDiff.text.Length;\n\t\t\t\tfirstDiff.text = nullPadding.Substring(firstDiff.text.Length)\n\t\t\t\t\t+ firstDiff.text;\n\t\t\t\tpatch.start1 -= extraLength;\n\t\t\t\tpatch.start2 -= extraLength;\n\t\t\t\tpatch.length1 += extraLength;\n\t\t\t\tpatch.length2 += extraLength;\n\t\t\t}\n\n\t\t\t// Add some padding on end of last diff.\n\t\t\tpatch = patches.Last();\n\t\t\tdiffs = patch.diffs;\n\t\t\tif(diffs.Count == 0 || diffs.Last().operation != Operation.EQUAL) {\n\t\t\t\t// Add nullPadding equality.\n\t\t\t\tdiffs.Add(new Diff(Operation.EQUAL, nullPadding));\n\t\t\t\tpatch.length1 += paddingLength;\n\t\t\t\tpatch.length2 += paddingLength;\n\t\t\t} else if(paddingLength > diffs.Last().text.Length) {\n\t\t\t\t// Grow last equality.\n\t\t\t\tDiff lastDiff = diffs.Last();\n\t\t\t\tint extraLength = paddingLength - lastDiff.text.Length;\n\t\t\t\tlastDiff.text += nullPadding.Substring(0, extraLength);\n\t\t\t\tpatch.length1 += extraLength;\n\t\t\t\tpatch.length2 += extraLength;\n\t\t\t}\n\n\t\t\treturn nullPadding;\n\t\t}\n\n\t\t/**\n\t\t * Look through the patches and break up any which are longer than the\n\t\t * maximum limit of the match algorithm.\n\t\t * Intended to be called only from within patch_apply.\n\t\t * @param patches List of Patch objects.\n\t\t */\n\t\tpublic void patch_splitMax(List<Patch> patches)\n\t\t{\n\t\t\tshort patch_size = Match_MaxBits;\n\t\t\tfor(int x = 0; x < patches.Count; x++) {\n\t\t\t\tif(patches[x].length1 <= patch_size) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tPatch bigpatch = patches[x];\n\t\t\t\t// Remove the big old patch.\n\t\t\t\tpatches.Splice(x--, 1);\n\t\t\t\tint start1 = bigpatch.start1;\n\t\t\t\tint start2 = bigpatch.start2;\n\t\t\t\tstring precontext = string.Empty;\n\t\t\t\twhile(bigpatch.diffs.Count != 0) {\n\t\t\t\t\t// Create one of several smaller patches.\n\t\t\t\t\tPatch patch = new Patch();\n\t\t\t\t\tbool empty = true;\n\t\t\t\t\tpatch.start1 = start1 - precontext.Length;\n\t\t\t\t\tpatch.start2 = start2 - precontext.Length;\n\t\t\t\t\tif(precontext.Length != 0) {\n\t\t\t\t\t\tpatch.length1 = patch.length2 = precontext.Length;\n\t\t\t\t\t\tpatch.diffs.Add(new Diff(Operation.EQUAL, precontext));\n\t\t\t\t\t}\n\t\t\t\t\twhile(bigpatch.diffs.Count != 0\n\t\t\t\t\t\t&& patch.length1 < patch_size - this.Patch_Margin) {\n\t\t\t\t\t\tOperation diff_type = bigpatch.diffs[0].operation;\n\t\t\t\t\t\tstring diff_text = bigpatch.diffs[0].text;\n\t\t\t\t\t\tif(diff_type == Operation.INSERT) {\n\t\t\t\t\t\t\t// Insertions are harmless.\n\t\t\t\t\t\t\tpatch.length2 += diff_text.Length;\n\t\t\t\t\t\t\tstart2 += diff_text.Length;\n\t\t\t\t\t\t\tpatch.diffs.Add(bigpatch.diffs.First());\n\t\t\t\t\t\t\tbigpatch.diffs.RemoveAt(0);\n\t\t\t\t\t\t\tempty = false;\n\t\t\t\t\t\t} else if(diff_type == Operation.DELETE && patch.diffs.Count == 1\n\t\t\t\t\t\t\t&& patch.diffs.First().operation == Operation.EQUAL\n\t\t\t\t\t\t\t&& diff_text.Length > 2 * patch_size) {\n\t\t\t\t\t\t\t// This is a large deletion.  Let it pass in one chunk.\n\t\t\t\t\t\t\tpatch.length1 += diff_text.Length;\n\t\t\t\t\t\t\tstart1 += diff_text.Length;\n\t\t\t\t\t\t\tempty = false;\n\t\t\t\t\t\t\tpatch.diffs.Add(new Diff(diff_type, diff_text));\n\t\t\t\t\t\t\tbigpatch.diffs.RemoveAt(0);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// Deletion or equality.  Only take as much as we can stomach.\n\t\t\t\t\t\t\tdiff_text = diff_text.Substring(0, Math.Min(diff_text.Length,\n\t\t\t\t\t\t\t\tpatch_size - patch.length1 - Patch_Margin));\n\t\t\t\t\t\t\tpatch.length1 += diff_text.Length;\n\t\t\t\t\t\t\tstart1 += diff_text.Length;\n\t\t\t\t\t\t\tif(diff_type == Operation.EQUAL) {\n\t\t\t\t\t\t\t\tpatch.length2 += diff_text.Length;\n\t\t\t\t\t\t\t\tstart2 += diff_text.Length;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tempty = false;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tpatch.diffs.Add(new Diff(diff_type, diff_text));\n\t\t\t\t\t\t\tif(diff_text == bigpatch.diffs[0].text) {\n\t\t\t\t\t\t\t\tbigpatch.diffs.RemoveAt(0);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tbigpatch.diffs[0].text =\n\t\t\t\t\t\t\t\t\tbigpatch.diffs[0].text.Substring(diff_text.Length);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// Compute the head context for the next patch.\n\t\t\t\t\tprecontext = this.diff_text2(patch.diffs);\n\t\t\t\t\tprecontext = precontext.Substring(Math.Max(0,\n\t\t\t\t\t\tprecontext.Length - this.Patch_Margin));\n\n\t\t\t\t\tstring postcontext = null;\n\t\t\t\t\t// Append the end context for this patch.\n\t\t\t\t\tif(diff_text1(bigpatch.diffs).Length > Patch_Margin) {\n\t\t\t\t\t\tpostcontext = diff_text1(bigpatch.diffs)\n\t\t\t\t\t\t\t.Substring(0, Patch_Margin);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tpostcontext = diff_text1(bigpatch.diffs);\n\t\t\t\t\t}\n\n\t\t\t\t\tif(postcontext.Length != 0) {\n\t\t\t\t\t\tpatch.length1 += postcontext.Length;\n\t\t\t\t\t\tpatch.length2 += postcontext.Length;\n\t\t\t\t\t\tif(patch.diffs.Count != 0\n\t\t\t\t\t\t\t&& patch.diffs[patch.diffs.Count - 1].operation\n\t\t\t\t\t\t\t== Operation.EQUAL) {\n\t\t\t\t\t\t\tpatch.diffs[patch.diffs.Count - 1].text += postcontext;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tpatch.diffs.Add(new Diff(Operation.EQUAL, postcontext));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif(!empty) {\n\t\t\t\t\t\tpatches.Splice(++x, 0, patch);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * Take a list of patches and return a textual representation.\n\t\t * @param patches List of Patch objects.\n\t\t * @return Text representation of patches.\n\t\t */\n\t\tpublic string patch_toText(List<Patch> patches)\n\t\t{\n\t\t\tStringBuilder text = new StringBuilder();\n\t\t\tforeach(Patch aPatch in patches) {\n\t\t\t\ttext.Append(aPatch);\n\t\t\t}\n\t\t\treturn text.ToString();\n\t\t}\n\n\t\t/**\n\t\t * Parse a textual representation of patches and return a List of Patch\n\t\t * objects.\n\t\t * @param textline Text representation of patches.\n\t\t * @return List of Patch objects.\n\t\t * @throws ArgumentException If invalid input.\n\t\t */\n\t\tpublic List<Patch> patch_fromText(string textline)\n\t\t{\n\t\t\tList<Patch> patches = new List<Patch>();\n\t\t\tif(textline.Length == 0) {\n\t\t\t\treturn patches;\n\t\t\t}\n\t\t\tstring[] text = textline.Split('\\n');\n\t\t\tint textPointer = 0;\n\t\t\tPatch patch;\n\t\t\tRegex patchHeader\n\t\t\t\t= new Regex(\"^@@ -(\\\\d+),?(\\\\d*) \\\\+(\\\\d+),?(\\\\d*) @@$\");\n\t\t\tMatch m;\n\t\t\tchar sign;\n\t\t\tstring line;\n\t\t\twhile(textPointer < text.Length) {\n\t\t\t\tm = patchHeader.Match(text[textPointer]);\n\t\t\t\tif(!m.Success) {\n\t\t\t\t\tthrow new ArgumentException(\"Invalid patch string: \"\n\t\t\t\t\t\t+ text[textPointer]);\n\t\t\t\t}\n\t\t\t\tpatch = new Patch();\n\t\t\t\tpatches.Add(patch);\n\t\t\t\tpatch.start1 = Convert.ToInt32(m.Groups[1].Value);\n\t\t\t\tif(m.Groups[2].Length == 0) {\n\t\t\t\t\tpatch.start1--;\n\t\t\t\t\tpatch.length1 = 1;\n\t\t\t\t} else if(m.Groups[2].Value == \"0\") {\n\t\t\t\t\tpatch.length1 = 0;\n\t\t\t\t} else {\n\t\t\t\t\tpatch.start1--;\n\t\t\t\t\tpatch.length1 = Convert.ToInt32(m.Groups[2].Value);\n\t\t\t\t}\n\n\t\t\t\tpatch.start2 = Convert.ToInt32(m.Groups[3].Value);\n\t\t\t\tif(m.Groups[4].Length == 0) {\n\t\t\t\t\tpatch.start2--;\n\t\t\t\t\tpatch.length2 = 1;\n\t\t\t\t} else if(m.Groups[4].Value == \"0\") {\n\t\t\t\t\tpatch.length2 = 0;\n\t\t\t\t} else {\n\t\t\t\t\tpatch.start2--;\n\t\t\t\t\tpatch.length2 = Convert.ToInt32(m.Groups[4].Value);\n\t\t\t\t}\n\t\t\t\ttextPointer++;\n\n\t\t\t\twhile(textPointer < text.Length) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tsign = text[textPointer][0];\n\t\t\t\t\t}\n\t\t\t\t\tcatch(IndexOutOfRangeException) {\n\t\t\t\t\t\t// Blank line?  Whatever.\n\t\t\t\t\t\ttextPointer++;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tline = text[textPointer].Substring(1);\n\t\t\t\t\tline = decodeURI(line);\n\t\t\t\t\tif(sign == '-') {\n\t\t\t\t\t\t// Deletion.\n\t\t\t\t\t\tpatch.diffs.Add(new Diff(Operation.DELETE, line));\n\t\t\t\t\t} else if(sign == '+') {\n\t\t\t\t\t\t// Insertion.\n\t\t\t\t\t\tpatch.diffs.Add(new Diff(Operation.INSERT, line));\n\t\t\t\t\t} else if(sign == ' ') {\n\t\t\t\t\t\t// Minor equality.\n\t\t\t\t\t\tpatch.diffs.Add(new Diff(Operation.EQUAL, line));\n\t\t\t\t\t} else if(sign == '@') {\n\t\t\t\t\t\t// Start of next patch.\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// WTF?\n\t\t\t\t\t\tthrow new ArgumentException(\n\t\t\t\t\t\t\t\"Invalid patch mode '\" + sign + \"' in: \" + line);\n\t\t\t\t\t}\n\t\t\t\t\ttextPointer++;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn patches;\n\t\t}\n\n#if true\n\t\tinternal static string encodeURI(string str)\n\t\t{\n\t\t\treturn str.Escape();\n\t\t}\n\n\t\tstatic string decodeURI(string str)\n\t\t{\n\t\t\tif(!str.Unescape(out var r)) throw new ArgumentException(\"Invalid diff delta string escaping.\");\n\t\t\treturn r;\n\t\t}\n#else //uses System.Web.dll\n\t\t/**\n\t\t * Encodes a string with URI-style % escaping.\n\t\t * Compatible with JavaScript's encodeURI function.\n\t\t *\n\t\t * @param str The string to encode.\n\t\t * @return The encoded string.\n\t\t */\n\t\tstatic string encodeURI(string str)\n\t\t{\n\t\t\t// C# is overzealous in the replacements.  Walk back on a few.\n\t\t\treturn new StringBuilder(HttpUtility.UrlEncode(str))\n\t\t\t\t.Replace('+', ' ').Replace(\"%20\", \" \").Replace(\"%21\", \"!\")\n\t\t\t\t.Replace(\"%2a\", \"*\").Replace(\"%27\", \"'\").Replace(\"%28\", \"(\")\n\t\t\t\t.Replace(\"%29\", \")\").Replace(\"%3b\", \";\").Replace(\"%2f\", \"/\")\n\t\t\t\t.Replace(\"%3f\", \"?\").Replace(\"%3a\", \":\").Replace(\"%40\", \"@\")\n\t\t\t\t.Replace(\"%26\", \"&\").Replace(\"%3d\", \"=\").Replace(\"%2b\", \"+\")\n\t\t\t\t.Replace(\"%24\", \"$\").Replace(\"%2c\", \",\").Replace(\"%23\", \"#\")\n\t\t\t\t.Replace(\"%7e\", \"~\")\n\t\t\t\t.ToString();\n\t\t}\n\n\t\tstatic string decodeURI(string str)\n\t\t{\n\t\t\treturn HttpUtility.UrlDecode(str.Replace(\"+\", \"%2b\"));\n\t\t}\n#endif\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/xUtil/Libs/EnglishPorter2Stemmer.cs",
    "content": "//https://github.com/nemec/porter2-stemmer\n\n#pragma warning disable 1591 //no XML comment\n\nnamespace Libs.Porter2Stemmer;\n\n/// <summary>\n/// Based off of the improved Porter2 algorithm:\n/// http://snowball.tartarus.org/algorithms/english/stemmer.html\n/// </summary>\npublic class EnglishPorter2Stemmer {\n\n\tprivate readonly char[] _alphabet =\n\t\tEnumerable\n\t\t\t.Range('a', 'z' - 'a' + 1)\n\t\t\t.Select(c => (char)c)\n\t\t\t.Concat(new[] { '\\'' }).ToArray();\n\tpublic char[] Alphabet { get { return _alphabet; } }\n\n\tprivate readonly char[] _vowels = \"aeiouy\".ToArray();\n\tpublic char[] Vowels { get { return _vowels; } }\n\n\tprivate readonly string[] _doubles =\n\t\t{ \"bb\", \"dd\", \"ff\", \"gg\", \"mm\", \"nn\", \"pp\", \"rr\", \"tt\" };\n\tpublic string[] Doubles { get { return _doubles; } }\n\n\tprivate readonly char[] _liEndings = \"cdeghkmnrt\".ToArray();\n\tpublic char[] LiEndings { get { return _liEndings; } }\n\n\tprivate readonly char[] _nonShortConsonants = \"wxY\".ToArray();\n\n\tprivate readonly Dictionary<string, string> _exceptions = new Dictionary<string, string>\n\t\t{\n\t\t\t\t{\"skis\", \"ski\"},\n\t\t\t\t{\"skies\", \"sky\"},\n\t\t\t\t{\"dying\", \"die\"},\n\t\t\t\t{\"lying\", \"lie\"},\n\t\t\t\t{\"tying\", \"tie\"},\n\t\t\t\t{\"idly\", \"idl\"},\n\t\t\t\t{\"gently\", \"gentl\"},\n\t\t\t\t{\"ugly\", \"ugli\"},\n\t\t\t\t{\"early\", \"earli\"},\n\t\t\t\t{\"only\", \"onli\"},\n\t\t\t\t{\"singly\", \"singl\"},\n\t\t\t\t{\"sky\", \"sky\"},\n\t\t\t\t{\"news\", \"news\"},\n\t\t\t\t{\"howe\", \"howe\"},\n\t\t\t\t{\"atlas\", \"atlas\"},\n\t\t\t\t{\"cosmos\", \"cosmos\"},\n\t\t\t\t{\"bias\", \"bias\"},\n\t\t\t\t{\"andes\", \"andes\"}\n\t\t\t};\n\n\tprivate readonly string[] _exceptionsPart2 = new[]\n\t\t{\n\t\t\t\t\"inning\", \"outing\", \"canning\", \"herring\", \"earring\",\n\t\t\t\t\"proceed\", \"exceed\", \"succeed\"\n\t\t\t};\n\n\tprivate readonly string[] _exceptionsRegion1 = new[]\n\t\t{\n\t\t\t\t\"gener\", \"arsen\", \"commun\"\n\t\t\t};\n\n\tpublic string Stem(string word) {\n\t\tif (word.Length <= 2) {\n\t\t\treturn word;\n\t\t}\n\n\t\tword = TrimStartingApostrophe(word.ToLowerInvariant());\n\n\t\tstring excpt;\n\t\tif (_exceptions.TryGetValue(word, out excpt)) {\n\t\t\treturn excpt;\n\t\t}\n\n\t\tword = MarkYsAsConsonants(word);\n\n\t\tvar r1 = GetRegion1(word);\n\t\tvar r2 = GetRegion2(word);\n\n\t\tword = Step0RemoveSPluralSuffix(word);\n\t\tword = Step1ARemoveOtherSPluralSuffixes(word);\n\n\t\tif (_exceptionsPart2.Contains(word)) {\n\t\t\treturn word;\n\t\t}\n\n\t\tword = Step1BRemoveLySuffixes(word, r1);\n\t\tword = Step1CReplaceSuffixYWithIIfPreceededWithConsonant(word);\n\t\tword = Step2ReplaceSuffixes(word, r1);\n\t\tword = Step3ReplaceSuffixes(word, r1, r2);\n\t\tword = Step4RemoveSomeSuffixesInR2(word, r2);\n\t\tword = Step5RemoveEorLSuffixes(word, r1, r2);\n\n\t\treturn word.ToLowerInvariant();\n\t}\n\n\tprivate bool IsVowel(char c) {\n\t\treturn Vowels.Contains(c);\n\t}\n\n\tprivate bool IsConsonant(char c) {\n\t\treturn !Vowels.Contains(c);\n\t}\n\n\tprivate static bool SuffixInR1(string word, int r1, string suffix) {\n\t\treturn r1 <= word.Length - suffix.Length;\n\t}\n\n\tprivate bool SuffixInR2(string word, int r2, string suffix) {\n\t\treturn r2 <= word.Length - suffix.Length;\n\t}\n\n\tprivate static string ReplaceSuffix(string word, string oldSuffix, string newSuffix = null) {\n\t\tif (oldSuffix != null) {\n\t\t\tword = word.Substring(0, word.Length - oldSuffix.Length);\n\t\t}\n\n\t\tif (newSuffix != null) {\n\t\t\tword += newSuffix;\n\t\t}\n\t\treturn word;\n\t}\n\n\tprivate static bool TryReplace(string word, string oldSuffix, string newSuffix, out string final) {\n\t\tif (word.Contains(oldSuffix)) {\n\t\t\tfinal = ReplaceSuffix(word, oldSuffix, newSuffix);\n\t\t\treturn true;\n\t\t}\n\t\tfinal = word;\n\t\treturn false;\n\t}\n\n\t/// <summary>\n\t/// The English stemmer treats apostrophe as a letter, removing it from the beginning of a word, where it might have stood for an opening quote, from the end of the word, where it might have stood for a closing quote, or been an apostrophe following s.\n\t/// </summary>\n\t/// <param name=\"word\"></param>\n\t/// <returns></returns>\n\tprivate static string TrimStartingApostrophe(string word) {\n\t\tif (word.StartsWith(\"'\")) {\n\t\t\tword = word.Substring(1);\n\t\t}\n\t\treturn word;\n\t}\n\n\t/// <summary>\n\t/// R1 is the region after the first non-vowel following a vowel, or the end of the word if there is no such non-vowel. \n\t/// </summary>\n\t/// <param name=\"word\"></param>\n\t/// <returns></returns>\n\tpublic int GetRegion1(string word) {\n\t\t// Exceptional forms\n\t\tforeach (var except in _exceptionsRegion1.Where(word.StartsWith)) {\n\t\t\treturn except.Length;\n\t\t}\n\t\treturn GetRegion(word, 0);\n\t}\n\n\t/// <summary>\n\t/// R2 is the region after the first non-vowel following a vowel in R1, or the end of the word if there is no such non-vowel. \n\t/// </summary>\n\t/// <param name=\"word\"></param>\n\t/// <returns></returns>\n\tpublic int GetRegion2(string word) {\n\t\tvar r1 = GetRegion1(word);\n\t\treturn GetRegion(word, r1);\n\t}\n\n\tprivate int GetRegion(string word, int begin) {\n\t\tvar foundVowel = false;\n\t\tfor (var i = begin; i < word.Length; i++) {\n\t\t\tif (IsVowel(word[i])) {\n\t\t\t\tfoundVowel = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (foundVowel && IsConsonant(word[i])) {\n\t\t\t\treturn i + 1;\n\t\t\t}\n\t\t}\n\n\t\treturn word.Length;\n\t}\n\n\t/// <summary>\n\t/// Define a short syllable in a word as either (a) a vowel followed \n\t/// by a non-vowel other than w, x or Y and preceded by a non-vowel, \n\t/// or * (b) a vowel at the beginning of the word followed by a non-vowel. \n\t/// </summary>\n\t/// <param name=\"word\"></param>\n\t/// <returns></returns>\n\tpublic bool EndsInShortSyllable(string word) {\n\t\tif (word.Length < 2) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// a vowel at the beginning of the word followed by a non-vowel\n\t\tif (word.Length == 2) {\n\t\t\treturn IsVowel(word[0]) && IsConsonant(word[1]);\n\t\t}\n\n\t\treturn IsVowel(word[word.Length - 2])\n\t\t\t   && IsConsonant(word[word.Length - 1])\n\t\t\t   && !_nonShortConsonants.Contains(word[word.Length - 1])\n\t\t\t   && IsConsonant(word[word.Length - 3]);\n\t}\n\n\t/// <summary>\n\t/// A word is called short if it ends in a short syllable, and if R1 is null.\n\t/// </summary>\n\t/// <param name=\"word\"></param>\n\t/// <returns></returns>\n\tpublic bool IsShortWord(string word) {\n\t\treturn EndsInShortSyllable(word) && GetRegion1(word) == word.Length;\n\t}\n\n\t/// <summary>\n\t/// Set initial y, or y after a vowel, to Y\n\t/// </summary>\n\t/// <param name=\"word\"></param>\n\t/// <returns></returns>\n\tpublic string MarkYsAsConsonants(string word) {\n\t\tvar chars = word.ToCharArray();\n\t\tfor (var i = 0; i < chars.Length; i++) {\n\t\t\tif (i == 0) {\n\t\t\t\tif (chars[i] == 'y') {\n\t\t\t\t\tchars[i] = 'Y';\n\t\t\t\t}\n\t\t\t} else if (Vowels.Contains(chars[i - 1]) && chars[i] == 'y') {\n\t\t\t\tchars[i] = 'Y';\n\t\t\t}\n\t\t}\n\t\treturn new string(chars);\n\t}\n\n\tpublic string Step0RemoveSPluralSuffix(string word) {\n\t\t// Ordered from longest to shortest\n\t\tvar suffixes = new[] { \"'s'\", \"'s\", \"'\" };\n\t\tforeach (var suffix in suffixes) {\n\t\t\tif (word.EndsWith(suffix)) {\n\t\t\t\treturn ReplaceSuffix(word, suffix);\n\t\t\t}\n\t\t}\n\t\treturn word;\n\t}\n\n\tpublic string Step1ARemoveOtherSPluralSuffixes(string word) {\n\t\tif (word.EndsWith(\"sses\")) {\n\t\t\treturn ReplaceSuffix(word, \"sses\", \"ss\");\n\t\t}\n\t\tif (word.EndsWith(\"ied\") || word.EndsWith(\"ies\")) {\n\t\t\tvar restOfWord = word.Substring(0, word.Length - 3);\n\t\t\tif (word.Length > 4) {\n\t\t\t\treturn restOfWord + \"i\";\n\t\t\t}\n\t\t\treturn restOfWord + \"ie\";\n\t\t}\n\t\tif (word.EndsWith(\"us\") || word.EndsWith(\"ss\")) {\n\t\t\treturn word;\n\t\t}\n\t\tif (word.EndsWith(\"s\")) {\n\t\t\tif (word.Length < 3) {\n\t\t\t\treturn word;\n\t\t\t}\n\n\t\t\t// Skip both the last letter ('s') and the letter before that\n\t\t\tfor (var i = 0; i < word.Length - 2; i++) {\n\t\t\t\tif (IsVowel(word[i])) {\n\t\t\t\t\treturn word.Substring(0, word.Length - 1);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn word;\n\t}\n\n\tpublic string Step1BRemoveLySuffixes(string word, int r1) {\n\t\tforeach (var suffix in new[] { \"eedly\", \"eed\" }.Where(word.EndsWith)) {\n\t\t\tif (SuffixInR1(word, r1, suffix)) {\n\t\t\t\treturn ReplaceSuffix(word, suffix, \"ee\");\n\t\t\t}\n\t\t\treturn word;\n\t\t}\n\n\t\tforeach (var suffix in new[] { \"ed\", \"edly\", \"ing\", \"ingly\" }.Where(word.EndsWith)) {\n\t\t\tvar trunc = ReplaceSuffix(word, suffix);//word.Substring(0, word.Length - suffix.Length);\n\t\t\tif (trunc.Any(IsVowel)) {\n\t\t\t\tif (new[] { \"at\", \"bl\", \"iz\" }.Any(trunc.EndsWith)) {\n\t\t\t\t\treturn trunc + \"e\";\n\t\t\t\t}\n\t\t\t\tif (Doubles.Any(trunc.EndsWith)) {\n\t\t\t\t\treturn trunc.Substring(0, trunc.Length - 1);\n\t\t\t\t}\n\t\t\t\tif (IsShortWord(trunc)) {\n\t\t\t\t\treturn trunc + \"e\";\n\t\t\t\t}\n\t\t\t\treturn trunc;\n\t\t\t}\n\t\t\treturn word;\n\t\t}\n\n\t\treturn word;\n\t}\n\n\tpublic string Step1CReplaceSuffixYWithIIfPreceededWithConsonant(string word) {\n\t\tif ((word.EndsWith(\"y\") || word.EndsWith(\"Y\"))\n\t\t\t&& word.Length > 2\n\t\t\t&& IsConsonant(word[word.Length - 2])) {\n\t\t\treturn word.Substring(0, word.Length - 1) + \"i\";\n\t\t}\n\t\treturn word;\n\t}\n\n\tpublic string Step2ReplaceSuffixes(string word, int r1) {\n\t\tvar suffixes = new Dictionary<string, string>\n\t\t\t{\n\t\t\t\t\t{\"ization\", \"ize\"},\n\t\t\t\t\t{\"ational\", \"ate\"},\n\t\t\t\t\t{\"ousness\", \"ous\"},\n\t\t\t\t\t{\"iveness\", \"ive\"},\n\t\t\t\t\t{\"fulness\", \"ful\"},\n\t\t\t\t\t{\"tional\", \"tion\"},\n\t\t\t\t\t{\"lessli\", \"less\"},\n\t\t\t\t\t{\"biliti\", \"ble\"},\n\t\t\t\t\t{\"entli\", \"ent\"},\n\t\t\t\t\t{\"ation\", \"ate\"},\n\t\t\t\t\t{\"alism\", \"al\"},\n\t\t\t\t\t{\"aliti\", \"al\"},\n\t\t\t\t\t{\"fulli\", \"ful\"},\n\t\t\t\t\t{\"ousli\", \"ous\"},\n\t\t\t\t\t{\"iviti\", \"ive\"},\n\t\t\t\t\t{\"enci\", \"ence\"},\n\t\t\t\t\t{\"anci\", \"ance\"},\n\t\t\t\t\t{\"abli\", \"able\"},\n\t\t\t\t\t{\"izer\", \"ize\"},\n\t\t\t\t\t{\"ator\", \"ate\"},\n\t\t\t\t\t{\"alli\", \"al\"},\n\t\t\t\t\t{\"bli\", \"ble\"}\n\t\t\t\t};\n\t\tforeach (var suffix in suffixes) {\n\t\t\tif (word.EndsWith(suffix.Key)) {\n\t\t\t\tstring final;\n\t\t\t\tif (SuffixInR1(word, r1, suffix.Key)\n\t\t\t\t\t&& TryReplace(word, suffix.Key, suffix.Value, out final)) {\n\t\t\t\t\treturn final;\n\t\t\t\t}\n\t\t\t\treturn word;\n\t\t\t}\n\t\t}\n\n\t\tif (word.EndsWith(\"ogi\")\n\t\t\t&& SuffixInR1(word, r1, \"ogi\")\n\t\t\t&& word[word.Length - 4] == 'l') {\n\t\t\treturn ReplaceSuffix(word, \"ogi\", \"og\");\n\t\t}\n\n\t\tif (word.EndsWith(\"li\") & SuffixInR1(word, r1, \"li\")) {\n\t\t\tif (LiEndings.Contains(word[word.Length - 3])) {\n\t\t\t\treturn ReplaceSuffix(word, \"li\");\n\t\t\t}\n\t\t}\n\n\t\treturn word;\n\t}\n\n\tpublic string Step3ReplaceSuffixes(string word, int r1, int r2) {\n\t\tvar suffixes = new Dictionary<string, string>\n\t\t\t{\n\t\t\t\t\t{\"ational\", \"ate\"},\n\t\t\t\t\t{\"tional\", \"tion\"},\n\t\t\t\t\t{\"alize\", \"al\"},\n\t\t\t\t\t{\"icate\", \"ic\"},\n\t\t\t\t\t{\"iciti\", \"ic\"},\n\t\t\t\t\t{\"ical\", \"ic\"},\n\t\t\t\t\t{\"ful\", null},\n\t\t\t\t\t{\"ness\", null}\n\t\t\t\t};\n\t\tforeach (var suffix in suffixes.Where(s => word.EndsWith(s.Key))) {\n\t\t\tstring final;\n\t\t\tif (SuffixInR1(word, r1, suffix.Key)\n\t\t\t\t&& TryReplace(word, suffix.Key, suffix.Value, out final)) {\n\t\t\t\treturn final;\n\t\t\t}\n\t\t}\n\n\t\tif (word.EndsWith(\"ative\")) {\n\t\t\tif (SuffixInR1(word, r1, \"ative\") && SuffixInR2(word, r2, \"ative\")) {\n\t\t\t\treturn ReplaceSuffix(word, \"ative\", null);\n\t\t\t}\n\t\t}\n\n\t\treturn word;\n\t}\n\n\tpublic string Step4RemoveSomeSuffixesInR2(string word, int r2) {\n\t\tforeach (var suffix in new[]\n\t\t\t{\n\t\t\t\t\t\"al\", \"ance\", \"ence\", \"er\", \"ic\", \"able\", \"ible\", \"ant\",\n\t\t\t\t\t\"ement\", \"ment\", \"ent\", \"ism\", \"ate\", \"iti\", \"ous\",\n\t\t\t\t\t\"ive\", \"ize\"\n\t\t\t\t}) {\n\t\t\tif (word.EndsWith(suffix)) {\n\t\t\t\tif (SuffixInR2(word, r2, suffix)) {\n\t\t\t\t\treturn ReplaceSuffix(word, suffix);\n\t\t\t\t}\n\t\t\t\treturn word;\n\t\t\t}\n\t\t}\n\n\t\tif (word.EndsWith(\"ion\") &&\n\t\t\tSuffixInR2(word, r2, \"ion\") &&\n\t\t\tnew[] { 's', 't' }.Contains(word[word.Length - 4])) {\n\t\t\treturn ReplaceSuffix(word, \"ion\");\n\t\t}\n\t\treturn word;\n\t}\n\n\tpublic string Step5RemoveEorLSuffixes(string word, int r1, int r2) {\n\t\tif (word.EndsWith(\"e\") &&\n\t\t\t(SuffixInR2(word, r2, \"e\") ||\n\t\t\t\t(SuffixInR1(word, r1, \"e\") &&\n\t\t\t\t\t!EndsInShortSyllable(ReplaceSuffix(word, \"e\"))))) {\n\t\t\treturn ReplaceSuffix(word, \"e\");\n\t\t}\n\n\t\tif (word.EndsWith(\"l\") &&\n\t\t\tSuffixInR2(word, r2, \"l\") &&\n\t\t\tword.Length > 1 &&\n\t\t\tword[word.Length - 2] == 'l') {\n\t\t\treturn ReplaceSuffix(word, \"l\");\n\t\t}\n\n\t\treturn word;\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/xUtil/MetaCommentsParser.cs",
    "content": "\nnamespace LA;\n\n/// <seealso cref=\"MetaComments.FindMetaComments\"/>\n/// <seealso cref=\"MetaComments.EnumOptions\"/>\nclass MetaCommentsParser {\n\tFileNode _fn;\n\tpublic string role, ifRunning, uac, platform,\n\t\toptimize, warningLevel, noWarnings, nullable, testInternal, define, preBuild, postBuild,\n\t\toutputPath, console, icon, manifest, sign, xmlDoc, miscFlags, noRef, startFaster;\n\tList<string> _pr, _r, _com, _nuget, _c, _resource, _file, _disabled;\n\t\n\tpublic List<string> pr => _pr ??= new();\n\tpublic List<string> r => _r ??= new();\n\tpublic List<string> com => _com ??= new();\n\tpublic List<string> nuget => _nuget ??= new();\n\tpublic List<string> c => _c ??= new();\n\tpublic List<string> resource => _resource ??= new();\n\tpublic List<string> file => _file ??= new();\n\t\n\tpublic MetaCommentsParser(FileNode f) : this(f.GetCurrentText(out var s) ? s : \"\") { _fn = f; }\n\t\n\tpublic MetaCommentsParser(string code) {\n\t\tvar meta = MetaComments.FindMetaComments(code);\n\t\tif (meta.end == 0) return;\n\t\tMetaRange = meta;\n\t\tforeach (var t in MetaComments.EnumOptions(code, meta)) _ParseOption(t.Name, t.Value);\n\t\tMultiline = code[meta.start..meta.end].Contains('\\n');\n\t}\n\t\n\tpublic StartEnd MetaRange { get; private set; }\n\t\n\tvoid _ParseOption(string name, string value) {\n\t\tswitch (name) {\n\t\tcase \"role\": role = value; break;\n\t\tcase \"outputPath\": outputPath = value; break;\n\t\tcase \"ifRunning\": ifRunning = value; break;\n\t\tcase \"uac\": uac = value; break;\n\t\tcase \"platform\": platform = value; break;\n\t\tcase \"bit32\": platform = value is \"true\" or \"!false\" ? \"x86\" : \"x64\"; break; //fbc\n\t\tcase \"optimize\": optimize = value; break;\n\t\tcase \"warningLevel\": warningLevel = value; break;\n\t\tcase \"noWarnings\": noWarnings = value; break;\n\t\tcase \"nullable\": nullable = value; break;\n\t\tcase \"testInternal\": testInternal = value; break;\n\t\tcase \"define\": define = value; break;\n\t\tcase \"preBuild\": preBuild = value; break;\n\t\tcase \"postBuild\": postBuild = value; break;\n\t\tcase \"console\": console = value; break;\n\t\tcase \"icon\": icon = value; break;\n\t\tcase \"manifest\": manifest = value; break;\n\t\tcase \"sign\": sign = value; break;\n\t\tcase \"xmlDoc\": xmlDoc = value; break;\n\t\tcase \"miscFlags\": miscFlags = value; break;\n\t\tcase \"noRef\": noRef = value; break;\n\t\tcase \"startFaster\": startFaster = value; break;\n\t\tcase \"pr\": pr.Add(value); break;\n\t\tcase \"r\": r.Add(value); break;\n\t\tcase \"com\": com.Add(value); break;\n\t\tcase \"nuget\": nuget.Add(value.Replace('/', '\\\\')); break;\n\t\tcase \"c\": c.Add(value); break;\n\t\tcase \"resource\": resource.Add(value); break;\n\t\tcase \"file\": file.Add(value); break;\n\t\tcase null: (_disabled ??= new()).Add(value); break;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Formats metacomments string \"/*/ ... /*/\".\n\t/// Returns \"\" if there are no options.\n\t/// </summary>\n\tpublic string Format(string prepend, string append) {\n\t\t//prepare to make relative paths\n\t\tstring dir = null;\n\t\tif (_fn != null) {\n\t\t\tdir = _fn.ItemPath;\n\t\t\tint i = dir.LastIndexOf('\\\\') + 1;\n\t\t\tif (i > 1) dir = dir.Remove(i); else dir = null;\n\t\t}\n\t\t\n\t\tvar sep = Multiline ? \"\\r\\n\" : \"; \";\n\t\tvar b = new StringBuilder();\n\t\tb.Append(prepend).Append(Multiline ? \"/*/\\r\\n\" : \"/*/ \");\n\t\t_Append(\"role\", role); //must be the first\n\t\t\n\t\t_Append(\"ifRunning\", ifRunning);\n\t\t_Append(\"uac\", uac);\n\t\t\n\t\t_Append(\"optimize\", optimize);\n\t\t_Append(\"define\", define);\n\t\t_Append(\"warningLevel\", warningLevel);\n\t\t_Append(\"noWarnings\", noWarnings);\n\t\t_Append(\"nullable\", nullable);\n\t\t_Append(\"testInternal\", testInternal);\n\t\t_Append(\"preBuild\", preBuild, true);\n\t\t_Append(\"postBuild\", postBuild, true);\n\t\t\n\t\t_Append(\"outputPath\", outputPath);\n\t\t_Append(\"icon\", icon, true);\n\t\t_Append(\"manifest\", manifest, true);\n\t\t_Append(\"sign\", sign, true);\n\t\t_Append(\"console\", console);\n\t\t_Append(\"platform\", platform);\n\t\t_Append(\"xmlDoc\", xmlDoc);\n\t\t\n\t\t_Append(\"miscFlags\", miscFlags);\n\t\t_Append(\"noRef\", noRef);\n\t\t_Append(\"startFaster\", startFaster);\n\t\t\n\t\t_AppendList(\"pr\", _pr);\n\t\t_AppendList(\"r\", _r);\n\t\t_AppendList(\"com\", _com, true);\n\t\t_AppendList(\"nuget\", _nuget);\n\t\t_AppendList(\"c\", _c, true);\n\t\t_AppendList(\"resource\", _resource, true);\n\t\t_AppendList(\"file\", _file, true);\n\t\t\n\t\tif (_disabled != null) {\n\t\t\tb.AppendJoin(sep, _disabled).Append(sep);\n\t\t}\n\t\t\n\t\tif (b.Length <= 5) return \"\";\n\t\tb.Append(\"/*/\");\n\t\tb.Append(append);\n\t\treturn b.ToString();\n\t\t\n\t\tvoid _Append(string name, string value, bool relativePath = false) {\n\t\t\tif (value != null) {\n\t\t\t\tif (relativePath && dir != null && value.Starts(dir, true)) value = value[dir.Length..];\n\t\t\t\tb.Append(name).Append(' ').Append(value).Append(sep);\n\t\t\t}\n\t\t}\n\t\t\n\t\tvoid _AppendList(string name, List<string> a, bool relativePath = false) {\n\t\t\tif (a != null) foreach (var v in a.Distinct()) _Append(name, v, relativePath);\n\t\t}\n\t}\n\t\n\tpublic bool Multiline { get; set; }\n\t\n\t/// <summary>\n\t/// Adds or updates meta comments in the active document.\n\t/// </summary>\n\t/// <returns>true if changed documnt text.</returns>\n\tpublic bool Apply() {\n\t\tvar doc = Panels.Editor.ActiveDoc;\n\t\tvar f = doc.EFile;\n\t\tvar code = doc.aaaText;\n\t\tvar meta = MetaComments.FindMetaComments(code);\n\t\tstring prepend = null, append = null;\n\t\tif (meta.end == 0) {\n\t\t\tif (code.RxMatch(@\"^(///.*\\R)+(?=\\R|$)\", 0, out RXGroup g)) { //description\n\t\t\t\tmeta = new(g.End, g.End);\n\t\t\t\tprepend = \"\\r\\n\";\n\t\t\t}\n\t\t\tappend = (f.IsScript && code.Eq(meta.end, \"//.\")) ? \" \" : \"\\r\\n\";\n\t\t}\n\t\tvar s = Format(prepend, append);\n\t\t\n\t\tif (s.Length == 0) {\n\t\t\tif (meta.end == 0) return false;\n\t\t\twhile (meta.end < code.Length && code[meta.end] <= ' ') meta.end++;\n\t\t} else if (s.Length == meta.end - meta.start) {\n\t\t\tif (s == doc.aaaRangeText(true, meta.start, meta.end)) return false; //did not change\n\t\t}\n\t\t\n\t\tdoc.EReplaceTextGently(meta.start, meta.end, s);\n\t\treturn true;\n\t}\n\t\n\t/// <summary>\n\t/// Merges meta options into this from <i>m</i>.\n\t/// If an option already exists in this: if it's a list (like c or define), adds list items that don't already exist; else replaces the value.\n\t/// </summary>\n\tpublic void Merge(MetaCommentsParser m) {\n\t\t_List(ref _pr, m._pr);\n\t\t_List(ref _r, m._r);\n\t\t_List(ref _com, m._com);\n\t\t_List(ref _nuget, m._nuget);\n\t\t_List(ref _c, m._c);\n\t\t_List(ref _resource, m._resource);\n\t\t_List(ref _file, m._file);\n\t\t\n\t\t_CommaList(ref define, m.define);\n\t\t_CommaList(ref noWarnings, m.noWarnings);\n\t\t_CommaList(ref testInternal, m.testInternal);\n\t\t\n\t\t_Single(ref platform, m.platform);\n\t\t_Single(ref console, m.console);\n\t\t_Single(ref icon, m.icon);\n\t\t_Single(ref ifRunning, m.ifRunning);\n\t\t_Single(ref manifest, m.manifest);\n\t\t_Single(ref miscFlags, m.miscFlags);\n\t\t_Single(ref noRef, m.noRef);\n\t\t_Single(ref nullable, m.nullable);\n\t\t_Single(ref optimize, m.optimize);\n\t\t_Single(ref outputPath, m.outputPath);\n\t\t_Single(ref postBuild, m.postBuild);\n\t\t_Single(ref preBuild, m.preBuild);\n\t\t_Single(ref role, m.role);\n\t\t_Single(ref sign, m.sign);\n\t\t_Single(ref startFaster, m.startFaster);\n\t\t_Single(ref uac, m.uac);\n\t\t_Single(ref warningLevel, m.warningLevel);\n\t\t_Single(ref xmlDoc, m.xmlDoc);\n\t\t\n\t\tstatic void _List(ref List<string> a, List<string> b) {\n\t\t\tif (b.NE_()) return;\n\t\t\ta ??= new();\n\t\t\tforeach (var s in b) {\n\t\t\t\tif (!a.Contains(s, StringComparer.OrdinalIgnoreCase)) a.Add(s);\n\t\t\t}\n\t\t}\n\t\t\n\t\tstatic void _CommaList(ref string a, string b, bool ignoreCase = false) {\n\t\t\tif (b.NE()) return;\n\t\t\tif (a.NE()) a = b;\n\t\t\telse {\n\t\t\t\tvar hs = a.SplitHS_(',', ignoreCase);\n\t\t\t\tforeach (var s in b.Split_(',')) hs.Add(s);\n\t\t\t\ta = string.Join(',', hs);\n\t\t\t}\n\t\t}\n\t\t\n\t\tstatic void _Single(ref string a, string b) {\n\t\t\tif (b.NE()) return;\n\t\t\ta = b;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au.Editor/xUtil/NugetDownloader.cs",
    "content": "using System.IO.Compression;\nusing System.Xml.Linq;\n\nnamespace LA;\n\nstatic class NugetDownloader {\n#if USED\n\t/// <summary>\n\t/// Downloads a NuGet package to a temp directory.\n\t/// </summary>\n\t/// <param name=\"package\">Package name.</param>\n\t/// <param name=\"version\">Package version. If null, gets the newest version compatible with the .NET Runtime used by this process.</param>\n\t/// <returns>Path of the downloaded nupkg file.</returns>\n\t/// <exception cref=\"OperationCanceledException\">User-canceled.</exception>\n\t/// <exception cref=\"Exception\">Failed.</exception>\n\t/// <remarks>\n\t/// If the package already exists in the temp directory, does not download again (just returns its path).\n\t/// </remarks>\n\tpublic static string Download(string package, string version = null) {\n\t\tpackage = package.Lower();\n\t\tversion ??= _GetLatestCompatibleVersion(package);\n\t\tversion = version.Lower();\n\t\tstring fileName = $\"{package}.{version}.nupkg\";\n\t\tstring tempDir = folders.ThisAppTemp + \"download\";\n\t\tstring filePath = tempDir + \"\\\\\" + fileName;\n\t\t\n\t\tbool exists = false;\n\t\tif (filesystem.exists(filePath)) {\n\t\t\ttry {\n\t\t\t\tusing (ZipFile.OpenRead(filePath)) { }\n\t\t\t\texists = true;\n\t\t\t}\n\t\t\tcatch (InvalidDataException) { filesystem.delete(filePath); } //probably partially downloaded\n\t\t}\n\t\t\n\t\tif (!exists) {\n\t\t\t//delete old\n\t\t\ttry {\n\t\t\t\tforeach (var v in filesystem.enumFiles(tempDir, $\"{package}.*.nupkg\")) {\n\t\t\t\t\tfilesystem.delete(v.FullPath, FDFlags.CanFail);\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch { } //eg the dir still does not exist\n\t\t\t\n\t\t\tstring nupkgUrl = $\"https://api.nuget.org/v3-flatcontainer/{package}/{version}/{fileName}\";\n\t\t\tprint.it($\"Downloading from NuGet: {fileName}\");\n\t\t\tbool ok = false;\n\t\t\ttry {\n\t\t\t\tif (!internet.http.Get(nupkgUrl, true).Download(filePath)) throw new OperationCanceledException();\n\t\t\t\tok = true;\n\t\t\t}\n\t\t\tfinally { if (!ok) filesystem.delete(filePath); } //if failed, delete partially downloaded file\n\t\t}\n\t\t\n\t\treturn filePath;\n\t}\n\t\n\t/// <summary>\n\t/// Extracts a nupkg file.\n\t/// </summary>\n\t/// <param name=\"nupkgPath\">Path of a nupkg file.</param>\n\t/// <param name=\"extractDir\">Base directory to extract files to. Creates if does not exist; else overwrites existing same files and does not delete other files.</param>\n\t/// <param name=\"rxPath\">If not null, extracts only files whose relative path matches this regex. Final path will be the matched part; eg can be used <c>\\K</c> to remove part of path (flatten).</param>\n\t/// <exception cref=\"Exception\">Failed.</exception>\n\tpublic static void Extract(string nupkgPath, string extractDir, regexp rxPath = null) {\n\t\tprint.it($\"Extracting NuGet package: {pathname.getName(nupkgPath)}\");\n\t\tfilesystem.createDirectory(extractDir);\n\t\tusing var z = ZipFile.OpenRead(nupkgPath);\n\t\tforeach (var e in z.Entries) {\n\t\t\tvar relPath = e.FullName;\n\t\t\tif (rxPath != null && !rxPath.Match(relPath, 0, out relPath)) continue;\n\t\t\tvar s = extractDir + \"\\\\\" + relPath;\n\t\t\tif (relPath.Contains('/')) filesystem.createDirectoryFor(s);\n\t\t\te.ExtractToFile(s, overwrite: true);\n\t\t}\n\t}\n\t\n\t//This was used before the normal download URLs were known. Now used DotnetUtil.DownloadNetRuntimesForOtherArch.\n\t///// <summary>\n\t///// Downloads and extracts both .NET runtimes (core and desktop) for CPU architecture x64/ARM64 other than of this process.\n\t///// </summary>\n\t///// <param name=\"dir\">Extract both to this directory.</param>\n\t///// <param name=\"portable\">Extract to <i>dir</i> subdirectory <c>\"dotnet\"</c> (if x64) or <c>\"dotnetARM\"</c> (if ARM64). Delete old subdirectory.</param>\n\t///// <exception cref=\"OperationCanceledException\">User-canceled.</exception>\n\t///// <exception cref=\"Exception\">Failed.</exception>\n\t//public static void DownloadNetRuntimesForOtherArch(string dir, bool portable) {\n\t//\tbool forArm = !osVersion.isArm64Process;\n\t\n\t//\tif (portable) {\n\t//\t\tdir = dir + \"\\\\dotnet\" + (forArm ? \"ARM\" : null);\n\t//\t\tfilesystem.delete(dir);\n\t//\t\tfilesystem.createDirectory(dir);\n\t//\t}\n\t\n\t//\tstring arch = forArm ? \"arm64\" : \"x64\";\n\t//\tstring version = Environment.Version.ToString();\n\t//\tregexp rx = new(@\"(?i)^runtimes/win-\\w+/(?:lib/net[^/]+|native)/\\K.+\");\n\t//\tvar f1 = Download($\"Microsoft.NETCore.App.Runtime.win-{arch}\", version);\n\t//\tvar f2 = Download($\"Microsoft.WindowsDesktop.App.Runtime.win-{arch}\", version);\n\t//\tExtract(f1, dir, rx);\n\t//\tExtract(f2, dir, rx);\n\t//}\n\t\n\tstatic string _GetLatestCompatibleVersion(string package) {\n\t\tvar runtimeVersion = Environment.Version.ToString(2);\n\t\tpackage = package.Lower();\n\t\t\n\t\tvar j = internet.http.Get($\"https://api.nuget.org/v3-flatcontainer/{package}/index.json\").Json();\n\t\tvar versions = j[\"versions\"].AsArray().Select(v => v.ToString()).Reverse();\n\t\t\n\t\tforeach (var v in versions) {\n\t\t\t//print.it(v);\n\t\t\tstring nuspecUrl = $\"https://api.nuget.org/v3-flatcontainer/{package}/{v}/{package}.nuspec\";\n\t\t\tvar nuspec = XElement.Parse(internet.http.Get(nuspecUrl).Text());\n\t\t\t//print.it(nuspec);\n\t\t\tvar ns = nuspec.GetDefaultNamespace();\n\t\t\tvar deps = nuspec.Element(ns + \"metadata\").Element(ns + \"dependencies\");\n\t\t\tif (deps == null) return v;\n\t\t\tforeach (var g in deps.Elements(ns + \"group\")) {\n\t\t\t\tvar s = g.Attr(\"targetFramework\");\n\t\t\t\t//print.it(s);\n\t\t\t\tif (s.Starts(\"net\", true)) {\n\t\t\t\t\tif (s.RxMatch(@\"^(?i)net(\\d+\\.\\d+)(?=-windows|$)\", 1, out s))\n\t\t\t\t\t\tif (s.CompareTo(runtimeVersion) <= 0) return v;\n\t\t\t\t} else {\n\t\t\t\t\tif (s.Starts(\".NETStandard\", true) || s.Starts(\".NETCoreApp\", true)) return v;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n#endif\n}\n"
  },
  {
    "path": "Au.Editor/xUtil/RegexParser.cs",
    "content": "using EStyle = LA.SciTheme.EStyle;\n\nnamespace LA;\n\nstatic class RegexParser {\n\tpublic record struct RXSpan(int start, int end, EStyle token);\n\t\n\tstatic regexp s_rx1 = new(@\"\\G[A-Z_]+(?:=\\d+)?\\)\");\n\t\n\tpublic static List<RXSpan> GetClassifiedSpans(RStr s, bool net = false) {\n\t\tvar a = new List<RXSpan>();\n\t\tint i = 0, ii = 0;\n\t\tbool altExtClasses = false;\n\t\t\n\t\twhile (s.Eq(i, \"(*\") && s_rx1.Match(s, 0, out var r1, (i + 2)..)) {\n\t\t\tif (r1.Length == 3 && s.Eq(i + 2, \"EC\")) altExtClasses = true; //au mod: (*EC) sets flag PCRE2_ALT_EXTENDED_CLASS. Without it cannot correctly format or validate the regex; also users could not use this feature in the Find tool etc.\n\t\t\ti = r1.end;\n\t\t}\n\t\tif (i > 0) _Add(0, i, EStyle.RxOption);\n\t\t\n\t\ttry { _Sub(s, false); }\n\t\tcatch (Exception ex) { Debug_.Print(ex); }\n\t\t\n\t\treturn a;\n\t\t\n\t\tvoid _Sub(RStr s, bool extended) {\n\t\t\twhile (i < s.Length) {\n\t\t\t\tii = i;\n\t\t\t\tchar c = s[i++];\n\t\t\t\tif (c is '\\\\') {\n\t\t\t\t\t_Backslash(s, false);\n\t\t\t\t} else if (c is '[') {\n\t\t\t\t\t_CharClass(s, altExtClasses);\n\t\t\t\t} else if (c is '.') {\n\t\t\t\t\t_Add(ii, i, EStyle.RxChars);\n\t\t\t\t} else if (c is '^' or '$' or '|' or '+' or '*' or '?') {\n\t\t\t\t\t_Add(ii, i, EStyle.RxMeta);\n\t\t\t\t} else if (c is '{') {\n\t\t\t\t\twhile (i < s.Length && s[i] is (>= '0' and <= '9') or ',' or ' ') i++;\n\t\t\t\t\tif (s.Eq(i, '}')) i++;\n\t\t\t\t\t_Add(ii, i, EStyle.RxMeta);\n\t\t\t\t} else if (c is '(') {\n\t\t\t\t\tif (!_Read(s, out c, EStyle.RxMeta)) return;\n\t\t\t\t\tif (c is '*') {\n\t\t\t\t\t\tif (!_Read(s, out c, EStyle.RxMeta)) return;\n\t\t\t\t\t\tif (char.IsLower(c)) { //`(*atomic:` etc\n\t\t\t\t\t\t\t_FindAdd(s, ':');\n\t\t\t\t\t\t\tif (i < s.Length) {\n\t\t\t\t\t\t\t\tif (s[(ii + 2)..(i - 1)] is \"scan_substring\" or \"scs\" && s[i] is '(') _FindAdd(s, ')');\n\t\t\t\t\t\t\t\t_Sub(s, extended);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else { //`(*FAIL)`, `(*MARK:name)`, `(*:markName)` etc\n\t\t\t\t\t\t\t_FindAdd(s, ')');\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (c is '?') {\n\t\t\t\t\t\tif (!_Read(s, out c, EStyle.RxOption)) return;\n\t\t\t\t\t\tif (c is ':' or '|' or '>' or '=' or '!' or '*') goto g1;\n\t\t\t\t\t\tif (c is '#') { //`(?comment)\n\t\t\t\t\t\t\t_FindAdd(s, ')', EStyle.RxComment);\n\t\t\t\t\t\t} else if (c is '<' && s.At_(i) is '=' or '!' or '*') { //`(<=` etc\n\t\t\t\t\t\t\ti++;\n\t\t\t\t\t\t\tgoto g1;\n\t\t\t\t\t\t} else if (c is '<' or '\\'') { //`(?<name>group)`, `(?'name'group)`\n\t\t\t\t\t\t\t_FindAdd(s, c is '\\'' ? c : '>');\n\t\t\t\t\t\t\t_Sub(s, extended);\n\t\t\t\t\t\t} else if (c is 'P') { //`(?P<name>group)`, `(?P=name)`, `(?P>name)`\n\t\t\t\t\t\t\tif (!_Read(s, out c, EStyle.RxMeta)) return;\n\t\t\t\t\t\t\tif (c is '<') {\n\t\t\t\t\t\t\t\t_FindAdd(s, '>');\n\t\t\t\t\t\t\t\t_Sub(s, extended);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t_FindAdd(s, ')');\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else if (c is '(') { //`(?(condition)group)`\n\t\t\t\t\t\t\tif (s.Eq(i, '?') && (s.At_(i + 1) is '=' or '!' || (s.At_(i + 1) is '<' && s.At_(i + 2) is '=' or '!'))) {\n\t\t\t\t\t\t\t\ti += s.At_(i + 1) is '<' ? 3 : 2;\n\t\t\t\t\t\t\t\t_Add(ii, i, EStyle.RxMeta);\n\t\t\t\t\t\t\t\t_Sub(s, extended);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t_FindAdd(s, ')');\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t_Sub(s, extended);\n\t\t\t\t\t\t} else if (c is 'R' or '&' || c.IsAsciiDigit() || (c is '-' or '+' && s.At_(i).IsAsciiDigit())) { //`(?R)`, `(?1)`, `(?-1)`, `(?&name)`\n\t\t\t\t\t\t\t_FindAdd(s, ')');\n\t\t\t\t\t\t} else if (c is 'C' && !net) { //callout\n\t\t\t\t\t\t\t//if (!s.Eq(i, ')')) { if (!_Read(s, EStyle.RxMeta)) return; if (c is '\\'' or '`' or '\"' or '^' or '%' or '#' or '$' or '{') {} } //never mind\n\t\t\t\t\t\t\t_FindAdd(s, ')', EStyle.RxCallout);\n\t\t\t\t\t\t} else if (c is '[' && !net) { //extended character class\n\t\t\t\t\t\t\t_ExtendedCharClass(s);\n\t\t\t\t\t\t} else { //`(?options)\n\t\t\t\t\t\t\tfor (bool minus = false; ;) {\n\t\t\t\t\t\t\t\tif (c is ')') break;\n\t\t\t\t\t\t\t\tif (c is '-') minus = true;\n\t\t\t\t\t\t\t\telse if (c is 'x') extended = !minus;\n\t\t\t\t\t\t\t\telse if (c is '^') extended = false;\n\t\t\t\t\t\t\t\telse if (c is ':') {\n\t\t\t\t\t\t\t\t\t_Add(ii, ii + 1, EStyle.RxMeta);\n\t\t\t\t\t\t\t\t\t_Add(ii + 1, i - 1, EStyle.RxOption);\n\t\t\t\t\t\t\t\t\tii = i - 1;\n\t\t\t\t\t\t\t\t\tgoto g1; //like `(?i:group)`\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (!_Read(s, out c, EStyle.RxOption)) return;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t_Add(ii, i, EStyle.RxOption);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\ti--;\n\t\t\t\t\tg1:\n\t\t\t\t\t_Add(ii, i, EStyle.RxMeta);\n\t\t\t\t\t_Sub(s, extended);\n\t\t\t\t} else if (c is ')') {\n\t\t\t\t\t_Add(ii, i, EStyle.RxMeta);\n\t\t\t\t\treturn;\n\t\t\t\t} else if (c is '#' && extended) {\n\t\t\t\t\t_FindAdd(s, '\\n', EStyle.RxComment);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tvoid _Backslash(RStr s, bool inCharClass) {\n\t\t\tif (!_Read(s, out char c)) return;\n\t\t\tswitch (c) {\n\t\t\tcase >= '1' and <= '9' when !inCharClass:\n\t\t\t\t_AddNumber(s);\n\t\t\t\tbreak;\n\t\t\tcase 'Q' when !inCharClass: //literal text\n\t\t\t\t_Add(ii, i, EStyle.RxMeta);\n\t\t\t\tif (_Find(s, @\"\\E\", i, out int j)) _Add(j, i = j + 2, EStyle.RxMeta); else i = s.Length;\n\t\t\t\tbreak;\n\t\t\tcase 'b' or 'B' or 'A' or 'Z' or 'z' or 'G' or 'R' or 'X' or 'K' when !inCharClass:\n\t\t\t\t_Add(ii, i, EStyle.RxMeta);\n\t\t\t\tbreak;\n\t\t\tcase 'g' or 'k' when !inCharClass:\n\t\t\t\tif (!_Read(s, out c)) return;\n\t\t\t\tif (c is '{') _FindAdd(s, '}');\n\t\t\t\telse if (c is '<') _FindAdd(s, '>');\n\t\t\t\telse if (c is '\\'') _FindAdd(s, '\\'');\n\t\t\t\telse if (c is '-' or '+' or (>= '0' and <= '9')) _AddNumber(s);\n\t\t\t\tbreak;\n\t\t\tcase 'N' when s.Eq(i, \"{U+\"): //`\\N{U+xxxx}` (Unicode char code)\n\t\t\t\t_FindAdd(s, '}', EStyle.RxEscape);\n\t\t\t\tbreak;\n\t\t\tcase 'w' or 'W' or 'd' or 'D' or 's' or 'S' or 'h' or 'H' or 'v' or 'V' or 'N' or 'C':\n\t\t\t\t_Add(ii, i, EStyle.RxChars);\n\t\t\t\tbreak;\n\t\t\tcase 'p' or 'P': //Unicode properties\n\t\t\t\tif (s.Eq(i, '{')) _FindAdd(s, '}', EStyle.RxChars); else if (char.IsAsciiLetter(s.At_(i))) _Add(ii, ++i, EStyle.RxChars);\n\t\t\t\tbreak;\n\t\t\tcase 'x': //ASCII char code\n\t\t\t\tif (s.Eq(i, '{')) i = _FindEnd(s, '}', i); else while (i < ii + 4 && char.IsAsciiHexDigit(s.At_(i))) i++;\n\t\t\t\t_Add(ii, i, EStyle.RxEscape);\n\t\t\t\tbreak;\n\t\t\tcase 'u' when net:\n\t\t\t\twhile (i < ii + 6 && char.IsAsciiHexDigit(s.At_(i))) i++;\n\t\t\t\t_Add(ii, i, EStyle.RxEscape);\n\t\t\t\tbreak;\n\t\t\tdefault:  //`\\n` etc\n\t\t\t\t_Add(ii, i, EStyle.RxEscape);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\t\n\t\tvoid _CharClass(RStr s, bool altExtended) {\n\t\t\tif (s.Eq(i, '^')) i++;\n\t\t\t_Add(ii, i, EStyle.RxChars);\n\t\t\tint ccStart = i;\n\t\t\twhile (i < s.Length) {\n\t\t\t\tii = i;\n\t\t\t\tchar c = s[i++];\n\t\t\t\tif (c == '\\\\') {\n\t\t\t\t\t_Backslash(s, true);\n\t\t\t\t} else if (c == '[') {\n\t\t\t\t\tif (net) {\n\t\t\t\t\t\tif (i > ccStart + 2 && s[i - 2] == '-') _CharClass(s, false); //`[ab-[cd]]`\n\t\t\t\t\t} else if (s.Eq(i, ':')) { //`[...[:posix:]...]`\n\t\t\t\t\t\t_PosixCharClass(s);\n\t\t\t\t\t} else if (altExtended) { //nested alt-extended class\n\t\t\t\t\t\t_CharClass(s, true);\n\t\t\t\t\t}\n\t\t\t\t} else if (c is ']') {\n\t\t\t\t\t_Add(ii, i, EStyle.RxChars);\n\t\t\t\t\treturn;\n\t\t\t\t} else if (altExtended) {\n\t\t\t\t\tif (c is '-' or '|' or '&' or '~' && s.Eq(i, c)) _Add(ii, ++i, EStyle.RxChars);\n\t\t\t\t} else {\n\t\t\t\t\tif (c is '-' && i > ccStart + 1 && !s.Eq(i, ']')) _Add(ii, i, EStyle.RxChars);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tvoid _ExtendedCharClass(RStr s) {\n\t\t\t_Add(ii, i, EStyle.RxChars);\n\t\t\twhile (i < s.Length) {\n\t\t\t\tii = i;\n\t\t\t\tchar c = s[i++];\n\t\t\t\tif (c == '\\\\') {\n\t\t\t\t\t_Backslash(s, true);\n\t\t\t\t} else if (c == '[') {\n\t\t\t\t\tif (s.Eq(i, ':')) { //`[...[:posix:]...]`\n\t\t\t\t\t\t_PosixCharClass(s);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t_CharClass(s, false);\n\t\t\t\t\t}\n\t\t\t\t} else if (c is ']') {\n\t\t\t\t\tif (s.Eq(i, ')')) i++;\n\t\t\t\t\t_Add(ii, i, EStyle.RxChars);\n\t\t\t\t\treturn;\n\t\t\t\t} else if (c is '-' or '+' or '&' or '|' or '^' or '!' or '(' or ')') {\n\t\t\t\t\t_Add(ii, i, EStyle.RxChars);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tvoid _PosixCharClass(RStr s) {\n\t\t\twhile (++i < s.Length && s[i] is >= 'a' and <= 'z') ;\n\t\t\tif (s.Eq(i, ':')) i++;\n\t\t\tif (s.Eq(i, ']')) i++;\n\t\t\t_Add(ii, i, EStyle.RxChars);\n\t\t}\n\t\t\n\t\tbool _Read(RStr s, out char c, EStyle tFalse = 0) {\n\t\t\tif (i < s.Length) { c = s[i++]; return true; }\n\t\t\tif (tFalse != 0) _Add(ii, s.Length, tFalse);\n\t\t\tc = default;\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\tvoid _FindAdd(RStr s, char c, EStyle t = EStyle.RxMeta) {\n\t\t\t_Add(ii, i = _FindEnd(s, c, i), t);\n\t\t}\n\t\t\n\t\tvoid _AddNumber(RStr s, EStyle t = EStyle.RxMeta) {\n\t\t\twhile (s.At_(i).IsAsciiDigit()) i++;\n\t\t\t_Add(ii, i, t);\n\t\t}\n\t\t\n\t\tvoid _Add(int from, int to, EStyle t) {\n\t\t\tif (a.Count > 0 && a[^1].token == t && a[^1].end == from) a[^1] = new(a[^1].start, to, t);\n\t\t\telse a.Add(new(from, to, t));\n\t\t}\n\t}\n\t\n\tstatic bool _Find(RStr t, char c, int from, out int foundAt) {\n\t\tfoundAt = t.IndexOf(from, c);\n\t\treturn foundAt >= 0;\n\t}\n\t\n\tstatic bool _Find(RStr t, string s, int from, out int foundAt) {\n\t\tfoundAt = t.IndexOf(from, s);\n\t\treturn foundAt >= 0;\n\t}\n\t\n\tstatic int _FindEnd(RStr t, char c, int from) {\n\t\tint i = t.IndexOf(from, c);\n\t\treturn i < 0 ? t.Length : i + 1;\n\t}\n\t\n\t/// <summary>\n\t/// Parses a regular expression or wildcard expression and writes regular expression syntax highlighting styles to <i>styles</i> (not UTF-8).\n\t/// </summary>\n\t/// <param name=\"s\"></param>\n\t/// <param name=\"format\"><b>Regexp</b>, <b>NetRegex</b> or <b>Wildex</b>.</param>\n\t/// <param name=\"styles\">Writes styles here. Must be of length greater or equal to <c>s.Length</c>.</param>\n\tpublic static void GetScintillaStylingBytes16(RStr s, PSFormat format, Span<byte> styles) {\n\t\tDebug.Assert(styles.Length >= s.Length);\n\t\tif (format is PSFormat.Wildex) {\n\t\t\tif (s is not ['*', '*', _, _, _, ..]) return;\n\t\t\tWXType type = 0;\n\t\t\tstring split = null;\n\t\t\tfor (int i = 2, j; i < s.Length; i++) {\n\t\t\t\tswitch (s[i]) {\n\t\t\t\tcase ' ':\n\t\t\t\t\tstyles = styles[(i + 1)..];\n\t\t\t\t\ts = s[(i + 1)..];\n\t\t\t\t\tgoto g1;\n\t\t\t\tcase 'r': type = WXType.RegexPcre; break;\n\t\t\t\tcase 'R': type = WXType.RegexNet; break;\n\t\t\t\tcase 'm': type = WXType.Multi; break;\n\t\t\t\tcase '(':\n\t\t\t\t\tif (s[i - 1] != 'm') return;\n\t\t\t\t\tfor (j = ++i; j < s.Length; j++) if (s[j] == ')') break;\n\t\t\t\t\tif (j == s.Length || j == i) return;\n\t\t\t\t\tsplit = s[i..j].ToString();\n\t\t\t\t\ti = j;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'c' or 'n': break;\n\t\t\t\tdefault: return;\n\t\t\t\t}\n\t\t\t}\n\t\t\tg1:\n\t\t\tif (type is WXType.RegexPcre or WXType.RegexNet) {\n\t\t\t\tvar a = GetClassifiedSpans(s, type is WXType.RegexNet);\n\t\t\t\t_ToScintillaStylingBytes16(s, a, styles);\n\t\t\t} else if (type is WXType.Multi) {\n\t\t\t\tforeach (var v in s.SplitSE(split ?? \"||\")) {\n\t\t\t\t\tGetScintillaStylingBytes16(s[v.Range], PSFormat.Wildex, styles[v.start..]);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tvar a = GetClassifiedSpans(s, format is PSFormat.NetRegex);\n\t\t\t_ToScintillaStylingBytes16(s, a, styles);\n\t\t}\n\t}\n\t\n\tstatic void _ToScintillaStylingBytes16(RStr s, List<RXSpan> a, Span<byte> styles) {\n\t\tstyles[..s.Length].Fill((byte)EStyle.RxText);\n\t\tforeach (var v in a) {\n\t\t\t//print.it(v, s[v.start..v.end].ToString());\n\t\t\tvar (i, end) = (v.start, v.end);\n\t\t\twhile (i < end) styles[i++] = (byte)v.token;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Au.sln",
    "content": "﻿\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio Version 17\nVisualStudioVersion = 17.0.31612.314\nMinimumVisualStudioVersion = 10.0.40219.1\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"App\", \"App\", \"{82D5A236-3610-446D-95BA-3888BF6E8F25}\"\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"Dll\", \"Dll\", \"{0C880B80-8ECC-4252-A9C9-00BAF5C58A3C}\"\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"Libraries\", \"Libraries\", \"{016834A7-5E0C-4356-81F4-E4034CD56BBD}\"\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"Other\", \"Other\", \"{4B65F34E-129D-46F9-9464-25737BC43029}\"\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"solution\", \"solution\", \"{2C03BC9E-C857-44FF-8585-08DF42C687A3}\"\n\tProjectSection(SolutionItems) = preProject\n\t\t.editorconfig = .editorconfig\n\t\t.gitignore = .gitignore\n\t\tAu.snk = Au.snk\n\t\tglobal.json = global.json\n\t\tLICENSE.txt = LICENSE.txt\n\t\tNotes.txt = Notes.txt\n\t\tREADME.md = README.md\n\tEndProjectSection\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"Au.Editor\", \"Au.Editor\\Au.Editor.csproj\", \"{4B47A184-5CB7-4E78-8EAD-E458FB11A7EF}\"\n\tProjectSection(ProjectDependencies) = postProject\n\t\t{19CCA8B8-46B9-4609-B7CE-198DA19F07BD} = {19CCA8B8-46B9-4609-B7CE-198DA19F07BD}\n\t\t{5DCF20C5-9BBD-44E2-96BE-323A845B99F4} = {5DCF20C5-9BBD-44E2-96BE-323A845B99F4}\n\t\t{8662DF62-3B21-4D23-806D-4590F6091918} = {8662DF62-3B21-4D23-806D-4590F6091918}\n\tEndProjectSection\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"Au\", \"Au\\Au.csproj\", \"{B014973F-FCBB-4BC4-92E7-97AE684EA4F7}\"\n\tProjectSection(ProjectDependencies) = postProject\n\t\t{10559875-C9A1-484C-BCFA-8DC53A428F1C} = {10559875-C9A1-484C-BCFA-8DC53A428F1C}\n\tEndProjectSection\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"Au.Controls\", \"Au.Controls\\Au.Controls.csproj\", \"{7A27F221-F05C-4545-B6BB-3DB0F6B9A50E}\"\nEndProject\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"Cpp\", \"Cpp\\Cpp.vcxproj\", \"{10559875-C9A1-484C-BCFA-8DC53A428F1C}\"\n\tProjectSection(ProjectDependencies) = postProject\n\t\t{8662DF62-3B21-4D23-806D-4590F6091918} = {8662DF62-3B21-4D23-806D-4590F6091918}\n\tEndProjectSection\nEndProject\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"PCRE\", \"Libraries\\PCRE\\PCRE.vcxproj\", \"{190BC611-EE00-4B10-87F2-A7E73B639E59}\"\nEndProject\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"Au.AppHost\", \"Au.AppHost\\Au.AppHost.vcxproj\", \"{5DCF20C5-9BBD-44E2-96BE-323A845B99F4}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"DocFX\", \"Other\\DocFX\\DocFX.csproj\", \"{C84FD40F-6D87-43FA-BBAD-5CA3C8B98E44}\"\nEndProject\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"Scintilla\", \"Libraries\\scintilla\\win32\\Scintilla.vcxproj\", \"{19CCA8B8-46B9-4609-B7CE-198DA19F07BD}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"Au.Net4\", \"Other\\Au.Net4\\Au.Net4.csproj\", \"{2157BC47-A5E1-405C-A003-F44970DC3718}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"DatabasesEtc\", \"Other\\DatabasesEtc\\DatabasesEtc.csproj\", \"{F7F1B96D-C391-4744-BE05-95264B0815E2}\"\nEndProject\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"Au.DllHost\", \"Other\\Au.DllHost\\Au.DllHost.vcxproj\", \"{E9D9F078-BF4F-437E-BE31-3AF89F3C6548}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"BuildEvents\", \"Other\\BuildEvents\\BuildEvents.csproj\", \"{8662DF62-3B21-4D23-806D-4590F6091918}\"\nEndProject\nGlobal\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n\t\tDebug|Any CPU = Debug|Any CPU\n\t\tDebug|ARM64 = Debug|ARM64\n\t\tDebug|x64 = Debug|x64\n\t\tDebug|x86 = Debug|x86\n\t\tRelease|Any CPU = Release|Any CPU\n\t\tRelease|ARM64 = Release|ARM64\n\t\tRelease|x64 = Release|x64\n\t\tRelease|x86 = Release|x86\n\tEndGlobalSection\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n\t\t{4B47A184-5CB7-4E78-8EAD-E458FB11A7EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{4B47A184-5CB7-4E78-8EAD-E458FB11A7EF}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{4B47A184-5CB7-4E78-8EAD-E458FB11A7EF}.Debug|ARM64.ActiveCfg = Debug|Any CPU\n\t\t{4B47A184-5CB7-4E78-8EAD-E458FB11A7EF}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{4B47A184-5CB7-4E78-8EAD-E458FB11A7EF}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{4B47A184-5CB7-4E78-8EAD-E458FB11A7EF}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{4B47A184-5CB7-4E78-8EAD-E458FB11A7EF}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{4B47A184-5CB7-4E78-8EAD-E458FB11A7EF}.Release|ARM64.ActiveCfg = Release|Any CPU\n\t\t{4B47A184-5CB7-4E78-8EAD-E458FB11A7EF}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{4B47A184-5CB7-4E78-8EAD-E458FB11A7EF}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{B014973F-FCBB-4BC4-92E7-97AE684EA4F7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{B014973F-FCBB-4BC4-92E7-97AE684EA4F7}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{B014973F-FCBB-4BC4-92E7-97AE684EA4F7}.Debug|ARM64.ActiveCfg = Debug|Any CPU\n\t\t{B014973F-FCBB-4BC4-92E7-97AE684EA4F7}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{B014973F-FCBB-4BC4-92E7-97AE684EA4F7}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{B014973F-FCBB-4BC4-92E7-97AE684EA4F7}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{B014973F-FCBB-4BC4-92E7-97AE684EA4F7}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{B014973F-FCBB-4BC4-92E7-97AE684EA4F7}.Release|ARM64.ActiveCfg = Release|Any CPU\n\t\t{B014973F-FCBB-4BC4-92E7-97AE684EA4F7}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{B014973F-FCBB-4BC4-92E7-97AE684EA4F7}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{7A27F221-F05C-4545-B6BB-3DB0F6B9A50E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{7A27F221-F05C-4545-B6BB-3DB0F6B9A50E}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{7A27F221-F05C-4545-B6BB-3DB0F6B9A50E}.Debug|ARM64.ActiveCfg = Debug|Any CPU\n\t\t{7A27F221-F05C-4545-B6BB-3DB0F6B9A50E}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{7A27F221-F05C-4545-B6BB-3DB0F6B9A50E}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{7A27F221-F05C-4545-B6BB-3DB0F6B9A50E}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{7A27F221-F05C-4545-B6BB-3DB0F6B9A50E}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{7A27F221-F05C-4545-B6BB-3DB0F6B9A50E}.Release|ARM64.ActiveCfg = Release|Any CPU\n\t\t{7A27F221-F05C-4545-B6BB-3DB0F6B9A50E}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{7A27F221-F05C-4545-B6BB-3DB0F6B9A50E}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{10559875-C9A1-484C-BCFA-8DC53A428F1C}.Debug|Any CPU.ActiveCfg = Debug|x64\n\t\t{10559875-C9A1-484C-BCFA-8DC53A428F1C}.Debug|Any CPU.Build.0 = Debug|x64\n\t\t{10559875-C9A1-484C-BCFA-8DC53A428F1C}.Debug|ARM64.ActiveCfg = Debug|ARM64\n\t\t{10559875-C9A1-484C-BCFA-8DC53A428F1C}.Debug|ARM64.Build.0 = Debug|ARM64\n\t\t{10559875-C9A1-484C-BCFA-8DC53A428F1C}.Debug|x64.ActiveCfg = Debug|x64\n\t\t{10559875-C9A1-484C-BCFA-8DC53A428F1C}.Debug|x64.Build.0 = Debug|x64\n\t\t{10559875-C9A1-484C-BCFA-8DC53A428F1C}.Debug|x86.ActiveCfg = Debug|Win32\n\t\t{10559875-C9A1-484C-BCFA-8DC53A428F1C}.Debug|x86.Build.0 = Debug|Win32\n\t\t{10559875-C9A1-484C-BCFA-8DC53A428F1C}.Release|Any CPU.ActiveCfg = Release|x64\n\t\t{10559875-C9A1-484C-BCFA-8DC53A428F1C}.Release|Any CPU.Build.0 = Release|x64\n\t\t{10559875-C9A1-484C-BCFA-8DC53A428F1C}.Release|ARM64.ActiveCfg = Release|ARM64\n\t\t{10559875-C9A1-484C-BCFA-8DC53A428F1C}.Release|ARM64.Build.0 = Release|ARM64\n\t\t{10559875-C9A1-484C-BCFA-8DC53A428F1C}.Release|x64.ActiveCfg = Release|x64\n\t\t{10559875-C9A1-484C-BCFA-8DC53A428F1C}.Release|x64.Build.0 = Release|x64\n\t\t{10559875-C9A1-484C-BCFA-8DC53A428F1C}.Release|x86.ActiveCfg = Release|Win32\n\t\t{10559875-C9A1-484C-BCFA-8DC53A428F1C}.Release|x86.Build.0 = Release|Win32\n\t\t{190BC611-EE00-4B10-87F2-A7E73B639E59}.Debug|Any CPU.ActiveCfg = Release|x64\n\t\t{190BC611-EE00-4B10-87F2-A7E73B639E59}.Debug|Any CPU.Build.0 = Release|x64\n\t\t{190BC611-EE00-4B10-87F2-A7E73B639E59}.Debug|Any CPU.Deploy.0 = Release|x64\n\t\t{190BC611-EE00-4B10-87F2-A7E73B639E59}.Debug|ARM64.ActiveCfg = Debug|ARM64\n\t\t{190BC611-EE00-4B10-87F2-A7E73B639E59}.Debug|ARM64.Build.0 = Debug|ARM64\n\t\t{190BC611-EE00-4B10-87F2-A7E73B639E59}.Debug|x64.ActiveCfg = Debug|x64\n\t\t{190BC611-EE00-4B10-87F2-A7E73B639E59}.Debug|x64.Build.0 = Debug|x64\n\t\t{190BC611-EE00-4B10-87F2-A7E73B639E59}.Debug|x86.ActiveCfg = Release|Win32\n\t\t{190BC611-EE00-4B10-87F2-A7E73B639E59}.Debug|x86.Build.0 = Release|Win32\n\t\t{190BC611-EE00-4B10-87F2-A7E73B639E59}.Release|Any CPU.ActiveCfg = Release|x64\n\t\t{190BC611-EE00-4B10-87F2-A7E73B639E59}.Release|Any CPU.Build.0 = Release|x64\n\t\t{190BC611-EE00-4B10-87F2-A7E73B639E59}.Release|ARM64.ActiveCfg = Release|ARM64\n\t\t{190BC611-EE00-4B10-87F2-A7E73B639E59}.Release|ARM64.Build.0 = Release|ARM64\n\t\t{190BC611-EE00-4B10-87F2-A7E73B639E59}.Release|x64.ActiveCfg = Release|x64\n\t\t{190BC611-EE00-4B10-87F2-A7E73B639E59}.Release|x64.Build.0 = Release|x64\n\t\t{190BC611-EE00-4B10-87F2-A7E73B639E59}.Release|x86.ActiveCfg = Release|Win32\n\t\t{190BC611-EE00-4B10-87F2-A7E73B639E59}.Release|x86.Build.0 = Release|Win32\n\t\t{5DCF20C5-9BBD-44E2-96BE-323A845B99F4}.Debug|Any CPU.ActiveCfg = Release|x64\n\t\t{5DCF20C5-9BBD-44E2-96BE-323A845B99F4}.Debug|Any CPU.Build.0 = Release|x64\n\t\t{5DCF20C5-9BBD-44E2-96BE-323A845B99F4}.Debug|ARM64.ActiveCfg = Release|ARM64\n\t\t{5DCF20C5-9BBD-44E2-96BE-323A845B99F4}.Debug|ARM64.Build.0 = Release|ARM64\n\t\t{5DCF20C5-9BBD-44E2-96BE-323A845B99F4}.Debug|x64.ActiveCfg = Release|x64\n\t\t{5DCF20C5-9BBD-44E2-96BE-323A845B99F4}.Debug|x64.Build.0 = Release|x64\n\t\t{5DCF20C5-9BBD-44E2-96BE-323A845B99F4}.Debug|x86.ActiveCfg = Release|Win32\n\t\t{5DCF20C5-9BBD-44E2-96BE-323A845B99F4}.Debug|x86.Build.0 = Release|Win32\n\t\t{5DCF20C5-9BBD-44E2-96BE-323A845B99F4}.Release|Any CPU.ActiveCfg = Release|x64\n\t\t{5DCF20C5-9BBD-44E2-96BE-323A845B99F4}.Release|Any CPU.Build.0 = Release|x64\n\t\t{5DCF20C5-9BBD-44E2-96BE-323A845B99F4}.Release|ARM64.ActiveCfg = Release|ARM64\n\t\t{5DCF20C5-9BBD-44E2-96BE-323A845B99F4}.Release|ARM64.Build.0 = Release|ARM64\n\t\t{5DCF20C5-9BBD-44E2-96BE-323A845B99F4}.Release|x64.ActiveCfg = Release|x64\n\t\t{5DCF20C5-9BBD-44E2-96BE-323A845B99F4}.Release|x64.Build.0 = Release|x64\n\t\t{5DCF20C5-9BBD-44E2-96BE-323A845B99F4}.Release|x86.ActiveCfg = Release|Win32\n\t\t{5DCF20C5-9BBD-44E2-96BE-323A845B99F4}.Release|x86.Build.0 = Release|Win32\n\t\t{C84FD40F-6D87-43FA-BBAD-5CA3C8B98E44}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{C84FD40F-6D87-43FA-BBAD-5CA3C8B98E44}.Debug|ARM64.ActiveCfg = Debug|Any CPU\n\t\t{C84FD40F-6D87-43FA-BBAD-5CA3C8B98E44}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{C84FD40F-6D87-43FA-BBAD-5CA3C8B98E44}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{C84FD40F-6D87-43FA-BBAD-5CA3C8B98E44}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{C84FD40F-6D87-43FA-BBAD-5CA3C8B98E44}.Release|ARM64.ActiveCfg = Release|Any CPU\n\t\t{C84FD40F-6D87-43FA-BBAD-5CA3C8B98E44}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{C84FD40F-6D87-43FA-BBAD-5CA3C8B98E44}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{19CCA8B8-46B9-4609-B7CE-198DA19F07BD}.Debug|Any CPU.ActiveCfg = Debug|x64\n\t\t{19CCA8B8-46B9-4609-B7CE-198DA19F07BD}.Debug|Any CPU.Build.0 = Debug|x64\n\t\t{19CCA8B8-46B9-4609-B7CE-198DA19F07BD}.Debug|ARM64.ActiveCfg = Debug|ARM64\n\t\t{19CCA8B8-46B9-4609-B7CE-198DA19F07BD}.Debug|ARM64.Build.0 = Debug|ARM64\n\t\t{19CCA8B8-46B9-4609-B7CE-198DA19F07BD}.Debug|x64.ActiveCfg = Debug|x64\n\t\t{19CCA8B8-46B9-4609-B7CE-198DA19F07BD}.Debug|x64.Build.0 = Debug|x64\n\t\t{19CCA8B8-46B9-4609-B7CE-198DA19F07BD}.Debug|x86.ActiveCfg = Debug|x64\n\t\t{19CCA8B8-46B9-4609-B7CE-198DA19F07BD}.Release|Any CPU.ActiveCfg = Release|x64\n\t\t{19CCA8B8-46B9-4609-B7CE-198DA19F07BD}.Release|Any CPU.Build.0 = Release|x64\n\t\t{19CCA8B8-46B9-4609-B7CE-198DA19F07BD}.Release|ARM64.ActiveCfg = Release|ARM64\n\t\t{19CCA8B8-46B9-4609-B7CE-198DA19F07BD}.Release|ARM64.Build.0 = Release|ARM64\n\t\t{19CCA8B8-46B9-4609-B7CE-198DA19F07BD}.Release|x64.ActiveCfg = Release|x64\n\t\t{19CCA8B8-46B9-4609-B7CE-198DA19F07BD}.Release|x64.Build.0 = Release|x64\n\t\t{19CCA8B8-46B9-4609-B7CE-198DA19F07BD}.Release|x86.ActiveCfg = Release|x64\n\t\t{2157BC47-A5E1-405C-A003-F44970DC3718}.Debug|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{2157BC47-A5E1-405C-A003-F44970DC3718}.Debug|Any CPU.Build.0 = Release|Any CPU\n\t\t{2157BC47-A5E1-405C-A003-F44970DC3718}.Debug|ARM64.ActiveCfg = Debug|Any CPU\n\t\t{2157BC47-A5E1-405C-A003-F44970DC3718}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{2157BC47-A5E1-405C-A003-F44970DC3718}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{2157BC47-A5E1-405C-A003-F44970DC3718}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{2157BC47-A5E1-405C-A003-F44970DC3718}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{2157BC47-A5E1-405C-A003-F44970DC3718}.Release|ARM64.ActiveCfg = Release|Any CPU\n\t\t{2157BC47-A5E1-405C-A003-F44970DC3718}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{2157BC47-A5E1-405C-A003-F44970DC3718}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{F7F1B96D-C391-4744-BE05-95264B0815E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{F7F1B96D-C391-4744-BE05-95264B0815E2}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{F7F1B96D-C391-4744-BE05-95264B0815E2}.Debug|ARM64.ActiveCfg = Debug|Any CPU\n\t\t{F7F1B96D-C391-4744-BE05-95264B0815E2}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{F7F1B96D-C391-4744-BE05-95264B0815E2}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{F7F1B96D-C391-4744-BE05-95264B0815E2}.Release|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{F7F1B96D-C391-4744-BE05-95264B0815E2}.Release|Any CPU.Build.0 = Debug|Any CPU\n\t\t{F7F1B96D-C391-4744-BE05-95264B0815E2}.Release|ARM64.ActiveCfg = Debug|Any CPU\n\t\t{F7F1B96D-C391-4744-BE05-95264B0815E2}.Release|x64.ActiveCfg = Debug|Any CPU\n\t\t{F7F1B96D-C391-4744-BE05-95264B0815E2}.Release|x86.ActiveCfg = Debug|Any CPU\n\t\t{E9D9F078-BF4F-437E-BE31-3AF89F3C6548}.Debug|Any CPU.ActiveCfg = Release|x64\n\t\t{E9D9F078-BF4F-437E-BE31-3AF89F3C6548}.Debug|Any CPU.Build.0 = Release|x64\n\t\t{E9D9F078-BF4F-437E-BE31-3AF89F3C6548}.Debug|ARM64.ActiveCfg = Release|ARM64\n\t\t{E9D9F078-BF4F-437E-BE31-3AF89F3C6548}.Debug|ARM64.Build.0 = Release|ARM64\n\t\t{E9D9F078-BF4F-437E-BE31-3AF89F3C6548}.Debug|x64.ActiveCfg = Release|x64\n\t\t{E9D9F078-BF4F-437E-BE31-3AF89F3C6548}.Debug|x64.Build.0 = Release|x64\n\t\t{E9D9F078-BF4F-437E-BE31-3AF89F3C6548}.Debug|x86.ActiveCfg = Release|Win32\n\t\t{E9D9F078-BF4F-437E-BE31-3AF89F3C6548}.Debug|x86.Build.0 = Release|Win32\n\t\t{E9D9F078-BF4F-437E-BE31-3AF89F3C6548}.Release|Any CPU.ActiveCfg = Release|x64\n\t\t{E9D9F078-BF4F-437E-BE31-3AF89F3C6548}.Release|Any CPU.Build.0 = Release|x64\n\t\t{E9D9F078-BF4F-437E-BE31-3AF89F3C6548}.Release|ARM64.ActiveCfg = Release|ARM64\n\t\t{E9D9F078-BF4F-437E-BE31-3AF89F3C6548}.Release|ARM64.Build.0 = Release|ARM64\n\t\t{E9D9F078-BF4F-437E-BE31-3AF89F3C6548}.Release|x64.ActiveCfg = Release|x64\n\t\t{E9D9F078-BF4F-437E-BE31-3AF89F3C6548}.Release|x64.Build.0 = Release|x64\n\t\t{E9D9F078-BF4F-437E-BE31-3AF89F3C6548}.Release|x86.ActiveCfg = Release|Win32\n\t\t{E9D9F078-BF4F-437E-BE31-3AF89F3C6548}.Release|x86.Build.0 = Release|Win32\n\t\t{8662DF62-3B21-4D23-806D-4590F6091918}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{8662DF62-3B21-4D23-806D-4590F6091918}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{8662DF62-3B21-4D23-806D-4590F6091918}.Debug|ARM64.ActiveCfg = Debug|Any CPU\n\t\t{8662DF62-3B21-4D23-806D-4590F6091918}.Debug|ARM64.Build.0 = Debug|Any CPU\n\t\t{8662DF62-3B21-4D23-806D-4590F6091918}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{8662DF62-3B21-4D23-806D-4590F6091918}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{8662DF62-3B21-4D23-806D-4590F6091918}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{8662DF62-3B21-4D23-806D-4590F6091918}.Debug|x86.Build.0 = Debug|Any CPU\n\t\t{8662DF62-3B21-4D23-806D-4590F6091918}.Release|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{8662DF62-3B21-4D23-806D-4590F6091918}.Release|Any CPU.Build.0 = Debug|Any CPU\n\t\t{8662DF62-3B21-4D23-806D-4590F6091918}.Release|ARM64.ActiveCfg = Debug|Any CPU\n\t\t{8662DF62-3B21-4D23-806D-4590F6091918}.Release|ARM64.Build.0 = Debug|Any CPU\n\t\t{8662DF62-3B21-4D23-806D-4590F6091918}.Release|x64.ActiveCfg = Debug|Any CPU\n\t\t{8662DF62-3B21-4D23-806D-4590F6091918}.Release|x64.Build.0 = Debug|Any CPU\n\t\t{8662DF62-3B21-4D23-806D-4590F6091918}.Release|x86.ActiveCfg = Debug|Any CPU\n\t\t{8662DF62-3B21-4D23-806D-4590F6091918}.Release|x86.Build.0 = Debug|Any CPU\n\tEndGlobalSection\n\tGlobalSection(SolutionProperties) = preSolution\n\t\tHideSolutionNode = FALSE\n\tEndGlobalSection\n\tGlobalSection(NestedProjects) = preSolution\n\t\t{4B47A184-5CB7-4E78-8EAD-E458FB11A7EF} = {82D5A236-3610-446D-95BA-3888BF6E8F25}\n\t\t{B014973F-FCBB-4BC4-92E7-97AE684EA4F7} = {0C880B80-8ECC-4252-A9C9-00BAF5C58A3C}\n\t\t{7A27F221-F05C-4545-B6BB-3DB0F6B9A50E} = {0C880B80-8ECC-4252-A9C9-00BAF5C58A3C}\n\t\t{10559875-C9A1-484C-BCFA-8DC53A428F1C} = {0C880B80-8ECC-4252-A9C9-00BAF5C58A3C}\n\t\t{190BC611-EE00-4B10-87F2-A7E73B639E59} = {016834A7-5E0C-4356-81F4-E4034CD56BBD}\n\t\t{5DCF20C5-9BBD-44E2-96BE-323A845B99F4} = {82D5A236-3610-446D-95BA-3888BF6E8F25}\n\t\t{19CCA8B8-46B9-4609-B7CE-198DA19F07BD} = {016834A7-5E0C-4356-81F4-E4034CD56BBD}\n\t\t{2157BC47-A5E1-405C-A003-F44970DC3718} = {4B65F34E-129D-46F9-9464-25737BC43029}\n\t\t{F7F1B96D-C391-4744-BE05-95264B0815E2} = {4B65F34E-129D-46F9-9464-25737BC43029}\n\t\t{E9D9F078-BF4F-437E-BE31-3AF89F3C6548} = {4B65F34E-129D-46F9-9464-25737BC43029}\n\t\t{8662DF62-3B21-4D23-806D-4590F6091918} = {4B65F34E-129D-46F9-9464-25737BC43029}\n\tEndGlobalSection\n\tGlobalSection(ExtensibilityGlobals) = postSolution\n\t\tSolutionGuid = {F4DC626A-22DF-4C8A-9686-8A694050B23E}\n\tEndGlobalSection\nEndGlobal\n"
  },
  {
    "path": "Cookbook/files.xml",
    "content": "﻿<files>\n  <d n=\"-\" i=\"357\">\n    <s n=\"How to write the cookbook.cs\" i=\"360\" />\n    <s n=\"Test.cs\" i=\"362\" />\n    <s n=\"TODO.cs\" i=\"363\" />\n    <s n=\"init.cs\" i=\"361\" />\n    <c n=\"global.cs\" i=\"359\" />\n  </d>\n  <s n=\"Documentation.cs\" i=\"388\" />\n  <d n=\"C# language\" i=\"4\">\n    <s n=\"C# introduction.cs\" i=\"25\" />\n    <s n=\"Comments, disabled code.cs\" i=\"35\" />\n    <s n=\"Strings (text), numbers, characters, bool, null, default.cs\" i=\"57\" />\n    <s n=\"Variables, fields, built-in types.cs\" i=\"36\" />\n    <s n=\"Constants, enum.cs\" i=\"60\" />\n    <s n=\"Types (class, struct, generic, nullable, tuple).cs\" i=\"37\" />\n    <s n=\"Functions (methods, properties).cs\" i=\"38\" />\n    <s n=\"Operators, expressions.cs\" i=\"39\" />\n    <s n=\"Namespaces, using.cs\" i=\"64\" />\n    <s n=\"if-else, switch.cs\" i=\"65\" />\n    <s n=\"for, foreach (repeat, loop, iterate).cs\" i=\"66\" />\n    <s n=\"return, goto (exit, end, stop, jump).cs\" i=\"67\" />\n    <s n=\"Errors, exceptions, try-catch-finally, throw.cs\" i=\"62\" />\n    <s n=\"Callback functions, lambda, delegate, event.cs\" i=\"63\" />\n    <s n=\"Collections - array, List, Stack, Queue.cs\" i=\"300\" />\n    <s n=\"Collections - Dictionary, HashSet.cs\" i=\"301\" />\n    <s n=\"Collections - LINQ functions.cs\" i=\"150\" />\n  </d>\n  <d n=\"Input, output\" i=\"5\">\n    <s n=\"Display text and variables.cs\" i=\"6\" />\n    <s n=\"Message box dialog, list.cs\" i=\"7\" />\n    <s n=\"Input box dialog.cs\" i=\"8\" />\n    <s n=\"File and folder dialogs, color dialog.cs\" i=\"74\" />\n    <s n=\"Popup menu, check list.cs\" i=\"10\" />\n    <s n=\"Play sound, speak.cs\" i=\"100\" />\n  </d>\n  <d n=\"Keys, mouse, clipboard\" i=\"12\">\n    <s n=\"Send keys, text.cs\" i=\"26\" />\n    <s n=\"Select-click menu, control (keyboard shortcuts).cs\" i=\"85\" />\n    <s n=\"If key pressed, toggled.cs\" i=\"75\" />\n    <s n=\"Wait for key, hotkey.cs\" i=\"76\" />\n    <s n=\"Mouse click, move, drag, restore.cs\" i=\"77\" />\n    <s n=\"Mouse get position, wait, cursor, etc.cs\" i=\"79\" />\n    <s n=\"Clipboard copy, paste, set-get text etc.cs\" i=\"80\" />\n  </d>\n  <d n=\"Window, UI automation\" i=\"13\">\n    <s n=\"Find window, controls, activate.cs\" i=\"27\" />\n    <s n=\"Window actions (close, move, resize, properties, etc).cs\" i=\"81\" />\n    <s n=\"Screens (display monitors).cs\" i=\"82\" />\n    <s n=\"UI elements (find, click, check, focus, select, expand, check menu item, wait for state, etc).cs\" i=\"83\" />\n    <s n=\"Web automation, navigate.cs\" i=\"89\" />\n    <s n=\"Extract table data using UI elements.cs\" i=\"373\" />\n    <s n=\"Web browser automation with Playwright.cs\" i=\"386\" />\n    <s n=\"Web browser automation with Selenium.cs\" i=\"141\" />\n    <s n=\"Find UI image or color.cs\" i=\"90\" />\n    <s n=\"Take screenshot.cs\" i=\"101\" />\n    <s n=\"OCR, find UI text, extract text.cs\" i=\"168\" />\n    <s n=\"-Computer vision.cs\" i=\"257\" />\n  </d>\n  <d n=\"Wait, time\" i=\"78\">\n    <s n=\"Simple wait (sleep, delay).cs\" i=\"86\" />\n    <s n=\"Wait for something.cs\" i=\"87\" />\n    <s n=\"Date and time.cs\" i=\"96\" />\n    <s n=\"Computer time, code speed.cs\" i=\"105\" />\n    <s n=\"Timers.cs\" i=\"111\" />\n  </d>\n  <d n=\"System\" i=\"15\">\n    <s n=\"Run program, open document, folder, URL.cs\" i=\"33\" />\n    <s n=\"Run console program and get its output text.cs\" i=\"92\" />\n    <s n=\"Processes.cs\" i=\"93\" />\n    <s n=\"Services.cs\" i=\"88\" />\n    <s n=\"Shutdown, restart, logoff, sleep, hibernate, lock computer, battery power.cs\" i=\"94\" />\n    <s n=\"Windows OS version, computer and user name.cs\" i=\"109\" />\n    <s n=\"Desktop (show etc).cs\" i=\"97\" />\n    <s n=\"Registry.cs\" i=\"99\" />\n    <s n=\"Environment variables.cs\" i=\"106\" />\n    <s n=\"Threads.cs\" i=\"112\" />\n  </d>\n  <d n=\"Filesystem\" i=\"14\">\n    <s n=\"File paths, special folders.cs\" i=\"28\" />\n    <s n=\"File exists, size, time, attributes.cs\" i=\"123\" />\n    <s n=\"Enumerate files, folders.cs\" i=\"122\" />\n    <s n=\"Enumerate drives, get drive properties.cs\" i=\"127\" />\n    <s n=\"File move, rename.cs\" i=\"118\" />\n    <s n=\"File copy.cs\" i=\"126\" />\n    <s n=\"File delete, Recycle Bin.cs\" i=\"125\" />\n    <s n=\"File shortcuts, symbolic links.cs\" i=\"124\" />\n    <s n=\"Create folder.cs\" i=\"120\" />\n    <s n=\"File read (load), write (save).cs\" i=\"119\" />\n    <s n=\"Zip files (compress, extract).cs\" i=\"136\" />\n    <s n=\"-Backup files.cs\" i=\"247\" />\n    <s n=\"Wait for file.cs\" i=\"250\" />\n  </d>\n  <d n=\"Internet\" i=\"16\">\n    <s n=\"Check internet connection, ping.cs\" i=\"152\" />\n    <s n=\"Http get web page, download file.cs\" i=\"137\" />\n    <s n=\"Http post web form, JSON.cs\" i=\"139\" />\n    <s n=\"Parse HTML.cs\" i=\"135\" />\n    <s n=\"SFTP (upload, download, etc).cs\" i=\"142\" />\n    <s n=\"SSH (manage website).cs\" i=\"191\" />\n    <s n=\"Send email message (SMTP).cs\" i=\"143\" />\n    <s n=\"Send and receive email (SMTP, POP3, IMAP).cs\" i=\"144\" />\n  </d>\n  <d n=\"Script\" i=\"186\">\n    <s n=\"Run script from script.cs\" i=\"185\" />\n    <s n=\"Run script command line, shortcut, schedule, shell menu.cs\" i=\"165\" />\n    <s n=\"Run script at startup, output link.cs\" i=\"187\" />\n    <s n=\"Run script on another computer; HTTP server.cs\" i=\"371\" />\n    <s n=\"Script run-time settings.cs\" i=\"110\" />\n    <s n=\"End script task, pause, exit.cs\" i=\"370\" />\n    <s n=\"Tray icon and notifications.cs\" i=\"115\" />\n    <s n=\"Script testing and debugging.cs\" i=\"173\" />\n    <s n=\"Saving variables, settings.cs\" i=\"140\" />\n    <s n=\"Script class with Main().cs\" i=\"261\" />\n    <s n=\"Multi-file scripts, projects.cs\" i=\"259\" />\n    <s n=\"Shared classes and functions, libraries.cs\" i=\"258\" />\n  </d>\n  <d n=\"Triggers, toolbars\" i=\"17\">\n    <s n=\"Keyboard triggers (hotkeys).cs\" i=\"160\" />\n    <s n=\"Autotext triggers, expand text.cs\" i=\"162\" />\n    <s n=\"Mouse triggers.cs\" i=\"161\" />\n    <s n=\"Window triggers, auto-close popup windows.cs\" i=\"163\" />\n    <d n=\"Using action triggers\" i=\"368\">\n      <s n=\"Trigger scopes (window, program).cs\" i=\"367\" />\n      <s n=\"-Trigger options.cs\" i=\"369\" />\n      <s n=\"Remap keys.cs\" i=\"365\" />\n      <s n=\"Show triggers.cs\" i=\"366\" />\n      <s n=\"Script 'Triggers and toolbars'.cs\" i=\"342\" />\n    </d>\n    <s n=\"Floating toolbars.cs\" i=\"164\" />\n    <s n=\"Process triggers (start, end).cs\" i=\"252\" />\n    <s n=\"File change triggers (events).cs\" i=\"251\" />\n    <s n=\"Other triggers.cs\" i=\"171\" />\n    <s n=\"-Hooks.cs\" i=\"172\" />\n    <s n=\"Run code when script ends.cs\" i=\"170\" />\n  </d>\n  <d n=\"Custom dialog windows\" i=\"264\">\n    <s n=\"Dialog - add elements, show, get values.cs\" i=\"265\" />\n    <s n=\"Dialog - set element properties and values.cs\" i=\"291\" />\n    <s n=\"Dialog - columns, rows.cs\" i=\"266\" />\n    <s n=\"Dialog - panels, group box.cs\" i=\"268\" />\n    <s n=\"Dialog - element size, position, margin.cs\" i=\"281\" />\n    <s n=\"Dialog - validation.cs\" i=\"267\" />\n    <s n=\"Dialog - events.cs\" i=\"270\" />\n    <s n=\"Dialog - splitters.cs\" i=\"278\" />\n    <s n=\"Dialog - links, formatted text.cs\" i=\"273\" />\n    <s n=\"Dialog - element color, font, border.cs\" i=\"282\" />\n    <s n=\"Dialog - button icon, toolbar.cs\" i=\"285\" />\n    <s n=\"Dialog - textbox watermark, adorner.cs\" i=\"283\" />\n    <s n=\"Dialog - icons, images, shapes.cs\" i=\"288\" />\n    <s n=\"Dialog - context menu, dropdown list.cs\" i=\"294\" />\n    <s n=\"Dialog - enum check-list, select.cs\" i=\"298\" />\n    <s n=\"Dialog - select date.cs\" i=\"269\" />\n    <s n=\"Dialog - ListView.cs\" i=\"349\" />\n    <s n=\"Dialog - DataGrid.cs\" i=\"350\" />\n    <s n=\"Dialog - WebView2 control.cs\" i=\"345\" />\n    <s n=\"Dialog - menu bar.cs\" i=\"271\" />\n    <s n=\"Dialog - multiple tabs.cs\" i=\"272\" />\n    <s n=\"Dialog - Window-based class.cs\" i=\"276\" />\n    <s n=\"Dialog - owned, non-modal (don't wait).cs\" i=\"286\" />\n    <s n=\"Dialog - window size, position, other properties.cs\" i=\"290\" />\n    <s n=\"Dialog - save window placement, control values.cs\" i=\"289\" />\n    <s n=\"Dialog - load XAML.cs\" i=\"295\" />\n    <s n=\"Dialog - libraries of controls and themes.cs\" i=\"296\" />\n    <s n=\"Dialog - use triggers.cs\" i=\"384\" />\n    <s n=\"-Dialog - rejected or future.cs\" i=\"297\" />\n  </d>\n  <d n=\"Math, convert, security\" i=\"129\">\n    <s n=\"Math functions.cs\" i=\"145\" />\n    <s n=\"Convert to-from string.cs\" i=\"148\" />\n    <s n=\"Convert Base64, hex, UTF-8, encoding, urlencode, htmlencode.cs\" i=\"147\" />\n    <s n=\"Other conversions.cs\" i=\"146\" />\n    <s n=\"Random numbers and strings, GUID.cs\" i=\"149\" />\n    <s n=\"Compress data.cs\" i=\"130\" />\n    <s n=\"Encrypt, hash.cs\" i=\"157\" />\n    <s n=\"Passwords in scripts.cs\" i=\"385\" />\n    <s n=\"-Color.cs\" i=\"169\" />\n  </d>\n  <d n=\"Text\" i=\"18\">\n    <s n=\"Strings and characters.cs\" i=\"174\" />\n    <s n=\"Simple string functions.cs\" i=\"154\" />\n    <s n=\"String formatting with variables.cs\" i=\"158\" />\n    <s n=\"Wildcard expressions (compare text).cs\" i=\"159\" />\n    <s n=\"Regular expressions (parse text, find-replace).cs\" i=\"155\" />\n    <s n=\"Fuzzy string matching, word stemming.cs\" i=\"302\" />\n  </d>\n  <d n=\"Data, document\" i=\"19\">\n    <s n=\"CSV table.cs\" i=\"128\" />\n    <s n=\"XML.cs\" i=\"131\" />\n    <s n=\"JSON.cs\" i=\"132\" />\n    <s n=\"Markdown.cs\" i=\"348\" />\n    <s n=\"SQLite database.cs\" i=\"133\" />\n    <s n=\"Databases.cs\" i=\"134\" />\n    <d n=\"Excel files\" i=\"379\">\n      <s n=\"Read Excel files with ExcelDataReader.cs\" i=\"245\" />\n      <s n=\"Read-write Excel files with EPPlus.cs\" i=\"246\" />\n      <s n=\"Read-write Excel files with ClosedXML.cs\" i=\"380\" />\n    </d>\n    <d n=\"-Excel automation\" i=\"374\">\n      <s n=\"Excel automation script.cs\" i=\"375\" />\n      <c n=\"ExcelExt.cs\" i=\"381\" />\n      <s n=\"Excel types, get Range etc, use object and dynamic.cs\" i=\"376\" />\n      <s n=\"Excel - get and set cell values.cs\" i=\"377\" />\n      <s n=\"Excel - TODO.cs\" i=\"383\" />\n    </d>\n  </d>\n  <d n=\"More\" i=\"20\">\n    <s n=\"Using .NET, NuGet, other libraries.cs\" i=\"389\" />\n    <s n=\"Windows API, native libraries.cs\" i=\"22\" />\n    <s n=\"COM components and type libraries.cs\" i=\"249\" />\n    <s n=\"Command line programs.cs\" i=\"167\" />\n    <s n=\"WMI.cs\" i=\"102\" />\n    <s n=\"PowerShell, VBScript, Python.cs\" i=\"103\" />\n    <s n=\"Use C# compiler at run time.cs\" i=\"346\" />\n    <s n=\"Editor extension - modify UI.cs\" i=\"351\" />\n    <s n=\"Frequently asked questions.cs\" i=\"387\" />\n  </d>\n</files>"
  },
  {
    "path": "Cpp/Cpp.cpp",
    "content": "#include \"stdafx.h\"\n#include \"Cpp.h\"\n\nHRESULT AccAgent::QueryInterface(REFIID riid, void ** ppvObject)\n{\n\tif(riid == IID_IAccessible || riid == IID_IUnknown || riid == IID_IDispatch) {\n\t\t*ppvObject = this;\n\t\treturn 0;\n\t}\n\treturn E_NOINTERFACE;\n}\n\nULONG AccAgent::AddRef(void)\n{\n\treturn 1;\n}\n\nULONG AccAgent::Release(void)\n{\n\treturn 1;\n}\n\nHRESULT AccAgent::GetTypeInfoCount(UINT * pctinfo)\n{\n\treturn E_NOTIMPL;\n}\n\nHRESULT AccAgent::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo ** ppTInfo)\n{\n\treturn E_NOTIMPL;\n}\n\nHRESULT AccAgent::GetIDsOfNames(REFIID riid, LPOLESTR * rgszNames, UINT cNames, LCID lcid, DISPID * rgDispId)\n{\n\treturn E_NOTIMPL;\n}\n\nHRESULT AccAgent::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS * pDispParams, VARIANT * pVarResult, EXCEPINFO * pExcepInfo, UINT * puArgErr)\n{\n\treturn E_NOTIMPL;\n}\n\nHRESULT AccAgent::get_accParent(IDispatch ** ppdispParent)\n{\n\t//Print(__FUNCTIONW__);\n\t*ppdispParent = null;\n\treturn 1;\n}\n\nHRESULT AccAgent::get_accChildCount(long * pcountChildren)\n{\n\treturn E_NOTIMPL;\n}\n\nHRESULT AccAgent::get_accChild(VARIANT varChild, IDispatch ** ppdispChild)\n{\n\treturn E_NOTIMPL;\n}\n\nHRESULT AccAgent::get_accName(VARIANT varChild, BSTR * pszName)\n{\n\t*pszName = SysAllocString(L\"TEST\");\n\treturn 0;\n}\n\nHRESULT AccAgent::get_accValue(VARIANT varChild, BSTR * pszValue)\n{\n\treturn E_NOTIMPL;\n}\n\nHRESULT AccAgent::get_accDescription(VARIANT varChild, BSTR * pszDescription)\n{\n\treturn E_NOTIMPL;\n}\n\nHRESULT AccAgent::get_accRole(VARIANT varChild, VARIANT * pvarRole)\n{\n\tpvarRole->vt = VT_I4; pvarRole->lVal = ROLE_SYSTEM_STATICTEXT;\n\treturn 0;\n}\n\nHRESULT AccAgent::get_accState(VARIANT varChild, VARIANT * pvarState)\n{\n\treturn E_NOTIMPL;\n}\n\nHRESULT AccAgent::get_accHelp(VARIANT varChild, BSTR * pszHelp)\n{\n\treturn E_NOTIMPL;\n}\n\nHRESULT AccAgent::get_accHelpTopic(BSTR * pszHelpFile, VARIANT varChild, long * pidTopic)\n{\n\treturn E_NOTIMPL;\n}\n\nHRESULT AccAgent::get_accKeyboardShortcut(VARIANT varChild, BSTR * pszKeyboardShortcut)\n{\n\treturn E_NOTIMPL;\n}\n\nHRESULT AccAgent::get_accFocus(VARIANT * pvarChild)\n{\n\treturn E_NOTIMPL;\n}\n\nHRESULT AccAgent::get_accSelection(VARIANT * pvarChildren)\n{\n\treturn E_NOTIMPL;\n}\n\nHRESULT AccAgent::get_accDefaultAction(VARIANT varChild, BSTR * pszDefaultAction)\n{\n\treturn E_NOTIMPL;\n}\n\nHRESULT AccAgent::accSelect(long flagsSelect, VARIANT varChild)\n{\n\treturn E_NOTIMPL;\n}\n\nHRESULT AccAgent::accLocation(long * pxLeft, long * pyTop, long * pcxWidth, long * pcyHeight, VARIANT varChild)\n{\n\treturn E_NOTIMPL;\n}\n\nLRESULT TestAcc2(HWND w, OUT IAccessible*& iacc);\n\n//HRESULT AccAgent::accNavigate(long navDir, VARIANT varStart, VARIANT * pvarEndUpAt)\n//{\n//\tif(varStart.vt!=VT_BSTR) return 1;\n//\t//Print(varStart.bstrVal);\n//\tHWND w = (HWND)navDir;\n//\tif(TestAcc2(w, *(IAccessible**)&pvarEndUpAt->pdispVal)) return 1;\n//\tpvarEndUpAt->vt = VT_DISPATCH;\n//\treturn 0;\n//}\n//\n//HRESULT AccAgent::accNavigate(long navDir, VARIANT varStart, VARIANT * pvarEndUpAt)\n//{\n//\tif(varStart.vt!=VT_BSTR) return 1;\n//\t//Print(varStart.bstrVal);\n//\tHWND w = (HWND)navDir;\n//\tif(AccessibleObjectFromWindow(w, 0, IID_IAccessible, (void**)&pvarEndUpAt->pdispVal)) return 1;\n//\tpvarEndUpAt->vt = VT_DISPATCH;\n//\treturn 0;\n//}\n\nHRESULT AccAgent::accNavigate(long navDir, VARIANT varStart, VARIANT * pvarEndUpAt)\n{\n\t//return 1;\n\tif(varStart.vt!=VT_BSTR) return 1;\n\t//Print(varStart.bstrVal);\n\tHWND w = (HWND)(LPARAM)navDir;\n\tIAccessiblePtr a;\n\tif(AccessibleObjectFromWindow(w, 0, IID_IAccessible, (void**)&a)) return 1;\n\tpvarEndUpAt->lVal = (long)LresultFromObject(IID_IAccessible, 0, a);\n\tpvarEndUpAt->vt = VT_I4;\n\treturn 0;\n}\n\nHRESULT AccAgent::accHitTest(long xLeft, long yTop, VARIANT * pvarChild)\n{\n\treturn E_NOTIMPL;\n}\n\nHRESULT AccAgent::accDoDefaultAction(VARIANT varChild)\n{\n\treturn E_NOTIMPL;\n}\n\nHRESULT AccAgent::put_accName(VARIANT varChild, BSTR szName)\n{\n\treturn E_NOTIMPL;\n}\n\nHRESULT AccAgent::put_accValue(VARIANT varChild, BSTR szValue)\n{\n\treturn E_NOTIMPL;\n}\n"
  },
  {
    "path": "Cpp/Cpp.def",
    "content": "EXPORTS\n\nUnloadAuCppDll ;__stdcall\n\npcre2_pattern_info=pcre2_pattern_info_16\npcre2_substring_nametable_scan=pcre2_substring_nametable_scan_16\n"
  },
  {
    "path": "Cpp/Cpp.h",
    "content": "#pragma once\n\n//This file is used internally.\n//\tIn the future it also could be used by C++ projects. Currently the C++ declarations are not added.\n//\tCurrently this dll is used only by a C# project.\n\n#ifdef Cpp_EXPORTS\n#include \"stdafx.h\"\n#define EXPORT extern \"C\" __declspec(dllexport)\n#else\n#define EXPORT extern \"C\" __declspec(dllimport)\n#endif\n\n//Cpp_Acc::MISC::flags.\nenum class eAccMiscFlags : BYTE {\n\tInProc = 1, //retrieved inproc\n\tUIA = 2,\n\tJava = 4,\n\tMarked = 128,\n\n\tInheritMask = InProc | UIA | Java,\n};\nENABLE_BITMASK_OPERATORS(eAccMiscFlags);\n\n//IAccessible* and child element id.\n//Has only ctors. Does not have a dtor (does not Release etc), operator=, etc.\nstruct Cpp_Acc {\n\tIAccessible* acc;\n\tlong elem;\n\tstruct MISC {\n\t\teAccMiscFlags flags;\n\t\tBYTE roleByte; //for optimization. 0 if not set or failed to get. ROLE_CUSTOM (0xFF) if VT_BSTR or not 1-ROLE_MAX.\n\t\tWORD level; //for ToString etc. 0 if not set.\n\t} misc;\n\n\tCpp_Acc() noexcept { Zero(); }\n\n\t//Does not AddRef.\n\tCpp_Acc(IAccessible* acc_, int elem_, eAccMiscFlags flags_ = {}) noexcept {\n\t\tacc = acc_;\n\t\telem = elem_;\n\t\t*(DWORD*)&misc = (DWORD)flags_;\n\t}\n\n#ifdef Cpp_EXPORTS\n\tvoid Zero() { memset(this, 0, sizeof(*this)); }\n\tvoid SetRoleByte();\n\tvoid SetLevel(DWORD level) { misc.level = (WORD)(level < 0xffff ? level : 0xffff); }\n#endif\n};\n\n#ifdef Cpp_EXPORTS\n//Same as Cpp_Acc, but has dtor that calls Release.\n#ifdef AGENTCACHE\ntypedef struct Cpp_Acc Cpp_Acc_Agent;\n#else\nstruct Cpp_Acc_Agent : Cpp_Acc {\n\t~Cpp_Acc_Agent() {\n\t\tif (acc != null) {\n\t\t\tacc->Release();\n\t\t\tacc = null;\n\t\t}\n\t}\n};\n#endif\n#endif\n\n//Flags for Cpp_AccFind.\n//The same as C# EFFlags. Documented there.\n//[Flags]\nenum class eAF {\n\tReverse = 1,\n\tHiddenToo = 2,\n\tMenuToo = 4,\n\tClientArea = 8,\n\tNotInProc = 0x100,\n\tUIA = 0x200,\n\tMark = 0x10000,\n\t//used only in this dll\n\tMarked_ = 0x40000000,\n};\nENABLE_BITMASK_OPERATORS(eAF);\n\n//Parameters for Cpp_AccFind.\nenum class eAF2;\nstruct Cpp_AccFindParams {\n\tSTR role, name, prop;\n\tint roleLength, nameLength, propLength;\n\teAF flags;\n\tint skip;\n\tWCHAR resultProp;\n\teAF2 flags2;\n\n\tCpp_AccFindParams() noexcept { memset(this, 0, sizeof(*this)); }\n};\n\n//Cpp_AccFind callback function type.\n//Must Release a.iacc. Preferably later, in spare time. Can do it in another thread.\nusing Cpp_AccFindCallbackT = BOOL(__stdcall*)(Cpp_Acc a, RECT* r);\n\nenum class eXYFlags {\n\tNotInProc = 1,\n\tUIA = 2,\n\tPreferLink = 4,\n\t//TrySmaller = 8, //rejected 2024-04-16\n\tOrUIA = 16,\n\n\t//internal flags, used in the C# side too\n\tDpiScaled_ = 0x10000,\n\tFail_ = 0x20000,\n};\nENABLE_BITMASK_OPERATORS(eXYFlags);\n\nusing Cpp_AccFromPointCallbackT = eXYFlags(__stdcall*)(eXYFlags flags, HWND wFP, HWND wTL);\n\nenum class eFocusedFlags {\n\tNotInProc = 1,\n\tUIA = 2,\n};\nENABLE_BITMASK_OPERATORS(eFocusedFlags);\n\nenum class eError {\n\tNotFound = 0x1001, //AO not found. With FindAll - no errors. This is actually not an error.\n\tInvalidParameter = 0x1002, //invalid parameter, for example wildcard expression (or regular expression in it)\n\tWindowClosed = 0x1003, //the specified window handle is invalid or the window was destroyed while injecting\n#ifdef Cpp_EXPORTS\n\tInject = 0x1100, //failed to inject this dll into the target process\n\tWindowOfThisThread = 0x1101, //the specified window belongs to the caller thread\n\tUseNotInProc = 0x1102, //window class name is Windows.UI.Core.CoreWindow etc\n\tWaitChromeDisabled = 0x1103, //need to wait while enabling Chrome AOs\n\t//WaitChromeEnabledPartially = 0x1104, //need to wait more while enabling Chrome AOs\n#endif\n};\n\n//Our custom OBJID for Cpp_AccFromWindow.\n#define OBJID_JAVA -100\n#define OBJID_UIA -101\n\n//Our custom NAVDIR_ for Cpp_AccNavigate.\n#define NAVDIR_PARENT 9\n#define NAVDIR_CHILD 10\n\n//FUTURE: declare exports (when stable), like:\n//EXPORT HRESULT Cpp_AccFind(...);\n\n#ifdef Cpp_EXPORTS\n#include \"util.h\"\n#include \"internal.h\"\n#endif\n"
  },
  {
    "path": "Cpp/Cpp.manifest",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<assembly manifestVersion=\"1.0\" xmlns=\"urn:schemas-microsoft-com:asm.v1\">\n\t<assemblyIdentity version=\"1.0.0.0\" name=\"Au\"/>\n\t<dependency>\n\t\t<dependentAssembly>\n\t\t\t<assemblyIdentity\n\t\t\t\ttype=\"win32\"\n\t\t\t\tname=\"Microsoft.Windows.Common-Controls\"\n\t\t\t\tversion=\"6.0.0.0\"\n\t\t\t\tprocessorArchitecture=\"*\"\n\t\t\t\tpublicKeyToken=\"6595b64144ccf1df\"\n\t\t\t\tlanguage=\"*\"\n        />\n\t\t</dependentAssembly>\n\t</dependency>\n</assembly>\n<!-- used by ActCtx_ -->\n"
  },
  {
    "path": "Cpp/Cpp.vcxproj",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project DefaultTargets=\"Build\" ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup Label=\"ProjectConfigurations\">\n    <ProjectConfiguration Include=\"Debug|ARM64\">\n      <Configuration>Debug</Configuration>\n      <Platform>ARM64</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Debug|Win32\">\n      <Configuration>Debug</Configuration>\n      <Platform>Win32</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release|ARM64\">\n      <Configuration>Release</Configuration>\n      <Platform>ARM64</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release|Win32\">\n      <Configuration>Release</Configuration>\n      <Platform>Win32</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Debug|x64\">\n      <Configuration>Debug</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release|x64\">\n      <Configuration>Release</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n  </ItemGroup>\n  <PropertyGroup Label=\"Globals\">\n    <VCProjectVersion>15.0</VCProjectVersion>\n    <ProjectGuid>{10559875-C9A1-484C-BCFA-8DC53A428F1C}</ProjectGuid>\n    <Keyword>Win32Proj</Keyword>\n    <RootNamespace>Cpp</RootNamespace>\n    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"Configuration\">\n    <ConfigurationType>DynamicLibrary</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v143</PlatformToolset>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\n    <ConfigurationType>DynamicLibrary</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v143</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"Configuration\">\n    <ConfigurationType>DynamicLibrary</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v143</PlatformToolset>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|ARM64'\" Label=\"Configuration\">\n    <ConfigurationType>DynamicLibrary</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v143</PlatformToolset>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\n    <ConfigurationType>DynamicLibrary</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v143</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|ARM64'\" Label=\"Configuration\">\n    <ConfigurationType>DynamicLibrary</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v143</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\n  <ImportGroup Label=\"ExtensionSettings\">\n  </ImportGroup>\n  <ImportGroup Label=\"Shared\">\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|ARM64'\" Label=\"PropertySheets\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|ARM64'\" Label=\"PropertySheets\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <PropertyGroup Label=\"UserMacros\" />\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\n    <LinkIncremental>true</LinkIncremental>\n    <TargetName>AuCpp</TargetName>\n    <OutDir>bin\\$(Configuration)\\$(Platform)\\</OutDir>\n    <IntDir>obj\\$(Configuration)\\$(Platform)\\</IntDir>\n    <GenerateManifest>false</GenerateManifest>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <TargetName>AuCpp</TargetName>\n    <OutDir>bin\\$(Configuration)\\$(Platform)\\</OutDir>\n    <IntDir>obj\\$(Configuration)\\$(Platform)\\</IntDir>\n    <GenerateManifest>false</GenerateManifest>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|ARM64'\">\n    <TargetName>AuCpp</TargetName>\n    <OutDir>bin\\$(Configuration)\\$(Platform)\\</OutDir>\n    <IntDir>obj\\$(Configuration)\\$(Platform)\\</IntDir>\n    <GenerateManifest>false</GenerateManifest>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <LinkIncremental>false</LinkIncremental>\n    <TargetName>AuCpp</TargetName>\n    <OutDir>bin\\$(Configuration)\\$(Platform)\\</OutDir>\n    <IntDir>obj\\$(Configuration)\\$(Platform)\\</IntDir>\n    <GenerateManifest>false</GenerateManifest>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <LinkIncremental>false</LinkIncremental>\n    <TargetName>AuCpp</TargetName>\n    <OutDir>bin\\$(Configuration)\\$(Platform)\\</OutDir>\n    <IntDir>obj\\$(Configuration)\\$(Platform)\\</IntDir>\n    <GenerateManifest>false</GenerateManifest>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|ARM64'\">\n    <LinkIncremental>false</LinkIncremental>\n    <TargetName>AuCpp</TargetName>\n    <OutDir>bin\\$(Configuration)\\$(Platform)\\</OutDir>\n    <IntDir>obj\\$(Configuration)\\$(Platform)\\</IntDir>\n    <GenerateManifest>false</GenerateManifest>\n  </PropertyGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>Disabled</Optimization>\n      <PreprocessorDefinitions>WIN32;_DEBUG;Cpp_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>\n      <LanguageStandard>stdcpp20</LanguageStandard>\n      <PrecompiledHeader>Use</PrecompiledHeader>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <ModuleDefinitionFile>Cpp.def</ModuleDefinitionFile>\n    </Link>\n    <PostBuildEvent>\n      <Command>$(SolutionDir)Other\\BuildEvents\\bin\\Debug\\BuildEvents.exe cppPostBuild $(Configuration) $(Platform)</Command>\n    </PostBuildEvent>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>Disabled</Optimization>\n      <PreprocessorDefinitions>_DEBUG;Cpp_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>\n      <LanguageStandard>stdcpp20</LanguageStandard>\n      <PrecompiledHeader>Use</PrecompiledHeader>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <ModuleDefinitionFile>Cpp.def</ModuleDefinitionFile>\n    </Link>\n    <PostBuildEvent>\n      <Command>$(SolutionDir)Other\\BuildEvents\\bin\\Debug\\BuildEvents.exe cppPostBuild $(Configuration) $(Platform)</Command>\n    </PostBuildEvent>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|ARM64'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>Disabled</Optimization>\n      <PreprocessorDefinitions>_DEBUG;Cpp_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>\n      <LanguageStandard>stdcpp20</LanguageStandard>\n      <PrecompiledHeader>Use</PrecompiledHeader>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <ModuleDefinitionFile>Cpp.def</ModuleDefinitionFile>\n    </Link>\n    <PostBuildEvent>\n      <Command>$(SolutionDir)Other\\BuildEvents\\bin\\Debug\\BuildEvents.exe cppPostBuild $(Configuration) $(Platform)</Command>\n    </PostBuildEvent>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>MaxSpeed</Optimization>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <PreprocessorDefinitions>WIN32;NDEBUG;Cpp_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\n      <StringPooling>true</StringPooling>\n      <BufferSecurityCheck>false</BufferSecurityCheck>\n      <LanguageStandard>stdcpp20</LanguageStandard>\n      <PrecompiledHeader>Use</PrecompiledHeader>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <ModuleDefinitionFile>Cpp.def</ModuleDefinitionFile>\n    </Link>\n    <PostBuildEvent>\n      <Command>$(SolutionDir)Other\\BuildEvents\\bin\\Debug\\BuildEvents.exe cppPostBuild $(Configuration) $(Platform)</Command>\n    </PostBuildEvent>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>MaxSpeed</Optimization>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\n      <StringPooling>true</StringPooling>\n      <BufferSecurityCheck>false</BufferSecurityCheck>\n      <LanguageStandard>stdcpp20</LanguageStandard>\n      <PreprocessorDefinitions>NDEBUG;Cpp_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <PrecompiledHeader>Use</PrecompiledHeader>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <ModuleDefinitionFile>Cpp.def</ModuleDefinitionFile>\n    </Link>\n    <PostBuildEvent>\n      <Command>$(SolutionDir)Other\\BuildEvents\\bin\\Debug\\BuildEvents.exe cppPostBuild $(Configuration) $(Platform)</Command>\n    </PostBuildEvent>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|ARM64'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>MaxSpeed</Optimization>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <PreprocessorDefinitions>NDEBUG;Cpp_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\n      <StringPooling>true</StringPooling>\n      <BufferSecurityCheck>false</BufferSecurityCheck>\n      <LanguageStandard>stdcpp20</LanguageStandard>\n      <PrecompiledHeader>Use</PrecompiledHeader>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <ModuleDefinitionFile>Cpp.def</ModuleDefinitionFile>\n    </Link>\n    <PostBuildEvent>\n      <Command>$(SolutionDir)Other\\BuildEvents\\bin\\Debug\\BuildEvents.exe cppPostBuild $(Configuration) $(Platform)</Command>\n    </PostBuildEvent>\n  </ItemDefinitionGroup>\n  <ItemGroup>\n    <ClCompile Include=\"acc bridge.cpp\" />\n    <ClCompile Include=\"acc get.cpp\" />\n    <ClCompile Include=\"acc java.cpp\" />\n    <ClCompile Include=\"acc func.cpp\" />\n    <ClCompile Include=\"acc web.cpp\" />\n    <ClCompile Include=\"acc workaround.cpp\" />\n    <ClCompile Include=\"MemoryPool.cpp\">\n      <PrecompiledHeader Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">NotUsing</PrecompiledHeader>\n      <PrecompiledHeader Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">NotUsing</PrecompiledHeader>\n      <PrecompiledHeader Condition=\"'$(Configuration)|$(Platform)'=='Debug|ARM64'\">NotUsing</PrecompiledHeader>\n      <PrecompiledHeader Condition=\"'$(Configuration)|$(Platform)'=='Release|ARM64'\">NotUsing</PrecompiledHeader>\n      <PrecompiledHeader Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">NotUsing</PrecompiledHeader>\n      <PrecompiledHeader Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">NotUsing</PrecompiledHeader>\n    </ClCompile>\n    <ClCompile Include=\"other.cpp\" />\n    <ClCompile Include=\"str.cpp\" />\n    <ClCompile Include=\"acc uia.cpp\" />\n    <ClCompile Include=\"in-proc.cpp\" />\n    <ClCompile Include=\"rejected.cpp\">\n      <ExcludedFromBuild Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">true</ExcludedFromBuild>\n      <ExcludedFromBuild Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">true</ExcludedFromBuild>\n      <ExcludedFromBuild Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">true</ExcludedFromBuild>\n      <ExcludedFromBuild Condition=\"'$(Configuration)|$(Platform)'=='Debug|ARM64'\">true</ExcludedFromBuild>\n      <ExcludedFromBuild Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">true</ExcludedFromBuild>\n      <ExcludedFromBuild Condition=\"'$(Configuration)|$(Platform)'=='Release|ARM64'\">true</ExcludedFromBuild>\n    </ClCompile>\n    <ClCompile Include=\"test.cpp\" />\n    <ClCompile Include=\"stdafx.cpp\">\n      <PrecompiledHeader Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">Create</PrecompiledHeader>\n      <PrecompiledHeader Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">Create</PrecompiledHeader>\n      <PrecompiledHeader Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">Create</PrecompiledHeader>\n      <PrecompiledHeader Condition=\"'$(Configuration)|$(Platform)'=='Debug|ARM64'\">Create</PrecompiledHeader>\n      <PrecompiledHeader Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">Create</PrecompiledHeader>\n      <PrecompiledHeader Condition=\"'$(Configuration)|$(Platform)'=='Release|ARM64'\">Create</PrecompiledHeader>\n    </ClCompile>\n    <ClCompile Include=\"acc find.cpp\" />\n    <ClCompile Include=\"test Uia.cpp\">\n      <ExcludedFromBuild Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">true</ExcludedFromBuild>\n      <ExcludedFromBuild Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">true</ExcludedFromBuild>\n      <ExcludedFromBuild Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">true</ExcludedFromBuild>\n      <ExcludedFromBuild Condition=\"'$(Configuration)|$(Platform)'=='Debug|ARM64'\">true</ExcludedFromBuild>\n      <ExcludedFromBuild Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">true</ExcludedFromBuild>\n      <ExcludedFromBuild Condition=\"'$(Configuration)|$(Platform)'=='Release|ARM64'\">true</ExcludedFromBuild>\n    </ClCompile>\n    <ClCompile Include=\"util.cpp\" />\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"Cpp.def\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ClInclude Include=\"acc.h\" />\n    <ClInclude Include=\"Cpp.h\" />\n    <ClInclude Include=\"IAccessible2.h\" />\n    <ClInclude Include=\"ISimpleDOMDocument.h\" />\n    <ClInclude Include=\"ISimpleDOMNode.h\" />\n    <ClInclude Include=\"ISimpleDOMText.h\" />\n    <ClInclude Include=\"JAB.h\" />\n    <ClInclude Include=\"MemoryPool.h\" />\n    <ClInclude Include=\"resource.h\" />\n    <ClInclude Include=\"str.h\" />\n    <ClInclude Include=\"internal.h\" />\n    <ClInclude Include=\"stdafx.h\" />\n    <ClInclude Include=\"util.h\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ProjectReference Include=\"..\\Libraries\\PCRE\\PCRE.vcxproj\">\n      <Project>{190bc611-ee00-4b10-87f2-a7e73b639e59}</Project>\n    </ProjectReference>\n  </ItemGroup>\n  <ItemGroup>\n    <Manifest Include=\"Cpp.manifest\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ResourceCompile Include=\"Cpp.rc\" />\n  </ItemGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\n  <ImportGroup Label=\"ExtensionTargets\">\n  </ImportGroup>\n</Project>"
  },
  {
    "path": "Cpp/Cpp.vcxproj.filters",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup>\n    <Filter Include=\"Source Files\">\n      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>\n      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>\n    </Filter>\n    <Filter Include=\"Header Files\">\n      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>\n      <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>\n    </Filter>\n    <Filter Include=\"Resource Files\">\n      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>\n      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>\n    </Filter>\n    <Filter Include=\"Source Files\\excluded\">\n      <UniqueIdentifier>{f751c698-699c-468e-b313-9e671a759bce}</UniqueIdentifier>\n    </Filter>\n    <Filter Include=\"Source Files\\util\">\n      <UniqueIdentifier>{37c1e08a-2650-49cc-8f21-56b63486efaf}</UniqueIdentifier>\n    </Filter>\n    <Filter Include=\"Source Files\\inactive\">\n      <UniqueIdentifier>{99af6249-7074-418b-8e33-94bba38046d9}</UniqueIdentifier>\n    </Filter>\n    <Filter Include=\"Source Files\\Acc\">\n      <UniqueIdentifier>{2173271c-7256-4ff0-8dd2-a9016fd326db}</UniqueIdentifier>\n    </Filter>\n    <Filter Include=\"Source Files\\other\">\n      <UniqueIdentifier>{e8318082-67dd-44f1-8bfd-e6fc6d1f1758}</UniqueIdentifier>\n    </Filter>\n    <Filter Include=\"Header Files\\interfaces\">\n      <UniqueIdentifier>{90b98acd-e3dc-495a-8cf1-aa3b8fe0f956}</UniqueIdentifier>\n    </Filter>\n  </ItemGroup>\n  <ItemGroup>\n    <ClCompile Include=\"test.cpp\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"test Uia.cpp\">\n      <Filter>Source Files\\excluded</Filter>\n    </ClCompile>\n    <ClCompile Include=\"rejected.cpp\">\n      <Filter>Source Files\\excluded</Filter>\n    </ClCompile>\n    <ClCompile Include=\"in-proc.cpp\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"util.cpp\">\n      <Filter>Source Files\\util</Filter>\n    </ClCompile>\n    <ClCompile Include=\"str.cpp\">\n      <Filter>Source Files\\util</Filter>\n    </ClCompile>\n    <ClCompile Include=\"acc bridge.cpp\">\n      <Filter>Source Files\\Acc</Filter>\n    </ClCompile>\n    <ClCompile Include=\"acc find.cpp\">\n      <Filter>Source Files\\Acc</Filter>\n    </ClCompile>\n    <ClCompile Include=\"acc func.cpp\">\n      <Filter>Source Files\\Acc</Filter>\n    </ClCompile>\n    <ClCompile Include=\"acc get.cpp\">\n      <Filter>Source Files\\Acc</Filter>\n    </ClCompile>\n    <ClCompile Include=\"acc java.cpp\">\n      <Filter>Source Files\\Acc</Filter>\n    </ClCompile>\n    <ClCompile Include=\"acc uia.cpp\">\n      <Filter>Source Files\\Acc</Filter>\n    </ClCompile>\n    <ClCompile Include=\"acc web.cpp\">\n      <Filter>Source Files\\Acc</Filter>\n    </ClCompile>\n    <ClCompile Include=\"other.cpp\">\n      <Filter>Source Files\\other</Filter>\n    </ClCompile>\n    <ClCompile Include=\"acc workaround.cpp\">\n      <Filter>Source Files\\other</Filter>\n    </ClCompile>\n    <ClCompile Include=\"MemoryPool.cpp\">\n      <Filter>Source Files\\util</Filter>\n    </ClCompile>\n    <ClCompile Include=\"stdafx.cpp\">\n      <Filter>Header Files</Filter>\n    </ClCompile>\n  </ItemGroup>\n  <ItemGroup>\n    <ClInclude Include=\"Cpp.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"stdafx.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"internal.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"str.h\">\n      <Filter>Source Files\\util</Filter>\n    </ClInclude>\n    <ClInclude Include=\"util.h\">\n      <Filter>Source Files\\util</Filter>\n    </ClInclude>\n    <ClInclude Include=\"JAB.h\">\n      <Filter>Source Files\\Acc</Filter>\n    </ClInclude>\n    <ClInclude Include=\"acc.h\">\n      <Filter>Source Files\\Acc</Filter>\n    </ClInclude>\n    <ClInclude Include=\"MemoryPool.h\">\n      <Filter>Source Files\\util</Filter>\n    </ClInclude>\n    <ClInclude Include=\"resource.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"IAccessible2.h\">\n      <Filter>Header Files\\interfaces</Filter>\n    </ClInclude>\n    <ClInclude Include=\"ISimpleDOMDocument.h\">\n      <Filter>Header Files\\interfaces</Filter>\n    </ClInclude>\n    <ClInclude Include=\"ISimpleDOMNode.h\">\n      <Filter>Header Files\\interfaces</Filter>\n    </ClInclude>\n    <ClInclude Include=\"ISimpleDOMText.h\">\n      <Filter>Header Files\\interfaces</Filter>\n    </ClInclude>\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"Cpp.def\">\n      <Filter>Header Files</Filter>\n    </None>\n  </ItemGroup>\n  <ItemGroup>\n    <Manifest Include=\"Cpp.manifest\">\n      <Filter>Resource Files</Filter>\n    </Manifest>\n  </ItemGroup>\n  <ItemGroup>\n    <ResourceCompile Include=\"Cpp.rc\">\n      <Filter>Resource Files</Filter>\n    </ResourceCompile>\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "Cpp/IAccessible2.h",
    "content": "\n\n/* this ALWAYS GENERATED file contains the definitions for the interfaces */\n\n\n /* File created by MIDL compiler version 7.00.0500 */\n/* at Sat Jan 18 19:37:28 2014\n */\n/* Compiler settings for .\\IAccessible2.idl:\n    Oicf, W1, Zp8, env=Win32 (32b run)\n    protocol : dce , ms_ext, c_ext, robust\n    error checks: allocation ref bounds_check enum stub_data \n    VC __declspec() decoration level: \n         __declspec(uuid()), __declspec(selectany), __declspec(novtable)\n         DECLSPEC_UUID(), MIDL_INTERFACE()\n*/\n//@@MIDL_FILE_HEADING(  )\n\n#pragma warning( disable: 4049 )  /* more than 64k source lines */\n\n\n/* verify that the <rpcndr.h> version is high enough to compile this file*/\n#ifndef __REQUIRED_RPCNDR_H_VERSION__\n#define __REQUIRED_RPCNDR_H_VERSION__ 475\n#endif\n\n#include \"rpc.h\"\n#include \"rpcndr.h\"\n\n#ifndef __RPCNDR_H_VERSION__\n#error this stub requires an updated version of <rpcndr.h>\n#endif // __RPCNDR_H_VERSION__\n\n#ifndef COM_NO_WINDOWS_H\n#include \"windows.h\"\n#include \"ole2.h\"\n#endif /*COM_NO_WINDOWS_H*/\n\n#ifndef __IAccessible2_h__\n#define __IAccessible2_h__\n\n#if defined(_MSC_VER) && (_MSC_VER >= 1020)\n#pragma once\n#endif\n\n/* Forward Declarations */ \n\n#ifndef __IAccessibleRelation_FWD_DEFINED__\n#define __IAccessibleRelation_FWD_DEFINED__\ntypedef interface IAccessibleRelation IAccessibleRelation;\n#endif \t/* __IAccessibleRelation_FWD_DEFINED__ */\n\n\n#ifndef __IAccessibleAction_FWD_DEFINED__\n#define __IAccessibleAction_FWD_DEFINED__\ntypedef interface IAccessibleAction IAccessibleAction;\n#endif \t/* __IAccessibleAction_FWD_DEFINED__ */\n\n\n#ifndef __IAccessible2_FWD_DEFINED__\n#define __IAccessible2_FWD_DEFINED__\ntypedef interface IAccessible2 IAccessible2;\n#endif \t/* __IAccessible2_FWD_DEFINED__ */\n\n\n#ifndef __IAccessible2_2_FWD_DEFINED__\n#define __IAccessible2_2_FWD_DEFINED__\ntypedef interface IAccessible2_2 IAccessible2_2;\n#endif \t/* __IAccessible2_2_FWD_DEFINED__ */\n\n\n#ifndef __IAccessibleComponent_FWD_DEFINED__\n#define __IAccessibleComponent_FWD_DEFINED__\ntypedef interface IAccessibleComponent IAccessibleComponent;\n#endif \t/* __IAccessibleComponent_FWD_DEFINED__ */\n\n\n#ifndef __IAccessibleValue_FWD_DEFINED__\n#define __IAccessibleValue_FWD_DEFINED__\ntypedef interface IAccessibleValue IAccessibleValue;\n#endif \t/* __IAccessibleValue_FWD_DEFINED__ */\n\n\n#ifndef __IAccessibleText_FWD_DEFINED__\n#define __IAccessibleText_FWD_DEFINED__\ntypedef interface IAccessibleText IAccessibleText;\n#endif \t/* __IAccessibleText_FWD_DEFINED__ */\n\n\n#ifndef __IAccessibleText2_FWD_DEFINED__\n#define __IAccessibleText2_FWD_DEFINED__\ntypedef interface IAccessibleText2 IAccessibleText2;\n#endif \t/* __IAccessibleText2_FWD_DEFINED__ */\n\n\n#ifndef __IAccessibleEditableText_FWD_DEFINED__\n#define __IAccessibleEditableText_FWD_DEFINED__\ntypedef interface IAccessibleEditableText IAccessibleEditableText;\n#endif \t/* __IAccessibleEditableText_FWD_DEFINED__ */\n\n\n#ifndef __IAccessibleHyperlink_FWD_DEFINED__\n#define __IAccessibleHyperlink_FWD_DEFINED__\ntypedef interface IAccessibleHyperlink IAccessibleHyperlink;\n#endif \t/* __IAccessibleHyperlink_FWD_DEFINED__ */\n\n\n#ifndef __IAccessibleHypertext_FWD_DEFINED__\n#define __IAccessibleHypertext_FWD_DEFINED__\ntypedef interface IAccessibleHypertext IAccessibleHypertext;\n#endif \t/* __IAccessibleHypertext_FWD_DEFINED__ */\n\n\n#ifndef __IAccessibleHypertext2_FWD_DEFINED__\n#define __IAccessibleHypertext2_FWD_DEFINED__\ntypedef interface IAccessibleHypertext2 IAccessibleHypertext2;\n#endif \t/* __IAccessibleHypertext2_FWD_DEFINED__ */\n\n\n#ifndef __IAccessibleTable_FWD_DEFINED__\n#define __IAccessibleTable_FWD_DEFINED__\ntypedef interface IAccessibleTable IAccessibleTable;\n#endif \t/* __IAccessibleTable_FWD_DEFINED__ */\n\n\n#ifndef __IAccessibleTable2_FWD_DEFINED__\n#define __IAccessibleTable2_FWD_DEFINED__\ntypedef interface IAccessibleTable2 IAccessibleTable2;\n#endif \t/* __IAccessibleTable2_FWD_DEFINED__ */\n\n\n#ifndef __IAccessibleTableCell_FWD_DEFINED__\n#define __IAccessibleTableCell_FWD_DEFINED__\ntypedef interface IAccessibleTableCell IAccessibleTableCell;\n#endif \t/* __IAccessibleTableCell_FWD_DEFINED__ */\n\n\n#ifndef __IAccessibleImage_FWD_DEFINED__\n#define __IAccessibleImage_FWD_DEFINED__\ntypedef interface IAccessibleImage IAccessibleImage;\n#endif \t/* __IAccessibleImage_FWD_DEFINED__ */\n\n\n#ifndef __IAccessibleApplication_FWD_DEFINED__\n#define __IAccessibleApplication_FWD_DEFINED__\ntypedef interface IAccessibleApplication IAccessibleApplication;\n#endif \t/* __IAccessibleApplication_FWD_DEFINED__ */\n\n\n#ifndef __IAccessibleDocument_FWD_DEFINED__\n#define __IAccessibleDocument_FWD_DEFINED__\ntypedef interface IAccessibleDocument IAccessibleDocument;\n#endif \t/* __IAccessibleDocument_FWD_DEFINED__ */\n\n\n#ifndef __IAccessible2_FWD_DEFINED__\n#define __IAccessible2_FWD_DEFINED__\ntypedef interface IAccessible2 IAccessible2;\n#endif \t/* __IAccessible2_FWD_DEFINED__ */\n\n\n#ifndef __IAccessible2_2_FWD_DEFINED__\n#define __IAccessible2_2_FWD_DEFINED__\ntypedef interface IAccessible2_2 IAccessible2_2;\n#endif \t/* __IAccessible2_2_FWD_DEFINED__ */\n\n\n#ifndef __IAccessibleAction_FWD_DEFINED__\n#define __IAccessibleAction_FWD_DEFINED__\ntypedef interface IAccessibleAction IAccessibleAction;\n#endif \t/* __IAccessibleAction_FWD_DEFINED__ */\n\n\n#ifndef __IAccessibleApplication_FWD_DEFINED__\n#define __IAccessibleApplication_FWD_DEFINED__\ntypedef interface IAccessibleApplication IAccessibleApplication;\n#endif \t/* __IAccessibleApplication_FWD_DEFINED__ */\n\n\n#ifndef __IAccessibleComponent_FWD_DEFINED__\n#define __IAccessibleComponent_FWD_DEFINED__\ntypedef interface IAccessibleComponent IAccessibleComponent;\n#endif \t/* __IAccessibleComponent_FWD_DEFINED__ */\n\n\n#ifndef __IAccessibleDocument_FWD_DEFINED__\n#define __IAccessibleDocument_FWD_DEFINED__\ntypedef interface IAccessibleDocument IAccessibleDocument;\n#endif \t/* __IAccessibleDocument_FWD_DEFINED__ */\n\n\n#ifndef __IAccessibleEditableText_FWD_DEFINED__\n#define __IAccessibleEditableText_FWD_DEFINED__\ntypedef interface IAccessibleEditableText IAccessibleEditableText;\n#endif \t/* __IAccessibleEditableText_FWD_DEFINED__ */\n\n\n#ifndef __IAccessibleHyperlink_FWD_DEFINED__\n#define __IAccessibleHyperlink_FWD_DEFINED__\ntypedef interface IAccessibleHyperlink IAccessibleHyperlink;\n#endif \t/* __IAccessibleHyperlink_FWD_DEFINED__ */\n\n\n#ifndef __IAccessibleText_FWD_DEFINED__\n#define __IAccessibleText_FWD_DEFINED__\ntypedef interface IAccessibleText IAccessibleText;\n#endif \t/* __IAccessibleText_FWD_DEFINED__ */\n\n\n#ifndef __IAccessibleHypertext_FWD_DEFINED__\n#define __IAccessibleHypertext_FWD_DEFINED__\ntypedef interface IAccessibleHypertext IAccessibleHypertext;\n#endif \t/* __IAccessibleHypertext_FWD_DEFINED__ */\n\n\n#ifndef __IAccessibleHypertext2_FWD_DEFINED__\n#define __IAccessibleHypertext2_FWD_DEFINED__\ntypedef interface IAccessibleHypertext2 IAccessibleHypertext2;\n#endif \t/* __IAccessibleHypertext2_FWD_DEFINED__ */\n\n\n#ifndef __IAccessibleImage_FWD_DEFINED__\n#define __IAccessibleImage_FWD_DEFINED__\ntypedef interface IAccessibleImage IAccessibleImage;\n#endif \t/* __IAccessibleImage_FWD_DEFINED__ */\n\n\n#ifndef __IAccessibleRelation_FWD_DEFINED__\n#define __IAccessibleRelation_FWD_DEFINED__\ntypedef interface IAccessibleRelation IAccessibleRelation;\n#endif \t/* __IAccessibleRelation_FWD_DEFINED__ */\n\n\n#ifndef __IAccessibleTable_FWD_DEFINED__\n#define __IAccessibleTable_FWD_DEFINED__\ntypedef interface IAccessibleTable IAccessibleTable;\n#endif \t/* __IAccessibleTable_FWD_DEFINED__ */\n\n\n#ifndef __IAccessibleTable2_FWD_DEFINED__\n#define __IAccessibleTable2_FWD_DEFINED__\ntypedef interface IAccessibleTable2 IAccessibleTable2;\n#endif \t/* __IAccessibleTable2_FWD_DEFINED__ */\n\n\n#ifndef __IAccessibleTableCell_FWD_DEFINED__\n#define __IAccessibleTableCell_FWD_DEFINED__\ntypedef interface IAccessibleTableCell IAccessibleTableCell;\n#endif \t/* __IAccessibleTableCell_FWD_DEFINED__ */\n\n\n#ifndef __IAccessibleText2_FWD_DEFINED__\n#define __IAccessibleText2_FWD_DEFINED__\ntypedef interface IAccessibleText2 IAccessibleText2;\n#endif \t/* __IAccessibleText2_FWD_DEFINED__ */\n\n\n#ifndef __IAccessibleValue_FWD_DEFINED__\n#define __IAccessibleValue_FWD_DEFINED__\ntypedef interface IAccessibleValue IAccessibleValue;\n#endif \t/* __IAccessibleValue_FWD_DEFINED__ */\n\n\n/* header files for imported files */\n#include \"objidl.h\"\n#include \"oaidl.h\"\n#include \"oleacc.h\"\n\n#ifdef __cplusplus\nextern \"C\"{\n#endif \n\n\n/* interface __MIDL_itf_IAccessible2_0000_0000 */\n/* [local] */ \n\n\nenum IA2ScrollType\n    {\tIA2_SCROLL_TYPE_TOP_LEFT\t= 0,\n\tIA2_SCROLL_TYPE_BOTTOM_RIGHT\t= ( IA2_SCROLL_TYPE_TOP_LEFT + 1 ) ,\n\tIA2_SCROLL_TYPE_TOP_EDGE\t= ( IA2_SCROLL_TYPE_BOTTOM_RIGHT + 1 ) ,\n\tIA2_SCROLL_TYPE_BOTTOM_EDGE\t= ( IA2_SCROLL_TYPE_TOP_EDGE + 1 ) ,\n\tIA2_SCROLL_TYPE_LEFT_EDGE\t= ( IA2_SCROLL_TYPE_BOTTOM_EDGE + 1 ) ,\n\tIA2_SCROLL_TYPE_RIGHT_EDGE\t= ( IA2_SCROLL_TYPE_LEFT_EDGE + 1 ) ,\n\tIA2_SCROLL_TYPE_ANYWHERE\t= ( IA2_SCROLL_TYPE_RIGHT_EDGE + 1 ) \n    } ;\n\nenum IA2CoordinateType\n    {\tIA2_COORDTYPE_SCREEN_RELATIVE\t= 0,\n\tIA2_COORDTYPE_PARENT_RELATIVE\t= ( IA2_COORDTYPE_SCREEN_RELATIVE + 1 ) \n    } ;\n\nenum IA2TextSpecialOffsets\n    {\tIA2_TEXT_OFFSET_LENGTH\t= -1,\n\tIA2_TEXT_OFFSET_CARET\t= -2\n    } ;\n\nenum IA2TableModelChangeType\n    {\tIA2_TABLE_MODEL_CHANGE_INSERT\t= 0,\n\tIA2_TABLE_MODEL_CHANGE_DELETE\t= ( IA2_TABLE_MODEL_CHANGE_INSERT + 1 ) ,\n\tIA2_TABLE_MODEL_CHANGE_UPDATE\t= ( IA2_TABLE_MODEL_CHANGE_DELETE + 1 ) \n    } ;\ntypedef struct IA2TableModelChange\n    {\n    enum IA2TableModelChangeType type;\n    long firstRow;\n    long lastRow;\n    long firstColumn;\n    long lastColumn;\n    } \tIA2TableModelChange;\n\n#define\tIA2_RELATION_CONTAINING_APPLICATION\t( L\"containingApplication\" )\n\n#define\tIA2_RELATION_CONTAINING_DOCUMENT\t( L\"containingDocument\" )\n\n#define\tIA2_RELATION_CONTAINING_TAB_PANE\t( L\"containingTabPane\" )\n\n#define\tIA2_RELATION_CONTAINING_WINDOW\t( L\"containingWindow\" )\n\n#define\tIA2_RELATION_CONTROLLED_BY\t( L\"controlledBy\" )\n\n#define\tIA2_RELATION_CONTROLLER_FOR\t( L\"controllerFor\" )\n\n#define\tIA2_RELATION_DESCRIBED_BY\t( L\"describedBy\" )\n\n#define\tIA2_RELATION_DESCRIPTION_FOR\t( L\"descriptionFor\" )\n\n#define\tIA2_RELATION_EMBEDDED_BY\t( L\"embeddedBy\" )\n\n#define\tIA2_RELATION_EMBEDS\t( L\"embeds\" )\n\n#define\tIA2_RELATION_FLOWS_FROM\t( L\"flowsFrom\" )\n\n#define\tIA2_RELATION_FLOWS_TO\t( L\"flowsTo\" )\n\n#define\tIA2_RELATION_LABEL_FOR\t( L\"labelFor\" )\n\n#define\tIA2_RELATION_LABELED_BY\t( L\"labelledBy\" )\n\n#define\tIA2_RELATION_LABELLED_BY\t( L\"labelledBy\" )\n\n#define\tIA2_RELATION_MEMBER_OF\t( L\"memberOf\" )\n\n#define\tIA2_RELATION_NEXT_TABBABLE\t( L\"nextTabbable\" )\n\n#define\tIA2_RELATION_NODE_CHILD_OF\t( L\"nodeChildOf\" )\n\n#define\tIA2_RELATION_NODE_PARENT_OF\t( L\"nodeParentOf\" )\n\n#define\tIA2_RELATION_PARENT_WINDOW_OF\t( L\"parentWindowOf\" )\n\n#define\tIA2_RELATION_POPUP_FOR\t( L\"popupFor\" )\n\n#define\tIA2_RELATION_PREVIOUS_TABBABLE\t( L\"previousTabbable\" )\n\n#define\tIA2_RELATION_SUBWINDOW_OF\t( L\"subwindowOf\" )\n\n\n\nextern RPC_IF_HANDLE __MIDL_itf_IAccessible2_0000_0000_v0_0_c_ifspec;\nextern RPC_IF_HANDLE __MIDL_itf_IAccessible2_0000_0000_v0_0_s_ifspec;\n\n#ifndef __IAccessibleRelation_INTERFACE_DEFINED__\n#define __IAccessibleRelation_INTERFACE_DEFINED__\n\n/* interface IAccessibleRelation */\n/* [uuid][object] */ \n\n\nEXTERN_C const IID IID_IAccessibleRelation;\n\n#if defined(__cplusplus) && !defined(CINTERFACE)\n    \n    MIDL_INTERFACE(\"7CDF86EE-C3DA-496a-BDA4-281B336E1FDC\")\n    IAccessibleRelation : public IUnknown\n    {\n    public:\n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_relationType( \n            /* [retval][out] */ BSTR *relationType) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_localizedRelationType( \n            /* [retval][out] */ BSTR *localizedRelationType) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_nTargets( \n            /* [retval][out] */ long *nTargets) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_target( \n            /* [in] */ long targetIndex,\n            /* [retval][out] */ IUnknown **target) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_targets( \n            /* [in] */ long maxTargets,\n            /* [length_is][size_is][out] */ IUnknown **targets,\n            /* [retval][out] */ long *nTargets) = 0;\n        \n    };\n    \n#else \t/* C style interface */\n\n    typedef struct IAccessibleRelationVtbl\n    {\n        BEGIN_INTERFACE\n        \n        HRESULT ( STDMETHODCALLTYPE *QueryInterface )( \n            IAccessibleRelation * This,\n            /* [in] */ REFIID riid,\n            /* [iid_is][out] */ \n            __RPC__deref_out  void **ppvObject);\n        \n        ULONG ( STDMETHODCALLTYPE *AddRef )( \n            IAccessibleRelation * This);\n        \n        ULONG ( STDMETHODCALLTYPE *Release )( \n            IAccessibleRelation * This);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_relationType )( \n            IAccessibleRelation * This,\n            /* [retval][out] */ BSTR *relationType);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_localizedRelationType )( \n            IAccessibleRelation * This,\n            /* [retval][out] */ BSTR *localizedRelationType);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_nTargets )( \n            IAccessibleRelation * This,\n            /* [retval][out] */ long *nTargets);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_target )( \n            IAccessibleRelation * This,\n            /* [in] */ long targetIndex,\n            /* [retval][out] */ IUnknown **target);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_targets )( \n            IAccessibleRelation * This,\n            /* [in] */ long maxTargets,\n            /* [length_is][size_is][out] */ IUnknown **targets,\n            /* [retval][out] */ long *nTargets);\n        \n        END_INTERFACE\n    } IAccessibleRelationVtbl;\n\n    interface IAccessibleRelation\n    {\n        CONST_VTBL struct IAccessibleRelationVtbl *lpVtbl;\n    };\n\n    \n\n#ifdef COBJMACROS\n\n\n#define IAccessibleRelation_QueryInterface(This,riid,ppvObject)\t\\\n    ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) \n\n#define IAccessibleRelation_AddRef(This)\t\\\n    ( (This)->lpVtbl -> AddRef(This) ) \n\n#define IAccessibleRelation_Release(This)\t\\\n    ( (This)->lpVtbl -> Release(This) ) \n\n\n#define IAccessibleRelation_get_relationType(This,relationType)\t\\\n    ( (This)->lpVtbl -> get_relationType(This,relationType) ) \n\n#define IAccessibleRelation_get_localizedRelationType(This,localizedRelationType)\t\\\n    ( (This)->lpVtbl -> get_localizedRelationType(This,localizedRelationType) ) \n\n#define IAccessibleRelation_get_nTargets(This,nTargets)\t\\\n    ( (This)->lpVtbl -> get_nTargets(This,nTargets) ) \n\n#define IAccessibleRelation_get_target(This,targetIndex,target)\t\\\n    ( (This)->lpVtbl -> get_target(This,targetIndex,target) ) \n\n#define IAccessibleRelation_get_targets(This,maxTargets,targets,nTargets)\t\\\n    ( (This)->lpVtbl -> get_targets(This,maxTargets,targets,nTargets) ) \n\n#endif /* COBJMACROS */\n\n\n#endif \t/* C style interface */\n\n\n\n\n#endif \t/* __IAccessibleRelation_INTERFACE_DEFINED__ */\n\n\n/* interface __MIDL_itf_IAccessible2_0000_0001 */\n/* [local] */ \n\n\nenum IA2Actions\n    {\tIA2_ACTION_OPEN\t= -1,\n\tIA2_ACTION_COMPLETE\t= -2,\n\tIA2_ACTION_CLOSE\t= -3\n    } ;\n\n\nextern RPC_IF_HANDLE __MIDL_itf_IAccessible2_0000_0001_v0_0_c_ifspec;\nextern RPC_IF_HANDLE __MIDL_itf_IAccessible2_0000_0001_v0_0_s_ifspec;\n\n#ifndef __IAccessibleAction_INTERFACE_DEFINED__\n#define __IAccessibleAction_INTERFACE_DEFINED__\n\n/* interface IAccessibleAction */\n/* [uuid][object] */ \n\n\nEXTERN_C const IID IID_IAccessibleAction;\n\n#if defined(__cplusplus) && !defined(CINTERFACE)\n    \n    MIDL_INTERFACE(\"B70D9F59-3B5A-4dba-AB9E-22012F607DF5\")\n    IAccessibleAction : public IUnknown\n    {\n    public:\n        virtual HRESULT STDMETHODCALLTYPE nActions( \n            /* [retval][out] */ long *nActions) = 0;\n        \n        virtual HRESULT STDMETHODCALLTYPE doAction( \n            /* [in] */ long actionIndex) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_description( \n            /* [in] */ long actionIndex,\n            /* [retval][out] */ BSTR *description) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_keyBinding( \n            /* [in] */ long actionIndex,\n            /* [in] */ long nMaxBindings,\n            /* [length_is][length_is][size_is][size_is][out] */ BSTR **keyBindings,\n            /* [retval][out] */ long *nBindings) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_name( \n            /* [in] */ long actionIndex,\n            /* [retval][out] */ BSTR *name) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_localizedName( \n            /* [in] */ long actionIndex,\n            /* [retval][out] */ BSTR *localizedName) = 0;\n        \n    };\n    \n#else \t/* C style interface */\n\n    typedef struct IAccessibleActionVtbl\n    {\n        BEGIN_INTERFACE\n        \n        HRESULT ( STDMETHODCALLTYPE *QueryInterface )( \n            IAccessibleAction * This,\n            /* [in] */ REFIID riid,\n            /* [iid_is][out] */ \n            __RPC__deref_out  void **ppvObject);\n        \n        ULONG ( STDMETHODCALLTYPE *AddRef )( \n            IAccessibleAction * This);\n        \n        ULONG ( STDMETHODCALLTYPE *Release )( \n            IAccessibleAction * This);\n        \n        HRESULT ( STDMETHODCALLTYPE *nActions )( \n            IAccessibleAction * This,\n            /* [retval][out] */ long *nActions);\n        \n        HRESULT ( STDMETHODCALLTYPE *doAction )( \n            IAccessibleAction * This,\n            /* [in] */ long actionIndex);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_description )( \n            IAccessibleAction * This,\n            /* [in] */ long actionIndex,\n            /* [retval][out] */ BSTR *description);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_keyBinding )( \n            IAccessibleAction * This,\n            /* [in] */ long actionIndex,\n            /* [in] */ long nMaxBindings,\n            /* [length_is][length_is][size_is][size_is][out] */ BSTR **keyBindings,\n            /* [retval][out] */ long *nBindings);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_name )( \n            IAccessibleAction * This,\n            /* [in] */ long actionIndex,\n            /* [retval][out] */ BSTR *name);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_localizedName )( \n            IAccessibleAction * This,\n            /* [in] */ long actionIndex,\n            /* [retval][out] */ BSTR *localizedName);\n        \n        END_INTERFACE\n    } IAccessibleActionVtbl;\n\n    interface IAccessibleAction\n    {\n        CONST_VTBL struct IAccessibleActionVtbl *lpVtbl;\n    };\n\n    \n\n#ifdef COBJMACROS\n\n\n#define IAccessibleAction_QueryInterface(This,riid,ppvObject)\t\\\n    ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) \n\n#define IAccessibleAction_AddRef(This)\t\\\n    ( (This)->lpVtbl -> AddRef(This) ) \n\n#define IAccessibleAction_Release(This)\t\\\n    ( (This)->lpVtbl -> Release(This) ) \n\n\n#define IAccessibleAction_nActions(This,nActions)\t\\\n    ( (This)->lpVtbl -> nActions(This,nActions) ) \n\n#define IAccessibleAction_doAction(This,actionIndex)\t\\\n    ( (This)->lpVtbl -> doAction(This,actionIndex) ) \n\n#define IAccessibleAction_get_description(This,actionIndex,description)\t\\\n    ( (This)->lpVtbl -> get_description(This,actionIndex,description) ) \n\n#define IAccessibleAction_get_keyBinding(This,actionIndex,nMaxBindings,keyBindings,nBindings)\t\\\n    ( (This)->lpVtbl -> get_keyBinding(This,actionIndex,nMaxBindings,keyBindings,nBindings) ) \n\n#define IAccessibleAction_get_name(This,actionIndex,name)\t\\\n    ( (This)->lpVtbl -> get_name(This,actionIndex,name) ) \n\n#define IAccessibleAction_get_localizedName(This,actionIndex,localizedName)\t\\\n    ( (This)->lpVtbl -> get_localizedName(This,actionIndex,localizedName) ) \n\n#endif /* COBJMACROS */\n\n\n#endif \t/* C style interface */\n\n\n\n\n#endif \t/* __IAccessibleAction_INTERFACE_DEFINED__ */\n\n\n/* interface __MIDL_itf_IAccessible2_0000_0002 */\n/* [local] */ \n\n\nenum IA2Role\n    {\tIA2_ROLE_UNKNOWN\t= 0,\n\tIA2_ROLE_CANVAS\t= 0x401,\n\tIA2_ROLE_CAPTION\t= ( IA2_ROLE_CANVAS + 1 ) ,\n\tIA2_ROLE_CHECK_MENU_ITEM\t= ( IA2_ROLE_CAPTION + 1 ) ,\n\tIA2_ROLE_COLOR_CHOOSER\t= ( IA2_ROLE_CHECK_MENU_ITEM + 1 ) ,\n\tIA2_ROLE_DATE_EDITOR\t= ( IA2_ROLE_COLOR_CHOOSER + 1 ) ,\n\tIA2_ROLE_DESKTOP_ICON\t= ( IA2_ROLE_DATE_EDITOR + 1 ) ,\n\tIA2_ROLE_DESKTOP_PANE\t= ( IA2_ROLE_DESKTOP_ICON + 1 ) ,\n\tIA2_ROLE_DIRECTORY_PANE\t= ( IA2_ROLE_DESKTOP_PANE + 1 ) ,\n\tIA2_ROLE_EDITBAR\t= ( IA2_ROLE_DIRECTORY_PANE + 1 ) ,\n\tIA2_ROLE_EMBEDDED_OBJECT\t= ( IA2_ROLE_EDITBAR + 1 ) ,\n\tIA2_ROLE_ENDNOTE\t= ( IA2_ROLE_EMBEDDED_OBJECT + 1 ) ,\n\tIA2_ROLE_FILE_CHOOSER\t= ( IA2_ROLE_ENDNOTE + 1 ) ,\n\tIA2_ROLE_FONT_CHOOSER\t= ( IA2_ROLE_FILE_CHOOSER + 1 ) ,\n\tIA2_ROLE_FOOTER\t= ( IA2_ROLE_FONT_CHOOSER + 1 ) ,\n\tIA2_ROLE_FOOTNOTE\t= ( IA2_ROLE_FOOTER + 1 ) ,\n\tIA2_ROLE_FORM\t= ( IA2_ROLE_FOOTNOTE + 1 ) ,\n\tIA2_ROLE_FRAME\t= ( IA2_ROLE_FORM + 1 ) ,\n\tIA2_ROLE_GLASS_PANE\t= ( IA2_ROLE_FRAME + 1 ) ,\n\tIA2_ROLE_HEADER\t= ( IA2_ROLE_GLASS_PANE + 1 ) ,\n\tIA2_ROLE_HEADING\t= ( IA2_ROLE_HEADER + 1 ) ,\n\tIA2_ROLE_ICON\t= ( IA2_ROLE_HEADING + 1 ) ,\n\tIA2_ROLE_IMAGE_MAP\t= ( IA2_ROLE_ICON + 1 ) ,\n\tIA2_ROLE_INPUT_METHOD_WINDOW\t= ( IA2_ROLE_IMAGE_MAP + 1 ) ,\n\tIA2_ROLE_INTERNAL_FRAME\t= ( IA2_ROLE_INPUT_METHOD_WINDOW + 1 ) ,\n\tIA2_ROLE_LABEL\t= ( IA2_ROLE_INTERNAL_FRAME + 1 ) ,\n\tIA2_ROLE_LAYERED_PANE\t= ( IA2_ROLE_LABEL + 1 ) ,\n\tIA2_ROLE_NOTE\t= ( IA2_ROLE_LAYERED_PANE + 1 ) ,\n\tIA2_ROLE_OPTION_PANE\t= ( IA2_ROLE_NOTE + 1 ) ,\n\tIA2_ROLE_PAGE\t= ( IA2_ROLE_OPTION_PANE + 1 ) ,\n\tIA2_ROLE_PARAGRAPH\t= ( IA2_ROLE_PAGE + 1 ) ,\n\tIA2_ROLE_RADIO_MENU_ITEM\t= ( IA2_ROLE_PARAGRAPH + 1 ) ,\n\tIA2_ROLE_REDUNDANT_OBJECT\t= ( IA2_ROLE_RADIO_MENU_ITEM + 1 ) ,\n\tIA2_ROLE_ROOT_PANE\t= ( IA2_ROLE_REDUNDANT_OBJECT + 1 ) ,\n\tIA2_ROLE_RULER\t= ( IA2_ROLE_ROOT_PANE + 1 ) ,\n\tIA2_ROLE_SCROLL_PANE\t= ( IA2_ROLE_RULER + 1 ) ,\n\tIA2_ROLE_SECTION\t= ( IA2_ROLE_SCROLL_PANE + 1 ) ,\n\tIA2_ROLE_SHAPE\t= ( IA2_ROLE_SECTION + 1 ) ,\n\tIA2_ROLE_SPLIT_PANE\t= ( IA2_ROLE_SHAPE + 1 ) ,\n\tIA2_ROLE_TEAR_OFF_MENU\t= ( IA2_ROLE_SPLIT_PANE + 1 ) ,\n\tIA2_ROLE_TERMINAL\t= ( IA2_ROLE_TEAR_OFF_MENU + 1 ) ,\n\tIA2_ROLE_TEXT_FRAME\t= ( IA2_ROLE_TERMINAL + 1 ) ,\n\tIA2_ROLE_TOGGLE_BUTTON\t= ( IA2_ROLE_TEXT_FRAME + 1 ) ,\n\tIA2_ROLE_VIEW_PORT\t= ( IA2_ROLE_TOGGLE_BUTTON + 1 ) ,\n\tIA2_ROLE_COMPLEMENTARY_CONTENT\t= ( IA2_ROLE_VIEW_PORT + 1 ) \n    } ;\ntypedef long AccessibleStates;\n\n\nenum IA2States\n    {\tIA2_STATE_ACTIVE\t= 0x1,\n\tIA2_STATE_ARMED\t= 0x2,\n\tIA2_STATE_DEFUNCT\t= 0x4,\n\tIA2_STATE_EDITABLE\t= 0x8,\n\tIA2_STATE_HORIZONTAL\t= 0x10,\n\tIA2_STATE_ICONIFIED\t= 0x20,\n\tIA2_STATE_INVALID_ENTRY\t= 0x40,\n\tIA2_STATE_MANAGES_DESCENDANTS\t= 0x80,\n\tIA2_STATE_MODAL\t= 0x100,\n\tIA2_STATE_MULTI_LINE\t= 0x200,\n\tIA2_STATE_OPAQUE\t= 0x400,\n\tIA2_STATE_REQUIRED\t= 0x800,\n\tIA2_STATE_SELECTABLE_TEXT\t= 0x1000,\n\tIA2_STATE_SINGLE_LINE\t= 0x2000,\n\tIA2_STATE_STALE\t= 0x4000,\n\tIA2_STATE_SUPPORTS_AUTOCOMPLETION\t= 0x8000,\n\tIA2_STATE_TRANSIENT\t= 0x10000,\n\tIA2_STATE_VERTICAL\t= 0x20000,\n\tIA2_STATE_CHECKABLE\t= 0x40000,\n\tIA2_STATE_PINNED\t= 0x80000\n    } ;\ntypedef struct IA2Locale\n    {\n    BSTR language;\n    BSTR country;\n    BSTR variant;\n    } \tIA2Locale;\n\n\n\nextern RPC_IF_HANDLE __MIDL_itf_IAccessible2_0000_0002_v0_0_c_ifspec;\nextern RPC_IF_HANDLE __MIDL_itf_IAccessible2_0000_0002_v0_0_s_ifspec;\n\n#ifndef __IAccessible2_INTERFACE_DEFINED__\n#define __IAccessible2_INTERFACE_DEFINED__\n\n/* interface IAccessible2 */\n/* [uuid][object] */ \n\n\nEXTERN_C const IID IID_IAccessible2;\n\n#if defined(__cplusplus) && !defined(CINTERFACE)\n    \n    MIDL_INTERFACE(\"E89F726E-C4F4-4c19-BB19-B647D7FA8478\")\n    IAccessible2 : public IAccessible\n    {\n    public:\n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_nRelations( \n            /* [retval][out] */ long *nRelations) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_relation( \n            /* [in] */ long relationIndex,\n            /* [retval][out] */ IAccessibleRelation **relation) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_relations( \n            /* [in] */ long maxRelations,\n            /* [length_is][size_is][out] */ IAccessibleRelation **relations,\n            /* [retval][out] */ long *nRelations) = 0;\n        \n        virtual HRESULT STDMETHODCALLTYPE role( \n            /* [retval][out] */ long *role) = 0;\n        \n        virtual HRESULT STDMETHODCALLTYPE scrollTo( \n            /* [in] */ enum IA2ScrollType scrollType) = 0;\n        \n        virtual HRESULT STDMETHODCALLTYPE scrollToPoint( \n            /* [in] */ enum IA2CoordinateType coordinateType,\n            /* [in] */ long x,\n            /* [in] */ long y) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_groupPosition( \n            /* [out] */ long *groupLevel,\n            /* [out] */ long *similarItemsInGroup,\n            /* [retval][out] */ long *positionInGroup) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_states( \n            /* [retval][out] */ AccessibleStates *states) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_extendedRole( \n            /* [retval][out] */ BSTR *extendedRole) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_localizedExtendedRole( \n            /* [retval][out] */ BSTR *localizedExtendedRole) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_nExtendedStates( \n            /* [retval][out] */ long *nExtendedStates) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_extendedStates( \n            /* [in] */ long maxExtendedStates,\n            /* [length_is][length_is][size_is][size_is][out] */ BSTR **extendedStates,\n            /* [retval][out] */ long *nExtendedStates) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_localizedExtendedStates( \n            /* [in] */ long maxLocalizedExtendedStates,\n            /* [length_is][length_is][size_is][size_is][out] */ BSTR **localizedExtendedStates,\n            /* [retval][out] */ long *nLocalizedExtendedStates) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_uniqueID( \n            /* [retval][out] */ long *uniqueID) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_windowHandle( \n            /* [retval][out] */ HWND *windowHandle) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_indexInParent( \n            /* [retval][out] */ long *indexInParent) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_locale( \n            /* [retval][out] */ IA2Locale *locale) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_attributes( \n            /* [retval][out] */ BSTR *attributes) = 0;\n        \n    };\n    \n#else \t/* C style interface */\n\n    typedef struct IAccessible2Vtbl\n    {\n        BEGIN_INTERFACE\n        \n        HRESULT ( STDMETHODCALLTYPE *QueryInterface )( \n            IAccessible2 * This,\n            /* [in] */ REFIID riid,\n            /* [iid_is][out] */ \n            __RPC__deref_out  void **ppvObject);\n        \n        ULONG ( STDMETHODCALLTYPE *AddRef )( \n            IAccessible2 * This);\n        \n        ULONG ( STDMETHODCALLTYPE *Release )( \n            IAccessible2 * This);\n        \n        HRESULT ( STDMETHODCALLTYPE *GetTypeInfoCount )( \n            IAccessible2 * This,\n            /* [out] */ UINT *pctinfo);\n        \n        HRESULT ( STDMETHODCALLTYPE *GetTypeInfo )( \n            IAccessible2 * This,\n            /* [in] */ UINT iTInfo,\n            /* [in] */ LCID lcid,\n            /* [out] */ ITypeInfo **ppTInfo);\n        \n        HRESULT ( STDMETHODCALLTYPE *GetIDsOfNames )( \n            IAccessible2 * This,\n            /* [in] */ REFIID riid,\n            /* [size_is][in] */ LPOLESTR *rgszNames,\n            /* [range][in] */ UINT cNames,\n            /* [in] */ LCID lcid,\n            /* [size_is][out] */ DISPID *rgDispId);\n        \n        /* [local] */ HRESULT ( STDMETHODCALLTYPE *Invoke )( \n            IAccessible2 * This,\n            /* [in] */ DISPID dispIdMember,\n            /* [in] */ REFIID riid,\n            /* [in] */ LCID lcid,\n            /* [in] */ WORD wFlags,\n            /* [out][in] */ DISPPARAMS *pDispParams,\n            /* [out] */ VARIANT *pVarResult,\n            /* [out] */ EXCEPINFO *pExcepInfo,\n            /* [out] */ UINT *puArgErr);\n        \n        /* [id][propget][hidden] */ HRESULT ( STDMETHODCALLTYPE *get_accParent )( \n            IAccessible2 * This,\n            /* [retval][out] */ IDispatch **ppdispParent);\n        \n        /* [id][propget][hidden] */ HRESULT ( STDMETHODCALLTYPE *get_accChildCount )( \n            IAccessible2 * This,\n            /* [retval][out] */ long *pcountChildren);\n        \n        /* [id][propget][hidden] */ HRESULT ( STDMETHODCALLTYPE *get_accChild )( \n            IAccessible2 * This,\n            /* [in] */ VARIANT varChild,\n            /* [retval][out] */ IDispatch **ppdispChild);\n        \n        /* [id][propget][hidden] */ HRESULT ( STDMETHODCALLTYPE *get_accName )( \n            IAccessible2 * This,\n            /* [optional][in] */ VARIANT varChild,\n            /* [retval][out] */ BSTR *pszName);\n        \n        /* [id][propget][hidden] */ HRESULT ( STDMETHODCALLTYPE *get_accValue )( \n            IAccessible2 * This,\n            /* [optional][in] */ VARIANT varChild,\n            /* [retval][out] */ BSTR *pszValue);\n        \n        /* [id][propget][hidden] */ HRESULT ( STDMETHODCALLTYPE *get_accDescription )( \n            IAccessible2 * This,\n            /* [optional][in] */ VARIANT varChild,\n            /* [retval][out] */ BSTR *pszDescription);\n        \n        /* [id][propget][hidden] */ HRESULT ( STDMETHODCALLTYPE *get_accRole )( \n            IAccessible2 * This,\n            /* [optional][in] */ VARIANT varChild,\n            /* [retval][out] */ VARIANT *pvarRole);\n        \n        /* [id][propget][hidden] */ HRESULT ( STDMETHODCALLTYPE *get_accState )( \n            IAccessible2 * This,\n            /* [optional][in] */ VARIANT varChild,\n            /* [retval][out] */ VARIANT *pvarState);\n        \n        /* [id][propget][hidden] */ HRESULT ( STDMETHODCALLTYPE *get_accHelp )( \n            IAccessible2 * This,\n            /* [optional][in] */ VARIANT varChild,\n            /* [retval][out] */ BSTR *pszHelp);\n        \n        /* [id][propget][hidden] */ HRESULT ( STDMETHODCALLTYPE *get_accHelpTopic )( \n            IAccessible2 * This,\n            /* [out] */ BSTR *pszHelpFile,\n            /* [optional][in] */ VARIANT varChild,\n            /* [retval][out] */ long *pidTopic);\n        \n        /* [id][propget][hidden] */ HRESULT ( STDMETHODCALLTYPE *get_accKeyboardShortcut )( \n            IAccessible2 * This,\n            /* [optional][in] */ VARIANT varChild,\n            /* [retval][out] */ BSTR *pszKeyboardShortcut);\n        \n        /* [id][propget][hidden] */ HRESULT ( STDMETHODCALLTYPE *get_accFocus )( \n            IAccessible2 * This,\n            /* [retval][out] */ VARIANT *pvarChild);\n        \n        /* [id][propget][hidden] */ HRESULT ( STDMETHODCALLTYPE *get_accSelection )( \n            IAccessible2 * This,\n            /* [retval][out] */ VARIANT *pvarChildren);\n        \n        /* [id][propget][hidden] */ HRESULT ( STDMETHODCALLTYPE *get_accDefaultAction )( \n            IAccessible2 * This,\n            /* [optional][in] */ VARIANT varChild,\n            /* [retval][out] */ BSTR *pszDefaultAction);\n        \n        /* [id][hidden] */ HRESULT ( STDMETHODCALLTYPE *accSelect )( \n            IAccessible2 * This,\n            /* [in] */ long flagsSelect,\n            /* [optional][in] */ VARIANT varChild);\n        \n        /* [id][hidden] */ HRESULT ( STDMETHODCALLTYPE *accLocation )( \n            IAccessible2 * This,\n            /* [out] */ long *pxLeft,\n            /* [out] */ long *pyTop,\n            /* [out] */ long *pcxWidth,\n            /* [out] */ long *pcyHeight,\n            /* [optional][in] */ VARIANT varChild);\n        \n        /* [id][hidden] */ HRESULT ( STDMETHODCALLTYPE *accNavigate )( \n            IAccessible2 * This,\n            /* [in] */ long navDir,\n            /* [optional][in] */ VARIANT varStart,\n            /* [retval][out] */ VARIANT *pvarEndUpAt);\n        \n        /* [id][hidden] */ HRESULT ( STDMETHODCALLTYPE *accHitTest )( \n            IAccessible2 * This,\n            /* [in] */ long xLeft,\n            /* [in] */ long yTop,\n            /* [retval][out] */ VARIANT *pvarChild);\n        \n        /* [id][hidden] */ HRESULT ( STDMETHODCALLTYPE *accDoDefaultAction )( \n            IAccessible2 * This,\n            /* [optional][in] */ VARIANT varChild);\n        \n        /* [id][propput][hidden] */ HRESULT ( STDMETHODCALLTYPE *put_accName )( \n            IAccessible2 * This,\n            /* [optional][in] */ VARIANT varChild,\n            /* [in] */ BSTR szName);\n        \n        /* [id][propput][hidden] */ HRESULT ( STDMETHODCALLTYPE *put_accValue )( \n            IAccessible2 * This,\n            /* [optional][in] */ VARIANT varChild,\n            /* [in] */ BSTR szValue);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_nRelations )( \n            IAccessible2 * This,\n            /* [retval][out] */ long *nRelations);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_relation )( \n            IAccessible2 * This,\n            /* [in] */ long relationIndex,\n            /* [retval][out] */ IAccessibleRelation **relation);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_relations )( \n            IAccessible2 * This,\n            /* [in] */ long maxRelations,\n            /* [length_is][size_is][out] */ IAccessibleRelation **relations,\n            /* [retval][out] */ long *nRelations);\n        \n        HRESULT ( STDMETHODCALLTYPE *role )( \n            IAccessible2 * This,\n            /* [retval][out] */ long *role);\n        \n        HRESULT ( STDMETHODCALLTYPE *scrollTo )( \n            IAccessible2 * This,\n            /* [in] */ enum IA2ScrollType scrollType);\n        \n        HRESULT ( STDMETHODCALLTYPE *scrollToPoint )( \n            IAccessible2 * This,\n            /* [in] */ enum IA2CoordinateType coordinateType,\n            /* [in] */ long x,\n            /* [in] */ long y);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_groupPosition )( \n            IAccessible2 * This,\n            /* [out] */ long *groupLevel,\n            /* [out] */ long *similarItemsInGroup,\n            /* [retval][out] */ long *positionInGroup);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_states )( \n            IAccessible2 * This,\n            /* [retval][out] */ AccessibleStates *states);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_extendedRole )( \n            IAccessible2 * This,\n            /* [retval][out] */ BSTR *extendedRole);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_localizedExtendedRole )( \n            IAccessible2 * This,\n            /* [retval][out] */ BSTR *localizedExtendedRole);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_nExtendedStates )( \n            IAccessible2 * This,\n            /* [retval][out] */ long *nExtendedStates);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_extendedStates )( \n            IAccessible2 * This,\n            /* [in] */ long maxExtendedStates,\n            /* [length_is][length_is][size_is][size_is][out] */ BSTR **extendedStates,\n            /* [retval][out] */ long *nExtendedStates);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_localizedExtendedStates )( \n            IAccessible2 * This,\n            /* [in] */ long maxLocalizedExtendedStates,\n            /* [length_is][length_is][size_is][size_is][out] */ BSTR **localizedExtendedStates,\n            /* [retval][out] */ long *nLocalizedExtendedStates);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_uniqueID )( \n            IAccessible2 * This,\n            /* [retval][out] */ long *uniqueID);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_windowHandle )( \n            IAccessible2 * This,\n            /* [retval][out] */ HWND *windowHandle);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_indexInParent )( \n            IAccessible2 * This,\n            /* [retval][out] */ long *indexInParent);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_locale )( \n            IAccessible2 * This,\n            /* [retval][out] */ IA2Locale *locale);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_attributes )( \n            IAccessible2 * This,\n            /* [retval][out] */ BSTR *attributes);\n        \n        END_INTERFACE\n    } IAccessible2Vtbl;\n\n    interface IAccessible2\n    {\n        CONST_VTBL struct IAccessible2Vtbl *lpVtbl;\n    };\n\n    \n\n#ifdef COBJMACROS\n\n\n#define IAccessible2_QueryInterface(This,riid,ppvObject)\t\\\n    ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) \n\n#define IAccessible2_AddRef(This)\t\\\n    ( (This)->lpVtbl -> AddRef(This) ) \n\n#define IAccessible2_Release(This)\t\\\n    ( (This)->lpVtbl -> Release(This) ) \n\n\n#define IAccessible2_GetTypeInfoCount(This,pctinfo)\t\\\n    ( (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo) ) \n\n#define IAccessible2_GetTypeInfo(This,iTInfo,lcid,ppTInfo)\t\\\n    ( (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo) ) \n\n#define IAccessible2_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)\t\\\n    ( (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId) ) \n\n#define IAccessible2_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)\t\\\n    ( (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr) ) \n\n\n#define IAccessible2_get_accParent(This,ppdispParent)\t\\\n    ( (This)->lpVtbl -> get_accParent(This,ppdispParent) ) \n\n#define IAccessible2_get_accChildCount(This,pcountChildren)\t\\\n    ( (This)->lpVtbl -> get_accChildCount(This,pcountChildren) ) \n\n#define IAccessible2_get_accChild(This,varChild,ppdispChild)\t\\\n    ( (This)->lpVtbl -> get_accChild(This,varChild,ppdispChild) ) \n\n#define IAccessible2_get_accName(This,varChild,pszName)\t\\\n    ( (This)->lpVtbl -> get_accName(This,varChild,pszName) ) \n\n#define IAccessible2_get_accValue(This,varChild,pszValue)\t\\\n    ( (This)->lpVtbl -> get_accValue(This,varChild,pszValue) ) \n\n#define IAccessible2_get_accDescription(This,varChild,pszDescription)\t\\\n    ( (This)->lpVtbl -> get_accDescription(This,varChild,pszDescription) ) \n\n#define IAccessible2_get_accRole(This,varChild,pvarRole)\t\\\n    ( (This)->lpVtbl -> get_accRole(This,varChild,pvarRole) ) \n\n#define IAccessible2_get_accState(This,varChild,pvarState)\t\\\n    ( (This)->lpVtbl -> get_accState(This,varChild,pvarState) ) \n\n#define IAccessible2_get_accHelp(This,varChild,pszHelp)\t\\\n    ( (This)->lpVtbl -> get_accHelp(This,varChild,pszHelp) ) \n\n#define IAccessible2_get_accHelpTopic(This,pszHelpFile,varChild,pidTopic)\t\\\n    ( (This)->lpVtbl -> get_accHelpTopic(This,pszHelpFile,varChild,pidTopic) ) \n\n#define IAccessible2_get_accKeyboardShortcut(This,varChild,pszKeyboardShortcut)\t\\\n    ( (This)->lpVtbl -> get_accKeyboardShortcut(This,varChild,pszKeyboardShortcut) ) \n\n#define IAccessible2_get_accFocus(This,pvarChild)\t\\\n    ( (This)->lpVtbl -> get_accFocus(This,pvarChild) ) \n\n#define IAccessible2_get_accSelection(This,pvarChildren)\t\\\n    ( (This)->lpVtbl -> get_accSelection(This,pvarChildren) ) \n\n#define IAccessible2_get_accDefaultAction(This,varChild,pszDefaultAction)\t\\\n    ( (This)->lpVtbl -> get_accDefaultAction(This,varChild,pszDefaultAction) ) \n\n#define IAccessible2_accSelect(This,flagsSelect,varChild)\t\\\n    ( (This)->lpVtbl -> accSelect(This,flagsSelect,varChild) ) \n\n#define IAccessible2_accLocation(This,pxLeft,pyTop,pcxWidth,pcyHeight,varChild)\t\\\n    ( (This)->lpVtbl -> accLocation(This,pxLeft,pyTop,pcxWidth,pcyHeight,varChild) ) \n\n#define IAccessible2_accNavigate(This,navDir,varStart,pvarEndUpAt)\t\\\n    ( (This)->lpVtbl -> accNavigate(This,navDir,varStart,pvarEndUpAt) ) \n\n#define IAccessible2_accHitTest(This,xLeft,yTop,pvarChild)\t\\\n    ( (This)->lpVtbl -> accHitTest(This,xLeft,yTop,pvarChild) ) \n\n#define IAccessible2_accDoDefaultAction(This,varChild)\t\\\n    ( (This)->lpVtbl -> accDoDefaultAction(This,varChild) ) \n\n#define IAccessible2_put_accName(This,varChild,szName)\t\\\n    ( (This)->lpVtbl -> put_accName(This,varChild,szName) ) \n\n#define IAccessible2_put_accValue(This,varChild,szValue)\t\\\n    ( (This)->lpVtbl -> put_accValue(This,varChild,szValue) ) \n\n\n#define IAccessible2_get_nRelations(This,nRelations)\t\\\n    ( (This)->lpVtbl -> get_nRelations(This,nRelations) ) \n\n#define IAccessible2_get_relation(This,relationIndex,relation)\t\\\n    ( (This)->lpVtbl -> get_relation(This,relationIndex,relation) ) \n\n#define IAccessible2_get_relations(This,maxRelations,relations,nRelations)\t\\\n    ( (This)->lpVtbl -> get_relations(This,maxRelations,relations,nRelations) ) \n\n#define IAccessible2_role(This,role)\t\\\n    ( (This)->lpVtbl -> role(This,role) ) \n\n#define IAccessible2_scrollTo(This,scrollType)\t\\\n    ( (This)->lpVtbl -> scrollTo(This,scrollType) ) \n\n#define IAccessible2_scrollToPoint(This,coordinateType,x,y)\t\\\n    ( (This)->lpVtbl -> scrollToPoint(This,coordinateType,x,y) ) \n\n#define IAccessible2_get_groupPosition(This,groupLevel,similarItemsInGroup,positionInGroup)\t\\\n    ( (This)->lpVtbl -> get_groupPosition(This,groupLevel,similarItemsInGroup,positionInGroup) ) \n\n#define IAccessible2_get_states(This,states)\t\\\n    ( (This)->lpVtbl -> get_states(This,states) ) \n\n#define IAccessible2_get_extendedRole(This,extendedRole)\t\\\n    ( (This)->lpVtbl -> get_extendedRole(This,extendedRole) ) \n\n#define IAccessible2_get_localizedExtendedRole(This,localizedExtendedRole)\t\\\n    ( (This)->lpVtbl -> get_localizedExtendedRole(This,localizedExtendedRole) ) \n\n#define IAccessible2_get_nExtendedStates(This,nExtendedStates)\t\\\n    ( (This)->lpVtbl -> get_nExtendedStates(This,nExtendedStates) ) \n\n#define IAccessible2_get_extendedStates(This,maxExtendedStates,extendedStates,nExtendedStates)\t\\\n    ( (This)->lpVtbl -> get_extendedStates(This,maxExtendedStates,extendedStates,nExtendedStates) ) \n\n#define IAccessible2_get_localizedExtendedStates(This,maxLocalizedExtendedStates,localizedExtendedStates,nLocalizedExtendedStates)\t\\\n    ( (This)->lpVtbl -> get_localizedExtendedStates(This,maxLocalizedExtendedStates,localizedExtendedStates,nLocalizedExtendedStates) ) \n\n#define IAccessible2_get_uniqueID(This,uniqueID)\t\\\n    ( (This)->lpVtbl -> get_uniqueID(This,uniqueID) ) \n\n#define IAccessible2_get_windowHandle(This,windowHandle)\t\\\n    ( (This)->lpVtbl -> get_windowHandle(This,windowHandle) ) \n\n#define IAccessible2_get_indexInParent(This,indexInParent)\t\\\n    ( (This)->lpVtbl -> get_indexInParent(This,indexInParent) ) \n\n#define IAccessible2_get_locale(This,locale)\t\\\n    ( (This)->lpVtbl -> get_locale(This,locale) ) \n\n#define IAccessible2_get_attributes(This,attributes)\t\\\n    ( (This)->lpVtbl -> get_attributes(This,attributes) ) \n\n#endif /* COBJMACROS */\n\n\n#endif \t/* C style interface */\n\n\n\n\n#endif \t/* __IAccessible2_INTERFACE_DEFINED__ */\n\n\n#ifndef __IAccessible2_2_INTERFACE_DEFINED__\n#define __IAccessible2_2_INTERFACE_DEFINED__\n\n/* interface IAccessible2_2 */\n/* [uuid][object] */ \n\n\nEXTERN_C const IID IID_IAccessible2_2;\n\n#if defined(__cplusplus) && !defined(CINTERFACE)\n    \n    MIDL_INTERFACE(\"6C9430E9-299D-4E6F-BD01-A82A1E88D3FF\")\n    IAccessible2_2 : public IAccessible2\n    {\n    public:\n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_attribute( \n            /* [in] */ BSTR name,\n            /* [retval][out] */ VARIANT *attribute) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_accessibleWithCaret( \n            /* [out] */ IUnknown **accessible,\n            /* [retval][out] */ long *caretOffset) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_relationTargetsOfType( \n            /* [in] */ BSTR type,\n            /* [in] */ long maxTargets,\n            /* [size_is][size_is][out] */ IUnknown ***targets,\n            /* [retval][out] */ long *nTargets) = 0;\n        \n    };\n    \n#else \t/* C style interface */\n\n    typedef struct IAccessible2_2Vtbl\n    {\n        BEGIN_INTERFACE\n        \n        HRESULT ( STDMETHODCALLTYPE *QueryInterface )( \n            IAccessible2_2 * This,\n            /* [in] */ REFIID riid,\n            /* [iid_is][out] */ \n            __RPC__deref_out  void **ppvObject);\n        \n        ULONG ( STDMETHODCALLTYPE *AddRef )( \n            IAccessible2_2 * This);\n        \n        ULONG ( STDMETHODCALLTYPE *Release )( \n            IAccessible2_2 * This);\n        \n        HRESULT ( STDMETHODCALLTYPE *GetTypeInfoCount )( \n            IAccessible2_2 * This,\n            /* [out] */ UINT *pctinfo);\n        \n        HRESULT ( STDMETHODCALLTYPE *GetTypeInfo )( \n            IAccessible2_2 * This,\n            /* [in] */ UINT iTInfo,\n            /* [in] */ LCID lcid,\n            /* [out] */ ITypeInfo **ppTInfo);\n        \n        HRESULT ( STDMETHODCALLTYPE *GetIDsOfNames )( \n            IAccessible2_2 * This,\n            /* [in] */ REFIID riid,\n            /* [size_is][in] */ LPOLESTR *rgszNames,\n            /* [range][in] */ UINT cNames,\n            /* [in] */ LCID lcid,\n            /* [size_is][out] */ DISPID *rgDispId);\n        \n        /* [local] */ HRESULT ( STDMETHODCALLTYPE *Invoke )( \n            IAccessible2_2 * This,\n            /* [in] */ DISPID dispIdMember,\n            /* [in] */ REFIID riid,\n            /* [in] */ LCID lcid,\n            /* [in] */ WORD wFlags,\n            /* [out][in] */ DISPPARAMS *pDispParams,\n            /* [out] */ VARIANT *pVarResult,\n            /* [out] */ EXCEPINFO *pExcepInfo,\n            /* [out] */ UINT *puArgErr);\n        \n        /* [id][propget][hidden] */ HRESULT ( STDMETHODCALLTYPE *get_accParent )( \n            IAccessible2_2 * This,\n            /* [retval][out] */ IDispatch **ppdispParent);\n        \n        /* [id][propget][hidden] */ HRESULT ( STDMETHODCALLTYPE *get_accChildCount )( \n            IAccessible2_2 * This,\n            /* [retval][out] */ long *pcountChildren);\n        \n        /* [id][propget][hidden] */ HRESULT ( STDMETHODCALLTYPE *get_accChild )( \n            IAccessible2_2 * This,\n            /* [in] */ VARIANT varChild,\n            /* [retval][out] */ IDispatch **ppdispChild);\n        \n        /* [id][propget][hidden] */ HRESULT ( STDMETHODCALLTYPE *get_accName )( \n            IAccessible2_2 * This,\n            /* [optional][in] */ VARIANT varChild,\n            /* [retval][out] */ BSTR *pszName);\n        \n        /* [id][propget][hidden] */ HRESULT ( STDMETHODCALLTYPE *get_accValue )( \n            IAccessible2_2 * This,\n            /* [optional][in] */ VARIANT varChild,\n            /* [retval][out] */ BSTR *pszValue);\n        \n        /* [id][propget][hidden] */ HRESULT ( STDMETHODCALLTYPE *get_accDescription )( \n            IAccessible2_2 * This,\n            /* [optional][in] */ VARIANT varChild,\n            /* [retval][out] */ BSTR *pszDescription);\n        \n        /* [id][propget][hidden] */ HRESULT ( STDMETHODCALLTYPE *get_accRole )( \n            IAccessible2_2 * This,\n            /* [optional][in] */ VARIANT varChild,\n            /* [retval][out] */ VARIANT *pvarRole);\n        \n        /* [id][propget][hidden] */ HRESULT ( STDMETHODCALLTYPE *get_accState )( \n            IAccessible2_2 * This,\n            /* [optional][in] */ VARIANT varChild,\n            /* [retval][out] */ VARIANT *pvarState);\n        \n        /* [id][propget][hidden] */ HRESULT ( STDMETHODCALLTYPE *get_accHelp )( \n            IAccessible2_2 * This,\n            /* [optional][in] */ VARIANT varChild,\n            /* [retval][out] */ BSTR *pszHelp);\n        \n        /* [id][propget][hidden] */ HRESULT ( STDMETHODCALLTYPE *get_accHelpTopic )( \n            IAccessible2_2 * This,\n            /* [out] */ BSTR *pszHelpFile,\n            /* [optional][in] */ VARIANT varChild,\n            /* [retval][out] */ long *pidTopic);\n        \n        /* [id][propget][hidden] */ HRESULT ( STDMETHODCALLTYPE *get_accKeyboardShortcut )( \n            IAccessible2_2 * This,\n            /* [optional][in] */ VARIANT varChild,\n            /* [retval][out] */ BSTR *pszKeyboardShortcut);\n        \n        /* [id][propget][hidden] */ HRESULT ( STDMETHODCALLTYPE *get_accFocus )( \n            IAccessible2_2 * This,\n            /* [retval][out] */ VARIANT *pvarChild);\n        \n        /* [id][propget][hidden] */ HRESULT ( STDMETHODCALLTYPE *get_accSelection )( \n            IAccessible2_2 * This,\n            /* [retval][out] */ VARIANT *pvarChildren);\n        \n        /* [id][propget][hidden] */ HRESULT ( STDMETHODCALLTYPE *get_accDefaultAction )( \n            IAccessible2_2 * This,\n            /* [optional][in] */ VARIANT varChild,\n            /* [retval][out] */ BSTR *pszDefaultAction);\n        \n        /* [id][hidden] */ HRESULT ( STDMETHODCALLTYPE *accSelect )( \n            IAccessible2_2 * This,\n            /* [in] */ long flagsSelect,\n            /* [optional][in] */ VARIANT varChild);\n        \n        /* [id][hidden] */ HRESULT ( STDMETHODCALLTYPE *accLocation )( \n            IAccessible2_2 * This,\n            /* [out] */ long *pxLeft,\n            /* [out] */ long *pyTop,\n            /* [out] */ long *pcxWidth,\n            /* [out] */ long *pcyHeight,\n            /* [optional][in] */ VARIANT varChild);\n        \n        /* [id][hidden] */ HRESULT ( STDMETHODCALLTYPE *accNavigate )( \n            IAccessible2_2 * This,\n            /* [in] */ long navDir,\n            /* [optional][in] */ VARIANT varStart,\n            /* [retval][out] */ VARIANT *pvarEndUpAt);\n        \n        /* [id][hidden] */ HRESULT ( STDMETHODCALLTYPE *accHitTest )( \n            IAccessible2_2 * This,\n            /* [in] */ long xLeft,\n            /* [in] */ long yTop,\n            /* [retval][out] */ VARIANT *pvarChild);\n        \n        /* [id][hidden] */ HRESULT ( STDMETHODCALLTYPE *accDoDefaultAction )( \n            IAccessible2_2 * This,\n            /* [optional][in] */ VARIANT varChild);\n        \n        /* [id][propput][hidden] */ HRESULT ( STDMETHODCALLTYPE *put_accName )( \n            IAccessible2_2 * This,\n            /* [optional][in] */ VARIANT varChild,\n            /* [in] */ BSTR szName);\n        \n        /* [id][propput][hidden] */ HRESULT ( STDMETHODCALLTYPE *put_accValue )( \n            IAccessible2_2 * This,\n            /* [optional][in] */ VARIANT varChild,\n            /* [in] */ BSTR szValue);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_nRelations )( \n            IAccessible2_2 * This,\n            /* [retval][out] */ long *nRelations);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_relation )( \n            IAccessible2_2 * This,\n            /* [in] */ long relationIndex,\n            /* [retval][out] */ IAccessibleRelation **relation);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_relations )( \n            IAccessible2_2 * This,\n            /* [in] */ long maxRelations,\n            /* [length_is][size_is][out] */ IAccessibleRelation **relations,\n            /* [retval][out] */ long *nRelations);\n        \n        HRESULT ( STDMETHODCALLTYPE *role )( \n            IAccessible2_2 * This,\n            /* [retval][out] */ long *role);\n        \n        HRESULT ( STDMETHODCALLTYPE *scrollTo )( \n            IAccessible2_2 * This,\n            /* [in] */ enum IA2ScrollType scrollType);\n        \n        HRESULT ( STDMETHODCALLTYPE *scrollToPoint )( \n            IAccessible2_2 * This,\n            /* [in] */ enum IA2CoordinateType coordinateType,\n            /* [in] */ long x,\n            /* [in] */ long y);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_groupPosition )( \n            IAccessible2_2 * This,\n            /* [out] */ long *groupLevel,\n            /* [out] */ long *similarItemsInGroup,\n            /* [retval][out] */ long *positionInGroup);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_states )( \n            IAccessible2_2 * This,\n            /* [retval][out] */ AccessibleStates *states);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_extendedRole )( \n            IAccessible2_2 * This,\n            /* [retval][out] */ BSTR *extendedRole);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_localizedExtendedRole )( \n            IAccessible2_2 * This,\n            /* [retval][out] */ BSTR *localizedExtendedRole);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_nExtendedStates )( \n            IAccessible2_2 * This,\n            /* [retval][out] */ long *nExtendedStates);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_extendedStates )( \n            IAccessible2_2 * This,\n            /* [in] */ long maxExtendedStates,\n            /* [length_is][length_is][size_is][size_is][out] */ BSTR **extendedStates,\n            /* [retval][out] */ long *nExtendedStates);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_localizedExtendedStates )( \n            IAccessible2_2 * This,\n            /* [in] */ long maxLocalizedExtendedStates,\n            /* [length_is][length_is][size_is][size_is][out] */ BSTR **localizedExtendedStates,\n            /* [retval][out] */ long *nLocalizedExtendedStates);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_uniqueID )( \n            IAccessible2_2 * This,\n            /* [retval][out] */ long *uniqueID);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_windowHandle )( \n            IAccessible2_2 * This,\n            /* [retval][out] */ HWND *windowHandle);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_indexInParent )( \n            IAccessible2_2 * This,\n            /* [retval][out] */ long *indexInParent);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_locale )( \n            IAccessible2_2 * This,\n            /* [retval][out] */ IA2Locale *locale);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_attributes )( \n            IAccessible2_2 * This,\n            /* [retval][out] */ BSTR *attributes);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_attribute )( \n            IAccessible2_2 * This,\n            /* [in] */ BSTR name,\n            /* [retval][out] */ VARIANT *attribute);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_accessibleWithCaret )( \n            IAccessible2_2 * This,\n            /* [out] */ IUnknown **accessible,\n            /* [retval][out] */ long *caretOffset);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_relationTargetsOfType )( \n            IAccessible2_2 * This,\n            /* [in] */ BSTR type,\n            /* [in] */ long maxTargets,\n            /* [size_is][size_is][out] */ IUnknown ***targets,\n            /* [retval][out] */ long *nTargets);\n        \n        END_INTERFACE\n    } IAccessible2_2Vtbl;\n\n    interface IAccessible2_2\n    {\n        CONST_VTBL struct IAccessible2_2Vtbl *lpVtbl;\n    };\n\n    \n\n#ifdef COBJMACROS\n\n\n#define IAccessible2_2_QueryInterface(This,riid,ppvObject)\t\\\n    ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) \n\n#define IAccessible2_2_AddRef(This)\t\\\n    ( (This)->lpVtbl -> AddRef(This) ) \n\n#define IAccessible2_2_Release(This)\t\\\n    ( (This)->lpVtbl -> Release(This) ) \n\n\n#define IAccessible2_2_GetTypeInfoCount(This,pctinfo)\t\\\n    ( (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo) ) \n\n#define IAccessible2_2_GetTypeInfo(This,iTInfo,lcid,ppTInfo)\t\\\n    ( (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo) ) \n\n#define IAccessible2_2_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)\t\\\n    ( (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId) ) \n\n#define IAccessible2_2_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)\t\\\n    ( (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr) ) \n\n\n#define IAccessible2_2_get_accParent(This,ppdispParent)\t\\\n    ( (This)->lpVtbl -> get_accParent(This,ppdispParent) ) \n\n#define IAccessible2_2_get_accChildCount(This,pcountChildren)\t\\\n    ( (This)->lpVtbl -> get_accChildCount(This,pcountChildren) ) \n\n#define IAccessible2_2_get_accChild(This,varChild,ppdispChild)\t\\\n    ( (This)->lpVtbl -> get_accChild(This,varChild,ppdispChild) ) \n\n#define IAccessible2_2_get_accName(This,varChild,pszName)\t\\\n    ( (This)->lpVtbl -> get_accName(This,varChild,pszName) ) \n\n#define IAccessible2_2_get_accValue(This,varChild,pszValue)\t\\\n    ( (This)->lpVtbl -> get_accValue(This,varChild,pszValue) ) \n\n#define IAccessible2_2_get_accDescription(This,varChild,pszDescription)\t\\\n    ( (This)->lpVtbl -> get_accDescription(This,varChild,pszDescription) ) \n\n#define IAccessible2_2_get_accRole(This,varChild,pvarRole)\t\\\n    ( (This)->lpVtbl -> get_accRole(This,varChild,pvarRole) ) \n\n#define IAccessible2_2_get_accState(This,varChild,pvarState)\t\\\n    ( (This)->lpVtbl -> get_accState(This,varChild,pvarState) ) \n\n#define IAccessible2_2_get_accHelp(This,varChild,pszHelp)\t\\\n    ( (This)->lpVtbl -> get_accHelp(This,varChild,pszHelp) ) \n\n#define IAccessible2_2_get_accHelpTopic(This,pszHelpFile,varChild,pidTopic)\t\\\n    ( (This)->lpVtbl -> get_accHelpTopic(This,pszHelpFile,varChild,pidTopic) ) \n\n#define IAccessible2_2_get_accKeyboardShortcut(This,varChild,pszKeyboardShortcut)\t\\\n    ( (This)->lpVtbl -> get_accKeyboardShortcut(This,varChild,pszKeyboardShortcut) ) \n\n#define IAccessible2_2_get_accFocus(This,pvarChild)\t\\\n    ( (This)->lpVtbl -> get_accFocus(This,pvarChild) ) \n\n#define IAccessible2_2_get_accSelection(This,pvarChildren)\t\\\n    ( (This)->lpVtbl -> get_accSelection(This,pvarChildren) ) \n\n#define IAccessible2_2_get_accDefaultAction(This,varChild,pszDefaultAction)\t\\\n    ( (This)->lpVtbl -> get_accDefaultAction(This,varChild,pszDefaultAction) ) \n\n#define IAccessible2_2_accSelect(This,flagsSelect,varChild)\t\\\n    ( (This)->lpVtbl -> accSelect(This,flagsSelect,varChild) ) \n\n#define IAccessible2_2_accLocation(This,pxLeft,pyTop,pcxWidth,pcyHeight,varChild)\t\\\n    ( (This)->lpVtbl -> accLocation(This,pxLeft,pyTop,pcxWidth,pcyHeight,varChild) ) \n\n#define IAccessible2_2_accNavigate(This,navDir,varStart,pvarEndUpAt)\t\\\n    ( (This)->lpVtbl -> accNavigate(This,navDir,varStart,pvarEndUpAt) ) \n\n#define IAccessible2_2_accHitTest(This,xLeft,yTop,pvarChild)\t\\\n    ( (This)->lpVtbl -> accHitTest(This,xLeft,yTop,pvarChild) ) \n\n#define IAccessible2_2_accDoDefaultAction(This,varChild)\t\\\n    ( (This)->lpVtbl -> accDoDefaultAction(This,varChild) ) \n\n#define IAccessible2_2_put_accName(This,varChild,szName)\t\\\n    ( (This)->lpVtbl -> put_accName(This,varChild,szName) ) \n\n#define IAccessible2_2_put_accValue(This,varChild,szValue)\t\\\n    ( (This)->lpVtbl -> put_accValue(This,varChild,szValue) ) \n\n\n#define IAccessible2_2_get_nRelations(This,nRelations)\t\\\n    ( (This)->lpVtbl -> get_nRelations(This,nRelations) ) \n\n#define IAccessible2_2_get_relation(This,relationIndex,relation)\t\\\n    ( (This)->lpVtbl -> get_relation(This,relationIndex,relation) ) \n\n#define IAccessible2_2_get_relations(This,maxRelations,relations,nRelations)\t\\\n    ( (This)->lpVtbl -> get_relations(This,maxRelations,relations,nRelations) ) \n\n#define IAccessible2_2_role(This,role)\t\\\n    ( (This)->lpVtbl -> role(This,role) ) \n\n#define IAccessible2_2_scrollTo(This,scrollType)\t\\\n    ( (This)->lpVtbl -> scrollTo(This,scrollType) ) \n\n#define IAccessible2_2_scrollToPoint(This,coordinateType,x,y)\t\\\n    ( (This)->lpVtbl -> scrollToPoint(This,coordinateType,x,y) ) \n\n#define IAccessible2_2_get_groupPosition(This,groupLevel,similarItemsInGroup,positionInGroup)\t\\\n    ( (This)->lpVtbl -> get_groupPosition(This,groupLevel,similarItemsInGroup,positionInGroup) ) \n\n#define IAccessible2_2_get_states(This,states)\t\\\n    ( (This)->lpVtbl -> get_states(This,states) ) \n\n#define IAccessible2_2_get_extendedRole(This,extendedRole)\t\\\n    ( (This)->lpVtbl -> get_extendedRole(This,extendedRole) ) \n\n#define IAccessible2_2_get_localizedExtendedRole(This,localizedExtendedRole)\t\\\n    ( (This)->lpVtbl -> get_localizedExtendedRole(This,localizedExtendedRole) ) \n\n#define IAccessible2_2_get_nExtendedStates(This,nExtendedStates)\t\\\n    ( (This)->lpVtbl -> get_nExtendedStates(This,nExtendedStates) ) \n\n#define IAccessible2_2_get_extendedStates(This,maxExtendedStates,extendedStates,nExtendedStates)\t\\\n    ( (This)->lpVtbl -> get_extendedStates(This,maxExtendedStates,extendedStates,nExtendedStates) ) \n\n#define IAccessible2_2_get_localizedExtendedStates(This,maxLocalizedExtendedStates,localizedExtendedStates,nLocalizedExtendedStates)\t\\\n    ( (This)->lpVtbl -> get_localizedExtendedStates(This,maxLocalizedExtendedStates,localizedExtendedStates,nLocalizedExtendedStates) ) \n\n#define IAccessible2_2_get_uniqueID(This,uniqueID)\t\\\n    ( (This)->lpVtbl -> get_uniqueID(This,uniqueID) ) \n\n#define IAccessible2_2_get_windowHandle(This,windowHandle)\t\\\n    ( (This)->lpVtbl -> get_windowHandle(This,windowHandle) ) \n\n#define IAccessible2_2_get_indexInParent(This,indexInParent)\t\\\n    ( (This)->lpVtbl -> get_indexInParent(This,indexInParent) ) \n\n#define IAccessible2_2_get_locale(This,locale)\t\\\n    ( (This)->lpVtbl -> get_locale(This,locale) ) \n\n#define IAccessible2_2_get_attributes(This,attributes)\t\\\n    ( (This)->lpVtbl -> get_attributes(This,attributes) ) \n\n\n#define IAccessible2_2_get_attribute(This,name,attribute)\t\\\n    ( (This)->lpVtbl -> get_attribute(This,name,attribute) ) \n\n#define IAccessible2_2_get_accessibleWithCaret(This,accessible,caretOffset)\t\\\n    ( (This)->lpVtbl -> get_accessibleWithCaret(This,accessible,caretOffset) ) \n\n#define IAccessible2_2_get_relationTargetsOfType(This,type,maxTargets,targets,nTargets)\t\\\n    ( (This)->lpVtbl -> get_relationTargetsOfType(This,type,maxTargets,targets,nTargets) ) \n\n#endif /* COBJMACROS */\n\n\n#endif \t/* C style interface */\n\n\n\n\n#endif \t/* __IAccessible2_2_INTERFACE_DEFINED__ */\n\n\n/* interface __MIDL_itf_IAccessible2_0000_0004 */\n/* [local] */ \n\ntypedef long IA2Color;\n\n\n\nextern RPC_IF_HANDLE __MIDL_itf_IAccessible2_0000_0004_v0_0_c_ifspec;\nextern RPC_IF_HANDLE __MIDL_itf_IAccessible2_0000_0004_v0_0_s_ifspec;\n\n#ifndef __IAccessibleComponent_INTERFACE_DEFINED__\n#define __IAccessibleComponent_INTERFACE_DEFINED__\n\n/* interface IAccessibleComponent */\n/* [uuid][object] */ \n\n\nEXTERN_C const IID IID_IAccessibleComponent;\n\n#if defined(__cplusplus) && !defined(CINTERFACE)\n    \n    MIDL_INTERFACE(\"1546D4B0-4C98-4bda-89AE-9A64748BDDE4\")\n    IAccessibleComponent : public IUnknown\n    {\n    public:\n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_locationInParent( \n            /* [out] */ long *x,\n            /* [retval][out] */ long *y) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_foreground( \n            /* [retval][out] */ IA2Color *foreground) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_background( \n            /* [retval][out] */ IA2Color *background) = 0;\n        \n    };\n    \n#else \t/* C style interface */\n\n    typedef struct IAccessibleComponentVtbl\n    {\n        BEGIN_INTERFACE\n        \n        HRESULT ( STDMETHODCALLTYPE *QueryInterface )( \n            IAccessibleComponent * This,\n            /* [in] */ REFIID riid,\n            /* [iid_is][out] */ \n            __RPC__deref_out  void **ppvObject);\n        \n        ULONG ( STDMETHODCALLTYPE *AddRef )( \n            IAccessibleComponent * This);\n        \n        ULONG ( STDMETHODCALLTYPE *Release )( \n            IAccessibleComponent * This);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_locationInParent )( \n            IAccessibleComponent * This,\n            /* [out] */ long *x,\n            /* [retval][out] */ long *y);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_foreground )( \n            IAccessibleComponent * This,\n            /* [retval][out] */ IA2Color *foreground);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_background )( \n            IAccessibleComponent * This,\n            /* [retval][out] */ IA2Color *background);\n        \n        END_INTERFACE\n    } IAccessibleComponentVtbl;\n\n    interface IAccessibleComponent\n    {\n        CONST_VTBL struct IAccessibleComponentVtbl *lpVtbl;\n    };\n\n    \n\n#ifdef COBJMACROS\n\n\n#define IAccessibleComponent_QueryInterface(This,riid,ppvObject)\t\\\n    ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) \n\n#define IAccessibleComponent_AddRef(This)\t\\\n    ( (This)->lpVtbl -> AddRef(This) ) \n\n#define IAccessibleComponent_Release(This)\t\\\n    ( (This)->lpVtbl -> Release(This) ) \n\n\n#define IAccessibleComponent_get_locationInParent(This,x,y)\t\\\n    ( (This)->lpVtbl -> get_locationInParent(This,x,y) ) \n\n#define IAccessibleComponent_get_foreground(This,foreground)\t\\\n    ( (This)->lpVtbl -> get_foreground(This,foreground) ) \n\n#define IAccessibleComponent_get_background(This,background)\t\\\n    ( (This)->lpVtbl -> get_background(This,background) ) \n\n#endif /* COBJMACROS */\n\n\n#endif \t/* C style interface */\n\n\n\n\n#endif \t/* __IAccessibleComponent_INTERFACE_DEFINED__ */\n\n\n#ifndef __IAccessibleValue_INTERFACE_DEFINED__\n#define __IAccessibleValue_INTERFACE_DEFINED__\n\n/* interface IAccessibleValue */\n/* [uuid][object] */ \n\n\nEXTERN_C const IID IID_IAccessibleValue;\n\n#if defined(__cplusplus) && !defined(CINTERFACE)\n    \n    MIDL_INTERFACE(\"35855B5B-C566-4fd0-A7B1-E65465600394\")\n    IAccessibleValue : public IUnknown\n    {\n    public:\n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_currentValue( \n            /* [retval][out] */ VARIANT *currentValue) = 0;\n        \n        virtual HRESULT STDMETHODCALLTYPE setCurrentValue( \n            /* [in] */ VARIANT value) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_maximumValue( \n            /* [retval][out] */ VARIANT *maximumValue) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_minimumValue( \n            /* [retval][out] */ VARIANT *minimumValue) = 0;\n        \n    };\n    \n#else \t/* C style interface */\n\n    typedef struct IAccessibleValueVtbl\n    {\n        BEGIN_INTERFACE\n        \n        HRESULT ( STDMETHODCALLTYPE *QueryInterface )( \n            IAccessibleValue * This,\n            /* [in] */ REFIID riid,\n            /* [iid_is][out] */ \n            __RPC__deref_out  void **ppvObject);\n        \n        ULONG ( STDMETHODCALLTYPE *AddRef )( \n            IAccessibleValue * This);\n        \n        ULONG ( STDMETHODCALLTYPE *Release )( \n            IAccessibleValue * This);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_currentValue )( \n            IAccessibleValue * This,\n            /* [retval][out] */ VARIANT *currentValue);\n        \n        HRESULT ( STDMETHODCALLTYPE *setCurrentValue )( \n            IAccessibleValue * This,\n            /* [in] */ VARIANT value);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_maximumValue )( \n            IAccessibleValue * This,\n            /* [retval][out] */ VARIANT *maximumValue);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_minimumValue )( \n            IAccessibleValue * This,\n            /* [retval][out] */ VARIANT *minimumValue);\n        \n        END_INTERFACE\n    } IAccessibleValueVtbl;\n\n    interface IAccessibleValue\n    {\n        CONST_VTBL struct IAccessibleValueVtbl *lpVtbl;\n    };\n\n    \n\n#ifdef COBJMACROS\n\n\n#define IAccessibleValue_QueryInterface(This,riid,ppvObject)\t\\\n    ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) \n\n#define IAccessibleValue_AddRef(This)\t\\\n    ( (This)->lpVtbl -> AddRef(This) ) \n\n#define IAccessibleValue_Release(This)\t\\\n    ( (This)->lpVtbl -> Release(This) ) \n\n\n#define IAccessibleValue_get_currentValue(This,currentValue)\t\\\n    ( (This)->lpVtbl -> get_currentValue(This,currentValue) ) \n\n#define IAccessibleValue_setCurrentValue(This,value)\t\\\n    ( (This)->lpVtbl -> setCurrentValue(This,value) ) \n\n#define IAccessibleValue_get_maximumValue(This,maximumValue)\t\\\n    ( (This)->lpVtbl -> get_maximumValue(This,maximumValue) ) \n\n#define IAccessibleValue_get_minimumValue(This,minimumValue)\t\\\n    ( (This)->lpVtbl -> get_minimumValue(This,minimumValue) ) \n\n#endif /* COBJMACROS */\n\n\n#endif \t/* C style interface */\n\n\n\n\n#endif \t/* __IAccessibleValue_INTERFACE_DEFINED__ */\n\n\n/* interface __MIDL_itf_IAccessible2_0000_0006 */\n/* [local] */ \n\ntypedef struct IA2TextSegment\n    {\n    BSTR text;\n    long start;\n    long end;\n    } \tIA2TextSegment;\n\n\nenum IA2TextBoundaryType\n    {\tIA2_TEXT_BOUNDARY_CHAR\t= 0,\n\tIA2_TEXT_BOUNDARY_WORD\t= ( IA2_TEXT_BOUNDARY_CHAR + 1 ) ,\n\tIA2_TEXT_BOUNDARY_SENTENCE\t= ( IA2_TEXT_BOUNDARY_WORD + 1 ) ,\n\tIA2_TEXT_BOUNDARY_PARAGRAPH\t= ( IA2_TEXT_BOUNDARY_SENTENCE + 1 ) ,\n\tIA2_TEXT_BOUNDARY_LINE\t= ( IA2_TEXT_BOUNDARY_PARAGRAPH + 1 ) ,\n\tIA2_TEXT_BOUNDARY_ALL\t= ( IA2_TEXT_BOUNDARY_LINE + 1 ) \n    } ;\n\n\nextern RPC_IF_HANDLE __MIDL_itf_IAccessible2_0000_0006_v0_0_c_ifspec;\nextern RPC_IF_HANDLE __MIDL_itf_IAccessible2_0000_0006_v0_0_s_ifspec;\n\n#ifndef __IAccessibleText_INTERFACE_DEFINED__\n#define __IAccessibleText_INTERFACE_DEFINED__\n\n/* interface IAccessibleText */\n/* [uuid][object] */ \n\n\nEXTERN_C const IID IID_IAccessibleText;\n\n#if defined(__cplusplus) && !defined(CINTERFACE)\n    \n    MIDL_INTERFACE(\"24FD2FFB-3AAD-4a08-8335-A3AD89C0FB4B\")\n    IAccessibleText : public IUnknown\n    {\n    public:\n        virtual HRESULT STDMETHODCALLTYPE addSelection( \n            /* [in] */ long startOffset,\n            /* [in] */ long endOffset) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_attributes( \n            /* [in] */ long offset,\n            /* [out] */ long *startOffset,\n            /* [out] */ long *endOffset,\n            /* [retval][out] */ BSTR *textAttributes) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_caretOffset( \n            /* [retval][out] */ long *offset) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_characterExtents( \n            /* [in] */ long offset,\n            /* [in] */ enum IA2CoordinateType coordType,\n            /* [out] */ long *x,\n            /* [out] */ long *y,\n            /* [out] */ long *width,\n            /* [retval][out] */ long *height) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_nSelections( \n            /* [retval][out] */ long *nSelections) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_offsetAtPoint( \n            /* [in] */ long x,\n            /* [in] */ long y,\n            /* [in] */ enum IA2CoordinateType coordType,\n            /* [retval][out] */ long *offset) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_selection( \n            /* [in] */ long selectionIndex,\n            /* [out] */ long *startOffset,\n            /* [retval][out] */ long *endOffset) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_text( \n            /* [in] */ long startOffset,\n            /* [in] */ long endOffset,\n            /* [retval][out] */ BSTR *text) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_textBeforeOffset( \n            /* [in] */ long offset,\n            /* [in] */ enum IA2TextBoundaryType boundaryType,\n            /* [out] */ long *startOffset,\n            /* [out] */ long *endOffset,\n            /* [retval][out] */ BSTR *text) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_textAfterOffset( \n            /* [in] */ long offset,\n            /* [in] */ enum IA2TextBoundaryType boundaryType,\n            /* [out] */ long *startOffset,\n            /* [out] */ long *endOffset,\n            /* [retval][out] */ BSTR *text) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_textAtOffset( \n            /* [in] */ long offset,\n            /* [in] */ enum IA2TextBoundaryType boundaryType,\n            /* [out] */ long *startOffset,\n            /* [out] */ long *endOffset,\n            /* [retval][out] */ BSTR *text) = 0;\n        \n        virtual HRESULT STDMETHODCALLTYPE removeSelection( \n            /* [in] */ long selectionIndex) = 0;\n        \n        virtual HRESULT STDMETHODCALLTYPE setCaretOffset( \n            /* [in] */ long offset) = 0;\n        \n        virtual HRESULT STDMETHODCALLTYPE setSelection( \n            /* [in] */ long selectionIndex,\n            /* [in] */ long startOffset,\n            /* [in] */ long endOffset) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_nCharacters( \n            /* [retval][out] */ long *nCharacters) = 0;\n        \n        virtual HRESULT STDMETHODCALLTYPE scrollSubstringTo( \n            /* [in] */ long startIndex,\n            /* [in] */ long endIndex,\n            /* [in] */ enum IA2ScrollType scrollType) = 0;\n        \n        virtual HRESULT STDMETHODCALLTYPE scrollSubstringToPoint( \n            /* [in] */ long startIndex,\n            /* [in] */ long endIndex,\n            /* [in] */ enum IA2CoordinateType coordinateType,\n            /* [in] */ long x,\n            /* [in] */ long y) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_newText( \n            /* [retval][out] */ IA2TextSegment *newText) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_oldText( \n            /* [retval][out] */ IA2TextSegment *oldText) = 0;\n        \n    };\n    \n#else \t/* C style interface */\n\n    typedef struct IAccessibleTextVtbl\n    {\n        BEGIN_INTERFACE\n        \n        HRESULT ( STDMETHODCALLTYPE *QueryInterface )( \n            IAccessibleText * This,\n            /* [in] */ REFIID riid,\n            /* [iid_is][out] */ \n            __RPC__deref_out  void **ppvObject);\n        \n        ULONG ( STDMETHODCALLTYPE *AddRef )( \n            IAccessibleText * This);\n        \n        ULONG ( STDMETHODCALLTYPE *Release )( \n            IAccessibleText * This);\n        \n        HRESULT ( STDMETHODCALLTYPE *addSelection )( \n            IAccessibleText * This,\n            /* [in] */ long startOffset,\n            /* [in] */ long endOffset);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_attributes )( \n            IAccessibleText * This,\n            /* [in] */ long offset,\n            /* [out] */ long *startOffset,\n            /* [out] */ long *endOffset,\n            /* [retval][out] */ BSTR *textAttributes);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_caretOffset )( \n            IAccessibleText * This,\n            /* [retval][out] */ long *offset);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_characterExtents )( \n            IAccessibleText * This,\n            /* [in] */ long offset,\n            /* [in] */ enum IA2CoordinateType coordType,\n            /* [out] */ long *x,\n            /* [out] */ long *y,\n            /* [out] */ long *width,\n            /* [retval][out] */ long *height);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_nSelections )( \n            IAccessibleText * This,\n            /* [retval][out] */ long *nSelections);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_offsetAtPoint )( \n            IAccessibleText * This,\n            /* [in] */ long x,\n            /* [in] */ long y,\n            /* [in] */ enum IA2CoordinateType coordType,\n            /* [retval][out] */ long *offset);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_selection )( \n            IAccessibleText * This,\n            /* [in] */ long selectionIndex,\n            /* [out] */ long *startOffset,\n            /* [retval][out] */ long *endOffset);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_text )( \n            IAccessibleText * This,\n            /* [in] */ long startOffset,\n            /* [in] */ long endOffset,\n            /* [retval][out] */ BSTR *text);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_textBeforeOffset )( \n            IAccessibleText * This,\n            /* [in] */ long offset,\n            /* [in] */ enum IA2TextBoundaryType boundaryType,\n            /* [out] */ long *startOffset,\n            /* [out] */ long *endOffset,\n            /* [retval][out] */ BSTR *text);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_textAfterOffset )( \n            IAccessibleText * This,\n            /* [in] */ long offset,\n            /* [in] */ enum IA2TextBoundaryType boundaryType,\n            /* [out] */ long *startOffset,\n            /* [out] */ long *endOffset,\n            /* [retval][out] */ BSTR *text);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_textAtOffset )( \n            IAccessibleText * This,\n            /* [in] */ long offset,\n            /* [in] */ enum IA2TextBoundaryType boundaryType,\n            /* [out] */ long *startOffset,\n            /* [out] */ long *endOffset,\n            /* [retval][out] */ BSTR *text);\n        \n        HRESULT ( STDMETHODCALLTYPE *removeSelection )( \n            IAccessibleText * This,\n            /* [in] */ long selectionIndex);\n        \n        HRESULT ( STDMETHODCALLTYPE *setCaretOffset )( \n            IAccessibleText * This,\n            /* [in] */ long offset);\n        \n        HRESULT ( STDMETHODCALLTYPE *setSelection )( \n            IAccessibleText * This,\n            /* [in] */ long selectionIndex,\n            /* [in] */ long startOffset,\n            /* [in] */ long endOffset);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_nCharacters )( \n            IAccessibleText * This,\n            /* [retval][out] */ long *nCharacters);\n        \n        HRESULT ( STDMETHODCALLTYPE *scrollSubstringTo )( \n            IAccessibleText * This,\n            /* [in] */ long startIndex,\n            /* [in] */ long endIndex,\n            /* [in] */ enum IA2ScrollType scrollType);\n        \n        HRESULT ( STDMETHODCALLTYPE *scrollSubstringToPoint )( \n            IAccessibleText * This,\n            /* [in] */ long startIndex,\n            /* [in] */ long endIndex,\n            /* [in] */ enum IA2CoordinateType coordinateType,\n            /* [in] */ long x,\n            /* [in] */ long y);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_newText )( \n            IAccessibleText * This,\n            /* [retval][out] */ IA2TextSegment *newText);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_oldText )( \n            IAccessibleText * This,\n            /* [retval][out] */ IA2TextSegment *oldText);\n        \n        END_INTERFACE\n    } IAccessibleTextVtbl;\n\n    interface IAccessibleText\n    {\n        CONST_VTBL struct IAccessibleTextVtbl *lpVtbl;\n    };\n\n    \n\n#ifdef COBJMACROS\n\n\n#define IAccessibleText_QueryInterface(This,riid,ppvObject)\t\\\n    ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) \n\n#define IAccessibleText_AddRef(This)\t\\\n    ( (This)->lpVtbl -> AddRef(This) ) \n\n#define IAccessibleText_Release(This)\t\\\n    ( (This)->lpVtbl -> Release(This) ) \n\n\n#define IAccessibleText_addSelection(This,startOffset,endOffset)\t\\\n    ( (This)->lpVtbl -> addSelection(This,startOffset,endOffset) ) \n\n#define IAccessibleText_get_attributes(This,offset,startOffset,endOffset,textAttributes)\t\\\n    ( (This)->lpVtbl -> get_attributes(This,offset,startOffset,endOffset,textAttributes) ) \n\n#define IAccessibleText_get_caretOffset(This,offset)\t\\\n    ( (This)->lpVtbl -> get_caretOffset(This,offset) ) \n\n#define IAccessibleText_get_characterExtents(This,offset,coordType,x,y,width,height)\t\\\n    ( (This)->lpVtbl -> get_characterExtents(This,offset,coordType,x,y,width,height) ) \n\n#define IAccessibleText_get_nSelections(This,nSelections)\t\\\n    ( (This)->lpVtbl -> get_nSelections(This,nSelections) ) \n\n#define IAccessibleText_get_offsetAtPoint(This,x,y,coordType,offset)\t\\\n    ( (This)->lpVtbl -> get_offsetAtPoint(This,x,y,coordType,offset) ) \n\n#define IAccessibleText_get_selection(This,selectionIndex,startOffset,endOffset)\t\\\n    ( (This)->lpVtbl -> get_selection(This,selectionIndex,startOffset,endOffset) ) \n\n#define IAccessibleText_get_text(This,startOffset,endOffset,text)\t\\\n    ( (This)->lpVtbl -> get_text(This,startOffset,endOffset,text) ) \n\n#define IAccessibleText_get_textBeforeOffset(This,offset,boundaryType,startOffset,endOffset,text)\t\\\n    ( (This)->lpVtbl -> get_textBeforeOffset(This,offset,boundaryType,startOffset,endOffset,text) ) \n\n#define IAccessibleText_get_textAfterOffset(This,offset,boundaryType,startOffset,endOffset,text)\t\\\n    ( (This)->lpVtbl -> get_textAfterOffset(This,offset,boundaryType,startOffset,endOffset,text) ) \n\n#define IAccessibleText_get_textAtOffset(This,offset,boundaryType,startOffset,endOffset,text)\t\\\n    ( (This)->lpVtbl -> get_textAtOffset(This,offset,boundaryType,startOffset,endOffset,text) ) \n\n#define IAccessibleText_removeSelection(This,selectionIndex)\t\\\n    ( (This)->lpVtbl -> removeSelection(This,selectionIndex) ) \n\n#define IAccessibleText_setCaretOffset(This,offset)\t\\\n    ( (This)->lpVtbl -> setCaretOffset(This,offset) ) \n\n#define IAccessibleText_setSelection(This,selectionIndex,startOffset,endOffset)\t\\\n    ( (This)->lpVtbl -> setSelection(This,selectionIndex,startOffset,endOffset) ) \n\n#define IAccessibleText_get_nCharacters(This,nCharacters)\t\\\n    ( (This)->lpVtbl -> get_nCharacters(This,nCharacters) ) \n\n#define IAccessibleText_scrollSubstringTo(This,startIndex,endIndex,scrollType)\t\\\n    ( (This)->lpVtbl -> scrollSubstringTo(This,startIndex,endIndex,scrollType) ) \n\n#define IAccessibleText_scrollSubstringToPoint(This,startIndex,endIndex,coordinateType,x,y)\t\\\n    ( (This)->lpVtbl -> scrollSubstringToPoint(This,startIndex,endIndex,coordinateType,x,y) ) \n\n#define IAccessibleText_get_newText(This,newText)\t\\\n    ( (This)->lpVtbl -> get_newText(This,newText) ) \n\n#define IAccessibleText_get_oldText(This,oldText)\t\\\n    ( (This)->lpVtbl -> get_oldText(This,oldText) ) \n\n#endif /* COBJMACROS */\n\n\n#endif \t/* C style interface */\n\n\n\n\n#endif \t/* __IAccessibleText_INTERFACE_DEFINED__ */\n\n\n#ifndef __IAccessibleText2_INTERFACE_DEFINED__\n#define __IAccessibleText2_INTERFACE_DEFINED__\n\n/* interface IAccessibleText2 */\n/* [uuid][object] */ \n\n\nEXTERN_C const IID IID_IAccessibleText2;\n\n#if defined(__cplusplus) && !defined(CINTERFACE)\n    \n    MIDL_INTERFACE(\"9690A9CC-5C80-4DF5-852E-2D5AE4189A54\")\n    IAccessibleText2 : public IAccessibleText\n    {\n    public:\n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_attributeRange( \n            /* [in] */ long offset,\n            /* [in] */ BSTR filter,\n            /* [out] */ long *startOffset,\n            /* [out] */ long *endOffset,\n            /* [retval][out] */ BSTR *attributeValues) = 0;\n        \n    };\n    \n#else \t/* C style interface */\n\n    typedef struct IAccessibleText2Vtbl\n    {\n        BEGIN_INTERFACE\n        \n        HRESULT ( STDMETHODCALLTYPE *QueryInterface )( \n            IAccessibleText2 * This,\n            /* [in] */ REFIID riid,\n            /* [iid_is][out] */ \n            __RPC__deref_out  void **ppvObject);\n        \n        ULONG ( STDMETHODCALLTYPE *AddRef )( \n            IAccessibleText2 * This);\n        \n        ULONG ( STDMETHODCALLTYPE *Release )( \n            IAccessibleText2 * This);\n        \n        HRESULT ( STDMETHODCALLTYPE *addSelection )( \n            IAccessibleText2 * This,\n            /* [in] */ long startOffset,\n            /* [in] */ long endOffset);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_attributes )( \n            IAccessibleText2 * This,\n            /* [in] */ long offset,\n            /* [out] */ long *startOffset,\n            /* [out] */ long *endOffset,\n            /* [retval][out] */ BSTR *textAttributes);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_caretOffset )( \n            IAccessibleText2 * This,\n            /* [retval][out] */ long *offset);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_characterExtents )( \n            IAccessibleText2 * This,\n            /* [in] */ long offset,\n            /* [in] */ enum IA2CoordinateType coordType,\n            /* [out] */ long *x,\n            /* [out] */ long *y,\n            /* [out] */ long *width,\n            /* [retval][out] */ long *height);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_nSelections )( \n            IAccessibleText2 * This,\n            /* [retval][out] */ long *nSelections);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_offsetAtPoint )( \n            IAccessibleText2 * This,\n            /* [in] */ long x,\n            /* [in] */ long y,\n            /* [in] */ enum IA2CoordinateType coordType,\n            /* [retval][out] */ long *offset);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_selection )( \n            IAccessibleText2 * This,\n            /* [in] */ long selectionIndex,\n            /* [out] */ long *startOffset,\n            /* [retval][out] */ long *endOffset);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_text )( \n            IAccessibleText2 * This,\n            /* [in] */ long startOffset,\n            /* [in] */ long endOffset,\n            /* [retval][out] */ BSTR *text);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_textBeforeOffset )( \n            IAccessibleText2 * This,\n            /* [in] */ long offset,\n            /* [in] */ enum IA2TextBoundaryType boundaryType,\n            /* [out] */ long *startOffset,\n            /* [out] */ long *endOffset,\n            /* [retval][out] */ BSTR *text);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_textAfterOffset )( \n            IAccessibleText2 * This,\n            /* [in] */ long offset,\n            /* [in] */ enum IA2TextBoundaryType boundaryType,\n            /* [out] */ long *startOffset,\n            /* [out] */ long *endOffset,\n            /* [retval][out] */ BSTR *text);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_textAtOffset )( \n            IAccessibleText2 * This,\n            /* [in] */ long offset,\n            /* [in] */ enum IA2TextBoundaryType boundaryType,\n            /* [out] */ long *startOffset,\n            /* [out] */ long *endOffset,\n            /* [retval][out] */ BSTR *text);\n        \n        HRESULT ( STDMETHODCALLTYPE *removeSelection )( \n            IAccessibleText2 * This,\n            /* [in] */ long selectionIndex);\n        \n        HRESULT ( STDMETHODCALLTYPE *setCaretOffset )( \n            IAccessibleText2 * This,\n            /* [in] */ long offset);\n        \n        HRESULT ( STDMETHODCALLTYPE *setSelection )( \n            IAccessibleText2 * This,\n            /* [in] */ long selectionIndex,\n            /* [in] */ long startOffset,\n            /* [in] */ long endOffset);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_nCharacters )( \n            IAccessibleText2 * This,\n            /* [retval][out] */ long *nCharacters);\n        \n        HRESULT ( STDMETHODCALLTYPE *scrollSubstringTo )( \n            IAccessibleText2 * This,\n            /* [in] */ long startIndex,\n            /* [in] */ long endIndex,\n            /* [in] */ enum IA2ScrollType scrollType);\n        \n        HRESULT ( STDMETHODCALLTYPE *scrollSubstringToPoint )( \n            IAccessibleText2 * This,\n            /* [in] */ long startIndex,\n            /* [in] */ long endIndex,\n            /* [in] */ enum IA2CoordinateType coordinateType,\n            /* [in] */ long x,\n            /* [in] */ long y);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_newText )( \n            IAccessibleText2 * This,\n            /* [retval][out] */ IA2TextSegment *newText);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_oldText )( \n            IAccessibleText2 * This,\n            /* [retval][out] */ IA2TextSegment *oldText);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_attributeRange )( \n            IAccessibleText2 * This,\n            /* [in] */ long offset,\n            /* [in] */ BSTR filter,\n            /* [out] */ long *startOffset,\n            /* [out] */ long *endOffset,\n            /* [retval][out] */ BSTR *attributeValues);\n        \n        END_INTERFACE\n    } IAccessibleText2Vtbl;\n\n    interface IAccessibleText2\n    {\n        CONST_VTBL struct IAccessibleText2Vtbl *lpVtbl;\n    };\n\n    \n\n#ifdef COBJMACROS\n\n\n#define IAccessibleText2_QueryInterface(This,riid,ppvObject)\t\\\n    ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) \n\n#define IAccessibleText2_AddRef(This)\t\\\n    ( (This)->lpVtbl -> AddRef(This) ) \n\n#define IAccessibleText2_Release(This)\t\\\n    ( (This)->lpVtbl -> Release(This) ) \n\n\n#define IAccessibleText2_addSelection(This,startOffset,endOffset)\t\\\n    ( (This)->lpVtbl -> addSelection(This,startOffset,endOffset) ) \n\n#define IAccessibleText2_get_attributes(This,offset,startOffset,endOffset,textAttributes)\t\\\n    ( (This)->lpVtbl -> get_attributes(This,offset,startOffset,endOffset,textAttributes) ) \n\n#define IAccessibleText2_get_caretOffset(This,offset)\t\\\n    ( (This)->lpVtbl -> get_caretOffset(This,offset) ) \n\n#define IAccessibleText2_get_characterExtents(This,offset,coordType,x,y,width,height)\t\\\n    ( (This)->lpVtbl -> get_characterExtents(This,offset,coordType,x,y,width,height) ) \n\n#define IAccessibleText2_get_nSelections(This,nSelections)\t\\\n    ( (This)->lpVtbl -> get_nSelections(This,nSelections) ) \n\n#define IAccessibleText2_get_offsetAtPoint(This,x,y,coordType,offset)\t\\\n    ( (This)->lpVtbl -> get_offsetAtPoint(This,x,y,coordType,offset) ) \n\n#define IAccessibleText2_get_selection(This,selectionIndex,startOffset,endOffset)\t\\\n    ( (This)->lpVtbl -> get_selection(This,selectionIndex,startOffset,endOffset) ) \n\n#define IAccessibleText2_get_text(This,startOffset,endOffset,text)\t\\\n    ( (This)->lpVtbl -> get_text(This,startOffset,endOffset,text) ) \n\n#define IAccessibleText2_get_textBeforeOffset(This,offset,boundaryType,startOffset,endOffset,text)\t\\\n    ( (This)->lpVtbl -> get_textBeforeOffset(This,offset,boundaryType,startOffset,endOffset,text) ) \n\n#define IAccessibleText2_get_textAfterOffset(This,offset,boundaryType,startOffset,endOffset,text)\t\\\n    ( (This)->lpVtbl -> get_textAfterOffset(This,offset,boundaryType,startOffset,endOffset,text) ) \n\n#define IAccessibleText2_get_textAtOffset(This,offset,boundaryType,startOffset,endOffset,text)\t\\\n    ( (This)->lpVtbl -> get_textAtOffset(This,offset,boundaryType,startOffset,endOffset,text) ) \n\n#define IAccessibleText2_removeSelection(This,selectionIndex)\t\\\n    ( (This)->lpVtbl -> removeSelection(This,selectionIndex) ) \n\n#define IAccessibleText2_setCaretOffset(This,offset)\t\\\n    ( (This)->lpVtbl -> setCaretOffset(This,offset) ) \n\n#define IAccessibleText2_setSelection(This,selectionIndex,startOffset,endOffset)\t\\\n    ( (This)->lpVtbl -> setSelection(This,selectionIndex,startOffset,endOffset) ) \n\n#define IAccessibleText2_get_nCharacters(This,nCharacters)\t\\\n    ( (This)->lpVtbl -> get_nCharacters(This,nCharacters) ) \n\n#define IAccessibleText2_scrollSubstringTo(This,startIndex,endIndex,scrollType)\t\\\n    ( (This)->lpVtbl -> scrollSubstringTo(This,startIndex,endIndex,scrollType) ) \n\n#define IAccessibleText2_scrollSubstringToPoint(This,startIndex,endIndex,coordinateType,x,y)\t\\\n    ( (This)->lpVtbl -> scrollSubstringToPoint(This,startIndex,endIndex,coordinateType,x,y) ) \n\n#define IAccessibleText2_get_newText(This,newText)\t\\\n    ( (This)->lpVtbl -> get_newText(This,newText) ) \n\n#define IAccessibleText2_get_oldText(This,oldText)\t\\\n    ( (This)->lpVtbl -> get_oldText(This,oldText) ) \n\n\n#define IAccessibleText2_get_attributeRange(This,offset,filter,startOffset,endOffset,attributeValues)\t\\\n    ( (This)->lpVtbl -> get_attributeRange(This,offset,filter,startOffset,endOffset,attributeValues) ) \n\n#endif /* COBJMACROS */\n\n\n#endif \t/* C style interface */\n\n\n\n\n#endif \t/* __IAccessibleText2_INTERFACE_DEFINED__ */\n\n\n#ifndef __IAccessibleEditableText_INTERFACE_DEFINED__\n#define __IAccessibleEditableText_INTERFACE_DEFINED__\n\n/* interface IAccessibleEditableText */\n/* [uuid][object] */ \n\n\nEXTERN_C const IID IID_IAccessibleEditableText;\n\n#if defined(__cplusplus) && !defined(CINTERFACE)\n    \n    MIDL_INTERFACE(\"A59AA09A-7011-4b65-939D-32B1FB5547E3\")\n    IAccessibleEditableText : public IUnknown\n    {\n    public:\n        virtual HRESULT STDMETHODCALLTYPE copyText( \n            /* [in] */ long startOffset,\n            /* [in] */ long endOffset) = 0;\n        \n        virtual HRESULT STDMETHODCALLTYPE deleteText( \n            /* [in] */ long startOffset,\n            /* [in] */ long endOffset) = 0;\n        \n        virtual HRESULT STDMETHODCALLTYPE insertText( \n            /* [in] */ long offset,\n            /* [in] */ BSTR *text) = 0;\n        \n        virtual HRESULT STDMETHODCALLTYPE cutText( \n            /* [in] */ long startOffset,\n            /* [in] */ long endOffset) = 0;\n        \n        virtual HRESULT STDMETHODCALLTYPE pasteText( \n            /* [in] */ long offset) = 0;\n        \n        virtual HRESULT STDMETHODCALLTYPE replaceText( \n            /* [in] */ long startOffset,\n            /* [in] */ long endOffset,\n            /* [in] */ BSTR *text) = 0;\n        \n        virtual HRESULT STDMETHODCALLTYPE setAttributes( \n            /* [in] */ long startOffset,\n            /* [in] */ long endOffset,\n            /* [in] */ BSTR *attributes) = 0;\n        \n    };\n    \n#else \t/* C style interface */\n\n    typedef struct IAccessibleEditableTextVtbl\n    {\n        BEGIN_INTERFACE\n        \n        HRESULT ( STDMETHODCALLTYPE *QueryInterface )( \n            IAccessibleEditableText * This,\n            /* [in] */ REFIID riid,\n            /* [iid_is][out] */ \n            __RPC__deref_out  void **ppvObject);\n        \n        ULONG ( STDMETHODCALLTYPE *AddRef )( \n            IAccessibleEditableText * This);\n        \n        ULONG ( STDMETHODCALLTYPE *Release )( \n            IAccessibleEditableText * This);\n        \n        HRESULT ( STDMETHODCALLTYPE *copyText )( \n            IAccessibleEditableText * This,\n            /* [in] */ long startOffset,\n            /* [in] */ long endOffset);\n        \n        HRESULT ( STDMETHODCALLTYPE *deleteText )( \n            IAccessibleEditableText * This,\n            /* [in] */ long startOffset,\n            /* [in] */ long endOffset);\n        \n        HRESULT ( STDMETHODCALLTYPE *insertText )( \n            IAccessibleEditableText * This,\n            /* [in] */ long offset,\n            /* [in] */ BSTR *text);\n        \n        HRESULT ( STDMETHODCALLTYPE *cutText )( \n            IAccessibleEditableText * This,\n            /* [in] */ long startOffset,\n            /* [in] */ long endOffset);\n        \n        HRESULT ( STDMETHODCALLTYPE *pasteText )( \n            IAccessibleEditableText * This,\n            /* [in] */ long offset);\n        \n        HRESULT ( STDMETHODCALLTYPE *replaceText )( \n            IAccessibleEditableText * This,\n            /* [in] */ long startOffset,\n            /* [in] */ long endOffset,\n            /* [in] */ BSTR *text);\n        \n        HRESULT ( STDMETHODCALLTYPE *setAttributes )( \n            IAccessibleEditableText * This,\n            /* [in] */ long startOffset,\n            /* [in] */ long endOffset,\n            /* [in] */ BSTR *attributes);\n        \n        END_INTERFACE\n    } IAccessibleEditableTextVtbl;\n\n    interface IAccessibleEditableText\n    {\n        CONST_VTBL struct IAccessibleEditableTextVtbl *lpVtbl;\n    };\n\n    \n\n#ifdef COBJMACROS\n\n\n#define IAccessibleEditableText_QueryInterface(This,riid,ppvObject)\t\\\n    ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) \n\n#define IAccessibleEditableText_AddRef(This)\t\\\n    ( (This)->lpVtbl -> AddRef(This) ) \n\n#define IAccessibleEditableText_Release(This)\t\\\n    ( (This)->lpVtbl -> Release(This) ) \n\n\n#define IAccessibleEditableText_copyText(This,startOffset,endOffset)\t\\\n    ( (This)->lpVtbl -> copyText(This,startOffset,endOffset) ) \n\n#define IAccessibleEditableText_deleteText(This,startOffset,endOffset)\t\\\n    ( (This)->lpVtbl -> deleteText(This,startOffset,endOffset) ) \n\n#define IAccessibleEditableText_insertText(This,offset,text)\t\\\n    ( (This)->lpVtbl -> insertText(This,offset,text) ) \n\n#define IAccessibleEditableText_cutText(This,startOffset,endOffset)\t\\\n    ( (This)->lpVtbl -> cutText(This,startOffset,endOffset) ) \n\n#define IAccessibleEditableText_pasteText(This,offset)\t\\\n    ( (This)->lpVtbl -> pasteText(This,offset) ) \n\n#define IAccessibleEditableText_replaceText(This,startOffset,endOffset,text)\t\\\n    ( (This)->lpVtbl -> replaceText(This,startOffset,endOffset,text) ) \n\n#define IAccessibleEditableText_setAttributes(This,startOffset,endOffset,attributes)\t\\\n    ( (This)->lpVtbl -> setAttributes(This,startOffset,endOffset,attributes) ) \n\n#endif /* COBJMACROS */\n\n\n#endif \t/* C style interface */\n\n\n\n\n#endif \t/* __IAccessibleEditableText_INTERFACE_DEFINED__ */\n\n\n#ifndef __IAccessibleHyperlink_INTERFACE_DEFINED__\n#define __IAccessibleHyperlink_INTERFACE_DEFINED__\n\n/* interface IAccessibleHyperlink */\n/* [uuid][object] */ \n\n\nEXTERN_C const IID IID_IAccessibleHyperlink;\n\n#if defined(__cplusplus) && !defined(CINTERFACE)\n    \n    MIDL_INTERFACE(\"01C20F2B-3DD2-400f-949F-AD00BDAB1D41\")\n    IAccessibleHyperlink : public IAccessibleAction\n    {\n    public:\n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_anchor( \n            /* [in] */ long index,\n            /* [retval][out] */ VARIANT *anchor) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_anchorTarget( \n            /* [in] */ long index,\n            /* [retval][out] */ VARIANT *anchorTarget) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_startIndex( \n            /* [retval][out] */ long *index) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_endIndex( \n            /* [retval][out] */ long *index) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_valid( \n            /* [retval][out] */ boolean *valid) = 0;\n        \n    };\n    \n#else \t/* C style interface */\n\n    typedef struct IAccessibleHyperlinkVtbl\n    {\n        BEGIN_INTERFACE\n        \n        HRESULT ( STDMETHODCALLTYPE *QueryInterface )( \n            IAccessibleHyperlink * This,\n            /* [in] */ REFIID riid,\n            /* [iid_is][out] */ \n            __RPC__deref_out  void **ppvObject);\n        \n        ULONG ( STDMETHODCALLTYPE *AddRef )( \n            IAccessibleHyperlink * This);\n        \n        ULONG ( STDMETHODCALLTYPE *Release )( \n            IAccessibleHyperlink * This);\n        \n        HRESULT ( STDMETHODCALLTYPE *nActions )( \n            IAccessibleHyperlink * This,\n            /* [retval][out] */ long *nActions);\n        \n        HRESULT ( STDMETHODCALLTYPE *doAction )( \n            IAccessibleHyperlink * This,\n            /* [in] */ long actionIndex);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_description )( \n            IAccessibleHyperlink * This,\n            /* [in] */ long actionIndex,\n            /* [retval][out] */ BSTR *description);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_keyBinding )( \n            IAccessibleHyperlink * This,\n            /* [in] */ long actionIndex,\n            /* [in] */ long nMaxBindings,\n            /* [length_is][length_is][size_is][size_is][out] */ BSTR **keyBindings,\n            /* [retval][out] */ long *nBindings);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_name )( \n            IAccessibleHyperlink * This,\n            /* [in] */ long actionIndex,\n            /* [retval][out] */ BSTR *name);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_localizedName )( \n            IAccessibleHyperlink * This,\n            /* [in] */ long actionIndex,\n            /* [retval][out] */ BSTR *localizedName);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_anchor )( \n            IAccessibleHyperlink * This,\n            /* [in] */ long index,\n            /* [retval][out] */ VARIANT *anchor);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_anchorTarget )( \n            IAccessibleHyperlink * This,\n            /* [in] */ long index,\n            /* [retval][out] */ VARIANT *anchorTarget);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_startIndex )( \n            IAccessibleHyperlink * This,\n            /* [retval][out] */ long *index);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_endIndex )( \n            IAccessibleHyperlink * This,\n            /* [retval][out] */ long *index);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_valid )( \n            IAccessibleHyperlink * This,\n            /* [retval][out] */ boolean *valid);\n        \n        END_INTERFACE\n    } IAccessibleHyperlinkVtbl;\n\n    interface IAccessibleHyperlink\n    {\n        CONST_VTBL struct IAccessibleHyperlinkVtbl *lpVtbl;\n    };\n\n    \n\n#ifdef COBJMACROS\n\n\n#define IAccessibleHyperlink_QueryInterface(This,riid,ppvObject)\t\\\n    ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) \n\n#define IAccessibleHyperlink_AddRef(This)\t\\\n    ( (This)->lpVtbl -> AddRef(This) ) \n\n#define IAccessibleHyperlink_Release(This)\t\\\n    ( (This)->lpVtbl -> Release(This) ) \n\n\n#define IAccessibleHyperlink_nActions(This,nActions)\t\\\n    ( (This)->lpVtbl -> nActions(This,nActions) ) \n\n#define IAccessibleHyperlink_doAction(This,actionIndex)\t\\\n    ( (This)->lpVtbl -> doAction(This,actionIndex) ) \n\n#define IAccessibleHyperlink_get_description(This,actionIndex,description)\t\\\n    ( (This)->lpVtbl -> get_description(This,actionIndex,description) ) \n\n#define IAccessibleHyperlink_get_keyBinding(This,actionIndex,nMaxBindings,keyBindings,nBindings)\t\\\n    ( (This)->lpVtbl -> get_keyBinding(This,actionIndex,nMaxBindings,keyBindings,nBindings) ) \n\n#define IAccessibleHyperlink_get_name(This,actionIndex,name)\t\\\n    ( (This)->lpVtbl -> get_name(This,actionIndex,name) ) \n\n#define IAccessibleHyperlink_get_localizedName(This,actionIndex,localizedName)\t\\\n    ( (This)->lpVtbl -> get_localizedName(This,actionIndex,localizedName) ) \n\n\n#define IAccessibleHyperlink_get_anchor(This,index,anchor)\t\\\n    ( (This)->lpVtbl -> get_anchor(This,index,anchor) ) \n\n#define IAccessibleHyperlink_get_anchorTarget(This,index,anchorTarget)\t\\\n    ( (This)->lpVtbl -> get_anchorTarget(This,index,anchorTarget) ) \n\n#define IAccessibleHyperlink_get_startIndex(This,index)\t\\\n    ( (This)->lpVtbl -> get_startIndex(This,index) ) \n\n#define IAccessibleHyperlink_get_endIndex(This,index)\t\\\n    ( (This)->lpVtbl -> get_endIndex(This,index) ) \n\n#define IAccessibleHyperlink_get_valid(This,valid)\t\\\n    ( (This)->lpVtbl -> get_valid(This,valid) ) \n\n#endif /* COBJMACROS */\n\n\n#endif \t/* C style interface */\n\n\n\n\n#endif \t/* __IAccessibleHyperlink_INTERFACE_DEFINED__ */\n\n\n#ifndef __IAccessibleHypertext_INTERFACE_DEFINED__\n#define __IAccessibleHypertext_INTERFACE_DEFINED__\n\n/* interface IAccessibleHypertext */\n/* [uuid][object] */ \n\n\nEXTERN_C const IID IID_IAccessibleHypertext;\n\n#if defined(__cplusplus) && !defined(CINTERFACE)\n    \n    MIDL_INTERFACE(\"6B4F8BBF-F1F2-418a-B35E-A195BC4103B9\")\n    IAccessibleHypertext : public IAccessibleText\n    {\n    public:\n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_nHyperlinks( \n            /* [retval][out] */ long *hyperlinkCount) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_hyperlink( \n            /* [in] */ long index,\n            /* [retval][out] */ IAccessibleHyperlink **hyperlink) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_hyperlinkIndex( \n            /* [in] */ long charIndex,\n            /* [retval][out] */ long *hyperlinkIndex) = 0;\n        \n    };\n    \n#else \t/* C style interface */\n\n    typedef struct IAccessibleHypertextVtbl\n    {\n        BEGIN_INTERFACE\n        \n        HRESULT ( STDMETHODCALLTYPE *QueryInterface )( \n            IAccessibleHypertext * This,\n            /* [in] */ REFIID riid,\n            /* [iid_is][out] */ \n            __RPC__deref_out  void **ppvObject);\n        \n        ULONG ( STDMETHODCALLTYPE *AddRef )( \n            IAccessibleHypertext * This);\n        \n        ULONG ( STDMETHODCALLTYPE *Release )( \n            IAccessibleHypertext * This);\n        \n        HRESULT ( STDMETHODCALLTYPE *addSelection )( \n            IAccessibleHypertext * This,\n            /* [in] */ long startOffset,\n            /* [in] */ long endOffset);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_attributes )( \n            IAccessibleHypertext * This,\n            /* [in] */ long offset,\n            /* [out] */ long *startOffset,\n            /* [out] */ long *endOffset,\n            /* [retval][out] */ BSTR *textAttributes);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_caretOffset )( \n            IAccessibleHypertext * This,\n            /* [retval][out] */ long *offset);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_characterExtents )( \n            IAccessibleHypertext * This,\n            /* [in] */ long offset,\n            /* [in] */ enum IA2CoordinateType coordType,\n            /* [out] */ long *x,\n            /* [out] */ long *y,\n            /* [out] */ long *width,\n            /* [retval][out] */ long *height);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_nSelections )( \n            IAccessibleHypertext * This,\n            /* [retval][out] */ long *nSelections);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_offsetAtPoint )( \n            IAccessibleHypertext * This,\n            /* [in] */ long x,\n            /* [in] */ long y,\n            /* [in] */ enum IA2CoordinateType coordType,\n            /* [retval][out] */ long *offset);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_selection )( \n            IAccessibleHypertext * This,\n            /* [in] */ long selectionIndex,\n            /* [out] */ long *startOffset,\n            /* [retval][out] */ long *endOffset);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_text )( \n            IAccessibleHypertext * This,\n            /* [in] */ long startOffset,\n            /* [in] */ long endOffset,\n            /* [retval][out] */ BSTR *text);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_textBeforeOffset )( \n            IAccessibleHypertext * This,\n            /* [in] */ long offset,\n            /* [in] */ enum IA2TextBoundaryType boundaryType,\n            /* [out] */ long *startOffset,\n            /* [out] */ long *endOffset,\n            /* [retval][out] */ BSTR *text);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_textAfterOffset )( \n            IAccessibleHypertext * This,\n            /* [in] */ long offset,\n            /* [in] */ enum IA2TextBoundaryType boundaryType,\n            /* [out] */ long *startOffset,\n            /* [out] */ long *endOffset,\n            /* [retval][out] */ BSTR *text);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_textAtOffset )( \n            IAccessibleHypertext * This,\n            /* [in] */ long offset,\n            /* [in] */ enum IA2TextBoundaryType boundaryType,\n            /* [out] */ long *startOffset,\n            /* [out] */ long *endOffset,\n            /* [retval][out] */ BSTR *text);\n        \n        HRESULT ( STDMETHODCALLTYPE *removeSelection )( \n            IAccessibleHypertext * This,\n            /* [in] */ long selectionIndex);\n        \n        HRESULT ( STDMETHODCALLTYPE *setCaretOffset )( \n            IAccessibleHypertext * This,\n            /* [in] */ long offset);\n        \n        HRESULT ( STDMETHODCALLTYPE *setSelection )( \n            IAccessibleHypertext * This,\n            /* [in] */ long selectionIndex,\n            /* [in] */ long startOffset,\n            /* [in] */ long endOffset);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_nCharacters )( \n            IAccessibleHypertext * This,\n            /* [retval][out] */ long *nCharacters);\n        \n        HRESULT ( STDMETHODCALLTYPE *scrollSubstringTo )( \n            IAccessibleHypertext * This,\n            /* [in] */ long startIndex,\n            /* [in] */ long endIndex,\n            /* [in] */ enum IA2ScrollType scrollType);\n        \n        HRESULT ( STDMETHODCALLTYPE *scrollSubstringToPoint )( \n            IAccessibleHypertext * This,\n            /* [in] */ long startIndex,\n            /* [in] */ long endIndex,\n            /* [in] */ enum IA2CoordinateType coordinateType,\n            /* [in] */ long x,\n            /* [in] */ long y);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_newText )( \n            IAccessibleHypertext * This,\n            /* [retval][out] */ IA2TextSegment *newText);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_oldText )( \n            IAccessibleHypertext * This,\n            /* [retval][out] */ IA2TextSegment *oldText);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_nHyperlinks )( \n            IAccessibleHypertext * This,\n            /* [retval][out] */ long *hyperlinkCount);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_hyperlink )( \n            IAccessibleHypertext * This,\n            /* [in] */ long index,\n            /* [retval][out] */ IAccessibleHyperlink **hyperlink);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_hyperlinkIndex )( \n            IAccessibleHypertext * This,\n            /* [in] */ long charIndex,\n            /* [retval][out] */ long *hyperlinkIndex);\n        \n        END_INTERFACE\n    } IAccessibleHypertextVtbl;\n\n    interface IAccessibleHypertext\n    {\n        CONST_VTBL struct IAccessibleHypertextVtbl *lpVtbl;\n    };\n\n    \n\n#ifdef COBJMACROS\n\n\n#define IAccessibleHypertext_QueryInterface(This,riid,ppvObject)\t\\\n    ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) \n\n#define IAccessibleHypertext_AddRef(This)\t\\\n    ( (This)->lpVtbl -> AddRef(This) ) \n\n#define IAccessibleHypertext_Release(This)\t\\\n    ( (This)->lpVtbl -> Release(This) ) \n\n\n#define IAccessibleHypertext_addSelection(This,startOffset,endOffset)\t\\\n    ( (This)->lpVtbl -> addSelection(This,startOffset,endOffset) ) \n\n#define IAccessibleHypertext_get_attributes(This,offset,startOffset,endOffset,textAttributes)\t\\\n    ( (This)->lpVtbl -> get_attributes(This,offset,startOffset,endOffset,textAttributes) ) \n\n#define IAccessibleHypertext_get_caretOffset(This,offset)\t\\\n    ( (This)->lpVtbl -> get_caretOffset(This,offset) ) \n\n#define IAccessibleHypertext_get_characterExtents(This,offset,coordType,x,y,width,height)\t\\\n    ( (This)->lpVtbl -> get_characterExtents(This,offset,coordType,x,y,width,height) ) \n\n#define IAccessibleHypertext_get_nSelections(This,nSelections)\t\\\n    ( (This)->lpVtbl -> get_nSelections(This,nSelections) ) \n\n#define IAccessibleHypertext_get_offsetAtPoint(This,x,y,coordType,offset)\t\\\n    ( (This)->lpVtbl -> get_offsetAtPoint(This,x,y,coordType,offset) ) \n\n#define IAccessibleHypertext_get_selection(This,selectionIndex,startOffset,endOffset)\t\\\n    ( (This)->lpVtbl -> get_selection(This,selectionIndex,startOffset,endOffset) ) \n\n#define IAccessibleHypertext_get_text(This,startOffset,endOffset,text)\t\\\n    ( (This)->lpVtbl -> get_text(This,startOffset,endOffset,text) ) \n\n#define IAccessibleHypertext_get_textBeforeOffset(This,offset,boundaryType,startOffset,endOffset,text)\t\\\n    ( (This)->lpVtbl -> get_textBeforeOffset(This,offset,boundaryType,startOffset,endOffset,text) ) \n\n#define IAccessibleHypertext_get_textAfterOffset(This,offset,boundaryType,startOffset,endOffset,text)\t\\\n    ( (This)->lpVtbl -> get_textAfterOffset(This,offset,boundaryType,startOffset,endOffset,text) ) \n\n#define IAccessibleHypertext_get_textAtOffset(This,offset,boundaryType,startOffset,endOffset,text)\t\\\n    ( (This)->lpVtbl -> get_textAtOffset(This,offset,boundaryType,startOffset,endOffset,text) ) \n\n#define IAccessibleHypertext_removeSelection(This,selectionIndex)\t\\\n    ( (This)->lpVtbl -> removeSelection(This,selectionIndex) ) \n\n#define IAccessibleHypertext_setCaretOffset(This,offset)\t\\\n    ( (This)->lpVtbl -> setCaretOffset(This,offset) ) \n\n#define IAccessibleHypertext_setSelection(This,selectionIndex,startOffset,endOffset)\t\\\n    ( (This)->lpVtbl -> setSelection(This,selectionIndex,startOffset,endOffset) ) \n\n#define IAccessibleHypertext_get_nCharacters(This,nCharacters)\t\\\n    ( (This)->lpVtbl -> get_nCharacters(This,nCharacters) ) \n\n#define IAccessibleHypertext_scrollSubstringTo(This,startIndex,endIndex,scrollType)\t\\\n    ( (This)->lpVtbl -> scrollSubstringTo(This,startIndex,endIndex,scrollType) ) \n\n#define IAccessibleHypertext_scrollSubstringToPoint(This,startIndex,endIndex,coordinateType,x,y)\t\\\n    ( (This)->lpVtbl -> scrollSubstringToPoint(This,startIndex,endIndex,coordinateType,x,y) ) \n\n#define IAccessibleHypertext_get_newText(This,newText)\t\\\n    ( (This)->lpVtbl -> get_newText(This,newText) ) \n\n#define IAccessibleHypertext_get_oldText(This,oldText)\t\\\n    ( (This)->lpVtbl -> get_oldText(This,oldText) ) \n\n\n#define IAccessibleHypertext_get_nHyperlinks(This,hyperlinkCount)\t\\\n    ( (This)->lpVtbl -> get_nHyperlinks(This,hyperlinkCount) ) \n\n#define IAccessibleHypertext_get_hyperlink(This,index,hyperlink)\t\\\n    ( (This)->lpVtbl -> get_hyperlink(This,index,hyperlink) ) \n\n#define IAccessibleHypertext_get_hyperlinkIndex(This,charIndex,hyperlinkIndex)\t\\\n    ( (This)->lpVtbl -> get_hyperlinkIndex(This,charIndex,hyperlinkIndex) ) \n\n#endif /* COBJMACROS */\n\n\n#endif \t/* C style interface */\n\n\n\n\n#endif \t/* __IAccessibleHypertext_INTERFACE_DEFINED__ */\n\n\n#ifndef __IAccessibleHypertext2_INTERFACE_DEFINED__\n#define __IAccessibleHypertext2_INTERFACE_DEFINED__\n\n/* interface IAccessibleHypertext2 */\n/* [uuid][object] */ \n\n\nEXTERN_C const IID IID_IAccessibleHypertext2;\n\n#if defined(__cplusplus) && !defined(CINTERFACE)\n    \n    MIDL_INTERFACE(\"CF64D89F-8287-4B44-8501-A827453A6077\")\n    IAccessibleHypertext2 : public IAccessibleHypertext\n    {\n    public:\n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_hyperlinks( \n            /* [size_is][size_is][out] */ IAccessibleHyperlink ***hyperlinks,\n            /* [retval][out] */ long *nHyperlinks) = 0;\n        \n    };\n    \n#else \t/* C style interface */\n\n    typedef struct IAccessibleHypertext2Vtbl\n    {\n        BEGIN_INTERFACE\n        \n        HRESULT ( STDMETHODCALLTYPE *QueryInterface )( \n            IAccessibleHypertext2 * This,\n            /* [in] */ REFIID riid,\n            /* [iid_is][out] */ \n            __RPC__deref_out  void **ppvObject);\n        \n        ULONG ( STDMETHODCALLTYPE *AddRef )( \n            IAccessibleHypertext2 * This);\n        \n        ULONG ( STDMETHODCALLTYPE *Release )( \n            IAccessibleHypertext2 * This);\n        \n        HRESULT ( STDMETHODCALLTYPE *addSelection )( \n            IAccessibleHypertext2 * This,\n            /* [in] */ long startOffset,\n            /* [in] */ long endOffset);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_attributes )( \n            IAccessibleHypertext2 * This,\n            /* [in] */ long offset,\n            /* [out] */ long *startOffset,\n            /* [out] */ long *endOffset,\n            /* [retval][out] */ BSTR *textAttributes);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_caretOffset )( \n            IAccessibleHypertext2 * This,\n            /* [retval][out] */ long *offset);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_characterExtents )( \n            IAccessibleHypertext2 * This,\n            /* [in] */ long offset,\n            /* [in] */ enum IA2CoordinateType coordType,\n            /* [out] */ long *x,\n            /* [out] */ long *y,\n            /* [out] */ long *width,\n            /* [retval][out] */ long *height);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_nSelections )( \n            IAccessibleHypertext2 * This,\n            /* [retval][out] */ long *nSelections);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_offsetAtPoint )( \n            IAccessibleHypertext2 * This,\n            /* [in] */ long x,\n            /* [in] */ long y,\n            /* [in] */ enum IA2CoordinateType coordType,\n            /* [retval][out] */ long *offset);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_selection )( \n            IAccessibleHypertext2 * This,\n            /* [in] */ long selectionIndex,\n            /* [out] */ long *startOffset,\n            /* [retval][out] */ long *endOffset);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_text )( \n            IAccessibleHypertext2 * This,\n            /* [in] */ long startOffset,\n            /* [in] */ long endOffset,\n            /* [retval][out] */ BSTR *text);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_textBeforeOffset )( \n            IAccessibleHypertext2 * This,\n            /* [in] */ long offset,\n            /* [in] */ enum IA2TextBoundaryType boundaryType,\n            /* [out] */ long *startOffset,\n            /* [out] */ long *endOffset,\n            /* [retval][out] */ BSTR *text);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_textAfterOffset )( \n            IAccessibleHypertext2 * This,\n            /* [in] */ long offset,\n            /* [in] */ enum IA2TextBoundaryType boundaryType,\n            /* [out] */ long *startOffset,\n            /* [out] */ long *endOffset,\n            /* [retval][out] */ BSTR *text);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_textAtOffset )( \n            IAccessibleHypertext2 * This,\n            /* [in] */ long offset,\n            /* [in] */ enum IA2TextBoundaryType boundaryType,\n            /* [out] */ long *startOffset,\n            /* [out] */ long *endOffset,\n            /* [retval][out] */ BSTR *text);\n        \n        HRESULT ( STDMETHODCALLTYPE *removeSelection )( \n            IAccessibleHypertext2 * This,\n            /* [in] */ long selectionIndex);\n        \n        HRESULT ( STDMETHODCALLTYPE *setCaretOffset )( \n            IAccessibleHypertext2 * This,\n            /* [in] */ long offset);\n        \n        HRESULT ( STDMETHODCALLTYPE *setSelection )( \n            IAccessibleHypertext2 * This,\n            /* [in] */ long selectionIndex,\n            /* [in] */ long startOffset,\n            /* [in] */ long endOffset);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_nCharacters )( \n            IAccessibleHypertext2 * This,\n            /* [retval][out] */ long *nCharacters);\n        \n        HRESULT ( STDMETHODCALLTYPE *scrollSubstringTo )( \n            IAccessibleHypertext2 * This,\n            /* [in] */ long startIndex,\n            /* [in] */ long endIndex,\n            /* [in] */ enum IA2ScrollType scrollType);\n        \n        HRESULT ( STDMETHODCALLTYPE *scrollSubstringToPoint )( \n            IAccessibleHypertext2 * This,\n            /* [in] */ long startIndex,\n            /* [in] */ long endIndex,\n            /* [in] */ enum IA2CoordinateType coordinateType,\n            /* [in] */ long x,\n            /* [in] */ long y);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_newText )( \n            IAccessibleHypertext2 * This,\n            /* [retval][out] */ IA2TextSegment *newText);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_oldText )( \n            IAccessibleHypertext2 * This,\n            /* [retval][out] */ IA2TextSegment *oldText);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_nHyperlinks )( \n            IAccessibleHypertext2 * This,\n            /* [retval][out] */ long *hyperlinkCount);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_hyperlink )( \n            IAccessibleHypertext2 * This,\n            /* [in] */ long index,\n            /* [retval][out] */ IAccessibleHyperlink **hyperlink);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_hyperlinkIndex )( \n            IAccessibleHypertext2 * This,\n            /* [in] */ long charIndex,\n            /* [retval][out] */ long *hyperlinkIndex);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_hyperlinks )( \n            IAccessibleHypertext2 * This,\n            /* [size_is][size_is][out] */ IAccessibleHyperlink ***hyperlinks,\n            /* [retval][out] */ long *nHyperlinks);\n        \n        END_INTERFACE\n    } IAccessibleHypertext2Vtbl;\n\n    interface IAccessibleHypertext2\n    {\n        CONST_VTBL struct IAccessibleHypertext2Vtbl *lpVtbl;\n    };\n\n    \n\n#ifdef COBJMACROS\n\n\n#define IAccessibleHypertext2_QueryInterface(This,riid,ppvObject)\t\\\n    ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) \n\n#define IAccessibleHypertext2_AddRef(This)\t\\\n    ( (This)->lpVtbl -> AddRef(This) ) \n\n#define IAccessibleHypertext2_Release(This)\t\\\n    ( (This)->lpVtbl -> Release(This) ) \n\n\n#define IAccessibleHypertext2_addSelection(This,startOffset,endOffset)\t\\\n    ( (This)->lpVtbl -> addSelection(This,startOffset,endOffset) ) \n\n#define IAccessibleHypertext2_get_attributes(This,offset,startOffset,endOffset,textAttributes)\t\\\n    ( (This)->lpVtbl -> get_attributes(This,offset,startOffset,endOffset,textAttributes) ) \n\n#define IAccessibleHypertext2_get_caretOffset(This,offset)\t\\\n    ( (This)->lpVtbl -> get_caretOffset(This,offset) ) \n\n#define IAccessibleHypertext2_get_characterExtents(This,offset,coordType,x,y,width,height)\t\\\n    ( (This)->lpVtbl -> get_characterExtents(This,offset,coordType,x,y,width,height) ) \n\n#define IAccessibleHypertext2_get_nSelections(This,nSelections)\t\\\n    ( (This)->lpVtbl -> get_nSelections(This,nSelections) ) \n\n#define IAccessibleHypertext2_get_offsetAtPoint(This,x,y,coordType,offset)\t\\\n    ( (This)->lpVtbl -> get_offsetAtPoint(This,x,y,coordType,offset) ) \n\n#define IAccessibleHypertext2_get_selection(This,selectionIndex,startOffset,endOffset)\t\\\n    ( (This)->lpVtbl -> get_selection(This,selectionIndex,startOffset,endOffset) ) \n\n#define IAccessibleHypertext2_get_text(This,startOffset,endOffset,text)\t\\\n    ( (This)->lpVtbl -> get_text(This,startOffset,endOffset,text) ) \n\n#define IAccessibleHypertext2_get_textBeforeOffset(This,offset,boundaryType,startOffset,endOffset,text)\t\\\n    ( (This)->lpVtbl -> get_textBeforeOffset(This,offset,boundaryType,startOffset,endOffset,text) ) \n\n#define IAccessibleHypertext2_get_textAfterOffset(This,offset,boundaryType,startOffset,endOffset,text)\t\\\n    ( (This)->lpVtbl -> get_textAfterOffset(This,offset,boundaryType,startOffset,endOffset,text) ) \n\n#define IAccessibleHypertext2_get_textAtOffset(This,offset,boundaryType,startOffset,endOffset,text)\t\\\n    ( (This)->lpVtbl -> get_textAtOffset(This,offset,boundaryType,startOffset,endOffset,text) ) \n\n#define IAccessibleHypertext2_removeSelection(This,selectionIndex)\t\\\n    ( (This)->lpVtbl -> removeSelection(This,selectionIndex) ) \n\n#define IAccessibleHypertext2_setCaretOffset(This,offset)\t\\\n    ( (This)->lpVtbl -> setCaretOffset(This,offset) ) \n\n#define IAccessibleHypertext2_setSelection(This,selectionIndex,startOffset,endOffset)\t\\\n    ( (This)->lpVtbl -> setSelection(This,selectionIndex,startOffset,endOffset) ) \n\n#define IAccessibleHypertext2_get_nCharacters(This,nCharacters)\t\\\n    ( (This)->lpVtbl -> get_nCharacters(This,nCharacters) ) \n\n#define IAccessibleHypertext2_scrollSubstringTo(This,startIndex,endIndex,scrollType)\t\\\n    ( (This)->lpVtbl -> scrollSubstringTo(This,startIndex,endIndex,scrollType) ) \n\n#define IAccessibleHypertext2_scrollSubstringToPoint(This,startIndex,endIndex,coordinateType,x,y)\t\\\n    ( (This)->lpVtbl -> scrollSubstringToPoint(This,startIndex,endIndex,coordinateType,x,y) ) \n\n#define IAccessibleHypertext2_get_newText(This,newText)\t\\\n    ( (This)->lpVtbl -> get_newText(This,newText) ) \n\n#define IAccessibleHypertext2_get_oldText(This,oldText)\t\\\n    ( (This)->lpVtbl -> get_oldText(This,oldText) ) \n\n\n#define IAccessibleHypertext2_get_nHyperlinks(This,hyperlinkCount)\t\\\n    ( (This)->lpVtbl -> get_nHyperlinks(This,hyperlinkCount) ) \n\n#define IAccessibleHypertext2_get_hyperlink(This,index,hyperlink)\t\\\n    ( (This)->lpVtbl -> get_hyperlink(This,index,hyperlink) ) \n\n#define IAccessibleHypertext2_get_hyperlinkIndex(This,charIndex,hyperlinkIndex)\t\\\n    ( (This)->lpVtbl -> get_hyperlinkIndex(This,charIndex,hyperlinkIndex) ) \n\n\n#define IAccessibleHypertext2_get_hyperlinks(This,hyperlinks,nHyperlinks)\t\\\n    ( (This)->lpVtbl -> get_hyperlinks(This,hyperlinks,nHyperlinks) ) \n\n#endif /* COBJMACROS */\n\n\n#endif \t/* C style interface */\n\n\n\n\n#endif \t/* __IAccessibleHypertext2_INTERFACE_DEFINED__ */\n\n\n#ifndef __IAccessibleTable_INTERFACE_DEFINED__\n#define __IAccessibleTable_INTERFACE_DEFINED__\n\n/* interface IAccessibleTable */\n/* [uuid][object] */ \n\n\nEXTERN_C const IID IID_IAccessibleTable;\n\n#if defined(__cplusplus) && !defined(CINTERFACE)\n    \n    MIDL_INTERFACE(\"35AD8070-C20C-4fb4-B094-F4F7275DD469\")\n    IAccessibleTable : public IUnknown\n    {\n    public:\n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_accessibleAt( \n            /* [in] */ long row,\n            /* [in] */ long column,\n            /* [retval][out] */ IUnknown **accessible) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_caption( \n            /* [retval][out] */ IUnknown **accessible) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_childIndex( \n            /* [in] */ long rowIndex,\n            /* [in] */ long columnIndex,\n            /* [retval][out] */ long *cellIndex) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_columnDescription( \n            /* [in] */ long column,\n            /* [retval][out] */ BSTR *description) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_columnExtentAt( \n            /* [in] */ long row,\n            /* [in] */ long column,\n            /* [retval][out] */ long *nColumnsSpanned) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_columnHeader( \n            /* [out] */ IAccessibleTable **accessibleTable,\n            /* [retval][out] */ long *startingRowIndex) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_columnIndex( \n            /* [in] */ long cellIndex,\n            /* [retval][out] */ long *columnIndex) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_nColumns( \n            /* [retval][out] */ long *columnCount) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_nRows( \n            /* [retval][out] */ long *rowCount) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_nSelectedChildren( \n            /* [retval][out] */ long *cellCount) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_nSelectedColumns( \n            /* [retval][out] */ long *columnCount) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_nSelectedRows( \n            /* [retval][out] */ long *rowCount) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_rowDescription( \n            /* [in] */ long row,\n            /* [retval][out] */ BSTR *description) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_rowExtentAt( \n            /* [in] */ long row,\n            /* [in] */ long column,\n            /* [retval][out] */ long *nRowsSpanned) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_rowHeader( \n            /* [out] */ IAccessibleTable **accessibleTable,\n            /* [retval][out] */ long *startingColumnIndex) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_rowIndex( \n            /* [in] */ long cellIndex,\n            /* [retval][out] */ long *rowIndex) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_selectedChildren( \n            /* [in] */ long maxChildren,\n            /* [length_is][length_is][size_is][size_is][out] */ long **children,\n            /* [retval][out] */ long *nChildren) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_selectedColumns( \n            /* [in] */ long maxColumns,\n            /* [length_is][length_is][size_is][size_is][out] */ long **columns,\n            /* [retval][out] */ long *nColumns) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_selectedRows( \n            /* [in] */ long maxRows,\n            /* [length_is][length_is][size_is][size_is][out] */ long **rows,\n            /* [retval][out] */ long *nRows) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_summary( \n            /* [retval][out] */ IUnknown **accessible) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_isColumnSelected( \n            /* [in] */ long column,\n            /* [retval][out] */ boolean *isSelected) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_isRowSelected( \n            /* [in] */ long row,\n            /* [retval][out] */ boolean *isSelected) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_isSelected( \n            /* [in] */ long row,\n            /* [in] */ long column,\n            /* [retval][out] */ boolean *isSelected) = 0;\n        \n        virtual HRESULT STDMETHODCALLTYPE selectRow( \n            /* [in] */ long row) = 0;\n        \n        virtual HRESULT STDMETHODCALLTYPE selectColumn( \n            /* [in] */ long column) = 0;\n        \n        virtual HRESULT STDMETHODCALLTYPE unselectRow( \n            /* [in] */ long row) = 0;\n        \n        virtual HRESULT STDMETHODCALLTYPE unselectColumn( \n            /* [in] */ long column) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_rowColumnExtentsAtIndex( \n            /* [in] */ long index,\n            /* [out] */ long *row,\n            /* [out] */ long *column,\n            /* [out] */ long *rowExtents,\n            /* [out] */ long *columnExtents,\n            /* [retval][out] */ boolean *isSelected) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_modelChange( \n            /* [retval][out] */ IA2TableModelChange *modelChange) = 0;\n        \n    };\n    \n#else \t/* C style interface */\n\n    typedef struct IAccessibleTableVtbl\n    {\n        BEGIN_INTERFACE\n        \n        HRESULT ( STDMETHODCALLTYPE *QueryInterface )( \n            IAccessibleTable * This,\n            /* [in] */ REFIID riid,\n            /* [iid_is][out] */ \n            __RPC__deref_out  void **ppvObject);\n        \n        ULONG ( STDMETHODCALLTYPE *AddRef )( \n            IAccessibleTable * This);\n        \n        ULONG ( STDMETHODCALLTYPE *Release )( \n            IAccessibleTable * This);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_accessibleAt )( \n            IAccessibleTable * This,\n            /* [in] */ long row,\n            /* [in] */ long column,\n            /* [retval][out] */ IUnknown **accessible);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_caption )( \n            IAccessibleTable * This,\n            /* [retval][out] */ IUnknown **accessible);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_childIndex )( \n            IAccessibleTable * This,\n            /* [in] */ long rowIndex,\n            /* [in] */ long columnIndex,\n            /* [retval][out] */ long *cellIndex);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_columnDescription )( \n            IAccessibleTable * This,\n            /* [in] */ long column,\n            /* [retval][out] */ BSTR *description);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_columnExtentAt )( \n            IAccessibleTable * This,\n            /* [in] */ long row,\n            /* [in] */ long column,\n            /* [retval][out] */ long *nColumnsSpanned);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_columnHeader )( \n            IAccessibleTable * This,\n            /* [out] */ IAccessibleTable **accessibleTable,\n            /* [retval][out] */ long *startingRowIndex);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_columnIndex )( \n            IAccessibleTable * This,\n            /* [in] */ long cellIndex,\n            /* [retval][out] */ long *columnIndex);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_nColumns )( \n            IAccessibleTable * This,\n            /* [retval][out] */ long *columnCount);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_nRows )( \n            IAccessibleTable * This,\n            /* [retval][out] */ long *rowCount);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_nSelectedChildren )( \n            IAccessibleTable * This,\n            /* [retval][out] */ long *cellCount);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_nSelectedColumns )( \n            IAccessibleTable * This,\n            /* [retval][out] */ long *columnCount);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_nSelectedRows )( \n            IAccessibleTable * This,\n            /* [retval][out] */ long *rowCount);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_rowDescription )( \n            IAccessibleTable * This,\n            /* [in] */ long row,\n            /* [retval][out] */ BSTR *description);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_rowExtentAt )( \n            IAccessibleTable * This,\n            /* [in] */ long row,\n            /* [in] */ long column,\n            /* [retval][out] */ long *nRowsSpanned);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_rowHeader )( \n            IAccessibleTable * This,\n            /* [out] */ IAccessibleTable **accessibleTable,\n            /* [retval][out] */ long *startingColumnIndex);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_rowIndex )( \n            IAccessibleTable * This,\n            /* [in] */ long cellIndex,\n            /* [retval][out] */ long *rowIndex);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_selectedChildren )( \n            IAccessibleTable * This,\n            /* [in] */ long maxChildren,\n            /* [length_is][length_is][size_is][size_is][out] */ long **children,\n            /* [retval][out] */ long *nChildren);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_selectedColumns )( \n            IAccessibleTable * This,\n            /* [in] */ long maxColumns,\n            /* [length_is][length_is][size_is][size_is][out] */ long **columns,\n            /* [retval][out] */ long *nColumns);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_selectedRows )( \n            IAccessibleTable * This,\n            /* [in] */ long maxRows,\n            /* [length_is][length_is][size_is][size_is][out] */ long **rows,\n            /* [retval][out] */ long *nRows);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_summary )( \n            IAccessibleTable * This,\n            /* [retval][out] */ IUnknown **accessible);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_isColumnSelected )( \n            IAccessibleTable * This,\n            /* [in] */ long column,\n            /* [retval][out] */ boolean *isSelected);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_isRowSelected )( \n            IAccessibleTable * This,\n            /* [in] */ long row,\n            /* [retval][out] */ boolean *isSelected);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_isSelected )( \n            IAccessibleTable * This,\n            /* [in] */ long row,\n            /* [in] */ long column,\n            /* [retval][out] */ boolean *isSelected);\n        \n        HRESULT ( STDMETHODCALLTYPE *selectRow )( \n            IAccessibleTable * This,\n            /* [in] */ long row);\n        \n        HRESULT ( STDMETHODCALLTYPE *selectColumn )( \n            IAccessibleTable * This,\n            /* [in] */ long column);\n        \n        HRESULT ( STDMETHODCALLTYPE *unselectRow )( \n            IAccessibleTable * This,\n            /* [in] */ long row);\n        \n        HRESULT ( STDMETHODCALLTYPE *unselectColumn )( \n            IAccessibleTable * This,\n            /* [in] */ long column);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_rowColumnExtentsAtIndex )( \n            IAccessibleTable * This,\n            /* [in] */ long index,\n            /* [out] */ long *row,\n            /* [out] */ long *column,\n            /* [out] */ long *rowExtents,\n            /* [out] */ long *columnExtents,\n            /* [retval][out] */ boolean *isSelected);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_modelChange )( \n            IAccessibleTable * This,\n            /* [retval][out] */ IA2TableModelChange *modelChange);\n        \n        END_INTERFACE\n    } IAccessibleTableVtbl;\n\n    interface IAccessibleTable\n    {\n        CONST_VTBL struct IAccessibleTableVtbl *lpVtbl;\n    };\n\n    \n\n#ifdef COBJMACROS\n\n\n#define IAccessibleTable_QueryInterface(This,riid,ppvObject)\t\\\n    ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) \n\n#define IAccessibleTable_AddRef(This)\t\\\n    ( (This)->lpVtbl -> AddRef(This) ) \n\n#define IAccessibleTable_Release(This)\t\\\n    ( (This)->lpVtbl -> Release(This) ) \n\n\n#define IAccessibleTable_get_accessibleAt(This,row,column,accessible)\t\\\n    ( (This)->lpVtbl -> get_accessibleAt(This,row,column,accessible) ) \n\n#define IAccessibleTable_get_caption(This,accessible)\t\\\n    ( (This)->lpVtbl -> get_caption(This,accessible) ) \n\n#define IAccessibleTable_get_childIndex(This,rowIndex,columnIndex,cellIndex)\t\\\n    ( (This)->lpVtbl -> get_childIndex(This,rowIndex,columnIndex,cellIndex) ) \n\n#define IAccessibleTable_get_columnDescription(This,column,description)\t\\\n    ( (This)->lpVtbl -> get_columnDescription(This,column,description) ) \n\n#define IAccessibleTable_get_columnExtentAt(This,row,column,nColumnsSpanned)\t\\\n    ( (This)->lpVtbl -> get_columnExtentAt(This,row,column,nColumnsSpanned) ) \n\n#define IAccessibleTable_get_columnHeader(This,accessibleTable,startingRowIndex)\t\\\n    ( (This)->lpVtbl -> get_columnHeader(This,accessibleTable,startingRowIndex) ) \n\n#define IAccessibleTable_get_columnIndex(This,cellIndex,columnIndex)\t\\\n    ( (This)->lpVtbl -> get_columnIndex(This,cellIndex,columnIndex) ) \n\n#define IAccessibleTable_get_nColumns(This,columnCount)\t\\\n    ( (This)->lpVtbl -> get_nColumns(This,columnCount) ) \n\n#define IAccessibleTable_get_nRows(This,rowCount)\t\\\n    ( (This)->lpVtbl -> get_nRows(This,rowCount) ) \n\n#define IAccessibleTable_get_nSelectedChildren(This,cellCount)\t\\\n    ( (This)->lpVtbl -> get_nSelectedChildren(This,cellCount) ) \n\n#define IAccessibleTable_get_nSelectedColumns(This,columnCount)\t\\\n    ( (This)->lpVtbl -> get_nSelectedColumns(This,columnCount) ) \n\n#define IAccessibleTable_get_nSelectedRows(This,rowCount)\t\\\n    ( (This)->lpVtbl -> get_nSelectedRows(This,rowCount) ) \n\n#define IAccessibleTable_get_rowDescription(This,row,description)\t\\\n    ( (This)->lpVtbl -> get_rowDescription(This,row,description) ) \n\n#define IAccessibleTable_get_rowExtentAt(This,row,column,nRowsSpanned)\t\\\n    ( (This)->lpVtbl -> get_rowExtentAt(This,row,column,nRowsSpanned) ) \n\n#define IAccessibleTable_get_rowHeader(This,accessibleTable,startingColumnIndex)\t\\\n    ( (This)->lpVtbl -> get_rowHeader(This,accessibleTable,startingColumnIndex) ) \n\n#define IAccessibleTable_get_rowIndex(This,cellIndex,rowIndex)\t\\\n    ( (This)->lpVtbl -> get_rowIndex(This,cellIndex,rowIndex) ) \n\n#define IAccessibleTable_get_selectedChildren(This,maxChildren,children,nChildren)\t\\\n    ( (This)->lpVtbl -> get_selectedChildren(This,maxChildren,children,nChildren) ) \n\n#define IAccessibleTable_get_selectedColumns(This,maxColumns,columns,nColumns)\t\\\n    ( (This)->lpVtbl -> get_selectedColumns(This,maxColumns,columns,nColumns) ) \n\n#define IAccessibleTable_get_selectedRows(This,maxRows,rows,nRows)\t\\\n    ( (This)->lpVtbl -> get_selectedRows(This,maxRows,rows,nRows) ) \n\n#define IAccessibleTable_get_summary(This,accessible)\t\\\n    ( (This)->lpVtbl -> get_summary(This,accessible) ) \n\n#define IAccessibleTable_get_isColumnSelected(This,column,isSelected)\t\\\n    ( (This)->lpVtbl -> get_isColumnSelected(This,column,isSelected) ) \n\n#define IAccessibleTable_get_isRowSelected(This,row,isSelected)\t\\\n    ( (This)->lpVtbl -> get_isRowSelected(This,row,isSelected) ) \n\n#define IAccessibleTable_get_isSelected(This,row,column,isSelected)\t\\\n    ( (This)->lpVtbl -> get_isSelected(This,row,column,isSelected) ) \n\n#define IAccessibleTable_selectRow(This,row)\t\\\n    ( (This)->lpVtbl -> selectRow(This,row) ) \n\n#define IAccessibleTable_selectColumn(This,column)\t\\\n    ( (This)->lpVtbl -> selectColumn(This,column) ) \n\n#define IAccessibleTable_unselectRow(This,row)\t\\\n    ( (This)->lpVtbl -> unselectRow(This,row) ) \n\n#define IAccessibleTable_unselectColumn(This,column)\t\\\n    ( (This)->lpVtbl -> unselectColumn(This,column) ) \n\n#define IAccessibleTable_get_rowColumnExtentsAtIndex(This,index,row,column,rowExtents,columnExtents,isSelected)\t\\\n    ( (This)->lpVtbl -> get_rowColumnExtentsAtIndex(This,index,row,column,rowExtents,columnExtents,isSelected) ) \n\n#define IAccessibleTable_get_modelChange(This,modelChange)\t\\\n    ( (This)->lpVtbl -> get_modelChange(This,modelChange) ) \n\n#endif /* COBJMACROS */\n\n\n#endif \t/* C style interface */\n\n\n\n\n#endif \t/* __IAccessibleTable_INTERFACE_DEFINED__ */\n\n\n#ifndef __IAccessibleTable2_INTERFACE_DEFINED__\n#define __IAccessibleTable2_INTERFACE_DEFINED__\n\n/* interface IAccessibleTable2 */\n/* [uuid][object] */ \n\n\nEXTERN_C const IID IID_IAccessibleTable2;\n\n#if defined(__cplusplus) && !defined(CINTERFACE)\n    \n    MIDL_INTERFACE(\"6167f295-06f0-4cdd-a1fa-02e25153d869\")\n    IAccessibleTable2 : public IUnknown\n    {\n    public:\n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_cellAt( \n            /* [in] */ long row,\n            /* [in] */ long column,\n            /* [retval][out] */ IUnknown **cell) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_caption( \n            /* [retval][out] */ IUnknown **accessible) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_columnDescription( \n            /* [in] */ long column,\n            /* [retval][out] */ BSTR *description) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_nColumns( \n            /* [retval][out] */ long *columnCount) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_nRows( \n            /* [retval][out] */ long *rowCount) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_nSelectedCells( \n            /* [retval][out] */ long *cellCount) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_nSelectedColumns( \n            /* [retval][out] */ long *columnCount) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_nSelectedRows( \n            /* [retval][out] */ long *rowCount) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_rowDescription( \n            /* [in] */ long row,\n            /* [retval][out] */ BSTR *description) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_selectedCells( \n            /* [size_is][size_is][out] */ IUnknown ***cells,\n            /* [retval][out] */ long *nSelectedCells) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_selectedColumns( \n            /* [size_is][size_is][out] */ long **selectedColumns,\n            /* [retval][out] */ long *nColumns) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_selectedRows( \n            /* [size_is][size_is][out] */ long **selectedRows,\n            /* [retval][out] */ long *nRows) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_summary( \n            /* [retval][out] */ IUnknown **accessible) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_isColumnSelected( \n            /* [in] */ long column,\n            /* [retval][out] */ boolean *isSelected) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_isRowSelected( \n            /* [in] */ long row,\n            /* [retval][out] */ boolean *isSelected) = 0;\n        \n        virtual HRESULT STDMETHODCALLTYPE selectRow( \n            /* [in] */ long row) = 0;\n        \n        virtual HRESULT STDMETHODCALLTYPE selectColumn( \n            /* [in] */ long column) = 0;\n        \n        virtual HRESULT STDMETHODCALLTYPE unselectRow( \n            /* [in] */ long row) = 0;\n        \n        virtual HRESULT STDMETHODCALLTYPE unselectColumn( \n            /* [in] */ long column) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_modelChange( \n            /* [retval][out] */ IA2TableModelChange *modelChange) = 0;\n        \n    };\n    \n#else \t/* C style interface */\n\n    typedef struct IAccessibleTable2Vtbl\n    {\n        BEGIN_INTERFACE\n        \n        HRESULT ( STDMETHODCALLTYPE *QueryInterface )( \n            IAccessibleTable2 * This,\n            /* [in] */ REFIID riid,\n            /* [iid_is][out] */ \n            __RPC__deref_out  void **ppvObject);\n        \n        ULONG ( STDMETHODCALLTYPE *AddRef )( \n            IAccessibleTable2 * This);\n        \n        ULONG ( STDMETHODCALLTYPE *Release )( \n            IAccessibleTable2 * This);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_cellAt )( \n            IAccessibleTable2 * This,\n            /* [in] */ long row,\n            /* [in] */ long column,\n            /* [retval][out] */ IUnknown **cell);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_caption )( \n            IAccessibleTable2 * This,\n            /* [retval][out] */ IUnknown **accessible);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_columnDescription )( \n            IAccessibleTable2 * This,\n            /* [in] */ long column,\n            /* [retval][out] */ BSTR *description);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_nColumns )( \n            IAccessibleTable2 * This,\n            /* [retval][out] */ long *columnCount);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_nRows )( \n            IAccessibleTable2 * This,\n            /* [retval][out] */ long *rowCount);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_nSelectedCells )( \n            IAccessibleTable2 * This,\n            /* [retval][out] */ long *cellCount);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_nSelectedColumns )( \n            IAccessibleTable2 * This,\n            /* [retval][out] */ long *columnCount);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_nSelectedRows )( \n            IAccessibleTable2 * This,\n            /* [retval][out] */ long *rowCount);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_rowDescription )( \n            IAccessibleTable2 * This,\n            /* [in] */ long row,\n            /* [retval][out] */ BSTR *description);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_selectedCells )( \n            IAccessibleTable2 * This,\n            /* [size_is][size_is][out] */ IUnknown ***cells,\n            /* [retval][out] */ long *nSelectedCells);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_selectedColumns )( \n            IAccessibleTable2 * This,\n            /* [size_is][size_is][out] */ long **selectedColumns,\n            /* [retval][out] */ long *nColumns);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_selectedRows )( \n            IAccessibleTable2 * This,\n            /* [size_is][size_is][out] */ long **selectedRows,\n            /* [retval][out] */ long *nRows);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_summary )( \n            IAccessibleTable2 * This,\n            /* [retval][out] */ IUnknown **accessible);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_isColumnSelected )( \n            IAccessibleTable2 * This,\n            /* [in] */ long column,\n            /* [retval][out] */ boolean *isSelected);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_isRowSelected )( \n            IAccessibleTable2 * This,\n            /* [in] */ long row,\n            /* [retval][out] */ boolean *isSelected);\n        \n        HRESULT ( STDMETHODCALLTYPE *selectRow )( \n            IAccessibleTable2 * This,\n            /* [in] */ long row);\n        \n        HRESULT ( STDMETHODCALLTYPE *selectColumn )( \n            IAccessibleTable2 * This,\n            /* [in] */ long column);\n        \n        HRESULT ( STDMETHODCALLTYPE *unselectRow )( \n            IAccessibleTable2 * This,\n            /* [in] */ long row);\n        \n        HRESULT ( STDMETHODCALLTYPE *unselectColumn )( \n            IAccessibleTable2 * This,\n            /* [in] */ long column);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_modelChange )( \n            IAccessibleTable2 * This,\n            /* [retval][out] */ IA2TableModelChange *modelChange);\n        \n        END_INTERFACE\n    } IAccessibleTable2Vtbl;\n\n    interface IAccessibleTable2\n    {\n        CONST_VTBL struct IAccessibleTable2Vtbl *lpVtbl;\n    };\n\n    \n\n#ifdef COBJMACROS\n\n\n#define IAccessibleTable2_QueryInterface(This,riid,ppvObject)\t\\\n    ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) \n\n#define IAccessibleTable2_AddRef(This)\t\\\n    ( (This)->lpVtbl -> AddRef(This) ) \n\n#define IAccessibleTable2_Release(This)\t\\\n    ( (This)->lpVtbl -> Release(This) ) \n\n\n#define IAccessibleTable2_get_cellAt(This,row,column,cell)\t\\\n    ( (This)->lpVtbl -> get_cellAt(This,row,column,cell) ) \n\n#define IAccessibleTable2_get_caption(This,accessible)\t\\\n    ( (This)->lpVtbl -> get_caption(This,accessible) ) \n\n#define IAccessibleTable2_get_columnDescription(This,column,description)\t\\\n    ( (This)->lpVtbl -> get_columnDescription(This,column,description) ) \n\n#define IAccessibleTable2_get_nColumns(This,columnCount)\t\\\n    ( (This)->lpVtbl -> get_nColumns(This,columnCount) ) \n\n#define IAccessibleTable2_get_nRows(This,rowCount)\t\\\n    ( (This)->lpVtbl -> get_nRows(This,rowCount) ) \n\n#define IAccessibleTable2_get_nSelectedCells(This,cellCount)\t\\\n    ( (This)->lpVtbl -> get_nSelectedCells(This,cellCount) ) \n\n#define IAccessibleTable2_get_nSelectedColumns(This,columnCount)\t\\\n    ( (This)->lpVtbl -> get_nSelectedColumns(This,columnCount) ) \n\n#define IAccessibleTable2_get_nSelectedRows(This,rowCount)\t\\\n    ( (This)->lpVtbl -> get_nSelectedRows(This,rowCount) ) \n\n#define IAccessibleTable2_get_rowDescription(This,row,description)\t\\\n    ( (This)->lpVtbl -> get_rowDescription(This,row,description) ) \n\n#define IAccessibleTable2_get_selectedCells(This,cells,nSelectedCells)\t\\\n    ( (This)->lpVtbl -> get_selectedCells(This,cells,nSelectedCells) ) \n\n#define IAccessibleTable2_get_selectedColumns(This,selectedColumns,nColumns)\t\\\n    ( (This)->lpVtbl -> get_selectedColumns(This,selectedColumns,nColumns) ) \n\n#define IAccessibleTable2_get_selectedRows(This,selectedRows,nRows)\t\\\n    ( (This)->lpVtbl -> get_selectedRows(This,selectedRows,nRows) ) \n\n#define IAccessibleTable2_get_summary(This,accessible)\t\\\n    ( (This)->lpVtbl -> get_summary(This,accessible) ) \n\n#define IAccessibleTable2_get_isColumnSelected(This,column,isSelected)\t\\\n    ( (This)->lpVtbl -> get_isColumnSelected(This,column,isSelected) ) \n\n#define IAccessibleTable2_get_isRowSelected(This,row,isSelected)\t\\\n    ( (This)->lpVtbl -> get_isRowSelected(This,row,isSelected) ) \n\n#define IAccessibleTable2_selectRow(This,row)\t\\\n    ( (This)->lpVtbl -> selectRow(This,row) ) \n\n#define IAccessibleTable2_selectColumn(This,column)\t\\\n    ( (This)->lpVtbl -> selectColumn(This,column) ) \n\n#define IAccessibleTable2_unselectRow(This,row)\t\\\n    ( (This)->lpVtbl -> unselectRow(This,row) ) \n\n#define IAccessibleTable2_unselectColumn(This,column)\t\\\n    ( (This)->lpVtbl -> unselectColumn(This,column) ) \n\n#define IAccessibleTable2_get_modelChange(This,modelChange)\t\\\n    ( (This)->lpVtbl -> get_modelChange(This,modelChange) ) \n\n#endif /* COBJMACROS */\n\n\n#endif \t/* C style interface */\n\n\n\n\n#endif \t/* __IAccessibleTable2_INTERFACE_DEFINED__ */\n\n\n#ifndef __IAccessibleTableCell_INTERFACE_DEFINED__\n#define __IAccessibleTableCell_INTERFACE_DEFINED__\n\n/* interface IAccessibleTableCell */\n/* [uuid][object] */ \n\n\nEXTERN_C const IID IID_IAccessibleTableCell;\n\n#if defined(__cplusplus) && !defined(CINTERFACE)\n    \n    MIDL_INTERFACE(\"594116B1-C99F-4847-AD06-0A7A86ECE645\")\n    IAccessibleTableCell : public IUnknown\n    {\n    public:\n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_columnExtent( \n            /* [retval][out] */ long *nColumnsSpanned) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_columnHeaderCells( \n            /* [size_is][size_is][out] */ IUnknown ***cellAccessibles,\n            /* [retval][out] */ long *nColumnHeaderCells) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_columnIndex( \n            /* [retval][out] */ long *columnIndex) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_rowExtent( \n            /* [retval][out] */ long *nRowsSpanned) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_rowHeaderCells( \n            /* [size_is][size_is][out] */ IUnknown ***cellAccessibles,\n            /* [retval][out] */ long *nRowHeaderCells) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_rowIndex( \n            /* [retval][out] */ long *rowIndex) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_isSelected( \n            /* [retval][out] */ boolean *isSelected) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_rowColumnExtents( \n            /* [out] */ long *row,\n            /* [out] */ long *column,\n            /* [out] */ long *rowExtents,\n            /* [out] */ long *columnExtents,\n            /* [retval][out] */ boolean *isSelected) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_table( \n            /* [retval][out] */ IUnknown **table) = 0;\n        \n    };\n    \n#else \t/* C style interface */\n\n    typedef struct IAccessibleTableCellVtbl\n    {\n        BEGIN_INTERFACE\n        \n        HRESULT ( STDMETHODCALLTYPE *QueryInterface )( \n            IAccessibleTableCell * This,\n            /* [in] */ REFIID riid,\n            /* [iid_is][out] */ \n            __RPC__deref_out  void **ppvObject);\n        \n        ULONG ( STDMETHODCALLTYPE *AddRef )( \n            IAccessibleTableCell * This);\n        \n        ULONG ( STDMETHODCALLTYPE *Release )( \n            IAccessibleTableCell * This);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_columnExtent )( \n            IAccessibleTableCell * This,\n            /* [retval][out] */ long *nColumnsSpanned);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_columnHeaderCells )( \n            IAccessibleTableCell * This,\n            /* [size_is][size_is][out] */ IUnknown ***cellAccessibles,\n            /* [retval][out] */ long *nColumnHeaderCells);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_columnIndex )( \n            IAccessibleTableCell * This,\n            /* [retval][out] */ long *columnIndex);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_rowExtent )( \n            IAccessibleTableCell * This,\n            /* [retval][out] */ long *nRowsSpanned);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_rowHeaderCells )( \n            IAccessibleTableCell * This,\n            /* [size_is][size_is][out] */ IUnknown ***cellAccessibles,\n            /* [retval][out] */ long *nRowHeaderCells);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_rowIndex )( \n            IAccessibleTableCell * This,\n            /* [retval][out] */ long *rowIndex);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_isSelected )( \n            IAccessibleTableCell * This,\n            /* [retval][out] */ boolean *isSelected);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_rowColumnExtents )( \n            IAccessibleTableCell * This,\n            /* [out] */ long *row,\n            /* [out] */ long *column,\n            /* [out] */ long *rowExtents,\n            /* [out] */ long *columnExtents,\n            /* [retval][out] */ boolean *isSelected);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_table )( \n            IAccessibleTableCell * This,\n            /* [retval][out] */ IUnknown **table);\n        \n        END_INTERFACE\n    } IAccessibleTableCellVtbl;\n\n    interface IAccessibleTableCell\n    {\n        CONST_VTBL struct IAccessibleTableCellVtbl *lpVtbl;\n    };\n\n    \n\n#ifdef COBJMACROS\n\n\n#define IAccessibleTableCell_QueryInterface(This,riid,ppvObject)\t\\\n    ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) \n\n#define IAccessibleTableCell_AddRef(This)\t\\\n    ( (This)->lpVtbl -> AddRef(This) ) \n\n#define IAccessibleTableCell_Release(This)\t\\\n    ( (This)->lpVtbl -> Release(This) ) \n\n\n#define IAccessibleTableCell_get_columnExtent(This,nColumnsSpanned)\t\\\n    ( (This)->lpVtbl -> get_columnExtent(This,nColumnsSpanned) ) \n\n#define IAccessibleTableCell_get_columnHeaderCells(This,cellAccessibles,nColumnHeaderCells)\t\\\n    ( (This)->lpVtbl -> get_columnHeaderCells(This,cellAccessibles,nColumnHeaderCells) ) \n\n#define IAccessibleTableCell_get_columnIndex(This,columnIndex)\t\\\n    ( (This)->lpVtbl -> get_columnIndex(This,columnIndex) ) \n\n#define IAccessibleTableCell_get_rowExtent(This,nRowsSpanned)\t\\\n    ( (This)->lpVtbl -> get_rowExtent(This,nRowsSpanned) ) \n\n#define IAccessibleTableCell_get_rowHeaderCells(This,cellAccessibles,nRowHeaderCells)\t\\\n    ( (This)->lpVtbl -> get_rowHeaderCells(This,cellAccessibles,nRowHeaderCells) ) \n\n#define IAccessibleTableCell_get_rowIndex(This,rowIndex)\t\\\n    ( (This)->lpVtbl -> get_rowIndex(This,rowIndex) ) \n\n#define IAccessibleTableCell_get_isSelected(This,isSelected)\t\\\n    ( (This)->lpVtbl -> get_isSelected(This,isSelected) ) \n\n#define IAccessibleTableCell_get_rowColumnExtents(This,row,column,rowExtents,columnExtents,isSelected)\t\\\n    ( (This)->lpVtbl -> get_rowColumnExtents(This,row,column,rowExtents,columnExtents,isSelected) ) \n\n#define IAccessibleTableCell_get_table(This,table)\t\\\n    ( (This)->lpVtbl -> get_table(This,table) ) \n\n#endif /* COBJMACROS */\n\n\n#endif \t/* C style interface */\n\n\n\n\n#endif \t/* __IAccessibleTableCell_INTERFACE_DEFINED__ */\n\n\n#ifndef __IAccessibleImage_INTERFACE_DEFINED__\n#define __IAccessibleImage_INTERFACE_DEFINED__\n\n/* interface IAccessibleImage */\n/* [uuid][object] */ \n\n\nEXTERN_C const IID IID_IAccessibleImage;\n\n#if defined(__cplusplus) && !defined(CINTERFACE)\n    \n    MIDL_INTERFACE(\"FE5ABB3D-615E-4f7b-909F-5F0EDA9E8DDE\")\n    IAccessibleImage : public IUnknown\n    {\n    public:\n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_description( \n            /* [retval][out] */ BSTR *description) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_imagePosition( \n            /* [in] */ enum IA2CoordinateType coordinateType,\n            /* [out] */ long *x,\n            /* [retval][out] */ long *y) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_imageSize( \n            /* [out] */ long *height,\n            /* [retval][out] */ long *width) = 0;\n        \n    };\n    \n#else \t/* C style interface */\n\n    typedef struct IAccessibleImageVtbl\n    {\n        BEGIN_INTERFACE\n        \n        HRESULT ( STDMETHODCALLTYPE *QueryInterface )( \n            IAccessibleImage * This,\n            /* [in] */ REFIID riid,\n            /* [iid_is][out] */ \n            __RPC__deref_out  void **ppvObject);\n        \n        ULONG ( STDMETHODCALLTYPE *AddRef )( \n            IAccessibleImage * This);\n        \n        ULONG ( STDMETHODCALLTYPE *Release )( \n            IAccessibleImage * This);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_description )( \n            IAccessibleImage * This,\n            /* [retval][out] */ BSTR *description);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_imagePosition )( \n            IAccessibleImage * This,\n            /* [in] */ enum IA2CoordinateType coordinateType,\n            /* [out] */ long *x,\n            /* [retval][out] */ long *y);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_imageSize )( \n            IAccessibleImage * This,\n            /* [out] */ long *height,\n            /* [retval][out] */ long *width);\n        \n        END_INTERFACE\n    } IAccessibleImageVtbl;\n\n    interface IAccessibleImage\n    {\n        CONST_VTBL struct IAccessibleImageVtbl *lpVtbl;\n    };\n\n    \n\n#ifdef COBJMACROS\n\n\n#define IAccessibleImage_QueryInterface(This,riid,ppvObject)\t\\\n    ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) \n\n#define IAccessibleImage_AddRef(This)\t\\\n    ( (This)->lpVtbl -> AddRef(This) ) \n\n#define IAccessibleImage_Release(This)\t\\\n    ( (This)->lpVtbl -> Release(This) ) \n\n\n#define IAccessibleImage_get_description(This,description)\t\\\n    ( (This)->lpVtbl -> get_description(This,description) ) \n\n#define IAccessibleImage_get_imagePosition(This,coordinateType,x,y)\t\\\n    ( (This)->lpVtbl -> get_imagePosition(This,coordinateType,x,y) ) \n\n#define IAccessibleImage_get_imageSize(This,height,width)\t\\\n    ( (This)->lpVtbl -> get_imageSize(This,height,width) ) \n\n#endif /* COBJMACROS */\n\n\n#endif \t/* C style interface */\n\n\n\n\n#endif \t/* __IAccessibleImage_INTERFACE_DEFINED__ */\n\n\n/* interface __MIDL_itf_IAccessible2_0000_0016 */\n/* [local] */ \n\n\nenum IA2EventID\n    {\tIA2_EVENT_ACTION_CHANGED\t= 0x101,\n\tIA2_EVENT_ACTIVE_DECENDENT_CHANGED\t= ( IA2_EVENT_ACTION_CHANGED + 1 ) ,\n\tIA2_EVENT_ACTIVE_DESCENDANT_CHANGED\t= IA2_EVENT_ACTIVE_DECENDENT_CHANGED,\n\tIA2_EVENT_DOCUMENT_ATTRIBUTE_CHANGED\t= ( IA2_EVENT_ACTIVE_DESCENDANT_CHANGED + 1 ) ,\n\tIA2_EVENT_DOCUMENT_CONTENT_CHANGED\t= ( IA2_EVENT_DOCUMENT_ATTRIBUTE_CHANGED + 1 ) ,\n\tIA2_EVENT_DOCUMENT_LOAD_COMPLETE\t= ( IA2_EVENT_DOCUMENT_CONTENT_CHANGED + 1 ) ,\n\tIA2_EVENT_DOCUMENT_LOAD_STOPPED\t= ( IA2_EVENT_DOCUMENT_LOAD_COMPLETE + 1 ) ,\n\tIA2_EVENT_DOCUMENT_RELOAD\t= ( IA2_EVENT_DOCUMENT_LOAD_STOPPED + 1 ) ,\n\tIA2_EVENT_HYPERLINK_END_INDEX_CHANGED\t= ( IA2_EVENT_DOCUMENT_RELOAD + 1 ) ,\n\tIA2_EVENT_HYPERLINK_NUMBER_OF_ANCHORS_CHANGED\t= ( IA2_EVENT_HYPERLINK_END_INDEX_CHANGED + 1 ) ,\n\tIA2_EVENT_HYPERLINK_SELECTED_LINK_CHANGED\t= ( IA2_EVENT_HYPERLINK_NUMBER_OF_ANCHORS_CHANGED + 1 ) ,\n\tIA2_EVENT_HYPERTEXT_LINK_ACTIVATED\t= ( IA2_EVENT_HYPERLINK_SELECTED_LINK_CHANGED + 1 ) ,\n\tIA2_EVENT_HYPERTEXT_LINK_SELECTED\t= ( IA2_EVENT_HYPERTEXT_LINK_ACTIVATED + 1 ) ,\n\tIA2_EVENT_HYPERLINK_START_INDEX_CHANGED\t= ( IA2_EVENT_HYPERTEXT_LINK_SELECTED + 1 ) ,\n\tIA2_EVENT_HYPERTEXT_CHANGED\t= ( IA2_EVENT_HYPERLINK_START_INDEX_CHANGED + 1 ) ,\n\tIA2_EVENT_HYPERTEXT_NLINKS_CHANGED\t= ( IA2_EVENT_HYPERTEXT_CHANGED + 1 ) ,\n\tIA2_EVENT_OBJECT_ATTRIBUTE_CHANGED\t= ( IA2_EVENT_HYPERTEXT_NLINKS_CHANGED + 1 ) ,\n\tIA2_EVENT_PAGE_CHANGED\t= ( IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED + 1 ) ,\n\tIA2_EVENT_SECTION_CHANGED\t= ( IA2_EVENT_PAGE_CHANGED + 1 ) ,\n\tIA2_EVENT_TABLE_CAPTION_CHANGED\t= ( IA2_EVENT_SECTION_CHANGED + 1 ) ,\n\tIA2_EVENT_TABLE_COLUMN_DESCRIPTION_CHANGED\t= ( IA2_EVENT_TABLE_CAPTION_CHANGED + 1 ) ,\n\tIA2_EVENT_TABLE_COLUMN_HEADER_CHANGED\t= ( IA2_EVENT_TABLE_COLUMN_DESCRIPTION_CHANGED + 1 ) ,\n\tIA2_EVENT_TABLE_MODEL_CHANGED\t= ( IA2_EVENT_TABLE_COLUMN_HEADER_CHANGED + 1 ) ,\n\tIA2_EVENT_TABLE_ROW_DESCRIPTION_CHANGED\t= ( IA2_EVENT_TABLE_MODEL_CHANGED + 1 ) ,\n\tIA2_EVENT_TABLE_ROW_HEADER_CHANGED\t= ( IA2_EVENT_TABLE_ROW_DESCRIPTION_CHANGED + 1 ) ,\n\tIA2_EVENT_TABLE_SUMMARY_CHANGED\t= ( IA2_EVENT_TABLE_ROW_HEADER_CHANGED + 1 ) ,\n\tIA2_EVENT_TEXT_ATTRIBUTE_CHANGED\t= ( IA2_EVENT_TABLE_SUMMARY_CHANGED + 1 ) ,\n\tIA2_EVENT_TEXT_CARET_MOVED\t= ( IA2_EVENT_TEXT_ATTRIBUTE_CHANGED + 1 ) ,\n\tIA2_EVENT_TEXT_CHANGED\t= ( IA2_EVENT_TEXT_CARET_MOVED + 1 ) ,\n\tIA2_EVENT_TEXT_COLUMN_CHANGED\t= ( IA2_EVENT_TEXT_CHANGED + 1 ) ,\n\tIA2_EVENT_TEXT_INSERTED\t= ( IA2_EVENT_TEXT_COLUMN_CHANGED + 1 ) ,\n\tIA2_EVENT_TEXT_REMOVED\t= ( IA2_EVENT_TEXT_INSERTED + 1 ) ,\n\tIA2_EVENT_TEXT_UPDATED\t= ( IA2_EVENT_TEXT_REMOVED + 1 ) ,\n\tIA2_EVENT_TEXT_SELECTION_CHANGED\t= ( IA2_EVENT_TEXT_UPDATED + 1 ) ,\n\tIA2_EVENT_VISIBLE_DATA_CHANGED\t= ( IA2_EVENT_TEXT_SELECTION_CHANGED + 1 ) \n    } ;\n\n\nextern RPC_IF_HANDLE __MIDL_itf_IAccessible2_0000_0016_v0_0_c_ifspec;\nextern RPC_IF_HANDLE __MIDL_itf_IAccessible2_0000_0016_v0_0_s_ifspec;\n\n#ifndef __IAccessibleApplication_INTERFACE_DEFINED__\n#define __IAccessibleApplication_INTERFACE_DEFINED__\n\n/* interface IAccessibleApplication */\n/* [uuid][object] */ \n\n\nEXTERN_C const IID IID_IAccessibleApplication;\n\n#if defined(__cplusplus) && !defined(CINTERFACE)\n    \n    MIDL_INTERFACE(\"D49DED83-5B25-43F4-9B95-93B44595979E\")\n    IAccessibleApplication : public IUnknown\n    {\n    public:\n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_appName( \n            /* [retval][out] */ BSTR *name) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_appVersion( \n            /* [retval][out] */ BSTR *version) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_toolkitName( \n            /* [retval][out] */ BSTR *name) = 0;\n        \n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_toolkitVersion( \n            /* [retval][out] */ BSTR *version) = 0;\n        \n    };\n    \n#else \t/* C style interface */\n\n    typedef struct IAccessibleApplicationVtbl\n    {\n        BEGIN_INTERFACE\n        \n        HRESULT ( STDMETHODCALLTYPE *QueryInterface )( \n            IAccessibleApplication * This,\n            /* [in] */ REFIID riid,\n            /* [iid_is][out] */ \n            __RPC__deref_out  void **ppvObject);\n        \n        ULONG ( STDMETHODCALLTYPE *AddRef )( \n            IAccessibleApplication * This);\n        \n        ULONG ( STDMETHODCALLTYPE *Release )( \n            IAccessibleApplication * This);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_appName )( \n            IAccessibleApplication * This,\n            /* [retval][out] */ BSTR *name);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_appVersion )( \n            IAccessibleApplication * This,\n            /* [retval][out] */ BSTR *version);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_toolkitName )( \n            IAccessibleApplication * This,\n            /* [retval][out] */ BSTR *name);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_toolkitVersion )( \n            IAccessibleApplication * This,\n            /* [retval][out] */ BSTR *version);\n        \n        END_INTERFACE\n    } IAccessibleApplicationVtbl;\n\n    interface IAccessibleApplication\n    {\n        CONST_VTBL struct IAccessibleApplicationVtbl *lpVtbl;\n    };\n\n    \n\n#ifdef COBJMACROS\n\n\n#define IAccessibleApplication_QueryInterface(This,riid,ppvObject)\t\\\n    ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) \n\n#define IAccessibleApplication_AddRef(This)\t\\\n    ( (This)->lpVtbl -> AddRef(This) ) \n\n#define IAccessibleApplication_Release(This)\t\\\n    ( (This)->lpVtbl -> Release(This) ) \n\n\n#define IAccessibleApplication_get_appName(This,name)\t\\\n    ( (This)->lpVtbl -> get_appName(This,name) ) \n\n#define IAccessibleApplication_get_appVersion(This,version)\t\\\n    ( (This)->lpVtbl -> get_appVersion(This,version) ) \n\n#define IAccessibleApplication_get_toolkitName(This,name)\t\\\n    ( (This)->lpVtbl -> get_toolkitName(This,name) ) \n\n#define IAccessibleApplication_get_toolkitVersion(This,version)\t\\\n    ( (This)->lpVtbl -> get_toolkitVersion(This,version) ) \n\n#endif /* COBJMACROS */\n\n\n#endif \t/* C style interface */\n\n\n\n\n#endif \t/* __IAccessibleApplication_INTERFACE_DEFINED__ */\n\n\n#ifndef __IAccessibleDocument_INTERFACE_DEFINED__\n#define __IAccessibleDocument_INTERFACE_DEFINED__\n\n/* interface IAccessibleDocument */\n/* [uuid][object] */ \n\n\nEXTERN_C const IID IID_IAccessibleDocument;\n\n#if defined(__cplusplus) && !defined(CINTERFACE)\n    \n    MIDL_INTERFACE(\"C48C7FCF-4AB5-4056-AFA6-902D6E1D1149\")\n    IAccessibleDocument : public IUnknown\n    {\n    public:\n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_anchorTarget( \n            /* [retval][out] */ IUnknown **accessible) = 0;\n        \n    };\n    \n#else \t/* C style interface */\n\n    typedef struct IAccessibleDocumentVtbl\n    {\n        BEGIN_INTERFACE\n        \n        HRESULT ( STDMETHODCALLTYPE *QueryInterface )( \n            IAccessibleDocument * This,\n            /* [in] */ REFIID riid,\n            /* [iid_is][out] */ \n            __RPC__deref_out  void **ppvObject);\n        \n        ULONG ( STDMETHODCALLTYPE *AddRef )( \n            IAccessibleDocument * This);\n        \n        ULONG ( STDMETHODCALLTYPE *Release )( \n            IAccessibleDocument * This);\n        \n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_anchorTarget )( \n            IAccessibleDocument * This,\n            /* [retval][out] */ IUnknown **accessible);\n        \n        END_INTERFACE\n    } IAccessibleDocumentVtbl;\n\n    interface IAccessibleDocument\n    {\n        CONST_VTBL struct IAccessibleDocumentVtbl *lpVtbl;\n    };\n\n    \n\n#ifdef COBJMACROS\n\n\n#define IAccessibleDocument_QueryInterface(This,riid,ppvObject)\t\\\n    ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) \n\n#define IAccessibleDocument_AddRef(This)\t\\\n    ( (This)->lpVtbl -> AddRef(This) ) \n\n#define IAccessibleDocument_Release(This)\t\\\n    ( (This)->lpVtbl -> Release(This) ) \n\n\n#define IAccessibleDocument_get_anchorTarget(This,accessible)\t\\\n    ( (This)->lpVtbl -> get_anchorTarget(This,accessible) ) \n\n#endif /* COBJMACROS */\n\n\n#endif \t/* C style interface */\n\n\n\n\n#endif \t/* __IAccessibleDocument_INTERFACE_DEFINED__ */\n\n\n/* interface __MIDL_itf_IAccessible2_0000_0018 */\n/* [local] */ \n\n\n// Type Library Definitions\n\n\n\nextern RPC_IF_HANDLE __MIDL_itf_IAccessible2_0000_0018_v0_0_c_ifspec;\nextern RPC_IF_HANDLE __MIDL_itf_IAccessible2_0000_0018_v0_0_s_ifspec;\n\n\n#ifndef __IAccessible2Lib_LIBRARY_DEFINED__\n#define __IAccessible2Lib_LIBRARY_DEFINED__\n\n/* library IAccessible2Lib */\n/* [hidden][version][helpstring][uuid] */ \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nEXTERN_C const IID LIBID_IAccessible2Lib;\n#endif /* __IAccessible2Lib_LIBRARY_DEFINED__ */\n\n/* Additional Prototypes for ALL interfaces */\n\nunsigned long             __RPC_USER  BSTR_UserSize(     unsigned long *, unsigned long            , BSTR * ); \nunsigned char * __RPC_USER  BSTR_UserMarshal(  unsigned long *, unsigned char *, BSTR * ); \nunsigned char * __RPC_USER  BSTR_UserUnmarshal(unsigned long *, unsigned char *, BSTR * ); \nvoid                      __RPC_USER  BSTR_UserFree(     unsigned long *, BSTR * ); \n\nunsigned long             __RPC_USER  HWND_UserSize(     unsigned long *, unsigned long            , HWND * ); \nunsigned char * __RPC_USER  HWND_UserMarshal(  unsigned long *, unsigned char *, HWND * ); \nunsigned char * __RPC_USER  HWND_UserUnmarshal(unsigned long *, unsigned char *, HWND * ); \nvoid                      __RPC_USER  HWND_UserFree(     unsigned long *, HWND * ); \n\nunsigned long             __RPC_USER  VARIANT_UserSize(     unsigned long *, unsigned long            , VARIANT * ); \nunsigned char * __RPC_USER  VARIANT_UserMarshal(  unsigned long *, unsigned char *, VARIANT * ); \nunsigned char * __RPC_USER  VARIANT_UserUnmarshal(unsigned long *, unsigned char *, VARIANT * ); \nvoid                      __RPC_USER  VARIANT_UserFree(     unsigned long *, VARIANT * ); \n\n/* end of Additional Prototypes */\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n\n\n"
  },
  {
    "path": "Cpp/ISimpleDOMDocument.h",
    "content": "/* this ALWAYS GENERATED file contains the definitions for the interfaces */\n\n\n /* File created by MIDL compiler version 7.00.0499 */\n/* at Mon Dec 01 09:02:08 2008\n */\n/* Compiler settings for e:/builds/tinderbox/XR-Trunk/WINNT_5.2_Depend/mozilla/accessible/public/msaa/ISimpleDOMDocument.idl:\n    Oicf, W1, Zp8, env=Win32 (32b run)\n    protocol : dce , ms_ext, c_ext\n    error checks: allocation ref bounds_check enum stub_data\n    VC __declspec() decoration level:\n         __declspec(uuid()), __declspec(selectany), __declspec(novtable)\n         DECLSPEC_UUID(), MIDL_INTERFACE()\n*/\n//@@MIDL_FILE_HEADING(  )\n\n#pragma warning( disable: 4049 )  /* more than 64k source lines */\n\n\n/* verify that the <rpcndr.h> version is high enough to compile this file*/\n#ifndef __REQUIRED_RPCNDR_H_VERSION__\n#define __REQUIRED_RPCNDR_H_VERSION__ 440\n#endif\n\n\n#ifndef __ISimpleDOMDocument_h__\n#define __ISimpleDOMDocument_h__\n\n#if defined(_MSC_VER) && (_MSC_VER >= 1020)\n#pragma once\n#endif\n\n/* Forward Declarations */\n\n#ifndef __ISimpleDOMDocument_FWD_DEFINED__\n#define __ISimpleDOMDocument_FWD_DEFINED__\ntypedef interface ISimpleDOMDocument ISimpleDOMDocument;\n#endif  /* __ISimpleDOMDocument_FWD_DEFINED__ */\n\n#ifdef __cplusplus\nextern \"C\"{\n#endif\n\n\n/* interface __MIDL_itf_ISimpleDOMDocument_0000_0000 */\n/* [local] */\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////\n//\n// ISimpleDOMDocument\n//\n// @STATUS UNDER_REVIEW\n// ---------------------------------------------------------------------------------------------------=\n//\n// get_URL(out] BSTR *url)\n// ---------------------------------------------------------------------------------------------------=\n// Get the internet URL associated with this document.\n//\n// get_title([out BSTR *title\n// ---------------------------------------------------------------------------------------------------=\n// Get the document's title from the <TITLE> element\n//\n// get_mimeType([out BSTR *mimeType\n// ---------------------------------------------------------------------------------------------------=\n// Get the registered mime type, such as text/html\n//\n// get_docType([out] BSTR *docType\n// ---------------------------------------------------------------------------------------------------=\n// Get doctype associated with the <!DOCTYPE ..> element\n//\n// get_nameSpaceURIForID([in] short nameSpaceID, [out] BSTR *nameSpaceURI)\n// ---------------------------------------------------------------------------------------------------=\n// Some of the methods for ISimpleDOMNode return a nameSpaceID (-1,0,1,2,3,....)\n// This method returns the associated namespace URI for each ID.\n//\n// set_alternateViewMediaTypes([in] BSTR *commaSeparatedMediaType)\n// ---------------------------------------------------------------------------------------------------=\n// For style property retrieval on nsISimpleDOMNode elements,\n// set the additional alternate media types that properties are available for.\n// [in] BSTR *commaSeparatedMediaTypes is a comma separate list, for example \"aural, braille\".\n// The alternate media properties are requested with nsISimpleDOMNode::get_computedStyle.\n// Note: setting this value on a document will increase memory overhead, and may create a small delay.\n//\n// W3C media Types:\n// * all:        Suitable for all devices.\n// * aural:      Intended for speech synthesizers. See the section on aural style sheets for details.\n// * braille:    Intended for braille tactile feedback devices.\n// * embossed:   Intended for paged braille printers.\n// * handheld:   Intended for handheld devices - typically small screen, monochrome, limited bandwidth.\n// * print:      Intended for paged, opaque material and for documents viewed on screen in print preview mode. Please consult the section on paged media for information about formatting issues that are specific to paged media.\n// * projection: Intended for projected presentations, for example projectors or print to transparencies. Please consult the section on paged media for information about formatting issues that are specific to paged media.\n// * screen:     Intended primarily for color computer screens.\n// * tty:        intended for media using a fixed-pitch character grid, such as teletypes, terminals, or portable devices with limited display capabilities. Authors should not use pixel units with the tty media type.\n// * tv:         Intended for television-type devices - low resolution, color, limited-scrollability screens, sound\n// * See latest W3C CSS specs for complete list of media types\n//\n//\n///////////////////////////////////////////////////////////////////////////////////////////////////////\n\n\n#define DISPID_DOC_URL  ( -5904 )\n\n#define DISPID_DOC_TITLE        ( -5905 )\n\n#define DISPID_DOC_MIMETYPE     ( -5906 )\n\n#define DISPID_DOC_DOCTYPE      ( -5907 )\n\n#define DISPID_DOC_NAMESPACE    ( -5908 )\n\n#define DISPID_DOC_MEDIATYPES   ( -5909 )\n\n\n\nextern RPC_IF_HANDLE __MIDL_itf_ISimpleDOMDocument_0000_0000_v0_0_c_ifspec;\nextern RPC_IF_HANDLE __MIDL_itf_ISimpleDOMDocument_0000_0000_v0_0_s_ifspec;\n\n#ifndef __ISimpleDOMDocument_INTERFACE_DEFINED__\n#define __ISimpleDOMDocument_INTERFACE_DEFINED__\n\n/* interface ISimpleDOMDocument */\n/* [uuid][object] */\n\n\nEXTERN_C const IID IID_ISimpleDOMDocument;\n\n#if defined(__cplusplus) && !defined(CINTERFACE)\n\n    MIDL_INTERFACE(\"0D68D6D0-D93D-4d08-A30D-F00DD1F45B24\")\n    ISimpleDOMDocument : public IUnknown\n    {\n    public:\n        virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_URL(\n            /* [retval][out] */ BSTR *url) = 0;\n\n        virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_title(\n            /* [retval][out] */ BSTR *title) = 0;\n\n        virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_mimeType(\n            /* [retval][out] */ BSTR *mimeType) = 0;\n\n        virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_docType(\n            /* [retval][out] */ BSTR *docType) = 0;\n\n        virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_nameSpaceURIForID(\n            /* [in] */ short nameSpaceID,\n            /* [retval][out] */ BSTR *nameSpaceURI) = 0;\n\n        virtual /* [id][propput] */ HRESULT STDMETHODCALLTYPE put_alternateViewMediaTypes(\n            /* [in] */ BSTR *commaSeparatedMediaTypes) = 0;\n\n    };\n\n#else   /* C style interface */\n\n    typedef struct ISimpleDOMDocumentVtbl\n    {\n        BEGIN_INTERFACE\n\n        HRESULT ( STDMETHODCALLTYPE *QueryInterface )(\n            ISimpleDOMDocument * This,\n            /* [in] */ REFIID riid,\n            /* [iid_is][out] */\n            __RPC__deref_out  void **ppvObject);\n\n        ULONG ( STDMETHODCALLTYPE *AddRef )(\n            ISimpleDOMDocument * This);\n\n        ULONG ( STDMETHODCALLTYPE *Release )(\n            ISimpleDOMDocument * This);\n\n        /* [id][propget] */ HRESULT ( STDMETHODCALLTYPE *get_URL )(\n            ISimpleDOMDocument * This,\n            /* [retval][out] */ BSTR *url);\n\n        /* [id][propget] */ HRESULT ( STDMETHODCALLTYPE *get_title )(\n            ISimpleDOMDocument * This,\n            /* [retval][out] */ BSTR *title);\n\n        /* [id][propget] */ HRESULT ( STDMETHODCALLTYPE *get_mimeType )(\n            ISimpleDOMDocument * This,\n            /* [retval][out] */ BSTR *mimeType);\n\n        /* [id][propget] */ HRESULT ( STDMETHODCALLTYPE *get_docType )(\n            ISimpleDOMDocument * This,\n            /* [retval][out] */ BSTR *docType);\n\n        /* [id][propget] */ HRESULT ( STDMETHODCALLTYPE *get_nameSpaceURIForID )(\n            ISimpleDOMDocument * This,\n            /* [in] */ short nameSpaceID,\n            /* [retval][out] */ BSTR *nameSpaceURI);\n\n        /* [id][propput] */ HRESULT ( STDMETHODCALLTYPE *put_alternateViewMediaTypes )(\n            ISimpleDOMDocument * This,\n            /* [in] */ BSTR *commaSeparatedMediaTypes);\n\n        END_INTERFACE\n    } ISimpleDOMDocumentVtbl;\n\n    interface ISimpleDOMDocument\n    {\n        CONST_VTBL struct ISimpleDOMDocumentVtbl *lpVtbl;\n    };\n\n\n\n#ifdef COBJMACROS\n\n\n#define ISimpleDOMDocument_QueryInterface(This,riid,ppvObject)  \\\n    ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )\n\n#define ISimpleDOMDocument_AddRef(This) \\\n    ( (This)->lpVtbl -> AddRef(This) )\n\n#define ISimpleDOMDocument_Release(This)        \\\n    ( (This)->lpVtbl -> Release(This) )\n\n\n#define ISimpleDOMDocument_get_URL(This,url)    \\\n    ( (This)->lpVtbl -> get_URL(This,url) )\n\n#define ISimpleDOMDocument_get_title(This,title)        \\\n    ( (This)->lpVtbl -> get_title(This,title) )\n\n#define ISimpleDOMDocument_get_mimeType(This,mimeType)  \\\n    ( (This)->lpVtbl -> get_mimeType(This,mimeType) )\n\n#define ISimpleDOMDocument_get_docType(This,docType)    \\\n    ( (This)->lpVtbl -> get_docType(This,docType) )\n\n#define ISimpleDOMDocument_get_nameSpaceURIForID(This,nameSpaceID,nameSpaceURI) \\\n    ( (This)->lpVtbl -> get_nameSpaceURIForID(This,nameSpaceID,nameSpaceURI) )\n\n#define ISimpleDOMDocument_put_alternateViewMediaTypes(This,commaSeparatedMediaTypes)   \\\n    ( (This)->lpVtbl -> put_alternateViewMediaTypes(This,commaSeparatedMediaTypes) )\n\n#endif /* COBJMACROS */\n\n\n#endif  /* C style interface */\n\n\n\n\n#endif  /* __ISimpleDOMDocument_INTERFACE_DEFINED__ */\n\n\n/* Additional Prototypes for ALL interfaces */\n\nunsigned long             __RPC_USER  BSTR_UserSize(     unsigned long *, unsigned long            , BSTR * );\nunsigned char * __RPC_USER  BSTR_UserMarshal(  unsigned long *, unsigned char *, BSTR * );\nunsigned char * __RPC_USER  BSTR_UserUnmarshal(unsigned long *, unsigned char *, BSTR * );\nvoid                      __RPC_USER  BSTR_UserFree(     unsigned long *, BSTR * );\n\n/* end of Additional Prototypes */\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n\n"
  },
  {
    "path": "Cpp/ISimpleDOMNode.h",
    "content": "#ifndef __REQUIRED_RPCNDR_H_VERSION__ \n#define __REQUIRED_RPCNDR_H_VERSION__ 440 \n#endif \n\n\n#ifndef __ISimpleDOMNode_h__ \n#define __ISimpleDOMNode_h__ \n\n#if defined(_MSC_VER) && (_MSC_VER >= 1020)\n#pragma once \n#endif \n\n/* Forward Declarations */  \n\n#ifndef __ISimpleDOMNode_FWD_DEFINED__ \n#define __ISimpleDOMNode_FWD_DEFINED__ \ntypedef interface ISimpleDOMNode ISimpleDOMNode; \n#endif \t/* __ISimpleDOMNode_FWD_DEFINED__ */ \n\n\n\n#ifdef __cplusplus \nextern \"C\"{ \n#endif  \n\n\tvoid * __RPC_USER MIDL_user_allocate(size_t); \n\tvoid __RPC_USER MIDL_user_free( void * );  \n\n\t/* interface __MIDL_itf_ISimpleDOMNode_0000 */ \n\t/* [local] */  \n\n\t/////////////////////////////////////////////////////////////////////////////////////////////////////// \n\t// \n\t// ISimpleDOMNode \n\t// ---------------------------------------------------------------------------------------------------= \n\t// An interface that extends MSAA's IAccessible to provide readonly DOM node information via cross-process COM. \n\t// \n\t// @STATUS UNDER_REVIEW \n\t// \n\t// get_nodeInfo( \n\t//  /* [out] */ BSTR  *nodeName,   // For elements, this is the tag name \n\t//  /* [out] */ short  *nameSpaceID, \n\t//  /* [out] */ BSTR  *nodeValue,  \n\t//  /* [out] */ unsigned int    *numChildren);  \n\t//  /* [out] */ unsigned int    *uniqueID;  // In Win32 accessible events we generate, the target's childID matches to this \n\t//  /* [out] */ unsigned short  *nodeType, \n\t// ---------------------------------------------------------------------------------------------------= \n\t// Get the basic information about a node. \n\t// The namespace ID can be mapped to an URI using nsISimpleDOMDocument::get_nameSpaceURIForID() \n\t// \n\t// get_attributes( \n\t//  /* [in]  */ unsigned short maxAttribs, \n\t//  /* [out] */ unsigned short  *numAttribs, \n\t//  /* [out] */ BSTR  *attribNames, \n\t//  /* [out] */ short *nameSpaceID, \n\t//  /* [out] */ BSTR  *attribValues); \n\t// ---------------------------------------------------------------------------------------------------= \n\t// Returns 3 arrays - the attribute names and values, and a namespace ID for each \n\t// If the namespace ID is 0, it's the same namespace as the node's namespace \n\t// \n\t// get_attributesForNames( \n\t//  /* [in] */ unsigned short numAttribs, \n\t//  /* [in] */ BSTR   *attribNames, \n\t//  /* [in] */ short  *nameSpaceID, \n\t//  /* [out] */ BSTR  *attribValues); \n\t// ---------------------------------------------------------------------------------------------------= \n\t// Takes 2 arrays - the attribute names and namespace IDs, and returns an array of corresponding values \n\t// If the namespace ID is 0, it's the same namespace as the node's namespace \n\t// \n\t// computedStyle(   \n\t//  /* [in]  */ unsigned short maxStyleProperties, \n\t//  /* [out] */ unsigned short *numStyleProperties,  \n\t//  /* [in]  */ boolean useAlternateView,  // If TRUE, returns properites for media as set in nsIDOMDocument::set_alternateViewMediaTypes \n\t//  /* [out] */ BSTR *styleProperties,  \n\t//  /* [out] */ BSTR *styleValues); \n\t// ---------------------------------------------------------------------------------------------------= \n\t// Returns 2 arrays -- the style properties and their values \n\t//  useAlternateView=FALSE: gets properties for the default media type (usually screen) \n\t//  useAlternateView=TRUE: properties for media types set w/ nsIDOMSimpleDocument::set_alternateViewMediaTypes() \n\t// \n\t// computedStyleForProperties(   \n\t//  /* [in] */  unsigned short numStyleProperties,  \n\t//  /* [in] */  boolean useAlternateView,  // If TRUE, returns properites for media as set in nsIDOMDocument::set_alternateViewMediaTypes \n\t//  /* [in] */  BSTR *styleProperties,  \n\t//  /* [out] */ BSTR *styleValues); \n\t// ---------------------------------------------------------------------------------------------------= \n\t// Scroll the current view so that this dom node is visible. \n\t//  placeTopLeft=TRUE: scroll until the top left corner of the dom node is at the top left corner of the view. \n\t//  placeTopLeft=FALSE: scroll minimally to make the dom node visible. Don't scroll at all if already visible. \n\t// \n\t// scrollTo(  \n\t//  /* [in] */ boolean placeTopLeft);  \n\t// ---------------------------------------------------------------------------------------------------= \n\t// Returns style property values for those properties in the styleProperties [in] array \n\t// Returns 2 arrays -- the style properties and their values \n\t//  useAlternateView=FALSE: gets properties for the default media type (usually screen) \n\t//  useAlternateView=TRUE: properties for media types set w/ nsIDOMSimpleDocument::set_alternateViewMediaTypes() \n\t// \n\t// get_parentNode     (/* [out] */ ISimpleDOMNode **newNodePtr); \n\t// get_firstChild     (/* [out] */ ISimpleDOMNode **newNodePtr); \n\t// get_lastChild      (/* [out] */ ISimpleDOMNode **newNodePtr); \n\t// get_previousSibling(/* [out] */ ISimpleDOMNode **newNodePtr); \n\t// get_nextSibling    (/* [out] */ ISimpleDOMNode **newNodePtr); \n\t// get_childAt        (/* [in] */ unsigned childIndex, /* [out] */ ISimpleDOMNode **newNodePtr); \n\t// ---------------------------------------------------------------------------------------------------= \n\t// DOM navigation - get a different node. \n\t// \n\t// get_innerHTML(/* [out] */ BSTR *htmlText); \n\t// ---------------------------------------------------------------------------------------------------= \n\t// Returns HTML of this DOM node's subtree. Does not include the start and end tag for this node/element. \n\t// \n\t// \n\t// get_localInterface(/* [out] */ void **localInterface); \n\t// ---------------------------------------------------------------------------------------------------= \n\t// Only available in Gecko's process - casts to an XPCOM nsIAccessNode interface pointer \n\t// \n\t// \n\t// get_language(/* [out] */ BSTR *htmlText); \n\t// ---------------------------------------------------------------------------------------------------= \n\t// Returns the computed language for this node, or empty string if unknown. \n\t// \n\t// \n\t/////////////////////////////////////////////////////////////////////////////////////////////////////// \n\n\n#define\tDISPID_NODE_NODEINFO\t( -5900 ) \n\n#define\tDISPID_NODE_ATTRIBUTES\t( -5901 ) \n\n#define\tDISPID_NODE_ATTRIBUTESFORNAMES\t( -5902 ) \n\n#define\tDISPID_NODE_COMPSTYLE\t( -5903 ) \n\n#define\tDISPID_NODE_COMPSTYLEFORPROPS\t( -5904 ) \n\n#define\tDISPID_NODE_LANGUAGE\t( -5905 ) \n\n\n\n\textern RPC_IF_HANDLE __MIDL_itf_ISimpleDOMNode_0000_v0_0_c_ifspec; \n\textern RPC_IF_HANDLE __MIDL_itf_ISimpleDOMNode_0000_v0_0_s_ifspec; \n\n#ifndef __ISimpleDOMNode_INTERFACE_DEFINED__ \n#define __ISimpleDOMNode_INTERFACE_DEFINED__ \n\n\t/* interface ISimpleDOMNode */ \n\t/* [uuid][object] */  \n\n#define\tNODETYPE_ELEMENT\t( 1 ) \n\n#define\tNODETYPE_ATTRIBUTE\t( 2 ) \n\n#define\tNODETYPE_TEXT\t( 3 ) \n\n#define\tNODETYPE_CDATA_SECTION\t( 4 ) \n\n#define\tNODETYPE_ENTITY_REFERENCE\t( 5 ) \n\n#define\tNODETYPE_ENTITY\t( 6 ) \n\n#define\tNODETYPE_PROCESSING_INSTRUCTION\t( 7 ) \n\n#define\tNODETYPE_COMMENT\t( 8 ) \n\n#define\tNODETYPE_DOCUMENT\t( 9 ) \n\n#define\tNODETYPE_DOCUMENT_TYPE\t( 10 ) \n\n#define\tNODETYPE_DOCUMENT_FRAGMENT\t( 11 ) \n\n#define\tNODETYPE_NOTATION\t( 12 ) \n\n\n\tEXTERN_C const IID IID_ISimpleDOMNode; \n\n#if defined(__cplusplus) && !defined(CINTERFACE) \n\n\tMIDL_INTERFACE(\"1814ceeb-49e2-407f-af99-fa755a7d2607\") \nISimpleDOMNode : public IUnknown \n\t{ \n\tpublic: \n\t\tvirtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_nodeInfo(  \n\t\t\t/* [out] */ BSTR *nodeName, \n\t\t\t/* [out] */ short *nameSpaceID, \n\t\t\t/* [out] */ BSTR *nodeValue, \n\t\t\t/* [out] */ unsigned int *numChildren, \n\t\t\t/* [out] */ unsigned int *uniqueID, \n\t\t\t/* [retval][out] */ unsigned short *nodeType) = 0; \n\n\t\t\tvirtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_attributes(  \n\t\t\t/* [in] */ unsigned short maxAttribs, \n\t\t\t/* [length_is][size_is][out] */ BSTR *attribNames, \n\t\t\t/* [length_is][size_is][out] */ short *nameSpaceID, \n\t\t\t/* [length_is][size_is][out] */ BSTR *attribValues, \n\t\t\t/* [retval][out] */ unsigned short *numAttribs) = 0; \n\n\t\t\tvirtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_attributesForNames(  \n\t\t\t/* [in] */ unsigned short numAttribs, \n\t\t\t/* [length_is][size_is][in] */ BSTR *attribNames, \n\t\t\t/* [length_is][size_is][in] */ short *nameSpaceID, \n\t\t\t/* [length_is][size_is][retval][out] */ BSTR *attribValues) = 0; \n\n\t\t\tvirtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_computedStyle(  \n\t\t\t/* [in] */ unsigned short maxStyleProperties, \n\t\t\t/* [in] */ boolean useAlternateView, \n\t\t\t/* [length_is][size_is][out] */ BSTR *styleProperties, \n\t\t\t/* [length_is][size_is][out] */ BSTR *styleValues, \n\t\t\t/* [retval][out] */ unsigned short *numStyleProperties) = 0; \n\n\t\t\tvirtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_computedStyleForProperties(  \n\t\t\t/* [in] */ unsigned short numStyleProperties, \n\t\t\t/* [in] */ boolean useAlternateView, \n\t\t\t/* [length_is][size_is][in] */ BSTR *styleProperties, \n\t\t\t/* [length_is][size_is][retval][out] */ BSTR *styleValues) = 0; \n\n\t\t\tvirtual HRESULT STDMETHODCALLTYPE scrollTo(  \n\t\t\t/* [in] */ boolean placeTopLeft) = 0; \n\n\t\t\tvirtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_parentNode(  \n\t\t\t/* [retval][out] */ ISimpleDOMNode **node) = 0; \n\n\t\t\tvirtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_firstChild(  \n\t\t\t/* [retval][out] */ ISimpleDOMNode **node) = 0; \n\n\t\t\tvirtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_lastChild(  \n\t\t\t/* [retval][out] */ ISimpleDOMNode **node) = 0; \n\n\t\t\tvirtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_previousSibling(  \n\t\t\t/* [retval][out] */ ISimpleDOMNode **node) = 0; \n\n\t\t\tvirtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_nextSibling(  \n\t\t\t/* [retval][out] */ ISimpleDOMNode **node) = 0; \n\n\t\t\tvirtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_childAt(  \n\t\t\t/* [in] */ unsigned int childIndex, \n\t\t\t/* [retval][out] */ ISimpleDOMNode **node) = 0; \n\n\t\t\tvirtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_innerHTML(  \n\t\t\t/* [retval][out] */ BSTR *innerHTML) = 0; \n\n\t\t\tvirtual /* [local][propget] */ HRESULT STDMETHODCALLTYPE get_localInterface(  \n\t\t\t/* [retval][out] */ void **localInterface) = 0; \n\n\t\t\tvirtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_language(  \n\t\t\t/* [retval][out] */ BSTR *language) = 0; \n\n\t}; \n\n#else \t/* C style interface */ \n\n\ttypedef struct ISimpleDOMNodeVtbl \n\t{ \n\t\tBEGIN_INTERFACE \n\n\t\t\tHRESULT ( STDMETHODCALLTYPE *QueryInterface )(  \n\t\t\tISimpleDOMNode * This, \n\t\t\t/* [in] */ REFIID riid, \n\t\t\t/* [iid_is][out] */ void **ppvObject); \n\n\t\t\tULONG ( STDMETHODCALLTYPE *AddRef )(  \n\t\t\tISimpleDOMNode * This); \n\n\t\t\tULONG ( STDMETHODCALLTYPE *Release )(  \n\t\t\t\tISimpleDOMNode * This); \n\n\t\t\t/* [id][propget] */ HRESULT ( STDMETHODCALLTYPE *get_nodeInfo )(  \n\t\t\tISimpleDOMNode * This, \n\t\t\t\t/* [out] */ BSTR *nodeName, \n\t\t\t\t/* [out] */ short *nameSpaceID, \n\t\t\t\t/* [out] */ BSTR *nodeValue, \n\t\t\t\t/* [out] */ unsigned int *numChildren, \n\t\t\t\t/* [out] */ unsigned int *uniqueID, \n\t\t\t\t/* [retval][out] */ unsigned short *nodeType); \n\n\t\t\t\t/* [id][propget] */ HRESULT ( STDMETHODCALLTYPE *get_attributes )(  \n\t\t\t\tISimpleDOMNode * This, \n\t\t\t\t/* [in] */ unsigned short maxAttribs, \n\t\t\t\t/* [length_is][size_is][out] */ BSTR *attribNames, \n\t\t\t\t/* [length_is][size_is][out] */ short *nameSpaceID, \n\t\t\t\t/* [length_is][size_is][out] */ BSTR *attribValues, \n\t\t\t\t/* [retval][out] */ unsigned short *numAttribs); \n\n\t\t\t\t/* [id][propget] */ HRESULT ( STDMETHODCALLTYPE *get_attributesForNames )(  \n\t\t\t\tISimpleDOMNode * This, \n\t\t\t\t/* [in] */ unsigned short numAttribs, \n\t\t\t\t/* [length_is][size_is][in] */ BSTR *attribNames, \n\t\t\t\t/* [length_is][size_is][in] */ short *nameSpaceID, \n\t\t\t\t/* [length_is][size_is][retval][out] */ BSTR *attribValues); \n\n\t\t\t\t/* [id][propget] */ HRESULT ( STDMETHODCALLTYPE *get_computedStyle )(  \n\t\t\t\tISimpleDOMNode * This, \n\t\t\t\t/* [in] */ unsigned short maxStyleProperties, \n\t\t\t\t/* [in] */ boolean useAlternateView, \n\t\t\t\t/* [length_is][size_is][out] */ BSTR *styleProperties, \n\t\t\t\t/* [length_is][size_is][out] */ BSTR *styleValues, \n\t\t\t\t/* [retval][out] */ unsigned short *numStyleProperties); \n\n\t\t\t\t/* [id][propget] */ HRESULT ( STDMETHODCALLTYPE *get_computedStyleForProperties )(  \n\t\t\t\tISimpleDOMNode * This, \n\t\t\t\t/* [in] */ unsigned short numStyleProperties, \n\t\t\t\t/* [in] */ boolean useAlternateView, \n\t\t\t\t/* [length_is][size_is][in] */ BSTR *styleProperties, \n\t\t\t\t/* [length_is][size_is][retval][out] */ BSTR *styleValues); \n\n\t\t\t\tHRESULT ( STDMETHODCALLTYPE *scrollTo )(  \n\t\t\t\tISimpleDOMNode * This, \n\t\t\t\t/* [in] */ boolean placeTopLeft); \n\n\t\t\t\t/* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_parentNode )(  \n\t\t\t\tISimpleDOMNode * This, \n\t\t\t\t/* [retval][out] */ ISimpleDOMNode **node); \n\n\t\t\t\t/* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_firstChild )(  \n\t\t\t\tISimpleDOMNode * This, \n\t\t\t\t/* [retval][out] */ ISimpleDOMNode **node); \n\n\t\t\t\t/* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_lastChild )(  \n\t\t\t\tISimpleDOMNode * This, \n\t\t\t\t/* [retval][out] */ ISimpleDOMNode **node); \n\n\t\t\t\t/* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_previousSibling )(  \n\t\t\t\tISimpleDOMNode * This, \n\t\t\t\t/* [retval][out] */ ISimpleDOMNode **node); \n\n\t\t\t\t/* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_nextSibling )(  \n\t\t\t\tISimpleDOMNode * This, \n\t\t\t\t/* [retval][out] */ ISimpleDOMNode **node); \n\n\t\t\t\t/* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_childAt )(  \n\t\t\t\tISimpleDOMNode * This, \n\t\t\t\t/* [in] */ unsigned int childIndex, \n\t\t\t\t/* [retval][out] */ ISimpleDOMNode **node); \n\n\t\t\t\t/* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_innerHTML )(  \n\t\t\t\tISimpleDOMNode * This, \n\t\t\t\t/* [retval][out] */ BSTR *innerHTML); \n\n\t\t\t\t/* [local][propget] */ HRESULT ( STDMETHODCALLTYPE *get_localInterface )(  \n\t\t\t\tISimpleDOMNode * This, \n\t\t\t\t/* [retval][out] */ void **localInterface); \n\n\t\t\t\t/* [id][propget] */ HRESULT ( STDMETHODCALLTYPE *get_language )(  \n\t\t\t\tISimpleDOMNode * This, \n\t\t\t\t/* [retval][out] */ BSTR *language); \n\n\t\tEND_INTERFACE \n\t} ISimpleDOMNodeVtbl; \n\n\tinterface ISimpleDOMNode \n\t{ \n\t\tCONST_VTBL struct ISimpleDOMNodeVtbl *lpVtbl; \n\t}; \n\n\n\n#ifdef COBJMACROS \n\n\n#define ISimpleDOMNode_QueryInterface(This,riid,ppvObject)\t\\\n\t(This)->lpVtbl -> QueryInterface(This,riid,ppvObject)\n\n#define ISimpleDOMNode_AddRef(This)\t\\\n\t\t(This)->lpVtbl -> AddRef(This)\n\n#define ISimpleDOMNode_Release(This)\t\\\n\t\t(This)->lpVtbl -> Release(This)\n\n\n#define ISimpleDOMNode_get_nodeInfo(This,nodeName,nameSpaceID,nodeValue,numChildren,uniqueID,nodeType)\t\\\n\t\t(This)->lpVtbl -> get_nodeInfo(This,nodeName,nameSpaceID,nodeValue,numChildren,uniqueID,nodeType)\n\n#define ISimpleDOMNode_get_attributes(This,maxAttribs,attribNames,nameSpaceID,attribValues,numAttribs)\t\\\n\t\t(This)->lpVtbl -> get_attributes(This,maxAttribs,attribNames,nameSpaceID,attribValues,numAttribs)\n\n#define ISimpleDOMNode_get_attributesForNames(This,numAttribs,attribNames,nameSpaceID,attribValues)\t\\\n\t\t(This)->lpVtbl -> get_attributesForNames(This,numAttribs,attribNames,nameSpaceID,attribValues)\n\n#define ISimpleDOMNode_get_computedStyle(This,maxStyleProperties,useAlternateView,styleProperties,styleValues,numStyleProperties)\t\\\n\t\t(This)->lpVtbl -> get_computedStyle(This,maxStyleProperties,useAlternateView,styleProperties,styleValues,numStyleProperties)\n\n#define ISimpleDOMNode_get_computedStyleForProperties(This,numStyleProperties,useAlternateView,styleProperties,styleValues)\t\\\n\t\t(This)->lpVtbl -> get_computedStyleForProperties(This,numStyleProperties,useAlternateView,styleProperties,styleValues)\n\n#define ISimpleDOMNode_scrollTo(This,placeTopLeft)\t\\\n\t\t(This)->lpVtbl -> scrollTo(This,placeTopLeft)\n\n#define ISimpleDOMNode_get_parentNode(This,node)\t\\\n\t\t(This)->lpVtbl -> get_parentNode(This,node)\n\n#define ISimpleDOMNode_get_firstChild(This,node)\t\\\n\t\t(This)->lpVtbl -> get_firstChild(This,node)\n\n#define ISimpleDOMNode_get_lastChild(This,node)\t\\\n\t\t(This)->lpVtbl -> get_lastChild(This,node)\n\n#define ISimpleDOMNode_get_previousSibling(This,node)\t\\\n\t\t(This)->lpVtbl -> get_previousSibling(This,node)\n\n#define ISimpleDOMNode_get_nextSibling(This,node)\t\\\n\t\t(This)->lpVtbl -> get_nextSibling(This,node)\n\n#define ISimpleDOMNode_get_childAt(This,childIndex,node)\t\\\n\t\t(This)->lpVtbl -> get_childAt(This,childIndex,node)\n\n#define ISimpleDOMNode_get_innerHTML(This,innerHTML)\t\\\n\t\t(This)->lpVtbl -> get_innerHTML(This,innerHTML)\n\n#define ISimpleDOMNode_get_localInterface(This,localInterface)\t\\\n\t\t(This)->lpVtbl -> get_localInterface(This,localInterface)\n\n#define ISimpleDOMNode_get_language(This,language)\t\\\n\t\t(This)->lpVtbl -> get_language(This,language)\n\n#endif /* COBJMACROS */ \n\n\n#endif \t/* C style interface */ \n\n\n\n\t\t/* [id][propget] */ HRESULT STDMETHODCALLTYPE ISimpleDOMNode_get_nodeInfo_Proxy(  \n\t\tISimpleDOMNode * This, \n\t\t/* [out] */ BSTR *nodeName, \n\t\t/* [out] */ short *nameSpaceID, \n\t\t/* [out] */ BSTR *nodeValue, \n\t\t/* [out] */ unsigned int *numChildren, \n\t\t/* [out] */ unsigned int *uniqueID, \n\t\t/* [retval][out] */ unsigned short *nodeType); \n\n\n\t\tvoid __RPC_STUB ISimpleDOMNode_get_nodeInfo_Stub( \n\t\tIRpcStubBuffer *This, \n\t\tIRpcChannelBuffer *_pRpcChannelBuffer, \n\t\tPRPC_MESSAGE _pRpcMessage, \n\t\tDWORD *_pdwStubPhase); \n\n\n\t/* [id][propget] */ HRESULT STDMETHODCALLTYPE ISimpleDOMNode_get_attributes_Proxy(  \n\tISimpleDOMNode * This, \n\t\t/* [in] */ unsigned short maxAttribs, \n\t\t/* [length_is][size_is][out] */ BSTR *attribNames, \n\t\t/* [length_is][size_is][out] */ short *nameSpaceID, \n\t\t/* [length_is][size_is][out] */ BSTR *attribValues, \n\t\t/* [retval][out] */ unsigned short *numAttribs); \n\n\n\t\tvoid __RPC_STUB ISimpleDOMNode_get_attributes_Stub( \n\t\tIRpcStubBuffer *This, \n\t\tIRpcChannelBuffer *_pRpcChannelBuffer, \n\t\tPRPC_MESSAGE _pRpcMessage, \n\t\tDWORD *_pdwStubPhase); \n\n\n\t/* [id][propget] */ HRESULT STDMETHODCALLTYPE ISimpleDOMNode_get_attributesForNames_Proxy(  \n\tISimpleDOMNode * This, \n\t\t/* [in] */ unsigned short numAttribs, \n\t\t/* [length_is][size_is][in] */ BSTR *attribNames, \n\t\t/* [length_is][size_is][in] */ short *nameSpaceID, \n\t\t/* [length_is][size_is][retval][out] */ BSTR *attribValues); \n\n\n\t\tvoid __RPC_STUB ISimpleDOMNode_get_attributesForNames_Stub( \n\t\tIRpcStubBuffer *This, \n\t\tIRpcChannelBuffer *_pRpcChannelBuffer, \n\t\tPRPC_MESSAGE _pRpcMessage, \n\t\tDWORD *_pdwStubPhase); \n\n\n\t/* [id][propget] */ HRESULT STDMETHODCALLTYPE ISimpleDOMNode_get_computedStyle_Proxy(  \n\tISimpleDOMNode * This, \n\t\t/* [in] */ unsigned short maxStyleProperties, \n\t\t/* [in] */ boolean useAlternateView, \n\t\t/* [length_is][size_is][out] */ BSTR *styleProperties, \n\t\t/* [length_is][size_is][out] */ BSTR *styleValues, \n\t\t/* [retval][out] */ unsigned short *numStyleProperties); \n\n\n\t\tvoid __RPC_STUB ISimpleDOMNode_get_computedStyle_Stub( \n\t\tIRpcStubBuffer *This, \n\t\tIRpcChannelBuffer *_pRpcChannelBuffer, \n\t\tPRPC_MESSAGE _pRpcMessage, \n\t\tDWORD *_pdwStubPhase); \n\n\n\t/* [id][propget] */ HRESULT STDMETHODCALLTYPE ISimpleDOMNode_get_computedStyleForProperties_Proxy(  \n\tISimpleDOMNode * This, \n\t\t/* [in] */ unsigned short numStyleProperties, \n\t\t/* [in] */ boolean useAlternateView, \n\t\t/* [length_is][size_is][in] */ BSTR *styleProperties, \n\t\t/* [length_is][size_is][retval][out] */ BSTR *styleValues); \n\n\n\t\tvoid __RPC_STUB ISimpleDOMNode_get_computedStyleForProperties_Stub( \n\t\tIRpcStubBuffer *This, \n\t\tIRpcChannelBuffer *_pRpcChannelBuffer, \n\t\tPRPC_MESSAGE _pRpcMessage, \n\t\tDWORD *_pdwStubPhase); \n\n\n\tHRESULT STDMETHODCALLTYPE ISimpleDOMNode_scrollTo_Proxy(  \n\t\tISimpleDOMNode * This, \n\t\t/* [in] */ boolean placeTopLeft); \n\n\n\t\tvoid __RPC_STUB ISimpleDOMNode_scrollTo_Stub( \n\t\tIRpcStubBuffer *This, \n\t\tIRpcChannelBuffer *_pRpcChannelBuffer, \n\t\tPRPC_MESSAGE _pRpcMessage, \n\t\tDWORD *_pdwStubPhase); \n\n\n\t/* [propget] */ HRESULT STDMETHODCALLTYPE ISimpleDOMNode_get_parentNode_Proxy(  \n\tISimpleDOMNode * This, \n\t\t/* [retval][out] */ ISimpleDOMNode **node); \n\n\n\t\tvoid __RPC_STUB ISimpleDOMNode_get_parentNode_Stub( \n\t\tIRpcStubBuffer *This, \n\t\tIRpcChannelBuffer *_pRpcChannelBuffer, \n\t\tPRPC_MESSAGE _pRpcMessage, \n\t\tDWORD *_pdwStubPhase); \n\n\n\t/* [propget] */ HRESULT STDMETHODCALLTYPE ISimpleDOMNode_get_firstChild_Proxy(  \n\tISimpleDOMNode * This, \n\t\t/* [retval][out] */ ISimpleDOMNode **node); \n\n\n\t\tvoid __RPC_STUB ISimpleDOMNode_get_firstChild_Stub( \n\t\tIRpcStubBuffer *This, \n\t\tIRpcChannelBuffer *_pRpcChannelBuffer, \n\t\tPRPC_MESSAGE _pRpcMessage, \n\t\tDWORD *_pdwStubPhase); \n\n\n\t/* [propget] */ HRESULT STDMETHODCALLTYPE ISimpleDOMNode_get_lastChild_Proxy(  \n\tISimpleDOMNode * This, \n\t\t/* [retval][out] */ ISimpleDOMNode **node); \n\n\n\t\tvoid __RPC_STUB ISimpleDOMNode_get_lastChild_Stub( \n\t\tIRpcStubBuffer *This, \n\t\tIRpcChannelBuffer *_pRpcChannelBuffer, \n\t\tPRPC_MESSAGE _pRpcMessage, \n\t\tDWORD *_pdwStubPhase); \n\n\n\t/* [propget] */ HRESULT STDMETHODCALLTYPE ISimpleDOMNode_get_previousSibling_Proxy(  \n\tISimpleDOMNode * This, \n\t\t/* [retval][out] */ ISimpleDOMNode **node); \n\n\n\t\tvoid __RPC_STUB ISimpleDOMNode_get_previousSibling_Stub( \n\t\tIRpcStubBuffer *This, \n\t\tIRpcChannelBuffer *_pRpcChannelBuffer, \n\t\tPRPC_MESSAGE _pRpcMessage, \n\t\tDWORD *_pdwStubPhase); \n\n\n\t/* [propget] */ HRESULT STDMETHODCALLTYPE ISimpleDOMNode_get_nextSibling_Proxy(  \n\tISimpleDOMNode * This, \n\t\t/* [retval][out] */ ISimpleDOMNode **node); \n\n\n\t\tvoid __RPC_STUB ISimpleDOMNode_get_nextSibling_Stub( \n\t\tIRpcStubBuffer *This, \n\t\tIRpcChannelBuffer *_pRpcChannelBuffer, \n\t\tPRPC_MESSAGE _pRpcMessage, \n\t\tDWORD *_pdwStubPhase); \n\n\n\t/* [propget] */ HRESULT STDMETHODCALLTYPE ISimpleDOMNode_get_childAt_Proxy(  \n\tISimpleDOMNode * This, \n\t\t/* [in] */ unsigned int childIndex, \n\t\t/* [retval][out] */ ISimpleDOMNode **node); \n\n\n\t\tvoid __RPC_STUB ISimpleDOMNode_get_childAt_Stub( \n\t\tIRpcStubBuffer *This, \n\t\tIRpcChannelBuffer *_pRpcChannelBuffer, \n\t\tPRPC_MESSAGE _pRpcMessage, \n\t\tDWORD *_pdwStubPhase); \n\n\n\t/* [propget] */ HRESULT STDMETHODCALLTYPE ISimpleDOMNode_get_innerHTML_Proxy(  \n\tISimpleDOMNode * This, \n\t\t/* [retval][out] */ BSTR *innerHTML); \n\n\n\t\tvoid __RPC_STUB ISimpleDOMNode_get_innerHTML_Stub( \n\t\tIRpcStubBuffer *This, \n\t\tIRpcChannelBuffer *_pRpcChannelBuffer, \n\t\tPRPC_MESSAGE _pRpcMessage, \n\t\tDWORD *_pdwStubPhase); \n\n\n\t/* [local][propget] */ HRESULT STDMETHODCALLTYPE ISimpleDOMNode_get_localInterface_Proxy(  \n\tISimpleDOMNode * This, \n\t\t/* [retval][out] */ void **localInterface); \n\n\n\t\tvoid __RPC_STUB ISimpleDOMNode_get_localInterface_Stub( \n\t\tIRpcStubBuffer *This, \n\t\tIRpcChannelBuffer *_pRpcChannelBuffer, \n\t\tPRPC_MESSAGE _pRpcMessage, \n\t\tDWORD *_pdwStubPhase); \n\n\n\t/* [id][propget] */ HRESULT STDMETHODCALLTYPE ISimpleDOMNode_get_language_Proxy(  \n\tISimpleDOMNode * This, \n\t\t/* [retval][out] */ BSTR *language); \n\n\n\t\tvoid __RPC_STUB ISimpleDOMNode_get_language_Stub( \n\t\tIRpcStubBuffer *This, \n\t\tIRpcChannelBuffer *_pRpcChannelBuffer, \n\t\tPRPC_MESSAGE _pRpcMessage, \n\t\tDWORD *_pdwStubPhase); \n\n\n\n#endif \t/* __ISimpleDOMNode_INTERFACE_DEFINED__ */ \n\n\n\t/* Additional Prototypes for ALL interfaces */ \n\n\tunsigned long             __RPC_USER  BSTR_UserSize(     unsigned long *, unsigned long            , BSTR * );  \n\tunsigned char * __RPC_USER  BSTR_UserMarshal(  unsigned long *, unsigned char *, BSTR * );  \n\tunsigned char * __RPC_USER  BSTR_UserUnmarshal(unsigned long *, unsigned char *, BSTR * );  \n\tvoid                      __RPC_USER  BSTR_UserFree(     unsigned long *, BSTR * );  \n\n\t/* end of Additional Prototypes */ \n\n#ifdef __cplusplus \n} \n#endif \n\n#endif \n\n\n"
  },
  {
    "path": "Cpp/ISimpleDOMText.h",
    "content": "\n/* this ALWAYS GENERATED file contains the definitions for the interfaces */\n\n\n /* File created by MIDL compiler version 7.00.0499 */\n/* at Mon Dec 01 09:02:09 2008\n */\n/* Compiler settings for e:/builds/tinderbox/XR-Trunk/WINNT_5.2_Depend/mozilla/accessible/public/msaa/ISimpleDOMText.idl:\n    Oicf, W1, Zp8, env=Win32 (32b run)\n    protocol : dce , ms_ext, c_ext\n    error checks: allocation ref bounds_check enum stub_data\n    VC __declspec() decoration level:\n         __declspec(uuid()), __declspec(selectany), __declspec(novtable)\n         DECLSPEC_UUID(), MIDL_INTERFACE()\n*/\n//@@MIDL_FILE_HEADING(  )\n\n#pragma warning( disable: 4049 )  /* more than 64k source lines */\n\n\n/* verify that the <rpcndr.h> version is high enough to compile this file*/\n#ifndef __REQUIRED_RPCNDR_H_VERSION__\n#define __REQUIRED_RPCNDR_H_VERSION__ 440\n#endif\n\n\n#ifndef __ISimpleDOMText_h__\n#define __ISimpleDOMText_h__\n\n#if defined(_MSC_VER) && (_MSC_VER >= 1020)\n#pragma once\n#endif\n\n/* Forward Declarations */\n\n#ifndef __ISimpleDOMText_FWD_DEFINED__\n#define __ISimpleDOMText_FWD_DEFINED__\ntypedef interface ISimpleDOMText ISimpleDOMText;\n#endif  /* __ISimpleDOMText_FWD_DEFINED__ */\n\n\n#ifdef __cplusplus\nextern \"C\"{\n#endif\n\n\n/* interface __MIDL_itf_ISimpleDOMText_0000_0000 */\n/* [local] */\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////\n//\n// ISimpleDOMText\n// ---------------------------------------------------------------------------------------------------=\n// An interface that extends MSAA's IAccessible to provide important additional capabilities on text nodes\n//\n// @STATUS UNDER_REVIEW\n//\n// [propget] domText(/* out,retval */ BSTR *domText\n// ---------------------------------------------------------------------------------------------------=\n// Similar to IAccessible::get_accName, but does not strip out whitespace characters.\n// Important for retrieving the correct start/end substring indices to use with other\n// methods in ISimpleDOMText.\n//\n//\n// get_[un]clippedSubstringBounds(\n//   /* [in] */ unsigned int startIndex,\n//   /* [in] */ unsigned int endIndex,\n//   /* [out] */ int *x,\n//   /* [out] */ int *y,\n//   /* [out] */ int *width,\n//   /* [out] */ int *height);\n// ---------------------------------------------------------------------------------------------------=\n// Both methods get_clippedSubstringBounds and get_unclippedSubstringBounds return the screen pixel\n// coordinates of the given text substring. The in parameters for start and end indices refer\n// to the string returned by ISimpleDOMText::get_domText().\n//\n//\n// scrollToSubstring(\n//   /* [in] */ unsigned int startIndex,\n//   /* [in] */ unsigned int endIndex);\n// ---------------------------------------------------------------------------------------------------=\n// In scrollable views, scrolls to ensure that the specified substring is visible onscreen.\n// The in parameters for start and end indices refer to the string returned\n// by ISimpleDOMText::get_domText().\n//\n//\n// [propget] fontFamily(/* out,retval */ BSTR *fontFamily);\n// ---------------------------------------------------------------------------------------------------=\n// Return a single computed font family name, which is better than the comma delineated list\n// that is returned by the ISimpleDOMNode computed style methods for font-family.\n// In other words, return something like 'Arial' instead of 'Arial, Helvetica, Sans-serif'.\n///////////////////////////////////////////////////////////////////////////////////////////////////////\n\n\n\n\nextern RPC_IF_HANDLE __MIDL_itf_ISimpleDOMText_0000_0000_v0_0_c_ifspec;\nextern RPC_IF_HANDLE __MIDL_itf_ISimpleDOMText_0000_0000_v0_0_s_ifspec;\n\n#ifndef __ISimpleDOMText_INTERFACE_DEFINED__\n#define __ISimpleDOMText_INTERFACE_DEFINED__\n\n/* interface ISimpleDOMText */\n/* [uuid][object] */\n\n\nEXTERN_C const IID IID_ISimpleDOMText;\n\n#if defined(__cplusplus) && !defined(CINTERFACE)\n\n    MIDL_INTERFACE(\"4e747be5-2052-4265-8af0-8ecad7aad1c0\")\n    ISimpleDOMText : public IUnknown\n    {\n    public:\n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_domText(\n            /* [retval][out] */ BSTR *domText) = 0;\n\n        virtual HRESULT STDMETHODCALLTYPE get_clippedSubstringBounds(\n            /* [in] */ unsigned int startIndex,\n            /* [in] */ unsigned int endIndex,\n            /* [out] */ int *x,\n            /* [out] */ int *y,\n            /* [out] */ int *width,\n            /* [out] */ int *height) = 0;\n\n        virtual HRESULT STDMETHODCALLTYPE get_unclippedSubstringBounds(\n            /* [in] */ unsigned int startIndex,\n            /* [in] */ unsigned int endIndex,\n            /* [out] */ int *x,\n            /* [out] */ int *y,\n            /* [out] */ int *width,\n            /* [out] */ int *height) = 0;\n\n        virtual HRESULT STDMETHODCALLTYPE scrollToSubstring(\n            /* [in] */ unsigned int startIndex,\n            /* [in] */ unsigned int endIndex) = 0;\n\n        virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_fontFamily(\n            /* [retval][out] */ BSTR *fontFamily) = 0;\n\n    };\n\n#else   /* C style interface */\n\n    typedef struct ISimpleDOMTextVtbl\n    {\n        BEGIN_INTERFACE\n\n        HRESULT ( STDMETHODCALLTYPE *QueryInterface )(\n            ISimpleDOMText * This,\n            /* [in] */ REFIID riid,\n            /* [iid_is][out] */\n            __RPC__deref_out  void **ppvObject);\n\n        ULONG ( STDMETHODCALLTYPE *AddRef )(\n            ISimpleDOMText * This);\n\n        ULONG ( STDMETHODCALLTYPE *Release )(\n            ISimpleDOMText * This);\n\n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_domText )(\n            ISimpleDOMText * This,\n            /* [retval][out] */ BSTR *domText);\n\n        HRESULT ( STDMETHODCALLTYPE *get_clippedSubstringBounds )(\n            ISimpleDOMText * This,\n            /* [in] */ unsigned int startIndex,\n            /* [in] */ unsigned int endIndex,\n            /* [out] */ int *x,\n            /* [out] */ int *y,\n            /* [out] */ int *width,\n            /* [out] */ int *height);\n\n        HRESULT ( STDMETHODCALLTYPE *get_unclippedSubstringBounds )(\n            ISimpleDOMText * This,\n            /* [in] */ unsigned int startIndex,\n            /* [in] */ unsigned int endIndex,\n            /* [out] */ int *x,\n            /* [out] */ int *y,\n            /* [out] */ int *width,\n            /* [out] */ int *height);\n\n        HRESULT ( STDMETHODCALLTYPE *scrollToSubstring )(\n            ISimpleDOMText * This,\n            /* [in] */ unsigned int startIndex,\n            /* [in] */ unsigned int endIndex);\n\n        /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_fontFamily )(\n            ISimpleDOMText * This,\n            /* [retval][out] */ BSTR *fontFamily);\n\n        END_INTERFACE\n    } ISimpleDOMTextVtbl;\n\n    interface ISimpleDOMText\n    {\n        CONST_VTBL struct ISimpleDOMTextVtbl *lpVtbl;\n    };\n\n\n\n#ifdef COBJMACROS\n\n\n#define ISimpleDOMText_QueryInterface(This,riid,ppvObject)      \\\n    ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )\n\n#define ISimpleDOMText_AddRef(This)     \\\n    ( (This)->lpVtbl -> AddRef(This) )\n\n#define ISimpleDOMText_Release(This)    \\\n    ( (This)->lpVtbl -> Release(This) )\n\n\n#define ISimpleDOMText_get_domText(This,domText)        \\\n    ( (This)->lpVtbl -> get_domText(This,domText) )\n\n#define ISimpleDOMText_get_clippedSubstringBounds(This,startIndex,endIndex,x,y,width,height)    \\\n    ( (This)->lpVtbl -> get_clippedSubstringBounds(This,startIndex,endIndex,x,y,width,height) )\n\n#define ISimpleDOMText_get_unclippedSubstringBounds(This,startIndex,endIndex,x,y,width,height)  \\\n    ( (This)->lpVtbl -> get_unclippedSubstringBounds(This,startIndex,endIndex,x,y,width,height) )\n\n#define ISimpleDOMText_scrollToSubstring(This,startIndex,endIndex)      \\\n    ( (This)->lpVtbl -> scrollToSubstring(This,startIndex,endIndex) )\n\n#define ISimpleDOMText_get_fontFamily(This,fontFamily)  \\\n    ( (This)->lpVtbl -> get_fontFamily(This,fontFamily) )\n\n#endif /* COBJMACROS */\n\n\n#endif  /* C style interface */\n\n\n\n\n#endif  /* __ISimpleDOMText_INTERFACE_DEFINED__ */\n\n\n/* Additional Prototypes for ALL interfaces */\n\nunsigned long             __RPC_USER  BSTR_UserSize(     unsigned long *, unsigned long            , BSTR * );\nunsigned char * __RPC_USER  BSTR_UserMarshal(  unsigned long *, unsigned char *, BSTR * );\nunsigned char * __RPC_USER  BSTR_UserUnmarshal(unsigned long *, unsigned char *, BSTR * );\nvoid                      __RPC_USER  BSTR_UserFree(     unsigned long *, BSTR * );\n\n/* end of Additional Prototypes */\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "Cpp/JAB.h",
    "content": "#include \"stdafx.h\"\n#include \"cpp.h\"\n\n//_________________________________________________________________\n\nnamespace jab {\n\n\t//From jni.h\n\n\ttypedef long jint;\n\ttypedef __int64 jlong;\n\ttypedef signed char jbyte;\n\ttypedef unsigned char   jboolean;\n\ttypedef unsigned short  jchar;\n\ttypedef short           jshort;\n\ttypedef float           jfloat;\n\ttypedef double          jdouble;\n\ttypedef int            jsize;\n\n\t//_________________________________________________________________\n\n\t//From AccessBridgePackages.h\n\n\ttypedef __int64 JObject;\n\n#define MAX_BUFFER_SIZE   10240\n#define MAX_STRING_SIZE   1024\n#define SHORT_STRING_SIZE   256\n\n\t// object types\n\ttypedef JObject AccessibleContext;\n\ttypedef JObject AccessibleText;\n\ttypedef JObject AccessibleValue;\n\ttypedef JObject AccessibleSelection;\n\n\n\t/**\n\t ******************************************************\n\t *  optional AccessibleContext interfaces\n\t *\n\t * This version of the bridge reuses the accessibleValue\n\t * field in the AccessibleContextInfo struct to represent\n\t * additional optional interfaces that are supported by\n\t * the Java AccessibleContext.  This is backwardly compatable\n\t * because the old accessibleValue was set to the BOOL\n\t * value TRUE (i.e., 1) if the AccessibleValue interface is\n\t * supported.\n\t ******************************************************\n\t */\n\n#define cAccessibleValueInterface (jlong) 1             // 1 << 1 (TRUE)\n\t //#define cAccessibleActionInterface (jlong) 2            // 1 << 2\n\t //#define cAccessibleComponentInterface (jlong) 4         // 1 << 3\n\t //#define cAccessibleSelectionInterface (jlong) 8         // 1 << 4\n\t //#define cAccessibleTableInterface (jlong) 16            // 1 << 5\n\t //#define cAccessibleTextInterface (jlong) 32             // 1 << 6\n\t //#define cAccessibleHypertextInterface (jlong) 64        // 1 << 7\n\n\n\ttypedef struct AccessibleContextInfoTag {\n\t\twchar_t name[MAX_STRING_SIZE];          // the AccessibleName of the object\n\t\twchar_t description[MAX_STRING_SIZE];   // the AccessibleDescription of the object\n\n\t\twchar_t role[SHORT_STRING_SIZE];        // localized AccesibleRole string\n\t\twchar_t role_en_US[SHORT_STRING_SIZE];  // AccesibleRole string in the en_US locale\n\t\twchar_t states[SHORT_STRING_SIZE];      // localized AccesibleStateSet string (comma separated)\n\t\twchar_t states_en_US[SHORT_STRING_SIZE]; // AccesibleStateSet string in the en_US locale (comma separated)\n\n\t\tint indexInParent;                     // index of object in parent\n\t\tint childrenCount;                     // # of children, if any\n\n\t\tint x;                                 // screen coords in pixels\n\t\tint y;                                 // \"\n\t\tint width;                             // pixel width of object\n\t\tint height;                            // pixel height of object\n\n\t\tBOOL accessibleComponent;               // flags for various additional\n\t\tBOOL accessibleAction;                  //  Java Accessibility interfaces\n\t\tBOOL accessibleSelection;               //  FALSE if this object doesn't\n\t\tBOOL accessibleText;                    //  implement the additional interface\n\t\t//  in question\n\n\t\tBOOL accessibleInterfaces;              // bitfield containing additional interface flags\n\n\t} AccessibleContextInfo;\n\n\t// AccessibleText packages\n\ttypedef struct AccessibleTextInfoTag {\n\t\tint charCount;                 // # of characters in this text object\n\t\tint caretIndex;                // index of caret\n\t\tint indexAtPoint;              // index at the passsed in point\n\t} AccessibleTextInfo;\n\n#define MAX_ACTION_INFO 256\n#define MAX_ACTIONS_TO_DO 32\n\n\t// an action assocated with a component\n\ttypedef struct AccessibleActionInfoTag {\n\t\twchar_t name[SHORT_STRING_SIZE];        // action name\n\t} AccessibleActionInfo;\n\n\t// all of the actions associated with a component\n\ttypedef struct AccessibleActionsTag {\n\t\tint actionsCount;              // number of actions\n\t\tAccessibleActionInfo actionInfo[MAX_ACTION_INFO];       // the action information\n\t} AccessibleActions;\n\n\t// list of AccessibleActions to do\n\ttypedef struct AccessibleActionsToDoTag {\n\t\tint actionsCount;                              // number of actions to do\n\t\tAccessibleActionInfo actions[MAX_ACTIONS_TO_DO];// the accessible actions to do\n\t} AccessibleActionsToDo;\n\n#define MAX_VISIBLE_CHILDREN 256\n\n\t// visible children information\n\ttypedef struct VisibleChildenInfoTag {\n\t\tint returnedChildrenCount; // number of children returned\n\t\tAccessibleContext children[MAX_VISIBLE_CHILDREN]; // the visible children\n\t} VisibleChildrenInfo;\n\n\n\t//_________________________________________________________________\n\n\t//From AccessBridgeCalls.h\n\n\t//Loads Java Access Bridge (JAB) dll and functions. Then you can call the functions.\n\tstruct _JApi {\n\n\t\tvoid(*Windows_run) ();\n\n\t\t//BOOL (*isJavaWindow) (HWND window); //very slow, and need to load dll etc\n\n\t\tvoid(*releaseJavaObject) (long vmID, JObject object);\n\n\t\tBOOL(*isSameObject) (long vmID, JObject obj1, JObject obj2);\n\n\t\tBOOL(*getAccessibleContextFromHWND) (HWND window, long* vmID, AccessibleContext* ac);\n\n\t\tHWND(*getHWNDFromAccessibleContext) (long vmID, AccessibleContext ac);\n\n\t\tBOOL(*getAccessibleContextAt) (long vmID, AccessibleContext acParent, jint x, jint y, AccessibleContext* ac);\n\n\t\tBOOL(*getAccessibleContextWithFocus) (HWND window, long* vmID, AccessibleContext* ac);\n\n\t\tBOOL(*getAccessibleContextInfo) (long vmID, AccessibleContext ac, AccessibleContextInfo* info);\n\n\t\tAccessibleContext(*getAccessibleChildFromContext) (long vmID, AccessibleContext ac, jint i);\n\n\t\tAccessibleContext(*getAccessibleParentFromContext) (long vmID, AccessibleContext ac);\n\n\t\tBOOL(*getAccessibleActions)(long vmID, AccessibleContext accessibleContext, AccessibleActions* actions);\n\n\t\tBOOL(*doAccessibleActions)(long vmID, AccessibleContext accessibleContext, AccessibleActionsToDo* actionsToDo, jint* failure);\n\n\t\tBOOL(*getAccessibleTextInfo) (long vmID, AccessibleText at, AccessibleTextInfo* textInfo, jint x, jint y);\n\n\t\tBOOL(*getAccessibleTextRange) (long vmID, AccessibleText at, jint start, jint end, wchar_t* text, short len);\n\n\t\tBOOL(*getCurrentAccessibleValueFromContext) (long vmID, AccessibleValue av, wchar_t* value, short len);\n\n\t\tvoid(*addAccessibleSelectionFromContext) (long vmID, AccessibleSelection as, int i);\n\n\t\tvoid(*clearAccessibleSelectionFromContext) (long vmID, AccessibleSelection as);\n\n\t\tvoid(*removeAccessibleSelectionFromContext) (long vmID, AccessibleSelection as, int i);\n\n\t\tBOOL(*setTextContents) (const long vmID, const AccessibleContext ac, const wchar_t* text);\n\n\t\tBOOL(*getVirtualAccessibleName) (const long vmID, const AccessibleContext accessibleContext, wchar_t* name, int len);\n\n\t\tBOOL(*requestFocus) (const long vmID, const AccessibleContext accessibleContext);\n\n\t\t//AccessibleContext(*getTopLevelObject) (const long vmID, const AccessibleContext ac);\n\n\t\t//AccessibleContext(*getParentWithRole) (const long vmID, const AccessibleContext ac, const wchar_t* role);\n\n\t\t//AccessibleContext(*getParentWithRoleElseRoot) (const long vmID, const AccessibleContext ac, const wchar_t* role);\n\n\t\t//AccessibleContext(*getActiveDescendent) (const long vmID, const AccessibleContext ac); //does not work\n\n\t\t//int (*getVisibleChildrenCount) (const long vmID, const AccessibleContext accessibleContext);\n\n\t\t//BOOL(*getVisibleChildren) (const long vmID, const AccessibleContext accessibleContext, const int startIndex, VisibleChildrenInfo* children); //slow\n\n\tprivate:\n\t\tHMODULE _hmodule;\n\t\tbool _isLoaded;\n\tpublic:\n\n\t\t_JApi() noexcept { ZEROTHIS; }\n\n\t\t~_JApi() {\n\t\t\t_isLoaded = false;\n\t\t\tif (_hmodule) {\n\t\t\t\tFreeLibrary(_hmodule);\n\t\t\t\t_hmodule = 0;\n\t\t\t}\n\t\t}\n\n\t\tbool IsLoaded() { return _isLoaded; }\n\n\t\tvoid Load() {\n\t\t\tif (_isLoaded) return;\n\t\t\tif (osVer.is32BitOS()) return; //don't support 32-bit OS. Then JObject etc is 32-bit. Too much work for the dying platform.\n\t\t\tSTR dllName = L\"WindowsAccessBridge-\"\n#ifdef _WIN64\n\t\t\t\tL\"64.dll\";\n#else\n\t\t\t\tL\"32.dll\";\n#endif\n\t\t\tHMODULE hm = LoadLibraryW(dllName);\n\t\t\tif (hm == 0) {\n\t\t\t\tPRINTF(L\"failed LoadLibrary: %s\", dllName);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t_hmodule = hm;\n#define JF(f) *(FARPROC*)&f=GetProcAddress(hm, #f)\n\t\t\tJF(Windows_run);\n\t\t\tJF(releaseJavaObject);\n\t\t\tJF(isSameObject);\n\t\t\tJF(getAccessibleContextFromHWND);\n\t\t\tJF(getHWNDFromAccessibleContext);\n\t\t\tJF(getAccessibleContextAt);\n\t\t\tJF(getAccessibleContextWithFocus);\n\t\t\tJF(getAccessibleContextInfo);\n\t\t\tJF(getAccessibleChildFromContext);\n\t\t\tJF(getAccessibleParentFromContext);\n\t\t\tJF(getAccessibleActions);\n\t\t\tJF(doAccessibleActions);\n\t\t\tJF(getAccessibleTextInfo);\n\t\t\tJF(getAccessibleTextRange);\n\t\t\tJF(getCurrentAccessibleValueFromContext);\n\t\t\tJF(addAccessibleSelectionFromContext);\n\t\t\tJF(clearAccessibleSelectionFromContext);\n\t\t\tJF(removeAccessibleSelectionFromContext);\n\t\t\tJF(setTextContents);\n\t\t\tJF(getVirtualAccessibleName);\n\t\t\tJF(requestFocus);\n\t\t\t//JF(getTopLevelObject);\n\t\t\t//JF(getParentWithRole);\n\t\t\t//JF(getParentWithRoleElseRoot);\n\t\t\t//JF(getActiveDescendent);\n\t\t\t//JF(getVisibleChildrenCount);\n\t\t\t//JF(getVisibleChildren);\n\n\t\t\tfor (FARPROC* p = (FARPROC*)&Windows_run, *pe = (FARPROC*)&_hmodule; p < pe; p++) if (*p == null) {\n\t\t\t\tPRINTS(L\"Some JAB functions not found\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t_isLoaded = true;\n\t\t}\n\n\t};\n\n} //namespace jab\n"
  },
  {
    "path": "Cpp/MemoryPool.cpp",
    "content": "//https://github.com/DevShiftTeam/AppShift-MemoryPool\n//Don't need modifications. Just set to not use precompiled headers for this file.\n//Speed: in Release config almost as fast as using stack memory or a most primitive thread-local memory block.\n\n/**\n * AppShift Memory Pool v2.0.0\n *\n * Copyright 2020-present Sapir Shemer, DevShift (devshift.biz)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *  http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * @author Sapir Shemer\n */\n\n#include \"MemoryPool.h\"\n#include <iostream>\n\nAppShift::Memory::MemoryPool::MemoryPool(size_t block_size)\n{\n\t// Add first block to memory pool\n\tthis->firstBlock = this->currentBlock = nullptr;\n\tthis->defaultBlockSize = block_size;\n\tthis->currentScope = nullptr;\n\tthis->createMemoryBlock(block_size);\n}\n\nAppShift::Memory::MemoryPool::~MemoryPool() {\n    SMemoryBlockHeader* block_iterator = firstBlock;\n\n    while (block_iterator != nullptr) {\n        SMemoryBlockHeader* next_iterator = block_iterator->next;\n        std::free(block_iterator);\n        block_iterator = next_iterator;\n    }\n}\n\nvoid AppShift::Memory::MemoryPool::createMemoryBlock(size_t block_size)\n{\n\t// Create the block\n\tSMemoryBlockHeader* block = reinterpret_cast<SMemoryBlockHeader*>(std::malloc(sizeof(SMemoryBlockHeader) + block_size));\n\tif (block == NULL) throw EMemoryErrors::CANNOT_CREATE_BLOCK;\n\n\t// Initalize block data\n\tblock->blockSize = block_size;\n\tblock->offset = 0;\n\tblock->numberOfAllocated = 0;\n\tblock->numberOfDeleted = 0;\n\n\tif (this->firstBlock != nullptr) {\n\t\tblock->next = nullptr;\n\t\tblock->prev = this->currentBlock;\n\t\tthis->currentBlock->next = block;\n\t\tthis->currentBlock = block;\n\t}\n\telse {\n\t\tblock->next = block->prev = nullptr;\n\t\tthis->firstBlock = block;\n\t\tthis->currentBlock = block;\n\t}\n}\n\nvoid* AppShift::Memory::MemoryPool::allocate(size_t size)\n{\n\t// If there is enough space in current block then use the current block\n\tif (size + sizeof(SMemoryUnitHeader) < this->currentBlock->blockSize - this->currentBlock->offset);\n\t// Create new block if not enough space\n\telse if (size + sizeof(SMemoryUnitHeader) >= this->defaultBlockSize) this->createMemoryBlock(size + sizeof(SMemoryUnitHeader));\n\telse this->createMemoryBlock(this->defaultBlockSize);\n\n\t// Add unit\n\tSMemoryUnitHeader* unit = reinterpret_cast<SMemoryUnitHeader*>(reinterpret_cast<char*>(this->currentBlock) + sizeof(SMemoryBlockHeader) + this->currentBlock->offset);\n\tunit->length = size;\n\tunit->container = this->currentBlock;\n\tthis->currentBlock->numberOfAllocated++;\n\tthis->currentBlock->offset += sizeof(SMemoryUnitHeader) + size;\n\n\treturn reinterpret_cast<char*>(unit) + sizeof(SMemoryUnitHeader);\n}\n\nvoid* AppShift::Memory::MemoryPool::reallocate(void* unit_pointer_start, size_t new_size)\n{\n\tif (unit_pointer_start == NULL) return nullptr;\n\n\t// Find unit\n\tSMemoryUnitHeader* unit = reinterpret_cast<SMemoryUnitHeader*>(reinterpret_cast<char*>(unit_pointer_start) - sizeof(SMemoryUnitHeader));\n\tSMemoryBlockHeader* block = unit->container;\n\n\t// If last in block && enough space in block, then reset length\n\tif (reinterpret_cast<char*>(block) + sizeof(SMemoryBlockHeader) + block->offset == reinterpret_cast<char*>(unit) + sizeof(SMemoryUnitHeader) + unit->length\n\t\t&& block->blockSize > block->offset + new_size - unit->length) {\n\t\tblock->offset += new_size - unit->length;\n\t\tunit->length = new_size;\n\n\t\treturn unit_pointer_start;\n\t}\n\n\t// Allocate new and free previous\n\tvoid* temp_point = this->allocate(new_size);\n\tstd::memcpy(temp_point, unit_pointer_start, unit->length);\n\tthis->free(unit_pointer_start);\n\n\treturn temp_point;\n}\n\nvoid AppShift::Memory::MemoryPool::free(void* unit_pointer_start)\n{\n\tif (unit_pointer_start == nullptr) return;\n\n\t// Find unit\n\tSMemoryUnitHeader* unit = reinterpret_cast<SMemoryUnitHeader*>(reinterpret_cast<char*>(unit_pointer_start) - sizeof(SMemoryUnitHeader));\n\tSMemoryBlockHeader* block = unit->container;\n\n\t// If last in block, then reset offset\n\tif (reinterpret_cast<char*>(block) + sizeof(SMemoryBlockHeader) + block->offset == reinterpret_cast<char*>(unit) + sizeof(SMemoryUnitHeader) + unit->length) {\n\t\tblock->offset -= sizeof(SMemoryUnitHeader) + unit->length;\n\t\tblock->numberOfAllocated--;\n\t}\n\telse block->numberOfDeleted++;\n\n\t// If block offset is 0 remove block if not the only one left\n\tif (this->currentBlock != this->firstBlock && (block->offset == 0 || block->numberOfAllocated == block->numberOfDeleted)) {\n\t\tif (block == this->firstBlock) {\n\t\t\tthis->firstBlock = block->next;\n\t\t\tthis->firstBlock->prev = nullptr;\n\t\t}\n\t\telse if (block == this->currentBlock) {\n\t\t\tthis->currentBlock = block->prev;\n\t\t\tthis->currentBlock->next = nullptr;\n\t\t}\n\t\telse {\n\t\t\tblock->prev->next = block->next;\n\t\t\tblock->next->prev = block->prev;\n\t\t}\n\t\tstd::free(block);\n\t}\n}\n\n\nvoid AppShift::Memory::MemoryPool::dumpPoolData()\n{\n\tSMemoryBlockHeader* block = this->firstBlock;\n\tSMemoryUnitHeader* unit;\n\n\tsize_t current_unit_offset;\n\tsize_t block_counter = 1;\n\tsize_t unit_counter = 1;\n\n\twhile (block != nullptr) {\n\t\t// Dump block data\n\t\tstd::cout << \"Block \" << block_counter << \": \" << std::endl;\n\t\tstd::cout << \"\\t\" << \"Used: \" << (float)(block->offset) / (float)(block->blockSize) * 100 << \"% \" << \"(\" << block->offset << \"/\" << block->blockSize << \")\" << std::endl;\n\n\t\tif (block->offset == 0) {\n\t\t\tblock = block->next;\n\t\t\tblock_counter++;\n\t\t\tcontinue;\n\t\t}\n\n\t\tstd::cout << \"\\t\" << \"Units: ========================\" << std::endl;\n\t\tcurrent_unit_offset = 0;\n\t\tunit_counter = 1;\n\t\twhile (current_unit_offset < block->offset) {\n\t\t\tunit = reinterpret_cast<SMemoryUnitHeader*>(reinterpret_cast<char*>(block + 1) + current_unit_offset);\n\t\t\tstd::cout << \"\\t\\t\" << \"Unit \" << unit_counter << \": \" << unit->length + sizeof(SMemoryUnitHeader) << std::endl;\n\t\t\tcurrent_unit_offset += sizeof(SMemoryUnitHeader) + unit->length;\n\t\t\tunit_counter++;\n\t\t}\n\n\t\tstd::cout << \"\\t\" << \"===============================\" << std::endl;\n\n\t\tblock = block->next;\n\t\tblock_counter++;\n\t}\n}\n\nvoid AppShift::Memory::MemoryPool::startScope()\n{\n\t// Create new scope, on top of previous if exists\n\tif (this->currentScope == nullptr) {\n\t\tthis->currentScope = new (this) SMemoryScopeHeader;\n\t\tthis->currentScope->prevScope = nullptr;\n\t}\n\telse {\n\t\tSMemoryScopeHeader* new_scope = new (this) SMemoryScopeHeader;\n\t\tnew_scope->prevScope = this->currentScope;\n\t\tthis->currentScope = new_scope;\n\t}\n\n\t// Simply load the current offset & block to return to when scope ends\n\tthis->currentScope->scopeOffset = this->currentBlock->offset - sizeof(SMemoryScopeHeader) - sizeof(SMemoryUnitHeader);\n\tthis->currentScope->firstScopeBlock = this->currentBlock;\n}\n\nvoid AppShift::Memory::MemoryPool::endScope()\n{\n\t// Free all blocks until the start of scope\n\twhile (this->currentBlock != this->currentScope->firstScopeBlock) {\n\t\tthis->currentBlock = this->currentBlock->prev;\n\t\tstd::free(this->currentBlock->next);\n\t\tthis->currentBlock->next = nullptr;\n\t}\n\n\tthis->currentBlock->offset = this->currentScope->scopeOffset;\n}\n\nvoid* operator new(size_t size, AppShift::Memory::MemoryPool* mp) {\n\treturn mp->allocate(size);\n}\n\nvoid* operator new[](size_t size, AppShift::Memory::MemoryPool* mp) {\n\treturn mp->allocate(size);\n}\n"
  },
  {
    "path": "Cpp/MemoryPool.h",
    "content": "/**\n * AppShift Memory Pool v2.0.0\n *\n * Copyright 2020-present Sapir Shemer, DevShift (devshift.biz)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *  http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * @author Sapir Shemer\n */\n#pragma once\n#define MEMORYPOOL_DEFAULT_BLOCK_SIZE 1024 * 1024\n\n#include <stdlib.h>\n#include <cstring>\n#include <cstddef>\n#include <memory>\n\nnamespace AppShift::Memory {\n\t// Simple error collection for memory pool\n    enum class EMemoryErrors {\n        CANNOT_CREATE_MEMORY_POOL,\n        CANNOT_CREATE_BLOCK,\n        OUT_OF_POOL,\n        EXCEEDS_MAX_SIZE,\n        CANNOT_CREATE_BLOCK_CHAIN\n    };\n\n    // Header for a single memory block\n    struct SMemoryBlockHeader {\n        // Block data\n        size_t blockSize;\n        size_t offset;\n\n        // Movement to other blocks\n        SMemoryBlockHeader* next;\n        SMemoryBlockHeader* prev;\n\n        // Garbage management data\n        size_t numberOfAllocated;\n        size_t numberOfDeleted;\n    };\n\n    // Header of a memory unit in the pool holding important metadata\n    struct SMemoryUnitHeader {\n        size_t length;\n        SMemoryBlockHeader* container;\n    };\n\n    // Header for a scope in memory\n    struct SMemoryScopeHeader {\n        size_t scopeOffset;\n        SMemoryBlockHeader* firstScopeBlock;\n        SMemoryScopeHeader* prevScope;\n    };\n\n\tclass MemoryPool {\n\tpublic:\n\t\t/**\n\t\t * Creates a memory pool structure and initializes it\n\t\t * \n\t\t * @param size_t block_size Defines the default size of a block in the pool, by default uses MEMORYPOOL_DEFAULT_BLOCK_SIZE\n\t\t */\n\t\tMemoryPool(size_t block_size = MEMORYPOOL_DEFAULT_BLOCK_SIZE);\n\t\t// Destructor\n\t\t~MemoryPool();\n\n        // Data about the memory pool blocks\n        SMemoryBlockHeader* firstBlock;\n        SMemoryBlockHeader* currentBlock;\n        size_t defaultBlockSize;\n\n        // Data about memory scopes\n        SMemoryScopeHeader* currentScope;\n\n\t\t/**\n\t\t * Create a new standalone memory block unattached to any memory pool\n\t\t * \n\t\t * @param size_t block_size Defines the default size of a block in the pool, by default uses MEMORYPOOL_DEFAULT_BLOCK_SIZE\n\t\t * \n\t\t * @returns SMemoryBlockHeader* Pointer to the header of the memory block\n\t\t */\n\t\tvoid createMemoryBlock(size_t block_size = MEMORYPOOL_DEFAULT_BLOCK_SIZE);\n\n\t\t/**\n\t\t * Allocates memory in a pool\n\t\t *\n\t\t * @param MemoryPool* mp Memory pool to allocate memory in\n\t\t * @param size_t size Size to allocate in memory pool\n\t\t *\n\t\t * @returns void* Pointer to the newly allocate space\n\t\t */\n\t\tvoid* allocate(size_t size);\n\n\t\t// Templated allocation\n\t\ttemplate<typename T>\n\t\tT* allocate(size_t instances);\n\n\t\t/**\n\t\t * Re-allocates memory in a pool\n\t\t *\n\t\t * @param void* unit_pointer_start Pointer to the object to re-allocate\n\t\t * @param size_t new_size New size to allocate in memory pool\n\t\t *\n\t\t * @returns void* Pointer to the newly allocate space\n\t\t */\n\t\tvoid* reallocate(void* unit_pointer_start, size_t new_size);\n\n\t\t// Templated re-allocation\n\t\ttemplate<typename T>\n\t\tT* reallocate(T* unit_pointer_start, size_t new_size);\n\n\t\t/**\n\t\t * Frees memory in a pool\n\t\t *\n\t\t * @param void* unit_pointer_start Pointer to the object to free\n\t\t */\n\t\tvoid free(void* unit_pointer_start);\n\n\t\t/**\n\t\t * Dump memory pool meta data of blocks unit to stream. \n\t\t * Might be useful for debugging and analyzing memory usage\n\t\t * \n\t\t * @param MemoryPool* mp Memory pool to dump data from\n\t\t */\n\t\tvoid dumpPoolData();\n\n\t\t/**\n\t\t * Start a scope in the memory pool.\n\t\t * All the allocations between startScope and andScope will be freed.\n\t\t * It is a very efficient way to free multiple allocations\n\t\t * \n\t\t * @param MemoryPool* mp Memory pool to start the scope in\n\t\t */\n\t\tvoid startScope();\n\n\t\t/**\n\t\t * \n\t\t */\n\t\tvoid endScope();\n\t};\n\n\ttemplate<typename T>\n\tinline T* MemoryPool::allocate(size_t instances) {\n\t\treturn reinterpret_cast<T*>(this->allocate(instances * sizeof(T)));\n\t}\n\n\ttemplate<typename T>\n\tinline T* MemoryPool::reallocate(T* unit_pointer_start, size_t instances) {\n\t\treturn reinterpret_cast<T*>(this->reallocate(reinterpret_cast<void*>(unit_pointer_start), instances * sizeof(T)));\n\t}\n}\n\n// Override new operators to create with memory pool\nextern void* operator new(size_t size, AppShift::Memory::MemoryPool* mp);\nextern void* operator new[](size_t size, AppShift::Memory::MemoryPool* mp);"
  },
  {
    "path": "Cpp/Util.cpp",
    "content": "#include \"stdafx.h\"\n#include \"cpp.h\"\n\n#pragma region print\n\nHWND s_QM2;\n\n#if TRACE\n\nvoid Print(STR s) {\n#if true\n\tif (!IsWindow(s_QM2)) {\n\t\ts_QM2 = wn::FindWnd(L\"QM_Editor\", 0); if (!s_QM2) return;\n\t}\n\t//SendMessageW(s_QM2, WM_SETTEXT, -1, (LPARAM)(s ? s : L\"\"));\n\tDWORD_PTR res;\n\tSendMessageTimeoutW(s_QM2, WM_SETTEXT, -1, (LPARAM)(s ? s : L\"\"), SMTO_BLOCK | SMTO_ABORTIFHUNG, 5000, &res);\n#else\n\t_cputws(s);\n\t_cputws(L\"\\r\\n\");\n#endif\n}\n\nvoid Printf(const wchar_t* frm, ...) {\n\twchar_t b[10000];\n\tva_list args;\n\tva_start(args, frm);\n\t_vsnwprintf(b, 9990, frm, args);\n\tva_end(args);\n\tPrint(b);\n}\n\n#endif\n\n#pragma endregion\n\n\n#if TRACE\n\nvoid Perf_Inst::First() {\n\tQueryPerformanceCounter((LARGE_INTEGER*)&_time0);\n\t_counter = 0;\n\t_nMeasurements++;\n}\n\nvoid Perf_Inst::Next(char cMark/* = '\\0'*/) {\n\tint n = _counter; if (n >= _nElem) return;\n\t_counter++;\n\t__int64 t; QueryPerformanceCounter((LARGE_INTEGER*)&t);\n\tt -= _time0;\n\tif (_incremental) _a[n] += t; else _a[n] = t;\n\t_aMark[n] = cMark;\n}\n\nvoid Perf_Inst::Write() {\n\tint i, n = _counter;\n\tif (n == 0) return;\n\tif (n > _nElem) n = _nElem;\n\t__int64 f; QueryPerformanceFrequency((LARGE_INTEGER*)&f); double freq = 1000000.0 / f;\n\tbool average = false; int nMeasurements = 1;\n\n\tstr::StringBuilder s;\n\ts << L\"speed:\";\ng1:\n\tdouble t = 0.0, tPrev = 0.0;\n\tfor (i = 0; i < n; i++) {\n\t\ts << L\"  \";\n\t\tif (_aMark[i] != '\\0') {\n\t\t\ts << _aMark[i];\n\t\t\ts << '=';\n\t\t}\n\t\tt = freq * _a[i];\n\t\tdouble d = t - tPrev;\n\t\tif (average) d /= nMeasurements;\n\t\ts << (__int64)d;\n\t\ttPrev = t;\n\t}\n\n\tif (n > 1) {\n\t\ts << L\"  (\";\n\t\tif (average) t /= nMeasurements;\n\t\ts << (__int64)t;\n\t\ts << ')';\n\t}\n\n\tif (!average && _incremental && (nMeasurements = _nMeasurements) > 1) {\n\t\taverage = true;\n\t\ts << L\";  measured \"; s << nMeasurements; s << L\" times, average\";\n\t\tgoto g1;\n\t}\n\n\tPrint(s);\n}\n\n#pragma section(\".shared\", read,write,shared)\n__declspec(allocate(\".shared\")) Perf_Inst Perf;\n\n#endif\n\nOSVersion osVer;\nDelayLoadedApi dlapi;\n\n//returns: 0 failed, 1 x86, 2 x64, 3 arm64\nEXPORT int Cpp_GetProcessArchitecture(DWORD pid) {\n\tif (osVer.is32BitOS()) return 1;\n\n\tHANDLE hp = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, pid);\n\tif (!hp) return 0;\n\n\tif (osVer.minWin11()) {\n\t\tPROCESS_MACHINE_INFORMATION m = {}; //Win11+\n\t\tbool ok = dlapi.GetProcessInformation(hp, ProcessMachineTypeInfo, &m, sizeof(m));\n\t\tCloseHandle(hp);\n\t\tif (!ok) return 0;\n\t\t//Printf(L\"m.ProcessMachine=0x%X\", m.ProcessMachine);\n\t\tauto k = m.ProcessMachine;\n\t\treturn k == IMAGE_FILE_MACHINE_I386 ? 1 : k == IMAGE_FILE_MACHINE_AMD64 ? 2 : k == IMAGE_FILE_MACHINE_ARM64 ? 3 : 0;\n\t} else if(dlapi.IsWow64Process2) {\n\t\t//never mind: not tested on Win10 ARM64.\n\n\t\tUSHORT processArch = 0, osArch = 0;\n\t\tBOOL ok = dlapi.IsWow64Process2(hp, &processArch, &osArch);\n\t\tCloseHandle(hp);\n\t\tif (!ok) return 0;\n\t\treturn processArch != 0 ? 1 : osArch == IMAGE_FILE_MACHINE_ARM64 ? 3 : 2; //3 on ARM64 OS because Windows 10 ARM64 does not support x64 processes\n\t} else {\n\t\tBOOL is32, ok = IsWow64Process(hp, &is32);\n\t\tCloseHandle(hp);\n\t\tif (!ok) return 0;\n\t\treturn is32 ? 1 : 2;\n\t}\n}\n\nnamespace wn {\n\tbool ClassName(HWND w, out Bstr& s) {\n\t\tWCHAR b[260];\n\t\tint n = GetClassNameW(w, b, 260);\n\t\tif (n == 0) {\n\t\t\tif (s) s.Empty();\n\t\t\treturn false;\n\t\t}\n\t\ts.Assign(b, n);\n\t\treturn true;\n\t}\n\n\tint ClassNameIs(HWND w, std::initializer_list<STR> a) {\n\t\tWCHAR b[260];\n\t\tint n = GetClassNameW(w, b, 260);\n\t\tif (n == 0) return 0;\n\t\tint i = 1;\n\t\tfor (const STR* p = a.begin(); p < a.end(); p++, i++) if (str::Like(b, n, *p, wcslen(*p), true)) return i;\n\t\treturn 0;\n\t}\n\n\tbool ClassNameIs(HWND w, STR s) {\n\t\tWCHAR b[260];\n\t\tint n = GetClassNameW(w, b, 260);\n\t\treturn n > 0 && str::Like(b, n, s, wcslen(s), true);\n\t}\n\n\tbool ClassNameIs(HWND w, const str::Wildex& s) {\n\t\tWCHAR b[260];\n\t\tint n = GetClassNameW(w, b, 260);\n\t\treturn n > 0 && s.Match(b, n);\n\t}\n\n\tbool Name(HWND w, out Bstr& s) {\n\t\tbool R = false;\n\t\tif (w) {\n\t\t\tBuffer<WCHAR, 1000> m;\n\t\t\tfor (int na = 1000; na <= 10'000'000; na *= 10) {\n\t\t\t\tint nr = InternalGetWindowText(w, m.Alloc(na), na);\n\t\t\t\tif (nr < na - 1) {\n\t\t\t\t\tif (nr > 0) {\n\t\t\t\t\t\ts.Assign(m, nr);\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t\tR = GetLastError() != 0;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (s) s.Empty();\n\t\treturn R;\n\t}\n\n\t//Like IsWindowVisible, but does not check wTL visibility.\n\t//Slower, but does not make EnumChildWindows significantly slower. Eg 550 -> 570-590 mcs.\n\tbool IsVisibleInWindow(HWND c, HWND wTL) {\n\t\tif (IsWindowVisible(c)) return true; //these two make faster in most cases (when wTL is visible)\n\t\tif (IsWindowVisible(wTL)) return false;\n\t\tint i;\n\t\tfor (i = 0; i < 10000; i++) {\n\t\t\t//PrintWnd(c);\n\t\t\tif (!(wn::Style(c) & WS_VISIBLE)) return false;\n\t\t\tc = (HWND)(LPARAM)GetWindowLongPtrW(c, GWLP_HWNDPARENT);\n\t\t\tif (c == wTL) return true;\n\t\t\tif (c == 0) break;\n\t\t}\n\t\tassert(false);\n\t\treturn false;\n\t}\n\n\tBOOL EnumChildWindows(HWND w, WNDENUMPROCL& callback) {\n\t\treturn ::EnumChildWindows(w, [](HWND c, LPARAM p) { return (BOOL)(*(WNDENUMPROCL*)p)(c); }, (LPARAM)&callback);\n\t}\n\n\t//className - wildcard.\n\tHWND FindChildByClassName(HWND w, STR className, bool visible) {\n\t\tHWND R = 0;\n\t\twn::EnumChildWindows(w, [&R, className, visible, w](HWND c) {\n\t\t\tif (visible && !IsVisibleInWindow(c, w)) return 1;\n\t\t\tif (!wn::ClassNameIs(c, className)) return 1;\n\t\t\tR = c;\n\t\t\treturn 0;\n\t\t\t});\n\t\treturn R;\n\t}\n\n\tHWND FindChildByClassName(HWND w, STR className1, STR className2, OUT bool& second, bool visible) {\n\t\tHWND R = 0;\n\t\twn::EnumChildWindows(w, [&R, className1, className2, &second, visible, w](HWND c) {\n\t\t\tif (visible && !IsVisibleInWindow(c, w)) return 1;\n\t\t\tint i = wn::ClassNameIs(c, { className1, className2 });\n\t\t\tif (i == 0) return 1;\n\t\t\tsecond = i == 2;\n\t\t\tR = c;\n\t\t\treturn 0;\n\t\t\t});\n\t\treturn R;\n\t}\n\n\tHWND FindWndEx(HWND wParent, HWND wAfter, STR cn, STR name) {\n\t\t//see comments in Api.FindWindowEx.\n\t\t//never mind: should repeat only on Win11. Anyway I guess MS soon will fix it.\n\t\tfor (int i = 5; --i >= 0;) {\n\t\t\tHWND w = ::FindWindowEx(wParent, wAfter, cn, name);\n\t\t\tif (w) return w;\n\t\t}\n\t\treturn 0;\n\t}\n\n\tHWND FindWnd(STR cn, STR name) {\n\t\treturn FindWndEx(0, 0, cn, name);\n\t}\n\n\tHWND FindWndExVisible(HWND wParent, STR cn) {\n\t\tHWND c = FindWndEx(wParent, 0, cn, null);\n\t\twhile (c && !IsWindowVisible(c)) c = FindWndEx(wParent, c, cn, null);\n\t\treturn c;\n\t}\n\n\tbool WinformsNameIs(HWND w, STR name) {\n\t\tstatic UINT WM_GETCONTROLNAME = RegisterWindowMessageW(L\"WM_GETCONTROLNAME\");\n\t\tconst int c_bufChars = 1024;\n\n\t\tDWORD pid; if (0 == GetWindowThreadProcessId(w, &pid)) return false;\n\t\twchar_t b[c_bufChars]; int len;\n\t\tif (pid == GetCurrentProcessId()) {\n\t\t\tlen = (int)SendMessageW(w, WM_GETCONTROLNAME, c_bufChars, (LPARAM)b) - 1; if (len < 0) return false;\n\t\t} else {\n\t\t\tHANDLE hp = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ, false, pid); if (!hp) return false;\n\t\t\tvoid* pm = VirtualAllocEx(hp, null, c_bufChars * 2, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);\n\t\t\t__try {\n\t\t\t\tif (pm == null) return false;\n\t\t\t\tDWORD_PTR n1; if (!SendMessageTimeoutW(w, WM_GETCONTROLNAME, c_bufChars, (LPARAM)pm, SMTO_ABORTIFHUNG, 10000, &n1) || --n1 < 0) return false;\n\t\t\t\tlen = (int)n1; n1 *= 2;\n\t\t\t\tSIZE_T n2; if (n1 > 0 && !(ReadProcessMemory(hp, pm, b, n1, &n2) && n2 == n1)) return false;\n\t\t\t}\n\t\t\t__finally {\n\t\t\t\tif (pm != null) VirtualFreeEx(hp, pm, 0, MEM_RELEASE);\n\t\t\t\tCloseHandle(hp);\n\t\t\t}\n\t\t}\n\t\t//b[len] = 0; Print(len); Print(b);\n\t\treturn str::Equals(name, str::Len(name), b, len);\n\n\t\t//note: don't need to cache the process handle and memory. It is several times faster than sendmessage. Anyway, inproc is rarely used with acc.\n\t\t//info: also there is WM_GETCONTROLTYPE, like \"PaintDotNet.Controls.AppWorkspace, PaintDotNet, Version=4.21.6589.7045, Culture=neutral, PublicKeyToken=null\"\n\t}\n\n#if TRACE\n\tvoid PrintWnd(HWND w) {\n\t\tBstr sc, sn;\n\t\tif (!w) {\n\t\t\tPrint(L\"<0 HWND>\");\n\t\t} else if (!ClassName(w, out sc)) {\n\t\t\tPrint(L\"<invalid HWND>\");\n\t\t} else {\n\t\t\tName(w, out sn);\n\t\t\tRECT r = {}; GetWindowRect(w, &r);\n\t\t\tSTR inv = IsWindowVisible(w) ? L\"\" : L\" invisible\";\n\t\t\tDWORD style = (DWORD)GetWindowLong(w, GWL_STYLE);\n\t\t\tPrintf(L\"%i %s \\\"%s\\\" {L=%i T=%i W=%i H=%i}%s style=0x%X\",\n\t\t\t\t(int)(LPARAM)w, sc, sn, r.left, r.top, r.right - r.left, r.bottom - r.top, inv, style);\n\t\t}\n\t}\n#endif\n}\n\n//Gets pointer to other interface through iserviceprovider.\n//Use guidService if it is different than iid.\nbool QueryService_(IUnknown* iFrom, OUT void** iTo, REFIID iid, const GUID* guidService/*=null*/) {\n\t*iTo = null;\n\tif (!guidService) guidService = &iid;\n\tSmart<IServiceProvider> sp;\n\treturn 0 == iFrom->QueryInterface(&sp) && 0 == sp->QueryService(*guidService, iid, iTo) && *iTo;\n}\n\n//void DoEvents()\n//{\n//\tMSG m;\n//\twhile(PeekMessageW(&m, 0, 0, 0, PM_REMOVE)) {\n//\t\t//while(PeekMessageW(&m, 0, 0, 0, PM_REMOVE| PM_QS_SENDMESSAGE)) { //does not work\n//\t\t//Print(m.message);\n//\t\t//Bstr s; if(wn::ClassName(m.hwnd, s)) Print(s);\n//\t\tif(m.message == WM_QUIT) { PostQuitMessage((int)m.wParam); return; }\n//\t\tTranslateMessage(&m);\n//\t\tDispatchMessageW(&m);\n//\t}\n//}\n//\n////void DoEvents2()\n////{\n////\tDWORD signaledIndex;\n////\tauto hr = CoWaitForMultipleHandles(0, 0, 0, null, &signaledIndex); //fails, invalid parameter\n////\tPrint((uint)hr);\n////}\n//\n//void SleepDoEvents(int milliseconds)\n//{\n//\tif(milliseconds == 0) { DoEvents(); return; }\n//\tfor(;;) {\n//\t\tULONGLONG t = 0;\n//\t\tint timeSlice = 100; //we call API in loop with small timeout to make it respond to Thread.Abort\n//\t\tif(milliseconds > 0) {\n//\t\t\tif(milliseconds < timeSlice) timeSlice = milliseconds;\n//\t\t\tt = GetTickCount64();\n//\t\t}\n//\n//\t\tDWORD k = MsgWaitForMultipleObjectsEx(0, null, timeSlice, QS_ALLINPUT, MWMO_ALERTABLE);\n//\t\t//info: k can be 0 (message etc), WAIT_TIMEOUT, WAIT_IO_COMPLETION, WAIT_FAILED.\n//\t\tif(k == WAIT_FAILED) return; //unlikely, because not using handles\n//\t\tif(k == 0) DoEvents();\n//\n//\t\tif(milliseconds > 0) {\n//\t\t\tmilliseconds -= (int)(GetTickCount64() - t);\n//\t\t\tif(milliseconds <= 0) break;\n//\t\t}\n//\t}\n//}\n"
  },
  {
    "path": "Cpp/acc bridge.cpp",
    "content": "#include \"stdafx.h\"\n#include \"cpp.h\"\n#include \"acc.h\"\n\nHRESULT AccFind(AccFindCallback& callback, HWND w, Cpp_Acc* aParent, const Cpp_AccFindParams& ap, out BSTR& errStr);\nHRESULT AccFromPoint(POINT p, HWND wFP, eXYFlags flags, eSpecWnd specWnd, out Cpp_Acc& aResult);\nHRESULT AccGetFocused(HWND w, eFocusedFlags flags, out Cpp_Acc& aResult);\nHRESULT AccNavigate(Cpp_Acc aFrom, STR navig, out Cpp_Acc& aResult);\nHRESULT AccGetProp(Cpp_Acc a, WCHAR what, out BSTR& sResult);\n\nnamespace {\n\n#pragma region marshal\n\n\t//Used for marshaling 'find AO' (IPA_AccFind) parameters when calling the get_accHelpTopic hook function.\n\t//A flat variable-size memory structure (strings follow the fixed-size part).\n\tstruct MarshalParams_AccFind {\n\t\tstruct _FlatStr { int offs, len; };\n\n\t\tMarshalParams_Header hdr;\n\t\tint hwnd; //not HWND, because it must be of same size in 32 and 64 bit process\n\t\teAF2 flags2;\n\tprivate:\n\t\t//these are the same as Cpp_AccFindParams, except is used int instead of STR. Cannot use STR because its size can be 32 or 64 bit.\n\t\t_FlatStr _role, _name, _prop;\n\t\teAF _flags;\n\t\tint _skip;\n\t\tWCHAR _resultProp;\n\n\t\tLPWSTR _SetString(STR s, int len, LPWSTR dest, out _FlatStr& r) {\n\t\t\tif (!s) {\n\t\t\t\tr.len = r.offs = 0;\n\t\t\t\treturn dest;\n\t\t\t}\n\t\t\tmemcpy(dest, s, len * 2); dest[len] = 0;\n\t\t\tr.offs = (int)(dest - (STR)this); r.len = len;\n\t\t\treturn dest + len + 1;\n\t\t}\n\n\t\tSTR _GetString(_FlatStr r, out int& len) {\n\t\t\tlen = r.len;\n\t\t\tif (!r.offs) return null;\n\t\t\treturn (STR)this + r.offs;\n\t\t}\n\tpublic:\n\t\tstatic int CalcMemSize(const Cpp_AccFindParams& ap) {\n\t\t\treturn sizeof(MarshalParams_AccFind) + (ap.roleLength + ap.nameLength + ap.propLength + 3) * 2;\n\t\t}\n\n\t\tvoid Marshal(HWND w, const Cpp_AccFindParams& ap) {\n\t\t\thwnd = (int)(LPARAM)w;\n\n\t\t\tauto s = (LPWSTR)(this + 1);\n\t\t\ts = _SetString(ap.role, ap.roleLength, s, out _role);\n\t\t\ts = _SetString(ap.name, ap.nameLength, s, out _name);\n\t\t\ts = _SetString(ap.prop, ap.propLength, s, out _prop);\n\t\t\t_flags = ap.flags;\n\t\t\t_skip = ap.skip;\n\t\t\t_resultProp = ap.resultProp;\n\t\t\tflags2 = ap.flags2;\n\t\t}\n\n\t\tvoid Unmarshal(out Cpp_AccFindParams& ap) {\n\t\t\tap.role = _GetString(_role, out ap.roleLength);\n\t\t\tap.name = _GetString(_name, out ap.nameLength);\n\t\t\tap.prop = _GetString(_prop, out ap.propLength);\n\t\t\tap.flags = _flags;\n\t\t\tap.skip = _skip;\n\t\t\tap.resultProp = _resultProp;\n\t\t\tap.flags2 = flags2;\n\t\t}\n\t};\n\n\tstatic long s_accMarshalWrapperCount;\n\n\t//This is used as a workaround when CoMarshalInterface fails.\n\t//More comments in WriteAccToStream.\n\tclass AccessibleMarshalWrapper : public IAccessible {\n\t\tIAccessible* _a;\n\tpublic:\n\t\tbool ignoreQI;\n\n\t\tAccessibleMarshalWrapper(IAccessible* a) {\n\t\t\t_a = a;\n\t\t\tignoreQI = true;\n\n\t\t\tInterlockedIncrement(&s_accMarshalWrapperCount);\n\t\t}\n\n\t\t~AccessibleMarshalWrapper() {\n\t\t\tInterlockedDecrement(&s_accMarshalWrapperCount);\n\t\t}\n\n\t\tvirtual STDMETHODIMP QueryInterface(REFIID riid, void** ppvObject) override {\n\t\t\tif (riid == IID_IAccessible || riid == IID_IUnknown || riid == IID_IDispatch) {\n\t\t\t\t_a->AddRef();\n\t\t\t\t*ppvObject = this;\n\t\t\t\treturn 0;\n\t\t\t\t//Old Firefox bug: TEXT AOs return E_NOINTERFACE for IID_IUnknown, that is why CoMarshalInterface fails.\n\t\t\t\t//\tAlso they don't give custom IMarshal, although all other AOs do.\n\t\t\t}\n\t\t\tif (ignoreQI) { //don't give IMarshal and other interfaces while in CoMarshalInterface\n\t\t\t\t*ppvObject = null;\n\t\t\t\treturn E_NOINTERFACE;\n\t\t\t}\n\n\t\t\treturn _a->QueryInterface(riid, ppvObject);\n\t\t}\n\t\tvirtual STDMETHODIMP_(ULONG) AddRef(void) override {\n\t\t\treturn _a->AddRef();\n\t\t}\n\t\tvirtual STDMETHODIMP_(ULONG) Release(void) override {\n\t\t\tauto r = _a->Release();\n\t\t\t//Print((int)r);\n\t\t\tif (r == 0) delete this;\n\t\t\treturn r;\n\t\t}\n#pragma region IAccessible\n\t\tvirtual STDMETHODIMP GetTypeInfoCount(UINT* pctinfo) override {\n\t\t\treturn E_NOTIMPL;\n\t\t}\n\t\tvirtual STDMETHODIMP GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo) override {\n\t\t\treturn E_NOTIMPL;\n\t\t}\n\t\tvirtual STDMETHODIMP GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgDispId) override {\n\t\t\treturn E_NOTIMPL;\n\t\t}\n\t\tvirtual STDMETHODIMP Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr) override {\n\t\t\treturn E_NOTIMPL;\n\t\t}\n\t\tvirtual STDMETHODIMP get_accParent(IDispatch** ppdispParent) override {\n\t\t\treturn _a->get_accParent(ppdispParent);\n\t\t}\n\t\tvirtual STDMETHODIMP get_accChildCount(long* pcountChildren) override {\n\t\t\treturn _a->get_accChildCount(pcountChildren);\n\t\t}\n\t\tvirtual STDMETHODIMP get_accChild(VARIANT varChild, IDispatch** ppdispChild) override {\n\t\t\treturn _a->get_accChild(varChild, ppdispChild);\n\t\t}\n\t\tvirtual STDMETHODIMP get_accName(VARIANT varChild, BSTR* pszName) override {\n\t\t\treturn _a->get_accName(varChild, pszName);\n\t\t}\n\t\tvirtual STDMETHODIMP get_accValue(VARIANT varChild, BSTR* pszValue) override {\n\t\t\treturn _a->get_accValue(varChild, pszValue);\n\t\t}\n\t\tvirtual STDMETHODIMP get_accDescription(VARIANT varChild, BSTR* pszDescription) override {\n\t\t\treturn _a->get_accDescription(varChild, pszDescription);\n\t\t}\n\t\tvirtual STDMETHODIMP get_accRole(VARIANT varChild, VARIANT* pvarRole) override {\n\t\t\treturn _a->get_accRole(varChild, pvarRole);\n\t\t}\n\t\tvirtual STDMETHODIMP get_accState(VARIANT varChild, VARIANT* pvarState) override {\n\t\t\treturn _a->get_accState(varChild, pvarState);\n\t\t}\n\t\tvirtual STDMETHODIMP get_accHelp(VARIANT varChild, BSTR* pszHelp) override {\n\t\t\treturn _a->get_accHelp(varChild, pszHelp);\n\t\t}\n\t\tvirtual STDMETHODIMP get_accHelpTopic(BSTR* pszHelpFile, VARIANT varChild, long* pidTopic) override {\n\t\t\treturn E_NOTIMPL;\n\t\t}\n\t\tvirtual STDMETHODIMP get_accKeyboardShortcut(VARIANT varChild, BSTR* pszKeyboardShortcut) override {\n\t\t\treturn _a->get_accKeyboardShortcut(varChild, pszKeyboardShortcut);\n\t\t}\n\t\tvirtual STDMETHODIMP get_accFocus(VARIANT* pvarChild) override {\n\t\t\treturn _a->get_accFocus(pvarChild);\n\t\t}\n\t\tvirtual STDMETHODIMP get_accSelection(VARIANT* pvarChildren) override {\n\t\t\treturn _a->get_accSelection(pvarChildren);\n\t\t}\n\t\tvirtual STDMETHODIMP get_accDefaultAction(VARIANT varChild, BSTR* pszDefaultAction) override {\n\t\t\treturn _a->get_accDefaultAction(varChild, pszDefaultAction);\n\t\t}\n\t\tvirtual STDMETHODIMP accSelect(long flagsSelect, VARIANT varChild) override {\n\t\t\treturn _a->accSelect(flagsSelect, varChild);\n\t\t}\n\t\tvirtual STDMETHODIMP accLocation(long* pxLeft, long* pyTop, long* pcxWidth, long* pcyHeight, VARIANT varChild) override {\n\t\t\treturn _a->accLocation(pxLeft, pyTop, pcxWidth, pcyHeight, varChild);\n\t\t}\n\t\tvirtual STDMETHODIMP accNavigate(long navDir, VARIANT varStart, VARIANT* pvarEndUpAt) override {\n\t\t\treturn _a->accNavigate(navDir, varStart, pvarEndUpAt);\n\t\t}\n\t\tvirtual STDMETHODIMP accHitTest(long xLeft, long yTop, VARIANT* pvarChild) override {\n\t\t\treturn _a->accHitTest(xLeft, yTop, pvarChild);\n\t\t}\n\t\tvirtual STDMETHODIMP accDoDefaultAction(VARIANT varChild) override {\n\t\t\treturn _a->accDoDefaultAction(varChild);\n\t\t}\n\t\tvirtual STDMETHODIMP put_accName(VARIANT varChild, BSTR szName) override {\n\t\t\treturn _a->put_accName(varChild, szName);\n\t\t}\n\t\tvirtual STDMETHODIMP put_accValue(VARIANT varChild, BSTR szValue) override {\n\t\t\treturn _a->put_accValue(varChild, szValue);\n\t\t}\n#pragma endregion\n\t};\n\n\t//The BSTR returned by our get_accHelpTopic hook contains data of one or more accessible objects (AO).\n\t//\tEach AO data can have IAccessible object data (created by CoMarshalInterface), child element id, level, role, rect.\n\t//\tThese flags tell what is in the data.\n\t//[Flags]\n\tenum class eAccResult {\n\t\tElem = 1,\n\t\tRole = 2,\n\t\tLevel = 4,\n\t\tRect = 8,\n\t\tUsePrevAcc = 0x40,\n\t\tUsePrevLevel = 0x80,\n\t};\n\tENABLE_BITMASK_OPERATORS(eAccResult);\n\n\tbool WriteAccToStream(ref Smart<IStream>& stream, Cpp_Acc a, Cpp_Acc* aPrev = null, RECT* rect = null) {\n\t\teAccResult has = {};\n\t\tif (aPrev != null) {\n\t\t\tif (a.acc == aPrev->acc && a.elem != 0) has |= eAccResult::UsePrevAcc; else aPrev->acc = a.acc;\n\t\t\tif (a.misc.level != 0) has |= a.misc.level == aPrev->misc.level ? eAccResult::UsePrevLevel : eAccResult::Level;\n\t\t\taPrev->misc.level = a.misc.level;\n\t\t} else {\n\t\t\tif (a.misc.level != 0) has |= eAccResult::Level;\n\t\t}\n\t\tif (a.elem != 0) has |= eAccResult::Elem;\n\t\tif (a.misc.roleByte != 0) has |= eAccResult::Role;\n\t\tif (rect != null) has |= eAccResult::Rect;\n\n\t\tif (0 != stream->Write(&has, 1, null)) return false;\n\n\t\ta.misc.flags |= eAccMiscFlags::InProc;\n\n\t\tif (!(has & eAccResult::UsePrevAcc)) {\n\t\t\t//problem: with some AO the hook is not called when we try to do something inproc, eg get all props.\n\t\t\t//\tThey use a custom IMarshal, which redirects to another (not hooked) IAccessible interface. In most cases it is even in another process.\n\t\t\t//\tKnown apps: 1. Old Firefox, when multiprocess not disabled. 2. Some hidden AO in IE. 3. Windows store apps, but we don't use inproc.\n\t\t\t//\tKnown apps where is custom IMarshal but the hook works: 1. Task Scheduler MMC: controls of other process.\n\t\t\t//\tWorkarounds:\n\t\t\t//\t\tTested, fails: replace CoMarshalInterface with CoGetStandardMarshal/MarshalInterface. Chrome works, old Firefox crashes.\n\t\t\t//\t\tOld, rejected: remove InProc flag. But with some old Firefox versions then all AOs are not inproc.\n\t\t\t//\t\tNow using: wrap the AO in AccessibleMarshalWrapper and marshal it instead.\n\t\t\tHRESULT hr = 1;\n\t\t\tIMarshal* m = null;\n\t\t\tif (0 == a.acc->QueryInterface(&m)) m->Release();\n\t\t\telse hr = CoMarshalInterface(stream, IID_IAccessible, a.acc, MSHCTX_LOCAL, null, MSHLFLAGS_NORMAL);\n\t\t\t//ao::PrintAcc(a.acc, a.elem); Print((DWORD)hr);\n\t\t\tif (hr != 0) {\n#if true\n\t\t\t\tHRESULT hr1 = hr;\n\t\t\t\tauto wrap = new AccessibleMarshalWrapper(a.acc);\n\t\t\t\ta.acc = wrap;\n\t\t\t\thr = CoMarshalInterface(stream, IID_IAccessible, a.acc, MSHCTX_LOCAL, null, MSHLFLAGS_NORMAL);\n\t\t\t\tif (hr == 0) wrap->ignoreQI = false; else delete wrap;\n\t\t\t\t//Print((UINT)hr);\n\n\t\t\t\tif (hr) PRINTF(L\"failed to marshal AO: 0x%X 0x%X\", hr1, hr);\n#endif\n\t\t\t\t//ao::PrintAcc(a.acc, a.elem);\n\t\t\t} //else Print(L\"OK\");\n\t\t\tif (hr != 0) return false;\n\t\t\tinproc::s_hookIAcc.Hook(a.acc);\n\t\t}\n\n\t\tif (!!(has & eAccResult::Elem))\n\t\t\tif (stream->Write(&a.elem, 4, null)) return false;\n\n\t\tif (stream->Write(&a.misc.flags, 1, null)) return false;\n\n\t\tif (!!(has & eAccResult::Role))\n\t\t\tif (stream->Write(&a.misc.roleByte, 1, null)) return false;\n\n\t\tif (!!(has & eAccResult::Level))\n\t\t\tif (stream->Write(&a.misc.level, 2, null)) return false;\n\n\t\tif (!!(has & eAccResult::Rect))\n\t\t\tif (stream->Write(rect, 16, null)) return false;\n\n\t\treturn true;\n\t}\n\n#pragma endregion\n\n\tstruct _AccRect {\n\t\tCpp_Acc a;\n\t\tRECT r;\n\t\tint state, nSiblings;\n\n\t\tint role() const { return a.misc.roleByte; }\n\t\tint level() const { return a.misc.level; }\n\n\t\tstatic bool Skip(ref Cpp_Acc& a, int state) {\n\t\t\tif (!(state & (STATE_SYSTEM_OFFSCREEN | STATE_SYSTEM_INVISIBLE))) return false;\n\t\t\tif (a.elem != 0) return true;\n\n\t\t\tbool skip = true;\n\t\t\tint role = a.misc.roleByte;\n\t\t\tif (!!(state & STATE_SYSTEM_OFFSCREEN)) {\n\t\t\t\tbool mayHaveVisibleChildren = (state & STATE_SYSTEM_EXPANDED) || IsIn(role, ROLE_SYSTEM_PAGETAB, ROLE_SYSTEM_PAGETABLIST);\n\t\t\t\tskip = !mayHaveVisibleChildren;\n\t\t\t}\n\n\t\t\t//workaround for bugs in some apps where a hidden control has visible acc descendants, eg in WinUI3 windows like Win11 mspaint.\n\t\t\t//\tIf it's the only child of a visible parent, likely its acc descendants are visible.\n\t\t\tif (skip && IsIn(role, ROLE_SYSTEM_WINDOW, ROLE_SYSTEM_CLIENT, ROLE_SYSTEM_PANE)) {\n\t\t\t\tHWND w = 0;\n\t\t\t\tif (0 == WindowFromAccessibleObject(a.acc, &w) && w) {\n\t\t\t\t\tif ((wn::Style(w) & WS_CHILD) != 0 && IsWindowVisible(GetParent(w)) && GetWindow(w, GW_HWNDNEXT) == 0 && GetWindow(w, GW_HWNDPREV) == 0) {\n\t\t\t\t\t\tskip = false;\n\t\t\t\t\t}\n\t\t\t\t\t//note: don't compare rect, it may be empty\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn skip;\n\t\t}\n\n\t\tstatic void FilterRects(ref std::vector<_AccRect>& a) {\n\t\t\t//remove all GROUPING that don't contain descendants other than GROUPING. And PANE too.\n\t\t\tint nRemove = 0;\n\t\t\tfor (int n = (int)a.size(), i = n; --i >= 0; ) {\n\t\t\t\tauto& v = a[i];\n\t\t\t\tif (IsIn(v.role(), ROLE_SYSTEM_GROUPING, ROLE_SYSTEM_PANE)) {\n\t\t\t\t\tbool noChildren = i == n - 1;\n\t\t\t\t\tif (!noChildren) {\n\t\t\t\t\t\tauto& v2 = a[i + 1];\n\t\t\t\t\t\tint levelPlus = v2.level() - v.level();\n\t\t\t\t\t\tnoChildren = levelPlus <= 0 || (levelPlus == 1 && v2.nSiblings == 0); //v2 is not child, or is removed child\n\t\t\t\t\t}\n\t\t\t\t\tif (noChildren) {\n\t\t\t\t\t\tv.nSiblings = 0;\n\t\t\t\t\t\tnRemove++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (nRemove > 0) {\n\t\t\t\t//auto len1 = a.size();\n\t\t\t\ta.erase(std::remove_if(a.begin(), a.end(), [](const _AccRect& v) { return v.nSiblings == 0; }), a.end());\n\t\t\t\t//Printf(L\"erase: %i, %i\", (int)len1, (int)a.size());\n\t\t\t}\n\n\t\t\tfor (int n = (int)a.size(), i = n; --i >= 0; ) {\n\t\t\t\tauto& v = a[i];\n\t\t\t\t//exclude STATICTEXT etc if it is single child in parent and does not have non-removed children\n\t\t\t\tif (v.nSiblings == 1 && v.level() > 0) { //single child in parent\n\t\t\t\t\tint role = v.role();\n\t\t\t\t\tbool isStatic = role == ROLE_SYSTEM_STATICTEXT || role == ROLE_SYSTEM_GRAPHIC || role == ROLE_SYSTEM_GROUPING || (role == ROLE_SYSTEM_TEXT && 0 == (v.state & (STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_UNAVAILABLE)));\n\t\t\t\t\tif (isStatic) {\n\t\t\t\t\t\tbool noChildren = i == n - 1;\n\t\t\t\t\t\tif (!noChildren) {\n\t\t\t\t\t\t\tauto& v2 = a[i + 1];\n\t\t\t\t\t\t\tint levelPlus = v2.level() - v.level();\n\t\t\t\t\t\t\tnoChildren = levelPlus <= 0 || (levelPlus == 1 && v2.nSiblings == 0); //v2 is not child, or is removed child\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (noChildren) { //does not have non-removed children (in window rect)\n\t\t\t\t\t\t\tv.nSiblings = 0; //mark to exclude\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t//maybe re-include previously excluded descendants\n\t\t\t\tif (i < n - 1 && a[i + 1].nSiblings == 0) {\n\t\t\t\t\tbool reInclude = v.role() == ROLE_SYSTEM_GROUPING;\n\t\t\t\t\tif (!(reInclude || ao::IsLinkOrButton(v.role()))) {\n\t\t\t\t\t\tBstr bn;\n\t\t\t\t\t\treInclude = !((0 == v.a.acc->get_accName(ao::VE(), &bn)) && bn && bn.Length() > 0); //no name\n\t\t\t\t\t}\n\t\t\t\t\tif (reInclude) {\n\t\t\t\t\t\tfor (int j = i + 1; j < n && a[j].nSiblings == 0 && a[j].level() - a[j - 1].level() == 1; j++) {\n\t\t\t\t\t\t\ta[j].nSiblings = 1;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t};\n\n} //namespace\n\nnamespace inproc {\n\n\t//Called from the hook to find or get AO.\n\t//Common for Cpp_AccFind, Cpp_AccFromWindow and other functions that return AO.\n\tHRESULT AccFindOrGet(MarshalParams_Header* h, IAccessible* iacc, out BSTR& sResult) {\n\t\tSmart<IStream> stream;\n\t\tif (0 != CreateStreamOnHGlobal(0, true, &stream)) return RPC_E_SERVER_CANTMARSHAL_DATA;\n\n\t\tauto action = h->action;\n\t\tif (action == InProcAction::IPA_AccNavigate) {\n\t\t\tauto p = (MarshalParams_AccElem*)h;\n\t\t\tCpp_Acc aFrom(iacc, p->elem, h->miscFlags), aResult;\n\n\t\t\tHRESULT hr = AccNavigate(aFrom, (STR)(p + 1), out aResult);\n\t\t\tif (hr != 0) return hr;\n\t\t\taResult.SetRoleByte();\n\n\t\t\tif (!WriteAccToStream(ref stream, aResult)) return RPC_E_SERVER_CANTMARSHAL_DATA;\n\n\t\t\tif (aResult.acc != iacc) aResult.acc->Release();\n\t\t} else if (action == InProcAction::IPA_AccFromWindow) {\n\t\t\tauto p = (MarshalParams_AccFromWindow*)h;\n\t\t\tSmart<IAccessible> a;\n\n\t\t\tHRESULT hr = ao::AccFromWindowSR((HWND)(LPARAM)p->hwnd, p->objid, &a);\n\t\t\tif (hr != 0) return hr;\n\n\t\t\tif (p->flags & 2) { //get name\n\t\t\t\treturn a->get_accName(ao::VE(), out & sResult);\n\t\t\t}\n\n\t\t\tCpp_Acc aResult(a, 0);\n\t\t\taResult.SetRoleByte();\n\n\t\t\tif (!WriteAccToStream(ref stream, aResult)) return RPC_E_SERVER_CANTMARSHAL_DATA;\n\n\t\t} else if (action == InProcAction::IPA_AccFromPoint) {\n\t\t\tauto x = (MarshalParams_AccFromPoint*)h;\n\t\t\tCpp_Acc aResult;\n\t\t\tHRESULT hr = AccFromPoint(x->p, (HWND)(LPARAM)x->wFP, x->flags, x->specWnd, out aResult);\n\t\t\tif (hr != 0) return hr;\n\t\t\tif (!WriteAccToStream(ref stream, aResult)) return RPC_E_SERVER_CANTMARSHAL_DATA;\n\n\t\t\t//Workaround for AO leak: the final Release called in the client process somehow does not release the true AO. Releases only the proxy.\n\t\t\t//\tBut FromWindow() and Find() work well without this Release, although use the same marshaling code etc.\n\t\t\t//\tTested raw AccessibleObjectFromPoint, the same.\n\t\t\t//\tActually there are 2 proxies: one is created on WM_GETOBJECT, other by marshaling to the client process. Probably the first proxy would leak too.\n\t\t\t//aResult.acc->Release(); aResult.acc->Release(); //somehow works even with this. But FromWindow() still works with 1 less Release.\n\t\t\t//Printf(L\"----- %i\", aResult.acc->Release());//3\n\t\t\taResult.acc->Release();\n\n\t\t} else if (action == InProcAction::IPA_AccFocused) {\n\t\t\tauto x = (MarshalParams_AccFocused*)h;\n\t\t\tCpp_Acc aResult;\n\t\t\tHRESULT hr = AccGetFocused((HWND)(LPARAM)x->hwnd, x->flags, out aResult);\n\t\t\tif (hr != 0) return hr;\n\t\t\tif (!WriteAccToStream(ref stream, aResult)) return RPC_E_SERVER_CANTMARSHAL_DATA;\n\t\t\taResult.acc->Release();\n\n\t\t} else { //IPA_AccFind\n\t\t\tCpp_AccFindParams ap;\n\t\t\tauto p = (MarshalParams_AccFind*)h; p->Unmarshal(out ap);\n\t\t\tHWND w = (HWND)(LPARAM)p->hwnd;\n\t\t\teAF2 flags2 = p->flags2;\n\t\t\tbool findAll = !!(flags2 & eAF2::FindAll), getRects = !!(flags2 & eAF2::GetRects);\n\t\t\tint skip = ap.skip;\n\t\t\tHRESULT hr = (HRESULT)eError::NotFound;\n\t\t\tCpp_Acc aParent(iacc, 0, h->miscFlags), aPrev;\n\t\t\tDpiElmScaling des(getRects, w, null);\n\t\t\tstd::vector<_AccRect> agr;\n\n\t\t\tHRESULT hr2 = AccFind(\n\t\t\t\t[&](Cpp_Acc a, int state, int nSiblings) mutable {\n\t\t\t\t\tif (!findAll && skip-- > 0) return eAccFindCallbackResult::Continue;\n\n\t\t\t\t\tif (ap.resultProp) {\n\t\t\t\t\t\tif (ap.resultProp != '-') {\n\t\t\t\t\t\t\ta.misc.flags |= eAccMiscFlags::InProc;\n\t\t\t\t\t\t\tAccGetProp(a, ap.resultProp, out sResult);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (getRects) { //Delm in \"capture smaller\" mode uses it together with findAll to get all AO and their rects when inproc\n\t\t\t\t\t\tif (!(ap.flags & eAF::HiddenToo)) {\n\t\t\t\t\t\t\tif (_AccRect::Skip(ref a, state)) return eAccFindCallbackResult::SkipChildren;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tRECT rect = { };\n\t\t\t\t\t\tif (0 != ao::accLocation(out rect, a.acc, a.elem)) return eAccFindCallbackResult::Continue;\n\t\t\t\t\t\tint scaleResult = des.ScaleIfNeed(ref rect, true);\n\t\t\t\t\t\tif (scaleResult == 1) return eAccFindCallbackResult::Continue; //not in window rect\n\n\t\t\t\t\t\tagr.emplace_back(a, rect, state, nSiblings);\n\t\t\t\t\t\ta.acc->AddRef();\n\t\t\t\t\t} else if (findAll) {\n\t\t\t\t\t\tDWORD pos = 0; istream::GetPos(stream, out pos);\n\n\t\t\t\t\t\tif (!WriteAccToStream(ref stream, a, &aPrev)) {\n\t\t\t\t\t\t\tstream->Seek(istream::LI(pos), STREAM_SEEK_SET, null);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (!WriteAccToStream(ref stream, a, &aPrev)) goto ge;\n\t\t\t\t\t}\n\n\t\t\t\t\thr = 0;\n\t\t\t\t\treturn findAll ? eAccFindCallbackResult::Continue : eAccFindCallbackResult::StopFound;\n\t\t\t\tge:\n\t\t\t\t\thr = RPC_E_SERVER_CANTMARSHAL_DATA;\n\t\t\t\t\treturn eAccFindCallbackResult::StopNotFound;\n\t\t\t\t}, w, w ? null : &aParent, ref ap, out sResult);\n\n\t\t\tif (hr2 != 0 && hr2 != (HRESULT)eError::NotFound) return hr2;\n\t\t\tif (hr != 0) return hr;\n\n\t\t\tif (getRects) {\n\t\t\t\t_AccRect::FilterRects(ref agr);\n\t\t\t\tfor (auto& k : agr) {\n\t\t\t\t\tif (k.nSiblings > 0) { //else removed\n\t\t\t\t\t\tDWORD pos = 0; istream::GetPos(stream, out pos);\n\t\t\t\t\t\tif (!WriteAccToStream(ref stream, k.a, &aPrev, &k.r)) {\n\t\t\t\t\t\t\tstream->Seek(istream::LI(pos), STREAM_SEEK_SET, null);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tk.a.acc->Release();\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (ap.resultProp) return 0;\n\t\t\t}\n\t\t}\n\n\t\tDWORD streamSize, readSize;\n\t\tif (istream::GetPos(stream, out streamSize) && istream::ResetPos(stream)) {\n\t\t\tsResult = SysAllocStringByteLen(null, streamSize);\n\t\t\tif (0 == stream->Read(sResult, streamSize, &readSize) && readSize == streamSize) return 0;\n\t\t\tSysFreeString(sResult); sResult = null;\n\t\t}\n\t\treturn RPC_E_SERVER_CANTMARSHAL_DATA;\n\t}\n\n\t//Returns false if there are AccessibleMarshalWrapper objects in this process.\n\t//Then cannot unload this dll, because later will be called Release and this process would crash if unloaded.\n\t//Could not find a way to prevent Release. Even if client does not call it, COM calls it after 6 minutes. CoDisconnectObject prevents only other method calls.\n\tbool AccDisconnectWrappers() {\n\t\tPRINTF_IF(s_accMarshalWrapperCount != 0, L\"cannot unload dll because of %i alive acc marshal wrappers.  %s\", s_accMarshalWrapperCount, GetCommandLineW());\n\t\treturn s_accMarshalWrapperCount == 0;\n\t}\n\n} //namespace inproc\n\nnamespace outproc {\n\t//Reads one AO from results.\n\t//When FindAll, the caller must call this in loop, until returns a non-zero. If returns NotFound, there are no more AO to read.\n\t//a - receives the AO, elem, etc. When FindAll, the caller must use the same variable for all, because this function uses it as an input parameter too (previous AO).\n\t//dontNeedAO - don't need AO. Only release marshal data if need.\n\tHRESULT InProcCall::ReadResultAcc(ref Cpp_Acc& a, bool dontNeedAO/* = false*/, RECT* rect/* =null*/) {\n\t\tif (!_stream) {\n\t\t\t_resultSize = _br.ByteLength(); if (_resultSize == 0) return RPC_E_CLIENT_CANTUNMARSHAL_DATA;\n\t\t\tHGLOBAL hg = GlobalAlloc(GMEM_MOVEABLE, _resultSize); if (hg == 0) return RPC_E_CLIENT_CANTUNMARSHAL_DATA;\n\t\t\tLPVOID mem = GlobalLock(hg); memcpy(mem, _br, _resultSize); GlobalUnlock(mem);\n\t\t\tif (0 != CreateStreamOnHGlobal(hg, true, &_stream)) return RPC_E_CLIENT_CANTUNMARSHAL_DATA;\n\t\t\t//Print(_resultSize);\n\t\t}\n\n\t\tDWORD pos; if (istream::GetPos(_stream, out pos) && pos == _resultSize) return (HRESULT)eError::NotFound; //no more results when FindAll. Fast.\n\n\t\teAccResult has = {};\n\t\tif (0 != _stream->Read(&has, 1, null)) return RPC_E_CLIENT_CANTUNMARSHAL_DATA;\n\n\t\tif (!(has & eAccResult::UsePrevAcc)) {\n\t\t\tHRESULT hr;\n\t\t\tif (dontNeedAO) {\n\t\t\t\t//Perf.First();\n\t\t\t\thr = CoReleaseMarshalData(_stream);\n\t\t\t\t//Perf.NW(); //slow, because calls Release in the server process\n\t\t\t\ta.acc = null;\n\t\t\t} else {\n\t\t\t\thr = CoUnmarshalInterface(_stream, IID_IAccessible, (void**)&a.acc);\n\t\t\t}\n\t\t\tif (hr) return RPC_E_CLIENT_CANTUNMARSHAL_DATA;\n\t\t} else if (!dontNeedAO) {\n\t\t\tassert(!!(has & eAccResult::Elem));\n\t\t\ta.acc->AddRef();\n\t\t}\n\n\t\tif (!(has & eAccResult::Elem)) a.elem = 0;\n\t\telse if (_stream->Read(&a.elem, 4, null)) return RPC_E_CLIENT_CANTUNMARSHAL_DATA;\n\n\t\tif (_stream->Read(&a.misc.flags, 1, null)) return RPC_E_CLIENT_CANTUNMARSHAL_DATA;\n\n\t\tif (!(has & eAccResult::Role)) a.misc.roleByte = 0;\n\t\telse if (_stream->Read(&a.misc.roleByte, 1, null)) return RPC_E_CLIENT_CANTUNMARSHAL_DATA;\n\n\t\tif (!(has & eAccResult::UsePrevLevel)) {\n\t\t\tif (!(has & eAccResult::Level)) a.misc.level = 0;\n\t\t\telse if (_stream->Read(&a.misc.level, 2, null)) return RPC_E_CLIENT_CANTUNMARSHAL_DATA;\n\t\t}\n\n\t\tif (!!(has & eAccResult::Rect)) {\n\t\t\tif (_stream->Read(rect, 16, null)) return RPC_E_CLIENT_CANTUNMARSHAL_DATA;\n\t\t}\n\n\t\treturn 0;\n\t}\n\n\t//Gets AO of window (calls AccessibleObjectFromWindow).\n\t//flags:\n\t//\t1 - not inproc. If used this flag, or if failed to inject dll, the returned AO will not be suitable for in-proc search.\n\t//\t2 - get name instead. Results: aResult = empty, sResult = name.\n\tEXPORT HRESULT Cpp_AccFromWindow(DWORD flags, HWND w, DWORD objid, out Cpp_Acc& aResult, out BSTR& sResult) {\n\t\taResult.Zero(); sResult = null;\n\n\t\tif (objid == OBJID_JAVA) {\n\t\t\tauto iacc = AccJavaFromWindow(w);\n\t\t\tif (iacc == null) return 1;\n\t\t\taResult.acc = iacc;\n\t\t\taResult.misc.flags = eAccMiscFlags::Java;\n\t\t\treturn 0;\n\t\t} else if (objid == OBJID_UIA) {\n\t\t\tHRESULT hr = AccUiaFromWindow(w, &aResult.acc);\n\t\t\tif (hr == 0) aResult.misc.flags = eAccMiscFlags::UIA;\n\t\t\treturn hr;\n\t\t\t//never mind: inproc. Maybe in the future.\n\t\t}\n\n\t\tHRESULT R;\n\tg1:\n\t\tif (flags & 1) { //not inproc\n\t\t\tR = ao::AccFromWindowSR(w, objid, &aResult.acc);\n\t\t\tif (R == 0 && flags & 2) {\n\t\t\t\tR = aResult.acc->get_accName(ao::VE(), out & sResult);\n\t\t\t\taResult.acc->Release(); aResult.acc = null;\n\t\t\t}\n\t\t\treturn R;\n\t\t}\n\n\t\t//Perf.First();\n\t\tCpp_Acc_Agent aAgent;\n\t\tif (0 != (R = InjectDllAndGetAgent(w, out aAgent.acc))) {\n\t\t\tswitch ((eError)R) {\n\t\t\tcase eError::WindowOfThisThread: case eError::UseNotInProc: case eError::Inject: break;\n\t\t\tdefault: return R;\n\t\t\t}\n\t\t\tflags |= 1; goto g1;\n\t\t}\n\t\t//Perf.Next();\n\n\t\tInProcCall ic;\n\t\tauto p = (MarshalParams_AccFromWindow*)ic.AllocParams(&aAgent, InProcAction::IPA_AccFromWindow, sizeof(MarshalParams_AccFromWindow));\n\t\tp->hwnd = (int)(LPARAM)w;\n\t\tp->objid = objid;\n\t\tp->flags = flags;\n\t\tif (0 != (R = ic.Call())) return R;\n\t\t//Perf.Next();\n\t\tif (flags & 2) sResult = ic.DetachResultBSTR();\n\t\telse R = ic.ReadResultAcc(ref aResult);\n\t\t//Perf.NW();\n\t\treturn R;\n\t}\n\n\t//Finds a descendant AO of w or aParent.\n\t//By default searches in the target process. If flag NotInProc (or if cannot inject), searches from this process (slow); then the returned AO will not be suitable for in-proc search.\n\t//w - parent window or 0 (if aParent used).\n\t//aParent - parent AO or null (if w used). Must be retrieved in-proc.\n\t//ap - AO parameters.\n\t//also - if not null, this func calls the callback function for each matching AO.\n\t//\tNeed to Release the AO, preferably later, maybe in another thread.\n\t//\tIf the callback returns true, it is not called again (unless 'skip' is used), and this function returns 0 (found).\n\t//\tIf the callback always returns false, this function returns eError::NotFound.\n\t//aResult - receives the found AO (if this function returns 0 (found)).\n\t//\tNeed to Release.\n\t//\tIf used 'also', it can be the same AO as the callback received the last time. Need to Release both.\n\t//\tIt is empty if this func returns not 0 or if used ap.resultProp.\n\t//sResult - error string or a property of the found AO.\n\t//\tWhen this func returns eError::InvalidParameter, it is error string.\n\t//\tWhen this func returns 0 and used ap.resultProp, it is the property (string, or binary struct); null if '-'.\n\t//\tElse null.\n\t//getRects - if inproc, get all rectangles too. Use with 'also'.\n\tEXPORT HRESULT Cpp_AccFind(HWND w, Cpp_Acc* aParent, Cpp_AccFindParams ap, Cpp_AccFindCallbackT also, out Cpp_Acc& aResult, out BSTR& sResult, bool getRects) {\n\t\t//Perf.First();\n\t\taResult.Zero(); sResult = null;\n\t\tbool inProc = !(ap.flags & eAF::NotInProc), findAll = (also != null), useWnd = (aParent == null);\n\t\tif (findAll) ap.flags2 |= eAF2::FindAll;\n\t\tif (getRects) ap.flags2 |= eAF2::GetRects;\n\t\tHRESULT R;\n\n\t\tassert(!!w == !aParent);\n\t\tassert(!ap.resultProp || !findAll);\n\t\tassert(!getRects || findAll);\n\n\t\tif (useWnd) {\n\t\t\tif ((ap.flags2 & (eAF2::InWebPage | eAF2::InChromePage | eAF2::InFirefoxPage | eAF2::InIES)) == eAF2::InWebPage) {\n\t\t\t\t//this is used only if could not detect browser type by w classname\n\t\t\t\tbool chrome = false;\n\t\t\t\tHWND c = wn::FindChildByClassName(w, c_IES, c_CRW, OUT chrome, true);\n\t\t\t\tif (c) {\n\t\t\t\t\tap.flags2 |= chrome ? eAF2::InChromePage : eAF2::InIES;\n\t\t\t\t\tif (!chrome) w = c; //info: in Internet Explorer the web browser control is in another process.\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tCpp_Acc_Agent aAgent;\n\t\tif (inProc && useWnd) {\n\t\t\tIAccessible* iagent = null;\n\t\t\tif (0 != (R = InjectDllAndGetAgent(w, out iagent))) {\n\t\t\t\tswitch ((eError)R) {\n\t\t\t\tcase eError::WindowOfThisThread: case eError::UseNotInProc: case eError::Inject: break;\n\t\t\t\tdefault: return R;\n\t\t\t\t}\n\t\t\t\tinProc = false;\n\t\t\t} else {\n\t\t\t\taAgent.acc = iagent;\n\t\t\t\taParent = &aAgent;\n\t\t\t}\n\t\t\t//Perf.Next();\n\t\t}\n\n\t\tif (inProc) {\n\t\t\tInProcCall ic;\n\t\t\tauto sizeofParams = MarshalParams_AccFind::CalcMemSize(ref ap);\n\t\t\tauto p = (MarshalParams_AccFind*)ic.AllocParams(aParent, InProcAction::IPA_AccFind, sizeofParams);\n\t\t\tp->Marshal(useWnd ? w : 0, ref ap);\n\n\t\t\tif (0 != (R = ic.Call())) {\n\t\t\t\tif (R == (HRESULT)eError::InvalidParameter) sResult = ic.DetachResultBSTR();\n\t\t\t} else if (!findAll) {\n\t\t\t\tif (!ap.resultProp) R = ic.ReadResultAcc(ref aResult);\n\t\t\t\telse if (ap.resultProp != '-') sResult = ic.DetachResultBSTR();\n\t\t\t} else {\n\t\t\t\tCpp_Acc a;\n\t\t\t\tRECT rect = {};\n\t\t\t\tint skip = ap.skip;\n\t\t\t\tfor (;;) {\n\t\t\t\t\tR = ic.ReadResultAcc(ref a, false, &rect);\n\t\t\t\t\tif (R) break; //NotFound when end of stream\n\t\t\t\t\tif (!also(a, &rect)) continue; //must Release u.acc, preferably later\n\t\t\t\t\tif (skip-- == 0) {\n\t\t\t\t\t\ta.acc->AddRef();\n\t\t\t\t\t\taResult = a;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t//release the marshal data of remaining AO\n\t\t\t\tfor (auto k = R; k == 0; ) k = ic.ReadResultAcc(ref a, true, &rect);\n\t\t\t}\n\t\t\t//Perf.Next();\n\t\t} else {\n\t\t\tap.flags2 |= eAF2::NotInProc;\n\t\t\tbool found = false;\n\t\t\tint skip = ap.skip;\n\t\t\tstd::vector<_AccRect> agr;\n\t\t\tRECT rWin = {}; if (getRects) GetWindowRect(w, &rWin);\n\n\t\t\tR = AccFind(\n\t\t\t\t[&](Cpp_Acc a, int state, int nSiblings) mutable {\n\t\t\t\t\tif (getRects) {\n\t\t\t\t\t\tif (!(ap.flags & eAF::HiddenToo)) {\n\t\t\t\t\t\t\tif (_AccRect::Skip(ref a, state)) return eAccFindCallbackResult::SkipChildren;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tRECT rect = { };\n\t\t\t\t\t\tif (0 != ao::accLocation(out rect, a.acc, a.elem)) return eAccFindCallbackResult::Continue;\n\t\t\t\t\t\tif (!IntersectRect(&rect, &rect, &rWin)) return eAccFindCallbackResult::Continue;\n\t\t\t\t\t\tagr.emplace_back(a, rect, state, nSiblings);\n\t\t\t\t\t\ta.acc->AddRef();\n\t\t\t\t\t\treturn eAccFindCallbackResult::Continue;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (also) {\n\t\t\t\t\t\ta.acc->AddRef(); //of proxy (fast)\n\t\t\t\t\t\tif (!also(a, null)) return eAccFindCallbackResult::Continue;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (skip-- > 0) return eAccFindCallbackResult::Continue;\n\t\t\t\t\tfound = true;\n\n\t\t\t\t\tif (ap.resultProp) {\n\t\t\t\t\t\tif (ap.resultProp != '-') AccGetProp(a, ap.resultProp, out sResult);\n\t\t\t\t\t} else {\n\t\t\t\t\t\taResult = a;\n\t\t\t\t\t\ta.acc->AddRef();\n\t\t\t\t\t}\n\n\t\t\t\t\treturn eAccFindCallbackResult::StopFound;\n\t\t\t\t}, w, aParent, ref ap, out sResult);\n\n\t\t\tif (getRects) {\n\t\t\t\tif (R != (HRESULT)eError::NotFound) return R;\n\t\t\t\t_AccRect::FilterRects(ref agr);\n\t\t\t\tfor (auto& k : agr) {\n\t\t\t\t\tif (k.nSiblings > 0) { //else removed\n\t\t\t\t\t\talso(k.a, &k.r);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (R == 0 && !found) R = (HRESULT)eError::NotFound;\n\t\t\t}\n\t\t}\n\t\t//Perf.NW();\n\n\t\treturn R;\n\t}\n\n} //namespace outproc\n"
  },
  {
    "path": "Cpp/acc find.cpp",
    "content": "//TODO2: in some windows not-in-proc finds much slower than QM. Eg 5 times slower in Resource Monitor -> Memory. UIA much slower.\n//\tI guess: maybe looks in hidden child windows, and QM doesn't.\n//\tIn other windows not-in-proc LA similar speed, sometimes 2 times faster.\n\n#include \"stdafx.h\"\n#include \"cpp.h\"\n#include \"acc.h\"\n#include \"IAccessible2.h\"\n\n#pragma comment(lib, \"oleacc.lib\")\n\nbool AccMatchHtmlAttributes(IAccessible* iacc, NameValue* prop, int count);\nbool AccChromeEnableHtml(IAccessible* aDoc);\n\nclass AccFinder {\n\t//these have ctors\n\tAccContext _context; //shared memory buffer and maxcc\n\tstr::Wildex _controlClass; //used when the prop parameter has \"class=x\". Then _flags2 has eAF2::InControls.\n\tstr::Wildex _name; //name. If the name parameter is null, _name.Is()==false.\n\t//Bstr _roleStrings; //a copy of the input role string when eg need to parse (modify) the string\n\tBstr _propStrings; //a copy of the input prop string when eg need to parse (modify) the string\n\tstr::Wildex _url; //Chrome DOCUMENT URL. Specified in the prop parameter. Used by FindDocumentSimple_.\n\n\t//our ctor ZEROTHISFROM(_callback)\n\tAccFindCallback* _callback; //receives found AO\n\tSTR _role; //null if used path or if the role parameter is null\n\tNameValue* _prop; //other string properties and HTML attributes. Specified in the prop parameter, like L\"value=XXX\\0 @id=YYY\".\n\tSTR _controlWF; //WinForms name. Used when the prop parameter has \"id=x\" where x is not a number. Then _flags2 has eAF2::InControls.\n\tSTR* _notin; //when searching, skip descendants of AO of these roles. Specified in the prop parameter.\n\tint _propCount; //_prop array element count\n\tint _notinCount; //_notin array element count\n\tint _controlId; //used when the prop parameter has \"id=x\" wherex x is a number. Then _flags2 has eAF2::InControls|IsId.\n\tint _minLevel, _maxLevel; //min and max level to search in the object subtree. Specified in the prop parameter. Default 0 1000.\n\tint _stateYes, _stateNo; //the AO must have all _stateYes flags and none of _stateNo flags. Specified in the prop parameter.\n\tint _elem; //simple element id. Specified in the prop parameter. _flags2 has IsElem.\n\tRECTWH _rect; //AO location. Specified in the prop parameter. _flags2 has IsRect.\n\teAF _flags; //user\n\teAF2 _flags2; //internal\n\tbool _found; //true when the AO has been found\n\tvoid* _findDOCUMENT; //used by FindDocumentSimple_, else null\n\tBSTR* _errStr; //error string, when a parameter is invalid\n\tHWND _wTL; //window in which currently searching\n\n\tbool _Error(STR es) {\n\t\tif (_errStr) *_errStr = SysAllocString(es);\n\t\treturn false;\n\t}\n\n\tHRESULT _ErrorHR(STR es) {\n\t\t_Error(es);\n\t\treturn (HRESULT)eError::InvalidParameter;\n\t}\n\n\tvoid _ParseNotin(LPWSTR s, LPWSTR eos) {\n\t\t_notinCount = (int)std::count(s, eos, ',') + 1;\n\t\t_notin = new STR[_notinCount];\n\t\tint i = 0;\n\t\tfor (LPWSTR start = s; s <= eos; ) {\n\t\t\tif (*s == ',' || s == eos) {\n\t\t\t\t_notin[i++] = start;\n\t\t\t\t*s++ = 0; if (*s == ' ') s++;\n\t\t\t\tstart = s;\n\t\t\t} else s++;\n\t\t}\n\n\t\t//Print(_notinCount); for(i = 0; i < _notinCount; i++) Print(_notin[i]);\n\t}\n\n\tbool _ParseState(LPWSTR s, LPWSTR eos) {\n\t\tfor (LPWSTR start = s; s <= eos; ) {\n\t\t\tif (*s == ',' || s == eos) {\n\t\t\t\tbool nott = false; if (*start == '!') { start++; nott = true; }\n\t\t\t\tint state;\n\t\t\t\tif (s > start && *start >= '0' && *start <= '9') {\n\t\t\t\t\tLPWSTR se;\n\t\t\t\t\tstate = strtoi(start, &se);\n\t\t\t\t\tif (se != s) return false;\n\t\t\t\t} else {\n\t\t\t\t\tstate = ao::StateFromString(start, s - start);\n\t\t\t\t\tif (state == 0) return _Error(L\"Unknown state name.\");\n\t\t\t\t}\n\t\t\t\tif (nott) _stateNo |= state; else _stateYes |= state;\n\t\t\t\tif (*++s == ' ') s++;\n\t\t\t\tstart = s;\n\t\t\t} else s++;\n\t\t}\n\t\t//Printf(L\"0x%X  0x%X\", _stateYes, _stateNo);\n\t\treturn true;\n\t}\n\n\tbool _ParseRect(LPWSTR s, LPWSTR eos) {\n\t\tif (*s++ != '{' || *(--eos) != '}') goto ge;\n\t\tfor (; s < eos; s++) {\n\t\t\tLPWSTR s1 = s++, s2;\n\t\t\tif (*s++ != '=') goto ge; //FUTURE: support operators < > etc. Also Coord. Example: {L>=0.5 T<^20}\n\t\t\tint t = strtoi(s, &s2);\n\t\t\tif (s2 == s) goto ge; s = s2;\n\t\t\tswitch (*s1) {\n\t\t\tcase 'L': _rect.L = t; _flags2 |= eAF2::IsRectL; break;\n\t\t\tcase 'T': _rect.T = t; _flags2 |= eAF2::IsRectT; break;\n\t\t\tcase 'W': _rect.W = t; _flags2 |= eAF2::IsRectW; break;\n\t\t\tcase 'H': _rect.H = t; _flags2 |= eAF2::IsRectH; break;\n\t\t\tdefault: goto ge;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\tge:\n\t\treturn _Error(L\"Invalid rect format.\");\n\t}\n\n\tbool _ParseProp(STR prop, int propLen) {\n\t\tif (prop == null) return true;\n\n\t\tint elemCount = (int)std::count(prop, prop + propLen, '\\0') + 1;\n\t\t_prop = new NameValue[elemCount]; _propCount = 0; //info: finally can be _propCount<elemCount, ie not all elements used\n\t\tLPWSTR s0 = _propStrings.Assign(prop, propLen), s2, s3;\n\t\tfor (LPWSTR s = s0, na = s0, va = null, eos = s0 + propLen; s <= eos; s++) {\n\t\t\tauto c = *s;\n\t\t\tif (c == 0) {\n\t\t\t\tif (s > s0) {\n\t\t\t\t\tif (va == null) return _Error(L\"Missing = in prop string.\");\n\t\t\t\t\t//Printf(L\"na='%s' va='%s'    naLen=%i vaLen=%i\", na, va, va - 1 - na, s - va);\n\n\t\t\t\t\tbool addToProp = true;\n\t\t\t\t\tif (na[0] != '@') { //HTML attribute names have prefix \"@\"\n\t\t\t\t\t\tint i = str::Switch(na, va - 1 - na, {\n\t\t\t\t\t\t\tL\"value\", L\"desc\", L\"help\", L\"action\", L\"key\", L\"uiaid\", L\"uiacn\", //string props\n\t\t\t\t\t\t\tL\"state\", L\"level\", L\"maxcc\", L\"notin\", L\"rect\", L\"item\",\n\t\t\t\t\t\t\tL\"class\", L\"id\", //control\n\t\t\t\t\t\t\tL\"url\"\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\tif (i == 0) return _Error(L\"Unknown name in prop. For HTML attributes use prefix @.\");\n\t\t\t\t\t\tconst int nStrProp = 7;\n\t\t\t\t\t\tif (i > nStrProp) {\n\t\t\t\t\t\t\ti -= nStrProp;\n\t\t\t\t\t\t\tint len = (int)(s - va); if (len == 0 && i != 8) goto ge; //winforms name can be empty\n\t\t\t\t\t\t\taddToProp = false;\n\t\t\t\t\t\t\tswitch (i) {\n\t\t\t\t\t\t\tcase 1:\n\t\t\t\t\t\t\t\tif (!_ParseState(va, s)) return false;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase 2:\n\t\t\t\t\t\t\t\t_minLevel = strtoi(va, &s2);\n\t\t\t\t\t\t\t\tif (s2 == va || _minLevel < 0) goto ge;\n\t\t\t\t\t\t\t\tif (s2 == s) _maxLevel = _minLevel;\n\t\t\t\t\t\t\t\telse if (s2 < s && *s2 == ' ') {\n\t\t\t\t\t\t\t\t\t_maxLevel = strtoi(++s2, &s3);\n\t\t\t\t\t\t\t\t\tif (s3 != s || _maxLevel < _minLevel) goto ge;\n\t\t\t\t\t\t\t\t} else goto ge;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase 3:\n\t\t\t\t\t\t\t\t_context.maxcc = strtoi(va, &s2);\n\t\t\t\t\t\t\t\tif (_context.maxcc < 1 || s2 != s) goto ge;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase 4:\n\t\t\t\t\t\t\t\t_ParseNotin(va, s);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase 5:\n\t\t\t\t\t\t\t\tif (!_ParseRect(va, s)) return false;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase 6:\n\t\t\t\t\t\t\t\t_elem = strtoi(va, &s2); if (s2 != s) goto ge;\n\t\t\t\t\t\t\t\t_flags2 |= eAF2::IsElem;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase 7:\n\t\t\t\t\t\t\t\tif (!_controlClass.Parse(va, len, true, _errStr)) return false;\n\t\t\t\t\t\t\t\t_flags2 |= eAF2::InControls;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase 8:\n\t\t\t\t\t\t\t\tif (len > 0) {\n\t\t\t\t\t\t\t\t\tint cid = strtoi(va, &s2);\n\t\t\t\t\t\t\t\t\tif (s2 == s) { _controlId = cid; _flags2 |= eAF2::IsId; } else _controlWF = va;\n\t\t\t\t\t\t\t\t} else _controlWF = va;\n\t\t\t\t\t\t\t\t_flags2 |= eAF2::InControls;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase 9:\n\t\t\t\t\t\t\t\tif (!_url.Parse(va, len, true, _errStr)) return false;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (addToProp) {\n\t\t\t\t\t\tassert(_propCount < elemCount);\n\t\t\t\t\t\tNameValue& x = _prop[_propCount++];\n\t\t\t\t\t\tx.name = na;\n\t\t\t\t\t\tif (!x.value.Parse(va, s - va, true, _errStr)) return false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\twhile (++s <= eos && *s <= ' '); //allow space before name, eg \"name1=value1\\0 name2=...\"\n\t\t\t\tna = s--;\n\t\t\t\tva = null;\n\t\t\t} else if (c == '=' && va == null) {\n\t\t\t\t*s = 0;\n\t\t\t\tva = s + 1;\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\tge: return _Error(L\"Invalid prop string.\");\n\t}\n\npublic:\n\n\tAccFinder(BSTR* errStr = null) {\n\t\tZEROTHISFROM(_callback);\n\t\t_errStr = errStr;\n\t\t_maxLevel = 1000;\n\t}\n\n\t~AccFinder() {\n\t\tdelete[] _prop;\n\t\tdelete[] _notin;\n\t}\n\n\tbool SetParams(const Cpp_AccFindParams& ap) {\n\t\t_flags = ap.flags;\n\t\t_flags2 = ap.flags2;\n\t\t//if(!_ParseRole(ap.role, ap.roleLength)) return false;\n\t\t_role = ap.role;\n\t\tif (ap.name != null && !_name.Parse(ap.name, ap.nameLength, true, _errStr)) return false;\n\t\tif (!_ParseProp(ap.prop, ap.propLength)) return false;\n\n\t\tif (!!(_flags2 & eAF2::InWebPage)) {\n\t\t\t_flags |= eAF::MenuToo;\n\t\t\tif (!!(_flags & (eAF::UIA | eAF::ClientArea))\n\t\t\t\t|| !!(_flags2 & eAF2::InControls)\n\t\t\t\t) return _Error(L\"role prefix cannot be used with: flag UIA, flag ClientArea, prop 'class', prop 'id'.\");\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tHRESULT Find(HWND w, const Cpp_Acc* a, AccFindCallback* callback) {\n\t\tassert(!!w == !a);\n\t\t_callback = callback;\n\n\t\tif (a) {\n\t\t\tif (!!(_flags2 & eAF2::InWebPage)) return _ErrorHR(L\"Don't use role prefix when searching in elm.\");\n\t\t\tif (!!(_flags2 & eAF2::InControls)) return _ErrorHR(L\"Don't use class/id when searching in elm.\");\n\t\t\tassert(!(_flags & (eAF::UIA | eAF::ClientArea))); //checked in C#\n\n\t\t\t_FindInAcc(ref * a, 0);\n\t\t} else if (!!(_flags2 & eAF2::InWebPage)) {\n\t\t\tif (!!(_flags2 & eAF2::InIES)) { //info: Cpp_AccFind finds IES control and adds this flag\n\t\t\t\t_FindInWnd(w, false, false);\n\t\t\t\t//info: the hierarchy is WINDOW/CLIENT/PANE, therefore PANE will be at level 0\n\t\t\t} else {\n\t\t\t\tAccDtorIfElem0 aDoc;\n\t\t\t\t//Perf.First();\n\t\t\t\tHRESULT hr = _FindDocument(w, out aDoc);\n\t\t\t\t//Perf.NW();\n\t\t\t\tif (hr) return hr;\n\n\t\t\t\tswitch (_Match(ref aDoc, 0)) {\n\t\t\t\tcase _eMatchResult::SkipChildren: return (HRESULT)eError::NotFound;\n\t\t\t\tcase _eMatchResult::Continue: _FindInAcc(ref aDoc, 1);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tbool isJava = !(_flags & eAF::UIA) && (_role == null || (_role[0] >= 'a' && _role[0] <= 'z')) && wn::ClassNameIs(w, L\"SunAwt*\"); //note: can be control. I know only 1 such app - Sweet Home 3D.\n\t\t\tif (!!(_flags2 & eAF2::InControls)) {\n\t\t\t\twn::EnumChildWindows(w, [this, w, isJava](HWND c) {\n\t\t\t\t\tif (!(_flags & eAF::HiddenToo) && !wn::IsVisibleInWindow(c, w)) return true; //not IsWindowVisible, because we want to find controls in invisible windows\n\t\t\t\t\tif (!!(_flags2 & eAF2::IsId) && GetDlgCtrlID(c) != _controlId) return true;\n\t\t\t\t\tif (_controlClass.Is() && !wn::ClassNameIs(c, _controlClass)) return true;\n\t\t\t\t\tif (_controlWF != null && !wn::WinformsNameIs(c, _controlWF)) return true;\n\t\t\t\t\treturn 0 != _FindInWnd(c, true, isJava && wn::ClassNameIs(c, L\"SunAwt*\"));\n\t\t\t\t\t});\n\t\t\t} else {\n\t\t\t\t_wTL = (wn::Style(w) & WS_CHILD) ? 0 : w;\n\t\t\t\tif (_wTL && !(_flags & eAF::UIA) && !isJava) {\n\t\t\t\t\tif (wn::ClassNameIs(w, L\"Mozilla*\")) _flags2 |= eAF2::InFirefoxNotWebNotUIA; //skip background tabs\n\t\t\t\t}\n\t\t\t\t_FindInWnd(w, false, isJava);\n\t\t\t}\n\t\t}\n\n\t\treturn _found ? 0 : (HRESULT)eError::NotFound;\n\t}\n\nprivate:\n\tHRESULT _FindInWnd(HWND w, bool isControl, bool isJava) {\n\t\tif (isJava) {\n\t\t\tAccDtorIfElem0 aj(AccJavaFromWindow(w), 0, eAccMiscFlags::Java);\n\t\t\tif (aj.acc) return _FindInAcc(ref aj, 0) ? 0 : (HRESULT)eError::NotFound;\n\t\t}\n\n\t\tAccDtorIfElem0 aw;\n\t\tHRESULT hr;\n\t\tif (!!(_flags & eAF::UIA)) {\n\t\t\thr = AccUiaFromWindow(w, &aw.acc);\n\t\t\taw.misc.flags = eAccMiscFlags::UIA;\n\t\t\t//FUTURE: to make faster, add option to use IUIAutomationElement::FindFirst or FindAll.\n\t\t\t//\tProblems: 1. No Level. 2. Cannot apply many flags; then in some cases can be slower or less reliable.\n\t\t\t//\tNot very important. Now fast enough. JavaFX almost same speed (inproc).\n\t\t} else {\n\t\t\tbool inCLIENT = !!(_flags & eAF::ClientArea);\n\t\t\thr = ao::AccFromWindowSR(w, inCLIENT ? OBJID_CLIENT : OBJID_WINDOW, &aw.acc);\n\t\t\taw.misc.roleByte = inCLIENT ? ROLE_SYSTEM_CLIENT : ROLE_SYSTEM_WINDOW; //not important: can be not CLIENT (eg DIALOG)\n\t\t}\n\t\tif (hr) return hr;\n\n\t\t//isControl is true when is specified class or id. Now caller is enumerating controls. Need _Match for control's WINDOW, not only for descendants.\n\t\tint level = 0;\n\t\tif (isControl) {\n\t\t\tswitch (_Match(ref aw, level++)) {\n\t\t\tcase _eMatchResult::Stop: return 0;\n\t\t\tcase _eMatchResult::SkipChildren: goto gnf;\n\t\t\t}\n\t\t}\n\n\t\tif (_FindInAcc(ref aw, level)) return 0; //note: caller also must check _found; this is just for EnumChildWindows.\n\tgnf:\n\t\treturn (HRESULT)eError::NotFound;\n\t}\n\n\t//Returns true to stop.\n\tbool _FindInAcc(const Cpp_Acc& aParent, int level) {\n\t\tint startIndex = 0; bool exactIndex = false;\n\n\t\tAccChildren c(ref _context, ref aParent, startIndex, exactIndex, !!(_flags & eAF::Reverse));\n\t\t//Printf(L\"%i  %i\", level, c.Count());\n\t\tif (c.Count() == 0) {\n\t\t\t//rejected: enable Chrome web AOs. Difficult to implement (lazy, etc). Let use prefix \"web:\".\n\t\t\t//if(_wTL) {\n\t\t\t//\tif(level == (!!(_flags & eAF::ClientArea) ? 0 : 1) && aParent.misc.roleByte == ROLE_SYSTEM_CLIENT) {\n\t\t\t//\t}\n\t\t\t//}\n\t\t\treturn false;\n\t\t}\n\t\tfor (;;) {\n\t\t\tAccDtorIfElem0 aChild;\n\t\t\tif (!c.GetNext(out aChild)) break;\n\n\t\t\tswitch (_Match(ref aChild, level, c.Count())) {\n\t\t\tcase _eMatchResult::Stop: return true;\n\t\t\tcase _eMatchResult::SkipChildren: continue;\n\t\t\t}\n\n\t\t\tif (_FindInAcc(ref aChild, level + 1)) return true;\n\t\t} //now a.a is released if a.elem==0\n\t\treturn false;\n\t}\n\n\tenum class _eMatchResult { Continue, Stop, SkipChildren };\n\n\t_eMatchResult _Match(ref AccDtorIfElem0& a, int level, int nSiblings = 0) {\n\t\tif (_findDOCUMENT && a.elem != 0) return _eMatchResult::SkipChildren;\n\n\t\tbool skipChildren = a.elem != 0 || level >= _maxLevel;\n\t\tbool hiddenToo = !!(_flags & eAF::HiddenToo);\n\t\t_AccState state(ref a);\n\n\t\t_variant_t varRole;\n\t\tBYTE role = a.GetRoleByteAndVariant(out varRole);\n\t\ta.misc.roleByte = role;\n\t\ta.SetLevel(level);\n\n\t\t//a.PrintAcc();\n\n\t\t//skip Firefox background tabs.\n\t\t//\tSearching without \"web:\" could take minutes. With \"web:\" would find wrong DOCUMENT when the fast way fails.\n\t\t//\tIn recent versions: DOCUMENT's state does not have invisible/offscreen flags; parent browser and PROPERTYPEGE have OFFSCREEN.\n\t\t//\tNever mind UIA. Roles Custom/PANE/DOCUMENT instead of PROPERTYPAGE/browser/DOCUMENT. Also cannot be used \"web:\".\n\t\tif (!!(_flags2 & eAF2::InFirefoxNotWebNotUIA) && role == ROLE_SYSTEM_PROPERTYPAGE && !skipChildren && level < 5) {\n\t\t\tif (state.State() & (STATE_SYSTEM_INVISIBLE | STATE_SYSTEM_OFFSCREEN)) {\n\t\t\t\t//Print(level); //2 in Firefox and Thunderbird\n\t\t\t\treturn _eMatchResult::SkipChildren;\n\t\t\t}\n\t\t}\n\n\t\tif (_findDOCUMENT) {\n\t\t\tauto fdr = _FindDocumentCallback(ref a);\n\t\t\tif (skipChildren && fdr == _eMatchResult::Continue) fdr = _eMatchResult::SkipChildren;\n\t\t\treturn fdr;\n\t\t}\n\n\t\tif (!!(_flags2 & eAF2::GetRects)) {\n\t\t\tswitch ((*_callback)(a, state.State(), nSiblings)) {\n\t\t\tcase eAccFindCallbackResult::Continue: return skipChildren ? _eMatchResult::SkipChildren : _eMatchResult::Continue;\n\t\t\tcase eAccFindCallbackResult::SkipChildren: return _eMatchResult::SkipChildren;\n\t\t\t}\n\t\t\treturn _eMatchResult::Stop;\n\t\t}\n\n\t\t//skip children of AO of user-specified roles\n\t\tSTR roleString = null;\n\t\tif (_notin && !skipChildren) {\n\t\t\troleString = ao::RoleToString(ref varRole);\n\t\t\tfor (int i = 0; i < _notinCount; i++) if (!wcscmp(_notin[i], roleString)) {\n\t\t\t\tskipChildren = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tSTR roleNeeded = _role;\n\n\t\tif (level >= _minLevel) {\n\t\t\t//If eAF::Mark, the caller is getting all AO using callback, and wants us to add eAccMiscFlags::Marked to AOs that match role, rect, name and state.\n\t\t\t//\tIf some of these props does not match, we set mark = -1, to avoid comparing other props.\n\t\t\tint mark = !!(_flags & eAF::Mark) ? 1 : 0;\n\t\t\tif (mark && !!(_flags & eAF::Marked_)) {\n\t\t\t\t//Currently the caller needs single marked object. Used internally.\n\t\t\t\t//\tTo make faster, don't compare properties of other objects. Some AO are very slow, eg .NET DataGridView with 10 columns and 1000 rows.\n\t\t\t\tmark = -1;\n\t\t\t}\n\n\t\t\tif (mark >= 0) {\n\t\t\t\tif (roleNeeded != null) {\n\t\t\t\t\tif (!roleString) roleString = ao::RoleToString(ref varRole);\n\t\t\t\t\tif (wcscmp(roleNeeded, roleString)) {\n\t\t\t\t\t\tif (mark) mark = -1;\n\t\t\t\t\t\telse goto gr;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!!(_flags2 & eAF2::IsElem) && a.elem != _elem) goto gr;\n\n\t\t\tif (mark > 0 && !_MatchRect(ref a)) mark = -1;\n\n\t\t\tif (_name.Is() && mark >= 0 && !a.MatchStringProp(L\"name\", ref _name)) {\n\t\t\t\tif (mark) mark = -1; else goto gr;\n\t\t\t}\n\n\t\t\tif (!hiddenToo) {\n\t\t\t\tint iiv = state.IsInvisible();\n\t\t\t\tswitch (iiv) {\n\t\t\t\tcase 2: //INVISISBLE and OFFSCREEN\n\t\t\t\t\tif (!_IsRoleToSkipIfInvisible(role)) break;\n\t\t\t\t\t[[fallthrough]];\n\t\t\t\tcase 1: //only INVISIBLE\n\t\t\t\t\tif (_IsRoleTopLevelClient(role, level)) break; //rare\n\t\t\t\t\treturn _eMatchResult::SkipChildren;\n\t\t\t\t}\n\n\t\t\t\t//never mind: MSAA bug of child windows classnamed \"Windows.UI.Input.InputSite.WindowClass\": WINDOW has INVISIBLE, although descendants are visible.\n\t\t\t\t//\tIt breaks MSAA in some Win11 apps, eg taskbar, terminal, paint. UIA OK.\n\t\t\t\t//  Could apply a workaround here, but:\n\t\t\t\t//\t1. Also need a workaround in \"elm from point\" code. Difficult.\n\t\t\t\t//\t2. The tool auto-switches to UIA, and it's even faster.\n\t\t\t}\n\n\t\t\tif (!!(_stateYes | _stateNo) && mark >= 0) {\n\t\t\t\tint k = state.State();\n\t\t\t\tif ((k & _stateYes) != _stateYes || !!(k & _stateNo)) {\n\t\t\t\t\tif (mark) mark = -1; else goto gr;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!mark && !_MatchRect(ref a))  goto gr;\n\n\t\t\tif (_propCount) {\n\t\t\t\tbool hasHTML = false;\n\t\t\t\tfor (int i = 0; i < _propCount; i++) {\n\t\t\t\t\tNameValue& p = _prop[i];\n\t\t\t\t\tif (p.name[0] == '@') hasHTML = true;\n\t\t\t\t\telse if (!a.MatchStringProp(p.name, ref p.value)) goto gr;\n\t\t\t\t}\n\t\t\t\tif (hasHTML) {\n\t\t\t\t\tif (a.elem || !AccMatchHtmlAttributes(a.acc, _prop, _propCount)) goto gr;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (mark > 0) {\n\t\t\t\ta.misc.flags |= eAccMiscFlags::Marked;\n\t\t\t\t_flags |= eAF::Marked_;\n\t\t\t}\n\n\t\t\tswitch ((*_callback)(a, 0, 0)) {\n\t\t\tcase eAccFindCallbackResult::Continue: goto gr;\n\t\t\tcase eAccFindCallbackResult::SkipChildren: return _eMatchResult::SkipChildren;\n\t\t\tcase eAccFindCallbackResult::StopFound: _found = true;\n\t\t\t\t//case eAccFindCallbackResult::StopNotFound: break;\n\t\t\t}\n\t\t\treturn _eMatchResult::Stop;\n\t\t}\n\tgr:\n\t\tif (!skipChildren) {\n\t\t\t//flag MenuToo\n\t\t\tskipChildren = role == ROLE_SYSTEM_MENUITEM && !(_flags & eAF::MenuToo) && !str::Switch(roleNeeded, { L\"MENUITEM\", L\"MENUPOPUP\" });\n\n\t\t\t//skip children of invisible AO that often have many descendants (eg DOCUMENT, WINDOW)\n\t\t\tif (!skipChildren && !hiddenToo && _IsRoleToSkipIfInvisible(role) && !_IsRoleTopLevelClient(role, level)) skipChildren = state.IsInvisible();\n\t\t}\n\n\t\treturn skipChildren ? _eMatchResult::SkipChildren : _eMatchResult::Continue;\n\t}\n\n\t//Gets AO state.\n\t//The first time calls get_accState. Later returns cached value.\n\tclass _AccState {\n\t\tconst AccRaw& _a;\n\t\tlong _state;\n\tpublic:\n\t\t_AccState(ref const AccRaw& a) : _a(a) { _state = -1; }\n\n\t\tint State() {\n\t\t\tif (_state == -1) _a.get_accState(out _state);\n\t\t\treturn _state;\n\t\t}\n\n\t\t//Returns: 1 INVISIBLE and not OFFSCREEN, 2 INVISIBLE and OFFSCREEN, 0 none.\n\t\tint IsInvisible() {\n\t\t\tswitch (State() & (STATE_SYSTEM_INVISIBLE | STATE_SYSTEM_OFFSCREEN)) {\n\t\t\tcase STATE_SYSTEM_INVISIBLE: return 1;\n\t\t\tcase STATE_SYSTEM_INVISIBLE | STATE_SYSTEM_OFFSCREEN: return 2;\n\t\t\t}\n\t\t\treturn 0;\n\t\t}\n\t};\n\n\tstatic bool _IsRoleToSkipIfInvisible(int roleE) {\n\t\tswitch (roleE) {\n\t\t\t//case ROLE_SYSTEM_MENUBAR: case ROLE_SYSTEM_TITLEBAR: case ROLE_SYSTEM_SCROLLBAR: case ROLE_SYSTEM_GRIP: //nonclient, already skipped\n\t\tcase ROLE_SYSTEM_WINDOW: //child control\n\t\tcase ROLE_SYSTEM_DOCUMENT: //web page in Firefox, Chrome\n\t\tcase ROLE_SYSTEM_PROPERTYPAGE: //page in multi-tab dialog or window\n\t\tcase ROLE_SYSTEM_GROUPING: //eg some objects in Firefox\n\t\tcase ROLE_SYSTEM_ALERT: //eg web browser message box. In Firefox can be some invisible alerts.\n\t\tcase ROLE_SYSTEM_MENUPOPUP: //eg in Firefox.\n\t\t\treturn true;\n\t\t\t//note: these roles must be the same as in elm.IsInvisible\n\t\t}\n\t\treturn false;\n\t\t//note: don't add CLIENT. It is often used as default role. Although in some windows it can make faster.\n\t\t//note: don't add PANE. Too often used for various purposes.\n\n\t\t//problem: some frameworks mark visible offscreen objects as invisible. Eg IE, WPF, Windows controls. Not Firefox, Chrome.\n\t\t//\tCan be even parent marked as invisible when child not. Then we'll not find child if parent's role is one of above.\n\t\t//\tNever mind. This probably will be rare with these roles. Then user can add flag HiddenToo.\n\t\t//\tBut code tools should somehow detect it and add the flag.\n\t}\n\n\t//Returns true if the AO is most likely the client area of the top-level window.\n\tbool _IsRoleTopLevelClient(int role, int level) {\n\t\tif (_wTL && level == 0 && !(_flags & eAF::ClientArea)) {\n\t\t\tswitch (role) {\n\t\t\tcase ROLE_SYSTEM_MENUBAR: case ROLE_SYSTEM_TITLEBAR: case ROLE_SYSTEM_SCROLLBAR: case ROLE_SYSTEM_GRIP: break;\n\t\t\tdefault: return true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tbool _MatchRect(ref AccDtorIfElem0& a) {\n\t\tif (!!(_flags2 & eAF2::IsRect)) {\n\t\t\tlong L, T, W, H;\n\t\t\tif (0 != a.acc->accLocation(&L, &T, &W, &H, ao::VE(a.elem))) L = T = W = H = 0;\n\n\t\t\t//note: _rect is raw AO rect, relative to the screen, not to the window/control/page. Its right/bottom actually are width/height.\n\t\t\t//\tIt is useful when you want to find AO in the object tree when you already have its another IAccessible eg retrieved from point.\n\t\t\t//\tFor example, Delm uses it to select the captured AO in the tree.\n\t\t\t//\tDo not try to make it relative to window etc. Don't need to encourage users to use unreliable ways to find AO.\n\n\t\t\tif (!!(_flags2 & eAF2::IsRectL) && L != _rect.L) return false;\n\t\t\tif (!!(_flags2 & eAF2::IsRectT) && T != _rect.T) return false;\n\t\t\tif (!!(_flags2 & eAF2::IsRectW) && W != _rect.W) return false;\n\t\t\tif (!!(_flags2 & eAF2::IsRectH) && H != _rect.H) return false;\n\t\t}\n\t\treturn true;\n\t}\n\n\t//Finds DOCUMENT of Firefox, Chrome or some other program.\n\t//Returns 0, NotFound.\n\t//Called if _flags2 & eAF2::InWebPage.\n\tHRESULT _FindDocument(HWND w, out AccRaw& ar) {\n\t\tassert(ar.IsEmpty());\n\n\t\tAccDtorIfElem0 ap_;\n\t\tif (0 != AccessibleObjectFromWindow(w, OBJID_CLIENT, IID_IAccessible, (void**)&ap_.acc)) return (HRESULT)eError::NotFound;\n\t\tIAccessible* ap = ap_.acc;\n\n\t\tif (!!(_flags2 & eAF2::InFirefoxPage)) {\n\t\t\t//To get DOCUMENT, use Navigate(0x1009). It is documented and tested on FF>=2.\n\t\t\t_variant_t vNav;\n\t\t\tint hr = ap->accNavigate(0x1009, ao::VE(), out & vNav);\n\t\t\tif (hr == 0 && vNav.vt == VT_DISPATCH && vNav.pdispVal && 0 == vNav.pdispVal->QueryInterface(&ar.acc) && ar.acc) {\n\t\t\t\treturn 0;\n\t\t\t\t//note: don't check BUSY state, it's unreliable.\n\t\t\t}\n\n\t\t\tPRINTS(L\"failed Firefox accNavigate(0x1009). It's OK first time after Firefox starts.\");\n\t\t\t//Fails when calling first time after starting Firefox. FindDocumentSimple_ too. Never mind, it is documented, let use Wait.\n\t\t\t//In some Firefox versions (56, 57) accNavigate(0x1009) is broken.\n\t\t\t//Also occasionally fails in some pages, even if page is loaded, maybe when executing scripts.\n\t\t\t//\tAlso fails in some tool windows, eg Browser Console. Sometimes always fails in full-screen mode.\n\t\t\t//\tThen FindDocumentSimple_ finds it.\n\t\t} else if (!!(_flags2 & eAF2::InChromePage)) {\n\t\t\tif ((wn::Style(w) & WS_CHILD) && wn::ClassNameIs(w, c_CRW)) {\n\t\t\t\t//ao::PrintAcc(ap);\n\t\t\t\tar.acc = ap;\n\t\t\t\tap_.acc = null;\n\t\t\t\treturn 0;\n\t\t\t\t//note: this cannot be used with Chrome. ap is a DOCUMENT, but it's a random document, not the active.\n\t\t\t}\n\n\t\t\t//tested: 0 relations.\n\t\t\t//IAccessible2* aw2 = null;\n\t\t\t//if (QueryService(ap, &aw2, &IID_IAccessible)) {\n\t\t\t//\tlong n = 0; auto r = aw2->get_nRelations(&n);\n\t\t\t//\tPrintf(L\"0x%X, %i\", r, n);\n\t\t\t//\taw2->Release();\n\t\t\t//}\n\t\t}\n\n\t\treturn FindDocumentSimple_(ap, out ar, _flags2, this);\n\t}\n\n\tstruct _DocumentFindData {\n\t\tCComPtr<IAccessible> doc;\n\t\tCComPtr<IAccessible> docs[2];\n\t\tint nDocs = 0;\n\t\tAccFinder* mainFinder = null;\n\t};\n\npublic:\n\t//Finds DOCUMENT with AccFinder::Find. Skips TREE etc.\n\t//Returns 0 or NotFound.\n\tstatic HRESULT FindDocumentSimple_(IAccessible* ap, out AccRaw& ar, eAF2 flags2, AccFinder* mainFinder = null) {\n\t\tAccFinder f;\n\t\t_DocumentFindData d; d.mainFinder = mainFinder;\n\t\tf._findDOCUMENT = &d;\n\t\tf._flags2 = flags2 & (eAF2::InChromePage | eAF2::InFirefoxPage);\n\t\tif (!!(flags2 & eAF2::InFirefoxPage)) f._flags2 |= eAF2::InFirefoxNotWebNotUIA; //skip background tabs\n\t\telse if (!!(flags2 & eAF2::InChromePage)) f._flags |= eAF::Reverse; //~20% faster\n\t\tf._maxLevel = 30;\n\t\tCpp_Acc a(ap, 0);\n\t\tif (0 == f.Find(0, &a, null)) {\n\t\t\tar.acc = d.doc.Detach();\n\t\t\treturn 0;\n\t\t}\n\n\t\t//fbc. If there are no DOCUMENT with URL starting with \"https:\" etc, use the most likely DOCUMENT. New scripts should use prop url instead.\n\t\tif (d.nDocs > 0) {\n\t\t\tif (d.nDocs == 1) {\n\t\t\t\tar.acc = d.docs[0].Detach();\n\t\t\t\treturn 0;\n\t\t\t} else { //2 DOCUMENT. Use the one that matches the window name. Probably the other is the side panel.\n\t\t\t\tHWND w1 = 0;\n\t\t\t\tif (0 == WindowFromAccessibleObject(d.docs[0], &w1)) {\n\t\t\t\t\tif (wn::ClassNameIs(w1, c_CRW)) w1 = GetParent(w1);\n\t\t\t\t\tBstr wName;\n\t\t\t\t\tif (wn::Name(w1, ref wName)) {\n\t\t\t\t\t\tsize_t lenW = wName.Length(), lenA;\n\t\t\t\t\t\tfor (int i = 0; i < 2; i++) {\n\t\t\t\t\t\t\tBstr aName;\n\t\t\t\t\t\t\tif (0 == d.docs[i]->get_accName(ao::VE(), &aName) && (lenA = aName.Length()) > 0 && lenA < lenW && wName[lenA] == ' ' && !wcsncmp(wName, aName, lenA)) {\n\t\t\t\t\t\t\t\tar.acc = d.docs[i].Detach();\n\t\t\t\t\t\t\t\treturn 0;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn (HRESULT)eError::NotFound;\n\n\t\t//when outproc, sometimes fails to get DOCUMENT role while enabling Chrome AOs. The caller will wait/retry.\n\t}\nprivate:\n\n\t//Used by FindDocumentSimple_.\n\t_eMatchResult _FindDocumentCallback(ref const AccRaw& a) {\n\t\t//a.PrintAcc();\n\t\tint role = a.misc.roleByte;\n\t\tif (role == ROLE_SYSTEM_DOCUMENT) {\n\t\t\tlong state; if (0 != a.get_accState(out state) || !!(state & STATE_SYSTEM_INVISIBLE)) return _eMatchResult::SkipChildren;\n\n\t\t\t_DocumentFindData& d = *(_DocumentFindData*)_findDOCUMENT;\n\n\t\t\tif (!!(_flags2 & eAF2::InChromePage)) {\n\t\t\t\t//tested: IAccessible2, ISimpleDOMDocument and IAccessibleApplication can't help to detect document type (web page, side panel etc)\n\t\t\t\t//IAccessible2* aw2 = null;\n\t\t\t\t//if(QueryService(a.acc, &aw2, &IID_IAccessible)) {\n\t\t\t\t//\t//Bstr b;\n\t\t\t\t//\t//auto r = aw2->get_attributes(&b);\n\t\t\t\t//\t//auto r = aw2->get_extendedRole(&b);\n\t\t\t\t//\t//auto r = aw2->get_localizedExtendedRole(&b);\n\t\t\t\t//\t//Printf(L\"0x%X, %s\", r, b.m_str);\n\t\t\t\t//\t//long n = 0; auto r = aw2->get_nExtendedStates(&n);\n\t\t\t\t//\t//long n = 0; auto r = aw2->get_nRelations(&n);\n\t\t\t\t//\t//long n = 0; auto r = aw2->get_uniqueID(&n);\n\t\t\t\t//\t//long n = 0; auto r = aw2->role(&n);\n\t\t\t\t//\t//long n = 0; auto r = aw2->get_indexInParent(&n);\n\t\t\t\t//\t//Printf(L\"0x%X, %i\", r, n);\n\t\t\t\t//\taw2->Release();\n\t\t\t\t//}\n\n\t\t\t\t//ISimpleDOMDocument* isd = null;\n\t\t\t\t//if (QueryService(a.acc, &isd, &IID_IAccessible)) {\n\t\t\t\t//\tBstr s1, s2, s3, s4;\n\t\t\t\t//\tif (0 == isd->get_URL(&s1)) Printf(L\"URL=%s\", s1.m_str);\n\t\t\t\t//\tif (0 == isd->get_title(&s2)) Printf(L\"title=%s\", s2.m_str);\n\t\t\t\t//\tif (0 == isd->get_docType(&s3)) Printf(L\"docType=%s\", s3.m_str);\n\t\t\t\t//\tif (0 == isd->get_mimeType(&s4)) Printf(L\"mimeType=%s\", s4.m_str);\n\t\t\t\t//\t//if (0 == isd->get_nameSpaceURIForID(&s1)) Printf(L\"=%s\", s1.m_str);\n\n\t\t\t\t//\tisd->Release();\n\t\t\t\t//}\n\n\t\t\t\t//IAccessibleApplication* iaa=null;\n\t\t\t\t//if (QueryService(a.acc, &iaa, &IID_IAccessible)) {\n\t\t\t\t//\tBstr s1, s2, s3, s4;\n\t\t\t\t//\tif(0==iaa->get_appName(&s1)) Printf(L\"appName=%s\", s1.m_str);\n\t\t\t\t//\tif(0==iaa->get_appVersion(&s2)) Printf(L\"appVersion=%s\", s2.m_str);\n\t\t\t\t//\tif(0==iaa->get_toolkitName(&s3)) Printf(L\"toolkitName=%s\", s3.m_str);\n\t\t\t\t//\tif(0==iaa->get_toolkitVersion(&s4)) Printf(L\"toolkitVersion=%s\", s4.m_str);\n\t\t\t\t//\tiaa->Release();\n\t\t\t\t//}\n\n\t\t\t\tif (d.mainFinder != null) { //else called by AccEnableChrome2 (can be any DOCUMENT)\n\t\t\t\t\tBstr b;\n\t\t\t\t\tif (!(0 == a.acc->get_accValue(ao::VE(), &b) && b && b.Length() > 0)) return _eMatchResult::SkipChildren;\n\n\t\t\t\t\tif (d.mainFinder->_url.Is()) {\n\t\t\t\t\t\tif (!d.mainFinder->_url.Match(b, b.Length())) return _eMatchResult::SkipChildren;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (b.Length() < 6 || (wcsncmp(b, L\"https:\", 6) && wcsncmp(b, L\"http:\", 5) && wcsncmp(b, L\"file:\", 5))) {\n\t\t\t\t\t\t\t//fbc. If there are no DOCUMENT with URL starting with \"https:\" etc, use the most likely DOCUMENT. New scripts should use prop url instead.\n\t\t\t\t\t\t\tif (d.nDocs < 2 && wcsncmp(b, L\"devtools:\", 9)) {\n\t\t\t\t\t\t\t\td.docs[d.nDocs++] = a.acc; //AddRef\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn _eMatchResult::SkipChildren;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t//note: sync with Delm._IsVisibleWebPage.\n\t\t\t}\n\n\t\t\td.doc = a.acc; //AddRef\n\t\t\t_found = true;\n\t\t\treturn _eMatchResult::Stop;\n\t\t}\n\n\t\tstatic const BYTE b[] = { ROLE_SYSTEM_MENUBAR, ROLE_SYSTEM_TITLEBAR, ROLE_SYSTEM_MENUPOPUP, ROLE_SYSTEM_TOOLBAR,\n\t\t\tROLE_SYSTEM_STATUSBAR, ROLE_SYSTEM_OUTLINE, ROLE_SYSTEM_LIST, ROLE_SYSTEM_SCROLLBAR, ROLE_SYSTEM_GRIP,\n\t\t\tROLE_SYSTEM_SEPARATOR, ROLE_SYSTEM_PUSHBUTTON, ROLE_SYSTEM_TEXT, ROLE_SYSTEM_TOOLTIP,\n\t\t\tROLE_SYSTEM_TABLE,\n\t\t\t//ROLE_SYSTEM_STATICTEXT, //no, eg WebView2 uses a Static container control\n\t\t};\n\t\tfor (int i = 0; i < _countof(b); i++) if (role == b[i]) return _eMatchResult::SkipChildren;\n\t\treturn _eMatchResult::Continue;\n\t}\n};\n\nHRESULT AccFind(AccFindCallback& callback, HWND w, Cpp_Acc* aParent, const Cpp_AccFindParams& ap, out BSTR& errStr) {\n\tAccFinder f(&errStr);\n\tif (!f.SetParams(ref ap)) return (HRESULT)eError::InvalidParameter;\n\treturn f.Find(w, aParent, &callback);\n}\n\nHRESULT AccEnableChrome2(HWND w, int i, HWND c) {\n\tSmart<IAccessible> aw, aDoc;\n\tHRESULT hr;\n\n\tif (i == 0 || c == 0) {\n\t\thr = AccessibleObjectFromWindow(w, OBJID_CLIENT, IID_IAccessible, (void**)&aw);\n\t\tif (hr != 0) return (HRESULT)eError::NotFound;\n\t}\n\n\tif (i == 0) {\n\t\tIAccessible2* aw2 = null;\n\t\tif (QueryService(aw, &aw2, &IID_IAccessible)) aw2->Release();\n\t\t//tested: the IAccessible2 does not have relations or something that would give the document\n\n\t\tSmart<IUIAutomationElement> e1, e2;\n\t\tSmart<IUIAutomationCondition> cond1;\n\t\tif (c) {\n\t\t\tif (0 != UIA()->ElementFromHandle(c, &e1)) return (HRESULT)eError::NotFound;\n\t\t\tUIA()->get_RawViewCondition(&cond1);\n\t\t\te1->FindFirst(TreeScope::TreeScope_Children, cond1, &e2);\n\t\t} else {\n\t\t\tif (0 != UIA()->ElementFromHandle(w, &e1)) return (HRESULT)eError::NotFound;\n\t\t\tUIA()->CreateFalseCondition(&cond1);\n\t\t\te1->FindFirst(TreeScope::TreeScope_Descendants, cond1, &e2);\n\t\t}\n\t}\n\n\tif (c) {\n\t\thr = AccessibleObjectFromWindow(c, OBJID_CLIENT, IID_IAccessible, (void**)&aDoc);\n\t\tif (hr != 0) return (HRESULT)eError::NotFound;\n\t} else { //the child control is detached if the window is not visible to the user, eg completely covered by other windows\n\t\tRECT r;\n\t\tif (GetClientRect(w, &r)) { //note: aDoc.accLocation fails\n\t\t\tMapWindowPoints(w, 0, (LPPOINT)&r, 2);\n\t\t\tint x = (r.left + r.right) / 2, y = r.bottom - 10;\n\t\t\tint j = i - 4;\n\t\t\tif (j >= 0) { //try to find x y in web page. Does not work if in a side panel or devtools.\n\t\t\t\tj %= 16;\n\t\t\t\tr.top += 80; //never mind DPI\n\t\t\t\tint wid = r.right - r.left, hei = r.bottom - r.top;\n\t\t\t\tx = (j % 4) * (wid / 4) + wid / 8 + r.left;\n\t\t\t\ty = (j / 4) * (hei / 4) + hei / 8 + r.top;\n\t\t\t}\n\t\t\tVARIANT v = {};\n\t\t\tif (0 == aw->accHitTest(x, y, &v)) { //need to call at least 2 times\n\t\t\t\tVariantClear(&v);\n\t\t\t}\n\t\t\t//never mind: does not work when the window is minimized.\n\t\t}\n\n\t\tAccRaw ar;\n\t\thr = AccFinder::FindDocumentSimple_(aw, out ar, eAF2::InChromePage);\n\t\tif (hr != 0) return (HRESULT)eError::NotFound;\n\t\taDoc.Attach(ar.acc);\n\t}\n\n\tlong cc = 0;\n\tbool isEnabled = 0 == aDoc->get_accChildCount(&cc) && cc > 0;\n\t//Printf(L\"isEnabled=%i, i=%i\", isEnabled, i);\n\n\tif (i == 0) {\n\t\tBstr name;\n\t\tif (!isEnabled) aDoc->get_accName(ao::VE(), &name); //enables everything except HTML, get_accDefaultAction and maybe more\n\t}\n\n\t//Enable HTML, get_accDefaultAction and maybe some other properties.\n\t//\tElse they will be unavailable when queried first time or several times, until Chrome enables them lazily.\n\t//Note: not before isEnabled true.\n\t//\tThen sometimes no aDoc->get_accValue and ISimpleDOMDocument.URL. Randomly, about every 5-th time, more often with large pages.\n\t//\tValue is very important. Without it fails with \"web:\" etc.\n\tif (isEnabled) {\n\t\t//Bstr action; aDoc->get_accDefaultAction(ao::VE(), &action);\n\t\tAccChromeEnableHtml(aDoc);\n\t\t//any of these enables both\n\n\t\t//rejected: wait.\n\t\t// Unreliable. Can make much slower. Can't rely on AccChromeEnableHtml returning correct result.\n\t\t// If somebody needs HTML etc ASAP, let use --force-renderer-accessibility.\n\t\t// The tool now works well with most pages.\n\t\t//isEnabled = AccChromeEnableHtml(aDoc);\n\t\t//if (!AccChromeEnableHtml(aDoc)) return (HRESULT)eError::WaitChromeEnabledPartially;\n\t}\n\n\treturn isEnabled ? 0 : (HRESULT)eError::WaitChromeDisabled;\n\n\t//note: some of these are obsolete. Eg in current Chrome need to get UIA element of the control and get its children.\n\t//I know these ways to auto-enable Chrome web page AOs:\n\t//1. Call get_accName of any AO, eg of main window or document.\n\t//\tNeed to wait. Also need to call some other functions to enable HTML and all acc properties.\n\t//\tIn current Chrome works alone. In Edge, WebView2 and old Chrome also need 2.\n\t//2. Try to QS IAccessible2 from the client IAccessible of the main window.\n\t//\tIt's undocumented. I found it in Chromium source code, and later on the internet.\n\t//\t\tOne Chromium developer suggested to use it instead of the broken WM_GETOBJECT way.\n\t//  Works inproc and outproc. If outproc, the QS fails, but enables anyway.\n\t//\tIt may not enable by itself. Or very slowly. Then need to call get_accName. It's undocumented.\n\t//\tNeed to wait. The time depends on how big is the webpage. May be even 1 s.\n\t//\tWhen used in certain way, used to kill Chrome process on Win7 (or it depends on Chrome settings etc). Current code works well.\n\t//3. (tested loong time ago) Get a UIA element.\n\t//\tChrome: of main window.\n\t//\tWebView2: of the legacy control. Then call its FindFirst with \"true\" condition. With Chrome it does not work.\n\t//\t\tOr from point.\n\t//\t\tIt enables for entire process (or thread, not tested). Eg the window may have multiple WebView2 controls; need to enable just for one.\n\t//\tBad: UIA at first is slow. And undocumented.\n\t//\tNeed to wait like always.\n\t//4. Edge enables if SPI_SETSCREENREADER. Not Chrome.\n\t//5. (obsolete, now does not work) Send WM_GETOBJECT(0, 1) to the legacy control (child window classnamed \"Chrome_RenderWidgetHostHWND\").\n\t//  Documented: Chrome calls NotifyWinEvent(ALERT) when starts, and enables if then receives WM_GETOBJECT with its arguments.\n\t//\tDoes not work with current Chrome. Maybe works only if called soon after NotifyWinEvent; not tested.\n}\n\nnamespace outproc {\n\tusing Cpp_HelperCallbackT = BOOL(__stdcall*)(int action, HWND w);\n\tstatic Cpp_HelperCallbackT s_helperCallback;\n\n\tEXPORT void Cpp_SetHelperCallback(Cpp_HelperCallbackT callback) {\n\t\ts_helperCallback = callback;\n\t}\n\n\tvoid AccEnableChrome(HWND w, HWND c = 0) {\n\t\tif (!!(WinFlags::Get(w) & eWinFlags::AccEnabled)) return;\n\n\t\tassert(!(wn::Style(w) & WS_CHILD));\n\t\tHWND wTL = w;\n\n\t\tif (c) {\n\t\t\tw = GetParent(c); //eg WebView2\n\t\t\tif (!w) return;\n\t\t\tPRINTS_IF(!wn::ClassNameIs(w, L\"Chrome_WidgetWin_1\"), L\"parent not Chrome_WidgetWin_1\");\n\t\t} else {\n\t\t\tc = wn::FindWndExVisible(w, c_CRW);\n\t\t\tif (!c) { //eg WebView2. //tested: enables in all WebView2 in that window.\n\t\t\t\tc = wn::FindChildByClassName(w, c_CRW, true);\n\t\t\t\tif (c) w = GetParent(c);\n\t\t\t}\n\n\t\t\tif (!c) {\n\t\t\t\t//Ideally should not need to enable, because there is no web content (and no DOCUMENT).\n\t\t\t\t//But need to enable anyway, because:\n\t\t\t\t// 1. In the future Chrome may remove the legacy control.\n\t\t\t\t// 2. Some Chrome/Edge versions detach the control when the browser window isn't visible, eg completely covered by other windows.\n\n\t\t\t\tDWORD style = (DWORD)GetWindowLong(w, GWL_STYLE);\n\t\t\t\tif (0 != (style & WS_POPUP) || style == 0) return; //a popup window, eg menu, tooltip, new bookmark. Or invalid window handle.\n#ifdef TRACE\n\t\t\t\tif (w == GetForegroundWindow()) {\n\t\t\t\t\tPRINTS(L\"no Chrome_RenderWidgetHostHWND in:\");\n\t\t\t\t\twn::PrintWnd(w); //eg Task Manager\n\t\t\t\t}\n#endif\n\t\t\t}\n\t\t}\n\n\t\t//return if Chrome started with --force-renderer-accessibility.\n\t\tif (s_helperCallback(1, wTL)) { //here in C++ we don't have a function to get command line\n\t\t\tWinFlags::Set(wTL, eWinFlags::AccEnabled);\n\t\t\treturn;\n\t\t}\n\t\tauto t1 = GetTickCount64();\n\n\t\t//notinproc FindDocumentSimple_ is very slow\n\t\tCpp_Acc_Agent aAgent;\n\t\tbool inProc = 0 == InjectDllAndGetAgent(w, out aAgent.acc);\n\n\t\tfor (int i = 0, iTo = inProc ? 70 : 25, nNoDoc = 0, nPartially = 0; i < iTo; i++) { //max 3 s\n\t\t\tHRESULT hr;\n\t\t\t//Perf.First();\n\t\t\tif (inProc) {\n\t\t\t\tInProcCall ic;\n\t\t\t\tauto p = (MarshalParams_AccInt4*)ic.AllocParams(&aAgent, InProcAction::IPA_AccEnableChrome, sizeof(MarshalParams_AccInt4));\n\t\t\t\tp->i0 = (int)(LPARAM)w;\n\t\t\t\tp->i1 = i;\n\t\t\t\tp->i2 = (int)(LPARAM)c;\n\t\t\t\thr = ic.Call();\n\t\t\t} else {\n\t\t\t\thr = AccEnableChrome2(w, i, c);\n\t\t\t}\n\t\t\t//Perf.NW();\n\n\t\t\tif (hr == (HRESULT)eError::NotFound) {\n\t\t\t\tif (!IsWindow(w)) return;\n\t\t\t\tif (!c) break;\n\t\t\t}\n\n\t\t\tif (hr == (HRESULT)eError::WaitChromeDisabled && i == iTo - 1 && !c && wTL != GetForegroundWindow())\n\t\t\t\treturn; //allow to retry later. See comments above about detached control.\n\n\t\t\t//if (hr == (HRESULT)eError::WaitChromeEnabledPartially) if (!inProc || !c || nPartially++ >= (i - nPartially) / 2 + 5) hr = 0;\n\n\t\t\tSleep((inProc ? 10 : 100) + i);\n\n\t\t\tif (hr == 0) break;\n\t\t\tif (hr != (HRESULT)eError::NotFound) nNoDoc = 0; else if (++nNoDoc < 20) i = -1; else break;\n\t\t}\n\t\t//never mind: possible timeout with large pages or at Chrome startup. Can't wait so long here. Let scripts wait.\n\n\t\tWinFlags::Set(wTL, eWinFlags::AccEnabled);\n\n\t\tif (GetTickCount64() - t1 > 500) s_helperCallback(2, wTL); //print warning \"start browser with --force-renderer-accessibility\". Regardless whether enabled or not.\n\t}\n\n\t//Called by elmFinder before find or find-wait in window (not in elm).\n\t//If s is a role prefix like \"web:\", detects/returns browser type flags for Cpp_AccFindParams::flags2.\n\t//Else returns 0.\n\t//If detects Chrome, enables its acc.\n\tEXPORT eAF2 Cpp_AccRolePrefix(STR s, int len, HWND w) {\n\t\teAF2 R = {};\n\t\tHWND c = 0;\n\t\tint prefix = str::Switch(s, len, { L\"web\", L\"chrome\", L\"firefox\" });\n\t\tif (prefix > 0) {\n\t\t\tswitch (prefix) {\n\t\t\tcase 1:\n\t\t\t\tR |= eAF2::InWebPage;\n\t\t\t\tif (wn::Style(w) & WS_CHILD) {\n\t\t\t\t\tswitch (wn::ClassNameIs(w, { c_CRW, c_IES })) {\n\t\t\t\t\tcase 1: R |= eAF2::InChromePage; c = w; break;\n\t\t\t\t\tcase 2: R |= eAF2::InIES; break;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tswitch (wn::ClassNameIs(w, { L\"Chrome*\", L\"Mozilla*\" })) {\n\t\t\t\t\tcase 1: R |= eAF2::InChromePage; break;\n\t\t\t\t\tcase 2: R |= eAF2::InFirefoxPage; break;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t//info: if no InIES|InChromePage|InFirefoxPage, Cpp_AccFind will try to find IES or CRW in w and use it instead of w.\n\t\t\t\t//\tNot here, because need to seach in each Cpp_AccFind when waiting.\n\t\t\t\tbreak;\n\t\t\tcase 2:\n\t\t\t\tR |= eAF2::InChromePage | eAF2::InWebPage;\n\t\t\t\tif ((wn::Style(w) & WS_CHILD) && wn::ClassNameIs(w, c_CRW)) c = w;\n\t\t\t\tbreak;\n\t\t\tcase 3:\n\t\t\t\tR |= eAF2::InFirefoxPage | eAF2::InWebPage;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (!!(R & eAF2::InChromePage)) {\n\t\t\t\tif (c) w = GetAncestor(c, GA_ROOT);\n\t\t\t\tAccEnableChrome(w, c);\n\t\t\t}\n\t\t}\n\t\treturn R;\n\t}\n\n}\n"
  },
  {
    "path": "Cpp/acc func.cpp",
    "content": "#include \"stdafx.h\"\n#include \"cpp.h\"\n#include \"acc.h\"\n\nvoid Cpp_Acc::SetRoleByte() { misc.roleByte = ao::GetRoleByte(acc, elem); }\n\n#pragma region navigate\n\nnamespace {\n\n\tstruct NavdirAndCount {\n\t\tint navDir, count;\n\t};\n\n\t//Converts navigation string to NavdirAndCount[n] array. Will need to delete[] it.\n\t//Positive values - NAVDIR_X\n\t//Returns null if s is invalid, eg contains unknown strings or invalid count.\n\tNavdirAndCount* Navig_Parse(STR s, out int& n) {\n\t\tauto len = str::Len(s); if (len < 2) return null;\n\t\tSTR eos = s + len;\n\t\tn = (int)std::count(s, eos, ' ') + 1;\n\t\tauto a = new NavdirAndCount[n];\n\t\tint i = 0;\n\t\tfor (STR start = s; s <= eos; ) {\n\t\t\tif (*s == ' ' || s == eos) {\n\t\t\t\tint navDir, count; STR s2, s3;\n\t\t\t\tif (*start == '#') { //custom, or by numeric value\n\t\t\t\t\tnavDir = strtoi(++start, (LPWSTR*)&s2);\n\t\t\t\t\tif (s2 == start) goto ge;\n\t\t\t\t} else {\n\t\t\t\t\t//find the end of the name part, because it can be followed by a number, like \"child3\" or ne,3\"\n\t\t\t\t\ts2 = start; while (s2 < s && *s2 >= 'a' && *s2 <= 'z') s2++;\n\t\t\t\t\tnavDir = str::Switch(start, s2 - start, { L\"ne\", L\"pr\", L\"fi\", L\"la\", L\"pa\", L\"ch\", L\"next\", L\"previous\", L\"first\", L\"last\", L\"parent\", L\"child\" });\n\t\t\t\t\tif (navDir > 0) navDir += (navDir < 7) ? 4 : -2;\n\t\t\t\t\telse { //rarely supported/used\n\t\t\t\t\t\tnavDir = str::Switch(start, s2 - start, { L\"up\", L\"do\", L\"le\", L\"ri\", L\"down\", L\"left\", L\"right\" });\n\t\t\t\t\t\tif (navDir == 0) goto ge;\n\t\t\t\t\t\tif (navDir >= 5) navDir -= 3;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (s2 < s) {\n\t\t\t\t\tif (*s2 == ',') s2++;\n\t\t\t\t\tcount = strtoi(s2, (LPWSTR*)&s3);\n\t\t\t\t\tif (s3 != s || count == 0 || (count < 0 && navDir != NAVDIR_CHILD)) goto ge;\n\t\t\t\t} else count = 1;\n\t\t\t\tNavdirAndCount x = { navDir, count };\n\t\t\t\ta[i++] = x;\n\t\t\t\tstart = ++s;\n\t\t\t} else s++;\n\t\t}\n\t\tassert(i == n);\n\t\t//for(int i = 0; i < n; i++) Printf(L\"%i %i\", a[i].navDir, a[i].count);\n\t\treturn a;\n\tge:\n\t\tdelete[] a;\n\t\tn = 0;\n\t\treturn null;\n\t}\n\n\t//Compares an AO with other AOs using some properties - rectangle, role.\n\tstruct AccComparer {\n\tprivate:\n\t\tRECT _rect;\n\t\tBstr _roleStr;\n\t\tint _roleInt;\n\t\tint _inited; //0 no, 1 yes, -1 failed\n\tpublic:\n\t\tAccComparer() noexcept { ZEROTHIS; }\n\n\t\t//Gets properties of the AO that will be compared with other AOs.\n\t\t//Does nothing if already done (can be called multiple times).\n\t\t//Returns false if failed to get properties.\n\t\tbool Init(AccRaw a) {\n\t\t\tif (_inited == 0) {\n\t\t\t\t_inited = -1;\n\t\t\t\tif (0 != a.accLocation(out _rect) || IsRectEmpty(&_rect)) return false;\n\t\t\t\tif (!a.GetRoleIntOrString(out _roleInt, out _roleStr.m_str)) return false;\n\t\t\t\t_inited = 1;\n\t\t\t}\n\t\t\treturn _inited == 1;\n\t\t}\n\n\t\t//Compares properties of another AO with properties of the Init AO.\n\t\t//Returns true if they match.\n\t\t//Init must be called and succeeded (asserts).\n\t\tbool Match(AccRaw a) {\n\t\t\tassert(_inited == 1); if (_inited != 1) return false;\n\t\t\tRECT rect2;\n\t\t\tif (0 != a.accLocation(out rect2) || !EqualRect(&rect2, &_rect)) return false;\n\t\t\tint ri; Bstr rs; if (!a.GetRoleIntOrString(out ri, out rs.m_str)) return false;\n\t\t\tif (ri != _roleInt) return false;\n\t\t\tif (rs && !rs.Equals(_roleStr, false)) return false;\n\t\t\treturn true;\n\t\t}\n\t};\n\n\tbool Navig_Alt(AccContext& context, int navDir, AccRaw af, out AccRaw& ar) {\n\t\tar.acc = null; ar.elem = 0;\n\t\tbool R = false;\n\t\tif (navDir == NAVDIR_FIRSTCHILD || navDir == NAVDIR_LASTCHILD) {\n\t\t\tif (af.elem != 0) return false;\n\t\t\tAccChildren c(ref context, ref af, navDir == NAVDIR_FIRSTCHILD ? 1 : -1, true);\n\t\t\tR = c.GetNext(out ar);\n\t\t} else if (navDir == NAVDIR_NEXT || navDir == NAVDIR_PREVIOUS) {\n\n\t\t\t//Get parent, then enum its children to find af by rect/role and get next.\n\t\t\t//\tnote: cannot compare IAccessibles, they always different.\n\n\t\t\tCpp_Acc aParent;\n\t\t\tbool releaseParent;\n\t\t\tif (af.elem != 0) {\n\t\t\t\taParent.acc = af.acc;\n\t\t\t\treleaseParent = false;\n\t\t\t\t//never mind: faster would be just to use af.elem-1 or af.elem+1 (with get_accChildCount) and try get_accChild. But less reliable (for some AO get_accChild fails). Anyway, non-0 af.elem is quite rare.\n\t\t\t} else {\n\t\t\t\tif (0 != ao::get_accParent(af.acc, out aParent.acc)) return false;\n\t\t\t\treleaseParent = true;\n\t\t\t\t//note: will get wrong AO if get_accParent is broken.\n\t\t\t\t//\tFor example WinForms TOOLBAR gets parent WINDOW which does not exist in the tree (must get parent of that WINDOW).\n\t\t\t\t//\t\tThen this func gets WINDOW's child MENUBAR or SCROLLBAR which does not exist in the tree.\n\t\t\t\t//\t\tNoticed it in our editor.\n\t\t\t\t//\tWe cannot detect/workaround it, or it would be too difficult/unreliable/slow. Better let the user try another way.\n\t\t\t}\n\n\t\t\tint retry = false; int foundAt = -1;\n\t\tg1:\n\t\t\t{\n\t\t\t\tAccChildren c(ref context, ref aParent, 0, false, navDir == NAVDIR_PREVIOUS);\n\t\t\t\tAccComparer acomp;\n\t\t\t\tfor (int i = 0;; i++) {\n\t\t\t\t\tAccDtorIfElem0 t;\n\t\t\t\t\tif (!c.GetNext(out t)) break;\n\t\t\t\t\tif (foundAt < 0) {\n\t\t\t\t\t\tif (!acomp.Init(af)) break;\n\t\t\t\t\t\tif (!acomp.Match(t)) continue;\n\t\t\t\t\t\tfoundAt = i;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (t.acc == aParent.acc) releaseParent = false;\n\t\t\t\t\t\tar = t; t.acc = null;\n\t\t\t\t\t\tR = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t//Workaround for bugs of some AO: get_accParent returns WINDOW that does not exist in the tree.\n\t\t\t//Try with parent of aParent.acc.\n\t\t\t//When foundAt==0, aParent.acc has single child, and it is af.\n\t\t\tif (!R && !retry && foundAt == 0 && af.elem == 0 && ao::GetRoleByte(aParent.acc) == ROLE_SYSTEM_WINDOW) {\n\t\t\t\tIAccessible* p2;\n\t\t\t\tif (0 == ao::get_accParent(aParent.acc, out p2)) {\n\t\t\t\t\taParent.acc->Release(); aParent.acc = p2;\n\t\t\t\t\tretry = true; goto g1;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (releaseParent) aParent.acc->Release();\n\t\t}\n\t\t//if(R) Print(\"<><c 0x8000>\" + t.a.ToString(t.elem) + \"</c>\");\n\t\treturn R;\n\t}\n\n\tHRESULT Navig_Parent(const ref Cpp_Acc& af, ref Cpp_Acc& ar) {\n\t\tif (af.elem != 0) {\n\t\t\tar.acc = af.acc; //caller must AddRef if need\n\t\t} else {\n\t\t\tHRESULT hr = ao::get_accParent(af.acc, out ar.acc);\n\t\t\tif (hr != 0) return hr;\n\t\t}\n\t\tar.misc.flags = af.misc.flags & eAccMiscFlags::InheritMask; //InProc | UIA | Java\n\t\tif (af.elem == 0 && !!(af.misc.flags & eAccMiscFlags::Java) && *(void**)ar.acc != *(void**)af.acc) ar.misc.flags &= ~eAccMiscFlags::Java; //\"frame\" -> WINDOW\n\t\treturn 0;\n\t}\n\n\tHRESULT Navig_Hresult(HRESULT hr) {\n\t\tswitch (hr) {\n\t\tcase DISP_E_MEMBERNOTFOUND: case E_NOTIMPL: case E_INVALIDARG: case E_FAIL: case E_NOINTERFACE: hr = 1; break;\n\t\t}\n\t\treturn hr;\n\t}\n\n\tHRESULT Navig_Step(AccContext& context, int navDir, int childIndex, AccRaw af, out AccRaw& ar) {\n\t\tar.acc = null; ar.elem = 0;\n\n\t\tif (navDir == NAVDIR_PARENT) return Navig_Parent(ref af, ref ar);\n\n\t\tif (af.elem != 0) if (navDir == NAVDIR_CHILD || navDir == NAVDIR_FIRSTCHILD || navDir == NAVDIR_LASTCHILD) return 1;\n\n\t\tHRESULT hr = 0;\n\t\tif (navDir == NAVDIR_CHILD) {\n\t\t\tAccChildren c(ref context, ref af, childIndex, true);\n\t\t\tif (!c.GetNext(out ar)) hr = 1;\n\t\t\t//note: for it cannot be used get_accChild. Its purpose is different. It accepts child id, not child index, which may be not the same.\n\t\t} else {\n\t\t\thr = af.Navigate(navDir, out ar);\n\t\t\tif (hr != 0 && !(af.misc.flags & (eAccMiscFlags::UIA | eAccMiscFlags::Java))) {\n\t\t\t\t//Perf.First();\n\t\t\t\tif (Navig_Alt(ref context, navDir, af, out ar)) hr = 0;\n\t\t\t\t//Perf.NW();\n\t\t\t}\n\t\t}\n\t\tassert((hr != 0) == (ar.acc == null));\n\t\tif (hr == 0) {\n\t\t\tar.misc.flags = af.misc.flags & eAccMiscFlags::InheritMask; //InProc | UIA | Java\n\t\t}\n\t\treturn hr;\n\t}\n\n} //namespace\n\nHRESULT AccNavigate(Cpp_Acc aFrom, STR navig, out Cpp_Acc& aResult) {\n\taResult.Zero();\n\tint n;\n\tauto a = Navig_Parse(navig, out n);\n\tif (a == null) return (HRESULT)eError::InvalidParameter;\n\n\tHRESULT hr = 0;\n\tAccContext context;\n\tAccRaw af(aFrom), ar;\n\tfor (int i = 0; i < n; i++) {\n\t\tNavdirAndCount x = a[i];\n\t\tfor (int nTimes = (x.navDir == NAVDIR_CHILD) ? 1 : x.count; nTimes > 0; nTimes--, af = ar) {\n\t\t\thr = Navig_Step(ref context, x.navDir, x.count, af, out ar);\n\t\t\tif (af.acc != aFrom.acc && af.acc != ar.acc) af.acc->Release(); //release intermediate AOs\n\t\t\tif (hr != 0) goto gBreak;\n\t\t}\n\t}\ngBreak:\n\tdelete a;\n\t//ar.PrintAcc();\n\tif (hr != 0) return Navig_Hresult(hr);\n\tif (ar.acc == aFrom.acc) ar.acc->AddRef(); //\"pa\" when aFrom.elem!=0, or eg \"fi\" when ar.elem!=0\n\taResult = ar;\n\treturn 0;\n}\n\nnamespace outproc {\n\tEXPORT HRESULT Cpp_AccNavigate(Cpp_Acc aFrom, STR navig, out Cpp_Acc& aResult) {\n\t\tHRESULT hr;\n\n\t\t//optimize \"pa\". Eg used by elm.Parent.\n#if true //only if elem!=0\n\t\tif (aFrom.elem != 0 && navig[0] == 'p' && navig[1] == 'a' && (navig[2] == 0 || 0 == wcscmp(navig, L\"parent\"))) {\n\t\t\taResult.Zero();\n\t\t\tNavig_Parent(ref aFrom, ref aResult);\n\t\t\taFrom.acc->AddRef();\n\t\t\treturn 0;\n\t\t}\n#else //does not work with AccessibleMarshalWrapper: the returned object isn't wrapped and therefore invalid. And somehow slower (tested only with Chrome).\n\t\tif (navig[0] == 'p' && navig[1] == 'a' && (navig[2] == 0 || 0 == wcscmp(navig, L\"parent\"))) {\n\t\t\taResult.Zero();\n\t\t\thr = Navig_Parent(ref aFrom, ref aResult);\n\t\t\tif (hr != 0) return Navig_Hresult(hr);\n\t\t\tif (aResult.acc == aFrom.acc) aFrom.acc->AddRef();\n\t\t\treturn hr;\n\t\t}\n#endif\n\n\t\tif (!(aFrom.misc.flags & eAccMiscFlags::InProc)) {\n\t\t\thr = AccNavigate(aFrom, navig, aResult);\n\t\t} else {\n\t\t\taResult.Zero();\n\t\t\tInProcCall ic;\n\t\t\tauto len = str::Len(navig);\n\t\t\tauto memSize = sizeof(MarshalParams_AccElem) + (len + 1) * 2;\n\t\t\tauto p = (MarshalParams_AccElem*)ic.AllocParams(&aFrom, InProcAction::IPA_AccNavigate, memSize);\n\t\t\tp->elem = aFrom.elem;\n\t\t\tauto s = (LPWSTR)(p + 1); memcpy(s, navig, len * 2); s[len] = 0;\n\t\t\thr = ic.Call();\n\t\t\tif (hr) return hr;\n\t\t\thr = ic.ReadResultAcc(ref aResult);\n\t\t}\n\t\treturn hr;\n\t}\n}\n\n#pragma endregion\n\n#pragma region get prop\n\nHRESULT AccWeb(IAccessible* iacc, STR what, out BSTR& sResult);\n\nHRESULT AccGetProp(Cpp_Acc a, WCHAR prop, out BSTR& sResult) {\n\tsResult = null;\n\tauto acc = a.acc;\n\t_variant_t v; ao::VE ve(a.elem);\n\tSTR role; long state, cc; RECT rect; HWND w;\n\tHRESULT hr = 0;\n\n\tswitch (prop) {\n\tcase 'o': case 'i': case '@':\n\t\tif (a.elem || (a.misc.flags & (eAccMiscFlags::InProc | eAccMiscFlags::UIA | eAccMiscFlags::Java)) != eAccMiscFlags::InProc) return 1;\n\t\tbreak;\n\t}\n\n\tswitch (prop) {\n\tcase 'R':\n\t\thr = acc->get_accRole(ve, &v); if (hr != 0) break;\n\t\trole = ao::RoleToString(ref v);\n\t\tsResult = v.vt == VT_BSTR ? v.Detach().bstrVal : SysAllocString(role);\n\t\tbreak;\n\tcase 'n': hr = acc->get_accName(ve, &sResult); break;\n\tcase 'v': hr = acc->get_accValue(ve, &sResult); break;\n\tcase 'd': hr = acc->get_accDescription(ve, &sResult); break;\n\tcase 'h': hr = acc->get_accHelp(ve, &sResult); break;\n\tcase 'u': //uiaid\n\tcase 'U': //uiacn\n\t\tif (!(a.misc.flags & eAccMiscFlags::UIA)) return 1;\n\t\tve.vt = VT_I1; ve.cVal = (char)prop; hr = acc->get_accHelp(ve, &sResult);\n\t\tbreak;\n\tcase 'a': hr = acc->get_accDefaultAction(ve, &sResult); break;\n\tcase 'k': hr = acc->get_accKeyboardShortcut(ve, &sResult); break;\n\tcase 's':\n\t\thr = ao::get_accState(out state, acc, a.elem);\n\t\tif (hr == 0) sResult = SysAllocStringByteLen((LPCSTR)&state, 4);\n\t\tbreak;\n\tcase 'r':\n\tcase 'D':\n\t\thr = ao::accLocation(out rect, acc, a.elem);\n\t\tif (hr == 0) {\n\t\t\tif (prop == 'D' && !!(a.misc.flags & eAccMiscFlags::InProc)) {\n\t\t\t\tDpiElmScaling des(true, 0, acc);\n\t\t\t\tdes.ScaleIfNeed(ref rect);\n\t\t\t}\n\t\t\tsResult = SysAllocStringByteLen((LPCSTR)&rect, 16);\n\t\t}\n\t\tbreak;\n\tcase 'c':\n\t\tif (a.elem) return 1;\n\t\thr = acc->get_accChildCount(out & cc);\n\t\tif (hr == 0) sResult = SysAllocStringByteLen((LPCSTR)&cc, 4);\n\t\tbreak;\n\tcase 'w':\n\t\thr = WindowFromAccessibleObject(acc, &w);\n\t\tif (hr == 0) sResult = SysAllocStringByteLen((LPCSTR)&w, 4);\n\t\tbreak;\n\tcase 'o':\n\t\thr = AccWeb(acc, L\"'o\", out sResult);\n\t\tbreak;\n\tcase 'i':\n\t\thr = AccWeb(acc, L\"'i\", out sResult);\n\t\tbreak;\n\tcase '@':\n\t\thr = AccWeb(acc, L\"'a\", out sResult);\n\t\tbreak;\n\tdefault: assert(false); hr = (HRESULT)eError::InvalidParameter;\n\t}\n\n\tif (hr != 0) {\n\t\tif (sResult != null) { SysFreeString(sResult); sResult = null; } //rare, but noticed few\n\tswitch (hr) { case DISP_E_MEMBERNOTFOUND: case E_NOTIMPL: hr = 1; break; }\n\t\t\t\t\t\t\t\t\t\t\t//DISP_E_MEMBERNOTFOUND: many many. E_NOTIMPL: many.\n\t\t\t\t\t\t\t\t\t\t\t//note: 0x80070005 (access denied) when trying to get value of a password field.\n\t}\n\treturn hr;\n}\n\nHRESULT AccGetProps(Cpp_Acc a, STR props, out BSTR& sResult) {\n\tsResult = null;\n\tstr::StringBuilder s;\n\ts.AppendChar(0, (int)str::Len(props) * 4); //reserve for offsets\n\tfor (int i = 0; *props; props++, i++) {\n\t\tBstr b;\n\t\tHRESULT hr = AccGetProp(a, *props, out b.m_str);\n\t\t((int*)(LPWSTR)s)[i] = (int)s.Length(); //offset\n\t\tif (hr) {\n\t\t\tif (hr == (HRESULT)eError::InvalidParameter) return hr;\n\t\t\tcontinue;\n\t\t}\n\t\ts.AppendBSTR(b);\n\t}\n\tsResult = s.ToBSTR();\n\treturn 0;\n}\n\nnamespace outproc {\n\t//note: don't need inproc to call methods of AO that were found inproc.\n\t//\tCall simply. Everything works as if we explicitly call inproc.\n\t//\tSame speed, no bugs (toolbar button name, focusing standard controls).\n\t//\tCall inproc only if may need multiple calls (Cpp_AccNavigate, Cpp_AccGetProps) or if works only inproc (Cpp_AccWeb).\n\n\t//this is called only for standard string props, like Name, Value. Not for rect, HTML attributes, etc.\n\tEXPORT HRESULT Cpp_AccGetStringProp(Cpp_Acc a, WCHAR prop, out BSTR& sResult) {\n\t\treturn ::AccGetProp(a, prop, sResult);\n\t}\n\n\tEXPORT HRESULT Cpp_AccGetProps(Cpp_Acc a, STR props, out BSTR& sResult) {\n\t\tif (!(a.misc.flags & eAccMiscFlags::InProc)) return AccGetProps(a, props, out sResult);\n\n\t\tsResult = null;\n\t\tInProcCall ic;\n\t\tauto len = str::Len(props);\n\t\tauto memSize = sizeof(MarshalParams_AccElem) + (len + 1) * 2;\n\t\tauto p = (MarshalParams_AccElem*)ic.AllocParams(&a, InProcAction::IPA_AccGetProps, memSize);\n\t\tp->elem = a.elem;\n\t\tauto s = (LPWSTR)(p + 1); memcpy(s, props, len * 2); s[len] = 0;\n\t\tHRESULT hr = ic.Call();\n\t\tif (hr) return hr;\n\t\tsResult = ic.DetachResultBSTR();\n\t\treturn 0;\n\t}\n\n\t//If a found inproc, gets raw rect (DPI-unscaled). To get physical rect use Cpp_AccGetProps('D').\n\tEXPORT HRESULT Cpp_AccGetRect(Cpp_Acc a, out RECT& r) {\n\t\treturn ao::accLocation(out r, a.acc, a.elem);\n\t}\n\n\tEXPORT HRESULT Cpp_AccGetRole(Cpp_Acc a, out int& roleInt, out BSTR& roleStr) {\n\t\troleInt = 0; roleStr = null;\n\t\tassert(a.misc.roleByte == 0 || a.misc.roleByte == ROLE_CUSTOM);\n\t\t_variant_t vr;\n\t\tHRESULT hr = ao::GetRoleIntAndVariant(a.acc, a.elem, out roleInt, out vr);\n\t\tif (hr == 0 && vr.vt == VT_BSTR) {\n\t\t\tao::RoleToString(ref vr); //lcase if need\n\t\t\troleStr = vr.Detach().bstrVal;\n\t\t}\n\t\treturn hr;\n\t}\n\n\tEXPORT HRESULT Cpp_AccGetInt(Cpp_Acc a, WCHAR what, out long& R) {\n\t\tR = 0;\n\t\tswitch (what) {\n\t\tcase 's': return ao::get_accState(out R, a.acc, a.elem);\n\t\tcase 'c': return a.acc->get_accChildCount(&R);\n\t\tcase 'w': goto g1;\n\t\t}\n\t\treturn E_INVALIDARG;\n\tg1:\n\t\t//Outproc WindowFromAccessibleObject is very slow when the AO retrieved using navigation.\n\t\t//\tThen MSAA walks ancestors until finds WINDOW. Makes many RPC if outproc.\n\n\t\tif (!(a.misc.flags & eAccMiscFlags::InProc)) {\n\t\t\tHWND w; HRESULT hr = WindowFromAccessibleObject(a.acc, &w);\n\t\t\tif (hr == 0) R = (long)(LPARAM)w;\n\t\t\treturn hr;\n\t\t}\n\n\t\tInProcCall ic;\n\t\tic.AllocParams(&a, InProcAction::IPA_AccGetWindow, sizeof(MarshalParams_Header));\n\t\tHRESULT hr = ic.Call();\n\t\tif (hr) return hr;\n\t\tR = *(long*)ic.GetResultBSTR();\n\t\treturn 0;\n\t}\n\n\tnamespace {\n\t\tvoid AGS_Add(Cpp_Acc a, ref VARIANT& v, ref CSimpleArray<AccRaw>& t) {\n\t\t\tAccRaw r;\n\t\t\tif (0 == r.FromVARIANT(a.acc, ref v, true)) {\n\t\t\t\tif (r.elem) r.acc->AddRef();\n\t\t\t\tr.misc.flags = a.misc.flags & eAccMiscFlags::InheritMask;\n\t\t\t\tt.Add(r);\n\t\t\t}\n\t\t}\n\t}\n\n\tEXPORT HRESULT Cpp_AccGetSelection(Cpp_Acc a, out BSTR& sResult) {\n\t\t//info: here we don't call inproc because usually the speed is not important.\n\n\t\tsResult = null;\n\t\tassert(a.elem == 0);\n\t\t_variant_t v;\n\t\tHRESULT hr = a.acc->get_accSelection(&v);\n\t\tif (hr == 0 && v.vt != 0) {\n\t\t\tCSimpleArray<AccRaw> t;\n\t\t\tif (v.vt == VT_UNKNOWN) {\n\t\t\t\tSmart<IEnumVARIANT> e;\n\t\t\t\tif (0 == v.punkVal->QueryInterface(&e)) {\n\t\t\t\t\tfor (;;) {\n\t\t\t\t\t\t_variant_t vv; ULONG n;\n\t\t\t\t\t\tif (e->Next(1, &vv, &n) || n != 1) break;\n\t\t\t\t\t\tAGS_Add(a, ref vv, ref t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tAGS_Add(a, ref v, ref t);\n\t\t\t}\n\t\t\tif (t.GetSize() > 0) sResult = SysAllocStringLen((STR)t.GetData(), t.GetSize() * sizeof(AccRaw));\n\t\t}\n\n\t\treturn hr;\n\t}\n} //namespace outproc\n\n#pragma endregion\n\n#pragma region actions\n\nnamespace outproc {\n\t//action: 'a' accDoDefaultAction, 'v' put_accValue(param), 's' scrollto (only UIA)\n\tEXPORT HRESULT Cpp_AccAction(Cpp_Acc a, WCHAR action, BSTR param) {\n\t\tVARIANT ve = {}; ve.vt = VT_I4; ve.lVal = a.elem;\n\t\tHRESULT hr;\n\t\tswitch (action) {\n\t\tcase 'a':\n\t\t\tif (param) { //Java action\n\t\t\t\tassert(!!(a.misc.flags & eAccMiscFlags::Java));\n\t\t\t\tve.vt = VT_BSTR; ve.bstrVal = param;\n\t\t\t}\n\t\t\thr = a.acc->accDoDefaultAction(ve);\n\t\t\tbreak;\n\t\tcase 'v':\n\t\t\thr = a.acc->put_accValue(ve, param);\n\t\t\tbreak;\n\t\tcase 's': //ScrollTo\n\t\tcase 'c': //Check\n\t\tcase 'E': //Expand(true)\n\t\tcase 'e': //Expand(false)\n\t\t\tassert(!!(a.misc.flags & eAccMiscFlags::UIA));\n\t\t\tve.vt = VT_I1; ve.cVal = (char)action;\n\t\t\thr = a.acc->accDoDefaultAction(ve);\n\t\t\tbreak;\n\t\tdefault: assert(false); hr = (HRESULT)eError::InvalidParameter;\n\t\t}\n\n\t\tif (hr == DISP_E_MEMBERNOTFOUND) hr = 1;\n\t\treturn hr;\n\t}\n\n\tEXPORT HRESULT Cpp_AccSelect(Cpp_Acc a, long flagsSelect) {\n\t\treturn a.acc->accSelect(flagsSelect, ao::VE(a.elem));\n\t}\n}\n\n#pragma endregion\n"
  },
  {
    "path": "Cpp/acc get.cpp",
    "content": "#include \"stdafx.h\"\n#include \"cpp.h\"\n#include \"acc.h\"\n\nnamespace outproc {\n\tvoid AccEnableChrome(HWND w, HWND c = 0);\n}\n\nnamespace {\n\n\teSpecWnd _IsSpecWnd(HWND wTL, HWND w) {\n\t\tif (w != wTL) {\n\t\t\tswitch (wn::ClassNameIs(w, { c_CRW, L\"Chrome_WidgetWin_1\" })) {\n\t\t\tcase 1: return eSpecWnd::ChromeControl; //eg WebView2\n\t\t\tcase 2: return eSpecWnd::ChromeControl2;\n\t\t\t}\n\t\t}\n\t\tint i = wn::ClassNameIs(wTL, { L\"Chrome*\", L\"SunAwt*\", L\"SAL*FRAME\", L\"Mozilla*\" });\n\t\tif (i > 0 && w != wTL && i != (int)_IsSpecWnd(w, w)) i = 0; //if control, ignore if classname not similar\n\t\treturn (eSpecWnd)i;\n\t}\n\n\tbool _IsContainer(BYTE role) {\n\t\tswitch (role) {\n\t\tcase ROLE_SYSTEM_APPLICATION:\n\t\tcase ROLE_SYSTEM_CLIENT:\n\t\tcase ROLE_SYSTEM_DIALOG:\n\t\t\t//case ROLE_SYSTEM_DOCUMENT: //often either empty or contains many slow elements\n\t\tcase ROLE_SYSTEM_GROUPING:\n\t\tcase ROLE_SYSTEM_PAGETABLIST:\n\t\tcase ROLE_SYSTEM_PANE:\n\t\tcase ROLE_SYSTEM_PROPERTYPAGE:\n\t\tcase ROLE_SYSTEM_WINDOW:\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tbool _IsStaticTextImage(int role, IAccessible* iacc) {\n\t\tif (role == ROLE_SYSTEM_STATICTEXT || role == ROLE_SYSTEM_GRAPHIC) return true;\n\t\tif (role != ROLE_SYSTEM_TEXT) return false;\n\t\tlong state = 0;\n\t\treturn 0 == ao::get_accState(state, iacc) && 0 == (state & (STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_UNAVAILABLE));\n\t}\n\n\tvoid _FromPoint_GetLink(ref IAccessible*& a, ref long& elem, ref BYTE& role, bool isUIA) {\n\t\t//note: the child AO of LINK/BUTTON can be anything except LINK/BUTTON, although usually TEXT, STATICTEXT, IMAGE.\n\t\tif (ao::IsLinkOrButton(role)) return;\n\t\tIAccessible* parent = null;\n\t\tif (elem != 0) parent = a; else if (0 != ao::get_accParent(a, out parent)) return;\n\t\tBYTE role2 = ao::GetRoleByte(parent);\n\t\tbool useParent = ao::IsLinkOrButton(role2);\n\t\tif (!useParent) {\n\t\t\tswitch (role2) {\n\t\t\tcase ROLE_SYSTEM_STATICTEXT:\n\t\t\t\tuseParent = role == ROLE_SYSTEM_STATICTEXT; //eg WPF label control\n\t\t\t\tbreak;\n\t\t\tcase 0: case ROLE_CUSTOM: case ROLE_SYSTEM_GROUPING: case ROLE_SYSTEM_GRAPHIC:\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tif (!isUIA || role2 == ROLE_SYSTEM_LISTITEM || role2 == ROLE_SYSTEM_OUTLINEITEM || role2 == ROLE_SYSTEM_MENUITEM) {\n\t\t\t\t\tif (_IsStaticTextImage(role, a)) {\n\t\t\t\t\t\tlong cc = 0;\n\t\t\t\t\t\t//Perf.First();\n\t\t\t\t\t\t//get_accChildCount can be very slow if UIA, eg in Firefox big pages.\n\t\t\t\t\t\t//\tTODO3: if UIA, try something faster, eg get next/previous sibling of a. See UIAccessible::accNavigate.\n\t\t\t\t\t\tuseParent = 0 == parent->get_accChildCount(&cc) && cc == 1;\n\t\t\t\t\t\t//Perf.NW();\n\t\t\t\t\t\tif (useParent) {\n\t\t\t\t\t\t\tBstr bn;\n\t\t\t\t\t\t\tuseParent = (0 == parent->get_accName(ao::VE(), &bn)) && bn && bn.Length() > 0;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (useParent) {\n\t\t\t//bug in old Chrome and secondary windows of Firefox: AO retrieved with get_accParent is invalid, eg cannot get its window.\n\t\t\tHWND wp;\n\t\t\tif (elem == 0 && 0 != WindowFromAccessibleObject(parent, &wp)) {\n\t\t\t\tPRINTF(L\"Cannot get parent LINK because WindowFromAccessibleObject would fail.\");\n\t\t\t\tuseParent = false;\n\t\t\t}\n\t\t\tif (useParent) {\n\t\t\t\tif (elem != 0) elem = 0; else util::Swap(ref a, ref parent);\n\t\t\t\trole = role2;\n\t\t\t}\n\t\t}\n\t\tif (parent != a) parent->Release();\n\t\t//rejected: support > 1 level. The capturing tool in C# supports it.\n\t}\n\n\t//Get UIA element from same point. If its size is < 0.5 than of the AO, use it. Dirty, but in most cases works well.\n\tbool _FromPoint_UiaWorkaround(POINT p, ref Smart<IAccessible>& iacc, ref long& elem, BYTE role) {\n\t\t//note: don't ignore when has children. Eg can be WINDOW, and its child count is not 0.\n\n\t\tSmart<IAccessible> auia;\n\t\tif (0 != AccUiaFromPoint(p, &auia)) return false; //speed: same inproc and outproc. AOFP usually inproc faster, outproc similar or slower.\n\t\t//Perf.Next('u');\n\n\t\tao::VE ve; long x1, y1, x2, y2, wid1, hei1, wid2, hei2;\n\t\tif (0 != iacc->accLocation(&x1, &y1, &wid1, &hei1, ao::VE(elem)) || 0 != auia->accLocation(&x2, &y2, &wid2, &hei2, ve)) return false;\n\t\t__int64 sq1 = (__int64)wid1 * hei1, sq2 = (__int64)wid2 * hei2;\n\t\tif (!(sq2 < sq1 / 2 && sq2 > 0)) return false;\n\n\t\t//auia may be DOCUMENT with rect = the visible page area, whereas iacc is eg a much bigger GROUPING.\n\t\tif (ao::GetRoleByte(auia, 0) == ROLE_SYSTEM_DOCUMENT) return false;\n\n\t\t//Printf(L\"{%i %i %i %i} {%i %i %i %i} 0x%X 0x%X\", x1, y1, wid1, hei1, x2, y2, wid2, hei2, role, ao::GetRoleByte(auia, 0));\n\n\t\t//TODO3: although smaller, in some cases it can be not a descendant. Often cannot detect it reliably. Some reasons:\n\t\t// 1. UIA filters out some objects.\n\t\t// 2. UIA sometimes gets different rect for same object. Eg clips treeitem if part of its text is offscreen.\n\t\t//Print(\"--------\");\n\t\t//ao::PrintAcc(iacc);\n\t\t//for(IAccessible* a = auia, *pa = null; ; a = pa) {\n\t\t//\tao::PrintAcc(a);\n\t\t//\tbool ok = 0 == a->get_accParent((IDispatch**)&pa);\n\t\t//\tif(a != auia) a->Release();\n\t\t//\tif(!ok) return false;\n\t\t//\tif(//ao::GetRoleByte(pa) == role && //no, can be different, eg iacc CLIENT but pa WINDOW\n\t\t//\t\t0 == pa->accLocation(&x2, &y2, &wid2, &hei2, ve) && x2 == x1 && y2 == y1 && wid2 == wid1 && hei2 == hei1) { //does not work too\n\t\t//\t\tPrint(\"OK\");\n\t\t//\t\tpa->Release();\n\t\t//\t\tbreak;\n\t\t//\t}\n\t\t//}\n\n\t\tiacc.Swap(ref auia);\n\t\telem = 0;\n\t\treturn true;\n\t}\n\n#define E_FP_RETRY 0x2001\n\n\t//Sometimes, while we are injecting dll etc, window from point changes. Then need to retry.\n\t//\tElse can be incorrect result; in some cases Delm and this thread can deadlock, eg when AccUiaFromPoint tries to get element from Delm.\n\tbool _FromPoint_ChangedWindow(HWND w, POINT p) {\n\t\tHWND w2 = WindowFromPoint(p);\n\t\tif (w2 == w || GetWindowThreadProcessId(w2, nullptr) == GetCurrentThreadId()) return false;\n\t\t//wn::PrintWnd(w2);\n\t\tPRINTS_IF(IsWindowVisible(w) && GetKeyState(1) >= 0, L\"changed window from point. It's OK if occasionally, eg when resizing or moving.\"); //often when closing or resizing w\n\t\treturn true;\n\t\t//This isn't fast and very reliable, but probably better than nothing. Maybe will need to reject after more testing.\n\t\t//\tWindow from point can change between WindowFromPoint and AccUiaFromPoint. Never seen, but possible.\n\t\t//\tPhysicalToLogicalPoint isn't perfect. Bugs, 1-pixel error.\n\t}\n#define RETRY_IF_CHANGED_WINDOW if(inProc && _FromPoint_ChangedWindow(wFP, p)) return E_FP_RETRY\n\n\tHRESULT _AccFromPoint(POINT p, HWND wFP, eXYFlags flags, eSpecWnd specWnd, out Cpp_Acc& aResult) {\n\t\t//Perf.First();\n\t\tSmart<IAccessible> iacc; long elem = 0;\n\t\teAccMiscFlags miscFlags = {};\n\t\tBYTE role = 0;\n\t\tbool inProc = !(flags & eXYFlags::NotInProc);\n\tg1:\n\t\tRETRY_IF_CHANGED_WINDOW;\n\t\t//Perf.Next('w');\n\t\tif (!!(flags & eXYFlags::UIA)) {\n\t\t\tHRESULT hr = AccUiaFromPoint(p, &iacc);\n\t\t\tif (hr != 0) return hr;\n\t\t\tmiscFlags |= eAccMiscFlags::UIA;\n\t\t\t//Perf.Next('p');\n\t\t} else {\n\t\t\tVARIANT v;\n\t\t\tHRESULT hr = AccessibleObjectFromPoint(p, &iacc, &v);\n\t\t\tif (hr == 0 && !iacc) hr = E_FAIL;\n\t\t\tif (hr != 0) { //rare. Examples: treeview in HtmlHelp 2; Windows Security. Then UIA works.\n\t\t\t\tif (!!(flags & eXYFlags::OrUIA)) { flags |= eXYFlags::UIA; goto g1; }\n\t\t\t\treturn hr;\n\t\t\t}\n\t\t\tassert(v.vt == VT_I4 || v.vt == 0);\n\t\t\telem = v.vt == VT_I4 ? v.lVal : 0;\n\t\t\t//Perf.Next('p');\n\n\t\t\trole = ao::GetRoleByte(iacc, elem);\n\t\t\t//Perf.Next('r');\n\n\t\t\t//UIA?\n\t\t\tif (specWnd == eSpecWnd::None && role != ROLE_CUSTOM && !!(flags & eXYFlags::OrUIA) && _IsContainer(role)) { //and ignore elem\n\t\t\t\tRETRY_IF_CHANGED_WINDOW;\n\t\t\t\tif (_FromPoint_UiaWorkaround(p, ref iacc, ref elem, role)) {\n\t\t\t\t\tmiscFlags |= eAccMiscFlags::UIA;\n\t\t\t\t\t//PRINTF(L\"switched to UIA.  p={%i, %i}  role=0x%X  w=%i  cl=%s\", p.x, p.y, role, (int)(LPARAM)wFP, GetCommandLineW());\n\t\t\t\t}\n\t\t\t\t//Perf.Next('X');\n\t\t\t}\n\t\t}\n\t\tRETRY_IF_CHANGED_WINDOW;\n\t\t//Perf.Next('w');\n\n\t\tbool isUIA = !!(miscFlags & eAccMiscFlags::UIA);\n\t\tif (isUIA) role = ao::GetRoleByte(iacc);\n\t\tif (!!(flags & eXYFlags::PreferLink)) _FromPoint_GetLink(ref iacc.p, ref elem, ref role, isUIA);\n\t\t//Perf.NW('Z');\n\n\t\taResult.acc = iacc.Detach(); aResult.elem = elem;\n\t\taResult.misc.flags = miscFlags;\n\t\taResult.misc.roleByte = role;\n\t\treturn 0;\n\t}\n\n#pragma comment(lib, \"comctl32.lib\")\n\n\tLRESULT CALLBACK _FromPoint_Subclass(HWND w, UINT m, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) {\n\t\tauto R = DefSubclassProc(w, m, wParam, lParam);\n\t\tif (m == WM_NCHITTEST && R == HTTRANSPARENT) R = HTCLIENT;\n\t\treturn R;\n\t}\n} //namespace\n\nHRESULT AccFromPoint(POINT p, HWND wFP, eXYFlags flags, eSpecWnd specWnd, out Cpp_Acc& aResult) {\n\t//Workaround for: WindowFromPoint (and AccessibleObjectFromPoint etc) works differently inproc.\n\t//\tInproc it sends WM_NCHITTEST and skips that window if returns HTTRANSPARENT.\n\t//\tThen the API gets wrong window, which even can be of another thread (then inproc has no sense).\n\t//\tWorkaround: subclass the window and disable HTTRANSPARENT.\n\t//\t\tBut only if another thread. Else skips controls covered by a transparent groupbox etc.\n\tbool transp = !(flags & eXYFlags::NotInProc)\n\t\t&& SendMessage(wFP, WM_NCHITTEST, 0, MAKELPARAM(p.x, p.y)) == HTTRANSPARENT\n\t\t&& GetWindowThreadProcessId(WindowFromPoint(p), null) != GetCurrentThreadId();\n\tif (transp) SetWindowSubclass(wFP, _FromPoint_Subclass, 1, 0);\n\tHRESULT hr;\n\t__try { hr = _AccFromPoint(p, wFP, flags, specWnd, out aResult); }\n\t__finally { if (transp) RemoveWindowSubclass(wFP, _FromPoint_Subclass, 1); }\n\treturn hr;\n}\n\nnamespace outproc {\n\tEXPORT HRESULT Cpp_AccFromPoint(POINT p, eXYFlags flags, Cpp_AccFromPointCallbackT callback, out Cpp_Acc& aResult) {\n\t\t//Perf.First();\n\t\taResult.Zero();\n\n\t\tHRESULT R;\n\t\tauto flags0 = flags;\n\t\tPOINT p0 = p;\n\n\t\t//About WindowFromPhysicalPoint:\n\t\t//\tOn Win8.1+ it's the same as WindowFromPoint. In DPI-aware thread uses physical point, in unaware logical.\n\t\t//\tOn Win7/8 WindowFromPhysicalPoint uses physical point, WindowFromPoint logical (when in a scaled window).\n\t\tHWND wFP = WindowFromPhysicalPoint(p); //never mind: skips disabled controls. It's even better with Chrome and Firefox.\n\tgRetry:\n\t\tHWND wTL = GetAncestor(wFP, GA_ROOT);\n\t\tif (!wTL) return 1; //let the caller retry\n\n\t\tao::TempSetScreenReader tsr;\n\t\teSpecWnd specWnd = _IsSpecWnd(wTL, wFP);\n\t\tif (specWnd == eSpecWnd::Java) {\n\t\t\tWINDOWINFO wi = { sizeof(wi) };\n\t\t\tif (GetWindowInfo(wFP, &wi) && PtInRect(&wi.rcClient, p)) {\n\t\t\t\tauto ja = AccJavaFromPoint(p, wFP);\n\t\t\t\tif (ja != null) {\n\t\t\t\t\taResult.acc = ja;\n\t\t\t\t\taResult.misc.flags = eAccMiscFlags::Java;\n\t\t\t\t\taResult.misc.roleByte = ROLE_CUSTOM;\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\t//specWnd = eSpecWnd::None;\n\t\t} else if (specWnd == eSpecWnd::Chrome) {\n\t\t\tAccEnableChrome(wTL);\n\t\t\t//note: now can get wrong AO, although the above func waits for new good DOCUMENT (max 3 s).\n\t\t\t//\tChrome updates web page AOs lazily. The speed depends on web page. Can get wrong AO even after loong time.\n\t\t\t//\tOr eg can be good AO, but some its properties are still not set.\n\t\t\t//\tThis func doesn't know what AO must be there, and cannot wait.\n\t\t\t//\tInstead, where need such reliability, the caller script can eg wait for certain AO (role LINK etc) at that point.\n\t\t} else if (specWnd == eSpecWnd::ChromeControl) {\n\t\t\tAccEnableChrome(wTL, wFP);\n\t\t} else if (specWnd == eSpecWnd::OO) { //OpenOffice, LibreOffice\n\t\t\ttsr.Set(wTL);\n\t\t\t//OpenOffice bug: crashes on exit if AccessibleObjectFromPoint or AccessibleObjectFromWindow called with SPI_GETSCREENREADER.\n\t\t\t//\tCould not find a workaround.\n\t\t\t//\tInspect.exe too.\n\t\t\t//\tDoes not if SPI_GETSCREENREADER was when starting OO.\n\t\t\t//\tDoes not if we get certain AO (eg DOCUMENT), eiher from point or when searching, now or later. Crashes eg if the AO is a toolbar button.\n\t\t\t//\tTested only with Writer.\n\t\t\t//\ttested: inproc does not help.\n\t\t\t//\tThis info is old, maybe now something changed. Anyway, OpenOffice often crashes when using its AO.\n\t\t}\n\n\t\t//The caller may want to modify flags depending on window. Also need to detect DPI-scaled windows (I don't want to duplicate the code here).\n\t\t//\tUse callback because this func can retry with another window.\n\t\tflags = callback(flags0, wFP, wTL);\n\t\tif (!!(flags & eXYFlags::Fail_)) return 1;\n\n\t\t//If the window is DPI-scaled, if inproc, convert physical to logical point.\n\t\tif (!!(flags & eXYFlags::DpiScaled_)) {\n\t\t\tassert(dlapi.minWin81 ? !(flags & eXYFlags::NotInProc) : !!(flags & eXYFlags::UIA));\n\t\t\tp = p0;\n\t\t\tif (!dlapi.PhysicalToLogicalPoint(wFP, &p)) {\n\t\t\t\tPRINTS(L\"PhysicalToLogicalPoint failed\");\n\t\t\t\treturn E_FAIL;\n\t\t\t\t//The API fails when:\n\t\t\t\t//\t- The point is not in the window. After WindowFromPhysicalPoint the window could be moved, resized or closed.\n\t\t\t\t//\t\tHere could retry WindowFromPhysicalPoint. But it never happened when testing. Never mind.\n\t\t\t\t//\t- The DPI-scaled window is in 2 screens and the point is in certain area in wrong screen.\n\t\t\t\t//\t\tNever mind. It's rare and usually temporary.\n\t\t\t\t//\t\tWith flags NotInProc+UIA (Win8.1+) often works, but not always. In this case it's better to fail.\n\t\t\t\t//API bug: in some cases when the window is in 2 screens, the API scales the point although the window isn't scaled.\n\t\t\t\t//\tGood: on Win10 then skips this code because the callback reliably detects DPI-scaled windows.\n\t\t\t}\n\t\t\t//Printf(L\"phy={%i %i}  log={%i %i}\", p0.x, p0.y, p.x, p.y);\n\t\t}\n\t\t//How 'from point' and 'get rect' API work with DPI-scaled windows:\n\t\t//Win10 and 8.1:\n\t\t//\tMSAA/inproc - good. For 'from point' need logical coord. After 'get rect' need to convert logical to physical.\n\t\t//\tMSAA/notinproc - bad, random, unusable.\n\t\t//\tUIA/inproc - good. For 'from point' need logical coord. After 'get rect' need to convert logical to physical.\n\t\t//\tUIA/notinproc - good.\n\t\t//Win7 and 8.0:\n\t\t//\tMSAA/inproc - good in most cases. With some elements bad 'get rect', especially if found not by 'from point'.\n\t\t//\tMSAA/notinproc - same as inproc.\n\t\t//\tUIA/inproc - bad, random, almost unusable. For 'from point' need logical coord. Randomly bad 'get rect' (we don't scale it).\n\t\t//\tUIA/notinproc - same as inproc.\n\n\tgNotinproc:\n\t\tif (!!(flags & eXYFlags::NotInProc)) {\n\t\t\tif (!!(flags & eXYFlags::DpiScaled_)) {\n\t\t\t\tif (dlapi.minWin81) flags |= eXYFlags::UIA;\n\t\t\t\telse p0 = p; //UIA\n\t\t\t}\n\n\t\t\tR = AccFromPoint(p0, wFP, flags, specWnd, out aResult);\n\t\t\treturn R;\n\t\t}\n\n\t\tCpp_Acc_Agent aAgent;\n\t\tif (0 != (R = InjectDllAndGetAgent(wFP, out aAgent.acc))) {\n\t\t\tswitch ((eError)R) {\n\t\t\tcase eError::WindowOfThisThread: case eError::UseNotInProc: case eError::Inject: break;\n\t\t\tdefault: return R;\n\t\t\t}\n\t\t\tflags |= eXYFlags::NotInProc; goto gNotinproc;\n\t\t}\n\n\t\tInProcCall ic;\n\t\tauto x = (MarshalParams_AccFromPoint*)ic.AllocParams(&aAgent, InProcAction::IPA_AccFromPoint, sizeof(MarshalParams_AccFromPoint));\n\t\tx->p = p;\n\t\tx->flags = flags;\n\t\tx->specWnd = specWnd;\n\t\tx->wFP = (int)(LPARAM)wFP;\n\t\tif (0 != (R = ic.Call())) {\n\t\t\tif (R == E_FP_RETRY) {\n\t\t\t\tHWND w2 = WindowFromPhysicalPoint(p);\n\t\t\t\tif (w2 != wFP) { wFP = w2; goto gRetry; }\n\t\t\t\tflags |= eXYFlags::NotInProc; goto gNotinproc;\n\t\t\t}\n\t\t\treturn R;\n\t\t}\n\t\t//Perf.Next();\n\t\tR = ic.ReadResultAcc(ref aResult);\n\t\t//Perf.NW();\n\t\treturn R;\n\t}\n} //namespace outproc\n\nHRESULT AccGetFocused(HWND w, eFocusedFlags flags, out Cpp_Acc& aResult) {\n\tif (!!(flags & eFocusedFlags::UIA)) {\n\t\tHRESULT hr = AccUiaFocused(&aResult.acc);\n\t\tif (hr != 0) return hr;\n\t\taResult.misc.flags = eAccMiscFlags::UIA;\n\t} else {\n\t\tSmart<IAccessible> aw;\n\t\tHRESULT hr = AccessibleObjectFromWindow(w, OBJID_CLIENT, IID_IAccessible, (void**)&aw);\n\t\tif (hr != 0) return hr;\n\n\t\tAccRaw a1(aw, 0), a2; bool isThis;\n\t\thr = a1.DescendantFocused(out a2, out isThis); if (hr != 0) return hr;\n\t\tif (isThis) {\n\t\t\taw.Detach();\n\t\t\taResult = a1;\n\t\t} else {\n\t\t\taResult = a2;\n\n\t\t\t//never mind: cannot get focused AO of UIA-only windows, eg Java FX.\n\t\t}\n\t}\n\treturn 0;\n}\n\nnamespace outproc {\n\t//w - must be the focused control or window.\n\tEXPORT HRESULT Cpp_AccGetFocused(HWND w, eFocusedFlags flags, out Cpp_Acc& aResult) {\n\t\taResult.Zero();\n\n\t\tHWND wTL = GetAncestor(w, GA_ROOT);\n\t\t//if(!wTL || wTL != GetForegroundWindow()) return 1; //return quickly, anyway would fail. No, does not work with some windows.\n\t\tif (!wTL) return 1;\n\n\t\tao::TempSetScreenReader tsr;\n\t\teSpecWnd specWnd = _IsSpecWnd(wTL, w);\n\t\tif (specWnd == eSpecWnd::Java) {\n\t\t\tauto ja = AccJavaFromWindow(w, true);\n\t\t\tif (ja != null) {\n\t\t\t\taResult.acc = ja;\n\t\t\t\taResult.misc.flags = eAccMiscFlags::Java;\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t} else if (specWnd == eSpecWnd::Chrome) {\n\t\t\tAccEnableChrome(w);\n\t\t} else if (specWnd == eSpecWnd::ChromeControl) {\n\t\t\tAccEnableChrome(wTL, w);\n\t\t} else if (specWnd == eSpecWnd::ChromeControl2) {\n\t\t\t//if WebView, focused is parent control (class Chrome_WidgetWin_1) of the c_CRW control. Need c_CRW to enable AO.\n\t\t\tHWND w2 = wn::FindWndExVisible(w, c_CRW);\n\t\t\tif (w2) AccEnableChrome(wTL, w2);\n\t\t} else if (specWnd == eSpecWnd::OO) { //OpenOffice, LibreOffice\n\t\t\ttsr.Set(wTL);\n\t\t\t//OpenOffice bug: crashes. More info in Cpp_AccFromPoint.\n\t\t}\n\n\tgNotinproc:\n\t\tif (!!(flags & eFocusedFlags::NotInProc)) {\n\t\t\treturn AccGetFocused(w, flags, out aResult);\n\t\t}\n\n\t\tHRESULT R = 0;\n\t\tCpp_Acc_Agent aAgent;\n\t\tif (0 != (R = InjectDllAndGetAgent(w, out aAgent.acc))) {\n\t\t\tswitch ((eError)R) {\n\t\t\tcase eError::WindowOfThisThread: case eError::UseNotInProc: case eError::Inject: break;\n\t\t\tdefault: return R;\n\t\t\t}\n\t\t\tflags |= eFocusedFlags::NotInProc; goto gNotinproc;\n\t\t}\n\n\t\tInProcCall ic;\n\t\tauto x = (MarshalParams_AccFocused*)ic.AllocParams(&aAgent, InProcAction::IPA_AccFocused, sizeof(MarshalParams_AccFocused));\n\t\tx->hwnd = (int)(LPARAM)w;\n\t\tx->flags = flags;\n\t\tif (0 != (R = ic.Call())) return R;\n\t\treturn ic.ReadResultAcc(ref aResult);\n\t}\n} //namespace outproc\n"
  },
  {
    "path": "Cpp/acc java.cpp",
    "content": "#include \"stdafx.h\"\n#include \"cpp.h\"\n#include \"acc.h\"\n#include \"JAB.h\"\n\n//Tested Java Swing apps:\n//\tJava Control Panel. Installed with Java.\n//\tAndroid Studio. Large.\n//\tPyCharm. Similar to Android Studio.\n//\tNetBeans.\n//\tSweet Home 3D. Has a child control with HWND. Tested 32-bit with the ini workaround; failed to start 64-bit.\n//\tmuCommander. Its listviews are detached from tree and cannot be found, but \"from point\" works.\n//\tRuneLite. Has 2 child controls.\n//\tFreeMind, JDiskReport, Art Of Illusion. Nothing special.\n//High DPI: Most are system-aware. PyCharm and muCommander are PM-aware, but need scaling anyway. NetBeans marks itself as DPI-aware but is unscaled.\n//Also would like to test Matlab and some Oracle apps, but failed to download. And not freeware.\n//More can be found here: https://wiki.archlinux.org/title/list_of_applications\n\n//Tested JavaFX apps: BlueJ and two other. For JavaFX need UIA, not JAB. MSAA does not work.\n\nnamespace jab\n{\n_JApi s_api;\n\nstruct _DPI_INFO { int wX, wY, jX, jY; double f; };\nbool _GetDpiInfo(out _DPI_INFO& d, HWND w, long vmID, JObject frame);\n\n#ifdef _DEBUG\nvoid _PrintElem(long vmID, JObject j, STR prefix = L\"\") {\n\tAccessibleContextInfo c;\n\tif(j == 0) Print(\"j 0\");\n\telse if(s_api.getAccessibleContextInfo(vmID, j, &c)) {\n\t\tHWND w = s_api.getHWNDFromAccessibleContext(vmID, j);\n\t\tPrintf(L\"%s%s  {%i %i %i %i}  cc=%i  hwnd=%i\", prefix, c.role_en_US, c.x, c.y, c.width, c.height, c.childrenCount, (int)(LPARAM)w);\n\t} else Print(\"getAccessibleContextInfo failed\");\n}\n\nvoid _PrintElemAndAncestors(long vmID, JObject j) {\n\tfor(auto v = j; v;) {\n\t\t_PrintElem(vmID, v);\n\t\tHWND h = s_api.getHWNDFromAccessibleContext(vmID, v);\n\t\tif(h) wn::PrintWnd(h);\n\t\tauto t = v; v = s_api.getAccessibleParentFromContext(vmID, v);\n\t\tif(t != j) s_api.releaseJavaObject(vmID, t);\n\t}\n}\n#endif\n\nclass JAccessible : public IAccessible\n{\n\tlong _cRef;\n\tlong _vmID;\n\tJObject _jo;\n\tHWND _hwnd; //need this field not only for speed, but also because getHWNDFromAccessibleContext in some cases cannot be used, for example when this object or an ancestor is disconnected from the tree\n\npublic:\n#pragma region ctor, dtor\n\tJAccessible(JObject jo, int vmID, HWND hwnd)\n\t{\n\t\t//PRINTS(L\"JAccessible\");\n\t\t_cRef = 1;\n\t\t_jo = jo; _vmID = vmID; _hwnd = hwnd;\n\t}\n\n\t~JAccessible()\n\t{\n\t\t//PRINTS(L\"~JAccessible\");\n\t\ts_api.releaseJavaObject(_vmID, _jo);\n\t\t_ReleaseObjectInfoCache(); //a new variable may be allocated in the same place soon\n\t}\n#pragma endregion\n\n#pragma region IDispatch\n\tSTDMETHODIMP QueryInterface(REFIID iid, void** ppv)\n\t{\n\t\tif(iid == IID_IAccessible || iid == IID_IDispatch || iid == IID_IUnknown) {\n\t\t\tInterlockedIncrement(&_cRef);\n\t\t\t*ppv = this;\n\t\t\treturn 0;\n\t\t}\n\t\t*ppv = 0;\n\t\treturn E_NOINTERFACE;\n\t}\n\n\tSTDMETHODIMP_(ULONG) AddRef()\n\t{\n\t\treturn InterlockedIncrement(&_cRef);\n\t}\n\n\tSTDMETHODIMP_(ULONG) Release()\n\t{\n\t\tlong ret = InterlockedDecrement(&_cRef);\n\t\tif(!ret) delete this;\n\t\treturn ret;\n\t}\n\n\tSTDMETHODIMP GetTypeInfoCount(UINT* pctinfo) { return E_NOTIMPL; }\n\n\tSTDMETHODIMP GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo) { return E_NOTIMPL; }\n\n\tSTDMETHODIMP GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, __RPC__out_ecount_full(cNames) DISPID* rgDispId) { return E_NOTIMPL; }\n\n\tSTDMETHODIMP Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr) { return E_NOTIMPL; }\n#pragma endregion\n\n#pragma region IAccessible\n\tSTDMETHODIMP get_accParent(IDispatch** ppdispParent)\n\t{\n\t\t//PRINTS(__FUNCTIONW__);\n\t\t*ppdispParent = null;\n\n\t\t//For owned dialogs etc GetAccessibleParentFromContext(dialog) gets a half-valid object of the owner window.\n\t\t//Instead get standard WINDOW object. May need it for WindowFromAccessibleObject.\n\t\tauto w = s_api.getHWNDFromAccessibleContext(_vmID, _jo);\n\t\tif(w) {\n\t\t\treturn AccessibleObjectFromWindow(w, OBJID_WINDOW, IID_IAccessible, (void**)ppdispParent);\n\t\t\t//never mind: if control...\n\t\t}\n\n\t\tauto jo = s_api.getAccessibleParentFromContext(_vmID, _jo); if(!jo) return 1;\n\t\t*ppdispParent = new JAccessible(jo, _vmID, _hwnd); //never mind: may need to update hwnd. Cannot get it reliably.\n\t\treturn 0;\n\t}\n\n\tSTDMETHODIMP get_accChildCount(long* pcountChildren)\n\t{\n\t\t//PRINTS(__FUNCTIONW__);\n\t\tauto k = _GetObjectInfo(); if(k == null) return E_FAIL;\n\t\t*pcountChildren = k->childrenCount;\n\t\treturn 0;\n\t}\n\n\tSTDMETHODIMP get_accChild(VARIANT varChild, IDispatch** ppdispChild)\n\t{\n\t\t//PRINTS(__FUNCTIONW__);\n\t\tint i = varChild.lVal - 1;\n\t\tif(varChild.vt != VT_I4 || i < 0) return E_INVALIDARG;\n\t\tauto jo = s_api.getAccessibleChildFromContext(_vmID, _jo, i);\n\t\tif(jo == 0) {\n\t\t\tauto k = _GetObjectInfo();\n\t\t\tif(k != null && i >= k->childrenCount) return E_INVALIDARG;\n\t\t\treturn E_FAIL;\n\t\t}\n\t\t*ppdispChild = new JAccessible(jo, _vmID, _hwnd); //can use same hwnd, because the tree does not have controls\n\t\treturn 0;\n\t}\n\n\tSTDMETHODIMP get_accName(VARIANT varChild, out BSTR* pszName)\n\t{\n\t\t//PRINTS(__FUNCTIONW__);\n\t\tif(_InvalidVarChildParam(ref varChild)) return E_INVALIDARG;\n\t\tWCHAR m[MAX_STRING_SIZE];\n\t\tif(!s_api.getVirtualAccessibleName(_vmID, _jo, m, MAX_STRING_SIZE)) return E_FAIL;\n\t\t*pszName = SysAllocString(m);\n\t\treturn 0;\n\n\t\t//getVirtualAccessibleName is better than getAccessibleContextInfo (GACI).\n\t\t//It gets names of toolbar buttons. With GACI, name empty, we can use description.\n\t\t//It gets name of related control - adjacent label etc.\n\t\t//Much faster than GACI.\n\t\t//Makes searching ~10% slower when GACI is cached, but not when role used (not called if role does not match).\n\t}\n\n\tSTDMETHODIMP get_accValue(VARIANT varChild, out BSTR* pszValue)\n\t{\n\t\t//PRINTS(__FUNCTIONW__);\n\t\tif(_InvalidVarChildParam(ref varChild)) return E_INVALIDARG;\n\t\tauto k = _GetObjectInfo(); if(k == null) return E_FAIL;\n\t\t*pszValue = null;\n\t\tif(k->accessibleValue) {\n\t\t\tWCHAR m[MAX_STRING_SIZE];\n\t\t\tif(s_api.getCurrentAccessibleValueFromContext(_vmID, _jo, m, MAX_STRING_SIZE)) //speed: 20 times faster than getAccessibleContextInfo\n\t\t\t\t*pszValue = SysAllocString(m);\n\t\t} else {\n\t\t\tAccessibleTextInfo ati;\n\t\t\tif(k->accessibleText && s_api.getAccessibleTextInfo(_vmID, _jo, &ati, 0, 0)) { //slow\n\t\t\t\tauto b = SysAllocStringLen(null, ati.charCount);\n\t\t\t\tif(ati.charCount == 0 || s_api.getAccessibleTextRange(_vmID, _jo, 0, ati.charCount - 1, b, ati.charCount)) *pszValue = b;\n\t\t\t\telse SysFreeString(b);\n\t\t\t}\n\t\t}\n\t\treturn *pszValue == null ? 1 : 0;\n\n\t\t//tested: most objects don't have value/text. Value usually is numeric, eg \"1\" for checked checkbox. Text objects don't have value, therefore we get text instead.\n\t\t//AccessibleContextInfo::accessibleInterfaces flags, from AccessibleBridgePackages.h (cAccessibleValueInterface etc):\n\t\t//\t1 value, 2 actions, 4 component, 8 selection, 0x10 table, 0x20 text, 0x40 hypertext.\n\t}\n\n\tSTDMETHODIMP get_accDescription(VARIANT varChild, out BSTR* pszDescription)\n\t{\n\t\t//PRINTS(__FUNCTIONW__);\n\t\tif(_InvalidVarChildParam(ref varChild)) return E_INVALIDARG;\n\t\tauto k = _GetObjectInfo(); if(k == null) return E_FAIL;\n\t\t*pszDescription = k->description.Copy();\n\t\treturn 0;\n\t}\n\n\tSTDMETHODIMP get_accRole(VARIANT varChild, out VARIANT* pvarRole)\n\t{\n\t\t//PRINTS(__FUNCTIONW__);\n\t\tif(_InvalidVarChildParam(ref varChild)) return E_INVALIDARG;\n\t\tauto k = _GetObjectInfo(); if(k == null) return E_FAIL;\n\t\treturn k->role.CopyTo(pvarRole);\n\t}\n\n\tSTDMETHODIMP get_accState(VARIANT varChild, out VARIANT* pvarState)\n\t{\n\t\t//PRINTS(__FUNCTIONW__);\n\t\tif(_InvalidVarChildParam(ref varChild)) return E_INVALIDARG;\n\t\tauto k = _GetObjectInfo(); if(k == null) return E_FAIL;\n\t\tpvarState->vt = VT_I4;\n\t\tpvarState->lVal = k->state;\n\t\treturn 0;\n\t}\n\n\tSTDMETHODIMP get_accHelp(VARIANT varChild, out BSTR* pszHelp) { return E_NOTIMPL; }\n\n\tSTDMETHODIMP get_accHelpTopic(BSTR* pszHelpFile, VARIANT varChild, long* pidTopic) { return E_NOTIMPL; }\n\n\tSTDMETHODIMP get_accKeyboardShortcut(VARIANT varChild, out BSTR* pszKeyboardShortcut) { return E_NOTIMPL; }\n\n\tSTDMETHODIMP get_accFocus(out VARIANT* pvarChild) { return E_NOTIMPL; }\n\t//Difficult to implement so that would work correctly, ie return direct child even if focused is another descendant.\n\t//getAccessibleContextWithFocus is unstable. More often does not work than works.\n\t//Rarely used.\n\n\tSTDMETHODIMP get_accSelection(out VARIANT* pvarChildren) { return E_NOTIMPL; }\n\t//Rarely used.\n\n\tSTDMETHODIMP get_accDefaultAction(VARIANT varChild, out BSTR* pszDefaultAction)\n\t{\n\t\t//PRINTS(__FUNCTIONW__);\n\t\tif(_InvalidVarChildParam(ref varChild)) return E_INVALIDARG;\n\t\treturn _get_accDefaultAction(out * pszDefaultAction, true);\n\t}\n\n\tHRESULT _get_accDefaultAction(out BSTR& b, bool getList)\n\t{\n\t\tb = null;\n\t\tauto k = _GetObjectInfo(); if(k == null) return E_FAIL;\n\t\tif(!k->accessibleAction) return 1;\n\t\tstd::unique_ptr<AccessibleActions> actions(new(AccessibleActions)); //130 KB\n\t\tHRESULT hr = 1; int n;\n\t\tif(s_api.getAccessibleActions(_vmID, _jo, actions.get()) && (n = actions->actionsCount) > 0 && n <= MAX_ACTION_INFO) {\n\t\t\thr = 0;\n\t\t\tif(!getList || n == 1) b = SysAllocString(actions->actionInfo[0].name);\n\t\t\telse {\n\t\t\t\tstr::StringBuilder t;\n\t\t\t\tfor(int i = 0; i < n; i++) {\n\t\t\t\t\tif(i) t << L\", \";\n\t\t\t\t\tt << actions->actionInfo[i].name;\n\t\t\t\t}\n\t\t\t\tb = t.ToBSTR();\n\t\t\t}\n\t\t}\n\t\treturn hr;\n\t}\n\n\tSTDMETHODIMP accSelect(long flagsSelect, VARIANT varChild)\n\t{\n\t\t//PRINTS(__FUNCTIONW__);\n\t\tif(_InvalidVarChildParam(ref varChild)) return E_INVALIDARG;\n\t\tif(flagsSelect & SELFLAG_EXTENDSELECTION) return E_INVALIDARG;\n\t\tif(flagsSelect & (SELFLAG_TAKESELECTION | SELFLAG_ADDSELECTION | SELFLAG_REMOVESELECTION)) {\n\t\t\tauto k = _GetObjectInfo(); if(k == null) return E_FAIL;\n\t\t\tauto ap = s_api.getAccessibleParentFromContext(_vmID, _jo); if(ap == 0) return E_FAIL;\n\t\t\t_ReleaseObjectInfoCache(); //the action changes object state\n\t\t\tint i = k->indexInParent;\n\t\t\tif(flagsSelect & SELFLAG_TAKESELECTION) s_api.clearAccessibleSelectionFromContext(_vmID, ap);\n\t\t\tif(flagsSelect & (SELFLAG_TAKESELECTION | SELFLAG_ADDSELECTION)) s_api.addAccessibleSelectionFromContext(_vmID, ap, i);\n\t\t\tif(flagsSelect & SELFLAG_REMOVESELECTION) s_api.removeAccessibleSelectionFromContext(_vmID, ap, i);\n\t\t\ts_api.releaseJavaObject(_vmID, ap);\n\t\t}\n\t\tif(flagsSelect & SELFLAG_TAKEFOCUS) {\n\t\t\t_ReleaseObjectInfoCache(); //the action changes object state\n\t\t\tif(!s_api.requestFocus(_vmID, _jo)) return E_FAIL;\n\t\t}\n\t\treturn 0;\n\t}\n\n\tSTDMETHODIMP accLocation(out long* pxLeft, out long* pyTop, out long* pcxWidth, out long* pcyHeight, VARIANT varChild)\n\t{\n\t\t//PRINTS(__FUNCTIONW__);\n\t\tif(_InvalidVarChildParam(ref varChild)) return E_INVALIDARG;\n\t\tauto k = _GetObjectInfo(); if(k == null) return E_FAIL;\n\t\t*pxLeft = k->x; *pyTop = k->y; *pcxWidth = k->width; *pcyHeight = k->height;\n\n\t\t//DPI-scale if need. Without it this func is useless when high DPI.\n\t\t//\tIn all tested apps the API gets logical coord. The physical view is scaled either by OS or by Java. In some not scaled (NetBeans).\n\t\t//\tMakes >2 times slower, eg 4 -> 9 ms. Cannot use any faster ways, because apps use different scaling (offsets etc).\n\t\tlong vmID; AccessibleContext jw;\n\t\tif(s_api.getAccessibleContextFromHWND(_hwnd, &vmID, &jw)) { //note: getTopLevelObject faster, but in some cases gets wrong object\n\t\t\t_DPI_INFO d;\n\t\t\tif(_GetDpiInfo(out d, _hwnd, vmID, jw)) {\n\t\t\t\t*pxLeft = d.wX + (int)round((*pxLeft - d.jX) * d.f);\n\t\t\t\t*pyTop = d.wY + (int)round((*pyTop - d.jY) * d.f);\n\t\t\t\t*pcxWidth = (int)round(*pcxWidth * d.f);\n\t\t\t\t*pcyHeight = (int)round(*pcyHeight * d.f);\n\t\t\t}\n\t\t\ts_api.releaseJavaObject(vmID, jw);\n\t\t}\n\n\t\treturn 0;\n\t}\n\n\tSTDMETHODIMP accNavigate(long navDir, VARIANT varStart, out VARIANT* pvarEndUpAt)\n\t{\n\t\t//Print(\"accNavigate\", navDir, varStart.vt, varStart.value);\n\n\t\t//WindowFromAccessibleObject (WFAO) at first calls this with an undocumented navDir 10.\n\t\t//\ttested: accNavigate(10) for a standard Windows control returns VARIANT(VT_I4, hwnd).\n\t\t//\tIf we return window handle, WFAO does not call get_accParent. Else also calls it for each ancestor.\n\t\tif(navDir == 10 && varStart.vt == VT_I4) {\n\t\t\tpvarEndUpAt->vt = VT_I4;\n\t\t\tpvarEndUpAt->lVal = (int)(LPARAM)_hwnd;\n\t\t\treturn 0;\n\t\t}\n\n\t\tif(navDir < NAVDIR_UP || navDir > NAVDIR_LASTCHILD) return E_INVALIDARG;\n\t\tif(_InvalidVarChildParam(ref varStart)) return E_INVALIDARG;\n\t\tJObject ac = 0;\n\n\t\tif(navDir >= NAVDIR_NEXT && navDir <= NAVDIR_LASTCHILD) {\n\t\t\tint iChild = -1; JObject ap = 0; _JObjectInfo* k = null;\n\t\t\tif(navDir != NAVDIR_FIRSTCHILD && null == (k = _GetObjectInfo())) return 1;\n\t\t\tswitch(navDir) {\n\t\t\tcase NAVDIR_FIRSTCHILD: iChild = 0; ap = _jo; break; //GetAccessibleChildFromContext will fail if this does not have children\n\t\t\tcase NAVDIR_LASTCHILD: iChild = k->childrenCount - 1; ap = _jo; break;\n\t\t\tcase NAVDIR_NEXT: iChild = k->indexInParent + 1; break; //GetAccessibleChildFromContext will fail if this is the last\n\t\t\tcase NAVDIR_PREVIOUS: iChild = k->indexInParent - 1; break;\n\t\t\t}\n\t\t\tif(iChild >= 0) {\n\t\t\t\tif(ap == 0) ap = s_api.getAccessibleParentFromContext(_vmID, _jo);\n\t\t\t\tif(ap != 0) { ac = s_api.getAccessibleChildFromContext(_vmID, ap, iChild); if(ap != _jo) s_api.releaseJavaObject(_vmID, ap); }\n\t\t\t}\n\t\t}\n\n\t\tif(ac == 0) return 1;\n\t\tpvarEndUpAt->pdispVal = new JAccessible(ac, _vmID, _hwnd); //can use same hwnd, because the tree does not have controls\n\t\tpvarEndUpAt->vt = VT_DISPATCH;\n\t\treturn 0;\n\t}\n\n\tSTDMETHODIMP accHitTest(long xLeft, long yTop, out VARIANT* pvarChild) { return E_NOTIMPL; }\n\t//Rarely used.\n\n\tSTDMETHODIMP accDoDefaultAction(VARIANT varChild)\n\t{\n\t\tBSTR name = null; int len = 0; Bstr name_;\n\t\tif(varChild.vt == VT_BSTR) {\n\t\t\tlen = SysStringLen(name = varChild.bstrVal);\n\t\t\tif(len >= SHORT_STRING_SIZE) return E_INVALIDARG;\n\t\t} else if(_InvalidVarChildParam(ref varChild)) return E_INVALIDARG;\n\n\t\tif(len == 0) {\n\t\t\tif(_InvalidVarChildParam(ref varChild)) return E_INVALIDARG;\n\t\t\tint hr = _get_accDefaultAction(out name, false);\n\t\t\tif(hr != 0) return hr == 1 ? DISP_E_MEMBERNOTFOUND : E_FAIL;\n\t\t\tname_.Attach(name);\n\t\t\tlen = name_.Length();\n\t\t}\n\t\t_ReleaseObjectInfoCache(); //the action may change object props\n\t\tAccessibleActionsToDo atd; //16 KB\n\t\tatd.actionsCount = 1;\n\t\tmemcpy(atd.actions, name, (len + 1) * 2);\n\t\tjint failure;\n\t\tif(!s_api.doAccessibleActions(_vmID, _jo, &atd, &failure)) {\n\t\t\t//PRINTI(failure); //0-based index of the first action that failed or is unknown, or -1 if not action-related\n\t\t\treturn E_FAIL;\n\t\t}\n\t\treturn 0;\n\t}\n\n\tSTDMETHODIMP put_accName(VARIANT varChild, BSTR szName) { return E_NOTIMPL; }\n\t//Rarely used, deprecated.\n\n\tSTDMETHODIMP put_accValue(VARIANT varChild, BSTR szValue)\n\t{\n\t\tif(_InvalidVarChildParam(ref varChild)) return E_INVALIDARG;\n\t\tif(!s_api.setTextContents(_vmID, _jo, szValue)) return 1;\n\t\t_ReleaseObjectInfoCache();\n\t\treturn 0;\n\t}\n#pragma endregion\n\n#pragma region get object info\n\n\t//s_api.getAccessibleContextInfo gets multiple properties (name, state, etc) that are then used by multiple JAccessible functions.\n\t//It is slow etc. We call it once and store the retrieved/converted properties in a thread-static variable t_joInfo. Not in JAccessible because big.\n\t//t_joInfo is used for all JAccessible instances, but owned by a single JAccessible instance at a time.\n\n\t//internal\n\tstruct _JObjectInfo\n\t{\n\t\tBstr /*name,*/ description, role;\n\t\tint indexInParent, childrenCount;\n\t\tint x, y, width, height;\n\t\tbool accessibleComponent, accessibleAction, accessibleSelection, accessibleText, accessibleValue;\n\t\tint state;\n\t\tLONGLONG time;\n\t\tJAccessible* owner;\n\n\t\tvoid Init(const AccessibleContextInfo& c)\n\t\t{\n\t\t\t//name = c.name;\n\t\t\tdescription = c.description;\n\t\t\trole = c.role_en_US;\n\t\t\tindexInParent = c.indexInParent; childrenCount = c.childrenCount;\n\t\t\tx = c.x; y = c.y; width = c.width; height = c.height;\n\t\t\taccessibleComponent = c.accessibleComponent != 0; accessibleAction = c.accessibleAction != 0; accessibleSelection = c.accessibleSelection != 0; accessibleText = c.accessibleText != 0;\n\t\t\taccessibleValue = (c.accessibleInterfaces & 1) != 0;\n\n\t\t\tstate = STATE_SYSTEM_READONLY | STATE_SYSTEM_UNAVAILABLE | STATE_SYSTEM_INVISIBLE;\n\t\t\tfor(STR s = c.states_en_US; *s;) {\n\t\t\t\tSTR se = s; while(*se && *se != ',') se++;\n\t\t\t\tswitch(str::Switch(s, (int)(se - s), { L\"busy\",L\"checked\",L\"collapsed\",L\"editable\",L\"enabled\",L\"expanded\",L\"focusable\",L\"focused\",L\"indeterminate\",L\"modal\",L\"multiselectable\",L\"pressed\",L\"resizable\",L\"selectable\",L\"selected\",L\"showing\",L\"visible\" })) {\n\t\t\t\tcase 1: state |= STATE_SYSTEM_BUSY; break;\n\t\t\t\tcase 2: state |= STATE_SYSTEM_CHECKED; break;\n\t\t\t\tcase 3: state |= STATE_SYSTEM_COLLAPSED; break;\n\t\t\t\tcase 4: state &= ~STATE_SYSTEM_READONLY; break;\n\t\t\t\tcase 5: state &= ~STATE_SYSTEM_UNAVAILABLE; break;\n\t\t\t\tcase 6: state |= STATE_SYSTEM_EXPANDED; break;\n\t\t\t\tcase 7: state |= STATE_SYSTEM_FOCUSABLE; break;\n\t\t\t\tcase 8: state |= STATE_SYSTEM_FOCUSED; break;\n\t\t\t\tcase 9: state |= STATE_SYSTEM_INDETERMINATE; break;\n\t\t\t\tcase 10: state |= STATE_SYSTEM_HASPOPUP; break;\n\t\t\t\tcase 11: state |= STATE_SYSTEM_MULTISELECTABLE; break;\n\t\t\t\tcase 12: state |= STATE_SYSTEM_PRESSED; break;\n\t\t\t\tcase 13: state |= STATE_SYSTEM_SIZEABLE; break;\n\t\t\t\tcase 14: state |= STATE_SYSTEM_SELECTABLE; break;\n\t\t\t\tcase 15: state |= STATE_SYSTEM_SELECTED; break;\n\t\t\t\tcase 16: state &= ~STATE_SYSTEM_INVISIBLE; break;\n\t\t\t\tcase 17: if(width > 0 && height > 0) state &= ~STATE_SYSTEM_INVISIBLE; break;\n\t\t\t\t}\n\t\t\t\tif(*se == 0) break;\n\t\t\t\ts = se + 1;\n\t\t\t}\n\t\t}\n\t};\n\n\tstatic _JObjectInfo& __JoInfo() {\n\t\tthread_local static _JObjectInfo t_joInfo;\n\t\treturn t_joInfo;\n\t}\n\n\t_JObjectInfo* _GetObjectInfo()\n\t{\n\t\t_JObjectInfo& k = __JoInfo();\n\t\tif(k.owner != this || GetTickCount64() - k.time > 10) {\n\t\t\tAccessibleContextInfo c;\n\t\t\tif(!s_api.getAccessibleContextInfo(_vmID, _jo, &c)) return null;\n\t\t\tk.Init(ref c);\n\t\t\tk.time = GetTickCount64();\n\t\t\tk.owner = this;\n\t\t}\n\t\treturn &k;\n\t}\n\n\tbool _IsObjectInfoCached()\n\t{\n\t\t_JObjectInfo& k = __JoInfo();\n\t\treturn k.owner == this && GetTickCount64() - k.time <= 10;\n\t}\n\n\tvoid _ReleaseObjectInfoCache()\n\t{\n\t\t_JObjectInfo& k = __JoInfo();\n\t\tif(k.owner == this) k.owner = null;\n\t}\n\n#pragma endregion\n\n#pragma region private\nprivate:\n\tbool _InvalidVarChildParam(const VARIANT& v)\n\t{\n\t\tif(v.vt == 0) return false; //forgive\n\t\treturn v.vt != VT_I4 || v.lVal != 0;\n\t}\n\n\t//HWND _GetHWND()\n\t//{\n\t//\tif(_hwnd) return _hwnd;\n\n\t//\tauto joTL = s_api.getTopLevelObject(_vmID, _jo);\n\t//\tif(joTL != 0) {\n\t//\t\t//_PrintElem(_vmID, joTL);\n\n\t//\t\tHWND w = s_api.getHWNDFromAccessibleContext(_vmID, joTL);\n\t//\t\ts_api.releaseJavaObject(_vmID, joTL);\n\t//\t\treturn _hwnd = w;\n\t//\t}\n\t//\treturn 0;\n\t//}\n#pragma endregion\n};\n\nint s_inited;\n\nDWORD WINAPI _JabThread(LPVOID param)\n{\n\t//How JAB works:\n\t//_InitJab loads JAB dll and functions, and calls Windows_run.\n\t//Windows_run creates a hidden dialog \"Access Bridge status\" and posts \"AccessBridge-FromJava-Hello\" messages to all top-level windows.\n\t//Java JAB-enabled processes also have hidden \"Access Bridge status\" dialogs. They run in a separate thread.\n\t//These dialogs, when received our hello message, post back the message to our dialog. wParam is poster dialog hwnd.\n\t//Only when our dialog receives these messages, we can get accessible context of that Java windows.\n\t//Therefore we must have a message loop etc. We use this thread for it.\n\t//Alternatively, each thread could call Windows_run and DoEvents etc, but then we would have multiple dialogs. Also would need DoEvents before each GetAccessibleContextFromHWND etc, or it would fail with Java windows created later.\n\t//Note: JAB events work only in thread that called Windows_run. Currently not using events. Not tested much. When tested with JavaFerret, most events either didn't work or used to stop working etc.\n\n\tUINT m1 = RegisterWindowMessageW(L\"AccessBridge-FromWindows-Hello\"); //received by our hidden \"Access Bridge status\" dialog once after _InitJab\n\tUINT m2 = RegisterWindowMessageW(L\"AccessBridge-FromJava-Hello\"); //received by the dialog from each JAB-enabled Java window\n\tChangeWindowMessageFilter(m1, 1); ChangeWindowMessageFilter(m2, 1);\n\n\ts_api.Windows_run(); //the slowest part, ~10 ms when warm CPU, else 10-33 ms\n\n\tint i, nmsg; MSG msg;\n\tfor(i = 0; i < 10; i++) {\n\t\tfor(nmsg = 0; PeekMessageW(&msg, 0, 0, 0, PM_REMOVE); nmsg++) DispatchMessageW(&msg);\n\t\tif(nmsg == 0 && i > 0) break; //non-Java windows that use JAB (probably as client) don't post back the message\n\t\tSleep(1);\n\t}\n\tPRINTF_IF(i > 2, L\"i=%i\", i); //test reliability in stress conditions. When testing, was 1 almost always, rarely 2, once >2.\n\n\ts_inited = 1;\n\tSetEvent(param);\n\n\twhile(GetMessageW(&msg, 0, 0, 0) > 0) DispatchMessageW(&msg);\n\ts_inited = 0;\n\n\treturn 0;\n}\n\nbool _InitJab()\n{\n\tif(s_inited == 0) {\n\t\tstatic CComAutoCriticalSection _cs; CComCritSecLock<CComAutoCriticalSection> lock(_cs);\n\t\tif(s_inited == 0) {\n\t\t\ts_inited = -1;\n\t\t\ts_api.Load();\n\t\t\tif(s_api.IsLoaded()) {\n\t\t\t\t//FUTURE: Time.LibSleepPrecision.LibTempSet1(1);\n\t\t\t\tCHandle ev(CreateEventW(null, true, false, null));\n\t\t\t\tDWORD tid;\n\t\t\t\tHANDLE ht = CreateThread(null, 0, _JabThread, ev, 0, &tid);\n\t\t\t\tif(ht) {\n\t\t\t\t\tHANDLE ha[2] = { ev, ht };\n\t\t\t\t\tWaitForMultipleObjects(2, ha, false, INFINITE);\n\t\t\t\t\tCloseHandle(ht);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn s_inited > 0;\n}\n\nbool _NoJab() { return s_inited < 0; }\n\n//Returns true if w process has a JAB hidden dialog (\"Access Bridge status\", \"#32770\").\n//It can be a Java window or a JAB client, but not a window of this process.\n//speed: faster than AccessibleObjectFromWindow. Much faster than _JApi::isJavaWindow, and don't need to load dll etc.\n//w class name must match \"SunAwt*\" (asserts).\nbool _IsJavaWindow(HWND w)\n{\n\t//if(wn::Style(w) & WS_CHILD) return false; //no, some Java windows have controls with similar classname\n\tassert(wn::ClassNameIs(w, L\"SunAwt*\") || !::IsWindow(w));\n\n\tauto wf = WinFlags::Get(w);\n\tif(!!(wf & eWinFlags::AccJavaYes)) return true;\n\tif(!!(wf & eWinFlags::AccJavaNo)) return false;\n\n\tbool yes = false; DWORD pid = 0, pidJAB = 0;\n\tfor(HWND wJAB = 0; wJAB = wn::FindWndEx(0, wJAB, L\"#32770\", L\"Access Bridge status\");) {\n\t\tif(pid == 0 && !GetWindowThreadProcessId(w, &pid)) break;\n\t\tGetWindowThreadProcessId(wJAB, &pidJAB); if(pidJAB != pid) continue;\n\t\tyes = pid != GetCurrentProcessId();\n\t\tbreak;\n\t}\n\n\tWinFlags::Set(w, yes ? eWinFlags::AccJavaYes : eWinFlags::AccJavaNo);\n\treturn yes;\n}\n\nJObject _JObjectFromWindow(HWND w, out long& vmID, bool getFocused = false)\n{\n\tvmID = 0;\n\tif(!_IsJavaWindow(w)) return 0;\n\tif(!_InitJab()) return 0;\n\tJObject jo;\n\tauto f = getFocused ? s_api.getAccessibleContextWithFocus : s_api.getAccessibleContextFromHWND;\n\tif(!f(w, &vmID, &jo)) return 0;\n\n\t//In tested Java window with controls the API gets wrong object. Then cannot find etc. Should be its parent.\n\t//  This code fixes it.\n\t//  Note: don't use getTopLevelObject. It randomly gets the same object as now or the window's root frame (bad).\n\tif(!getFocused && (wn::Style(w) & WS_CHILD)) {\n\t\tAccessibleContextInfo ci;\n\t\tif(s_api.getAccessibleContextInfo(vmID, jo, &ci) && ci.childrenCount == 0) {\n\t\t\tauto pa = s_api.getAccessibleParentFromContext(vmID, jo);\n\t\t\tif(pa) {\n\t\t\t\tint x = ci.x, y = ci.y, wid = ci.width, hei = ci.height;\n\t\t\t\tif(s_api.getAccessibleContextInfo(vmID, pa, &ci) && ci.childrenCount > 0 && ci.x == x && ci.y == y && ci.width == wid && ci.height == hei) {\n\t\t\t\t\tauto bad = jo; jo = pa; s_api.releaseJavaObject(vmID, bad);\n\t\t\t\t} else s_api.releaseJavaObject(vmID, pa);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn jo;\n}\n\nIAccessible* AccFromWindow(HWND w, bool getFocused = false)\n{\n\tif(_NoJab()) return null;\n\tlong vmID;\n\tJObject jo = _JObjectFromWindow(w, out vmID, getFocused); if(jo == 0) return null;\n\treturn new JAccessible(jo, vmID, w);\n}\n\n//Gets window rect info for DPI correction: physical and JAB-logical x, y and scaling factor.\n//Returns false if fails or if don't need DPI correction.\nbool _GetDpiInfo(out _DPI_INFO& d, HWND w, long vmID, JObject frame) {\n\tthread_local static struct _DPI_CACHE {\n\t\tHWND w;\n\t\tRECT rw;\n\t\tLONGLONG time;\n\t\t_DPI_INFO d;\n\t} t_cache; //use cache because getAccessibleContextInfo is slow\n\n\tRECT rw;\n\tif(GetWindowRect(w, &rw)) {\n\t\tif(t_cache.w == w && !memcmp(&t_cache.rw, &rw, 16) && GetTickCount64() - t_cache.time < 5000) {\n\t\t\td = t_cache.d;\n\t\t\treturn d.f != 1.0;\n\t\t}\n\n\t\tAccessibleContextInfo ci;\n\t\tif(s_api.getAccessibleContextInfo(vmID, frame, &ci)) {\n\t\t\t//Printf(L\"{%i %i %i %i}  {%i %i %i %i}\", ci.x, ci.y, ci.width, ci.height, rw.left, rw.top, rw.right-rw.left, rw.bottom-rw.top);\n\t\t\td.wX = rw.left; d.wY = rw.top;\n\t\t\td.jX = ci.x; d.jY = ci.y;\n\t\t\tint wK = (rw.right - rw.left) + (rw.bottom - rw.top), jK = ci.width + ci.height;\n\t\t\tbool scaled = jK != wK && jK >= 16 && wK >= 16;\n\t\t\td.f = scaled ? round((double)wK / jK * 100.0) / 100.0 : 1.0; //eg 1.25123 to 1.25. Never mind very small windows where the error can be too big.\n\n\t\t\tt_cache.w = w;\n\t\t\tt_cache.rw = rw;\n\t\t\tt_cache.time = GetTickCount64();\n\t\t\tt_cache.d = d;\n\n\t\t\treturn scaled;\n\t\t}\n\t}\n\tt_cache.w = 0;\n\treturn false;\n}\n\nbool _IsPointInRect(POINT p, AccessibleContextInfo& c) {\n\treturn p.y >= c.y && p.y < c.y + c.height && p.x >= c.x && p.x < c.x + c.width;\n}\n\nIAccessible* AccFromPoint(POINT p, HWND w)\n{\n\t//bool test = 0 != (GetKeyState(VK_SCROLL) & 1);\n\t//Perf.First();\n\tif(_NoJab()) return null;\n\n\tlong vmID = 0;\n\tJObject jo = 0, ap = _JObjectFromWindow(w, out vmID);\n\tif(ap == 0) return null;\n\t//Perf.Next('w');\n\n\t//DPI-unscale if need\n\t_DPI_INFO d;\n\tif(_GetDpiInfo(out d, w, vmID, ap)) {\n\t\tp.x = (int)round(d.jX + (p.x - d.wX) / d.f);\n\t\tp.y = (int)round(d.jY + (p.y - d.wY) / d.f);\n\t\t//never mind: not perfect. Sometimes 1-pixel error.\n\t\t//Print(\"DPI-unscaled\");\n\t}\n\t//Perf.Next('d');\n\n\t//workaround for JAB bug: if ap is a popup menu, getAccessibleContextAt randomly returns an object from parent window.\n\t//  Especially when high DPI. Depends on mouse movements.\n\t//\tSame with tooltips and other cn=\"SunAwtWindow\" windows. Not with popup menus in cn=\"SunAwtFrame\" windows.\n\t//\tCannot detect whether the object is good or bad. In both cases getTopLevelObject and getAccessibleParentFromContext skip ap.\n\t//\tTherefore even don't try getAccessibleContextAt at first.\n\tif(wn::ClassNameIs(w, L\"SunAwtWindow\")) {\n\t\tstd::function<AccessibleContext(AccessibleContext, int)> func;\n\t\tfunc = [&func, vmID, p](AccessibleContext ac, int level) -> AccessibleContext {\n\t\t\tAccessibleContextInfo c;\n\t\t\tif(!s_api.getAccessibleContextInfo(vmID, ac, &c)) return 0;\n\t\t\tbool notThis = false;\n\t\t\tif(!_IsPointInRect(p, c)) {\n\t\t\t\tif(c.childrenCount == 0 || wcscmp(c.role_en_US, L\"page tab\")) return 0;\n\t\t\t\t//Usually page tab's rect is just the button, and children are in the page below.\n\t\t\t\t//\tAny object can have such children. Eg tree view. But enumerating all would be too slow.\n\t\t\t\tnotThis = true;\n\t\t\t}\n\t\t\tif(!wcsstr(c.states_en_US, L\"showing\")) return 0; //s_api.getVisibleChildren checks this state\n\t\t\t//Printf(L\"%i %i %s  states=%s\", level, c.childrenCount, c.role_en_US, c.states_en_US);\n\t\t\tif(c.childrenCount > 0 && c.childrenCount <= 100 && wcscmp(c.role_en_US, L\"menu\")) {\n\t\t\t\t//Enumerate in reverse order, because:\n\t\t\t\t// 1. Else could find the first child of the root pane, eg in Android Studio Settings.\n\t\t\t\t// 2. Finds links in paragraphs.\n\t\t\t\t// The root pane usually has 2 children, and the first is an invisible zero-rect pane. But in some windows it has properties like visible.\n\t\t\t\t// Actually should be used algorithm \"find smallest object containing p\". This algorithm is \"find first object containing p\".\n\t\t\t\t// But that algorithm would be slower, because need to enum entire tree in all cases.\n\t\t\t\t// What is really important, this algorithm works well with all tested menus. Other windows not so important.\n\t\t\t\t//for(int i = 0; i < c.childrenCount; i++) {\n\t\t\t\tfor(int i = c.childrenCount; --i >= 0; ) {\n\t\t\t\t\tauto k = s_api.getAccessibleChildFromContext(vmID, ac, i);\n\t\t\t\t\tif(k) {\n\t\t\t\t\t\tauto v = func(k, level + 1);\n\t\t\t\t\t\tif(k != v) s_api.releaseJavaObject(vmID, k);\n\t\t\t\t\t\tif(v) return v;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn notThis ? 0 : ac;\n\t\t};\n\t\tjo = func(ap, 0);\n\t\t//if(jo) _PrintElem(vmID, jo);\n\t\t//Perf.Next('m');\n\t}\n\n\tif(!jo) {\n\t\tif(!s_api.getAccessibleContextAt(vmID, ap, p.x, p.y, &jo)) {\n\t\t\tjo = 0;\n\t\t\tPRINTS(L\"failed 1\");\n\t\t} else if(jo == 0) {\n\t\t\tPRINTS(L\"failed 2\");\n\t\t\t//JAB bug: jo is 0 if the window never received a mouse message.\n\t\t\t//\tCould post message now, but it's too dirty.\n\t\t\t//  Randomly fails in other cases too, and the workaround does not help then, but can cause blinking etc.\n\t\t\t//\tNever mind.\n\t\t\t//PostMessageW(w, WM_MOUSEMOVE, 0, 0);\n\t\t\t//for(int i = 0; i < 10; i++) {\n\t\t\t//\tSleep(15); SendMessageTimeoutW(w, 0, 0, 0, SMTO_ABORTIFHUNG, 1000, null);\n\t\t\t//\tif(s_api.getAccessibleContextAt(vmID, ap, p.x, p.y, &jo) && jo != 0) break; jo=0;\n\t\t\t//}\n\t\t} else {\n\t\t\t//Perf.Next('p');\n\t\t\t//_PrintElem(vmID, ap, L\"----- \");\n\t\t\t//_PrintElemAndAncestors(vmID, jo);\n\n\t\t\t//workaround for:\n\t\t\t//\t1. JAB gives empty rect for some objects. Some such objects also are disconnected from DOM and therefore cannot be found. Then use parent.\n\t\t\t//\t2. If on a menubar item, jo often is the first item of the popup menu. Then wrong _hwnd etc.\n\t\t\tAccessibleContextInfo ci;\n\t\t\tif(s_api.getAccessibleContextInfo(vmID, jo, &ci) && !_IsPointInRect(p, ci)) {\n\t\t\t\tauto pa = s_api.getAccessibleParentFromContext(vmID, jo);\n\t\t\t\t//_PrintElem(vmID, pa);\n\t\t\t\tif(pa) {\n\t\t\t\t\tif(s_api.getAccessibleContextInfo(vmID, pa, &ci) && _IsPointInRect(p, ci)) {\n\t\t\t\t\t\tauto bad = jo; jo = pa; s_api.releaseJavaObject(vmID, bad);\n\t\t\t\t\t} else s_api.releaseJavaObject(vmID, pa);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t//Perf.Next('P');\n\n\ts_api.releaseJavaObject(vmID, ap);\n\t//Perf.NW('r');\n\n\tif(!jo) return null;\n\treturn new JAccessible(jo, vmID, w);\n}\n\n} //namespace jab\n\nIAccessible* AccJavaFromWindow(HWND w, bool getFocused /*= false*/)\n{\n\treturn jab::AccFromWindow(w, getFocused);\n}\n\n//Gets Java accessible object from point.\n//p - point in screen coordinates.\n//w - window containing the point.\n//alt - enumerate descendants. Slow.\nIAccessible* AccJavaFromPoint(POINT p, HWND w)\n{\n\treturn jab::AccFromPoint(p, w);\n}\n\n//TODO3: when Delm creates tree and tries to find the captured object, use isSameObject, not rect+role. If possible and faster.\n"
  },
  {
    "path": "Cpp/acc uia.cpp",
    "content": "#include \"stdafx.h\"\n#include \"cpp.h\"\n#include \"acc.h\"\n\nnamespace uia {\n#ifdef _DEBUG\n\tvoid PrintGuid(const GUID& g) {\n\t\tLPOLESTR os = null;\n\t\tStringFromIID(g, &os);\n\t\tPrint(os);\n\t\tCoTaskMemFree(os);\n\t}\n\n\tvoid PrintUiaElem(IUIAutomationElement* e) {\n\t\tRECT r = {};\n\t\tauto hr = e->get_CurrentBoundingRectangle(&r);\n\t\tif (hr != 0) Print(\"bad\"); else Printf(L\"{%i %i %i %i}\", r.left, r.top, r.right - r.left, r.bottom - r.top);\n\t}\n#endif\n\n\tstruct ThreadVar {\n\t\tSmart<IUIAutomation> uia;\n\t\tSmart<IUIAutomationCondition> rawCond;\n\t\tSmart<IUIAutomationTreeWalker> rawWalk;\n\t};\n\tthread_local ThreadVar t_var;\n\n\tIUIAutomation* UIA() {\n\t\tThreadVar& tv = t_var;\n\t\tif (!tv.uia) tv.uia.CoCreateInstance(osVer.minWin8() ? __uuidof(CUIAutomation8) : __uuidof(CUIAutomation), null, CLSCTX_INPROC_SERVER);\n\t\treturn tv.uia;\n\t}\n\n\t//Used in get_accChildCount with FindAll.\n\tIUIAutomationCondition* CondAll() {\n\t\tThreadVar& tv = t_var;\n\t\t//if(!t_var.rawCond) UIA()->get_RawViewCondition(&t_var.rawCond); //FindFirst/FirstAll ignores this and uses control view instead\n\t\tif (!t_var.rawCond) UIA()->get_ControlViewCondition(&t_var.rawCond);\n\t\treturn t_var.rawCond;\n\t}\n\n\t//Used in get_accParent, accNavigate, _GetHWND.\n\tIUIAutomationTreeWalker* WalkAll() {\n\t\tThreadVar& tv = t_var;\n\t\t//if(!tv.rawWalk) UIA()->get_RawViewWalker(&tv.rawWalk); //don't use this because must be consistent with FindFirst/FirstAll\n\t\tif (!tv.rawWalk) UIA()->get_ControlViewWalker(&tv.rawWalk);\n\t\treturn tv.rawWalk;\n\t}\n\n\tstatic long s_uiaWrapperCount;\n\n\t//Returns false if there are UIAccessible objects in this process.\n\t//Then cannot unload this dll, because later will be called Release and this process would crash if unloaded.\n\t//Could not find a way to prevent Release. Even if client does not call it, COM calls it after 6 minutes. CoDisconnectObject prevents only other method calls.\n\tbool UiaDisconnectWrappers() {\n\t\tPRINTF_IF(s_uiaWrapperCount != 0, L\"cannot unload dll because of %i alive UIA wrappers.  %s\", s_uiaWrapperCount, GetCommandLineW());\n\t\treturn s_uiaWrapperCount == 0;\n\t}\n\n\tclass UIAccessible : public IAccessible, IEnumVARIANT {\n\t\tlong _cRef;\n\t\tint _next;\n\t\tIUIAutomationElement* _ae;\n\t\tIUIAutomationElementArray* _children;\n\t\tULONGLONG _timeOfChildren;\n\n\tpublic:\n\t\tUIAccessible(IUIAutomationElement* ae) {\n\t\t\t//PRINTF(L\"+ %p\", _ae);\n\t\t\t//ae->AddRef(); Printf(L\"+ %p ref=%i tid=%i\", ae, ae->Release(), GetCurrentThreadId());\n\t\t\tInterlockedIncrement(&s_uiaWrapperCount);\n\n\t\t\t_cRef = 1;\n\t\t\t_next = 0;\n\t\t\t_ae = ae;\n\t\t\t_children = null;\n\t\t\t_timeOfChildren = 0;\n\t\t\tassert(_ae != null);\n\n\t\t}\n\n\t\t~UIAccessible() {\n\t\t\tif (_children) _children->Release();\n\t\t\t_ae->Release();\n\t\t\t//Printf(L\"- %p ref=%i tid=%i\", _ae, _ae->Release(), GetCurrentThreadId());\n\t\t\t//note: intermediate elements still have refcount 1, because they are referenced by parent's IUIAutomationElementArray.\n\n\t\t\t//PRINTF(L\"- %p\", _ae);\n\t\t\tInterlockedDecrement(&s_uiaWrapperCount);\n\t\t}\n\n#pragma region IUnknown, IDispatch\n\t\tSTDMETHODIMP QueryInterface(REFIID iid, void** ppv) {\n\t\t\t//PrintGuid(iid);\n\n\t\t\tif (iid == IID_IAccessible || iid == IID_IDispatch || iid == IID_IUnknown) {\n\t\t\t\tInterlockedIncrement(&_cRef);\n\t\t\t\t*ppv = this;\n\t\t\t\treturn 0;\n\t\t\t} else if (iid == IID_IEnumVARIANT) {\n\t\t\t\tInterlockedIncrement(&_cRef);\n\t\t\t\t*ppv = (IEnumVARIANT*)this;\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\t//else if(iid == IID_IUIAutomationElement) {\n\t\t\t//\t//note: does not work for inproc AO. This func is called, but QI (of proxy) fails. If need, try QS, not tested.\n\t\t\t//\t//tested: IUIAutomation.ElementFromIAccessible does not QI/QS IUIAutomationElement.\n\t\t\t//\t//Print(\"--UIA\");\n\t\t\t//\t_ae->AddRef();\n\t\t\t//\t*ppv = _ae;\n\t\t\t//\treturn 0;\n\t\t\t//}\n\t\t\t//else if(iid == IID_IServiceProvider) {\n\t\t\t//\tInterlockedIncrement(&_cRef);\n\t\t\t//\t*ppv = (IServiceProvider*)this;\n\t\t\t//\treturn 0;\n\t\t\t//}\n\n\t\t\t//if(iid == IID_IOleWindow) Print(\"IID_IOleWindow\");\n\t\t\t//else if(iid == IID_IServiceProvider) Print(\"IID_IServiceProvider\");\n\t\t\t//else PrintGuid(iid);\n\n\t\t\t*ppv = 0;\n\t\t\treturn E_NOINTERFACE;\n\t\t}\n\n\t\tSTDMETHODIMP_(ULONG) AddRef() {\n\t\t\treturn InterlockedIncrement(&_cRef);\n\t\t}\n\n\t\tSTDMETHODIMP_(ULONG) Release() {\n\t\t\tlong ret = InterlockedDecrement(&_cRef);\n\t\t\tif (!ret) delete this;\n\t\t\treturn ret;\n\t\t}\n\n\t\tSTDMETHODIMP GetTypeInfoCount(UINT* pctinfo) { return E_NOTIMPL; }\n\n\t\tSTDMETHODIMP GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo) { return E_NOTIMPL; }\n\n\t\tSTDMETHODIMP GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, __RPC__out_ecount_full(cNames) DISPID* rgDispId) { return E_NOTIMPL; }\n\n\t\tSTDMETHODIMP Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr) { return E_NOTIMPL; }\n#pragma endregion\n\n#pragma region IEnumVARIANT\n\t\tSTDMETHODIMP Next(ULONG celt, VARIANT* rgVar, ULONG* pCeltFetched) {\n\t\t\t//PRINTS(__FUNCTIONW__);\n\t\t\tif (pCeltFetched) *pCeltFetched = 0;\n\t\t\tlong cc;\n\t\t\tHRESULT hr = get_accChildCount(&cc); if (hr != 0) return hr;\n\t\t\t//Printf(__FUNCTIONW__ L\" %i %i\", celt, cc);\n\t\t\tfor (ULONG i = 0; i < celt; i++, _next++) {\n\t\t\t\tif (_next >= cc) return 1;\n\t\t\t\tIUIAutomationElement* e = null;\n\t\t\t\tHRESULT hr = _children->GetElement(_next, &e); if (hr != 0) return hr;\n\t\t\t\trgVar[i].pdispVal = new UIAccessible(e);\n\t\t\t\trgVar[i].vt = VT_DISPATCH;\n\t\t\t\tif (pCeltFetched) (*pCeltFetched)++;\n\t\t\t}\n\t\t\treturn 0;\n\t\t}\n\t\tSTDMETHODIMP Skip(ULONG celt) {\n\t\t\tPRINTS(__FUNCTIONW__);\n\t\t\treturn E_NOTIMPL;\n\t\t}\n\t\tSTDMETHODIMP Reset(void) {\n\t\t\t_next = 0;\n\t\t\treturn 0;\n\t\t}\n\t\tSTDMETHODIMP Clone(IEnumVARIANT** ppEnum) {\n\t\t\tPRINTS(__FUNCTIONW__);\n\t\t\treturn E_NOTIMPL;\n\t\t}\n#pragma endregion\n\n#pragma region IAccessible\n\t\tSTDMETHODIMP get_accParent(IDispatch** ppdispParent) {\n\t\t\t//PRINTS(__FUNCTIONW__);\n\t\t\t*ppdispParent = null;\n\t\t\tIUIAutomationElement* p = null;\n\t\t\tHRESULT hr = WalkAll()->GetParentElement(_ae, &p);\n\t\t\t//Printf(L\"0x%X %p\", hr, p);\n\t\t\tif (hr == 0 && p == null) hr = 1;\n\t\t\tif (hr == 0) *ppdispParent = new UIAccessible(p);\n\t\t\treturn hr;\n\t\t}\n\n\t\tSTDMETHODIMP get_accChildCount(long* pcountChildren) {\n\t\t\t//PRINTS(__FUNCTIONW__);\n\t\t\t//Perf.First();\n\t\t\t*pcountChildren = 0;\n\t\t\tHRESULT hr;\n\t\t\tif (!_children || GetTickCount64() - _timeOfChildren > 40) {\n\t\t\t\tif (_children) { _children->Release(); _children = null; }\n\t\t\t\thr = _ae->FindAll(TreeScope::TreeScope_Children, CondAll(), &_children);\n\t\t\t\t//Printf(L\"0x%X %p\", hr, _children.p);\n\t\t\t\tif (hr != 0 || !_children) return hr; //msdn lies: \"NULL is returned if no matching element is found\"\n\t\t\t\t_timeOfChildren = GetTickCount64();\n\t\t\t}\n\t\t\t//Perf.Next(); //in some cases very slow, eg in Firefox big pages\n\t\t\thr = _children->get_Length((int*)pcountChildren);\n\t\t\t//Perf.NW(); //0-1\n\t\t\treturn hr;\n\t\t}\n\n\t\tSTDMETHODIMP get_accChild(VARIANT varChild, IDispatch** ppdispChild) {\n\t\t\t//PRINTS(__FUNCTIONW__);\n\t\t\t*ppdispChild = null;\n\t\t\tif (varChild.vt != VT_I4) return E_INVALIDARG;\n\t\t\tlong cc;\n\t\t\tHRESULT hr = get_accChildCount(&cc); if (hr != 0) return hr;\n\t\t\tlong i = varChild.lVal - 1; if ((DWORD)i >= (DWORD)cc) return E_INVALIDARG;\n\t\t\tIUIAutomationElement* e = null;\n\t\t\thr = _children->GetElement(i, &e);\n\t\t\tif (hr == 0) *ppdispChild = new UIAccessible(e);\n\t\t\treturn hr;\n\t\t}\n\n\t\tSTDMETHODIMP get_accName(VARIANT varChild, out BSTR* pszName) {\n\t\t\t//PRINTS(__FUNCTIONW__);\n\t\t\tif (_InvalidVarChildParam(ref varChild)) return E_INVALIDARG;\n\t\t\treturn _ae->get_CurrentName(pszName);\n\t\t}\n\n\t\tSTDMETHODIMP get_accValue(VARIANT varChild, out BSTR* pszValue) {\n\t\t\treturn _GetProp(varChild, 'v', pszValue);\n\t\t}\n\n\t\tSTDMETHODIMP get_accDescription(VARIANT varChild, out BSTR* pszDescription) {\n\t\t\treturn _GetProp(varChild, 'd', pszDescription);\n\t\t}\n\n\t\tSTDMETHODIMP get_accRole(VARIANT varChild, out VARIANT* pvarRole) {\n\t\t\t//PRINTS(__FUNCTIONW__);\n\t\t\tif (_InvalidVarChildParam(ref varChild)) return E_INVALIDARG;\n\t\t\tCONTROLTYPEID t;\n\t\t\tHRESULT hr = _ae->get_CurrentControlType(&t);\n\t\t\tif (hr == 0) {\n\t\t\t\t//https://learn.microsoft.com/en-us/windows/win32/winauto/appendix-g--active-accessibility-bridge-to-ui-automation\n\t\t\t\tint i = 0; STR s = L\"unknown\";\n\t\t\t\tswitch (t) {\n\t\t\t\tcase UIA_ButtonControlTypeId: i = ROLE_SYSTEM_PUSHBUTTON; break;\n\t\t\t\tcase UIA_CalendarControlTypeId: s = L\"Calendar\"; break;\n\t\t\t\tcase UIA_CheckBoxControlTypeId: i = ROLE_SYSTEM_CHECKBUTTON; break;\n\t\t\t\tcase UIA_ComboBoxControlTypeId: i = ROLE_SYSTEM_COMBOBOX; break;\n\t\t\t\tcase UIA_EditControlTypeId: i = ROLE_SYSTEM_TEXT; break;\n\t\t\t\tcase UIA_HyperlinkControlTypeId: i = ROLE_SYSTEM_LINK; break;\n\t\t\t\tcase UIA_ImageControlTypeId: i = ROLE_SYSTEM_GRAPHIC; break;\n\t\t\t\tcase UIA_ListItemControlTypeId: i = ROLE_SYSTEM_LISTITEM; break;\n\t\t\t\tcase UIA_ListControlTypeId: i = ROLE_SYSTEM_LIST; break;\n\t\t\t\tcase UIA_MenuControlTypeId: i = ROLE_SYSTEM_MENUPOPUP; break;\n\t\t\t\tcase UIA_MenuBarControlTypeId: i = ROLE_SYSTEM_MENUBAR; break;\n\t\t\t\tcase UIA_MenuItemControlTypeId: i = ROLE_SYSTEM_MENUITEM; break;\n\t\t\t\tcase UIA_ProgressBarControlTypeId: i = ROLE_SYSTEM_PROGRESSBAR; break;\n\t\t\t\tcase UIA_RadioButtonControlTypeId: i = ROLE_SYSTEM_RADIOBUTTON; break;\n\t\t\t\tcase UIA_ScrollBarControlTypeId: i = ROLE_SYSTEM_SCROLLBAR; break;\n\t\t\t\tcase UIA_SliderControlTypeId: i = ROLE_SYSTEM_SLIDER; break;\n\t\t\t\tcase UIA_SpinnerControlTypeId: i = ROLE_SYSTEM_SPINBUTTON; break;\n\t\t\t\tcase UIA_StatusBarControlTypeId: i = ROLE_SYSTEM_STATUSBAR; break;\n\t\t\t\tcase UIA_TabControlTypeId: i = ROLE_SYSTEM_PAGETABLIST; break;\n\t\t\t\tcase UIA_TabItemControlTypeId: i = ROLE_SYSTEM_PAGETAB; break;\n\t\t\t\tcase UIA_TextControlTypeId: i = ROLE_SYSTEM_STATICTEXT; break;\n\t\t\t\tcase UIA_ToolBarControlTypeId: i = ROLE_SYSTEM_TOOLBAR; break;\n\t\t\t\tcase UIA_ToolTipControlTypeId: i = ROLE_SYSTEM_TOOLTIP; break;\n\t\t\t\tcase UIA_TreeControlTypeId: i = ROLE_SYSTEM_OUTLINE; break;\n\t\t\t\tcase UIA_TreeItemControlTypeId: i = ROLE_SYSTEM_OUTLINEITEM; break;\n\t\t\t\tcase UIA_CustomControlTypeId: i = ROLE_SYSTEM_CLIENT; break; //documented as the default MSAA role. And it's good for _IsContainer.\n\t\t\t\tcase UIA_GroupControlTypeId: i = ROLE_SYSTEM_GROUPING; break;\n\t\t\t\tcase UIA_ThumbControlTypeId: i = ROLE_SYSTEM_INDICATOR; break;\n\t\t\t\tcase UIA_DataGridControlTypeId: i = ROLE_SYSTEM_LIST; break;\n\t\t\t\tcase UIA_DataItemControlTypeId: i = ROLE_SYSTEM_LISTITEM; break;\n\t\t\t\tcase UIA_DocumentControlTypeId: i = ROLE_SYSTEM_DOCUMENT; break;\n\t\t\t\tcase UIA_SplitButtonControlTypeId: i = ROLE_SYSTEM_SPLITBUTTON; break;\n\t\t\t\tcase UIA_WindowControlTypeId: i = ROLE_SYSTEM_WINDOW; break;\n\t\t\t\tcase UIA_PaneControlTypeId: i = ROLE_SYSTEM_PANE; break;\n\t\t\t\tcase UIA_HeaderControlTypeId: i = ROLE_SYSTEM_LIST; break;\n\t\t\t\tcase UIA_HeaderItemControlTypeId: i = ROLE_SYSTEM_COLUMNHEADER; break;\n\t\t\t\tcase UIA_TableControlTypeId: i = ROLE_SYSTEM_TABLE; break;\n\t\t\t\tcase UIA_TitleBarControlTypeId: i = ROLE_SYSTEM_TITLEBAR; break;\n\t\t\t\tcase UIA_SeparatorControlTypeId: i = ROLE_SYSTEM_SEPARATOR; break;\n\t\t\t\tcase UIA_SemanticZoomControlTypeId: s = L\"SemanticZoom\"; break;\n\t\t\t\tcase UIA_AppBarControlTypeId: s = L\"AppBar\"; break;\n\t\t\t\t}\n\t\t\t\tif (i) {\n\t\t\t\t\tpvarRole->vt = VT_I4;\n\t\t\t\t\tpvarRole->lVal = i;\n\t\t\t\t} else {\n\t\t\t\t\tpvarRole->vt = VT_BSTR;\n\t\t\t\t\tpvarRole->bstrVal = SysAllocString(s);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn hr;\n\t\t}\n\n\t\tSTDMETHODIMP get_accState(VARIANT varChild, out VARIANT* pvarState) {\n\t\t\t//PRINTS(__FUNCTIONW__);\n\t\t\tlong state;\n\t\t\tHRESULT hr = _GetProp(varChild, 's', &state);\n\t\t\tif (hr == 0) {\n\t\t\t\tpvarState->vt = VT_I4;\n\t\t\t\tpvarState->lVal = state;\n\t\t\t}\n\t\t\treturn hr;\n\t\t}\n\n\t\tSTDMETHODIMP get_accHelp(VARIANT varChild, out BSTR* pszHelp) {\n\t\t\tif (varChild.vt == VT_I1) { //prop uiaid, uiacn\n\t\t\t\tswitch (varChild.cVal) {\n\t\t\t\tcase 'u': return _ae->get_CurrentAutomationId(pszHelp);\n\t\t\t\tcase 'U': return _ae->get_CurrentClassName(pszHelp);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn _GetProp(varChild, 'h', pszHelp);\n\t\t}\n\n\t\tSTDMETHODIMP get_accHelpTopic(BSTR* pszHelpFile, VARIANT varChild, long* pidTopic) {\n\t\t\treturn E_NOTIMPL;\n\t\t}\n\n\t\tSTDMETHODIMP get_accKeyboardShortcut(VARIANT varChild, out BSTR* pszKeyboardShortcut) {\n\t\t\treturn _GetProp(varChild, 'k', pszKeyboardShortcut);\n\t\t}\n\n\t\tSTDMETHODIMP get_accFocus(out VARIANT* pvarChild) {\n\t\t\treturn E_NOTIMPL;\n\t\t\t//Rarely used.\n\t\t}\n\n\t\tclass UIAccessibleSelectedChildren : public IEnumVARIANT {\n\t\t\tlong _cRef;\n\t\t\tint _next, _count;\n\t\t\tSmart<IUIAutomationElementArray> _a;\n\n\t\tpublic:\n\t\t\tUIAccessibleSelectedChildren(IUIAutomationElementArray* a, int count) : _a(a, false) {\n\t\t\t\t//PRINTS(__FUNCTIONW__);\n\t\t\t\t_cRef = 1;\n\t\t\t\t_next = 0;\n\t\t\t\t_count = count;\n\t\t\t}\n\n\t\t\t//~UIAccessibleSelectedChildren()\n\t\t\t//{\n\t\t\t//\tPRINTS(__FUNCTIONW__);\n\t\t\t//}\n\n\t\t\tSTDMETHODIMP QueryInterface(REFIID riid, void** ppvObject) {\n\t\t\t\tif (riid == IID_IEnumVARIANT || riid == IID_IUnknown) {\n\t\t\t\t\t++_cRef;\n\t\t\t\t\t*ppvObject = this;\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t\t*ppvObject = null;\n\t\t\t\treturn E_NOINTERFACE;\n\t\t\t}\n\t\t\tSTDMETHODIMP_(ULONG) AddRef(void) {\n\t\t\t\treturn ++_cRef;\n\t\t\t}\n\t\t\tSTDMETHODIMP_(ULONG) Release(void) {\n\t\t\t\tlong ret = --_cRef;\n\t\t\t\tif (!ret) delete this;\n\t\t\t\treturn ret;\n\t\t\t}\n\t\t\tSTDMETHODIMP Next(ULONG celt, VARIANT* rgVar, ULONG* pCeltFetched) {\n\t\t\t\tif (pCeltFetched) *pCeltFetched = 0;\n\t\t\t\tfor (ULONG i = 0; i < celt; i++, _next++) {\n\t\t\t\t\tif (_next == _count) return 1;\n\t\t\t\t\tIUIAutomationElement* e = null;\n\t\t\t\t\tHRESULT hr = _a->GetElement(_next, &e); if (hr != 0) return hr;\n\t\t\t\t\trgVar[i].pdispVal = new UIAccessible(e);\n\t\t\t\t\trgVar[i].vt = VT_DISPATCH;\n\t\t\t\t\tif (pCeltFetched) (*pCeltFetched)++;\n\t\t\t\t}\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tSTDMETHODIMP Skip(ULONG celt) {\n\t\t\t\treturn E_NOTIMPL;\n\t\t\t}\n\t\t\tSTDMETHODIMP Reset(void) {\n\t\t\t\t_next = 0;\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tSTDMETHODIMP Clone(IEnumVARIANT** ppEnum) {\n\t\t\t\treturn E_NOTIMPL;\n\t\t\t}\n\t\t};\n\n\t\tSTDMETHODIMP get_accSelection(out VARIANT* pvarChildren) {\n\t\t\tSmart<IUIAutomationSelectionPattern> p;\n\t\t\tHRESULT hr = _ae->GetCurrentPatternAs(UIA_SelectionPatternId, IID_PPV_ARGS(&p)); if (hr == 0 && !p) hr = 1;\n\t\t\tif (hr == 0) {\n\t\t\t\tSmart<IUIAutomationElementArray> a;\n\t\t\t\thr = p->GetCurrentSelection(&a);\n\t\t\t\tif (hr == 0) {\n\t\t\t\t\tpvarChildren->vt = 0;\n\t\t\t\t\tint n; hr = a->get_Length(&n); if (hr != 0) n = 0;\n\t\t\t\t\tif (n == 1) {\n\t\t\t\t\t\tIUIAutomationElement* e = null;\n\t\t\t\t\t\thr = a->GetElement(0, &e);\n\t\t\t\t\t\tif (hr == 0) {\n\t\t\t\t\t\t\tpvarChildren->pdispVal = new UIAccessible(e);\n\t\t\t\t\t\t\tpvarChildren->vt = VT_DISPATCH;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (n > 1) {\n\t\t\t\t\t\tpvarChildren->punkVal = new UIAccessibleSelectedChildren(a.Detach(), n);\n\t\t\t\t\t\tpvarChildren->vt = VT_UNKNOWN;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn hr;\n\t\t}\n\n\t\tSTDMETHODIMP get_accDefaultAction(VARIANT varChild, out BSTR* pszDefaultAction) {\n\t\t\treturn _GetProp(varChild, 'a', pszDefaultAction);\n\t\t}\n\n\t\tSTDMETHODIMP accSelect(long flagsSelect, VARIANT varChild) {\n\t\t\tif (_InvalidVarChildParam(ref varChild)) return E_INVALIDARG;\n\t\t\tif (flagsSelect & SELFLAG_EXTENDSELECTION) return E_INVALIDARG;\n\t\t\tHRESULT hr = 0;\n\t\t\tif (flagsSelect & (SELFLAG_TAKESELECTION | SELFLAG_ADDSELECTION | SELFLAG_REMOVESELECTION)) {\n\t\t\t\tSmart<IUIAutomationSelectionItemPattern> p;\n\t\t\t\thr = _ae->GetCurrentPatternAs(UIA_SelectionItemPatternId, IID_PPV_ARGS(&p)); if (hr == 0 && !p) hr = 1;\n\t\t\t\tif (hr == 0) {\n\t\t\t\t\tif (flagsSelect & SELFLAG_TAKESELECTION) hr = p->Select();\n\t\t\t\t\tif (flagsSelect & SELFLAG_ADDSELECTION && hr == 0) hr = p->AddToSelection();\n\t\t\t\t\tif (flagsSelect & SELFLAG_REMOVESELECTION && hr == 0) hr = p->RemoveFromSelection();\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (flagsSelect & SELFLAG_TAKEFOCUS && hr == 0) {\n\t\t\t\thr = _ae->SetFocus();\n\t\t\t}\n\t\t\treturn hr;\n\t\t}\n\n\t\tSTDMETHODIMP accLocation(out long* pxLeft, out long* pyTop, out long* pcxWidth, out long* pcyHeight, VARIANT varChild) {\n\t\t\t//PRINTS(__FUNCTIONW__);\n\t\t\tif (_InvalidVarChildParam(ref varChild)) return E_INVALIDARG;\n\t\t\tRECT r;\n\t\t\tHRESULT hr = _ae->get_CurrentBoundingRectangle(&r);\n\t\t\tif (hr == 0) {\n\t\t\t\t*pxLeft = r.left; *pyTop = r.top; *pcxWidth = r.right - r.left; *pcyHeight = r.bottom - r.top;\n\t\t\t}\n\t\t\treturn hr;\n\t\t}\n\n\t\tSTDMETHODIMP accNavigate(long navDir, VARIANT varStart, out VARIANT* pvarEndUpAt) {\n\t\t\t//Print(\"accNavigate\", navDir, varStart.vt, varStart.value);\n\n\t\t\t//WindowFromAccessibleObject (WFAO) at first calls this with an undocumented navDir 10.\n\t\t\t//\ttested: accNavigate(10) for a standard Windows control returns VARIANT(VT_I4, hwnd).\n\t\t\t//\tIf we return window handle, WFAO does not call get_accParent. Else also calls it for each ancestor.\n\t\t\t//\tCurrently WFAO cannot get hwnd when calling our get_accParent.\n\t\t\tif (navDir == 10 && varStart.vt == VT_I4) {\n\t\t\t\t//Perf.First();\n\t\t\t\tHWND w = _GetHWND();\n\t\t\t\t//Perf.NW();\n\t\t\t\tif (w == 0) return 1;\n\t\t\t\tpvarEndUpAt->vt = VT_I4;\n\t\t\t\tpvarEndUpAt->lVal = (int)(LPARAM)w;\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\tif (navDir < NAVDIR_UP || navDir > NAVDIR_LASTCHILD) return E_INVALIDARG;\n\t\t\tif (_InvalidVarChildParam(ref varStart)) return E_INVALIDARG;\n\n\t\t\tauto walker = WalkAll();\n\t\t\tIUIAutomationElement* p = null;\n\t\t\tHRESULT hr = 0;\n\t\t\tswitch (navDir) {\n\t\t\tcase NAVDIR_NEXT: hr = walker->GetNextSiblingElement(_ae, &p); break;\n\t\t\tcase NAVDIR_PREVIOUS: hr = walker->GetPreviousSiblingElement(_ae, &p); break;\n\t\t\tcase NAVDIR_FIRSTCHILD: hr = walker->GetFirstChildElement(_ae, &p); break;\n\t\t\tcase NAVDIR_LASTCHILD: hr = walker->GetLastChildElement(_ae, &p); break;\n\t\t\t}\n\t\t\tif (hr == 0 && p == null) hr = 1;\n\t\t\tif (hr == 0) {\n\t\t\t\tpvarEndUpAt->pdispVal = new UIAccessible(p);\n\t\t\t\tpvarEndUpAt->vt = VT_DISPATCH;\n\t\t\t}\n\t\t\treturn hr;\n\t\t}\n\n\t\tSTDMETHODIMP accHitTest(long xLeft, long yTop, out VARIANT* pvarChild) {\n\t\t\treturn E_NOTIMPL;\n\t\t\t//Rarely used.\n\t\t}\n\n\t\t//The _DDA_x functions try to get a pattern and call its method. They return true if pattern available, even if method failed.\n\t\tbool _DDA_InvokeUIA(ref HRESULT& hr) {\n\t\t\tSmart<IUIAutomationInvokePattern> p;\n\t\t\thr = _ae->GetCurrentPatternAs(UIA_InvokePatternId, IID_PPV_ARGS(&p)); if (hr == 0 && !p) hr = 1;\n\t\t\tif (hr != 0) return false;\n\t\t\t//Print(\"Invoke\");/\n\t\t\thr = p->Invoke();\n\t\t\treturn true;\n\t\t}\n\n\t\tbool _DDA_Toggle(ref HRESULT& hr) {\n\t\t\tSmart<IUIAutomationTogglePattern> p;\n\t\t\thr = _ae->GetCurrentPatternAs(UIA_TogglePatternId, IID_PPV_ARGS(&p)); if (hr == 0 && !p) hr = 1;\n\t\t\tif (hr != 0) return false;\n\t\t\t//Print(\"Toggle\");\n\t\t\thr = p->Toggle();\n\t\t\treturn true;\n\t\t}\n\n\t\t//expand: 0 collapse, 1 expand, 2 toggle\n\t\tbool _DDA_Expand(ref HRESULT& hr, int expand) {\n\t\t\tSmart<IUIAutomationExpandCollapsePattern> p;\n\t\t\thr = _ae->GetCurrentPatternAs(UIA_ExpandCollapsePatternId, IID_PPV_ARGS(&p)); if (hr == 0 && !p) hr = 1;\n\t\t\tif (hr != 0) return false;\n\t\t\t//Print(\"Expand\");\n\t\t\tif (expand == 2) {\n\t\t\t\tExpandCollapseState ecs = ExpandCollapseState_LeafNode;\n\t\t\t\tif (0 == p->get_CurrentExpandCollapseState(&ecs)) {\n\t\t\t\t\tif (ecs == ExpandCollapseState_Expanded) expand = 0;\n\t\t\t\t\telse if (ecs != ExpandCollapseState_LeafNode) expand = 1; //collapsed or partially expanded\n\t\t\t\t} else hr = 1;\n\t\t\t}\n\t\t\tif (expand == 1) hr = p->Expand(); else if (expand == 0) hr = p->Collapse();\n\t\t\treturn true;\n\t\t}\n\n\t\tbool _DDA_Select(ref HRESULT& hr) {\n\t\t\tSmart<IUIAutomationSelectionItemPattern> p;\n\t\t\thr = _ae->GetCurrentPatternAs(UIA_SelectionItemPatternId, IID_PPV_ARGS(&p)); if (hr == 0 && !p) hr = 1;\n\t\t\tif (hr != 0) return false;\n\t\t\t//Print(\"Select\");\n\t\t\thr = p->Select();\n\t\t\treturn true;\n\t\t}\n\n\t\tbool _DDA_InvokeMSAA(ref HRESULT& hr) {\n\t\t\tSmart<IUIAutomationLegacyIAccessiblePattern> p;\n\t\t\thr = _ae->GetCurrentPatternAs(UIA_LegacyIAccessiblePatternId, IID_PPV_ARGS(&p)); if (hr == 0 && !p) hr = 1;\n\t\t\tif (hr != 0) return false;\n\t\t\t//Print(\"DoDefaultAction\");\n\t\t\thr = p->DoDefaultAction();\n\t\t\treturn true;\n\t\t}\n\n\t\tSTDMETHODIMP accDoDefaultAction(VARIANT varChild) {\n\t\t\tHRESULT hr = 1;\n\n\t\t\tif (varChild.vt == VT_I1) { //specified action\n\t\t\t\tif (varChild.cVal == 's') { //ScrollTo\n\t\t\t\t\tSmart<IUIAutomationScrollItemPattern> p;\n\t\t\t\t\thr = _ae->GetCurrentPatternAs(UIA_ScrollItemPatternId, IID_PPV_ARGS(&p)); if (hr == 0 && !p) hr = 1;\n\t\t\t\t\tif (hr == 0) hr = p->ScrollIntoView();\n\t\t\t\t} else if (varChild.cVal == 'c') { //Check(true/false)\n\t\t\t\t\tbool ok = _DDA_Toggle(ref hr)\n\t\t\t\t\t\t|| _DDA_InvokeUIA(ref hr);\n\t\t\t\t} else if (varChild.cVal == 'E' || varChild.cVal == 'e') { //Expand(true/false)\n\t\t\t\t\tbool ok = _DDA_Expand(ref hr, varChild.cVal == 'E' ? 1 : 0) //treeitem, combobox\n\t\t\t\t\t\t|| _DDA_Toggle(ref hr); //some expanders\n\t\t\t\t}\n\t\t\t} else { //default action\n\t\t\t\tif (_InvalidVarChildParam(ref varChild)) return E_INVALIDARG;\n\t\t\t\tbool ok = _DDA_InvokeUIA(ref hr)\n\t\t\t\t\t|| _DDA_Toggle(ref hr)\n\t\t\t\t\t|| _DDA_Expand(ref hr, 2)\n\t\t\t\t\t|| _DDA_Select(ref hr)\n\t\t\t\t\t|| _DDA_InvokeMSAA(ref hr);\n\t\t\t\t//Print((DWORD)hr);\n\n\t\t\t\t//Many UIA elements don't have Invoke pattern but have some other pattern that can be used instead.\n\t\t\t\t//Many have IAccessible pattern, but often its DoDefaultAction doesn't work.\n\t\t\t}\n\t\t\treturn hr;\n\t\t}\n\n\t\tSTDMETHODIMP put_accName(VARIANT varChild, BSTR szName) {\n\t\t\treturn E_NOTIMPL;\n\t\t\t//Rarely used, deprecated.\n\t\t}\n\n\t\tSTDMETHODIMP put_accValue(VARIANT varChild, BSTR szValue) {\n\t\t\tif (_InvalidVarChildParam(ref varChild)) return E_INVALIDARG;\n\t\t\tSmart<IUIAutomationValuePattern> p;\n\t\t\tHRESULT hr = _ae->GetCurrentPatternAs(UIA_ValuePatternId, IID_PPV_ARGS(&p));\n\t\t\tif (hr == 0 && !p) hr = 1;\n\t\t\tif (hr == 0) hr = p->SetValue(szValue);\n\t\t\treturn hr;\n\t\t}\n#pragma endregion\n\n#pragma region private\n\tprivate:\n\t\tbool _InvalidVarChildParam(const VARIANT& v) {\n\t\t\tif (v.vt == 0) return false; //forgive\n\t\t\treturn v.vt != VT_I4 || v.lVal != 0;\n\t\t}\n\n\t\tHRESULT _GetProp(const VARIANT& varChild, WCHAR prop, void* R) {\n\t\t\tif (_InvalidVarChildParam(ref varChild)) return E_INVALIDARG;\n\n\t\t\tHRESULT hr = 1;\n#if true\n\t\t\tbool useMSAA = false;\n\t\t\tif (prop == 'd') { //tested: most either msaa or uia; some same\n\t\t\t\tVARIANT v = {};\n\t\t\t\thr = _ae->GetCurrentPropertyValue(UIA_FullDescriptionPropertyId, &v);\n\t\t\t\tif (hr == 0) {\n\t\t\t\t\tif (v.vt != VT_BSTR) { hr = 1; VariantClear(&v); } else if (SysStringLen(v.bstrVal) == 0) useMSAA = true;\n\t\t\t\t\telse *(BSTR*)R = v.bstrVal;\n\t\t\t\t}\n\t\t\t} else if (prop == 'v') { //tested: most same (msaa and uia); some only msaa (eg scrollbar arrow, rarely useful)\n\t\t\t\tSmart<IUIAutomationValuePattern> p;\n\t\t\t\thr = _ae->GetCurrentPatternAs(UIA_ValuePatternId, IID_PPV_ARGS(&p));\n\t\t\t\tif (hr == 0 && !p) hr = 1;\n\t\t\t\tif (hr == 0) hr = p->get_CurrentValue((BSTR*)R);\n\t\t\t} else if (prop == 'h') { //tested: all same\n\t\t\t\thr = _ae->get_CurrentHelpText((BSTR*)R);\n\t\t\t} else if (prop == 'k') { //tested: most either msaa or uia; some diff (uia better); get_CurrentAccessKey all == get_CurrentKeyboardShortcut\n\t\t\t\thr = _ae->get_CurrentAcceleratorKey((BSTR*)R);\n\t\t\t\tif (hr == 0 && SysStringLen(*(BSTR*)R) == 0) hr = _ae->get_CurrentAccessKey((BSTR*)R);\n\t\t\t} else useMSAA = true;\n\n\t\t\tif (useMSAA) {\n\t\t\t\tSmart<IUIAutomationLegacyIAccessiblePattern> p;\n\t\t\t\thr = _ae->GetCurrentPatternAs(UIA_LegacyIAccessiblePatternId, IID_PPV_ARGS(&p));\n\t\t\t\tif (hr == 0 && !p) hr = 1;\n\t\t\t\tif (hr == 0) {\n\t\t\t\t\tswitch (prop) {\n\t\t\t\t\tcase 'd': hr = p->get_CurrentDescription((BSTR*)R); break;\n\t\t\t\t\t\t//case 'v': hr = p->get_CurrentValue((BSTR*)R); break;\n\t\t\t\t\t\t//case 'h': hr = p->get_CurrentHelp((BSTR*)R); break;\n\t\t\t\t\t\t//case 'k': hr = p->get_CurrentKeyboardShortcut((BSTR*)R); break;\n\t\t\t\t\tcase 'a': hr = p->get_CurrentDefaultAction((BSTR*)R); break;\n\t\t\t\t\tcase 's': hr = p->get_CurrentState((DWORD*)R); break;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n#else\n\t\t\t{\n\t\t\t\tSmart<IUIAutomationLegacyIAccessiblePattern> p;\n\t\t\t\thr = _ae->GetCurrentPatternAs(UIA_LegacyIAccessiblePatternId, IID_PPV_ARGS(&p));\n\t\t\t\tif (hr == 0 && !p) hr = 1;\n\t\t\t\tif (hr == 0) {\n\t\t\t\t\tswitch (prop) {\n\t\t\t\t\tcase 'd': hr = p->get_CurrentDescription((BSTR*)R); break;\n\t\t\t\t\tcase 'v': hr = p->get_CurrentValue((BSTR*)R); break;\n\t\t\t\t\tcase 'h': hr = p->get_CurrentHelp((BSTR*)R); break;\n\t\t\t\t\tcase 'k': hr = p->get_CurrentKeyboardShortcut((BSTR*)R); break;\n\t\t\t\t\tcase 'a': hr = p->get_CurrentDefaultAction((BSTR*)R); break;\n\t\t\t\t\tcase 's': hr = p->get_CurrentState((DWORD*)R); break;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (GetKeyState(VK_NUMLOCK) & 1) {\n\t\t\t\tHRESULT hr2 = 1;\n\t\t\t\tif (prop == 's') {\n\t\t\t\t\t//DWORD s1 = *(DWORD*)R;\n\t\t\t\t\t//DWORD R2 = 0; R = &R2;\n\t\t\t\t\t//BOOL bo = 0;\n\t\t\t\t\t//if (0 == _ae->get_CurrentHasKeyboardFocus(&bo) && bo) R2 |= STATE_SYSTEM_FOCUSED;\n\t\t\t\t\t//if (0 == _ae->get_CurrentIsKeyboardFocusable(&bo) && bo) R2 |= STATE_SYSTEM_FOCUSABLE;\n\t\t\t\t\t//if (0 == _ae->get_CurrentIsEnabled(&bo) && !bo) R2 |= STATE_SYSTEM_UNAVAILABLE;\n\t\t\t\t\t//if (0 == _ae->get_CurrentIsOffscreen(&bo) && bo) R2 |= STATE_SYSTEM_OFFSCREEN;\n\t\t\t\t\t//if (0 == _ae->get_CurrentIsPassword(&bo) && bo) R2 |= STATE_SYSTEM_PROTECTED;\n\t\t\t\t\t//if (0 == _ae->) R2 |= STATE_SYSTEM_\n\t\t\t\t\t//Probably don't need this test code. All tested UIA-only elements had correct state retrieved with the legacy accessible pattern.\n\t\t\t\t} else if (prop != 'a') {\n\t\t\t\t\tint len1 = hr == 0 ? SysStringLen(*(BSTR*)R) : 0;\n\t\t\t\t\tBSTR b1 = *(BSTR*)R;\n\t\t\t\t\tBSTR R2 = null; R = &R2;\n\t\t\t\t\tif (prop == 'd') {\n\t\t\t\t\t\tVARIANT v = {};\n\t\t\t\t\t\thr2 = _ae->GetCurrentPropertyValue(UIA_FullDescriptionPropertyId, &v);\n\t\t\t\t\t\tif (hr2 == 0) {\n\t\t\t\t\t\t\tif (v.vt == VT_BSTR) *(BSTR*)R = v.bstrVal;\n\t\t\t\t\t\t\telse { hr2 = 1; VariantClear(&v); }\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (prop == 'v') {\n\t\t\t\t\t\tSmart<IUIAutomationValuePattern> p;\n\t\t\t\t\t\thr2 = _ae->GetCurrentPatternAs(UIA_ValuePatternId, IID_PPV_ARGS(&p));\n\t\t\t\t\t\tif (hr2 == 0 && !p) hr2 = 1;\n\t\t\t\t\t\tif (hr2 == 0) hr2 = p->get_CurrentValue((BSTR*)R);\n\t\t\t\t\t} else if (prop == 'h') {\n\t\t\t\t\t\thr2 = _ae->get_CurrentHelpText((BSTR*)R);\n\t\t\t\t\t} else if (prop == 'k') {\n\t\t\t\t\t\thr2 = _ae->get_CurrentAcceleratorKey((BSTR*)R);\n\t\t\t\t\t\t//hr2 = _ae->get_CurrentAccessKey((BSTR*)R);\n\t\t\t\t\t}\n\t\t\t\t\tint len2 = hr2 == 0 ? SysStringLen(*(BSTR*)R) : 0;\n\t\t\t\t\tBSTR b2 = *(BSTR*)R;\n\n\t\t\t\t\tif (len1 > 0 || len2 > 0) {\n\t\t\t\t\t\tBSTR provider = null;\n\t\t\t\t\t\t_ae->get_CurrentProviderDescription(&provider);\n\n\t\t\t\t\t\tif (len1 == len2 && wcscmp(b1, b2) == 0) Printf(L\"same: %s        provider=%s\", b1, provider);\n\t\t\t\t\t\telse if (len1 > 0 && len2 > 0) Printf(L\"<><c 0xff>diff:</c> msaa=%s, uia=%s        provider=%s\", b1, b2, provider);\n\t\t\t\t\t\telse if (len1 > 0) Printf(L\"<><c 0xff>msaa:</c> %s        hr2=0x%x isnull=%i    provider=%s\", b1, hr2, b2 == null, provider);\n\t\t\t\t\t\telse Printf(L\"<><c 0xff>uia:</c> %s        hr1=0x%x isnull=%i    provider=%s\", b2, hr, b1 == null, provider);\n\n\t\t\t\t\t\t//if (len1 > 0 && len2 == 0) PrintUiaElem(_ae);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n#endif\n\n\t\t\treturn hr;\n\t\t}\n\n#if true\n\t\tHWND _GetHWND() {\n\t\t\t//PRINTS(__FUNCTIONW__);\n\t\t\tfor (auto e = _ae; ;) {\n\t\t\t\tUIA_HWND w = 0;\n\t\t\t\tHRESULT hr = e->get_CurrentNativeWindowHandle(&w);\n\t\t\t\tif (hr || w) {\n\t\t\t\t\tif (e != _ae) e->Release();\n\t\t\t\t\tif (hr) break;\n\t\t\t\t\treturn (HWND)w;\n\t\t\t\t}\n\t\t\t\tIUIAutomationElement* parent = null;\n\t\t\t\thr = WalkAll()->GetParentElement(e, &parent);\n\t\t\t\tif (e != _ae) e->Release();\n\t\t\t\tif (hr || !parent) break;\n\t\t\t\te = parent;\n\t\t\t}\n\t\t\treturn 0;\n\t\t}\n#elif true\n\t\t//50-100% slower. May get not the direct parent control.\n\t\tHWND _GetHWND() {\n\t\t\t//PRINTS(__FUNCTIONW__);\n\t\t\tUIA_HWND w = 0;\n\t\t\tHRESULT hr = _ae->get_CurrentNativeWindowHandle(&w);\n\t\t\tif (hr) return 0;\n\t\t\tif (w) return (HWND)w;\n\n\t\t\tSmart<IUIAutomationCondition> cond;\n\t\t\tif (0 != UIA()->CreatePropertyCondition(UIA_ControlTypePropertyId, ao::VE(UIA_WindowControlTypeId), &cond)) return 0;\n\t\t\tSmart<IUIAutomationTreeWalker> walker;\n\t\t\tif (0 != UIA()->CreateTreeWalker(cond, &walker)) return 0;\n\t\t\tPerf.Next();\n\n\t\t\tSmart<IUIAutomationElement> p;\n\t\t\thr = walker->GetParentElement(_ae, &p); //why so slow?\n\t\t\tif (hr == 0 && !p) hr = 1;\n\t\t\tPerf.Next();\n\t\t\tif (hr == 0) hr = p->get_CurrentNativeWindowHandle(&w);\n\t\t\tif (hr) return 0;\n\t\t\treturn (HWND)w;\n\t\t}\n#else\n\t\t//Fast, but unreliable. Many objects don't have RuntimeId or it does not contain HWND.\n\t\tHWND _GetHWND() {\n\t\t\t//PRINTS(__FUNCTIONW__);\n\t\t\t//UIA_HWND w = 0;\n\t\t\t//HRESULT hr = _ae->get_CurrentNativeWindowHandle(&w);\n\t\t\t//if(hr) return 0;\n\t\t\t//if(w) return (HWND)w;\n\n\t\t\tSAFEARRAY* a = null;\n\t\t\tif (0 == _ae->GetRuntimeId(&a)) {\n\t\t\t\tHWND w = 0;\n\t\t\t\tif (a->cbElements == 4 && a->rgsabound[0].cElements >= 2) {\n\t\t\t\t\tint* p = (int*)a->pvData;\n\t\t\t\t\tif (p[0] == 0x2A) {\n\t\t\t\t\t\tw = (HWND)(LPARAM)p[1];\n\t\t\t\t\t\tassert(IsWindow(w));\n\t\t\t\t\t\tif (!IsWindow(w)) w = 0;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tSafeArrayDestroy(a);\n\t\t\t\treturn w;\n\t\t\t}\n\n\t\t\treturn 0;\n\t\t}\n#endif\n\n#pragma endregion\n\n\t\t// Inherited via IServiceProvider\n\t\t//virtual HRESULT QueryService(REFGUID guidService, REFIID riid, void ** ppvObject) override\n\t\t//{\n\t\t//\tPrint(\"QS:\");\n\t\t//\tPrintGuid(riid);\n\t\t//\treturn E_NOTIMPL;\n\t\t//}\n\t};\n\n\tHRESULT AccFromWindow(HWND w, out IAccessible** iacc) {\n\t\tIUIAutomationElement* e = null;\n\t\tHRESULT hr = UIA()->ElementFromHandle(w, &e);\n\t\t*iacc = hr == 0 ? new UIAccessible(e) : null;\n\t\treturn hr;\n\t}\n\n\tHRESULT AccFromPoint(POINT p, out IAccessible** iacc) {\n\t\tIUIAutomationElement* e = null;\n\t\tHRESULT hr = UIA()->ElementFromPoint(p, &e);\n\t\t*iacc = hr == 0 ? new UIAccessible(e) : null;\n\t\treturn hr;\n\t}\n\n\tHRESULT AccFocused(out IAccessible** iacc) {\n\t\tIUIAutomationElement* e = null;\n\t\tHRESULT hr = UIA()->GetFocusedElement(&e);\n\t\t*iacc = hr == 0 ? new UIAccessible(e) : null;\n\t\treturn hr;\n\t}\n\n\t//Fails with most. When succeeds, the object often is half-valid.\n\t//HRESULT AccFromMSAA(IAccessible* msaa, int elem, out IAccessible** iacc)\n\t//{\n\t//\tIUIAutomationElement* e = null;\n\t//\tHRESULT hr = UIA()->ElementFromIAccessible(msaa, elem, &e);\n\t//\t*iacc = hr == 0 ? new UIAccessible(e) : null;\n\t//\treturn hr;\n\t//}\n\n} //namespace uia\n\nHRESULT AccUiaFromWindow(HWND w, out IAccessible** iacc) {\n\treturn uia::AccFromWindow(w, iacc);\n}\n\nHRESULT AccUiaFromPoint(POINT p, out IAccessible** iacc) {\n\treturn uia::AccFromPoint(p, iacc);\n}\n\nHRESULT AccUiaFocused(out IAccessible** iacc) {\n\treturn uia::AccFocused(iacc);\n}\n\nIUIAutomation* UIA() { return uia::UIA(); }\n\n//HRESULT AccUiaFromMSAA(IAccessible* msaa, int elem, out IAccessible** iacc)\n//{\n//\treturn uia::AccFromMSAA(msaa, elem, iacc);\n//}\n"
  },
  {
    "path": "Cpp/acc web.cpp",
    "content": "#include \"stdafx.h\"\n#include \"cpp.h\"\n#include \"acc.h\"\n#include <mshtml.h>\n#include \"ISimpleDOMNode.h\"\n#include \"ISimpleDOMText.h\"\n\nnamespace {\n\tclass IEElem {\n\t\tIHTMLElement* _x;\n\tpublic:\n\t\tIEElem() {\n\t\t\t_x = null;\n\t\t}\n\n\t\t~IEElem() {\n\t\t\tif (_x) _x->Release();\n\t\t}\n\n\t\tIHTMLElement* operator ->() {\n\t\t\treturn _x;\n\t\t}\n\n\t\toperator bool() {\n\t\t\treturn _x != null;\n\t\t}\n\n\t\tbool FromAcc(IAccessible* iacc) {\n\t\t\tassert(_x == null);\n\t\t\treturn QueryService(iacc, out & _x);\n\t\t}\n\n\t\tBSTR GetAttribute(STR name) {\n\t\t\tif (CMP5(name, L\"class\")) {\n\t\t\t\tBSTR b = null;\n\t\t\t\t_x->get_className(&b);\n\t\t\t\treturn b;\n\t\t\t} else {\n\t\t\t\tVARIANT v;\n\t\t\t\tif (0 != _x->getAttribute(Bstr(name), 2, &v)) return null;\n\t\t\t\tif (v.vt != VT_BSTR && 0 != VariantChangeType(&v, &v, 0, VT_BSTR)) { VariantClear(&v); return null; }\n\t\t\t\treturn v.bstrVal;\n\t\t\t}\n\t\t}\n\n\t\t//Returns null if fails or has 0 attributes.\n\t\t//Later delete[] the result.\n\t\tBstrNameValue* GetAttributes(out int& count) {\n\t\t\tcount = 0;\n\t\t\tBstrNameValue* a = null;\n\t\t\tIHTMLElement5* e5 = null;\n\t\t\tif (0 != _x->QueryInterface(&e5)) return null;\n\t\t\tIHTMLAttributeCollection3* col = null;\n\t\t\tif (0 == e5->get_attributes(&col)) {\n\t\t\t\tlong n;\n\t\t\t\tif (0 == col->get_length(&n) && n > 0) {\n\t\t\t\t\tcount = n;\n\t\t\t\t\ta = new BstrNameValue[n];\n\t\t\t\t\tfor (long i = 0; i < n; i++) {\n\t\t\t\t\t\tIHTMLDOMAttribute* ida = null;\n\t\t\t\t\t\tif (0 == col->item(i, &ida)) {\n\t\t\t\t\t\t\t_variant_t va;\n\t\t\t\t\t\t\tif (0 == ida->get_nodeName(&a[i].name) && 0 == ida->get_nodeValue(&va)) {\n\t\t\t\t\t\t\t\tif (va.vt == VT_BSTR || 0 == VariantChangeType(&va, &va, 0, VT_BSTR))\n\t\t\t\t\t\t\t\t\ta[i].value.Attach(va.Detach().bstrVal);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tida->Release();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcol->Release();\n\t\t\t}\n\t\t\te5->Release();\n\t\t\treturn a;\n\t\t}\n\n\t};\n\n\tclass HtmlNode {\n\t\tstatic constexpr GUID IID_ISimpleDOMNodeService = { 0x0c539790, 0x12e4, 0x11cf, 0xb6, 0x61, 0x00, 0xaa, 0x00, 0x4c, 0xd6, 0xd8 };\n\n\t\tISimpleDOMNode* _x;\n\n\t\tHtmlNode(ISimpleDOMNode* x) {\n\t\t\t_x = x;\n\t\t}\n\tpublic:\n\t\tHtmlNode() {\n\t\t\t_x = null;\n\t\t}\n\n\t\t~HtmlNode() {\n\t\t\tif (_x) _x->Release();\n\t\t}\n\n\t\tISimpleDOMNode* operator ->() {\n\t\t\treturn _x;\n\t\t}\n\n\t\toperator bool() {\n\t\t\treturn _x != null;\n\t\t}\n\n\t\tISimpleDOMNode* Detach() {\n\t\t\tauto r = _x; _x = null; return r;\n\t\t}\n\n\n\t\tbool FromAcc(IAccessible* iacc) {\n\t\t\tassert(_x == null);\n\t\t\treturn QueryService(iacc, &_x, &IID_ISimpleDOMNodeService);\n\t\t}\n\n\t\tstruct NodeInfo {\n\t\t\tBstr tag, text;\n\t\t\tUINT childCount, uniqueId;\n\t\t\tshort namespaceId;\n\t\t\tunsigned short nodeType;\n\t\t};\n\n\t\tbool GetNodeInfo(out NodeInfo& r, bool needText) {\n\t\t\tassert(!r.tag && !r.text);\n\t\t\tif (0 != _x->get_nodeInfo(&r.tag, &r.namespaceId, &r.text, &r.childCount, &r.uniqueId, &r.nodeType)) return false;\n\t\t\tif (needText) {\n\t\t\t\tif (r.nodeType == NODETYPE_TEXT && (!r.text || !r.text.Length())) { //Chrome\n\t\t\t\t\tif (r.text) r.text.Empty();\n\t\t\t\t\tISimpleDOMText* dt = null;\n\t\t\t\t\tif (0 == _x->QueryInterface(&dt)) {\n\t\t\t\t\t\tif (0 != dt->get_domText(&r.text) || r.text.Length() == 0) r.text.Empty();\n\t\t\t\t\t\tdt->Release();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (r.text) r.text.Empty();\n\t\t\treturn true;\n\t\t}\n\n\t\tBSTR GetTag() {\n\t\t\tNodeInfo x;\n\t\t\tif (!GetNodeInfo(x, true)) return null;\n\t\t\treturn x.tag.Detach();\n\t\t}\n\n#if false //get_attributesForNames broken in new Chrome\n\t\tBSTR GetAttribute(STR name) {\n\t\t\tBstr bn(name); short ns = 0; BSTR r = null;\n\t\t\tif (0 != _x->get_attributesForNames(1, &bn, &ns, &r)) return null;\n\n\t\t\t//problem: Firefox gets \"\" for missing attributes. I don't know a fast workaround. Chrome and IE then fail (null).\n\t\t\t//if(r != null && *r == 0) {\n\t\t\t//\tIAccessible2_2* ia2=null;\n\t\t\t//\tif(0==_x->QueryInterface(&ia2)) {\n\t\t\t//\t\t_variant_t v;\n\t\t\t//\t\tPrint((uint)ia2->get_attribute(bn, &v)); //E_NOTIMPL\n\t\t\t//\t\tia2->Release();\n\t\t\t//\t}\n\t\t\t//}\n\n\t\t\treturn r;\n\n\t\t\t//problem: Chrome case-sensitive. Firefox and IE not.\n\t\t}\n#else\n\t\tBSTR GetAttribute(STR name) {\n\t\t\tint n = 0;\n\t\t\tBstrNameValue* a = GetAttributes(out n);\n\t\t\tif (a == null) return null;\n\t\t\tBSTR R = null;\n\t\t\tfor (int i = 0; i < n; i++) {\n\t\t\t\tif (a[i].name.Equals(name, true)) { R = a[i].value.Detach(); break; }\n\t\t\t}\n\t\t\tdelete[] a;\n\t\t\treturn R;\n\t\t}\n#endif\n\n\t\t//Returns null if fails or has 0 attributes.\n\t\t//Later delete[] the result.\n\t\tBstrNameValue* GetAttributes(out int& count) {\n\t\t\tBSTR na[300], va[_countof(na)]; short nsa[_countof(na)]; unsigned short n = 0; //max seen: 22 in FF UI, 16 in web page (rarely > 12)\n\t\t\tif (0 != _x->get_attributes(_countof(na), na, nsa, va, &n) || n == 0) { count = 0; return null; } //new FF returns E_NOTIMPL\n\t\t\tcount = n;\n\t\t\tauto a = new BstrNameValue[n];\n\t\t\tfor (int i = 0; i < n; i++) {\n\t\t\t\tBstrNameValue& r = a[i];\n\t\t\t\tr.name.Attach(na[i]);\n\t\t\t\tr.value.Attach(va[i]);\n\t\t\t}\n\t\t\treturn a;\n\t\t}\n\n\t\t//Chrome returns max 1 style, always \"display\". Not useful.\n\t\t////Returns null if fails or has 0 styles.\n\t\t////Later delete[] the result.\n\t\t//BstrNameValue* GetStyles(out int& count)\n\t\t//{\n\t\t//\tBSTR na[300], va[_countof(na)]; unsigned short n = 0;\n\t\t//\tif(0 != _x->get_computedStyle(_countof(na), false, na, va, &n) || n == 0) { count = 0; return null; }\n\t\t//\tcount = n;\n\t\t//\tauto a = new BstrNameValue[n];\n\t\t//\tfor(int i = 0; i < n; i++) {\n\t\t//\t\tBstrNameValue& r = a[i];\n\t\t//\t\tr.name.Attach(na[i]);\n\t\t//\t\tr.value.Attach(va[i]);\n\t\t//\t}\n\t\t//\treturn a;\n\t\t//}\n\n\t\tBSTR GetInnerHTML() {\n\t\t\tBstr s;\n\t\t\tHRESULT hr = _x->get_innerHTML(&s);\n\t\t\tif (hr) {\n\t\t\t\tNodeInfo x;\n\t\t\t\tif (GetNodeInfo(x, true)) {\n\t\t\t\t\tif (hr == E_NOTIMPL) { //Chrome does not implement this method. Workaround: compose from descendants.\n\t\t\t\t\t\thr = 0;\n\t\t\t\t\t\tif (x.childCount > 0) {\n\t\t\t\t\t\t\tstr::StringBuilder b;\n\t\t\t\t\t\t\t_ChromeComposeInnerHTML(b, x.childCount);\n\t\t\t\t\t\t\ts = (LPWSTR)b;\n\t\t\t\t\t\t} else s = L\"\";\n\t\t\t\t\t} else { //Firefox does not give HTML for document. Get it from its descendant <BODY>.\n\t\t\t\t\t\treturn _FirefoxGetBodyHtml(x.childCount, false);\n\t\t\t\t\t}\n\t\t\t\t} else PRINTS(L\"failed\");\n\t\t\t}\n\t\t\treturn hr ? null : s.Detach();\n\t\t}\n\n\t\tBSTR GetOuterHTML() {\n\t\t\tNodeInfo x;\n\t\t\tif (!GetNodeInfo(x, true)) { PRINTS(L\"failed\"); return null; }\n\n\t\t\tif (str::IsEmpty(x.tag)) {\n\t\t\t\tPRINTF_IF(x.nodeType != NODETYPE_TEXT, L\"--- not Text: %i\", x.nodeType);\n\t\t\t\treturn x.text.Detach();\n\t\t\t}\n\n\t\t\t//ISimpleDOMNode does not have a method to get outer HTML. Compose it from tag, attributes and inner HTML.\n\t\t\tbool isDoc = x.nodeType == NODETYPE_DOCUMENT;\n\t\t\tif (isDoc) x.tag = L\"body\"; //\"#document\"\n\t\t\tPRINTF_IF(x.nodeType != NODETYPE_ELEMENT && !isDoc && x.tag != L\"br\", L\"--- not Element: %i\", x.nodeType);\n\t\t\tstr::StringBuilder b;\n\t\t\t_HtmlAppendHead(b, x.tag);\n\t\t\tif (x.childCount > 0) {\n\t\t\t\tBstr inner;\n\t\t\t\tint hr = _x->get_innerHTML(&inner);\n\t\t\t\tif (hr == 0) {\n\t\t\t\t\tb.AppendBSTR(inner);\n\t\t\t\t} else if (hr == E_NOTIMPL) { //Chrome does not implement this method. Workaround: compose from descendants.\n\t\t\t\t\t_ChromeComposeInnerHTML(b, x.childCount);\n\t\t\t\t} else if (isDoc) { //Firefox does not give HTML for document. Get it from its descendant <BODY>.\n\t\t\t\t\treturn _FirefoxGetBodyHtml(x.childCount, true);\n\t\t\t\t}\n\t\t\t}\n\t\t\t_HtmlAppendTail(b, x.tag);\n\t\t\treturn b.ToBSTR();\n\t\t}\n\n\tprivate:\n#pragma region private\n\t\tvoid _ChromeComposeInnerHTML(str::StringBuilder& b, int childCount) {\n\t\t\tfor (int i = 0; i < childCount; i++) {\n\t\t\t\tHtmlNode child;\n\t\t\t\tif (0 != _x->get_childAt(i, &child._x)) { PRINTS(L\"failed\"); if (i == 0) return; continue; }\n\t\t\t\tchild._ChromeComposeHTML(b);\n\t\t\t}\n\t\t}\n\n\t\tvoid _ChromeComposeHTML(str::StringBuilder& b) {\n\t\t\tNodeInfo r;\n\t\t\tif (!GetNodeInfo(r, true)) { PRINTS(L\"failed\"); return; }\n\t\t\tif (str::IsEmpty(r.tag)) {\n\t\t\t\tb.AppendBSTR(r.text);\n\t\t\t} else {\n\t\t\t\t_HtmlAppendHead(b, r.tag);\n\t\t\t\t_ChromeComposeInnerHTML(b, r.childCount);\n\t\t\t\t_HtmlAppendTail(b, r.tag);\n\t\t\t}\n\t\t}\n\n\t\tvoid _HtmlAppendHead(str::StringBuilder& b, STR tag) {\n\t\t\tb << '<'; b << tag;\n\t\t\tint n;\n\t\t\tBstrNameValue* a = GetAttributes(out n);\n\t\t\tif (a) {\n\t\t\t\tfor (int i = 0; i < n; i++) {\n\t\t\t\t\tb << ' '; b.AppendBSTR(a[i].name); b << '='; b << '\\\"'; b.AppendBSTR(a[i].value); b << '\\\"';\n\t\t\t\t}\n\t\t\t\tdelete[] a;\n\t\t\t}\n\t\t\tb << '>';\n\t\t}\n\n\t\tvoid _HtmlAppendTail(str::StringBuilder& b, STR tag) {\n\t\t\tb << '<'; b << '/'; b << tag; b << '>';\n\t\t}\n\n\t\tISimpleDOMNode* _FindChild(int childCount, int nodeType, STR tag, int lenT, out int& childChildCount) {\n\t\t\t//search in reverse order, because usually what we need is the last child.\n\t\t\t//\tDocument often has 2 children: doctype and HTML.\n\t\t\t//\tHTML usually has 2 children: HEAD and BODY.\n\t\t\tfor (int i = childCount; i > 0; i--) {\n\t\t\t\tHtmlNode child;\n\t\t\t\tif (0 != _x->get_childAt(i - 1, &child._x)) continue;\n\t\t\t\tNodeInfo info;\n\t\t\t\tif (child.GetNodeInfo(info, false)\n\t\t\t\t\t&& info.nodeType == nodeType\n\t\t\t\t\t&& info.tag.Equals(tag, lenT, true)\n\t\t\t\t\t) {\n\t\t\t\t\tchildChildCount = info.childCount;\n\t\t\t\t\treturn child.Detach();\n\t\t\t\t} else PRINTS(L\"failed\");\n\t\t\t}\n\t\t\tchildChildCount = 0;\n\t\t\treturn null;\n\t\t}\n\n\t\t//note: This code is for old Firefox versions. Now Firefox does not support getting HTML etc.\n\t\tBSTR _FirefoxGetBodyHtml(int childCount, bool outer) {\n\t\t\tint cc2, cc3;\n\t\t\tHtmlNode childHTML(_FindChild(childCount, NODETYPE_ELEMENT, L\"HTML\", 4, out cc2));\n\t\t\tif (childHTML) {\n\t\t\t\t//get BODY, not whole HTML. Like IE and Chrome.\n\t\t\t\tHtmlNode childBODY(childHTML._FindChild(cc2, NODETYPE_ELEMENT, L\"BODY\", 4, out cc3));\n\t\t\t\tif (childBODY) {\n\t\t\t\t\tif (outer) return childBODY.GetOuterHTML();\n\t\t\t\t\tBSTR s = null;\n\t\t\t\t\tif (0 == childBODY->get_innerHTML(&s)) return s;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n#pragma endregion\n\t};\n\n\tclass _BrowserInterface {\n\tpublic:\n\t\tIEElem ie;\n\t\tHtmlNode node;\n\n\t\t_BrowserInterface() { ZEROTHIS; }\n\n\t\tbool Init(IAccessible* iacc) {\n\t\t\tbool ok;\n\t\t\tstatic thread_local bool t_preferIE;\n\t\t\tif (t_preferIE) { //if previously was IE, now try IE first, to make faster\n\t\t\t\tok = ie.FromAcc(iacc) || node.FromAcc(iacc);\n\t\t\t} else {\n\t\t\t\tok = node.FromAcc(iacc) || ie.FromAcc(iacc);\n\t\t\t}\n\t\t\tt_preferIE = !!ie;\n\t\t\treturn ok;\n\t\t}\n\t};\n} //namespace\n\n//Gets/compares specified HTML attributes and returns true if all match.\n//Returns false if cannot get HTML attributes, for example if this is not a HTML element, or if called not inproc.\n//Supports Chrome, Internet Explorer, Firefox (except in web content) and apps that use their code.\n//Names of HTML attributes must be with \"@\" prefix, like \"@href\". Other names are ignored.\nbool AccMatchHtmlAttributes(IAccessible* iacc, NameValue* prop, int count) {\n\t_BrowserInterface bi; bool isBI = false;\n\tfor (int i = 0; i < count; i++) {\n\t\tSTR name = prop[i].name;\n\t\tif (*name++ != '@') continue;\n\t\tif (!isBI && !(isBI = bi.Init(iacc))) return false;\n\t\tBSTR b = bi.ie ? bi.ie.GetAttribute(name) : bi.node.GetAttribute(name);\n\t\tbool yes = prop[i].value.Match(b ? b : L\"\", b ? SysStringLen(b) : 0);\n\t\tif (b) SysFreeString(b);\n\t\tif (!yes) return false;\n\t}\n\treturn true;\n}\n\nbool AccChromeEnableHtml(IAccessible* aDoc) {\n\tHtmlNode node;\n\tif (node.FromAcc(aDoc)) {\n\t\tBstr b(node.GetTag());\n\t\t//Printf(L\"yes, %s\", b.m_str);\n\t\tif (b.m_str == null) return false;\n\t}\n\treturn true;\n}\n\nHRESULT AccWeb(IAccessible* iacc, STR what, out BSTR& sResult) {\n\t//Perf.First();\n\t_BrowserInterface bi;\n\tif (!bi.Init(iacc)) return E_NOINTERFACE;\n\t//Perf.Next();\n\tif (what[0] == '\\'') {\n\t\tswitch (what[1]) {\n\t\tcase 't': //tag\n\t\t\tif (bi.node) sResult = bi.node.GetTag();\n\t\t\telse if (0 != bi.ie->get_tagName(&sResult)) return 1;\n\t\t\tbreak;\n\t\tcase 'o': //outer HTML\n\t\t\tif (bi.node) sResult = bi.node.GetOuterHTML();\n\t\t\telse if (0 != bi.ie->get_outerHTML(&sResult)) return 1;\n\t\t\tbreak;\n\t\tcase 'i': //inner HTML\n\t\t\tif (bi.node) sResult = bi.node.GetInnerHTML();\n\t\t\telse if (0 != bi.ie->get_innerHTML(&sResult)) return 1;\n\t\t\tbreak;\n\t\tcase 'a':\n\t\t{ //attributes\n\t\t\tint n;\n\t\t\tauto a = bi.ie ? bi.ie.GetAttributes(out n) : bi.node.GetAttributes(out n);\n\t\t\t//Perf.Next();\n\t\t\tif (a) {\n\t\t\t\tstr::StringBuilder b;\n\t\t\t\tfor (int i = 0; i < n; i++) {\n\t\t\t\t\tBstrNameValue& r = a[i];\n\t\t\t\t\tif (r.name.Length() == 0) continue;\n\t\t\t\t\t//if(bi.ie && r.value.Length() == 0 && r.name == L\"shape\") continue; //somehow IE adds attributes that don't exist in the HTML\n\t\t\t\t\tb.AppendBSTR(r.name); b << '='; b.AppendBSTR(r.value); b << '\\0';\n\t\t\t\t}\n\t\t\t\tdelete[] a;\n\t\t\t\tsResult = b.ToBSTR();\n\t\t\t}\n\t\t} break;\n\t\tcase 's':\n\t\t{ //scroll\n\t\t\tif (bi.node) {\n\t\t\t\tif (0 != bi.node->scrollTo(true)) return 1;\n\t\t\t} else {\n\t\t\t\tif (0 != bi.ie->scrollIntoView(_variant_t(true))) return 1;\n\t\t\t}\n\t\t} break;\n\t\tdefault: assert(false); return (HRESULT)eError::InvalidParameter;\n\t\t}\n\t} else { //attribute\n\t\tsResult = bi.ie ? bi.ie.GetAttribute(what) : bi.node.GetAttribute(what);\n\t\t//note: for missing attributes Chrome/IE return null, but Firefox \"\".\n\t}\n\t//Perf.NW();\n\treturn 0;\n}\n\nnamespace outproc {\n\t//what - \"'t\" tag, \"'o\" outer HTML, \"'i\" inner HTML, \"'a\" attributes, \"'s\" scroll, \"attributeName\".\n\tEXPORT HRESULT Cpp_AccWeb(Cpp_Acc a, STR what, out BSTR& sResult) {\n\t\tsResult = null;\n\t\tif (!(a.misc.flags & eAccMiscFlags::InProc)) return E_NOINTERFACE;\n\t\tif (a.elem) return 1; //eg TEXT of LINK in IE. Let use the LINK instead.\n\n\t\tInProcCall ic;\n\t\tauto len = str::Len(what);\n\t\tauto memSize = sizeof(MarshalParams_Header) + (len + 1) * 2;\n\t\tauto p = ic.AllocParams(&a, InProcAction::IPA_AccGetHtml, memSize);\n\t\tauto s = (LPWSTR)(p + 1); memcpy(s, what, len * 2); s[len] = 0;\n\t\tHRESULT hr = ic.Call();\n\t\tif (hr) return hr;\n\t\tsResult = ic.DetachResultBSTR();\n\t\treturn 0;\n\t}\n}\n"
  },
  {
    "path": "Cpp/acc workaround.cpp",
    "content": "#include \"stdafx.h\"\n#include \"cpp.h\"\n\n//Workaround for .NET bug: calls IAccessible implementation methods in other thread.\n\nclass AccWorkaround :IAccessible\n{\n\tIAccessible* _a;\n\tint _ref;\n\npublic:\n\tAccWorkaround(IAccessible* a) {\n\t\t_a = a;\n\t\t_ref = 1;\n\t}\n#pragma region\n\tvirtual HRESULT __stdcall QueryInterface(REFIID riid, void** ppvObject) override\n\t{\n\t\tif(riid == IID_IUnknown || riid == IID_IDispatch || riid == IID_IAccessible) {\n\t\t\t*ppvObject = this;\n\t\t\t_ref++;\n\t\t\treturn 0;\n\t\t}\n\t\t*ppvObject = 0;\n\t\treturn E_NOINTERFACE;\n\t}\n\tvirtual ULONG __stdcall AddRef(void) override\n\t{\n\t\treturn ++_ref;\n\t}\n\tvirtual ULONG __stdcall Release(void) override\n\t{\n\t\tint r = --_ref;\n\t\t//Print(r);\n\t\tif(r == 0) delete this;\n\t\treturn r;\n\t}\n\tvirtual HRESULT __stdcall GetTypeInfoCount(UINT* pctinfo) override\n\t{\n\t\treturn E_NOTIMPL;\n\t}\n\tvirtual HRESULT __stdcall GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo) override\n\t{\n\t\treturn E_NOTIMPL;\n\t}\n\tvirtual HRESULT __stdcall GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgDispId) override\n\t{\n\t\treturn E_NOTIMPL;\n\t}\n\tvirtual HRESULT __stdcall Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr) override\n\t{\n\t\treturn E_NOTIMPL;\n\t}\n#pragma endregion\n\tvirtual HRESULT __stdcall get_accParent(IDispatch** ppdispParent) override\n\t{\n\t\treturn _a->get_accParent(ppdispParent);\n\t}\n\tvirtual HRESULT __stdcall get_accChildCount(long* pcountChildren) override\n\t{\n\t\t//Print(__FUNCTION__);\n\t\treturn _a->get_accChildCount(pcountChildren);\n\t}\n\tvirtual HRESULT __stdcall get_accChild(VARIANT varChild, IDispatch** ppdispChild) override\n\t{\n\t\treturn _a->get_accChild(varChild, ppdispChild);\n\t}\n\tvirtual HRESULT __stdcall get_accName(VARIANT varChild, BSTR* pszName) override\n\t{\n\t\treturn _a->get_accName(varChild, pszName);\n\t}\n\tvirtual HRESULT __stdcall get_accValue(VARIANT varChild, BSTR* pszValue) override\n\t{\n\t\treturn _a->get_accValue(varChild, pszValue);\n\t}\n\tvirtual HRESULT __stdcall get_accDescription(VARIANT varChild, BSTR* pszDescription) override\n\t{\n\t\treturn _a->get_accDescription(varChild, pszDescription);\n\t}\n\tvirtual HRESULT __stdcall get_accRole(VARIANT varChild, VARIANT* pvarRole) override\n\t{\n\t\treturn _a->get_accRole(varChild, pvarRole);\n\t}\n\tvirtual HRESULT __stdcall get_accState(VARIANT varChild, VARIANT* pvarState) override\n\t{\n\t\treturn _a->get_accState(varChild, pvarState);\n\t}\n\tvirtual HRESULT __stdcall get_accHelp(VARIANT varChild, BSTR* pszHelp) override\n\t{\n\t\treturn _a->get_accHelp(varChild, pszHelp);\n\t}\n\tvirtual HRESULT __stdcall get_accHelpTopic(BSTR* pszHelpFile, VARIANT varChild, long* pidTopic) override\n\t{\n\t\treturn E_NOTIMPL;\n\t}\n\tvirtual HRESULT __stdcall get_accKeyboardShortcut(VARIANT varChild, BSTR* pszKeyboardShortcut) override\n\t{\n\t\treturn _a->get_accKeyboardShortcut(varChild, pszKeyboardShortcut);\n\t}\n\tvirtual HRESULT __stdcall get_accFocus(VARIANT* pvarChild) override\n\t{\n\t\treturn _a->get_accFocus(pvarChild);\n\t}\n\tvirtual HRESULT __stdcall get_accSelection(VARIANT* pvarChildren) override\n\t{\n\t\treturn _a->get_accSelection(pvarChildren);\n\t}\n\tvirtual HRESULT __stdcall get_accDefaultAction(VARIANT varChild, BSTR* pszDefaultAction) override\n\t{\n\t\treturn _a->get_accDefaultAction(varChild, pszDefaultAction);\n\t}\n\tvirtual HRESULT __stdcall accSelect(long flagsSelect, VARIANT varChild) override\n\t{\n\t\treturn _a->accSelect(flagsSelect, varChild);\n\t}\n\tvirtual HRESULT __stdcall accLocation(long* pxLeft, long* pyTop, long* pcxWidth, long* pcyHeight, VARIANT varChild) override\n\t{\n\t\treturn _a->accLocation(pxLeft, pyTop, pcxWidth, pcyHeight, varChild);\n\t}\n\tvirtual HRESULT __stdcall accNavigate(long navDir, VARIANT varStart, VARIANT* pvarEndUpAt) override\n\t{\n\t\treturn _a->accNavigate(navDir, varStart, pvarEndUpAt);\n\t}\n\tvirtual HRESULT __stdcall accHitTest(long xLeft, long yTop, VARIANT* pvarChild) override\n\t{\n\t\treturn _a->accHitTest(xLeft, yTop, pvarChild);\n\t}\n\tvirtual HRESULT __stdcall accDoDefaultAction(VARIANT varChild) override\n\t{\n\t\treturn _a->accDoDefaultAction(varChild);\n\t}\n\tvirtual HRESULT __stdcall put_accName(VARIANT varChild, BSTR szName) override\n\t{\n\t\treturn E_NOTIMPL;\n\t}\n\tvirtual HRESULT __stdcall put_accValue(VARIANT varChild, BSTR szValue) override\n\t{\n\t\treturn E_NOTIMPL;\n\t}\n};\n\nEXPORT LRESULT Cpp_AccWorkaround(IAccessible* a, WPARAM wParam, AccWorkaround*& m) {\n\t//Print(a->AddRef());\n\tif(a != null) {\n\t\tif(m==null) m = new AccWorkaround(a);\n\t\treturn LresultFromObject(IID_IAccessible, wParam, (LPUNKNOWN)m);\n\t} else if(m!=null){\n\t\tm->Release();\n\t\tm = null;\n\t}\n\treturn 0;\n}\n"
  },
  {
    "path": "Cpp/acc.h",
    "content": "#pragma once\n#include \"stdafx.h\"\n#include \"internal.h\"\n\n#define\tROLE_MAX 0x40 //ROLE_SYSTEM_OUTLINEBUTTON\n#define\tROLE_CUSTOM 0xFF\n\n//IAccessible helpers. All functions are static; use this class like a namespace.\n//Other helpers are in AccRaw. This class contains functions that don't depend on AccRaw.\nnamespace ao {\n\t//VARIANT(VT_I4) for IAccessible function parameters.\n\tstruct VE : VARIANT {\n\t\tVE() { vt = VT_I4; lVal = 0; }\n\t\tVE(long elem) { vt = VT_I4; lVal = elem; }\n\t};\n\n\t//Calls QueryInterface to get IAccessible from IDispatch idisp. Releases idisp.\n\t//idisp - if null, returns E_FAIL.\n\tstatic HRESULT IDispatchToIAccessible(IDispatch* idisp, out IAccessible*& iacc) {\n\t\tiacc = null;\n\t\tif (idisp == null) return E_FAIL; //note: no assert\n\t\tHRESULT hr = idisp->QueryInterface(IID_IAccessible, (void**)&iacc);\n\t\tidisp->Release();\n\t\tif (hr == 0 && iacc == null) hr = E_FAIL;\n\t\treturn hr;\n\t}\n\n\t//Calls IAccessible::get_accParent and IDispatchToIAccessible.\n\tstatic HRESULT get_accParent(IAccessible* acc, out IAccessible*& aParent) {\n\t\tassert(acc != null); if (acc == null) return E_FAIL;\n\n\t\taParent = null;\n\t\tIDispatch* idisp = null;\n\t\tHRESULT hr = acc->get_accParent(out & idisp);\n\t\tif (hr == 0) hr = IDispatchToIAccessible(idisp, out aParent);\n\t\treturn hr;\n\t}\n\n\t//Calls IAccessible::get_accChild and IDispatchToIAccessible.\n\t//elem must not be 0.\n\tstatic HRESULT get_accChild(IAccessible* acc, long elem, out IAccessible*& aChild) {\n\t\tassert(!(acc == null || elem == 0)); if (acc == null || elem == 0) return E_FAIL;\n\n\t\taChild = null;\n\t\tIDispatch* idisp = null;\n\t\tHRESULT hr = acc->get_accChild(VE(elem), &idisp);\n\t\tif (hr == 0) hr = IDispatchToIAccessible(idisp, aChild);\n\t\treturn hr;\n\t}\n\n\t//Gets role (get_accRole) as int (raw) and VARIANT.\n\t//roleInt - 0 if failed or not VT_I4.\n\tstatic HRESULT GetRoleIntAndVariant(IAccessible* acc, long elem, out int& roleInt, out _variant_t& roleVariant) {\n\t\tassert(roleVariant.vt == 0);\n\t\troleInt = 0;\n\t\tHRESULT hr = acc->get_accRole(VE(elem), &roleVariant);\n\t\tif (hr != 0) PRINTF(L\"failed to get role.  hr=0x%X  elem=%i\", hr, elem);\n\t\telse if (roleVariant.vt == VT_I4) {\n\t\t\troleInt = roleVariant.lVal;\n\t\t} else if (roleVariant.vt == VT_BSTR) {\n\t\t\tif (!roleVariant.bstrVal) { roleVariant.vt = 0; hr = 1; }\n\t\t} else hr = 1;\n\t\treturn hr;\n\t}\n\n\t//Gets role (get_accRole) as BYTE.\n\t//Returns 0 if failed. Returns ROLE_CUSTOM (0xFF) if custom (VT_BSTR or not 1-ROLE_MAX).\n\tstatic BYTE GetRoleByte(IAccessible* acc, long elem = 0) {\n\t\t_variant_t v;\n\t\tHRESULT hr = acc->get_accRole(VE(elem), &v);\n\t\tif (hr != 0) PRINTF(L\"failed to get role.  hr=0x%X  elem=%i\", hr, elem);\n\t\telse if (v.vt == VT_I4) return v.lVal > 0 && v.lVal <= ROLE_MAX ? (BYTE)v.lVal : ROLE_CUSTOM;\n\t\telse if (v.vt == VT_BSTR && v.bstrVal) return ROLE_CUSTOM;\n\t\treturn 0;\n\t}\n\n\t//Converts VARIANT role to string.\n\t//If VT_BSTR, returns role.bstrVal. If all chars ucase, makes lcase.\n\t//If VT_I4: If its a standard role, returns a static const string. Else calls VariantChangeType(role) and returns role.bstrVal.\n\t//Returns L\"\" if failed. Never null.\n\tstatic STR RoleToString(ref VARIANT& role) {\n\t\tstatic const STR s_roles[] = { L\"0\", L\"TITLEBAR\", L\"MENUBAR\", L\"SCROLLBAR\", L\"GRIP\", L\"SOUND\", L\"CURSOR\", L\"CARET\", L\"ALERT\", L\"WINDOW\", L\"CLIENT\", L\"MENUPOPUP\", L\"MENUITEM\", L\"TOOLTIP\", L\"APPLICATION\", L\"DOCUMENT\", L\"PANE\", L\"CHART\", L\"DIALOG\", L\"BORDER\", L\"GROUPING\", L\"SEPARATOR\", L\"TOOLBAR\", L\"STATUSBAR\", L\"TABLE\", L\"COLUMNHEADER\", L\"ROWHEADER\", L\"COLUMN\", L\"ROW\", L\"CELL\", L\"LINK\", L\"HELPBALLOON\", L\"CHARACTER\", L\"LIST\", L\"LISTITEM\", L\"TREE\", L\"TREEITEM\", L\"PAGETAB\", L\"PROPERTYPAGE\", L\"INDICATOR\", L\"IMAGE\", L\"STATICTEXT\", L\"TEXT\", L\"BUTTON\", L\"CHECKBOX\", L\"RADIOBUTTON\", L\"COMBOBOX\", L\"DROPLIST\", L\"PROGRESSBAR\", L\"DIAL\", L\"HOTKEYFIELD\", L\"SLIDER\", L\"SPINBUTTON\", L\"DIAGRAM\", L\"ANIMATION\", L\"EQUATION\", L\"BUTTONDROPDOWN\", L\"BUTTONMENU\", L\"BUTTONDROPDOWNGRID\", L\"WHITESPACE\", L\"PAGETABLIST\", L\"CLOCK\", L\"SPLITBUTTON\", L\"IPADDRESS\", L\"TREEBUTTON\" };\n\t\tstatic_assert(sizeof(s_roles) / sizeof(STR) == ROLE_MAX + 1);\n\t\tSTR R = null; size_t i;\n\tg1:\n\t\tswitch (role.vt) {\n\t\tcase VT_BSTR:\n\t\t\tR = role.bstrVal;\n\t\t\tif (R != null) { //lcase if need, to distinguish from standard roles\n\t\t\t\tBSTR b = role.bstrVal;\n\t\t\t\tint i, len = SysStringLen(b);\n\t\t\t\tfor (i = 0; i < len; i++) {\n\t\t\t\t\tWCHAR c = b[i]; if (c < 'A' || c > 'Z') break;\n\t\t\t\t}\n\t\t\t\tif (i == len) { //all ucase\n\t\t\t\t\tfor (i = 0; i < len; i++) b[i] += 32;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\tcase VT_I4:\n\t\t\ti = role.lVal;\n\t\t\tif (i < _countof(s_roles)) return s_roles[i];\n\t\t\tif (0 == VariantChangeType(&role, &role, 0, VT_BSTR)) goto g1;\n\t\t\tbreak;\n\t\tcase 0: break; //failed to get role\n\t\tdefault: PRINTF(L\"role.vt=%i\", role.vt);\n\t\t}\n\t\treturn R ? R : L\"\";\n\t}\n\n\tstatic int StateFromString(STR s, size_t lenS) {\n\t\tstatic const STR s_states[] = { L\"DISABLED\", L\"SELECTED\", L\"FOCUSED\", L\"PRESSED\", L\"CHECKED\", L\"INDETERMINATE\", L\"READONLY\", L\"HOTTRACKED\", L\"DEFAULT\", L\"EXPANDED\", L\"COLLAPSED\", L\"BUSY\", L\"FLOATING\", L\"MARQUEED\", L\"ANIMATED\", L\"INVISIBLE\", L\"OFFSCREEN\", L\"SIZEABLE\", L\"MOVEABLE\", L\"SELFVOICING\", L\"FOCUSABLE\", L\"SELECTABLE\", L\"LINKED\", L\"TRAVERSED\", L\"MULTISELECTABLE\", L\"EXTSELECTABLE\", L\"ALERT_LOW\", L\"ALERT_MEDIUM\", L\"ALERT_HIGH\", L\"PROTECTED\", L\"HASPOPUP\", };\n\t\tif (lenS >= 4) {\n\t\t\t__int64 fourChars = *(__int64*)s;\n\t\t\tfor (int i = 0; i < _countof(s_states); i++) {\n\t\t\t\tSTR k = s_states[i];\n\t\t\t\tif (*(__int64*)k == fourChars && !wcsncmp(s, k, lenS) && k[lenS] == 0) return (1 << i);\n\t\t\t}\n\t\t}\n\t\treturn 0;\n\t}\n\n\t//not used\n\t//Appends to b.\n\t//static void StateToString(int state, str::StringBuilder& b)\n\t//{\n\t//\tbool appendedOnce = false;\n\t//\tfor(int i = 0; i < _countof(s_states); i++) {\n\t//\t\tif(!(state & (1 << i))) continue;\n\t//\t\tif(!appendedOnce) appendedOnce = true; else b << L\", \";\n\t//\t\tb << s_states[i];\n\t//\t}\n\t//}\n\n\tstatic HRESULT get_accState(out long& state, IAccessible* iacc, long elem = 0) {\n\t\tstate = 0;\n\t\t_variant_t v;\n\t\tHRESULT hr = iacc->get_accState(VE(elem), out & v);\n\t\tif (hr == 0 && v.vt == VT_I4) state = v.lVal;\n\t\treturn hr;\n\t}\n\n\tstatic HRESULT accLocation(out RECT& r, IAccessible* iacc, long elem = 0) {\n\t\tlong x, y, wid, hei;\n\t\tHRESULT hr = iacc->accLocation(&x, &y, &wid, &hei, VE(elem));\n\t\tif (hr != 0) x = y = wid = hei = 0;\n\t\tSetRect(&r, x, y, x + wid, y + hei);\n\t\treturn hr;\n\t}\n\n#if TRACE\n\tstatic void PrintAcc(IAccessible* acc, long elem = 0, int level = 0) {\n\t\t_variant_t vr; HRESULT hr = acc->get_accRole(VE(elem), &vr);\n\t\tSTR sr = (hr == 0 && (vr.vt == VT_I4 || (vr.vt == VT_BSTR && vr.bstrVal))) ? ao::RoleToString(ref vr) : L\"<failed>\";\n\t\tBstr bn; STR sn = L\"\"; if (0 == acc->get_accName(ao::VE(elem), &bn) && bn) sn = bn;\n\n\t\tPrintf(L\"<><c 0x80>%*s%s  \\\"%s\\\"</c>\", level, L\"\", sr, sn);\n\t}\n\n\tstatic void PrintAcc(ref Cpp_Acc& a) {\n\t\tPrintAcc(a.acc, a.elem, a.misc.level);\n\t}\n#endif\n\n\t//Temporarily sets SPI_SETSCREENREADER. Restores in dtor.\n\t//It enables accessible objects and UI automation elements (AO/AE) in OpenOffice and LibreOffice. Not tested with new versions, but works.\n\tclass TempSetScreenReader {\n\t\tbool _restore;\n\tpublic:\n\t\t//Does not set SPI_SETSCREENREADER.\n\t\tTempSetScreenReader() noexcept { _restore = false; }\n\n\t\t//Calls Set() if w is not 0 and its classname matches \"SAL*FRAME\".\n\t\tTempSetScreenReader(HWND w) {\n\t\t\t_restore = false;\n\t\t\tif (w && wn::ClassNameIs(w, L\"SAL*FRAME\")) Set(w);\n\t\t}\n\n\t\t~TempSetScreenReader() {\n\t\t\tif (_restore) SystemParametersInfoW(SPI_SETSCREENREADER, 0, 0, 0);\n\t\t}\n\n\t\t//If SPI_GETSCREENREADER says false, sets SPI_SETSCREENREADER = true, and dtor will set it false.\n\t\t//note: Windows does not use a reference counting for this setting.\n\t\t//Does nothing if w has flag AccEnableYes. Adds this flag if need.\n\t\tvoid Set(HWND w) {\n\t\t\tif (!!(WinFlags::Get(w) & eWinFlags::AccEnabled)) return;\n\t\t\tint r = 0;\n\t\t\tSystemParametersInfoW(SPI_GETSCREENREADER, 0, &r, 0);\n\t\t\t_restore = r == 0 && SystemParametersInfoW(SPI_SETSCREENREADER, 1, 0, 0);\n\t\t\tif (_restore) WinFlags::Set(w, eWinFlags::AccEnabled);\n\t\t}\n\t};\n\n\t//Calls AccessibleObjectFromWindow. Uses TempSetScreenReader if w class name matches \"SAL*FRAME\".\n\tstatic HRESULT AccFromWindowSR(HWND w, DWORD objid, out IAccessible** a) {\n\t\tTempSetScreenReader tsr(w);\n\t\treturn AccessibleObjectFromWindow(w, objid, IID_IAccessible, (void**)a);\n\t}\n\n\t//Calls AccessibleObjectFromWindow. Uses TempSetScreenReader if screenReader is true (does not check classname).\n\t//Currently not used.\n\tstatic HRESULT AccFromWindow(HWND w, DWORD objid, out IAccessible** a, bool screenReader = false) {\n\t\tTempSetScreenReader tsr; if (screenReader) tsr.Set(w);\n\t\treturn AccessibleObjectFromWindow(w, objid, IID_IAccessible, (void**)a);\n\t}\n\n\tstatic bool IsLinkOrButton(int role) {\n\t\treturn IsIn(role, ROLE_SYSTEM_LINK, ROLE_SYSTEM_PUSHBUTTON, ROLE_SYSTEM_BUTTONMENU, ROLE_SYSTEM_BUTTONDROPDOWN, ROLE_SYSTEM_BUTTONDROPDOWNGRID, ROLE_SYSTEM_CHECKBUTTON, ROLE_SYSTEM_RADIOBUTTON);\n\t}\n} //namespace ao\n\n//IAccessible* and child element id.\n//Has only ctors. Does not have a dtor (does not Release etc), operator=, etc.\n//Like Cpp_Acc, but has methods and is internal.\nstruct AccRaw : public Cpp_Acc {\n\tAccRaw() noexcept : Cpp_Acc() {}\n\n\t//Does not AddRef.\n\tAccRaw(IAccessible* acc_, int elem_, eAccMiscFlags flags_ = {}) noexcept : Cpp_Acc(acc_, elem_, flags_) {}\n\n\t//Does not AddRef.\n\tAccRaw(ref const Cpp_Acc& x) noexcept : Cpp_Acc(x) {}\n\n\t//Calls Release (even if elem is not 0) and clears this variable.\n\tvoid Dispose() {\n\t\tif (acc != null) acc->Release();\n\t\tZero();\n\t}\n\n\tbool IsEmpty() const { return acc == null && elem == 0; }\n\n\tHRESULT get_accState(out long& state) const {\n\t\treturn ao::get_accState(out state, acc, elem);\n\t}\n\n\tHRESULT accLocation(out RECT& r) const {\n\t\treturn ao::accLocation(out r, acc, elem);\n\t}\n\n\t//Gets a string property and calls w.Match.\n\t//If fails to get, uses L\"\".\n\t//propName: name, value, desc, help, action, key, uiaid, uiacn. Compares only the first character, and the last of uiaX.\n\tbool MatchStringProp(STR propName, const str::Wildex& w) const {\n\t\tBstr b; STR s = L\"\"; auto lens = 0;\n\t\tao::VE ve(elem);\n\t\tHRESULT hr = 0;\n\t\tswitch (propName[0]) {\n\t\tcase 'n': hr = acc->get_accName(ve, &b); break;\n\t\tcase 'v': hr = acc->get_accValue(ve, &b); break;\n\t\tcase 'd': hr = acc->get_accDescription(ve, &b); break;\n\t\tcase 'h': hr = acc->get_accHelp(ve, &b); break;\n\t\tcase 'a': hr = acc->get_accDefaultAction(ve, &b); break;\n\t\tcase 'k': hr = acc->get_accKeyboardShortcut(ve, &b); break;\n\t\tcase 'u': //uiaid, uiacn\n\t\t\tve.vt = VT_I1;\n\t\t\tve.cVal = propName[4] == 'n' ? 'U' : 'u';\n\t\t\thr = acc->get_accHelp(ve, &b);\n\t\t\tbreak;\n\t\tdefault: assert(false);\n\t\t}\n\t\tif (hr == 0 && b.m_str) { s = b; lens = b.Length(); }\n\t\t//Printf(L\"0x%X %i %s\", hr, lens, s);\n\t\treturn w.Match(s, lens);\n\t}\n\n#if _DEBUG\n\tvoid PrintAcc() const {\n\t\tao::PrintAcc(acc, elem, misc.level);\n\t}\n#endif\n\n\t//Gets child IAccessible/elem from IAccessible (parent) and VARIANT (child id).\n\t//If VT_DISPATCH, calls IDispatchToIAccessible, which gets IAccessible and releases the IDispatch. Sets elem=0.\n\t//If VT_I4, sets non-zero elem and sets acc=parent. Does not AddRef.\n\t//Clears the VARIANT (releases IDispatch etc, sets vt=0).\n\t//If vt is not VT_DISPATCH/VT_I4 or value is 0, asserts and returns E_FAIL.\n\t//This must be empty (assert(IsEmpty());).\n\t//tryGetObjectFromId - If VT_I4, try to call get_accChild. If it succeeds, sets elem = 0.\n\tHRESULT FromVARIANT(IAccessible* parent, ref VARIANT& v, bool tryGetObjectFromId = false) {\n\t\tassert(IsEmpty());\n\t\tint hr = 0;\n\t\tswitch (v.vt) {\n\t\tcase VT_DISPATCH:\n\t\t\tassert(v.pdispVal != null);\n\t\t\thr = ao::IDispatchToIAccessible(v.pdispVal, out acc);\n\t\t\tbreak;\n\t\tcase VT_I4:\n\t\t\t//case VT_UI4: //.NET 5 RC1 bug\n\t\t\tassert(v.lVal != 0);\n\t\t\tif (v.lVal == 0) hr = E_FAIL;\n\t\t\telse if (tryGetObjectFromId && 0 == ao::get_accChild(parent, v.lVal, out acc));\n\t\t\telse { acc = parent; elem = v.lVal; }\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tVariantClear(&v);\n\t\t\thr = E_FAIL;\n\t\t\tbreak;\n\t\t}\n\t\tv.vt = 0;\n\t\tPRINTF_IF(hr != 0, \"0x%X\", hr);\n\t\treturn hr;\n\t}\n\n\t//Gets role (get_accRole) as BYTE.\n\t//Returns 0 if failed. Returns ROLE_CUSTOM (0xFF) if custom (VT_BSTR or not 1-ROLE_MAX).\n\tBYTE GetRoleByte() const {\n\t\treturn ao::GetRoleByte(acc, elem);\n\t}\n\n\t//Gets role (get_accRole) as BYTE and VARIANT.\n\t//Returns 0 if failed. Returns ROLE_CUSTOM (0xFF) if custom (VT_BSTR or not 1-ROLE_MAX).\n\tBYTE GetRoleByteAndVariant(out _variant_t& varRole) const {\n\t\tint roleInt;\n\t\tif (0 != ao::GetRoleIntAndVariant(acc, elem, out roleInt, out varRole)) return 0;\n\t\treturn roleInt > 0 && roleInt <= ROLE_MAX ? (BYTE)roleInt : ROLE_CUSTOM;\n\t}\n\n\t//Gets role (get_accRole) as raw int (VT_I4) or string (VT_BSTR).\n\t//If string, roleStr will be not null. Will need to free it.\n\tbool GetRoleIntOrString(out int& roleInt, out BSTR& roleStr) const {\n\t\troleStr = null;\n\t\t_variant_t v;\n\t\tif (0 != ao::GetRoleIntAndVariant(acc, elem, out roleInt, out v)) return false;\n\t\tif (v.vt == VT_BSTR) roleStr = v.Detach().bstrVal;\n\t\treturn true;\n\t}\n\n\t//Calls acc->accNavigate and gets IAccessible/elem from VARIANT using standard pattern which may involve get_accParent/get_accChild.\n\t//Does not support PARENT and CHILD (asserts). If NAVDIR_FIRSTCHILD or NAVDIR_LASTCHILD, elem must be 0 (asserts).\n\t//Does not set a.misc.flags.\n\tHRESULT Navigate(int navDir, out AccRaw& a) const {\n\t\tassert(!(navDir == NAVDIR_PARENT || navDir == NAVDIR_CHILD)); //our special navdirs\n\t\tassert(!(elem != 0 && (navDir == NAVDIR_FIRSTCHILD || navDir == NAVDIR_LASTCHILD)));\n\n\t\ta.acc = null; a.elem = 0;\n\t\t_variant_t v;\n\t\tint hr = acc->accNavigate(navDir, ao::VE(elem), out & v);\n\t\tif (hr == 0 && v.vt != 0) {\n\t\t\tif (elem == 0 && v.vt == VT_I4 && v.lVal != 0 && navDir >= NAVDIR_UP && navDir <= NAVDIR_PREVIOUS) {\n\t\t\t\tIAccessible* aParent;\n\t\t\t\thr = ao::get_accParent(acc, out aParent);\n\t\t\t\tif (hr == 0) {\n\t\t\t\t\thr = ao::get_accChild(aParent, v.lVal, out a.acc);\n\t\t\t\t\tif (hr == 0) aParent->Release();\n\t\t\t\t\telse { hr = 0; a.acc = aParent; a.elem = v.lVal; } //note: some AO return wrong childid. Then a will be invalid. Noticed only 1 such object. hr are various. Never mind.\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (v.vt == VT_I4 && v.lVal == 0) PRINTS(L\"AO bug: VT_I4 value 0\"); //eg .NET submenu-item\n\t\t\t\telse hr = a.FromVARIANT(acc, ref v, true);\n\t\t\t}\n\t\t}\n\t\tif (hr == 0 && a.acc == null) hr = S_FALSE;\n\t\treturn hr;\n\t}\n\nprivate:\n\t/// Gets focused object, which can be a direct child or this.\n\t/// If neither a child or this is focused, sets a.acc=0 and a.elem=0. Returns 1.\n\t/// Else if focused is this, sets a.acc=acc and a.elem=0. Does not AddRef.\n\t/// Else if focused is a simple element, sets a.acc=this and a.elem!=0. Does not AddRef.\n\t/// Else sets a.acc=child and a.elem=0. Will need to release a.acc.\n\tHRESULT _get_accFocus(out AccRaw& a) {\n\t\ta.Zero();\n\t\t_variant_t v;\n\t\tHRESULT hr = acc->get_accFocus(out & v);\n\t\tif (hr == 0) {\n\t\t\tif (v.vt == 0) hr = 1;\n\t\t\telse if (v.vt == VT_I4 && v.lVal == 0) a.acc = acc;\n\t\t\telse hr = a.FromVARIANT(acc, ref v, true);\n\t\t}\n\t\treturn hr;\n\t}\npublic:\n\n\t/// Gets focused descendant or this.\n\t/// Returns 1 if nothing is focused in this. Returns !0 if fails.\n\t/// If isThis receives true, does not AddRef. Else does AddRef even if ar.a==this.a.\n\t/// <param name=\"isThis\">Receives true if ar is this.</param>\n\tHRESULT DescendantFocused(out AccRaw& ar, out bool& isThis, bool directChild = false) {\n\t\tisThis = false;\n\t\tint hr = _get_accFocus(out ar);\n\t\tif (hr != 0) return hr;\n\t\tif (ar.acc == acc) {\n\t\t\tif (ar.elem == elem) {\n\t\t\t\tisThis = true;\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tar.acc->AddRef();\n\t\t} else if (!directChild) {\n\t\t\tAccRaw t; bool isThis2;\n\t\t\tif (0 == ar.DescendantFocused(out t, out isThis2) && !isThis2) {\n\t\t\t\tutil::Swap(ref ar, ref t);\n\t\t\t\tt.Dispose();\n\t\t\t}\n\t\t}\n\t\treturn 0;\n\t}\n\n};\n\n//The same as AccRaw, but has a dtor which releases the acc, but only if elem is 0. Also does not allow to copy.\nclass AccDtorIfElem0 : public AccRaw {\n\tAccDtorIfElem0(AccDtorIfElem0&&) = delete; //disable copying\npublic:\n\n\tAccDtorIfElem0() noexcept {}\n\n\t//Does not AddRef.\n\tAccDtorIfElem0(IAccessible* acc_, int elem_, eAccMiscFlags flags_ = {}) noexcept : AccRaw(acc_, elem_, flags_) {}\n\n\t//Does not AddRef.\n\tAccDtorIfElem0(ref const AccRaw& x) noexcept : AccRaw(x) {}\n\n\t//Does not AddRef.\n\tAccDtorIfElem0(ref const Cpp_Acc& x) noexcept : AccRaw(x) {}\n\n\t//Calls Release if elem is 0. Else acc is considered not owned by this.\n\t~AccDtorIfElem0() {\n\t\tif (acc != null && elem == 0) {\n\t\t\t//Perf.First();\n\t\t\tacc->Release();\n\t\t\t//Perf.NW('~');\n\t\t}\n\t}\n};\n\n//Provides a memory buffer for AccessibleChildren that can be reused by multiple AccChildren instances.\nclass AccContext {\npublic:\n\tVARIANT* buffer;\n\tint lenBuffer, maxcc;\n\n\texplicit AccContext(int maxcc_ = 10000) noexcept {\n\t\tbuffer = null;\n\t\tlenBuffer = 0;\n\t\tmaxcc = maxcc_;\n\t}\n\n\tbool Init() {\n\t\tif (buffer == null) {\n\t\t\tlenBuffer = min(maxcc, 1000000) + 1;\n\t\t\tdo {\n\t\t\t\tbuffer = (VARIANT*)VirtualAlloc(null, lenBuffer * sizeof(VARIANT), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);\n\t\t\t} while (buffer == null && (lenBuffer /= 2) > 5000);\n\t\t\tif (buffer == null) {\n\t\t\t\tmaxcc = lenBuffer = 0;\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tmaxcc = lenBuffer - 1;\n\t\t}\n\t\treturn true;\n\t}\n\n\t~AccContext() {\n\t\tif (buffer != null) {\n\t\t\tVirtualFree(buffer, 0, MEM_RELEASE);\n\t\t\tbuffer = null;\n\t\t}\n\t}\n};\n\n//Gets child AOs.\nclass AccChildren {\n\tIAccessible* _parent;\n\tVARIANT* _v;\n\tint _count, _i, _startAtIndex;\n\tbool _exactIndex, _reverse;\n\teAccMiscFlags _miscFlags;\n\npublic:\n\tAccChildren(AccContext& context, const Cpp_Acc& parent, int startAtIndex = 0, bool exactIndex = false, bool reverse = false) {\n\t\t_parent = parent.acc;\n\t\t_miscFlags = parent.misc.flags & eAccMiscFlags::InheritMask;\n\t\t_v = null;\n\t\t_count = -1;\n\t\t_i = 0;\n\t\t_exactIndex = exactIndex;\n\t\t_reverse = reverse;\n\t\t_startAtIndex = startAtIndex;\n\n\t\t//With get_accChildCount was faster in most tested cases, usually 10-20%, sometimes 50%, sometimes same speed, Chrome 30%, WPF 50%, never slower.\n\t\t//\tNote: get_accChildCount can return different count than AccessibleChildren. Usually more. With this code bad is only when incorrectly returns 0 or >maxcc.\n\t\t//\tNever mind: with VS 2022 Preview get_accChildCount occasionally hangs when parent is PAGETABLIST of document. OK with only AccessibleChildren.\n\n\t\t//For AccessibleChildren we use buffer of maxcc+1 size.\n\t\t//\tThe buffer is in *context*. Reused by all AccChildren instances of that main function (find, navigate, etc).\n\t\t//\tMax possible maxcc is 1000000 (24 MB in 64-bit process, 16 MB in 32-bit). If fails to allocate, sets smaller maxcc.\n\n\t\t//Perf.First();\n\t\tlong n = 0;\n#if true\n\t\tif (0 == _parent->get_accChildCount(&n) && n > 0 && n <= context.maxcc && context.Init()) {\n\t\t\tn = 0;\n\t\t\tint hr = AccessibleChildren(_parent, 0, context.lenBuffer, context.buffer, &n);\n\t\t\tif (hr < 0) { //rare\n\t\t\t\tn = 0;\n\t\t\t\t//PRINTHEX(hr);\n\t\t\t\t//ao::PrintAcc(_parent);\n\t\t\t} else if (n > 0) {\n\t\t\t\t//Printf(L\"A %i\", n);\n\t\t\t\tif (!(parent.misc.flags & (eAccMiscFlags::UIA | eAccMiscFlags::Java))) {\n\t\t\t\t\tn = _RemoveInvisibleNonclient(context.buffer, n, parent.misc.roleByte);\n\t\t\t\t}\n\n\t\t\t\tint memSize = n * sizeof(VARIANT);\n\t\t\t\t_v = (VARIANT*)malloc(memSize);\n\t\t\t\tif (_v != null) memcpy(_v, context.buffer, memSize);\n\t\t\t\telse while (n > 0) VariantClear(&context.buffer[--n]);\n\t\t\t}\n\t\t} else n = 0;\n#else //bad: Slow anyway when there are >maxcc children. And slower in most cases.\n\t\tif (context.Init()) { //else failed to allocate memory, unlikely\n\t\t\tint hr = AccessibleChildren(_parent, 0, context.lenBuffer, context.buffer, &n);\n\t\t\tif (hr < 0) { //rare\n\t\t\t\tn = 0;\n\t\t\t\t//PRINTHEX(hr);\n\t\t\t\t//ao::PrintAcc(_parent);\n\t\t\t} else if (n > 0) {\n\t\t\t\tif (n <= context.maxcc) { //maxcc default 10000, max 1000000\n\t\t\t\t\t//Printf(L\"A %i\", n);\n\t\t\t\t\tif (!(parent.misc.flags & (eAccMiscFlags::UIA | eAccMiscFlags::Java))) {\n\t\t\t\t\t\tn = _RemoveInvisibleNonclient(context.buffer, n, parent.misc.roleByte);\n\t\t\t\t\t}\n\n\t\t\t\t\tint memSize = n * sizeof(VARIANT);\n\t\t\t\t\t_v = (VARIANT*)malloc(memSize);\n\t\t\t\t\tif (_v != null) memcpy(_v, context.buffer, memSize);\n\t\t\t\t} else {\n\t\t\t\t\t//Print(n);\n\t\t\t\t\tn = min(n, context.lenBuffer);\n\t\t\t\t}\n\n\t\t\t\tif (_v == null) while (n > 0) VariantClear(&context.buffer[--n]);\n\t\t\t}\n\t\t}\n#endif\n\t\t//Perf.NW();\n\n\t\t_count = n;\n\t\tif (n > 0 && _startAtIndex != 0) {\n\t\t\tif (_startAtIndex < 0) _startAtIndex = n + _startAtIndex; else _startAtIndex--; //if < 0, it is index from end\n\t\t\tint i = _startAtIndex; if (i < 0) i = 0; else if (i >= n) i = n - 1;\n\t\t\tif (_exactIndex && i != _startAtIndex) _startAtIndex = -1; else _startAtIndex = i;\n\t\t} else _startAtIndex = -1; //not used\n\n\t\t//speed: AccessibleChildren same as IEnumVARIANT with array. IEnumVARIANT.Next(1, ...) much slower (if out-proc).\n\n\t\t//50% AO have 0 children. 20% have 1 child. Few have > 7.\n\t}\n\n\t~AccChildren() {\n\t\tif (_v != null) {\n\t\t\twhile (_count > 0) VariantClear(&_v[--_count]); //info: it's OK to clear variants for which FromVARIANT was called because then vt is 0\n\t\t\tfree(_v); _v = null;\n\t\t}\n\t}\n\n\tint Count() { return _count; }\n\n\tbool GetNext(out AccRaw& a) {\n\t\tassert(a.IsEmpty());\n\t\tif (_count == 0) return false;\n\t\tif (_exactIndex) {\n\t\t\tint i = _startAtIndex; _startAtIndex = -1;\n\t\t\tif (i < 0 || 0 != a.FromVARIANT(_parent, _v[i])) return false;\n\t\t} else {\n\t\tg1:\n\t\t\tif (_startAtIndex < 0) { //_startAtIndex is -1 if not used\n\t\t\t\tif (_i >= _count) return false;\n\t\t\t\tint i = _i++; if (_reverse) i = _count - i - 1;\n\t\t\t\tif (0 != a.FromVARIANT(_parent, _v[i])) goto g1;\n\t\t\t} else { //_startAtIndex is in _count range\n\t\t\t\tint i = _startAtIndex + _i;\n\t\t\t\tif (i < 0 || i >= _count) return false; //no more\n\t\t\t\t//calculate next i\n\t\t\t\tif (_i >= 0) {\n\t\t\t\t\t_i = -(_i + 1);\n\t\t\t\t\tif (_startAtIndex + _i < 0) _i = -_i;\n\t\t\t\t} else {\n\t\t\t\t\t_i = -_i;\n\t\t\t\t\tif (_startAtIndex + _i >= _count) _i = -(_i + 1);\n\t\t\t\t}\n\t\t\t\tif (0 != a.FromVARIANT(_parent, _v[i])) goto g1;\n\t\t\t}\n\t\t}\n\t\ta.misc.flags = _miscFlags;\n\t\treturn true;\n\t}\n\nprivate:\n\t//Removes invisible nonclient children of WINDOW. They are annoying and make slower.\n\tint _RemoveInvisibleNonclient(VARIANT* v, int n, int role) {\n\t\tif (n == 7 && (role == ROLE_SYSTEM_WINDOW || role == 0)) {\n\t\t\tfor (int i = 0; i < 7; i++) if (v[i].vt != VT_DISPATCH) goto gr;\n\t\t\t//Perf.First();\n\t\t\tif (role == 0 && ao::GetRoleByte(_parent) != ROLE_SYSTEM_WINDOW) goto gr;\n\t\t\tHWND w; if (0 != WindowFromAccessibleObject(_parent, &w)) goto gr;\n\n\t\t\t//is it native WINDOW AO? Eg WPF uses role WINDOW instead of CLIENT.\n\t\t\t//Perf.Next();\n\t\t\t{\n\t\t\t\t//this way is fast, even if not inproc. Tried 2 other ways, both slow.\n\t\t\t\t//\tBad: the identity string is undocumented, and \"clients should not attempt to dissect it or otherwise interpret it manually\".\n\t\t\t\tSmart<IAccIdentity> aid; DWORD* k = nullptr; DWORD len = 0;\n\t\t\t\tif (0 != _parent->QueryInterface(&aid) || 0 != aid->GetIdentityString(0, (BYTE**)&k, &len)) goto gr;\n\t\t\t\t//Printf(L\"0x%X 0x%X 0x%X 0x%X  w=0x%X\", k[0], k[1], k[2], k[3], (int)w);\n\t\t\t\tbool ok = len == 16 && k[0] == 0x80000001 && k[1] == (DWORD)(LPARAM)w && k[2] == 0 && k[3] == 0; //0x80000001, hwnd, objid (OBJID_WINDOW is 0), childid\n\t\t\t\tCoTaskMemFree(k);\n\t\t\t\tif (!ok) goto gr;\n\t\t\t}\n\t\t\t//Perf.Next();\n\n\t\t\tDWORD bits = 1 << 3; //CLIENT\n\n\t\t\tDWORD style = wn::Style(w);\n\t\t\tif (style & WS_VSCROLL) bits |= 1 << 4;\n\t\t\tif (style & WS_HSCROLL) bits |= 1 << 5;\n\t\t\tif ((style & (WS_VSCROLL | WS_HSCROLL)) == (WS_VSCROLL | WS_HSCROLL)) bits |= 1 << 6; //GRIP\n\n\t\t\tif (style & WS_CHILD) {\n\t\t\t\t//note: child windows cannot have app MENUBAR.\n\t\t\t\t//note: assume system MENUBAR always visible if TITLEBAR visible; it depends on WS_SYSMENU + on don't know what.\n\t\t\t\tif ((style & WS_CAPTION) == WS_CAPTION) bits |= 3; //system MENUBAR, TITLEBAR\n\t\t\t} else {\n\t\t\t\t//With top-level windows don't use style/GetWindowLongPtrW(w, GWL_ID)/GetMenu. Can be custom MENUBAR etc. Here the speed is not so important.\n\t\t\t\tfor (int i = 0; i < 3; i++) {\n\t\t\t\t\tSmart<IAccessible> iacc; long state;\n\t\t\t\t\tif (0 != v[i].pdispVal->QueryInterface(&iacc)) continue;\n\t\t\t\t\tif (0 != ao::get_accState(out state, iacc) || !(state & STATE_SYSTEM_INVISIBLE)) bits |= 1 << i;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t//wn::PrintWnd(w); Print((uint)bits);\n\n\t\t\tint iMoveTo = 0;\n\t\t\tfor (int i = 0; i < 7; i++) {\n\t\t\t\tif (bits & (1 << i)) {\n\t\t\t\t\tif (i != iMoveTo) memmove(v + iMoveTo, v + i, sizeof(VARIANT));\n\t\t\t\t\tiMoveTo++;\n\t\t\t\t} else {\n\t\t\t\t\tVariantClear(v + i);\n\t\t\t\t\tn--;\n\t\t\t\t}\n\t\t\t}\n\t\t\t//Perf.NW();\n\t\t}\n\tgr:\treturn n;\n\n\t\t//never mind: does not remove invisible children of TITLEBAR, SCROLLBAR etc. It could be a custom TITLEBAR etc. Not so important.\n\t}\n};\n\nIAccessible* AccJavaFromWindow(HWND w, bool getFocused = false);\nIAccessible* AccJavaFromPoint(POINT p, HWND w);\nHRESULT AccUiaFromWindow(HWND w, out IAccessible** iacc);\nHRESULT AccUiaFromPoint(POINT p, out IAccessible** iacc);\nHRESULT AccUiaFocused(out IAccessible** iacc);\nIUIAutomation* UIA();\n//HRESULT AccUiaFromMSAA(IAccessible* msaa, int elem, out IAccessible** iacc);\n"
  },
  {
    "path": "Cpp/in-proc.cpp",
    "content": "//This file contains DllMain and code that injects this dll into other processes and calls functions there.\n\n//Dll injection is used for finding accessible objects (AO). Much faster when runs in the target thread.\n//For example, can find an AO in Chrome web page about 60 times faster.\n//It seems UI Automation also searches inproc, but somehow it manages to find the same object only 2-3 times faster (instead of 60) than outproc MSAA, and in some cases slower. Also its loading is very slow.\n//\tTested: UI Automation is slightly faster when we call it inproc. Depends on window. Can be eg 2 times faster, or same speed.\n\n//The main entry functions to find/get AO are Cpp_AccFind and Cpp_AccFromWindow (in \"acc bridge.cpp\").\n//They call InjectDllAndGetAgent, which injects this dll into the target process (if not already done). Then they call the real 'find AO' etc function in the target thread.\n\n//To inject dll, temporarily sets a WH_CALLWNDPROC hook and sends a message to the target window.\n//When injected, creates a message-only window (\"agent\"), in each target thread. It is used for several purposes:\n//\tTo detect whether the dll is injected into the target process and is ready to call functions in the target thread.\n//\tTo get a proxy COM object that is used to call functions in that process/thread.\n//\tTo unload this dll from processes when need (dll development, setup).\n\n//To call functions in the target process/thread, we hook and call IAccessible::get_accHelpTopic.\n//It is probably the easiest way.\n//\tCOM automatically marshals parameters and the returned data (as BSTR).\n//\tIt does not marshal IAccessible objects. For it we use CoMarshalInterface/CoUnmarshalInterface.\n//\t\tWe don't use LresultFromObject/ObjectFromLresult because it is slower and unreliable (fails when returning multiple objects).\n//Another possible way - send message. I tested and rejected it. Slightly faster, but has 2 problems:\n//\t1. Quite big code, need PostMessage (not SendMessage), shared memory, event, 2-3 mutexes, etc, and therefore can be less reliable. Better let COM do all it.\n//\t2. It can be used to find AO in window. But to find AO in AO would need the hook anyway (I could not find another way, or it would be too complicated).\n//\tAlso, I expected to make 'find all' much faster, because then can search and send/unmarshal results at the same time on different CPU cores. But it made faster only by 15%. Also, calling the final callback function before finishing searching is not a good idea, because then the finder must DoEvents because the callback would probably call object's methods.\n\n//Cannot inject dll into some processes, including:\n//\tWindows Store apps;\n//\tConsole (not useful anyway);\n//\tProtected processes (eg antivirus);\n//\tProcesses of higher UAC integrity level (unless this process is uiAccess);\n//Tested: works with processes running as another user, if UAC allows it.\n\n//When 'find all', the slowest part is releasing all found AO (if many), because calls the server process for each AO.\n//\tPartial solution: when used in C#, let GC release later in other thread.\n//\tHowever when using 'also' callback, and it says 'stop', remaining objects are released synchronously, which makes much slower.\n\n//For more details, read the code below and code in \"acc bridge.cpp\".\n\n#include \"stdafx.h\"\n#include \"cpp.h\"\n\n//#ifdef _DEBUG\n//void InProcAccTest(IAccessible* a);\n//#endif\n\nHMODULE s_moduleHandle; //module handle of this dll\n\nnamespace {\n\tconst STR c_agentWindowClassName = L\"AuCpp_IPA_1\"; //in-proc agent window class name\n\tconst int c_agentWndExtra = 200; //size of agent window's extra memory, which contains its AO marshal data\n}\n\n//Namespace inproc contains code used only in the server process.\n//Namespace outproc contains code used only in the client process.\nnamespace inproc {\n\tlong s_nAgentThreads; //how many threads in this process have our agent window\n\tATOM s_agentWindowClassAtom;\n\tthread_local HWND t_agentWnd;\n}\nnamespace outproc {\n#ifdef AGENTCACHE\n\tvoid HwndTidCache_OnThreadDetach();\n#endif\n}\n\nnamespace str::pcre {\n\tvoid thread_detach();\n}\n\nBOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {\n\tswitch (ul_reason_for_call) {\n\tcase DLL_PROCESS_ATTACH:\n//\t{\n//#if _M_ARM64EC //currently not used\n//\t\tSTR arch = L\"ARM64EC\";\n//#elif _M_ARM64\n//\t\tSTR arch = L\"ARM64\";\n//#elif _M_X64\n//\t\tSTR arch = L\"x64\";\n//#else\n//\t\tSTR arch = L\"x86\";\n//#endif\n//\t\twchar_t b[500];\n//\t\tGetModuleFileNameW(hModule, b, 500);\n//\t\tPrintf(L\"P+  %i %s    tid=%i   arch=%s  dll=%s\", GetCurrentProcessId(), GetCommandLineW(), GetCurrentThreadId(), arch, b);\n//\t}\n\ts_moduleHandle = hModule;\n\tbreak;\n\t//case DLL_PROCESS_DETACH:\n\t//\tPrintf(L\"P-  %i %s    tid=%i\", GetCurrentProcessId(), GetCommandLineW(), GetCurrentThreadId());\n\t//\tbreak;\n\t//case DLL_THREAD_ATTACH:\n\t//\tPrintf(L\"T+  %i %i\", GetCurrentProcessId(), GetCurrentThreadId());\n\t//\tbreak;\n\tcase DLL_THREAD_DETACH:\n\t\t//Printf(L\"T-  %i %i\", GetCurrentProcessId(), GetCurrentThreadId());\n\t\tHWND wAgent = inproc::t_agentWnd;\n\t\tif (wAgent) DestroyWindow(wAgent);\n#ifdef AGENTCACHE\n\t\toutproc::HwndTidCache_OnThreadDetach();\n#endif\n\t\tstr::pcre::thread_detach();\n\t\tbreak;\n\t}\n\treturn TRUE;\n}\n\nHRESULT AccGetProps(Cpp_Acc a, STR props, out BSTR& sResult);\nHRESULT AccGetProp(Cpp_Acc a, WCHAR prop, out BSTR& sResult);\nHRESULT AccWeb(IAccessible* iacc, STR what, out BSTR& sResult);\nHRESULT AccEnableChrome2(HWND w, int i, HWND c);\n\nnamespace inproc {\n\tHRESULT AccFindOrGet(MarshalParams_Header* h, IAccessible* iacc, out BSTR& sResult);\n\tHRESULT ShellExec(MarshalParams_Header* h, out BSTR& sResult);\n\n\t//Our hook of get_accHelpTopic.\n\tHRESULT STDMETHODCALLTYPE Hook_get_accHelpTopic(IAccessible* iacc, out BSTR& sResult, VARIANT vParams, long* pMagic) {\n\t\tif (vParams.vt == VT_BSTR) {\n\t\t\t//try {\n\t\t\tauto size = SysStringByteLen(vParams.bstrVal);\n\t\t\tif (size >= sizeof(MarshalParams_Header)) {\n\t\t\t\t*pMagic = c_magic;\n\t\t\t\tauto h = (MarshalParams_Header*)vParams.bstrVal;\n\t\t\t\tauto p = (MarshalParams_AccElem*)h;\n\t\t\t\tif (h->magic == c_magic) {\n\t\t\t\t\tsResult = null;\n\t\t\t\t\tHRESULT hr = 0;\n\t\t\t\t\tswitch (h->action) {\n\t\t\t\t\t\t//#ifdef _DEBUG\n\t\t\t\t\t\t//\t\t\t\tcase InProcAction::IPA_AccTest:\n\t\t\t\t\t\t//\t\t\t\t\tInProcAccTest(iacc);\n\t\t\t\t\t\t//\t\t\t\t\tbreak;\n\t\t\t\t\t\t//#endif\n\t\t\t\t\tcase InProcAction::IPA_AccFind:\n\t\t\t\t\tcase InProcAction::IPA_AccFromWindow:\n\t\t\t\t\tcase InProcAction::IPA_AccFromPoint:\n\t\t\t\t\tcase InProcAction::IPA_AccFocused:\n\t\t\t\t\tcase InProcAction::IPA_AccNavigate:\n\t\t\t\t\t\thr = AccFindOrGet(h, iacc, sResult);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase InProcAction::IPA_AccGetProps:\n\t\t\t\t\t\thr = AccGetProps(Cpp_Acc(iacc, p->elem, h->miscFlags), (STR)(p + 1), out sResult);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase InProcAction::IPA_AccGetWindow:\n\t\t\t\t\t\thr = AccGetProp(Cpp_Acc(iacc, 0, h->miscFlags), 'w', out sResult);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase InProcAction::IPA_AccGetHtml:\n\t\t\t\t\t\thr = AccWeb(iacc, (STR)(h + 1), sResult);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase InProcAction::IPA_AccEnableChrome:\n\t\t\t\t\t\thr = AccEnableChrome2((HWND)(LPARAM)((MarshalParams_AccInt4*)h)->i0, ((MarshalParams_AccInt4*)h)->i1, (HWND)(LPARAM)((MarshalParams_AccInt4*)h)->i2);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase InProcAction::IPA_ShellExec:\n\t\t\t\t\t\thr = ShellExec(h, out sResult);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\treturn hr;\n\t\t\t\t}\n\t\t\t}\n\t\t\t//} catch(...) { PRINTS(L\"exception\"); }\n\t\t\t//don't need try/catch. COM catches SEH and C++ exceptions and returns hresult \"The server threw an exception\".\n\t\t\t//\tAlso don't need /EHa. We don't throw and don't expect any exceptions.\n\t\t}\n\t\treturn E_NOTIMPL;\n\t\t//never mind: we don't call the old method. Nobody implement or use it. MSDN: \"is deprecated and should not be used\".\n\t}\n} //namespace inproc\n\n#pragma region marshal unmarshal agent IAccessible\n\nnamespace inproc {\n\tHookIAccessible s_hookIAcc;\n\n\t//Gets wAgent AO, hooks its IAccessible interface, calls CoMarshalInterface, writes the data to the window extra memory (SetWindowLong).\n\tIStream* MarshalAgentIAccessible(HWND wAgent) {\n\t\t//Perf.First();\n\t\tSmart<IAccessible> iacc;\n\t\tif (AccessibleObjectFromWindow(wAgent, OBJID_WINDOW, IID_IAccessible, (void**)&iacc)) return null;\n\t\tif (!s_hookIAcc.Hook(iacc)) return null;\n\n\t\tSmart<IStream> stream;\n\t\tCreateStreamOnHGlobal(0, true, &stream);\n\t\tif (CoMarshalInterface(stream, IID_IAccessible, iacc, MSHCTX_LOCAL, null, MSHLFLAGS_TABLESTRONG)) return null;\n\n\t\tDWORD streamSize, readSize;\n\t\tif (!istream::GetPos(stream, out streamSize)) return null;\n\t\t//Print((int)streamSize); //68\n\t\tassert(streamSize <= c_agentWndExtra / 2); //c_agentWndExtra=200\n\t\tif (streamSize > c_agentWndExtra - 4) return null;\n\n\t\tlong b[c_agentWndExtra / 4];\n\t\tif (!istream::ResetPos(stream) || stream->Read(b, streamSize, &readSize) || readSize != streamSize || !istream::ResetPos(stream)) return null;\n\n\t\tSetWindowLongW(wAgent, 0, streamSize);\n\t\tfor (DWORD i = 0; i < streamSize; i += 4) SetWindowLongW(wAgent, i + 4, b[i / 4]);\n\n\t\treturn stream.Detach();\n\t\t//Perf.NW(); //190\n\t}\n}\n\nnamespace outproc {\n\t//Reads wAgent AO marshal data from the window extra memory (GetWindowLongPtrW), calls CoUnmarshalInterface.\n\tbool UnmarshalAgentIAccessible(HWND wAgent, out IAccessible*& iacc) {\n\t\tiacc = null;\n\n\t\tDWORD streamSize = (DWORD)GetWindowLongPtrW(wAgent, 0); if (streamSize == 0) return false;\n\t\tlong b[c_agentWndExtra / 4];\n\t\tfor (DWORD i = 0; i < streamSize; i += 4) b[i / 4] = (long)GetWindowLongPtrW(wAgent, i + 4);\n\n\t\tHGLOBAL hg = GlobalAlloc(GMEM_MOVEABLE, streamSize); if (hg == 0) return false;\n\t\tLPVOID mem = GlobalLock(hg); memcpy(mem, b, streamSize); GlobalUnlock(mem);\n\n\t\tSmart<IStream> stream;\n\t\tCreateStreamOnHGlobal(hg, true, &stream);\n\t\tif (CoUnmarshalInterface(stream, IID_IAccessible, (void**)&iacc)) return false;\n\t\t//TODO3: once run.it in TT process started to always print warning \"Failed to run as non-admin.\".\n\t\t//\tAttached debugger shows that CoUnmarshalInterface fails.\n\t\t//\tOK in other processes. OK after restarting TT. Can't reproduce.\n\n\t\treturn true;\n\t}\n}\n#pragma endregion\n\nnamespace uia { bool UiaDisconnectWrappers(); }\n\nnamespace inproc {\n\t//Thread proc that unloads this dll.\n\t//Waits while all agent windows closed, unregisters agent window class, calls FreeLibraryAndExitThread.\n\tDWORD WINAPI UnloadDllThreadProc(LPVOID lpParameter) {\n\t\tSleep(15);\n\t\t//MSDN: \"No window classes registered by a DLL are unregistered when the DLL is unloaded. A DLL must explicitly unregister its classes when it is unloaded.\"\n\t\twhile (s_agentWindowClassAtom && !UnregisterClassW((STR)s_agentWindowClassAtom, s_moduleHandle) && GetLastError() != ERROR_CLASS_DOES_NOT_EXIST) {\n\t\t\tPrintf(L\"UnregisterClass failed. Error 0x%X\", GetLastError());\n\t\t\tSleep(50);\n\t\t}\n\t\ts_agentWindowClassAtom = 0;\n\t\tSleep(100);\n\t\tif (!AccDisconnectWrappers() | !uia::UiaDisconnectWrappers()) return 0;\n\n\t\tFreeLibraryAndExitThread(s_moduleHandle, 0);\n\t\treturn 0;\n\t}\n\n\t//Agent window procedure.\n\tLRESULT WINAPI AgentWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {\n\t\t//agent window's AO marshal data, to pass to CoReleaseMarshalData on WM_NCDESTROY, because marshaled with MSHLFLAGS_TABLESTRONG\n\t\tstatic thread_local IStream* t_agentStream;\n\n\t\tswitch (msg) {\n\t\tcase WM_CREATE:\n\t\t{\n\t\t\tif (!(t_agentStream = MarshalAgentIAccessible(hwnd))) return -1; //-1 destroys the window\n\t\t} break;\n\t\t} //else Print(msg);\n\n\t\tauto R = DefWindowProcW(hwnd, msg, wParam, lParam);\n\n\t\tif (msg == WM_NCDESTROY) {\n\t\t\t//this normally happens in two cases:\n\t\t\t//\t1. This thread is ending. Then our DllMain calls DestroyWindow.\n\t\t\t//\t2. Called Cpp_Unload. It closes all agent windows.\n\n\t\t\tif (t_agentStream) {\n\t\t\t\t//release marshal data, because marshaled with MSHLFLAGS_TABLESTRONG\n\t\t\t\tHRESULT hr = CoReleaseMarshalData(t_agentStream); if (hr) PRINTHEX(hr);\n\t\t\t\tt_agentStream->Release(); t_agentStream = null;\n\t\t\t}\n\t\t\tt_agentWnd = 0;\n\n\t\t\tif (0 == InterlockedDecrement(&s_nAgentThreads)) {\n\t\t\t\t//unload dll\n\t\t\t\tCloseHandle(CreateThread(null, 64 * 1024, UnloadDllThreadProc, null, 0, null));\n\t\t\t}\n\t\t}\n\n\t\treturn R;\n\t}\n\n\t//Creates agent window.\n\t//First time in process registers window class and increments dll refcount.\n\tHWND CreateAgentWindow() {\n\t\tif (!t_agentWnd) {\n\t\t\tif (1 == InterlockedIncrement(&s_nAgentThreads)) {\n\t\t\t\t//increment dll refcount to prevent unloading this dll when the caller process unhooks this hook\n\t\t\t\tHMODULE hm = 0;\n\t\t\t\tGetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (STR)s_moduleHandle, &hm);\n\n\t\t\t\tWNDCLASS c = { };\n\t\t\t\tc.hInstance = s_moduleHandle;\n\t\t\t\tc.lpfnWndProc = AgentWndProc;\n\t\t\t\tc.lpszClassName = c_agentWindowClassName;\n\t\t\t\tc.cbWndExtra = c_agentWndExtra; //for IAccessible marshaling data\n\t\t\t\ts_agentWindowClassAtom = RegisterClassW(&c);\n\t\t\t\tif (!s_agentWindowClassAtom) PRINTF(L\"failed to register class. Error 0x%X\", GetLastError());\n\t\t\t}\n\n\t\t\twchar_t name[12]; _itow(GetCurrentThreadId(), name, 10);\n\t\t\tt_agentWnd = CreateWindowExW(WS_EX_NOACTIVATE, (STR)s_agentWindowClassAtom, name, WS_POPUP, 0, 0, 0, 0, HWND_MESSAGE, 0, 0, 0);\n\t\t}\n\t\t//Printf(L\"s_nAgentThreads=%i t_agentWnd=%i s_agentWindowClassAtom=%i\", s_nAgentThreads, (int)t_agentWnd, s_agentWindowClassAtom);\n\t\treturn t_agentWnd;\n\t}\n\n\t//SetWindowsHookEx(WH_CALLWNDPROC) hook procedure.\n\t//If message==WM_NULL and wParam==c_magic, creates agent window and returns its handle.\n\tLRESULT WINAPI HookCallWndProc(int nCode, WPARAM wParam, LPARAM lParam) {\n\t\tCWPSTRUCT& m = *(CWPSTRUCT*)lParam;\n\n\t\tif (m.message == WM_NULL && m.wParam == c_magic) {\n\t\t\tHWND wAgent = CreateAgentWindow();\n\t\t\tReplyMessage((LRESULT)wAgent);\n\t\t\treturn 1;\n\t\t\t//tested: not faster if we unhook before creating window\n\t\t} //else Print(m.message);\n\n\t\treturn CallNextHookEx(0, nCode, wParam, lParam);\n\t}\n} //namespace inproc\n\nnamespace outproc {\n\t//Injects this dll into the target process/thread.\n\t//Uses the SetWindowsHookEx/SendMessage method.\n\t//w - a window in the target thread.\n\t//tid, pid - target thread/process id. This func could call GetWindowThreadProcessId, but the caller already did it.\n\t//Returns: agent window.\n\tHWND InjectDll(HWND w, DWORD tid, DWORD pid) {\n\t\tauto hh = SetWindowsHookExW(WH_CALLWNDPROC, inproc::HookCallWndProc, s_moduleHandle, tid);\n\t\tif (hh == 0) return 0;\n\t\tHWND wAgent = (HWND)SendMessageW(w, 0, c_magic, 0);\n\t\tUnhookWindowsHookEx(hh);\n\t\treturn wAgent;\n\n\t\t//problem: does not work with windows of class \"Windows.UI.Core.CoreWindow\", ie Windows Store app processes.\n\t\t//\tSetWindowsHookEx succeeds, but the hook proc is never called, and the dll is not injected.\n\t\t//\tBut SendMessage works, eg WM_CLOSE closes the app.\n\t\t//\tThe CreateRemoteThread(LoadLibrary) method fails too. LoadLibrary returns 0.\n\t\t//\tFound some info on the internet. The dll must be signed etc. Or it can be in system directory, and with LoadLibrary use just file name.\n\t\t//\tAlso there are other protected processes, for example some system processes, antivirus, protected processes of web browsers.\n\n\t\t//Also does not work with console windows. Not a problem, because there are no useful AO.\n\t\t//\tSetWindowsHookEx sets error 0x57, \"The parameter is incorrect\".\n\t\t//\tThe console window actually belongs to the conhost process, and GetWindowThreadProcessId lies.\n\t}\n\n#pragma region transition 64/32 bit or 64/arm64\n\n\t//Called by Au.DllHost.exe.\n\t//Calls InjectDll.\n\tEXPORT void Cpp_Arch(STR a0, STR a1) {\n\t\tHWND w = (HWND)(LPARAM)strtoi(a0);\n\t\tHANDLE ev = (HANDLE)(LPARAM)strtoi(a1);\n\t\tDWORD pid, tid = GetWindowThreadProcessId(w, &pid); if (tid == 0) return;\n\t\tHWND wa = InjectDll(w, tid, pid);\n\t\tif (wa) SetEvent(ev);\n\t}\n\n\t//Finds and executes Au.DllHost.exe for arch architecture (1 32-bit, 2 x64, 3 ARM64), which calls InjectDll.\n\tbool SwitchArchAndInjectDll(HWND w, int arch) {\n\t\tstr::StringBuilder b;\n\t\tb << L\"\\\"\";\n\t\tauto m = b.GetBufferToAppend();\n\t\tb.FixBuffer(GetModuleFileNameW(s_moduleHandle, m.p, m.n) - 10); //eg \"program\\64\"\n\t\tint len = b.Length();\n\t\tLPWSTR s = b, end = s + len;\n\n#if _M_ARM64 //this dll is ARM64 on ARM64 OS. Find dir of x86 (arch 1) or x64 (arch 2).\n\t\tif (b.EndsI(LR\"(\\64\\ARM)\")) {\n\t\t\tb.ReplaceTail(6, arch == 1 ? L\"32\" : L\"64\");\n\t\t} else if (b.EndsI(LR\"(\\runtimes\\win-arm64\\native)\")) { //using Au nuget\n\t\t\tb.ReplaceTail(12, arch == 1 ? L\"x86\\\\native\" : L\"x64\\\\native\");\n\t\t} else { //probably in app dir, eg in temp dir when published single-file\n\t\t\tb << (arch == 1 ? L\"\\\\32\" : L\"\\\\64\");\n\t\t}\n#elif _WIN64 //this dll is x64 on any OS. Find dir of x86 (arch 1) or ARM64 (arch 3).\n\t\tif (s[len - 3] == '\\\\' && s[len - 2] == '6' && s[len - 1] == '4') {\n\t\t\tb.ReplaceTail(2, arch == 1 ? L\"32\" : L\"64\\\\ARM\");\n\t\t} else if (b.EndsI(LR\"(\\runtimes\\win-x64\\native)\")) { //using Au nuget\n\t\t\tb.ReplaceTail(10, arch == 1 ? L\"x86\\\\native\" : L\"arm64\\\\native\");\n\t\t} else { //probably in app dir, eg in temp dir when published single-file\n\t\t\tb << (arch == 1 ? L\"\\\\32\" : L\"\\\\64\\\\ARM\");\n\t\t}\n#else //this dll is x86 on any OS. Find dir of x64 (arch 2) or ARM64 (arch 3).\n\t\tif (s[len - 3] == '\\\\' && s[len - 2] == '3' && s[len - 1] == '2') {\n\t\t\tb.ReplaceTail(2, arch == 2 ? L\"64\" : L\"64\\\\ARM\");\n\t\t} else if (b.EndsI(LR\"(\\runtimes\\win-x86\\native)\")) { //using Au nuget\n\t\t\tb.ReplaceTail(10, arch == 2 ? L\"x64\\\\native\" : L\"arm64\\\\native\");\n\t\t} else { //probably in app dir, eg in temp dir when published single-file\n\t\t\tb << (arch == 2 ? L\"\\\\64\" : L\"\\\\64\\\\ARM\");\n\t\t}\n#endif\n\n\t\tb << L\"\\\\Au.DllHost.exe\";\n\t\t//Printf(L\"run: %s\", (wchar_t*)b);\n\t\tif (GetFileAttributes(b + 1) == INVALID_FILE_ATTRIBUTES) return 0;\n\t\tb << L\"\\\" \";\n\n\t\tSECURITY_ATTRIBUTES sa = { }; sa.bInheritHandle = true;\n\t\tCHandle ev(CreateEventW(&sa, false, false, null)); //not necessary, but makes faster\n\n\t\tb << (__int64)w << ' ' << (__int64)ev.m_h;\n\n\t\tSTARTUPINFOW si = { }; si.cb = sizeof(si); si.dwFlags = STARTF_FORCEOFFFEEDBACK;\n\t\tPROCESS_INFORMATION pi;\n\t\tbool ok = CreateProcessW(null, b, null, null, true, 0, null, null, &si, &pi);\n\t\tif (ok) {\n\t\t\tCloseHandle(pi.hThread);\n\t\t\tHANDLE ha[] = { ev, pi.hProcess };\n\t\t\tok = WAIT_OBJECT_0 == WaitForMultipleObjects(2, ha, false, INFINITE);\n\t\t\tCloseHandle(pi.hProcess);\n\t\t}\n\t\treturn ok;\n\t}\n\n#pragma endregion\n\n\t//Simple cache of a window, its thread id and optionally AO.\n\t//Can make faster, for example when waiting.\n\t//Use thread_local.\n\tclass HwndTidCache {\n\t\tDWORD _tid, _hwnd;\n\t\tULONGLONG _time;\n#ifdef AGENTCACHE\n\t\tIAccessible* _iaccAgent; //rejected. Too many problems. The speed improvement in many cases is very small.\n#endif\n\tpublic:\n\t\tHwndTidCache() noexcept {\n\t\t\tZEROTHIS;\n\t\t}\n\n#ifdef AGENTCACHE\n\t\t//~HwndTidCache() {\n\t\t//\tif(_iaccAgent) _iaccAgent->Release();\n\t\t//\t//if(_iaccAgent) {\n\t\t//\t//\tPrint(1);\n\t\t//\t//\t_iaccAgent->Release();\n\t\t//\t//\tPrint(2);\n\t\t//\t//}\n\t\t//}\n\t\t//shoulddo: release _iaccAgent always. Now dtor is disabled because of this problem:\n\t\t//\tRelease hangs.\n\t\t//\tConditions:\n\t\t//\t\tWin7 (tested only on the virtual PC).\n\t\t//\t\t.NET primary STA thread. Only when primary, only when STA, only of primary appdomain.\n\t\t//\tPossible reasons (I guess):\n\t\t//\t\tdtor is called while unloading this dll.\n\t\t//\t\t.NET uninitializes COM before dtor is called.\n\n\t\t//this is a workaround for the above problem.\n\t\t//called on DLL_THREAD_DETACH, which is not called for the primary thread.\n\t\t//now we will not have memory leaks in most cases, and in other cases it is not so important.\n\t\tvoid OnThreadDetach() {\n\t\t\tif (_iaccAgent) {\n\t\t\t\t//Print(1);\n\t\t\t\t_iaccAgent->Release();\n\t\t\t\t//Print(2);\n\t\t\t\t_iaccAgent = null;\n\t\t\t}\n\t\t}\n\t\t//is dtor always called? No. Eg not called for threadpool threads. Called for the primary thread and for threads that end before the process ends.\n\t\t//~HwndTidCache() {\n\t\t//\tif(_iaccAgent) Printf(L\"dtor, %i\", GetCurrentThreadId());\n\t\t//}\n\n\t\tbool Get(DWORD tid, out HWND& w, out IAccessible** iaccAgent = null) {\n\t\t\tif (tid != _tid) return false;\n\t\t\tULONGLONG time = GetTickCount64(); if (time - _time > 5000) return false;\n\t\t\tHWND wCached = (HWND)(LPARAM)_hwnd;\n\t\t\tif (tid != GetWindowThreadProcessId(wCached, null)) return false;\n\t\t\t_time = time;\n\t\t\tw = wCached;\n\t\t\tif (iaccAgent) *iaccAgent = _iaccAgent;\n\t\t\treturn true;\n\t\t}\n\n\t\tvoid Set(DWORD tid, HWND w, IAccessible* iaccAgent = null) {\n\t\t\t_tid = tid;\n\t\t\t_hwnd = (int)(LPARAM)w;\n\t\t\tif (_iaccAgent) _iaccAgent->Release(); //problem: hangs if agent's thread now is in a blocked wait function or busy.\n\t\t\t_iaccAgent = iaccAgent;\n\t\t\t_time = GetTickCount64();\n\t\t}\n#else\n\n\t\tbool Get(DWORD tid, out HWND& w) {\n\t\t\tif (tid != _tid) return false;\n\t\t\tULONGLONG time = GetTickCount64(); if (time - _time > 5000) return false;\n\t\t\tHWND wCached = (HWND)(LPARAM)_hwnd;\n\t\t\tif (tid != GetWindowThreadProcessId(wCached, null)) return false;\n\t\t\t_time = time;\n\t\t\tw = wCached;\n\t\t\treturn true;\n\t\t}\n\n\t\tvoid Set(DWORD tid, HWND w) {\n\t\t\t_tid = tid;\n\t\t\t_hwnd = (int)(LPARAM)w;\n\t\t\t_time = GetTickCount64();\n\t\t}\n#endif\n\t};\n\n#ifdef AGENTCACHE\n\tthread_local HwndTidCache t_agentCache, t_failedCache;\n\n\tvoid HwndTidCache_OnThreadDetach() {\n\t\tt_agentCache.OnThreadDetach();\n\t}\n#else\n\tthread_local HwndTidCache t_failedCache;\n#endif\n\n\t//Finds agent window and gets its AO.\n\t//If dll still not injected, injects and creates agent window.\n\t//w - a window in the target process/thread.\n\t//iaccAgent - receives agent's AO. Don't Release, it's cached.\n\t//wAgent - receives agent window. Optional.\n\t//Returns: 0, eError::WindowClosed, eError::WindowOfThisThread, eError::UseNotInProc, eError::Inject.\n\tHRESULT InjectDllAndGetAgent(HWND w, out IAccessible*& iaccAgent, out HWND* wAgent /*= null*/) {\n\t\tHWND wa = 0;\n\t\tDWORD pid, tid = GetWindowThreadProcessId(w, &pid); if (tid == 0) return (HRESULT)eError::WindowClosed;\n\n\t\t//use a simple cache to make faster, for example when waiting for AO in w\n#ifdef AGENTCACHE\n\t\tif (t_agentCache.Get(tid, out wa, out & iaccAgent)) {\n\t\t\tif (wAgent) *wAgent = wa;\n\t\t\treturn 0;\n\t\t}\n#endif\n\t\tHWND wFailed; if (t_failedCache.Get(tid, out wFailed)) return (HRESULT)eError::Inject;\n\n\t\tif (tid == GetCurrentThreadId()) return (HRESULT)eError::WindowOfThisThread;\n\n\t\t//problem: cannot inject dll into Store processes.\n\t\t//\ttested: uiAccess does not help.\n\t\t//\ttested: SetProcessRestrictionExemption always returns true, but dll injection fails. We cannot know whether a license exists.\n\t\t//\t\tMSDN says \"This function only succeeds if a developer license is present on the system.\". Probably the API does not work as documented.\n\t\t//\t\ttested: AcquireDeveloperLicense. It opens the Settings app -> security. No options to acquire a license.\n\t\t//\t\tsee also: https://superuser.com/questions/496104/windows-8-developer-license\n\t\t//\tnote: the child Windows.UI.Core.CoreWindow runs in other process than its host ApplicationFrameWindow. We need to inject into the control's process. Injection into the host's process succeeds but is useless.\n\t\tif (wn::ClassNameIs(GetAncestor(w, GA_ROOT), { L\"ApplicationFrameWindow\", L\"Windows.UI.Core.CoreWindow\", L\"ConsoleWindowClass\", L\"SunAwt*\" }))\n\t\t\treturn (HRESULT)eError::UseNotInProc;\n\n\t\tstatic CHandle s_mutex(CreateMutexW(SecurityAttributes::Common(), false, L\"AuCpp_MutexGAW\"));\n\t\tDWORD wfso = WaitForSingleObject(s_mutex, INFINITE);\n\t\tassert(wfso == 0 || wfso == WAIT_ABANDONED);\n\t\tAutoReleaseMutex arm(s_mutex);\n\n\t\t//Perf.First();\n\t\twchar_t name[12]; _itow(tid, name, 10);\n\t\twa = wn::FindWndEx(HWND_MESSAGE, 0, c_agentWindowClassName, name);\n\t\t//Perf.Next();\n\t\tif (!wa) {\n\t\t\tint arch = Cpp_GetProcessArchitecture(pid);\n\t\t\t//Printf(L\"arch=%i\", arch);\n\t\t\t//Perf.Next();\n\t\t\tbool ok = arch > 0;\n\t\t\tif (ok) {\n\t\t\t\tif (arch == GetProcessArchitecture()) {\n\t\t\t\t\twa = InjectDll(w, tid, pid);\n\t\t\t\t} else if (SwitchArchAndInjectDll(w, arch)) {\n\t\t\t\t\twa = wn::FindWndEx(HWND_MESSAGE, 0, c_agentWindowClassName, name);\n\t\t\t\t}\n\t\t\t\tok = !!wa;\n\t\t\t}\n\t\t\t//Perf.Next();\n\n\t\t\tif (!ok) {\n\t\t\t\tif (!IsWindow(w)) return (HRESULT)eError::WindowClosed;\n\t\t\t\tt_failedCache.Set(tid, w);\n\t\t\t\treturn (HRESULT)eError::Inject;\n\t\t\t}\n\t\t}\n\t\t//Perf.Write();\n\n\t\tif (!UnmarshalAgentIAccessible(wa, iaccAgent)) {\n\t\t\tt_failedCache.Set(tid, w);\n\t\t\treturn (HRESULT)eError::Inject;\n\t\t}\n\n#ifdef AGENTCACHE\n\t\tt_agentCache.Set(tid, wa, iaccAgent);\n#endif\n\n\t\tif (wAgent) *wAgent = wa;\n\t\treturn 0;\n\t}\n\n#ifdef _DEBUG\n\tEXPORT void Cpp_InProcTest(IAccessible* a) {\n\t\tInProcCall ic;\n\t\tic.AllocParams(a, InProcAction::IPA_AccTest, sizeof(MarshalParams_Header));\n\t}\n#endif\n\n} //namespace outproc\n"
  },
  {
    "path": "Cpp/internal.h",
    "content": "//This file is #included in all cpp files through Cpp.h.\n\n#pragma once\n#include \"stdafx.h\"\n\n\n//Internal flags used by 'find AO' functions.\nenum class eAF2 {\n\t//these are from role prefix\n\tInWebPage = 1, //\"web:\", \"firefox:\" or \"chrome:\"\n\tInFirefoxPage = 2, //\"firefox:\", or \"web:\" and detected Firefox\n\tInChromePage = 4, //\"chrome:\", or \"web:\" and detected Chrome\n\tInIES = 8, //\"web:\", detected IE web browser control\n\n\tNotInProc = 0x100, //from eAF::NotInProc\n\tFindAll = 0x200,\n\tGetRects = 0x400,\n\n\t//these are from prop parameter\n\tInControls = 0x40, //\"class=x\" or \"id=x\"\n\tIsRectL = 0x1000,\n\tIsRectT = 0x2000,\n\tIsRectW = 0x4000,\n\tIsRectH = 0x8000,\n\tIsRect = 0xF000,\n\tIsElem = 0x10000,\n\tIsId = 0x20000, //\"id=x\", where x is a number\n\n\tInFirefoxNotWebNotUIA = 0x100000,\n};\nENABLE_BITMASK_OPERATORS(eAF2);\n\nstruct RECTWH { long L; long T; long W; long H; };\n\n//AccFindCallback return type.\nenum class eAccFindCallbackResult { Continue, SkipChildren, StopFound, StopNotFound };\n\n//Type of callback functor that receives results of the AO finder.\nusing AccFindCallback = const std::function <eAccFindCallbackResult(Cpp_Acc a, int state, int nSiblings)>;\n\n//STR name and str::Wildex value.\nstruct NameValue {\n\tSTR name;\n\tstr::Wildex value;\n};\n\nconst STR c_IES = L\"Internet Explorer_Server\";\nconst STR c_CRW = L\"Chrome_RenderWidgetHostHWND\";\n\nenum class eSpecWnd { None, Chrome, Java, OO, Mozilla, ChromeControl, ChromeControl2 };\n\n//wParam of the injection message.\n//Also MarshalParams_Header::magic.\nconst long c_magic = -1'572'289'143;\n\n//MarshalParams_Header::action. Action for the get_accHelpTopic hook function.\nenum InProcAction : char {\n#ifdef _DEBUG\n\tIPA_AccTest,\n#endif\n\tIPA_AccFind = 1,\n\tIPA_AccFromWindow,\n\tIPA_AccFromPoint,\n\tIPA_AccFocused,\n\tIPA_AccNavigate,\n\tIPA_AccGetProps,\n\tIPA_AccGetWindow,\n\tIPA_AccGetHtml,\n\tIPA_AccEnableChrome,\n\n\tIPA_ShellExec = 100,\n};\n\n//Common fields of parameters-marshaling structures.\nstruct MarshalParams_Header {\n\tint magic;\n\tInProcAction action;\n\teAccMiscFlags miscFlags;\n};\n\n//Used for marshaling parameters of Cpp_AccFromWindow when calling the get_accHelpTopic hook function.\nstruct MarshalParams_AccFromWindow {\n\tMarshalParams_Header hdr;\n\tint hwnd; //not HWND, because it must be of same size in 32 and 64 bit process\n\tDWORD objid;\n\tDWORD flags; //2 - get name instead of AO\n};\n\n//Used for marshaling parameters of Cpp_AccFromPoint when calling the get_accHelpTopic hook function.\nstruct MarshalParams_AccFromPoint {\n\tMarshalParams_Header hdr;\n\tPOINT p;\n\teXYFlags flags;\n\teSpecWnd specWnd;\n\tint wFP;\n};\n\n//Used for marshaling parameters of Cpp_AccFocused when calling the get_accHelpTopic hook function.\nstruct MarshalParams_AccFocused {\n\tMarshalParams_Header hdr;\n\tint hwnd;\n\teFocusedFlags flags;\n};\n\n//Used for marshaling parameters of Cpp_AccNavigate, Cpp_AccGetProps, etc when calling the get_accHelpTopic hook function.\nstruct MarshalParams_AccElem {\n\tMarshalParams_Header hdr;\n\tlong elem;\n};\n\n//Used for marshaling 1-4 int parameters when calling the get_accHelpTopic hook function.\nstruct MarshalParams_AccInt4 {\n\tMarshalParams_Header hdr;\n\tint i0, i1, i2, i3;\n};\n\nnamespace outproc {\n\tHRESULT InjectDllAndGetAgent(HWND w, out IAccessible*& iaccAgent, out HWND* wAgent = null);\n\n\t//Calls the hooked get_accHelpTopic in the target process.\n\t//Packs some parameters, unpacks the returned data.\n\tclass InProcCall {\n\t\tIAccessible* _a;\n\t\t_variant_t _vParams;\n\t\tBstr _br;\n\t\tSmart<IStream> _stream;\n\t\tDWORD _resultSize;\n\tpublic:\n\t\t//Allocates memory to pass parameters.\n\t\t//Writes MarshalParams_Header fields. Then let the caller cast the return value to MarshalParams_AccFind* etc and write other fields.\n\t\tMarshalParams_Header* AllocParams(Cpp_Acc* a, InProcAction action, size_t size) {\n\t\t\t_a = a->acc;\n\t\t\t_vParams.bstrVal = SysAllocStringByteLen(null, (UINT)size);\n\t\t\t_vParams.vt = VT_BSTR;\n\t\t\tauto h = (MarshalParams_Header*)_vParams.bstrVal;\n\t\t\th->magic = c_magic;\n\t\t\th->action = action;\n\t\t\th->miscFlags = a->misc.flags;\n\t\t\treturn h;\n\t\t}\n\n\t\t//Allocates memory to pass parameters.\n\t\t//Writes MarshalParams_Header fields. Then let the caller cast the return value to MarshalParams_AccFind* etc and write other fields.\n\t\tMarshalParams_Header* AllocParams(IAccessible* iacc, InProcAction action, size_t size) {\n\t\t\tCpp_Acc a(iacc, 0);\n\t\t\treturn AllocParams(&a, action, size);\n\t\t}\n\n\t\t//Calls the hooked get_accHelpTopic in the target process.\n\t\t//Returns 0 if successful. Else returns (HRESULT)eError::X (>0x1000), or a standard COM error code, eg exception, disconnected, etc.\n\t\tHRESULT Call() {\n\t\t\tlong magic = 0;\n\t\t\tHRESULT hr = _a->get_accHelpTopic(&_br, _vParams, &magic);\n\t\t\tif (magic != c_magic) {\n\t\t\t\t//possible reasons:\n\t\t\t\t//\tnot hooked.\n\t\t\t\t//\texception in hook. Then magic is rejected.\n\t\t\t\t//\tRPC failed, eg the object is disconnected.\n\t\t\t\tswitch (hr) {\n\t\t\t\tcase E_NOTIMPL: case DISP_E_MEMBERNOTFOUND: case E_INVALIDARG: case S_FALSE: case 0: //guess\n\t\t\t\t\thr = E_NOINTERFACE;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn hr;\n\t\t}\n\n\t\tHRESULT ReadResultAcc(ref Cpp_Acc& a, bool dontNeedAO = false, RECT* rect = null);\n\n\t\tBSTR DetachResultBSTR() {\n\t\t\treturn _br.Detach();\n\t\t}\n\n\t\tBSTR GetResultBSTR() {\n\t\t\treturn _br;\n\t\t}\n\t};\n} //namespace outproc\n\nnamespace inproc {\n\tHRESULT STDMETHODCALLTYPE Hook_get_accHelpTopic(IAccessible* iacc, out BSTR& sResult, VARIANT vParams, long* pMagic);\n\tbool AccDisconnectWrappers();\n\n\t//Sets and restores get_accHelpTopic hook for all IAccessible interface tables.\n\t//Usually there are 1 or 2 interface tables in a process, but eg Firefox with multiple tabs can have ~10.\n\tclass HookIAccessible {\n\t\tstruct _RESTORE {\n\t\t\tLPVOID* place; //address of the function pointer in the interface table\n\t\t\tLPVOID oldFunc;\n\t\t};\n\n\t\tCSimpleArray<_RESTORE> _a;\n\n\t\t//place - address of the function pointer in the interface table.\n\t\t//func - the hook function or the old function (to unhook).\n\t\tbool _ReplaceFunctionInTable(LPVOID* place, LPVOID func) {\n\t\t\tDWORD oldProt = 0;\n\t\t\tBOOL vpOK = VirtualProtect(place, sizeof(LPVOID), PAGE_EXECUTE_READWRITE, &oldProt);\n\t\t\tassert(vpOK); if (!vpOK) return false;\n\t\t\t*place = func;\n\t\t\tif (oldProt != PAGE_EXECUTE_READWRITE) VirtualProtect(place, sizeof(LPVOID), oldProt, &oldProt);\n\t\t\treturn true;\n\t\t}\n\n\tpublic:\n\t\tbool Hook(IAccessible* iacc) {\n\t\t\tLPVOID* place = *(LPVOID**)iacc + 16; //&get_accHelpTopic\n\t\t\tLPVOID func = *place; //get_accHelpTopic\n\t\t\tif (func != Hook_get_accHelpTopic) {\n\t\t\t\t//Printf(L\"%p\", func);\n\t\t\t\tstatic CComAutoCriticalSection _cs; CComCritSecLock<CComAutoCriticalSection> lock(_cs);\n\t\t\t\tfor (int i = 0, n = _a.GetSize(); i < n; i++) if (_a[i].place == place) return true; //some unknown hook may be installed after ours\n\t\t\t\tif (!_ReplaceFunctionInTable(place, Hook_get_accHelpTopic)) return false;\n\t\t\t\t_RESTORE r = { place, func };\n\t\t\t\t_a.Add(r);\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\t~HookIAccessible() {\n\t\t\tfor (int i = _a.GetSize() - 1; i >= 0; i--) {\n\t\t\t\t_RESTORE r = _a[i];\n\t\t\t\tbool restored = _ReplaceFunctionInTable(r.place, r.oldFunc);\n\t\t\t\t//Printf(L\"restored=%i, func=%p\", restored, r.oldFunc);\n\t\t\t}\n\t\t}\n\t};\n\textern HookIAccessible s_hookIAcc;\n\n}\n\nenum class eWinFlags {\n\tAccEnabled = 1,\n\tAccJavaYes = 8,\n\tAccJavaNo = 16,\n};\nENABLE_BITMASK_OPERATORS(eWinFlags);\n\nclass WinFlags {\n\tCComAutoCriticalSection _cs;\n\tATOM _atom;\n\n\tWinFlags(int u) {\n\t\t_atom = GlobalFindAtomW(L\"PuGVNJS5Ck2lc5K/DYEUwg\");\n\t\tif (_atom == 0) _atom = GlobalAddAtomW(L\"PuGVNJS5Ck2lc5K/DYEUwg\");\n\t}\n\t//~WinFlags() { GlobalDeleteAtom(_atom); } //don't. Deletes even if currently used by a window prop, making the prop useless.\n\t//FUTURE: can be simplified, because now don't need dtor\n\n\tstatic WinFlags& _Inst() {\n\t\tstatic WinFlags s_inst(0);\n\t\treturn s_inst;\n\t}\n\npublic:\n\tstatic eWinFlags Get(HWND w) {\n\t\treturn (eWinFlags)(LPARAM)GetPropW(w, (LPCWSTR)_Inst()._atom);\n\t}\n\n\t//Removes removeFlags flags and adds addFlags.\n\tstatic void Set(HWND w, eWinFlags addFlags, eWinFlags removeFlags = {}) {\n\t\tauto inst = _Inst();\n\t\tCComCritSecLock<CComAutoCriticalSection> lock(inst._cs);\n\t\teWinFlags t = Get(w), t0 = t;\n\t\tt &= ~removeFlags;\n\t\tt |= addFlags;\n\t\tif (t != t0) SetPropW(w, (LPCWSTR)inst._atom, (HANDLE)(LPARAM)t);\n\t}\n};\n"
  },
  {
    "path": "Cpp/other.cpp",
    "content": "#include \"stdafx.h\"\n#include \"cpp.h\"\n\nnamespace other {\n\tEXPORT HMODULE Cpp_ModuleHandle() {\n\t\treturn s_moduleHandle;\n\t}\n\n\tLRESULT CALLBACK ClipboardHook(int code, WPARAM wParam, LPARAM lParam) {\n\t\tauto m = (MSG*)lParam;\n\t\tif (code < 0) goto g1;\n\t\tif (m->message == WM_CLIPBOARDUPDATE) {\n\t\t\t//Print(\"WM_CLIPBOARDUPDATE\");\n\t\t\tchar cn[256];\n\t\t\tif (0 == GetClassNameA(m->hwnd, cn, sizeof(cn)) || 0 != strcmp(cn, \"Au.DWP\")) {\n\t\t\t\tm->message = 0;\n\t\t\t\t//Print(cn);\n\t\t\t}\n\t\t\treturn 0;\n\t\t}\n\tg1:\n\t\treturn CallNextHookEx(0, code, wParam, lParam);\n\n\t\t//After unhooking, this dll remains loaded until hooked threads receive messages.\n\t\t//\tTo unload when [un]installing, installer uses code like in Cpp_Unload (broadcasts messages).\n\t\t//\tTo unload when building, Cpp project's pre-link event runs BuildEvents.exe which calls Cpp_Unload.\n\t}\n\n\tEXPORT HHOOK Cpp_Clipboard(HHOOK hh) {\n\t\tif (hh == NULL) {\n\t\t\tauto hh = SetWindowsHookExW(WH_GETMESSAGE, ClipboardHook, s_moduleHandle, 0);\n\t\t\treturn hh;\n\t\t} else {\n\t\t\tUnhookWindowsHookEx(hh);\n\t\t}\n\t\treturn NULL;\n\t}\n\n\t//auto-restore the unhandled exception filter used by .NET for UnhandledException event.\n\t//\tSome API replace or remove it. Then UnhandledException does not work.\n\t//\tKnown bad API:\n\t//\t\tCommon file dialog API and their wrappers. Fixed on Win11.\n\t//\t\tWPF on Win11. Eg Panel.Children.Add. Not on Win10 (same .NET version).\n\t//\tThis code could be in C#, but then on exception \"Unknown hard error\" (not in all cases).\n\n\tstatic LPTOP_LEVEL_EXCEPTION_FILTER s_ueh;\n\tstatic PVOID s_veh;\n\n\tstatic long __stdcall _Veh(_EXCEPTION_POINTERS* ExceptionInfo) {\n\t\tSetUnhandledExceptionFilter(s_ueh);\n\t\treturn 0;\n\t}\n\n\tEXPORT void Cpp_UEF(BOOL on) {\n\t\tif (on) { //called from AppModuleInit_\n\t\t\tSetUnhandledExceptionFilter(s_ueh = SetUnhandledExceptionFilter(0)); //get current UEF\n\t\t\ts_veh = AddVectoredExceptionHandler(1, _Veh); //restore on every exception, handled or not\n\t\t} else if (s_veh) { //called on process exit\n\t\t\tSetUnhandledExceptionFilter(s_ueh);\n\t\t\tRemoveVectoredExceptionHandler(s_veh);\n\t\t\ts_veh = null;\n\t\t}\n\t}\n\n\tstatic HWINEVENTHOOK s_iww_hook;\n\n\tvoid __stdcall _IWW_Hook(HWINEVENTHOOK hWinEventHook, DWORD event, HWND hwnd, LONG idObject, LONG idChild, DWORD idEventThread, DWORD dwmsEventTime) {\n\t\tif (!(wn::ExStyle(hwnd) & WS_EX_NOACTIVATE)) {\n\t\t\t//wn::PrintWnd(hwnd);\n\t\t\t//Print(hwnd == GetForegroundWindow());\n\n\t\t\tbool isActive = hwnd == GetForegroundWindow(), activate = !isActive && hwnd == GetActiveWindow();\n\t\t\tif (isActive || activate) {\n\t\t\t\tUnhookWinEvent(hWinEventHook);\n\t\t\t\ts_iww_hook = 0;\n\t\t\t\tif (activate) SetForegroundWindow(hwnd);\n\t\t\t}\n\t\t}\n\t}\n\n\t//Workaround for: miniProgram window usually is inactive (eg MessageBox), and may be even under other windows (WPF).\n\t//\tIt is because of task process preloading. OS does not activate the first window of an old process.\n\t//\tWorkaround: set EVENT_SYSTEM_FOREGROUND hook for this process. It runs whenever a window wants to be active,\n\t//\t\teven if OS does not activate it. The hook activates the first window and uninstalls self.\n\t//\t\tIt must run in the window's thread (because uses GetActiveWindow), therefore need WINEVENT_INCONTEXT+hmodule and cannot be in C#.\n\tEXPORT void Cpp_InactiveWindowWorkaround(BOOL on) {\n\t\tif (on) {\n\t\t\ts_iww_hook = SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, (HMODULE)&__ImageBase, _IWW_Hook, GetCurrentProcessId(), 0, WINEVENT_INCONTEXT);\n\t\t} else if (s_iww_hook) {\n\t\t\tUnhookWinEvent(s_iww_hook);\n\t\t\ts_iww_hook = 0;\n\t\t}\n\t}\n}\n\nnamespace {\n\t//Used for marshaling Cpp_ShellExec (IPA_ShellExec) parameters when calling the get_accHelpTopic hook function.\n\t//A flat variable-size memory structure (strings follow the fixed-size part).\n\tstruct MarshalParams_ShellExec {\n\t\tMarshalParams_Header hdr;\n\tprivate:\n\t\tint _file, _dir, _verb, _params, _class, _idlist; //offsets\n\t\tint _nshow, _hwnd;\n\t\tULONG _mask;\n\n\t\tLPWSTR _SetString(STR s, LPWSTR dest, out int& offset) {\n\t\t\tif (!s) {\n\t\t\t\toffset = 0;\n\t\t\t\treturn dest;\n\t\t\t}\n\t\t\tint len = (int)wcslen(s);\n\t\t\tmemcpy(dest, s, len * 2); dest[len] = 0;\n\t\t\toffset = (int)(dest - (STR)this);\n\t\t\treturn dest + len + 1;\n\t\t}\n\n\t\tSTR _GetString(int offset) {\n\t\t\tif (!offset) return null;\n\t\t\treturn (STR)this + offset;\n\t\t}\n\n\t\tvoid _SetIL(void* il, LPWSTR dest, out int& offset) {\n\t\t\tif (!il) {\n\t\t\t\toffset = 0;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tint size = ILGetSize((LPCITEMIDLIST)il);\n\t\t\tmemcpy(dest, il, size);\n\t\t\toffset = (int)(dest - (STR)this);\n\t\t}\n\n\t\tvoid* _GetIL(int offset) {\n\t\t\tif (!offset) return null;\n\t\t\treturn (LPWSTR)this + offset;\n\t\t}\n\tpublic:\n\t\tstatic int _Size(STR s) {\n\t\t\treturn s == null ? 0 : ((int)wcslen(s) + 1) * 2;\n\t\t}\n\t\tstatic int _SizeIL(void* idlist) {\n\t\t\treturn idlist == null ? 0 : ILGetSize((LPCITEMIDLIST)idlist) + 1 & ~1;\n\t\t}\n\n\t\tstatic int CalcMemSize(const SHELLEXECUTEINFO& x) {\n\t\t\treturn sizeof(MarshalParams_ShellExec) + _SizeIL(x.lpIDList) + _Size(x.lpFile) + _Size(x.lpDirectory) + _Size(x.lpParameters) + _Size(x.lpVerb);\n\t\t}\n\n#pragma warning(disable: 4302 4311 4312) //conversion HWND <-> int\n\t\tvoid Marshal(const SHELLEXECUTEINFO& x) {\n\t\t\t_mask = x.fMask;\n\t\t\t_nshow = x.nShow;\n\t\t\t_hwnd = (int)x.hwnd;\n\t\t\tauto s = (LPWSTR)(this + 1);\n\t\t\ts = _SetString(x.lpFile, s, out _file);\n\t\t\ts = _SetString(x.lpDirectory, s, out _dir);\n\t\t\ts = _SetString(x.lpVerb, s, out _verb);\n\t\t\ts = _SetString(x.lpParameters, s, out _params);\n\t\t\ts = _SetString(x.lpClass, s, out _class);\n\t\t\t_SetIL(x.lpIDList, s, out _idlist);\n\n\t\t\t//never mind: the new process does not inherit environment variables.\n\t\t\t//\tI don't know how to pass them when using shell prcess. Canot modify its environment variables, even temporarily.\n\t\t\t//\tIt is documented.\n\t\t}\n\n\t\tSHELLEXECUTEINFO Unmarshal() {\n\t\t\tSHELLEXECUTEINFO x = { sizeof(SHELLEXECUTEINFO), _mask };\n\t\t\tx.nShow = _nshow;\n\t\t\tx.hwnd = (HWND)_hwnd;\n\t\t\tx.lpFile = _GetString(_file);\n\t\t\tx.lpDirectory = _GetString(_dir);\n\t\t\tx.lpVerb = _GetString(_verb);\n\t\t\tx.lpParameters = _GetString(_params);\n\t\t\tx.lpClass = _GetString(_class);\n\t\t\tx.lpIDList = _GetIL(_idlist);\n\t\t\treturn x;\n\t\t}\n\t};\n}\n\nnamespace inproc {\n\tHRESULT ShellExec(MarshalParams_Header* h, out BSTR& sResult) {\n\t\tsResult = null;\n\t\tauto m = (MarshalParams_ShellExec*)h;\n\t\tauto x = m->Unmarshal();\n\t\tif (!ShellExecuteExW(&x)) return GetLastError();\n\t\tif (x.hProcess) {\n\t\t\tDWORD pid = GetProcessId(x.hProcess);\n\t\t\tCloseHandle(x.hProcess);\n\t\t\tif (pid != 0) sResult = SysAllocStringByteLen((LPCSTR)&pid, 4);\n\t\t}\n\t\treturn 0;\n\t}\n}\n\nnamespace other {\n\tEXPORT bool Cpp_ShellExec(const SHELLEXECUTEINFO& x, out DWORD& pid, out HRESULT& injectError, out HRESULT& execError) {\n\t\tpid = 0; injectError = 0; execError = 0;\n\t\tCpp_Acc_Agent aAgent;\n\t\tif (0 != (injectError = outproc::InjectDllAndGetAgent(GetShellWindow(), out aAgent.acc))) {\n\t\t\treturn false;\n\t\t}\n\n\t\toutproc::InProcCall ic;\n\t\tauto p = (MarshalParams_ShellExec*)ic.AllocParams(&aAgent, InProcAction::IPA_ShellExec, MarshalParams_ShellExec::CalcMemSize(x));\n\t\tp->Marshal(x);\n\t\tif (0 != (execError = ic.Call())) return false;\n\n\t\tBSTR b = ic.GetResultBSTR();\n\t\tif (b) pid = *(DWORD*)b;\n\n\t\treturn true;\n\t}\n\n\t//Unloads this dll (AuCpp.dll) from other processes.\n\t//flags: 1 wait less.\n\tEXPORT void Cpp_Unload(DWORD flags = 0) {\n\t\tint less = flags & 1 ? 5 : 1;\n\t\tDWORD_PTR res;\n\t\tstd::vector<HWND> a;\n\n\t\t//close acc agent windows\n\t\tfor (HWND w = 0; w = FindWindowExW(HWND_MESSAGE, w, L\"AuCpp_IPA_1\", nullptr); ) a.push_back(w);\n\t\tint n = (int)a.size();\n\t\tif (n > 0) {\n\t\t\tfor (int i = 0; i < n; i++) SendMessageTimeout(a[i], WM_CLOSE, 0, 0, SMTO_ABORTIFHUNG, 5000 / less, &res);\n\t\t\ta.clear();\n\t\t\tSleep(n * 50);\n\t\t}\n\n\t\t//unload from processes where loaded by the clipboard hook\n\t\tSendMessageTimeout(HWND_BROADCAST, 0, 0, 0, SMTO_ABORTIFHUNG, 1000 / less, &res);\n\t\tfor (HWND w = 0; w = FindWindowExW(HWND_MESSAGE, w, nullptr, nullptr); ) a.push_back(w);\n\t\tfor (int i = 0; i < (int)a.size(); i++) SendMessageTimeout(a[i], 0, 0, 0, SMTO_ABORTIFHUNG, 1000 / less, &res);\n\t\tSleep(500 / less);\n\t}\n\n\t//for rundll32.exe\n\tEXPORT void WINAPI UnloadAuCppDll(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow) {\n\t\tDWORD flags = (DWORD)atoi(lpszCmdLine);\n\t\tCpp_Unload(flags);\n\t}\n\n} //namespace other\n"
  },
  {
    "path": "Cpp/rejected.cpp",
    "content": "#if false\n\n\n#endif\n"
  },
  {
    "path": "Cpp/resource.h",
    "content": "//{{NO_DEPENDENCIES}}\n// Microsoft Visual C++ generated include file.\n// Used by Cpp.rc\n//\n#define IDR_RT_MANIFEST1                2\n\n// Next default values for new objects\n// \n#ifdef APSTUDIO_INVOKED\n#ifndef APSTUDIO_READONLY_SYMBOLS\n#define _APS_NEXT_RESOURCE_VALUE        103\n#define _APS_NEXT_COMMAND_VALUE         40001\n#define _APS_NEXT_CONTROL_VALUE         1001\n#define _APS_NEXT_SYMED_VALUE           101\n#endif\n#endif\n"
  },
  {
    "path": "Cpp/stdafx.cpp",
    "content": "// stdafx.cpp : source file that includes just the standard includes for generating pre-compiled header (pch) file\n\n#include \"stdafx.h\"\n"
  },
  {
    "path": "Cpp/stdafx.h",
    "content": "// stdafx.h : include file for standard system include files,\n// or project specific include files that are used frequently, but\n// are changed infrequently\n//\n\n#pragma once\n\n// Including SDKDDKVer.h defines the highest available Windows platform.\n// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.\n#include <SDKDDKVer.h>\n\n#define WIN32_LEAN_AND_MEAN\n#define _CRT_SECURE_NO_WARNINGS\n\n#include <assert.h>\n#include <string>\n#include <memory>\n#include <algorithm>\n#include <functional>\n#include <initializer_list>\n\n#include <comdef.h>\n#include <comdefsp.h> //smart pointers of many interfaces\n\n#include <atlbase.h>\n#include <atlcoll.h>\n//#include <concrt.h> //critical_section\n\n#include <windows.h>\n#include <Sddl.h>\n#include <OleAuto.h>\n#include <OleAcc.h>\n#include <UIAutomation.h>\n#include <shellapi.h>\n#include <shlobj.h>\n#include <shellscalingapi.h>\n\n#define PCRE2_STATIC 1\n#define PCRE2_CODE_UNIT_WIDTH 16\n#include \"..\\Libraries\\pcre\\pcre2.h\"\n\n\n\n#define null nullptr\n#define out\n#define ref\n\nusing STR = LPCWSTR;\n\n#pragma region enum operators\n//http://blog.bitwigglers.org/using-enum-classes-as-type-safe-bitmasks/\n\ntemplate<typename Enum>\nstruct EnableBitMaskOperators\n{\n\tstatic const bool enable = false;\n};\n\n#define ENABLE_BITMASK_OPERATORS(x) template<> struct EnableBitMaskOperators<x> { static const bool enable = true; }\n\ntemplate<typename Enum> constexpr\nstd::enable_if_t<EnableBitMaskOperators<Enum>::enable, Enum>\noperator |(Enum lhs, Enum rhs)\n{\n\tusing underlying = typename std::underlying_type<Enum>::type;\n\treturn static_cast<Enum> (static_cast<underlying>(lhs) | static_cast<underlying>(rhs));\n}\n\ntemplate<typename Enum> constexpr\nstd::enable_if_t<EnableBitMaskOperators<Enum>::enable, Enum>\noperator &(Enum lhs, Enum rhs)\n{\n\tusing underlying = typename std::underlying_type<Enum>::type;\n\treturn static_cast<Enum> (static_cast<underlying>(lhs) & static_cast<underlying>(rhs));\n}\n\ntemplate<typename Enum> constexpr\nstd::enable_if_t<EnableBitMaskOperators<Enum>::enable, Enum>\noperator ^(Enum lhs, Enum rhs)\n{\n\tusing underlying = typename std::underlying_type<Enum>::type;\n\treturn static_cast<Enum> (static_cast<underlying>(lhs) ^ static_cast<underlying>(rhs));\n}\n\ntemplate<typename Enum>\nstd::enable_if_t<EnableBitMaskOperators<Enum>::enable, Enum>\n& operator |=(Enum& lhs, Enum rhs)\n{\n\tusing underlying = typename std::underlying_type<Enum>::type;\n\treturn lhs = static_cast<Enum> (static_cast<underlying>(lhs) | static_cast<underlying>(rhs));\n}\n\ntemplate<typename Enum>\nstd::enable_if_t<EnableBitMaskOperators<Enum>::enable, Enum>\n& operator &=(Enum& lhs, Enum rhs)\n{\n\tusing underlying = typename std::underlying_type<Enum>::type;\n\treturn lhs = static_cast<Enum> (static_cast<underlying>(lhs) & static_cast<underlying>(rhs));\n}\n\ntemplate<typename Enum>\nstd::enable_if_t<EnableBitMaskOperators<Enum>::enable, Enum>\n& operator ^=(Enum& lhs, Enum rhs)\n{\n\tusing underlying = typename std::underlying_type<Enum>::type;\n\treturn lhs = static_cast<Enum> (static_cast<underlying>(lhs) ^ static_cast<underlying>(rhs));\n}\n\ntemplate<typename Enum> constexpr\nstd::enable_if_t<EnableBitMaskOperators<Enum>::enable, Enum>\noperator ~(Enum rhs)\n{\n\tusing underlying = typename std::underlying_type<Enum>::type;\n\treturn static_cast<Enum> (~static_cast<underlying>(rhs));\n}\n\n//compiler error: cannot be static\n//template<typename Enum>\n//operator bool(Enum rhs)\n//{\n//\tusing underlying = typename std::underlying_type<Enum>::type;\n//\treturn static_cast<underlying>(rhs) != 0;\n//}\n\ntemplate<typename Enum> constexpr\nstd::enable_if_t<EnableBitMaskOperators<Enum>::enable, bool>\noperator !(Enum rhs)\n{\n\tusing underlying = typename std::underlying_type<Enum>::type;\n\treturn 0 == static_cast<underlying>(rhs);\n}\n\n////Returns true if x has all flags.\n//x can be any enum or integer type of size <= 32-bit.\n//template<typename E>\n//bool F(E x, E flags) {\n//\tstatic_assert(sizeof(E) <= 4);\n//\treturn ((int)x&(int)flags) == (int)flags;\n//}\n//\n////Returns true if x has any of flags.\n////x can be any enum or integer type of size <= 32-bit.\n//template<typename E>\n//bool F_ANY(E x, E flags) {\n//\tstatic_assert(sizeof(E) <= 4);\n//\treturn (int)x&((int)flags);\n//}\n\n#pragma endregion\n\ntemplate<class T, class... Ts>\nconstexpr bool IsIn(const T& v, Ts... vs) {\n    return ((v == vs) || ...);\n}\n"
  },
  {
    "path": "Cpp/str.cpp",
    "content": "#include \"stdafx.h\"\n#include \"cpp.h\"\n#include \"MemoryPool.h\"\n\nnamespace str\n{\n\n#pragma region Like, Equals, lowercase table\n\nstatic WCHAR _caseTable[0x10000];\nstatic bool _caseTableCreated;\n\n//Returns static WCHAR table[0x10000] containing all Unicode characters in that range. Uppercase characters converted to lowercase.\nEXPORT STR Cpp_LowercaseTable()\n{\n\tif(!_caseTableCreated) {\n\t\tLPWSTR t = _caseTable;\n\t\tfor(size_t i = 0; i < 0x10000; i++) t[i] = (WCHAR)i;\n\t\tCharLowerBuff(t, 0x10000);\n\t\t_caseTableCreated = true;\n\t} //speed: 350\n\treturn _caseTable;\n}\n\ninline STR _LowercaseTable() {\n\treturn _caseTableCreated ? _caseTable : Cpp_LowercaseTable();\n}\n\n//Compares string s of length lenS with wildcard pattern w of length lenW.\n//Returns true if match. Returns false if s==null. Exception if w==null.\n//EXPORT //C# has own function. Calling this from C# is significantly slower when string short.\nbool Like(STR s, size_t lenS, STR w, size_t lenW, bool ignoreCase /*= false*/)\n{\n\tif(s == null) return false;\n\tif(lenW == 0) return lenS == 0;\n\tif(lenW == 1 && w[0] == '*') return true;\n\tif(lenS == 0) return false;\n\n\tSTR table = ignoreCase ? _LowercaseTable() : null;\n\tSTR se = s + lenS, we = w + lenW;\n\n\t//find '*' from start. Makes faster in some cases.\n\tfor(; (w < we && s < se); w++, s++) {\n\t\tsize_t cS = s[0], cW = w[0];\n\t\tif(cW == '*') goto g1;\n\t\tif(cW == cS || cW == '?') continue;\n\t\tif((table == null) || (table[cW] != table[cS])) return false;\n\t}\n\tif(w == we) return s == se; //w ended?\n\tgoto gr; //s ended\ng1:\n\n\t//find '*' from end. Makes \"*text\" much faster.\n\tfor(; (we > w && se > s); we--, se--) {\n\t\tsize_t cS = se[-1], cW = we[-1];\n\t\tif(cW == '*') break;\n\t\tif(cW == cS || cW == '?') continue;\n\t\tif((table == null) || (table[cW] != table[cS])) return false;\n\t}\n\n\t//Algorithm by Alessandro Felice Cantatore, http://xoomer.virgilio.it/acantato/dev/wildcard/wildmatch.html\n\t//Changes: supports '\\0' in string; case-sensitive or not; restructured, in many cases faster.\n\t{\n\t\tsize_t i = 0;\n\t\tgStar: //info: goto used because C# compiler makes the loop faster when it contains less code\n\t\tw += i + 1;\n\t\tif(w == we) return true;\n\t\ts += i;\n\n\t\tfor(i = 0; s + i < se; i++) {\n\t\t\tsize_t sW = w[i];\n\t\t\tif(sW == '*') goto gStar;\n\t\t\tif(sW == s[i] || sW == '?') continue;\n\t\t\tif((table != null) && (table[sW] == table[s[i]])) continue;\n\t\t\ts++; i = -1;\n\t\t}\n\n\t\tw += i;\n\t}\ngr:\n\twhile(w < we && *w == '*') w++;\n\treturn w == we;\n\n\t//info: Could implement escape sequence ** for * and maybe *? for ?.\n\t//\tBut it makes code slower etc.\n\t//\tNot so important.\n\t//\tMost users would not know about it.\n\t//\tUsually can use ? for literal * and ?.\n\t//\tUsually can use regular expression if need such precision.\n\t//\tThen cannot use \"**options \" for wildcard expressions.\n\t//\tCould use other escape sequences, eg [*], [?] and [[], but it makes slower and is more harmful than useful.\n\n\t//The first two loops are fast, but Equals much faster when !ignoreCase. We cannot use its optimizations.\n\t//The slowest case is \"*substring*\", because then the first two loops don't help.\n\t//\tThen similar speed as string.IndexOf(ordinal) and API <msdn>FindStringOrdinal</msdn>.\n\t//\tPossible optimization, but need to add much code, and makes not much faster, and makes other cases slower, difficult to avoid it.\n}\n\n//Compares string s of length lenS with string w of length lenW.\n//Returns true if match. Returns false if s==null. Exception if w==null.\n//EXPORT //C# has own function. Calling this from C# is significantly slower when string short.\nbool Equals(STR w, size_t lenW, STR s, size_t lenS, bool ignoreCase /*= false*/)\n{\n\tif(s == null || lenS != lenW) return false;\n\tif(lenW == 0) return true;\n\n\tif(!ignoreCase) return 0 == memcmp(s, w, lenW * 2); //fastest\n\n\t//optimization: at first compare case-sensitive, as much as possible.\n\t//\tnever mind: in 32-bit process this is not the fastest code (too few registers). But makes much faster anyway.\n\t//\ttested: strings don't have to be aligned at 4 or 8.\n\twhile(lenW >= 12) {\n\t\tif(*(__int64*)w != *(__int64*)s) break;\n\t\tif(*(__int64*)(w + 4) != *(__int64*)(s + 4)) break;\n\t\tif(*(__int64*)(w + 8) != *(__int64*)(s + 8)) break;\n\t\tw += 12; s += 12; lenW -= 12;\n\t}\n\n\tauto table = _LowercaseTable();\n\tfor(size_t i = 0; i < lenW; i++) {\n\t\tsize_t c1 = w[i], c2 = s[i];\n\t\tif(c1 != c2 && table[c1] != table[c2]) goto gFalse;\n\t}\n\treturn true; gFalse: return false;\n}\n\n//this could be used by C#, but calling unmanaged functions from C# is slow when with 'fixed', which makes much slower with short strings.\n//EXPORT bool Cpp_StringEqualsI(STR a, STR b, int len)\n//{\n//\tif(len != 0) {\n//\t\t//optimization: at first compare case-sensitive, as much as possible.\n//\t\t//\tnever mind: in 32-bit process this is not the fastest code (too few registers). But makes much faster anyway.\n//\t\t//\ttested: strings don't have to be aligned at 4 or 8.\n//\t\twhile(len >= 12) {\n//\t\t\tif(*(__int64*)a != *(__int64*)b) break;\n//\t\t\tif(*(__int64*)(a + 4) != *(__int64*)(b + 4)) break;\n//\t\t\tif(*(__int64*)(a + 8) != *(__int64*)(b + 8)) break;\n//\t\t\ta += 12; b += 12; len -= 12;\n//\t\t}\n//\n//\t\tauto table = _LowercaseTable();\n//\n//\t\tfor(size_t i = 0; i < len; i++) {\n//\t\t\tsize_t c1 = a[i], c2 = b[i];\n//\t\t\tif(c1 != c2 && table[c1] != table[c2]) goto gFalse;\n//\t\t}\n//\t}\n//\treturn true; gFalse: return false;\n//}\n\n#pragma endregion\n\n//EXPORT void Cpp_Free(void* p) {\n//\tif(p) free(p);\n//}\n\n\nnamespace pcre\n{\n\nBSTR _GetErrorMessage(int code, int compileErrorOffset)\n{\n\tWCHAR b[300]; b[0] = 0;\n\tauto t = b;\n\tif(compileErrorOffset >= 0) {\n\t\twcscat(t, L\"Regular expression error at offset \"); t += wcslen(t);\n\t\t_itow((int)compileErrorOffset, t, 10); t += wcslen(t);\n\t\twcscat(t, L\": \"); t += 2;\n\t}\n\tpcre2_get_error_message_16(code, t, _countof(b) - (t - b));\n\treturn SysAllocString(b);\n}\n\n//Calls pcre2_get_error_message_16 and returns SysAllocString.\nBSTR GetErrorMessage(int code) {\n\treturn _GetErrorMessage(code, -1);\n}\n\n//Calls pcre2_compile_16.\n//This version is used in this dll, eg by Wildex.\n//Adds PCRE2_UTF if rx contains non-ASCII characters and flags does not contain PCRE2_UTF or PCRE2_NEVER_UTF.\n//More info in Cpp_RegexCompile.\npcre2_code_16* Compile(STR rx, size_t len, __int64 flags /*= 0*/, out BSTR* errStr /*= null*/)\n{\n\tint errCode; size_t errOffset;\n\tUINT f = (UINT)flags, fe = flags >> 32;\n\tfe &= 0xFFFFFF; //hi 8 bits are used for match flags in C#.\n\n\t//If rx contains non-ASCII characters, add PCRE2_UTF flag. Else case-insensitive does not work.\n\tif(!(f & (PCRE2_UTF | PCRE2_NEVER_UTF))) {\n\t\tfor(size_t i = 0; i < len; i++) if(rx[i] >= 128) { f |= PCRE2_UTF; break; }\n\t}\n\n\tpcre2_compile_context_16* cc = null;\n\tif(fe) {\n\t\tcc = pcre2_compile_context_create_16(null);\n\t\tpcre2_set_compile_extra_options_16(cc, fe);\n\t}\n\tauto re = pcre2_compile_16(rx, len, f, &errCode, &errOffset, cc);\n\tif(cc) pcre2_compile_context_free_16(cc);\n\tif(re == null) {\n\t\tif(errStr)* errStr = _GetErrorMessage(errCode, (int)errOffset);\n\t\treturn null;\n\t}\n\treturn re;\n}\n\n//Calls/returns pcre2_compile_16. Returns null if fails (errors in regular expression etc).\n//This version is called from C#. In this dll use Compile instead.\n//flags is __int64 consisting of pcre2_compile_16 flags in lo 32 bits and pcre2_set_compile_extra_options_16 flags in lo 24 bits of hi 32 bits.\n//\tAdds PCRE2_UTF if rx contains non-ASCII characters and flags does not contain PCRE2_UTF or PCRE2_NEVER_UTF.\n//codeSize receives code size (PCRE2_INFO_SIZE).\n//errStr, if not null, receives error text when fails. Caller then must SysFreeString it.\nEXPORT pcre2_code_16* Cpp_RegexCompile(STR rx, size_t len, __int64 flags, out int& codeSize/*, out int& nGroups*/, out BSTR* errStr = null)\n{\n\tauto code = pcre::Compile(rx, len, flags, errStr);\n\tif(code != null) {\n\t\tsize_t z = 0;\n\t\tpcre2_pattern_info_16(code, PCRE2_INFO_SIZE, &z);\n\t\tcodeSize = (int)z;\n\t\t//pcre2_pattern_info_16(code, PCRE2_INFO_CAPTURECOUNT, &nGroups); nGroups++;\n\t} else {\n\t\tcodeSize = 0;\n\t\t//nGroups = 0;\n\t}\n\treturn code;\n}\n\n//If code is not null, calls pcre2_code_free_16 and returns code memory size (PCRE2_INFO_SIZE). Else returs 0.\n//This version is called from C#. In this dll use Free instead.\nEXPORT int Cpp_RegexDtor(pcre2_code_16* code)\n{\n\tif(code == null) return 0;\n\tsize_t codeSize = 0;\n\tpcre2_pattern_info_16(code, PCRE2_INFO_SIZE, &codeSize);\n\tpcre2_code_free_16(code);\n\treturn (int)codeSize;\n}\n\nstruct _RxMdVec { CHeapPtr<POINT> a; int n; };\n\n//After upgrading PCRE library, this reminds to check/reapply its modifications. Then edit this line.\n//More info in config.h in PCRE project.\nstatic_assert(PCRE2_MAJOR == 10 && PCRE2_MINOR == 46);\n\n//Cpp_RegexMatch results.\nstruct RegexMatch\n{\n\t//[out] Array that receives x=from and y=to of match and submatches.\n\t//Func allocates thread-local memory for it. Then caller copies it ASAP and does not free.\n\t//tested: this is the fastest way when caller is C#.\n\t//Func sets it on full and partial match. Else sets null.\n\tPOINT* vec;\n\n\t//[out] vec element count: pcre2_get_ovector_count_16 if matched, 0 if not, 1 if partial.\n\tint vecCount;\n\n\t//[out] pcre2_get_startchar_16.\n\tint indexNoK;\n\n\t//[out] pcre2_get_mark_16.\n\tSTR mark;\n};\n\n#define FAST_MD\n\nstatic void* _mp_malloc(size_t size, void* data) {\n\tauto mp = (AppShift::Memory::MemoryPool*)data;\n\treturn mp->allocate(size);\n}\n\nstatic void _mp_free(void* block, void* data) {\n\tauto mp = (AppShift::Memory::MemoryPool*)data;\n\tmp->free(block);\n}\n\nthread_local AppShift::Memory::MemoryPool* t_mp;\nthread_local pcre2_general_context_16* t_gc;\n\n//Gets thread_local pcre2_general_context_16 that uses memory pool.\nstatic pcre2_general_context_16* _GetGC() {\n\tauto gc = t_gc;\n\tif (gc == null) {\n\t\tt_mp = new AppShift::Memory::MemoryPool(25000); //#define START_FRAMES_SIZE 20480, and some for small allocations\n\t\tt_gc = gc = pcre2_general_context_create_16(_mp_malloc, _mp_free, t_mp);\n\t}\n\treturn gc;\n}\n\nvoid thread_detach() {\n\tif (t_mp == nullptr) return;\n\tdelete t_mp;\n\tt_mp = nullptr;\n\tt_gc = nullptr;\n}\n\n//Calls pcre2_match_16 and returns its return value. If PCRE2_ERROR_PARTIAL, returns 0.\n//Allocates/frees match data in a fast way. Copies results to m, if not null.\n//errStr, if not null, receives error text when fails, except when no match or partial match. Caller then must SysFreeString it.\n//This version is called from C#. In this dll you can use Free; use this func when need match data (ovector etc).\nEXPORT int Cpp_RegexMatch(pcre2_code_16* code, STR s, size_t len, size_t start = 0, UINT flags = 0,\n\tint(*callout)(pcre2_callout_block*, void*) = null, ref RegexMatch * m = null, bool needM = true, out BSTR * errStr = null)\n{\n\tpcre2_match_data_16* md;\n#ifdef FAST_MD\n\tmd = pcre2_match_data_create_from_pattern_16(code, _GetGC());\n\t//tested: startScope/endScope usually makes slightly slower.\n#else\n\tmd = pcre2_match_data_create_from_pattern_16(code, null);\n#endif\n\n\tint R = pcre2_match_16(code, s, len, start, flags, md, null, callout);\n\n\tassert(R != 0); //this could be if md contains too small ovector\n\tif(R == PCRE2_ERROR_PARTIAL) R = 0;\n\n\tif(needM) {\n\t\t//info: read PCRE API doc, section \"HOW PCRE2_MATCH() RETURNS A STRING AND CAPTURED SUBSTRINGS\"\n\t\tint n = R > 0 ? pcre2_get_ovector_count_16(md) : (R == 0 ? 1 : 0);\n\t\t//Printf(L\"R=%i, n=%i\", R, n);\n\n\t\tif(n == 0) {\n\t\t\tm->vec = null;\n\t\t} else {\n\t\t\tthread_local _RxMdVec t_vec; _RxMdVec& t = t_vec;\n\t\t\tif(t.n < n) {\n\t\t\t\tif(t.a) t.a.Free();\n\t\t\t\tif(t.a.Allocate(n)) t.n = (int)n;\n\t\t\t\telse { t.n = n = 0; R = PCRE2_ERROR_NOMEMORY; }\n\t\t\t}\n\t\t\tPOINT* g = t.a;\n\n\t\t\tauto v = pcre2_get_ovector_pointer_16(md);\n\t\t\tfor(int i = 0; i < n; i++) {\n\t\t\t\tPOINT& p = g[i];\n\t\t\t\tp.x = (int)v[i * 2]; p.y = (int)v[i * 2 + 1];\n\t\t\t}\n\t\t\tm->vec = g;\n\t\t}\n\t\tm->vecCount = n;\n\t\tm->indexNoK = (int)pcre2_get_startchar_16(md);\n\t\tm->mark = pcre2_get_mark_16(md);\n\t}\n\n\tstatic_assert(PCRE2_ERROR_NOMATCH == -1 && PCRE2_ERROR_PARTIAL == -2);\n\tif(R < PCRE2_ERROR_PARTIAL && errStr != null) {\n\t\t*errStr = GetErrorMessage(R);\n\t\t//FUTURE: if UTF error, in error text include the offset. It seems pcre2_get_startchar_16 returns it.\n\t}\n\n\tpcre2_match_data_free_16(md);\n\treturn R;\n}\n\n//Calls pcre2_match_16 and returns true if it returns >0.\n//Allocates/frees match data in a fast way.\n//This version is used in this dll, eg by Wildex.\nbool Match(pcre2_code_16* code, STR s, size_t len, size_t start, UINT flags)\n{\n\tpcre2_match_data_16* md;\n#ifdef FAST_MD\n\tmd = pcre2_match_data_create_from_pattern_16(code, _GetGC());\n#else\n\tmd = pcre2_match_data_create_from_pattern_16(code, null);\n#endif\n\n\tint R = pcre2_match_16(code, s, len, start, flags, md, null, null);\n\n\tpcre2_match_data_free_16(md);\n\n\treturn R > 0 || R == PCRE2_ERROR_PARTIAL;\n}\n}\n\n#pragma region Wildex\n\nWildex::~Wildex()\n{\n\tif(_text != null) {\n\t\tswitch(_type) {\n\t\tcase WildType::RegexPcre: pcre::Free(_regex); break;\n\t\tcase WildType::Multi: delete[] _multi_array; break;\n\t\tdefault: if(_freeText) free(_text);\n\t\t}\n\t\t_text = null;\n\t}\n}\n\n//Parses wildcard expression and initializes this variable.\n//On error sets errStr (if not null) and returns false.\n//dontCopyString - don't allocate/free own copy of w. The caller keeps w valid while using this variable.\n//Call once (asserts).\nbool Wildex::Parse(STR w, size_t lenW, bool dontCopyString/* = false*/, out BSTR* errStr/* = null*/)\n{\n\t//if(w == null) return false;\n\tassert(_text == null); //_text = null; _not = _freeText = false;\n\t_type = WildType::Wildcard;\n\t_ignoreCase = true;\n\tSTR es = L\"Invalid \\\"**options \\\" in wildcard expression.\";\n\tSTR split = L\"||\"; size_t splitLen = 2;\n\n\tif(lenW >= 3 && w[0] == '*' && w[1] == '*') {\n\t\tfor(size_t i = 2, j; i < lenW; i++) {\n\t\t\tswitch(w[i]) {\n\t\t\tcase 't': _type = WildType::Text; break;\n\t\t\tcase 'r': _type = WildType::RegexPcre; break;\n\t\t\tcase 'm': _type = WildType::Multi; break;\n\t\t\tcase 'c': _ignoreCase = false; break;\n\t\t\tcase 'n': _not = true; break;\n\t\t\tcase ' ': w += ++i; lenW -= i; goto g1;\n\t\t\tcase 'R': es = L\"Option R in wildcard expression. Use r instead.\"; //.NET Regex\n\t\t\tcase '(':\n\t\t\t\tif(w[i - 1] != 'm') goto ge;\n\t\t\t\tfor(j = ++i; j < lenW; j++) if(w[j] == ')') break;\n\t\t\t\tif(j >= lenW || j == i) goto ge;\n\t\t\t\tsplit = w + i; splitLen = j - i;\n\t\t\t\ti = j;\n\t\t\t\tbreak;\n\t\t\tdefault: goto ge;\n\t\t\t}\n\t\t}\n\tge:\n\t\tif(errStr)* errStr = SysAllocString(es);\n\t\treturn false;\n\tg1:\n\t\tswitch(_type) {\n\t\tcase WildType::RegexPcre: {\n\t\t\t_regex = pcre::Compile(w, lenW, _ignoreCase ? PCRE2_CASELESS : 0, out errStr);\n\t\t\tif(_regex == null) return false;\n\t\t} goto gr;\n\t\tcase WildType::Multi: {\n\t\t\tint count = 1; auto eos = w + lenW; auto splitChar = split[0];\n\t\t\tfor(auto t = w; t <= eos - splitLen; ) { //calc part count\n\t\t\t\tif(t[0] == splitChar && 0 == wcsncmp(t, split, splitLen)) { count++; t += splitLen; } else t++;\n\t\t\t}\n\t\t\t_multi_array = new Wildex[_multi_count = count];\n\n\t\t\tfor(int i = 0; i < count; i++, w += splitLen) {\n\t\t\t\tSTR wi = w;\n\t\t\t\tif(i == count - 1) w = eos;\n\t\t\t\telse { //find next splitter\n\t\t\t\t\tfor(; w <= eos - splitLen; w++) if(w[0] == splitChar && 0 == wcsncmp(w, split, splitLen)) break;\n\t\t\t\t}\n\t\t\t\tif(!_multi_array[i].Parse(wi, w - wi, true, out errStr)) return false;\n\t\t\t}\n\t\t} goto gr;\n\t\t}\n\t}\n\n\tif(_type == WildType::Wildcard && !HasWildcards(w, lenW)) _type = WildType::Text;\n\tif(dontCopyString) _text = (LPWSTR)w;\n\telse {\n\t\t_text = (LPWSTR)malloc((lenW + 1) * 2); memcpy(_text, w, lenW * 2); _text[lenW] = 0;\n\t\t_freeText = true;\n\t}\n\t_text_length = (int)lenW;\ngr:\n\treturn true;\n}\n\nbool Wildex::Match(STR s, size_t lenS) const\n{\n\tif(s == null) return false;\n\n\tbool R = false;\n\tswitch(_type) {\n\tcase WildType::Wildcard:\n\t\tR = Like(s, lenS, _text, _text_length, _ignoreCase);\n\t\tbreak;\n\tcase WildType::Text:\n\t\tR = Equals(s, lenS, _text, _text_length, _ignoreCase);\n\t\tbreak;\n\tcase WildType::RegexPcre:\n\t\tR = pcre::Match(_regex, s, lenS);\n\t\tbreak;\n\tcase WildType::Multi:\n\t\t//[n] parts: all must match (with their option n applied)\n\t\tint nNot = 0;\n\t\tfor(int i = 0; i < _multi_count; i++) {\n\t\t\tWildex& w = _multi_array[i];\n\t\t\tif(w._not) {\n\t\t\t\tif(!w.Match(s, lenS)) return _not; //!v->Match(s) means 'matches if without option n applied'\n\t\t\t\tnNot++;\n\t\t\t}\n\t\t}\n\t\tif(nNot == _multi_count) return !_not; //there are no parts without option n\n\n\t\t//non-[n] parts: at least one must match\n\t\tfor(int i = 0; i < _multi_count; i++) {\n\t\t\tWildex& w = _multi_array[i];\n\t\t\tif(!w._not && w.Match(s, lenS)) return !_not;\n\t\t}\n\t\tbreak;\n\t}\n\treturn R ^ _not;\n}\n\n/// <summary>\n/// Returns true if string contains wildcard characters: '*', '?'.\n/// </summary>\n/// <param name=\"s\">Can be null.</param>\n/*static*/ bool Wildex::HasWildcards(STR s, size_t lenS)\n{\n\tif(s == null) return false;\n\tfor(size_t i = 0; i < lenS; i++) {\n\t\tauto c = s[i];\n\t\tif(c == '*' || c == '?') goto yes;\n\t}\n\treturn false; yes: return true;\n}\n\n//EXPORT bool Cpp_WildexParse(Wildex& x, STR w, size_t lenW, out BSTR* errStr) {\n//\treturn x.Parse(w, lenW, false, errStr);\n//}\n//\n//EXPORT bool Cpp_WildexMatch(Wildex& x, STR s, size_t lenS) {\n//\treturn x.Match(s, lenS);\n//}\n//\n//EXPORT void Cpp_WildexDtor(Wildex& x) {\n//\tx.~Wildex();\n//}\n\n#pragma endregion\n\n#pragma region Switch\n\n//Compares string s of length lenS with a list of '\\0'-terminated non-null strings, and returns 1-based index of the matched string, or 0 if none matches.\n//Example: int i=str::Switch(L\"two\", -1, { L\"one\", L\"two\", L\"three\" }); //2\n//If s==null, returns 0.\nint Switch(STR s, size_t lenS, std::initializer_list<STR> a)\n{\n\tif(s == null) return 0;\n\tint i = 1;\n\tfor(const STR* p = a.begin(); p < a.end(); i++) {\n\t\tSTR t = *p++;\n\t\tfor(size_t j = 0; j < lenS; j++) {\n\t\t\tWCHAR c = t[j];\n\t\t\tif(c != s[j] || c == 0) goto g1;\n\t\t}\n\t\tif(t[lenS] == 0) return i;\n\tg1:;\n\t}\n\treturn 0;\n}\n\n//Compares '\\0'-terminated string s with a list of '\\0'-terminated non-null strings, and returns 1-based index of the matched string, or 0 if none matches.\n//Example: int i=str::Switch(L\"two\", { L\"one\", L\"two\", L\"three\" }); //2\n//If s==null, returns 0.\nint Switch(STR s, std::initializer_list<STR> a)\n{\n\tif(s == null) return 0;\n\tint i = 1;\n\tfor(const STR* p = a.begin(); p < a.end(); i++) {\n\t\tSTR t = *p++;\n\t\tfor(STR ss = s; ; ) {\n\t\t\tWCHAR c = *t++;\n\t\t\tif(c != *ss++) goto g1;\n\t\t\tif(c == 0) break;\n\t\t}\n\t\treturn i;\n\tg1:;\n\t}\n\treturn 0;\n}\n\n#pragma endregion\n\n} //namespace str\n"
  },
  {
    "path": "Cpp/str.h",
    "content": "#pragma once\n#include \"stdafx.h\"\n\n#define CMP2(s, c)\t((s)[0]==c[0] && (s)[1]==c[1])\n#define CMP3(s, c)\t(CMP2(s, c) && (s)[2]==c[2])\n#define CMP4(s, c)\t(CMP3(s, c) && (s)[3]==c[3])\n#define CMP5(s, c)\t(CMP4(s, c) && (s)[4]==c[4])\n#define CMP6(s, c)\t(CMP5(s, c) && (s)[5]==c[5])\n\n//Calls _wcstoi64 and casts to int.\n//Use instead of wcstol to avoid its overflow problem. Eg for \"0xFFFFFFFF\" it returns INT_MAX; this func returns -1.\ninline int strtoi(STR s, LPWSTR* end = nullptr, int radix = 0) {\n\treturn (int)_wcstoi64(s, end, radix);\n}\n\n//Fast memory buffer.\n//Depending on required size, uses memory in fixed-size memory in this variable (eg on stack) or allocates from heap.\n//Does not call ctor/dtor.\ntemplate <class T, size_t nElemOnStack = 1000>\nclass Buffer {\n\tstatic const size_t c_nStackBytes = nElemOnStack * sizeof(T);\n\n\tT* _p;\n\tBYTE _onStack[c_nStackBytes];\npublic:\n\texplicit Buffer() noexcept { _p = (T*)_onStack; } //later call Init, unless need <= elements than nElemOnStack\n\n\t__forceinline explicit Buffer(size_t nElem) noexcept { _Init(nElem); }\n\n\t~Buffer() { FreeHeapMemory(); }\n\n\t//Frees old memory and allocates new. Does not preserve.\n\tT* Alloc(size_t nElem) { FreeHeapMemory(); return _Init(nElem); }\n\n\t//Frees old memory, allocates new and calls memset(0). Does not preserve.\n\tT* AllocAndZero(size_t nElem) { FreeHeapMemory(); return _InitZ(nElem); }\n\n\t//Reallocates memory in the grow direction, preserving existing data.\n\tT* Realloc(size_t nElem) { if (nElem > nElemOnStack) _Realloc(nElem * sizeof(T)); return _p; }\n\n\t//Frees heap memory. Called by dtor.\n\t__declspec(noinline)\n\t\tvoid FreeHeapMemory() {\n\t\tif ((LPBYTE)_p != _onStack) {\n\t\t\tfree(_p);\n\t\t\t_p = (T*)_onStack;\n\t\t}\n\t}\n\n\toperator T* () { return _p; }\n\n\tsize_t Capacity() { return (LPBYTE)_p == _onStack ? nElemOnStack : _msize(_p) * sizeof(T); }\n\nprivate:\n\t__forceinline T* _Init(size_t nElem) { return (T*)_Init(nElem * sizeof(T), c_nStackBytes); }\n\n\tT* _InitZ(size_t nElem) { return (T*)memset(_Init(nElem), 0, nElem * sizeof(T)); }\n\n\t__declspec(noinline)\n\t\tvoid* _Init(size_t requiredSize, size_t stackSize) {\n\t\t_p = (T*)_onStack;\n\t\tif (requiredSize > stackSize) _p = (T*)malloc(requiredSize);\n\t\treturn _p;\n\t}\n\n\t__declspec(noinline)\n\t\tvoid _Realloc(size_t requiredSize) {\n\t\tif ((LPBYTE)_p != _onStack) {\n\t\t\t_p = (T*)realloc(_p, requiredSize);\n\t\t} else {\n\t\t\t_p = (T*)malloc(requiredSize);\n\t\t\tmemcpy(_p, _onStack, c_nStackBytes);\n\t\t}\n\t}\n\n\t//tested: the __declspec(noinline) functions are added once for all T, making template instance code very small.\n};\n\nnamespace str {\n\t//null-safe wcslen.\n\tinline size_t Len(STR s) { return s ? wcslen(s) : 0; }\n\n\tinline bool IsEmpty(STR s) { return s == null || *s == 0; }\n\n\tstruct SBBuffer { LPWSTR p; int n; };\n\n\t//Formats string by appending strings and numbers to an internal buffer (Buffer<WCHAR, 1000>.\n\t//Can be used instead of std::wstringstream which adds ~130 KB to the dll size.\n\tclass StringBuilder {\n\t\tstatic const size_t c_bufferSize = 1000;\n\t\tsize_t _len, _all;\n\t\tBuffer<WCHAR, c_bufferSize> _b;\n\n\t\tvoid _ReallocIfNeed(size_t lenAppend) {\n\t\t\tauto n = _len + lenAppend;\n\t\t\tif (n >= _all) {\n\t\t\t\tn += _all;\n\t\t\t\t_b.Realloc(n);\n\t\t\t\t_all = n;\n\t\t\t}\n\t\t}\n\tpublic:\n\t\tStringBuilder() {\n\t\t\t_len = 0;\n\t\t\t_all = c_bufferSize;\n\t\t\t_b[0] = 0;\n\t\t}\n\n\t\tvoid Clear() {\n\t\t\t_b.FreeHeapMemory();\n\t\t\t_len = 0;\n\t\t\t_all = c_bufferSize;\n\t\t\t_b[0] = 0;\n\t\t}\n\n\t\toperator LPWSTR() {\n\t\t\treturn _b;\n\t\t}\n\n\t\tint Length() {\n\t\t\treturn (int)_len;\n\t\t}\n\n\t\tvoid SetLength(int len) {\n\t\t\t_len = (size_t)len;\n\t\t}\n\n\t\tBSTR ToBSTR() {\n\t\t\treturn SysAllocStringLen(_b, (UINT)_len);\n\t\t}\n\n\t\tvoid Append(STR s, size_t lenS) {\n\t\t\tif (lenS > 0) {\n\t\t\t\t_ReallocIfNeed(lenS);\n\t\t\t\tmemcpy(_b + _len, s, lenS * 2);\n\t\t\t\tauto n = _len + lenS;\n\t\t\t\t_b[n] = 0;\n\t\t\t\t_len = n;\n\t\t\t}\n\t\t}\n\n\t\tvoid Append(STR s) { Append(s, Len(s)); }\n\n\t\tfriend StringBuilder& operator<<(StringBuilder& b, STR s) {\n\t\t\tb.Append(s);\n\t\t\treturn b;\n\t\t}\n\n\t\tvoid AppendBSTR(BSTR s) { Append(s, SysStringLen(s)); }\n\t\t//note: cannot add Append and << overloads for BSTR because then compiler chooses them for LPWSTR etc.\n\n\t\tvoid Append(__int64 i, int radix = 10) {\n\t\t\t_ReallocIfNeed(20);\n\t\t\t_i64tow(i, _b + _len, radix);\n\t\t\tauto n = _len; while (_b[n]) n++;\n\t\t\t_b[n] = 0;\n\t\t\t_len = n;\n\t\t}\n\n\t\tfriend StringBuilder& operator<<(StringBuilder& b, __int64 i) {\n\t\t\tb.Append(i);\n\t\t\treturn b;\n\t\t}\n\n\t\tfriend StringBuilder& operator<<(StringBuilder& b, int i) {\n\t\t\tb.Append((__int64)i);\n\t\t\treturn b;\n\t\t}\n\n\t\tvoid AppendChar(WCHAR c, int count = 1) {\n\t\t\tif (count > 0) {\n\t\t\t\t_ReallocIfNeed(count);\n\t\t\t\tLPWSTR t = _b + _len;\n\t\t\t\twhile (--count >= 0) *t++ = c;\n\t\t\t\t*t = 0;\n\t\t\t\t_len = t - _b;\n\t\t\t}\n\t\t}\n\n\t\tfriend StringBuilder& operator<<(StringBuilder& b, WCHAR c) {\n\t\t\tb.AppendChar(c);\n\t\t\treturn b;\n\t\t}\n\n\t\tfriend StringBuilder& operator<<(StringBuilder& b, char c) {\n\t\t\tb.AppendChar((WCHAR)c);\n\t\t\treturn b;\n\t\t}\n\n\t\t//Gets buffer that can be passed to an API function that needs it.\n\t\t//The buffer is after the formatted string, so the API will append text, not replace.\n\t\t//After calling the API, call FixBuffer.\n\t\t//minSize - minimal buffer size you need. Default 500.\n\t\t//Returns: p - buffer pointer; n - buffer size (>=minSize).\n\t\tSBBuffer GetBufferToAppend(int minSize = 500) {\n\t\t\t_ReallocIfNeed(minSize + 1);\n\t\t\treturn { _b + _len, (int)(_all - _len - 1) };\n\t\t}\n\n\t\t//Sets correct length after calling GetBufferToAppend and an API function that writes to the buffer.\n\t\t//If appendLen<0, calls wcslen.\n\t\tvoid FixBuffer(int appendLen = -1) {\n\t\t\tauto n = _len + (appendLen < 0 ? wcslen(_b + _len) : appendLen);\n\t\t\tassert(n < _all); if (n >= _all) n = _len;\n\t\t\t_b[n] = 0;\n\t\t\t_len = n;\n\t\t}\n\n\t\t//Returns true if the formatted string ends with s, case-insensitive.\n\t\tbool EndsI(STR s) {\n\t\t\tauto n = wcslen(s);\n\t\t\treturn _len >= n && 0 == _wcsnicmp(_b + _len - n, s, n);\n\t\t}\n\n\t\t//Decrements length by tailLength, and appends s.\n\t\tvoid ReplaceTail(int tailLength, STR s) {\n\t\t\t_len -= tailLength;\n\t\t\tAppend(s);\n\t\t}\n\t};\n\n\tnamespace pcre {\n\n\t\tBSTR GetErrorMessage(int code);\n\t\tpcre2_code_16* Compile(STR rx, size_t len, __int64 flags = 0, out BSTR* errStr = null);\n\t\tbool Match(pcre2_code_16* code, STR s, size_t len, size_t start = 0, UINT flags = 0);\n\n\t\t//If code is not null, calls pcre2_code_free_16.\n\t\tstatic void Free(pcre2_code_16* code) {\n\t\t\tif (code != null) pcre2_code_free_16(code);\n\t\t}\n\n\t};\n\n\n\t//Wildcard expression.\n\t//More info in the C# version and help file.\n\tclass Wildex {\n\t\tWildex(Wildex&& x) = delete; //disable copying\n\tpublic:\n\t\t/// <summary>\n\t\t/// The type of text (wildcard expression) used when creating the Wildex variable.\n\t\t/// </summary>\n\t\tenum class WildType :byte {\n\t\t\t/// Simple text (option t, or no *? characters and no t r options).\n\t\t\tText,\n\n\t\t\t/// Wildcard (has *? characters and no t r options).\n\t\t\t/// Match() calls str::Like.\n\t\t\tWildcard,\n\n\t\t\t/// PCRE regular expression (option r).\n\t\t\tRegexPcre,\n\n\t\t\t/// Multiple parts (option m).\n\t\t\t/// Match() calls Match() for each part and returns true if all negative (option n) parts return true (or there are no such parts) and some positive (no option n) part returns true (or there are no such parts).\n\t\t\tMulti,\n\t\t};\n\n\tprivate:\n\t\tunion {\n\t\t\tLPWSTR _text;\n\t\t\tpcre2_code_16* _regex;\n\t\t\tWildex* _multi_array;\n\t\t};\n\t\tunion {\n\t\t\tint _text_length;\n\t\t\tint _multi_count;\n\t\t};\n\t\tWildType _type;\n\t\tbool _ignoreCase;\n\t\tbool _not;\n\t\tbool _freeText;\n\n\tpublic:\n\t\tWildex() { ZEROTHIS; }\n\t\t~Wildex();\n\t\tbool Parse(STR w, size_t lenW, bool dontCopyString = false, out BSTR* errStr = null);\n\t\tbool Match(STR s, size_t lenS) const;\n\t\t//Returns true if not null.\n\t\tbool Is() const { return _text != null; }\n\n\t\tstatic bool HasWildcards(STR s, size_t lenS);\n\t};\n\n\tEXPORT STR Cpp_LowercaseTable();\n\tbool Like(STR s, size_t lenS, STR w, size_t lenW, bool ignoreCase = false);\n\tbool Equals(STR w, size_t lenW, STR s, size_t lenS, bool ignoreCase = false);\n\n\tint Switch(STR s, size_t lenS, std::initializer_list<STR> a);\n\tint Switch(STR s, std::initializer_list<STR> a);\n\n\n} //namespace str\n\n\nclass Bstr : public CComBSTR {\npublic:\n\tusing CComBSTR::CComBSTR; //inherit ctors\n\n\t//Calls Attach(SysAllocStringLen(s, len)) and returns m_str.\n\tBSTR Assign(STR s, int len) {\n\t\tAttach(SysAllocStringLen(s, len));\n\t\treturn m_str;\n\t}\n\n\tbool Equals(STR s, int lenS, bool ignoreCase) {\n\t\tif (m_str == null) return s == null;\n\t\treturn str::Equals(m_str, Length(), s, lenS);\n\t}\n\n\tbool Equals(STR s, bool ignoreCase) {\n\t\tif (m_str == null) return s == null;\n\t\treturn str::Equals(m_str, Length(), s, str::Len(s));\n\t}\n\n\tbool Equals(BSTR s, bool ignoreCase) {\n\t\tif (m_str == null) return s == null;\n\t\treturn str::Equals(m_str, Length(), s, SysStringLen(s));\n\t}\n};\n\nstruct BstrNameValue {\n\tBstr name;\n\tBstr value;\n};\n"
  },
  {
    "path": "Cpp/test Uia.cpp",
    "content": "#include \"stdafx.h\"\n#include \"cpp.h\"\n\nbool ElementFromWindow(IUIAutomation* f, HWND w, Smart<IUIAutomationElement>& e)\n{\n\tif(f->ElementFromHandle(w, &e)) return false;\n\treturn true;\n}\n\nclass UiaFinder\n{\n\tSmart<IUIAutomation> _f;\n\tBstr _name;\n\tSmart<IUIAutomationCondition> _condRaw;\npublic:\n\tSmart<IUIAutomationElement> eFound;\n\n\tUiaFinder() {\n\t\tif(_f.CoCreateInstance(__uuidof(CUIAutomation8), null, CLSCTX_INPROC_SERVER)) return;\n\t\tif(_f->get_RawViewCondition(&_condRaw)) return;\n\t}\n\n\tint Find(HWND w, STR name) {\n\t\tif(!_f) return 1;\n\n\t\tSmart<IUIAutomationElement> ew;\n\t\tif(!ElementFromWindow(_f, w, ew)) return 2;\n\n\t\treturn Find(ew, name);\n\t}\n\n\t//int Find(IUIAutomationElement* ew, STR name) {\n\n\t//\tIUIAutomationCondition> cond;\n\t//\tif (_f->CreatePropertyCondition(UIA_NamePropertyId, _variant_t(name), &cond)) return 3;\n\n\t//\tif (ew->FindFirst(TreeScope_Descendants, cond, &eFound)) return 4;\n\n\t//\treturn 0;\n\t//}\n\n\tint Find(IUIAutomationElement* ew, STR name) {\n\t\tif(!_f) return 1;\n\t\t_name = name;\n\n\t\treturn _Find(ew, 0);\n\t}\n\nprivate:\n\tint _Find(IUIAutomationElement* ew, int level) {\n\n\t\tSmart<IUIAutomationElementArray> a;\n\t\tif(ew->FindAll(TreeScope_Children, _condRaw, &a)) return 4;\n\n\t\tint n = 0;\n\t\tif(a->get_Length(&n)) return 5;\n\n\t\tfor(int i = 0; i < n; i++) {\n\t\t\tSmart<IUIAutomationElement> e;\n\t\t\tif(a->GetElement(i, &e)) return 6;\n\n\t\t\tBstr b;\n\t\t\tif(0 == e->get_CurrentName(&b)) {\n\t\t\t\t//Printf(L\"%i %s\", level, (STR)b);\n\t\t\t\tif(b == _name) {\n\t\t\t\t\teFound.Attach(e.Detach());\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tint k = _Find(e, level + 1);\n\t\t\tif(k) return k;\n\t\t\tif(eFound) break;\n\t\t}\n\n\t\treturn 0;\n\t}\n};\n\n#if true\nLRESULT TestUia(HWND w)\n{\n\tUiaFinder x;\n\tint err = x.Find(w, L\"Untitled\");\n\tif(err) return err;\n\n\tif(x.eFound) {\n\t\tBstr b;\n\t\tif(x.eFound->get_CurrentName(&b)) return 50;\n\t\tPrint(b);\n\t} else Print(L\"not found\");\n\n\treturn 0;\n}\n#else\nLRESULT TestUia(HWND w)\n{\n\t//Print(L\"TestUia\");\n\n\t//_TODO: CoInitialize if need.\n\n\t//Smart<IUIAutomation> f(__uuidof(CUIAutomation8), null, CLSCTX_INPROC_SERVER);\n\tSmart<IUIAutomation> f;\n\tif(f.CreateInstance(__uuidof(CUIAutomation8), null, CLSCTX_INPROC_SERVER)) return 1;\n\t//Print((__int64)f.p);\n\n\tSmart<IUIAutomationElement> ew;\n\tif(!ElementFromWindow(f, w, ew)) return 2;\n\n\tSmart<IUIAutomationCondition> cond;\n\tif(f->CreatePropertyCondition(UIA_NamePropertyId, _variant_t(L\"Untitled\"), &cond)) return 3;\n\n\tSmart<IUIAutomationElement> e;\n\tif(ew->FindFirst(TreeScope_Descendants, cond, &e)) return 4;\n\tif(!e) return 100;\n\n\t_bstr_t b;\n\tif(e->get_CurrentName(&b.GetBSTR())) return 5;\n\n\tPrint(b);\n\n\treturn 0;\n}\n#endif\n"
  },
  {
    "path": "Cpp/test.cpp",
    "content": "#include \"stdafx.h\"\n#include \"cpp.h\"\n//#include \"ISimpleDOMNode.h\"\n\n//#include <sphelper.h>\n//#include <taskschd.h>\n\n#if _DEBUG\n\n//#if 1\n\n__interface __declspec(uuid(\"3AB5235E-2768-47A2-909A-B5852A9D1868\"))\n\tIInterface : IUnknown {\n\tint __stdcall Add(int a, int b);\n\tHRESULT __stdcall put_Prop(int r);\n\tHRESULT __stdcall get_Prop(int* r);\n\tHRESULT __stdcall put_Prop2(IUnknown* r);\n\tHRESULT __stdcall get_Prop2(IUnknown** r);\n};\n\nclass Inter :public IInterface {\npublic:\n\tint __stdcall Add(int a, int b) {\n\t\treturn a + b;\n\t}\n\n\t// Inherited via IInterface\n\tvirtual HRESULT __stdcall QueryInterface(REFIID riid, void** ppvObject) override {\n\t\tif (riid == __uuidof(IInterface) || riid == __uuidof(IUnknown)) {\n\t\t\tPrintf(L\"QueryInterface(%s)\", riid == __uuidof(IInterface) ? L\"IInterface\" : L\"IUnknown\");\n\t\t\t*ppvObject = this;\n\t\t\treturn 0;\n\t\t}\n\t\t*ppvObject = nullptr;\n\t\treturn E_NOINTERFACE;\n\t}\n\tvirtual ULONG __stdcall AddRef(void) override {\n\t\tPrint(L\"AddRef\");\n\t\treturn 1;\n\t}\n\tvirtual ULONG __stdcall Release(void) override {\n\t\tPrint(L\"Release\");\n\t\treturn 1;\n\t}\n\n\tHRESULT __stdcall put_Prop(int r) { Print(r); return 0; }\n\tHRESULT __stdcall get_Prop(int* r) { *r = 3; return 0; }\n\n\tHRESULT __stdcall put_Prop2(IUnknown* r) { Print(r->AddRef()); return 0; }\n\tHRESULT __stdcall get_Prop2(IUnknown** r) { *r = this; return 0; }\n};\n\nEXPORT IInterface* Cpp_GetInterface() { return new Inter(); }\n\n\n\n\n\n\nLRESULT CALLBACK KeyHookProc(int code, WPARAM wParam, LPARAM lParam) {\n\tif (code < 0) goto g1;\n\tif (wParam == 0x8F) {\n\t\t//Print(L\"<hook>\");\n\t\tHANDLE ev = OpenEventW(EVENT_MODIFY_STATE, false, L\"ee57812345hh\");\n\t\tif (ev == 0) {\n\t\t\tPRINTS(L\"key sync hook: OpenEventW failed\");\n\t\t\tgoto g1;\n\t\t}\n\t\tSetEvent(ev);\n\t\tCloseHandle(ev);\n\t}\ng1:\n\treturn CallNextHookEx(0, code, wParam, lParam);\n}\n\nEXPORT HHOOK Cpp_KeyboardHook(int action, int tid, HHOOK hh) {\n\tif (action == 1) {\n\t\tauto hh = SetWindowsHookExW(WH_KEYBOARD, KeyHookProc, s_moduleHandle, tid);\n\t\tif (hh == 0 && tid != 0) hh = SetWindowsHookExW(WH_KEYBOARD, KeyHookProc, s_moduleHandle, 0); //console. GetWindowThreadProcessId lies. To get real id probably need to enumerate threads and call EnumThreadWindows for each. Too slow.\n\t\treturn hh;\n\t} else if (action == 2) {\n\t\tUnhookWindowsHookEx(hh);\n\t}\n\treturn 0;\n}\n//\n//LRESULT CALLBACK ForegroundIdleProc(int code, WPARAM wParam, LPARAM lParam)\n//{\n//\t//Print(code);\n//\tif(code < 0) goto g1;\n//\tHANDLE ev = OpenEventW(EVENT_MODIFY_STATE, false, L\"ee57812345hh\");\n//\tif(ev == 0) {\n//\t\tPrint(L\"failed\");\n//\t\tgoto g1;\n//\t}\n//\tSetEvent(ev);\n//\tCloseHandle(ev);\n//\n//\tg1:\n//\treturn CallNextHookEx(0, code, wParam, lParam);\n//}\n//\n//EXPORT HHOOK Cpp_InputSync(int action, int tid, HHOOK hh)\n//{\n//\tif(action == 1) {\n//\t\treturn SetWindowsHookExW(WH_FOREGROUNDIDLE, ForegroundIdleProc, GetCurrentModuleHandle(), 0);\n//\t} else if(action == 2) {\n//\t\tUnhookWindowsHookEx(hh);\n//\t}\n//\treturn 0;\n//}\n\n//#define IID_IThreadExitEvent __uuidof(IThreadExitEvent)\n//__interface  __declspec(uuid(\"57017F56-E7CA-4A7B-A8F8-2AE36077F50D\"))\n//\tIThreadExitEvent :IUnknown\n//{\n//\tSTDMETHODIMP Unsubscribe();\n//};\n//\n//class ThreadExitEvent :public IThreadExitEvent\n//{\n//\tlong _cRef;\n//\tFARPROC _callback;\n//\tSTD_IUNKNOWN_METHODS(IThreadExitEvent)\n//public:\n//\tThreadExitEvent(FARPROC callback) {\n//\t\t_cRef = 1;\n//\t\t_callback = callback;\n//\t}\n//\n//\t~ThreadExitEvent() {\n//\t\tif(_callback == null) return;\n//\t\tPrint(GetCurrentThreadId());\n//\t\t_callback();\n//\t}\n//\n//\tSTDMETHODIMP Unsubscribe() {\n//\t\t_callback = null;\n//\t\treturn 0;\n//\t}\n//};\n//\n//EXPORT\n//IThreadExitEvent* Cpp_ThreadExitEvent(FARPROC callback)\n//{\n//\treturn new ThreadExitEvent(callback);\n//}\n//\n//FARPROC s_callback;\n//\n//EXPORT\n//void Cpp_ThreadExitEvent2(FARPROC callback)\n//{\n//\ts_callback = callback;\n//}\n//\n//void OnThreadExit()\n//{\n//\tPrint(GetCurrentThreadId());\n//\tif(s_callback) s_callback();\n//}\n\n\n//#include <atlstr.h>\n//#include <atlfile.h>\n\nextern HMODULE s_moduleHandle;\n\n//void TestStringBuilder() {\n//\tPerf.First();\n//\tstr::StringBuilder b;\n//\t//b.Append(L\"one \");\n//\t//b.Append(50);\n//\t//b.Append(L\" four\", 2);\n//\t//b.Append(L\" 0x\");\n//\t//b.Append(10, 16);\n//\t//b.AppendChar(' ');\n//\t//b.AppendChar('A');\n//\t//b.AppendChar(' ', 4);\n//\t//b.AppendChar('A');\n//\n//\tHWND ww = 0;\n//\n//\tstatic const STR rundll = L\"\\\\SysWOW64\\\\rundll32.exe \\\"\";\n//\tstatic const STR bits = L\"32\";\n//\n//\tb << L\"kkk\" << L' ' << 5 << ',';\n//\tb.AppendChar(' ');\n//\tb.AppendChar(L' ');\n//\n//\tLPWSTR t; int n;\n//\tt = b.GetBufferToAppend(out n); b.FixBuffer(GetWindowsDirectoryW(t, n));\n//\tb << rundll;\n//\tt = b.GetBufferToAppend(out n); b.FixBuffer(GetModuleFileNameW(s_moduleHandle, t, n));\n//\tauto u = wcsrchr(t, '\\\\') - 5; u[0] = bits[0]; u[1] = bits[1]; //\"32\" to \"64\" or vice versa\n//\tif (GetFileAttributes(t) == INVALID_FILE_ATTRIBUTES) return; //avoid messagebox when our antimatter dll does not exist\n//\tb << L\"\\\",Cpp_RunDll \" << (__int64)ww; //note: without W\n//\n//\tb << 'A';\n//\tb.AppendChar('.', 4);\n//\n//\tPerf.Next();\n//\tPrint(b);\n//\tPerf.NW();\n//}\n\nEXPORT void Cpp_TestWildex(STR s, STR w) {\n\tauto lenS = wcslen(s);\n\tauto lenW = wcslen(w);\n\n\tPerf.First();\n\tstr::Wildex x;\n\tBstr es;\n\tif (!x.Parse(w, lenW, false, &es)) { Print(es); return; }\n\tPerf.Next();\n\n\tbool yes;\n\tfor (int i = 0; i < 1000; i++) yes = x.Match(s, lenS);\n\tPerf.NW();\n\n\tPrint(yes);\n}\n\nEXPORT void Cpp_TestPCRE(STR s, STR p, DWORD flags) {\n\tint rc = 0;\n\n\tPerf.First();\n\tint errNum; size_t errOffs;\n\tauto re = pcre2_compile(p, -1, flags | PCRE2_UTF, &errNum, &errOffs, null);\n\tif (re == null) {\n\t\tPCRE2_UCHAR buffer[256];\n\t\tpcre2_get_error_message(errNum, buffer, _countof(buffer));\n\t\tPrintf(L\"error %s at %i\", buffer, errOffs);\n\t\treturn;\n\t}\n\n#if false\n\tPerf.Next();\n\tLPBYTE ser = null; size_t serSize = 0;\n\tpcre2_code* a[2] = { re,re };\n\trc = pcre2_serialize_encode((const pcre2_code_16**)a, 2, &ser, &serSize, null);\n\tif (rc <= 0) {\n\t\tPrint(L\"pcre2_serialize_encode error\");\n\t\treturn;\n\t}\n\tpcre2_code_free(re); re = null;\n\tPerf.Next();\n\n\tPrint(serSize);\n\n\trc = pcre2_serialize_decode(&re, 1, ser, null);\n\tif (rc <= 0) {\n\t\tPrint(L\"pcre2_serialize_decode error\");\n\t\treturn;\n\t}\n\n\tpcre2_serialize_free(ser);\n#endif\n\n\tPerf.Next();\n\tauto m = pcre2_match_data_create_from_pattern(re, null);\n\tauto len = wcslen(s);\n\n\tfor (int i = 0; i < 1000; i++) {\n\t\trc = pcre2_match(re, s, len, 0, 0, m, null, null);\n\t\tif (rc <= 0) {\n\t\t\tPrint(rc);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tPerf.Next();\n\n\t//auto v = pcre2_get_ovector_pointer(m);\n\t//for(int i = 0; i < rc; i++) {\n\t//\tPCRE2_SPTR substring_start = s + v[2 * i];\n\t//\tsize_t substring_length = v[2 * i + 1] - v[2 * i];\n\t//\tPrintf(L\"%i: %.*s\", i, (int)substring_length, (char *)substring_start);\n\t//}\n\n\tpcre2_match_data_free(m);\n\tpcre2_code_free(re);\n\tPerf.NW();\n}\n\n\n//class TestTL {\n//public:\n//\tTestTL() {\n//\t\tPrintf(L\"+ %p %i\", this, GetCurrentThreadId());\n//\t}\n//\n//\t~TestTL() {\n//\t\tPrintf(L\"- %p %i\", this, GetCurrentThreadId());\n//\t}\n//};\n//\n//void Voo(LPWSTR s) {\n//\t//std::wstring k(s);\n//\t//Print(k.c_str() == null);\n//\tCString g(s);\n//\t//Print(g.GetString() == null);\n//\t//Print((STR)g == null);\n//}\n//\n//enum\n//\t//class\n//\teTest {\n//\tOne = 1,\n//\tTwo = 2,\n//\tFour = 4,\n//};\n//ENABLE_BITMASK_OPERATORS(eTest);\n//\n//#include <vector>\n//#include <array>\n//\n//STR s_testSTR = L\"te\";\n//\n//struct eKKK {\n//\tenum {\n//\t\tOne = 1,\n//\t\tTwo = 2,\n//\t\tFour = 4,\n//\t};\n//\n//\t//static const int One = 1;\n//\t//static const int Two = 2;\n//\t//static const int Four = 4;\n//};\n//\n//enum class eKKK {\n//\tOne = 1,\n//\tTwo = 2,\n//\tFour = 4,\n//\tEight = 8,\n//};\n//ENABLE_BITMASK_OPERATORS(eKKK);\n\nEXPORT\nint Cpp_TestInt(int a) {\n\treturn sizeof(VARIANT);\n}\n\nEXPORT\nint Cpp_TestString(STR a) {\n\treturn 1;\n}\n\n\n#define IID_ICppTest __uuidof(ICppTest)\n__interface  __declspec(uuid(\"3426CF3C-F7C2-4322-A292-463DB8729B54\"))\n\tICppTest :IUnknown {\n\tSTDMETHODIMP TestInt(int a, int b, int c);\n\tSTDMETHODIMP TestString(STR a, int b, int c);\n\tSTDMETHODIMP TestBSTR(BSTR a, int b, int c);\n};\n\nclass CppTest :public ICppTest {\n\tSTD_IUNKNOWN_METHODS_SIMPLE(ICppTest)\npublic:\n\tSTDMETHODIMP TestInt(int a, int b, int c) {\n\t\treturn E_NOTIMPL;\n\t}\n\tSTDMETHODIMP TestString(STR a, int b, int c) {\n\t\treturn E_NOTIMPL;\n\t}\n\tSTDMETHODIMP TestBSTR(BSTR a, int b, int c) {\n\t\treturn E_NOTIMPL;\n\t}\n};\n\nEXPORT\nICppTest* Cpp_Interface() {\n\treturn new CppTest();\n}\n\n//#include <OCIdl.h>\n//#include <DispEx.h>\n//\n//\n//EXPORT\n//void TestSimple()\n//{\n//\t//throw 5;\n//\tint* p = 0;\n//\tPrint(*p);\n//}\n//\n//EXPORT\n//void TestUnmanaged(int& k, int& g)\n//{\n////MessageBox(0, L\"\", L\"bb\", 0);\n//}\n//\n//EXPORT\n//void TestStructBlit(POINT& p)\n//{\n//\tPrint(p.x);\n//\tp.y = 8;\n//}\n//\n//struct STSTR\n//{\n//\tint k;\n//\tLPWSTR s;\n//};\n//\n//EXPORT\n//void TestStructString(STSTR& p)\n//{\n//\tPrint(p.s);\n//\tPrint(p.k);\n//\t//p.s = L\"retstr\";\n//\t//p.s = SysAllocString(L\"retstr\");\n//\t//p.s = (LPWSTR)CoTaskMemAlloc(10); CopyMemory(p.s, L\"retstr\", sizeof(L\"retstr\"));\n//\tp.s[0] = 'A';\n//\tp.k = 8;\n//\tPrint(L\"returning\");\n//}\n//\n//EXPORT\n//void TestArray(int* p)\n//{\n//\tPrint(p[0]);\n//\tp[0] = 5;\n//}\n//\n//EXPORT\n//void TestArrayStr(LPWSTR* p)\n//{\n//\tPrint(p[0]);\n//\t*p[0] = 'A';\n//}\n//\n//__interface __declspec(uuid(\"3AB5235E-2768-47A2-909A-B5852A9D1868\"))\n//\tITest :IUnknown\n//{\n//\tHRESULT STDMETHODCALLTYPE Test1(int i);\n//\t//HRESULT STDMETHODCALLTYPE Test2(int* p);\n//\tHRESULT STDMETHODCALLTYPE TestOL(int* p);\n//\tHRESULT STDMETHODCALLTYPE TestNext(char* p);\n//};\n//\n//class CTest :public ITest\n//{\n//\tint _refCount;\n//\n//public:\n//\tCTest() {\n//\t\t_refCount = 1;\n//\t}\n//\n//\t~CTest() {\n//\t\tPrintf(L\"dtor thread: %i\", GetCurrentThreadId());\n//\t\t//Print(L\"dtor\");\n//\t}\n//\n//\tHRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) {\n//\t\t//wchar_t* name = L\"?\"; //info: QM2 has DebugGetInterfaceName\n//\t\t//if (riid == __uuidof(ITest)) name = L\"ITest\"; //called\n//\t\t//else if (riid == __uuidof(IUnknown)) name = L\"IUnknown\"; //called\n//\t\t//else if (riid == __uuidof(IProvideClassInfo)) name = L\"IProvideClassInfo\"; //called\n//\t\t//else if (riid == __uuidof(IDispatch)) name = L\"IDispatch\";\n//\t\t//else if (riid == __uuidof(IDispatchEx)) name = L\"IDispatchEx\";\n//\t\t//else if (riid == __uuidof(IErrorInfo)) name = L\"IErrorInfo\";\n//\t\t//else if (riid == __uuidof(IEnumVARIANT)) name = L\"IEnumVARIANT\";\n//\t\t//else if (riid == __uuidof(IConnectionPoint)) name = L\"IConnectionPoint\";\n//\t\t//else if (riid == __uuidof(IConnectionPointContainer)) name = L\"IConnectionPointContainer\";\n//\n//\t\t//Printf(L\"QueryInterface: %s\", name);\n//\n//\t\tif (riid != __uuidof(ITest) && riid != __uuidof(IUnknown)) {\n//\t\t\t*ppvObject = 0;\n//\t\t\treturn E_NOINTERFACE;\n//\t\t}\n//\t\t_refCount++;\n//\t\t*ppvObject = this;\n//\t\treturn 0;\n//\t}\n//\tULONG STDMETHODCALLTYPE AddRef() {\n//\t\t++_refCount;\n//\t\t//Printf(L\"AddRef, %i\", _refCount);\n//\t\treturn _refCount;\n//\t}\n//\tULONG STDMETHODCALLTYPE Release() {\n//\t\t_refCount--;\n//\t\t//Printf(L\"Release, %i\", _refCount);\n//\t\tif (_refCount == 0) delete this;\n//\t\treturn _refCount;\n//\t}\n//\n//\tHRESULT STDMETHODCALLTYPE Test1(int i)\n//\t{\n//\t\tPrint(L\"Test1\");\n//\t\treturn 0;\n//\t}\n//\n//\t//HRESULT STDMETHODCALLTYPE Test2(int* p)\n//\t//{\n//\t//\tPrint(p[0]);\n//\t//\treturn 0;\n//\t//}\n//\n//\tHRESULT STDMETHODCALLTYPE TestOL(int* p)\n//\t{\n//\t\tPrint(1);\n//\t\treturn 0;\n//\t}\n//\n//\tHRESULT STDMETHODCALLTYPE TestNext(char* p)\n//\t{\n//\t\tPrint(2);\n//\t\treturn 0;\n//\t}\n//};\n//\n//EXPORT\n//ITest* CreateTestInterface()\n//{\n//\treturn new CTest();\n//}\n//\n//EXPORT void CreateTestInterface2(ITest*& t)\n//{\n//\tt = new CTest();\n//}\n//\n//\n//\n//\n//\n//\n//#include <OleAuto.h>\n//#include <oleacc.h>\n//#pragma comment(lib, \"oleacc.lib\")\n//\n//\n//EXPORT\n//void DllTestAcc()\n//{\n//\tPOINT p = { 257, 1138 };\n//\tIAccessible* a = 0; VARIANT vc = {};\n//\tHRESULT hr = AccessibleObjectFromPoint(p, &a, &vc);\n//\tif (hr != 0) {\n//\t\tPrint((uint)hr); return;\n//\t}\n//\ttry {\n//\t\tBSTR s = 0;\n//\t\thr = a->get_accName(vc, &s);\n//\t\tif (hr != 0) {\n//\t\t\tPrint((uint)hr); return;\n//\t\t}\n//\n//\t\tPrint(s);\n//\t\t//Print((__int64)s);\n//\t\t//SysFreeString(s);\n//\n//\t}\n//\tcatch (...) {\n//\t\ta->Release();\n//\t\tPrint(L\"exception\");\n//\t}\n//}\n\nclass One {\npublic:\n\tint m;\n\tOne() {\n\t\tm = 1;\n\t}\n\tvirtual ~One() {\n\t\tPrintf(L\"~One, %i\", m);\n\t}\n};\n\nclass Two : public One {\npublic:\n\t~Two() {\n\t\tPrint(L\"~Two\");\n\t\tm = 0;\n\t}\n};\n\nclass Move {\npublic:\n\tint m;\n\n\tMove() {\n\t\tm = 1;\n\t}\n\n\t//~Move() {\n\t//\tPrintf(L\"~Move, %i\", m);\n\t//}\n\n\tMove(Move& x) {\n\t\tm = x.m;\n\t}\n\n\tMove(Move&& x) {\n\t\tm = x.m;\n\t\tx.m = 0;\n\t}\n\n\tMove& operator=(Move& x) {\n\t\tm = x.m;\n\t\treturn *this;\n\t}\n\n\tMove& operator=(Move&& x) {\n\t\tm = x.m;\n\t\tx.m = 0;\n\t\treturn *this;\n\t}\n};\n\nclass Move2 :public Move {\npublic:\n\tMove2() : Move() {}\n\n\t~Move2() {\n\t\tPrintf(L\"~Move2, %i\", m);\n\t}\n\n\tMove2(Move2& x) {\n\t\tm = x.m;\n\t}\n\n\tMove2(Move2&& x) {\n\t\tm = x.m;\n\t\tx.m = 0;\n\t}\n\n\tMove2& operator=(Move2& x) {\n\t\tm = x.m;\n\t\treturn *this;\n\t}\n\n\tMove2& operator=(Move2&& x) {\n\t\tMove::operator=(std::move(x));\n\t\treturn *this;\n\t}\n};\n\nvoid _TestIAccessibleImpl();\n\nvoid _test1(HWND w) {\n\tDWORD pid = 0;\n\tGetWindowThreadProcessId(w, &pid);\n\tPrint(Cpp_GetProcessArchitecture(pid));\n}\n\nEXPORT void Cpp_Test() {\n\t//_test1(FindWindow(L\"QM_Editor\", null));\n\t//_test1(FindWindow(null, L\"LibreAutomate\"));\n\t//_test1(FindWindow(L\"Shell_TrayWnd\", null));\n\t//_test1(FindWindow(L\"#32770\", L\"test\"));\n\n#if _M_ARM64EC\n\t\tSTR arch = L\"ARM64EC\";\n#elif _M_ARM64\n\t\tSTR arch = L\"ARM64\";\n#elif _M_X64\n\t\tSTR arch = L\"x64\";\n#else\n\t\tSTR arch = L\"x86\";\n#endif\n\tPrintf(L\"major=%i minor=%i min10=%i min11=%i is32=%i arch=%s\", osVer.major(), osVer.minor(), osVer.minWin10(), osVer.minWin11(), osVer.is32BitOS(), arch);\n\n\t//auto hh = SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, 0, _Wineventproc, GetCurrentProcessId(), 0, 0);\n\t//auto hh = SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, (HMODULE)&__ImageBase, _Wineventproc, GetCurrentProcessId(), 0, WINEVENT_INCONTEXT);\n\n\t//TestFileDialog();\n\n\n\t//_TestIAccessibleImpl();\n\t\t//CHandle hRead, hWrite;\n\t\t//if(!CreatePipe(&hRead.m_h, &hWrite.m_h, null, 0)) return;\n\t\t//Printf(L\"%p %p\", hRead.m_h, hWrite.m_h);\n\n\n\t\t//HWND w = FindWindowW(L\"QM_Editor\", null);\n\t\t//wn::PrintWnd(w);\n\t\t////Print(wn::ClassNameIs(w, L\"QM_Editor\"));\n\t\t////Print(wn::ClassNameIs(w, L\"QM_Edito\"));\n\t\t////Print(wn::ClassNameIs(w, L\"QM_*\"));\n\t\t////Print(wn::ClassNameIs(w, L\"-QM_*\"));\n\n\t\t//str::Wildex x;\n\t\t////STR s = L\"**k kkk\";\n\t\t////STR s = L\"**r (kkk\";\n\t\t//STR s = L\"QM_Editor\";\n\t\t//s=L\"**t QM_Editor\";\n\t\t////s=L\"**r .+r$\";\n\t\t//s=L\"**m moo||QM_Editor\";\n\t\t//s=L\"**n QM_Editor-\";\n\t\t//s=L\"**m *_Editor\";\n\t\t//s=L\"**m moo||**r .+r$\";\n\t\t//s=L\"**m moo||**r .+r$||**n *i*\";\n\t\t//Bstr es;\n\t\t//if(!x.Parse(s, wcslen(s), true, &es)) {\n\t\t//\tPrint(es); return;\n\t\t//}\n\t\t//Print(wn::ClassNameIs(w, x));\n\t\t//Print(x.Match(L\"QM_Editor\", 9));\n\n\t\t//str::StringBuilder t;\n\t\t////STR s = L\"TEST\";\n\t\t//LPWSTR s = L\"TEST\";\n\t\t//t.Append(s);\n\t\t//Print(t);\n\n\t\t//Move2 t1;\n\t\t//t1.m = 5;\n\n\t\t////Move2 t2 = t1;\n\t\t////Move2 t2; t2 = t1;\n\n\t\t////Move2 t2 = std::move(t1);\n\t\t//Move2 t2; t2 = std::move(t1);\n\n\t\t//Printf(L\"%i %i\", t1.m, t2.m);\n\n\t\t//str::StringBuilder b;\n\t\t////b.Append(L\"---\");\n\t\t////b.Append(L\"123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 \");\n\t\t//for(int i = 0; i < 1000; i++) {\n\t\t//\tb << i;\n\t\t//\tb << L\"    \";\n\t\t//}\n\t\t//\n\t\t//Print(b);\n\n\t\t//for(int i = 0; i < 5; i++) {\n\t\t//\tSleep(100);\n\t\t//\tPerf.First();\n\t\t//\tSmart<IStream> x;\n\t\t//\tCreateStreamOnHGlobal(0, true, &x);\n\t\t//\tfor(int j = 0; j < 100000; j++) if(x->Write(\"abcdefgh\", 8, null)) { Print(\"failed\"); return; }\n\n\t\t//\tPerf.NW();\n\n\t\t//\tDWORD size = 0; istream::GetSize(x, size); Print(size);\n\t\t//}\n\n\n\t\t//eKKK e = eKKK::One | eKKK::Two;\n\t\t//eKKK e = eKKK::One;\n\n\t\t//Print(!!e);\n\n\t\t////if(e&eKKK::One) Print(1);\n\t\t//if((bool)(e & eKKK::One)) Print(1);\n\t\t//if(ALL(e, eKKK::One)) Print(2);\n\t\t//if((bool)(e & (eKKK::One | eKKK::Four))) Print(3);\n\t\t////if(ANY(e, eKKK::One, eKKK::Four)) Print(4);\n\t\t//if(ANY(e, eKKK::One | eKKK::Four)) Print(4);\n\n\t\t//eAF2 f = eAF::eAF2::InWebPage| eAF::eAF2::Java;\n\n\t\t//Printf(L\"%*sa\", 4, L\"\");\n\n\t\t//HWND w = FindWindowW(L\"QM_Editor\", null);\n\t\t////int x = 7;\n\t\t////wn::EnumChildWindows(w, [](HWND c) { wn::PrintWnd(c); return true; });\n\n\t\t//STR s_testSTR = L\"thames\";\n\n\t\t//Print(str::Switch(s_testSTR, 6, { L\"moo\", L\"te\", L\"thames\", L\"notin\" }));\n\n\t\t////Print(wn::ClassNameIs(w, { L\"moo\", L\"qm_*itor\", L\"khy\" }));\n\n\t\t//TestStringBuilder();\n\n\t\t//Bstr b1;\n\t\t//Print((void*)b1.m_str);\n\t\t//Bstr b2(L\"test\");\n\t\t//Print(b2);\n\n\t\t//Bstr s;\n\t\t//Print(wn::ClassName(w, s));\n\t\t//Print(s);\n\t\t//Print(wn::Name(w, s));\n\t\t//Print(s);\n\n\t\t//Print(sizeof(CSimpleArray<int>));\n\t\t//Print(sizeof(CAtlArray<int>));\n\t\t//Print(sizeof(std::vector<int>));\n\t\t//Print(sizeof(std::array<int, 100>));\n\n\t\t//Print(str::Switch(L\"two\", 3, L\"one\", L\"two\", L\"three\", null));\n\n\t\t//#if true\n\t\t//\teTest t = eTest::One | eTest::Two;\n\t\t//\t//eTest t = eTest::Two;\n\t\t//\t//t = {};\n\t\t//\tPrint(t); //error if 'enum class'\n\t\t//\tPrint((int)t);\n\t\t//\n\t\t//\tif(t) Print(\"t is not 0\"); //error if 'enum class'\n\t\t//\tif(t&eTest::Two) Print(\"has Two\"); //error if 'enum class'\n\t\t//\tif(!t) Print(\"t is 0\");\n\t\t//\tif(!!t) Print(\"t is not 0\");\n\t\t//\tif(!!(t&eTest::Two)) Print(\"has Two\");\n\t\t//\n\t\t//\tif((t&eTest::Two) == eTest::Two) Print(\"has Two\");\n\t\t//\tt |= eTest::Four;\n\t\t//\tif((t&(eTest::Two | eTest::Four)) == (eTest::Two | eTest::Four)) Print(\"has (Two|Four)\");\n\t\t//\tPrint((int)(t&~eTest::Two));\n\t\t//#else\n\t\t//\teTest t = One | Two;\n\t\t//\t//eTest t = Two;\n\t\t//\tPrint(t);\n\t\t//\tif(t&Two) Print(\"has Two\");\n\t\t//\tif(t&Two == Two) Print(\"has Two\");\n\t\t//\tt |= Four;\n\t\t//\tif(t&(Two | Four) == (Two | Four)) Print(\"has (Two|Four)\");\n\t\t//\tPrint(t&~Two);\n\t\t//#endif\n\t\t//\n\t\t//\tPOINT p = {}, pp=p;\n\t\t//\t//int p = 5, pp = p;\n\t\t//\t//p |= pp;\n\t\t//\t//if(!p) Print(1);\n\n\t\t//Print(sizeof(std::wstring));\n\t\t//Print(sizeof(CString));\n\n\t\t//Perf.First();\n\t\t//for(size_t i = 0; i < 100000; i++) {\n\t\t////for(size_t i = 0; i < 1; i++) {\n\t\t//\tVoo(L\"1234567890\");\n\t\t//\t//Voo(null);\n\t\t//}\n\t\t//Perf.NW();\n\n\t\t//static TestTL s;\n\t\t//static thread_local TestTL t;\n\n\t\t//CSimpleMap<int, int> m;\n\t\t//m.Add(1, 10);\n\t\t//m.Add(2, 20);\n\t\t//Print(m.FindKey(2));\n\n\t\t//void* p = null;\n\t\t//for(int i = 1; i < 20; i++) {\n\t\t//\tp = _recalloc(p, i, 8);\n\t\t//\tPrintf(L\"%i %p\", i, p);\n\t\t//}\n\n\t\t//CAtlMap<int, int> m;\n\n\n\t\t//CAtlList<int> _a;\n\t\t//_a.AddTail(1);\n\t\t//_a.AddTail(2);\n\t\t//for(auto pos = _a.GetHeadPosition(); pos != null;) {\n\t\t//\tPrint(_a.GetNext(ref pos));\n\t\t//}\n\n\t\t//PRINTHEX(10);\n\t\t//PRINTF(L\"%s %i\", L\"MOO\", 10);\n\n\t\t////Print(IsOS64Bit());\n\n\t\t////HWND w = FindWindow(L\"Chrome_WidgetWin_1\", null);\n\t\t//HWND w = FindWindow(L\"QM_Editor\", null);\n\t\t//if(!w) {\n\t\t//\tPrint(\"window not found\");\n\t\t//\treturn;\n\t\t//}\n\t\t//DWORD pid, tid = GetWindowThreadProcessId(w, &pid);\n\t\t////bool is64bit, ok = IsProcess64Bit(pid, is64bit);\n\t\t////Printf(L\"ok=%i is64=%i\", ok, is64bit);\n\n\t\t//Bstr s;\n\n}\n\n//#include \"IAccessible2.h\"\n//\n//void InProcAccTest(IAccessible* a)\n//{\n//\t//Print(L\"TEST\");\n//\tSmart<IAccessible2> a2;\n//\tif(!QueryService(a, &a2, &IID_IAccessible)) { Print(\"QS failed\"); return; }\n//\n//\t//AccessibleStates states;\n//\t//if(a2->get_states(&states)) { Print(\"get_states failed\"); return; }\n//\t//Print((uint)states);\n//\n//\t//long n;\n//\t//if(a2->get_nExtendedStates(&n)) { Print(\"get_nExtendedStates failed\"); return; } //fails\n//\t//Print(n);\n//}\n\nclass MyAcc :IAccessible {\n\tHWND _w;\n\tint _ref;\n\npublic:\n\tMyAcc(HWND w) {\n\t\t_w = w;\n\t\t_ref = 1;\n\t}\n#pragma region\n\t// Inherited via IAccessible\n\tvirtual HRESULT __stdcall QueryInterface(REFIID riid, void** ppvObject) override {\n\t\tif (riid == IID_IUnknown || riid == IID_IDispatch || riid == IID_IAccessible) {\n\t\t\t*ppvObject = this;\n\t\t\t_ref++;\n\t\t\treturn 0;\n\t\t}\n\t\t*ppvObject = 0;\n\t\treturn E_NOINTERFACE;\n\t}\n\tvirtual ULONG __stdcall AddRef(void) override {\n\t\t_ref++;\n\t\treturn 1;\n\t}\n\tvirtual ULONG __stdcall Release(void) override {\n\t\t_ref--;\n\t\tPrintf(L\"Release: %i\", _ref);\n\t\treturn 1;\n\t}\n\tvirtual HRESULT __stdcall GetTypeInfoCount(UINT* pctinfo) override {\n\t\treturn E_NOTIMPL;\n\t}\n\tvirtual HRESULT __stdcall GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo) override {\n\t\treturn E_NOTIMPL;\n\t}\n\tvirtual HRESULT __stdcall GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgDispId) override {\n\t\treturn E_NOTIMPL;\n\t}\n\tvirtual HRESULT __stdcall Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr) override {\n\t\treturn E_NOTIMPL;\n\t}\n#pragma endregion\n\tvirtual HRESULT __stdcall get_accParent(IDispatch** ppdispParent) override {\n\t\t//Print(__FUNCTION__);\n\t\treturn AccessibleObjectFromWindow(_w, 0, IID_IAccessible, (void**)ppdispParent);\n\t}\n\tvirtual HRESULT __stdcall get_accChildCount(long* pcountChildren) override {\n\t\t//Print(__FUNCTION__);\n\t\t*pcountChildren = 0;\n\t\treturn 0;\n\t}\n\tvirtual HRESULT __stdcall get_accChild(VARIANT varChild, IDispatch** ppdispChild) override {\n\t\t//Print(__FUNCTION__);\n\t\t*ppdispChild = 0;\n\t\tif (varChild.vt != VT_I4)return E_INVALIDARG;\n\t\treturn S_FALSE;\n\t}\n\tvirtual HRESULT __stdcall get_accName(VARIANT varChild, BSTR* pszName) override {\n\t\treturn E_NOTIMPL;\n\t}\n\tvirtual HRESULT __stdcall get_accValue(VARIANT varChild, BSTR* pszValue) override {\n\t\treturn E_NOTIMPL;\n\t}\n\tvirtual HRESULT __stdcall get_accDescription(VARIANT varChild, BSTR* pszDescription) override {\n\t\treturn E_NOTIMPL;\n\t}\n\tvirtual HRESULT __stdcall get_accRole(VARIANT varChild, VARIANT* pvarRole) override {\n\t\t//Print(__FUNCTION__);\n\t\tpvarRole->vt = VT_I4; pvarRole->lVal = varChild.lVal == 0 ? ROLE_SYSTEM_LIST : ROLE_SYSTEM_LISTITEM;\n\t\treturn 0;\n\t}\n\tvirtual HRESULT __stdcall get_accState(VARIANT varChild, VARIANT* pvarState) override {\n\t\t//Print(__FUNCTION__);\n\t\treturn E_NOTIMPL;\n\t}\n\tvirtual HRESULT __stdcall get_accHelp(VARIANT varChild, BSTR* pszHelp) override {\n\t\treturn E_NOTIMPL;\n\t}\n\tvirtual HRESULT __stdcall get_accHelpTopic(BSTR* pszHelpFile, VARIANT varChild, long* pidTopic) override {\n\t\treturn E_NOTIMPL;\n\t}\n\tvirtual HRESULT __stdcall get_accKeyboardShortcut(VARIANT varChild, BSTR* pszKeyboardShortcut) override {\n\t\treturn E_NOTIMPL;\n\t}\n\tvirtual HRESULT __stdcall get_accFocus(VARIANT* pvarChild) override {\n\t\treturn E_NOTIMPL;\n\t}\n\tvirtual HRESULT __stdcall get_accSelection(VARIANT* pvarChildren) override {\n\t\treturn E_NOTIMPL;\n\t}\n\tvirtual HRESULT __stdcall get_accDefaultAction(VARIANT varChild, BSTR* pszDefaultAction) override {\n\t\treturn E_NOTIMPL;\n\t}\n\tvirtual HRESULT __stdcall accSelect(long flagsSelect, VARIANT varChild) override {\n\t\treturn E_NOTIMPL;\n\t}\n\tvirtual HRESULT __stdcall accLocation(long* pxLeft, long* pyTop, long* pcxWidth, long* pcyHeight, VARIANT varChild) override {\n\t\t//Print(__FUNCTION__);\n\t\tWINDOWINFO wi = { sizeof(WINDOWINFO) }; GetWindowInfo(_w, &wi);\n\t\t*pxLeft = wi.rcClient.left;\n\t\t*pyTop = wi.rcClient.top;\n\t\t*pcxWidth = wi.rcClient.right - wi.rcClient.left;\n\t\t*pcyHeight = wi.rcClient.bottom - wi.rcClient.top;\n\t\treturn 0;\n\t}\n\tvirtual HRESULT __stdcall accNavigate(long navDir, VARIANT varStart, VARIANT* pvarEndUpAt) override {\n\t\treturn E_NOTIMPL;\n\t}\n\tvirtual HRESULT __stdcall accHitTest(long xLeft, long yTop, VARIANT* pvarChild) override {\n\t\t//Print(__FUNCTION__);\n\t\tpvarChild->vt = VT_I4;\n\t\tpvarChild->lVal = 0;\n\t\treturn 0;\n\t}\n\tvirtual HRESULT __stdcall accDoDefaultAction(VARIANT varChild) override {\n\t\treturn E_NOTIMPL;\n\t}\n\tvirtual HRESULT __stdcall put_accName(VARIANT varChild, BSTR szName) override {\n\t\treturn E_NOTIMPL;\n\t}\n\tvirtual HRESULT __stdcall put_accValue(VARIANT varChild, BSTR szValue) override {\n\t\treturn E_NOTIMPL;\n\t}\n};\n\nMyAcc* m;\n\nLRESULT __stdcall _WndProc(HWND w, UINT msg, WPARAM wParam, LPARAM lParam) {\n\n\t//Print(msg);\n\tswitch (msg) {\n\tcase WM_DESTROY:\n\t\tm = null;\n\t\t//PostQuitMessage(0);\n\t\tPostMessage(w, WM_APP + 127, 0, 0);\n\t\tbreak;\n\tcase WM_GETOBJECT:\n\t\t//Print((int)lParam);\n\t\tif ((int)lParam == OBJID_CLIENT) {\n\t\t\tPrint(\"WM_GETOBJECT\");\n\t\t\tif (m == null) m = new MyAcc(w);\n\t\t\treturn LresultFromObject(IID_IAccessible, wParam, (LPUNKNOWN)m);\n\t\t}\n\t\tbreak;\n\t}\n\n\treturn DefWindowProcW(w, msg, wParam, lParam);\n}\n\nvoid _TestIAccessibleImpl() {\n\n\tWNDCLASSEXW x = {};\n\tx.cbSize = sizeof(x);\n\tx.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);\n\tx.hCursor = LoadCursor(0, (STR)IDC_ARROW);\n\tx.lpszClassName = L\"Montejo\";\n\tx.lpfnWndProc = _WndProc;\n\tauto atom = RegisterClassExW(&x);\n\tauto w = CreateWindowExW(WS_EX_TOPMOST, (STR)atom, L\"Gooo\", WS_VISIBLE | WS_POPUPWINDOW | WS_CAPTION, 500, 300, 300, 300, 0, 0, 0, 0);\n\tMSG m;\n\twhile (GetMessageW(&m, 0, 0, 0) > 0) {\n\t\tif (m.message == WM_APP + 127) break;\n\t\tTranslateMessage(&m);\n\t\tDispatchMessageW(&m);\n\t}\n}\n\n#endif //#if _DEBUG\n"
  },
  {
    "path": "Cpp/util.h",
    "content": "#pragma once\n#include \"stdafx.h\"\n\n#define ZEROTHIS memset(this, 0, sizeof(*this))\n#define ZEROTHISFROM(member) memset((LPBYTE)this+((LPBYTE)&member-(LPBYTE)this), 0, sizeof(*this)-((LPBYTE)&member-(LPBYTE)this))\n\n#if _DEBUG\n#define TRACE 1\n#endif\n\n#if TRACE\n\nvoid Print(STR s);\nvoid Printf(STR frm, ...);\n\ninline void Print(LPCSTR s) { Printf(L\"%S\", s); }\ninline void Print(const std::wstring& s) { Print(s.c_str()); }\ninline void Print(int i) { Printf(L\"%i\", i); }\ninline void Print(unsigned int i) { Printf(L\"0x%X\", i); }\ninline void Print(long i) { Print((int)i); }\ninline void Print(unsigned long i) { Print((unsigned int)i); }\ninline void Print(__int64 i) { Printf(L\"%I64i\", i); }\ninline void Print(unsigned __int64 i) { Printf(L\"0x%I64X\", i); }\ninline void Print(void* i) { Printf(sizeof(void*) == 8 ? L\"%I64i\" : L\"%i\", i); }\n\n#define PRINTI(x) Printf(L\"debug: \" __FILEW__ \"(\" _CRT_STRINGIZE(__LINE__) \"):  %i\", x)\n#define PRINTS(x) Printf(L\"debug: \" __FILEW__ \"(\" _CRT_STRINGIZE(__LINE__) \"):  %s\", x)\n#define PRINTHEX(x) Printf(L\"debug: \" __FILEW__ \"(\" _CRT_STRINGIZE(__LINE__) \"):  0x%X\", x)\n#define PRINTF(formatString, ...) Printf(L\"debug: \" __FILEW__ \"(\" _CRT_STRINGIZE(__LINE__) \"):  \" formatString, __VA_ARGS__)\n#define PRINTF_IF(condition, formatString, ...) { if(condition) PRINTF(formatString, __VA_ARGS__); }\n#define PRINTS_IF(condition, x) { if(condition) PRINTS(x); }\n\ninline void PrintComRefCount(IUnknown* u) {\n\tif (u) {\n\t\tu->AddRef();\n\t\tint i = u->Release();\n\t\tPrintf(L\"%p  %i\", u, i);\n\t} else Print(L\"null\");\n}\n\n#else\n#define Print __noop\n#define Printf __noop\n#define PRINTI __noop\n#define PRINTS __noop\n#define PRINTHEX __noop\n#define PRINTF __noop\n#define PRINTF_IF __noop\n#define PRINTS_IF __noop\n#define PrintComRefCount __noop\n#endif\n\n#if TRACE\n\nstruct Perf_Inst {\nprivate:\n\tint _counter;\n\tbool _incremental;\n\tint _nMeasurements; //used with incremental to display n measurements and average times\n\t__int64 _time0;\n\tstatic const int _nElem = 16;\n\t__int64 _a[_nElem];\n\twchar_t _aMark[_nElem];\n\npublic:\n\t//Perf_Inst() noexcept { ZEROTHIS; } //not used because then does not work shared data section\n\tPerf_Inst() {}\n\tPerf_Inst(bool isLocal) { if (isLocal) ZEROTHIS; }\n\n\tvoid First();\n\tvoid Next(char cMark = '\\0');\n\tvoid Write();\n\n\tvoid NW(char cMark = '\\0') { Next(cMark); Write(); }\n\n\tvoid SetIncremental(bool yes) {\n\t\tif (_incremental = yes) {\n\t\t\tfor (int i = 0; i < _nElem; i++) _a[i] = 0;\n\t\t\t_nMeasurements = 0;\n\t\t}\n\t}\n};\n\nextern Perf_Inst Perf;\n\nclass PerfLocal {\n\tPerf_Inst _p;\npublic:\n\tPerfLocal() : _p(true) { _p.First(); }\n\t~PerfLocal() { _p.NW(); }\n\tvoid Next(char mark = '\\0') { _p.Next(mark); }\n};\n\n#endif\n\n#include \"str.h\"\n\n\nclass OSVersion {\n\tDWORD _major, _minor, _winver, _win10build;\npublic:\n\tOSVersion() noexcept {\n\t\ttypedef int(WINAPI* tRtlGetVersion)(RTL_OSVERSIONINFOW*);\n\t\tauto RtlGetVersion = (tRtlGetVersion)GetProcAddress(GetModuleHandleA(\"ntdll\"), \"RtlGetVersion\");\n\n\t\tRTL_OSVERSIONINFOW x = { sizeof(RTL_OSVERSIONINFOW) };\n\t\tRtlGetVersion(&x);\n\t\t_winver = MAKEWORD(_minor = x.dwMinorVersion, _major = x.dwMajorVersion);\n\t\tif (_major >= 10) _win10build = x.dwBuildNumber;\n\t}\n\n\tint major() { return _major; }\n\n\tint minor() { return _minor; }\n\n\tint winver() { return _winver; }\n\n\tbool minWin8() { return _winver >= 0x602; }\n\n\tbool minWin10() { return _major >= 10; }\n\n\tbool minWin11() { return _win10build >= 22000; }\n\n\tbool is32BitOS() {\n#ifdef _WIN64\n\t\treturn false;\n#else\n\t\tstatic int r;\n\t\tif (!r) {\n\t\t\tif (IsWow64Process(GetCurrentProcess(), &r)) r = r ? 1 : -1;\n\t\t}\n\t\treturn r < 0;\n#endif\n\t}\n};\nextern OSVersion osVer;\n\nextern HMODULE s_moduleHandle;\n\n//returns: 1 32, 2 x64, 3 arm64\ninline int GetProcessArchitecture() {\n#if _M_ARM64\n\treturn 3;\n#elif _WIN64\n\treturn 2;\n#else\n\treturn 1;\n#endif\n}\n\nEXPORT int Cpp_GetProcessArchitecture(DWORD pid);\n\n\n//Standard IUnknown implementation with thread-unsafe refcounting.\n#define STD_IUNKNOWN_METHODS(iface) \\\nSTDMETHODIMP QueryInterface(REFIID iid, void** ppv)\\\n{\\\n\tif(iid == IID_IUnknown || iid == IID_##iface) { _cRef++; *ppv = this; return S_OK; }\\\n\telse { *ppv = nullptr; return E_NOINTERFACE; }\\\n}\\\nSTDMETHODIMP_(ULONG) AddRef()\\\n{\\\n\treturn ++_cRef;\\\n}\\\nSTDMETHODIMP_(ULONG) Release()\\\n{\\\n\tlong ret=--_cRef;\\\n\tif(!ret) delete this;\\\n\treturn ret;\\\n}\n\n\n//Standard IUnknown implementation without refcounting.\n#define STD_IUNKNOWN_METHODS_SIMPLE(iface) \\\nSTDMETHODIMP QueryInterface(REFIID iid, void** ppv)\\\n{\\\n\tif(iid == IID_IUnknown || iid == IID_##iface) { *ppv = this; return S_OK; }\\\n\telse { *ppv = nullptr; return E_NOINTERFACE; }\\\n}\\\nSTDMETHODIMP_(ULONG) AddRef() { return 1; }\\\nSTDMETHODIMP_(ULONG) Release() { return 1; }\n\n\n//Smart pointer that extends CComPtr.\n//I don't use _com_ptr_t because: 1. Can throw. 2. Intellisense bug after upgrading VS: shows many false errors.\ntemplate <class T>\nclass Smart : public CComPtr<T> {\npublic:\n\tSmart() throw() {\n\t}\n\tSmart(_Inout_opt_ T* lp, bool addRef) throw() {\n\t\tthis->p = lp;\n\t\tif (addRef) this->p->AddRef();\n\t}\n\tSmart(_Inout_ const Smart<T>& lp) throw() : CComPtr<T>(lp.p) {\n\t}\n\n\tvoid Swap(CComPtrBase<T>& other) {\n\t\tT* pTemp = this->p;\n\t\tthis->p = other.p;\n\t\tother.p = pTemp;\n\t}\n\n};\n\n\n//Delay-loaded API pointers.\nstruct DelayLoadedApi {\n\tbool minWin81, minWin10;\n\n\t//user32\n\tBOOL(WINAPI* PhysicalToLogicalPoint)(HWND hWnd, LPPOINT lpPoint);\n\tBOOL(WINAPI* LogicalToPhysicalPoint)(HWND hWnd, LPPOINT lpPoint);\n\tDPI_AWARENESS_CONTEXT(WINAPI* GetWindowDpiAwarenessContext)(HWND hwnd);\n\tDPI_AWARENESS(WINAPI* GetAwarenessFromDpiAwarenessContext)(DPI_AWARENESS_CONTEXT value);\n\tDPI_AWARENESS_CONTEXT(WINAPI* SetThreadDpiAwarenessContext)(DPI_AWARENESS_CONTEXT dpiContext);\n\n\t//shcore\n\tHRESULT(__stdcall* GetProcessDpiAwareness)(HANDLE hprocess, PROCESS_DPI_AWARENESS* value);\n\n\t//kernel32\n\tBOOL(WINAPI* IsWow64Process2)(HANDLE hProcess, USHORT* pProcessMachine, USHORT* pNativeMachine);\n\tBOOL(WINAPI* GetProcessInformation)(HANDLE hProcess, PROCESS_INFORMATION_CLASS ProcessInformationClass, LPVOID ProcessInformation, DWORD ProcessInformationSize);\n\n#define GPA(hm, f) *(FARPROC*)&f=GetProcAddress(hm, #f)\n#define GPA2(hm, f, name) *(FARPROC*)&f=GetProcAddress(hm, name)\n\tDelayLoadedApi() noexcept {\n\t\tminWin81 = minWin10 = false;\n\t\tauto hKernel = GetModuleHandle(L\"kernel32.dll\");\n\t\tauto hUser = GetModuleHandle(L\"user32.dll\");\n\t\tGPA2(hUser, PhysicalToLogicalPoint, \"PhysicalToLogicalPointForPerMonitorDPI\");\n\t\tif (minWin81 = PhysicalToLogicalPoint) { //Win8.1+\n\t\t\tGPA2(hUser, LogicalToPhysicalPoint, \"LogicalToPhysicalPointForPerMonitorDPI\");\n\n\t\t\tGPA(hUser, GetWindowDpiAwarenessContext);\n\t\t\tif (minWin10 = GetWindowDpiAwarenessContext) { //Win10 1607+\n\t\t\t\tGPA(hUser, GetAwarenessFromDpiAwarenessContext);\n\t\t\t\tGPA(hUser, SetThreadDpiAwarenessContext);\n\n\t\t\t\tGPA(hKernel, IsWow64Process2); //Win10 1709+\n\t\t\t\tGPA(hKernel, GetProcessInformation); //Win8, but only on Win11 supports what we need\n\t\t\t} else {\n\t\t\t\tauto hShcore = GetModuleHandle(L\"shcore.dll\");\n\t\t\t\tGPA(hShcore, GetProcessDpiAwareness);\n\t\t\t}\n\n\n\t\t} else { //Win7/8\n\t\t\tGPA(hUser, PhysicalToLogicalPoint);\n\t\t\t//GPA(hUser, LogicalToPhysicalPoint);\n\n\t\t}\n\t}\n};\nextern DelayLoadedApi dlapi;\n\n\n//SECURITY_ATTRIBUTES that allows UAC low integrity level processes to open the kernel object.\n//can instead use CSecurityAttributes/CSecurityDesc, but this added before including ATL, and don't want to change now.\nclass SecurityAttributes {\n\tDWORD nLength;\n\tLPVOID lpSecurityDescriptor;\n\tBOOL bInheritHandle;\npublic:\n\n\tSecurityAttributes() {\n\t\tnLength = sizeof(SecurityAttributes);\n\t\tbInheritHandle = false;\n\t\tlpSecurityDescriptor = null;\n\t\tBOOL ok = ConvertStringSecurityDescriptorToSecurityDescriptorW(L\"D:NO_ACCESS_CONTROLS:(ML;;NW;;;LW)\", 1, &lpSecurityDescriptor, null);\n\t\tassert(ok);\n\t}\n\n\t~SecurityAttributes() {\n\t\tLocalFree(lpSecurityDescriptor);\n\t}\n\n\t//SECURITY_ATTRIBUTES* operator&() {\n\t//\treturn (SECURITY_ATTRIBUTES*)this;\n\t//}\n\n\tstatic SECURITY_ATTRIBUTES* Common() {\n\t\tstatic SecurityAttributes s_sa;\n\t\treturn (SECURITY_ATTRIBUTES*)&s_sa;\n\t}\n};\n\nclass AutoReleaseMutex {\n\tHANDLE _mutex;\npublic:\n\tAutoReleaseMutex(HANDLE mutex) noexcept {\n\t\t_mutex = mutex;\n\t}\n\n\t~AutoReleaseMutex() {\n\t\tif (_mutex) ReleaseMutex(_mutex);\n\t}\n\n\tvoid ReleaseNow() {\n\t\tif (_mutex) ReleaseMutex(_mutex);\n\t\t_mutex = 0;\n\t}\n};\n\n//class ProcessMemory\n//{\n//\tLPBYTE m_mem;\n//\tHANDLE m_hproc;\n//public:\n//\tProcessMemory() { ZEROTHIS; }\n//\t~ProcessMemory() { Free(); }\n//\tLPBYTE Alloc(HWND hWnd, DWORD nBytes, DWORD flags = 0);\n//};\n\n//currently not used.\n////can instead use CAtlFileMappingBase from atlfile.h, but this added before including ATL, and don't want to change now.\n//class SharedMemory\n//{\n//\tHANDLE _hmapfile;\n//\tLPBYTE _mem;\n//public:\n//\tSharedMemory() { _hmapfile = 0; _mem = 0; }\n//\t~SharedMemory() { Close(); }\n//\n//\tbool Create(STR name, DWORD size)\n//\t{\n//\t\tClose();\n//\t\t_hmapfile = CreateFileMappingW((HANDLE)(-1), SecurityAttributes::Common(), PAGE_READWRITE, 0, size, name);\n//\t\tif(!_hmapfile) return false;\n//\t\t_mem = (LPBYTE)MapViewOfFile(_hmapfile, FILE_MAP_ALL_ACCESS, 0, 0, 0);\n//\t\treturn _mem != null;\n//\t}\n//\n//\tbool Open(STR name)\n//\t{\n//\t\tClose();\n//\t\t_hmapfile = OpenFileMappingW(FILE_MAP_ALL_ACCESS, 0, name);\n//\t\tif(!_hmapfile) return false;\n//\t\t_mem = (LPBYTE)MapViewOfFile(_hmapfile, FILE_MAP_ALL_ACCESS, 0, 0, 0);\n//\t\treturn _mem != null;\n//\t}\n//\n//\tvoid Close()\n//\t{\n//\t\tif(_mem) { UnmapViewOfFile(_mem); _mem = 0; }\n//\t\tif(_hmapfile) { CloseHandle(_hmapfile); _hmapfile = 0; }\n//\t}\n//\n//\tLPBYTE Mem() { return _mem; }\n//\n//\tbool Is0() { return _mem == null; }\n//};\n\n//currently not used.\n//template<class T>\n//class AutoResetVariable\n//{\n//\tT* _b;\n//public:\n//\tAutoResetVariable(T* b, T value) { _b = b; *b = value; }\n//\t~AutoResetVariable() { *_b = 0; }\n//};\n\n//IStream helpers.\nclass istream {\npublic:\n\tstatic LARGE_INTEGER LI(__int64 i) {\n\t\tLARGE_INTEGER r; r.QuadPart = i;\n\t\treturn r;\n\t}\n\n\tstatic ULARGE_INTEGER ULI(__int64 i) {\n\t\tULARGE_INTEGER r; r.QuadPart = i;\n\t\treturn r;\n\t}\n\n\tstatic bool ResetPos(IStream* x) {\n\t\treturn 0 == x->Seek(LI(0), STREAM_SEEK_SET, null);\n\t}\n\n\tstatic bool GetPos(IStream* x, out DWORD& pos) {\n\t\tpos = 0;\n\t\t__int64 pos64;\n\t\tif (x->Seek(LI(0), STREAM_SEEK_CUR, (ULARGE_INTEGER*)&pos64)) return false;\n\t\tpos = (DWORD)pos64;\n\t\treturn true;\n\t}\n\n\t//static bool GetSize(IStream* x, out DWORD& size) {\n\t//\tsize = 0;\n\t//\tSTATSTG stat;\n\t//\tif(x->Stat(&stat, STATFLAG_NONAME)) return false;\n\t//\tsize = stat.cbSize.LowPart;\n\t//\treturn true;\n\t//}\n\n\tstatic bool Clear(IStream* x) {\n\t\treturn 0 == x->SetSize(ULI(0));\n\t}\n};\n\n//Use inproc to DPI-scale elm rect from logical to physical.\nclass DpiElmScaling {\n\tHWND _w;\n\tRECT _rw;\n\tbool _scaled;\n\tbool _haveRect;\npublic:\n\t//Use w or acc. If acc not null, ignores w and calls WindowFromAccessibleObject.\n\tDpiElmScaling(bool use, HWND w, IAccessible* acc) {\n\t\tZEROTHIS;\n\t\tif (!use || !dlapi.minWin81) return; //on Win7/8 we get physical rect\n\n\t\tif (acc != null) {\n\t\t\tif (!(0 == WindowFromAccessibleObject(acc, &w) && w)) { //TODO3: if w specified too in the props string, don't call twice. Same with r/D.\n\t\t\t\tPRINTS(L\"failed WindowFromAccessibleObject\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\t_w = w;\n\n\t\tauto da = DPI_AWARENESS::DPI_AWARENESS_SYSTEM_AWARE;\n\t\tif (dlapi.GetWindowDpiAwarenessContext) { //Win10 1607\n\t\t\tda = dlapi.GetAwarenessFromDpiAwarenessContext(dlapi.GetWindowDpiAwarenessContext(w)); //fast\n\t\t\tif (da != DPI_AWARENESS::DPI_AWARENESS_SYSTEM_AWARE && da != DPI_AWARENESS::DPI_AWARENESS_UNAWARE) return;\n\t\t} else { //Win8.1\n\t\t\tPROCESS_DPI_AWARENESS pda;\n\t\t\tif (0 == dlapi.GetProcessDpiAwareness(GetCurrentProcess(), &pda) && pda == PROCESS_DPI_AWARENESS::PROCESS_PER_MONITOR_DPI_AWARE) return;\n\t\t}\n\n\t\tif (!(_haveRect = GetWindowRect(_w, &_rw))) { _w = 0; return; }\n\n\t\t//On Win10+ we can easily, quickly and reliably detect whether the window is DPI-scaled.\n\t\tif (dlapi.SetThreadDpiAwarenessContext) { //Win10 1607\n\t\t\tauto ac = dlapi.SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); //fast\n\t\t\tRECT r2;\n\t\t\t_scaled = GetWindowRect(_w, &r2) && memcmp(&_rw, &r2, 16);\n\t\t\tdlapi.SetThreadDpiAwarenessContext(ac);\n\t\t} else _scaled = true; //on Win8.1 assume need scaling, it does not harm\n\t}\n\n\t//Returns: 0 don't need to scale, 1 r is not in the window, 2 scaled ok, -1 failed to scale.\n\t//needReturn1 - even if don't need to scale, return 1 if r is not in the window.\n\tint ScaleIfNeed(ref RECT& r, bool needReturn1 = false) {\n\t\tif (!_scaled) {\n\t\t\tif (needReturn1 && _w) {\n\t\t\t\tif (!_haveRect && !(_haveRect = GetWindowRect(_w, &_rw))) { _w = 0; return 0; }\n\t\t\t\tif (!IntersectRect(&r, &r, &_rw)) return 1;\n\t\t\t}\n\t\t\treturn 0;\n\t\t}\n\n\t\t//The API fails if the point is not in the window rect, except when touches it at the right/bottom.\n\t\t//Tried workaround: create a hidden window with same DPI awareness and use it with the API instead of w.\n\t\t//\tIn most cases it works, but often does not scale if the point is outside the top-level window.\n\t\t//Current workaround: use r intersection with the container window rect.\n\t\tif (!IntersectRect(&r, &r, &_rw)) return 1;\n\n\t\tPOINT p1 = { r.left, r.top }, p2 = { r.right, r.bottom };\n\t\tif (dlapi.LogicalToPhysicalPoint(_w, &p1) && dlapi.LogicalToPhysicalPoint(_w, &p2)) {\n\t\t\tSetRect(&r, p1.x, p1.y, p2.x, p2.y);\n\t\t\treturn 2;\n\t\t} else {\n\t\t\tPRINTS(L\"failed LogicalToPhysicalPoint\");\n\t\t\treturn -1;\n\t\t}\n\t}\n};\n\nnamespace wn {\n\tinline DWORD Style(HWND w) { return (DWORD)GetWindowLongPtrW(w, GWL_STYLE); }\n\tinline DWORD ExStyle(HWND w) { return (DWORD)GetWindowLongPtrW(w, GWL_EXSTYLE); }\n\tbool ClassName(HWND w, out Bstr& s);\n\tint ClassNameIs(HWND w, std::initializer_list<STR> a);\n\tbool ClassNameIs(HWND w, STR s);\n\tbool ClassNameIs(HWND w, const str::Wildex& s);\n\tbool Name(HWND w, out Bstr& s);\n\tbool IsVisibleInWindow(HWND c, HWND wTL);\n\n\tusing WNDENUMPROCL = const std::function <bool(HWND c)>;\n\n\tBOOL EnumChildWindows(HWND w, WNDENUMPROCL& callback);\n\tHWND FindChildByClassName(HWND w, STR className, bool visible);\n\tHWND FindChildByClassName(HWND w, STR className1, STR className2, OUT bool& second, bool visible);\n\tHWND FindWndEx(HWND wParent, HWND wAfter, STR cn, STR name = null);\n\tHWND FindWnd(STR cn, STR name = null);\n\tHWND FindWndExVisible(HWND wParent, STR cn);\n\tbool WinformsNameIs(HWND w, STR name);\n\n#if TRACE\n\tvoid PrintWnd(HWND w);\n#else\n#define PrintWnd __noop\n#endif\n}\n\nbool QueryService_(IUnknown* iFrom, OUT void** iTo, REFIID iid, const GUID* guidService = null);\n\ntemplate<class T>\nbool QueryService(IUnknown* iFrom, OUT T** iTo, const GUID* guidService = null) {\n\treturn QueryService_(iFrom, (void**)iTo, __uuidof(T), guidService);\n}\n\nnamespace util {\n\t//Swaps values of variables a and b: <c>T t = a; a = b; b = t;</c>\n\ttemplate<class T>\n\tvoid Swap(ref T& a, ref T& b) {\n\t\tT t = a; a = b; b = t;\n\t}\n\n}\n"
  },
  {
    "path": "LICENSE.txt",
    "content": "MIT License\n\nCopyright (c) 2025 Gintaras Didžgalvis\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "Libraries/PCRE/PCRE.vcxproj",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project DefaultTargets=\"Build\" ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup Label=\"ProjectConfigurations\">\n    <ProjectConfiguration Include=\"Debug|ARM64\">\n      <Configuration>Debug</Configuration>\n      <Platform>ARM64</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Debug|Win32\">\n      <Configuration>Debug</Configuration>\n      <Platform>Win32</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release|ARM64\">\n      <Configuration>Release</Configuration>\n      <Platform>ARM64</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release|Win32\">\n      <Configuration>Release</Configuration>\n      <Platform>Win32</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Debug|x64\">\n      <Configuration>Debug</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release|x64\">\n      <Configuration>Release</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n  </ItemGroup>\n  <PropertyGroup Label=\"Globals\">\n    <VCProjectVersion>15.0</VCProjectVersion>\n    <ProjectGuid>{190BC611-EE00-4B10-87F2-A7E73B639E59}</ProjectGuid>\n    <Keyword>Win32Proj</Keyword>\n    <RootNamespace>PCRE</RootNamespace>\n    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"Configuration\">\n    <ConfigurationType>StaticLibrary</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v143</PlatformToolset>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\n    <ConfigurationType>StaticLibrary</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v143</PlatformToolset>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"Configuration\">\n    <ConfigurationType>StaticLibrary</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v143</PlatformToolset>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|ARM64'\" Label=\"Configuration\">\n    <ConfigurationType>StaticLibrary</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v143</PlatformToolset>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\n    <ConfigurationType>StaticLibrary</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v143</PlatformToolset>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|ARM64'\" Label=\"Configuration\">\n    <ConfigurationType>StaticLibrary</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v143</PlatformToolset>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\n  <ImportGroup Label=\"ExtensionSettings\">\n  </ImportGroup>\n  <ImportGroup Label=\"Shared\">\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|ARM64'\" Label=\"PropertySheets\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|ARM64'\" Label=\"PropertySheets\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <PropertyGroup Label=\"UserMacros\" />\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\n    <LinkIncremental>true</LinkIncremental>\n    <OutDir>bin\\$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>obj\\$(Platform)\\$(Configuration)\\</IntDir>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <LinkIncremental>true</LinkIncremental>\n    <OutDir>bin\\$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>obj\\$(Platform)\\$(Configuration)\\</IntDir>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|ARM64'\">\n    <LinkIncremental>true</LinkIncremental>\n    <OutDir>bin\\$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>obj\\$(Platform)\\$(Configuration)\\</IntDir>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <LinkIncremental>false</LinkIncremental>\n    <OutDir>bin\\$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>obj\\$(Platform)\\$(Configuration)\\</IntDir>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <LinkIncremental>false</LinkIncremental>\n    <OutDir>bin\\$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>obj\\$(Platform)\\$(Configuration)\\</IntDir>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|ARM64'\">\n    <LinkIncremental>false</LinkIncremental>\n    <OutDir>bin\\$(Platform)\\$(Configuration)\\</OutDir>\n    <IntDir>obj\\$(Platform)\\$(Configuration)\\</IntDir>\n  </PropertyGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>Disabled</Optimization>\n      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions);HAVE_CONFIG_H;PCRE2_CODE_UNIT_WIDTH=16</PreprocessorDefinitions>\n      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>Disabled</Optimization>\n      <PreprocessorDefinitions>_DEBUG;_LIB;%(PreprocessorDefinitions);HAVE_CONFIG_H;PCRE2_CODE_UNIT_WIDTH=16</PreprocessorDefinitions>\n      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|ARM64'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>Disabled</Optimization>\n      <PreprocessorDefinitions>_DEBUG;_LIB;%(PreprocessorDefinitions);HAVE_CONFIG_H;PCRE2_CODE_UNIT_WIDTH=16</PreprocessorDefinitions>\n      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>MaxSpeed</Optimization>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions);HAVE_CONFIG_H;PCRE2_CODE_UNIT_WIDTH=16</PreprocessorDefinitions>\n      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\n      <BufferSecurityCheck>false</BufferSecurityCheck>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>MaxSpeed</Optimization>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <PreprocessorDefinitions>NDEBUG;_LIB;%(PreprocessorDefinitions);HAVE_CONFIG_H;PCRE2_CODE_UNIT_WIDTH=16</PreprocessorDefinitions>\n      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\n      <BufferSecurityCheck>false</BufferSecurityCheck>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|ARM64'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>MaxSpeed</Optimization>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <PreprocessorDefinitions>NDEBUG;_LIB;%(PreprocessorDefinitions);HAVE_CONFIG_H;PCRE2_CODE_UNIT_WIDTH=16</PreprocessorDefinitions>\n      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\n      <BufferSecurityCheck>false</BufferSecurityCheck>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemGroup>\n    <ClCompile Include=\"pcre2_auto_possess.c\" />\n    <ClCompile Include=\"pcre2_chartables.c\" />\n    <ClCompile Include=\"pcre2_chkdint.c\" />\n    <ClCompile Include=\"pcre2_compile.c\" />\n    <ClCompile Include=\"pcre2_compile_class.c\" />\n    <ClCompile Include=\"pcre2_config.c\" />\n    <ClCompile Include=\"pcre2_context.c\" />\n    <ClCompile Include=\"pcre2_dfa_match.c\">\n      <ExcludedFromBuild Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">true</ExcludedFromBuild>\n      <ExcludedFromBuild Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">true</ExcludedFromBuild>\n      <ExcludedFromBuild Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">true</ExcludedFromBuild>\n      <ExcludedFromBuild Condition=\"'$(Configuration)|$(Platform)'=='Debug|ARM64'\">true</ExcludedFromBuild>\n      <ExcludedFromBuild Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">true</ExcludedFromBuild>\n      <ExcludedFromBuild Condition=\"'$(Configuration)|$(Platform)'=='Release|ARM64'\">true</ExcludedFromBuild>\n    </ClCompile>\n    <ClCompile Include=\"pcre2_error.c\" />\n    <ClCompile Include=\"pcre2_extuni.c\" />\n    <ClCompile Include=\"pcre2_find_bracket.c\" />\n    <ClCompile Include=\"pcre2_maketables.c\" />\n    <ClCompile Include=\"pcre2_match.c\" />\n    <ClCompile Include=\"pcre2_match_data.c\" />\n    <ClCompile Include=\"pcre2_newline.c\" />\n    <ClCompile Include=\"pcre2_ord2utf.c\" />\n    <ClCompile Include=\"pcre2_pattern_info.c\" />\n    <ClCompile Include=\"pcre2_script_run.c\" />\n    <ClCompile Include=\"pcre2_serialize.c\" />\n    <ClCompile Include=\"pcre2_string_utils.c\" />\n    <ClCompile Include=\"pcre2_study.c\" />\n    <ClCompile Include=\"pcre2_substitute.c\">\n      <ExcludedFromBuild Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">true</ExcludedFromBuild>\n      <ExcludedFromBuild Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">true</ExcludedFromBuild>\n      <ExcludedFromBuild Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">true</ExcludedFromBuild>\n      <ExcludedFromBuild Condition=\"'$(Configuration)|$(Platform)'=='Debug|ARM64'\">true</ExcludedFromBuild>\n      <ExcludedFromBuild Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">true</ExcludedFromBuild>\n      <ExcludedFromBuild Condition=\"'$(Configuration)|$(Platform)'=='Release|ARM64'\">true</ExcludedFromBuild>\n    </ClCompile>\n    <ClCompile Include=\"pcre2_substring.c\" />\n    <ClCompile Include=\"pcre2_tables.c\" />\n    <ClCompile Include=\"pcre2_ucd.c\" />\n    <ClCompile Include=\"pcre2_ucptables.c\" />\n    <ClCompile Include=\"pcre2_valid_utf.c\" />\n    <ClCompile Include=\"pcre2_xclass.c\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ClInclude Include=\"config.h\" />\n    <ClInclude Include=\"pcre2.h\" />\n    <ClInclude Include=\"pcre2_compile.h\" />\n    <ClInclude Include=\"pcre2_internal.h\" />\n    <ClInclude Include=\"pcre2_intmodedep.h\" />\n    <ClInclude Include=\"pcre2_ucp.h\" />\n    <ClInclude Include=\"pcre2_util.h\" />\n  </ItemGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\n  <ImportGroup Label=\"ExtensionTargets\">\n  </ImportGroup>\n</Project>"
  },
  {
    "path": "Libraries/PCRE/PCRE.vcxproj.filters",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup>\n    <Filter Include=\"Source Files\">\n      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>\n      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>\n    </Filter>\n    <Filter Include=\"Header Files\">\n      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>\n      <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>\n    </Filter>\n    <Filter Include=\"Resource Files\">\n      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>\n      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>\n    </Filter>\n    <Filter Include=\"Source Files\\excluded\">\n      <UniqueIdentifier>{db256fb5-df1c-4de2-8315-4927b4223fa1}</UniqueIdentifier>\n    </Filter>\n  </ItemGroup>\n  <ItemGroup>\n    <ClCompile Include=\"pcre2_auto_possess.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"pcre2_compile.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"pcre2_config.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"pcre2_context.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"pcre2_error.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"pcre2_find_bracket.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"pcre2_maketables.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"pcre2_match.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"pcre2_match_data.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"pcre2_newline.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"pcre2_ord2utf.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"pcre2_pattern_info.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"pcre2_serialize.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"pcre2_string_utils.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"pcre2_study.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"pcre2_substring.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"pcre2_tables.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"pcre2_ucd.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"pcre2_valid_utf.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"pcre2_xclass.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"pcre2_chartables.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"pcre2_dfa_match.c\">\n      <Filter>Source Files\\excluded</Filter>\n    </ClCompile>\n    <ClCompile Include=\"pcre2_extuni.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"pcre2_script_run.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"pcre2_substitute.c\">\n      <Filter>Source Files\\excluded</Filter>\n    </ClCompile>\n    <ClCompile Include=\"pcre2_ucptables.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"pcre2_chkdint.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"pcre2_compile_class.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n  </ItemGroup>\n  <ItemGroup>\n    <ClInclude Include=\"pcre2_internal.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"pcre2_intmodedep.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"pcre2_ucp.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"config.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"pcre2.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"pcre2_compile.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"pcre2_util.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "Libraries/PCRE/config.h",
    "content": "/* src/config.h.  Generated from config.h.in by configure.  */\n/* src/config.h.in.  Generated from configure.ac by autoheader.  */\n\n/* PCRE2 is written in Standard C, but there are a few non-standard things it\ncan cope with, allowing it to run on SunOS4 and other \"close to standard\"\nsystems.\n\nIn environments that support the GNU autotools, config.h.in is converted into\nconfig.h by the \"configure\" script. In environments that use CMake,\nconfig-cmake.in is converted into config.h. If you are going to build PCRE2 \"by\nhand\" without using \"configure\" or CMake, you should copy the distributed\nconfig.h.generic to config.h, and edit the macro definitions to be the way you\nneed them. You must then add -DHAVE_CONFIG_H to all of your compile commands,\nso that config.h is included at the start of every source.\n\nAlternatively, you can avoid editing by using -D on the compiler command line\nto set the macro values. In this case, you do not have to set -DHAVE_CONFIG_H,\nbut if you do, default values will be taken from config.h for non-boolean\nmacros that are not defined on the command line.\n\nBoolean macros such as HAVE_STDLIB_H and SUPPORT_PCRE2_8 should either be\ndefined (conventionally to 1) for TRUE, and not defined at all for FALSE. All\nsuch macros are listed as a commented #undef in config.h.generic. Macros such\nas MATCH_LIMIT, whose actual value is relevant, have defaults defined, but are\nsurrounded by #ifndef/#endif lines so that the value can be overridden by -D.\n\nPCRE2 uses memmove() if HAVE_MEMMOVE is defined; otherwise it uses bcopy() if\nHAVE_BCOPY is defined. If your system has neither bcopy() nor memmove(), make\nsure both macros are undefined; an emulation function will then be used. */\n\n/* By default, the \\R escape sequence matches any Unicode line ending\n   character or sequence of characters. If BSR_ANYCRLF is defined (to any\n   value), this is changed so that backslash-R matches only CR, LF, or CRLF.\n   The build-time default can be overridden by the user of PCRE2 at runtime.\n   */\n#define BSR_ANYCRLF 1\n\n/* Define to any value to disable the use of the z and t modifiers in\n   formatting settings such as %zu or %td (this is rarely needed). */\n/* #undef DISABLE_PERCENT_ZT */\n\n/* If you are compiling for a system that uses EBCDIC instead of ASCII\n   character codes, define this macro to any value. When EBCDIC is set, PCRE2\n   assumes that all input strings are in EBCDIC. If you do not define this\n   macro, PCRE2 will assume input strings are ASCII or UTF-8/16/32 Unicode. It\n   is not possible to build a version of PCRE2 that supports both EBCDIC and\n   UTF-8/16/32. */\n/* #undef EBCDIC */\n\n/* In an EBCDIC environment, define this macro to any value to arrange for the\n   NL character to be 0x25 instead of the default 0x15. NL plays the role that\n   LF does in an ASCII/Unicode environment. */\n/* #undef EBCDIC_NL25 */\n\n/* Define to 1 if you have the <assert.h> header file. */\n/* #undef HAVE_ASSERT_H */\n\n/* Define this if your compiler supports __attribute__((uninitialized)) */\n/* #undef HAVE_ATTRIBUTE_UNINITIALIZED */\n\n/* Define to 1 if you have the `bcopy' function. */\n/* #undef HAVE_BCOPY */\n\n/* Define this if your compiler provides __assume() */\n/* #undef HAVE_BUILTIN_ASSUME */\n\n/* Define this if your compiler provides __builtin_mul_overflow() */\n/* #undef HAVE_BUILTIN_MUL_OVERFLOW */\n\n/* Define this if your compiler provides __builtin_unreachable() */\n/* #undef HAVE_BUILTIN_UNREACHABLE */\n\n/* Define to 1 if you have the <bzlib.h> header file. */\n/* #undef HAVE_BZLIB_H */\n\n/* Define to 1 if you have the <dirent.h> header file. */\n/* #undef HAVE_DIRENT_H */\n\n/* Define to 1 if you have the <dlfcn.h> header file. */\n/* #undef HAVE_DLFCN_H */\n\n/* Define to 1 if you have the <editline/readline.h> header file. */\n/* #undef HAVE_EDITLINE_READLINE_H */\n\n/* Define to 1 if you have the <edit/readline/readline.h> header file. */\n/* #undef HAVE_EDIT_READLINE_READLINE_H */\n\n/* Define to 1 if you have the <inttypes.h> header file. */\n#define HAVE_INTTYPES_H\n\n/* Define to 1 if you have the <limits.h> header file. */\n#define HAVE_LIMITS_H 1\n\n/* Define to 1 if you have the `memfd_create' function. */\n/* #undef HAVE_MEMFD_CREATE */\n\n/* Define to 1 if you have the `memmove' function. */\n#define HAVE_MEMMOVE 1\n\n/* Define to 1 if you have the <minix/config.h> header file. */\n/* #undef HAVE_MINIX_CONFIG_H */\n\n/* Define to 1 if you have the `mkostemp' function. */\n/* #undef HAVE_MKOSTEMP */\n\n/* Define if you have POSIX threads libraries and header files. */\n/* #undef HAVE_PTHREAD */\n\n/* Have PTHREAD_PRIO_INHERIT. */\n/* #undef HAVE_PTHREAD_PRIO_INHERIT */\n\n/* Define to 1 if you have the <readline.h> header file. */\n/* #undef HAVE_READLINE_H */\n\n/* Define to 1 if you have the <readline/history.h> header file. */\n/* #undef HAVE_READLINE_HISTORY_H */\n\n/* Define to 1 if you have the <readline/readline.h> header file. */\n/* #undef HAVE_READLINE_READLINE_H */\n\n/* Define to 1 if you have the `realpath' function. */\n/* #undef HAVE_REALPATH */\n\n/* Define to 1 if you have the `secure_getenv' function. */\n/* #undef HAVE_SECURE_GETENV */\n\n/* Define to 1 if you have the <stdint.h> header file. */\n#define HAVE_STDINT_H 1\n\n/* Define to 1 if you have the <stdio.h> header file. */\n#define HAVE_STDIO_H 1\n\n/* Define to 1 if you have the <stdlib.h> header file. */\n#define HAVE_STDLIB_H 1\n\n/* Define to 1 if you have the `strerror' function. */\n/* #undef HAVE_STRERROR */\n\n/* Define to 1 if you have the <strings.h> header file. */\n/* #undef HAVE_STRINGS_H */\n\n/* Define to 1 if you have the <string.h> header file. */\n#define HAVE_STRING_H 1\n\n/* Define to 1 if you have the <sys/stat.h> header file. */\n#define HAVE_SYS_STAT_H 1\n\n/* Define to 1 if you have the <sys/types.h> header file. */\n#define HAVE_SYS_TYPES_H 1\n\n/* Define to 1 if you have the <sys/wait.h> header file. */\n/* #undef HAVE_SYS_WAIT_H */\n\n/* Define to 1 if you have the <unistd.h> header file. */\n/* #undef HAVE_UNISTD_H */\n\n/* Define to 1 if the compiler supports GCC compatible visibility\n   declarations. */\n/* #undef HAVE_VISIBILITY */\n\n/* Define to 1 if you have the <wchar.h> header file. */\n#define HAVE_WCHAR_H\n\n/* Define to 1 if you have the <windows.h> header file. */\n#define HAVE_WINDOWS_H 1\n\n/* Define to 1 if you have the <zlib.h> header file. */\n/* #undef HAVE_ZLIB_H */\n\n/* This limits the amount of memory that may be used while matching a pattern.\n   It applies to both pcre2_match() and pcre2_dfa_match(). It does not apply\n   to JIT matching. The value is in kibibytes (units of 1024 bytes). */\n#ifndef HEAP_LIMIT\n#define HEAP_LIMIT 20000000\n#endif\n\n/* The value of LINK_SIZE determines the number of bytes used to store links\n   as offsets within the compiled regex. The default is 2, which allows for\n   compiled patterns up to 65535 code units long. This covers the vast\n   majority of cases. However, PCRE2 can also be compiled to use 3 or 4 bytes\n   instead. This allows for longer patterns in extreme cases. */\n#ifndef LINK_SIZE\n#define LINK_SIZE 2\n#endif\n\n/* Define to the sub-directory where libtool stores uninstalled libraries. */\n/* This is ignored unless you are using libtool. */\n#ifndef LT_OBJDIR\n#define LT_OBJDIR \".libs/\"\n#endif\n\n/* The value of MATCH_LIMIT determines the default number of times the\n   pcre2_match() function can record a backtrack position during a single\n   matching attempt. The value is also used to limit a loop counter in\n   pcre2_dfa_match(). There is a runtime interface for setting a different\n   limit. The limit exists in order to catch runaway regular expressions that\n   take forever to determine that they do not match. The default is set very\n   large so that it does not accidentally catch legitimate cases. */\n#ifndef MATCH_LIMIT\n#define MATCH_LIMIT 10000000\n#endif\n\n/* The above limit applies to all backtracks, whether or not they are nested.\n   In some environments it is desirable to limit the nesting of backtracking\n   (that is, the depth of tree that is searched) more strictly, in order to\n   restrict the maximum amount of heap memory that is used. The value of\n   MATCH_LIMIT_DEPTH provides this facility. To have any useful effect, it\n   must be less than the value of MATCH_LIMIT. The default is to use the same\n   value as MATCH_LIMIT. There is a runtime method for setting a different\n   limit. In the case of pcre2_dfa_match(), this limit controls the depth of\n   the internal nested function calls that are used for pattern recursions,\n   lookarounds, and atomic groups. */\n#ifndef MATCH_LIMIT_DEPTH\n#define MATCH_LIMIT_DEPTH MATCH_LIMIT\n#endif\n\n/* This limit is parameterized just in case anybody ever wants to change it.\n   Care must be taken if it is increased, because it guards against integer\n   overflow caused by enormously large patterns. */\n#ifndef MAX_NAME_COUNT\n#define MAX_NAME_COUNT 10000\n#endif\n\n/* This limit is parameterized just in case anybody ever wants to change it.\n   Care must be taken if it is increased, because it guards against integer\n   overflow caused by enormously large patterns. */\n#ifndef MAX_NAME_SIZE\n#define MAX_NAME_SIZE 128\n#endif\n\n/* The value of MAX_VARLOOKBEHIND specifies the default maximum length, in\n   characters, for a variable-length lookbehind assertion. */\n#ifndef MAX_VARLOOKBEHIND\n#define MAX_VARLOOKBEHIND 255\n#endif\n\n/* Defining NEVER_BACKSLASH_C locks out the use of \\C in all patterns. */\n/* #undef NEVER_BACKSLASH_C */\n\n/* The value of NEWLINE_DEFAULT determines the default newline character\n   sequence. PCRE2 client programs can override this by selecting other values\n   at run time. The valid values are 1 (CR), 2 (LF), 3 (CRLF), 4 (ANY), 5\n   (ANYCRLF), and 6 (NUL). */\n#ifndef NEWLINE_DEFAULT\n#define NEWLINE_DEFAULT 5\n#endif\n\n/* Name of package */\n#define PACKAGE \"pcre2\"\n\n/* Define to the address where bug reports for this package should be sent. */\n#define PACKAGE_BUGREPORT \"\"\n\n/* Define to the full name of this package. */\n#define PACKAGE_NAME \"PCRE2\"\n\n/* Define to the full name and version of this package. */\n#define PACKAGE_STRING \"PCRE2 10.46\"\n\n/* Define to the one symbol short name of this package. */\n#define PACKAGE_TARNAME \"pcre2\"\n\n/* Define to the home page for this package. */\n#define PACKAGE_URL \"\"\n\n/* Define to the version of this package. */\n#define PACKAGE_VERSION \"10.46\"\n\n/* The value of PARENS_NEST_LIMIT specifies the maximum depth of nested\n   parentheses (of any kind) in a pattern. This limits the amount of system\n   stack that is used while compiling a pattern. */\n#ifndef PARENS_NEST_LIMIT\n#define PARENS_NEST_LIMIT 250\n#endif\n\n/* The value of PCRE2GREP_BUFSIZE is the starting size of the buffer used by\n   pcre2grep to hold parts of the file it is searching. The buffer will be\n   expanded up to PCRE2GREP_MAX_BUFSIZE if necessary, for files containing\n   very long lines. The actual amount of memory used by pcre2grep is three\n   times this number, because it allows for the buffering of \"before\" and\n   \"after\" lines. */\n#ifndef PCRE2GREP_BUFSIZE\n#define PCRE2GREP_BUFSIZE 20480\n#endif\n\n/* The value of PCRE2GREP_MAX_BUFSIZE specifies the maximum size of the buffer\n   used by pcre2grep to hold parts of the file it is searching. The actual\n   amount of memory used by pcre2grep is three times this number, because it\n   allows for the buffering of \"before\" and \"after\" lines. */\n#ifndef PCRE2GREP_MAX_BUFSIZE\n#define PCRE2GREP_MAX_BUFSIZE 1048576\n#endif\n\n/* Define to any value to include debugging code. */\n/* #undef PCRE2_DEBUG */\n\n/* to make a symbol visible */\n#define PCRE2_EXPORT\n\n/* If you are compiling for a system other than a Unix-like system or\n   Win32, and it needs some magic to be inserted before the definition\n   of a function that is exported by the library, define this macro to\n   contain the relevant magic. If you do not define this macro, a suitable\n   __declspec value is used for Windows systems; in other environments\n   a compiler relevant \"extern\" is used with any \"visibility\" related\n   attributes from PCRE2_EXPORT included.\n   This macro apears at the start of every exported function that is part\n   of the external API. It does not appear on functions that are \"external\"\n   in the C sense, but which are internal to the library. */\n/* #undef PCRE2_EXP_DEFN */\n\n/* Define to any value if linking statically (TODO: make nice with Libtool) */\n#define PCRE2_STATIC 1\n\n/* Define to necessary symbol if this constant uses a non-standard name on\n   your system. */\n/* #undef PTHREAD_CREATE_JOINABLE */\n\n/* Define to any non-zero number to enable support for SELinux compatible\n   executable memory allocator in JIT. Note that this will have no effect\n   unless SUPPORT_JIT is also defined. */\n/* #undef SLJIT_PROT_EXECUTABLE_ALLOCATOR */\n\n/* Define to 1 if all of the C90 standard headers exist (not just the ones\n   required in a freestanding environment). This macro is provided for\n   backward compatibility; new code need not use it. */\n/* #undef STDC_HEADERS */\n\n/* Define to any value to enable differential fuzzing support. */\n/* #undef SUPPORT_DIFF_FUZZ */\n\n/* Define to any value to enable support for Just-In-Time compiling. */\n/* #undef SUPPORT_JIT */\n\n/* Define to any value to allow pcre2grep to be linked with libbz2, so that it\n   is able to handle .bz2 files. */\n/* #undef SUPPORT_LIBBZ2 */\n\n/* Define to any value to allow pcre2test to be linked with libedit. */\n/* #undef SUPPORT_LIBEDIT */\n\n/* Define to any value to allow pcre2test to be linked with libreadline. */\n/* #undef SUPPORT_LIBREADLINE */\n\n/* Define to any value to allow pcre2grep to be linked with libz, so that it\n   is able to handle .gz files. */\n/* #undef SUPPORT_LIBZ */\n\n/* Define to any value to enable callout script support in pcre2grep. */\n/* #undef SUPPORT_PCRE2GREP_CALLOUT */\n\n/* Define to any value to enable fork support in pcre2grep callout scripts.\n   This will have no effect unless SUPPORT_PCRE2GREP_CALLOUT is also defined.\n   */\n/* #undef SUPPORT_PCRE2GREP_CALLOUT_FORK */\n\n/* Define to any value to enable JIT support in pcre2grep. Note that this will\n   have no effect unless SUPPORT_JIT is also defined. */\n/* #undef SUPPORT_PCRE2GREP_JIT */\n\n/* Define to any value to enable the 16 bit PCRE2 library. */\n#define SUPPORT_PCRE2_16\n\n/* Define to any value to enable the 32 bit PCRE2 library. */\n/* #undef SUPPORT_PCRE2_32 */\n\n/* Define to any value to enable the 8 bit PCRE2 library. */\n/* #undef SUPPORT_PCRE2_8 */\n\n/* Define to any value to enable support for Unicode and UTF encoding. This\n   will work even in an EBCDIC environment, but it is incompatible with the\n   EBCDIC macro. That is, PCRE2 can support *either* EBCDIC code *or*\n   ASCII/Unicode, but not both at once. */\n#define SUPPORT_UNICODE\n\n/* Define to any value for valgrind support to find invalid memory reads. */\n/* #undef SUPPORT_VALGRIND */\n\n/* Enable extensions on AIX 3, Interix.  */\n#ifndef _ALL_SOURCE\n# define _ALL_SOURCE 1\n#endif\n/* Enable general extensions on macOS.  */\n#ifndef _DARWIN_C_SOURCE\n# define _DARWIN_C_SOURCE 1\n#endif\n/* Enable general extensions on Solaris.  */\n#ifndef __EXTENSIONS__\n# define __EXTENSIONS__ 1\n#endif\n/* Enable GNU extensions on systems that have them.  */\n#ifndef _GNU_SOURCE\n# define _GNU_SOURCE 1\n#endif\n/* Enable X/Open compliant socket functions that do not require linking\n   with -lxnet on HP-UX 11.11.  */\n#ifndef _HPUX_ALT_XOPEN_SOCKET_API\n# define _HPUX_ALT_XOPEN_SOCKET_API 1\n#endif\n/* Identify the host operating system as Minix.\n   This macro does not affect the system headers' behavior.\n   A future release of Autoconf may stop defining this macro.  */\n#ifndef _MINIX\n/* # undef _MINIX */\n#endif\n/* Enable general extensions on NetBSD.\n   Enable NetBSD compatibility extensions on Minix.  */\n#ifndef _NETBSD_SOURCE\n# define _NETBSD_SOURCE 1\n#endif\n/* Enable OpenBSD compatibility extensions on NetBSD.\n   Oddly enough, this does nothing on OpenBSD.  */\n#ifndef _OPENBSD_SOURCE\n# define _OPENBSD_SOURCE 1\n#endif\n/* Define to 1 if needed for POSIX-compatible behavior.  */\n#ifndef _POSIX_SOURCE\n/* # undef _POSIX_SOURCE */\n#endif\n/* Define to 2 if needed for POSIX-compatible behavior.  */\n#ifndef _POSIX_1_SOURCE\n/* # undef _POSIX_1_SOURCE */\n#endif\n/* Enable POSIX-compatible threading on Solaris.  */\n#ifndef _POSIX_PTHREAD_SEMANTICS\n# define _POSIX_PTHREAD_SEMANTICS 1\n#endif\n/* Enable extensions specified by ISO/IEC TS 18661-5:2014.  */\n#ifndef __STDC_WANT_IEC_60559_ATTRIBS_EXT__\n# define __STDC_WANT_IEC_60559_ATTRIBS_EXT__ 1\n#endif\n/* Enable extensions specified by ISO/IEC TS 18661-1:2014.  */\n#ifndef __STDC_WANT_IEC_60559_BFP_EXT__\n# define __STDC_WANT_IEC_60559_BFP_EXT__ 1\n#endif\n/* Enable extensions specified by ISO/IEC TS 18661-2:2015.  */\n#ifndef __STDC_WANT_IEC_60559_DFP_EXT__\n# define __STDC_WANT_IEC_60559_DFP_EXT__ 1\n#endif\n/* Enable extensions specified by ISO/IEC TS 18661-4:2015.  */\n#ifndef __STDC_WANT_IEC_60559_FUNCS_EXT__\n# define __STDC_WANT_IEC_60559_FUNCS_EXT__ 1\n#endif\n/* Enable extensions specified by ISO/IEC TS 18661-3:2015.  */\n#ifndef __STDC_WANT_IEC_60559_TYPES_EXT__\n# define __STDC_WANT_IEC_60559_TYPES_EXT__ 1\n#endif\n/* Enable extensions specified by ISO/IEC TR 24731-2:2010.  */\n#ifndef __STDC_WANT_LIB_EXT2__\n# define __STDC_WANT_LIB_EXT2__ 1\n#endif\n/* Enable extensions specified by ISO/IEC 24747:2009.  */\n#ifndef __STDC_WANT_MATH_SPEC_FUNCS__\n# define __STDC_WANT_MATH_SPEC_FUNCS__ 1\n#endif\n/* Enable extensions on HP NonStop.  */\n#ifndef _TANDEM_SOURCE\n# define _TANDEM_SOURCE 1\n#endif\n/* Enable X/Open extensions.  Define to 500 only if necessary\n   to make mbstate_t available.  */\n#ifndef _XOPEN_SOURCE\n/* # undef _XOPEN_SOURCE */\n#endif\n\n/* Version number of package */\n#define VERSION \"10.46\"\n\n/* Number of bits in a file offset, on hosts where this is settable. */\n/* #undef _FILE_OFFSET_BITS */\n\n/* Define for large files, on AIX-style hosts. */\n/* #undef _LARGE_FILES */\n\n/* Define to empty if `const' does not conform to ANSI C. */\n/* #undef const */\n\n/* Define to the type of a signed integer type of width exactly 64 bits if\n   such a type exists and the standard includes do not define it. */\n/* #undef int64_t */\n\n/* Define to `unsigned int' if <sys/types.h> does not define. */\n/* #undef size_t */\n\n// -----------------------\n\n//disable conversion different-size integer warnings\n#pragma warning(disable: 4244)\n#pragma warning(disable: 4267)\n\n// -----------------------\n\n//PCRE library code modifications made for the Au library have comment \"//au\".\n\n// -----------------------\n\n//PCRE project settings:\n\n//Output dir (all config): bin\\$(Platform)\\$(Configuration)\\\n//Static lib, unicode.\n//Preprocessor: ...;HAVE_CONFIG_H;PCRE2_CODE_UNIT_WIDTH=16\n//Run-time lib: multithreaded\n//Disable security checks\n//Warning level 3\n\n// -----------------------\n\n//Exclude files moved to the `excluded` dir.\n\n"
  },
  {
    "path": "Libraries/PCRE/pcre2.h",
    "content": "/*************************************************\n*       Perl-Compatible Regular Expressions      *\n*************************************************/\n\n/* This is the public header file for the PCRE library, second API, to be\n#included by applications that call PCRE2 functions.\n\n           Copyright (c) 2016-2024 University of Cambridge\n\n-----------------------------------------------------------------------------\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n    * Redistributions of source code must retain the above copyright notice,\n      this list of conditions and the following disclaimer.\n\n    * Redistributions in binary form must reproduce the above copyright\n      notice, this list of conditions and the following disclaimer in the\n      documentation and/or other materials provided with the distribution.\n\n    * Neither the name of the University of Cambridge nor the names of its\n      contributors may be used to endorse or promote products derived from\n      this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\nLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\nCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\nSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\nINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\nCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGE.\n-----------------------------------------------------------------------------\n*/\n\n#ifndef PCRE2_H_IDEMPOTENT_GUARD\n#define PCRE2_H_IDEMPOTENT_GUARD\n\n/* The current PCRE version information. */\n\n#define PCRE2_MAJOR           10\n#define PCRE2_MINOR           46\n#define PCRE2_PRERELEASE      \n#define PCRE2_DATE            2025-08-27\n\n/* When an application links to a PCRE DLL in Windows, the symbols that are\nimported have to be identified as such. When building PCRE2, the appropriate\nexport setting is defined in pcre2_internal.h, which includes this file. So we\ndon't change existing definitions of PCRE2_EXP_DECL. */\n\n#if defined(_WIN32) && !defined(PCRE2_STATIC)\n#  ifndef PCRE2_EXP_DECL\n#    define PCRE2_EXP_DECL  extern __declspec(dllimport)\n#  endif\n#endif\n\n/* By default, we use the standard \"extern\" declarations. */\n\n#ifndef PCRE2_EXP_DECL\n#  ifdef __cplusplus\n#    define PCRE2_EXP_DECL  extern \"C\"\n#  else\n#    define PCRE2_EXP_DECL  extern\n#  endif\n#endif\n\n/* When compiling with the MSVC compiler, it is sometimes necessary to include\na \"calling convention\" before exported function names. (This is secondhand\ninformation; I know nothing about MSVC myself). For example, something like\n\n  void __cdecl function(....)\n\nmight be needed. In order so make this easy, all the exported functions have\nPCRE2_CALL_CONVENTION just before their names. It is rarely needed; if not\nset, we ensure here that it has no effect. */\n\n#ifndef PCRE2_CALL_CONVENTION\n#define PCRE2_CALL_CONVENTION\n#endif\n\n/* Have to include limits.h, stdlib.h, and inttypes.h to ensure that size_t and\nuint8_t, UCHAR_MAX, etc are defined. Some systems that do have inttypes.h do\nnot have stdint.h, which is why we use inttypes.h, which according to the C\nstandard is a superset of stdint.h. If inttypes.h is not available the build\nwill break and the relevant values must be provided by some other means. */\n\n#include <limits.h>\n#include <stdlib.h>\n#include <inttypes.h>\n\n/* Allow for C++ users compiling this directly. */\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* The following option bits can be passed to pcre2_compile(), pcre2_match(),\nor pcre2_dfa_match(). PCRE2_NO_UTF_CHECK affects only the function to which it\nis passed. Put these bits at the most significant end of the options word so\nothers can be added next to them */\n\n#define PCRE2_ANCHORED            0x80000000u\n#define PCRE2_NO_UTF_CHECK        0x40000000u\n#define PCRE2_ENDANCHORED         0x20000000u\n\n/* The following option bits can be passed only to pcre2_compile(). However,\nthey may affect compilation, JIT compilation, and/or interpretive execution.\nThe following tags indicate which:\n\nC   alters what is compiled by pcre2_compile()\nJ   alters what is compiled by pcre2_jit_compile()\nM   is inspected during pcre2_match() execution\nD   is inspected during pcre2_dfa_match() execution\n*/\n\n#define PCRE2_ALLOW_EMPTY_CLASS   0x00000001u  /* C       */\n#define PCRE2_ALT_BSUX            0x00000002u  /* C       */\n#define PCRE2_AUTO_CALLOUT        0x00000004u  /* C       */\n#define PCRE2_CASELESS            0x00000008u  /* C       */\n#define PCRE2_DOLLAR_ENDONLY      0x00000010u  /*   J M D */\n#define PCRE2_DOTALL              0x00000020u  /* C       */\n#define PCRE2_DUPNAMES            0x00000040u  /* C       */\n#define PCRE2_EXTENDED            0x00000080u  /* C       */\n#define PCRE2_FIRSTLINE           0x00000100u  /*   J M D */\n#define PCRE2_MATCH_UNSET_BACKREF 0x00000200u  /* C J M   */\n#define PCRE2_MULTILINE           0x00000400u  /* C       */\n#define PCRE2_NEVER_UCP           0x00000800u  /* C       */\n#define PCRE2_NEVER_UTF           0x00001000u  /* C       */\n#define PCRE2_NO_AUTO_CAPTURE     0x00002000u  /* C       */\n#define PCRE2_NO_AUTO_POSSESS     0x00004000u  /* C       */\n#define PCRE2_NO_DOTSTAR_ANCHOR   0x00008000u  /* C       */\n#define PCRE2_NO_START_OPTIMIZE   0x00010000u  /*   J M D */\n#define PCRE2_UCP                 0x00020000u  /* C J M D */\n#define PCRE2_UNGREEDY            0x00040000u  /* C       */\n#define PCRE2_UTF                 0x00080000u  /* C J M D */\n#define PCRE2_NEVER_BACKSLASH_C   0x00100000u  /* C       */\n#define PCRE2_ALT_CIRCUMFLEX      0x00200000u  /*   J M D */\n#define PCRE2_ALT_VERBNAMES       0x00400000u  /* C       */\n#define PCRE2_USE_OFFSET_LIMIT    0x00800000u  /*   J M D */\n#define PCRE2_EXTENDED_MORE       0x01000000u  /* C       */\n#define PCRE2_LITERAL             0x02000000u  /* C       */\n#define PCRE2_MATCH_INVALID_UTF   0x04000000u  /*   J M D */\n#define PCRE2_ALT_EXTENDED_CLASS  0x08000000u  /* C       */\n\n/* An additional compile options word is available in the compile context. */\n\n#define PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES  0x00000001u  /* C */\n#define PCRE2_EXTRA_BAD_ESCAPE_IS_LITERAL    0x00000002u  /* C */\n#define PCRE2_EXTRA_MATCH_WORD               0x00000004u  /* C */\n#define PCRE2_EXTRA_MATCH_LINE               0x00000008u  /* C */\n#define PCRE2_EXTRA_ESCAPED_CR_IS_LF         0x00000010u  /* C */\n#define PCRE2_EXTRA_ALT_BSUX                 0x00000020u  /* C */\n#define PCRE2_EXTRA_ALLOW_LOOKAROUND_BSK     0x00000040u  /* C */\n#define PCRE2_EXTRA_CASELESS_RESTRICT        0x00000080u  /* C */\n#define PCRE2_EXTRA_ASCII_BSD                0x00000100u  /* C */\n#define PCRE2_EXTRA_ASCII_BSS                0x00000200u  /* C */\n#define PCRE2_EXTRA_ASCII_BSW                0x00000400u  /* C */\n#define PCRE2_EXTRA_ASCII_POSIX              0x00000800u  /* C */\n#define PCRE2_EXTRA_ASCII_DIGIT              0x00001000u  /* C */\n#define PCRE2_EXTRA_PYTHON_OCTAL             0x00002000u  /* C */\n#define PCRE2_EXTRA_NO_BS0                   0x00004000u  /* C */\n#define PCRE2_EXTRA_NEVER_CALLOUT            0x00008000u  /* C */\n#define PCRE2_EXTRA_TURKISH_CASING           0x00010000u  /* C */\n\n/* These are for pcre2_jit_compile(). */\n\n#define PCRE2_JIT_COMPLETE        0x00000001u  /* For full matching */\n#define PCRE2_JIT_PARTIAL_SOFT    0x00000002u\n#define PCRE2_JIT_PARTIAL_HARD    0x00000004u\n#define PCRE2_JIT_INVALID_UTF     0x00000100u\n#define PCRE2_JIT_TEST_ALLOC      0x00000200u\n\n/* These are for pcre2_match(), pcre2_dfa_match(), pcre2_jit_match(), and\npcre2_substitute(). Some are allowed only for one of the functions, and in\nthese cases it is noted below. Note that PCRE2_ANCHORED, PCRE2_ENDANCHORED and\nPCRE2_NO_UTF_CHECK can also be passed to these functions (though\npcre2_jit_match() ignores the latter since it bypasses all sanity checks). */\n\n#define PCRE2_NOTBOL                      0x00000001u\n#define PCRE2_NOTEOL                      0x00000002u\n#define PCRE2_NOTEMPTY                    0x00000004u  /* ) These two must be kept */\n#define PCRE2_NOTEMPTY_ATSTART            0x00000008u  /* ) adjacent to each other. */\n#define PCRE2_PARTIAL_SOFT                0x00000010u\n#define PCRE2_PARTIAL_HARD                0x00000020u\n#define PCRE2_DFA_RESTART                 0x00000040u  /* pcre2_dfa_match() only */\n#define PCRE2_DFA_SHORTEST                0x00000080u  /* pcre2_dfa_match() only */\n#define PCRE2_SUBSTITUTE_GLOBAL           0x00000100u  /* pcre2_substitute() only */\n#define PCRE2_SUBSTITUTE_EXTENDED         0x00000200u  /* pcre2_substitute() only */\n#define PCRE2_SUBSTITUTE_UNSET_EMPTY      0x00000400u  /* pcre2_substitute() only */\n#define PCRE2_SUBSTITUTE_UNKNOWN_UNSET    0x00000800u  /* pcre2_substitute() only */\n#define PCRE2_SUBSTITUTE_OVERFLOW_LENGTH  0x00001000u  /* pcre2_substitute() only */\n#define PCRE2_NO_JIT                      0x00002000u  /* not for pcre2_dfa_match() */\n#define PCRE2_COPY_MATCHED_SUBJECT        0x00004000u\n#define PCRE2_SUBSTITUTE_LITERAL          0x00008000u  /* pcre2_substitute() only */\n#define PCRE2_SUBSTITUTE_MATCHED          0x00010000u  /* pcre2_substitute() only */\n#define PCRE2_SUBSTITUTE_REPLACEMENT_ONLY 0x00020000u  /* pcre2_substitute() only */\n#define PCRE2_DISABLE_RECURSELOOP_CHECK   0x00040000u  /* not for pcre2_dfa_match() or pcre2_jit_match() */\n\n/* Options for pcre2_pattern_convert(). */\n\n#define PCRE2_CONVERT_UTF                    0x00000001u\n#define PCRE2_CONVERT_NO_UTF_CHECK           0x00000002u\n#define PCRE2_CONVERT_POSIX_BASIC            0x00000004u\n#define PCRE2_CONVERT_POSIX_EXTENDED         0x00000008u\n#define PCRE2_CONVERT_GLOB                   0x00000010u\n#define PCRE2_CONVERT_GLOB_NO_WILD_SEPARATOR 0x00000030u\n#define PCRE2_CONVERT_GLOB_NO_STARSTAR       0x00000050u\n\n/* Newline and \\R settings, for use in compile contexts. The newline values\nmust be kept in step with values set in config.h and both sets must all be\ngreater than zero. */\n\n#define PCRE2_NEWLINE_CR          1\n#define PCRE2_NEWLINE_LF          2\n#define PCRE2_NEWLINE_CRLF        3\n#define PCRE2_NEWLINE_ANY         4\n#define PCRE2_NEWLINE_ANYCRLF     5\n#define PCRE2_NEWLINE_NUL         6\n\n#define PCRE2_BSR_UNICODE         1\n#define PCRE2_BSR_ANYCRLF         2\n\n/* Error codes for pcre2_compile(). Some of these are also used by\npcre2_pattern_convert(). */\n\n#define PCRE2_ERROR_END_BACKSLASH                  101\n#define PCRE2_ERROR_END_BACKSLASH_C                102\n#define PCRE2_ERROR_UNKNOWN_ESCAPE                 103\n#define PCRE2_ERROR_QUANTIFIER_OUT_OF_ORDER        104\n#define PCRE2_ERROR_QUANTIFIER_TOO_BIG             105\n#define PCRE2_ERROR_MISSING_SQUARE_BRACKET         106\n#define PCRE2_ERROR_ESCAPE_INVALID_IN_CLASS        107\n#define PCRE2_ERROR_CLASS_RANGE_ORDER              108\n#define PCRE2_ERROR_QUANTIFIER_INVALID             109\n#define PCRE2_ERROR_INTERNAL_UNEXPECTED_REPEAT     110\n#define PCRE2_ERROR_INVALID_AFTER_PARENS_QUERY     111\n#define PCRE2_ERROR_POSIX_CLASS_NOT_IN_CLASS       112\n#define PCRE2_ERROR_POSIX_NO_SUPPORT_COLLATING     113\n#define PCRE2_ERROR_MISSING_CLOSING_PARENTHESIS    114\n#define PCRE2_ERROR_BAD_SUBPATTERN_REFERENCE       115\n#define PCRE2_ERROR_NULL_PATTERN                   116\n#define PCRE2_ERROR_BAD_OPTIONS                    117\n#define PCRE2_ERROR_MISSING_COMMENT_CLOSING        118\n#define PCRE2_ERROR_PARENTHESES_NEST_TOO_DEEP      119\n#define PCRE2_ERROR_PATTERN_TOO_LARGE              120\n#define PCRE2_ERROR_HEAP_FAILED                    121\n#define PCRE2_ERROR_UNMATCHED_CLOSING_PARENTHESIS  122\n#define PCRE2_ERROR_INTERNAL_CODE_OVERFLOW         123\n#define PCRE2_ERROR_MISSING_CONDITION_CLOSING      124\n#define PCRE2_ERROR_LOOKBEHIND_NOT_FIXED_LENGTH    125\n#define PCRE2_ERROR_ZERO_RELATIVE_REFERENCE        126\n#define PCRE2_ERROR_TOO_MANY_CONDITION_BRANCHES    127\n#define PCRE2_ERROR_CONDITION_ASSERTION_EXPECTED   128\n#define PCRE2_ERROR_BAD_RELATIVE_REFERENCE         129\n#define PCRE2_ERROR_UNKNOWN_POSIX_CLASS            130\n#define PCRE2_ERROR_INTERNAL_STUDY_ERROR           131\n#define PCRE2_ERROR_UNICODE_NOT_SUPPORTED          132\n#define PCRE2_ERROR_PARENTHESES_STACK_CHECK        133\n#define PCRE2_ERROR_CODE_POINT_TOO_BIG             134\n#define PCRE2_ERROR_LOOKBEHIND_TOO_COMPLICATED     135\n#define PCRE2_ERROR_LOOKBEHIND_INVALID_BACKSLASH_C 136\n#define PCRE2_ERROR_UNSUPPORTED_ESCAPE_SEQUENCE    137\n#define PCRE2_ERROR_CALLOUT_NUMBER_TOO_BIG         138\n#define PCRE2_ERROR_MISSING_CALLOUT_CLOSING        139\n#define PCRE2_ERROR_ESCAPE_INVALID_IN_VERB         140\n#define PCRE2_ERROR_UNRECOGNIZED_AFTER_QUERY_P     141\n#define PCRE2_ERROR_MISSING_NAME_TERMINATOR        142\n#define PCRE2_ERROR_DUPLICATE_SUBPATTERN_NAME      143\n#define PCRE2_ERROR_INVALID_SUBPATTERN_NAME        144\n#define PCRE2_ERROR_UNICODE_PROPERTIES_UNAVAILABLE 145\n#define PCRE2_ERROR_MALFORMED_UNICODE_PROPERTY     146\n#define PCRE2_ERROR_UNKNOWN_UNICODE_PROPERTY       147\n#define PCRE2_ERROR_SUBPATTERN_NAME_TOO_LONG       148\n#define PCRE2_ERROR_TOO_MANY_NAMED_SUBPATTERNS     149\n#define PCRE2_ERROR_CLASS_INVALID_RANGE            150\n#define PCRE2_ERROR_OCTAL_BYTE_TOO_BIG             151\n#define PCRE2_ERROR_INTERNAL_OVERRAN_WORKSPACE     152\n#define PCRE2_ERROR_INTERNAL_MISSING_SUBPATTERN    153\n#define PCRE2_ERROR_DEFINE_TOO_MANY_BRANCHES       154\n#define PCRE2_ERROR_BACKSLASH_O_MISSING_BRACE      155\n#define PCRE2_ERROR_INTERNAL_UNKNOWN_NEWLINE       156\n#define PCRE2_ERROR_BACKSLASH_G_SYNTAX             157\n#define PCRE2_ERROR_PARENS_QUERY_R_MISSING_CLOSING 158\n/* Error 159 is obsolete and should now never occur */\n#define PCRE2_ERROR_VERB_ARGUMENT_NOT_ALLOWED      159\n#define PCRE2_ERROR_VERB_UNKNOWN                   160\n#define PCRE2_ERROR_SUBPATTERN_NUMBER_TOO_BIG      161\n#define PCRE2_ERROR_SUBPATTERN_NAME_EXPECTED       162\n#define PCRE2_ERROR_INTERNAL_PARSED_OVERFLOW       163\n#define PCRE2_ERROR_INVALID_OCTAL                  164\n#define PCRE2_ERROR_SUBPATTERN_NAMES_MISMATCH      165\n#define PCRE2_ERROR_MARK_MISSING_ARGUMENT          166\n#define PCRE2_ERROR_INVALID_HEXADECIMAL            167\n#define PCRE2_ERROR_BACKSLASH_C_SYNTAX             168\n#define PCRE2_ERROR_BACKSLASH_K_SYNTAX             169\n#define PCRE2_ERROR_INTERNAL_BAD_CODE_LOOKBEHINDS  170\n#define PCRE2_ERROR_BACKSLASH_N_IN_CLASS           171\n#define PCRE2_ERROR_CALLOUT_STRING_TOO_LONG        172\n#define PCRE2_ERROR_UNICODE_DISALLOWED_CODE_POINT  173\n#define PCRE2_ERROR_UTF_IS_DISABLED                174\n#define PCRE2_ERROR_UCP_IS_DISABLED                175\n#define PCRE2_ERROR_VERB_NAME_TOO_LONG             176\n#define PCRE2_ERROR_BACKSLASH_U_CODE_POINT_TOO_BIG 177\n#define PCRE2_ERROR_MISSING_OCTAL_OR_HEX_DIGITS    178\n#define PCRE2_ERROR_VERSION_CONDITION_SYNTAX       179\n#define PCRE2_ERROR_INTERNAL_BAD_CODE_AUTO_POSSESS 180\n#define PCRE2_ERROR_CALLOUT_NO_STRING_DELIMITER    181\n#define PCRE2_ERROR_CALLOUT_BAD_STRING_DELIMITER   182\n#define PCRE2_ERROR_BACKSLASH_C_CALLER_DISABLED    183\n#define PCRE2_ERROR_QUERY_BARJX_NEST_TOO_DEEP      184\n#define PCRE2_ERROR_BACKSLASH_C_LIBRARY_DISABLED   185\n#define PCRE2_ERROR_PATTERN_TOO_COMPLICATED        186\n#define PCRE2_ERROR_LOOKBEHIND_TOO_LONG            187\n#define PCRE2_ERROR_PATTERN_STRING_TOO_LONG        188\n#define PCRE2_ERROR_INTERNAL_BAD_CODE              189\n#define PCRE2_ERROR_INTERNAL_BAD_CODE_IN_SKIP      190\n#define PCRE2_ERROR_NO_SURROGATES_IN_UTF16         191\n#define PCRE2_ERROR_BAD_LITERAL_OPTIONS            192\n#define PCRE2_ERROR_SUPPORTED_ONLY_IN_UNICODE      193\n#define PCRE2_ERROR_INVALID_HYPHEN_IN_OPTIONS      194\n#define PCRE2_ERROR_ALPHA_ASSERTION_UNKNOWN        195\n#define PCRE2_ERROR_SCRIPT_RUN_NOT_AVAILABLE       196\n#define PCRE2_ERROR_TOO_MANY_CAPTURES              197\n#define PCRE2_ERROR_MISSING_OCTAL_DIGIT            198\n#define PCRE2_ERROR_BACKSLASH_K_IN_LOOKAROUND      199\n#define PCRE2_ERROR_MAX_VAR_LOOKBEHIND_EXCEEDED    200\n#define PCRE2_ERROR_PATTERN_COMPILED_SIZE_TOO_BIG  201\n#define PCRE2_ERROR_OVERSIZE_PYTHON_OCTAL          202\n#define PCRE2_ERROR_CALLOUT_CALLER_DISABLED        203\n#define PCRE2_ERROR_EXTRA_CASING_REQUIRES_UNICODE  204\n#define PCRE2_ERROR_TURKISH_CASING_REQUIRES_UTF    205\n#define PCRE2_ERROR_EXTRA_CASING_INCOMPATIBLE      206\n#define PCRE2_ERROR_ECLASS_NEST_TOO_DEEP           207\n#define PCRE2_ERROR_ECLASS_INVALID_OPERATOR        208\n#define PCRE2_ERROR_ECLASS_UNEXPECTED_OPERATOR     209\n#define PCRE2_ERROR_ECLASS_EXPECTED_OPERAND        210\n#define PCRE2_ERROR_ECLASS_MIXED_OPERATORS         211\n#define PCRE2_ERROR_ECLASS_HINT_SQUARE_BRACKET     212\n#define PCRE2_ERROR_PERL_ECLASS_UNEXPECTED_EXPR    213\n#define PCRE2_ERROR_PERL_ECLASS_EMPTY_EXPR         214\n#define PCRE2_ERROR_PERL_ECLASS_MISSING_CLOSE      215\n#define PCRE2_ERROR_PERL_ECLASS_UNEXPECTED_CHAR    216\n\n/* \"Expected\" matching error codes: no match and partial match. */\n\n#define PCRE2_ERROR_NOMATCH          (-1)\n#define PCRE2_ERROR_PARTIAL          (-2)\n\n/* Error codes for UTF-8 validity checks */\n\n#define PCRE2_ERROR_UTF8_ERR1        (-3)\n#define PCRE2_ERROR_UTF8_ERR2        (-4)\n#define PCRE2_ERROR_UTF8_ERR3        (-5)\n#define PCRE2_ERROR_UTF8_ERR4        (-6)\n#define PCRE2_ERROR_UTF8_ERR5        (-7)\n#define PCRE2_ERROR_UTF8_ERR6        (-8)\n#define PCRE2_ERROR_UTF8_ERR7        (-9)\n#define PCRE2_ERROR_UTF8_ERR8       (-10)\n#define PCRE2_ERROR_UTF8_ERR9       (-11)\n#define PCRE2_ERROR_UTF8_ERR10      (-12)\n#define PCRE2_ERROR_UTF8_ERR11      (-13)\n#define PCRE2_ERROR_UTF8_ERR12      (-14)\n#define PCRE2_ERROR_UTF8_ERR13      (-15)\n#define PCRE2_ERROR_UTF8_ERR14      (-16)\n#define PCRE2_ERROR_UTF8_ERR15      (-17)\n#define PCRE2_ERROR_UTF8_ERR16      (-18)\n#define PCRE2_ERROR_UTF8_ERR17      (-19)\n#define PCRE2_ERROR_UTF8_ERR18      (-20)\n#define PCRE2_ERROR_UTF8_ERR19      (-21)\n#define PCRE2_ERROR_UTF8_ERR20      (-22)\n#define PCRE2_ERROR_UTF8_ERR21      (-23)\n\n/* Error codes for UTF-16 validity checks */\n\n#define PCRE2_ERROR_UTF16_ERR1      (-24)\n#define PCRE2_ERROR_UTF16_ERR2      (-25)\n#define PCRE2_ERROR_UTF16_ERR3      (-26)\n\n/* Error codes for UTF-32 validity checks */\n\n#define PCRE2_ERROR_UTF32_ERR1      (-27)\n#define PCRE2_ERROR_UTF32_ERR2      (-28)\n\n/* Miscellaneous error codes for pcre2[_dfa]_match(), substring extraction\nfunctions, context functions, and serializing functions. They are in numerical\norder. Originally they were in alphabetical order too, but now that PCRE2 is\nreleased, the numbers must not be changed. */\n\n#define PCRE2_ERROR_BADDATA           (-29)\n#define PCRE2_ERROR_MIXEDTABLES       (-30)  /* Name was changed */\n#define PCRE2_ERROR_BADMAGIC          (-31)\n#define PCRE2_ERROR_BADMODE           (-32)\n#define PCRE2_ERROR_BADOFFSET         (-33)\n#define PCRE2_ERROR_BADOPTION         (-34)\n#define PCRE2_ERROR_BADREPLACEMENT    (-35)\n#define PCRE2_ERROR_BADUTFOFFSET      (-36)\n#define PCRE2_ERROR_CALLOUT           (-37)  /* Never used by PCRE2 itself */\n#define PCRE2_ERROR_DFA_BADRESTART    (-38)\n#define PCRE2_ERROR_DFA_RECURSE       (-39)\n#define PCRE2_ERROR_DFA_UCOND         (-40)\n#define PCRE2_ERROR_DFA_UFUNC         (-41)\n#define PCRE2_ERROR_DFA_UITEM         (-42)\n#define PCRE2_ERROR_DFA_WSSIZE        (-43)\n#define PCRE2_ERROR_INTERNAL          (-44)\n#define PCRE2_ERROR_JIT_BADOPTION     (-45)\n#define PCRE2_ERROR_JIT_STACKLIMIT    (-46)\n#define PCRE2_ERROR_MATCHLIMIT        (-47)\n#define PCRE2_ERROR_NOMEMORY          (-48)\n#define PCRE2_ERROR_NOSUBSTRING       (-49)\n#define PCRE2_ERROR_NOUNIQUESUBSTRING (-50)\n#define PCRE2_ERROR_NULL              (-51)\n#define PCRE2_ERROR_RECURSELOOP       (-52)\n#define PCRE2_ERROR_DEPTHLIMIT        (-53)\n#define PCRE2_ERROR_RECURSIONLIMIT    (-53)  /* Obsolete synonym */\n#define PCRE2_ERROR_UNAVAILABLE       (-54)\n#define PCRE2_ERROR_UNSET             (-55)\n#define PCRE2_ERROR_BADOFFSETLIMIT    (-56)\n#define PCRE2_ERROR_BADREPESCAPE      (-57)\n#define PCRE2_ERROR_REPMISSINGBRACE   (-58)\n#define PCRE2_ERROR_BADSUBSTITUTION   (-59)\n#define PCRE2_ERROR_BADSUBSPATTERN    (-60)\n#define PCRE2_ERROR_TOOMANYREPLACE    (-61)\n#define PCRE2_ERROR_BADSERIALIZEDDATA (-62)\n#define PCRE2_ERROR_HEAPLIMIT         (-63)\n#define PCRE2_ERROR_CONVERT_SYNTAX    (-64)\n#define PCRE2_ERROR_INTERNAL_DUPMATCH (-65)\n#define PCRE2_ERROR_DFA_UINVALID_UTF  (-66)\n#define PCRE2_ERROR_INVALIDOFFSET     (-67)\n#define PCRE2_ERROR_JIT_UNSUPPORTED   (-68)\n#define PCRE2_ERROR_REPLACECASE       (-69)\n#define PCRE2_ERROR_TOOLARGEREPLACE   (-70)\n\n\n/* Request types for pcre2_pattern_info() */\n\n#define PCRE2_INFO_ALLOPTIONS            0\n#define PCRE2_INFO_ARGOPTIONS            1\n#define PCRE2_INFO_BACKREFMAX            2\n#define PCRE2_INFO_BSR                   3\n#define PCRE2_INFO_CAPTURECOUNT          4\n#define PCRE2_INFO_FIRSTCODEUNIT         5\n#define PCRE2_INFO_FIRSTCODETYPE         6\n#define PCRE2_INFO_FIRSTBITMAP           7\n#define PCRE2_INFO_HASCRORLF             8\n#define PCRE2_INFO_JCHANGED              9\n#define PCRE2_INFO_JITSIZE              10\n#define PCRE2_INFO_LASTCODEUNIT         11\n#define PCRE2_INFO_LASTCODETYPE         12\n#define PCRE2_INFO_MATCHEMPTY           13\n#define PCRE2_INFO_MATCHLIMIT           14\n#define PCRE2_INFO_MAXLOOKBEHIND        15\n#define PCRE2_INFO_MINLENGTH            16\n#define PCRE2_INFO_NAMECOUNT            17\n#define PCRE2_INFO_NAMEENTRYSIZE        18\n#define PCRE2_INFO_NAMETABLE            19\n#define PCRE2_INFO_NEWLINE              20\n#define PCRE2_INFO_DEPTHLIMIT           21\n#define PCRE2_INFO_RECURSIONLIMIT       21  /* Obsolete synonym */\n#define PCRE2_INFO_SIZE                 22\n#define PCRE2_INFO_HASBACKSLASHC        23\n#define PCRE2_INFO_FRAMESIZE            24\n#define PCRE2_INFO_HEAPLIMIT            25\n#define PCRE2_INFO_EXTRAOPTIONS         26\n\n/* Request types for pcre2_config(). */\n\n#define PCRE2_CONFIG_BSR                     0\n#define PCRE2_CONFIG_JIT                     1\n#define PCRE2_CONFIG_JITTARGET               2\n#define PCRE2_CONFIG_LINKSIZE                3\n#define PCRE2_CONFIG_MATCHLIMIT              4\n#define PCRE2_CONFIG_NEWLINE                 5\n#define PCRE2_CONFIG_PARENSLIMIT             6\n#define PCRE2_CONFIG_DEPTHLIMIT              7\n#define PCRE2_CONFIG_RECURSIONLIMIT          7  /* Obsolete synonym */\n#define PCRE2_CONFIG_STACKRECURSE            8  /* Obsolete */\n#define PCRE2_CONFIG_UNICODE                 9\n#define PCRE2_CONFIG_UNICODE_VERSION        10\n#define PCRE2_CONFIG_VERSION                11\n#define PCRE2_CONFIG_HEAPLIMIT              12\n#define PCRE2_CONFIG_NEVER_BACKSLASH_C      13\n#define PCRE2_CONFIG_COMPILED_WIDTHS        14\n#define PCRE2_CONFIG_TABLES_LENGTH          15\n\n/* Optimization directives for pcre2_set_optimize().\nFor binary compatibility, only add to this list; do not renumber. */\n\n#define PCRE2_OPTIMIZATION_NONE    0\n#define PCRE2_OPTIMIZATION_FULL    1\n\n#define PCRE2_AUTO_POSSESS         64\n#define PCRE2_AUTO_POSSESS_OFF     65\n#define PCRE2_DOTSTAR_ANCHOR       66\n#define PCRE2_DOTSTAR_ANCHOR_OFF   67\n#define PCRE2_START_OPTIMIZE       68\n#define PCRE2_START_OPTIMIZE_OFF   69\n\n/* Types used in pcre2_set_substitute_case_callout().\n\nPCRE2_SUBSTITUTE_CASE_LOWER and PCRE2_SUBSTITUTE_CASE_UPPER are passed to the\ncallout to indicate that the case of the entire callout input should be\ncase-transformed. PCRE2_SUBSTITUTE_CASE_TITLE_FIRST is passed to indicate that\nonly the first character or glyph should be transformed to Unicode titlecase,\nand the rest to lowercase. */\n\n#define PCRE2_SUBSTITUTE_CASE_LOWER        1\n#define PCRE2_SUBSTITUTE_CASE_UPPER        2\n#define PCRE2_SUBSTITUTE_CASE_TITLE_FIRST  3\n\n/* Types for code units in patterns and subject strings. */\n\ntypedef uint8_t  PCRE2_UCHAR8;\n\n//au:\n//typedef uint16_t PCRE2_UCHAR16;\ntypedef wchar_t PCRE2_UCHAR16;\n\ntypedef uint32_t PCRE2_UCHAR32;\n\ntypedef const PCRE2_UCHAR8  *PCRE2_SPTR8;\ntypedef const PCRE2_UCHAR16 *PCRE2_SPTR16;\ntypedef const PCRE2_UCHAR32 *PCRE2_SPTR32;\n\n/* The PCRE2_SIZE type is used for all string lengths and offsets in PCRE2,\nincluding pattern offsets for errors and subject offsets after a match. We\ndefine special values to indicate zero-terminated strings and unset offsets in\nthe offset vector (ovector). */\n\n#define PCRE2_SIZE            size_t\n#define PCRE2_SIZE_MAX        SIZE_MAX\n#define PCRE2_ZERO_TERMINATED (~(PCRE2_SIZE)0)\n#define PCRE2_UNSET           (~(PCRE2_SIZE)0)\n\n/* Generic types for opaque structures and JIT callback functions. These\ndeclarations are defined in a macro that is expanded for each width later. */\n\n#define PCRE2_TYPES_LIST \\\nstruct pcre2_real_general_context; \\\ntypedef struct pcre2_real_general_context pcre2_general_context; \\\n\\\nstruct pcre2_real_compile_context; \\\ntypedef struct pcre2_real_compile_context pcre2_compile_context; \\\n\\\nstruct pcre2_real_match_context; \\\ntypedef struct pcre2_real_match_context pcre2_match_context; \\\n\\\nstruct pcre2_real_convert_context; \\\ntypedef struct pcre2_real_convert_context pcre2_convert_context; \\\n\\\nstruct pcre2_real_code; \\\ntypedef struct pcre2_real_code pcre2_code; \\\n\\\nstruct pcre2_real_match_data; \\\ntypedef struct pcre2_real_match_data pcre2_match_data; \\\n\\\nstruct pcre2_real_jit_stack; \\\ntypedef struct pcre2_real_jit_stack pcre2_jit_stack; \\\n\\\ntypedef pcre2_jit_stack *(*pcre2_jit_callback)(void *);\n\n\n/* The structures for passing out data via callout functions. We use structures\nso that new fields can be added on the end in future versions, without changing\nthe API of the function, thereby allowing old clients to work without\nmodification. Define the generic versions in a macro; the width-specific\nversions are generated from this macro below. */\n\n/* Flags for the callout_flags field. These are cleared after a callout. */\n\n#define PCRE2_CALLOUT_STARTMATCH    0x00000001u  /* Set for each bumpalong */\n#define PCRE2_CALLOUT_BACKTRACK     0x00000002u  /* Set after a backtrack */\n\n#define PCRE2_STRUCTURE_LIST \\\ntypedef struct pcre2_callout_block { \\\n  uint32_t      version;           /* Identifies version of block */ \\\n  /* ------------------------ Version 0 ------------------------------- */ \\\n  uint32_t      callout_number;    /* Number compiled into pattern */ \\\n  uint32_t      capture_top;       /* Max current capture */ \\\n  uint32_t      capture_last;      /* Most recently closed capture */ \\\n  PCRE2_SIZE   *offset_vector;     /* The offset vector */ \\\n  PCRE2_SPTR    mark;              /* Pointer to current mark or NULL */ \\\n  PCRE2_SPTR    subject;           /* The subject being matched */ \\\n  PCRE2_SIZE    subject_length;    /* The length of the subject */ \\\n  PCRE2_SIZE    start_match;       /* Offset to start of this match attempt */ \\\n  PCRE2_SIZE    current_position;  /* Where we currently are in the subject */ \\\n  PCRE2_SIZE    pattern_position;  /* Offset to next item in the pattern */ \\\n  PCRE2_SIZE    next_item_length;  /* Length of next item in the pattern */ \\\n  /* ------------------- Added for Version 1 -------------------------- */ \\\n  PCRE2_SIZE    callout_string_offset; /* Offset to string within pattern */ \\\n  PCRE2_SIZE    callout_string_length; /* Length of string compiled into pattern */ \\\n  PCRE2_SPTR    callout_string;    /* String compiled into pattern */ \\\n  /* ------------------- Added for Version 2 -------------------------- */ \\\n  uint32_t      callout_flags;     /* See above for list */ \\\n  /* ------------------------------------------------------------------ */ \\\n} pcre2_callout_block; \\\n\\\ntypedef struct pcre2_callout_enumerate_block { \\\n  uint32_t      version;           /* Identifies version of block */ \\\n  /* ------------------------ Version 0 ------------------------------- */ \\\n  PCRE2_SIZE    pattern_position;  /* Offset to next item in the pattern */ \\\n  PCRE2_SIZE    next_item_length;  /* Length of next item in the pattern */ \\\n  uint32_t      callout_number;    /* Number compiled into pattern */ \\\n  PCRE2_SIZE    callout_string_offset; /* Offset to string within pattern */ \\\n  PCRE2_SIZE    callout_string_length; /* Length of string compiled into pattern */ \\\n  PCRE2_SPTR    callout_string;    /* String compiled into pattern */ \\\n  /* ------------------------------------------------------------------ */ \\\n} pcre2_callout_enumerate_block; \\\n\\\ntypedef struct pcre2_substitute_callout_block { \\\n  uint32_t      version;           /* Identifies version of block */ \\\n  /* ------------------------ Version 0 ------------------------------- */ \\\n  PCRE2_SPTR    input;             /* Pointer to input subject string */ \\\n  PCRE2_SPTR    output;            /* Pointer to output buffer */ \\\n  PCRE2_SIZE    output_offsets[2]; /* Changed portion of the output */ \\\n  PCRE2_SIZE   *ovector;           /* Pointer to current ovector */ \\\n  uint32_t      oveccount;         /* Count of pairs set in ovector */ \\\n  uint32_t      subscount;         /* Substitution number */ \\\n  /* ------------------------------------------------------------------ */ \\\n} pcre2_substitute_callout_block;\n\n\n/* List the generic forms of all other functions in macros, which will be\nexpanded for each width below. Start with functions that give general\ninformation. */\n\n#define PCRE2_GENERAL_INFO_FUNCTIONS \\\nPCRE2_EXP_DECL int PCRE2_CALL_CONVENTION pcre2_config(uint32_t, void *);\n\n\n/* Functions for manipulating contexts. */\n\n#define PCRE2_GENERAL_CONTEXT_FUNCTIONS \\\nPCRE2_EXP_DECL pcre2_general_context *PCRE2_CALL_CONVENTION \\\n  pcre2_general_context_copy(pcre2_general_context *); \\\nPCRE2_EXP_DECL pcre2_general_context *PCRE2_CALL_CONVENTION \\\n  pcre2_general_context_create(void *(*)(size_t, void *), \\\n    void (*)(void *, void *), void *); \\\nPCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \\\n  pcre2_general_context_free(pcre2_general_context *);\n\n#define PCRE2_COMPILE_CONTEXT_FUNCTIONS \\\nPCRE2_EXP_DECL pcre2_compile_context *PCRE2_CALL_CONVENTION \\\n  pcre2_compile_context_copy(pcre2_compile_context *); \\\nPCRE2_EXP_DECL pcre2_compile_context *PCRE2_CALL_CONVENTION \\\n  pcre2_compile_context_create(pcre2_general_context *);\\\nPCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \\\n  pcre2_compile_context_free(pcre2_compile_context *); \\\nPCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \\\n  pcre2_set_bsr(pcre2_compile_context *, uint32_t); \\\nPCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \\\n  pcre2_set_character_tables(pcre2_compile_context *, const uint8_t *); \\\nPCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \\\n  pcre2_set_compile_extra_options(pcre2_compile_context *, uint32_t); \\\nPCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \\\n  pcre2_set_max_pattern_length(pcre2_compile_context *, PCRE2_SIZE); \\\nPCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \\\n  pcre2_set_max_pattern_compiled_length(pcre2_compile_context *, PCRE2_SIZE); \\\nPCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \\\n  pcre2_set_max_varlookbehind(pcre2_compile_context *, uint32_t); \\\nPCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \\\n  pcre2_set_newline(pcre2_compile_context *, uint32_t); \\\nPCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \\\n  pcre2_set_parens_nest_limit(pcre2_compile_context *, uint32_t); \\\nPCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \\\n  pcre2_set_compile_recursion_guard(pcre2_compile_context *, \\\n    int (*)(uint32_t, void *), void *); \\\nPCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \\\n  pcre2_set_optimize(pcre2_compile_context *, uint32_t);\n\n#define PCRE2_MATCH_CONTEXT_FUNCTIONS \\\nPCRE2_EXP_DECL pcre2_match_context *PCRE2_CALL_CONVENTION \\\n  pcre2_match_context_copy(pcre2_match_context *); \\\nPCRE2_EXP_DECL pcre2_match_context *PCRE2_CALL_CONVENTION \\\n  pcre2_match_context_create(pcre2_general_context *); \\\nPCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \\\n  pcre2_match_context_free(pcre2_match_context *); \\\nPCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \\\n  pcre2_set_callout(pcre2_match_context *, \\\n    int (*)(pcre2_callout_block *, void *), void *); \\\nPCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \\\n  pcre2_set_substitute_callout(pcre2_match_context *, \\\n    int (*)(pcre2_substitute_callout_block *, void *), void *); \\\nPCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \\\n  pcre2_set_substitute_case_callout(pcre2_match_context *, \\\n    PCRE2_SIZE (*)(PCRE2_SPTR, PCRE2_SIZE, PCRE2_UCHAR *, PCRE2_SIZE, int, \\\n                   void *), \\\n    void *); \\\nPCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \\\n  pcre2_set_depth_limit(pcre2_match_context *, uint32_t); \\\nPCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \\\n  pcre2_set_heap_limit(pcre2_match_context *, uint32_t); \\\nPCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \\\n  pcre2_set_match_limit(pcre2_match_context *, uint32_t); \\\nPCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \\\n  pcre2_set_offset_limit(pcre2_match_context *, PCRE2_SIZE); \\\nPCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \\\n  pcre2_set_recursion_limit(pcre2_match_context *, uint32_t); \\\nPCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \\\n  pcre2_set_recursion_memory_management(pcre2_match_context *, \\\n    void *(*)(size_t, void *), void (*)(void *, void *), void *);\n\n#define PCRE2_CONVERT_CONTEXT_FUNCTIONS \\\nPCRE2_EXP_DECL pcre2_convert_context *PCRE2_CALL_CONVENTION \\\n  pcre2_convert_context_copy(pcre2_convert_context *); \\\nPCRE2_EXP_DECL pcre2_convert_context *PCRE2_CALL_CONVENTION \\\n  pcre2_convert_context_create(pcre2_general_context *); \\\nPCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \\\n  pcre2_convert_context_free(pcre2_convert_context *); \\\nPCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \\\n  pcre2_set_glob_escape(pcre2_convert_context *, uint32_t); \\\nPCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \\\n  pcre2_set_glob_separator(pcre2_convert_context *, uint32_t);\n\n\n/* Functions concerned with compiling a pattern to PCRE internal code. */\n\n#define PCRE2_COMPILE_FUNCTIONS \\\nPCRE2_EXP_DECL pcre2_code *PCRE2_CALL_CONVENTION \\\n  pcre2_compile(PCRE2_SPTR, PCRE2_SIZE, uint32_t, int *, PCRE2_SIZE *, \\\n    pcre2_compile_context *); \\\nPCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \\\n  pcre2_code_free(pcre2_code *); \\\nPCRE2_EXP_DECL pcre2_code *PCRE2_CALL_CONVENTION \\\n  pcre2_code_copy(const pcre2_code *); \\\nPCRE2_EXP_DECL pcre2_code *PCRE2_CALL_CONVENTION \\\n  pcre2_code_copy_with_tables(const pcre2_code *);\n\n\n/* Functions that give information about a compiled pattern. */\n\n#define PCRE2_PATTERN_INFO_FUNCTIONS \\\nPCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \\\n  pcre2_pattern_info(const pcre2_code *, uint32_t, void *); \\\nPCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \\\n  pcre2_callout_enumerate(const pcre2_code *, \\\n    int (*)(pcre2_callout_enumerate_block *, void *), void *);\n\n\n/* Functions for running a match and inspecting the result. */\n\n#define PCRE2_MATCH_FUNCTIONS \\\nPCRE2_EXP_DECL pcre2_match_data *PCRE2_CALL_CONVENTION \\\n  pcre2_match_data_create(uint32_t, pcre2_general_context *); \\\nPCRE2_EXP_DECL pcre2_match_data *PCRE2_CALL_CONVENTION \\\n  pcre2_match_data_create_from_pattern(const pcre2_code *, \\\n    pcre2_general_context *); \\\nPCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \\\n  pcre2_dfa_match(const pcre2_code *, PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE, \\\n    uint32_t, pcre2_match_data *, pcre2_match_context *, int *, PCRE2_SIZE); \\\nPCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \\\n  pcre2_match(const pcre2_code *, PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE, \\\n    uint32_t, pcre2_match_data *, pcre2_match_context *, int(*callout)(pcre2_callout_block *, void *)); \\\nPCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \\\n  pcre2_match_data_free(pcre2_match_data *); \\\nPCRE2_EXP_DECL PCRE2_SPTR PCRE2_CALL_CONVENTION \\\n  pcre2_get_mark(pcre2_match_data *); \\\nPCRE2_EXP_DECL PCRE2_SIZE PCRE2_CALL_CONVENTION \\\n  pcre2_get_match_data_size(pcre2_match_data *); \\\nPCRE2_EXP_DECL PCRE2_SIZE PCRE2_CALL_CONVENTION \\\n  pcre2_get_match_data_heapframes_size(pcre2_match_data *); \\\nPCRE2_EXP_DECL uint32_t PCRE2_CALL_CONVENTION \\\n  pcre2_get_ovector_count(pcre2_match_data *); \\\nPCRE2_EXP_DECL PCRE2_SIZE *PCRE2_CALL_CONVENTION \\\n  pcre2_get_ovector_pointer(pcre2_match_data *); \\\nPCRE2_EXP_DECL PCRE2_SIZE PCRE2_CALL_CONVENTION \\\n  pcre2_get_startchar(pcre2_match_data *);\n\n//au: added pcre2_match parameter: , int(*callout)(pcre2_callout_block *, void *)\n\n/* Convenience functions for handling matched substrings. */\n\n#define PCRE2_SUBSTRING_FUNCTIONS \\\nPCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \\\n  pcre2_substring_copy_byname(pcre2_match_data *, PCRE2_SPTR, PCRE2_UCHAR *, \\\n    PCRE2_SIZE *); \\\nPCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \\\n  pcre2_substring_copy_bynumber(pcre2_match_data *, uint32_t, PCRE2_UCHAR *, \\\n    PCRE2_SIZE *); \\\nPCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \\\n  pcre2_substring_free(PCRE2_UCHAR *); \\\nPCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \\\n  pcre2_substring_get_byname(pcre2_match_data *, PCRE2_SPTR, PCRE2_UCHAR **, \\\n    PCRE2_SIZE *); \\\nPCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \\\n  pcre2_substring_get_bynumber(pcre2_match_data *, uint32_t, PCRE2_UCHAR **, \\\n    PCRE2_SIZE *); \\\nPCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \\\n  pcre2_substring_length_byname(pcre2_match_data *, PCRE2_SPTR, PCRE2_SIZE *); \\\nPCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \\\n  pcre2_substring_length_bynumber(pcre2_match_data *, uint32_t, PCRE2_SIZE *); \\\nPCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \\\n  pcre2_substring_nametable_scan(const pcre2_code *, PCRE2_SPTR, PCRE2_SPTR *, \\\n    PCRE2_SPTR *); \\\nPCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \\\n  pcre2_substring_number_from_name(const pcre2_code *, PCRE2_SPTR); \\\nPCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \\\n  pcre2_substring_list_free(PCRE2_UCHAR **); \\\nPCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \\\n  pcre2_substring_list_get(pcre2_match_data *, PCRE2_UCHAR ***, PCRE2_SIZE **);\n\n\n/* Functions for serializing / deserializing compiled patterns. */\n\n#define PCRE2_SERIALIZE_FUNCTIONS \\\nPCRE2_EXP_DECL int32_t PCRE2_CALL_CONVENTION \\\n  pcre2_serialize_encode(const pcre2_code **, int32_t, uint8_t **, \\\n    PCRE2_SIZE *, pcre2_general_context *); \\\nPCRE2_EXP_DECL int32_t PCRE2_CALL_CONVENTION \\\n  pcre2_serialize_decode(pcre2_code **, int32_t, const uint8_t *, \\\n    pcre2_general_context *); \\\nPCRE2_EXP_DECL int32_t PCRE2_CALL_CONVENTION \\\n  pcre2_serialize_get_number_of_codes(const uint8_t *); \\\nPCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \\\n  pcre2_serialize_free(uint8_t *);\n\n\n/* Convenience function for match + substitute. */\n\n#define PCRE2_SUBSTITUTE_FUNCTION \\\nPCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \\\n  pcre2_substitute(const pcre2_code *, PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE, \\\n    uint32_t, pcre2_match_data *, pcre2_match_context *, PCRE2_SPTR, \\\n    PCRE2_SIZE, PCRE2_UCHAR *, PCRE2_SIZE *);\n\n\n/* Functions for converting pattern source strings. */\n\n#define PCRE2_CONVERT_FUNCTIONS \\\nPCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \\\n  pcre2_pattern_convert(PCRE2_SPTR, PCRE2_SIZE, uint32_t, PCRE2_UCHAR **, \\\n    PCRE2_SIZE *, pcre2_convert_context *); \\\nPCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \\\n  pcre2_converted_pattern_free(PCRE2_UCHAR *);\n\n\n/* Functions for JIT processing */\n\n#define PCRE2_JIT_FUNCTIONS \\\nPCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \\\n  pcre2_jit_compile(pcre2_code *, uint32_t); \\\nPCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \\\n  pcre2_jit_match(const pcre2_code *, PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE, \\\n    uint32_t, pcre2_match_data *, pcre2_match_context *); \\\nPCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \\\n  pcre2_jit_free_unused_memory(pcre2_general_context *); \\\nPCRE2_EXP_DECL pcre2_jit_stack *PCRE2_CALL_CONVENTION \\\n  pcre2_jit_stack_create(size_t, size_t, pcre2_general_context *); \\\nPCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \\\n  pcre2_jit_stack_assign(pcre2_match_context *, pcre2_jit_callback, void *); \\\nPCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \\\n  pcre2_jit_stack_free(pcre2_jit_stack *);\n\n\n/* Other miscellaneous functions. */\n\n#define PCRE2_OTHER_FUNCTIONS \\\nPCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \\\n  pcre2_get_error_message(int, PCRE2_UCHAR *, PCRE2_SIZE); \\\nPCRE2_EXP_DECL const uint8_t *PCRE2_CALL_CONVENTION \\\n  pcre2_maketables(pcre2_general_context *); \\\nPCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \\\n  pcre2_maketables_free(pcre2_general_context *, const uint8_t *);\n\n/* Define macros that generate width-specific names from generic versions. The\nthree-level macro scheme is necessary to get the macros expanded when we want\nthem to be. First we get the width from PCRE2_LOCAL_WIDTH, which is used for\ngenerating three versions of everything below. After that, PCRE2_SUFFIX will be\nre-defined to use PCRE2_CODE_UNIT_WIDTH, for use when macros such as\npcre2_compile are called by application code. */\n\n#define PCRE2_JOIN(a,b) a ## b\n#define PCRE2_GLUE(a,b) PCRE2_JOIN(a,b)\n#define PCRE2_SUFFIX(a) PCRE2_GLUE(a,PCRE2_LOCAL_WIDTH)\n\n\n/* Data types */\n\n#define PCRE2_UCHAR                 PCRE2_SUFFIX(PCRE2_UCHAR)\n#define PCRE2_SPTR                  PCRE2_SUFFIX(PCRE2_SPTR)\n\n#define pcre2_code                  PCRE2_SUFFIX(pcre2_code_)\n#define pcre2_jit_callback          PCRE2_SUFFIX(pcre2_jit_callback_)\n#define pcre2_jit_stack             PCRE2_SUFFIX(pcre2_jit_stack_)\n\n#define pcre2_real_code             PCRE2_SUFFIX(pcre2_real_code_)\n#define pcre2_real_general_context  PCRE2_SUFFIX(pcre2_real_general_context_)\n#define pcre2_real_compile_context  PCRE2_SUFFIX(pcre2_real_compile_context_)\n#define pcre2_real_convert_context  PCRE2_SUFFIX(pcre2_real_convert_context_)\n#define pcre2_real_match_context    PCRE2_SUFFIX(pcre2_real_match_context_)\n#define pcre2_real_jit_stack        PCRE2_SUFFIX(pcre2_real_jit_stack_)\n#define pcre2_real_match_data       PCRE2_SUFFIX(pcre2_real_match_data_)\n\n\n/* Data blocks */\n\n#define pcre2_callout_block            PCRE2_SUFFIX(pcre2_callout_block_)\n#define pcre2_callout_enumerate_block  PCRE2_SUFFIX(pcre2_callout_enumerate_block_)\n#define pcre2_substitute_callout_block PCRE2_SUFFIX(pcre2_substitute_callout_block_)\n#define pcre2_general_context          PCRE2_SUFFIX(pcre2_general_context_)\n#define pcre2_compile_context          PCRE2_SUFFIX(pcre2_compile_context_)\n#define pcre2_convert_context          PCRE2_SUFFIX(pcre2_convert_context_)\n#define pcre2_match_context            PCRE2_SUFFIX(pcre2_match_context_)\n#define pcre2_match_data               PCRE2_SUFFIX(pcre2_match_data_)\n\n\n/* Functions: the complete list in alphabetical order */\n\n#define pcre2_callout_enumerate               PCRE2_SUFFIX(pcre2_callout_enumerate_)\n#define pcre2_code_copy                       PCRE2_SUFFIX(pcre2_code_copy_)\n#define pcre2_code_copy_with_tables           PCRE2_SUFFIX(pcre2_code_copy_with_tables_)\n#define pcre2_code_free                       PCRE2_SUFFIX(pcre2_code_free_)\n#define pcre2_compile                         PCRE2_SUFFIX(pcre2_compile_)\n#define pcre2_compile_context_copy            PCRE2_SUFFIX(pcre2_compile_context_copy_)\n#define pcre2_compile_context_create          PCRE2_SUFFIX(pcre2_compile_context_create_)\n#define pcre2_compile_context_free            PCRE2_SUFFIX(pcre2_compile_context_free_)\n#define pcre2_config                          PCRE2_SUFFIX(pcre2_config_)\n#define pcre2_convert_context_copy            PCRE2_SUFFIX(pcre2_convert_context_copy_)\n#define pcre2_convert_context_create          PCRE2_SUFFIX(pcre2_convert_context_create_)\n#define pcre2_convert_context_free            PCRE2_SUFFIX(pcre2_convert_context_free_)\n#define pcre2_converted_pattern_free          PCRE2_SUFFIX(pcre2_converted_pattern_free_)\n#define pcre2_dfa_match                       PCRE2_SUFFIX(pcre2_dfa_match_)\n#define pcre2_general_context_copy            PCRE2_SUFFIX(pcre2_general_context_copy_)\n#define pcre2_general_context_create          PCRE2_SUFFIX(pcre2_general_context_create_)\n#define pcre2_general_context_free            PCRE2_SUFFIX(pcre2_general_context_free_)\n#define pcre2_get_error_message               PCRE2_SUFFIX(pcre2_get_error_message_)\n#define pcre2_get_mark                        PCRE2_SUFFIX(pcre2_get_mark_)\n#define pcre2_get_match_data_heapframes_size  PCRE2_SUFFIX(pcre2_get_match_data_heapframes_size_)\n#define pcre2_get_match_data_size             PCRE2_SUFFIX(pcre2_get_match_data_size_)\n#define pcre2_get_ovector_pointer             PCRE2_SUFFIX(pcre2_get_ovector_pointer_)\n#define pcre2_get_ovector_count               PCRE2_SUFFIX(pcre2_get_ovector_count_)\n#define pcre2_get_startchar                   PCRE2_SUFFIX(pcre2_get_startchar_)\n#define pcre2_jit_compile                     PCRE2_SUFFIX(pcre2_jit_compile_)\n#define pcre2_jit_match                       PCRE2_SUFFIX(pcre2_jit_match_)\n#define pcre2_jit_free_unused_memory          PCRE2_SUFFIX(pcre2_jit_free_unused_memory_)\n#define pcre2_jit_stack_assign                PCRE2_SUFFIX(pcre2_jit_stack_assign_)\n#define pcre2_jit_stack_create                PCRE2_SUFFIX(pcre2_jit_stack_create_)\n#define pcre2_jit_stack_free                  PCRE2_SUFFIX(pcre2_jit_stack_free_)\n#define pcre2_maketables                      PCRE2_SUFFIX(pcre2_maketables_)\n#define pcre2_maketables_free                 PCRE2_SUFFIX(pcre2_maketables_free_)\n#define pcre2_match                           PCRE2_SUFFIX(pcre2_match_)\n#define pcre2_match_context_copy              PCRE2_SUFFIX(pcre2_match_context_copy_)\n#define pcre2_match_context_create            PCRE2_SUFFIX(pcre2_match_context_create_)\n#define pcre2_match_context_free              PCRE2_SUFFIX(pcre2_match_context_free_)\n#define pcre2_match_data_create               PCRE2_SUFFIX(pcre2_match_data_create_)\n#define pcre2_match_data_create_from_pattern  PCRE2_SUFFIX(pcre2_match_data_create_from_pattern_)\n#define pcre2_match_data_free                 PCRE2_SUFFIX(pcre2_match_data_free_)\n#define pcre2_pattern_convert                 PCRE2_SUFFIX(pcre2_pattern_convert_)\n#define pcre2_pattern_info                    PCRE2_SUFFIX(pcre2_pattern_info_)\n#define pcre2_serialize_decode                PCRE2_SUFFIX(pcre2_serialize_decode_)\n#define pcre2_serialize_encode                PCRE2_SUFFIX(pcre2_serialize_encode_)\n#define pcre2_serialize_free                  PCRE2_SUFFIX(pcre2_serialize_free_)\n#define pcre2_serialize_get_number_of_codes   PCRE2_SUFFIX(pcre2_serialize_get_number_of_codes_)\n#define pcre2_set_bsr                         PCRE2_SUFFIX(pcre2_set_bsr_)\n#define pcre2_set_callout                     PCRE2_SUFFIX(pcre2_set_callout_)\n#define pcre2_set_character_tables            PCRE2_SUFFIX(pcre2_set_character_tables_)\n#define pcre2_set_compile_extra_options       PCRE2_SUFFIX(pcre2_set_compile_extra_options_)\n#define pcre2_set_compile_recursion_guard     PCRE2_SUFFIX(pcre2_set_compile_recursion_guard_)\n#define pcre2_set_depth_limit                 PCRE2_SUFFIX(pcre2_set_depth_limit_)\n#define pcre2_set_glob_escape                 PCRE2_SUFFIX(pcre2_set_glob_escape_)\n#define pcre2_set_glob_separator              PCRE2_SUFFIX(pcre2_set_glob_separator_)\n#define pcre2_set_heap_limit                  PCRE2_SUFFIX(pcre2_set_heap_limit_)\n#define pcre2_set_match_limit                 PCRE2_SUFFIX(pcre2_set_match_limit_)\n#define pcre2_set_max_varlookbehind           PCRE2_SUFFIX(pcre2_set_max_varlookbehind_)\n#define pcre2_set_max_pattern_length          PCRE2_SUFFIX(pcre2_set_max_pattern_length_)\n#define pcre2_set_max_pattern_compiled_length PCRE2_SUFFIX(pcre2_set_max_pattern_compiled_length_)\n#define pcre2_set_newline                     PCRE2_SUFFIX(pcre2_set_newline_)\n#define pcre2_set_parens_nest_limit           PCRE2_SUFFIX(pcre2_set_parens_nest_limit_)\n#define pcre2_set_offset_limit                PCRE2_SUFFIX(pcre2_set_offset_limit_)\n#define pcre2_set_optimize                    PCRE2_SUFFIX(pcre2_set_optimize_)\n#define pcre2_set_substitute_callout          PCRE2_SUFFIX(pcre2_set_substitute_callout_)\n#define pcre2_set_substitute_case_callout     PCRE2_SUFFIX(pcre2_set_substitute_case_callout_)\n#define pcre2_substitute                      PCRE2_SUFFIX(pcre2_substitute_)\n#define pcre2_substring_copy_byname           PCRE2_SUFFIX(pcre2_substring_copy_byname_)\n#define pcre2_substring_copy_bynumber         PCRE2_SUFFIX(pcre2_substring_copy_bynumber_)\n#define pcre2_substring_free                  PCRE2_SUFFIX(pcre2_substring_free_)\n#define pcre2_substring_get_byname            PCRE2_SUFFIX(pcre2_substring_get_byname_)\n#define pcre2_substring_get_bynumber          PCRE2_SUFFIX(pcre2_substring_get_bynumber_)\n#define pcre2_substring_length_byname         PCRE2_SUFFIX(pcre2_substring_length_byname_)\n#define pcre2_substring_length_bynumber       PCRE2_SUFFIX(pcre2_substring_length_bynumber_)\n#define pcre2_substring_list_get              PCRE2_SUFFIX(pcre2_substring_list_get_)\n#define pcre2_substring_list_free             PCRE2_SUFFIX(pcre2_substring_list_free_)\n#define pcre2_substring_nametable_scan        PCRE2_SUFFIX(pcre2_substring_nametable_scan_)\n#define pcre2_substring_number_from_name      PCRE2_SUFFIX(pcre2_substring_number_from_name_)\n\n/* Keep this old function name for backwards compatibility */\n#define pcre2_set_recursion_limit PCRE2_SUFFIX(pcre2_set_recursion_limit_)\n\n/* Keep this obsolete function for backwards compatibility: it is now a noop. */\n#define pcre2_set_recursion_memory_management PCRE2_SUFFIX(pcre2_set_recursion_memory_management_)\n\n/* Now generate all three sets of width-specific structures and function\nprototypes. */\n\n#define PCRE2_TYPES_STRUCTURES_AND_FUNCTIONS \\\nPCRE2_TYPES_LIST \\\nPCRE2_STRUCTURE_LIST \\\nPCRE2_GENERAL_INFO_FUNCTIONS \\\nPCRE2_GENERAL_CONTEXT_FUNCTIONS \\\nPCRE2_COMPILE_CONTEXT_FUNCTIONS \\\nPCRE2_CONVERT_CONTEXT_FUNCTIONS \\\nPCRE2_CONVERT_FUNCTIONS \\\nPCRE2_MATCH_CONTEXT_FUNCTIONS \\\nPCRE2_COMPILE_FUNCTIONS \\\nPCRE2_PATTERN_INFO_FUNCTIONS \\\nPCRE2_MATCH_FUNCTIONS \\\nPCRE2_SUBSTRING_FUNCTIONS \\\nPCRE2_SERIALIZE_FUNCTIONS \\\nPCRE2_SUBSTITUTE_FUNCTION \\\nPCRE2_JIT_FUNCTIONS \\\nPCRE2_OTHER_FUNCTIONS\n\n#define PCRE2_LOCAL_WIDTH 8\nPCRE2_TYPES_STRUCTURES_AND_FUNCTIONS\n#undef PCRE2_LOCAL_WIDTH\n\n#define PCRE2_LOCAL_WIDTH 16\nPCRE2_TYPES_STRUCTURES_AND_FUNCTIONS\n#undef PCRE2_LOCAL_WIDTH\n\n#define PCRE2_LOCAL_WIDTH 32\nPCRE2_TYPES_STRUCTURES_AND_FUNCTIONS\n#undef PCRE2_LOCAL_WIDTH\n\n/* Undefine the list macros; they are no longer needed. */\n\n#undef PCRE2_TYPES_LIST\n#undef PCRE2_STRUCTURE_LIST\n#undef PCRE2_GENERAL_INFO_FUNCTIONS\n#undef PCRE2_GENERAL_CONTEXT_FUNCTIONS\n#undef PCRE2_COMPILE_CONTEXT_FUNCTIONS\n#undef PCRE2_CONVERT_CONTEXT_FUNCTIONS\n#undef PCRE2_MATCH_CONTEXT_FUNCTIONS\n#undef PCRE2_COMPILE_FUNCTIONS\n#undef PCRE2_PATTERN_INFO_FUNCTIONS\n#undef PCRE2_MATCH_FUNCTIONS\n#undef PCRE2_SUBSTRING_FUNCTIONS\n#undef PCRE2_SERIALIZE_FUNCTIONS\n#undef PCRE2_SUBSTITUTE_FUNCTION\n#undef PCRE2_JIT_FUNCTIONS\n#undef PCRE2_OTHER_FUNCTIONS\n#undef PCRE2_TYPES_STRUCTURES_AND_FUNCTIONS\n\n/* PCRE2_CODE_UNIT_WIDTH must be defined. If it is 8, 16, or 32, redefine\nPCRE2_SUFFIX to use it. If it is 0, undefine the other macros and make\nPCRE2_SUFFIX a no-op. Otherwise, generate an error. */\n\n#undef PCRE2_SUFFIX\n#ifndef PCRE2_CODE_UNIT_WIDTH\n#error PCRE2_CODE_UNIT_WIDTH must be defined before including pcre2.h.\n#error Use 8, 16, or 32; or 0 for a multi-width application.\n#else  /* PCRE2_CODE_UNIT_WIDTH is defined */\n#if PCRE2_CODE_UNIT_WIDTH == 8 || \\\n    PCRE2_CODE_UNIT_WIDTH == 16 || \\\n    PCRE2_CODE_UNIT_WIDTH == 32\n#define PCRE2_SUFFIX(a) PCRE2_GLUE(a, PCRE2_CODE_UNIT_WIDTH)\n#elif PCRE2_CODE_UNIT_WIDTH == 0\n#undef PCRE2_JOIN\n#undef PCRE2_GLUE\n#define PCRE2_SUFFIX(a) a\n#else\n#error PCRE2_CODE_UNIT_WIDTH must be 0, 8, 16, or 32.\n#endif\n#endif  /* PCRE2_CODE_UNIT_WIDTH is defined */\n\n#ifdef __cplusplus\n}  /* extern \"C\" */\n#endif\n\n#endif  /* PCRE2_H_IDEMPOTENT_GUARD */\n\n/* End of pcre2.h */\n"
  },
  {
    "path": "Libraries/PCRE/pcre2_auto_possess.c",
    "content": "/*************************************************\n*      Perl-Compatible Regular Expressions       *\n*************************************************/\n\n/* PCRE is a library of functions to support regular expressions whose syntax\nand semantics are as close as possible to those of the Perl 5 language.\n\n                       Written by Philip Hazel\n     Original API code Copyright (c) 1997-2012 University of Cambridge\n          New API code Copyright (c) 2016-2024 University of Cambridge\n\n-----------------------------------------------------------------------------\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n    * Redistributions of source code must retain the above copyright notice,\n      this list of conditions and the following disclaimer.\n\n    * Redistributions in binary form must reproduce the above copyright\n      notice, this list of conditions and the following disclaimer in the\n      documentation and/or other materials provided with the distribution.\n\n    * Neither the name of the University of Cambridge nor the names of its\n      contributors may be used to endorse or promote products derived from\n      this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\nLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\nCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\nSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\nINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\nCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGE.\n-----------------------------------------------------------------------------\n*/\n\n/* This module contains functions that scan a compiled pattern and change\nrepeats into possessive repeats where possible. */\n\n\n#ifdef HAVE_CONFIG_H\n#include \"config.h\"\n#endif\n\n\n#include \"pcre2_internal.h\"\n\n/* This macro represents the max size of list[] and that is used to keep\ntrack of UCD info in several places, it should be kept on sync with the\nvalue used by GenerateUcd.py */\n#define MAX_LIST 8\n\n/*************************************************\n*        Tables for auto-possessification        *\n*************************************************/\n\n/* This table is used to check whether auto-possessification is possible\nbetween adjacent character-type opcodes. The left-hand (repeated) opcode is\nused to select the row, and the right-hand opcode is use to select the column.\nA value of 1 means that auto-possessification is OK. For example, the second\nvalue in the first row means that \\D+\\d can be turned into \\D++\\d.\n\nThe Unicode property types (\\P and \\p) have to be present to fill out the table\nbecause of what their opcode values are, but the table values should always be\nzero because property types are handled separately in the code. The last four\ncolumns apply to items that cannot be repeated, so there is no need to have\nrows for them. Note that OP_DIGIT etc. are generated only when PCRE2_UCP is\n*not* set. When it is set, \\d etc. are converted into OP_(NOT_)PROP codes. */\n\n#define APTROWS (LAST_AUTOTAB_LEFT_OP - FIRST_AUTOTAB_OP + 1)\n#define APTCOLS (LAST_AUTOTAB_RIGHT_OP - FIRST_AUTOTAB_OP + 1)\n\nstatic const uint8_t autoposstab[APTROWS][APTCOLS] = {\n/* \\D \\d \\S \\s \\W \\w  . .+ \\C \\P \\p \\R \\H \\h \\V \\v \\X \\Z \\z  $ $M */\n  { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 },  /* \\D */\n  { 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1 },  /* \\d */\n  { 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1 },  /* \\S */\n  { 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 },  /* \\s */\n  { 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 },  /* \\W */\n  { 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1 },  /* \\w */\n  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0 },  /* .  */\n  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 },  /* .+ */\n  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 },  /* \\C */\n  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },  /* \\P */\n  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },  /* \\p */\n  { 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0 },  /* \\R */\n  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0 },  /* \\H */\n  { 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0 },  /* \\h */\n  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0 },  /* \\V */\n  { 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0 },  /* \\v */\n  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }   /* \\X */\n};\n\n#ifdef SUPPORT_UNICODE\n/* This table is used to check whether auto-possessification is possible\nbetween adjacent Unicode property opcodes (OP_PROP and OP_NOTPROP). The\nleft-hand (repeated) opcode is used to select the row, and the right-hand\nopcode is used to select the column. The values are as follows:\n\n  0   Always return FALSE (never auto-possessify)\n  1   Character groups are distinct (possessify if both are OP_PROP)\n  2   Check character categories in the same group (general or particular)\n  3   TRUE if the two opcodes are not the same (PROP vs NOTPROP)\n\n  4   Check left general category vs right particular category\n  5   Check right general category vs left particular category\n\n  6   Left alphanum vs right general category\n  7   Left space vs right general category\n  8   Left word vs right general category\n\n  9   Right alphanum vs left general category\n 10   Right space vs left general category\n 11   Right word vs left general category\n\n 12   Left alphanum vs right particular category\n 13   Left space vs right particular category\n 14   Left word vs right particular category\n\n 15   Right alphanum vs left particular category\n 16   Right space vs left particular category\n 17   Right word vs left particular category\n*/\n\nstatic const uint8_t propposstab[PT_TABSIZE][PT_TABSIZE] = {\n/* LAMP GC  PC  SC  SCX ALNUM SPACE PXSPACE WORD CLIST UCNC BIDICL BOOL */\n  { 3,  0,  0,  0,   0,    3,    1,      1,   0,    0,   0,    0,    0 },  /* PT_LAMP */\n  { 0,  2,  4,  0,   0,    9,   10,     10,  11,    0,   0,    0,    0 },  /* PT_GC */\n  { 0,  5,  2,  0,   0,   15,   16,     16,  17,    0,   0,    0,    0 },  /* PT_PC */\n  { 0,  0,  0,  2,   2,    0,    0,      0,   0,    0,   0,    0,    0 },  /* PT_SC */\n  { 0,  0,  0,  2,   2,    0,    0,      0,   0,    0,   0,    0,    0 },  /* PT_SCX */\n  { 3,  6, 12,  0,   0,    3,    1,      1,   0,    0,   0,    0,    0 },  /* PT_ALNUM */\n  { 1,  7, 13,  0,   0,    1,    3,      3,   1,    0,   0,    0,    0 },  /* PT_SPACE */\n  { 1,  7, 13,  0,   0,    1,    3,      3,   1,    0,   0,    0,    0 },  /* PT_PXSPACE */\n  { 0,  8, 14,  0,   0,    0,    1,      1,   3,    0,   0,    0,    0 },  /* PT_WORD */\n  { 0,  0,  0,  0,   0,    0,    0,      0,   0,    0,   0,    0,    0 },  /* PT_CLIST */\n  { 0,  0,  0,  0,   0,    0,    0,      0,   0,    0,   3,    0,    0 },  /* PT_UCNC */\n  { 0,  0,  0,  0,   0,    0,    0,      0,   0,    0,   0,    0,    0 },  /* PT_BIDICL */\n  { 0,  0,  0,  0,   0,    0,    0,      0,   0,    0,   0,    0,    0 }   /* PT_BOOL */\n  /* PT_ANY does not need a record. */\n};\n\n/* This table is used to check whether auto-possessification is possible\nbetween adjacent Unicode property opcodes (OP_PROP and OP_NOTPROP) when one\nspecifies a general category and the other specifies a particular category. The\nrow is selected by the general category and the column by the particular\ncategory. The value is 1 if the particular category is not part of the general\ncategory. */\n\nstatic const uint8_t catposstab[7][30] = {\n/* Cc Cf Cn Co Cs Ll Lm Lo Lt Lu Mc Me Mn Nd Nl No Pc Pd Pe Pf Pi Po Ps Sc Sk Sm So Zl Zp Zs */\n  { 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },  /* C */\n  { 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },  /* L */\n  { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },  /* M */\n  { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },  /* N */\n  { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1 },  /* P */\n  { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1 },  /* S */\n  { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 }   /* Z */\n};\n\n/* This table is used when checking ALNUM, (PX)SPACE, SPACE, and WORD against\na general or particular category. The properties in each row are those\nthat apply to the character set in question. Duplication means that a little\nunnecessary work is done when checking, but this keeps things much simpler\nbecause they can all use the same code. For more details see the comment where\nthis table is used.\n\nNote: SPACE and PXSPACE used to be different because Perl excluded VT from\n\"space\", but from Perl 5.18 it's included, so both categories are treated the\nsame here. */\n\nstatic const uint8_t posspropstab[3][4] = {\n  { ucp_L, ucp_N, ucp_N, ucp_Nl },  /* ALNUM, 3rd and 4th values redundant */\n  { ucp_Z, ucp_Z, ucp_C, ucp_Cc },  /* SPACE and PXSPACE, 2nd value redundant */\n  { ucp_L, ucp_N, ucp_P, ucp_Po }   /* WORD */\n};\n#endif  /* SUPPORT_UNICODE */\n\n\n\n#ifdef SUPPORT_UNICODE\n/*************************************************\n*        Check a character and a property        *\n*************************************************/\n\n/* This function is called by compare_opcodes() when a property item is\nadjacent to a fixed character.\n\nArguments:\n  c            the character\n  ptype        the property type\n  pdata        the data for the type\n  negated      TRUE if it's a negated property (\\P or \\p{^)\n\nReturns:       TRUE if auto-possessifying is OK\n*/\n\nstatic BOOL\ncheck_char_prop(uint32_t c, unsigned int ptype, unsigned int pdata,\n  BOOL negated)\n{\nBOOL ok, rc;\nconst uint32_t *p;\nconst ucd_record *prop = GET_UCD(c);\n\nswitch(ptype)\n  {\n  case PT_LAMP:\n  return (prop->chartype == ucp_Lu ||\n          prop->chartype == ucp_Ll ||\n          prop->chartype == ucp_Lt) == negated;\n\n  case PT_GC:\n  return (pdata == PRIV(ucp_gentype)[prop->chartype]) == negated;\n\n  case PT_PC:\n  return (pdata == prop->chartype) == negated;\n\n  case PT_SC:\n  return (pdata == prop->script) == negated;\n\n  case PT_SCX:\n  ok = (pdata == prop->script\n        || MAPBIT(PRIV(ucd_script_sets) + UCD_SCRIPTX_PROP(prop), pdata) != 0);\n  return ok == negated;\n\n  /* These are specials */\n\n  case PT_ALNUM:\n  return (PRIV(ucp_gentype)[prop->chartype] == ucp_L ||\n          PRIV(ucp_gentype)[prop->chartype] == ucp_N) == negated;\n\n  /* Perl space used to exclude VT, but from Perl 5.18 it is included, which\n  means that Perl space and POSIX space are now identical. PCRE was changed\n  at release 8.34. */\n\n  case PT_SPACE:    /* Perl space */\n  case PT_PXSPACE:  /* POSIX space */\n  switch(c)\n    {\n    HSPACE_CASES:\n    VSPACE_CASES:\n    rc = negated;\n    break;\n\n    default:\n    rc = (PRIV(ucp_gentype)[prop->chartype] == ucp_Z) == negated;\n    }\n  return rc;\n\n  case PT_WORD:\n  return (PRIV(ucp_gentype)[prop->chartype] == ucp_L ||\n          PRIV(ucp_gentype)[prop->chartype] == ucp_N ||\n          c == CHAR_UNDERSCORE) == negated;\n\n  case PT_CLIST:\n  p = PRIV(ucd_caseless_sets) + prop->caseset;\n  for (;;)\n    {\n    if (c < *p) return !negated;\n    if (c == *p++) return negated;\n    }\n  PCRE2_DEBUG_UNREACHABLE(); /* Control should never reach here */\n  break;\n\n  /* Haven't yet thought these through. */\n\n  case PT_BIDICL:\n  return FALSE;\n\n  case PT_BOOL:\n  return FALSE;\n  }\n\nreturn FALSE;\n}\n#endif  /* SUPPORT_UNICODE */\n\n\n\n/*************************************************\n*        Base opcode of repeated opcodes         *\n*************************************************/\n\n/* Returns the base opcode for repeated single character type opcodes. If the\nopcode is not a repeated character type, it returns with the original value.\n\nArguments:  c opcode\nReturns:    base opcode for the type\n*/\n\nstatic PCRE2_UCHAR\nget_repeat_base(PCRE2_UCHAR c)\n{\nreturn (c > OP_TYPEPOSUPTO)? c :\n       (c >= OP_TYPESTAR)?   OP_TYPESTAR :\n       (c >= OP_NOTSTARI)?   OP_NOTSTARI :\n       (c >= OP_NOTSTAR)?    OP_NOTSTAR :\n       (c >= OP_STARI)?      OP_STARI :\n                             OP_STAR;\n}\n\n\n/*************************************************\n*        Fill the character property list        *\n*************************************************/\n\n/* Checks whether the code points to an opcode that can take part in auto-\npossessification, and if so, fills a list with its properties.\n\nArguments:\n  code        points to start of expression\n  utf         TRUE if in UTF mode\n  ucp         TRUE if in UCP mode\n  fcc         points to the case-flipping table\n  list        points to output list\n              list[0] will be filled with the opcode\n              list[1] will be non-zero if this opcode\n                can match an empty character string\n              list[2..7] depends on the opcode\n\nReturns:      points to the start of the next opcode if *code is accepted\n              NULL if *code is not accepted\n*/\n\nstatic PCRE2_SPTR\nget_chr_property_list(PCRE2_SPTR code, BOOL utf, BOOL ucp, const uint8_t *fcc,\n  uint32_t *list)\n{\nPCRE2_UCHAR c = *code;\nPCRE2_UCHAR base;\nPCRE2_SPTR end;\nPCRE2_SPTR class_end;\nuint32_t chr;\n\n#ifdef SUPPORT_UNICODE\nuint32_t *clist_dest;\nconst uint32_t *clist_src;\n#else\n(void)utf;    /* Suppress \"unused parameter\" compiler warnings */\n(void)ucp;\n#endif\n\nlist[0] = c;\nlist[1] = FALSE;\ncode++;\n\nif (c >= OP_STAR && c <= OP_TYPEPOSUPTO)\n  {\n  base = get_repeat_base(c);\n  c -= (base - OP_STAR);\n\n  if (c == OP_UPTO || c == OP_MINUPTO || c == OP_EXACT || c == OP_POSUPTO)\n    code += IMM2_SIZE;\n\n  list[1] = (c != OP_PLUS && c != OP_MINPLUS && c != OP_EXACT &&\n             c != OP_POSPLUS);\n\n  switch(base)\n    {\n    case OP_STAR:\n    list[0] = OP_CHAR;\n    break;\n\n    case OP_STARI:\n    list[0] = OP_CHARI;\n    break;\n\n    case OP_NOTSTAR:\n    list[0] = OP_NOT;\n    break;\n\n    case OP_NOTSTARI:\n    list[0] = OP_NOTI;\n    break;\n\n    case OP_TYPESTAR:\n    list[0] = *code;\n    code++;\n    break;\n    }\n  c = list[0];\n  }\n\nswitch(c)\n  {\n  case OP_NOT_DIGIT:\n  case OP_DIGIT:\n  case OP_NOT_WHITESPACE:\n  case OP_WHITESPACE:\n  case OP_NOT_WORDCHAR:\n  case OP_WORDCHAR:\n  case OP_ANY:\n  case OP_ALLANY:\n  case OP_ANYNL:\n  case OP_NOT_HSPACE:\n  case OP_HSPACE:\n  case OP_NOT_VSPACE:\n  case OP_VSPACE:\n  case OP_EXTUNI:\n  case OP_EODN:\n  case OP_EOD:\n  case OP_DOLL:\n  case OP_DOLLM:\n  return code;\n\n  case OP_CHAR:\n  case OP_NOT:\n  GETCHARINCTEST(chr, code);\n  list[2] = chr;\n  list[3] = NOTACHAR;\n  return code;\n\n  case OP_CHARI:\n  case OP_NOTI:\n  list[0] = (c == OP_CHARI) ? OP_CHAR : OP_NOT;\n  GETCHARINCTEST(chr, code);\n  list[2] = chr;\n\n#ifdef SUPPORT_UNICODE\n  if (chr < 128 || (chr < 256 && !utf && !ucp))\n    list[3] = fcc[chr];\n  else\n    list[3] = UCD_OTHERCASE(chr);\n#elif defined SUPPORT_WIDE_CHARS\n  list[3] = (chr < 256) ? fcc[chr] : chr;\n#else\n  list[3] = fcc[chr];\n#endif\n\n  /* The othercase might be the same value. */\n\n  if (chr == list[3])\n    list[3] = NOTACHAR;\n  else\n    list[4] = NOTACHAR;\n  return code;\n\n#ifdef SUPPORT_UNICODE\n  case OP_PROP:\n  case OP_NOTPROP:\n  if (code[0] != PT_CLIST)\n    {\n    list[2] = code[0];\n    list[3] = code[1];\n    return code + 2;\n    }\n\n  /* Convert only if we have enough space. */\n\n  clist_src = PRIV(ucd_caseless_sets) + code[1];\n  clist_dest = list + 2;\n  code += 2;\n\n  do {\n     if (clist_dest >= list + MAX_LIST)\n       {\n       /* Early return if there is not enough space. GenerateUcd.py\n       generated a list with more than 5 characters and something\n       must be done about that going forward. */\n       PCRE2_DEBUG_UNREACHABLE();   /* Remove if it ever triggers */\n       list[2] = code[0];\n       list[3] = code[1];\n       return code;\n       }\n     *clist_dest++ = *clist_src;\n     }\n  while(*clist_src++ != NOTACHAR);\n\n  /* All characters are stored. The terminating NOTACHAR is copied from the\n  clist itself. */\n\n  list[0] = (c == OP_PROP) ? OP_CHAR : OP_NOT;\n  return code;\n#endif\n\n  case OP_NCLASS:\n  case OP_CLASS:\n#ifdef SUPPORT_WIDE_CHARS\n  case OP_XCLASS:\n  case OP_ECLASS:\n  if (c == OP_XCLASS || c == OP_ECLASS)\n    end = code + GET(code, 0) - 1;\n  else\n#endif\n    end = code + 32 / sizeof(PCRE2_UCHAR);\n  class_end = end;\n\n  switch(*end)\n    {\n    case OP_CRSTAR:\n    case OP_CRMINSTAR:\n    case OP_CRQUERY:\n    case OP_CRMINQUERY:\n    case OP_CRPOSSTAR:\n    case OP_CRPOSQUERY:\n    list[1] = TRUE;\n    end++;\n    break;\n\n    case OP_CRPLUS:\n    case OP_CRMINPLUS:\n    case OP_CRPOSPLUS:\n    end++;\n    break;\n\n    case OP_CRRANGE:\n    case OP_CRMINRANGE:\n    case OP_CRPOSRANGE:\n    list[1] = (GET2(end, 1) == 0);\n    end += 1 + 2 * IMM2_SIZE;\n    break;\n    }\n  list[2] = (uint32_t)(end - code);\n  list[3] = (uint32_t)(end - class_end);\n  return end;\n  }\n\nreturn NULL;    /* Opcode not accepted */\n}\n\n\n\n/*************************************************\n*    Scan further character sets for match       *\n*************************************************/\n\n/* Checks whether the base and the current opcode have a common character, in\nwhich case the base cannot be possessified.\n\nArguments:\n  code        points to the byte code\n  utf         TRUE in UTF mode\n  ucp         TRUE in UCP mode\n  cb          compile data block\n  base_list   the data list of the base opcode\n  base_end    the end of the base opcode\n  rec_limit   points to recursion depth counter\n\nReturns:      TRUE if the auto-possessification is possible\n*/\n\nstatic BOOL\ncompare_opcodes(PCRE2_SPTR code, BOOL utf, BOOL ucp, const compile_block *cb,\n  const uint32_t *base_list, PCRE2_SPTR base_end, int *rec_limit)\n{\nPCRE2_UCHAR c;\nuint32_t list[MAX_LIST];\nconst uint32_t *chr_ptr;\nconst uint32_t *ochr_ptr;\nconst uint32_t *list_ptr;\nPCRE2_SPTR next_code;\n#ifdef SUPPORT_WIDE_CHARS\nPCRE2_SPTR xclass_flags;\n#endif\nconst uint8_t *class_bitset;\nconst uint8_t *set1, *set2, *set_end;\nuint32_t chr;\nBOOL accepted, invert_bits;\nBOOL entered_a_group = FALSE;\n\nif (--(*rec_limit) <= 0) return FALSE;  /* Recursion has gone too deep */\n\n/* Note: the base_list[1] contains whether the current opcode has a greedy\n(represented by a non-zero value) quantifier. This is a different from\nother character type lists, which store here that the character iterator\nmatches to an empty string (also represented by a non-zero value). */\n\nfor(;;)\n  {\n  PCRE2_SPTR bracode;\n\n  /* All operations move the code pointer forward.\n  Therefore infinite recursions are not possible. */\n\n  c = *code;\n\n  /* Skip over callouts */\n\n  if (c == OP_CALLOUT)\n    {\n    code += PRIV(OP_lengths)[c];\n    continue;\n    }\n\n  if (c == OP_CALLOUT_STR)\n    {\n    code += GET(code, 1 + 2*LINK_SIZE);\n    continue;\n    }\n\n  /* At the end of a branch, skip to the end of the group and process it. */\n\n  if (c == OP_ALT)\n    {\n    do code += GET(code, 1); while (*code == OP_ALT);\n    c = *code;\n    }\n\n  /* Inspect the next opcode. */\n\n  switch(c)\n    {\n    /* We can always possessify a greedy iterator at the end of the pattern,\n    which is reached after skipping over the final OP_KET. A non-greedy\n    iterator must never be possessified. */\n\n    case OP_END:\n    return base_list[1] != 0;\n\n    /* When an iterator is at the end of certain kinds of group we can inspect\n    what follows the group by skipping over the closing ket. Note that this\n    does not apply to OP_KETRMAX or OP_KETRMIN because what follows any given\n    iteration is variable (could be another iteration or could be the next\n    item). As these two opcodes are not listed in the next switch, they will\n    end up as the next code to inspect, and return FALSE by virtue of being\n    unsupported. */\n\n    case OP_KET:\n    case OP_KETRPOS:\n    /* The non-greedy case cannot be converted to a possessive form. */\n\n    if (base_list[1] == 0) return FALSE;\n\n    /* If the bracket is capturing it might be referenced by an OP_RECURSE\n    so its last iterator can never be possessified if the pattern contains\n    recursions. (This could be improved by keeping a list of group numbers that\n    are called by recursion.) */\n\n    bracode = code - GET(code, 1);\n    switch(*bracode)\n      {\n      case OP_CBRA:\n      case OP_SCBRA:\n      case OP_CBRAPOS:\n      case OP_SCBRAPOS:\n      if (cb->had_recurse) return FALSE;\n      break;\n\n      /* A script run might have to backtrack if the iterated item can match\n      characters from more than one script. So give up unless repeating an\n      explicit character. */\n\n      case OP_SCRIPT_RUN:\n      if (base_list[0] != OP_CHAR && base_list[0] != OP_CHARI)\n        return FALSE;\n      break;\n\n      /* Atomic sub-patterns and forward assertions can always auto-possessify\n      their last iterator. However, if the group was entered as a result of\n      checking a previous iterator, this is not possible. */\n\n      case OP_ASSERT:\n      case OP_ASSERT_NOT:\n      case OP_ONCE:\n      return !entered_a_group;\n\n      /* Fixed-length lookbehinds can be treated the same way, but variable\n      length lookbehinds must not auto-possessify their last iterator. Note\n      that in order to identify a variable length lookbehind we must check\n      through all branches, because some may be of fixed length. */\n\n      case OP_ASSERTBACK:\n      case OP_ASSERTBACK_NOT:\n      do\n        {\n        if (bracode[1+LINK_SIZE] == OP_VREVERSE) return FALSE;  /* Variable */\n        bracode += GET(bracode, 1);\n        }\n      while (*bracode == OP_ALT);\n      return !entered_a_group;  /* Not variable length */\n\n      /* Non-atomic assertions - don't possessify last iterator. This needs\n      more thought. */\n\n      case OP_ASSERT_NA:\n      case OP_ASSERTBACK_NA:\n      return FALSE;\n      }\n\n    /* Skip over the bracket and inspect what comes next. */\n\n    code += PRIV(OP_lengths)[c];\n    continue;\n\n    /* Handle cases where the next item is a group. */\n\n    case OP_ONCE:\n    case OP_BRA:\n    case OP_CBRA:\n    next_code = code + GET(code, 1);\n    code += PRIV(OP_lengths)[c];\n\n    /* Check each branch. We have to recurse a level for all but the last\n    branch. */\n\n    while (*next_code == OP_ALT)\n      {\n      if (!compare_opcodes(code, utf, ucp, cb, base_list, base_end, rec_limit))\n        return FALSE;\n      code = next_code + 1 + LINK_SIZE;\n      next_code += GET(next_code, 1);\n      }\n\n    entered_a_group = TRUE;\n    continue;\n\n    case OP_BRAZERO:\n    case OP_BRAMINZERO:\n\n    next_code = code + 1;\n    if (*next_code != OP_BRA && *next_code != OP_CBRA &&\n        *next_code != OP_ONCE) return FALSE;\n\n    do next_code += GET(next_code, 1); while (*next_code == OP_ALT);\n\n    /* The bracket content will be checked by the OP_BRA/OP_CBRA case above. */\n\n    next_code += 1 + LINK_SIZE;\n    if (!compare_opcodes(next_code, utf, ucp, cb, base_list, base_end,\n         rec_limit))\n      return FALSE;\n\n    code += PRIV(OP_lengths)[c];\n    continue;\n\n    /* The next opcode does not need special handling; fall through and use it\n    to see if the base can be possessified. */\n\n    default:\n    break;\n    }\n\n  /* We now have the next appropriate opcode to compare with the base. Check\n  for a supported opcode, and load its properties. */\n\n  code = get_chr_property_list(code, utf, ucp, cb->fcc, list);\n  if (code == NULL) return FALSE;    /* Unsupported */\n\n  /* If either opcode is a small character list, set pointers for comparing\n  characters from that list with another list, or with a property. */\n\n  if (base_list[0] == OP_CHAR)\n    {\n    chr_ptr = base_list + 2;\n    list_ptr = list;\n    }\n  else if (list[0] == OP_CHAR)\n    {\n    chr_ptr = list + 2;\n    list_ptr = base_list;\n    }\n\n  /* Character bitsets can also be compared to certain opcodes. */\n\n  else if (base_list[0] == OP_CLASS || list[0] == OP_CLASS\n#if PCRE2_CODE_UNIT_WIDTH == 8\n      /* In 8 bit, non-UTF mode, OP_CLASS and OP_NCLASS are the same. */\n      || (!utf && (base_list[0] == OP_NCLASS || list[0] == OP_NCLASS))\n#endif\n      )\n    {\n#if PCRE2_CODE_UNIT_WIDTH == 8\n    if (base_list[0] == OP_CLASS || (!utf && base_list[0] == OP_NCLASS))\n#else\n    if (base_list[0] == OP_CLASS)\n#endif\n      {\n      set1 = (const uint8_t *)(base_end - base_list[2]);\n      list_ptr = list;\n      }\n    else\n      {\n      set1 = (const uint8_t *)(code - list[2]);\n      list_ptr = base_list;\n      }\n\n    invert_bits = FALSE;\n    switch(list_ptr[0])\n      {\n      case OP_CLASS:\n      case OP_NCLASS:\n      set2 = (const uint8_t *)\n        ((list_ptr == list ? code : base_end) - list_ptr[2]);\n      break;\n\n#ifdef SUPPORT_WIDE_CHARS\n      case OP_XCLASS:\n      xclass_flags = (list_ptr == list ? code : base_end) -\n        list_ptr[2] + LINK_SIZE;\n      if ((*xclass_flags & XCL_HASPROP) != 0) return FALSE;\n      if ((*xclass_flags & XCL_MAP) == 0)\n        {\n        /* No bits are set for characters < 256. */\n        if (list[1] == 0) return (*xclass_flags & XCL_NOT) == 0;\n        /* Might be an empty repeat. */\n        continue;\n        }\n      set2 = (const uint8_t *)(xclass_flags + 1);\n      break;\n#endif\n\n      case OP_NOT_DIGIT:\n      invert_bits = TRUE;\n      /* Fall through */\n      case OP_DIGIT:\n      set2 = (const uint8_t *)(cb->cbits + cbit_digit);\n      break;\n\n      case OP_NOT_WHITESPACE:\n      invert_bits = TRUE;\n      /* Fall through */\n      case OP_WHITESPACE:\n      set2 = (const uint8_t *)(cb->cbits + cbit_space);\n      break;\n\n      case OP_NOT_WORDCHAR:\n      invert_bits = TRUE;\n      /* Fall through */\n      case OP_WORDCHAR:\n      set2 = (const uint8_t *)(cb->cbits + cbit_word);\n      break;\n\n      default:\n      return FALSE;\n      }\n\n    /* Because the bit sets are unaligned bytes, we need to perform byte\n    comparison here. */\n\n    set_end = set1 + 32;\n    if (invert_bits)\n      {\n      do\n        {\n        if ((*set1++ & ~(*set2++)) != 0) return FALSE;\n        }\n      while (set1 < set_end);\n      }\n    else\n      {\n      do\n        {\n        if ((*set1++ & *set2++) != 0) return FALSE;\n        }\n      while (set1 < set_end);\n      }\n\n    if (list[1] == 0) return TRUE;\n    /* Might be an empty repeat. */\n    continue;\n    }\n\n  /* Some property combinations also acceptable. Unicode property opcodes are\n  processed specially; the rest can be handled with a lookup table. */\n\n  else\n    {\n    uint32_t leftop, rightop;\n\n    leftop = base_list[0];\n    rightop = list[0];\n\n#ifdef SUPPORT_UNICODE\n    accepted = FALSE; /* Always set in non-unicode case. */\n    if (leftop == OP_PROP || leftop == OP_NOTPROP)\n      {\n      if (rightop == OP_EOD)\n        accepted = TRUE;\n      else if (rightop == OP_PROP || rightop == OP_NOTPROP)\n        {\n        int n;\n        const uint8_t *p;\n        BOOL same = leftop == rightop;\n        BOOL lisprop = leftop == OP_PROP;\n        BOOL risprop = rightop == OP_PROP;\n        BOOL bothprop = lisprop && risprop;\n\n        /* There's a table that specifies how each combination is to be\n        processed:\n          0   Always return FALSE (never auto-possessify)\n          1   Character groups are distinct (possessify if both are OP_PROP)\n          2   Check character categories in the same group (general or particular)\n          3   Return TRUE if the two opcodes are not the same\n          ... see comments below\n        */\n\n        n = propposstab[base_list[2]][list[2]];\n        switch(n)\n          {\n          case 0: break;\n          case 1: accepted = bothprop; break;\n          case 2: accepted = (base_list[3] == list[3]) != same; break;\n          case 3: accepted = !same; break;\n\n          case 4:  /* Left general category, right particular category */\n          accepted = risprop && catposstab[base_list[3]][list[3]] == same;\n          break;\n\n          case 5:  /* Right general category, left particular category */\n          accepted = lisprop && catposstab[list[3]][base_list[3]] == same;\n          break;\n\n          /* This code is logically tricky. Think hard before fiddling with it.\n          The posspropstab table has four entries per row. Each row relates to\n          one of PCRE's special properties such as ALNUM or SPACE or WORD.\n          Only WORD actually needs all four entries, but using repeats for the\n          others means they can all use the same code below.\n\n          The first two entries in each row are Unicode general categories, and\n          apply always, because all the characters they include are part of the\n          PCRE character set. The third and fourth entries are a general and a\n          particular category, respectively, that include one or more relevant\n          characters. One or the other is used, depending on whether the check\n          is for a general or a particular category. However, in both cases the\n          category contains more characters than the specials that are defined\n          for the property being tested against. Therefore, it cannot be used\n          in a NOTPROP case.\n\n          Example: the row for WORD contains ucp_L, ucp_N, ucp_P, ucp_Po.\n          Underscore is covered by ucp_P or ucp_Po. */\n\n          case 6:  /* Left alphanum vs right general category */\n          case 7:  /* Left space vs right general category */\n          case 8:  /* Left word vs right general category */\n          p = posspropstab[n-6];\n          accepted = risprop && lisprop ==\n            (list[3] != p[0] &&\n             list[3] != p[1] &&\n            (list[3] != p[2] || !lisprop));\n          break;\n\n          case 9:   /* Right alphanum vs left general category */\n          case 10:  /* Right space vs left general category */\n          case 11:  /* Right word vs left general category */\n          p = posspropstab[n-9];\n          accepted = lisprop && risprop ==\n            (base_list[3] != p[0] &&\n             base_list[3] != p[1] &&\n            (base_list[3] != p[2] || !risprop));\n          break;\n\n          case 12:  /* Left alphanum vs right particular category */\n          case 13:  /* Left space vs right particular category */\n          case 14:  /* Left word vs right particular category */\n          p = posspropstab[n-12];\n          accepted = risprop && lisprop ==\n            (catposstab[p[0]][list[3]] &&\n             catposstab[p[1]][list[3]] &&\n            (list[3] != p[3] || !lisprop));\n          break;\n\n          case 15:  /* Right alphanum vs left particular category */\n          case 16:  /* Right space vs left particular category */\n          case 17:  /* Right word vs left particular category */\n          p = posspropstab[n-15];\n          accepted = lisprop && risprop ==\n            (catposstab[p[0]][base_list[3]] &&\n             catposstab[p[1]][base_list[3]] &&\n            (base_list[3] != p[3] || !risprop));\n          break;\n          }\n        }\n      }\n\n    else\n#endif  /* SUPPORT_UNICODE */\n\n    accepted = leftop >= FIRST_AUTOTAB_OP && leftop <= LAST_AUTOTAB_LEFT_OP &&\n           rightop >= FIRST_AUTOTAB_OP && rightop <= LAST_AUTOTAB_RIGHT_OP &&\n           autoposstab[leftop - FIRST_AUTOTAB_OP][rightop - FIRST_AUTOTAB_OP];\n\n    if (!accepted) return FALSE;\n\n    if (list[1] == 0) return TRUE;\n    /* Might be an empty repeat. */\n    continue;\n    }\n\n  /* Control reaches here only if one of the items is a small character list.\n  All characters are checked against the other side. */\n\n  do\n    {\n    chr = *chr_ptr;\n\n    switch(list_ptr[0])\n      {\n      case OP_CHAR:\n      ochr_ptr = list_ptr + 2;\n      do\n        {\n        if (chr == *ochr_ptr) return FALSE;\n        ochr_ptr++;\n        }\n      while(*ochr_ptr != NOTACHAR);\n      break;\n\n      case OP_NOT:\n      ochr_ptr = list_ptr + 2;\n      do\n        {\n        if (chr == *ochr_ptr)\n          break;\n        ochr_ptr++;\n        }\n      while(*ochr_ptr != NOTACHAR);\n      if (*ochr_ptr == NOTACHAR) return FALSE;   /* Not found */\n      break;\n\n      /* Note that OP_DIGIT etc. are generated only when PCRE2_UCP is *not*\n      set. When it is set, \\d etc. are converted into OP_(NOT_)PROP codes. */\n\n      case OP_DIGIT:\n      if (chr < 256 && (cb->ctypes[chr] & ctype_digit) != 0) return FALSE;\n      break;\n\n      case OP_NOT_DIGIT:\n      if (chr > 255 || (cb->ctypes[chr] & ctype_digit) == 0) return FALSE;\n      break;\n\n      case OP_WHITESPACE:\n      if (chr < 256 && (cb->ctypes[chr] & ctype_space) != 0) return FALSE;\n      break;\n\n      case OP_NOT_WHITESPACE:\n      if (chr > 255 || (cb->ctypes[chr] & ctype_space) == 0) return FALSE;\n      break;\n\n      case OP_WORDCHAR:\n      if (chr < 255 && (cb->ctypes[chr] & ctype_word) != 0) return FALSE;\n      break;\n\n      case OP_NOT_WORDCHAR:\n      if (chr > 255 || (cb->ctypes[chr] & ctype_word) == 0) return FALSE;\n      break;\n\n      case OP_HSPACE:\n      switch(chr)\n        {\n        HSPACE_CASES: return FALSE;\n        default: break;\n        }\n      break;\n\n      case OP_NOT_HSPACE:\n      switch(chr)\n        {\n        HSPACE_CASES: break;\n        default: return FALSE;\n        }\n      break;\n\n      case OP_ANYNL:\n      case OP_VSPACE:\n      switch(chr)\n        {\n        VSPACE_CASES: return FALSE;\n        default: break;\n        }\n      break;\n\n      case OP_NOT_VSPACE:\n      switch(chr)\n        {\n        VSPACE_CASES: break;\n        default: return FALSE;\n        }\n      break;\n\n      case OP_DOLL:\n      case OP_EODN:\n      switch (chr)\n        {\n        case CHAR_CR:\n        case CHAR_LF:\n        case CHAR_VT:\n        case CHAR_FF:\n        case CHAR_NEL:\n#ifndef EBCDIC\n        case 0x2028:\n        case 0x2029:\n#endif  /* Not EBCDIC */\n        return FALSE;\n        }\n      break;\n\n      case OP_EOD:    /* Can always possessify before \\z */\n      break;\n\n#ifdef SUPPORT_UNICODE\n      case OP_PROP:\n      case OP_NOTPROP:\n      if (!check_char_prop(chr, list_ptr[2], list_ptr[3],\n            list_ptr[0] == OP_NOTPROP))\n        return FALSE;\n      break;\n#endif\n\n      case OP_NCLASS:\n      if (chr > 255) return FALSE;\n      /* Fall through */\n\n      case OP_CLASS:\n      if (chr > 255) break;\n      class_bitset = (const uint8_t *)\n        ((list_ptr == list ? code : base_end) - list_ptr[2]);\n      if ((class_bitset[chr >> 3] & (1u << (chr & 7))) != 0) return FALSE;\n      break;\n\n#ifdef SUPPORT_WIDE_CHARS\n      case OP_XCLASS:\n      if (PRIV(xclass)(chr, (list_ptr == list ? code : base_end) -\n          list_ptr[2] + LINK_SIZE, (const uint8_t*)cb->start_code, utf))\n        return FALSE;\n      break;\n\n      case OP_ECLASS:\n      if (PRIV(eclass)(chr,\n          (list_ptr == list ? code : base_end) - list_ptr[2] + LINK_SIZE,\n          (list_ptr == list ? code : base_end) - list_ptr[3],\n          (const uint8_t*)cb->start_code, utf))\n        return FALSE;\n      break;\n#endif /* SUPPORT_WIDE_CHARS */\n\n      default:\n      return FALSE;\n      }\n\n    chr_ptr++;\n    }\n  while(*chr_ptr != NOTACHAR);\n\n  /* At least one character must be matched from this opcode. */\n\n  if (list[1] == 0) return TRUE;\n  }\n\nPCRE2_DEBUG_UNREACHABLE(); /* Control should never reach here */\nreturn FALSE;              /* Avoid compiler warnings */\n}\n\n\n\n/*************************************************\n*    Scan compiled regex for auto-possession     *\n*************************************************/\n\n/* Replaces single character iterations with their possessive alternatives\nif appropriate. This function modifies the compiled opcode! Hitting a\nnon-existent opcode may indicate a bug in PCRE2, but it can also be caused if a\nbad UTF string was compiled with PCRE2_NO_UTF_CHECK. The rec_limit catches\noverly complicated or large patterns. In these cases, the check just stops,\nleaving the remainder of the pattern unpossessified.\n\nArguments:\n  code        points to start of the byte code\n  cb          compile data block\n\nReturns:      0 for success\n              -1 if a non-existant opcode is encountered\n*/\n\nint\nPRIV(auto_possessify)(PCRE2_UCHAR *code, const compile_block *cb)\n{\nPCRE2_UCHAR c;\nPCRE2_SPTR end;\nPCRE2_UCHAR *repeat_opcode;\nuint32_t list[MAX_LIST];\nint rec_limit = 1000;  /* Was 10,000 but clang+ASAN uses a lot of stack. */\nBOOL utf = (cb->external_options & PCRE2_UTF) != 0;\nBOOL ucp = (cb->external_options & PCRE2_UCP) != 0;\n\nfor (;;)\n  {\n  c = *code;\n\n  if (c >= OP_TABLE_LENGTH)\n    {\n    PCRE2_DEBUG_UNREACHABLE();\n    return -1;   /* Something gone wrong */\n    }\n\n  if (c >= OP_STAR && c <= OP_TYPEPOSUPTO)\n    {\n    c -= get_repeat_base(c) - OP_STAR;\n    end = (c <= OP_MINUPTO) ?\n      get_chr_property_list(code, utf, ucp, cb->fcc, list) : NULL;\n    list[1] = c == OP_STAR || c == OP_PLUS || c == OP_QUERY || c == OP_UPTO;\n\n    if (end != NULL && compare_opcodes(end, utf, ucp, cb, list, end,\n        &rec_limit))\n      {\n      switch(c)\n        {\n        case OP_STAR:\n        *code += OP_POSSTAR - OP_STAR;\n        break;\n\n        case OP_MINSTAR:\n        *code += OP_POSSTAR - OP_MINSTAR;\n        break;\n\n        case OP_PLUS:\n        *code += OP_POSPLUS - OP_PLUS;\n        break;\n\n        case OP_MINPLUS:\n        *code += OP_POSPLUS - OP_MINPLUS;\n        break;\n\n        case OP_QUERY:\n        *code += OP_POSQUERY - OP_QUERY;\n        break;\n\n        case OP_MINQUERY:\n        *code += OP_POSQUERY - OP_MINQUERY;\n        break;\n\n        case OP_UPTO:\n        *code += OP_POSUPTO - OP_UPTO;\n        break;\n\n        case OP_MINUPTO:\n        *code += OP_POSUPTO - OP_MINUPTO;\n        break;\n        }\n      }\n    c = *code;\n    }\n  else if (c == OP_CLASS || c == OP_NCLASS\n#ifdef SUPPORT_WIDE_CHARS\n           || c == OP_XCLASS || c == OP_ECLASS\n#endif\n           )\n    {\n#ifdef SUPPORT_WIDE_CHARS\n    if (c == OP_XCLASS || c == OP_ECLASS)\n      repeat_opcode = code + GET(code, 1);\n    else\n#endif\n      repeat_opcode = code + 1 + (32 / sizeof(PCRE2_UCHAR));\n\n    c = *repeat_opcode;\n    if (c >= OP_CRSTAR && c <= OP_CRMINRANGE)\n      {\n      /* The return from get_chr_property_list() will never be NULL when\n      *code (aka c) is one of the four class opcodes. However, gcc with\n      -fanalyzer notes that a NULL return is possible, and grumbles. Hence we\n      put in a check. */\n\n      end = get_chr_property_list(code, utf, ucp, cb->fcc, list);\n      list[1] = (c & 1) == 0;\n\n      if (end != NULL &&\n          compare_opcodes(end, utf, ucp, cb, list, end, &rec_limit))\n        {\n        switch (c)\n          {\n          case OP_CRSTAR:\n          case OP_CRMINSTAR:\n          *repeat_opcode = OP_CRPOSSTAR;\n          break;\n\n          case OP_CRPLUS:\n          case OP_CRMINPLUS:\n          *repeat_opcode = OP_CRPOSPLUS;\n          break;\n\n          case OP_CRQUERY:\n          case OP_CRMINQUERY:\n          *repeat_opcode = OP_CRPOSQUERY;\n          break;\n\n          case OP_CRRANGE:\n          case OP_CRMINRANGE:\n          *repeat_opcode = OP_CRPOSRANGE;\n          break;\n          }\n        }\n      }\n    c = *code;\n    }\n\n  switch(c)\n    {\n    case OP_END:\n    return 0;\n\n    case OP_TYPESTAR:\n    case OP_TYPEMINSTAR:\n    case OP_TYPEPLUS:\n    case OP_TYPEMINPLUS:\n    case OP_TYPEQUERY:\n    case OP_TYPEMINQUERY:\n    case OP_TYPEPOSSTAR:\n    case OP_TYPEPOSPLUS:\n    case OP_TYPEPOSQUERY:\n    if (code[1] == OP_PROP || code[1] == OP_NOTPROP) code += 2;\n    break;\n\n    case OP_TYPEUPTO:\n    case OP_TYPEMINUPTO:\n    case OP_TYPEEXACT:\n    case OP_TYPEPOSUPTO:\n    if (code[1 + IMM2_SIZE] == OP_PROP || code[1 + IMM2_SIZE] == OP_NOTPROP)\n      code += 2;\n    break;\n\n    case OP_CALLOUT_STR:\n    code += GET(code, 1 + 2*LINK_SIZE);\n    break;\n\n#ifdef SUPPORT_WIDE_CHARS\n    case OP_XCLASS:\n    case OP_ECLASS:\n    code += GET(code, 1);\n    break;\n#endif\n\n    case OP_MARK:\n    case OP_COMMIT_ARG:\n    case OP_PRUNE_ARG:\n    case OP_SKIP_ARG:\n    case OP_THEN_ARG:\n    code += code[1];\n    break;\n    }\n\n  /* Add in the fixed length from the table */\n\n  code += PRIV(OP_lengths)[c];\n\n  /* In UTF-8 and UTF-16 modes, opcodes that are followed by a character may be\n  followed by a multi-byte character. The length in the table is a minimum, so\n  we have to arrange to skip the extra code units. */\n\n#ifdef MAYBE_UTF_MULTI\n  if (utf) switch(c)\n    {\n    case OP_CHAR:\n    case OP_CHARI:\n    case OP_NOT:\n    case OP_NOTI:\n    case OP_STAR:\n    case OP_MINSTAR:\n    case OP_PLUS:\n    case OP_MINPLUS:\n    case OP_QUERY:\n    case OP_MINQUERY:\n    case OP_UPTO:\n    case OP_MINUPTO:\n    case OP_EXACT:\n    case OP_POSSTAR:\n    case OP_POSPLUS:\n    case OP_POSQUERY:\n    case OP_POSUPTO:\n    case OP_STARI:\n    case OP_MINSTARI:\n    case OP_PLUSI:\n    case OP_MINPLUSI:\n    case OP_QUERYI:\n    case OP_MINQUERYI:\n    case OP_UPTOI:\n    case OP_MINUPTOI:\n    case OP_EXACTI:\n    case OP_POSSTARI:\n    case OP_POSPLUSI:\n    case OP_POSQUERYI:\n    case OP_POSUPTOI:\n    case OP_NOTSTAR:\n    case OP_NOTMINSTAR:\n    case OP_NOTPLUS:\n    case OP_NOTMINPLUS:\n    case OP_NOTQUERY:\n    case OP_NOTMINQUERY:\n    case OP_NOTUPTO:\n    case OP_NOTMINUPTO:\n    case OP_NOTEXACT:\n    case OP_NOTPOSSTAR:\n    case OP_NOTPOSPLUS:\n    case OP_NOTPOSQUERY:\n    case OP_NOTPOSUPTO:\n    case OP_NOTSTARI:\n    case OP_NOTMINSTARI:\n    case OP_NOTPLUSI:\n    case OP_NOTMINPLUSI:\n    case OP_NOTQUERYI:\n    case OP_NOTMINQUERYI:\n    case OP_NOTUPTOI:\n    case OP_NOTMINUPTOI:\n    case OP_NOTEXACTI:\n    case OP_NOTPOSSTARI:\n    case OP_NOTPOSPLUSI:\n    case OP_NOTPOSQUERYI:\n    case OP_NOTPOSUPTOI:\n    if (HAS_EXTRALEN(code[-1])) code += GET_EXTRALEN(code[-1]);\n    break;\n    }\n#else\n  (void)(utf);  /* Keep compiler happy by referencing function argument */\n#endif  /* SUPPORT_WIDE_CHARS */\n  }\n}\n\n/* End of pcre2_auto_possess.c */\n"
  },
  {
    "path": "Libraries/PCRE/pcre2_chartables.c",
    "content": "/*************************************************\n*      Perl-Compatible Regular Expressions       *\n*************************************************/\n\n/* This file was automatically written by the pcre2_dftables auxiliary\nprogram. It contains character tables that are used when no external\ntables are passed to PCRE2 by the application that calls it. The tables\nare used only for characters whose code values are less than 256, and\nonly relevant if not in UCP mode. */\n\n/* This set of tables was written in the C locale. */\n\n/* The pcre2_ftables program (which is distributed with PCRE2) can be used\nto build alternative versions of this file. This is necessary if you are\nrunning in an EBCDIC environment, or if you want to default to a different\nencoding, for example ISO-8859-1. When pcre2_dftables is run, it creates\nthese tables in the \"C\" locale by default. This happens automatically if\nPCRE2 is configured with --enable-rebuild-chartables. However, you can run\npcre2_dftables manually with the -L option to build tables using the LC_ALL\nlocale. */\n\n#ifdef HAVE_CONFIG_H\n#include \"config.h\"\n#endif\n\n#include \"pcre2_internal.h\"\n\nconst uint8_t PRIV(default_tables)[] = {\n\n/* This table is a lower casing table. */\n\n    0,  1,  2,  3,  4,  5,  6,  7,\n    8,  9, 10, 11, 12, 13, 14, 15,\n   16, 17, 18, 19, 20, 21, 22, 23,\n   24, 25, 26, 27, 28, 29, 30, 31,\n   32, 33, 34, 35, 36, 37, 38, 39,\n   40, 41, 42, 43, 44, 45, 46, 47,\n   48, 49, 50, 51, 52, 53, 54, 55,\n   56, 57, 58, 59, 60, 61, 62, 63,\n   64, 97, 98, 99,100,101,102,103,\n  104,105,106,107,108,109,110,111,\n  112,113,114,115,116,117,118,119,\n  120,121,122, 91, 92, 93, 94, 95,\n   96, 97, 98, 99,100,101,102,103,\n  104,105,106,107,108,109,110,111,\n  112,113,114,115,116,117,118,119,\n  120,121,122,123,124,125,126,127,\n  128,129,130,131,132,133,134,135,\n  136,137,138,139,140,141,142,143,\n  144,145,146,147,148,149,150,151,\n  152,153,154,155,156,157,158,159,\n  160,161,162,163,164,165,166,167,\n  168,169,170,171,172,173,174,175,\n  176,177,178,179,180,181,182,183,\n  184,185,186,187,188,189,190,191,\n  192,193,194,195,196,197,198,199,\n  200,201,202,203,204,205,206,207,\n  208,209,210,211,212,213,214,215,\n  216,217,218,219,220,221,222,223,\n  224,225,226,227,228,229,230,231,\n  232,233,234,235,236,237,238,239,\n  240,241,242,243,244,245,246,247,\n  248,249,250,251,252,253,254,255,\n\n/* This table is a case flipping table. */\n\n    0,  1,  2,  3,  4,  5,  6,  7,\n    8,  9, 10, 11, 12, 13, 14, 15,\n   16, 17, 18, 19, 20, 21, 22, 23,\n   24, 25, 26, 27, 28, 29, 30, 31,\n   32, 33, 34, 35, 36, 37, 38, 39,\n   40, 41, 42, 43, 44, 45, 46, 47,\n   48, 49, 50, 51, 52, 53, 54, 55,\n   56, 57, 58, 59, 60, 61, 62, 63,\n   64, 97, 98, 99,100,101,102,103,\n  104,105,106,107,108,109,110,111,\n  112,113,114,115,116,117,118,119,\n  120,121,122, 91, 92, 93, 94, 95,\n   96, 65, 66, 67, 68, 69, 70, 71,\n   72, 73, 74, 75, 76, 77, 78, 79,\n   80, 81, 82, 83, 84, 85, 86, 87,\n   88, 89, 90,123,124,125,126,127,\n  128,129,130,131,132,133,134,135,\n  136,137,138,139,140,141,142,143,\n  144,145,146,147,148,149,150,151,\n  152,153,154,155,156,157,158,159,\n  160,161,162,163,164,165,166,167,\n  168,169,170,171,172,173,174,175,\n  176,177,178,179,180,181,182,183,\n  184,185,186,187,188,189,190,191,\n  192,193,194,195,196,197,198,199,\n  200,201,202,203,204,205,206,207,\n  208,209,210,211,212,213,214,215,\n  216,217,218,219,220,221,222,223,\n  224,225,226,227,228,229,230,231,\n  232,233,234,235,236,237,238,239,\n  240,241,242,243,244,245,246,247,\n  248,249,250,251,252,253,254,255,\n\n/* This table contains bit maps for various character classes. Each map is 32\nbytes long and the bits run from the least significant end of each byte. The\nclasses that have their own maps are: space, xdigit, digit, upper, lower, word,\ngraph, print, punct, and cntrl. Other classes are built from combinations. */\n\n  0x00,0x3e,0x00,0x00,0x01,0x00,0x00,0x00,  /* space */\n  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\n  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\n  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\n\n  0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03,  /* xdigit */\n  0x7e,0x00,0x00,0x00,0x7e,0x00,0x00,0x00,\n  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\n  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\n\n  0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03,  /* digit */\n  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\n  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\n  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\n\n  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  /* upper */\n  0xfe,0xff,0xff,0x07,0x00,0x00,0x00,0x00,\n  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\n  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\n\n  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  /* lower */\n  0x00,0x00,0x00,0x00,0xfe,0xff,0xff,0x07,\n  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\n  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\n\n  0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03,  /* word */\n  0xfe,0xff,0xff,0x87,0xfe,0xff,0xff,0x07,\n  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\n  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\n\n  0x00,0x00,0x00,0x00,0xfe,0xff,0xff,0xff,  /* graph */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f,\n  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\n  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\n\n  0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,  /* print */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f,\n  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\n  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\n\n  0x00,0x00,0x00,0x00,0xfe,0xff,0x00,0xfc,  /* punct */\n  0x01,0x00,0x00,0xf8,0x01,0x00,0x00,0x78,\n  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\n  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\n\n  0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,  /* cntrl */\n  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,\n  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\n  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\n\n/* This table identifies various classes of character by individual bits:\n  0x01   white space character\n  0x02   letter\n  0x04   lower case letter\n  0x08   decimal digit\n  0x10   word (alphanumeric or '_')\n*/\n\n  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*   0-  7 */\n  0x00,0x01,0x01,0x01,0x01,0x01,0x00,0x00, /*   8- 15 */\n  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  16- 23 */\n  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  24- 31 */\n  0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*    - '  */\n  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  ( - /  */\n  0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, /*  0 - 7  */\n  0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00, /*  8 - ?  */\n  0x00,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /*  @ - G  */\n  0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /*  H - O  */\n  0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /*  P - W  */\n  0x12,0x12,0x12,0x00,0x00,0x00,0x00,0x10, /*  X - _  */\n  0x00,0x16,0x16,0x16,0x16,0x16,0x16,0x16, /*  ` - g  */\n  0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16, /*  h - o  */\n  0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16, /*  p - w  */\n  0x16,0x16,0x16,0x00,0x00,0x00,0x00,0x00, /*  x -127 */\n  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 128-135 */\n  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 136-143 */\n  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 144-151 */\n  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 152-159 */\n  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 160-167 */\n  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 168-175 */\n  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 176-183 */\n  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 184-191 */\n  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 192-199 */\n  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 200-207 */\n  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 208-215 */\n  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 216-223 */\n  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 224-231 */\n  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 232-239 */\n  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 240-247 */\n  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};/* 248-255 */\n\n/* End of pcre2_chartables.c */\n"
  },
  {
    "path": "Libraries/PCRE/pcre2_chkdint.c",
    "content": "/*************************************************\n*      Perl-Compatible Regular Expressions       *\n*************************************************/\n\n/* PCRE is a library of functions to support regular expressions whose syntax\nand semantics are as close as possible to those of the Perl 5 language.\n\n                     Written by Philip Hazel\n            Copyright (c) 2023 University of Cambridge\n\n-----------------------------------------------------------------------------\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n    * Redistributions of source code must retain the above copyright notice,\n      this list of conditions and the following disclaimer.\n\n    * Redistributions in binary form must reproduce the above copyright\n      notice, this list of conditions and the following disclaimer in the\n      documentation and/or other materials provided with the distribution.\n\n    * Neither the name of the University of Cambridge nor the names of its\n      contributors may be used to endorse or promote products derived from\n      this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\nLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\nCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\nSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\nINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\nCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGE.\n-----------------------------------------------------------------------------\n*/\n\n/* This file contains functions to implement checked integer operation */\n\n#ifndef PCRE2_PCRE2TEST\n#ifdef HAVE_CONFIG_H\n#include \"config.h\"\n#endif\n\n#include \"pcre2_internal.h\"\n#endif\n\n/*************************************************\n*        Checked Integer Multiplication          *\n*************************************************/\n\n/*\nArguments:\n  r         A pointer to PCRE2_SIZE to store the answer\n  a, b      Two integers\n\nReturns:    Bool indicating if the operation overflows\n\nIt is modeled after C23's <stdckdint.h> interface\nThe INT64_OR_DOUBLE type is a 64-bit integer type when available,\notherwise double. */\n\nBOOL\nPRIV(ckd_smul)(PCRE2_SIZE *r, int a, int b)\n{\n#ifdef HAVE_BUILTIN_MUL_OVERFLOW\nPCRE2_SIZE m;\n\nif (__builtin_mul_overflow(a, b, &m)) return TRUE;\n\n*r = m;\n#else\nINT64_OR_DOUBLE m;\n\nPCRE2_ASSERT(a >= 0 && b >= 0);\n\nm = (INT64_OR_DOUBLE)a * (INT64_OR_DOUBLE)b;\n\n#if defined INT64_MAX || defined int64_t\nif (sizeof(m) > sizeof(*r) && m > (INT64_OR_DOUBLE)PCRE2_SIZE_MAX) return TRUE;\n*r = (PCRE2_SIZE)m;\n#else\nif (m > PCRE2_SIZE_MAX) return TRUE;\n*r = m;\n#endif\n\n#endif\n\nreturn FALSE;\n}\n\n/* End of pcre2_chkdint.c */\n"
  },
  {
    "path": "Libraries/PCRE/pcre2_compile.c",
    "content": "/*************************************************\n*      Perl-Compatible Regular Expressions       *\n*************************************************/\n\n/* PCRE is a library of functions to support regular expressions whose syntax\nand semantics are as close as possible to those of the Perl 5 language.\n\n                       Written by Philip Hazel\n     Original API code Copyright (c) 1997-2012 University of Cambridge\n          New API code Copyright (c) 2016-2024 University of Cambridge\n\n-----------------------------------------------------------------------------\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n    * Redistributions of source code must retain the above copyright notice,\n      this list of conditions and the following disclaimer.\n\n    * Redistributions in binary form must reproduce the above copyright\n      notice, this list of conditions and the following disclaimer in the\n      documentation and/or other materials provided with the distribution.\n\n    * Neither the name of the University of Cambridge nor the names of its\n      contributors may be used to endorse or promote products derived from\n      this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\nLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\nCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\nSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\nINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\nCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGE.\n-----------------------------------------------------------------------------\n*/\n\n\n#ifdef HAVE_CONFIG_H\n#include \"config.h\"\n#endif\n\n#define NLBLOCK cb             /* Block containing newline information */\n#define PSSTART start_pattern  /* Field containing processed string start */\n#define PSEND   end_pattern    /* Field containing processed string end */\n\n#include \"pcre2_compile.h\"\n\n/* In rare error cases debugging might require calling pcre2_printint(). */\n\n#if 0\n#ifdef EBCDIC\n#define PRINTABLE(c) ((c) >= 64 && (c) < 255)\n#else\n#define PRINTABLE(c) ((c) >= 32 && (c) < 127)\n#endif\n#include \"pcre2_printint.c\"\n#define DEBUG_CALL_PRINTINT\n#endif\n\n/* Other debugging code can be enabled by these defines. */\n\n/* #define DEBUG_SHOW_CAPTURES */\n/* #define DEBUG_SHOW_PARSED */\n\n/* There are a few things that vary with different code unit sizes. Handle them\nby defining macros in order to minimize #if usage. */\n\n#if PCRE2_CODE_UNIT_WIDTH == 8\n#define STRING_UTFn_RIGHTPAR     STRING_UTF8_RIGHTPAR, 5\n#define XDIGIT(c)                xdigitab[c]\n\n#else  /* Either 16-bit or 32-bit */\n#define XDIGIT(c)                (MAX_255(c)? xdigitab[c] : 0xff)\n\n#if PCRE2_CODE_UNIT_WIDTH == 16\n#define STRING_UTFn_RIGHTPAR     STRING_UTF16_RIGHTPAR, 6\n\n#else  /* 32-bit */\n#define STRING_UTFn_RIGHTPAR     STRING_UTF32_RIGHTPAR, 6\n#endif\n#endif\n\n/* Macros to store and retrieve a PCRE2_SIZE value in the parsed pattern, which\nconsists of uint32_t elements. Assume that if uint32_t can't hold it, two of\nthem will be able to (i.e. assume a 64-bit world). */\n\n#if PCRE2_SIZE_MAX <= UINT32_MAX\n#define PUTOFFSET(s,p) *p++ = s\n#define GETOFFSET(s,p) s = *p++\n#define GETPLUSOFFSET(s,p) s = *(++p)\n#define READPLUSOFFSET(s,p) s = p[1]\n#define SKIPOFFSET(p) p++\n#define SIZEOFFSET 1\n#else\n#define PUTOFFSET(s,p) \\\n  { *p++ = (uint32_t)(s >> 32); *p++ = (uint32_t)(s & 0xffffffff); }\n#define GETOFFSET(s,p) \\\n  { s = ((PCRE2_SIZE)p[0] << 32) | (PCRE2_SIZE)p[1]; p += 2; }\n#define GETPLUSOFFSET(s,p) \\\n  { s = ((PCRE2_SIZE)p[1] << 32) | (PCRE2_SIZE)p[2]; p += 2; }\n#define READPLUSOFFSET(s,p) \\\n  { s = ((PCRE2_SIZE)p[1] << 32) | (PCRE2_SIZE)p[2]; }\n#define SKIPOFFSET(p) p += 2\n#define SIZEOFFSET 2\n#endif\n\n/* Function definitions to allow mutual recursion */\n\nstatic int\n  compile_regex(uint32_t, uint32_t, PCRE2_UCHAR **, uint32_t **, int *,\n    uint32_t, uint32_t *, uint32_t *, uint32_t *, uint32_t *, branch_chain *,\n    open_capitem *, compile_block *, PCRE2_SIZE *);\n\nstatic int\n  get_branchlength(uint32_t **, int *, int *, int *, parsed_recurse_check *,\n    compile_block *);\n\nstatic BOOL\n  set_lookbehind_lengths(uint32_t **, int *, int *, parsed_recurse_check *,\n    compile_block *);\n\nstatic int\n  check_lookbehinds(uint32_t *, uint32_t **, parsed_recurse_check *,\n    compile_block *, int *);\n\n\n/*************************************************\n*      Code parameters and static tables         *\n*************************************************/\n\n#define MAX_GROUP_NUMBER   65535u\n#define MAX_REPEAT_COUNT   65535u\n#define REPEAT_UNLIMITED   (MAX_REPEAT_COUNT+1)\n\n/* COMPILE_WORK_SIZE specifies the size of stack workspace, which is used in\ndifferent ways in the different pattern scans. The parsing and group-\nidentifying pre-scan uses it to handle nesting, and needs it to be 16-bit\naligned for this. Having defined the size in code units, we set up\nC16_WORK_SIZE as the number of elements in the 16-bit vector.\n\nDuring the first compiling phase, when determining how much memory is required,\nthe regex is partly compiled into this space, but the compiled parts are\ndiscarded as soon as they can be, so that hopefully there will never be an\noverrun. The code does, however, check for an overrun, which can occur for\npathological patterns. The size of the workspace depends on LINK_SIZE because\nthe length of compiled items varies with this.\n\nIn the real compile phase, this workspace is not currently used. */\n\n#define COMPILE_WORK_SIZE (3000*LINK_SIZE)   /* Size in code units */\n\n#define C16_WORK_SIZE \\\n  ((COMPILE_WORK_SIZE * sizeof(PCRE2_UCHAR))/sizeof(uint16_t))\n\n/* A uint32_t vector is used for caching information about the size of\ncapturing groups, to improve performance. A default is created on the stack of\nthis size. */\n\n#define GROUPINFO_DEFAULT_SIZE 256\n\n/* The overrun tests check for a slightly smaller size so that they detect the\noverrun before it actually does run off the end of the data block. */\n\n#define WORK_SIZE_SAFETY_MARGIN (100)\n\n/* This value determines the size of the initial vector that is used for\nremembering named groups during the pre-compile. It is allocated on the stack,\nbut if it is too small, it is expanded, in a similar way to the workspace. The\nvalue is the number of slots in the list. */\n\n#define NAMED_GROUP_LIST_SIZE  20\n\n/* The pre-compiling pass over the pattern creates a parsed pattern in a vector\nof uint32_t. For short patterns this lives on the stack, with this size. Heap\nmemory is used for longer patterns. */\n\n#define PARSED_PATTERN_DEFAULT_SIZE 1024\n\n/* Maximum length value to check against when making sure that the variable\nthat holds the compiled pattern length does not overflow. We make it a bit less\nthan INT_MAX to allow for adding in group terminating code units, so that we\ndon't have to check them every time. */\n\n#define OFLOW_MAX (INT_MAX - 20)\n\n/* Table of extra lengths for each of the meta codes. Must be kept in step with\nthe definitions above. For some items these values are a basic length to which\na variable amount has to be added. */\n\nstatic unsigned char meta_extra_lengths[] = {\n  0,             /* META_END */\n  0,             /* META_ALT */\n  0,             /* META_ATOMIC */\n  0,             /* META_BACKREF - more if group is >= 10 */\n  1+SIZEOFFSET,  /* META_BACKREF_BYNAME */\n  1,             /* META_BIGVALUE */\n  3,             /* META_CALLOUT_NUMBER */\n  3+SIZEOFFSET,  /* META_CALLOUT_STRING */\n  0,             /* META_CAPTURE */\n  0,             /* META_CIRCUMFLEX */\n  0,             /* META_CLASS */\n  0,             /* META_CLASS_EMPTY */\n  0,             /* META_CLASS_EMPTY_NOT */\n  0,             /* META_CLASS_END */\n  0,             /* META_CLASS_NOT */\n  0,             /* META_COND_ASSERT */\n  SIZEOFFSET,    /* META_COND_DEFINE */\n  1+SIZEOFFSET,  /* META_COND_NAME */\n  1+SIZEOFFSET,  /* META_COND_NUMBER */\n  1+SIZEOFFSET,  /* META_COND_RNAME */\n  1+SIZEOFFSET,  /* META_COND_RNUMBER */\n  3,             /* META_COND_VERSION */\n  SIZEOFFSET,    /* META_OFFSET */\n  0,             /* META_SCS */\n  1,             /* META_SCS_NAME */\n  1,             /* META_SCS_NUMBER */\n  0,             /* META_DOLLAR */\n  0,             /* META_DOT */\n  0,             /* META_ESCAPE - one more for ESC_P and ESC_p */\n  0,             /* META_KET */\n  0,             /* META_NOCAPTURE */\n  2,             /* META_OPTIONS */\n  1,             /* META_POSIX */\n  1,             /* META_POSIX_NEG */\n  0,             /* META_RANGE_ESCAPED */\n  0,             /* META_RANGE_LITERAL */\n  SIZEOFFSET,    /* META_RECURSE */\n  1+SIZEOFFSET,  /* META_RECURSE_BYNAME */\n  0,             /* META_SCRIPT_RUN */\n  0,             /* META_LOOKAHEAD */\n  0,             /* META_LOOKAHEADNOT */\n  SIZEOFFSET,    /* META_LOOKBEHIND */\n  SIZEOFFSET,    /* META_LOOKBEHINDNOT */\n  0,             /* META_LOOKAHEAD_NA */\n  SIZEOFFSET,    /* META_LOOKBEHIND_NA */\n  1,             /* META_MARK - plus the string length */\n  0,             /* META_ACCEPT */\n  0,             /* META_FAIL */\n  0,             /* META_COMMIT */\n  1,             /* META_COMMIT_ARG - plus the string length */\n  0,             /* META_PRUNE */\n  1,             /* META_PRUNE_ARG - plus the string length */\n  0,             /* META_SKIP */\n  1,             /* META_SKIP_ARG - plus the string length */\n  0,             /* META_THEN */\n  1,             /* META_THEN_ARG - plus the string length */\n  0,             /* META_ASTERISK */\n  0,             /* META_ASTERISK_PLUS */\n  0,             /* META_ASTERISK_QUERY */\n  0,             /* META_PLUS */\n  0,             /* META_PLUS_PLUS */\n  0,             /* META_PLUS_QUERY */\n  0,             /* META_QUERY */\n  0,             /* META_QUERY_PLUS */\n  0,             /* META_QUERY_QUERY */\n  2,             /* META_MINMAX */\n  2,             /* META_MINMAX_PLUS */\n  2,             /* META_MINMAX_QUERY */\n  0,             /* META_ECLASS_AND */\n  0,             /* META_ECLASS_OR */\n  0,             /* META_ECLASS_SUB */\n  0,             /* META_ECLASS_XOR */\n  0              /* META_ECLASS_NOT */\n};\n\n/* Types for skipping parts of a parsed pattern. */\n\nenum { PSKIP_ALT, PSKIP_CLASS, PSKIP_KET };\n\n/* Values and flags for the unsigned xxcuflags variables that accompany xxcu\nvariables, which are concerned with first and required code units. A value\ngreater than or equal to REQ_NONE means \"no code unit set\"; otherwise the\nmatching xxcu variable is set, and the low valued bits are relevant. */\n\n#define REQ_UNSET     0xffffffffu  /* Not yet found anything */\n#define REQ_NONE      0xfffffffeu  /* Found not fixed character */\n#define REQ_CASELESS  0x00000001u  /* Code unit in xxcu is caseless */\n#define REQ_VARY      0x00000002u  /* Code unit is followed by non-literal */\n\n/* These flags are used in the groupinfo vector. */\n\n#define GI_SET_FIXED_LENGTH    0x80000000u\n#define GI_NOT_FIXED_LENGTH    0x40000000u\n#define GI_FIXED_LENGTH_MASK   0x0000ffffu\n\n/* This simple test for a decimal digit works for both ASCII/Unicode and EBCDIC\nand is fast (a good compiler can turn it into a subtraction and unsigned\ncomparison). */\n\n#define IS_DIGIT(x) ((x) >= CHAR_0 && (x) <= CHAR_9)\n\n/* Table to identify hex digits. The tables in chartables are dependent on the\nlocale, and may mark arbitrary characters as digits. We want to recognize only\n0-9, a-z, and A-Z as hex digits, which is why we have a private table here. It\ncosts 256 bytes, but it is a lot faster than doing character value tests (at\nleast in some simple cases I timed), and in some applications one wants PCRE2\nto compile efficiently as well as match efficiently. The value in the table is\nthe binary hex digit value, or 0xff for non-hex digits. */\n\n/* This is the \"normal\" case, for ASCII systems, and EBCDIC systems running in\nUTF-8 mode. */\n\n#ifndef EBCDIC\nstatic const uint8_t xdigitab[] =\n  {\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /*   0-  7 */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /*   8- 15 */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /*  16- 23 */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /*  24- 31 */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /*    - '  */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /*  ( - /  */\n  0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, /*  0 - 7  */\n  0x08,0x09,0xff,0xff,0xff,0xff,0xff,0xff, /*  8 - ?  */\n  0xff,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0xff, /*  @ - G  */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /*  H - O  */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /*  P - W  */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /*  X - _  */\n  0xff,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0xff, /*  ` - g  */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /*  h - o  */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /*  p - w  */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /*  x -127 */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 128-135 */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 136-143 */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 144-151 */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 152-159 */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 160-167 */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 168-175 */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 176-183 */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 184-191 */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 192-199 */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 2ff-207 */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 208-215 */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 216-223 */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 224-231 */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 232-239 */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 240-247 */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};/* 248-255 */\n\n#else\n\n/* This is the \"abnormal\" case, for EBCDIC systems not running in UTF-8 mode. */\n\nstatic const uint8_t xdigitab[] =\n  {\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /*   0-  7  0 */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /*   8- 15    */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /*  16- 23 10 */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /*  24- 31    */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /*  32- 39 20 */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /*  40- 47    */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /*  48- 55 30 */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /*  56- 63    */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /*    - 71 40 */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /*  72- |     */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /*  & - 87 50 */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /*  88- 95    */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /*  - -103 60 */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 104- ?     */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 112-119 70 */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 120- \"     */\n  0xff,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0xff, /* 128- g  80 */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /*  h -143    */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 144- p  90 */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /*  q -159    */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 160- x  A0 */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /*  y -175    */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /*  ^ -183 B0 */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 184-191    */\n  0xff,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0xff, /*  { - G  C0 */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /*  H -207    */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /*  } - P  D0 */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /*  Q -223    */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /*  \\ - X  E0 */\n  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /*  Y -239    */\n  0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, /*  0 - 7  F0 */\n  0x08,0x09,0xff,0xff,0xff,0xff,0xff,0xff};/*  8 -255    */\n#endif  /* EBCDIC */\n\n\n/* Table for handling alphanumeric escaped characters. Positive returns are\nsimple data values; negative values are for special things like \\d and so on.\nZero means further processing is needed (for things like \\x), or the escape is\ninvalid. */\n\n/* This is the \"normal\" table for ASCII systems or for EBCDIC systems running\nin UTF-8 mode. It runs from '0' to 'z'. */\n\n#ifndef EBCDIC\n#define ESCAPES_FIRST       CHAR_0\n#define ESCAPES_LAST        CHAR_z\n#define UPPER_CASE(c)       (c-32)\n\nstatic const short int escapes[] = {\n    /* 0 */ 0,                       /* 1 */ 0,\n    /* 2 */ 0,                       /* 3 */ 0,\n    /* 4 */ 0,                       /* 5 */ 0,\n    /* 6 */ 0,                       /* 7 */ 0,\n    /* 8 */ 0,                       /* 9 */ 0,\n    /* : */ CHAR_COLON,              /* ; */ CHAR_SEMICOLON,\n    /* < */ CHAR_LESS_THAN_SIGN,     /* = */ CHAR_EQUALS_SIGN,\n    /* > */ CHAR_GREATER_THAN_SIGN,  /* ? */ CHAR_QUESTION_MARK,\n    /* @ */ CHAR_COMMERCIAL_AT,      /* A */ -ESC_A,\n    /* B */ -ESC_B,                  /* C */ -ESC_C,\n    /* D */ -ESC_D,                  /* E */ -ESC_E,\n    /* F */ 0,                       /* G */ -ESC_G,\n    /* H */ -ESC_H,                  /* I */ 0,\n    /* J */ 0,                       /* K */ -ESC_K,\n    /* L */ 0,                       /* M */ 0,\n    /* N */ -ESC_N,                  /* O */ 0,\n    /* P */ -ESC_P,                  /* Q */ -ESC_Q,\n    /* R */ -ESC_R,                  /* S */ -ESC_S,\n    /* T */ 0,                       /* U */ 0,\n    /* V */ -ESC_V,                  /* W */ -ESC_W,\n    /* X */ -ESC_X,                  /* Y */ 0,\n    /* Z */ -ESC_Z,                  /* [ */ CHAR_LEFT_SQUARE_BRACKET,\n    /* \\ */ CHAR_BACKSLASH,          /* ] */ CHAR_RIGHT_SQUARE_BRACKET,\n    /* ^ */ CHAR_CIRCUMFLEX_ACCENT,  /* _ */ CHAR_UNDERSCORE,\n    /* ` */ CHAR_GRAVE_ACCENT,       /* a */ CHAR_BEL,\n    /* b */ -ESC_b,                  /* c */ 0,\n    /* d */ -ESC_d,                  /* e */ CHAR_ESC,\n    /* f */ CHAR_FF,                 /* g */ 0,\n    /* h */ -ESC_h,                  /* i */ 0,\n    /* j */ 0,                       /* k */ -ESC_k,\n    /* l */ 0,                       /* m */ 0,\n    /* n */ CHAR_LF,                 /* o */ 0,\n    /* p */ -ESC_p,                  /* q */ 0,\n    /* r */ CHAR_CR,                 /* s */ -ESC_s,\n    /* t */ CHAR_HT,                 /* u */ 0,\n    /* v */ -ESC_v,                  /* w */ -ESC_w,\n    /* x */ 0,                       /* y */ 0,\n    /* z */ -ESC_z\n};\n\n#else\n\n/* This is the \"abnormal\" table for EBCDIC systems without UTF-8 support.\nIt runs from 'a' to '9'. For some minimal testing of EBCDIC features, the code\nis sometimes compiled on an ASCII system. In this case, we must not use CHAR_a\nbecause it is defined as 'a', which of course picks up the ASCII value. */\n\n#if 'a' == 0x81                    /* Check for a real EBCDIC environment */\n#define ESCAPES_FIRST       CHAR_a\n#define ESCAPES_LAST        CHAR_9\n#define UPPER_CASE(c)       (c+64)\n#else                              /* Testing in an ASCII environment */\n#define ESCAPES_FIRST  ((unsigned char)'\\x81')   /* EBCDIC 'a' */\n#define ESCAPES_LAST   ((unsigned char)'\\xf9')   /* EBCDIC '9' */\n#define UPPER_CASE(c)  (c-32)\n#endif\n\nstatic const short int escapes[] = {\n/*  80 */         CHAR_BEL, -ESC_b,       0, -ESC_d, CHAR_ESC, CHAR_FF,      0,\n/*  88 */ -ESC_h,        0,      0,     '{',      0,        0,       0,      0,\n/*  90 */      0,        0, -ESC_k,       0,      0,  CHAR_LF,       0, -ESC_p,\n/*  98 */      0,  CHAR_CR,      0,     '}',      0,        0,       0,      0,\n/*  A0 */      0,      '~', -ESC_s, CHAR_HT,      0,   -ESC_v,  -ESC_w,      0,\n/*  A8 */      0,   -ESC_z,      0,       0,      0,      '[',       0,      0,\n/*  B0 */      0,        0,      0,       0,      0,        0,       0,      0,\n/*  B8 */      0,        0,      0,       0,      0,      ']',     '=',    '-',\n/*  C0 */    '{',   -ESC_A, -ESC_B,  -ESC_C, -ESC_D,   -ESC_E,       0, -ESC_G,\n/*  C8 */ -ESC_H,        0,      0,       0,      0,        0,       0,      0,\n/*  D0 */    '}',        0, -ESC_K,       0,      0,   -ESC_N,       0, -ESC_P,\n/*  D8 */ -ESC_Q,   -ESC_R,      0,       0,      0,        0,       0,      0,\n/*  E0 */   '\\\\',        0, -ESC_S,       0,      0,   -ESC_V,  -ESC_W, -ESC_X,\n/*  E8 */      0,   -ESC_Z,      0,       0,      0,        0,       0,      0,\n/*  F0 */      0,        0,      0,       0,      0,        0,       0,      0,\n/*  F8 */      0,        0\n};\n\n/* We also need a table of characters that may follow \\c in an EBCDIC\nenvironment for characters 0-31. */\n\nstatic unsigned char ebcdic_escape_c[] = \"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_\";\n\n#endif   /* EBCDIC */\n\n\n/* Table of special \"verbs\" like (*PRUNE). This is a short table, so it is\nsearched linearly. Put all the names into a single string, in order to reduce\nthe number of relocations when a shared library is dynamically linked. The\nstring is built from string macros so that it works in UTF-8 mode on EBCDIC\nplatforms. */\n\ntypedef struct verbitem {\n  unsigned int len;          /* Length of verb name */\n  uint32_t meta;             /* Base META_ code */\n  int has_arg;               /* Argument requirement */\n} verbitem;\n\nstatic const char verbnames[] =\n  \"\\0\"                       /* Empty name is a shorthand for MARK */\n  STRING_MARK0\n  STRING_ACCEPT0\n  STRING_F0\n  STRING_FAIL0\n  STRING_COMMIT0\n  STRING_PRUNE0\n  STRING_SKIP0\n  STRING_THEN;\n\nstatic const verbitem verbs[] = {\n  { 0, META_MARK,   +1 },  /* > 0 => must have an argument */\n  { 4, META_MARK,   +1 },\n  { 6, META_ACCEPT, -1 },  /* < 0 => Optional argument, convert to pre-MARK */\n  { 1, META_FAIL,   -1 },\n  { 4, META_FAIL,   -1 },\n  { 6, META_COMMIT,  0 },\n  { 5, META_PRUNE,   0 },  /* Optional argument; bump META code if found */\n  { 4, META_SKIP,    0 },\n  { 4, META_THEN,    0 }\n};\n\nstatic const int verbcount = sizeof(verbs)/sizeof(verbitem);\n\n/* Verb opcodes, indexed by their META code offset from META_MARK. */\n\nstatic const uint32_t verbops[] = {\n  OP_MARK, OP_ACCEPT, OP_FAIL, OP_COMMIT, OP_COMMIT_ARG, OP_PRUNE,\n  OP_PRUNE_ARG, OP_SKIP, OP_SKIP_ARG, OP_THEN, OP_THEN_ARG };\n\n/* Table of \"alpha assertions\" like (*pla:...), similar to the (*VERB) table. */\n\ntypedef struct alasitem {\n  unsigned int len;          /* Length of name */\n  uint32_t meta;             /* Base META_ code */\n} alasitem;\n\nstatic const char alasnames[] =\n  STRING_pla0\n  STRING_plb0\n  STRING_napla0\n  STRING_naplb0\n  STRING_nla0\n  STRING_nlb0\n  STRING_positive_lookahead0\n  STRING_positive_lookbehind0\n  STRING_non_atomic_positive_lookahead0\n  STRING_non_atomic_positive_lookbehind0\n  STRING_negative_lookahead0\n  STRING_negative_lookbehind0\n  STRING_scs0\n  STRING_scan_substring0\n  STRING_atomic0\n  STRING_sr0\n  STRING_asr0\n  STRING_script_run0\n  STRING_atomic_script_run;\n\nstatic const alasitem alasmeta[] = {\n  {  3, META_LOOKAHEAD         },\n  {  3, META_LOOKBEHIND        },\n  {  5, META_LOOKAHEAD_NA      },\n  {  5, META_LOOKBEHIND_NA     },\n  {  3, META_LOOKAHEADNOT      },\n  {  3, META_LOOKBEHINDNOT     },\n  { 18, META_LOOKAHEAD         },\n  { 19, META_LOOKBEHIND        },\n  { 29, META_LOOKAHEAD_NA      },\n  { 30, META_LOOKBEHIND_NA     },\n  { 18, META_LOOKAHEADNOT      },\n  { 19, META_LOOKBEHINDNOT     },\n  {  3, META_SCS               },\n  { 14, META_SCS               },\n  {  6, META_ATOMIC            },\n  {  2, META_SCRIPT_RUN        }, /* sr = script run */\n  {  3, META_ATOMIC_SCRIPT_RUN }, /* asr = atomic script run */\n  { 10, META_SCRIPT_RUN        }, /* script run */\n  { 17, META_ATOMIC_SCRIPT_RUN }  /* atomic script run */\n};\n\nstatic const int alascount = sizeof(alasmeta)/sizeof(alasitem);\n\n/* Offsets from OP_STAR for case-independent and negative repeat opcodes. */\n\nstatic uint32_t chartypeoffset[] = {\n  OP_STAR - OP_STAR,    OP_STARI - OP_STAR,\n  OP_NOTSTAR - OP_STAR, OP_NOTSTARI - OP_STAR };\n\n/* Tables of names of POSIX character classes and their lengths. The names are\nnow all in a single string, to reduce the number of relocations when a shared\nlibrary is dynamically loaded. The list of lengths is terminated by a zero\nlength entry. The first three must be alpha, lower, upper, as this is assumed\nfor handling case independence.\n\nThe indices for several classes are stored in pcre2_compile.h - these must\nbe kept in sync with posix_names, posix_name_lengths, posix_class_maps,\nand posix_substitutes. */\n\nstatic const char posix_names[] =\n  STRING_alpha0 STRING_lower0 STRING_upper0 STRING_alnum0\n  STRING_ascii0 STRING_blank0 STRING_cntrl0 STRING_digit0\n  STRING_graph0 STRING_print0 STRING_punct0 STRING_space0\n  STRING_word0  STRING_xdigit;\n\nstatic const uint8_t posix_name_lengths[] = {\n  5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 6, 0 };\n\n/* Table of class bit maps for each POSIX class. Each class is formed from a\nbase map, with an optional addition or removal of another map. Then, for some\nclasses, there is some additional tweaking: for [:blank:] the vertical space\ncharacters are removed, and for [:alpha:] and [:alnum:] the underscore\ncharacter is removed. The triples in the table consist of the base map offset,\nsecond map offset or -1 if no second map, and a non-negative value for map\naddition or a negative value for map subtraction (if there are two maps). The\nabsolute value of the third field has these meanings: 0 => no tweaking, 1 =>\nremove vertical space characters, 2 => remove underscore. */\n\nconst int PRIV(posix_class_maps)[] = {\n  cbit_word,   cbit_digit, -2,            /* alpha */\n  cbit_lower,  -1,          0,            /* lower */\n  cbit_upper,  -1,          0,            /* upper */\n  cbit_word,   -1,          2,            /* alnum - word without underscore */\n  cbit_print,  cbit_cntrl,  0,            /* ascii */\n  cbit_space,  -1,          1,            /* blank - a GNU extension */\n  cbit_cntrl,  -1,          0,            /* cntrl */\n  cbit_digit,  -1,          0,            /* digit */\n  cbit_graph,  -1,          0,            /* graph */\n  cbit_print,  -1,          0,            /* print */\n  cbit_punct,  -1,          0,            /* punct */\n  cbit_space,  -1,          0,            /* space */\n  cbit_word,   -1,          0,            /* word - a Perl extension */\n  cbit_xdigit, -1,          0             /* xdigit */\n};\n\n#ifdef SUPPORT_UNICODE\n\n/* The POSIX class Unicode property substitutes that are used in UCP mode must\nbe in the order of the POSIX class names, defined above. */\n\nstatic int posix_substitutes[] = {\n  PT_GC, ucp_L,     /* alpha */\n  PT_PC, ucp_Ll,    /* lower */\n  PT_PC, ucp_Lu,    /* upper */\n  PT_ALNUM, 0,      /* alnum */\n  -1, 0,            /* ascii, treat as non-UCP */\n  -1, 1,            /* blank, treat as \\h */\n  PT_PC, ucp_Cc,    /* cntrl */\n  PT_PC, ucp_Nd,    /* digit */\n  PT_PXGRAPH, 0,    /* graph */\n  PT_PXPRINT, 0,    /* print */\n  PT_PXPUNCT, 0,    /* punct */\n  PT_PXSPACE, 0,    /* space */   /* Xps is POSIX space, but from 8.34 */\n  PT_WORD, 0,       /* word  */   /* Perl and POSIX space are the same */\n  PT_PXXDIGIT, 0    /* xdigit */  /* Perl has additional hex digits */\n};\n#endif  /* SUPPORT_UNICODE */\n\n/* Masks for checking option settings. When PCRE2_LITERAL is set, only a subset\nare allowed. */\n\n#define PUBLIC_LITERAL_COMPILE_OPTIONS \\\n  (PCRE2_ANCHORED|PCRE2_AUTO_CALLOUT|PCRE2_CASELESS|PCRE2_ENDANCHORED| \\\n   PCRE2_FIRSTLINE|PCRE2_LITERAL|PCRE2_MATCH_INVALID_UTF| \\\n   PCRE2_NO_START_OPTIMIZE|PCRE2_NO_UTF_CHECK|PCRE2_USE_OFFSET_LIMIT|PCRE2_UTF)\n\n#define PUBLIC_COMPILE_OPTIONS \\\n  (PUBLIC_LITERAL_COMPILE_OPTIONS| \\\n   PCRE2_ALLOW_EMPTY_CLASS|PCRE2_ALT_BSUX|PCRE2_ALT_CIRCUMFLEX| \\\n   PCRE2_ALT_VERBNAMES|PCRE2_DOLLAR_ENDONLY|PCRE2_DOTALL|PCRE2_DUPNAMES| \\\n   PCRE2_EXTENDED|PCRE2_EXTENDED_MORE|PCRE2_MATCH_UNSET_BACKREF| \\\n   PCRE2_MULTILINE|PCRE2_NEVER_BACKSLASH_C|PCRE2_NEVER_UCP| \\\n   PCRE2_NEVER_UTF|PCRE2_NO_AUTO_CAPTURE|PCRE2_NO_AUTO_POSSESS| \\\n   PCRE2_NO_DOTSTAR_ANCHOR|PCRE2_UCP|PCRE2_UNGREEDY|PCRE2_ALT_EXTENDED_CLASS)\n\n#define PUBLIC_LITERAL_COMPILE_EXTRA_OPTIONS \\\n   (PCRE2_EXTRA_MATCH_LINE|PCRE2_EXTRA_MATCH_WORD| \\\n    PCRE2_EXTRA_CASELESS_RESTRICT|PCRE2_EXTRA_TURKISH_CASING)\n\n#define PUBLIC_COMPILE_EXTRA_OPTIONS \\\n   (PUBLIC_LITERAL_COMPILE_EXTRA_OPTIONS| \\\n    PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES|PCRE2_EXTRA_BAD_ESCAPE_IS_LITERAL| \\\n    PCRE2_EXTRA_ESCAPED_CR_IS_LF|PCRE2_EXTRA_ALT_BSUX| \\\n    PCRE2_EXTRA_ALLOW_LOOKAROUND_BSK|PCRE2_EXTRA_ASCII_BSD| \\\n    PCRE2_EXTRA_ASCII_BSS|PCRE2_EXTRA_ASCII_BSW|PCRE2_EXTRA_ASCII_POSIX| \\\n    PCRE2_EXTRA_ASCII_DIGIT|PCRE2_EXTRA_PYTHON_OCTAL|PCRE2_EXTRA_NO_BS0| \\\n    PCRE2_EXTRA_NEVER_CALLOUT)\n\n/* This is a table of start-of-pattern options such as (*UTF) and settings such\nas (*LIMIT_MATCH=nnnn) and (*CRLF). For completeness and backward\ncompatibility, (*UTFn) is supported in the relevant libraries, but (*UTF) is\ngeneric and always supported. */\n\nenum { PSO_OPT,     /* Value is an option bit */\n       PSO_XOPT,    /* Value is an xoption bit */\n       PSO_FLG,     /* Value is a flag bit */\n       PSO_NL,      /* Value is a newline type */\n       PSO_BSR,     /* Value is a \\R type */\n       PSO_LIMH,    /* Read integer value for heap limit */\n       PSO_LIMM,    /* Read integer value for match limit */\n       PSO_LIMD,    /* Read integer value for depth limit */\n       PSO_OPTMZ    /* Value is an optimization bit */\n     };\n\ntypedef struct pso {\n  const char *name;\n  uint16_t length;\n  uint16_t type;\n  uint32_t value;\n} pso;\n\n/* NB: STRING_UTFn_RIGHTPAR contains the length as well */\n\nstatic const pso pso_list[] = {\n  { STRING_UTFn_RIGHTPAR,                  PSO_OPT, PCRE2_UTF },\n  { STRING_UTF_RIGHTPAR,                4, PSO_OPT, PCRE2_UTF },\n  { STRING_UCP_RIGHTPAR,                4, PSO_OPT, PCRE2_UCP },\n  { STRING_EC_RIGHTPAR,                 3, PSO_OPT, PCRE2_ALT_EXTENDED_CLASS }, //au\n  { STRING_NOTEMPTY_RIGHTPAR,           9, PSO_FLG, PCRE2_NOTEMPTY_SET },\n  { STRING_NOTEMPTY_ATSTART_RIGHTPAR,  17, PSO_FLG, PCRE2_NE_ATST_SET },\n  { STRING_NO_AUTO_POSSESS_RIGHTPAR,   16, PSO_OPTMZ, PCRE2_OPTIM_AUTO_POSSESS },\n  { STRING_NO_DOTSTAR_ANCHOR_RIGHTPAR, 18, PSO_OPTMZ, PCRE2_OPTIM_DOTSTAR_ANCHOR },\n  { STRING_NO_JIT_RIGHTPAR,             7, PSO_FLG, PCRE2_NOJIT },\n  { STRING_NO_START_OPT_RIGHTPAR,      13, PSO_OPTMZ, PCRE2_OPTIM_START_OPTIMIZE },\n  { STRING_CASELESS_RESTRICT_RIGHTPAR, 18, PSO_XOPT, PCRE2_EXTRA_CASELESS_RESTRICT },\n  { STRING_TURKISH_CASING_RIGHTPAR,    15, PSO_XOPT, PCRE2_EXTRA_TURKISH_CASING },\n  { STRING_LIMIT_HEAP_EQ,              11, PSO_LIMH, 0 },\n  { STRING_LIMIT_MATCH_EQ,             12, PSO_LIMM, 0 },\n  { STRING_LIMIT_DEPTH_EQ,             12, PSO_LIMD, 0 },\n  { STRING_LIMIT_RECURSION_EQ,         16, PSO_LIMD, 0 },\n  { STRING_CR_RIGHTPAR,                 3, PSO_NL,  PCRE2_NEWLINE_CR },\n  { STRING_LF_RIGHTPAR,                 3, PSO_NL,  PCRE2_NEWLINE_LF },\n  { STRING_CRLF_RIGHTPAR,               5, PSO_NL,  PCRE2_NEWLINE_CRLF },\n  { STRING_ANY_RIGHTPAR,                4, PSO_NL,  PCRE2_NEWLINE_ANY },\n  { STRING_NUL_RIGHTPAR,                4, PSO_NL,  PCRE2_NEWLINE_NUL },\n  { STRING_ANYCRLF_RIGHTPAR,            8, PSO_NL,  PCRE2_NEWLINE_ANYCRLF },\n  { STRING_BSR_ANYCRLF_RIGHTPAR,       12, PSO_BSR, PCRE2_BSR_ANYCRLF },\n  { STRING_BSR_UNICODE_RIGHTPAR,       12, PSO_BSR, PCRE2_BSR_UNICODE }\n};\n\n/* This table is used when converting repeating opcodes into possessified\nversions as a result of an explicit possessive quantifier such as ++. A zero\nvalue means there is no possessified version - in those cases the item in\nquestion must be wrapped in ONCE brackets. The table is truncated at OP_CALLOUT\nbecause all relevant opcodes are less than that. */\n\nstatic const uint8_t opcode_possessify[] = {\n  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   /* 0 - 15  */\n  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   /* 16 - 31 */\n\n  0,                       /* NOTI */\n  OP_POSSTAR, 0,           /* STAR, MINSTAR */\n  OP_POSPLUS, 0,           /* PLUS, MINPLUS */\n  OP_POSQUERY, 0,          /* QUERY, MINQUERY */\n  OP_POSUPTO, 0,           /* UPTO, MINUPTO */\n  0,                       /* EXACT */\n  0, 0, 0, 0,              /* POS{STAR,PLUS,QUERY,UPTO} */\n\n  OP_POSSTARI, 0,          /* STARI, MINSTARI */\n  OP_POSPLUSI, 0,          /* PLUSI, MINPLUSI */\n  OP_POSQUERYI, 0,         /* QUERYI, MINQUERYI */\n  OP_POSUPTOI, 0,          /* UPTOI, MINUPTOI */\n  0,                       /* EXACTI */\n  0, 0, 0, 0,              /* POS{STARI,PLUSI,QUERYI,UPTOI} */\n\n  OP_NOTPOSSTAR, 0,        /* NOTSTAR, NOTMINSTAR */\n  OP_NOTPOSPLUS, 0,        /* NOTPLUS, NOTMINPLUS */\n  OP_NOTPOSQUERY, 0,       /* NOTQUERY, NOTMINQUERY */\n  OP_NOTPOSUPTO, 0,        /* NOTUPTO, NOTMINUPTO */\n  0,                       /* NOTEXACT */\n  0, 0, 0, 0,              /* NOTPOS{STAR,PLUS,QUERY,UPTO} */\n\n  OP_NOTPOSSTARI, 0,       /* NOTSTARI, NOTMINSTARI */\n  OP_NOTPOSPLUSI, 0,       /* NOTPLUSI, NOTMINPLUSI */\n  OP_NOTPOSQUERYI, 0,      /* NOTQUERYI, NOTMINQUERYI */\n  OP_NOTPOSUPTOI, 0,       /* NOTUPTOI, NOTMINUPTOI */\n  0,                       /* NOTEXACTI */\n  0, 0, 0, 0,              /* NOTPOS{STARI,PLUSI,QUERYI,UPTOI} */\n\n  OP_TYPEPOSSTAR, 0,       /* TYPESTAR, TYPEMINSTAR */\n  OP_TYPEPOSPLUS, 0,       /* TYPEPLUS, TYPEMINPLUS */\n  OP_TYPEPOSQUERY, 0,      /* TYPEQUERY, TYPEMINQUERY */\n  OP_TYPEPOSUPTO, 0,       /* TYPEUPTO, TYPEMINUPTO */\n  0,                       /* TYPEEXACT */\n  0, 0, 0, 0,              /* TYPEPOS{STAR,PLUS,QUERY,UPTO} */\n\n  OP_CRPOSSTAR, 0,         /* CRSTAR, CRMINSTAR */\n  OP_CRPOSPLUS, 0,         /* CRPLUS, CRMINPLUS */\n  OP_CRPOSQUERY, 0,        /* CRQUERY, CRMINQUERY */\n  OP_CRPOSRANGE, 0,        /* CRRANGE, CRMINRANGE */\n  0, 0, 0, 0,              /* CRPOS{STAR,PLUS,QUERY,RANGE} */\n\n  0, 0, 0, 0,              /* CLASS, NCLASS, XCLASS, ECLASS */\n  0, 0,                    /* REF, REFI */\n  0, 0,                    /* DNREF, DNREFI */\n  0, 0,                    /* RECURSE, CALLOUT */\n};\n\n/* Compile-time check that the table has the correct size. */\nSTATIC_ASSERT(sizeof(opcode_possessify) == OP_CALLOUT+1, opcode_possessify);\n\n\n#ifdef DEBUG_SHOW_PARSED\n/*************************************************\n*     Show the parsed pattern for debugging      *\n*************************************************/\n\n/* For debugging the pre-scan, this code, which outputs the parsed data vector,\ncan be enabled. */\n\nstatic void show_parsed(compile_block *cb)\n{\nuint32_t *pptr = cb->parsed_pattern;\n\nfor (;;)\n  {\n  int max, min;\n  PCRE2_SIZE offset;\n  uint32_t i;\n  uint32_t length;\n  uint32_t meta_arg = META_DATA(*pptr);\n\n  fprintf(stderr, \"+++ %02d %.8x \", (int)(pptr - cb->parsed_pattern), *pptr);\n\n  if (*pptr < META_END)\n    {\n    if (*pptr > 32 && *pptr < 128) fprintf(stderr, \"%c\", *pptr);\n    pptr++;\n    }\n\n  else switch (META_CODE(*pptr++))\n    {\n    default:\n    fprintf(stderr, \"**** OOPS - unknown META value - giving up ****\\n\");\n    return;\n\n    case META_END:\n    fprintf(stderr, \"META_END\\n\");\n    return;\n\n    case META_CAPTURE:\n    fprintf(stderr, \"META_CAPTURE %d\", meta_arg);\n    break;\n\n    case META_RECURSE:\n    GETOFFSET(offset, pptr);\n    fprintf(stderr, \"META_RECURSE %d %zd\", meta_arg, offset);\n    break;\n\n    case META_BACKREF:\n    if (meta_arg < 10)\n      offset = cb->small_ref_offset[meta_arg];\n    else\n      GETOFFSET(offset, pptr);\n    fprintf(stderr, \"META_BACKREF %d %zd\", meta_arg, offset);\n    break;\n\n    case META_ESCAPE:\n    if (meta_arg == ESC_P || meta_arg == ESC_p)\n      {\n      uint32_t ptype = *pptr >> 16;\n      uint32_t pvalue = *pptr++ & 0xffff;\n      fprintf(stderr, \"META \\\\%c %d %d\", (meta_arg == ESC_P)? CHAR_P:CHAR_p,\n        ptype, pvalue);\n      }\n    else\n      {\n      uint32_t cc;\n      /* There's just one escape we might have here that isn't negated in the\n      escapes table. */\n      if (meta_arg == ESC_g) cc = CHAR_g;\n      else for (cc = ESCAPES_FIRST; cc <= ESCAPES_LAST; cc++)\n        {\n        if (meta_arg == (uint32_t)(-escapes[cc - ESCAPES_FIRST])) break;\n        }\n      if (cc > ESCAPES_LAST) cc = CHAR_QUESTION_MARK;\n      fprintf(stderr, \"META \\\\%c\", cc);\n      }\n    break;\n\n    case META_MINMAX:\n    min = *pptr++;\n    max = *pptr++;\n    if (max != REPEAT_UNLIMITED)\n      fprintf(stderr, \"META {%d,%d}\", min, max);\n    else\n      fprintf(stderr, \"META {%d,}\", min);\n    break;\n\n    case META_MINMAX_QUERY:\n    min = *pptr++;\n    max = *pptr++;\n    if (max != REPEAT_UNLIMITED)\n      fprintf(stderr, \"META {%d,%d}?\", min, max);\n    else\n      fprintf(stderr, \"META {%d,}?\", min);\n    break;\n\n    case META_MINMAX_PLUS:\n    min = *pptr++;\n    max = *pptr++;\n    if (max != REPEAT_UNLIMITED)\n      fprintf(stderr, \"META {%d,%d}+\", min, max);\n    else\n      fprintf(stderr, \"META {%d,}+\", min);\n    break;\n\n    case META_BIGVALUE: fprintf(stderr, \"META_BIGVALUE %.8x\", *pptr++); break;\n    case META_CIRCUMFLEX: fprintf(stderr, \"META_CIRCUMFLEX\"); break;\n    case META_COND_ASSERT: fprintf(stderr, \"META_COND_ASSERT\"); break;\n    case META_DOLLAR: fprintf(stderr, \"META_DOLLAR\"); break;\n    case META_DOT: fprintf(stderr, \"META_DOT\"); break;\n    case META_ASTERISK: fprintf(stderr, \"META *\"); break;\n    case META_ASTERISK_QUERY: fprintf(stderr, \"META *?\"); break;\n    case META_ASTERISK_PLUS: fprintf(stderr, \"META *+\"); break;\n    case META_PLUS: fprintf(stderr, \"META +\"); break;\n    case META_PLUS_QUERY: fprintf(stderr, \"META +?\"); break;\n    case META_PLUS_PLUS: fprintf(stderr, \"META ++\"); break;\n    case META_QUERY: fprintf(stderr, \"META ?\"); break;\n    case META_QUERY_QUERY: fprintf(stderr, \"META ??\"); break;\n    case META_QUERY_PLUS: fprintf(stderr, \"META ?+\"); break;\n\n    case META_ATOMIC: fprintf(stderr, \"META (?>\"); break;\n    case META_NOCAPTURE: fprintf(stderr, \"META (?:\"); break;\n    case META_LOOKAHEAD: fprintf(stderr, \"META (?=\"); break;\n    case META_LOOKAHEADNOT: fprintf(stderr, \"META (?!\"); break;\n    case META_LOOKAHEAD_NA: fprintf(stderr, \"META (*napla:\"); break;\n    case META_SCRIPT_RUN: fprintf(stderr, \"META (*sr:\"); break;\n    case META_KET: fprintf(stderr, \"META )\"); break;\n    case META_ALT: fprintf(stderr, \"META | %d\", meta_arg); break;\n\n    case META_CLASS: fprintf(stderr, \"META [\"); break;\n    case META_CLASS_NOT: fprintf(stderr, \"META [^\"); break;\n    case META_CLASS_END: fprintf(stderr, \"META ]\"); break;\n    case META_CLASS_EMPTY: fprintf(stderr, \"META []\"); break;\n    case META_CLASS_EMPTY_NOT: fprintf(stderr, \"META [^]\"); break;\n\n    case META_RANGE_LITERAL: fprintf(stderr, \"META - (literal)\"); break;\n    case META_RANGE_ESCAPED: fprintf(stderr, \"META - (escaped)\"); break;\n\n    case META_POSIX: fprintf(stderr, \"META_POSIX %d\", *pptr++); break;\n    case META_POSIX_NEG: fprintf(stderr, \"META_POSIX_NEG %d\", *pptr++); break;\n\n    case META_ACCEPT: fprintf(stderr, \"META (*ACCEPT)\"); break;\n    case META_FAIL: fprintf(stderr, \"META (*FAIL)\"); break;\n    case META_COMMIT: fprintf(stderr, \"META (*COMMIT)\"); break;\n    case META_PRUNE: fprintf(stderr, \"META (*PRUNE)\"); break;\n    case META_SKIP: fprintf(stderr, \"META (*SKIP)\"); break;\n    case META_THEN: fprintf(stderr, \"META (*THEN)\"); break;\n\n    case META_OPTIONS:\n    fprintf(stderr, \"META_OPTIONS 0x%08x 0x%08x\", pptr[0], pptr[1]);\n    pptr += 2;\n    break;\n\n    case META_LOOKBEHIND:\n    fprintf(stderr, \"META (?<= %d %d\", meta_arg, *pptr);\n    pptr += 2;\n    break;\n\n    case META_LOOKBEHIND_NA:\n    fprintf(stderr, \"META (*naplb: %d %d\", meta_arg, *pptr);\n    pptr += 2;\n    break;\n\n    case META_LOOKBEHINDNOT:\n    fprintf(stderr, \"META (?<! %d %d\", meta_arg, *pptr);\n    pptr += 2;\n    break;\n\n    case META_CALLOUT_NUMBER:\n    fprintf(stderr, \"META (?C%d) next=%d/%d\", pptr[2], pptr[0],\n       pptr[1]);\n    pptr += 3;\n    break;\n\n    case META_CALLOUT_STRING:\n      {\n      uint32_t patoffset = *pptr++;    /* Offset of next pattern item */\n      uint32_t patlength = *pptr++;    /* Length of next pattern item */\n      fprintf(stderr, \"META (?Cstring) length=%d offset=\", *pptr++);\n      GETOFFSET(offset, pptr);\n      fprintf(stderr, \"%zd next=%d/%d\", offset, patoffset, patlength);\n      }\n    break;\n\n    case META_RECURSE_BYNAME:\n    fprintf(stderr, \"META (?(&name) length=%d offset=\", *pptr++);\n    GETOFFSET(offset, pptr);\n    fprintf(stderr, \"%zd\", offset);\n    break;\n\n    case META_BACKREF_BYNAME:\n    fprintf(stderr, \"META_BACKREF_BYNAME length=%d offset=\", *pptr++);\n    GETOFFSET(offset, pptr);\n    fprintf(stderr, \"%zd\", offset);\n    break;\n\n    case META_COND_NUMBER:\n    fprintf(stderr, \"META_COND_NUMBER %d offset=\", pptr[SIZEOFFSET]);\n    GETOFFSET(offset, pptr);\n    fprintf(stderr, \"%zd\", offset);\n    pptr++;\n    break;\n\n    case META_COND_DEFINE:\n    fprintf(stderr, \"META (?(DEFINE) offset=\");\n    GETOFFSET(offset, pptr);\n    fprintf(stderr, \"%zd\", offset);\n    break;\n\n    case META_COND_VERSION:\n    fprintf(stderr, \"META (?(VERSION%s\", (*pptr++ == 0)? \"=\" : \">=\");\n    fprintf(stderr, \"%d.\", *pptr++);\n    fprintf(stderr, \"%d)\", *pptr++);\n    break;\n\n    case META_COND_NAME:\n    fprintf(stderr, \"META (?(<name>) length=%d offset=\", *pptr++);\n    GETOFFSET(offset, pptr);\n    fprintf(stderr, \"%zd\", offset);\n    break;\n\n    case META_COND_RNAME:\n    fprintf(stderr, \"META (?(R&name) length=%d offset=\", *pptr++);\n    GETOFFSET(offset, pptr);\n    fprintf(stderr, \"%zd\", offset);\n    break;\n\n    /* This is kept as a name, because it might be. */\n\n    case META_COND_RNUMBER:\n    fprintf(stderr, \"META (?(Rnumber) length=%d offset=\", *pptr++);\n    GETOFFSET(offset, pptr);\n    fprintf(stderr, \"%zd\", offset);\n    break;\n\n    case META_OFFSET:\n    fprintf(stderr, \"META_OFFSET offset=\");\n    GETOFFSET(offset, pptr);\n    fprintf(stderr, \"%zd\", offset);\n    break;\n\n    case META_SCS:\n    fprintf(stderr, \"META (*scan_substring:\");\n    break;\n\n    case META_SCS_NAME:\n    fprintf(stderr, \"META_SCS_NAME length=%d relative_offset=%d\", *pptr++, (int)meta_arg);\n    break;\n\n    case META_SCS_NUMBER:\n    fprintf(stderr, \"META_SCS_NUMBER %d relative_offset=%d\", *pptr++, (int)meta_arg);\n    break;\n\n    case META_MARK:\n    fprintf(stderr, \"META (*MARK:\");\n    goto SHOWARG;\n\n    case META_COMMIT_ARG:\n    fprintf(stderr, \"META (*COMMIT:\");\n    goto SHOWARG;\n\n    case META_PRUNE_ARG:\n    fprintf(stderr, \"META (*PRUNE:\");\n    goto SHOWARG;\n\n    case META_SKIP_ARG:\n    fprintf(stderr, \"META (*SKIP:\");\n    goto SHOWARG;\n\n    case META_THEN_ARG:\n    fprintf(stderr, \"META (*THEN:\");\n    SHOWARG:\n    length = *pptr++;\n    for (i = 0; i < length; i++)\n      {\n      uint32_t cc = *pptr++;\n      if (cc > 32 && cc < 128) fprintf(stderr, \"%c\", cc);\n        else fprintf(stderr, \"\\\\x{%x}\", cc);\n      }\n    fprintf(stderr, \") length=%u\", length);\n    break;\n\n    case META_ECLASS_AND: fprintf(stderr, \"META_ECLASS_AND\"); break;\n    case META_ECLASS_OR: fprintf(stderr, \"META_ECLASS_OR\"); break;\n    case META_ECLASS_SUB: fprintf(stderr, \"META_ECLASS_SUB\"); break;\n    case META_ECLASS_XOR: fprintf(stderr, \"META_ECLASS_XOR\"); break;\n    case META_ECLASS_NOT: fprintf(stderr, \"META_ECLASS_NOT\"); break;\n    }\n  fprintf(stderr, \"\\n\");\n  }\nreturn;\n}\n#endif  /* DEBUG_SHOW_PARSED */\n\n\n\n/*************************************************\n*               Copy compiled code               *\n*************************************************/\n\n/* Compiled JIT code cannot be copied, so the new compiled block has no\nassociated JIT data. */\n\nPCRE2_EXP_DEFN pcre2_code * PCRE2_CALL_CONVENTION\npcre2_code_copy(const pcre2_code *code)\n{\nPCRE2_SIZE *ref_count;\npcre2_code *newcode;\n\nif (code == NULL) return NULL;\nnewcode = code->memctl.malloc(code->blocksize, code->memctl.memory_data);\nif (newcode == NULL) return NULL;\nmemcpy(newcode, code, code->blocksize);\nnewcode->executable_jit = NULL;\n\n/* If the code is one that has been deserialized, increment the reference count\nin the decoded tables. */\n\nif ((code->flags & PCRE2_DEREF_TABLES) != 0)\n  {\n  ref_count = (PCRE2_SIZE *)(code->tables + TABLES_LENGTH);\n  (*ref_count)++;\n  }\n\nreturn newcode;\n}\n\n\n\n/*************************************************\n*     Copy compiled code and character tables    *\n*************************************************/\n\n/* Compiled JIT code cannot be copied, so the new compiled block has no\nassociated JIT data. This version of code_copy also makes a separate copy of\nthe character tables. */\n\nPCRE2_EXP_DEFN pcre2_code * PCRE2_CALL_CONVENTION\npcre2_code_copy_with_tables(const pcre2_code *code)\n{\nPCRE2_SIZE* ref_count;\npcre2_code *newcode;\nuint8_t *newtables;\n\nif (code == NULL) return NULL;\nnewcode = code->memctl.malloc(code->blocksize, code->memctl.memory_data);\nif (newcode == NULL) return NULL;\nmemcpy(newcode, code, code->blocksize);\nnewcode->executable_jit = NULL;\n\nnewtables = code->memctl.malloc(TABLES_LENGTH + sizeof(PCRE2_SIZE),\n  code->memctl.memory_data);\nif (newtables == NULL)\n  {\n  code->memctl.free((void *)newcode, code->memctl.memory_data);\n  return NULL;\n  }\nmemcpy(newtables, code->tables, TABLES_LENGTH);\nref_count = (PCRE2_SIZE *)(newtables + TABLES_LENGTH);\n*ref_count = 1;\n\nnewcode->tables = newtables;\nnewcode->flags |= PCRE2_DEREF_TABLES;\nreturn newcode;\n}\n\n\n\n/*************************************************\n*               Free compiled code               *\n*************************************************/\n\nPCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION\npcre2_code_free(pcre2_code *code)\n{\nPCRE2_SIZE* ref_count;\n\nif (code != NULL)\n  {\n#ifdef SUPPORT_JIT\n  if (code->executable_jit != NULL)\n    PRIV(jit_free)(code->executable_jit, &code->memctl);\n#endif\n\n  if ((code->flags & PCRE2_DEREF_TABLES) != 0)\n    {\n    /* Decoded tables belong to the codes after deserialization, and they must\n    be freed when there are no more references to them. The *ref_count should\n    always be > 0. */\n\n    ref_count = (PCRE2_SIZE *)(code->tables + TABLES_LENGTH);\n    if (*ref_count > 0)\n      {\n      (*ref_count)--;\n      if (*ref_count == 0)\n        code->memctl.free((void *)code->tables, code->memctl.memory_data);\n      }\n    }\n\n  code->memctl.free(code, code->memctl.memory_data);\n  }\n}\n\n\n\n/*************************************************\n*         Read a number, possibly signed         *\n*************************************************/\n\n/* This function is used to read numbers in the pattern. The initial pointer\nmust be at the sign or first digit of the number. When relative values\n(introduced by + or -) are allowed, they are relative group numbers, and the\nresult must be greater than zero.\n\nArguments:\n  ptrptr      points to the character pointer variable\n  ptrend      points to the end of the input string\n  allow_sign  if < 0, sign not allowed; if >= 0, sign is relative to this\n  max_value   the largest number allowed;\n              you must not pass a value for max_value larger than\n              INT_MAX/10 - 1 because this function relies on max_value to\n              avoid integer overflow\n  max_error   the error to give for an over-large number\n  intptr      where to put the result\n  errcodeptr  where to put an error code\n\nReturns:      TRUE  - a number was read\n              FALSE - errorcode == 0 => no number was found\n                      errorcode != 0 => an error occurred\n*/\n\nstatic BOOL\nread_number(PCRE2_SPTR *ptrptr, PCRE2_SPTR ptrend, int32_t allow_sign,\n  uint32_t max_value, uint32_t max_error, int *intptr, int *errorcodeptr)\n{\nint sign = 0;\nuint32_t n = 0;\nPCRE2_SPTR ptr = *ptrptr;\nBOOL yield = FALSE;\n\nPCRE2_ASSERT(max_value <= INT_MAX/10 - 1);\n\n*errorcodeptr = 0;\n\nif (allow_sign >= 0 && ptr < ptrend)\n  {\n  if (*ptr == CHAR_PLUS)\n    {\n    sign = +1;\n    max_value -= allow_sign;\n    ptr++;\n    }\n  else if (*ptr == CHAR_MINUS)\n    {\n    sign = -1;\n    ptr++;\n    }\n  }\n\nif (ptr >= ptrend || !IS_DIGIT(*ptr)) return FALSE;\nwhile (ptr < ptrend && IS_DIGIT(*ptr))\n  {\n  n = n * 10 + (*ptr++ - CHAR_0);\n  if (n > max_value)\n    {\n    *errorcodeptr = max_error;\n    while (ptr < ptrend && IS_DIGIT(*ptr)) ptr++;\n    goto EXIT;\n    }\n  }\n\nif (allow_sign >= 0 && sign != 0)\n  {\n  if (n == 0)\n    {\n    *errorcodeptr = ERR26;  /* +0 and -0 are not allowed */\n    goto EXIT;\n    }\n\n  if (sign > 0) n += allow_sign;\n  else if (n > (uint32_t)allow_sign)\n    {\n    *errorcodeptr = ERR15;  /* Non-existent subpattern */\n    goto EXIT;\n    }\n  else n = allow_sign + 1 - n;\n  }\n\nyield = TRUE;\n\nEXIT:\n*intptr = n;\n*ptrptr = ptr;\nreturn yield;\n}\n\n\n\n/*************************************************\n*         Read repeat counts                     *\n*************************************************/\n\n/* Read an item of the form {n,m} and return the values when non-NULL pointers\nare supplied. Repeat counts must be less than 65536 (MAX_REPEAT_COUNT); a\nlarger value is used for \"unlimited\". We have to use signed arguments for\nread_number() because it is capable of returning a signed value. As of Perl\n5.34.0 either n or m may be absent, but not both. Perl also allows spaces and\ntabs after { and before } and between the numbers and the comma, so we do too.\n\nArguments:\n  ptrptr         points to pointer to character after '{'\n  ptrend         pointer to end of input\n  minp           if not NULL, pointer to int for min\n  maxp           if not NULL, pointer to int for max\n  errorcodeptr   points to error code variable\n\nReturns:         FALSE if not a repeat quantifier, errorcode set zero\n                 FALSE on error, with errorcode set non-zero\n                 TRUE on success, with pointer updated to point after '}'\n*/\n\nstatic BOOL\nread_repeat_counts(PCRE2_SPTR *ptrptr, PCRE2_SPTR ptrend, uint32_t *minp,\n  uint32_t *maxp, int *errorcodeptr)\n{\nPCRE2_SPTR p = *ptrptr;\nPCRE2_SPTR pp;\nBOOL yield = FALSE;\nBOOL had_minimum = FALSE;\nint32_t min = 0;\nint32_t max = REPEAT_UNLIMITED; /* This value is larger than MAX_REPEAT_COUNT */\n\n*errorcodeptr = 0;\nwhile (p < ptrend && (*p == CHAR_SPACE || *p == CHAR_HT)) p++;\n\n/* Check the syntax before interpreting. Otherwise, a non-quantifier sequence\nsuch as \"X{123456ABC\" would incorrectly give a \"number too big in quantifier\"\nerror. */\n\npp = p;\nif (pp < ptrend && IS_DIGIT(*pp))\n  {\n  had_minimum = TRUE;\n  while (++pp < ptrend && IS_DIGIT(*pp)) {}\n  }\n\nwhile (pp < ptrend && (*pp == CHAR_SPACE || *pp == CHAR_HT)) pp++;\nif (pp >= ptrend) return FALSE;\n\nif (*pp == CHAR_RIGHT_CURLY_BRACKET)\n  {\n  if (!had_minimum) return FALSE;\n  }\nelse\n  {\n  if (*pp++ != CHAR_COMMA) return FALSE;\n  while (pp < ptrend && (*pp == CHAR_SPACE || *pp == CHAR_HT)) pp++;\n  if (pp >= ptrend) return FALSE;\n  if (IS_DIGIT(*pp))\n    {\n    while (++pp < ptrend && IS_DIGIT(*pp)) {}\n    }\n  else if (!had_minimum) return FALSE;\n  while (pp < ptrend && (*pp == CHAR_SPACE || *pp == CHAR_HT)) pp++;\n  if (pp >= ptrend || *pp != CHAR_RIGHT_CURLY_BRACKET) return FALSE;\n  }\n\n/* Now process the quantifier for real. We know it must be {n} or {n,} or {,m}\nor {n,m}. The only error that read_number() can return is for a number that is\ntoo big. If *errorcodeptr is returned as zero it means no number was found. */\n\n/* Deal with {,m} or n too big. If we successfully read m there is no need to\ncheck m >= n because n defaults to zero. */\n\nif (!read_number(&p, ptrend, -1, MAX_REPEAT_COUNT, ERR5, &min, errorcodeptr))\n  {\n  if (*errorcodeptr != 0) goto EXIT;    /* n too big */\n  p++;  /* Skip comma and subsequent spaces */\n  while (p < ptrend && (*p == CHAR_SPACE || *p == CHAR_HT)) p++;\n  if (!read_number(&p, ptrend, -1, MAX_REPEAT_COUNT, ERR5, &max, errorcodeptr))\n    {\n    if (*errorcodeptr != 0) goto EXIT;  /* m too big */\n    }\n  }\n\n/* Have read one number. Deal with {n} or {n,} or {n,m} */\n\nelse\n  {\n  while (p < ptrend && (*p == CHAR_SPACE || *p == CHAR_HT)) p++;\n  if (*p == CHAR_RIGHT_CURLY_BRACKET)\n    {\n    max = min;\n    }\n  else   /* Handle {n,} or {n,m} */\n    {\n    p++;    /* Skip comma and subsequent spaces */\n    while (p < ptrend && (*p == CHAR_SPACE || *p == CHAR_HT)) p++;\n    if (!read_number(&p, ptrend, -1, MAX_REPEAT_COUNT, ERR5, &max, errorcodeptr))\n      {\n      if (*errorcodeptr != 0) goto EXIT;   /* m too big */\n      }\n\n    if (max < min)\n      {\n      *errorcodeptr = ERR4;\n      goto EXIT;\n      }\n    }\n  }\n\n/* Valid quantifier exists */\n\nwhile (p < ptrend && (*p == CHAR_SPACE || *p == CHAR_HT)) p++;\np++;\nyield = TRUE;\nif (minp != NULL) *minp = (uint32_t)min;\nif (maxp != NULL) *maxp = (uint32_t)max;\n\n/* Update the pattern pointer */\n\nEXIT:\n*ptrptr = p;\nreturn yield;\n}\n\n\n\n/*************************************************\n*            Handle escapes                      *\n*************************************************/\n\n/* This function is called when a \\ has been encountered. It either returns a\npositive value for a simple escape such as \\d, or 0 for a data character, which\nis placed in chptr. A backreference to group n is returned as -(n+1). On\nentry, ptr is pointing at the character after \\. On exit, it points after the\nfinal code unit of the escape sequence.\n\nThis function is also called from pcre2_substitute() to handle escape sequences\nin replacement strings. In this case, the cb argument is NULL, and in the case\nof escapes that have further processing, only sequences that define a data\ncharacter are recognised. The options argument is the final value of the\ncompiled pattern's options.\n\nArguments:\n  ptrptr         points to the input position pointer\n  ptrend         points to the end of the input\n  chptr          points to a returned data character\n  errorcodeptr   points to the errorcode variable (containing zero)\n  options        the current options bits\n  xoptions       the current extra options bits\n  bracount       the number of capturing parentheses encountered so far\n  isclass        TRUE if in a character class\n  cb             compile data block or NULL when called from pcre2_substitute()\n\nReturns:         zero => a data character\n                 positive => a special escape sequence\n                 negative => a numerical back reference\n                 on error, errorcodeptr is set non-zero\n*/\n\nint\nPRIV(check_escape)(PCRE2_SPTR *ptrptr, PCRE2_SPTR ptrend, uint32_t *chptr,\n  int *errorcodeptr, uint32_t options, uint32_t xoptions, uint32_t bracount,\n  BOOL isclass, compile_block *cb)\n{\nBOOL utf = (options & PCRE2_UTF) != 0;\nBOOL alt_bsux =\n  ((options & PCRE2_ALT_BSUX) | (xoptions & PCRE2_EXTRA_ALT_BSUX)) != 0;\nPCRE2_SPTR ptr = *ptrptr;\nuint32_t c, cc;\nint escape = 0;\nint i;\n\n/* If backslash is at the end of the string, it's an error. */\n\nif (ptr >= ptrend)\n  {\n  *errorcodeptr = ERR1;\n  return 0;\n  }\n\nGETCHARINCTEST(c, ptr);         /* Get character value, increment pointer */\n*errorcodeptr = 0;              /* Be optimistic */\n\n/* Non-alphanumerics are literals, so we just leave the value in c. An initial\nvalue test saves a memory lookup for code points outside the alphanumeric\nrange. */\n\nif (c < ESCAPES_FIRST || c > ESCAPES_LAST) {}  /* Definitely literal */\n\n/* Otherwise, do a table lookup. Non-zero values need little processing here. A\npositive value is a literal value for something like \\n. A negative value is\nthe negation of one of the ESC_ macros that is passed back for handling by the\ncalling function. Some extra checking is needed for \\N because only \\N{U+dddd}\nis supported. If the value is zero, further processing is handled below. */\n\nelse if ((i = escapes[c - ESCAPES_FIRST]) != 0)\n  {\n  if (i > 0)\n    {\n    c = (uint32_t)i;\n    if (c == CHAR_CR && (xoptions & PCRE2_EXTRA_ESCAPED_CR_IS_LF) != 0)\n      c = CHAR_LF;\n    }\n  else  /* Negative table entry */\n    {\n    escape = -i;                    /* Else return a special escape */\n    if (cb != NULL && (escape == ESC_P || escape == ESC_p || escape == ESC_X))\n      cb->external_flags |= PCRE2_HASBKPORX;   /* Note \\P, \\p, or \\X */\n\n    /* Perl supports \\N{name} for character names and \\N{U+dddd} for numerical\n    Unicode code points, as well as plain \\N for \"not newline\". PCRE does not\n    support \\N{name}. However, it does support quantification such as \\N{2,3},\n    so if \\N{ is not followed by U+dddd we check for a quantifier. */\n\n    if (escape == ESC_N && ptr < ptrend && *ptr == CHAR_LEFT_CURLY_BRACKET)\n      {\n      PCRE2_SPTR p = ptr + 1;\n\n      /* Perl ignores spaces and tabs after { */\n\n      while (p < ptrend && (*p == CHAR_SPACE || *p == CHAR_HT)) p++;\n\n      /* \\N{U+ can be handled by the \\x{ code. However, this construction is\n      not valid in EBCDIC environments because it specifies a Unicode\n      character, not a codepoint in the local code. For example \\N{U+0041}\n      must be \"A\" in all environments. Also, in Perl, \\N{U+ forces Unicode\n      casing semantics for the entire pattern, so allow it only in UTF (i.e.\n      Unicode) mode. */\n\n      if (ptrend - p > 1 && *p == CHAR_U && p[1] == CHAR_PLUS)\n        {\n#ifndef EBCDIC\n        if (utf)\n          {\n          ptr = p + 2;\n          escape = 0;   /* Not a fancy escape after all */\n          goto COME_FROM_NU;\n          }\n#endif\n        *errorcodeptr = ERR93;\n        }\n\n      /* Give an error in contexts where quantifiers are not allowed\n      (character classes; substitution strings). */\n\n      else if (isclass || cb == NULL)\n        {\n        *errorcodeptr = ERR37;\n        }\n\n      /* Give an error if what follows is not a quantifier, but don't override\n      an error set by the quantifier reader (e.g. number overflow). */\n\n      else\n        {\n        if (!read_repeat_counts(&p, ptrend, NULL, NULL, errorcodeptr) &&\n             *errorcodeptr == 0)\n          *errorcodeptr = ERR37;\n        }\n      }\n    }\n  }\n\n/* Escapes that need further processing, including those that are unknown, have\na zero entry in the lookup table. When called from pcre2_substitute(), only \\c,\n\\o, and \\x are recognized (\\u and \\U can never appear as they are used for case\nforcing). */\n\nelse\n  {\n  int s;\n  PCRE2_SPTR oldptr;\n  BOOL overflow;\n\n  /* Filter calls from pcre2_substitute(). */\n\n  if (cb == NULL)\n    {\n    if (c < CHAR_0 ||\n       (c > CHAR_9 && (c != CHAR_c && c != CHAR_o && c != CHAR_x && c != CHAR_g)))\n      {\n      *errorcodeptr = ERR3;\n      return 0;\n      }\n    alt_bsux = FALSE;   /* Do not modify \\x handling */\n    }\n\n  switch (c)\n    {\n    /* A number of Perl escapes are not handled by PCRE. We give an explicit\n    error. */\n\n    case CHAR_F:\n    case CHAR_l:\n    case CHAR_L:\n    *errorcodeptr = ERR37;\n    break;\n\n    /* \\u is unrecognized when neither PCRE2_ALT_BSUX nor PCRE2_EXTRA_ALT_BSUX\n    is set. Otherwise, \\u must be followed by exactly four hex digits or, if\n    PCRE2_EXTRA_ALT_BSUX is set, by any number of hex digits in braces.\n    Otherwise it is a lowercase u letter. This gives some compatibility with\n    ECMAScript (aka JavaScript). Unlike other braced items, white space is NOT\n    allowed. When \\u{ is not followed by hex digits, a special return is given\n    because otherwise \\u{ 12} (for example) would be treated as u{12}. */\n\n    case CHAR_u:\n    if (!alt_bsux) *errorcodeptr = ERR37; else\n      {\n      uint32_t xc;\n\n      if (ptr >= ptrend) break;\n      if (*ptr == CHAR_LEFT_CURLY_BRACKET &&\n          (xoptions & PCRE2_EXTRA_ALT_BSUX) != 0)\n        {\n        PCRE2_SPTR hptr = ptr + 1;\n\n        cc = 0;\n        while (hptr < ptrend && (xc = XDIGIT(*hptr)) != 0xff)\n          {\n          if ((cc & 0xf0000000) != 0)  /* Test for 32-bit overflow */\n            {\n            *errorcodeptr = ERR77;\n            ptr = hptr;   /* Show where */\n            break;        /* *hptr != } will cause another break below */\n            }\n          cc = (cc << 4) | xc;\n          hptr++;\n          }\n\n        if (hptr == ptr + 1 ||   /* No hex digits */\n            hptr >= ptrend ||    /* Hit end of input */\n            *hptr != CHAR_RIGHT_CURLY_BRACKET)  /* No } terminator */\n          {\n          if (isclass) break; /* In a class, just treat as '\\u' literal */\n          escape = ESC_ub;    /* Special return */\n          ptr++;              /* Skip { */\n          break;              /* Hex escape not recognized */\n          }\n\n        c = cc;          /* Accept the code point */\n        ptr = hptr + 1;\n        }\n\n      else  /* Must be exactly 4 hex digits */\n        {\n        if (ptrend - ptr < 4) break;               /* Less than 4 chars */\n        if ((cc = XDIGIT(ptr[0])) == 0xff) break;  /* Not a hex digit */\n        if ((xc = XDIGIT(ptr[1])) == 0xff) break;  /* Not a hex digit */\n        cc = (cc << 4) | xc;\n        if ((xc = XDIGIT(ptr[2])) == 0xff) break;  /* Not a hex digit */\n        cc = (cc << 4) | xc;\n        if ((xc = XDIGIT(ptr[3])) == 0xff) break;  /* Not a hex digit */\n        c = (cc << 4) | xc;\n        ptr += 4;\n        }\n\n      if (utf)\n        {\n        if (c > 0x10ffffU) *errorcodeptr = ERR77;\n        else\n          if (c >= 0xd800 && c <= 0xdfff &&\n              (xoptions & PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES) == 0)\n                *errorcodeptr = ERR73;\n        }\n      else if (c > MAX_NON_UTF_CHAR) *errorcodeptr = ERR77;\n      }\n    break;\n\n    /* \\U is unrecognized unless PCRE2_ALT_BSUX or PCRE2_EXTRA_ALT_BSUX is set,\n    in which case it is an upper case letter. */\n\n    case CHAR_U:\n    if (!alt_bsux) *errorcodeptr = ERR37;\n    break;\n\n    /* In a character class, \\g is just a literal \"g\". Outside a character\n    class, \\g must be followed by one of a number of specific things:\n\n    (1) A number, either plain or braced. If positive, it is an absolute\n    backreference. If negative, it is a relative backreference. This is a Perl\n    5.10 feature.\n\n    (2) Perl 5.10 also supports \\g{name} as a reference to a named group. This\n    is part of Perl's movement towards a unified syntax for back references. As\n    this is synonymous with \\k{name}, we fudge it up by pretending it really\n    was \\k{name}.\n\n    (3) For Oniguruma compatibility we also support \\g followed by a name or a\n    number either in angle brackets or in single quotes. However, these are\n    (possibly recursive) subroutine calls, _not_ backreferences. We return\n    the ESC_g code.\n\n    Summary: Return a negative number for a numerical back reference (offset\n    by 1), ESC_k for a named back reference, and ESC_g for a named or\n    numbered subroutine call.\n\n    The above describes the \\g behaviour inside patterns. Inside replacement\n    strings (pcre2_substitute) we support only \\g<nameornum> for Python\n    compatibility. Return ESG_g for the named case, and -(num+1) for the\n    numbered case.\n    */\n\n    case CHAR_g:\n    if (isclass) break;\n\n    if (ptr >= ptrend)\n      {\n      *errorcodeptr = ERR57;\n      break;\n      }\n\n    if (cb == NULL)\n      {\n      PCRE2_SPTR p;\n      /* Substitution strings */\n      if (*ptr != CHAR_LESS_THAN_SIGN)\n        {\n        *errorcodeptr = ERR57;\n        break;\n        }\n\n      p = ptr + 1;\n\n      if (!read_number(&p, ptrend, -1, MAX_GROUP_NUMBER, ERR61, &s,\n          errorcodeptr))\n        {\n        if (*errorcodeptr == 0) escape = ESC_g;  /* No number found */\n        break;\n        }\n\n      if (p >= ptrend || *p != CHAR_GREATER_THAN_SIGN)\n        {\n        /* not advancing ptr; report error at the \\g character */\n        *errorcodeptr = ERR57;\n        break;\n        }\n\n      /* This is the reason that back references are returned as -(s+1) rather\n      than just -s. In a pattern, \\0 is not a back reference, but \\g<0> is\n      valid in a substitution string, so this must be representable. */\n      ptr = p + 1;\n      escape = -(s+1);\n      break;\n      }\n\n    if (*ptr == CHAR_LESS_THAN_SIGN || *ptr == CHAR_APOSTROPHE)\n      {\n      escape = ESC_g;\n      break;\n      }\n\n    /* If there is a brace delimiter, try to read a numerical reference. If\n    there isn't one, assume we have a name and treat it as \\k. */\n\n    if (*ptr == CHAR_LEFT_CURLY_BRACKET)\n      {\n      PCRE2_SPTR p = ptr + 1;\n\n      while (p < ptrend && (*p == CHAR_SPACE || *p == CHAR_HT)) p++;\n      if (!read_number(&p, ptrend, bracount, MAX_GROUP_NUMBER, ERR61, &s,\n          errorcodeptr))\n        {\n        if (*errorcodeptr == 0) escape = ESC_k;  /* No number found */\n        break;\n        }\n      while (p < ptrend && (*p == CHAR_SPACE || *p == CHAR_HT)) p++;\n\n      if (p >= ptrend || *p != CHAR_RIGHT_CURLY_BRACKET)\n        {\n        /* not advancing ptr; report error at the \\g character */\n        *errorcodeptr = ERR57;\n        break;\n        }\n      ptr = p + 1;\n      }\n\n    /* Read an undelimited number */\n\n    else\n      {\n      if (!read_number(&ptr, ptrend, bracount, MAX_GROUP_NUMBER, ERR61, &s,\n          errorcodeptr))\n        {\n        if (*errorcodeptr == 0) *errorcodeptr = ERR57;  /* No number found */\n        break;\n        }\n      }\n\n    if (s <= 0)\n      {\n      *errorcodeptr = ERR15;\n      break;\n      }\n\n    escape = -(s+1);\n    break;\n\n    /* The handling of escape sequences consisting of a string of digits\n    starting with one that is not zero is not straightforward. Perl has changed\n    over the years. Nowadays \\g{} for backreferences and \\o{} for octal are\n    recommended to avoid the ambiguities in the old syntax.\n\n    Outside a character class, the digits are read as a decimal number. If the\n    number is less than 10, or if there are that many previous extracting left\n    brackets, it is a back reference. Otherwise, up to three octal digits are\n    read to form an escaped character code. Thus \\123 is likely to be octal 123\n    (cf \\0123, which is octal 012 followed by the literal 3). This is the \"Perl\n    style\" of handling ambiguous octal/backrefences such as \\12.\n\n    There is an alternative disambiguation strategy, selected by\n    PCRE2_EXTRA_PYTHON_OCTAL, which follows Python's behaviour. An octal must\n    have either a leading zero, or exactly three octal digits; otherwise it's\n    a backreference. The disambiguation is stable, and does not depend on how\n    many capture groups are defined (it's simply an invalid backreference if\n    there is no corresponding capture group). Additionally, octal values above\n    \\377 (\\xff) are rejected.\n\n    Inside a character class, \\ followed by a digit is always either a literal\n    8 or 9 or an octal number. */\n\n    case CHAR_1: case CHAR_2: case CHAR_3: case CHAR_4: case CHAR_5:\n    case CHAR_6: case CHAR_7: case CHAR_8: case CHAR_9:\n\n    if (isclass)\n      {\n      /* Fall through to octal handling; never a backreference inside a class. */\n      }\n    else if ((xoptions & PCRE2_EXTRA_PYTHON_OCTAL) != 0)\n      {\n      /* Python-style disambiguation. */\n      if (ptr[-1] <= CHAR_7 && ptr + 1 < ptrend && ptr[0] >= CHAR_0 &&\n          ptr[0] <= CHAR_7 && ptr[1] >= CHAR_0 && ptr[1] <= CHAR_7)\n        {\n        /* We peeked a three-digit octal, so fall through */\n        }\n      else\n        {\n        /* We are at a digit, so the only possible error from read_number() is\n        a number that is too large. */\n        ptr--;   /* Back to the digit */\n\n        if (!read_number(&ptr, ptrend, -1, MAX_GROUP_NUMBER, 0, &s, errorcodeptr))\n          {\n          *errorcodeptr = ERR61;\n          break;\n          }\n\n        escape = -(s+1);\n        break;\n        }\n      }\n    else\n      {\n      /* Perl-style disambiguation. */\n      oldptr = ptr;\n      ptr--;   /* Back to the digit */\n\n      /* As we know we are at a digit, the only possible error from\n      read_number() is a number that is too large to be a group number. Because\n      that number might be still valid if read as an octal, errorcodeptr is not\n      set on failure and therefore a sentinel value of INT_MAX is used instead\n      of the original value, and will be used later to properly set the error,\n      if not falling through. */\n\n      if (!read_number(&ptr, ptrend, -1, MAX_GROUP_NUMBER, 0, &s, errorcodeptr))\n        s = INT_MAX;\n\n      /* \\1 to \\9 are always back references. \\8x and \\9x are too; \\1x to \\7x\n      are octal escapes if there are not that many previous captures. */\n\n      if (s < 10 || c >= CHAR_8 || (unsigned)s <= bracount)\n        {\n        /* s > MAX_GROUP_NUMBER should not be possible because of read_number(),\n        but we keep it just to be safe and because it will also catch the\n        sentinel value that was set on failure by that function. */\n\n        if ((unsigned)s > MAX_GROUP_NUMBER)\n          {\n          PCRE2_ASSERT(s == INT_MAX);\n          *errorcodeptr = ERR61;\n          }\n        else escape = -(s+1);     /* Indicates a back reference */\n        break;\n        }\n\n      ptr = oldptr;      /* Put the pointer back and fall through */\n      }\n\n    /* Handle a digit following \\ when the number is not a back reference, or\n    we are within a character class. If the first digit is 8 or 9, Perl used to\n    generate a binary zero and then treat the digit as a following literal. At\n    least by Perl 5.18 this changed so as not to insert the binary zero. */\n\n    if (c >= CHAR_8) break;\n\n    /* Fall through */\n\n    /* \\0 always starts an octal number, but we may drop through to here with a\n    larger first octal digit. The original code used just to take the least\n    significant 8 bits of octal numbers (I think this is what early Perls used\n    to do). Nowadays we allow for larger numbers in UTF-8 mode and 16/32-bit mode,\n    but no more than 3 octal digits. */\n\n    case CHAR_0:\n    c -= CHAR_0;\n    while(i++ < 2 && ptr < ptrend && *ptr >= CHAR_0 && *ptr <= CHAR_7)\n        c = c * 8 + *ptr++ - CHAR_0;\n    if (c > 0xff)\n      {\n      if ((xoptions & PCRE2_EXTRA_PYTHON_OCTAL) != 0) *errorcodeptr = ERR102;\n#if PCRE2_CODE_UNIT_WIDTH == 8\n      else if (!utf) *errorcodeptr = ERR51;\n#endif\n      }\n\n    /* PCRE2_EXTRA_NO_BS0 disables the NUL escape '\\0' but doesn't affect\n    two- or three-character octal escapes \\00 and \\000, nor \\x00. */\n\n    if ((xoptions & PCRE2_EXTRA_NO_BS0) != 0 && c == 0 && i == 1)\n        *errorcodeptr = ERR98;\n    break;\n\n    /* \\o is a relatively new Perl feature, supporting a more general way of\n    specifying character codes in octal. The only supported form is \\o{ddd},\n    with optional spaces or tabs after { and before }. */\n\n    case CHAR_o:\n    if (ptr >= ptrend || *ptr++ != CHAR_LEFT_CURLY_BRACKET)\n      {\n      ptr--;\n      *errorcodeptr = ERR55;\n      break;\n      }\n\n    while (ptr < ptrend && (*ptr == CHAR_SPACE || *ptr == CHAR_HT)) ptr++;\n    if (ptr >= ptrend || *ptr == CHAR_RIGHT_CURLY_BRACKET)\n      {\n      *errorcodeptr = ERR78;\n      break;\n      }\n\n    c = 0;\n    overflow = FALSE;\n    while (ptr < ptrend && *ptr >= CHAR_0 && *ptr <= CHAR_7)\n      {\n      cc = *ptr++;\n      if (c == 0 && cc == CHAR_0) continue;     /* Leading zeroes */\n#if PCRE2_CODE_UNIT_WIDTH == 32\n      if (c >= 0x20000000u) { overflow = TRUE; break; }\n#endif\n      c = (c << 3) + (cc - CHAR_0);\n#if PCRE2_CODE_UNIT_WIDTH == 8\n      if (c > (utf ? 0x10ffffU : 0xffU)) { overflow = TRUE; break; }\n#elif PCRE2_CODE_UNIT_WIDTH == 16\n      if (c > (utf ? 0x10ffffU : 0xffffU)) { overflow = TRUE; break; }\n#elif PCRE2_CODE_UNIT_WIDTH == 32\n      if (utf && c > 0x10ffffU) { overflow = TRUE; break; }\n#endif\n      }\n\n    while (ptr < ptrend && (*ptr == CHAR_SPACE || *ptr == CHAR_HT)) ptr++;\n\n    if (overflow)\n      {\n      while (ptr < ptrend && *ptr >= CHAR_0 && *ptr <= CHAR_7) ptr++;\n      *errorcodeptr = ERR34;\n      }\n    else if (ptr < ptrend && *ptr++ == CHAR_RIGHT_CURLY_BRACKET)\n      {\n      if (utf && c >= 0xd800 && c <= 0xdfff &&\n          (xoptions & PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES) == 0)\n        {\n        ptr--;\n        *errorcodeptr = ERR73;\n        }\n      }\n    else\n      {\n      ptr--;\n      *errorcodeptr = ERR64;\n      }\n    break;\n\n    /* When PCRE2_ALT_BSUX or PCRE2_EXTRA_ALT_BSUX is set, \\x must be followed\n    by two hexadecimal digits. Otherwise it is a lowercase x letter. */\n\n    case CHAR_x:\n    if (alt_bsux)\n      {\n      uint32_t xc;\n      if (ptrend - ptr < 2) break;               /* Less than 2 characters */\n      if ((cc = XDIGIT(ptr[0])) == 0xff) break;  /* Not a hex digit */\n      if ((xc = XDIGIT(ptr[1])) == 0xff) break;  /* Not a hex digit */\n      c = (cc << 4) | xc;\n      ptr += 2;\n      }\n\n    /* Handle \\x in Perl's style. \\x{ddd} is a character code which can be\n    greater than 0xff in UTF-8 or non-8bit mode, but only if the ddd are hex\n    digits. If not, { used to be treated as a data character. However, Perl\n    seems to read hex digits up to the first non-such, and ignore the rest, so\n    that, for example \\x{zz} matches a binary zero. This seems crazy, so PCRE\n    now gives an error. */\n\n    else\n      {\n      if (ptr < ptrend && *ptr == CHAR_LEFT_CURLY_BRACKET)\n        {\n        ptr++;\n        while (ptr < ptrend && (*ptr == CHAR_SPACE || *ptr == CHAR_HT)) ptr++;\n\n#ifndef EBCDIC\n        COME_FROM_NU:\n#endif\n        if (ptr >= ptrend || *ptr == CHAR_RIGHT_CURLY_BRACKET)\n          {\n          *errorcodeptr = ERR78;\n          break;\n          }\n        c = 0;\n        overflow = FALSE;\n\n        while (ptr < ptrend && (cc = XDIGIT(*ptr)) != 0xff)\n          {\n          ptr++;\n          if (c == 0 && cc == 0) continue;   /* Leading zeroes */\n#if PCRE2_CODE_UNIT_WIDTH == 32\n          if (c >= 0x10000000l) { overflow = TRUE; break; }\n#endif\n          c = (c << 4) | cc;\n          if ((utf && c > 0x10ffffU) || (!utf && c > MAX_NON_UTF_CHAR))\n            {\n            overflow = TRUE;\n            break;\n            }\n          }\n\n        /* Perl ignores spaces and tabs before } */\n\n        while (ptr < ptrend && (*ptr == CHAR_SPACE || *ptr == CHAR_HT)) ptr++;\n\n        /* On overflow, skip remaining hex digits */\n\n        if (overflow)\n          {\n          while (ptr < ptrend && XDIGIT(*ptr) != 0xff) ptr++;\n          *errorcodeptr = ERR34;\n          }\n        else if (ptr < ptrend && *ptr++ == CHAR_RIGHT_CURLY_BRACKET)\n          {\n          if (utf && c >= 0xd800 && c <= 0xdfff &&\n              (xoptions & PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES) == 0)\n            {\n            ptr--;\n            *errorcodeptr = ERR73;\n            }\n          }\n\n        /* If the sequence of hex digits (followed by optional space) does not\n        end with '}', give an error. We used just to recognize this construct\n        and fall through to the normal \\x handling, but nowadays Perl gives an\n        error, which seems much more sensible, so we do too. */\n\n        else\n          {\n          ptr--;\n          *errorcodeptr = ERR67;\n          }\n        }   /* End of \\x{} processing */\n\n      /* Read a up to two hex digits after \\x */\n\n      else\n        {\n        /* Perl has the surprising/broken behaviour that \\x without following\n        hex digits is treated as an escape for NUL. Their source code laments\n        this but keeps it for backwards compatibility. A warning is printed\n        when \"use warnings\" is enabled. Because we don't have warnings, we\n        simply forbid it. */\n        if (ptr >= ptrend || (cc = XDIGIT(*ptr)) == 0xff)\n          {\n          /* Not a hex digit */\n          *errorcodeptr = ERR78;\n          break;\n          }\n        ptr++;\n        c = cc;\n\n        /* With \"use re 'strict'\" Perl actually requires exactly two digits (error\n        for \\x, \\xA and \\xAAA). While \\x was already rejected, this seems overly\n        strict, and there seems little incentive to align with that, given the\n        backwards-compatibility cost.\n\n        For comparison, note that other engines disagree. For example:\n          - Java allows 1 or 2 hex digits. Error if 0 digits. No error if >2 digits\n          - .NET requires 2 hex digits. Error if 0, 1 digits. No error if >2 digits.\n        */\n        if (ptr >= ptrend || (cc = XDIGIT(*ptr)) == 0xff) break;  /* Not a hex digit */\n        ptr++;\n        c = (c << 4) | cc;\n        }     /* End of \\xdd handling */\n      }       /* End of Perl-style \\x handling */\n    break;\n\n    /* The handling of \\c is different in ASCII and EBCDIC environments. In an\n    ASCII (or Unicode) environment, an error is given if the character\n    following \\c is not a printable ASCII character. Otherwise, the following\n    character is upper-cased if it is a letter, and after that the 0x40 bit is\n    flipped. The result is the value of the escape.\n\n    In an EBCDIC environment the handling of \\c is compatible with the\n    specification in the perlebcdic document. The following character must be\n    a letter or one of small number of special characters. These provide a\n    means of defining the character values 0-31.\n\n    For testing the EBCDIC handling of \\c in an ASCII environment, recognize\n    the EBCDIC value of 'c' explicitly. */\n\n#if defined EBCDIC && 'a' != 0x81\n    case 0x83:\n#else\n    case CHAR_c:\n#endif\n    if (ptr >= ptrend)\n      {\n      *errorcodeptr = ERR2;\n      break;\n      }\n    c = *ptr;\n    if (c >= CHAR_a && c <= CHAR_z) c = UPPER_CASE(c);\n\n    /* Handle \\c in an ASCII/Unicode environment. */\n\n#ifndef EBCDIC    /* ASCII/UTF-8 coding */\n    if (c < 32 || c > 126)  /* Excludes all non-printable ASCII */\n      {\n      *errorcodeptr = ERR68;\n      break;\n      }\n    c ^= 0x40;\n\n    /* Handle \\c in an EBCDIC environment. The special case \\c? is converted to\n    255 (0xff) or 95 (0x5f) if other characters suggest we are using the\n    POSIX-BC encoding. (This is the way Perl indicates that it handles \\c?.)\n    The other valid sequences correspond to a list of specific characters. */\n\n#else\n    if (c == CHAR_QUESTION_MARK)\n      c = ('\\\\' == 188 && '`' == 74)? 0x5f : 0xff;\n    else\n      {\n      for (i = 0; i < 32; i++)\n        {\n        if (c == ebcdic_escape_c[i]) break;\n        }\n      if (i < 32) c = i; else *errorcodeptr = ERR68;\n      }\n#endif  /* EBCDIC */\n\n    ptr++;\n    break;\n\n    /* Any other alphanumeric following \\ is an error. Perl gives an error only\n    if in warning mode, but PCRE doesn't have a warning mode. */\n\n    default:\n    *errorcodeptr = ERR3;\n    *ptrptr = ptr - 1;     /* Point to the character at fault */\n    return 0;\n    }\n  }\n\n/* Set the pointer to the next character before returning. */\n\n*ptrptr = ptr;\n*chptr = c;\nreturn escape;\n}\n\n\n\n#ifdef SUPPORT_UNICODE\n/*************************************************\n*               Handle \\P and \\p                 *\n*************************************************/\n\n/* This function is called after \\P or \\p has been encountered, provided that\nPCRE2 is compiled with support for UTF and Unicode properties. On entry, the\ncontents of ptrptr are pointing after the P or p. On exit, it is left pointing\nafter the final code unit of the escape sequence.\n\nArguments:\n  ptrptr         the pattern position pointer\n  negptr         a boolean that is set TRUE for negation else FALSE\n  ptypeptr       an unsigned int that is set to the type value\n  pdataptr       an unsigned int that is set to the detailed property value\n  errorcodeptr   the error code variable\n  cb             the compile data\n\nReturns:         TRUE if the type value was found, or FALSE for an invalid type\n*/\n\nstatic BOOL\nget_ucp(PCRE2_SPTR *ptrptr, BOOL *negptr, uint16_t *ptypeptr,\n  uint16_t *pdataptr, int *errorcodeptr, compile_block *cb)\n{\nPCRE2_UCHAR c;\nPCRE2_SIZE i, bot, top;\nPCRE2_SPTR ptr = *ptrptr;\nPCRE2_UCHAR name[50];\nPCRE2_UCHAR *vptr = NULL;\nuint16_t ptscript = PT_NOTSCRIPT;\n\nif (ptr >= cb->end_pattern) goto ERROR_RETURN;\nc = *ptr++;\n*negptr = FALSE;\n\n/* \\P or \\p can be followed by a name in {}, optionally preceded by ^ for\nnegation. We must be handling Unicode encoding here, though we may be compiling\nfor UTF-8 input in an EBCDIC environment. (PCRE2 does not support both EBCDIC\ninput and Unicode input in the same build.) In accordance with Unicode's \"loose\nmatching\" rules, ASCII white space, hyphens, and underscores are ignored. We\ndon't use isspace() or tolower() because (a) code points may be greater than\n255, and (b) they wouldn't work when compiling for Unicode in an EBCDIC\nenvironment. */\n\nif (c == CHAR_LEFT_CURLY_BRACKET)\n  {\n  if (ptr >= cb->end_pattern) goto ERROR_RETURN;\n\n  for (i = 0; i < (int)(sizeof(name) / sizeof(PCRE2_UCHAR)) - 1; i++)\n    {\n    REDO:\n\n    if (ptr >= cb->end_pattern) goto ERROR_RETURN;\n    c = *ptr++;\n\n    /* Skip ignorable Unicode characters. */\n\n    while (c == CHAR_UNDERSCORE || c == CHAR_MINUS || c == CHAR_SPACE ||\n          (c >= CHAR_HT && c <= CHAR_CR))\n      {\n      if (ptr >= cb->end_pattern) goto ERROR_RETURN;\n      c = *ptr++;\n      }\n\n    /* The first significant character being circumflex negates the meaning of\n    the item. */\n\n    if (i == 0 && !*negptr && c == CHAR_CIRCUMFLEX_ACCENT)\n      {\n      *negptr = TRUE;\n      goto REDO;\n      }\n\n    if (c == CHAR_RIGHT_CURLY_BRACKET) break;\n\n    /* Names consist of ASCII letters and digits, but equals and colon may also\n    occur as a name/value separator. We must also allow for \\p{L&}. A simple\n    check for a value between '&' and 'z' suffices because anything else in a\n    name or value will cause an \"unknown property\" error anyway. */\n\n    if (c < CHAR_AMPERSAND || c > CHAR_z) goto ERROR_RETURN;\n\n    /* Lower case a capital letter or remember where the name/value separator\n    is. */\n\n    if (c >= CHAR_A && c <= CHAR_Z) c |= 0x20;\n    else if ((c == CHAR_COLON || c == CHAR_EQUALS_SIGN) && vptr == NULL)\n      vptr = name + i;\n\n    name[i] = c;\n    }\n\n  /* Error if the loop didn't end with '}' - either we hit the end of the\n  pattern or the name was longer than any legal property name. */\n\n  if (c != CHAR_RIGHT_CURLY_BRACKET) goto ERROR_RETURN;\n  name[i] = 0;\n  }\n\n/* If { doesn't follow \\p or \\P there is just one following character, which\nmust be an ASCII letter. */\n\nelse if (c >= CHAR_A && c <= CHAR_Z)\n  {\n  name[0] = c | 0x20;  /* Lower case */\n  name[1] = 0;\n  }\nelse if (c >= CHAR_a && c <= CHAR_z)\n  {\n  name[0] = c;\n  name[1] = 0;\n  }\nelse goto ERROR_RETURN;\n\n*ptrptr = ptr;   /* Update pattern pointer */\n\n/* If the property contains ':' or '=' we have class name and value separately\nspecified. The following are supported:\n\n  . Bidi_Class (synonym bc), for which the property names are \"bidi<name>\".\n  . Script (synonym sc) for which the property name is the script name\n  . Script_Extensions (synonym scx), ditto\n\nAs this is a small number, we currently just check the names directly. If this\ngrows, a sorted table and a switch will be neater.\n\nFor both the script properties, set a PT_xxx value so that (1) they can be\ndistinguished and (2) invalid script names that happen to be the name of\nanother property can be diagnosed. */\n\nif (vptr != NULL)\n  {\n  int offset = 0;\n  PCRE2_UCHAR sname[8];\n\n  *vptr = 0;   /* Terminate property name */\n  if (PRIV(strcmp_c8)(name, STRING_bidiclass) == 0 ||\n      PRIV(strcmp_c8)(name, STRING_bc) == 0)\n    {\n    offset = 4;\n    sname[0] = CHAR_b;\n    sname[1] = CHAR_i;  /* There is no strcpy_c8 function */\n    sname[2] = CHAR_d;\n    sname[3] = CHAR_i;\n    }\n\n  else if (PRIV(strcmp_c8)(name, STRING_script) == 0 ||\n           PRIV(strcmp_c8)(name, STRING_sc) == 0)\n    ptscript = PT_SC;\n\n  else if (PRIV(strcmp_c8)(name, STRING_scriptextensions) == 0 ||\n           PRIV(strcmp_c8)(name, STRING_scx) == 0)\n    ptscript = PT_SCX;\n\n  else\n    {\n    *errorcodeptr = ERR47;\n    return FALSE;\n    }\n\n  /* Adjust the string in name[] as needed */\n\n  memmove(name + offset, vptr + 1, (name + i - vptr)*sizeof(PCRE2_UCHAR));\n  if (offset != 0) memmove(name, sname, offset*sizeof(PCRE2_UCHAR));\n  }\n\n/* Search for a recognized property using binary chop. */\n\nbot = 0;\ntop = PRIV(utt_size);\n\nwhile (bot < top)\n  {\n  int r;\n  i = (bot + top) >> 1;\n  r = PRIV(strcmp_c8)(name, PRIV(utt_names) + PRIV(utt)[i].name_offset);\n\n  /* When a matching property is found, some extra checking is needed when the\n  \\p{xx:yy} syntax is used and xx is either sc or scx. */\n\n  if (r == 0)\n    {\n    *pdataptr = PRIV(utt)[i].value;\n    if (vptr == NULL || ptscript == PT_NOTSCRIPT)\n      {\n      *ptypeptr = PRIV(utt)[i].type;\n      return TRUE;\n      }\n\n    switch (PRIV(utt)[i].type)\n      {\n      case PT_SC:\n      *ptypeptr = PT_SC;\n      return TRUE;\n\n      case PT_SCX:\n      *ptypeptr = ptscript;\n      return TRUE;\n      }\n\n    break;  /* Non-script found */\n    }\n\n  if (r > 0) bot = i + 1; else top = i;\n  }\n\n*errorcodeptr = ERR47;   /* Unrecognized property */\nreturn FALSE;\n\nERROR_RETURN:            /* Malformed \\P or \\p */\n*errorcodeptr = ERR46;\n*ptrptr = ptr;\nreturn FALSE;\n}\n#endif\n\n\n\n/*************************************************\n*           Check for POSIX class syntax         *\n*************************************************/\n\n/* This function is called when the sequence \"[:\" or \"[.\" or \"[=\" is\nencountered in a character class. It checks whether this is followed by a\nsequence of characters terminated by a matching \":]\" or \".]\" or \"=]\". If we\nreach an unescaped ']' without the special preceding character, return FALSE.\n\nOriginally, this function only recognized a sequence of letters between the\nterminators, but it seems that Perl recognizes any sequence of characters,\nthough of course unknown POSIX names are subsequently rejected. Perl gives an\n\"Unknown POSIX class\" error for [:f\\oo:] for example, where previously PCRE\ndidn't consider this to be a POSIX class. Likewise for [:1234:].\n\nThe problem in trying to be exactly like Perl is in the handling of escapes. We\nhave to be sure that [abc[:x\\]pqr] is *not* treated as containing a POSIX\nclass, but [abc[:x\\]pqr:]] is (so that an error can be generated). The code\nbelow handles the special cases \\\\ and \\], but does not try to do any other\nescape processing. This makes it different from Perl for cases such as\n[:l\\ower:] where Perl recognizes it as the POSIX class \"lower\" but PCRE does\nnot recognize \"l\\ower\". This is a lesser evil than not diagnosing bad classes\nwhen Perl does, I think.\n\nA user pointed out that PCRE was rejecting [:a[:digit:]] whereas Perl was not.\nIt seems that the appearance of a nested POSIX class supersedes an apparent\nexternal class. For example, [:a[:digit:]b:] matches \"a\", \"b\", \":\", or\na digit. This is handled by returning FALSE if the start of a new group with\nthe same terminator is encountered, since the next closing sequence must close\nthe nested group, not the outer one.\n\nIn Perl, unescaped square brackets may also appear as part of class names. For\nexample, [:a[:abc]b:] gives unknown POSIX class \"[:abc]b:]\". However, for\n[:a[:abc]b][b:] it gives unknown POSIX class \"[:abc]b][b:]\", which does not\nseem right at all. PCRE does not allow closing square brackets in POSIX class\nnames.\n\nArguments:\n  ptr      pointer to the character after the initial [ (colon, dot, equals)\n  ptrend   pointer to the end of the pattern\n  endptr   where to return a pointer to the terminating ':', '.', or '='\n\nReturns:   TRUE or FALSE\n*/\n\nstatic BOOL\ncheck_posix_syntax(PCRE2_SPTR ptr, PCRE2_SPTR ptrend, PCRE2_SPTR *endptr)\n{\nPCRE2_UCHAR terminator;  /* Don't combine these lines; the Solaris cc */\nterminator = *ptr++;     /* compiler warns about \"non-constant\" initializer. */\n\nfor (; ptrend - ptr >= 2; ptr++)\n  {\n  if (*ptr == CHAR_BACKSLASH &&\n      (ptr[1] == CHAR_RIGHT_SQUARE_BRACKET || ptr[1] == CHAR_BACKSLASH))\n    ptr++;\n\n  else if ((*ptr == CHAR_LEFT_SQUARE_BRACKET && ptr[1] == terminator) ||\n            *ptr == CHAR_RIGHT_SQUARE_BRACKET) return FALSE;\n\n  else if (*ptr == terminator && ptr[1] == CHAR_RIGHT_SQUARE_BRACKET)\n    {\n    *endptr = ptr;\n    return TRUE;\n    }\n  }\n\nreturn FALSE;\n}\n\n\n\n/*************************************************\n*          Check POSIX class name                *\n*************************************************/\n\n/* This function is called to check the name given in a POSIX-style class entry\nsuch as [:alnum:].\n\nArguments:\n  ptr        points to the first letter\n  len        the length of the name\n\nReturns:     a value representing the name, or -1 if unknown\n*/\n\nstatic int\ncheck_posix_name(PCRE2_SPTR ptr, int len)\n{\nconst char *pn = posix_names;\nint yield = 0;\nwhile (posix_name_lengths[yield] != 0)\n  {\n  if (len == posix_name_lengths[yield] &&\n    PRIV(strncmp_c8)(ptr, pn, (unsigned int)len) == 0) return yield;\n  pn += posix_name_lengths[yield] + 1;\n  yield++;\n  }\nreturn -1;\n}\n\n\n\n/*************************************************\n*       Read a subpattern or VERB name           *\n*************************************************/\n\n/* This function is called from parse_regex() below whenever it needs to read\nthe name of a subpattern or a (*VERB) or an (*alpha_assertion). The initial\npointer must be to the preceding character. If that character is '*' we are\nreading a verb or alpha assertion name. The pointer is updated to point after\nthe name, for a VERB or alpha assertion name, or after tha name's terminator\nfor a subpattern name. Returning both the offset and the name pointer is\nredundant information, but some callers use one and some the other, so it is\nsimplest just to return both. When the name is in braces, spaces and tabs are\nallowed (and ignored) at either end.\n\nArguments:\n  ptrptr      points to the character pointer variable\n  ptrend      points to the end of the input string\n  utf         true if the input is UTF-encoded\n  terminator  the terminator of a subpattern name must be this\n  offsetptr   where to put the offset from the start of the pattern\n  nameptr     where to put a pointer to the name in the input\n  namelenptr  where to put the length of the name\n  errcodeptr  where to put an error code\n  cb          pointer to the compile data block\n\nReturns:    TRUE if a name was read\n            FALSE otherwise, with error code set\n*/\n\nstatic BOOL\nread_name(PCRE2_SPTR *ptrptr, PCRE2_SPTR ptrend, BOOL utf, uint32_t terminator,\n  PCRE2_SIZE *offsetptr, PCRE2_SPTR *nameptr, uint32_t *namelenptr,\n  int *errorcodeptr, compile_block *cb)\n{\nPCRE2_SPTR ptr = *ptrptr;\nBOOL is_group = (*ptr++ != CHAR_ASTERISK);\nBOOL is_braced = terminator == CHAR_RIGHT_CURLY_BRACKET;\n\nif (is_braced)\n  while (ptr < ptrend && (*ptr == CHAR_SPACE || *ptr == CHAR_HT)) ptr++;\n\nif (ptr >= ptrend)                 /* No characters in name */\n  {\n  *errorcodeptr = is_group? ERR62: /* Subpattern name expected */\n                            ERR60; /* Verb not recognized or malformed */\n  goto FAILED;\n  }\n\n*nameptr = ptr;\n*offsetptr = (PCRE2_SIZE)(ptr - cb->start_pattern);\n\n/* If this logic were ever to change, the matching function in pcre2_substitute.c\nought to be updated to match. */\n\n/* In UTF mode, a group name may contain letters and decimal digits as defined\nby Unicode properties, and underscores, but must not start with a digit. */\n\n#ifdef SUPPORT_UNICODE\nif (utf && is_group)\n  {\n  uint32_t c, type;\n\n  GETCHAR(c, ptr);\n  type = UCD_CHARTYPE(c);\n\n  if (type == ucp_Nd)\n    {\n    *errorcodeptr = ERR44;\n    goto FAILED;\n    }\n\n  for(;;)\n    {\n    if (type != ucp_Nd && PRIV(ucp_gentype)[type] != ucp_L &&\n        c != CHAR_UNDERSCORE) break;\n    ptr++;\n    FORWARDCHARTEST(ptr, ptrend);\n    if (ptr >= ptrend) break;\n    GETCHAR(c, ptr);\n    type = UCD_CHARTYPE(c);\n    }\n  }\nelse\n#else\n(void)utf;  /* Avoid compiler warning */\n#endif      /* SUPPORT_UNICODE */\n\n/* Handle non-group names and group names in non-UTF modes. A group name must\nnot start with a digit. If either of the others start with a digit it just\nwon't be recognized. */\n\n  {\n  if (is_group && IS_DIGIT(*ptr))\n    {\n    *errorcodeptr = ERR44;\n    goto FAILED;\n    }\n\n  while (ptr < ptrend && MAX_255(*ptr) && (cb->ctypes[*ptr] & ctype_word) != 0)\n    {\n    ptr++;\n    }\n  }\n\n/* Check name length */\n\nif (ptr > *nameptr + MAX_NAME_SIZE)\n  {\n  *errorcodeptr = ERR48;\n  goto FAILED;\n  }\n*namelenptr = (uint32_t)(ptr - *nameptr);\n\n/* Subpattern names must not be empty, and their terminator is checked here.\n(What follows a verb or alpha assertion name is checked separately.) */\n\nif (is_group)\n  {\n  if (ptr == *nameptr)\n    {\n    *errorcodeptr = ERR62;   /* Subpattern name expected */\n    goto FAILED;\n    }\n  if (is_braced)\n    while (ptr < ptrend && (*ptr == CHAR_SPACE || *ptr == CHAR_HT)) ptr++;\n  if (ptr >= ptrend || *ptr != (PCRE2_UCHAR)terminator)\n    {\n    *errorcodeptr = ERR42;\n    goto FAILED;\n    }\n  ptr++;\n  }\n\n*ptrptr = ptr;\nreturn TRUE;\n\nFAILED:\n*ptrptr = ptr;\nreturn FALSE;\n}\n\n\n\n/*************************************************\n*          Manage callouts at start of cycle     *\n*************************************************/\n\n/* At the start of a new item in parse_regex() we are able to record the\ndetails of the previous item in a prior callout, and also to set up an\nautomatic callout if enabled. Avoid having two adjacent automatic callouts,\nwhich would otherwise happen for items such as \\Q that contribute nothing to\nthe parsed pattern.\n\nArguments:\n  ptr              current pattern pointer\n  pcalloutptr      points to a pointer to previous callout, or NULL\n  auto_callout     TRUE if auto_callouts are enabled\n  parsed_pattern   the parsed pattern pointer\n  cb               compile block\n\nReturns: possibly updated parsed_pattern pointer.\n*/\n\nstatic uint32_t *\nmanage_callouts(PCRE2_SPTR ptr, uint32_t **pcalloutptr, BOOL auto_callout,\n  uint32_t *parsed_pattern, compile_block *cb)\n{\nuint32_t *previous_callout = *pcalloutptr;\n\nif (previous_callout != NULL) previous_callout[2] = (uint32_t)(ptr -\n  cb->start_pattern - (PCRE2_SIZE)previous_callout[1]);\n\nif (!auto_callout) previous_callout = NULL; else\n  {\n  if (previous_callout == NULL ||\n      previous_callout != parsed_pattern - 4 ||\n      previous_callout[3] != 255)\n    {\n    previous_callout = parsed_pattern;  /* Set up new automatic callout */\n    parsed_pattern += 4;\n    previous_callout[0] = META_CALLOUT_NUMBER;\n    previous_callout[2] = 0;\n    previous_callout[3] = 255;\n    }\n  previous_callout[1] = (uint32_t)(ptr - cb->start_pattern);\n  }\n\n*pcalloutptr = previous_callout;\nreturn parsed_pattern;\n}\n\n\n\n/*************************************************\n*          Handle \\d, \\D, \\s, \\S, \\w, \\W         *\n*************************************************/\n\n/* This function is called from parse_regex() below, both for freestanding\nescapes, and those within classes, to handle those escapes that may change when\nUnicode property support is requested. Note that PCRE2_UCP will never be set\nwithout Unicode support because that is checked when pcre2_compile() is called.\n\nArguments:\n  escape          the ESC_... value\n  parsed_pattern  where to add the code\n  options         options bits\n  xoptions        extra options bits\n\nReturns:          updated value of parsed_pattern\n*/\nstatic uint32_t *\nhandle_escdsw(int escape, uint32_t *parsed_pattern, uint32_t options,\n  uint32_t xoptions)\n{\nuint32_t ascii_option = 0;\nuint32_t prop = ESC_p;\n\nswitch(escape)\n  {\n  case ESC_D:\n  prop = ESC_P;\n  /* Fall through */\n  case ESC_d:\n  ascii_option = PCRE2_EXTRA_ASCII_BSD;\n  break;\n\n  case ESC_S:\n  prop = ESC_P;\n  /* Fall through */\n  case ESC_s:\n  ascii_option = PCRE2_EXTRA_ASCII_BSS;\n  break;\n\n  case ESC_W:\n  prop = ESC_P;\n  /* Fall through */\n  case ESC_w:\n  ascii_option = PCRE2_EXTRA_ASCII_BSW;\n  break;\n  }\n\nif ((options & PCRE2_UCP) == 0 || (xoptions & ascii_option) != 0)\n  {\n  *parsed_pattern++ = META_ESCAPE + escape;\n  }\nelse\n  {\n  *parsed_pattern++ = META_ESCAPE + prop;\n  switch(escape)\n    {\n    case ESC_d:\n    case ESC_D:\n    *parsed_pattern++ = (PT_PC << 16) | ucp_Nd;\n    break;\n\n    case ESC_s:\n    case ESC_S:\n    *parsed_pattern++ = PT_SPACE << 16;\n    break;\n\n    case ESC_w:\n    case ESC_W:\n    *parsed_pattern++ = PT_WORD << 16;\n    break;\n    }\n  }\n\nreturn parsed_pattern;\n}\n\n\n\n/*************************************************\n* Maximum size of parsed_pattern for given input *\n*************************************************/\n\n/* This function is called from parse_regex() below, to determine the amount\nof memory to allocate for parsed_pattern. It is also called to check whether\nthe amount of data written respects the amount of memory allocated.\n\nArguments:\n  ptr             points to the start of the pattern\n  ptrend          points to the end of the pattern\n  utf             TRUE in UTF mode\n  options         the options bits\n\nReturns:          the number of uint32_t units for parsed_pattern\n*/\nstatic ptrdiff_t\nmax_parsed_pattern(PCRE2_SPTR ptr, PCRE2_SPTR ptrend, BOOL utf,\n  uint32_t options)\n{\nPCRE2_SIZE big32count = 0;\nptrdiff_t parsed_size_needed;\n\n/* When PCRE2_AUTO_CALLOUT is not set, in all but one case the number of\nunsigned 32-bit ints written out to the parsed pattern is bounded by the length\nof the pattern. The exceptional case is when running in 32-bit, non-UTF mode,\nwhen literal characters greater than META_END (0x80000000) have to be coded as\ntwo units. In this case, therefore, we scan the pattern to check for such\nvalues. */\n\n#if PCRE2_CODE_UNIT_WIDTH == 32\nif (!utf)\n  {\n  PCRE2_SPTR p;\n  for (p = ptr; p < ptrend; p++) if (*p >= META_END) big32count++;\n  }\n#else\n(void)utf;  /* Avoid compiler warning */\n#endif\n\nparsed_size_needed = (ptrend - ptr) + big32count;\n\n/* When PCRE2_AUTO_CALLOUT is set we have to assume a numerical callout (4\nelements) for each character. This is overkill, but memory is plentiful these\ndays. */\n\nif ((options & PCRE2_AUTO_CALLOUT) != 0)\n  parsed_size_needed += (ptrend - ptr) * 4;\n\nreturn parsed_size_needed;\n}\n\n\n\n/*************************************************\n*      Parse regex and identify named groups     *\n*************************************************/\n\n/* This function is called first of all. It scans the pattern and does two\nthings: (1) It identifies capturing groups and makes a table of named capturing\ngroups so that information about them is fully available to both the compiling\nscans. (2) It writes a parsed version of the pattern with comments omitted and\nescapes processed into the parsed_pattern vector.\n\nArguments:\n  ptr             points to the start of the pattern\n  options         compiling dynamic options (may change during the scan)\n  has_lookbehind  points to a boolean, set TRUE if a lookbehind is found\n  cb              pointer to the compile data block\n\nReturns:   zero on success or a non-zero error code, with the\n             error offset placed in the cb field\n*/\n\n/* A structure and some flags for dealing with nested groups. */\n\ntypedef struct nest_save {\n  uint16_t  nest_depth;\n  uint16_t  reset_group;\n  uint16_t  max_group;\n  uint16_t  flags;\n  uint32_t  options;\n  uint32_t  xoptions;\n} nest_save;\n\n#define NSF_RESET          0x0001u\n#define NSF_CONDASSERT     0x0002u\n#define NSF_ATOMICSR       0x0004u\n\n/* Options that are changeable within the pattern must be tracked during\nparsing. Some (e.g. PCRE2_EXTENDED) are implemented entirely during parsing,\nbut all must be tracked so that META_OPTIONS items set the correct values for\nthe main compiling phase. */\n\n#define PARSE_TRACKED_OPTIONS (PCRE2_CASELESS|PCRE2_DOTALL|PCRE2_DUPNAMES| \\\n  PCRE2_EXTENDED|PCRE2_EXTENDED_MORE|PCRE2_MULTILINE|PCRE2_NO_AUTO_CAPTURE| \\\n  PCRE2_UNGREEDY)\n\n#define PARSE_TRACKED_EXTRA_OPTIONS (PCRE2_EXTRA_CASELESS_RESTRICT| \\\n  PCRE2_EXTRA_ASCII_BSD|PCRE2_EXTRA_ASCII_BSS|PCRE2_EXTRA_ASCII_BSW| \\\n  PCRE2_EXTRA_ASCII_DIGIT|PCRE2_EXTRA_ASCII_POSIX)\n\n/* States used for analyzing ranges in character classes. The two OK values\nmust be last. */\n\nenum {\n  RANGE_NO, /* State after '[' (initial), or '[a-z'; hyphen is literal */\n  RANGE_STARTED, /* State after '[1-'; last-emitted code is META_RANGE_XYZ */\n  RANGE_FORBID_NO, /* State after '[\\d'; '-]' is allowed but not '-1]' */\n  RANGE_FORBID_STARTED, /* State after '[\\d-'*/\n  RANGE_OK_ESCAPED, /* State after '[\\1'; hyphen may be a range */\n  RANGE_OK_LITERAL /* State after '[1'; hyphen may be a range */\n};\n\n/* States used for analyzing operators and operands in extended character\nclasses. */\n\nenum {\n  CLASS_OP_EMPTY, /* At start of an expression; empty previous contents */\n  CLASS_OP_OPERAND, /* Have preceding operand; after \"z\" a \"--\" can follow */\n  CLASS_OP_OPERATOR /* Have preceding operator; after \"--\" operand must follow */\n};\n\n/* States used for determining the parse mode in character classes. The two\nPERL_EXT values must be last. */\n\nenum {\n  CLASS_MODE_NORMAL, /* Ordinary PCRE2 '[...]' class. */\n  CLASS_MODE_ALT_EXT, /* UTS#18-style extended '[...]' class. */\n  CLASS_MODE_PERL_EXT, /* Perl extended '(?[...])' class. */\n  CLASS_MODE_PERL_EXT_LEAF /* Leaf within extended '(?[ [...] ])' class. */\n};\n\n/* Only in 32-bit mode can there be literals > META_END. A macro encapsulates\nthe storing of literal values in the main parsed pattern, where they can always\nbe quantified. */\n\n#if PCRE2_CODE_UNIT_WIDTH == 32\n#define PARSED_LITERAL(c, p) \\\n  { \\\n  if (c >= META_END) *p++ = META_BIGVALUE; \\\n  *p++ = c; \\\n  okquantifier = TRUE; \\\n  }\n#else\n#define PARSED_LITERAL(c, p) *p++ = c; okquantifier = TRUE;\n#endif\n\n/* Here's the actual function. */\n\nstatic int parse_regex(PCRE2_SPTR ptr, uint32_t options, uint32_t xoptions,\n  BOOL *has_lookbehind, compile_block *cb)\n{\nuint32_t c;\nuint32_t delimiter;\nuint32_t namelen;\nuint32_t class_range_state;\nuint32_t class_op_state;\nuint32_t class_mode_state;\nuint32_t *class_start;\nuint32_t *verblengthptr = NULL;     /* Value avoids compiler warning */\nuint32_t *verbstartptr = NULL;\nuint32_t *previous_callout = NULL;\nuint32_t *parsed_pattern = cb->parsed_pattern;\nuint32_t *parsed_pattern_end = cb->parsed_pattern_end;\nuint32_t *this_parsed_item = NULL;\nuint32_t *prev_parsed_item = NULL;\nuint32_t meta_quantifier = 0;\nuint32_t add_after_mark = 0;\nuint16_t nest_depth = 0;\nint16_t class_depth_m1 = -1; /* The m1 means minus 1. */\nint16_t class_maxdepth_m1 = -1;\nint after_manual_callout = 0;\nint expect_cond_assert = 0;\nint errorcode = 0;\nint escape;\nint i;\nBOOL inescq = FALSE;\nBOOL inverbname = FALSE;\nBOOL utf = (options & PCRE2_UTF) != 0;\nBOOL auto_callout = (options & PCRE2_AUTO_CALLOUT) != 0;\nBOOL isdupname;\nBOOL negate_class;\nBOOL okquantifier = FALSE;\nPCRE2_SPTR thisptr;\nPCRE2_SPTR name;\nPCRE2_SPTR ptrend = cb->end_pattern;\nPCRE2_SPTR verbnamestart = NULL;    /* Value avoids compiler warning */\nPCRE2_SPTR class_range_forbid_ptr = NULL;\nnamed_group *ng;\nnest_save *top_nest, *end_nests;\n#ifdef PCRE2_DEBUG\nuint32_t *parsed_pattern_check;\nptrdiff_t parsed_pattern_extra = 0;\nptrdiff_t parsed_pattern_extra_check = 0;\nPCRE2_SPTR ptr_check;\n#endif\n\nPCRE2_ASSERT(parsed_pattern != NULL);\n\n/* Insert leading items for word and line matching (features provided for the\nbenefit of pcre2grep). */\n\nif ((xoptions & PCRE2_EXTRA_MATCH_LINE) != 0)\n  {\n  *parsed_pattern++ = META_CIRCUMFLEX;\n  *parsed_pattern++ = META_NOCAPTURE;\n  }\nelse if ((xoptions & PCRE2_EXTRA_MATCH_WORD) != 0)\n  {\n  *parsed_pattern++ = META_ESCAPE + ESC_b;\n  *parsed_pattern++ = META_NOCAPTURE;\n  }\n\n#ifdef PCRE2_DEBUG\nparsed_pattern_check = parsed_pattern;\nptr_check = ptr;\n#endif\n\n/* If the pattern is actually a literal string, process it separately to avoid\ncluttering up the main loop. */\n\nif ((options & PCRE2_LITERAL) != 0)\n  {\n  while (ptr < ptrend)\n    {\n    if (parsed_pattern >= parsed_pattern_end)\n      {\n      PCRE2_DEBUG_UNREACHABLE();\n      errorcode = ERR63;  /* Internal error (parsed pattern overflow) */\n      goto FAILED;\n      }\n    thisptr = ptr;\n    GETCHARINCTEST(c, ptr);\n    if (auto_callout)\n      parsed_pattern = manage_callouts(thisptr, &previous_callout,\n        auto_callout, parsed_pattern, cb);\n    PARSED_LITERAL(c, parsed_pattern);\n    }\n  goto PARSED_END;\n  }\n\n/* Process a real regex which may contain meta-characters. */\n\ntop_nest = NULL;\nend_nests = (nest_save *)(cb->start_workspace + cb->workspace_size);\n\n/* The size of the nest_save structure might not be a factor of the size of the\nworkspace. Therefore we must round down end_nests so as to correctly avoid\ncreating a nest_save that spans the end of the workspace. */\n\nend_nests = (nest_save *)((char *)end_nests -\n  ((cb->workspace_size * sizeof(PCRE2_UCHAR)) % sizeof(nest_save)));\n\n/* PCRE2_EXTENDED_MORE implies PCRE2_EXTENDED */\n\nif ((options & PCRE2_EXTENDED_MORE) != 0) options |= PCRE2_EXTENDED;\n\n/* Now scan the pattern */\n\nwhile (ptr < ptrend)\n  {\n  int prev_expect_cond_assert;\n  uint32_t min_repeat = 0, max_repeat = 0;\n  uint32_t set, unset, *optset;\n  uint32_t xset, xunset, *xoptset;\n  uint32_t terminator;\n  uint32_t prev_meta_quantifier;\n  BOOL prev_okquantifier;\n  PCRE2_SPTR tempptr;\n  PCRE2_SIZE offset;\n\n  if (nest_depth > cb->cx->parens_nest_limit)\n    {\n    errorcode = ERR19;\n    goto FAILED;        /* Parentheses too deeply nested */\n    }\n\n  /* Check that we haven't emitted too much into parsed_pattern. We allocate\n  a suitably-sized buffer upfront, then do unchecked writes to it. If we only\n  write a little bit too much, everything will appear to be OK, because the\n  upfront size is an overestimate... but a malicious pattern could end up\n  forcing a write past the buffer end. We must catch this during\n  development. */\n\n#ifdef PCRE2_DEBUG\n  /* Strong post-write check. Won't help in release builds - at this point\n  the write has already occurred so it's too late. However, should stop us\n  committing unsafe code. */\n  PCRE2_ASSERT((parsed_pattern - parsed_pattern_check) +\n               (parsed_pattern_extra - parsed_pattern_extra_check) <=\n                 max_parsed_pattern(ptr_check, ptr, utf, options));\n  parsed_pattern_check = parsed_pattern;\n  parsed_pattern_extra_check = parsed_pattern_extra;\n  ptr_check = ptr;\n#endif\n\n  if (parsed_pattern >= parsed_pattern_end)\n    {\n    /* Weak pre-write check; only ensures parsed_pattern[0] is writeable\n    (but the code below can write many chars). Better than nothing. */\n    PCRE2_DEBUG_UNREACHABLE();\n    errorcode = ERR63;  /* Internal error (parsed pattern overflow) */\n    goto FAILED;\n    }\n\n  /* If the last time round this loop something was added, parsed_pattern will\n  no longer be equal to this_parsed_item. Remember where the previous item\n  started and reset for the next item. Note that sometimes round the loop,\n  nothing gets added (e.g. for ignored white space). */\n\n  if (this_parsed_item != parsed_pattern)\n    {\n    prev_parsed_item = this_parsed_item;\n    this_parsed_item = parsed_pattern;\n    }\n\n  /* Get next input character, save its position for callout handling. */\n\n  thisptr = ptr;\n  GETCHARINCTEST(c, ptr);\n\n  /* Copy quoted literals until \\E, allowing for the possibility of automatic\n  callouts, except when processing a (*VERB) \"name\".  */\n\n  if (inescq)\n    {\n    if (c == CHAR_BACKSLASH && ptr < ptrend && *ptr == CHAR_E)\n      {\n      inescq = FALSE;\n      ptr++;   /* Skip E */\n      }\n    else\n      {\n      if (expect_cond_assert > 0)   /* A literal is not allowed if we are */\n        {                           /* expecting a conditional assertion, */\n        ptr--;                      /* but an empty \\Q\\E sequence is OK.  */\n        errorcode = ERR28;\n        goto FAILED;\n        }\n      if (inverbname)\n        {                          /* Don't use PARSED_LITERAL() because it */\n#if PCRE2_CODE_UNIT_WIDTH == 32    /* sets okquantifier. */\n        if (c >= META_END) *parsed_pattern++ = META_BIGVALUE;\n#endif\n        *parsed_pattern++ = c;\n        }\n      else\n        {\n        if (after_manual_callout-- <= 0)\n          parsed_pattern = manage_callouts(thisptr, &previous_callout,\n            auto_callout, parsed_pattern, cb);\n        PARSED_LITERAL(c, parsed_pattern);\n        }\n      meta_quantifier = 0;\n      }\n    continue;  /* Next character */\n    }\n\n  /* If we are processing the \"name\" part of a (*VERB:NAME) item, all\n  characters up to the closing parenthesis are literals except when\n  PCRE2_ALT_VERBNAMES is set. That causes backslash interpretation, but only \\Q\n  and \\E and escaped characters are allowed (no character types such as \\d). If\n  PCRE2_EXTENDED is also set, we must ignore white space and # comments. Do\n  this by not entering the special (*VERB:NAME) processing - they are then\n  picked up below. Note that c is a character, not a code unit, so we must not\n  use MAX_255 to test its size because MAX_255 tests code units and is assumed\n  TRUE in 8-bit mode. */\n\n  if (inverbname &&\n       (\n        /* EITHER: not both options set */\n        ((options & (PCRE2_EXTENDED | PCRE2_ALT_VERBNAMES)) !=\n                    (PCRE2_EXTENDED | PCRE2_ALT_VERBNAMES)) ||\n#ifdef SUPPORT_UNICODE\n        /* OR: character > 255 AND not Unicode Pattern White Space */\n        (c > 255 && (c|1) != 0x200f && (c|1) != 0x2029) ||\n#endif\n        /* OR: not a # comment or isspace() white space */\n        (c < 256 && c != CHAR_NUMBER_SIGN && (cb->ctypes[c] & ctype_space) == 0\n#ifdef SUPPORT_UNICODE\n        /* and not CHAR_NEL when Unicode is supported */\n          && c != CHAR_NEL\n#endif\n       )))\n    {\n    PCRE2_SIZE verbnamelength;\n\n    switch(c)\n      {\n      default:                     /* Don't use PARSED_LITERAL() because it */\n#if PCRE2_CODE_UNIT_WIDTH == 32    /* sets okquantifier. */\n      if (c >= META_END) *parsed_pattern++ = META_BIGVALUE;\n#endif\n      *parsed_pattern++ = c;\n      break;\n\n      case CHAR_RIGHT_PARENTHESIS:\n      inverbname = FALSE;\n      /* This is the length in characters */\n      verbnamelength = (PCRE2_SIZE)(parsed_pattern - verblengthptr - 1);\n      /* But the limit on the length is in code units */\n      if (ptr - verbnamestart - 1 > (int)MAX_MARK)\n        {\n        ptr--;\n        errorcode = ERR76;\n        goto FAILED;\n        }\n      *verblengthptr = (uint32_t)verbnamelength;\n\n      /* If this name was on a verb such as (*ACCEPT) which does not continue,\n      a (*MARK) was generated for the name. We now add the original verb as the\n      next item. */\n\n      if (add_after_mark != 0)\n        {\n        *parsed_pattern++ = add_after_mark;\n        add_after_mark = 0;\n        }\n      break;\n\n      case CHAR_BACKSLASH:\n      if ((options & PCRE2_ALT_VERBNAMES) != 0)\n        {\n        escape = PRIV(check_escape)(&ptr, ptrend, &c, &errorcode, options,\n          xoptions, cb->bracount, FALSE, cb);\n        if (errorcode != 0) goto FAILED;\n        }\n      else escape = 0;   /* Treat all as literal */\n\n      switch(escape)\n        {\n        case 0:                    /* Don't use PARSED_LITERAL() because it */\n#if PCRE2_CODE_UNIT_WIDTH == 32    /* sets okquantifier. */\n        if (c >= META_END) *parsed_pattern++ = META_BIGVALUE;\n#endif\n        *parsed_pattern++ = c;\n        break;\n\n        case ESC_ub:\n        *parsed_pattern++ = CHAR_u;\n        PARSED_LITERAL(CHAR_LEFT_CURLY_BRACKET, parsed_pattern);\n        break;\n\n        case ESC_Q:\n        inescq = TRUE;\n        break;\n\n        case ESC_E:           /* Ignore */\n        break;\n\n        default:\n        errorcode = ERR40;    /* Invalid in verb name */\n        goto FAILED;\n        }\n      }\n    continue;   /* Next character in pattern */\n    }\n\n  /* Not a verb name character. At this point we must process everything that\n  must not change the quantification state. This is mainly comments, but we\n  handle \\Q and \\E here as well, so that an item such as A\\Q\\E+ is treated as\n  A+, as in Perl. An isolated \\E is ignored. */\n\n  if (c == CHAR_BACKSLASH && ptr < ptrend)\n    {\n    if (*ptr == CHAR_Q || *ptr == CHAR_E)\n      {\n      inescq = *ptr == CHAR_Q;\n      ptr++;\n      continue;\n      }\n    }\n\n  /* Skip over whitespace and # comments in extended mode. Note that c is a\n  character, not a code unit, so we must not use MAX_255 to test its size\n  because MAX_255 tests code units and is assumed TRUE in 8-bit mode. The\n  whitespace characters are those designated as \"Pattern White Space\" by\n  Unicode, which are the isspace() characters plus CHAR_NEL (newline), which is\n  U+0085 in Unicode, plus U+200E, U+200F, U+2028, and U+2029. These are a\n  subset of space characters that match \\h and \\v. */\n\n  if ((options & PCRE2_EXTENDED) != 0)\n    {\n    if (c < 256 && (cb->ctypes[c] & ctype_space) != 0) continue;\n#ifdef SUPPORT_UNICODE\n    if (c == CHAR_NEL || (c|1) == 0x200f || (c|1) == 0x2029) continue;\n#endif\n    if (c == CHAR_NUMBER_SIGN)\n      {\n      while (ptr < ptrend)\n        {\n        if (IS_NEWLINE(ptr))      /* For non-fixed-length newline cases, */\n          {                       /* IS_NEWLINE sets cb->nllen. */\n          ptr += cb->nllen;\n          break;\n          }\n        ptr++;\n#ifdef SUPPORT_UNICODE\n        if (utf) FORWARDCHARTEST(ptr, ptrend);\n#endif\n        }\n      continue;  /* Next character in pattern */\n      }\n    }\n\n  /* Skip over bracketed comments */\n\n  if (c == CHAR_LEFT_PARENTHESIS && ptrend - ptr >= 2 &&\n      ptr[0] == CHAR_QUESTION_MARK && ptr[1] == CHAR_NUMBER_SIGN)\n    {\n    while (++ptr < ptrend && *ptr != CHAR_RIGHT_PARENTHESIS);\n    if (ptr >= ptrend)\n      {\n      errorcode = ERR18;  /* A special error for missing ) in a comment */\n      goto FAILED;        /* to make it easier to debug. */\n      }\n    ptr++;\n    continue;  /* Next character in pattern */\n    }\n\n  /* If the next item is not a quantifier, fill in length of any previous\n  callout and create an auto callout if required. */\n\n  if (c != CHAR_ASTERISK && c != CHAR_PLUS && c != CHAR_QUESTION_MARK &&\n       (c != CHAR_LEFT_CURLY_BRACKET ||\n         (tempptr = ptr,\n         !read_repeat_counts(&tempptr, ptrend, NULL, NULL, &errorcode))))\n    {\n    if (after_manual_callout-- <= 0)\n      {\n      parsed_pattern = manage_callouts(thisptr, &previous_callout, auto_callout,\n        parsed_pattern, cb);\n      this_parsed_item = parsed_pattern;  /* New start for current item */\n      }\n    }\n\n  /* If expect_cond_assert is 2, we have just passed (?( and are expecting an\n  assertion, possibly preceded by a callout. If the value is 1, we have just\n  had the callout and expect an assertion. There must be at least 3 more\n  characters in all cases. When expect_cond_assert is 2, we know that the\n  current character is an opening parenthesis, as otherwise we wouldn't be\n  here. However, when it is 1, we need to check, and it's easiest just to check\n  always. Note that expect_cond_assert may be negative, since all callouts just\n  decrement it. */\n\n  if (expect_cond_assert > 0)\n    {\n    BOOL ok = c == CHAR_LEFT_PARENTHESIS && ptrend - ptr >= 3 &&\n              (ptr[0] == CHAR_QUESTION_MARK || ptr[0] == CHAR_ASTERISK);\n    if (ok)\n      {\n      if (ptr[0] == CHAR_ASTERISK)  /* New alpha assertion format, possibly */\n        {\n        ok = MAX_255(ptr[1]) && (cb->ctypes[ptr[1]] & ctype_lcletter) != 0;\n        }\n      else switch(ptr[1])  /* Traditional symbolic format */\n        {\n        case CHAR_C:\n        ok = expect_cond_assert == 2;\n        break;\n\n        case CHAR_EQUALS_SIGN:\n        case CHAR_EXCLAMATION_MARK:\n        break;\n\n        case CHAR_LESS_THAN_SIGN:\n        ok = ptr[2] == CHAR_EQUALS_SIGN || ptr[2] == CHAR_EXCLAMATION_MARK;\n        break;\n\n        default:\n        ok = FALSE;\n        }\n      }\n\n    if (!ok)\n      {\n      ptr--;   /* Adjust error offset */\n      errorcode = ERR28;\n      goto FAILED;\n      }\n    }\n\n  /* Remember whether we are expecting a conditional assertion, and set the\n  default for this item. */\n\n  prev_expect_cond_assert = expect_cond_assert;\n  expect_cond_assert = 0;\n\n  /* Remember quantification status for the previous significant item, then set\n  default for this item. */\n\n  prev_okquantifier = okquantifier;\n  prev_meta_quantifier = meta_quantifier;\n  okquantifier = FALSE;\n  meta_quantifier = 0;\n\n  /* If the previous significant item was a quantifier, adjust the parsed code\n  if there is a following modifier. The base meta value is always followed by\n  the PLUS and QUERY values, in that order. We do this here rather than after\n  reading a quantifier so that intervening comments and /x whitespace can be\n  ignored without having to replicate code. */\n\n  if (prev_meta_quantifier != 0 && (c == CHAR_QUESTION_MARK || c == CHAR_PLUS))\n    {\n    parsed_pattern[(prev_meta_quantifier == META_MINMAX)? -3 : -1] =\n      prev_meta_quantifier + ((c == CHAR_QUESTION_MARK)?\n        0x00020000u : 0x00010000u);\n    continue;  /* Next character in pattern */\n    }\n\n  /* Process the next item in the main part of a pattern. */\n\n  switch(c)\n    {\n    default:              /* Non-special character */\n    PARSED_LITERAL(c, parsed_pattern);\n    break;\n\n\n    /* ---- Escape sequence ---- */\n\n    case CHAR_BACKSLASH:\n    tempptr = ptr;\n    escape = PRIV(check_escape)(&ptr, ptrend, &c, &errorcode, options,\n      xoptions, cb->bracount, FALSE, cb);\n    if (errorcode != 0)\n      {\n      ESCAPE_FAILED:\n      if ((xoptions & PCRE2_EXTRA_BAD_ESCAPE_IS_LITERAL) == 0)\n        goto FAILED;\n      ptr = tempptr;\n      if (ptr >= ptrend) c = CHAR_BACKSLASH; else\n        {\n        GETCHARINCTEST(c, ptr);   /* Get character value, increment pointer */\n        }\n      escape = 0;                 /* Treat as literal character */\n      }\n\n    /* The escape was a data escape or literal character. */\n\n    if (escape == 0)\n      {\n      PARSED_LITERAL(c, parsed_pattern);\n      }\n\n    /* The escape was a back (or forward) reference. We keep the offset in\n    order to give a more useful diagnostic for a bad forward reference. For\n    references to groups numbered less than 10 we can't use more than two items\n    in parsed_pattern because they may be just two characters in the input (and\n    in a 64-bit world an offset may need two elements). So for them, the offset\n    of the first occurrent is held in a special vector. */\n\n    else if (escape < 0)\n      {\n      offset = (PCRE2_SIZE)(ptr - cb->start_pattern - 1);\n      escape = -escape - 1;\n      *parsed_pattern++ = META_BACKREF | (uint32_t)escape;\n      if (escape < 10)\n        {\n        if (cb->small_ref_offset[escape] == PCRE2_UNSET)\n          cb->small_ref_offset[escape] = offset;\n        }\n      else\n        {\n        PUTOFFSET(offset, parsed_pattern);\n        }\n      okquantifier = TRUE;\n      }\n\n    /* The escape was a character class such as \\d etc. or other special\n    escape indicator such as \\A or \\X. Most of them generate just a single\n    parsed item, but \\P and \\p are followed by a 16-bit type and a 16-bit\n    value. They are supported only when Unicode is available. The type and\n    value are packed into a single 32-bit value so that the whole sequences\n    uses only two elements in the parsed_vector. This is because the same\n    coding is used if \\d (for example) is turned into \\p{Nd} when PCRE2_UCP is\n    set.\n\n    There are also some cases where the escape sequence is followed by a name:\n    \\k{name}, \\k<name>, and \\k'name' are backreferences by name, and \\g<name>\n    and \\g'name' are subroutine calls by name; \\g{name} is a synonym for\n    \\k{name}. Note that \\g<number> and \\g'number' are handled by check_escape()\n    and returned as a negative value (handled above). A name is coded as an\n    offset into the pattern and a length. */\n\n    else switch (escape)\n      {\n      case ESC_C:\n#ifdef NEVER_BACKSLASH_C\n      errorcode = ERR85;\n      goto ESCAPE_FAILED;\n#else\n      if ((options & PCRE2_NEVER_BACKSLASH_C) != 0)\n        {\n        errorcode = ERR83;\n        goto ESCAPE_FAILED;\n        }\n#endif\n      okquantifier = TRUE;\n      *parsed_pattern++ = META_ESCAPE + escape;\n      break;\n\n      /* This is a special return that happens only in EXTRA_ALT_BSUX mode,\n      when \\u{ is not followed by hex digits and }. It requests two literal\n      characters, u and { and we need this, as otherwise \\u{ 12} (for example)\n      would be treated as u{12} now that spaces are allowed in quantifiers. */\n\n      case ESC_ub:\n      *parsed_pattern++ = CHAR_u;\n      PARSED_LITERAL(CHAR_LEFT_CURLY_BRACKET, parsed_pattern);\n      break;\n\n      case ESC_X:\n#ifndef SUPPORT_UNICODE\n      errorcode = ERR45;   /* Supported only with Unicode support */\n      goto ESCAPE_FAILED;\n#endif\n      case ESC_H:\n      case ESC_h:\n      case ESC_N:\n      case ESC_R:\n      case ESC_V:\n      case ESC_v:\n      okquantifier = TRUE;\n      *parsed_pattern++ = META_ESCAPE + escape;\n      break;\n\n      default:  /* \\A, \\B, \\b, \\G, \\K, \\Z, \\z cannot be quantified. */\n      *parsed_pattern++ = META_ESCAPE + escape;\n      break;\n\n      /* Escapes that may change in UCP mode. */\n\n      case ESC_d:\n      case ESC_D:\n      case ESC_s:\n      case ESC_S:\n      case ESC_w:\n      case ESC_W:\n      okquantifier = TRUE;\n      parsed_pattern = handle_escdsw(escape, parsed_pattern, options,\n        xoptions);\n      break;\n\n      /* Unicode property matching */\n\n      case ESC_P:\n      case ESC_p:\n#ifdef SUPPORT_UNICODE\n        {\n        BOOL negated;\n        uint16_t ptype = 0, pdata = 0;\n        if (!get_ucp(&ptr, &negated, &ptype, &pdata, &errorcode, cb))\n          goto ESCAPE_FAILED;\n        if (negated) escape = (escape == ESC_P)? ESC_p : ESC_P;\n        *parsed_pattern++ = META_ESCAPE + escape;\n        *parsed_pattern++ = (ptype << 16) | pdata;\n        okquantifier = TRUE;\n        }\n#else\n      errorcode = ERR45;\n      goto ESCAPE_FAILED;\n#endif\n      break;  /* End \\P and \\p */\n\n      /* When \\g is used with quotes or angle brackets as delimiters, it is a\n      numerical or named subroutine call, and control comes here. When used\n      with brace delimiters it is a numerical back reference and does not come\n      here because check_escape() returns it directly as a reference. \\k is\n      always a named back reference. */\n\n      case ESC_g:\n      case ESC_k:\n      if (ptr >= ptrend || (*ptr != CHAR_LEFT_CURLY_BRACKET &&\n          *ptr != CHAR_LESS_THAN_SIGN && *ptr != CHAR_APOSTROPHE))\n        {\n        errorcode = (escape == ESC_g)? ERR57 : ERR69;\n        goto ESCAPE_FAILED;\n        }\n      terminator = (*ptr == CHAR_LESS_THAN_SIGN)?\n        CHAR_GREATER_THAN_SIGN : (*ptr == CHAR_APOSTROPHE)?\n        CHAR_APOSTROPHE : CHAR_RIGHT_CURLY_BRACKET;\n\n      /* For a non-braced \\g, check for a numerical recursion. */\n\n      if (escape == ESC_g && terminator != CHAR_RIGHT_CURLY_BRACKET)\n        {\n        PCRE2_SPTR p = ptr + 1;\n\n        if (read_number(&p, ptrend, cb->bracount, MAX_GROUP_NUMBER, ERR61, &i,\n            &errorcode))\n          {\n          if (p >= ptrend || *p != terminator)\n            {\n            errorcode = ERR57;\n            goto ESCAPE_FAILED;\n            }\n          ptr = p;\n          goto SET_RECURSION;\n          }\n        if (errorcode != 0) goto ESCAPE_FAILED;\n        }\n\n      /* Not a numerical recursion. Perl allows spaces and tabs after { and\n      before } but not for other delimiters. */\n\n      if (!read_name(&ptr, ptrend, utf, terminator, &offset, &name, &namelen,\n          &errorcode, cb)) goto ESCAPE_FAILED;\n\n      /* \\k and \\g when used with braces are back references, whereas \\g used\n      with quotes or angle brackets is a recursion */\n\n      *parsed_pattern++ =\n        (escape == ESC_k || terminator == CHAR_RIGHT_CURLY_BRACKET)?\n          META_BACKREF_BYNAME : META_RECURSE_BYNAME;\n      *parsed_pattern++ = namelen;\n\n      PUTOFFSET(offset, parsed_pattern);\n      okquantifier = TRUE;\n      break;  /* End special escape processing */\n      }\n    break;    /* End escape sequence processing */\n\n\n    /* ---- Single-character special items ---- */\n\n    case CHAR_CIRCUMFLEX_ACCENT:\n    *parsed_pattern++ = META_CIRCUMFLEX;\n    break;\n\n    case CHAR_DOLLAR_SIGN:\n    *parsed_pattern++ = META_DOLLAR;\n    break;\n\n    case CHAR_DOT:\n    *parsed_pattern++ = META_DOT;\n    okquantifier = TRUE;\n    break;\n\n\n    /* ---- Single-character quantifiers ---- */\n\n    case CHAR_ASTERISK:\n    meta_quantifier = META_ASTERISK;\n    goto CHECK_QUANTIFIER;\n\n    case CHAR_PLUS:\n    meta_quantifier = META_PLUS;\n    goto CHECK_QUANTIFIER;\n\n    case CHAR_QUESTION_MARK:\n    meta_quantifier = META_QUERY;\n    goto CHECK_QUANTIFIER;\n\n\n    /* ---- Potential {n,m} quantifier ---- */\n\n    case CHAR_LEFT_CURLY_BRACKET:\n    if (!read_repeat_counts(&ptr, ptrend, &min_repeat, &max_repeat,\n        &errorcode))\n      {\n      if (errorcode != 0) goto FAILED;     /* Error in quantifier. */\n      PARSED_LITERAL(c, parsed_pattern);   /* Not a quantifier */\n      break;                               /* No more quantifier processing */\n      }\n    meta_quantifier = META_MINMAX;\n    /* Fall through */\n\n\n    /* ---- Quantifier post-processing ---- */\n\n    /* Check that a quantifier is allowed after the previous item. This\n    guarantees that there is a previous item. */\n\n    CHECK_QUANTIFIER:\n    if (!prev_okquantifier)\n      {\n      errorcode = ERR9;\n      goto FAILED_BACK;  // TODO https://github.com/PCRE2Project/pcre2/issues/549\n      }\n\n    /* Most (*VERB)s are not allowed to be quantified, but an ungreedy\n    quantifier can be useful for (*ACCEPT) - meaning \"succeed on backtrack\", a\n    sort of negated (*COMMIT). We therefore allow (*ACCEPT) to be quantified by\n    wrapping it in non-capturing brackets, but we have to allow for a preceding\n    (*MARK) for when (*ACCEPT) has an argument. */\n\n    if (*prev_parsed_item == META_ACCEPT)\n      {\n      uint32_t *p;\n      for (p = parsed_pattern - 1; p >= verbstartptr; p--) p[1] = p[0];\n      *verbstartptr = META_NOCAPTURE;\n      parsed_pattern[1] = META_KET;\n      parsed_pattern += 2;\n\n#ifdef PCRE2_DEBUG\n      PCRE2_ASSERT(parsed_pattern_extra >= 2);\n      parsed_pattern_extra -= 2;\n#endif\n      }\n\n    /* Now we can put the quantifier into the parsed pattern vector. At this\n    stage, we have only the basic quantifier. The check for a following + or ?\n    modifier happens at the top of the loop, after any intervening comments\n    have been removed. */\n\n    *parsed_pattern++ = meta_quantifier;\n    if (c == CHAR_LEFT_CURLY_BRACKET)\n      {\n      *parsed_pattern++ = min_repeat;\n      *parsed_pattern++ = max_repeat;\n      }\n    break;\n\n\n    /* ---- Character class ---- */\n\n    case CHAR_LEFT_SQUARE_BRACKET:\n\n    /* In another (POSIX) regex library, the ugly syntax [[:<:]] and [[:>:]] is\n    used for \"start of word\" and \"end of word\". As these are otherwise illegal\n    sequences, we don't break anything by recognizing them. They are replaced\n    by \\b(?=\\w) and \\b(?<=\\w) respectively. Sequences like [a[:<:]] are\n    erroneous and are handled by the normal code below. */\n\n    if (ptrend - ptr >= 6 &&\n         (PRIV(strncmp_c8)(ptr, STRING_WEIRD_STARTWORD, 6) == 0 ||\n          PRIV(strncmp_c8)(ptr, STRING_WEIRD_ENDWORD, 6) == 0))\n      {\n      *parsed_pattern++ = META_ESCAPE + ESC_b;\n\n      if (ptr[2] == CHAR_LESS_THAN_SIGN)\n        {\n        *parsed_pattern++ = META_LOOKAHEAD;\n        }\n      else\n        {\n        *parsed_pattern++ = META_LOOKBEHIND;\n        *has_lookbehind = TRUE;\n\n        /* The offset is used only for the \"non-fixed length\" error; this won't\n        occur here, so just store zero. */\n\n        PUTOFFSET((PCRE2_SIZE)0, parsed_pattern);\n        }\n\n      if ((options & PCRE2_UCP) == 0)\n        *parsed_pattern++ = META_ESCAPE + ESC_w;\n      else\n        {\n        *parsed_pattern++ = META_ESCAPE + ESC_p;\n        *parsed_pattern++ = PT_WORD << 16;\n        }\n      *parsed_pattern++ = META_KET;\n      ptr += 6;\n      okquantifier = TRUE;\n      break;\n      }\n\n    /* PCRE supports POSIX class stuff inside a class. Perl gives an error if\n    they are encountered at the top level, so we'll do that too. */\n\n    if (ptr < ptrend && (*ptr == CHAR_COLON || *ptr == CHAR_DOT ||\n         *ptr == CHAR_EQUALS_SIGN) &&\n        check_posix_syntax(ptr, ptrend, &tempptr))\n      {\n      errorcode = (*ptr-- == CHAR_COLON)? ERR12 : ERR13;\n      goto FAILED;\n      }\n\n    class_mode_state = ((options & PCRE2_ALT_EXTENDED_CLASS) != 0)?\n        CLASS_MODE_ALT_EXT : CLASS_MODE_NORMAL;\n\n    /* Jump here from '(?[...])'. That jump must initialize class_mode_state,\n    set c to the '[' character, and ptr to just after the '['. */\n\n    FROM_PERL_EXTENDED_CLASS:\n    okquantifier = TRUE;\n\n    /* In an EBCDIC environment, Perl treats alphabetic ranges specially\n    because there are holes in the encoding, and simply using the range A-Z\n    (for example) would include the characters in the holes. This applies only\n    to ranges where both values are literal; [\\xC1-\\xE9] is different to [A-Z]\n    in this respect. In order to accommodate this, we keep track of whether\n    character values are literal or not, and a state variable for handling\n    ranges. */\n\n    /* Loop for the contents of the class. Classes may be nested, if\n    PCRE2_ALT_EXTENDED_CLASS is set, or the class is of the form (?[...]). */\n\n    /* c is still set to '[' so the loop will handle the start of the class. */\n\n    class_depth_m1 = -1;\n    class_maxdepth_m1 = -1;\n    class_range_state = RANGE_NO;\n    class_op_state = CLASS_OP_EMPTY;\n    class_start = NULL;\n\n    for (;;)\n      {\n      BOOL char_is_literal = TRUE;\n\n      /* Inside \\Q...\\E everything is literal except \\E */\n\n      if (inescq)\n        {\n        if (c == CHAR_BACKSLASH && ptr < ptrend && *ptr == CHAR_E)\n          {\n          inescq = FALSE;                   /* Reset literal state */\n          ptr++;                            /* Skip the 'E' */\n          goto CLASS_CONTINUE;\n          }\n\n        /* Surprisingly, you cannot use \\Q..\\E to escape a character inside a\n        Perl extended class. However, empty \\Q\\E sequences are allowed, so here\n        were're only giving an error if the \\Q..\\E is non-empty. */\n\n        if (class_mode_state == CLASS_MODE_PERL_EXT)\n          {\n          errorcode = ERR116;\n          goto FAILED;\n          }\n\n        goto CLASS_LITERAL;\n        }\n\n      /* Skip over space and tab (only) in extended-more mode, or anywhere\n      inside a Perl extended class (which implies /xx). */\n\n      if ((c == CHAR_SPACE || c == CHAR_HT) &&\n          ((options & PCRE2_EXTENDED_MORE) != 0 ||\n           class_mode_state >= CLASS_MODE_PERL_EXT))\n        goto CLASS_CONTINUE;\n\n      /* Handle POSIX class names. Perl allows a negation extension of the\n      form [:^name:]. A square bracket that doesn't match the syntax is\n      treated as a literal. We also recognize the POSIX constructions\n      [.ch.] and [=ch=] (\"collating elements\") and fault them, as Perl\n      5.6 and 5.8 do. */\n\n      if (class_depth_m1 >= 0 &&\n          c == CHAR_LEFT_SQUARE_BRACKET &&\n          ptrend - ptr >= 3 &&\n          (*ptr == CHAR_COLON || *ptr == CHAR_DOT ||\n           *ptr == CHAR_EQUALS_SIGN) &&\n          check_posix_syntax(ptr, ptrend, &tempptr))\n        {\n        BOOL posix_negate = FALSE;\n        int posix_class;\n\n        /* Perl treats a hyphen before a POSIX class as a literal, not the\n        start of a range. However, it gives a warning in its warning mode. PCRE\n        does not have a warning mode, so we give an error, because this is\n        likely an error on the user's part. */\n\n        if (class_range_state == RANGE_STARTED)\n          {\n          ptr = tempptr + 2;\n          errorcode = ERR50;\n          goto FAILED;\n          }\n\n        /* Perl treats a hyphen after a POSIX class as a literal, not the\n        start of a range. However, it gives a warning in its warning mode\n        unless the hyphen is the last character in the class. PCRE does not\n        have a warning mode, so we give an error, because this is likely an\n        error on the user's part.\n\n        Roll back to the hyphen for the error position. */\n\n        if (class_range_state == RANGE_FORBID_STARTED)\n          {\n          ptr = class_range_forbid_ptr;\n          errorcode = ERR50;\n          goto FAILED;\n          }\n\n        /* Disallow implicit union in Perl extended classes. */\n\n        if (class_op_state == CLASS_OP_OPERAND &&\n            class_mode_state == CLASS_MODE_PERL_EXT)\n          {\n          ptr = tempptr + 2;\n          errorcode = ERR113;\n          goto FAILED;\n          }\n\n        if (*ptr != CHAR_COLON)\n          {\n          ptr = tempptr + 2;\n          errorcode = ERR13;\n          goto FAILED;\n          }\n\n        if (*(++ptr) == CHAR_CIRCUMFLEX_ACCENT)\n          {\n          posix_negate = TRUE;\n          ptr++;\n          }\n\n        posix_class = check_posix_name(ptr, (int)(tempptr - ptr));\n        ptr = tempptr + 2;\n        if (posix_class < 0)\n          {\n          errorcode = ERR30;\n          goto FAILED;\n          }\n\n        /* Set \"a hyphen is forbidden to be the start of a range\". For the '-]'\n        case, the hyphen is treated as a literal, but for '-1' it is disallowed\n        (because it would be interpreted as range). */\n\n        class_range_state = RANGE_FORBID_NO;\n        class_op_state = CLASS_OP_OPERAND;\n\n        /* When PCRE2_UCP is set, unless PCRE2_EXTRA_ASCII_POSIX is set, some\n        of the POSIX classes are converted to use Unicode properties \\p or \\P\n        or, in one case, \\h or \\H. The substitutes table has two values per\n        class, containing the type and value of a \\p or \\P item. The special\n        cases are specified with a negative type: a non-zero value causes \\h or\n        \\H to be used, and a zero value falls through to behave like a non-UCP\n        POSIX class. There are now also some extra options that force ASCII for\n        some classes. */\n\n#ifdef SUPPORT_UNICODE\n        if ((options & PCRE2_UCP) != 0 &&\n            (xoptions & PCRE2_EXTRA_ASCII_POSIX) == 0 &&\n            !((xoptions & PCRE2_EXTRA_ASCII_DIGIT) != 0 &&\n              (posix_class == PC_DIGIT || posix_class == PC_XDIGIT)))\n          {\n          int ptype = posix_substitutes[2*posix_class];\n          int pvalue = posix_substitutes[2*posix_class + 1];\n\n          if (ptype >= 0)\n            {\n            *parsed_pattern++ = META_ESCAPE + (posix_negate? ESC_P : ESC_p);\n            *parsed_pattern++ = (ptype << 16) | pvalue;\n            goto CLASS_CONTINUE;\n            }\n\n          if (pvalue != 0)\n            {\n            *parsed_pattern++ = META_ESCAPE + (posix_negate? ESC_H : ESC_h);\n            goto CLASS_CONTINUE;\n            }\n\n          /* Fall through */\n          }\n#endif  /* SUPPORT_UNICODE */\n\n        /* Non-UCP POSIX class */\n\n        *parsed_pattern++ = posix_negate? META_POSIX_NEG : META_POSIX;\n        *parsed_pattern++ = posix_class;\n        }\n\n      /* Check for the start of the outermost class, or the start of a nested class. */\n\n      else if ((c == CHAR_LEFT_SQUARE_BRACKET &&\n                (class_depth_m1 < 0 || class_mode_state == CLASS_MODE_ALT_EXT ||\n                 class_mode_state == CLASS_MODE_PERL_EXT)) ||\n               (c == CHAR_LEFT_PARENTHESIS &&\n                class_mode_state == CLASS_MODE_PERL_EXT))\n        {\n        uint32_t start_c = c;\n        uint32_t new_class_mode_state;\n\n        /* Update the class mode, if moving into a 'leaf' inside a Perl extended\n        class. */\n\n        if (start_c == CHAR_LEFT_SQUARE_BRACKET &&\n            class_mode_state == CLASS_MODE_PERL_EXT && class_depth_m1 >= 0)\n          new_class_mode_state = CLASS_MODE_PERL_EXT_LEAF;\n        else\n          new_class_mode_state = class_mode_state;\n\n        /* Tidy up the other class before starting the nested class. */\n        /* -[ beginning a nested class is a literal '-' */\n\n        if (class_range_state == RANGE_STARTED)\n          parsed_pattern[-1] = CHAR_MINUS;\n\n        /* Disallow implicit union in Perl extended classes. */\n\n        if (class_op_state == CLASS_OP_OPERAND &&\n            class_mode_state == CLASS_MODE_PERL_EXT)\n          {\n          errorcode = ERR113;\n          goto FAILED;\n          }\n\n        /* Validate nesting depth */\n        if (class_depth_m1 >= ECLASS_NEST_LIMIT - 1)\n          {\n          errorcode = ERR107;\n          goto FAILED;        /* Classes too deeply nested */\n          }\n\n        /* Process the character class start. If the first character is '^', set\n        the negation flag. If the first few characters (either before or after ^)\n        are \\Q\\E or \\E or space or tab in extended-more mode, we skip them too.\n        This makes for compatibility with Perl. */\n\n        negate_class = FALSE;\n        for (;;)\n          {\n          if (ptr >= ptrend)\n            {\n            if (start_c == CHAR_LEFT_PARENTHESIS)\n              errorcode = ERR14;  /* Missing terminating ')' */\n            else\n              errorcode = ERR6;   /* Missing terminating ']' */\n            goto FAILED;\n            }\n\n          GETCHARINCTEST(c, ptr);\n          if (new_class_mode_state == CLASS_MODE_PERL_EXT) break;\n          else if (c == CHAR_BACKSLASH)\n            {\n            if (ptr < ptrend && *ptr == CHAR_E) ptr++;\n            else if (ptrend - ptr >= 3 &&\n                PRIV(strncmp_c8)(ptr, STR_Q STR_BACKSLASH STR_E, 3) == 0)\n              ptr += 3;\n            else\n              break;\n            }\n          else if ((c == CHAR_SPACE || c == CHAR_HT) &&  /* Note: just these two */\n                   ((options & PCRE2_EXTENDED_MORE) != 0 ||\n                    new_class_mode_state >= CLASS_MODE_PERL_EXT))\n            continue;\n          else if (!negate_class && c == CHAR_CIRCUMFLEX_ACCENT)\n            negate_class = TRUE;\n          else break;\n          }\n\n        /* Now the real contents of the class; c has the first \"real\" character.\n        Empty classes are permitted only if the option is set, and if it's not\n        a Perl-extended class. */\n\n        if (c == CHAR_RIGHT_SQUARE_BRACKET &&\n            (cb->external_options & PCRE2_ALLOW_EMPTY_CLASS) != 0 &&\n            new_class_mode_state < CLASS_MODE_PERL_EXT)\n          {\n          PCRE2_ASSERT(start_c == CHAR_LEFT_SQUARE_BRACKET);\n\n          if (class_start != NULL)\n            {\n            PCRE2_ASSERT(class_depth_m1 >= 0);\n            /* Represents that the class is an extended class. */\n            *class_start |= CLASS_IS_ECLASS;\n            class_start = NULL;\n            }\n\n          *parsed_pattern++ = negate_class? META_CLASS_EMPTY_NOT : META_CLASS_EMPTY;\n\n          /* Leave nesting depth unchanged; but check for zero depth to handle the\n          very first (top-level) class being empty. */\n          if (class_depth_m1 < 0) break;\n\n          class_range_state = RANGE_NO; /* for processing the containing class */\n          class_op_state = CLASS_OP_OPERAND;\n          goto CLASS_CONTINUE;\n          }\n\n        /* Enter a non-empty class. */\n\n        if (class_start != NULL)\n          {\n          PCRE2_ASSERT(class_depth_m1 >= 0);\n          /* Represents that the class is an extended class. */\n          *class_start |= CLASS_IS_ECLASS;\n          class_start = NULL;\n          }\n\n        class_start = parsed_pattern;\n        *parsed_pattern++ = negate_class? META_CLASS_NOT : META_CLASS;\n        class_range_state = RANGE_NO;\n        class_op_state = CLASS_OP_EMPTY;\n        class_mode_state = new_class_mode_state;\n        ++class_depth_m1;\n        if (class_maxdepth_m1 < class_depth_m1)\n          class_maxdepth_m1 = class_depth_m1;\n        /* Reset; no op seen yet at new depth. */\n        cb->class_op_used[class_depth_m1] = 0;\n\n        /* Implement the special start-of-class literal meaning of ']'. */\n        if (c == CHAR_RIGHT_SQUARE_BRACKET &&\n            new_class_mode_state != CLASS_MODE_PERL_EXT)\n          {\n          class_range_state = RANGE_OK_LITERAL;\n          class_op_state = CLASS_OP_OPERAND;\n          PARSED_LITERAL(c, parsed_pattern);\n          goto CLASS_CONTINUE;\n          }\n\n        continue;  /* We have already loaded c with the next character */\n        }\n\n      /* Check for the end of the class. */\n\n      else if (c == CHAR_RIGHT_SQUARE_BRACKET ||\n               (c == CHAR_RIGHT_PARENTHESIS && class_mode_state == CLASS_MODE_PERL_EXT))\n        {\n        /* In Perl extended mode, the ']' can only be used to match the\n        opening '[', and ')' must match an opening parenthesis. */\n        if (class_mode_state == CLASS_MODE_PERL_EXT)\n          {\n          if (c == CHAR_RIGHT_SQUARE_BRACKET && class_depth_m1 != 0)\n            {\n            errorcode = ERR14;\n            goto FAILED_BACK;\n            }\n          if (c == CHAR_RIGHT_PARENTHESIS && class_depth_m1 < 1)\n            {\n            errorcode = ERR22;\n            goto FAILED;\n            }\n          }\n\n        /* Check no trailing operator. */\n        if (class_op_state == CLASS_OP_OPERATOR)\n          {\n          errorcode = ERR110;\n          goto FAILED;\n          }\n\n        /* Check no empty expression for Perl extended expressions. */\n        if (class_mode_state == CLASS_MODE_PERL_EXT &&\n            class_op_state == CLASS_OP_EMPTY)\n          {\n          errorcode = ERR114;\n          goto FAILED;\n          }\n\n        /* -] at the end of a class is a literal '-' */\n        if (class_range_state == RANGE_STARTED)\n          parsed_pattern[-1] = CHAR_MINUS;\n\n        *parsed_pattern++ = META_CLASS_END;\n\n        if (--class_depth_m1 < 0)\n          {\n          /* Check for and consume ')' after '(?[...]'. */\n          PCRE2_ASSERT(class_mode_state != CLASS_MODE_PERL_EXT_LEAF);\n          if (class_mode_state == CLASS_MODE_PERL_EXT)\n            {\n            if (ptr >= ptrend || *ptr != CHAR_RIGHT_PARENTHESIS)\n              {\n              errorcode = ERR115;\n              goto FAILED;\n              }\n\n            ptr++;\n            }\n\n          break;\n          }\n\n        class_range_state = RANGE_NO; /* for processing the containing class */\n        class_op_state = CLASS_OP_OPERAND;\n        if (class_mode_state == CLASS_MODE_PERL_EXT_LEAF)\n          class_mode_state = CLASS_MODE_PERL_EXT;\n        /* The extended class flag has already\n        been set for the parent class. */\n        class_start = NULL;\n        }\n\n      /* Handle a Perl set binary operator */\n\n      else if (class_mode_state == CLASS_MODE_PERL_EXT &&\n               (c == CHAR_PLUS || c == CHAR_VERTICAL_LINE || c == CHAR_MINUS ||\n                c == CHAR_AMPERSAND || c == CHAR_CIRCUMFLEX_ACCENT))\n        {\n        /* Check that there was a preceding operand. */\n        if (class_op_state != CLASS_OP_OPERAND)\n          {\n          errorcode = ERR109;\n          goto FAILED;\n          }\n\n        if (class_start != NULL)\n          {\n          PCRE2_ASSERT(class_depth_m1 >= 0);\n          /* Represents that the class is an extended class. */\n          *class_start |= CLASS_IS_ECLASS;\n          class_start = NULL;\n          }\n\n        PCRE2_ASSERT(class_range_state != RANGE_STARTED &&\n                     class_range_state != RANGE_FORBID_STARTED);\n\n        *parsed_pattern++ = c == CHAR_PLUS? META_ECLASS_OR :\n                            c == CHAR_VERTICAL_LINE? META_ECLASS_OR :\n                            c == CHAR_MINUS? META_ECLASS_SUB :\n                            c == CHAR_AMPERSAND? META_ECLASS_AND :\n                            META_ECLASS_XOR;\n        class_range_state = RANGE_NO;\n        class_op_state = CLASS_OP_OPERATOR;\n        }\n\n      /* Handle a Perl set unary operator */\n\n      else if (class_mode_state == CLASS_MODE_PERL_EXT &&\n               c == CHAR_EXCLAMATION_MARK)\n        {\n        /* Check that the \"!\" has not got a preceding operand (i.e. it's the\n        start of the class, or follows an operator). */\n        if (class_op_state == CLASS_OP_OPERAND)\n          {\n          errorcode = ERR113;\n          goto FAILED;\n          }\n\n        if (class_start != NULL)\n          {\n          PCRE2_ASSERT(class_depth_m1 >= 0);\n          /* Represents that the class is an extended class. */\n          *class_start |= CLASS_IS_ECLASS;\n          class_start = NULL;\n          }\n\n        PCRE2_ASSERT(class_range_state != RANGE_STARTED &&\n                     class_range_state != RANGE_FORBID_STARTED);\n\n        *parsed_pattern++ = META_ECLASS_NOT;\n        class_range_state = RANGE_NO;\n        class_op_state = CLASS_OP_OPERATOR;\n        }\n\n      /* Handle a UTS#18 set operator */\n\n      else if (class_mode_state == CLASS_MODE_ALT_EXT &&\n               (c == CHAR_VERTICAL_LINE || c == CHAR_MINUS ||\n                c == CHAR_AMPERSAND || c == CHAR_TILDE) &&\n               ptr < ptrend && *ptr == c)\n        {\n        ++ptr;\n\n        /* Check there isn't a triple-repetition. */\n        if (ptr < ptrend && *ptr == c)\n          {\n          while (ptr < ptrend && *ptr == c) ++ptr;  /* Improve error offset. */\n          errorcode = ERR108;\n          goto FAILED;\n          }\n\n        /* Check for a preceding operand. */\n        if (class_op_state != CLASS_OP_OPERAND)\n          {\n          errorcode = ERR109;\n          goto FAILED;\n          }\n\n        /* Check for mixed precedence. Forbid [A--B&&C]. */\n        if (cb->class_op_used[class_depth_m1] != 0 &&\n            cb->class_op_used[class_depth_m1] != (uint8_t)c)\n          {\n          errorcode = ERR111;\n          goto FAILED;\n          }\n\n        if (class_start != NULL)\n          {\n          PCRE2_ASSERT(class_depth_m1 >= 0);\n          /* Represents that the class is an extended class. */\n          *class_start |= CLASS_IS_ECLASS;\n          class_start = NULL;\n          }\n\n        /* Dangling '-' before an operator is a literal */\n        if (class_range_state == RANGE_STARTED)\n          parsed_pattern[-1] = CHAR_MINUS;\n\n        *parsed_pattern++ = c == CHAR_VERTICAL_LINE? META_ECLASS_OR :\n                            c == CHAR_MINUS? META_ECLASS_SUB :\n                            c == CHAR_AMPERSAND? META_ECLASS_AND :\n                            META_ECLASS_XOR;\n        class_range_state = RANGE_NO;\n        class_op_state = CLASS_OP_OPERATOR;\n        cb->class_op_used[class_depth_m1] = (uint8_t)c;\n        }\n\n      /* Handle escapes in a class */\n\n      else if (c == CHAR_BACKSLASH)\n        {\n        tempptr = ptr;\n        escape = PRIV(check_escape)(&ptr, ptrend, &c, &errorcode, options,\n          xoptions, cb->bracount, TRUE, cb);\n\n        if (errorcode != 0)\n          {\n          if ((xoptions & PCRE2_EXTRA_BAD_ESCAPE_IS_LITERAL) == 0 ||\n              class_mode_state >= CLASS_MODE_PERL_EXT)\n            goto FAILED;\n          ptr = tempptr;\n          if (ptr >= ptrend) c = CHAR_BACKSLASH; else\n            {\n            GETCHARINCTEST(c, ptr);   /* Get character value, increment pointer */\n            }\n          escape = 0;                 /* Treat as literal character */\n          }\n\n        switch(escape)\n          {\n          case 0:  /* Escaped character code point is in c */\n          char_is_literal = FALSE;\n          goto CLASS_LITERAL;      /* (a few lines above) */\n\n          case ESC_b:\n          c = CHAR_BS;    /* \\b is backspace in a class */\n          char_is_literal = FALSE;\n          goto CLASS_LITERAL;\n\n          case ESC_k:\n          c = CHAR_k;     /* \\k is not special in a class, just like \\g */\n          char_is_literal = FALSE;\n          goto CLASS_LITERAL;\n\n          case ESC_Q:\n          inescq = TRUE;  /* Enter literal mode */\n          goto CLASS_CONTINUE;\n\n          case ESC_E:     /* Ignore orphan \\E */\n          goto CLASS_CONTINUE;\n\n          case ESC_B:     /* Always an error in a class */\n          case ESC_R:\n          case ESC_X:\n          errorcode = ERR7;\n          ptr--;  // TODO https://github.com/PCRE2Project/pcre2/issues/549\n          goto FAILED;\n\n          case ESC_N:     /* Not permitted by Perl either */\n          errorcode = ERR71;\n          goto FAILED;\n\n          case ESC_H:\n          case ESC_h:\n          case ESC_V:\n          case ESC_v:\n          *parsed_pattern++ = META_ESCAPE + escape;\n          break;\n\n          /* These escapes may be converted to Unicode property tests when\n          PCRE2_UCP is set. */\n\n          case ESC_d:\n          case ESC_D:\n          case ESC_s:\n          case ESC_S:\n          case ESC_w:\n          case ESC_W:\n          parsed_pattern = handle_escdsw(escape, parsed_pattern, options,\n            xoptions);\n          break;\n\n          /* Explicit Unicode property matching */\n\n          case ESC_P:\n          case ESC_p:\n#ifdef SUPPORT_UNICODE\n            {\n            BOOL negated;\n            uint16_t ptype = 0, pdata = 0;\n            if (!get_ucp(&ptr, &negated, &ptype, &pdata, &errorcode, cb))\n              goto FAILED;\n\n            /* In caseless matching, particular characteristics Lu, Ll, and Lt\n            get converted to the general characteristic L&. That is, upper,\n            lower, and title case letters are all conflated. */\n\n            if ((options & PCRE2_CASELESS) != 0 && ptype == PT_PC &&\n                (pdata == ucp_Lu || pdata == ucp_Ll || pdata == ucp_Lt))\n              {\n              ptype = PT_LAMP;\n              pdata = 0;\n              }\n\n            if (negated) escape = (escape == ESC_P)? ESC_p : ESC_P;\n            *parsed_pattern++ = META_ESCAPE + escape;\n            *parsed_pattern++ = (ptype << 16) | pdata;\n            }\n#else\n          errorcode = ERR45;\n          goto FAILED;\n#endif\n          break;  /* End \\P and \\p */\n\n          /* All others are not allowed in a class */\n\n          default:\n          PCRE2_DEBUG_UNREACHABLE();\n          /* Fall through */\n\n          case ESC_A:\n          case ESC_Z:\n          case ESC_z:\n          case ESC_G:\n          case ESC_K:\n          case ESC_C:\n          errorcode = ERR7;\n          ptr--;  // TODO https://github.com/PCRE2Project/pcre2/issues/549\n          goto FAILED;\n          }\n\n        /* All the switch-cases above which end in \"break\" describe a set\n        of characters. None may start a range. */\n\n        /* The second part of a range can be a single-character escape\n        sequence (detected above), but not any of the other escapes. Perl\n        treats a hyphen as a literal in such circumstances. However, in Perl's\n        warning mode, a warning is given, so PCRE now faults it, as it is\n        almost certainly a mistake on the user's part. */\n\n        if (class_range_state == RANGE_STARTED)\n          {\n          errorcode = ERR50;\n          goto FAILED;\n          }\n\n        /* Perl gives a warning unless the hyphen following a multi-character\n        escape is the last character in the class. PCRE throws an error. */\n\n        if (class_range_state == RANGE_FORBID_STARTED)\n          {\n          ptr = class_range_forbid_ptr;\n          errorcode = ERR50;\n          goto FAILED;\n          }\n\n        /* Disallow implicit union in Perl extended classes. */\n\n        if (class_op_state == CLASS_OP_OPERAND &&\n            class_mode_state == CLASS_MODE_PERL_EXT)\n          {\n          errorcode = ERR113;\n          goto FAILED;\n          }\n\n        class_range_state = RANGE_FORBID_NO;\n        class_op_state = CLASS_OP_OPERAND;\n        }\n\n      /* Forbid unescaped literals, and the special meaning of '-', inside a\n      Perl extended class. */\n\n      else if (class_mode_state == CLASS_MODE_PERL_EXT)\n        {\n        errorcode = ERR116;\n        goto FAILED;\n        }\n\n      /* Handle potential start of range */\n\n      else if (c == CHAR_MINUS && class_range_state >= RANGE_OK_ESCAPED)\n        {\n        *parsed_pattern++ = (class_range_state == RANGE_OK_LITERAL)?\n          META_RANGE_LITERAL : META_RANGE_ESCAPED;\n        class_range_state = RANGE_STARTED;\n        }\n\n      /* Handle forbidden start of range */\n\n      else if (c == CHAR_MINUS && class_range_state == RANGE_FORBID_NO)\n        {\n        *parsed_pattern++ = CHAR_MINUS;\n        class_range_state = RANGE_FORBID_STARTED;\n        class_range_forbid_ptr = ptr;\n        }\n\n      /* Handle a literal character */\n\n      else\n        {\n        CLASS_LITERAL:\n\n        /* Disallow implicit union in Perl extended classes. */\n\n        if (class_op_state == CLASS_OP_OPERAND &&\n            class_mode_state == CLASS_MODE_PERL_EXT)\n          {\n          errorcode = ERR113;\n          goto FAILED;\n          }\n\n        if (class_range_state == RANGE_STARTED)\n          {\n          if (c == parsed_pattern[-2])       /* Optimize one-char range */\n            parsed_pattern--;\n          else if (parsed_pattern[-2] > c)   /* Check range is in order */\n            {\n            errorcode = ERR8;\n            goto FAILED_BACK;  // TODO https://github.com/PCRE2Project/pcre2/issues/549\n            }\n          else\n            {\n            if (!char_is_literal && parsed_pattern[-1] == META_RANGE_LITERAL)\n              parsed_pattern[-1] = META_RANGE_ESCAPED;\n            PARSED_LITERAL(c, parsed_pattern);\n            }\n          class_range_state = RANGE_NO;\n          class_op_state = CLASS_OP_OPERAND;\n          }\n        else if (class_range_state == RANGE_FORBID_STARTED)\n          {\n          ptr = class_range_forbid_ptr;\n          errorcode = ERR50;\n          goto FAILED;\n          }\n        else  /* Potential start of range */\n          {\n          class_range_state = char_is_literal?\n            RANGE_OK_LITERAL : RANGE_OK_ESCAPED;\n          class_op_state = CLASS_OP_OPERAND;\n          PARSED_LITERAL(c, parsed_pattern);\n          }\n        }\n\n      /* Proceed to next thing in the class. */\n\n      CLASS_CONTINUE:\n      if (ptr >= ptrend)\n        {\n        if (class_mode_state == CLASS_MODE_PERL_EXT && class_depth_m1 > 0)\n          errorcode = ERR14;   /* Missing terminating ')' */\n        if (class_mode_state == CLASS_MODE_ALT_EXT &&\n            class_depth_m1 == 0 && class_maxdepth_m1 == 1)\n          errorcode = ERR112;  /* Missing terminating ']', but we saw '[ [ ]...' */\n        else\n          errorcode = ERR6;    /* Missing terminating ']' */\n        goto FAILED;\n        }\n      GETCHARINCTEST(c, ptr);\n      }     /* End of class-processing loop */\n\n    break;  /* End of character class */\n\n\n    /* ---- Opening parenthesis ---- */\n\n    case CHAR_LEFT_PARENTHESIS:\n    if (ptr >= ptrend) goto UNCLOSED_PARENTHESIS;\n\n    /* If ( is not followed by ? it is either a capture or a special verb or an\n    alpha assertion or a positive non-atomic lookahead. */\n\n    if (*ptr != CHAR_QUESTION_MARK)\n      {\n      const char *vn;\n\n      /* Handle capturing brackets (or non-capturing if auto-capture is turned\n      off). */\n\n      if (*ptr != CHAR_ASTERISK)\n        {\n        nest_depth++;\n        if ((options & PCRE2_NO_AUTO_CAPTURE) == 0)\n          {\n          if (cb->bracount >= MAX_GROUP_NUMBER)\n            {\n            errorcode = ERR97;\n            goto FAILED;\n            }\n          cb->bracount++;\n          *parsed_pattern++ = META_CAPTURE | cb->bracount;\n          }\n        else *parsed_pattern++ = META_NOCAPTURE;\n        }\n\n      /* Do nothing for (* followed by end of pattern or ) so it gives a \"bad\n      quantifier\" error rather than \"(*MARK) must have an argument\". */\n\n      else if (ptrend - ptr <= 1 || (c = ptr[1]) == CHAR_RIGHT_PARENTHESIS)\n        break;\n\n      /* Handle \"alpha assertions\" such as (*pla:...). Most of these are\n      synonyms for the historical symbolic assertions, but the script run and\n      non-atomic lookaround ones are new. They are distinguished by starting\n      with a lower case letter. Checking both ends of the alphabet makes this\n      work in all character codes. */\n\n      else if (CHMAX_255(c) && (cb->ctypes[c] & ctype_lcletter) != 0)\n        {\n        uint32_t meta;\n\n        vn = alasnames;\n        if (!read_name(&ptr, ptrend, utf, 0, &offset, &name, &namelen,\n          &errorcode, cb)) goto FAILED;\n        if (ptr >= ptrend || *ptr != CHAR_COLON)\n          {\n          errorcode = ERR95;  /* Malformed */\n          goto FAILED;\n          }\n\n        /* Scan the table of alpha assertion names */\n\n        for (i = 0; i < alascount; i++)\n          {\n          if (namelen == alasmeta[i].len &&\n              PRIV(strncmp_c8)(name, vn, namelen) == 0)\n            break;\n          vn += alasmeta[i].len + 1;\n          }\n\n        if (i >= alascount)\n          {\n          errorcode = ERR95;  /* Alpha assertion not recognized */\n          goto FAILED;\n          }\n\n        /* Check for expecting an assertion condition. If so, only atomic\n        lookaround assertions are valid. */\n\n        meta = alasmeta[i].meta;\n        if (prev_expect_cond_assert > 0 &&\n            (meta < META_LOOKAHEAD || meta > META_LOOKBEHINDNOT))\n          {\n          errorcode = ERR28;  /* Atomic assertion expected */\n          goto FAILED;\n          }\n\n        /* The lookaround alphabetic synonyms can mostly be handled by jumping\n        to the code that handles the traditional symbolic forms. */\n\n        switch(meta)\n          {\n          default:\n          PCRE2_DEBUG_UNREACHABLE();\n          errorcode = ERR89;  /* Unknown code; should never occur because */\n          goto FAILED;        /* the meta values come from a table above. */\n\n          case META_ATOMIC:\n          goto ATOMIC_GROUP;\n\n          case META_LOOKAHEAD:\n          goto POSITIVE_LOOK_AHEAD;\n\n          case META_LOOKAHEAD_NA:\n          goto POSITIVE_NONATOMIC_LOOK_AHEAD;\n\n          case META_LOOKAHEADNOT:\n          goto NEGATIVE_LOOK_AHEAD;\n\n          case META_SCS:\n          if (++ptr >= ptrend) goto UNCLOSED_PARENTHESIS;\n\n          if (*ptr != CHAR_LEFT_PARENTHESIS)\n            {\n            errorcode = ERR15;\n            goto FAILED;\n            }\n\n          ptr++;\n          *parsed_pattern++ = META_SCS;\n          /* Temporary variable, zero in the first iteration. */\n          offset = 0;\n\n          for (;;)\n            {\n            PCRE2_SIZE next_offset = (PCRE2_SIZE)(ptr - cb->start_pattern);\n\n            /* Handle (scan_substring:([+-]number)... */\n            if (read_number(&ptr, ptrend, cb->bracount, MAX_GROUP_NUMBER, ERR61,\n                &i, &errorcode))\n              {\n              PCRE2_ASSERT(i >= 0);\n              if (i <= 0)\n                {\n                errorcode = ERR15;\n                goto FAILED;\n                }\n              meta = META_SCS_NUMBER;\n              namelen = (uint32_t)i;\n              }\n            else if (errorcode != 0) goto FAILED;   /* Number too big */\n            else\n              {\n              if (ptr >= ptrend) goto UNCLOSED_PARENTHESIS;\n\n              /* Handle (*scan_substring:('name') or (*scan_substring:(<name>) */\n              if (*ptr == CHAR_LESS_THAN_SIGN)\n                terminator = CHAR_GREATER_THAN_SIGN;\n              else if (*ptr == CHAR_APOSTROPHE)\n                terminator = CHAR_APOSTROPHE;\n              else\n                {\n                errorcode = ERR15;\n                goto FAILED;\n                }\n\n              if (!read_name(&ptr, ptrend, utf, terminator, &next_offset,\n                  &name, &namelen, &errorcode, cb)) goto FAILED;\n\n              meta = META_SCS_NAME;\n              }\n\n            PCRE2_ASSERT(next_offset > 0);\n            if (offset == 0 || (next_offset - offset) >= 0x10000)\n              {\n              *parsed_pattern++ = META_OFFSET;\n              PUTOFFSET(next_offset, parsed_pattern);\n              offset = next_offset;\n              }\n\n            /* The offset is encoded as a relative offset, because for some\n            inputs such as \",2\" in (*scs:(1,2,3)...), we only have space for\n            two uint32_t values, and an opcode and absolute offset may require\n            three uint32_t values. */\n            *parsed_pattern++ = meta | (uint32_t)(next_offset - offset);\n            *parsed_pattern++ = namelen;\n            offset = next_offset;\n\n            if (ptr >= ptrend) goto UNCLOSED_PARENTHESIS;\n\n            if (*ptr == CHAR_RIGHT_PARENTHESIS) break;\n\n            if (*ptr != CHAR_COMMA)\n              {\n              errorcode = ERR24;\n              goto FAILED;\n              }\n\n            ptr++;\n            }\n          ptr++;\n          goto POST_ASSERTION;\n\n          case META_LOOKBEHIND:\n          case META_LOOKBEHINDNOT:\n          case META_LOOKBEHIND_NA:\n          *parsed_pattern++ = meta;\n          ptr--;\n          goto POST_LOOKBEHIND;\n\n          /* The script run facilities are handled here. Unicode support is\n          required (give an error if not, as this is a security issue). Always\n          record a META_SCRIPT_RUN item. Then, for the atomic version, insert\n          META_ATOMIC and remember that we need two META_KETs at the end. */\n\n          case META_SCRIPT_RUN:\n          case META_ATOMIC_SCRIPT_RUN:\n#ifdef SUPPORT_UNICODE\n          *parsed_pattern++ = META_SCRIPT_RUN;\n          nest_depth++;\n          ptr++;\n          if (meta == META_ATOMIC_SCRIPT_RUN)\n            {\n            *parsed_pattern++ = META_ATOMIC;\n            if (top_nest == NULL) top_nest = (nest_save *)(cb->start_workspace);\n            else if (++top_nest >= end_nests)\n              {\n              errorcode = ERR84;\n              goto FAILED;\n              }\n            top_nest->nest_depth = nest_depth;\n            top_nest->flags = NSF_ATOMICSR;\n            top_nest->options = options & PARSE_TRACKED_OPTIONS;\n            top_nest->xoptions = xoptions & PARSE_TRACKED_EXTRA_OPTIONS;\n\n#ifdef PCRE2_DEBUG\n            /* We'll write out two META_KETs for a single \")\" in the input\n            pattern, so we reserve space for that in our bounds check. */\n            parsed_pattern_extra++;\n#endif\n            }\n          break;\n#else  /* SUPPORT_UNICODE */\n          errorcode = ERR96;\n          goto FAILED;\n#endif\n          }\n        }\n\n\n      /* ---- Handle (*VERB) and (*VERB:NAME) ---- */\n\n      else\n        {\n        vn = verbnames;\n        if (!read_name(&ptr, ptrend, utf, 0, &offset, &name, &namelen,\n          &errorcode, cb)) goto FAILED;\n        if (ptr >= ptrend || (*ptr != CHAR_COLON &&\n                              *ptr != CHAR_RIGHT_PARENTHESIS))\n          {\n          errorcode = ERR60;  /* Malformed */\n          goto FAILED;\n          }\n\n        /* Scan the table of verb names */\n\n        for (i = 0; i < verbcount; i++)\n          {\n          if (namelen == verbs[i].len &&\n              PRIV(strncmp_c8)(name, vn, namelen) == 0)\n            break;\n          vn += verbs[i].len + 1;\n          }\n\n        if (i >= verbcount)\n          {\n          errorcode = ERR60;  /* Verb not recognized */\n          goto FAILED;\n          }\n\n        /* An empty argument is treated as no argument. */\n\n        if (*ptr == CHAR_COLON && ptr + 1 < ptrend &&\n             ptr[1] == CHAR_RIGHT_PARENTHESIS)\n          ptr++;    /* Advance to the closing parens */\n\n        /* Check for mandatory non-empty argument; this is (*MARK) */\n\n        if (verbs[i].has_arg > 0 && *ptr != CHAR_COLON)\n          {\n          errorcode = ERR66;\n          goto FAILED;\n          }\n\n        /* Remember where this verb, possibly with a preceding (*MARK), starts,\n        for handling quantified (*ACCEPT). */\n\n        verbstartptr = parsed_pattern;\n        okquantifier = (verbs[i].meta == META_ACCEPT);\n#ifdef PCRE2_DEBUG\n        /* Reserve space in our bounds check for optionally wrapping the (*ACCEPT)\n        with a non-capturing bracket, if there is a following quantifier. */\n        if (okquantifier) parsed_pattern_extra += 2;\n#endif\n\n        /* It appears that Perl allows any characters whatsoever, other than a\n        closing parenthesis, to appear in arguments (\"names\"), so we no longer\n        insist on letters, digits, and underscores. Perl does not, however, do\n        any interpretation within arguments, and has no means of including a\n        closing parenthesis. PCRE supports escape processing but only when it\n        is requested by an option. We set inverbname TRUE here, and let the\n        main loop take care of this so that escape and \\x processing is done by\n        the main code above. */\n\n        if (*ptr++ == CHAR_COLON)   /* Skip past : or ) */\n          {\n          /* Some optional arguments can be treated as a preceding (*MARK) */\n\n          if (verbs[i].has_arg < 0)\n            {\n            add_after_mark = verbs[i].meta;\n            *parsed_pattern++ = META_MARK;\n            }\n\n          /* The remaining verbs with arguments (except *MARK) need a different\n          opcode. */\n\n          else\n            {\n            *parsed_pattern++ = verbs[i].meta +\n              ((verbs[i].meta != META_MARK)? 0x00010000u:0);\n            }\n\n          /* Set up for reading the name in the main loop. */\n\n          verblengthptr = parsed_pattern++;\n          verbnamestart = ptr;\n          inverbname = TRUE;\n          }\n        else  /* No verb \"name\" argument */\n          {\n          *parsed_pattern++ = verbs[i].meta;\n          }\n        }     /* End of (*VERB) handling */\n      break;  /* Done with this parenthesis */\n      }       /* End of groups that don't start with (? */\n\n\n    /* ---- Items starting (? ---- */\n\n    /* The type of item is determined by what follows (?. Handle (?| and option\n    changes under \"default\" because both need a new block on the nest stack.\n    Comments starting with (?# are handled above. Note that there is some\n    ambiguity about the sequence (?- because if a digit follows it's a relative\n    recursion or subroutine call whereas otherwise it's an option unsetting. */\n\n    if (++ptr >= ptrend) goto UNCLOSED_PARENTHESIS;\n\n    switch(*ptr)\n      {\n      default:\n      if (*ptr == CHAR_MINUS && ptrend - ptr > 1 && IS_DIGIT(ptr[1]))\n        goto RECURSION_BYNUMBER;  /* The + case is handled by CHAR_PLUS */\n\n      /* We now have either (?| or a (possibly empty) option setting,\n      optionally followed by a non-capturing group. */\n\n      nest_depth++;\n      if (top_nest == NULL) top_nest = (nest_save *)(cb->start_workspace);\n      else if (++top_nest >= end_nests)\n        {\n        errorcode = ERR84;\n        goto FAILED;\n        }\n      top_nest->nest_depth = nest_depth;\n      top_nest->flags = 0;\n      top_nest->options = options & PARSE_TRACKED_OPTIONS;\n      top_nest->xoptions = xoptions & PARSE_TRACKED_EXTRA_OPTIONS;\n\n      /* Start of non-capturing group that resets the capture count for each\n      branch. */\n\n      if (*ptr == CHAR_VERTICAL_LINE)\n        {\n        top_nest->reset_group = (uint16_t)cb->bracount;\n        top_nest->max_group = (uint16_t)cb->bracount;\n        top_nest->flags |= NSF_RESET;\n        cb->external_flags |= PCRE2_DUPCAPUSED;\n        *parsed_pattern++ = META_NOCAPTURE;\n        ptr++;\n        }\n\n      /* Scan for options imnrsxJU to be set or unset. */\n\n      else\n        {\n        BOOL hyphenok = TRUE;\n        uint32_t oldoptions = options;\n        uint32_t oldxoptions = xoptions;\n\n        top_nest->reset_group = 0;\n        top_nest->max_group = 0;\n        set = unset = 0;\n        optset = &set;\n        xset = xunset = 0;\n        xoptset = &xset;\n\n        /* ^ at the start unsets irmnsx and disables the subsequent use of - */\n\n        if (ptr < ptrend && *ptr == CHAR_CIRCUMFLEX_ACCENT)\n          {\n          options &= ~(PCRE2_CASELESS|PCRE2_MULTILINE|PCRE2_NO_AUTO_CAPTURE|\n                       PCRE2_DOTALL|PCRE2_EXTENDED|PCRE2_EXTENDED_MORE);\n          xoptions &= ~(PCRE2_EXTRA_CASELESS_RESTRICT);\n          hyphenok = FALSE;\n          ptr++;\n          }\n\n        while (ptr < ptrend && *ptr != CHAR_RIGHT_PARENTHESIS &&\n                               *ptr != CHAR_COLON)\n          {\n          switch (*ptr++)\n            {\n            case CHAR_MINUS:\n            if (!hyphenok)\n              {\n              errorcode = ERR94;\n              ptr--;  /* Correct the offset */\n              goto FAILED;\n              }\n            optset = &unset;\n            xoptset = &xunset;\n            hyphenok = FALSE;\n            break;\n\n            /* There are some two-character sequences that start with 'a'. */\n\n            case CHAR_a:\n            if (ptr < ptrend)\n              {\n              if (*ptr == CHAR_D)\n                {\n                *xoptset |= PCRE2_EXTRA_ASCII_BSD;\n                ptr++;\n                break;\n                }\n              if (*ptr == CHAR_P)\n                {\n                *xoptset |= (PCRE2_EXTRA_ASCII_POSIX|PCRE2_EXTRA_ASCII_DIGIT);\n                ptr++;\n                break;\n                }\n              if (*ptr == CHAR_S)\n                {\n                *xoptset |= PCRE2_EXTRA_ASCII_BSS;\n                ptr++;\n                break;\n                }\n              if (*ptr == CHAR_T)\n                {\n                *xoptset |= PCRE2_EXTRA_ASCII_DIGIT;\n                ptr++;\n                break;\n                }\n              if (*ptr == CHAR_W)\n                {\n                *xoptset |= PCRE2_EXTRA_ASCII_BSW;\n                ptr++;\n                break;\n                }\n              }\n            *xoptset |= PCRE2_EXTRA_ASCII_BSD|PCRE2_EXTRA_ASCII_BSS|\n                        PCRE2_EXTRA_ASCII_BSW|\n                        PCRE2_EXTRA_ASCII_DIGIT|PCRE2_EXTRA_ASCII_POSIX;\n            break;\n\n            case CHAR_J:  /* Record that it changed in the external options */\n            *optset |= PCRE2_DUPNAMES;\n            cb->external_flags |= PCRE2_JCHANGED;\n            break;\n\n            case CHAR_i: *optset |= PCRE2_CASELESS; break;\n            case CHAR_m: *optset |= PCRE2_MULTILINE; break;\n            case CHAR_n: *optset |= PCRE2_NO_AUTO_CAPTURE; break;\n            case CHAR_r: *xoptset|= PCRE2_EXTRA_CASELESS_RESTRICT; break;\n            case CHAR_s: *optset |= PCRE2_DOTALL; break;\n            case CHAR_U: *optset |= PCRE2_UNGREEDY; break;\n\n            /* If x appears twice it sets the extended extended option. */\n\n            case CHAR_x:\n            *optset |= PCRE2_EXTENDED;\n            if (ptr < ptrend && *ptr == CHAR_x)\n              {\n              *optset |= PCRE2_EXTENDED_MORE;\n              ptr++;\n              }\n            break;\n\n            default:\n            errorcode = ERR11;\n            ptr--;    /* Correct the offset */\n            goto FAILED;\n            }\n          }\n\n        /* If we are setting extended without extended-more, ensure that any\n        existing extended-more gets unset. Also, unsetting extended must also\n        unset extended-more. */\n\n        if ((set & (PCRE2_EXTENDED|PCRE2_EXTENDED_MORE)) == PCRE2_EXTENDED ||\n            (unset & PCRE2_EXTENDED) != 0)\n          unset |= PCRE2_EXTENDED_MORE;\n\n        options = (options | set) & (~unset);\n        xoptions = (xoptions | xset) & (~xunset);\n\n        /* If the options ended with ')' this is not the start of a nested\n        group with option changes, so the options change at this level.\n        In this case, if the previous level set up a nest block, discard the\n        one we have just created. Otherwise adjust it for the previous level.\n        If the options ended with ':' we are starting a non-capturing group,\n        possibly with an options setting. */\n\n        if (ptr >= ptrend) goto UNCLOSED_PARENTHESIS;\n        if (*ptr++ == CHAR_RIGHT_PARENTHESIS)\n          {\n          nest_depth--;  /* This is not a nested group after all. */\n          if (top_nest > (nest_save *)(cb->start_workspace) &&\n              (top_nest-1)->nest_depth == nest_depth) top_nest--;\n          else top_nest->nest_depth = nest_depth;\n          }\n        else *parsed_pattern++ = META_NOCAPTURE;\n\n        /* If nothing changed, no need to record. */\n\n        if (options != oldoptions || xoptions != oldxoptions)\n          {\n          *parsed_pattern++ = META_OPTIONS;\n          *parsed_pattern++ = options;\n          *parsed_pattern++ = xoptions;\n          }\n        }     /* End options processing */\n      break;  /* End default case after (? */\n\n\n      /* ---- Python syntax support ---- */\n\n      case CHAR_P:\n      if (++ptr >= ptrend) goto UNCLOSED_PARENTHESIS;\n\n      /* (?P<name> is the same as (?<name>, which defines a named group. */\n\n      if (*ptr == CHAR_LESS_THAN_SIGN)\n        {\n        terminator = CHAR_GREATER_THAN_SIGN;\n        goto DEFINE_NAME;\n        }\n\n      /* (?P>name) is the same as (?&name), which is a recursion or subroutine\n      call. */\n\n      if (*ptr == CHAR_GREATER_THAN_SIGN) goto RECURSE_BY_NAME;\n\n      /* (?P=name) is the same as \\k<name>, a back reference by name. Anything\n      else after (?P is an error. */\n\n      if (*ptr != CHAR_EQUALS_SIGN)\n        {\n        errorcode = ERR41;\n        goto FAILED;\n        }\n      if (!read_name(&ptr, ptrend, utf, CHAR_RIGHT_PARENTHESIS, &offset, &name,\n          &namelen, &errorcode, cb)) goto FAILED;\n      *parsed_pattern++ = META_BACKREF_BYNAME;\n      *parsed_pattern++ = namelen;\n      PUTOFFSET(offset, parsed_pattern);\n      okquantifier = TRUE;\n      break;   /* End of (?P processing */\n\n\n      /* ---- Recursion/subroutine calls by number ---- */\n\n      case CHAR_R:\n      i = 0;         /* (?R) == (?R0) */\n      ptr++;\n      if (ptr >= ptrend || *ptr != CHAR_RIGHT_PARENTHESIS)\n        {\n        errorcode = ERR58;\n        goto FAILED;\n        }\n      goto SET_RECURSION;\n\n      /* An item starting (?- followed by a digit comes here via the \"default\"\n      case because (?- followed by a non-digit is an options setting. */\n\n      case CHAR_PLUS:\n      if (ptrend - ptr < 2 || !IS_DIGIT(ptr[1]))\n        {\n        errorcode = ERR29;   /* Missing number */\n        goto FAILED;\n        }\n      /* Fall through */\n\n      case CHAR_0: case CHAR_1: case CHAR_2: case CHAR_3: case CHAR_4:\n      case CHAR_5: case CHAR_6: case CHAR_7: case CHAR_8: case CHAR_9:\n      RECURSION_BYNUMBER:\n      if (!read_number(&ptr, ptrend,\n          (IS_DIGIT(*ptr))? -1:(int)(cb->bracount), /* + and - are relative */\n          MAX_GROUP_NUMBER, ERR61,\n          &i, &errorcode)) goto FAILED;\n      PCRE2_ASSERT(i >= 0);  /* NB (?0) is permitted, represented by i=0 */\n      if (ptr >= ptrend || *ptr != CHAR_RIGHT_PARENTHESIS)\n        goto UNCLOSED_PARENTHESIS;\n\n      SET_RECURSION:\n      *parsed_pattern++ = META_RECURSE | (uint32_t)i;\n      offset = (PCRE2_SIZE)(ptr - cb->start_pattern);\n      ptr++;\n      PUTOFFSET(offset, parsed_pattern);\n      okquantifier = TRUE;\n      break;  /* End of recursive call by number handling */\n\n\n      /* ---- Recursion/subroutine calls by name ---- */\n\n      case CHAR_AMPERSAND:\n      RECURSE_BY_NAME:\n      if (!read_name(&ptr, ptrend, utf, CHAR_RIGHT_PARENTHESIS, &offset, &name,\n          &namelen, &errorcode, cb)) goto FAILED;\n      *parsed_pattern++ = META_RECURSE_BYNAME;\n      *parsed_pattern++ = namelen;\n      PUTOFFSET(offset, parsed_pattern);\n      okquantifier = TRUE;\n      break;\n\n      /* ---- Callout with numerical or string argument ---- */\n\n      case CHAR_C:\n      if ((xoptions & PCRE2_EXTRA_NEVER_CALLOUT) != 0)\n        {\n        errorcode = ERR103;\n        goto FAILED;\n        }\n\n      if (++ptr >= ptrend) goto UNCLOSED_PARENTHESIS;\n\n      /* If the previous item was a condition starting (?(? an assertion,\n      optionally preceded by a callout, is expected. This is checked later on,\n      during actual compilation. However we need to identify this kind of\n      assertion in this pass because it must not be qualified. The value of\n      expect_cond_assert is set to 2 after (?(? is processed. We decrement it\n      for a callout - still leaving a positive value that identifies the\n      assertion. Multiple callouts or any other items will make it zero or\n      less, which doesn't matter because they will cause an error later. */\n\n      expect_cond_assert = prev_expect_cond_assert - 1;\n\n      /* If previous_callout is not NULL, it means this follows a previous\n      callout. If it was a manual callout, do nothing; this means its \"length\n      of next pattern item\" field will remain zero. If it was an automatic\n      callout, abolish it. */\n\n      if (previous_callout != NULL && (options & PCRE2_AUTO_CALLOUT) != 0 &&\n          previous_callout == parsed_pattern - 4 &&\n          parsed_pattern[-1] == 255)\n        parsed_pattern = previous_callout;\n\n      /* Save for updating next pattern item length, and skip one item before\n      completing. */\n\n      previous_callout = parsed_pattern;\n      after_manual_callout = 1;\n\n      /* Handle a string argument; specific delimiter is required. */\n\n      if (*ptr != CHAR_RIGHT_PARENTHESIS && !IS_DIGIT(*ptr))\n        {\n        PCRE2_SIZE calloutlength;\n        PCRE2_SPTR startptr = ptr;\n\n        delimiter = 0;\n        for (i = 0; PRIV(callout_start_delims)[i] != 0; i++)\n          {\n          if (*ptr == PRIV(callout_start_delims)[i])\n            {\n            delimiter = PRIV(callout_end_delims)[i];\n            break;\n            }\n          }\n        if (delimiter == 0)\n          {\n          errorcode = ERR82;\n          goto FAILED;\n          }\n\n        *parsed_pattern = META_CALLOUT_STRING;\n        parsed_pattern += 3;   /* Skip pattern info */\n\n        for (;;)\n          {\n          if (++ptr >= ptrend)\n            {\n            errorcode = ERR81;\n            ptr = startptr;   /* To give a more useful message */\n            goto FAILED;\n            }\n          if (*ptr == delimiter && (++ptr >= ptrend || *ptr != delimiter))\n            break;\n          }\n\n        calloutlength = (PCRE2_SIZE)(ptr - startptr);\n        if (calloutlength > UINT32_MAX)\n          {\n          errorcode = ERR72;\n          goto FAILED;\n          }\n        *parsed_pattern++ = (uint32_t)calloutlength;\n        offset = (PCRE2_SIZE)(startptr - cb->start_pattern);\n        PUTOFFSET(offset, parsed_pattern);\n        }\n\n      /* Handle a callout with an optional numerical argument, which must be\n      less than or equal to 255. A missing argument gives 0. */\n\n      else\n        {\n        int n = 0;\n        *parsed_pattern = META_CALLOUT_NUMBER;     /* Numerical callout */\n        parsed_pattern += 3;                       /* Skip pattern info */\n        while (ptr < ptrend && IS_DIGIT(*ptr))\n          {\n          n = n * 10 + (*ptr++ - CHAR_0);\n          if (n > 255)\n            {\n            errorcode = ERR38;\n            goto FAILED;\n            }\n          }\n        *parsed_pattern++ = n;\n        }\n\n      /* Both formats must have a closing parenthesis */\n\n      if (ptr >= ptrend || *ptr != CHAR_RIGHT_PARENTHESIS)\n        {\n        errorcode = ERR39;\n        goto FAILED;\n        }\n      ptr++;\n\n      /* Remember the offset to the next item in the pattern, and set a default\n      length. This should get updated after the next item is read. */\n\n      previous_callout[1] = (uint32_t)(ptr - cb->start_pattern);\n      previous_callout[2] = 0;\n      break;                  /* End callout */\n\n\n      /* ---- Conditional group ---- */\n\n      /* A condition can be an assertion, a number (referring to a numbered\n      group's having been set), a name (referring to a named group), or 'R',\n      referring to overall recursion. R<digits> and R&name are also permitted\n      for recursion state tests. Numbers may be preceded by + or - to specify a\n      relative group number.\n\n      There are several syntaxes for testing a named group: (?(name)) is used\n      by Python; Perl 5.10 onwards uses (?(<name>) or (?('name')).\n\n      There are two unfortunate ambiguities. 'R' can be the recursive thing or\n      the name 'R' (and similarly for 'R' followed by digits). 'DEFINE' can be\n      the Perl DEFINE feature or the Python named test. We look for a name\n      first; if not found, we try the other case.\n\n      For compatibility with auto-callouts, we allow a callout to be specified\n      before a condition that is an assertion. */\n\n      case CHAR_LEFT_PARENTHESIS:\n      if (++ptr >= ptrend) goto UNCLOSED_PARENTHESIS;\n      nest_depth++;\n\n      /* If the next character is ? or * there must be an assertion next\n      (optionally preceded by a callout). We do not check this here, but\n      instead we set expect_cond_assert to 2. If this is still greater than\n      zero (callouts decrement it) when the next assertion is read, it will be\n      marked as a condition that must not be repeated. A value greater than\n      zero also causes checking that an assertion (possibly with callout)\n      follows. */\n\n      if (*ptr == CHAR_QUESTION_MARK || *ptr == CHAR_ASTERISK)\n        {\n        *parsed_pattern++ = META_COND_ASSERT;\n        ptr--;   /* Pull pointer back to the opening parenthesis. */\n        expect_cond_assert = 2;\n        break;  /* End of conditional */\n        }\n\n      /* Handle (?([+-]number)... */\n\n      if (read_number(&ptr, ptrend, cb->bracount, MAX_GROUP_NUMBER, ERR61, &i,\n          &errorcode))\n        {\n        PCRE2_ASSERT(i >= 0);\n        if (i <= 0)\n          {\n          errorcode = ERR15;\n          goto FAILED;\n          }\n        *parsed_pattern++ = META_COND_NUMBER;\n        offset = (PCRE2_SIZE)(ptr - cb->start_pattern - 2);\n        PUTOFFSET(offset, parsed_pattern);\n        *parsed_pattern++ = i;\n        }\n      else if (errorcode != 0) goto FAILED;   /* Number too big */\n\n      /* No number found. Handle the special case (?(VERSION[>]=n.m)... */\n\n      else if (ptrend - ptr >= 10 &&\n               PRIV(strncmp_c8)(ptr, STRING_VERSION, 7) == 0 &&\n               ptr[7] != CHAR_RIGHT_PARENTHESIS)\n        {\n        uint32_t ge = 0;\n        int major = 0;\n        int minor = 0;\n\n        ptr += 7;\n        if (*ptr == CHAR_GREATER_THAN_SIGN)\n          {\n          ge = 1;\n          ptr++;\n          }\n\n        /* NOTE: cannot write IS_DIGIT(*(++ptr)) here because IS_DIGIT\n        references its argument twice. */\n\n        if (*ptr != CHAR_EQUALS_SIGN || (ptr++, !IS_DIGIT(*ptr)))\n          goto BAD_VERSION_CONDITION;\n\n        if (!read_number(&ptr, ptrend, -1, 1000, ERR79, &major, &errorcode))\n          goto FAILED;\n\n        if (ptr >= ptrend) goto BAD_VERSION_CONDITION;\n        if (*ptr == CHAR_DOT)\n          {\n          if (++ptr >= ptrend || !IS_DIGIT(*ptr)) goto BAD_VERSION_CONDITION;\n          minor = (*ptr++ - CHAR_0) * 10;\n          if (ptr >= ptrend) goto BAD_VERSION_CONDITION;\n          if (IS_DIGIT(*ptr)) minor += *ptr++ - CHAR_0;\n          if (ptr >= ptrend || *ptr != CHAR_RIGHT_PARENTHESIS)\n            goto BAD_VERSION_CONDITION;\n          }\n\n        *parsed_pattern++ = META_COND_VERSION;\n        *parsed_pattern++ = ge;\n        *parsed_pattern++ = major;\n        *parsed_pattern++ = minor;\n        }\n\n      /* All the remaining cases now require us to read a name. We cannot at\n      this stage distinguish ambiguous cases such as (?(R12) which might be a\n      recursion test by number or a name, because the named groups have not yet\n      all been identified. Those cases are treated as names, but given a\n      different META code. */\n\n      else\n        {\n        BOOL was_r_ampersand = FALSE;\n\n        if (*ptr == CHAR_R && ptrend - ptr > 1 && ptr[1] == CHAR_AMPERSAND)\n          {\n          terminator = CHAR_RIGHT_PARENTHESIS;\n          was_r_ampersand = TRUE;\n          ptr++;\n          }\n        else if (*ptr == CHAR_LESS_THAN_SIGN)\n          terminator = CHAR_GREATER_THAN_SIGN;\n        else if (*ptr == CHAR_APOSTROPHE)\n          terminator = CHAR_APOSTROPHE;\n        else\n          {\n          terminator = CHAR_RIGHT_PARENTHESIS;\n          ptr--;   /* Point to char before name */\n          }\n        if (!read_name(&ptr, ptrend, utf, terminator, &offset, &name, &namelen,\n            &errorcode, cb)) goto FAILED;\n\n        /* Handle (?(R&name) */\n\n        if (was_r_ampersand)\n          {\n          *parsed_pattern = META_COND_RNAME;\n          ptr--;   /* Back to closing parens */\n          }\n\n        /* Handle (?(name). If the name is \"DEFINE\" we identify it with a\n        special code. Likewise if the name consists of R followed only by\n        digits. Otherwise, handle it like a quoted name. */\n\n        else if (terminator == CHAR_RIGHT_PARENTHESIS)\n          {\n          if (namelen == 6 && PRIV(strncmp_c8)(name, STRING_DEFINE, 6) == 0)\n            *parsed_pattern = META_COND_DEFINE;\n          else\n            {\n            for (i = 1; i < (int)namelen; i++)\n              if (!IS_DIGIT(name[i])) break;\n            *parsed_pattern = (*name == CHAR_R && i >= (int)namelen)?\n              META_COND_RNUMBER : META_COND_NAME;\n            }\n          ptr--;   /* Back to closing parens */\n          }\n\n        /* Handle (?('name') or (?(<name>) */\n\n        else *parsed_pattern = META_COND_NAME;\n\n        /* All these cases except DEFINE end with the name length and offset;\n        DEFINE just has an offset (for the \"too many branches\" error). */\n\n        if (*parsed_pattern++ != META_COND_DEFINE) *parsed_pattern++ = namelen;\n        PUTOFFSET(offset, parsed_pattern);\n        }  /* End cases that read a name */\n\n      /* Check the closing parenthesis of the condition */\n\n      if (ptr >= ptrend || *ptr != CHAR_RIGHT_PARENTHESIS)\n        {\n        errorcode = ERR24;\n        goto FAILED;\n        }\n      ptr++;\n      break;  /* End of condition processing */\n\n\n      /* ---- Atomic group ---- */\n\n      case CHAR_GREATER_THAN_SIGN:\n      ATOMIC_GROUP:                          /* Come from (*atomic: */\n      *parsed_pattern++ = META_ATOMIC;\n      nest_depth++;\n      ptr++;\n      break;\n\n\n      /* ---- Lookahead assertions ---- */\n\n      case CHAR_EQUALS_SIGN:\n      POSITIVE_LOOK_AHEAD:                   /* Come from (*pla: */\n      *parsed_pattern++ = META_LOOKAHEAD;\n      ptr++;\n      goto POST_ASSERTION;\n\n      case CHAR_ASTERISK:\n      POSITIVE_NONATOMIC_LOOK_AHEAD:         /* Come from (*napla: */\n      *parsed_pattern++ = META_LOOKAHEAD_NA;\n      ptr++;\n      goto POST_ASSERTION;\n\n      case CHAR_EXCLAMATION_MARK:\n      NEGATIVE_LOOK_AHEAD:                   /* Come from (*nla: */\n      *parsed_pattern++ = META_LOOKAHEADNOT;\n      ptr++;\n      goto POST_ASSERTION;\n\n\n      /* ---- Lookbehind assertions ---- */\n\n      /* (?< followed by = or ! or * is a lookbehind assertion. Otherwise (?<\n      is the start of the name of a capturing group. */\n\n      case CHAR_LESS_THAN_SIGN:\n      if (ptrend - ptr <= 1 ||\n         (ptr[1] != CHAR_EQUALS_SIGN &&\n          ptr[1] != CHAR_EXCLAMATION_MARK &&\n          ptr[1] != CHAR_ASTERISK))\n        {\n        terminator = CHAR_GREATER_THAN_SIGN;\n        goto DEFINE_NAME;\n        }\n      *parsed_pattern++ = (ptr[1] == CHAR_EQUALS_SIGN)?\n        META_LOOKBEHIND : (ptr[1] == CHAR_EXCLAMATION_MARK)?\n        META_LOOKBEHINDNOT : META_LOOKBEHIND_NA;\n\n      POST_LOOKBEHIND:           /* Come from (*plb: (*naplb: and (*nlb: */\n      *has_lookbehind = TRUE;\n      offset = (PCRE2_SIZE)(ptr - cb->start_pattern - 2);\n      PUTOFFSET(offset, parsed_pattern);\n      ptr += 2;\n      /* Fall through */\n\n      /* If the previous item was a condition starting (?(? an assertion,\n      optionally preceded by a callout, is expected. This is checked later on,\n      during actual compilation. However we need to identify this kind of\n      assertion in this pass because it must not be qualified. The value of\n      expect_cond_assert is set to 2 after (?(? is processed. We decrement it\n      for a callout - still leaving a positive value that identifies the\n      assertion. Multiple callouts or any other items will make it zero or\n      less, which doesn't matter because they will cause an error later. */\n\n      POST_ASSERTION:\n      nest_depth++;\n      if (prev_expect_cond_assert > 0)\n        {\n        if (top_nest == NULL) top_nest = (nest_save *)(cb->start_workspace);\n        else if (++top_nest >= end_nests)\n          {\n          errorcode = ERR84;\n          goto FAILED;\n          }\n        top_nest->nest_depth = nest_depth;\n        top_nest->flags = NSF_CONDASSERT;\n        top_nest->options = options & PARSE_TRACKED_OPTIONS;\n        top_nest->xoptions = xoptions & PARSE_TRACKED_EXTRA_OPTIONS;\n        }\n      break;\n\n\n      /* ---- Define a named group ---- */\n\n      /* A named group may be defined as (?'name') or (?<name>). In the latter\n      case we jump to DEFINE_NAME from the disambiguation of (?< above with the\n      terminator set to '>'. */\n\n      case CHAR_APOSTROPHE:\n      terminator = CHAR_APOSTROPHE;    /* Terminator */\n\n      DEFINE_NAME:\n      if (!read_name(&ptr, ptrend, utf, terminator, &offset, &name, &namelen,\n          &errorcode, cb)) goto FAILED;\n\n      /* We have a name for this capturing group. It is also assigned a number,\n      which is its primary means of identification. */\n\n      if (cb->bracount >= MAX_GROUP_NUMBER)\n        {\n        errorcode = ERR97;\n        goto FAILED;\n        }\n      cb->bracount++;\n      *parsed_pattern++ = META_CAPTURE | cb->bracount;\n      nest_depth++;\n\n      /* Check not too many names */\n\n      if (cb->names_found >= MAX_NAME_COUNT)\n        {\n        errorcode = ERR49;\n        goto FAILED;\n        }\n\n      /* Adjust the entry size to accommodate the longest name found. */\n\n      if (namelen + IMM2_SIZE + 1 > cb->name_entry_size)\n        cb->name_entry_size = (uint16_t)(namelen + IMM2_SIZE + 1);\n\n      /* Scan the list to check for duplicates. For duplicate names, if the\n      number is the same, break the loop, which causes the name to be\n      discarded; otherwise, if DUPNAMES is not set, give an error.\n      If it is set, allow the name with a different number, but continue\n      scanning in case this is a duplicate with the same number. For\n      non-duplicate names, give an error if the number is duplicated. */\n\n      isdupname = FALSE;\n      ng = cb->named_groups;\n      for (i = 0; i < cb->names_found; i++, ng++)\n        {\n        if (namelen == ng->length &&\n            PRIV(strncmp)(name, ng->name, (PCRE2_SIZE)namelen) == 0)\n          {\n          if (ng->number == cb->bracount) break;\n          if ((options & PCRE2_DUPNAMES) == 0)\n            {\n            errorcode = ERR43;\n            goto FAILED;\n            }\n          isdupname = ng->isdup = TRUE;     /* Mark as a duplicate */\n          cb->dupnames = TRUE;              /* Duplicate names exist */\n          }\n        else if (ng->number == cb->bracount)\n          {\n          errorcode = ERR65;\n          goto FAILED;\n          }\n        }\n\n      if (i < cb->names_found) break;   /* Ignore duplicate with same number */\n\n      /* Increase the list size if necessary */\n\n      if (cb->names_found >= cb->named_group_list_size)\n        {\n        uint32_t newsize = cb->named_group_list_size * 2;\n        named_group *newspace =\n          cb->cx->memctl.malloc(newsize * sizeof(named_group),\n          cb->cx->memctl.memory_data);\n        if (newspace == NULL)\n          {\n          errorcode = ERR21;\n          goto FAILED;\n          }\n\n        memcpy(newspace, cb->named_groups,\n          cb->named_group_list_size * sizeof(named_group));\n        if (cb->named_group_list_size > NAMED_GROUP_LIST_SIZE)\n          cb->cx->memctl.free((void *)cb->named_groups,\n          cb->cx->memctl.memory_data);\n        cb->named_groups = newspace;\n        cb->named_group_list_size = newsize;\n        }\n\n      /* Add this name to the list */\n\n      cb->named_groups[cb->names_found].name = name;\n      cb->named_groups[cb->names_found].length = (uint16_t)namelen;\n      cb->named_groups[cb->names_found].number = cb->bracount;\n      cb->named_groups[cb->names_found].isdup = (uint16_t)isdupname;\n      cb->names_found++;\n      break;\n\n\n      /* ---- Perl extended character class ---- */\n\n      /* These are of the form '(?[...])'. We handle these via the same parser\n      that consumes ordinary '[...]' classes, but with a flag set to activate\n      the extended behaviour. */\n\n      case CHAR_LEFT_SQUARE_BRACKET:\n      class_mode_state = CLASS_MODE_PERL_EXT;\n      c = *ptr++;\n      goto FROM_PERL_EXTENDED_CLASS;\n      }        /* End of (? switch */\n    break;     /* End of ( handling */\n\n\n    /* ---- Branch terminators ---- */\n\n    /* Alternation: reset the capture count if we are in a (?| group. */\n\n    case CHAR_VERTICAL_LINE:\n    if (top_nest != NULL && top_nest->nest_depth == nest_depth &&\n        (top_nest->flags & NSF_RESET) != 0)\n      {\n      if (cb->bracount > top_nest->max_group)\n        top_nest->max_group = (uint16_t)cb->bracount;\n      cb->bracount = top_nest->reset_group;\n      }\n    *parsed_pattern++ = META_ALT;\n    break;\n\n    /* End of group; reset the capture count to the maximum if we are in a (?|\n    group and/or reset the options that are tracked during parsing. Disallow\n    quantifier for a condition that is an assertion. */\n\n    case CHAR_RIGHT_PARENTHESIS:\n    okquantifier = TRUE;\n    if (top_nest != NULL && top_nest->nest_depth == nest_depth)\n      {\n      options = (options & ~PARSE_TRACKED_OPTIONS) | top_nest->options;\n      xoptions = (xoptions & ~PARSE_TRACKED_EXTRA_OPTIONS) | top_nest->xoptions;\n      if ((top_nest->flags & NSF_RESET) != 0 &&\n          top_nest->max_group > cb->bracount)\n        cb->bracount = top_nest->max_group;\n      if ((top_nest->flags & NSF_CONDASSERT) != 0)\n        okquantifier = FALSE;\n\n      if ((top_nest->flags & NSF_ATOMICSR) != 0)\n        {\n        *parsed_pattern++ = META_KET;\n\n#ifdef PCRE2_DEBUG\n        PCRE2_ASSERT(parsed_pattern_extra > 0);\n        parsed_pattern_extra--;\n#endif\n        }\n\n      if (top_nest == (nest_save *)(cb->start_workspace)) top_nest = NULL;\n        else top_nest--;\n      }\n    if (nest_depth == 0)    /* Unmatched closing parenthesis */\n      {\n      errorcode = ERR22;\n      goto FAILED_BACK;  // TODO https://github.com/PCRE2Project/pcre2/issues/549\n      }\n    nest_depth--;\n    *parsed_pattern++ = META_KET;\n    break;\n    }  /* End of switch on pattern character */\n  }    /* End of main character scan loop */\n\n/* End of pattern reached. Check for missing ) at the end of a verb name. */\n\nif (inverbname && ptr >= ptrend)\n  {\n  errorcode = ERR60;\n  goto FAILED;\n  }\n\n\nPARSED_END:\n\nPCRE2_ASSERT((parsed_pattern - parsed_pattern_check) +\n             (parsed_pattern_extra - parsed_pattern_extra_check) <=\n               max_parsed_pattern(ptr_check, ptr, utf, options));\n\n/* Manage callout for the final item */\n\nparsed_pattern = manage_callouts(ptr, &previous_callout, auto_callout,\n  parsed_pattern, cb);\n\n/* Insert trailing items for word and line matching (features provided for the\nbenefit of pcre2grep). */\n\nif ((xoptions & PCRE2_EXTRA_MATCH_LINE) != 0)\n  {\n  *parsed_pattern++ = META_KET;\n  *parsed_pattern++ = META_DOLLAR;\n  }\nelse if ((xoptions & PCRE2_EXTRA_MATCH_WORD) != 0)\n  {\n  *parsed_pattern++ = META_KET;\n  *parsed_pattern++ = META_ESCAPE + ESC_b;\n  }\n\n/* Terminate the parsed pattern, then return success if all groups are closed.\nOtherwise we have unclosed parentheses. */\n\nif (parsed_pattern >= parsed_pattern_end)\n  {\n  PCRE2_DEBUG_UNREACHABLE();\n  errorcode = ERR63;  /* Internal error (parsed pattern overflow) */\n  goto FAILED;\n  }\n\n*parsed_pattern = META_END;\nif (nest_depth == 0) return 0;\n\nUNCLOSED_PARENTHESIS:\nerrorcode = ERR14;\n\n/* Come here for all failures. */\n\nFAILED:\ncb->erroroffset = (PCRE2_SIZE)(ptr - cb->start_pattern);\nreturn errorcode;\n\n/* Some errors need to indicate the previous character. */\n\nFAILED_BACK:\nptr--;\ngoto FAILED;\n\n/* This failure happens several times. */\n\nBAD_VERSION_CONDITION:\nerrorcode = ERR79;\ngoto FAILED;\n}\n\n\n\n/*************************************************\n*       Find first significant opcode            *\n*************************************************/\n\n/* This is called by several functions that scan a compiled expression looking\nfor a fixed first character, or an anchoring opcode etc. It skips over things\nthat do not influence this. For some calls, it makes sense to skip negative\nforward and all backward assertions, and also the \\b assertion; for others it\ndoes not.\n\nArguments:\n  code         pointer to the start of the group\n  skipassert   TRUE if certain assertions are to be skipped\n\nReturns:       pointer to the first significant opcode\n*/\n\nstatic const PCRE2_UCHAR*\nfirst_significant_code(PCRE2_SPTR code, BOOL skipassert)\n{\nfor (;;)\n  {\n  switch ((int)*code)\n    {\n    case OP_ASSERT_NOT:\n    case OP_ASSERTBACK:\n    case OP_ASSERTBACK_NOT:\n    case OP_ASSERTBACK_NA:\n    if (!skipassert) return code;\n    do code += GET(code, 1); while (*code == OP_ALT);\n    code += PRIV(OP_lengths)[*code];\n    break;\n\n    case OP_WORD_BOUNDARY:\n    case OP_NOT_WORD_BOUNDARY:\n    case OP_UCP_WORD_BOUNDARY:\n    case OP_NOT_UCP_WORD_BOUNDARY:\n    if (!skipassert) return code;\n    /* Fall through */\n\n    case OP_CALLOUT:\n    case OP_CREF:\n    case OP_DNCREF:\n    case OP_RREF:\n    case OP_DNRREF:\n    case OP_FALSE:\n    case OP_TRUE:\n    code += PRIV(OP_lengths)[*code];\n    break;\n\n    case OP_CALLOUT_STR:\n    code += GET(code, 1 + 2*LINK_SIZE);\n    break;\n\n    case OP_SKIPZERO:\n    code += 2 + GET(code, 2) + LINK_SIZE;\n    break;\n\n    case OP_COND:\n    case OP_SCOND:\n    if (code[1+LINK_SIZE] != OP_FALSE ||   /* Not DEFINE */\n        code[GET(code, 1)] != OP_KET)      /* More than one branch */\n      return code;\n    code += GET(code, 1) + 1 + LINK_SIZE;\n    break;\n\n    case OP_MARK:\n    case OP_COMMIT_ARG:\n    case OP_PRUNE_ARG:\n    case OP_SKIP_ARG:\n    case OP_THEN_ARG:\n    code += code[1] + PRIV(OP_lengths)[*code];\n    break;\n\n    default:\n    return code;\n    }\n  }\n\nPCRE2_DEBUG_UNREACHABLE(); /* Control should never reach here */\n}\n\n\n\n/*************************************************\n*    Find details of duplicate group names       *\n*************************************************/\n\n/* This is called from compile_branch() when it needs to know the index and\ncount of duplicates in the names table when processing named backreferences,\neither directly, or as conditions.\n\nArguments:\n  name          points to the name\n  length        the length of the name\n  indexptr      where to put the index\n  countptr      where to put the count of duplicates\n  errorcodeptr  where to put an error code\n  cb            the compile block\n\nReturns:        TRUE if OK, FALSE if not, error code set\n*/\n\nstatic BOOL\nfind_dupname_details(PCRE2_SPTR name, uint32_t length, int *indexptr,\n  int *countptr, int *errorcodeptr, compile_block *cb)\n{\nuint32_t i, groupnumber;\nint count;\nPCRE2_UCHAR *slot = cb->name_table;\n\n/* Find the first entry in the table */\n\nfor (i = 0; i < cb->names_found; i++)\n  {\n  if (PRIV(strncmp)(name, slot+IMM2_SIZE, length) == 0 &&\n      slot[IMM2_SIZE+length] == 0) break;\n  slot += cb->name_entry_size;\n  }\n\n/* This should not occur, because this function is called only when we know we\nhave duplicate names. Give an internal error. */\n\nif (i >= cb->names_found)\n  {\n  PCRE2_DEBUG_UNREACHABLE();\n  *errorcodeptr = ERR53;\n  cb->erroroffset = name - cb->start_pattern;\n  return FALSE;\n  }\n\n/* Record the index and then see how many duplicates there are, updating the\nbackref map and maximum back reference as we do. */\n\n*indexptr = i;\ncount = 0;\n\nfor (;;)\n  {\n  count++;\n  groupnumber = GET2(slot,0);\n  cb->backref_map |= (groupnumber < 32)? (1u << groupnumber) : 1;\n  if (groupnumber > cb->top_backref) cb->top_backref = groupnumber;\n  if (++i >= cb->names_found) break;\n  slot += cb->name_entry_size;\n  if (PRIV(strncmp)(name, slot+IMM2_SIZE, length) != 0 ||\n    (slot+IMM2_SIZE)[length] != 0) break;\n  }\n\n*countptr = count;\nreturn TRUE;\n}\n\n\n\n/*************************************************\n*           Compile one branch                   *\n*************************************************/\n\n/* Scan the parsed pattern, compiling it into the a vector of PCRE2_UCHAR. If\nthe options are changed during the branch, the pointer is used to change the\nexternal options bits. This function is used during the pre-compile phase when\nwe are trying to find out the amount of memory needed, as well as during the\nreal compile phase. The value of lengthptr distinguishes the two phases.\n\nArguments:\n  optionsptr        pointer to the option bits\n  xoptionsptr       pointer to the extra option bits\n  codeptr           points to the pointer to the current code point\n  pptrptr           points to the current parsed pattern pointer\n  errorcodeptr      points to error code variable\n  firstcuptr        place to put the first required code unit\n  firstcuflagsptr   place to put the first code unit flags\n  reqcuptr          place to put the last required code unit\n  reqcuflagsptr     place to put the last required code unit flags\n  bcptr             points to current branch chain\n  open_caps         points to current capitem\n  cb                contains pointers to tables etc.\n  lengthptr         NULL during the real compile phase\n                    points to length accumulator during pre-compile phase\n\nReturns:            0 There's been an error, *errorcodeptr is non-zero\n                   +1 Success, this branch must match at least one character\n                   -1 Success, this branch may match an empty string\n*/\n\nstatic int\ncompile_branch(uint32_t *optionsptr, uint32_t *xoptionsptr,\n  PCRE2_UCHAR **codeptr, uint32_t **pptrptr, int *errorcodeptr,\n  uint32_t *firstcuptr, uint32_t *firstcuflagsptr, uint32_t *reqcuptr,\n  uint32_t *reqcuflagsptr, branch_chain *bcptr, open_capitem *open_caps,\n  compile_block *cb, PCRE2_SIZE *lengthptr)\n{\nint bravalue = 0;\nint okreturn = -1;\nint group_return = 0;\nuint32_t repeat_min = 0, repeat_max = 0;      /* To please picky compilers */\nuint32_t greedy_default, greedy_non_default;\nuint32_t repeat_type, op_type;\nuint32_t options = *optionsptr;               /* May change dynamically */\nuint32_t xoptions = *xoptionsptr;             /* May change dynamically */\nuint32_t firstcu, reqcu;\nuint32_t zeroreqcu, zerofirstcu;\nuint32_t *pptr = *pptrptr;\nuint32_t meta, meta_arg;\nuint32_t firstcuflags, reqcuflags;\nuint32_t zeroreqcuflags, zerofirstcuflags;\nuint32_t req_caseopt, reqvary, tempreqvary;\n/* Some opcodes, such as META_SCS_NUMBER or META_SCS_NAME,\ndepends on the previous value of offset. */\nPCRE2_SIZE offset = 0;\nPCRE2_SIZE length_prevgroup = 0;\nPCRE2_UCHAR *code = *codeptr;\nPCRE2_UCHAR *last_code = code;\nPCRE2_UCHAR *orig_code = code;\nPCRE2_UCHAR *tempcode;\nPCRE2_UCHAR *previous = NULL;\nPCRE2_UCHAR op_previous;\nBOOL groupsetfirstcu = FALSE;\nBOOL had_accept = FALSE;\nBOOL matched_char = FALSE;\nBOOL previous_matched_char = FALSE;\nBOOL reset_caseful = FALSE;\n\n/* We can fish out the UTF setting once and for all into a BOOL, but we must\nnot do this for other options (e.g. PCRE2_EXTENDED) that may change dynamically\nas we process the pattern. */\n\n#ifdef SUPPORT_UNICODE\nBOOL utf = (options & PCRE2_UTF) != 0;\nBOOL ucp = (options & PCRE2_UCP) != 0;\n#else  /* No Unicode support */\nBOOL utf = FALSE;\n#endif\n\n/* Set up the default and non-default settings for greediness */\n\ngreedy_default = ((options & PCRE2_UNGREEDY) != 0);\ngreedy_non_default = greedy_default ^ 1;\n\n/* Initialize no first unit, no required unit. REQ_UNSET means \"no char\nmatching encountered yet\". It gets changed to REQ_NONE if we hit something that\nmatches a non-fixed first unit; reqcu just remains unset if we never find one.\n\nWhen we hit a repeat whose minimum is zero, we may have to adjust these values\nto take the zero repeat into account. This is implemented by setting them to\nzerofirstcu and zeroreqcu when such a repeat is encountered. The individual\nitem types that can be repeated set these backoff variables appropriately. */\n\nfirstcu = reqcu = zerofirstcu = zeroreqcu = 0;\nfirstcuflags = reqcuflags = zerofirstcuflags = zeroreqcuflags = REQ_UNSET;\n\n/* The variable req_caseopt contains either the REQ_CASELESS bit or zero,\naccording to the current setting of the caseless flag. The REQ_CASELESS value\nleaves the lower 28 bit empty. It is added into the firstcu or reqcu variables\nto record the case status of the value. This is used only for ASCII characters.\n*/\n\nreq_caseopt = ((options & PCRE2_CASELESS) != 0)? REQ_CASELESS : 0;\n\n/* Switch on next META item until the end of the branch */\n\nfor (;; pptr++)\n  {\n  BOOL possessive_quantifier;\n  BOOL note_group_empty;\n  uint32_t mclength;\n  uint32_t skipunits;\n  uint32_t subreqcu, subfirstcu;\n  uint32_t groupnumber;\n  uint32_t verbarglen, verbculen;\n  uint32_t subreqcuflags, subfirstcuflags;\n  open_capitem *oc;\n  PCRE2_UCHAR mcbuffer[8];\n\n  /* Get next META item in the pattern and its potential argument. */\n\n  meta = META_CODE(*pptr);\n  meta_arg = META_DATA(*pptr);\n\n  /* If we are in the pre-compile phase, accumulate the length used for the\n  previous cycle of this loop, unless the next item is a quantifier. */\n\n  if (lengthptr != NULL)\n    {\n    if (code > cb->start_workspace + cb->workspace_size -\n        WORK_SIZE_SAFETY_MARGIN)                       /* Check for overrun */\n      {\n      if (code >= cb->start_workspace + cb->workspace_size)\n        {\n        PCRE2_DEBUG_UNREACHABLE();\n        *errorcodeptr = ERR52;  /* Over-ran workspace - internal error */\n        }\n      else\n        *errorcodeptr = ERR86;\n      return 0;\n      }\n\n    /* There is at least one situation where code goes backwards: this is the\n    case of a zero quantifier after a class (e.g. [ab]{0}). When the quantifier\n    is processed, the whole class is eliminated. However, it is created first,\n    so we have to allow memory for it. Therefore, don't ever reduce the length\n    at this point. */\n\n    if (code < last_code) code = last_code;\n\n    /* If the next thing is not a quantifier, we add the length of the previous\n    item into the total, and reset the code pointer to the start of the\n    workspace. Otherwise leave the previous item available to be quantified. */\n\n    if (meta < META_ASTERISK || meta > META_MINMAX_QUERY)\n      {\n      if (OFLOW_MAX - *lengthptr < (PCRE2_SIZE)(code - orig_code))\n        {\n        *errorcodeptr = ERR20;   /* Integer overflow */\n        return 0;\n        }\n      *lengthptr += (PCRE2_SIZE)(code - orig_code);\n      if (*lengthptr > MAX_PATTERN_SIZE)\n        {\n        *errorcodeptr = ERR20;   /* Pattern is too large */\n        return 0;\n        }\n      code = orig_code;\n      }\n\n    /* Remember where this code item starts so we can catch the \"backwards\"\n    case above next time round. */\n\n    last_code = code;\n    }\n\n  /* Process the next parsed pattern item. If it is not a quantifier, remember\n  where it starts so that it can be quantified when a quantifier follows.\n  Checking for the legality of quantifiers happens in parse_regex(), except for\n  a quantifier after an assertion that is a condition. */\n\n  if (meta < META_ASTERISK || meta > META_MINMAX_QUERY)\n    {\n    previous = code;\n    if (matched_char && !had_accept) okreturn = 1;\n    }\n\n  previous_matched_char = matched_char;\n  matched_char = FALSE;\n  note_group_empty = FALSE;\n  skipunits = 0;         /* Default value for most subgroups */\n\n  switch(meta)\n    {\n    /* ===================================================================*/\n    /* The branch terminates at pattern end or | or ) */\n\n    case META_END:\n    case META_ALT:\n    case META_KET:\n    *firstcuptr = firstcu;\n    *firstcuflagsptr = firstcuflags;\n    *reqcuptr = reqcu;\n    *reqcuflagsptr = reqcuflags;\n    *codeptr = code;\n    *pptrptr = pptr;\n    return okreturn;\n\n\n    /* ===================================================================*/\n    /* Handle single-character metacharacters. In multiline mode, ^ disables\n    the setting of any following char as a first character. */\n\n    case META_CIRCUMFLEX:\n    if ((options & PCRE2_MULTILINE) != 0)\n      {\n      if (firstcuflags == REQ_UNSET)\n        zerofirstcuflags = firstcuflags = REQ_NONE;\n      *code++ = OP_CIRCM;\n      }\n    else *code++ = OP_CIRC;\n    break;\n\n    case META_DOLLAR:\n    *code++ = ((options & PCRE2_MULTILINE) != 0)? OP_DOLLM : OP_DOLL;\n    break;\n\n    /* There can never be a first char if '.' is first, whatever happens about\n    repeats. The value of reqcu doesn't change either. */\n\n    case META_DOT:\n    matched_char = TRUE;\n    if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE;\n    zerofirstcu = firstcu;\n    zerofirstcuflags = firstcuflags;\n    zeroreqcu = reqcu;\n    zeroreqcuflags = reqcuflags;\n    *code++ = ((options & PCRE2_DOTALL) != 0)? OP_ALLANY: OP_ANY;\n    break;\n\n\n    /* ===================================================================*/\n    /* Empty character classes are allowed if PCRE2_ALLOW_EMPTY_CLASS is set.\n    Otherwise, an initial ']' is taken as a data character. When empty classes\n    are allowed, [] must generate an empty class - we have no dedicated opcode\n    to optimise the representation, but it's a rare case (the '(*FAIL)'\n    construct would be a clearer way for a pattern author to represent a\n    non-matching branch, but it does have different semantics to '[]' if both\n    are followed by a quantifier). The empty-negated [^] matches any character,\n    so is useful: generate OP_ALLANY for this. */\n\n    case META_CLASS_EMPTY:\n    case META_CLASS_EMPTY_NOT:\n    matched_char = TRUE;\n    if (meta == META_CLASS_EMPTY_NOT) *code++ = OP_ALLANY;\n    else\n      {\n      *code++ = OP_CLASS;\n      memset(code, 0, 32);\n      code += 32 / sizeof(PCRE2_UCHAR);\n      }\n\n    if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE;\n    zerofirstcu = firstcu;\n    zerofirstcuflags = firstcuflags;\n    break;\n\n\n    /* ===================================================================*/\n    /* Non-empty character class. If the included characters are all < 256, we\n    build a 32-byte bitmap of the permitted characters, except in the special\n    case where there is only one such character. For negated classes, we build\n    the map as usual, then invert it at the end. However, we use a different\n    opcode so that data characters > 255 can be handled correctly.\n\n    If the class contains characters outside the 0-255 range, a different\n    opcode is compiled. It may optionally have a bit map for characters < 256,\n    but those above are explicitly listed afterwards. A flag code unit tells\n    whether the bitmap is present, and whether this is a negated class or\n    not. */\n\n    case META_CLASS_NOT:\n    case META_CLASS:\n    matched_char = TRUE;\n\n    /* Check for complex extended classes and handle them separately. */\n\n    if ((*pptr & CLASS_IS_ECLASS) != 0)\n      {\n      if (!PRIV(compile_class_nested)(options, xoptions, &pptr, &code,\n                                      errorcodeptr, cb, lengthptr))\n        return 0;\n      goto CLASS_END_PROCESSING;\n      }\n\n    /* We can optimize the case of a single character in a class by generating\n    OP_CHAR or OP_CHARI if it's positive, or OP_NOT or OP_NOTI if it's\n    negative. In the negative case there can be no first char if this item is\n    first, whatever repeat count may follow. In the case of reqcu, save the\n    previous value for reinstating. */\n\n    /* NOTE: at present this optimization is not effective if the only\n    character in a class in 32-bit, non-UCP mode has its top bit set. */\n\n    if (pptr[1] < META_END && pptr[2] == META_CLASS_END)\n      {\n      uint32_t c = pptr[1];\n\n      pptr += 2;                 /* Move on to class end */\n      if (meta == META_CLASS)    /* A positive one-char class can be */\n        {                        /* handled as a normal literal character. */\n        meta = c;                /* Set up the character */\n        goto NORMAL_CHAR_SET;\n        }\n\n      /* Handle a negative one-character class */\n\n      zeroreqcu = reqcu;\n      zeroreqcuflags = reqcuflags;\n      if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE;\n      zerofirstcu = firstcu;\n      zerofirstcuflags = firstcuflags;\n\n      /* For caseless UTF or UCP mode, check whether this character has more\n      than one other case. If so, generate a special OP_NOTPROP item instead of\n      OP_NOTI. When restricted by PCRE2_EXTRA_CASELESS_RESTRICT, ignore any\n      caseless set that starts with an ASCII character. If the character is\n      affected by the special Turkish rules, hardcode the not-matching\n      characters using a caseset. */\n\n#ifdef SUPPORT_UNICODE\n      if ((utf||ucp) && (options & PCRE2_CASELESS) != 0)\n        {\n        uint32_t caseset;\n\n        if ((xoptions & (PCRE2_EXTRA_TURKISH_CASING|PCRE2_EXTRA_CASELESS_RESTRICT)) ==\n              PCRE2_EXTRA_TURKISH_CASING &&\n            UCD_ANY_I(c))\n          {\n          caseset = PRIV(ucd_turkish_dotted_i_caseset) + (UCD_DOTTED_I(c)? 0 : 3);\n          }\n        else if ((caseset = UCD_CASESET(c)) != 0 &&\n                 (xoptions & PCRE2_EXTRA_CASELESS_RESTRICT) != 0 &&\n                 PRIV(ucd_caseless_sets)[caseset] < 128)\n          {\n          caseset = 0;  /* Ignore the caseless set if it's restricted. */\n          }\n\n        if (caseset != 0)\n          {\n          *code++ = OP_NOTPROP;\n          *code++ = PT_CLIST;\n          *code++ = caseset;\n          break;   /* We are finished with this class */\n          }\n        }\n#endif\n      /* Char has only one other (usable) case, or UCP not available */\n\n      *code++ = ((options & PCRE2_CASELESS) != 0)? OP_NOTI: OP_NOT;\n      code += PUTCHAR(c, code);\n      break;   /* We are finished with this class */\n      }        /* End of 1-char optimization */\n\n    /* Handle character classes that contain more than just one literal\n    character. If there are exactly two characters in a positive class, see if\n    they are case partners. This can be optimized to generate a caseless single\n    character match (which also sets first/required code units if relevant).\n    When casing restrictions apply, ignore a caseless set if both characters\n    are ASCII. When Turkish casing applies, an 'i' does not match its normal\n    Unicode \"othercase\". */\n\n    if (meta == META_CLASS && pptr[1] < META_END && pptr[2] < META_END &&\n        pptr[3] == META_CLASS_END)\n      {\n      uint32_t c = pptr[1];\n\n#ifdef SUPPORT_UNICODE\n      if ((UCD_CASESET(c) == 0 ||\n           ((xoptions & PCRE2_EXTRA_CASELESS_RESTRICT) != 0 &&\n            c < 128 && pptr[2] < 128)) &&\n          !((xoptions & (PCRE2_EXTRA_TURKISH_CASING|PCRE2_EXTRA_CASELESS_RESTRICT)) ==\n              PCRE2_EXTRA_TURKISH_CASING &&\n            UCD_ANY_I(c)))\n#endif\n        {\n        uint32_t d;\n\n#ifdef SUPPORT_UNICODE\n        if ((utf || ucp) && c > 127) d = UCD_OTHERCASE(c); else\n#endif\n          {\n#if PCRE2_CODE_UNIT_WIDTH != 8\n          if (c > 255) d = c; else\n#endif\n          d = TABLE_GET(c, cb->fcc, c);\n          }\n\n        if (c != d && pptr[2] == d)\n          {\n          pptr += 3;                 /* Move on to class end */\n          meta = c;\n          if ((options & PCRE2_CASELESS) == 0)\n            {\n            reset_caseful = TRUE;\n            options |= PCRE2_CASELESS;\n            req_caseopt = REQ_CASELESS;\n            }\n          goto CLASS_CASELESS_CHAR;\n          }\n        }\n      }\n\n    /* Now emit the OP_CLASS/OP_NCLASS/OP_XCLASS/OP_ALLANY opcode. */\n\n    pptr = PRIV(compile_class_not_nested)(options, xoptions, pptr + 1,\n                                          &code, meta == META_CLASS_NOT, NULL,\n                                          errorcodeptr, cb, lengthptr);\n    if (pptr == NULL) return 0;\n    PCRE2_ASSERT(*pptr == META_CLASS_END);\n\n    CLASS_END_PROCESSING:\n\n    /* If this class is the first thing in the branch, there can be no first\n    char setting, whatever the repeat count. Any reqcu setting must remain\n    unchanged after any kind of repeat. */\n\n    if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE;\n    zerofirstcu = firstcu;\n    zerofirstcuflags = firstcuflags;\n    zeroreqcu = reqcu;\n    zeroreqcuflags = reqcuflags;\n    break;  /* End of class processing */\n\n\n    /* ===================================================================*/\n    /* Deal with (*VERB)s. */\n\n    /* Check for open captures before ACCEPT and close those that are within\n    the same assertion level, also converting ACCEPT to ASSERT_ACCEPT in an\n    assertion. In the first pass, just accumulate the length required;\n    otherwise hitting (*ACCEPT) inside many nested parentheses can cause\n    workspace overflow. Do not set firstcu after *ACCEPT. */\n\n    case META_ACCEPT:\n    cb->had_accept = had_accept = TRUE;\n    for (oc = open_caps;\n         oc != NULL && oc->assert_depth >= cb->assert_depth;\n         oc = oc->next)\n      {\n      if (lengthptr != NULL)\n        {\n        *lengthptr += CU2BYTES(1) + IMM2_SIZE;\n        }\n      else\n        {\n        *code++ = OP_CLOSE;\n        PUT2INC(code, 0, oc->number);\n        }\n      }\n    *code++ = (cb->assert_depth > 0)? OP_ASSERT_ACCEPT : OP_ACCEPT;\n    if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE;\n    break;\n\n    case META_PRUNE:\n    case META_SKIP:\n    cb->had_pruneorskip = TRUE;\n    /* Fall through */\n    case META_COMMIT:\n    case META_FAIL:\n    *code++ = verbops[(meta - META_MARK) >> 16];\n    break;\n\n    case META_THEN:\n    cb->external_flags |= PCRE2_HASTHEN;\n    *code++ = OP_THEN;\n    break;\n\n    /* Handle verbs with arguments. Arguments can be very long, especially in\n    16- and 32-bit modes, and can overflow the workspace in the first pass.\n    However, the argument length is constrained to be small enough to fit in\n    one code unit. This check happens in parse_regex(). In the first pass,\n    instead of putting the argument into memory, we just update the length\n    counter and set up an empty argument. */\n\n    case META_THEN_ARG:\n    cb->external_flags |= PCRE2_HASTHEN;\n    goto VERB_ARG;\n\n    case META_PRUNE_ARG:\n    case META_SKIP_ARG:\n    cb->had_pruneorskip = TRUE;\n    /* Fall through */\n    case META_MARK:\n    case META_COMMIT_ARG:\n    VERB_ARG:\n    *code++ = verbops[(meta - META_MARK) >> 16];\n    /* The length is in characters. */\n    verbarglen = *(++pptr);\n    verbculen = 0;\n    tempcode = code++;\n    for (int i = 0; i < (int)verbarglen; i++)\n      {\n      meta = *(++pptr);\n#ifdef SUPPORT_UNICODE\n      if (utf) mclength = PRIV(ord2utf)(meta, mcbuffer); else\n#endif\n        {\n        mclength = 1;\n        mcbuffer[0] = meta;\n        }\n      if (lengthptr != NULL) *lengthptr += mclength; else\n        {\n        memcpy(code, mcbuffer, CU2BYTES(mclength));\n        code += mclength;\n        verbculen += mclength;\n        }\n      }\n\n    *tempcode = verbculen;   /* Fill in the code unit length */\n    *code++ = 0;             /* Terminating zero */\n    break;\n\n\n    /* ===================================================================*/\n    /* Handle options change. The new setting must be passed back for use in\n    subsequent branches. Reset the greedy defaults and the case value for\n    firstcu and reqcu. */\n\n    case META_OPTIONS:\n    *optionsptr = options = *(++pptr);\n    *xoptionsptr = xoptions = *(++pptr);\n    greedy_default = ((options & PCRE2_UNGREEDY) != 0);\n    greedy_non_default = greedy_default ^ 1;\n    req_caseopt = ((options & PCRE2_CASELESS) != 0)? REQ_CASELESS : 0;\n    break;\n\n    case META_OFFSET:\n    GETPLUSOFFSET(offset, pptr);\n    break;\n\n    case META_SCS:\n    bravalue = OP_ASSERT_SCS;\n    cb->assert_depth += 1;\n    goto GROUP_PROCESS;\n\n\n    /* ===================================================================*/\n    /* Handle conditional subpatterns. The case of (?(Rdigits) is ambiguous\n    because it could be a numerical check on recursion, or a name check on a\n    group's being set. The pre-pass sets up META_COND_RNUMBER as a name so that\n    we can handle it either way. We first try for a name; if not found, process\n    the number. */\n\n    case META_COND_RNUMBER:   /* (?(Rdigits) */\n    case META_COND_NAME:      /* (?(name) or (?'name') or ?(<name>) */\n    case META_COND_RNAME:     /* (?(R&name) - test for recursion */\n    case META_SCS_NAME:       /* Name of scan substring */\n    bravalue = OP_COND;\n      {\n      int count, index;\n      unsigned int i;\n      PCRE2_SPTR name;\n      named_group *ng = cb->named_groups;\n      uint32_t length = *(++pptr);\n\n      if (meta == META_SCS_NAME)\n        offset += meta_arg;\n      else\n        GETPLUSOFFSET(offset, pptr);\n      name = cb->start_pattern + offset;\n\n      /* In the first pass, the names generated in the pre-pass are available,\n      but the main name table has not yet been created. Scan the list of names\n      generated in the pre-pass in order to get a number and whether or not\n      this name is duplicated. If it is not duplicated, we can handle it as a\n      numerical group. */\n\n      for (i = 0; i < cb->names_found; i++, ng++)\n        if (length == ng->length &&\n            PRIV(strncmp)(name, ng->name, length) == 0) break;\n\n      if (i >= cb->names_found)\n        {\n        /* If the name was not found we have a bad reference, unless we are\n        dealing with R<digits>, which is treated as a recursion test by\n        number. */\n\n        groupnumber = 0;\n        if (meta == META_COND_RNUMBER)\n          {\n          for (i = 1; i < length; i++)\n            {\n            groupnumber = groupnumber * 10 + (name[i] - CHAR_0);\n            if (groupnumber > MAX_GROUP_NUMBER)\n              {\n              *errorcodeptr = ERR61;\n              cb->erroroffset = offset + i;\n              return 0;\n              }\n            }\n          }\n\n        if (meta != META_COND_RNUMBER || groupnumber > cb->bracount)\n          {\n          *errorcodeptr = ERR15;\n          cb->erroroffset = offset;\n          return 0;\n          }\n\n        /* (?Rdigits) treated as a recursion reference by number. A value of\n        zero (which is the result of both (?R) and (?R0)) means \"any\", and is\n        translated into RREF_ANY (which is 0xffff). */\n\n        if (groupnumber == 0) groupnumber = RREF_ANY;\n        code[1+LINK_SIZE] = OP_RREF;\n        PUT2(code, 2+LINK_SIZE, groupnumber);\n        skipunits = 1+IMM2_SIZE;\n        goto GROUP_PROCESS_NOTE_EMPTY;\n        }\n      else if (!ng->isdup)\n        {\n        /* Otherwise found a duplicated name */\n        if (ng->number > cb->top_backref) cb->top_backref = ng->number;\n\n        if (meta == META_SCS_NAME)\n          {\n          code[0] = OP_CREF;\n          PUT2(code, 1, ng->number);\n          code += 1+IMM2_SIZE;\n          break;\n          }\n\n        code[1+LINK_SIZE] = (meta == META_COND_RNAME)? OP_RREF : OP_CREF;\n        PUT2(code, 2+LINK_SIZE, ng->number);\n        skipunits = 1+IMM2_SIZE;\n        if (meta != META_SCS_NAME) goto GROUP_PROCESS_NOTE_EMPTY;\n        cb->assert_depth += 1;\n        goto GROUP_PROCESS;\n        }\n\n      /* We have a duplicated name. In the compile pass we have to search the\n      main table in order to get the index and count values. */\n\n      count = 0;  /* Values for first pass (avoids compiler warning) */\n      index = 0;\n      if (lengthptr == NULL && !find_dupname_details(name, length, &index,\n            &count, errorcodeptr, cb)) return 0;\n\n      if (meta == META_SCS_NAME)\n        {\n        code[0] = OP_DNCREF;\n        PUT2(code, 1, index);\n        PUT2(code, 1+IMM2_SIZE, count);\n        code += 1+2*IMM2_SIZE;\n        break;\n        }\n\n      /* A duplicated name was found. Note that if an R<digits> name is found\n      (META_COND_RNUMBER), it is a reference test, not a recursion test. */\n\n      code[1+LINK_SIZE] = (meta == META_COND_RNAME)? OP_DNRREF : OP_DNCREF;\n\n      /* Insert appropriate data values. */\n      skipunits = 1+2*IMM2_SIZE;\n      PUT2(code, 2+LINK_SIZE, index);\n      PUT2(code, 2+LINK_SIZE+IMM2_SIZE, count);\n      }\n\n    PCRE2_ASSERT(meta != META_SCS_NAME);\n    goto GROUP_PROCESS_NOTE_EMPTY;\n\n    /* The DEFINE condition is always false. Its internal groups may never\n    be called, so matched_char must remain false, hence the jump to\n    GROUP_PROCESS rather than GROUP_PROCESS_NOTE_EMPTY. */\n\n    case META_COND_DEFINE:\n    bravalue = OP_COND;\n    GETPLUSOFFSET(offset, pptr);\n    code[1+LINK_SIZE] = OP_DEFINE;\n    skipunits = 1;\n    goto GROUP_PROCESS;\n\n    /* Conditional test of a group's being set. */\n\n    case META_COND_NUMBER:\n    case META_SCS_NUMBER:\n    bravalue = OP_COND;\n    if (meta == META_SCS_NUMBER)\n      offset += meta_arg;\n    else\n      GETPLUSOFFSET(offset, pptr);\n\n    groupnumber = *(++pptr);\n    if (groupnumber > cb->bracount)\n      {\n      *errorcodeptr = ERR15;\n      cb->erroroffset = offset;\n      return 0;\n      }\n    if (groupnumber > cb->top_backref) cb->top_backref = groupnumber;\n\n    if (meta == META_SCS_NUMBER)\n      {\n      code[0] = OP_CREF;\n      PUT2(code, 1, groupnumber);\n      code += 1+IMM2_SIZE;\n      break;\n      }\n\n    /* Point at initial ( for too many branches error */\n    offset -= 2;\n    code[1+LINK_SIZE] = OP_CREF;\n    skipunits = 1+IMM2_SIZE;\n    PUT2(code, 2+LINK_SIZE, groupnumber);\n    goto GROUP_PROCESS_NOTE_EMPTY;\n\n    /* Test for the PCRE2 version. */\n\n    case META_COND_VERSION:\n    bravalue = OP_COND;\n    if (pptr[1] > 0)\n      code[1+LINK_SIZE] = ((PCRE2_MAJOR > pptr[2]) ||\n        (PCRE2_MAJOR == pptr[2] && PCRE2_MINOR >= pptr[3]))?\n          OP_TRUE : OP_FALSE;\n    else\n      code[1+LINK_SIZE] = (PCRE2_MAJOR == pptr[2] && PCRE2_MINOR == pptr[3])?\n        OP_TRUE : OP_FALSE;\n    skipunits = 1;\n    pptr += 3;\n    goto GROUP_PROCESS_NOTE_EMPTY;\n\n    /* The condition is an assertion, possibly preceded by a callout. */\n\n    case META_COND_ASSERT:\n    bravalue = OP_COND;\n    goto GROUP_PROCESS_NOTE_EMPTY;\n\n\n    /* ===================================================================*/\n    /* Handle all kinds of nested bracketed groups. The non-capturing,\n    non-conditional cases are here; others come to GROUP_PROCESS via goto. */\n\n    case META_LOOKAHEAD:\n    bravalue = OP_ASSERT;\n    cb->assert_depth += 1;\n    goto GROUP_PROCESS;\n\n    case META_LOOKAHEAD_NA:\n    bravalue = OP_ASSERT_NA;\n    cb->assert_depth += 1;\n    goto GROUP_PROCESS;\n\n    /* Optimize (?!) to (*FAIL) unless it is quantified - which is a weird\n    thing to do, but Perl allows all assertions to be quantified, and when\n    they contain capturing parentheses there may be a potential use for\n    this feature. Not that that applies to a quantified (?!) but we allow\n    it for uniformity. */\n\n    case META_LOOKAHEADNOT:\n    if (pptr[1] == META_KET &&\n         (pptr[2] < META_ASTERISK || pptr[2] > META_MINMAX_QUERY))\n      {\n      *code++ = OP_FAIL;\n      pptr++;\n      }\n    else\n      {\n      bravalue = OP_ASSERT_NOT;\n      cb->assert_depth += 1;\n      goto GROUP_PROCESS;\n      }\n    break;\n\n    case META_LOOKBEHIND:\n    bravalue = OP_ASSERTBACK;\n    cb->assert_depth += 1;\n    goto GROUP_PROCESS;\n\n    case META_LOOKBEHINDNOT:\n    bravalue = OP_ASSERTBACK_NOT;\n    cb->assert_depth += 1;\n    goto GROUP_PROCESS;\n\n    case META_LOOKBEHIND_NA:\n    bravalue = OP_ASSERTBACK_NA;\n    cb->assert_depth += 1;\n    goto GROUP_PROCESS;\n\n    case META_ATOMIC:\n    bravalue = OP_ONCE;\n    goto GROUP_PROCESS_NOTE_EMPTY;\n\n    case META_SCRIPT_RUN:\n    bravalue = OP_SCRIPT_RUN;\n    goto GROUP_PROCESS_NOTE_EMPTY;\n\n    case META_NOCAPTURE:\n    bravalue = OP_BRA;\n    /* Fall through */\n\n    /* Process nested bracketed regex. The nesting depth is maintained for the\n    benefit of the stackguard function. The test for too deep nesting is now\n    done in parse_regex(). Assertion and DEFINE groups come to GROUP_PROCESS;\n    others come to GROUP_PROCESS_NOTE_EMPTY, to indicate that we need to take\n    note of whether or not they may match an empty string. */\n\n    GROUP_PROCESS_NOTE_EMPTY:\n    note_group_empty = TRUE;\n\n    GROUP_PROCESS:\n    cb->parens_depth += 1;\n    *code = bravalue;\n    pptr++;\n    tempcode = code;\n    tempreqvary = cb->req_varyopt;        /* Save value before group */\n    length_prevgroup = 0;                 /* Initialize for pre-compile phase */\n\n    if ((group_return =\n         compile_regex(\n         options,                         /* The options state */\n         xoptions,                        /* The extra options state */\n         &tempcode,                       /* Where to put code (updated) */\n         &pptr,                           /* Input pointer (updated) */\n         errorcodeptr,                    /* Where to put an error message */\n         skipunits,                       /* Skip over bracket number */\n         &subfirstcu,                     /* For possible first char */\n         &subfirstcuflags,\n         &subreqcu,                       /* For possible last char */\n         &subreqcuflags,\n         bcptr,                           /* Current branch chain */\n         open_caps,                       /* Pointer to capture stack */\n         cb,                              /* Compile data block */\n         (lengthptr == NULL)? NULL :      /* Actual compile phase */\n           &length_prevgroup              /* Pre-compile phase */\n         )) == 0)\n      return 0;  /* Error */\n\n    cb->parens_depth -= 1;\n\n    /* If that was a non-conditional significant group (not an assertion, not a\n    DEFINE) that matches at least one character, then the current item matches\n    a character. Conditionals are handled below. */\n\n    if (note_group_empty && bravalue != OP_COND && group_return > 0)\n      matched_char = TRUE;\n\n    /* If we've just compiled an assertion, pop the assert depth. */\n\n    if (bravalue >= OP_ASSERT && bravalue <= OP_ASSERT_SCS)\n      cb->assert_depth -= 1;\n\n    /* At the end of compiling, code is still pointing to the start of the\n    group, while tempcode has been updated to point past the end of the group.\n    The parsed pattern pointer (pptr) is on the closing META_KET.\n\n    If this is a conditional bracket, check that there are no more than\n    two branches in the group, or just one if it's a DEFINE group. We do this\n    in the real compile phase, not in the pre-pass, where the whole group may\n    not be available. */\n\n    if (bravalue == OP_COND && lengthptr == NULL)\n      {\n      PCRE2_UCHAR *tc = code;\n      int condcount = 0;\n\n      do {\n         condcount++;\n         tc += GET(tc,1);\n         }\n      while (*tc != OP_KET);\n\n      /* A DEFINE group is never obeyed inline (the \"condition\" is always\n      false). It must have only one branch. Having checked this, change the\n      opcode to OP_FALSE. */\n\n      if (code[LINK_SIZE+1] == OP_DEFINE)\n        {\n        if (condcount > 1)\n          {\n          cb->erroroffset = offset;\n          *errorcodeptr = ERR54;\n          return 0;\n          }\n        code[LINK_SIZE+1] = OP_FALSE;\n        bravalue = OP_DEFINE;   /* A flag to suppress char handling below */\n        }\n\n      /* A \"normal\" conditional group. If there is just one branch, we must not\n      make use of its firstcu or reqcu, because this is equivalent to an\n      empty second branch. Also, it may match an empty string. If there are two\n      branches, this item must match a character if the group must. */\n\n      else\n        {\n        if (condcount > 2)\n          {\n          cb->erroroffset = offset;\n          *errorcodeptr = ERR27;\n          return 0;\n          }\n        if (condcount == 1) subfirstcuflags = subreqcuflags = REQ_NONE;\n          else if (group_return > 0) matched_char = TRUE;\n        }\n      }\n\n    /* In the pre-compile phase, update the length by the length of the group,\n    less the brackets at either end. Then reduce the compiled code to just a\n    set of non-capturing brackets so that it doesn't use much memory if it is\n    duplicated by a quantifier.*/\n\n    if (lengthptr != NULL)\n      {\n      if (OFLOW_MAX - *lengthptr < length_prevgroup - 2 - 2*LINK_SIZE)\n        {\n        *errorcodeptr = ERR20;\n        return 0;\n        }\n      *lengthptr += length_prevgroup - 2 - 2*LINK_SIZE;\n      code++;   /* This already contains bravalue */\n      PUTINC(code, 0, 1 + LINK_SIZE);\n      *code++ = OP_KET;\n      PUTINC(code, 0, 1 + LINK_SIZE);\n      break;    /* No need to waste time with special character handling */\n      }\n\n    /* Otherwise update the main code pointer to the end of the group. */\n\n    code = tempcode;\n\n    /* For a DEFINE group, required and first character settings are not\n    relevant. */\n\n    if (bravalue == OP_DEFINE) break;\n\n    /* Handle updating of the required and first code units for other types of\n    group. Update for normal brackets of all kinds, and conditions with two\n    branches (see code above). If the bracket is followed by a quantifier with\n    zero repeat, we have to back off. Hence the definition of zeroreqcu and\n    zerofirstcu outside the main loop so that they can be accessed for the back\n    off. */\n\n    zeroreqcu = reqcu;\n    zeroreqcuflags = reqcuflags;\n    zerofirstcu = firstcu;\n    zerofirstcuflags = firstcuflags;\n    groupsetfirstcu = FALSE;\n\n    if (bravalue >= OP_ONCE)  /* Not an assertion */\n      {\n      /* If we have not yet set a firstcu in this branch, take it from the\n      subpattern, remembering that it was set here so that a repeat of more\n      than one can replicate it as reqcu if necessary. If the subpattern has\n      no firstcu, set \"none\" for the whole branch. In both cases, a zero\n      repeat forces firstcu to \"none\". */\n\n      if (firstcuflags == REQ_UNSET && subfirstcuflags != REQ_UNSET)\n        {\n        if (subfirstcuflags < REQ_NONE)\n          {\n          firstcu = subfirstcu;\n          firstcuflags = subfirstcuflags;\n          groupsetfirstcu = TRUE;\n          }\n        else firstcuflags = REQ_NONE;\n        zerofirstcuflags = REQ_NONE;\n        }\n\n      /* If firstcu was previously set, convert the subpattern's firstcu\n      into reqcu if there wasn't one, using the vary flag that was in\n      existence beforehand. */\n\n      else if (subfirstcuflags < REQ_NONE && subreqcuflags >= REQ_NONE)\n        {\n        subreqcu = subfirstcu;\n        subreqcuflags = subfirstcuflags | tempreqvary;\n        }\n\n      /* If the subpattern set a required code unit (or set a first code unit\n      that isn't really the first code unit - see above), set it. */\n\n      if (subreqcuflags < REQ_NONE)\n        {\n        reqcu = subreqcu;\n        reqcuflags = subreqcuflags;\n        }\n      }\n\n    /* For a forward assertion, we take the reqcu, if set, provided that the\n    group has also set a firstcu. This can be helpful if the pattern that\n    follows the assertion doesn't set a different char. For example, it's\n    useful for /(?=abcde).+/. We can't set firstcu for an assertion, however\n    because it leads to incorrect effect for patterns such as /(?=a)a.+/ when\n    the \"real\" \"a\" would then become a reqcu instead of a firstcu. This is\n    overcome by a scan at the end if there's no firstcu, looking for an\n    asserted first char. A similar effect for patterns like /(?=.*X)X$/ means\n    we must only take the reqcu when the group also set a firstcu. Otherwise,\n    in that example, 'X' ends up set for both. */\n\n    else if ((bravalue == OP_ASSERT || bravalue == OP_ASSERT_NA) &&\n             subreqcuflags < REQ_NONE && subfirstcuflags < REQ_NONE)\n      {\n      reqcu = subreqcu;\n      reqcuflags = subreqcuflags;\n      }\n\n    break;  /* End of nested group handling */\n\n\n    /* ===================================================================*/\n    /* Handle named backreferences and recursions. */\n\n    case META_BACKREF_BYNAME:\n    case META_RECURSE_BYNAME:\n      {\n      int count, index;\n      PCRE2_SPTR name;\n      BOOL is_dupname = FALSE;\n      named_group *ng = cb->named_groups;\n      uint32_t length = *(++pptr);\n\n      GETPLUSOFFSET(offset, pptr);\n      name = cb->start_pattern + offset;\n\n      /* In the first pass, the names generated in the pre-pass are available,\n      but the main name table has not yet been created. Scan the list of names\n      generated in the pre-pass in order to get a number and whether or not\n      this name is duplicated. */\n\n      groupnumber = 0;\n      for (unsigned int i = 0; i < cb->names_found; i++, ng++)\n        {\n        if (length == ng->length &&\n            PRIV(strncmp)(name, ng->name, length) == 0)\n          {\n          is_dupname = ng->isdup;\n          groupnumber = ng->number;\n\n          /* For a recursion, that's all that is needed. We can now go to\n          the code that handles numerical recursion, applying it to the first\n          group with the given name. */\n\n          if (meta == META_RECURSE_BYNAME)\n            {\n            meta_arg = groupnumber;\n            goto HANDLE_NUMERICAL_RECURSION;\n            }\n\n          /* For a back reference, update the back reference map and the\n          maximum back reference. */\n\n          cb->backref_map |= (groupnumber < 32)? (1u << groupnumber) : 1;\n          if (groupnumber > cb->top_backref)\n            cb->top_backref = groupnumber;\n          }\n        }\n\n      /* If the name was not found we have a bad reference. */\n\n      if (groupnumber == 0)\n        {\n        *errorcodeptr = ERR15;\n        cb->erroroffset = offset;\n        return 0;\n        }\n\n      /* If a back reference name is not duplicated, we can handle it as\n      a numerical reference. */\n\n      if (!is_dupname)\n        {\n        meta_arg = groupnumber;\n        goto HANDLE_SINGLE_REFERENCE;\n        }\n\n      /* If a back reference name is duplicated, we generate a different\n      opcode to a numerical back reference. In the second pass we must\n      search for the index and count in the final name table. */\n\n      count = 0;  /* Values for first pass (avoids compiler warning) */\n      index = 0;\n      if (lengthptr == NULL && !find_dupname_details(name, length, &index,\n            &count, errorcodeptr, cb)) return 0;\n\n      if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE;\n      *code++ = ((options & PCRE2_CASELESS) != 0)? OP_DNREFI : OP_DNREF;\n      PUT2INC(code, 0, index);\n      PUT2INC(code, 0, count);\n      if ((options & PCRE2_CASELESS) != 0)\n        *code++ = (((xoptions & PCRE2_EXTRA_CASELESS_RESTRICT) != 0)?\n                   REFI_FLAG_CASELESS_RESTRICT : 0) |\n                  (((xoptions & PCRE2_EXTRA_TURKISH_CASING) != 0)?\n                   REFI_FLAG_TURKISH_CASING : 0);\n      }\n    break;\n\n\n    /* ===================================================================*/\n    /* Handle a numerical callout. */\n\n    case META_CALLOUT_NUMBER:\n    code[0] = OP_CALLOUT;\n    PUT(code, 1, pptr[1]);               /* Offset to next pattern item */\n    PUT(code, 1 + LINK_SIZE, pptr[2]);   /* Length of next pattern item */\n    code[1 + 2*LINK_SIZE] = pptr[3];\n    pptr += 3;\n    code += PRIV(OP_lengths)[OP_CALLOUT];\n    break;\n\n\n    /* ===================================================================*/\n    /* Handle a callout with a string argument. In the pre-pass we just compute\n    the length without generating anything. The length in pptr[3] includes both\n    delimiters; in the actual compile only the first one is copied, but a\n    terminating zero is added. Any doubled delimiters within the string make\n    this an overestimate, but it is not worth bothering about. */\n\n    case META_CALLOUT_STRING:\n    if (lengthptr != NULL)\n      {\n      *lengthptr += pptr[3] + (1 + 4*LINK_SIZE);\n      pptr += 3;\n      SKIPOFFSET(pptr);\n      }\n\n    /* In the real compile we can copy the string. The starting delimiter is\n     included so that the client can discover it if they want. We also pass the\n     start offset to help a script language give better error messages. */\n\n    else\n      {\n      PCRE2_SPTR pp;\n      uint32_t delimiter;\n      uint32_t length = pptr[3];\n      PCRE2_UCHAR *callout_string = code + (1 + 4*LINK_SIZE);\n\n      code[0] = OP_CALLOUT_STR;\n      PUT(code, 1, pptr[1]);               /* Offset to next pattern item */\n      PUT(code, 1 + LINK_SIZE, pptr[2]);   /* Length of next pattern item */\n\n      pptr += 3;\n      GETPLUSOFFSET(offset, pptr);         /* Offset to string in pattern */\n      pp = cb->start_pattern + offset;\n      delimiter = *callout_string++ = *pp++;\n      if (delimiter == CHAR_LEFT_CURLY_BRACKET)\n        delimiter = CHAR_RIGHT_CURLY_BRACKET;\n      PUT(code, 1 + 3*LINK_SIZE, (int)(offset + 1));  /* One after delimiter */\n\n      /* The syntax of the pattern was checked in the parsing scan. The length\n      includes both delimiters, but we have passed the opening one just above,\n      so we reduce length before testing it. The test is for > 1 because we do\n      not want to copy the final delimiter. This also ensures that pp[1] is\n      accessible. */\n\n      while (--length > 1)\n        {\n        if (*pp == delimiter && pp[1] == delimiter)\n          {\n          *callout_string++ = delimiter;\n          pp += 2;\n          length--;\n          }\n        else *callout_string++ = *pp++;\n        }\n      *callout_string++ = CHAR_NUL;\n\n      /* Set the length of the entire item, the advance to its end. */\n\n      PUT(code, 1 + 2*LINK_SIZE, (int)(callout_string - code));\n      code = callout_string;\n      }\n    break;\n\n\n    /* ===================================================================*/\n    /* Handle repetition. The different types are all sorted out in the parsing\n    pass. */\n\n    case META_MINMAX_PLUS:\n    case META_MINMAX_QUERY:\n    case META_MINMAX:\n    repeat_min = *(++pptr);\n    repeat_max = *(++pptr);\n    goto REPEAT;\n\n    case META_ASTERISK:\n    case META_ASTERISK_PLUS:\n    case META_ASTERISK_QUERY:\n    repeat_min = 0;\n    repeat_max = REPEAT_UNLIMITED;\n    goto REPEAT;\n\n    case META_PLUS:\n    case META_PLUS_PLUS:\n    case META_PLUS_QUERY:\n    repeat_min = 1;\n    repeat_max = REPEAT_UNLIMITED;\n    goto REPEAT;\n\n    case META_QUERY:\n    case META_QUERY_PLUS:\n    case META_QUERY_QUERY:\n    repeat_min = 0;\n    repeat_max = 1;\n\n    REPEAT:\n    if (previous_matched_char && repeat_min > 0) matched_char = TRUE;\n\n    /* Remember whether this is a variable length repeat, and default to\n    single-char opcodes. */\n\n    reqvary = (repeat_min == repeat_max)? 0 : REQ_VARY;\n\n    /* Adjust first and required code units for a zero repeat. */\n\n    if (repeat_min == 0)\n      {\n      firstcu = zerofirstcu;\n      firstcuflags = zerofirstcuflags;\n      reqcu = zeroreqcu;\n      reqcuflags = zeroreqcuflags;\n      }\n\n    /* Note the greediness and possessiveness. */\n\n    switch (meta)\n      {\n      case META_MINMAX_PLUS:\n      case META_ASTERISK_PLUS:\n      case META_PLUS_PLUS:\n      case META_QUERY_PLUS:\n      repeat_type = 0;                  /* Force greedy */\n      possessive_quantifier = TRUE;\n      break;\n\n      case META_MINMAX_QUERY:\n      case META_ASTERISK_QUERY:\n      case META_PLUS_QUERY:\n      case META_QUERY_QUERY:\n      repeat_type = greedy_non_default;\n      possessive_quantifier = FALSE;\n      break;\n\n      default:\n      repeat_type = greedy_default;\n      possessive_quantifier = FALSE;\n      break;\n      }\n\n    /* Save start of previous item, in case we have to move it up in order to\n    insert something before it, and remember what it was. */\n\n    PCRE2_ASSERT(previous != NULL);\n    tempcode = previous;\n    op_previous = *previous;\n\n    /* Now handle repetition for the different types of item. If the repeat\n    minimum and the repeat maximum are both 1, we can ignore the quantifier for\n    non-parenthesized items, as they have only one alternative. For anything in\n    parentheses, we must not ignore if {1} is possessive. */\n\n    switch (op_previous)\n      {\n      /* If previous was a character or negated character match, abolish the\n      item and generate a repeat item instead. If a char item has a minimum of\n      more than one, ensure that it is set in reqcu - it might not be if a\n      sequence such as x{3} is the first thing in a branch because the x will\n      have gone into firstcu instead.  */\n\n      case OP_CHAR:\n      case OP_CHARI:\n      case OP_NOT:\n      case OP_NOTI:\n      if (repeat_max == 1 && repeat_min == 1) goto END_REPEAT;\n      op_type = chartypeoffset[op_previous - OP_CHAR];\n\n      /* Deal with UTF characters that take up more than one code unit. */\n\n#ifdef MAYBE_UTF_MULTI\n      if (utf && NOT_FIRSTCU(code[-1]))\n        {\n        PCRE2_UCHAR *lastchar = code - 1;\n        BACKCHAR(lastchar);\n        mclength = (uint32_t)(code - lastchar);   /* Length of UTF character */\n        memcpy(mcbuffer, lastchar, CU2BYTES(mclength));  /* Save the char */\n        }\n      else\n#endif  /* MAYBE_UTF_MULTI */\n\n      /* Handle the case of a single code unit - either with no UTF support, or\n      with UTF disabled, or for a single-code-unit UTF character. In the latter\n      case, for a repeated positive match, get the caseless flag for the\n      required code unit from the previous character, because a class like [Aa]\n      sets a caseless A but by now the req_caseopt flag has been reset. */\n\n        {\n        mcbuffer[0] = code[-1];\n        mclength = 1;\n        if (op_previous <= OP_CHARI && repeat_min > 1)\n          {\n          reqcu = mcbuffer[0];\n          reqcuflags = cb->req_varyopt;\n          if (op_previous == OP_CHARI) reqcuflags |= REQ_CASELESS;\n          }\n        }\n      goto OUTPUT_SINGLE_REPEAT;  /* Code shared with single character types */\n\n      /* If previous was a character class or a back reference, we put the\n      repeat stuff after it, but just skip the item if the repeat was {0,0}. */\n\n#ifdef SUPPORT_WIDE_CHARS\n      case OP_XCLASS:\n      case OP_ECLASS:\n#endif\n      case OP_CLASS:\n      case OP_NCLASS:\n      case OP_REF:\n      case OP_REFI:\n      case OP_DNREF:\n      case OP_DNREFI:\n\n      if (repeat_max == 0)\n        {\n        code = previous;\n        goto END_REPEAT;\n        }\n      if (repeat_max == 1 && repeat_min == 1) goto END_REPEAT;\n\n      if (repeat_min == 0 && repeat_max == REPEAT_UNLIMITED)\n        *code++ = OP_CRSTAR + repeat_type;\n      else if (repeat_min == 1 && repeat_max == REPEAT_UNLIMITED)\n        *code++ = OP_CRPLUS + repeat_type;\n      else if (repeat_min == 0 && repeat_max == 1)\n        *code++ = OP_CRQUERY + repeat_type;\n      else\n        {\n        *code++ = OP_CRRANGE + repeat_type;\n        PUT2INC(code, 0, repeat_min);\n        if (repeat_max == REPEAT_UNLIMITED) repeat_max = 0;  /* 2-byte encoding for max */\n        PUT2INC(code, 0, repeat_max);\n        }\n      break;\n\n      /* Prior to 10.30, repeated recursions were wrapped in OP_ONCE brackets\n      because pcre2_match() could not handle backtracking into recursively\n      called groups. Now that this backtracking is available, we no longer need\n      to do this. However, we still need to replicate recursions as we do for\n      groups so as to have independent backtracking points. We can replicate\n      for the minimum number of repeats directly. For optional repeats we now\n      wrap the recursion in OP_BRA brackets and make use of the bracket\n      repetition. */\n\n      case OP_RECURSE:\n      if (repeat_max == 1 && repeat_min == 1 && !possessive_quantifier)\n        goto END_REPEAT;\n\n      /* Generate unwrapped repeats for a non-zero minimum, except when the\n      minimum is 1 and the maximum unlimited, because that can be handled with\n      OP_BRA terminated by OP_KETRMAX/MIN. When the maximum is equal to the\n      minimum, we just need to generate the appropriate additional copies.\n      Otherwise we need to generate one more, to simulate the situation when\n      the minimum is zero. */\n\n      if (repeat_min > 0 && (repeat_min != 1 || repeat_max != REPEAT_UNLIMITED))\n        {\n        int replicate = repeat_min;\n        if (repeat_min == repeat_max) replicate--;\n\n        /* In the pre-compile phase, we don't actually do the replication. We\n        just adjust the length as if we had. Do some paranoid checks for\n        potential integer overflow. */\n\n        if (lengthptr != NULL)\n          {\n          PCRE2_SIZE delta;\n          if (PRIV(ckd_smul)(&delta, replicate, 1 + LINK_SIZE) ||\n              OFLOW_MAX - *lengthptr < delta)\n            {\n            *errorcodeptr = ERR20;\n            return 0;\n            }\n          *lengthptr += delta;\n          }\n\n        else for (int i = 0; i < replicate; i++)\n          {\n          memcpy(code, previous, CU2BYTES(1 + LINK_SIZE));\n          previous = code;\n          code += 1 + LINK_SIZE;\n          }\n\n        /* If the number of repeats is fixed, we are done. Otherwise, adjust\n        the counts and fall through. */\n\n        if (repeat_min == repeat_max) break;\n        if (repeat_max != REPEAT_UNLIMITED) repeat_max -= repeat_min;\n        repeat_min = 0;\n        }\n\n      /* Wrap the recursion call in OP_BRA brackets. */\n\n      (void)memmove(previous + 1 + LINK_SIZE, previous, CU2BYTES(1 + LINK_SIZE));\n      op_previous = *previous = OP_BRA;\n      PUT(previous, 1, 2 + 2*LINK_SIZE);\n      previous[2 + 2*LINK_SIZE] = OP_KET;\n      PUT(previous, 3 + 2*LINK_SIZE, 2 + 2*LINK_SIZE);\n      code += 2 + 2 * LINK_SIZE;\n      length_prevgroup = 3 + 3*LINK_SIZE;\n      group_return = -1;  /* Set \"may match empty string\" */\n\n      /* Now treat as a repeated OP_BRA. */\n      /* Fall through */\n\n      /* If previous was a bracket group, we may have to replicate it in\n      certain cases. Note that at this point we can encounter only the \"basic\"\n      bracket opcodes such as BRA and CBRA, as this is the place where they get\n      converted into the more special varieties such as BRAPOS and SBRA.\n      Originally, PCRE did not allow repetition of assertions, but now it does,\n      for Perl compatibility. */\n\n      case OP_ASSERT:\n      case OP_ASSERT_NOT:\n      case OP_ASSERT_NA:\n      case OP_ASSERTBACK:\n      case OP_ASSERTBACK_NOT:\n      case OP_ASSERTBACK_NA:\n      case OP_ASSERT_SCS:\n      case OP_ONCE:\n      case OP_SCRIPT_RUN:\n      case OP_BRA:\n      case OP_CBRA:\n      case OP_COND:\n        {\n        int len = (int)(code - previous);\n        PCRE2_UCHAR *bralink = NULL;\n        PCRE2_UCHAR *brazeroptr = NULL;\n\n        if (repeat_max == 1 && repeat_min == 1 && !possessive_quantifier)\n          goto END_REPEAT;\n\n        /* Repeating a DEFINE group (or any group where the condition is always\n        FALSE and there is only one branch) is pointless, but Perl allows the\n        syntax, so we just ignore the repeat. */\n\n        if (op_previous == OP_COND && previous[LINK_SIZE+1] == OP_FALSE &&\n            previous[GET(previous, 1)] != OP_ALT)\n          goto END_REPEAT;\n\n        /* Perl allows all assertions to be quantified, and when they contain\n        capturing parentheses and/or are optional there are potential uses for\n        this feature. PCRE2 used to force the maximum quantifier to 1 on the\n        invalid grounds that further repetition was never useful. This was\n        always a bit pointless, since an assertion could be wrapped with a\n        repeated group to achieve the effect. General repetition is now\n        permitted, but if the maximum is unlimited it is set to one more than\n        the minimum. */\n\n        if (op_previous < OP_ONCE)    /* Assertion */\n          {\n          if (repeat_max == REPEAT_UNLIMITED) repeat_max = repeat_min + 1;\n          }\n\n        /* The case of a zero minimum is special because of the need to stick\n        OP_BRAZERO in front of it, and because the group appears once in the\n        data, whereas in other cases it appears the minimum number of times. For\n        this reason, it is simplest to treat this case separately, as otherwise\n        the code gets far too messy. There are several special subcases when the\n        minimum is zero. */\n\n        if (repeat_min == 0)\n          {\n          /* If the maximum is also zero, we used to just omit the group from\n          the output altogether, like this:\n\n          ** if (repeat_max == 0)\n          **   {\n          **   code = previous;\n          **   goto END_REPEAT;\n          **   }\n\n          However, that fails when a group or a subgroup within it is\n          referenced as a subroutine from elsewhere in the pattern, so now we\n          stick in OP_SKIPZERO in front of it so that it is skipped on\n          execution. As we don't have a list of which groups are referenced, we\n          cannot do this selectively.\n\n          If the maximum is 1 or unlimited, we just have to stick in the\n          BRAZERO and do no more at this point. */\n\n          if (repeat_max <= 1 || repeat_max == REPEAT_UNLIMITED)\n            {\n            (void)memmove(previous + 1, previous, CU2BYTES(len));\n            code++;\n            if (repeat_max == 0)\n              {\n              *previous++ = OP_SKIPZERO;\n              goto END_REPEAT;\n              }\n            brazeroptr = previous;    /* Save for possessive optimizing */\n            *previous++ = OP_BRAZERO + repeat_type;\n            }\n\n          /* If the maximum is greater than 1 and limited, we have to replicate\n          in a nested fashion, sticking OP_BRAZERO before each set of brackets.\n          The first one has to be handled carefully because it's the original\n          copy, which has to be moved up. The remainder can be handled by code\n          that is common with the non-zero minimum case below. We have to\n          adjust the value or repeat_max, since one less copy is required. */\n\n          else\n            {\n            int linkoffset;\n            (void)memmove(previous + 2 + LINK_SIZE, previous, CU2BYTES(len));\n            code += 2 + LINK_SIZE;\n            *previous++ = OP_BRAZERO + repeat_type;\n            *previous++ = OP_BRA;\n\n            /* We chain together the bracket link offset fields that have to be\n            filled in later when the ends of the brackets are reached. */\n\n            linkoffset = (bralink == NULL)? 0 : (int)(previous - bralink);\n            bralink = previous;\n            PUTINC(previous, 0, linkoffset);\n            }\n\n          if (repeat_max != REPEAT_UNLIMITED) repeat_max--;\n          }\n\n        /* If the minimum is greater than zero, replicate the group as many\n        times as necessary, and adjust the maximum to the number of subsequent\n        copies that we need. */\n\n        else\n          {\n          if (repeat_min > 1)\n            {\n            /* In the pre-compile phase, we don't actually do the replication.\n            We just adjust the length as if we had. Do some paranoid checks for\n            potential integer overflow. */\n\n            if (lengthptr != NULL)\n              {\n              PCRE2_SIZE delta;\n              if (PRIV(ckd_smul)(&delta, repeat_min - 1,\n                                 (int)length_prevgroup) ||\n                  OFLOW_MAX - *lengthptr < delta)\n                {\n                *errorcodeptr = ERR20;\n                return 0;\n                }\n              *lengthptr += delta;\n              }\n\n            /* This is compiling for real. If there is a set first code unit\n            for the group, and we have not yet set a \"required code unit\", set\n            it. */\n\n            else\n              {\n              if (groupsetfirstcu && reqcuflags >= REQ_NONE)\n                {\n                reqcu = firstcu;\n                reqcuflags = firstcuflags;\n                }\n              for (uint32_t i = 1; i < repeat_min; i++)\n                {\n                memcpy(code, previous, CU2BYTES(len));\n                code += len;\n                }\n              }\n            }\n\n          if (repeat_max != REPEAT_UNLIMITED) repeat_max -= repeat_min;\n          }\n\n        /* This code is common to both the zero and non-zero minimum cases. If\n        the maximum is limited, it replicates the group in a nested fashion,\n        remembering the bracket starts on a stack. In the case of a zero\n        minimum, the first one was set up above. In all cases the repeat_max\n        now specifies the number of additional copies needed. Again, we must\n        remember to replicate entries on the forward reference list. */\n\n        if (repeat_max != REPEAT_UNLIMITED)\n          {\n          /* In the pre-compile phase, we don't actually do the replication. We\n          just adjust the length as if we had. For each repetition we must add\n          1 to the length for BRAZERO and for all but the last repetition we\n          must add 2 + 2*LINKSIZE to allow for the nesting that occurs. Do some\n          paranoid checks to avoid integer overflow. */\n\n          if (lengthptr != NULL && repeat_max > 0)\n            {\n            PCRE2_SIZE delta;\n            if (PRIV(ckd_smul)(&delta, repeat_max,\n                               (int)length_prevgroup + 1 + 2 + 2*LINK_SIZE) ||\n                OFLOW_MAX + (2 + 2*LINK_SIZE) - *lengthptr < delta)\n              {\n              *errorcodeptr = ERR20;\n              return 0;\n              }\n            delta -= (2 + 2*LINK_SIZE);   /* Last one doesn't nest */\n            *lengthptr += delta;\n            }\n\n          /* This is compiling for real */\n\n          else for (uint32_t i = repeat_max; i >= 1; i--)\n            {\n            *code++ = OP_BRAZERO + repeat_type;\n\n            /* All but the final copy start a new nesting, maintaining the\n            chain of brackets outstanding. */\n\n            if (i != 1)\n              {\n              int linkoffset;\n              *code++ = OP_BRA;\n              linkoffset = (bralink == NULL)? 0 : (int)(code - bralink);\n              bralink = code;\n              PUTINC(code, 0, linkoffset);\n              }\n\n            memcpy(code, previous, CU2BYTES(len));\n            code += len;\n            }\n\n          /* Now chain through the pending brackets, and fill in their length\n          fields (which are holding the chain links pro tem). */\n\n          while (bralink != NULL)\n            {\n            int oldlinkoffset;\n            int linkoffset = (int)(code - bralink + 1);\n            PCRE2_UCHAR *bra = code - linkoffset;\n            oldlinkoffset = GET(bra, 1);\n            bralink = (oldlinkoffset == 0)? NULL : bralink - oldlinkoffset;\n            *code++ = OP_KET;\n            PUTINC(code, 0, linkoffset);\n            PUT(bra, 1, linkoffset);\n            }\n          }\n\n        /* If the maximum is unlimited, set a repeater in the final copy. For\n        SCRIPT_RUN and ONCE brackets, that's all we need to do. However,\n        possessively repeated ONCE brackets can be converted into non-capturing\n        brackets, as the behaviour of (?:xx)++ is the same as (?>xx)++ and this\n        saves having to deal with possessive ONCEs specially.\n\n        Otherwise, when we are doing the actual compile phase, check to see\n        whether this group is one that could match an empty string. If so,\n        convert the initial operator to the S form (e.g. OP_BRA -> OP_SBRA) so\n        that runtime checking can be done. [This check is also applied to ONCE\n        and SCRIPT_RUN groups at runtime, but in a different way.]\n\n        Then, if the quantifier was possessive and the bracket is not a\n        conditional, we convert the BRA code to the POS form, and the KET code\n        to KETRPOS. (It turns out to be convenient at runtime to detect this\n        kind of subpattern at both the start and at the end.) The use of\n        special opcodes makes it possible to reduce greatly the stack usage in\n        pcre2_match(). If the group is preceded by OP_BRAZERO, convert this to\n        OP_BRAPOSZERO.\n\n        Then, if the minimum number of matches is 1 or 0, cancel the possessive\n        flag so that the default action below, of wrapping everything inside\n        atomic brackets, does not happen. When the minimum is greater than 1,\n        there will be earlier copies of the group, and so we still have to wrap\n        the whole thing. */\n\n        else\n          {\n          PCRE2_UCHAR *ketcode = code - 1 - LINK_SIZE;\n          PCRE2_UCHAR *bracode = ketcode - GET(ketcode, 1);\n\n          /* Convert possessive ONCE brackets to non-capturing */\n\n          if (*bracode == OP_ONCE && possessive_quantifier) *bracode = OP_BRA;\n\n          /* For non-possessive ONCE and for SCRIPT_RUN brackets, all we need\n          to do is to set the KET. */\n\n          if (*bracode == OP_ONCE || *bracode == OP_SCRIPT_RUN)\n            *ketcode = OP_KETRMAX + repeat_type;\n\n          /* Handle non-SCRIPT_RUN and non-ONCE brackets and possessive ONCEs\n          (which have been converted to non-capturing above). */\n\n          else\n            {\n            /* In the compile phase, adjust the opcode if the group can match\n            an empty string. For a conditional group with only one branch, the\n            value of group_return will not show \"could be empty\", so we must\n            check that separately. */\n\n            if (lengthptr == NULL)\n              {\n              if (group_return < 0) *bracode += OP_SBRA - OP_BRA;\n              if (*bracode == OP_COND && bracode[GET(bracode,1)] != OP_ALT)\n                *bracode = OP_SCOND;\n              }\n\n            /* Handle possessive quantifiers. */\n\n            if (possessive_quantifier)\n              {\n              /* For COND brackets, we wrap the whole thing in a possessively\n              repeated non-capturing bracket, because we have not invented POS\n              versions of the COND opcodes. */\n\n              if (*bracode == OP_COND || *bracode == OP_SCOND)\n                {\n                int nlen = (int)(code - bracode);\n                (void)memmove(bracode + 1 + LINK_SIZE, bracode, CU2BYTES(nlen));\n                code += 1 + LINK_SIZE;\n                nlen += 1 + LINK_SIZE;\n                *bracode = (*bracode == OP_COND)? OP_BRAPOS : OP_SBRAPOS;\n                *code++ = OP_KETRPOS;\n                PUTINC(code, 0, nlen);\n                PUT(bracode, 1, nlen);\n                }\n\n              /* For non-COND brackets, we modify the BRA code and use KETRPOS. */\n\n              else\n                {\n                *bracode += 1;              /* Switch to xxxPOS opcodes */\n                *ketcode = OP_KETRPOS;\n                }\n\n              /* If the minimum is zero, mark it as possessive, then unset the\n              possessive flag when the minimum is 0 or 1. */\n\n              if (brazeroptr != NULL) *brazeroptr = OP_BRAPOSZERO;\n              if (repeat_min < 2) possessive_quantifier = FALSE;\n              }\n\n            /* Non-possessive quantifier */\n\n            else *ketcode = OP_KETRMAX + repeat_type;\n            }\n          }\n        }\n      break;\n\n      /* If previous was a character type match (\\d or similar), abolish it and\n      create a suitable repeat item. The code is shared with single-character\n      repeats by setting op_type to add a suitable offset into repeat_type.\n      Note the the Unicode property types will be present only when\n      SUPPORT_UNICODE is defined, but we don't wrap the little bits of code\n      here because it just makes it horribly messy. */\n\n      default:\n      if (op_previous >= OP_EODN || op_previous <= OP_WORD_BOUNDARY)\n        {\n        PCRE2_DEBUG_UNREACHABLE();\n        *errorcodeptr = ERR10;  /* Not a character type - internal error */\n        return 0;\n        }\n      else\n        {\n        int prop_type, prop_value;\n        PCRE2_UCHAR *oldcode;\n\n        if (repeat_max == 1 && repeat_min == 1) goto END_REPEAT;\n\n        op_type = OP_TYPESTAR - OP_STAR;      /* Use type opcodes */\n        mclength = 0;                         /* Not a character */\n\n        if (op_previous == OP_PROP || op_previous == OP_NOTPROP)\n          {\n          prop_type = previous[1];\n          prop_value = previous[2];\n          }\n        else\n          {\n          /* Come here from just above with a character in mcbuffer/mclength.\n          You must also set op_type before the jump. */\n          OUTPUT_SINGLE_REPEAT:\n          prop_type = prop_value = -1;\n          }\n\n        /* At this point, if prop_type == prop_value == -1 we either have a\n        character in mcbuffer when mclength is greater than zero, or we have\n        mclength zero, in which case there is a non-property character type in\n        op_previous. If prop_type/value are not negative, we have a property\n        character type in op_previous. */\n\n        oldcode = code;                   /* Save where we were */\n        code = previous;                  /* Usually overwrite previous item */\n\n        /* If the maximum is zero then the minimum must also be zero; Perl allows\n        this case, so we do too - by simply omitting the item altogether. */\n\n        if (repeat_max == 0) goto END_REPEAT;\n\n        /* Combine the op_type with the repeat_type */\n\n        repeat_type += op_type;\n\n        /* A minimum of zero is handled either as the special case * or ?, or as\n        an UPTO, with the maximum given. */\n\n        if (repeat_min == 0)\n          {\n          if (repeat_max == REPEAT_UNLIMITED) *code++ = OP_STAR + repeat_type;\n            else if (repeat_max == 1) *code++ = OP_QUERY + repeat_type;\n          else\n            {\n            *code++ = OP_UPTO + repeat_type;\n            PUT2INC(code, 0, repeat_max);\n            }\n          }\n\n        /* A repeat minimum of 1 is optimized into some special cases. If the\n        maximum is unlimited, we use OP_PLUS. Otherwise, the original item is\n        left in place and, if the maximum is greater than 1, we use OP_UPTO with\n        one less than the maximum. */\n\n        else if (repeat_min == 1)\n          {\n          if (repeat_max == REPEAT_UNLIMITED)\n            *code++ = OP_PLUS + repeat_type;\n          else\n            {\n            code = oldcode;  /* Leave previous item in place */\n            if (repeat_max == 1) goto END_REPEAT;\n            *code++ = OP_UPTO + repeat_type;\n            PUT2INC(code, 0, repeat_max - 1);\n            }\n          }\n\n        /* The case {n,n} is just an EXACT, while the general case {n,m} is\n        handled as an EXACT followed by an UPTO or STAR or QUERY. */\n\n        else\n          {\n          *code++ = OP_EXACT + op_type;  /* NB EXACT doesn't have repeat_type */\n          PUT2INC(code, 0, repeat_min);\n\n          /* Unless repeat_max equals repeat_min, fill in the data for EXACT,\n          and then generate the second opcode. For a repeated Unicode property\n          match, there are two extra values that define the required property,\n          and mclength is set zero to indicate this. */\n\n          if (repeat_max != repeat_min)\n            {\n            if (mclength > 0)\n              {\n              memcpy(code, mcbuffer, CU2BYTES(mclength));\n              code += mclength;\n              }\n            else\n              {\n              *code++ = op_previous;\n              if (prop_type >= 0)\n                {\n                *code++ = prop_type;\n                *code++ = prop_value;\n                }\n              }\n\n            /* Now set up the following opcode */\n\n            if (repeat_max == REPEAT_UNLIMITED)\n              *code++ = OP_STAR + repeat_type;\n            else\n              {\n              repeat_max -= repeat_min;\n              if (repeat_max == 1)\n                {\n                *code++ = OP_QUERY + repeat_type;\n                }\n              else\n                {\n                *code++ = OP_UPTO + repeat_type;\n                PUT2INC(code, 0, repeat_max);\n                }\n              }\n            }\n          }\n\n        /* Fill in the character or character type for the final opcode. */\n\n        if (mclength > 0)\n          {\n          memcpy(code, mcbuffer, CU2BYTES(mclength));\n          code += mclength;\n          }\n        else\n          {\n          *code++ = op_previous;\n          if (prop_type >= 0)\n            {\n            *code++ = prop_type;\n            *code++ = prop_value;\n            }\n          }\n        }\n      break;\n      }  /* End of switch on different op_previous values */\n\n\n    /* If the character following a repeat is '+', possessive_quantifier is\n    TRUE. For some opcodes, there are special alternative opcodes for this\n    case. For anything else, we wrap the entire repeated item inside OP_ONCE\n    brackets. Logically, the '+' notation is just syntactic sugar, taken from\n    Sun's Java package, but the special opcodes can optimize it.\n\n    Some (but not all) possessively repeated subpatterns have already been\n    completely handled in the code just above. For them, possessive_quantifier\n    is always FALSE at this stage. Note that the repeated item starts at\n    tempcode, not at previous, which might be the first part of a string whose\n    (former) last char we repeated. */\n\n    if (possessive_quantifier)\n      {\n      int len;\n\n      /* Possessifying an EXACT quantifier has no effect, so we can ignore it.\n      However, QUERY, STAR, or UPTO may follow (for quantifiers such as {5,6},\n      {5,}, or {5,10}). We skip over an EXACT item; if the length of what\n      remains is greater than zero, there's a further opcode that can be\n      handled. If not, do nothing, leaving the EXACT alone. */\n\n      switch(*tempcode)\n        {\n        case OP_TYPEEXACT:\n        tempcode += PRIV(OP_lengths)[*tempcode] +\n          ((tempcode[1 + IMM2_SIZE] == OP_PROP\n          || tempcode[1 + IMM2_SIZE] == OP_NOTPROP)? 2 : 0);\n        break;\n\n        /* CHAR opcodes are used for exacts whose count is 1. */\n\n        case OP_CHAR:\n        case OP_CHARI:\n        case OP_NOT:\n        case OP_NOTI:\n        case OP_EXACT:\n        case OP_EXACTI:\n        case OP_NOTEXACT:\n        case OP_NOTEXACTI:\n        tempcode += PRIV(OP_lengths)[*tempcode];\n#ifdef SUPPORT_UNICODE\n        if (utf && HAS_EXTRALEN(tempcode[-1]))\n          tempcode += GET_EXTRALEN(tempcode[-1]);\n#endif\n        break;\n\n        /* For the class opcodes, the repeat operator appears at the end;\n        adjust tempcode to point to it. */\n\n        case OP_CLASS:\n        case OP_NCLASS:\n        tempcode += 1 + 32/sizeof(PCRE2_UCHAR);\n        break;\n\n#ifdef SUPPORT_WIDE_CHARS\n        case OP_XCLASS:\n        case OP_ECLASS:\n        tempcode += GET(tempcode, 1);\n        break;\n#endif\n        }\n\n      /* If tempcode is equal to code (which points to the end of the repeated\n      item), it means we have skipped an EXACT item but there is no following\n      QUERY, STAR, or UPTO; the value of len will be 0, and we do nothing. In\n      all other cases, tempcode will be pointing to the repeat opcode, and will\n      be less than code, so the value of len will be greater than 0. */\n\n      len = (int)(code - tempcode);\n      if (len > 0)\n        {\n        unsigned int repcode = *tempcode;\n\n        /* There is a table for possessifying opcodes, all of which are less\n        than OP_CALLOUT. A zero entry means there is no possessified version.\n        */\n\n        if (repcode < OP_CALLOUT && opcode_possessify[repcode] > 0)\n          *tempcode = opcode_possessify[repcode];\n\n        /* For opcode without a special possessified version, wrap the item in\n        ONCE brackets. */\n\n        else\n          {\n          (void)memmove(tempcode + 1 + LINK_SIZE, tempcode, CU2BYTES(len));\n          code += 1 + LINK_SIZE;\n          len += 1 + LINK_SIZE;\n          tempcode[0] = OP_ONCE;\n          *code++ = OP_KET;\n          PUTINC(code, 0, len);\n          PUT(tempcode, 1, len);\n          }\n        }\n      }\n\n    /* We set the \"follows varying string\" flag for subsequently encountered\n    reqcus if it isn't already set and we have just passed a varying length\n    item. */\n\n    END_REPEAT:\n    cb->req_varyopt |= reqvary;\n    break;\n\n\n    /* ===================================================================*/\n    /* Handle a 32-bit data character with a value greater than META_END. */\n\n    case META_BIGVALUE:\n    pptr++;\n    goto NORMAL_CHAR;\n\n\n    /* ===============================================================*/\n    /* Handle a back reference by number, which is the meta argument. The\n    pattern offsets for back references to group numbers less than 10 are held\n    in a special vector, to avoid using more than two parsed pattern elements\n    in 64-bit environments. We only need the offset to the first occurrence,\n    because if that doesn't fail, subsequent ones will also be OK. */\n\n    case META_BACKREF:\n    if (meta_arg < 10) offset = cb->small_ref_offset[meta_arg];\n      else GETPLUSOFFSET(offset, pptr);\n\n    if (meta_arg > cb->bracount)\n      {\n      cb->erroroffset = offset;\n      *errorcodeptr = ERR15;  /* Non-existent subpattern */\n      return 0;\n      }\n\n    /* Come here from named backref handling when the reference is to a\n    single group (that is, not to a duplicated name). The back reference\n    data will have already been updated. We must disable firstcu if not\n    set, to cope with cases like (?=(\\w+))\\1: which would otherwise set ':'\n    later. */\n\n    HANDLE_SINGLE_REFERENCE:\n    if (firstcuflags == REQ_UNSET) zerofirstcuflags = firstcuflags = REQ_NONE;\n    *code++ = ((options & PCRE2_CASELESS) != 0)? OP_REFI : OP_REF;\n    PUT2INC(code, 0, meta_arg);\n    if ((options & PCRE2_CASELESS) != 0)\n      *code++ = (((xoptions & PCRE2_EXTRA_CASELESS_RESTRICT) != 0)?\n                 REFI_FLAG_CASELESS_RESTRICT : 0) |\n                (((xoptions & PCRE2_EXTRA_TURKISH_CASING) != 0)?\n                 REFI_FLAG_TURKISH_CASING : 0);\n\n    /* Update the map of back references, and keep the highest one. We\n    could do this in parse_regex() for numerical back references, but not\n    for named back references, because we don't know the numbers to which\n    named back references refer. So we do it all in this function. */\n\n    cb->backref_map |= (meta_arg < 32)? (1u << meta_arg) : 1;\n    if (meta_arg > cb->top_backref) cb->top_backref = meta_arg;\n    break;\n\n\n    /* ===============================================================*/\n    /* Handle recursion by inserting the number of the called group (which is\n    the meta argument) after OP_RECURSE. At the end of compiling the pattern is\n    scanned and these numbers are replaced by offsets within the pattern. It is\n    done like this to avoid problems with forward references and adjusting\n    offsets when groups are duplicated and moved (as discovered in previous\n    implementations). Note that a recursion does not have a set first\n    character. */\n\n    case META_RECURSE:\n    GETPLUSOFFSET(offset, pptr);\n    if (meta_arg > cb->bracount)\n      {\n      cb->erroroffset = offset;\n      *errorcodeptr = ERR15;  /* Non-existent subpattern */\n      return 0;\n      }\n    HANDLE_NUMERICAL_RECURSION:\n    *code = OP_RECURSE;\n    PUT(code, 1, meta_arg);\n    code += 1 + LINK_SIZE;\n    groupsetfirstcu = FALSE;\n    cb->had_recurse = TRUE;\n    if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE;\n    zerofirstcu = firstcu;\n    zerofirstcuflags = firstcuflags;\n    break;\n\n\n    /* ===============================================================*/\n    /* Handle capturing parentheses; the number is the meta argument. */\n\n    case META_CAPTURE:\n    bravalue = OP_CBRA;\n    skipunits = IMM2_SIZE;\n    PUT2(code, 1+LINK_SIZE, meta_arg);\n    cb->lastcapture = meta_arg;\n    goto GROUP_PROCESS_NOTE_EMPTY;\n\n\n    /* ===============================================================*/\n    /* Handle escape sequence items. For ones like \\d, the ESC_values are\n    arranged to be the same as the corresponding OP_values in the default case\n    when PCRE2_UCP is not set (which is the only case in which they will appear\n    here).\n\n    Note: \\Q and \\E are never seen here, as they were dealt with in\n    parse_pattern(). Neither are numerical back references or recursions, which\n    were turned into META_BACKREF or META_RECURSE items, respectively. \\k and\n    \\g, when followed by names, are turned into META_BACKREF_BYNAME or\n    META_RECURSE_BYNAME. */\n\n    case META_ESCAPE:\n\n    /* We can test for escape sequences that consume a character because their\n    values lie between ESC_b and ESC_Z; this may have to change if any new ones\n    are ever created. For these sequences, we disable the setting of a first\n    character if it hasn't already been set. */\n\n    if (meta_arg > ESC_b && meta_arg < ESC_Z)\n      {\n      matched_char = TRUE;\n      if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE;\n      }\n\n    /* Set values to reset to if this is followed by a zero repeat. */\n\n    zerofirstcu = firstcu;\n    zerofirstcuflags = firstcuflags;\n    zeroreqcu = reqcu;\n    zeroreqcuflags = reqcuflags;\n\n    /* If Unicode is not supported, \\P and \\p are not allowed and are\n    faulted at parse time, so will never appear here. */\n\n#ifdef SUPPORT_UNICODE\n    if (meta_arg == ESC_P || meta_arg == ESC_p)\n      {\n      uint32_t ptype = *(++pptr) >> 16;\n      uint32_t pdata = *pptr & 0xffff;\n\n      /* In caseless matching, particular characteristics Lu, Ll, and Lt get\n      converted to the general characteristic L&. That is, upper, lower, and\n      title case letters are all conflated. */\n\n      if ((options & PCRE2_CASELESS) != 0 && ptype == PT_PC &&\n          (pdata == ucp_Lu || pdata == ucp_Ll || pdata == ucp_Lt))\n        {\n        ptype = PT_LAMP;\n        pdata = 0;\n        }\n\n      /* The special case of \\p{Any} is compiled to OP_ALLANY and \\P{Any}\n      is compiled to [] so as to benefit from the auto-anchoring code. */\n\n      if (ptype == PT_ANY)\n        {\n        if (meta_arg == ESC_P)\n          {\n          *code++ = OP_CLASS;\n          memset(code, 0, 32);\n          code += 32 / sizeof(PCRE2_UCHAR);\n          }\n        else\n          *code++ = OP_ALLANY;\n        }\n      else\n        {\n        *code++ = (meta_arg == ESC_p)? OP_PROP : OP_NOTPROP;\n        *code++ = ptype;\n        *code++ = pdata;\n        }\n      break;  /* End META_ESCAPE */\n      }\n#endif\n\n    /* \\K is forbidden in lookarounds since 10.38 because that's what Perl has\n    done. However, there's an option, in case anyone was relying on it. */\n\n    if (cb->assert_depth > 0 && meta_arg == ESC_K &&\n        (xoptions & PCRE2_EXTRA_ALLOW_LOOKAROUND_BSK) == 0)\n      {\n      *errorcodeptr = ERR99;\n      return 0;\n      }\n\n    /* For the rest (including \\X when Unicode is supported - if not it's\n    faulted at parse time), the OP value is the escape value when PCRE2_UCP is\n    not set; if it is set, most of them do not show up here because they are\n    converted into Unicode property tests in parse_regex().\n\n    In non-UTF mode, and for both 32-bit modes, we turn \\C into OP_ALLANY\n    instead of OP_ANYBYTE so that it works in DFA mode and in lookbehinds.\n    There are special UCP codes for \\B and \\b which are used in UCP mode unless\n    \"word\" matching is being forced to ASCII.\n\n    Note that \\b and \\B do a one-character lookbehind, and \\A also behaves as\n    if it does. */\n\n    switch(meta_arg)\n      {\n      case ESC_C:\n      cb->external_flags |= PCRE2_HASBKC;  /* Record */\n#if PCRE2_CODE_UNIT_WIDTH == 32\n      meta_arg = OP_ALLANY;\n#else\n      if (!utf) meta_arg = OP_ALLANY;\n#endif\n      break;\n\n      case ESC_B:\n      case ESC_b:\n      if ((options & PCRE2_UCP) != 0 && (xoptions & PCRE2_EXTRA_ASCII_BSW) == 0)\n        meta_arg = (meta_arg == ESC_B)? OP_NOT_UCP_WORD_BOUNDARY :\n          OP_UCP_WORD_BOUNDARY;\n      /* Fall through */\n\n      case ESC_A:\n      if (cb->max_lookbehind == 0) cb->max_lookbehind = 1;\n      break;\n      }\n\n    *code++ = meta_arg;\n    break;  /* End META_ESCAPE */\n\n\n    /* ===================================================================*/\n    /* Handle an unrecognized meta value. A parsed pattern value less than\n    META_END is a literal. Otherwise we have a problem. */\n\n    default:\n    if (meta >= META_END)\n      {\n      PCRE2_DEBUG_UNREACHABLE();\n      *errorcodeptr = ERR89;  /* Internal error - unrecognized. */\n      return 0;\n      }\n\n    /* Handle a literal character. We come here by goto in the case of a\n    32-bit, non-UTF character whose value is greater than META_END. */\n\n    NORMAL_CHAR:\n    meta = *pptr;     /* Get the full 32 bits */\n    NORMAL_CHAR_SET:  /* Character is already in meta */\n    matched_char = TRUE;\n\n    /* For caseless UTF or UCP mode, check whether this character has more than\n    one other case. If so, generate a special OP_PROP item instead of OP_CHARI.\n    When casing restrictions apply, ignore caseless sets that start with an\n    ASCII character. If the character is affected by the special Turkish rules,\n    hardcode the matching characters using a caseset. */\n\n#ifdef SUPPORT_UNICODE\n    if ((utf||ucp) && (options & PCRE2_CASELESS) != 0)\n      {\n      uint32_t caseset;\n\n      if ((xoptions & (PCRE2_EXTRA_TURKISH_CASING|PCRE2_EXTRA_CASELESS_RESTRICT)) ==\n            PCRE2_EXTRA_TURKISH_CASING &&\n          UCD_ANY_I(meta))\n        {\n        caseset = PRIV(ucd_turkish_dotted_i_caseset) + (UCD_DOTTED_I(meta)? 0 : 3);\n        }\n      else if ((caseset = UCD_CASESET(meta)) != 0 &&\n               (xoptions & PCRE2_EXTRA_CASELESS_RESTRICT) != 0 &&\n               PRIV(ucd_caseless_sets)[caseset] < 128)\n        {\n        caseset = 0;  /* Ignore the caseless set if it's restricted. */\n        }\n\n      if (caseset != 0)\n        {\n        *code++ = OP_PROP;\n        *code++ = PT_CLIST;\n        *code++ = caseset;\n        if (firstcuflags == REQ_UNSET)\n          firstcuflags = zerofirstcuflags = REQ_NONE;\n        break;  /* End handling this meta item */\n        }\n      }\n#endif\n\n    /* Caseful matches, or caseless and not one of the multicase characters. We\n    come here by goto in the case of a positive class that contains only\n    case-partners of a character with just two cases; matched_char has already\n    been set TRUE and options fudged if necessary. */\n\n    CLASS_CASELESS_CHAR:\n\n    /* Get the character's code units into mcbuffer, with the length in\n    mclength. When not in UTF mode, the length is always 1. */\n\n#ifdef SUPPORT_UNICODE\n    if (utf) mclength = PRIV(ord2utf)(meta, mcbuffer); else\n#endif\n      {\n      mclength = 1;\n      mcbuffer[0] = meta;\n      }\n\n    /* Generate the appropriate code */\n\n    *code++ = ((options & PCRE2_CASELESS) != 0)? OP_CHARI : OP_CHAR;\n    memcpy(code, mcbuffer, CU2BYTES(mclength));\n    code += mclength;\n\n    /* Remember if \\r or \\n were seen */\n\n    if (mcbuffer[0] == CHAR_CR || mcbuffer[0] == CHAR_NL)\n      cb->external_flags |= PCRE2_HASCRORLF;\n\n    /* Set the first and required code units appropriately. If no previous\n    first code unit, set it from this character, but revert to none on a zero\n    repeat. Otherwise, leave the firstcu value alone, and don't change it on\n    a zero repeat. */\n\n    if (firstcuflags == REQ_UNSET)\n      {\n      zerofirstcuflags = REQ_NONE;\n      zeroreqcu = reqcu;\n      zeroreqcuflags = reqcuflags;\n\n      /* If the character is more than one code unit long, we can set a single\n      firstcu only if it is not to be matched caselessly. Multiple possible\n      starting code units may be picked up later in the studying code. */\n\n      if (mclength == 1 || req_caseopt == 0)\n        {\n        firstcu = mcbuffer[0];\n        firstcuflags = req_caseopt;\n        if (mclength != 1)\n          {\n          reqcu = code[-1];\n          reqcuflags = cb->req_varyopt;\n          }\n        }\n      else firstcuflags = reqcuflags = REQ_NONE;\n      }\n\n    /* firstcu was previously set; we can set reqcu only if the length is\n    1 or the matching is caseful. */\n\n    else\n      {\n      zerofirstcu = firstcu;\n      zerofirstcuflags = firstcuflags;\n      zeroreqcu = reqcu;\n      zeroreqcuflags = reqcuflags;\n      if (mclength == 1 || req_caseopt == 0)\n        {\n        reqcu = code[-1];\n        reqcuflags = req_caseopt | cb->req_varyopt;\n        }\n      }\n\n    /* If caselessness was temporarily instated, reset it. */\n\n    if (reset_caseful)\n      {\n      options &= ~PCRE2_CASELESS;\n      req_caseopt = 0;\n      reset_caseful = FALSE;\n      }\n\n    break;    /* End literal character handling */\n    }         /* End of big switch */\n  }           /* End of big loop */\n\nPCRE2_DEBUG_UNREACHABLE(); /* Control should never reach here */\nreturn 0;                  /* Avoid compiler warnings */\n}\n\n\n\n/*************************************************\n*   Compile regex: a sequence of alternatives    *\n*************************************************/\n\n/* On entry, pptr is pointing past the bracket meta, but on return it points to\nthe closing bracket or META_END. The code variable is pointing at the code unit\ninto which the BRA operator has been stored. This function is used during the\npre-compile phase when we are trying to find out the amount of memory needed,\nas well as during the real compile phase. The value of lengthptr distinguishes\nthe two phases.\n\nArguments:\n  options           option bits, including any changes for this subpattern\n  xoptions          extra option bits, ditto\n  codeptr           -> the address of the current code pointer\n  pptrptr           -> the address of the current parsed pattern pointer\n  errorcodeptr      -> pointer to error code variable\n  skipunits         skip this many code units at start (for brackets and OP_COND)\n  firstcuptr        place to put the first required code unit\n  firstcuflagsptr   place to put the first code unit flags\n  reqcuptr          place to put the last required code unit\n  reqcuflagsptr     place to put the last required code unit flags\n  bcptr             pointer to the chain of currently open branches\n  cb                points to the data block with tables pointers etc.\n  lengthptr         NULL during the real compile phase\n                    points to length accumulator during pre-compile phase\n\nReturns:            0 There has been an error\n                   +1 Success, this group must match at least one character\n                   -1 Success, this group may match an empty string\n*/\n\nstatic int\ncompile_regex(uint32_t options, uint32_t xoptions, PCRE2_UCHAR **codeptr,\n  uint32_t **pptrptr, int *errorcodeptr, uint32_t skipunits,\n  uint32_t *firstcuptr, uint32_t *firstcuflagsptr, uint32_t *reqcuptr,\n  uint32_t *reqcuflagsptr, branch_chain *bcptr, open_capitem *open_caps,\n  compile_block *cb, PCRE2_SIZE *lengthptr)\n{\nPCRE2_UCHAR *code = *codeptr;\nPCRE2_UCHAR *last_branch = code;\nPCRE2_UCHAR *start_bracket = code;\nBOOL lookbehind;\nopen_capitem capitem;\nint capnumber = 0;\nint okreturn = 1;\nuint32_t *pptr = *pptrptr;\nuint32_t firstcu, reqcu;\nuint32_t lookbehindlength;\nuint32_t lookbehindminlength;\nuint32_t firstcuflags, reqcuflags;\nPCRE2_SIZE length;\nbranch_chain bc;\n\n/* If set, call the external function that checks for stack availability. */\n\nif (cb->cx->stack_guard != NULL &&\n    cb->cx->stack_guard(cb->parens_depth, cb->cx->stack_guard_data))\n  {\n  *errorcodeptr= ERR33;\n  return 0;\n  }\n\n/* Miscellaneous initialization */\n\nbc.outer = bcptr;\nbc.current_branch = code;\n\nfirstcu = reqcu = 0;\nfirstcuflags = reqcuflags = REQ_UNSET;\n\n/* Accumulate the length for use in the pre-compile phase. Start with the\nlength of the BRA and KET and any extra code units that are required at the\nbeginning. We accumulate in a local variable to save frequent testing of\nlengthptr for NULL. We cannot do this by looking at the value of 'code' at the\nstart and end of each alternative, because compiled items are discarded during\nthe pre-compile phase so that the workspace is not exceeded. */\n\nlength = 2 + 2*LINK_SIZE + skipunits;\n\n/* Remember if this is a lookbehind assertion, and if it is, save its length\nand skip over the pattern offset. */\n\nlookbehind = *code == OP_ASSERTBACK ||\n             *code == OP_ASSERTBACK_NOT ||\n             *code == OP_ASSERTBACK_NA;\n\nif (lookbehind)\n  {\n  lookbehindlength = META_DATA(pptr[-1]);\n  lookbehindminlength = *pptr;\n  pptr += SIZEOFFSET;\n  }\nelse lookbehindlength = lookbehindminlength = 0;\n\n/* If this is a capturing subpattern, add to the chain of open capturing items\nso that we can detect them if (*ACCEPT) is encountered. Note that only OP_CBRA\nneed be tested here; changing this opcode to one of its variants, e.g.\nOP_SCBRAPOS, happens later, after the group has been compiled. */\n\nif (*code == OP_CBRA)\n  {\n  capnumber = GET2(code, 1 + LINK_SIZE);\n  capitem.number = capnumber;\n  capitem.next = open_caps;\n  capitem.assert_depth = cb->assert_depth;\n  open_caps = &capitem;\n  }\n\n/* Offset is set zero to mark that this bracket is still open */\n\nPUT(code, 1, 0);\ncode += 1 + LINK_SIZE + skipunits;\n\n/* Loop for each alternative branch */\n\nfor (;;)\n  {\n  int branch_return;\n  uint32_t branchfirstcu = 0, branchreqcu = 0;\n  uint32_t branchfirstcuflags = REQ_UNSET, branchreqcuflags = REQ_UNSET;\n\n  /* Insert OP_REVERSE or OP_VREVERSE if this is a lookbehind assertion. There\n  is only a single minimum length for the whole assertion. When the minimum\n  length is LOOKBEHIND_MAX it means that all branches are of fixed length,\n  though not necessarily the same length. In this case, the original OP_REVERSE\n  can be used. It can also be used if a branch in a variable length lookbehind\n  has the same maximum and minimum. Otherwise, use OP_VREVERSE, which has both\n  maximum and minimum values. */\n\n  if (lookbehind && lookbehindlength > 0)\n    {\n    if (lookbehindminlength == LOOKBEHIND_MAX ||\n        lookbehindminlength == lookbehindlength)\n      {\n      *code++ = OP_REVERSE;\n      PUT2INC(code, 0, lookbehindlength);\n      length += 1 + IMM2_SIZE;\n      }\n    else\n      {\n      *code++ = OP_VREVERSE;\n      PUT2INC(code, 0, lookbehindminlength);\n      PUT2INC(code, 0, lookbehindlength);\n      length += 1 + 2*IMM2_SIZE;\n      }\n    }\n\n  /* Now compile the branch; in the pre-compile phase its length gets added\n  into the length. */\n\n  if ((branch_return =\n        compile_branch(&options, &xoptions, &code, &pptr, errorcodeptr,\n          &branchfirstcu, &branchfirstcuflags, &branchreqcu, &branchreqcuflags,\n          &bc, open_caps, cb, (lengthptr == NULL)? NULL : &length)) == 0)\n    return 0;\n\n  /* If a branch can match an empty string, so can the whole group. */\n\n  if (branch_return < 0) okreturn = -1;\n\n  /* In the real compile phase, there is some post-processing to be done. */\n\n  if (lengthptr == NULL)\n    {\n    /* If this is the first branch, the firstcu and reqcu values for the\n    branch become the values for the regex. */\n\n    if (*last_branch != OP_ALT)\n      {\n      firstcu = branchfirstcu;\n      firstcuflags = branchfirstcuflags;\n      reqcu = branchreqcu;\n      reqcuflags = branchreqcuflags;\n      }\n\n    /* If this is not the first branch, the first char and reqcu have to\n    match the values from all the previous branches, except that if the\n    previous value for reqcu didn't have REQ_VARY set, it can still match,\n    and we set REQ_VARY for the group from this branch's value. */\n\n    else\n      {\n      /* If we previously had a firstcu, but it doesn't match the new branch,\n      we have to abandon the firstcu for the regex, but if there was\n      previously no reqcu, it takes on the value of the old firstcu. */\n\n      if (firstcuflags != branchfirstcuflags || firstcu != branchfirstcu)\n        {\n        if (firstcuflags < REQ_NONE)\n          {\n          if (reqcuflags >= REQ_NONE)\n            {\n            reqcu = firstcu;\n            reqcuflags = firstcuflags;\n            }\n          }\n        firstcuflags = REQ_NONE;\n        }\n\n      /* If we (now or from before) have no firstcu, a firstcu from the\n      branch becomes a reqcu if there isn't a branch reqcu. */\n\n      if (firstcuflags >= REQ_NONE && branchfirstcuflags < REQ_NONE &&\n          branchreqcuflags >= REQ_NONE)\n        {\n        branchreqcu = branchfirstcu;\n        branchreqcuflags = branchfirstcuflags;\n        }\n\n      /* Now ensure that the reqcus match */\n\n      if (((reqcuflags & ~REQ_VARY) != (branchreqcuflags & ~REQ_VARY)) ||\n          reqcu != branchreqcu)\n        reqcuflags = REQ_NONE;\n      else\n        {\n        reqcu = branchreqcu;\n        reqcuflags |= branchreqcuflags; /* To \"or\" REQ_VARY if present */\n        }\n      }\n    }\n\n  /* Handle reaching the end of the expression, either ')' or end of pattern.\n  In the real compile phase, go back through the alternative branches and\n  reverse the chain of offsets, with the field in the BRA item now becoming an\n  offset to the first alternative. If there are no alternatives, it points to\n  the end of the group. The length in the terminating ket is always the length\n  of the whole bracketed item. Return leaving the pointer at the terminating\n  char. */\n\n  if (META_CODE(*pptr) != META_ALT)\n    {\n    if (lengthptr == NULL)\n      {\n      uint32_t branch_length = (uint32_t)(code - last_branch);\n      do\n        {\n        uint32_t prev_length = GET(last_branch, 1);\n        PUT(last_branch, 1, branch_length);\n        branch_length = prev_length;\n        last_branch -= branch_length;\n        }\n      while (branch_length > 0);\n      }\n\n    /* Fill in the ket */\n\n    *code = OP_KET;\n    PUT(code, 1, (uint32_t)(code - start_bracket));\n    code += 1 + LINK_SIZE;\n\n    /* Set values to pass back */\n\n    *codeptr = code;\n    *pptrptr = pptr;\n    *firstcuptr = firstcu;\n    *firstcuflagsptr = firstcuflags;\n    *reqcuptr = reqcu;\n    *reqcuflagsptr = reqcuflags;\n    if (lengthptr != NULL)\n      {\n      if (OFLOW_MAX - *lengthptr < length)\n        {\n        *errorcodeptr = ERR20;\n        return 0;\n        }\n      *lengthptr += length;\n      }\n    return okreturn;\n    }\n\n  /* Another branch follows. In the pre-compile phase, we can move the code\n  pointer back to where it was for the start of the first branch. (That is,\n  pretend that each branch is the only one.)\n\n  In the real compile phase, insert an ALT node. Its length field points back\n  to the previous branch while the bracket remains open. At the end the chain\n  is reversed. It's done like this so that the start of the bracket has a\n  zero offset until it is closed, making it possible to detect recursion. */\n\n  if (lengthptr != NULL)\n    {\n    code = *codeptr + 1 + LINK_SIZE + skipunits;\n    length += 1 + LINK_SIZE;\n    }\n  else\n    {\n    *code = OP_ALT;\n    PUT(code, 1, (int)(code - last_branch));\n    bc.current_branch = last_branch = code;\n    code += 1 + LINK_SIZE;\n    }\n\n  /* Set the maximum lookbehind length for the next branch (if not in a\n  lookbehind the value will be zero) and then advance past the vertical bar. */\n\n  lookbehindlength = META_DATA(*pptr);\n  pptr++;\n  }\n\nPCRE2_DEBUG_UNREACHABLE(); /* Control should never reach here */\nreturn 0;                  /* Avoid compiler warnings */\n}\n\n\n\n/*************************************************\n*          Check for anchored pattern            *\n*************************************************/\n\n/* Try to find out if this is an anchored regular expression. Consider each\nalternative branch. If they all start with OP_SOD or OP_CIRC, or with a bracket\nall of whose alternatives start with OP_SOD or OP_CIRC (recurse ad lib), then\nit's anchored. However, if this is a multiline pattern, then only OP_SOD will\nbe found, because ^ generates OP_CIRCM in that mode.\n\nWe can also consider a regex to be anchored if OP_SOM starts all its branches.\nThis is the code for \\G, which means \"match at start of match position, taking\ninto account the match offset\".\n\nA branch is also implicitly anchored if it starts with .* and DOTALL is set,\nbecause that will try the rest of the pattern at all possible matching points,\nso there is no point trying again.... er ....\n\n.... except when the .* appears inside capturing parentheses, and there is a\nsubsequent back reference to those parentheses. We haven't enough information\nto catch that case precisely.\n\nAt first, the best we could do was to detect when .* was in capturing brackets\nand the highest back reference was greater than or equal to that level.\nHowever, by keeping a bitmap of the first 31 back references, we can catch some\nof the more common cases more precisely.\n\n... A second exception is when the .* appears inside an atomic group, because\nthis prevents the number of characters it matches from being adjusted.\n\nArguments:\n  code           points to start of the compiled pattern\n  bracket_map    a bitmap of which brackets we are inside while testing; this\n                   handles up to substring 31; after that we just have to take\n                   the less precise approach\n  cb             points to the compile data block\n  atomcount      atomic group level\n  inassert       TRUE if in an assertion\n  dotstar_anchor TRUE if automatic anchoring optimization is enabled\n\nReturns:     TRUE or FALSE\n*/\n\nstatic BOOL\nis_anchored(PCRE2_SPTR code, uint32_t bracket_map, compile_block *cb,\n  int atomcount, BOOL inassert, BOOL dotstar_anchor)\n{\ndo {\n   PCRE2_SPTR scode = first_significant_code(\n     code + PRIV(OP_lengths)[*code], FALSE);\n   int op = *scode;\n\n   /* Non-capturing brackets */\n\n   if (op == OP_BRA  || op == OP_BRAPOS ||\n       op == OP_SBRA || op == OP_SBRAPOS)\n     {\n     if (!is_anchored(scode, bracket_map, cb, atomcount, inassert, dotstar_anchor))\n       return FALSE;\n     }\n\n   /* Capturing brackets */\n\n   else if (op == OP_CBRA  || op == OP_CBRAPOS ||\n            op == OP_SCBRA || op == OP_SCBRAPOS)\n     {\n     int n = GET2(scode, 1+LINK_SIZE);\n     uint32_t new_map = bracket_map | ((n < 32)? (1u << n) : 1);\n     if (!is_anchored(scode, new_map, cb, atomcount, inassert, dotstar_anchor)) return FALSE;\n     }\n\n   /* Positive forward assertion */\n\n   else if (op == OP_ASSERT || op == OP_ASSERT_NA)\n     {\n     if (!is_anchored(scode, bracket_map, cb, atomcount, TRUE, dotstar_anchor)) return FALSE;\n     }\n\n   /* Condition. If there is no second branch, it can't be anchored. */\n\n   else if (op == OP_COND || op == OP_SCOND)\n     {\n     if (scode[GET(scode,1)] != OP_ALT) return FALSE;\n     if (!is_anchored(scode, bracket_map, cb, atomcount, inassert, dotstar_anchor))\n       return FALSE;\n     }\n\n   /* Atomic groups */\n\n   else if (op == OP_ONCE)\n     {\n     if (!is_anchored(scode, bracket_map, cb, atomcount + 1, inassert, dotstar_anchor))\n       return FALSE;\n     }\n\n   /* .* is not anchored unless DOTALL is set (which generates OP_ALLANY) and\n   it isn't in brackets that are or may be referenced or inside an atomic\n   group or an assertion. Also the pattern must not contain *PRUNE or *SKIP,\n   because these break the feature. Consider, for example, /(?s).*?(*PRUNE)b/\n   with the subject \"aab\", which matches \"b\", i.e. not at the start of a line.\n   There is also an option that disables auto-anchoring. */\n\n   else if ((op == OP_TYPESTAR || op == OP_TYPEMINSTAR ||\n             op == OP_TYPEPOSSTAR))\n     {\n     if (scode[1] != OP_ALLANY || (bracket_map & cb->backref_map) != 0 ||\n         atomcount > 0 || cb->had_pruneorskip || inassert || !dotstar_anchor)\n       return FALSE;\n     }\n\n   /* Check for explicit anchoring */\n\n   else if (op != OP_SOD && op != OP_SOM && op != OP_CIRC) return FALSE;\n\n   code += GET(code, 1);\n   }\nwhile (*code == OP_ALT);   /* Loop for each alternative */\nreturn TRUE;\n}\n\n\n\n/*************************************************\n*         Check for starting with ^ or .*        *\n*************************************************/\n\n/* This is called to find out if every branch starts with ^ or .* so that\n\"first char\" processing can be done to speed things up in multiline\nmatching and for non-DOTALL patterns that start with .* (which must start at\nthe beginning or after \\n). As in the case of is_anchored() (see above), we\nhave to take account of back references to capturing brackets that contain .*\nbecause in that case we can't make the assumption. Also, the appearance of .*\ninside atomic brackets or in an assertion, or in a pattern that contains *PRUNE\nor *SKIP does not count, because once again the assumption no longer holds.\n\nArguments:\n  code           points to start of the compiled pattern or a group\n  bracket_map    a bitmap of which brackets we are inside while testing; this\n                   handles up to substring 31; after that we just have to take\n                   the less precise approach\n  cb             points to the compile data\n  atomcount      atomic group level\n  inassert       TRUE if in an assertion\n  dotstar_anchor TRUE if automatic anchoring optimization is enabled\n\nReturns:         TRUE or FALSE\n*/\n\nstatic BOOL\nis_startline(PCRE2_SPTR code, unsigned int bracket_map, compile_block *cb,\n  int atomcount, BOOL inassert, BOOL dotstar_anchor)\n{\ndo {\n   PCRE2_SPTR scode = first_significant_code(\n     code + PRIV(OP_lengths)[*code], FALSE);\n   int op = *scode;\n\n   /* If we are at the start of a conditional assertion group, *both* the\n   conditional assertion *and* what follows the condition must satisfy the test\n   for start of line. Other kinds of condition fail. Note that there may be an\n   auto-callout at the start of a condition. */\n\n   if (op == OP_COND)\n     {\n     scode += 1 + LINK_SIZE;\n\n     if (*scode == OP_CALLOUT) scode += PRIV(OP_lengths)[OP_CALLOUT];\n       else if (*scode == OP_CALLOUT_STR) scode += GET(scode, 1 + 2*LINK_SIZE);\n\n     switch (*scode)\n       {\n       case OP_CREF:\n       case OP_DNCREF:\n       case OP_RREF:\n       case OP_DNRREF:\n       case OP_FAIL:\n       case OP_FALSE:\n       case OP_TRUE:\n       return FALSE;\n\n       default:     /* Assertion */\n       if (!is_startline(scode, bracket_map, cb, atomcount, TRUE, dotstar_anchor))\n         return FALSE;\n       do scode += GET(scode, 1); while (*scode == OP_ALT);\n       scode += 1 + LINK_SIZE;\n       break;\n       }\n     scode = first_significant_code(scode, FALSE);\n     op = *scode;\n     }\n\n   /* Non-capturing brackets */\n\n   if (op == OP_BRA  || op == OP_BRAPOS ||\n       op == OP_SBRA || op == OP_SBRAPOS)\n     {\n     if (!is_startline(scode, bracket_map, cb, atomcount, inassert, dotstar_anchor))\n       return FALSE;\n     }\n\n   /* Capturing brackets */\n\n   else if (op == OP_CBRA  || op == OP_CBRAPOS ||\n            op == OP_SCBRA || op == OP_SCBRAPOS)\n     {\n     int n = GET2(scode, 1+LINK_SIZE);\n     unsigned int new_map = bracket_map | ((n < 32)? (1u << n) : 1);\n     if (!is_startline(scode, new_map, cb, atomcount, inassert, dotstar_anchor))\n       return FALSE;\n     }\n\n   /* Positive forward assertions */\n\n   else if (op == OP_ASSERT || op == OP_ASSERT_NA)\n     {\n     if (!is_startline(scode, bracket_map, cb, atomcount, TRUE, dotstar_anchor))\n       return FALSE;\n     }\n\n   /* Atomic brackets */\n\n   else if (op == OP_ONCE)\n     {\n     if (!is_startline(scode, bracket_map, cb, atomcount + 1, inassert, dotstar_anchor))\n       return FALSE;\n     }\n\n   /* .* means \"start at start or after \\n\" if it isn't in atomic brackets or\n   brackets that may be referenced or an assertion, and as long as the pattern\n   does not contain *PRUNE or *SKIP, because these break the feature. Consider,\n   for example, /.*?a(*PRUNE)b/ with the subject \"aab\", which matches \"ab\",\n   i.e. not at the start of a line. There is also an option that disables this\n   optimization. */\n\n   else if (op == OP_TYPESTAR || op == OP_TYPEMINSTAR || op == OP_TYPEPOSSTAR)\n     {\n     if (scode[1] != OP_ANY || (bracket_map & cb->backref_map) != 0 ||\n         atomcount > 0 || cb->had_pruneorskip || inassert || !dotstar_anchor)\n       return FALSE;\n     }\n\n   /* Check for explicit circumflex; anything else gives a FALSE result. Note\n   in particular that this includes atomic brackets OP_ONCE because the number\n   of characters matched by .* cannot be adjusted inside them. */\n\n   else if (op != OP_CIRC && op != OP_CIRCM) return FALSE;\n\n   /* Move on to the next alternative */\n\n   code += GET(code, 1);\n   }\nwhile (*code == OP_ALT);  /* Loop for each alternative */\nreturn TRUE;\n}\n\n\n\n/*************************************************\n*   Scan compiled regex for recursion reference  *\n*************************************************/\n\n/* This function scans through a compiled pattern until it finds an instance of\nOP_RECURSE.\n\nArguments:\n  code        points to start of expression\n  utf         TRUE in UTF mode\n\nReturns:      pointer to the opcode for OP_RECURSE, or NULL if not found\n*/\n\nstatic PCRE2_UCHAR *\nfind_recurse(PCRE2_UCHAR *code, BOOL utf)\n{\nfor (;;)\n  {\n  PCRE2_UCHAR c = *code;\n  if (c == OP_END) return NULL;\n  if (c == OP_RECURSE) return code;\n\n  /* XCLASS is used for classes that cannot be represented just by a bit map.\n  This includes negated single high-valued characters. ECLASS is used for\n  classes that use set operations internally. CALLOUT_STR is used for\n  callouts with string arguments. In each case the length in the table is\n  zero; the actual length is stored in the compiled code. */\n\n  if (c == OP_XCLASS || c == OP_ECLASS) code += GET(code, 1);\n  else if (c == OP_CALLOUT_STR) code += GET(code, 1 + 2*LINK_SIZE);\n\n  /* Otherwise, we can get the item's length from the table, except that for\n  repeated character types, we have to test for \\p and \\P, which have an extra\n  two code units of parameters, and for MARK/PRUNE/SKIP/THEN with an argument,\n  we must add in its length. */\n\n  else\n    {\n    switch(c)\n      {\n      case OP_TYPESTAR:\n      case OP_TYPEMINSTAR:\n      case OP_TYPEPLUS:\n      case OP_TYPEMINPLUS:\n      case OP_TYPEQUERY:\n      case OP_TYPEMINQUERY:\n      case OP_TYPEPOSSTAR:\n      case OP_TYPEPOSPLUS:\n      case OP_TYPEPOSQUERY:\n      if (code[1] == OP_PROP || code[1] == OP_NOTPROP) code += 2;\n      break;\n\n      case OP_TYPEPOSUPTO:\n      case OP_TYPEUPTO:\n      case OP_TYPEMINUPTO:\n      case OP_TYPEEXACT:\n      if (code[1 + IMM2_SIZE] == OP_PROP || code[1 + IMM2_SIZE] == OP_NOTPROP)\n        code += 2;\n      break;\n\n      case OP_MARK:\n      case OP_COMMIT_ARG:\n      case OP_PRUNE_ARG:\n      case OP_SKIP_ARG:\n      case OP_THEN_ARG:\n      code += code[1];\n      break;\n      }\n\n    /* Add in the fixed length from the table */\n\n    code += PRIV(OP_lengths)[c];\n\n    /* In UTF-8 and UTF-16 modes, opcodes that are followed by a character may\n    be followed by a multi-unit character. The length in the table is a\n    minimum, so we have to arrange to skip the extra units. */\n\n#ifdef MAYBE_UTF_MULTI\n    if (utf) switch(c)\n      {\n      case OP_CHAR:\n      case OP_CHARI:\n      case OP_NOT:\n      case OP_NOTI:\n      case OP_EXACT:\n      case OP_EXACTI:\n      case OP_NOTEXACT:\n      case OP_NOTEXACTI:\n      case OP_UPTO:\n      case OP_UPTOI:\n      case OP_NOTUPTO:\n      case OP_NOTUPTOI:\n      case OP_MINUPTO:\n      case OP_MINUPTOI:\n      case OP_NOTMINUPTO:\n      case OP_NOTMINUPTOI:\n      case OP_POSUPTO:\n      case OP_POSUPTOI:\n      case OP_NOTPOSUPTO:\n      case OP_NOTPOSUPTOI:\n      case OP_STAR:\n      case OP_STARI:\n      case OP_NOTSTAR:\n      case OP_NOTSTARI:\n      case OP_MINSTAR:\n      case OP_MINSTARI:\n      case OP_NOTMINSTAR:\n      case OP_NOTMINSTARI:\n      case OP_POSSTAR:\n      case OP_POSSTARI:\n      case OP_NOTPOSSTAR:\n      case OP_NOTPOSSTARI:\n      case OP_PLUS:\n      case OP_PLUSI:\n      case OP_NOTPLUS:\n      case OP_NOTPLUSI:\n      case OP_MINPLUS:\n      case OP_MINPLUSI:\n      case OP_NOTMINPLUS:\n      case OP_NOTMINPLUSI:\n      case OP_POSPLUS:\n      case OP_POSPLUSI:\n      case OP_NOTPOSPLUS:\n      case OP_NOTPOSPLUSI:\n      case OP_QUERY:\n      case OP_QUERYI:\n      case OP_NOTQUERY:\n      case OP_NOTQUERYI:\n      case OP_MINQUERY:\n      case OP_MINQUERYI:\n      case OP_NOTMINQUERY:\n      case OP_NOTMINQUERYI:\n      case OP_POSQUERY:\n      case OP_POSQUERYI:\n      case OP_NOTPOSQUERY:\n      case OP_NOTPOSQUERYI:\n      if (HAS_EXTRALEN(code[-1])) code += GET_EXTRALEN(code[-1]);\n      break;\n      }\n#else\n    (void)(utf);  /* Keep compiler happy by referencing function argument */\n#endif  /* MAYBE_UTF_MULTI */\n    }\n  }\n}\n\n\n\n/*************************************************\n*    Check for asserted fixed first code unit    *\n*************************************************/\n\n/* During compilation, the \"first code unit\" settings from forward assertions\nare discarded, because they can cause conflicts with actual literals that\nfollow. However, if we end up without a first code unit setting for an\nunanchored pattern, it is worth scanning the regex to see if there is an\ninitial asserted first code unit. If all branches start with the same asserted\ncode unit, or with a non-conditional bracket all of whose alternatives start\nwith the same asserted code unit (recurse ad lib), then we return that code\nunit, with the flags set to zero or REQ_CASELESS; otherwise return zero with\nREQ_NONE in the flags.\n\nArguments:\n  code       points to start of compiled pattern\n  flags      points to the first code unit flags\n  inassert   non-zero if in an assertion\n\nReturns:     the fixed first code unit, or 0 with REQ_NONE in flags\n*/\n\nstatic uint32_t\nfind_firstassertedcu(PCRE2_SPTR code, uint32_t *flags, uint32_t inassert)\n{\nuint32_t c = 0;\nuint32_t cflags = REQ_NONE;\n\n*flags = REQ_NONE;\ndo {\n   uint32_t d;\n   uint32_t dflags;\n   int xl = (*code == OP_CBRA || *code == OP_SCBRA ||\n             *code == OP_CBRAPOS || *code == OP_SCBRAPOS)? IMM2_SIZE:0;\n   PCRE2_SPTR scode = first_significant_code(code + 1+LINK_SIZE + xl, TRUE);\n   PCRE2_UCHAR op = *scode;\n\n   switch(op)\n     {\n     default:\n     return 0;\n\n     case OP_BRA:\n     case OP_BRAPOS:\n     case OP_CBRA:\n     case OP_SCBRA:\n     case OP_CBRAPOS:\n     case OP_SCBRAPOS:\n     case OP_ASSERT:\n     case OP_ASSERT_NA:\n     case OP_ONCE:\n     case OP_SCRIPT_RUN:\n     d = find_firstassertedcu(scode, &dflags, inassert +\n       ((op == OP_ASSERT || op == OP_ASSERT_NA)?1:0));\n     if (dflags >= REQ_NONE) return 0;\n     if (cflags >= REQ_NONE) { c = d; cflags = dflags; }\n       else if (c != d || cflags != dflags) return 0;\n     break;\n\n     case OP_EXACT:\n     scode += IMM2_SIZE;\n     /* Fall through */\n\n     case OP_CHAR:\n     case OP_PLUS:\n     case OP_MINPLUS:\n     case OP_POSPLUS:\n     if (inassert == 0) return 0;\n     if (cflags >= REQ_NONE) { c = scode[1]; cflags = 0; }\n       else if (c != scode[1]) return 0;\n     break;\n\n     case OP_EXACTI:\n     scode += IMM2_SIZE;\n     /* Fall through */\n\n     case OP_CHARI:\n     case OP_PLUSI:\n     case OP_MINPLUSI:\n     case OP_POSPLUSI:\n     if (inassert == 0) return 0;\n\n     /* If the character is more than one code unit long, we cannot set its\n     first code unit when matching caselessly. Later scanning may pick up\n     multiple code units. */\n\n#ifdef SUPPORT_UNICODE\n#if PCRE2_CODE_UNIT_WIDTH == 8\n     if (scode[1] >= 0x80) return 0;\n#elif PCRE2_CODE_UNIT_WIDTH == 16\n     if (scode[1] >= 0xd800 && scode[1] <= 0xdfff) return 0;\n#endif\n#endif\n\n     if (cflags >= REQ_NONE) { c = scode[1]; cflags = REQ_CASELESS; }\n       else if (c != scode[1]) return 0;\n     break;\n     }\n\n   code += GET(code, 1);\n   }\nwhile (*code == OP_ALT);\n\n*flags = cflags;\nreturn c;\n}\n\n\n\n/*************************************************\n*     Add an entry to the name/number table      *\n*************************************************/\n\n/* This function is called between compiling passes to add an entry to the\nname/number table, maintaining alphabetical order. Checking for permitted\nand forbidden duplicates has already been done.\n\nArguments:\n  cb           the compile data block\n  name         the name to add\n  length       the length of the name\n  groupno      the group number\n  tablecount   the count of names in the table so far\n\nReturns:       nothing\n*/\n\nstatic void\nadd_name_to_table(compile_block *cb, PCRE2_SPTR name, int length,\n  unsigned int groupno, uint32_t tablecount)\n{\nuint32_t i;\nPCRE2_UCHAR *slot = cb->name_table;\n\nfor (i = 0; i < tablecount; i++)\n  {\n  int crc = memcmp(name, slot+IMM2_SIZE, CU2BYTES(length));\n  if (crc == 0 && slot[IMM2_SIZE+length] != 0)\n    crc = -1; /* Current name is a substring */\n\n  /* Make space in the table and break the loop for an earlier name. For a\n  duplicate or later name, carry on. We do this for duplicates so that in the\n  simple case (when ?(| is not used) they are in order of their numbers. In all\n  cases they are in the order in which they appear in the pattern. */\n\n  if (crc < 0)\n    {\n    (void)memmove(slot + cb->name_entry_size, slot,\n      CU2BYTES((tablecount - i) * cb->name_entry_size));\n    break;\n    }\n\n  /* Continue the loop for a later or duplicate name */\n\n  slot += cb->name_entry_size;\n  }\n\nPUT2(slot, 0, groupno);\nmemcpy(slot + IMM2_SIZE, name, CU2BYTES(length));\n\n/* Add a terminating zero and fill the rest of the slot with zeroes so that\nthe memory is all initialized. Otherwise valgrind moans about uninitialized\nmemory when saving serialized compiled patterns. */\n\nmemset(slot + IMM2_SIZE + length, 0,\n  CU2BYTES(cb->name_entry_size - length - IMM2_SIZE));\n}\n\n\n\n/*************************************************\n*             Skip in parsed pattern             *\n*************************************************/\n\n/* This function is called to skip parts of the parsed pattern when finding the\nlength of a lookbehind branch. It is called after (*ACCEPT) and (*FAIL) to find\nthe end of the branch, it is called to skip over an internal lookaround or\n(DEFINE) group, and it is also called to skip to the end of a class, during\nwhich it will never encounter nested groups (but there's no need to have\nspecial code for that).\n\nWhen called to find the end of a branch or group, pptr must point to the first\nmeta code inside the branch, not the branch-starting code. In other cases it\ncan point to the item that causes the function to be called.\n\nArguments:\n  pptr       current pointer to skip from\n  skiptype   PSKIP_CLASS when skipping to end of class\n             PSKIP_ALT when META_ALT ends the skip\n             PSKIP_KET when only META_KET ends the skip\n\nReturns:     new value of pptr\n             NULL if META_END is reached - should never occur\n               or for an unknown meta value - likewise\n*/\n\nstatic uint32_t *\nparsed_skip(uint32_t *pptr, uint32_t skiptype)\n{\nuint32_t nestlevel = 0;\n\nfor (;; pptr++)\n  {\n  uint32_t meta = META_CODE(*pptr);\n\n  switch(meta)\n    {\n    default:  /* Just skip over most items */\n    if (meta < META_END) continue;  /* Literal */\n    break;\n\n    case META_END:\n\n    /* The parsed regex is malformed; we have reached the end and did\n    not find the end of the construct which we are skipping over. */\n\n    PCRE2_DEBUG_UNREACHABLE();\n    return NULL;\n\n    /* The data for these items is variable in length. */\n\n    case META_BACKREF:  /* Offset is present only if group >= 10 */\n    if (META_DATA(*pptr) >= 10) pptr += SIZEOFFSET;\n    break;\n\n    case META_ESCAPE:\n    if (*pptr - META_ESCAPE == ESC_P || *pptr - META_ESCAPE == ESC_p)\n      pptr += 1;     /* Skip prop data */\n    break;\n\n    case META_MARK:     /* Add the length of the name. */\n    case META_COMMIT_ARG:\n    case META_PRUNE_ARG:\n    case META_SKIP_ARG:\n    case META_THEN_ARG:\n    pptr += pptr[1];\n    break;\n\n    /* These are the \"active\" items in this loop. */\n\n    case META_CLASS_END:\n    if (skiptype == PSKIP_CLASS) return pptr;\n    break;\n\n    case META_ATOMIC:\n    case META_CAPTURE:\n    case META_COND_ASSERT:\n    case META_COND_DEFINE:\n    case META_COND_NAME:\n    case META_COND_NUMBER:\n    case META_COND_RNAME:\n    case META_COND_RNUMBER:\n    case META_COND_VERSION:\n    case META_SCS:\n    case META_LOOKAHEAD:\n    case META_LOOKAHEADNOT:\n    case META_LOOKAHEAD_NA:\n    case META_LOOKBEHIND:\n    case META_LOOKBEHINDNOT:\n    case META_LOOKBEHIND_NA:\n    case META_NOCAPTURE:\n    case META_SCRIPT_RUN:\n    nestlevel++;\n    break;\n\n    case META_ALT:\n    if (nestlevel == 0 && skiptype == PSKIP_ALT) return pptr;\n    break;\n\n    case META_KET:\n    if (nestlevel == 0) return pptr;\n    nestlevel--;\n    break;\n    }\n\n  /* The extra data item length for each meta is in a table. */\n\n  meta = (meta >> 16) & 0x7fff;\n  if (meta >= sizeof(meta_extra_lengths)) return NULL;\n  pptr += meta_extra_lengths[meta];\n  }\n\nPCRE2_UNREACHABLE(); /* Control never reaches here */\n}\n\n\n\n/*************************************************\n*       Find length of a parsed group            *\n*************************************************/\n\n/* This is called for nested groups within a branch of a lookbehind whose\nlength is being computed. On entry, the pointer must be at the first element\nafter the group initializing code. On exit it points to OP_KET. Caching is used\nto improve processing speed when the same capturing group occurs many times.\n\nArguments:\n  pptrptr     pointer to pointer in the parsed pattern\n  minptr      where to return the minimum length\n  isinline    FALSE if a reference or recursion; TRUE for inline group\n  errcodeptr  pointer to the errorcode\n  lcptr       pointer to the loop counter\n  group       number of captured group or -1 for a non-capturing group\n  recurses    chain of recurse_check to catch mutual recursion\n  cb          pointer to the compile data\n\nReturns:      the maximum group length or a negative number\n*/\n\nstatic int\nget_grouplength(uint32_t **pptrptr, int *minptr, BOOL isinline, int *errcodeptr,\n  int *lcptr, int group, parsed_recurse_check *recurses, compile_block *cb)\n{\nuint32_t *gi = cb->groupinfo + 2 * group;\nint branchlength, branchminlength;\nint grouplength = -1;\nint groupminlength = INT_MAX;\n\n/* The cache can be used only if there is no possibility of there being two\ngroups with the same number. We do not need to set the end pointer for a group\nthat is being processed as a back reference or recursion, but we must do so for\nan inline group. */\n\nif (group > 0 && (cb->external_flags & PCRE2_DUPCAPUSED) == 0)\n  {\n  uint32_t groupinfo = gi[0];\n  if ((groupinfo & GI_NOT_FIXED_LENGTH) != 0) return -1;\n  if ((groupinfo & GI_SET_FIXED_LENGTH) != 0)\n    {\n    if (isinline) *pptrptr = parsed_skip(*pptrptr, PSKIP_KET);\n    *minptr = gi[1];\n    return groupinfo & GI_FIXED_LENGTH_MASK;\n    }\n  }\n\n/* Scan the group. In this case we find the end pointer of necessity. */\n\nfor(;;)\n  {\n  branchlength = get_branchlength(pptrptr, &branchminlength, errcodeptr, lcptr,\n    recurses, cb);\n  if (branchlength < 0) goto ISNOTFIXED;\n  if (branchlength > grouplength) grouplength = branchlength;\n  if (branchminlength < groupminlength) groupminlength = branchminlength;\n  if (**pptrptr == META_KET) break;\n  *pptrptr += 1;   /* Skip META_ALT */\n  }\n\nif (group > 0)\n  {\n  gi[0] |= (uint32_t)(GI_SET_FIXED_LENGTH | grouplength);\n  gi[1] = groupminlength;\n  }\n\n*minptr = groupminlength;\nreturn grouplength;\n\nISNOTFIXED:\nif (group > 0) gi[0] |= GI_NOT_FIXED_LENGTH;\nreturn -1;\n}\n\n\n\n/*************************************************\n*        Find length of a parsed branch          *\n*************************************************/\n\n/* Return fixed maximum and minimum lengths for a branch in a lookbehind,\ngiving an error if the length is not limited. On entry, *pptrptr points to the\nfirst element inside the branch. On exit it is set to point to the ALT or KET.\n\nArguments:\n  pptrptr     pointer to pointer in the parsed pattern\n  minptr      where to return the minimum length\n  errcodeptr  pointer to error code\n  lcptr       pointer to loop counter\n  recurses    chain of recurse_check to catch mutual recursion\n  cb          pointer to compile block\n\nReturns:      the maximum length, or a negative value on error\n*/\n\nstatic int\nget_branchlength(uint32_t **pptrptr, int *minptr, int *errcodeptr, int *lcptr,\n  parsed_recurse_check *recurses, compile_block *cb)\n{\nint branchlength = 0;\nint branchminlength = 0;\nint grouplength, groupminlength;\nuint32_t lastitemlength = 0;\nuint32_t lastitemminlength = 0;\nuint32_t *pptr = *pptrptr;\nPCRE2_SIZE offset;\nparsed_recurse_check this_recurse;\n\n/* A large and/or complex regex can take too long to process. This can happen\nmore often when (?| groups are present in the pattern because their length\ncannot be cached. */\n\nif ((*lcptr)++ > 2000)\n  {\n  *errcodeptr = ERR35;  /* Lookbehind is too complicated */\n  return -1;\n  }\n\n/* Scan the branch, accumulating the length. */\n\nfor (;; pptr++)\n  {\n  parsed_recurse_check *r;\n  uint32_t *gptr, *gptrend;\n  uint32_t escape;\n  uint32_t min, max;\n  uint32_t group = 0;\n  uint32_t itemlength = 0;\n  uint32_t itemminlength = 0;\n\n  if (*pptr < META_END)\n    {\n    itemlength = itemminlength = 1;\n    }\n\n  else switch (META_CODE(*pptr))\n    {\n    case META_KET:\n    case META_ALT:\n    goto EXIT;\n\n    /* (*ACCEPT) and (*FAIL) terminate the branch, but we must skip to the\n    actual termination. */\n\n    case META_ACCEPT:\n    case META_FAIL:\n    pptr = parsed_skip(pptr, PSKIP_ALT);\n    if (pptr == NULL) goto PARSED_SKIP_FAILED;\n    goto EXIT;\n\n    case META_MARK:\n    case META_COMMIT_ARG:\n    case META_PRUNE_ARG:\n    case META_SKIP_ARG:\n    case META_THEN_ARG:\n    pptr += pptr[1] + 1;\n    break;\n\n    case META_CIRCUMFLEX:\n    case META_COMMIT:\n    case META_DOLLAR:\n    case META_PRUNE:\n    case META_SKIP:\n    case META_THEN:\n    break;\n\n    case META_OPTIONS:\n    pptr += 2;\n    break;\n\n    case META_BIGVALUE:\n    itemlength = itemminlength = 1;\n    pptr += 1;\n    break;\n\n    case META_CLASS:\n    case META_CLASS_NOT:\n    itemlength = itemminlength = 1;\n    pptr = parsed_skip(pptr, PSKIP_CLASS);\n    if (pptr == NULL) goto PARSED_SKIP_FAILED;\n    break;\n\n    case META_CLASS_EMPTY_NOT:\n    case META_DOT:\n    itemlength = itemminlength = 1;\n    break;\n\n    case META_CALLOUT_NUMBER:\n    pptr += 3;\n    break;\n\n    case META_CALLOUT_STRING:\n    pptr += 3 + SIZEOFFSET;\n    break;\n\n    /* Only some escapes consume a character. Of those, \\R can match one or two\n    characters, but \\X is never allowed because it matches an unknown number of\n    characters. \\C is allowed only in 32-bit and non-UTF 8/16-bit modes. */\n\n    case META_ESCAPE:\n    escape = META_DATA(*pptr);\n    if (escape == ESC_X) return -1;\n    if (escape == ESC_R)\n      {\n      itemminlength = 1;\n      itemlength = 2;\n      }\n    else if (escape > ESC_b && escape < ESC_Z)\n      {\n#if PCRE2_CODE_UNIT_WIDTH != 32\n      if ((cb->external_options & PCRE2_UTF) != 0 && escape == ESC_C)\n        {\n        *errcodeptr = ERR36;\n        return -1;\n        }\n#endif\n      itemlength = itemminlength = 1;\n      if (escape == ESC_p || escape == ESC_P) pptr++;  /* Skip prop data */\n      }\n    break;\n\n    /* Lookaheads do not contribute to the length of this branch, but they may\n    contain lookbehinds within them whose lengths need to be set. */\n\n    case META_LOOKAHEAD:\n    case META_LOOKAHEADNOT:\n    case META_LOOKAHEAD_NA:\n    case META_SCS:\n    *errcodeptr = check_lookbehinds(pptr + 1, &pptr, recurses, cb, lcptr);\n    if (*errcodeptr != 0) return -1;\n\n    /* Ignore any qualifiers that follow a lookahead assertion. */\n\n    switch (pptr[1])\n      {\n      case META_ASTERISK:\n      case META_ASTERISK_PLUS:\n      case META_ASTERISK_QUERY:\n      case META_PLUS:\n      case META_PLUS_PLUS:\n      case META_PLUS_QUERY:\n      case META_QUERY:\n      case META_QUERY_PLUS:\n      case META_QUERY_QUERY:\n      pptr++;\n      break;\n\n      case META_MINMAX:\n      case META_MINMAX_PLUS:\n      case META_MINMAX_QUERY:\n      pptr += 3;\n      break;\n\n      default:\n      break;\n      }\n    break;\n\n    /* A nested lookbehind does not contribute any length to this lookbehind,\n    but must itself be checked and have its lengths set. Note that\n    set_lookbehind_lengths() updates pptr, leaving it pointing to the final ket\n    of the group, so no need to update it here. */\n\n    case META_LOOKBEHIND:\n    case META_LOOKBEHINDNOT:\n    case META_LOOKBEHIND_NA:\n    if (!set_lookbehind_lengths(&pptr, errcodeptr, lcptr, recurses, cb))\n      return -1;\n    break;\n\n    /* Back references and recursions are handled by very similar code. At this\n    stage, the names generated in the parsing pass are available, but the main\n    name table has not yet been created. So for the named varieties, scan the\n    list of names in order to get the number of the first one in the pattern,\n    and whether or not this name is duplicated. */\n\n    case META_BACKREF_BYNAME:\n    if ((cb->external_options & PCRE2_MATCH_UNSET_BACKREF) != 0)\n      goto ISNOTFIXED;\n    /* Fall through */\n\n    case META_RECURSE_BYNAME:\n      {\n      int i;\n      PCRE2_SPTR name;\n      BOOL is_dupname = FALSE;\n      named_group *ng = cb->named_groups;\n      uint32_t meta_code = META_CODE(*pptr);\n      uint32_t length = *(++pptr);\n\n      GETPLUSOFFSET(offset, pptr);\n      name = cb->start_pattern + offset;\n      for (i = 0; i < cb->names_found; i++, ng++)\n        {\n        if (length == ng->length && PRIV(strncmp)(name, ng->name, length) == 0)\n          {\n          group = ng->number;\n          is_dupname = ng->isdup;\n          break;\n          }\n        }\n\n      if (group == 0)\n        {\n        *errcodeptr = ERR15;  /* Non-existent subpattern */\n        cb->erroroffset = offset;\n        return -1;\n        }\n\n      /* A numerical back reference can be fixed length if duplicate capturing\n      groups are not being used. A non-duplicate named back reference can also\n      be handled. */\n\n      if (meta_code == META_RECURSE_BYNAME ||\n          (!is_dupname && (cb->external_flags & PCRE2_DUPCAPUSED) == 0))\n        goto RECURSE_OR_BACKREF_LENGTH;  /* Handle as a numbered version. */\n      }\n    goto ISNOTFIXED;                     /* Duplicate name or number */\n\n    /* The offset values for back references < 10 are in a separate vector\n    because otherwise they would use more than two parsed pattern elements on\n    64-bit systems. */\n\n    case META_BACKREF:\n    if ((cb->external_options & PCRE2_MATCH_UNSET_BACKREF) != 0 ||\n        (cb->external_flags & PCRE2_DUPCAPUSED) != 0)\n      goto ISNOTFIXED;\n    group = META_DATA(*pptr);\n    if (group < 10)\n      {\n      offset = cb->small_ref_offset[group];\n      goto RECURSE_OR_BACKREF_LENGTH;\n      }\n\n    /* Fall through */\n    /* For groups >= 10 - picking up group twice does no harm. */\n\n    /* A true recursion implies not fixed length, but a subroutine call may\n    be OK. Back reference \"recursions\" are also failed. */\n\n    case META_RECURSE:\n    group = META_DATA(*pptr);\n    GETPLUSOFFSET(offset, pptr);\n\n    RECURSE_OR_BACKREF_LENGTH:\n    if (group > cb->bracount)\n      {\n      cb->erroroffset = offset;\n      *errcodeptr = ERR15;  /* Non-existent subpattern */\n      return -1;\n      }\n    if (group == 0) goto ISNOTFIXED;  /* Local recursion */\n    for (gptr = cb->parsed_pattern; *gptr != META_END; gptr++)\n      {\n      if (META_CODE(*gptr) == META_BIGVALUE) gptr++;\n        else if (*gptr == (META_CAPTURE | group)) break;\n      }\n\n    /* We must start the search for the end of the group at the first meta code\n    inside the group. Otherwise it will be treated as an enclosed group. */\n\n    gptrend = parsed_skip(gptr + 1, PSKIP_KET);\n    if (gptrend == NULL) goto PARSED_SKIP_FAILED;\n    if (pptr > gptr && pptr < gptrend) goto ISNOTFIXED;  /* Local recursion */\n    for (r = recurses; r != NULL; r = r->prev) if (r->groupptr == gptr) break;\n    if (r != NULL) goto ISNOTFIXED;   /* Mutual recursion */\n    this_recurse.prev = recurses;\n    this_recurse.groupptr = gptr;\n\n    /* We do not need to know the position of the end of the group, that is,\n    gptr is not used after the call to get_grouplength(). Setting the second\n    argument FALSE stops it scanning for the end when the length can be found\n    in the cache. */\n\n    gptr++;\n    grouplength = get_grouplength(&gptr, &groupminlength, FALSE, errcodeptr,\n      lcptr, group, &this_recurse, cb);\n    if (grouplength < 0)\n      {\n      if (*errcodeptr == 0) goto ISNOTFIXED;\n      return -1;  /* Error already set */\n      }\n    itemlength = grouplength;\n    itemminlength = groupminlength;\n    break;\n\n    /* A (DEFINE) group is never obeyed inline and so it does not contribute to\n    the length of this branch. Skip from the following item to the next\n    unpaired ket. */\n\n    case META_COND_DEFINE:\n    pptr = parsed_skip(pptr + 1, PSKIP_KET);\n    break;\n\n    /* Check other nested groups - advance past the initial data for each type\n    and then seek a fixed length with get_grouplength(). */\n\n    case META_COND_NAME:\n    case META_COND_NUMBER:\n    case META_COND_RNAME:\n    case META_COND_RNUMBER:\n    pptr += 2 + SIZEOFFSET;\n    goto CHECK_GROUP;\n\n    case META_COND_ASSERT:\n    pptr += 1;\n    goto CHECK_GROUP;\n\n    case META_COND_VERSION:\n    pptr += 4;\n    goto CHECK_GROUP;\n\n    case META_CAPTURE:\n    group = META_DATA(*pptr);\n    /* Fall through */\n\n    case META_ATOMIC:\n    case META_NOCAPTURE:\n    case META_SCRIPT_RUN:\n    pptr++;\n    CHECK_GROUP:\n    grouplength = get_grouplength(&pptr, &groupminlength, TRUE, errcodeptr,\n      lcptr, group, recurses, cb);\n    if (grouplength < 0) return -1;\n    itemlength = grouplength;\n    itemminlength = groupminlength;\n    break;\n\n    case META_QUERY:\n    case META_QUERY_PLUS:\n    case META_QUERY_QUERY:\n    min = 0;\n    max = 1;\n    goto REPETITION;\n\n    /* Exact repetition is OK; variable repetition is not. A repetition of zero\n    must subtract the length that has already been added. */\n\n    case META_MINMAX:\n    case META_MINMAX_PLUS:\n    case META_MINMAX_QUERY:\n    min = pptr[1];\n    max = pptr[2];\n    pptr += 2;\n\n    REPETITION:\n    if (max != REPEAT_UNLIMITED)\n      {\n      if (lastitemlength != 0 &&  /* Should not occur, but just in case */\n          max != 0 &&\n          (INT_MAX - branchlength)/lastitemlength < max - 1)\n        {\n        *errcodeptr = ERR87;  /* Integer overflow; lookbehind too big */\n        return -1;\n        }\n      if (min == 0) branchminlength -= lastitemminlength;\n        else itemminlength = (min - 1) * lastitemminlength;\n      if (max == 0) branchlength -= lastitemlength;\n        else itemlength = (max - 1) * lastitemlength;\n      break;\n      }\n    /* Fall through */\n\n    /* Any other item means this branch does not have a fixed length. */\n\n    default:\n    ISNOTFIXED:\n    *errcodeptr = ERR25;   /* Not fixed length */\n    return -1;\n    }\n\n  /* Add the item length to the branchlength, checking for integer overflow and\n  for the branch length exceeding the overall limit. Later, if there is at\n  least one variable-length branch in the group, there is a test for the\n  (smaller) variable-length branch length limit. */\n\n  if (INT_MAX - branchlength < (int)itemlength ||\n      (branchlength += itemlength) > LOOKBEHIND_MAX)\n    {\n    *errcodeptr = ERR87;\n    return -1;\n    }\n\n  branchminlength += itemminlength;\n\n  /* Save this item length for use if the next item is a quantifier. */\n\n  lastitemlength = itemlength;\n  lastitemminlength = itemminlength;\n  }\n\nEXIT:\n*pptrptr = pptr;\n*minptr = branchminlength;\nreturn branchlength;\n\nPARSED_SKIP_FAILED:\nPCRE2_DEBUG_UNREACHABLE();\n*errcodeptr = ERR90;  /* Unhandled META code - internal error */\nreturn -1;\n}\n\n\n\n/*************************************************\n*        Set lengths in a lookbehind             *\n*************************************************/\n\n/* This function is called for each lookbehind, to set the lengths in its\nbranches. An error occurs if any branch does not have a limited maximum length\nthat is less than the limit (65535). On exit, the pointer must be left on the\nfinal ket.\n\nThe function also maintains the max_lookbehind value. Any lookbehind branch\nthat contains a nested lookbehind may actually look further back than the\nlength of the branch. The additional amount is passed back from\nget_branchlength() as an \"extra\" value.\n\nArguments:\n  pptrptr     pointer to pointer in the parsed pattern\n  errcodeptr  pointer to error code\n  lcptr       pointer to loop counter\n  recurses    chain of recurse_check to catch mutual recursion\n  cb          pointer to compile block\n\nReturns:      TRUE if all is well\n              FALSE otherwise, with error code and offset set\n*/\n\nstatic BOOL\nset_lookbehind_lengths(uint32_t **pptrptr, int *errcodeptr, int *lcptr,\n  parsed_recurse_check *recurses, compile_block *cb)\n{\nPCRE2_SIZE offset;\nuint32_t *bptr = *pptrptr;\nuint32_t *gbptr = bptr;\nint maxlength = 0;\nint minlength = INT_MAX;\nBOOL variable = FALSE;\n\nREADPLUSOFFSET(offset, bptr);  /* Offset for error messages */\n*pptrptr += SIZEOFFSET;\n\n/* Each branch can have a different maximum length, but we can keep only a\nsingle minimum for the whole group, because there's nowhere to save individual\nvalues in the META_ALT item. */\n\ndo\n  {\n  int branchlength, branchminlength;\n\n  *pptrptr += 1;\n  branchlength = get_branchlength(pptrptr, &branchminlength, errcodeptr, lcptr,\n    recurses, cb);\n\n  if (branchlength < 0)\n    {\n    /* The errorcode and offset may already be set from a nested lookbehind. */\n    if (*errcodeptr == 0) *errcodeptr = ERR25;\n    if (cb->erroroffset == PCRE2_UNSET) cb->erroroffset = offset;\n    return FALSE;\n    }\n\n  if (branchlength != branchminlength) variable = TRUE;\n  if (branchminlength < minlength) minlength = branchminlength;\n  if (branchlength > maxlength) maxlength = branchlength;\n  if (branchlength > cb->max_lookbehind) cb->max_lookbehind = branchlength;\n  *bptr |= branchlength;  /* branchlength never more than 65535 */\n  bptr = *pptrptr;\n  }\nwhile (META_CODE(*bptr) == META_ALT);\n\n/* If any branch is of variable length, the whole lookbehind is of variable\nlength. If the maximum length of any branch exceeds the maximum for variable\nlookbehinds, give an error. Otherwise, the minimum length is set in the word\nthat follows the original group META value. For a fixed-length lookbehind, this\nis set to LOOKBEHIND_MAX, to indicate that each branch is of a fixed (but\npossibly different) length. */\n\nif (variable)\n  {\n  gbptr[1] = minlength;\n  if ((PCRE2_SIZE)maxlength > cb->max_varlookbehind)\n    {\n    *errcodeptr = ERR100;\n    cb->erroroffset = offset;\n    return FALSE;\n    }\n  }\nelse gbptr[1] = LOOKBEHIND_MAX;\n\nreturn TRUE;\n}\n\n\n\n/*************************************************\n*         Check parsed pattern lookbehinds       *\n*************************************************/\n\n/* This function is called at the end of parsing a pattern if any lookbehinds\nwere encountered. It scans the parsed pattern for them, calling\nset_lookbehind_lengths() for each one. At the start, the errorcode is zero and\nthe error offset is marked unset. The enables the functions above not to\noverride settings from deeper nestings.\n\nThis function is called recursively from get_branchlength() for lookaheads in\norder to process any lookbehinds that they may contain. It stops when it hits a\nnon-nested closing parenthesis in this case, returning a pointer to it.\n\nArguments\n  pptr      points to where to start (start of pattern or start of lookahead)\n  retptr    if not NULL, return the ket pointer here\n  recurses  chain of recurse_check to catch mutual recursion\n  cb        points to the compile block\n  lcptr     points to loop counter\n\nReturns:    0 on success, or an errorcode (cb->erroroffset will be set)\n*/\n\nstatic int\ncheck_lookbehinds(uint32_t *pptr, uint32_t **retptr,\n  parsed_recurse_check *recurses, compile_block *cb, int *lcptr)\n{\nint errorcode = 0;\nint nestlevel = 0;\n\ncb->erroroffset = PCRE2_UNSET;\n\nfor (; *pptr != META_END; pptr++)\n  {\n  if (*pptr < META_END) continue;  /* Literal */\n\n  switch (META_CODE(*pptr))\n    {\n    default:\n\n    /* The following erroroffset is a bogus but safe value. This branch should\n    be avoided by providing a proper implementation for all supported cases\n    below. */\n\n    PCRE2_DEBUG_UNREACHABLE();\n    cb->erroroffset = 0;\n    return ERR70;  /* Unrecognized meta code */\n\n    case META_ESCAPE:\n    if (*pptr - META_ESCAPE == ESC_P || *pptr - META_ESCAPE == ESC_p)\n      pptr += 1;    /* Skip prop data */\n    break;\n\n    case META_KET:\n    if (--nestlevel < 0)\n      {\n      if (retptr != NULL) *retptr = pptr;\n      return 0;\n      }\n    break;\n\n    case META_ATOMIC:\n    case META_CAPTURE:\n    case META_COND_ASSERT:\n    case META_SCS:\n    case META_LOOKAHEAD:\n    case META_LOOKAHEADNOT:\n    case META_LOOKAHEAD_NA:\n    case META_NOCAPTURE:\n    case META_SCRIPT_RUN:\n    nestlevel++;\n    break;\n\n    case META_ACCEPT:\n    case META_ALT:\n    case META_ASTERISK:\n    case META_ASTERISK_PLUS:\n    case META_ASTERISK_QUERY:\n    case META_BACKREF:\n    case META_CIRCUMFLEX:\n    case META_CLASS:\n    case META_CLASS_EMPTY:\n    case META_CLASS_EMPTY_NOT:\n    case META_CLASS_END:\n    case META_CLASS_NOT:\n    case META_COMMIT:\n    case META_DOLLAR:\n    case META_DOT:\n    case META_FAIL:\n    case META_PLUS:\n    case META_PLUS_PLUS:\n    case META_PLUS_QUERY:\n    case META_PRUNE:\n    case META_QUERY:\n    case META_QUERY_PLUS:\n    case META_QUERY_QUERY:\n    case META_RANGE_ESCAPED:\n    case META_RANGE_LITERAL:\n    case META_SKIP:\n    case META_THEN:\n    break;\n\n    case META_OFFSET:\n    case META_RECURSE:\n    pptr += SIZEOFFSET;\n    break;\n\n    case META_BACKREF_BYNAME:\n    case META_RECURSE_BYNAME:\n    pptr += 1 + SIZEOFFSET;\n    break;\n\n    case META_COND_DEFINE:\n    pptr += SIZEOFFSET;\n    nestlevel++;\n    break;\n\n    case META_COND_NAME:\n    case META_COND_NUMBER:\n    case META_COND_RNAME:\n    case META_COND_RNUMBER:\n    pptr += 1 + SIZEOFFSET;\n    nestlevel++;\n    break;\n\n    case META_COND_VERSION:\n    pptr += 3;\n    nestlevel++;\n    break;\n\n    case META_CALLOUT_STRING:\n    pptr += 3 + SIZEOFFSET;\n    break;\n\n    case META_BIGVALUE:\n    case META_POSIX:\n    case META_POSIX_NEG:\n    case META_SCS_NAME:\n    case META_SCS_NUMBER:\n    pptr += 1;\n    break;\n\n    case META_MINMAX:\n    case META_MINMAX_QUERY:\n    case META_MINMAX_PLUS:\n    case META_OPTIONS:\n    pptr += 2;\n    break;\n\n    case META_CALLOUT_NUMBER:\n    pptr += 3;\n    break;\n\n    case META_MARK:\n    case META_COMMIT_ARG:\n    case META_PRUNE_ARG:\n    case META_SKIP_ARG:\n    case META_THEN_ARG:\n    pptr += 1 + pptr[1];\n    break;\n\n    /* Note that set_lookbehind_lengths() updates pptr, leaving it pointing to\n    the final ket of the group, so no need to update it here. */\n\n    case META_LOOKBEHIND:\n    case META_LOOKBEHINDNOT:\n    case META_LOOKBEHIND_NA:\n    if (!set_lookbehind_lengths(&pptr, &errorcode, lcptr, recurses, cb))\n      return errorcode;\n    break;\n    }\n  }\n\nreturn 0;\n}\n\n\n\n/*************************************************\n*     External function to compile a pattern     *\n*************************************************/\n\n/* This function reads a regular expression in the form of a string and returns\na pointer to a block of store holding a compiled version of the expression.\n\nArguments:\n  pattern       the regular expression\n  patlen        the length of the pattern, or PCRE2_ZERO_TERMINATED\n  options       option bits\n  errorptr      pointer to errorcode\n  erroroffset   pointer to error offset\n  ccontext      points to a compile context or is NULL\n\nReturns:        pointer to compiled data block, or NULL on error,\n                with errorcode and erroroffset set\n*/\n\nPCRE2_EXP_DEFN pcre2_code * PCRE2_CALL_CONVENTION\npcre2_compile(PCRE2_SPTR pattern, PCRE2_SIZE patlen, uint32_t options,\n   int *errorptr, PCRE2_SIZE *erroroffset, pcre2_compile_context *ccontext)\n{\nBOOL utf;                             /* Set TRUE for UTF mode */\nBOOL ucp;                             /* Set TRUE for UCP mode */\nBOOL has_lookbehind = FALSE;          /* Set TRUE if a lookbehind is found */\nBOOL zero_terminated;                 /* Set TRUE for zero-terminated pattern */\npcre2_real_code *re = NULL;           /* What we will return */\ncompile_block cb;                     /* \"Static\" compile-time data */\nconst uint8_t *tables;                /* Char tables base pointer */\n\nPCRE2_UCHAR *code;                    /* Current pointer in compiled code */\nPCRE2_UCHAR * codestart;              /* Start of compiled code */\nPCRE2_SPTR ptr;                       /* Current pointer in pattern */\nuint32_t *pptr;                       /* Current pointer in parsed pattern */\n\nPCRE2_SIZE length = 1;                /* Allow for final END opcode */\nPCRE2_SIZE usedlength;                /* Actual length used */\nPCRE2_SIZE re_blocksize;              /* Size of memory block */\nPCRE2_SIZE parsed_size_needed;        /* Needed for parsed pattern */\n\nuint32_t firstcuflags, reqcuflags;    /* Type of first/req code unit */\nuint32_t firstcu, reqcu;              /* Value of first/req code unit */\nuint32_t setflags = 0;                /* NL and BSR set flags */\nuint32_t xoptions;                    /* Flags from context, modified */\n\nuint32_t skipatstart;                 /* When checking (*UTF) etc */\nuint32_t limit_heap  = UINT32_MAX;\nuint32_t limit_match = UINT32_MAX;    /* Unset match limits */\nuint32_t limit_depth = UINT32_MAX;\n\nint newline = 0;                      /* Unset; can be set by the pattern */\nint bsr = 0;                          /* Unset; can be set by the pattern */\nint errorcode = 0;                    /* Initialize to avoid compiler warn */\nint regexrc;                          /* Return from compile */\n\nuint32_t i;                           /* Local loop counter */\n\n/* Enable all optimizations by default. */\nuint32_t optim_flags = ccontext != NULL ? ccontext->optimization_flags :\n                                          PCRE2_OPTIMIZATION_ALL;\n\n/* Comments at the head of this file explain about these variables. */\n\nuint32_t stack_groupinfo[GROUPINFO_DEFAULT_SIZE];\nuint32_t stack_parsed_pattern[PARSED_PATTERN_DEFAULT_SIZE];\nnamed_group named_groups[NAMED_GROUP_LIST_SIZE];\n\n/* The workspace is used in different ways in the different compiling phases.\nIt needs to be 16-bit aligned for the preliminary parsing scan. */\n\nuint32_t c16workspace[C16_WORK_SIZE];\nPCRE2_UCHAR *cworkspace = (PCRE2_UCHAR *)c16workspace;\n\n\n/* -------------- Check arguments and set up the pattern ----------------- */\n\n/* There must be error code and offset pointers. */\n\nif (errorptr == NULL || erroroffset == NULL) return NULL;\n*errorptr = ERR0;\n*erroroffset = 0;\n\n/* There must be a pattern, but NULL is allowed with zero length. */\n\nif (pattern == NULL)\n  {\n  if (patlen == 0) pattern = (PCRE2_SPTR)\"\"; else\n    {\n    *errorptr = ERR16;\n    return NULL;\n    }\n  }\n\n/* A NULL compile context means \"use a default context\" */\n\nif (ccontext == NULL)\n  ccontext = (pcre2_compile_context *)(&PRIV(default_compile_context));\n\n/* PCRE2_MATCH_INVALID_UTF implies UTF */\n\nif ((options & PCRE2_MATCH_INVALID_UTF) != 0) options |= PCRE2_UTF;\n\n/* Check that all undefined public option bits are zero. */\n\nif ((options & ~PUBLIC_COMPILE_OPTIONS) != 0 ||\n    (ccontext->extra_options & ~PUBLIC_COMPILE_EXTRA_OPTIONS) != 0)\n  {\n  *errorptr = ERR17;\n  return NULL;\n  }\n\nif ((options & PCRE2_LITERAL) != 0 &&\n    ((options & ~PUBLIC_LITERAL_COMPILE_OPTIONS) != 0 ||\n     (ccontext->extra_options & ~PUBLIC_LITERAL_COMPILE_EXTRA_OPTIONS) != 0))\n  {\n  *errorptr = ERR92;\n  return NULL;\n  }\n\n/* A zero-terminated pattern is indicated by the special length value\nPCRE2_ZERO_TERMINATED. Check for an overlong pattern. */\n\nif ((zero_terminated = (patlen == PCRE2_ZERO_TERMINATED)))\n  patlen = PRIV(strlen)(pattern);\n(void)zero_terminated; /* Silence compiler; only used if Valgrind enabled */\n\nif (patlen > ccontext->max_pattern_length)\n  {\n  *errorptr = ERR88;\n  return NULL;\n  }\n\n/* Optimization flags in 'options' can override those in the compile context.\nThis is because some options to disable optimizations were added before the\noptimization flags word existed, and we need to continue supporting them\nfor backwards compatibility. */\n\nif ((options & PCRE2_NO_AUTO_POSSESS) != 0)\n  optim_flags &= ~PCRE2_OPTIM_AUTO_POSSESS;\nif ((options & PCRE2_NO_DOTSTAR_ANCHOR) != 0)\n  optim_flags &= ~PCRE2_OPTIM_DOTSTAR_ANCHOR;\nif ((options & PCRE2_NO_START_OPTIMIZE) != 0)\n  optim_flags &= ~PCRE2_OPTIM_START_OPTIMIZE;\n\n/* From here on, all returns from this function should end up going via the\nEXIT label. */\n\n\n/* ------------ Initialize the \"static\" compile data -------------- */\n\ntables = (ccontext->tables != NULL)? ccontext->tables : PRIV(default_tables);\n\ncb.lcc = tables + lcc_offset;          /* Individual */\ncb.fcc = tables + fcc_offset;          /*   character */\ncb.cbits = tables + cbits_offset;      /*      tables */\ncb.ctypes = tables + ctypes_offset;\n\ncb.assert_depth = 0;\ncb.bracount = 0;\ncb.cx = ccontext;\ncb.dupnames = FALSE;\ncb.end_pattern = pattern + patlen;\ncb.erroroffset = 0;\ncb.external_flags = 0;\ncb.external_options = options;\ncb.groupinfo = stack_groupinfo;\ncb.had_recurse = FALSE;\ncb.lastcapture = 0;\ncb.max_lookbehind = 0;                               /* Max encountered */\ncb.max_varlookbehind = ccontext->max_varlookbehind;  /* Limit */\ncb.name_entry_size = 0;\ncb.name_table = NULL;\ncb.named_groups = named_groups;\ncb.named_group_list_size = NAMED_GROUP_LIST_SIZE;\ncb.names_found = 0;\ncb.parens_depth = 0;\ncb.parsed_pattern = stack_parsed_pattern;\ncb.req_varyopt = 0;\ncb.start_code = cworkspace;\ncb.start_pattern = pattern;\ncb.start_workspace = cworkspace;\ncb.workspace_size = COMPILE_WORK_SIZE;\n#ifdef SUPPORT_WIDE_CHARS\ncb.cranges = NULL;\ncb.next_cranges = NULL;\ncb.char_lists_size = 0;\n#endif\n\n/* Maximum back reference and backref bitmap. The bitmap records up to 31 back\nreferences to help in deciding whether (.*) can be treated as anchored or not.\n*/\n\ncb.top_backref = 0;\ncb.backref_map = 0;\n\n/* Escape sequences \\1 to \\9 are always back references, but as they are only\ntwo characters long, only two elements can be used in the parsed_pattern\nvector. The first contains the reference, and we'd like to use the second to\nrecord the offset in the pattern, so that forward references to non-existent\ngroups can be diagnosed later with an offset. However, on 64-bit systems,\nPCRE2_SIZE won't fit. Instead, we have a vector of offsets for the first\noccurrence of \\1 to \\9, indexed by the second parsed_pattern value. All other\nreferences have enough space for the offset to be put into the parsed pattern.\n*/\n\nfor (i = 0; i < 10; i++) cb.small_ref_offset[i] = PCRE2_UNSET;\n\n\n/* --------------- Start looking at the pattern --------------- */\n\n/* Unless PCRE2_LITERAL is set, check for global one-time option settings at\nthe start of the pattern, and remember the offset to the actual regex. With\nvalgrind support, make the terminator of a zero-terminated pattern\ninaccessible. This catches bugs that would otherwise only show up for\nnon-zero-terminated patterns. */\n\n#ifdef SUPPORT_VALGRIND\nif (zero_terminated) VALGRIND_MAKE_MEM_NOACCESS(pattern + patlen, CU2BYTES(1));\n#endif\n\nxoptions = ccontext->extra_options;\nptr = pattern;\nskipatstart = 0;\n\nif ((options & PCRE2_LITERAL) == 0)\n  {\n  while (patlen - skipatstart >= 2 &&\n         ptr[skipatstart] == CHAR_LEFT_PARENTHESIS &&\n         ptr[skipatstart+1] == CHAR_ASTERISK)\n    {\n    for (i = 0; i < sizeof(pso_list)/sizeof(pso); i++)\n      {\n      const pso *p = pso_list + i;\n\n      if (patlen - skipatstart - 2 >= p->length &&\n          PRIV(strncmp_c8)(ptr + skipatstart + 2, p->name, p->length) == 0)\n        {\n        uint32_t c, pp;\n\n        skipatstart += p->length + 2;\n        switch(p->type)\n          {\n          case PSO_OPT:\n          cb.external_options |= p->value;\n          break;\n\n          case PSO_XOPT:\n          xoptions |= p->value;\n          break;\n\n          case PSO_FLG:\n          setflags |= p->value;\n          break;\n\n          case PSO_NL:\n          newline = p->value;\n          setflags |= PCRE2_NL_SET;\n          break;\n\n          case PSO_BSR:\n          bsr = p->value;\n          setflags |= PCRE2_BSR_SET;\n          break;\n\n          case PSO_LIMM:\n          case PSO_LIMD:\n          case PSO_LIMH:\n          c = 0;\n          pp = skipatstart;\n          while (pp < patlen && IS_DIGIT(ptr[pp]))\n            {\n            if (c > UINT32_MAX / 10 - 1) break;   /* Integer overflow */\n            c = c*10 + (ptr[pp++] - CHAR_0);\n            }\n          if (pp >= patlen || pp == skipatstart || ptr[pp] != CHAR_RIGHT_PARENTHESIS)\n            {\n            errorcode = ERR60;\n            ptr += pp;\n            goto HAD_EARLY_ERROR;\n            }\n          if (p->type == PSO_LIMH) limit_heap = c;\n            else if (p->type == PSO_LIMM) limit_match = c;\n            else limit_depth = c;\n          skipatstart = ++pp;\n          break;\n\n          case PSO_OPTMZ:\n          optim_flags &= ~(p->value);\n\n          /* For backward compatibility the three original VERBs to disable\n          optimizations need to also update the corresponding bit in the\n          external options. */\n\n          switch(p->value)\n            {\n            case PCRE2_OPTIM_AUTO_POSSESS:\n            cb.external_options |= PCRE2_NO_AUTO_POSSESS;\n            break;\n\n            case PCRE2_OPTIM_DOTSTAR_ANCHOR:\n            cb.external_options |= PCRE2_NO_DOTSTAR_ANCHOR;\n            break;\n\n            case PCRE2_OPTIM_START_OPTIMIZE:\n            cb.external_options |= PCRE2_NO_START_OPTIMIZE;\n            break;\n            }\n\n          break;\n\n          default:\n          /* All values in the enum need an explicit entry for this switch\n          but until a better way to prevent coding mistakes is invented keep\n          a catch all that triggers a debug build assert as a failsafe */\n          PCRE2_DEBUG_UNREACHABLE();\n          }\n        break;   /* Out of the table scan loop */\n        }\n      }\n    if (i >= sizeof(pso_list)/sizeof(pso)) break;   /* Out of pso loop */\n    }\n    PCRE2_ASSERT(skipatstart <= patlen);\n  }\n\n/* End of pattern-start options; advance to start of real regex. */\n\nptr += skipatstart;\n\n/* Can't support UTF or UCP if PCRE2 was built without Unicode support. */\n\n#ifndef SUPPORT_UNICODE\nif ((cb.external_options & (PCRE2_UTF|PCRE2_UCP)) != 0)\n  {\n  errorcode = ERR32;\n  goto HAD_EARLY_ERROR;\n  }\n#endif\n\n/* Check UTF. We have the original options in 'options', with that value as\nmodified by (*UTF) etc in cb->external_options. The extra option\nPCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES is not permitted in UTF-16 mode because the\nsurrogate code points cannot be represented in UTF-16. */\n\nutf = (cb.external_options & PCRE2_UTF) != 0;\nif (utf)\n  {\n  if ((options & PCRE2_NEVER_UTF) != 0)\n    {\n    errorcode = ERR74;\n    goto HAD_EARLY_ERROR;\n    }\n  if ((options & PCRE2_NO_UTF_CHECK) == 0 &&\n       (errorcode = PRIV(valid_utf)(pattern, patlen, erroroffset)) != 0)\n    goto HAD_ERROR;  /* Offset was set by valid_utf() */\n\n#if PCRE2_CODE_UNIT_WIDTH == 16\n  if ((ccontext->extra_options & PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES) != 0)\n    {\n    errorcode = ERR91;\n    goto HAD_EARLY_ERROR;\n    }\n#endif\n  }\n\n/* Check UCP lockout. */\n\nucp = (cb.external_options & PCRE2_UCP) != 0;\nif (ucp && (cb.external_options & PCRE2_NEVER_UCP) != 0)\n  {\n  errorcode = ERR75;\n  goto HAD_EARLY_ERROR;\n  }\n\n/* PCRE2_EXTRA_TURKISH_CASING checks */\n\nif ((xoptions & PCRE2_EXTRA_TURKISH_CASING) != 0)\n  {\n  if (!utf && !ucp)\n    {\n    errorcode = ERR104;\n    goto HAD_EARLY_ERROR;\n    }\n\n#if PCRE2_CODE_UNIT_WIDTH == 8\n  if (!utf)\n    {\n    errorcode = ERR105;\n    goto HAD_EARLY_ERROR;\n    }\n#endif\n\n  if ((xoptions & PCRE2_EXTRA_CASELESS_RESTRICT) != 0)\n    {\n    errorcode = ERR106;\n    goto HAD_EARLY_ERROR;\n    }\n  }\n\n/* Process the BSR setting. */\n\nif (bsr == 0) bsr = ccontext->bsr_convention;\n\n/* Process the newline setting. */\n\nif (newline == 0) newline = ccontext->newline_convention;\ncb.nltype = NLTYPE_FIXED;\nswitch(newline)\n  {\n  case PCRE2_NEWLINE_CR:\n  cb.nllen = 1;\n  cb.nl[0] = CHAR_CR;\n  break;\n\n  case PCRE2_NEWLINE_LF:\n  cb.nllen = 1;\n  cb.nl[0] = CHAR_NL;\n  break;\n\n  case PCRE2_NEWLINE_NUL:\n  cb.nllen = 1;\n  cb.nl[0] = CHAR_NUL;\n  break;\n\n  case PCRE2_NEWLINE_CRLF:\n  cb.nllen = 2;\n  cb.nl[0] = CHAR_CR;\n  cb.nl[1] = CHAR_NL;\n  break;\n\n  case PCRE2_NEWLINE_ANY:\n  cb.nltype = NLTYPE_ANY;\n  break;\n\n  case PCRE2_NEWLINE_ANYCRLF:\n  cb.nltype = NLTYPE_ANYCRLF;\n  break;\n\n  default:\n  PCRE2_DEBUG_UNREACHABLE();\n  errorcode = ERR56;\n  goto HAD_EARLY_ERROR;\n  }\n\n/* Pre-scan the pattern to do two things: (1) Discover the named groups and\ntheir numerical equivalents, so that this information is always available for\nthe remaining processing. (2) At the same time, parse the pattern and put a\nprocessed version into the parsed_pattern vector. This has escapes interpreted\nand comments removed (amongst other things). */\n\n/* Ensure that the parsed pattern buffer is big enough. For many smaller\npatterns the vector on the stack (which was set up above) can be used. */\n\nparsed_size_needed = max_parsed_pattern(ptr, cb.end_pattern, utf, options);\n\n/* Allow for 2x uint32_t at the start and 2 at the end, for\nPCRE2_EXTRA_MATCH_WORD or PCRE2_EXTRA_MATCH_LINE (which are exclusive). */\n\nif ((ccontext->extra_options &\n     (PCRE2_EXTRA_MATCH_WORD|PCRE2_EXTRA_MATCH_LINE)) != 0)\n  parsed_size_needed += 4;\n\n/* When PCRE2_AUTO_CALLOUT is set we allow for one callout at the end. */\n\nif ((options & PCRE2_AUTO_CALLOUT) != 0)\n  parsed_size_needed += 4;\n\nparsed_size_needed += 1;  /* For the final META_END */\n\nif (parsed_size_needed > PARSED_PATTERN_DEFAULT_SIZE)\n  {\n  uint32_t *heap_parsed_pattern = ccontext->memctl.malloc(\n    parsed_size_needed * sizeof(uint32_t), ccontext->memctl.memory_data);\n  if (heap_parsed_pattern == NULL)\n    {\n    *errorptr = ERR21;\n    goto EXIT;\n    }\n  cb.parsed_pattern = heap_parsed_pattern;\n  }\ncb.parsed_pattern_end = cb.parsed_pattern + parsed_size_needed;\n\n/* Do the parsing scan. */\n\nerrorcode = parse_regex(ptr, cb.external_options, xoptions, &has_lookbehind, &cb);\nif (errorcode != 0) goto HAD_CB_ERROR;\n\n/* If there are any lookbehinds, scan the parsed pattern to figure out their\nlengths. Workspace is needed to remember whether numbered groups are or are not\nof limited length, and if limited, what the minimum and maximum lengths are.\nThis caching saves re-computing the length of any group that is referenced more\nthan once, which is particularly relevant when recursion is involved.\nUnnumbered groups do not have this exposure because they cannot be referenced.\nIf there are sufficiently few groups, the default index vector on the stack, as\nset up above, can be used. Otherwise we have to get/free some heap memory. The\nvector must be initialized to zero. */\n\nif (has_lookbehind)\n  {\n  int loopcount = 0;\n  if (cb.bracount >= GROUPINFO_DEFAULT_SIZE/2)\n    {\n    cb.groupinfo = ccontext->memctl.malloc(\n      (2 * (cb.bracount + 1))*sizeof(uint32_t), ccontext->memctl.memory_data);\n    if (cb.groupinfo == NULL)\n      {\n      errorcode = ERR21;\n      cb.erroroffset = 0;\n      goto HAD_CB_ERROR;\n      }\n    }\n  memset(cb.groupinfo, 0, (2 * cb.bracount + 1) * sizeof(uint32_t));\n  errorcode = check_lookbehinds(cb.parsed_pattern, NULL, NULL, &cb, &loopcount);\n  if (errorcode != 0) goto HAD_CB_ERROR;\n  }\n\n/* For debugging, there is a function that shows the parsed pattern vector. */\n\n#ifdef DEBUG_SHOW_PARSED\nfprintf(stderr, \"+++ Pre-scan complete:\\n\");\nshow_parsed(&cb);\n#endif\n\n/* For debugging capturing information this code can be enabled. */\n\n#ifdef DEBUG_SHOW_CAPTURES\n  {\n  named_group *ng = cb.named_groups;\n  fprintf(stderr, \"+++Captures: %d\\n\", cb.bracount);\n  for (i = 0; i < cb.names_found; i++, ng++)\n    {\n    fprintf(stderr, \"+++%3d %.*s\\n\", ng->number, ng->length, ng->name);\n    }\n  }\n#endif\n\n/* Pretend to compile the pattern while actually just accumulating the amount\nof memory required in the 'length' variable. This behaviour is triggered by\npassing a non-NULL final argument to compile_regex(). We pass a block of\nworkspace (cworkspace) for it to compile parts of the pattern into; the\ncompiled code is discarded when it is no longer needed, so hopefully this\nworkspace will never overflow, though there is a test for its doing so.\n\nOn error, errorcode will be set non-zero, so we don't need to look at the\nresult of the function. The initial options have been put into the cb block,\nbut we still have to pass a separate options variable (the first argument)\nbecause the options may change as the pattern is processed. */\n\ncb.erroroffset = patlen;   /* For any subsequent errors that do not set it */\npptr = cb.parsed_pattern;\ncode = cworkspace;\n*code = OP_BRA;\n\n(void)compile_regex(cb.external_options, xoptions, &code, &pptr,\n   &errorcode, 0, &firstcu, &firstcuflags, &reqcu, &reqcuflags, NULL, NULL,\n   &cb, &length);\n\nif (errorcode != 0) goto HAD_CB_ERROR;  /* Offset is in cb.erroroffset */\n\n/* This should be caught in compile_regex(), but just in case... */\n\n#if defined SUPPORT_WIDE_CHARS\nPCRE2_ASSERT((cb.char_lists_size & 0x3) == 0);\nif (length > MAX_PATTERN_SIZE ||\n    MAX_PATTERN_SIZE - length < (cb.char_lists_size / sizeof(PCRE2_UCHAR)))\n#else\nif (length > MAX_PATTERN_SIZE)\n#endif\n  {\n  errorcode = ERR20;\n  goto HAD_CB_ERROR;\n  }\n\n/* Compute the size of, then, if not too large, get and initialize the data\nblock for storing the compiled pattern and names table. Integer overflow should\nno longer be possible because nowadays we limit the maximum value of\ncb.names_found and cb.name_entry_size. */\n\nre_blocksize =\n  CU2BYTES((PCRE2_SIZE)cb.names_found * (PCRE2_SIZE)cb.name_entry_size);\n\n#if defined SUPPORT_WIDE_CHARS\nif (cb.char_lists_size != 0)\n  {\n#if PCRE2_CODE_UNIT_WIDTH != 32\n  /* Align to 32 bit first. This ensures the\n  allocated area will also be 32 bit aligned. */\n  re_blocksize = (PCRE2_SIZE)CLIST_ALIGN_TO(re_blocksize, sizeof(uint32_t));\n#endif\n  re_blocksize += cb.char_lists_size;\n  }\n#endif\n\nre_blocksize += CU2BYTES(length);\n\nif (re_blocksize > ccontext->max_pattern_compiled_length)\n  {\n  errorcode = ERR101;\n  goto HAD_CB_ERROR;\n  }\n\nre_blocksize += sizeof(pcre2_real_code);\nre = (pcre2_real_code *)\n  ccontext->memctl.malloc(re_blocksize, ccontext->memctl.memory_data);\nif (re == NULL)\n  {\n  errorcode = ERR21;\n  goto HAD_CB_ERROR;\n  }\n\n/* The compiler may put padding at the end of the pcre2_real_code structure in\norder to round it up to a multiple of 4 or 8 bytes. This means that when a\ncompiled pattern is copied (for example, when serialized) undefined bytes are\nread, and this annoys debuggers such as valgrind. To avoid this, we explicitly\nwrite to the last 8 bytes of the structure before setting the fields. */\n\nmemset((char *)re + sizeof(pcre2_real_code) - 8, 0, 8);\nre->memctl = ccontext->memctl;\nre->tables = tables;\nre->executable_jit = NULL;\nmemset(re->start_bitmap, 0, 32 * sizeof(uint8_t));\nre->blocksize = re_blocksize;\nre->code_start = re_blocksize - CU2BYTES(length);\nre->magic_number = MAGIC_NUMBER;\nre->compile_options = options;\nre->overall_options = cb.external_options;\nre->extra_options = xoptions;\nre->flags = PCRE2_CODE_UNIT_WIDTH/8 | cb.external_flags | setflags;\nre->limit_heap = limit_heap;\nre->limit_match = limit_match;\nre->limit_depth = limit_depth;\nre->first_codeunit = 0;\nre->last_codeunit = 0;\nre->bsr_convention = bsr;\nre->newline_convention = newline;\nre->max_lookbehind = 0;\nre->minlength = 0;\nre->top_bracket = 0;\nre->top_backref = 0;\nre->name_entry_size = cb.name_entry_size;\nre->name_count = cb.names_found;\nre->optimization_flags = optim_flags;\n\n/* The basic block is immediately followed by the name table, and the compiled\ncode follows after that. */\n\ncodestart = (PCRE2_UCHAR *)((uint8_t *)re + re->code_start);\n\n/* Update the compile data block for the actual compile. The starting points of\nthe name/number translation table and of the code are passed around in the\ncompile data block. The start/end pattern and initial options are already set\nfrom the pre-compile phase, as is the name_entry_size field. */\n\ncb.parens_depth = 0;\ncb.assert_depth = 0;\ncb.lastcapture = 0;\ncb.name_table = (PCRE2_UCHAR *)((uint8_t *)re + sizeof(pcre2_real_code));\ncb.start_code = codestart;\ncb.req_varyopt = 0;\ncb.had_accept = FALSE;\ncb.had_pruneorskip = FALSE;\n#ifdef SUPPORT_WIDE_CHARS\ncb.char_lists_size = 0;\n#endif\n\n\n/* If any named groups were found, create the name/number table from the list\ncreated in the pre-pass. */\n\nif (cb.names_found > 0)\n  {\n  named_group *ng = cb.named_groups;\n  for (i = 0; i < cb.names_found; i++, ng++)\n    add_name_to_table(&cb, ng->name, ng->length, ng->number, i);\n  }\n\n/* Set up a starting, non-extracting bracket, then compile the expression. On\nerror, errorcode will be set non-zero, so we don't need to look at the result\nof the function here. */\n\npptr = cb.parsed_pattern;\ncode = (PCRE2_UCHAR *)codestart;\n*code = OP_BRA;\nregexrc = compile_regex(re->overall_options, re->extra_options, &code,\n  &pptr, &errorcode, 0, &firstcu, &firstcuflags, &reqcu, &reqcuflags, NULL,\n  NULL, &cb, NULL);\nif (regexrc < 0) re->flags |= PCRE2_MATCH_EMPTY;\nre->top_bracket = cb.bracount;\nre->top_backref = cb.top_backref;\nre->max_lookbehind = cb.max_lookbehind;\n\nif (cb.had_accept)\n  {\n  reqcu = 0;                     /* Must disable after (*ACCEPT) */\n  reqcuflags = REQ_NONE;\n  re->flags |= PCRE2_HASACCEPT;  /* Disables minimum length */\n  }\n\n/* Fill in the final opcode and check for disastrous overflow. If no overflow,\nbut the estimated length exceeds the really used length, adjust the value of\nre->blocksize, and if valgrind support is configured, mark the extra allocated\nmemory as unaddressable, so that any out-of-bound reads can be detected. */\n\n*code++ = OP_END;\nusedlength = code - codestart;\nif (usedlength > length)\n  {\n  PCRE2_DEBUG_UNREACHABLE();\n  errorcode = ERR23;  /* Overflow of code block - internal error */\n  }\nelse\n  {\n  re->blocksize -= CU2BYTES(length - usedlength);\n#ifdef SUPPORT_VALGRIND\n  VALGRIND_MAKE_MEM_NOACCESS(code, CU2BYTES(length - usedlength));\n#endif\n  }\n\n/* Scan the pattern for recursion/subroutine calls and convert the group\nnumbers into offsets. Maintain a small cache so that repeated groups containing\nrecursions are efficiently handled. */\n\n#define RSCAN_CACHE_SIZE 8\n\nif (errorcode == 0 && cb.had_recurse)\n  {\n  PCRE2_UCHAR *rcode;\n  PCRE2_SPTR rgroup;\n  unsigned int ccount = 0;\n  int start = RSCAN_CACHE_SIZE;\n  recurse_cache rc[RSCAN_CACHE_SIZE];\n\n  for (rcode = find_recurse(codestart, utf);\n       rcode != NULL;\n       rcode = find_recurse(rcode + 1 + LINK_SIZE, utf))\n    {\n    int p, groupnumber;\n\n    groupnumber = (int)GET(rcode, 1);\n    if (groupnumber == 0) rgroup = codestart; else\n      {\n      PCRE2_SPTR search_from = codestart;\n      rgroup = NULL;\n      for (i = 0, p = start; i < ccount; i++, p = (p + 1) & 7)\n        {\n        if (groupnumber == rc[p].groupnumber)\n          {\n          rgroup = rc[p].group;\n          break;\n          }\n\n        /* Group n+1 must always start to the right of group n, so we can save\n        search time below when the new group number is greater than any of the\n        previously found groups. */\n\n        if (groupnumber > rc[p].groupnumber) search_from = rc[p].group;\n        }\n\n      if (rgroup == NULL)\n        {\n        rgroup = PRIV(find_bracket)(search_from, utf, groupnumber);\n        if (rgroup == NULL)\n          {\n          PCRE2_DEBUG_UNREACHABLE();\n          errorcode = ERR53;\n          break;\n          }\n        if (--start < 0) start = RSCAN_CACHE_SIZE - 1;\n        rc[start].groupnumber = groupnumber;\n        rc[start].group = rgroup;\n        if (ccount < RSCAN_CACHE_SIZE) ccount++;\n        }\n      }\n\n    PUT(rcode, 1, (uint32_t)(rgroup - codestart));\n    }\n  }\n\n/* In rare debugging situations we sometimes need to look at the compiled code\nat this stage. */\n\n#ifdef DEBUG_CALL_PRINTINT\npcre2_printint(re, stderr, TRUE);\nfprintf(stderr, \"Length=%lu Used=%lu\\n\", length, usedlength);\n#endif\n\n/* Unless disabled, check whether any single character iterators can be\nauto-possessified. The function overwrites the appropriate opcode values, so\nthe type of the pointer must be cast. NOTE: the intermediate variable \"temp\" is\nused in this code because at least one compiler gives a warning about loss of\n\"const\" attribute if the cast (PCRE2_UCHAR *)codestart is used directly in the\nfunction call. */\n\nif (errorcode == 0 && (optim_flags & PCRE2_OPTIM_AUTO_POSSESS) != 0)\n  {\n  PCRE2_UCHAR *temp = (PCRE2_UCHAR *)codestart;\n  if (PRIV(auto_possessify)(temp, &cb) != 0)\n    {\n    PCRE2_DEBUG_UNREACHABLE();\n    errorcode = ERR80;\n    }\n  }\n\n/* Failed to compile, or error while post-processing. */\n\nif (errorcode != 0) goto HAD_CB_ERROR;\n\n/* Successful compile. If the anchored option was not passed, set it if\nwe can determine that the pattern is anchored by virtue of ^ characters or \\A\nor anything else, such as starting with non-atomic .* when DOTALL is set and\nthere are no occurrences of *PRUNE or *SKIP (though there is an option to\ndisable this case). */\n\nif ((re->overall_options & PCRE2_ANCHORED) == 0)\n  {\n  BOOL dotstar_anchor = ((optim_flags & PCRE2_OPTIM_DOTSTAR_ANCHOR) != 0);\n  if (is_anchored(codestart, 0, &cb, 0, FALSE, dotstar_anchor))\n    re->overall_options |= PCRE2_ANCHORED;\n  }\n\n/* Set up the first code unit or startline flag, the required code unit, and\nthen study the pattern. This code need not be obeyed if PCRE2_OPTIM_START_OPTIMIZE\nis disabled, as the data it would create will not be used. Note that a first code\nunit (but not the startline flag) is useful for anchored patterns because it\ncan still give a quick \"no match\" and also avoid searching for a last code\nunit. */\n\nif ((optim_flags & PCRE2_OPTIM_START_OPTIMIZE) != 0)\n  {\n  int minminlength = 0;  /* For minimal minlength from first/required CU */\n\n  /* If we do not have a first code unit, see if there is one that is asserted\n  (these are not saved during the compile because they can cause conflicts with\n  actual literals that follow). */\n\n  if (firstcuflags >= REQ_NONE) {\n    uint32_t assertedcuflags = 0;\n    uint32_t assertedcu = find_firstassertedcu(codestart, &assertedcuflags, 0);\n    /* It would be wrong to use the asserted first code unit as `firstcu` for\n     * regexes which are able to match a 1-character string (e.g. /(?=a)b?a/)\n     * For that example, if we set both firstcu and reqcu to 'a', it would mean\n     * the subject string needs to be at least 2 characters long, which is wrong.\n     * With more analysis, we would be able to set firstcu in more cases. */\n    if (assertedcuflags < REQ_NONE && assertedcu != reqcu) {\n      firstcu = assertedcu;\n      firstcuflags = assertedcuflags;\n    }\n  }\n\n  /* Save the data for a first code unit. The existence of one means the\n  minimum length must be at least 1. */\n\n  if (firstcuflags < REQ_NONE)\n    {\n    re->first_codeunit = firstcu;\n    re->flags |= PCRE2_FIRSTSET;\n    minminlength++;\n\n    /* Handle caseless first code units. */\n\n    if ((firstcuflags & REQ_CASELESS) != 0)\n      {\n      if (firstcu < 128 || (!utf && !ucp && firstcu < 255))\n        {\n        if (cb.fcc[firstcu] != firstcu) re->flags |= PCRE2_FIRSTCASELESS;\n        }\n\n      /* The first code unit is > 128 in UTF or UCP mode, or > 255 otherwise.\n      In 8-bit UTF mode, code units in the range 128-255 are introductory code\n      units and cannot have another case, but if UCP is set they may do. */\n\n#ifdef SUPPORT_UNICODE\n#if PCRE2_CODE_UNIT_WIDTH == 8\n      else if (ucp && !utf && UCD_OTHERCASE(firstcu) != firstcu)\n        re->flags |= PCRE2_FIRSTCASELESS;\n#else\n      else if ((utf || ucp) && firstcu <= MAX_UTF_CODE_POINT &&\n               UCD_OTHERCASE(firstcu) != firstcu)\n        re->flags |= PCRE2_FIRSTCASELESS;\n#endif\n#endif  /* SUPPORT_UNICODE */\n      }\n    }\n\n  /* When there is no first code unit, for non-anchored patterns, see if we can\n  set the PCRE2_STARTLINE flag. This is helpful for multiline matches when all\n  branches start with ^ and also when all branches start with non-atomic .* for\n  non-DOTALL matches when *PRUNE and SKIP are not present. (There is an option\n  that disables this case.) */\n\n  else if ((re->overall_options & PCRE2_ANCHORED) == 0)\n    {\n    BOOL dotstar_anchor = ((optim_flags & PCRE2_OPTIM_DOTSTAR_ANCHOR) != 0);\n    if (is_startline(codestart, 0, &cb, 0, FALSE, dotstar_anchor))\n      re->flags |= PCRE2_STARTLINE;\n    }\n\n  /* Handle the \"required code unit\", if one is set. In the UTF case we can\n  increment the minimum minimum length only if we are sure this really is a\n  different character and not a non-starting code unit of the first character,\n  because the minimum length count is in characters, not code units. */\n\n  if (reqcuflags < REQ_NONE)\n    {\n#if PCRE2_CODE_UNIT_WIDTH == 16\n    if ((re->overall_options & PCRE2_UTF) == 0 ||   /* Not UTF */\n        firstcuflags >= REQ_NONE ||                 /* First not set */\n        (firstcu & 0xf800) != 0xd800 ||             /* First not surrogate */\n        (reqcu & 0xfc00) != 0xdc00)                 /* Req not low surrogate */\n#elif PCRE2_CODE_UNIT_WIDTH == 8\n    if ((re->overall_options & PCRE2_UTF) == 0 ||   /* Not UTF */\n        firstcuflags >= REQ_NONE ||                 /* First not set */\n        (firstcu & 0x80) == 0 ||                    /* First is ASCII */\n        (reqcu & 0x80) == 0)                        /* Req is ASCII */\n#endif\n      {\n      minminlength++;\n      }\n\n    /* In the case of an anchored pattern, set up the value only if it follows\n    a variable length item in the pattern. */\n\n    if ((re->overall_options & PCRE2_ANCHORED) == 0 ||\n        (reqcuflags & REQ_VARY) != 0)\n      {\n      re->last_codeunit = reqcu;\n      re->flags |= PCRE2_LASTSET;\n\n      /* Handle caseless required code units as for first code units (above). */\n\n      if ((reqcuflags & REQ_CASELESS) != 0)\n        {\n        if (reqcu < 128 || (!utf && !ucp && reqcu < 255))\n          {\n          if (cb.fcc[reqcu] != reqcu) re->flags |= PCRE2_LASTCASELESS;\n          }\n#ifdef SUPPORT_UNICODE\n#if PCRE2_CODE_UNIT_WIDTH == 8\n      else if (ucp && !utf && UCD_OTHERCASE(reqcu) != reqcu)\n        re->flags |= PCRE2_LASTCASELESS;\n#else\n      else if ((utf || ucp) && reqcu <= MAX_UTF_CODE_POINT &&\n               UCD_OTHERCASE(reqcu) != reqcu)\n        re->flags |= PCRE2_LASTCASELESS;\n#endif\n#endif  /* SUPPORT_UNICODE */\n        }\n      }\n    }\n\n  /* Study the compiled pattern to set up information such as a bitmap of\n  starting code units and a minimum matching length. */\n\n  if (PRIV(study)(re) != 0)\n    {\n    PCRE2_DEBUG_UNREACHABLE();\n    errorcode = ERR31;\n    goto HAD_CB_ERROR;\n    }\n\n  /* If study() set a bitmap of starting code units, it implies a minimum\n  length of at least one. */\n\n  if ((re->flags & PCRE2_FIRSTMAPSET) != 0 && minminlength == 0)\n    minminlength = 1;\n\n  /* If the minimum length set (or not set) by study() is less than the minimum\n  implied by required code units, override it. */\n\n  if (re->minlength < minminlength) re->minlength = minminlength;\n  }   /* End of start-of-match optimizations. */\n\n/* Control ends up here in all cases. When running under valgrind, make a\npattern's terminating zero defined again. If memory was obtained for the parsed\nversion of the pattern, free it before returning. Also free the list of named\ngroups if a larger one had to be obtained, and likewise the group information\nvector. */\n\n#ifdef SUPPORT_UNICODE\nPCRE2_ASSERT(cb.cranges == NULL);\n#endif\n\nEXIT:\n#ifdef SUPPORT_VALGRIND\nif (zero_terminated) VALGRIND_MAKE_MEM_DEFINED(pattern + patlen, CU2BYTES(1));\n#endif\nif (cb.parsed_pattern != stack_parsed_pattern)\n  ccontext->memctl.free(cb.parsed_pattern, ccontext->memctl.memory_data);\nif (cb.named_group_list_size > NAMED_GROUP_LIST_SIZE)\n  ccontext->memctl.free((void *)cb.named_groups, ccontext->memctl.memory_data);\nif (cb.groupinfo != stack_groupinfo)\n  ccontext->memctl.free((void *)cb.groupinfo, ccontext->memctl.memory_data);\n\nreturn re;    /* Will be NULL after an error */\n\n/* Errors discovered in parse_regex() set the offset value in the compile\nblock. Errors discovered before it is called must compute it from the ptr\nvalue. After parse_regex() is called, the offset in the compile block is set to\nthe end of the pattern, but certain errors in compile_regex() may reset it if\nan offset is available in the parsed pattern. */\n\nHAD_CB_ERROR:\nptr = pattern + cb.erroroffset;\n\nHAD_EARLY_ERROR:\nPCRE2_ASSERT(ptr >= pattern); /* Ensure we don't return invalid erroroffset */\nPCRE2_ASSERT(ptr <= (pattern + patlen));\n*erroroffset = ptr - pattern;\n\nHAD_ERROR:\n*errorptr = errorcode;\npcre2_code_free(re);\nre = NULL;\n\n#ifdef SUPPORT_WIDE_CHARS\nif (cb.cranges != NULL)\n  {\n  class_ranges* cranges = cb.cranges;\n  do\n    {\n    class_ranges* next_cranges = cranges->next;\n    cb.cx->memctl.free(cranges, cb.cx->memctl.memory_data);\n    cranges = next_cranges;\n    }\n  while (cranges != NULL);\n  }\n#endif\ngoto EXIT;\n}\n\n/* These #undefs are here to enable unity builds with CMake. */\n\n#undef NLBLOCK /* Block containing newline information */\n#undef PSSTART /* Field containing processed string start */\n#undef PSEND   /* Field containing processed string end */\n\n/* End of pcre2_compile.c */\n"
  },
  {
    "path": "Libraries/PCRE/pcre2_compile.h",
    "content": "/*************************************************\n*      Perl-Compatible Regular Expressions       *\n*************************************************/\n\n/* PCRE2 is a library of functions to support regular expressions whose syntax\nand semantics are as close as possible to those of the Perl 5 language.\n\n                       Written by Philip Hazel\n     Original API code Copyright (c) 1997-2012 University of Cambridge\n          New API code Copyright (c) 2016-2024 University of Cambridge\n\n-----------------------------------------------------------------------------\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n    * Redistributions of source code must retain the above copyright notice,\n      this list of conditions and the following disclaimer.\n\n    * Redistributions in binary form must reproduce the above copyright\n      notice, this list of conditions and the following disclaimer in the\n      documentation and/or other materials provided with the distribution.\n\n    * Neither the name of the University of Cambridge nor the names of its\n      contributors may be used to endorse or promote products derived from\n      this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\nLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\nCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\nSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\nINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\nCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGE.\n-----------------------------------------------------------------------------\n*/\n\n#ifndef PCRE2_COMPILE_H_IDEMPOTENT_GUARD\n#define PCRE2_COMPILE_H_IDEMPOTENT_GUARD\n\n#include \"pcre2_internal.h\"\n\n/* Compile time error code numbers. They are given names so that they can more\neasily be tracked. When a new number is added, the tables called eint1 and\neint2 in pcre2posix.c may need to be updated, and a new error text must be\nadded to compile_error_texts in pcre2_error.c. Also, the error codes in\npcre2.h.in must be updated - their values are exactly 100 greater than these\nvalues. */\n\nenum { ERR0 = COMPILE_ERROR_BASE,\n       ERR1,  ERR2,  ERR3,  ERR4,  ERR5,  ERR6,  ERR7,  ERR8,  ERR9,  ERR10,\n       ERR11, ERR12, ERR13, ERR14, ERR15, ERR16, ERR17, ERR18, ERR19, ERR20,\n       ERR21, ERR22, ERR23, ERR24, ERR25, ERR26, ERR27, ERR28, ERR29, ERR30,\n       ERR31, ERR32, ERR33, ERR34, ERR35, ERR36, ERR37, ERR38, ERR39, ERR40,\n       ERR41, ERR42, ERR43, ERR44, ERR45, ERR46, ERR47, ERR48, ERR49, ERR50,\n       ERR51, ERR52, ERR53, ERR54, ERR55, ERR56, ERR57, ERR58, ERR59, ERR60,\n       ERR61, ERR62, ERR63, ERR64, ERR65, ERR66, ERR67, ERR68, ERR69, ERR70,\n       ERR71, ERR72, ERR73, ERR74, ERR75, ERR76, ERR77, ERR78, ERR79, ERR80,\n       ERR81, ERR82, ERR83, ERR84, ERR85, ERR86, ERR87, ERR88, ERR89, ERR90,\n       ERR91, ERR92, ERR93, ERR94, ERR95, ERR96, ERR97, ERR98, ERR99, ERR100,\n       ERR101,ERR102,ERR103,ERR104,ERR105,ERR106,ERR107,ERR108,ERR109,ERR110,\n       ERR111,ERR112,ERR113,ERR114,ERR115,ERR116 };\n\n/* Code values for parsed patterns, which are stored in a vector of 32-bit\nunsigned ints. Values less than META_END are literal data values. The coding\nfor identifying the item is in the top 16-bits, leaving 16 bits for the\nadditional data that some of them need. The META_CODE, META_DATA, and META_DIFF\nmacros are used to manipulate parsed pattern elements.\n\nNOTE: When these definitions are changed, the table of extra lengths for each\ncode (meta_extra_lengths) must be updated to remain in step. */\n\n#define META_END              0x80000000u  /* End of pattern */\n\n#define META_ALT              0x80010000u  /* alternation */\n#define META_ATOMIC           0x80020000u  /* atomic group */\n#define META_BACKREF          0x80030000u  /* Back ref */\n#define META_BACKREF_BYNAME   0x80040000u  /* \\k'name' */\n#define META_BIGVALUE         0x80050000u  /* Next is a literal > META_END */\n#define META_CALLOUT_NUMBER   0x80060000u  /* (?C with numerical argument */\n#define META_CALLOUT_STRING   0x80070000u  /* (?C with string argument */\n#define META_CAPTURE          0x80080000u  /* Capturing parenthesis */\n#define META_CIRCUMFLEX       0x80090000u  /* ^ metacharacter */\n#define META_CLASS            0x800a0000u  /* start non-empty class */\n#define META_CLASS_EMPTY      0x800b0000u  /* empty class */\n#define META_CLASS_EMPTY_NOT  0x800c0000u  /* negative empty class */\n#define META_CLASS_END        0x800d0000u  /* end of non-empty class */\n#define META_CLASS_NOT        0x800e0000u  /* start non-empty negative class */\n#define META_COND_ASSERT      0x800f0000u  /* (?(?assertion)... */\n#define META_COND_DEFINE      0x80100000u  /* (?(DEFINE)... */\n#define META_COND_NAME        0x80110000u  /* (?(<name>)... */\n#define META_COND_NUMBER      0x80120000u  /* (?(digits)... */\n#define META_COND_RNAME       0x80130000u  /* (?(R&name)... */\n#define META_COND_RNUMBER     0x80140000u  /* (?(Rdigits)... */\n#define META_COND_VERSION     0x80150000u  /* (?(VERSION<op>x.y)... */\n#define META_OFFSET           0x80160000u  /* Setting offset for various\n                                              META codes (e.g. META_SCS_NAME) */\n#define META_SCS              0x80170000u  /* (*scan_substring:... */\n#define META_SCS_NAME         0x80180000u  /* Next <name> of scan_substring */\n#define META_SCS_NUMBER       0x80190000u  /* Next digits of scan_substring */\n#define META_DOLLAR           0x801a0000u  /* $ metacharacter */\n#define META_DOT              0x801b0000u  /* . metacharacter */\n#define META_ESCAPE           0x801c0000u  /* \\d and friends */\n#define META_KET              0x801d0000u  /* closing parenthesis */\n#define META_NOCAPTURE        0x801e0000u  /* no capture parens */\n#define META_OPTIONS          0x801f0000u  /* (?i) and friends */\n#define META_POSIX            0x80200000u  /* POSIX class item */\n#define META_POSIX_NEG        0x80210000u  /* negative POSIX class item */\n#define META_RANGE_ESCAPED    0x80220000u  /* range with at least one escape */\n#define META_RANGE_LITERAL    0x80230000u  /* range defined literally */\n#define META_RECURSE          0x80240000u  /* Recursion */\n#define META_RECURSE_BYNAME   0x80250000u  /* (?&name) */\n#define META_SCRIPT_RUN       0x80260000u  /* (*script_run:...) */\n\n/* These must be kept together to make it easy to check that an assertion\nis present where expected in a conditional group. */\n\n#define META_LOOKAHEAD        0x80270000u  /* (?= */\n#define META_LOOKAHEADNOT     0x80280000u  /* (?! */\n#define META_LOOKBEHIND       0x80290000u  /* (?<= */\n#define META_LOOKBEHINDNOT    0x802a0000u  /* (?<! */\n\n/* These cannot be conditions */\n\n#define META_LOOKAHEAD_NA     0x802b0000u  /* (*napla: */\n#define META_LOOKBEHIND_NA    0x802c0000u  /* (*naplb: */\n\n/* These must be kept in this order, with consecutive values, and the _ARG\nversions of COMMIT, PRUNE, SKIP, and THEN immediately after their non-argument\nversions. */\n\n#define META_MARK             0x802d0000u  /* (*MARK) */\n#define META_ACCEPT           0x802e0000u  /* (*ACCEPT) */\n#define META_FAIL             0x802f0000u  /* (*FAIL) */\n#define META_COMMIT           0x80300000u  /* These               */\n#define META_COMMIT_ARG       0x80310000u  /*   pairs             */\n#define META_PRUNE            0x80320000u  /*     must            */\n#define META_PRUNE_ARG        0x80330000u  /*       be            */\n#define META_SKIP             0x80340000u  /*         kept        */\n#define META_SKIP_ARG         0x80350000u  /*           in        */\n#define META_THEN             0x80360000u  /*             this    */\n#define META_THEN_ARG         0x80370000u  /*               order */\n\n/* These must be kept in groups of adjacent 3 values, and all together. */\n\n#define META_ASTERISK         0x80380000u  /* *  */\n#define META_ASTERISK_PLUS    0x80390000u  /* *+ */\n#define META_ASTERISK_QUERY   0x803a0000u  /* *? */\n#define META_PLUS             0x803b0000u  /* +  */\n#define META_PLUS_PLUS        0x803c0000u  /* ++ */\n#define META_PLUS_QUERY       0x803d0000u  /* +? */\n#define META_QUERY            0x803e0000u  /* ?  */\n#define META_QUERY_PLUS       0x803f0000u  /* ?+ */\n#define META_QUERY_QUERY      0x80400000u  /* ?? */\n#define META_MINMAX           0x80410000u  /* {n,m}  repeat */\n#define META_MINMAX_PLUS      0x80420000u  /* {n,m}+ repeat */\n#define META_MINMAX_QUERY     0x80430000u  /* {n,m}? repeat */\n\n/* These meta codes must be kept in a group, with the OR/SUB/XOR in\nthis order, and AND/NOT at the start/end. */\n\n#define META_ECLASS_AND       0x80440000u  /* && (or &) in a class */\n#define META_ECLASS_OR        0x80450000u  /* || (or |, +) in a class */\n#define META_ECLASS_SUB       0x80460000u  /* -- (or -) in a class */\n#define META_ECLASS_XOR       0x80470000u  /* ~~ (or ^) in a class */\n#define META_ECLASS_NOT       0x80480000u  /* ! in a class */\n\n/* Convenience aliases. */\n\n#define META_FIRST_QUANTIFIER META_ASTERISK\n#define META_LAST_QUANTIFIER  META_MINMAX_QUERY\n\n/* This is a special \"meta code\" that is used only to distinguish (*asr: from\n(*sr: in the table of alphabetic assertions. It is never stored in the parsed\npattern because (*asr: is turned into (*sr:(*atomic: at that stage. There is\ntherefore no need for it to have a length entry, so use a high value. */\n\n#define META_ATOMIC_SCRIPT_RUN 0x8fff0000u\n\n/* Macros for manipulating elements of the parsed pattern vector. */\n\n#define META_CODE(x)   (x & 0xffff0000u)\n#define META_DATA(x)   (x & 0x0000ffffu)\n#define META_DIFF(x,y) ((x-y)>>16)\n\n/* Extended class management flags. */\n\n#define CLASS_IS_ECLASS 0x1\n\n/* Macro for the highest character value. */\n\n#if PCRE2_CODE_UNIT_WIDTH == 8\n#define MAX_UCHAR_VALUE 0xffu\n#elif PCRE2_CODE_UNIT_WIDTH == 16\n#define MAX_UCHAR_VALUE 0xffffu\n#else\n#define MAX_UCHAR_VALUE 0xffffffffu\n#endif\n\n#define GET_MAX_CHAR_VALUE(utf) \\\n  ((utf) ? MAX_UTF_CODE_POINT : MAX_UCHAR_VALUE)\n\n/* Macro for setting individual bits in class bitmaps. */\n\n#define SETBIT(a,b) a[(b) >> 3] |= (uint8_t)(1u << ((b) & 0x7))\n\n/* Macro for 8 bit specific checks. */\n#if PCRE2_CODE_UNIT_WIDTH == 8\n#define SELECT_VALUE8(value8, value) (value8)\n#else\n#define SELECT_VALUE8(value8, value) (value)\n#endif\n\n/* Macro for aligning data. */\n#define CLIST_ALIGN_TO(base, align) \\\n  ((base + ((size_t)(align) - 1)) & ~((size_t)(align) - 1))\n\n/* Structure for holding information about an OP_ECLASS internal operand.\nAn \"operand\" here could be just a single OP_[X]CLASS, or it could be some\ncomplex expression; but it's some sequence of ECL_* codes which pushes one\nvalue to the stack. */\ntypedef struct {\n  /* The position of the operand - or NULL if (lengthptr != NULL). */\n  PCRE2_UCHAR *code_start;\n  PCRE2_SIZE length;\n  /* The operand's type if it is a single code (ECL_XCLASS, ECL_ANY, ECL_NONE);\n  otherwise zero if the operand is not atomic. */\n  uint8_t op_single_type;\n  /* Regardless of whether it's a single code or not, we fully constant-fold\n  the bitmap for code points < 256. */\n  class_bits_storage bits;\n} eclass_op_info;\n\n/* Macros for the definitions below, to prevent name collisions. */\n\n#define _pcre2_posix_class_maps          PCRE2_SUFFIX(_pcre2_posix_class_maps)\n#define _pcre2_update_classbits          PCRE2_SUFFIX(_pcre2_update_classbits_)\n#define _pcre2_compile_class_nested      PCRE2_SUFFIX(_pcre2_compile_class_nested_)\n#define _pcre2_compile_class_not_nested  PCRE2_SUFFIX(_pcre2_compile_class_not_nested_)\n\n\n/* Indices of the POSIX classes in posix_names, posix_name_lengths,\nposix_class_maps, and posix_substitutes. They must be kept in sync. */\n\n#define PC_DIGIT   7\n#define PC_GRAPH   8\n#define PC_PRINT   9\n#define PC_PUNCT  10\n#define PC_XDIGIT 13\n\nextern const int PRIV(posix_class_maps)[];\n\n\n/* Set bits in classbits according to the property type */\n\nvoid PRIV(update_classbits)(uint32_t ptype, uint32_t pdata, BOOL negated,\n  uint8_t *classbits);\n\n/* Compile the META codes from start_ptr...end_ptr, writing a single OP_CLASS\nOP_CLASS, OP_NCLASS, OP_XCLASS, or OP_ALLANY into pcode. */\n\nuint32_t *PRIV(compile_class_not_nested)(uint32_t options, uint32_t xoptions,\n  uint32_t *start_ptr, PCRE2_UCHAR **pcode, BOOL negate_class, BOOL* has_bitmap,\n  int *errorcodeptr, compile_block *cb, PCRE2_SIZE *lengthptr);\n\n/* Compile the META codes in pptr into opcodes written to pcode. The pptr must\nstart at a META_CLASS or META_CLASS_NOT.\n\nThe pptr will be left pointing at the matching META_CLASS_END. */\n\nBOOL PRIV(compile_class_nested)(uint32_t options, uint32_t xoptions,\n  uint32_t **pptr, PCRE2_UCHAR **pcode, int *errorcodeptr,\n  compile_block *cb, PCRE2_SIZE *lengthptr);\n\n#endif  /* PCRE2_COMPILE_H_IDEMPOTENT_GUARD */\n\n/* End of pcre2_compile.h */\n"
  },
  {
    "path": "Libraries/PCRE/pcre2_compile_class.c",
    "content": "/*************************************************\n*      Perl-Compatible Regular Expressions       *\n*************************************************/\n\n/* PCRE is a library of functions to support regular expressions whose syntax\nand semantics are as close as possible to those of the Perl 5 language.\n\n                       Written by Philip Hazel\n     Original API code Copyright (c) 1997-2012 University of Cambridge\n          New API code Copyright (c) 2016-2024 University of Cambridge\n\n-----------------------------------------------------------------------------\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n    * Redistributions of source code must retain the above copyright notice,\n      this list of conditions and the following disclaimer.\n\n    * Redistributions in binary form must reproduce the above copyright\n      notice, this list of conditions and the following disclaimer in the\n      documentation and/or other materials provided with the distribution.\n\n    * Neither the name of the University of Cambridge nor the names of its\n      contributors may be used to endorse or promote products derived from\n      this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\nLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\nCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\nSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\nINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\nCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGE.\n-----------------------------------------------------------------------------\n*/\n\n#ifdef HAVE_CONFIG_H\n#include \"config.h\"\n#endif\n\n#include \"pcre2_compile.h\"\n\ntypedef struct {\n  /* Option bits for eclass. */\n  uint32_t options;\n  uint32_t xoptions;\n  /* Rarely used members. */\n  int *errorcodeptr;\n  compile_block *cb;\n  /* Bitmap is needed. */\n  BOOL needs_bitmap;\n} eclass_context;\n\n/* Checks the allowed tokens at the end of a class structure in debug mode.\nWhen a new token is not processed by all loops, and the token is equals to\na) one of the cases here:\n   the compiler will complain about a duplicated case value.\nb) none of the cases here:\n   the loop without the handler will stop with an assertion failure. */\n\n#ifdef PCRE2_DEBUG\n#define CLASS_END_CASES(meta) \\\n  default: \\\n  PCRE2_ASSERT((meta) <= META_END); \\\n  /* Fall through */ \\\n  case META_CLASS: \\\n  case META_CLASS_NOT: \\\n  case META_CLASS_EMPTY: \\\n  case META_CLASS_EMPTY_NOT: \\\n  case META_CLASS_END: \\\n  case META_ECLASS_AND: \\\n  case META_ECLASS_OR: \\\n  case META_ECLASS_SUB: \\\n  case META_ECLASS_XOR: \\\n  case META_ECLASS_NOT:\n#else\n#define CLASS_END_CASES(meta) \\\n  default:\n#endif\n\n#ifdef SUPPORT_WIDE_CHARS\n\n/* Heapsort algorithm. */\n\nstatic void do_heapify(uint32_t *buffer, size_t size, size_t i)\n{\nsize_t max;\nsize_t left;\nsize_t right;\nuint32_t tmp1, tmp2;\n\nwhile (TRUE)\n  {\n  max = i;\n  left = (i << 1) + 2;\n  right = left + 2;\n\n  if (left < size && buffer[left] > buffer[max]) max = left;\n  if (right < size && buffer[right] > buffer[max]) max = right;\n  if (i == max) return;\n\n  /* Swap items. */\n  tmp1 = buffer[i];\n  tmp2 = buffer[i + 1];\n  buffer[i] = buffer[max];\n  buffer[i + 1] = buffer[max + 1];\n  buffer[max] = tmp1;\n  buffer[max + 1] = tmp2;\n  i = max;\n  }\n}\n\n#ifdef SUPPORT_UNICODE\n\n#define PARSE_CLASS_UTF               0x1\n#define PARSE_CLASS_CASELESS_UTF      0x2\n#define PARSE_CLASS_RESTRICTED_UTF    0x4\n#define PARSE_CLASS_TURKISH_UTF       0x8\n\n/* Get the range of nocase characters which includes the\n'c' character passed as argument, or directly follows 'c'. */\n\nstatic const uint32_t*\nget_nocase_range(uint32_t c)\n{\nuint32_t left = 0;\nuint32_t right = PRIV(ucd_nocase_ranges_size);\nuint32_t middle;\n\nif (c > MAX_UTF_CODE_POINT) return PRIV(ucd_nocase_ranges) + right;\n\nwhile (TRUE)\n  {\n  /* Range end of the middle element. */\n  middle = ((left + right) >> 1) | 0x1;\n\n  if (PRIV(ucd_nocase_ranges)[middle] <= c)\n    left = middle + 1;\n  else if (middle > 1 && PRIV(ucd_nocase_ranges)[middle - 2] > c)\n    right = middle - 1;\n  else\n    return PRIV(ucd_nocase_ranges) + (middle - 1);\n  }\n}\n\n/* Get the list of othercase characters, which belongs to the passed range.\nCreate ranges from these characters, and append them to the buffer argument. */\n\nstatic size_t\nutf_caseless_extend(uint32_t start, uint32_t end, uint32_t options,\n  uint32_t *buffer)\n{\nuint32_t new_start = start;\nuint32_t new_end = end;\nuint32_t c = start;\nconst uint32_t *list;\nuint32_t tmp[3];\nsize_t result = 2;\nconst uint32_t *skip_range = get_nocase_range(c);\nuint32_t skip_start = skip_range[0];\n\n#if PCRE2_CODE_UNIT_WIDTH == 8\nPCRE2_ASSERT(options & PARSE_CLASS_UTF);\n#endif\n\n#if PCRE2_CODE_UNIT_WIDTH == 32\nif (end > MAX_UTF_CODE_POINT) end = MAX_UTF_CODE_POINT;\n#endif\n\nwhile (c <= end)\n  {\n  uint32_t co;\n\n  if (c > skip_start)\n    {\n    c = skip_range[1];\n    skip_range += 2;\n    skip_start = skip_range[0];\n    continue;\n    }\n\n  /* Compute caseless set. */\n\n  if ((options & (PARSE_CLASS_TURKISH_UTF|PARSE_CLASS_RESTRICTED_UTF)) ==\n        PARSE_CLASS_TURKISH_UTF &&\n      UCD_ANY_I(c))\n    {\n    co = PRIV(ucd_turkish_dotted_i_caseset) + (UCD_DOTTED_I(c)? 0 : 3);\n    }\n  else if ((co = UCD_CASESET(c)) != 0 &&\n           (options & PARSE_CLASS_RESTRICTED_UTF) != 0 &&\n           PRIV(ucd_caseless_sets)[co] < 128)\n    {\n    co = 0;  /* Ignore the caseless set if it's restricted. */\n    }\n\n  if (co != 0)\n    list = PRIV(ucd_caseless_sets) + co;\n  else\n    {\n    co = UCD_OTHERCASE(c);\n    list = tmp;\n    tmp[0] = c;\n    tmp[1] = NOTACHAR;\n\n    if (co != c)\n      {\n      tmp[1] = co;\n      tmp[2] = NOTACHAR;\n      }\n    }\n  c++;\n\n  /* Add characters. */\n  do\n    {\n#if PCRE2_CODE_UNIT_WIDTH == 16\n    if (!(options & PARSE_CLASS_UTF) && *list > 0xffff) continue;\n#endif\n\n    if (*list < new_start)\n      {\n      if (*list + 1 == new_start)\n        {\n        new_start--;\n        continue;\n        }\n      }\n    else if (*list > new_end)\n      {\n      if (*list - 1 == new_end)\n        {\n        new_end++;\n        continue;\n        }\n      }\n    else continue;\n\n    result += 2;\n    if (buffer != NULL)\n      {\n      buffer[0] = *list;\n      buffer[1] = *list;\n      buffer += 2;\n      }\n    }\n  while (*(++list) != NOTACHAR);\n  }\n\n  if (buffer != NULL)\n    {\n    buffer[0] = new_start;\n    buffer[1] = new_end;\n    buffer += 2;\n    (void)buffer;\n    }\n  return result;\n}\n\n#endif\n\n/* Add a character list to a buffer. */\n\nstatic size_t\nappend_char_list(const uint32_t *p, uint32_t *buffer)\n{\nconst uint32_t *n;\nsize_t result = 0;\n\nwhile (*p != NOTACHAR)\n  {\n  n = p;\n  while (n[0] == n[1] - 1) n++;\n\n  PCRE2_ASSERT(*p < 0xffff);\n\n  if (buffer != NULL)\n    {\n    buffer[0] = *p;\n    buffer[1] = *n;\n    buffer += 2;\n    }\n\n  result += 2;\n  p = n + 1;\n  }\n\n  return result;\n}\n\nstatic uint32_t\nget_highest_char(uint32_t options)\n{\n(void)options; /* Avoid compiler warning. */\n\n#if PCRE2_CODE_UNIT_WIDTH == 8\nreturn MAX_UTF_CODE_POINT;\n#else\n#ifdef SUPPORT_UNICODE\nreturn GET_MAX_CHAR_VALUE((options & PARSE_CLASS_UTF) != 0);\n#else\nreturn MAX_UCHAR_VALUE;\n#endif\n#endif\n}\n\n/* Add a negated character list to a buffer. */\nstatic size_t\nappend_negated_char_list(const uint32_t *p, uint32_t options, uint32_t *buffer)\n{\nconst uint32_t *n;\nuint32_t start = 0;\nsize_t result = 2;\n\nPCRE2_ASSERT(*p > 0);\n\nwhile (*p != NOTACHAR)\n  {\n  n = p;\n  while (n[0] == n[1] - 1) n++;\n\n  PCRE2_ASSERT(*p < 0xffff);\n\n  if (buffer != NULL)\n    {\n    buffer[0] = start;\n    buffer[1] = *p - 1;\n    buffer += 2;\n    }\n\n  result += 2;\n  start = *n + 1;\n  p = n + 1;\n  }\n\n  if (buffer != NULL)\n    {\n    buffer[0] = start;\n    buffer[1] = get_highest_char(options);\n    buffer += 2;\n    (void)buffer;\n    }\n\n  return result;\n}\n\nstatic uint32_t *\nappend_non_ascii_range(uint32_t options, uint32_t *buffer)\n{\n  if (buffer == NULL) return NULL;\n\n  buffer[0] = 0x100;\n  buffer[1] = get_highest_char(options);\n  return buffer + 2;\n}\n\nstatic size_t\nparse_class(uint32_t *ptr, uint32_t options, uint32_t *buffer)\n{\nsize_t total_size = 0;\nsize_t size;\nuint32_t meta_arg;\nuint32_t start_char;\n\nwhile (TRUE)\n  {\n  switch (META_CODE(*ptr))\n    {\n    case META_ESCAPE:\n      meta_arg = META_DATA(*ptr);\n      switch (meta_arg)\n        {\n        case ESC_D:\n        case ESC_W:\n        case ESC_S:\n        buffer = append_non_ascii_range(options, buffer);\n        total_size += 2;\n        break;\n\n        case ESC_h:\n        size = append_char_list(PRIV(hspace_list), buffer);\n        total_size += size;\n        if (buffer != NULL) buffer += size;\n        break;\n\n        case ESC_H:\n        size = append_negated_char_list(PRIV(hspace_list), options, buffer);\n        total_size += size;\n        if (buffer != NULL) buffer += size;\n        break;\n\n        case ESC_v:\n        size = append_char_list(PRIV(vspace_list), buffer);\n        total_size += size;\n        if (buffer != NULL) buffer += size;\n        break;\n\n        case ESC_V:\n        size = append_negated_char_list(PRIV(vspace_list), options, buffer);\n        total_size += size;\n        if (buffer != NULL) buffer += size;\n        break;\n\n        case ESC_p:\n        case ESC_P:\n        ptr++;\n        if (meta_arg == ESC_p && (*ptr >> 16) == PT_ANY)\n          {\n          if (buffer != NULL)\n            {\n            buffer[0] = 0;\n            buffer[1] = get_highest_char(options);\n            buffer += 2;\n            }\n          total_size += 2;\n          }\n        break;\n        }\n      ptr++;\n      continue;\n    case META_POSIX_NEG:\n      buffer = append_non_ascii_range(options, buffer);\n      total_size += 2;\n      ptr += 2;\n      continue;\n    case META_POSIX:\n      ptr += 2;\n      continue;\n    case META_BIGVALUE:\n      /* Character literal */\n      ptr++;\n      break;\n    CLASS_END_CASES(*ptr)\n      if (*ptr >= META_END) return total_size;\n      break;\n    }\n\n    start_char = *ptr;\n\n    if (ptr[1] == META_RANGE_LITERAL || ptr[1] == META_RANGE_ESCAPED)\n      {\n      ptr += 2;\n      PCRE2_ASSERT(*ptr < META_END || *ptr == META_BIGVALUE);\n\n      if (*ptr == META_BIGVALUE) ptr++;\n\n#ifdef EBCDIC\n#error \"Missing EBCDIC support\"\n#endif\n      }\n\n#ifdef SUPPORT_UNICODE\n    if (options & PARSE_CLASS_CASELESS_UTF)\n      {\n      size = utf_caseless_extend(start_char, *ptr++, options, buffer);\n      if (buffer != NULL) buffer += size;\n      total_size += size;\n      continue;\n      }\n#endif\n\n    if (buffer != NULL)\n      {\n      buffer[0] = start_char;\n      buffer[1] = *ptr;\n      buffer += 2;\n      }\n\n    ptr++;\n    total_size += 2;\n  }\n\n  return total_size;\n}\n\n/* Extra uint32_t values for storing the lengths of range lists in\nthe worst case. Two uint32_t lengths and a range end for a range\nstarting before 255 */\n#define CHAR_LIST_EXTRA_SIZE 3\n\n/* Starting character values for each character list. */\n\nstatic const uint32_t char_list_starts[] = {\n#if PCRE2_CODE_UNIT_WIDTH == 32\n  XCL_CHAR_LIST_HIGH_32_START,\n#endif\n#if PCRE2_CODE_UNIT_WIDTH == 32 || defined SUPPORT_UNICODE\n  XCL_CHAR_LIST_LOW_32_START,\n#endif\n  XCL_CHAR_LIST_HIGH_16_START,\n  /* Must be terminated by XCL_CHAR_LIST_LOW_16_START,\n  which also represents the end of the bitset. */\n  XCL_CHAR_LIST_LOW_16_START,\n};\n\nstatic class_ranges *\ncompile_optimize_class(uint32_t *start_ptr, uint32_t options,\n  uint32_t xoptions, compile_block *cb)\n{\nclass_ranges* cranges;\nuint32_t *ptr;\nuint32_t *buffer;\nuint32_t *dst;\nuint32_t class_options = 0;\nsize_t range_list_size = 0, total_size, i;\nuint32_t tmp1, tmp2;\nconst uint32_t *char_list_next;\nuint16_t *next_char;\nuint32_t char_list_start, char_list_end;\nuint32_t range_start, range_end;\n\n#ifdef SUPPORT_UNICODE\nif (options & PCRE2_UTF)\n  class_options |= PARSE_CLASS_UTF;\n\nif ((options & PCRE2_CASELESS) && (options & (PCRE2_UTF|PCRE2_UCP)))\n  class_options |= PARSE_CLASS_CASELESS_UTF;\n\nif (xoptions & PCRE2_EXTRA_CASELESS_RESTRICT)\n  class_options |= PARSE_CLASS_RESTRICTED_UTF;\n\nif (xoptions & PCRE2_EXTRA_TURKISH_CASING)\n  class_options |= PARSE_CLASS_TURKISH_UTF;\n#endif\n\n/* Compute required space for the range. */\n\nrange_list_size = parse_class(start_ptr, class_options, NULL);\nPCRE2_ASSERT((range_list_size & 0x1) == 0);\n\n/* Allocate buffer. The total_size also represents the end of the buffer. */\n\ntotal_size = range_list_size +\n   ((range_list_size >= 2) ? CHAR_LIST_EXTRA_SIZE : 0);\n\ncranges = cb->cx->memctl.malloc(\n  sizeof(class_ranges) + total_size * sizeof(uint32_t),\n  cb->cx->memctl.memory_data);\n\nif (cranges == NULL) return NULL;\n\ncranges->next = NULL;\ncranges->range_list_size = (uint16_t)range_list_size;\ncranges->char_lists_types = 0;\ncranges->char_lists_size = 0;\ncranges->char_lists_start = 0;\n\nif (range_list_size == 0) return cranges;\n\nbuffer = (uint32_t*)(cranges + 1);\nparse_class(start_ptr, class_options, buffer);\n\n/* Using <= instead of == to help static analysis. */\nif (range_list_size <= 2) return cranges;\n\n/* In-place sorting of ranges. */\n\ni = (((range_list_size >> 2) - 1) << 1);\nwhile (TRUE)\n  {\n  do_heapify(buffer, range_list_size, i);\n  if (i == 0) break;\n  i -= 2;\n  }\n\ni = range_list_size - 2;\nwhile (TRUE)\n  {\n  tmp1 = buffer[i];\n  tmp2 = buffer[i + 1];\n  buffer[i] = buffer[0];\n  buffer[i + 1] = buffer[1];\n  buffer[0] = tmp1;\n  buffer[1] = tmp2;\n\n  do_heapify(buffer, i, 0);\n  if (i == 0) break;\n  i -= 2;\n  }\n\n/* Merge ranges whenever possible. */\ndst = buffer;\nptr = buffer + 2;\nrange_list_size -= 2;\n\n/* The second condition is a very rare corner case, where the end of the last\nrange is the maximum character. This range cannot be extended further. */\n\nwhile (range_list_size > 0 && dst[1] != ~(uint32_t)0)\n  {\n  if (dst[1] + 1 < ptr[0])\n    {\n    dst += 2;\n    dst[0] = ptr[0];\n    dst[1] = ptr[1];\n    }\n  else if (dst[1] < ptr[1]) dst[1] = ptr[1];\n\n  ptr += 2;\n  range_list_size -= 2;\n  }\n\nPCRE2_ASSERT(dst[1] <= get_highest_char(class_options));\n\n/* When the number of ranges are less than six,\nthey are not converted to range lists. */\n\nptr = buffer;\nwhile (ptr < dst && ptr[1] < 0x100) ptr += 2;\nif (dst - ptr < (2 * (6 - 1)))\n  {\n  cranges->range_list_size = (uint16_t)(dst + 2 - buffer);\n  return cranges;\n  }\n\n/* Compute character lists structures. */\n\nchar_list_next = char_list_starts;\nchar_list_start = *char_list_next++;\n#if PCRE2_CODE_UNIT_WIDTH == 32\nchar_list_end = XCL_CHAR_LIST_HIGH_32_END;\n#elif defined SUPPORT_UNICODE\nchar_list_end = XCL_CHAR_LIST_LOW_32_END;\n#else\nchar_list_end = XCL_CHAR_LIST_HIGH_16_END;\n#endif\nnext_char = (uint16_t*)(buffer + total_size);\n\ntmp1 = 0;\ntmp2 = ((sizeof(char_list_starts) / sizeof(uint32_t)) - 1) * XCL_TYPE_BIT_LEN;\nPCRE2_ASSERT(tmp2 <= 3 * XCL_TYPE_BIT_LEN && tmp2 >= XCL_TYPE_BIT_LEN);\nrange_start = dst[0];\nrange_end = dst[1];\n\nwhile (TRUE)\n  {\n  if (range_start >= char_list_start)\n    {\n    if (range_start == range_end || range_end < char_list_end)\n      {\n      tmp1++;\n      next_char--;\n\n      if (char_list_start < XCL_CHAR_LIST_LOW_32_START)\n        *next_char = (uint16_t)((range_end << XCL_CHAR_SHIFT) | XCL_CHAR_END);\n      else\n        *(uint32_t*)(--next_char) =\n          (range_end << XCL_CHAR_SHIFT) | XCL_CHAR_END;\n      }\n\n    if (range_start < range_end)\n      {\n      if (range_start > char_list_start)\n        {\n        tmp1++;\n        next_char--;\n\n        if (char_list_start < XCL_CHAR_LIST_LOW_32_START)\n          *next_char = (uint16_t)(range_start << XCL_CHAR_SHIFT);\n        else\n          *(uint32_t*)(--next_char) = (range_start << XCL_CHAR_SHIFT);\n        }\n      else\n        cranges->char_lists_types |= XCL_BEGIN_WITH_RANGE << tmp2;\n      }\n\n    PCRE2_ASSERT((uint32_t*)next_char >= dst + 2);\n\n    if (dst > buffer)\n      {\n      dst -= 2;\n      range_start = dst[0];\n      range_end = dst[1];\n      continue;\n      }\n\n    range_start = 0;\n    range_end = 0;\n    }\n\n  if (range_end >= char_list_start)\n    {\n    PCRE2_ASSERT(range_start < char_list_start);\n\n    if (range_end < char_list_end)\n      {\n      tmp1++;\n      next_char--;\n\n      if (char_list_start < XCL_CHAR_LIST_LOW_32_START)\n        *next_char = (uint16_t)((range_end << XCL_CHAR_SHIFT) | XCL_CHAR_END);\n      else\n        *(uint32_t*)(--next_char) =\n          (range_end << XCL_CHAR_SHIFT) | XCL_CHAR_END;\n\n      PCRE2_ASSERT((uint32_t*)next_char >= dst + 2);\n      }\n\n    cranges->char_lists_types |= XCL_BEGIN_WITH_RANGE << tmp2;\n    }\n\n  if (tmp1 >= XCL_ITEM_COUNT_MASK)\n    {\n    cranges->char_lists_types |= XCL_ITEM_COUNT_MASK << tmp2;\n    next_char--;\n\n    if (char_list_start < XCL_CHAR_LIST_LOW_32_START)\n      *next_char = (uint16_t)tmp1;\n    else\n      *(uint32_t*)(--next_char) = tmp1;\n    }\n  else\n    cranges->char_lists_types |= tmp1 << tmp2;\n\n  if (range_start < XCL_CHAR_LIST_LOW_16_START) break;\n\n  PCRE2_ASSERT(tmp2 >= XCL_TYPE_BIT_LEN);\n  char_list_end = char_list_start - 1;\n  char_list_start = *char_list_next++;\n  tmp1 = 0;\n  tmp2 -= XCL_TYPE_BIT_LEN;\n  }\n\nif (dst[0] < XCL_CHAR_LIST_LOW_16_START) dst += 2;\nPCRE2_ASSERT((uint16_t*)dst <= next_char);\n\ncranges->char_lists_size =\n  (size_t)((uint8_t*)(buffer + total_size) - (uint8_t*)next_char);\ncranges->char_lists_start = (size_t)((uint8_t*)next_char - (uint8_t*)buffer);\ncranges->range_list_size = (uint16_t)(dst - buffer);\nreturn cranges;\n}\n\n#endif /* SUPPORT_WIDE_CHARS */\n\n#ifdef SUPPORT_UNICODE\n\nvoid PRIV(update_classbits)(uint32_t ptype, uint32_t pdata, BOOL negated,\n  uint8_t *classbits)\n{\n/* Update PRIV(xclass) when this function is changed. */\nint c, chartype;\nconst ucd_record *prop;\nuint32_t gentype;\nBOOL set_bit;\n\nif (ptype == PT_ANY)\n  {\n  if (!negated) memset(classbits, 0xff, 32);\n  return;\n  }\n\nfor (c = 0; c < 256; c++)\n  {\n  prop = GET_UCD(c);\n  set_bit = FALSE;\n  (void)set_bit;\n\n  switch (ptype)\n    {\n    case PT_LAMP:\n    chartype = prop->chartype;\n    set_bit = (chartype == ucp_Lu || chartype == ucp_Ll || chartype == ucp_Lt);\n    break;\n\n    case PT_GC:\n    set_bit = (PRIV(ucp_gentype)[prop->chartype] == pdata);\n    break;\n\n    case PT_PC:\n    set_bit = (prop->chartype == pdata);\n    break;\n\n    case PT_SC:\n    set_bit = (prop->script == pdata);\n    break;\n\n    case PT_SCX:\n    set_bit = (prop->script == pdata ||\n      MAPBIT(PRIV(ucd_script_sets) + UCD_SCRIPTX_PROP(prop), pdata) != 0);\n    break;\n\n    case PT_ALNUM:\n    gentype = PRIV(ucp_gentype)[prop->chartype];\n    set_bit = (gentype == ucp_L || gentype == ucp_N);\n    break;\n\n    case PT_SPACE:    /* Perl space */\n    case PT_PXSPACE:  /* POSIX space */\n    switch(c)\n      {\n      HSPACE_BYTE_CASES:\n      VSPACE_BYTE_CASES:\n      set_bit = TRUE;\n      break;\n\n      default:\n      set_bit = (PRIV(ucp_gentype)[prop->chartype] == ucp_Z);\n      break;\n      }\n    break;\n\n    case PT_WORD:\n    chartype = prop->chartype;\n    gentype = PRIV(ucp_gentype)[chartype];\n    set_bit = (gentype == ucp_L || gentype == ucp_N ||\n               chartype == ucp_Mn || chartype == ucp_Pc);\n    break;\n\n    case PT_UCNC:\n    set_bit = (c == CHAR_DOLLAR_SIGN || c == CHAR_COMMERCIAL_AT ||\n               c == CHAR_GRAVE_ACCENT || c >= 0xa0);\n    break;\n\n    case PT_BIDICL:\n    set_bit = (UCD_BIDICLASS_PROP(prop) == pdata);\n    break;\n\n    case PT_BOOL:\n    set_bit = MAPBIT(PRIV(ucd_boolprop_sets) +\n                     UCD_BPROPS_PROP(prop), pdata) != 0;\n    break;\n\n    case PT_PXGRAPH:\n    chartype = prop->chartype;\n    gentype = PRIV(ucp_gentype)[chartype];\n    set_bit = (gentype != ucp_Z && (gentype != ucp_C || chartype == ucp_Cf));\n    break;\n\n    case PT_PXPRINT:\n    chartype = prop->chartype;\n    set_bit = (chartype != ucp_Zl && chartype != ucp_Zp &&\n       (PRIV(ucp_gentype)[chartype] != ucp_C || chartype == ucp_Cf));\n    break;\n\n    case PT_PXPUNCT:\n    gentype = PRIV(ucp_gentype)[prop->chartype];\n    set_bit = (gentype == ucp_P || (c < 128 && gentype == ucp_S));\n    break;\n\n    default:\n    PCRE2_ASSERT(ptype == PT_PXXDIGIT);\n    set_bit = (c >= CHAR_0 && c <= CHAR_9) ||\n              (c >= CHAR_A && c <= CHAR_F) ||\n              (c >= CHAR_a && c <= CHAR_f);\n    break;\n    }\n\n  if (negated) set_bit = !set_bit;\n  if (set_bit) *classbits |= (uint8_t)(1 << (c & 0x7));\n  if ((c & 0x7) == 0x7) classbits++;\n  }\n}\n\n#endif /* SUPPORT_UNICODE */\n\n\n\n#ifdef SUPPORT_WIDE_CHARS\n\n/*************************************************\n*           XClass related properties            *\n*************************************************/\n\n/* XClass needs to be generated. */\n#define XCLASS_REQUIRED 0x1\n/* XClass has 8 bit character. */\n#define XCLASS_HAS_8BIT_CHARS 0x2\n/* XClass has properties. */\n#define XCLASS_HAS_PROPS 0x4\n/* XClass has character lists. */\n#define XCLASS_HAS_CHAR_LISTS 0x8\n/* XClass matches to all >= 256 characters. */\n#define XCLASS_HIGH_ANY 0x10\n\n#endif\n\n\n/*************************************************\n*   Internal entry point for add range to class  *\n*************************************************/\n\n/* This function sets the overall range for characters < 256.\nIt also handles non-utf case folding.\n\nArguments:\n  options       the options bits\n  xoptions      the extra options bits\n  cb            compile data\n  start         start of range character\n  end           end of range character\n\nReturns:        cb->classbits is updated\n*/\n\nstatic void\nadd_to_class(uint32_t options, uint32_t xoptions, compile_block *cb,\n  uint32_t start, uint32_t end)\n{\nuint8_t *classbits = cb->classbits.classbits;\nuint32_t c, byte_start, byte_end;\nuint32_t classbits_end = (end <= 0xff ? end : 0xff);\n\n/* If caseless matching is required, scan the range and process alternate\ncases. In Unicode, there are 8-bit characters that have alternate cases that\nare greater than 255 and vice-versa (though these may be ignored if caseless\nrestriction is in force). Sometimes we can just extend the original range. */\n\nif ((options & PCRE2_CASELESS) != 0)\n  {\n#ifdef SUPPORT_UNICODE\n  /* UTF mode. This branch is taken if we don't support wide characters (e.g.\n  8-bit library, without UTF), but we do treat those characters as Unicode\n  (if UCP flag is set). In this case, we only need to expand the character class\n  set to include the case pairs which are in the 0-255 codepoint range. */\n  if ((options & (PCRE2_UTF|PCRE2_UCP)) != 0)\n    {\n      BOOL turkish_i = (xoptions & (PCRE2_EXTRA_TURKISH_CASING|PCRE2_EXTRA_CASELESS_RESTRICT)) ==\n        PCRE2_EXTRA_TURKISH_CASING;\n      if (start < 128)\n        {\n        uint32_t lo_end = (classbits_end < 127 ? classbits_end : 127);\n        for (c = start; c <= lo_end; c++)\n          {\n          if (turkish_i && UCD_ANY_I(c)) continue;\n          SETBIT(classbits, cb->fcc[c]);\n          }\n        }\n      if (classbits_end >= 128)\n        {\n        uint32_t hi_start = (start > 128 ? start : 128);\n        for (c = hi_start; c <= classbits_end; c++)\n          {\n          uint32_t co = UCD_OTHERCASE(c);\n          if (co <= 0xff) SETBIT(classbits, co);\n          }\n        }\n    }\n\n  else\n#endif  /* SUPPORT_UNICODE */\n\n  /* Not UTF mode */\n    {\n    for (c = start; c <= classbits_end; c++)\n      SETBIT(classbits, cb->fcc[c]);\n    }\n  }\n\n/* Use the bitmap for characters < 256. Otherwise use extra data. */\n\nbyte_start = (start + 7) >> 3;\nbyte_end = (classbits_end + 1) >> 3;\n\nif (byte_start >= byte_end)\n  {\n  for (c = start; c <= classbits_end; c++)\n    /* Regardless of start, c will always be <= 255. */\n    SETBIT(classbits, c);\n  return;\n  }\n\nfor (c = byte_start; c < byte_end; c++)\n  classbits[c] = 0xff;\n\nbyte_start <<= 3;\nbyte_end <<= 3;\n\nfor (c = start; c < byte_start; c++)\n  SETBIT(classbits, c);\n\nfor (c = byte_end; c <= classbits_end; c++)\n  SETBIT(classbits, c);\n}\n\n\n#if PCRE2_CODE_UNIT_WIDTH == 8\n/*************************************************\n*   Internal entry point for add list to class   *\n*************************************************/\n\n/* This function is used for adding a list of horizontal or vertical whitespace\ncharacters to a class. The list must be in order so that ranges of characters\ncan be detected and handled appropriately. This function sets the overall range\nso that the internal functions can try to avoid duplication when handling\ncase-independence.\n\nArguments:\n  options       the options bits\n  xoptions      the extra options bits\n  cb            contains pointers to tables etc.\n  p             points to row of 32-bit values, terminated by NOTACHAR\n\nReturns:        cb->classbits is updated\n*/\n\nstatic void\nadd_list_to_class(uint32_t options, uint32_t xoptions, compile_block *cb,\n  const uint32_t *p)\n{\nwhile (p[0] < 256)\n  {\n  unsigned int n = 0;\n\n  while(p[n+1] == p[0] + n + 1) n++;\n  add_to_class(options, xoptions, cb, p[0], p[n]);\n\n  p += n + 1;\n  }\n}\n\n\n\n/*************************************************\n*    Add characters not in a list to a class     *\n*************************************************/\n\n/* This function is used for adding the complement of a list of horizontal or\nvertical whitespace to a class. The list must be in order.\n\nArguments:\n  options       the options bits\n  xoptions      the extra options bits\n  cb            contains pointers to tables etc.\n  p             points to row of 32-bit values, terminated by NOTACHAR\n\nReturns:        cb->classbits is updated\n*/\n\nstatic void\nadd_not_list_to_class(uint32_t options, uint32_t xoptions, compile_block *cb,\n  const uint32_t *p)\n{\nif (p[0] > 0)\n  add_to_class(options, xoptions, cb, 0, p[0] - 1);\nwhile (p[0] < 256)\n  {\n  while (p[1] == p[0] + 1) p++;\n  add_to_class(options, xoptions, cb, p[0] + 1, (p[1] > 255) ? 255 : p[1] - 1);\n  p++;\n  }\n}\n#endif /* PCRE2_CODE_UNIT_WIDTH == 8 */\n\n\n\n/*************************************************\n*  Main entry-point to compile a character class *\n*************************************************/\n\n/* This function consumes a \"leaf\", which is a set of characters that will\nbecome a single OP_CLASS OP_NCLASS, OP_XCLASS, or OP_ALLANY. */\n\nuint32_t *\nPRIV(compile_class_not_nested)(uint32_t options, uint32_t xoptions,\n  uint32_t *start_ptr, PCRE2_UCHAR **pcode, BOOL negate_class, BOOL* has_bitmap,\n  int *errorcodeptr, compile_block *cb, PCRE2_SIZE *lengthptr)\n{\nuint32_t *pptr = start_ptr;\nPCRE2_UCHAR *code = *pcode;\nBOOL should_flip_negation;\nconst uint8_t *cbits = cb->cbits;\n/* Some functions such as add_to_class() or eclass processing\nexpects that the bitset is stored in cb->classbits.classbits. */\nuint8_t *const classbits = cb->classbits.classbits;\n\n#ifdef SUPPORT_UNICODE\nBOOL utf = (options & PCRE2_UTF) != 0;\n#else  /* No Unicode support */\nBOOL utf = FALSE;\n#endif\n\n/* Helper variables for OP_XCLASS opcode (for characters > 255). */\n\n#ifdef SUPPORT_WIDE_CHARS\nuint32_t xclass_props;\nPCRE2_UCHAR *class_uchardata;\nclass_ranges* cranges;\n#endif\n\n/* If an XClass contains a negative special such as \\S, we need to flip the\nnegation flag at the end, so that support for characters > 255 works correctly\n(they are all included in the class). An XClass may need to insert specific\nmatching or non-matching code for wide characters.\n*/\n\nshould_flip_negation = FALSE;\n\n/* XClass will be used when characters > 255 might match. */\n\n#ifdef SUPPORT_WIDE_CHARS\nxclass_props = 0;\n\n#if PCRE2_CODE_UNIT_WIDTH == 8\ncranges = NULL;\n\nif (utf)\n#endif\n  {\n  if (lengthptr != NULL)\n    {\n    cranges = compile_optimize_class(pptr, options, xoptions, cb);\n\n    if (cranges == NULL)\n      {\n      *errorcodeptr = ERR21;\n      return NULL;\n      }\n\n    /* Caching the pre-processed character ranges. */\n    if (cb->next_cranges != NULL)\n      cb->next_cranges->next = cranges;\n    else\n      cb->cranges = cranges;\n\n    cb->next_cranges = cranges;\n    }\n  else\n    {\n    /* Reuse the pre-processed character ranges. */\n    cranges = cb->cranges;\n    PCRE2_ASSERT(cranges != NULL);\n    cb->cranges = cranges->next;\n    }\n\n  if (cranges->range_list_size > 0)\n    {\n    const uint32_t *ranges = (const uint32_t*)(cranges + 1);\n\n    if (ranges[0] <= 255)\n      xclass_props |= XCLASS_HAS_8BIT_CHARS;\n\n    if (ranges[cranges->range_list_size - 1] == GET_MAX_CHAR_VALUE(utf) &&\n        ranges[cranges->range_list_size - 2] <= 256)\n      xclass_props |= XCLASS_HIGH_ANY;\n    }\n  }\n\nclass_uchardata = code + LINK_SIZE + 2;   /* For XCLASS items */\n#endif /* SUPPORT_WIDE_CHARS */\n\n/* Initialize the 256-bit (32-byte) bit map to all zeros. We build the map\nin a temporary bit of memory, in case the class contains fewer than two\n8-bit characters because in that case the compiled code doesn't use the bit\nmap. */\n\nmemset(classbits, 0, 32);\n\n/* Process items until end_ptr is reached. */\n\nwhile (TRUE)\n  {\n  uint32_t meta = *(pptr++);\n  BOOL local_negate;\n  int posix_class;\n  int taboffset, tabopt;\n  class_bits_storage pbits;\n  uint32_t escape, c;\n\n  /* Handle POSIX classes such as [:alpha:] etc. */\n  switch (META_CODE(meta))\n    {\n    case META_POSIX:\n    case META_POSIX_NEG:\n\n    local_negate = (meta == META_POSIX_NEG);\n    posix_class = *(pptr++);\n\n    if (local_negate) should_flip_negation = TRUE;  /* Note negative special */\n\n    /* If matching is caseless, upper and lower are converted to alpha.\n    This relies on the fact that the class table starts with alpha,\n    lower, upper as the first 3 entries. */\n\n    if ((options & PCRE2_CASELESS) != 0 && posix_class <= 2)\n      posix_class = 0;\n\n    /* When PCRE2_UCP is set, some of the POSIX classes are converted to\n    different escape sequences that use Unicode properties \\p or \\P.\n    Others that are not available via \\p or \\P have to generate\n    XCL_PROP/XCL_NOTPROP directly, which is done here. */\n\n#ifdef SUPPORT_UNICODE\n    /* TODO This entire block of code here appears to be unreachable!? I simply\n    can't see how it can be hit, given that the frontend parser doesn't emit\n    META_POSIX for GRAPH/PRINT/PUNCT when UCP is set. */\n    if ((options & PCRE2_UCP) != 0 &&\n        (xoptions & PCRE2_EXTRA_ASCII_POSIX) == 0)\n      {\n      uint32_t ptype;\n\n      switch(posix_class)\n        {\n        case PC_GRAPH:\n        case PC_PRINT:\n        case PC_PUNCT:\n        ptype = (posix_class == PC_GRAPH)? PT_PXGRAPH :\n                (posix_class == PC_PRINT)? PT_PXPRINT : PT_PXPUNCT;\n\n        PRIV(update_classbits)(ptype, 0, local_negate, classbits);\n\n        if ((xclass_props & XCLASS_HIGH_ANY) == 0)\n          {\n          if (lengthptr != NULL)\n            *lengthptr += 3;\n          else\n            {\n            *class_uchardata++ = local_negate? XCL_NOTPROP : XCL_PROP;\n            *class_uchardata++ = (PCRE2_UCHAR)ptype;\n            *class_uchardata++ = 0;\n            }\n          xclass_props |= XCLASS_REQUIRED | XCLASS_HAS_PROPS;\n          }\n        continue;\n\n        /* For the other POSIX classes (ex: ascii) we are going to\n        fall through to the non-UCP case and build a bit map for\n        characters with code points less than 256. However, if we are in\n        a negated POSIX class, characters with code points greater than\n        255 must either all match or all not match, depending on whether\n        the whole class is not or is negated. For example, for\n        [[:^ascii:]... they must all match, whereas for [^[:^ascii:]...\n        they must not.\n\n        In the special case where there are no xclass items, this is\n        automatically handled by the use of OP_CLASS or OP_NCLASS, but an\n        explicit range is needed for OP_XCLASS. Setting a flag here\n        causes the range to be generated later when it is known that\n        OP_XCLASS is required. In the 8-bit library this is relevant only in\n        utf mode, since no wide characters can exist otherwise. */\n\n        default:\n        break;\n        }\n      }\n#endif  /* SUPPORT_UNICODE */\n\n    /* In the non-UCP case, or when UCP makes no difference, we build the\n    bit map for the POSIX class in a chunk of local store because we may\n    be adding and subtracting from it, and we don't want to subtract bits\n    that may be in the main map already. At the end we or the result into\n    the bit map that is being built. */\n\n    posix_class *= 3;\n\n    /* Copy in the first table (always present) */\n\n    memcpy(pbits.classbits, cbits + PRIV(posix_class_maps)[posix_class], 32);\n\n    /* If there is a second table, add or remove it as required. */\n\n    taboffset = PRIV(posix_class_maps)[posix_class + 1];\n    tabopt = PRIV(posix_class_maps)[posix_class + 2];\n\n    if (taboffset >= 0)\n      {\n      if (tabopt >= 0)\n        for (int i = 0; i < 32; i++)\n          pbits.classbits[i] |= cbits[i + taboffset];\n      else\n        for (int i = 0; i < 32; i++)\n          pbits.classbits[i] &= (uint8_t)(~cbits[i + taboffset]);\n      }\n\n    /* Now see if we need to remove any special characters. An option\n    value of 1 removes vertical space and 2 removes underscore. */\n\n    if (tabopt < 0) tabopt = -tabopt;\n    if (tabopt == 1) pbits.classbits[1] &= ~0x3c;\n      else if (tabopt == 2) pbits.classbits[11] &= 0x7f;\n\n    /* Add the POSIX table or its complement into the main table that is\n    being built and we are done. */\n\n      {\n      uint32_t *classwords = cb->classbits.classwords;\n\n      if (local_negate)\n        for (int i = 0; i < 8; i++)\n          classwords[i] |= (uint32_t)(~pbits.classwords[i]);\n      else\n        for (int i = 0; i < 8; i++)\n          classwords[i] |= pbits.classwords[i];\n      }\n\n#ifdef SUPPORT_WIDE_CHARS\n    /* Every class contains at least one < 256 character. */\n    xclass_props |= XCLASS_HAS_8BIT_CHARS;\n#endif\n    continue;               /* End of POSIX handling */\n\n    /* Other than POSIX classes, the only items we should encounter are\n    \\d-type escapes and literal characters (possibly as ranges). */\n    case META_BIGVALUE:\n    meta = *(pptr++);\n    break;\n\n    case META_ESCAPE:\n    escape = META_DATA(meta);\n\n    switch(escape)\n      {\n      case ESC_d:\n      for (int i = 0; i < 32; i++) classbits[i] |= cbits[i+cbit_digit];\n      break;\n\n      case ESC_D:\n      should_flip_negation = TRUE;\n      for (int i = 0; i < 32; i++)\n        classbits[i] |= (uint8_t)(~cbits[i+cbit_digit]);\n      break;\n\n      case ESC_w:\n      for (int i = 0; i < 32; i++) classbits[i] |= cbits[i+cbit_word];\n      break;\n\n      case ESC_W:\n      should_flip_negation = TRUE;\n      for (int i = 0; i < 32; i++)\n        classbits[i] |= (uint8_t)(~cbits[i+cbit_word]);\n      break;\n\n      /* Perl 5.004 onwards omitted VT from \\s, but restored it at Perl\n      5.18. Before PCRE 8.34, we had to preserve the VT bit if it was\n      previously set by something earlier in the character class.\n      Luckily, the value of CHAR_VT is 0x0b in both ASCII and EBCDIC, so\n      we could just adjust the appropriate bit. From PCRE 8.34 we no\n      longer treat \\s and \\S specially. */\n\n      case ESC_s:\n      for (int i = 0; i < 32; i++) classbits[i] |= cbits[i+cbit_space];\n      break;\n\n      case ESC_S:\n      should_flip_negation = TRUE;\n      for (int i = 0; i < 32; i++)\n        classbits[i] |= (uint8_t)(~cbits[i+cbit_space]);\n      break;\n\n      /* When adding the horizontal or vertical space lists to a class, or\n      their complements, disable PCRE2_CASELESS, because it justs wastes\n      time, and in the \"not-x\" UTF cases can create unwanted duplicates in\n      the XCLASS list (provoked by characters that have more than one other\n      case and by both cases being in the same \"not-x\" sublist). */\n\n      case ESC_h:\n#if PCRE2_CODE_UNIT_WIDTH == 8\n#ifdef SUPPORT_UNICODE\n      if (cranges != NULL) break;\n#endif\n      add_list_to_class(options & ~PCRE2_CASELESS, xoptions,\n        cb, PRIV(hspace_list));\n#else\n      PCRE2_ASSERT(cranges != NULL);\n#endif\n      break;\n\n      case ESC_H:\n#if PCRE2_CODE_UNIT_WIDTH == 8\n#ifdef SUPPORT_UNICODE\n      if (cranges != NULL) break;\n#endif\n      add_not_list_to_class(options & ~PCRE2_CASELESS, xoptions,\n        cb, PRIV(hspace_list));\n#else\n      PCRE2_ASSERT(cranges != NULL);\n#endif\n      break;\n\n      case ESC_v:\n#if PCRE2_CODE_UNIT_WIDTH == 8\n#ifdef SUPPORT_UNICODE\n      if (cranges != NULL) break;\n#endif\n      add_list_to_class(options & ~PCRE2_CASELESS, xoptions,\n        cb, PRIV(vspace_list));\n#else\n      PCRE2_ASSERT(cranges != NULL);\n#endif\n      break;\n\n      case ESC_V:\n#if PCRE2_CODE_UNIT_WIDTH == 8\n#ifdef SUPPORT_UNICODE\n      if (cranges != NULL) break;\n#endif\n      add_not_list_to_class(options & ~PCRE2_CASELESS, xoptions,\n        cb, PRIV(vspace_list));\n#else\n      PCRE2_ASSERT(cranges != NULL);\n#endif\n      break;\n\n      /* If Unicode is not supported, \\P and \\p are not allowed and are\n      faulted at parse time, so will never appear here. */\n\n#ifdef SUPPORT_UNICODE\n      case ESC_p:\n      case ESC_P:\n        {\n        uint32_t ptype = *pptr >> 16;\n        uint32_t pdata = *(pptr++) & 0xffff;\n\n        /* The \"Any\" is processed by PRIV(update_classbits)(). */\n        if (ptype == PT_ANY)\n          {\n#if PCRE2_CODE_UNIT_WIDTH == 8\n          if (!utf && escape == ESC_p) memset(classbits, 0xff, 32);\n#endif\n          continue;\n          }\n\n        PRIV(update_classbits)(ptype, pdata, (escape == ESC_P), classbits);\n\n        if ((xclass_props & XCLASS_HIGH_ANY) == 0)\n          {\n          if (lengthptr != NULL)\n            *lengthptr += 3;\n          else\n            {\n            *class_uchardata++ = (escape == ESC_p)? XCL_PROP : XCL_NOTPROP;\n            *class_uchardata++ = ptype;\n            *class_uchardata++ = pdata;\n            }\n          xclass_props |= XCLASS_REQUIRED | XCLASS_HAS_PROPS;\n          }\n        }\n      continue;\n#endif\n      }\n\n#ifdef SUPPORT_WIDE_CHARS\n    /* Every non-property class contains at least one < 256 character. */\n    xclass_props |= XCLASS_HAS_8BIT_CHARS;\n#endif\n    /* End handling \\d-type escapes */\n    continue;\n\n    CLASS_END_CASES(meta)\n    /* Literals. */\n    if (meta < META_END) break;\n    /* Non-literals: end of class contents. */\n    goto END_PROCESSING;\n    }\n\n  /* A literal character may be followed by a range meta. At parse time\n  there are checks for out-of-order characters, for ranges where the two\n  characters are equal, and for hyphens that cannot indicate a range. At\n  this point, therefore, no checking is needed. */\n\n  c = meta;\n\n  /* Remember if \\r or \\n were explicitly used */\n\n  if (c == CHAR_CR || c == CHAR_NL) cb->external_flags |= PCRE2_HASCRORLF;\n\n  /* Process a character range */\n\n  if (*pptr == META_RANGE_LITERAL || *pptr == META_RANGE_ESCAPED)\n    {\n    uint32_t d;\n\n#ifdef EBCDIC\n    BOOL range_is_literal = (*pptr == META_RANGE_LITERAL);\n#endif\n    ++pptr;\n    d = *(pptr++);\n    if (d == META_BIGVALUE) d = *(pptr++);\n\n    /* Remember an explicit \\r or \\n, and add the range to the class. */\n\n    if (d == CHAR_CR || d == CHAR_NL) cb->external_flags |= PCRE2_HASCRORLF;\n\n#if PCRE2_CODE_UNIT_WIDTH == 8\n#ifdef SUPPORT_UNICODE\n    if (cranges != NULL) continue;\n    xclass_props |= XCLASS_HAS_8BIT_CHARS;\n#endif\n\n    /* In an EBCDIC environment, Perl treats alphabetic ranges specially\n    because there are holes in the encoding, and simply using the range\n    A-Z (for example) would include the characters in the holes. This\n    applies only to literal ranges; [\\xC1-\\xE9] is different to [A-Z]. */\n\n#ifdef EBCDIC\n    if (range_is_literal &&\n         (cb->ctypes[c] & ctype_letter) != 0 &&\n         (cb->ctypes[d] & ctype_letter) != 0 &&\n         (c <= CHAR_z) == (d <= CHAR_z))\n      {\n      uint32_t uc = (d <= CHAR_z)? 0 : 64;\n      uint32_t C = c - uc;\n      uint32_t D = d - uc;\n\n      if (C <= CHAR_i)\n        {\n        add_to_class(options, xoptions, cb, C + uc,\n          ((D < CHAR_i)? D : CHAR_i) + uc);\n        C = CHAR_j;\n        }\n\n      if (C <= D && C <= CHAR_r)\n        {\n        add_to_class(options, xoptions, cb, C + uc,\n          ((D < CHAR_r)? D : CHAR_r) + uc);\n        C = CHAR_s;\n        }\n\n      if (C <= D)\n        add_to_class(options, xoptions, cb, C + uc, D + uc);\n      }\n    else\n#endif\n    /* Not an EBCDIC special range */\n\n    add_to_class(options, xoptions, cb, c, d);\n#else\n    PCRE2_ASSERT(cranges != NULL);\n#endif\n    continue;\n    }  /* End of range handling */\n\n  /* Character ranges are ignored when class_ranges is present. */\n#if PCRE2_CODE_UNIT_WIDTH == 8\n#ifdef SUPPORT_UNICODE\n  if (cranges != NULL) continue;\n  xclass_props |= XCLASS_HAS_8BIT_CHARS;\n#endif\n  /* Handle a single character. */\n\n  add_to_class(options, xoptions, cb, meta, meta);\n#else\n  PCRE2_ASSERT(cranges != NULL);\n#endif\n  }   /* End of main class-processing loop */\n\nEND_PROCESSING:\n\n#ifdef SUPPORT_WIDE_CHARS\nPCRE2_ASSERT((xclass_props & XCLASS_HAS_PROPS) == 0 ||\n             (xclass_props & XCLASS_HIGH_ANY) == 0);\n\nif (cranges != NULL)\n  {\n  uint32_t *range = (uint32_t*)(cranges + 1);\n  uint32_t *end = range + cranges->range_list_size;\n\n  while (range < end && range[0] < 256)\n    {\n    PCRE2_ASSERT((xclass_props & XCLASS_HAS_8BIT_CHARS) != 0);\n    /* Add range to bitset. If we are in UTF or UCP mode, then clear the\n    caseless bit, because the cranges handle caselessness (only) in this\n    condition; see the condition for PARSE_CLASS_CASELESS_UTF in\n    compile_optimize_class(). */\n    add_to_class(((options & (PCRE2_UTF|PCRE2_UCP)) != 0)?\n        (options & ~PCRE2_CASELESS) : options, xoptions, cb, range[0], range[1]);\n\n    if (range[1] > 255) break;\n    range += 2;\n    }\n\n  if (cranges->char_lists_size > 0)\n    {\n    /* The cranges structure is still used and freed later. */\n    PCRE2_ASSERT((xclass_props & XCLASS_HIGH_ANY) == 0);\n    xclass_props |= XCLASS_REQUIRED | XCLASS_HAS_CHAR_LISTS;\n    }\n  else\n    {\n    if ((xclass_props & XCLASS_HIGH_ANY) != 0)\n      {\n      PCRE2_ASSERT(range + 2 == end && range[0] <= 256 &&\n        range[1] >= GET_MAX_CHAR_VALUE(utf));\n      should_flip_negation = TRUE;\n      range = end;\n      }\n\n    while (range < end)\n      {\n      uint32_t range_start = range[0];\n      uint32_t range_end = range[1];\n\n      range += 2;\n      xclass_props |= XCLASS_REQUIRED;\n\n      if (range_start < 256) range_start = 256;\n\n      if (lengthptr != NULL)\n        {\n#ifdef SUPPORT_UNICODE\n        if (utf)\n          {\n          *lengthptr += 1;\n\n          if (range_start < range_end)\n            *lengthptr += PRIV(ord2utf)(range_start, class_uchardata);\n\n          *lengthptr += PRIV(ord2utf)(range_end, class_uchardata);\n          continue;\n          }\n#endif  /* SUPPORT_UNICODE */\n\n        *lengthptr += range_start < range_end ? 3 : 2;\n        continue;\n        }\n\n#ifdef SUPPORT_UNICODE\n      if (utf)\n        {\n        if (range_start < range_end)\n          {\n          *class_uchardata++ = XCL_RANGE;\n          class_uchardata += PRIV(ord2utf)(range_start, class_uchardata);\n          }\n        else\n          *class_uchardata++ = XCL_SINGLE;\n\n        class_uchardata += PRIV(ord2utf)(range_end, class_uchardata);\n        continue;\n        }\n#endif  /* SUPPORT_UNICODE */\n\n      /* Without UTF support, character values are constrained\n      by the bit length, and can only be > 256 for 16-bit and\n      32-bit libraries. */\n#if PCRE2_CODE_UNIT_WIDTH != 8\n      if (range_start < range_end)\n        {\n        *class_uchardata++ = XCL_RANGE;\n        *class_uchardata++ = range_start;\n        }\n      else\n        *class_uchardata++ = XCL_SINGLE;\n\n      *class_uchardata++ = range_end;\n#endif  /* PCRE2_CODE_UNIT_WIDTH == 8 */\n      }\n\n    if (lengthptr == NULL)\n      cb->cx->memctl.free(cranges, cb->cx->memctl.memory_data);\n    }\n  }\n#endif /* SUPPORT_WIDE_CHARS */\n\n/* If there are characters with values > 255, or Unicode property settings\n(\\p or \\P), we have to compile an extended class, with its own opcode,\nunless there were no property settings and there was a negated special such\nas \\S in the class, and PCRE2_UCP is not set, because in that case all\ncharacters > 255 are in or not in the class, so any that were explicitly\ngiven as well can be ignored.\n\nIn the UCP case, if certain negated POSIX classes (ex: [:^ascii:]) were\nwere present in a class, we either have to match or not match all wide\ncharacters (depending on whether the whole class is or is not negated).\nThis requirement is indicated by match_all_or_no_wide_chars being true.\nWe do this by including an explicit range, which works in both cases.\nThis applies only in UTF and 16-bit and 32-bit non-UTF modes, since there\ncannot be any wide characters in 8-bit non-UTF mode.\n\nWhen there *are* properties in a positive UTF-8 or any 16-bit or 32_bit\nclass where \\S etc is present without PCRE2_UCP, causing an extended class\nto be compiled, we make sure that all characters > 255 are included by\nforcing match_all_or_no_wide_chars to be true.\n\nIf, when generating an xclass, there are no characters < 256, we can omit\nthe bitmap in the actual compiled code. */\n\n#ifdef SUPPORT_WIDE_CHARS  /* Defined for 16/32 bits, or 8-bit with Unicode */\nif ((xclass_props & XCLASS_REQUIRED) != 0)\n  {\n  PCRE2_UCHAR *previous = code;\n\n  if ((xclass_props & XCLASS_HAS_CHAR_LISTS) == 0)\n    *class_uchardata++ = XCL_END;    /* Marks the end of extra data */\n  *code++ = OP_XCLASS;\n  code += LINK_SIZE;\n  *code = negate_class? XCL_NOT:0;\n  if ((xclass_props & XCLASS_HAS_PROPS) != 0) *code |= XCL_HASPROP;\n\n  /* If the map is required, move up the extra data to make room for it;\n  otherwise just move the code pointer to the end of the extra data. */\n\n  if ((xclass_props & XCLASS_HAS_8BIT_CHARS) != 0 || has_bitmap != NULL)\n    {\n    if (negate_class)\n      {\n      uint32_t *classwords = cb->classbits.classwords;\n      for (int i = 0; i < 8; i++) classwords[i] = ~classwords[i];\n      }\n\n    if (has_bitmap == NULL)\n      {\n      *code++ |= XCL_MAP;\n      (void)memmove(code + (32 / sizeof(PCRE2_UCHAR)), code,\n        CU2BYTES(class_uchardata - code));\n      memcpy(code, classbits, 32);\n      code = class_uchardata + (32 / sizeof(PCRE2_UCHAR));\n      }\n    else\n      {\n      code = class_uchardata;\n      if ((xclass_props & XCLASS_HAS_8BIT_CHARS) != 0)\n        *has_bitmap = TRUE;\n      }\n    }\n  else code = class_uchardata;\n\n  if ((xclass_props & XCLASS_HAS_CHAR_LISTS) != 0)\n    {\n    /* Char lists size is an even number, because all items are 16 or 32\n    bit values. The character list data is always aligned to 32 bits. */\n    size_t char_lists_size = cranges->char_lists_size;\n    PCRE2_ASSERT((char_lists_size & 0x1) == 0 &&\n                 (cb->char_lists_size & 0x3) == 0);\n\n    if (lengthptr != NULL)\n      {\n      char_lists_size = CLIST_ALIGN_TO(char_lists_size, sizeof(uint32_t));\n\n#if PCRE2_CODE_UNIT_WIDTH == 8\n      *lengthptr += 2 + LINK_SIZE;\n#else\n      *lengthptr += 1 + LINK_SIZE;\n#endif\n\n      cb->char_lists_size += char_lists_size;\n\n      char_lists_size /= sizeof(PCRE2_UCHAR);\n\n      /* Storage space for character lists is included\n      in the maximum pattern size. */\n      if (*lengthptr > MAX_PATTERN_SIZE ||\n          MAX_PATTERN_SIZE - *lengthptr < char_lists_size)\n        {\n        *errorcodeptr = ERR20;   /* Pattern is too large */\n        return NULL;\n        }\n      }\n    else\n      {\n      uint8_t *data;\n\n      PCRE2_ASSERT(cranges->char_lists_types <= XCL_TYPE_MASK);\n#if PCRE2_CODE_UNIT_WIDTH == 8\n      /* Encode as high / low bytes. */\n      code[0] = (uint8_t)(XCL_LIST |\n        (cranges->char_lists_types >> 8));\n      code[1] = (uint8_t)cranges->char_lists_types;\n      code += 2;\n#else\n      *code++ = (PCRE2_UCHAR)(XCL_LIST | cranges->char_lists_types);\n#endif\n\n      /* Character lists are stored in backwards direction from\n      byte code start. The non-dfa/dfa matchers can access these\n      lists using the byte code start stored in match blocks.\n      Each list is aligned to 32 bit with an optional unused\n      16 bit value at the beginning of the character list. */\n\n      cb->char_lists_size += char_lists_size;\n      data = (uint8_t*)cb->start_code - cb->char_lists_size;\n\n      memcpy(data, (uint8_t*)(cranges + 1) + cranges->char_lists_start,\n        char_lists_size);\n\n      /* Since character lists total size is less than MAX_PATTERN_SIZE,\n      their starting offset fits into a value which size is LINK_SIZE. */\n\n      char_lists_size = cb->char_lists_size;\n      PUT(code, 0, (uint32_t)(char_lists_size >> 1));\n      code += LINK_SIZE;\n\n#if defined PCRE2_DEBUG || defined SUPPORT_VALGRIND\n      if ((char_lists_size & 0x2) != 0)\n        {\n        /* In debug the unused 16 bit value is set\n        to a fixed value and marked unused. */\n        ((uint16_t*)data)[-1] = 0x5555;\n#ifdef SUPPORT_VALGRIND\n        VALGRIND_MAKE_MEM_NOACCESS(data - 2, 2);\n#endif\n        }\n#endif\n\n      cb->char_lists_size =\n        CLIST_ALIGN_TO(char_lists_size, sizeof(uint32_t));\n\n      cb->cx->memctl.free(cranges, cb->cx->memctl.memory_data);\n      }\n    }\n\n  /* Now fill in the complete length of the item */\n\n  PUT(previous, 1, (int)(code - previous));\n  goto DONE;   /* End of class handling */\n  }\n#endif  /* SUPPORT_WIDE_CHARS */\n\n/* If there are no characters > 255, or they are all to be included or\nexcluded, set the opcode to OP_CLASS or OP_NCLASS, depending on whether the\nwhole class was negated and whether there were negative specials such as \\S\n(non-UCP) in the class. Then copy the 32-byte map into the code vector,\nnegating it if necessary. */\n\nif (negate_class)\n  {\n  uint32_t *classwords = cb->classbits.classwords;\n\n  for (int i = 0; i < 8; i++) classwords[i] = ~classwords[i];\n  }\n\nif ((SELECT_VALUE8(!utf, 0) || negate_class != should_flip_negation) &&\n    cb->classbits.classwords[0] == ~(uint32_t)0)\n  {\n  const uint32_t *classwords = cb->classbits.classwords;\n  int i;\n\n  for (i = 0; i < 8; i++)\n    if (classwords[i] != ~(uint32_t)0) break;\n\n  if (i == 8)\n    {\n    *code++ = OP_ALLANY;\n    goto DONE;   /* End of class handling */\n    }\n  }\n\n*code++ = (negate_class == should_flip_negation) ? OP_CLASS : OP_NCLASS;\nmemcpy(code, classbits, 32);\ncode += 32 / sizeof(PCRE2_UCHAR);\n\nDONE:\n*pcode = code;\nreturn pptr - 1;\n}\n\n\n\n/* ===================================================================*/\n/* Here follows a block of ECLASS-compiling functions. You may well want to\nread them from top to bottom; they are ordered from leafmost (at the top) to\noutermost parser (at the bottom of the file). */\n\n/* This function folds one operand using the negation operator.\nThe new, combined chunk of stack code is written out to *pop_info. */\n\nstatic void\nfold_negation(eclass_op_info *pop_info, PCRE2_SIZE *lengthptr,\n  BOOL preserve_classbits)\n{\n/* If the chunk of stack code is already composed of multiple ops, we won't\ndescend in and try and propagate the negation down the tree. (That would lead\nto O(n^2) compile-time, which could be exploitable with a malicious regex -\nalthough maybe that's not really too much of a worry in a library that offers\nan exponential-time matching function!) */\n\nif (pop_info->op_single_type == 0)\n  {\n  if (lengthptr != NULL)\n    *lengthptr += 1;\n  else\n    pop_info->code_start[pop_info->length] = ECL_NOT;\n  pop_info->length += 1;\n  }\n\n/* Otherwise, it's a nice single-op item, so we can easily fold in the negation\nwithout needing to produce an ECL_NOT. */\n\nelse if (pop_info->op_single_type == ECL_ANY ||\n         pop_info->op_single_type == ECL_NONE)\n  {\n  pop_info->op_single_type = (pop_info->op_single_type == ECL_NONE)?\n      ECL_ANY : ECL_NONE;\n  if (lengthptr == NULL)\n    *(pop_info->code_start) = pop_info->op_single_type;\n  }\nelse\n  {\n  PCRE2_ASSERT(pop_info->op_single_type == ECL_XCLASS &&\n               pop_info->length >= 1 + LINK_SIZE + 1);\n  if (lengthptr == NULL)\n    pop_info->code_start[1 + LINK_SIZE] ^= XCL_NOT;\n  }\n\nif (!preserve_classbits)\n  {\n  for (int i = 0; i < 8; i++)\n    pop_info->bits.classwords[i] = ~pop_info->bits.classwords[i];\n  }\n}\n\n\n\n/* This function folds together two operands using a binary operator.\nThe new, combined chunk of stack code is written out to *lhs_op_info. */\n\nstatic void\nfold_binary(int op, eclass_op_info *lhs_op_info, eclass_op_info *rhs_op_info,\n  PCRE2_SIZE *lengthptr)\n{\nswitch (op)\n  {\n  /* ECL_AND truth table:\n\n     LHS  RHS  RESULT\n     ----------------\n     ANY  *    RHS\n     *    ANY  LHS\n     NONE *    NONE\n     *    NONE NONE\n     X    Y    X & Y\n  */\n\n  case ECL_AND:\n  if (rhs_op_info->op_single_type == ECL_ANY)\n    {\n    /* no-op: drop the RHS */\n    }\n  else if (lhs_op_info->op_single_type == ECL_ANY)\n    {\n    /* no-op: drop the LHS, and memmove the RHS into its place */\n    if (lengthptr == NULL)\n      memmove(lhs_op_info->code_start, rhs_op_info->code_start,\n              CU2BYTES(rhs_op_info->length));\n    lhs_op_info->length = rhs_op_info->length;\n    lhs_op_info->op_single_type = rhs_op_info->op_single_type;\n    }\n  else if (rhs_op_info->op_single_type == ECL_NONE)\n    {\n    /* the result is ECL_NONE: write into the LHS */\n    if (lengthptr == NULL)\n      lhs_op_info->code_start[0] = ECL_NONE;\n    lhs_op_info->length = 1;\n    lhs_op_info->op_single_type = ECL_NONE;\n    }\n  else if (lhs_op_info->op_single_type == ECL_NONE)\n    {\n    /* the result is ECL_NONE: drop the RHS */\n    }\n  else\n    {\n    /* Both of LHS & RHS are either ECL_XCLASS, or compound operations. */\n    if (lengthptr != NULL)\n      *lengthptr += 1;\n    else\n      {\n      PCRE2_ASSERT(rhs_op_info->code_start ==\n          lhs_op_info->code_start + lhs_op_info->length);\n      rhs_op_info->code_start[rhs_op_info->length] = ECL_AND;\n      }\n    lhs_op_info->length += rhs_op_info->length + 1;\n    lhs_op_info->op_single_type = 0;\n    }\n\n  for (int i = 0; i < 8; i++)\n    lhs_op_info->bits.classwords[i] &= rhs_op_info->bits.classwords[i];\n  break;\n\n  /* ECL_OR truth table:\n\n     LHS  RHS  RESULT\n     ----------------\n     ANY  *    ANY\n     *    ANY  ANY\n     NONE *    RHS\n     *    NONE LHS\n     X    Y    X | Y\n  */\n\n  case ECL_OR:\n  if (rhs_op_info->op_single_type == ECL_NONE)\n    {\n    /* no-op: drop the RHS */\n    }\n  else if (lhs_op_info->op_single_type == ECL_NONE)\n    {\n    /* no-op: drop the LHS, and memmove the RHS into its place */\n    if (lengthptr == NULL)\n      memmove(lhs_op_info->code_start, rhs_op_info->code_start,\n              CU2BYTES(rhs_op_info->length));\n    lhs_op_info->length = rhs_op_info->length;\n    lhs_op_info->op_single_type = rhs_op_info->op_single_type;\n    }\n  else if (rhs_op_info->op_single_type == ECL_ANY)\n    {\n    /* the result is ECL_ANY: write into the LHS */\n    if (lengthptr == NULL)\n      lhs_op_info->code_start[0] = ECL_ANY;\n    lhs_op_info->length = 1;\n    lhs_op_info->op_single_type = ECL_ANY;\n    }\n  else if (lhs_op_info->op_single_type == ECL_ANY)\n    {\n    /* the result is ECL_ANY: drop the RHS */\n    }\n  else\n    {\n    /* Both of LHS & RHS are either ECL_XCLASS, or compound operations. */\n    if (lengthptr != NULL)\n      *lengthptr += 1;\n    else\n      {\n      PCRE2_ASSERT(rhs_op_info->code_start ==\n          lhs_op_info->code_start + lhs_op_info->length);\n      rhs_op_info->code_start[rhs_op_info->length] = ECL_OR;\n      }\n    lhs_op_info->length += rhs_op_info->length + 1;\n    lhs_op_info->op_single_type = 0;\n    }\n\n  for (int i = 0; i < 8; i++)\n    lhs_op_info->bits.classwords[i] |= rhs_op_info->bits.classwords[i];\n  break;\n\n  /* ECL_XOR truth table:\n\n     LHS  RHS  RESULT\n     ----------------\n     ANY  *    !RHS\n     *    ANY  !LHS\n     NONE *    RHS\n     *    NONE LHS\n     X    Y    X ^ Y\n  */\n\n  case ECL_XOR:\n  if (rhs_op_info->op_single_type == ECL_NONE)\n    {\n    /* no-op: drop the RHS */\n    }\n  else if (lhs_op_info->op_single_type == ECL_NONE)\n    {\n    /* no-op: drop the LHS, and memmove the RHS into its place */\n    if (lengthptr == NULL)\n      memmove(lhs_op_info->code_start, rhs_op_info->code_start,\n              CU2BYTES(rhs_op_info->length));\n    lhs_op_info->length = rhs_op_info->length;\n    lhs_op_info->op_single_type = rhs_op_info->op_single_type;\n    }\n  else if (rhs_op_info->op_single_type == ECL_ANY)\n    {\n    /* the result is !LHS: fold in the negation, and drop the RHS */\n    /* Preserve the classbits, because we promise to deal with them later. */\n    fold_negation(lhs_op_info, lengthptr, TRUE);\n    }\n  else if (lhs_op_info->op_single_type == ECL_ANY)\n    {\n    /* the result is !RHS: drop the LHS, memmove the RHS into its place, and\n    fold in the negation */\n    if (lengthptr == NULL)\n      memmove(lhs_op_info->code_start, rhs_op_info->code_start,\n              CU2BYTES(rhs_op_info->length));\n    lhs_op_info->length = rhs_op_info->length;\n    lhs_op_info->op_single_type = rhs_op_info->op_single_type;\n\n    /* Preserve the classbits, because we promise to deal with them later. */\n    fold_negation(lhs_op_info, lengthptr, TRUE);\n    }\n  else\n    {\n    /* Both of LHS & RHS are either ECL_XCLASS, or compound operations. */\n    if (lengthptr != NULL)\n      *lengthptr += 1;\n    else\n      {\n      PCRE2_ASSERT(rhs_op_info->code_start ==\n          lhs_op_info->code_start + lhs_op_info->length);\n      rhs_op_info->code_start[rhs_op_info->length] = ECL_XOR;\n      }\n    lhs_op_info->length += rhs_op_info->length + 1;\n    lhs_op_info->op_single_type = 0;\n    }\n\n  for (int i = 0; i < 8; i++)\n    lhs_op_info->bits.classwords[i] ^= rhs_op_info->bits.classwords[i];\n  break;\n\n  default:\n  PCRE2_DEBUG_UNREACHABLE();\n  break;\n  }\n}\n\n\n\nstatic BOOL\ncompile_eclass_nested(eclass_context *context, BOOL negated,\n  uint32_t **pptr, PCRE2_UCHAR **pcode,\n  eclass_op_info *pop_info, PCRE2_SIZE *lengthptr);\n\n/* This function consumes a group of implicitly-unioned class elements.\nThese can be characters, ranges, properties, or nested classes, as long\nas they are all joined by being placed adjacently. */\n\nstatic BOOL\ncompile_class_operand(eclass_context *context, BOOL negated,\n  uint32_t **pptr, PCRE2_UCHAR **pcode, eclass_op_info *pop_info,\n  PCRE2_SIZE *lengthptr)\n{\nuint32_t *ptr = *pptr;\nuint32_t *prev_ptr;\nPCRE2_UCHAR *code = *pcode;\nPCRE2_UCHAR *code_start = code;\nPCRE2_SIZE prev_length = (lengthptr != NULL)? *lengthptr : 0;\nPCRE2_SIZE extra_length;\nuint32_t meta = META_CODE(*ptr);\n\nswitch (meta)\n  {\n  case META_CLASS_EMPTY_NOT:\n  case META_CLASS_EMPTY:\n  ++ptr;\n  pop_info->length = 1;\n  if ((meta == META_CLASS_EMPTY) == negated)\n    {\n    *code++ = pop_info->op_single_type = ECL_ANY;\n    memset(pop_info->bits.classbits, 0xff, 32);\n    }\n  else\n    {\n    *code++ = pop_info->op_single_type = ECL_NONE;\n    memset(pop_info->bits.classbits, 0, 32);\n    }\n  break;\n\n  case META_CLASS:\n  case META_CLASS_NOT:\n  if ((*ptr & CLASS_IS_ECLASS) != 0)\n    {\n    if (!compile_eclass_nested(context, negated, &ptr, &code,\n                               pop_info, lengthptr))\n      return FALSE;\n\n    PCRE2_ASSERT(*ptr == META_CLASS_END);\n    ptr++;\n    goto DONE;\n    }\n\n  ptr++;\n  /* Fall through */\n\n  default:\n  /* Scan forward characters, ranges, and properties.\n  For example: inside [a-z_ -- m] we don't have brackets around \"a-z_\" but\n  we still need to collect that fragment up into a \"leaf\" OP_CLASS. */\n\n  prev_ptr = ptr;\n  ptr = PRIV(compile_class_not_nested)(\n    context->options, context->xoptions, ptr, &code,\n    (meta != META_CLASS_NOT) == negated, &context->needs_bitmap,\n    context->errorcodeptr, context->cb, lengthptr);\n  if (ptr == NULL) return FALSE;\n\n  /* We must have a 100% guarantee that ptr increases when\n  compile_class_operand() returns, even on Release builds, so that we can\n  statically prove our loops terminate. */\n  if (ptr <= prev_ptr)\n    {\n    PCRE2_DEBUG_UNREACHABLE();\n    return FALSE;\n    }\n\n  /* If we fell through above, consume the closing ']'. */\n  if (meta == META_CLASS || meta == META_CLASS_NOT)\n    {\n    PCRE2_ASSERT(*ptr == META_CLASS_END);\n    ptr++;\n    }\n\n  /* Regardless of whether (lengthptr == NULL), some data will still be written\n  out to *pcode, which we need: we have to peek at it, to transform the opcode\n  into the ECLASS version (since we need to hoist up the bitmaps). */\n  PCRE2_ASSERT(code > code_start);\n  extra_length = (lengthptr != NULL)? *lengthptr - prev_length : 0;\n\n  /* Easiest case: convert OP_ALLANY to ECL_ANY */\n\n  if (*code_start == OP_ALLANY)\n    {\n    PCRE2_ASSERT(code - code_start == 1 && extra_length == 0);\n    pop_info->length = 1;\n    *code_start = pop_info->op_single_type = ECL_ANY;\n    memset(pop_info->bits.classbits, 0xff, 32);\n    }\n\n  /* For OP_CLASS and OP_NCLASS, we hoist out the bitmap and convert to\n  ECL_NONE / ECL_ANY respectively. */\n\n  else if (*code_start == OP_CLASS || *code_start == OP_NCLASS)\n    {\n    PCRE2_ASSERT(code - code_start == 1 + 32 / sizeof(PCRE2_UCHAR) &&\n                 extra_length == 0);\n    pop_info->length = 1;\n    *code_start = pop_info->op_single_type =\n        (*code_start == OP_CLASS)? ECL_NONE : ECL_ANY;\n    memcpy(pop_info->bits.classbits, code_start + 1, 32);\n    /* Rewind the code pointer, but make sure we adjust *lengthptr, because we\n    do need to reserve that space (even though we only use it temporarily). */\n    if (lengthptr != NULL)\n      *lengthptr += code - (code_start + 1);\n    code = code_start + 1;\n\n    if (!context->needs_bitmap && *code_start == ECL_NONE)\n      {\n      uint32_t *classwords = pop_info->bits.classwords;\n\n      for (int i = 0; i < 8; i++)\n        if (classwords[i] != 0)\n          {\n          context->needs_bitmap = TRUE;\n          break;\n          }\n      }\n    else\n      context->needs_bitmap = TRUE;\n    }\n\n  /* Finally, for OP_XCLASS we hoist out the bitmap (if any), and convert to\n  ECL_XCLASS. */\n\n  else\n    {\n    PCRE2_ASSERT(*code_start == OP_XCLASS);\n    *code_start = pop_info->op_single_type = ECL_XCLASS;\n\n    PCRE2_ASSERT(code - code_start >= 1 + LINK_SIZE + 1);\n\n    memcpy(pop_info->bits.classbits, context->cb->classbits.classbits, 32);\n    pop_info->length = (code - code_start) + extra_length;\n    }\n\n  break;\n  }  /* End of switch(meta) */\n\npop_info->code_start = (lengthptr == NULL)? code_start : NULL;\n\nif (lengthptr != NULL)\n  {\n  *lengthptr += code - code_start;\n  code = code_start;\n  }\n\nDONE:\nPCRE2_ASSERT(lengthptr == NULL || (code == code_start));\n\n*pptr = ptr;\n*pcode = code;\nreturn TRUE;\n}\n\n\n\n/* This function consumes a group of implicitly-unioned class elements.\nThese can be characters, ranges, properties, or nested classes, as long\nas they are all joined by being placed adjacently. */\n\nstatic BOOL\ncompile_class_juxtaposition(eclass_context *context, BOOL negated,\n  uint32_t **pptr, PCRE2_UCHAR **pcode, eclass_op_info *pop_info,\n  PCRE2_SIZE *lengthptr)\n{\nuint32_t *ptr = *pptr;\nPCRE2_UCHAR *code = *pcode;\n#ifdef PCRE2_DEBUG\nPCRE2_UCHAR *start_code = *pcode;\n#endif\n\n/* See compile_class_binary_loose() for comments on compile-time folding of\nthe \"negated\" flag. */\n\n/* Because it's a non-empty class, there must be an operand at the start. */\nif (!compile_class_operand(context, negated, &ptr, &code, pop_info, lengthptr))\n  return FALSE;\n\nwhile (*ptr != META_CLASS_END &&\n       !(*ptr >= META_ECLASS_AND && *ptr <= META_ECLASS_NOT))\n  {\n  uint32_t op;\n  BOOL rhs_negated;\n  eclass_op_info rhs_op_info;\n\n  if (negated)\n    {\n    /* !(A juxtapose B)  ->  !A && !B */\n    op = ECL_AND;\n    rhs_negated = TRUE;\n    }\n  else\n    {\n    /* A juxtapose B  ->  A || B */\n    op = ECL_OR;\n    rhs_negated = FALSE;\n    }\n\n  /* An operand must follow the operator. */\n  if (!compile_class_operand(context, rhs_negated, &ptr, &code,\n                             &rhs_op_info, lengthptr))\n    return FALSE;\n\n  /* Convert infix to postfix (RPN). */\n  fold_binary(op, pop_info, &rhs_op_info, lengthptr);\n  if (lengthptr == NULL)\n    code = pop_info->code_start + pop_info->length;\n  }\n\nPCRE2_ASSERT(lengthptr == NULL || code == start_code);\n\n*pptr = ptr;\n*pcode = code;\nreturn TRUE;\n}\n\n\n\n/* This function consumes unary prefix operators. */\n\nstatic BOOL\ncompile_class_unary(eclass_context *context, BOOL negated,\n  uint32_t **pptr, PCRE2_UCHAR **pcode, eclass_op_info *pop_info,\n  PCRE2_SIZE *lengthptr)\n{\nuint32_t *ptr = *pptr;\n#ifdef PCRE2_DEBUG\nPCRE2_UCHAR *start_code = *pcode;\n#endif\n\nwhile (*ptr == META_ECLASS_NOT)\n  {\n  ++ptr;\n  negated = !negated;\n  }\n\n*pptr = ptr;\n/* Because it's a non-empty class, there must be an operand. */\nif (!compile_class_juxtaposition(context, negated, pptr, pcode,\n                                 pop_info, lengthptr))\n  return FALSE;\n\nPCRE2_ASSERT(lengthptr == NULL || *pcode == start_code);\nreturn TRUE;\n}\n\n\n\n/* This function consumes tightly-binding binary operators. */\n\nstatic BOOL\ncompile_class_binary_tight(eclass_context *context, BOOL negated,\n  uint32_t **pptr, PCRE2_UCHAR **pcode, eclass_op_info *pop_info,\n  PCRE2_SIZE *lengthptr)\n{\nuint32_t *ptr = *pptr;\nPCRE2_UCHAR *code = *pcode;\n#ifdef PCRE2_DEBUG\nPCRE2_UCHAR *start_code = *pcode;\n#endif\n\n/* See compile_class_binary_loose() for comments on compile-time folding of\nthe \"negated\" flag. */\n\n/* Because it's a non-empty class, there must be an operand at the start. */\nif (!compile_class_unary(context, negated, &ptr, &code, pop_info, lengthptr))\n  return FALSE;\n\nwhile (*ptr == META_ECLASS_AND)\n  {\n  uint32_t op;\n  BOOL rhs_negated;\n  eclass_op_info rhs_op_info;\n\n  if (negated)\n    {\n    /* !(A && B)  ->  !A || !B */\n    op = ECL_OR;\n    rhs_negated = TRUE;\n    }\n  else\n    {\n    /* A && B  ->  A && B */\n    op = ECL_AND;\n    rhs_negated = FALSE;\n    }\n\n  ++ptr;\n\n  /* An operand must follow the operator. */\n  if (!compile_class_unary(context, rhs_negated, &ptr, &code,\n                           &rhs_op_info, lengthptr))\n    return FALSE;\n\n  /* Convert infix to postfix (RPN). */\n  fold_binary(op, pop_info, &rhs_op_info, lengthptr);\n  if (lengthptr == NULL)\n    code = pop_info->code_start + pop_info->length;\n  }\n\nPCRE2_ASSERT(lengthptr == NULL || code == start_code);\n\n*pptr = ptr;\n*pcode = code;\nreturn TRUE;\n}\n\n\n\n/* This function consumes loosely-binding binary operators. */\n\nstatic BOOL\ncompile_class_binary_loose(eclass_context *context, BOOL negated,\n  uint32_t **pptr, PCRE2_UCHAR **pcode, eclass_op_info *pop_info,\n  PCRE2_SIZE *lengthptr)\n{\nuint32_t *ptr = *pptr;\nPCRE2_UCHAR *code = *pcode;\n#ifdef PCRE2_DEBUG\nPCRE2_UCHAR *start_code = *pcode;\n#endif\n\n/* We really want to fold the negation operator, if at all possible, so that\nsimple cases can be reduced down. In particular, in 8-bit no-UTF mode, we want\nto produce a fully-folded expression, so that we can guarantee not to emit any\nOP_ECLASS codes (in the same way that we never emit OP_XCLASS in this mode).\n\nThis has the consequence that with a little ingenuity, we can in fact avoid\nemitting (nearly...) all cases of the \"NOT\" operator. Imagine that we have:\n    !(A ...\nWe have parsed the preceding \"!\", and we are about to parse the \"A\" operand. We\ndon't know yet whether there will even be a following binary operand! Both of\nthese are possibilities for what follows:\n    !(A && B)\n    !(A)\nHowever, we can still fold the \"!\" into the \"A\" operand, because no matter what\nthe following binary operator will be, we can produce an expression which is\nequivalent. */\n\n/* Because it's a non-empty class, there must be an operand at the start. */\nif (!compile_class_binary_tight(context, negated, &ptr, &code,\n                                pop_info, lengthptr))\n  return FALSE;\n\nwhile (*ptr >= META_ECLASS_OR && *ptr <= META_ECLASS_XOR)\n  {\n  uint32_t op;\n  BOOL op_neg;\n  BOOL rhs_negated;\n  eclass_op_info rhs_op_info;\n\n  if (negated)\n    {\n    /* The whole expression is being negated; we respond by unconditionally\n    negating the LHS A, before seeing what follows. And hooray! We can recover,\n    no matter what follows. */\n    /* !(A || B)   ->  !A && !B                     */\n    /* !(A -- B)   ->  !(A && !B)    ->  !A || B    */\n    /* !(A XOR B)  ->  !(!A XOR !B)  ->  !A XNOR !B */\n    op = (*ptr == META_ECLASS_OR )? ECL_AND :\n         (*ptr == META_ECLASS_SUB)? ECL_OR  :\n         /*ptr == META_ECLASS_XOR*/ ECL_XOR;\n    op_neg = (*ptr == META_ECLASS_XOR);\n    rhs_negated = *ptr != META_ECLASS_SUB;\n    }\n  else\n    {\n    /* A || B   ->  A || B  */\n    /* A -- B   ->  A && !B */\n    /* A XOR B  ->  A XOR B */\n    op = (*ptr == META_ECLASS_OR )? ECL_OR  :\n         (*ptr == META_ECLASS_SUB)? ECL_AND :\n         /*ptr == META_ECLASS_XOR*/ ECL_XOR;\n    op_neg = FALSE;\n    rhs_negated = *ptr == META_ECLASS_SUB;\n    }\n\n  ++ptr;\n\n  /* An operand must follow the operator. */\n  if (!compile_class_binary_tight(context, rhs_negated, &ptr, &code,\n                                  &rhs_op_info, lengthptr))\n    return FALSE;\n\n  /* Convert infix to postfix (RPN). */\n  fold_binary(op, pop_info, &rhs_op_info, lengthptr);\n  if (op_neg) fold_negation(pop_info, lengthptr, FALSE);\n  if (lengthptr == NULL)\n    code = pop_info->code_start + pop_info->length;\n  }\n\nPCRE2_ASSERT(lengthptr == NULL || code == start_code);\n\n*pptr = ptr;\n*pcode = code;\nreturn TRUE;\n}\n\n\n\n/* This function converts the META codes in pptr into opcodes written to\npcode. The pptr must start at a META_CLASS or META_CLASS_NOT.\n\nThe class is compiled as a left-associative sequence of operator\napplications.\n\nThe pptr will be left pointing at the matching META_CLASS_END. */\n\nstatic BOOL\ncompile_eclass_nested(eclass_context *context, BOOL negated,\n  uint32_t **pptr, PCRE2_UCHAR **pcode,\n  eclass_op_info *pop_info, PCRE2_SIZE *lengthptr)\n{\nuint32_t *ptr = *pptr;\n#ifdef PCRE2_DEBUG\nPCRE2_UCHAR *start_code = *pcode;\n#endif\n\n/* The CLASS_IS_ECLASS bit must be set since it is a nested class. */\nPCRE2_ASSERT(*ptr == (META_CLASS | CLASS_IS_ECLASS) ||\n             *ptr == (META_CLASS_NOT | CLASS_IS_ECLASS));\n\nif (*ptr++ == (META_CLASS_NOT | CLASS_IS_ECLASS))\n  negated = !negated;\n\n(*pptr)++;\n\n/* Because it's a non-empty class, there must be an operand at the start. */\nif (!compile_class_binary_loose(context, negated, pptr, pcode,\n                                pop_info, lengthptr))\n  return FALSE;\n\nPCRE2_ASSERT(**pptr == META_CLASS_END);\nPCRE2_ASSERT(lengthptr == NULL || *pcode == start_code);\nreturn TRUE;\n}\n\nBOOL\nPRIV(compile_class_nested)(uint32_t options, uint32_t xoptions,\n  uint32_t **pptr, PCRE2_UCHAR **pcode, int *errorcodeptr,\n  compile_block *cb, PCRE2_SIZE *lengthptr)\n{\neclass_context context;\neclass_op_info op_info;\nPCRE2_SIZE previous_length = (lengthptr != NULL)? *lengthptr : 0;\nPCRE2_UCHAR *code = *pcode;\nPCRE2_UCHAR *previous;\nBOOL allbitsone = TRUE;\n\ncontext.needs_bitmap = FALSE;\ncontext.options = options;\ncontext.xoptions = xoptions;\ncontext.errorcodeptr = errorcodeptr;\ncontext.cb = cb;\n\nprevious = code;\n*code++ = OP_ECLASS;\ncode += LINK_SIZE;\n*code++ = 0;  /* Flags, currently zero. */\nif (!compile_eclass_nested(&context, FALSE, pptr, &code, &op_info, lengthptr))\n  return FALSE;\n\nif (lengthptr != NULL)\n  {\n  *lengthptr += code - previous;\n  code = previous;\n  /* (*lengthptr - previous_length) now holds the amount of buffer that\n  we require to make the call to compile_class_nested() with\n  lengthptr = NULL, and including the (1+LINK_SIZE+1) that we write out\n  before that call. */\n  }\n\n/* Do some useful counting of what's in the bitmap. */\nfor (int i = 0; i < 8; i++)\n  if (op_info.bits.classwords[i] != 0xffffffff)\n    {\n    allbitsone = FALSE;\n    break;\n    }\n\n/* After constant-folding the extended class syntax, it may turn out to be\na simple class after all. In that case, we can unwrap it from the\nOP_ECLASS container - and in fact, we must do so, because in 8-bit\nno-Unicode mode the matcher is compiled without support for OP_ECLASS. */\n\n#ifndef SUPPORT_WIDE_CHARS\nPCRE2_ASSERT(op_info.op_single_type != 0);\n#else\nif (op_info.op_single_type != 0)\n#endif\n  {\n  /* Rewind back over the OP_ECLASS. */\n  code = previous;\n\n  /* If the bits are all ones, and the \"high characters\" are all matched\n  too, we use a special-cased encoding of OP_ALLANY. */\n\n  if (op_info.op_single_type == ECL_ANY && allbitsone)\n    {\n    /* Advancing code means rewinding lengthptr, at this point. */\n    if (lengthptr != NULL) *lengthptr -= 1;\n    *code++ = OP_ALLANY;\n    }\n\n  /* If the high bits are all matched / all not-matched, then we emit an\n  OP_NCLASS/OP_CLASS respectively. */\n\n  else if (op_info.op_single_type == ECL_ANY ||\n           op_info.op_single_type == ECL_NONE)\n    {\n    PCRE2_SIZE required_len = 1 + (32 / sizeof(PCRE2_UCHAR));\n\n    if (lengthptr != NULL)\n      {\n      if (required_len > (*lengthptr - previous_length))\n      *lengthptr = previous_length + required_len;\n      }\n\n    /* Advancing code means rewinding lengthptr, at this point. */\n    if (lengthptr != NULL) *lengthptr -= required_len;\n    *code++ = (op_info.op_single_type == ECL_ANY)? OP_NCLASS : OP_CLASS;\n    memcpy(code, op_info.bits.classbits, 32);\n    code += 32 / sizeof(PCRE2_UCHAR);\n    }\n\n  /* Otherwise, we have an ECL_XCLASS, so we have the OP_XCLASS data\n  there, but, we pulled out its bitmap into op_info, so now we have to\n  put that back into the OP_XCLASS. */\n\n  else\n    {\n#ifndef SUPPORT_WIDE_CHARS\n    PCRE2_DEBUG_UNREACHABLE();\n#else\n    BOOL need_map = context.needs_bitmap;\n    PCRE2_SIZE required_len;\n\n    PCRE2_ASSERT(op_info.op_single_type == ECL_XCLASS);\n    required_len = op_info.length + (need_map? 32/sizeof(PCRE2_UCHAR) : 0);\n\n    if (lengthptr != NULL)\n      {\n      /* Don't unconditionally request all the space we need - we may\n      already have asked for more during processing of the ECLASS. */\n      if (required_len > (*lengthptr - previous_length))\n        *lengthptr = previous_length + required_len;\n\n      /* The code we write out here won't be ignored, even during the\n      (lengthptr != NULL) phase, because if there's a following quantifier\n      it will peek backwards. So we do have to write out a (truncated)\n      OP_XCLASS, even on this branch. */\n      *lengthptr -= 1 + LINK_SIZE + 1;\n      *code++ = OP_XCLASS;\n      PUT(code, 0, 1 + LINK_SIZE + 1);\n      code += LINK_SIZE;\n      *code++ = 0;\n      }\n    else\n      {\n      PCRE2_UCHAR *rest;\n      PCRE2_SIZE rest_len;\n      PCRE2_UCHAR flags;\n\n      /* 1 unit: OP_XCLASS | LINK_SIZE units | 1 unit: flags | ...rest */\n      PCRE2_ASSERT(op_info.length >= 1 + LINK_SIZE + 1);\n      rest = op_info.code_start + 1 + LINK_SIZE + 1;\n      rest_len = (op_info.code_start + op_info.length) - rest;\n\n      /* First read any data we use, before memmove splats it. */\n      flags = op_info.code_start[1 + LINK_SIZE];\n      PCRE2_ASSERT((flags & XCL_MAP) == 0);\n\n      /* Next do the memmove before any writes. */\n      memmove(code + 1 + LINK_SIZE + 1 + (need_map? 32/sizeof(PCRE2_UCHAR) : 0),\n              rest, CU2BYTES(rest_len));\n\n      /* Finally write the header data. */\n      *code++ = OP_XCLASS;\n      PUT(code, 0, (int)required_len);\n      code += LINK_SIZE;\n      *code++ = flags | (need_map? XCL_MAP : 0);\n      if (need_map)\n        {\n        memcpy(code, op_info.bits.classbits, 32);\n        code += 32 / sizeof(PCRE2_UCHAR);\n        }\n      code += rest_len;\n      }\n#endif /* SUPPORT_WIDE_CHARS */\n    }\n  }\n\n/* Otherwise, we're going to keep the OP_ECLASS. However, again we need\nto do some adjustment to insert the bitmap if we have one. */\n\n#ifdef SUPPORT_WIDE_CHARS\nelse\n  {\n  BOOL need_map = context.needs_bitmap;\n  PCRE2_SIZE required_len =\n    1 + LINK_SIZE + 1 + (need_map? 32/sizeof(PCRE2_UCHAR) : 0) + op_info.length;\n\n  if (lengthptr != NULL)\n    {\n    if (required_len > (*lengthptr - previous_length))\n      *lengthptr = previous_length + required_len;\n\n    /* As for the XCLASS branch above, we do have to write out a dummy\n    OP_ECLASS, because of the backwards peek by the quantifier code. Write\n    out a (truncated) OP_ECLASS, even on this branch. */\n    *lengthptr -= 1 + LINK_SIZE + 1;\n    *code++ = OP_ECLASS;\n    PUT(code, 0, 1 + LINK_SIZE + 1);\n    code += LINK_SIZE;\n    *code++ = 0;\n    }\n  else\n    {\n    if (need_map)\n      {\n      PCRE2_UCHAR *map_start = previous + 1 + LINK_SIZE + 1;\n      previous[1 + LINK_SIZE] |= ECL_MAP;\n      memmove(map_start + 32/sizeof(PCRE2_UCHAR), map_start,\n              CU2BYTES(code - map_start));\n      memcpy(map_start, op_info.bits.classbits, 32);\n      code += 32 / sizeof(PCRE2_UCHAR);\n      }\n    PUT(previous, 1, (int)(code - previous));\n    }\n  }\n#endif /* SUPPORT_WIDE_CHARS */\n\n*pcode = code;\nreturn TRUE;\n}\n\n/* End of pcre2_compile_class.c */\n"
  },
  {
    "path": "Libraries/PCRE/pcre2_config.c",
    "content": "/*************************************************\n*      Perl-Compatible Regular Expressions       *\n*************************************************/\n\n/* PCRE is a library of functions to support regular expressions whose syntax\nand semantics are as close as possible to those of the Perl 5 language.\n\n                       Written by Philip Hazel\n     Original API code Copyright (c) 1997-2012 University of Cambridge\n          New API code Copyright (c) 2016-2020 University of Cambridge\n\n-----------------------------------------------------------------------------\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n    * Redistributions of source code must retain the above copyright notice,\n      this list of conditions and the following disclaimer.\n\n    * Redistributions in binary form must reproduce the above copyright\n      notice, this list of conditions and the following disclaimer in the\n      documentation and/or other materials provided with the distribution.\n\n    * Neither the name of the University of Cambridge nor the names of its\n      contributors may be used to endorse or promote products derived from\n      this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\nLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\nCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\nSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\nINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\nCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGE.\n-----------------------------------------------------------------------------\n*/\n\n#ifdef HAVE_CONFIG_H\n#include \"config.h\"\n#endif\n\n/* Save the configured link size, which is in bytes. In 16-bit and 32-bit modes\nits value gets changed by pcre2_intmodedep.h (included by pcre2_internal.h) to\nbe in code units. */\n\nstatic int configured_link_size = LINK_SIZE;\n\n#include \"pcre2_internal.h\"\n\n/* These macros are the standard way of turning unquoted text into C strings.\nThey allow macros like PCRE2_MAJOR to be defined without quotes, which is\nconvenient for user programs that want to test their values. */\n\n#define STRING(a)  # a\n#define XSTRING(s) STRING(s)\n\n\n/*************************************************\n* Return info about what features are configured *\n*************************************************/\n\n/* If where is NULL, the length of memory required is returned.\n\nArguments:\n  what             what information is required\n  where            where to put the information\n\nReturns:           0 if a numerical value is returned\n                   >= 0 if a string value\n                   PCRE2_ERROR_BADOPTION if \"where\" not recognized\n                     or JIT target requested when JIT not enabled\n*/\n\nPCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION\npcre2_config(uint32_t what, void *where)\n{\nif (where == NULL)  /* Requests a length */\n  {\n  switch(what)\n    {\n    default:\n    return PCRE2_ERROR_BADOPTION;\n\n    case PCRE2_CONFIG_BSR:\n    case PCRE2_CONFIG_COMPILED_WIDTHS:\n    case PCRE2_CONFIG_DEPTHLIMIT:\n    case PCRE2_CONFIG_HEAPLIMIT:\n    case PCRE2_CONFIG_JIT:\n    case PCRE2_CONFIG_LINKSIZE:\n    case PCRE2_CONFIG_MATCHLIMIT:\n    case PCRE2_CONFIG_NEVER_BACKSLASH_C:\n    case PCRE2_CONFIG_NEWLINE:\n    case PCRE2_CONFIG_PARENSLIMIT:\n    case PCRE2_CONFIG_STACKRECURSE:    /* Obsolete */\n    case PCRE2_CONFIG_TABLES_LENGTH:\n    case PCRE2_CONFIG_UNICODE:\n    return sizeof(uint32_t);\n\n    /* These are handled below */\n\n    case PCRE2_CONFIG_JITTARGET:\n    case PCRE2_CONFIG_UNICODE_VERSION:\n    case PCRE2_CONFIG_VERSION:\n    break;\n    }\n  }\n\nswitch (what)\n  {\n  default:\n  return PCRE2_ERROR_BADOPTION;\n\n  case PCRE2_CONFIG_BSR:\n#ifdef BSR_ANYCRLF\n  *((uint32_t *)where) = PCRE2_BSR_ANYCRLF;\n#else\n  *((uint32_t *)where) = PCRE2_BSR_UNICODE;\n#endif\n  break;\n\n  case PCRE2_CONFIG_COMPILED_WIDTHS:\n  *((uint32_t *)where) = 0\n#ifdef SUPPORT_PCRE2_8\n  + 1\n#endif\n#ifdef SUPPORT_PCRE2_16\n  + 2\n#endif\n#ifdef SUPPORT_PCRE2_32\n  + 4\n#endif\n  ;\n  break;\n\n  case PCRE2_CONFIG_DEPTHLIMIT:\n  *((uint32_t *)where) = MATCH_LIMIT_DEPTH;\n  break;\n\n  case PCRE2_CONFIG_HEAPLIMIT:\n  *((uint32_t *)where) = HEAP_LIMIT;\n  break;\n\n  case PCRE2_CONFIG_JIT:\n#ifdef SUPPORT_JIT\n  *((uint32_t *)where) = 1;\n#else\n  *((uint32_t *)where) = 0;\n#endif\n  break;\n\n  case PCRE2_CONFIG_JITTARGET:\n#ifdef SUPPORT_JIT\n    {\n    const char *v = PRIV(jit_get_target)();\n    return (int)(1 + ((where == NULL)?\n      strlen(v) : PRIV(strcpy_c8)((PCRE2_UCHAR *)where, v)));\n    }\n#else\n  return PCRE2_ERROR_BADOPTION;\n#endif\n\n  case PCRE2_CONFIG_LINKSIZE:\n  *((uint32_t *)where) = (uint32_t)configured_link_size;\n  break;\n\n  case PCRE2_CONFIG_MATCHLIMIT:\n  *((uint32_t *)where) = MATCH_LIMIT;\n  break;\n\n  case PCRE2_CONFIG_NEWLINE:\n  *((uint32_t *)where) = NEWLINE_DEFAULT;\n  break;\n\n  case PCRE2_CONFIG_NEVER_BACKSLASH_C:\n#ifdef NEVER_BACKSLASH_C\n  *((uint32_t *)where) = 1;\n#else\n  *((uint32_t *)where) = 0;\n#endif\n  break;\n\n  case PCRE2_CONFIG_PARENSLIMIT:\n  *((uint32_t *)where) = PARENS_NEST_LIMIT;\n  break;\n\n  /* This is now obsolete. The stack is no longer used via recursion for\n  handling backtracking in pcre2_match(). */\n\n  case PCRE2_CONFIG_STACKRECURSE:\n  *((uint32_t *)where) = 0;\n  break;\n\n  case PCRE2_CONFIG_TABLES_LENGTH:\n  *((uint32_t *)where) = TABLES_LENGTH;\n  break;\n\n  case PCRE2_CONFIG_UNICODE_VERSION:\n    {\n#if defined SUPPORT_UNICODE\n    const char *v = PRIV(unicode_version);\n#else\n    const char *v = \"Unicode not supported\";\n#endif\n    return (int)(1 + ((where == NULL)?\n      strlen(v) : PRIV(strcpy_c8)((PCRE2_UCHAR *)where, v)));\n   }\n  break;\n\n  case PCRE2_CONFIG_UNICODE:\n#if defined SUPPORT_UNICODE\n  *((uint32_t *)where) = 1;\n#else\n  *((uint32_t *)where) = 0;\n#endif\n  break;\n\n  /* The hackery in setting \"v\" below is to cope with the case when\n  PCRE2_PRERELEASE is set to an empty string (which it is for real releases).\n  If the second alternative is used in this case, it does not leave a space\n  before the date. On the other hand, if all four macros are put into a single\n  XSTRING when PCRE2_PRERELEASE is not empty, an unwanted space is inserted.\n  There are problems using an \"obvious\" approach like this:\n\n     XSTRING(PCRE2_MAJOR) \".\" XSTRING(PCRE2_MINOR)\n     XSTRING(PCRE2_PRERELEASE) \" \" XSTRING(PCRE2_DATE)\n\n  because, when PCRE2_PRERELEASE is empty, this leads to an attempted expansion\n  of STRING(). The C standard states: \"If (before argument substitution) any\n  argument consists of no preprocessing tokens, the behavior is undefined.\" It\n  turns out the gcc treats this case as a single empty string - which is what\n  we really want - but Visual C grumbles about the lack of an argument for the\n  macro. Unfortunately, both are within their rights. As there seems to be no\n  way to test for a macro's value being empty at compile time, we have to\n  resort to a runtime test. */\n\n  case PCRE2_CONFIG_VERSION:\n    {\n    const char *v = (XSTRING(Z PCRE2_PRERELEASE)[1] == 0)?\n      XSTRING(PCRE2_MAJOR.PCRE2_MINOR PCRE2_DATE) :\n      XSTRING(PCRE2_MAJOR.PCRE2_MINOR) XSTRING(PCRE2_PRERELEASE PCRE2_DATE);\n    return (int)(1 + ((where == NULL)?\n      strlen(v) : PRIV(strcpy_c8)((PCRE2_UCHAR *)where, v)));\n    }\n  }\n\nreturn 0;\n}\n\n/* End of pcre2_config.c */\n"
  },
  {
    "path": "Libraries/PCRE/pcre2_context.c",
    "content": "/*************************************************\n*      Perl-Compatible Regular Expressions       *\n*************************************************/\n\n/* PCRE is a library of functions to support regular expressions whose syntax\nand semantics are as close as possible to those of the Perl 5 language.\n\n                       Written by Philip Hazel\n     Original API code Copyright (c) 1997-2012 University of Cambridge\n          New API code Copyright (c) 2016-2024 University of Cambridge\n\n-----------------------------------------------------------------------------\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n    * Redistributions of source code must retain the above copyright notice,\n      this list of conditions and the following disclaimer.\n\n    * Redistributions in binary form must reproduce the above copyright\n      notice, this list of conditions and the following disclaimer in the\n      documentation and/or other materials provided with the distribution.\n\n    * Neither the name of the University of Cambridge nor the names of its\n      contributors may be used to endorse or promote products derived from\n      this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\nLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\nCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\nSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\nINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\nCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGE.\n-----------------------------------------------------------------------------\n*/\n\n\n#ifdef HAVE_CONFIG_H\n#include \"config.h\"\n#endif\n\n#include \"pcre2_internal.h\"\n\n\n\n/*************************************************\n*          Default malloc/free functions         *\n*************************************************/\n\n/* Ignore the \"user data\" argument in each case. */\n\nstatic void *default_malloc(size_t size, void *data)\n{\n(void)data;\nreturn malloc(size);\n}\n\n\nstatic void default_free(void *block, void *data)\n{\n(void)data;\nfree(block);\n}\n\n\n\n/*************************************************\n*        Get a block and save memory control     *\n*************************************************/\n\n/* This internal function is called to get a block of memory in which the\nmemory control data is to be stored at the start for future use.\n\nArguments:\n  size        amount of memory required\n  memctl      pointer to a memctl block or NULL\n\nReturns:      pointer to memory or NULL on failure\n*/\n\nextern void *\nPRIV(memctl_malloc)(size_t size, pcre2_memctl *memctl)\n{\npcre2_memctl *newmemctl;\nvoid *yield = (memctl == NULL)? malloc(size) :\n  memctl->malloc(size, memctl->memory_data);\nif (yield == NULL) return NULL;\nnewmemctl = (pcre2_memctl *)yield;\nif (memctl == NULL)\n  {\n  newmemctl->malloc = default_malloc;\n  newmemctl->free = default_free;\n  newmemctl->memory_data = NULL;\n  }\nelse *newmemctl = *memctl;\nreturn yield;\n}\n\n\n\n/*************************************************\n*          Create and initialize contexts        *\n*************************************************/\n\n/* Initializing for compile and match contexts is done in separate, private\nfunctions so that these can be called from functions such as pcre2_compile()\nwhen an external context is not supplied. The initializing functions have an\noption to set up default memory management. */\n\nPCRE2_EXP_DEFN pcre2_general_context * PCRE2_CALL_CONVENTION\npcre2_general_context_create(void *(*private_malloc)(size_t, void *),\n  void (*private_free)(void *, void *), void *memory_data)\n{\npcre2_general_context *gcontext;\nif (private_malloc == NULL) private_malloc = default_malloc;\nif (private_free == NULL) private_free = default_free;\ngcontext = private_malloc(sizeof(pcre2_real_general_context), memory_data);\nif (gcontext == NULL) return NULL;\ngcontext->memctl.malloc = private_malloc;\ngcontext->memctl.free = private_free;\ngcontext->memctl.memory_data = memory_data;\nreturn gcontext;\n}\n\n\n/* A default compile context is set up to save having to initialize at run time\nwhen no context is supplied to the compile function. */\n\npcre2_compile_context PRIV(default_compile_context) = {\n  { default_malloc, default_free, NULL },    /* Default memory handling */\n  NULL,                                      /* Stack guard */\n  NULL,                                      /* Stack guard data */\n  PRIV(default_tables),                      /* Character tables */\n  PCRE2_UNSET,                               /* Max pattern length */\n  PCRE2_UNSET,                               /* Max pattern compiled length */\n  BSR_DEFAULT,                               /* Backslash R default */\n  NEWLINE_DEFAULT,                           /* Newline convention */\n  PARENS_NEST_LIMIT,                         /* As it says */\n  0,                                         /* Extra options */\n  MAX_VARLOOKBEHIND,                         /* As it says */\n  PCRE2_OPTIMIZATION_ALL                     /* All optimizations enabled */\n  };\n\n/* The create function copies the default into the new memory, but must\noverride the default memory handling functions if a gcontext was provided. */\n\nPCRE2_EXP_DEFN pcre2_compile_context * PCRE2_CALL_CONVENTION\npcre2_compile_context_create(pcre2_general_context *gcontext)\n{\npcre2_compile_context *ccontext = PRIV(memctl_malloc)(\n  sizeof(pcre2_real_compile_context), (pcre2_memctl *)gcontext);\nif (ccontext == NULL) return NULL;\n*ccontext = PRIV(default_compile_context);\nif (gcontext != NULL)\n  *((pcre2_memctl *)ccontext) = *((pcre2_memctl *)gcontext);\nreturn ccontext;\n}\n\n\n/* A default match context is set up to save having to initialize at run time\nwhen no context is supplied to a match function. */\n\npcre2_match_context PRIV(default_match_context) = {\n  { default_malloc, default_free, NULL },\n#ifdef SUPPORT_JIT\n  NULL,          /* JIT callback */\n  NULL,          /* JIT callback data */\n#endif\n  NULL,          /* Callout function */\n  NULL,          /* Callout data */\n  NULL,          /* Substitute callout function */\n  NULL,          /* Substitute callout data */\n  NULL,          /* Substitute case callout function */\n  NULL,          /* Substitute case callout data */\n  PCRE2_UNSET,   /* Offset limit */\n  HEAP_LIMIT,\n  MATCH_LIMIT,\n  MATCH_LIMIT_DEPTH };\n\n/* The create function copies the default into the new memory, but must\noverride the default memory handling functions if a gcontext was provided. */\n\nPCRE2_EXP_DEFN pcre2_match_context * PCRE2_CALL_CONVENTION\npcre2_match_context_create(pcre2_general_context *gcontext)\n{\npcre2_match_context *mcontext = PRIV(memctl_malloc)(\n  sizeof(pcre2_real_match_context), (pcre2_memctl *)gcontext);\nif (mcontext == NULL) return NULL;\n*mcontext = PRIV(default_match_context);\nif (gcontext != NULL)\n  *((pcre2_memctl *)mcontext) = *((pcre2_memctl *)gcontext);\nreturn mcontext;\n}\n\n\n/* A default convert context is set up to save having to initialize at run time\nwhen no context is supplied to the convert function. */\n\npcre2_convert_context PRIV(default_convert_context) = {\n  { default_malloc, default_free, NULL },    /* Default memory handling */\n#ifdef _WIN32\n  CHAR_BACKSLASH,                            /* Default path separator */\n  CHAR_GRAVE_ACCENT                          /* Default escape character */\n#else  /* Not Windows */\n  CHAR_SLASH,                                /* Default path separator */\n  CHAR_BACKSLASH                             /* Default escape character */\n#endif\n  };\n\n/* The create function copies the default into the new memory, but must\noverride the default memory handling functions if a gcontext was provided. */\n\nPCRE2_EXP_DEFN pcre2_convert_context * PCRE2_CALL_CONVENTION\npcre2_convert_context_create(pcre2_general_context *gcontext)\n{\npcre2_convert_context *ccontext = PRIV(memctl_malloc)(\n  sizeof(pcre2_real_convert_context), (pcre2_memctl *)gcontext);\nif (ccontext == NULL) return NULL;\n*ccontext = PRIV(default_convert_context);\nif (gcontext != NULL)\n  *((pcre2_memctl *)ccontext) = *((pcre2_memctl *)gcontext);\nreturn ccontext;\n}\n\n\n/*************************************************\n*              Context copy functions            *\n*************************************************/\n\nPCRE2_EXP_DEFN pcre2_general_context * PCRE2_CALL_CONVENTION\npcre2_general_context_copy(pcre2_general_context *gcontext)\n{\npcre2_general_context *newcontext =\n  gcontext->memctl.malloc(sizeof(pcre2_real_general_context),\n  gcontext->memctl.memory_data);\nif (newcontext == NULL) return NULL;\nmemcpy(newcontext, gcontext, sizeof(pcre2_real_general_context));\nreturn newcontext;\n}\n\n\nPCRE2_EXP_DEFN pcre2_compile_context * PCRE2_CALL_CONVENTION\npcre2_compile_context_copy(pcre2_compile_context *ccontext)\n{\npcre2_compile_context *newcontext =\n  ccontext->memctl.malloc(sizeof(pcre2_real_compile_context),\n  ccontext->memctl.memory_data);\nif (newcontext == NULL) return NULL;\nmemcpy(newcontext, ccontext, sizeof(pcre2_real_compile_context));\nreturn newcontext;\n}\n\n\nPCRE2_EXP_DEFN pcre2_match_context * PCRE2_CALL_CONVENTION\npcre2_match_context_copy(pcre2_match_context *mcontext)\n{\npcre2_match_context *newcontext =\n  mcontext->memctl.malloc(sizeof(pcre2_real_match_context),\n  mcontext->memctl.memory_data);\nif (newcontext == NULL) return NULL;\nmemcpy(newcontext, mcontext, sizeof(pcre2_real_match_context));\nreturn newcontext;\n}\n\n\nPCRE2_EXP_DEFN pcre2_convert_context * PCRE2_CALL_CONVENTION\npcre2_convert_context_copy(pcre2_convert_context *ccontext)\n{\npcre2_convert_context *newcontext =\n  ccontext->memctl.malloc(sizeof(pcre2_real_convert_context),\n  ccontext->memctl.memory_data);\nif (newcontext == NULL) return NULL;\nmemcpy(newcontext, ccontext, sizeof(pcre2_real_convert_context));\nreturn newcontext;\n}\n\n\n/*************************************************\n*              Context free functions            *\n*************************************************/\n\nPCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION\npcre2_general_context_free(pcre2_general_context *gcontext)\n{\nif (gcontext != NULL)\n  gcontext->memctl.free(gcontext, gcontext->memctl.memory_data);\n}\n\n\nPCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION\npcre2_compile_context_free(pcre2_compile_context *ccontext)\n{\nif (ccontext != NULL)\n  ccontext->memctl.free(ccontext, ccontext->memctl.memory_data);\n}\n\n\nPCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION\npcre2_match_context_free(pcre2_match_context *mcontext)\n{\nif (mcontext != NULL)\n  mcontext->memctl.free(mcontext, mcontext->memctl.memory_data);\n}\n\n\nPCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION\npcre2_convert_context_free(pcre2_convert_context *ccontext)\n{\nif (ccontext != NULL)\n  ccontext->memctl.free(ccontext, ccontext->memctl.memory_data);\n}\n\n\n/*************************************************\n*             Set values in contexts             *\n*************************************************/\n\n/* All these functions return 0 for success or PCRE2_ERROR_BADDATA if invalid\ndata is given. Only some of the functions are able to test the validity of the\ndata. */\n\n\n/* ------------ Compile context ------------ */\n\nPCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION\npcre2_set_character_tables(pcre2_compile_context *ccontext,\n  const uint8_t *tables)\n{\nccontext->tables = tables;\nreturn 0;\n}\n\nPCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION\npcre2_set_bsr(pcre2_compile_context *ccontext, uint32_t value)\n{\nswitch(value)\n  {\n  case PCRE2_BSR_ANYCRLF:\n  case PCRE2_BSR_UNICODE:\n  ccontext->bsr_convention = value;\n  return 0;\n\n  default:\n  return PCRE2_ERROR_BADDATA;\n  }\n}\n\nPCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION\npcre2_set_max_pattern_length(pcre2_compile_context *ccontext, PCRE2_SIZE length)\n{\nccontext->max_pattern_length = length;\nreturn 0;\n}\n\nPCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION\npcre2_set_max_pattern_compiled_length(pcre2_compile_context *ccontext, PCRE2_SIZE length)\n{\nccontext->max_pattern_compiled_length = length;\nreturn 0;\n}\n\nPCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION\npcre2_set_newline(pcre2_compile_context *ccontext, uint32_t newline)\n{\nswitch(newline)\n  {\n  case PCRE2_NEWLINE_CR:\n  case PCRE2_NEWLINE_LF:\n  case PCRE2_NEWLINE_CRLF:\n  case PCRE2_NEWLINE_ANY:\n  case PCRE2_NEWLINE_ANYCRLF:\n  case PCRE2_NEWLINE_NUL:\n  ccontext->newline_convention = newline;\n  return 0;\n\n  default:\n  return PCRE2_ERROR_BADDATA;\n  }\n}\n\nPCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION\npcre2_set_max_varlookbehind(pcre2_compile_context *ccontext, uint32_t limit)\n{\nccontext->max_varlookbehind = limit;\nreturn 0;\n}\n\nPCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION\npcre2_set_parens_nest_limit(pcre2_compile_context *ccontext, uint32_t limit)\n{\nccontext->parens_nest_limit = limit;\nreturn 0;\n}\n\nPCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION\npcre2_set_compile_extra_options(pcre2_compile_context *ccontext, uint32_t options)\n{\nccontext->extra_options = options;\nreturn 0;\n}\n\nPCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION\npcre2_set_compile_recursion_guard(pcre2_compile_context *ccontext,\n  int (*guard)(uint32_t, void *), void *user_data)\n{\nccontext->stack_guard = guard;\nccontext->stack_guard_data = user_data;\nreturn 0;\n}\n\nPCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION\npcre2_set_optimize(pcre2_compile_context *ccontext, uint32_t directive)\n{\nif (ccontext == NULL)\n  return PCRE2_ERROR_NULL;\n\nswitch (directive)\n  {\n  case PCRE2_OPTIMIZATION_NONE:\n  ccontext->optimization_flags = 0;\n  break;\n\n  case PCRE2_OPTIMIZATION_FULL:\n  ccontext->optimization_flags = PCRE2_OPTIMIZATION_ALL;\n  break;\n\n  default:\n  if (directive >= PCRE2_AUTO_POSSESS && directive <= PCRE2_START_OPTIMIZE_OFF)\n    {\n    /* Even directive numbers starting from 64 switch a bit on;\n     * Odd directive numbers starting from 65 switch a bit off */\n    if ((directive & 1) != 0)\n      ccontext->optimization_flags &= ~(1u << ((directive >> 1) - 32));\n    else\n      ccontext->optimization_flags |= 1u << ((directive >> 1) - 32);\n    return 0;\n    }\n  return PCRE2_ERROR_BADOPTION;\n  }\n\nreturn 0;\n}\n\n/* ------------ Match context ------------ */\n\nPCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION\npcre2_set_callout(pcre2_match_context *mcontext,\n  int (*callout)(pcre2_callout_block *, void *), void *callout_data)\n{\nmcontext->callout = callout;\nmcontext->callout_data = callout_data;\nreturn 0;\n}\n\nPCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION\npcre2_set_substitute_callout(pcre2_match_context *mcontext,\n  int (*substitute_callout)(pcre2_substitute_callout_block *, void *),\n  void *substitute_callout_data)\n{\nmcontext->substitute_callout = substitute_callout;\nmcontext->substitute_callout_data = substitute_callout_data;\nreturn 0;\n}\n\nPCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION\npcre2_set_substitute_case_callout(pcre2_match_context *mcontext,\n  PCRE2_SIZE (*substitute_case_callout)(PCRE2_SPTR, PCRE2_SIZE, PCRE2_UCHAR *,\n                                        PCRE2_SIZE, int, void *),\n  void *substitute_case_callout_data)\n{\nmcontext->substitute_case_callout = substitute_case_callout;\nmcontext->substitute_case_callout_data = substitute_case_callout_data;\nreturn 0;\n}\n\nPCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION\npcre2_set_heap_limit(pcre2_match_context *mcontext, uint32_t limit)\n{\nmcontext->heap_limit = limit;\nreturn 0;\n}\n\nPCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION\npcre2_set_match_limit(pcre2_match_context *mcontext, uint32_t limit)\n{\nmcontext->match_limit = limit;\nreturn 0;\n}\n\nPCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION\npcre2_set_depth_limit(pcre2_match_context *mcontext, uint32_t limit)\n{\nmcontext->depth_limit = limit;\nreturn 0;\n}\n\nPCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION\npcre2_set_offset_limit(pcre2_match_context *mcontext, PCRE2_SIZE limit)\n{\nmcontext->offset_limit = limit;\nreturn 0;\n}\n\n/* These functions became obsolete at release 10.30. The first is kept as a\nsynonym for backwards compatibility. The second now does nothing. Exclude both\nfrom coverage reports. */\n\n/* LCOV_EXCL_START */\n\nPCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION\npcre2_set_recursion_limit(pcre2_match_context *mcontext, uint32_t limit)\n{\nreturn pcre2_set_depth_limit(mcontext, limit);\n}\n\nPCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION\npcre2_set_recursion_memory_management(pcre2_match_context *mcontext,\n  void *(*mymalloc)(size_t, void *), void (*myfree)(void *, void *),\n  void *mydata)\n{\n(void)mcontext;\n(void)mymalloc;\n(void)myfree;\n(void)mydata;\nreturn 0;\n}\n\n/* LCOV_EXCL_STOP */\n\n\n/* ------------ Convert context ------------ */\n\nPCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION\npcre2_set_glob_separator(pcre2_convert_context *ccontext, uint32_t separator)\n{\nif (separator != CHAR_SLASH && separator != CHAR_BACKSLASH &&\n    separator != CHAR_DOT) return PCRE2_ERROR_BADDATA;\nccontext->glob_separator = separator;\nreturn 0;\n}\n\nPCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION\npcre2_set_glob_escape(pcre2_convert_context *ccontext, uint32_t escape)\n{\nif (escape > 255 || (escape != 0 && !ispunct(escape)))\n  return PCRE2_ERROR_BADDATA;\nccontext->glob_escape = escape;\nreturn 0;\n}\n\n/* End of pcre2_context.c */\n\n"
  },
  {
    "path": "Libraries/PCRE/pcre2_error.c",
    "content": "/*************************************************\n*      Perl-Compatible Regular Expressions       *\n*************************************************/\n\n/* PCRE is a library of functions to support regular expressions whose syntax\nand semantics are as close as possible to those of the Perl 5 language.\n\n                       Written by Philip Hazel\n     Original API code Copyright (c) 1997-2012 University of Cambridge\n          New API code Copyright (c) 2016-2024 University of Cambridge\n\n-----------------------------------------------------------------------------\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n    * Redistributions of source code must retain the above copyright notice,\n      this list of conditions and the following disclaimer.\n\n    * Redistributions in binary form must reproduce the above copyright\n      notice, this list of conditions and the following disclaimer in the\n      documentation and/or other materials provided with the distribution.\n\n    * Neither the name of the University of Cambridge nor the names of its\n      contributors may be used to endorse or promote products derived from\n      this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\nLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\nCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\nSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\nINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\nCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGE.\n-----------------------------------------------------------------------------\n*/\n\n\n#ifdef HAVE_CONFIG_H\n#include \"config.h\"\n#endif\n\n#include \"pcre2_internal.h\"\n\n#define STRING(a)  # a\n#define XSTRING(s) STRING(s)\n\n/* The texts of compile-time error messages. Compile-time error numbers start\nat COMPILE_ERROR_BASE (100).\n\nThis used to be a table of strings, but in order to reduce the number of\nrelocations needed when a shared library is loaded dynamically, it is now one\nlong string. We cannot use a table of offsets, because the lengths of inserts\nsuch as XSTRING(MAX_NAME_SIZE) are not known. Instead,\npcre2_get_error_message() counts through to the one it wants - this isn't a\nperformance issue because these strings are used only when there is an error.\n\nEach substring ends with \\0 to insert a null character. This includes the final\nsubstring, so that the whole string ends with \\0\\0, which can be detected when\ncounting through. */\n\nstatic const unsigned char compile_error_texts[] =\n  \"no error\\0\"\n  \"\\\\ at end of pattern\\0\"\n  \"\\\\c at end of pattern\\0\"\n  \"unrecognized character follows \\\\\\0\"\n  \"numbers out of order in {} quantifier\\0\"\n  /* 5 */\n  \"number too big in {} quantifier\\0\"\n  \"missing terminating ] for character class\\0\"\n  \"escape sequence is invalid in character class\\0\"\n  \"range out of order in character class\\0\"\n  \"quantifier does not follow a repeatable item\\0\"\n  /* 10 */\n  \"internal error: unexpected repeat\\0\"\n  \"unrecognized character after (? or (?-\\0\"\n  \"POSIX named classes are supported only within a class\\0\"\n  \"POSIX collating elements are not supported\\0\"\n  \"missing closing parenthesis\\0\"\n  /* 15 */\n  \"reference to non-existent subpattern\\0\"\n  \"pattern passed as NULL with non-zero length\\0\"\n  \"unrecognised compile-time option bit(s)\\0\"\n  \"missing ) after (?# comment\\0\"\n  \"parentheses are too deeply nested\\0\"\n  /* 20 */\n  \"regular expression is too large\\0\"\n  \"failed to allocate heap memory\\0\"\n  \"unmatched closing parenthesis\\0\"\n  \"internal error: code overflow\\0\"\n  \"missing closing parenthesis for condition\\0\"\n  /* 25 */\n  \"length of lookbehind assertion is not limited\\0\"\n  \"a relative value of zero is not allowed\\0\"\n  \"conditional subpattern contains more than two branches\\0\"\n  \"atomic assertion expected after (?( or (?(?C)\\0\"\n  \"digit expected after (?+ or (?-\\0\"\n  /* 30 */\n  \"unknown POSIX class name\\0\"\n  \"internal error in pcre2_study(): should not occur\\0\"\n  \"this version of PCRE2 does not have Unicode support\\0\"\n  \"parentheses are too deeply nested (stack check)\\0\"\n  \"character code point value in \\\\x{} or \\\\o{} is too large\\0\"\n  /* 35 */\n  \"lookbehind is too complicated\\0\"\n  \"\\\\C is not allowed in a lookbehind assertion in UTF-\" XSTRING(PCRE2_CODE_UNIT_WIDTH) \" mode\\0\"\n  \"PCRE2 does not support \\\\F, \\\\L, \\\\l, \\\\N{name}, \\\\U, or \\\\u\\0\"\n  \"number after (?C is greater than 255\\0\"\n  \"closing parenthesis for (?C expected\\0\"\n  /* 40 */\n  \"invalid escape sequence in (*VERB) name\\0\"\n  \"unrecognized character after (?P\\0\"\n  \"syntax error in subpattern name (missing terminator?)\\0\"\n  \"two named subpatterns have the same name (PCRE2_DUPNAMES not set)\\0\"\n  \"subpattern name must start with a non-digit\\0\"\n  /* 45 */\n  \"this version of PCRE2 does not have support for \\\\P, \\\\p, or \\\\X\\0\"\n  \"malformed \\\\P or \\\\p sequence\\0\"\n  \"unknown property after \\\\P or \\\\p\\0\"\n  \"subpattern name is too long (maximum \" XSTRING(MAX_NAME_SIZE) \" code units)\\0\"\n  \"too many named subpatterns (maximum \" XSTRING(MAX_NAME_COUNT) \")\\0\"\n  /* 50 */\n  \"invalid range in character class\\0\"\n  \"octal value is greater than \\\\377 in 8-bit non-UTF-8 mode\\0\"\n  \"internal error: overran compiling workspace\\0\"\n  \"internal error: previously-checked referenced subpattern not found\\0\"\n  \"DEFINE subpattern contains more than one branch\\0\"\n  /* 55 */\n  \"missing opening brace after \\\\o\\0\"\n  \"internal error: unknown newline setting\\0\"\n  \"\\\\g is not followed by a braced, angle-bracketed, or quoted name/number or by a plain number\\0\"\n  \"(?R (recursive pattern call) must be followed by a closing parenthesis\\0\"\n  /* \"an argument is not allowed for (*ACCEPT), (*FAIL), or (*COMMIT)\\0\" */\n  \"obsolete error (should not occur)\\0\"  /* Was the above */\n  /* 60 */\n  \"(*VERB) not recognized or malformed\\0\"\n  \"subpattern number is too big\\0\"\n  \"subpattern name expected\\0\"\n  \"internal error: parsed pattern overflow\\0\"\n  \"non-octal character in \\\\o{} (closing brace missing?)\\0\"\n  /* 65 */\n  \"different names for subpatterns of the same number are not allowed\\0\"\n  \"(*MARK) must have an argument\\0\"\n  \"non-hex character in \\\\x{} (closing brace missing?)\\0\"\n#ifndef EBCDIC\n  \"\\\\c must be followed by a printable ASCII character\\0\"\n#else\n  \"\\\\c must be followed by a letter or one of [\\\\]^_?\\0\"\n#endif\n  \"\\\\k is not followed by a braced, angle-bracketed, or quoted name\\0\"\n  /* 70 */\n  \"internal error: unknown meta code in check_lookbehinds()\\0\"\n  \"\\\\N is not supported in a class\\0\"\n  \"callout string is too long\\0\"\n  \"disallowed Unicode code point (>= 0xd800 && <= 0xdfff)\\0\"\n  \"using UTF is disabled by the application\\0\"\n  /* 75 */\n  \"using UCP is disabled by the application\\0\"\n  \"name is too long in (*MARK), (*PRUNE), (*SKIP), or (*THEN)\\0\"\n  \"character code point value in \\\\u.... sequence is too large\\0\"\n  \"digits missing after \\\\x or in \\\\x{} or \\\\o{} or \\\\N{U+}\\0\"\n  \"syntax error or number too big in (?(VERSION condition\\0\"\n  /* 80 */\n  \"internal error: unknown opcode in auto_possessify()\\0\"\n  \"missing terminating delimiter for callout with string argument\\0\"\n  \"unrecognized string delimiter follows (?C\\0\"\n  \"using \\\\C is disabled by the application\\0\"\n  \"(?| and/or (?J: or (?x: parentheses are too deeply nested\\0\"\n  /* 85 */\n  \"using \\\\C is disabled in this PCRE2 library\\0\"\n  \"regular expression is too complicated\\0\"\n  \"lookbehind assertion is too long\\0\"\n  \"pattern string is longer than the limit set by the application\\0\"\n  \"internal error: unknown code in parsed pattern\\0\"\n  /* 90 */\n  \"internal error: bad code value in parsed_skip()\\0\"\n  \"PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES is not allowed in UTF-16 mode\\0\"\n  \"invalid option bits with PCRE2_LITERAL\\0\"\n  \"\\\\N{U+dddd} is supported only in Unicode (UTF) mode\\0\"\n  \"invalid hyphen in option setting\\0\"\n  /* 95 */\n  \"(*alpha_assertion) not recognized\\0\"\n  \"script runs require Unicode support, which this version of PCRE2 does not have\\0\"\n  \"too many capturing groups (maximum 65535)\\0\"\n  \"octal digit missing after \\\\0 (PCRE2_EXTRA_NO_BS0 is set)\\0\"\n  \"\\\\K is not allowed in lookarounds (but see PCRE2_EXTRA_ALLOW_LOOKAROUND_BSK)\\0\"\n  /* 100 */\n  \"branch too long in variable-length lookbehind assertion\\0\"\n  \"compiled pattern would be longer than the limit set by the application\\0\"\n  \"octal value given by \\\\ddd is greater than \\\\377 (forbidden by PCRE2_EXTRA_PYTHON_OCTAL)\\0\"\n  \"using callouts is disabled by the application\\0\"\n  \"PCRE2_EXTRA_TURKISH_CASING require Unicode (UTF or UCP) mode\\0\"\n  /* 105 */\n  \"PCRE2_EXTRA_TURKISH_CASING requires UTF in 8-bit mode\\0\"\n  \"PCRE2_EXTRA_TURKISH_CASING and PCRE2_EXTRA_CASELESS_RESTRICT are not compatible\\0\"\n  \"extended character class nesting is too deep\\0\"\n  \"invalid operator in extended character class\\0\"\n  \"unexpected operator in extended character class (no preceding operand)\\0\"\n  /* 110 */\n  \"expected operand after operator in extended character class\\0\"\n  \"square brackets needed to clarify operator precedence in extended character class\\0\"\n  \"missing terminating ] for extended character class (note '[' must be escaped under PCRE2_ALT_EXTENDED_CLASS)\\0\"\n  \"unexpected expression in extended character class (no preceding operator)\\0\"\n  \"empty expression in extended character class\\0\"\n  /* 115 */\n  \"terminating ] with no following closing parenthesis in (?[...]\\0\"\n  \"unexpected character in (?[...]) extended character class\\0\"\n  ;\n\n/* Match-time and UTF error texts are in the same format. */\n\nstatic const unsigned char match_error_texts[] =\n  \"no error\\0\"\n  \"no match\\0\"\n  \"partial match\\0\"\n  \"UTF-8 error: 1 byte missing at end\\0\"\n  \"UTF-8 error: 2 bytes missing at end\\0\"\n  /* 5 */\n  \"UTF-8 error: 3 bytes missing at end\\0\"\n  \"UTF-8 error: 4 bytes missing at end\\0\"\n  \"UTF-8 error: 5 bytes missing at end\\0\"\n  \"UTF-8 error: byte 2 top bits not 0x80\\0\"\n  \"UTF-8 error: byte 3 top bits not 0x80\\0\"\n  /* 10 */\n  \"UTF-8 error: byte 4 top bits not 0x80\\0\"\n  \"UTF-8 error: byte 5 top bits not 0x80\\0\"\n  \"UTF-8 error: byte 6 top bits not 0x80\\0\"\n  \"UTF-8 error: 5-byte character is not allowed (RFC 3629)\\0\"\n  \"UTF-8 error: 6-byte character is not allowed (RFC 3629)\\0\"\n  /* 15 */\n  \"UTF-8 error: code points greater than 0x10ffff are not defined\\0\"\n  \"UTF-8 error: code points 0xd800-0xdfff are not defined\\0\"\n  \"UTF-8 error: overlong 2-byte sequence\\0\"\n  \"UTF-8 error: overlong 3-byte sequence\\0\"\n  \"UTF-8 error: overlong 4-byte sequence\\0\"\n  /* 20 */\n  \"UTF-8 error: overlong 5-byte sequence\\0\"\n  \"UTF-8 error: overlong 6-byte sequence\\0\"\n  \"UTF-8 error: isolated byte with 0x80 bit set\\0\"\n  \"UTF-8 error: illegal byte (0xfe or 0xff)\\0\"\n  \"UTF-16 error: missing low surrogate at end\\0\"\n  /* 25 */\n  \"UTF-16 error: invalid low surrogate\\0\"\n  \"UTF-16 error: isolated low surrogate\\0\"\n  \"UTF-32 error: code points 0xd800-0xdfff are not defined\\0\"\n  \"UTF-32 error: code points greater than 0x10ffff are not defined\\0\"\n  \"bad data value\\0\"\n  /* 30 */\n  \"patterns do not all use the same character tables\\0\"\n  \"magic number missing\\0\"\n  \"pattern compiled in wrong mode: 8/16/32-bit error\\0\"\n  \"bad offset value\\0\"\n  \"bad option value\\0\"\n  /* 35 */\n  \"invalid replacement string\\0\"\n  \"bad offset into UTF string\\0\"\n  \"callout error code\\0\"              /* Never returned by PCRE2 itself */\n  \"invalid data in workspace for DFA restart\\0\"\n  \"too much recursion for DFA matching\\0\"\n  /* 40 */\n  \"backreference condition or recursion test is not supported for DFA matching\\0\"\n  \"function is not supported for DFA matching\\0\"\n  \"pattern contains an item that is not supported for DFA matching\\0\"\n  \"workspace size exceeded in DFA matching\\0\"\n  \"internal error - pattern overwritten?\\0\"\n  /* 45 */\n  \"bad JIT option\\0\"\n  \"JIT stack limit reached\\0\"\n  \"match limit exceeded\\0\"\n  \"no more memory\\0\"\n  \"unknown substring\\0\"\n  /* 50 */\n  \"non-unique substring name\\0\"\n  \"NULL argument passed with non-zero length\\0\"\n  \"nested recursion at the same subject position\\0\"\n  \"matching depth limit exceeded\\0\"\n  \"requested value is not available\\0\"\n  /* 55 */\n  \"requested value is not set\\0\"\n  \"offset limit set without PCRE2_USE_OFFSET_LIMIT\\0\"\n  \"bad escape sequence in replacement string\\0\"\n  \"expected closing curly bracket in replacement string\\0\"\n  \"bad substitution in replacement string\\0\"\n  /* 60 */\n  \"match with end before start or start moved backwards is not supported\\0\"\n  \"too many replacements (more than INT_MAX)\\0\"\n  \"bad serialized data\\0\"\n  \"heap limit exceeded\\0\"\n  \"invalid syntax\\0\"\n  /* 65 */\n  \"internal error - duplicate substitution match\\0\"\n  \"PCRE2_MATCH_INVALID_UTF is not supported for DFA matching\\0\"\n  \"INTERNAL ERROR: invalid substring offset\\0\"\n  \"feature is not supported by the JIT compiler\\0\"\n  \"error performing replacement case transformation\\0\"\n  /* 70 */\n  \"replacement too large (longer than PCRE2_SIZE)\\0\"\n  ;\n\n\n/*************************************************\n*            Return error message                *\n*************************************************/\n\n/* This function copies an error message into a buffer whose units are of an\nappropriate width. Error numbers are positive for compile-time errors, and\nnegative for match-time errors (except for UTF errors), but the numbers are all\ndistinct.\n\nArguments:\n  enumber       error number\n  buffer        where to put the message (zero terminated)\n  size          size of the buffer in code units\n\nReturns:        length of message if all is well\n                negative on error\n*/\n\nPCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION\npcre2_get_error_message(int enumber, PCRE2_UCHAR *buffer, PCRE2_SIZE size)\n{\nconst unsigned char *message;\nPCRE2_SIZE i;\nint n;\n\nif (size == 0) return PCRE2_ERROR_NOMEMORY;\n\nif (enumber >= COMPILE_ERROR_BASE)  /* Compile error */\n  {\n  message = compile_error_texts;\n  n = enumber - COMPILE_ERROR_BASE;\n  }\nelse if (enumber < 0)               /* Match or UTF error */\n  {\n  message = match_error_texts;\n  n = -enumber;\n  }\nelse                                /* Invalid error number */\n  {\n  message = (const unsigned char *)\"\\0\";  /* Empty message list */\n  n = 1;\n  }\n\nfor (; n > 0; n--)\n  {\n  while (*message++ != CHAR_NUL) {};\n  if (*message == CHAR_NUL) return PCRE2_ERROR_BADDATA;\n  }\n\nfor (i = 0; *message != 0; i++)\n  {\n  if (i >= size - 1)\n    {\n    buffer[i] = 0;     /* Terminate partial message */\n    return PCRE2_ERROR_NOMEMORY;\n    }\n  buffer[i] = *message++;\n  }\n\nbuffer[i] = 0;\nreturn (int)i;\n}\n\n/* End of pcre2_error.c */\n"
  },
  {
    "path": "Libraries/PCRE/pcre2_extuni.c",
    "content": "/*************************************************\n*      Perl-Compatible Regular Expressions       *\n*************************************************/\n\n/* PCRE is a library of functions to support regular expressions whose syntax\nand semantics are as close as possible to those of the Perl 5 language.\n\n                       Written by Philip Hazel\n     Original API code Copyright (c) 1997-2012 University of Cambridge\n          New API code Copyright (c) 2016-2024 University of Cambridge\n\n-----------------------------------------------------------------------------\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n    * Redistributions of source code must retain the above copyright notice,\n      this list of conditions and the following disclaimer.\n\n    * Redistributions in binary form must reproduce the above copyright\n      notice, this list of conditions and the following disclaimer in the\n      documentation and/or other materials provided with the distribution.\n\n    * Neither the name of the University of Cambridge nor the names of its\n      contributors may be used to endorse or promote products derived from\n      this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\nLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\nCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\nSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\nINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\nCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGE.\n-----------------------------------------------------------------------------\n*/\n\n/* This module contains an internal function that is used to match a Unicode\nextended grapheme sequence. It is used by both pcre2_match() and\npcre2_dfa_match(). However, it is called only when Unicode support is being\ncompiled. Nevertheless, we provide a dummy function when there is no Unicode\nsupport, because some compilers do not like functionless source files. */\n\n\n#ifdef HAVE_CONFIG_H\n#include \"config.h\"\n#endif\n\n\n#include \"pcre2_internal.h\"\n\n\n/* Dummy function */\n\n#ifndef SUPPORT_UNICODE\nPCRE2_SPTR\nPRIV(extuni)(uint32_t c, PCRE2_SPTR eptr, PCRE2_SPTR start_subject,\n  PCRE2_SPTR end_subject, BOOL utf, int *xcount)\n{\n(void)c;\n(void)eptr;\n(void)start_subject;\n(void)end_subject;\n(void)utf;\n(void)xcount;\nreturn NULL;\n}\n#else\n\n\n/*************************************************\n*      Match an extended grapheme sequence       *\n*************************************************/\n\n/* NOTE: The logic contained in this function is replicated in three special-\npurpose functions in the pcre2_jit_compile.c module. If the logic below is\nchanged, they must be kept in step so that the interpreter and the JIT have the\nsame behaviour.\n\nArguments:\n  c              the first character\n  eptr           pointer to next character\n  start_subject  pointer to start of subject\n  end_subject    pointer to end of subject\n  utf            TRUE if in UTF mode\n  xcount         pointer to count of additional characters,\n                   or NULL if count not needed\n\nReturns:         pointer after the end of the sequence\n*/\n\nPCRE2_SPTR\nPRIV(extuni)(uint32_t c, PCRE2_SPTR eptr, PCRE2_SPTR start_subject,\n  PCRE2_SPTR end_subject, BOOL utf, int *xcount)\n{\nBOOL was_ep_ZWJ = FALSE;\nint lgb = UCD_GRAPHBREAK(c);\n\nwhile (eptr < end_subject)\n  {\n  int rgb;\n  int len = 1;\n  if (!utf) c = *eptr; else { GETCHARLEN(c, eptr, len); }\n  rgb = UCD_GRAPHBREAK(c);\n  if ((PRIV(ucp_gbtable)[lgb] & (1u << rgb)) == 0) break;\n\n  /* ZWJ followed by Extended Pictographic is allowed only if the ZWJ was\n  preceded by Extended Pictographic. */\n\n  if (lgb == ucp_gbZWJ && rgb == ucp_gbExtended_Pictographic && !was_ep_ZWJ)\n    break;\n\n  /* Not breaking between Regional Indicators is allowed only if there\n  are an even number of preceding RIs. */\n\n  if (lgb == ucp_gbRegional_Indicator && rgb == ucp_gbRegional_Indicator)\n    {\n    int ricount = 0;\n    PCRE2_SPTR bptr = eptr - 1;\n    if (utf) BACKCHAR(bptr);\n\n    /* bptr is pointing to the left-hand character */\n\n    while (bptr > start_subject)\n      {\n      bptr--;\n      if (utf)\n        {\n        BACKCHAR(bptr);\n        GETCHAR(c, bptr);\n        }\n      else\n      c = *bptr;\n      if (UCD_GRAPHBREAK(c) != ucp_gbRegional_Indicator) break;\n      ricount++;\n      }\n    if ((ricount & 1) != 0) break;  /* Grapheme break required */\n    }\n\n  /* Set a flag when ZWJ follows Extended Pictographic (with optional Extend in\n  between; see next statement). */\n\n  was_ep_ZWJ = (lgb == ucp_gbExtended_Pictographic && rgb == ucp_gbZWJ);\n\n  /* If Extend follows Extended_Pictographic, do not update lgb; this allows\n  any number of them before a following ZWJ. */\n\n  if (rgb != ucp_gbExtend || lgb != ucp_gbExtended_Pictographic) lgb = rgb;\n\n  eptr += len;\n  if (xcount != NULL) *xcount += 1;\n  }\n\nreturn eptr;\n}\n\n#endif  /* SUPPORT_UNICODE */\n\n/* End of pcre2_extuni.c */\n"
  },
  {
    "path": "Libraries/PCRE/pcre2_find_bracket.c",
    "content": "/*************************************************\n*      Perl-Compatible Regular Expressions       *\n*************************************************/\n\n/* PCRE is a library of functions to support regular expressions whose syntax\nand semantics are as close as possible to those of the Perl 5 language.\n\n                       Written by Philip Hazel\n     Original API code Copyright (c) 1997-2012 University of Cambridge\n          New API code Copyright (c) 2016-2024 University of Cambridge\n\n-----------------------------------------------------------------------------\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n    * Redistributions of source code must retain the above copyright notice,\n      this list of conditions and the following disclaimer.\n\n    * Redistributions in binary form must reproduce the above copyright\n      notice, this list of conditions and the following disclaimer in the\n      documentation and/or other materials provided with the distribution.\n\n    * Neither the name of the University of Cambridge nor the names of its\n      contributors may be used to endorse or promote products derived from\n      this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\nLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\nCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\nSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\nINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\nCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGE.\n-----------------------------------------------------------------------------\n*/\n\n\n/* This module contains a single function that scans through a compiled pattern\nuntil it finds a capturing bracket with the given number, or, if the number is\nnegative, an instance of OP_REVERSE or OP_VREVERSE for a lookbehind. The\nfunction is called from pcre2_compile.c and also from pcre2_study.c when\nfinding the minimum matching length. */\n\n\n#ifdef HAVE_CONFIG_H\n#include \"config.h\"\n#endif\n\n#include \"pcre2_internal.h\"\n\n\n/*************************************************\n*    Scan compiled regex for specific bracket    *\n*************************************************/\n\n/*\nArguments:\n  code        points to start of expression\n  utf         TRUE in UTF mode\n  number      the required bracket number or negative to find a lookbehind\n\nReturns:      pointer to the opcode for the bracket, or NULL if not found\n*/\n\nPCRE2_SPTR\nPRIV(find_bracket)(PCRE2_SPTR code, BOOL utf, int number)\n{\nfor (;;)\n  {\n  PCRE2_UCHAR c = *code;\n\n  if (c == OP_END) return NULL;\n\n  /* XCLASS is used for classes that cannot be represented just by a bit map.\n  This includes negated single high-valued characters. ECLASS is used for\n  classes that use set operations internally. CALLOUT_STR is used for\n  callouts with string arguments. In each case the length in the table is\n  zero; the actual length is stored in the compiled code. */\n\n  if (c == OP_XCLASS || c == OP_ECLASS) code += GET(code, 1);\n  else if (c == OP_CALLOUT_STR) code += GET(code, 1 + 2*LINK_SIZE);\n\n  /* Handle lookbehind */\n\n  else if (c == OP_REVERSE || c == OP_VREVERSE)\n    {\n    if (number < 0) return code;\n    code += PRIV(OP_lengths)[c];\n    }\n\n  /* Handle capturing bracket */\n\n  else if (c == OP_CBRA || c == OP_SCBRA ||\n           c == OP_CBRAPOS || c == OP_SCBRAPOS)\n    {\n    int n = (int)GET2(code, 1+LINK_SIZE);\n    if (n == number) return code;\n    code += PRIV(OP_lengths)[c];\n    }\n\n  /* Otherwise, we can get the item's length from the table, except that for\n  repeated character types, we have to test for \\p and \\P, which have an extra\n  two bytes of parameters, and for MARK/PRUNE/SKIP/THEN with an argument, we\n  must add in its length. */\n\n  else\n    {\n    switch(c)\n      {\n      case OP_TYPESTAR:\n      case OP_TYPEMINSTAR:\n      case OP_TYPEPLUS:\n      case OP_TYPEMINPLUS:\n      case OP_TYPEQUERY:\n      case OP_TYPEMINQUERY:\n      case OP_TYPEPOSSTAR:\n      case OP_TYPEPOSPLUS:\n      case OP_TYPEPOSQUERY:\n      if (code[1] == OP_PROP || code[1] == OP_NOTPROP) code += 2;\n      break;\n\n      case OP_TYPEUPTO:\n      case OP_TYPEMINUPTO:\n      case OP_TYPEEXACT:\n      case OP_TYPEPOSUPTO:\n      if (code[1 + IMM2_SIZE] == OP_PROP || code[1 + IMM2_SIZE] == OP_NOTPROP)\n        code += 2;\n      break;\n\n      case OP_MARK:\n      case OP_COMMIT_ARG:\n      case OP_PRUNE_ARG:\n      case OP_SKIP_ARG:\n      case OP_THEN_ARG:\n      code += code[1];\n      break;\n      }\n\n    /* Add in the fixed length from the table */\n\n    code += PRIV(OP_lengths)[c];\n\n  /* In UTF-8 and UTF-16 modes, opcodes that are followed by a character may be\n  followed by a multi-byte character. The length in the table is a minimum, so\n  we have to arrange to skip the extra bytes. */\n\n#ifdef MAYBE_UTF_MULTI\n    if (utf) switch(c)\n      {\n      case OP_CHAR:\n      case OP_CHARI:\n      case OP_NOT:\n      case OP_NOTI:\n      case OP_EXACT:\n      case OP_EXACTI:\n      case OP_NOTEXACT:\n      case OP_NOTEXACTI:\n      case OP_UPTO:\n      case OP_UPTOI:\n      case OP_NOTUPTO:\n      case OP_NOTUPTOI:\n      case OP_MINUPTO:\n      case OP_MINUPTOI:\n      case OP_NOTMINUPTO:\n      case OP_NOTMINUPTOI:\n      case OP_POSUPTO:\n      case OP_POSUPTOI:\n      case OP_NOTPOSUPTO:\n      case OP_NOTPOSUPTOI:\n      case OP_STAR:\n      case OP_STARI:\n      case OP_NOTSTAR:\n      case OP_NOTSTARI:\n      case OP_MINSTAR:\n      case OP_MINSTARI:\n      case OP_NOTMINSTAR:\n      case OP_NOTMINSTARI:\n      case OP_POSSTAR:\n      case OP_POSSTARI:\n      case OP_NOTPOSSTAR:\n      case OP_NOTPOSSTARI:\n      case OP_PLUS:\n      case OP_PLUSI:\n      case OP_NOTPLUS:\n      case OP_NOTPLUSI:\n      case OP_MINPLUS:\n      case OP_MINPLUSI:\n      case OP_NOTMINPLUS:\n      case OP_NOTMINPLUSI:\n      case OP_POSPLUS:\n      case OP_POSPLUSI:\n      case OP_NOTPOSPLUS:\n      case OP_NOTPOSPLUSI:\n      case OP_QUERY:\n      case OP_QUERYI:\n      case OP_NOTQUERY:\n      case OP_NOTQUERYI:\n      case OP_MINQUERY:\n      case OP_MINQUERYI:\n      case OP_NOTMINQUERY:\n      case OP_NOTMINQUERYI:\n      case OP_POSQUERY:\n      case OP_POSQUERYI:\n      case OP_NOTPOSQUERY:\n      case OP_NOTPOSQUERYI:\n      if (HAS_EXTRALEN(code[-1])) code += GET_EXTRALEN(code[-1]);\n      break;\n      }\n#else\n    (void)(utf);  /* Keep compiler happy by referencing function argument */\n#endif  /* MAYBE_UTF_MULTI */\n    }\n  }\n}\n\n/* End of pcre2_find_bracket.c */\n"
  },
  {
    "path": "Libraries/PCRE/pcre2_internal.h",
    "content": "/*************************************************\n*      Perl-Compatible Regular Expressions       *\n*************************************************/\n\n/* PCRE2 is a library of functions to support regular expressions whose syntax\nand semantics are as close as possible to those of the Perl 5 language.\n\n                       Written by Philip Hazel\n     Original API code Copyright (c) 1997-2012 University of Cambridge\n          New API code Copyright (c) 2016-2024 University of Cambridge\n\n-----------------------------------------------------------------------------\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n    * Redistributions of source code must retain the above copyright notice,\n      this list of conditions and the following disclaimer.\n\n    * Redistributions in binary form must reproduce the above copyright\n      notice, this list of conditions and the following disclaimer in the\n      documentation and/or other materials provided with the distribution.\n\n    * Neither the name of the University of Cambridge nor the names of its\n      contributors may be used to endorse or promote products derived from\n      this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\nLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\nCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\nSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\nINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\nCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGE.\n-----------------------------------------------------------------------------\n*/\n\n#ifndef PCRE2_INTERNAL_H_IDEMPOTENT_GUARD\n#define PCRE2_INTERNAL_H_IDEMPOTENT_GUARD\n\n/* We do not support both EBCDIC and Unicode at the same time. The \"configure\"\nscript prevents both being selected, but not everybody uses \"configure\". EBCDIC\nis only supported for the 8-bit library, but the check for this has to be later\nin this file, because the first part is not width-dependent, and is included by\npcre2test.c with CODE_UNIT_WIDTH == 0. */\n\n#if defined EBCDIC && defined SUPPORT_UNICODE\n#error The use of both EBCDIC and SUPPORT_UNICODE is not supported.\n#endif\n\n/* When compiling one of the libraries, the value of PCRE2_CODE_UNIT_WIDTH must\nbe 8, 16, or 32. AutoTools and CMake ensure that this is always the case, but\nother other building methods may not, so here is a check. It is cut out when\nbuilding pcre2test, bcause that sets the value to zero. No other source should\nbe including this file. There is no explicit way of forcing a compile to be\nabandoned, but trying to include a non-existent file seems cleanest. Otherwise\nthere will be many irrelevant consequential errors. */\n\n#if (!defined PCRE2_BUILDING_PCRE2TEST && !defined PCRE2_DFTABLES) && \\\n  (!defined PCRE2_CODE_UNIT_WIDTH ||     \\\n    (PCRE2_CODE_UNIT_WIDTH != 8 &&       \\\n     PCRE2_CODE_UNIT_WIDTH != 16 &&      \\\n     PCRE2_CODE_UNIT_WIDTH != 32))\n#error PCRE2_CODE_UNIT_WIDTH must be defined as 8, 16, or 32.\n#include <AbandonCompile>\n#endif\n\n\n/* Standard C headers */\n\n#include <ctype.h>\n#include <limits.h>\n#include <stddef.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n/* Macros to make boolean values more obvious. The #ifndef is to pacify\ncompiler warnings in environments where these macros are defined elsewhere.\nUnfortunately, there is no way to do the same for the typedef. */\n\ntypedef int BOOL;\n#ifndef FALSE\n#define FALSE   0\n#define TRUE    1\n#endif\n\n/* Helper macro for static (compile-time) assertions. Can be used inside\nfunctions, or at the top-level of a file. */\n#define STATIC_ASSERT_JOIN(a,b) a ## b\n#define STATIC_ASSERT(cond, msg) \\\n  typedef int STATIC_ASSERT_JOIN(static_assertion_,msg)[(cond)?1:-1]\n\n/* Valgrind (memcheck) support */\n\n#ifdef SUPPORT_VALGRIND\n#include <valgrind/memcheck.h>\n#endif\n\n/* -ftrivial-auto-var-init support supports initializing all local variables\nto avoid some classes of bug, but this can cause an unacceptable slowdown\nfor large on-stack arrays in hot functions. This macro lets us annotate\nsuch arrays. */\n\n#ifdef HAVE_ATTRIBUTE_UNINITIALIZED\n#define PCRE2_KEEP_UNINITIALIZED __attribute__((uninitialized))\n#else\n#define PCRE2_KEEP_UNINITIALIZED\n#endif\n\n/* Older versions of MSVC lack snprintf(). This define allows for\nwarning/error-free compilation and testing with MSVC compilers back to at least\nMSVC 10/2010. Except for VC6 (which is missing some fundamentals and fails). */\n\n#if defined(_MSC_VER) && (_MSC_VER < 1900)\n#define snprintf _snprintf\n#endif\n\n/* When compiling a DLL for Windows, the exported symbols have to be declared\nusing some MS magic. I found some useful information on this web page:\nhttp://msdn2.microsoft.com/en-us/library/y4h7bcy6(VS.80).aspx. According to the\ninformation there, using __declspec(dllexport) without \"extern\" we have a\ndefinition; with \"extern\" we have a declaration. The settings here override the\nsetting in pcre2.h (which is included below); it defines only PCRE2_EXP_DECL,\nwhich is all that is needed for applications (they just import the symbols). We\nuse:\n\n  PCRE2_EXP_DECL    for declarations\n  PCRE2_EXP_DEFN    for definitions\n\nThe reason for wrapping this in #ifndef PCRE2_EXP_DECL is so that pcre2test,\nwhich is an application, but needs to import this file in order to \"peek\" at\ninternals, can #include pcre2.h first to get an application's-eye view.\n\nIn principle, people compiling for non-Windows, non-Unix-like (i.e. uncommon,\nspecial-purpose environments) might want to stick other stuff in front of\nexported symbols. That's why, in the non-Windows case, we set PCRE2_EXP_DEFN\nonly if it is not already set. */\n\n#ifndef PCRE2_EXP_DECL\n#  ifdef _WIN32\n#    ifndef PCRE2_STATIC\n#      define PCRE2_EXP_DECL\t\textern __declspec(dllexport)\n#      define PCRE2_EXP_DEFN\t\t__declspec(dllexport)\n#    else\n#      define PCRE2_EXP_DECL\t\textern PCRE2_EXPORT\n#      define PCRE2_EXP_DEFN\n#    endif\n#  else\n#    ifdef __cplusplus\n#      define PCRE2_EXP_DECL\t\textern \"C\" PCRE2_EXPORT\n#    else\n#      define PCRE2_EXP_DECL\t\textern PCRE2_EXPORT\n#    endif\n#    ifndef PCRE2_EXP_DEFN\n#      define PCRE2_EXP_DEFN\t\tPCRE2_EXP_DECL\n#    endif\n#  endif\n#endif\n\n/* Include the public PCRE2 header and the definitions of UCP character\nproperty values. This must follow the setting of PCRE2_EXP_DECL above. */\n\n#include \"pcre2.h\"\n#include \"pcre2_ucp.h\"\n\n/* When PCRE2 is compiled as a C++ library, the subject pointer can be replaced\nwith a custom type. This makes it possible, for example, to allow pcre2_match()\nto process subject strings that are discontinuous by using a smart pointer\nclass. It must always be possible to inspect all of the subject string in\npcre2_match() because of the way it backtracks. */\n\n/* WARNING: This is as yet untested for PCRE2. */\n\n#ifdef CUSTOM_SUBJECT_PTR\n#undef PCRE2_SPTR\n#define PCRE2_SPTR CUSTOM_SUBJECT_PTR\n#endif\n\n/* When checking for integer overflow, we need to handle large integers.\nIf a 64-bit integer type is available, we can use that.\nOtherwise we have to cast to double, which of course requires floating point\narithmetic. Handle this by defining a macro for the appropriate type. */\n\n#if defined INT64_MAX || defined int64_t\n#define INT64_OR_DOUBLE int64_t\n#else\n#define INT64_OR_DOUBLE double\n#endif\n\n/* External (in the C sense) functions and tables that are private to the\nlibraries are always referenced using the PRIV macro. This makes it possible\nfor pcre2test.c to include some of the source files from the libraries using a\ndifferent PRIV definition to avoid name clashes. It also makes it clear in the\ncode that a non-static object is being referenced. */\n\n#ifndef PRIV\n#define PRIV(name) _pcre2_##name\n#endif\n\n/* When compiling for use with the Virtual Pascal compiler, these functions\nneed to have their names changed. PCRE2 must be compiled with the -DVPCOMPAT\noption on the command line. */\n\n#ifdef VPCOMPAT\n#define strlen(s)        _strlen(s)\n#define strncmp(s1,s2,m) _strncmp(s1,s2,m)\n#define memcmp(s,c,n)    _memcmp(s,c,n)\n#define memcpy(d,s,n)    _memcpy(d,s,n)\n#define memmove(d,s,n)   _memmove(d,s,n)\n#define memset(s,c,n)    _memset(s,c,n)\n#else  /* VPCOMPAT */\n\n/* Otherwise, to cope with SunOS4 and other systems that lack memmove(), define\na macro that calls an emulating function. */\n\n#ifndef HAVE_MEMMOVE\n#undef  memmove          /* Some systems may have a macro */\n#define memmove(a, b, c) PRIV(memmove)(a, b, c)\n#endif   /* not HAVE_MEMMOVE */\n#endif   /* not VPCOMPAT */\n\n/* This is an unsigned int value that no UTF character can ever have, as\nUnicode doesn't go beyond 0x0010ffff. */\n\n#define NOTACHAR 0xffffffff\n\n/* This is the largest valid UTF/Unicode code point. */\n\n#define MAX_UTF_CODE_POINT 0x10ffff\n\n/* Compile-time positive error numbers (all except UTF errors, which are\nnegative) start at this value. It should probably never be changed, in case\nsome application is checking for specific numbers. There is a copy of this\n#define in pcre2posix.c (which now no longer includes this file). Ideally, a\nway of having a single definition should be found, but as the number is\nunlikely to change, this is not a pressing issue. The original reason for\nhaving a base other than 0 was to keep the absolute values of compile-time and\nrun-time error numbers numerically different, but in the event the code does\nnot rely on this. */\n\n#define COMPILE_ERROR_BASE 100\n\n/* The initial frames vector for remembering pcre2_match() backtracking points\nis allocated on the heap, of this size (bytes) or ten times the frame size if\nlarger, unless the heap limit is smaller. Typical frame sizes are a few hundred\nbytes (it depends on the number of capturing parentheses) so 20KiB handles\nquite a few frames. A larger vector on the heap is obtained for matches that\nneed more frames, subject to the heap limit. */\n\n#define START_FRAMES_SIZE 20480\n\n/* For DFA matching, an initial internal workspace vector is allocated on the\nstack. The heap is used only if this turns out to be too small. */\n\n#define DFA_START_RWS_SIZE 30720\n\n/* Define the default BSR convention. */\n\n#ifdef BSR_ANYCRLF\n#define BSR_DEFAULT PCRE2_BSR_ANYCRLF\n#else\n#define BSR_DEFAULT PCRE2_BSR_UNICODE\n#endif\n\n\n/* ---------------- Basic UTF-8 macros ---------------- */\n\n/* These UTF-8 macros are always defined because they are used in pcre2test for\nhandling wide characters in 16-bit and 32-bit modes, even if an 8-bit library\nis not supported. */\n\n/* Tests whether a UTF-8 code point needs extra bytes to decode. */\n\n#define HASUTF8EXTRALEN(c) ((c) >= 0xc0)\n\n/* The following macros were originally written in the form of loops that used\ndata from the tables whose names start with PRIV(utf8_table). They were\nrewritten by a user so as not to use loops, because in some environments this\ngives a significant performance advantage, and it seems never to do any harm.\n*/\n\n/* Base macro to pick up the remaining bytes of a UTF-8 character, not\nadvancing the pointer. */\n\n#define GETUTF8(c, eptr) \\\n    { \\\n    if ((c & 0x20u) == 0) \\\n      c = ((c & 0x1fu) << 6) | (eptr[1] & 0x3fu); \\\n    else if ((c & 0x10u) == 0) \\\n      c = ((c & 0x0fu) << 12) | ((eptr[1] & 0x3fu) << 6) | (eptr[2] & 0x3fu); \\\n    else if ((c & 0x08u) == 0) \\\n      c = ((c & 0x07u) << 18) | ((eptr[1] & 0x3fu) << 12) | \\\n      ((eptr[2] & 0x3fu) << 6) | (eptr[3] & 0x3fu); \\\n    else if ((c & 0x04u) == 0) \\\n      c = ((c & 0x03u) << 24) | ((eptr[1] & 0x3fu) << 18) | \\\n          ((eptr[2] & 0x3fu) << 12) | ((eptr[3] & 0x3fu) << 6) | \\\n          (eptr[4] & 0x3fu); \\\n    else \\\n      c = ((c & 0x01u) << 30) | ((eptr[1] & 0x3fu) << 24) | \\\n          ((eptr[2] & 0x3fu) << 18) | ((eptr[3] & 0x3fu) << 12) | \\\n          ((eptr[4] & 0x3fu) << 6) | (eptr[5] & 0x3fu); \\\n    }\n\n/* Base macro to pick up the remaining bytes of a UTF-8 character, advancing\nthe pointer. */\n\n#define GETUTF8INC(c, eptr) \\\n    { \\\n    if ((c & 0x20u) == 0) \\\n      c = ((c & 0x1fu) << 6) | (*eptr++ & 0x3fu); \\\n    else if ((c & 0x10u) == 0) \\\n      { \\\n      c = ((c & 0x0fu) << 12) | ((*eptr & 0x3fu) << 6) | (eptr[1] & 0x3fu); \\\n      eptr += 2; \\\n      } \\\n    else if ((c & 0x08u) == 0) \\\n      { \\\n      c = ((c & 0x07u) << 18) | ((*eptr & 0x3fu) << 12) | \\\n          ((eptr[1] & 0x3fu) << 6) | (eptr[2] & 0x3fu); \\\n      eptr += 3; \\\n      } \\\n    else if ((c & 0x04u) == 0) \\\n      { \\\n      c = ((c & 0x03u) << 24) | ((*eptr & 0x3fu) << 18) | \\\n          ((eptr[1] & 0x3fu) << 12) | ((eptr[2] & 0x3fu) << 6) | \\\n          (eptr[3] & 0x3fu); \\\n      eptr += 4; \\\n      } \\\n    else \\\n      { \\\n      c = ((c & 0x01u) << 30) | ((*eptr & 0x3fu) << 24) | \\\n          ((eptr[1] & 0x3fu) << 18) | ((eptr[2] & 0x3fu) << 12) | \\\n          ((eptr[3] & 0x3fu) << 6) | (eptr[4] & 0x3fu); \\\n      eptr += 5; \\\n      } \\\n    }\n\n/* Base macro to pick up the remaining bytes of a UTF-8 character, not\nadvancing the pointer, incrementing the length. */\n\n#define GETUTF8LEN(c, eptr, len) \\\n    { \\\n    if ((c & 0x20u) == 0) \\\n      { \\\n      c = ((c & 0x1fu) << 6) | (eptr[1] & 0x3fu); \\\n      len++; \\\n      } \\\n    else if ((c & 0x10u)  == 0) \\\n      { \\\n      c = ((c & 0x0fu) << 12) | ((eptr[1] & 0x3fu) << 6) | (eptr[2] & 0x3fu); \\\n      len += 2; \\\n      } \\\n    else if ((c & 0x08u)  == 0) \\\n      {\\\n      c = ((c & 0x07u) << 18) | ((eptr[1] & 0x3fu) << 12) | \\\n          ((eptr[2] & 0x3fu) << 6) | (eptr[3] & 0x3fu); \\\n      len += 3; \\\n      } \\\n    else if ((c & 0x04u)  == 0) \\\n      { \\\n      c = ((c & 0x03u) << 24) | ((eptr[1] & 0x3fu) << 18) | \\\n          ((eptr[2] & 0x3fu) << 12) | ((eptr[3] & 0x3fu) << 6) | \\\n          (eptr[4] & 0x3fu); \\\n      len += 4; \\\n      } \\\n    else \\\n      {\\\n      c = ((c & 0x01u) << 30) | ((eptr[1] & 0x3fu) << 24) | \\\n          ((eptr[2] & 0x3fu) << 18) | ((eptr[3] & 0x3fu) << 12) | \\\n          ((eptr[4] & 0x3fu) << 6) | (eptr[5] & 0x3fu); \\\n      len += 5; \\\n      } \\\n    }\n\n/* --------------- Whitespace macros ---------------- */\n\n/* Tests for Unicode horizontal and vertical whitespace characters must check a\nnumber of different values. Using a switch statement for this generates the\nfastest code (no loop, no memory access), and there are several places in the\ninterpreter code where this happens. In order to ensure that all the case lists\nremain in step, we use macros so that there is only one place where the lists\nare defined.\n\nThese values are also required as lists in pcre2_compile.c when processing \\h,\n\\H, \\v and \\V in a character class. The lists are defined in pcre2_tables.c,\nbut macros that define the values are here so that all the definitions are\ntogether. The lists must be in ascending character order, terminated by\nNOTACHAR (which is 0xffffffff).\n\nAny changes should ensure that the various macros are kept in step with each\nother. NOTE: The values also appear in pcre2_jit_compile.c. */\n\n/* -------------- ASCII/Unicode environments -------------- */\n\n#ifndef EBCDIC\n\n/* Character U+180E (Mongolian Vowel Separator) is not included in the list of\nspaces in the Unicode file PropList.txt, and Perl does not recognize it as a\nspace. However, in many other sources it is listed as a space and has been in\nPCRE (both APIs) for a long time. */\n\n#define HSPACE_LIST \\\n  CHAR_HT, CHAR_SPACE, CHAR_NBSP, \\\n  0x1680, 0x180e, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, \\\n  0x2006, 0x2007, 0x2008, 0x2009, 0x200A, 0x202f, 0x205f, 0x3000, \\\n  NOTACHAR\n\n#define HSPACE_MULTIBYTE_CASES \\\n  case 0x1680:  /* OGHAM SPACE MARK */ \\\n  case 0x180e:  /* MONGOLIAN VOWEL SEPARATOR */ \\\n  case 0x2000:  /* EN QUAD */ \\\n  case 0x2001:  /* EM QUAD */ \\\n  case 0x2002:  /* EN SPACE */ \\\n  case 0x2003:  /* EM SPACE */ \\\n  case 0x2004:  /* THREE-PER-EM SPACE */ \\\n  case 0x2005:  /* FOUR-PER-EM SPACE */ \\\n  case 0x2006:  /* SIX-PER-EM SPACE */ \\\n  case 0x2007:  /* FIGURE SPACE */ \\\n  case 0x2008:  /* PUNCTUATION SPACE */ \\\n  case 0x2009:  /* THIN SPACE */ \\\n  case 0x200A:  /* HAIR SPACE */ \\\n  case 0x202f:  /* NARROW NO-BREAK SPACE */ \\\n  case 0x205f:  /* MEDIUM MATHEMATICAL SPACE */ \\\n  case 0x3000   /* IDEOGRAPHIC SPACE */\n\n#define HSPACE_BYTE_CASES \\\n  case CHAR_HT: \\\n  case CHAR_SPACE: \\\n  case CHAR_NBSP\n\n#define HSPACE_CASES \\\n  HSPACE_BYTE_CASES: \\\n  HSPACE_MULTIBYTE_CASES\n\n#define VSPACE_LIST \\\n  CHAR_LF, CHAR_VT, CHAR_FF, CHAR_CR, CHAR_NEL, 0x2028, 0x2029, NOTACHAR\n\n#define VSPACE_MULTIBYTE_CASES \\\n  case 0x2028:    /* LINE SEPARATOR */ \\\n  case 0x2029     /* PARAGRAPH SEPARATOR */\n\n#define VSPACE_BYTE_CASES \\\n  case CHAR_LF: \\\n  case CHAR_VT: \\\n  case CHAR_FF: \\\n  case CHAR_CR: \\\n  case CHAR_NEL\n\n#define VSPACE_CASES \\\n  VSPACE_BYTE_CASES: \\\n  VSPACE_MULTIBYTE_CASES\n\n/* -------------- EBCDIC environments -------------- */\n\n#else\n#define HSPACE_LIST CHAR_HT, CHAR_SPACE, CHAR_NBSP, NOTACHAR\n\n#define HSPACE_BYTE_CASES \\\n  case CHAR_HT: \\\n  case CHAR_SPACE: \\\n  case CHAR_NBSP\n\n#define HSPACE_CASES HSPACE_BYTE_CASES\n\n#ifdef EBCDIC_NL25\n#define VSPACE_LIST \\\n  CHAR_VT, CHAR_FF, CHAR_CR, CHAR_NEL, CHAR_LF, NOTACHAR\n#else\n#define VSPACE_LIST \\\n  CHAR_VT, CHAR_FF, CHAR_CR, CHAR_LF, CHAR_NEL, NOTACHAR\n#endif\n\n#define VSPACE_BYTE_CASES \\\n  case CHAR_LF: \\\n  case CHAR_VT: \\\n  case CHAR_FF: \\\n  case CHAR_CR: \\\n  case CHAR_NEL\n\n#define VSPACE_CASES VSPACE_BYTE_CASES\n#endif  /* EBCDIC */\n\n/* -------------- End of whitespace macros -------------- */\n\n\n/* PCRE2 is able to support several different kinds of newline (CR, LF, CRLF,\n\"any\" and \"anycrlf\" at present). The following macros are used to package up\ntesting for newlines. NLBLOCK, PSSTART, and PSEND are defined in the various\nmodules to indicate in which datablock the parameters exist, and what the\nstart/end of string field names are. */\n\n#define NLTYPE_FIXED    0     /* Newline is a fixed length string */\n#define NLTYPE_ANY      1     /* Newline is any Unicode line ending */\n#define NLTYPE_ANYCRLF  2     /* Newline is CR, LF, or CRLF */\n\n/* This macro checks for a newline at the given position */\n\n#define IS_NEWLINE(p) \\\n  ((NLBLOCK->nltype != NLTYPE_FIXED)? \\\n    ((p) < NLBLOCK->PSEND && \\\n     PRIV(is_newline)((p), NLBLOCK->nltype, NLBLOCK->PSEND, \\\n       &(NLBLOCK->nllen), utf)) \\\n    : \\\n    ((p) <= NLBLOCK->PSEND - NLBLOCK->nllen && \\\n     UCHAR21TEST(p) == NLBLOCK->nl[0] && \\\n     (NLBLOCK->nllen == 1 || UCHAR21TEST(p+1) == NLBLOCK->nl[1])       \\\n    ) \\\n  )\n\n/* This macro checks for a newline immediately preceding the given position */\n\n#define WAS_NEWLINE(p) \\\n  ((NLBLOCK->nltype != NLTYPE_FIXED)? \\\n    ((p) > NLBLOCK->PSSTART && \\\n     PRIV(was_newline)((p), NLBLOCK->nltype, NLBLOCK->PSSTART, \\\n       &(NLBLOCK->nllen), utf)) \\\n    : \\\n    ((p) >= NLBLOCK->PSSTART + NLBLOCK->nllen && \\\n     UCHAR21TEST(p - NLBLOCK->nllen) == NLBLOCK->nl[0] &&              \\\n     (NLBLOCK->nllen == 1 || UCHAR21TEST(p - NLBLOCK->nllen + 1) == NLBLOCK->nl[1]) \\\n    ) \\\n  )\n\n/* Private flags containing information about the compiled pattern. The first\nthree must not be changed, because whichever is set is actually the number of\nbytes in a code unit in that mode. */\n\n#define PCRE2_MODE8         0x00000001u /* compiled in 8 bit mode */\n#define PCRE2_MODE16        0x00000002u /* compiled in 16 bit mode */\n#define PCRE2_MODE32        0x00000004u /* compiled in 32 bit mode */\n#define PCRE2_FIRSTSET      0x00000010u /* first_code unit is set */\n#define PCRE2_FIRSTCASELESS 0x00000020u /* caseless first code unit */\n#define PCRE2_FIRSTMAPSET   0x00000040u /* bitmap of first code units is set */\n#define PCRE2_LASTSET       0x00000080u /* last code unit is set */\n#define PCRE2_LASTCASELESS  0x00000100u /* caseless last code unit */\n#define PCRE2_STARTLINE     0x00000200u /* start after \\n for multiline */\n#define PCRE2_JCHANGED      0x00000400u /* j option used in pattern */\n#define PCRE2_HASCRORLF     0x00000800u /* explicit \\r or \\n in pattern */\n#define PCRE2_HASTHEN       0x00001000u /* pattern contains (*THEN) */\n#define PCRE2_MATCH_EMPTY   0x00002000u /* pattern can match empty string */\n#define PCRE2_BSR_SET       0x00004000u /* BSR was set in the pattern */\n#define PCRE2_NL_SET        0x00008000u /* newline was set in the pattern */\n#define PCRE2_NOTEMPTY_SET  0x00010000u /* (*NOTEMPTY) used        ) keep */\n#define PCRE2_NE_ATST_SET   0x00020000u /* (*NOTEMPTY_ATSTART) used) together */\n#define PCRE2_DEREF_TABLES  0x00040000u /* release character tables */\n#define PCRE2_NOJIT         0x00080000u /* (*NOJIT) used */\n#define PCRE2_HASBKPORX     0x00100000u /* contains \\P, \\p, or \\X */\n#define PCRE2_DUPCAPUSED    0x00200000u /* contains (?| */\n#define PCRE2_HASBKC        0x00400000u /* contains \\C */\n#define PCRE2_HASACCEPT     0x00800000u /* contains (*ACCEPT) */\n\n#define PCRE2_MODE_MASK     (PCRE2_MODE8 | PCRE2_MODE16 | PCRE2_MODE32)\n\n/* Values for the matchedby field in a match data block. */\n\nenum { PCRE2_MATCHEDBY_INTERPRETER,     /* pcre2_match() */\n       PCRE2_MATCHEDBY_DFA_INTERPRETER, /* pcre2_dfa_match() */\n       PCRE2_MATCHEDBY_JIT };           /* pcre2_jit_match() */\n\n/* Values for the flags field in a match data block. */\n\n#define PCRE2_MD_COPIED_SUBJECT  0x01u\n\n/* Magic number to provide a small check against being handed junk. */\n\n#define MAGIC_NUMBER  0x50435245UL   /* 'PCRE' */\n\n/* The maximum remaining length of subject we are prepared to search for a\nreq_unit match from an anchored pattern. In 8-bit mode, memchr() is used and is\nmuch faster than the search loop that has to be used in 16-bit and 32-bit\nmodes. */\n\n#if PCRE2_CODE_UNIT_WIDTH == 8\n#define REQ_CU_MAX       5000\n#else\n#define REQ_CU_MAX       2000\n#endif\n\n/* The maximum nesting depth for Unicode character class sets.\nCurrently fixed. Warning: the interpreter relies on this so it can encode\nthe operand stack in a uint32_t. A nesting limit of 15 implies (15*2+1)=31\nstack operands required, due to the fact that we have two (and only two)\nlevels of operator precedence. In the UTS#18 syntax, you can write 'x&&y[z]'\nand in Perl syntax you can write '(?[ x - y & (z) ])', both of which imply\npushing the match results for x & y to the stack. */\n\n#define ECLASS_NEST_LIMIT  15\n\n/* Offsets for the bitmap tables in the cbits set of tables. Each table\ncontains a set of bits for a class map. Some classes are built by combining\nthese tables. */\n\n#define cbit_space     0      /* [:space:] or \\s */\n#define cbit_xdigit   32      /* [:xdigit:] */\n#define cbit_digit    64      /* [:digit:] or \\d */\n#define cbit_upper    96      /* [:upper:] */\n#define cbit_lower   128      /* [:lower:] */\n#define cbit_word    160      /* [:word:] or \\w */\n#define cbit_graph   192      /* [:graph:] */\n#define cbit_print   224      /* [:print:] */\n#define cbit_punct   256      /* [:punct:] */\n#define cbit_cntrl   288      /* [:cntrl:] */\n#define cbit_length  320      /* Length of the cbits table */\n\n/* Bit definitions for entries in the ctypes table. Do not change these values\nwithout checking pcre2_jit_compile.c, which has an assertion to ensure that\nctype_word has the value 16. */\n\n#define ctype_space    0x01\n#define ctype_letter   0x02\n#define ctype_lcletter 0x04\n#define ctype_digit    0x08\n#define ctype_word     0x10    /* alphanumeric or '_' */\n\n/* Offsets of the various tables from the base tables pointer, and\ntotal length of the tables. */\n\n#define lcc_offset      0                           /* Lower case */\n#define fcc_offset    256                           /* Flip case */\n#define cbits_offset  512                           /* Character classes */\n#define ctypes_offset (cbits_offset + cbit_length)  /* Character types */\n#define TABLES_LENGTH (ctypes_offset + 256)\n\n/* Private flags used in compile_context.optimization_flags */\n\n#define PCRE2_OPTIM_AUTO_POSSESS    0x00000001u\n#define PCRE2_OPTIM_DOTSTAR_ANCHOR  0x00000002u\n#define PCRE2_OPTIM_START_OPTIMIZE  0x00000004u\n\n#define PCRE2_OPTIMIZATION_ALL      0x00000007u\n\n/* -------------------- Character and string names ------------------------ */\n\n/* If PCRE2 is to support UTF-8 on EBCDIC platforms, we cannot use normal\ncharacter constants like '*' because the compiler would emit their EBCDIC code,\nwhich is different from their ASCII/UTF-8 code. Instead we define macros for\nthe characters so that they always use the ASCII/UTF-8 code when UTF-8 support\nis enabled. When UTF-8 support is not enabled, the definitions use character\nliterals. Both character and string versions of each character are needed, and\nthere are some longer strings as well.\n\nThis means that, on EBCDIC platforms, the PCRE2 library can handle either\nEBCDIC, or UTF-8, but not both. To support both in the same compiled library\nwould need different lookups depending on whether PCRE2_UTF was set or not.\nThis would make it impossible to use characters in switch/case statements,\nwhich would reduce performance. For a theoretical use (which nobody has asked\nfor) in a minority area (EBCDIC platforms), this is not sensible. Any\napplication that did need both could compile two versions of the library, using\nmacros to give the functions distinct names. */\n\n#ifndef SUPPORT_UNICODE\n\n/* UTF-8 support is not enabled; use the platform-dependent character literals\nso that PCRE2 works in both ASCII and EBCDIC environments, but only in non-UTF\nmode. Newline characters are problematic in EBCDIC. Though it has CR and LF\ncharacters, a common practice has been to use its NL (0x15) character as the\nline terminator in C-like processing environments. However, sometimes the LF\n(0x25) character is used instead, according to this Unicode document:\n\nhttp://unicode.org/standard/reports/tr13/tr13-5.html\n\nPCRE2 defaults EBCDIC NL to 0x15, but has a build-time option to select 0x25\ninstead. Whichever is *not* chosen is defined as NEL.\n\nIn both ASCII and EBCDIC environments, CHAR_NL and CHAR_LF are synonyms for the\nsame code point. */\n\n#ifdef EBCDIC\n\n#ifndef EBCDIC_NL25\n#define CHAR_NL                     '\\x15'\n#define CHAR_NEL                    '\\x25'\n#define STR_NL                      \"\\x15\"\n#define STR_NEL                     \"\\x25\"\n#else\n#define CHAR_NL                     '\\x25'\n#define CHAR_NEL                    '\\x15'\n#define STR_NL                      \"\\x25\"\n#define STR_NEL                     \"\\x15\"\n#endif\n\n#define CHAR_LF                     CHAR_NL\n#define STR_LF                      STR_NL\n\n#define CHAR_ESC                    '\\047'\n#define CHAR_DEL                    '\\007'\n#define CHAR_NBSP                   ((unsigned char)'\\x41')\n#define STR_ESC                     \"\\047\"\n#define STR_DEL                     \"\\007\"\n\n#else  /* Not EBCDIC */\n\n/* In ASCII/Unicode, linefeed is '\\n' and we equate this to NL for\ncompatibility. NEL is the Unicode newline character; make sure it is\na positive value. */\n\n#define CHAR_LF                     '\\n'\n#define CHAR_NL                     CHAR_LF\n#define CHAR_NEL                    ((unsigned char)'\\x85')\n#define CHAR_ESC                    '\\033'\n#define CHAR_DEL                    '\\177'\n#define CHAR_NBSP                   ((unsigned char)'\\xa0')\n\n#define STR_LF                      \"\\n\"\n#define STR_NL                      STR_LF\n#define STR_NEL                     \"\\x85\"\n#define STR_ESC                     \"\\033\"\n#define STR_DEL                     \"\\177\"\n\n#endif  /* EBCDIC */\n\n/* The remaining definitions work in both environments. */\n\n#define CHAR_NUL                    '\\0'\n#define CHAR_HT                     '\\t'\n#define CHAR_VT                     '\\v'\n#define CHAR_FF                     '\\f'\n#define CHAR_CR                     '\\r'\n#define CHAR_BS                     '\\b'\n#define CHAR_BEL                    '\\a'\n\n#define CHAR_SPACE                  ' '\n#define CHAR_EXCLAMATION_MARK       '!'\n#define CHAR_QUOTATION_MARK         '\"'\n#define CHAR_NUMBER_SIGN            '#'\n#define CHAR_DOLLAR_SIGN            '$'\n#define CHAR_PERCENT_SIGN           '%'\n#define CHAR_AMPERSAND              '&'\n#define CHAR_APOSTROPHE             '\\''\n#define CHAR_LEFT_PARENTHESIS       '('\n#define CHAR_RIGHT_PARENTHESIS      ')'\n#define CHAR_ASTERISK               '*'\n#define CHAR_PLUS                   '+'\n#define CHAR_COMMA                  ','\n#define CHAR_MINUS                  '-'\n#define CHAR_DOT                    '.'\n#define CHAR_SLASH                  '/'\n#define CHAR_0                      '0'\n#define CHAR_1                      '1'\n#define CHAR_2                      '2'\n#define CHAR_3                      '3'\n#define CHAR_4                      '4'\n#define CHAR_5                      '5'\n#define CHAR_6                      '6'\n#define CHAR_7                      '7'\n#define CHAR_8                      '8'\n#define CHAR_9                      '9'\n#define CHAR_COLON                  ':'\n#define CHAR_SEMICOLON              ';'\n#define CHAR_LESS_THAN_SIGN         '<'\n#define CHAR_EQUALS_SIGN            '='\n#define CHAR_GREATER_THAN_SIGN      '>'\n#define CHAR_QUESTION_MARK          '?'\n#define CHAR_COMMERCIAL_AT          '@'\n#define CHAR_A                      'A'\n#define CHAR_B                      'B'\n#define CHAR_C                      'C'\n#define CHAR_D                      'D'\n#define CHAR_E                      'E'\n#define CHAR_F                      'F'\n#define CHAR_G                      'G'\n#define CHAR_H                      'H'\n#define CHAR_I                      'I'\n#define CHAR_J                      'J'\n#define CHAR_K                      'K'\n#define CHAR_L                      'L'\n#define CHAR_M                      'M'\n#define CHAR_N                      'N'\n#define CHAR_O                      'O'\n#define CHAR_P                      'P'\n#define CHAR_Q                      'Q'\n#define CHAR_R                      'R'\n#define CHAR_S                      'S'\n#define CHAR_T                      'T'\n#define CHAR_U                      'U'\n#define CHAR_V                      'V'\n#define CHAR_W                      'W'\n#define CHAR_X                      'X'\n#define CHAR_Y                      'Y'\n#define CHAR_Z                      'Z'\n#define CHAR_LEFT_SQUARE_BRACKET    '['\n#define CHAR_BACKSLASH              '\\\\'\n#define CHAR_RIGHT_SQUARE_BRACKET   ']'\n#define CHAR_CIRCUMFLEX_ACCENT      '^'\n#define CHAR_UNDERSCORE             '_'\n#define CHAR_GRAVE_ACCENT           '`'\n#define CHAR_a                      'a'\n#define CHAR_b                      'b'\n#define CHAR_c                      'c'\n#define CHAR_d                      'd'\n#define CHAR_e                      'e'\n#define CHAR_f                      'f'\n#define CHAR_g                      'g'\n#define CHAR_h                      'h'\n#define CHAR_i                      'i'\n#define CHAR_j                      'j'\n#define CHAR_k                      'k'\n#define CHAR_l                      'l'\n#define CHAR_m                      'm'\n#define CHAR_n                      'n'\n#define CHAR_o                      'o'\n#define CHAR_p                      'p'\n#define CHAR_q                      'q'\n#define CHAR_r                      'r'\n#define CHAR_s                      's'\n#define CHAR_t                      't'\n#define CHAR_u                      'u'\n#define CHAR_v                      'v'\n#define CHAR_w                      'w'\n#define CHAR_x                      'x'\n#define CHAR_y                      'y'\n#define CHAR_z                      'z'\n#define CHAR_LEFT_CURLY_BRACKET     '{'\n#define CHAR_VERTICAL_LINE          '|'\n#define CHAR_RIGHT_CURLY_BRACKET    '}'\n#define CHAR_TILDE                  '~'\n\n#define STR_HT                      \"\\t\"\n#define STR_VT                      \"\\v\"\n#define STR_FF                      \"\\f\"\n#define STR_CR                      \"\\r\"\n#define STR_BS                      \"\\b\"\n#define STR_BEL                     \"\\a\"\n\n#define STR_SPACE                   \" \"\n#define STR_EXCLAMATION_MARK        \"!\"\n#define STR_QUOTATION_MARK          \"\\\"\"\n#define STR_NUMBER_SIGN             \"#\"\n#define STR_DOLLAR_SIGN             \"$\"\n#define STR_PERCENT_SIGN            \"%\"\n#define STR_AMPERSAND               \"&\"\n#define STR_APOSTROPHE              \"'\"\n#define STR_LEFT_PARENTHESIS        \"(\"\n#define STR_RIGHT_PARENTHESIS       \")\"\n#define STR_ASTERISK                \"*\"\n#define STR_PLUS                    \"+\"\n#define STR_COMMA                   \",\"\n#define STR_MINUS                   \"-\"\n#define STR_DOT                     \".\"\n#define STR_SLASH                   \"/\"\n#define STR_0                       \"0\"\n#define STR_1                       \"1\"\n#define STR_2                       \"2\"\n#define STR_3                       \"3\"\n#define STR_4                       \"4\"\n#define STR_5                       \"5\"\n#define STR_6                       \"6\"\n#define STR_7                       \"7\"\n#define STR_8                       \"8\"\n#define STR_9                       \"9\"\n#define STR_COLON                   \":\"\n#define STR_SEMICOLON               \";\"\n#define STR_LESS_THAN_SIGN          \"<\"\n#define STR_EQUALS_SIGN             \"=\"\n#define STR_GREATER_THAN_SIGN       \">\"\n#define STR_QUESTION_MARK           \"?\"\n#define STR_COMMERCIAL_AT           \"@\"\n#define STR_A                       \"A\"\n#define STR_B                       \"B\"\n#define STR_C                       \"C\"\n#define STR_D                       \"D\"\n#define STR_E                       \"E\"\n#define STR_F                       \"F\"\n#define STR_G                       \"G\"\n#define STR_H                       \"H\"\n#define STR_I                       \"I\"\n#define STR_J                       \"J\"\n#define STR_K                       \"K\"\n#define STR_L                       \"L\"\n#define STR_M                       \"M\"\n#define STR_N                       \"N\"\n#define STR_O                       \"O\"\n#define STR_P                       \"P\"\n#define STR_Q                       \"Q\"\n#define STR_R                       \"R\"\n#define STR_S                       \"S\"\n#define STR_T                       \"T\"\n#define STR_U                       \"U\"\n#define STR_V                       \"V\"\n#define STR_W                       \"W\"\n#define STR_X                       \"X\"\n#define STR_Y                       \"Y\"\n#define STR_Z                       \"Z\"\n#define STR_LEFT_SQUARE_BRACKET     \"[\"\n#define STR_BACKSLASH               \"\\\\\"\n#define STR_RIGHT_SQUARE_BRACKET    \"]\"\n#define STR_CIRCUMFLEX_ACCENT       \"^\"\n#define STR_UNDERSCORE              \"_\"\n#define STR_GRAVE_ACCENT            \"`\"\n#define STR_a                       \"a\"\n#define STR_b                       \"b\"\n#define STR_c                       \"c\"\n#define STR_d                       \"d\"\n#define STR_e                       \"e\"\n#define STR_f                       \"f\"\n#define STR_g                       \"g\"\n#define STR_h                       \"h\"\n#define STR_i                       \"i\"\n#define STR_j                       \"j\"\n#define STR_k                       \"k\"\n#define STR_l                       \"l\"\n#define STR_m                       \"m\"\n#define STR_n                       \"n\"\n#define STR_o                       \"o\"\n#define STR_p                       \"p\"\n#define STR_q                       \"q\"\n#define STR_r                       \"r\"\n#define STR_s                       \"s\"\n#define STR_t                       \"t\"\n#define STR_u                       \"u\"\n#define STR_v                       \"v\"\n#define STR_w                       \"w\"\n#define STR_x                       \"x\"\n#define STR_y                       \"y\"\n#define STR_z                       \"z\"\n#define STR_LEFT_CURLY_BRACKET      \"{\"\n#define STR_VERTICAL_LINE           \"|\"\n#define STR_RIGHT_CURLY_BRACKET     \"}\"\n#define STR_TILDE                   \"~\"\n\n#define STRING_ACCEPT0               \"ACCEPT\\0\"\n#define STRING_COMMIT0               \"COMMIT\\0\"\n#define STRING_F0                    \"F\\0\"\n#define STRING_FAIL0                 \"FAIL\\0\"\n#define STRING_MARK0                 \"MARK\\0\"\n#define STRING_PRUNE0                \"PRUNE\\0\"\n#define STRING_SKIP0                 \"SKIP\\0\"\n#define STRING_THEN                  \"THEN\"\n\n#define STRING_atomic0               \"atomic\\0\"\n#define STRING_pla0                  \"pla\\0\"\n#define STRING_plb0                  \"plb\\0\"\n#define STRING_napla0                \"napla\\0\"\n#define STRING_naplb0                \"naplb\\0\"\n#define STRING_nla0                  \"nla\\0\"\n#define STRING_nlb0                  \"nlb\\0\"\n#define STRING_scs0                  \"scs\\0\"\n#define STRING_sr0                   \"sr\\0\"\n#define STRING_asr0                  \"asr\\0\"\n#define STRING_positive_lookahead0   \"positive_lookahead\\0\"\n#define STRING_positive_lookbehind0  \"positive_lookbehind\\0\"\n#define STRING_non_atomic_positive_lookahead0   \"non_atomic_positive_lookahead\\0\"\n#define STRING_non_atomic_positive_lookbehind0  \"non_atomic_positive_lookbehind\\0\"\n#define STRING_negative_lookahead0   \"negative_lookahead\\0\"\n#define STRING_negative_lookbehind0  \"negative_lookbehind\\0\"\n#define STRING_script_run0           \"script_run\\0\"\n#define STRING_atomic_script_run     \"atomic_script_run\"\n#define STRING_scan_substring0       \"scan_substring\\0\"\n\n#define STRING_alpha0                \"alpha\\0\"\n#define STRING_lower0                \"lower\\0\"\n#define STRING_upper0                \"upper\\0\"\n#define STRING_alnum0                \"alnum\\0\"\n#define STRING_ascii0                \"ascii\\0\"\n#define STRING_blank0                \"blank\\0\"\n#define STRING_cntrl0                \"cntrl\\0\"\n#define STRING_digit0                \"digit\\0\"\n#define STRING_graph0                \"graph\\0\"\n#define STRING_print0                \"print\\0\"\n#define STRING_punct0                \"punct\\0\"\n#define STRING_space0                \"space\\0\"\n#define STRING_word0                 \"word\\0\"\n#define STRING_xdigit                \"xdigit\"\n\n#define STRING_DEFINE                \"DEFINE\"\n#define STRING_VERSION               \"VERSION\"\n#define STRING_WEIRD_STARTWORD       \"[:<:]]\"\n#define STRING_WEIRD_ENDWORD         \"[:>:]]\"\n\n#define STRING_CR_RIGHTPAR                \"CR)\"\n#define STRING_LF_RIGHTPAR                \"LF)\"\n#define STRING_CRLF_RIGHTPAR              \"CRLF)\"\n#define STRING_ANY_RIGHTPAR               \"ANY)\"\n#define STRING_ANYCRLF_RIGHTPAR           \"ANYCRLF)\"\n#define STRING_NUL_RIGHTPAR               \"NUL)\"\n#define STRING_BSR_ANYCRLF_RIGHTPAR       \"BSR_ANYCRLF)\"\n#define STRING_BSR_UNICODE_RIGHTPAR       \"BSR_UNICODE)\"\n#define STRING_UTF8_RIGHTPAR              \"UTF8)\"\n#define STRING_UTF16_RIGHTPAR             \"UTF16)\"\n#define STRING_UTF32_RIGHTPAR             \"UTF32)\"\n#define STRING_UTF_RIGHTPAR               \"UTF)\"\n#define STRING_UCP_RIGHTPAR               \"UCP)\"\n#define STRING_NO_AUTO_POSSESS_RIGHTPAR   \"NO_AUTO_POSSESS)\"\n#define STRING_NO_DOTSTAR_ANCHOR_RIGHTPAR \"NO_DOTSTAR_ANCHOR)\"\n#define STRING_NO_JIT_RIGHTPAR            \"NO_JIT)\"\n#define STRING_NO_START_OPT_RIGHTPAR      \"NO_START_OPT)\"\n#define STRING_NOTEMPTY_RIGHTPAR          \"NOTEMPTY)\"\n#define STRING_NOTEMPTY_ATSTART_RIGHTPAR  \"NOTEMPTY_ATSTART)\"\n#define STRING_CASELESS_RESTRICT_RIGHTPAR \"CASELESS_RESTRICT)\"\n#define STRING_TURKISH_CASING_RIGHTPAR    \"TURKISH_CASING)\"\n#define STRING_LIMIT_HEAP_EQ              \"LIMIT_HEAP=\"\n#define STRING_LIMIT_MATCH_EQ             \"LIMIT_MATCH=\"\n#define STRING_LIMIT_DEPTH_EQ             \"LIMIT_DEPTH=\"\n#define STRING_LIMIT_RECURSION_EQ         \"LIMIT_RECURSION=\"\n#define STRING_MARK                       \"MARK\"\n\n#define STRING_bc                         \"bc\"\n#define STRING_bidiclass                  \"bidiclass\"\n#define STRING_sc                         \"sc\"\n#define STRING_script                     \"script\"\n#define STRING_scriptextensions           \"scriptextensions\"\n#define STRING_scx                        \"scx\"\n\n#else  /* SUPPORT_UNICODE */\n\n/* UTF-8 support is enabled; always use UTF-8 (=ASCII) character codes. This\nworks in both modes non-EBCDIC platforms, and on EBCDIC platforms in UTF-8 mode\nonly. */\n\n#define CHAR_HT                     '\\011'\n#define CHAR_VT                     '\\013'\n#define CHAR_FF                     '\\014'\n#define CHAR_CR                     '\\015'\n#define CHAR_LF                     '\\012'\n#define CHAR_NL                     CHAR_LF\n#define CHAR_NEL                    ((unsigned char)'\\x85')\n#define CHAR_BS                     '\\010'\n#define CHAR_BEL                    '\\007'\n#define CHAR_ESC                    '\\033'\n#define CHAR_DEL                    '\\177'\n\n#define CHAR_NUL                    '\\0'\n#define CHAR_SPACE                  '\\040'\n#define CHAR_EXCLAMATION_MARK       '\\041'\n#define CHAR_QUOTATION_MARK         '\\042'\n#define CHAR_NUMBER_SIGN            '\\043'\n#define CHAR_DOLLAR_SIGN            '\\044'\n#define CHAR_PERCENT_SIGN           '\\045'\n#define CHAR_AMPERSAND              '\\046'\n#define CHAR_APOSTROPHE             '\\047'\n#define CHAR_LEFT_PARENTHESIS       '\\050'\n#define CHAR_RIGHT_PARENTHESIS      '\\051'\n#define CHAR_ASTERISK               '\\052'\n#define CHAR_PLUS                   '\\053'\n#define CHAR_COMMA                  '\\054'\n#define CHAR_MINUS                  '\\055'\n#define CHAR_DOT                    '\\056'\n#define CHAR_SLASH                  '\\057'\n#define CHAR_0                      '\\060'\n#define CHAR_1                      '\\061'\n#define CHAR_2                      '\\062'\n#define CHAR_3                      '\\063'\n#define CHAR_4                      '\\064'\n#define CHAR_5                      '\\065'\n#define CHAR_6                      '\\066'\n#define CHAR_7                      '\\067'\n#define CHAR_8                      '\\070'\n#define CHAR_9                      '\\071'\n#define CHAR_COLON                  '\\072'\n#define CHAR_SEMICOLON              '\\073'\n#define CHAR_LESS_THAN_SIGN         '\\074'\n#define CHAR_EQUALS_SIGN            '\\075'\n#define CHAR_GREATER_THAN_SIGN      '\\076'\n#define CHAR_QUESTION_MARK          '\\077'\n#define CHAR_COMMERCIAL_AT          '\\100'\n#define CHAR_A                      '\\101'\n#define CHAR_B                      '\\102'\n#define CHAR_C                      '\\103'\n#define CHAR_D                      '\\104'\n#define CHAR_E                      '\\105'\n#define CHAR_F                      '\\106'\n#define CHAR_G                      '\\107'\n#define CHAR_H                      '\\110'\n#define CHAR_I                      '\\111'\n#define CHAR_J                      '\\112'\n#define CHAR_K                      '\\113'\n#define CHAR_L                      '\\114'\n#define CHAR_M                      '\\115'\n#define CHAR_N                      '\\116'\n#define CHAR_O                      '\\117'\n#define CHAR_P                      '\\120'\n#define CHAR_Q                      '\\121'\n#define CHAR_R                      '\\122'\n#define CHAR_S                      '\\123'\n#define CHAR_T                      '\\124'\n#define CHAR_U                      '\\125'\n#define CHAR_V                      '\\126'\n#define CHAR_W                      '\\127'\n#define CHAR_X                      '\\130'\n#define CHAR_Y                      '\\131'\n#define CHAR_Z                      '\\132'\n#define CHAR_LEFT_SQUARE_BRACKET    '\\133'\n#define CHAR_BACKSLASH              '\\134'\n#define CHAR_RIGHT_SQUARE_BRACKET   '\\135'\n#define CHAR_CIRCUMFLEX_ACCENT      '\\136'\n#define CHAR_UNDERSCORE             '\\137'\n#define CHAR_GRAVE_ACCENT           '\\140'\n#define CHAR_a                      '\\141'\n#define CHAR_b                      '\\142'\n#define CHAR_c                      '\\143'\n#define CHAR_d                      '\\144'\n#define CHAR_e                      '\\145'\n#define CHAR_f                      '\\146'\n#define CHAR_g                      '\\147'\n#define CHAR_h                      '\\150'\n#define CHAR_i                      '\\151'\n#define CHAR_j                      '\\152'\n#define CHAR_k                      '\\153'\n#define CHAR_l                      '\\154'\n#define CHAR_m                      '\\155'\n#define CHAR_n                      '\\156'\n#define CHAR_o                      '\\157'\n#define CHAR_p                      '\\160'\n#define CHAR_q                      '\\161'\n#define CHAR_r                      '\\162'\n#define CHAR_s                      '\\163'\n#define CHAR_t                      '\\164'\n#define CHAR_u                      '\\165'\n#define CHAR_v                      '\\166'\n#define CHAR_w                      '\\167'\n#define CHAR_x                      '\\170'\n#define CHAR_y                      '\\171'\n#define CHAR_z                      '\\172'\n#define CHAR_LEFT_CURLY_BRACKET     '\\173'\n#define CHAR_VERTICAL_LINE          '\\174'\n#define CHAR_RIGHT_CURLY_BRACKET    '\\175'\n#define CHAR_TILDE                  '\\176'\n#define CHAR_NBSP                   ((unsigned char)'\\xa0')\n\n#define STR_HT                      \"\\011\"\n#define STR_VT                      \"\\013\"\n#define STR_FF                      \"\\014\"\n#define STR_CR                      \"\\015\"\n#define STR_NL                      \"\\012\"\n#define STR_BS                      \"\\010\"\n#define STR_BEL                     \"\\007\"\n#define STR_ESC                     \"\\033\"\n#define STR_DEL                     \"\\177\"\n\n#define STR_SPACE                   \"\\040\"\n#define STR_EXCLAMATION_MARK        \"\\041\"\n#define STR_QUOTATION_MARK          \"\\042\"\n#define STR_NUMBER_SIGN             \"\\043\"\n#define STR_DOLLAR_SIGN             \"\\044\"\n#define STR_PERCENT_SIGN            \"\\045\"\n#define STR_AMPERSAND               \"\\046\"\n#define STR_APOSTROPHE              \"\\047\"\n#define STR_LEFT_PARENTHESIS        \"\\050\"\n#define STR_RIGHT_PARENTHESIS       \"\\051\"\n#define STR_ASTERISK                \"\\052\"\n#define STR_PLUS                    \"\\053\"\n#define STR_COMMA                   \"\\054\"\n#define STR_MINUS                   \"\\055\"\n#define STR_DOT                     \"\\056\"\n#define STR_SLASH                   \"\\057\"\n#define STR_0                       \"\\060\"\n#define STR_1                       \"\\061\"\n#define STR_2                       \"\\062\"\n#define STR_3                       \"\\063\"\n#define STR_4                       \"\\064\"\n#define STR_5                       \"\\065\"\n#define STR_6                       \"\\066\"\n#define STR_7                       \"\\067\"\n#define STR_8                       \"\\070\"\n#define STR_9                       \"\\071\"\n#define STR_COLON                   \"\\072\"\n#define STR_SEMICOLON               \"\\073\"\n#define STR_LESS_THAN_SIGN          \"\\074\"\n#define STR_EQUALS_SIGN             \"\\075\"\n#define STR_GREATER_THAN_SIGN       \"\\076\"\n#define STR_QUESTION_MARK           \"\\077\"\n#define STR_COMMERCIAL_AT           \"\\100\"\n#define STR_A                       \"\\101\"\n#define STR_B                       \"\\102\"\n#define STR_C                       \"\\103\"\n#define STR_D                       \"\\104\"\n#define STR_E                       \"\\105\"\n#define STR_F                       \"\\106\"\n#define STR_G                       \"\\107\"\n#define STR_H                       \"\\110\"\n#define STR_I                       \"\\111\"\n#define STR_J                       \"\\112\"\n#define STR_K                       \"\\113\"\n#define STR_L                       \"\\114\"\n#define STR_M                       \"\\115\"\n#define STR_N                       \"\\116\"\n#define STR_O                       \"\\117\"\n#define STR_P                       \"\\120\"\n#define STR_Q                       \"\\121\"\n#define STR_R                       \"\\122\"\n#define STR_S                       \"\\123\"\n#define STR_T                       \"\\124\"\n#define STR_U                       \"\\125\"\n#define STR_V                       \"\\126\"\n#define STR_W                       \"\\127\"\n#define STR_X                       \"\\130\"\n#define STR_Y                       \"\\131\"\n#define STR_Z                       \"\\132\"\n#define STR_LEFT_SQUARE_BRACKET     \"\\133\"\n#define STR_BACKSLASH               \"\\134\"\n#define STR_RIGHT_SQUARE_BRACKET    \"\\135\"\n#define STR_CIRCUMFLEX_ACCENT       \"\\136\"\n#define STR_UNDERSCORE              \"\\137\"\n#define STR_GRAVE_ACCENT            \"\\140\"\n#define STR_a                       \"\\141\"\n#define STR_b                       \"\\142\"\n#define STR_c                       \"\\143\"\n#define STR_d                       \"\\144\"\n#define STR_e                       \"\\145\"\n#define STR_f                       \"\\146\"\n#define STR_g                       \"\\147\"\n#define STR_h                       \"\\150\"\n#define STR_i                       \"\\151\"\n#define STR_j                       \"\\152\"\n#define STR_k                       \"\\153\"\n#define STR_l                       \"\\154\"\n#define STR_m                       \"\\155\"\n#define STR_n                       \"\\156\"\n#define STR_o                       \"\\157\"\n#define STR_p                       \"\\160\"\n#define STR_q                       \"\\161\"\n#define STR_r                       \"\\162\"\n#define STR_s                       \"\\163\"\n#define STR_t                       \"\\164\"\n#define STR_u                       \"\\165\"\n#define STR_v                       \"\\166\"\n#define STR_w                       \"\\167\"\n#define STR_x                       \"\\170\"\n#define STR_y                       \"\\171\"\n#define STR_z                       \"\\172\"\n#define STR_LEFT_CURLY_BRACKET      \"\\173\"\n#define STR_VERTICAL_LINE           \"\\174\"\n#define STR_RIGHT_CURLY_BRACKET     \"\\175\"\n#define STR_TILDE                   \"\\176\"\n\n#define STRING_ACCEPT0               STR_A STR_C STR_C STR_E STR_P STR_T \"\\0\"\n#define STRING_COMMIT0               STR_C STR_O STR_M STR_M STR_I STR_T \"\\0\"\n#define STRING_F0                    STR_F \"\\0\"\n#define STRING_FAIL0                 STR_F STR_A STR_I STR_L \"\\0\"\n#define STRING_MARK0                 STR_M STR_A STR_R STR_K \"\\0\"\n#define STRING_PRUNE0                STR_P STR_R STR_U STR_N STR_E \"\\0\"\n#define STRING_SKIP0                 STR_S STR_K STR_I STR_P \"\\0\"\n#define STRING_THEN                  STR_T STR_H STR_E STR_N\n\n#define STRING_atomic0               STR_a STR_t STR_o STR_m STR_i STR_c \"\\0\"\n#define STRING_pla0                  STR_p STR_l STR_a \"\\0\"\n#define STRING_plb0                  STR_p STR_l STR_b \"\\0\"\n#define STRING_napla0                STR_n STR_a STR_p STR_l STR_a \"\\0\"\n#define STRING_naplb0                STR_n STR_a STR_p STR_l STR_b \"\\0\"\n#define STRING_nla0                  STR_n STR_l STR_a \"\\0\"\n#define STRING_nlb0                  STR_n STR_l STR_b \"\\0\"\n#define STRING_scs0                  STR_s STR_c STR_s \"\\0\"\n#define STRING_sr0                   STR_s STR_r \"\\0\"\n#define STRING_asr0                  STR_a STR_s STR_r \"\\0\"\n#define STRING_positive_lookahead0   STR_p STR_o STR_s STR_i STR_t STR_i STR_v STR_e STR_UNDERSCORE STR_l STR_o STR_o STR_k STR_a STR_h STR_e STR_a STR_d \"\\0\"\n#define STRING_positive_lookbehind0  STR_p STR_o STR_s STR_i STR_t STR_i STR_v STR_e STR_UNDERSCORE STR_l STR_o STR_o STR_k STR_b STR_e STR_h STR_i STR_n STR_d \"\\0\"\n#define STRING_non_atomic_positive_lookahead0   STR_n STR_o STR_n STR_UNDERSCORE STR_a STR_t STR_o STR_m STR_i STR_c STR_UNDERSCORE STR_p STR_o STR_s STR_i STR_t STR_i STR_v STR_e STR_UNDERSCORE STR_l STR_o STR_o STR_k STR_a STR_h STR_e STR_a STR_d \"\\0\"\n#define STRING_non_atomic_positive_lookbehind0  STR_n STR_o STR_n STR_UNDERSCORE STR_a STR_t STR_o STR_m STR_i STR_c STR_UNDERSCORE STR_p STR_o STR_s STR_i STR_t STR_i STR_v STR_e STR_UNDERSCORE STR_l STR_o STR_o STR_k STR_b STR_e STR_h STR_i STR_n STR_d \"\\0\"\n#define STRING_negative_lookahead0   STR_n STR_e STR_g STR_a STR_t STR_i STR_v STR_e STR_UNDERSCORE STR_l STR_o STR_o STR_k STR_a STR_h STR_e STR_a STR_d \"\\0\"\n#define STRING_negative_lookbehind0  STR_n STR_e STR_g STR_a STR_t STR_i STR_v STR_e STR_UNDERSCORE STR_l STR_o STR_o STR_k STR_b STR_e STR_h STR_i STR_n STR_d \"\\0\"\n#define STRING_script_run0           STR_s STR_c STR_r STR_i STR_p STR_t STR_UNDERSCORE STR_r STR_u STR_n \"\\0\"\n#define STRING_atomic_script_run     STR_a STR_t STR_o STR_m STR_i STR_c STR_UNDERSCORE STR_s STR_c STR_r STR_i STR_p STR_t STR_UNDERSCORE STR_r STR_u STR_n\n#define STRING_scan_substring0       STR_s STR_c STR_a STR_n STR_UNDERSCORE STR_s STR_u STR_b STR_s STR_t STR_r STR_i STR_n STR_g \"\\0\"\n\n#define STRING_alpha0                STR_a STR_l STR_p STR_h STR_a \"\\0\"\n#define STRING_lower0                STR_l STR_o STR_w STR_e STR_r \"\\0\"\n#define STRING_upper0                STR_u STR_p STR_p STR_e STR_r \"\\0\"\n#define STRING_alnum0                STR_a STR_l STR_n STR_u STR_m \"\\0\"\n#define STRING_ascii0                STR_a STR_s STR_c STR_i STR_i \"\\0\"\n#define STRING_blank0                STR_b STR_l STR_a STR_n STR_k \"\\0\"\n#define STRING_cntrl0                STR_c STR_n STR_t STR_r STR_l \"\\0\"\n#define STRING_digit0                STR_d STR_i STR_g STR_i STR_t \"\\0\"\n#define STRING_graph0                STR_g STR_r STR_a STR_p STR_h \"\\0\"\n#define STRING_print0                STR_p STR_r STR_i STR_n STR_t \"\\0\"\n#define STRING_punct0                STR_p STR_u STR_n STR_c STR_t \"\\0\"\n#define STRING_space0                STR_s STR_p STR_a STR_c STR_e \"\\0\"\n#define STRING_word0                 STR_w STR_o STR_r STR_d       \"\\0\"\n#define STRING_xdigit                STR_x STR_d STR_i STR_g STR_i STR_t\n\n#define STRING_DEFINE                STR_D STR_E STR_F STR_I STR_N STR_E\n#define STRING_VERSION               STR_V STR_E STR_R STR_S STR_I STR_O STR_N\n#define STRING_WEIRD_STARTWORD       STR_LEFT_SQUARE_BRACKET STR_COLON STR_LESS_THAN_SIGN STR_COLON STR_RIGHT_SQUARE_BRACKET STR_RIGHT_SQUARE_BRACKET\n#define STRING_WEIRD_ENDWORD         STR_LEFT_SQUARE_BRACKET STR_COLON STR_GREATER_THAN_SIGN STR_COLON STR_RIGHT_SQUARE_BRACKET STR_RIGHT_SQUARE_BRACKET\n\n#define STRING_CR_RIGHTPAR                STR_C STR_R STR_RIGHT_PARENTHESIS\n#define STRING_LF_RIGHTPAR                STR_L STR_F STR_RIGHT_PARENTHESIS\n#define STRING_CRLF_RIGHTPAR              STR_C STR_R STR_L STR_F STR_RIGHT_PARENTHESIS\n#define STRING_ANY_RIGHTPAR               STR_A STR_N STR_Y STR_RIGHT_PARENTHESIS\n#define STRING_ANYCRLF_RIGHTPAR           STR_A STR_N STR_Y STR_C STR_R STR_L STR_F STR_RIGHT_PARENTHESIS\n#define STRING_NUL_RIGHTPAR               STR_N STR_U STR_L STR_RIGHT_PARENTHESIS\n#define STRING_BSR_ANYCRLF_RIGHTPAR       STR_B STR_S STR_R STR_UNDERSCORE STR_A STR_N STR_Y STR_C STR_R STR_L STR_F STR_RIGHT_PARENTHESIS\n#define STRING_BSR_UNICODE_RIGHTPAR       STR_B STR_S STR_R STR_UNDERSCORE STR_U STR_N STR_I STR_C STR_O STR_D STR_E STR_RIGHT_PARENTHESIS\n#define STRING_UTF8_RIGHTPAR              STR_U STR_T STR_F STR_8 STR_RIGHT_PARENTHESIS\n#define STRING_UTF16_RIGHTPAR             STR_U STR_T STR_F STR_1 STR_6 STR_RIGHT_PARENTHESIS\n#define STRING_UTF32_RIGHTPAR             STR_U STR_T STR_F STR_3 STR_2 STR_RIGHT_PARENTHESIS\n#define STRING_UTF_RIGHTPAR               STR_U STR_T STR_F STR_RIGHT_PARENTHESIS\n#define STRING_UCP_RIGHTPAR               STR_U STR_C STR_P STR_RIGHT_PARENTHESIS\n#define STRING_EC_RIGHTPAR                STR_E STR_C STR_RIGHT_PARENTHESIS //au\n#define STRING_NO_AUTO_POSSESS_RIGHTPAR   STR_N STR_O STR_UNDERSCORE STR_A STR_U STR_T STR_O STR_UNDERSCORE STR_P STR_O STR_S STR_S STR_E STR_S STR_S STR_RIGHT_PARENTHESIS\n#define STRING_NO_DOTSTAR_ANCHOR_RIGHTPAR STR_N STR_O STR_UNDERSCORE STR_D STR_O STR_T STR_S STR_T STR_A STR_R STR_UNDERSCORE STR_A STR_N STR_C STR_H STR_O STR_R STR_RIGHT_PARENTHESIS\n#define STRING_NO_JIT_RIGHTPAR            STR_N STR_O STR_UNDERSCORE STR_J STR_I STR_T STR_RIGHT_PARENTHESIS\n#define STRING_NO_START_OPT_RIGHTPAR      STR_N STR_O STR_UNDERSCORE STR_S STR_T STR_A STR_R STR_T STR_UNDERSCORE STR_O STR_P STR_T STR_RIGHT_PARENTHESIS\n#define STRING_NOTEMPTY_RIGHTPAR          STR_N STR_O STR_T STR_E STR_M STR_P STR_T STR_Y STR_RIGHT_PARENTHESIS\n#define STRING_NOTEMPTY_ATSTART_RIGHTPAR  STR_N STR_O STR_T STR_E STR_M STR_P STR_T STR_Y STR_UNDERSCORE STR_A STR_T STR_S STR_T STR_A STR_R STR_T STR_RIGHT_PARENTHESIS\n#define STRING_CASELESS_RESTRICT_RIGHTPAR STR_C STR_A STR_S STR_E STR_L STR_E STR_S STR_S STR_UNDERSCORE STR_R STR_E STR_S STR_T STR_R STR_I STR_C STR_T STR_RIGHT_PARENTHESIS\n#define STRING_TURKISH_CASING_RIGHTPAR    STR_T STR_U STR_R STR_K STR_I STR_S STR_H STR_UNDERSCORE STR_C STR_A STR_S STR_I STR_N STR_G STR_RIGHT_PARENTHESIS\n#define STRING_LIMIT_HEAP_EQ              STR_L STR_I STR_M STR_I STR_T STR_UNDERSCORE STR_H STR_E STR_A STR_P STR_EQUALS_SIGN\n#define STRING_LIMIT_MATCH_EQ             STR_L STR_I STR_M STR_I STR_T STR_UNDERSCORE STR_M STR_A STR_T STR_C STR_H STR_EQUALS_SIGN\n#define STRING_LIMIT_DEPTH_EQ             STR_L STR_I STR_M STR_I STR_T STR_UNDERSCORE STR_D STR_E STR_P STR_T STR_H STR_EQUALS_SIGN\n#define STRING_LIMIT_RECURSION_EQ         STR_L STR_I STR_M STR_I STR_T STR_UNDERSCORE STR_R STR_E STR_C STR_U STR_R STR_S STR_I STR_O STR_N STR_EQUALS_SIGN\n#define STRING_MARK                       STR_M STR_A STR_R STR_K\n\n#define STRING_bc                         STR_b STR_c\n#define STRING_bidiclass                  STR_b STR_i STR_d STR_i STR_c STR_l STR_a STR_s STR_s\n#define STRING_sc                         STR_s STR_c\n#define STRING_script                     STR_s STR_c STR_r STR_i STR_p STR_t\n#define STRING_scriptextensions           STR_s STR_c STR_r STR_i STR_p STR_t STR_e STR_x STR_t STR_e STR_n STR_s STR_i STR_o STR_n STR_s\n#define STRING_scx                        STR_s STR_c STR_x\n\n\n#endif  /* SUPPORT_UNICODE */\n\n/* -------------------- End of character and string names -------------------*/\n\n/* -------------------- Definitions for compiled patterns -------------------*/\n\n/* Codes for different types of Unicode property. If these definitions are\nchanged, the autopossessifying table in pcre2_auto_possess.c must be updated to\nmatch. */\n\n#define PT_LAMP       0    /* L& - the union of Lu, Ll, Lt */\n#define PT_GC         1    /* Specified general characteristic (e.g. L) */\n#define PT_PC         2    /* Specified particular characteristic (e.g. Lu) */\n#define PT_SC         3    /* Script only (e.g. Han) */\n#define PT_SCX        4    /* Script extensions (includes SC) */\n#define PT_ALNUM      5    /* Alphanumeric - the union of L and N */\n#define PT_SPACE      6    /* Perl space - general category Z plus 9,10,12,13 */\n#define PT_PXSPACE    7    /* POSIX space - Z plus 9,10,11,12,13 */\n#define PT_WORD       8    /* Word - L, N, Mn, or Pc */\n#define PT_CLIST      9    /* Pseudo-property: match character list */\n#define PT_UCNC      10    /* Universal Character nameable character */\n#define PT_BIDICL    11    /* Specified bidi class */\n#define PT_BOOL      12    /* Boolean property */\n#define PT_ANY       13    /* Must be the last entry!\n                              Any property - matches all chars */\n#define PT_TABSIZE PT_ANY  /* Size of square table for autopossessify tests */\n\n/* The following special properties are used only in XCLASS items, when POSIX\nclasses are specified and PCRE2_UCP is set - in other words, for Unicode\nhandling of these classes. They are not available via the \\p or \\P escapes like\nthose in the above list, and so they do not take part in the autopossessifying\ntable. */\n\n#define PT_PXGRAPH   14    /* [:graph:] - characters that mark the paper */\n#define PT_PXPRINT   15    /* [:print:] - [:graph:] plus non-control spaces */\n#define PT_PXPUNCT   16    /* [:punct:] - punctuation characters */\n#define PT_PXXDIGIT  17    /* [:xdigit:] - hex digits */\n\n/* This value is used when parsing \\p and \\P escapes to indicate that neither\n\\p{script:...} nor \\p{scx:...} has been encountered. */\n\n#define PT_NOTSCRIPT 255\n\n/* Flag bits and data types for the extended class (OP_XCLASS) for classes that\ncontain characters with values greater than 255. */\n\n#define XCL_NOT      0x01  /* Flag: this is a negative class */\n#define XCL_MAP      0x02  /* Flag: a 32-byte map is present */\n#define XCL_HASPROP  0x04  /* Flag: property checks are present. */\n\n#define XCL_END      0     /* Marks end of individual items */\n#define XCL_SINGLE   1     /* Single item (one multibyte char) follows */\n#define XCL_RANGE    2     /* A range (two multibyte chars) follows */\n#define XCL_PROP     3     /* Unicode property (2-byte property code follows) */\n#define XCL_NOTPROP  4     /* Unicode inverted property (ditto) */\n/* This value represents the beginning of character lists. The value\nis 16 bit long, and stored as a high and low byte pair in 8 bit mode.\nThe lower 12 bit contains information about character lists (see later). */\n#define XCL_LIST     (sizeof(PCRE2_UCHAR) == 1 ? 0x10 : 0x1000)\n\n/* When a character class contains many characters/ranges,\nthey are stored in character lists. There are four character\nlists which contain characters/ranges within a given range.\n\nThe name, character range and item size for each list:\nLow16    [0x100 - 0x7fff]            16 bit items\nHigh16   [0x8000 - 0xffff]           16 bit items\nLow32    [0x10000 - 0x7fffffff]      32 bit items\nHigh32   [0x80000000 - 0xffffffff]   32 bit items\n\nThe Low32 character list is used only when utf encoding or 32 bit\ncharacter width is enabled, and the High32 character is used only\nwhen 32 bit character width is enabled.\n\nEach character list contain items. The lowest bit represents that\nan item is the beginning of a range (bit is cleared), or not (bit\nis set). The other bits represent the character shifted left by\none, so its highest bit is discarded. Due to the layout of character\nlists, the highest bit of a character is always known:\n\nLow16 and Low32: the highest bit is always zero\nHigh16 and High32: the highest bit is always one\n\nThe items are ordered in increasing order, so binary search can be\nused to find the lower bound of an input character. The lower bound\nis the highest item, which value is less or equal than the input\ncharacter. If the lower bit of the item is cleard, or the character\nstored in the item equals to the input character, the input\ncharacter is in the character list. */\n\n/* Character list constants. */\n#define XCL_CHAR_LIST_LOW_16_START 0x100\n#define XCL_CHAR_LIST_LOW_16_END 0x7fff\n#define XCL_CHAR_LIST_LOW_16_ADD 0x0\n\n#define XCL_CHAR_LIST_HIGH_16_START 0x8000\n#define XCL_CHAR_LIST_HIGH_16_END 0xffff\n#define XCL_CHAR_LIST_HIGH_16_ADD 0x8000\n\n#define XCL_CHAR_LIST_LOW_32_START 0x10000\n#define XCL_CHAR_LIST_LOW_32_END 0x7fffffff\n#define XCL_CHAR_LIST_LOW_32_ADD 0x0\n\n#define XCL_CHAR_LIST_HIGH_32_START 0x80000000\n#define XCL_CHAR_LIST_HIGH_32_END 0xffffffff\n#define XCL_CHAR_LIST_HIGH_32_ADD 0x80000000\n\n/* Mask for getting the descriptors of character list ranges.\nEach descriptor has XCL_TYPE_BIT_LEN bits, and can be processed\nby XCL_BEGIN_WITH_RANGE and XCL_ITEM_COUNT_MASK macros. */\n#define XCL_TYPE_MASK 0xfff\n#define XCL_TYPE_BIT_LEN 3\n/* If this bit is set, the first item of the character list is the\nend of a range, which started before the starting character of the\ncharacter list. */\n#define XCL_BEGIN_WITH_RANGE 0x4\n/* Number of items in the character list: 0, 1, or 2. The value 3\nrepresents that the item count is stored at the begining of the\ncharacter list. The item count has the same width as the items\nin the character list (e.g. 16 bit for Low16 and High16 lists). */\n#define XCL_ITEM_COUNT_MASK 0x3\n/* Shift and flag for constructing character list items. The XCL_CHAR_END\nis set, when the item is not the beginning of a range. The XCL_CHAR_SHIFT\ncan be used to encode / decode the character value stored in an item. */\n#define XCL_CHAR_END 0x1\n#define XCL_CHAR_SHIFT 1\n\n/* Flag bits for an extended class (OP_ECLASS), which is used for complex\ncharacter matches such as [\\p{Greek} && \\p{Ll}]. */\n\n#define ECL_MAP     0x01  /* Flag: a 32-byte map is present */\n\n/* Type tags for the items stored in an extended class (OP_ECLASS). These items\nfollow the OP_ECLASS's flag char and bitmap, and represent a Reverse Polish\nNotation list of operands and operators manipulating a stack of bits. */\n\n#define ECL_AND     1 /* Pop two from the stack, AND, and push result. */\n#define ECL_OR      2 /* Pop two from the stack, OR, and push result. */\n#define ECL_XOR     3 /* Pop two from the stack, XOR, and push result. */\n#define ECL_NOT     4 /* Pop one from the stack, NOT, and push result. */\n#define ECL_XCLASS  5 /* XCLASS nested within ECLASS; match and push result. */\n#define ECL_ANY     6 /* Temporary, only used during compilation. */\n#define ECL_NONE    7 /* Temporary, only used during compilation. */\n\n/* These are escaped items that aren't just an encoding of a particular data\nvalue such as \\n. They must have non-zero values, as check_escape() returns 0\nfor a data character. In the escapes[] table in pcre2_compile.c their values\nare negated in order to distinguish them from data values.\n\nThey must appear here in the same order as in the opcode definitions below, up\nto ESC_z. There's a dummy for OP_ALLANY because it corresponds to \".\" in DOTALL\nmode rather than an escape sequence. It is also used for [^] in JavaScript\ncompatibility mode, and for \\C in non-utf mode. In non-DOTALL mode, \".\" behaves\nlike \\N.\n\nESC_ub is a special return from check_escape() when, in BSUX mode, \\u{ is not\nfollowed by hex digits and }, in which case it should mean a literal \"u\"\nfollowed by a literal \"{\". This hack is necessary for cases like \\u{ 12}\nbecause without it, this is interpreted as u{12} now that spaces are allowed in\nquantifiers.\n\nNegative numbers are used to encode a backreference (\\1, \\2, \\3, etc.) in\ncheck_escape(). There are tests in the code for an escape greater than ESC_b\nand less than ESC_Z to detect the types that may be repeated. These are the\ntypes that consume characters. If any new escapes are put in between that don't\nconsume a character, that code will have to change. */\n\nenum { ESC_A = 1, ESC_G, ESC_K, ESC_B, ESC_b, ESC_D, ESC_d, ESC_S, ESC_s,\n       ESC_W, ESC_w, ESC_N, ESC_dum, ESC_C, ESC_P, ESC_p, ESC_R, ESC_H,\n       ESC_h, ESC_V, ESC_v, ESC_X, ESC_Z, ESC_z,\n       ESC_E, ESC_Q, ESC_g, ESC_k, ESC_ub };\n\n\n/********************** Opcode definitions ******************/\n\n/****** NOTE NOTE NOTE ******\n\nStarting from 1 (i.e. after OP_END), the values up to OP_EOD must correspond in\norder to the list of escapes immediately above. Furthermore, values up to\nOP_DOLLM must not be changed without adjusting the table called autoposstab in\npcre2_auto_possess.c.\n\nWhenever this list is updated, the two macro definitions that follow must be\nupdated to match. The possessification table called \"opcode_possessify\" in\npcre2_compile.c must also be updated, and also the tables called \"coptable\"\nand \"poptable\" in pcre2_dfa_match.c.\n\n****** NOTE NOTE NOTE ******/\n\n\n/* The values between FIRST_AUTOTAB_OP and LAST_AUTOTAB_RIGHT_OP, inclusive,\nare used in a table for deciding whether a repeated character type can be\nauto-possessified. */\n\n#define FIRST_AUTOTAB_OP       OP_NOT_DIGIT\n#define LAST_AUTOTAB_LEFT_OP   OP_EXTUNI\n#define LAST_AUTOTAB_RIGHT_OP  OP_DOLLM\n\nenum {\n  OP_END,            /* 0 End of pattern */\n\n  /* Values corresponding to backslashed metacharacters */\n\n  OP_SOD,            /* 1 Start of data: \\A */\n  OP_SOM,            /* 2 Start of match (subject + offset): \\G */\n  OP_SET_SOM,        /* 3 Set start of match (\\K) */\n  OP_NOT_WORD_BOUNDARY,  /*  4 \\B -- see also OP_NOT_UCP_WORD_BOUNDARY */\n  OP_WORD_BOUNDARY,      /*  5 \\b -- see also OP_UCP_WORD_BOUNDARY */\n  OP_NOT_DIGIT,          /*  6 \\D */\n  OP_DIGIT,              /*  7 \\d */\n  OP_NOT_WHITESPACE,     /*  8 \\S */\n  OP_WHITESPACE,         /*  9 \\s */\n  OP_NOT_WORDCHAR,       /* 10 \\W */\n  OP_WORDCHAR,           /* 11 \\w */\n\n  OP_ANY,            /* 12 Match any character except newline (\\N) */\n  OP_ALLANY,         /* 13 Match any character */\n  OP_ANYBYTE,        /* 14 Match any byte (\\C); different to OP_ANY for UTF-8 */\n  OP_NOTPROP,        /* 15 \\P (not Unicode property) */\n  OP_PROP,           /* 16 \\p (Unicode property) */\n  OP_ANYNL,          /* 17 \\R (any newline sequence) */\n  OP_NOT_HSPACE,     /* 18 \\H (not horizontal whitespace) */\n  OP_HSPACE,         /* 19 \\h (horizontal whitespace) */\n  OP_NOT_VSPACE,     /* 20 \\V (not vertical whitespace) */\n  OP_VSPACE,         /* 21 \\v (vertical whitespace) */\n  OP_EXTUNI,         /* 22 \\X (extended Unicode sequence */\n  OP_EODN,           /* 23 End of data or \\n at end of data (\\Z) */\n  OP_EOD,            /* 24 End of data (\\z) */\n\n  /* Line end assertions */\n\n  OP_DOLL,           /* 25 End of line - not multiline */\n  OP_DOLLM,          /* 26 End of line - multiline */\n  OP_CIRC,           /* 27 Start of line - not multiline */\n  OP_CIRCM,          /* 28 Start of line - multiline */\n\n  /* Single characters; caseful must precede the caseless ones, and these\n  must remain in this order, and adjacent. */\n\n  OP_CHAR,           /* 29 Match one character, casefully */\n  OP_CHARI,          /* 30 Match one character, caselessly */\n  OP_NOT,            /* 31 Match one character, not the given one, casefully */\n  OP_NOTI,           /* 32 Match one character, not the given one, caselessly */\n\n  /* The following sets of 13 opcodes must always be kept in step because\n  the offset from the first one is used to generate the others. */\n\n  /* Repeated characters; caseful must precede the caseless ones */\n\n  OP_STAR,           /* 33 The maximizing and minimizing versions of */\n  OP_MINSTAR,        /* 34 these six opcodes must come in pairs, with */\n  OP_PLUS,           /* 35 the minimizing one second. */\n  OP_MINPLUS,        /* 36 */\n  OP_QUERY,          /* 37 */\n  OP_MINQUERY,       /* 38 */\n\n  OP_UPTO,           /* 39 From 0 to n matches of one character, caseful*/\n  OP_MINUPTO,        /* 40 */\n  OP_EXACT,          /* 41 Exactly n matches */\n\n  OP_POSSTAR,        /* 42 Possessified star, caseful */\n  OP_POSPLUS,        /* 43 Possessified plus, caseful */\n  OP_POSQUERY,       /* 44 Posesssified query, caseful */\n  OP_POSUPTO,        /* 45 Possessified upto, caseful */\n\n  /* Repeated characters; caseless must follow the caseful ones */\n\n  OP_STARI,          /* 46 */\n  OP_MINSTARI,       /* 47 */\n  OP_PLUSI,          /* 48 */\n  OP_MINPLUSI,       /* 49 */\n  OP_QUERYI,         /* 50 */\n  OP_MINQUERYI,      /* 51 */\n\n  OP_UPTOI,          /* 52 From 0 to n matches of one character, caseless */\n  OP_MINUPTOI,       /* 53 */\n  OP_EXACTI,         /* 54 */\n\n  OP_POSSTARI,       /* 55 Possessified star, caseless */\n  OP_POSPLUSI,       /* 56 Possessified plus, caseless */\n  OP_POSQUERYI,      /* 57 Posesssified query, caseless */\n  OP_POSUPTOI,       /* 58 Possessified upto, caseless */\n\n  /* The negated ones must follow the non-negated ones, and match them */\n  /* Negated repeated character, caseful; must precede the caseless ones */\n\n  OP_NOTSTAR,        /* 59 The maximizing and minimizing versions of */\n  OP_NOTMINSTAR,     /* 60 these six opcodes must come in pairs, with */\n  OP_NOTPLUS,        /* 61 the minimizing one second. They must be in */\n  OP_NOTMINPLUS,     /* 62 exactly the same order as those above. */\n  OP_NOTQUERY,       /* 63 */\n  OP_NOTMINQUERY,    /* 64 */\n\n  OP_NOTUPTO,        /* 65 From 0 to n matches, caseful */\n  OP_NOTMINUPTO,     /* 66 */\n  OP_NOTEXACT,       /* 67 Exactly n matches */\n\n  OP_NOTPOSSTAR,     /* 68 Possessified versions, caseful */\n  OP_NOTPOSPLUS,     /* 69 */\n  OP_NOTPOSQUERY,    /* 70 */\n  OP_NOTPOSUPTO,     /* 71 */\n\n  /* Negated repeated character, caseless; must follow the caseful ones */\n\n  OP_NOTSTARI,       /* 72 */\n  OP_NOTMINSTARI,    /* 73 */\n  OP_NOTPLUSI,       /* 74 */\n  OP_NOTMINPLUSI,    /* 75 */\n  OP_NOTQUERYI,      /* 76 */\n  OP_NOTMINQUERYI,   /* 77 */\n\n  OP_NOTUPTOI,       /* 78 From 0 to n matches, caseless */\n  OP_NOTMINUPTOI,    /* 79 */\n  OP_NOTEXACTI,      /* 80 Exactly n matches */\n\n  OP_NOTPOSSTARI,    /* 81 Possessified versions, caseless */\n  OP_NOTPOSPLUSI,    /* 82 */\n  OP_NOTPOSQUERYI,   /* 83 */\n  OP_NOTPOSUPTOI,    /* 84 */\n\n  /* Character types */\n\n  OP_TYPESTAR,       /* 85 The maximizing and minimizing versions of */\n  OP_TYPEMINSTAR,    /* 86 these six opcodes must come in pairs, with */\n  OP_TYPEPLUS,       /* 87 the minimizing one second. These codes must */\n  OP_TYPEMINPLUS,    /* 88 be in exactly the same order as those above. */\n  OP_TYPEQUERY,      /* 89 */\n  OP_TYPEMINQUERY,   /* 90 */\n\n  OP_TYPEUPTO,       /* 91 From 0 to n matches */\n  OP_TYPEMINUPTO,    /* 92 */\n  OP_TYPEEXACT,      /* 93 Exactly n matches */\n\n  OP_TYPEPOSSTAR,    /* 94 Possessified versions */\n  OP_TYPEPOSPLUS,    /* 95 */\n  OP_TYPEPOSQUERY,   /* 96 */\n  OP_TYPEPOSUPTO,    /* 97 */\n\n  /* These are used for character classes and back references; only the\n  first six are the same as the sets above. */\n\n  OP_CRSTAR,         /* 98 The maximizing and minimizing versions of */\n  OP_CRMINSTAR,      /* 99 all these opcodes must come in pairs, with */\n  OP_CRPLUS,         /* 100 the minimizing one second. These codes must */\n  OP_CRMINPLUS,      /* 101 be in exactly the same order as those above. */\n  OP_CRQUERY,        /* 102 */\n  OP_CRMINQUERY,     /* 103 */\n\n  OP_CRRANGE,        /* 104 These are different to the three sets above. */\n  OP_CRMINRANGE,     /* 105 */\n\n  OP_CRPOSSTAR,      /* 106 Possessified versions */\n  OP_CRPOSPLUS,      /* 107 */\n  OP_CRPOSQUERY,     /* 108 */\n  OP_CRPOSRANGE,     /* 109 */\n\n  /* End of quantifier opcodes */\n\n  OP_CLASS,          /* 110 Match a character class, chars < 256 only */\n  OP_NCLASS,         /* 111 Same, but the bitmap was created from a negative\n                              class - the difference is relevant only when a\n                              character > 255 is encountered. */\n  OP_XCLASS,         /* 112 Extended class for handling > 255 chars within the\n                              class. This does both positive and negative. */\n  OP_ECLASS,         /* 113 Really-extended class, for handling logical\n                              expressions computed over characters. */\n  OP_REF,            /* 114 Match a back reference, casefully */\n  OP_REFI,           /* 115 Match a back reference, caselessly */\n  OP_DNREF,          /* 116 Match a duplicate name backref, casefully */\n  OP_DNREFI,         /* 117 Match a duplicate name backref, caselessly */\n  OP_RECURSE,        /* 118 Match a numbered subpattern (possibly recursive) */\n  OP_CALLOUT,        /* 119 Call out to external function if provided */\n  OP_CALLOUT_STR,    /* 120 Call out with string argument */\n\n  OP_ALT,            /* 121 Start of alternation */\n  OP_KET,            /* 122 End of group that doesn't have an unbounded repeat */\n  OP_KETRMAX,        /* 123 These two must remain together and in this */\n  OP_KETRMIN,        /* 124 order. They are for groups the repeat for ever. */\n  OP_KETRPOS,        /* 125 Possessive unlimited repeat. */\n\n  /* The assertions must come before BRA, CBRA, ONCE, and COND. */\n\n  OP_REVERSE,        /* 126 Move pointer back - used in lookbehind assertions */\n  OP_VREVERSE,       /* 127 Move pointer back - variable */\n  OP_ASSERT,         /* 128 Positive lookahead */\n  OP_ASSERT_NOT,     /* 129 Negative lookahead */\n  OP_ASSERTBACK,     /* 130 Positive lookbehind */\n  OP_ASSERTBACK_NOT, /* 131 Negative lookbehind */\n  OP_ASSERT_NA,      /* 132 Positive non-atomic lookahead */\n  OP_ASSERTBACK_NA,  /* 133 Positive non-atomic lookbehind */\n  OP_ASSERT_SCS,     /* 134 Scan substring */\n\n  /* ONCE, SCRIPT_RUN, BRA, BRAPOS, CBRA, CBRAPOS, and COND must come\n  immediately after the assertions, with ONCE first, as there's a test for >=\n  ONCE for a subpattern that isn't an assertion. The POS versions must\n  immediately follow the non-POS versions in each case. */\n\n  OP_ONCE,           /* 135 Atomic group, contains captures */\n  OP_SCRIPT_RUN,     /* 136 Non-capture, but check characters' scripts */\n  OP_BRA,            /* 137 Start of non-capturing bracket */\n  OP_BRAPOS,         /* 138 Ditto, with unlimited, possessive repeat */\n  OP_CBRA,           /* 139 Start of capturing bracket */\n  OP_CBRAPOS,        /* 140 Ditto, with unlimited, possessive repeat */\n  OP_COND,           /* 141 Conditional group */\n\n  /* These five must follow the previous five, in the same order. There's a\n  check for >= SBRA to distinguish the two sets. */\n\n  OP_SBRA,           /* 142 Start of non-capturing bracket, check empty  */\n  OP_SBRAPOS,        /* 143 Ditto, with unlimited, possessive repeat */\n  OP_SCBRA,          /* 144 Start of capturing bracket, check empty */\n  OP_SCBRAPOS,       /* 145 Ditto, with unlimited, possessive repeat */\n  OP_SCOND,          /* 146 Conditional group, check empty */\n\n  /* The next two pairs must (respectively) be kept together. */\n\n  OP_CREF,           /* 147 Used to hold a capture number as condition */\n  OP_DNCREF,         /* 148 Used to point to duplicate names as a condition */\n  OP_RREF,           /* 149 Used to hold a recursion number as condition */\n  OP_DNRREF,         /* 150 Used to point to duplicate names as a condition */\n  OP_FALSE,          /* 151 Always false (used by DEFINE and VERSION) */\n  OP_TRUE,           /* 152 Always true (used by VERSION) */\n\n  OP_BRAZERO,        /* 153 These two must remain together and in this */\n  OP_BRAMINZERO,     /* 154 order. */\n  OP_BRAPOSZERO,     /* 155 */\n\n  /* These are backtracking control verbs */\n\n  OP_MARK,           /* 156 always has an argument */\n  OP_PRUNE,          /* 157 */\n  OP_PRUNE_ARG,      /* 158 same, but with argument */\n  OP_SKIP,           /* 159 */\n  OP_SKIP_ARG,       /* 160 same, but with argument */\n  OP_THEN,           /* 161 */\n  OP_THEN_ARG,       /* 162 same, but with argument */\n  OP_COMMIT,         /* 163 */\n  OP_COMMIT_ARG,     /* 164 same, but with argument */\n\n  /* These are forced failure and success verbs. FAIL and ACCEPT do accept an\n  argument, but these cases can be compiled as, for example, (*MARK:X)(*FAIL)\n  without the need for a special opcode. */\n\n  OP_FAIL,           /* 165 */\n  OP_ACCEPT,         /* 166 */\n  OP_ASSERT_ACCEPT,  /* 167 Used inside assertions */\n  OP_CLOSE,          /* 168 Used before OP_ACCEPT to close open captures */\n\n  /* This is used to skip a subpattern with a {0} quantifier */\n\n  OP_SKIPZERO,       /* 169 */\n\n  /* This is used to identify a DEFINE group during compilation so that it can\n  be checked for having only one branch. It is changed to OP_FALSE before\n  compilation finishes. */\n\n  OP_DEFINE,         /* 170 */\n\n  /* These opcodes replace their normal counterparts in UCP mode when\n  PCRE2_EXTRA_ASCII_BSW is not set. */\n\n  OP_NOT_UCP_WORD_BOUNDARY, /* 171 */\n  OP_UCP_WORD_BOUNDARY,     /* 172 */\n\n  /* This is not an opcode, but is used to check that tables indexed by opcode\n  are the correct length, in order to catch updating errors - there have been\n  some in the past. */\n\n  OP_TABLE_LENGTH\n\n};\n\n/* *** NOTE NOTE NOTE *** Whenever the list above is updated, the two macro\ndefinitions that follow must also be updated to match. There are also tables\ncalled \"opcode_possessify\" in pcre2_compile.c and \"coptable\" and \"poptable\" in\npcre2_dfa_match.c that must be updated. */\n\n\n/* This macro defines textual names for all the opcodes. These are used only\nfor debugging, and some of them are only partial names. The macro is referenced\nonly in pcre2_printint.c, which fills out the full names in many cases (and in\nsome cases doesn't actually use these names at all). */\n\n#define OP_NAME_LIST \\\n  \"End\", \"\\\\A\", \"\\\\G\", \"\\\\K\", \"\\\\B\", \"\\\\b\", \"\\\\D\", \"\\\\d\",         \\\n  \"\\\\S\", \"\\\\s\", \"\\\\W\", \"\\\\w\", \"Any\", \"AllAny\", \"Anybyte\",         \\\n  \"notprop\", \"prop\", \"\\\\R\", \"\\\\H\", \"\\\\h\", \"\\\\V\", \"\\\\v\",           \\\n  \"extuni\",  \"\\\\Z\", \"\\\\z\",                                        \\\n  \"$\", \"$\", \"^\", \"^\", \"char\", \"chari\", \"not\", \"noti\",             \\\n  \"*\", \"*?\", \"+\", \"+?\", \"?\", \"??\",                                \\\n  \"{\", \"{\", \"{\",                                                  \\\n  \"*+\",\"++\", \"?+\", \"{\",                                           \\\n  \"*\", \"*?\", \"+\", \"+?\", \"?\", \"??\",                                \\\n  \"{\", \"{\", \"{\",                                                  \\\n  \"*+\",\"++\", \"?+\", \"{\",                                           \\\n  \"*\", \"*?\", \"+\", \"+?\", \"?\", \"??\",                                \\\n  \"{\", \"{\", \"{\",                                                  \\\n  \"*+\",\"++\", \"?+\", \"{\",                                           \\\n  \"*\", \"*?\", \"+\", \"+?\", \"?\", \"??\",                                \\\n  \"{\", \"{\", \"{\",                                                  \\\n  \"*+\",\"++\", \"?+\", \"{\",                                           \\\n  \"*\", \"*?\", \"+\", \"+?\", \"?\", \"??\", \"{\", \"{\", \"{\",                 \\\n  \"*+\",\"++\", \"?+\", \"{\",                                           \\\n  \"*\", \"*?\", \"+\", \"+?\", \"?\", \"??\", \"{\", \"{\",                      \\\n  \"*+\",\"++\", \"?+\", \"{\",                                           \\\n  \"class\", \"nclass\", \"xclass\", \"eclass\",                          \\\n  \"Ref\", \"Refi\", \"DnRef\", \"DnRefi\",                               \\\n  \"Recurse\", \"Callout\", \"CalloutStr\",                             \\\n  \"Alt\", \"Ket\", \"KetRmax\", \"KetRmin\", \"KetRpos\",                  \\\n  \"Reverse\", \"VReverse\", \"Assert\", \"Assert not\",                  \\\n  \"Assert back\", \"Assert back not\",                               \\\n  \"Non-atomic assert\", \"Non-atomic assert back\",                  \\\n  \"Scan substring\",                                               \\\n  \"Once\",                                                         \\\n  \"Script run\",                                                   \\\n  \"Bra\", \"BraPos\", \"CBra\", \"CBraPos\",                             \\\n  \"Cond\",                                                         \\\n  \"SBra\", \"SBraPos\", \"SCBra\", \"SCBraPos\",                         \\\n  \"SCond\",                                                        \\\n  \"Capture ref\", \"Capture dnref\", \"Cond rec\", \"Cond dnrec\",       \\\n  \"Cond false\", \"Cond true\",                                      \\\n  \"Brazero\", \"Braminzero\", \"Braposzero\",                          \\\n  \"*MARK\", \"*PRUNE\", \"*PRUNE\", \"*SKIP\", \"*SKIP\",                  \\\n  \"*THEN\", \"*THEN\", \"*COMMIT\", \"*COMMIT\", \"*FAIL\",                \\\n  \"*ACCEPT\", \"*ASSERT_ACCEPT\",                                    \\\n  \"Close\", \"Skip zero\", \"Define\", \"\\\\B (ucp)\", \"\\\\b (ucp)\"\n\n\n/* This macro defines the length of fixed length operations in the compiled\nregex. The lengths are used when searching for specific things, and also in the\ndebugging printing of a compiled regex. We use a macro so that it can be\ndefined close to the definitions of the opcodes themselves.\n\nAs things have been extended, some of these are no longer fixed lenths, but are\nminima instead. For example, the length of a single-character repeat may vary\nin UTF-8 mode. The code that uses this table must know about such things. */\n\n#define OP_LENGTHS \\\n  1,                             /* End                                    */ \\\n  1, 1, 1, 1, 1,                 /* \\A, \\G, \\K, \\B, \\b                     */ \\\n  1, 1, 1, 1, 1, 1,              /* \\D, \\d, \\S, \\s, \\W, \\w                 */ \\\n  1, 1, 1,                       /* Any, AllAny, Anybyte                   */ \\\n  3, 3,                          /* \\P, \\p                                 */ \\\n  1, 1, 1, 1, 1,                 /* \\R, \\H, \\h, \\V, \\v                     */ \\\n  1,                             /* \\X                                     */ \\\n  1, 1, 1, 1, 1, 1,              /* \\Z, \\z, $, $M ^, ^M                    */ \\\n  2,                             /* Char  - the minimum length             */ \\\n  2,                             /* Chari  - the minimum length            */ \\\n  2,                             /* not                                    */ \\\n  2,                             /* noti                                   */ \\\n  /* Positive single-char repeats                             ** These are */ \\\n  2, 2, 2, 2, 2, 2,              /* *, *?, +, +?, ?, ??       ** minima in */ \\\n  2+IMM2_SIZE, 2+IMM2_SIZE,      /* upto, minupto             ** mode      */ \\\n  2+IMM2_SIZE,                   /* exact                                  */ \\\n  2, 2, 2, 2+IMM2_SIZE,          /* *+, ++, ?+, upto+                      */ \\\n  2, 2, 2, 2, 2, 2,              /* *I, *?I, +I, +?I, ?I, ??I ** UTF-8     */ \\\n  2+IMM2_SIZE, 2+IMM2_SIZE,      /* upto I, minupto I                      */ \\\n  2+IMM2_SIZE,                   /* exact I                                */ \\\n  2, 2, 2, 2+IMM2_SIZE,          /* *+I, ++I, ?+I, upto+I                  */ \\\n  /* Negative single-char repeats - only for chars < 256                   */ \\\n  2, 2, 2, 2, 2, 2,              /* NOT *, *?, +, +?, ?, ??                */ \\\n  2+IMM2_SIZE, 2+IMM2_SIZE,      /* NOT upto, minupto                      */ \\\n  2+IMM2_SIZE,                   /* NOT exact                              */ \\\n  2, 2, 2, 2+IMM2_SIZE,          /* Possessive NOT *, +, ?, upto           */ \\\n  2, 2, 2, 2, 2, 2,              /* NOT *I, *?I, +I, +?I, ?I, ??I          */ \\\n  2+IMM2_SIZE, 2+IMM2_SIZE,      /* NOT upto I, minupto I                  */ \\\n  2+IMM2_SIZE,                   /* NOT exact I                            */ \\\n  2, 2, 2, 2+IMM2_SIZE,          /* Possessive NOT *I, +I, ?I, upto I      */ \\\n  /* Positive type repeats                                                 */ \\\n  2, 2, 2, 2, 2, 2,              /* Type *, *?, +, +?, ?, ??               */ \\\n  2+IMM2_SIZE, 2+IMM2_SIZE,      /* Type upto, minupto                     */ \\\n  2+IMM2_SIZE,                   /* Type exact                             */ \\\n  2, 2, 2, 2+IMM2_SIZE,          /* Possessive *+, ++, ?+, upto+           */ \\\n  /* Character class & ref repeats                                         */ \\\n  1, 1, 1, 1, 1, 1,              /* *, *?, +, +?, ?, ??                    */ \\\n  1+2*IMM2_SIZE, 1+2*IMM2_SIZE,  /* CRRANGE, CRMINRANGE                    */ \\\n  1, 1, 1, 1+2*IMM2_SIZE,        /* Possessive *+, ++, ?+, CRPOSRANGE      */ \\\n  1+(32/sizeof(PCRE2_UCHAR)),    /* CLASS                                  */ \\\n  1+(32/sizeof(PCRE2_UCHAR)),    /* NCLASS                                 */ \\\n  0,                             /* XCLASS - variable length               */ \\\n  0,                             /* ECLASS - variable length               */ \\\n  1+IMM2_SIZE,                   /* REF                                    */ \\\n  1+IMM2_SIZE+1,                 /* REFI                                   */ \\\n  1+2*IMM2_SIZE,                 /* DNREF                                  */ \\\n  1+2*IMM2_SIZE+1,               /* DNREFI                                 */ \\\n  1+LINK_SIZE,                   /* RECURSE                                */ \\\n  1+2*LINK_SIZE+1,               /* CALLOUT                                */ \\\n  0,                             /* CALLOUT_STR - variable length          */ \\\n  1+LINK_SIZE,                   /* Alt                                    */ \\\n  1+LINK_SIZE,                   /* Ket                                    */ \\\n  1+LINK_SIZE,                   /* KetRmax                                */ \\\n  1+LINK_SIZE,                   /* KetRmin                                */ \\\n  1+LINK_SIZE,                   /* KetRpos                                */ \\\n  1+IMM2_SIZE,                   /* Reverse                                */ \\\n  1+2*IMM2_SIZE,                 /* VReverse                               */ \\\n  1+LINK_SIZE,                   /* Assert                                 */ \\\n  1+LINK_SIZE,                   /* Assert not                             */ \\\n  1+LINK_SIZE,                   /* Assert behind                          */ \\\n  1+LINK_SIZE,                   /* Assert behind not                      */ \\\n  1+LINK_SIZE,                   /* NA Assert                              */ \\\n  1+LINK_SIZE,                   /* NA Assert behind                       */ \\\n  1+LINK_SIZE,                   /* Scan substring                         */ \\\n  1+LINK_SIZE,                   /* ONCE                                   */ \\\n  1+LINK_SIZE,                   /* SCRIPT_RUN                             */ \\\n  1+LINK_SIZE,                   /* BRA                                    */ \\\n  1+LINK_SIZE,                   /* BRAPOS                                 */ \\\n  1+LINK_SIZE+IMM2_SIZE,         /* CBRA                                   */ \\\n  1+LINK_SIZE+IMM2_SIZE,         /* CBRAPOS                                */ \\\n  1+LINK_SIZE,                   /* COND                                   */ \\\n  1+LINK_SIZE,                   /* SBRA                                   */ \\\n  1+LINK_SIZE,                   /* SBRAPOS                                */ \\\n  1+LINK_SIZE+IMM2_SIZE,         /* SCBRA                                  */ \\\n  1+LINK_SIZE+IMM2_SIZE,         /* SCBRAPOS                               */ \\\n  1+LINK_SIZE,                   /* SCOND                                  */ \\\n  1+IMM2_SIZE, 1+2*IMM2_SIZE,    /* CREF, DNCREF                           */ \\\n  1+IMM2_SIZE, 1+2*IMM2_SIZE,    /* RREF, DNRREF                           */ \\\n  1, 1,                          /* FALSE, TRUE                            */ \\\n  1, 1, 1,                       /* BRAZERO, BRAMINZERO, BRAPOSZERO        */ \\\n  3, 1, 3,                       /* MARK, PRUNE, PRUNE_ARG                 */ \\\n  1, 3,                          /* SKIP, SKIP_ARG                         */ \\\n  1, 3,                          /* THEN, THEN_ARG                         */ \\\n  1, 3,                          /* COMMIT, COMMIT_ARG                     */ \\\n  1, 1, 1,                       /* FAIL, ACCEPT, ASSERT_ACCEPT            */ \\\n  1+IMM2_SIZE, 1,                /* CLOSE, SKIPZERO                        */ \\\n  1,                             /* DEFINE                                 */ \\\n  1, 1                           /* \\B and \\b in UCP mode                  */\n\n/* A magic value for OP_RREF to indicate the \"any recursion\" condition. */\n\n#define RREF_ANY  0xffff\n\n/* Constants used by OP_REFI and OP_DNREFI to control matching behaviour. */\n\n#define REFI_FLAG_CASELESS_RESTRICT  0x1\n#define REFI_FLAG_TURKISH_CASING     0x2\n\n\n/* ---------- Private structures that are mode-independent. ---------- */\n\n/* Structure to hold data for custom memory management. */\n\ntypedef struct pcre2_memctl {\n  void *    (*malloc)(size_t, void *);\n  void      (*free)(void *, void *);\n  void      *memory_data;\n} pcre2_memctl;\n\n/* Structure for building a chain of open capturing subpatterns during\ncompiling, so that instructions to close them can be compiled when (*ACCEPT) is\nencountered. */\n\ntypedef struct open_capitem {\n  struct open_capitem *next;    /* Chain link */\n  uint16_t number;              /* Capture number */\n  uint16_t assert_depth;        /* Assertion depth when opened */\n} open_capitem;\n\n/* Layout of the UCP type table that translates property names into types and\ncodes. Each entry used to point directly to a name, but to reduce the number of\nrelocations in shared libraries, it now has an offset into a single string\ninstead. */\n\ntypedef struct {\n  uint16_t name_offset;\n  uint16_t type;\n  uint16_t value;\n} ucp_type_table;\n\n/* Unicode character database (UCD) record format */\n\ntypedef struct {\n  uint8_t script;     /* ucp_Arabic, etc. */\n  uint8_t chartype;   /* ucp_Cc, etc. (general categories) */\n  uint8_t gbprop;     /* ucp_gbControl, etc. (grapheme break property) */\n  uint8_t caseset;    /* offset to multichar other cases or zero */\n  int32_t other_case; /* offset to other case, or zero if none */\n  uint16_t scriptx_bidiclass; /* script extension (11 bit) and bidi class (5 bit) values */\n  uint16_t bprops;    /* binary properties offset */\n} ucd_record;\n\n/* UCD access macros */\n\n#define UCD_BLOCK_SIZE 128\n#define REAL_GET_UCD(ch) (PRIV(ucd_records) + \\\n        PRIV(ucd_stage2)[PRIV(ucd_stage1)[(int)(ch) / UCD_BLOCK_SIZE] * \\\n        UCD_BLOCK_SIZE + (int)(ch) % UCD_BLOCK_SIZE])\n\n#if PCRE2_CODE_UNIT_WIDTH == 32\n#define GET_UCD(ch) ((ch > MAX_UTF_CODE_POINT)? \\\n  PRIV(dummy_ucd_record) : REAL_GET_UCD(ch))\n#else\n#define GET_UCD(ch) REAL_GET_UCD(ch)\n#endif\n\n#define UCD_SCRIPTX_MASK 0x3ff\n#define UCD_BIDICLASS_SHIFT 11\n#define UCD_BPROPS_MASK 0xfff\n\n#define UCD_SCRIPTX_PROP(prop) ((prop)->scriptx_bidiclass & UCD_SCRIPTX_MASK)\n#define UCD_BIDICLASS_PROP(prop) ((prop)->scriptx_bidiclass >> UCD_BIDICLASS_SHIFT)\n#define UCD_BPROPS_PROP(prop) ((prop)->bprops & UCD_BPROPS_MASK)\n\n#define UCD_CHARTYPE(ch)    GET_UCD(ch)->chartype\n#define UCD_SCRIPT(ch)      GET_UCD(ch)->script\n#define UCD_CATEGORY(ch)    PRIV(ucp_gentype)[UCD_CHARTYPE(ch)]\n#define UCD_GRAPHBREAK(ch)  GET_UCD(ch)->gbprop\n#define UCD_CASESET(ch)     GET_UCD(ch)->caseset\n#define UCD_OTHERCASE(ch)   ((uint32_t)((int)ch + (int)(GET_UCD(ch)->other_case)))\n#define UCD_SCRIPTX(ch)     UCD_SCRIPTX_PROP(GET_UCD(ch))\n#define UCD_BPROPS(ch)      UCD_BPROPS_PROP(GET_UCD(ch))\n#define UCD_BIDICLASS(ch)   UCD_BIDICLASS_PROP(GET_UCD(ch))\n#define UCD_ANY_I(ch) \\\n  /* match any of the four characters 'i', 'I', U+0130, U+0131 */ \\\n  (((uint32_t)(ch) | 0x20u) == 0x69u || ((uint32_t)(ch) | 1u) == 0x0131u)\n#define UCD_DOTTED_I(ch) \\\n  ((uint32_t)(ch) == 0x69u || (uint32_t)(ch) == 0x0130u)\n#define UCD_FOLD_I_TURKISH(ch) \\\n  ((uint32_t)(ch) == 0x0130u ?   0x69u : \\\n   (uint32_t)(ch) ==   0x49u ? 0x0131u : (uint32_t)(ch))\n\n/* The \"scriptx\" and bprops fields contain offsets into vectors of 32-bit words\nthat form a bitmap representing a list of scripts or boolean properties. These\nmacros test or set a bit in the map by number. */\n\n#define MAPBIT(map,n) ((map)[(n)/32]&(1u<<((n)%32)))\n#define MAPSET(map,n) ((map)[(n)/32]|=(1u<<((n)%32)))\n\n/* Header for serialized pcre2 codes. */\n\ntypedef struct pcre2_serialized_data {\n  uint32_t magic;\n  uint32_t version;\n  uint32_t config;\n  int32_t  number_of_codes;\n} pcre2_serialized_data;\n\n\n\n/* ----------------- Items that need PCRE2_CODE_UNIT_WIDTH ----------------- */\n\n/* When this file is included by pcre2test, PCRE2_CODE_UNIT_WIDTH is defined as\n0, so the following items are omitted. */\n\n#if defined PCRE2_CODE_UNIT_WIDTH && PCRE2_CODE_UNIT_WIDTH != 0\n\n/* EBCDIC is supported only for the 8-bit library. */\n\n#if defined EBCDIC && PCRE2_CODE_UNIT_WIDTH != 8\n#error EBCDIC is not supported for the 16-bit or 32-bit libraries\n#endif\n\n/* This is the largest non-UTF code point. */\n\n#define MAX_NON_UTF_CHAR (0xffffffffU >> (32 - PCRE2_CODE_UNIT_WIDTH))\n\n/* Internal shared data tables and variables. These are used by more than one\nof the exported public functions. They have to be \"external\" in the C sense,\nbut are not part of the PCRE2 public API. Although the data for some of them is\nidentical in all libraries, they must have different names so that multiple\nlibraries can be simultaneously linked to a single application. However, UTF-8\ntables are needed only when compiling the 8-bit library. */\n\n#if PCRE2_CODE_UNIT_WIDTH == 8\nextern const int              PRIV(utf8_table1)[];\nextern const int              PRIV(utf8_table1_size);\nextern const int              PRIV(utf8_table2)[];\nextern const int              PRIV(utf8_table3)[];\nextern const uint8_t          PRIV(utf8_table4)[];\n#endif\n\n#define _pcre2_OP_lengths              PCRE2_SUFFIX(_pcre2_OP_lengths_)\n#define _pcre2_callout_end_delims      PCRE2_SUFFIX(_pcre2_callout_end_delims_)\n#define _pcre2_callout_start_delims    PCRE2_SUFFIX(_pcre2_callout_start_delims_)\n#define _pcre2_default_compile_context PCRE2_SUFFIX(_pcre2_default_compile_context_)\n#define _pcre2_default_convert_context PCRE2_SUFFIX(_pcre2_default_convert_context_)\n#define _pcre2_default_match_context   PCRE2_SUFFIX(_pcre2_default_match_context_)\n#define _pcre2_default_tables          PCRE2_SUFFIX(_pcre2_default_tables_)\n#if PCRE2_CODE_UNIT_WIDTH == 32\n#define _pcre2_dummy_ucd_record        PCRE2_SUFFIX(_pcre2_dummy_ucd_record_)\n#endif\n#define _pcre2_hspace_list             PCRE2_SUFFIX(_pcre2_hspace_list_)\n#define _pcre2_vspace_list             PCRE2_SUFFIX(_pcre2_vspace_list_)\n#define _pcre2_ucd_boolprop_sets       PCRE2_SUFFIX(_pcre2_ucd_boolprop_sets_)\n#define _pcre2_ucd_caseless_sets       PCRE2_SUFFIX(_pcre2_ucd_caseless_sets_)\n#define _pcre2_ucd_turkish_dotted_i_caseset  PCRE2_SUFFIX(_pcre2_ucd_turkish_dotted_i_caseset_)\n#define _pcre2_ucd_nocase_ranges       PCRE2_SUFFIX(_pcre2_ucd_nocase_ranges_)\n#define _pcre2_ucd_nocase_ranges_size  PCRE2_SUFFIX(_pcre2_ucd_nocase_ranges_size_)\n#define _pcre2_ucd_digit_sets          PCRE2_SUFFIX(_pcre2_ucd_digit_sets_)\n#define _pcre2_ucd_script_sets         PCRE2_SUFFIX(_pcre2_ucd_script_sets_)\n#define _pcre2_ucd_records             PCRE2_SUFFIX(_pcre2_ucd_records_)\n#define _pcre2_ucd_stage1              PCRE2_SUFFIX(_pcre2_ucd_stage1_)\n#define _pcre2_ucd_stage2              PCRE2_SUFFIX(_pcre2_ucd_stage2_)\n#define _pcre2_ucp_gbtable             PCRE2_SUFFIX(_pcre2_ucp_gbtable_)\n#define _pcre2_ucp_gentype             PCRE2_SUFFIX(_pcre2_ucp_gentype_)\n#define _pcre2_ucp_typerange           PCRE2_SUFFIX(_pcre2_ucp_typerange_)\n#define _pcre2_unicode_version         PCRE2_SUFFIX(_pcre2_unicode_version_)\n#define _pcre2_utt                     PCRE2_SUFFIX(_pcre2_utt_)\n#define _pcre2_utt_names               PCRE2_SUFFIX(_pcre2_utt_names_)\n#define _pcre2_utt_size                PCRE2_SUFFIX(_pcre2_utt_size_)\n\nextern const uint8_t                   PRIV(OP_lengths)[];\nextern const uint32_t                  PRIV(callout_end_delims)[];\nextern const uint32_t                  PRIV(callout_start_delims)[];\nextern pcre2_compile_context           PRIV(default_compile_context);\nextern pcre2_convert_context           PRIV(default_convert_context);\nextern pcre2_match_context             PRIV(default_match_context);\nextern const uint8_t                   PRIV(default_tables)[];\nextern const uint32_t                  PRIV(hspace_list)[];\nextern const uint32_t                  PRIV(vspace_list)[];\nextern const uint32_t                  PRIV(ucd_boolprop_sets)[];\nextern const uint32_t                  PRIV(ucd_caseless_sets)[];\nextern const uint32_t                  PRIV(ucd_turkish_dotted_i_caseset);\nextern const uint32_t                  PRIV(ucd_nocase_ranges)[];\nextern const uint32_t                  PRIV(ucd_nocase_ranges_size);\nextern const uint32_t                  PRIV(ucd_digit_sets)[];\nextern const uint32_t                  PRIV(ucd_script_sets)[];\nextern const ucd_record                PRIV(ucd_records)[];\n#if PCRE2_CODE_UNIT_WIDTH == 32\nextern const ucd_record                PRIV(dummy_ucd_record)[];\n#endif\nextern const uint16_t                  PRIV(ucd_stage1)[];\nextern const uint16_t                  PRIV(ucd_stage2)[];\nextern const uint32_t                  PRIV(ucp_gbtable)[];\nextern const uint32_t                  PRIV(ucp_gentype)[];\n#ifdef SUPPORT_JIT\nextern const int                       PRIV(ucp_typerange)[];\n#endif\nextern const char                     *PRIV(unicode_version);\nextern const ucp_type_table            PRIV(utt)[];\nextern const char                      PRIV(utt_names)[];\nextern const size_t                    PRIV(utt_size);\n\n/* Mode-dependent macros and hidden and private structures are defined in a\nseparate file so that pcre2test can include them at all supported widths. When\ncompiling the library, PCRE2_CODE_UNIT_WIDTH will be defined, and we can\ninclude them at the appropriate width, after setting up suffix macros for the\nprivate structures. */\n\n#define branch_chain                 PCRE2_SUFFIX(branch_chain_)\n#define compile_block                PCRE2_SUFFIX(compile_block_)\n#define dfa_match_block              PCRE2_SUFFIX(dfa_match_block_)\n#define match_block                  PCRE2_SUFFIX(match_block_)\n#define named_group                  PCRE2_SUFFIX(named_group_)\n\n#include \"pcre2_intmodedep.h\"\n\n/* Private \"external\" functions. These are internal functions that are called\nfrom modules other than the one in which they are defined. They have to be\n\"external\" in the C sense, but are not part of the PCRE2 public API. They are\nnot referenced from pcre2test, and must not be defined when no code unit width\nis available. */\n\n#define _pcre2_auto_possessify       PCRE2_SUFFIX(_pcre2_auto_possessify_)\n#define _pcre2_check_escape          PCRE2_SUFFIX(_pcre2_check_escape_)\n#define _pcre2_extuni                PCRE2_SUFFIX(_pcre2_extuni_)\n#define _pcre2_find_bracket          PCRE2_SUFFIX(_pcre2_find_bracket_)\n#define _pcre2_is_newline            PCRE2_SUFFIX(_pcre2_is_newline_)\n#define _pcre2_jit_free_rodata       PCRE2_SUFFIX(_pcre2_jit_free_rodata_)\n#define _pcre2_jit_free              PCRE2_SUFFIX(_pcre2_jit_free_)\n#define _pcre2_jit_get_size          PCRE2_SUFFIX(_pcre2_jit_get_size_)\n#define _pcre2_jit_get_target        PCRE2_SUFFIX(_pcre2_jit_get_target_)\n#define _pcre2_memctl_malloc         PCRE2_SUFFIX(_pcre2_memctl_malloc_)\n#define _pcre2_ord2utf               PCRE2_SUFFIX(_pcre2_ord2utf_)\n#define _pcre2_script_run            PCRE2_SUFFIX(_pcre2_script_run_)\n#define _pcre2_strcmp                PCRE2_SUFFIX(_pcre2_strcmp_)\n#define _pcre2_strcmp_c8             PCRE2_SUFFIX(_pcre2_strcmp_c8_)\n#define _pcre2_strcpy_c8             PCRE2_SUFFIX(_pcre2_strcpy_c8_)\n#define _pcre2_strlen                PCRE2_SUFFIX(_pcre2_strlen_)\n#define _pcre2_strncmp               PCRE2_SUFFIX(_pcre2_strncmp_)\n#define _pcre2_strncmp_c8            PCRE2_SUFFIX(_pcre2_strncmp_c8_)\n#define _pcre2_study                 PCRE2_SUFFIX(_pcre2_study_)\n#define _pcre2_valid_utf             PCRE2_SUFFIX(_pcre2_valid_utf_)\n#define _pcre2_was_newline           PCRE2_SUFFIX(_pcre2_was_newline_)\n#define _pcre2_xclass                PCRE2_SUFFIX(_pcre2_xclass_)\n#define _pcre2_eclass                PCRE2_SUFFIX(_pcre2_eclass_)\n\nextern int          _pcre2_auto_possessify(PCRE2_UCHAR *,\n                      const compile_block *);\nextern int          _pcre2_check_escape(PCRE2_SPTR *, PCRE2_SPTR, uint32_t *,\n                      int *, uint32_t, uint32_t, uint32_t, BOOL, compile_block *);\nextern PCRE2_SPTR   _pcre2_extuni(uint32_t, PCRE2_SPTR, PCRE2_SPTR, PCRE2_SPTR,\n                      BOOL, int *);\nextern PCRE2_SPTR   _pcre2_find_bracket(PCRE2_SPTR, BOOL, int);\nextern BOOL         _pcre2_is_newline(PCRE2_SPTR, uint32_t, PCRE2_SPTR,\n                      uint32_t *, BOOL);\nextern void         _pcre2_jit_free_rodata(void *, void *);\nextern void         _pcre2_jit_free(void *, pcre2_memctl *);\nextern size_t       _pcre2_jit_get_size(void *);\nconst char *        _pcre2_jit_get_target(void);\nextern void *       _pcre2_memctl_malloc(size_t, pcre2_memctl *);\nextern unsigned int _pcre2_ord2utf(uint32_t, PCRE2_UCHAR *);\nextern BOOL         _pcre2_script_run(PCRE2_SPTR, PCRE2_SPTR, BOOL);\nextern int          _pcre2_strcmp(PCRE2_SPTR, PCRE2_SPTR);\nextern int          _pcre2_strcmp_c8(PCRE2_SPTR, const char *);\nextern PCRE2_SIZE   _pcre2_strcpy_c8(PCRE2_UCHAR *, const char *);\nextern PCRE2_SIZE   _pcre2_strlen(PCRE2_SPTR);\nextern int          _pcre2_strncmp(PCRE2_SPTR, PCRE2_SPTR, size_t);\nextern int          _pcre2_strncmp_c8(PCRE2_SPTR, const char *, size_t);\nextern int          _pcre2_study(pcre2_real_code *);\nextern int          _pcre2_valid_utf(PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE *);\nextern BOOL         _pcre2_was_newline(PCRE2_SPTR, uint32_t, PCRE2_SPTR,\n                      uint32_t *, BOOL);\nextern BOOL         _pcre2_xclass(uint32_t, PCRE2_SPTR, const uint8_t *, BOOL);\nextern BOOL         _pcre2_eclass(uint32_t, PCRE2_SPTR, PCRE2_SPTR,\n                      const uint8_t *, BOOL);\n\n/* This function is needed only when memmove() is not available. */\n\n#if !defined(VPCOMPAT) && !defined(HAVE_MEMMOVE)\n#define _pcre2_memmove               PCRE2_SUFFIX(_pcre2_memmove)\nextern void *       _pcre2_memmove(void *, const void *, size_t);\n#endif\n\n#endif  /* PCRE2_CODE_UNIT_WIDTH */\n\nextern BOOL         PRIV(ckd_smul)(PCRE2_SIZE *, int, int);\n\n#include \"pcre2_util.h\"\n\n#endif  /* PCRE2_INTERNAL_H_IDEMPOTENT_GUARD */\n\n/* End of pcre2_internal.h */\n"
  },
  {
    "path": "Libraries/PCRE/pcre2_intmodedep.h",
    "content": "/*************************************************\n*      Perl-Compatible Regular Expressions       *\n*************************************************/\n\n/* PCRE is a library of functions to support regular expressions whose syntax\nand semantics are as close as possible to those of the Perl 5 language.\n\n                       Written by Philip Hazel\n     Original API code Copyright (c) 1997-2012 University of Cambridge\n          New API code Copyright (c) 2016-2024 University of Cambridge\n\n-----------------------------------------------------------------------------\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n    * Redistributions of source code must retain the above copyright notice,\n      this list of conditions and the following disclaimer.\n\n    * Redistributions in binary form must reproduce the above copyright\n      notice, this list of conditions and the following disclaimer in the\n      documentation and/or other materials provided with the distribution.\n\n    * Neither the name of the University of Cambridge nor the names of its\n      contributors may be used to endorse or promote products derived from\n      this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\nLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\nCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\nSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\nINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\nCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGE.\n-----------------------------------------------------------------------------\n*/\n\n\n/* This module contains mode-dependent macro and structure definitions. The\nfile is #included by pcre2_internal.h if PCRE2_CODE_UNIT_WIDTH is defined.\nThese mode-dependent items are kept in a separate file so that they can also be\n#included multiple times for different code unit widths by pcre2test in order\nto have access to the hidden structures at all supported widths.\n\nSome of the mode-dependent macros are required at different widths for\ndifferent parts of the pcre2test code (in particular, the included\npcre2_printint.c file). We undefine them here so that they can be re-defined for\nmultiple inclusions. Not all of these are used in pcre2test, but it's easier\njust to undefine them all. */\n\n#undef ACROSSCHAR\n#undef BACKCHAR\n#undef BYTES2CU\n#undef CHMAX_255\n#undef CU2BYTES\n#undef FORWARDCHAR\n#undef FORWARDCHARTEST\n#undef GET\n#undef GET2\n#undef GETCHAR\n#undef GETCHARINC\n#undef GETCHARINCTEST\n#undef GETCHARLEN\n#undef GETCHARLENTEST\n#undef GETCHARTEST\n#undef GET_EXTRALEN\n#undef HAS_EXTRALEN\n#undef IMM2_SIZE\n#undef MAX_255\n#undef MAX_MARK\n#undef MAX_PATTERN_SIZE\n#undef MAX_UTF_SINGLE_CU\n#undef NOT_FIRSTCU\n#undef PUT\n#undef PUT2\n#undef PUT2INC\n#undef PUTCHAR\n#undef PUTINC\n#undef TABLE_GET\n\n\n\n/* -------------------------- MACROS ----------------------------- */\n\n/* PCRE keeps offsets in its compiled code as at least 16-bit quantities\n(always stored in big-endian order in 8-bit mode) by default. These are used,\nfor example, to link from the start of a subpattern to its alternatives and its\nend. The use of 16 bits per offset limits the size of an 8-bit compiled regex\nto around 64K, which is big enough for almost everybody. However, I received a\nrequest for an even bigger limit. For this reason, and also to make the code\neasier to maintain, the storing and loading of offsets from the compiled code\nunit string is now handled by the macros that are defined here.\n\nThe macros are controlled by the value of LINK_SIZE. This defaults to 2, but\nvalues of 3 or 4 are also supported. */\n\n/* ------------------- 8-bit support  ------------------ */\n\n#if PCRE2_CODE_UNIT_WIDTH == 8\n\n#if LINK_SIZE == 2\n#define PUT(a,n,d)   \\\n  (a[n] = (PCRE2_UCHAR)((d) >> 8)), \\\n  (a[(n)+1] = (PCRE2_UCHAR)((d) & 255))\n#define GET(a,n) \\\n  (unsigned int)(((a)[n] << 8) | (a)[(n)+1])\n#define MAX_PATTERN_SIZE (1 << 16)\n\n#elif LINK_SIZE == 3\n#define PUT(a,n,d)       \\\n  (a[n] = (PCRE2_UCHAR)((d) >> 16)),    \\\n  (a[(n)+1] = (PCRE2_UCHAR)((d) >> 8)), \\\n  (a[(n)+2] = (PCRE2_UCHAR)((d) & 255))\n#define GET(a,n) \\\n  (unsigned int)(((a)[n] << 16) | ((a)[(n)+1] << 8) | (a)[(n)+2])\n#define MAX_PATTERN_SIZE (1 << 24)\n\n#elif LINK_SIZE == 4\n#define PUT(a,n,d)        \\\n  (a[n] = (PCRE2_UCHAR)((d) >> 24)),     \\\n  (a[(n)+1] = (PCRE2_UCHAR)((d) >> 16)), \\\n  (a[(n)+2] = (PCRE2_UCHAR)((d) >> 8)),  \\\n  (a[(n)+3] = (PCRE2_UCHAR)((d) & 255))\n#define GET(a,n) \\\n  (unsigned int)(((a)[n] << 24) | ((a)[(n)+1] << 16) | ((a)[(n)+2] << 8) | (a)[(n)+3])\n#define MAX_PATTERN_SIZE (1 << 30)   /* Keep it positive */\n\n#else\n#error LINK_SIZE must be 2, 3, or 4\n#endif\n\n\n/* ------------------- 16-bit support  ------------------ */\n\n#elif PCRE2_CODE_UNIT_WIDTH == 16\n\n#if LINK_SIZE == 2\n#undef LINK_SIZE\n#define LINK_SIZE 1\n#define PUT(a,n,d)   \\\n  (a[n] = (PCRE2_UCHAR)(d))\n#define GET(a,n) \\\n  (a[n])\n#define MAX_PATTERN_SIZE (1 << 16)\n\n#elif LINK_SIZE == 3 || LINK_SIZE == 4\n#undef LINK_SIZE\n#define LINK_SIZE 2\n#define PUT(a,n,d)   \\\n  (a[n] = (PCRE2_UCHAR)((d) >> 16)), \\\n  (a[(n)+1] = (PCRE2_UCHAR)((d) & 65535))\n#define GET(a,n) \\\n  (unsigned int)(((a)[n] << 16) | (a)[(n)+1])\n#define MAX_PATTERN_SIZE (1 << 30)  /* Keep it positive */\n\n#else\n#error LINK_SIZE must be 2, 3, or 4\n#endif\n\n\n/* ------------------- 32-bit support  ------------------ */\n\n#elif PCRE2_CODE_UNIT_WIDTH == 32\n#undef LINK_SIZE\n#define LINK_SIZE 1\n#define PUT(a,n,d)   \\\n  (a[n] = (d))\n#define GET(a,n) \\\n  (a[n])\n#define MAX_PATTERN_SIZE (1 << 30)  /* Keep it positive */\n\n#else\n#error Unsupported compiling mode\n#endif\n\n\n/* --------------- Other mode-specific macros ----------------- */\n\n/* PCRE uses some other (at least) 16-bit quantities that do not change when\nthe size of offsets changes. There are used for repeat counts and for other\nthings such as capturing parenthesis numbers in back references.\n\nDefine the number of code units required to hold a 16-bit count/offset, and\nmacros to load and store such a value. For reasons that I do not understand,\nthe expression in the 8-bit GET2 macro is treated by gcc as a signed\nexpression, even when a is declared as unsigned. It seems that any kind of\narithmetic results in a signed value. Hence the cast. */\n\n#if PCRE2_CODE_UNIT_WIDTH == 8\n#define IMM2_SIZE 2\n#define GET2(a,n) (unsigned int)(((a)[n] << 8) | (a)[(n)+1])\n#define PUT2(a,n,d) a[n] = (d) >> 8, a[(n)+1] = (d) & 255\n\n#else  /* Code units are 16 or 32 bits */\n#define IMM2_SIZE 1\n#define GET2(a,n) a[n]\n#define PUT2(a,n,d) a[n] = d\n#endif\n\n/* Other macros that are different for 8-bit mode. The MAX_255 macro checks\nwhether its argument, which is assumed to be one code unit, is less than 256.\nThe CHMAX_255 macro does not assume one code unit. The maximum length of a MARK\nname must fit in one code unit; currently it is set to 255 or 65535. The\nTABLE_GET macro is used to access elements of tables containing exactly 256\nitems. Its argument is a code unit. When code points can be greater than 255, a\ncheck is needed before accessing these tables. */\n\n#if PCRE2_CODE_UNIT_WIDTH == 8\n#define MAX_255(c) TRUE\n#define MAX_MARK ((1u << 8) - 1)\n#define TABLE_GET(c, table, default) ((table)[c])\n#ifdef SUPPORT_UNICODE\n#define SUPPORT_WIDE_CHARS\n#define CHMAX_255(c) ((c) <= 255u)\n#else\n#define CHMAX_255(c) TRUE\n#endif  /* SUPPORT_UNICODE */\n\n#else  /* Code units are 16 or 32 bits */\n#define CHMAX_255(c) ((c) <= 255u)\n#define MAX_255(c) ((c) <= 255u)\n#define MAX_MARK ((1u << 16) - 1)\n#define SUPPORT_WIDE_CHARS\n#define TABLE_GET(c, table, default) (MAX_255(c)? ((table)[c]):(default))\n#endif\n\n\n/* ----------------- Character-handling macros ----------------- */\n\n/* There is a proposed future special \"UTF-21\" mode, in which only the lowest\n21 bits of a 32-bit character are interpreted as UTF, with the remaining 11\nhigh-order bits available to the application for other uses. In preparation for\nthe future implementation of this mode, there are macros that load a data item\nand, if in this special mode, mask it to 21 bits. These macros all have names\nstarting with UCHAR21. In all other modes, including the normal 32-bit\nlibrary, the macros all have the same simple definitions. When the new mode is\nimplemented, it is expected that these definitions will be varied appropriately\nusing #ifdef when compiling the library that supports the special mode. */\n\n#define UCHAR21(eptr)        (*(eptr))\n#define UCHAR21TEST(eptr)    (*(eptr))\n#define UCHAR21INC(eptr)     (*(eptr)++)\n#define UCHAR21INCTEST(eptr) (*(eptr)++)\n\n/* When UTF encoding is being used, a character is no longer just a single\nbyte in 8-bit mode or a single short in 16-bit mode. The macros for character\nhandling generate simple sequences when used in the basic mode, and more\ncomplicated ones for UTF characters. GETCHARLENTEST and other macros are not\nused when UTF is not supported. To make sure they can never even appear when\nUTF support is omitted, we don't even define them. */\n\n#ifndef SUPPORT_UNICODE\n\n/* #define MAX_UTF_SINGLE_CU */\n/* #define HAS_EXTRALEN(c) */\n/* #define GET_EXTRALEN(c) */\n/* #define NOT_FIRSTCU(c) */\n#define GETCHAR(c, eptr) c = *eptr;\n#define GETCHARTEST(c, eptr) c = *eptr;\n#define GETCHARINC(c, eptr) c = *eptr++;\n#define GETCHARINCTEST(c, eptr) c = *eptr++;\n#define GETCHARLEN(c, eptr, len) c = *eptr;\n#define PUTCHAR(c, p) (*p = c, 1)\n/* #define GETCHARLENTEST(c, eptr, len) */\n/* #define BACKCHAR(eptr) */\n/* #define FORWARDCHAR(eptr) */\n/* #define FORWARCCHARTEST(eptr,end) */\n/* #define ACROSSCHAR(condition, eptr, action) */\n\n#else   /* SUPPORT_UNICODE */\n\n/* ------------------- 8-bit support  ------------------ */\n\n#if PCRE2_CODE_UNIT_WIDTH == 8\n#define MAYBE_UTF_MULTI          /* UTF chars may use multiple code units */\n\n/* The largest UTF code point that can be encoded as a single code unit. */\n\n#define MAX_UTF_SINGLE_CU 127\n\n/* Tests whether the code point needs extra characters to decode. */\n\n#define HAS_EXTRALEN(c) HASUTF8EXTRALEN(c)\n\n/* Returns with the additional number of characters if IS_MULTICHAR(c) is TRUE.\nOtherwise it has an undefined behaviour. */\n\n#define GET_EXTRALEN(c) (PRIV(utf8_table4)[(c) & 0x3fu])\n\n/* Returns TRUE, if the given value is not the first code unit of a UTF\nsequence. */\n\n#define NOT_FIRSTCU(c) (((c) & 0xc0u) == 0x80u)\n\n/* Get the next UTF-8 character, not advancing the pointer. This is called when\nwe know we are in UTF-8 mode. */\n\n#define GETCHAR(c, eptr) \\\n  c = *eptr; \\\n  if (c >= 0xc0u) GETUTF8(c, eptr);\n\n/* Get the next UTF-8 character, testing for UTF-8 mode, and not advancing the\npointer. */\n\n#define GETCHARTEST(c, eptr) \\\n  c = *eptr; \\\n  if (utf && c >= 0xc0u) GETUTF8(c, eptr);\n\n/* Get the next UTF-8 character, advancing the pointer. This is called when we\nknow we are in UTF-8 mode. */\n\n#define GETCHARINC(c, eptr) \\\n  c = *eptr++; \\\n  if (c >= 0xc0u) GETUTF8INC(c, eptr);\n\n/* Get the next character, testing for UTF-8 mode, and advancing the pointer.\nThis is called when we don't know if we are in UTF-8 mode. */\n\n#define GETCHARINCTEST(c, eptr) \\\n  c = *eptr++; \\\n  if (utf && c >= 0xc0u) GETUTF8INC(c, eptr);\n\n/* Get the next UTF-8 character, not advancing the pointer, incrementing length\nif there are extra bytes. This is called when we know we are in UTF-8 mode. */\n\n#define GETCHARLEN(c, eptr, len) \\\n  c = *eptr; \\\n  if (c >= 0xc0u) GETUTF8LEN(c, eptr, len);\n\n/* Get the next UTF-8 character, testing for UTF-8 mode, not advancing the\npointer, incrementing length if there are extra bytes. This is called when we\ndo not know if we are in UTF-8 mode. */\n\n#define GETCHARLENTEST(c, eptr, len) \\\n  c = *eptr; \\\n  if (utf && c >= 0xc0u) GETUTF8LEN(c, eptr, len);\n\n/* If the pointer is not at the start of a character, move it back until\nit is. This is called only in UTF-8 mode - we don't put a test within the macro\nbecause almost all calls are already within a block of UTF-8 only code. */\n\n#define BACKCHAR(eptr) while((*eptr & 0xc0u) == 0x80u) eptr--\n\n/* Same as above, just in the other direction. */\n#define FORWARDCHAR(eptr) while((*eptr & 0xc0u) == 0x80u) eptr++\n#define FORWARDCHARTEST(eptr,end) while(eptr < end && (*eptr & 0xc0u) == 0x80u) eptr++\n\n/* Same as above, but it allows a fully customizable form. */\n#define ACROSSCHAR(condition, eptr, action) \\\n  while((condition) && ((*eptr) & 0xc0u) == 0x80u) action\n\n/* Deposit a character into memory, returning the number of code units. */\n\n#define PUTCHAR(c, p) ((utf && c > MAX_UTF_SINGLE_CU)? \\\n  PRIV(ord2utf)(c,p) : (*p = c, 1))\n\n\n/* ------------------- 16-bit support  ------------------ */\n\n#elif PCRE2_CODE_UNIT_WIDTH == 16\n#define MAYBE_UTF_MULTI          /* UTF chars may use multiple code units */\n\n/* The largest UTF code point that can be encoded as a single code unit. */\n\n#define MAX_UTF_SINGLE_CU 65535\n\n/* Tests whether the code point needs extra characters to decode. */\n\n#define HAS_EXTRALEN(c) (((c) & 0xfc00u) == 0xd800u)\n\n/* Returns with the additional number of characters if IS_MULTICHAR(c) is TRUE.\nOtherwise it has an undefined behaviour. */\n\n#define GET_EXTRALEN(c) 1\n\n/* Returns TRUE, if the given value is not the first code unit of a UTF\nsequence. */\n\n#define NOT_FIRSTCU(c) (((c) & 0xfc00u) == 0xdc00u)\n\n/* Base macro to pick up the low surrogate of a UTF-16 character, not\nadvancing the pointer. */\n\n#define GETUTF16(c, eptr) \\\n   { c = (((c & 0x3ffu) << 10) | (eptr[1] & 0x3ffu)) + 0x10000u; }\n\n/* Get the next UTF-16 character, not advancing the pointer. This is called when\nwe know we are in UTF-16 mode. */\n\n#define GETCHAR(c, eptr) \\\n  c = *eptr; \\\n  if ((c & 0xfc00u) == 0xd800u) GETUTF16(c, eptr);\n\n/* Get the next UTF-16 character, testing for UTF-16 mode, and not advancing the\npointer. */\n\n#define GETCHARTEST(c, eptr) \\\n  c = *eptr; \\\n  if (utf && (c & 0xfc00u) == 0xd800u) GETUTF16(c, eptr);\n\n/* Base macro to pick up the low surrogate of a UTF-16 character, advancing\nthe pointer. */\n\n#define GETUTF16INC(c, eptr) \\\n   { c = (((c & 0x3ffu) << 10) | (*eptr++ & 0x3ffu)) + 0x10000u; }\n\n/* Get the next UTF-16 character, advancing the pointer. This is called when we\nknow we are in UTF-16 mode. */\n\n#define GETCHARINC(c, eptr) \\\n  c = *eptr++; \\\n  if ((c & 0xfc00u) == 0xd800u) GETUTF16INC(c, eptr);\n\n/* Get the next character, testing for UTF-16 mode, and advancing the pointer.\nThis is called when we don't know if we are in UTF-16 mode. */\n\n#define GETCHARINCTEST(c, eptr) \\\n  c = *eptr++; \\\n  if (utf && (c & 0xfc00u) == 0xd800u) GETUTF16INC(c, eptr);\n\n/* Base macro to pick up the low surrogate of a UTF-16 character, not\nadvancing the pointer, incrementing the length. */\n\n#define GETUTF16LEN(c, eptr, len) \\\n   { c = (((c & 0x3ffu) << 10) | (eptr[1] & 0x3ffu)) + 0x10000u; len++; }\n\n/* Get the next UTF-16 character, not advancing the pointer, incrementing\nlength if there is a low surrogate. This is called when we know we are in\nUTF-16 mode. */\n\n#define GETCHARLEN(c, eptr, len) \\\n  c = *eptr; \\\n  if ((c & 0xfc00u) == 0xd800u) GETUTF16LEN(c, eptr, len);\n\n/* Get the next UTF-16 character, testing for UTF-16 mode, not advancing the\npointer, incrementing length if there is a low surrogate. This is called when\nwe do not know if we are in UTF-16 mode. */\n\n#define GETCHARLENTEST(c, eptr, len) \\\n  c = *eptr; \\\n  if (utf && (c & 0xfc00u) == 0xd800u) GETUTF16LEN(c, eptr, len);\n\n/* If the pointer is not at the start of a character, move it back until\nit is. This is called only in UTF-16 mode - we don't put a test within the\nmacro because almost all calls are already within a block of UTF-16 only\ncode. */\n\n#define BACKCHAR(eptr) if ((*eptr & 0xfc00u) == 0xdc00u) eptr--\n\n/* Same as above, just in the other direction. */\n#define FORWARDCHAR(eptr) if ((*eptr & 0xfc00u) == 0xdc00u) eptr++\n#define FORWARDCHARTEST(eptr,end) if (eptr < end && (*eptr & 0xfc00u) == 0xdc00u) eptr++\n\n/* Same as above, but it allows a fully customizable form. */\n#define ACROSSCHAR(condition, eptr, action) \\\n  if ((condition) && ((*eptr) & 0xfc00u) == 0xdc00u) action\n\n/* Deposit a character into memory, returning the number of code units. */\n\n#define PUTCHAR(c, p) ((utf && c > MAX_UTF_SINGLE_CU)? \\\n  PRIV(ord2utf)(c,p) : (*p = c, 1))\n\n\n/* ------------------- 32-bit support  ------------------ */\n\n#else\n\n/* These are trivial for the 32-bit library, since all UTF-32 characters fit\ninto one PCRE2_UCHAR unit. */\n\n#define MAX_UTF_SINGLE_CU (0x10ffffu)\n#define HAS_EXTRALEN(c) (0)\n#define GET_EXTRALEN(c) (0)\n#define NOT_FIRSTCU(c) (0)\n\n/* Get the next UTF-32 character, not advancing the pointer. This is called when\nwe know we are in UTF-32 mode. */\n\n#define GETCHAR(c, eptr) \\\n  c = *(eptr);\n\n/* Get the next UTF-32 character, testing for UTF-32 mode, and not advancing the\npointer. */\n\n#define GETCHARTEST(c, eptr) \\\n  c = *(eptr);\n\n/* Get the next UTF-32 character, advancing the pointer. This is called when we\nknow we are in UTF-32 mode. */\n\n#define GETCHARINC(c, eptr) \\\n  c = *((eptr)++);\n\n/* Get the next character, testing for UTF-32 mode, and advancing the pointer.\nThis is called when we don't know if we are in UTF-32 mode. */\n\n#define GETCHARINCTEST(c, eptr) \\\n  c = *((eptr)++);\n\n/* Get the next UTF-32 character, not advancing the pointer, not incrementing\nlength (since all UTF-32 is of length 1). This is called when we know we are in\nUTF-32 mode. */\n\n#define GETCHARLEN(c, eptr, len) \\\n  GETCHAR(c, eptr)\n\n/* Get the next UTF-32character, testing for UTF-32 mode, not advancing the\npointer, not incrementing the length (since all UTF-32 is of length 1).\nThis is called when we do not know if we are in UTF-32 mode. */\n\n#define GETCHARLENTEST(c, eptr, len) \\\n  GETCHARTEST(c, eptr)\n\n/* If the pointer is not at the start of a character, move it back until\nit is. This is called only in UTF-32 mode - we don't put a test within the\nmacro because almost all calls are already within a block of UTF-32 only\ncode.\n\nThese are all no-ops since all UTF-32 characters fit into one PCRE2_UCHAR. */\n\n#define BACKCHAR(eptr) do { } while (0)\n\n/* Same as above, just in the other direction. */\n\n#define FORWARDCHAR(eptr) do { } while (0)\n#define FORWARDCHARTEST(eptr,end) do { } while (0)\n\n/* Same as above, but it allows a fully customizable form. */\n\n#define ACROSSCHAR(condition, eptr, action) do { } while (0)\n\n/* Deposit a character into memory, returning the number of code units. */\n\n#define PUTCHAR(c, p) (*p = c, 1)\n\n#endif  /* UTF-32 character handling */\n#endif  /* SUPPORT_UNICODE */\n\n\n/* Mode-dependent macros that have the same definition in all modes. */\n\n#define CU2BYTES(x)     ((x)*((PCRE2_CODE_UNIT_WIDTH/8)))\n#define BYTES2CU(x)     ((x)/((PCRE2_CODE_UNIT_WIDTH/8)))\n#define PUTINC(a,n,d)   PUT(a,n,d), a += LINK_SIZE\n#define PUT2INC(a,n,d)  PUT2(a,n,d), a += IMM2_SIZE\n\n\n/* ----------------------- HIDDEN STRUCTURES ----------------------------- */\n\n/* NOTE: All these structures *must* start with a pcre2_memctl structure. The\ncode that uses them is simpler because it assumes this. */\n\n/* The real general context structure. At present it holds only data for custom\nmemory control. */\n\n/* WARNING: if this is ever changed, code in pcre2_substitute.c will have to be\nchanged because it builds a general context \"by hand\" in order to avoid the\nmalloc() call in pcre2_general_context)_create(). There is also code in\npcre2_match.c that makes the same assumption. */\n\ntypedef struct pcre2_real_general_context {\n  pcre2_memctl memctl;\n} pcre2_real_general_context;\n\n/* The real compile context structure */\n\ntypedef struct pcre2_real_compile_context {\n  pcre2_memctl memctl;\n  int (*stack_guard)(uint32_t, void *);\n  void *stack_guard_data;\n  const uint8_t *tables;\n  PCRE2_SIZE max_pattern_length;\n  PCRE2_SIZE max_pattern_compiled_length;\n  uint16_t bsr_convention;\n  uint16_t newline_convention;\n  uint32_t parens_nest_limit;\n  uint32_t extra_options;\n  uint32_t max_varlookbehind;\n  uint32_t optimization_flags;\n} pcre2_real_compile_context;\n\n/* The real match context structure. */\n\ntypedef struct pcre2_real_match_context {\n  pcre2_memctl memctl;\n#ifdef SUPPORT_JIT\n  pcre2_jit_callback jit_callback;\n  void *jit_callback_data;\n#endif\n  int        (*callout)(pcre2_callout_block *, void *);\n  void        *callout_data;\n  int        (*substitute_callout)(pcre2_substitute_callout_block *, void *);\n  void        *substitute_callout_data;\n  PCRE2_SIZE (*substitute_case_callout)(PCRE2_SPTR, PCRE2_SIZE, PCRE2_UCHAR *,\n                                        PCRE2_SIZE, int, void *);\n  void        *substitute_case_callout_data;\n  PCRE2_SIZE offset_limit;\n  uint32_t heap_limit;\n  uint32_t match_limit;\n  uint32_t depth_limit;\n} pcre2_real_match_context;\n\n/* The real convert context structure. */\n\ntypedef struct pcre2_real_convert_context {\n  pcre2_memctl memctl;\n  uint32_t glob_separator;\n  uint32_t glob_escape;\n} pcre2_real_convert_context;\n\n/* The real compiled code structure. The type for the blocksize field is\ndefined specially because it is required in pcre2_serialize_decode() when\ncopying the size from possibly unaligned memory into a variable of the same\ntype. Use a macro rather than a typedef to avoid compiler warnings when this\nfile is included multiple times by pcre2test. LOOKBEHIND_MAX specifies the\nlargest lookbehind that is supported. (OP_REVERSE and OP_VREVERSE in a pattern\nhave 16-bit arguments in 8-bit and 16-bit modes, so we need no more than a\n16-bit field here.) */\n\n#undef  CODE_BLOCKSIZE_TYPE\n#define CODE_BLOCKSIZE_TYPE PCRE2_SIZE\n\n#undef  LOOKBEHIND_MAX\n#define LOOKBEHIND_MAX UINT16_MAX\n\ntypedef struct pcre2_real_code {\n  pcre2_memctl memctl;            /* Memory control fields */\n  const uint8_t *tables;          /* The character tables */\n  void    *executable_jit;        /* Pointer to JIT code */\n  uint8_t  start_bitmap[32];      /* Bitmap for starting code unit < 256 */\n  CODE_BLOCKSIZE_TYPE blocksize;  /* Total (bytes) that was malloc-ed */\n  CODE_BLOCKSIZE_TYPE code_start; /* Byte code start offset */\n  uint32_t magic_number;          /* Paranoid and endianness check */\n  uint32_t compile_options;       /* Options passed to pcre2_compile() */\n  uint32_t overall_options;       /* Options after processing the pattern */\n  uint32_t extra_options;         /* Taken from compile_context */\n  uint32_t flags;                 /* Various state flags */\n  uint32_t limit_heap;            /* Limit set in the pattern */\n  uint32_t limit_match;           /* Limit set in the pattern */\n  uint32_t limit_depth;           /* Limit set in the pattern */\n  uint32_t first_codeunit;        /* Starting code unit */\n  uint32_t last_codeunit;         /* This codeunit must be seen */\n  uint16_t bsr_convention;        /* What \\R matches */\n  uint16_t newline_convention;    /* What is a newline? */\n  uint16_t max_lookbehind;        /* Longest lookbehind (characters) */\n  uint16_t minlength;             /* Minimum length of match */\n  uint16_t top_bracket;           /* Highest numbered group */\n  uint16_t top_backref;           /* Highest numbered back reference */\n  uint16_t name_entry_size;       /* Size (code units) of table entries */\n  uint16_t name_count;            /* Number of name entries in the table */\n  uint32_t optimization_flags;    /* Optimizations enabled at compile time */\n} pcre2_real_code;\n\n/* The real match data structure. Define ovector as large as it can ever\nactually be so that array bound checkers don't grumble. Memory for this\nstructure is obtained by calling pcre2_match_data_create(), which sets the size\nas the offset of ovector plus a pair of elements for each capturable string, so\nthe size varies from call to call. As the maximum number of capturing\nsubpatterns is 65535 we must allow for 65536 strings to include the overall\nmatch. (See also the heapframe structure below.) */\n\nstruct heapframe;  /* Forward reference */\n\ntypedef struct pcre2_real_match_data {\n  pcre2_memctl     memctl;           /* Memory control fields */\n  const pcre2_real_code *code;       /* The pattern used for the match */\n  PCRE2_SPTR       subject;          /* The subject that was matched */\n  PCRE2_SPTR       mark;             /* Pointer to last mark */\n  struct heapframe *heapframes;      /* Backtracking frames heap memory */\n  PCRE2_SIZE       heapframes_size;  /* Malloc-ed size */\n  PCRE2_SIZE       subject_length;   /* Subject length */\n  PCRE2_SIZE       leftchar;         /* Offset to leftmost code unit */\n  PCRE2_SIZE       rightchar;        /* Offset to rightmost code unit */\n  PCRE2_SIZE       startchar;        /* Offset to starting code unit */\n  uint8_t          matchedby;        /* Type of match (normal, JIT, DFA) */\n  uint8_t          flags;            /* Various flags */\n  uint16_t         oveccount;        /* Number of pairs */\n  int              rc;               /* The return code from the match */\n  PCRE2_SIZE       ovector[131072];  /* Must be last in the structure */\n} pcre2_real_match_data;\n\n\n/* ----------------------- PRIVATE STRUCTURES ----------------------------- */\n\n/* These structures are not needed for pcre2test. */\n\n#ifndef PCRE2_PCRE2TEST\n\n/* Structures for checking for mutual function recursion when scanning compiled\nor parsed code. */\n\ntypedef struct recurse_check {\n  struct recurse_check *prev;\n  PCRE2_SPTR group;\n} recurse_check;\n\ntypedef struct parsed_recurse_check {\n  struct parsed_recurse_check *prev;\n  uint32_t *groupptr;\n} parsed_recurse_check;\n\n/* Structure for building a cache when filling in pattern recursion offsets. */\n\ntypedef struct recurse_cache {\n  PCRE2_SPTR group;\n  int groupnumber;\n} recurse_cache;\n\n/* Structure for maintaining a chain of pointers to the currently incomplete\nbranches, for testing for left recursion while compiling. */\n\ntypedef struct branch_chain {\n  struct branch_chain *outer;\n  PCRE2_UCHAR *current_branch;\n} branch_chain;\n\n/* Structure for building a list of named groups during the first pass of\ncompiling. */\n\ntypedef struct named_group {\n  PCRE2_SPTR   name;          /* Points to the name in the pattern */\n  uint32_t     number;        /* Group number */\n  uint16_t     length;        /* Length of the name */\n  uint16_t     isdup;         /* TRUE if a duplicate */\n} named_group;\n\n/* Structure for caching sorted ranges. This improves the performance\nof translating META code to byte code. */\n\ntypedef struct class_ranges {\n  struct class_ranges *next;       /* Next class ranges */\n  size_t char_lists_size;          /* Total size of encoded char lists */\n  size_t char_lists_start;         /* Start offset of encoded char lists */\n  uint16_t range_list_size;        /* Size of ranges array */\n  uint16_t char_lists_types;       /* The XCL_LIST header of char lists */\n  /* Followed by the list of ranges (start/end pairs) */\n} class_ranges;\n\ntypedef union class_bits_storage {\n  uint8_t classbits[32];\n  uint32_t classwords[8];\n} class_bits_storage;\n\n/* Structure for passing \"static\" information around between the functions\ndoing the compiling, so that they are thread-safe. */\n\ntypedef struct compile_block {\n  pcre2_real_compile_context *cx;  /* Points to the compile context */\n  const uint8_t *lcc;              /* Points to lower casing table */\n  const uint8_t *fcc;              /* Points to case-flipping table */\n  const uint8_t *cbits;            /* Points to character type table */\n  const uint8_t *ctypes;           /* Points to table of type maps */\n  PCRE2_UCHAR *start_workspace;    /* The start of working space */\n  PCRE2_UCHAR *start_code;         /* The start of the compiled code */\n  PCRE2_SPTR start_pattern;        /* The start of the pattern */\n  PCRE2_SPTR end_pattern;          /* The end of the pattern */\n  PCRE2_UCHAR *name_table;         /* The name/number table */\n  PCRE2_SIZE workspace_size;       /* Size of workspace */\n  PCRE2_SIZE small_ref_offset[10]; /* Offsets for \\1 to \\9 */\n  PCRE2_SIZE erroroffset;          /* Offset of error in pattern */\n  class_bits_storage classbits;    /* Temporary store for classbits */\n  uint16_t names_found;            /* Number of entries so far */\n  uint16_t name_entry_size;        /* Size of each entry */\n  uint16_t parens_depth;           /* Depth of nested parentheses */\n  uint16_t assert_depth;           /* Depth of nested assertions */\n  named_group *named_groups;       /* Points to vector in pre-compile */\n  uint32_t named_group_list_size;  /* Number of entries in the list */\n  uint32_t external_options;       /* External (initial) options */\n  uint32_t external_flags;         /* External flag bits to be set */\n  uint32_t bracount;               /* Count of capturing parentheses */\n  uint32_t lastcapture;            /* Last capture encountered */\n  uint32_t *parsed_pattern;        /* Parsed pattern buffer */\n  uint32_t *parsed_pattern_end;    /* Parsed pattern should not get here */\n  uint32_t *groupinfo;             /* Group info vector */\n  uint32_t top_backref;            /* Maximum back reference */\n  uint32_t backref_map;            /* Bitmap of low back refs */\n  uint32_t nltype;                 /* Newline type */\n  uint32_t nllen;                  /* Newline string length */\n  PCRE2_UCHAR nl[4];               /* Newline string when fixed length */\n  uint8_t class_op_used[ECLASS_NEST_LIMIT]; /* Operation used for\n                                               extended classes */\n  uint32_t req_varyopt;            /* \"After variable item\" flag for reqbyte */\n  uint32_t max_varlookbehind;      /* Limit for variable lookbehinds */\n  int  max_lookbehind;             /* Maximum lookbehind encountered (characters) */\n  BOOL had_accept;                 /* (*ACCEPT) encountered */\n  BOOL had_pruneorskip;            /* (*PRUNE) or (*SKIP) encountered */\n  BOOL had_recurse;                /* Had a pattern recursion or subroutine call */\n  BOOL dupnames;                   /* Duplicate names exist */\n#ifdef SUPPORT_WIDE_CHARS\n  class_ranges *cranges;           /* First class range. */\n  class_ranges *next_cranges;      /* Next class range. */\n  size_t char_lists_size;          /* Current size of character lists */\n#endif\n} compile_block;\n\n/* Structure for keeping the properties of the in-memory stack used\nby the JIT matcher. */\n\ntypedef struct pcre2_real_jit_stack {\n  pcre2_memctl memctl;\n  void* stack;\n} pcre2_real_jit_stack;\n\n/* Structure for items in a linked list that represents an explicit recursive\ncall within the pattern when running pcre2_dfa_match(). */\n\ntypedef struct dfa_recursion_info {\n  struct dfa_recursion_info *prevrec;\n  PCRE2_SPTR subject_position;\n  PCRE2_SPTR last_used_ptr;\n  uint32_t group_num;\n} dfa_recursion_info;\n\n/* Structure for \"stack\" frames that are used for remembering backtracking\npositions during matching. As these are used in a vector, with the ovector item\nbeing extended, the size of the structure must be a multiple of PCRE2_SIZE. The\nonly way to check this at compile time is to force an error by generating an\narray with a negative size. By putting this in a typedef (which is never used),\nwe don't generate any code when all is well. */\n\ntypedef struct heapframe {\n\n  /* The first set of fields are variables that have to be preserved over calls\n  to RRMATCH(), but which do not need to be copied to new frames. */\n\n  PCRE2_SPTR ecode;          /* The current position in the pattern */\n  PCRE2_SPTR temp_sptr[2];   /* Used for short-term PCRE2_SPTR values */\n  PCRE2_SIZE length;         /* Used for character, string, or code lengths */\n  PCRE2_SIZE back_frame;     /* Amount to subtract on RRETURN */\n  PCRE2_SIZE temp_size;      /* Used for short-term PCRE2_SIZE values */\n  uint32_t rdepth;           /* Function \"recursion\" depth within pcre2_match() */\n  uint32_t group_frame_type; /* Type information for group frames */\n  uint32_t temp_32[4];       /* Used for short-term 32-bit or BOOL values */\n  uint8_t return_id;         /* Where to go on in internal \"return\" */\n  uint8_t op;                /* Processing opcode */\n\n  /* At this point, the structure is 16-bit aligned. On most architectures\n  the alignment requirement for a pointer will ensure that the eptr field below\n  is 32-bit or 64-bit aligned. However, on m68k it is fine to have a pointer\n  that is 16-bit aligned. We must therefore ensure that what comes between here\n  and eptr is an odd multiple of 16 bits so as to get back into 32-bit\n  alignment. This happens naturally when PCRE2_UCHAR is 8 bits wide, but needs\n  fudges in the other cases. In the 32-bit case the padding comes first so that\n  the occu field itself is 32-bit aligned. Without the padding, this structure\n  is no longer a multiple of PCRE2_SIZE on m68k, and the check below fails. */\n\n#if PCRE2_CODE_UNIT_WIDTH == 8\n  PCRE2_UCHAR occu[6];       /* Used for other case code units */\n#elif PCRE2_CODE_UNIT_WIDTH == 16\n  PCRE2_UCHAR occu[2];       /* Used for other case code units */\n  uint8_t unused[2];         /* Ensure 32-bit alignment (see above) */\n#else\n  uint8_t unused[2];         /* Ensure 32-bit alignment (see above) */\n  PCRE2_UCHAR occu[1];       /* Used for other case code units */\n#endif\n\n  /* The rest have to be copied from the previous frame whenever a new frame\n  becomes current. The final field is specified as a large vector so that\n  runtime array bound checks don't catch references to it. However, for any\n  specific call to pcre2_match() the memory allocated for each frame structure\n  allows for exactly the right size ovector for the number of capturing\n  parentheses. (See also the comment for pcre2_real_match_data above.) */\n\n  PCRE2_SPTR eptr;              /* MUST BE FIRST */\n  PCRE2_SPTR start_match;       /* Can be adjusted by \\K */\n  PCRE2_SPTR mark;              /* Most recent mark on the success path */\n  PCRE2_SPTR recurse_last_used; /* Last character used at time of pattern recursion */\n  uint32_t current_recurse;     /* Group number of current (deepest) pattern recursion */\n  uint32_t capture_last;        /* Most recent capture */\n  PCRE2_SIZE last_group_offset; /* Saved offset to most recent group frame */\n  PCRE2_SIZE offset_top;        /* Offset after highest capture */\n  PCRE2_SIZE ovector[131072];   /* Must be last in the structure */\n} heapframe;\n\n/* Assert that the size of the heapframe structure is a multiple of PCRE2_SIZE.\nSee various comments above. */\n\nSTATIC_ASSERT((sizeof(heapframe) % sizeof(PCRE2_SIZE)) == 0, heapframe_size);\n\n/* Structure for computing the alignment of heapframe. */\n\ntypedef struct heapframe_align {\n  char unalign;    /* Completely unalign the current offset */\n  heapframe frame; /* Offset is its alignment */\n} heapframe_align;\n\n/* This define is the minimum alignment required for a heapframe, in bytes. */\n\n#define HEAPFRAME_ALIGNMENT offsetof(heapframe_align, frame)\n\n/* Structure for passing \"static\" information around between the functions\ndoing traditional NFA matching (pcre2_match() and friends). */\n\ntypedef struct match_block {\n  pcre2_memctl memctl;            /* For general use */\n  uint32_t heap_limit;            /* As it says */\n  uint32_t match_limit;           /* As it says */\n  uint32_t match_limit_depth;     /* As it says */\n  uint32_t match_call_count;      /* Number of times a new frame is created */\n  BOOL hitend;                    /* Hit the end of the subject at some point */\n  BOOL hasthen;                   /* Pattern contains (*THEN) */\n  BOOL allowemptypartial;         /* Allow empty hard partial */\n  const uint8_t *lcc;             /* Points to lower casing table */\n  const uint8_t *fcc;             /* Points to case-flipping table */\n  const uint8_t *ctypes;          /* Points to table of type maps */\n  PCRE2_SIZE start_offset;        /* The start offset value */\n  PCRE2_SIZE end_offset_top;      /* Highwater mark at end of match */\n  uint16_t partial;               /* PARTIAL options */\n  uint16_t bsr_convention;        /* \\R interpretation */\n  uint16_t name_count;            /* Number of names in name table */\n  uint16_t name_entry_size;       /* Size of entry in names table */\n  PCRE2_SPTR name_table;          /* Table of group names */\n  PCRE2_SPTR start_code;          /* For use in pattern recursion */\n  PCRE2_SPTR start_subject;       /* Start of the subject string */\n  PCRE2_SPTR check_subject;       /* Where UTF-checked from */\n  PCRE2_SPTR end_subject;         /* Usable end of the subject string */\n  PCRE2_SPTR true_end_subject;    /* Actual end of the subject string */\n  PCRE2_SPTR end_match_ptr;       /* Subject position at end match */\n  PCRE2_SPTR start_used_ptr;      /* Earliest consulted character */\n  PCRE2_SPTR last_used_ptr;       /* Latest consulted character */\n  PCRE2_SPTR mark;                /* Mark pointer to pass back on success */\n  PCRE2_SPTR nomatch_mark;        /* Mark pointer to pass back on failure */\n  PCRE2_SPTR verb_ecode_ptr;      /* For passing back info */\n  PCRE2_SPTR verb_skip_ptr;       /* For passing back a (*SKIP) name */\n  uint32_t verb_current_recurse;  /* Current recursion group when (*VERB) happens */\n  uint32_t moptions;              /* Match options */\n  uint32_t poptions;              /* Pattern options */\n  uint32_t skip_arg_count;        /* For counting SKIP_ARGs */\n  uint32_t ignore_skip_arg;       /* For re-run when SKIP arg name not found */\n  uint32_t nltype;                /* Newline type */\n  uint32_t nllen;                 /* Newline string length */\n  PCRE2_UCHAR nl[4];              /* Newline string when fixed */\n  pcre2_callout_block *cb;        /* Points to a callout block */\n  void  *callout_data;            /* To pass back to callouts */\n  int (*callout)(pcre2_callout_block *,void *);  /* Callout function or NULL */\n} match_block;\n\n/* A similar structure is used for the same purpose by the DFA matching\nfunctions. */\n\ntypedef struct dfa_match_block {\n  pcre2_memctl memctl;            /* For general use */\n  PCRE2_SPTR start_code;          /* Start of the compiled pattern */\n  PCRE2_SPTR start_subject ;      /* Start of the subject string */\n  PCRE2_SPTR end_subject;         /* End of subject string */\n  PCRE2_SPTR start_used_ptr;      /* Earliest consulted character */\n  PCRE2_SPTR last_used_ptr;       /* Latest consulted character */\n  const uint8_t *tables;          /* Character tables */\n  PCRE2_SIZE start_offset;        /* The start offset value */\n  uint32_t heap_limit;            /* As it says */\n  PCRE2_SIZE heap_used;           /* As it says */\n  uint32_t match_limit;           /* As it says */\n  uint32_t match_limit_depth;     /* As it says */\n  uint32_t match_call_count;      /* Number of calls of internal function */\n  uint32_t moptions;              /* Match options */\n  uint32_t poptions;              /* Pattern options */\n  uint32_t nltype;                /* Newline type */\n  uint32_t nllen;                 /* Newline string length */\n  BOOL allowemptypartial;         /* Allow empty hard partial */\n  PCRE2_UCHAR nl[4];              /* Newline string when fixed */\n  uint16_t bsr_convention;        /* \\R interpretation */\n  pcre2_callout_block *cb;        /* Points to a callout block */\n  void *callout_data;             /* To pass back to callouts */\n  int (*callout)(pcre2_callout_block *,void *);  /* Callout function or NULL */\n  dfa_recursion_info *recursive;  /* Linked list of pattern recursion data */\n} dfa_match_block;\n\n#endif  /* PCRE2_PCRE2TEST */\n\n/* End of pcre2_intmodedep.h */\n"
  },
  {
    "path": "Libraries/PCRE/pcre2_maketables.c",
    "content": "/*************************************************\n*      Perl-Compatible Regular Expressions       *\n*************************************************/\n\n/* PCRE is a library of functions to support regular expressions whose syntax\nand semantics are as close as possible to those of the Perl 5 language.\n\n                       Written by Philip Hazel\n     Original API code Copyright (c) 1997-2012 University of Cambridge\n          New API code Copyright (c) 2016-2024 University of Cambridge\n\n-----------------------------------------------------------------------------\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n    * Redistributions of source code must retain the above copyright notice,\n      this list of conditions and the following disclaimer.\n\n    * Redistributions in binary form must reproduce the above copyright\n      notice, this list of conditions and the following disclaimer in the\n      documentation and/or other materials provided with the distribution.\n\n    * Neither the name of the University of Cambridge nor the names of its\n      contributors may be used to endorse or promote products derived from\n      this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\nLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\nCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\nSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\nINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\nCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGE.\n-----------------------------------------------------------------------------\n*/\n\n\n/* This module contains the external function pcre2_maketables(), which builds\ncharacter tables for PCRE2 in the current locale. The file is compiled on its\nown as part of the PCRE2 library. It is also included in the compilation of\npcre2_dftables.c as a freestanding program, in which case the macro\nPCRE2_DFTABLES is defined. */\n\n#ifndef PCRE2_DFTABLES    /* Compiling the library */\n#  ifdef HAVE_CONFIG_H\n#  include \"config.h\"\n#  endif\n#  include \"pcre2_internal.h\"\n#endif\n\n/*************************************************\n*           Create PCRE2 character tables        *\n*************************************************/\n\n/* This function builds a set of character tables for use by PCRE2 and returns\na pointer to them. They are build using the ctype functions, and consequently\ntheir contents will depend upon the current locale setting. When compiled as\npart of the library, the store is obtained via a general context malloc, if\nsupplied, but when PCRE2_DFTABLES is defined (when compiling the pcre2_dftables\nfreestanding auxiliary program) malloc() is used, and the function has a\ndifferent name so as not to clash with the prototype in pcre2.h.\n\nArguments:   none when PCRE2_DFTABLES is defined\n               else a PCRE2 general context or NULL\nReturns:     pointer to the contiguous block of data\n               else NULL if memory allocation failed\n*/\n\n#ifdef PCRE2_DFTABLES  /* Included in freestanding pcre2_dftables program */\nstatic const uint8_t *maketables(void)\n{\nuint8_t *yield = (uint8_t *)malloc(TABLES_LENGTH);\n\n#else  /* Not PCRE2_DFTABLES, that is, compiling the library */\nPCRE2_EXP_DEFN const uint8_t * PCRE2_CALL_CONVENTION\npcre2_maketables(pcre2_general_context *gcontext)\n{\nuint8_t *yield = (uint8_t *)((gcontext != NULL)?\n  gcontext->memctl.malloc(TABLES_LENGTH, gcontext->memctl.memory_data) :\n  malloc(TABLES_LENGTH));\n#endif  /* PCRE2_DFTABLES */\n\nint i;\nuint8_t *p;\n\nif (yield == NULL) return NULL;\np = yield;\n\n/* First comes the lower casing table */\n\nfor (i = 0; i < 256; i++) *p++ = tolower(i);\n\n/* Next the case-flipping table */\n\nfor (i = 0; i < 256; i++)\n  {\n  int c = islower(i)? toupper(i) : tolower(i);\n  *p++ = (c < 256)? c : i;\n  }\n\n/* Then the character class tables. Don't try to be clever and save effort on\nexclusive ones - in some locales things may be different.\n\nNote that the table for \"space\" includes everything \"isspace\" gives, including\nVT in the default locale. This makes it work for the POSIX class [:space:].\nFrom PCRE1 release 8.34 and for all PCRE2 releases it is also correct for Perl\nspace, because Perl added VT at release 5.18.\n\nNote also that it is possible for a character to be alnum or alpha without\nbeing lower or upper, such as \"male and female ordinals\" (\\xAA and \\xBA) in the\nfr_FR locale (at least under Debian Linux's locales as of 12/2005). So we must\ntest for alnum specially. */\n\nmemset(p, 0, cbit_length);\nfor (i = 0; i < 256; i++)\n  {\n  if (isdigit(i))  p[cbit_digit  + i/8] |= 1u << (i&7);\n  if (isupper(i))  p[cbit_upper  + i/8] |= 1u << (i&7);\n  if (islower(i))  p[cbit_lower  + i/8] |= 1u << (i&7);\n  if (isalnum(i))  p[cbit_word   + i/8] |= 1u << (i&7);\n  if (i == '_')    p[cbit_word   + i/8] |= 1u << (i&7);\n  if (isspace(i))  p[cbit_space  + i/8] |= 1u << (i&7);\n  if (isxdigit(i)) p[cbit_xdigit + i/8] |= 1u << (i&7);\n  if (isgraph(i))  p[cbit_graph  + i/8] |= 1u << (i&7);\n  if (isprint(i))  p[cbit_print  + i/8] |= 1u << (i&7);\n  if (ispunct(i))  p[cbit_punct  + i/8] |= 1u << (i&7);\n  if (iscntrl(i))  p[cbit_cntrl  + i/8] |= 1u << (i&7);\n  }\np += cbit_length;\n\n/* Finally, the character type table. In this, we used to exclude VT from the\nwhite space chars, because Perl didn't recognize it as such for \\s and for\ncomments within regexes. However, Perl changed at release 5.18, so PCRE1\nchanged at release 8.34 and it's always been this way for PCRE2. */\n\nfor (i = 0; i < 256; i++)\n  {\n  int x = 0;\n  if (isspace(i)) x += ctype_space;\n  if (isalpha(i)) x += ctype_letter;\n  if (islower(i)) x += ctype_lcletter;\n  if (isdigit(i)) x += ctype_digit;\n  if (isalnum(i) || i == '_') x += ctype_word;\n  *p++ = x;\n  }\n\nreturn yield;\n}\n\n#ifndef PCRE2_DFTABLES   /* Compiling the library */\nPCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION\npcre2_maketables_free(pcre2_general_context *gcontext, const uint8_t *tables)\n{\nif (gcontext != NULL)\n  gcontext->memctl.free((void *)tables, gcontext->memctl.memory_data);\nelse\n  free((void *)tables);\n}\n#endif\n\n/* End of pcre2_maketables.c */\n"
  },
  {
    "path": "Libraries/PCRE/pcre2_match.c",
    "content": "/*************************************************\n*      Perl-Compatible Regular Expressions       *\n*************************************************/\n\n/* PCRE is a library of functions to support regular expressions whose syntax\nand semantics are as close as possible to those of the Perl 5 language.\n\n                       Written by Philip Hazel\n     Original API code Copyright (c) 1997-2012 University of Cambridge\n          New API code Copyright (c) 2015-2024 University of Cambridge\n\n-----------------------------------------------------------------------------\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n    * Redistributions of source code must retain the above copyright notice,\n      this list of conditions and the following disclaimer.\n\n    * Redistributions in binary form must reproduce the above copyright\n      notice, this list of conditions and the following disclaimer in the\n      documentation and/or other materials provided with the distribution.\n\n    * Neither the name of the University of Cambridge nor the names of its\n      contributors may be used to endorse or promote products derived from\n      this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\nLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\nCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\nSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\nINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\nCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGE.\n-----------------------------------------------------------------------------\n*/\n\n\n#ifdef HAVE_CONFIG_H\n#include \"config.h\"\n#endif\n\n#include \"pcre2_internal.h\"\n\n/* These defines enable debugging code */\n\n/* #define DEBUG_FRAMES_DISPLAY */\n/* #define DEBUG_SHOW_OPS */\n/* #define DEBUG_SHOW_RMATCH */\n\n#ifdef DEBUG_FRAMES_DISPLAY\n#include <stdarg.h>\n#endif\n\n#ifdef DEBUG_SHOW_OPS\nstatic const char *OP_names[] = { OP_NAME_LIST };\n#endif\n\n/* These defines identify the name of the block containing \"static\"\ninformation, and fields within it. */\n\n#define NLBLOCK mb              /* Block containing newline information */\n#define PSSTART start_subject   /* Field containing processed string start */\n#define PSEND   end_subject     /* Field containing processed string end */\n\n#define RECURSE_UNSET 0xffffffffu  /* Bigger than max group number */\n\n/* Masks for identifying the public options that are permitted at match time. */\n\n#define PUBLIC_MATCH_OPTIONS \\\n  (PCRE2_ANCHORED|PCRE2_ENDANCHORED|PCRE2_NOTBOL|PCRE2_NOTEOL|PCRE2_NOTEMPTY| \\\n   PCRE2_NOTEMPTY_ATSTART|PCRE2_NO_UTF_CHECK|PCRE2_PARTIAL_HARD| \\\n   PCRE2_PARTIAL_SOFT|PCRE2_NO_JIT|PCRE2_COPY_MATCHED_SUBJECT| \\\n   PCRE2_DISABLE_RECURSELOOP_CHECK)\n\n#define PUBLIC_JIT_MATCH_OPTIONS \\\n   (PCRE2_NO_UTF_CHECK|PCRE2_NOTBOL|PCRE2_NOTEOL|PCRE2_NOTEMPTY|\\\n    PCRE2_NOTEMPTY_ATSTART|PCRE2_PARTIAL_SOFT|PCRE2_PARTIAL_HARD|\\\n    PCRE2_COPY_MATCHED_SUBJECT)\n\n/* Non-error returns from and within the match() function. Error returns are\nexternally defined PCRE2_ERROR_xxx codes, which are all negative. */\n\n#define MATCH_MATCH        1\n#define MATCH_NOMATCH      0\n\n/* Special internal returns used in the match() function. Make them\nsufficiently negative to avoid the external error codes. */\n\n#define MATCH_ACCEPT       (-999)\n#define MATCH_KETRPOS      (-998)\n/* The next 5 must be kept together and in sequence so that a test that checks\nfor any one of them can use a range. */\n#define MATCH_COMMIT       (-997)\n#define MATCH_PRUNE        (-996)\n#define MATCH_SKIP         (-995)\n#define MATCH_SKIP_ARG     (-994)\n#define MATCH_THEN         (-993)\n#define MATCH_BACKTRACK_MAX MATCH_THEN\n#define MATCH_BACKTRACK_MIN MATCH_COMMIT\n\n/* Group frame type values. Zero means the frame is not a group frame. The\nlower 16 bits are used for data (e.g. the capture number). Group frames are\nused for most groups so that information about the start is easily available at\nthe end without having to scan back through intermediate frames (backtrack\npoints). */\n\n#define GF_CAPTURE     0x00010000u\n#define GF_NOCAPTURE   0x00020000u\n#define GF_CONDASSERT  0x00030000u\n#define GF_RECURSE     0x00040000u\n\n/* Masks for the identity and data parts of the group frame type. */\n\n#define GF_IDMASK(a)   ((a) & 0xffff0000u)\n#define GF_DATAMASK(a) ((a) & 0x0000ffffu)\n\n/* Repetition types */\n\nenum { REPTYPE_MIN, REPTYPE_MAX, REPTYPE_POS };\n\n/* Min and max values for the common repeats; a maximum of UINT32_MAX =>\ninfinity. */\n\nstatic const uint32_t rep_min[] = {\n  0, 0,       /* * and *? */\n  1, 1,       /* + and +? */\n  0, 0,       /* ? and ?? */\n  0, 0,       /* dummy placefillers for OP_CR[MIN]RANGE */\n  0, 1, 0 };  /* OP_CRPOS{STAR, PLUS, QUERY} */\n\nstatic const uint32_t rep_max[] = {\n  UINT32_MAX, UINT32_MAX,      /* * and *? */\n  UINT32_MAX, UINT32_MAX,      /* + and +? */\n  1, 1,                        /* ? and ?? */\n  0, 0,                        /* dummy placefillers for OP_CR[MIN]RANGE */\n  UINT32_MAX, UINT32_MAX, 1 }; /* OP_CRPOS{STAR, PLUS, QUERY} */\n\n/* Repetition types - must include OP_CRPOSRANGE (not needed above) */\n\nstatic const uint32_t rep_typ[] = {\n  REPTYPE_MAX, REPTYPE_MIN,    /* * and *? */\n  REPTYPE_MAX, REPTYPE_MIN,    /* + and +? */\n  REPTYPE_MAX, REPTYPE_MIN,    /* ? and ?? */\n  REPTYPE_MAX, REPTYPE_MIN,    /* OP_CRRANGE and OP_CRMINRANGE */\n  REPTYPE_POS, REPTYPE_POS,    /* OP_CRPOSSTAR, OP_CRPOSPLUS */\n  REPTYPE_POS, REPTYPE_POS };  /* OP_CRPOSQUERY, OP_CRPOSRANGE */\n\n/* Numbers for RMATCH calls at backtracking points. When these lists are\nchanged, the code at RETURN_SWITCH below must be updated in sync.  */\n\nenum { RM1=1, RM2,  RM3,  RM4,  RM5,  RM6,  RM7,  RM8,  RM9,  RM10,\n       RM11,  RM12, RM13, RM14, RM15, RM16, RM17, RM18, RM19, RM20,\n       RM21,  RM22, RM23, RM24, RM25, RM26, RM27, RM28, RM29, RM30,\n       RM31,  RM32, RM33, RM34, RM35, RM36, RM37, RM38, RM39 };\n\n#ifdef SUPPORT_WIDE_CHARS\nenum { RM100=100, RM101, RM102, RM103 };\n#endif\n\n#ifdef SUPPORT_UNICODE\nenum { RM200=200, RM201, RM202, RM203, RM204, RM205, RM206, RM207,\n       RM208,     RM209, RM210, RM211, RM212, RM213, RM214, RM215,\n       RM216,     RM217, RM218, RM219, RM220, RM221, RM222, RM223,\n       RM224 };\n#endif\n\n/* Define short names for general fields in the current backtrack frame, which\nis always pointed to by the F variable. Occasional references to fields in\nother frames are written out explicitly. There are also some fields in the\ncurrent frame whose names start with \"temp\" that are used for short-term,\nlocalised backtracking memory. These are #defined with Lxxx names at the point\nof use and undefined afterwards. */\n\n#define Fback_frame        F->back_frame\n#define Fcapture_last      F->capture_last\n#define Fcurrent_recurse   F->current_recurse\n#define Fecode             F->ecode\n#define Feptr              F->eptr\n#define Fgroup_frame_type  F->group_frame_type\n#define Flast_group_offset F->last_group_offset\n#define Flength            F->length\n#define Fmark              F->mark\n#define Frdepth            F->rdepth\n#define Fstart_match       F->start_match\n#define Foffset_top        F->offset_top\n#define Foccu              F->occu\n#define Fop                F->op\n#define Fovector           F->ovector\n#define Freturn_id         F->return_id\n\n\n#ifdef DEBUG_FRAMES_DISPLAY\n/*************************************************\n*      Display current frames and contents       *\n*************************************************/\n\n/* This debugging function displays the current set of frames and their\ncontents. It is not called automatically from anywhere, the intention being\nthat calls can be inserted where necessary when debugging frame-related\nproblems.\n\nArguments:\n  f           the file to write to\n  F           the current top frame\n  P           a previous frame of interest\n  frame_size  the frame size\n  mb          points to the match block\n  match_data  points to the match data block\n  s           identification text\n\nReturns:    nothing\n*/\n\nstatic void\ndisplay_frames(FILE *f, heapframe *F, heapframe *P, PCRE2_SIZE frame_size,\n  match_block *mb, pcre2_match_data *match_data, const char *s, ...)\n{\nuint32_t i;\nheapframe *Q;\nva_list ap;\nva_start(ap, s);\n\nfprintf(f, \"FRAMES \");\nvfprintf(f, s, ap);\nva_end(ap);\n\nif (P != NULL) fprintf(f, \" P=%lu\",\n  ((char *)P - (char *)(match_data->heapframes))/frame_size);\nfprintf(f, \"\\n\");\n\nfor (i = 0, Q = match_data->heapframes;\n     Q <= F;\n     i++, Q = (heapframe *)((char *)Q + frame_size))\n  {\n  fprintf(f, \"Frame %d type=%x subj=%lu code=%d back=%lu id=%d\",\n    i, Q->group_frame_type, Q->eptr - mb->start_subject, *(Q->ecode),\n    Q->back_frame, Q->return_id);\n\n  if (Q->last_group_offset == PCRE2_UNSET)\n    fprintf(f, \" lgoffset=unset\\n\");\n  else\n    fprintf(f, \" lgoffset=%lu\\n\",  Q->last_group_offset/frame_size);\n  }\n}\n\n#endif\n\n\n\n/*************************************************\n*                Process a callout               *\n*************************************************/\n\n/* This function is called for all callouts, whether \"standalone\" or at the\nstart of a conditional group. Feptr will be pointing to either OP_CALLOUT or\nOP_CALLOUT_STR. A callout block is allocated in pcre2_match() and initialized\nwith fixed values.\n\nArguments:\n  F          points to the current backtracking frame\n  mb         points to the match block\n  lengthptr  where to return the length of the callout item\n\nReturns:     the return from the callout\n             or 0 if no callout function exists\n*/\n\nstatic int\ndo_callout(heapframe *F, match_block *mb, PCRE2_SIZE *lengthptr)\n{\nint rc;\nPCRE2_SIZE save0, save1;\nPCRE2_SIZE *callout_ovector;\npcre2_callout_block *cb;\n\n*lengthptr = (*Fecode == OP_CALLOUT)?\n  PRIV(OP_lengths)[OP_CALLOUT] : GET(Fecode, 1 + 2*LINK_SIZE);\n\nif (mb->callout == NULL) return 0;   /* No callout function provided */\n\n/* The original matching code (pre 10.30) worked directly with the ovector\npassed by the user, and this was passed to callouts. Now that the working\novector is in the backtracking frame, it no longer needs to reserve space for\nthe overall match offsets (which would waste space in the frame). For backward\ncompatibility, however, we pass capture_top and offset_vector to the callout as\nif for the extended ovector, and we ensure that the first two slots are unset\nby preserving and restoring their current contents. Picky compilers complain if\nreferences such as Fovector[-2] are use directly, so we set up a separate\npointer. */\n\ncallout_ovector = (PCRE2_SIZE *)(Fovector) - 2;\n\n/* The cb->version, cb->subject, cb->subject_length, and cb->start_match fields\nare set externally. The first 3 never change; the last is updated for each\nbumpalong. */\n\ncb = mb->cb;\ncb->capture_top      = (uint32_t)Foffset_top/2 + 1;\ncb->capture_last     = Fcapture_last;\ncb->offset_vector    = callout_ovector;\ncb->mark             = mb->nomatch_mark;\ncb->current_position = (PCRE2_SIZE)(Feptr - mb->start_subject);\ncb->pattern_position = GET(Fecode, 1);\ncb->next_item_length = GET(Fecode, 1 + LINK_SIZE);\n\nif (*Fecode == OP_CALLOUT)  /* Numerical callout */\n  {\n  cb->callout_number = Fecode[1 + 2*LINK_SIZE];\n  cb->callout_string_offset = 0;\n  cb->callout_string = NULL;\n  cb->callout_string_length = 0;\n  }\nelse  /* String callout */\n  {\n  cb->callout_number = 0;\n  cb->callout_string_offset = GET(Fecode, 1 + 3*LINK_SIZE);\n  cb->callout_string = Fecode + (1 + 4*LINK_SIZE) + 1;\n  cb->callout_string_length =\n    *lengthptr - (1 + 4*LINK_SIZE) - 2;\n  }\n\nsave0 = callout_ovector[0];\nsave1 = callout_ovector[1];\ncallout_ovector[0] = callout_ovector[1] = PCRE2_UNSET;\nrc = mb->callout(cb, mb->callout_data);\ncallout_ovector[0] = save0;\ncallout_ovector[1] = save1;\ncb->callout_flags = 0;\nreturn rc;\n}\n\n\n\n/*************************************************\n*          Match a back-reference                *\n*************************************************/\n\n/* This function is called only when it is known that the offset lies within\nthe offsets that have so far been used in the match. Note that in caseless\nUTF-8 mode, the number of subject bytes matched may be different to the number\nof reference bytes. (In theory this could also happen in UTF-16 mode, but it\nseems unlikely.)\n\nArguments:\n  offset      index into the offset vector\n  caseless    TRUE if caseless\n  caseopts    bitmask of REFI_FLAG_XYZ values\n  F           the current backtracking frame pointer\n  mb          points to match block\n  lengthptr   pointer for returning the length matched\n\nReturns:      = 0 sucessful match; number of code units matched is set\n              < 0 no match\n              > 0 partial match\n*/\n\nstatic int\nmatch_ref(PCRE2_SIZE offset, BOOL caseless, int caseopts, heapframe *F,\n  match_block *mb, PCRE2_SIZE *lengthptr)\n{\nPCRE2_SPTR p;\nPCRE2_SIZE length;\nPCRE2_SPTR eptr;\nPCRE2_SPTR eptr_start;\n\n/* Deal with an unset group. The default is no match, but there is an option to\nmatch an empty string. */\n\nif (offset >= Foffset_top || Fovector[offset] == PCRE2_UNSET)\n  {\n  if ((mb->poptions & PCRE2_MATCH_UNSET_BACKREF) != 0)\n    {\n    *lengthptr = 0;\n    return 0;      /* Match */\n    }\n  else return -1;  /* No match */\n  }\n\n/* Separate the caseless and UTF cases for speed. */\n\neptr = eptr_start = Feptr;\np = mb->start_subject + Fovector[offset];\nlength = Fovector[offset+1] - Fovector[offset];\n\nif (caseless)\n  {\n#if defined SUPPORT_UNICODE\n  BOOL utf = (mb->poptions & PCRE2_UTF) != 0;\n  BOOL caseless_restrict = (caseopts & REFI_FLAG_CASELESS_RESTRICT) != 0;\n  BOOL turkish_casing = !caseless_restrict && (caseopts & REFI_FLAG_TURKISH_CASING) != 0;\n\n  if (utf || (mb->poptions & PCRE2_UCP) != 0)\n    {\n    PCRE2_SPTR endptr = p + length;\n\n    /* Match characters up to the end of the reference. NOTE: the number of\n    code units matched may differ, because in UTF-8 there are some characters\n    whose upper and lower case codes have different numbers of bytes. For\n    example, U+023A (2 bytes in UTF-8) is the upper case version of U+2C65 (3\n    bytes in UTF-8); a sequence of 3 of the former uses 6 bytes, as does a\n    sequence of two of the latter. It is important, therefore, to check the\n    length along the reference, not along the subject (earlier code did this\n    wrong). UCP without uses Unicode properties but without UTF encoding. */\n\n    while (p < endptr)\n      {\n      uint32_t c, d;\n      const ucd_record *ur;\n      if (eptr >= mb->end_subject) return 1;   /* Partial match */\n\n      if (utf)\n        {\n        GETCHARINC(c, eptr);\n        GETCHARINC(d, p);\n        }\n      else\n        {\n        c = *eptr++;\n        d = *p++;\n        }\n\n      if (turkish_casing && UCD_ANY_I(d))\n        {\n        c = UCD_FOLD_I_TURKISH(c);\n        d = UCD_FOLD_I_TURKISH(d);\n        if (c != d) return -1;  /* No match */\n        }\n      else if (c != d && c != (uint32_t)((int)d + (ur = GET_UCD(d))->other_case))\n        {\n        const uint32_t *pp = PRIV(ucd_caseless_sets) + ur->caseset;\n\n        /* When PCRE2_EXTRA_CASELESS_RESTRICT is set, ignore any caseless sets\n        that start with an ASCII character. */\n        if (caseless_restrict && *pp < 128) return -1;  /* No match */\n\n        for (;;)\n          {\n          if (c < *pp) return -1;  /* No match */\n          if (c == *pp++) break;\n          }\n        }\n      }\n    }\n  else\n#endif\n\n  /* Not in UTF or UCP mode */\n    {\n    for (; length > 0; length--)\n      {\n      uint32_t cc, cp;\n      if (eptr >= mb->end_subject) return 1;   /* Partial match */\n      cc = UCHAR21TEST(eptr);\n      cp = UCHAR21TEST(p);\n      if (TABLE_GET(cp, mb->lcc, cp) != TABLE_GET(cc, mb->lcc, cc))\n        return -1;  /* No match */\n      p++;\n      eptr++;\n      }\n    }\n  }\n\n/* In the caseful case, we can just compare the code units, whether or not we\nare in UTF and/or UCP mode. When partial matching, we have to do this unit by\nunit. */\n\nelse\n  {\n  if (mb->partial != 0)\n    {\n    for (; length > 0; length--)\n      {\n      if (eptr >= mb->end_subject) return 1;   /* Partial match */\n      if (UCHAR21INCTEST(p) != UCHAR21INCTEST(eptr)) return -1;  /* No match */\n      }\n    }\n\n  /* Not partial matching */\n\n  else\n    {\n    if ((PCRE2_SIZE)(mb->end_subject - eptr) < length) return 1; /* Partial */\n    if (memcmp(p, eptr, CU2BYTES(length)) != 0) return -1;  /* No match */\n    eptr += length;\n    }\n  }\n\n*lengthptr = eptr - eptr_start;\nreturn 0;  /* Match */\n}\n\n\n\n/******************************************************************************\n*******************************************************************************\n                   \"Recursion\" in the match() function\n\nThe original match() function was highly recursive, but this proved to be the\nsource of a number of problems over the years, mostly because of the relatively\nsmall system stacks that are commonly found. As new features were added to\npatterns, various kludges were invented to reduce the amount of stack used,\nmaking the code hard to understand in places.\n\nA version did exist that used individual frames on the heap instead of calling\nmatch() recursively, but this ran substantially slower. The current version is\na refactoring that uses a vector of frames to remember backtracking points.\nThis runs no slower, and possibly even a bit faster than the original recursive\nimplementation.\n\nAt first, an initial vector of size START_FRAMES_SIZE (enough for maybe 50\nframes) was allocated on the system stack. If this was not big enough, the heap\nwas used for a larger vector. However, it turns out that there are environments\nwhere taking as little as 20KiB from the system stack is an embarrassment.\nAfter another refactoring, the heap is used exclusively, but a pointer the\nframes vector and its size are cached in the match_data block, so that there is\nno new memory allocation if the same match_data block is used for multiple\nmatches (unless the frames vector has to be extended).\n*******************************************************************************\n******************************************************************************/\n\n\n\n\n/*************************************************\n*       Macros for the match() function          *\n*************************************************/\n\n/* These macros pack up tests that are used for partial matching several times\nin the code. The second one is used when we already know we are past the end of\nthe subject. We set the \"hit end\" flag if the pointer is at the end of the\nsubject and either (a) the pointer is past the earliest inspected character\n(i.e. something has been matched, even if not part of the actual matched\nstring), or (b) the pattern contains a lookbehind. These are the conditions for\nwhich adding more characters may allow the current match to continue.\n\nFor hard partial matching, we immediately return a partial match. Otherwise,\ncarrying on means that a complete match on the current subject will be sought.\nA partial match is returned only if no complete match can be found. */\n\n#define CHECK_PARTIAL() \\\n  do { \\\n     if (Feptr >= mb->end_subject) \\\n       { \\\n       SCHECK_PARTIAL(); \\\n       } \\\n     } \\\n  while (0)\n\n#define SCHECK_PARTIAL() \\\n  do { \\\n     if (mb->partial != 0 && \\\n         (Feptr > mb->start_used_ptr || mb->allowemptypartial)) \\\n       { \\\n       mb->hitend = TRUE; \\\n       if (mb->partial > 1) return PCRE2_ERROR_PARTIAL; \\\n       } \\\n     } \\\n  while (0)\n\n\n/* These macros are used to implement backtracking. They simulate a recursive\ncall to the match() function by means of a local vector of frames which\nremember the backtracking points. */\n\n#define RMATCH(ra,rb) \\\n  do { \\\n     start_ecode = ra; \\\n     Freturn_id = rb; \\\n     goto MATCH_RECURSE; \\\n     L_##rb:; \\\n     } \\\n  while (0)\n\n#define RRETURN(ra) \\\n  do { \\\n     rrc = ra; \\\n     goto RETURN_SWITCH; \\\n     } \\\n  while (0)\n\n\n\n/*************************************************\n*         Match from current position            *\n*************************************************/\n\n/* This function is called to run one match attempt at a single starting point\nin the subject.\n\nPerformance note: It might be tempting to extract commonly used fields from the\nmb structure (e.g. end_subject) into individual variables to improve\nperformance. Tests using gcc on a SPARC disproved this; in the first case, it\nmade performance worse.\n\nArguments:\n   start_eptr   starting character in subject\n   start_ecode  starting position in compiled code\n   top_bracket  number of capturing parentheses in the pattern\n   frame_size   size of each backtracking frame\n   match_data   pointer to the match_data block\n   mb           pointer to \"static\" variables block\n\nReturns:        MATCH_MATCH if matched            )  these values are >= 0\n                MATCH_NOMATCH if failed to match  )\n                negative MATCH_xxx value for PRUNE, SKIP, etc\n                negative PCRE2_ERROR_xxx value if aborted by an error condition\n                (e.g. stopped by repeated call or depth limit)\n*/\n\nstatic int\nmatch(PCRE2_SPTR start_eptr, PCRE2_SPTR start_ecode, uint16_t top_bracket,\n  PCRE2_SIZE frame_size, pcre2_match_data *match_data, match_block *mb)\n{\n/* Frame-handling variables */\n\nheapframe *F;           /* Current frame pointer */\nheapframe *N = NULL;    /* Temporary frame pointers */\nheapframe *P = NULL;\n\nheapframe *frames_top;  /* End of frames vector */\nheapframe *assert_accept_frame = NULL;  /* For passing back a frame with captures */\nPCRE2_SIZE frame_copy_size;   /* Amount to copy when creating a new frame */\n\n/* Local variables that do not need to be preserved over calls to RRMATCH(). */\n\nPCRE2_SPTR branch_end = NULL;\nPCRE2_SPTR branch_start;\nPCRE2_SPTR bracode;     /* Temp pointer to start of group */\nPCRE2_SIZE offset;      /* Used for group offsets */\nPCRE2_SIZE length;      /* Used for various length calculations */\n\nint rrc;                /* Return from functions & backtracking \"recursions\" */\n#ifdef SUPPORT_UNICODE\nint proptype;           /* Type of character property */\n#endif\n\nuint32_t i;             /* Used for local loops */\nuint32_t fc;            /* Character values */\nuint32_t number;        /* Used for group and other numbers */\nuint32_t reptype = 0;   /* Type of repetition (0 to avoid compiler warning) */\nuint32_t group_frame_type;  /* Specifies type for new group frames */\n\nBOOL condition;         /* Used in conditional groups */\nBOOL cur_is_word;       /* Used in \"word\" tests */\nBOOL prev_is_word;      /* Used in \"word\" tests */\n\n/* UTF and UCP flags */\n\n#ifdef SUPPORT_UNICODE\nBOOL utf = (mb->poptions & PCRE2_UTF) != 0;\nBOOL ucp = (mb->poptions & PCRE2_UCP) != 0;\n#else\nBOOL utf = FALSE;  /* Required for convenience even when no Unicode support */\n#endif\n\n/* This is the length of the last part of a backtracking frame that must be\ncopied when a new frame is created. */\n\nframe_copy_size = frame_size - offsetof(heapframe, eptr);\n\n/* Set up the first frame and the end of the frames vector. */\n\nF = match_data->heapframes;\nframes_top = (heapframe *)((char *)F + match_data->heapframes_size);\n\nFrdepth = 0;                        /* \"Recursion\" depth */\nFcapture_last = 0;                  /* Number of most recent capture */\nFcurrent_recurse = RECURSE_UNSET;   /* Not pattern recursing. */\nFstart_match = Feptr = start_eptr;  /* Current data pointer and start match */\nFmark = NULL;                       /* Most recent mark */\nFoffset_top = 0;                    /* End of captures within the frame */\nFlast_group_offset = PCRE2_UNSET;   /* Saved frame of most recent group */\ngroup_frame_type = 0;               /* Not a start of group frame */\ngoto NEW_FRAME;                     /* Start processing with this frame */\n\n/* Come back here when we want to create a new frame for remembering a\nbacktracking point. */\n\nMATCH_RECURSE:\n\n/* Set up a new backtracking frame. If the vector is full, get a new one,\ndoubling the size, but constrained by the heap limit (which is in KiB). */\n\nN = (heapframe *)((char *)F + frame_size);\nif ((heapframe *)((char *)N + frame_size) >= frames_top)\n  {\n  heapframe *new;\n  PCRE2_SIZE newsize;\n  PCRE2_SIZE usedsize = (char *)N - (char *)(match_data->heapframes);\n\n  if (match_data->heapframes_size >= PCRE2_SIZE_MAX / 2)\n    {\n    if (match_data->heapframes_size == PCRE2_SIZE_MAX - 1)\n      return PCRE2_ERROR_NOMEMORY;\n    newsize = PCRE2_SIZE_MAX - 1;\n    }\n  else\n    newsize = match_data->heapframes_size * 2;\n\n  if (newsize / 1024 >= mb->heap_limit)\n    {\n    PCRE2_SIZE old_size = match_data->heapframes_size / 1024;\n    if (mb->heap_limit <= old_size)\n      return PCRE2_ERROR_HEAPLIMIT;\n    else\n      {\n      PCRE2_SIZE max_delta = 1024 * (mb->heap_limit - old_size);\n      int over_bytes = match_data->heapframes_size % 1024;\n      if (over_bytes) max_delta -= (1024 - over_bytes);\n      newsize = match_data->heapframes_size + max_delta;\n      }\n    }\n\n  /* With a heap limit set, the permitted additional size may not be enough for\n  another frame, so do a final check. */\n\n  if (newsize - usedsize < frame_size) return PCRE2_ERROR_HEAPLIMIT;\n  new = match_data->memctl.malloc(newsize, match_data->memctl.memory_data);\n  if (new == NULL) return PCRE2_ERROR_NOMEMORY;\n  memcpy(new, match_data->heapframes, usedsize);\n\n  N = (heapframe *)((char *)new + usedsize);\n  F = (heapframe *)((char *)N - frame_size);\n\n  match_data->memctl.free(match_data->heapframes, match_data->memctl.memory_data);\n  match_data->heapframes = new;\n  match_data->heapframes_size = newsize;\n  frames_top = (heapframe *)((char *)new + newsize);\n  }\n\n#ifdef DEBUG_SHOW_RMATCH\nfprintf(stderr, \"++ RMATCH %d frame=%d\", Freturn_id, Frdepth + 1);\nif (group_frame_type != 0)\n  {\n  fprintf(stderr, \" type=%x \", group_frame_type);\n  switch (GF_IDMASK(group_frame_type))\n    {\n    case GF_CAPTURE:\n    fprintf(stderr, \"capture=%d\", GF_DATAMASK(group_frame_type));\n    break;\n\n    case GF_NOCAPTURE:\n    fprintf(stderr, \"nocapture op=%d\", GF_DATAMASK(group_frame_type));\n    break;\n\n    case GF_CONDASSERT:\n    fprintf(stderr, \"condassert op=%d\", GF_DATAMASK(group_frame_type));\n    break;\n\n    case GF_RECURSE:\n    fprintf(stderr, \"recurse=%d\", GF_DATAMASK(group_frame_type));\n    break;\n\n    default:\n    fprintf(stderr, \"*** unknown ***\");\n    break;\n    }\n  }\nfprintf(stderr, \"\\n\");\n#endif\n\n/* Copy those fields that must be copied into the new frame, increase the\n\"recursion\" depth (i.e. the new frame's index) and then make the new frame\ncurrent. */\n\nmemcpy((char *)N + offsetof(heapframe, eptr),\n       (char *)F + offsetof(heapframe, eptr),\n       frame_copy_size);\n\nN->rdepth = Frdepth + 1;\nF = N;\n\n/* Carry on processing with a new frame. */\n\nNEW_FRAME:\nFgroup_frame_type = group_frame_type;\nFecode = start_ecode;      /* Starting code pointer */\nFback_frame = frame_size;  /* Default is go back one frame */\n\n/* If this is a special type of group frame, remember its offset for quick\naccess at the end of the group. If this is a recursion, set a new current\nrecursion value. */\n\nif (group_frame_type != 0)\n  {\n  Flast_group_offset = (char *)F - (char *)match_data->heapframes;\n  if (GF_IDMASK(group_frame_type) == GF_RECURSE)\n    Fcurrent_recurse = GF_DATAMASK(group_frame_type);\n  group_frame_type = 0;\n  }\n\n\n/* ========================================================================= */\n/* This is the main processing loop. First check that we haven't recorded too\nmany backtracks (search tree is too large), or that we haven't exceeded the\nrecursive depth limit (used too many backtracking frames). If not, process the\nopcodes. */\n\nif (mb->match_call_count++ >= mb->match_limit) return PCRE2_ERROR_MATCHLIMIT;\nif (Frdepth >= mb->match_limit_depth) return PCRE2_ERROR_DEPTHLIMIT;\n\n#ifdef DEBUG_SHOW_OPS\nfprintf(stderr, \"\\n++ New frame: type=0x%x subject offset %ld\\n\",\n  GF_IDMASK(Fgroup_frame_type), Feptr - mb->start_subject);\n#endif\n\nfor (;;)\n  {\n#ifdef DEBUG_SHOW_OPS\nfprintf(stderr, \"++ %2ld op=%3d %s\\n\", Fecode - mb->start_code, *Fecode,\n  OP_names[*Fecode]);\n#endif\n\n  Fop = (uint8_t)(*Fecode);  /* Cast needed for 16-bit and 32-bit modes */\n  switch(Fop)\n    {\n    /* ===================================================================== */\n    /* Before OP_ACCEPT there may be any number of OP_CLOSE opcodes, to close\n    any currently open capturing brackets. Unlike reaching the end of a group,\n    where we know the starting frame is at the top of the chained frames, in\n    this case we have to search back for the relevant frame in case other types\n    of group that use chained frames have intervened. Multiple OP_CLOSEs always\n    come innermost first, which matches the chain order. We can ignore this in\n    a recursion, because captures are not passed out of recursions. */\n\n    case OP_CLOSE:\n    if (Fcurrent_recurse == RECURSE_UNSET)\n      {\n      number = GET2(Fecode, 1);\n      offset = Flast_group_offset;\n      for(;;)\n        {\n        /* Corrupted heapframes?. Trigger an assert and return an error */\n        PCRE2_ASSERT(offset != PCRE2_UNSET);\n        if (offset == PCRE2_UNSET) return PCRE2_ERROR_INTERNAL;\n\n        N = (heapframe *)((char *)match_data->heapframes + offset);\n        P = (heapframe *)((char *)N - frame_size);\n        if (N->group_frame_type == (GF_CAPTURE | number)) break;\n        offset = P->last_group_offset;\n        }\n      offset = (number << 1) - 2;\n      Fcapture_last = number;\n      Fovector[offset] = P->eptr - mb->start_subject;\n      Fovector[offset+1] = Feptr - mb->start_subject;\n      if (offset >= Foffset_top) Foffset_top = offset + 2;\n      }\n    Fecode += PRIV(OP_lengths)[*Fecode];\n    break;\n\n\n    /* ===================================================================== */\n    /* Real or forced end of the pattern, assertion, or recursion. In an\n    assertion ACCEPT, update the last used pointer and remember the current\n    frame so that the captures and mark can be fished out of it. */\n\n    case OP_ASSERT_ACCEPT:\n    if (Feptr > mb->last_used_ptr) mb->last_used_ptr = Feptr;\n    assert_accept_frame = F;\n    RRETURN(MATCH_ACCEPT);\n\n    /* For ACCEPT within a recursion, we have to find the most recent\n    recursion. If not in a recursion, fall through to code that is common with\n    OP_END. */\n\n    case OP_ACCEPT:\n    if (Fcurrent_recurse != RECURSE_UNSET)\n      {\n#ifdef DEBUG_SHOW_OPS\n      fprintf(stderr, \"++ Accept within recursion\\n\");\n#endif\n      offset = Flast_group_offset;\n      for(;;)\n        {\n        /* Corrupted heapframes?. Trigger an assert and return an error */\n        PCRE2_ASSERT(offset != PCRE2_UNSET);\n        if (offset == PCRE2_UNSET) return PCRE2_ERROR_INTERNAL;\n\n        N = (heapframe *)((char *)match_data->heapframes + offset);\n        P = (heapframe *)((char *)N - frame_size);\n        if (GF_IDMASK(N->group_frame_type) == GF_RECURSE) break;\n        offset = P->last_group_offset;\n        }\n\n      /* N is now the frame of the recursion; the previous frame is at the\n      OP_RECURSE position. Go back there, copying the current subject position\n      and mark, and the start_match position (\\K might have changed it), and\n      then move on past the OP_RECURSE. */\n\n      P->eptr = Feptr;\n      P->mark = Fmark;\n      P->start_match = Fstart_match;\n      F = P;\n      Fecode += 1 + LINK_SIZE;\n      continue;\n      }\n    /* Fall through */\n\n    /* OP_END itself can never be reached within a recursion because that is\n    picked up when the OP_KET that always precedes OP_END is reached. */\n\n    case OP_END:\n\n    /* Fail for an empty string match if either PCRE2_NOTEMPTY is set, or if\n    PCRE2_NOTEMPTY_ATSTART is set and we have matched at the start of the\n    subject. In both cases, backtracking will then try other alternatives, if\n    any. */\n\n    if (Feptr == Fstart_match &&\n         ((mb->moptions & PCRE2_NOTEMPTY) != 0 ||\n           ((mb->moptions & PCRE2_NOTEMPTY_ATSTART) != 0 &&\n             Fstart_match == mb->start_subject + mb->start_offset)))\n      {\n#ifdef DEBUG_SHOW_OPS\n      fprintf(stderr, \"++ Backtrack because empty string\\n\");\n#endif\n      RRETURN(MATCH_NOMATCH);\n      }\n\n    /* Fail if PCRE2_ENDANCHORED is set and the end of the match is not\n    the end of the subject. After (*ACCEPT) we fail the entire match (at this\n    position) but backtrack if we've reached the end of the pattern. This\n    applies whether or not we are in a recursion. */\n\n    if (Feptr < mb->end_subject &&\n        ((mb->moptions | mb->poptions) & PCRE2_ENDANCHORED) != 0)\n      {\n      if (Fop == OP_END)\n        {\n#ifdef DEBUG_SHOW_OPS\n        fprintf(stderr, \"++ Backtrack because not at end (endanchored set)\\n\");\n#endif\n        RRETURN(MATCH_NOMATCH);\n        }\n\n#ifdef DEBUG_SHOW_OPS\n      fprintf(stderr, \"++ Failed ACCEPT not at end (endanchnored set)\\n\");\n#endif\n      return MATCH_NOMATCH;   /* (*ACCEPT) */\n      }\n\n    /* We have a successful match of the whole pattern. Record the result and\n    then do a direct return from the function. If there is space in the offset\n    vector, set any pairs that follow the highest-numbered captured string but\n    are less than the number of capturing groups in the pattern to PCRE2_UNSET.\n    It is documented that this happens. \"Gaps\" are set to PCRE2_UNSET\n    dynamically. It is only those at the end that need setting here. */\n\n    mb->end_match_ptr = Feptr;           /* Record where we ended */\n    mb->end_offset_top = Foffset_top;    /* and how many extracts were taken */\n    mb->mark = Fmark;                    /* and the last success mark */\n    if (Feptr > mb->last_used_ptr) mb->last_used_ptr = Feptr;\n\n    match_data->ovector[0] = Fstart_match - mb->start_subject;\n    match_data->ovector[1] = Feptr - mb->start_subject;\n\n    /* Set i to the smaller of the sizes of the external and frame ovectors. */\n\n    i = 2 * ((top_bracket + 1 > match_data->oveccount)?\n      match_data->oveccount : top_bracket + 1);\n    memcpy(match_data->ovector + 2, Fovector, (i - 2) * sizeof(PCRE2_SIZE));\n    while (--i >= Foffset_top + 2) match_data->ovector[i] = PCRE2_UNSET;\n    return MATCH_MATCH;  /* Note: NOT RRETURN */\n\n\n    /*===================================================================== */\n    /* Match any single character type except newline; have to take care with\n    CRLF newlines and partial matching. */\n\n    case OP_ANY:\n    if (IS_NEWLINE(Feptr)) RRETURN(MATCH_NOMATCH);\n    if (mb->partial != 0 &&\n        Feptr == mb->end_subject - 1 &&\n        NLBLOCK->nltype == NLTYPE_FIXED &&\n        NLBLOCK->nllen == 2 &&\n        UCHAR21TEST(Feptr) == NLBLOCK->nl[0])\n      {\n      mb->hitend = TRUE;\n      if (mb->partial > 1) return PCRE2_ERROR_PARTIAL;\n      }\n    /* Fall through */\n\n    /* Match any single character whatsoever. */\n\n    case OP_ALLANY:\n    if (Feptr >= mb->end_subject)  /* DO NOT merge the Feptr++ here; it must */\n      {                            /* not be updated before SCHECK_PARTIAL. */\n      SCHECK_PARTIAL();\n      RRETURN(MATCH_NOMATCH);\n      }\n    Feptr++;\n#ifdef SUPPORT_UNICODE\n    if (utf) ACROSSCHAR(Feptr < mb->end_subject, Feptr, Feptr++);\n#endif\n    Fecode++;\n    break;\n\n\n    /* ===================================================================== */\n    /* Match a single code unit, even in UTF mode. This opcode really does\n    match any code unit, even newline. (It really should be called ANYCODEUNIT,\n    of course - the byte name is from pre-16 bit days.) */\n\n    case OP_ANYBYTE:\n    if (Feptr >= mb->end_subject)   /* DO NOT merge the Feptr++ here; it must */\n      {                             /* not be updated before SCHECK_PARTIAL. */\n      SCHECK_PARTIAL();\n      RRETURN(MATCH_NOMATCH);\n      }\n    Feptr++;\n    Fecode++;\n    break;\n\n\n    /* ===================================================================== */\n    /* Match a single character, casefully */\n\n    case OP_CHAR:\n#ifdef SUPPORT_UNICODE\n    if (utf)\n      {\n      Flength = 1;\n      Fecode++;\n      GETCHARLEN(fc, Fecode, Flength);\n      if (Flength > (PCRE2_SIZE)(mb->end_subject - Feptr))\n        {\n        CHECK_PARTIAL();             /* Not SCHECK_PARTIAL() */\n        RRETURN(MATCH_NOMATCH);\n        }\n      for (; Flength > 0; Flength--)\n        {\n        if (*Fecode++ != UCHAR21INC(Feptr)) RRETURN(MATCH_NOMATCH);\n        }\n      }\n    else\n#endif\n\n    /* Not UTF mode */\n      {\n      if (mb->end_subject - Feptr < 1)\n        {\n        SCHECK_PARTIAL();            /* This one can use SCHECK_PARTIAL() */\n        RRETURN(MATCH_NOMATCH);\n        }\n      if (Fecode[1] != *Feptr++) RRETURN(MATCH_NOMATCH);\n      Fecode += 2;\n      }\n    break;\n\n\n    /* ===================================================================== */\n    /* Match a single character, caselessly. If we are at the end of the\n    subject, give up immediately. We get here only when the pattern character\n    has at most one other case. Characters with more than two cases are coded\n    as OP_PROP with the pseudo-property PT_CLIST. */\n\n    case OP_CHARI:\n    if (Feptr >= mb->end_subject)\n      {\n      SCHECK_PARTIAL();\n      RRETURN(MATCH_NOMATCH);\n      }\n\n#ifdef SUPPORT_UNICODE\n    if (utf)\n      {\n      Flength = 1;\n      Fecode++;\n      GETCHARLEN(fc, Fecode, Flength);\n\n      /* If the pattern character's value is < 128, we know that its other case\n      (if any) is also < 128 (and therefore only one code unit long in all\n      code-unit widths), so we can use the fast lookup table. We checked above\n      that there is at least one character left in the subject. */\n\n      if (fc < 128)\n        {\n        uint32_t cc = UCHAR21(Feptr);\n        if (mb->lcc[fc] != TABLE_GET(cc, mb->lcc, cc)) RRETURN(MATCH_NOMATCH);\n        Fecode++;\n        Feptr++;\n        }\n\n      /* Otherwise we must pick up the subject character and use Unicode\n      property support to test its other case. Note that we cannot use the\n      value of \"Flength\" to check for sufficient bytes left, because the other\n      case of the character may have more or fewer code units. */\n\n      else\n        {\n        uint32_t dc;\n        GETCHARINC(dc, Feptr);\n        Fecode += Flength;\n        if (dc != fc && dc != UCD_OTHERCASE(fc)) RRETURN(MATCH_NOMATCH);\n        }\n      }\n\n    /* If UCP is set without UTF we must do the same as above, but with one\n    character per code unit. */\n\n    else if (ucp)\n      {\n      uint32_t cc = UCHAR21(Feptr);\n      fc = Fecode[1];\n      if (fc < 128)\n        {\n        if (mb->lcc[fc] != TABLE_GET(cc, mb->lcc, cc)) RRETURN(MATCH_NOMATCH);\n        }\n      else\n        {\n        if (cc != fc && cc != UCD_OTHERCASE(fc)) RRETURN(MATCH_NOMATCH);\n        }\n      Feptr++;\n      Fecode += 2;\n      }\n\n    else\n#endif   /* SUPPORT_UNICODE */\n\n    /* Not UTF or UCP mode; use the table for characters < 256. */\n      {\n      if (TABLE_GET(Fecode[1], mb->lcc, Fecode[1])\n          != TABLE_GET(*Feptr, mb->lcc, *Feptr)) RRETURN(MATCH_NOMATCH);\n      Feptr++;\n      Fecode += 2;\n      }\n    break;\n\n\n    /* ===================================================================== */\n    /* Match not a single character. */\n\n    case OP_NOT:\n    case OP_NOTI:\n    if (Feptr >= mb->end_subject)\n      {\n      SCHECK_PARTIAL();\n      RRETURN(MATCH_NOMATCH);\n      }\n\n#ifdef SUPPORT_UNICODE\n    if (utf)\n      {\n      uint32_t ch;\n      Fecode++;\n      GETCHARINC(ch, Fecode);\n      GETCHARINC(fc, Feptr);\n      if (ch == fc)\n        {\n        RRETURN(MATCH_NOMATCH);  /* Caseful match */\n        }\n      else if (Fop == OP_NOTI)   /* If caseless */\n        {\n        if (ch > 127)\n          ch = UCD_OTHERCASE(ch);\n        else\n          ch = (mb->fcc)[ch];\n        if (ch == fc) RRETURN(MATCH_NOMATCH);\n        }\n      }\n\n    /* UCP without UTF is as above, but with one character per code unit. */\n\n    else if (ucp)\n      {\n      uint32_t ch;\n      fc = UCHAR21INC(Feptr);\n      ch = Fecode[1];\n      Fecode += 2;\n\n      if (ch == fc)\n        {\n        RRETURN(MATCH_NOMATCH);  /* Caseful match */\n        }\n      else if (Fop == OP_NOTI)   /* If caseless */\n        {\n        if (ch > 127)\n          ch = UCD_OTHERCASE(ch);\n        else\n          ch = (mb->fcc)[ch];\n        if (ch == fc) RRETURN(MATCH_NOMATCH);\n        }\n      }\n\n    else\n#endif  /* SUPPORT_UNICODE */\n\n    /* Neither UTF nor UCP is set */\n\n      {\n      uint32_t ch = Fecode[1];\n      fc = UCHAR21INC(Feptr);\n      if (ch == fc || (Fop == OP_NOTI && TABLE_GET(ch, mb->fcc, ch) == fc))\n        RRETURN(MATCH_NOMATCH);\n      Fecode += 2;\n      }\n    break;\n\n\n    /* ===================================================================== */\n    /* Match a single character repeatedly. */\n\n#define Loclength    F->temp_size\n#define Lstart_eptr  F->temp_sptr[0]\n#define Lcharptr     F->temp_sptr[1]\n#define Lmin         F->temp_32[0]\n#define Lmax         F->temp_32[1]\n#define Lc           F->temp_32[2]\n#define Loc          F->temp_32[3]\n\n    case OP_EXACT:\n    case OP_EXACTI:\n    Lmin = Lmax = GET2(Fecode, 1);\n    Fecode += 1 + IMM2_SIZE;\n    goto REPEATCHAR;\n\n    case OP_POSUPTO:\n    case OP_POSUPTOI:\n    reptype = REPTYPE_POS;\n    Lmin = 0;\n    Lmax = GET2(Fecode, 1);\n    Fecode += 1 + IMM2_SIZE;\n    goto REPEATCHAR;\n\n    case OP_UPTO:\n    case OP_UPTOI:\n    reptype = REPTYPE_MAX;\n    Lmin = 0;\n    Lmax = GET2(Fecode, 1);\n    Fecode += 1 + IMM2_SIZE;\n    goto REPEATCHAR;\n\n    case OP_MINUPTO:\n    case OP_MINUPTOI:\n    reptype = REPTYPE_MIN;\n    Lmin = 0;\n    Lmax = GET2(Fecode, 1);\n    Fecode += 1 + IMM2_SIZE;\n    goto REPEATCHAR;\n\n    case OP_POSSTAR:\n    case OP_POSSTARI:\n    reptype = REPTYPE_POS;\n    Lmin = 0;\n    Lmax = UINT32_MAX;\n    Fecode++;\n    goto REPEATCHAR;\n\n    case OP_POSPLUS:\n    case OP_POSPLUSI:\n    reptype = REPTYPE_POS;\n    Lmin = 1;\n    Lmax = UINT32_MAX;\n    Fecode++;\n    goto REPEATCHAR;\n\n    case OP_POSQUERY:\n    case OP_POSQUERYI:\n    reptype = REPTYPE_POS;\n    Lmin = 0;\n    Lmax = 1;\n    Fecode++;\n    goto REPEATCHAR;\n\n    case OP_STAR:\n    case OP_STARI:\n    case OP_MINSTAR:\n    case OP_MINSTARI:\n    case OP_PLUS:\n    case OP_PLUSI:\n    case OP_MINPLUS:\n    case OP_MINPLUSI:\n    case OP_QUERY:\n    case OP_QUERYI:\n    case OP_MINQUERY:\n    case OP_MINQUERYI:\n    fc = *Fecode++ - ((Fop < OP_STARI)? OP_STAR : OP_STARI);\n    Lmin = rep_min[fc];\n    Lmax = rep_max[fc];\n    reptype = rep_typ[fc];\n\n    /* Common code for all repeated single-character matches. We first check\n    for the minimum number of characters. If the minimum equals the maximum, we\n    are done. Otherwise, if minimizing, check the rest of the pattern for a\n    match; if there isn't one, advance up to the maximum, one character at a\n    time.\n\n    If maximizing, advance up to the maximum number of matching characters,\n    until Feptr is past the end of the maximum run. If possessive, we are\n    then done (no backing up). Otherwise, match at this position; anything\n    other than no match is immediately returned. For nomatch, back up one\n    character, unless we are matching \\R and the last thing matched was\n    \\r\\n, in which case, back up two code units until we reach the first\n    optional character position.\n\n    The various UTF/non-UTF and caseful/caseless cases are handled separately,\n    for speed. */\n\n    REPEATCHAR:\n#ifdef SUPPORT_UNICODE\n    if (utf)\n      {\n      Flength = 1;\n      Lcharptr = Fecode;\n      GETCHARLEN(fc, Fecode, Flength);\n      Fecode += Flength;\n\n      /* Handle multi-code-unit character matching, caseful and caseless. */\n\n      if (Flength > 1)\n        {\n        uint32_t othercase;\n\n        if (Fop >= OP_STARI &&     /* Caseless */\n            (othercase = UCD_OTHERCASE(fc)) != fc)\n          Loclength = PRIV(ord2utf)(othercase, Foccu);\n        else Loclength = 0;\n\n        for (i = 1; i <= Lmin; i++)\n          {\n          if (Feptr <= mb->end_subject - Flength &&\n            memcmp(Feptr, Lcharptr, CU2BYTES(Flength)) == 0) Feptr += Flength;\n          else if (Loclength > 0 &&\n                   Feptr <= mb->end_subject - Loclength &&\n                   memcmp(Feptr, Foccu, CU2BYTES(Loclength)) == 0)\n            Feptr += Loclength;\n          else\n            {\n            CHECK_PARTIAL();\n            RRETURN(MATCH_NOMATCH);\n            }\n          }\n\n        if (Lmin == Lmax) continue;\n\n        if (reptype == REPTYPE_MIN)\n          {\n          for (;;)\n            {\n            RMATCH(Fecode, RM202);\n            if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n            if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);\n            if (Feptr <= mb->end_subject - Flength &&\n              memcmp(Feptr, Lcharptr, CU2BYTES(Flength)) == 0) Feptr += Flength;\n            else if (Loclength > 0 &&\n                     Feptr <= mb->end_subject - Loclength &&\n                     memcmp(Feptr, Foccu, CU2BYTES(Loclength)) == 0)\n              Feptr += Loclength;\n            else\n              {\n              CHECK_PARTIAL();\n              RRETURN(MATCH_NOMATCH);\n              }\n            }\n          PCRE2_UNREACHABLE(); /* Control never reaches here */\n          }\n\n        else  /* Maximize */\n          {\n          Lstart_eptr = Feptr;\n          for (i = Lmin; i < Lmax; i++)\n            {\n            if (Feptr <= mb->end_subject - Flength &&\n                memcmp(Feptr, Lcharptr, CU2BYTES(Flength)) == 0)\n              Feptr += Flength;\n            else if (Loclength > 0 &&\n                     Feptr <= mb->end_subject - Loclength &&\n                     memcmp(Feptr, Foccu, CU2BYTES(Loclength)) == 0)\n              Feptr += Loclength;\n            else\n              {\n              CHECK_PARTIAL();\n              break;\n              }\n            }\n\n          /* After \\C in UTF mode, Lstart_eptr might be in the middle of a\n          Unicode character. Use <= Lstart_eptr to ensure backtracking doesn't\n          go too far. */\n\n          if (reptype != REPTYPE_POS) for(;;)\n            {\n            if (Feptr <= Lstart_eptr) break;\n            RMATCH(Fecode, RM203);\n            if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n            Feptr--;\n            BACKCHAR(Feptr);\n            }\n          }\n        break;   /* End of repeated wide character handling */\n        }\n\n      /* Length of UTF character is 1. Put it into the preserved variable and\n      fall through to the non-UTF code. */\n\n      Lc = fc;\n      }\n    else\n#endif  /* SUPPORT_UNICODE */\n\n    /* When not in UTF mode, load a single-code-unit character. Then proceed as\n    above, using Unicode casing if either UTF or UCP is set. */\n\n    Lc = *Fecode++;\n\n    /* Caseless comparison */\n\n    if (Fop >= OP_STARI)\n      {\n#if PCRE2_CODE_UNIT_WIDTH == 8\n#ifdef SUPPORT_UNICODE\n      if (ucp && !utf && Lc > 127) Loc = UCD_OTHERCASE(Lc);\n      else\n#endif  /* SUPPORT_UNICODE */\n      /* Lc will be < 128 in UTF-8 mode. */\n      Loc = mb->fcc[Lc];\n#else /* 16-bit & 32-bit */\n#ifdef SUPPORT_UNICODE\n      if ((utf || ucp) && Lc > 127) Loc = UCD_OTHERCASE(Lc);\n      else\n#endif  /* SUPPORT_UNICODE */\n      Loc = TABLE_GET(Lc, mb->fcc, Lc);\n#endif  /* PCRE2_CODE_UNIT_WIDTH == 8 */\n\n      for (i = 1; i <= Lmin; i++)\n        {\n        uint32_t cc;                 /* Faster than PCRE2_UCHAR */\n        if (Feptr >= mb->end_subject)\n          {\n          SCHECK_PARTIAL();\n          RRETURN(MATCH_NOMATCH);\n          }\n        cc = UCHAR21TEST(Feptr);\n        if (Lc != cc && Loc != cc) RRETURN(MATCH_NOMATCH);\n        Feptr++;\n        }\n      if (Lmin == Lmax) continue;\n\n      if (reptype == REPTYPE_MIN)\n        {\n        for (;;)\n          {\n          uint32_t cc;               /* Faster than PCRE2_UCHAR */\n          RMATCH(Fecode, RM25);\n          if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n          if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);\n          if (Feptr >= mb->end_subject)\n            {\n            SCHECK_PARTIAL();\n            RRETURN(MATCH_NOMATCH);\n            }\n          cc = UCHAR21TEST(Feptr);\n          if (Lc != cc && Loc != cc) RRETURN(MATCH_NOMATCH);\n          Feptr++;\n          }\n        PCRE2_UNREACHABLE(); /* Control never reaches here */\n        }\n\n      else  /* Maximize */\n        {\n        Lstart_eptr = Feptr;\n        for (i = Lmin; i < Lmax; i++)\n          {\n          uint32_t cc;               /* Faster than PCRE2_UCHAR */\n          if (Feptr >= mb->end_subject)\n            {\n            SCHECK_PARTIAL();\n            break;\n            }\n          cc = UCHAR21TEST(Feptr);\n          if (Lc != cc && Loc != cc) break;\n          Feptr++;\n          }\n        if (reptype != REPTYPE_POS) for (;;)\n          {\n          if (Feptr == Lstart_eptr) break;\n          RMATCH(Fecode, RM26);\n          Feptr--;\n          if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n          }\n        }\n      }\n\n    /* Caseful comparisons (includes all multi-byte characters) */\n\n    else\n      {\n      for (i = 1; i <= Lmin; i++)\n        {\n        if (Feptr >= mb->end_subject)\n          {\n          SCHECK_PARTIAL();\n          RRETURN(MATCH_NOMATCH);\n          }\n        if (Lc != UCHAR21INCTEST(Feptr)) RRETURN(MATCH_NOMATCH);\n        }\n\n      if (Lmin == Lmax) continue;\n\n      if (reptype == REPTYPE_MIN)\n        {\n        for (;;)\n          {\n          RMATCH(Fecode, RM27);\n          if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n          if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);\n          if (Feptr >= mb->end_subject)\n            {\n            SCHECK_PARTIAL();\n            RRETURN(MATCH_NOMATCH);\n            }\n          if (Lc != UCHAR21INCTEST(Feptr)) RRETURN(MATCH_NOMATCH);\n          }\n        PCRE2_UNREACHABLE(); /* Control never reaches here */\n        }\n      else  /* Maximize */\n        {\n        Lstart_eptr = Feptr;\n        for (i = Lmin; i < Lmax; i++)\n          {\n          if (Feptr >= mb->end_subject)\n            {\n            SCHECK_PARTIAL();\n            break;\n            }\n\n          if (Lc != UCHAR21TEST(Feptr)) break;\n          Feptr++;\n          }\n\n        if (reptype != REPTYPE_POS) for (;;)\n          {\n          if (Feptr <= Lstart_eptr) break;\n          RMATCH(Fecode, RM28);\n          Feptr--;\n          if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n          }\n        }\n      }\n    break;\n\n#undef Loclength\n#undef Lstart_eptr\n#undef Lcharptr\n#undef Lmin\n#undef Lmax\n#undef Lc\n#undef Loc\n\n\n    /* ===================================================================== */\n    /* Match a negated single one-byte character repeatedly. This is almost a\n    repeat of the code for a repeated single character, but I haven't found a\n    nice way of commoning these up that doesn't require a test of the\n    positive/negative option for each character match. Maybe that wouldn't add\n    very much to the time taken, but character matching *is* what this is all\n    about... */\n\n#define Lstart_eptr  F->temp_sptr[0]\n#define Lmin         F->temp_32[0]\n#define Lmax         F->temp_32[1]\n#define Lc           F->temp_32[2]\n#define Loc          F->temp_32[3]\n\n    case OP_NOTEXACT:\n    case OP_NOTEXACTI:\n    Lmin = Lmax = GET2(Fecode, 1);\n    Fecode += 1 + IMM2_SIZE;\n    goto REPEATNOTCHAR;\n\n    case OP_NOTUPTO:\n    case OP_NOTUPTOI:\n    Lmin = 0;\n    Lmax = GET2(Fecode, 1);\n    reptype = REPTYPE_MAX;\n    Fecode += 1 + IMM2_SIZE;\n    goto REPEATNOTCHAR;\n\n    case OP_NOTMINUPTO:\n    case OP_NOTMINUPTOI:\n    Lmin = 0;\n    Lmax = GET2(Fecode, 1);\n    reptype = REPTYPE_MIN;\n    Fecode += 1 + IMM2_SIZE;\n    goto REPEATNOTCHAR;\n\n    case OP_NOTPOSSTAR:\n    case OP_NOTPOSSTARI:\n    reptype = REPTYPE_POS;\n    Lmin = 0;\n    Lmax = UINT32_MAX;\n    Fecode++;\n    goto REPEATNOTCHAR;\n\n    case OP_NOTPOSPLUS:\n    case OP_NOTPOSPLUSI:\n    reptype = REPTYPE_POS;\n    Lmin = 1;\n    Lmax = UINT32_MAX;\n    Fecode++;\n    goto REPEATNOTCHAR;\n\n    case OP_NOTPOSQUERY:\n    case OP_NOTPOSQUERYI:\n    reptype = REPTYPE_POS;\n    Lmin = 0;\n    Lmax = 1;\n    Fecode++;\n    goto REPEATNOTCHAR;\n\n    case OP_NOTPOSUPTO:\n    case OP_NOTPOSUPTOI:\n    reptype = REPTYPE_POS;\n    Lmin = 0;\n    Lmax = GET2(Fecode, 1);\n    Fecode += 1 + IMM2_SIZE;\n    goto REPEATNOTCHAR;\n\n    case OP_NOTSTAR:\n    case OP_NOTSTARI:\n    case OP_NOTMINSTAR:\n    case OP_NOTMINSTARI:\n    case OP_NOTPLUS:\n    case OP_NOTPLUSI:\n    case OP_NOTMINPLUS:\n    case OP_NOTMINPLUSI:\n    case OP_NOTQUERY:\n    case OP_NOTQUERYI:\n    case OP_NOTMINQUERY:\n    case OP_NOTMINQUERYI:\n    fc = *Fecode++ - ((Fop >= OP_NOTSTARI)? OP_NOTSTARI: OP_NOTSTAR);\n    Lmin = rep_min[fc];\n    Lmax = rep_max[fc];\n    reptype = rep_typ[fc];\n\n    /* Common code for all repeated single-character non-matches. */\n\n    REPEATNOTCHAR:\n    GETCHARINCTEST(Lc, Fecode);\n\n    /* The code is duplicated for the caseless and caseful cases, for speed,\n    since matching characters is likely to be quite common. First, ensure the\n    minimum number of matches are present. If Lmin = Lmax, we are done.\n    Otherwise, if minimizing, keep trying the rest of the expression and\n    advancing one matching character if failing, up to the maximum.\n    Alternatively, if maximizing, find the maximum number of characters and\n    work backwards. */\n\n    if (Fop >= OP_NOTSTARI)     /* Caseless */\n      {\n#ifdef SUPPORT_UNICODE\n      if ((utf || ucp) && Lc > 127)\n        Loc = UCD_OTHERCASE(Lc);\n      else\n#endif /* SUPPORT_UNICODE */\n\n      Loc = TABLE_GET(Lc, mb->fcc, Lc);  /* Other case from table */\n\n#ifdef SUPPORT_UNICODE\n      if (utf)\n        {\n        uint32_t d;\n        for (i = 1; i <= Lmin; i++)\n          {\n          if (Feptr >= mb->end_subject)\n            {\n            SCHECK_PARTIAL();\n            RRETURN(MATCH_NOMATCH);\n            }\n          GETCHARINC(d, Feptr);\n          if (Lc == d || Loc == d) RRETURN(MATCH_NOMATCH);\n          }\n        }\n      else\n#endif  /* SUPPORT_UNICODE */\n\n      /* Not UTF mode */\n        {\n        for (i = 1; i <= Lmin; i++)\n          {\n          if (Feptr >= mb->end_subject)\n            {\n            SCHECK_PARTIAL();\n            RRETURN(MATCH_NOMATCH);\n            }\n          if (Lc == *Feptr || Loc == *Feptr) RRETURN(MATCH_NOMATCH);\n          Feptr++;\n          }\n        }\n\n      if (Lmin == Lmax) continue;  /* Finished for exact count */\n\n      if (reptype == REPTYPE_MIN)\n        {\n#ifdef SUPPORT_UNICODE\n        if (utf)\n          {\n          uint32_t d;\n          for (;;)\n            {\n            RMATCH(Fecode, RM204);\n            if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n            if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              RRETURN(MATCH_NOMATCH);\n              }\n            GETCHARINC(d, Feptr);\n            if (Lc == d || Loc == d) RRETURN(MATCH_NOMATCH);\n            }\n          }\n        else\n#endif  /*SUPPORT_UNICODE */\n\n        /* Not UTF mode */\n          {\n          for (;;)\n            {\n            RMATCH(Fecode, RM29);\n            if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n            if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              RRETURN(MATCH_NOMATCH);\n              }\n            if (Lc == *Feptr || Loc == *Feptr) RRETURN(MATCH_NOMATCH);\n            Feptr++;\n            }\n          }\n        PCRE2_UNREACHABLE(); /* Control never reaches here */\n        }\n\n      /* Maximize case */\n\n      else\n        {\n        Lstart_eptr = Feptr;\n\n#ifdef SUPPORT_UNICODE\n        if (utf)\n          {\n          uint32_t d;\n          for (i = Lmin; i < Lmax; i++)\n            {\n            int len = 1;\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              break;\n              }\n            GETCHARLEN(d, Feptr, len);\n            if (Lc == d || Loc == d) break;\n            Feptr += len;\n            }\n\n          /* After \\C in UTF mode, Lstart_eptr might be in the middle of a\n          Unicode character. Use <= Lstart_eptr to ensure backtracking doesn't\n          go too far. */\n\n          if (reptype != REPTYPE_POS) for(;;)\n            {\n            if (Feptr <= Lstart_eptr) break;\n            RMATCH(Fecode, RM205);\n            if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n            Feptr--;\n            BACKCHAR(Feptr);\n            }\n          }\n        else\n#endif  /* SUPPORT_UNICODE */\n\n        /* Not UTF mode */\n          {\n          for (i = Lmin; i < Lmax; i++)\n            {\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              break;\n              }\n            if (Lc == *Feptr || Loc == *Feptr) break;\n            Feptr++;\n            }\n          if (reptype != REPTYPE_POS) for (;;)\n            {\n            if (Feptr == Lstart_eptr) break;\n            RMATCH(Fecode, RM30);\n            if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n            Feptr--;\n            }\n          }\n        }\n      }\n\n    /* Caseful comparisons */\n\n    else\n      {\n#ifdef SUPPORT_UNICODE\n      if (utf)\n        {\n        uint32_t d;\n        for (i = 1; i <= Lmin; i++)\n          {\n          if (Feptr >= mb->end_subject)\n            {\n            SCHECK_PARTIAL();\n            RRETURN(MATCH_NOMATCH);\n            }\n          GETCHARINC(d, Feptr);\n          if (Lc == d) RRETURN(MATCH_NOMATCH);\n          }\n        }\n      else\n#endif\n      /* Not UTF mode */\n        {\n        for (i = 1; i <= Lmin; i++)\n          {\n          if (Feptr >= mb->end_subject)\n            {\n            SCHECK_PARTIAL();\n            RRETURN(MATCH_NOMATCH);\n            }\n          if (Lc == *Feptr++) RRETURN(MATCH_NOMATCH);\n          }\n        }\n\n      if (Lmin == Lmax) continue;\n\n      if (reptype == REPTYPE_MIN)\n        {\n#ifdef SUPPORT_UNICODE\n        if (utf)\n          {\n          uint32_t d;\n          for (;;)\n            {\n            RMATCH(Fecode, RM206);\n            if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n            if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              RRETURN(MATCH_NOMATCH);\n              }\n            GETCHARINC(d, Feptr);\n            if (Lc == d) RRETURN(MATCH_NOMATCH);\n            }\n          }\n        else\n#endif\n        /* Not UTF mode */\n          {\n          for (;;)\n            {\n            RMATCH(Fecode, RM31);\n            if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n            if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              RRETURN(MATCH_NOMATCH);\n              }\n            if (Lc == *Feptr++) RRETURN(MATCH_NOMATCH);\n            }\n          }\n        PCRE2_UNREACHABLE(); /* Control never reaches here */\n        }\n\n      /* Maximize case */\n\n      else\n        {\n        Lstart_eptr = Feptr;\n\n#ifdef SUPPORT_UNICODE\n        if (utf)\n          {\n          uint32_t d;\n          for (i = Lmin; i < Lmax; i++)\n            {\n            int len = 1;\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              break;\n              }\n            GETCHARLEN(d, Feptr, len);\n            if (Lc == d) break;\n            Feptr += len;\n            }\n\n          /* After \\C in UTF mode, Lstart_eptr might be in the middle of a\n          Unicode character. Use <= Lstart_eptr to ensure backtracking doesn't\n          go too far. */\n\n          if (reptype != REPTYPE_POS) for(;;)\n            {\n            if (Feptr <= Lstart_eptr) break;\n            RMATCH(Fecode, RM207);\n            if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n            Feptr--;\n            BACKCHAR(Feptr);\n            }\n          }\n        else\n#endif\n        /* Not UTF mode */\n          {\n          for (i = Lmin; i < Lmax; i++)\n            {\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              break;\n              }\n            if (Lc == *Feptr) break;\n            Feptr++;\n            }\n          if (reptype != REPTYPE_POS) for (;;)\n            {\n            if (Feptr == Lstart_eptr) break;\n            RMATCH(Fecode, RM32);\n            if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n            Feptr--;\n            }\n          }\n        }\n      }\n    break;\n\n#undef Lstart_eptr\n#undef Lmin\n#undef Lmax\n#undef Lc\n#undef Loc\n\n\n    /* ===================================================================== */\n    /* Match a bit-mapped character class, possibly repeatedly. These opcodes\n    are used when all the characters in the class have values in the range\n    0-255, and either the matching is caseful, or the characters are in the\n    range 0-127 when UTF processing is enabled. The only difference between\n    OP_CLASS and OP_NCLASS occurs when a data character outside the range is\n    encountered. */\n\n#define Lmin               F->temp_32[0]\n#define Lmax               F->temp_32[1]\n#define Lstart_eptr        F->temp_sptr[0]\n#define Lbyte_map_address  F->temp_sptr[1]\n#define Lbyte_map          ((const unsigned char *)Lbyte_map_address)\n\n    case OP_NCLASS:\n    case OP_CLASS:\n      {\n      Lbyte_map_address = Fecode + 1;           /* Save for matching */\n      Fecode += 1 + (32 / sizeof(PCRE2_UCHAR)); /* Advance past the item */\n\n      /* Look past the end of the item to see if there is repeat information\n      following. Then obey similar code to character type repeats. */\n\n      switch (*Fecode)\n        {\n        case OP_CRSTAR:\n        case OP_CRMINSTAR:\n        case OP_CRPLUS:\n        case OP_CRMINPLUS:\n        case OP_CRQUERY:\n        case OP_CRMINQUERY:\n        case OP_CRPOSSTAR:\n        case OP_CRPOSPLUS:\n        case OP_CRPOSQUERY:\n        fc = *Fecode++ - OP_CRSTAR;\n        Lmin = rep_min[fc];\n        Lmax = rep_max[fc];\n        reptype = rep_typ[fc];\n        break;\n\n        case OP_CRRANGE:\n        case OP_CRMINRANGE:\n        case OP_CRPOSRANGE:\n        Lmin = GET2(Fecode, 1);\n        Lmax = GET2(Fecode, 1 + IMM2_SIZE);\n        if (Lmax == 0) Lmax = UINT32_MAX;       /* Max 0 => infinity */\n        reptype = rep_typ[*Fecode - OP_CRSTAR];\n        Fecode += 1 + 2 * IMM2_SIZE;\n        break;\n\n        default:               /* No repeat follows */\n        Lmin = Lmax = 1;\n        break;\n        }\n\n      /* First, ensure the minimum number of matches are present. */\n\n#ifdef SUPPORT_UNICODE\n      if (utf)\n        {\n        for (i = 1; i <= Lmin; i++)\n          {\n          if (Feptr >= mb->end_subject)\n            {\n            SCHECK_PARTIAL();\n            RRETURN(MATCH_NOMATCH);\n            }\n          GETCHARINC(fc, Feptr);\n          if (fc > 255)\n            {\n            if (Fop == OP_CLASS) RRETURN(MATCH_NOMATCH);\n            }\n          else\n            if ((Lbyte_map[fc/8] & (1u << (fc&7))) == 0) RRETURN(MATCH_NOMATCH);\n          }\n        }\n      else\n#endif\n      /* Not UTF mode */\n        {\n        for (i = 1; i <= Lmin; i++)\n          {\n          if (Feptr >= mb->end_subject)\n            {\n            SCHECK_PARTIAL();\n            RRETURN(MATCH_NOMATCH);\n            }\n          fc = *Feptr++;\n#if PCRE2_CODE_UNIT_WIDTH != 8\n          if (fc > 255)\n            {\n            if (Fop == OP_CLASS) RRETURN(MATCH_NOMATCH);\n            }\n          else\n#endif\n          if ((Lbyte_map[fc/8] & (1u << (fc&7))) == 0) RRETURN(MATCH_NOMATCH);\n          }\n        }\n\n      /* If Lmax == Lmin we are done. Continue with main loop. */\n\n      if (Lmin == Lmax) continue;\n\n      /* If minimizing, keep testing the rest of the expression and advancing\n      the pointer while it matches the class. */\n\n      if (reptype == REPTYPE_MIN)\n        {\n#ifdef SUPPORT_UNICODE\n        if (utf)\n          {\n          for (;;)\n            {\n            RMATCH(Fecode, RM200);\n            if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n            if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              RRETURN(MATCH_NOMATCH);\n              }\n            GETCHARINC(fc, Feptr);\n            if (fc > 255)\n              {\n              if (Fop == OP_CLASS) RRETURN(MATCH_NOMATCH);\n              }\n            else\n              if ((Lbyte_map[fc/8] & (1u << (fc&7))) == 0) RRETURN(MATCH_NOMATCH);\n            }\n          }\n        else\n#endif\n        /* Not UTF mode */\n          {\n          for (;;)\n            {\n            RMATCH(Fecode, RM23);\n            if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n            if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              RRETURN(MATCH_NOMATCH);\n              }\n            fc = *Feptr++;\n#if PCRE2_CODE_UNIT_WIDTH != 8\n            if (fc > 255)\n              {\n              if (Fop == OP_CLASS) RRETURN(MATCH_NOMATCH);\n              }\n            else\n#endif\n            if ((Lbyte_map[fc/8] & (1u << (fc&7))) == 0) RRETURN(MATCH_NOMATCH);\n            }\n          }\n        PCRE2_UNREACHABLE(); /* Control never reaches here */\n        }\n\n      /* If maximizing, find the longest possible run, then work backwards. */\n\n      else\n        {\n        Lstart_eptr = Feptr;\n\n#ifdef SUPPORT_UNICODE\n        if (utf)\n          {\n          for (i = Lmin; i < Lmax; i++)\n            {\n            int len = 1;\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              break;\n              }\n            GETCHARLEN(fc, Feptr, len);\n            if (fc > 255)\n              {\n              if (Fop == OP_CLASS) break;\n              }\n            else\n              if ((Lbyte_map[fc/8] & (1u << (fc&7))) == 0) break;\n            Feptr += len;\n            }\n\n          if (reptype == REPTYPE_POS) continue;    /* No backtracking */\n\n          /* After \\C in UTF mode, Lstart_eptr might be in the middle of a\n          Unicode character. Use <= Lstart_eptr to ensure backtracking doesn't\n          go too far. */\n\n          for (;;)\n            {\n            RMATCH(Fecode, RM201);\n            if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n            if (Feptr-- <= Lstart_eptr) break;  /* Tried at original position */\n            BACKCHAR(Feptr);\n            }\n          }\n        else\n#endif\n          /* Not UTF mode */\n          {\n          for (i = Lmin; i < Lmax; i++)\n            {\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              break;\n              }\n            fc = *Feptr;\n#if PCRE2_CODE_UNIT_WIDTH != 8\n            if (fc > 255)\n              {\n              if (Fop == OP_CLASS) break;\n              }\n            else\n#endif\n            if ((Lbyte_map[fc/8] & (1u << (fc&7))) == 0) break;\n            Feptr++;\n            }\n\n          if (reptype == REPTYPE_POS) continue;    /* No backtracking */\n\n          while (Feptr >= Lstart_eptr)\n            {\n            RMATCH(Fecode, RM24);\n            if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n            Feptr--;\n            }\n          }\n\n        RRETURN(MATCH_NOMATCH);\n        }\n      }\n\n    PCRE2_UNREACHABLE(); /* Control never reaches here */\n\n#undef Lbyte_map_address\n#undef Lbyte_map\n#undef Lstart_eptr\n#undef Lmin\n#undef Lmax\n\n\n    /* ===================================================================== */\n    /* Match an extended character class. In the 8-bit library, this opcode is\n    encountered only when UTF-8 mode mode is supported. In the 16-bit and\n    32-bit libraries, codepoints greater than 255 may be encountered even when\n    UTF is not supported. */\n\n#define Lstart_eptr  F->temp_sptr[0]\n#define Lxclass_data F->temp_sptr[1]\n#define Lmin         F->temp_32[0]\n#define Lmax         F->temp_32[1]\n\n#ifdef SUPPORT_WIDE_CHARS\n    case OP_XCLASS:\n      {\n      Lxclass_data = Fecode + 1 + LINK_SIZE;  /* Save for matching */\n      Fecode += GET(Fecode, 1);               /* Advance past the item */\n\n      switch (*Fecode)\n        {\n        case OP_CRSTAR:\n        case OP_CRMINSTAR:\n        case OP_CRPLUS:\n        case OP_CRMINPLUS:\n        case OP_CRQUERY:\n        case OP_CRMINQUERY:\n        case OP_CRPOSSTAR:\n        case OP_CRPOSPLUS:\n        case OP_CRPOSQUERY:\n        fc = *Fecode++ - OP_CRSTAR;\n        Lmin = rep_min[fc];\n        Lmax = rep_max[fc];\n        reptype = rep_typ[fc];\n        break;\n\n        case OP_CRRANGE:\n        case OP_CRMINRANGE:\n        case OP_CRPOSRANGE:\n        Lmin = GET2(Fecode, 1);\n        Lmax = GET2(Fecode, 1 + IMM2_SIZE);\n        if (Lmax == 0) Lmax = UINT32_MAX;  /* Max 0 => infinity */\n        reptype = rep_typ[*Fecode - OP_CRSTAR];\n        Fecode += 1 + 2 * IMM2_SIZE;\n        break;\n\n        default:               /* No repeat follows */\n        Lmin = Lmax = 1;\n        break;\n        }\n\n      /* First, ensure the minimum number of matches are present. */\n\n      for (i = 1; i <= Lmin; i++)\n        {\n        if (Feptr >= mb->end_subject)\n          {\n          SCHECK_PARTIAL();\n          RRETURN(MATCH_NOMATCH);\n          }\n        GETCHARINCTEST(fc, Feptr);\n        if (!PRIV(xclass)(fc, Lxclass_data,\n            (const uint8_t*)mb->start_code, utf))\n          RRETURN(MATCH_NOMATCH);\n        }\n\n      /* If Lmax == Lmin we can just continue with the main loop. */\n\n      if (Lmin == Lmax) continue;\n\n      /* If minimizing, keep testing the rest of the expression and advancing\n      the pointer while it matches the class. */\n\n      if (reptype == REPTYPE_MIN)\n        {\n        for (;;)\n          {\n          RMATCH(Fecode, RM100);\n          if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n          if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);\n          if (Feptr >= mb->end_subject)\n            {\n            SCHECK_PARTIAL();\n            RRETURN(MATCH_NOMATCH);\n            }\n          GETCHARINCTEST(fc, Feptr);\n          if (!PRIV(xclass)(fc, Lxclass_data,\n              (const uint8_t*)mb->start_code, utf))\n            RRETURN(MATCH_NOMATCH);\n          }\n        PCRE2_UNREACHABLE(); /* Control never reaches here */\n        }\n\n      /* If maximizing, find the longest possible run, then work backwards. */\n\n      else\n        {\n        Lstart_eptr = Feptr;\n        for (i = Lmin; i < Lmax; i++)\n          {\n          int len = 1;\n          if (Feptr >= mb->end_subject)\n            {\n            SCHECK_PARTIAL();\n            break;\n            }\n#ifdef SUPPORT_UNICODE\n          GETCHARLENTEST(fc, Feptr, len);\n#else\n          fc = *Feptr;\n#endif\n          if (!PRIV(xclass)(fc, Lxclass_data,\n              (const uint8_t*)mb->start_code, utf)) break;\n          Feptr += len;\n          }\n\n        if (reptype == REPTYPE_POS) continue;    /* No backtracking */\n\n        /* After \\C in UTF mode, Lstart_eptr might be in the middle of a\n        Unicode character. Use <= Lstart_eptr to ensure backtracking doesn't\n        go too far. */\n\n        for(;;)\n          {\n          RMATCH(Fecode, RM101);\n          if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n          if (Feptr-- <= Lstart_eptr) break;  /* Tried at original position */\n#ifdef SUPPORT_UNICODE\n          if (utf) BACKCHAR(Feptr);\n#endif\n          }\n        RRETURN(MATCH_NOMATCH);\n        }\n\n      PCRE2_UNREACHABLE(); /* Control never reaches here */\n      }\n#endif  /* SUPPORT_WIDE_CHARS: end of XCLASS */\n\n#undef Lstart_eptr\n#undef Lxclass_data\n#undef Lmin\n#undef Lmax\n\n\n    /* ===================================================================== */\n    /* Match a complex, set-based character class. This opcodes are used when\n    there is complex nesting or logical operations within the character\n    class. */\n\n#define Lstart_eptr  F->temp_sptr[0]\n#define Leclass_data F->temp_sptr[1]\n#define Leclass_len  F->temp_size\n#define Lmin         F->temp_32[0]\n#define Lmax         F->temp_32[1]\n\n#ifdef SUPPORT_WIDE_CHARS\n    case OP_ECLASS:\n      {\n      Leclass_data = Fecode + 1 + LINK_SIZE;  /* Save for matching */\n      Fecode += GET(Fecode, 1);               /* Advance past the item */\n      Leclass_len = (PCRE2_SIZE)(Fecode - Leclass_data);\n\n      switch (*Fecode)\n        {\n        case OP_CRSTAR:\n        case OP_CRMINSTAR:\n        case OP_CRPLUS:\n        case OP_CRMINPLUS:\n        case OP_CRQUERY:\n        case OP_CRMINQUERY:\n        case OP_CRPOSSTAR:\n        case OP_CRPOSPLUS:\n        case OP_CRPOSQUERY:\n        fc = *Fecode++ - OP_CRSTAR;\n        Lmin = rep_min[fc];\n        Lmax = rep_max[fc];\n        reptype = rep_typ[fc];\n        break;\n\n        case OP_CRRANGE:\n        case OP_CRMINRANGE:\n        case OP_CRPOSRANGE:\n        Lmin = GET2(Fecode, 1);\n        Lmax = GET2(Fecode, 1 + IMM2_SIZE);\n        if (Lmax == 0) Lmax = UINT32_MAX;  /* Max 0 => infinity */\n        reptype = rep_typ[*Fecode - OP_CRSTAR];\n        Fecode += 1 + 2 * IMM2_SIZE;\n        break;\n\n        default:               /* No repeat follows */\n        Lmin = Lmax = 1;\n        break;\n        }\n\n      /* First, ensure the minimum number of matches are present. */\n\n      for (i = 1; i <= Lmin; i++)\n        {\n        if (Feptr >= mb->end_subject)\n          {\n          SCHECK_PARTIAL();\n          RRETURN(MATCH_NOMATCH);\n          }\n        GETCHARINCTEST(fc, Feptr);\n        if (!PRIV(eclass)(fc, Leclass_data, Leclass_data + Leclass_len,\n                          (const uint8_t*)mb->start_code, utf))\n          RRETURN(MATCH_NOMATCH);\n        }\n\n      /* If Lmax == Lmin we can just continue with the main loop. */\n\n      if (Lmin == Lmax) continue;\n\n      /* If minimizing, keep testing the rest of the expression and advancing\n      the pointer while it matches the class. */\n\n      if (reptype == REPTYPE_MIN)\n        {\n        for (;;)\n          {\n          RMATCH(Fecode, RM102);\n          if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n          if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);\n          if (Feptr >= mb->end_subject)\n            {\n            SCHECK_PARTIAL();\n            RRETURN(MATCH_NOMATCH);\n            }\n          GETCHARINCTEST(fc, Feptr);\n          if (!PRIV(eclass)(fc, Leclass_data, Leclass_data + Leclass_len,\n                            (const uint8_t*)mb->start_code, utf))\n            RRETURN(MATCH_NOMATCH);\n          }\n        PCRE2_UNREACHABLE(); /* Control never reaches here */\n        }\n\n      /* If maximizing, find the longest possible run, then work backwards. */\n\n      else\n        {\n        Lstart_eptr = Feptr;\n        for (i = Lmin; i < Lmax; i++)\n          {\n          int len = 1;\n          if (Feptr >= mb->end_subject)\n            {\n            SCHECK_PARTIAL();\n            break;\n            }\n#ifdef SUPPORT_UNICODE\n          GETCHARLENTEST(fc, Feptr, len);\n#else\n          fc = *Feptr;\n#endif\n          if (!PRIV(eclass)(fc, Leclass_data, Leclass_data + Leclass_len,\n                            (const uint8_t*)mb->start_code, utf))\n            break;\n          Feptr += len;\n          }\n\n        if (reptype == REPTYPE_POS) continue;    /* No backtracking */\n\n        /* After \\C in UTF mode, Lstart_eptr might be in the middle of a\n        Unicode character. Use <= Lstart_eptr to ensure backtracking doesn't\n        go too far. */\n\n        for(;;)\n          {\n          RMATCH(Fecode, RM103);\n          if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n          if (Feptr-- <= Lstart_eptr) break;  /* Tried at original position */\n#ifdef SUPPORT_UNICODE\n          if (utf) BACKCHAR(Feptr);\n#endif\n          }\n        RRETURN(MATCH_NOMATCH);\n        }\n\n      PCRE2_UNREACHABLE(); /* Control never reaches here */\n      }\n#endif  /* SUPPORT_WIDE_CHARS: end of ECLASS */\n\n#undef Lstart_eptr\n#undef Leclass_data\n#undef Leclass_len\n#undef Lmin\n#undef Lmax\n\n\n    /* ===================================================================== */\n    /* Match various character types when PCRE2_UCP is not set. These opcodes\n    are not generated when PCRE2_UCP is set - instead appropriate property\n    tests are compiled. */\n\n    case OP_NOT_DIGIT:\n    if (Feptr >= mb->end_subject)\n      {\n      SCHECK_PARTIAL();\n      RRETURN(MATCH_NOMATCH);\n      }\n    GETCHARINCTEST(fc, Feptr);\n    if (CHMAX_255(fc) && (mb->ctypes[fc] & ctype_digit) != 0)\n      RRETURN(MATCH_NOMATCH);\n    Fecode++;\n    break;\n\n    case OP_DIGIT:\n    if (Feptr >= mb->end_subject)\n      {\n      SCHECK_PARTIAL();\n      RRETURN(MATCH_NOMATCH);\n      }\n    GETCHARINCTEST(fc, Feptr);\n    if (!CHMAX_255(fc) || (mb->ctypes[fc] & ctype_digit) == 0)\n      RRETURN(MATCH_NOMATCH);\n    Fecode++;\n    break;\n\n    case OP_NOT_WHITESPACE:\n    if (Feptr >= mb->end_subject)\n      {\n      SCHECK_PARTIAL();\n      RRETURN(MATCH_NOMATCH);\n      }\n    GETCHARINCTEST(fc, Feptr);\n    if (CHMAX_255(fc) && (mb->ctypes[fc] & ctype_space) != 0)\n      RRETURN(MATCH_NOMATCH);\n    Fecode++;\n    break;\n\n    case OP_WHITESPACE:\n    if (Feptr >= mb->end_subject)\n      {\n      SCHECK_PARTIAL();\n      RRETURN(MATCH_NOMATCH);\n      }\n    GETCHARINCTEST(fc, Feptr);\n    if (!CHMAX_255(fc) || (mb->ctypes[fc] & ctype_space) == 0)\n      RRETURN(MATCH_NOMATCH);\n    Fecode++;\n    break;\n\n    case OP_NOT_WORDCHAR:\n    if (Feptr >= mb->end_subject)\n      {\n      SCHECK_PARTIAL();\n      RRETURN(MATCH_NOMATCH);\n      }\n    GETCHARINCTEST(fc, Feptr);\n    if (CHMAX_255(fc) && (mb->ctypes[fc] & ctype_word) != 0)\n      RRETURN(MATCH_NOMATCH);\n    Fecode++;\n    break;\n\n    case OP_WORDCHAR:\n    if (Feptr >= mb->end_subject)\n      {\n      SCHECK_PARTIAL();\n      RRETURN(MATCH_NOMATCH);\n      }\n    GETCHARINCTEST(fc, Feptr);\n    if (!CHMAX_255(fc) || (mb->ctypes[fc] & ctype_word) == 0)\n      RRETURN(MATCH_NOMATCH);\n    Fecode++;\n    break;\n\n    case OP_ANYNL:\n    if (Feptr >= mb->end_subject)\n      {\n      SCHECK_PARTIAL();\n      RRETURN(MATCH_NOMATCH);\n      }\n    GETCHARINCTEST(fc, Feptr);\n    switch(fc)\n      {\n      default: RRETURN(MATCH_NOMATCH);\n\n      case CHAR_CR:\n      if (Feptr >= mb->end_subject)\n        {\n        SCHECK_PARTIAL();\n        }\n      else if (UCHAR21TEST(Feptr) == CHAR_LF) Feptr++;\n      break;\n\n      case CHAR_LF:\n      break;\n\n      case CHAR_VT:\n      case CHAR_FF:\n      case CHAR_NEL:\n#ifndef EBCDIC\n      case 0x2028:\n      case 0x2029:\n#endif  /* Not EBCDIC */\n      if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) RRETURN(MATCH_NOMATCH);\n      break;\n      }\n    Fecode++;\n    break;\n\n    case OP_NOT_HSPACE:\n    if (Feptr >= mb->end_subject)\n      {\n      SCHECK_PARTIAL();\n      RRETURN(MATCH_NOMATCH);\n      }\n    GETCHARINCTEST(fc, Feptr);\n    switch(fc)\n      {\n      HSPACE_CASES: RRETURN(MATCH_NOMATCH);  /* Byte and multibyte cases */\n      default: break;\n      }\n    Fecode++;\n    break;\n\n    case OP_HSPACE:\n    if (Feptr >= mb->end_subject)\n      {\n      SCHECK_PARTIAL();\n      RRETURN(MATCH_NOMATCH);\n      }\n    GETCHARINCTEST(fc, Feptr);\n    switch(fc)\n      {\n      HSPACE_CASES: break;  /* Byte and multibyte cases */\n      default: RRETURN(MATCH_NOMATCH);\n      }\n    Fecode++;\n    break;\n\n    case OP_NOT_VSPACE:\n    if (Feptr >= mb->end_subject)\n      {\n      SCHECK_PARTIAL();\n      RRETURN(MATCH_NOMATCH);\n      }\n    GETCHARINCTEST(fc, Feptr);\n    switch(fc)\n      {\n      VSPACE_CASES: RRETURN(MATCH_NOMATCH);\n      default: break;\n      }\n    Fecode++;\n    break;\n\n    case OP_VSPACE:\n    if (Feptr >= mb->end_subject)\n      {\n      SCHECK_PARTIAL();\n      RRETURN(MATCH_NOMATCH);\n      }\n    GETCHARINCTEST(fc, Feptr);\n    switch(fc)\n      {\n      VSPACE_CASES: break;\n      default: RRETURN(MATCH_NOMATCH);\n      }\n    Fecode++;\n    break;\n\n\n#ifdef SUPPORT_UNICODE\n\n    /* ===================================================================== */\n    /* Check the next character by Unicode property. We will get here only\n    if the support is in the binary; otherwise a compile-time error occurs. */\n\n    case OP_PROP:\n    case OP_NOTPROP:\n    if (Feptr >= mb->end_subject)\n      {\n      SCHECK_PARTIAL();\n      RRETURN(MATCH_NOMATCH);\n      }\n    GETCHARINCTEST(fc, Feptr);\n      {\n      const uint32_t *cp;\n      uint32_t chartype;\n      const ucd_record *prop = GET_UCD(fc);\n      BOOL notmatch = Fop == OP_NOTPROP;\n\n      switch(Fecode[1])\n        {\n        case PT_LAMP:\n        chartype = prop->chartype;\n        if ((chartype == ucp_Lu ||\n             chartype == ucp_Ll ||\n             chartype == ucp_Lt) == notmatch)\n          RRETURN(MATCH_NOMATCH);\n        break;\n\n        case PT_GC:\n        if ((Fecode[2] == PRIV(ucp_gentype)[prop->chartype]) == notmatch)\n          RRETURN(MATCH_NOMATCH);\n        break;\n\n        case PT_PC:\n        if ((Fecode[2] == prop->chartype) == notmatch)\n          RRETURN(MATCH_NOMATCH);\n        break;\n\n        case PT_SC:\n        if ((Fecode[2] == prop->script) == notmatch)\n          RRETURN(MATCH_NOMATCH);\n        break;\n\n        case PT_SCX:\n          {\n          BOOL ok = (Fecode[2] == prop->script ||\n                     MAPBIT(PRIV(ucd_script_sets) + UCD_SCRIPTX_PROP(prop), Fecode[2]) != 0);\n          if (ok == notmatch) RRETURN(MATCH_NOMATCH);\n          }\n        break;\n\n        /* These are specials */\n\n        case PT_ALNUM:\n        chartype = prop->chartype;\n        if ((PRIV(ucp_gentype)[chartype] == ucp_L ||\n             PRIV(ucp_gentype)[chartype] == ucp_N) == notmatch)\n          RRETURN(MATCH_NOMATCH);\n        break;\n\n        /* Perl space used to exclude VT, but from Perl 5.18 it is included,\n        which means that Perl space and POSIX space are now identical. PCRE\n        was changed at release 8.34. */\n\n        case PT_SPACE:    /* Perl space */\n        case PT_PXSPACE:  /* POSIX space */\n        switch(fc)\n          {\n          HSPACE_CASES:\n          VSPACE_CASES:\n          if (notmatch) RRETURN(MATCH_NOMATCH);\n          break;\n\n          default:\n          if ((PRIV(ucp_gentype)[prop->chartype] == ucp_Z) == notmatch)\n            RRETURN(MATCH_NOMATCH);\n          break;\n          }\n        break;\n\n        case PT_WORD:\n        chartype = prop->chartype;\n        if ((PRIV(ucp_gentype)[chartype] == ucp_L ||\n             PRIV(ucp_gentype)[chartype] == ucp_N ||\n             chartype == ucp_Mn ||\n             chartype == ucp_Pc) == notmatch)\n          RRETURN(MATCH_NOMATCH);\n        break;\n\n        case PT_CLIST:\n#if PCRE2_CODE_UNIT_WIDTH == 32\n            if (fc > MAX_UTF_CODE_POINT)\n              {\n              if (notmatch) break;;\n              RRETURN(MATCH_NOMATCH);\n              }\n#endif\n        cp = PRIV(ucd_caseless_sets) + Fecode[2];\n        for (;;)\n          {\n          if (fc < *cp)\n            { if (notmatch) break; else { RRETURN(MATCH_NOMATCH); } }\n          if (fc == *cp++)\n            { if (notmatch) { RRETURN(MATCH_NOMATCH); } else break; }\n          }\n        break;\n\n        case PT_UCNC:\n        if ((fc == CHAR_DOLLAR_SIGN || fc == CHAR_COMMERCIAL_AT ||\n             fc == CHAR_GRAVE_ACCENT || (fc >= 0xa0 && fc <= 0xd7ff) ||\n             fc >= 0xe000) == notmatch)\n          RRETURN(MATCH_NOMATCH);\n        break;\n\n        case PT_BIDICL:\n        if ((UCD_BIDICLASS_PROP(prop) == Fecode[2]) == notmatch)\n          RRETURN(MATCH_NOMATCH);\n        break;\n\n        case PT_BOOL:\n          {\n          BOOL ok = MAPBIT(PRIV(ucd_boolprop_sets) +\n            UCD_BPROPS_PROP(prop), Fecode[2]) != 0;\n          if (ok == notmatch) RRETURN(MATCH_NOMATCH);\n          }\n        break;\n\n        /* This should never occur */\n\n        default:\n        PCRE2_DEBUG_UNREACHABLE();\n        return PCRE2_ERROR_INTERNAL;\n        }\n\n      Fecode += 3;\n      }\n    break;\n\n\n    /* ===================================================================== */\n    /* Match an extended Unicode sequence. We will get here only if the support\n    is in the binary; otherwise a compile-time error occurs. */\n\n    case OP_EXTUNI:\n    if (Feptr >= mb->end_subject)\n      {\n      SCHECK_PARTIAL();\n      RRETURN(MATCH_NOMATCH);\n      }\n    else\n      {\n      GETCHARINCTEST(fc, Feptr);\n      Feptr = PRIV(extuni)(fc, Feptr, mb->start_subject, mb->end_subject, utf,\n        NULL);\n      }\n    CHECK_PARTIAL();\n    Fecode++;\n    break;\n\n#endif  /* SUPPORT_UNICODE */\n\n\n    /* ===================================================================== */\n    /* Match a single character type repeatedly. Note that the property type\n    does not need to be in a stack frame as it is not used within an RMATCH()\n    loop. */\n\n#define Lstart_eptr  F->temp_sptr[0]\n#define Lmin         F->temp_32[0]\n#define Lmax         F->temp_32[1]\n#define Lctype       F->temp_32[2]\n#define Lpropvalue   F->temp_32[3]\n\n    case OP_TYPEEXACT:\n    Lmin = Lmax = GET2(Fecode, 1);\n    Fecode += 1 + IMM2_SIZE;\n    goto REPEATTYPE;\n\n    case OP_TYPEUPTO:\n    case OP_TYPEMINUPTO:\n    Lmin = 0;\n    Lmax = GET2(Fecode, 1);\n    reptype = (*Fecode == OP_TYPEMINUPTO)? REPTYPE_MIN : REPTYPE_MAX;\n    Fecode += 1 + IMM2_SIZE;\n    goto REPEATTYPE;\n\n    case OP_TYPEPOSSTAR:\n    reptype = REPTYPE_POS;\n    Lmin = 0;\n    Lmax = UINT32_MAX;\n    Fecode++;\n    goto REPEATTYPE;\n\n    case OP_TYPEPOSPLUS:\n    reptype = REPTYPE_POS;\n    Lmin = 1;\n    Lmax = UINT32_MAX;\n    Fecode++;\n    goto REPEATTYPE;\n\n    case OP_TYPEPOSQUERY:\n    reptype = REPTYPE_POS;\n    Lmin = 0;\n    Lmax = 1;\n    Fecode++;\n    goto REPEATTYPE;\n\n    case OP_TYPEPOSUPTO:\n    reptype = REPTYPE_POS;\n    Lmin = 0;\n    Lmax = GET2(Fecode, 1);\n    Fecode += 1 + IMM2_SIZE;\n    goto REPEATTYPE;\n\n    case OP_TYPESTAR:\n    case OP_TYPEMINSTAR:\n    case OP_TYPEPLUS:\n    case OP_TYPEMINPLUS:\n    case OP_TYPEQUERY:\n    case OP_TYPEMINQUERY:\n    fc = *Fecode++ - OP_TYPESTAR;\n    Lmin = rep_min[fc];\n    Lmax = rep_max[fc];\n    reptype = rep_typ[fc];\n\n    /* Common code for all repeated character type matches. */\n\n    REPEATTYPE:\n    Lctype = *Fecode++;      /* Code for the character type */\n\n#ifdef SUPPORT_UNICODE\n    if (Lctype == OP_PROP || Lctype == OP_NOTPROP)\n      {\n      proptype = *Fecode++;\n      Lpropvalue = *Fecode++;\n      }\n    else proptype = -1;\n#endif\n\n    /* First, ensure the minimum number of matches are present. Use inline\n    code for maximizing the speed, and do the type test once at the start\n    (i.e. keep it out of the loops). As there are no calls to RMATCH in the\n    loops, we can use an ordinary variable for \"notmatch\". The code for UTF\n    mode is separated out for tidiness, except for Unicode property tests. */\n\n    if (Lmin > 0)\n      {\n#ifdef SUPPORT_UNICODE\n      if (proptype >= 0)  /* Property tests in all modes */\n        {\n        BOOL notmatch = Lctype == OP_NOTPROP;\n        switch(proptype)\n          {\n          case PT_LAMP:\n          for (i = 1; i <= Lmin; i++)\n            {\n            int chartype;\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              RRETURN(MATCH_NOMATCH);\n              }\n            GETCHARINCTEST(fc, Feptr);\n            chartype = UCD_CHARTYPE(fc);\n            if ((chartype == ucp_Lu ||\n                 chartype == ucp_Ll ||\n                 chartype == ucp_Lt) == notmatch)\n              RRETURN(MATCH_NOMATCH);\n            }\n          break;\n\n          case PT_GC:\n          for (i = 1; i <= Lmin; i++)\n            {\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              RRETURN(MATCH_NOMATCH);\n              }\n            GETCHARINCTEST(fc, Feptr);\n            if ((UCD_CATEGORY(fc) == Lpropvalue) == notmatch)\n              RRETURN(MATCH_NOMATCH);\n            }\n          break;\n\n          case PT_PC:\n          for (i = 1; i <= Lmin; i++)\n            {\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              RRETURN(MATCH_NOMATCH);\n              }\n            GETCHARINCTEST(fc, Feptr);\n            if ((UCD_CHARTYPE(fc) == Lpropvalue) == notmatch)\n              RRETURN(MATCH_NOMATCH);\n            }\n          break;\n\n          case PT_SC:\n          for (i = 1; i <= Lmin; i++)\n            {\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              RRETURN(MATCH_NOMATCH);\n              }\n            GETCHARINCTEST(fc, Feptr);\n            if ((UCD_SCRIPT(fc) == Lpropvalue) == notmatch)\n              RRETURN(MATCH_NOMATCH);\n            }\n          break;\n\n          case PT_SCX:\n          for (i = 1; i <= Lmin; i++)\n            {\n            BOOL ok;\n            const ucd_record *prop;\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              RRETURN(MATCH_NOMATCH);\n              }\n            GETCHARINCTEST(fc, Feptr);\n            prop = GET_UCD(fc);\n            ok = (prop->script == Lpropvalue ||\n                  MAPBIT(PRIV(ucd_script_sets) + UCD_SCRIPTX_PROP(prop), Lpropvalue) != 0);\n            if (ok == notmatch)\n              RRETURN(MATCH_NOMATCH);\n            }\n          break;\n\n          case PT_ALNUM:\n          for (i = 1; i <= Lmin; i++)\n            {\n            int category;\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              RRETURN(MATCH_NOMATCH);\n              }\n            GETCHARINCTEST(fc, Feptr);\n            category = UCD_CATEGORY(fc);\n            if ((category == ucp_L || category == ucp_N) == notmatch)\n              RRETURN(MATCH_NOMATCH);\n            }\n          break;\n\n          /* Perl space used to exclude VT, but from Perl 5.18 it is included,\n          which means that Perl space and POSIX space are now identical. PCRE\n          was changed at release 8.34. */\n\n          case PT_SPACE:    /* Perl space */\n          case PT_PXSPACE:  /* POSIX space */\n          for (i = 1; i <= Lmin; i++)\n            {\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              RRETURN(MATCH_NOMATCH);\n              }\n            GETCHARINCTEST(fc, Feptr);\n            switch(fc)\n              {\n              HSPACE_CASES:\n              VSPACE_CASES:\n              if (notmatch) RRETURN(MATCH_NOMATCH);\n              break;\n\n              default:\n              if ((UCD_CATEGORY(fc) == ucp_Z) == notmatch)\n                RRETURN(MATCH_NOMATCH);\n              break;\n              }\n            }\n          break;\n\n          case PT_WORD:\n          for (i = 1; i <= Lmin; i++)\n            {\n            int chartype, category;\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              RRETURN(MATCH_NOMATCH);\n              }\n            GETCHARINCTEST(fc, Feptr);\n            chartype = UCD_CHARTYPE(fc);\n            category = PRIV(ucp_gentype)[chartype];\n            if ((category == ucp_L || category == ucp_N ||\n                 chartype == ucp_Mn || chartype == ucp_Pc) == notmatch)\n              RRETURN(MATCH_NOMATCH);\n            }\n          break;\n\n          case PT_CLIST:\n          for (i = 1; i <= Lmin; i++)\n            {\n            const uint32_t *cp;\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              RRETURN(MATCH_NOMATCH);\n              }\n            GETCHARINCTEST(fc, Feptr);\n#if PCRE2_CODE_UNIT_WIDTH == 32\n            if (fc > MAX_UTF_CODE_POINT)\n              {\n              if (notmatch) continue;\n              RRETURN(MATCH_NOMATCH);\n              }\n#endif\n            cp = PRIV(ucd_caseless_sets) + Lpropvalue;\n            for (;;)\n              {\n              if (fc < *cp)\n                {\n                if (notmatch) break;\n                RRETURN(MATCH_NOMATCH);\n                }\n              if (fc == *cp++)\n                {\n                if (notmatch) RRETURN(MATCH_NOMATCH);\n                break;\n                }\n              }\n            }\n          break;\n\n          case PT_UCNC:\n          for (i = 1; i <= Lmin; i++)\n            {\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              RRETURN(MATCH_NOMATCH);\n              }\n            GETCHARINCTEST(fc, Feptr);\n            if ((fc == CHAR_DOLLAR_SIGN || fc == CHAR_COMMERCIAL_AT ||\n                 fc == CHAR_GRAVE_ACCENT || (fc >= 0xa0 && fc <= 0xd7ff) ||\n                 fc >= 0xe000) == notmatch)\n              RRETURN(MATCH_NOMATCH);\n            }\n          break;\n\n          case PT_BIDICL:\n          for (i = 1; i <= Lmin; i++)\n            {\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              RRETURN(MATCH_NOMATCH);\n              }\n            GETCHARINCTEST(fc, Feptr);\n            if ((UCD_BIDICLASS(fc) == Lpropvalue) == notmatch)\n              RRETURN(MATCH_NOMATCH);\n            }\n          break;\n\n          case PT_BOOL:\n          for (i = 1; i <= Lmin; i++)\n            {\n            BOOL ok;\n            const ucd_record *prop;\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              RRETURN(MATCH_NOMATCH);\n              }\n            GETCHARINCTEST(fc, Feptr);\n            prop = GET_UCD(fc);\n            ok = MAPBIT(PRIV(ucd_boolprop_sets) +\n              UCD_BPROPS_PROP(prop), Lpropvalue) != 0;\n            if (ok == notmatch)\n              RRETURN(MATCH_NOMATCH);\n            }\n          break;\n\n          /* This should not occur */\n\n          default:\n          PCRE2_DEBUG_UNREACHABLE();\n          return PCRE2_ERROR_INTERNAL;\n          }\n        }\n\n      /* Match extended Unicode sequences. We will get here only if the\n      support is in the binary; otherwise a compile-time error occurs. */\n\n      else if (Lctype == OP_EXTUNI)\n        {\n        for (i = 1; i <= Lmin; i++)\n          {\n          if (Feptr >= mb->end_subject)\n            {\n            SCHECK_PARTIAL();\n            RRETURN(MATCH_NOMATCH);\n            }\n          else\n            {\n            GETCHARINCTEST(fc, Feptr);\n            Feptr = PRIV(extuni)(fc, Feptr, mb->start_subject,\n              mb->end_subject, utf, NULL);\n            }\n          CHECK_PARTIAL();\n          }\n        }\n      else\n#endif     /* SUPPORT_UNICODE */\n\n/* Handle all other cases in UTF mode */\n\n#ifdef SUPPORT_UNICODE\n      if (utf) switch(Lctype)\n        {\n        case OP_ANY:\n        for (i = 1; i <= Lmin; i++)\n          {\n          if (Feptr >= mb->end_subject)\n            {\n            SCHECK_PARTIAL();\n            RRETURN(MATCH_NOMATCH);\n            }\n          if (IS_NEWLINE(Feptr)) RRETURN(MATCH_NOMATCH);\n          if (mb->partial != 0 &&\n              Feptr + 1 >= mb->end_subject &&\n              NLBLOCK->nltype == NLTYPE_FIXED &&\n              NLBLOCK->nllen == 2 &&\n              UCHAR21(Feptr) == NLBLOCK->nl[0])\n            {\n            mb->hitend = TRUE;\n            if (mb->partial > 1) return PCRE2_ERROR_PARTIAL;\n            }\n          Feptr++;\n          ACROSSCHAR(Feptr < mb->end_subject, Feptr, Feptr++);\n          }\n        break;\n\n        case OP_ALLANY:\n        for (i = 1; i <= Lmin; i++)\n          {\n          if (Feptr >= mb->end_subject)\n            {\n            SCHECK_PARTIAL();\n            RRETURN(MATCH_NOMATCH);\n            }\n          Feptr++;\n          ACROSSCHAR(Feptr < mb->end_subject, Feptr, Feptr++);\n          }\n        break;\n\n        case OP_ANYBYTE:\n        if (Feptr > mb->end_subject - Lmin) RRETURN(MATCH_NOMATCH);\n        Feptr += Lmin;\n        break;\n\n        case OP_ANYNL:\n        for (i = 1; i <= Lmin; i++)\n          {\n          if (Feptr >= mb->end_subject)\n            {\n            SCHECK_PARTIAL();\n            RRETURN(MATCH_NOMATCH);\n            }\n          GETCHARINC(fc, Feptr);\n          switch(fc)\n            {\n            default: RRETURN(MATCH_NOMATCH);\n\n            case CHAR_CR:\n            if (Feptr < mb->end_subject && UCHAR21(Feptr) == CHAR_LF) Feptr++;\n            break;\n\n            case CHAR_LF:\n            break;\n\n            case CHAR_VT:\n            case CHAR_FF:\n            case CHAR_NEL:\n#ifndef EBCDIC\n            case 0x2028:\n            case 0x2029:\n#endif  /* Not EBCDIC */\n            if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) RRETURN(MATCH_NOMATCH);\n            break;\n            }\n          }\n        break;\n\n        case OP_NOT_HSPACE:\n        for (i = 1; i <= Lmin; i++)\n          {\n          if (Feptr >= mb->end_subject)\n            {\n            SCHECK_PARTIAL();\n            RRETURN(MATCH_NOMATCH);\n            }\n          GETCHARINC(fc, Feptr);\n          switch(fc)\n            {\n            HSPACE_CASES: RRETURN(MATCH_NOMATCH);\n            default: break;\n            }\n          }\n        break;\n\n        case OP_HSPACE:\n        for (i = 1; i <= Lmin; i++)\n          {\n          if (Feptr >= mb->end_subject)\n            {\n            SCHECK_PARTIAL();\n            RRETURN(MATCH_NOMATCH);\n            }\n          GETCHARINC(fc, Feptr);\n          switch(fc)\n            {\n            HSPACE_CASES: break;\n            default: RRETURN(MATCH_NOMATCH);\n            }\n          }\n        break;\n\n        case OP_NOT_VSPACE:\n        for (i = 1; i <= Lmin; i++)\n          {\n          if (Feptr >= mb->end_subject)\n            {\n            SCHECK_PARTIAL();\n            RRETURN(MATCH_NOMATCH);\n            }\n          GETCHARINC(fc, Feptr);\n          switch(fc)\n            {\n            VSPACE_CASES: RRETURN(MATCH_NOMATCH);\n            default: break;\n            }\n          }\n        break;\n\n        case OP_VSPACE:\n        for (i = 1; i <= Lmin; i++)\n          {\n          if (Feptr >= mb->end_subject)\n            {\n            SCHECK_PARTIAL();\n            RRETURN(MATCH_NOMATCH);\n            }\n          GETCHARINC(fc, Feptr);\n          switch(fc)\n            {\n            VSPACE_CASES: break;\n            default: RRETURN(MATCH_NOMATCH);\n            }\n          }\n        break;\n\n        case OP_NOT_DIGIT:\n        for (i = 1; i <= Lmin; i++)\n          {\n          if (Feptr >= mb->end_subject)\n            {\n            SCHECK_PARTIAL();\n            RRETURN(MATCH_NOMATCH);\n            }\n          GETCHARINC(fc, Feptr);\n          if (fc < 128 && (mb->ctypes[fc] & ctype_digit) != 0)\n            RRETURN(MATCH_NOMATCH);\n          }\n        break;\n\n        case OP_DIGIT:\n        for (i = 1; i <= Lmin; i++)\n          {\n          uint32_t cc;\n          if (Feptr >= mb->end_subject)\n            {\n            SCHECK_PARTIAL();\n            RRETURN(MATCH_NOMATCH);\n            }\n          cc = UCHAR21(Feptr);\n          if (cc >= 128 || (mb->ctypes[cc] & ctype_digit) == 0)\n            RRETURN(MATCH_NOMATCH);\n          Feptr++;\n          /* No need to skip more code units - we know it has only one. */\n          }\n        break;\n\n        case OP_NOT_WHITESPACE:\n        for (i = 1; i <= Lmin; i++)\n          {\n          uint32_t cc;\n          if (Feptr >= mb->end_subject)\n            {\n            SCHECK_PARTIAL();\n            RRETURN(MATCH_NOMATCH);\n            }\n          cc = UCHAR21(Feptr);\n          if (cc < 128 && (mb->ctypes[cc] & ctype_space) != 0)\n            RRETURN(MATCH_NOMATCH);\n          Feptr++;\n          ACROSSCHAR(Feptr < mb->end_subject, Feptr, Feptr++);\n          }\n        break;\n\n        case OP_WHITESPACE:\n        for (i = 1; i <= Lmin; i++)\n          {\n          uint32_t cc;\n          if (Feptr >= mb->end_subject)\n            {\n            SCHECK_PARTIAL();\n            RRETURN(MATCH_NOMATCH);\n            }\n          cc = UCHAR21(Feptr);\n          if (cc >= 128 || (mb->ctypes[cc] & ctype_space) == 0)\n            RRETURN(MATCH_NOMATCH);\n          Feptr++;\n          /* No need to skip more code units - we know it has only one. */\n          }\n        break;\n\n        case OP_NOT_WORDCHAR:\n        for (i = 1; i <= Lmin; i++)\n          {\n          uint32_t cc;\n          if (Feptr >= mb->end_subject)\n            {\n            SCHECK_PARTIAL();\n            RRETURN(MATCH_NOMATCH);\n            }\n          cc = UCHAR21(Feptr);\n          if (cc < 128 && (mb->ctypes[cc] & ctype_word) != 0)\n            RRETURN(MATCH_NOMATCH);\n          Feptr++;\n          ACROSSCHAR(Feptr < mb->end_subject, Feptr, Feptr++);\n          }\n        break;\n\n        case OP_WORDCHAR:\n        for (i = 1; i <= Lmin; i++)\n          {\n          uint32_t cc;\n          if (Feptr >= mb->end_subject)\n            {\n            SCHECK_PARTIAL();\n            RRETURN(MATCH_NOMATCH);\n            }\n          cc = UCHAR21(Feptr);\n          if (cc >= 128 || (mb->ctypes[cc] & ctype_word) == 0)\n            RRETURN(MATCH_NOMATCH);\n          Feptr++;\n          /* No need to skip more code units - we know it has only one. */\n          }\n        break;\n\n        default:\n        PCRE2_DEBUG_UNREACHABLE();\n        return PCRE2_ERROR_INTERNAL;\n        }  /* End switch(Lctype) */\n\n      else\n#endif     /* SUPPORT_UNICODE */\n\n      /* Code for the non-UTF case for minimum matching of operators other\n      than OP_PROP and OP_NOTPROP. */\n\n      switch(Lctype)\n        {\n        case OP_ANY:\n        for (i = 1; i <= Lmin; i++)\n          {\n          if (Feptr >= mb->end_subject)\n            {\n            SCHECK_PARTIAL();\n            RRETURN(MATCH_NOMATCH);\n            }\n          if (IS_NEWLINE(Feptr)) RRETURN(MATCH_NOMATCH);\n          if (mb->partial != 0 &&\n              Feptr + 1 >= mb->end_subject &&\n              NLBLOCK->nltype == NLTYPE_FIXED &&\n              NLBLOCK->nllen == 2 &&\n              *Feptr == NLBLOCK->nl[0])\n            {\n            mb->hitend = TRUE;\n            if (mb->partial > 1) return PCRE2_ERROR_PARTIAL;\n            }\n          Feptr++;\n          }\n        break;\n\n        case OP_ALLANY:\n        if (Feptr > mb->end_subject - Lmin)\n          {\n          SCHECK_PARTIAL();\n          RRETURN(MATCH_NOMATCH);\n          }\n        Feptr += Lmin;\n        break;\n\n        /* This OP_ANYBYTE case will never be reached because \\C gets turned\n        into OP_ALLANY in non-UTF mode. Cut out the code so that coverage\n        reports don't complain about it's never being used. */\n\n/*        case OP_ANYBYTE:\n*        if (Feptr > mb->end_subject - Lmin)\n*          {\n*          SCHECK_PARTIAL();\n*          RRETURN(MATCH_NOMATCH);\n*          }\n*        Feptr += Lmin;\n*        break;\n*/\n        case OP_ANYNL:\n        for (i = 1; i <= Lmin; i++)\n          {\n          if (Feptr >= mb->end_subject)\n            {\n            SCHECK_PARTIAL();\n            RRETURN(MATCH_NOMATCH);\n            }\n          switch(*Feptr++)\n            {\n            default: RRETURN(MATCH_NOMATCH);\n\n            case CHAR_CR:\n            if (Feptr < mb->end_subject && *Feptr == CHAR_LF) Feptr++;\n            break;\n\n            case CHAR_LF:\n            break;\n\n            case CHAR_VT:\n            case CHAR_FF:\n            case CHAR_NEL:\n#if PCRE2_CODE_UNIT_WIDTH != 8\n            case 0x2028:\n            case 0x2029:\n#endif\n            if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) RRETURN(MATCH_NOMATCH);\n            break;\n            }\n          }\n        break;\n\n        case OP_NOT_HSPACE:\n        for (i = 1; i <= Lmin; i++)\n          {\n          if (Feptr >= mb->end_subject)\n            {\n            SCHECK_PARTIAL();\n            RRETURN(MATCH_NOMATCH);\n            }\n          switch(*Feptr++)\n            {\n            default: break;\n            HSPACE_BYTE_CASES:\n#if PCRE2_CODE_UNIT_WIDTH != 8\n            HSPACE_MULTIBYTE_CASES:\n#endif\n            RRETURN(MATCH_NOMATCH);\n            }\n          }\n        break;\n\n        case OP_HSPACE:\n        for (i = 1; i <= Lmin; i++)\n          {\n          if (Feptr >= mb->end_subject)\n            {\n            SCHECK_PARTIAL();\n            RRETURN(MATCH_NOMATCH);\n            }\n          switch(*Feptr++)\n            {\n            default: RRETURN(MATCH_NOMATCH);\n            HSPACE_BYTE_CASES:\n#if PCRE2_CODE_UNIT_WIDTH != 8\n            HSPACE_MULTIBYTE_CASES:\n#endif\n            break;\n            }\n          }\n        break;\n\n        case OP_NOT_VSPACE:\n        for (i = 1; i <= Lmin; i++)\n          {\n          if (Feptr >= mb->end_subject)\n            {\n            SCHECK_PARTIAL();\n            RRETURN(MATCH_NOMATCH);\n            }\n          switch(*Feptr++)\n            {\n            VSPACE_BYTE_CASES:\n#if PCRE2_CODE_UNIT_WIDTH != 8\n            VSPACE_MULTIBYTE_CASES:\n#endif\n            RRETURN(MATCH_NOMATCH);\n            default: break;\n            }\n          }\n        break;\n\n        case OP_VSPACE:\n        for (i = 1; i <= Lmin; i++)\n          {\n          if (Feptr >= mb->end_subject)\n            {\n            SCHECK_PARTIAL();\n            RRETURN(MATCH_NOMATCH);\n            }\n          switch(*Feptr++)\n            {\n            default: RRETURN(MATCH_NOMATCH);\n            VSPACE_BYTE_CASES:\n#if PCRE2_CODE_UNIT_WIDTH != 8\n            VSPACE_MULTIBYTE_CASES:\n#endif\n            break;\n            }\n          }\n        break;\n\n        case OP_NOT_DIGIT:\n        for (i = 1; i <= Lmin; i++)\n          {\n          if (Feptr >= mb->end_subject)\n            {\n            SCHECK_PARTIAL();\n            RRETURN(MATCH_NOMATCH);\n            }\n          if (MAX_255(*Feptr) && (mb->ctypes[*Feptr] & ctype_digit) != 0)\n            RRETURN(MATCH_NOMATCH);\n          Feptr++;\n          }\n        break;\n\n        case OP_DIGIT:\n        for (i = 1; i <= Lmin; i++)\n          {\n          if (Feptr >= mb->end_subject)\n            {\n            SCHECK_PARTIAL();\n            RRETURN(MATCH_NOMATCH);\n            }\n          if (!MAX_255(*Feptr) || (mb->ctypes[*Feptr] & ctype_digit) == 0)\n            RRETURN(MATCH_NOMATCH);\n          Feptr++;\n          }\n        break;\n\n        case OP_NOT_WHITESPACE:\n        for (i = 1; i <= Lmin; i++)\n          {\n          if (Feptr >= mb->end_subject)\n            {\n            SCHECK_PARTIAL();\n            RRETURN(MATCH_NOMATCH);\n            }\n          if (MAX_255(*Feptr) && (mb->ctypes[*Feptr] & ctype_space) != 0)\n            RRETURN(MATCH_NOMATCH);\n          Feptr++;\n          }\n        break;\n\n        case OP_WHITESPACE:\n        for (i = 1; i <= Lmin; i++)\n          {\n          if (Feptr >= mb->end_subject)\n            {\n            SCHECK_PARTIAL();\n            RRETURN(MATCH_NOMATCH);\n            }\n          if (!MAX_255(*Feptr) || (mb->ctypes[*Feptr] & ctype_space) == 0)\n            RRETURN(MATCH_NOMATCH);\n          Feptr++;\n          }\n        break;\n\n        case OP_NOT_WORDCHAR:\n        for (i = 1; i <= Lmin; i++)\n          {\n          if (Feptr >= mb->end_subject)\n            {\n            SCHECK_PARTIAL();\n            RRETURN(MATCH_NOMATCH);\n            }\n          if (MAX_255(*Feptr) && (mb->ctypes[*Feptr] & ctype_word) != 0)\n            RRETURN(MATCH_NOMATCH);\n          Feptr++;\n          }\n        break;\n\n        case OP_WORDCHAR:\n        for (i = 1; i <= Lmin; i++)\n          {\n          if (Feptr >= mb->end_subject)\n            {\n            SCHECK_PARTIAL();\n            RRETURN(MATCH_NOMATCH);\n            }\n          if (!MAX_255(*Feptr) || (mb->ctypes[*Feptr] & ctype_word) == 0)\n            RRETURN(MATCH_NOMATCH);\n          Feptr++;\n          }\n        break;\n\n        default:\n        PCRE2_DEBUG_UNREACHABLE();\n        return PCRE2_ERROR_INTERNAL;\n        }\n      }\n\n    /* If Lmin = Lmax we are done. Continue with the main loop. */\n\n    if (Lmin == Lmax) continue;\n\n    /* If minimizing, we have to test the rest of the pattern before each\n    subsequent match. This means we cannot use a local \"notmatch\" variable as\n    in the other cases. As all 4 temporary 32-bit values in the frame are\n    already in use, just test the type each time. */\n\n    if (reptype == REPTYPE_MIN)\n      {\n#ifdef SUPPORT_UNICODE\n      if (proptype >= 0)\n        {\n        switch(proptype)\n          {\n          case PT_LAMP:\n          for (;;)\n            {\n            int chartype;\n            RMATCH(Fecode, RM208);\n            if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n            if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              RRETURN(MATCH_NOMATCH);\n              }\n            GETCHARINCTEST(fc, Feptr);\n            chartype = UCD_CHARTYPE(fc);\n            if ((chartype == ucp_Lu ||\n                 chartype == ucp_Ll ||\n                 chartype == ucp_Lt) == (Lctype == OP_NOTPROP))\n              RRETURN(MATCH_NOMATCH);\n            }\n          PCRE2_UNREACHABLE(); /* Control never reaches here */\n\n          case PT_GC:\n          for (;;)\n            {\n            RMATCH(Fecode, RM209);\n            if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n            if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              RRETURN(MATCH_NOMATCH);\n              }\n            GETCHARINCTEST(fc, Feptr);\n            if ((UCD_CATEGORY(fc) == Lpropvalue) == (Lctype == OP_NOTPROP))\n              RRETURN(MATCH_NOMATCH);\n            }\n          PCRE2_UNREACHABLE(); /* Control never reaches here */\n\n          case PT_PC:\n          for (;;)\n            {\n            RMATCH(Fecode, RM210);\n            if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n            if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              RRETURN(MATCH_NOMATCH);\n              }\n            GETCHARINCTEST(fc, Feptr);\n            if ((UCD_CHARTYPE(fc) == Lpropvalue) == (Lctype == OP_NOTPROP))\n              RRETURN(MATCH_NOMATCH);\n            }\n          PCRE2_UNREACHABLE(); /* Control never reaches here */\n\n          case PT_SC:\n          for (;;)\n            {\n            RMATCH(Fecode, RM211);\n            if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n            if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              RRETURN(MATCH_NOMATCH);\n              }\n            GETCHARINCTEST(fc, Feptr);\n            if ((UCD_SCRIPT(fc) == Lpropvalue) == (Lctype == OP_NOTPROP))\n              RRETURN(MATCH_NOMATCH);\n            }\n          PCRE2_UNREACHABLE(); /* Control never reaches here */\n\n          case PT_SCX:\n          for (;;)\n            {\n            BOOL ok;\n            const ucd_record *prop;\n            RMATCH(Fecode, RM224);\n            if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n            if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              RRETURN(MATCH_NOMATCH);\n              }\n            GETCHARINCTEST(fc, Feptr);\n            prop = GET_UCD(fc);\n            ok = (prop->script == Lpropvalue\n                  || MAPBIT(PRIV(ucd_script_sets) + UCD_SCRIPTX_PROP(prop), Lpropvalue) != 0);\n            if (ok == (Lctype == OP_NOTPROP))\n              RRETURN(MATCH_NOMATCH);\n            }\n          PCRE2_UNREACHABLE(); /* Control never reaches here */\n\n          case PT_ALNUM:\n          for (;;)\n            {\n            int category;\n            RMATCH(Fecode, RM212);\n            if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n            if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              RRETURN(MATCH_NOMATCH);\n              }\n            GETCHARINCTEST(fc, Feptr);\n            category = UCD_CATEGORY(fc);\n            if ((category == ucp_L || category == ucp_N) == (Lctype == OP_NOTPROP))\n              RRETURN(MATCH_NOMATCH);\n            }\n          PCRE2_UNREACHABLE(); /* Control never reaches here */\n\n          /* Perl space used to exclude VT, but from Perl 5.18 it is included,\n          which means that Perl space and POSIX space are now identical. PCRE\n          was changed at release 8.34. */\n\n          case PT_SPACE:    /* Perl space */\n          case PT_PXSPACE:  /* POSIX space */\n          for (;;)\n            {\n            RMATCH(Fecode, RM213);\n            if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n            if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              RRETURN(MATCH_NOMATCH);\n              }\n            GETCHARINCTEST(fc, Feptr);\n            switch(fc)\n              {\n              HSPACE_CASES:\n              VSPACE_CASES:\n              if (Lctype == OP_NOTPROP) RRETURN(MATCH_NOMATCH);\n              break;\n\n              default:\n              if ((UCD_CATEGORY(fc) == ucp_Z) == (Lctype == OP_NOTPROP))\n                RRETURN(MATCH_NOMATCH);\n              break;\n              }\n            }\n          PCRE2_UNREACHABLE(); /* Control never reaches here */\n\n          case PT_WORD:\n          for (;;)\n            {\n            int chartype, category;\n            RMATCH(Fecode, RM214);\n            if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n            if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              RRETURN(MATCH_NOMATCH);\n              }\n            GETCHARINCTEST(fc, Feptr);\n            chartype = UCD_CHARTYPE(fc);\n            category = PRIV(ucp_gentype)[chartype];\n            if ((category == ucp_L ||\n                 category == ucp_N ||\n                 chartype == ucp_Mn ||\n                 chartype == ucp_Pc) == (Lctype == OP_NOTPROP))\n              RRETURN(MATCH_NOMATCH);\n            }\n          PCRE2_UNREACHABLE(); /* Control never reaches here */\n\n          case PT_CLIST:\n          for (;;)\n            {\n            const uint32_t *cp;\n            RMATCH(Fecode, RM215);\n            if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n            if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              RRETURN(MATCH_NOMATCH);\n              }\n            GETCHARINCTEST(fc, Feptr);\n#if PCRE2_CODE_UNIT_WIDTH == 32\n            if (fc > MAX_UTF_CODE_POINT)\n              {\n              if (Lctype == OP_NOTPROP) continue;\n              RRETURN(MATCH_NOMATCH);\n              }\n#endif\n            cp = PRIV(ucd_caseless_sets) + Lpropvalue;\n            for (;;)\n              {\n              if (fc < *cp)\n                {\n                if (Lctype == OP_NOTPROP) break;\n                RRETURN(MATCH_NOMATCH);\n                }\n              if (fc == *cp++)\n                {\n                if (Lctype == OP_NOTPROP) RRETURN(MATCH_NOMATCH);\n                break;\n                }\n              }\n            }\n          PCRE2_UNREACHABLE(); /* Control never reaches here */\n\n          case PT_UCNC:\n          for (;;)\n            {\n            RMATCH(Fecode, RM216);\n            if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n            if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              RRETURN(MATCH_NOMATCH);\n              }\n            GETCHARINCTEST(fc, Feptr);\n            if ((fc == CHAR_DOLLAR_SIGN || fc == CHAR_COMMERCIAL_AT ||\n                 fc == CHAR_GRAVE_ACCENT || (fc >= 0xa0 && fc <= 0xd7ff) ||\n                 fc >= 0xe000) == (Lctype == OP_NOTPROP))\n              RRETURN(MATCH_NOMATCH);\n            }\n          PCRE2_UNREACHABLE(); /* Control never reaches here */\n\n          case PT_BIDICL:\n          for (;;)\n            {\n            RMATCH(Fecode, RM223);\n            if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n            if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              RRETURN(MATCH_NOMATCH);\n              }\n            GETCHARINCTEST(fc, Feptr);\n            if ((UCD_BIDICLASS(fc) == Lpropvalue) == (Lctype == OP_NOTPROP))\n              RRETURN(MATCH_NOMATCH);\n            }\n          PCRE2_UNREACHABLE(); /* Control never reaches here */\n\n          case PT_BOOL:\n          for (;;)\n            {\n            BOOL ok;\n            const ucd_record *prop;\n            RMATCH(Fecode, RM222);\n            if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n            if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              RRETURN(MATCH_NOMATCH);\n              }\n            GETCHARINCTEST(fc, Feptr);\n            prop = GET_UCD(fc);\n            ok = MAPBIT(PRIV(ucd_boolprop_sets) +\n              UCD_BPROPS_PROP(prop), Lpropvalue) != 0;\n            if (ok == (Lctype == OP_NOTPROP))\n              RRETURN(MATCH_NOMATCH);\n            }\n          PCRE2_UNREACHABLE(); /* Control never reaches here */\n\n          /* This should never occur */\n          default:\n          PCRE2_DEBUG_UNREACHABLE();\n          return PCRE2_ERROR_INTERNAL;\n          }\n        }\n\n      /* Match extended Unicode sequences. We will get here only if the\n      support is in the binary; otherwise a compile-time error occurs. */\n\n      else if (Lctype == OP_EXTUNI)\n        {\n        for (;;)\n          {\n          RMATCH(Fecode, RM217);\n          if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n          if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);\n          if (Feptr >= mb->end_subject)\n            {\n            SCHECK_PARTIAL();\n            RRETURN(MATCH_NOMATCH);\n            }\n          else\n            {\n            GETCHARINCTEST(fc, Feptr);\n            Feptr = PRIV(extuni)(fc, Feptr, mb->start_subject, mb->end_subject,\n              utf, NULL);\n            }\n          CHECK_PARTIAL();\n          }\n        }\n      else\n#endif     /* SUPPORT_UNICODE */\n\n      /* UTF mode for non-property testing character types. */\n\n#ifdef SUPPORT_UNICODE\n      if (utf)\n        {\n        for (;;)\n          {\n          RMATCH(Fecode, RM218);\n          if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n          if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);\n          if (Feptr >= mb->end_subject)\n            {\n            SCHECK_PARTIAL();\n            RRETURN(MATCH_NOMATCH);\n            }\n          if (Lctype == OP_ANY && IS_NEWLINE(Feptr)) RRETURN(MATCH_NOMATCH);\n          GETCHARINC(fc, Feptr);\n          switch(Lctype)\n            {\n            case OP_ANY:               /* This is the non-NL case */\n            if (mb->partial != 0 &&    /* Take care with CRLF partial */\n                Feptr >= mb->end_subject &&\n                NLBLOCK->nltype == NLTYPE_FIXED &&\n                NLBLOCK->nllen == 2 &&\n                fc == NLBLOCK->nl[0])\n              {\n              mb->hitend = TRUE;\n              if (mb->partial > 1) return PCRE2_ERROR_PARTIAL;\n              }\n            break;\n\n            case OP_ALLANY:\n            case OP_ANYBYTE:\n            break;\n\n            case OP_ANYNL:\n            switch(fc)\n              {\n              default: RRETURN(MATCH_NOMATCH);\n\n              case CHAR_CR:\n              if (Feptr < mb->end_subject && UCHAR21(Feptr) == CHAR_LF) Feptr++;\n              break;\n\n              case CHAR_LF:\n              break;\n\n              case CHAR_VT:\n              case CHAR_FF:\n              case CHAR_NEL:\n#ifndef EBCDIC\n              case 0x2028:\n              case 0x2029:\n#endif  /* Not EBCDIC */\n              if (mb->bsr_convention == PCRE2_BSR_ANYCRLF)\n                RRETURN(MATCH_NOMATCH);\n              break;\n              }\n            break;\n\n            case OP_NOT_HSPACE:\n            switch(fc)\n              {\n              HSPACE_CASES: RRETURN(MATCH_NOMATCH);\n              default: break;\n              }\n            break;\n\n            case OP_HSPACE:\n            switch(fc)\n              {\n              HSPACE_CASES: break;\n              default: RRETURN(MATCH_NOMATCH);\n              }\n            break;\n\n            case OP_NOT_VSPACE:\n            switch(fc)\n              {\n              VSPACE_CASES: RRETURN(MATCH_NOMATCH);\n              default: break;\n              }\n            break;\n\n            case OP_VSPACE:\n            switch(fc)\n              {\n              VSPACE_CASES: break;\n              default: RRETURN(MATCH_NOMATCH);\n              }\n            break;\n\n            case OP_NOT_DIGIT:\n            if (fc < 256 && (mb->ctypes[fc] & ctype_digit) != 0)\n              RRETURN(MATCH_NOMATCH);\n            break;\n\n            case OP_DIGIT:\n            if (fc >= 256 || (mb->ctypes[fc] & ctype_digit) == 0)\n              RRETURN(MATCH_NOMATCH);\n            break;\n\n            case OP_NOT_WHITESPACE:\n            if (fc < 256 && (mb->ctypes[fc] & ctype_space) != 0)\n              RRETURN(MATCH_NOMATCH);\n            break;\n\n            case OP_WHITESPACE:\n            if (fc >= 256 || (mb->ctypes[fc] & ctype_space) == 0)\n              RRETURN(MATCH_NOMATCH);\n            break;\n\n            case OP_NOT_WORDCHAR:\n            if (fc < 256 && (mb->ctypes[fc] & ctype_word) != 0)\n              RRETURN(MATCH_NOMATCH);\n            break;\n\n            case OP_WORDCHAR:\n            if (fc >= 256 || (mb->ctypes[fc] & ctype_word) == 0)\n              RRETURN(MATCH_NOMATCH);\n            break;\n\n            default:\n            PCRE2_DEBUG_UNREACHABLE();\n            return PCRE2_ERROR_INTERNAL;\n            }\n          }\n        }\n      else\n#endif  /* SUPPORT_UNICODE */\n\n      /* Not UTF mode */\n        {\n        for (;;)\n          {\n          RMATCH(Fecode, RM33);\n          if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n          if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);\n          if (Feptr >= mb->end_subject)\n            {\n            SCHECK_PARTIAL();\n            RRETURN(MATCH_NOMATCH);\n            }\n          if (Lctype == OP_ANY && IS_NEWLINE(Feptr))\n            RRETURN(MATCH_NOMATCH);\n          fc = *Feptr++;\n          switch(Lctype)\n            {\n            case OP_ANY:               /* This is the non-NL case */\n            if (mb->partial != 0 &&    /* Take care with CRLF partial */\n                Feptr >= mb->end_subject &&\n                NLBLOCK->nltype == NLTYPE_FIXED &&\n                NLBLOCK->nllen == 2 &&\n                fc == NLBLOCK->nl[0])\n              {\n              mb->hitend = TRUE;\n              if (mb->partial > 1) return PCRE2_ERROR_PARTIAL;\n              }\n            break;\n\n            case OP_ALLANY:\n            case OP_ANYBYTE:\n            break;\n\n            case OP_ANYNL:\n            switch(fc)\n              {\n              default: RRETURN(MATCH_NOMATCH);\n\n              case CHAR_CR:\n              if (Feptr < mb->end_subject && *Feptr == CHAR_LF) Feptr++;\n              break;\n\n              case CHAR_LF:\n              break;\n\n              case CHAR_VT:\n              case CHAR_FF:\n              case CHAR_NEL:\n#if PCRE2_CODE_UNIT_WIDTH != 8\n              case 0x2028:\n              case 0x2029:\n#endif\n              if (mb->bsr_convention == PCRE2_BSR_ANYCRLF)\n                RRETURN(MATCH_NOMATCH);\n              break;\n              }\n            break;\n\n            case OP_NOT_HSPACE:\n            switch(fc)\n              {\n              default: break;\n              HSPACE_BYTE_CASES:\n#if PCRE2_CODE_UNIT_WIDTH != 8\n              HSPACE_MULTIBYTE_CASES:\n#endif\n              RRETURN(MATCH_NOMATCH);\n              }\n            break;\n\n            case OP_HSPACE:\n            switch(fc)\n              {\n              default: RRETURN(MATCH_NOMATCH);\n              HSPACE_BYTE_CASES:\n#if PCRE2_CODE_UNIT_WIDTH != 8\n              HSPACE_MULTIBYTE_CASES:\n#endif\n              break;\n              }\n            break;\n\n            case OP_NOT_VSPACE:\n            switch(fc)\n              {\n              default: break;\n              VSPACE_BYTE_CASES:\n#if PCRE2_CODE_UNIT_WIDTH != 8\n              VSPACE_MULTIBYTE_CASES:\n#endif\n              RRETURN(MATCH_NOMATCH);\n              }\n            break;\n\n            case OP_VSPACE:\n            switch(fc)\n              {\n              default: RRETURN(MATCH_NOMATCH);\n              VSPACE_BYTE_CASES:\n#if PCRE2_CODE_UNIT_WIDTH != 8\n              VSPACE_MULTIBYTE_CASES:\n#endif\n              break;\n              }\n            break;\n\n            case OP_NOT_DIGIT:\n            if (MAX_255(fc) && (mb->ctypes[fc] & ctype_digit) != 0)\n              RRETURN(MATCH_NOMATCH);\n            break;\n\n            case OP_DIGIT:\n            if (!MAX_255(fc) || (mb->ctypes[fc] & ctype_digit) == 0)\n              RRETURN(MATCH_NOMATCH);\n            break;\n\n            case OP_NOT_WHITESPACE:\n            if (MAX_255(fc) && (mb->ctypes[fc] & ctype_space) != 0)\n              RRETURN(MATCH_NOMATCH);\n            break;\n\n            case OP_WHITESPACE:\n            if (!MAX_255(fc) || (mb->ctypes[fc] & ctype_space) == 0)\n              RRETURN(MATCH_NOMATCH);\n            break;\n\n            case OP_NOT_WORDCHAR:\n            if (MAX_255(fc) && (mb->ctypes[fc] & ctype_word) != 0)\n              RRETURN(MATCH_NOMATCH);\n            break;\n\n            case OP_WORDCHAR:\n            if (!MAX_255(fc) || (mb->ctypes[fc] & ctype_word) == 0)\n              RRETURN(MATCH_NOMATCH);\n            break;\n\n            default:\n            PCRE2_DEBUG_UNREACHABLE();\n            return PCRE2_ERROR_INTERNAL;\n            }\n          }\n        }\n\n      PCRE2_DEBUG_UNREACHABLE(); /* Control should never reach here */\n      }\n\n    /* If maximizing, it is worth using inline code for speed, doing the type\n    test once at the start (i.e. keep it out of the loops). Once again,\n    \"notmatch\" can be an ordinary local variable because the loops do not call\n    RMATCH. */\n\n    else\n      {\n      Lstart_eptr = Feptr;  /* Remember where we started */\n\n#ifdef SUPPORT_UNICODE\n      if (proptype >= 0)\n        {\n        BOOL notmatch = Lctype == OP_NOTPROP;\n        switch(proptype)\n          {\n          case PT_LAMP:\n          for (i = Lmin; i < Lmax; i++)\n            {\n            int chartype;\n            int len = 1;\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              break;\n              }\n            GETCHARLENTEST(fc, Feptr, len);\n            chartype = UCD_CHARTYPE(fc);\n            if ((chartype == ucp_Lu ||\n                 chartype == ucp_Ll ||\n                 chartype == ucp_Lt) == notmatch)\n              break;\n            Feptr+= len;\n            }\n          break;\n\n          case PT_GC:\n          for (i = Lmin; i < Lmax; i++)\n            {\n            int len = 1;\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              break;\n              }\n            GETCHARLENTEST(fc, Feptr, len);\n            if ((UCD_CATEGORY(fc) == Lpropvalue) == notmatch) break;\n            Feptr+= len;\n            }\n          break;\n\n          case PT_PC:\n          for (i = Lmin; i < Lmax; i++)\n            {\n            int len = 1;\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              break;\n              }\n            GETCHARLENTEST(fc, Feptr, len);\n            if ((UCD_CHARTYPE(fc) == Lpropvalue) == notmatch) break;\n            Feptr+= len;\n            }\n          break;\n\n          case PT_SC:\n          for (i = Lmin; i < Lmax; i++)\n            {\n            int len = 1;\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              break;\n              }\n            GETCHARLENTEST(fc, Feptr, len);\n            if ((UCD_SCRIPT(fc) == Lpropvalue) == notmatch) break;\n            Feptr+= len;\n            }\n          break;\n\n          case PT_SCX:\n          for (i = Lmin; i < Lmax; i++)\n            {\n            BOOL ok;\n            const ucd_record *prop;\n            int len = 1;\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              break;\n              }\n            GETCHARLENTEST(fc, Feptr, len);\n            prop = GET_UCD(fc);\n            ok = (prop->script == Lpropvalue ||\n                  MAPBIT(PRIV(ucd_script_sets) + UCD_SCRIPTX_PROP(prop), Lpropvalue) != 0);\n            if (ok == notmatch) break;\n            Feptr+= len;\n            }\n          break;\n\n          case PT_ALNUM:\n          for (i = Lmin; i < Lmax; i++)\n            {\n            int category;\n            int len = 1;\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              break;\n              }\n            GETCHARLENTEST(fc, Feptr, len);\n            category = UCD_CATEGORY(fc);\n            if ((category == ucp_L || category == ucp_N) == notmatch)\n              break;\n            Feptr+= len;\n            }\n          break;\n\n          /* Perl space used to exclude VT, but from Perl 5.18 it is included,\n          which means that Perl space and POSIX space are now identical. PCRE\n          was changed at release 8.34. */\n\n          case PT_SPACE:    /* Perl space */\n          case PT_PXSPACE:  /* POSIX space */\n          for (i = Lmin; i < Lmax; i++)\n            {\n            int len = 1;\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              break;\n              }\n            GETCHARLENTEST(fc, Feptr, len);\n            switch(fc)\n              {\n              HSPACE_CASES:\n              VSPACE_CASES:\n              if (notmatch) goto ENDLOOP99;  /* Break the loop */\n              break;\n\n              default:\n              if ((UCD_CATEGORY(fc) == ucp_Z) == notmatch)\n                goto ENDLOOP99;   /* Break the loop */\n              break;\n              }\n            Feptr+= len;\n            }\n          ENDLOOP99:\n          break;\n\n          case PT_WORD:\n          for (i = Lmin; i < Lmax; i++)\n            {\n            int chartype, category;\n            int len = 1;\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              break;\n              }\n            GETCHARLENTEST(fc, Feptr, len);\n            chartype = UCD_CHARTYPE(fc);\n            category = PRIV(ucp_gentype)[chartype];\n            if ((category == ucp_L ||\n                 category == ucp_N ||\n                 chartype == ucp_Mn ||\n                 chartype == ucp_Pc) == notmatch)\n              break;\n            Feptr+= len;\n            }\n          break;\n\n          case PT_CLIST:\n          for (i = Lmin; i < Lmax; i++)\n            {\n            const uint32_t *cp;\n            int len = 1;\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              break;\n              }\n            GETCHARLENTEST(fc, Feptr, len);\n#if PCRE2_CODE_UNIT_WIDTH == 32\n            if (fc > MAX_UTF_CODE_POINT)\n              {\n              if (!notmatch) goto GOT_MAX;\n              }\n            else\n#endif\n              {\n              cp = PRIV(ucd_caseless_sets) + Lpropvalue;\n              for (;;)\n                {\n                if (fc < *cp)\n                  { if (notmatch) break; else goto GOT_MAX; }\n                if (fc == *cp++)\n                  { if (notmatch) goto GOT_MAX; else break; }\n                }\n              }\n\n            Feptr += len;\n            }\n          GOT_MAX:\n          break;\n\n          case PT_UCNC:\n          for (i = Lmin; i < Lmax; i++)\n            {\n            int len = 1;\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              break;\n              }\n            GETCHARLENTEST(fc, Feptr, len);\n            if ((fc == CHAR_DOLLAR_SIGN || fc == CHAR_COMMERCIAL_AT ||\n                 fc == CHAR_GRAVE_ACCENT || (fc >= 0xa0 && fc <= 0xd7ff) ||\n                 fc >= 0xe000) == notmatch)\n              break;\n            Feptr += len;\n            }\n          break;\n\n          case PT_BIDICL:\n          for (i = Lmin; i < Lmax; i++)\n            {\n            int len = 1;\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              break;\n              }\n            GETCHARLENTEST(fc, Feptr, len);\n            if ((UCD_BIDICLASS(fc) == Lpropvalue) == notmatch) break;\n            Feptr+= len;\n            }\n          break;\n\n          case PT_BOOL:\n          for (i = Lmin; i < Lmax; i++)\n            {\n            BOOL ok;\n            const ucd_record *prop;\n            int len = 1;\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              break;\n              }\n            GETCHARLENTEST(fc, Feptr, len);\n            prop = GET_UCD(fc);\n            ok = MAPBIT(PRIV(ucd_boolprop_sets) +\n              UCD_BPROPS_PROP(prop), Lpropvalue) != 0;\n            if (ok == notmatch) break;\n            Feptr+= len;\n            }\n          break;\n\n          default:\n          PCRE2_DEBUG_UNREACHABLE();\n          return PCRE2_ERROR_INTERNAL;\n          }\n\n        /* Feptr is now past the end of the maximum run */\n\n        if (reptype == REPTYPE_POS) continue;    /* No backtracking */\n\n        /* After \\C in UTF mode, Lstart_eptr might be in the middle of a\n        Unicode character. Use <= Lstart_eptr to ensure backtracking doesn't\n        go too far. */\n\n        for(;;)\n          {\n          if (Feptr <= Lstart_eptr) break;\n          RMATCH(Fecode, RM221);\n          if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n          Feptr--;\n          if (utf) BACKCHAR(Feptr);\n          }\n        }\n\n      /* Match extended Unicode grapheme clusters. We will get here only if the\n      support is in the binary; otherwise a compile-time error occurs. */\n\n      else if (Lctype == OP_EXTUNI)\n        {\n        for (i = Lmin; i < Lmax; i++)\n          {\n          if (Feptr >= mb->end_subject)\n            {\n            SCHECK_PARTIAL();\n            break;\n            }\n          else\n            {\n            GETCHARINCTEST(fc, Feptr);\n            Feptr = PRIV(extuni)(fc, Feptr, mb->start_subject, mb->end_subject,\n              utf, NULL);\n            }\n          CHECK_PARTIAL();\n          }\n\n        /* Feptr is now past the end of the maximum run */\n\n        if (reptype == REPTYPE_POS) continue;    /* No backtracking */\n\n        /* We use <= Lstart_eptr rather than == Lstart_eptr to detect the start\n        of the run while backtracking because the use of \\C in UTF mode can\n        cause BACKCHAR to move back past Lstart_eptr. This is just palliative;\n        the use of \\C in UTF mode is fraught with danger. */\n\n        for(;;)\n          {\n          int lgb, rgb;\n          PCRE2_SPTR fptr;\n\n          if (Feptr <= Lstart_eptr) break;   /* At start of char run */\n          RMATCH(Fecode, RM219);\n          if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n\n          /* Backtracking over an extended grapheme cluster involves inspecting\n          the previous two characters (if present) to see if a break is\n          permitted between them. */\n\n          Feptr--;\n          if (!utf) fc = *Feptr; else\n            {\n            BACKCHAR(Feptr);\n            GETCHAR(fc, Feptr);\n            }\n          rgb = UCD_GRAPHBREAK(fc);\n\n          for (;;)\n            {\n            if (Feptr <= Lstart_eptr) break;   /* At start of char run */\n            fptr = Feptr - 1;\n            if (!utf) fc = *fptr; else\n              {\n              BACKCHAR(fptr);\n              GETCHAR(fc, fptr);\n              }\n            lgb = UCD_GRAPHBREAK(fc);\n            if ((PRIV(ucp_gbtable)[lgb] & (1u << rgb)) == 0) break;\n            Feptr = fptr;\n            rgb = lgb;\n            }\n          }\n        }\n\n      else\n#endif   /* SUPPORT_UNICODE */\n\n#ifdef SUPPORT_UNICODE\n      if (utf)\n        {\n        switch(Lctype)\n          {\n          case OP_ANY:\n          for (i = Lmin; i < Lmax; i++)\n            {\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              break;\n              }\n            if (IS_NEWLINE(Feptr)) break;\n            if (mb->partial != 0 &&    /* Take care with CRLF partial */\n                Feptr + 1 >= mb->end_subject &&\n                NLBLOCK->nltype == NLTYPE_FIXED &&\n                NLBLOCK->nllen == 2 &&\n                UCHAR21(Feptr) == NLBLOCK->nl[0])\n              {\n              mb->hitend = TRUE;\n              if (mb->partial > 1) return PCRE2_ERROR_PARTIAL;\n              }\n            Feptr++;\n            ACROSSCHAR(Feptr < mb->end_subject, Feptr, Feptr++);\n            }\n          break;\n\n          case OP_ALLANY:\n          if (Lmax < UINT32_MAX)\n            {\n            for (i = Lmin; i < Lmax; i++)\n              {\n              if (Feptr >= mb->end_subject)\n                {\n                SCHECK_PARTIAL();\n                break;\n                }\n              Feptr++;\n              ACROSSCHAR(Feptr < mb->end_subject, Feptr, Feptr++);\n              }\n            }\n          else\n            {\n            Feptr = mb->end_subject;   /* Unlimited UTF-8 repeat */\n            SCHECK_PARTIAL();\n            }\n          break;\n\n          /* The \"byte\" (i.e. \"code unit\") case is the same as non-UTF */\n\n          case OP_ANYBYTE:\n          fc = Lmax - Lmin;\n          if (fc > (uint32_t)(mb->end_subject - Feptr))\n            {\n            Feptr = mb->end_subject;\n            SCHECK_PARTIAL();\n            }\n          else Feptr += fc;\n          break;\n\n          case OP_ANYNL:\n          for (i = Lmin; i < Lmax; i++)\n            {\n            int len = 1;\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              break;\n              }\n            GETCHARLEN(fc, Feptr, len);\n            if (fc == CHAR_CR)\n              {\n              if (++Feptr >= mb->end_subject) break;\n              if (UCHAR21(Feptr) == CHAR_LF) Feptr++;\n              }\n            else\n              {\n              if (fc != CHAR_LF &&\n                  (mb->bsr_convention == PCRE2_BSR_ANYCRLF ||\n                   (fc != CHAR_VT && fc != CHAR_FF && fc != CHAR_NEL\n#ifndef EBCDIC\n                    && fc != 0x2028 && fc != 0x2029\n#endif  /* Not EBCDIC */\n                    )))\n                break;\n              Feptr += len;\n              }\n            }\n          break;\n\n          case OP_NOT_HSPACE:\n          case OP_HSPACE:\n          for (i = Lmin; i < Lmax; i++)\n            {\n            BOOL gotspace;\n            int len = 1;\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              break;\n              }\n            GETCHARLEN(fc, Feptr, len);\n            switch(fc)\n              {\n              HSPACE_CASES: gotspace = TRUE; break;\n              default: gotspace = FALSE; break;\n              }\n            if (gotspace == (Lctype == OP_NOT_HSPACE)) break;\n            Feptr += len;\n            }\n          break;\n\n          case OP_NOT_VSPACE:\n          case OP_VSPACE:\n          for (i = Lmin; i < Lmax; i++)\n            {\n            BOOL gotspace;\n            int len = 1;\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              break;\n              }\n            GETCHARLEN(fc, Feptr, len);\n            switch(fc)\n              {\n              VSPACE_CASES: gotspace = TRUE; break;\n              default: gotspace = FALSE; break;\n              }\n            if (gotspace == (Lctype == OP_NOT_VSPACE)) break;\n            Feptr += len;\n            }\n          break;\n\n          case OP_NOT_DIGIT:\n          for (i = Lmin; i < Lmax; i++)\n            {\n            int len = 1;\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              break;\n              }\n            GETCHARLEN(fc, Feptr, len);\n            if (fc < 256 && (mb->ctypes[fc] & ctype_digit) != 0) break;\n            Feptr+= len;\n            }\n          break;\n\n          case OP_DIGIT:\n          for (i = Lmin; i < Lmax; i++)\n            {\n            int len = 1;\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              break;\n              }\n            GETCHARLEN(fc, Feptr, len);\n            if (fc >= 256 ||(mb->ctypes[fc] & ctype_digit) == 0) break;\n            Feptr+= len;\n            }\n          break;\n\n          case OP_NOT_WHITESPACE:\n          for (i = Lmin; i < Lmax; i++)\n            {\n            int len = 1;\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              break;\n              }\n            GETCHARLEN(fc, Feptr, len);\n            if (fc < 256 && (mb->ctypes[fc] & ctype_space) != 0) break;\n            Feptr+= len;\n            }\n          break;\n\n          case OP_WHITESPACE:\n          for (i = Lmin; i < Lmax; i++)\n            {\n            int len = 1;\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              break;\n              }\n            GETCHARLEN(fc, Feptr, len);\n            if (fc >= 256 ||(mb->ctypes[fc] & ctype_space) == 0) break;\n            Feptr+= len;\n            }\n          break;\n\n          case OP_NOT_WORDCHAR:\n          for (i = Lmin; i < Lmax; i++)\n            {\n            int len = 1;\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              break;\n              }\n            GETCHARLEN(fc, Feptr, len);\n            if (fc < 256 && (mb->ctypes[fc] & ctype_word) != 0) break;\n            Feptr+= len;\n            }\n          break;\n\n          case OP_WORDCHAR:\n          for (i = Lmin; i < Lmax; i++)\n            {\n            int len = 1;\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              break;\n              }\n            GETCHARLEN(fc, Feptr, len);\n            if (fc >= 256 || (mb->ctypes[fc] & ctype_word) == 0) break;\n            Feptr+= len;\n            }\n          break;\n\n          default:\n          PCRE2_DEBUG_UNREACHABLE();\n          return PCRE2_ERROR_INTERNAL;\n          }\n\n        if (reptype == REPTYPE_POS) continue;    /* No backtracking */\n\n        /* After \\C in UTF mode, Lstart_eptr might be in the middle of a\n        Unicode character. Use <= Lstart_eptr to ensure backtracking doesn't go\n        too far. */\n\n        for(;;)\n          {\n          if (Feptr <= Lstart_eptr) break;\n          RMATCH(Fecode, RM220);\n          if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n          Feptr--;\n          BACKCHAR(Feptr);\n          if (Lctype == OP_ANYNL && Feptr > Lstart_eptr &&\n              UCHAR21(Feptr) == CHAR_NL && UCHAR21(Feptr - 1) == CHAR_CR)\n            Feptr--;\n          }\n        }\n      else\n#endif  /* SUPPORT_UNICODE */\n\n      /* Not UTF mode */\n        {\n        switch(Lctype)\n          {\n          case OP_ANY:\n          for (i = Lmin; i < Lmax; i++)\n            {\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              break;\n              }\n            if (IS_NEWLINE(Feptr)) break;\n            if (mb->partial != 0 &&    /* Take care with CRLF partial */\n                Feptr + 1 >= mb->end_subject &&\n                NLBLOCK->nltype == NLTYPE_FIXED &&\n                NLBLOCK->nllen == 2 &&\n                *Feptr == NLBLOCK->nl[0])\n              {\n              mb->hitend = TRUE;\n              if (mb->partial > 1) return PCRE2_ERROR_PARTIAL;\n              }\n            Feptr++;\n            }\n          break;\n\n          case OP_ALLANY:\n          case OP_ANYBYTE:\n          fc = Lmax - Lmin;\n          if (fc > (uint32_t)(mb->end_subject - Feptr))\n            {\n            Feptr = mb->end_subject;\n            SCHECK_PARTIAL();\n            }\n          else Feptr += fc;\n          break;\n\n          case OP_ANYNL:\n          for (i = Lmin; i < Lmax; i++)\n            {\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              break;\n              }\n            fc = *Feptr;\n            if (fc == CHAR_CR)\n              {\n              if (++Feptr >= mb->end_subject) break;\n              if (*Feptr == CHAR_LF) Feptr++;\n              }\n            else\n              {\n              if (fc != CHAR_LF && (mb->bsr_convention == PCRE2_BSR_ANYCRLF ||\n                 (fc != CHAR_VT && fc != CHAR_FF && fc != CHAR_NEL\n#if PCRE2_CODE_UNIT_WIDTH != 8\n                 && fc != 0x2028 && fc != 0x2029\n#endif\n                 ))) break;\n              Feptr++;\n              }\n            }\n          break;\n\n          case OP_NOT_HSPACE:\n          for (i = Lmin; i < Lmax; i++)\n            {\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              break;\n              }\n            switch(*Feptr)\n              {\n              default: Feptr++; break;\n              HSPACE_BYTE_CASES:\n#if PCRE2_CODE_UNIT_WIDTH != 8\n              HSPACE_MULTIBYTE_CASES:\n#endif\n              goto ENDLOOP00;\n              }\n            }\n          ENDLOOP00:\n          break;\n\n          case OP_HSPACE:\n          for (i = Lmin; i < Lmax; i++)\n            {\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              break;\n              }\n            switch(*Feptr)\n              {\n              default: goto ENDLOOP01;\n              HSPACE_BYTE_CASES:\n#if PCRE2_CODE_UNIT_WIDTH != 8\n              HSPACE_MULTIBYTE_CASES:\n#endif\n              Feptr++; break;\n              }\n            }\n          ENDLOOP01:\n          break;\n\n          case OP_NOT_VSPACE:\n          for (i = Lmin; i < Lmax; i++)\n            {\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              break;\n              }\n            switch(*Feptr)\n              {\n              default: Feptr++; break;\n              VSPACE_BYTE_CASES:\n#if PCRE2_CODE_UNIT_WIDTH != 8\n              VSPACE_MULTIBYTE_CASES:\n#endif\n              goto ENDLOOP02;\n              }\n            }\n          ENDLOOP02:\n          break;\n\n          case OP_VSPACE:\n          for (i = Lmin; i < Lmax; i++)\n            {\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              break;\n              }\n            switch(*Feptr)\n              {\n              default: goto ENDLOOP03;\n              VSPACE_BYTE_CASES:\n#if PCRE2_CODE_UNIT_WIDTH != 8\n              VSPACE_MULTIBYTE_CASES:\n#endif\n              Feptr++; break;\n              }\n            }\n          ENDLOOP03:\n          break;\n\n          case OP_NOT_DIGIT:\n          for (i = Lmin; i < Lmax; i++)\n            {\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              break;\n              }\n            if (MAX_255(*Feptr) && (mb->ctypes[*Feptr] & ctype_digit) != 0)\n              break;\n            Feptr++;\n            }\n          break;\n\n          case OP_DIGIT:\n          for (i = Lmin; i < Lmax; i++)\n            {\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              break;\n              }\n            if (!MAX_255(*Feptr) || (mb->ctypes[*Feptr] & ctype_digit) == 0)\n              break;\n            Feptr++;\n            }\n          break;\n\n          case OP_NOT_WHITESPACE:\n          for (i = Lmin; i < Lmax; i++)\n            {\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              break;\n              }\n            if (MAX_255(*Feptr) && (mb->ctypes[*Feptr] & ctype_space) != 0)\n              break;\n            Feptr++;\n            }\n          break;\n\n          case OP_WHITESPACE:\n          for (i = Lmin; i < Lmax; i++)\n            {\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              break;\n              }\n            if (!MAX_255(*Feptr) || (mb->ctypes[*Feptr] & ctype_space) == 0)\n              break;\n            Feptr++;\n            }\n          break;\n\n          case OP_NOT_WORDCHAR:\n          for (i = Lmin; i < Lmax; i++)\n            {\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              break;\n              }\n            if (MAX_255(*Feptr) && (mb->ctypes[*Feptr] & ctype_word) != 0)\n              break;\n            Feptr++;\n            }\n          break;\n\n          case OP_WORDCHAR:\n          for (i = Lmin; i < Lmax; i++)\n            {\n            if (Feptr >= mb->end_subject)\n              {\n              SCHECK_PARTIAL();\n              break;\n              }\n            if (!MAX_255(*Feptr) || (mb->ctypes[*Feptr] & ctype_word) == 0)\n              break;\n            Feptr++;\n            }\n          break;\n\n          default:\n          PCRE2_DEBUG_UNREACHABLE();\n          return PCRE2_ERROR_INTERNAL;\n          }\n\n        if (reptype == REPTYPE_POS) continue;    /* No backtracking */\n\n        for (;;)\n          {\n          if (Feptr == Lstart_eptr) break;\n          RMATCH(Fecode, RM34);\n          if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n          Feptr--;\n          if (Lctype == OP_ANYNL && Feptr > Lstart_eptr && *Feptr == CHAR_LF &&\n              Feptr[-1] == CHAR_CR) Feptr--;\n          }\n        }\n      }\n    break;  /* End of repeat character type processing */\n\n#undef Lstart_eptr\n#undef Lmin\n#undef Lmax\n#undef Lctype\n#undef Lpropvalue\n\n\n    /* ===================================================================== */\n    /* Match a back reference, possibly repeatedly. Look past the end of the\n    item to see if there is repeat information following. The OP_REF and\n    OP_REFI opcodes are used for a reference to a numbered group or to a\n    non-duplicated named group. For a duplicated named group, OP_DNREF and\n    OP_DNREFI are used. In this case we must scan the list of groups to which\n    the name refers, and use the first one that is set. */\n\n#define Lmin      F->temp_32[0]\n#define Lmax      F->temp_32[1]\n#define Lcaseless F->temp_32[2]\n#define Lcaseopts F->temp_32[3]\n#define Lstart    F->temp_sptr[0]\n#define Loffset   F->temp_size\n\n    case OP_DNREF:\n    case OP_DNREFI:\n    Lcaseless = (Fop == OP_DNREFI);\n    Lcaseopts = (Fop == OP_DNREFI)? Fecode[1 + 2*IMM2_SIZE] : 0;\n      {\n      int count = GET2(Fecode, 1+IMM2_SIZE);\n      PCRE2_SPTR slot = mb->name_table + GET2(Fecode, 1) * mb->name_entry_size;\n      Fecode += 1 + 2*IMM2_SIZE + (Fop == OP_DNREFI? 1 : 0);\n\n      while (count-- > 0)\n        {\n        Loffset = (GET2(slot, 0) << 1) - 2;\n        if (Loffset < Foffset_top && Fovector[Loffset] != PCRE2_UNSET) break;\n        slot += mb->name_entry_size;\n        }\n      }\n    goto REF_REPEAT;\n\n    case OP_REF:\n    case OP_REFI:\n    Lcaseless = (Fop == OP_REFI);\n    Lcaseopts = (Fop == OP_REFI)? Fecode[1 + IMM2_SIZE] : 0;\n    Loffset = (GET2(Fecode, 1) << 1) - 2;\n    Fecode += 1 + IMM2_SIZE + (Fop == OP_REFI? 1 : 0);\n\n    /* Set up for repetition, or handle the non-repeated case. The maximum and\n    minimum must be in the heap frame, but as they are short-term values, we\n    use temporary fields. */\n\n    REF_REPEAT:\n    switch (*Fecode)\n      {\n      case OP_CRSTAR:\n      case OP_CRMINSTAR:\n      case OP_CRPLUS:\n      case OP_CRMINPLUS:\n      case OP_CRQUERY:\n      case OP_CRMINQUERY:\n      fc = *Fecode++ - OP_CRSTAR;\n      Lmin = rep_min[fc];\n      Lmax = rep_max[fc];\n      reptype = rep_typ[fc];\n      break;\n\n      case OP_CRRANGE:\n      case OP_CRMINRANGE:\n      Lmin = GET2(Fecode, 1);\n      Lmax = GET2(Fecode, 1 + IMM2_SIZE);\n      reptype = rep_typ[*Fecode - OP_CRSTAR];\n      if (Lmax == 0) Lmax = UINT32_MAX;  /* Max 0 => infinity */\n      Fecode += 1 + 2 * IMM2_SIZE;\n      break;\n\n      default:                  /* No repeat follows */\n        {\n        rrc = match_ref(Loffset, Lcaseless, Lcaseopts, F, mb, &length);\n        if (rrc != 0)\n          {\n          if (rrc > 0) Feptr = mb->end_subject;   /* Partial match */\n          CHECK_PARTIAL();\n          RRETURN(MATCH_NOMATCH);\n          }\n        }\n      Feptr += length;\n      continue;              /* With the main loop */\n      }\n\n    /* Handle repeated back references. If a set group has length zero, just\n    continue with the main loop, because it matches however many times. For an\n    unset reference, if the minimum is zero, we can also just continue. We can\n    also continue if PCRE2_MATCH_UNSET_BACKREF is set, because this makes unset\n    group behave as a zero-length group. For any other unset cases, carrying\n    on will result in NOMATCH. */\n\n    if (Loffset < Foffset_top && Fovector[Loffset] != PCRE2_UNSET)\n      {\n      if (Fovector[Loffset] == Fovector[Loffset + 1]) continue;\n      }\n    else  /* Group is not set */\n      {\n      if (Lmin == 0 || (mb->poptions & PCRE2_MATCH_UNSET_BACKREF) != 0)\n        continue;\n      }\n\n    /* First, ensure the minimum number of matches are present. */\n\n    for (i = 1; i <= Lmin; i++)\n      {\n      PCRE2_SIZE slength;\n      rrc = match_ref(Loffset, Lcaseless, Lcaseopts, F, mb, &slength);\n      if (rrc != 0)\n        {\n        if (rrc > 0) Feptr = mb->end_subject;   /* Partial match */\n        CHECK_PARTIAL();\n        RRETURN(MATCH_NOMATCH);\n        }\n      Feptr += slength;\n      }\n\n    /* If min = max, we are done. They are not both allowed to be zero. */\n\n    if (Lmin == Lmax) continue;\n\n    /* If minimizing, keep trying and advancing the pointer. */\n\n    if (reptype == REPTYPE_MIN)\n      {\n      for (;;)\n        {\n        PCRE2_SIZE slength;\n        RMATCH(Fecode, RM20);\n        if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n        if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);\n        rrc = match_ref(Loffset, Lcaseless, Lcaseopts, F, mb, &slength);\n        if (rrc != 0)\n          {\n          if (rrc > 0) Feptr = mb->end_subject;   /* Partial match */\n          CHECK_PARTIAL();\n          RRETURN(MATCH_NOMATCH);\n          }\n        Feptr += slength;\n        }\n\n      PCRE2_UNREACHABLE(); /* Control never reaches here */\n      }\n\n    /* If maximizing, find the longest string and work backwards, as long as\n    the matched lengths for each iteration are the same. */\n\n    else\n      {\n      BOOL samelengths = TRUE;\n      Lstart = Feptr;     /* Starting position */\n      Flength = Fovector[Loffset+1] - Fovector[Loffset];\n\n      for (i = Lmin; i < Lmax; i++)\n        {\n        PCRE2_SIZE slength;\n        rrc = match_ref(Loffset, Lcaseless, Lcaseopts, F, mb, &slength);\n        if (rrc != 0)\n          {\n          /* Can't use CHECK_PARTIAL because we don't want to update Feptr in\n          the soft partial matching case. */\n\n          if (rrc > 0 && mb->partial != 0 &&\n              mb->end_subject > mb->start_used_ptr)\n            {\n            mb->hitend = TRUE;\n            if (mb->partial > 1) return PCRE2_ERROR_PARTIAL;\n            }\n          break;\n          }\n\n        if (slength != Flength) samelengths = FALSE;\n        Feptr += slength;\n        }\n\n      /* If the length matched for each repetition is the same as the length of\n      the captured group, we can easily work backwards. This is the normal\n      case. However, in caseless UTF-8 mode there are pairs of case-equivalent\n      characters whose lengths (in terms of code units) differ. However, this\n      is very rare, so we handle it by re-matching fewer and fewer times. */\n\n      if (samelengths)\n        {\n        while (Feptr >= Lstart)\n          {\n          RMATCH(Fecode, RM21);\n          if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n          Feptr -= Flength;\n          }\n        }\n\n      /* The rare case of non-matching lengths. Re-scan the repetition for each\n      iteration. We know that match_ref() will succeed every time. */\n\n      else\n        {\n        Lmax = i;\n        for (;;)\n          {\n          RMATCH(Fecode, RM22);\n          if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n          if (Feptr == Lstart) break; /* Failed after minimal repetition */\n          Feptr = Lstart;\n          Lmax--;\n          for (i = Lmin; i < Lmax; i++)\n            {\n            PCRE2_SIZE slength;\n            (void)match_ref(Loffset, Lcaseless, Lcaseopts, F, mb, &slength);\n            Feptr += slength;\n            }\n          }\n        }\n\n      RRETURN(MATCH_NOMATCH);\n      }\n\n    PCRE2_DEBUG_UNREACHABLE(); /* Control should never reach here */\n\n#undef Lcaseless\n#undef Lmin\n#undef Lmax\n#undef Lstart\n#undef Loffset\n\n\n\n/* ========================================================================= */\n/*           Opcodes for the start of various parenthesized items            */\n/* ========================================================================= */\n\n    /* In all cases, if the result of RMATCH() is MATCH_THEN, check whether the\n    (*THEN) is within the current branch by comparing the address of OP_THEN\n    that is passed back with the end of the branch. If (*THEN) is within the\n    current branch, and the branch is one of two or more alternatives (it\n    either starts or ends with OP_ALT), we have reached the limit of THEN's\n    action, so convert the return code to NOMATCH, which will cause normal\n    backtracking to happen from now on. Otherwise, THEN is passed back to an\n    outer alternative. This implements Perl's treatment of parenthesized\n    groups, where a group not containing | does not affect the current\n    alternative, that is, (X) is NOT the same as (X|(*F)). */\n\n\n    /* ===================================================================== */\n    /* BRAZERO, BRAMINZERO and SKIPZERO occur just before a non-possessive\n    bracket group, indicating that it may occur zero times. It may repeat\n    infinitely, or not at all - i.e. it could be ()* or ()? or even (){0} in\n    the pattern. Brackets with fixed upper repeat limits are compiled as a\n    number of copies, with the optional ones preceded by BRAZERO or BRAMINZERO.\n    Possessive groups with possible zero repeats are preceded by BRAPOSZERO. */\n\n#define Lnext_ecode F->temp_sptr[0]\n\n    case OP_BRAZERO:\n    Lnext_ecode = Fecode + 1;\n    RMATCH(Lnext_ecode, RM9);\n    if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n    do Lnext_ecode += GET(Lnext_ecode, 1); while (*Lnext_ecode == OP_ALT);\n    Fecode = Lnext_ecode + 1 + LINK_SIZE;\n    break;\n\n    case OP_BRAMINZERO:\n    Lnext_ecode = Fecode + 1;\n    do Lnext_ecode += GET(Lnext_ecode, 1); while (*Lnext_ecode == OP_ALT);\n    RMATCH(Lnext_ecode + 1 + LINK_SIZE, RM10);\n    if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n    Fecode++;\n    break;\n\n#undef Lnext_ecode\n\n    case OP_SKIPZERO:\n    Fecode++;\n    do Fecode += GET(Fecode,1); while (*Fecode == OP_ALT);\n    Fecode += 1 + LINK_SIZE;\n    break;\n\n\n    /* ===================================================================== */\n    /* Handle possessive brackets with an unlimited repeat. The end of these\n    brackets will always be OP_KETRPOS, which returns MATCH_KETRPOS without\n    going further in the pattern. */\n\n#define Lframe_type    F->temp_32[0]\n#define Lmatched_once  F->temp_32[1]\n#define Lzero_allowed  F->temp_32[2]\n#define Lstart_eptr    F->temp_sptr[0]\n#define Lstart_group   F->temp_sptr[1]\n\n    case OP_BRAPOSZERO:\n    Lzero_allowed = TRUE;                /* Zero repeat is allowed */\n    Fecode += 1;\n    if (*Fecode == OP_CBRAPOS || *Fecode == OP_SCBRAPOS)\n      goto POSSESSIVE_CAPTURE;\n    goto POSSESSIVE_NON_CAPTURE;\n\n    case OP_BRAPOS:\n    case OP_SBRAPOS:\n    Lzero_allowed = FALSE;               /* Zero repeat not allowed */\n\n    POSSESSIVE_NON_CAPTURE:\n    Lframe_type = GF_NOCAPTURE;          /* Remembered frame type */\n    goto POSSESSIVE_GROUP;\n\n    case OP_CBRAPOS:\n    case OP_SCBRAPOS:\n    Lzero_allowed = FALSE;               /* Zero repeat not allowed */\n\n    POSSESSIVE_CAPTURE:\n    number = GET2(Fecode, 1+LINK_SIZE);\n    Lframe_type = GF_CAPTURE | number;   /* Remembered frame type */\n\n    POSSESSIVE_GROUP:\n    Lmatched_once = FALSE;               /* Never matched */\n    Lstart_group = Fecode;               /* Start of this group */\n\n    for (;;)\n      {\n      Lstart_eptr = Feptr;               /* Position at group start */\n      group_frame_type = Lframe_type;\n      RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM8);\n      if (rrc == MATCH_KETRPOS)\n        {\n        Lmatched_once = TRUE;            /* Matched at least once */\n        if (Feptr == Lstart_eptr)        /* Empty match; skip to end */\n          {\n          do Fecode += GET(Fecode, 1); while (*Fecode == OP_ALT);\n          break;\n          }\n\n        Fecode = Lstart_group;\n        continue;\n        }\n\n      /* See comment above about handling THEN. */\n\n      if (rrc == MATCH_THEN)\n        {\n        PCRE2_SPTR next_ecode = Fecode + GET(Fecode,1);\n        if (mb->verb_ecode_ptr < next_ecode &&\n            (*Fecode == OP_ALT || *next_ecode == OP_ALT))\n          rrc = MATCH_NOMATCH;\n        }\n\n      if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n      Fecode += GET(Fecode, 1);\n      if (*Fecode != OP_ALT) break;\n      }\n\n    /* Success if matched something or zero repeat allowed */\n\n    if (Lmatched_once || Lzero_allowed)\n      {\n      Fecode += 1 + LINK_SIZE;\n      break;\n      }\n\n    RRETURN(MATCH_NOMATCH);\n\n#undef Lmatched_once\n#undef Lzero_allowed\n#undef Lframe_type\n#undef Lstart_eptr\n#undef Lstart_group\n\n\n    /* ===================================================================== */\n    /* Handle non-capturing brackets that cannot match an empty string. When we\n    get to the final alternative within the brackets, as long as there are no\n    THEN's in the pattern, we can optimize by not recording a new backtracking\n    point. (Ideally we should test for a THEN within this group, but we don't\n    have that information.) Don't do this if we are at the very top level,\n    however, because that would make handling assertions and once-only brackets\n    messier when there is nothing to go back to. */\n\n#define Lframe_type F->temp_32[0]     /* Set for all that use GROUPLOOP */\n#define Lnext_branch F->temp_sptr[0]  /* Used only in OP_BRA handling */\n\n    case OP_BRA:\n    if (mb->hasthen || Frdepth == 0)\n      {\n      Lframe_type = 0;\n      goto GROUPLOOP;\n      }\n\n    for (;;)\n      {\n      Lnext_branch = Fecode + GET(Fecode, 1);\n      if (*Lnext_branch != OP_ALT) break;\n\n      /* This is never the final branch. We do not need to test for MATCH_THEN\n      here because this code is not used when there is a THEN in the pattern. */\n\n      RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM1);\n      if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n      Fecode = Lnext_branch;\n      }\n\n    /* Hit the start of the final branch. Continue at this level. */\n\n    Fecode += PRIV(OP_lengths)[*Fecode];\n    break;\n\n#undef Lnext_branch\n\n\n    /* ===================================================================== */\n    /* Handle a capturing bracket, other than those that are possessive with an\n    unlimited repeat. */\n\n    case OP_CBRA:\n    case OP_SCBRA:\n    Lframe_type = GF_CAPTURE | GET2(Fecode, 1+LINK_SIZE);\n    goto GROUPLOOP;\n\n\n    /* ===================================================================== */\n    /* Atomic groups and non-capturing brackets that can match an empty string\n    must record a backtracking point and also set up a chained frame. */\n\n    case OP_ONCE:\n    case OP_SCRIPT_RUN:\n    case OP_SBRA:\n    Lframe_type = GF_NOCAPTURE | Fop;\n\n    GROUPLOOP:\n    for (;;)\n      {\n      group_frame_type = Lframe_type;\n      RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM2);\n      if (rrc == MATCH_THEN)\n        {\n        PCRE2_SPTR next_ecode = Fecode + GET(Fecode,1);\n        if (mb->verb_ecode_ptr < next_ecode &&\n            (*Fecode == OP_ALT || *next_ecode == OP_ALT))\n          rrc = MATCH_NOMATCH;\n        }\n      if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n      Fecode += GET(Fecode, 1);\n      if (*Fecode != OP_ALT) RRETURN(MATCH_NOMATCH);\n      }\n    PCRE2_UNREACHABLE(); /* Control never reaches here */\n\n#undef Lframe_type\n\n\n    /* ===================================================================== */\n    /* Pattern recursion either matches the current regex, or some\n    subexpression. The offset data is the offset to the starting bracket from\n    the start of the whole pattern. This is so that it works from duplicated\n    subpatterns. For a whole-pattern recursion, we have to infer the number\n    zero. */\n\n#define Lframe_type F->temp_32[0]\n#define Lstart_branch F->temp_sptr[0]\n\n    case OP_RECURSE:\n    bracode = mb->start_code + GET(Fecode, 1);\n    number = (bracode == mb->start_code)? 0 : GET2(bracode, 1 + LINK_SIZE);\n\n    /* If we are already in a pattern recursion, check for repeating the same\n    one without changing the subject pointer or the last referenced character\n    in the subject. This should catch convoluted mutual recursions; some\n    simple cases are caught at compile time. However, there are rare cases when\n    this check needs to be turned off. In this case, actual recursion loops\n    will be caught by the match or heap limits. */\n\n    if (Fcurrent_recurse != RECURSE_UNSET)\n      {\n      offset = Flast_group_offset;\n      while (offset != PCRE2_UNSET)\n        {\n        N = (heapframe *)((char *)match_data->heapframes + offset);\n        P = (heapframe *)((char *)N - frame_size);\n        if (N->group_frame_type == (GF_RECURSE | number))\n          {\n          if (Feptr == P->eptr && mb->last_used_ptr == P->recurse_last_used &&\n               (mb->moptions & PCRE2_DISABLE_RECURSELOOP_CHECK) == 0)\n            return PCRE2_ERROR_RECURSELOOP;\n          break;\n          }\n        offset = P->last_group_offset;\n        }\n      }\n\n    /* Remember the current last referenced character and then run the\n    recursion branch by branch. */\n\n    F->recurse_last_used = mb->last_used_ptr;\n    Lstart_branch = bracode;\n    Lframe_type = GF_RECURSE | number;\n\n    for (;;)\n      {\n      PCRE2_SPTR next_ecode;\n\n      group_frame_type = Lframe_type;\n      RMATCH(Lstart_branch + PRIV(OP_lengths)[*Lstart_branch], RM11);\n      next_ecode = Lstart_branch + GET(Lstart_branch,1);\n\n      /* Handle backtracking verbs, which are defined in a range that can\n      easily be tested for. PCRE does not allow THEN, SKIP, PRUNE or COMMIT to\n      escape beyond a recursion; they cause a NOMATCH for the entire recursion.\n\n      When one of these verbs triggers, the current recursion group number is\n      recorded. If it matches the recursion we are processing, the verb\n      happened within the recursion and we must deal with it. Otherwise it must\n      have happened after the recursion completed, and so has to be passed\n      back. See comment above about handling THEN. */\n\n      if (rrc >= MATCH_BACKTRACK_MIN && rrc <= MATCH_BACKTRACK_MAX &&\n          mb->verb_current_recurse == (Lframe_type ^ GF_RECURSE))\n        {\n        if (rrc == MATCH_THEN && mb->verb_ecode_ptr < next_ecode &&\n            (*Lstart_branch == OP_ALT || *next_ecode == OP_ALT))\n          rrc = MATCH_NOMATCH;\n        else RRETURN(MATCH_NOMATCH);\n        }\n\n      /* Note that carrying on after (*ACCEPT) in a recursion is handled in the\n      OP_ACCEPT code. Nothing needs to be done here. */\n\n      if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n      Lstart_branch = next_ecode;\n      if (*Lstart_branch != OP_ALT) RRETURN(MATCH_NOMATCH);\n      }\n    PCRE2_UNREACHABLE(); /* Control never reaches here */\n\n#undef Lframe_type\n#undef Lstart_branch\n\n\n    /* ===================================================================== */\n    /* Positive assertions are like other groups except that PCRE doesn't allow\n    the effect of (*THEN) to escape beyond an assertion; it is therefore\n    treated as NOMATCH. (*ACCEPT) is treated as successful assertion, with its\n    captures and mark retained. Any other return is an error. */\n\n#define Lframe_type  F->temp_32[0]\n\n    case OP_ASSERT:\n    case OP_ASSERTBACK:\n    case OP_ASSERT_NA:\n    case OP_ASSERTBACK_NA:\n    Lframe_type = GF_NOCAPTURE | Fop;\n    for (;;)\n      {\n      group_frame_type = Lframe_type;\n      RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM3);\n      if (rrc == MATCH_ACCEPT)\n        {\n        memcpy(Fovector,\n              (char *)assert_accept_frame + offsetof(heapframe, ovector),\n              assert_accept_frame->offset_top * sizeof(PCRE2_SIZE));\n        Foffset_top = assert_accept_frame->offset_top;\n        Fmark = assert_accept_frame->mark;\n        break;\n        }\n      if (rrc != MATCH_NOMATCH && rrc != MATCH_THEN) RRETURN(rrc);\n      Fecode += GET(Fecode, 1);\n      if (*Fecode != OP_ALT) RRETURN(MATCH_NOMATCH);\n      }\n\n    do Fecode += GET(Fecode, 1); while (*Fecode == OP_ALT);\n    Fecode += 1 + LINK_SIZE;\n    break;\n\n#undef Lframe_type\n\n\n    /* ===================================================================== */\n    /* Handle negative assertions. Loop for each non-matching branch as for\n    positive assertions. */\n\n#define Lframe_type  F->temp_32[0]\n\n    case OP_ASSERT_NOT:\n    case OP_ASSERTBACK_NOT:\n    Lframe_type  = GF_NOCAPTURE | Fop;\n\n    for (;;)\n      {\n      group_frame_type = Lframe_type;\n      RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM4);\n      switch(rrc)\n        {\n        case MATCH_ACCEPT:   /* Assertion matched, therefore it fails. */\n        case MATCH_MATCH:\n        RRETURN (MATCH_NOMATCH);\n\n        case MATCH_NOMATCH:  /* Branch failed, try next if present. */\n        case MATCH_THEN:\n        Fecode += GET(Fecode, 1);\n        if (*Fecode != OP_ALT) goto ASSERT_NOT_FAILED;\n        break;\n\n        case MATCH_COMMIT:   /* Assertion forced to fail, therefore continue. */\n        case MATCH_SKIP:\n        case MATCH_PRUNE:\n        do Fecode += GET(Fecode, 1); while (*Fecode == OP_ALT);\n        goto ASSERT_NOT_FAILED;\n\n        default:             /* Pass back any other return */\n        RRETURN(rrc);\n        }\n      }\n\n    /* None of the branches have matched or there was a backtrack to (*COMMIT),\n    (*SKIP), (*PRUNE), or (*THEN) in the last branch. This is success for a\n    negative assertion, so carry on. */\n\n    ASSERT_NOT_FAILED:\n    Fecode += 1 + LINK_SIZE;\n    break;\n\n#undef Lframe_type\n\n    /* ===================================================================== */\n    /* Handle scan substring operation. */\n\n#define Lframe_type          F->temp_32[0]\n#define Lextra_size          F->temp_32[1]\n#define Lsaved_moptions      F->temp_32[2]\n#define Lsaved_end_subject   F->temp_sptr[0]\n#define Lsaved_eptr          F->temp_sptr[1]\n#define Ltrue_end_extra      F->temp_size\n\n    case OP_ASSERT_SCS:\n      {\n      PCRE2_SPTR ecode = Fecode + 1 + LINK_SIZE;\n      uint32_t extra_size = 0;\n      int count;\n      PCRE2_SPTR slot;\n\n      /* Disable compiler warning. */\n      offset = 0;\n      (void)offset;\n\n      for (;;)\n        {\n        if (*ecode == OP_CREF)\n          {\n          extra_size += 1+IMM2_SIZE;\n          offset = (GET2(ecode, 1) << 1) - 2;\n          ecode += 1+IMM2_SIZE;\n          if (offset < Foffset_top && Fovector[offset] != PCRE2_UNSET)\n            goto SCS_OFFSET_FOUND;\n          continue;\n          }\n\n        if (*ecode != OP_DNCREF) RRETURN(MATCH_NOMATCH);\n\n        count = GET2(ecode, 1 + IMM2_SIZE);\n        slot = mb->name_table + GET2(ecode, 1) * mb->name_entry_size;\n        extra_size += 1+2*IMM2_SIZE;\n        ecode += 1+2*IMM2_SIZE;\n\n        while (count > 0)\n          {\n          offset = (GET2(slot, 0) << 1) - 2;\n          if (offset < Foffset_top && Fovector[offset] != PCRE2_UNSET)\n            goto SCS_OFFSET_FOUND;\n          slot += mb->name_entry_size;\n          count--;\n          }\n        }\n\n      SCS_OFFSET_FOUND:\n\n      /* Skip remaining options. */\n      for (;;)\n        {\n        if (*ecode == OP_CREF)\n          {\n          extra_size += 1+IMM2_SIZE;\n          ecode += 1+IMM2_SIZE;\n          }\n        else if (*ecode == OP_DNCREF)\n          {\n          extra_size += 1+2*IMM2_SIZE;\n          ecode += 1+2*IMM2_SIZE;\n          }\n        else break;\n        }\n\n      Lextra_size = extra_size;\n      }\n\n    Lsaved_end_subject = mb->end_subject;\n    Ltrue_end_extra = mb->true_end_subject - mb->end_subject;\n    Lsaved_eptr = Feptr;\n    Lsaved_moptions = mb->moptions;\n\n    Feptr = mb->start_subject + Fovector[offset];\n    mb->true_end_subject = mb->end_subject =\n      mb->start_subject + Fovector[offset + 1];\n    mb->moptions &= ~PCRE2_NOTEOL;\n\n    Lframe_type = GF_NOCAPTURE | Fop;\n    for (;;)\n      {\n      group_frame_type = Lframe_type;\n      RMATCH(Fecode + 1 + LINK_SIZE + Lextra_size, RM38);\n      if (rrc == MATCH_ACCEPT)\n        {\n        memcpy(Fovector,\n              (char *)assert_accept_frame + offsetof(heapframe, ovector),\n              assert_accept_frame->offset_top * sizeof(PCRE2_SIZE));\n        Foffset_top = assert_accept_frame->offset_top;\n        Fmark = assert_accept_frame->mark;\n        mb->end_subject = Lsaved_end_subject;\n        mb->true_end_subject = mb->end_subject + Ltrue_end_extra;\n        mb->moptions = Lsaved_moptions;\n        break;\n        }\n\n      if (rrc != MATCH_NOMATCH && rrc != MATCH_THEN)\n        {\n        mb->end_subject = Lsaved_end_subject;\n        mb->true_end_subject = mb->end_subject + Ltrue_end_extra;\n        mb->moptions = Lsaved_moptions;\n        RRETURN(rrc);\n        }\n\n      Fecode += GET(Fecode, 1);\n      if (*Fecode != OP_ALT)\n        {\n        mb->end_subject = Lsaved_end_subject;\n        mb->true_end_subject = mb->end_subject + Ltrue_end_extra;\n        mb->moptions = Lsaved_moptions;\n        RRETURN(MATCH_NOMATCH);\n        }\n      Lextra_size = 0;\n      }\n\n    do Fecode += GET(Fecode, 1); while (*Fecode == OP_ALT);\n    Fecode += 1 + LINK_SIZE;\n    Feptr = Lsaved_eptr;\n    break;\n\n#undef Lframe_type\n#undef Lextra_size\n#undef Lsaved_end_subject\n#undef Lsaved_eptr\n#undef Ltrue_end_extra\n#undef Lsave_moptions\n\n    /* ===================================================================== */\n    /* The callout item calls an external function, if one is provided, passing\n    details of the match so far. This is mainly for debugging, though the\n    function is able to force a failure. */\n\n    case OP_CALLOUT:\n    case OP_CALLOUT_STR:\n    rrc = do_callout(F, mb, &length);\n    if (rrc > 0) RRETURN(MATCH_NOMATCH);\n    if (rrc < 0) RRETURN(rrc);\n    Fecode += length;\n    break;\n\n\n    /* ===================================================================== */\n    /* Conditional group: compilation checked that there are no more than two\n    branches. If the condition is false, skipping the first branch takes us\n    past the end of the item if there is only one branch, but that's exactly\n    what we want. */\n\n    case OP_COND:\n    case OP_SCOND:\n\n    /* The variable Flength will be added to Fecode when the condition is\n    false, to get to the second branch. Setting it to the offset to the ALT or\n    KET, then incrementing Fecode achieves this effect. However, if the second\n    branch is non-existent, we must point to the KET so that the end of the\n    group is correctly processed. We now have Fecode pointing to the condition\n    or callout. */\n\n    Flength = GET(Fecode, 1);    /* Offset to the second branch */\n    if (Fecode[Flength] != OP_ALT) Flength -= 1 + LINK_SIZE;\n    Fecode += 1 + LINK_SIZE;     /* From this opcode */\n\n    /* Because of the way auto-callout works during compile, a callout item is\n    inserted between OP_COND and an assertion condition. Such a callout can\n    also be inserted manually. */\n\n    if (*Fecode == OP_CALLOUT || *Fecode == OP_CALLOUT_STR)\n      {\n      rrc = do_callout(F, mb, &length);\n      if (rrc > 0) RRETURN(MATCH_NOMATCH);\n      if (rrc < 0) RRETURN(rrc);\n\n      /* Advance Fecode past the callout, so it now points to the condition. We\n      must adjust Flength so that the value of Fecode+Flength is unchanged. */\n\n      Fecode += length;\n      Flength -= length;\n      }\n\n    /* Test the various possible conditions */\n\n    condition = FALSE;\n    switch(*Fecode)\n      {\n      case OP_RREF:                  /* Group recursion test */\n      if (Fcurrent_recurse != RECURSE_UNSET)\n        {\n        number = GET2(Fecode, 1);\n        condition = (number == RREF_ANY || number == Fcurrent_recurse);\n        }\n      break;\n\n      case OP_DNRREF:       /* Duplicate named group recursion test */\n      if (Fcurrent_recurse != RECURSE_UNSET)\n        {\n        int count = GET2(Fecode, 1 + IMM2_SIZE);\n        PCRE2_SPTR slot = mb->name_table + GET2(Fecode, 1) * mb->name_entry_size;\n        while (count-- > 0)\n          {\n          number = GET2(slot, 0);\n          condition = number == Fcurrent_recurse;\n          if (condition) break;\n          slot += mb->name_entry_size;\n          }\n        }\n      break;\n\n      case OP_CREF:                         /* Numbered group used test */\n      offset = (GET2(Fecode, 1) << 1) - 2;  /* Doubled ref number */\n      condition = offset < Foffset_top && Fovector[offset] != PCRE2_UNSET;\n      break;\n\n      case OP_DNCREF:      /* Duplicate named group used test */\n        {\n        int count = GET2(Fecode, 1 + IMM2_SIZE);\n        PCRE2_SPTR slot = mb->name_table + GET2(Fecode, 1) * mb->name_entry_size;\n        while (count-- > 0)\n          {\n          offset = (GET2(slot, 0) << 1) - 2;\n          condition = offset < Foffset_top && Fovector[offset] != PCRE2_UNSET;\n          if (condition) break;\n          slot += mb->name_entry_size;\n          }\n        }\n      break;\n\n      case OP_FALSE:\n      case OP_FAIL:   /* The assertion (?!) becomes OP_FAIL */\n      break;\n\n      case OP_TRUE:\n      condition = TRUE;\n      break;\n\n      /* The condition is an assertion. Run code similar to the assertion code\n      above. */\n\n#define Lpositive      F->temp_32[0]\n#define Lstart_branch  F->temp_sptr[0]\n\n      default:\n      Lpositive = (*Fecode == OP_ASSERT || *Fecode == OP_ASSERTBACK);\n      Lstart_branch = Fecode;\n\n      for (;;)\n        {\n        group_frame_type = GF_CONDASSERT | *Fecode;\n        RMATCH(Lstart_branch + PRIV(OP_lengths)[*Lstart_branch], RM5);\n\n        switch(rrc)\n          {\n          case MATCH_ACCEPT:  /* Save captures */\n          memcpy(Fovector,\n                (char *)assert_accept_frame + offsetof(heapframe, ovector),\n                assert_accept_frame->offset_top * sizeof(PCRE2_SIZE));\n          Foffset_top = assert_accept_frame->offset_top;\n\n          /* Fall through */\n          /* In the case of a match, the captures have already been put into\n          the current frame. */\n\n          case MATCH_MATCH:\n          condition = Lpositive;   /* TRUE for positive assertion */\n          break;\n\n          /* PCRE doesn't allow the effect of (*THEN) to escape beyond an\n          assertion; it is therefore always treated as NOMATCH. */\n\n          case MATCH_NOMATCH:\n          case MATCH_THEN:\n          Lstart_branch += GET(Lstart_branch, 1);\n          if (*Lstart_branch == OP_ALT) continue;  /* Try next branch */\n          condition = !Lpositive;  /* TRUE for negative assertion */\n          break;\n\n          /* These force no match without checking other branches. */\n\n          case MATCH_COMMIT:\n          case MATCH_SKIP:\n          case MATCH_PRUNE:\n          condition = !Lpositive;\n          break;\n\n          default:\n          RRETURN(rrc);\n          }\n        break;  /* Out of the branch loop */\n        }\n\n      /* If the condition is true, find the end of the assertion so that\n      advancing past it gets us to the start of the first branch. */\n\n      if (condition)\n        {\n        do Fecode += GET(Fecode, 1); while (*Fecode == OP_ALT);\n        }\n      break;  /* End of assertion condition */\n      }\n\n#undef Lpositive\n#undef Lstart_branch\n\n    /* Choose branch according to the condition. */\n\n    Fecode += condition? PRIV(OP_lengths)[*Fecode] : Flength;\n\n    /* If the opcode is OP_SCOND it means we are at a repeated conditional\n    group that might match an empty string. We must therefore descend a level\n    so that the start is remembered for checking. For OP_COND we can just\n    continue at this level. */\n\n    if (Fop == OP_SCOND)\n      {\n      group_frame_type  = GF_NOCAPTURE | Fop;\n      RMATCH(Fecode, RM35);\n      RRETURN(rrc);\n      }\n    break;\n\n\n\n/* ========================================================================= */\n/*                  End of start of parenthesis opcodes                      */\n/* ========================================================================= */\n\n\n    /* ===================================================================== */\n    /* Move the subject pointer back by one fixed amount. This occurs at the\n    start of each branch that has a fixed length in a lookbehind assertion. If\n    we are too close to the start to move back, fail. When working with UTF-8\n    we move back a number of characters, not bytes. */\n\n    case OP_REVERSE:\n    number = GET2(Fecode, 1);\n#ifdef SUPPORT_UNICODE\n    if (utf)\n      {\n      /* We used to do a simpler `while (number-- > 0)` but that triggers\n      clang's unsigned integer overflow sanitizer. */\n      while (number > 0)\n        {\n        --number;\n        if (Feptr <= mb->check_subject) RRETURN(MATCH_NOMATCH);\n        Feptr--;\n        BACKCHAR(Feptr);\n        }\n      }\n    else\n#endif\n\n    /* No UTF support, or not in UTF mode: count is code unit count */\n\n      {\n      if ((ptrdiff_t)number > Feptr - mb->start_subject) RRETURN(MATCH_NOMATCH);\n      Feptr -= number;\n      }\n\n    /* Save the earliest consulted character, then skip to next opcode */\n\n    if (Feptr < mb->start_used_ptr) mb->start_used_ptr = Feptr;\n    Fecode += 1 + IMM2_SIZE;\n    break;\n\n\n    /* ===================================================================== */\n    /* Move the subject pointer back by a variable amount. This occurs at the\n    start of each branch of a lookbehind assertion when the branch has a\n    variable, but limited, length. A loop is needed to try matching the branch\n    after moving back different numbers of characters. If we are too close to\n    the start to move back even the minimum amount, fail. When working with\n    UTF-8 we move back a number of characters, not bytes. */\n\n#define Lmin F->temp_32[0]\n#define Lmax F->temp_32[1]\n#define Leptr F->temp_sptr[0]\n\n    case OP_VREVERSE:\n    Lmin = GET2(Fecode, 1);\n    Lmax = GET2(Fecode, 1 + IMM2_SIZE);\n    Leptr = Feptr;\n\n    /* Move back by the maximum branch length and then work forwards. This\n    ensures that items such as \\d{3,5} get the maximum length, which is\n    relevant for captures, and makes for Perl compatibility. */\n\n#ifdef SUPPORT_UNICODE\n    if (utf)\n      {\n      for (i = 0; i < Lmax; i++)\n        {\n        if (Feptr == mb->start_subject)\n          {\n          if (i < Lmin) RRETURN(MATCH_NOMATCH);\n          Lmax = i;\n          break;\n          }\n        Feptr--;\n        BACKCHAR(Feptr);\n        }\n      }\n    else\n#endif\n\n    /* No UTF support or not in UTF mode */\n\n      {\n      ptrdiff_t diff = Feptr - mb->start_subject;\n      uint32_t available = (diff > 65535)? 65535 : ((diff > 0)? (int)diff : 0);\n      if (Lmin > available) RRETURN(MATCH_NOMATCH);\n      if (Lmax > available) Lmax = available;\n      Feptr -= Lmax;\n      }\n\n    /* Now try matching, moving forward one character on failure, until we\n    reach the minimum back length. */\n\n    for (;;)\n      {\n      RMATCH(Fecode + 1 + 2 * IMM2_SIZE, RM37);\n      if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n      if (Lmax-- <= Lmin) RRETURN(MATCH_NOMATCH);\n      Feptr++;\n#ifdef SUPPORT_UNICODE\n      if (utf) { FORWARDCHARTEST(Feptr, mb->end_subject); }\n#endif\n      }\n    PCRE2_UNREACHABLE(); /* Control never reaches here */\n\n#undef Lmin\n#undef Lmax\n#undef Leptr\n\n    /* ===================================================================== */\n    /* An alternation is the end of a branch; scan along to find the end of the\n    bracketed group. */\n\n    case OP_ALT:\n    branch_end = Fecode;\n    do Fecode += GET(Fecode,1); while (*Fecode == OP_ALT);\n    break;\n\n\n    /* ===================================================================== */\n    /* The end of a parenthesized group. For all but OP_BRA and OP_COND, the\n    starting frame was added to the chained frames in order to remember the\n    starting subject position for the group. (Not true for OP_BRA when it's a\n    whole pattern recursion, but that is handled separately below.)*/\n\n    case OP_KET:\n    case OP_KETRMIN:\n    case OP_KETRMAX:\n    case OP_KETRPOS:\n\n    bracode = Fecode - GET(Fecode, 1);\n\n    if (branch_end == NULL) branch_end = Fecode;\n    branch_start = bracode;\n    while (branch_start + GET(branch_start, 1) != branch_end)\n      branch_start += GET(branch_start, 1);\n    branch_end = NULL;\n\n    /* Point N to the frame at the start of the most recent group, and P to its\n    predecessor. Remember the subject pointer at the start of the group. */\n\n    if (*bracode != OP_BRA && *bracode != OP_COND)\n      {\n      N = (heapframe *)((char *)match_data->heapframes + Flast_group_offset);\n      P = (heapframe *)((char *)N - frame_size);\n      Flast_group_offset = P->last_group_offset;\n\n#ifdef DEBUG_SHOW_RMATCH\n      fprintf(stderr, \"++ KET for frame=%d type=%x prev char offset=%lu\\n\",\n        N->rdepth, N->group_frame_type,\n        (char *)P->eptr - (char *)mb->start_subject);\n#endif\n\n      /* If we are at the end of an assertion that is a condition, first check\n      to see if we are at the end of a variable-length branch in a lookbehind.\n      If this is the case and we have not landed on the current character,\n      return no match. Compare code below for non-condition lookbehinds. In\n      other cases, return a match, discarding any intermediate backtracking\n      points. Copy back the mark setting and the captures into the frame before\n      N so that they are set on return. Doing this for all assertions, both\n      positive and negative, seems to match what Perl does. */\n\n      if (GF_IDMASK(N->group_frame_type) == GF_CONDASSERT)\n        {\n        if ((*bracode == OP_ASSERTBACK || *bracode == OP_ASSERTBACK_NOT) &&\n            branch_start[1 + LINK_SIZE] == OP_VREVERSE && Feptr != P->eptr)\n          RRETURN(MATCH_NOMATCH);\n        memcpy((char *)P + offsetof(heapframe, ovector), Fovector,\n          Foffset_top * sizeof(PCRE2_SIZE));\n        P->offset_top = Foffset_top;\n        P->mark = Fmark;\n        Fback_frame = (char *)F - (char *)P;\n        RRETURN(MATCH_MATCH);\n        }\n      }\n    else P = NULL;   /* Indicates starting frame not recorded */\n\n    /* The group was not a conditional assertion. */\n\n    switch (*bracode)\n      {\n      /* Whole pattern recursion is handled as a recursion into group 0, but\n      the entire pattern is wrapped in OP_BRA/OP_KET rather than a capturing\n      group - a design mistake: it should perhaps have been capture group 0.\n      Anyway, that means the end of such recursion must be handled here. It is\n      detected by checking for an immediately following OP_END when we are\n      recursing in group 0. If this is not the end of a whole-pattern\n      recursion, there is nothing to be done. */\n\n      case OP_BRA:\n      if (Fcurrent_recurse != 0 || Fecode[1+LINK_SIZE] != OP_END) break;\n\n      /* It is the end of whole-pattern recursion. */\n\n      offset = Flast_group_offset;\n\n      /* Corrupted heapframes?. Trigger an assert and return an error */\n      PCRE2_ASSERT(offset != PCRE2_UNSET);\n      if (offset == PCRE2_UNSET) return PCRE2_ERROR_INTERNAL;\n\n      N = (heapframe *)((char *)match_data->heapframes + offset);\n      P = (heapframe *)((char *)N - frame_size);\n      Flast_group_offset = P->last_group_offset;\n\n      /* Reinstate the previous set of captures and then carry on after the\n      recursion call. */\n\n      memcpy((char *)F + offsetof(heapframe, ovector), P->ovector,\n        Foffset_top * sizeof(PCRE2_SIZE));\n      Foffset_top = P->offset_top;\n      Fcapture_last = P->capture_last;\n      Fcurrent_recurse = P->current_recurse;\n      Fecode = P->ecode + 1 + LINK_SIZE;\n      continue;  /* With next opcode */\n\n      case OP_COND:     /* No need to do anything for these */\n      case OP_SCOND:\n      break;\n\n      /* Non-atomic positive assertions are like OP_BRA, except that the\n      subject pointer must be put back to where it was at the start of the\n      assertion. For a variable lookbehind, check its end point. */\n\n      case OP_ASSERTBACK_NA:\n      if (branch_start[1 + LINK_SIZE] == OP_VREVERSE && Feptr != P->eptr)\n        RRETURN(MATCH_NOMATCH);\n      /* Fall through */\n\n      case OP_ASSERT_NA:\n      if (Feptr > mb->last_used_ptr) mb->last_used_ptr = Feptr;\n      Feptr = P->eptr;\n      break;\n\n      /* Atomic positive assertions are like OP_ONCE, except that in addition\n      the subject pointer must be put back to where it was at the start of the\n      assertion. For a variable lookbehind, check its end point. */\n\n      case OP_ASSERTBACK:\n      if (branch_start[1 + LINK_SIZE] == OP_VREVERSE && Feptr != P->eptr)\n        RRETURN(MATCH_NOMATCH);\n      /* Fall through */\n\n      case OP_ASSERT:\n      if (Feptr > mb->last_used_ptr) mb->last_used_ptr = Feptr;\n      Feptr = P->eptr;\n      /* Fall through */\n\n      /* For an atomic group, discard internal backtracking points. We must\n      also ensure that any remaining branches within the top-level of the group\n      are not tried. Do this by adjusting the code pointer within the backtrack\n      frame so that it points to the final branch. */\n\n      case OP_ONCE:\n      Fback_frame = ((char *)F - (char *)P);\n      for (;;)\n        {\n        uint32_t y = GET(P->ecode,1);\n        if ((P->ecode)[y] != OP_ALT) break;\n        P->ecode += y;\n        }\n      break;\n\n      /* A matching negative assertion returns MATCH, which is turned into\n      NOMATCH at the assertion level. For a variable lookbehind, check its end\n      point. */\n\n      case OP_ASSERTBACK_NOT:\n      if (branch_start[1 + LINK_SIZE] == OP_VREVERSE && Feptr != P->eptr)\n        RRETURN(MATCH_NOMATCH);\n      /* Fall through */\n\n      case OP_ASSERT_NOT:\n      RRETURN(MATCH_MATCH);\n\n      /* A scan substring group must preserve the current end_subject,\n      and restore it before the backtracking is performed into its sub\n      pattern. */\n\n      case OP_ASSERT_SCS:\n      F->temp_sptr[0] = mb->end_subject;\n      mb->end_subject = P->temp_sptr[0];\n      mb->true_end_subject = mb->end_subject + P->temp_size;\n      Feptr = P->temp_sptr[1];\n\n      RMATCH(Fecode + 1 + LINK_SIZE, RM39);\n\n      mb->end_subject = F->temp_sptr[0];\n      mb->true_end_subject = mb->end_subject;\n      RRETURN(rrc);\n      break;\n\n      /* At the end of a script run, apply the script-checking rules. This code\n      will never by exercised if Unicode support it not compiled, because in\n      that environment script runs cause an error at compile time. */\n\n      case OP_SCRIPT_RUN:\n      if (!PRIV(script_run)(P->eptr, Feptr, utf)) RRETURN(MATCH_NOMATCH);\n      break;\n\n      /* Whole-pattern recursion is coded as a recurse into group 0, and is\n      handled with OP_BRA above. Other recursion is handled here. */\n\n      case OP_CBRA:\n      case OP_CBRAPOS:\n      case OP_SCBRA:\n      case OP_SCBRAPOS:\n      number = GET2(bracode, 1+LINK_SIZE);\n\n      /* Handle a recursively called group. We reinstate the previous set of\n      captures and then carry on after the recursion call. */\n\n      if (Fcurrent_recurse == number)\n        {\n        P = (heapframe *)((char *)N - frame_size);\n        memcpy((char *)F + offsetof(heapframe, ovector), P->ovector,\n          Foffset_top * sizeof(PCRE2_SIZE));\n        Foffset_top = P->offset_top;\n        Fcapture_last = P->capture_last;\n        Fcurrent_recurse = P->current_recurse;\n        Fecode = P->ecode + 1 + LINK_SIZE;\n        continue;  /* With next opcode */\n        }\n\n      /* Deal with actual capturing. */\n\n      offset = (number << 1) - 2;\n      Fcapture_last = number;\n      Fovector[offset] = P->eptr - mb->start_subject;\n      Fovector[offset+1] = Feptr - mb->start_subject;\n      if (offset >= Foffset_top) Foffset_top = offset + 2;\n      break;\n      }  /* End actions relating to the starting opcode */\n\n    /* OP_KETRPOS is a possessive repeating ket. Remember the current position,\n    and return the MATCH_KETRPOS. This makes it possible to do the repeats one\n    at a time from the outer level. This must precede the empty string test -\n    in this case that test is done at the outer level. */\n\n    if (*Fecode == OP_KETRPOS)\n      {\n      memcpy((char *)P + offsetof(heapframe, eptr),\n             (char *)F + offsetof(heapframe, eptr),\n             frame_copy_size);\n      RRETURN(MATCH_KETRPOS);\n      }\n\n    /* Handle the different kinds of closing brackets. A non-repeating ket\n    needs no special action, just continuing at this level. This also happens\n    for the repeating kets if the group matched no characters, in order to\n    forcibly break infinite loops. Otherwise, the repeating kets try the rest\n    of the pattern or restart from the preceding bracket, in the appropriate\n    order. */\n\n    if (Fop != OP_KET && (P == NULL || Feptr != P->eptr))\n      {\n      if (Fop == OP_KETRMIN)\n        {\n        RMATCH(Fecode + 1 + LINK_SIZE, RM6);\n        if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n        Fecode -= GET(Fecode, 1);\n        break;   /* End of ket processing */\n        }\n\n      /* Repeat the maximum number of times (KETRMAX) */\n\n      RMATCH(bracode, RM7);\n      if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n      }\n\n    /* Carry on at this level for a non-repeating ket, or after matching an\n    empty string, or after repeating for a maximum number of times. */\n\n    Fecode += 1 + LINK_SIZE;\n    break;\n\n\n    /* ===================================================================== */\n    /* Start and end of line assertions, not multiline mode. */\n\n    case OP_CIRC:   /* Start of line, unless PCRE2_NOTBOL is set. */\n    if (Feptr != mb->start_subject || (mb->moptions & PCRE2_NOTBOL) != 0)\n      RRETURN(MATCH_NOMATCH);\n    Fecode++;\n    break;\n\n    case OP_SOD:    /* Unconditional start of subject */\n    if (Feptr != mb->start_subject) RRETURN(MATCH_NOMATCH);\n    Fecode++;\n    break;\n\n    /* When PCRE2_NOTEOL is unset, assert before the subject end, or a\n    terminating newline unless PCRE2_DOLLAR_ENDONLY is set. */\n\n    case OP_DOLL:\n    if ((mb->moptions & PCRE2_NOTEOL) != 0) RRETURN(MATCH_NOMATCH);\n    if ((mb->poptions & PCRE2_DOLLAR_ENDONLY) == 0) goto ASSERT_NL_OR_EOS;\n\n    /* Fall through */\n    /* Unconditional end of subject assertion (\\z). */\n\n    case OP_EOD:\n    if (Feptr < mb->true_end_subject) RRETURN(MATCH_NOMATCH);\n    if (mb->partial != 0)\n      {\n      mb->hitend = TRUE;\n      if (mb->partial > 1) return PCRE2_ERROR_PARTIAL;\n      }\n    Fecode++;\n    break;\n\n    /* End of subject or ending \\n assertion (\\Z) */\n\n    case OP_EODN:\n    ASSERT_NL_OR_EOS:\n    if (Feptr < mb->true_end_subject &&\n        (!IS_NEWLINE(Feptr) || Feptr != mb->true_end_subject - mb->nllen))\n      {\n      if (mb->partial != 0 &&\n          Feptr + 1 >= mb->end_subject &&\n          NLBLOCK->nltype == NLTYPE_FIXED &&\n          NLBLOCK->nllen == 2 &&\n          UCHAR21TEST(Feptr) == NLBLOCK->nl[0])\n        {\n        mb->hitend = TRUE;\n        if (mb->partial > 1) return PCRE2_ERROR_PARTIAL;\n        }\n      RRETURN(MATCH_NOMATCH);\n      }\n\n    /* Either at end of string or \\n before end. */\n\n    if (mb->partial != 0)\n      {\n      mb->hitend = TRUE;\n      if (mb->partial > 1) return PCRE2_ERROR_PARTIAL;\n      }\n    Fecode++;\n    break;\n\n\n    /* ===================================================================== */\n    /* Start and end of line assertions, multiline mode. */\n\n    /* Start of subject unless notbol, or after any newline except for one at\n    the very end, unless PCRE2_ALT_CIRCUMFLEX is set. */\n\n    case OP_CIRCM:\n    if ((mb->moptions & PCRE2_NOTBOL) != 0 && Feptr == mb->start_subject)\n      RRETURN(MATCH_NOMATCH);\n    if (Feptr != mb->start_subject &&\n        ((Feptr == mb->end_subject &&\n           (mb->poptions & PCRE2_ALT_CIRCUMFLEX) == 0) ||\n         !WAS_NEWLINE(Feptr)))\n      RRETURN(MATCH_NOMATCH);\n    Fecode++;\n    break;\n\n    /* Assert before any newline, or before end of subject unless noteol is\n    set. */\n\n    case OP_DOLLM:\n    if (Feptr < mb->end_subject)\n      {\n      if (!IS_NEWLINE(Feptr))\n        {\n        if (mb->partial != 0 &&\n            Feptr + 1 >= mb->end_subject &&\n            NLBLOCK->nltype == NLTYPE_FIXED &&\n            NLBLOCK->nllen == 2 &&\n            UCHAR21TEST(Feptr) == NLBLOCK->nl[0])\n          {\n          mb->hitend = TRUE;\n          if (mb->partial > 1) return PCRE2_ERROR_PARTIAL;\n          }\n        RRETURN(MATCH_NOMATCH);\n        }\n      }\n    else\n      {\n      if ((mb->moptions & PCRE2_NOTEOL) != 0) RRETURN(MATCH_NOMATCH);\n      SCHECK_PARTIAL();\n      }\n    Fecode++;\n    break;\n\n\n    /* ===================================================================== */\n    /* Start of match assertion */\n\n    case OP_SOM:\n    if (Feptr != mb->start_subject + mb->start_offset) RRETURN(MATCH_NOMATCH);\n    Fecode++;\n    break;\n\n\n    /* ===================================================================== */\n    /* Reset the start of match point */\n\n    case OP_SET_SOM:\n    Fstart_match = Feptr;\n    Fecode++;\n    break;\n\n\n    /* ===================================================================== */\n    /* Word boundary assertions. Find out if the previous and current\n    characters are \"word\" characters. It takes a bit more work in UTF mode.\n    Characters > 255 are assumed to be \"non-word\" characters when PCRE2_UCP is\n    not set. When it is set, use Unicode properties if available, even when not\n    in UTF mode. Remember the earliest and latest consulted characters. */\n\n    case OP_NOT_WORD_BOUNDARY:\n    case OP_WORD_BOUNDARY:\n    case OP_NOT_UCP_WORD_BOUNDARY:\n    case OP_UCP_WORD_BOUNDARY:\n    if (Feptr == mb->check_subject) prev_is_word = FALSE; else\n      {\n      PCRE2_SPTR lastptr = Feptr - 1;\n#ifdef SUPPORT_UNICODE\n      if (utf)\n        {\n        BACKCHAR(lastptr);\n        GETCHAR(fc, lastptr);\n        }\n      else\n#endif  /* SUPPORT_UNICODE */\n      fc = *lastptr;\n      if (lastptr < mb->start_used_ptr) mb->start_used_ptr = lastptr;\n#ifdef SUPPORT_UNICODE\n      if (Fop == OP_UCP_WORD_BOUNDARY || Fop == OP_NOT_UCP_WORD_BOUNDARY)\n        {\n        int chartype = UCD_CHARTYPE(fc);\n        int category = PRIV(ucp_gentype)[chartype];\n        prev_is_word = (category == ucp_L || category == ucp_N ||\n          chartype == ucp_Mn || chartype == ucp_Pc);\n        }\n      else\n#endif  /* SUPPORT_UNICODE */\n      prev_is_word = CHMAX_255(fc) && (mb->ctypes[fc] & ctype_word) != 0;\n      }\n\n    /* Get status of next character */\n\n    if (Feptr >= mb->end_subject)\n      {\n      SCHECK_PARTIAL();\n      cur_is_word = FALSE;\n      }\n    else\n      {\n      PCRE2_SPTR nextptr = Feptr + 1;\n#ifdef SUPPORT_UNICODE\n      if (utf)\n        {\n        FORWARDCHARTEST(nextptr, mb->end_subject);\n        GETCHAR(fc, Feptr);\n        }\n      else\n#endif  /* SUPPORT_UNICODE */\n      fc = *Feptr;\n      if (nextptr > mb->last_used_ptr) mb->last_used_ptr = nextptr;\n#ifdef SUPPORT_UNICODE\n      if (Fop == OP_UCP_WORD_BOUNDARY || Fop == OP_NOT_UCP_WORD_BOUNDARY)\n        {\n        int chartype = UCD_CHARTYPE(fc);\n        int category = PRIV(ucp_gentype)[chartype];\n        cur_is_word = (category == ucp_L || category == ucp_N ||\n          chartype == ucp_Mn || chartype == ucp_Pc);\n        }\n      else\n#endif  /* SUPPORT_UNICODE */\n      cur_is_word = CHMAX_255(fc) && (mb->ctypes[fc] & ctype_word) != 0;\n      }\n\n    /* Now see if the situation is what we want */\n\n    if ((*Fecode++ == OP_WORD_BOUNDARY || Fop == OP_UCP_WORD_BOUNDARY)?\n         cur_is_word == prev_is_word : cur_is_word != prev_is_word)\n      RRETURN(MATCH_NOMATCH);\n    break;\n\n\n    /* ===================================================================== */\n    /* Backtracking (*VERB)s, with and without arguments. Note that if the\n    pattern is successfully matched, we do not come back from RMATCH. */\n\n    case OP_MARK:\n    Fmark = mb->nomatch_mark = Fecode + 2;\n    RMATCH(Fecode + PRIV(OP_lengths)[*Fecode] + Fecode[1], RM12);\n\n    /* A return of MATCH_SKIP_ARG means that matching failed at SKIP with an\n    argument, and we must check whether that argument matches this MARK's\n    argument. It is passed back in mb->verb_skip_ptr. If it does match, we\n    return MATCH_SKIP with mb->verb_skip_ptr now pointing to the subject\n    position that corresponds to this mark. Otherwise, pass back the return\n    code unaltered. */\n\n    if (rrc == MATCH_SKIP_ARG &&\n             PRIV(strcmp)(Fecode + 2, mb->verb_skip_ptr) == 0)\n      {\n      mb->verb_skip_ptr = Feptr;   /* Pass back current position */\n      RRETURN(MATCH_SKIP);\n      }\n    RRETURN(rrc);\n\n    case OP_FAIL:\n    RRETURN(MATCH_NOMATCH);\n\n    /* Record the current recursing group number in mb->verb_current_recurse\n    when a backtracking return such as MATCH_COMMIT is given. This enables the\n    recurse processing to catch verbs from within the recursion. */\n\n    case OP_COMMIT:\n    RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM13);\n    if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n    mb->verb_current_recurse = Fcurrent_recurse;\n    RRETURN(MATCH_COMMIT);\n\n    case OP_COMMIT_ARG:\n    Fmark = mb->nomatch_mark = Fecode + 2;\n    RMATCH(Fecode + PRIV(OP_lengths)[*Fecode] + Fecode[1], RM36);\n    if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n    mb->verb_current_recurse = Fcurrent_recurse;\n    RRETURN(MATCH_COMMIT);\n\n    case OP_PRUNE:\n    RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM14);\n    if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n    mb->verb_current_recurse = Fcurrent_recurse;\n    RRETURN(MATCH_PRUNE);\n\n    case OP_PRUNE_ARG:\n    Fmark = mb->nomatch_mark = Fecode + 2;\n    RMATCH(Fecode + PRIV(OP_lengths)[*Fecode] + Fecode[1], RM15);\n    if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n    mb->verb_current_recurse = Fcurrent_recurse;\n    RRETURN(MATCH_PRUNE);\n\n    case OP_SKIP:\n    RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM16);\n    if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n    mb->verb_skip_ptr = Feptr;   /* Pass back current position */\n    mb->verb_current_recurse = Fcurrent_recurse;\n    RRETURN(MATCH_SKIP);\n\n    /* Note that, for Perl compatibility, SKIP with an argument does NOT set\n    nomatch_mark. When a pattern match ends with a SKIP_ARG for which there was\n    not a matching mark, we have to re-run the match, ignoring the SKIP_ARG\n    that failed and any that precede it (either they also failed, or were not\n    triggered). To do this, we maintain a count of executed SKIP_ARGs. If a\n    SKIP_ARG gets to top level, the match is re-run with mb->ignore_skip_arg\n    set to the count of the one that failed. */\n\n    case OP_SKIP_ARG:\n    mb->skip_arg_count++;\n    if (mb->skip_arg_count <= mb->ignore_skip_arg)\n      {\n      Fecode += PRIV(OP_lengths)[*Fecode] + Fecode[1];\n      break;\n      }\n    RMATCH(Fecode + PRIV(OP_lengths)[*Fecode] + Fecode[1], RM17);\n    if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n\n    /* Pass back the current skip name and return the special MATCH_SKIP_ARG\n    return code. This will either be caught by a matching MARK, or get to the\n    top, where it causes a rematch with mb->ignore_skip_arg set to the value of\n    mb->skip_arg_count. */\n\n    mb->verb_skip_ptr = Fecode + 2;\n    mb->verb_current_recurse = Fcurrent_recurse;\n    RRETURN(MATCH_SKIP_ARG);\n\n    /* For THEN (and THEN_ARG) we pass back the address of the opcode, so that\n    the branch in which it occurs can be determined. */\n\n    case OP_THEN:\n    RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM18);\n    if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n    mb->verb_ecode_ptr = Fecode;\n    mb->verb_current_recurse = Fcurrent_recurse;\n    RRETURN(MATCH_THEN);\n\n    case OP_THEN_ARG:\n    Fmark = mb->nomatch_mark = Fecode + 2;\n    RMATCH(Fecode + PRIV(OP_lengths)[*Fecode] + Fecode[1], RM19);\n    if (rrc != MATCH_NOMATCH) RRETURN(rrc);\n    mb->verb_ecode_ptr = Fecode;\n    mb->verb_current_recurse = Fcurrent_recurse;\n    RRETURN(MATCH_THEN);\n\n\n    /* ===================================================================== */\n    /* There's been some horrible disaster. Arrival here can only mean there is\n    something seriously wrong in the code above or the OP_xxx definitions. */\n\n    default:\n    PCRE2_DEBUG_UNREACHABLE();\n    return PCRE2_ERROR_INTERNAL;\n    }\n\n  /* Do not insert any code in here without much thought; it is assumed\n  that \"continue\" in the code above comes out to here to repeat the main\n  loop. */\n\n  }  /* End of main loop */\n\nPCRE2_DEBUG_UNREACHABLE(); /* Control should never reach here */\n\n/* ========================================================================= */\n/* The RRETURN() macro jumps here. The number that is saved in Freturn_id\nindicates which label we actually want to return to. The value in Frdepth is\nthe index number of the frame in the vector. The return value has been placed\nin rrc. */\n\n#define LBL(val) case val: goto L_RM##val;\n\nRETURN_SWITCH:\nif (Feptr > mb->last_used_ptr) mb->last_used_ptr = Feptr;\nif (Frdepth == 0) return rrc;                     /* Exit from the top level */\nF = (heapframe *)((char *)F - Fback_frame);       /* Backtrack */\nmb->cb->callout_flags |= PCRE2_CALLOUT_BACKTRACK; /* Note for callouts */\n\n#ifdef DEBUG_SHOW_RMATCH\nfprintf(stderr, \"++ RETURN %d to RM%d\\n\", rrc, Freturn_id);\n#endif\n\nswitch (Freturn_id)\n  {\n  LBL( 1) LBL( 2) LBL( 3) LBL( 4) LBL( 5) LBL( 6) LBL( 7) LBL( 8)\n  LBL( 9) LBL(10) LBL(11) LBL(12) LBL(13) LBL(14) LBL(15) LBL(16)\n  LBL(17) LBL(18) LBL(19) LBL(20) LBL(21) LBL(22) LBL(23) LBL(24)\n  LBL(25) LBL(26) LBL(27) LBL(28) LBL(29) LBL(30) LBL(31) LBL(32)\n  LBL(33) LBL(34) LBL(35) LBL(36) LBL(37) LBL(38) LBL(39)\n\n#ifdef SUPPORT_WIDE_CHARS\n  LBL(100) LBL(101) LBL(102) LBL(103)\n#endif\n\n#ifdef SUPPORT_UNICODE\n  LBL(200) LBL(201) LBL(202) LBL(203) LBL(204) LBL(205) LBL(206)\n  LBL(207) LBL(208) LBL(209) LBL(210) LBL(211) LBL(212) LBL(213)\n  LBL(214) LBL(215) LBL(216) LBL(217) LBL(218) LBL(219) LBL(220)\n  LBL(221) LBL(222) LBL(223) LBL(224)\n#endif\n\n  default:\n  PCRE2_DEBUG_UNREACHABLE();\n  return PCRE2_ERROR_INTERNAL;\n  }\n#undef LBL\n}\n\n\n/*************************************************\n*           Match a Regular Expression           *\n*************************************************/\n\n/* This function applies a compiled pattern to a subject string and picks out\nportions of the string if it matches. Two elements in the vector are set for\neach substring: the offsets to the start and end of the substring.\n\nArguments:\n  code            points to the compiled expression\n  subject         points to the subject string\n  length          length of subject string (may contain binary zeros)\n  start_offset    where to start in the subject string\n  options         option bits\n  match_data      points to a match_data block\n  mcontext        points a PCRE2 context\n\nReturns:          > 0 => success; value is the number of ovector pairs filled\n                  = 0 => success, but ovector is not big enough\n                  = -1 => failed to match (PCRE2_ERROR_NOMATCH)\n                  = -2 => partial match (PCRE2_ERROR_PARTIAL)\n                  < -2 => some kind of unexpected problem\n*/\n\n//au: added parameter: , int(*callout)(pcre2_callout_block *, void *)\n\nPCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION\npcre2_match(const pcre2_code *code, PCRE2_SPTR subject, PCRE2_SIZE length,\n  PCRE2_SIZE start_offset, uint32_t options, pcre2_match_data *match_data,\n  pcre2_match_context *mcontext, int(*callout)(pcre2_callout_block*, void*))\n{\nint rc;\nint was_zero_terminated = 0;\nconst uint8_t *start_bits = NULL;\nconst pcre2_real_code *re = (const pcre2_real_code *)code;\n\nBOOL anchored;\nBOOL firstline;\nBOOL has_first_cu = FALSE;\nBOOL has_req_cu = FALSE;\nBOOL startline;\n\n#if PCRE2_CODE_UNIT_WIDTH == 8\nPCRE2_SPTR memchr_found_first_cu;\nPCRE2_SPTR memchr_found_first_cu2;\n#endif\n\nPCRE2_UCHAR first_cu = 0;\nPCRE2_UCHAR first_cu2 = 0;\nPCRE2_UCHAR req_cu = 0;\nPCRE2_UCHAR req_cu2 = 0;\n\nPCRE2_SPTR bumpalong_limit;\nPCRE2_SPTR end_subject;\nPCRE2_SPTR true_end_subject;\nPCRE2_SPTR start_match;\nPCRE2_SPTR req_cu_ptr;\nPCRE2_SPTR start_partial;\nPCRE2_SPTR match_partial;\n\n#ifdef SUPPORT_JIT\nBOOL use_jit;\n#endif\n\n/* This flag is needed even when Unicode is not supported for convenience\n(it is used by the IS_NEWLINE macro). */\n\nBOOL utf = FALSE;\n\n#ifdef SUPPORT_UNICODE\nBOOL ucp = FALSE;\nBOOL allow_invalid;\nuint32_t fragment_options = 0;\n#ifdef SUPPORT_JIT\nBOOL jit_checked_utf = FALSE;\n#endif\n#endif  /* SUPPORT_UNICODE */\n\nPCRE2_SIZE frame_size;\nPCRE2_SIZE heapframes_size;\n\n/* We need to have mb as a pointer to a match block, because the IS_NEWLINE\nmacro is used below, and it expects NLBLOCK to be defined as a pointer. */\n\npcre2_callout_block cb;\nmatch_block actual_match_block;\nmatch_block *mb = &actual_match_block;\n\n/* Recognize NULL, length 0 as an empty string. */\n\nif (subject == NULL && length == 0) subject = (PCRE2_SPTR)\"\";\n\n/* Plausibility checks */\n\nif ((options & ~PUBLIC_MATCH_OPTIONS) != 0) return PCRE2_ERROR_BADOPTION;\nif (code == NULL || subject == NULL || match_data == NULL)\n  return PCRE2_ERROR_NULL;\n\nstart_match = subject + start_offset;\nreq_cu_ptr = start_match - 1;\nif (length == PCRE2_ZERO_TERMINATED)\n  {\n  length = PRIV(strlen)(subject);\n  was_zero_terminated = 1;\n  }\ntrue_end_subject = end_subject = subject + length;\n\nif (start_offset > length) return PCRE2_ERROR_BADOFFSET;\n\n/* Check that the first field in the block is the magic number. */\n\nif (re->magic_number != MAGIC_NUMBER) return PCRE2_ERROR_BADMAGIC;\n\n/* Check the code unit width. */\n\nif ((re->flags & PCRE2_MODE_MASK) != PCRE2_CODE_UNIT_WIDTH/8)\n  return PCRE2_ERROR_BADMODE;\n\n/* PCRE2_NOTEMPTY and PCRE2_NOTEMPTY_ATSTART are match-time flags in the\noptions variable for this function. Users of PCRE2 who are not calling the\nfunction directly would like to have a way of setting these flags, in the same\nway that they can set pcre2_compile() flags like PCRE2_NO_AUTO_POSSESS with\nconstructions like (*NO_AUTOPOSSESS). To enable this, (*NOTEMPTY) and\n(*NOTEMPTY_ATSTART) set bits in the pattern's \"flag\" function which we now\ntransfer to the options for this function. The bits are guaranteed to be\nadjacent, but do not have the same values. This bit of Boolean trickery assumes\nthat the match-time bits are not more significant than the flag bits. If by\naccident this is not the case, a compile-time division by zero error will\noccur. */\n\n#define FF (PCRE2_NOTEMPTY_SET|PCRE2_NE_ATST_SET)\n#define OO (PCRE2_NOTEMPTY|PCRE2_NOTEMPTY_ATSTART)\noptions |= (re->flags & FF) / ((FF & (~FF+1)) / (OO & (~OO+1)));\n#undef FF\n#undef OO\n\n/* If the pattern was successfully studied with JIT support, we will run the\nJIT executable instead of the rest of this function. Most options must be set\nat compile time for the JIT code to be usable. */\n\n#ifdef SUPPORT_JIT\nuse_jit = (re->executable_jit != NULL &&\n          (options & ~PUBLIC_JIT_MATCH_OPTIONS) == 0);\n#endif\n\n/* Initialize UTF/UCP parameters. */\n\n#ifdef SUPPORT_UNICODE\nutf = (re->overall_options & PCRE2_UTF) != 0;\nallow_invalid = (re->overall_options & PCRE2_MATCH_INVALID_UTF) != 0;\nucp = (re->overall_options & PCRE2_UCP) != 0;\n#endif  /* SUPPORT_UNICODE */\n\n/* Convert the partial matching flags into an integer. */\n\nmb->partial = ((options & PCRE2_PARTIAL_HARD) != 0)? 2 :\n              ((options & PCRE2_PARTIAL_SOFT) != 0)? 1 : 0;\n\n/* Partial matching and PCRE2_ENDANCHORED are currently not allowed at the same\ntime. */\n\nif (mb->partial != 0 &&\n   ((re->overall_options | options) & PCRE2_ENDANCHORED) != 0)\n  return PCRE2_ERROR_BADOPTION;\n\n/* It is an error to set an offset limit without setting the flag at compile\ntime. */\n\nif (mcontext != NULL && mcontext->offset_limit != PCRE2_UNSET &&\n     (re->overall_options & PCRE2_USE_OFFSET_LIMIT) == 0)\n  return PCRE2_ERROR_BADOFFSETLIMIT;\n\n/* If the match data block was previously used with PCRE2_COPY_MATCHED_SUBJECT,\nfree the memory that was obtained. Set the field to NULL for no match cases. */\n\nif ((match_data->flags & PCRE2_MD_COPIED_SUBJECT) != 0)\n  {\n  match_data->memctl.free((void *)match_data->subject,\n    match_data->memctl.memory_data);\n  match_data->flags &= ~PCRE2_MD_COPIED_SUBJECT;\n  }\nmatch_data->subject = NULL;\n\n/* Zero the error offset in case the first code unit is invalid UTF. */\n\nmatch_data->startchar = 0;\n\n\n/* ============================= JIT matching ============================== */\n\n/* Prepare for JIT matching. Check a UTF string for validity unless no check is\nrequested or invalid UTF can be handled. We check only the portion of the\nsubject that might be be inspected during matching - from the offset minus the\nmaximum lookbehind to the given length. This saves time when a small part of a\nlarge subject is being matched by the use of a starting offset. Note that the\nmaximum lookbehind is a number of characters, not code units. */\n\n#ifdef SUPPORT_JIT\nif (use_jit)\n  {\n#ifdef SUPPORT_UNICODE\n  if (utf && (options & PCRE2_NO_UTF_CHECK) == 0 && !allow_invalid)\n    {\n\n    /* For 8-bit and 16-bit UTF, check that the first code unit is a valid\n    character start. */\n\n#if PCRE2_CODE_UNIT_WIDTH != 32\n    if (start_match < end_subject && NOT_FIRSTCU(*start_match))\n      {\n      if (start_offset > 0) return PCRE2_ERROR_BADUTFOFFSET;\n#if PCRE2_CODE_UNIT_WIDTH == 8\n      return PCRE2_ERROR_UTF8_ERR20;  /* Isolated 0x80 byte */\n#else\n      return PCRE2_ERROR_UTF16_ERR3;  /* Isolated low surrogate */\n#endif\n      }\n#endif  /* WIDTH != 32 */\n\n    /* Move back by the maximum lookbehind, just in case it happens at the very\n    start of matching. */\n\n#if PCRE2_CODE_UNIT_WIDTH != 32\n    for (unsigned int i = re->max_lookbehind; i > 0 && start_match > subject; i--)\n      {\n      start_match--;\n      while (start_match > subject &&\n#if PCRE2_CODE_UNIT_WIDTH == 8\n      (*start_match & 0xc0) == 0x80)\n#else  /* 16-bit */\n      (*start_match & 0xfc00) == 0xdc00)\n#endif\n        start_match--;\n      }\n#else  /* PCRE2_CODE_UNIT_WIDTH != 32 */\n\n    /* In the 32-bit library, one code unit equals one character. However,\n    we cannot just subtract the lookbehind and then compare pointers, because\n    a very large lookbehind could create an invalid pointer. */\n\n    if (start_offset >= re->max_lookbehind)\n      start_match -= re->max_lookbehind;\n    else\n      start_match = subject;\n#endif  /* PCRE2_CODE_UNIT_WIDTH != 32 */\n\n    /* Validate the relevant portion of the subject. Adjust the offset of an\n    invalid code point to be an absolute offset in the whole string. */\n\n    match_data->rc = PRIV(valid_utf)(start_match,\n      length - (start_match - subject), &(match_data->startchar));\n    if (match_data->rc != 0)\n      {\n      match_data->startchar += start_match - subject;\n      return match_data->rc;\n      }\n    jit_checked_utf = TRUE;\n    }\n#endif  /* SUPPORT_UNICODE */\n\n  /* If JIT returns BADOPTION, which means that the selected complete or\n  partial matching mode was not compiled, fall through to the interpreter. */\n\n  rc = pcre2_jit_match(code, subject, length, start_offset, options,\n    match_data, mcontext);\n  if (rc != PCRE2_ERROR_JIT_BADOPTION)\n    {\n    match_data->subject_length = length;\n    if (rc >= 0 && (options & PCRE2_COPY_MATCHED_SUBJECT) != 0)\n      {\n      length = CU2BYTES(length + was_zero_terminated);\n      match_data->subject = match_data->memctl.malloc(length,\n        match_data->memctl.memory_data);\n      if (match_data->subject == NULL) return PCRE2_ERROR_NOMEMORY;\n      memcpy((void *)match_data->subject, subject, length);\n      match_data->flags |= PCRE2_MD_COPIED_SUBJECT;\n      }\n    return rc;\n    }\n  }\n#endif  /* SUPPORT_JIT */\n\n/* ========================= End of JIT matching ========================== */\n\n\n/* Proceed with non-JIT matching. The default is to allow lookbehinds to the\nstart of the subject. A UTF check when there is a non-zero offset may change\nthis. */\n\nmb->check_subject = subject;\n\n/* If a UTF subject string was not checked for validity in the JIT code above,\ncheck it here, and handle support for invalid UTF strings. The check above\nhappens only when invalid UTF is not supported and PCRE2_NO_CHECK_UTF is unset.\nIf we get here in those circumstances, it means the subject string is valid,\nbut for some reason JIT matching was not successful. There is no need to check\nthe subject again.\n\nWe check only the portion of the subject that might be be inspected during\nmatching - from the offset minus the maximum lookbehind to the given length.\nThis saves time when a small part of a large subject is being matched by the\nuse of a starting offset. Note that the maximum lookbehind is a number of\ncharacters, not code units.\n\nNote also that support for invalid UTF forces a check, overriding the setting\nof PCRE2_NO_CHECK_UTF. */\n\n#ifdef SUPPORT_UNICODE\nif (utf &&\n#ifdef SUPPORT_JIT\n    !jit_checked_utf &&\n#endif\n    ((options & PCRE2_NO_UTF_CHECK) == 0 || allow_invalid))\n  {\n#if PCRE2_CODE_UNIT_WIDTH != 32\n  BOOL skipped_bad_start = FALSE;\n#endif\n\n  /* For 8-bit and 16-bit UTF, check that the first code unit is a valid\n  character start. If we are handling invalid UTF, just skip over such code\n  units. Otherwise, give an appropriate error. */\n\n#if PCRE2_CODE_UNIT_WIDTH != 32\n  if (allow_invalid)\n    {\n    while (start_match < end_subject && NOT_FIRSTCU(*start_match))\n      {\n      start_match++;\n      skipped_bad_start = TRUE;\n      }\n    }\n  else if (start_match < end_subject && NOT_FIRSTCU(*start_match))\n    {\n    if (start_offset > 0) return PCRE2_ERROR_BADUTFOFFSET;\n#if PCRE2_CODE_UNIT_WIDTH == 8\n    return PCRE2_ERROR_UTF8_ERR20;  /* Isolated 0x80 byte */\n#else\n    return PCRE2_ERROR_UTF16_ERR3;  /* Isolated low surrogate */\n#endif\n    }\n#endif  /* WIDTH != 32 */\n\n  /* The mb->check_subject field points to the start of UTF checking;\n  lookbehinds can go back no further than this. */\n\n  mb->check_subject = start_match;\n\n  /* Move back by the maximum lookbehind, just in case it happens at the very\n  start of matching, but don't do this if we skipped bad 8-bit or 16-bit code\n  units above. */\n\n#if PCRE2_CODE_UNIT_WIDTH != 32\n  if (!skipped_bad_start)\n    {\n    unsigned int i;\n    for (i = re->max_lookbehind; i > 0 && mb->check_subject > subject; i--)\n      {\n      mb->check_subject--;\n      while (mb->check_subject > subject &&\n#if PCRE2_CODE_UNIT_WIDTH == 8\n      (*mb->check_subject & 0xc0) == 0x80)\n#else  /* 16-bit */\n      (*mb->check_subject & 0xfc00) == 0xdc00)\n#endif\n        mb->check_subject--;\n      }\n    }\n#else  /* PCRE2_CODE_UNIT_WIDTH != 32 */\n\n  /* In the 32-bit library, one code unit equals one character. However,\n  we cannot just subtract the lookbehind and then compare pointers, because\n  a very large lookbehind could create an invalid pointer. */\n\n  if (start_offset >= re->max_lookbehind)\n    mb->check_subject -= re->max_lookbehind;\n  else\n    mb->check_subject = subject;\n#endif  /* PCRE2_CODE_UNIT_WIDTH != 32 */\n\n  /* Validate the relevant portion of the subject. There's a loop in case we\n  encounter bad UTF in the characters preceding start_match which we are\n  scanning because of a lookbehind. */\n\n  for (;;)\n    {\n    match_data->rc = PRIV(valid_utf)(mb->check_subject,\n      length - (mb->check_subject - subject), &(match_data->startchar));\n\n    if (match_data->rc == 0) break;   /* Valid UTF string */\n\n    /* Invalid UTF string. Adjust the offset to be an absolute offset in the\n    whole string. If we are handling invalid UTF strings, set end_subject to\n    stop before the bad code unit, and set the options to \"not end of line\".\n    Otherwise return the error. */\n\n    match_data->startchar += mb->check_subject - subject;\n    if (!allow_invalid || match_data->rc > 0) return match_data->rc;\n    end_subject = subject + match_data->startchar;\n\n    /* If the end precedes start_match, it means there is invalid UTF in the\n    extra code units we reversed over because of a lookbehind. Advance past the\n    first bad code unit, and then skip invalid character starting code units in\n    8-bit and 16-bit modes, and try again with the original end point. */\n\n    if (end_subject < start_match)\n      {\n      mb->check_subject = end_subject + 1;\n#if PCRE2_CODE_UNIT_WIDTH != 32\n      while (mb->check_subject < start_match && NOT_FIRSTCU(*mb->check_subject))\n        mb->check_subject++;\n#endif\n      end_subject = true_end_subject;\n      }\n\n    /* Otherwise, set the not end of line option, and do the match. */\n\n    else\n      {\n      fragment_options = PCRE2_NOTEOL;\n      break;\n      }\n    }\n  }\n#endif  /* SUPPORT_UNICODE */\n\n/* A NULL match context means \"use a default context\", but we take the memory\ncontrol functions from the pattern. */\n\nif (mcontext == NULL)\n  {\n  mcontext = (pcre2_match_context *)(&PRIV(default_match_context));\n  mb->memctl = re->memctl;\n  }\nelse mb->memctl = mcontext->memctl;\n\nanchored = ((re->overall_options | options) & PCRE2_ANCHORED) != 0;\nfirstline = !anchored && (re->overall_options & PCRE2_FIRSTLINE) != 0;\nstartline = (re->flags & PCRE2_STARTLINE) != 0;\nbumpalong_limit = (mcontext->offset_limit == PCRE2_UNSET)?\n  true_end_subject : subject + mcontext->offset_limit;\n\n/* Initialize and set up the fixed fields in the callout block, with a pointer\nin the match block. */\n\nmb->cb = &cb;\ncb.version = 2;\ncb.subject = subject;\ncb.subject_length = (PCRE2_SIZE)(end_subject - subject);\ncb.callout_flags = 0;\n\n/* Fill in the remaining fields in the match block, except for moptions, which\ngets set later. */\n\n//au: use callout parameter instead of match context\nmb->callout = callout;\n//mb->callout = mcontext->callout;\n//mb->callout_data = mcontext->callout_data;\n\nmb->start_subject = subject;\nmb->start_offset = start_offset;\nmb->end_subject = end_subject;\nmb->true_end_subject = true_end_subject;\nmb->hasthen = (re->flags & PCRE2_HASTHEN) != 0;\nmb->allowemptypartial = (re->max_lookbehind > 0) ||\n    (re->flags & PCRE2_MATCH_EMPTY) != 0;\nmb->poptions = re->overall_options;          /* Pattern options */\nmb->ignore_skip_arg = 0;\nmb->mark = mb->nomatch_mark = NULL;          /* In case never set */\n\n/* The name table is needed for finding all the numbers associated with a\ngiven name, for condition testing. The code follows the name table. */\n\nmb->name_table = (PCRE2_SPTR)((const uint8_t *)re + sizeof(pcre2_real_code));\nmb->name_count = re->name_count;\nmb->name_entry_size = re->name_entry_size;\nmb->start_code = (PCRE2_SPTR)((const uint8_t *)re + re->code_start);\n\n/* Process the \\R and newline settings. */\n\nmb->bsr_convention = re->bsr_convention;\nmb->nltype = NLTYPE_FIXED;\nswitch(re->newline_convention)\n  {\n  case PCRE2_NEWLINE_CR:\n  mb->nllen = 1;\n  mb->nl[0] = CHAR_CR;\n  break;\n\n  case PCRE2_NEWLINE_LF:\n  mb->nllen = 1;\n  mb->nl[0] = CHAR_NL;\n  break;\n\n  case PCRE2_NEWLINE_NUL:\n  mb->nllen = 1;\n  mb->nl[0] = CHAR_NUL;\n  break;\n\n  case PCRE2_NEWLINE_CRLF:\n  mb->nllen = 2;\n  mb->nl[0] = CHAR_CR;\n  mb->nl[1] = CHAR_NL;\n  break;\n\n  case PCRE2_NEWLINE_ANY:\n  mb->nltype = NLTYPE_ANY;\n  break;\n\n  case PCRE2_NEWLINE_ANYCRLF:\n  mb->nltype = NLTYPE_ANYCRLF;\n  break;\n\n  default:\n  PCRE2_DEBUG_UNREACHABLE();\n  return PCRE2_ERROR_INTERNAL;\n  }\n\n/* The backtracking frames have fixed data at the front, and a PCRE2_SIZE\nvector at the end, whose size depends on the number of capturing parentheses in\nthe pattern. It is not used at all if there are no capturing parentheses.\n\n  frame_size                   is the total size of each frame\n  match_data->heapframes       is the pointer to the frames vector\n  match_data->heapframes_size  is the allocated size of the vector\n\nWe must pad the frame_size for alignment to ensure subsequent frames are as\naligned as heapframe. Whilst ovector is word-aligned due to being a PCRE2_SIZE\narray, that does not guarantee it is suitably aligned for pointers, as some\narchitectures have pointers that are larger than a size_t. */\n\nframe_size = (offsetof(heapframe, ovector) +\n  re->top_bracket * 2 * sizeof(PCRE2_SIZE) + HEAPFRAME_ALIGNMENT - 1) &\n  ~(HEAPFRAME_ALIGNMENT - 1);\n\n/* Limits set in the pattern override the match context only if they are\nsmaller. */\n\nmb->heap_limit = ((mcontext->heap_limit < re->limit_heap)?\n  mcontext->heap_limit : re->limit_heap);\n\nmb->match_limit = (mcontext->match_limit < re->limit_match)?\n  mcontext->match_limit : re->limit_match;\n\nmb->match_limit_depth = (mcontext->depth_limit < re->limit_depth)?\n  mcontext->depth_limit : re->limit_depth;\n\n/* If a pattern has very many capturing parentheses, the frame size may be very\nlarge. Set the initial frame vector size to ensure that there are at least 10\navailable frames, but enforce a minimum of START_FRAMES_SIZE. If this is\ngreater than the heap limit, get as large a vector as possible. */\n\nheapframes_size = frame_size * 10;\nif (heapframes_size < START_FRAMES_SIZE) heapframes_size = START_FRAMES_SIZE;\nif (heapframes_size / 1024 > mb->heap_limit)\n  {\n  PCRE2_SIZE max_size = 1024 * mb->heap_limit;\n  if (max_size < frame_size) return PCRE2_ERROR_HEAPLIMIT;\n  heapframes_size = max_size;\n  }\n\n/* If an existing frame vector in the match_data block is large enough, we can\nuse it. Otherwise, free any pre-existing vector and get a new one. */\n\nif (match_data->heapframes_size < heapframes_size)\n  {\n  match_data->memctl.free(match_data->heapframes,\n    match_data->memctl.memory_data);\n  match_data->heapframes = match_data->memctl.malloc(heapframes_size,\n    match_data->memctl.memory_data);\n  if (match_data->heapframes == NULL)\n    {\n    match_data->heapframes_size = 0;\n    return PCRE2_ERROR_NOMEMORY;\n    }\n  match_data->heapframes_size = heapframes_size;\n  }\n\n/* Write to the ovector within the first frame to mark every capture unset and\nto avoid uninitialized memory read errors when it is copied to a new frame. */\n\nmemset((char *)(match_data->heapframes) + offsetof(heapframe, ovector), 0xff,\n  frame_size - offsetof(heapframe, ovector));\n\n/* Pointers to the individual character tables */\n\nmb->lcc = re->tables + lcc_offset;\nmb->fcc = re->tables + fcc_offset;\nmb->ctypes = re->tables + ctypes_offset;\n\n/* Set up the first code unit to match, if available. If there's no first code\nunit there may be a bitmap of possible first characters. */\n\nif ((re->flags & PCRE2_FIRSTSET) != 0)\n  {\n  has_first_cu = TRUE;\n  first_cu = first_cu2 = (PCRE2_UCHAR)(re->first_codeunit);\n  if ((re->flags & PCRE2_FIRSTCASELESS) != 0)\n    {\n    first_cu2 = TABLE_GET(first_cu, mb->fcc, first_cu);\n#ifdef SUPPORT_UNICODE\n#if PCRE2_CODE_UNIT_WIDTH == 8\n    if (first_cu > 127 && ucp && !utf) first_cu2 = UCD_OTHERCASE(first_cu);\n#else\n    if (first_cu > 127 && (utf || ucp)) first_cu2 = UCD_OTHERCASE(first_cu);\n#endif\n#endif  /* SUPPORT_UNICODE */\n    }\n  }\nelse\n  if (!startline && (re->flags & PCRE2_FIRSTMAPSET) != 0)\n    start_bits = re->start_bitmap;\n\n/* There may also be a \"last known required character\" set. */\n\nif ((re->flags & PCRE2_LASTSET) != 0)\n  {\n  has_req_cu = TRUE;\n  req_cu = req_cu2 = (PCRE2_UCHAR)(re->last_codeunit);\n  if ((re->flags & PCRE2_LASTCASELESS) != 0)\n    {\n    req_cu2 = TABLE_GET(req_cu, mb->fcc, req_cu);\n#ifdef SUPPORT_UNICODE\n#if PCRE2_CODE_UNIT_WIDTH == 8\n    if (req_cu > 127 && ucp && !utf) req_cu2 = UCD_OTHERCASE(req_cu);\n#else\n    if (req_cu > 127 && (utf || ucp)) req_cu2 = UCD_OTHERCASE(req_cu);\n#endif\n#endif  /* SUPPORT_UNICODE */\n    }\n  }\n\n\n/* ==========================================================================*/\n\n/* Loop for handling unanchored repeated matching attempts; for anchored regexs\nthe loop runs just once. */\n\n#ifdef SUPPORT_UNICODE\nFRAGMENT_RESTART:\n#endif\n\nstart_partial = match_partial = NULL;\nmb->hitend = FALSE;\n\n#if PCRE2_CODE_UNIT_WIDTH == 8\nmemchr_found_first_cu = NULL;\nmemchr_found_first_cu2 = NULL;\n#endif\n\nfor(;;)\n  {\n  PCRE2_SPTR new_start_match;\n\n  /* ----------------- Start of match optimizations ---------------- */\n\n  /* There are some optimizations that avoid running the match if a known\n  starting point is not found, or if a known later code unit is not present.\n  However, there is an option (settable at compile time) that disables these,\n  for testing and for ensuring that all callouts do actually occur. */\n\n  if ((re->optimization_flags & PCRE2_OPTIM_START_OPTIMIZE) != 0)\n    {\n    /* If firstline is TRUE, the start of the match is constrained to the first\n    line of a multiline string. That is, the match must be before or at the\n    first newline following the start of matching. Temporarily adjust\n    end_subject so that we stop the scans for a first code unit at a newline.\n    If the match fails at the newline, later code breaks the loop. */\n\n    if (firstline)\n      {\n      PCRE2_SPTR t = start_match;\n#ifdef SUPPORT_UNICODE\n      if (utf)\n        {\n        while (t < end_subject && !IS_NEWLINE(t))\n          {\n          t++;\n          ACROSSCHAR(t < end_subject, t, t++);\n          }\n        }\n      else\n#endif\n      while (t < end_subject && !IS_NEWLINE(t)) t++;\n      end_subject = t;\n      }\n\n    /* Anchored: check the first code unit if one is recorded. This may seem\n    pointless but it can help in detecting a no match case without scanning for\n    the required code unit. */\n\n    if (anchored)\n      {\n      if (has_first_cu || start_bits != NULL)\n        {\n        BOOL ok = start_match < end_subject;\n        if (ok)\n          {\n          PCRE2_UCHAR c = UCHAR21TEST(start_match);\n          ok = has_first_cu && (c == first_cu || c == first_cu2);\n          if (!ok && start_bits != NULL)\n            {\n#if PCRE2_CODE_UNIT_WIDTH != 8\n            if (c > 255) c = 255;\n#endif\n            ok = (start_bits[c/8] & (1u << (c&7))) != 0;\n            }\n          }\n        if (!ok)\n          {\n          rc = MATCH_NOMATCH;\n          break;\n          }\n        }\n      }\n\n    /* Not anchored. Advance to a unique first code unit if there is one. */\n\n    else\n      {\n      if (has_first_cu)\n        {\n        if (first_cu != first_cu2)  /* Caseless */\n          {\n          /* In 16-bit and 32_bit modes we have to do our own search, so can\n          look for both cases at once. */\n\n#if PCRE2_CODE_UNIT_WIDTH != 8\n          PCRE2_UCHAR smc;\n          while (start_match < end_subject &&\n                (smc = UCHAR21TEST(start_match)) != first_cu &&\n                 smc != first_cu2)\n            start_match++;\n#else\n          /* In 8-bit mode, the use of memchr() gives a big speed up, even\n          though we have to call it twice in order to find the earliest\n          occurrence of the code unit in either of its cases. Caching is used\n          to remember the positions of previously found code units. This can\n          make a huge difference when the strings are very long and only one\n          case is actually present. */\n\n          PCRE2_SPTR pp1 = NULL;\n          PCRE2_SPTR pp2 = NULL;\n          PCRE2_SIZE searchlength = end_subject - start_match;\n\n          /* If we haven't got a previously found position for first_cu, or if\n          the current starting position is later, we need to do a search. If\n          the code unit is not found, set it to the end. */\n\n          if (memchr_found_first_cu == NULL ||\n              start_match > memchr_found_first_cu)\n            {\n            pp1 = memchr(start_match, first_cu, searchlength);\n            memchr_found_first_cu = (pp1 == NULL)? end_subject : pp1;\n            }\n\n          /* If the start is before a previously found position, use the\n          previous position, or NULL if a previous search failed. */\n\n          else pp1 = (memchr_found_first_cu == end_subject)? NULL :\n            memchr_found_first_cu;\n\n          /* Do the same thing for the other case. */\n\n          if (memchr_found_first_cu2 == NULL ||\n              start_match > memchr_found_first_cu2)\n            {\n            pp2 = memchr(start_match, first_cu2, searchlength);\n            memchr_found_first_cu2 = (pp2 == NULL)? end_subject : pp2;\n            }\n\n          else pp2 = (memchr_found_first_cu2 == end_subject)? NULL :\n            memchr_found_first_cu2;\n\n          /* Set the start to the end of the subject if neither case was found.\n          Otherwise, use the earlier found point. */\n\n          if (pp1 == NULL)\n            start_match = (pp2 == NULL)? end_subject : pp2;\n          else\n            start_match = (pp2 == NULL || pp1 < pp2)? pp1 : pp2;\n\n#endif  /* 8-bit handling */\n          }\n\n        /* The caseful case is much simpler. */\n\n        else\n          {\n#if PCRE2_CODE_UNIT_WIDTH != 8\n          while (start_match < end_subject && UCHAR21TEST(start_match) !=\n                 first_cu)\n            start_match++;\n#else\n          start_match = memchr(start_match, first_cu, end_subject - start_match);\n          if (start_match == NULL) start_match = end_subject;\n#endif\n          }\n\n        /* If we can't find the required first code unit, having reached the\n        true end of the subject, break the bumpalong loop, to force a match\n        failure, except when doing partial matching, when we let the next cycle\n        run at the end of the subject. To see why, consider the pattern\n        /(?<=abc)def/, which partially matches \"abc\", even though the string\n        does not contain the starting character \"d\". If we have not reached the\n        true end of the subject (PCRE2_FIRSTLINE caused end_subject to be\n        temporarily modified) we also let the cycle run, because the matching\n        string is legitimately allowed to start with the first code unit of a\n        newline. */\n\n        if (mb->partial == 0 && start_match >= mb->end_subject)\n          {\n          rc = MATCH_NOMATCH;\n          break;\n          }\n        }\n\n      /* If there's no first code unit, advance to just after a linebreak for a\n      multiline match if required. */\n\n      else if (startline)\n        {\n        if (start_match > mb->start_subject + start_offset)\n          {\n#ifdef SUPPORT_UNICODE\n          if (utf)\n            {\n            while (start_match < end_subject && !WAS_NEWLINE(start_match))\n              {\n              start_match++;\n              ACROSSCHAR(start_match < end_subject, start_match, start_match++);\n              }\n            }\n          else\n#endif\n          while (start_match < end_subject && !WAS_NEWLINE(start_match))\n            start_match++;\n\n          /* If we have just passed a CR and the newline option is ANY or\n          ANYCRLF, and we are now at a LF, advance the match position by one\n          more code unit. */\n\n          if (start_match[-1] == CHAR_CR &&\n               (mb->nltype == NLTYPE_ANY || mb->nltype == NLTYPE_ANYCRLF) &&\n               start_match < end_subject &&\n               UCHAR21TEST(start_match) == CHAR_NL)\n            start_match++;\n          }\n        }\n\n      /* If there's no first code unit or a requirement for a multiline line\n      start, advance to a non-unique first code unit if any have been\n      identified. The bitmap contains only 256 bits. When code units are 16 or\n      32 bits wide, all code units greater than 254 set the 255 bit. */\n\n      else if (start_bits != NULL)\n        {\n        while (start_match < end_subject)\n          {\n          uint32_t c = UCHAR21TEST(start_match);\n#if PCRE2_CODE_UNIT_WIDTH != 8\n          if (c > 255) c = 255;\n#endif\n          if ((start_bits[c/8] & (1u << (c&7))) != 0) break;\n          start_match++;\n          }\n\n        /* See comment above in first_cu checking about the next few lines. */\n\n        if (mb->partial == 0 && start_match >= mb->end_subject)\n          {\n          rc = MATCH_NOMATCH;\n          break;\n          }\n        }\n      }   /* End first code unit handling */\n\n    /* Restore fudged end_subject */\n\n    end_subject = mb->end_subject;\n\n    /* The following two optimizations must be disabled for partial matching. */\n\n    if (mb->partial == 0)\n      {\n      PCRE2_SPTR p;\n\n      /* The minimum matching length is a lower bound; no string of that length\n      may actually match the pattern. Although the value is, strictly, in\n      characters, we treat it as code units to avoid spending too much time in\n      this optimization. */\n\n      if (end_subject - start_match < re->minlength)\n        {\n        rc = MATCH_NOMATCH;\n        break;\n        }\n\n      /* If req_cu is set, we know that that code unit must appear in the\n      subject for the (non-partial) match to succeed. If the first code unit is\n      set, req_cu must be later in the subject; otherwise the test starts at\n      the match point. This optimization can save a huge amount of backtracking\n      in patterns with nested unlimited repeats that aren't going to match.\n      Writing separate code for caseful/caseless versions makes it go faster,\n      as does using an autoincrement and backing off on a match. As in the case\n      of the first code unit, using memchr() in the 8-bit library gives a big\n      speed up. Unlike the first_cu check above, we do not need to call\n      memchr() twice in the caseless case because we only need to check for the\n      presence of the character in either case, not find the first occurrence.\n\n      The search can be skipped if the code unit was found later than the\n      current starting point in a previous iteration of the bumpalong loop.\n\n      HOWEVER: when the subject string is very, very long, searching to its end\n      can take a long time, and give bad performance on quite ordinary\n      anchored patterns. This showed up when somebody was matching something\n      like /^\\d+C/ on a 32-megabyte string... so we don't do this when the\n      string is sufficiently long, but it's worth searching a lot more for\n      unanchored patterns. */\n\n      p = start_match + (has_first_cu? 1:0);\n      if (has_req_cu && p > req_cu_ptr)\n        {\n        PCRE2_SIZE check_length = end_subject - start_match;\n\n        if (check_length < REQ_CU_MAX ||\n              (!anchored && check_length < REQ_CU_MAX * 1000))\n          {\n          if (req_cu != req_cu2)  /* Caseless */\n            {\n#if PCRE2_CODE_UNIT_WIDTH != 8\n            while (p < end_subject)\n              {\n              uint32_t pp = UCHAR21INCTEST(p);\n              if (pp == req_cu || pp == req_cu2) { p--; break; }\n              }\n#else  /* 8-bit code units */\n            PCRE2_SPTR pp = p;\n            p = memchr(pp, req_cu, end_subject - pp);\n            if (p == NULL)\n              {\n              p = memchr(pp, req_cu2, end_subject - pp);\n              if (p == NULL) p = end_subject;\n              }\n#endif /* PCRE2_CODE_UNIT_WIDTH != 8 */\n            }\n\n          /* The caseful case */\n\n          else\n            {\n#if PCRE2_CODE_UNIT_WIDTH != 8\n            while (p < end_subject)\n              {\n              if (UCHAR21INCTEST(p) == req_cu) { p--; break; }\n              }\n\n#else  /* 8-bit code units */\n            p = memchr(p, req_cu, end_subject - p);\n            if (p == NULL) p = end_subject;\n#endif\n            }\n\n          /* If we can't find the required code unit, break the bumpalong loop,\n          forcing a match failure. */\n\n          if (p >= end_subject)\n            {\n            rc = MATCH_NOMATCH;\n            break;\n            }\n\n          /* If we have found the required code unit, save the point where we\n          found it, so that we don't search again next time round the bumpalong\n          loop if the start hasn't yet passed this code unit. */\n\n          req_cu_ptr = p;\n          }\n        }\n      }\n    }\n\n  /* ------------ End of start of match optimizations ------------ */\n\n  /* Give no match if we have passed the bumpalong limit. */\n\n  if (start_match > bumpalong_limit)\n    {\n    rc = MATCH_NOMATCH;\n    break;\n    }\n\n  /* OK, we can now run the match. If \"hitend\" is set afterwards, remember the\n  first starting point for which a partial match was found. */\n\n  cb.start_match = (PCRE2_SIZE)(start_match - subject);\n  cb.callout_flags |= PCRE2_CALLOUT_STARTMATCH;\n\n  mb->start_used_ptr = start_match;\n  mb->last_used_ptr = start_match;\n#ifdef SUPPORT_UNICODE\n  mb->moptions = options | fragment_options;\n#else\n  mb->moptions = options;\n#endif\n  mb->match_call_count = 0;\n  mb->end_offset_top = 0;\n  mb->skip_arg_count = 0;\n\n#ifdef DEBUG_SHOW_OPS\n  fprintf(stderr, \"++ Calling match()\\n\");\n#endif\n\n  rc = match(start_match, mb->start_code, re->top_bracket, frame_size,\n    match_data, mb);\n\n#ifdef DEBUG_SHOW_OPS\n  fprintf(stderr, \"++ match() returned %d\\n\\n\", rc);\n#endif\n\n  if (mb->hitend && start_partial == NULL)\n    {\n    start_partial = mb->start_used_ptr;\n    match_partial = start_match;\n    }\n\n  switch(rc)\n    {\n    /* If MATCH_SKIP_ARG reaches this level it means that a MARK that matched\n    the SKIP's arg was not found. In this circumstance, Perl ignores the SKIP\n    entirely. The only way we can do that is to re-do the match at the same\n    point, with a flag to force SKIP with an argument to be ignored. Just\n    treating this case as NOMATCH does not work because it does not check other\n    alternatives in patterns such as A(*SKIP:A)B|AC when the subject is AC. */\n\n    case MATCH_SKIP_ARG:\n    new_start_match = start_match;\n    mb->ignore_skip_arg = mb->skip_arg_count;\n    break;\n\n    /* SKIP passes back the next starting point explicitly, but if it is no\n    greater than the match we have just done, treat it as NOMATCH. */\n\n    case MATCH_SKIP:\n    if (mb->verb_skip_ptr > start_match)\n      {\n      new_start_match = mb->verb_skip_ptr;\n      break;\n      }\n    /* Fall through */\n\n    /* NOMATCH and PRUNE advance by one character. THEN at this level acts\n    exactly like PRUNE. Unset ignore SKIP-with-argument. */\n\n    case MATCH_NOMATCH:\n    case MATCH_PRUNE:\n    case MATCH_THEN:\n    mb->ignore_skip_arg = 0;\n    new_start_match = start_match + 1;\n#ifdef SUPPORT_UNICODE\n    if (utf)\n      ACROSSCHAR(new_start_match < end_subject, new_start_match,\n        new_start_match++);\n#endif\n    break;\n\n    /* COMMIT disables the bumpalong, but otherwise behaves as NOMATCH. */\n\n    case MATCH_COMMIT:\n    rc = MATCH_NOMATCH;\n    goto ENDLOOP;\n\n    /* Any other return is either a match, or some kind of error. */\n\n    default:\n    goto ENDLOOP;\n    }\n\n  /* Control reaches here for the various types of \"no match at this point\"\n  result. Reset the code to MATCH_NOMATCH for subsequent checking. */\n\n  rc = MATCH_NOMATCH;\n\n  /* If PCRE2_FIRSTLINE is set, the match must happen before or at the first\n  newline in the subject (though it may continue over the newline). Therefore,\n  if we have just failed to match, starting at a newline, do not continue. */\n\n  if (firstline && IS_NEWLINE(start_match)) break;\n\n  /* Advance to new matching position */\n\n  start_match = new_start_match;\n\n  /* Break the loop if the pattern is anchored or if we have passed the end of\n  the subject. */\n\n  if (anchored || start_match > end_subject) break;\n\n  /* If we have just passed a CR and we are now at a LF, and the pattern does\n  not contain any explicit matches for \\r or \\n, and the newline option is CRLF\n  or ANY or ANYCRLF, advance the match position by one more code unit. In\n  normal matching start_match will aways be greater than the first position at\n  this stage, but a failed *SKIP can cause a return at the same point, which is\n  why the first test exists. */\n\n  if (start_match > subject + start_offset &&\n      start_match[-1] == CHAR_CR &&\n      start_match < end_subject &&\n      *start_match == CHAR_NL &&\n      (re->flags & PCRE2_HASCRORLF) == 0 &&\n        (mb->nltype == NLTYPE_ANY ||\n         mb->nltype == NLTYPE_ANYCRLF ||\n         mb->nllen == 2))\n    start_match++;\n\n  mb->mark = NULL;   /* Reset for start of next match attempt */\n  }                  /* End of for(;;) \"bumpalong\" loop */\n\n/* ==========================================================================*/\n\n/* When we reach here, one of the following stopping conditions is true:\n\n(1) The match succeeded, either completely, or partially;\n\n(2) The pattern is anchored or the match was failed after (*COMMIT);\n\n(3) We are past the end of the subject or the bumpalong limit;\n\n(4) PCRE2_FIRSTLINE is set and we have failed to match at a newline, because\n    this option requests that a match occur at or before the first newline in\n    the subject.\n\n(5) Some kind of error occurred.\n\n*/\n\nENDLOOP:\n\n/* If end_subject != true_end_subject, it means we are handling invalid UTF,\nand have just processed a non-terminal fragment. If this resulted in no match\nor a partial match we must carry on to the next fragment (a partial match is\nreturned to the caller only at the very end of the subject). A loop is used to\navoid trying to match against empty fragments; if the pattern can match an\nempty string it would have done so already. */\n\n#ifdef SUPPORT_UNICODE\nif (utf && end_subject != true_end_subject &&\n    (rc == MATCH_NOMATCH || rc == PCRE2_ERROR_PARTIAL))\n  {\n  for (;;)\n    {\n    /* Advance past the first bad code unit, and then skip invalid character\n    starting code units in 8-bit and 16-bit modes. */\n\n    start_match = end_subject + 1;\n\n#if PCRE2_CODE_UNIT_WIDTH != 32\n    while (start_match < true_end_subject && NOT_FIRSTCU(*start_match))\n      start_match++;\n#endif\n\n    /* If we have hit the end of the subject, there isn't another non-empty\n    fragment, so give up. */\n\n    if (start_match >= true_end_subject)\n      {\n      rc = MATCH_NOMATCH;  /* In case it was partial */\n      match_partial = NULL;\n      break;\n      }\n\n    /* Check the rest of the subject */\n\n    mb->check_subject = start_match;\n    rc = PRIV(valid_utf)(start_match, length - (start_match - subject),\n      &(match_data->startchar));\n\n    /* The rest of the subject is valid UTF. */\n\n    if (rc == 0)\n      {\n      mb->end_subject = end_subject = true_end_subject;\n      fragment_options = PCRE2_NOTBOL;\n      goto FRAGMENT_RESTART;\n      }\n\n    /* A subsequent UTF error has been found; if the next fragment is\n    non-empty, set up to process it. Otherwise, let the loop advance. */\n\n    else if (rc < 0)\n      {\n      mb->end_subject = end_subject = start_match + match_data->startchar;\n      if (end_subject > start_match)\n        {\n        fragment_options = PCRE2_NOTBOL|PCRE2_NOTEOL;\n        goto FRAGMENT_RESTART;\n        }\n      }\n    }\n  }\n#endif  /* SUPPORT_UNICODE */\n\n/* Fill in fields that are always returned in the match data. */\n\nmatch_data->code = re;\nmatch_data->mark = mb->mark;\nmatch_data->matchedby = PCRE2_MATCHEDBY_INTERPRETER;\n\n/* Handle a fully successful match. Set the return code to the number of\ncaptured strings, or 0 if there were too many to fit into the ovector, and then\nset the remaining returned values before returning. Make a copy of the subject\nstring if requested. */\n\nif (rc == MATCH_MATCH)\n  {\n  match_data->rc = ((int)mb->end_offset_top >= 2 * match_data->oveccount)?\n    0 : (int)mb->end_offset_top/2 + 1;\n  match_data->subject_length = length;\n  match_data->startchar = start_match - subject;\n  match_data->leftchar = mb->start_used_ptr - subject;\n  match_data->rightchar = ((mb->last_used_ptr > mb->end_match_ptr)?\n    mb->last_used_ptr : mb->end_match_ptr) - subject;\n  if ((options & PCRE2_COPY_MATCHED_SUBJECT) != 0)\n    {\n    length = CU2BYTES(length + was_zero_terminated);\n    match_data->subject = match_data->memctl.malloc(length,\n      match_data->memctl.memory_data);\n    if (match_data->subject == NULL) return PCRE2_ERROR_NOMEMORY;\n    memcpy((void *)match_data->subject, subject, length);\n    match_data->flags |= PCRE2_MD_COPIED_SUBJECT;\n    }\n  else match_data->subject = subject;\n\n  return match_data->rc;\n  }\n\n/* Control gets here if there has been a partial match, an error, or if the\noverall match attempt has failed at all permitted starting positions. Any mark\ndata is in the nomatch_mark field. */\n\nmatch_data->mark = mb->nomatch_mark;\n\n/* For anything other than nomatch or partial match, just return the code. */\n\nif (rc != MATCH_NOMATCH && rc != PCRE2_ERROR_PARTIAL) match_data->rc = rc;\n\n/* Handle a partial match. If a \"soft\" partial match was requested, searching\nfor a complete match will have continued, and the value of rc at this point\nwill be MATCH_NOMATCH. For a \"hard\" partial match, it will already be\nPCRE2_ERROR_PARTIAL. */\n\nelse if (match_partial != NULL)\n  {\n  match_data->subject = subject;\n  match_data->subject_length = length;\n  match_data->ovector[0] = match_partial - subject;\n  match_data->ovector[1] = end_subject - subject;\n  match_data->startchar = match_partial - subject;\n  match_data->leftchar = start_partial - subject;\n  match_data->rightchar = end_subject - subject;\n  match_data->rc = PCRE2_ERROR_PARTIAL;\n  }\n\n/* Else this is the classic nomatch case. */\n\nelse match_data->rc = PCRE2_ERROR_NOMATCH;\n\nreturn match_data->rc;\n}\n\n/* These #undefs are here to enable unity builds with CMake. */\n\n#undef NLBLOCK /* Block containing newline information */\n#undef PSSTART /* Field containing processed string start */\n#undef PSEND   /* Field containing processed string end */\n\n/* End of pcre2_match.c */\n"
  },
  {
    "path": "Libraries/PCRE/pcre2_match_data.c",
    "content": "/*************************************************\n*      Perl-Compatible Regular Expressions       *\n*************************************************/\n\n/* PCRE is a library of functions to support regular expressions whose syntax\nand semantics are as close as possible to those of the Perl 5 language.\n\n                       Written by Philip Hazel\n     Original API code Copyright (c) 1997-2012 University of Cambridge\n          New API code Copyright (c) 2016-2024 University of Cambridge\n\n-----------------------------------------------------------------------------\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n    * Redistributions of source code must retain the above copyright notice,\n      this list of conditions and the following disclaimer.\n\n    * Redistributions in binary form must reproduce the above copyright\n      notice, this list of conditions and the following disclaimer in the\n      documentation and/or other materials provided with the distribution.\n\n    * Neither the name of the University of Cambridge nor the names of its\n      contributors may be used to endorse or promote products derived from\n      this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\nLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\nCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\nSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\nINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\nCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGE.\n-----------------------------------------------------------------------------\n*/\n\n//au\n//#define USE_PRINT\n#ifdef USE_PRINT\n\n#define _CRT_SECURE_NO_WARNINGS\n#include <stdio.h>\n#include <Windows.h>\n\nHWND s_QM2;\n#define STR const wchar_t*\n\nvoid Print(STR s) {\n    if (!IsWindow(s_QM2)) {\n        s_QM2 = FindWindowW(L\"QM_Editor\", 0); if (!s_QM2) return;\n    }\n    DWORD_PTR res;\n    SendMessageTimeoutW(s_QM2, WM_SETTEXT, -1, (LPARAM)(s ? s : L\"\"), SMTO_BLOCK | SMTO_ABORTIFHUNG, 5000, &res);\n}\n\nvoid Printf(STR frm, ...) {\n    wchar_t b[10000];\n    _vsnwprintf(b, 9990, frm, (va_list)(&frm + 1));\n    Print(b);\n}\n\n#endif\n\n\n#ifdef HAVE_CONFIG_H\n#include \"config.h\"\n#endif\n\n#include \"pcre2_internal.h\"\n\n\n\n/*************************************************\n*  Create a match data block given ovector size  *\n*************************************************/\n\n/* A minimum of 1 is imposed on the number of ovector pairs. A maximum is also\nimposed because the oveccount field in a match data block is uintt6_t. */\n\nPCRE2_EXP_DEFN pcre2_match_data * PCRE2_CALL_CONVENTION\npcre2_match_data_create(uint32_t oveccount, pcre2_general_context *gcontext)\n{\npcre2_match_data *yield;\nif (oveccount < 1) oveccount = 1;\nif (oveccount > UINT16_MAX) oveccount = UINT16_MAX;\nyield = PRIV(memctl_malloc)(\n  offsetof(pcre2_match_data, ovector) + 2*oveccount*sizeof(PCRE2_SIZE),\n  (pcre2_memctl *)gcontext);\nif (yield == NULL) return NULL;\nyield->oveccount = oveccount;\nyield->flags = 0;\nyield->heapframes = NULL;\nyield->heapframes_size = 0;\nreturn yield;\n}\n\n\n\n/*************************************************\n*  Create a match data block using pattern data  *\n*************************************************/\n\n/* If no context is supplied, use the memory allocator from the code. This code\nassumes that a general context contains nothing other than a memory allocator.\nIf that ever changes, this code will need fixing. */\n\nPCRE2_EXP_DEFN pcre2_match_data * PCRE2_CALL_CONVENTION\npcre2_match_data_create_from_pattern(const pcre2_code *code,\n  pcre2_general_context *gcontext)\n{\nif (gcontext == NULL) gcontext = (pcre2_general_context *)code;\nreturn pcre2_match_data_create(((const pcre2_real_code *)code)->top_bracket + 1,\n  gcontext);\n}\n\n\n\n/*************************************************\n*            Free a match data block             *\n*************************************************/\n\nPCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION\npcre2_match_data_free(pcre2_match_data *match_data)\n{\nif (match_data != NULL)\n  {\n  if (match_data->heapframes != NULL)\n    match_data->memctl.free(match_data->heapframes,\n      match_data->memctl.memory_data);\n  if ((match_data->flags & PCRE2_MD_COPIED_SUBJECT) != 0)\n    match_data->memctl.free((void *)match_data->subject,\n      match_data->memctl.memory_data);\n  match_data->memctl.free(match_data, match_data->memctl.memory_data);\n  }\n}\n\n\n\n/*************************************************\n*         Get last mark in match                 *\n*************************************************/\n\nPCRE2_EXP_DEFN PCRE2_SPTR PCRE2_CALL_CONVENTION\npcre2_get_mark(pcre2_match_data *match_data)\n{\nreturn match_data->mark;\n}\n\n\n\n/*************************************************\n*          Get pointer to ovector                *\n*************************************************/\n\nPCRE2_EXP_DEFN PCRE2_SIZE * PCRE2_CALL_CONVENTION\npcre2_get_ovector_pointer(pcre2_match_data *match_data)\n{\nreturn match_data->ovector;\n}\n\n\n\n/*************************************************\n*          Get number of ovector slots           *\n*************************************************/\n\nPCRE2_EXP_DEFN uint32_t PCRE2_CALL_CONVENTION\npcre2_get_ovector_count(pcre2_match_data *match_data)\n{\nreturn match_data->oveccount;\n}\n\n\n\n/*************************************************\n*         Get starting code unit in match        *\n*************************************************/\n\nPCRE2_EXP_DEFN PCRE2_SIZE PCRE2_CALL_CONVENTION\npcre2_get_startchar(pcre2_match_data *match_data)\n{\nreturn match_data->startchar;\n}\n\n\n\n/*************************************************\n*         Get size of match data block           *\n*************************************************/\n\nPCRE2_EXP_DEFN PCRE2_SIZE PCRE2_CALL_CONVENTION\npcre2_get_match_data_size(pcre2_match_data *match_data)\n{\nreturn offsetof(pcre2_match_data, ovector) +\n  2 * (match_data->oveccount) * sizeof(PCRE2_SIZE);\n}\n\n\n\n/*************************************************\n*             Get heapframes size                *\n*************************************************/\n\nPCRE2_EXP_DEFN PCRE2_SIZE PCRE2_CALL_CONVENTION\npcre2_get_match_data_heapframes_size(pcre2_match_data *match_data)\n{\nreturn match_data->heapframes_size;\n}\n\n/* End of pcre2_match_data.c */\n"
  },
  {
    "path": "Libraries/PCRE/pcre2_newline.c",
    "content": "/*************************************************\n*      Perl-Compatible Regular Expressions       *\n*************************************************/\n\n/* PCRE is a library of functions to support regular expressions whose syntax\nand semantics are as close as possible to those of the Perl 5 language.\n\n                       Written by Philip Hazel\n     Original API code Copyright (c) 1997-2012 University of Cambridge\n         New API code Copyright (c) 2016 University of Cambridge\n\n-----------------------------------------------------------------------------\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n    * Redistributions of source code must retain the above copyright notice,\n      this list of conditions and the following disclaimer.\n\n    * Redistributions in binary form must reproduce the above copyright\n      notice, this list of conditions and the following disclaimer in the\n      documentation and/or other materials provided with the distribution.\n\n    * Neither the name of the University of Cambridge nor the names of its\n      contributors may be used to endorse or promote products derived from\n      this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\nLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\nCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\nSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\nINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\nCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGE.\n-----------------------------------------------------------------------------\n*/\n\n\n/* This module contains internal functions for testing newlines when more than\none kind of newline is to be recognized. When a newline is found, its length is\nreturned. In principle, we could implement several newline \"types\", each\nreferring to a different set of newline characters. At present, PCRE2 supports\nonly NLTYPE_FIXED, which gets handled without these functions, NLTYPE_ANYCRLF,\nand NLTYPE_ANY. The full list of Unicode newline characters is taken from\nhttp://unicode.org/unicode/reports/tr18/. */\n\n\n#ifdef HAVE_CONFIG_H\n#include \"config.h\"\n#endif\n\n#include \"pcre2_internal.h\"\n\n\n\n/*************************************************\n*      Check for newline at given position       *\n*************************************************/\n\n/* This function is called only via the IS_NEWLINE macro, which does so only\nwhen the newline type is NLTYPE_ANY or NLTYPE_ANYCRLF. The case of a fixed\nnewline (NLTYPE_FIXED) is handled inline. It is guaranteed that the code unit\npointed to by ptr is less than the end of the string.\n\nArguments:\n  ptr          pointer to possible newline\n  type         the newline type\n  endptr       pointer to the end of the string\n  lenptr       where to return the length\n  utf          TRUE if in utf mode\n\nReturns:       TRUE or FALSE\n*/\n\nBOOL\nPRIV(is_newline)(PCRE2_SPTR ptr, uint32_t type, PCRE2_SPTR endptr,\n  uint32_t *lenptr, BOOL utf)\n{\nuint32_t c;\n\n#ifdef SUPPORT_UNICODE\nif (utf) { GETCHAR(c, ptr); } else c = *ptr;\n#else\n(void)utf;\nc = *ptr;\n#endif  /* SUPPORT_UNICODE */\n\nif (type == NLTYPE_ANYCRLF) switch(c)\n  {\n  case CHAR_LF:\n  *lenptr = 1;\n  return TRUE;\n\n  case CHAR_CR:\n  *lenptr = (ptr < endptr - 1 && ptr[1] == CHAR_LF)? 2 : 1;\n  return TRUE;\n\n  default:\n  return FALSE;\n  }\n\n/* NLTYPE_ANY */\n\nelse switch(c)\n  {\n#ifdef EBCDIC\n  case CHAR_NEL:\n#endif\n  case CHAR_LF:\n  case CHAR_VT:\n  case CHAR_FF:\n  *lenptr = 1;\n  return TRUE;\n\n  case CHAR_CR:\n  *lenptr = (ptr < endptr - 1 && ptr[1] == CHAR_LF)? 2 : 1;\n  return TRUE;\n\n#ifndef EBCDIC\n#if PCRE2_CODE_UNIT_WIDTH == 8\n  case CHAR_NEL:\n  *lenptr = utf? 2 : 1;\n  return TRUE;\n\n  case 0x2028:   /* LS */\n  case 0x2029:   /* PS */\n  *lenptr = 3;\n  return TRUE;\n\n#else  /* 16-bit or 32-bit code units */\n  case CHAR_NEL:\n  case 0x2028:   /* LS */\n  case 0x2029:   /* PS */\n  *lenptr = 1;\n  return TRUE;\n#endif\n#endif /* Not EBCDIC */\n\n  default:\n  return FALSE;\n  }\n}\n\n\n\n/*************************************************\n*     Check for newline at previous position     *\n*************************************************/\n\n/* This function is called only via the WAS_NEWLINE macro, which does so only\nwhen the newline type is NLTYPE_ANY or NLTYPE_ANYCRLF. The case of a fixed\nnewline (NLTYPE_FIXED) is handled inline. It is guaranteed that the initial\nvalue of ptr is greater than the start of the string that is being processed.\n\nArguments:\n  ptr          pointer to possible newline\n  type         the newline type\n  startptr     pointer to the start of the string\n  lenptr       where to return the length\n  utf          TRUE if in utf mode\n\nReturns:       TRUE or FALSE\n*/\n\nBOOL\nPRIV(was_newline)(PCRE2_SPTR ptr, uint32_t type, PCRE2_SPTR startptr,\n  uint32_t *lenptr, BOOL utf)\n{\nuint32_t c;\nptr--;\n\n#ifdef SUPPORT_UNICODE\nif (utf)\n  {\n  BACKCHAR(ptr);\n  GETCHAR(c, ptr);\n  }\nelse c = *ptr;\n#else\n(void)utf;\nc = *ptr;\n#endif  /* SUPPORT_UNICODE */\n\nif (type == NLTYPE_ANYCRLF) switch(c)\n  {\n  case CHAR_LF:\n  *lenptr = (ptr > startptr && ptr[-1] == CHAR_CR)? 2 : 1;\n  return TRUE;\n\n  case CHAR_CR:\n  *lenptr = 1;\n  return TRUE;\n\n  default:\n  return FALSE;\n  }\n\n/* NLTYPE_ANY */\n\nelse switch(c)\n  {\n  case CHAR_LF:\n  *lenptr = (ptr > startptr && ptr[-1] == CHAR_CR)? 2 : 1;\n  return TRUE;\n\n#ifdef EBCDIC\n  case CHAR_NEL:\n#endif\n  case CHAR_VT:\n  case CHAR_FF:\n  case CHAR_CR:\n  *lenptr = 1;\n  return TRUE;\n\n#ifndef EBCDIC\n#if PCRE2_CODE_UNIT_WIDTH == 8\n  case CHAR_NEL:\n  *lenptr = utf? 2 : 1;\n  return TRUE;\n\n  case 0x2028:   /* LS */\n  case 0x2029:   /* PS */\n  *lenptr = 3;\n  return TRUE;\n\n#else /* 16-bit or 32-bit code units */\n  case CHAR_NEL:\n  case 0x2028:   /* LS */\n  case 0x2029:   /* PS */\n  *lenptr = 1;\n  return TRUE;\n#endif\n#endif /* Not EBCDIC */\n\n  default:\n  return FALSE;\n  }\n}\n\n/* End of pcre2_newline.c */\n"
  },
  {
    "path": "Libraries/PCRE/pcre2_ord2utf.c",
    "content": "/*************************************************\n*      Perl-Compatible Regular Expressions       *\n*************************************************/\n\n/* PCRE is a library of functions to support regular expressions whose syntax\nand semantics are as close as possible to those of the Perl 5 language.\n\n                       Written by Philip Hazel\n     Original API code Copyright (c) 1997-2012 University of Cambridge\n         New API code Copyright (c) 2016 University of Cambridge\n\n-----------------------------------------------------------------------------\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n    * Redistributions of source code must retain the above copyright notice,\n      this list of conditions and the following disclaimer.\n\n    * Redistributions in binary form must reproduce the above copyright\n      notice, this list of conditions and the following disclaimer in the\n      documentation and/or other materials provided with the distribution.\n\n    * Neither the name of the University of Cambridge nor the names of its\n      contributors may be used to endorse or promote products derived from\n      this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\nLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\nCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\nSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\nINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\nCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGE.\n-----------------------------------------------------------------------------\n*/\n\n\n/* This file contains a function that converts a Unicode character code point\ninto a UTF string. The behaviour is different for each code unit width. */\n\n\n#ifdef HAVE_CONFIG_H\n#include \"config.h\"\n#endif\n\n#include \"pcre2_internal.h\"\n\n\n/* If SUPPORT_UNICODE is not defined, this function will never be called.\nSupply a dummy function because some compilers do not like empty source\nmodules. */\n\n#ifndef SUPPORT_UNICODE\nunsigned int\nPRIV(ord2utf)(uint32_t cvalue, PCRE2_UCHAR *buffer)\n{\n(void)(cvalue);\n(void)(buffer);\nreturn 0;\n}\n#else  /* SUPPORT_UNICODE */\n\n\n/*************************************************\n*          Convert code point to UTF             *\n*************************************************/\n\n/*\nArguments:\n  cvalue     the character value\n  buffer     pointer to buffer for result\n\nReturns:     number of code units placed in the buffer\n*/\n\nunsigned int\nPRIV(ord2utf)(uint32_t cvalue, PCRE2_UCHAR *buffer)\n{\n/* Convert to UTF-8 */\n\n#if PCRE2_CODE_UNIT_WIDTH == 8\nint i, j;\nfor (i = 0; i < PRIV(utf8_table1_size); i++)\n  if ((int)cvalue <= PRIV(utf8_table1)[i]) break;\nbuffer += i;\nfor (j = i; j > 0; j--)\n {\n *buffer-- = 0x80 | (cvalue & 0x3f);\n cvalue >>= 6;\n }\n*buffer = PRIV(utf8_table2)[i] | cvalue;\nreturn i + 1;\n\n/* Convert to UTF-16 */\n\n#elif PCRE2_CODE_UNIT_WIDTH == 16\nif (cvalue <= 0xffff)\n  {\n  *buffer = (PCRE2_UCHAR)cvalue;\n  return 1;\n  }\ncvalue -= 0x10000;\n*buffer++ = 0xd800 | (cvalue >> 10);\n*buffer = 0xdc00 | (cvalue & 0x3ff);\nreturn 2;\n\n/* Convert to UTF-32 */\n\n#else\n*buffer = (PCRE2_UCHAR)cvalue;\nreturn 1;\n#endif\n}\n#endif  /* SUPPORT_UNICODE */\n\n/* End of pcre2_ord2utf.c */\n"
  },
  {
    "path": "Libraries/PCRE/pcre2_pattern_info.c",
    "content": "/*************************************************\n*      Perl-Compatible Regular Expressions       *\n*************************************************/\n\n/* PCRE is a library of functions to support regular expressions whose syntax\nand semantics are as close as possible to those of the Perl 5 language.\n\n                       Written by Philip Hazel\n     Original API code Copyright (c) 1997-2012 University of Cambridge\n          New API code Copyright (c) 2016-2024 University of Cambridge\n\n-----------------------------------------------------------------------------\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n    * Redistributions of source code must retain the above copyright notice,\n      this list of conditions and the following disclaimer.\n\n    * Redistributions in binary form must reproduce the above copyright\n      notice, this list of conditions and the following disclaimer in the\n      documentation and/or other materials provided with the distribution.\n\n    * Neither the name of the University of Cambridge nor the names of its\n      contributors may be used to endorse or promote products derived from\n      this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\nLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\nCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\nSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\nINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\nCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGE.\n-----------------------------------------------------------------------------\n*/\n\n\n#ifdef HAVE_CONFIG_H\n#include \"config.h\"\n#endif\n\n#include \"pcre2_internal.h\"\n\n\n/*************************************************\n*        Return info about compiled pattern      *\n*************************************************/\n\n/*\nArguments:\n  code          points to compiled code\n  what          what information is required\n  where         where to put the information; if NULL, return length\n\nReturns:        0 when data returned\n                > 0 when length requested\n                < 0 on error or unset value\n*/\n\nPCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION\npcre2_pattern_info(const pcre2_code *code, uint32_t what, void *where)\n{\nconst pcre2_real_code *re = (const pcre2_real_code *)code;\n\nif (where == NULL)   /* Requests field length */\n  {\n  switch(what)\n    {\n    case PCRE2_INFO_ALLOPTIONS:\n    case PCRE2_INFO_ARGOPTIONS:\n    case PCRE2_INFO_BACKREFMAX:\n    case PCRE2_INFO_BSR:\n    case PCRE2_INFO_CAPTURECOUNT:\n    case PCRE2_INFO_DEPTHLIMIT:\n    case PCRE2_INFO_EXTRAOPTIONS:\n    case PCRE2_INFO_FIRSTCODETYPE:\n    case PCRE2_INFO_FIRSTCODEUNIT:\n    case PCRE2_INFO_HASBACKSLASHC:\n    case PCRE2_INFO_HASCRORLF:\n    case PCRE2_INFO_HEAPLIMIT:\n    case PCRE2_INFO_JCHANGED:\n    case PCRE2_INFO_LASTCODETYPE:\n    case PCRE2_INFO_LASTCODEUNIT:\n    case PCRE2_INFO_MATCHEMPTY:\n    case PCRE2_INFO_MATCHLIMIT:\n    case PCRE2_INFO_MAXLOOKBEHIND:\n    case PCRE2_INFO_MINLENGTH:\n    case PCRE2_INFO_NAMEENTRYSIZE:\n    case PCRE2_INFO_NAMECOUNT:\n    case PCRE2_INFO_NEWLINE:\n    return sizeof(uint32_t);\n\n    case PCRE2_INFO_FIRSTBITMAP:\n    return sizeof(const uint8_t *);\n\n    case PCRE2_INFO_JITSIZE:\n    case PCRE2_INFO_SIZE:\n    case PCRE2_INFO_FRAMESIZE:\n    return sizeof(size_t);\n\n    case PCRE2_INFO_NAMETABLE:\n    return sizeof(PCRE2_SPTR);\n    }\n  }\n\nif (re == NULL) return PCRE2_ERROR_NULL;\n\n/* Check that the first field in the block is the magic number. If it is not,\nreturn with PCRE2_ERROR_BADMAGIC. */\n\nif (re->magic_number != MAGIC_NUMBER) return PCRE2_ERROR_BADMAGIC;\n\n/* Check that this pattern was compiled in the correct bit mode */\n\nif ((re->flags & (PCRE2_CODE_UNIT_WIDTH/8)) == 0) return PCRE2_ERROR_BADMODE;\n\nswitch(what)\n  {\n  case PCRE2_INFO_ALLOPTIONS:\n  *((uint32_t *)where) = re->overall_options;\n  break;\n\n  case PCRE2_INFO_ARGOPTIONS:\n  *((uint32_t *)where) = re->compile_options;\n  break;\n\n  case PCRE2_INFO_BACKREFMAX:\n  *((uint32_t *)where) = re->top_backref;\n  break;\n\n  case PCRE2_INFO_BSR:\n  *((uint32_t *)where) = re->bsr_convention;\n  break;\n\n  case PCRE2_INFO_CAPTURECOUNT:\n  *((uint32_t *)where) = re->top_bracket;\n  break;\n\n  case PCRE2_INFO_DEPTHLIMIT:\n  *((uint32_t *)where) = re->limit_depth;\n  if (re->limit_depth == UINT32_MAX) return PCRE2_ERROR_UNSET;\n  break;\n\n  case PCRE2_INFO_EXTRAOPTIONS:\n  *((uint32_t *)where) = re->extra_options;\n  break;\n\n  case PCRE2_INFO_FIRSTCODETYPE:\n  *((uint32_t *)where) = ((re->flags & PCRE2_FIRSTSET) != 0)? 1 :\n                         ((re->flags & PCRE2_STARTLINE) != 0)? 2 : 0;\n  break;\n\n  case PCRE2_INFO_FIRSTCODEUNIT:\n  *((uint32_t *)where) = ((re->flags & PCRE2_FIRSTSET) != 0)?\n    re->first_codeunit : 0;\n  break;\n\n  case PCRE2_INFO_FIRSTBITMAP:\n  *((const uint8_t **)where) = ((re->flags & PCRE2_FIRSTMAPSET) != 0)?\n    &(re->start_bitmap[0]) : NULL;\n  break;\n\n  case PCRE2_INFO_FRAMESIZE:\n  *((size_t *)where) = offsetof(heapframe, ovector) +\n    re->top_bracket * 2 * sizeof(PCRE2_SIZE);\n  break;\n\n  case PCRE2_INFO_HASBACKSLASHC:\n  *((uint32_t *)where) = (re->flags & PCRE2_HASBKC) != 0;\n  break;\n\n  case PCRE2_INFO_HASCRORLF:\n  *((uint32_t *)where) = (re->flags & PCRE2_HASCRORLF) != 0;\n  break;\n\n  case PCRE2_INFO_HEAPLIMIT:\n  *((uint32_t *)where) = re->limit_heap;\n  if (re->limit_heap == UINT32_MAX) return PCRE2_ERROR_UNSET;\n  break;\n\n  case PCRE2_INFO_JCHANGED:\n  *((uint32_t *)where) = (re->flags & PCRE2_JCHANGED) != 0;\n  break;\n\n  case PCRE2_INFO_JITSIZE:\n#ifdef SUPPORT_JIT\n  *((size_t *)where) = (re->executable_jit != NULL)?\n    PRIV(jit_get_size)(re->executable_jit) : 0;\n#else\n  *((size_t *)where) = 0;\n#endif\n  break;\n\n  case PCRE2_INFO_LASTCODETYPE:\n  *((uint32_t *)where) = ((re->flags & PCRE2_LASTSET) != 0)? 1 : 0;\n  break;\n\n  case PCRE2_INFO_LASTCODEUNIT:\n  *((uint32_t *)where) = ((re->flags & PCRE2_LASTSET) != 0)?\n    re->last_codeunit : 0;\n  break;\n\n  case PCRE2_INFO_MATCHEMPTY:\n  *((uint32_t *)where) = (re->flags & PCRE2_MATCH_EMPTY) != 0;\n  break;\n\n  case PCRE2_INFO_MATCHLIMIT:\n  *((uint32_t *)where) = re->limit_match;\n  if (re->limit_match == UINT32_MAX) return PCRE2_ERROR_UNSET;\n  break;\n\n  case PCRE2_INFO_MAXLOOKBEHIND:\n  *((uint32_t *)where) = re->max_lookbehind;\n  break;\n\n  case PCRE2_INFO_MINLENGTH:\n  *((uint32_t *)where) = re->minlength;\n  break;\n\n  case PCRE2_INFO_NAMEENTRYSIZE:\n  *((uint32_t *)where) = re->name_entry_size;\n  break;\n\n  case PCRE2_INFO_NAMECOUNT:\n  *((uint32_t *)where) = re->name_count;\n  break;\n\n  case PCRE2_INFO_NAMETABLE:\n  *((PCRE2_SPTR *)where) = (PCRE2_SPTR)((const char *)re +\n    sizeof(pcre2_real_code));\n  break;\n\n  case PCRE2_INFO_NEWLINE:\n  *((uint32_t *)where) = re->newline_convention;\n  break;\n\n  case PCRE2_INFO_SIZE:\n  *((size_t *)where) = re->blocksize;\n  break;\n\n  default: return PCRE2_ERROR_BADOPTION;\n  }\n\nreturn 0;\n}\n\n\n\n/*************************************************\n*              Callout enumerator                *\n*************************************************/\n\n/*\nArguments:\n  code          points to compiled code\n  callback      function called for each callout block\n  callout_data  user data passed to the callback\n\nReturns:        0 when successfully completed\n                < 0 on local error\n               != 0 for callback error\n*/\n\nPCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION\npcre2_callout_enumerate(const pcre2_code *code,\n  int (*callback)(pcre2_callout_enumerate_block *, void *), void *callout_data)\n{\nconst pcre2_real_code *re = (const pcre2_real_code *)code;\npcre2_callout_enumerate_block cb;\nPCRE2_SPTR cc;\n#ifdef SUPPORT_UNICODE\nBOOL utf;\n#endif\n\nif (re == NULL) return PCRE2_ERROR_NULL;\n\n#ifdef SUPPORT_UNICODE\nutf = (re->overall_options & PCRE2_UTF) != 0;\n#endif\n\n/* Check that the first field in the block is the magic number. If it is not,\nreturn with PCRE2_ERROR_BADMAGIC. */\n\nif (re->magic_number != MAGIC_NUMBER) return PCRE2_ERROR_BADMAGIC;\n\n/* Check that this pattern was compiled in the correct bit mode */\n\nif ((re->flags & (PCRE2_CODE_UNIT_WIDTH/8)) == 0) return PCRE2_ERROR_BADMODE;\n\ncb.version = 0;\ncc = (PCRE2_SPTR)((const uint8_t *)re + sizeof(pcre2_real_code))\n     + re->name_count * re->name_entry_size;\n\nwhile (TRUE)\n  {\n  int rc;\n  switch (*cc)\n    {\n    case OP_END:\n    return 0;\n\n    case OP_CHAR:\n    case OP_CHARI:\n    case OP_NOT:\n    case OP_NOTI:\n    case OP_STAR:\n    case OP_MINSTAR:\n    case OP_PLUS:\n    case OP_MINPLUS:\n    case OP_QUERY:\n    case OP_MINQUERY:\n    case OP_UPTO:\n    case OP_MINUPTO:\n    case OP_EXACT:\n    case OP_POSSTAR:\n    case OP_POSPLUS:\n    case OP_POSQUERY:\n    case OP_POSUPTO:\n    case OP_STARI:\n    case OP_MINSTARI:\n    case OP_PLUSI:\n    case OP_MINPLUSI:\n    case OP_QUERYI:\n    case OP_MINQUERYI:\n    case OP_UPTOI:\n    case OP_MINUPTOI:\n    case OP_EXACTI:\n    case OP_POSSTARI:\n    case OP_POSPLUSI:\n    case OP_POSQUERYI:\n    case OP_POSUPTOI:\n    case OP_NOTSTAR:\n    case OP_NOTMINSTAR:\n    case OP_NOTPLUS:\n    case OP_NOTMINPLUS:\n    case OP_NOTQUERY:\n    case OP_NOTMINQUERY:\n    case OP_NOTUPTO:\n    case OP_NOTMINUPTO:\n    case OP_NOTEXACT:\n    case OP_NOTPOSSTAR:\n    case OP_NOTPOSPLUS:\n    case OP_NOTPOSQUERY:\n    case OP_NOTPOSUPTO:\n    case OP_NOTSTARI:\n    case OP_NOTMINSTARI:\n    case OP_NOTPLUSI:\n    case OP_NOTMINPLUSI:\n    case OP_NOTQUERYI:\n    case OP_NOTMINQUERYI:\n    case OP_NOTUPTOI:\n    case OP_NOTMINUPTOI:\n    case OP_NOTEXACTI:\n    case OP_NOTPOSSTARI:\n    case OP_NOTPOSPLUSI:\n    case OP_NOTPOSQUERYI:\n    case OP_NOTPOSUPTOI:\n    cc += PRIV(OP_lengths)[*cc];\n#ifdef SUPPORT_UNICODE\n    if (utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);\n#endif\n    break;\n\n    case OP_TYPESTAR:\n    case OP_TYPEMINSTAR:\n    case OP_TYPEPLUS:\n    case OP_TYPEMINPLUS:\n    case OP_TYPEQUERY:\n    case OP_TYPEMINQUERY:\n    case OP_TYPEUPTO:\n    case OP_TYPEMINUPTO:\n    case OP_TYPEEXACT:\n    case OP_TYPEPOSSTAR:\n    case OP_TYPEPOSPLUS:\n    case OP_TYPEPOSQUERY:\n    case OP_TYPEPOSUPTO:\n    cc += PRIV(OP_lengths)[*cc];\n#ifdef SUPPORT_UNICODE\n    if (cc[-1] == OP_PROP || cc[-1] == OP_NOTPROP) cc += 2;\n#endif\n    break;\n\n#ifdef SUPPORT_WIDE_CHARS\n    case OP_XCLASS:\n    case OP_ECLASS:\n    cc += GET(cc, 1);\n    break;\n#endif\n\n    case OP_MARK:\n    case OP_COMMIT_ARG:\n    case OP_PRUNE_ARG:\n    case OP_SKIP_ARG:\n    case OP_THEN_ARG:\n    cc += PRIV(OP_lengths)[*cc] + cc[1];\n    break;\n\n    case OP_CALLOUT:\n    cb.pattern_position = GET(cc, 1);\n    cb.next_item_length = GET(cc, 1 + LINK_SIZE);\n    cb.callout_number = cc[1 + 2*LINK_SIZE];\n    cb.callout_string_offset = 0;\n    cb.callout_string_length = 0;\n    cb.callout_string = NULL;\n    rc = callback(&cb, callout_data);\n    if (rc != 0) return rc;\n    cc += PRIV(OP_lengths)[*cc];\n    break;\n\n    case OP_CALLOUT_STR:\n    cb.pattern_position = GET(cc, 1);\n    cb.next_item_length = GET(cc, 1 + LINK_SIZE);\n    cb.callout_number = 0;\n    cb.callout_string_offset = GET(cc, 1 + 3*LINK_SIZE);\n    cb.callout_string_length =\n      GET(cc, 1 + 2*LINK_SIZE) - (1 + 4*LINK_SIZE) - 2;\n    cb.callout_string = cc + (1 + 4*LINK_SIZE) + 1;\n    rc = callback(&cb, callout_data);\n    if (rc != 0) return rc;\n    cc += GET(cc, 1 + 2*LINK_SIZE);\n    break;\n\n    default:\n    cc += PRIV(OP_lengths)[*cc];\n    break;\n    }\n  }\n}\n\n/* End of pcre2_pattern_info.c */\n"
  },
  {
    "path": "Libraries/PCRE/pcre2_script_run.c",
    "content": "/*************************************************\n*      Perl-Compatible Regular Expressions       *\n*************************************************/\n\n/* PCRE is a library of functions to support regular expressions whose syntax\nand semantics are as close as possible to those of the Perl 5 language.\n\n                       Written by Philip Hazel\n     Original API code Copyright (c) 1997-2012 University of Cambridge\n          New API code Copyright (c) 2016-2021 University of Cambridge\n\n-----------------------------------------------------------------------------\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n    * Redistributions of source code must retain the above copyright notice,\n      this list of conditions and the following disclaimer.\n\n    * Redistributions in binary form must reproduce the above copyright\n      notice, this list of conditions and the following disclaimer in the\n      documentation and/or other materials provided with the distribution.\n\n    * Neither the name of the University of Cambridge nor the names of its\n      contributors may be used to endorse or promote products derived from\n      this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\nLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\nCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\nSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\nINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\nCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGE.\n-----------------------------------------------------------------------------\n*/\n\n/* This module contains the function for checking a script run. */\n\n#ifdef HAVE_CONFIG_H\n#include \"config.h\"\n#endif\n\n#include \"pcre2_internal.h\"\n\n\n/*************************************************\n*                Check script run                *\n*************************************************/\n\n/* A script run is conceptually a sequence of characters all in the same\nUnicode script. However, it isn't quite that simple. There are special rules\nfor scripts that are commonly used together, and also special rules for digits.\nThis function implements the appropriate checks, which is possible only when\nPCRE2 is compiled with Unicode support. The function returns TRUE if there is\nno Unicode support; however, it should never be called in that circumstance\nbecause an error is given by pcre2_compile() if a script run is called for in a\nversion of PCRE2 compiled without Unicode support.\n\nArguments:\n  pgr       point to the first character\n  endptr    point after the last character\n  utf       TRUE if in UTF mode\n\nReturns:    TRUE if this is a valid script run\n*/\n\n/* These are states in the checking process. */\n\nenum { SCRIPT_UNSET,          /* Requirement as yet unknown */\n       SCRIPT_MAP,            /* Bitmap contains acceptable scripts */\n       SCRIPT_HANPENDING,     /* Have had only Han characters */\n       SCRIPT_HANHIRAKATA,    /* Expect Han or Hirikata */\n       SCRIPT_HANBOPOMOFO,    /* Expect Han or Bopomofo */\n       SCRIPT_HANHANGUL       /* Expect Han or Hangul */\n       };\n\n#define UCD_MAPSIZE (ucp_Unknown/32 + 1)\n#define FULL_MAPSIZE (ucp_Script_Count/32 + 1)\n\nBOOL\nPRIV(script_run)(PCRE2_SPTR ptr, PCRE2_SPTR endptr, BOOL utf)\n{\n#ifdef SUPPORT_UNICODE\nuint32_t require_state = SCRIPT_UNSET;\nuint32_t require_map[FULL_MAPSIZE];\nuint32_t map[FULL_MAPSIZE];\nuint32_t require_digitset = 0;\nuint32_t c;\n\n#if PCRE2_CODE_UNIT_WIDTH == 32\n(void)utf;    /* Avoid compiler warning */\n#endif\n\n/* Any string containing fewer than 2 characters is a valid script run. */\n\nif (ptr >= endptr) return TRUE;\nGETCHARINCTEST(c, ptr);\nif (ptr >= endptr) return TRUE;\n\n/* Initialize the require map. This is a full-size bitmap that has a bit for\nevery script, as opposed to the maps in ucd_script_sets, which only have bits\nfor scripts less than ucp_Unknown - those that appear in script extension\nlists. */\n\nfor (int i = 0; i < FULL_MAPSIZE; i++) require_map[i] = 0;\n\n/* Scan strings of two or more characters, checking the Unicode characteristics\nof each code point. There is special code for scripts that can be combined with\ncharacters from the Han Chinese script. This may be used in conjunction with\nfour other scripts in these combinations:\n\n. Han with Hiragana and Katakana is allowed (for Japanese).\n. Han with Bopomofo is allowed (for Taiwanese Mandarin).\n. Han with Hangul is allowed (for Korean).\n\nIf the first significant character's script is one of the four, the required\nscript type is immediately known. However, if the first significant\ncharacter's script is Han, we have to keep checking for a non-Han character.\nHence the SCRIPT_HANPENDING state. */\n\nfor (;;)\n  {\n  const ucd_record *ucd = GET_UCD(c);\n  uint32_t script = ucd->script;\n\n  /* If the script is Unknown, the string is not a valid script run. Such\n  characters can only form script runs of length one (see test above). */\n\n  if (script == ucp_Unknown) return FALSE;\n\n  /* A character without any script extensions whose script is Inherited or\n  Common is always accepted with any script. If there are extensions, the\n  following processing happens for all scripts. */\n\n  if (UCD_SCRIPTX_PROP(ucd) != 0 || (script != ucp_Inherited && script != ucp_Common))\n    {\n    BOOL OK;\n\n    /* Set up a full-sized map for this character that can include bits for all\n    scripts. Copy the scriptx map for this character (which covers those\n    scripts that appear in script extension lists), set the remaining values to\n    zero, and then, except for Common or Inherited, add this script's bit to\n    the map. */\n\n    memcpy(map, PRIV(ucd_script_sets) + UCD_SCRIPTX_PROP(ucd), UCD_MAPSIZE * sizeof(uint32_t));\n    memset(map + UCD_MAPSIZE, 0, (FULL_MAPSIZE - UCD_MAPSIZE) * sizeof(uint32_t));\n    if (script != ucp_Common && script != ucp_Inherited) MAPSET(map, script);\n\n    /* Handle the different checking states */\n\n    switch(require_state)\n      {\n      /* First significant character - it might follow Common or Inherited\n      characters that do not have any script extensions. */\n\n      case SCRIPT_UNSET:\n      switch(script)\n        {\n        case ucp_Han:\n        require_state = SCRIPT_HANPENDING;\n        break;\n\n        case ucp_Hiragana:\n        case ucp_Katakana:\n        require_state = SCRIPT_HANHIRAKATA;\n        break;\n\n        case ucp_Bopomofo:\n        require_state = SCRIPT_HANBOPOMOFO;\n        break;\n\n        case ucp_Hangul:\n        require_state = SCRIPT_HANHANGUL;\n        break;\n\n        default:\n        memcpy(require_map, map, FULL_MAPSIZE * sizeof(uint32_t));\n        require_state = SCRIPT_MAP;\n        break;\n        }\n      break;\n\n      /* The first significant character was Han. An inspection of the Unicode\n      11.0.0 files shows that there are the following types of Script Extension\n      list that involve the Han, Bopomofo, Hiragana, Katakana, and Hangul\n      scripts:\n\n      . Bopomofo + Han\n      . Han + Hiragana + Katakana\n      . Hiragana + Katakana\n      . Bopopmofo + Hangul + Han + Hiragana + Katakana\n\n      The following code tries to make sense of this. */\n\n#define FOUND_BOPOMOFO 1\n#define FOUND_HIRAGANA 2\n#define FOUND_KATAKANA 4\n#define FOUND_HANGUL   8\n\n      case SCRIPT_HANPENDING:\n      if (script != ucp_Han)   /* Another Han does nothing */\n        {\n        uint32_t chspecial = 0;\n\n        if (MAPBIT(map, ucp_Bopomofo) != 0) chspecial |= FOUND_BOPOMOFO;\n        if (MAPBIT(map, ucp_Hiragana) != 0) chspecial |= FOUND_HIRAGANA;\n        if (MAPBIT(map, ucp_Katakana) != 0) chspecial |= FOUND_KATAKANA;\n        if (MAPBIT(map, ucp_Hangul) != 0)   chspecial |= FOUND_HANGUL;\n\n        if (chspecial == 0) return FALSE;   /* Not allowed with Han */\n\n        if (chspecial == FOUND_BOPOMOFO)\n          require_state = SCRIPT_HANBOPOMOFO;\n        else if (chspecial == (FOUND_HIRAGANA|FOUND_KATAKANA))\n          require_state = SCRIPT_HANHIRAKATA;\n\n        /* Otherwise this character must be allowed with all of them, so remain\n        in the pending state. */\n        }\n      break;\n\n      /* Previously encountered one of the \"with Han\" scripts. Check that\n      this character is appropriate. */\n\n      case SCRIPT_HANHIRAKATA:\n      if (MAPBIT(map, ucp_Han) + MAPBIT(map, ucp_Hiragana) +\n          MAPBIT(map, ucp_Katakana) == 0) return FALSE;\n      break;\n\n      case SCRIPT_HANBOPOMOFO:\n      if (MAPBIT(map, ucp_Han) + MAPBIT(map, ucp_Bopomofo) == 0) return FALSE;\n      break;\n\n      case SCRIPT_HANHANGUL:\n      if (MAPBIT(map, ucp_Han) + MAPBIT(map, ucp_Hangul) == 0) return FALSE;\n      break;\n\n      /* Previously encountered one or more characters that are allowed with a\n      list of scripts. */\n\n      case SCRIPT_MAP:\n      OK = FALSE;\n\n      for (int i = 0; i < FULL_MAPSIZE; i++)\n        {\n        if ((require_map[i] & map[i]) != 0)\n          {\n          OK = TRUE;\n          break;\n          }\n        }\n\n      if (!OK) return FALSE;\n\n      /* The rest of the string must be in this script, but we have to\n      allow for the Han complications. */\n\n      switch(script)\n        {\n        case ucp_Han:\n        require_state = SCRIPT_HANPENDING;\n        break;\n\n        case ucp_Hiragana:\n        case ucp_Katakana:\n        require_state = SCRIPT_HANHIRAKATA;\n        break;\n\n        case ucp_Bopomofo:\n        require_state = SCRIPT_HANBOPOMOFO;\n        break;\n\n        case ucp_Hangul:\n        require_state = SCRIPT_HANHANGUL;\n        break;\n\n        /* Compute the intersection of the required list of scripts and the\n        allowed scripts for this character. */\n\n        default:\n        for (int i = 0; i < FULL_MAPSIZE; i++) require_map[i] &= map[i];\n        break;\n        }\n\n      break;\n      }\n    }   /* End checking character's script and extensions. */\n\n  /* The character is in an acceptable script. We must now ensure that all\n  decimal digits in the string come from the same set. Some scripts (e.g.\n  Common, Arabic) have more than one set of decimal digits. This code does\n  not allow mixing sets, even within the same script. The vector called\n  PRIV(ucd_digit_sets)[] contains, in its first element, the number of\n  following elements, and then, in ascending order, the code points of the\n  '9' characters in every set of 10 digits. Each set is identified by the\n  offset in the vector of its '9' character. An initial check of the first\n  value picks up ASCII digits quickly. Otherwise, a binary chop is used. */\n\n  if (ucd->chartype == ucp_Nd)\n    {\n    uint32_t digitset;\n\n    if (c <= PRIV(ucd_digit_sets)[1]) digitset = 1; else\n      {\n      int mid;\n      int bot = 1;\n      int top = PRIV(ucd_digit_sets)[0];\n      for (;;)\n        {\n        if (top <= bot + 1)    /* <= rather than == is paranoia */\n          {\n          digitset = top;\n          break;\n          }\n        mid = (top + bot) / 2;\n        if (c <= PRIV(ucd_digit_sets)[mid]) top = mid; else bot = mid;\n        }\n      }\n\n    /* A required value of 0 means \"unset\". */\n\n    if (require_digitset == 0) require_digitset = digitset;\n      else if (digitset != require_digitset) return FALSE;\n    }   /* End digit handling */\n\n  /* If we haven't yet got to the end, pick up the next character. */\n\n  if (ptr >= endptr) return TRUE;\n  GETCHARINCTEST(c, ptr);\n  }  /* End checking loop */\n\n#else   /* NOT SUPPORT_UNICODE */\n(void)ptr;\n(void)endptr;\n(void)utf;\nreturn TRUE;\n#endif  /* SUPPORT_UNICODE */\n}\n\n/* End of pcre2_script_run.c */\n"
  },
  {
    "path": "Libraries/PCRE/pcre2_serialize.c",
    "content": "/*************************************************\n*      Perl-Compatible Regular Expressions       *\n*************************************************/\n\n/* PCRE is a library of functions to support regular expressions whose syntax\nand semantics are as close as possible to those of the Perl 5 language.\n\n                       Written by Philip Hazel\n     Original API code Copyright (c) 1997-2012 University of Cambridge\n          New API code Copyright (c) 2016-2024 University of Cambridge\n\n-----------------------------------------------------------------------------\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n    * Redistributions of source code must retain the above copyright notice,\n      this list of conditions and the following disclaimer.\n\n    * Redistributions in binary form must reproduce the above copyright\n      notice, this list of conditions and the following disclaimer in the\n      documentation and/or other materials provided with the distribution.\n\n    * Neither the name of the University of Cambridge nor the names of its\n      contributors may be used to endorse or promote products derived from\n      this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\nLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\nCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\nSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\nINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\nCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGE.\n-----------------------------------------------------------------------------\n*/\n\n/* This module contains functions for serializing and deserializing\na sequence of compiled codes. */\n\n\n#ifdef HAVE_CONFIG_H\n#include \"config.h\"\n#endif\n\n\n#include \"pcre2_internal.h\"\n\n/* Magic number to provide a small check against being handed junk. */\n\n#define SERIALIZED_DATA_MAGIC 0x50523253u\n\n/* Deserialization is limited to the current PCRE version and\ncharacter width. */\n\n#define SERIALIZED_DATA_VERSION \\\n  ((PCRE2_MAJOR) | ((PCRE2_MINOR) << 16))\n\n#define SERIALIZED_DATA_CONFIG \\\n  (sizeof(PCRE2_UCHAR) | ((sizeof(void*)) << 8) | ((sizeof(PCRE2_SIZE)) << 16))\n\n\n\n/*************************************************\n*           Serialize compiled patterns          *\n*************************************************/\n\nPCRE2_EXP_DEFN int32_t PCRE2_CALL_CONVENTION\npcre2_serialize_encode(const pcre2_code **codes, int32_t number_of_codes,\n   uint8_t **serialized_bytes, PCRE2_SIZE *serialized_size,\n   pcre2_general_context *gcontext)\n{\nuint8_t *bytes;\nuint8_t *dst_bytes;\nint32_t i;\nPCRE2_SIZE total_size;\nconst pcre2_real_code *re;\nconst uint8_t *tables;\npcre2_serialized_data *data;\n\nconst pcre2_memctl *memctl = (gcontext != NULL) ?\n  &gcontext->memctl : &PRIV(default_compile_context).memctl;\n\nif (codes == NULL || serialized_bytes == NULL || serialized_size == NULL)\n  return PCRE2_ERROR_NULL;\n\nif (number_of_codes <= 0) return PCRE2_ERROR_BADDATA;\n\n/* Compute total size. */\ntotal_size = sizeof(pcre2_serialized_data) + TABLES_LENGTH;\ntables = NULL;\n\nfor (i = 0; i < number_of_codes; i++)\n  {\n  if (codes[i] == NULL) return PCRE2_ERROR_NULL;\n  re = (const pcre2_real_code *)(codes[i]);\n  if (re->magic_number != MAGIC_NUMBER) return PCRE2_ERROR_BADMAGIC;\n  if (tables == NULL)\n    tables = re->tables;\n  else if (tables != re->tables)\n    return PCRE2_ERROR_MIXEDTABLES;\n  total_size += re->blocksize;\n  }\n\n/* Initialize the byte stream. */\nbytes = memctl->malloc(total_size + sizeof(pcre2_memctl), memctl->memory_data);\nif (bytes == NULL) return PCRE2_ERROR_NOMEMORY;\n\n/* The controller is stored as a hidden parameter. */\nmemcpy(bytes, memctl, sizeof(pcre2_memctl));\nbytes += sizeof(pcre2_memctl);\n\ndata = (pcre2_serialized_data *)bytes;\ndata->magic = SERIALIZED_DATA_MAGIC;\ndata->version = SERIALIZED_DATA_VERSION;\ndata->config = SERIALIZED_DATA_CONFIG;\ndata->number_of_codes = number_of_codes;\n\n/* Copy all compiled code data. */\ndst_bytes = bytes + sizeof(pcre2_serialized_data);\nmemcpy(dst_bytes, tables, TABLES_LENGTH);\ndst_bytes += TABLES_LENGTH;\n\nfor (i = 0; i < number_of_codes; i++)\n  {\n  re = (const pcre2_real_code *)(codes[i]);\n  (void)memcpy(dst_bytes, (const char *)re, re->blocksize);\n\n  /* Certain fields in the compiled code block are re-set during\n  deserialization. In order to ensure that the serialized data stream is always\n  the same for the same pattern, set them to zero here. We can't assume the\n  copy of the pattern is correctly aligned for accessing the fields as part of\n  a structure. Note the use of sizeof(void *) in the second of these, to\n  specify the size of a pointer. If sizeof(uint8_t *) is used (tables is a\n  pointer to uint8_t), gcc gives a warning because the first argument is also a\n  pointer to uint8_t. Casting the first argument to (void *) can stop this, but\n  it didn't stop Coverity giving the same complaint. */\n\n  (void)memset(dst_bytes + offsetof(pcre2_real_code, memctl), 0,\n    sizeof(pcre2_memctl));\n  (void)memset(dst_bytes + offsetof(pcre2_real_code, tables), 0,\n    sizeof(void *));\n  (void)memset(dst_bytes + offsetof(pcre2_real_code, executable_jit), 0,\n    sizeof(void *));\n\n  dst_bytes += re->blocksize;\n  }\n\n*serialized_bytes = bytes;\n*serialized_size = total_size;\nreturn number_of_codes;\n}\n\n\n/*************************************************\n*          Deserialize compiled patterns         *\n*************************************************/\n\nPCRE2_EXP_DEFN int32_t PCRE2_CALL_CONVENTION\npcre2_serialize_decode(pcre2_code **codes, int32_t number_of_codes,\n   const uint8_t *bytes, pcre2_general_context *gcontext)\n{\nconst pcre2_serialized_data *data = (const pcre2_serialized_data *)bytes;\nconst pcre2_memctl *memctl = (gcontext != NULL) ?\n  &gcontext->memctl : &PRIV(default_compile_context).memctl;\n\nconst uint8_t *src_bytes;\npcre2_real_code *dst_re;\nuint8_t *tables;\nint32_t i, j;\n\n/* Sanity checks. */\n\nif (data == NULL || codes == NULL) return PCRE2_ERROR_NULL;\nif (number_of_codes <= 0) return PCRE2_ERROR_BADDATA;\nif (data->number_of_codes <= 0) return PCRE2_ERROR_BADSERIALIZEDDATA;\nif (data->magic != SERIALIZED_DATA_MAGIC) return PCRE2_ERROR_BADMAGIC;\nif (data->version != SERIALIZED_DATA_VERSION) return PCRE2_ERROR_BADMODE;\nif (data->config != SERIALIZED_DATA_CONFIG) return PCRE2_ERROR_BADMODE;\n\nif (number_of_codes > data->number_of_codes)\n  number_of_codes = data->number_of_codes;\n\nsrc_bytes = bytes + sizeof(pcre2_serialized_data);\n\n/* Decode tables. The reference count for the tables is stored immediately\nfollowing them. */\n\ntables = memctl->malloc(TABLES_LENGTH + sizeof(PCRE2_SIZE), memctl->memory_data);\nif (tables == NULL) return PCRE2_ERROR_NOMEMORY;\n\nmemcpy(tables, src_bytes, TABLES_LENGTH);\n*(PCRE2_SIZE *)(tables + TABLES_LENGTH) = number_of_codes;\nsrc_bytes += TABLES_LENGTH;\n\n/* Decode the byte stream. We must not try to read the size from the compiled\ncode block in the stream, because it might be unaligned, which causes errors on\nhardware such as Sparc-64 that doesn't like unaligned memory accesses. The type\nof the blocksize field is given its own name to ensure that it is the same here\nas in the block. */\n\nfor (i = 0; i < number_of_codes; i++)\n  {\n  CODE_BLOCKSIZE_TYPE blocksize;\n  memcpy(&blocksize, src_bytes + offsetof(pcre2_real_code, blocksize),\n    sizeof(CODE_BLOCKSIZE_TYPE));\n  if (blocksize <= sizeof(pcre2_real_code))\n    return PCRE2_ERROR_BADSERIALIZEDDATA;\n\n  /* The allocator provided by gcontext replaces the original one. */\n\n  dst_re = (pcre2_real_code *)PRIV(memctl_malloc)(blocksize,\n    (pcre2_memctl *)gcontext);\n  if (dst_re == NULL)\n    {\n    memctl->free(tables, memctl->memory_data);\n    for (j = 0; j < i; j++)\n      {\n      memctl->free(codes[j], memctl->memory_data);\n      codes[j] = NULL;\n      }\n    return PCRE2_ERROR_NOMEMORY;\n    }\n\n  /* The new allocator must be preserved. */\n\n  memcpy(((uint8_t *)dst_re) + sizeof(pcre2_memctl),\n    src_bytes + sizeof(pcre2_memctl), blocksize - sizeof(pcre2_memctl));\n  if (dst_re->magic_number != MAGIC_NUMBER ||\n      dst_re->name_entry_size > MAX_NAME_SIZE + IMM2_SIZE + 1 ||\n      dst_re->name_count > MAX_NAME_COUNT)\n    {\n    memctl->free(dst_re, memctl->memory_data);\n    return PCRE2_ERROR_BADSERIALIZEDDATA;\n    }\n\n  /* At the moment only one table is supported. */\n\n  dst_re->tables = tables;\n  dst_re->executable_jit = NULL;\n  dst_re->flags |= PCRE2_DEREF_TABLES;\n\n  codes[i] = dst_re;\n  src_bytes += blocksize;\n  }\n\nreturn number_of_codes;\n}\n\n\n/*************************************************\n*    Get the number of serialized patterns       *\n*************************************************/\n\nPCRE2_EXP_DEFN int32_t PCRE2_CALL_CONVENTION\npcre2_serialize_get_number_of_codes(const uint8_t *bytes)\n{\nconst pcre2_serialized_data *data = (const pcre2_serialized_data *)bytes;\n\nif (data == NULL) return PCRE2_ERROR_NULL;\nif (data->magic != SERIALIZED_DATA_MAGIC) return PCRE2_ERROR_BADMAGIC;\nif (data->version != SERIALIZED_DATA_VERSION) return PCRE2_ERROR_BADMODE;\nif (data->config != SERIALIZED_DATA_CONFIG) return PCRE2_ERROR_BADMODE;\n\nreturn data->number_of_codes;\n}\n\n\n/*************************************************\n*            Free the allocated stream           *\n*************************************************/\n\nPCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION\npcre2_serialize_free(uint8_t *bytes)\n{\nif (bytes != NULL)\n  {\n  pcre2_memctl *memctl = (pcre2_memctl *)(bytes - sizeof(pcre2_memctl));\n  memctl->free(memctl, memctl->memory_data);\n  }\n}\n\n/* End of pcre2_serialize.c */\n"
  },
  {
    "path": "Libraries/PCRE/pcre2_string_utils.c",
    "content": "/*************************************************\n*      Perl-Compatible Regular Expressions       *\n*************************************************/\n\n/* PCRE is a library of functions to support regular expressions whose syntax\nand semantics are as close as possible to those of the Perl 5 language.\n\n                       Written by Philip Hazel\n     Original API code Copyright (c) 1997-2012 University of Cambridge\n          New API code Copyright (c) 2018-2021 University of Cambridge\n\n-----------------------------------------------------------------------------\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n    * Redistributions of source code must retain the above copyright notice,\n      this list of conditions and the following disclaimer.\n\n    * Redistributions in binary form must reproduce the above copyright\n      notice, this list of conditions and the following disclaimer in the\n      documentation and/or other materials provided with the distribution.\n\n    * Neither the name of the University of Cambridge nor the names of its\n      contributors may be used to endorse or promote products derived from\n      this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\nLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\nCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\nSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\nINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\nCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGE.\n-----------------------------------------------------------------------------\n*/\n\n/* This module contains internal functions for comparing and finding the length\nof strings. These are used instead of strcmp() etc because the standard\nfunctions work only on 8-bit data. */\n\n\n#ifdef HAVE_CONFIG_H\n#include \"config.h\"\n#endif\n\n#include \"pcre2_internal.h\"\n\n\n/*************************************************\n*    Emulated memmove() for systems without it   *\n*************************************************/\n\n/* This function can make use of bcopy() if it is available. Otherwise do it by\nsteam, as there some non-Unix environments that lack both memmove() and\nbcopy(). */\n\n#if !defined(VPCOMPAT) && !defined(HAVE_MEMMOVE)\nvoid *\nPRIV(memmove)(void *d, const void *s, size_t n)\n{\n#ifdef HAVE_BCOPY\nbcopy(s, d, n);\nreturn d;\n#else\nsize_t i;\nunsigned char *dest = (unsigned char *)d;\nconst unsigned char *src = (const unsigned char *)s;\nif (dest > src)\n  {\n  dest += n;\n  src += n;\n  for (i = 0; i < n; ++i) *(--dest) = *(--src);\n  return (void *)dest;\n  }\nelse\n  {\n  for (i = 0; i < n; ++i) *dest++ = *src++;\n  return (void *)(dest - n);\n  }\n#endif   /* not HAVE_BCOPY */\n}\n#endif   /* not VPCOMPAT && not HAVE_MEMMOVE */\n\n\n/*************************************************\n*    Compare two zero-terminated PCRE2 strings   *\n*************************************************/\n\n/*\nArguments:\n  str1        first string\n  str2        second string\n\nReturns:      0, 1, or -1\n*/\n\nint\nPRIV(strcmp)(PCRE2_SPTR str1, PCRE2_SPTR str2)\n{\nPCRE2_UCHAR c1, c2;\nwhile (*str1 != '\\0' || *str2 != '\\0')\n  {\n  c1 = *str1++;\n  c2 = *str2++;\n  if (c1 != c2) return ((c1 > c2) << 1) - 1;\n  }\nreturn 0;\n}\n\n\n/*************************************************\n*  Compare zero-terminated PCRE2 & 8-bit strings *\n*************************************************/\n\n/* As the 8-bit string is almost always a literal, its type is specified as\nconst char *.\n\nArguments:\n  str1        first string\n  str2        second string\n\nReturns:      0, 1, or -1\n*/\n\nint\nPRIV(strcmp_c8)(PCRE2_SPTR str1, const char *str2)\n{\nPCRE2_UCHAR c1, c2;\nwhile (*str1 != '\\0' || *str2 != '\\0')\n  {\n  c1 = *str1++;\n  c2 = *str2++;\n  if (c1 != c2) return ((c1 > c2) << 1) - 1;\n  }\nreturn 0;\n}\n\n\n/*************************************************\n*    Compare two PCRE2 strings, given a length   *\n*************************************************/\n\n/*\nArguments:\n  str1        first string\n  str2        second string\n  len         the length\n\nReturns:      0, 1, or -1\n*/\n\nint\nPRIV(strncmp)(PCRE2_SPTR str1, PCRE2_SPTR str2, size_t len)\n{\nPCRE2_UCHAR c1, c2;\nfor (; len > 0; len--)\n  {\n  c1 = *str1++;\n  c2 = *str2++;\n  if (c1 != c2) return ((c1 > c2) << 1) - 1;\n  }\nreturn 0;\n}\n\n\n/*************************************************\n* Compare PCRE2 string to 8-bit string by length *\n*************************************************/\n\n/* As the 8-bit string is almost always a literal, its type is specified as\nconst char *.\n\nArguments:\n  str1        first string\n  str2        second string\n  len         the length\n\nReturns:      0, 1, or -1\n*/\n\nint\nPRIV(strncmp_c8)(PCRE2_SPTR str1, const char *str2, size_t len)\n{\nPCRE2_UCHAR c1, c2;\nfor (; len > 0; len--)\n  {\n  c1 = *str1++;\n  c2 = *str2++;\n  if (c1 != c2) return ((c1 > c2) << 1) - 1;\n  }\nreturn 0;\n}\n\n\n/*************************************************\n*        Find the length of a PCRE2 string       *\n*************************************************/\n\n/*\nArgument:    the string\nReturns:     the length\n*/\n\nPCRE2_SIZE\nPRIV(strlen)(PCRE2_SPTR str)\n{\nPCRE2_SIZE c = 0;\nwhile (*str++ != 0) c++;\nreturn c;\n}\n\n\n/*************************************************\n* Copy 8-bit 0-terminated string to PCRE2 string *\n*************************************************/\n\n/* Arguments:\n  str1     buffer to receive the string\n  str2     8-bit string to be copied\n\nReturns:   the number of code units used (excluding trailing zero)\n*/\n\nPCRE2_SIZE\nPRIV(strcpy_c8)(PCRE2_UCHAR *str1, const char *str2)\n{\nPCRE2_UCHAR *t = str1;\nwhile (*str2 != 0) *t++ = *str2++;\n*t = 0;\nreturn t - str1;\n}\n\n/* End of pcre2_string_utils.c */\n"
  },
  {
    "path": "Libraries/PCRE/pcre2_study.c",
    "content": "/*************************************************\n*      Perl-Compatible Regular Expressions       *\n*************************************************/\n\n/* PCRE is a library of functions to support regular expressions whose syntax\nand semantics are as close as possible to those of the Perl 5 language.\n\n                       Written by Philip Hazel\n     Original API code Copyright (c) 1997-2012 University of Cambridge\n          New API code Copyright (c) 2016-2024 University of Cambridge\n\n-----------------------------------------------------------------------------\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n    * Redistributions of source code must retain the above copyright notice,\n      this list of conditions and the following disclaimer.\n\n    * Redistributions in binary form must reproduce the above copyright\n      notice, this list of conditions and the following disclaimer in the\n      documentation and/or other materials provided with the distribution.\n\n    * Neither the name of the University of Cambridge nor the names of its\n      contributors may be used to endorse or promote products derived from\n      this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\nLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\nCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\nSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\nINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\nCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGE.\n-----------------------------------------------------------------------------\n*/\n\n/* This module contains functions for scanning a compiled pattern and\ncollecting data (e.g. minimum matching length). */\n\n\n#ifdef HAVE_CONFIG_H\n#include \"config.h\"\n#endif\n\n#include \"pcre2_internal.h\"\n\n/* The maximum remembered capturing brackets minimum. */\n\n#define MAX_CACHE_BACKREF 128\n\n/* Set a bit in the starting code unit bit map. */\n\n#define SET_BIT(c) re->start_bitmap[(c)/8] |= (1u << ((c)&7))\n\n/* Returns from set_start_bits() */\n\nenum { SSB_FAIL, SSB_DONE, SSB_CONTINUE, SSB_UNKNOWN, SSB_TOODEEP };\n\n\n/*************************************************\n*   Find the minimum subject length for a group  *\n*************************************************/\n\n/* Scan a parenthesized group and compute the minimum length of subject that\nis needed to match it. This is a lower bound; it does not mean there is a\nstring of that length that matches. In UTF mode, the result is in characters\nrather than code units. The field in a compiled pattern for storing the minimum\nlength is 16-bits long (on the grounds that anything longer than that is\npathological), so we give up when we reach that amount. This also means that\ninteger overflow for really crazy patterns cannot happen.\n\nBackreference minimum lengths are cached to speed up multiple references. This\nfunction is called only when the highest back reference in the pattern is less\nthan or equal to MAX_CACHE_BACKREF, which is one less than the size of the\ncaching vector. The zeroth element contains the number of the highest set\nvalue.\n\nArguments:\n  re              compiled pattern block\n  code            pointer to start of group (the bracket)\n  startcode       pointer to start of the whole pattern's code\n  utf             UTF flag\n  recurses        chain of recurse_check to catch mutual recursion\n  countptr        pointer to call count (to catch over complexity)\n  backref_cache   vector for caching back references.\n\nThis function is no longer called when the pattern contains (*ACCEPT); however,\nthe old code for returning -1 is retained, just in case.\n\nReturns:   the minimum length\n           -1 \\C in UTF-8 mode\n              or (*ACCEPT)\n              or pattern too complicated\n           -2 internal error (missing capturing bracket)\n           -3 internal error (opcode not listed)\n*/\n\nstatic int\nfind_minlength(const pcre2_real_code *re, PCRE2_SPTR code,\n  PCRE2_SPTR startcode, BOOL utf, recurse_check *recurses, int *countptr,\n  int *backref_cache)\n{\nint length = -1;\nint branchlength = 0;\nint prev_cap_recno = -1;\nint prev_cap_d = 0;\nint prev_recurse_recno = -1;\nint prev_recurse_d = 0;\nuint32_t once_fudge = 0;\nBOOL had_recurse = FALSE;\nBOOL dupcapused = (re->flags & PCRE2_DUPCAPUSED) != 0;\nPCRE2_SPTR nextbranch = code + GET(code, 1);\nPCRE2_SPTR cc = code + 1 + LINK_SIZE;\nrecurse_check this_recurse;\n\n/* If this is a \"could be empty\" group, its minimum length is 0. */\n\nif (*code >= OP_SBRA && *code <= OP_SCOND) return 0;\n\n/* Skip over capturing bracket number */\n\nif (*code == OP_CBRA || *code == OP_CBRAPOS) cc += IMM2_SIZE;\n\n/* A large and/or complex regex can take too long to process. */\n\nif ((*countptr)++ > 1000) return -1;\n\n/* Scan along the opcodes for this branch. If we get to the end of the branch,\ncheck the length against that of the other branches. If the accumulated length\npasses 16-bits, reset to that value and skip the rest of the branch. */\n\nfor (;;)\n  {\n  int d, min, recno;\n  PCRE2_UCHAR op;\n  PCRE2_SPTR cs, ce;\n\n  if (branchlength >= UINT16_MAX)\n    {\n    branchlength = UINT16_MAX;\n    cc = nextbranch;\n    }\n\n  op = *cc;\n  switch (op)\n    {\n    case OP_COND:\n    case OP_SCOND:\n\n    /* If there is only one branch in a condition, the implied branch has zero\n    length, so we don't add anything. This covers the DEFINE \"condition\"\n    automatically. If there are two branches we can treat it the same as any\n    other non-capturing subpattern. */\n\n    cs = cc + GET(cc, 1);\n    if (*cs != OP_ALT)\n      {\n      cc = cs + 1 + LINK_SIZE;\n      break;\n      }\n    goto PROCESS_NON_CAPTURE;\n\n    case OP_BRA:\n    /* There's a special case of OP_BRA, when it is wrapped round a repeated\n    OP_RECURSE. We'd like to process the latter at this level so that\n    remembering the value works for repeated cases. So we do nothing, but\n    set a fudge value to skip over the OP_KET after the recurse. */\n\n    if (cc[1+LINK_SIZE] == OP_RECURSE && cc[2*(1+LINK_SIZE)] == OP_KET)\n      {\n      once_fudge = 1 + LINK_SIZE;\n      cc += 1 + LINK_SIZE;\n      break;\n      }\n    /* Fall through */\n\n    case OP_ONCE:\n    case OP_SCRIPT_RUN:\n    case OP_SBRA:\n    case OP_BRAPOS:\n    case OP_SBRAPOS:\n    PROCESS_NON_CAPTURE:\n    d = find_minlength(re, cc, startcode, utf, recurses, countptr,\n      backref_cache);\n    if (d < 0) return d;\n    branchlength += d;\n    do cc += GET(cc, 1); while (*cc == OP_ALT);\n    cc += 1 + LINK_SIZE;\n    break;\n\n    /* To save time for repeated capturing subpatterns, we remember the\n    length of the previous one. Unfortunately we can't do the same for\n    the unnumbered ones above. Nor can we do this if (?| is present in the\n    pattern because captures with the same number are not then identical. */\n\n    case OP_CBRA:\n    case OP_SCBRA:\n    case OP_CBRAPOS:\n    case OP_SCBRAPOS:\n    recno = (int)GET2(cc, 1+LINK_SIZE);\n    if (dupcapused || recno != prev_cap_recno)\n      {\n      prev_cap_recno = recno;\n      prev_cap_d = find_minlength(re, cc, startcode, utf, recurses, countptr,\n        backref_cache);\n      if (prev_cap_d < 0) return prev_cap_d;\n      }\n    branchlength += prev_cap_d;\n    do cc += GET(cc, 1); while (*cc == OP_ALT);\n    cc += 1 + LINK_SIZE;\n    break;\n\n    /* ACCEPT makes things far too complicated; we have to give up. In fact,\n    from 10.34 onwards, if a pattern contains (*ACCEPT), this function is not\n    used. However, leave the code in place, just in case. */\n\n    case OP_ACCEPT:\n    case OP_ASSERT_ACCEPT:\n    return -1;\n\n    /* Reached end of a branch; if it's a ket it is the end of a nested\n    call. If it's ALT it is an alternation in a nested call. If it is END it's\n    the end of the outer call. All can be handled by the same code. If the\n    length of any branch is zero, there is no need to scan any subsequent\n    branches. */\n\n    case OP_ALT:\n    case OP_KET:\n    case OP_KETRMAX:\n    case OP_KETRMIN:\n    case OP_KETRPOS:\n    case OP_END:\n    if (length < 0 || (!had_recurse && branchlength < length))\n      length = branchlength;\n    if (op != OP_ALT || length == 0) return length;\n    nextbranch = cc + GET(cc, 1);\n    cc += 1 + LINK_SIZE;\n    branchlength = 0;\n    had_recurse = FALSE;\n    break;\n\n    /* Skip over assertive subpatterns */\n\n    case OP_ASSERT:\n    case OP_ASSERT_NOT:\n    case OP_ASSERTBACK:\n    case OP_ASSERTBACK_NOT:\n    case OP_ASSERT_NA:\n    case OP_ASSERT_SCS:\n    case OP_ASSERTBACK_NA:\n    do cc += GET(cc, 1); while (*cc == OP_ALT);\n    /* Fall through */\n\n    /* Skip over things that don't match chars */\n\n    case OP_REVERSE:\n    case OP_VREVERSE:\n    case OP_CREF:\n    case OP_DNCREF:\n    case OP_RREF:\n    case OP_DNRREF:\n    case OP_FALSE:\n    case OP_TRUE:\n    case OP_CALLOUT:\n    case OP_SOD:\n    case OP_SOM:\n    case OP_EOD:\n    case OP_EODN:\n    case OP_CIRC:\n    case OP_CIRCM:\n    case OP_DOLL:\n    case OP_DOLLM:\n    case OP_NOT_WORD_BOUNDARY:\n    case OP_WORD_BOUNDARY:\n    case OP_NOT_UCP_WORD_BOUNDARY:\n    case OP_UCP_WORD_BOUNDARY:\n    cc += PRIV(OP_lengths)[*cc];\n    break;\n\n    case OP_CALLOUT_STR:\n    cc += GET(cc, 1 + 2*LINK_SIZE);\n    break;\n\n    /* Skip over a subpattern that has a {0} or {0,x} quantifier */\n\n    case OP_BRAZERO:\n    case OP_BRAMINZERO:\n    case OP_BRAPOSZERO:\n    case OP_SKIPZERO:\n    cc += PRIV(OP_lengths)[*cc];\n    do cc += GET(cc, 1); while (*cc == OP_ALT);\n    cc += 1 + LINK_SIZE;\n    break;\n\n    /* Handle literal characters and + repetitions */\n\n    case OP_CHAR:\n    case OP_CHARI:\n    case OP_NOT:\n    case OP_NOTI:\n    case OP_PLUS:\n    case OP_PLUSI:\n    case OP_MINPLUS:\n    case OP_MINPLUSI:\n    case OP_POSPLUS:\n    case OP_POSPLUSI:\n    case OP_NOTPLUS:\n    case OP_NOTPLUSI:\n    case OP_NOTMINPLUS:\n    case OP_NOTMINPLUSI:\n    case OP_NOTPOSPLUS:\n    case OP_NOTPOSPLUSI:\n    branchlength++;\n    cc += 2;\n#ifdef SUPPORT_UNICODE\n    if (utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);\n#endif\n    break;\n\n    case OP_TYPEPLUS:\n    case OP_TYPEMINPLUS:\n    case OP_TYPEPOSPLUS:\n    branchlength++;\n    cc += (cc[1] == OP_PROP || cc[1] == OP_NOTPROP)? 4 : 2;\n    break;\n\n    /* Handle exact repetitions. The count is already in characters, but we\n    may need to skip over a multibyte character in UTF mode.  */\n\n    case OP_EXACT:\n    case OP_EXACTI:\n    case OP_NOTEXACT:\n    case OP_NOTEXACTI:\n    branchlength += GET2(cc,1);\n    cc += 2 + IMM2_SIZE;\n#ifdef SUPPORT_UNICODE\n    if (utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);\n#endif\n    break;\n\n    case OP_TYPEEXACT:\n    branchlength += GET2(cc,1);\n    cc += 2 + IMM2_SIZE + ((cc[1 + IMM2_SIZE] == OP_PROP\n      || cc[1 + IMM2_SIZE] == OP_NOTPROP)? 2 : 0);\n    break;\n\n    /* Handle single-char non-literal matchers */\n\n    case OP_PROP:\n    case OP_NOTPROP:\n    cc += 2;\n    /* Fall through */\n\n    case OP_NOT_DIGIT:\n    case OP_DIGIT:\n    case OP_NOT_WHITESPACE:\n    case OP_WHITESPACE:\n    case OP_NOT_WORDCHAR:\n    case OP_WORDCHAR:\n    case OP_ANY:\n    case OP_ALLANY:\n    case OP_EXTUNI:\n    case OP_HSPACE:\n    case OP_NOT_HSPACE:\n    case OP_VSPACE:\n    case OP_NOT_VSPACE:\n    branchlength++;\n    cc++;\n    break;\n\n    /* \"Any newline\" might match two characters, but it also might match just\n    one. */\n\n    case OP_ANYNL:\n    branchlength += 1;\n    cc++;\n    break;\n\n    /* The single-byte matcher means we can't proceed in UTF mode. (In\n    non-UTF mode \\C will actually be turned into OP_ALLANY, so won't ever\n    appear, but leave the code, just in case.) */\n\n    case OP_ANYBYTE:\n#ifdef SUPPORT_UNICODE\n    if (utf) return -1;\n#endif\n    branchlength++;\n    cc++;\n    break;\n\n    /* For repeated character types, we have to test for \\p and \\P, which have\n    an extra two bytes of parameters. */\n\n    case OP_TYPESTAR:\n    case OP_TYPEMINSTAR:\n    case OP_TYPEQUERY:\n    case OP_TYPEMINQUERY:\n    case OP_TYPEPOSSTAR:\n    case OP_TYPEPOSQUERY:\n    if (cc[1] == OP_PROP || cc[1] == OP_NOTPROP) cc += 2;\n    cc += PRIV(OP_lengths)[op];\n    break;\n\n    case OP_TYPEUPTO:\n    case OP_TYPEMINUPTO:\n    case OP_TYPEPOSUPTO:\n    if (cc[1 + IMM2_SIZE] == OP_PROP\n      || cc[1 + IMM2_SIZE] == OP_NOTPROP) cc += 2;\n    cc += PRIV(OP_lengths)[op];\n    break;\n\n    /* Check a class for variable quantification */\n\n    case OP_CLASS:\n    case OP_NCLASS:\n#ifdef SUPPORT_WIDE_CHARS\n    case OP_XCLASS:\n    case OP_ECLASS:\n    /* The original code caused an unsigned overflow in 64 bit systems,\n    so now we use a conditional statement. */\n    if (op == OP_XCLASS || op == OP_ECLASS)\n      cc += GET(cc, 1);\n    else\n#endif\n      cc += PRIV(OP_lengths)[OP_CLASS];\n\n    switch (*cc)\n      {\n      case OP_CRPLUS:\n      case OP_CRMINPLUS:\n      case OP_CRPOSPLUS:\n      branchlength++;\n      /* Fall through */\n\n      case OP_CRSTAR:\n      case OP_CRMINSTAR:\n      case OP_CRQUERY:\n      case OP_CRMINQUERY:\n      case OP_CRPOSSTAR:\n      case OP_CRPOSQUERY:\n      cc++;\n      break;\n\n      case OP_CRRANGE:\n      case OP_CRMINRANGE:\n      case OP_CRPOSRANGE:\n      branchlength += GET2(cc,1);\n      cc += 1 + 2 * IMM2_SIZE;\n      break;\n\n      default:\n      branchlength++;\n      break;\n      }\n    break;\n\n    /* Backreferences and subroutine calls (OP_RECURSE) are treated in the same\n    way: we find the minimum length for the subpattern. A recursion\n    (backreference or subroutine) causes an a flag to be set that causes the\n    length of this branch to be ignored. The logic is that a recursion can only\n    make sense if there is another alternative that stops the recursing. That\n    will provide the minimum length (when no recursion happens).\n\n    If PCRE2_MATCH_UNSET_BACKREF is set, a backreference to an unset bracket\n    matches an empty string (by default it causes a matching failure), so in\n    that case we must set the minimum length to zero.\n\n    For backreferenes, if duplicate numbers are present in the pattern we check\n    for a reference to a duplicate. If it is, we don't know which version will\n    be referenced, so we have to set the minimum length to zero. */\n\n    /* Duplicate named pattern back reference. */\n\n    case OP_DNREF:\n    case OP_DNREFI:\n    if (!dupcapused && (re->overall_options & PCRE2_MATCH_UNSET_BACKREF) == 0)\n      {\n      int count = GET2(cc, 1+IMM2_SIZE);\n      PCRE2_SPTR slot =\n        (PCRE2_SPTR)((const uint8_t *)re + sizeof(pcre2_real_code)) +\n          GET2(cc, 1) * re->name_entry_size;\n\n      d = INT_MAX;\n\n      /* Scan all groups with the same name; find the shortest. */\n\n      while (count-- > 0)\n        {\n        int dd, i;\n        recno = GET2(slot, 0);\n\n        if (recno <= backref_cache[0] && backref_cache[recno] >= 0)\n          dd = backref_cache[recno];\n        else\n          {\n          ce = cs = PRIV(find_bracket)(startcode, utf, recno);\n          if (cs == NULL) return -2;\n          do ce += GET(ce, 1); while (*ce == OP_ALT);\n\n          dd = 0;\n          if (!dupcapused || PRIV(find_bracket)(ce, utf, recno) == NULL)\n            {\n            if (cc > cs && cc < ce)    /* Simple recursion */\n              {\n              had_recurse = TRUE;\n              }\n            else\n              {\n              recurse_check *r = recurses;\n              for (r = recurses; r != NULL; r = r->prev)\n                if (r->group == cs) break;\n              if (r != NULL)           /* Mutual recursion */\n                {\n                had_recurse = TRUE;\n                }\n              else\n                {\n                this_recurse.prev = recurses;  /* No recursion */\n                this_recurse.group = cs;\n                dd = find_minlength(re, cs, startcode, utf, &this_recurse,\n                  countptr, backref_cache);\n                if (dd < 0) return dd;\n                }\n              }\n            }\n\n          backref_cache[recno] = dd;\n          for (i = backref_cache[0] + 1; i < recno; i++) backref_cache[i] = -1;\n          backref_cache[0] = recno;\n          }\n\n        if (dd < d) d = dd;\n        if (d <= 0) break;    /* No point looking at any more */\n        slot += re->name_entry_size;\n        }\n      }\n    else d = 0;\n    cc += PRIV(OP_lengths)[*cc];\n    goto REPEAT_BACK_REFERENCE;\n\n    /* Single back reference by number. References by name are converted to by\n    number when there is no duplication. */\n\n    case OP_REF:\n    case OP_REFI:\n    recno = GET2(cc, 1);\n    if (recno <= backref_cache[0] && backref_cache[recno] >= 0)\n      d = backref_cache[recno];\n    else\n      {\n      int i;\n      d = 0;\n\n      if ((re->overall_options & PCRE2_MATCH_UNSET_BACKREF) == 0)\n        {\n        ce = cs = PRIV(find_bracket)(startcode, utf, recno);\n        if (cs == NULL) return -2;\n        do ce += GET(ce, 1); while (*ce == OP_ALT);\n\n        if (!dupcapused || PRIV(find_bracket)(ce, utf, recno) == NULL)\n          {\n          if (cc > cs && cc < ce)    /* Simple recursion */\n            {\n            had_recurse = TRUE;\n            }\n          else\n            {\n            recurse_check *r = recurses;\n            for (r = recurses; r != NULL; r = r->prev) if (r->group == cs) break;\n            if (r != NULL)           /* Mutual recursion */\n              {\n              had_recurse = TRUE;\n              }\n            else                     /* No recursion */\n              {\n              this_recurse.prev = recurses;\n              this_recurse.group = cs;\n              d = find_minlength(re, cs, startcode, utf, &this_recurse, countptr,\n                backref_cache);\n              if (d < 0) return d;\n              }\n            }\n          }\n        }\n\n      backref_cache[recno] = d;\n      for (i = backref_cache[0] + 1; i < recno; i++) backref_cache[i] = -1;\n      backref_cache[0] = recno;\n      }\n\n    cc += PRIV(OP_lengths)[*cc];\n\n    /* Handle repeated back references */\n\n    REPEAT_BACK_REFERENCE:\n    switch (*cc)\n      {\n      case OP_CRSTAR:\n      case OP_CRMINSTAR:\n      case OP_CRQUERY:\n      case OP_CRMINQUERY:\n      case OP_CRPOSSTAR:\n      case OP_CRPOSQUERY:\n      min = 0;\n      cc++;\n      break;\n\n      case OP_CRPLUS:\n      case OP_CRMINPLUS:\n      case OP_CRPOSPLUS:\n      min = 1;\n      cc++;\n      break;\n\n      case OP_CRRANGE:\n      case OP_CRMINRANGE:\n      case OP_CRPOSRANGE:\n      min = GET2(cc, 1);\n      cc += 1 + 2 * IMM2_SIZE;\n      break;\n\n      default:\n      min = 1;\n      break;\n      }\n\n     /* Take care not to overflow: (1) min and d are ints, so check that their\n     product is not greater than INT_MAX. (2) branchlength is limited to\n     UINT16_MAX (checked at the top of the loop). */\n\n    if ((d > 0 && (INT_MAX/d) < min) || UINT16_MAX - branchlength < min*d)\n      branchlength = UINT16_MAX;\n    else branchlength += min * d;\n    break;\n\n    /* Recursion always refers to the first occurrence of a subpattern with a\n    given number. Therefore, we can always make use of caching, even when the\n    pattern contains multiple subpatterns with the same number. */\n\n    case OP_RECURSE:\n    cs = ce = startcode + GET(cc, 1);\n    recno = GET2(cs, 1+LINK_SIZE);\n    if (recno == prev_recurse_recno)\n      {\n      branchlength += prev_recurse_d;\n      }\n    else\n      {\n      do ce += GET(ce, 1); while (*ce == OP_ALT);\n      if (cc > cs && cc < ce)    /* Simple recursion */\n        had_recurse = TRUE;\n      else\n        {\n        recurse_check *r = recurses;\n        for (r = recurses; r != NULL; r = r->prev) if (r->group == cs) break;\n        if (r != NULL)          /* Mutual recursion */\n          had_recurse = TRUE;\n        else\n          {\n          this_recurse.prev = recurses;\n          this_recurse.group = cs;\n          prev_recurse_d = find_minlength(re, cs, startcode, utf, &this_recurse,\n            countptr, backref_cache);\n          if (prev_recurse_d < 0) return prev_recurse_d;\n          prev_recurse_recno = recno;\n          branchlength += prev_recurse_d;\n          }\n        }\n      }\n    cc += 1 + LINK_SIZE + once_fudge;\n    once_fudge = 0;\n    break;\n\n    /* Anything else does not or need not match a character. We can get the\n    item's length from the table, but for those that can match zero occurrences\n    of a character, we must take special action for UTF-8 characters. As it\n    happens, the \"NOT\" versions of these opcodes are used at present only for\n    ASCII characters, so they could be omitted from this list. However, in\n    future that may change, so we include them here so as not to leave a\n    gotcha for a future maintainer. */\n\n    case OP_UPTO:\n    case OP_UPTOI:\n    case OP_NOTUPTO:\n    case OP_NOTUPTOI:\n    case OP_MINUPTO:\n    case OP_MINUPTOI:\n    case OP_NOTMINUPTO:\n    case OP_NOTMINUPTOI:\n    case OP_POSUPTO:\n    case OP_POSUPTOI:\n    case OP_NOTPOSUPTO:\n    case OP_NOTPOSUPTOI:\n\n    case OP_STAR:\n    case OP_STARI:\n    case OP_NOTSTAR:\n    case OP_NOTSTARI:\n    case OP_MINSTAR:\n    case OP_MINSTARI:\n    case OP_NOTMINSTAR:\n    case OP_NOTMINSTARI:\n    case OP_POSSTAR:\n    case OP_POSSTARI:\n    case OP_NOTPOSSTAR:\n    case OP_NOTPOSSTARI:\n\n    case OP_QUERY:\n    case OP_QUERYI:\n    case OP_NOTQUERY:\n    case OP_NOTQUERYI:\n    case OP_MINQUERY:\n    case OP_MINQUERYI:\n    case OP_NOTMINQUERY:\n    case OP_NOTMINQUERYI:\n    case OP_POSQUERY:\n    case OP_POSQUERYI:\n    case OP_NOTPOSQUERY:\n    case OP_NOTPOSQUERYI:\n\n    cc += PRIV(OP_lengths)[op];\n#ifdef SUPPORT_UNICODE\n    if (utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);\n#endif\n    break;\n\n    /* Skip these, but we need to add in the name length. */\n\n    case OP_MARK:\n    case OP_COMMIT_ARG:\n    case OP_PRUNE_ARG:\n    case OP_SKIP_ARG:\n    case OP_THEN_ARG:\n    cc += PRIV(OP_lengths)[op] + cc[1];\n    break;\n\n    /* The remaining opcodes are just skipped over. */\n\n    case OP_CLOSE:\n    case OP_COMMIT:\n    case OP_FAIL:\n    case OP_PRUNE:\n    case OP_SET_SOM:\n    case OP_SKIP:\n    case OP_THEN:\n    cc += PRIV(OP_lengths)[op];\n    break;\n\n    /* This should not occur: we list all opcodes explicitly so that when\n    new ones get added they are properly considered. */\n\n    default:\n    PCRE2_DEBUG_UNREACHABLE();\n    return -3;\n    }\n  }\n\nPCRE2_DEBUG_UNREACHABLE(); /* Control should never reach here */\nreturn -3;                 /* Avoid compiler warnings */\n}\n\n\n\n/*************************************************\n*      Set a bit and maybe its alternate case    *\n*************************************************/\n\n/* Given a character, set its first code unit's bit in the table, and also the\ncorresponding bit for the other version of a letter if we are caseless.\n\nArguments:\n  re            points to the regex block\n  p             points to the first code unit of the character\n  caseless      TRUE if caseless\n  utf           TRUE for UTF mode\n  ucp           TRUE for UCP mode\n\nReturns:        pointer after the character\n*/\n\nstatic PCRE2_SPTR\nset_table_bit(pcre2_real_code *re, PCRE2_SPTR p, BOOL caseless, BOOL utf,\n  BOOL ucp)\n{\nuint32_t c = *p++;   /* First code unit */\n\n(void)utf;           /* Stop compiler warnings when UTF not supported */\n(void)ucp;\n\n/* In 16-bit and 32-bit modes, code units greater than 0xff set the bit for\n0xff. */\n\n#if PCRE2_CODE_UNIT_WIDTH != 8\nif (c > 0xff) SET_BIT(0xff); else\n#endif\n\nSET_BIT(c);\n\n/* In UTF-8 or UTF-16 mode, pick up the remaining code units in order to find\nthe end of the character, even when caseless. */\n\n#ifdef SUPPORT_UNICODE\nif (utf)\n  {\n#if PCRE2_CODE_UNIT_WIDTH == 8\n  if (c >= 0xc0) GETUTF8INC(c, p);\n#elif PCRE2_CODE_UNIT_WIDTH == 16\n  if ((c & 0xfc00) == 0xd800) GETUTF16INC(c, p);\n#endif\n  }\n#endif  /* SUPPORT_UNICODE */\n\n/* If caseless, handle the other case of the character. */\n\nif (caseless)\n  {\n#ifdef SUPPORT_UNICODE\n  if (utf || ucp)\n    {\n    c = UCD_OTHERCASE(c);\n#if PCRE2_CODE_UNIT_WIDTH == 8\n    if (utf)\n      {\n      PCRE2_UCHAR buff[6];\n      (void)PRIV(ord2utf)(c, buff);\n      SET_BIT(buff[0]);\n      }\n    else if (c < 256) SET_BIT(c);\n#else  /* 16-bit or 32-bit mode */\n    if (c > 0xff) SET_BIT(0xff); else SET_BIT(c);\n#endif\n    }\n\n  else\n#endif  /* SUPPORT_UNICODE */\n\n  /* Not UTF or UCP */\n\n  if (MAX_255(c)) SET_BIT(re->tables[fcc_offset + c]);\n  }\n\nreturn p;\n}\n\n\n\n/*************************************************\n*     Set bits for a positive character type     *\n*************************************************/\n\n/* This function sets starting bits for a character type. In UTF-8 mode, we can\nonly do a direct setting for bytes less than 128, as otherwise there can be\nconfusion with bytes in the middle of UTF-8 characters. In a \"traditional\"\nenvironment, the tables will only recognize ASCII characters anyway, but in at\nleast one Windows environment, some higher bytes bits were set in the tables.\nSo we deal with that case by considering the UTF-8 encoding.\n\nArguments:\n  re             the regex block\n  cbit type      the type of character wanted\n  table_limit    32 for non-UTF-8; 16 for UTF-8\n\nReturns:         nothing\n*/\n\nstatic void\nset_type_bits(pcre2_real_code *re, int cbit_type, unsigned int table_limit)\n{\nuint32_t c;\nfor (c = 0; c < table_limit; c++)\n  re->start_bitmap[c] |= re->tables[c+cbits_offset+cbit_type];\n#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8\nif (table_limit == 32) return;\nfor (c = 128; c < 256; c++)\n  {\n  if ((re->tables[cbits_offset + c/8] & (1u << (c&7))) != 0)\n    {\n    PCRE2_UCHAR buff[6];\n    (void)PRIV(ord2utf)(c, buff);\n    SET_BIT(buff[0]);\n    }\n  }\n#endif  /* UTF-8 */\n}\n\n\n/*************************************************\n*     Set bits for a negative character type     *\n*************************************************/\n\n/* This function sets starting bits for a negative character type such as \\D.\nIn UTF-8 mode, we can only do a direct setting for bytes less than 128, as\notherwise there can be confusion with bytes in the middle of UTF-8 characters.\nUnlike in the positive case, where we can set appropriate starting bits for\nspecific high-valued UTF-8 characters, in this case we have to set the bits for\nall high-valued characters. The lowest is 0xc2, but we overkill by starting at\n0xc0 (192) for simplicity.\n\nArguments:\n  re             the regex block\n  cbit type      the type of character wanted\n  table_limit    32 for non-UTF-8; 16 for UTF-8\n\nReturns:         nothing\n*/\n\nstatic void\nset_nottype_bits(pcre2_real_code *re, int cbit_type, unsigned int table_limit)\n{\nuint32_t c;\nfor (c = 0; c < table_limit; c++)\n  re->start_bitmap[c] |= (uint8_t)(~(re->tables[c+cbits_offset+cbit_type]));\n#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8\nif (table_limit != 32) for (c = 24; c < 32; c++) re->start_bitmap[c] = 0xff;\n#endif\n}\n\n\n\n#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8\n/*************************************************\n*     Set starting bits for a character list.    *\n*************************************************/\n\n/* This function sets starting bits for a character list. It enumerates\nall characters and character ranges in the character list, and sets\nthe starting bits accordingly.\n\nArguments:\n  code           pointer to the code\n  start_bitmap   pointer to the starting bitmap\n\nReturns:         nothing\n*/\nstatic void\nstudy_char_list(PCRE2_SPTR code, uint8_t *start_bitmap,\n  const uint8_t *char_lists_end)\n{\nuint32_t type, list_ind;\nuint32_t char_list_add = XCL_CHAR_LIST_LOW_16_ADD;\nuint32_t range_start = ~(uint32_t)0, range_end = 0;\nconst uint8_t *next_char;\nPCRE2_UCHAR start_buffer[6], end_buffer[6];\nPCRE2_UCHAR start, end;\n\n/* Only needed in 8-bit mode at the moment. */\ntype = (uint32_t)(code[0] << 8) | code[1];\ncode += 2;\n\n/* Align characters. */\nnext_char = char_lists_end - (GET(code, 0) << 1);\ntype &= XCL_TYPE_MASK;\nlist_ind = 0;\n\nif ((type & XCL_BEGIN_WITH_RANGE) != 0)\n  range_start = XCL_CHAR_LIST_LOW_16_START;\n\nwhile (type > 0)\n  {\n  uint32_t item_count = type & XCL_ITEM_COUNT_MASK;\n\n  if (item_count == XCL_ITEM_COUNT_MASK)\n    {\n    if (list_ind <= 1)\n      {\n      item_count = *(const uint16_t*)next_char;\n      next_char += 2;\n      }\n    else\n      {\n      item_count = *(const uint32_t*)next_char;\n      next_char += 4;\n      }\n    }\n\n  while (item_count > 0)\n    {\n    if (list_ind <= 1)\n      {\n      range_end = *(const uint16_t*)next_char;\n      next_char += 2;\n      }\n    else\n      {\n      range_end = *(const uint32_t*)next_char;\n      next_char += 4;\n      }\n\n    if ((range_end & XCL_CHAR_END) != 0)\n      {\n      range_end = char_list_add + (range_end >> XCL_CHAR_SHIFT);\n\n      PRIV(ord2utf)(range_end, end_buffer);\n      end = end_buffer[0];\n\n      if (range_start < range_end)\n        {\n        PRIV(ord2utf)(range_start, start_buffer);\n        for (start = start_buffer[0]; start <= end; start++)\n          start_bitmap[start / 8] |= (1u << (start & 7));\n        }\n      else\n        start_bitmap[end / 8] |= (1u << (end & 7));\n\n      range_start = ~(uint32_t)0;\n      }\n    else\n      range_start = char_list_add + (range_end >> XCL_CHAR_SHIFT);\n\n    item_count--;\n    }\n\n  list_ind++;\n  type >>= XCL_TYPE_BIT_LEN;\n\n  if (range_start == ~(uint32_t)0)\n    {\n    if ((type & XCL_BEGIN_WITH_RANGE) != 0)\n      {\n      /* In 8 bit mode XCL_CHAR_LIST_HIGH_32_START is not possible. */\n      if (list_ind == 1) range_start = XCL_CHAR_LIST_HIGH_16_START;\n      else range_start = XCL_CHAR_LIST_LOW_32_START;\n      }\n    }\n  else if ((type & XCL_BEGIN_WITH_RANGE) == 0)\n    {\n    PRIV(ord2utf)(range_start, start_buffer);\n\n    /* In 8 bit mode XCL_CHAR_LIST_LOW_32_END and\n    XCL_CHAR_LIST_HIGH_32_END are not possible. */\n    if (list_ind == 1) range_end = XCL_CHAR_LIST_LOW_16_END;\n    else range_end = XCL_CHAR_LIST_HIGH_16_END;\n\n    PRIV(ord2utf)(range_end, end_buffer);\n    end = end_buffer[0];\n\n    for (start = start_buffer[0]; start <= end; start++)\n      start_bitmap[start / 8] |= (1u << (start & 7));\n\n    range_start = ~(uint32_t)0;\n    }\n\n  /* In 8 bit mode XCL_CHAR_LIST_HIGH_32_ADD is not possible. */\n  if (list_ind == 1) char_list_add = XCL_CHAR_LIST_HIGH_16_ADD;\n  else char_list_add = XCL_CHAR_LIST_LOW_32_ADD;\n  }\n}\n#endif\n\n\n\n/*************************************************\n*      Create bitmap of starting code units      *\n*************************************************/\n\n/* This function scans a compiled unanchored expression recursively and\nattempts to build a bitmap of the set of possible starting code units whose\nvalues are less than 256. In 16-bit and 32-bit mode, values above 255 all cause\nthe 255 bit to be set. When calling set[_not]_type_bits() in UTF-8 (sic) mode\nwe pass a value of 16 rather than 32 as the final argument. (See comments in\nthose functions for the reason.)\n\nThe SSB_CONTINUE return is useful for parenthesized groups in patterns such as\n(a*)b where the group provides some optional starting code units but scanning\nmust continue at the outer level to find at least one mandatory code unit. At\nthe outermost level, this function fails unless the result is SSB_DONE.\n\nWe restrict recursion (for nested groups) to 1000 to avoid stack overflow\nissues.\n\nArguments:\n  re           points to the compiled regex block\n  code         points to an expression\n  utf          TRUE if in UTF mode\n  ucp          TRUE if in UCP mode\n  depthptr     pointer to recurse depth\n\nReturns:       SSB_FAIL     => Failed to find any starting code units\n               SSB_DONE     => Found mandatory starting code units\n               SSB_CONTINUE => Found optional starting code units\n               SSB_UNKNOWN  => Hit an unrecognized opcode\n               SSB_TOODEEP  => Recursion is too deep\n*/\n\nstatic int\nset_start_bits(pcre2_real_code *re, PCRE2_SPTR code, BOOL utf, BOOL ucp,\n  int *depthptr)\n{\nuint32_t c;\nint yield = SSB_DONE;\n\n#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8\nint table_limit = utf? 16:32;\n#else\nint table_limit = 32;\n#endif\n\n*depthptr += 1;\nif (*depthptr > 1000) return SSB_TOODEEP;\n\ndo\n  {\n  BOOL try_next = TRUE;\n  PCRE2_SPTR tcode = code + 1 + LINK_SIZE;\n\n  if (*code == OP_CBRA || *code == OP_SCBRA ||\n      *code == OP_CBRAPOS || *code == OP_SCBRAPOS) tcode += IMM2_SIZE;\n\n  while (try_next)    /* Loop for items in this branch */\n    {\n    int rc;\n    PCRE2_SPTR ncode;\n    const uint8_t *classmap = NULL;\n#ifdef SUPPORT_WIDE_CHARS\n    PCRE2_UCHAR xclassflags;\n#endif\n\n    switch(*tcode)\n      {\n      /* If we reach something we don't understand, it means a new opcode has\n      been created that hasn't been added to this function. Hopefully this\n      problem will be discovered during testing. */\n\n      default:\n      return SSB_UNKNOWN;\n\n      /* Fail for a valid opcode that implies no starting bits. */\n\n      case OP_ACCEPT:\n      case OP_ASSERT_ACCEPT:\n      case OP_ALLANY:\n      case OP_ANY:\n      case OP_ANYBYTE:\n      case OP_CIRCM:\n      case OP_CLOSE:\n      case OP_COMMIT:\n      case OP_COMMIT_ARG:\n      case OP_COND:\n      case OP_CREF:\n      case OP_FALSE:\n      case OP_TRUE:\n      case OP_DNCREF:\n      case OP_DNREF:\n      case OP_DNREFI:\n      case OP_DNRREF:\n      case OP_DOLL:\n      case OP_DOLLM:\n      case OP_END:\n      case OP_EOD:\n      case OP_EODN:\n      case OP_EXTUNI:\n      case OP_FAIL:\n      case OP_MARK:\n      case OP_NOT:\n      case OP_NOTEXACT:\n      case OP_NOTEXACTI:\n      case OP_NOTI:\n      case OP_NOTMINPLUS:\n      case OP_NOTMINPLUSI:\n      case OP_NOTMINQUERY:\n      case OP_NOTMINQUERYI:\n      case OP_NOTMINSTAR:\n      case OP_NOTMINSTARI:\n      case OP_NOTMINUPTO:\n      case OP_NOTMINUPTOI:\n      case OP_NOTPLUS:\n      case OP_NOTPLUSI:\n      case OP_NOTPOSPLUS:\n      case OP_NOTPOSPLUSI:\n      case OP_NOTPOSQUERY:\n      case OP_NOTPOSQUERYI:\n      case OP_NOTPOSSTAR:\n      case OP_NOTPOSSTARI:\n      case OP_NOTPOSUPTO:\n      case OP_NOTPOSUPTOI:\n      case OP_NOTPROP:\n      case OP_NOTQUERY:\n      case OP_NOTQUERYI:\n      case OP_NOTSTAR:\n      case OP_NOTSTARI:\n      case OP_NOTUPTO:\n      case OP_NOTUPTOI:\n      case OP_NOT_HSPACE:\n      case OP_NOT_VSPACE:\n      case OP_PRUNE:\n      case OP_PRUNE_ARG:\n      case OP_RECURSE:\n      case OP_REF:\n      case OP_REFI:\n      case OP_REVERSE:\n      case OP_VREVERSE:\n      case OP_RREF:\n      case OP_SCOND:\n      case OP_SET_SOM:\n      case OP_SKIP:\n      case OP_SKIP_ARG:\n      case OP_SOD:\n      case OP_SOM:\n      case OP_THEN:\n      case OP_THEN_ARG:\n      return SSB_FAIL;\n\n      /* OP_CIRC happens only at the start of an anchored branch (multiline ^\n      uses OP_CIRCM). Skip over it. */\n\n      case OP_CIRC:\n      tcode += PRIV(OP_lengths)[OP_CIRC];\n      break;\n\n      /* A \"real\" property test implies no starting bits, but the fake property\n      PT_CLIST identifies a list of characters. These lists are short, as they\n      are used for characters with more than one \"other case\", so there is no\n      point in recognizing them for OP_NOTPROP. */\n\n      case OP_PROP:\n      if (tcode[1] != PT_CLIST) return SSB_FAIL;\n        {\n        const uint32_t *p = PRIV(ucd_caseless_sets) + tcode[2];\n        while ((c = *p++) < NOTACHAR)\n          {\n#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8\n          if (utf)\n            {\n            PCRE2_UCHAR buff[6];\n            (void)PRIV(ord2utf)(c, buff);\n            c = buff[0];\n            }\n#endif\n          if (c > 0xff) SET_BIT(0xff); else SET_BIT(c);\n          }\n        }\n      try_next = FALSE;\n      break;\n\n      /* We can ignore word boundary tests. */\n\n      case OP_WORD_BOUNDARY:\n      case OP_NOT_WORD_BOUNDARY:\n      case OP_UCP_WORD_BOUNDARY:\n      case OP_NOT_UCP_WORD_BOUNDARY:\n      tcode++;\n      break;\n\n      /* For a positive lookahead assertion, inspect what immediately follows,\n      ignoring intermediate assertions and callouts. If the next item is one\n      that sets a mandatory character, skip this assertion. Otherwise, treat it\n      the same as other bracket groups. */\n\n      case OP_ASSERT:\n      case OP_ASSERT_NA:\n      ncode = tcode + GET(tcode, 1);\n      while (*ncode == OP_ALT) ncode += GET(ncode, 1);\n      ncode += 1 + LINK_SIZE;\n\n      /* Skip irrelevant items */\n\n      for (BOOL done = FALSE; !done;)\n        {\n        switch (*ncode)\n          {\n          case OP_ASSERT:\n          case OP_ASSERT_NOT:\n          case OP_ASSERTBACK:\n          case OP_ASSERTBACK_NOT:\n          case OP_ASSERT_NA:\n          case OP_ASSERTBACK_NA:\n          case OP_ASSERT_SCS:\n          ncode += GET(ncode, 1);\n          while (*ncode == OP_ALT) ncode += GET(ncode, 1);\n          ncode += 1 + LINK_SIZE;\n          break;\n\n          case OP_WORD_BOUNDARY:\n          case OP_NOT_WORD_BOUNDARY:\n          case OP_UCP_WORD_BOUNDARY:\n          case OP_NOT_UCP_WORD_BOUNDARY:\n          ncode++;\n          break;\n\n          case OP_CALLOUT:\n          ncode += PRIV(OP_lengths)[OP_CALLOUT];\n          break;\n\n          case OP_CALLOUT_STR:\n          ncode += GET(ncode, 1 + 2*LINK_SIZE);\n          break;\n\n          default:\n          done = TRUE;\n          break;\n          }\n        }\n\n      /* Now check the next significant item. */\n\n      switch(*ncode)\n        {\n        default:\n        break;\n\n        case OP_PROP:\n        if (ncode[1] != PT_CLIST) break;\n        /* Fall through */\n        case OP_ANYNL:\n        case OP_CHAR:\n        case OP_CHARI:\n        case OP_EXACT:\n        case OP_EXACTI:\n        case OP_HSPACE:\n        case OP_MINPLUS:\n        case OP_MINPLUSI:\n        case OP_PLUS:\n        case OP_PLUSI:\n        case OP_POSPLUS:\n        case OP_POSPLUSI:\n        case OP_VSPACE:\n        /* Note that these types will only be present in non-UCP mode. */\n        case OP_DIGIT:\n        case OP_NOT_DIGIT:\n        case OP_WORDCHAR:\n        case OP_NOT_WORDCHAR:\n        case OP_WHITESPACE:\n        case OP_NOT_WHITESPACE:\n        tcode = ncode;\n        continue;   /* With the following significant opcode */\n        }\n      /* Fall through */\n\n      /* For a group bracket or a positive assertion without an immediately\n      following mandatory setting, recurse to set bits from within the\n      subpattern. If it can't find anything, we have to give up. If it finds\n      some mandatory character(s), we are done for this branch. Otherwise,\n      carry on scanning after the subpattern. */\n\n      case OP_BRA:\n      case OP_SBRA:\n      case OP_CBRA:\n      case OP_SCBRA:\n      case OP_BRAPOS:\n      case OP_SBRAPOS:\n      case OP_CBRAPOS:\n      case OP_SCBRAPOS:\n      case OP_ONCE:\n      case OP_SCRIPT_RUN:\n      rc = set_start_bits(re, tcode, utf, ucp, depthptr);\n      if (rc == SSB_DONE)\n        {\n        try_next = FALSE;\n        }\n      else if (rc == SSB_CONTINUE)\n        {\n        do tcode += GET(tcode, 1); while (*tcode == OP_ALT);\n        tcode += 1 + LINK_SIZE;\n        }\n      else return rc;   /* FAIL, UNKNOWN, or TOODEEP */\n      break;\n\n      /* If we hit ALT or KET, it means we haven't found anything mandatory in\n      this branch, though we might have found something optional. For ALT, we\n      continue with the next alternative, but we have to arrange that the final\n      result from subpattern is SSB_CONTINUE rather than SSB_DONE. For KET,\n      return SSB_CONTINUE: if this is the top level, that indicates failure,\n      but after a nested subpattern, it causes scanning to continue. */\n\n      case OP_ALT:\n      yield = SSB_CONTINUE;\n      try_next = FALSE;\n      break;\n\n      case OP_KET:\n      case OP_KETRMAX:\n      case OP_KETRMIN:\n      case OP_KETRPOS:\n      return SSB_CONTINUE;\n\n      /* Skip over callout */\n\n      case OP_CALLOUT:\n      tcode += PRIV(OP_lengths)[OP_CALLOUT];\n      break;\n\n      case OP_CALLOUT_STR:\n      tcode += GET(tcode, 1 + 2*LINK_SIZE);\n      break;\n\n      /* Skip over lookbehind, negative lookahead, and scan substring\n      assertions */\n\n      case OP_ASSERT_NOT:\n      case OP_ASSERTBACK:\n      case OP_ASSERTBACK_NOT:\n      case OP_ASSERTBACK_NA:\n      case OP_ASSERT_SCS:\n      do tcode += GET(tcode, 1); while (*tcode == OP_ALT);\n      tcode += 1 + LINK_SIZE;\n      break;\n\n      /* BRAZERO does the bracket, but carries on. */\n\n      case OP_BRAZERO:\n      case OP_BRAMINZERO:\n      case OP_BRAPOSZERO:\n      rc = set_start_bits(re, ++tcode, utf, ucp, depthptr);\n      if (rc == SSB_FAIL || rc == SSB_UNKNOWN || rc == SSB_TOODEEP) return rc;\n      do tcode += GET(tcode,1); while (*tcode == OP_ALT);\n      tcode += 1 + LINK_SIZE;\n      break;\n\n      /* SKIPZERO skips the bracket. */\n\n      case OP_SKIPZERO:\n      tcode++;\n      do tcode += GET(tcode,1); while (*tcode == OP_ALT);\n      tcode += 1 + LINK_SIZE;\n      break;\n\n      /* Single-char * or ? sets the bit and tries the next item */\n\n      case OP_STAR:\n      case OP_MINSTAR:\n      case OP_POSSTAR:\n      case OP_QUERY:\n      case OP_MINQUERY:\n      case OP_POSQUERY:\n      tcode = set_table_bit(re, tcode + 1, FALSE, utf, ucp);\n      break;\n\n      case OP_STARI:\n      case OP_MINSTARI:\n      case OP_POSSTARI:\n      case OP_QUERYI:\n      case OP_MINQUERYI:\n      case OP_POSQUERYI:\n      tcode = set_table_bit(re, tcode + 1, TRUE, utf, ucp);\n      break;\n\n      /* Single-char upto sets the bit and tries the next */\n\n      case OP_UPTO:\n      case OP_MINUPTO:\n      case OP_POSUPTO:\n      tcode = set_table_bit(re, tcode + 1 + IMM2_SIZE, FALSE, utf, ucp);\n      break;\n\n      case OP_UPTOI:\n      case OP_MINUPTOI:\n      case OP_POSUPTOI:\n      tcode = set_table_bit(re, tcode + 1 + IMM2_SIZE, TRUE, utf, ucp);\n      break;\n\n      /* At least one single char sets the bit and stops */\n\n      case OP_EXACT:\n      tcode += IMM2_SIZE;\n      /* Fall through */\n      case OP_CHAR:\n      case OP_PLUS:\n      case OP_MINPLUS:\n      case OP_POSPLUS:\n      (void)set_table_bit(re, tcode + 1, FALSE, utf, ucp);\n      try_next = FALSE;\n      break;\n\n      case OP_EXACTI:\n      tcode += IMM2_SIZE;\n      /* Fall through */\n      case OP_CHARI:\n      case OP_PLUSI:\n      case OP_MINPLUSI:\n      case OP_POSPLUSI:\n      (void)set_table_bit(re, tcode + 1, TRUE, utf, ucp);\n      try_next = FALSE;\n      break;\n\n      /* Special spacing and line-terminating items. These recognize specific\n      lists of characters. The difference between VSPACE and ANYNL is that the\n      latter can match the two-character CRLF sequence, but that is not\n      relevant for finding the first character, so their code here is\n      identical. */\n\n      case OP_HSPACE:\n      SET_BIT(CHAR_HT);\n      SET_BIT(CHAR_SPACE);\n\n      /* For the 16-bit and 32-bit libraries (which can never be EBCDIC), set\n      the bits for 0xA0 and for code units >= 255, independently of UTF. */\n\n#if PCRE2_CODE_UNIT_WIDTH != 8\n      SET_BIT(0xA0);\n      SET_BIT(0xFF);\n#else\n      /* For the 8-bit library in UTF-8 mode, set the bits for the first code\n      units of horizontal space characters. */\n\n#ifdef SUPPORT_UNICODE\n      if (utf)\n        {\n        SET_BIT(0xC2);  /* For U+00A0 */\n        SET_BIT(0xE1);  /* For U+1680, U+180E */\n        SET_BIT(0xE2);  /* For U+2000 - U+200A, U+202F, U+205F */\n        SET_BIT(0xE3);  /* For U+3000 */\n        }\n      else\n#endif\n      /* For the 8-bit library not in UTF-8 mode, set the bit for 0xA0, unless\n      the code is EBCDIC. */\n        {\n#ifndef EBCDIC\n        SET_BIT(0xA0);\n#endif  /* Not EBCDIC */\n        }\n#endif  /* 8-bit support */\n\n      try_next = FALSE;\n      break;\n\n      case OP_ANYNL:\n      case OP_VSPACE:\n      SET_BIT(CHAR_LF);\n      SET_BIT(CHAR_VT);\n      SET_BIT(CHAR_FF);\n      SET_BIT(CHAR_CR);\n\n      /* For the 16-bit and 32-bit libraries (which can never be EBCDIC), set\n      the bits for NEL and for code units >= 255, independently of UTF. */\n\n#if PCRE2_CODE_UNIT_WIDTH != 8\n      SET_BIT(CHAR_NEL);\n      SET_BIT(0xFF);\n#else\n      /* For the 8-bit library in UTF-8 mode, set the bits for the first code\n      units of vertical space characters. */\n\n#ifdef SUPPORT_UNICODE\n      if (utf)\n        {\n        SET_BIT(0xC2);  /* For U+0085 (NEL) */\n        SET_BIT(0xE2);  /* For U+2028, U+2029 */\n        }\n      else\n#endif\n      /* For the 8-bit library not in UTF-8 mode, set the bit for NEL. */\n        {\n        SET_BIT(CHAR_NEL);\n        }\n#endif  /* 8-bit support */\n\n      try_next = FALSE;\n      break;\n\n      /* Single character types set the bits and stop. Note that if PCRE2_UCP\n      is set, we do not see these opcodes because \\d etc are converted to\n      properties. Therefore, these apply in the case when only characters less\n      than 256 are recognized to match the types. */\n\n      case OP_NOT_DIGIT:\n      set_nottype_bits(re, cbit_digit, table_limit);\n      try_next = FALSE;\n      break;\n\n      case OP_DIGIT:\n      set_type_bits(re, cbit_digit, table_limit);\n      try_next = FALSE;\n      break;\n\n      case OP_NOT_WHITESPACE:\n      set_nottype_bits(re, cbit_space, table_limit);\n      try_next = FALSE;\n      break;\n\n      case OP_WHITESPACE:\n      set_type_bits(re, cbit_space, table_limit);\n      try_next = FALSE;\n      break;\n\n      case OP_NOT_WORDCHAR:\n      set_nottype_bits(re, cbit_word, table_limit);\n      try_next = FALSE;\n      break;\n\n      case OP_WORDCHAR:\n      set_type_bits(re, cbit_word, table_limit);\n      try_next = FALSE;\n      break;\n\n      /* One or more character type fudges the pointer and restarts, knowing\n      it will hit a single character type and stop there. */\n\n      case OP_TYPEPLUS:\n      case OP_TYPEMINPLUS:\n      case OP_TYPEPOSPLUS:\n      tcode++;\n      break;\n\n      case OP_TYPEEXACT:\n      tcode += 1 + IMM2_SIZE;\n      break;\n\n      /* Zero or more repeats of character types set the bits and then\n      try again. */\n\n      case OP_TYPEUPTO:\n      case OP_TYPEMINUPTO:\n      case OP_TYPEPOSUPTO:\n      tcode += IMM2_SIZE;  /* Fall through */\n\n      case OP_TYPESTAR:\n      case OP_TYPEMINSTAR:\n      case OP_TYPEPOSSTAR:\n      case OP_TYPEQUERY:\n      case OP_TYPEMINQUERY:\n      case OP_TYPEPOSQUERY:\n      switch(tcode[1])\n        {\n        default:\n        case OP_ANY:\n        case OP_ALLANY:\n        return SSB_FAIL;\n\n        case OP_HSPACE:\n        SET_BIT(CHAR_HT);\n        SET_BIT(CHAR_SPACE);\n\n        /* For the 16-bit and 32-bit libraries (which can never be EBCDIC), set\n        the bits for 0xA0 and for code units >= 255, independently of UTF. */\n\n#if PCRE2_CODE_UNIT_WIDTH != 8\n        SET_BIT(0xA0);\n        SET_BIT(0xFF);\n#else\n        /* For the 8-bit library in UTF-8 mode, set the bits for the first code\n        units of horizontal space characters. */\n\n#ifdef SUPPORT_UNICODE\n        if (utf)\n          {\n          SET_BIT(0xC2);  /* For U+00A0 */\n          SET_BIT(0xE1);  /* For U+1680, U+180E */\n          SET_BIT(0xE2);  /* For U+2000 - U+200A, U+202F, U+205F */\n          SET_BIT(0xE3);  /* For U+3000 */\n          }\n        else\n#endif\n        /* For the 8-bit library not in UTF-8 mode, set the bit for 0xA0, unless\n        the code is EBCDIC. */\n          {\n#ifndef EBCDIC\n          SET_BIT(0xA0);\n#endif  /* Not EBCDIC */\n          }\n#endif  /* 8-bit support */\n        break;\n\n        case OP_ANYNL:\n        case OP_VSPACE:\n        SET_BIT(CHAR_LF);\n        SET_BIT(CHAR_VT);\n        SET_BIT(CHAR_FF);\n        SET_BIT(CHAR_CR);\n\n        /* For the 16-bit and 32-bit libraries (which can never be EBCDIC), set\n        the bits for NEL and for code units >= 255, independently of UTF. */\n\n#if PCRE2_CODE_UNIT_WIDTH != 8\n        SET_BIT(CHAR_NEL);\n        SET_BIT(0xFF);\n#else\n        /* For the 8-bit library in UTF-8 mode, set the bits for the first code\n        units of vertical space characters. */\n\n#ifdef SUPPORT_UNICODE\n        if (utf)\n          {\n          SET_BIT(0xC2);  /* For U+0085 (NEL) */\n          SET_BIT(0xE2);  /* For U+2028, U+2029 */\n          }\n        else\n#endif\n        /* For the 8-bit library not in UTF-8 mode, set the bit for NEL. */\n          {\n          SET_BIT(CHAR_NEL);\n          }\n#endif  /* 8-bit support */\n        break;\n\n        case OP_NOT_DIGIT:\n        set_nottype_bits(re, cbit_digit, table_limit);\n        break;\n\n        case OP_DIGIT:\n        set_type_bits(re, cbit_digit, table_limit);\n        break;\n\n        case OP_NOT_WHITESPACE:\n        set_nottype_bits(re, cbit_space, table_limit);\n        break;\n\n        case OP_WHITESPACE:\n        set_type_bits(re, cbit_space, table_limit);\n        break;\n\n        case OP_NOT_WORDCHAR:\n        set_nottype_bits(re, cbit_word, table_limit);\n        break;\n\n        case OP_WORDCHAR:\n        set_type_bits(re, cbit_word, table_limit);\n        break;\n        }\n\n      tcode += 2;\n      break;\n\n      /* Set-based ECLASS: treat it the same as a \"complex\" XCLASS; give up. */\n\n#ifdef SUPPORT_WIDE_CHARS\n      case OP_ECLASS:\n      return SSB_FAIL;\n#endif\n\n      /* Extended class: if there are any property checks, or if this is a\n      negative XCLASS without a map, give up. If there are no property checks,\n      there must be wide characters on the XCLASS list, because otherwise an\n      XCLASS would not have been created. This means that code points >= 255\n      are potential starters. In the UTF-8 case we can scan them and set bits\n      for the relevant leading bytes. */\n\n#ifdef SUPPORT_WIDE_CHARS\n      case OP_XCLASS:\n      xclassflags = tcode[1 + LINK_SIZE];\n      if ((xclassflags & XCL_HASPROP) != 0 ||\n          (xclassflags & (XCL_MAP|XCL_NOT)) == XCL_NOT)\n        return SSB_FAIL;\n\n      /* We have a positive XCLASS or a negative one without a map. Set up the\n      map pointer if there is one, and fall through. */\n\n      classmap = ((xclassflags & XCL_MAP) == 0)? NULL :\n        (const uint8_t *)(tcode + 1 + LINK_SIZE + 1);\n\n      /* In UTF-8 mode, scan the character list and set bits for leading bytes,\n      then jump to handle the map. */\n\n#if PCRE2_CODE_UNIT_WIDTH == 8\n      if (utf && (xclassflags & XCL_NOT) == 0)\n        {\n        PCRE2_UCHAR b, e;\n        PCRE2_SPTR p = tcode + 1 + LINK_SIZE + 1 + ((classmap == NULL)? 0:32);\n        tcode += GET(tcode, 1);\n\n        if (*p >= XCL_LIST)\n          {\n          study_char_list(p, re->start_bitmap,\n            ((const uint8_t *)re + re->code_start));\n          goto HANDLE_CLASSMAP;\n          }\n\n        for (;;) switch (*p++)\n          {\n          case XCL_SINGLE:\n          b = *p++;\n          while ((*p & 0xc0) == 0x80) p++;\n          re->start_bitmap[b/8] |= (1u << (b&7));\n          break;\n\n          case XCL_RANGE:\n          b = *p++;\n          while ((*p & 0xc0) == 0x80) p++;\n          e = *p++;\n          while ((*p & 0xc0) == 0x80) p++;\n          for (; b <= e; b++)\n            re->start_bitmap[b/8] |= (1u << (b&7));\n          break;\n\n          case XCL_END:\n          goto HANDLE_CLASSMAP;\n\n          default:\n          PCRE2_DEBUG_UNREACHABLE();\n          return SSB_UNKNOWN;   /* Internal error, should not occur */\n          }\n        }\n#endif  /* SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8 */\n#endif  /* SUPPORT_WIDE_CHARS */\n\n      /* It seems that the fall through comment must be outside the #ifdef if\n      it is to avoid the gcc compiler warning. */\n\n      /* Fall through */\n\n      /* Enter here for a negative non-XCLASS. In the 8-bit library, if we are\n      in UTF mode, any byte with a value >= 0xc4 is a potentially valid starter\n      because it starts a character with a value > 255. In 8-bit non-UTF mode,\n      there is no difference between CLASS and NCLASS. In all other wide\n      character modes, set the 0xFF bit to indicate code units >= 255. */\n\n      case OP_NCLASS:\n#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8\n      if (utf)\n        {\n        re->start_bitmap[24] |= 0xf0;            /* Bits for 0xc4 - 0xc8 */\n        memset(re->start_bitmap+25, 0xff, 7);    /* Bits for 0xc9 - 0xff */\n        }\n#elif PCRE2_CODE_UNIT_WIDTH != 8\n      SET_BIT(0xFF);                             /* For characters >= 255 */\n#endif\n      /* Fall through */\n\n      /* Enter here for a positive non-XCLASS. If we have fallen through from\n      an XCLASS, classmap will already be set; just advance the code pointer.\n      Otherwise, set up classmap for a a non-XCLASS and advance past it. */\n\n      case OP_CLASS:\n      if (*tcode == OP_XCLASS) tcode += GET(tcode, 1); else\n        {\n        classmap = (const uint8_t *)(++tcode);\n        tcode += 32 / sizeof(PCRE2_UCHAR);\n        }\n\n      /* When wide characters are supported, classmap may be NULL. In UTF-8\n      (sic) mode, the bits in a class bit map correspond to character values,\n      not to byte values. However, the bit map we are constructing is for byte\n      values. So we have to do a conversion for characters whose code point is\n      greater than 127. In fact, there are only two possible starting bytes for\n      characters in the range 128 - 255. */\n\n#if defined SUPPORT_WIDE_CHARS && PCRE2_CODE_UNIT_WIDTH == 8\n      HANDLE_CLASSMAP:\n#endif\n      if (classmap != NULL)\n        {\n#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8\n        if (utf)\n          {\n          for (c = 0; c < 16; c++) re->start_bitmap[c] |= classmap[c];\n          for (c = 128; c < 256; c++)\n            {\n            if ((classmap[c/8] & (1u << (c&7))) != 0)\n              {\n              int d = (c >> 6) | 0xc0;                 /* Set bit for this starter */\n              re->start_bitmap[d/8] |= (1u << (d&7));  /* and then skip on to the */\n              c = (c & 0xc0) + 0x40 - 1;               /* next relevant character. */\n              }\n            }\n          }\n        else\n#endif\n        /* In all modes except UTF-8, the two bit maps are compatible. */\n\n          {\n          for (c = 0; c < 32; c++) re->start_bitmap[c] |= classmap[c];\n          }\n        }\n\n      /* Act on what follows the class. For a zero minimum repeat, continue;\n      otherwise stop processing. */\n\n      switch (*tcode)\n        {\n        case OP_CRSTAR:\n        case OP_CRMINSTAR:\n        case OP_CRQUERY:\n        case OP_CRMINQUERY:\n        case OP_CRPOSSTAR:\n        case OP_CRPOSQUERY:\n        tcode++;\n        break;\n\n        case OP_CRRANGE:\n        case OP_CRMINRANGE:\n        case OP_CRPOSRANGE:\n        if (GET2(tcode, 1) == 0) tcode += 1 + 2 * IMM2_SIZE;\n          else try_next = FALSE;\n        break;\n\n        default:\n        try_next = FALSE;\n        break;\n        }\n      break; /* End of class handling case */\n      }      /* End of switch for opcodes */\n    }        /* End of try_next loop */\n\n  code += GET(code, 1);   /* Advance to next branch */\n  }\nwhile (*code == OP_ALT);\n\nreturn yield;\n}\n\n\n\n/*************************************************\n*          Study a compiled expression           *\n*************************************************/\n\n/* This function is handed a compiled expression that it must study to produce\ninformation that will speed up the matching.\n\nArgument:\n  re       points to the compiled expression\n\nReturns:   0 normally; non-zero should never normally occur\n           1 unknown opcode in set_start_bits\n           2 missing capturing bracket\n           3 unknown opcode in find_minlength\n*/\n\nint\nPRIV(study)(pcre2_real_code *re)\n{\nint count = 0;\nPCRE2_UCHAR *code;\nBOOL utf = (re->overall_options & PCRE2_UTF) != 0;\nBOOL ucp = (re->overall_options & PCRE2_UCP) != 0;\n\n/* Find start of compiled code */\n\ncode = (PCRE2_UCHAR *)((uint8_t *)re + re->code_start);\n\n/* For a pattern that has a first code unit, or a multiline pattern that\nmatches only at \"line start\", there is no point in seeking a list of starting\ncode units. */\n\nif ((re->flags & (PCRE2_FIRSTSET|PCRE2_STARTLINE)) == 0)\n  {\n  int depth = 0;\n  int rc = set_start_bits(re, code, utf, ucp, &depth);\n  if (rc == SSB_UNKNOWN)\n    {\n    PCRE2_DEBUG_UNREACHABLE();\n    return 1;\n    }\n\n  /* If a list of starting code units was set up, scan the list to see if only\n  one or two were listed. Having only one listed is rare because usually a\n  single starting code unit will have been recognized and PCRE2_FIRSTSET set.\n  If two are listed, see if they are caseless versions of the same character;\n  if so we can replace the list with a caseless first code unit. This gives\n  better performance and is plausibly worth doing for patterns such as [Ww]ord\n  or (word|WORD). */\n\n  if (rc == SSB_DONE)\n    {\n    int i;\n    int a = -1;\n    int b = -1;\n    uint8_t *p = re->start_bitmap;\n    uint32_t flags = PCRE2_FIRSTMAPSET;\n\n    for (i = 0; i < 256; p++, i += 8)\n      {\n      uint8_t x = *p;\n      if (x != 0)\n        {\n        int c;\n        uint8_t y = x & (~x + 1);   /* Least significant bit */\n        if (y != x) goto DONE;      /* More than one bit set */\n\n        /* In the 16-bit and 32-bit libraries, the bit for 0xff means \"0xff and\n        all wide characters\", so we cannot use it here. */\n\n#if PCRE2_CODE_UNIT_WIDTH != 8\n        if (i == 248 && x == 0x80) goto DONE;\n#endif\n\n        /* Compute the character value */\n\n        c = i;\n        switch (x)\n          {\n          case 1:   break;\n          case 2:   c += 1; break;  case 4:  c += 2; break;\n          case 8:   c += 3; break;  case 16: c += 4; break;\n          case 32:  c += 5; break;  case 64: c += 6; break;\n          case 128: c += 7; break;\n          }\n\n        /* c contains the code unit value, in the range 0-255. In 8-bit UTF\n        mode, only values < 128 can be used. In all the other cases, c is a\n        character value. */\n\n#if PCRE2_CODE_UNIT_WIDTH == 8\n        if (utf && c > 127) goto DONE;\n#endif\n        if (a < 0) a = c;   /* First one found, save in a */\n        else if (b < 0)     /* Second one found */\n          {\n          int d = TABLE_GET((unsigned int)c, re->tables + fcc_offset, c);\n\n#ifdef SUPPORT_UNICODE\n          if (utf || ucp)\n            {\n            if (UCD_CASESET(c) != 0) goto DONE;     /* Multiple case set */\n            if (c > 127) d = UCD_OTHERCASE(c);\n            }\n#endif  /* SUPPORT_UNICODE */\n\n          if (d != a) goto DONE;   /* Not the other case of a */\n          b = c;                   /* Save second in b */\n          }\n        else goto DONE;   /* More than two characters found */\n        }\n      }\n\n    /* Replace the start code unit bits with a first code unit. If it is the\n    same as a required later code unit, then clear the required later code\n    unit. This is because a search for a required code unit starts after an\n    explicit first code unit, but at a code unit found from the bitmap.\n    Patterns such as /a*a/ don't work if both the start unit and required\n    unit are the same. */\n\n    if (a >= 0) {\n      if ((re->flags & PCRE2_LASTSET) && (re->last_codeunit == (uint32_t)a || (b >= 0 && re->last_codeunit == (uint32_t)b))) {\n        re->flags &= ~(PCRE2_LASTSET | PCRE2_LASTCASELESS);\n        re->last_codeunit = 0;\n      }\n      re->first_codeunit = a;\n      flags = PCRE2_FIRSTSET;\n      if (b >= 0) flags |= PCRE2_FIRSTCASELESS;\n    }\n\n    DONE:\n    re->flags |= flags;\n    }\n  }\n\n/* Find the minimum length of subject string. If the pattern can match an empty\nstring, the minimum length is already known. If the pattern contains (*ACCEPT)\nall bets are off, and we don't even try to find a minimum length. If there are\nmore back references than the size of the vector we are going to cache them in,\ndo nothing. A pattern that complicated will probably take a long time to\nanalyze and may in any case turn out to be too complicated. Note that back\nreference minima are held as 16-bit numbers. */\n\nif ((re->flags & (PCRE2_MATCH_EMPTY|PCRE2_HASACCEPT)) == 0 &&\n     re->top_backref <= MAX_CACHE_BACKREF)\n  {\n  int min;\n  int backref_cache[MAX_CACHE_BACKREF+1];\n  backref_cache[0] = 0;    /* Highest one that is set */\n  min = find_minlength(re, code, code, utf, NULL, &count, backref_cache);\n  switch(min)\n    {\n    case -1:  /* \\C in UTF mode or over-complex regex */\n    break;    /* Leave minlength unchanged (will be zero) */\n\n    case -2:\n    PCRE2_DEBUG_UNREACHABLE();\n    return 2; /* missing capturing bracket */\n\n    case -3:\n    PCRE2_DEBUG_UNREACHABLE();\n    return 3; /* unrecognized opcode */\n\n    default:\n    re->minlength = (min > UINT16_MAX)? UINT16_MAX : min;\n    break;\n    }\n  }\n\nreturn 0;\n}\n\n/* End of pcre2_study.c */\n"
  },
  {
    "path": "Libraries/PCRE/pcre2_substring.c",
    "content": "/*************************************************\n*      Perl-Compatible Regular Expressions       *\n*************************************************/\n\n/* PCRE is a library of functions to support regular expressions whose syntax\nand semantics are as close as possible to those of the Perl 5 language.\n\n                       Written by Philip Hazel\n     Original API code Copyright (c) 1997-2012 University of Cambridge\n          New API code Copyright (c) 2016-2024 University of Cambridge\n\n-----------------------------------------------------------------------------\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n    * Redistributions of source code must retain the above copyright notice,\n      this list of conditions and the following disclaimer.\n\n    * Redistributions in binary form must reproduce the above copyright\n      notice, this list of conditions and the following disclaimer in the\n      documentation and/or other materials provided with the distribution.\n\n    * Neither the name of the University of Cambridge nor the names of its\n      contributors may be used to endorse or promote products derived from\n      this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\nLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\nCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\nSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\nINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\nCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGE.\n-----------------------------------------------------------------------------\n*/\n\n\n#ifdef HAVE_CONFIG_H\n#include \"config.h\"\n#endif\n\n#include \"pcre2_internal.h\"\n\n\n\n/*************************************************\n*   Copy named captured string to given buffer   *\n*************************************************/\n\n/* This function copies a single captured substring into a given buffer,\nidentifying it by name. If the regex permits duplicate names, the first\nsubstring that is set is chosen.\n\nArguments:\n  match_data     points to the match data\n  stringname     the name of the required substring\n  buffer         where to put the substring\n  sizeptr        the size of the buffer, updated to the size of the substring\n\nReturns:         if successful: zero\n                 if not successful, a negative error code:\n                   (1) an error from nametable_scan()\n                   (2) an error from copy_bynumber()\n                   (3) PCRE2_ERROR_UNAVAILABLE: no group is in ovector\n                   (4) PCRE2_ERROR_UNSET: all named groups in ovector are unset\n*/\n\nPCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION\npcre2_substring_copy_byname(pcre2_match_data *match_data, PCRE2_SPTR stringname,\n  PCRE2_UCHAR *buffer, PCRE2_SIZE *sizeptr)\n{\nPCRE2_SPTR first, last, entry;\nint failrc, entrysize;\nif (match_data->matchedby == PCRE2_MATCHEDBY_DFA_INTERPRETER)\n  return PCRE2_ERROR_DFA_UFUNC;\nentrysize = pcre2_substring_nametable_scan(match_data->code, stringname,\n  &first, &last);\nif (entrysize < 0) return entrysize;\nfailrc = PCRE2_ERROR_UNAVAILABLE;\nfor (entry = first; entry <= last; entry += entrysize)\n  {\n  uint32_t n = GET2(entry, 0);\n  if (n < match_data->oveccount)\n    {\n    if (match_data->ovector[n*2] != PCRE2_UNSET)\n      return pcre2_substring_copy_bynumber(match_data, n, buffer, sizeptr);\n    failrc = PCRE2_ERROR_UNSET;\n    }\n  }\nreturn failrc;\n}\n\n\n\n/*************************************************\n*  Copy numbered captured string to given buffer *\n*************************************************/\n\n/* This function copies a single captured substring into a given buffer,\nidentifying it by number.\n\nArguments:\n  match_data     points to the match data\n  stringnumber   the number of the required substring\n  buffer         where to put the substring\n  sizeptr        the size of the buffer, updated to the size of the substring\n\nReturns:         if successful: 0\n                 if not successful, a negative error code:\n                   PCRE2_ERROR_NOMEMORY: buffer too small\n                   PCRE2_ERROR_NOSUBSTRING: no such substring\n                   PCRE2_ERROR_UNAVAILABLE: ovector too small\n                   PCRE2_ERROR_UNSET: substring is not set\n*/\n\nPCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION\npcre2_substring_copy_bynumber(pcre2_match_data *match_data,\n  uint32_t stringnumber, PCRE2_UCHAR *buffer, PCRE2_SIZE *sizeptr)\n{\nint rc;\nPCRE2_SIZE size;\nrc = pcre2_substring_length_bynumber(match_data, stringnumber, &size);\nif (rc < 0) return rc;\nif (size + 1 > *sizeptr) return PCRE2_ERROR_NOMEMORY;\nmemcpy(buffer, match_data->subject + match_data->ovector[stringnumber*2],\n  CU2BYTES(size));\nbuffer[size] = 0;\n*sizeptr = size;\nreturn 0;\n}\n\n\n\n/*************************************************\n*          Extract named captured string         *\n*************************************************/\n\n/* This function copies a single captured substring, identified by name, into\nnew memory. If the regex permits duplicate names, the first substring that is\nset is chosen.\n\nArguments:\n  match_data     pointer to match_data\n  stringname     the name of the required substring\n  stringptr      where to put the pointer to the new memory\n  sizeptr        where to put the length of the substring\n\nReturns:         if successful: zero\n                 if not successful, a negative value:\n                   (1) an error from nametable_scan()\n                   (2) an error from get_bynumber()\n                   (3) PCRE2_ERROR_UNAVAILABLE: no group is in ovector\n                   (4) PCRE2_ERROR_UNSET: all named groups in ovector are unset\n*/\n\nPCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION\npcre2_substring_get_byname(pcre2_match_data *match_data,\n  PCRE2_SPTR stringname, PCRE2_UCHAR **stringptr, PCRE2_SIZE *sizeptr)\n{\nPCRE2_SPTR first, last, entry;\nint failrc, entrysize;\nif (match_data->matchedby == PCRE2_MATCHEDBY_DFA_INTERPRETER)\n  return PCRE2_ERROR_DFA_UFUNC;\nentrysize = pcre2_substring_nametable_scan(match_data->code, stringname,\n  &first, &last);\nif (entrysize < 0) return entrysize;\nfailrc = PCRE2_ERROR_UNAVAILABLE;\nfor (entry = first; entry <= last; entry += entrysize)\n  {\n  uint32_t n = GET2(entry, 0);\n  if (n < match_data->oveccount)\n    {\n    if (match_data->ovector[n*2] != PCRE2_UNSET)\n      return pcre2_substring_get_bynumber(match_data, n, stringptr, sizeptr);\n    failrc = PCRE2_ERROR_UNSET;\n    }\n  }\nreturn failrc;\n}\n\n\n\n/*************************************************\n*      Extract captured string to new memory     *\n*************************************************/\n\n/* This function copies a single captured substring into a piece of new\nmemory.\n\nArguments:\n  match_data     points to match data\n  stringnumber   the number of the required substring\n  stringptr      where to put a pointer to the new memory\n  sizeptr        where to put the size of the substring\n\nReturns:         if successful: 0\n                 if not successful, a negative error code:\n                   PCRE2_ERROR_NOMEMORY: failed to get memory\n                   PCRE2_ERROR_NOSUBSTRING: no such substring\n                   PCRE2_ERROR_UNAVAILABLE: ovector too small\n                   PCRE2_ERROR_UNSET: substring is not set\n*/\n\nPCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION\npcre2_substring_get_bynumber(pcre2_match_data *match_data,\n  uint32_t stringnumber, PCRE2_UCHAR **stringptr, PCRE2_SIZE *sizeptr)\n{\nint rc;\nPCRE2_SIZE size;\nPCRE2_UCHAR *yield;\nrc = pcre2_substring_length_bynumber(match_data, stringnumber, &size);\nif (rc < 0) return rc;\nyield = PRIV(memctl_malloc)(sizeof(pcre2_memctl) +\n  (size + 1)*PCRE2_CODE_UNIT_WIDTH, (pcre2_memctl *)match_data);\nif (yield == NULL) return PCRE2_ERROR_NOMEMORY;\nyield = (PCRE2_UCHAR *)(((char *)yield) + sizeof(pcre2_memctl));\nmemcpy(yield, match_data->subject + match_data->ovector[stringnumber*2],\n  CU2BYTES(size));\nyield[size] = 0;\n*stringptr = yield;\n*sizeptr = size;\nreturn 0;\n}\n\n\n\n/*************************************************\n*       Free memory obtained by get_substring    *\n*************************************************/\n\n/*\nArgument:     the result of a previous pcre2_substring_get_byxxx()\nReturns:      nothing\n*/\n\nPCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION\npcre2_substring_free(PCRE2_UCHAR *string)\n{\nif (string != NULL)\n  {\n  pcre2_memctl *memctl = (pcre2_memctl *)((char *)string - sizeof(pcre2_memctl));\n  memctl->free(memctl, memctl->memory_data);\n  }\n}\n\n\n\n/*************************************************\n*         Get length of a named substring        *\n*************************************************/\n\n/* This function returns the length of a named captured substring. If the regex\npermits duplicate names, the first substring that is set is chosen.\n\nArguments:\n  match_data      pointer to match data\n  stringname      the name of the required substring\n  sizeptr         where to put the length\n\nReturns:          0 if successful, else a negative error number\n*/\n\nPCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION\npcre2_substring_length_byname(pcre2_match_data *match_data,\n  PCRE2_SPTR stringname, PCRE2_SIZE *sizeptr)\n{\nPCRE2_SPTR first, last, entry;\nint failrc, entrysize;\nif (match_data->matchedby == PCRE2_MATCHEDBY_DFA_INTERPRETER)\n  return PCRE2_ERROR_DFA_UFUNC;\nentrysize = pcre2_substring_nametable_scan(match_data->code, stringname,\n  &first, &last);\nif (entrysize < 0) return entrysize;\nfailrc = PCRE2_ERROR_UNAVAILABLE;\nfor (entry = first; entry <= last; entry += entrysize)\n  {\n  uint32_t n = GET2(entry, 0);\n  if (n < match_data->oveccount)\n    {\n    if (match_data->ovector[n*2] != PCRE2_UNSET)\n      return pcre2_substring_length_bynumber(match_data, n, sizeptr);\n    failrc = PCRE2_ERROR_UNSET;\n    }\n  }\nreturn failrc;\n}\n\n\n\n/*************************************************\n*        Get length of a numbered substring      *\n*************************************************/\n\n/* This function returns the length of a captured substring. If the start is\nbeyond the end (which can happen when \\K is used in an assertion), it sets the\nlength to zero.\n\nArguments:\n  match_data      pointer to match data\n  stringnumber    the number of the required substring\n  sizeptr         where to put the length, if not NULL\n\nReturns:         if successful: 0\n                 if not successful, a negative error code:\n                   PCRE2_ERROR_NOSUBSTRING: no such substring\n                   PCRE2_ERROR_UNAVAILABLE: ovector is too small\n                   PCRE2_ERROR_UNSET: substring is not set\n                   PCRE2_ERROR_INVALIDOFFSET: internal error, should not occur\n*/\n\nPCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION\npcre2_substring_length_bynumber(pcre2_match_data *match_data,\n  uint32_t stringnumber, PCRE2_SIZE *sizeptr)\n{\nPCRE2_SIZE left, right;\nint count = match_data->rc;\nif (count == PCRE2_ERROR_PARTIAL)\n  {\n  if (stringnumber > 0) return PCRE2_ERROR_PARTIAL;\n  count = 0;\n  }\nelse if (count < 0) return count;            /* Match failed */\n\nif (match_data->matchedby != PCRE2_MATCHEDBY_DFA_INTERPRETER)\n  {\n  if (stringnumber > match_data->code->top_bracket)\n    return PCRE2_ERROR_NOSUBSTRING;\n  if (stringnumber >= match_data->oveccount)\n    return PCRE2_ERROR_UNAVAILABLE;\n  if (match_data->ovector[stringnumber*2] == PCRE2_UNSET)\n    return PCRE2_ERROR_UNSET;\n  }\nelse  /* Matched using pcre2_dfa_match() */\n  {\n  if (stringnumber >= match_data->oveccount) return PCRE2_ERROR_UNAVAILABLE;\n  if (count != 0 && stringnumber >= (uint32_t)count) return PCRE2_ERROR_UNSET;\n  }\n\nleft = match_data->ovector[stringnumber*2];\nright = match_data->ovector[stringnumber*2+1];\nif (left > match_data->subject_length || right > match_data->subject_length)\n  return PCRE2_ERROR_INVALIDOFFSET;\nif (sizeptr != NULL) *sizeptr = (left > right)? 0 : right - left;\nreturn 0;\n}\n\n\n\n/*************************************************\n*    Extract all captured strings to new memory  *\n*************************************************/\n\n/* This function gets one chunk of memory and builds a list of pointers and all\nthe captured substrings in it. A NULL pointer is put on the end of the list.\nThe substrings are zero-terminated, but also, if the final argument is\nnon-NULL, a list of lengths is also returned. This allows binary data to be\nhandled.\n\nArguments:\n  match_data     points to the match data\n  listptr        set to point to the list of pointers\n  lengthsptr     set to point to the list of lengths (may be NULL)\n\nReturns:         if successful: 0\n                 if not successful, a negative error code:\n                   PCRE2_ERROR_NOMEMORY: failed to get memory,\n                   or a match failure code\n*/\n\nPCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION\npcre2_substring_list_get(pcre2_match_data *match_data, PCRE2_UCHAR ***listptr,\n  PCRE2_SIZE **lengthsptr)\n{\nint i, count, count2;\nPCRE2_SIZE size;\nPCRE2_SIZE *lensp;\npcre2_memctl *memp;\nPCRE2_UCHAR **listp;\nPCRE2_UCHAR *sp;\nPCRE2_SIZE *ovector;\n\nif ((count = match_data->rc) < 0) return count;   /* Match failed */\nif (count == 0) count = match_data->oveccount;    /* Ovector too small */\n\ncount2 = 2*count;\novector = match_data->ovector;\nsize = sizeof(pcre2_memctl) + sizeof(PCRE2_UCHAR *);      /* For final NULL */\nif (lengthsptr != NULL) size += sizeof(PCRE2_SIZE) * count;  /* For lengths */\n\nfor (i = 0; i < count2; i += 2)\n  {\n  size += sizeof(PCRE2_UCHAR *) + CU2BYTES(1);\n  if (ovector[i+1] > ovector[i]) size += CU2BYTES(ovector[i+1] - ovector[i]);\n  }\n\nmemp = PRIV(memctl_malloc)(size, (pcre2_memctl *)match_data);\nif (memp == NULL) return PCRE2_ERROR_NOMEMORY;\n\n*listptr = listp = (PCRE2_UCHAR **)((char *)memp + sizeof(pcre2_memctl));\nlensp = (PCRE2_SIZE *)((char *)listp + sizeof(PCRE2_UCHAR *) * (count + 1));\n\nif (lengthsptr == NULL)\n  {\n  sp = (PCRE2_UCHAR *)lensp;\n  lensp = NULL;\n  }\nelse\n  {\n  *lengthsptr = lensp;\n  sp = (PCRE2_UCHAR *)((char *)lensp + sizeof(PCRE2_SIZE) * count);\n  }\n\nfor (i = 0; i < count2; i += 2)\n  {\n  size = (ovector[i+1] > ovector[i])? (ovector[i+1] - ovector[i]) : 0;\n\n  /* Size == 0 includes the case when the capture is unset. Avoid adding\n  PCRE2_UNSET to match_data->subject because it overflows, even though with\n  zero size calling memcpy() is harmless. */\n\n  if (size != 0) memcpy(sp, match_data->subject + ovector[i], CU2BYTES(size));\n  *listp++ = sp;\n  if (lensp != NULL) *lensp++ = size;\n  sp += size;\n  *sp++ = 0;\n  }\n\n*listp = NULL;\nreturn 0;\n}\n\n\n\n/*************************************************\n*   Free memory obtained by substring_list_get   *\n*************************************************/\n\n/*\nArgument:     the result of a previous pcre2_substring_list_get()\nReturns:      nothing\n*/\n\nPCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION\npcre2_substring_list_free(PCRE2_UCHAR **list)\n{\nif (list != NULL)\n  {\n  pcre2_memctl *memctl = (pcre2_memctl *)((char *)list - sizeof(pcre2_memctl));\n  memctl->free(memctl, memctl->memory_data);\n  }\n}\n\n\n\n/*************************************************\n*     Find (multiple) entries for named string   *\n*************************************************/\n\n/* This function scans the nametable for a given name, using binary chop. It\nreturns either two pointers to the entries in the table, or, if no pointers are\ngiven, the number of a unique group with the given name. If duplicate names are\npermitted, and the name is not unique, an error is generated.\n\nArguments:\n  code        the compiled regex\n  stringname  the name whose entries required\n  firstptr    where to put the pointer to the first entry\n  lastptr     where to put the pointer to the last entry\n\nReturns:      PCRE2_ERROR_NOSUBSTRING if the name is not found\n              otherwise, if firstptr and lastptr are NULL:\n                a group number for a unique substring\n                else PCRE2_ERROR_NOUNIQUESUBSTRING\n              otherwise:\n                the length of each entry, having set firstptr and lastptr\n*/\n\nPCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION\npcre2_substring_nametable_scan(const pcre2_code *code, PCRE2_SPTR stringname,\n  PCRE2_SPTR *firstptr, PCRE2_SPTR *lastptr)\n{\nuint16_t bot = 0;\nuint16_t top = code->name_count;\nuint16_t entrysize = code->name_entry_size;\nPCRE2_SPTR nametable = (PCRE2_SPTR)((const char *)code + sizeof(pcre2_real_code));\n\nwhile (top > bot)\n  {\n  uint16_t mid = (top + bot) / 2;\n  PCRE2_SPTR entry = nametable + entrysize*mid;\n  int c = PRIV(strcmp)(stringname, entry + IMM2_SIZE);\n  if (c == 0)\n    {\n    PCRE2_SPTR first;\n    PCRE2_SPTR last;\n    PCRE2_SPTR lastentry;\n    lastentry = nametable + entrysize * (code->name_count - 1);\n    first = last = entry;\n    while (first > nametable)\n      {\n      if (PRIV(strcmp)(stringname, (first - entrysize + IMM2_SIZE)) != 0) break;\n      first -= entrysize;\n      }\n    while (last < lastentry)\n      {\n      if (PRIV(strcmp)(stringname, (last + entrysize + IMM2_SIZE)) != 0) break;\n      last += entrysize;\n      }\n    if (firstptr == NULL) return (first == last)?\n      (int)GET2(entry, 0) : PCRE2_ERROR_NOUNIQUESUBSTRING;\n    *firstptr = first;\n    *lastptr = last;\n    return entrysize;\n    }\n  if (c > 0) bot = mid + 1; else top = mid;\n  }\n\nreturn PCRE2_ERROR_NOSUBSTRING;\n}\n\n\n/*************************************************\n*           Find number for named string         *\n*************************************************/\n\n/* This function is a convenience wrapper for pcre2_substring_nametable_scan()\nwhen it is known that names are unique. If there are duplicate names, it is not\ndefined which number is returned.\n\nArguments:\n  code        the compiled regex\n  stringname  the name whose number is required\n\nReturns:      the number of the named parenthesis, or a negative number\n                PCRE2_ERROR_NOSUBSTRING if not found\n                PCRE2_ERROR_NOUNIQUESUBSTRING if not unique\n*/\n\nPCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION\npcre2_substring_number_from_name(const pcre2_code *code,\n  PCRE2_SPTR stringname)\n{\nreturn pcre2_substring_nametable_scan(code, stringname, NULL, NULL);\n}\n\n/* End of pcre2_substring.c */\n"
  },
  {
    "path": "Libraries/PCRE/pcre2_tables.c",
    "content": "/*************************************************\n*      Perl-Compatible Regular Expressions       *\n*************************************************/\n\n/* PCRE is a library of functions to support regular expressions whose syntax\nand semantics are as close as possible to those of the Perl 5 language.\n\n                       Written by Philip Hazel\n     Original API code Copyright (c) 1997-2012 University of Cambridge\n          New API code Copyright (c) 2016-2024 University of Cambridge\n\n-----------------------------------------------------------------------------\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n    * Redistributions of source code must retain the above copyright notice,\n      this list of conditions and the following disclaimer.\n\n    * Redistributions in binary form must reproduce the above copyright\n      notice, this list of conditions and the following disclaimer in the\n      documentation and/or other materials provided with the distribution.\n\n    * Neither the name of the University of Cambridge nor the names of its\n      contributors may be used to endorse or promote products derived from\n      this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\nLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\nCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\nSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\nINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\nCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGE.\n-----------------------------------------------------------------------------\n*/\n\n/* This module contains some fixed tables that are used by more than one of the\nPCRE2 code modules. The tables are also #included by the pcre2test program,\nwhich uses macros to change their names from _pcre2_xxx to xxxx, thereby\navoiding name clashes with the library. In this case, PCRE2_PCRE2TEST is\ndefined. */\n\n#ifndef PCRE2_PCRE2TEST           /* We're compiling the library */\n#ifdef HAVE_CONFIG_H\n#include \"config.h\"\n#endif\n#include \"pcre2_internal.h\"\n#endif /* PCRE2_PCRE2TEST */\n\n/* Table of sizes for the fixed-length opcodes. It's defined in a macro so that\nthe definition is next to the definition of the opcodes in pcre2_internal.h.\nThis is mode-dependent, so it is skipped when this file is included by\npcre2test. */\n\n#ifndef PCRE2_PCRE2TEST\nconst uint8_t PRIV(OP_lengths)[] = { OP_LENGTHS };\n#endif\n\n/* Tables of horizontal and vertical whitespace characters, suitable for\nadding to classes. */\n\nconst uint32_t PRIV(hspace_list)[] = { HSPACE_LIST };\nconst uint32_t PRIV(vspace_list)[] = { VSPACE_LIST };\n\n/* These tables are the pairs of delimiters that are valid for callout string\narguments. For each starting delimiter there must be a matching ending\ndelimiter, which in fact is different only for bracket-like delimiters. */\n\nconst uint32_t PRIV(callout_start_delims)[] = {\n  CHAR_GRAVE_ACCENT, CHAR_APOSTROPHE, CHAR_QUOTATION_MARK,\n  CHAR_CIRCUMFLEX_ACCENT, CHAR_PERCENT_SIGN, CHAR_NUMBER_SIGN,\n  CHAR_DOLLAR_SIGN, CHAR_LEFT_CURLY_BRACKET, 0 };\n\nconst uint32_t PRIV(callout_end_delims[]) = {\n  CHAR_GRAVE_ACCENT, CHAR_APOSTROPHE, CHAR_QUOTATION_MARK,\n  CHAR_CIRCUMFLEX_ACCENT, CHAR_PERCENT_SIGN, CHAR_NUMBER_SIGN,\n  CHAR_DOLLAR_SIGN, CHAR_RIGHT_CURLY_BRACKET, 0 };\n\n\n/*************************************************\n*           Tables for UTF-8 support             *\n*************************************************/\n\n/* These tables are required by pcre2test in 16- or 32-bit mode, as well\nas for the library in 8-bit mode, because pcre2test uses UTF-8 internally for\nhandling wide characters. */\n\n#if defined PCRE2_PCRE2TEST || \\\n   (defined SUPPORT_UNICODE && \\\n    defined PCRE2_CODE_UNIT_WIDTH && \\\n    PCRE2_CODE_UNIT_WIDTH == 8)\n\n/* These are the breakpoints for different numbers of bytes in a UTF-8\ncharacter. */\n\nconst int PRIV(utf8_table1)[] =\n  { 0x7f, 0x7ff, 0xffff, 0x1fffff, 0x3ffffff, 0x7fffffff};\n\nconst int PRIV(utf8_table1_size) = sizeof(PRIV(utf8_table1)) / sizeof(int);\n\n/* These are the indicator bits and the mask for the data bits to set in the\nfirst byte of a character, indexed by the number of additional bytes. */\n\nconst int PRIV(utf8_table2)[] = { 0,    0xc0, 0xe0, 0xf0, 0xf8, 0xfc};\nconst int PRIV(utf8_table3)[] = { 0xff, 0x1f, 0x0f, 0x07, 0x03, 0x01};\n\n/* Table of the number of extra bytes, indexed by the first byte masked with\n0x3f. The highest number for a valid UTF-8 first byte is in fact 0x3d. */\n\nconst uint8_t PRIV(utf8_table4)[] = {\n  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,\n  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,\n  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,\n  3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 };\n\n#endif /* UTF-8 support needed */\n\n/* Tables concerned with Unicode properties are relevant only when Unicode\nsupport is enabled. See also the pcre2_ucptables.c file, which is generated by\na Python script from Unicode data files. */\n\n#ifdef SUPPORT_UNICODE\n\n/* Table to translate from particular type value to the general value. */\n\nconst uint32_t PRIV(ucp_gentype)[] = {\n  ucp_C, ucp_C, ucp_C, ucp_C, ucp_C,  /* Cc, Cf, Cn, Co, Cs */\n  ucp_L, ucp_L, ucp_L, ucp_L, ucp_L,  /* Ll, Lu, Lm, Lo, Lt */\n  ucp_M, ucp_M, ucp_M,                /* Mc, Me, Mn */\n  ucp_N, ucp_N, ucp_N,                /* Nd, Nl, No */\n  ucp_P, ucp_P, ucp_P, ucp_P, ucp_P,  /* Pc, Pd, Pe, Pf, Pi */\n  ucp_P, ucp_P,                       /* Ps, Po */\n  ucp_S, ucp_S, ucp_S, ucp_S,         /* Sc, Sk, Sm, So */\n  ucp_Z, ucp_Z, ucp_Z                 /* Zl, Zp, Zs */\n};\n\n/* This table encodes the rules for finding the end of an extended grapheme\ncluster. Every code point has a grapheme break property which is one of the\nucp_gbXX values defined in pcre2_ucp.h. These changed between Unicode versions\n10 and 11. The 2-dimensional table is indexed by the properties of two adjacent\ncode points. The left property selects a word from the table, and the right\nproperty selects a bit from that word like this:\n\n  PRIV(ucp_gbtable)[left-property] & (1u << right-property)\n\nThe value is non-zero if a grapheme break is NOT permitted between the relevant\ntwo code points. The breaking rules are as follows:\n\n1. Break at the start and end of text (pretty obviously).\n\n2. Do not break between a CR and LF; otherwise, break before and after\n   controls.\n\n3. Do not break Hangul syllable sequences, the rules for which are:\n\n    L may be followed by L, V, LV or LVT\n    LV or V may be followed by V or T\n    LVT or T may be followed by T\n\n4. Do not break before extending characters or zero-width-joiner (ZWJ).\n\nThe following rules are only for extended grapheme clusters (but that's what we\nare implementing).\n\n5. Do not break before SpacingMarks.\n\n6. Do not break after Prepend characters.\n\n7. Do not break within emoji modifier sequences or emoji zwj sequences. That\n   is, do not break between characters with the Extended_Pictographic property\n   if a ZWJ intervenes. Extend characters are allowed between the characters;\n   this cannot be represented in this table, the code has to deal with it.\n\n8. Do not break within emoji flag sequences. That is, do not break between\n   regional indicator (RI) symbols if there are an odd number of RI characters\n   before the break point. This table encodes \"join RI characters\"; the code\n   has to deal with checking for previous adjoining RIs.\n\n9. Otherwise, break everywhere.\n*/\n\n#define ESZ (1<<ucp_gbExtend)|(1<<ucp_gbSpacingMark)|(1<<ucp_gbZWJ)\n\nconst uint32_t PRIV(ucp_gbtable)[] = {\n   (1u<<ucp_gbLF),                                      /*  0 CR */\n   0,                                                   /*  1 LF */\n   0,                                                   /*  2 Control */\n   ESZ,                                                 /*  3 Extend */\n   ESZ|(1u<<ucp_gbPrepend)|                             /*  4 Prepend */\n       (1u<<ucp_gbL)|(1u<<ucp_gbV)|(1u<<ucp_gbT)|\n       (1u<<ucp_gbLV)|(1u<<ucp_gbLVT)|(1u<<ucp_gbOther)|\n       (1u<<ucp_gbRegional_Indicator),\n   ESZ,                                                 /*  5 SpacingMark */\n   ESZ|(1u<<ucp_gbL)|(1u<<ucp_gbV)|(1u<<ucp_gbLV)|      /*  6 L */\n       (1u<<ucp_gbLVT),\n   ESZ|(1u<<ucp_gbV)|(1u<<ucp_gbT),                     /*  7 V */\n   ESZ|(1u<<ucp_gbT),                                   /*  8 T */\n   ESZ|(1u<<ucp_gbV)|(1u<<ucp_gbT),                     /*  9 LV */\n   ESZ|(1u<<ucp_gbT),                                   /* 10 LVT */\n   (1u<<ucp_gbRegional_Indicator),                      /* 11 Regional Indicator */\n   ESZ,                                                 /* 12 Other */\n   ESZ|(1u<<ucp_gbExtended_Pictographic),               /* 13 ZWJ */\n   ESZ                                                  /* 14 Extended Pictographic */\n};\n\n#undef ESZ\n\n#ifdef SUPPORT_JIT\n/* This table reverses PRIV(ucp_gentype). We can save the cost\nof a memory load. */\n\nconst int PRIV(ucp_typerange)[] = {\n  ucp_Cc, ucp_Cs,\n  ucp_Ll, ucp_Lu,\n  ucp_Mc, ucp_Mn,\n  ucp_Nd, ucp_No,\n  ucp_Pc, ucp_Ps,\n  ucp_Sc, ucp_So,\n  ucp_Zl, ucp_Zs,\n};\n#endif /* SUPPORT_JIT */\n\n/* Finally, include the tables that are auto-generated from the Unicode data\nfiles. */\n\n#include \"pcre2_ucptables.c\"\n\n#endif /* SUPPORT_UNICODE */\n\n/* End of pcre2_tables.c */\n"
  },
  {
    "path": "Libraries/PCRE/pcre2_ucd.c",
    "content": "/*************************************************\n*      Perl-Compatible Regular Expressions       *\n*************************************************/\n\n/* PCRE is a library of functions to support regular expressions whose syntax\nand semantics are as close as possible to those of the Perl 5 language.\n\n                       Written by Philip Hazel\n     Original API code Copyright (c) 1997-2012 University of Cambridge\n          New API code Copyright (c) 2016-2022 University of Cambridge\n\nThis module is auto-generated from Unicode data files. DO NOT EDIT MANUALLY!\nInstead, modify the maint/GenerateUcd.py script and run it to generate\na new version of this code.\n\n-----------------------------------------------------------------------------\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n    * Redistributions of source code must retain the above copyright notice,\n      this list of conditions and the following disclaimer.\n\n    * Redistributions in binary form must reproduce the above copyright\n      notice, this list of conditions and the following disclaimer in the\n      documentation and/or other materials provided with the distribution.\n\n    * Neither the name of the University of Cambridge nor the names of its\n      contributors may be used to endorse or promote products derived from\n      this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\nLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\nCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\nSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\nINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\nCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGE.\n-----------------------------------------------------------------------------\n*/\n\n/* This file contains tables of Unicode properties that are extracted from\nUnicode data files. See the comments at the start of maint/GenerateUcd.py for\ndetails.\n\nAs well as being part of the PCRE2 library, this file is #included by the\npcre2test program, which redefines the PRIV macro to change table names from\n_pcre2_xxx to xxxx, thereby avoiding name clashes with the library. At present,\njust one of these tables is actually needed. When compiling the library, some\nheaders are needed. */\n\n#ifndef PCRE2_PCRE2TEST\n#ifdef HAVE_CONFIG_H\n#include \"config.h\"\n#endif\n#include \"pcre2_internal.h\"\n#endif /* PCRE2_PCRE2TEST */\n\n/* The tables herein are needed only when UCP support is built, and in PCRE2\nthat happens automatically with UTF support. This module should not be\nreferenced otherwise, so it should not matter whether it is compiled or not.\nHowever a comment was received about space saving - maybe the guy linked all\nthe modules rather than using a library - so we include a condition to cut out\nthe tables when not needed. But don't leave a totally empty module because some\ncompilers barf at that. Instead, just supply some small dummy tables. */\n\n#ifndef SUPPORT_UNICODE\nconst ucd_record PRIV(ucd_records)[] = {{0,0,0,0,0,0,0}};\nconst uint16_t PRIV(ucd_stage1)[] = {0};\nconst uint16_t PRIV(ucd_stage2)[] = {0};\nconst uint32_t PRIV(ucd_caseless_sets)[] = {0};\nconst uint32_t PRIV(ucd_nocase_ranges)[] = {0};\nconst uint32_t PRIV(ucd_nocase_ranges_size) = 0;\n#else\n\n/* Total size: 116564 bytes, block size: 128. */\n\nconst char *PRIV(unicode_version) = \"16.0.0\";\n\n/* When recompiling tables with a new Unicode version, please check the types\nin this structure definition with those in pcre2_internal.h (the actual field\nnames will be different).\n\ntypedef struct {\nuint8_t property_0;\nuint8_t property_1;\nuint8_t property_2;\nuint8_t property_3;\nint32_t property_4;\nuint16_t property_5;\nuint16_t property_6;\n} ucd_record;\n*/\n\n/* If the 32-bit library is run in non-32-bit mode, character values greater\nthan 0x10ffff may be encountered. For these we set up a special record. */\n\n#if PCRE2_CODE_UNIT_WIDTH == 32\nconst ucd_record PRIV(dummy_ucd_record)[] = {{\n  ucp_Unknown,    /* script */\n  ucp_Cn,         /* type unassigned */\n  ucp_gbOther,    /* grapheme break property */\n  0,              /* case set */\n  0,              /* other case */\n  0 | (ucp_bidiL << UCD_BIDICLASS_SHIFT), /* script extension and bidi class */\n  0,              /* bool properties offset */\n  }};\n#endif\n\n/* This table contains lists of characters that are caseless sets of\nmore than one character. Each list is terminated by NOTACHAR. */\n\nconst uint32_t PRIV(ucd_caseless_sets)[] = {\n  NOTACHAR,\n  0x0053,  0x0073,  0x017f,  NOTACHAR,\n  0x01c4,  0x01c5,  0x01c6,  NOTACHAR,\n  0x01c7,  0x01c8,  0x01c9,  NOTACHAR,\n  0x01ca,  0x01cb,  0x01cc,  NOTACHAR,\n  0x01f1,  0x01f2,  0x01f3,  NOTACHAR,\n  0x0345,  0x0399,  0x03b9,  0x1fbe,  NOTACHAR,\n  0x00b5,  0x039c,  0x03bc,  NOTACHAR,\n  0x03a3,  0x03c2,  0x03c3,  NOTACHAR,\n  0x0392,  0x03b2,  0x03d0,  NOTACHAR,\n  0x0398,  0x03b8,  0x03d1,  0x03f4,  NOTACHAR,\n  0x03a6,  0x03c6,  0x03d5,  NOTACHAR,\n  0x03a0,  0x03c0,  0x03d6,  NOTACHAR,\n  0x039a,  0x03ba,  0x03f0,  NOTACHAR,\n  0x03a1,  0x03c1,  0x03f1,  NOTACHAR,\n  0x0395,  0x03b5,  0x03f5,  NOTACHAR,\n  0x0412,  0x0432,  0x1c80,  NOTACHAR,\n  0x0414,  0x0434,  0x1c81,  NOTACHAR,\n  0x041e,  0x043e,  0x1c82,  NOTACHAR,\n  0x0421,  0x0441,  0x1c83,  NOTACHAR,\n  0x0422,  0x0442,  0x1c84,  0x1c85,  NOTACHAR,\n  0x042a,  0x044a,  0x1c86,  NOTACHAR,\n  0x0462,  0x0463,  0x1c87,  NOTACHAR,\n  0x1e60,  0x1e61,  0x1e9b,  NOTACHAR,\n  0x03a9,  0x03c9,  0x2126,  NOTACHAR,\n  0x004b,  0x006b,  0x212a,  NOTACHAR,\n  0x00c5,  0x00e5,  0x212b,  NOTACHAR,\n  0x1c88,  0xa64a,  0xa64b,  NOTACHAR,\n  0x0069,  0x0130,  NOTACHAR,\n  0x0049,  0x0131,  NOTACHAR,\n};\n\n/* This is the index, within ucd_caseless_sets, of the additional\nTurkish case-equivalences. The dotted I ones are this offset; the\ndotless I are +3 from here. */\n\nconst uint32_t PRIV(ucd_turkish_dotted_i_caseset) = 112;\n\n/* When #included in pcre2test, we don't need the table of digit sets, nor the\nthe large main UCD tables. */\n\n#ifndef PCRE2_PCRE2TEST\n\n/* This table contains character ranges, where the characters in the range have\nno other case. Both start and end values are excluded from the range. */\n\nconst uint32_t PRIV(ucd_nocase_ranges)[] = {\n  0x0000, 0x0041, /* 64 */\n  0x007a, 0x00b5, /* 58 */\n  0x00b5, 0x00c0, /* 10 */\n  0x0292, 0x029d, /* 10 */\n  0x029e, 0x0345, /* 166 */\n  0x0345, 0x0370, /* 42 */\n  0x0481, 0x048a, /* 8 */\n  0x0556, 0x0561, /* 10 */\n  0x0586, 0x10a0, /* 2841 */\n  0x10ff, 0x13a0, /* 672 */\n  0x13fd, 0x1c80, /* 2178 */\n  0x1cbf, 0x1d79, /* 185 */\n  0x1d7d, 0x1d8e, /* 16 */\n  0x1d8e, 0x1e00, /* 113 */\n  0x1ffc, 0x2126, /* 297 */\n  0x2132, 0x214e, /* 27 */\n  0x214e, 0x2160, /* 17 */\n  0x2184, 0x24b6, /* 817 */\n  0x24e9, 0x2c00, /* 1814 */\n  0x2cf3, 0x2d00, /* 12 */\n  0x2d2d, 0xa640, /* 30994 */\n  0xa66d, 0xa680, /* 18 */\n  0xa69b, 0xa722, /* 134 */\n  0xa76f, 0xa779, /* 9 */\n  0xa7dc, 0xa7f5, /* 24 */\n  0xa7f6, 0xab53, /* 860 */\n  0xab53, 0xab70, /* 28 */\n  0xabbf, 0xfb05, /* 20293 */\n  0xfb06, 0xff21, /* 1050 */\n  0xff5a, 0x10400, /* 1189 */\n  0x1044f, 0x104b0, /* 96 */\n  0x104fb, 0x10570, /* 116 */\n  0x105bc, 0x10c80, /* 1731 */\n  0x10cb2, 0x10cc0, /* 13 */\n  0x10cf2, 0x10d50, /* 93 */\n  0x10d65, 0x10d70, /* 10 */\n  0x10d85, 0x118a0, /* 2842 */\n  0x118df, 0x16e40, /* 21856 */\n  0x16e7f, 0x1e900, /* 31360 */\n  0x1e943, 0x110000, /* 988860 */\n  0xffffffff, 0xffffffff /* terminator */\n};\n\n/* Total: 1110933 characters. */\nconst uint32_t PRIV(ucd_nocase_ranges_size) = 80;\n\n/* This table lists the code points for the '9' characters in each set of\ndecimal digits. It is used to ensure that all the digits in a script run come\nfrom the same set. */\n\nconst uint32_t PRIV(ucd_digit_sets)[] = {\n  76,  /* Number of subsequent values */\n  0x00039, 0x00669, 0x006f9, 0x007c9, 0x0096f, 0x009ef, 0x00a6f, 0x00aef,\n  0x00b6f, 0x00bef, 0x00c6f, 0x00cef, 0x00d6f, 0x00def, 0x00e59, 0x00ed9,\n  0x00f29, 0x01049, 0x01099, 0x017e9, 0x01819, 0x0194f, 0x019d9, 0x01a89,\n  0x01a99, 0x01b59, 0x01bb9, 0x01c49, 0x01c59, 0x0a629, 0x0a8d9, 0x0a909,\n  0x0a9d9, 0x0a9f9, 0x0aa59, 0x0abf9, 0x0ff19, 0x104a9, 0x10d39, 0x10d49,\n  0x1106f, 0x110f9, 0x1113f, 0x111d9, 0x112f9, 0x11459, 0x114d9, 0x11659,\n  0x116c9, 0x116d9, 0x116e3, 0x11739, 0x118e9, 0x11959, 0x11bf9, 0x11c59,\n  0x11d59, 0x11da9, 0x11f59, 0x16139, 0x16a69, 0x16ac9, 0x16b59, 0x16d79,\n  0x1ccf9, 0x1d7d7, 0x1d7e1, 0x1d7eb, 0x1d7f5, 0x1d7ff, 0x1e149, 0x1e2f9,\n  0x1e4f9, 0x1e5fa, 0x1e959, 0x1fbf9,\n};\n\n/* This vector is a list of script bitsets for the Script Extension property.\nThe number of 32-bit words in each bitset is #defined in pcre2_ucp.h as\nucd_script_sets_item_size. */\n\nconst uint32_t PRIV(ucd_script_sets)[] = {\n 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u,\n 0x40200003u, 0x00381901u, 0x00100246u, 0x00000000u,\n 0x00040305u, 0x00800000u, 0x08000000u, 0x00000000u,\n 0x20000001u, 0x00000000u, 0x00000000u, 0x00000000u,\n 0x00000001u, 0x00800000u, 0x00000000u, 0x00000000u,\n 0x00040001u, 0x00000000u, 0x00000000u, 0x00000000u,\n 0x01000007u, 0x00000840u, 0x80000200u, 0x00000000u,\n 0x01000007u, 0x00000040u, 0x80010000u, 0x00000001u,\n 0x01000005u, 0x00002000u, 0x00000000u, 0x00000000u,\n 0x00040041u, 0x00001000u, 0x80000000u, 0x00000000u,\n 0x01000047u, 0x00002801u, 0x00010001u, 0x00000001u,\n 0x10000001u, 0x00001801u, 0x00000004u, 0x00000000u,\n 0x00000007u, 0x00000000u, 0x00000200u, 0x00000000u,\n 0x00000051u, 0x00002840u, 0x00000202u, 0x00000001u,\n 0x0000005fu, 0x00000041u, 0x00000202u, 0x00000000u,\n 0x00000001u, 0x00002000u, 0x00000000u, 0x00000000u,\n 0x00000041u, 0x00000000u, 0x00000002u, 0x00000000u,\n 0x01000005u, 0x00000000u, 0x00010000u, 0x00000000u,\n 0x01000001u, 0x00000040u, 0x00000000u, 0x00000000u,\n 0x00000001u, 0x00000000u, 0x80000000u, 0x00000000u,\n 0x00800001u, 0x00000000u, 0x00000000u, 0x00000000u,\n 0x00000005u, 0x00000000u, 0x00000000u, 0x00000001u,\n 0x00000003u, 0x00000000u, 0x00000200u, 0x00000001u,\n 0x00000041u, 0x00000000u, 0x00000000u, 0x00000000u,\n 0x11000041u, 0x00000000u, 0x00000002u, 0x00000000u,\n 0x01000041u, 0x00000000u, 0x00000002u, 0x00000000u,\n 0x00000041u, 0x00000000u, 0x80000000u, 0x00000000u,\n 0x01000041u, 0x00000000u, 0x00000000u, 0x00000000u,\n 0x01040001u, 0x00000001u, 0x80000001u, 0x00000000u,\n 0x00000002u, 0x00000000u, 0x00000000u, 0x00000000u,\n 0x00000001u, 0x00000000u, 0x00010000u, 0x00000000u,\n 0x00000001u, 0x00000000u, 0x00000001u, 0x00000001u,\n 0x00000001u, 0x00000000u, 0x00000000u, 0x00000000u,\n 0x00000002u, 0x00000800u, 0x00000000u, 0x00000000u,\n 0x00000004u, 0x00000000u, 0x00000200u, 0x00000000u,\n 0x00000004u, 0x00001000u, 0x00000000u, 0x00000000u,\n 0x00000005u, 0x00000000u, 0x00000000u, 0x00000000u,\n 0x00200008u, 0x00001000u, 0x00000000u, 0x00000000u,\n 0x000000e0u, 0x00010000u, 0x11200000u, 0x00000000u,\n 0x000000e0u, 0x00000000u, 0x00000000u, 0x00000000u,\n 0x000000e0u, 0x00010000u, 0x11208000u, 0x00000000u,\n 0x00000060u, 0x08000000u, 0x04608480u, 0x00000000u,\n 0x00000060u, 0x00000000u, 0x00000000u, 0x00000000u,\n 0x000000a0u, 0x00000000u, 0x01000000u, 0x00000000u,\n 0x00000020u, 0x00000000u, 0x00200000u, 0x00000000u,\n 0x0001ff01u, 0x40000000u, 0x00001008u, 0x00000000u,\n 0x0001ff01u, 0x00000000u, 0x00001008u, 0x00000000u,\n 0x0003ff00u, 0x80004000u, 0x409c1848u, 0x00000000u,\n 0x0003ff00u, 0x80004020u, 0x609c1848u, 0x00000000u,\n 0x00000100u, 0x04000000u, 0x00080040u, 0x00000000u,\n 0x00000200u, 0x10004000u, 0x00000000u, 0x00000000u,\n 0x00000400u, 0x00000000u, 0x00002000u, 0x00000000u,\n 0x00000800u, 0x00000000u, 0x00000010u, 0x00000000u,\n 0x00002000u, 0x00000000u, 0x00000008u, 0x00000000u,\n 0x00008000u, 0x00000000u, 0x00800000u, 0x00000002u,\n 0x00100000u, 0x10000040u, 0x00000000u, 0x00000000u,\n 0x00200001u, 0x00001000u, 0x00000000u, 0x00000000u,\n 0x02000000u, 0x00000000u, 0x00000000u, 0x00000000u,\n 0x00000000u, 0x0000001eu, 0x00000000u, 0x00000000u,\n 0x04000000u, 0x00008000u, 0x00000000u, 0x00000000u,\n 0x00008300u, 0x00000000u, 0x00000008u, 0x00000000u,\n 0x00000100u, 0x00000000u, 0x00000000u, 0x00000000u,\n 0x00008100u, 0x00000000u, 0x00000008u, 0x00000000u,\n 0x00000300u, 0x00000000u, 0x00000000u, 0x00000000u,\n 0x00000100u, 0x40000000u, 0x00000000u, 0x00000000u,\n 0x0001f100u, 0x00000000u, 0x00000000u, 0x00000000u,\n 0x00000100u, 0x00000000u, 0x00800000u, 0x00000000u,\n 0x0003d300u, 0x00000000u, 0x00801008u, 0x00000002u,\n 0x00000100u, 0x00000000u, 0x00000008u, 0x00000000u,\n 0x00008100u, 0x00000000u, 0x00000008u, 0x00000002u,\n 0x00000200u, 0x00000000u, 0x00000000u, 0x00000000u,\n 0x00000000u, 0x00000000u, 0x00800000u, 0x00000000u,\n 0x00000045u, 0x00000000u, 0x00000000u, 0x00000000u,\n 0x00000040u, 0x00000000u, 0x00000000u, 0x00000000u,\n 0x04000001u, 0x00008000u, 0x00000000u, 0x00000000u,\n 0x00000020u, 0x00000000u, 0x00008000u, 0x00000000u,\n 0x00200000u, 0x020c1000u, 0x00004000u, 0x00000000u,\n 0x00000002u, 0x20080000u, 0x00004000u, 0x00000000u,\n 0x00000101u, 0x00000000u, 0x00000008u, 0x00000000u,\n 0x00000001u, 0x00000800u, 0x00000000u, 0x00000000u,\n 0x00000000u, 0x02200000u, 0x00000000u, 0x00000000u,\n 0x00200000u, 0x04780000u, 0x00004000u, 0x00000000u,\n 0x00000000u, 0x00000000u, 0x00000002u, 0x00000000u,\n 0x00000020u, 0x00000000u, 0x0000c000u, 0x00000000u,\n 0x40000000u, 0x00000000u, 0x00020000u, 0x00000000u,\n 0xfc400000u, 0x00000000u, 0x00000000u, 0x00000000u,\n 0xfc400000u, 0x00008000u, 0x00000000u, 0x00000000u,\n 0x78400000u, 0x00000000u, 0x00000000u, 0x00000000u,\n 0x40000000u, 0x00000000u, 0x00000000u, 0x00000000u,\n 0xfc480000u, 0x00000000u, 0x00000000u, 0x00000000u,\n 0xfc480000u, 0x00800000u, 0x00000000u, 0x00000000u,\n 0xf8400000u, 0x00000000u, 0x00000000u, 0x00000000u,\n 0x60000000u, 0x00000000u, 0x00000000u, 0x00000000u,\n 0x18000000u, 0x00000000u, 0x00000000u, 0x00000000u,\n 0x58000000u, 0x00000000u, 0x00000000u, 0x00000000u,\n 0x40000001u, 0x00000000u, 0x00000000u, 0x00000000u,\n 0x00018d00u, 0xc4000000u, 0x00881950u, 0x00000002u,\n 0x00008d00u, 0xc4000000u, 0x00881950u, 0x00000002u,\n 0x00000d00u, 0x84000000u, 0x00081950u, 0x00000000u,\n 0x00000d00u, 0xc4000000u, 0x00081950u, 0x00000000u,\n 0x00000300u, 0x00000000u, 0x00000000u, 0x00000002u,\n 0x00002100u, 0x00000000u, 0x00000000u, 0x00000000u,\n 0x00100001u, 0x00020000u, 0x00000000u, 0x00000000u,\n 0x00000000u, 0x01000400u, 0x00000000u, 0x00000000u,\n 0x00000020u, 0x00010000u, 0x00000000u, 0x00000000u,\n 0x000000a0u, 0x00000000u, 0x00000000u, 0x00000000u,\n 0x00000000u, 0x00000280u, 0x02000000u, 0x00000000u,\n 0x00000000u, 0x00000280u, 0x00000000u, 0x00000000u,\n 0x00000000u, 0x00000280u, 0x00000020u, 0x00000000u,\n 0x00000020u, 0x00000800u, 0x00000000u, 0x00000000u,\n 0x00000000u, 0x00000000u, 0x04000080u, 0x00000000u,\n};\n\n/* This vector is a list of bitsets for Boolean properties. The number of\n32_bit words in each bitset is #defined as ucd_boolprop_sets_item_size in\npcre2_ucp.h. */\n\nconst uint32_t PRIV(ucd_boolprop_sets)[] = {\n 0x00000000u, 0x00000000u,\n 0x00000001u, 0x00000000u,\n 0x00000001u, 0x00400800u,\n 0x00800001u, 0x00400800u,\n 0x00800001u, 0x00050400u,\n 0x00800001u, 0x00002400u,\n 0x00830001u, 0x00000400u,\n 0x00800001u, 0x00000400u,\n 0x00800021u, 0x00002400u,\n 0x00800011u, 0x00000400u,\n 0x00800001u, 0x00000480u,\n 0x00800001u, 0x00040400u,\n 0x00801001u, 0x00000400u,\n 0x00800021u, 0x00050400u,\n 0x04830003u, 0x00800001u,\n 0x00800021u, 0x00040400u,\n 0x00800011u, 0x00000480u,\n 0x048003c7u, 0x01900003u,\n 0x008003c5u, 0x01900003u,\n 0x00808021u, 0x00000480u,\n 0x00800001u, 0x00800001u,\n 0x00808021u, 0x00000400u,\n 0x04800d47u, 0x01800043u,\n 0x00800d45u, 0x01800043u,\n 0x00800d45u, 0x01820043u,\n 0x00000000u, 0x00400800u,\n 0x00800000u, 0x00400000u,\n 0x00800000u, 0x00000400u,\n 0x00808020u, 0x00000000u,\n 0x00a10000u, 0x00000400u,\n 0x00800044u, 0x01800043u,\n 0x00800010u, 0x00002400u,\n 0x00800000u, 0x00000480u,\n 0x00002020u, 0x00000000u,\n 0x40800000u, 0x00000000u,\n 0x00800dc4u, 0x01800043u,\n 0x00c08020u, 0x00800001u,\n 0x00800000u, 0x00000000u,\n 0x008003c4u, 0x01900003u,\n 0x00800d44u, 0x01800043u,\n 0x00800d44u, 0x01820043u,\n 0x00804dc4u, 0x01800043u,\n 0x00800004u, 0x01800003u,\n 0x008007c4u, 0x01900003u,\n 0x00800bc4u, 0x01800003u,\n 0x00808064u, 0x01800043u,\n 0x00808064u, 0x01820043u,\n 0x00808024u, 0x01800003u,\n 0x00c08024u, 0x01800003u,\n 0x01008020u, 0x00800009u,\n 0x01008de4u, 0x00800049u,\n 0x01002020u, 0x00800009u,\n 0x01000020u, 0x00800009u,\n 0x01000024u, 0x00800009u,\n 0x00808064u, 0x00000043u,\n 0x00800000u, 0x00040000u,\n 0x00800020u, 0x00840001u,\n 0x00800dc4u, 0x018000c3u,\n 0x00800044u, 0x01900083u,\n 0x00800044u, 0x01900003u,\n 0x008003c4u, 0x01900083u,\n 0x00800000u, 0x00000080u,\n 0x01000020u, 0x00000008u,\n 0x00800020u, 0x00000000u,\n 0x00800000u, 0x00050000u,\n 0x00801000u, 0x00000000u,\n 0x01008024u, 0x00800009u,\n 0x00000020u, 0x00001000u,\n 0x00002028u, 0x00000000u,\n 0x00c00024u, 0x01800003u,\n 0x01000024u, 0x00800109u,\n 0x01008020u, 0x00800109u,\n 0x00800000u, 0x00800001u,\n 0x00804004u, 0x01800003u,\n 0x00800024u, 0x01800003u,\n 0x01000020u, 0x00800109u,\n 0x01008024u, 0x00800109u,\n 0x00800004u, 0x00800001u,\n 0x00800004u, 0x0180000bu,\n 0x03008020u, 0x00800009u,\n 0x01000004u, 0x00800009u,\n 0x01400024u, 0x00800009u,\n 0x01408020u, 0x00800009u,\n 0x00800004u, 0x00800003u,\n 0x03008024u, 0x00800009u,\n 0x00800004u, 0x01800023u,\n 0x00800010u, 0x00000000u,\n 0x00808000u, 0x00800001u,\n 0x01004024u, 0x00800009u,\n 0x00808004u, 0x00800001u,\n 0x00800944u, 0x01800043u,\n 0x00800064u, 0x01800043u,\n 0x00802004u, 0x01800003u,\n 0x00800344u, 0x01900003u,\n 0x03008000u, 0x00800009u,\n 0x00c00000u, 0x00000000u,\n 0x01002020u, 0x00a00009u,\n 0x01000024u, 0x0180000bu,\n 0x01008020u, 0x00000008u,\n 0x01408024u, 0x00800009u,\n 0x00808000u, 0x00000000u,\n 0x00800044u, 0x01820043u,\n 0x00800064u, 0x01820043u,\n 0x01002020u, 0x00800011u,\n 0x00022020u, 0x00800019u,\n 0x00002028u, 0x00000800u,\n 0x00801000u, 0x00000400u,\n 0x00800020u, 0x00002400u,\n 0x00800000u, 0x00002400u,\n 0x00800020u, 0x00050400u,\n 0x00800020u, 0x00000400u,\n 0x00a10000u, 0x00050400u,\n 0x00800000u, 0x00050400u,\n 0x00800000u, 0x00800081u,\n 0x00800010u, 0x00000400u,\n 0x00002020u, 0x00000080u,\n 0x00002000u, 0x00000000u,\n 0x00006020u, 0x00000000u,\n 0x40800000u, 0x00000080u,\n 0x40801000u, 0x00000080u,\n 0x40800010u, 0x00000080u,\n 0x01000020u, 0x00800089u,\n 0x01020020u, 0x00000008u,\n 0x00800044u, 0x018000c3u,\n 0x00800000u, 0x01800083u,\n 0x00a10000u, 0x00000000u,\n 0x00800000u, 0x01800003u,\n 0x00800004u, 0x01800083u,\n 0x00a10044u, 0x01800043u,\n 0x00800044u, 0x018200c3u,\n 0x00a10000u, 0x00000480u,\n 0xc0800000u, 0x00000480u,\n 0x00800010u, 0x00000480u,\n 0x00801000u, 0x00000480u,\n 0x00b10000u, 0x00000400u,\n 0x00804010u, 0x00000400u,\n 0x00a00000u, 0x00000400u,\n 0x00000000u, 0x00000400u,\n 0x008003c4u, 0x00100000u,\n 0x00a103c4u, 0x00100000u,\n 0x00800d44u, 0x00000040u,\n 0x00b10000u, 0x00000480u,\n 0x00a00000u, 0x00000480u,\n 0x00a90000u, 0x00000400u,\n 0x00b90000u, 0x00000400u,\n 0x03000020u, 0x00800009u,\n 0x00808024u, 0x00000400u,\n 0x00800000u, 0x00040400u,\n 0x00800000u, 0x00004000u,\n 0x08800000u, 0x00000000u,\n 0x10800000u, 0x00000000u,\n 0x20800000u, 0x00000000u,\n 0x00800004u, 0x01800007u,\n 0x01008000u, 0x00800009u,\n 0x00a11000u, 0x00000400u,\n 0x00808020u, 0x00000003u,\n 0x00800004u, 0x01880007u,\n 0x00808004u, 0x01800003u,\n 0x00800004u, 0x00000003u,\n 0x00000000u, 0x00000200u,\n 0x01022020u, 0x00a00009u,\n 0x00800000u, 0x00002000u,\n 0x00800020u, 0x00050000u,\n 0x00800020u, 0x00040000u,\n 0x00801000u, 0x00000080u,\n 0x00800010u, 0x00000080u,\n 0x00800020u, 0x00002000u,\n 0x04800000u, 0x00800001u,\n 0x048003c4u, 0x01900003u,\n 0x00808020u, 0x00000080u,\n 0x04800d44u, 0x01800043u,\n 0x00800010u, 0x00002000u,\n 0x01008024u, 0x0080000bu,\n 0x00000020u, 0x00000000u,\n 0x00c00004u, 0x01800003u,\n 0x00c08004u, 0x01800003u,\n 0x01400020u, 0x00800009u,\n 0x01000020u, 0x0080000du,\n 0x01008004u, 0x00800009u,\n 0x01000000u, 0x00800009u,\n 0xc0800000u, 0x00000080u,\n 0x00a00000u, 0x00000000u,\n 0x00b10000u, 0x00000000u,\n 0x00200000u, 0x00000000u,\n 0x00800044u, 0x00100000u,\n 0x00a10044u, 0x00100000u,\n 0x00930000u, 0x00008000u,\n 0x00b90000u, 0x00000000u,\n 0x00a90000u, 0x00000000u,\n 0x00970020u, 0x00000008u,\n 0x00b30000u, 0x00000000u,\n 0x01022020u, 0x00000008u,\n};\n\n/* These are the main two-stage UCD tables. The fields in each record are:\nscript (8 bits), character type (8 bits), grapheme break property (8 bits),\noffset to multichar other cases or zero (8 bits), offset to other case or zero\n(32 bits, signed), bidi class (5 bits) and script extension (11 bits) packed\ninto a 16-bit field, and offset in binary properties table (16 bits). */\n\nconst ucd_record PRIV(ucd_records)[] = { /* 18516 bytes, record size 12 */\n  {    99,      0,      2,      0,      0,   6144,      2, }, /*   0 */\n  {    99,      0,      2,      0,      0,  43008,      4, }, /*   1 */\n  {    99,      0,      1,      0,      0,   4096,      4, }, /*   2 */\n  {    99,      0,      2,      0,      0,  45056,      4, }, /*   3 */\n  {    99,      0,      0,      0,      0,   4096,      4, }, /*   4 */\n  {    99,      0,      2,      0,      0,   4096,      2, }, /*   5 */\n  {    99,      0,      2,      0,      0,  43008,      2, }, /*   6 */\n  {    99,     29,     12,      0,      0,  45056,      6, }, /*   7 */\n  {    99,     21,     12,      0,      0,  28672,      8, }, /*   8 */\n  {    99,     21,     12,      0,      0,  28672,     10, }, /*   9 */\n  {    99,     21,     12,      0,      0,  14336,     12, }, /*  10 */\n  {    99,     23,     12,      0,      0,  14336,     14, }, /*  11 */\n  {    99,     21,     12,      0,      0,  14336,     14, }, /*  12 */\n  {    99,     21,     12,      0,      0,  28672,     14, }, /*  13 */\n  {    99,     21,     12,      0,      0,  28672,     16, }, /*  14 */\n  {    99,     22,     12,      0,      0,  28672,     18, }, /*  15 */\n  {    99,     18,     12,      0,      0,  28672,     18, }, /*  16 */\n  {    99,     21,     12,      0,      0,  28672,     12, }, /*  17 */\n  {    99,     25,     12,      0,      0,  12288,     20, }, /*  18 */\n  {    99,     21,     12,      0,      0,   8192,     22, }, /*  19 */\n  {    99,     17,     12,      0,      0,  12288,     24, }, /*  20 */\n  {    99,     21,     12,      0,      0,   8192,     26, }, /*  21 */\n  {    99,     21,     12,      0,      0,   8192,     14, }, /*  22 */\n  {    99,     13,     12,      0,      0,  10240,     28, }, /*  23 */\n  {    99,     21,     12,      0,      0,   8192,     30, }, /*  24 */\n  {    99,     21,     12,      0,      0,  28672,     22, }, /*  25 */\n  {    99,     25,     12,      0,      0,  28672,     32, }, /*  26 */\n  {    99,     25,     12,      0,      0,  28672,     20, }, /*  27 */\n  {     0,      9,     12,      0,     32,  18432,     34, }, /*  28 */\n  {     0,      9,     12,      0,     32,  18432,     36, }, /*  29 */\n  {     0,      9,     12,    100,     32,  18432,     36, }, /*  30 */\n  {     0,      9,     12,      1,     32,  18432,     36, }, /*  31 */\n  {    99,     24,     12,      0,      0,  28672,     38, }, /*  32 */\n  {    99,     16,     12,      0,      0,  28672,     40, }, /*  33 */\n  {    99,     24,     12,      0,      0,  28672,     42, }, /*  34 */\n  {     0,      5,     12,      0,    -32,  18432,     44, }, /*  35 */\n  {     0,      5,     12,      0,    -32,  18432,     46, }, /*  36 */\n  {     0,      5,     12,      0,    -32,  18432,     48, }, /*  37 */\n  {     0,      5,     12,    100,    -32,  18432,     46, }, /*  38 */\n  {     0,      5,     12,      1,    -32,  18432,     46, }, /*  39 */\n  {    99,      0,      2,      0,      0,   6144,      0, }, /*  40 */\n  {    99,      0,      2,      0,      0,   4096,     50, }, /*  41 */\n  {    99,     29,     12,      0,      0,   8192,     52, }, /*  42 */\n  {    99,     21,     12,      0,      0,  28672,     54, }, /*  43 */\n  {    99,     23,     12,      0,      0,  14336,     54, }, /*  44 */\n  {    99,     26,     12,      0,      0,  28672,     54, }, /*  45 */\n  {    99,     24,     12,      0,      0,  28672,     56, }, /*  46 */\n  {    99,     26,     14,      0,      0,  28672,     58, }, /*  47 */\n  {     0,      7,     12,      0,      0,  18432,     60, }, /*  48 */\n  {    99,     20,     12,      0,      0,  28672,     62, }, /*  49 */\n  {    99,     25,     12,      0,      0,  28672,     64, }, /*  50 */\n  {    99,      1,      2,      0,      0,   6144,     66, }, /*  51 */\n  {    99,     26,     12,      0,      0,  14336,     54, }, /*  52 */\n  {    99,     25,     12,      0,      0,  14336,     64, }, /*  53 */\n  {    99,     15,     12,      0,      0,  10240,     68, }, /*  54 */\n  {    99,      5,     12,     26,    775,  18432,     70, }, /*  55 */\n  {    99,     21,     12,      0,      0,  28676,     72, }, /*  56 */\n  {    99,     19,     12,      0,      0,  28672,     62, }, /*  57 */\n  {    99,     15,     12,      0,      0,  28672,     74, }, /*  58 */\n  {     0,      9,     12,      0,     32,  18432,     76, }, /*  59 */\n  {     0,      9,     12,    104,     32,  18432,     76, }, /*  60 */\n  {     0,      5,     12,      0,   7615,  18432,     70, }, /*  61 */\n  {     0,      5,     12,      0,    -32,  18432,     78, }, /*  62 */\n  {     0,      5,     12,    104,    -32,  18432,     78, }, /*  63 */\n  {     0,      5,     12,      0,    121,  18432,     78, }, /*  64 */\n  {     0,      9,     12,      0,      1,  18432,     76, }, /*  65 */\n  {     0,      5,     12,      0,     -1,  18432,     78, }, /*  66 */\n  {     0,      5,     12,      0,     -1,  18432,     80, }, /*  67 */\n  {     0,      9,     12,      0,      0,  18432,     76, }, /*  68 */\n  {     0,      5,     12,      0,      0,  18432,     78, }, /*  69 */\n  {     0,      5,     12,      0,      0,  18432,     60, }, /*  70 */\n  {     0,      5,     12,      0,      0,  18432,     82, }, /*  71 */\n  {     0,      9,     12,      0,   -121,  18432,     76, }, /*  72 */\n  {     0,      5,     12,      1,      0,  18432,     70, }, /*  73 */\n  {     0,      5,     12,      0,    195,  18432,     78, }, /*  74 */\n  {     0,      9,     12,      0,    210,  18432,     76, }, /*  75 */\n  {     0,      9,     12,      0,    206,  18432,     76, }, /*  76 */\n  {     0,      9,     12,      0,    205,  18432,     76, }, /*  77 */\n  {     0,      9,     12,      0,     79,  18432,     76, }, /*  78 */\n  {     0,      9,     12,      0,    202,  18432,     76, }, /*  79 */\n  {     0,      9,     12,      0,    203,  18432,     76, }, /*  80 */\n  {     0,      9,     12,      0,    207,  18432,     76, }, /*  81 */\n  {     0,      5,     12,      0,     97,  18432,     78, }, /*  82 */\n  {     0,      9,     12,      0,    211,  18432,     76, }, /*  83 */\n  {     0,      9,     12,      0,    209,  18432,     76, }, /*  84 */\n  {     0,      5,     12,      0,    163,  18432,     78, }, /*  85 */\n  {     0,      5,     12,      0,  42561,  18432,     78, }, /*  86 */\n  {     0,      9,     12,      0,    213,  18432,     76, }, /*  87 */\n  {     0,      5,     12,      0,    130,  18432,     78, }, /*  88 */\n  {     0,      9,     12,      0,    214,  18432,     76, }, /*  89 */\n  {     0,      9,     12,      0,    218,  18432,     76, }, /*  90 */\n  {     0,      9,     12,      0,    217,  18432,     76, }, /*  91 */\n  {     0,      9,     12,      0,    219,  18432,     76, }, /*  92 */\n  {     0,      7,     12,      0,      0,  18432,     84, }, /*  93 */\n  {     0,      5,     12,      0,     56,  18432,     78, }, /*  94 */\n  {     0,      9,     12,      5,      2,  18432,     86, }, /*  95 */\n  {     0,      8,     12,      5,      1,  18432,     88, }, /*  96 */\n  {     0,      5,     12,      5,     -2,  18432,     78, }, /*  97 */\n  {     0,      9,     12,      9,      2,  18432,     86, }, /*  98 */\n  {     0,      8,     12,      9,      1,  18432,     88, }, /*  99 */\n  {     0,      5,     12,      9,     -2,  18432,     78, }, /* 100 */\n  {     0,      9,     12,     13,      2,  18432,     86, }, /* 101 */\n  {     0,      8,     12,     13,      1,  18432,     88, }, /* 102 */\n  {     0,      5,     12,     13,     -2,  18432,     78, }, /* 103 */\n  {     0,      5,     12,      0,    -79,  18432,     78, }, /* 104 */\n  {     0,      9,     12,     17,      2,  18432,     86, }, /* 105 */\n  {     0,      8,     12,     17,      1,  18432,     88, }, /* 106 */\n  {     0,      5,     12,     17,     -2,  18432,     78, }, /* 107 */\n  {     0,      9,     12,      0,    -97,  18432,     76, }, /* 108 */\n  {     0,      9,     12,      0,    -56,  18432,     76, }, /* 109 */\n  {     0,      9,     12,      0,   -130,  18432,     76, }, /* 110 */\n  {     0,      9,     12,      0,  10795,  18432,     76, }, /* 111 */\n  {     0,      9,     12,      0,   -163,  18432,     76, }, /* 112 */\n  {     0,      9,     12,      0,  10792,  18432,     76, }, /* 113 */\n  {     0,      5,     12,      0,  10815,  18432,     78, }, /* 114 */\n  {     0,      9,     12,      0,   -195,  18432,     76, }, /* 115 */\n  {     0,      9,     12,      0,     69,  18432,     76, }, /* 116 */\n  {     0,      9,     12,      0,     71,  18432,     76, }, /* 117 */\n  {     0,      5,     12,      0,  10783,  18432,     78, }, /* 118 */\n  {     0,      5,     12,      0,  10780,  18432,     78, }, /* 119 */\n  {     0,      5,     12,      0,  10782,  18432,     78, }, /* 120 */\n  {     0,      5,     12,      0,   -210,  18432,     78, }, /* 121 */\n  {     0,      5,     12,      0,   -206,  18432,     78, }, /* 122 */\n  {     0,      5,     12,      0,   -205,  18432,     78, }, /* 123 */\n  {     0,      5,     12,      0,   -202,  18432,     78, }, /* 124 */\n  {     0,      5,     12,      0,   -203,  18432,     78, }, /* 125 */\n  {     0,      5,     12,      0,  42319,  18432,     78, }, /* 126 */\n  {     0,      5,     12,      0,  42315,  18432,     78, }, /* 127 */\n  {     0,      5,     12,      0,   -207,  18432,     78, }, /* 128 */\n  {     0,      5,     12,      0,  42343,  18432,     78, }, /* 129 */\n  {     0,      5,     12,      0,  42280,  18432,     78, }, /* 130 */\n  {     0,      5,     12,      0,  42308,  18432,     78, }, /* 131 */\n  {     0,      5,     12,      0,   -209,  18432,     80, }, /* 132 */\n  {     0,      5,     12,      0,   -211,  18432,     78, }, /* 133 */\n  {     0,      5,     12,      0,  10743,  18432,     78, }, /* 134 */\n  {     0,      5,     12,      0,  42305,  18432,     78, }, /* 135 */\n  {     0,      5,     12,      0,  10749,  18432,     78, }, /* 136 */\n  {     0,      5,     12,      0,   -213,  18432,     78, }, /* 137 */\n  {     0,      5,     12,      0,   -214,  18432,     78, }, /* 138 */\n  {     0,      5,     12,      0,  10727,  18432,     78, }, /* 139 */\n  {     0,      5,     12,      0,   -218,  18432,     78, }, /* 140 */\n  {     0,      5,     12,      0,  42307,  18432,     78, }, /* 141 */\n  {     0,      5,     12,      0,  42282,  18432,     78, }, /* 142 */\n  {     0,      5,     12,      0,    -69,  18432,     78, }, /* 143 */\n  {     0,      5,     12,      0,   -217,  18432,     78, }, /* 144 */\n  {     0,      5,     12,      0,    -71,  18432,     78, }, /* 145 */\n  {     0,      5,     12,      0,   -219,  18432,     78, }, /* 146 */\n  {     0,      5,     12,      0,  42261,  18432,     80, }, /* 147 */\n  {     0,      5,     12,      0,  42258,  18432,     78, }, /* 148 */\n  {     0,      6,     12,      0,      0,  18432,     90, }, /* 149 */\n  {     0,      6,     12,      0,      0,  18432,     92, }, /* 150 */\n  {    99,      6,     12,      0,      0,  28672,     94, }, /* 151 */\n  {    99,      6,     12,      0,      0,  18432,     94, }, /* 152 */\n  {    99,      6,     12,      0,      0,  18440,     94, }, /* 153 */\n  {    99,      6,     12,      0,      0,  18432,     90, }, /* 154 */\n  {    99,      6,     12,      0,      0,  28684,     94, }, /* 155 */\n  {    99,      6,     12,      0,      0,  28688,     94, }, /* 156 */\n  {    99,      6,     12,      0,      0,  18432,     96, }, /* 157 */\n  {    99,     24,     12,      0,      0,  28692,     56, }, /* 158 */\n  {    99,     24,     12,      0,      0,  28684,     56, }, /* 159 */\n  {    29,     24,     12,      0,      0,  28672,     56, }, /* 160 */\n  {   106,     12,      3,      0,      0,  26648,     98, }, /* 161 */\n  {   106,     12,      3,      0,      0,  26652,     98, }, /* 162 */\n  {   106,     12,      3,      0,      0,  26656,     98, }, /* 163 */\n  {   106,     12,      3,      0,      0,  26660,     98, }, /* 164 */\n  {   106,     12,      3,      0,      0,  26664,     98, }, /* 165 */\n  {   106,     12,      3,      0,      0,  26668,     98, }, /* 166 */\n  {   106,     12,      3,      0,      0,  26672,     98, }, /* 167 */\n  {   106,     12,      3,      0,      0,  26676,     98, }, /* 168 */\n  {   106,     12,      3,      0,      0,  26680,     98, }, /* 169 */\n  {   106,     12,      3,      0,      0,  26684,     98, }, /* 170 */\n  {   106,     12,      3,      0,      0,  26688,     98, }, /* 171 */\n  {   106,     12,      3,      0,      0,  26692,     98, }, /* 172 */\n  {   106,     12,      3,      0,      0,  26696,     98, }, /* 173 */\n  {   106,     12,      3,      0,      0,  26700,     98, }, /* 174 */\n  {   106,     12,      3,      0,      0,  26704,     98, }, /* 175 */\n  {   106,     12,      3,      0,      0,  26624,     98, }, /* 176 */\n  {   106,     12,      3,      0,      0,  26708,     98, }, /* 177 */\n  {   106,     12,      3,      0,      0,  26712,     98, }, /* 178 */\n  {   106,     12,      3,      0,      0,  26716,     98, }, /* 179 */\n  {   106,     12,      3,      0,      0,  26720,     98, }, /* 180 */\n  {   106,     12,      3,      0,      0,  26724,     98, }, /* 181 */\n  {   106,     12,      3,      0,      0,  26728,     98, }, /* 182 */\n  {   106,     12,      3,      0,      0,  26732,     98, }, /* 183 */\n  {   106,     12,      3,      0,      0,  26736,     98, }, /* 184 */\n  {   106,     12,      3,      0,      0,  26740,     98, }, /* 185 */\n  {   106,     12,      3,     21,    116,  26740,    100, }, /* 186 */\n  {   106,     12,      3,      0,      0,  26624,    102, }, /* 187 */\n  {   106,     12,      3,      0,      0,  26744,    104, }, /* 188 */\n  {   106,     12,      3,      0,      0,  26624,    104, }, /* 189 */\n  {   106,     12,      3,      0,      0,  26748,     98, }, /* 190 */\n  {   106,     12,      3,      0,      0,  26752,    106, }, /* 191 */\n  {     1,      9,     12,      0,      1,  18432,     76, }, /* 192 */\n  {     1,      5,     12,      0,     -1,  18432,     78, }, /* 193 */\n  {    99,      6,     12,      0,      0,  28804,     94, }, /* 194 */\n  {     1,     24,     12,      0,      0,  28804,     56, }, /* 195 */\n  {    98,      2,     12,      0,      0,  18432,      0, }, /* 196 */\n  {     1,      6,     12,      0,      0,  18432,    108, }, /* 197 */\n  {     1,      5,     12,      0,    130,  18432,     78, }, /* 198 */\n  {    99,     21,     12,      0,      0,  28672,    110, }, /* 199 */\n  {     1,      9,     12,      0,    116,  18432,     76, }, /* 200 */\n  {     1,     24,     12,      0,      0,  28672,     56, }, /* 201 */\n  {     1,      9,     12,      0,     38,  18432,     76, }, /* 202 */\n  {    99,     21,     12,      0,      0,  28672,    112, }, /* 203 */\n  {     1,      9,     12,      0,     37,  18432,     76, }, /* 204 */\n  {     1,      9,     12,      0,     64,  18432,     76, }, /* 205 */\n  {     1,      9,     12,      0,     63,  18432,     76, }, /* 206 */\n  {     1,      5,     12,      0,   7235,  18432,     78, }, /* 207 */\n  {     1,      9,     12,      0,     32,  18432,     76, }, /* 208 */\n  {     1,      9,     12,     34,     32,  18432,     76, }, /* 209 */\n  {     1,      9,     12,     59,     32,  18432,     76, }, /* 210 */\n  {     1,      9,     12,     38,     32,  18432,     76, }, /* 211 */\n  {     1,      9,     12,     21,     32,  18432,     76, }, /* 212 */\n  {     1,      9,     12,     51,     32,  18432,     76, }, /* 213 */\n  {     1,      9,     12,     26,     32,  18432,     76, }, /* 214 */\n  {     1,      9,     12,     47,     32,  18432,     76, }, /* 215 */\n  {     1,      9,     12,     55,     32,  18432,     76, }, /* 216 */\n  {     1,      9,     12,     30,     32,  18432,     76, }, /* 217 */\n  {     1,      9,     12,     43,     32,  18432,     76, }, /* 218 */\n  {     1,      9,     12,     96,     32,  18432,     76, }, /* 219 */\n  {     1,      5,     12,      0,    -38,  18432,     78, }, /* 220 */\n  {     1,      5,     12,      0,    -37,  18432,     78, }, /* 221 */\n  {     1,      5,     12,      0,   7219,  18432,     78, }, /* 222 */\n  {     1,      5,     12,      0,    -32,  18432,     78, }, /* 223 */\n  {     1,      5,     12,     34,    -32,  18432,     78, }, /* 224 */\n  {     1,      5,     12,     59,    -32,  18432,     78, }, /* 225 */\n  {     1,      5,     12,     38,    -32,  18432,     78, }, /* 226 */\n  {     1,      5,     12,     21,   -116,  18432,     78, }, /* 227 */\n  {     1,      5,     12,     51,    -32,  18432,     78, }, /* 228 */\n  {     1,      5,     12,     26,   -775,  18432,     78, }, /* 229 */\n  {     1,      5,     12,     47,    -32,  18432,     78, }, /* 230 */\n  {     1,      5,     12,     55,    -32,  18432,     78, }, /* 231 */\n  {     1,      5,     12,     30,      1,  18432,     70, }, /* 232 */\n  {     1,      5,     12,     30,    -32,  18432,     78, }, /* 233 */\n  {     1,      5,     12,     43,    -32,  18432,     78, }, /* 234 */\n  {     1,      5,     12,     96,    -32,  18432,     78, }, /* 235 */\n  {     1,      5,     12,      0,    -64,  18432,     78, }, /* 236 */\n  {     1,      5,     12,      0,    -63,  18432,     78, }, /* 237 */\n  {     1,      9,     12,      0,      8,  18432,     76, }, /* 238 */\n  {     1,      5,     12,     34,    -30,  18432,    114, }, /* 239 */\n  {     1,      5,     12,     38,    -25,  18432,    114, }, /* 240 */\n  {     1,      9,     12,      0,      0,  18432,    116, }, /* 241 */\n  {     1,      9,     12,      0,      0,  18432,    118, }, /* 242 */\n  {     1,      5,     12,     43,    -15,  18432,    114, }, /* 243 */\n  {     1,      5,     12,     47,    -22,  18432,     70, }, /* 244 */\n  {     1,      5,     12,      0,     -8,  18432,     78, }, /* 245 */\n  {    43,      9,     12,      0,      1,  18432,     76, }, /* 246 */\n  {    43,      5,     12,      0,     -1,  18432,     78, }, /* 247 */\n  {     1,      5,     12,     51,    -54,  18432,    114, }, /* 248 */\n  {     1,      5,     12,     55,    -48,  18432,    114, }, /* 249 */\n  {     1,      5,     12,      0,      7,  18432,     78, }, /* 250 */\n  {     1,      5,     12,      0,   -116,  18432,     80, }, /* 251 */\n  {     1,      9,     12,     38,    -60,  18432,    120, }, /* 252 */\n  {     1,      5,     12,     59,    -64,  18432,    114, }, /* 253 */\n  {     1,     25,     12,      0,      0,  28672,    122, }, /* 254 */\n  {     1,      9,     12,      0,     -7,  18432,     76, }, /* 255 */\n  {     1,      5,     12,      0,      0,  18432,     60, }, /* 256 */\n  {     1,      9,     12,      0,   -130,  18432,     76, }, /* 257 */\n  {     2,      9,     12,      0,     80,  18432,     76, }, /* 258 */\n  {     2,      9,     12,      0,     32,  18432,     76, }, /* 259 */\n  {     2,      9,     12,     63,     32,  18432,     76, }, /* 260 */\n  {     2,      9,     12,     67,     32,  18432,     76, }, /* 261 */\n  {     2,      9,     12,     71,     32,  18432,     76, }, /* 262 */\n  {     2,      9,     12,     75,     32,  18432,     76, }, /* 263 */\n  {     2,      9,     12,     79,     32,  18432,     76, }, /* 264 */\n  {     2,      9,     12,     84,     32,  18432,     76, }, /* 265 */\n  {     2,      5,     12,      0,    -32,  18432,     78, }, /* 266 */\n  {     2,      5,     12,     63,    -32,  18432,     78, }, /* 267 */\n  {     2,      5,     12,     67,    -32,  18432,     78, }, /* 268 */\n  {     2,      5,     12,     71,    -32,  18432,     78, }, /* 269 */\n  {     2,      5,     12,     75,    -32,  18432,     78, }, /* 270 */\n  {     2,      5,     12,     79,    -32,  18432,     78, }, /* 271 */\n  {     2,      5,     12,     84,    -32,  18432,     78, }, /* 272 */\n  {     2,      5,     12,      0,    -80,  18432,     78, }, /* 273 */\n  {     2,      5,     12,      0,    -80,  18432,     80, }, /* 274 */\n  {     2,      9,     12,      0,      1,  18432,     76, }, /* 275 */\n  {     2,      5,     12,      0,     -1,  18432,     78, }, /* 276 */\n  {     2,      9,     12,     88,      1,  18432,     76, }, /* 277 */\n  {     2,      5,     12,     88,     -1,  18432,     78, }, /* 278 */\n  {     2,     26,     12,      0,      0,  18432,     74, }, /* 279 */\n  {     2,     12,      3,      0,      0,  26760,     98, }, /* 280 */\n  {     2,     12,      3,      0,      0,  26764,     98, }, /* 281 */\n  {   106,     12,      3,      0,      0,  26768,     98, }, /* 282 */\n  {     2,     11,      3,      0,      0,  26624,    124, }, /* 283 */\n  {     2,      9,     12,      0,     15,  18432,     76, }, /* 284 */\n  {     2,      5,     12,      0,    -15,  18432,     78, }, /* 285 */\n  {     3,      9,     12,      0,     48,  18432,     76, }, /* 286 */\n  {     3,      6,     12,      0,      0,  18432,     94, }, /* 287 */\n  {     3,     21,     12,      0,      0,  18432,     74, }, /* 288 */\n  {     3,     21,     12,      0,      0,  18432,    126, }, /* 289 */\n  {     3,      5,     12,      0,      0,  18432,     60, }, /* 290 */\n  {     3,      5,     12,      0,    -48,  18432,     78, }, /* 291 */\n  {     3,      5,     12,      0,      0,  18432,     70, }, /* 292 */\n  {     3,     21,     12,      0,      0,  18580,    128, }, /* 293 */\n  {     3,     17,     12,      0,      0,  28672,    130, }, /* 294 */\n  {     3,     26,     12,      0,      0,  28672,     74, }, /* 295 */\n  {     3,     23,     12,      0,      0,  14336,     74, }, /* 296 */\n  {    98,      2,     12,      0,      0,  34816,      0, }, /* 297 */\n  {     4,     12,      3,      0,      0,  26624,     98, }, /* 298 */\n  {     4,     12,      3,      0,      0,  26624,    104, }, /* 299 */\n  {     4,     12,      3,      0,      0,  26624,    132, }, /* 300 */\n  {     4,     17,     12,      0,      0,  34816,    130, }, /* 301 */\n  {     4,     21,     12,      0,      0,  34816,     74, }, /* 302 */\n  {     4,     21,     12,      0,      0,  34816,    110, }, /* 303 */\n  {     4,     12,      3,      0,      0,  26624,    106, }, /* 304 */\n  {     4,      7,     12,      0,      0,  34816,     84, }, /* 305 */\n  {     4,     21,     12,      0,      0,  34816,    126, }, /* 306 */\n  {     5,      1,      4,      0,      0,   2048,    134, }, /* 307 */\n  {    99,      1,      4,      0,      0,   2048,    134, }, /* 308 */\n  {     5,     25,     12,      0,      0,  28672,    122, }, /* 309 */\n  {     5,     25,     12,      0,      0,      0,    122, }, /* 310 */\n  {     5,     21,     12,      0,      0,  14336,     74, }, /* 311 */\n  {     5,     23,     12,      0,      0,      0,     74, }, /* 312 */\n  {    99,     21,     12,      0,      0,   8344,    110, }, /* 313 */\n  {     5,     21,     12,      0,      0,      0,     74, }, /* 314 */\n  {     5,     26,     12,      0,      0,  28672,     74, }, /* 315 */\n  {     5,     12,      3,      0,      0,  26624,    106, }, /* 316 */\n  {    99,     21,     12,      0,      0,    152,    110, }, /* 317 */\n  {     5,      1,      2,      0,      0,    156,    136, }, /* 318 */\n  {     5,     21,     12,      0,      0,      0,    128, }, /* 319 */\n  {    99,     21,     12,      0,      0,    160,    128, }, /* 320 */\n  {     5,      7,     12,      0,      0,      0,     84, }, /* 321 */\n  {    99,      6,     12,      0,      0,    164,    138, }, /* 322 */\n  {   106,     12,      3,      0,      0,  26792,    132, }, /* 323 */\n  {   106,     12,      3,      0,      0,  26792,    106, }, /* 324 */\n  {   106,     12,      3,      0,      0,  26792,    140, }, /* 325 */\n  {     5,     12,      3,      0,      0,  26624,    132, }, /* 326 */\n  {     5,     12,      3,      0,      0,  26624,    142, }, /* 327 */\n  {     5,     13,     12,      0,      0,   2220,    144, }, /* 328 */\n  {     5,     21,     12,      0,      0,   2048,     74, }, /* 329 */\n  {     5,      7,     12,      0,      0,      0,    146, }, /* 330 */\n  {     5,     21,     12,      0,      0,    176,    128, }, /* 331 */\n  {     5,     12,      3,      0,      0,  26624,    140, }, /* 332 */\n  {     5,     12,      3,      0,      0,  26624,     98, }, /* 333 */\n  {     5,      6,     12,      0,      0,      0,     94, }, /* 334 */\n  {     5,     13,     12,      0,      0,  10240,    144, }, /* 335 */\n  {     5,     26,     12,      0,      0,      0,     74, }, /* 336 */\n  {     6,     21,     12,      0,      0,      0,    128, }, /* 337 */\n  {     6,     21,     12,      0,      0,      0,    110, }, /* 338 */\n  {     6,     21,     12,      0,      0,      0,     74, }, /* 339 */\n  {    98,      2,     12,      0,      0,      0,      0, }, /* 340 */\n  {     6,      1,      4,      0,      0,      0,    134, }, /* 341 */\n  {     6,      7,     12,      0,      0,      0,     84, }, /* 342 */\n  {     6,     12,      3,      0,      0,  26624,    106, }, /* 343 */\n  {     6,     12,      3,      0,      0,  26624,    132, }, /* 344 */\n  {     6,     12,      3,      0,      0,  26624,     98, }, /* 345 */\n  {     7,      7,     12,      0,      0,      0,     84, }, /* 346 */\n  {     7,     12,      3,      0,      0,  26624,    132, }, /* 347 */\n  {    48,     13,     12,      0,      0,  34816,    144, }, /* 348 */\n  {    48,      7,     12,      0,      0,  34816,     84, }, /* 349 */\n  {    48,     12,      3,      0,      0,  26624,     98, }, /* 350 */\n  {    48,      6,     12,      0,      0,  34816,     94, }, /* 351 */\n  {    48,     26,     12,      0,      0,  28672,     74, }, /* 352 */\n  {    48,     21,     12,      0,      0,  28672,     74, }, /* 353 */\n  {    48,     21,     12,      0,      0,  28672,    110, }, /* 354 */\n  {    48,     21,     12,      0,      0,  28672,    128, }, /* 355 */\n  {    48,      6,     12,      0,      0,  34816,    138, }, /* 356 */\n  {    48,     12,      3,      0,      0,  26624,    104, }, /* 357 */\n  {    48,     23,     12,      0,      0,  34816,     74, }, /* 358 */\n  {    54,      7,     12,      0,      0,  34816,     84, }, /* 359 */\n  {    54,     12,      3,      0,      0,  26624,    106, }, /* 360 */\n  {    54,     12,      3,      0,      0,  26624,     98, }, /* 361 */\n  {    54,      6,     12,      0,      0,  34816,    148, }, /* 362 */\n  {    54,     12,      3,      0,      0,  26624,    104, }, /* 363 */\n  {    54,     21,     12,      0,      0,  34816,    110, }, /* 364 */\n  {    54,     21,     12,      0,      0,  34816,     74, }, /* 365 */\n  {    54,     21,     12,      0,      0,  34816,    128, }, /* 366 */\n  {    59,      7,     12,      0,      0,  34816,     84, }, /* 367 */\n  {    59,     12,      3,      0,      0,  26624,    104, }, /* 368 */\n  {    59,     21,     12,      0,      0,  34816,    110, }, /* 369 */\n  {     5,     24,     12,      0,      0,      0,    126, }, /* 370 */\n  {     5,     12,      3,      0,      0,  26624,    150, }, /* 371 */\n  {     5,     12,      3,      0,      0,  26624,    104, }, /* 372 */\n  {     5,     12,      3,      0,      0,  26624,    152, }, /* 373 */\n  {     8,     12,      3,      0,      0,  26624,    106, }, /* 374 */\n  {     8,     10,      5,      0,      0,  18432,    154, }, /* 375 */\n  {     8,      7,     12,      0,      0,  18432,     84, }, /* 376 */\n  {     8,      7,     12,      0,      0,  18432,    156, }, /* 377 */\n  {     8,     12,      3,      0,      0,  26624,     98, }, /* 378 */\n  {     8,     12,      3,      0,      0,  26624,    158, }, /* 379 */\n  {   106,     12,      3,      0,      0,  26804,     98, }, /* 380 */\n  {   106,     12,      3,      0,      0,  26808,     98, }, /* 381 */\n  {    99,     21,     12,      0,      0,  18620,    128, }, /* 382 */\n  {    99,     21,     12,      0,      0,  18624,    128, }, /* 383 */\n  {     8,     13,     12,      0,      0,  18628,    144, }, /* 384 */\n  {     8,     21,     12,      0,      0,  18432,     74, }, /* 385 */\n  {     8,      6,     12,      0,      0,  18432,     94, }, /* 386 */\n  {     9,      7,     12,      0,      0,  18432,     84, }, /* 387 */\n  {     9,     12,      3,      0,      0,  26624,    106, }, /* 388 */\n  {     9,     10,      5,      0,      0,  18432,    154, }, /* 389 */\n  {     9,      7,     12,      0,      0,  18432,    156, }, /* 390 */\n  {     9,     12,      3,      0,      0,  26624,     98, }, /* 391 */\n  {     9,     10,      3,      0,      0,  18432,    160, }, /* 392 */\n  {     9,     12,      3,      0,      0,  26624,    158, }, /* 393 */\n  {     9,     13,     12,      0,      0,  18632,    144, }, /* 394 */\n  {     9,     23,     12,      0,      0,  14336,     74, }, /* 395 */\n  {     9,     15,     12,      0,      0,  18432,     74, }, /* 396 */\n  {     9,     26,     12,      0,      0,  18432,     74, }, /* 397 */\n  {     9,     21,     12,      0,      0,  18432,     74, }, /* 398 */\n  {     9,     12,      3,      0,      0,  26624,    104, }, /* 399 */\n  {    10,     12,      3,      0,      0,  26624,    106, }, /* 400 */\n  {    10,     10,      5,      0,      0,  18432,    154, }, /* 401 */\n  {    10,      7,     12,      0,      0,  18432,     84, }, /* 402 */\n  {    10,     12,      3,      0,      0,  26624,     98, }, /* 403 */\n  {    10,     12,      3,      0,      0,  26624,    158, }, /* 404 */\n  {    10,     13,     12,      0,      0,  18636,    144, }, /* 405 */\n  {    10,     12,      3,      0,      0,  26624,    162, }, /* 406 */\n  {    10,     21,     12,      0,      0,  18432,     74, }, /* 407 */\n  {    11,     12,      3,      0,      0,  26624,    106, }, /* 408 */\n  {    11,     10,      5,      0,      0,  18432,    154, }, /* 409 */\n  {    11,      7,     12,      0,      0,  18432,     84, }, /* 410 */\n  {    11,      7,     12,      0,      0,  18432,    156, }, /* 411 */\n  {    11,     12,      3,      0,      0,  26624,     98, }, /* 412 */\n  {    11,     12,      3,      0,      0,  26624,    158, }, /* 413 */\n  {    11,     13,     12,      0,      0,  18640,    144, }, /* 414 */\n  {    11,     21,     12,      0,      0,  18432,     74, }, /* 415 */\n  {    11,     23,     12,      0,      0,  14336,     74, }, /* 416 */\n  {    11,     12,      3,      0,      0,  26624,    162, }, /* 417 */\n  {    12,     12,      3,      0,      0,  26624,    106, }, /* 418 */\n  {    12,     10,      5,      0,      0,  18432,    154, }, /* 419 */\n  {    12,      7,     12,      0,      0,  18432,     84, }, /* 420 */\n  {    12,      7,     12,      0,      0,  18432,    156, }, /* 421 */\n  {    12,     12,      3,      0,      0,  26624,     98, }, /* 422 */\n  {    12,     10,      3,      0,      0,  18432,    160, }, /* 423 */\n  {    12,     12,      3,      0,      0,  26624,    158, }, /* 424 */\n  {    12,     12,      3,      0,      0,  26624,    164, }, /* 425 */\n  {    12,     13,     12,      0,      0,  18432,    144, }, /* 426 */\n  {    12,     26,     12,      0,      0,  18432,     74, }, /* 427 */\n  {    12,     15,     12,      0,      0,  18432,     74, }, /* 428 */\n  {    13,     12,      3,      0,      0,  26624,    106, }, /* 429 */\n  {    13,      7,     12,      0,      0,  18432,     84, }, /* 430 */\n  {    13,     10,      3,      0,      0,  18432,    160, }, /* 431 */\n  {    13,     10,      5,      0,      0,  18432,    154, }, /* 432 */\n  {    13,     12,      3,      0,      0,  26624,    158, }, /* 433 */\n  {    13,     13,     12,      0,      0,  18644,    144, }, /* 434 */\n  {    13,     15,     12,      0,      0,  18644,     74, }, /* 435 */\n  {    13,     26,     12,      0,      0,  28884,     74, }, /* 436 */\n  {    13,     26,     12,      0,      0,  28672,     74, }, /* 437 */\n  {    13,     23,     12,      0,      0,  14336,     74, }, /* 438 */\n  {    14,     12,      3,      0,      0,  26624,    106, }, /* 439 */\n  {    14,     10,      5,      0,      0,  18432,    154, }, /* 440 */\n  {    14,      7,     12,      0,      0,  18432,     84, }, /* 441 */\n  {    14,      7,     12,      0,      0,  18432,    156, }, /* 442 */\n  {    14,     12,      3,      0,      0,  26624,     98, }, /* 443 */\n  {    14,     12,      3,      0,      0,  26624,    158, }, /* 444 */\n  {    14,     13,     12,      0,      0,  18432,    144, }, /* 445 */\n  {    14,     21,     12,      0,      0,  18432,     74, }, /* 446 */\n  {    14,     15,     12,      0,      0,  28672,     74, }, /* 447 */\n  {    14,     26,     12,      0,      0,  18432,     74, }, /* 448 */\n  {    15,      7,     12,      0,      0,  18432,     84, }, /* 449 */\n  {    15,     12,      3,      0,      0,  26624,    106, }, /* 450 */\n  {    15,     10,      5,      0,      0,  18432,    154, }, /* 451 */\n  {    15,     21,     12,      0,      0,  18432,     74, }, /* 452 */\n  {    15,     12,      3,      0,      0,  26624,     98, }, /* 453 */\n  {    15,     12,      3,      0,      0,  18432,    106, }, /* 454 */\n  {    15,     10,      3,      0,      0,  18432,    160, }, /* 455 */\n  {    15,     12,      3,      0,      0,  26624,    158, }, /* 456 */\n  {    15,     13,     12,      0,      0,  18648,    144, }, /* 457 */\n  {    16,     12,      3,      0,      0,  26624,    106, }, /* 458 */\n  {    16,     10,      5,      0,      0,  18432,    154, }, /* 459 */\n  {    16,      7,     12,      0,      0,  18432,     84, }, /* 460 */\n  {    16,      7,     12,      0,      0,  18432,    156, }, /* 461 */\n  {    16,     12,      3,      0,      0,  26624,    158, }, /* 462 */\n  {    16,     10,      3,      0,      0,  18432,    160, }, /* 463 */\n  {    16,      7,      4,      0,      0,  18432,     84, }, /* 464 */\n  {    16,     26,     12,      0,      0,  18432,     74, }, /* 465 */\n  {    16,     15,     12,      0,      0,  18432,     74, }, /* 466 */\n  {    16,     13,     12,      0,      0,  18432,    144, }, /* 467 */\n  {    17,     12,      3,      0,      0,  26624,    106, }, /* 468 */\n  {    17,     10,      5,      0,      0,  18432,    154, }, /* 469 */\n  {    17,      7,     12,      0,      0,  18432,     84, }, /* 470 */\n  {    17,     12,      3,      0,      0,  26624,    158, }, /* 471 */\n  {    17,     10,      3,      0,      0,  18432,    160, }, /* 472 */\n  {    17,     13,     12,      0,      0,  18432,    144, }, /* 473 */\n  {    17,     21,     12,      0,      0,  18432,     74, }, /* 474 */\n  {    18,      7,     12,      0,      0,  18432,     84, }, /* 475 */\n  {    18,     12,      3,      0,      0,  26624,    106, }, /* 476 */\n  {    18,      7,      5,      0,      0,  18432,    166, }, /* 477 */\n  {    18,     12,      3,      0,      0,  26624,    168, }, /* 478 */\n  {    99,     23,     12,      0,      0,  14336,     74, }, /* 479 */\n  {    18,      7,     12,      0,      0,  18432,    170, }, /* 480 */\n  {    18,      6,     12,      0,      0,  18432,    138, }, /* 481 */\n  {    18,     12,      3,      0,      0,  26624,     98, }, /* 482 */\n  {    18,     21,     12,      0,      0,  18432,     74, }, /* 483 */\n  {    18,     13,     12,      0,      0,  18432,    144, }, /* 484 */\n  {    18,     21,     12,      0,      0,  18432,    110, }, /* 485 */\n  {   100,      7,     12,      0,      0,  18432,     84, }, /* 486 */\n  {   100,     12,      3,      0,      0,  26624,    106, }, /* 487 */\n  {   100,      7,      5,      0,      0,  18432,    166, }, /* 488 */\n  {   100,     12,      3,      0,      0,  26624,    158, }, /* 489 */\n  {   100,      7,     12,      0,      0,  18432,    170, }, /* 490 */\n  {   100,      6,     12,      0,      0,  18432,    138, }, /* 491 */\n  {   100,     12,      3,      0,      0,  26624,     98, }, /* 492 */\n  {   100,     12,      3,      0,      0,  26624,    104, }, /* 493 */\n  {   100,     13,     12,      0,      0,  18432,    144, }, /* 494 */\n  {    19,      7,     12,      0,      0,  18432,     84, }, /* 495 */\n  {    19,     26,     12,      0,      0,  18432,     74, }, /* 496 */\n  {    19,     21,     12,      0,      0,  18432,     74, }, /* 497 */\n  {    19,     21,     12,      0,      0,  18432,    110, }, /* 498 */\n  {    19,     12,      3,      0,      0,  26624,     98, }, /* 499 */\n  {    19,     13,     12,      0,      0,  18432,    144, }, /* 500 */\n  {    19,     15,     12,      0,      0,  18432,     74, }, /* 501 */\n  {    19,     22,     12,      0,      0,  28672,    172, }, /* 502 */\n  {    19,     18,     12,      0,      0,  28672,    172, }, /* 503 */\n  {    19,     10,      5,      0,      0,  18432,    174, }, /* 504 */\n  {    19,     12,      3,      0,      0,  26624,    106, }, /* 505 */\n  {    19,     12,      3,      0,      0,  26624,    176, }, /* 506 */\n  {    19,     10,      5,      0,      0,  18432,    154, }, /* 507 */\n  {    19,     12,      3,      0,      0,  26624,    132, }, /* 508 */\n  {    19,     12,      3,      0,      0,  26624,    158, }, /* 509 */\n  {    99,     26,     12,      0,      0,  18432,     74, }, /* 510 */\n  {    20,      7,     12,      0,      0,  18432,     84, }, /* 511 */\n  {    20,     10,     12,      0,      0,  18432,    154, }, /* 512 */\n  {    20,     12,      3,      0,      0,  26624,    106, }, /* 513 */\n  {    20,     10,      5,      0,      0,  18432,    154, }, /* 514 */\n  {    20,     12,      3,      0,      0,  26624,     98, }, /* 515 */\n  {    20,     12,      3,      0,      0,  26624,    158, }, /* 516 */\n  {    20,     13,     12,      0,      0,  18652,    144, }, /* 517 */\n  {    20,     21,     12,      0,      0,  18432,    128, }, /* 518 */\n  {    20,     21,     12,      0,      0,  18432,     74, }, /* 519 */\n  {    20,     10,     12,      0,      0,  18432,    178, }, /* 520 */\n  {    20,     12,      3,      0,      0,  26624,    132, }, /* 521 */\n  {    20,     13,     12,      0,      0,  18432,    144, }, /* 522 */\n  {    20,     26,     12,      0,      0,  18432,     74, }, /* 523 */\n  {    21,      9,     12,      0,   7264,  18432,     76, }, /* 524 */\n  {    21,      5,     12,      0,   3008,  18432,    180, }, /* 525 */\n  {    99,     21,     12,      0,      0,  18656,     74, }, /* 526 */\n  {    21,      6,     12,      0,      0,  18432,    182, }, /* 527 */\n  {    22,      7,      6,      0,      0,  18432,     84, }, /* 528 */\n  {    22,      7,      6,      0,      0,  18432,    184, }, /* 529 */\n  {    22,      7,      7,      0,      0,  18432,    184, }, /* 530 */\n  {    22,      7,      7,      0,      0,  18432,     84, }, /* 531 */\n  {    22,      7,      8,      0,      0,  18432,     84, }, /* 532 */\n  {    23,      7,     12,      0,      0,  18432,     84, }, /* 533 */\n  {    23,     12,      3,      0,      0,  26624,     98, }, /* 534 */\n  {    23,     21,     12,      0,      0,  18432,     74, }, /* 535 */\n  {    23,     21,     12,      0,      0,  18432,    110, }, /* 536 */\n  {    23,     21,     12,      0,      0,  18432,    128, }, /* 537 */\n  {    23,     15,     12,      0,      0,  18432,    144, }, /* 538 */\n  {    23,     15,     12,      0,      0,  18432,     74, }, /* 539 */\n  {    23,     26,     12,      0,      0,  28672,     74, }, /* 540 */\n  {    24,      9,     12,      0,  38864,  18432,    186, }, /* 541 */\n  {    24,      9,     12,      0,      8,  18432,    186, }, /* 542 */\n  {    24,      5,     12,      0,     -8,  18432,     70, }, /* 543 */\n  {   101,     17,     12,      0,      0,  28672,    130, }, /* 544 */\n  {   101,      7,     12,      0,      0,  18432,     84, }, /* 545 */\n  {   101,     26,     12,      0,      0,  18432,     74, }, /* 546 */\n  {   101,     21,     12,      0,      0,  18432,    128, }, /* 547 */\n  {   102,     29,     12,      0,      0,  45056,     52, }, /* 548 */\n  {   102,      7,     12,      0,      0,  18432,     84, }, /* 549 */\n  {   102,     22,     12,      0,      0,  28672,    172, }, /* 550 */\n  {   102,     18,     12,      0,      0,  28672,    172, }, /* 551 */\n  {    25,      7,     12,      0,      0,  18432,     84, }, /* 552 */\n  {    99,     21,     12,      0,      0,  18660,    110, }, /* 553 */\n  {    25,     14,     12,      0,      0,  18432,     84, }, /* 554 */\n  {    33,      7,     12,      0,      0,  18432,     84, }, /* 555 */\n  {    33,     12,      3,      0,      0,  26624,    106, }, /* 556 */\n  {    33,     12,      3,      0,      0,  26624,    158, }, /* 557 */\n  {    33,     10,      3,      0,      0,  18432,    188, }, /* 558 */\n  {    34,      7,     12,      0,      0,  18432,     84, }, /* 559 */\n  {    34,     12,      3,      0,      0,  26624,    106, }, /* 560 */\n  {    34,     10,      3,      0,      0,  18432,    188, }, /* 561 */\n  {    99,     21,     12,      0,      0,  18664,    128, }, /* 562 */\n  {    35,      7,     12,      0,      0,  18432,     84, }, /* 563 */\n  {    35,     12,      3,      0,      0,  26624,    106, }, /* 564 */\n  {    36,      7,     12,      0,      0,  18432,     84, }, /* 565 */\n  {    36,     12,      3,      0,      0,  26624,    106, }, /* 566 */\n  {   103,      7,     12,      0,      0,  18432,     84, }, /* 567 */\n  {   103,      7,     12,      0,      0,  18432,    146, }, /* 568 */\n  {   103,     12,      3,      0,      0,  26624,    102, }, /* 569 */\n  {   103,     10,      5,      0,      0,  18432,    154, }, /* 570 */\n  {   103,     12,      3,      0,      0,  26624,    106, }, /* 571 */\n  {   103,     12,      3,      0,      0,  26624,     98, }, /* 572 */\n  {   103,     12,      3,      0,      0,  26624,    158, }, /* 573 */\n  {   103,     21,     12,      0,      0,  18432,    128, }, /* 574 */\n  {   103,     21,     12,      0,      0,  18432,    110, }, /* 575 */\n  {   103,      6,     12,      0,      0,  18432,    148, }, /* 576 */\n  {   103,     21,     12,      0,      0,  18432,     74, }, /* 577 */\n  {   103,     23,     12,      0,      0,  14336,     74, }, /* 578 */\n  {   103,     13,     12,      0,      0,  18432,    144, }, /* 579 */\n  {   103,     15,     12,      0,      0,  28672,     74, }, /* 580 */\n  {    26,     21,     12,      0,      0,  28672,     74, }, /* 581 */\n  {    99,     21,     12,      0,      0,  28908,    110, }, /* 582 */\n  {    99,     21,     12,      0,      0,  28908,    128, }, /* 583 */\n  {    26,     21,     12,      0,      0,  28672,    110, }, /* 584 */\n  {    26,     17,     12,      0,      0,  28672,    130, }, /* 585 */\n  {    26,     21,     12,      0,      0,  28672,    128, }, /* 586 */\n  {    26,     21,     12,      0,      0,  28672,    190, }, /* 587 */\n  {    26,     12,      3,      0,      0,  26624,    192, }, /* 588 */\n  {    26,      1,      2,      0,      0,   6144,     66, }, /* 589 */\n  {    26,     13,     12,      0,      0,  18432,    144, }, /* 590 */\n  {    26,      7,     12,      0,      0,  18432,     84, }, /* 591 */\n  {    26,      6,     12,      0,      0,  18432,    138, }, /* 592 */\n  {    26,     12,      3,      0,      0,  26624,    194, }, /* 593 */\n  {    26,     12,      3,      0,      0,  26624,    106, }, /* 594 */\n  {    37,      7,     12,      0,      0,  18432,     84, }, /* 595 */\n  {    37,     12,      3,      0,      0,  26624,    106, }, /* 596 */\n  {    37,     10,      5,      0,      0,  18432,    154, }, /* 597 */\n  {    37,     12,      3,      0,      0,  26624,     98, }, /* 598 */\n  {    37,     26,     12,      0,      0,  28672,     74, }, /* 599 */\n  {    37,     21,     12,      0,      0,  28672,    128, }, /* 600 */\n  {    37,     13,     12,      0,      0,  18432,    144, }, /* 601 */\n  {    38,      7,     12,      0,      0,  18432,     84, }, /* 602 */\n  {   110,      7,     12,      0,      0,  18432,     84, }, /* 603 */\n  {   110,      7,     12,      0,      0,  18432,    170, }, /* 604 */\n  {   110,     13,     12,      0,      0,  18432,    144, }, /* 605 */\n  {   110,     15,     12,      0,      0,  18432,    144, }, /* 606 */\n  {   110,     26,     12,      0,      0,  28672,     74, }, /* 607 */\n  {   103,     26,     12,      0,      0,  28672,     74, }, /* 608 */\n  {    42,      7,     12,      0,      0,  18432,     84, }, /* 609 */\n  {    42,     12,      3,      0,      0,  26624,    106, }, /* 610 */\n  {    42,     10,      5,      0,      0,  18432,    154, }, /* 611 */\n  {    42,     21,     12,      0,      0,  18432,     74, }, /* 612 */\n  {   123,      7,     12,      0,      0,  18432,     84, }, /* 613 */\n  {   123,     10,      5,      0,      0,  18432,    154, }, /* 614 */\n  {   123,     12,      3,      0,      0,  26624,    106, }, /* 615 */\n  {   123,     12,      3,      0,      0,  26624,    158, }, /* 616 */\n  {   123,     10,     12,      0,      0,  18432,    154, }, /* 617 */\n  {   123,     12,      3,      0,      0,  26624,     98, }, /* 618 */\n  {   123,     13,     12,      0,      0,  18432,    144, }, /* 619 */\n  {   123,     21,     12,      0,      0,  18432,     74, }, /* 620 */\n  {   123,      6,     12,      0,      0,  18432,    138, }, /* 621 */\n  {   123,     21,     12,      0,      0,  18432,    128, }, /* 622 */\n  {   106,     11,      3,      0,      0,  26624,    196, }, /* 623 */\n  {   106,     12,      3,      0,      0,  26624,    106, }, /* 624 */\n  {   113,     12,      3,      0,      0,  26624,    106, }, /* 625 */\n  {   113,     10,      5,      0,      0,  18432,    154, }, /* 626 */\n  {   113,      7,     12,      0,      0,  18432,     84, }, /* 627 */\n  {   113,     12,      3,      0,      0,  26624,     98, }, /* 628 */\n  {   113,     10,      3,      0,      0,  18432,    160, }, /* 629 */\n  {   113,     10,      3,      0,      0,  18432,    188, }, /* 630 */\n  {   113,     21,     12,      0,      0,  18432,    128, }, /* 631 */\n  {   113,     13,     12,      0,      0,  18432,    144, }, /* 632 */\n  {   113,     21,     12,      0,      0,  18432,     74, }, /* 633 */\n  {   113,     21,     12,      0,      0,  18432,    110, }, /* 634 */\n  {   113,     26,     12,      0,      0,  18432,     74, }, /* 635 */\n  {   116,     12,      3,      0,      0,  26624,    106, }, /* 636 */\n  {   116,     10,      5,      0,      0,  18432,    154, }, /* 637 */\n  {   116,      7,     12,      0,      0,  18432,     84, }, /* 638 */\n  {   116,     10,      3,      0,      0,  18432,    188, }, /* 639 */\n  {   116,     12,      3,      0,      0,  26624,    158, }, /* 640 */\n  {   116,     13,     12,      0,      0,  18432,    144, }, /* 641 */\n  {   132,      7,     12,      0,      0,  18432,     84, }, /* 642 */\n  {   132,     12,      3,      0,      0,  26624,     98, }, /* 643 */\n  {   132,     10,      5,      0,      0,  18432,    154, }, /* 644 */\n  {   132,     12,      3,      0,      0,  26624,    106, }, /* 645 */\n  {   132,     10,      3,      0,      0,  18432,    188, }, /* 646 */\n  {   132,     21,     12,      0,      0,  18432,     74, }, /* 647 */\n  {   117,      7,     12,      0,      0,  18432,     84, }, /* 648 */\n  {   117,     10,      5,      0,      0,  18432,    154, }, /* 649 */\n  {   117,     12,      3,      0,      0,  26624,    106, }, /* 650 */\n  {   117,     12,      3,      0,      0,  26624,    198, }, /* 651 */\n  {   117,     12,      3,      0,      0,  26624,     98, }, /* 652 */\n  {   117,     21,     12,      0,      0,  18432,    128, }, /* 653 */\n  {   117,     21,     12,      0,      0,  18432,    110, }, /* 654 */\n  {   117,     13,     12,      0,      0,  18432,    144, }, /* 655 */\n  {   118,     13,     12,      0,      0,  18432,    144, }, /* 656 */\n  {   118,      7,     12,      0,      0,  18432,     84, }, /* 657 */\n  {   118,      6,     12,      0,      0,  18432,     94, }, /* 658 */\n  {   118,      6,     12,      0,      0,  18432,     96, }, /* 659 */\n  {   118,     21,     12,      0,      0,  18432,    128, }, /* 660 */\n  {     2,      5,     12,     63,  -6222,  18432,     70, }, /* 661 */\n  {     2,      5,     12,     67,  -6221,  18432,     70, }, /* 662 */\n  {     2,      5,     12,     71,  -6212,  18432,     70, }, /* 663 */\n  {     2,      5,     12,     75,  -6210,  18432,     70, }, /* 664 */\n  {     2,      5,     12,     79,  -6210,  18432,     70, }, /* 665 */\n  {     2,      5,     12,     79,  -6211,  18432,     70, }, /* 666 */\n  {     2,      5,     12,     84,  -6204,  18432,     70, }, /* 667 */\n  {     2,      5,     12,     88,  -6180,  18432,     70, }, /* 668 */\n  {     2,      5,     12,    108,  35267,  18432,     70, }, /* 669 */\n  {    21,      9,     12,      0,  -3008,  18432,     76, }, /* 670 */\n  {   116,     21,     12,      0,      0,  18432,     74, }, /* 671 */\n  {   106,     12,      3,      0,      0,  26864,     98, }, /* 672 */\n  {   106,     12,      3,      0,      0,  26868,     98, }, /* 673 */\n  {    99,     21,     12,      0,      0,  18680,    200, }, /* 674 */\n  {   106,     12,      3,      0,      0,  26876,     98, }, /* 675 */\n  {   106,     12,      3,      0,      0,  26880,     98, }, /* 676 */\n  {   106,     12,      3,      0,      0,  26884,     98, }, /* 677 */\n  {    99,     10,      5,      0,      0,  18684,    174, }, /* 678 */\n  {    99,      7,     12,      0,      0,  18696,     84, }, /* 679 */\n  {    99,      7,     12,      0,      0,  18684,     84, }, /* 680 */\n  {    99,      7,     12,      0,      0,  18676,     84, }, /* 681 */\n  {    99,      7,     12,      0,      0,  18700,     84, }, /* 682 */\n  {    99,      7,     12,      0,      0,  18704,     84, }, /* 683 */\n  {   106,     12,      3,      0,      0,  26900,     98, }, /* 684 */\n  {    99,     10,      5,      0,      0,  18712,    174, }, /* 685 */\n  {   106,     12,      3,      0,      0,  26896,     98, }, /* 686 */\n  {    99,      7,     12,      0,      0,  18716,     84, }, /* 687 */\n  {     2,      5,     12,      0,      0,  18432,     60, }, /* 688 */\n  {     1,      6,     12,      0,      0,  18432,     90, }, /* 689 */\n  {     2,      6,     12,      0,      0,  18432,    182, }, /* 690 */\n  {     0,      5,     12,      0,  35332,  18432,     78, }, /* 691 */\n  {     0,      5,     12,      0,   3814,  18432,     78, }, /* 692 */\n  {     0,      5,     12,      0,  35384,  18432,     78, }, /* 693 */\n  {     0,      5,     12,      0,      0,  18432,    202, }, /* 694 */\n  {     0,      6,     12,      0,      0,  18432,    182, }, /* 695 */\n  {     0,      6,     12,      0,      0,  18432,    204, }, /* 696 */\n  {     1,      6,     12,      0,      0,  18432,    182, }, /* 697 */\n  {   106,     12,      3,      0,      0,  26740,    104, }, /* 698 */\n  {   106,     12,      3,      0,      0,  26912,     98, }, /* 699 */\n  {   106,     12,      3,      0,      0,  26916,     98, }, /* 700 */\n  {     0,      9,     12,     92,      1,  18432,     76, }, /* 701 */\n  {     0,      5,     12,     92,     -1,  18432,     78, }, /* 702 */\n  {     0,      5,     12,      0,      0,  18432,     70, }, /* 703 */\n  {     0,      5,     12,     92,    -58,  18432,     70, }, /* 704 */\n  {     0,      9,     12,      0,  -7615,  18432,     76, }, /* 705 */\n  {     1,      5,     12,      0,      8,  18432,     78, }, /* 706 */\n  {     1,      9,     12,      0,     -8,  18432,     76, }, /* 707 */\n  {     1,      5,     12,      0,      0,  18432,     78, }, /* 708 */\n  {     1,      5,     12,      0,     74,  18432,     78, }, /* 709 */\n  {     1,      5,     12,      0,     86,  18432,     78, }, /* 710 */\n  {     1,      5,     12,      0,    100,  18432,     78, }, /* 711 */\n  {     1,      5,     12,      0,    128,  18432,     78, }, /* 712 */\n  {     1,      5,     12,      0,    112,  18432,     78, }, /* 713 */\n  {     1,      5,     12,      0,    126,  18432,     78, }, /* 714 */\n  {     1,      5,     12,      0,      8,  18432,     70, }, /* 715 */\n  {     1,      8,     12,      0,     -8,  18432,     88, }, /* 716 */\n  {     1,      5,     12,      0,      0,  18432,     70, }, /* 717 */\n  {     1,      5,     12,      0,      9,  18432,     70, }, /* 718 */\n  {     1,      9,     12,      0,    -74,  18432,     76, }, /* 719 */\n  {     1,      8,     12,      0,     -9,  18432,     88, }, /* 720 */\n  {     1,      5,     12,     21,  -7173,  18432,     78, }, /* 721 */\n  {     1,      9,     12,      0,    -86,  18432,     76, }, /* 722 */\n  {     1,      5,     12,      0,  -7235,  18432,     78, }, /* 723 */\n  {     1,      9,     12,      0,   -100,  18432,     76, }, /* 724 */\n  {     1,      5,     12,      0,  -7219,  18432,     78, }, /* 725 */\n  {     1,      9,     12,      0,   -112,  18432,     76, }, /* 726 */\n  {     1,      9,     12,      0,   -128,  18432,     76, }, /* 727 */\n  {     1,      9,     12,      0,   -126,  18432,     76, }, /* 728 */\n  {    99,     29,     12,      0,      0,  45056,     52, }, /* 729 */\n  {   106,      1,      3,      0,      0,   6144,    206, }, /* 730 */\n  {   106,      1,     13,      0,      0,   6144,    208, }, /* 731 */\n  {    99,      1,      2,      0,      0,  18432,    210, }, /* 732 */\n  {    99,      1,      2,      0,      0,  34816,    210, }, /* 733 */\n  {    99,     17,     12,      0,      0,  28672,    212, }, /* 734 */\n  {    99,     21,     12,      0,      0,  28672,     64, }, /* 735 */\n  {    99,     20,     12,      0,      0,  28672,    214, }, /* 736 */\n  {    99,     19,     12,      0,      0,  28672,    214, }, /* 737 */\n  {    99,     22,     12,      0,      0,  28672,    216, }, /* 738 */\n  {    99,     20,     12,      0,      0,  28672,    216, }, /* 739 */\n  {    99,     19,     12,      0,      0,  28672,    216, }, /* 740 */\n  {    99,     21,     12,      0,      0,  28672,    218, }, /* 741 */\n  {    99,     21,     12,      0,      0,  28672,    220, }, /* 742 */\n  {    99,     27,      2,      0,      0,  45056,     50, }, /* 743 */\n  {    99,     28,      2,      0,      0,   4096,     50, }, /* 744 */\n  {    99,      1,      2,      0,      0,  20480,    136, }, /* 745 */\n  {    99,      1,      2,      0,      0,  36864,    136, }, /* 746 */\n  {    99,      1,      2,      0,      0,  30720,    136, }, /* 747 */\n  {    99,      1,      2,      0,      0,  24576,    136, }, /* 748 */\n  {    99,      1,      2,      0,      0,  40960,    136, }, /* 749 */\n  {    99,     29,     12,      0,      0,   8488,     52, }, /* 750 */\n  {    99,     21,     12,      0,      0,  14336,     54, }, /* 751 */\n  {    99,     21,     12,      0,      0,  14336,     64, }, /* 752 */\n  {    99,     21,     14,      0,      0,  28672,    222, }, /* 753 */\n  {    99,     21,     12,      0,      0,  28672,    224, }, /* 754 */\n  {    99,     16,     12,      0,      0,  28672,    144, }, /* 755 */\n  {    99,     16,     12,      0,      0,  28672,    226, }, /* 756 */\n  {    99,     25,     12,      0,      0,   8192,     64, }, /* 757 */\n  {    99,     22,     12,      0,      0,  28672,    228, }, /* 758 */\n  {    99,     18,     12,      0,      0,  28672,    228, }, /* 759 */\n  {    99,     21,     12,      0,      0,  28972,     54, }, /* 760 */\n  {    99,     21,     12,      0,      0,  28672,    212, }, /* 761 */\n  {    99,     21,     12,      0,      0,  28976,     54, }, /* 762 */\n  {    99,     21,     12,      0,      0,  28980,     54, }, /* 763 */\n  {    99,      1,      2,      0,      0,   6144,    230, }, /* 764 */\n  {    98,      2,      2,      0,      0,   6144,    232, }, /* 765 */\n  {    99,      1,      2,      0,      0,  22528,    136, }, /* 766 */\n  {    99,      1,      2,      0,      0,  38912,    136, }, /* 767 */\n  {    99,      1,      2,      0,      0,  16384,    136, }, /* 768 */\n  {    99,      1,      2,      0,      0,  32768,    136, }, /* 769 */\n  {    99,      1,      2,      0,      0,   6144,    234, }, /* 770 */\n  {    99,     25,     12,      0,      0,  12288,    236, }, /* 771 */\n  {    99,     25,     12,      0,      0,  12288,    238, }, /* 772 */\n  {    99,     25,     12,      0,      0,  28672,    236, }, /* 773 */\n  {    99,     22,     12,      0,      0,  28672,    240, }, /* 774 */\n  {    99,     18,     12,      0,      0,  28672,    240, }, /* 775 */\n  {    98,      2,     12,      0,      0,  14336,      0, }, /* 776 */\n  {   106,     12,      3,      0,      0,  26624,    242, }, /* 777 */\n  {   106,     11,      3,      0,      0,  26624,    124, }, /* 778 */\n  {   106,     11,      3,      0,      0,  26624,    244, }, /* 779 */\n  {   106,     12,      3,      0,      0,  26936,    104, }, /* 780 */\n  {    99,     26,     12,      0,      0,  28672,     74, }, /* 781 */\n  {    99,      9,     12,      0,      0,  18432,    116, }, /* 782 */\n  {    99,      5,     12,      0,      0,  18432,    246, }, /* 783 */\n  {    99,     25,     12,      0,      0,  28672,    248, }, /* 784 */\n  {    99,     26,     14,      0,      0,  28672,    250, }, /* 785 */\n  {     1,      9,     12,     96,  -7517,  18432,     76, }, /* 786 */\n  {    99,     26,     12,      0,      0,  28672,    122, }, /* 787 */\n  {     0,      9,     12,    100,      0,  18432,     76, }, /* 788 */\n  {     0,      9,     12,    104,  -8262,  18432,     76, }, /* 789 */\n  {    99,     26,     12,      0,      0,  14336,    252, }, /* 790 */\n  {     0,      9,     12,      0,     28,  18432,     76, }, /* 791 */\n  {    99,      7,     12,      0,      0,  18432,    254, }, /* 792 */\n  {    99,      5,     14,      0,      0,  18432,    256, }, /* 793 */\n  {    99,     25,     12,      0,      0,  28672,    122, }, /* 794 */\n  {    99,      5,     12,      0,      0,  18432,    258, }, /* 795 */\n  {     0,      5,     12,      0,    -28,  18432,     78, }, /* 796 */\n  {     0,     14,     12,      0,     16,  18432,     76, }, /* 797 */\n  {     0,     14,     12,      0,    -16,  18432,     78, }, /* 798 */\n  {     0,     14,     12,      0,      0,  18432,     84, }, /* 799 */\n  {    99,     25,     14,      0,      0,  28672,    260, }, /* 800 */\n  {    99,     26,     14,      0,      0,  28672,    260, }, /* 801 */\n  {    99,     26,     12,      0,      0,  28672,     64, }, /* 802 */\n  {    99,     25,     12,      0,      0,  28672,    262, }, /* 803 */\n  {    99,     25,     12,      0,      0,  28672,    264, }, /* 804 */\n  {    99,     25,     12,      0,      0,  12288,    266, }, /* 805 */\n  {    99,     22,     12,      0,      0,  28672,    264, }, /* 806 */\n  {    99,     18,     12,      0,      0,  28672,    264, }, /* 807 */\n  {    99,     26,     14,      0,      0,  28672,    268, }, /* 808 */\n  {    99,     22,     12,      0,      0,  28672,    270, }, /* 809 */\n  {    99,     18,     12,      0,      0,  28672,    270, }, /* 810 */\n  {    99,     26,     12,      0,      0,  18432,     54, }, /* 811 */\n  {    99,     26,     14,      0,      0,  28672,    272, }, /* 812 */\n  {    98,      2,     12,      0,      0,  18432,    274, }, /* 813 */\n  {    99,     15,     12,      0,      0,  10240,     74, }, /* 814 */\n  {    99,     26,     12,      0,     26,  18432,    276, }, /* 815 */\n  {    99,     26,     14,      0,     26,  18432,    278, }, /* 816 */\n  {    99,     26,     12,      0,    -26,  18432,    280, }, /* 817 */\n  {    99,     25,     14,      0,      0,  28672,    282, }, /* 818 */\n  {    99,     26,     14,      0,      0,  28672,    284, }, /* 819 */\n  {    99,     26,     14,      0,      0,  28672,    286, }, /* 820 */\n  {    99,     25,     14,      0,      0,  28672,    284, }, /* 821 */\n  {    99,     26,     14,      0,      0,  18432,    272, }, /* 822 */\n  {    99,     26,     14,      0,      0,  28672,    288, }, /* 823 */\n  {   109,     26,     12,      0,      0,  18432,     54, }, /* 824 */\n  {    99,     26,     12,      0,      0,  28672,    228, }, /* 825 */\n  {    44,      9,     12,      0,     48,  18432,     76, }, /* 826 */\n  {    44,      5,     12,      0,    -48,  18432,     78, }, /* 827 */\n  {     0,      9,     12,      0, -10743,  18432,     76, }, /* 828 */\n  {     0,      9,     12,      0,  -3814,  18432,     76, }, /* 829 */\n  {     0,      9,     12,      0, -10727,  18432,     76, }, /* 830 */\n  {     0,      5,     12,      0, -10795,  18432,     78, }, /* 831 */\n  {     0,      5,     12,      0, -10792,  18432,     78, }, /* 832 */\n  {     0,      9,     12,      0, -10780,  18432,     76, }, /* 833 */\n  {     0,      9,     12,      0, -10749,  18432,     76, }, /* 834 */\n  {     0,      9,     12,      0, -10783,  18432,     76, }, /* 835 */\n  {     0,      9,     12,      0, -10782,  18432,     76, }, /* 836 */\n  {     0,      9,     12,      0, -10815,  18432,     76, }, /* 837 */\n  {    43,      5,     12,      0,      0,  18432,     60, }, /* 838 */\n  {    43,     26,     12,      0,      0,  28672,     74, }, /* 839 */\n  {    43,     12,      3,      0,      0,  26624,     98, }, /* 840 */\n  {    43,     21,     12,      0,      0,  28672,    128, }, /* 841 */\n  {    43,     21,     12,      0,      0,  28672,     74, }, /* 842 */\n  {    43,     15,     12,      0,      0,  28672,     74, }, /* 843 */\n  {    21,      5,     12,      0,  -7264,  18432,     78, }, /* 844 */\n  {    45,      7,     12,      0,      0,  18432,     84, }, /* 845 */\n  {    45,      6,     12,      0,      0,  18432,    148, }, /* 846 */\n  {    45,     21,     12,      0,      0,  18432,     74, }, /* 847 */\n  {    45,     12,      3,      0,      0,  26624,    290, }, /* 848 */\n  {     2,     12,      3,      0,      0,  26624,    106, }, /* 849 */\n  {    99,     20,     12,      0,      0,  28672,    228, }, /* 850 */\n  {    99,     19,     12,      0,      0,  28672,    228, }, /* 851 */\n  {    99,     17,     12,      0,      0,  28988,    212, }, /* 852 */\n  {    99,      6,     12,      0,      0,  28672,    292, }, /* 853 */\n  {    99,     21,     12,      0,      0,  28992,     54, }, /* 854 */\n  {    99,     21,     12,      0,      0,  28996,     54, }, /* 855 */\n  {    99,     21,     12,      0,      0,  29000,    224, }, /* 856 */\n  {    99,     21,     12,      0,      0,  29004,    294, }, /* 857 */\n  {    99,     21,     12,      0,      0,  28812,     54, }, /* 858 */\n  {    99,     21,     12,      0,      0,  28672,    294, }, /* 859 */\n  {    30,     26,     12,      0,      0,  28672,    296, }, /* 860 */\n  {    99,     26,     12,      0,      0,  29008,    298, }, /* 861 */\n  {    99,     26,     12,      0,      0,  29008,    300, }, /* 862 */\n  {    99,     26,     12,      0,      0,  29008,    302, }, /* 863 */\n  {    99,     21,     12,      0,      0,  29012,    294, }, /* 864 */\n  {    99,     21,     12,      0,      0,  29016,    224, }, /* 865 */\n  {    99,     21,     12,      0,      0,  29020,     54, }, /* 866 */\n  {    30,      6,     12,      0,      0,  18432,    138, }, /* 867 */\n  {    99,      7,     12,      0,      0,  18784,    304, }, /* 868 */\n  {    30,     14,     12,      0,      0,  18432,    304, }, /* 869 */\n  {    99,     22,     12,      0,      0,  29028,    228, }, /* 870 */\n  {    99,     18,     12,      0,      0,  29028,    228, }, /* 871 */\n  {    99,     22,     12,      0,      0,  29032,    228, }, /* 872 */\n  {    99,     18,     12,      0,      0,  29032,    228, }, /* 873 */\n  {    99,     22,     12,      0,      0,  29036,     62, }, /* 874 */\n  {    99,     18,     12,      0,      0,  29036,     62, }, /* 875 */\n  {    99,     22,     12,      0,      0,  29036,    228, }, /* 876 */\n  {    99,     18,     12,      0,      0,  29036,    228, }, /* 877 */\n  {    99,     26,     12,      0,      0,  29020,     54, }, /* 878 */\n  {    99,     17,     12,      0,      0,  29020,    212, }, /* 879 */\n  {    99,     22,     12,      0,      0,  29020,    216, }, /* 880 */\n  {    99,     18,     12,      0,      0,  29020,    216, }, /* 881 */\n  {   106,     12,      3,      0,      0,  26992,     98, }, /* 882 */\n  {    22,     10,      3,      0,      0,  18432,    306, }, /* 883 */\n  {    99,     17,     14,      0,      0,  29020,    308, }, /* 884 */\n  {    99,      6,     12,      0,      0,  18804,    138, }, /* 885 */\n  {    99,     26,     12,      0,      0,  29020,     74, }, /* 886 */\n  {    30,      6,     12,      0,      0,  18432,    148, }, /* 887 */\n  {    99,      7,     12,      0,      0,  18808,     84, }, /* 888 */\n  {    99,     21,     14,      0,      0,  29048,    250, }, /* 889 */\n  {    99,     26,     12,      0,      0,  29024,     74, }, /* 890 */\n  {    27,      7,     12,      0,      0,  18432,     84, }, /* 891 */\n  {   106,     12,      3,      0,      0,  26996,     98, }, /* 892 */\n  {    99,     24,     12,      0,      0,  29044,    310, }, /* 893 */\n  {    27,      6,     12,      0,      0,  18432,    138, }, /* 894 */\n  {    99,     17,     12,      0,      0,  29044,    130, }, /* 895 */\n  {    28,      7,     12,      0,      0,  18432,     84, }, /* 896 */\n  {    99,     21,     12,      0,      0,  29036,    144, }, /* 897 */\n  {    99,      6,     12,      0,      0,  18804,     96, }, /* 898 */\n  {    28,      6,     12,      0,      0,  18432,    138, }, /* 899 */\n  {    29,      7,     12,      0,      0,  18432,     84, }, /* 900 */\n  {    22,      7,     12,      0,      0,  18432,     84, }, /* 901 */\n  {    22,      7,     12,      0,      0,  18432,    184, }, /* 902 */\n  {    99,     26,     12,      0,      0,  18784,     74, }, /* 903 */\n  {    99,     15,     12,      0,      0,  18784,     74, }, /* 904 */\n  {    22,     26,     12,      0,      0,  18432,     74, }, /* 905 */\n  {    22,     26,     12,      0,      0,  28672,     74, }, /* 906 */\n  {    99,     15,     12,      0,      0,  18432,     74, }, /* 907 */\n  {    99,     26,     14,      0,      0,  18784,    250, }, /* 908 */\n  {    28,     26,     12,      0,      0,  18432,     74, }, /* 909 */\n  {    30,      7,     12,      0,      0,  18432,    312, }, /* 910 */\n  {    31,      7,     12,      0,      0,  18432,     84, }, /* 911 */\n  {    31,      6,     12,      0,      0,  18432,    138, }, /* 912 */\n  {    31,     26,     12,      0,      0,  28672,     74, }, /* 913 */\n  {    55,      7,     12,      0,      0,  18432,     84, }, /* 914 */\n  {    55,      6,     12,      0,      0,  18432,    148, }, /* 915 */\n  {    55,     21,     12,      0,      0,  18432,    110, }, /* 916 */\n  {    55,     21,     12,      0,      0,  18432,    128, }, /* 917 */\n  {   119,      7,     12,      0,      0,  18432,     84, }, /* 918 */\n  {   119,      6,     12,      0,      0,  18432,    138, }, /* 919 */\n  {   119,     21,     12,      0,      0,  28672,    110, }, /* 920 */\n  {   119,     21,     12,      0,      0,  28672,    128, }, /* 921 */\n  {   119,     13,     12,      0,      0,  18432,    144, }, /* 922 */\n  {     2,      9,     12,    108,      1,  18432,     76, }, /* 923 */\n  {     2,      5,     12,    108, -35267,  18432,     78, }, /* 924 */\n  {     2,      7,     12,      0,      0,  18432,     84, }, /* 925 */\n  {     2,     21,     12,      0,      0,  28672,     74, }, /* 926 */\n  {     2,     12,      3,      0,      0,  26624,     98, }, /* 927 */\n  {     2,      6,     12,      0,      0,  28672,     94, }, /* 928 */\n  {     2,      6,     12,      0,      0,  18432,     90, }, /* 929 */\n  {   126,      7,     12,      0,      0,  18432,     84, }, /* 930 */\n  {   126,     14,     12,      0,      0,  18432,     84, }, /* 931 */\n  {   126,     12,      3,      0,      0,  26624,     98, }, /* 932 */\n  {   126,     21,     12,      0,      0,  18432,     74, }, /* 933 */\n  {   126,     21,     12,      0,      0,  18432,    128, }, /* 934 */\n  {   126,     21,     12,      0,      0,  18432,    110, }, /* 935 */\n  {    99,     24,     12,      0,      0,  29052,     56, }, /* 936 */\n  {     0,      9,     12,      0, -35332,  18432,     76, }, /* 937 */\n  {    99,     24,     12,      0,      0,  18432,     56, }, /* 938 */\n  {     0,      9,     12,      0, -42280,  18432,     76, }, /* 939 */\n  {     0,      5,     12,      0,     48,  18432,     78, }, /* 940 */\n  {     0,      9,     12,      0, -42308,  18432,     76, }, /* 941 */\n  {     0,      9,     12,      0, -42319,  18432,     76, }, /* 942 */\n  {     0,      9,     12,      0, -42315,  18432,     76, }, /* 943 */\n  {     0,      9,     12,      0, -42305,  18432,     76, }, /* 944 */\n  {     0,      9,     12,      0, -42258,  18432,     76, }, /* 945 */\n  {     0,      9,     12,      0, -42282,  18432,     76, }, /* 946 */\n  {     0,      9,     12,      0, -42261,  18432,     76, }, /* 947 */\n  {     0,      9,     12,      0,    928,  18432,     76, }, /* 948 */\n  {     0,      9,     12,      0,    -48,  18432,     76, }, /* 949 */\n  {     0,      9,     12,      0, -42307,  18432,     76, }, /* 950 */\n  {     0,      9,     12,      0, -35384,  18432,     76, }, /* 951 */\n  {     0,      9,     12,      0, -42343,  18432,     76, }, /* 952 */\n  {     0,      9,     12,      0, -42561,  18432,     76, }, /* 953 */\n  {    46,      7,     12,      0,      0,  18432,     84, }, /* 954 */\n  {    46,     12,      3,      0,      0,  26624,    106, }, /* 955 */\n  {    46,     12,      3,      0,      0,  26624,    158, }, /* 956 */\n  {    46,     10,      5,      0,      0,  18432,    154, }, /* 957 */\n  {    46,     26,     12,      0,      0,  28672,     74, }, /* 958 */\n  {    99,     15,     12,      0,      0,  18816,     74, }, /* 959 */\n  {    99,     15,     12,      0,      0,  18820,     74, }, /* 960 */\n  {    99,     26,     12,      0,      0,  18824,     74, }, /* 961 */\n  {    99,     23,     12,      0,      0,  14732,     74, }, /* 962 */\n  {    99,     26,     12,      0,      0,  14728,     74, }, /* 963 */\n  {    47,      7,     12,      0,      0,  18432,     84, }, /* 964 */\n  {    47,     21,     12,      0,      0,  28672,     74, }, /* 965 */\n  {    47,     21,     12,      0,      0,  28672,    128, }, /* 966 */\n  {   120,     10,      5,      0,      0,  18432,    154, }, /* 967 */\n  {   120,      7,     12,      0,      0,  18432,     84, }, /* 968 */\n  {   120,     12,      3,      0,      0,  26624,    158, }, /* 969 */\n  {   120,     12,      3,      0,      0,  26624,    106, }, /* 970 */\n  {   120,     21,     12,      0,      0,  18432,    128, }, /* 971 */\n  {   120,     13,     12,      0,      0,  18432,    144, }, /* 972 */\n  {     8,     12,      3,      0,      0,  27024,     98, }, /* 973 */\n  {     8,      7,     12,      0,      0,  18836,     84, }, /* 974 */\n  {    49,     13,     12,      0,      0,  18432,    144, }, /* 975 */\n  {    49,      7,     12,      0,      0,  18432,     84, }, /* 976 */\n  {    49,     12,      3,      0,      0,  26624,    106, }, /* 977 */\n  {    49,     12,      3,      0,      0,  26624,     98, }, /* 978 */\n  {    99,     21,     12,      0,      0,  18840,    200, }, /* 979 */\n  {    49,     21,     12,      0,      0,  18432,    128, }, /* 980 */\n  {   121,      7,     12,      0,      0,  18432,     84, }, /* 981 */\n  {   121,     12,      3,      0,      0,  26624,    106, }, /* 982 */\n  {   121,     10,      5,      0,      0,  18432,    154, }, /* 983 */\n  {   121,     10,      3,      0,      0,  18432,    188, }, /* 984 */\n  {   121,     21,     12,      0,      0,  18432,     74, }, /* 985 */\n  {    56,     12,      3,      0,      0,  26624,    106, }, /* 986 */\n  {    56,     10,      5,      0,      0,  18432,    154, }, /* 987 */\n  {    56,      7,     12,      0,      0,  18432,     84, }, /* 988 */\n  {    56,     12,      3,      0,      0,  26624,     98, }, /* 989 */\n  {    56,     10,      3,      0,      0,  18432,    188, }, /* 990 */\n  {    56,     21,     12,      0,      0,  18432,     74, }, /* 991 */\n  {    56,     21,     12,      0,      0,  18432,    110, }, /* 992 */\n  {    56,     21,     12,      0,      0,  18432,    128, }, /* 993 */\n  {    99,      6,     12,      0,      0,  18844,    138, }, /* 994 */\n  {    56,     13,     12,      0,      0,  18432,    144, }, /* 995 */\n  {    20,      6,     12,      0,      0,  18432,    138, }, /* 996 */\n  {   122,      7,     12,      0,      0,  18432,     84, }, /* 997 */\n  {   122,     12,      3,      0,      0,  26624,    106, }, /* 998 */\n  {   122,     10,      5,      0,      0,  18432,    154, }, /* 999 */\n  {   122,     13,     12,      0,      0,  18432,    144, }, /* 1000 */\n  {   122,     21,     12,      0,      0,  18432,     74, }, /* 1001 */\n  {   122,     21,     12,      0,      0,  18432,    128, }, /* 1002 */\n  {   124,      7,     12,      0,      0,  18432,     84, }, /* 1003 */\n  {   124,     12,      3,      0,      0,  26624,    106, }, /* 1004 */\n  {   124,      7,     12,      0,      0,  18432,    170, }, /* 1005 */\n  {   124,     12,      3,      0,      0,  26624,     98, }, /* 1006 */\n  {   124,      7,     12,      0,      0,  18432,    314, }, /* 1007 */\n  {   124,      6,     12,      0,      0,  18432,    138, }, /* 1008 */\n  {   124,     21,     12,      0,      0,  18432,     74, }, /* 1009 */\n  {   124,     21,     12,      0,      0,  18432,    110, }, /* 1010 */\n  {   127,      7,     12,      0,      0,  18432,     84, }, /* 1011 */\n  {   127,     10,      5,      0,      0,  18432,    154, }, /* 1012 */\n  {   127,     12,      3,      0,      0,  26624,    106, }, /* 1013 */\n  {   127,     21,     12,      0,      0,  18432,    128, }, /* 1014 */\n  {   127,      6,     12,      0,      0,  18432,    138, }, /* 1015 */\n  {   127,     12,      3,      0,      0,  26624,    158, }, /* 1016 */\n  {     0,      5,     12,      0,   -928,  18432,     78, }, /* 1017 */\n  {    24,      5,     12,      0, -38864,  18432,     70, }, /* 1018 */\n  {   127,     10,      5,      0,      0,  18432,    174, }, /* 1019 */\n  {   127,     13,     12,      0,      0,  18432,    144, }, /* 1020 */\n  {    22,      7,      9,      0,      0,  18432,     84, }, /* 1021 */\n  {    22,      7,     10,      0,      0,  18432,     84, }, /* 1022 */\n  {    98,      4,     12,      0,      0,  18432,      0, }, /* 1023 */\n  {    98,      3,     12,      0,      0,  18432,      0, }, /* 1024 */\n  {    30,      7,     12,      0,      0,  18432,    304, }, /* 1025 */\n  {     0,      5,     12,      0,      1,  18432,     70, }, /* 1026 */\n  {     0,      5,     12,      0,     -1,  18432,     70, }, /* 1027 */\n  {     4,     25,     12,      0,      0,  12288,    122, }, /* 1028 */\n  {     5,      7,     12,      0,      0,      0,    316, }, /* 1029 */\n  {    99,     18,     12,      0,      0,  29088,     54, }, /* 1030 */\n  {    99,     22,     12,      0,      0,  29088,     54, }, /* 1031 */\n  {    98,      2,     12,      0,      0,   6144,    318, }, /* 1032 */\n  {     5,      7,     12,      0,      0,    420,     84, }, /* 1033 */\n  {     5,     26,     12,      0,      0,  29092,     74, }, /* 1034 */\n  {   106,     12,      3,      0,      0,  26624,    192, }, /* 1035 */\n  {   106,     12,      3,      0,      0,  26624,    320, }, /* 1036 */\n  {    99,     21,     12,      0,      0,  28672,     74, }, /* 1037 */\n  {    99,     21,     12,      0,      0,  28672,    128, }, /* 1038 */\n  {    99,     21,     12,      0,      0,  28672,    126, }, /* 1039 */\n  {    99,     22,     12,      0,      0,  28672,     74, }, /* 1040 */\n  {    99,     18,     12,      0,      0,  28672,     74, }, /* 1041 */\n  {    99,     17,     12,      0,      0,  28672,    130, }, /* 1042 */\n  {    99,     22,     12,      0,      0,  28672,    322, }, /* 1043 */\n  {    99,     18,     12,      0,      0,  28672,    322, }, /* 1044 */\n  {    99,     21,     12,      0,      0,   8192,    110, }, /* 1045 */\n  {    99,     21,     12,      0,      0,   8192,    324, }, /* 1046 */\n  {    99,     21,     12,      0,      0,   8192,    326, }, /* 1047 */\n  {    99,     22,     12,      0,      0,  28672,    172, }, /* 1048 */\n  {    99,     18,     12,      0,      0,  28672,    172, }, /* 1049 */\n  {    99,     21,     12,      0,      0,  14336,     74, }, /* 1050 */\n  {    99,     21,     12,      0,      0,  28672,    122, }, /* 1051 */\n  {    99,     25,     12,      0,      0,  12288,    122, }, /* 1052 */\n  {    99,     17,     12,      0,      0,  12288,    328, }, /* 1053 */\n  {    99,     25,     12,      0,      0,  28672,    330, }, /* 1054 */\n  {    99,     21,     12,      0,      0,  28672,    322, }, /* 1055 */\n  {    99,     21,     12,      0,      0,  28672,    332, }, /* 1056 */\n  {    99,     17,     12,      0,      0,  12288,    130, }, /* 1057 */\n  {    99,     21,     12,      0,      0,   8192,     74, }, /* 1058 */\n  {    99,     13,     12,      0,      0,  10240,    334, }, /* 1059 */\n  {     0,      9,     12,      0,     32,  18432,    336, }, /* 1060 */\n  {    99,     24,     12,      0,      0,  28672,    338, }, /* 1061 */\n  {     0,      5,     12,      0,    -32,  18432,    340, }, /* 1062 */\n  {    99,     21,     12,      0,      0,  29036,    128, }, /* 1063 */\n  {    99,     22,     12,      0,      0,  29036,    342, }, /* 1064 */\n  {    99,     18,     12,      0,      0,  29036,    342, }, /* 1065 */\n  {    99,     21,     12,      0,      0,  29036,    110, }, /* 1066 */\n  {    99,      6,      3,      0,      0,  18804,    344, }, /* 1067 */\n  {    99,      1,      2,      0,      0,  28672,    346, }, /* 1068 */\n  {    39,      7,     12,      0,      0,  18432,     84, }, /* 1069 */\n  {    99,     21,     12,      0,      0,  18856,     74, }, /* 1070 */\n  {    99,     21,     12,      0,      0,  29096,     74, }, /* 1071 */\n  {    99,     21,     12,      0,      0,  18860,     74, }, /* 1072 */\n  {    99,     15,     12,      0,      0,  18864,     74, }, /* 1073 */\n  {    99,     26,     12,      0,      0,  18860,     74, }, /* 1074 */\n  {     1,     14,     12,      0,      0,  28672,     84, }, /* 1075 */\n  {     1,     15,     12,      0,      0,  28672,     74, }, /* 1076 */\n  {     1,     26,     12,      0,      0,  28672,     74, }, /* 1077 */\n  {     1,     26,     12,      0,      0,  18432,     74, }, /* 1078 */\n  {    50,      7,     12,      0,      0,  18432,     84, }, /* 1079 */\n  {    51,      7,     12,      0,      0,  18432,     84, }, /* 1080 */\n  {   106,     12,      3,      0,      0,  27060,     98, }, /* 1081 */\n  {    99,     15,     12,      0,      0,  10676,     74, }, /* 1082 */\n  {   104,      7,     12,      0,      0,  18432,     84, }, /* 1083 */\n  {   104,     15,     12,      0,      0,  18432,     74, }, /* 1084 */\n  {    32,      7,     12,      0,      0,  18432,     84, }, /* 1085 */\n  {    32,     14,     12,      0,      0,  18432,     84, }, /* 1086 */\n  {    73,      7,     12,      0,      0,  18432,     84, }, /* 1087 */\n  {    73,     12,      3,      0,      0,  26624,    106, }, /* 1088 */\n  {   107,      7,     12,      0,      0,  18432,     84, }, /* 1089 */\n  {   107,     21,     12,      0,      0,  18432,    110, }, /* 1090 */\n  {   111,      7,     12,      0,      0,  18432,     84, }, /* 1091 */\n  {   111,     21,     12,      0,      0,  18432,    110, }, /* 1092 */\n  {   111,     14,     12,      0,      0,  18432,     84, }, /* 1093 */\n  {   105,      9,     12,      0,     40,  18432,     76, }, /* 1094 */\n  {   105,      5,     12,      0,    -40,  18432,     78, }, /* 1095 */\n  {    40,      7,     12,      0,      0,  18432,     84, }, /* 1096 */\n  {   108,      7,     12,      0,      0,  18432,     84, }, /* 1097 */\n  {   108,     13,     12,      0,      0,  18432,    144, }, /* 1098 */\n  {    80,      9,     12,      0,     40,  18432,     76, }, /* 1099 */\n  {    80,      5,     12,      0,    -40,  18432,     78, }, /* 1100 */\n  {    66,      7,     12,      0,      0,  18432,     84, }, /* 1101 */\n  {    64,      7,     12,      0,      0,  18432,     84, }, /* 1102 */\n  {    64,     21,     12,      0,      0,  18432,     74, }, /* 1103 */\n  {   167,      9,     12,      0,     39,  18432,     76, }, /* 1104 */\n  {   167,      5,     12,      0,    -39,  18432,     78, }, /* 1105 */\n  {    96,      7,     12,      0,      0,  18432,     84, }, /* 1106 */\n  {    69,      7,     12,      0,      0,  18432,     84, }, /* 1107 */\n  {     0,      6,     12,      0,      0,  18432,     96, }, /* 1108 */\n  {    41,      7,     12,      0,      0,  34816,     84, }, /* 1109 */\n  {   128,      7,     12,      0,      0,  34816,     84, }, /* 1110 */\n  {   128,     21,     12,      0,      0,  34816,    110, }, /* 1111 */\n  {   128,     15,     12,      0,      0,  34816,     74, }, /* 1112 */\n  {   143,      7,     12,      0,      0,  34816,     84, }, /* 1113 */\n  {   143,     26,     12,      0,      0,  34816,     74, }, /* 1114 */\n  {   143,     15,     12,      0,      0,  34816,     74, }, /* 1115 */\n  {   142,      7,     12,      0,      0,  34816,     84, }, /* 1116 */\n  {   142,     15,     12,      0,      0,  34816,     74, }, /* 1117 */\n  {   149,      7,     12,      0,      0,  34816,     84, }, /* 1118 */\n  {   149,     15,     12,      0,      0,  34816,     74, }, /* 1119 */\n  {   115,      7,     12,      0,      0,  34816,     84, }, /* 1120 */\n  {   115,     15,     12,      0,      0,  34816,     74, }, /* 1121 */\n  {   115,     21,     12,      0,      0,  28672,    110, }, /* 1122 */\n  {    52,      7,     12,      0,      0,  34816,     84, }, /* 1123 */\n  {    52,     21,     12,      0,      0,  34816,     74, }, /* 1124 */\n  {    61,      7,     12,      0,      0,  34816,     84, }, /* 1125 */\n  {   134,      7,     12,      0,      0,  34816,     84, }, /* 1126 */\n  {   134,     15,     12,      0,      0,  34816,     74, }, /* 1127 */\n  {   112,      7,     12,      0,      0,  34816,     84, }, /* 1128 */\n  {   112,     12,      3,      0,      0,  26624,    106, }, /* 1129 */\n  {   112,     12,      3,      0,      0,  26624,     98, }, /* 1130 */\n  {   112,     12,      3,      0,      0,  26624,    158, }, /* 1131 */\n  {   112,     15,     12,      0,      0,  34816,     74, }, /* 1132 */\n  {   112,     21,     12,      0,      0,  34816,     74, }, /* 1133 */\n  {   112,     21,     12,      0,      0,  34816,    128, }, /* 1134 */\n  {   129,      7,     12,      0,      0,  34816,     84, }, /* 1135 */\n  {   129,     15,     12,      0,      0,  34816,     74, }, /* 1136 */\n  {   129,     21,     12,      0,      0,  34816,     74, }, /* 1137 */\n  {   141,      7,     12,      0,      0,  34816,     84, }, /* 1138 */\n  {   141,     15,     12,      0,      0,  34816,     74, }, /* 1139 */\n  {    71,      7,     12,      0,      0,  34816,     84, }, /* 1140 */\n  {    71,     26,     12,      0,      0,  34816,     74, }, /* 1141 */\n  {    71,     12,      3,      0,      0,  26624,     98, }, /* 1142 */\n  {    71,     15,     12,      0,      0,  34816,     74, }, /* 1143 */\n  {    71,     21,     12,      0,      0,  34816,    110, }, /* 1144 */\n  {    71,     21,     12,      0,      0,  35256,    110, }, /* 1145 */\n  {    71,     21,     12,      0,      0,  34816,     74, }, /* 1146 */\n  {    53,      7,     12,      0,      0,  34816,     84, }, /* 1147 */\n  {    53,     21,     12,      0,      0,  28672,     74, }, /* 1148 */\n  {    53,     21,     12,      0,      0,  28672,    110, }, /* 1149 */\n  {   130,      7,     12,      0,      0,  34816,     84, }, /* 1150 */\n  {   130,     15,     12,      0,      0,  34816,     74, }, /* 1151 */\n  {   131,      7,     12,      0,      0,  34816,     84, }, /* 1152 */\n  {   131,     15,     12,      0,      0,  34816,     74, }, /* 1153 */\n  {    74,      7,     12,      0,      0,  34816,     84, }, /* 1154 */\n  {    74,     21,     12,      0,      0,  34816,    110, }, /* 1155 */\n  {    74,     15,     12,      0,      0,  34816,     74, }, /* 1156 */\n  {    57,      7,     12,      0,      0,  34816,     84, }, /* 1157 */\n  {    78,      9,     12,      0,     64,  34816,     76, }, /* 1158 */\n  {    78,      5,     12,      0,    -64,  34816,     78, }, /* 1159 */\n  {    78,     15,     12,      0,      0,  34816,     74, }, /* 1160 */\n  {    85,      7,     12,      0,      0,      0,     84, }, /* 1161 */\n  {    85,      7,     12,      0,      0,      0,    314, }, /* 1162 */\n  {    85,     12,      3,      0,      0,  26624,    132, }, /* 1163 */\n  {    85,     13,     12,      0,      0,   2048,    144, }, /* 1164 */\n  {    92,     13,     12,      0,      0,   2048,    144, }, /* 1165 */\n  {    92,      7,     12,      0,      0,  34816,     84, }, /* 1166 */\n  {    92,      6,     12,      0,      0,  34816,     96, }, /* 1167 */\n  {    92,      9,     12,      0,     32,  34816,     76, }, /* 1168 */\n  {    92,     12,      3,      0,      0,  26624,    132, }, /* 1169 */\n  {    92,     12,      3,      0,      0,  26624,    164, }, /* 1170 */\n  {    92,     12,      3,      0,      0,  26624,     98, }, /* 1171 */\n  {    92,     17,     12,      0,      0,  28672,    130, }, /* 1172 */\n  {    92,      6,     12,      0,      0,  34816,    138, }, /* 1173 */\n  {    92,      5,     12,      0,    -32,  34816,     78, }, /* 1174 */\n  {    92,     25,     12,      0,      0,  34816,    122, }, /* 1175 */\n  {     5,     15,     12,      0,      0,   2048,     74, }, /* 1176 */\n  {    88,      7,     12,      0,      0,  34816,     84, }, /* 1177 */\n  {    88,     12,      3,      0,      0,  26624,    106, }, /* 1178 */\n  {    88,     17,     12,      0,      0,  34816,    130, }, /* 1179 */\n  {   159,      7,     12,      0,      0,  34816,     84, }, /* 1180 */\n  {   159,     15,     12,      0,      0,  34816,     74, }, /* 1181 */\n  {    86,      7,     12,      0,      0,      0,     84, }, /* 1182 */\n  {    86,     12,      3,      0,      0,  26624,     98, }, /* 1183 */\n  {    86,     15,     12,      0,      0,      0,     74, }, /* 1184 */\n  {    86,     21,     12,      0,      0,      0,    128, }, /* 1185 */\n  {    90,      7,     12,      0,      0,  34816,     84, }, /* 1186 */\n  {    90,     12,      3,      0,      0,  26624,     98, }, /* 1187 */\n  {    90,     21,     12,      0,      0,  34816,    128, }, /* 1188 */\n  {   163,      7,     12,      0,      0,  34816,     84, }, /* 1189 */\n  {   163,     15,     12,      0,      0,  34816,     74, }, /* 1190 */\n  {   160,      7,     12,      0,      0,  34816,     84, }, /* 1191 */\n  {   133,     10,      5,      0,      0,  18432,    154, }, /* 1192 */\n  {   133,     12,      3,      0,      0,  26624,    106, }, /* 1193 */\n  {   133,      7,     12,      0,      0,  18432,     84, }, /* 1194 */\n  {   133,     12,      3,      0,      0,  26624,    158, }, /* 1195 */\n  {   133,     21,     12,      0,      0,  18432,    128, }, /* 1196 */\n  {   133,     21,     12,      0,      0,  18432,    110, }, /* 1197 */\n  {   133,     15,     12,      0,      0,  28672,     74, }, /* 1198 */\n  {   133,     13,     12,      0,      0,  18432,    144, }, /* 1199 */\n  {   133,     12,      3,      0,      0,  26624,    290, }, /* 1200 */\n  {    58,     12,      3,      0,      0,  26624,    106, }, /* 1201 */\n  {    58,     10,      5,      0,      0,  18432,    154, }, /* 1202 */\n  {    58,      7,     12,      0,      0,  18432,     84, }, /* 1203 */\n  {    58,     12,      3,      0,      0,  26624,    158, }, /* 1204 */\n  {    58,     12,      3,      0,      0,  26624,     98, }, /* 1205 */\n  {    58,     21,     12,      0,      0,  18432,     74, }, /* 1206 */\n  {    58,      1,      4,      0,      0,  18432,    134, }, /* 1207 */\n  {    58,     21,     12,      0,      0,  18432,    128, }, /* 1208 */\n  {   136,      7,     12,      0,      0,  18432,     84, }, /* 1209 */\n  {   136,     13,     12,      0,      0,  18432,    144, }, /* 1210 */\n  {    60,     12,      3,      0,      0,  26624,    106, }, /* 1211 */\n  {    60,      7,     12,      0,      0,  18432,     84, }, /* 1212 */\n  {    60,     10,      5,      0,      0,  18432,    154, }, /* 1213 */\n  {    60,     12,      3,      0,      0,  26624,    158, }, /* 1214 */\n  {    60,     13,     12,      0,      0,  18432,    144, }, /* 1215 */\n  {    60,     21,     12,      0,      0,  18432,     74, }, /* 1216 */\n  {    60,     21,     12,      0,      0,  18432,    128, }, /* 1217 */\n  {    70,      7,     12,      0,      0,  18432,     84, }, /* 1218 */\n  {    70,     12,      3,      0,      0,  26624,     98, }, /* 1219 */\n  {    70,     21,     12,      0,      0,  18432,     74, }, /* 1220 */\n  {    62,     12,      3,      0,      0,  26624,    106, }, /* 1221 */\n  {    62,     10,      5,      0,      0,  18432,    154, }, /* 1222 */\n  {    62,      7,     12,      0,      0,  18432,     84, }, /* 1223 */\n  {    62,     10,      3,      0,      0,  18432,    188, }, /* 1224 */\n  {    62,      7,      4,      0,      0,  18432,     84, }, /* 1225 */\n  {    62,     21,     12,      0,      0,  18432,    128, }, /* 1226 */\n  {    62,     21,     12,      0,      0,  18432,     74, }, /* 1227 */\n  {    62,     12,      3,      0,      0,  26624,    104, }, /* 1228 */\n  {    62,     12,      3,      0,      0,  26624,     98, }, /* 1229 */\n  {    62,     13,     12,      0,      0,  18432,    144, }, /* 1230 */\n  {    17,     15,     12,      0,      0,  18432,     74, }, /* 1231 */\n  {    68,      7,     12,      0,      0,  18432,     84, }, /* 1232 */\n  {    68,     10,      5,      0,      0,  18432,    154, }, /* 1233 */\n  {    68,     12,      3,      0,      0,  26624,    106, }, /* 1234 */\n  {    68,     10,      3,      0,      0,  18432,    188, }, /* 1235 */\n  {    68,     12,      3,      0,      0,  26624,     98, }, /* 1236 */\n  {    68,     12,      3,      0,      0,  26624,    162, }, /* 1237 */\n  {    68,     21,     12,      0,      0,  18432,    128, }, /* 1238 */\n  {    68,     21,     12,      0,      0,  18432,    110, }, /* 1239 */\n  {    68,     21,     12,      0,      0,  18432,     74, }, /* 1240 */\n  {    77,      7,     12,      0,      0,  18432,     84, }, /* 1241 */\n  {    77,     21,     12,      0,      0,  18432,    128, }, /* 1242 */\n  {    75,      7,     12,      0,      0,  18432,     84, }, /* 1243 */\n  {    75,     12,      3,      0,      0,  26624,    106, }, /* 1244 */\n  {    75,     10,      5,      0,      0,  18432,    154, }, /* 1245 */\n  {    75,     12,      3,      0,      0,  26624,     98, }, /* 1246 */\n  {    75,     12,      3,      0,      0,  26624,    158, }, /* 1247 */\n  {    75,     13,     12,      0,      0,  18432,    144, }, /* 1248 */\n  {    67,     12,      3,      0,      0,  26624,    106, }, /* 1249 */\n  {    67,     12,      3,      0,      0,  26836,    106, }, /* 1250 */\n  {    67,     10,      5,      0,      0,  18432,    154, }, /* 1251 */\n  {    67,     10,      5,      0,      0,  18644,    154, }, /* 1252 */\n  {    67,      7,     12,      0,      0,  18432,     84, }, /* 1253 */\n  {   106,     12,      3,      0,      0,  26836,     98, }, /* 1254 */\n  {    67,     12,      3,      0,      0,  26836,     98, }, /* 1255 */\n  {    67,     10,      3,      0,      0,  18432,    160, }, /* 1256 */\n  {    67,     10,      3,      0,      0,  18432,    188, }, /* 1257 */\n  {    67,      7,     12,      0,      0,  18432,    348, }, /* 1258 */\n  {    67,     12,      3,      0,      0,  26624,     98, }, /* 1259 */\n  {    97,      7,     12,      0,      0,  18432,     84, }, /* 1260 */\n  {    97,     10,      3,      0,      0,  18432,    160, }, /* 1261 */\n  {    97,     10,      5,      0,      0,  18432,    154, }, /* 1262 */\n  {    97,     12,      3,      0,      0,  26624,    106, }, /* 1263 */\n  {    97,     12,      3,      0,      0,  26624,    158, }, /* 1264 */\n  {    97,     10,      3,      0,      0,  18432,    188, }, /* 1265 */\n  {    97,      7,      4,      0,      0,  18432,     84, }, /* 1266 */\n  {    97,     12,      3,      0,      0,  26624,    164, }, /* 1267 */\n  {    97,      7,     12,      0,      0,  18432,    350, }, /* 1268 */\n  {    97,     21,     12,      0,      0,  18432,    128, }, /* 1269 */\n  {    97,     21,     12,      0,      0,  18432,     74, }, /* 1270 */\n  {    97,     12,      3,      0,      0,  26624,     98, }, /* 1271 */\n  {   153,      7,     12,      0,      0,  18432,     84, }, /* 1272 */\n  {   153,     10,      5,      0,      0,  18432,    154, }, /* 1273 */\n  {   153,     12,      3,      0,      0,  26624,    106, }, /* 1274 */\n  {   153,     12,      3,      0,      0,  26624,    158, }, /* 1275 */\n  {   153,     12,      3,      0,      0,  26624,     98, }, /* 1276 */\n  {   153,     21,     12,      0,      0,  18432,    128, }, /* 1277 */\n  {   153,     21,     12,      0,      0,  18432,    110, }, /* 1278 */\n  {   153,     21,     12,      0,      0,  18432,     74, }, /* 1279 */\n  {   153,     13,     12,      0,      0,  18432,    144, }, /* 1280 */\n  {   153,     12,      3,      0,      0,  26624,    104, }, /* 1281 */\n  {    76,      7,     12,      0,      0,  18432,     84, }, /* 1282 */\n  {    76,     10,      3,      0,      0,  18432,    160, }, /* 1283 */\n  {    76,     10,      5,      0,      0,  18432,    154, }, /* 1284 */\n  {    76,     12,      3,      0,      0,  26624,    106, }, /* 1285 */\n  {    76,     12,      3,      0,      0,  26624,    158, }, /* 1286 */\n  {    76,     12,      3,      0,      0,  26624,     98, }, /* 1287 */\n  {    76,     21,     12,      0,      0,  18432,     74, }, /* 1288 */\n  {    76,     13,     12,      0,      0,  18432,    144, }, /* 1289 */\n  {   145,      7,     12,      0,      0,  18432,     84, }, /* 1290 */\n  {   145,     10,      3,      0,      0,  18432,    160, }, /* 1291 */\n  {   145,     10,      5,      0,      0,  18432,    154, }, /* 1292 */\n  {   145,     12,      3,      0,      0,  26624,    106, }, /* 1293 */\n  {   145,     12,      3,      0,      0,  26624,    158, }, /* 1294 */\n  {   145,     12,      3,      0,      0,  26624,     98, }, /* 1295 */\n  {   145,     21,     12,      0,      0,  18432,     74, }, /* 1296 */\n  {   145,     21,     12,      0,      0,  18432,    128, }, /* 1297 */\n  {   145,     21,     12,      0,      0,  18432,    110, }, /* 1298 */\n  {   145,     21,     12,      0,      0,  18432,    190, }, /* 1299 */\n  {    72,      7,     12,      0,      0,  18432,     84, }, /* 1300 */\n  {    72,     10,      5,      0,      0,  18432,    154, }, /* 1301 */\n  {    72,     12,      3,      0,      0,  26624,    106, }, /* 1302 */\n  {    72,     12,      3,      0,      0,  26624,    158, }, /* 1303 */\n  {    72,     21,     12,      0,      0,  18432,    128, }, /* 1304 */\n  {    72,     21,     12,      0,      0,  18432,     74, }, /* 1305 */\n  {    72,     13,     12,      0,      0,  18432,    144, }, /* 1306 */\n  {    63,      7,     12,      0,      0,  18432,     84, }, /* 1307 */\n  {    63,     12,      3,      0,      0,  26624,    106, }, /* 1308 */\n  {    63,     10,      5,      0,      0,  18432,    154, }, /* 1309 */\n  {    63,     10,      3,      0,      0,  18432,    188, }, /* 1310 */\n  {    63,     12,      3,      0,      0,  26624,     98, }, /* 1311 */\n  {    63,     21,     12,      0,      0,  18432,     74, }, /* 1312 */\n  {    63,     13,     12,      0,      0,  18432,    144, }, /* 1313 */\n  {   147,      7,     12,      0,      0,  18432,     84, }, /* 1314 */\n  {   147,     12,      3,      0,      0,  26624,    106, }, /* 1315 */\n  {   147,     10,      5,      0,      0,  18432,    154, }, /* 1316 */\n  {   147,     10,     12,      0,      0,  18432,    154, }, /* 1317 */\n  {   147,     12,      3,      0,      0,  26624,    158, }, /* 1318 */\n  {   147,     13,     12,      0,      0,  18432,    144, }, /* 1319 */\n  {   147,     15,     12,      0,      0,  18432,     74, }, /* 1320 */\n  {   147,     21,     12,      0,      0,  18432,    128, }, /* 1321 */\n  {   147,     26,     12,      0,      0,  18432,     74, }, /* 1322 */\n  {    83,      7,     12,      0,      0,  18432,     84, }, /* 1323 */\n  {    83,     10,      5,      0,      0,  18432,    154, }, /* 1324 */\n  {    83,     12,      3,      0,      0,  26624,    106, }, /* 1325 */\n  {    83,     12,      3,      0,      0,  26624,    158, }, /* 1326 */\n  {    83,     12,      3,      0,      0,  26624,     98, }, /* 1327 */\n  {    83,     21,     12,      0,      0,  18432,     74, }, /* 1328 */\n  {   146,      9,     12,      0,     32,  18432,     76, }, /* 1329 */\n  {   146,      5,     12,      0,    -32,  18432,     78, }, /* 1330 */\n  {   146,     13,     12,      0,      0,  18432,    144, }, /* 1331 */\n  {   146,     15,     12,      0,      0,  18432,     74, }, /* 1332 */\n  {   146,      7,     12,      0,      0,  18432,     84, }, /* 1333 */\n  {   164,      7,     12,      0,      0,  18432,     84, }, /* 1334 */\n  {   164,     10,      3,      0,      0,  18432,    160, }, /* 1335 */\n  {   164,     10,      5,      0,      0,  18432,    154, }, /* 1336 */\n  {   164,     12,      3,      0,      0,  26624,    106, }, /* 1337 */\n  {   164,     10,      3,      0,      0,  18432,    188, }, /* 1338 */\n  {   164,     12,      3,      0,      0,  26624,    158, }, /* 1339 */\n  {   164,      7,      4,      0,      0,  18432,     84, }, /* 1340 */\n  {   164,     12,      3,      0,      0,  26624,     98, }, /* 1341 */\n  {   164,     21,     12,      0,      0,  18432,    128, }, /* 1342 */\n  {   164,     21,     12,      0,      0,  18432,     74, }, /* 1343 */\n  {   164,     13,     12,      0,      0,  18432,    144, }, /* 1344 */\n  {    87,      7,     12,      0,      0,  18432,     84, }, /* 1345 */\n  {    87,     10,      5,      0,      0,  18432,    154, }, /* 1346 */\n  {    87,     12,      3,      0,      0,  26624,    106, }, /* 1347 */\n  {    87,     12,      3,      0,      0,  26624,    158, }, /* 1348 */\n  {    87,     21,     12,      0,      0,  18432,     74, }, /* 1349 */\n  {   156,      7,     12,      0,      0,  18432,     84, }, /* 1350 */\n  {   156,     12,      3,      0,      0,  26624,    106, }, /* 1351 */\n  {   156,     12,      3,      0,      0,  18432,    106, }, /* 1352 */\n  {   156,     12,      3,      0,      0,  26624,    104, }, /* 1353 */\n  {   156,     12,      3,      0,      0,  26624,    158, }, /* 1354 */\n  {   156,     10,      5,      0,      0,  18432,    154, }, /* 1355 */\n  {   156,      7,      4,      0,      0,  18432,     84, }, /* 1356 */\n  {   156,     21,     12,      0,      0,  18432,     74, }, /* 1357 */\n  {   156,     21,     12,      0,      0,  18432,    128, }, /* 1358 */\n  {   155,      7,     12,      0,      0,  18432,     84, }, /* 1359 */\n  {   155,     12,      3,      0,      0,  26624,    106, }, /* 1360 */\n  {   155,     10,      5,      0,      0,  18432,    154, }, /* 1361 */\n  {   155,      7,      4,      0,      0,  18432,     84, }, /* 1362 */\n  {   155,     12,      3,      0,      0,  26624,    352, }, /* 1363 */\n  {   155,     12,      3,      0,      0,  26624,    158, }, /* 1364 */\n  {   155,     21,     12,      0,      0,  18432,     74, }, /* 1365 */\n  {   155,     21,     12,      0,      0,  18432,    128, }, /* 1366 */\n  {   155,     21,     12,      0,      0,  18432,    110, }, /* 1367 */\n  {   144,      7,     12,      0,      0,  18432,     84, }, /* 1368 */\n  {    95,      7,     12,      0,      0,  18432,     84, }, /* 1369 */\n  {    95,     21,     12,      0,      0,  18432,     74, }, /* 1370 */\n  {    95,     13,     12,      0,      0,  18432,    144, }, /* 1371 */\n  {   151,      7,     12,      0,      0,  18432,     84, }, /* 1372 */\n  {   151,     10,      5,      0,      0,  18432,    154, }, /* 1373 */\n  {   151,     12,      3,      0,      0,  26624,    106, }, /* 1374 */\n  {   151,     12,      3,      0,      0,  18432,    158, }, /* 1375 */\n  {   151,     21,     12,      0,      0,  18432,    128, }, /* 1376 */\n  {   151,     21,     12,      0,      0,  18432,    110, }, /* 1377 */\n  {   151,     21,     12,      0,      0,  18432,     74, }, /* 1378 */\n  {   151,     13,     12,      0,      0,  18432,    144, }, /* 1379 */\n  {   151,     15,     12,      0,      0,  18432,     74, }, /* 1380 */\n  {   152,     21,     12,      0,      0,  18432,     74, }, /* 1381 */\n  {   152,     21,     12,      0,      0,  18432,    110, }, /* 1382 */\n  {   152,      7,     12,      0,      0,  18432,     84, }, /* 1383 */\n  {   152,     12,      3,      0,      0,  26624,    106, }, /* 1384 */\n  {   152,     10,      5,      0,      0,  18432,    154, }, /* 1385 */\n  {    82,      7,     12,      0,      0,  18432,     84, }, /* 1386 */\n  {    82,     12,      3,      0,      0,  26624,    106, }, /* 1387 */\n  {    82,     12,      3,      0,      0,  26624,     98, }, /* 1388 */\n  {    82,     12,      3,      0,      0,  26624,    158, }, /* 1389 */\n  {    82,      7,      4,      0,      0,  18432,     84, }, /* 1390 */\n  {    82,     13,     12,      0,      0,  18432,    144, }, /* 1391 */\n  {    84,      7,     12,      0,      0,  18432,     84, }, /* 1392 */\n  {    84,     10,      5,      0,      0,  18432,    154, }, /* 1393 */\n  {    84,     12,      3,      0,      0,  26624,    106, }, /* 1394 */\n  {    84,     12,      3,      0,      0,  26624,    158, }, /* 1395 */\n  {    84,     13,     12,      0,      0,  18432,    144, }, /* 1396 */\n  {   157,      7,     12,      0,      0,  18432,     84, }, /* 1397 */\n  {   157,     12,      3,      0,      0,  26624,    106, }, /* 1398 */\n  {   157,     10,      5,      0,      0,  18432,    154, }, /* 1399 */\n  {   157,     21,     12,      0,      0,  18432,    128, }, /* 1400 */\n  {   168,     12,      3,      0,      0,  26624,    106, }, /* 1401 */\n  {   168,      7,      4,      0,      0,  18432,     84, }, /* 1402 */\n  {   168,     10,      5,      0,      0,  18432,    154, }, /* 1403 */\n  {   168,      7,     12,      0,      0,  18432,     84, }, /* 1404 */\n  {   168,     10,      3,      0,      0,  18432,    188, }, /* 1405 */\n  {   168,     12,      3,      0,      0,  26624,    158, }, /* 1406 */\n  {   168,     21,     12,      0,      0,  18432,    128, }, /* 1407 */\n  {   168,     21,     12,      0,      0,  18432,     74, }, /* 1408 */\n  {   168,     13,     12,      0,      0,  18432,    144, }, /* 1409 */\n  {   168,     12,      3,      0,      0,  26624,     98, }, /* 1410 */\n  {    13,     15,     12,      0,      0,  18432,     74, }, /* 1411 */\n  {    13,     21,     12,      0,      0,  18432,     74, }, /* 1412 */\n  {   114,      7,     12,      0,      0,  18432,     84, }, /* 1413 */\n  {   114,     14,     12,      0,      0,  18432,     84, }, /* 1414 */\n  {   114,     21,     12,      0,      0,  18432,    110, }, /* 1415 */\n  {    89,      7,     12,      0,      0,  18432,     84, }, /* 1416 */\n  {    89,     21,     12,      0,      0,  18432,     74, }, /* 1417 */\n  {   125,      7,     12,      0,      0,  18432,     84, }, /* 1418 */\n  {   125,      1,      2,      0,      0,  18432,    346, }, /* 1419 */\n  {   125,     12,      3,      0,      0,  26624,    104, }, /* 1420 */\n  {   125,     12,      3,      0,      0,  26624,     98, }, /* 1421 */\n  {   148,      7,     12,      0,      0,  18432,     84, }, /* 1422 */\n  {    93,      7,     12,      0,      0,  18432,     84, }, /* 1423 */\n  {    93,     12,      3,      0,      0,  26624,    106, }, /* 1424 */\n  {    93,     10,      5,      0,      0,  18432,    154, }, /* 1425 */\n  {    93,     12,      3,      0,      0,  26624,    158, }, /* 1426 */\n  {    93,     13,     12,      0,      0,  18432,    144, }, /* 1427 */\n  {   140,      7,     12,      0,      0,  18432,     84, }, /* 1428 */\n  {   140,     13,     12,      0,      0,  18432,    144, }, /* 1429 */\n  {   140,     21,     12,      0,      0,  18432,    128, }, /* 1430 */\n  {   166,      7,     12,      0,      0,  18432,     84, }, /* 1431 */\n  {   166,     13,     12,      0,      0,  18432,    144, }, /* 1432 */\n  {   137,      7,     12,      0,      0,  18432,     84, }, /* 1433 */\n  {   137,     12,      3,      0,      0,  26624,     98, }, /* 1434 */\n  {   137,     21,     12,      0,      0,  18432,    128, }, /* 1435 */\n  {   138,      7,     12,      0,      0,  18432,     84, }, /* 1436 */\n  {   138,     12,      3,      0,      0,  26624,     98, }, /* 1437 */\n  {   138,     21,     12,      0,      0,  18432,    128, }, /* 1438 */\n  {   138,     21,     12,      0,      0,  18432,    110, }, /* 1439 */\n  {   138,     21,     12,      0,      0,  18432,     74, }, /* 1440 */\n  {   138,     26,     12,      0,      0,  18432,     74, }, /* 1441 */\n  {   138,      6,     12,      0,      0,  18432,    148, }, /* 1442 */\n  {   138,      6,     12,      0,      0,  18432,    138, }, /* 1443 */\n  {   138,     13,     12,      0,      0,  18432,    144, }, /* 1444 */\n  {   138,     15,     12,      0,      0,  18432,     74, }, /* 1445 */\n  {   170,      6,     12,      0,      0,  18432,    148, }, /* 1446 */\n  {   170,      7,     12,      0,      0,  18432,     84, }, /* 1447 */\n  {   170,      7,      7,      0,      0,  18432,     84, }, /* 1448 */\n  {   170,      6,     12,      0,      0,  18432,     94, }, /* 1449 */\n  {   170,     21,     12,      0,      0,  18432,     74, }, /* 1450 */\n  {   170,     21,     12,      0,      0,  18432,    128, }, /* 1451 */\n  {   170,     13,     12,      0,      0,  18432,    144, }, /* 1452 */\n  {   158,      9,     12,      0,     32,  18432,     76, }, /* 1453 */\n  {   158,      5,     12,      0,    -32,  18432,     78, }, /* 1454 */\n  {   158,     15,     12,      0,      0,  18432,     74, }, /* 1455 */\n  {   158,     21,     12,      0,      0,  18432,    110, }, /* 1456 */\n  {   158,     21,     12,      0,      0,  18432,    128, }, /* 1457 */\n  {   158,     21,     12,      0,      0,  18432,     74, }, /* 1458 */\n  {   135,      7,     12,      0,      0,  18432,     84, }, /* 1459 */\n  {   135,     12,      3,      0,      0,  26624,    106, }, /* 1460 */\n  {   135,     10,      5,      0,      0,  18432,    154, }, /* 1461 */\n  {   135,     12,      3,      0,      0,  26624,    132, }, /* 1462 */\n  {   135,      6,     12,      0,      0,  18432,     94, }, /* 1463 */\n  {    81,      6,     12,      0,      0,  18432,    138, }, /* 1464 */\n  {   154,      6,     12,      0,      0,  18432,    138, }, /* 1465 */\n  {    30,     21,     12,      0,      0,  28672,     74, }, /* 1466 */\n  {   165,     12,      3,      0,      0,  26624,    354, }, /* 1467 */\n  {    30,     10,      3,      0,      0,  18432,    356, }, /* 1468 */\n  {    81,      7,     12,      0,      0,  18432,    304, }, /* 1469 */\n  {   165,      7,     12,      0,      0,  18432,    304, }, /* 1470 */\n  {    28,      6,     12,      0,      0,  18432,     94, }, /* 1471 */\n  {   154,      7,     12,      0,      0,  18432,    304, }, /* 1472 */\n  {    65,      7,     12,      0,      0,  18432,     84, }, /* 1473 */\n  {    65,     26,     12,      0,      0,  18432,     74, }, /* 1474 */\n  {    65,     12,      3,      0,      0,  26624,    104, }, /* 1475 */\n  {    65,     12,      3,      0,      0,  26624,    106, }, /* 1476 */\n  {    65,     21,     12,      0,      0,  18432,    128, }, /* 1477 */\n  {    99,      1,      2,      0,      0,   6472,     66, }, /* 1478 */\n  {    99,     13,     12,      0,      0,  10240,    144, }, /* 1479 */\n  {    99,     10,      3,      0,      0,  18432,    358, }, /* 1480 */\n  {    99,     10,      3,      0,      0,  18432,    306, }, /* 1481 */\n  {     1,     12,      3,      0,      0,  26624,    104, }, /* 1482 */\n  {    99,     25,     12,      0,      0,  28672,    360, }, /* 1483 */\n  {    99,     13,     12,      0,      0,  10240,    226, }, /* 1484 */\n  {   150,     26,     12,      0,      0,  18432,     74, }, /* 1485 */\n  {   150,     12,      3,      0,      0,  26624,    104, }, /* 1486 */\n  {   150,     21,     12,      0,      0,  18432,    110, }, /* 1487 */\n  {   150,     21,     12,      0,      0,  18432,    128, }, /* 1488 */\n  {   150,     21,     12,      0,      0,  18432,     74, }, /* 1489 */\n  {    44,     12,      3,      0,      0,  26624,    106, }, /* 1490 */\n  {     2,      6,     12,      0,      0,  18432,     92, }, /* 1491 */\n  {   161,      7,     12,      0,      0,  18432,     84, }, /* 1492 */\n  {   161,     12,      3,      0,      0,  26624,     98, }, /* 1493 */\n  {   161,      6,     12,      0,      0,  18432,    148, }, /* 1494 */\n  {   161,      6,     12,      0,      0,  18432,    138, }, /* 1495 */\n  {   161,     13,     12,      0,      0,  18432,    144, }, /* 1496 */\n  {   161,     26,     12,      0,      0,  18432,     74, }, /* 1497 */\n  {    91,      7,     12,      0,      0,  18432,     84, }, /* 1498 */\n  {    91,     12,      3,      0,      0,  26624,     98, }, /* 1499 */\n  {   162,      7,     12,      0,      0,  18432,     84, }, /* 1500 */\n  {   162,     12,      3,      0,      0,  26624,     98, }, /* 1501 */\n  {   162,     13,     12,      0,      0,  18432,    144, }, /* 1502 */\n  {   162,     23,     12,      0,      0,  14336,     74, }, /* 1503 */\n  {   169,      7,     12,      0,      0,  18432,     84, }, /* 1504 */\n  {   169,      6,     12,      0,      0,  18432,    148, }, /* 1505 */\n  {   169,     12,      3,      0,      0,  26624,    104, }, /* 1506 */\n  {   169,     13,     12,      0,      0,  18432,    144, }, /* 1507 */\n  {    94,      7,     12,      0,      0,  18432,     84, }, /* 1508 */\n  {    94,     12,      3,      0,      0,  26624,     98, }, /* 1509 */\n  {    94,     12,      3,      0,      0,  26624,    164, }, /* 1510 */\n  {    94,     13,     12,      0,      0,  18432,    144, }, /* 1511 */\n  {    94,     21,     12,      0,      0,  18432,     74, }, /* 1512 */\n  {   139,      7,     12,      0,      0,  34816,     84, }, /* 1513 */\n  {   139,     15,     12,      0,      0,  34816,     74, }, /* 1514 */\n  {   139,     12,      3,      0,      0,  26624,     98, }, /* 1515 */\n  {    79,      9,     12,      0,     34,  34816,     76, }, /* 1516 */\n  {    79,      5,     12,      0,    -34,  34816,     78, }, /* 1517 */\n  {    79,     12,      3,      0,      0,  26624,    164, }, /* 1518 */\n  {    79,     12,      3,      0,      0,  26624,    106, }, /* 1519 */\n  {    79,     12,      3,      0,      0,  26624,     98, }, /* 1520 */\n  {    79,      6,     12,      0,      0,  34816,    148, }, /* 1521 */\n  {    79,     13,     12,      0,      0,  34816,    144, }, /* 1522 */\n  {    79,     21,     12,      0,      0,  34816,     74, }, /* 1523 */\n  {    99,     15,     12,      0,      0,      0,     74, }, /* 1524 */\n  {    99,     26,     12,      0,      0,      0,     74, }, /* 1525 */\n  {    99,     23,     12,      0,      0,      0,     74, }, /* 1526 */\n  {     5,      7,     12,      0,      0,      0,    254, }, /* 1527 */\n  {    99,     26,     14,      0,      0,  28672,    362, }, /* 1528 */\n  {    99,     26,     14,      0,      0,  28672,    364, }, /* 1529 */\n  {    98,      2,     14,      0,      0,  18432,    366, }, /* 1530 */\n  {    99,     26,     12,      0,      0,  18432,    368, }, /* 1531 */\n  {    99,     26,     14,      0,      0,  18432,    370, }, /* 1532 */\n  {    99,     26,     14,      0,      0,  18432,    364, }, /* 1533 */\n  {    99,     26,     11,      0,      0,  18432,    372, }, /* 1534 */\n  {    27,     26,     12,      0,      0,  18432,     74, }, /* 1535 */\n  {    99,     26,     14,      0,      0,  18432,    250, }, /* 1536 */\n  {    99,     26,     14,      0,      0,  18784,    364, }, /* 1537 */\n  {    99,     26,     14,      0,      0,  28672,    374, }, /* 1538 */\n  {    99,     26,     14,      0,      0,  28672,    376, }, /* 1539 */\n  {    99,     24,      3,      0,      0,  28672,    378, }, /* 1540 */\n  {    99,     26,     14,      0,      0,  28672,    380, }, /* 1541 */\n  {    99,      1,      3,      0,      0,   6144,    382, }, /* 1542 */\n};\n\nconst uint16_t PRIV(ucd_stage1)[] = { /* 17408 bytes */\n  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, /* U+0000 */\n 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, /* U+0800 */\n 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 41, 41, 42, 43, 44, 45, /* U+1000 */\n 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, /* U+1800 */\n 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, /* U+2000 */\n 78, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, /* U+2800 */\n 93, 94, 95, 96, 97, 98, 99,100,101,101,101,101,101,101,101,101, /* U+3000 */\n101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+3800 */\n101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+4000 */\n101,101,101,101,101,101,101,101,101,101,101,102,101,101,101,101, /* U+4800 */\n101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+5000 */\n101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+5800 */\n101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+6000 */\n101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+6800 */\n101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+7000 */\n101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+7800 */\n101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+8000 */\n101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+8800 */\n101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+9000 */\n101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+9800 */\n103,104,104,104,104,104,104,104,104,105,106,106,107,108,109,110, /* U+A000 */\n111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,119, /* U+A800 */\n120,121,122,123,124,125,119,120,121,122,123,124,125,119,120,121, /* U+B000 */\n122,123,124,125,119,120,121,122,123,124,125,119,120,121,122,123, /* U+B800 */\n124,125,119,120,121,122,123,124,125,119,120,121,122,123,124,125, /* U+C000 */\n119,120,121,122,123,124,125,119,120,121,122,123,124,125,119,120, /* U+C800 */\n121,122,123,124,125,119,120,121,122,123,124,125,119,120,121,126, /* U+D000 */\n127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127, /* U+D800 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+E000 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+E800 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F000 */\n128,128,129,129,130,131,132,133,134,135,136,137,138,139,140,141, /* U+F800 */\n142,143,144,145,146,147,148,149,150,151,152,153,154,154,155,156, /* U+10000 */\n157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172, /* U+10800 */\n173,174,175,176,177,178,179,180,181,182,146,183,184,185,186,146, /* U+11000 */\n187,188,189,190,191,192,193,194,195,196,197,198,146,199,200,201, /* U+11800 */\n202,202,202,202,202,202,202,203,204,202,205,146,146,146,146,146, /* U+12000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,206, /* U+12800 */\n207,207,207,207,207,207,207,207,208,207,207,207,207,207,207,207, /* U+13000 */\n207,207,207,207,207,207,207,207,207,207,207,207,207,207,207,207, /* U+13800 */\n207,207,207,207,207,207,207,209,210,210,210,210,211,146,146,146, /* U+14000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+14800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+15000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+15800 */\n146,146,212,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+16000 */\n213,213,213,213,214,215,216,217,146,146,218,146,219,220,221,222, /* U+16800 */\n223,223,223,223,223,223,223,223,223,223,223,223,223,223,223,223, /* U+17000 */\n223,223,223,223,223,223,223,223,223,223,223,223,223,223,223,223, /* U+17800 */\n223,223,223,223,223,223,223,223,223,223,223,223,223,223,223,224, /* U+18000 */\n223,223,223,223,223,223,225,225,225,226,227,146,146,146,146,146, /* U+18800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+19000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+19800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+1A000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,228, /* U+1A800 */\n229,230,231,232,232,233,146,146,146,146,146,146,146,146,146,146, /* U+1B000 */\n146,146,146,146,146,146,146,146,234,235,146,146,146,146,146,146, /* U+1B800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+1C000 */\n146,146,146,146,146,146,146,146,236,237,236,236,236,238,239,240, /* U+1C800 */\n241,242,243,244,245,246,247,146,248,249,250,251,252,253,254,255, /* U+1D000 */\n256,256,256,256,257,258,146,146,146,146,146,146,146,146,259,146, /* U+1D800 */\n260,261,262,146,146,263,146,146,146,264,146,265,146,146,146,266, /* U+1E000 */\n267,268,269,270,270,270,270,270,271,272,273,270,274,275,270,270, /* U+1E800 */\n276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291, /* U+1F000 */\n292,293,294,295,296,297,236,298,281,281,281,281,281,281,281,299, /* U+1F800 */\n101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+20000 */\n101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+20800 */\n101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+21000 */\n101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+21800 */\n101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+22000 */\n101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+22800 */\n101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+23000 */\n101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+23800 */\n101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+24000 */\n101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+24800 */\n101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+25000 */\n101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+25800 */\n101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+26000 */\n101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+26800 */\n101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+27000 */\n101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+27800 */\n101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+28000 */\n101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+28800 */\n101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+29000 */\n101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+29800 */\n101,101,101,101,101,101,101,101,101,101,101,101,101,300,101,101, /* U+2A000 */\n101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+2A800 */\n101,101,101,101,101,101,101,101,101,101,101,101,101,101,301,101, /* U+2B000 */\n302,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+2B800 */\n101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+2C000 */\n101,101,101,101,101,101,101,101,101,101,101,101,101,303,101,101, /* U+2C800 */\n101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+2D000 */\n101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+2D800 */\n101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+2E000 */\n101,101,101,101,101,101,101,304,101,101,101,101,305,146,146,146, /* U+2E800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+2F000 */\n129,129,129,129,306,146,146,146,146,146,146,146,146,146,146,307, /* U+2F800 */\n101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+30000 */\n101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+30800 */\n101,101,101,101,101,101,308,101,101,101,101,101,101,101,101,101, /* U+31000 */\n101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+31800 */\n101,101,101,101,101,101,101,309,146,146,146,146,146,146,146,146, /* U+32000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+32800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+33000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+33800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+34000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+34800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+35000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+35800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+36000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+36800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+37000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+37800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+38000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+38800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+39000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+39800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+3A000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+3A800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+3B000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+3B800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+3C000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+3C800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+3D000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+3D800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+3E000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+3E800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+3F000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,307, /* U+3F800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+40000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+40800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+41000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+41800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+42000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+42800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+43000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+43800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+44000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+44800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+45000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+45800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+46000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+46800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+47000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+47800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+48000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+48800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+49000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+49800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+4A000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+4A800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+4B000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+4B800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+4C000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+4C800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+4D000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+4D800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+4E000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+4E800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+4F000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,307, /* U+4F800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+50000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+50800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+51000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+51800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+52000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+52800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+53000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+53800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+54000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+54800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+55000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+55800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+56000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+56800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+57000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+57800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+58000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+58800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+59000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+59800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+5A000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+5A800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+5B000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+5B800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+5C000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+5C800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+5D000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+5D800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+5E000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+5E800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+5F000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,307, /* U+5F800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+60000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+60800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+61000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+61800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+62000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+62800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+63000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+63800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+64000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+64800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+65000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+65800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+66000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+66800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+67000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+67800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+68000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+68800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+69000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+69800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+6A000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+6A800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+6B000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+6B800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+6C000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+6C800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+6D000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+6D800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+6E000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+6E800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+6F000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,307, /* U+6F800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+70000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+70800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+71000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+71800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+72000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+72800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+73000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+73800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+74000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+74800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+75000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+75800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+76000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+76800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+77000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+77800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+78000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+78800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+79000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+79800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+7A000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+7A800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+7B000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+7B800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+7C000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+7C800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+7D000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+7D800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+7E000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+7E800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+7F000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,307, /* U+7F800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+80000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+80800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+81000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+81800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+82000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+82800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+83000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+83800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+84000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+84800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+85000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+85800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+86000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+86800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+87000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+87800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+88000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+88800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+89000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+89800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+8A000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+8A800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+8B000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+8B800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+8C000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+8C800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+8D000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+8D800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+8E000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+8E800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+8F000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,307, /* U+8F800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+90000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+90800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+91000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+91800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+92000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+92800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+93000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+93800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+94000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+94800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+95000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+95800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+96000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+96800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+97000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+97800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+98000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+98800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+99000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+99800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+9A000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+9A800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+9B000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+9B800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+9C000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+9C800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+9D000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+9D800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+9E000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+9E800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+9F000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,307, /* U+9F800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A0000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A0800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A1000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A1800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A2000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A2800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A3000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A3800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A4000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A4800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A5000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A5800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A6000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A6800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A7000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A7800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A8000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A8800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A9000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A9800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+AA000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+AA800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+AB000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+AB800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+AC000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+AC800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+AD000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+AD800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+AE000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+AE800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+AF000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,307, /* U+AF800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B0000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B0800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B1000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B1800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B2000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B2800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B3000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B3800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B4000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B4800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B5000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B5800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B6000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B6800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B7000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B7800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B8000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B8800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B9000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B9800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+BA000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+BA800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+BB000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+BB800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+BC000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+BC800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+BD000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+BD800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+BE000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+BE800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+BF000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,307, /* U+BF800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C0000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C0800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C1000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C1800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C2000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C2800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C3000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C3800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C4000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C4800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C5000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C5800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C6000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C6800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C7000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C7800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C8000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C8800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C9000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C9800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+CA000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+CA800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+CB000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+CB800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+CC000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+CC800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+CD000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+CD800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+CE000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+CE800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+CF000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,307, /* U+CF800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D0000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D0800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D1000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D1800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D2000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D2800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D3000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D3800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D4000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D4800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D5000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D5800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D6000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D6800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D7000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D7800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D8000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D8800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D9000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D9800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+DA000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+DA800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+DB000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+DB800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+DC000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+DC800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+DD000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+DD800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+DE000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+DE800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+DF000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,307, /* U+DF800 */\n310,311,312,313,311,311,311,311,311,311,311,311,311,311,311,311, /* U+E0000 */\n311,311,311,311,311,311,311,311,311,311,311,311,311,311,311,311, /* U+E0800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E1000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E1800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E2000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E2800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E3000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E3800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E4000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E4800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E5000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E5800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E6000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E6800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E7000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E7800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E8000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E8800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E9000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E9800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+EA000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+EA800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+EB000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+EB800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+EC000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+EC800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+ED000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+ED800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+EE000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+EE800 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+EF000 */\n146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,307, /* U+EF800 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F0000 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F0800 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F1000 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F1800 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F2000 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F2800 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F3000 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F3800 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F4000 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F4800 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F5000 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F5800 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F6000 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F6800 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F7000 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F7800 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F8000 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F8800 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F9000 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F9800 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+FA000 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+FA800 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+FB000 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+FB800 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+FC000 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+FC800 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+FD000 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+FD800 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+FE000 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+FE800 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+FF000 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,314, /* U+FF800 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+100000 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+100800 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+101000 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+101800 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+102000 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+102800 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+103000 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+103800 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+104000 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+104800 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+105000 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+105800 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+106000 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+106800 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+107000 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+107800 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+108000 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+108800 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+109000 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+109800 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+10A000 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+10A800 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+10B000 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+10B800 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+10C000 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+10C800 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+10D000 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+10D800 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+10E000 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+10E800 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+10F000 */\n128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,314, /* U+10F800 */\n};\n\nconst uint16_t PRIV(ucd_stage2)[] = { /* 80640 bytes, block = 128 */\n\n/* block 0 */\n  0,  0,  0,  0,  0,  0,  0,  0,  0,  1,  2,  1,  3,  4,  0,  0,\n  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  5,  5,  5,  6,\n  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,\n 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 24, 25, 26, 27, 26,  8,\n 13, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 30, 29, 29, 29, 29,\n 29, 29, 29, 31, 29, 29, 29, 29, 29, 29, 29, 15, 13, 16, 32, 33,\n 34, 35, 35, 35, 35, 35, 35, 36, 36, 37, 37, 38, 36, 36, 36, 36,\n 36, 36, 36, 39, 36, 36, 36, 36, 36, 36, 36, 15, 27, 16, 27,  0,\n\n/* block 1 */\n 40, 40, 40, 40, 40, 41, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40,\n 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40,\n 42, 43, 44, 44, 44, 44, 45, 43, 46, 47, 48, 49, 50, 51, 47, 46,\n 52, 53, 54, 54, 46, 55, 43, 56, 46, 54, 48, 57, 58, 58, 58, 43,\n 59, 59, 59, 59, 59, 60, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,\n 59, 59, 59, 59, 59, 59, 59, 50, 59, 59, 59, 59, 59, 59, 59, 61,\n 62, 62, 62, 62, 62, 63, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62,\n 62, 62, 62, 62, 62, 62, 62, 50, 62, 62, 62, 62, 62, 62, 62, 64,\n\n/* block 2 */\n 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,\n 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,\n 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 67,\n 68, 69, 65, 66, 65, 66, 65, 66, 70, 65, 66, 65, 66, 65, 66, 65,\n 66, 65, 66, 65, 66, 65, 66, 65, 66, 71, 65, 66, 65, 66, 65, 66,\n 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,\n 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,\n 65, 66, 65, 66, 65, 66, 65, 66, 72, 65, 66, 65, 66, 65, 66, 73,\n\n/* block 3 */\n 74, 75, 65, 66, 65, 66, 76, 65, 66, 77, 77, 65, 66, 70, 78, 79,\n 80, 65, 66, 77, 81, 82, 83, 84, 65, 66, 85, 86, 83, 87, 88, 89,\n 65, 66, 65, 66, 65, 66, 90, 65, 66, 90, 70, 70, 65, 66, 90, 65,\n 66, 91, 91, 65, 66, 65, 66, 92, 65, 66, 70, 93, 65, 66, 70, 94,\n 93, 93, 93, 93, 95, 96, 97, 98, 99,100,101,102,103, 65, 66, 65,\n 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,104, 65, 66,\n 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,\n 69,105,106,107, 65, 66,108,109, 65, 66, 65, 66, 65, 66, 65, 66,\n\n/* block 4 */\n 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,\n 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,\n110, 70, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,\n 65, 66, 65, 66, 70, 70, 70, 70, 70, 70,111, 65, 66,112,113,114,\n114, 65, 66,115,116,117, 65, 66, 65, 67, 65, 66, 65, 66, 65, 66,\n118,119,120,121,122, 70,123,123, 70,124, 70,125,126, 70, 70, 70,\n123,127, 70,128,129,130,131, 70,132,133,131,134,135, 70, 70,133,\n 70,136,137, 70, 70,138, 70, 70, 70, 70, 70, 70, 70,139, 70, 70,\n\n/* block 5 */\n140, 70,141,140, 70, 70, 70,142,140,143,144,144,145, 70, 70, 70,\n 70, 70,146, 70, 93, 70, 70, 70, 70, 70, 70, 70, 70,147,148, 70,\n 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70,\n149,149,150,149,149,149,149,149,149,151,151,152,153,152,152,152,\n154,154, 46, 46, 46, 46,151,155,151,155,155,155,151,156,151,151,\n157,157, 46, 46, 46, 46, 46,158, 46,159, 46, 46, 46, 46, 46, 46,\n149,149,149,149,149, 46, 46, 46, 46, 46,160,160,151, 46,152, 46,\n 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46,\n\n/* block 6 */\n161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,\n174,177,176,178,176,176,176,176,176,176,176,176,176,176,176,176,\n179,176,176,180,181,179,176,176,176,176,176,176,176,182,179,176,\n183,184,176,176,176,176,176,176,176,176,176,176,176,176,176,176,\n176,176,185,176,176,186,176,176,176,176,176,176,176,176,176,187,\n176,176,176,176,176,176,176,176,188,189,189,189,189,176,190,176,\n176,176,176,191,191,191,191,191,191,191,191,191,191,191,191,191,\n192,193,192,193,194,195,192,193,196,196,197,198,198,198,199,200,\n\n/* block 7 */\n196,196,196,196,201, 46,202,203,204,204,204,196,205,196,206,206,\n207,208,209,208,208,210,208,208,211,212,213,208,214,208,208,208,\n215,216,196,217,208,208,218,208,208,219,208,208,220,221,221,221,\n222,223,224,223,223,225,223,223,226,227,228,223,229,223,223,223,\n230,231,232,233,223,223,234,223,223,235,223,223,236,237,237,238,\n239,240,241,242,242,243,244,245,192,193,192,193,192,193,192,193,\n192,193,246,247,246,247,246,247,246,247,246,247,246,247,246,247,\n248,249,250,251,252,253,254,192,193,255,192,193,256,257,257,257,\n\n/* block 8 */\n258,258,258,258,258,258,258,258,258,258,258,258,258,258,258,258,\n259,259,260,259,261,259,259,259,259,259,259,259,259,259,262,259,\n259,263,264,259,259,259,259,259,259,259,265,259,259,259,259,259,\n266,266,267,266,268,266,266,266,266,266,266,266,266,266,269,266,\n266,270,271,266,266,266,266,266,266,266,272,266,266,266,266,266,\n273,273,273,273,273,273,274,273,274,273,273,273,273,273,273,273,\n275,276,277,278,275,276,275,276,275,276,275,276,275,276,275,276,\n275,276,275,276,275,276,275,276,275,276,275,276,275,276,275,276,\n\n/* block 9 */\n275,276,279,280,281,282,282,281,283,283,275,276,275,276,275,276,\n275,276,275,276,275,276,275,276,275,276,275,276,275,276,275,276,\n275,276,275,276,275,276,275,276,275,276,275,276,275,276,275,276,\n275,276,275,276,275,276,275,276,275,276,275,276,275,276,275,276,\n284,275,276,275,276,275,276,275,276,275,276,275,276,275,276,285,\n275,276,275,276,275,276,275,276,275,276,275,276,275,276,275,276,\n275,276,275,276,275,276,275,276,275,276,275,276,275,276,275,276,\n275,276,275,276,275,276,275,276,275,276,275,276,275,276,275,276,\n\n/* block 10 */\n275,276,275,276,275,276,275,276,275,276,275,276,275,276,275,276,\n275,276,275,276,275,276,275,276,275,276,275,276,275,276,275,276,\n275,276,275,276,275,276,275,276,275,276,275,276,275,276,275,276,\n196,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,\n286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,\n286,286,286,286,286,286,286,196,196,287,288,288,288,288,288,289,\n290,291,291,291,291,291,291,291,291,291,291,291,291,291,291,291,\n291,291,291,291,291,291,291,291,291,291,291,291,291,291,291,291,\n\n/* block 11 */\n291,291,291,291,291,291,291,292,290,293,294,196,196,295,295,296,\n297,298,298,298,298,298,298,298,298,298,298,298,298,298,298,298,\n298,298,299,298,298,298,298,298,298,298,298,298,298,298,298,298,\n300,300,300,300,300,300,300,300,300,300,300,300,300,300,301,300,\n302,300,300,303,300,304,302,304,297,297,297,297,297,297,297,297,\n305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,\n305,305,305,305,305,305,305,305,305,305,305,297,297,297,297,305,\n305,305,305,302,306,297,297,297,297,297,297,297,297,297,297,297,\n\n/* block 12 */\n307,307,307,307,307,308,309,309,310,311,311,312,313,314,315,315,\n316,316,316,316,316,316,316,316,316,316,316,317,318,319,319,320,\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,\n322,321,321,321,321,321,321,321,321,321,321,323,323,323,323,323,\n323,323,323,324,325,325,316,326,327,316,316,316,316,316,316,316,\n328,328,328,328,328,328,328,328,328,328,311,329,329,314,321,321,\n324,321,321,330,321,321,321,321,321,321,321,321,321,321,321,321,\n\n/* block 13 */\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,\n321,321,321,321,331,321,316,316,316,316,316,316,332,308,315,333,\n333,316,316,332,316,334,334,332,332,315,333,333,333,316,321,321,\n335,335,335,335,335,335,335,335,335,335,321,321,321,336,336,321,\n\n/* block 14 */\n337,337,337,338,338,338,338,338,338,338,338,339,338,339,340,341,\n342,343,342,342,342,342,342,342,342,342,342,342,342,342,342,342,\n342,342,342,342,342,342,342,342,342,342,342,342,342,342,342,342,\n344,344,344,344,344,344,344,344,344,344,344,344,344,344,344,344,\n345,345,345,345,345,345,345,345,345,345,345,340,340,342,342,342,\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,\n\n/* block 15 */\n346,346,346,346,346,346,346,346,346,346,346,346,346,346,346,346,\n346,346,346,346,346,346,346,346,346,346,346,346,346,346,346,346,\n346,346,346,346,346,346,347,347,347,347,347,347,347,347,347,347,\n347,346,340,340,340,340,340,340,340,340,340,340,340,340,340,340,\n348,348,348,348,348,348,348,348,348,348,349,349,349,349,349,349,\n349,349,349,349,349,349,349,349,349,349,349,349,349,349,349,349,\n349,349,349,349,349,349,349,349,349,349,349,350,350,350,350,350,\n350,350,350,350,351,351,352,353,354,355,356,297,297,357,358,358,\n\n/* block 16 */\n359,359,359,359,359,359,359,359,359,359,359,359,359,359,359,359,\n359,359,359,359,359,359,360,360,361,361,362,360,360,360,360,360,\n360,360,360,360,362,360,360,360,362,360,360,360,360,363,297,297,\n364,364,364,364,364,364,365,366,364,366,364,364,364,366,366,297,\n367,367,367,367,367,367,367,367,367,367,367,367,367,367,367,367,\n367,367,367,367,367,367,367,367,367,368,368,368,297,297,369,297,\n342,342,342,342,342,342,342,342,342,342,342,340,340,340,340,340,\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,\n\n/* block 17 */\n321,321,321,321,321,321,321,321,370,321,321,321,321,321,321,340,\n307,307,340,340,340,340,340,316,333,333,333,333,333,333,333,333,\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,\n321,321,321,321,321,321,321,321,321,334,327,327,333,327,327,327,\n333,333,333,371,316,316,316,316,316,316,316,316,316,316,316,316,\n372,372,308,326,326,326,326,326,326,326,333,333,333,333,333,333,\n326,326,326,373,326,326,326,326,326,326,326,326,326,326,326,316,\n\n/* block 18 */\n374,374,374,375,376,376,376,376,376,376,376,376,376,376,376,376,\n376,376,376,376,376,377,377,377,377,377,377,377,377,377,377,377,\n377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,\n377,377,377,377,377,377,377,377,377,377,374,375,378,376,375,375,\n375,374,374,374,374,374,374,374,374,375,375,375,375,379,375,375,\n376,380,381,176,176,374,374,374,377,377,377,377,377,377,377,377,\n376,376,374,374,382,383,384,384,384,384,384,384,384,384,384,384,\n385,386,376,376,376,376,376,376,377,377,377,377,377,377,377,377,\n\n/* block 19 */\n387,388,389,389,196,387,387,387,387,387,387,387,387,196,196,387,\n387,196,196,387,387,390,390,390,390,390,390,390,390,390,390,390,\n390,390,390,390,390,390,390,390,390,196,390,390,390,390,390,390,\n390,196,390,196,196,196,390,390,390,390,196,196,391,387,392,389,\n389,388,388,388,388,196,196,389,389,196,196,389,389,393,387,196,\n196,196,196,196,196,196,196,392,196,196,196,196,390,390,196,390,\n387,387,388,388,196,196,394,394,394,394,394,394,394,394,394,394,\n390,390,395,395,396,396,396,396,396,396,397,395,387,398,399,196,\n\n/* block 20 */\n196,400,400,401,196,402,402,402,402,402,402,196,196,196,196,402,\n402,196,196,402,402,402,402,402,402,402,402,402,402,402,402,402,\n402,402,402,402,402,402,402,402,402,196,402,402,402,402,402,402,\n402,196,402,402,196,402,402,196,402,402,196,196,403,196,401,401,\n401,400,400,196,196,196,196,400,400,196,196,400,400,404,196,196,\n196,400,196,196,196,196,196,196,196,402,402,402,402,196,402,196,\n196,196,196,196,196,196,405,405,405,405,405,405,405,405,405,405,\n400,406,402,402,402,400,407,196,196,196,196,196,196,196,196,196,\n\n/* block 21 */\n196,408,408,409,196,410,410,410,410,410,410,410,410,410,196,410,\n410,410,196,410,410,411,411,411,411,411,411,411,411,411,411,411,\n411,411,411,411,411,411,411,411,411,196,411,411,411,411,411,411,\n411,196,411,411,196,411,411,411,411,411,196,196,412,410,409,409,\n409,408,408,408,408,408,196,408,408,409,196,409,409,413,196,196,\n410,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n410,410,408,408,196,196,414,414,414,414,414,414,414,414,414,414,\n415,416,196,196,196,196,196,196,196,411,408,417,408,412,412,412,\n\n/* block 22 */\n196,418,419,419,196,420,420,420,420,420,420,420,420,196,196,420,\n420,196,196,420,420,421,421,421,421,421,421,421,421,421,421,421,\n421,421,421,421,421,421,421,421,421,196,421,421,421,421,421,421,\n421,196,421,421,196,421,421,421,421,421,196,196,422,420,423,418,\n419,418,418,418,418,196,196,419,419,196,196,419,419,424,196,196,\n196,196,196,196,196,425,418,423,196,196,196,196,421,421,196,421,\n420,420,418,418,196,196,426,426,426,426,426,426,426,426,426,426,\n427,421,428,428,428,428,428,428,196,196,196,196,196,196,196,196,\n\n/* block 23 */\n196,196,429,430,196,430,430,430,430,430,430,196,196,196,430,430,\n430,196,430,430,430,430,196,196,196,430,430,196,430,196,430,430,\n196,196,196,430,430,196,196,196,430,430,430,196,196,196,430,430,\n430,430,430,430,430,430,430,430,430,430,196,196,196,196,431,432,\n429,432,432,196,196,196,432,432,432,196,432,432,432,433,196,196,\n430,196,196,196,196,196,196,431,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,434,434,434,434,434,434,434,434,434,434,\n435,435,435,436,437,437,437,437,437,438,437,196,196,196,196,196,\n\n/* block 24 */\n439,440,440,440,439,441,441,441,441,441,441,441,441,196,441,441,\n441,196,441,441,441,442,442,442,442,442,442,442,442,442,442,442,\n442,442,442,442,442,442,442,442,442,196,442,442,442,442,442,442,\n442,442,442,442,442,442,442,442,442,442,196,196,443,441,439,439,\n439,440,440,440,440,196,439,439,439,196,439,439,439,444,196,196,\n196,196,196,196,196,439,439,196,442,442,442,196,196,441,196,196,\n441,441,439,439,196,196,445,445,445,445,445,445,445,445,445,445,\n196,196,196,196,196,196,196,446,447,447,447,447,447,447,447,448,\n\n/* block 25 */\n449,450,451,451,452,449,449,449,449,449,449,449,449,196,449,449,\n449,196,449,449,449,449,449,449,449,449,449,449,449,449,449,449,\n449,449,449,449,449,449,449,449,449,196,449,449,449,449,449,449,\n449,449,449,449,196,449,449,449,449,449,196,196,453,449,451,454,\n455,451,455,451,451,196,454,455,455,196,455,455,450,456,196,196,\n196,196,196,196,196,455,455,196,196,196,196,196,196,449,449,196,\n449,449,450,450,196,196,457,457,457,457,457,457,457,457,457,457,\n196,449,449,451,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 26 */\n458,458,459,459,460,460,460,460,460,460,460,460,460,196,460,460,\n460,196,460,460,460,461,461,461,461,461,461,461,461,461,461,461,\n461,461,461,461,461,461,461,461,461,461,461,461,461,461,461,461,\n461,461,461,461,461,461,461,461,461,461,461,462,462,460,463,459,\n459,458,458,458,458,196,459,459,459,196,459,459,459,462,464,465,\n196,196,196,196,460,460,460,463,466,466,466,466,466,466,466,460,\n460,460,458,458,196,196,467,467,467,467,467,467,467,467,467,467,\n466,466,466,466,466,466,466,466,466,465,460,460,460,460,460,460,\n\n/* block 27 */\n196,468,469,469,196,470,470,470,470,470,470,470,470,470,470,470,\n470,470,470,470,470,470,470,196,196,196,470,470,470,470,470,470,\n470,470,470,470,470,470,470,470,470,470,470,470,470,470,470,470,\n470,470,196,470,470,470,470,470,470,470,470,470,196,470,196,196,\n470,470,470,470,470,470,470,196,196,196,471,196,196,196,196,472,\n469,469,468,468,468,196,468,196,469,469,469,469,469,469,469,472,\n196,196,196,196,196,196,473,473,473,473,473,473,473,473,473,473,\n196,196,469,469,474,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 28 */\n196,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,\n475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,\n475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,\n475,476,475,477,476,476,476,476,476,476,478,196,196,196,196,479,\n480,480,480,480,480,475,481,482,482,482,482,482,482,476,482,483,\n484,484,484,484,484,484,484,484,484,484,485,485,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 29 */\n196,486,486,196,486,196,486,486,486,486,486,196,486,486,486,486,\n486,486,486,486,486,486,486,486,486,486,486,486,486,486,486,486,\n486,486,486,486,196,486,196,486,486,486,486,486,486,486,486,486,\n486,487,486,488,487,487,487,487,487,487,489,487,487,486,196,196,\n490,490,490,490,490,196,491,196,492,492,492,492,492,487,493,196,\n494,494,494,494,494,494,494,494,494,494,196,196,486,486,486,486,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 30 */\n495,496,496,496,497,497,497,497,498,497,497,497,497,498,498,498,\n498,498,498,496,497,496,496,496,499,499,496,496,496,496,496,496,\n500,500,500,500,500,500,500,500,500,500,501,501,501,501,501,501,\n501,501,501,501,496,499,496,499,496,499,502,503,502,503,504,504,\n495,495,495,495,495,495,495,495,196,495,495,495,495,495,495,495,\n495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,\n495,495,495,495,495,495,495,495,495,495,495,495,495,196,196,196,\n196,505,505,505,505,505,505,506,505,506,505,505,505,505,505,507,\n\n/* block 31 */\n505,505,508,508,509,497,499,499,495,495,495,495,495,505,505,505,\n505,505,505,505,505,505,505,505,196,505,505,505,505,505,505,505,\n505,505,505,505,505,505,505,505,505,505,505,505,505,505,505,505,\n505,505,505,505,505,505,505,505,505,505,505,505,505,196,496,496,\n496,496,496,496,496,496,499,496,496,496,496,496,496,196,496,496,\n497,497,497,497,497,510,510,510,510,497,497,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 32 */\n511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,\n511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,\n511,511,511,511,511,511,511,511,511,511,511,512,512,513,513,513,\n513,514,513,513,513,513,513,515,512,516,516,514,514,513,513,511,\n517,517,517,517,517,517,517,517,517,517,518,518,519,519,519,519,\n511,511,511,511,511,511,514,514,513,513,511,511,511,511,513,513,\n513,511,512,520,520,511,511,512,512,520,520,520,520,520,511,511,\n511,513,513,513,513,511,511,511,511,511,511,511,511,511,511,511,\n\n/* block 33 */\n511,511,513,512,514,513,513,520,520,520,520,520,520,521,511,520,\n522,522,522,522,522,522,522,522,522,522,520,520,512,513,523,523,\n524,524,524,524,524,524,524,524,524,524,524,524,524,524,524,524,\n524,524,524,524,524,524,524,524,524,524,524,524,524,524,524,524,\n524,524,524,524,524,524,196,524,196,196,196,196,196,524,196,196,\n525,525,525,525,525,525,525,525,525,525,525,525,525,525,525,525,\n525,525,525,525,525,525,525,525,525,525,525,525,525,525,525,525,\n525,525,525,525,525,525,525,525,525,525,525,526,527,525,525,525,\n\n/* block 34 */\n528,528,528,528,528,528,528,528,528,528,528,528,528,528,528,528,\n528,528,528,528,528,528,528,528,528,528,528,528,528,528,528,528,\n528,528,528,528,528,528,528,528,528,528,528,528,528,528,528,528,\n528,528,528,528,528,528,528,528,528,528,528,528,528,528,528,528,\n528,528,528,528,528,528,528,528,528,528,528,528,528,528,528,528,\n528,528,528,528,528,528,528,528,528,528,528,528,528,528,528,529,\n530,531,531,531,531,531,531,531,531,531,531,531,531,531,531,531,\n531,531,531,531,531,531,531,531,531,531,531,531,531,531,531,531,\n\n/* block 35 */\n531,531,531,531,531,531,531,531,531,531,531,531,531,531,531,531,\n531,531,531,531,531,531,531,531,531,531,531,531,531,531,531,531,\n531,531,531,531,531,531,531,531,532,532,532,532,532,532,532,532,\n532,532,532,532,532,532,532,532,532,532,532,532,532,532,532,532,\n532,532,532,532,532,532,532,532,532,532,532,532,532,532,532,532,\n532,532,532,532,532,532,532,532,532,532,532,532,532,532,532,532,\n532,532,532,532,532,532,532,532,532,532,532,532,532,532,532,532,\n532,532,532,532,532,532,532,532,532,532,532,532,532,532,532,532,\n\n/* block 36 */\n533,533,533,533,533,533,533,533,533,533,533,533,533,533,533,533,\n533,533,533,533,533,533,533,533,533,533,533,533,533,533,533,533,\n533,533,533,533,533,533,533,533,533,533,533,533,533,533,533,533,\n533,533,533,533,533,533,533,533,533,533,533,533,533,533,533,533,\n533,533,533,533,533,533,533,533,533,196,533,533,533,533,196,196,\n533,533,533,533,533,533,533,196,533,196,533,533,533,533,196,196,\n533,533,533,533,533,533,533,533,533,533,533,533,533,533,533,533,\n533,533,533,533,533,533,533,533,533,533,533,533,533,533,533,533,\n\n/* block 37 */\n533,533,533,533,533,533,533,533,533,196,533,533,533,533,196,196,\n533,533,533,533,533,533,533,533,533,533,533,533,533,533,533,533,\n533,533,533,533,533,533,533,533,533,533,533,533,533,533,533,533,\n533,196,533,533,533,533,196,196,533,533,533,533,533,533,533,196,\n533,196,533,533,533,533,196,196,533,533,533,533,533,533,533,533,\n533,533,533,533,533,533,533,196,533,533,533,533,533,533,533,533,\n533,533,533,533,533,533,533,533,533,533,533,533,533,533,533,533,\n533,533,533,533,533,533,533,533,533,533,533,533,533,533,533,533,\n\n/* block 38 */\n533,533,533,533,533,533,533,533,533,533,533,533,533,533,533,533,\n533,196,533,533,533,533,196,196,533,533,533,533,533,533,533,533,\n533,533,533,533,533,533,533,533,533,533,533,533,533,533,533,533,\n533,533,533,533,533,533,533,533,533,533,533,533,533,533,533,533,\n533,533,533,533,533,533,533,533,533,533,533,533,533,533,533,533,\n533,533,533,533,533,533,533,533,533,533,533,196,196,534,534,534,\n535,536,537,536,536,536,536,537,537,538,538,538,538,538,538,538,\n538,538,539,539,539,539,539,539,539,539,539,539,539,196,196,196,\n\n/* block 39 */\n533,533,533,533,533,533,533,533,533,533,533,533,533,533,533,533,\n540,540,540,540,540,540,540,540,540,540,196,196,196,196,196,196,\n541,541,541,541,541,541,541,541,541,541,541,541,541,541,541,541,\n541,541,541,541,541,541,541,541,541,541,541,541,541,541,541,541,\n541,541,541,541,541,541,541,541,541,541,541,541,541,541,541,541,\n541,541,541,541,541,541,541,541,541,541,541,541,541,541,541,541,\n541,541,541,541,541,541,541,541,541,541,541,541,541,541,541,541,\n542,542,542,542,542,542,196,196,543,543,543,543,543,543,196,196,\n\n/* block 40 */\n544,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,\n545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,\n545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,\n545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,\n545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,\n545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,\n545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,\n545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,\n\n/* block 41 */\n545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,\n545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,\n545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,\n545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,\n545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,\n545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,\n545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,\n545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,\n\n/* block 42 */\n545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,\n545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,\n545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,\n545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,\n545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,\n545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,\n545,545,545,545,545,545,545,545,545,545,545,545,545,546,547,545,\n545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,\n\n/* block 43 */\n548,549,549,549,549,549,549,549,549,549,549,549,549,549,549,549,\n549,549,549,549,549,549,549,549,549,549,549,550,551,196,196,196,\n552,552,552,552,552,552,552,552,552,552,552,552,552,552,552,552,\n552,552,552,552,552,552,552,552,552,552,552,552,552,552,552,552,\n552,552,552,552,552,552,552,552,552,552,552,552,552,552,552,552,\n552,552,552,552,552,552,552,552,552,552,552,552,552,552,552,552,\n552,552,552,552,552,552,552,552,552,552,552,553,553,553,554,554,\n554,552,552,552,552,552,552,552,552,196,196,196,196,196,196,196,\n\n/* block 44 */\n555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,555,\n555,555,556,556,557,558,196,196,196,196,196,196,196,196,196,555,\n559,559,559,559,559,559,559,559,559,559,559,559,559,559,559,559,\n559,559,560,560,561,562,562,196,196,196,196,196,196,196,196,196,\n563,563,563,563,563,563,563,563,563,563,563,563,563,563,563,563,\n563,563,564,564,196,196,196,196,196,196,196,196,196,196,196,196,\n565,565,565,565,565,565,565,565,565,565,565,565,565,196,565,565,\n565,196,566,566,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 45 */\n567,567,567,567,567,567,567,567,567,567,567,567,567,567,567,567,\n567,567,567,567,567,567,567,567,567,567,567,567,567,567,567,567,\n567,567,567,568,568,567,567,567,567,567,567,567,567,567,567,567,\n567,567,567,567,569,569,570,571,571,571,571,571,571,571,570,570,\n570,570,570,570,570,570,571,570,570,572,572,572,572,572,572,572,\n572,572,573,572,574,574,575,576,577,577,575,578,567,572,196,196,\n579,579,579,579,579,579,579,579,579,579,196,196,196,196,196,196,\n580,580,580,580,580,580,580,580,580,580,196,196,196,196,196,196,\n\n/* block 46 */\n581,581,582,583,584,582,585,581,584,586,587,588,588,588,589,588,\n590,590,590,590,590,590,590,590,590,590,196,196,196,196,196,196,\n591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,\n591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,\n591,591,591,592,591,591,591,591,591,591,591,591,591,591,591,591,\n591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,\n591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,\n591,591,591,591,591,591,591,591,591,196,196,196,196,196,196,196,\n\n/* block 47 */\n591,591,591,591,591,593,593,591,591,591,591,591,591,591,591,591,\n591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,\n591,591,591,591,591,591,591,591,591,594,591,196,196,196,196,196,\n545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,\n545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,\n545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,\n545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,\n545,545,545,545,545,545,196,196,196,196,196,196,196,196,196,196,\n\n/* block 48 */\n595,595,595,595,595,595,595,595,595,595,595,595,595,595,595,595,\n595,595,595,595,595,595,595,595,595,595,595,595,595,595,595,196,\n596,596,596,597,597,597,597,596,596,597,597,597,196,196,196,196,\n597,597,596,597,597,597,597,597,597,598,598,598,196,196,196,196,\n599,196,196,196,600,600,601,601,601,601,601,601,601,601,601,601,\n602,602,602,602,602,602,602,602,602,602,602,602,602,602,602,602,\n602,602,602,602,602,602,602,602,602,602,602,602,602,602,196,196,\n602,602,602,602,602,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 49 */\n603,603,603,603,603,603,603,603,603,603,603,603,603,603,603,603,\n603,603,603,603,603,603,603,603,603,603,603,603,603,603,603,603,\n603,603,603,603,603,603,603,603,603,603,603,603,196,196,196,196,\n603,603,603,603,603,604,604,604,603,603,604,603,603,603,603,603,\n603,603,603,603,603,603,603,603,603,603,196,196,196,196,196,196,\n605,605,605,605,605,605,605,605,605,605,606,196,196,196,607,607,\n608,608,608,608,608,608,608,608,608,608,608,608,608,608,608,608,\n608,608,608,608,608,608,608,608,608,608,608,608,608,608,608,608,\n\n/* block 50 */\n609,609,609,609,609,609,609,609,609,609,609,609,609,609,609,609,\n609,609,609,609,609,609,609,610,610,611,611,610,196,196,612,612,\n613,613,613,613,613,613,613,613,613,613,613,613,613,613,613,613,\n613,613,613,613,613,613,613,613,613,613,613,613,613,613,613,613,\n613,613,613,613,613,613,613,613,613,613,613,613,613,613,613,613,\n613,613,613,613,613,614,615,614,615,615,615,615,615,615,615,196,\n616,617,615,617,617,615,615,615,615,615,615,615,615,614,614,614,\n614,614,614,615,615,618,618,618,618,618,618,618,618,196,196,618,\n\n/* block 51 */\n619,619,619,619,619,619,619,619,619,619,196,196,196,196,196,196,\n619,619,619,619,619,619,619,619,619,619,196,196,196,196,196,196,\n620,620,620,620,620,620,620,621,622,622,622,622,620,620,196,196,\n176,176,176,176,176,176,176,176,176,176,176,176,176,176,623,624,\n624,176,176,176,176,176,176,176,176,176,176,176,624,624,624,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 52 */\n625,625,625,625,626,627,627,627,627,627,627,627,627,627,627,627,\n627,627,627,627,627,627,627,627,627,627,627,627,627,627,627,627,\n627,627,627,627,627,627,627,627,627,627,627,627,627,627,627,627,\n627,627,627,627,628,629,625,625,625,625,625,629,625,629,626,626,\n626,626,625,629,630,627,627,627,627,627,627,627,627,196,631,631,\n632,632,632,632,632,632,632,632,632,632,631,631,633,634,631,631,\n633,635,635,635,635,635,635,635,635,635,635,628,628,628,628,628,\n628,628,628,628,635,635,635,635,635,635,635,635,635,631,631,631,\n\n/* block 53 */\n636,636,637,638,638,638,638,638,638,638,638,638,638,638,638,638,\n638,638,638,638,638,638,638,638,638,638,638,638,638,638,638,638,\n638,637,636,636,636,636,637,637,636,636,639,640,636,636,638,638,\n641,641,641,641,641,641,641,641,641,641,638,638,638,638,638,638,\n642,642,642,642,642,642,642,642,642,642,642,642,642,642,642,642,\n642,642,642,642,642,642,642,642,642,642,642,642,642,642,642,642,\n642,642,642,642,642,642,643,644,645,645,644,644,644,645,644,645,\n645,645,646,646,196,196,196,196,196,196,196,196,647,647,647,647,\n\n/* block 54 */\n648,648,648,648,648,648,648,648,648,648,648,648,648,648,648,648,\n648,648,648,648,648,648,648,648,648,648,648,648,648,648,648,648,\n648,648,648,648,649,649,649,649,649,649,649,649,650,650,650,650,\n650,650,650,650,649,649,651,652,196,196,196,653,653,654,654,654,\n655,655,655,655,655,655,655,655,655,655,196,196,196,648,648,648,\n656,656,656,656,656,656,656,656,656,656,657,657,657,657,657,657,\n657,657,657,657,657,657,657,657,657,657,657,657,657,657,657,657,\n657,657,657,657,657,657,657,657,658,658,658,659,658,658,660,660,\n\n/* block 55 */\n661,662,663,664,665,666,667,668,669,275,276,196,196,196,196,196,\n670,670,670,670,670,670,670,670,670,670,670,670,670,670,670,670,\n670,670,670,670,670,670,670,670,670,670,670,670,670,670,670,670,\n670,670,670,670,670,670,670,670,670,670,670,196,196,670,670,670,\n671,671,671,671,671,671,671,671,196,196,196,196,196,196,196,196,\n672,673,672,674,673,675,675,676,675,676,677,673,676,676,673,673,\n676,678,673,673,673,673,673,673,673,679,680,681,681,675,681,681,\n681,681,682,683,684,680,680,685,686,686,687,196,196,196,196,196,\n\n/* block 56 */\n 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70,\n 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70,\n 70, 70, 70, 70, 70, 70,256,256,256,256,256,688,149,149,149,149,\n149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,\n149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,\n149,149,149,149,149,149,149,149,149,149,149,149,149,689,689,689,\n689,689,150,149,149,149,689,689,689,689,689, 70, 70, 70, 70, 70,\n 70, 70, 70, 70, 70, 70, 70, 70,690,691, 70, 70, 70,692, 70, 70,\n\n/* block 57 */\n 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70,693, 70,\n 70, 70, 70, 70, 70, 70,694, 70, 70, 70, 70,695,695,695,695,695,\n695,695,695,695,696,695,695,695,696,695,695,695,695,695,695,695,\n695,695,695,695,695,695,695,695,695,695,695,695,695,695,695,697,\n698,698,189,189,176,176,176,176,176,176,176,176,176,176,176,176,\n189,189,189,624,624,624,624,624,624,624,624,624,624,624,624,624,\n624,624,624,624,624,624,624,624,624,624,624,624,624,624,624,624,\n624,624,624,624,624,176,176,176,699,176,700,176,176,176,176,176,\n\n/* block 58 */\n 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,\n 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,\n 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 67, 65, 66,\n 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,\n 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,\n 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,\n701,702, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,\n 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,\n\n/* block 59 */\n 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,\n 65, 66, 65, 66, 65, 66, 69, 69, 69, 69,703,704, 70, 70,705, 70,\n 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,\n 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,\n 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 67, 65, 66, 65, 66,\n 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,\n 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,\n 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,\n\n/* block 60 */\n706,706,706,706,706,706,706,706,707,707,707,707,707,707,707,707,\n706,706,706,706,706,706,196,196,707,707,707,707,707,707,196,196,\n706,706,706,706,706,706,706,706,707,707,707,707,707,707,707,707,\n706,706,706,706,706,706,706,706,707,707,707,707,707,707,707,707,\n706,706,706,706,706,706,196,196,707,707,707,707,707,707,196,196,\n708,706,708,706,708,706,708,706,196,707,196,707,196,707,196,707,\n706,706,706,706,706,706,706,706,707,707,707,707,707,707,707,707,\n709,709,710,710,710,710,711,711,712,712,713,713,714,714,196,196,\n\n/* block 61 */\n715,715,715,715,715,715,715,715,716,716,716,716,716,716,716,716,\n715,715,715,715,715,715,715,715,716,716,716,716,716,716,716,716,\n715,715,715,715,715,715,715,715,716,716,716,716,716,716,716,716,\n706,706,717,718,717,196,708,717,707,707,719,719,720,201,721,201,\n201,201,717,718,717,196,708,717,722,722,722,722,720,201,201,201,\n706,706,708,723,196,196,708,708,707,707,724,724,196,201,201,201,\n706,706,708,725,708,250,708,708,707,707,726,726,255,201,201,201,\n196,196,717,718,717,196,708,717,727,727,728,728,720,201,201,196,\n\n/* block 62 */\n729,729,729,729,729,729,729,729,729,729,729, 51,730,731,732,733,\n734,734,734,734,734,734,735, 43,736,737,738,739,739,740,738,739,\n 43, 43, 43, 43,741, 43, 43,742,743,744,745,746,747,748,749,750,\n751,751,752,752,752, 43, 43, 43, 43, 49, 57, 43,753,754, 43,755,\n756, 43, 43, 43,757,758,759,754,754,753, 43, 43, 43, 43, 43,760,\n 43, 43, 50,761,755, 43, 43, 43, 43, 43,762, 43, 43,763, 43,729,\n 51,764,764,764,764,765,766,767,768,769,770,770,770,770,770,770,\n 54,696,196,196, 54, 54, 54, 54, 54, 54,771,772,773,774,775,695,\n\n/* block 63 */\n 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,771,772,773,774,775,196,\n695,695,695,695,695,695,695,695,695,695,695,695,695,196,196,196,\n479,479,479,479,479,479,479,479,479,479,479,479,479,479,479,479,\n479,479,479,479,479,479,479,479,479,479,479,479,479,479,479,479,\n479,776,776,776,776,776,776,776,776,776,776,776,776,776,776,776,\n777,777,777,777,777,777,777,777,777,777,777,777,777,778,778,778,\n778,777,778,779,778,777,777,189,189,189,189,777,777,777,777,777,\n780,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 64 */\n781,781,782,781,781,781,781,782,781,781,783,782,782,782,783,783,\n782,782,782,783,781,782,781,781,784,782,782,782,782,782,781,781,\n781,781,785,781,782,781,786,781,782,787,788,789,782,782,790,783,\n782,782,791,782,783,792,792,792,792,793,781,781,783,783,782,782,\n794,794,794,794,794,782,783,783,795,795,781,794,781,781,796,510,\n 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,\n797,797,797,797,797,797,797,797,797,797,797,797,797,797,797,797,\n798,798,798,798,798,798,798,798,798,798,798,798,798,798,798,798,\n\n/* block 65 */\n799,799,799, 65, 66,799,799,799,799, 58,781,781,196,196,196,196,\n 50, 50, 50, 50,800,801,801,801,801,801, 50, 50,802,802,802,802,\n 50,802,802, 50,802,802, 50,802, 45,801,801,802,802,802, 50, 45,\n802,802, 45, 45, 45, 45,802,802, 45, 45, 45, 45,802,802,802,802,\n802,802,802,802,802,802,802,802,802,802,802,802,802,802, 50, 50,\n802,802, 50,802, 50,802,802,802,802,802,802,802, 45,802, 45, 45,\n 45, 45, 45, 45,802,802, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,\n 45, 45, 45, 45, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,\n\n/* block 66 */\n 50, 50,803, 50, 50, 50, 50,803,804,804,804,804,804,804, 50, 50,\n 50, 50,805, 53, 50,804, 50, 50, 50, 50, 50, 50, 50, 50,803,804,\n804,804,804, 50,804, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,\n 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,804,804, 50, 50,\n 50, 50, 50,804, 50,804, 50, 50, 50, 50, 50, 50,804, 50, 50, 50,\n 50, 50,804,804,804,804, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,\n 50, 50, 50, 50,804,804,804,804,804,804,804,804, 50, 50,804,804,\n804,804,804,804,804,804,804,804,804,804,804,804,804,804,804,804,\n\n/* block 67 */\n804,804,804,804,804,804,804,804,804,804,804,804, 50, 50, 50,804,\n804,804,804, 50, 50, 50, 50, 50,804, 50, 50, 50, 50, 50, 50, 50,\n 50, 50,804,804, 50, 50,804, 50,804,804, 50,804, 50, 50, 50, 50,\n804,804,804,804,804,804,804,804,804, 50, 50, 50, 50, 50, 50, 50,\n 50, 50, 50, 50, 50, 50, 50, 50, 50,804,804,804,804,804, 50, 50,\n804,804, 50, 50, 50, 50,804,804,804,804,804,804,804,804,804,804,\n804,804,804,804,804,804,804,804,804,804,804,804,804,804, 50, 50,\n804,804,804,804,804, 50,804,804, 50, 50,804,804,804,804,804, 50,\n\n/* block 68 */\n 45, 45, 45, 45, 45, 45, 45, 45,806,807,806,807, 45, 45, 45, 45,\n 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,808,808, 45, 45, 45, 45,\n 50, 50, 45, 45, 45, 45, 45, 45, 47,809,810, 45, 45, 45, 45, 45,\n 45, 45, 45, 45, 45, 45,811,811,811,811,811,811,811,811,811,811,\n811,811,811,811,811,811,811,811,811,811,811,811,811,811,811,811,\n811,811,811,811,811,811,811,811,811,811,811,811,811,811,811,811,\n811,811,811,811,811,811,811,811,811,811,811,811,811,811,811,811,\n811,811,811,811,811,811,811,811,811,811,811, 45, 50, 45, 45, 45,\n\n/* block 69 */\n 45, 45, 45, 45, 45, 45, 45, 45,812, 45, 45, 45, 45, 45, 45, 45,\n 45, 45, 45, 45, 45,811, 45, 45, 45, 45, 45, 50, 50, 50, 50, 50,\n 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,\n 50, 50, 50, 50,802,802, 45,802, 45, 45, 45, 45, 45, 45, 45, 45,\n 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 47,\n802, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 50, 50, 50, 50,\n 50, 50,802, 45, 45, 45, 45, 45, 45,808,808,808,808, 47, 47, 47,\n808, 47, 47,808, 45, 45, 45, 45, 47, 47, 47, 45, 45, 45, 45, 45,\n\n/* block 70 */\n 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,\n 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,\n 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,813,813,813,813,813,813,\n813,813,813,813,813,813,813,813,813,813,813,813,813,813,813,813,\n 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,813,813,813,813,813,\n813,813,813,813,813,813,813,813,813,813,813,813,813,813,813,813,\n 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,\n 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,\n\n/* block 71 */\n 58, 58, 58, 58, 58, 58, 58, 58,814,814,814,814,814,814,814,814,\n814,814,814,814,814,814,814,814,814,814,814,814,510,510,510,510,\n510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,\n510,510,510,510,510,510,815,815,815,815,815,815,815,815,815,815,\n815,815,816,815,815,815,815,815,815,815,815,815,815,815,815,815,\n817,817,817,817,817,817,817,817,817,817,817,817,817,817,817,817,\n817,817,817,817,817,817,817,817,817,817, 58, 58, 58, 58, 58, 58,\n 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,\n\n/* block 72 */\n 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,\n 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,\n 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,\n 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,\n 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,\n 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,\n 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,\n 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,\n\n/* block 73 */\n 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,\n 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,\n802,802, 45, 45, 45, 45, 45, 45, 45, 45, 47, 47, 45, 45,802,802,\n802,802,802,802,802,802,801, 50, 45, 45, 45, 45,802,802,802,802,\n801, 50, 45, 45, 45, 45,802,802, 45, 45,802,802, 45, 45, 45,802,\n802,802,802,802, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,\n 45, 45,802, 45,802, 45, 45,802,802,802,802,802,802, 45, 45, 45,\n 45, 45, 45, 45, 45, 45, 45, 45, 50, 50, 50,800,800,818,818, 50,\n\n/* block 74 */\n 47, 47, 47, 47, 47,819,802,812,812,812,812,812,812,812, 47,812,\n812, 47,812, 45,808,808,812,812, 47,812,812,812,812,820,812,812,\n 47,812, 47, 47,812,812, 47,812,812,812, 47,812,812,812, 47, 47,\n812,812,812,812,812,812,812,812, 47, 47, 47,812,812,812,812,812,\n801,812,801,812,812,812,812,812,808,808,808,808,808,808,808,808,\n808,808,808,808,812,812,812,812,812,812,812,812,812,812,812, 47,\n801,819,819,801,812, 47, 47,812, 47,812,812,812,812,819,819,821,\n812,812,812,812,812,812,812,812,812,812,812, 47,812,812, 47,808,\n\n/* block 75 */\n812,812,812,812,812,812, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,\n812,812, 47,808, 47, 47, 47, 47,812, 47,812, 47, 47,812,812,812,\n 47,808,812,812,812,812,812, 47,812,812,808,808,822,812,812,812,\n 47, 47,812,812,812,812,812,812,812,812,812,812,812,808,808,812,\n812,812,812,812,808,808,812,812, 47,812,812,812,812,812,808, 47,\n812, 47,812, 47,808,812,812,812,812,812,812,812,812,812,812,812,\n812,812,812,812,812,812,812,812,812, 47,808,812,812,812,812,812,\n 47, 47,808,808, 47,808,812, 47, 47,820,808,812,812,808,812,812,\n\n/* block 76 */\n812,812, 47,812,812,808, 45, 45, 47, 47,823,823,820,820,812, 47,\n812,812, 47, 45, 47, 45, 47, 45, 45, 45, 45, 45, 45, 47, 45, 45,\n 45, 47, 45, 45, 45, 45, 45, 45,808, 45, 45, 45, 45, 45, 45, 45,\n 45, 45, 45, 47, 47, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,\n 45, 45, 45, 45, 47, 45, 45, 47, 45, 45, 45, 45,808, 45,808, 45,\n 45, 45, 45,808,808,808, 45,808, 45, 45, 45, 45, 45, 45, 45, 45,\n 45, 45, 45, 47, 47,812,812,812,758,759,758,759,758,759,758,759,\n758,759,758,759,758,759, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,\n\n/* block 77 */\n 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,\n 58, 58, 58, 58, 45,808,808,808, 45, 45, 45, 45, 45, 45, 45, 45,\n 45, 47, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,\n808, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,808,\n 50, 50, 50,804,804,806,807, 50,804,804, 50,804, 50,804, 50, 50,\n 50, 50, 50, 50, 50,804,804, 50, 50, 50, 50, 50,804,804,804, 50,\n 50, 50,804,804,804,804,806,807,806,807,806,807,806,807,806,807,\n 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,\n\n/* block 78 */\n824,824,824,824,824,824,824,824,824,824,824,824,824,824,824,824,\n824,824,824,824,824,824,824,824,824,824,824,824,824,824,824,824,\n824,824,824,824,824,824,824,824,824,824,824,824,824,824,824,824,\n824,824,824,824,824,824,824,824,824,824,824,824,824,824,824,824,\n824,824,824,824,824,824,824,824,824,824,824,824,824,824,824,824,\n824,824,824,824,824,824,824,824,824,824,824,824,824,824,824,824,\n824,824,824,824,824,824,824,824,824,824,824,824,824,824,824,824,\n824,824,824,824,824,824,824,824,824,824,824,824,824,824,824,824,\n\n/* block 79 */\n 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,\n 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,\n 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,\n 50, 50, 50, 50,800,800, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,\n 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,\n 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,\n 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,\n 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,\n\n/* block 80 */\n 50, 50, 50,806,807,806,807,806,807,806,807,806,807,806,807,806,\n807,806,807,806,807,806,807,806,807, 50, 50,804, 50, 50, 50, 50,\n804, 50, 50,804,804,804, 50, 50,804,804,804,804,804,804,804,804,\n 50, 50, 50, 50, 50, 50, 50, 50,804, 50, 50, 50, 50, 50, 50, 50,\n804,804, 50, 50,804,804, 50, 50, 50, 50, 50, 50, 50, 50, 50,804,\n804,804,804, 50,804,804, 50, 50,806,807,806,807, 50, 50, 50, 50,\n 50, 50, 50, 50, 50, 50, 50, 50,804,804, 50, 50, 50, 50, 50, 50,\n 50, 50, 50, 50, 50,804, 50, 50,804,804, 50, 50,806,807, 50, 50,\n\n/* block 81 */\n 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,\n 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,\n 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,804,804,804,804, 50,\n 50, 50, 50, 50,804,804, 50, 50, 50, 50, 50, 50,804,804, 50, 50,\n 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,\n 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,\n 50, 50, 50, 50,804,804, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,\n 50, 50, 50, 50, 50, 50, 50, 50, 50,804,804,804,804,804,804,804,\n\n/* block 82 */\n804,804,804,804,804,804,804,804,804,804,804,804,804,804,804,804,\n804,804,804,804,804,804,804,804,804,804,804,804,804,804,804,804,\n804,804,804, 50, 50, 50,804,804,804,804,804,804,804,804, 50,804,\n804,804,804,804,804,804,804,804,804,804,804,804,804,804,804,804,\n804,804,804,804,804,804,804,804,804,804,804,804,804,804,804,804,\n804,804,804,804,804,804,804, 50, 50, 50, 50, 50, 50, 50,804, 50,\n 50, 50, 50,804,804,804, 50, 50, 50, 50, 50, 50,804,804,804, 50,\n 50, 50, 50, 50, 50, 50, 50,804,804,804,804, 50, 50, 50, 50, 50,\n\n/* block 83 */\n 45, 45, 45, 45, 45, 47, 47, 47, 45, 45, 45, 45, 45, 45, 45, 45,\n 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,808,808, 45, 45, 45,\n 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,\n 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,\n 50, 50, 50, 50, 50, 45, 45, 50, 50, 50, 50, 50, 50, 45, 45, 45,\n808, 45, 45, 45, 45,808, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,\n 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,\n 45, 45, 45, 45,813,813, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,\n\n/* block 84 */\n 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,\n 45, 45, 45, 45, 45, 45,813, 45, 45, 45, 45, 45, 45, 45, 45, 45,\n 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,\n 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,\n 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,\n 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,\n 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,\n 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,825, 45,\n\n/* block 85 */\n826,826,826,826,826,826,826,826,826,826,826,826,826,826,826,826,\n826,826,826,826,826,826,826,826,826,826,826,826,826,826,826,826,\n826,826,826,826,826,826,826,826,826,826,826,826,826,826,826,826,\n827,827,827,827,827,827,827,827,827,827,827,827,827,827,827,827,\n827,827,827,827,827,827,827,827,827,827,827,827,827,827,827,827,\n827,827,827,827,827,827,827,827,827,827,827,827,827,827,827,827,\n 65, 66,828,829,830,831,832, 65, 66, 65, 66, 65, 66,833,834,835,\n836, 70, 65, 66, 70, 65, 66, 70, 70, 70, 70, 70,696,695,837,837,\n\n/* block 86 */\n246,247,246,247,246,247,246,247,246,247,246,247,246,247,246,247,\n246,247,246,247,246,247,246,247,246,247,246,247,246,247,246,247,\n246,247,246,247,246,247,246,247,246,247,246,247,246,247,246,247,\n246,247,246,247,246,247,246,247,246,247,246,247,246,247,246,247,\n246,247,246,247,246,247,246,247,246,247,246,247,246,247,246,247,\n246,247,246,247,246,247,246,247,246,247,246,247,246,247,246,247,\n246,247,246,247,838,839,839,839,839,839,839,246,247,246,247,840,\n840,840,246,247,196,196,196,196,196,841,841,841,842,843,842,842,\n\n/* block 87 */\n844,844,844,844,844,844,844,844,844,844,844,844,844,844,844,844,\n844,844,844,844,844,844,844,844,844,844,844,844,844,844,844,844,\n844,844,844,844,844,844,196,844,196,196,196,196,196,844,196,196,\n845,845,845,845,845,845,845,845,845,845,845,845,845,845,845,845,\n845,845,845,845,845,845,845,845,845,845,845,845,845,845,845,845,\n845,845,845,845,845,845,845,845,845,845,845,845,845,845,845,845,\n845,845,845,845,845,845,845,845,196,196,196,196,196,196,196,846,\n847,196,196,196,196,196,196,196,196,196,196,196,196,196,196,848,\n\n/* block 88 */\n533,533,533,533,533,533,533,533,533,533,533,533,533,533,533,533,\n533,533,533,533,533,533,533,196,196,196,196,196,196,196,196,196,\n533,533,533,533,533,533,533,196,533,533,533,533,533,533,533,196,\n533,533,533,533,533,533,533,196,533,533,533,533,533,533,533,196,\n533,533,533,533,533,533,533,196,533,533,533,533,533,533,533,196,\n533,533,533,533,533,533,533,196,533,533,533,533,533,533,533,196,\n849,849,849,849,849,849,849,849,849,849,849,849,849,849,849,849,\n849,849,849,849,849,849,849,849,849,849,849,849,849,849,849,849,\n\n/* block 89 */\n 43, 43,850,851,850,851, 43, 43, 43,850,851, 43,850,851, 43, 43,\n 43, 43, 43, 43, 43, 43, 43,852, 43, 43,734, 43,850,851, 43, 43,\n850,851,758,759,758,759,758,759,758,759, 43, 43, 43, 43,754,853,\n854,855, 43, 43, 43, 43, 43, 43, 43, 43,734,734,856, 43, 43, 43,\n734,857,738,858, 43, 43, 43, 43, 43, 43, 43, 43,859, 43,859,859,\n 45, 45, 43,754,754,758,759,758,759,758,759,758,759,734,813,813,\n813,813,813,813,813,813,813,813,813,813,813,813,813,813,813,813,\n813,813,813,813,813,813,813,813,813,813,813,813,813,813,813,813,\n\n/* block 90 */\n860,860,860,860,860,860,860,860,860,860,860,860,860,860,860,860,\n860,860,860,860,860,860,860,860,860,860,196,860,860,860,860,860,\n860,860,860,860,860,860,860,860,860,860,860,860,860,860,860,860,\n860,860,860,860,860,860,860,860,860,860,860,860,860,860,860,860,\n860,860,860,860,860,860,860,860,860,860,860,860,860,860,860,860,\n860,860,860,860,860,860,860,860,860,860,860,860,860,860,860,860,\n860,860,860,860,860,860,860,860,860,860,860,860,860,860,860,860,\n860,860,860,860,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 91 */\n860,860,860,860,860,860,860,860,860,860,860,860,860,860,860,860,\n860,860,860,860,860,860,860,860,860,860,860,860,860,860,860,860,\n860,860,860,860,860,860,860,860,860,860,860,860,860,860,860,860,\n860,860,860,860,860,860,860,860,860,860,860,860,860,860,860,860,\n860,860,860,860,860,860,860,860,860,860,860,860,860,860,860,860,\n860,860,860,860,860,860,860,860,860,860,860,860,860,860,860,860,\n860,860,860,860,860,860,860,860,860,860,860,860,860,860,860,860,\n860,860,860,860,860,860,860,860,860,860,860,860,860,860,860,860,\n\n/* block 92 */\n860,860,860,860,860,860,860,860,860,860,860,860,860,860,860,860,\n860,860,860,860,860,860,860,860,860,860,860,860,860,860,860,860,\n860,860,860,860,860,860,860,860,860,860,860,860,860,860,860,860,\n860,860,860,860,860,860,860,860,860,860,860,860,860,860,860,860,\n860,860,860,860,860,860,860,860,860,860,860,860,860,860,860,860,\n860,860,860,860,860,860,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n861,861,862,862,861,861,861,861,861,861,861,861,861,861,863,863,\n\n/* block 93 */\n729,864,865,866,781,867,868,869,870,871,872,873,874,875,874,875,\n876,877, 45,878,876,877,876,877,876,877,876,877,879,880,881,881,\n 45,869,869,869,869,869,869,869,869,869,882,882,882,882,883,883,\n884,885,885,885,885,885,781,886,869,869,869,887,888,889,890,890,\n196,891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,\n891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,\n891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,\n891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,\n\n/* block 94 */\n891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,\n891,891,891,891,891,891,891,196,196,892,892,893,893,894,894,891,\n895,896,896,896,896,896,896,896,896,896,896,896,896,896,896,896,\n896,896,896,896,896,896,896,896,896,896,896,896,896,896,896,896,\n896,896,896,896,896,896,896,896,896,896,896,896,896,896,896,896,\n896,896,896,896,896,896,896,896,896,896,896,896,896,896,896,896,\n896,896,896,896,896,896,896,896,896,896,896,896,896,896,896,896,\n896,896,896,896,896,896,896,896,896,896,896,897,898,899,899,896,\n\n/* block 95 */\n196,196,196,196,196,900,900,900,900,900,900,900,900,900,900,900,\n900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,\n900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,\n196,901,901,901,901,901,901,901,901,901,901,901,901,901,901,901,\n901,901,901,901,901,901,901,901,901,901,901,901,901,901,901,901,\n901,901,901,901,901,901,901,901,901,901,901,901,901,901,901,901,\n901,901,901,901,902,901,901,901,901,901,901,901,901,901,901,901,\n901,901,901,901,901,901,901,901,901,901,901,901,901,901,901,901,\n\n/* block 96 */\n901,901,901,901,901,901,901,901,901,901,901,901,901,901,901,196,\n903,903,904,904,904,904,903,903,903,903,903,903,903,903,903,903,\n900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,\n900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,\n890,890,890,890,890,890,890,890,890,890,890,890,890,890,890,890,\n890,890,890,890,890,890,890,890,890,890,890,890,890,890,890,890,\n890,890,890,890,890,890,196,196,196,196,196,196,196,196,196,861,\n896,896,896,896,896,896,896,896,896,896,896,896,896,896,896,896,\n\n/* block 97 */\n905,905,905,905,905,905,905,905,905,905,905,905,905,905,905,905,\n905,905,905,905,905,905,905,905,905,905,905,905,905,906,906,196,\n904,904,904,904,904,904,904,904,904,904,903,903,903,903,903,903,\n903,903,903,903,903,903,903,903,903,903,903,903,903,903,903,903,\n903,903,903,903,903,903,903,903,907,907,907,907,907,907,907,907,\n781, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,\n905,905,905,905,905,905,905,905,905,905,905,905,905,905,905,905,\n905,905,905,905,905,905,905,905,905,905,905,905,906,906,906,510,\n\n/* block 98 */\n904,904,904,904,904,904,904,904,904,904,903,903,903,903,903,903,\n903,903,903,903,903,903,903,908,903,908,903,903,903,903,903,903,\n903,903,903,903,903,903,903,903,903,903,903,903,903,903,903,903,\n903, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,\n903,903,903,903,903,903,903,903,903,903,903,903,781,781,781,781,\n909,909,909,909,909,909,909,909,909,909,909,909,909,909,909,909,\n909,909,909,909,909,909,909,909,909,909,909,909,909,909,909,909,\n909,909,909,909,909,909,909,909,909,909,909,909,909,909,909,903,\n\n/* block 99 */\n909,909,909,909,909,909,909,909,909,909,909,909,909,909,909,909,\n909,909,909,909,909,909,909,909,909,909,909,909,909,909,909,909,\n909,909,909,909,909,909,909,909,909,909,909,909,909,909,909,909,\n909,909,909,909,909,909,909,909,909,909,909,909,909,909,909,909,\n909,909,909,909,909,909,909,909,909,909,909,909,909,909,909,909,\n909,909,909,909,909,909,909,909,903,903,903,903,903,903,903,903,\n903,903,903,903,903,903,903,903,903,903,903,903,903,903,903,903,\n903,510,510,510,510,510,510,781,781,781,781,903,903,903,903,903,\n\n/* block 100 */\n510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,\n510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,\n510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,\n510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,\n510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,\n510,510,510,510,510,510,510,510,510,510,510,510,510,510,781,781,\n903,903,903,903,903,903,903,903,903,903,903,903,903,903,903,903,\n903,903,903,903,903,903,903,903,903,903,903,903,903,903,903,781,\n\n/* block 101 */\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n\n/* block 102 */\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,\n\n/* block 103 */\n911,911,911,911,911,911,911,911,911,911,911,911,911,911,911,911,\n911,911,911,911,911,912,911,911,911,911,911,911,911,911,911,911,\n911,911,911,911,911,911,911,911,911,911,911,911,911,911,911,911,\n911,911,911,911,911,911,911,911,911,911,911,911,911,911,911,911,\n911,911,911,911,911,911,911,911,911,911,911,911,911,911,911,911,\n911,911,911,911,911,911,911,911,911,911,911,911,911,911,911,911,\n911,911,911,911,911,911,911,911,911,911,911,911,911,911,911,911,\n911,911,911,911,911,911,911,911,911,911,911,911,911,911,911,911,\n\n/* block 104 */\n911,911,911,911,911,911,911,911,911,911,911,911,911,911,911,911,\n911,911,911,911,911,911,911,911,911,911,911,911,911,911,911,911,\n911,911,911,911,911,911,911,911,911,911,911,911,911,911,911,911,\n911,911,911,911,911,911,911,911,911,911,911,911,911,911,911,911,\n911,911,911,911,911,911,911,911,911,911,911,911,911,911,911,911,\n911,911,911,911,911,911,911,911,911,911,911,911,911,911,911,911,\n911,911,911,911,911,911,911,911,911,911,911,911,911,911,911,911,\n911,911,911,911,911,911,911,911,911,911,911,911,911,911,911,911,\n\n/* block 105 */\n911,911,911,911,911,911,911,911,911,911,911,911,911,196,196,196,\n913,913,913,913,913,913,913,913,913,913,913,913,913,913,913,913,\n913,913,913,913,913,913,913,913,913,913,913,913,913,913,913,913,\n913,913,913,913,913,913,913,913,913,913,913,913,913,913,913,913,\n913,913,913,913,913,913,913,196,196,196,196,196,196,196,196,196,\n914,914,914,914,914,914,914,914,914,914,914,914,914,914,914,914,\n914,914,914,914,914,914,914,914,914,914,914,914,914,914,914,914,\n914,914,914,914,914,914,914,914,915,915,915,915,915,915,916,917,\n\n/* block 106 */\n918,918,918,918,918,918,918,918,918,918,918,918,918,918,918,918,\n918,918,918,918,918,918,918,918,918,918,918,918,918,918,918,918,\n918,918,918,918,918,918,918,918,918,918,918,918,918,918,918,918,\n918,918,918,918,918,918,918,918,918,918,918,918,918,918,918,918,\n918,918,918,918,918,918,918,918,918,918,918,918,918,918,918,918,\n918,918,918,918,918,918,918,918,918,918,918,918,918,918,918,918,\n918,918,918,918,918,918,918,918,918,918,918,918,918,918,918,918,\n918,918,918,918,918,918,918,918,918,918,918,918,918,918,918,918,\n\n/* block 107 */\n918,918,918,918,918,918,918,918,918,918,918,918,919,920,921,921,\n918,918,918,918,918,918,918,918,918,918,918,918,918,918,918,918,\n922,922,922,922,922,922,922,922,922,922,918,918,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n275,276,275,276,275,276,275,276,275,276,923,924,275,276,275,276,\n275,276,275,276,275,276,275,276,275,276,275,276,275,276,275,276,\n275,276,275,276,275,276,275,276,275,276,275,276,275,276,925,281,\n283,283,283,926,849,849,849,849,849,849,849,849,927,927,926,928,\n\n/* block 108 */\n275,276,275,276,275,276,275,276,275,276,275,276,275,276,275,276,\n275,276,275,276,275,276,275,276,275,276,275,276,929,929,849,849,\n930,930,930,930,930,930,930,930,930,930,930,930,930,930,930,930,\n930,930,930,930,930,930,930,930,930,930,930,930,930,930,930,930,\n930,930,930,930,930,930,930,930,930,930,930,930,930,930,930,930,\n930,930,930,930,930,930,930,930,930,930,930,930,930,930,930,930,\n930,930,930,930,930,930,931,931,931,931,931,931,931,931,931,931,\n932,932,933,934,935,935,935,934,196,196,196,196,196,196,196,196,\n\n/* block 109 */\n936,936,936,936,936,936,936,936, 46, 46, 46, 46, 46, 46, 46, 46,\n 46, 46, 46, 46, 46, 46, 46,151,151,151,151,151,151,151,151,151,\n 46, 46, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,\n 70, 70, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,\n 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,\n 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,\n 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,\n695, 70, 70, 70, 70, 70, 70, 70, 70, 65, 66, 65, 66,937, 65, 66,\n\n/* block 110 */\n 65, 66, 65, 66, 65, 66, 65, 66,151,938,938, 65, 66,939, 70, 93,\n 65, 66, 65, 66,940, 70, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,\n 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,941,942,943,944,941, 70,\n945,946,947,948, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,\n 65, 66, 65, 66,949,950,951, 65, 66, 65, 66,952, 65, 66,196,196,\n 65, 66,196, 70,196, 70, 65, 66, 65, 66, 65, 66,953,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,695,695,695, 65, 66, 93,149,149, 70, 93, 93, 93, 93, 93,\n\n/* block 111 */\n954,954,955,954,954,954,956,954,954,954,954,955,954,954,954,954,\n954,954,954,954,954,954,954,954,954,954,954,954,954,954,954,954,\n954,954,954,957,957,955,955,957,958,958,958,958,956,196,196,196,\n959,959,959,960,960,960,961,961,962,963,196,196,196,196,196,196,\n964,964,964,964,964,964,964,964,964,964,964,964,964,964,964,964,\n964,964,964,964,964,964,964,964,964,964,964,964,964,964,964,964,\n964,964,964,964,964,964,964,964,964,964,964,964,964,964,964,964,\n964,964,964,964,965,965,966,966,196,196,196,196,196,196,196,196,\n\n/* block 112 */\n967,967,968,968,968,968,968,968,968,968,968,968,968,968,968,968,\n968,968,968,968,968,968,968,968,968,968,968,968,968,968,968,968,\n968,968,968,968,968,968,968,968,968,968,968,968,968,968,968,968,\n968,968,968,968,967,967,967,967,967,967,967,967,967,967,967,967,\n967,967,967,967,969,970,196,196,196,196,196,196,196,196,971,971,\n972,972,972,972,972,972,972,972,972,972,196,196,196,196,196,196,\n378,378,378,378,378,378,378,378,378,378,378,378,378,378,378,378,\n378,973,376,974,376,376,376,376,385,385,385,376,385,376,376,374,\n\n/* block 113 */\n975,975,975,975,975,975,975,975,975,975,976,976,976,976,976,976,\n976,976,976,976,976,976,976,976,976,976,976,976,976,976,976,976,\n976,976,976,976,976,976,977,977,977,977,977,978,978,978,979,980,\n981,981,981,981,981,981,981,981,981,981,981,981,981,981,981,981,\n981,981,981,981,981,981,981,982,982,982,982,982,982,982,982,982,\n982,982,983,984,196,196,196,196,196,196,196,196,196,196,196,985,\n528,528,528,528,528,528,528,528,528,528,528,528,528,528,528,528,\n528,528,528,528,528,528,528,528,528,528,528,528,528,196,196,196,\n\n/* block 114 */\n986,986,986,987,988,988,988,988,988,988,988,988,988,988,988,988,\n988,988,988,988,988,988,988,988,988,988,988,988,988,988,988,988,\n988,988,988,988,988,988,988,988,988,988,988,988,988,988,988,988,\n988,988,988,989,987,987,986,986,986,986,987,987,986,986,987,987,\n990,991,991,991,991,991,991,992,993,993,991,991,991,991,196,994,\n995,995,995,995,995,995,995,995,995,995,196,196,196,196,991,991,\n511,511,511,511,511,521,996,511,511,511,511,511,511,511,511,511,\n522,522,522,522,522,522,522,522,522,522,511,511,511,511,511,196,\n\n/* block 115 */\n997,997,997,997,997,997,997,997,997,997,997,997,997,997,997,997,\n997,997,997,997,997,997,997,997,997,997,997,997,997,997,997,997,\n997,997,997,997,997,997,997,997,997,998,998,998,998,998,998,999,\n999,998,998,999,999,998,998,196,196,196,196,196,196,196,196,196,\n997,997,997,998,997,997,997,997,997,997,997,997,998,999,196,196,\n1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,196,196,1001,1002,1002,1002,\n511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,\n996,511,511,511,511,511,511,523,523,523,511,520,521,520,511,511,\n\n/* block 116 */\n1003,1003,1003,1003,1003,1003,1003,1003,1003,1003,1003,1003,1003,1003,1003,1003,\n1003,1003,1003,1003,1003,1003,1003,1003,1003,1003,1003,1003,1003,1003,1003,1003,\n1003,1003,1003,1003,1003,1003,1003,1003,1003,1003,1003,1003,1003,1003,1003,1003,\n1004,1003,1004,1004,1004,1005,1005,1004,1004,1005,1003,1005,1005,1003,1004,1006,\n1007,1006,1007,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,1003,1003,1008,1009,1010,\n1011,1011,1011,1011,1011,1011,1011,1011,1011,1011,1011,1012,1013,1013,1012,1012,\n1014,1014,1011,1015,1015,1012,1016,196,196,196,196,196,196,196,196,196,\n\n/* block 117 */\n196,533,533,533,533,533,533,196,196,533,533,533,533,533,533,196,\n196,533,533,533,533,533,533,196,196,196,196,196,196,196,196,196,\n533,533,533,533,533,533,533,196,533,533,533,533,533,533,533,196,\n 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70,\n 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70,\n 70, 70, 70,1017, 70, 70, 70, 70, 70, 70, 70,938,149,149,149,149,\n 70, 70, 70, 70, 70,256, 70, 70, 70,149, 46, 46,196,196,196,196,\n1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,\n\n/* block 118 */\n1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,\n1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,\n1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,\n1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,1018,\n1011,1011,1011,1011,1011,1011,1011,1011,1011,1011,1011,1011,1011,1011,1011,1011,\n1011,1011,1011,1011,1011,1011,1011,1011,1011,1011,1011,1011,1011,1011,1011,1011,\n1011,1011,1011,1012,1012,1013,1012,1012,1013,1012,1012,1014,1019,1016,196,196,\n1020,1020,1020,1020,1020,1020,1020,1020,1020,1020,196,196,196,196,196,196,\n\n/* block 119 */\n1021,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,\n1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1021,1022,1022,1022,\n1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,\n1022,1022,1022,1022,1022,1022,1022,1022,1021,1022,1022,1022,1022,1022,1022,1022,\n1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,\n1022,1022,1022,1022,1021,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,\n1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,\n1021,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,\n\n/* block 120 */\n1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1021,1022,1022,1022,\n1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,\n1022,1022,1022,1022,1022,1022,1022,1022,1021,1022,1022,1022,1022,1022,1022,1022,\n1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,\n1022,1022,1022,1022,1021,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,\n1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,\n1021,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,\n1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1021,1022,1022,1022,\n\n/* block 121 */\n1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,\n1022,1022,1022,1022,1022,1022,1022,1022,1021,1022,1022,1022,1022,1022,1022,1022,\n1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,\n1022,1022,1022,1022,1021,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,\n1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,\n1021,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,\n1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1021,1022,1022,1022,\n1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,\n\n/* block 122 */\n1022,1022,1022,1022,1022,1022,1022,1022,1021,1022,1022,1022,1022,1022,1022,1022,\n1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,\n1022,1022,1022,1022,1021,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,\n1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,\n1021,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,\n1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1021,1022,1022,1022,\n1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,\n1022,1022,1022,1022,1022,1022,1022,1022,1021,1022,1022,1022,1022,1022,1022,1022,\n\n/* block 123 */\n1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,\n1022,1022,1022,1022,1021,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,\n1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,\n1021,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,\n1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1021,1022,1022,1022,\n1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,\n1022,1022,1022,1022,1022,1022,1022,1022,1021,1022,1022,1022,1022,1022,1022,1022,\n1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,\n\n/* block 124 */\n1022,1022,1022,1022,1021,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,\n1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,\n1021,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,\n1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1021,1022,1022,1022,\n1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,\n1022,1022,1022,1022,1022,1022,1022,1022,1021,1022,1022,1022,1022,1022,1022,1022,\n1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,\n1022,1022,1022,1022,1021,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,\n\n/* block 125 */\n1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,\n1021,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,\n1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1021,1022,1022,1022,\n1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,\n1022,1022,1022,1022,1022,1022,1022,1022,1021,1022,1022,1022,1022,1022,1022,1022,\n1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,\n1022,1022,1022,1022,1021,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,\n1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,\n\n/* block 126 */\n1022,1022,1022,1022,1022,1022,1022,1022,1021,1022,1022,1022,1022,1022,1022,1022,\n1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,\n1022,1022,1022,1022,196,196,196,196,196,196,196,196,196,196,196,196,\n531,531,531,531,531,531,531,531,531,531,531,531,531,531,531,531,\n531,531,531,531,531,531,531,196,196,196,196,532,532,532,532,532,\n532,532,532,532,532,532,532,532,532,532,532,532,532,532,532,532,\n532,532,532,532,532,532,532,532,532,532,532,532,532,532,532,532,\n532,532,532,532,532,532,532,532,532,532,532,532,196,196,196,196,\n\n/* block 127 */\n1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,\n1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,\n1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,\n1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,\n1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,\n1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,\n1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,\n1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,\n\n/* block 128 */\n1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,\n1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,\n1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,\n1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,\n1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,\n1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,\n1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,\n1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,\n\n/* block 129 */\n1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,\n1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,\n1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,\n1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,\n1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,\n1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,\n1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,\n1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,\n\n/* block 130 */\n1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,910,910,\n1025,910,1025,910,910,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,910,\n1025,910,1025,910,910,1025,1025,910,910,910,1025,1025,1025,1025,1025,1025,\n1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,\n1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,\n1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,\n1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,196,196,\n1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,\n\n/* block 131 */\n1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,\n1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,\n1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,\n1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,\n1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,\n1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 132 */\n703,703,703,703,703,1026,1027,196,196,196,196,196,196,196,196,196,\n196,196,196,292,292,292,292,292,196,196,196,196,196,305,300,305,\n305,305,305,305,305,305,305,305,305,1028,305,305,305,305,305,305,\n305,305,305,305,305,305,305,297,305,305,305,305,305,297,305,297,\n305,305,297,305,305,297,305,305,305,305,305,305,305,305,305,305,\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,\n\n/* block 133 */\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,\n321,321,370,370,370,370,370,370,370,370,370,370,370,370,370,370,\n370,370,370,340,340,340,340,340,340,340,340,340,340,340,340,340,\n340,340,340,321,321,321,321,321,321,321,321,321,321,321,321,321,\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,\n\n/* block 134 */\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,1029,1029,\n1029,1029,1029,1029,321,321,321,321,321,321,321,321,321,321,321,321,\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,\n\n/* block 135 */\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,\n\n/* block 136 */\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,1030,1031,\n315,315,315,315,315,315,315,315,315,315,315,315,315,315,315,315,\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,\n\n/* block 137 */\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,\n340,340,321,321,321,321,321,321,321,321,321,321,321,321,321,321,\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,\n321,321,321,321,321,321,321,321,340,340,340,340,340,340,340,315,\n1032,1032,1032,1032,1032,1032,1032,1032,1032,1032,1032,1032,1032,1032,1032,1032,\n1032,1032,1032,1032,1032,1032,1032,1032,1032,1032,1032,1032,1032,1032,1032,1032,\n321,321,1033,321,321,321,321,321,321,321,1029,1029,312,1034,315,315,\n\n/* block 138 */\n1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1036,\n1037,1037,1038,1039,1037,1038,1038,1040,1041,1037,196,196,196,196,196,196,\n176,176,176,176,176,176,176,176,176,176,176,176,176,176,927,927,\n1037,1042,1042,755,755,1040,1041,1040,1041,1040,1041,1040,1041,1040,1041,1040,\n1041,1043,1044,1043,1044,866,866,1040,1041,1037,1037,1037,1037,755,755,755,\n1045,199,1046,196,199,1047,1038,1038,1042,1048,1049,1048,1049,1048,1049,1050,\n1037,1051,1052,1053,1054,1054,794,196,1051,479,1050,1037,196,196,196,196,\n1029,321,1029,321,1029,340,1029,321,1029,321,1029,321,1029,321,1029,321,\n\n/* block 139 */\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,\n321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,\n321,321,321,321,321,321,321,321,321,321,321,321,321,340,340, 51,\n\n/* block 140 */\n196,1038,1055,1050,479,1050,1037,1056,1048,1049,1037,1052,1045,1057,1046,1058,\n1059,1059,1059,1059,1059,1059,1059,1059,1059,1059,1047,199,1054,794,1054,1038,\n1037,1060,1060,1060,1060,1060,1060, 59, 59, 59, 59, 59, 59, 59, 59, 59,\n 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,1048,1051,1049,1061,755,\n 46,1062,1062,1062,1062,1062,1062, 62, 62, 62, 62, 62, 62, 62, 62, 62,\n 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62,1048,794,1049,794,1048,\n1049,1063,1064,1065,1066,897,896,896,896,896,896,896,896,896,896,896,\n898,896,896,896,896,896,896,896,896,896,896,896,896,896,896,896,\n\n/* block 141 */\n896,896,896,896,896,896,896,896,896,896,896,896,896,896,896,896,\n896,896,896,896,896,896,896,896,896,896,896,896,896,896,1067,1067,\n902,901,901,901,901,901,901,901,901,901,901,901,901,901,901,901,\n901,901,901,901,901,901,901,901,901,901,901,901,901,901,901,196,\n196,196,901,901,901,901,901,901,196,196,901,901,901,901,901,901,\n196,196,901,901,901,901,901,901,196,196,901,901,901,196,196,196,\n479,479,794, 46,781,479,479,196,781,794,794,794,794,781,781,196,\n765,765,765,765,765,765,765,765,765,1068,1068,1068,781,781,1032,1032,\n\n/* block 142 */\n1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,196,1069,1069,1069,\n1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,\n1069,1069,1069,1069,1069,1069,1069,196,1069,1069,1069,1069,1069,1069,1069,1069,\n1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,196,1069,1069,196,1069,\n1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,196,196,\n1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 143 */\n1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,\n1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,\n1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,\n1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,\n1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,\n1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,\n1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,\n1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,1069,196,196,196,196,196,\n\n/* block 144 */\n1070,1071,1072,196,196,196,196,1073,1073,1073,1073,1073,1073,1073,1073,1073,\n1073,1073,1073,1073,1073,1073,1073,1073,1073,1073,1073,1073,1073,1073,1073,1073,\n1073,1073,1073,1073,1073,1073,1073,1073,1073,1073,1073,1073,1073,1073,1073,1073,\n1073,1073,1073,1073,196,196,196,1074,1074,1074,1074,1074,1074,1074,1074,1074,\n1075,1075,1075,1075,1075,1075,1075,1075,1075,1075,1075,1075,1075,1075,1075,1075,\n1075,1075,1075,1075,1075,1075,1075,1075,1075,1075,1075,1075,1075,1075,1075,1075,\n1075,1075,1075,1075,1075,1075,1075,1075,1075,1075,1075,1075,1075,1075,1075,1075,\n1075,1075,1075,1075,1075,1076,1076,1076,1076,1077,1077,1077,1077,1077,1077,1077,\n\n/* block 145 */\n1077,1077,1077,1077,1077,1077,1077,1077,1077,1077,1076,1076,1077,1078,1078,196,\n781,781,781,781,781,781,781,781,781,781,781,781,781,196,196,196,\n1077,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,\n510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,\n510,510,510,510,510,510,510,510,510,510,510,510,510,189,196,196,\n\n/* block 146 */\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 147 */\n1079,1079,1079,1079,1079,1079,1079,1079,1079,1079,1079,1079,1079,1079,1079,1079,\n1079,1079,1079,1079,1079,1079,1079,1079,1079,1079,1079,1079,1079,196,196,196,\n1080,1080,1080,1080,1080,1080,1080,1080,1080,1080,1080,1080,1080,1080,1080,1080,\n1080,1080,1080,1080,1080,1080,1080,1080,1080,1080,1080,1080,1080,1080,1080,1080,\n1080,1080,1080,1080,1080,1080,1080,1080,1080,1080,1080,1080,1080,1080,1080,1080,\n1080,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n1081,1082,1082,1082,1082,1082,1082,1082,1082,1082,1082,1082,1082,1082,1082,1082,\n1082,1082,1082,1082,1082,1082,1082,1082,1082,1082,1082,1082,196,196,196,196,\n\n/* block 148 */\n1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,\n1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,\n1084,1084,1084,1084,196,196,196,196,196,196,196,196,196,1083,1083,1083,\n1085,1085,1085,1085,1085,1085,1085,1085,1085,1085,1085,1085,1085,1085,1085,1085,\n1085,1086,1085,1085,1085,1085,1085,1085,1085,1085,1086,196,196,196,196,196,\n1087,1087,1087,1087,1087,1087,1087,1087,1087,1087,1087,1087,1087,1087,1087,1087,\n1087,1087,1087,1087,1087,1087,1087,1087,1087,1087,1087,1087,1087,1087,1087,1087,\n1087,1087,1087,1087,1087,1087,1088,1088,1088,1088,1088,196,196,196,196,196,\n\n/* block 149 */\n1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,\n1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,196,1090,\n1091,1091,1091,1091,1091,1091,1091,1091,1091,1091,1091,1091,1091,1091,1091,1091,\n1091,1091,1091,1091,1091,1091,1091,1091,1091,1091,1091,1091,1091,1091,1091,1091,\n1091,1091,1091,1091,196,196,196,196,1091,1091,1091,1091,1091,1091,1091,1091,\n1092,1093,1093,1093,1093,1093,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 150 */\n1094,1094,1094,1094,1094,1094,1094,1094,1094,1094,1094,1094,1094,1094,1094,1094,\n1094,1094,1094,1094,1094,1094,1094,1094,1094,1094,1094,1094,1094,1094,1094,1094,\n1094,1094,1094,1094,1094,1094,1094,1094,1095,1095,1095,1095,1095,1095,1095,1095,\n1095,1095,1095,1095,1095,1095,1095,1095,1095,1095,1095,1095,1095,1095,1095,1095,\n1095,1095,1095,1095,1095,1095,1095,1095,1095,1095,1095,1095,1095,1095,1095,1095,\n1096,1096,1096,1096,1096,1096,1096,1096,1096,1096,1096,1096,1096,1096,1096,1096,\n1096,1096,1096,1096,1096,1096,1096,1096,1096,1096,1096,1096,1096,1096,1096,1096,\n1096,1096,1096,1096,1096,1096,1096,1096,1096,1096,1096,1096,1096,1096,1096,1096,\n\n/* block 151 */\n1097,1097,1097,1097,1097,1097,1097,1097,1097,1097,1097,1097,1097,1097,1097,1097,\n1097,1097,1097,1097,1097,1097,1097,1097,1097,1097,1097,1097,1097,1097,196,196,\n1098,1098,1098,1098,1098,1098,1098,1098,1098,1098,196,196,196,196,196,196,\n1099,1099,1099,1099,1099,1099,1099,1099,1099,1099,1099,1099,1099,1099,1099,1099,\n1099,1099,1099,1099,1099,1099,1099,1099,1099,1099,1099,1099,1099,1099,1099,1099,\n1099,1099,1099,1099,196,196,196,196,1100,1100,1100,1100,1100,1100,1100,1100,\n1100,1100,1100,1100,1100,1100,1100,1100,1100,1100,1100,1100,1100,1100,1100,1100,\n1100,1100,1100,1100,1100,1100,1100,1100,1100,1100,1100,1100,196,196,196,196,\n\n/* block 152 */\n1101,1101,1101,1101,1101,1101,1101,1101,1101,1101,1101,1101,1101,1101,1101,1101,\n1101,1101,1101,1101,1101,1101,1101,1101,1101,1101,1101,1101,1101,1101,1101,1101,\n1101,1101,1101,1101,1101,1101,1101,1101,196,196,196,196,196,196,196,196,\n1102,1102,1102,1102,1102,1102,1102,1102,1102,1102,1102,1102,1102,1102,1102,1102,\n1102,1102,1102,1102,1102,1102,1102,1102,1102,1102,1102,1102,1102,1102,1102,1102,\n1102,1102,1102,1102,1102,1102,1102,1102,1102,1102,1102,1102,1102,1102,1102,1102,\n1102,1102,1102,1102,196,196,196,196,196,196,196,196,196,196,196,1103,\n1104,1104,1104,1104,1104,1104,1104,1104,1104,1104,1104,196,1104,1104,1104,1104,\n\n/* block 153 */\n1104,1104,1104,1104,1104,1104,1104,1104,1104,1104,1104,196,1104,1104,1104,1104,\n1104,1104,1104,196,1104,1104,196,1105,1105,1105,1105,1105,1105,1105,1105,1105,\n1105,1105,196,1105,1105,1105,1105,1105,1105,1105,1105,1105,1105,1105,1105,1105,\n1105,1105,196,1105,1105,1105,1105,1105,1105,1105,196,1105,1105,196,196,196,\n1106,1106,1106,1106,1106,1106,1106,1106,1106,1106,1106,1106,1106,1106,1106,1106,\n1106,1106,1106,1106,1106,1106,1106,1106,1106,1106,1106,1106,1106,1106,1106,1106,\n1106,1106,1106,1106,1106,1106,1106,1106,1106,1106,1106,1106,1106,1106,1106,1106,\n1106,1106,1106,1106,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 154 */\n1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,\n1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,\n1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,\n1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,\n1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,\n1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,\n1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,\n1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,\n\n/* block 155 */\n1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,\n1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,\n1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,\n1107,1107,1107,1107,1107,1107,1107,196,196,196,196,196,196,196,196,196,\n1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,\n1107,1107,1107,1107,1107,1107,196,196,196,196,196,196,196,196,196,196,\n1107,1107,1107,1107,1107,1107,1107,1107,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 156 */\n149,1108,1108,149,149,149,196,149,149,149,149,149,149,149,149,149,\n149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,\n149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,\n149,196,149,149,149,149,149,149,149,149,149,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 157 */\n1109,1109,1109,1109,1109,1109,297,297,1109,297,1109,1109,1109,1109,1109,1109,\n1109,1109,1109,1109,1109,1109,1109,1109,1109,1109,1109,1109,1109,1109,1109,1109,\n1109,1109,1109,1109,1109,1109,1109,1109,1109,1109,1109,1109,1109,1109,1109,1109,\n1109,1109,1109,1109,1109,1109,297,1109,1109,297,297,297,1109,297,297,1109,\n1110,1110,1110,1110,1110,1110,1110,1110,1110,1110,1110,1110,1110,1110,1110,1110,\n1110,1110,1110,1110,1110,1110,297,1111,1112,1112,1112,1112,1112,1112,1112,1112,\n1113,1113,1113,1113,1113,1113,1113,1113,1113,1113,1113,1113,1113,1113,1113,1113,\n1113,1113,1113,1113,1113,1113,1113,1114,1114,1115,1115,1115,1115,1115,1115,1115,\n\n/* block 158 */\n1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,\n1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,297,\n297,297,297,297,297,297,297,1117,1117,1117,1117,1117,1117,1117,1117,1117,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n1118,1118,1118,1118,1118,1118,1118,1118,1118,1118,1118,1118,1118,1118,1118,1118,\n1118,1118,1118,297,1118,1118,297,297,297,297,297,1119,1119,1119,1119,1119,\n\n/* block 159 */\n1120,1120,1120,1120,1120,1120,1120,1120,1120,1120,1120,1120,1120,1120,1120,1120,\n1120,1120,1120,1120,1120,1120,1121,1121,1121,1121,1121,1121,297,297,297,1122,\n1123,1123,1123,1123,1123,1123,1123,1123,1123,1123,1123,1123,1123,1123,1123,1123,\n1123,1123,1123,1123,1123,1123,1123,1123,1123,1123,297,297,297,297,297,1124,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n\n/* block 160 */\n1125,1125,1125,1125,1125,1125,1125,1125,1125,1125,1125,1125,1125,1125,1125,1125,\n1125,1125,1125,1125,1125,1125,1125,1125,1125,1125,1125,1125,1125,1125,1125,1125,\n1126,1126,1126,1126,1126,1126,1126,1126,1126,1126,1126,1126,1126,1126,1126,1126,\n1126,1126,1126,1126,1126,1126,1126,1126,297,297,297,297,1127,1127,1126,1126,\n1127,1127,1127,1127,1127,1127,1127,1127,1127,1127,1127,1127,1127,1127,1127,1127,\n297,297,1127,1127,1127,1127,1127,1127,1127,1127,1127,1127,1127,1127,1127,1127,\n1127,1127,1127,1127,1127,1127,1127,1127,1127,1127,1127,1127,1127,1127,1127,1127,\n1127,1127,1127,1127,1127,1127,1127,1127,1127,1127,1127,1127,1127,1127,1127,1127,\n\n/* block 161 */\n1128,1129,1129,1129,297,1129,1129,297,297,297,297,297,1129,1129,1129,1129,\n1128,1128,1128,1128,297,1128,1128,1128,297,1128,1128,1128,1128,1128,1128,1128,\n1128,1128,1128,1128,1128,1128,1128,1128,1128,1128,1128,1128,1128,1128,1128,1128,\n1128,1128,1128,1128,1128,1128,297,297,1130,1130,1130,297,297,297,297,1131,\n1132,1132,1132,1132,1132,1132,1132,1132,1132,297,297,297,297,297,297,297,\n1133,1133,1133,1133,1133,1133,1134,1134,1133,297,297,297,297,297,297,297,\n1135,1135,1135,1135,1135,1135,1135,1135,1135,1135,1135,1135,1135,1135,1135,1135,\n1135,1135,1135,1135,1135,1135,1135,1135,1135,1135,1135,1135,1135,1136,1136,1137,\n\n/* block 162 */\n1138,1138,1138,1138,1138,1138,1138,1138,1138,1138,1138,1138,1138,1138,1138,1138,\n1138,1138,1138,1138,1138,1138,1138,1138,1138,1138,1138,1138,1138,1139,1139,1139,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n1140,1140,1140,1140,1140,1140,1140,1140,1141,1140,1140,1140,1140,1140,1140,1140,\n1140,1140,1140,1140,1140,1140,1140,1140,1140,1140,1140,1140,1140,1140,1140,1140,\n1140,1140,1140,1140,1140,1142,1142,297,297,297,297,1143,1143,1143,1143,1143,\n1144,1144,1145,1144,1144,1144,1146,297,297,297,297,297,297,297,297,297,\n\n/* block 163 */\n1147,1147,1147,1147,1147,1147,1147,1147,1147,1147,1147,1147,1147,1147,1147,1147,\n1147,1147,1147,1147,1147,1147,1147,1147,1147,1147,1147,1147,1147,1147,1147,1147,\n1147,1147,1147,1147,1147,1147,1147,1147,1147,1147,1147,1147,1147,1147,1147,1147,\n1147,1147,1147,1147,1147,1147,297,297,297,1148,1149,1149,1149,1149,1149,1149,\n1150,1150,1150,1150,1150,1150,1150,1150,1150,1150,1150,1150,1150,1150,1150,1150,\n1150,1150,1150,1150,1150,1150,297,297,1151,1151,1151,1151,1151,1151,1151,1151,\n1152,1152,1152,1152,1152,1152,1152,1152,1152,1152,1152,1152,1152,1152,1152,1152,\n1152,1152,1152,297,297,297,297,297,1153,1153,1153,1153,1153,1153,1153,1153,\n\n/* block 164 */\n1154,1154,1154,1154,1154,1154,1154,1154,1154,1154,1154,1154,1154,1154,1154,1154,\n1154,1154,297,297,297,297,297,297,297,1155,1155,1155,1155,297,297,297,\n297,297,297,297,297,297,297,297,297,1156,1156,1156,1156,1156,1156,1156,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n\n/* block 165 */\n1157,1157,1157,1157,1157,1157,1157,1157,1157,1157,1157,1157,1157,1157,1157,1157,\n1157,1157,1157,1157,1157,1157,1157,1157,1157,1157,1157,1157,1157,1157,1157,1157,\n1157,1157,1157,1157,1157,1157,1157,1157,1157,1157,1157,1157,1157,1157,1157,1157,\n1157,1157,1157,1157,1157,1157,1157,1157,1157,1157,1157,1157,1157,1157,1157,1157,\n1157,1157,1157,1157,1157,1157,1157,1157,1157,297,297,297,297,297,297,297,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n\n/* block 166 */\n1158,1158,1158,1158,1158,1158,1158,1158,1158,1158,1158,1158,1158,1158,1158,1158,\n1158,1158,1158,1158,1158,1158,1158,1158,1158,1158,1158,1158,1158,1158,1158,1158,\n1158,1158,1158,1158,1158,1158,1158,1158,1158,1158,1158,1158,1158,1158,1158,1158,\n1158,1158,1158,297,297,297,297,297,297,297,297,297,297,297,297,297,\n1159,1159,1159,1159,1159,1159,1159,1159,1159,1159,1159,1159,1159,1159,1159,1159,\n1159,1159,1159,1159,1159,1159,1159,1159,1159,1159,1159,1159,1159,1159,1159,1159,\n1159,1159,1159,1159,1159,1159,1159,1159,1159,1159,1159,1159,1159,1159,1159,1159,\n1159,1159,1159,297,297,297,297,297,297,297,1160,1160,1160,1160,1160,1160,\n\n/* block 167 */\n1161,1161,1161,1161,1161,1161,1161,1161,1161,1161,1161,1161,1161,1161,1161,1161,\n1161,1161,1161,1161,1161,1161,1161,1161,1161,1161,1161,1161,1161,1161,1161,1161,\n1161,1161,1162,1162,1163,1163,1163,1163,340,340,340,340,340,340,340,340,\n1164,1164,1164,1164,1164,1164,1164,1164,1164,1164,340,340,340,340,340,340,\n1165,1165,1165,1165,1165,1165,1165,1165,1165,1165,1166,1166,1166,1166,1167,1166,\n1168,1168,1168,1168,1168,1168,1168,1168,1168,1168,1168,1168,1168,1168,1168,1168,\n1168,1168,1168,1168,1168,1168,297,297,297,1169,1170,1171,1171,1171,1172,1173,\n1174,1174,1174,1174,1174,1174,1174,1174,1174,1174,1174,1174,1174,1174,1174,1174,\n\n/* block 168 */\n1174,1174,1174,1174,1174,1174,297,297,297,297,297,297,297,297,1175,1175,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n\n/* block 169 */\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n1176,1176,1176,1176,1176,1176,1176,1176,1176,1176,1176,1176,1176,1176,1176,1176,\n1176,1176,1176,1176,1176,1176,1176,1176,1176,1176,1176,1176,1176,1176,1176,297,\n\n/* block 170 */\n1177,1177,1177,1177,1177,1177,1177,1177,1177,1177,1177,1177,1177,1177,1177,1177,\n1177,1177,1177,1177,1177,1177,1177,1177,1177,1177,1177,1177,1177,1177,1177,1177,\n1177,1177,1177,1177,1177,1177,1177,1177,1177,1177,297,1178,1178,1179,297,297,\n1177,1177,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n340,340,321,321,321,340,340,340,340,340,340,340,340,340,340,340,\n340,340,340,340,340,340,340,340,340,340,340,340,340,340,340,340,\n340,340,340,340,340,340,340,340,340,340,340,340,340,340,340,340,\n340,340,340,340,340,340,340,340,340,340,340,340,316,333,333,333,\n\n/* block 171 */\n1180,1180,1180,1180,1180,1180,1180,1180,1180,1180,1180,1180,1180,1180,1180,1180,\n1180,1180,1180,1180,1180,1180,1180,1180,1180,1180,1180,1180,1180,1181,1181,1181,\n1181,1181,1181,1181,1181,1181,1181,1180,297,297,297,297,297,297,297,297,\n1182,1182,1182,1182,1182,1182,1182,1182,1182,1182,1182,1182,1182,1182,1182,1182,\n1182,1182,1182,1182,1182,1182,1183,1183,1183,1183,1183,1183,1183,1183,1183,1183,\n1183,1184,1184,1184,1184,1185,1185,1185,1185,1185,340,340,340,340,340,340,\n340,340,340,340,340,340,340,340,340,340,340,340,340,340,340,340,\n1186,1186,1186,1186,1186,1186,1186,1186,1186,1186,1186,1186,1186,1186,1186,1186,\n\n/* block 172 */\n1186,1186,1187,1187,1187,1187,1188,1188,1188,1188,297,297,297,297,297,297,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n1189,1189,1189,1189,1189,1189,1189,1189,1189,1189,1189,1189,1189,1189,1189,1189,\n1189,1189,1189,1189,1189,1190,1190,1190,1190,1190,1190,1190,297,297,297,297,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n1191,1191,1191,1191,1191,1191,1191,1191,1191,1191,1191,1191,1191,1191,1191,1191,\n1191,1191,1191,1191,1191,1191,1191,297,297,297,297,297,297,297,297,297,\n\n/* block 173 */\n1192,1193,1192,1194,1194,1194,1194,1194,1194,1194,1194,1194,1194,1194,1194,1194,\n1194,1194,1194,1194,1194,1194,1194,1194,1194,1194,1194,1194,1194,1194,1194,1194,\n1194,1194,1194,1194,1194,1194,1194,1194,1194,1194,1194,1194,1194,1194,1194,1194,\n1194,1194,1194,1194,1194,1194,1194,1194,1193,1193,1193,1193,1193,1193,1193,1193,\n1193,1193,1193,1193,1193,1193,1195,1196,1196,1197,1197,1197,1197,1197,196,196,\n196,196,1198,1198,1198,1198,1198,1198,1198,1198,1198,1198,1198,1198,1198,1198,\n1198,1198,1198,1198,1198,1198,1199,1199,1199,1199,1199,1199,1199,1199,1199,1199,\n1195,1194,1194,1193,1193,1194,196,196,196,196,196,196,196,196,196,1200,\n\n/* block 174 */\n1201,1201,1202,1203,1203,1203,1203,1203,1203,1203,1203,1203,1203,1203,1203,1203,\n1203,1203,1203,1203,1203,1203,1203,1203,1203,1203,1203,1203,1203,1203,1203,1203,\n1203,1203,1203,1203,1203,1203,1203,1203,1203,1203,1203,1203,1203,1203,1203,1203,\n1202,1202,1202,1201,1201,1201,1201,1202,1202,1204,1205,1206,1206,1207,1208,1208,\n1208,1208,1201,196,196,196,196,196,196,196,196,196,196,1207,196,196,\n1209,1209,1209,1209,1209,1209,1209,1209,1209,1209,1209,1209,1209,1209,1209,1209,\n1209,1209,1209,1209,1209,1209,1209,1209,1209,196,196,196,196,196,196,196,\n1210,1210,1210,1210,1210,1210,1210,1210,1210,1210,196,196,196,196,196,196,\n\n/* block 175 */\n1211,1211,1211,1212,1212,1212,1212,1212,1212,1212,1212,1212,1212,1212,1212,1212,\n1212,1212,1212,1212,1212,1212,1212,1212,1212,1212,1212,1212,1212,1212,1212,1212,\n1212,1212,1212,1212,1212,1212,1212,1211,1211,1211,1211,1211,1213,1211,1211,1211,\n1211,1211,1211,1214,1214,196,1215,1215,1215,1215,1215,1215,1215,1215,1215,1215,\n1216,1217,1217,1217,1212,1213,1213,1212,196,196,196,196,196,196,196,196,\n1218,1218,1218,1218,1218,1218,1218,1218,1218,1218,1218,1218,1218,1218,1218,1218,\n1218,1218,1218,1218,1218,1218,1218,1218,1218,1218,1218,1218,1218,1218,1218,1218,\n1218,1218,1218,1219,1220,1220,1218,196,196,196,196,196,196,196,196,196,\n\n/* block 176 */\n1221,1221,1222,1223,1223,1223,1223,1223,1223,1223,1223,1223,1223,1223,1223,1223,\n1223,1223,1223,1223,1223,1223,1223,1223,1223,1223,1223,1223,1223,1223,1223,1223,\n1223,1223,1223,1223,1223,1223,1223,1223,1223,1223,1223,1223,1223,1223,1223,1223,\n1223,1223,1223,1222,1222,1222,1221,1221,1221,1221,1221,1221,1221,1221,1221,1222,\n1224,1223,1225,1225,1223,1226,1226,1227,1227,1228,1229,1229,1229,1226,1222,1221,\n1230,1230,1230,1230,1230,1230,1230,1230,1230,1230,1223,1227,1223,1227,1226,1226,\n196,1231,1231,1231,1231,1231,1231,1231,1231,1231,1231,1231,1231,1231,1231,1231,\n1231,1231,1231,1231,1231,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 177 */\n1232,1232,1232,1232,1232,1232,1232,1232,1232,1232,1232,1232,1232,1232,1232,1232,\n1232,1232,196,1232,1232,1232,1232,1232,1232,1232,1232,1232,1232,1232,1232,1232,\n1232,1232,1232,1232,1232,1232,1232,1232,1232,1232,1232,1232,1233,1233,1233,1234,\n1234,1234,1233,1233,1234,1235,1236,1237,1238,1238,1239,1238,1238,1240,1234,1232,\n1232,1234,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 178 */\n1241,1241,1241,1241,1241,1241,1241,196,1241,196,1241,1241,1241,1241,196,1241,\n1241,1241,1241,1241,1241,1241,1241,1241,1241,1241,1241,1241,1241,1241,196,1241,\n1241,1241,1241,1241,1241,1241,1241,1241,1241,1242,196,196,196,196,196,196,\n1243,1243,1243,1243,1243,1243,1243,1243,1243,1243,1243,1243,1243,1243,1243,1243,\n1243,1243,1243,1243,1243,1243,1243,1243,1243,1243,1243,1243,1243,1243,1243,1243,\n1243,1243,1243,1243,1243,1243,1243,1243,1243,1243,1243,1243,1243,1243,1243,1244,\n1245,1245,1245,1244,1244,1244,1244,1244,1244,1246,1247,196,196,196,196,196,\n1248,1248,1248,1248,1248,1248,1248,1248,1248,1248,196,196,196,196,196,196,\n\n/* block 179 */\n1249,1250,1251,1252,196,1253,1253,1253,1253,1253,1253,1253,1253,196,196,1253,\n1253,196,196,1253,1253,1253,1253,1253,1253,1253,1253,1253,1253,1253,1253,1253,\n1253,1253,1253,1253,1253,1253,1253,1253,1253,196,1253,1253,1253,1253,1253,1253,\n1253,196,1253,1253,196,1253,1253,1253,1253,1253,196,1254,1255,1253,1256,1251,\n1249,1251,1251,1251,1251,196,196,1251,1251,196,196,1251,1251,1257,196,196,\n1253,196,196,196,196,196,196,1256,196,196,196,196,196,1258,1253,1253,\n1253,1253,1251,1251,196,196,1259,1259,1259,1259,1259,1259,1259,196,196,196,\n1259,1259,1259,1259,1259,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 180 */\n1260,1260,1260,1260,1260,1260,1260,1260,1260,1260,196,1260,196,196,1260,196,\n1260,1260,1260,1260,1260,1260,1260,1260,1260,1260,1260,1260,1260,1260,1260,1260,\n1260,1260,1260,1260,1260,1260,1260,1260,1260,1260,1260,1260,1260,1260,1260,1260,\n1260,1260,1260,1260,1260,1260,196,1260,1261,1262,1262,1263,1263,1263,1263,1263,\n1263,196,1261,196,196,1261,196,1261,1261,1261,1262,196,1262,1262,1264,1265,\n1264,1266,1267,1268,1269,1269,196,1270,1270,196,196,196,196,196,196,196,\n196,1271,1271,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 181 */\n1272,1272,1272,1272,1272,1272,1272,1272,1272,1272,1272,1272,1272,1272,1272,1272,\n1272,1272,1272,1272,1272,1272,1272,1272,1272,1272,1272,1272,1272,1272,1272,1272,\n1272,1272,1272,1272,1272,1272,1272,1272,1272,1272,1272,1272,1272,1272,1272,1272,\n1272,1272,1272,1272,1272,1273,1273,1273,1274,1274,1274,1274,1274,1274,1274,1274,\n1273,1273,1275,1274,1274,1273,1276,1272,1272,1272,1272,1277,1277,1278,1279,1279,\n1280,1280,1280,1280,1280,1280,1280,1280,1280,1280,1278,1278,196,1279,1281,1272,\n1272,1272,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 182 */\n1282,1282,1282,1282,1282,1282,1282,1282,1282,1282,1282,1282,1282,1282,1282,1282,\n1282,1282,1282,1282,1282,1282,1282,1282,1282,1282,1282,1282,1282,1282,1282,1282,\n1282,1282,1282,1282,1282,1282,1282,1282,1282,1282,1282,1282,1282,1282,1282,1282,\n1283,1284,1284,1285,1285,1285,1285,1285,1285,1284,1285,1284,1284,1283,1284,1285,\n1285,1284,1286,1287,1282,1282,1288,1282,196,196,196,196,196,196,196,196,\n1289,1289,1289,1289,1289,1289,1289,1289,1289,1289,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 183 */\n1290,1290,1290,1290,1290,1290,1290,1290,1290,1290,1290,1290,1290,1290,1290,1290,\n1290,1290,1290,1290,1290,1290,1290,1290,1290,1290,1290,1290,1290,1290,1290,1290,\n1290,1290,1290,1290,1290,1290,1290,1290,1290,1290,1290,1290,1290,1290,1290,1291,\n1292,1292,1293,1293,1293,1293,196,196,1292,1292,1292,1292,1293,1293,1292,1294,\n1295,1296,1297,1297,1298,1298,1299,1299,1299,1297,1297,1297,1297,1297,1297,1297,\n1297,1297,1297,1297,1297,1297,1297,1297,1290,1290,1290,1290,1293,1293,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 184 */\n1300,1300,1300,1300,1300,1300,1300,1300,1300,1300,1300,1300,1300,1300,1300,1300,\n1300,1300,1300,1300,1300,1300,1300,1300,1300,1300,1300,1300,1300,1300,1300,1300,\n1300,1300,1300,1300,1300,1300,1300,1300,1300,1300,1300,1300,1300,1300,1300,1300,\n1301,1301,1301,1302,1302,1302,1302,1302,1302,1302,1302,1301,1301,1302,1301,1303,\n1302,1304,1304,1305,1300,196,196,196,196,196,196,196,196,196,196,196,\n1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,196,196,196,196,196,196,\n581,581,581,581,581,581,581,581,581,581,581,581,581,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 185 */\n1307,1307,1307,1307,1307,1307,1307,1307,1307,1307,1307,1307,1307,1307,1307,1307,\n1307,1307,1307,1307,1307,1307,1307,1307,1307,1307,1307,1307,1307,1307,1307,1307,\n1307,1307,1307,1307,1307,1307,1307,1307,1307,1307,1307,1308,1309,1308,1309,1309,\n1308,1308,1308,1308,1308,1308,1310,1311,1307,1312,196,196,196,196,196,196,\n1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,196,196,196,196,196,196,\n522,522,522,522,522,522,522,522,522,522,522,522,522,522,522,522,\n522,522,522,522,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 186 */\n1314,1314,1314,1314,1314,1314,1314,1314,1314,1314,1314,1314,1314,1314,1314,1314,\n1314,1314,1314,1314,1314,1314,1314,1314,1314,1314,1314,196,196,1315,1316,1315,\n1317,1317,1315,1315,1315,1315,1316,1315,1315,1315,1315,1318,196,196,196,196,\n1319,1319,1319,1319,1319,1319,1319,1319,1319,1319,1320,1320,1321,1321,1321,1322,\n1314,1314,1314,1314,1314,1314,1314,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 187 */\n1323,1323,1323,1323,1323,1323,1323,1323,1323,1323,1323,1323,1323,1323,1323,1323,\n1323,1323,1323,1323,1323,1323,1323,1323,1323,1323,1323,1323,1323,1323,1323,1323,\n1323,1323,1323,1323,1323,1323,1323,1323,1323,1323,1323,1323,1324,1324,1324,1325,\n1325,1325,1325,1325,1325,1325,1325,1325,1324,1326,1327,1328,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 188 */\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n1329,1329,1329,1329,1329,1329,1329,1329,1329,1329,1329,1329,1329,1329,1329,1329,\n1329,1329,1329,1329,1329,1329,1329,1329,1329,1329,1329,1329,1329,1329,1329,1329,\n1330,1330,1330,1330,1330,1330,1330,1330,1330,1330,1330,1330,1330,1330,1330,1330,\n1330,1330,1330,1330,1330,1330,1330,1330,1330,1330,1330,1330,1330,1330,1330,1330,\n1331,1331,1331,1331,1331,1331,1331,1331,1331,1331,1332,1332,1332,1332,1332,1332,\n1332,1332,1332,196,196,196,196,196,196,196,196,196,196,196,196,1333,\n\n/* block 189 */\n1334,1334,1334,1334,1334,1334,1334,196,196,1334,196,196,1334,1334,1334,1334,\n1334,1334,1334,1334,196,1334,1334,196,1334,1334,1334,1334,1334,1334,1334,1334,\n1334,1334,1334,1334,1334,1334,1334,1334,1334,1334,1334,1334,1334,1334,1334,1334,\n1335,1336,1336,1336,1336,1336,196,1336,1336,196,196,1337,1337,1338,1339,1340,\n1336,1340,1336,1341,1342,1343,1342,196,196,196,196,196,196,196,196,196,\n1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 190 */\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n1345,1345,1345,1345,1345,1345,1345,1345,196,196,1345,1345,1345,1345,1345,1345,\n1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,\n1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,1345,\n1345,1346,1346,1346,1347,1347,1347,1347,196,196,1347,1347,1346,1346,1346,1346,\n1348,1345,1349,1345,1346,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 191 */\n1350,1351,1351,1351,1351,1351,1351,1352,1352,1351,1351,1350,1350,1350,1350,1350,\n1350,1350,1350,1350,1350,1350,1350,1350,1350,1350,1350,1350,1350,1350,1350,1350,\n1350,1350,1350,1350,1350,1350,1350,1350,1350,1350,1350,1350,1350,1350,1350,1350,\n1350,1350,1350,1353,1354,1351,1351,1351,1351,1355,1356,1351,1351,1351,1351,1357,\n1357,1357,1358,1358,1357,1357,1357,1354,196,196,196,196,196,196,196,196,\n1359,1360,1360,1360,1360,1360,1360,1361,1361,1360,1360,1360,1359,1359,1359,1359,\n1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,\n1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,\n\n/* block 192 */\n1359,1359,1359,1359,1362,1362,1362,1362,1362,1362,1360,1360,1360,1360,1360,1360,\n1360,1360,1360,1360,1360,1360,1360,1361,1363,1364,1365,1366,1366,1359,1365,1365,\n1365,1367,1367,196,196,196,196,196,196,196,196,196,196,196,196,196,\n545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,545,\n1368,1368,1368,1368,1368,1368,1368,1368,1368,1368,1368,1368,1368,1368,1368,1368,\n1368,1368,1368,1368,1368,1368,1368,1368,1368,1368,1368,1368,1368,1368,1368,1368,\n1368,1368,1368,1368,1368,1368,1368,1368,1368,1368,1368,1368,1368,1368,1368,1368,\n1368,1368,1368,1368,1368,1368,1368,1368,1368,196,196,196,196,196,196,196,\n\n/* block 193 */\n385,385,385,385,385,385,385,385,385,385,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 194 */\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n1369,1369,1369,1369,1369,1369,1369,1369,1369,1369,1369,1369,1369,1369,1369,1369,\n1369,1369,1369,1369,1369,1369,1369,1369,1369,1369,1369,1369,1369,1369,1369,1369,\n1369,1370,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n1371,1371,1371,1371,1371,1371,1371,1371,1371,1371,196,196,196,196,196,196,\n\n/* block 195 */\n1372,1372,1372,1372,1372,1372,1372,1372,1372,196,1372,1372,1372,1372,1372,1372,\n1372,1372,1372,1372,1372,1372,1372,1372,1372,1372,1372,1372,1372,1372,1372,1372,\n1372,1372,1372,1372,1372,1372,1372,1372,1372,1372,1372,1372,1372,1372,1372,1373,\n1374,1374,1374,1374,1374,1374,1374,196,1374,1374,1374,1374,1374,1374,1373,1375,\n1372,1376,1376,1377,1378,1378,196,196,196,196,196,196,196,196,196,196,\n1379,1379,1379,1379,1379,1379,1379,1379,1379,1379,1380,1380,1380,1380,1380,1380,\n1380,1380,1380,1380,1380,1380,1380,1380,1380,1380,1380,1380,1380,196,196,196,\n1381,1382,1383,1383,1383,1383,1383,1383,1383,1383,1383,1383,1383,1383,1383,1383,\n\n/* block 196 */\n1383,1383,1383,1383,1383,1383,1383,1383,1383,1383,1383,1383,1383,1383,1383,1383,\n196,196,1384,1384,1384,1384,1384,1384,1384,1384,1384,1384,1384,1384,1384,1384,\n1384,1384,1384,1384,1384,1384,1384,1384,196,1385,1384,1384,1384,1384,1384,1384,\n1384,1385,1384,1384,1385,1384,1384,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 197 */\n1386,1386,1386,1386,1386,1386,1386,196,1386,1386,196,1386,1386,1386,1386,1386,\n1386,1386,1386,1386,1386,1386,1386,1386,1386,1386,1386,1386,1386,1386,1386,1386,\n1386,1386,1386,1386,1386,1386,1386,1386,1386,1386,1386,1386,1386,1386,1386,1386,\n1386,1387,1387,1387,1387,1387,1387,196,196,196,1387,196,1387,1387,196,1387,\n1387,1387,1388,1387,1389,1389,1390,1387,196,196,196,196,196,196,196,196,\n1391,1391,1391,1391,1391,1391,1391,1391,1391,1391,196,196,196,196,196,196,\n1392,1392,1392,1392,1392,1392,196,1392,1392,196,1392,1392,1392,1392,1392,1392,\n1392,1392,1392,1392,1392,1392,1392,1392,1392,1392,1392,1392,1392,1392,1392,1392,\n\n/* block 198 */\n1392,1392,1392,1392,1392,1392,1392,1392,1392,1392,1393,1393,1393,1393,1393,196,\n1394,1394,196,1393,1393,1394,1393,1395,1392,196,196,196,196,196,196,196,\n1396,1396,1396,1396,1396,1396,1396,1396,1396,1396,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 199 */\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n1397,1397,1397,1397,1397,1397,1397,1397,1397,1397,1397,1397,1397,1397,1397,1397,\n1397,1397,1397,1398,1398,1399,1399,1400,1400,196,196,196,196,196,196,196,\n\n/* block 200 */\n1401,1401,1402,1403,1404,1404,1404,1404,1404,1404,1404,1404,1404,1404,1404,1404,\n1404,196,1404,1404,1404,1404,1404,1404,1404,1404,1404,1404,1404,1404,1404,1404,\n1404,1404,1404,1404,1404,1404,1404,1404,1404,1404,1404,1404,1404,1404,1404,1404,\n1404,1404,1404,1404,1403,1403,1401,1401,1401,1401,1401,196,196,196,1403,1403,\n1401,1405,1406,1407,1407,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,\n1409,1409,1409,1409,1409,1409,1409,1409,1409,1409,1410,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 201 */\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n914,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n1411,1411,1411,1411,1411,1411,1411,1411,1411,1411,1411,1411,1411,1411,1411,1411,\n435,435,1411,435,1411,437,437,437,437,437,437,437,437,438,438,438,\n438,437,437,437,437,437,437,437,437,437,437,437,437,437,437,437,\n437,437,196,196,196,196,196,196,196,196,196,196,196,196,196,1412,\n\n/* block 202 */\n1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,\n1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,\n1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,\n1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,\n1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,\n1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,\n1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,\n1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,\n\n/* block 203 */\n1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,\n1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 204 */\n1414,1414,1414,1414,1414,1414,1414,1414,1414,1414,1414,1414,1414,1414,1414,1414,\n1414,1414,1414,1414,1414,1414,1414,1414,1414,1414,1414,1414,1414,1414,1414,1414,\n1414,1414,1414,1414,1414,1414,1414,1414,1414,1414,1414,1414,1414,1414,1414,1414,\n1414,1414,1414,1414,1414,1414,1414,1414,1414,1414,1414,1414,1414,1414,1414,1414,\n1414,1414,1414,1414,1414,1414,1414,1414,1414,1414,1414,1414,1414,1414,1414,1414,\n1414,1414,1414,1414,1414,1414,1414,1414,1414,1414,1414,1414,1414,1414,1414,1414,\n1414,1414,1414,1414,1414,1414,1414,1414,1414,1414,1414,1414,1414,1414,1414,196,\n1415,1415,1415,1415,1415,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 205 */\n1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,\n1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,\n1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,\n1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,1413,\n1413,1413,1413,1413,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 206 */\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n1416,1416,1416,1416,1416,1416,1416,1416,1416,1416,1416,1416,1416,1416,1416,1416,\n1416,1416,1416,1416,1416,1416,1416,1416,1416,1416,1416,1416,1416,1416,1416,1416,\n1416,1416,1416,1416,1416,1416,1416,1416,1416,1416,1416,1416,1416,1416,1416,1416,\n1416,1416,1416,1416,1416,1416,1416,1416,1416,1416,1416,1416,1416,1416,1416,1416,\n1416,1416,1416,1416,1416,1416,1416,1416,1416,1416,1416,1416,1416,1416,1416,1416,\n1416,1416,1416,1416,1416,1416,1416,1416,1416,1416,1416,1416,1416,1416,1416,1416,\n1416,1417,1417,196,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 207 */\n1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,\n1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,\n1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,\n1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,\n1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,\n1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,\n1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,\n1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,\n\n/* block 208 */\n1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,\n1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,\n1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,\n1419,1419,1419,1419,1419,1419,1419,1419,1419,1419,1419,1419,1419,1419,1419,1419,\n1420,1418,1418,1418,1418,1418,1418,1421,1421,1421,1421,1421,1421,1421,1421,1421,\n1421,1421,1421,1421,1421,1421,196,196,196,196,196,196,196,196,196,196,\n1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,\n1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,\n\n/* block 209 */\n1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,\n1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,\n1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,\n1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,\n1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,\n1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,\n1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,\n1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,1418,196,196,196,196,196,\n\n/* block 210 */\n1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,\n1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,\n1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,\n1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,\n1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,\n1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,\n1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,\n1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,\n\n/* block 211 */\n1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,\n1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,\n1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,\n1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,1422,\n1422,1422,1422,1422,1422,1422,1422,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 212 */\n1423,1423,1423,1423,1423,1423,1423,1423,1423,1423,1423,1423,1423,1423,1423,1423,\n1423,1423,1423,1423,1423,1423,1423,1423,1423,1423,1423,1423,1423,1423,1424,1424,\n1424,1424,1424,1424,1424,1424,1424,1424,1424,1424,1425,1425,1425,1424,1424,1426,\n1427,1427,1427,1427,1427,1427,1427,1427,1427,1427,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 213 */\n930,930,930,930,930,930,930,930,930,930,930,930,930,930,930,930,\n930,930,930,930,930,930,930,930,930,930,930,930,930,930,930,930,\n930,930,930,930,930,930,930,930,930,930,930,930,930,930,930,930,\n930,930,930,930,930,930,930,930,930,930,930,930,930,930,930,930,\n930,930,930,930,930,930,930,930,930,930,930,930,930,930,930,930,\n930,930,930,930,930,930,930,930,930,930,930,930,930,930,930,930,\n930,930,930,930,930,930,930,930,930,930,930,930,930,930,930,930,\n930,930,930,930,930,930,930,930,930,930,930,930,930,930,930,930,\n\n/* block 214 */\n930,930,930,930,930,930,930,930,930,930,930,930,930,930,930,930,\n930,930,930,930,930,930,930,930,930,930,930,930,930,930,930,930,\n930,930,930,930,930,930,930,930,930,930,930,930,930,930,930,930,\n930,930,930,930,930,930,930,930,930,196,196,196,196,196,196,196,\n1428,1428,1428,1428,1428,1428,1428,1428,1428,1428,1428,1428,1428,1428,1428,1428,\n1428,1428,1428,1428,1428,1428,1428,1428,1428,1428,1428,1428,1428,1428,1428,196,\n1429,1429,1429,1429,1429,1429,1429,1429,1429,1429,196,196,196,196,1430,1430,\n1431,1431,1431,1431,1431,1431,1431,1431,1431,1431,1431,1431,1431,1431,1431,1431,\n\n/* block 215 */\n1431,1431,1431,1431,1431,1431,1431,1431,1431,1431,1431,1431,1431,1431,1431,1431,\n1431,1431,1431,1431,1431,1431,1431,1431,1431,1431,1431,1431,1431,1431,1431,1431,\n1431,1431,1431,1431,1431,1431,1431,1431,1431,1431,1431,1431,1431,1431,1431,1431,\n1431,1431,1431,1431,1431,1431,1431,1431,1431,1431,1431,1431,1431,1431,1431,196,\n1432,1432,1432,1432,1432,1432,1432,1432,1432,1432,196,196,196,196,196,196,\n1433,1433,1433,1433,1433,1433,1433,1433,1433,1433,1433,1433,1433,1433,1433,1433,\n1433,1433,1433,1433,1433,1433,1433,1433,1433,1433,1433,1433,1433,1433,196,196,\n1434,1434,1434,1434,1434,1435,196,196,196,196,196,196,196,196,196,196,\n\n/* block 216 */\n1436,1436,1436,1436,1436,1436,1436,1436,1436,1436,1436,1436,1436,1436,1436,1436,\n1436,1436,1436,1436,1436,1436,1436,1436,1436,1436,1436,1436,1436,1436,1436,1436,\n1436,1436,1436,1436,1436,1436,1436,1436,1436,1436,1436,1436,1436,1436,1436,1436,\n1437,1437,1437,1437,1437,1437,1437,1438,1438,1439,1440,1440,1441,1441,1441,1441,\n1442,1442,1443,1443,1438,1441,196,196,196,196,196,196,196,196,196,196,\n1444,1444,1444,1444,1444,1444,1444,1444,1444,1444,196,1445,1445,1445,1445,1445,\n1445,1445,196,1436,1436,1436,1436,1436,1436,1436,1436,1436,1436,1436,1436,1436,\n1436,1436,1436,1436,1436,1436,1436,1436,196,196,196,196,196,1436,1436,1436,\n\n/* block 217 */\n1436,1436,1436,1436,1436,1436,1436,1436,1436,1436,1436,1436,1436,1436,1436,1436,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 218 */\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n1446,1446,1446,1447,1447,1447,1447,1447,1447,1447,1447,1447,1447,1447,1447,1447,\n1447,1447,1447,1447,1447,1447,1447,1447,1447,1447,1447,1447,1447,1447,1447,1447,\n1447,1447,1447,1448,1447,1447,1447,1448,1448,1448,1448,1449,1449,1450,1451,1451,\n1452,1452,1452,1452,1452,1452,1452,1452,1452,1452,196,196,196,196,196,196,\n\n/* block 219 */\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n1453,1453,1453,1453,1453,1453,1453,1453,1453,1453,1453,1453,1453,1453,1453,1453,\n1453,1453,1453,1453,1453,1453,1453,1453,1453,1453,1453,1453,1453,1453,1453,1453,\n1454,1454,1454,1454,1454,1454,1454,1454,1454,1454,1454,1454,1454,1454,1454,1454,\n1454,1454,1454,1454,1454,1454,1454,1454,1454,1454,1454,1454,1454,1454,1454,1454,\n\n/* block 220 */\n1455,1455,1455,1455,1455,1455,1455,1455,1455,1455,1455,1455,1455,1455,1455,1455,\n1455,1455,1455,1455,1455,1455,1455,1456,1457,1458,1458,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 221 */\n1459,1459,1459,1459,1459,1459,1459,1459,1459,1459,1459,1459,1459,1459,1459,1459,\n1459,1459,1459,1459,1459,1459,1459,1459,1459,1459,1459,1459,1459,1459,1459,1459,\n1459,1459,1459,1459,1459,1459,1459,1459,1459,1459,1459,1459,1459,1459,1459,1459,\n1459,1459,1459,1459,1459,1459,1459,1459,1459,1459,1459,1459,1459,1459,1459,1459,\n1459,1459,1459,1459,1459,1459,1459,1459,1459,1459,1459,196,196,196,196,1460,\n1459,1461,1461,1461,1461,1461,1461,1461,1461,1461,1461,1461,1461,1461,1461,1461,\n1461,1461,1461,1461,1461,1461,1461,1461,1461,1461,1461,1461,1461,1461,1461,1461,\n1461,1461,1461,1461,1461,1461,1461,1461,1461,1461,1461,1461,1461,1461,1461,1461,\n\n/* block 222 */\n1461,1461,1461,1461,1461,1461,1461,1461,196,196,196,196,196,196,196,1462,\n1462,1462,1462,1463,1463,1463,1463,1463,1463,1463,1463,1463,1463,1463,1463,1463,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n1464,1465,1466,867,1467,196,196,196,196,196,196,196,196,196,196,196,\n1468,1468,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 223 */\n1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,\n1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,\n1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,\n1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,\n1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,\n1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,\n1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,\n1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,\n\n/* block 224 */\n1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,\n1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,\n1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,\n1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,\n1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,\n1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,\n1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,1469,\n1469,1469,1469,1469,1469,1469,1469,1469,196,196,196,196,196,196,196,196,\n\n/* block 225 */\n1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,\n1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,\n1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,\n1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,\n1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,\n1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,\n1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,\n1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,\n\n/* block 226 */\n1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,\n1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,\n1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,\n1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,\n1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,1470,\n1470,1470,1470,1470,1470,1470,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,1470,\n\n/* block 227 */\n1469,1469,1469,1469,1469,1469,1469,1469,1469,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 228 */\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n1471,1471,1471,1471,196,1471,1471,1471,1471,1471,1471,1471,196,1471,1471,196,\n\n/* block 229 */\n896,891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,\n891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,\n891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,\n891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,\n891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,\n891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,\n891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,\n891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,\n\n/* block 230 */\n891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,\n891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,\n891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,\n891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,\n891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,\n891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,\n891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,\n891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,\n\n/* block 231 */\n891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,\n891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,\n896,896,896,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,891,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n891,891,891,196,196,896,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,896,896,896,896,196,196,196,196,196,196,196,196,\n1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,\n\n/* block 232 */\n1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,\n1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,\n1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,\n1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,\n1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,\n1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,\n1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,\n1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,\n\n/* block 233 */\n1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,\n1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,\n1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,\n1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,\n1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,\n1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,\n1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,\n1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,1472,196,196,196,196,\n\n/* block 234 */\n1473,1473,1473,1473,1473,1473,1473,1473,1473,1473,1473,1473,1473,1473,1473,1473,\n1473,1473,1473,1473,1473,1473,1473,1473,1473,1473,1473,1473,1473,1473,1473,1473,\n1473,1473,1473,1473,1473,1473,1473,1473,1473,1473,1473,1473,1473,1473,1473,1473,\n1473,1473,1473,1473,1473,1473,1473,1473,1473,1473,1473,1473,1473,1473,1473,1473,\n1473,1473,1473,1473,1473,1473,1473,1473,1473,1473,1473,1473,1473,1473,1473,1473,\n1473,1473,1473,1473,1473,1473,1473,1473,1473,1473,1473,1473,1473,1473,1473,1473,\n1473,1473,1473,1473,1473,1473,1473,1473,1473,1473,1473,196,196,196,196,196,\n1473,1473,1473,1473,1473,1473,1473,1473,1473,1473,1473,1473,1473,196,196,196,\n\n/* block 235 */\n1473,1473,1473,1473,1473,1473,1473,1473,1473,196,196,196,196,196,196,196,\n1473,1473,1473,1473,1473,1473,1473,1473,1473,1473,196,196,1474,1475,1476,1477,\n1478,1478,1478,1478,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 236 */\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,\n\n/* block 237 */\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,\n781,781,781,781,781,781,510,510,510,510,510,510,510,510,510,510,\n510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,\n1479,1479,1479,1479,1479,1479,1479,1479,1479,1479,196,196,196,196,196,196,\n\n/* block 238 */\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,\n781,781,781,781,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 239 */\n176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,\n176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,\n176,176,176,176,176,176,176,176,176,176,176,176,176,176,196,196,\n176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,\n176,176,176,176,176,176,176,196,196,196,196,196,196,196,196,196,\n510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,\n510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,\n510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,\n\n/* block 240 */\n510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,\n510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,\n510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,\n510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,\n510,510,510,510,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 241 */\n510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,\n510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,\n510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,\n510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,\n510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,\n510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,\n510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,\n510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,\n\n/* block 242 */\n510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,\n510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,\n510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,\n510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,\n510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,\n510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,\n510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,\n510,510,510,510,510,510,196,196,196,196,196,196,196,196,196,196,\n\n/* block 243 */\n510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,\n510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,\n510,510,510,510,510,510,510,196,196,510,510,510,510,510,510,510,\n510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,\n510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,\n510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,\n510,510,510,510,510,1480,1480,176,176,176,510,510,510,1481,1481,1481,\n1481,1481,1481, 51, 51, 51, 51, 51, 51, 51, 51,176,176,176,176,176,\n\n/* block 244 */\n176,176,176,510,510,176,176,176,176,176,176,176,510,510,510,510,\n510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,\n510,510,510,510,510,510,510,510,510,510,176,176,176,176,510,510,\n510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,\n510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,\n510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,\n510,510,510,510,510,510,510,510,510,781,781,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 245 */\n1077,1077,1077,1077,1077,1077,1077,1077,1077,1077,1077,1077,1077,1077,1077,1077,\n1077,1077,1077,1077,1077,1077,1077,1077,1077,1077,1077,1077,1077,1077,1077,1077,\n1077,1077,1077,1077,1077,1077,1077,1077,1077,1077,1077,1077,1077,1077,1077,1077,\n1077,1077,1077,1077,1077,1077,1077,1077,1077,1077,1077,1077,1077,1077,1077,1077,\n1077,1077,1482,1482,1482,1077,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 246 */\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n907,907,907,907,907,907,907,907,907,907,907,907,907,907,907,907,\n907,907,907,907,196,196,196,196,196,196,196,196,196,196,196,196,\n907,907,907,907,907,907,907,907,907,907,907,907,907,907,907,907,\n907,907,907,907,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 247 */\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,\n781,781,781,781,781,781,781,196,196,196,196,196,196,196,196,196,\n904,904,904,904,904,904,904,904,904,904,904,904,904,904,904,904,\n904,904,907,907,907,907,907,907,907,196,196,196,196,196,196,196,\n\n/* block 248 */\n782,782,782,782,782,782,782,782,782,782,782,782,782,782,782,782,\n782,782,782,782,782,782,782,782,782,782,783,783,783,783,783,783,\n783,783,795,795,783,783,783,783,783,783,783,783,783,783,783,783,\n783,783,783,783,782,782,782,782,782,782,782,782,782,782,782,782,\n782,782,782,782,782,782,782,782,782,782,782,782,782,782,783,783,\n783,783,783,783,783,196,795,795,783,783,783,783,783,783,783,783,\n783,783,783,783,783,783,783,783,782,782,782,782,782,782,782,782,\n782,782,782,782,782,782,782,782,782,782,782,782,782,782,782,782,\n\n/* block 249 */\n782,782,783,783,783,783,783,783,783,783,795,795,783,783,783,783,\n783,783,783,783,783,783,783,783,783,783,783,783,782,196,782,782,\n196,196,782,196,196,782,782,196,196,782,782,782,782,196,782,782,\n782,782,782,782,782,782,783,783,783,783,196,783,196,783,795,795,\n783,783,783,783,196,783,783,783,783,783,783,783,783,783,783,783,\n782,782,782,782,782,782,782,782,782,782,782,782,782,782,782,782,\n782,782,782,782,782,782,782,782,782,782,783,783,783,783,783,783,\n783,783,795,795,783,783,783,783,783,783,783,783,783,783,783,783,\n\n/* block 250 */\n783,783,783,783,782,782,196,782,782,782,782,196,196,782,782,782,\n782,782,782,782,782,196,782,782,782,782,782,782,782,196,783,783,\n783,783,783,783,783,783,795,795,783,783,783,783,783,783,783,783,\n783,783,783,783,783,783,783,783,782,782,196,782,782,782,782,196,\n782,782,782,782,782,196,782,196,196,196,782,782,782,782,782,782,\n782,196,783,783,783,783,783,783,783,783,795,795,783,783,783,783,\n783,783,783,783,783,783,783,783,783,783,783,783,782,782,782,782,\n782,782,782,782,782,782,782,782,782,782,782,782,782,782,782,782,\n\n/* block 251 */\n782,782,782,782,782,782,783,783,783,783,783,783,783,783,795,795,\n783,783,783,783,783,783,783,783,783,783,783,783,783,783,783,783,\n782,782,782,782,782,782,782,782,782,782,782,782,782,782,782,782,\n782,782,782,782,782,782,782,782,782,782,783,783,783,783,783,783,\n783,783,795,795,783,783,783,783,783,783,783,783,783,783,783,783,\n783,783,783,783,782,782,782,782,782,782,782,782,782,782,782,782,\n782,782,782,782,782,782,782,782,782,782,782,782,782,782,783,783,\n783,783,783,783,783,783,795,795,783,783,783,783,783,783,783,783,\n\n/* block 252 */\n783,783,783,783,783,783,783,783,782,782,782,782,782,782,782,782,\n782,782,782,782,782,782,782,782,782,782,782,782,782,782,782,782,\n782,782,783,783,783,783,783,783,783,783,795,795,783,783,783,783,\n783,783,783,783,783,783,783,783,783,783,783,783,782,782,782,782,\n782,782,782,782,782,782,782,782,782,782,782,782,782,782,782,782,\n782,782,782,782,782,782,783,783,783,783,783,783,783,783,795,795,\n783,783,783,783,783,783,783,783,783,783,783,783,783,783,783,783,\n782,782,782,782,782,782,782,782,782,782,782,782,782,782,782,782,\n\n/* block 253 */\n782,782,782,782,782,782,782,782,782,782,783,783,783,783,783,783,\n783,783,795,795,783,783,783,783,783,783,783,783,783,783,783,783,\n783,783,783,783,783,783,196,196,782,782,782,782,782,782,782,782,\n782,782,782,782,782,782,782,782,782,782,782,782,782,782,782,782,\n782,1483,783,783,783,783,783,783,783,783,783,783,783,783,783,783,\n783,783,783,783,783,783,783,783,783,783,783,1483,783,783,783,783,\n783,783,782,782,782,782,782,782,782,782,782,782,782,782,782,782,\n782,782,782,782,782,782,782,782,782,782,782,1483,783,783,783,783,\n\n/* block 254 */\n783,783,783,783,783,783,783,783,783,783,783,783,783,783,783,783,\n783,783,783,783,783,1483,783,783,783,783,783,783,782,782,782,782,\n782,782,782,782,782,782,782,782,782,782,782,782,782,782,782,782,\n782,782,782,782,782,1483,783,783,783,783,783,783,783,783,783,783,\n783,783,783,783,783,783,783,783,783,783,783,783,783,783,783,1483,\n783,783,783,783,783,783,782,782,782,782,782,782,782,782,782,782,\n782,782,782,782,782,782,782,782,782,782,782,782,782,782,782,1483,\n783,783,783,783,783,783,783,783,783,783,783,783,783,783,783,783,\n\n/* block 255 */\n783,783,783,783,783,783,783,783,783,1483,783,783,783,783,783,783,\n782,782,782,782,782,782,782,782,782,782,782,782,782,782,782,782,\n782,782,782,782,782,782,782,782,782,1483,783,783,783,783,783,783,\n783,783,783,783,783,783,783,783,783,783,783,783,783,783,783,783,\n783,783,783,1483,783,783,783,783,783,783,782,783,196,196,1484,1484,\n1484,1484,1484,1484,1484,1484,1484,1484,1484,1484,1484,1484,1484,1484,1484,1484,\n1484,1484,1484,1484,1484,1484,1484,1484,1484,1484,1484,1484,1484,1484,1484,1484,\n1484,1484,1484,1484,1484,1484,1484,1484,1484,1484,1484,1484,1484,1484,1484,1484,\n\n/* block 256 */\n1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,\n1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,\n1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,\n1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,\n1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,\n1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,\n1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,\n1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,\n\n/* block 257 */\n1486,1486,1486,1486,1486,1486,1486,1486,1486,1486,1486,1486,1486,1486,1486,1486,\n1486,1486,1486,1486,1486,1486,1486,1486,1486,1486,1486,1486,1486,1486,1486,1486,\n1486,1486,1486,1486,1486,1486,1486,1486,1486,1486,1486,1486,1486,1486,1486,1486,\n1486,1486,1486,1486,1486,1486,1486,1485,1485,1485,1485,1486,1486,1486,1486,1486,\n1486,1486,1486,1486,1486,1486,1486,1486,1486,1486,1486,1486,1486,1486,1486,1486,\n1486,1486,1486,1486,1486,1486,1486,1486,1486,1486,1486,1486,1486,1486,1486,1486,\n1486,1486,1486,1486,1486,1486,1486,1486,1486,1486,1486,1486,1486,1485,1485,1485,\n1485,1485,1485,1485,1485,1486,1485,1485,1485,1485,1485,1485,1485,1485,1485,1485,\n\n/* block 258 */\n1485,1485,1485,1485,1486,1485,1485,1487,1488,1487,1487,1489,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,1486,1486,1486,1486,1486,\n196,1486,1486,1486,1486,1486,1486,1486,1486,1486,1486,1486,1486,1486,1486,1486,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 259 */\n 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 93, 70, 70, 70, 70, 70,\n 70, 70, 70, 70, 70, 70, 70, 70, 70, 70,694, 70, 70, 70, 70,196,\n196,196,196,196,196, 70, 70, 70, 70, 70, 70,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 260 */\n1490,1490,1490,1490,1490,1490,1490,196,1490,1490,1490,1490,1490,1490,1490,1490,\n1490,1490,1490,1490,1490,1490,1490,1490,1490,196,196,1490,1490,1490,1490,1490,\n1490,1490,196,1490,1490,196,1490,1490,1490,1490,1490,196,196,196,196,196,\n929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,\n929,929,929,929,929,929,929,929,929,929,929,929,1491,1491,929,929,\n929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,929,\n929,929,929,929,929,929,929,929,1491,929,929,929,929,929,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 261 */\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,849,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 262 */\n1492,1492,1492,1492,1492,1492,1492,1492,1492,1492,1492,1492,1492,1492,1492,1492,\n1492,1492,1492,1492,1492,1492,1492,1492,1492,1492,1492,1492,1492,1492,1492,1492,\n1492,1492,1492,1492,1492,1492,1492,1492,1492,1492,1492,1492,1492,196,196,196,\n1493,1493,1493,1493,1493,1493,1493,1494,1494,1494,1494,1494,1495,1495,196,196,\n1496,1496,1496,1496,1496,1496,1496,1496,1496,1496,196,196,196,196,1492,1497,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 263 */\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n1498,1498,1498,1498,1498,1498,1498,1498,1498,1498,1498,1498,1498,1498,1498,1498,\n1498,1498,1498,1498,1498,1498,1498,1498,1498,1498,1498,1498,1498,1498,1499,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n1500,1500,1500,1500,1500,1500,1500,1500,1500,1500,1500,1500,1500,1500,1500,1500,\n1500,1500,1500,1500,1500,1500,1500,1500,1500,1500,1500,1500,1500,1500,1500,1500,\n1500,1500,1500,1500,1500,1500,1500,1500,1500,1500,1500,1500,1501,1501,1501,1501,\n1502,1502,1502,1502,1502,1502,1502,1502,1502,1502,196,196,196,196,196,1503,\n\n/* block 264 */\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n1504,1504,1504,1504,1504,1504,1504,1504,1504,1504,1504,1504,1504,1504,1504,1504,\n1504,1504,1504,1504,1504,1504,1504,1504,1504,1504,1504,1505,1506,1506,1506,1506,\n1507,1507,1507,1507,1507,1507,1507,1507,1507,1507,196,196,196,196,196,196,\n\n/* block 265 */\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n1508,1508,1508,1508,1508,1508,1508,1508,1508,1508,1508,1508,1508,1508,1508,1508,\n1508,1508,1508,1508,1508,1508,1508,1508,1508,1508,1508,1508,1508,1508,1509,1510,\n1508,1511,1511,1511,1511,1511,1511,1511,1511,1511,1511,196,196,196,196,1512,\n\n/* block 266 */\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n533,533,533,533,533,533,533,196,533,533,533,533,196,533,533,196,\n533,533,533,533,533,533,533,533,533,533,533,533,533,533,533,196,\n\n/* block 267 */\n1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,\n1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,\n1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,\n1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,\n1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,\n1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,\n1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,\n1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,\n\n/* block 268 */\n1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,\n1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,\n1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,\n1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,1513,\n1513,1513,1513,1513,1513,297,297,1514,1514,1514,1514,1514,1514,1514,1514,1514,\n1515,1515,1515,1515,1515,1515,1515,297,297,297,297,297,297,297,297,297,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n\n/* block 269 */\n1516,1516,1516,1516,1516,1516,1516,1516,1516,1516,1516,1516,1516,1516,1516,1516,\n1516,1516,1516,1516,1516,1516,1516,1516,1516,1516,1516,1516,1516,1516,1516,1516,\n1516,1516,1517,1517,1517,1517,1517,1517,1517,1517,1517,1517,1517,1517,1517,1517,\n1517,1517,1517,1517,1517,1517,1517,1517,1517,1517,1517,1517,1517,1517,1517,1517,\n1517,1517,1517,1517,1518,1518,1518,1519,1520,1520,1520,1521,297,297,297,297,\n1522,1522,1522,1522,1522,1522,1522,1522,1522,1522,297,297,297,297,1523,1523,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n\n/* block 270 */\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n\n/* block 271 */\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n340,1524,1524,1524,1524,1524,1524,1524,1524,1524,1524,1524,1524,1524,1524,1524,\n\n/* block 272 */\n1524,1524,1524,1524,1524,1524,1524,1524,1524,1524,1524,1524,1524,1524,1524,1524,\n1524,1524,1524,1524,1524,1524,1524,1524,1524,1524,1524,1524,1524,1524,1524,1524,\n1524,1524,1524,1524,1524,1524,1524,1524,1524,1524,1524,1524,1525,1524,1524,1524,\n1526,1524,1524,1524,1524,340,340,340,340,340,340,340,340,340,340,340,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n\n/* block 273 */\n340,1524,1524,1524,1524,1524,1524,1524,1524,1524,1524,1524,1524,1524,1524,1524,\n1524,1524,1524,1524,1524,1524,1524,1524,1524,1524,1524,1524,1524,1524,1524,1524,\n1524,1524,1524,1524,1524,1524,1524,1524,1524,1524,1524,1524,1524,1524,1525,1524,\n1524,1524,1524,1524,1524,1524,1524,1524,1524,1524,1524,1524,1524,1524,340,340,\n340,340,340,340,340,340,340,340,340,340,340,340,340,340,340,340,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,297,\n\n/* block 274 */\n1527,1527,1527,1527,340,1527,1527,1527,1527,1527,1527,1527,1527,1527,1527,1527,\n1527,1527,1527,1527,1527,1527,1527,1527,1527,1527,1527,1527,1527,1527,1527,1527,\n340,1527,1527,340,1527,340,340,1527,340,1527,1527,1527,1527,1527,1527,1527,\n1527,1527,1527,340,1527,1527,1527,1527,340,1527,340,1527,340,340,340,340,\n340,340,1527,340,340,340,340,1527,340,1527,340,1527,340,1527,1527,1527,\n340,1527,1527,340,1527,340,340,1527,340,1527,340,1527,340,1527,340,1527,\n340,1527,1527,340,1527,340,340,1527,1527,1527,1527,340,1527,1527,1527,1527,\n1527,1527,1527,340,1527,1527,1527,1527,340,1527,1527,1527,1527,340,1527,340,\n\n/* block 275 */\n1527,1527,1527,1527,1527,1527,1527,1527,1527,1527,340,1527,1527,1527,1527,1527,\n1527,1527,1527,1527,1527,1527,1527,1527,1527,1527,1527,1527,340,340,340,340,\n340,1527,1527,1527,340,1527,1527,1527,1527,1527,340,1527,1527,1527,1527,1527,\n1527,1527,1527,1527,1527,1527,1527,1527,1527,1527,1527,1527,340,340,340,340,\n340,340,340,340,340,340,340,340,340,340,340,340,340,340,340,340,\n340,340,340,340,340,340,340,340,340,340,340,340,340,340,340,340,\n340,340,340,340,340,340,340,340,340,340,340,340,340,340,340,340,\n309,309,340,340,340,340,340,340,340,340,340,340,340,340,340,340,\n\n/* block 276 */\n1528,1528,1528,1528,1529,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,\n1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,\n1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1530,1530,1530,1530,\n1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,\n1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,\n1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,\n1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,\n1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,\n\n/* block 277 */\n1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,\n1528,1528,1528,1528,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,\n1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1530,\n1530,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,\n1530,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1529,\n1530,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,\n1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,\n1528,1528,1528,1528,1528,1528,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,\n\n/* block 278 */\n814,814,814,814,814,814,814,814,814,814,814, 58, 58,1528,1528,1528,\n510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,\n510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,1528,\n1531,1531,1531,1531,1531,1531,1531,1531,1531,1531,1531,1531,1531,1531,1531,1531,\n1531,1531,1531,1531,1531,1531,1531,1531,1531,1531,510,510,510,510,510,510,\n1531,1531,1531,1531,1531,1531,1531,1531,1531,1531,1531,1531,1531,1531,1531,1531,\n1531,1531,1531,1531,1531,1531,1531,1531,1531,1531,781,781,1528,1528,1528,1528,\n1532,1532,1531,1531,1531,1531,1531,1531,1531,1531,1531,1531,1531,1531,1532,1532,\n\n/* block 279 */\n1531,1531,1531,1531,1531,1531,1531,1531,1531,1531,510,510,510,510,1533,510,\n510,1533,1533,1533,1533,1533,1533,1533,1533,1533,1533,510,510,510,510,510,\n510,510,510,510,510,510,510,510,510,510,510,510,510,1528,1530,1530,\n1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,\n1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,\n1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,\n1530,1530,1530,1530,1530,1530,1534,1534,1534,1534,1534,1534,1534,1534,1534,1534,\n1534,1534,1534,1534,1534,1534,1534,1534,1534,1534,1534,1534,1534,1534,1534,1534,\n\n/* block 280 */\n1535,1533,1536,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,\n510,510,510,510,510,510,510,510,510,510,1533,510,510,510,510,510,\n510,510,510,510,510,510,510,510,510,510,510,510,510,510,510,1533,\n510,510,1533,1533,1533,1533,1533,1536,1533,1533,1533,510,1530,1530,1530,1530,\n510,510,510,510,510,510,510,510,510,1530,1530,1530,1530,1530,1530,1530,\n1537,1537,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,\n1528,1528,1528,1528,1528,1528,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,\n1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,\n\n/* block 281 */\n1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,\n1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,\n1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,\n1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,\n1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,\n1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,\n1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,\n1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,\n\n/* block 282 */\n1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,\n1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,\n1529,785,1528,1528,785,785,785,785,785,785,785,785,785,1529,1529,1529,\n1529,1529,1529,1529,1529,1529,785,1529,1529,1529,1529,1529,1529,1529,1529,1529,\n1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,\n1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,\n1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,\n1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,785,1529,1529,\n\n/* block 283 */\n1529,1529,1529,1529,1529,1538,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,\n1529,1529,1529,1529,1528,1528,785,785,1528,785,785,785,1528,1528,785,785,\n1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,\n1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,\n1529,1529,1538,1538,1538,1529,1529,1538,1529,1529,1538,1539,1539,785,785,1529,\n1529,1529,1529,1529,785,785,785,785,785,785,785,785,785,785,785,785,\n1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,\n1529,1528,1528,785,1529,785,1528,785,1529,1529,1529,1540,1540,1540,1540,1540,\n\n/* block 284 */\n1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,\n1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,\n1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,\n1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,785,\n1529,785,1538,1538,1529,1529,1538,1538,1538,1538,1538,1538,1538,1538,1538,1538,\n1538,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,\n1529,1529,1529,1529,1529,1529,1538,1538,1538,1538,1538,1538,1538,1538,1538,1538,\n1538,1538,1538,1538,1538,1538,1538,1538,1538,1529,1529,1529,1538,1529,1529,1529,\n\n/* block 285 */\n1529,1538,1538,1538,1529,1538,1538,1538,1529,1529,1529,1529,1529,1529,1529,1538,\n1529,1538,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,\n1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1538,1529,1529,1529,1529,1529,\n1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,\n1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,\n1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,\n1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,\n1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,785,1528,1529,\n\n/* block 286 */\n1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,\n1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,\n1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,\n1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,781,781,\n781,781,781,781,781,781,1528,1528,1528,785,785,1529,1529,1529,1529,1528,\n1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,\n1529,1529,1529,1529,1529,1529,1529,1529,1528,1528,1528,1528,1528,1528,1528,785,\n785,1528,1528,785,1539,1539,785,785,785,785,1538,1528,1528,1528,1528,1528,\n\n/* block 287 */\n1528,1528,1528,1528,1528,1528,1528,785,1528,1528,785,785,785,785,1528,1528,\n1539,1528,1528,1528,1528,1538,1538,1528,1528,1528,1528,1528,1528,1528,1528,1528,\n1528,1528,1528,1528,1529,785,1528,1528,785,1528,1528,1528,1528,1528,1528,1528,\n1528,785,785,1528,1528,1528,1528,1528,1528,1528,1528,1528,785,1528,1528,1528,\n1528,1528,785,785,785,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,\n1528,785,785,785,1528,1528,1528,1528,1528,1528,1528,1528,785,785,785,1528,\n1528,785,1528,785,1528,1528,1528,1528,785,1528,1528,1528,1528,1528,1528,785,\n1528,1528,1528,785,1528,1528,1528,1528,1528,1528,785,1529,1529,1529,1529,1529,\n\n/* block 288 */\n1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,\n1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,\n1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,\n1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,\n1529,1529,1529,1529,1529,1538,1538,1538,1529,1529,1529,1538,1538,1538,1538,1538,\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,\n\n/* block 289 */\n1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,\n1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,\n1529,1529,1529,1538,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,\n1529,1529,1529,1529,1538,1538,1538,1529,1529,1529,1529,1529,1529,1529,1529,1529,\n1538,1529,1529,1529,1529,1529,1528,1528,1528,1528,1528,785,1538,785,785,785,\n1529,1529,1529,1528,1528,1529,1529,1529,1530,1530,1530,1530,1529,1529,1529,1529,\n785,785,785,785,785,785,1528,1528,1528,785,1528,1529,1529,1530,1530,1530,\n785,1528,1528,785,1529,1529,1529,1529,1529,1529,1529,1529,1529,1530,1530,1530,\n\n/* block 290 */\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,\n781,781,781,781,1528,1528,1528,1530,1530,1530,1530,1528,1528,1528,1528,1528,\n\n/* block 291 */\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,\n781,781,781,781,781,1528,1528,1528,1528,1528,1530,1530,1530,1530,1530,1530,\n1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1530,1530,1530,1530,\n1529,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,\n\n/* block 292 */\n781,781,781,781,781,781,781,781,781,781,781,781,1530,1530,1530,1530,\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,\n781,781,781,781,781,781,781,781,1530,1530,1530,1530,1530,1530,1530,1530,\n781,781,781,781,781,781,781,781,781,781,1530,1530,1530,1530,1530,1530,\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,\n\n/* block 293 */\n781,781,781,781,781,781,781,781,1530,1530,1530,1530,1530,1530,1530,1530,\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,1530,1530,\n1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1530,1530,1530,1530,\n1528,1528,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,\n1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,\n1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,\n1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,\n\n/* block 294 */\n781,781,781,781,781,781,781,781,781,781,781,781,1538,1529,1529,1538,\n1529,1529,1529,1529,1529,1529,1529,1529,1538,1538,1538,1538,1538,1538,1538,1538,\n1529,1529,1529,1529,1529,1529,1538,1529,1529,1529,1529,1529,1529,1529,1529,1529,\n1538,1538,1538,1538,1538,1538,1538,1538,1538,1538,1529,781,1538,1538,1538,1529,\n1529,1529,1529,1529,1529,1529,781,1529,1529,1529,1529,1529,1529,1529,1529,1529,\n1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,\n1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,\n1529,1529,1529,1529,1529,1529,1529,1538,1529,1529,1529,1529,1529,1529,1529,1529,\n\n/* block 295 */\n1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,\n1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,\n1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,\n1541,1541,1541,1541,1529,1538,1538,1529,1538,1538,1529,1538,1529,1529,1529,1529,\n1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1538,1538,1538,\n1529,1538,1538,1538,1538,1538,1538,1538,1538,1538,1538,1538,1538,1538,1529,1529,\n1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,\n1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,\n\n/* block 296 */\n1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,\n1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,\n1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,\n1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,\n1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,\n1528,1528,1528,1528,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,\n1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1528,1530,1530,\n1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1530,1530,1530,\n\n/* block 297 */\n1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1530,1530,1530,1530,1530,1529,\n1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,\n1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,\n1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,\n1529,1529,1529,1538,1538,1538,1529,1530,1530,1530,1530,1530,1530,1530,1529,1529,\n1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1530,1530,1529,\n1529,1529,1529,1529,1529,1529,1529,1529,1529,1529,1530,1530,1530,1530,1530,1530,\n1538,1538,1538,1538,1538,1538,1538,1538,1538,1530,1530,1530,1530,1530,1530,1530,\n\n/* block 298 */\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,\n781,781,781,196,781,781,781,781,781,781,781,781,781,781,781,781,\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,\n781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,781,\n1479,1479,1479,1479,1479,1479,1479,1479,1479,1479,196,196,196,196,196,196,\n\n/* block 299 */\n1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,\n1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,\n1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,\n1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,\n1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,\n1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,\n1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,\n1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1530,1032,1032,\n\n/* block 300 */\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 301 */\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n910,910,910,910,910,910,910,910,910,910,196,196,196,196,196,196,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n\n/* block 302 */\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,196,196,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n\n/* block 303 */\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n910,910,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n\n/* block 304 */\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n910,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n\n/* block 305 */\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 306 */\n1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,\n1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 307 */\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,1032,1032,\n\n/* block 308 */\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n910,910,910,910,910,910,910,910,910,910,910,196,196,196,196,196,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n\n/* block 309 */\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,910,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,196,\n\n/* block 310 */\n765,770,765,765,765,765,765,765,765,765,765,765,765,765,765,765,\n765,765,765,765,765,765,765,765,765,765,765,765,765,765,765,765,\n1542,1542,1542,1542,1542,1542,1542,1542,1542,1542,1542,1542,1542,1542,1542,1542,\n1542,1542,1542,1542,1542,1542,1542,1542,1542,1542,1542,1542,1542,1542,1542,1542,\n1542,1542,1542,1542,1542,1542,1542,1542,1542,1542,1542,1542,1542,1542,1542,1542,\n1542,1542,1542,1542,1542,1542,1542,1542,1542,1542,1542,1542,1542,1542,1542,1542,\n1542,1542,1542,1542,1542,1542,1542,1542,1542,1542,1542,1542,1542,1542,1542,1542,\n1542,1542,1542,1542,1542,1542,1542,1542,1542,1542,1542,1542,1542,1542,1542,1542,\n\n/* block 311 */\n765,765,765,765,765,765,765,765,765,765,765,765,765,765,765,765,\n765,765,765,765,765,765,765,765,765,765,765,765,765,765,765,765,\n765,765,765,765,765,765,765,765,765,765,765,765,765,765,765,765,\n765,765,765,765,765,765,765,765,765,765,765,765,765,765,765,765,\n765,765,765,765,765,765,765,765,765,765,765,765,765,765,765,765,\n765,765,765,765,765,765,765,765,765,765,765,765,765,765,765,765,\n765,765,765,765,765,765,765,765,765,765,765,765,765,765,765,765,\n765,765,765,765,765,765,765,765,765,765,765,765,765,765,765,765,\n\n/* block 312 */\n1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,\n1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,\n1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,\n1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,\n1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,\n1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,\n1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,\n1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,\n\n/* block 313 */\n1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,\n1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,\n1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,\n1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,\n1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,\n1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,\n1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,1035,\n765,765,765,765,765,765,765,765,765,765,765,765,765,765,765,765,\n\n/* block 314 */\n1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,\n1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,\n1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,\n1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,\n1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,\n1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,\n1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,\n1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1032,1032,\n};\n\n#if UCD_BLOCK_SIZE != 128\n#error Please correct UCD_BLOCK_SIZE in pcre2_internal.h\n#endif\n#endif  /* SUPPORT_UNICODE */\n\n#endif  /* PCRE2_PCRE2TEST */\n\n/* End of pcre2_ucd.c */\n"
  },
  {
    "path": "Libraries/PCRE/pcre2_ucp.h",
    "content": "/*************************************************\n*      Perl-Compatible Regular Expressions       *\n*************************************************/\n\n/* PCRE is a library of functions to support regular expressions whose syntax\nand semantics are as close as possible to those of the Perl 5 language.\n\n                       Written by Philip Hazel\n     Original API code Copyright (c) 1997-2012 University of Cambridge\n          New API code Copyright (c) 2016-2022 University of Cambridge\n\nThis module is auto-generated from Unicode data files. DO NOT EDIT MANUALLY!\nInstead, modify the maint/GenerateUcpHeader.py script and run it to generate\na new version of this code.\n\n-----------------------------------------------------------------------------\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n    * Redistributions of source code must retain the above copyright notice,\n      this list of conditions and the following disclaimer.\n\n    * Redistributions in binary form must reproduce the above copyright\n      notice, this list of conditions and the following disclaimer in the\n      documentation and/or other materials provided with the distribution.\n\n    * Neither the name of the University of Cambridge nor the names of its\n      contributors may be used to endorse or promote products derived from\n      this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\nLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\nCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\nSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\nINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\nCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGE.\n-----------------------------------------------------------------------------\n*/\n\n#ifndef PCRE2_UCP_H_IDEMPOTENT_GUARD\n#define PCRE2_UCP_H_IDEMPOTENT_GUARD\n\n/* This file contains definitions of the Unicode property values that are\nreturned by the UCD access macros and used throughout PCRE2.\n\nIMPORTANT: The specific values of the first two enums (general and particular\ncharacter categories) are assumed by the table called catposstab in the file\npcre2_auto_possess.c. They are unlikely to change, but should be checked after\nan update. */\n\n/* These are the general character categories. */\n\nenum {\n  ucp_C,\n  ucp_L,\n  ucp_M,\n  ucp_N,\n  ucp_P,\n  ucp_S,\n  ucp_Z,\n};\n\n/* These are the particular character categories. */\n\nenum {\n  ucp_Cc,    /* Control */\n  ucp_Cf,    /* Format */\n  ucp_Cn,    /* Unassigned */\n  ucp_Co,    /* Private use */\n  ucp_Cs,    /* Surrogate */\n  ucp_Ll,    /* Lower case letter */\n  ucp_Lm,    /* Modifier letter */\n  ucp_Lo,    /* Other letter */\n  ucp_Lt,    /* Title case letter */\n  ucp_Lu,    /* Upper case letter */\n  ucp_Mc,    /* Spacing mark */\n  ucp_Me,    /* Enclosing mark */\n  ucp_Mn,    /* Non-spacing mark */\n  ucp_Nd,    /* Decimal number */\n  ucp_Nl,    /* Letter number */\n  ucp_No,    /* Other number */\n  ucp_Pc,    /* Connector punctuation */\n  ucp_Pd,    /* Dash punctuation */\n  ucp_Pe,    /* Close punctuation */\n  ucp_Pf,    /* Final punctuation */\n  ucp_Pi,    /* Initial punctuation */\n  ucp_Po,    /* Other punctuation */\n  ucp_Ps,    /* Open punctuation */\n  ucp_Sc,    /* Currency symbol */\n  ucp_Sk,    /* Modifier symbol */\n  ucp_Sm,    /* Mathematical symbol */\n  ucp_So,    /* Other symbol */\n  ucp_Zl,    /* Line separator */\n  ucp_Zp,    /* Paragraph separator */\n  ucp_Zs,    /* Space separator */\n};\n\n/* These are Boolean properties. */\n\nenum {\n  ucp_ASCII,\n  ucp_ASCII_Hex_Digit,\n  ucp_Alphabetic,\n  ucp_Bidi_Control,\n  ucp_Bidi_Mirrored,\n  ucp_Case_Ignorable,\n  ucp_Cased,\n  ucp_Changes_When_Casefolded,\n  ucp_Changes_When_Casemapped,\n  ucp_Changes_When_Lowercased,\n  ucp_Changes_When_Titlecased,\n  ucp_Changes_When_Uppercased,\n  ucp_Dash,\n  ucp_Default_Ignorable_Code_Point,\n  ucp_Deprecated,\n  ucp_Diacritic,\n  ucp_Emoji,\n  ucp_Emoji_Component,\n  ucp_Emoji_Modifier,\n  ucp_Emoji_Modifier_Base,\n  ucp_Emoji_Presentation,\n  ucp_Extended_Pictographic,\n  ucp_Extender,\n  ucp_Grapheme_Base,\n  ucp_Grapheme_Extend,\n  ucp_Grapheme_Link,\n  ucp_Hex_Digit,\n  ucp_IDS_Binary_Operator,\n  ucp_IDS_Trinary_Operator,\n  ucp_IDS_Unary_Operator,\n  ucp_ID_Compat_Math_Continue,\n  ucp_ID_Compat_Math_Start,\n  ucp_ID_Continue,\n  ucp_ID_Start,\n  ucp_Ideographic,\n  ucp_InCB,\n  ucp_Join_Control,\n  ucp_Logical_Order_Exception,\n  ucp_Lowercase,\n  ucp_Math,\n  ucp_Modifier_Combining_Mark,\n  ucp_Noncharacter_Code_Point,\n  ucp_Pattern_Syntax,\n  ucp_Pattern_White_Space,\n  ucp_Prepended_Concatenation_Mark,\n  ucp_Quotation_Mark,\n  ucp_Radical,\n  ucp_Regional_Indicator,\n  ucp_Sentence_Terminal,\n  ucp_Soft_Dotted,\n  ucp_Terminal_Punctuation,\n  ucp_Unified_Ideograph,\n  ucp_Uppercase,\n  ucp_Variation_Selector,\n  ucp_White_Space,\n  ucp_XID_Continue,\n  ucp_XID_Start,\n  /* This must be last */\n  ucp_Bprop_Count\n};\n\n/* Size of entries in ucd_boolprop_sets[] */\n\n#define ucd_boolprop_sets_item_size 2\n\n/* These are the bidi class values. */\n\nenum {\n  ucp_bidiAL,   /* Arabic_Letter */\n  ucp_bidiAN,   /* Arabic_Number */\n  ucp_bidiB,    /* Paragraph_Separator */\n  ucp_bidiBN,   /* Boundary_Neutral */\n  ucp_bidiCS,   /* Common_Separator */\n  ucp_bidiEN,   /* European_Number */\n  ucp_bidiES,   /* European_Separator */\n  ucp_bidiET,   /* European_Terminator */\n  ucp_bidiFSI,  /* First_Strong_Isolate */\n  ucp_bidiL,    /* Left_To_Right */\n  ucp_bidiLRE,  /* Left_To_Right_Embedding */\n  ucp_bidiLRI,  /* Left_To_Right_Isolate */\n  ucp_bidiLRO,  /* Left_To_Right_Override */\n  ucp_bidiNSM,  /* Nonspacing_Mark */\n  ucp_bidiON,   /* Other_Neutral */\n  ucp_bidiPDF,  /* Pop_Directional_Format */\n  ucp_bidiPDI,  /* Pop_Directional_Isolate */\n  ucp_bidiR,    /* Right_To_Left */\n  ucp_bidiRLE,  /* Right_To_Left_Embedding */\n  ucp_bidiRLI,  /* Right_To_Left_Isolate */\n  ucp_bidiRLO,  /* Right_To_Left_Override */\n  ucp_bidiS,    /* Segment_Separator */\n  ucp_bidiWS,   /* White_Space */\n};\n\n/* These are grapheme break properties. The Extended Pictographic property\ncomes from the emoji-data.txt file. */\n\nenum {\n  ucp_gbCR,                    /*  0 */\n  ucp_gbLF,                    /*  1 */\n  ucp_gbControl,               /*  2 */\n  ucp_gbExtend,                /*  3 */\n  ucp_gbPrepend,               /*  4 */\n  ucp_gbSpacingMark,           /*  5 */\n  ucp_gbL,                     /*  6 Hangul syllable type L */\n  ucp_gbV,                     /*  7 Hangul syllable type V */\n  ucp_gbT,                     /*  8 Hangul syllable type T */\n  ucp_gbLV,                    /*  9 Hangul syllable type LV */\n  ucp_gbLVT,                   /* 10 Hangul syllable type LVT */\n  ucp_gbRegional_Indicator,    /* 11 */\n  ucp_gbOther,                 /* 12 */\n  ucp_gbZWJ,                   /* 13 */\n  ucp_gbExtended_Pictographic, /* 14 */\n};\n\n/* These are the script identifications. */\n\nenum {\n  /* Scripts which has characters in other scripts. */\n  ucp_Latin,\n  ucp_Greek,\n  ucp_Cyrillic,\n  ucp_Armenian,\n  ucp_Hebrew,\n  ucp_Arabic,\n  ucp_Syriac,\n  ucp_Thaana,\n  ucp_Devanagari,\n  ucp_Bengali,\n  ucp_Gurmukhi,\n  ucp_Gujarati,\n  ucp_Oriya,\n  ucp_Tamil,\n  ucp_Telugu,\n  ucp_Kannada,\n  ucp_Malayalam,\n  ucp_Sinhala,\n  ucp_Thai,\n  ucp_Tibetan,\n  ucp_Myanmar,\n  ucp_Georgian,\n  ucp_Hangul,\n  ucp_Ethiopic,\n  ucp_Cherokee,\n  ucp_Runic,\n  ucp_Mongolian,\n  ucp_Hiragana,\n  ucp_Katakana,\n  ucp_Bopomofo,\n  ucp_Han,\n  ucp_Yi,\n  ucp_Gothic,\n  ucp_Tagalog,\n  ucp_Hanunoo,\n  ucp_Buhid,\n  ucp_Tagbanwa,\n  ucp_Limbu,\n  ucp_Tai_Le,\n  ucp_Linear_B,\n  ucp_Shavian,\n  ucp_Cypriot,\n  ucp_Buginese,\n  ucp_Coptic,\n  ucp_Glagolitic,\n  ucp_Tifinagh,\n  ucp_Syloti_Nagri,\n  ucp_Phags_Pa,\n  ucp_Nko,\n  ucp_Kayah_Li,\n  ucp_Lycian,\n  ucp_Carian,\n  ucp_Lydian,\n  ucp_Avestan,\n  ucp_Samaritan,\n  ucp_Lisu,\n  ucp_Javanese,\n  ucp_Old_Turkic,\n  ucp_Kaithi,\n  ucp_Mandaic,\n  ucp_Chakma,\n  ucp_Meroitic_Hieroglyphs,\n  ucp_Sharada,\n  ucp_Takri,\n  ucp_Caucasian_Albanian,\n  ucp_Duployan,\n  ucp_Elbasan,\n  ucp_Grantha,\n  ucp_Khojki,\n  ucp_Linear_A,\n  ucp_Mahajani,\n  ucp_Manichaean,\n  ucp_Modi,\n  ucp_Old_Permic,\n  ucp_Psalter_Pahlavi,\n  ucp_Khudawadi,\n  ucp_Tirhuta,\n  ucp_Multani,\n  ucp_Old_Hungarian,\n  ucp_Adlam,\n  ucp_Osage,\n  ucp_Tangut,\n  ucp_Masaram_Gondi,\n  ucp_Dogra,\n  ucp_Gunjala_Gondi,\n  ucp_Hanifi_Rohingya,\n  ucp_Sogdian,\n  ucp_Nandinagari,\n  ucp_Yezidi,\n  ucp_Cypro_Minoan,\n  ucp_Old_Uyghur,\n  ucp_Toto,\n  ucp_Garay,\n  ucp_Gurung_Khema,\n  ucp_Ol_Onal,\n  ucp_Sunuwar,\n  ucp_Todhri,\n  ucp_Tulu_Tigalari,\n\n  /* Scripts which has no characters in other scripts. */\n  ucp_Unknown,\n  ucp_Common,\n  ucp_Lao,\n  ucp_Canadian_Aboriginal,\n  ucp_Ogham,\n  ucp_Khmer,\n  ucp_Old_Italic,\n  ucp_Deseret,\n  ucp_Inherited,\n  ucp_Ugaritic,\n  ucp_Osmanya,\n  ucp_Braille,\n  ucp_New_Tai_Lue,\n  ucp_Old_Persian,\n  ucp_Kharoshthi,\n  ucp_Balinese,\n  ucp_Cuneiform,\n  ucp_Phoenician,\n  ucp_Sundanese,\n  ucp_Lepcha,\n  ucp_Ol_Chiki,\n  ucp_Vai,\n  ucp_Saurashtra,\n  ucp_Rejang,\n  ucp_Cham,\n  ucp_Tai_Tham,\n  ucp_Tai_Viet,\n  ucp_Egyptian_Hieroglyphs,\n  ucp_Bamum,\n  ucp_Meetei_Mayek,\n  ucp_Imperial_Aramaic,\n  ucp_Old_South_Arabian,\n  ucp_Inscriptional_Parthian,\n  ucp_Inscriptional_Pahlavi,\n  ucp_Batak,\n  ucp_Brahmi,\n  ucp_Meroitic_Cursive,\n  ucp_Miao,\n  ucp_Sora_Sompeng,\n  ucp_Bassa_Vah,\n  ucp_Pahawh_Hmong,\n  ucp_Mende_Kikakui,\n  ucp_Mro,\n  ucp_Old_North_Arabian,\n  ucp_Nabataean,\n  ucp_Palmyrene,\n  ucp_Pau_Cin_Hau,\n  ucp_Siddham,\n  ucp_Warang_Citi,\n  ucp_Ahom,\n  ucp_Anatolian_Hieroglyphs,\n  ucp_Hatran,\n  ucp_SignWriting,\n  ucp_Bhaiksuki,\n  ucp_Marchen,\n  ucp_Newa,\n  ucp_Nushu,\n  ucp_Soyombo,\n  ucp_Zanabazar_Square,\n  ucp_Makasar,\n  ucp_Medefaidrin,\n  ucp_Old_Sogdian,\n  ucp_Elymaic,\n  ucp_Nyiakeng_Puachue_Hmong,\n  ucp_Wancho,\n  ucp_Chorasmian,\n  ucp_Dives_Akuru,\n  ucp_Khitan_Small_Script,\n  ucp_Tangsa,\n  ucp_Vithkuqi,\n  ucp_Kawi,\n  ucp_Nag_Mundari,\n  ucp_Kirat_Rai,\n\n  /* This must be last */\n  ucp_Script_Count\n};\n\n/* Size of entries in ucd_script_sets[] */\n\n#define ucd_script_sets_item_size 4\n\n#endif  /* PCRE2_UCP_H_IDEMPOTENT_GUARD */\n\n/* End of pcre2_ucp.h */\n"
  },
  {
    "path": "Libraries/PCRE/pcre2_ucptables.c",
    "content": "/*************************************************\n*      Perl-Compatible Regular Expressions       *\n*************************************************/\n\n/* PCRE is a library of functions to support regular expressions whose syntax\nand semantics are as close as possible to those of the Perl 5 language.\n\n                       Written by Philip Hazel\n     Original API code Copyright (c) 1997-2012 University of Cambridge\n          New API code Copyright (c) 2016-2022 University of Cambridge\n\nThis module is auto-generated from Unicode data files. DO NOT EDIT MANUALLY!\nInstead, modify the maint/GenerateUcpTables.py script and run it to generate\na new version of this code.\n\n-----------------------------------------------------------------------------\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n    * Redistributions of source code must retain the above copyright notice,\n      this list of conditions and the following disclaimer.\n\n    * Redistributions in binary form must reproduce the above copyright\n      notice, this list of conditions and the following disclaimer in the\n      documentation and/or other materials provided with the distribution.\n\n    * Neither the name of the University of Cambridge nor the names of its\n      contributors may be used to endorse or promote products derived from\n      this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\nLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\nCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\nSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\nINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\nCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGE.\n-----------------------------------------------------------------------------\n*/\n\n#ifdef SUPPORT_UNICODE\n\n/* The PRIV(utt)[] table below translates Unicode property names into type and\ncode values. It is searched by binary chop, so must be in collating sequence of\nname. Originally, the table contained pointers to the name strings in the first\nfield of each entry. However, that leads to a large number of relocations when\na shared library is dynamically loaded. A significant reduction is made by\nputting all the names into a single, large string and using offsets instead.\nAll letters are lower cased, and underscores are removed, in accordance with\nthe \"loose matching\" rules that Unicode advises and Perl uses. */\n\n#define STRING_adlam0 STR_a STR_d STR_l STR_a STR_m \"\\0\"\n#define STRING_adlm0 STR_a STR_d STR_l STR_m \"\\0\"\n#define STRING_aghb0 STR_a STR_g STR_h STR_b \"\\0\"\n#define STRING_ahex0 STR_a STR_h STR_e STR_x \"\\0\"\n#define STRING_ahom0 STR_a STR_h STR_o STR_m \"\\0\"\n#define STRING_alpha0 STR_a STR_l STR_p STR_h STR_a \"\\0\"\n#define STRING_alphabetic0 STR_a STR_l STR_p STR_h STR_a STR_b STR_e STR_t STR_i STR_c \"\\0\"\n#define STRING_anatolianhieroglyphs0 STR_a STR_n STR_a STR_t STR_o STR_l STR_i STR_a STR_n STR_h STR_i STR_e STR_r STR_o STR_g STR_l STR_y STR_p STR_h STR_s \"\\0\"\n#define STRING_any0 STR_a STR_n STR_y \"\\0\"\n#define STRING_arab0 STR_a STR_r STR_a STR_b \"\\0\"\n#define STRING_arabic0 STR_a STR_r STR_a STR_b STR_i STR_c \"\\0\"\n#define STRING_armenian0 STR_a STR_r STR_m STR_e STR_n STR_i STR_a STR_n \"\\0\"\n#define STRING_armi0 STR_a STR_r STR_m STR_i \"\\0\"\n#define STRING_armn0 STR_a STR_r STR_m STR_n \"\\0\"\n#define STRING_ascii0 STR_a STR_s STR_c STR_i STR_i \"\\0\"\n#define STRING_asciihexdigit0 STR_a STR_s STR_c STR_i STR_i STR_h STR_e STR_x STR_d STR_i STR_g STR_i STR_t \"\\0\"\n#define STRING_avestan0 STR_a STR_v STR_e STR_s STR_t STR_a STR_n \"\\0\"\n#define STRING_avst0 STR_a STR_v STR_s STR_t \"\\0\"\n#define STRING_bali0 STR_b STR_a STR_l STR_i \"\\0\"\n#define STRING_balinese0 STR_b STR_a STR_l STR_i STR_n STR_e STR_s STR_e \"\\0\"\n#define STRING_bamu0 STR_b STR_a STR_m STR_u \"\\0\"\n#define STRING_bamum0 STR_b STR_a STR_m STR_u STR_m \"\\0\"\n#define STRING_bass0 STR_b STR_a STR_s STR_s \"\\0\"\n#define STRING_bassavah0 STR_b STR_a STR_s STR_s STR_a STR_v STR_a STR_h \"\\0\"\n#define STRING_batak0 STR_b STR_a STR_t STR_a STR_k \"\\0\"\n#define STRING_batk0 STR_b STR_a STR_t STR_k \"\\0\"\n#define STRING_beng0 STR_b STR_e STR_n STR_g \"\\0\"\n#define STRING_bengali0 STR_b STR_e STR_n STR_g STR_a STR_l STR_i \"\\0\"\n#define STRING_bhaiksuki0 STR_b STR_h STR_a STR_i STR_k STR_s STR_u STR_k STR_i \"\\0\"\n#define STRING_bhks0 STR_b STR_h STR_k STR_s \"\\0\"\n#define STRING_bidial0 STR_b STR_i STR_d STR_i STR_a STR_l \"\\0\"\n#define STRING_bidian0 STR_b STR_i STR_d STR_i STR_a STR_n \"\\0\"\n#define STRING_bidib0 STR_b STR_i STR_d STR_i STR_b \"\\0\"\n#define STRING_bidibn0 STR_b STR_i STR_d STR_i STR_b STR_n \"\\0\"\n#define STRING_bidic0 STR_b STR_i STR_d STR_i STR_c \"\\0\"\n#define STRING_bidicontrol0 STR_b STR_i STR_d STR_i STR_c STR_o STR_n STR_t STR_r STR_o STR_l \"\\0\"\n#define STRING_bidics0 STR_b STR_i STR_d STR_i STR_c STR_s \"\\0\"\n#define STRING_bidien0 STR_b STR_i STR_d STR_i STR_e STR_n \"\\0\"\n#define STRING_bidies0 STR_b STR_i STR_d STR_i STR_e STR_s \"\\0\"\n#define STRING_bidiet0 STR_b STR_i STR_d STR_i STR_e STR_t \"\\0\"\n#define STRING_bidifsi0 STR_b STR_i STR_d STR_i STR_f STR_s STR_i \"\\0\"\n#define STRING_bidil0 STR_b STR_i STR_d STR_i STR_l \"\\0\"\n#define STRING_bidilre0 STR_b STR_i STR_d STR_i STR_l STR_r STR_e \"\\0\"\n#define STRING_bidilri0 STR_b STR_i STR_d STR_i STR_l STR_r STR_i \"\\0\"\n#define STRING_bidilro0 STR_b STR_i STR_d STR_i STR_l STR_r STR_o \"\\0\"\n#define STRING_bidim0 STR_b STR_i STR_d STR_i STR_m \"\\0\"\n#define STRING_bidimirrored0 STR_b STR_i STR_d STR_i STR_m STR_i STR_r STR_r STR_o STR_r STR_e STR_d \"\\0\"\n#define STRING_bidinsm0 STR_b STR_i STR_d STR_i STR_n STR_s STR_m \"\\0\"\n#define STRING_bidion0 STR_b STR_i STR_d STR_i STR_o STR_n \"\\0\"\n#define STRING_bidipdf0 STR_b STR_i STR_d STR_i STR_p STR_d STR_f \"\\0\"\n#define STRING_bidipdi0 STR_b STR_i STR_d STR_i STR_p STR_d STR_i \"\\0\"\n#define STRING_bidir0 STR_b STR_i STR_d STR_i STR_r \"\\0\"\n#define STRING_bidirle0 STR_b STR_i STR_d STR_i STR_r STR_l STR_e \"\\0\"\n#define STRING_bidirli0 STR_b STR_i STR_d STR_i STR_r STR_l STR_i \"\\0\"\n#define STRING_bidirlo0 STR_b STR_i STR_d STR_i STR_r STR_l STR_o \"\\0\"\n#define STRING_bidis0 STR_b STR_i STR_d STR_i STR_s \"\\0\"\n#define STRING_bidiws0 STR_b STR_i STR_d STR_i STR_w STR_s \"\\0\"\n#define STRING_bopo0 STR_b STR_o STR_p STR_o \"\\0\"\n#define STRING_bopomofo0 STR_b STR_o STR_p STR_o STR_m STR_o STR_f STR_o \"\\0\"\n#define STRING_brah0 STR_b STR_r STR_a STR_h \"\\0\"\n#define STRING_brahmi0 STR_b STR_r STR_a STR_h STR_m STR_i \"\\0\"\n#define STRING_brai0 STR_b STR_r STR_a STR_i \"\\0\"\n#define STRING_braille0 STR_b STR_r STR_a STR_i STR_l STR_l STR_e \"\\0\"\n#define STRING_bugi0 STR_b STR_u STR_g STR_i \"\\0\"\n#define STRING_buginese0 STR_b STR_u STR_g STR_i STR_n STR_e STR_s STR_e \"\\0\"\n#define STRING_buhd0 STR_b STR_u STR_h STR_d \"\\0\"\n#define STRING_buhid0 STR_b STR_u STR_h STR_i STR_d \"\\0\"\n#define STRING_c0 STR_c \"\\0\"\n#define STRING_cakm0 STR_c STR_a STR_k STR_m \"\\0\"\n#define STRING_canadianaboriginal0 STR_c STR_a STR_n STR_a STR_d STR_i STR_a STR_n STR_a STR_b STR_o STR_r STR_i STR_g STR_i STR_n STR_a STR_l \"\\0\"\n#define STRING_cans0 STR_c STR_a STR_n STR_s \"\\0\"\n#define STRING_cari0 STR_c STR_a STR_r STR_i \"\\0\"\n#define STRING_carian0 STR_c STR_a STR_r STR_i STR_a STR_n \"\\0\"\n#define STRING_cased0 STR_c STR_a STR_s STR_e STR_d \"\\0\"\n#define STRING_caseignorable0 STR_c STR_a STR_s STR_e STR_i STR_g STR_n STR_o STR_r STR_a STR_b STR_l STR_e \"\\0\"\n#define STRING_caucasianalbanian0 STR_c STR_a STR_u STR_c STR_a STR_s STR_i STR_a STR_n STR_a STR_l STR_b STR_a STR_n STR_i STR_a STR_n \"\\0\"\n#define STRING_cc0 STR_c STR_c \"\\0\"\n#define STRING_cf0 STR_c STR_f \"\\0\"\n#define STRING_chakma0 STR_c STR_h STR_a STR_k STR_m STR_a \"\\0\"\n#define STRING_cham0 STR_c STR_h STR_a STR_m \"\\0\"\n#define STRING_changeswhencasefolded0 STR_c STR_h STR_a STR_n STR_g STR_e STR_s STR_w STR_h STR_e STR_n STR_c STR_a STR_s STR_e STR_f STR_o STR_l STR_d STR_e STR_d \"\\0\"\n#define STRING_changeswhencasemapped0 STR_c STR_h STR_a STR_n STR_g STR_e STR_s STR_w STR_h STR_e STR_n STR_c STR_a STR_s STR_e STR_m STR_a STR_p STR_p STR_e STR_d \"\\0\"\n#define STRING_changeswhenlowercased0 STR_c STR_h STR_a STR_n STR_g STR_e STR_s STR_w STR_h STR_e STR_n STR_l STR_o STR_w STR_e STR_r STR_c STR_a STR_s STR_e STR_d \"\\0\"\n#define STRING_changeswhentitlecased0 STR_c STR_h STR_a STR_n STR_g STR_e STR_s STR_w STR_h STR_e STR_n STR_t STR_i STR_t STR_l STR_e STR_c STR_a STR_s STR_e STR_d \"\\0\"\n#define STRING_changeswhenuppercased0 STR_c STR_h STR_a STR_n STR_g STR_e STR_s STR_w STR_h STR_e STR_n STR_u STR_p STR_p STR_e STR_r STR_c STR_a STR_s STR_e STR_d \"\\0\"\n#define STRING_cher0 STR_c STR_h STR_e STR_r \"\\0\"\n#define STRING_cherokee0 STR_c STR_h STR_e STR_r STR_o STR_k STR_e STR_e \"\\0\"\n#define STRING_chorasmian0 STR_c STR_h STR_o STR_r STR_a STR_s STR_m STR_i STR_a STR_n \"\\0\"\n#define STRING_chrs0 STR_c STR_h STR_r STR_s \"\\0\"\n#define STRING_ci0 STR_c STR_i \"\\0\"\n#define STRING_cn0 STR_c STR_n \"\\0\"\n#define STRING_co0 STR_c STR_o \"\\0\"\n#define STRING_common0 STR_c STR_o STR_m STR_m STR_o STR_n \"\\0\"\n#define STRING_copt0 STR_c STR_o STR_p STR_t \"\\0\"\n#define STRING_coptic0 STR_c STR_o STR_p STR_t STR_i STR_c \"\\0\"\n#define STRING_cpmn0 STR_c STR_p STR_m STR_n \"\\0\"\n#define STRING_cprt0 STR_c STR_p STR_r STR_t \"\\0\"\n#define STRING_cs0 STR_c STR_s \"\\0\"\n#define STRING_cuneiform0 STR_c STR_u STR_n STR_e STR_i STR_f STR_o STR_r STR_m \"\\0\"\n#define STRING_cwcf0 STR_c STR_w STR_c STR_f \"\\0\"\n#define STRING_cwcm0 STR_c STR_w STR_c STR_m \"\\0\"\n#define STRING_cwl0 STR_c STR_w STR_l \"\\0\"\n#define STRING_cwt0 STR_c STR_w STR_t \"\\0\"\n#define STRING_cwu0 STR_c STR_w STR_u \"\\0\"\n#define STRING_cypriot0 STR_c STR_y STR_p STR_r STR_i STR_o STR_t \"\\0\"\n#define STRING_cyprominoan0 STR_c STR_y STR_p STR_r STR_o STR_m STR_i STR_n STR_o STR_a STR_n \"\\0\"\n#define STRING_cyrillic0 STR_c STR_y STR_r STR_i STR_l STR_l STR_i STR_c \"\\0\"\n#define STRING_cyrl0 STR_c STR_y STR_r STR_l \"\\0\"\n#define STRING_dash0 STR_d STR_a STR_s STR_h \"\\0\"\n#define STRING_defaultignorablecodepoint0 STR_d STR_e STR_f STR_a STR_u STR_l STR_t STR_i STR_g STR_n STR_o STR_r STR_a STR_b STR_l STR_e STR_c STR_o STR_d STR_e STR_p STR_o STR_i STR_n STR_t \"\\0\"\n#define STRING_dep0 STR_d STR_e STR_p \"\\0\"\n#define STRING_deprecated0 STR_d STR_e STR_p STR_r STR_e STR_c STR_a STR_t STR_e STR_d \"\\0\"\n#define STRING_deseret0 STR_d STR_e STR_s STR_e STR_r STR_e STR_t \"\\0\"\n#define STRING_deva0 STR_d STR_e STR_v STR_a \"\\0\"\n#define STRING_devanagari0 STR_d STR_e STR_v STR_a STR_n STR_a STR_g STR_a STR_r STR_i \"\\0\"\n#define STRING_di0 STR_d STR_i \"\\0\"\n#define STRING_dia0 STR_d STR_i STR_a \"\\0\"\n#define STRING_diacritic0 STR_d STR_i STR_a STR_c STR_r STR_i STR_t STR_i STR_c \"\\0\"\n#define STRING_diak0 STR_d STR_i STR_a STR_k \"\\0\"\n#define STRING_divesakuru0 STR_d STR_i STR_v STR_e STR_s STR_a STR_k STR_u STR_r STR_u \"\\0\"\n#define STRING_dogr0 STR_d STR_o STR_g STR_r \"\\0\"\n#define STRING_dogra0 STR_d STR_o STR_g STR_r STR_a \"\\0\"\n#define STRING_dsrt0 STR_d STR_s STR_r STR_t \"\\0\"\n#define STRING_dupl0 STR_d STR_u STR_p STR_l \"\\0\"\n#define STRING_duployan0 STR_d STR_u STR_p STR_l STR_o STR_y STR_a STR_n \"\\0\"\n#define STRING_ebase0 STR_e STR_b STR_a STR_s STR_e \"\\0\"\n#define STRING_ecomp0 STR_e STR_c STR_o STR_m STR_p \"\\0\"\n#define STRING_egyp0 STR_e STR_g STR_y STR_p \"\\0\"\n#define STRING_egyptianhieroglyphs0 STR_e STR_g STR_y STR_p STR_t STR_i STR_a STR_n STR_h STR_i STR_e STR_r STR_o STR_g STR_l STR_y STR_p STR_h STR_s \"\\0\"\n#define STRING_elba0 STR_e STR_l STR_b STR_a \"\\0\"\n#define STRING_elbasan0 STR_e STR_l STR_b STR_a STR_s STR_a STR_n \"\\0\"\n#define STRING_elym0 STR_e STR_l STR_y STR_m \"\\0\"\n#define STRING_elymaic0 STR_e STR_l STR_y STR_m STR_a STR_i STR_c \"\\0\"\n#define STRING_emod0 STR_e STR_m STR_o STR_d \"\\0\"\n#define STRING_emoji0 STR_e STR_m STR_o STR_j STR_i \"\\0\"\n#define STRING_emojicomponent0 STR_e STR_m STR_o STR_j STR_i STR_c STR_o STR_m STR_p STR_o STR_n STR_e STR_n STR_t \"\\0\"\n#define STRING_emojimodifier0 STR_e STR_m STR_o STR_j STR_i STR_m STR_o STR_d STR_i STR_f STR_i STR_e STR_r \"\\0\"\n#define STRING_emojimodifierbase0 STR_e STR_m STR_o STR_j STR_i STR_m STR_o STR_d STR_i STR_f STR_i STR_e STR_r STR_b STR_a STR_s STR_e \"\\0\"\n#define STRING_emojipresentation0 STR_e STR_m STR_o STR_j STR_i STR_p STR_r STR_e STR_s STR_e STR_n STR_t STR_a STR_t STR_i STR_o STR_n \"\\0\"\n#define STRING_epres0 STR_e STR_p STR_r STR_e STR_s \"\\0\"\n#define STRING_ethi0 STR_e STR_t STR_h STR_i \"\\0\"\n#define STRING_ethiopic0 STR_e STR_t STR_h STR_i STR_o STR_p STR_i STR_c \"\\0\"\n#define STRING_ext0 STR_e STR_x STR_t \"\\0\"\n#define STRING_extendedpictographic0 STR_e STR_x STR_t STR_e STR_n STR_d STR_e STR_d STR_p STR_i STR_c STR_t STR_o STR_g STR_r STR_a STR_p STR_h STR_i STR_c \"\\0\"\n#define STRING_extender0 STR_e STR_x STR_t STR_e STR_n STR_d STR_e STR_r \"\\0\"\n#define STRING_extpict0 STR_e STR_x STR_t STR_p STR_i STR_c STR_t \"\\0\"\n#define STRING_gara0 STR_g STR_a STR_r STR_a \"\\0\"\n#define STRING_garay0 STR_g STR_a STR_r STR_a STR_y \"\\0\"\n#define STRING_geor0 STR_g STR_e STR_o STR_r \"\\0\"\n#define STRING_georgian0 STR_g STR_e STR_o STR_r STR_g STR_i STR_a STR_n \"\\0\"\n#define STRING_glag0 STR_g STR_l STR_a STR_g \"\\0\"\n#define STRING_glagolitic0 STR_g STR_l STR_a STR_g STR_o STR_l STR_i STR_t STR_i STR_c \"\\0\"\n#define STRING_gong0 STR_g STR_o STR_n STR_g \"\\0\"\n#define STRING_gonm0 STR_g STR_o STR_n STR_m \"\\0\"\n#define STRING_goth0 STR_g STR_o STR_t STR_h \"\\0\"\n#define STRING_gothic0 STR_g STR_o STR_t STR_h STR_i STR_c \"\\0\"\n#define STRING_gran0 STR_g STR_r STR_a STR_n \"\\0\"\n#define STRING_grantha0 STR_g STR_r STR_a STR_n STR_t STR_h STR_a \"\\0\"\n#define STRING_graphemebase0 STR_g STR_r STR_a STR_p STR_h STR_e STR_m STR_e STR_b STR_a STR_s STR_e \"\\0\"\n#define STRING_graphemeextend0 STR_g STR_r STR_a STR_p STR_h STR_e STR_m STR_e STR_e STR_x STR_t STR_e STR_n STR_d \"\\0\"\n#define STRING_graphemelink0 STR_g STR_r STR_a STR_p STR_h STR_e STR_m STR_e STR_l STR_i STR_n STR_k \"\\0\"\n#define STRING_grbase0 STR_g STR_r STR_b STR_a STR_s STR_e \"\\0\"\n#define STRING_greek0 STR_g STR_r STR_e STR_e STR_k \"\\0\"\n#define STRING_grek0 STR_g STR_r STR_e STR_k \"\\0\"\n#define STRING_grext0 STR_g STR_r STR_e STR_x STR_t \"\\0\"\n#define STRING_grlink0 STR_g STR_r STR_l STR_i STR_n STR_k \"\\0\"\n#define STRING_gujarati0 STR_g STR_u STR_j STR_a STR_r STR_a STR_t STR_i \"\\0\"\n#define STRING_gujr0 STR_g STR_u STR_j STR_r \"\\0\"\n#define STRING_gukh0 STR_g STR_u STR_k STR_h \"\\0\"\n#define STRING_gunjalagondi0 STR_g STR_u STR_n STR_j STR_a STR_l STR_a STR_g STR_o STR_n STR_d STR_i \"\\0\"\n#define STRING_gurmukhi0 STR_g STR_u STR_r STR_m STR_u STR_k STR_h STR_i \"\\0\"\n#define STRING_guru0 STR_g STR_u STR_r STR_u \"\\0\"\n#define STRING_gurungkhema0 STR_g STR_u STR_r STR_u STR_n STR_g STR_k STR_h STR_e STR_m STR_a \"\\0\"\n#define STRING_han0 STR_h STR_a STR_n \"\\0\"\n#define STRING_hang0 STR_h STR_a STR_n STR_g \"\\0\"\n#define STRING_hangul0 STR_h STR_a STR_n STR_g STR_u STR_l \"\\0\"\n#define STRING_hani0 STR_h STR_a STR_n STR_i \"\\0\"\n#define STRING_hanifirohingya0 STR_h STR_a STR_n STR_i STR_f STR_i STR_r STR_o STR_h STR_i STR_n STR_g STR_y STR_a \"\\0\"\n#define STRING_hano0 STR_h STR_a STR_n STR_o \"\\0\"\n#define STRING_hanunoo0 STR_h STR_a STR_n STR_u STR_n STR_o STR_o \"\\0\"\n#define STRING_hatr0 STR_h STR_a STR_t STR_r \"\\0\"\n#define STRING_hatran0 STR_h STR_a STR_t STR_r STR_a STR_n \"\\0\"\n#define STRING_hebr0 STR_h STR_e STR_b STR_r \"\\0\"\n#define STRING_hebrew0 STR_h STR_e STR_b STR_r STR_e STR_w \"\\0\"\n#define STRING_hex0 STR_h STR_e STR_x \"\\0\"\n#define STRING_hexdigit0 STR_h STR_e STR_x STR_d STR_i STR_g STR_i STR_t \"\\0\"\n#define STRING_hira0 STR_h STR_i STR_r STR_a \"\\0\"\n#define STRING_hiragana0 STR_h STR_i STR_r STR_a STR_g STR_a STR_n STR_a \"\\0\"\n#define STRING_hluw0 STR_h STR_l STR_u STR_w \"\\0\"\n#define STRING_hmng0 STR_h STR_m STR_n STR_g \"\\0\"\n#define STRING_hmnp0 STR_h STR_m STR_n STR_p \"\\0\"\n#define STRING_hung0 STR_h STR_u STR_n STR_g \"\\0\"\n#define STRING_idc0 STR_i STR_d STR_c \"\\0\"\n#define STRING_idcompatmathcontinue0 STR_i STR_d STR_c STR_o STR_m STR_p STR_a STR_t STR_m STR_a STR_t STR_h STR_c STR_o STR_n STR_t STR_i STR_n STR_u STR_e \"\\0\"\n#define STRING_idcompatmathstart0 STR_i STR_d STR_c STR_o STR_m STR_p STR_a STR_t STR_m STR_a STR_t STR_h STR_s STR_t STR_a STR_r STR_t \"\\0\"\n#define STRING_idcontinue0 STR_i STR_d STR_c STR_o STR_n STR_t STR_i STR_n STR_u STR_e \"\\0\"\n#define STRING_ideo0 STR_i STR_d STR_e STR_o \"\\0\"\n#define STRING_ideographic0 STR_i STR_d STR_e STR_o STR_g STR_r STR_a STR_p STR_h STR_i STR_c \"\\0\"\n#define STRING_ids0 STR_i STR_d STR_s \"\\0\"\n#define STRING_idsb0 STR_i STR_d STR_s STR_b \"\\0\"\n#define STRING_idsbinaryoperator0 STR_i STR_d STR_s STR_b STR_i STR_n STR_a STR_r STR_y STR_o STR_p STR_e STR_r STR_a STR_t STR_o STR_r \"\\0\"\n#define STRING_idst0 STR_i STR_d STR_s STR_t \"\\0\"\n#define STRING_idstart0 STR_i STR_d STR_s STR_t STR_a STR_r STR_t \"\\0\"\n#define STRING_idstrinaryoperator0 STR_i STR_d STR_s STR_t STR_r STR_i STR_n STR_a STR_r STR_y STR_o STR_p STR_e STR_r STR_a STR_t STR_o STR_r \"\\0\"\n#define STRING_idsu0 STR_i STR_d STR_s STR_u \"\\0\"\n#define STRING_idsunaryoperator0 STR_i STR_d STR_s STR_u STR_n STR_a STR_r STR_y STR_o STR_p STR_e STR_r STR_a STR_t STR_o STR_r \"\\0\"\n#define STRING_imperialaramaic0 STR_i STR_m STR_p STR_e STR_r STR_i STR_a STR_l STR_a STR_r STR_a STR_m STR_a STR_i STR_c \"\\0\"\n#define STRING_incb0 STR_i STR_n STR_c STR_b \"\\0\"\n#define STRING_inherited0 STR_i STR_n STR_h STR_e STR_r STR_i STR_t STR_e STR_d \"\\0\"\n#define STRING_inscriptionalpahlavi0 STR_i STR_n STR_s STR_c STR_r STR_i STR_p STR_t STR_i STR_o STR_n STR_a STR_l STR_p STR_a STR_h STR_l STR_a STR_v STR_i \"\\0\"\n#define STRING_inscriptionalparthian0 STR_i STR_n STR_s STR_c STR_r STR_i STR_p STR_t STR_i STR_o STR_n STR_a STR_l STR_p STR_a STR_r STR_t STR_h STR_i STR_a STR_n \"\\0\"\n#define STRING_ital0 STR_i STR_t STR_a STR_l \"\\0\"\n#define STRING_java0 STR_j STR_a STR_v STR_a \"\\0\"\n#define STRING_javanese0 STR_j STR_a STR_v STR_a STR_n STR_e STR_s STR_e \"\\0\"\n#define STRING_joinc0 STR_j STR_o STR_i STR_n STR_c \"\\0\"\n#define STRING_joincontrol0 STR_j STR_o STR_i STR_n STR_c STR_o STR_n STR_t STR_r STR_o STR_l \"\\0\"\n#define STRING_kaithi0 STR_k STR_a STR_i STR_t STR_h STR_i \"\\0\"\n#define STRING_kali0 STR_k STR_a STR_l STR_i \"\\0\"\n#define STRING_kana0 STR_k STR_a STR_n STR_a \"\\0\"\n#define STRING_kannada0 STR_k STR_a STR_n STR_n STR_a STR_d STR_a \"\\0\"\n#define STRING_katakana0 STR_k STR_a STR_t STR_a STR_k STR_a STR_n STR_a \"\\0\"\n#define STRING_kawi0 STR_k STR_a STR_w STR_i \"\\0\"\n#define STRING_kayahli0 STR_k STR_a STR_y STR_a STR_h STR_l STR_i \"\\0\"\n#define STRING_khar0 STR_k STR_h STR_a STR_r \"\\0\"\n#define STRING_kharoshthi0 STR_k STR_h STR_a STR_r STR_o STR_s STR_h STR_t STR_h STR_i \"\\0\"\n#define STRING_khitansmallscript0 STR_k STR_h STR_i STR_t STR_a STR_n STR_s STR_m STR_a STR_l STR_l STR_s STR_c STR_r STR_i STR_p STR_t \"\\0\"\n#define STRING_khmer0 STR_k STR_h STR_m STR_e STR_r \"\\0\"\n#define STRING_khmr0 STR_k STR_h STR_m STR_r \"\\0\"\n#define STRING_khoj0 STR_k STR_h STR_o STR_j \"\\0\"\n#define STRING_khojki0 STR_k STR_h STR_o STR_j STR_k STR_i \"\\0\"\n#define STRING_khudawadi0 STR_k STR_h STR_u STR_d STR_a STR_w STR_a STR_d STR_i \"\\0\"\n#define STRING_kiratrai0 STR_k STR_i STR_r STR_a STR_t STR_r STR_a STR_i \"\\0\"\n#define STRING_kits0 STR_k STR_i STR_t STR_s \"\\0\"\n#define STRING_knda0 STR_k STR_n STR_d STR_a \"\\0\"\n#define STRING_krai0 STR_k STR_r STR_a STR_i \"\\0\"\n#define STRING_kthi0 STR_k STR_t STR_h STR_i \"\\0\"\n#define STRING_l0 STR_l \"\\0\"\n#define STRING_l_AMPERSAND0 STR_l STR_AMPERSAND \"\\0\"\n#define STRING_lana0 STR_l STR_a STR_n STR_a \"\\0\"\n#define STRING_lao0 STR_l STR_a STR_o \"\\0\"\n#define STRING_laoo0 STR_l STR_a STR_o STR_o \"\\0\"\n#define STRING_latin0 STR_l STR_a STR_t STR_i STR_n \"\\0\"\n#define STRING_latn0 STR_l STR_a STR_t STR_n \"\\0\"\n#define STRING_lc0 STR_l STR_c \"\\0\"\n#define STRING_lepc0 STR_l STR_e STR_p STR_c \"\\0\"\n#define STRING_lepcha0 STR_l STR_e STR_p STR_c STR_h STR_a \"\\0\"\n#define STRING_limb0 STR_l STR_i STR_m STR_b \"\\0\"\n#define STRING_limbu0 STR_l STR_i STR_m STR_b STR_u \"\\0\"\n#define STRING_lina0 STR_l STR_i STR_n STR_a \"\\0\"\n#define STRING_linb0 STR_l STR_i STR_n STR_b \"\\0\"\n#define STRING_lineara0 STR_l STR_i STR_n STR_e STR_a STR_r STR_a \"\\0\"\n#define STRING_linearb0 STR_l STR_i STR_n STR_e STR_a STR_r STR_b \"\\0\"\n#define STRING_lisu0 STR_l STR_i STR_s STR_u \"\\0\"\n#define STRING_ll0 STR_l STR_l \"\\0\"\n#define STRING_lm0 STR_l STR_m \"\\0\"\n#define STRING_lo0 STR_l STR_o \"\\0\"\n#define STRING_loe0 STR_l STR_o STR_e \"\\0\"\n#define STRING_logicalorderexception0 STR_l STR_o STR_g STR_i STR_c STR_a STR_l STR_o STR_r STR_d STR_e STR_r STR_e STR_x STR_c STR_e STR_p STR_t STR_i STR_o STR_n \"\\0\"\n#define STRING_lower0 STR_l STR_o STR_w STR_e STR_r \"\\0\"\n#define STRING_lowercase0 STR_l STR_o STR_w STR_e STR_r STR_c STR_a STR_s STR_e \"\\0\"\n#define STRING_lt0 STR_l STR_t \"\\0\"\n#define STRING_lu0 STR_l STR_u \"\\0\"\n#define STRING_lyci0 STR_l STR_y STR_c STR_i \"\\0\"\n#define STRING_lycian0 STR_l STR_y STR_c STR_i STR_a STR_n \"\\0\"\n#define STRING_lydi0 STR_l STR_y STR_d STR_i \"\\0\"\n#define STRING_lydian0 STR_l STR_y STR_d STR_i STR_a STR_n \"\\0\"\n#define STRING_m0 STR_m \"\\0\"\n#define STRING_mahajani0 STR_m STR_a STR_h STR_a STR_j STR_a STR_n STR_i \"\\0\"\n#define STRING_mahj0 STR_m STR_a STR_h STR_j \"\\0\"\n#define STRING_maka0 STR_m STR_a STR_k STR_a \"\\0\"\n#define STRING_makasar0 STR_m STR_a STR_k STR_a STR_s STR_a STR_r \"\\0\"\n#define STRING_malayalam0 STR_m STR_a STR_l STR_a STR_y STR_a STR_l STR_a STR_m \"\\0\"\n#define STRING_mand0 STR_m STR_a STR_n STR_d \"\\0\"\n#define STRING_mandaic0 STR_m STR_a STR_n STR_d STR_a STR_i STR_c \"\\0\"\n#define STRING_mani0 STR_m STR_a STR_n STR_i \"\\0\"\n#define STRING_manichaean0 STR_m STR_a STR_n STR_i STR_c STR_h STR_a STR_e STR_a STR_n \"\\0\"\n#define STRING_marc0 STR_m STR_a STR_r STR_c \"\\0\"\n#define STRING_marchen0 STR_m STR_a STR_r STR_c STR_h STR_e STR_n \"\\0\"\n#define STRING_masaramgondi0 STR_m STR_a STR_s STR_a STR_r STR_a STR_m STR_g STR_o STR_n STR_d STR_i \"\\0\"\n#define STRING_math0 STR_m STR_a STR_t STR_h \"\\0\"\n#define STRING_mc0 STR_m STR_c \"\\0\"\n#define STRING_mcm0 STR_m STR_c STR_m \"\\0\"\n#define STRING_me0 STR_m STR_e \"\\0\"\n#define STRING_medefaidrin0 STR_m STR_e STR_d STR_e STR_f STR_a STR_i STR_d STR_r STR_i STR_n \"\\0\"\n#define STRING_medf0 STR_m STR_e STR_d STR_f \"\\0\"\n#define STRING_meeteimayek0 STR_m STR_e STR_e STR_t STR_e STR_i STR_m STR_a STR_y STR_e STR_k \"\\0\"\n#define STRING_mend0 STR_m STR_e STR_n STR_d \"\\0\"\n#define STRING_mendekikakui0 STR_m STR_e STR_n STR_d STR_e STR_k STR_i STR_k STR_a STR_k STR_u STR_i \"\\0\"\n#define STRING_merc0 STR_m STR_e STR_r STR_c \"\\0\"\n#define STRING_mero0 STR_m STR_e STR_r STR_o \"\\0\"\n#define STRING_meroiticcursive0 STR_m STR_e STR_r STR_o STR_i STR_t STR_i STR_c STR_c STR_u STR_r STR_s STR_i STR_v STR_e \"\\0\"\n#define STRING_meroitichieroglyphs0 STR_m STR_e STR_r STR_o STR_i STR_t STR_i STR_c STR_h STR_i STR_e STR_r STR_o STR_g STR_l STR_y STR_p STR_h STR_s \"\\0\"\n#define STRING_miao0 STR_m STR_i STR_a STR_o \"\\0\"\n#define STRING_mlym0 STR_m STR_l STR_y STR_m \"\\0\"\n#define STRING_mn0 STR_m STR_n \"\\0\"\n#define STRING_modi0 STR_m STR_o STR_d STR_i \"\\0\"\n#define STRING_modifiercombiningmark0 STR_m STR_o STR_d STR_i STR_f STR_i STR_e STR_r STR_c STR_o STR_m STR_b STR_i STR_n STR_i STR_n STR_g STR_m STR_a STR_r STR_k \"\\0\"\n#define STRING_mong0 STR_m STR_o STR_n STR_g \"\\0\"\n#define STRING_mongolian0 STR_m STR_o STR_n STR_g STR_o STR_l STR_i STR_a STR_n \"\\0\"\n#define STRING_mro0 STR_m STR_r STR_o \"\\0\"\n#define STRING_mroo0 STR_m STR_r STR_o STR_o \"\\0\"\n#define STRING_mtei0 STR_m STR_t STR_e STR_i \"\\0\"\n#define STRING_mult0 STR_m STR_u STR_l STR_t \"\\0\"\n#define STRING_multani0 STR_m STR_u STR_l STR_t STR_a STR_n STR_i \"\\0\"\n#define STRING_myanmar0 STR_m STR_y STR_a STR_n STR_m STR_a STR_r \"\\0\"\n#define STRING_mymr0 STR_m STR_y STR_m STR_r \"\\0\"\n#define STRING_n0 STR_n \"\\0\"\n#define STRING_nabataean0 STR_n STR_a STR_b STR_a STR_t STR_a STR_e STR_a STR_n \"\\0\"\n#define STRING_nagm0 STR_n STR_a STR_g STR_m \"\\0\"\n#define STRING_nagmundari0 STR_n STR_a STR_g STR_m STR_u STR_n STR_d STR_a STR_r STR_i \"\\0\"\n#define STRING_nand0 STR_n STR_a STR_n STR_d \"\\0\"\n#define STRING_nandinagari0 STR_n STR_a STR_n STR_d STR_i STR_n STR_a STR_g STR_a STR_r STR_i \"\\0\"\n#define STRING_narb0 STR_n STR_a STR_r STR_b \"\\0\"\n#define STRING_nbat0 STR_n STR_b STR_a STR_t \"\\0\"\n#define STRING_nchar0 STR_n STR_c STR_h STR_a STR_r \"\\0\"\n#define STRING_nd0 STR_n STR_d \"\\0\"\n#define STRING_newa0 STR_n STR_e STR_w STR_a \"\\0\"\n#define STRING_newtailue0 STR_n STR_e STR_w STR_t STR_a STR_i STR_l STR_u STR_e \"\\0\"\n#define STRING_nko0 STR_n STR_k STR_o \"\\0\"\n#define STRING_nkoo0 STR_n STR_k STR_o STR_o \"\\0\"\n#define STRING_nl0 STR_n STR_l \"\\0\"\n#define STRING_no0 STR_n STR_o \"\\0\"\n#define STRING_noncharactercodepoint0 STR_n STR_o STR_n STR_c STR_h STR_a STR_r STR_a STR_c STR_t STR_e STR_r STR_c STR_o STR_d STR_e STR_p STR_o STR_i STR_n STR_t \"\\0\"\n#define STRING_nshu0 STR_n STR_s STR_h STR_u \"\\0\"\n#define STRING_nushu0 STR_n STR_u STR_s STR_h STR_u \"\\0\"\n#define STRING_nyiakengpuachuehmong0 STR_n STR_y STR_i STR_a STR_k STR_e STR_n STR_g STR_p STR_u STR_a STR_c STR_h STR_u STR_e STR_h STR_m STR_o STR_n STR_g \"\\0\"\n#define STRING_ogam0 STR_o STR_g STR_a STR_m \"\\0\"\n#define STRING_ogham0 STR_o STR_g STR_h STR_a STR_m \"\\0\"\n#define STRING_olchiki0 STR_o STR_l STR_c STR_h STR_i STR_k STR_i \"\\0\"\n#define STRING_olck0 STR_o STR_l STR_c STR_k \"\\0\"\n#define STRING_oldhungarian0 STR_o STR_l STR_d STR_h STR_u STR_n STR_g STR_a STR_r STR_i STR_a STR_n \"\\0\"\n#define STRING_olditalic0 STR_o STR_l STR_d STR_i STR_t STR_a STR_l STR_i STR_c \"\\0\"\n#define STRING_oldnortharabian0 STR_o STR_l STR_d STR_n STR_o STR_r STR_t STR_h STR_a STR_r STR_a STR_b STR_i STR_a STR_n \"\\0\"\n#define STRING_oldpermic0 STR_o STR_l STR_d STR_p STR_e STR_r STR_m STR_i STR_c \"\\0\"\n#define STRING_oldpersian0 STR_o STR_l STR_d STR_p STR_e STR_r STR_s STR_i STR_a STR_n \"\\0\"\n#define STRING_oldsogdian0 STR_o STR_l STR_d STR_s STR_o STR_g STR_d STR_i STR_a STR_n \"\\0\"\n#define STRING_oldsoutharabian0 STR_o STR_l STR_d STR_s STR_o STR_u STR_t STR_h STR_a STR_r STR_a STR_b STR_i STR_a STR_n \"\\0\"\n#define STRING_oldturkic0 STR_o STR_l STR_d STR_t STR_u STR_r STR_k STR_i STR_c \"\\0\"\n#define STRING_olduyghur0 STR_o STR_l STR_d STR_u STR_y STR_g STR_h STR_u STR_r \"\\0\"\n#define STRING_olonal0 STR_o STR_l STR_o STR_n STR_a STR_l \"\\0\"\n#define STRING_onao0 STR_o STR_n STR_a STR_o \"\\0\"\n#define STRING_oriya0 STR_o STR_r STR_i STR_y STR_a \"\\0\"\n#define STRING_orkh0 STR_o STR_r STR_k STR_h \"\\0\"\n#define STRING_orya0 STR_o STR_r STR_y STR_a \"\\0\"\n#define STRING_osage0 STR_o STR_s STR_a STR_g STR_e \"\\0\"\n#define STRING_osge0 STR_o STR_s STR_g STR_e \"\\0\"\n#define STRING_osma0 STR_o STR_s STR_m STR_a \"\\0\"\n#define STRING_osmanya0 STR_o STR_s STR_m STR_a STR_n STR_y STR_a \"\\0\"\n#define STRING_ougr0 STR_o STR_u STR_g STR_r \"\\0\"\n#define STRING_p0 STR_p \"\\0\"\n#define STRING_pahawhhmong0 STR_p STR_a STR_h STR_a STR_w STR_h STR_h STR_m STR_o STR_n STR_g \"\\0\"\n#define STRING_palm0 STR_p STR_a STR_l STR_m \"\\0\"\n#define STRING_palmyrene0 STR_p STR_a STR_l STR_m STR_y STR_r STR_e STR_n STR_e \"\\0\"\n#define STRING_patsyn0 STR_p STR_a STR_t STR_s STR_y STR_n \"\\0\"\n#define STRING_patternsyntax0 STR_p STR_a STR_t STR_t STR_e STR_r STR_n STR_s STR_y STR_n STR_t STR_a STR_x \"\\0\"\n#define STRING_patternwhitespace0 STR_p STR_a STR_t STR_t STR_e STR_r STR_n STR_w STR_h STR_i STR_t STR_e STR_s STR_p STR_a STR_c STR_e \"\\0\"\n#define STRING_patws0 STR_p STR_a STR_t STR_w STR_s \"\\0\"\n#define STRING_pauc0 STR_p STR_a STR_u STR_c \"\\0\"\n#define STRING_paucinhau0 STR_p STR_a STR_u STR_c STR_i STR_n STR_h STR_a STR_u \"\\0\"\n#define STRING_pc0 STR_p STR_c \"\\0\"\n#define STRING_pcm0 STR_p STR_c STR_m \"\\0\"\n#define STRING_pd0 STR_p STR_d \"\\0\"\n#define STRING_pe0 STR_p STR_e \"\\0\"\n#define STRING_perm0 STR_p STR_e STR_r STR_m \"\\0\"\n#define STRING_pf0 STR_p STR_f \"\\0\"\n#define STRING_phag0 STR_p STR_h STR_a STR_g \"\\0\"\n#define STRING_phagspa0 STR_p STR_h STR_a STR_g STR_s STR_p STR_a \"\\0\"\n#define STRING_phli0 STR_p STR_h STR_l STR_i \"\\0\"\n#define STRING_phlp0 STR_p STR_h STR_l STR_p \"\\0\"\n#define STRING_phnx0 STR_p STR_h STR_n STR_x \"\\0\"\n#define STRING_phoenician0 STR_p STR_h STR_o STR_e STR_n STR_i STR_c STR_i STR_a STR_n \"\\0\"\n#define STRING_pi0 STR_p STR_i \"\\0\"\n#define STRING_plrd0 STR_p STR_l STR_r STR_d \"\\0\"\n#define STRING_po0 STR_p STR_o \"\\0\"\n#define STRING_prependedconcatenationmark0 STR_p STR_r STR_e STR_p STR_e STR_n STR_d STR_e STR_d STR_c STR_o STR_n STR_c STR_a STR_t STR_e STR_n STR_a STR_t STR_i STR_o STR_n STR_m STR_a STR_r STR_k \"\\0\"\n#define STRING_prti0 STR_p STR_r STR_t STR_i \"\\0\"\n#define STRING_ps0 STR_p STR_s \"\\0\"\n#define STRING_psalterpahlavi0 STR_p STR_s STR_a STR_l STR_t STR_e STR_r STR_p STR_a STR_h STR_l STR_a STR_v STR_i \"\\0\"\n#define STRING_qaac0 STR_q STR_a STR_a STR_c \"\\0\"\n#define STRING_qaai0 STR_q STR_a STR_a STR_i \"\\0\"\n#define STRING_qmark0 STR_q STR_m STR_a STR_r STR_k \"\\0\"\n#define STRING_quotationmark0 STR_q STR_u STR_o STR_t STR_a STR_t STR_i STR_o STR_n STR_m STR_a STR_r STR_k \"\\0\"\n#define STRING_radical0 STR_r STR_a STR_d STR_i STR_c STR_a STR_l \"\\0\"\n#define STRING_regionalindicator0 STR_r STR_e STR_g STR_i STR_o STR_n STR_a STR_l STR_i STR_n STR_d STR_i STR_c STR_a STR_t STR_o STR_r \"\\0\"\n#define STRING_rejang0 STR_r STR_e STR_j STR_a STR_n STR_g \"\\0\"\n#define STRING_ri0 STR_r STR_i \"\\0\"\n#define STRING_rjng0 STR_r STR_j STR_n STR_g \"\\0\"\n#define STRING_rohg0 STR_r STR_o STR_h STR_g \"\\0\"\n#define STRING_runic0 STR_r STR_u STR_n STR_i STR_c \"\\0\"\n#define STRING_runr0 STR_r STR_u STR_n STR_r \"\\0\"\n#define STRING_s0 STR_s \"\\0\"\n#define STRING_samaritan0 STR_s STR_a STR_m STR_a STR_r STR_i STR_t STR_a STR_n \"\\0\"\n#define STRING_samr0 STR_s STR_a STR_m STR_r \"\\0\"\n#define STRING_sarb0 STR_s STR_a STR_r STR_b \"\\0\"\n#define STRING_saur0 STR_s STR_a STR_u STR_r \"\\0\"\n#define STRING_saurashtra0 STR_s STR_a STR_u STR_r STR_a STR_s STR_h STR_t STR_r STR_a \"\\0\"\n#define STRING_sc0 STR_s STR_c \"\\0\"\n#define STRING_sd0 STR_s STR_d \"\\0\"\n#define STRING_sentenceterminal0 STR_s STR_e STR_n STR_t STR_e STR_n STR_c STR_e STR_t STR_e STR_r STR_m STR_i STR_n STR_a STR_l \"\\0\"\n#define STRING_sgnw0 STR_s STR_g STR_n STR_w \"\\0\"\n#define STRING_sharada0 STR_s STR_h STR_a STR_r STR_a STR_d STR_a \"\\0\"\n#define STRING_shavian0 STR_s STR_h STR_a STR_v STR_i STR_a STR_n \"\\0\"\n#define STRING_shaw0 STR_s STR_h STR_a STR_w \"\\0\"\n#define STRING_shrd0 STR_s STR_h STR_r STR_d \"\\0\"\n#define STRING_sidd0 STR_s STR_i STR_d STR_d \"\\0\"\n#define STRING_siddham0 STR_s STR_i STR_d STR_d STR_h STR_a STR_m \"\\0\"\n#define STRING_signwriting0 STR_s STR_i STR_g STR_n STR_w STR_r STR_i STR_t STR_i STR_n STR_g \"\\0\"\n#define STRING_sind0 STR_s STR_i STR_n STR_d \"\\0\"\n#define STRING_sinh0 STR_s STR_i STR_n STR_h \"\\0\"\n#define STRING_sinhala0 STR_s STR_i STR_n STR_h STR_a STR_l STR_a \"\\0\"\n#define STRING_sk0 STR_s STR_k \"\\0\"\n#define STRING_sm0 STR_s STR_m \"\\0\"\n#define STRING_so0 STR_s STR_o \"\\0\"\n#define STRING_softdotted0 STR_s STR_o STR_f STR_t STR_d STR_o STR_t STR_t STR_e STR_d \"\\0\"\n#define STRING_sogd0 STR_s STR_o STR_g STR_d \"\\0\"\n#define STRING_sogdian0 STR_s STR_o STR_g STR_d STR_i STR_a STR_n \"\\0\"\n#define STRING_sogo0 STR_s STR_o STR_g STR_o \"\\0\"\n#define STRING_sora0 STR_s STR_o STR_r STR_a \"\\0\"\n#define STRING_sorasompeng0 STR_s STR_o STR_r STR_a STR_s STR_o STR_m STR_p STR_e STR_n STR_g \"\\0\"\n#define STRING_soyo0 STR_s STR_o STR_y STR_o \"\\0\"\n#define STRING_soyombo0 STR_s STR_o STR_y STR_o STR_m STR_b STR_o \"\\0\"\n#define STRING_space0 STR_s STR_p STR_a STR_c STR_e \"\\0\"\n#define STRING_sterm0 STR_s STR_t STR_e STR_r STR_m \"\\0\"\n#define STRING_sund0 STR_s STR_u STR_n STR_d \"\\0\"\n#define STRING_sundanese0 STR_s STR_u STR_n STR_d STR_a STR_n STR_e STR_s STR_e \"\\0\"\n#define STRING_sunu0 STR_s STR_u STR_n STR_u \"\\0\"\n#define STRING_sunuwar0 STR_s STR_u STR_n STR_u STR_w STR_a STR_r \"\\0\"\n#define STRING_sylo0 STR_s STR_y STR_l STR_o \"\\0\"\n#define STRING_sylotinagri0 STR_s STR_y STR_l STR_o STR_t STR_i STR_n STR_a STR_g STR_r STR_i \"\\0\"\n#define STRING_syrc0 STR_s STR_y STR_r STR_c \"\\0\"\n#define STRING_syriac0 STR_s STR_y STR_r STR_i STR_a STR_c \"\\0\"\n#define STRING_tagalog0 STR_t STR_a STR_g STR_a STR_l STR_o STR_g \"\\0\"\n#define STRING_tagb0 STR_t STR_a STR_g STR_b \"\\0\"\n#define STRING_tagbanwa0 STR_t STR_a STR_g STR_b STR_a STR_n STR_w STR_a \"\\0\"\n#define STRING_taile0 STR_t STR_a STR_i STR_l STR_e \"\\0\"\n#define STRING_taitham0 STR_t STR_a STR_i STR_t STR_h STR_a STR_m \"\\0\"\n#define STRING_taiviet0 STR_t STR_a STR_i STR_v STR_i STR_e STR_t \"\\0\"\n#define STRING_takr0 STR_t STR_a STR_k STR_r \"\\0\"\n#define STRING_takri0 STR_t STR_a STR_k STR_r STR_i \"\\0\"\n#define STRING_tale0 STR_t STR_a STR_l STR_e \"\\0\"\n#define STRING_talu0 STR_t STR_a STR_l STR_u \"\\0\"\n#define STRING_tamil0 STR_t STR_a STR_m STR_i STR_l \"\\0\"\n#define STRING_taml0 STR_t STR_a STR_m STR_l \"\\0\"\n#define STRING_tang0 STR_t STR_a STR_n STR_g \"\\0\"\n#define STRING_tangsa0 STR_t STR_a STR_n STR_g STR_s STR_a \"\\0\"\n#define STRING_tangut0 STR_t STR_a STR_n STR_g STR_u STR_t \"\\0\"\n#define STRING_tavt0 STR_t STR_a STR_v STR_t \"\\0\"\n#define STRING_telu0 STR_t STR_e STR_l STR_u \"\\0\"\n#define STRING_telugu0 STR_t STR_e STR_l STR_u STR_g STR_u \"\\0\"\n#define STRING_term0 STR_t STR_e STR_r STR_m \"\\0\"\n#define STRING_terminalpunctuation0 STR_t STR_e STR_r STR_m STR_i STR_n STR_a STR_l STR_p STR_u STR_n STR_c STR_t STR_u STR_a STR_t STR_i STR_o STR_n \"\\0\"\n#define STRING_tfng0 STR_t STR_f STR_n STR_g \"\\0\"\n#define STRING_tglg0 STR_t STR_g STR_l STR_g \"\\0\"\n#define STRING_thaa0 STR_t STR_h STR_a STR_a \"\\0\"\n#define STRING_thaana0 STR_t STR_h STR_a STR_a STR_n STR_a \"\\0\"\n#define STRING_thai0 STR_t STR_h STR_a STR_i \"\\0\"\n#define STRING_tibetan0 STR_t STR_i STR_b STR_e STR_t STR_a STR_n \"\\0\"\n#define STRING_tibt0 STR_t STR_i STR_b STR_t \"\\0\"\n#define STRING_tifinagh0 STR_t STR_i STR_f STR_i STR_n STR_a STR_g STR_h \"\\0\"\n#define STRING_tirh0 STR_t STR_i STR_r STR_h \"\\0\"\n#define STRING_tirhuta0 STR_t STR_i STR_r STR_h STR_u STR_t STR_a \"\\0\"\n#define STRING_tnsa0 STR_t STR_n STR_s STR_a \"\\0\"\n#define STRING_todhri0 STR_t STR_o STR_d STR_h STR_r STR_i \"\\0\"\n#define STRING_todr0 STR_t STR_o STR_d STR_r \"\\0\"\n#define STRING_toto0 STR_t STR_o STR_t STR_o \"\\0\"\n#define STRING_tulutigalari0 STR_t STR_u STR_l STR_u STR_t STR_i STR_g STR_a STR_l STR_a STR_r STR_i \"\\0\"\n#define STRING_tutg0 STR_t STR_u STR_t STR_g \"\\0\"\n#define STRING_ugar0 STR_u STR_g STR_a STR_r \"\\0\"\n#define STRING_ugaritic0 STR_u STR_g STR_a STR_r STR_i STR_t STR_i STR_c \"\\0\"\n#define STRING_uideo0 STR_u STR_i STR_d STR_e STR_o \"\\0\"\n#define STRING_unifiedideograph0 STR_u STR_n STR_i STR_f STR_i STR_e STR_d STR_i STR_d STR_e STR_o STR_g STR_r STR_a STR_p STR_h \"\\0\"\n#define STRING_unknown0 STR_u STR_n STR_k STR_n STR_o STR_w STR_n \"\\0\"\n#define STRING_upper0 STR_u STR_p STR_p STR_e STR_r \"\\0\"\n#define STRING_uppercase0 STR_u STR_p STR_p STR_e STR_r STR_c STR_a STR_s STR_e \"\\0\"\n#define STRING_vai0 STR_v STR_a STR_i \"\\0\"\n#define STRING_vaii0 STR_v STR_a STR_i STR_i \"\\0\"\n#define STRING_variationselector0 STR_v STR_a STR_r STR_i STR_a STR_t STR_i STR_o STR_n STR_s STR_e STR_l STR_e STR_c STR_t STR_o STR_r \"\\0\"\n#define STRING_vith0 STR_v STR_i STR_t STR_h \"\\0\"\n#define STRING_vithkuqi0 STR_v STR_i STR_t STR_h STR_k STR_u STR_q STR_i \"\\0\"\n#define STRING_vs0 STR_v STR_s \"\\0\"\n#define STRING_wancho0 STR_w STR_a STR_n STR_c STR_h STR_o \"\\0\"\n#define STRING_wara0 STR_w STR_a STR_r STR_a \"\\0\"\n#define STRING_warangciti0 STR_w STR_a STR_r STR_a STR_n STR_g STR_c STR_i STR_t STR_i \"\\0\"\n#define STRING_wcho0 STR_w STR_c STR_h STR_o \"\\0\"\n#define STRING_whitespace0 STR_w STR_h STR_i STR_t STR_e STR_s STR_p STR_a STR_c STR_e \"\\0\"\n#define STRING_wspace0 STR_w STR_s STR_p STR_a STR_c STR_e \"\\0\"\n#define STRING_xan0 STR_x STR_a STR_n \"\\0\"\n#define STRING_xidc0 STR_x STR_i STR_d STR_c \"\\0\"\n#define STRING_xidcontinue0 STR_x STR_i STR_d STR_c STR_o STR_n STR_t STR_i STR_n STR_u STR_e \"\\0\"\n#define STRING_xids0 STR_x STR_i STR_d STR_s \"\\0\"\n#define STRING_xidstart0 STR_x STR_i STR_d STR_s STR_t STR_a STR_r STR_t \"\\0\"\n#define STRING_xpeo0 STR_x STR_p STR_e STR_o \"\\0\"\n#define STRING_xps0 STR_x STR_p STR_s \"\\0\"\n#define STRING_xsp0 STR_x STR_s STR_p \"\\0\"\n#define STRING_xsux0 STR_x STR_s STR_u STR_x \"\\0\"\n#define STRING_xuc0 STR_x STR_u STR_c \"\\0\"\n#define STRING_xwd0 STR_x STR_w STR_d \"\\0\"\n#define STRING_yezi0 STR_y STR_e STR_z STR_i \"\\0\"\n#define STRING_yezidi0 STR_y STR_e STR_z STR_i STR_d STR_i \"\\0\"\n#define STRING_yi0 STR_y STR_i \"\\0\"\n#define STRING_yiii0 STR_y STR_i STR_i STR_i \"\\0\"\n#define STRING_z0 STR_z \"\\0\"\n#define STRING_zanabazarsquare0 STR_z STR_a STR_n STR_a STR_b STR_a STR_z STR_a STR_r STR_s STR_q STR_u STR_a STR_r STR_e \"\\0\"\n#define STRING_zanb0 STR_z STR_a STR_n STR_b \"\\0\"\n#define STRING_zinh0 STR_z STR_i STR_n STR_h \"\\0\"\n#define STRING_zl0 STR_z STR_l \"\\0\"\n#define STRING_zp0 STR_z STR_p \"\\0\"\n#define STRING_zs0 STR_z STR_s \"\\0\"\n#define STRING_zyyy0 STR_z STR_y STR_y STR_y \"\\0\"\n#define STRING_zzzz0 STR_z STR_z STR_z STR_z \"\\0\"\n\nconst char PRIV(utt_names)[] =\n  STRING_adlam0\n  STRING_adlm0\n  STRING_aghb0\n  STRING_ahex0\n  STRING_ahom0\n  STRING_alpha0\n  STRING_alphabetic0\n  STRING_anatolianhieroglyphs0\n  STRING_any0\n  STRING_arab0\n  STRING_arabic0\n  STRING_armenian0\n  STRING_armi0\n  STRING_armn0\n  STRING_ascii0\n  STRING_asciihexdigit0\n  STRING_avestan0\n  STRING_avst0\n  STRING_bali0\n  STRING_balinese0\n  STRING_bamu0\n  STRING_bamum0\n  STRING_bass0\n  STRING_bassavah0\n  STRING_batak0\n  STRING_batk0\n  STRING_beng0\n  STRING_bengali0\n  STRING_bhaiksuki0\n  STRING_bhks0\n  STRING_bidial0\n  STRING_bidian0\n  STRING_bidib0\n  STRING_bidibn0\n  STRING_bidic0\n  STRING_bidicontrol0\n  STRING_bidics0\n  STRING_bidien0\n  STRING_bidies0\n  STRING_bidiet0\n  STRING_bidifsi0\n  STRING_bidil0\n  STRING_bidilre0\n  STRING_bidilri0\n  STRING_bidilro0\n  STRING_bidim0\n  STRING_bidimirrored0\n  STRING_bidinsm0\n  STRING_bidion0\n  STRING_bidipdf0\n  STRING_bidipdi0\n  STRING_bidir0\n  STRING_bidirle0\n  STRING_bidirli0\n  STRING_bidirlo0\n  STRING_bidis0\n  STRING_bidiws0\n  STRING_bopo0\n  STRING_bopomofo0\n  STRING_brah0\n  STRING_brahmi0\n  STRING_brai0\n  STRING_braille0\n  STRING_bugi0\n  STRING_buginese0\n  STRING_buhd0\n  STRING_buhid0\n  STRING_c0\n  STRING_cakm0\n  STRING_canadianaboriginal0\n  STRING_cans0\n  STRING_cari0\n  STRING_carian0\n  STRING_cased0\n  STRING_caseignorable0\n  STRING_caucasianalbanian0\n  STRING_cc0\n  STRING_cf0\n  STRING_chakma0\n  STRING_cham0\n  STRING_changeswhencasefolded0\n  STRING_changeswhencasemapped0\n  STRING_changeswhenlowercased0\n  STRING_changeswhentitlecased0\n  STRING_changeswhenuppercased0\n  STRING_cher0\n  STRING_cherokee0\n  STRING_chorasmian0\n  STRING_chrs0\n  STRING_ci0\n  STRING_cn0\n  STRING_co0\n  STRING_common0\n  STRING_copt0\n  STRING_coptic0\n  STRING_cpmn0\n  STRING_cprt0\n  STRING_cs0\n  STRING_cuneiform0\n  STRING_cwcf0\n  STRING_cwcm0\n  STRING_cwl0\n  STRING_cwt0\n  STRING_cwu0\n  STRING_cypriot0\n  STRING_cyprominoan0\n  STRING_cyrillic0\n  STRING_cyrl0\n  STRING_dash0\n  STRING_defaultignorablecodepoint0\n  STRING_dep0\n  STRING_deprecated0\n  STRING_deseret0\n  STRING_deva0\n  STRING_devanagari0\n  STRING_di0\n  STRING_dia0\n  STRING_diacritic0\n  STRING_diak0\n  STRING_divesakuru0\n  STRING_dogr0\n  STRING_dogra0\n  STRING_dsrt0\n  STRING_dupl0\n  STRING_duployan0\n  STRING_ebase0\n  STRING_ecomp0\n  STRING_egyp0\n  STRING_egyptianhieroglyphs0\n  STRING_elba0\n  STRING_elbasan0\n  STRING_elym0\n  STRING_elymaic0\n  STRING_emod0\n  STRING_emoji0\n  STRING_emojicomponent0\n  STRING_emojimodifier0\n  STRING_emojimodifierbase0\n  STRING_emojipresentation0\n  STRING_epres0\n  STRING_ethi0\n  STRING_ethiopic0\n  STRING_ext0\n  STRING_extendedpictographic0\n  STRING_extender0\n  STRING_extpict0\n  STRING_gara0\n  STRING_garay0\n  STRING_geor0\n  STRING_georgian0\n  STRING_glag0\n  STRING_glagolitic0\n  STRING_gong0\n  STRING_gonm0\n  STRING_goth0\n  STRING_gothic0\n  STRING_gran0\n  STRING_grantha0\n  STRING_graphemebase0\n  STRING_graphemeextend0\n  STRING_graphemelink0\n  STRING_grbase0\n  STRING_greek0\n  STRING_grek0\n  STRING_grext0\n  STRING_grlink0\n  STRING_gujarati0\n  STRING_gujr0\n  STRING_gukh0\n  STRING_gunjalagondi0\n  STRING_gurmukhi0\n  STRING_guru0\n  STRING_gurungkhema0\n  STRING_han0\n  STRING_hang0\n  STRING_hangul0\n  STRING_hani0\n  STRING_hanifirohingya0\n  STRING_hano0\n  STRING_hanunoo0\n  STRING_hatr0\n  STRING_hatran0\n  STRING_hebr0\n  STRING_hebrew0\n  STRING_hex0\n  STRING_hexdigit0\n  STRING_hira0\n  STRING_hiragana0\n  STRING_hluw0\n  STRING_hmng0\n  STRING_hmnp0\n  STRING_hung0\n  STRING_idc0\n  STRING_idcompatmathcontinue0\n  STRING_idcompatmathstart0\n  STRING_idcontinue0\n  STRING_ideo0\n  STRING_ideographic0\n  STRING_ids0\n  STRING_idsb0\n  STRING_idsbinaryoperator0\n  STRING_idst0\n  STRING_idstart0\n  STRING_idstrinaryoperator0\n  STRING_idsu0\n  STRING_idsunaryoperator0\n  STRING_imperialaramaic0\n  STRING_incb0\n  STRING_inherited0\n  STRING_inscriptionalpahlavi0\n  STRING_inscriptionalparthian0\n  STRING_ital0\n  STRING_java0\n  STRING_javanese0\n  STRING_joinc0\n  STRING_joincontrol0\n  STRING_kaithi0\n  STRING_kali0\n  STRING_kana0\n  STRING_kannada0\n  STRING_katakana0\n  STRING_kawi0\n  STRING_kayahli0\n  STRING_khar0\n  STRING_kharoshthi0\n  STRING_khitansmallscript0\n  STRING_khmer0\n  STRING_khmr0\n  STRING_khoj0\n  STRING_khojki0\n  STRING_khudawadi0\n  STRING_kiratrai0\n  STRING_kits0\n  STRING_knda0\n  STRING_krai0\n  STRING_kthi0\n  STRING_l0\n  STRING_l_AMPERSAND0\n  STRING_lana0\n  STRING_lao0\n  STRING_laoo0\n  STRING_latin0\n  STRING_latn0\n  STRING_lc0\n  STRING_lepc0\n  STRING_lepcha0\n  STRING_limb0\n  STRING_limbu0\n  STRING_lina0\n  STRING_linb0\n  STRING_lineara0\n  STRING_linearb0\n  STRING_lisu0\n  STRING_ll0\n  STRING_lm0\n  STRING_lo0\n  STRING_loe0\n  STRING_logicalorderexception0\n  STRING_lower0\n  STRING_lowercase0\n  STRING_lt0\n  STRING_lu0\n  STRING_lyci0\n  STRING_lycian0\n  STRING_lydi0\n  STRING_lydian0\n  STRING_m0\n  STRING_mahajani0\n  STRING_mahj0\n  STRING_maka0\n  STRING_makasar0\n  STRING_malayalam0\n  STRING_mand0\n  STRING_mandaic0\n  STRING_mani0\n  STRING_manichaean0\n  STRING_marc0\n  STRING_marchen0\n  STRING_masaramgondi0\n  STRING_math0\n  STRING_mc0\n  STRING_mcm0\n  STRING_me0\n  STRING_medefaidrin0\n  STRING_medf0\n  STRING_meeteimayek0\n  STRING_mend0\n  STRING_mendekikakui0\n  STRING_merc0\n  STRING_mero0\n  STRING_meroiticcursive0\n  STRING_meroitichieroglyphs0\n  STRING_miao0\n  STRING_mlym0\n  STRING_mn0\n  STRING_modi0\n  STRING_modifiercombiningmark0\n  STRING_mong0\n  STRING_mongolian0\n  STRING_mro0\n  STRING_mroo0\n  STRING_mtei0\n  STRING_mult0\n  STRING_multani0\n  STRING_myanmar0\n  STRING_mymr0\n  STRING_n0\n  STRING_nabataean0\n  STRING_nagm0\n  STRING_nagmundari0\n  STRING_nand0\n  STRING_nandinagari0\n  STRING_narb0\n  STRING_nbat0\n  STRING_nchar0\n  STRING_nd0\n  STRING_newa0\n  STRING_newtailue0\n  STRING_nko0\n  STRING_nkoo0\n  STRING_nl0\n  STRING_no0\n  STRING_noncharactercodepoint0\n  STRING_nshu0\n  STRING_nushu0\n  STRING_nyiakengpuachuehmong0\n  STRING_ogam0\n  STRING_ogham0\n  STRING_olchiki0\n  STRING_olck0\n  STRING_oldhungarian0\n  STRING_olditalic0\n  STRING_oldnortharabian0\n  STRING_oldpermic0\n  STRING_oldpersian0\n  STRING_oldsogdian0\n  STRING_oldsoutharabian0\n  STRING_oldturkic0\n  STRING_olduyghur0\n  STRING_olonal0\n  STRING_onao0\n  STRING_oriya0\n  STRING_orkh0\n  STRING_orya0\n  STRING_osage0\n  STRING_osge0\n  STRING_osma0\n  STRING_osmanya0\n  STRING_ougr0\n  STRING_p0\n  STRING_pahawhhmong0\n  STRING_palm0\n  STRING_palmyrene0\n  STRING_patsyn0\n  STRING_patternsyntax0\n  STRING_patternwhitespace0\n  STRING_patws0\n  STRING_pauc0\n  STRING_paucinhau0\n  STRING_pc0\n  STRING_pcm0\n  STRING_pd0\n  STRING_pe0\n  STRING_perm0\n  STRING_pf0\n  STRING_phag0\n  STRING_phagspa0\n  STRING_phli0\n  STRING_phlp0\n  STRING_phnx0\n  STRING_phoenician0\n  STRING_pi0\n  STRING_plrd0\n  STRING_po0\n  STRING_prependedconcatenationmark0\n  STRING_prti0\n  STRING_ps0\n  STRING_psalterpahlavi0\n  STRING_qaac0\n  STRING_qaai0\n  STRING_qmark0\n  STRING_quotationmark0\n  STRING_radical0\n  STRING_regionalindicator0\n  STRING_rejang0\n  STRING_ri0\n  STRING_rjng0\n  STRING_rohg0\n  STRING_runic0\n  STRING_runr0\n  STRING_s0\n  STRING_samaritan0\n  STRING_samr0\n  STRING_sarb0\n  STRING_saur0\n  STRING_saurashtra0\n  STRING_sc0\n  STRING_sd0\n  STRING_sentenceterminal0\n  STRING_sgnw0\n  STRING_sharada0\n  STRING_shavian0\n  STRING_shaw0\n  STRING_shrd0\n  STRING_sidd0\n  STRING_siddham0\n  STRING_signwriting0\n  STRING_sind0\n  STRING_sinh0\n  STRING_sinhala0\n  STRING_sk0\n  STRING_sm0\n  STRING_so0\n  STRING_softdotted0\n  STRING_sogd0\n  STRING_sogdian0\n  STRING_sogo0\n  STRING_sora0\n  STRING_sorasompeng0\n  STRING_soyo0\n  STRING_soyombo0\n  STRING_space0\n  STRING_sterm0\n  STRING_sund0\n  STRING_sundanese0\n  STRING_sunu0\n  STRING_sunuwar0\n  STRING_sylo0\n  STRING_sylotinagri0\n  STRING_syrc0\n  STRING_syriac0\n  STRING_tagalog0\n  STRING_tagb0\n  STRING_tagbanwa0\n  STRING_taile0\n  STRING_taitham0\n  STRING_taiviet0\n  STRING_takr0\n  STRING_takri0\n  STRING_tale0\n  STRING_talu0\n  STRING_tamil0\n  STRING_taml0\n  STRING_tang0\n  STRING_tangsa0\n  STRING_tangut0\n  STRING_tavt0\n  STRING_telu0\n  STRING_telugu0\n  STRING_term0\n  STRING_terminalpunctuation0\n  STRING_tfng0\n  STRING_tglg0\n  STRING_thaa0\n  STRING_thaana0\n  STRING_thai0\n  STRING_tibetan0\n  STRING_tibt0\n  STRING_tifinagh0\n  STRING_tirh0\n  STRING_tirhuta0\n  STRING_tnsa0\n  STRING_todhri0\n  STRING_todr0\n  STRING_toto0\n  STRING_tulutigalari0\n  STRING_tutg0\n  STRING_ugar0\n  STRING_ugaritic0\n  STRING_uideo0\n  STRING_unifiedideograph0\n  STRING_unknown0\n  STRING_upper0\n  STRING_uppercase0\n  STRING_vai0\n  STRING_vaii0\n  STRING_variationselector0\n  STRING_vith0\n  STRING_vithkuqi0\n  STRING_vs0\n  STRING_wancho0\n  STRING_wara0\n  STRING_warangciti0\n  STRING_wcho0\n  STRING_whitespace0\n  STRING_wspace0\n  STRING_xan0\n  STRING_xidc0\n  STRING_xidcontinue0\n  STRING_xids0\n  STRING_xidstart0\n  STRING_xpeo0\n  STRING_xps0\n  STRING_xsp0\n  STRING_xsux0\n  STRING_xuc0\n  STRING_xwd0\n  STRING_yezi0\n  STRING_yezidi0\n  STRING_yi0\n  STRING_yiii0\n  STRING_z0\n  STRING_zanabazarsquare0\n  STRING_zanb0\n  STRING_zinh0\n  STRING_zl0\n  STRING_zp0\n  STRING_zs0\n  STRING_zyyy0\n  STRING_zzzz0;\n\nconst ucp_type_table PRIV(utt)[] = {\n  {   0, PT_SCX, ucp_Adlam },\n  {   6, PT_SCX, ucp_Adlam },\n  {  11, PT_SCX, ucp_Caucasian_Albanian },\n  {  16, PT_BOOL, ucp_ASCII_Hex_Digit },\n  {  21, PT_SC, ucp_Ahom },\n  {  26, PT_BOOL, ucp_Alphabetic },\n  {  32, PT_BOOL, ucp_Alphabetic },\n  {  43, PT_SC, ucp_Anatolian_Hieroglyphs },\n  {  64, PT_ANY, 0 },\n  {  68, PT_SCX, ucp_Arabic },\n  {  73, PT_SCX, ucp_Arabic },\n  {  80, PT_SCX, ucp_Armenian },\n  {  89, PT_SC, ucp_Imperial_Aramaic },\n  {  94, PT_SCX, ucp_Armenian },\n  {  99, PT_BOOL, ucp_ASCII },\n  { 105, PT_BOOL, ucp_ASCII_Hex_Digit },\n  { 119, PT_SCX, ucp_Avestan },\n  { 127, PT_SCX, ucp_Avestan },\n  { 132, PT_SC, ucp_Balinese },\n  { 137, PT_SC, ucp_Balinese },\n  { 146, PT_SC, ucp_Bamum },\n  { 151, PT_SC, ucp_Bamum },\n  { 157, PT_SC, ucp_Bassa_Vah },\n  { 162, PT_SC, ucp_Bassa_Vah },\n  { 171, PT_SC, ucp_Batak },\n  { 177, PT_SC, ucp_Batak },\n  { 182, PT_SCX, ucp_Bengali },\n  { 187, PT_SCX, ucp_Bengali },\n  { 195, PT_SC, ucp_Bhaiksuki },\n  { 205, PT_SC, ucp_Bhaiksuki },\n  { 210, PT_BIDICL, ucp_bidiAL },\n  { 217, PT_BIDICL, ucp_bidiAN },\n  { 224, PT_BIDICL, ucp_bidiB },\n  { 230, PT_BIDICL, ucp_bidiBN },\n  { 237, PT_BOOL, ucp_Bidi_Control },\n  { 243, PT_BOOL, ucp_Bidi_Control },\n  { 255, PT_BIDICL, ucp_bidiCS },\n  { 262, PT_BIDICL, ucp_bidiEN },\n  { 269, PT_BIDICL, ucp_bidiES },\n  { 276, PT_BIDICL, ucp_bidiET },\n  { 283, PT_BIDICL, ucp_bidiFSI },\n  { 291, PT_BIDICL, ucp_bidiL },\n  { 297, PT_BIDICL, ucp_bidiLRE },\n  { 305, PT_BIDICL, ucp_bidiLRI },\n  { 313, PT_BIDICL, ucp_bidiLRO },\n  { 321, PT_BOOL, ucp_Bidi_Mirrored },\n  { 327, PT_BOOL, ucp_Bidi_Mirrored },\n  { 340, PT_BIDICL, ucp_bidiNSM },\n  { 348, PT_BIDICL, ucp_bidiON },\n  { 355, PT_BIDICL, ucp_bidiPDF },\n  { 363, PT_BIDICL, ucp_bidiPDI },\n  { 371, PT_BIDICL, ucp_bidiR },\n  { 377, PT_BIDICL, ucp_bidiRLE },\n  { 385, PT_BIDICL, ucp_bidiRLI },\n  { 393, PT_BIDICL, ucp_bidiRLO },\n  { 401, PT_BIDICL, ucp_bidiS },\n  { 407, PT_BIDICL, ucp_bidiWS },\n  { 414, PT_SCX, ucp_Bopomofo },\n  { 419, PT_SCX, ucp_Bopomofo },\n  { 428, PT_SC, ucp_Brahmi },\n  { 433, PT_SC, ucp_Brahmi },\n  { 440, PT_SC, ucp_Braille },\n  { 445, PT_SC, ucp_Braille },\n  { 453, PT_SCX, ucp_Buginese },\n  { 458, PT_SCX, ucp_Buginese },\n  { 467, PT_SCX, ucp_Buhid },\n  { 472, PT_SCX, ucp_Buhid },\n  { 478, PT_GC, ucp_C },\n  { 480, PT_SCX, ucp_Chakma },\n  { 485, PT_SC, ucp_Canadian_Aboriginal },\n  { 504, PT_SC, ucp_Canadian_Aboriginal },\n  { 509, PT_SCX, ucp_Carian },\n  { 514, PT_SCX, ucp_Carian },\n  { 521, PT_BOOL, ucp_Cased },\n  { 527, PT_BOOL, ucp_Case_Ignorable },\n  { 541, PT_SCX, ucp_Caucasian_Albanian },\n  { 559, PT_PC, ucp_Cc },\n  { 562, PT_PC, ucp_Cf },\n  { 565, PT_SCX, ucp_Chakma },\n  { 572, PT_SC, ucp_Cham },\n  { 577, PT_BOOL, ucp_Changes_When_Casefolded },\n  { 599, PT_BOOL, ucp_Changes_When_Casemapped },\n  { 621, PT_BOOL, ucp_Changes_When_Lowercased },\n  { 643, PT_BOOL, ucp_Changes_When_Titlecased },\n  { 665, PT_BOOL, ucp_Changes_When_Uppercased },\n  { 687, PT_SCX, ucp_Cherokee },\n  { 692, PT_SCX, ucp_Cherokee },\n  { 701, PT_SC, ucp_Chorasmian },\n  { 712, PT_SC, ucp_Chorasmian },\n  { 717, PT_BOOL, ucp_Case_Ignorable },\n  { 720, PT_PC, ucp_Cn },\n  { 723, PT_PC, ucp_Co },\n  { 726, PT_SC, ucp_Common },\n  { 733, PT_SCX, ucp_Coptic },\n  { 738, PT_SCX, ucp_Coptic },\n  { 745, PT_SCX, ucp_Cypro_Minoan },\n  { 750, PT_SCX, ucp_Cypriot },\n  { 755, PT_PC, ucp_Cs },\n  { 758, PT_SC, ucp_Cuneiform },\n  { 768, PT_BOOL, ucp_Changes_When_Casefolded },\n  { 773, PT_BOOL, ucp_Changes_When_Casemapped },\n  { 778, PT_BOOL, ucp_Changes_When_Lowercased },\n  { 782, PT_BOOL, ucp_Changes_When_Titlecased },\n  { 786, PT_BOOL, ucp_Changes_When_Uppercased },\n  { 790, PT_SCX, ucp_Cypriot },\n  { 798, PT_SCX, ucp_Cypro_Minoan },\n  { 810, PT_SCX, ucp_Cyrillic },\n  { 819, PT_SCX, ucp_Cyrillic },\n  { 824, PT_BOOL, ucp_Dash },\n  { 829, PT_BOOL, ucp_Default_Ignorable_Code_Point },\n  { 855, PT_BOOL, ucp_Deprecated },\n  { 859, PT_BOOL, ucp_Deprecated },\n  { 870, PT_SC, ucp_Deseret },\n  { 878, PT_SCX, ucp_Devanagari },\n  { 883, PT_SCX, ucp_Devanagari },\n  { 894, PT_BOOL, ucp_Default_Ignorable_Code_Point },\n  { 897, PT_BOOL, ucp_Diacritic },\n  { 901, PT_BOOL, ucp_Diacritic },\n  { 911, PT_SC, ucp_Dives_Akuru },\n  { 916, PT_SC, ucp_Dives_Akuru },\n  { 927, PT_SCX, ucp_Dogra },\n  { 932, PT_SCX, ucp_Dogra },\n  { 938, PT_SC, ucp_Deseret },\n  { 943, PT_SCX, ucp_Duployan },\n  { 948, PT_SCX, ucp_Duployan },\n  { 957, PT_BOOL, ucp_Emoji_Modifier_Base },\n  { 963, PT_BOOL, ucp_Emoji_Component },\n  { 969, PT_SC, ucp_Egyptian_Hieroglyphs },\n  { 974, PT_SC, ucp_Egyptian_Hieroglyphs },\n  { 994, PT_SCX, ucp_Elbasan },\n  { 999, PT_SCX, ucp_Elbasan },\n  { 1007, PT_SC, ucp_Elymaic },\n  { 1012, PT_SC, ucp_Elymaic },\n  { 1020, PT_BOOL, ucp_Emoji_Modifier },\n  { 1025, PT_BOOL, ucp_Emoji },\n  { 1031, PT_BOOL, ucp_Emoji_Component },\n  { 1046, PT_BOOL, ucp_Emoji_Modifier },\n  { 1060, PT_BOOL, ucp_Emoji_Modifier_Base },\n  { 1078, PT_BOOL, ucp_Emoji_Presentation },\n  { 1096, PT_BOOL, ucp_Emoji_Presentation },\n  { 1102, PT_SCX, ucp_Ethiopic },\n  { 1107, PT_SCX, ucp_Ethiopic },\n  { 1116, PT_BOOL, ucp_Extender },\n  { 1120, PT_BOOL, ucp_Extended_Pictographic },\n  { 1141, PT_BOOL, ucp_Extender },\n  { 1150, PT_BOOL, ucp_Extended_Pictographic },\n  { 1158, PT_SCX, ucp_Garay },\n  { 1163, PT_SCX, ucp_Garay },\n  { 1169, PT_SCX, ucp_Georgian },\n  { 1174, PT_SCX, ucp_Georgian },\n  { 1183, PT_SCX, ucp_Glagolitic },\n  { 1188, PT_SCX, ucp_Glagolitic },\n  { 1199, PT_SCX, ucp_Gunjala_Gondi },\n  { 1204, PT_SCX, ucp_Masaram_Gondi },\n  { 1209, PT_SCX, ucp_Gothic },\n  { 1214, PT_SCX, ucp_Gothic },\n  { 1221, PT_SCX, ucp_Grantha },\n  { 1226, PT_SCX, ucp_Grantha },\n  { 1234, PT_BOOL, ucp_Grapheme_Base },\n  { 1247, PT_BOOL, ucp_Grapheme_Extend },\n  { 1262, PT_BOOL, ucp_Grapheme_Link },\n  { 1275, PT_BOOL, ucp_Grapheme_Base },\n  { 1282, PT_SCX, ucp_Greek },\n  { 1288, PT_SCX, ucp_Greek },\n  { 1293, PT_BOOL, ucp_Grapheme_Extend },\n  { 1299, PT_BOOL, ucp_Grapheme_Link },\n  { 1306, PT_SCX, ucp_Gujarati },\n  { 1315, PT_SCX, ucp_Gujarati },\n  { 1320, PT_SCX, ucp_Gurung_Khema },\n  { 1325, PT_SCX, ucp_Gunjala_Gondi },\n  { 1338, PT_SCX, ucp_Gurmukhi },\n  { 1347, PT_SCX, ucp_Gurmukhi },\n  { 1352, PT_SCX, ucp_Gurung_Khema },\n  { 1364, PT_SCX, ucp_Han },\n  { 1368, PT_SCX, ucp_Hangul },\n  { 1373, PT_SCX, ucp_Hangul },\n  { 1380, PT_SCX, ucp_Han },\n  { 1385, PT_SCX, ucp_Hanifi_Rohingya },\n  { 1400, PT_SCX, ucp_Hanunoo },\n  { 1405, PT_SCX, ucp_Hanunoo },\n  { 1413, PT_SC, ucp_Hatran },\n  { 1418, PT_SC, ucp_Hatran },\n  { 1425, PT_SCX, ucp_Hebrew },\n  { 1430, PT_SCX, ucp_Hebrew },\n  { 1437, PT_BOOL, ucp_Hex_Digit },\n  { 1441, PT_BOOL, ucp_Hex_Digit },\n  { 1450, PT_SCX, ucp_Hiragana },\n  { 1455, PT_SCX, ucp_Hiragana },\n  { 1464, PT_SC, ucp_Anatolian_Hieroglyphs },\n  { 1469, PT_SC, ucp_Pahawh_Hmong },\n  { 1474, PT_SC, ucp_Nyiakeng_Puachue_Hmong },\n  { 1479, PT_SCX, ucp_Old_Hungarian },\n  { 1484, PT_BOOL, ucp_ID_Continue },\n  { 1488, PT_BOOL, ucp_ID_Compat_Math_Continue },\n  { 1509, PT_BOOL, ucp_ID_Compat_Math_Start },\n  { 1527, PT_BOOL, ucp_ID_Continue },\n  { 1538, PT_BOOL, ucp_Ideographic },\n  { 1543, PT_BOOL, ucp_Ideographic },\n  { 1555, PT_BOOL, ucp_ID_Start },\n  { 1559, PT_BOOL, ucp_IDS_Binary_Operator },\n  { 1564, PT_BOOL, ucp_IDS_Binary_Operator },\n  { 1582, PT_BOOL, ucp_IDS_Trinary_Operator },\n  { 1587, PT_BOOL, ucp_ID_Start },\n  { 1595, PT_BOOL, ucp_IDS_Trinary_Operator },\n  { 1614, PT_BOOL, ucp_IDS_Unary_Operator },\n  { 1619, PT_BOOL, ucp_IDS_Unary_Operator },\n  { 1636, PT_SC, ucp_Imperial_Aramaic },\n  { 1652, PT_BOOL, ucp_InCB },\n  { 1657, PT_SC, ucp_Inherited },\n  { 1667, PT_SC, ucp_Inscriptional_Pahlavi },\n  { 1688, PT_SC, ucp_Inscriptional_Parthian },\n  { 1710, PT_SC, ucp_Old_Italic },\n  { 1715, PT_SCX, ucp_Javanese },\n  { 1720, PT_SCX, ucp_Javanese },\n  { 1729, PT_BOOL, ucp_Join_Control },\n  { 1735, PT_BOOL, ucp_Join_Control },\n  { 1747, PT_SCX, ucp_Kaithi },\n  { 1754, PT_SCX, ucp_Kayah_Li },\n  { 1759, PT_SCX, ucp_Katakana },\n  { 1764, PT_SCX, ucp_Kannada },\n  { 1772, PT_SCX, ucp_Katakana },\n  { 1781, PT_SC, ucp_Kawi },\n  { 1786, PT_SCX, ucp_Kayah_Li },\n  { 1794, PT_SC, ucp_Kharoshthi },\n  { 1799, PT_SC, ucp_Kharoshthi },\n  { 1810, PT_SC, ucp_Khitan_Small_Script },\n  { 1828, PT_SC, ucp_Khmer },\n  { 1834, PT_SC, ucp_Khmer },\n  { 1839, PT_SCX, ucp_Khojki },\n  { 1844, PT_SCX, ucp_Khojki },\n  { 1851, PT_SCX, ucp_Khudawadi },\n  { 1861, PT_SC, ucp_Kirat_Rai },\n  { 1870, PT_SC, ucp_Khitan_Small_Script },\n  { 1875, PT_SCX, ucp_Kannada },\n  { 1880, PT_SC, ucp_Kirat_Rai },\n  { 1885, PT_SCX, ucp_Kaithi },\n  { 1890, PT_GC, ucp_L },\n  { 1892, PT_LAMP, 0 },\n  { 1895, PT_SC, ucp_Tai_Tham },\n  { 1900, PT_SC, ucp_Lao },\n  { 1904, PT_SC, ucp_Lao },\n  { 1909, PT_SCX, ucp_Latin },\n  { 1915, PT_SCX, ucp_Latin },\n  { 1920, PT_LAMP, 0 },\n  { 1923, PT_SC, ucp_Lepcha },\n  { 1928, PT_SC, ucp_Lepcha },\n  { 1935, PT_SCX, ucp_Limbu },\n  { 1940, PT_SCX, ucp_Limbu },\n  { 1946, PT_SCX, ucp_Linear_A },\n  { 1951, PT_SCX, ucp_Linear_B },\n  { 1956, PT_SCX, ucp_Linear_A },\n  { 1964, PT_SCX, ucp_Linear_B },\n  { 1972, PT_SCX, ucp_Lisu },\n  { 1977, PT_PC, ucp_Ll },\n  { 1980, PT_PC, ucp_Lm },\n  { 1983, PT_PC, ucp_Lo },\n  { 1986, PT_BOOL, ucp_Logical_Order_Exception },\n  { 1990, PT_BOOL, ucp_Logical_Order_Exception },\n  { 2012, PT_BOOL, ucp_Lowercase },\n  { 2018, PT_BOOL, ucp_Lowercase },\n  { 2028, PT_PC, ucp_Lt },\n  { 2031, PT_PC, ucp_Lu },\n  { 2034, PT_SCX, ucp_Lycian },\n  { 2039, PT_SCX, ucp_Lycian },\n  { 2046, PT_SCX, ucp_Lydian },\n  { 2051, PT_SCX, ucp_Lydian },\n  { 2058, PT_GC, ucp_M },\n  { 2060, PT_SCX, ucp_Mahajani },\n  { 2069, PT_SCX, ucp_Mahajani },\n  { 2074, PT_SC, ucp_Makasar },\n  { 2079, PT_SC, ucp_Makasar },\n  { 2087, PT_SCX, ucp_Malayalam },\n  { 2097, PT_SCX, ucp_Mandaic },\n  { 2102, PT_SCX, ucp_Mandaic },\n  { 2110, PT_SCX, ucp_Manichaean },\n  { 2115, PT_SCX, ucp_Manichaean },\n  { 2126, PT_SC, ucp_Marchen },\n  { 2131, PT_SC, ucp_Marchen },\n  { 2139, PT_SCX, ucp_Masaram_Gondi },\n  { 2152, PT_BOOL, ucp_Math },\n  { 2157, PT_PC, ucp_Mc },\n  { 2160, PT_BOOL, ucp_Modifier_Combining_Mark },\n  { 2164, PT_PC, ucp_Me },\n  { 2167, PT_SC, ucp_Medefaidrin },\n  { 2179, PT_SC, ucp_Medefaidrin },\n  { 2184, PT_SC, ucp_Meetei_Mayek },\n  { 2196, PT_SC, ucp_Mende_Kikakui },\n  { 2201, PT_SC, ucp_Mende_Kikakui },\n  { 2214, PT_SC, ucp_Meroitic_Cursive },\n  { 2219, PT_SCX, ucp_Meroitic_Hieroglyphs },\n  { 2224, PT_SC, ucp_Meroitic_Cursive },\n  { 2240, PT_SCX, ucp_Meroitic_Hieroglyphs },\n  { 2260, PT_SC, ucp_Miao },\n  { 2265, PT_SCX, ucp_Malayalam },\n  { 2270, PT_PC, ucp_Mn },\n  { 2273, PT_SCX, ucp_Modi },\n  { 2278, PT_BOOL, ucp_Modifier_Combining_Mark },\n  { 2300, PT_SCX, ucp_Mongolian },\n  { 2305, PT_SCX, ucp_Mongolian },\n  { 2315, PT_SC, ucp_Mro },\n  { 2319, PT_SC, ucp_Mro },\n  { 2324, PT_SC, ucp_Meetei_Mayek },\n  { 2329, PT_SCX, ucp_Multani },\n  { 2334, PT_SCX, ucp_Multani },\n  { 2342, PT_SCX, ucp_Myanmar },\n  { 2350, PT_SCX, ucp_Myanmar },\n  { 2355, PT_GC, ucp_N },\n  { 2357, PT_SC, ucp_Nabataean },\n  { 2367, PT_SC, ucp_Nag_Mundari },\n  { 2372, PT_SC, ucp_Nag_Mundari },\n  { 2383, PT_SCX, ucp_Nandinagari },\n  { 2388, PT_SCX, ucp_Nandinagari },\n  { 2400, PT_SC, ucp_Old_North_Arabian },\n  { 2405, PT_SC, ucp_Nabataean },\n  { 2410, PT_BOOL, ucp_Noncharacter_Code_Point },\n  { 2416, PT_PC, ucp_Nd },\n  { 2419, PT_SC, ucp_Newa },\n  { 2424, PT_SC, ucp_New_Tai_Lue },\n  { 2434, PT_SCX, ucp_Nko },\n  { 2438, PT_SCX, ucp_Nko },\n  { 2443, PT_PC, ucp_Nl },\n  { 2446, PT_PC, ucp_No },\n  { 2449, PT_BOOL, ucp_Noncharacter_Code_Point },\n  { 2471, PT_SC, ucp_Nushu },\n  { 2476, PT_SC, ucp_Nushu },\n  { 2482, PT_SC, ucp_Nyiakeng_Puachue_Hmong },\n  { 2503, PT_SC, ucp_Ogham },\n  { 2508, PT_SC, ucp_Ogham },\n  { 2514, PT_SC, ucp_Ol_Chiki },\n  { 2522, PT_SC, ucp_Ol_Chiki },\n  { 2527, PT_SCX, ucp_Old_Hungarian },\n  { 2540, PT_SC, ucp_Old_Italic },\n  { 2550, PT_SC, ucp_Old_North_Arabian },\n  { 2566, PT_SCX, ucp_Old_Permic },\n  { 2576, PT_SC, ucp_Old_Persian },\n  { 2587, PT_SC, ucp_Old_Sogdian },\n  { 2598, PT_SC, ucp_Old_South_Arabian },\n  { 2614, PT_SCX, ucp_Old_Turkic },\n  { 2624, PT_SCX, ucp_Old_Uyghur },\n  { 2634, PT_SCX, ucp_Ol_Onal },\n  { 2641, PT_SCX, ucp_Ol_Onal },\n  { 2646, PT_SCX, ucp_Oriya },\n  { 2652, PT_SCX, ucp_Old_Turkic },\n  { 2657, PT_SCX, ucp_Oriya },\n  { 2662, PT_SCX, ucp_Osage },\n  { 2668, PT_SCX, ucp_Osage },\n  { 2673, PT_SC, ucp_Osmanya },\n  { 2678, PT_SC, ucp_Osmanya },\n  { 2686, PT_SCX, ucp_Old_Uyghur },\n  { 2691, PT_GC, ucp_P },\n  { 2693, PT_SC, ucp_Pahawh_Hmong },\n  { 2705, PT_SC, ucp_Palmyrene },\n  { 2710, PT_SC, ucp_Palmyrene },\n  { 2720, PT_BOOL, ucp_Pattern_Syntax },\n  { 2727, PT_BOOL, ucp_Pattern_Syntax },\n  { 2741, PT_BOOL, ucp_Pattern_White_Space },\n  { 2759, PT_BOOL, ucp_Pattern_White_Space },\n  { 2765, PT_SC, ucp_Pau_Cin_Hau },\n  { 2770, PT_SC, ucp_Pau_Cin_Hau },\n  { 2780, PT_PC, ucp_Pc },\n  { 2783, PT_BOOL, ucp_Prepended_Concatenation_Mark },\n  { 2787, PT_PC, ucp_Pd },\n  { 2790, PT_PC, ucp_Pe },\n  { 2793, PT_SCX, ucp_Old_Permic },\n  { 2798, PT_PC, ucp_Pf },\n  { 2801, PT_SCX, ucp_Phags_Pa },\n  { 2806, PT_SCX, ucp_Phags_Pa },\n  { 2814, PT_SC, ucp_Inscriptional_Pahlavi },\n  { 2819, PT_SCX, ucp_Psalter_Pahlavi },\n  { 2824, PT_SC, ucp_Phoenician },\n  { 2829, PT_SC, ucp_Phoenician },\n  { 2840, PT_PC, ucp_Pi },\n  { 2843, PT_SC, ucp_Miao },\n  { 2848, PT_PC, ucp_Po },\n  { 2851, PT_BOOL, ucp_Prepended_Concatenation_Mark },\n  { 2878, PT_SC, ucp_Inscriptional_Parthian },\n  { 2883, PT_PC, ucp_Ps },\n  { 2886, PT_SCX, ucp_Psalter_Pahlavi },\n  { 2901, PT_SCX, ucp_Coptic },\n  { 2906, PT_SC, ucp_Inherited },\n  { 2911, PT_BOOL, ucp_Quotation_Mark },\n  { 2917, PT_BOOL, ucp_Quotation_Mark },\n  { 2931, PT_BOOL, ucp_Radical },\n  { 2939, PT_BOOL, ucp_Regional_Indicator },\n  { 2957, PT_SC, ucp_Rejang },\n  { 2964, PT_BOOL, ucp_Regional_Indicator },\n  { 2967, PT_SC, ucp_Rejang },\n  { 2972, PT_SCX, ucp_Hanifi_Rohingya },\n  { 2977, PT_SCX, ucp_Runic },\n  { 2983, PT_SCX, ucp_Runic },\n  { 2988, PT_GC, ucp_S },\n  { 2990, PT_SCX, ucp_Samaritan },\n  { 3000, PT_SCX, ucp_Samaritan },\n  { 3005, PT_SC, ucp_Old_South_Arabian },\n  { 3010, PT_SC, ucp_Saurashtra },\n  { 3015, PT_SC, ucp_Saurashtra },\n  { 3026, PT_PC, ucp_Sc },\n  { 3029, PT_BOOL, ucp_Soft_Dotted },\n  { 3032, PT_BOOL, ucp_Sentence_Terminal },\n  { 3049, PT_SC, ucp_SignWriting },\n  { 3054, PT_SCX, ucp_Sharada },\n  { 3062, PT_SCX, ucp_Shavian },\n  { 3070, PT_SCX, ucp_Shavian },\n  { 3075, PT_SCX, ucp_Sharada },\n  { 3080, PT_SC, ucp_Siddham },\n  { 3085, PT_SC, ucp_Siddham },\n  { 3093, PT_SC, ucp_SignWriting },\n  { 3105, PT_SCX, ucp_Khudawadi },\n  { 3110, PT_SCX, ucp_Sinhala },\n  { 3115, PT_SCX, ucp_Sinhala },\n  { 3123, PT_PC, ucp_Sk },\n  { 3126, PT_PC, ucp_Sm },\n  { 3129, PT_PC, ucp_So },\n  { 3132, PT_BOOL, ucp_Soft_Dotted },\n  { 3143, PT_SCX, ucp_Sogdian },\n  { 3148, PT_SCX, ucp_Sogdian },\n  { 3156, PT_SC, ucp_Old_Sogdian },\n  { 3161, PT_SC, ucp_Sora_Sompeng },\n  { 3166, PT_SC, ucp_Sora_Sompeng },\n  { 3178, PT_SC, ucp_Soyombo },\n  { 3183, PT_SC, ucp_Soyombo },\n  { 3191, PT_BOOL, ucp_White_Space },\n  { 3197, PT_BOOL, ucp_Sentence_Terminal },\n  { 3203, PT_SC, ucp_Sundanese },\n  { 3208, PT_SC, ucp_Sundanese },\n  { 3218, PT_SCX, ucp_Sunuwar },\n  { 3223, PT_SCX, ucp_Sunuwar },\n  { 3231, PT_SCX, ucp_Syloti_Nagri },\n  { 3236, PT_SCX, ucp_Syloti_Nagri },\n  { 3248, PT_SCX, ucp_Syriac },\n  { 3253, PT_SCX, ucp_Syriac },\n  { 3260, PT_SCX, ucp_Tagalog },\n  { 3268, PT_SCX, ucp_Tagbanwa },\n  { 3273, PT_SCX, ucp_Tagbanwa },\n  { 3282, PT_SCX, ucp_Tai_Le },\n  { 3288, PT_SC, ucp_Tai_Tham },\n  { 3296, PT_SC, ucp_Tai_Viet },\n  { 3304, PT_SCX, ucp_Takri },\n  { 3309, PT_SCX, ucp_Takri },\n  { 3315, PT_SCX, ucp_Tai_Le },\n  { 3320, PT_SC, ucp_New_Tai_Lue },\n  { 3325, PT_SCX, ucp_Tamil },\n  { 3331, PT_SCX, ucp_Tamil },\n  { 3336, PT_SCX, ucp_Tangut },\n  { 3341, PT_SC, ucp_Tangsa },\n  { 3348, PT_SCX, ucp_Tangut },\n  { 3355, PT_SC, ucp_Tai_Viet },\n  { 3360, PT_SCX, ucp_Telugu },\n  { 3365, PT_SCX, ucp_Telugu },\n  { 3372, PT_BOOL, ucp_Terminal_Punctuation },\n  { 3377, PT_BOOL, ucp_Terminal_Punctuation },\n  { 3397, PT_SCX, ucp_Tifinagh },\n  { 3402, PT_SCX, ucp_Tagalog },\n  { 3407, PT_SCX, ucp_Thaana },\n  { 3412, PT_SCX, ucp_Thaana },\n  { 3419, PT_SCX, ucp_Thai },\n  { 3424, PT_SCX, ucp_Tibetan },\n  { 3432, PT_SCX, ucp_Tibetan },\n  { 3437, PT_SCX, ucp_Tifinagh },\n  { 3446, PT_SCX, ucp_Tirhuta },\n  { 3451, PT_SCX, ucp_Tirhuta },\n  { 3459, PT_SC, ucp_Tangsa },\n  { 3464, PT_SCX, ucp_Todhri },\n  { 3471, PT_SCX, ucp_Todhri },\n  { 3476, PT_SCX, ucp_Toto },\n  { 3481, PT_SCX, ucp_Tulu_Tigalari },\n  { 3494, PT_SCX, ucp_Tulu_Tigalari },\n  { 3499, PT_SC, ucp_Ugaritic },\n  { 3504, PT_SC, ucp_Ugaritic },\n  { 3513, PT_BOOL, ucp_Unified_Ideograph },\n  { 3519, PT_BOOL, ucp_Unified_Ideograph },\n  { 3536, PT_SC, ucp_Unknown },\n  { 3544, PT_BOOL, ucp_Uppercase },\n  { 3550, PT_BOOL, ucp_Uppercase },\n  { 3560, PT_SC, ucp_Vai },\n  { 3564, PT_SC, ucp_Vai },\n  { 3569, PT_BOOL, ucp_Variation_Selector },\n  { 3587, PT_SC, ucp_Vithkuqi },\n  { 3592, PT_SC, ucp_Vithkuqi },\n  { 3601, PT_BOOL, ucp_Variation_Selector },\n  { 3604, PT_SC, ucp_Wancho },\n  { 3611, PT_SC, ucp_Warang_Citi },\n  { 3616, PT_SC, ucp_Warang_Citi },\n  { 3627, PT_SC, ucp_Wancho },\n  { 3632, PT_BOOL, ucp_White_Space },\n  { 3643, PT_BOOL, ucp_White_Space },\n  { 3650, PT_ALNUM, 0 },\n  { 3654, PT_BOOL, ucp_XID_Continue },\n  { 3659, PT_BOOL, ucp_XID_Continue },\n  { 3671, PT_BOOL, ucp_XID_Start },\n  { 3676, PT_BOOL, ucp_XID_Start },\n  { 3685, PT_SC, ucp_Old_Persian },\n  { 3690, PT_PXSPACE, 0 },\n  { 3694, PT_SPACE, 0 },\n  { 3698, PT_SC, ucp_Cuneiform },\n  { 3703, PT_UCNC, 0 },\n  { 3707, PT_WORD, 0 },\n  { 3711, PT_SCX, ucp_Yezidi },\n  { 3716, PT_SCX, ucp_Yezidi },\n  { 3723, PT_SCX, ucp_Yi },\n  { 3726, PT_SCX, ucp_Yi },\n  { 3731, PT_GC, ucp_Z },\n  { 3733, PT_SC, ucp_Zanabazar_Square },\n  { 3749, PT_SC, ucp_Zanabazar_Square },\n  { 3754, PT_SC, ucp_Inherited },\n  { 3759, PT_PC, ucp_Zl },\n  { 3762, PT_PC, ucp_Zp },\n  { 3765, PT_PC, ucp_Zs },\n  { 3768, PT_SC, ucp_Common },\n  { 3773, PT_SC, ucp_Unknown }\n};\n\nconst size_t PRIV(utt_size) = sizeof(PRIV(utt)) / sizeof(ucp_type_table);\n\n#endif /* SUPPORT_UNICODE */\n\n/* End of pcre2_ucptables.c */\n"
  },
  {
    "path": "Libraries/PCRE/pcre2_util.h",
    "content": "/*************************************************\n*      Perl-Compatible Regular Expressions       *\n*************************************************/\n\n/* PCRE2 is a library of functions to support regular expressions whose syntax\nand semantics are as close as possible to those of the Perl 5 language.\n\n                       Written by Philip Hazel\n     Original API code Copyright (c) 1997-2012 University of Cambridge\n          New API code Copyright (c) 2016-2024 University of Cambridge\n\n-----------------------------------------------------------------------------\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n    * Redistributions of source code must retain the above copyright notice,\n      this list of conditions and the following disclaimer.\n\n    * Redistributions in binary form must reproduce the above copyright\n      notice, this list of conditions and the following disclaimer in the\n      documentation and/or other materials provided with the distribution.\n\n    * Neither the name of the University of Cambridge nor the names of its\n      contributors may be used to endorse or promote products derived from\n      this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\nLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\nCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\nSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\nINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\nCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGE.\n-----------------------------------------------------------------------------\n*/\n\n#ifndef PCRE2_UTIL_H_IDEMPOTENT_GUARD\n#define PCRE2_UTIL_H_IDEMPOTENT_GUARD\n\n/* Assertion macros */\n\n#ifdef PCRE2_DEBUG\n\n#if defined(HAVE_ASSERT_H) && !defined(NDEBUG)\n#include <assert.h>\n#endif\n\n/* PCRE2_ASSERT(x) can be used to inject an assert() for conditions\nthat the code below doesn't support. It is a NOP for non debug builds\nbut in debug builds will print information about the location of the\ncode where it triggered and crash.\n\nIt is meant to work like assert(), and therefore the expression used\nshould indicate what the expected state is, and shouldn't have any\nside-effects. */\n\n#if defined(HAVE_ASSERT_H) && !defined(NDEBUG)\n#define PCRE2_ASSERT(x) assert(x)\n#else\n#define PCRE2_ASSERT(x) do                                            \\\n{                                                                     \\\n  if (!(x))                                                           \\\n  {                                                                   \\\n  fprintf(stderr, \"Assertion failed at \" __FILE__ \":%d\\n\", __LINE__); \\\n  abort();                                                            \\\n  }                                                                   \\\n} while(0)\n#endif\n\n/* PCRE2_UNREACHABLE() can be used to mark locations on the code that\nshouldn't be reached. In non debug builds is defined as a hint for\nthe compiler to eliminate any code after it, so it is useful also for\nperformance reasons, but should be used with care because if it is\never reached will trigger Undefined Behaviour and if you are lucky a\ncrash. In debug builds it will report the location where it was triggered\nand crash. One important point to consider when using this macro, is\nthat it is only implemented for a few compilers, and therefore can't\nbe relied on to always be active either, so if it is followed by some\ncode it is important to make sure that the whole thing is safe to\nuse even if the macro is not there (ex: make sure there is a `break`\nafter it if used at the end of a `case`) and to test your code also\nwith a configuration where the macro will be a NOP. */\n\n#if defined(HAVE_ASSERT_H) && !defined(NDEBUG)\n#define PCRE2_UNREACHABLE()                                         \\\nassert(((void)\"Execution reached unexpected point\", 0))\n#else\n#define PCRE2_UNREACHABLE() do                                      \\\n{                                                                   \\\nfprintf(stderr, \"Execution reached unexpected point at \" __FILE__   \\\n                \":%d\\n\", __LINE__);                                 \\\nabort();                                                            \\\n} while(0)\n#endif\n\n/* PCRE2_DEBUG_UNREACHABLE() is a debug only version of the previous\nmacro. It is meant to be used in places where the code is handling\nan error situation in code that shouldn't be reached, but that has\nsome sort of fallback code to normally handle the error. When in\ndoubt you should use this instead of the previous macro. Like in\nthe previous case, it is a good idea to document as much as possible\nthe reason and the actions that should be taken if it ever triggers. */\n\n#define PCRE2_DEBUG_UNREACHABLE() PCRE2_UNREACHABLE()\n\n#endif /* PCRE2_DEBUG */\n\n#ifndef PCRE2_DEBUG_UNREACHABLE\n#define PCRE2_DEBUG_UNREACHABLE() do {} while(0)\n#endif\n\n#ifndef PCRE2_UNREACHABLE\n#ifdef HAVE_BUILTIN_UNREACHABLE\n#define PCRE2_UNREACHABLE() __builtin_unreachable()\n#elif defined(HAVE_BUILTIN_ASSUME)\n#define PCRE2_UNREACHABLE() __assume(0)\n#else\n#define PCRE2_UNREACHABLE() do {} while(0)\n#endif\n#endif /* !PCRE2_UNREACHABLE */\n\n#ifndef PCRE2_ASSERT\n#define PCRE2_ASSERT(x) do {} while(0)\n#endif\n\n#endif /* PCRE2_UTIL_H_IDEMPOTENT_GUARD */\n\n/* End of pcre2_util.h */\n"
  },
  {
    "path": "Libraries/PCRE/pcre2_valid_utf.c",
    "content": "/*************************************************\n*      Perl-Compatible Regular Expressions       *\n*************************************************/\n\n/* PCRE is a library of functions to support regular expressions whose syntax\nand semantics are as close as possible to those of the Perl 5 language.\n\n                       Written by Philip Hazel\n     Original API code Copyright (c) 1997-2012 University of Cambridge\n          New API code Copyright (c) 2016-2020 University of Cambridge\n\n-----------------------------------------------------------------------------\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n    * Redistributions of source code must retain the above copyright notice,\n      this list of conditions and the following disclaimer.\n\n    * Redistributions in binary form must reproduce the above copyright\n      notice, this list of conditions and the following disclaimer in the\n      documentation and/or other materials provided with the distribution.\n\n    * Neither the name of the University of Cambridge nor the names of its\n      contributors may be used to endorse or promote products derived from\n      this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\nLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\nCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\nSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\nINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\nCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGE.\n-----------------------------------------------------------------------------\n*/\n\n\n/* This module contains an internal function for validating UTF character\nstrings. This file is also #included by the pcre2test program, which uses\nmacros to change names from _pcre2_xxx to xxxx, thereby avoiding name clashes\nwith the library. In this case, PCRE2_PCRE2TEST is defined. */\n\n#ifndef PCRE2_PCRE2TEST           /* We're compiling the library */\n#ifdef HAVE_CONFIG_H\n#include \"config.h\"\n#endif\n#include \"pcre2_internal.h\"\n#endif /* PCRE2_PCRE2TEST */\n\n\n#ifndef SUPPORT_UNICODE\n/*************************************************\n*  Dummy function when Unicode is not supported  *\n*************************************************/\n\n/* This function should never be called when Unicode is not supported. */\n\nint\nPRIV(valid_utf)(PCRE2_SPTR string, PCRE2_SIZE length, PCRE2_SIZE *erroroffset)\n{\n(void)string;\n(void)length;\n(void)erroroffset;\nreturn 0;\n}\n#else  /* UTF is supported */\n\n\n\n/*************************************************\n*           Validate a UTF string                *\n*************************************************/\n\n/* This function is called (optionally) at the start of compile or match, to\ncheck that a supposed UTF string is actually valid. The early check means\nthat subsequent code can assume it is dealing with a valid string. The check\ncan be turned off for maximum performance, but the consequences of supplying an\ninvalid string are then undefined.\n\nArguments:\n  string       points to the string\n  length       length of string\n  errp         pointer to an error position offset variable\n\nReturns:       == 0    if the string is a valid UTF string\n               != 0    otherwise, setting the offset of the bad character\n*/\n\nint\nPRIV(valid_utf)(PCRE2_SPTR string, PCRE2_SIZE length, PCRE2_SIZE *erroroffset)\n{\nPCRE2_SPTR p;\nuint32_t c;\n\n/* ----------------- Check a UTF-8 string ----------------- */\n\n#if PCRE2_CODE_UNIT_WIDTH == 8\n\n/* Originally, this function checked according to RFC 2279, allowing for values\nin the range 0 to 0x7fffffff, up to 6 bytes long, but ensuring that they were\nin the canonical format. Once somebody had pointed out RFC 3629 to me (it\nobsoletes 2279), additional restrictions were applied. The values are now\nlimited to be between 0 and 0x0010ffff, no more than 4 bytes long, and the\nsubrange 0xd000 to 0xdfff is excluded. However, the format of 5-byte and 6-byte\ncharacters is still checked. Error returns are as follows:\n\nPCRE2_ERROR_UTF8_ERR1   Missing 1 byte at the end of the string\nPCRE2_ERROR_UTF8_ERR2   Missing 2 bytes at the end of the string\nPCRE2_ERROR_UTF8_ERR3   Missing 3 bytes at the end of the string\nPCRE2_ERROR_UTF8_ERR4   Missing 4 bytes at the end of the string\nPCRE2_ERROR_UTF8_ERR5   Missing 5 bytes at the end of the string\nPCRE2_ERROR_UTF8_ERR6   2nd-byte's two top bits are not 0x80\nPCRE2_ERROR_UTF8_ERR7   3rd-byte's two top bits are not 0x80\nPCRE2_ERROR_UTF8_ERR8   4th-byte's two top bits are not 0x80\nPCRE2_ERROR_UTF8_ERR9   5th-byte's two top bits are not 0x80\nPCRE2_ERROR_UTF8_ERR10  6th-byte's two top bits are not 0x80\nPCRE2_ERROR_UTF8_ERR11  5-byte character is not permitted by RFC 3629\nPCRE2_ERROR_UTF8_ERR12  6-byte character is not permitted by RFC 3629\nPCRE2_ERROR_UTF8_ERR13  4-byte character with value > 0x10ffff is not permitted\nPCRE2_ERROR_UTF8_ERR14  3-byte character with value 0xd800-0xdfff is not permitted\nPCRE2_ERROR_UTF8_ERR15  Overlong 2-byte sequence\nPCRE2_ERROR_UTF8_ERR16  Overlong 3-byte sequence\nPCRE2_ERROR_UTF8_ERR17  Overlong 4-byte sequence\nPCRE2_ERROR_UTF8_ERR18  Overlong 5-byte sequence (won't ever occur)\nPCRE2_ERROR_UTF8_ERR19  Overlong 6-byte sequence (won't ever occur)\nPCRE2_ERROR_UTF8_ERR20  Isolated 0x80 byte (not within UTF-8 character)\nPCRE2_ERROR_UTF8_ERR21  Byte with the illegal value 0xfe or 0xff\n*/\n\nfor (p = string; length > 0; p++)\n  {\n  uint32_t ab, d;\n\n  c = *p;\n  length--;\n\n  if (c < 128) continue;                /* ASCII character */\n\n  if (c < 0xc0)                         /* Isolated 10xx xxxx byte */\n    {\n    *erroroffset = (PCRE2_SIZE)(p - string);\n    return PCRE2_ERROR_UTF8_ERR20;\n    }\n\n  if (c >= 0xfe)                        /* Invalid 0xfe or 0xff bytes */\n    {\n    *erroroffset = (PCRE2_SIZE)(p - string);\n    return PCRE2_ERROR_UTF8_ERR21;\n    }\n\n  ab = PRIV(utf8_table4)[c & 0x3f];     /* Number of additional bytes (1-5) */\n  if (length < ab)                      /* Missing bytes */\n    {\n    *erroroffset = (PCRE2_SIZE)(p - string);\n    switch(ab - length)\n      {\n      case 1: return PCRE2_ERROR_UTF8_ERR1;\n      case 2: return PCRE2_ERROR_UTF8_ERR2;\n      case 3: return PCRE2_ERROR_UTF8_ERR3;\n      case 4: return PCRE2_ERROR_UTF8_ERR4;\n      case 5: return PCRE2_ERROR_UTF8_ERR5;\n      }\n    }\n  length -= ab;                         /* Length remaining */\n\n  /* Check top bits in the second byte */\n\n  if (((d = *(++p)) & 0xc0) != 0x80)\n    {\n    *erroroffset = (PCRE2_SIZE)(p - string) - 1;\n    return PCRE2_ERROR_UTF8_ERR6;\n    }\n\n  /* For each length, check that the remaining bytes start with the 0x80 bit\n  set and not the 0x40 bit. Then check for an overlong sequence, and for the\n  excluded range 0xd800 to 0xdfff. */\n\n  switch (ab)\n    {\n    /* 2-byte character. No further bytes to check for 0x80. Check first byte\n    for for xx00 000x (overlong sequence). */\n\n    case 1: if ((c & 0x3e) == 0)\n      {\n      *erroroffset = (PCRE2_SIZE)(p - string) - 1;\n      return PCRE2_ERROR_UTF8_ERR15;\n      }\n    break;\n\n    /* 3-byte character. Check third byte for 0x80. Then check first 2 bytes\n      for 1110 0000, xx0x xxxx (overlong sequence) or\n          1110 1101, 1010 xxxx (0xd800 - 0xdfff) */\n\n    case 2:\n    if ((*(++p) & 0xc0) != 0x80)     /* Third byte */\n      {\n      *erroroffset = (PCRE2_SIZE)(p - string) - 2;\n      return PCRE2_ERROR_UTF8_ERR7;\n      }\n    if (c == 0xe0 && (d & 0x20) == 0)\n      {\n      *erroroffset = (PCRE2_SIZE)(p - string) - 2;\n      return PCRE2_ERROR_UTF8_ERR16;\n      }\n    if (c == 0xed && d >= 0xa0)\n      {\n      *erroroffset = (PCRE2_SIZE)(p - string) - 2;\n      return PCRE2_ERROR_UTF8_ERR14;\n      }\n    break;\n\n    /* 4-byte character. Check 3rd and 4th bytes for 0x80. Then check first 2\n       bytes for for 1111 0000, xx00 xxxx (overlong sequence), then check for a\n       character greater than 0x0010ffff (f4 8f bf bf) */\n\n    case 3:\n    if ((*(++p) & 0xc0) != 0x80)     /* Third byte */\n      {\n      *erroroffset = (PCRE2_SIZE)(p - string) - 2;\n      return PCRE2_ERROR_UTF8_ERR7;\n      }\n    if ((*(++p) & 0xc0) != 0x80)     /* Fourth byte */\n      {\n      *erroroffset = (PCRE2_SIZE)(p - string) - 3;\n      return PCRE2_ERROR_UTF8_ERR8;\n      }\n    if (c == 0xf0 && (d & 0x30) == 0)\n      {\n      *erroroffset = (PCRE2_SIZE)(p - string) - 3;\n      return PCRE2_ERROR_UTF8_ERR17;\n      }\n    if (c > 0xf4 || (c == 0xf4 && d > 0x8f))\n      {\n      *erroroffset = (PCRE2_SIZE)(p - string) - 3;\n      return PCRE2_ERROR_UTF8_ERR13;\n      }\n    break;\n\n    /* 5-byte and 6-byte characters are not allowed by RFC 3629, and will be\n    rejected by the length test below. However, we do the appropriate tests\n    here so that overlong sequences get diagnosed, and also in case there is\n    ever an option for handling these larger code points. */\n\n    /* 5-byte character. Check 3rd, 4th, and 5th bytes for 0x80. Then check for\n    1111 1000, xx00 0xxx */\n\n    case 4:\n    if ((*(++p) & 0xc0) != 0x80)     /* Third byte */\n      {\n      *erroroffset = (PCRE2_SIZE)(p - string) - 2;\n      return PCRE2_ERROR_UTF8_ERR7;\n      }\n    if ((*(++p) & 0xc0) != 0x80)     /* Fourth byte */\n      {\n      *erroroffset = (PCRE2_SIZE)(p - string) - 3;\n      return PCRE2_ERROR_UTF8_ERR8;\n      }\n    if ((*(++p) & 0xc0) != 0x80)     /* Fifth byte */\n      {\n      *erroroffset = (PCRE2_SIZE)(p - string) - 4;\n      return PCRE2_ERROR_UTF8_ERR9;\n      }\n    if (c == 0xf8 && (d & 0x38) == 0)\n      {\n      *erroroffset = (PCRE2_SIZE)(p - string) - 4;\n      return PCRE2_ERROR_UTF8_ERR18;\n      }\n    break;\n\n    /* 6-byte character. Check 3rd-6th bytes for 0x80. Then check for\n    1111 1100, xx00 00xx. */\n\n    case 5:\n    if ((*(++p) & 0xc0) != 0x80)     /* Third byte */\n      {\n      *erroroffset = (PCRE2_SIZE)(p - string) - 2;\n      return PCRE2_ERROR_UTF8_ERR7;\n      }\n    if ((*(++p) & 0xc0) != 0x80)     /* Fourth byte */\n      {\n      *erroroffset = (PCRE2_SIZE)(p - string) - 3;\n      return PCRE2_ERROR_UTF8_ERR8;\n      }\n    if ((*(++p) & 0xc0) != 0x80)     /* Fifth byte */\n      {\n      *erroroffset = (PCRE2_SIZE)(p - string) - 4;\n      return PCRE2_ERROR_UTF8_ERR9;\n      }\n    if ((*(++p) & 0xc0) != 0x80)     /* Sixth byte */\n      {\n      *erroroffset = (PCRE2_SIZE)(p - string) - 5;\n      return PCRE2_ERROR_UTF8_ERR10;\n      }\n    if (c == 0xfc && (d & 0x3c) == 0)\n      {\n      *erroroffset = (PCRE2_SIZE)(p - string) - 5;\n      return PCRE2_ERROR_UTF8_ERR19;\n      }\n    break;\n    }\n\n  /* Character is valid under RFC 2279, but 4-byte and 5-byte characters are\n  excluded by RFC 3629. The pointer p is currently at the last byte of the\n  character. */\n\n  if (ab > 3)\n    {\n    *erroroffset = (PCRE2_SIZE)(p - string) - ab;\n    return (ab == 4)? PCRE2_ERROR_UTF8_ERR11 : PCRE2_ERROR_UTF8_ERR12;\n    }\n  }\nreturn 0;\n\n\n/* ----------------- Check a UTF-16 string ----------------- */\n\n#elif PCRE2_CODE_UNIT_WIDTH == 16\n\n/* There's not so much work, nor so many errors, for UTF-16.\nPCRE2_ERROR_UTF16_ERR1  Missing low surrogate at the end of the string\nPCRE2_ERROR_UTF16_ERR2  Invalid low surrogate\nPCRE2_ERROR_UTF16_ERR3  Isolated low surrogate\n*/\n\nfor (p = string; length > 0; p++)\n  {\n  c = *p;\n  length--;\n\n  if ((c & 0xf800) != 0xd800)\n    {\n    /* Normal UTF-16 code point. Neither high nor low surrogate. */\n    }\n  else if ((c & 0x0400) == 0)\n    {\n    /* High surrogate. Must be a followed by a low surrogate. */\n    if (length == 0)\n      {\n      *erroroffset = (PCRE2_SIZE)(p - string);\n      return PCRE2_ERROR_UTF16_ERR1;\n      }\n    p++;\n    length--;\n    if ((*p & 0xfc00) != 0xdc00)\n      {\n      *erroroffset = (PCRE2_SIZE)(p - string) - 1;\n      return PCRE2_ERROR_UTF16_ERR2;\n      }\n    }\n  else\n    {\n    /* Isolated low surrogate. Always an error. */\n    *erroroffset = (PCRE2_SIZE)(p - string);\n    return PCRE2_ERROR_UTF16_ERR3;\n    }\n  }\nreturn 0;\n\n\n\n/* ----------------- Check a UTF-32 string ----------------- */\n\n#else\n\n/* There is very little to do for a UTF-32 string.\nPCRE2_ERROR_UTF32_ERR1  Surrogate character\nPCRE2_ERROR_UTF32_ERR2  Character > 0x10ffff\n*/\n\nfor (p = string; length > 0; length--, p++)\n  {\n  c = *p;\n  if ((c & 0xfffff800u) != 0xd800u)\n    {\n    /* Normal UTF-32 code point. Neither high nor low surrogate. */\n    if (c > 0x10ffffu)\n      {\n      *erroroffset = (PCRE2_SIZE)(p - string);\n      return PCRE2_ERROR_UTF32_ERR2;\n      }\n    }\n  else\n    {\n    /* A surrogate */\n    *erroroffset = (PCRE2_SIZE)(p - string);\n    return PCRE2_ERROR_UTF32_ERR1;\n    }\n  }\nreturn 0;\n#endif  /* CODE_UNIT_WIDTH */\n}\n#endif  /* SUPPORT_UNICODE */\n\n/* End of pcre2_valid_utf.c */\n"
  },
  {
    "path": "Libraries/PCRE/pcre2_xclass.c",
    "content": "/*************************************************\n*      Perl-Compatible Regular Expressions       *\n*************************************************/\n\n/* PCRE is a library of functions to support regular expressions whose syntax\nand semantics are as close as possible to those of the Perl 5 language.\n\n                       Written by Philip Hazel\n     Original API code Copyright (c) 1997-2012 University of Cambridge\n          New API code Copyright (c) 2016-2024 University of Cambridge\n\n-----------------------------------------------------------------------------\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n    * Redistributions of source code must retain the above copyright notice,\n      this list of conditions and the following disclaimer.\n\n    * Redistributions in binary form must reproduce the above copyright\n      notice, this list of conditions and the following disclaimer in the\n      documentation and/or other materials provided with the distribution.\n\n    * Neither the name of the University of Cambridge nor the names of its\n      contributors may be used to endorse or promote products derived from\n      this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\nLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\nCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\nSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\nINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\nCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGE.\n-----------------------------------------------------------------------------\n*/\n\n/* This module contains two internal functions that are used to match\nOP_XCLASS and OP_ECLASS. It is used by pcre2_auto_possessify() and by both\npcre2_match() and pcre2_dfa_match(). */\n\n\n#ifdef HAVE_CONFIG_H\n#include \"config.h\"\n#endif\n\n\n#include \"pcre2_internal.h\"\n\n/*************************************************\n*       Match character against an XCLASS        *\n*************************************************/\n\n/* This function is called to match a character against an extended class that\nmight contain codepoints above 255 and/or Unicode properties.\n\nArguments:\n  c           the character\n  data        points to the flag code unit of the XCLASS data\n  utf         TRUE if in UTF mode\n\nReturns:      TRUE if character matches, else FALSE\n*/\n\nBOOL\nPRIV(xclass)(uint32_t c, PCRE2_SPTR data, const uint8_t *char_lists_end, BOOL utf)\n{\n/* Update PRIV(update_classbits) when this function is changed. */\nPCRE2_UCHAR t;\nBOOL not_negated = (*data & XCL_NOT) == 0;\nuint32_t type, max_index, min_index, value;\nconst uint8_t *next_char;\n\n#if PCRE2_CODE_UNIT_WIDTH == 8\n/* In 8 bit mode, this must always be TRUE. Help the compiler to know that. */\nutf = TRUE;\n#endif\n\n/* Code points < 256 are matched against a bitmap, if one is present. */\n\nif ((*data++ & XCL_MAP) != 0)\n  {\n  if (c < 256)\n    return (((const uint8_t *)data)[c/8] & (1u << (c&7))) != 0;\n  /* Skip bitmap. */\n  data += 32 / sizeof(PCRE2_UCHAR);\n  }\n\n/* Match against the list of Unicode properties. We won't ever\nencounter XCL_PROP or XCL_NOTPROP when UTF support is not compiled. */\n#ifdef SUPPORT_UNICODE\nif (*data == XCL_PROP || *data == XCL_NOTPROP)\n  {\n  /* The UCD record is the same for all properties. */\n  const ucd_record *prop = GET_UCD(c);\n\n  do\n    {\n    int chartype;\n    BOOL isprop = (*data++) == XCL_PROP;\n    BOOL ok;\n\n    switch(*data)\n      {\n      case PT_LAMP:\n      chartype = prop->chartype;\n      if ((chartype == ucp_Lu || chartype == ucp_Ll ||\n           chartype == ucp_Lt) == isprop) return not_negated;\n      break;\n\n      case PT_GC:\n      if ((data[1] == PRIV(ucp_gentype)[prop->chartype]) == isprop)\n        return not_negated;\n      break;\n\n      case PT_PC:\n      if ((data[1] == prop->chartype) == isprop) return not_negated;\n      break;\n\n      case PT_SC:\n      if ((data[1] == prop->script) == isprop) return not_negated;\n      break;\n\n      case PT_SCX:\n      ok = (data[1] == prop->script ||\n            MAPBIT(PRIV(ucd_script_sets) + UCD_SCRIPTX_PROP(prop), data[1]) != 0);\n      if (ok == isprop) return not_negated;\n      break;\n\n      case PT_ALNUM:\n      chartype = prop->chartype;\n      if ((PRIV(ucp_gentype)[chartype] == ucp_L ||\n           PRIV(ucp_gentype)[chartype] == ucp_N) == isprop)\n        return not_negated;\n      break;\n\n      /* Perl space used to exclude VT, but from Perl 5.18 it is included,\n      which means that Perl space and POSIX space are now identical. PCRE\n      was changed at release 8.34. */\n\n      case PT_SPACE:    /* Perl space */\n      case PT_PXSPACE:  /* POSIX space */\n      switch(c)\n        {\n        HSPACE_CASES:\n        VSPACE_CASES:\n        if (isprop) return not_negated;\n        break;\n\n        default:\n        if ((PRIV(ucp_gentype)[prop->chartype] == ucp_Z) == isprop)\n          return not_negated;\n        break;\n        }\n      break;\n\n      case PT_WORD:\n      chartype = prop->chartype;\n      if ((PRIV(ucp_gentype)[chartype] == ucp_L ||\n           PRIV(ucp_gentype)[chartype] == ucp_N ||\n           chartype == ucp_Mn || chartype == ucp_Pc) == isprop)\n        return not_negated;\n      break;\n\n      case PT_UCNC:\n      if (c < 0xa0)\n        {\n        if ((c == CHAR_DOLLAR_SIGN || c == CHAR_COMMERCIAL_AT ||\n             c == CHAR_GRAVE_ACCENT) == isprop)\n          return not_negated;\n        }\n      else\n        {\n        if ((c < 0xd800 || c > 0xdfff) == isprop)\n          return not_negated;\n        }\n      break;\n\n      case PT_BIDICL:\n      if ((UCD_BIDICLASS_PROP(prop) == data[1]) == isprop)\n        return not_negated;\n      break;\n\n      case PT_BOOL:\n      ok = MAPBIT(PRIV(ucd_boolprop_sets) +\n        UCD_BPROPS_PROP(prop), data[1]) != 0;\n      if (ok == isprop) return not_negated;\n      break;\n\n      /* The following three properties can occur only in an XCLASS, as there\n      is no \\p or \\P coding for them. */\n\n      /* Graphic character. Implement this as not Z (space or separator) and\n      not C (other), except for Cf (format) with a few exceptions. This seems\n      to be what Perl does. The exceptional characters are:\n\n      U+061C           Arabic Letter Mark\n      U+180E           Mongolian Vowel Separator\n      U+2066 - U+2069  Various \"isolate\"s\n      */\n\n      case PT_PXGRAPH:\n      chartype = prop->chartype;\n      if ((PRIV(ucp_gentype)[chartype] != ucp_Z &&\n            (PRIV(ucp_gentype)[chartype] != ucp_C ||\n              (chartype == ucp_Cf &&\n                c != 0x061c && c != 0x180e && (c < 0x2066 || c > 0x2069))\n         )) == isprop)\n        return not_negated;\n      break;\n\n      /* Printable character: same as graphic, with the addition of Zs, i.e.\n      not Zl and not Zp, and U+180E. */\n\n      case PT_PXPRINT:\n      chartype = prop->chartype;\n      if ((chartype != ucp_Zl &&\n           chartype != ucp_Zp &&\n            (PRIV(ucp_gentype)[chartype] != ucp_C ||\n              (chartype == ucp_Cf &&\n                c != 0x061c && (c < 0x2066 || c > 0x2069))\n         )) == isprop)\n        return not_negated;\n      break;\n\n      /* Punctuation: all Unicode punctuation, plus ASCII characters that\n      Unicode treats as symbols rather than punctuation, for Perl\n      compatibility (these are $+<=>^`|~). */\n\n      case PT_PXPUNCT:\n      chartype = prop->chartype;\n      if ((PRIV(ucp_gentype)[chartype] == ucp_P ||\n            (c < 128 && PRIV(ucp_gentype)[chartype] == ucp_S)) == isprop)\n        return not_negated;\n      break;\n\n      /* Perl has two sets of hex digits */\n\n      case PT_PXXDIGIT:\n      if (((c >= CHAR_0 && c <= CHAR_9) ||\n           (c >= CHAR_A && c <= CHAR_F) ||\n           (c >= CHAR_a && c <= CHAR_f) ||\n           (c >= 0xff10 && c <= 0xff19) ||  /* Fullwidth digits */\n           (c >= 0xff21 && c <= 0xff26) ||  /* Fullwidth letters */\n           (c >= 0xff41 && c <= 0xff46)) == isprop)\n        return not_negated;\n      break;\n\n      /* This should never occur, but compilers may mutter if there is no\n      default. */\n\n      default:\n      PCRE2_DEBUG_UNREACHABLE();\n      return FALSE;\n      }\n\n    data += 2;\n    }\n  while (*data == XCL_PROP || *data == XCL_NOTPROP);\n  }\n#else\n  (void)utf;  /* Avoid compiler warning */\n#endif  /* SUPPORT_UNICODE */\n\n/* Match against large chars or ranges that end with a large char. */\nif (*data < XCL_LIST)\n  {\n  while ((t = *data++) != XCL_END)\n    {\n    uint32_t x, y;\n\n#ifdef SUPPORT_UNICODE\n    if (utf)\n      {\n      GETCHARINC(x, data); /* macro generates multiple statements */\n      }\n    else\n#endif\n      x = *data++;\n\n    if (t == XCL_SINGLE)\n      {\n      /* Since character ranges follow the properties, and they are\n      sorted, early return is possible for all characters <= x. */\n      if (c <= x) return (c == x) ? not_negated : !not_negated;\n      continue;\n      }\n\n    PCRE2_ASSERT(t == XCL_RANGE);\n#ifdef SUPPORT_UNICODE\n    if (utf)\n      {\n      GETCHARINC(y, data); /* macro generates multiple statements */\n      }\n    else\n#endif\n      y = *data++;\n\n    /* Since character ranges follow the properties, and they are\n    sorted, early return is possible for all characters <= y. */\n    if (c <= y) return (c >= x) ? not_negated : !not_negated;\n    }\n\n  return !not_negated;   /* char did not match */\n  }\n\n#if PCRE2_CODE_UNIT_WIDTH == 8\ntype = (uint32_t)(data[0] << 8) | data[1];\ndata += 2;\n#else\ntype = data[0];\ndata++;\n#endif  /* CODE_UNIT_WIDTH */\n\n/* Align characters. */\nnext_char = char_lists_end - (GET(data, 0) << 1);\ntype &= XCL_TYPE_MASK;\n\n/* Alignment check. */\nPCRE2_ASSERT(((uintptr_t)next_char & 0x1) == 0);\n\nif (c >= XCL_CHAR_LIST_HIGH_16_START)\n  {\n  max_index = type & XCL_ITEM_COUNT_MASK;\n  if (max_index == XCL_ITEM_COUNT_MASK)\n    {\n    max_index = *(const uint16_t*)next_char;\n    PCRE2_ASSERT(max_index >= XCL_ITEM_COUNT_MASK);\n    next_char += 2;\n    }\n\n  next_char += max_index << 1;\n  type >>= XCL_TYPE_BIT_LEN;\n  }\n\nif (c < XCL_CHAR_LIST_LOW_32_START)\n  {\n  max_index = type & XCL_ITEM_COUNT_MASK;\n\n  c = (uint16_t)((c << XCL_CHAR_SHIFT) | XCL_CHAR_END);\n\n  if (max_index == XCL_ITEM_COUNT_MASK)\n    {\n    max_index = *(const uint16_t*)next_char;\n    PCRE2_ASSERT(max_index >= XCL_ITEM_COUNT_MASK);\n    next_char += 2;\n    }\n\n  if (max_index == 0 || c < *(const uint16_t*)next_char)\n    return ((type & XCL_BEGIN_WITH_RANGE) != 0) == not_negated;\n\n  min_index = 0;\n  value = ((const uint16_t*)next_char)[--max_index];\n  if (c >= value)\n    return (value == c || (value & XCL_CHAR_END) == 0) == not_negated;\n\n  max_index--;\n\n  /* Binary search of a range. */\n  while (TRUE)\n    {\n    uint32_t mid_index = (min_index + max_index) >> 1;\n    value = ((const uint16_t*)next_char)[mid_index];\n\n    if (c < value)\n      max_index = mid_index - 1;\n    else if (((const uint16_t*)next_char)[mid_index + 1] <= c)\n      min_index = mid_index + 1;\n    else\n      return (value == c || (value & XCL_CHAR_END) == 0) == not_negated;\n    }\n  }\n\n/* Skip the 16 bit ranges. */\nmax_index = type & XCL_ITEM_COUNT_MASK;\nif (max_index == XCL_ITEM_COUNT_MASK)\n  {\n  max_index = *(const uint16_t*)next_char;\n  PCRE2_ASSERT(max_index >= XCL_ITEM_COUNT_MASK);\n  next_char += 2;\n  }\n\nnext_char += (max_index << 1);\ntype >>= XCL_TYPE_BIT_LEN;\n\n/* Alignment check. */\nPCRE2_ASSERT(((uintptr_t)next_char & 0x3) == 0);\n\nmax_index = type & XCL_ITEM_COUNT_MASK;\n\n#if PCRE2_CODE_UNIT_WIDTH == 32\nif (c >= XCL_CHAR_LIST_HIGH_32_START)\n  {\n  if (max_index == XCL_ITEM_COUNT_MASK)\n    {\n    max_index = *(const uint32_t*)next_char;\n    PCRE2_ASSERT(max_index >= XCL_ITEM_COUNT_MASK);\n    next_char += 4;\n    }\n\n  next_char += max_index << 2;\n  type >>= XCL_TYPE_BIT_LEN;\n  max_index = type & XCL_ITEM_COUNT_MASK;\n  }\n#endif\n\nc = (uint32_t)((c << XCL_CHAR_SHIFT) | XCL_CHAR_END);\n\nif (max_index == XCL_ITEM_COUNT_MASK)\n  {\n  max_index = *(const uint32_t*)next_char;\n  next_char += 4;\n  }\n\nif (max_index == 0 || c < *(const uint32_t*)next_char)\n  return ((type & XCL_BEGIN_WITH_RANGE) != 0) == not_negated;\n\nmin_index = 0;\nvalue = ((const uint32_t*)next_char)[--max_index];\nif (c >= value)\n  return (value == c || (value & XCL_CHAR_END) == 0) == not_negated;\n\nmax_index--;\n\n/* Binary search of a range. */\nwhile (TRUE)\n  {\n  uint32_t mid_index = (min_index + max_index) >> 1;\n  value = ((const uint32_t*)next_char)[mid_index];\n\n  if (c < value)\n    max_index = mid_index - 1;\n  else if (((const uint32_t*)next_char)[mid_index + 1] <= c)\n    min_index = mid_index + 1;\n  else\n    return (value == c || (value & XCL_CHAR_END) == 0) == not_negated;\n  }\n}\n\n\n\n/*************************************************\n*       Match character against an ECLASS        *\n*************************************************/\n\n/* This function is called to match a character against an extended class\nused for describing characters using boolean operations on sets.\n\nArguments:\n  c           the character\n  data_start  points to the start of the ECLASS data\n  data_end    points one-past-the-last of the ECLASS data\n  utf         TRUE if in UTF mode\n\nReturns:      TRUE if character matches, else FALSE\n*/\n\nBOOL\nPRIV(eclass)(uint32_t c, PCRE2_SPTR data_start, PCRE2_SPTR data_end,\n  const uint8_t *char_lists_end, BOOL utf)\n{\nPCRE2_SPTR ptr = data_start;\nPCRE2_UCHAR flags;\nuint32_t stack = 0;\nint stack_depth = 0;\n\nPCRE2_ASSERT(data_start < data_end);\nflags = *ptr++;\nPCRE2_ASSERT((flags & ECL_MAP) == 0 ||\n             (data_end - ptr) >= 32 / (int)sizeof(PCRE2_UCHAR));\n\n/* Code points < 256 are matched against a bitmap, if one is present.\nOtherwise all codepoints are checked later. */\n\nif ((flags & ECL_MAP) != 0)\n  {\n  if (c < 256)\n    return (((const uint8_t *)ptr)[c/8] & (1u << (c&7))) != 0;\n\n  /* Skip the bitmap. */\n  ptr += 32 / sizeof(PCRE2_UCHAR);\n  }\n\n/* Do a little loop, until we reach the end of the ECLASS. */\nwhile (ptr < data_end)\n  {\n  switch (*ptr)\n    {\n    case ECL_AND:\n    ++ptr;\n    stack = (stack >> 1) & (stack | ~(uint32_t)1u);\n    PCRE2_ASSERT(stack_depth >= 2);\n    --stack_depth;\n    break;\n\n    case ECL_OR:\n    ++ptr;\n    stack = (stack >> 1) | (stack & (uint32_t)1u);\n    PCRE2_ASSERT(stack_depth >= 2);\n    --stack_depth;\n    break;\n\n    case ECL_XOR:\n    ++ptr;\n    stack = (stack >> 1) ^ (stack & (uint32_t)1u);\n    PCRE2_ASSERT(stack_depth >= 2);\n    --stack_depth;\n    break;\n\n    case ECL_NOT:\n    ++ptr;\n    stack ^= (uint32_t)1u;\n    PCRE2_ASSERT(stack_depth >= 1);\n    break;\n\n    case ECL_XCLASS:\n      {\n      uint32_t matched = PRIV(xclass)(c, ptr + 1 + LINK_SIZE, char_lists_end, utf);\n\n      ptr += GET(ptr, 1);\n      stack = (stack << 1) | matched;\n      ++stack_depth;\n      break;\n      }\n\n    /* This should never occur, but compilers may mutter if there is no\n    default. */\n\n    default:\n    PCRE2_DEBUG_UNREACHABLE();\n    return FALSE;\n    }\n  }\n\nPCRE2_ASSERT(stack_depth == 1);\n(void)stack_depth;  /* Ignore unused variable, if assertions are disabled. */\n\n/* The final bit left on the stack now holds the match result. */\nreturn (stack & 1u) != 0;\n}\n\n/* End of pcre2_xclass.c */\n"
  },
  {
    "path": "Libraries/scintilla/.editorconfig",
    "content": "root = true\n\n[**]\nindent_style = tab\n"
  },
  {
    "path": "Libraries/scintilla/include/ILexer.h",
    "content": "// Scintilla source code edit control\n/** @file ILexer.h\n ** Interface between Scintilla and lexers.\n **/\n// Copyright 1998-2010 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#ifndef ILEXER_H\n#define ILEXER_H\n\n#include \"Sci_Position.h\"\n\nnamespace Scintilla {\n\nenum { dvRelease4=2 };\n\nclass IDocument {\npublic:\n\tvirtual int SCI_METHOD Version() const = 0;\n\tvirtual void SCI_METHOD SetErrorStatus(int status) = 0;\n\tvirtual Sci_Position SCI_METHOD Length() const = 0;\n\tvirtual void SCI_METHOD GetCharRange(char *buffer, Sci_Position position, Sci_Position lengthRetrieve) const = 0;\n\tvirtual char SCI_METHOD StyleAt(Sci_Position position) const = 0;\n\tvirtual Sci_Position SCI_METHOD LineFromPosition(Sci_Position position) const = 0;\n\tvirtual Sci_Position SCI_METHOD LineStart(Sci_Position line) const = 0;\n\tvirtual int SCI_METHOD GetLevel(Sci_Position line) const = 0;\n\tvirtual int SCI_METHOD SetLevel(Sci_Position line, int level) = 0;\n\tvirtual int SCI_METHOD GetLineState(Sci_Position line) const = 0;\n\tvirtual int SCI_METHOD SetLineState(Sci_Position line, int state) = 0;\n\tvirtual void SCI_METHOD StartStyling(Sci_Position position) = 0;\n\tvirtual bool SCI_METHOD SetStyleFor(Sci_Position length, char style) = 0;\n\tvirtual bool SCI_METHOD SetStyles(Sci_Position length, const char *styles) = 0;\n\tvirtual void SCI_METHOD DecorationSetCurrentIndicator(int indicator) = 0;\n\tvirtual void SCI_METHOD DecorationFillRange(Sci_Position position, int value, Sci_Position fillLength) = 0;\n\tvirtual void SCI_METHOD ChangeLexerState(Sci_Position start, Sci_Position end) = 0;\n\tvirtual int SCI_METHOD CodePage() const = 0;\n\tvirtual bool SCI_METHOD IsDBCSLeadByte(char ch) const = 0;\n\tvirtual const char * SCI_METHOD BufferPointer() = 0;\n\tvirtual int SCI_METHOD GetLineIndentation(Sci_Position line) = 0;\n\tvirtual Sci_Position SCI_METHOD LineEnd(Sci_Position line) const = 0;\n\tvirtual Sci_Position SCI_METHOD GetRelativePosition(Sci_Position positionStart, Sci_Position characterOffset) const = 0;\n\tvirtual int SCI_METHOD GetCharacterAndWidth(Sci_Position position, Sci_Position *pWidth) const = 0;\n};\n\nenum { lvRelease4=2, lvRelease5=3 };\n\nclass ILexer4 {\npublic:\n\tvirtual int SCI_METHOD Version() const = 0;\n\tvirtual void SCI_METHOD Release() = 0;\n\tvirtual const char * SCI_METHOD PropertyNames() = 0;\n\tvirtual int SCI_METHOD PropertyType(const char *name) = 0;\n\tvirtual const char * SCI_METHOD DescribeProperty(const char *name) = 0;\n\tvirtual Sci_Position SCI_METHOD PropertySet(const char *key, const char *val) = 0;\n\tvirtual const char * SCI_METHOD DescribeWordListSets() = 0;\n\tvirtual Sci_Position SCI_METHOD WordListSet(int n, const char *wl) = 0;\n\tvirtual void SCI_METHOD Lex(Sci_PositionU startPos, Sci_Position lengthDoc, int initStyle, IDocument *pAccess) = 0;\n\tvirtual void SCI_METHOD Fold(Sci_PositionU startPos, Sci_Position lengthDoc, int initStyle, IDocument *pAccess) = 0;\n\tvirtual void * SCI_METHOD PrivateCall(int operation, void *pointer) = 0;\n\tvirtual int SCI_METHOD LineEndTypesSupported() = 0;\n\tvirtual int SCI_METHOD AllocateSubStyles(int styleBase, int numberStyles) = 0;\n\tvirtual int SCI_METHOD SubStylesStart(int styleBase) = 0;\n\tvirtual int SCI_METHOD SubStylesLength(int styleBase) = 0;\n\tvirtual int SCI_METHOD StyleFromSubStyle(int subStyle) = 0;\n\tvirtual int SCI_METHOD PrimaryStyleFromStyle(int style) = 0;\n\tvirtual void SCI_METHOD FreeSubStyles() = 0;\n\tvirtual void SCI_METHOD SetIdentifiers(int style, const char *identifiers) = 0;\n\tvirtual int SCI_METHOD DistanceToSecondaryStyles() = 0;\n\tvirtual const char * SCI_METHOD GetSubStyleBases() = 0;\n\tvirtual int SCI_METHOD NamedStyles() = 0;\n\tvirtual const char * SCI_METHOD NameOfStyle(int style) = 0;\n\tvirtual const char * SCI_METHOD TagsOfStyle(int style) = 0;\n\tvirtual const char * SCI_METHOD DescriptionOfStyle(int style) = 0;\n};\n\nclass ILexer5 : public ILexer4 {\npublic:\n\tvirtual const char * SCI_METHOD GetName() = 0;\n\tvirtual int SCI_METHOD  GetIdentifier() = 0;\n\tvirtual const char * SCI_METHOD PropertyGet(const char *key) = 0;\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/include/ILoader.h",
    "content": "// Scintilla source code edit control\n/** @file ILoader.h\n ** Interface for loading into a Scintilla document from a background thread.\n ** Interface for manipulating a document without a view.\n **/\n// Copyright 1998-2017 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#ifndef ILOADER_H\n#define ILOADER_H\n\n#include \"Sci_Position.h\"\n\nnamespace Scintilla {\n\nclass ILoader {\npublic:\n\tvirtual int SCI_METHOD Release() = 0;\n\t// Returns a status code from SC_STATUS_*\n\tvirtual int SCI_METHOD AddData(const char *data, Sci_Position length) = 0;\n\tvirtual void * SCI_METHOD ConvertToDocument() = 0;\n};\n\nstatic constexpr int deRelease0 = 0;\n\nclass IDocumentEditable {\npublic:\n\t// Allow this interface to add methods over time and discover whether new methods available.\n\tvirtual int SCI_METHOD DEVersion() const noexcept = 0;\n\n\t// Lifetime control\n\tvirtual int SCI_METHOD AddRef() noexcept = 0;\n\tvirtual int SCI_METHOD Release() = 0;\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/include/Sci_Position.h",
    "content": "// Scintilla source code edit control\n/** @file Sci_Position.h\n ** Define the Sci_Position type used in Scintilla's external interfaces.\n ** These need to be available to clients written in C so are not in a C++ namespace.\n **/\n// Copyright 2015 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#ifndef SCI_POSITION_H\n#define SCI_POSITION_H\n\n#include <stddef.h>\n\n// Basic signed type used throughout interface\ntypedef ptrdiff_t Sci_Position;\n\n// Unsigned variant used for ILexer::Lex and ILexer::Fold\ntypedef size_t Sci_PositionU;\n\n// For Sci_CharacterRange  which is defined as long to be compatible with Win32 CHARRANGE\ntypedef long Sci_PositionCR;\n\n#ifdef _WIN32\n\t#define SCI_METHOD __stdcall\n#else\n\t#define SCI_METHOD\n#endif\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/include/Scintilla.iface",
    "content": "## First line may be used for shbang\n\n## This file defines the interface to Scintilla\n\n## Copyright 2000-2003 by Neil Hodgson <neilh@scintilla.org>\n## The License.txt file describes the conditions under which this software may be distributed.\n\n## A line starting with ## is a pure comment and should be stripped by readers.\n## A line starting with #! is for future shbang use\n## A line starting with # followed by a space is a documentation comment and refers\n## to the next feature definition.\n\n## Each feature is defined by a line starting with fun, get, set, val, evt, enu, lex, or ali.\n##     cat -> start a category\n##     fun -> a function\n##     get -> a property get function\n##     set -> a property set function\n##     val -> definition of a constant\n##     evt -> an event\n##     enu -> associate an enumeration with a set of vals with a prefix\n##     lex -> associate a lexer with the lexical classes it produces\n##     ali -> add an alias for a val, commonly adding '_' to separate words\n##\n## All other feature names should be ignored. They may be defined in the future.\n## A property may have a set function, a get function or both. Each will have\n## \"Get\" or \"Set\" in their names and the corresponding name will have the obvious switch.\n## A property may be subscripted, in which case the first parameter is the subscript.\n## fun, get, and set features have a strict syntax:\n## <featureType><ws><returnType><ws><name>[=<number](<param>,<param>)\n## where <ws> stands for white space.\n## param may be empty (null value) or is <paramType><ws><paramName>[=<value>]\n## Additional white space is allowed between elements.\n## The syntax for evt is <featureType><ws><returnType><ws><name>[=<number]([<param>[,<param>]*])\n## Feature names that contain an underscore are defined by Windows, so in these\n## cases, using the Windows definition is preferred where available.\n## The feature numbers are stable so features will not be renumbered.\n## Features may be removed but they will go through a period of deprecation\n## before removal which is signalled by moving them into the Deprecated category.\n##\n## enu has the syntax enu<ws><enumeration>=<prefix>[<ws><prefix>]* where all the val\n## features in this file starting with a given <prefix> are considered part of the\n## enumeration.\n##\n## lex has the syntax lex<ws><name>=<lexerVal><ws><prefix>[<ws><prefix>]*\n## where name is a reasonably capitalised (Python, XML) identifier or UI name,\n## lexerVal is the val used to specify the lexer, and the list of prefixes is similar\n## to enu. The name may not be the same as that used within the lexer so the lexerVal\n## should be used to tie these entities together.\n\n## Types: Never start with a capital letter\n##     void\n##     int\n##     bool -> integer, 1=true, 0=false\n##     position -> intptr_t position in a document\n##     line -> intptr_t line in a document\n##     colour -> colour integer containing red, green, and blue bytes with red as least-significant and blue as most.\n##     colouralpha -> colour integer containing red, green, blue, and alpha bytes with red as least-significant and alpha as most.\n##     string -> pointer to const character\n##     stringresult -> pointer to character, NULL-> return size of result\n##     cells -> pointer to array of cells, each cell containing a style byte and character byte\n##     pointer -> void* pointer that may point to a document, loader, internal text storage or similar\n##     textrange -> range of a min and a max position with an output string\n##     textrangefull -> range of a min and a max position with an output string - supports 64-bit\n##     findtext -> searchrange, text -> foundposition\n##     findtextfull -> searchrange, text -> foundposition\n##     keymod -> integer containing key in low half and modifiers in high half\n##     formatrange\n##     formatrangefull\n## Enumeration types always start with a capital letter\n## Types no longer used:\n##     findtextex -> searchrange\n##     charrange -> range of a min and a max position\n##     charrangeresult -> like charrange, but output param\n##     countedstring\n##     point -> x,y\n##     pointresult  -> like point, but output param\n##     rectangle -> left,top,right,bottom\n## Client code should ignore definitions containing types it does not understand, except\n## for possibly #defining the constants\n\n## Line numbers and positions start at 0.\n## String arguments may contain NUL ('\\0') characters where the calls provide a length\n## argument and retrieve NUL characters. APIs marked as NUL-terminated also have a\n## NUL appended but client code should calculate the size that will be returned rather\n## than relying upon the NUL whenever possible. Allow for the extra NUL character when\n## allocating buffers. The size to allocate for a stringresult (not including NUL) can be\n## determined by calling with a NULL (0) pointer.\n\ncat Basics\n\n################################################\n## For Scintilla.h\nval INVALID_POSITION=-1\n# Define start of Scintilla messages to be greater than all Windows edit (EM_*) messages\n# as many EM_ messages can be used although that use is deprecated.\nval SCI_START=2000\nval SCI_OPTIONAL_START=3000\nval SCI_LEXER_START=4000\n\n# Add text to the document at current position.\nfun void AddText=2001(position length, string text)\n\n# Add array of cells to document.\nfun void AddStyledText=2002(position length, cells c)\n\n# Insert string at a position.\nfun void InsertText=2003(position pos, string text)\n\n# Change the text that is being inserted in response to SC_MOD_INSERTCHECK\nfun void ChangeInsertion=2672(position length, string text)\n\n# Delete all text in the document.\nfun void ClearAll=2004(,)\n\n# Delete a range of text in the document.\nfun void DeleteRange=2645(position start, position lengthDelete)\n\n# Set all style bytes to 0, remove all folding information.\nfun void ClearDocumentStyle=2005(,)\n\n# Returns the number of bytes in the document.\nget position GetLength=2006(,)\n\n# Returns the character byte at the position.\nget int GetCharAt=2007(position pos,)\n\n# Returns the position of the caret.\nget position GetCurrentPos=2008(,)\n\n# Returns the position of the opposite end of the selection to the caret.\nget position GetAnchor=2009(,)\n\n# Returns the style byte at the position.\nget int GetStyleAt=2010(position pos,)\n\n# Returns the unsigned style byte at the position.\nget int GetStyleIndexAt=2038(position pos,)\n\n# Redoes the next action on the undo history.\nfun void Redo=2011(,)\n\n# Choose between collecting actions into the undo\n# history and discarding them.\nset void SetUndoCollection=2012(bool collectUndo,)\n\n# Select all the text in the document.\nfun void SelectAll=2013(,)\n\n# Remember the current position in the undo history as the position\n# at which the document was saved.\nfun void SetSavePoint=2014(,)\n\n# Retrieve a buffer of cells.\n# Returns the number of bytes in the buffer not including terminating NULs.\nfun position GetStyledText=2015(, textrange tr)\n\n# Retrieve a buffer of cells that can be past 2GB.\n# Returns the number of bytes in the buffer not including terminating NULs.\nfun position GetStyledTextFull=2778(, textrangefull tr)\n\n# Are there any redoable actions in the undo history?\nfun bool CanRedo=2016(,)\n\n# Retrieve the line number at which a particular marker is located.\nfun line MarkerLineFromHandle=2017(int markerHandle,)\n\n# Delete a marker.\nfun void MarkerDeleteHandle=2018(int markerHandle,)\n\n# Retrieve marker handles of a line\nfun int MarkerHandleFromLine=2732(line line, int which)\n\n# Retrieve marker number of a marker handle\nfun int MarkerNumberFromLine=2733(line line, int which)\n\n# Is undo history being collected?\nget bool GetUndoCollection=2019(,)\n\nenu WhiteSpace=SCWS_\nval SCWS_INVISIBLE=0\nval SCWS_VISIBLEALWAYS=1\nval SCWS_VISIBLEAFTERINDENT=2\nval SCWS_VISIBLEONLYININDENT=3\n\nali SCWS_VISIBLEALWAYS=VISIBLE_ALWAYS\nali SCWS_VISIBLEAFTERINDENT=VISIBLE_AFTER_INDENT\nali SCWS_VISIBLEONLYININDENT=VISIBLE_ONLY_IN_INDENT\n\n# Are white space characters currently visible?\n# Returns one of SCWS_* constants.\nget WhiteSpace GetViewWS=2020(,)\n\n# Make white space characters invisible, always visible or visible outside indentation.\nset void SetViewWS=2021(WhiteSpace viewWS,)\n\nenu TabDrawMode=SCTD_\nval SCTD_LONGARROW=0\nval SCTD_STRIKEOUT=1\n\nali SCTD_LONGARROW=LONG_ARROW\nali SCTD_STRIKEOUT=STRIKE_OUT\n\n# Retrieve the current tab draw mode.\n# Returns one of SCTD_* constants.\nget TabDrawMode GetTabDrawMode=2698(,)\n\n# Set how tabs are drawn when visible.\nset void SetTabDrawMode=2699(TabDrawMode tabDrawMode,)\n\n# Find the position from a point within the window.\nfun position PositionFromPoint=2022(int x, int y)\n\n# Find the position from a point within the window but return\n# INVALID_POSITION if not close to text.\nfun position PositionFromPointClose=2023(int x, int y)\n\n# Set caret to start of a line and ensure it is visible.\nfun void GotoLine=2024(line line,)\n\n# Set caret to a position and ensure it is visible.\nfun void GotoPos=2025(position caret,)\n\n# Set the selection anchor to a position. The anchor is the opposite\n# end of the selection from the caret.\nset void SetAnchor=2026(position anchor,)\n\n# Retrieve the text of the line containing the caret.\n# Returns the index of the caret on the line.\n# Result is NUL-terminated.\nfun position GetCurLine=2027(position length, stringresult text)\n\n# Retrieve the position of the last correctly styled character.\nget position GetEndStyled=2028(,)\n\nenu EndOfLine=SC_EOL_\nval SC_EOL_CRLF=0\nval SC_EOL_CR=1\nval SC_EOL_LF=2\n\nali SC_EOL_CRLF=CR_LF\n\n# Convert all line endings in the document to one mode.\nfun void ConvertEOLs=2029(EndOfLine eolMode,)\n\n# Retrieve the current end of line mode - one of CRLF, CR, or LF.\nget EndOfLine GetEOLMode=2030(,)\n\n# Set the current end of line mode.\nset void SetEOLMode=2031(EndOfLine eolMode,)\n\n# Set the current styling position to start.\n# The unused parameter is no longer used and should be set to 0.\nfun void StartStyling=2032(position start, int unused)\n\n# Change style from current styling position for length characters to a style\n# and move the current styling position to after this newly styled segment.\nfun void SetStyling=2033(position length, int style)\n\n# Is drawing done first into a buffer or direct to the screen?\nget bool GetBufferedDraw=2034(,)\n\n# If drawing is buffered then each line of text is drawn into a bitmap buffer\n# before drawing it to the screen to avoid flicker.\nset void SetBufferedDraw=2035(bool buffered,)\n\n# Change the visible size of a tab to be a multiple of the width of a space character.\nset void SetTabWidth=2036(int tabWidth,)\n\n# Retrieve the visible size of a tab.\nget int GetTabWidth=2121(,)\n\n# Set the minimum visual width of a tab.\nset void SetTabMinimumWidth=2724(int pixels,)\n\n# Get the minimum visual width of a tab.\nget int GetTabMinimumWidth=2725(,)\n\n# Clear explicit tabstops on a line.\nfun void ClearTabStops=2675(line line,)\n\n# Add an explicit tab stop for a line.\nfun void AddTabStop=2676(line line, int x)\n\n# Find the next explicit tab stop position on a line after a position.\nfun int GetNextTabStop=2677(line line, int x)\n\n# The SC_CP_UTF8 value can be used to enter Unicode mode.\n# This is the same value as CP_UTF8 in Windows\nval SC_CP_UTF8=65001\n\n# Set the code page used to interpret the bytes of the document as characters.\n# The SC_CP_UTF8 value can be used to enter Unicode mode.\nset void SetCodePage=2037(int codePage,)\n\n# Set the locale for displaying text.\nset void SetFontLocale=2760(, string localeName)\n\n# Get the locale for displaying text.\nget int GetFontLocale=2761(, stringresult localeName)\n\nenu IMEInteraction=SC_IME_\nval SC_IME_WINDOWED=0\nval SC_IME_INLINE=1\n\n# Is the IME displayed in a window or inline?\nget IMEInteraction GetIMEInteraction=2678(,)\n\n# Choose to display the IME in a window or inline.\nset void SetIMEInteraction=2679(IMEInteraction imeInteraction,)\n\nenu Alpha=SC_ALPHA_\nval SC_ALPHA_TRANSPARENT=0\nval SC_ALPHA_OPAQUE=255\nval SC_ALPHA_NOALPHA=256\n\nali SC_ALPHA_NOALPHA=NO_ALPHA\n\nenu CursorShape=SC_CURSOR\nval SC_CURSORNORMAL=-1\nval SC_CURSORARROW=2\nval SC_CURSORWAIT=4\nval SC_CURSORREVERSEARROW=7\n\nali SC_CURSORREVERSEARROW=REVERSE_ARROW\n\nenu MarkerSymbol=SC_MARK_\nval MARKER_MAX=31\nval SC_MARK_CIRCLE=0\nval SC_MARK_ROUNDRECT=1\nval SC_MARK_ARROW=2\nval SC_MARK_SMALLRECT=3\nval SC_MARK_SHORTARROW=4\nval SC_MARK_EMPTY=5\nval SC_MARK_ARROWDOWN=6\nval SC_MARK_MINUS=7\nval SC_MARK_PLUS=8\n\n# Shapes used for outlining column.\nval SC_MARK_VLINE=9\nval SC_MARK_LCORNER=10\nval SC_MARK_TCORNER=11\nval SC_MARK_BOXPLUS=12\nval SC_MARK_BOXPLUSCONNECTED=13\nval SC_MARK_BOXMINUS=14\nval SC_MARK_BOXMINUSCONNECTED=15\nval SC_MARK_LCORNERCURVE=16\nval SC_MARK_TCORNERCURVE=17\nval SC_MARK_CIRCLEPLUS=18\nval SC_MARK_CIRCLEPLUSCONNECTED=19\nval SC_MARK_CIRCLEMINUS=20\nval SC_MARK_CIRCLEMINUSCONNECTED=21\n\n# Invisible mark that only sets the line background colour.\nval SC_MARK_BACKGROUND=22\nval SC_MARK_DOTDOTDOT=23\nval SC_MARK_ARROWS=24\nval SC_MARK_PIXMAP=25\nval SC_MARK_FULLRECT=26\nval SC_MARK_LEFTRECT=27\nval SC_MARK_AVAILABLE=28\nval SC_MARK_UNDERLINE=29\nval SC_MARK_RGBAIMAGE=30\nval SC_MARK_BOOKMARK=31\nval SC_MARK_VERTICALBOOKMARK=32\nval SC_MARK_BAR=33\n\nval SC_MARK_CHARACTER=10000\n\nali SC_MARK_ROUNDRECT=ROUND_RECT\nali SC_MARK_SMALLRECT=SMALL_RECT\nali SC_MARK_SHORTARROW=SHORT_ARROW\nali SC_MARK_ARROWDOWN=ARROW_DOWN\nali SC_MARK_VLINE=V_LINE\nali SC_MARK_LCORNER=L_CORNER\nali SC_MARK_TCORNER=T_CORNER\nali SC_MARK_BOXPLUS=BOX_PLUS\nali SC_MARK_BOXPLUSCONNECTED=BOX_PLUS_CONNECTED\nali SC_MARK_BOXMINUS=BOX_MINUS\nali SC_MARK_BOXMINUSCONNECTED=BOX_MINUS_CONNECTED\nali SC_MARK_LCORNERCURVE=L_CORNER_CURVE\nali SC_MARK_TCORNERCURVE=T_CORNER_CURVE\nali SC_MARK_CIRCLEPLUS=CIRCLE_PLUS\nali SC_MARK_CIRCLEPLUSCONNECTED=CIRCLE_PLUS_CONNECTED\nali SC_MARK_CIRCLEMINUS=CIRCLE_MINUS\nali SC_MARK_CIRCLEMINUSCONNECTED=CIRCLE_MINUS_CONNECTED\nali SC_MARK_DOTDOTDOT=DOT_DOT_DOT\nali SC_MARK_FULLRECT=FULL_RECT\nali SC_MARK_LEFTRECT=LEFT_RECT\nali SC_MARK_RGBAIMAGE=RGBA_IMAGE\nali SC_MARK_VERTICALBOOKMARK=VERTICAL_BOOKMARK\n\nenu MarkerOutline=SC_MARKNUM_\n# Markers used for outlining and change history columns.\nval SC_MARKNUM_HISTORY_REVERTED_TO_ORIGIN=21\nval SC_MARKNUM_HISTORY_SAVED=22\nval SC_MARKNUM_HISTORY_MODIFIED=23\nval SC_MARKNUM_HISTORY_REVERTED_TO_MODIFIED=24\nval SC_MARKNUM_FOLDEREND=25\nval SC_MARKNUM_FOLDEROPENMID=26\nval SC_MARKNUM_FOLDERMIDTAIL=27\nval SC_MARKNUM_FOLDERTAIL=28\nval SC_MARKNUM_FOLDERSUB=29\nval SC_MARKNUM_FOLDER=30\nval SC_MARKNUM_FOLDEROPEN=31\n\nali SC_MARKNUM_FOLDEREND=FOLDER_END\nali SC_MARKNUM_FOLDEROPENMID=FOLDER_OPEN_MID\nali SC_MARKNUM_FOLDERMIDTAIL=FOLDER_MID_TAIL\nali SC_MARKNUM_FOLDERTAIL=FOLDER_TAIL\nali SC_MARKNUM_FOLDERSUB=FOLDER_SUB\nali SC_MARKNUM_FOLDEROPEN=FOLDER_OPEN\n\nval SC_MASK_HISTORY=0x01E00000\n\n# SC_MASK_FOLDERS doesn't go in an enumeration as larger than max 32-bit positive integer\nval SC_MASK_FOLDERS=0xFE000000\n\n# Set the symbol used for a particular marker number.\nfun void MarkerDefine=2040(int markerNumber, MarkerSymbol markerSymbol)\n\n# Set the foreground colour used for a particular marker number.\nset void MarkerSetFore=2041(int markerNumber, colour fore)\n\n# Set the background colour used for a particular marker number.\nset void MarkerSetBack=2042(int markerNumber, colour back)\n\n# Set the background colour used for a particular marker number when its folding block is selected.\nset void MarkerSetBackSelected=2292(int markerNumber, colour back)\n\n# Set the foreground colour used for a particular marker number.\nset void MarkerSetForeTranslucent=2294(int markerNumber, colouralpha fore)\n\n# Set the background colour used for a particular marker number.\nset void MarkerSetBackTranslucent=2295(int markerNumber, colouralpha back)\n\n# Set the background colour used for a particular marker number when its folding block is selected.\nset void MarkerSetBackSelectedTranslucent=2296(int markerNumber, colouralpha back)\n\n# Set the width of strokes used in .01 pixels so 50  = 1/2 pixel width.\nset void MarkerSetStrokeWidth=2297(int markerNumber, int hundredths)\n\n# Enable/disable highlight for current folding block (smallest one that contains the caret)\nfun void MarkerEnableHighlight=2293(bool enabled,)\n\n# Add a marker to a line, returning an ID which can be used to find or delete the marker.\nfun int MarkerAdd=2043(line line, int markerNumber)\n\n# Delete a marker from a line.\nfun void MarkerDelete=2044(line line, int markerNumber)\n\n# Delete all markers with a particular number from all lines.\nfun void MarkerDeleteAll=2045(int markerNumber,)\n\n# Get a bit mask of all the markers set on a line.\nfun int MarkerGet=2046(line line,)\n\n# Find the next line at or after lineStart that includes a marker in mask.\n# Return -1 when no more lines.\nfun line MarkerNext=2047(line lineStart, int markerMask)\n\n# Find the previous line before lineStart that includes a marker in mask.\nfun line MarkerPrevious=2048(line lineStart, int markerMask)\n\n# Define a marker from a pixmap.\nfun void MarkerDefinePixmap=2049(int markerNumber, string pixmap)\n\n# Add a set of markers to a line.\nfun void MarkerAddSet=2466(line line, int markerSet)\n\n# Set the alpha used for a marker that is drawn in the text area, not the margin.\nset void MarkerSetAlpha=2476(int markerNumber, Alpha alpha)\n\n# Get the layer used for a marker that is drawn in the text area, not the margin.\nget Layer MarkerGetLayer=2734(int markerNumber,)\n\n# Set the layer used for a marker that is drawn in the text area, not the margin.\nset void MarkerSetLayer=2735(int markerNumber, Layer layer)\n\nval SC_MAX_MARGIN=4\n\nenu MarginType=SC_MARGIN_\nval SC_MARGIN_SYMBOL=0\nval SC_MARGIN_NUMBER=1\nval SC_MARGIN_BACK=2\nval SC_MARGIN_FORE=3\nval SC_MARGIN_TEXT=4\nval SC_MARGIN_RTEXT=5\nval SC_MARGIN_COLOUR=6\n\nali SC_MARGIN_RTEXT=R_TEXT\n\n# Set a margin to be either numeric or symbolic.\nset void SetMarginTypeN=2240(int margin, MarginType marginType)\n\n# Retrieve the type of a margin.\nget MarginType GetMarginTypeN=2241(int margin,)\n\n# Set the width of a margin to a width expressed in pixels.\nset void SetMarginWidthN=2242(int margin, int pixelWidth)\n\n# Retrieve the width of a margin in pixels.\nget int GetMarginWidthN=2243(int margin,)\n\n# Set a mask that determines which markers are displayed in a margin.\nset void SetMarginMaskN=2244(int margin, int mask)\n\n# Retrieve the marker mask of a margin.\nget int GetMarginMaskN=2245(int margin,)\n\n# Make a margin sensitive or insensitive to mouse clicks.\nset void SetMarginSensitiveN=2246(int margin, bool sensitive)\n\n# Retrieve the mouse click sensitivity of a margin.\nget bool GetMarginSensitiveN=2247(int margin,)\n\n# Set the cursor shown when the mouse is inside a margin.\nset void SetMarginCursorN=2248(int margin, CursorShape cursor)\n\n# Retrieve the cursor shown in a margin.\nget CursorShape GetMarginCursorN=2249(int margin,)\n\n# Set the background colour of a margin. Only visible for SC_MARGIN_COLOUR.\nset void SetMarginBackN=2250(int margin, colour back)\n\n# Retrieve the background colour of a margin\nget colour GetMarginBackN=2251(int margin,)\n\n# Allocate a non-standard number of margins.\nset void SetMargins=2252(int margins,)\n\n# How many margins are there?.\nget int GetMargins=2253(,)\n\n# Styles in range 32..39 are predefined for parts of the UI and are not used as normal styles.\nenu StylesCommon=STYLE_\nval STYLE_DEFAULT=32\nval STYLE_LINENUMBER=33\nval STYLE_BRACELIGHT=34\nval STYLE_BRACEBAD=35\nval STYLE_CONTROLCHAR=36\nval STYLE_INDENTGUIDE=37\nval STYLE_CALLTIP=38\nval STYLE_FOLDDISPLAYTEXT=39\nval STYLE_LASTPREDEFINED=39\nval STYLE_MAX=255\n\nali STYLE_LINENUMBER=LINE_NUMBER\nali STYLE_BRACELIGHT=BRACE_LIGHT\nali STYLE_BRACEBAD=BRACE_BAD\nali STYLE_CONTROLCHAR=CONTROL_CHAR\nali STYLE_INDENTGUIDE=INDENT_GUIDE\nali STYLE_CALLTIP=CALL_TIP\nali STYLE_FOLDDISPLAYTEXT=FOLD_DISPLAY_TEXT\nali STYLE_LASTPREDEFINED=LAST_PREDEFINED\n\n# Character set identifiers are used in StyleSetCharacterSet.\n# The values are the same as the Windows *_CHARSET values.\nenu CharacterSet=SC_CHARSET_\nval SC_CHARSET_ANSI=0\nval SC_CHARSET_DEFAULT=1\nval SC_CHARSET_BALTIC=186\nval SC_CHARSET_CHINESEBIG5=136\nval SC_CHARSET_EASTEUROPE=238\nval SC_CHARSET_GB2312=134\nval SC_CHARSET_GREEK=161\nval SC_CHARSET_HANGUL=129\nval SC_CHARSET_MAC=77\nval SC_CHARSET_OEM=255\nval SC_CHARSET_RUSSIAN=204\nval SC_CHARSET_OEM866=866\nval SC_CHARSET_CYRILLIC=1251\nval SC_CHARSET_SHIFTJIS=128\nval SC_CHARSET_SYMBOL=2\nval SC_CHARSET_TURKISH=162\nval SC_CHARSET_JOHAB=130\nval SC_CHARSET_HEBREW=177\nval SC_CHARSET_ARABIC=178\nval SC_CHARSET_VIETNAMESE=163\nval SC_CHARSET_THAI=222\nval SC_CHARSET_8859_15=1000\n\nali SC_CHARSET_CHINESEBIG5=CHINESE_BIG5\nali SC_CHARSET_EASTEUROPE=EAST_EUROPE\nali SC_CHARSET_GB2312=G_B_2312\nali SC_CHARSET_OEM866=OEM_866\nali SC_CHARSET_SHIFTJIS=SHIFT_JIS\nali SC_CHARSET_8859_15=ISO_8859_15\n\n# Clear all the styles and make equivalent to the global default style.\nfun void StyleClearAll=2050(,)\n\n# Set the foreground colour of a style.\nset void StyleSetFore=2051(int style, colour fore)\n\n# Set the background colour of a style.\nset void StyleSetBack=2052(int style, colour back)\n\n# Set a style to be bold or not.\nset void StyleSetBold=2053(int style, bool bold)\n\n# Set a style to be italic or not.\nset void StyleSetItalic=2054(int style, bool italic)\n\n# Set the size of characters of a style.\nset void StyleSetSize=2055(int style, int sizePoints)\n\n# Set the font of a style.\nset void StyleSetFont=2056(int style, string fontName)\n\n# Set a style to have its end of line filled or not.\nset void StyleSetEOLFilled=2057(int style, bool eolFilled)\n\n# Reset the default style to its state at startup\nfun void StyleResetDefault=2058(,)\n\n# Set a style to be underlined or not.\nset void StyleSetUnderline=2059(int style, bool underline)\n\nenu CaseVisible=SC_CASE_\nval SC_CASE_MIXED=0\nval SC_CASE_UPPER=1\nval SC_CASE_LOWER=2\nval SC_CASE_CAMEL=3\n\n# Get the foreground colour of a style.\nget colour StyleGetFore=2481(int style,)\n\n# Get the background colour of a style.\nget colour StyleGetBack=2482(int style,)\n\n# Get is a style bold or not.\nget bool StyleGetBold=2483(int style,)\n\n# Get is a style italic or not.\nget bool StyleGetItalic=2484(int style,)\n\n# Get the size of characters of a style.\nget int StyleGetSize=2485(int style,)\n\n# Get the font of a style.\n# Returns the length of the fontName\n# Result is NUL-terminated.\nget int StyleGetFont=2486(int style, stringresult fontName)\n\n# Get is a style to have its end of line filled or not.\nget bool StyleGetEOLFilled=2487(int style,)\n\n# Get is a style underlined or not.\nget bool StyleGetUnderline=2488(int style,)\n\n# Get is a style mixed case, or to force upper or lower case.\nget CaseVisible StyleGetCase=2489(int style,)\n\n# Get the character get of the font in a style.\nget CharacterSet StyleGetCharacterSet=2490(int style,)\n\n# Get is a style visible or not.\nget bool StyleGetVisible=2491(int style,)\n\n# Get is a style changeable or not (read only).\n# Experimental feature, currently buggy.\nget bool StyleGetChangeable=2492(int style,)\n\n# Get is a style a hotspot or not.\nget bool StyleGetHotSpot=2493(int style,)\n\n# Set a style to be mixed case, or to force upper or lower case.\nset void StyleSetCase=2060(int style, CaseVisible caseVisible)\n\nval SC_FONT_SIZE_MULTIPLIER=100\n\n# Set the size of characters of a style. Size is in points multiplied by 100.\nset void StyleSetSizeFractional=2061(int style, int sizeHundredthPoints)\n\n# Get the size of characters of a style in points multiplied by 100\nget int StyleGetSizeFractional=2062(int style,)\n\nenu FontWeight=SC_WEIGHT_\nval SC_WEIGHT_NORMAL=400\nval SC_WEIGHT_SEMIBOLD=600\nval SC_WEIGHT_BOLD=700\n\nali SC_WEIGHT_SEMIBOLD=SEMI_BOLD\n\n# Set the weight of characters of a style.\nset void StyleSetWeight=2063(int style, FontWeight weight)\n\n# Get the weight of characters of a style.\nget FontWeight StyleGetWeight=2064(int style,)\n\n# Set the character set of the font in a style.\nset void StyleSetCharacterSet=2066(int style, CharacterSet characterSet)\n\n# Set a style to be a hotspot or not.\nset void StyleSetHotSpot=2409(int style, bool hotspot)\n\n# Indicate that a style may be monospaced over ASCII graphics characters which enables optimizations.\nset void StyleSetCheckMonospaced=2254(int style, bool checkMonospaced)\n\n# Get whether a style may be monospaced.\nget bool StyleGetCheckMonospaced=2255(int style,)\n\nenu FontStretch=SC_STRETCH_\nval SC_STRETCH_ULTRA_CONDENSED=1\nval SC_STRETCH_EXTRA_CONDENSED=2\nval SC_STRETCH_CONDENSED=3\nval SC_STRETCH_SEMI_CONDENSED=4\nval SC_STRETCH_NORMAL=5\nval SC_STRETCH_SEMI_EXPANDED=6\nval SC_STRETCH_EXPANDED=7\nval SC_STRETCH_EXTRA_EXPANDED=8\nval SC_STRETCH_ULTRA_EXPANDED=9\n\n# Set the stretch of characters of a style.\nset void StyleSetStretch=2258(int style, FontStretch stretch)\n\n# Get the stretch of characters of a style.\nget FontStretch StyleGetStretch=2259(int style,)\n\n# Set the invisible representation for a style.\nset void StyleSetInvisibleRepresentation=2256(int style, string representation)\n\n# Get the invisible representation for a style.\nget int StyleGetInvisibleRepresentation=2257(int style, stringresult representation)\n\nenu Element=SC_ELEMENT_\nval SC_ELEMENT_LIST=0\nval SC_ELEMENT_LIST_BACK=1\nval SC_ELEMENT_LIST_SELECTED=2\nval SC_ELEMENT_LIST_SELECTED_BACK=3\nval SC_ELEMENT_SELECTION_TEXT=10\nval SC_ELEMENT_SELECTION_BACK=11\nval SC_ELEMENT_SELECTION_ADDITIONAL_TEXT=12\nval SC_ELEMENT_SELECTION_ADDITIONAL_BACK=13\nval SC_ELEMENT_SELECTION_SECONDARY_TEXT=14\nval SC_ELEMENT_SELECTION_SECONDARY_BACK=15\nval SC_ELEMENT_SELECTION_INACTIVE_TEXT=16\nval SC_ELEMENT_SELECTION_INACTIVE_BACK=17\nval SC_ELEMENT_SELECTION_INACTIVE_ADDITIONAL_TEXT=18\nval SC_ELEMENT_SELECTION_INACTIVE_ADDITIONAL_BACK=19\nval SC_ELEMENT_CARET=40\nval SC_ELEMENT_CARET_ADDITIONAL=41\nval SC_ELEMENT_CARET_LINE_BACK=50\nval SC_ELEMENT_WHITE_SPACE=60\nval SC_ELEMENT_WHITE_SPACE_BACK=61\nval SC_ELEMENT_HOT_SPOT_ACTIVE=70\nval SC_ELEMENT_HOT_SPOT_ACTIVE_BACK=71\nval SC_ELEMENT_FOLD_LINE=80\nval SC_ELEMENT_HIDDEN_LINE=81\n\n# Set the colour of an element. Translucency (alpha) may or may not be significant\n# and this may depend on the platform. The alpha byte should commonly be 0xff for opaque.\nset void SetElementColour=2753(Element element, colouralpha colourElement)\n\n# Get the colour of an element.\nget colouralpha GetElementColour=2754(Element element,)\n\n# Use the default or platform-defined colour for an element.\nfun void ResetElementColour=2755(Element element,)\n\n# Get whether an element has been set by SetElementColour.\n# When false, a platform-defined or default colour is used.\nget bool GetElementIsSet=2756(Element element,)\n\n# Get whether an element supports translucency.\nget bool GetElementAllowsTranslucent=2757(Element element,)\n\n# Get the colour of an element.\nget colouralpha GetElementBaseColour=2758(Element element,)\n\n# Set the foreground colour of the main and additional selections and whether to use this setting.\nfun void SetSelFore=2067(bool useSetting, colour fore)\n\n# Set the background colour of the main and additional selections and whether to use this setting.\nfun void SetSelBack=2068(bool useSetting, colour back)\n\n# Get the alpha of the selection.\nget Alpha GetSelAlpha=2477(,)\n\n# Set the alpha of the selection.\nset void SetSelAlpha=2478(Alpha alpha,)\n\n# Is the selection end of line filled?\nget bool GetSelEOLFilled=2479(,)\n\n# Set the selection to have its end of line filled or not.\nset void SetSelEOLFilled=2480(bool filled,)\n\nenu Layer=SC_LAYER_\nval SC_LAYER_BASE=0\nval SC_LAYER_UNDER_TEXT=1\nval SC_LAYER_OVER_TEXT=2\n\n# Get the layer for drawing selections\nget Layer GetSelectionLayer=2762(,)\n\n# Set the layer for drawing selections: either opaquely on base layer or translucently over text\nset void SetSelectionLayer=2763(Layer layer,)\n\n# Get the layer of the background of the line containing the caret.\nget Layer GetCaretLineLayer=2764(,)\n\n# Set the layer of the background of the line containing the caret.\nset void SetCaretLineLayer=2765(Layer layer,)\n\n# Get only highlighting subline instead of whole line.\nget bool GetCaretLineHighlightSubLine=2773(,)\n\n# Set only highlighting subline instead of whole line.\nset void SetCaretLineHighlightSubLine=2774(bool subLine,)\n\n# Set the foreground colour of the caret.\nset void SetCaretFore=2069(colour fore,)\n\n# When key+modifier combination keyDefinition is pressed perform sciCommand.\nfun void AssignCmdKey=2070(keymod keyDefinition, int sciCommand)\n\n# When key+modifier combination keyDefinition is pressed do nothing.\nfun void ClearCmdKey=2071(keymod keyDefinition,)\n\n# Drop all key mappings.\nfun void ClearAllCmdKeys=2072(,)\n\n# Set the styles for a segment of the document.\nfun void SetStylingEx=2073(position length, string styles)\n\n# Set a style to be visible or not.\nset void StyleSetVisible=2074(int style, bool visible)\n\n# Get the time in milliseconds that the caret is on and off.\nget int GetCaretPeriod=2075(,)\n\n# Get the time in milliseconds that the caret is on and off. 0 = steady on.\nset void SetCaretPeriod=2076(int periodMilliseconds,)\n\n# Set the set of characters making up words for when moving or selecting by word.\n# First sets defaults like SetCharsDefault.\nset void SetWordChars=2077(, string characters)\n\n# Get the set of characters making up words for when moving or selecting by word.\n# Returns the number of characters\nget int GetWordChars=2646(, stringresult characters)\n\n# Set the number of characters to have directly indexed categories\nset void SetCharacterCategoryOptimization=2720(int countCharacters,)\n\n# Get the number of characters to have directly indexed categories\nget int GetCharacterCategoryOptimization=2721(,)\n\n# Start a sequence of actions that is undone and redone as a unit.\n# May be nested.\nfun void BeginUndoAction=2078(,)\n\n# End a sequence of actions that is undone and redone as a unit.\nfun void EndUndoAction=2079(,)\n\n# Is an undo sequence active?\nget int GetUndoSequence=2799(,)\n\n# How many undo actions are in the history?\nget int GetUndoActions=2790(,)\n\n# Set action as the save point\nset void SetUndoSavePoint=2791(int action,)\n\n# Which action is the save point?\nget int GetUndoSavePoint=2792(,)\n\n# Set action as the detach point\nset void SetUndoDetach=2793(int action,)\n\n# Which action is the detach point?\nget int GetUndoDetach=2794(,)\n\n# Set action as the tentative point\nset void SetUndoTentative=2795(int action,)\n\n# Which action is the tentative point?\nget int GetUndoTentative=2796(,)\n\n# Set action as the current point\nset void SetUndoCurrent=2797(int action,)\n\n# Which action is the current point?\nget int GetUndoCurrent=2798(,)\n\n# Push one action onto undo history with no text\nfun void PushUndoActionType=2800(int type, position pos)\n\n# Set the text and length of the most recently pushed action\nfun void ChangeLastUndoActionText=2801(position length, string text)\n\n# What is the type of an action?\nget int GetUndoActionType=2802(int action,)\n\n# What is the position of an action?\nget position GetUndoActionPosition=2803(int action,)\n\n# What is the text of an action?\nget int GetUndoActionText=2804(int action, stringresult text)\n\n# Indicator style enumeration and some constants\nenu IndicatorStyle=INDIC_\nval INDIC_PLAIN=0\nval INDIC_SQUIGGLE=1\nval INDIC_TT=2\nval INDIC_DIAGONAL=3\nval INDIC_STRIKE=4\nval INDIC_HIDDEN=5\nval INDIC_BOX=6\nval INDIC_ROUNDBOX=7\nval INDIC_STRAIGHTBOX=8\nval INDIC_DASH=9\nval INDIC_DOTS=10\nval INDIC_SQUIGGLELOW=11\nval INDIC_DOTBOX=12\nval INDIC_SQUIGGLEPIXMAP=13\nval INDIC_COMPOSITIONTHICK=14\nval INDIC_COMPOSITIONTHIN=15\nval INDIC_FULLBOX=16\nval INDIC_TEXTFORE=17\nval INDIC_POINT=18\nval INDIC_POINTCHARACTER=19\nval INDIC_GRADIENT=20\nval INDIC_GRADIENTCENTRE=21\nval INDIC_POINT_TOP=22\n\n# INDIC_CONTAINER, INDIC_IME, INDIC_IME_MAX, and INDIC_MAX are indicator numbers,\n# not IndicatorStyles so should not really be in the INDIC_ enumeration.\n# They are redeclared in IndicatorNumbers INDICATOR_.\nval INDIC_CONTAINER=8\nval INDIC_IME=32\nval INDIC_IME_MAX=35\nval INDIC_MAX=35\n\nenu IndicatorNumbers=INDICATOR_\nval INDICATOR_CONTAINER=8\nval INDICATOR_IME=32\nval INDICATOR_IME_MAX=35\nval INDICATOR_HISTORY_REVERTED_TO_ORIGIN_INSERTION=36\nval INDICATOR_HISTORY_REVERTED_TO_ORIGIN_DELETION=37\nval INDICATOR_HISTORY_SAVED_INSERTION=38\nval INDICATOR_HISTORY_SAVED_DELETION=39\nval INDICATOR_HISTORY_MODIFIED_INSERTION=40\nval INDICATOR_HISTORY_MODIFIED_DELETION=41\nval INDICATOR_HISTORY_REVERTED_TO_MODIFIED_INSERTION=42\nval INDICATOR_HISTORY_REVERTED_TO_MODIFIED_DELETION=43\nval INDICATOR_MAX=43\n\nali INDIC_TT=T_T\nali INDIC_ROUNDBOX=ROUND_BOX\nali INDIC_STRAIGHTBOX=STRAIGHT_BOX\nali INDIC_SQUIGGLELOW=SQUIGGLE_LOW\nali INDIC_DOTBOX=DOT_BOX\nali INDIC_SQUIGGLEPIXMAP=SQUIGGLE_PIXMAP\nali INDIC_COMPOSITIONTHICK=COMPOSITION_THICK\nali INDIC_COMPOSITIONTHIN=COMPOSITION_THIN\nali INDIC_FULLBOX=FULL_BOX\nali INDIC_TEXTFORE=TEXT_FORE\nali INDIC_POINTCHARACTER=POINT_CHARACTER\nali INDIC_GRADIENTCENTRE=GRADIENT_CENTRE\n\n# Set an indicator to plain, squiggle or TT.\nset void IndicSetStyle=2080(int indicator, IndicatorStyle indicatorStyle)\n\n# Retrieve the style of an indicator.\nget IndicatorStyle IndicGetStyle=2081(int indicator,)\n\n# Set the foreground colour of an indicator.\nset void IndicSetFore=2082(int indicator, colour fore)\n\n# Retrieve the foreground colour of an indicator.\nget colour IndicGetFore=2083(int indicator,)\n\n# Set an indicator to draw under text or over(default).\nset void IndicSetUnder=2510(int indicator, bool under)\n\n# Retrieve whether indicator drawn under or over text.\nget bool IndicGetUnder=2511(int indicator,)\n\n# Set a hover indicator to plain, squiggle or TT.\nset void IndicSetHoverStyle=2680(int indicator, IndicatorStyle indicatorStyle)\n\n# Retrieve the hover style of an indicator.\nget IndicatorStyle IndicGetHoverStyle=2681(int indicator,)\n\n# Set the foreground hover colour of an indicator.\nset void IndicSetHoverFore=2682(int indicator, colour fore)\n\n# Retrieve the foreground hover colour of an indicator.\nget colour IndicGetHoverFore=2683(int indicator,)\n\nenu IndicValue=SC_INDICVALUE\nval SC_INDICVALUEBIT=0x1000000\nval SC_INDICVALUEMASK=0xFFFFFF\n\nenu IndicFlag=SC_INDICFLAG_\nval SC_INDICFLAG_NONE=0\nval SC_INDICFLAG_VALUEFORE=1\n\nali SC_INDICFLAG_VALUEFORE=VALUE_FORE\n\n# Set the attributes of an indicator.\nset void IndicSetFlags=2684(int indicator, IndicFlag flags)\n\n# Retrieve the attributes of an indicator.\nget IndicFlag IndicGetFlags=2685(int indicator,)\n\n# Set the stroke width of an indicator in hundredths of a pixel.\nset void IndicSetStrokeWidth=2751(int indicator, int hundredths)\n\n# Retrieve the stroke width of an indicator.\nget int IndicGetStrokeWidth=2752(int indicator,)\n\n# Set the foreground colour of all whitespace and whether to use this setting.\nfun void SetWhitespaceFore=2084(bool useSetting, colour fore)\n\n# Set the background colour of all whitespace and whether to use this setting.\nfun void SetWhitespaceBack=2085(bool useSetting, colour back)\n\n# Set the size of the dots used to mark space characters.\nset void SetWhitespaceSize=2086(int size,)\n\n# Get the size of the dots used to mark space characters.\nget int GetWhitespaceSize=2087(,)\n\n# Used to hold extra styling information for each line.\nset void SetLineState=2092(line line, int state)\n\n# Retrieve the extra styling information for a line.\nget int GetLineState=2093(line line,)\n\n# Retrieve the last line number that has line state.\nget int GetMaxLineState=2094(,)\n\n# Is the background of the line containing the caret in a different colour?\nget bool GetCaretLineVisible=2095(,)\n\n# Display the background of the line containing the caret in a different colour.\nset void SetCaretLineVisible=2096(bool show,)\n\n# Get the colour of the background of the line containing the caret.\nget colour GetCaretLineBack=2097(,)\n\n# Set the colour of the background of the line containing the caret.\nset void SetCaretLineBack=2098(colour back,)\n\n# Retrieve the caret line frame width.\n# Width = 0 means this option is disabled.\nget int GetCaretLineFrame=2704(,)\n\n# Display the caret line framed.\n# Set width != 0 to enable this option and width = 0 to disable it.\nset void SetCaretLineFrame=2705(int width,)\n\n# Set a style to be changeable or not (read only).\n# Experimental feature, currently buggy.\nset void StyleSetChangeable=2099(int style, bool changeable)\n\n# Display a auto-completion list.\n# The lengthEntered parameter indicates how many characters before\n# the caret should be used to provide context.\nfun void AutoCShow=2100(position lengthEntered, string itemList)\n\n# Remove the auto-completion list from the screen.\nfun void AutoCCancel=2101(,)\n\n# Is there an auto-completion list visible?\nfun bool AutoCActive=2102(,)\n\n# Retrieve the position of the caret when the auto-completion list was displayed.\nfun position AutoCPosStart=2103(,)\n\n# User has selected an item so remove the list and insert the selection.\nfun void AutoCComplete=2104(,)\n\n# Define a set of character that when typed cancel the auto-completion list.\nfun void AutoCStops=2105(, string characterSet)\n\n# Change the separator character in the string setting up an auto-completion list.\n# Default is space but can be changed if items contain space.\nset void AutoCSetSeparator=2106(int separatorCharacter,)\n\n# Retrieve the auto-completion list separator character.\nget int AutoCGetSeparator=2107(,)\n\n# Select the item in the auto-completion list that starts with a string.\nfun void AutoCSelect=2108(, string select)\n\n# Should the auto-completion list be cancelled if the user backspaces to a\n# position before where the box was created.\nset void AutoCSetCancelAtStart=2110(bool cancel,)\n\n# Retrieve whether auto-completion cancelled by backspacing before start.\nget bool AutoCGetCancelAtStart=2111(,)\n\n# Define a set of characters that when typed will cause the autocompletion to\n# choose the selected item.\nset void AutoCSetFillUps=2112(, string characterSet)\n\n# Should a single item auto-completion list automatically choose the item.\nset void AutoCSetChooseSingle=2113(bool chooseSingle,)\n\n# Retrieve whether a single item auto-completion list automatically choose the item.\nget bool AutoCGetChooseSingle=2114(,)\n\n# Set whether case is significant when performing auto-completion searches.\nset void AutoCSetIgnoreCase=2115(bool ignoreCase,)\n\n# Retrieve state of ignore case flag.\nget bool AutoCGetIgnoreCase=2116(,)\n\n# Display a list of strings and send notification when user chooses one.\nfun void UserListShow=2117(int listType, string itemList)\n\n# Set whether or not autocompletion is hidden automatically when nothing matches.\nset void AutoCSetAutoHide=2118(bool autoHide,)\n\n# Retrieve whether or not autocompletion is hidden automatically when nothing matches.\nget bool AutoCGetAutoHide=2119(,)\n\n# Define option flags for autocompletion lists\nenu AutoCompleteOption=SC_AUTOCOMPLETE_\nval SC_AUTOCOMPLETE_NORMAL=0\n# Win32 specific:\nval SC_AUTOCOMPLETE_FIXED_SIZE=1\n# Always select the first item in the autocompletion list:\nval SC_AUTOCOMPLETE_SELECT_FIRST_ITEM=2\n\n# Set autocompletion options.\nset void AutoCSetOptions=2638(AutoCompleteOption options,)\n\n# Retrieve autocompletion options.\nget AutoCompleteOption AutoCGetOptions=2639(,)\n\n# Set whether or not autocompletion deletes any word characters\n# after the inserted text upon completion.\nset void AutoCSetDropRestOfWord=2270(bool dropRestOfWord,)\n\n# Retrieve whether or not autocompletion deletes any word characters\n# after the inserted text upon completion.\nget bool AutoCGetDropRestOfWord=2271(,)\n\n# Register an XPM image for use in autocompletion lists.\nfun void RegisterImage=2405(int type, string xpmData)\n\n# Clear all the registered XPM images.\nfun void ClearRegisteredImages=2408(,)\n\n# Retrieve the auto-completion list type-separator character.\nget int AutoCGetTypeSeparator=2285(,)\n\n# Change the type-separator character in the string setting up an auto-completion list.\n# Default is '?' but can be changed if items contain '?'.\nset void AutoCSetTypeSeparator=2286(int separatorCharacter,)\n\n# Set the maximum width, in characters, of auto-completion and user lists.\n# Set to 0 to autosize to fit longest item, which is the default.\nset void AutoCSetMaxWidth=2208(int characterCount,)\n\n# Get the maximum width, in characters, of auto-completion and user lists.\nget int AutoCGetMaxWidth=2209(,)\n\n# Set the maximum height, in rows, of auto-completion and user lists.\n# The default is 5 rows.\nset void AutoCSetMaxHeight=2210(int rowCount,)\n\n# Set the maximum height, in rows, of auto-completion and user lists.\nget int AutoCGetMaxHeight=2211(,)\n\n# Set the style number used for auto-completion and user lists fonts.\nset void AutoCSetStyle=2109(int style,)\n\n# Get the style number used for auto-completion and user lists fonts.\nget int AutoCGetStyle=2120(,)\n\n# Set the scale factor in percent for auto-completion list images.\nset void AutoCSetImageScale=2815(int scalePercent,)\n\n# Get the scale factor in percent for auto-completion list images.\nget int AutoCGetImageScale=2816(,)\n\n# Set the number of spaces used for one level of indentation.\nset void SetIndent=2122(int indentSize,)\n\n# Retrieve indentation size.\nget int GetIndent=2123(,)\n\n# Indentation will only use space characters if useTabs is false, otherwise\n# it will use a combination of tabs and spaces.\nset void SetUseTabs=2124(bool useTabs,)\n\n# Retrieve whether tabs will be used in indentation.\nget bool GetUseTabs=2125(,)\n\n# Change the indentation of a line to a number of columns.\nset void SetLineIndentation=2126(line line, int indentation)\n\n# Retrieve the number of columns that a line is indented.\nget int GetLineIndentation=2127(line line,)\n\n# Retrieve the position before the first non indentation character on a line.\nget position GetLineIndentPosition=2128(line line,)\n\n# Retrieve the column number of a position, taking tab width into account.\nget position GetColumn=2129(position pos,)\n\n# Count characters between two positions.\nfun position CountCharacters=2633(position start, position end)\n\n# Count code units between two positions.\nfun position CountCodeUnits=2715(position start, position end)\n\n# Show or hide the horizontal scroll bar.\nset void SetHScrollBar=2130(bool visible,)\n# Is the horizontal scroll bar visible?\nget bool GetHScrollBar=2131(,)\n\nenu IndentView=SC_IV_\nval SC_IV_NONE=0\nval SC_IV_REAL=1\nval SC_IV_LOOKFORWARD=2\nval SC_IV_LOOKBOTH=3\n\nali SC_IV_LOOKFORWARD=LOOK_FORWARD\nali SC_IV_LOOKBOTH=LOOK_BOTH\n\n# Show or hide indentation guides.\nset void SetIndentationGuides=2132(IndentView indentView,)\n\n# Are the indentation guides visible?\nget IndentView GetIndentationGuides=2133(,)\n\n# Set the highlighted indentation guide column.\n# 0 = no highlighted guide.\nset void SetHighlightGuide=2134(position column,)\n\n# Get the highlighted indentation guide column.\nget position GetHighlightGuide=2135(,)\n\n# Get the position after the last visible characters on a line.\nget position GetLineEndPosition=2136(line line,)\n\n# Get the code page used to interpret the bytes of the document as characters.\nget int GetCodePage=2137(,)\n\n# Get the foreground colour of the caret.\nget colour GetCaretFore=2138(,)\n\n# In read-only mode?\nget bool GetReadOnly=2140(,)\n\n# Sets the position of the caret.\nset void SetCurrentPos=2141(position caret,)\n\n# Sets the position that starts the selection - this becomes the anchor.\nset void SetSelectionStart=2142(position anchor,)\n\n# Returns the position at the start of the selection.\nget position GetSelectionStart=2143(,)\n\n# Sets the position that ends the selection - this becomes the caret.\nset void SetSelectionEnd=2144(position caret,)\n\n# Returns the position at the end of the selection.\nget position GetSelectionEnd=2145(,)\n\n# Set caret to a position, while removing any existing selection.\nfun void SetEmptySelection=2556(position caret,)\n\n# Sets the print magnification added to the point size of each style for printing.\nset void SetPrintMagnification=2146(int magnification,)\n\n# Returns the print magnification.\nget int GetPrintMagnification=2147(,)\n\nenu PrintOption=SC_PRINT_\n# PrintColourMode - use same colours as screen.\n# with the exception of line number margins, which use a white background\nval SC_PRINT_NORMAL=0\n# PrintColourMode - invert the light value of each style for printing.\nval SC_PRINT_INVERTLIGHT=1\n# PrintColourMode - force black text on white background for printing.\nval SC_PRINT_BLACKONWHITE=2\n# PrintColourMode - text stays coloured, but all background is forced to be white for printing.\nval SC_PRINT_COLOURONWHITE=3\n# PrintColourMode - only the default-background is forced to be white for printing.\nval SC_PRINT_COLOURONWHITEDEFAULTBG=4\n# PrintColourMode - use same colours as screen, including line number margins.\nval SC_PRINT_SCREENCOLOURS=5\n\nali SC_PRINT_INVERTLIGHT=INVERT_LIGHT\nali SC_PRINT_BLACKONWHITE=BLACK_ON_WHITE\nali SC_PRINT_COLOURONWHITE=COLOUR_ON_WHITE\nali SC_PRINT_COLOURONWHITEDEFAULTBG=COLOUR_ON_WHITE_DEFAULT_B_G\nali SC_PRINT_SCREENCOLOURS=SCREEN_COLOURS\n\n# Modify colours when printing for clearer printed text.\nset void SetPrintColourMode=2148(PrintOption mode,)\n\n# Returns the print colour mode.\nget PrintOption GetPrintColourMode=2149(,)\n\nenu FindOption=SCFIND_\nval SCFIND_NONE=0x0\nval SCFIND_WHOLEWORD=0x2\nval SCFIND_MATCHCASE=0x4\nval SCFIND_WORDSTART=0x00100000\nval SCFIND_REGEXP=0x00200000\nval SCFIND_POSIX=0x00400000\nval SCFIND_CXX11REGEX=0x00800000\n\nali SCFIND_WHOLEWORD=WHOLE_WORD\nali SCFIND_MATCHCASE=MATCH_CASE\nali SCFIND_WORDSTART=WORD_START\nali SCFIND_REGEXP=REG_EXP\nali SCFIND_CXX11REGEX=CXX11_REG_EX\n\n# Find some text in the document.\nfun position FindText=2150(FindOption searchFlags, findtext ft)\n\n# Find some text in the document.\nfun position FindTextFull=2196(FindOption searchFlags, findtextfull ft)\n\n# Draw the document into a display context such as a printer.\nfun position FormatRange=2151(bool draw, formatrange fr)\n\n# Draw the document into a display context such as a printer.\nfun position FormatRangeFull=2777(bool draw, formatrangefull fr)\n\nenu ChangeHistoryOption=SC_CHANGE_HISTORY_\nval SC_CHANGE_HISTORY_DISABLED=0\nval SC_CHANGE_HISTORY_ENABLED=1\nval SC_CHANGE_HISTORY_MARKERS=2\nval SC_CHANGE_HISTORY_INDICATORS=4\n\n# Enable or disable change history.\nset void SetChangeHistory=2780(ChangeHistoryOption changeHistory,)\n\n# Report change history status.\nget ChangeHistoryOption GetChangeHistory=2781(,)\n\nenu UndoSelectionHistoryOption=SC_UNDO_SELECTION_HISTORY_\nval SC_UNDO_SELECTION_HISTORY_DISABLED=0\nval SC_UNDO_SELECTION_HISTORY_ENABLED=1\nval SC_UNDO_SELECTION_HISTORY_SCROLL=2\n\n# Enable or disable undo selection history.\nset void SetUndoSelectionHistory=2782(UndoSelectionHistoryOption undoSelectionHistory,)\n\n# Report undo selection history status.\nget UndoSelectionHistoryOption GetUndoSelectionHistory=2783(,)\n\n# Set selection from serialized form.\nset void SetSelectionSerialized=2784(, string selectionString)\n\n# Retrieve serialized form of selection.\nget position GetSelectionSerialized=2785(, stringresult selectionString)\n\n# Retrieve the display line at the top of the display.\nget line GetFirstVisibleLine=2152(,)\n\n# Retrieve the contents of a line.\n# Returns the length of the line.\nfun position GetLine=2153(line line, stringresult text)\n\n# Returns the number of lines in the document. There is always at least one.\nget line GetLineCount=2154(,)\n\n# Enlarge the number of lines allocated.\nset void AllocateLines=2089(line lines,)\n\n# Sets the size in pixels of the left margin.\nset void SetMarginLeft=2155(, int pixelWidth)\n\n# Returns the size in pixels of the left margin.\nget int GetMarginLeft=2156(,)\n\n# Sets the size in pixels of the right margin.\nset void SetMarginRight=2157(, int pixelWidth)\n\n# Returns the size in pixels of the right margin.\nget int GetMarginRight=2158(,)\n\n# Is the document different from when it was last saved?\nget bool GetModify=2159(,)\n\n# Select a range of text.\nfun void SetSel=2160(position anchor, position caret)\n\n# Retrieve the selected text.\n# Return the length of the text.\n# Result is NUL-terminated.\nfun position GetSelText=2161(, stringresult text)\n\n# Retrieve a range of text.\n# Return the length of the text.\nfun position GetTextRange=2162(, textrange tr)\n\n# Retrieve a range of text that can be past 2GB.\n# Return the length of the text.\nfun position GetTextRangeFull=2039(, textrangefull tr)\n\n# Draw the selection either highlighted or in normal (non-highlighted) style.\nfun void HideSelection=2163(bool hide,)\n\n#Is the selection visible or hidden?\nget bool GetSelectionHidden=2088(,)\n\n# Retrieve the x value of the point in the window where a position is displayed.\nfun int PointXFromPosition=2164(, position pos)\n\n# Retrieve the y value of the point in the window where a position is displayed.\nfun int PointYFromPosition=2165(, position pos)\n\n# Retrieve the line containing a position.\nfun line LineFromPosition=2166(position pos,)\n\n# Retrieve the position at the start of a line.\nfun position PositionFromLine=2167(line line,)\n\n# Scroll horizontally and vertically.\nfun void LineScroll=2168(position columns, line lines)\n\n# Scroll vertically with allowance for wrapping.\nfun void ScrollVertical=2817(line docLine, line subLine)\n\n# Ensure the caret is visible.\nfun void ScrollCaret=2169(,)\n\n# Scroll the argument positions and the range between them into view giving\n# priority to the primary position then the secondary position.\n# This may be used to make a search match visible.\nfun void ScrollRange=2569(position secondary, position primary)\n\n# Replace the selected text with the argument text.\nfun void ReplaceSel=2170(, string text)\n\n# Set to read only or read write.\nset void SetReadOnly=2171(bool readOnly,)\n\n# Null operation.\nfun void Null=2172(,)\n\n# Will a paste succeed?\nfun bool CanPaste=2173(,)\n\n# Are there any undoable actions in the undo history?\nfun bool CanUndo=2174(,)\n\n# Delete the undo history.\nfun void EmptyUndoBuffer=2175(,)\n\n# Undo one action in the undo history.\nfun void Undo=2176(,)\n\n# Cut the selection to the clipboard.\nfun void Cut=2177(,)\n\n# Copy the selection to the clipboard.\nfun void Copy=2178(,)\n\n# Paste the contents of the clipboard into the document replacing the selection.\nfun void Paste=2179(,)\n\n# Clear the selection.\nfun void Clear=2180(,)\n\n# Replace the contents of the document with the argument text.\nfun void SetText=2181(, string text)\n\n# Retrieve all the text in the document.\n# Returns number of characters retrieved.\n# Result is NUL-terminated.\nfun position GetText=2182(position length, stringresult text)\n\n# Retrieve the number of characters in the document.\nget position GetTextLength=2183(,)\n\n# Retrieve a pointer to a function that processes messages for this Scintilla.\nget pointer GetDirectFunction=2184(,)\n\n# Retrieve a pointer to a function that processes messages for this Scintilla and returns status.\nget pointer GetDirectStatusFunction=2772(,)\n\n# Retrieve a pointer value to use as the first argument when calling\n# the function returned by GetDirectFunction.\nget pointer GetDirectPointer=2185(,)\n\n# Set to overtype (true) or insert mode.\nset void SetOvertype=2186(bool overType,)\n\n# Returns true if overtype mode is active otherwise false is returned.\nget bool GetOvertype=2187(,)\n\n# Set the width of the insert mode caret.\nset void SetCaretWidth=2188(int pixelWidth,)\n\n# Returns the width of the insert mode caret.\nget int GetCaretWidth=2189(,)\n\n# Sets the position that starts the target which is used for updating the\n# document without affecting the scroll position.\nset void SetTargetStart=2190(position start,)\n\n# Get the position that starts the target.\nget position GetTargetStart=2191(,)\n\n# Sets the virtual space of the target start\nset void SetTargetStartVirtualSpace=2728(position space,)\n\n# Get the virtual space of the target start\nget position GetTargetStartVirtualSpace=2729(,)\n\n# Sets the position that ends the target which is used for updating the\n# document without affecting the scroll position.\nset void SetTargetEnd=2192(position end,)\n\n# Get the position that ends the target.\nget position GetTargetEnd=2193(,)\n\n# Sets the virtual space of the target end\nset void SetTargetEndVirtualSpace=2730(position space,)\n\n# Get the virtual space of the target end\nget position GetTargetEndVirtualSpace=2731(,)\n\n# Sets both the start and end of the target in one call.\nfun void SetTargetRange=2686(position start, position end)\n\n# Retrieve the text in the target.\nget position GetTargetText=2687(, stringresult text)\n\n# Make the target range start and end be the same as the selection range start and end.\nfun void TargetFromSelection=2287(,)\n\n# Sets the target to the whole document.\nfun void TargetWholeDocument=2690(,)\n\n# Replace the target text with the argument text.\n# Text is counted so it can contain NULs.\n# Returns the length of the replacement text.\nfun position ReplaceTarget=2194(position length, string text)\n\n# Replace the target text with the argument text after \\d processing.\n# Text is counted so it can contain NULs.\n# Looks for \\d where d is between 1 and 9 and replaces these with the strings\n# matched in the last search operation which were surrounded by \\( and \\).\n# Returns the length of the replacement text including any change\n# caused by processing the \\d patterns.\nfun position ReplaceTargetRE=2195(position length, string text)\n\n# Replace the target text with the argument text but ignore prefix and suffix that\n# are the same as current.\nfun position ReplaceTargetMinimal=2779(position length, string text)\n\n# Search for a counted string in the target and set the target to the found\n# range. Text is counted so it can contain NULs.\n# Returns start of found range or -1 for failure in which case target is not moved.\nfun position SearchInTarget=2197(position length, string text)\n\n# Set the search flags used by SearchInTarget.\nset void SetSearchFlags=2198(FindOption searchFlags,)\n\n# Get the search flags used by SearchInTarget.\nget FindOption GetSearchFlags=2199(,)\n\n# Show a call tip containing a definition near position pos.\nfun void CallTipShow=2200(position pos, string definition)\n\n# Remove the call tip from the screen.\nfun void CallTipCancel=2201(,)\n\n# Is there an active call tip?\nfun bool CallTipActive=2202(,)\n\n# Retrieve the position where the caret was before displaying the call tip.\nget position CallTipPosStart=2203(,)\n\n# Set the start position in order to change when backspacing removes the calltip.\nset void CallTipSetPosStart=2214(position posStart,)\n\n# Highlight a segment of the definition.\nfun void CallTipSetHlt=2204(position highlightStart, position highlightEnd)\n\n# Set the background colour for the call tip.\nset void CallTipSetBack=2205(colour back,)\n\n# Set the foreground colour for the call tip.\nset void CallTipSetFore=2206(colour fore,)\n\n# Set the foreground colour for the highlighted part of the call tip.\nset void CallTipSetForeHlt=2207(colour fore,)\n\n# Enable use of STYLE_CALLTIP and set call tip tab size in pixels.\nset void CallTipUseStyle=2212(int tabSize,)\n\n# Set position of calltip, above or below text.\nset void CallTipSetPosition=2213(bool above,)\n\n# Find the display line of a document line taking hidden lines into account.\nfun line VisibleFromDocLine=2220(line docLine,)\n\n# Find the document line of a display line taking hidden lines into account.\nfun line DocLineFromVisible=2221(line displayLine,)\n\n# The number of display lines needed to wrap a document line\nfun line WrapCount=2235(line docLine,)\n\nenu FoldLevel=SC_FOLDLEVEL\nval SC_FOLDLEVELNONE=0x0\nval SC_FOLDLEVELBASE=0x400\nval SC_FOLDLEVELWHITEFLAG=0x1000\nval SC_FOLDLEVELHEADERFLAG=0x2000\nval SC_FOLDLEVELNUMBERMASK=0x0FFF\n\nali SC_FOLDLEVELWHITEFLAG=WHITE_FLAG\nali SC_FOLDLEVELHEADERFLAG=HEADER_FLAG\nali SC_FOLDLEVELNUMBERMASK=NUMBER_MASK\n\n# Set the fold level of a line.\n# This encodes an integer level along with flags indicating whether the\n# line is a header and whether it is effectively white space.\nset void SetFoldLevel=2222(line line, FoldLevel level)\n\n# Retrieve the fold level of a line.\nget FoldLevel GetFoldLevel=2223(line line,)\n\n# Find the last child line of a header line.\nget line GetLastChild=2224(line line, FoldLevel level)\n\n# Find the parent line of a child line.\nget line GetFoldParent=2225(line line,)\n\n# Make a range of lines visible.\nfun void ShowLines=2226(line lineStart, line lineEnd)\n\n# Make a range of lines invisible.\nfun void HideLines=2227(line lineStart, line lineEnd)\n\n# Is a line visible?\nget bool GetLineVisible=2228(line line,)\n\n# Are all lines visible?\nget bool GetAllLinesVisible=2236(,)\n\n# Show the children of a header line.\nset void SetFoldExpanded=2229(line line, bool expanded)\n\n# Is a header line expanded?\nget bool GetFoldExpanded=2230(line line,)\n\n# Switch a header line between expanded and contracted.\nfun void ToggleFold=2231(line line,)\n\n# Switch a header line between expanded and contracted and show some text after the line.\nfun void ToggleFoldShowText=2700(line line, string text)\n\nenu FoldDisplayTextStyle=SC_FOLDDISPLAYTEXT_\nval SC_FOLDDISPLAYTEXT_HIDDEN=0\nval SC_FOLDDISPLAYTEXT_STANDARD=1\nval SC_FOLDDISPLAYTEXT_BOXED=2\n\n# Set the style of fold display text.\nset void FoldDisplayTextSetStyle=2701(FoldDisplayTextStyle style,)\n\n# Get the style of fold display text.\nget FoldDisplayTextStyle FoldDisplayTextGetStyle=2707(,)\n\n# Set the default fold display text.\nfun void SetDefaultFoldDisplayText=2722(, string text)\n\n# Get the default fold display text.\nfun int GetDefaultFoldDisplayText=2723(, stringresult text)\n\nenu FoldAction=SC_FOLDACTION_\nval SC_FOLDACTION_CONTRACT=0\nval SC_FOLDACTION_EXPAND=1\nval SC_FOLDACTION_TOGGLE=2\nval SC_FOLDACTION_CONTRACT_EVERY_LEVEL=4\n\n# Expand or contract a fold header.\nfun void FoldLine=2237(line line, FoldAction action)\n\n# Expand or contract a fold header and its children.\nfun void FoldChildren=2238(line line, FoldAction action)\n\n# Expand a fold header and all children. Use the level argument instead of the line's current level.\nfun void ExpandChildren=2239(line line, FoldLevel level)\n\n# Expand or contract all fold headers.\nfun void FoldAll=2662(FoldAction action,)\n\n# Ensure a particular line is visible by expanding any header line hiding it.\nfun void EnsureVisible=2232(line line,)\n\nenu AutomaticFold=SC_AUTOMATICFOLD_\nval SC_AUTOMATICFOLD_NONE=0x0000\nval SC_AUTOMATICFOLD_SHOW=0x0001\nval SC_AUTOMATICFOLD_CLICK=0x0002\nval SC_AUTOMATICFOLD_CHANGE=0x0004\n\n# Set automatic folding behaviours.\nset void SetAutomaticFold=2663(AutomaticFold automaticFold,)\n\n# Get automatic folding behaviours.\nget AutomaticFold GetAutomaticFold=2664(,)\n\nenu FoldFlag=SC_FOLDFLAG_\nval SC_FOLDFLAG_NONE=0x0000\nval SC_FOLDFLAG_LINEBEFORE_EXPANDED=0x0002\nval SC_FOLDFLAG_LINEBEFORE_CONTRACTED=0x0004\nval SC_FOLDFLAG_LINEAFTER_EXPANDED=0x0008\nval SC_FOLDFLAG_LINEAFTER_CONTRACTED=0x0010\nval SC_FOLDFLAG_LEVELNUMBERS=0x0040\nval SC_FOLDFLAG_LINESTATE=0x0080\n\nali SC_FOLDFLAG_LINEBEFORE_EXPANDED=LINE_BEFORE_EXPANDED\nali SC_FOLDFLAG_LINEBEFORE_CONTRACTED=LINE_BEFORE_CONTRACTED\nali SC_FOLDFLAG_LINEAFTER_EXPANDED=LINE_AFTER_EXPANDED\nali SC_FOLDFLAG_LINEAFTER_CONTRACTED=LINE_AFTER_CONTRACTED\nali SC_FOLDFLAG_LEVELNUMBERS=LEVEL_NUMBERS\nali SC_FOLDFLAG_LINESTATE=LINE_STATE\n\n# Set some style options for folding.\nset void SetFoldFlags=2233(FoldFlag flags,)\n\n# Ensure a particular line is visible by expanding any header line hiding it.\n# Use the currently set visibility policy to determine which range to display.\nfun void EnsureVisibleEnforcePolicy=2234(line line,)\n\n# Sets whether a tab pressed when caret is within indentation indents.\nset void SetTabIndents=2260(bool tabIndents,)\n\n# Does a tab pressed when caret is within indentation indent?\nget bool GetTabIndents=2261(,)\n\n# Sets whether a backspace pressed when caret is within indentation unindents.\nset void SetBackSpaceUnIndents=2262(bool bsUnIndents,)\n\n# Does a backspace pressed when caret is within indentation unindent?\nget bool GetBackSpaceUnIndents=2263(,)\n\nval SC_TIME_FOREVER=10000000\n\n# Sets the time the mouse must sit still to generate a mouse dwell event.\nset void SetMouseDwellTime=2264(int periodMilliseconds,)\n\n# Retrieve the time the mouse must sit still to generate a mouse dwell event.\nget int GetMouseDwellTime=2265(,)\n\n# Get position of start of word.\nfun position WordStartPosition=2266(position pos, bool onlyWordCharacters)\n\n# Get position of end of word.\nfun position WordEndPosition=2267(position pos, bool onlyWordCharacters)\n\n# Is the range start..end considered a word?\nfun bool IsRangeWord=2691(position start, position end)\n\nenu IdleStyling=SC_IDLESTYLING_\nval SC_IDLESTYLING_NONE=0\nval SC_IDLESTYLING_TOVISIBLE=1\nval SC_IDLESTYLING_AFTERVISIBLE=2\nval SC_IDLESTYLING_ALL=3\n\nali SC_IDLESTYLING_TOVISIBLE=TO_VISIBLE\nali SC_IDLESTYLING_AFTERVISIBLE=AFTER_VISIBLE\n\n# Sets limits to idle styling.\nset void SetIdleStyling=2692(IdleStyling idleStyling,)\n\n# Retrieve the limits to idle styling.\nget IdleStyling GetIdleStyling=2693(,)\n\nenu Wrap=SC_WRAP_\nval SC_WRAP_NONE=0\nval SC_WRAP_WORD=1\nval SC_WRAP_CHAR=2\nval SC_WRAP_WHITESPACE=3\n\nali SC_WRAP_WHITESPACE=WHITE_SPACE\n\n# Sets whether text is word wrapped.\nset void SetWrapMode=2268(Wrap wrapMode,)\n\n# Retrieve whether text is word wrapped.\nget Wrap GetWrapMode=2269(,)\n\nenu WrapVisualFlag=SC_WRAPVISUALFLAG_\nval SC_WRAPVISUALFLAG_NONE=0x0000\nval SC_WRAPVISUALFLAG_END=0x0001\nval SC_WRAPVISUALFLAG_START=0x0002\nval SC_WRAPVISUALFLAG_MARGIN=0x0004\n\n# Set the display mode of visual flags for wrapped lines.\nset void SetWrapVisualFlags=2460(WrapVisualFlag wrapVisualFlags,)\n\n# Retrive the display mode of visual flags for wrapped lines.\nget WrapVisualFlag GetWrapVisualFlags=2461(,)\n\nenu WrapVisualLocation=SC_WRAPVISUALFLAGLOC_\nval SC_WRAPVISUALFLAGLOC_DEFAULT=0x0000\nval SC_WRAPVISUALFLAGLOC_END_BY_TEXT=0x0001\nval SC_WRAPVISUALFLAGLOC_START_BY_TEXT=0x0002\n\n# Set the location of visual flags for wrapped lines.\nset void SetWrapVisualFlagsLocation=2462(WrapVisualLocation wrapVisualFlagsLocation,)\n\n# Retrive the location of visual flags for wrapped lines.\nget WrapVisualLocation GetWrapVisualFlagsLocation=2463(,)\n\n# Set the start indent for wrapped lines.\nset void SetWrapStartIndent=2464(int indent,)\n\n# Retrive the start indent for wrapped lines.\nget int GetWrapStartIndent=2465(,)\n\nenu WrapIndentMode=SC_WRAPINDENT_\nval SC_WRAPINDENT_FIXED=0\nval SC_WRAPINDENT_SAME=1\nval SC_WRAPINDENT_INDENT=2\nval SC_WRAPINDENT_DEEPINDENT=3\n\nali SC_WRAPINDENT_DEEPINDENT=DEEP_INDENT\n\n# Sets how wrapped sublines are placed. Default is fixed.\nset void SetWrapIndentMode=2472(WrapIndentMode wrapIndentMode,)\n\n# Retrieve how wrapped sublines are placed. Default is fixed.\nget WrapIndentMode GetWrapIndentMode=2473(,)\n\nenu LineCache=SC_CACHE_\nval SC_CACHE_NONE=0\nval SC_CACHE_CARET=1\nval SC_CACHE_PAGE=2\nval SC_CACHE_DOCUMENT=3\n\n# Sets the degree of caching of layout information.\nset void SetLayoutCache=2272(LineCache cacheMode,)\n\n# Retrieve the degree of caching of layout information.\nget LineCache GetLayoutCache=2273(,)\n\n# Sets the document width assumed for scrolling.\nset void SetScrollWidth=2274(int pixelWidth,)\n\n# Retrieve the document width assumed for scrolling.\nget int GetScrollWidth=2275(,)\n\n# Sets whether the maximum width line displayed is used to set scroll width.\nset void SetScrollWidthTracking=2516(bool tracking,)\n\n# Retrieve whether the scroll width tracks wide lines.\nget bool GetScrollWidthTracking=2517(,)\n\n# Measure the pixel width of some text in a particular style.\n# NUL terminated text argument.\n# Does not handle tab or control characters.\nfun int TextWidth=2276(int style, string text)\n\n# Sets the scroll range so that maximum scroll position has\n# the last line at the bottom of the view (default).\n# Setting this to false allows scrolling one page below the last line.\nset void SetEndAtLastLine=2277(bool endAtLastLine,)\n\n# Retrieve whether the maximum scroll position has the last\n# line at the bottom of the view.\nget bool GetEndAtLastLine=2278(,)\n\n# Retrieve the height of a particular line of text in pixels.\nfun int TextHeight=2279(line line,)\n\n# Show or hide the vertical scroll bar.\nset void SetVScrollBar=2280(bool visible,)\n\n# Is the vertical scroll bar visible?\nget bool GetVScrollBar=2281(,)\n\n# Append a string to the end of the document without changing the selection.\nfun void AppendText=2282(position length, string text)\n\nenu PhasesDraw=SC_PHASES_\nval SC_PHASES_ONE=0\nval SC_PHASES_TWO=1\nval SC_PHASES_MULTIPLE=2\n\n# How many phases is drawing done in?\nget PhasesDraw GetPhasesDraw=2673(,)\n\n# In one phase draw, text is drawn in a series of rectangular blocks with no overlap.\n# In two phase draw, text is drawn in a series of lines allowing runs to overlap horizontally.\n# In multiple phase draw, each element is drawn over the whole drawing area, allowing text\n# to overlap from one line to the next.\nset void SetPhasesDraw=2674(PhasesDraw phases,)\n\n# Control font anti-aliasing.\n\nenu FontQuality=SC_EFF_\nval SC_EFF_QUALITY_MASK=0xF\nval SC_EFF_QUALITY_DEFAULT=0\nval SC_EFF_QUALITY_NON_ANTIALIASED=1\nval SC_EFF_QUALITY_ANTIALIASED=2\nval SC_EFF_QUALITY_LCD_OPTIMIZED=3\n\n# Choose the quality level for text from the FontQuality enumeration.\nset void SetFontQuality=2611(FontQuality fontQuality,)\n\n# Retrieve the quality level for text.\nget FontQuality GetFontQuality=2612(,)\n\n# Scroll so that a display line is at the top of the display.\nset void SetFirstVisibleLine=2613(line displayLine,)\n\nenu MultiPaste=SC_MULTIPASTE_\nval SC_MULTIPASTE_ONCE=0\nval SC_MULTIPASTE_EACH=1\n\n# Change the effect of pasting when there are multiple selections.\nset void SetMultiPaste=2614(MultiPaste multiPaste,)\n\n# Retrieve the effect of pasting when there are multiple selections.\nget MultiPaste GetMultiPaste=2615(,)\n\n# Retrieve the value of a tag from a regular expression search.\n# Result is NUL-terminated.\nget int GetTag=2616(int tagNumber, stringresult tagValue)\n\n# Join the lines in the target.\nfun void LinesJoin=2288(,)\n\n# Split the lines in the target into lines that are less wide than pixelWidth\n# where possible.\nfun void LinesSplit=2289(int pixelWidth,)\n\n# Set one of the colours used as a chequerboard pattern in the fold margin\nfun void SetFoldMarginColour=2290(bool useSetting, colour back)\n# Set the other colour used as a chequerboard pattern in the fold margin\nfun void SetFoldMarginHiColour=2291(bool useSetting, colour fore)\n\nenu Accessibility=SC_ACCESSIBILITY_\nval SC_ACCESSIBILITY_DISABLED=0\nval SC_ACCESSIBILITY_ENABLED=1\n\n# Enable or disable accessibility.\nset void SetAccessibility=2702(Accessibility accessibility,)\n\n# Report accessibility status.\nget Accessibility GetAccessibility=2703(,)\n\n## New messages go here\n\n## Start of key messages\n# Move caret down one line.\nfun void LineDown=2300(,)\n\n# Move caret down one line extending selection to new caret position.\nfun void LineDownExtend=2301(,)\n\n# Move caret up one line.\nfun void LineUp=2302(,)\n\n# Move caret up one line extending selection to new caret position.\nfun void LineUpExtend=2303(,)\n\n# Move caret left one character.\nfun void CharLeft=2304(,)\n\n# Move caret left one character extending selection to new caret position.\nfun void CharLeftExtend=2305(,)\n\n# Move caret right one character.\nfun void CharRight=2306(,)\n\n# Move caret right one character extending selection to new caret position.\nfun void CharRightExtend=2307(,)\n\n# Move caret left one word.\nfun void WordLeft=2308(,)\n\n# Move caret left one word extending selection to new caret position.\nfun void WordLeftExtend=2309(,)\n\n# Move caret right one word.\nfun void WordRight=2310(,)\n\n# Move caret right one word extending selection to new caret position.\nfun void WordRightExtend=2311(,)\n\n# Move caret to first position on line.\nfun void Home=2312(,)\n\n# Move caret to first position on line extending selection to new caret position.\nfun void HomeExtend=2313(,)\n\n# Move caret to last position on line.\nfun void LineEnd=2314(,)\n\n# Move caret to last position on line extending selection to new caret position.\nfun void LineEndExtend=2315(,)\n\n# Move caret to first position in document.\nfun void DocumentStart=2316(,)\n\n# Move caret to first position in document extending selection to new caret position.\nfun void DocumentStartExtend=2317(,)\n\n# Move caret to last position in document.\nfun void DocumentEnd=2318(,)\n\n# Move caret to last position in document extending selection to new caret position.\nfun void DocumentEndExtend=2319(,)\n\n# Move caret one page up.\nfun void PageUp=2320(,)\n\n# Move caret one page up extending selection to new caret position.\nfun void PageUpExtend=2321(,)\n\n# Move caret one page down.\nfun void PageDown=2322(,)\n\n# Move caret one page down extending selection to new caret position.\nfun void PageDownExtend=2323(,)\n\n# Switch from insert to overtype mode or the reverse.\nfun void EditToggleOvertype=2324(,)\n\n# Cancel any modes such as call tip or auto-completion list display.\nfun void Cancel=2325(,)\n\n# Delete the selection or if no selection, the character before the caret.\nfun void DeleteBack=2326(,)\n\n# If selection is empty or all on one line replace the selection with a tab character.\n# If more than one line selected, indent the lines.\nfun void Tab=2327(,)\n\n# Indent the current and selected lines.\nfun void LineIndent=2813(,)\n\n# If selection is empty or all on one line dedent the line if caret is at start, else move caret.\n# If more than one line selected, dedent the lines.\nfun void BackTab=2328(,)\n\n# Dedent the current and selected lines.\nfun void LineDedent=2814(,)\n\n# Insert a new line, may use a CRLF, CR or LF depending on EOL mode.\nfun void NewLine=2329(,)\n\n# Insert a Form Feed character.\nfun void FormFeed=2330(,)\n\n# Move caret to before first visible character on line.\n# If already there move to first character on line.\nfun void VCHome=2331(,)\n\n# Like VCHome but extending selection to new caret position.\nfun void VCHomeExtend=2332(,)\n\n# Magnify the displayed text by increasing the sizes by 1 point.\nfun void ZoomIn=2333(,)\n\n# Make the displayed text smaller by decreasing the sizes by 1 point.\nfun void ZoomOut=2334(,)\n\n# Delete the word to the left of the caret.\nfun void DelWordLeft=2335(,)\n\n# Delete the word to the right of the caret.\nfun void DelWordRight=2336(,)\n\n# Delete the word to the right of the caret, but not the trailing non-word characters.\nfun void DelWordRightEnd=2518(,)\n\n# Cut the line containing the caret.\nfun void LineCut=2337(,)\n\n# Delete the line containing the caret.\nfun void LineDelete=2338(,)\n\n# Switch the current line with the previous.\nfun void LineTranspose=2339(,)\n\n# Reverse order of selected lines.\nfun void LineReverse=2354(,)\n\n# Duplicate the current line.\nfun void LineDuplicate=2404(,)\n\n# Transform the selection to lower case.\nfun void LowerCase=2340(,)\n\n# Transform the selection to upper case.\nfun void UpperCase=2341(,)\n\n# Scroll the document down, keeping the caret visible.\nfun void LineScrollDown=2342(,)\n\n# Scroll the document up, keeping the caret visible.\nfun void LineScrollUp=2343(,)\n\n# Delete the selection or if no selection, the character before the caret.\n# Will not delete the character before at the start of a line.\nfun void DeleteBackNotLine=2344(,)\n\n# Move caret to first position on display line.\nfun void HomeDisplay=2345(,)\n\n# Move caret to first position on display line extending selection to\n# new caret position.\nfun void HomeDisplayExtend=2346(,)\n\n# Move caret to last position on display line.\nfun void LineEndDisplay=2347(,)\n\n# Move caret to last position on display line extending selection to new\n# caret position.\nfun void LineEndDisplayExtend=2348(,)\n\n# Like Home but when word-wrap is enabled goes first to start of display line\n# HomeDisplay, then to start of document line Home.\nfun void HomeWrap=2349(,)\n\n# Like HomeExtend but when word-wrap is enabled extends first to start of display line\n# HomeDisplayExtend, then to start of document line HomeExtend.\nfun void HomeWrapExtend=2450(,)\n\n# Like LineEnd but when word-wrap is enabled goes first to end of display line\n# LineEndDisplay, then to start of document line LineEnd.\nfun void LineEndWrap=2451(,)\n\n# Like LineEndExtend but when word-wrap is enabled extends first to end of display line\n# LineEndDisplayExtend, then to start of document line LineEndExtend.\nfun void LineEndWrapExtend=2452(,)\n\n# Like VCHome but when word-wrap is enabled goes first to start of display line\n# VCHomeDisplay, then behaves like VCHome.\nfun void VCHomeWrap=2453(,)\n\n# Like VCHomeExtend but when word-wrap is enabled extends first to start of display line\n# VCHomeDisplayExtend, then behaves like VCHomeExtend.\nfun void VCHomeWrapExtend=2454(,)\n\n# Copy the line containing the caret.\nfun void LineCopy=2455(,)\n\n# Move the caret inside current view if it's not there already.\nfun void MoveCaretInsideView=2401(,)\n\n# How many characters are on a line, including end of line characters?\nfun position LineLength=2350(line line,)\n\n# Highlight the characters at two positions.\nfun void BraceHighlight=2351(position posA, position posB)\n\n# Use specified indicator to highlight matching braces instead of changing their style.\nfun void BraceHighlightIndicator=2498(bool useSetting, int indicator)\n\n# Highlight the character at a position indicating there is no matching brace.\nfun void BraceBadLight=2352(position pos,)\n\n# Use specified indicator to highlight non matching brace instead of changing its style.\nfun void BraceBadLightIndicator=2499(bool useSetting, int indicator)\n\n# Find the position of a matching brace or INVALID_POSITION if no match.\n# The maxReStyle must be 0 for now. It may be defined in a future release.\nfun position BraceMatch=2353(position pos, int maxReStyle)\n\n# Similar to BraceMatch, but matching starts at the explicit start position.\nfun position BraceMatchNext=2369(position pos, position startPos)\n\n# Are the end of line characters visible?\nget bool GetViewEOL=2355(,)\n\n# Make the end of line characters visible or invisible.\nset void SetViewEOL=2356(bool visible,)\n\n# Retrieve a pointer to the document object.\nget pointer GetDocPointer=2357(,)\n\n# Change the document object used.\nset void SetDocPointer=2358(, pointer doc)\n\n# Set which document modification events are sent to the container.\nset void SetModEventMask=2359(ModificationFlags eventMask,)\n\nenu EdgeVisualStyle=EDGE_\nval EDGE_NONE=0\nval EDGE_LINE=1\nval EDGE_BACKGROUND=2\nval EDGE_MULTILINE=3\n\nali EDGE_MULTILINE=MULTI_LINE\n\n# Retrieve the column number which text should be kept within.\nget position GetEdgeColumn=2360(,)\n\n# Set the column number of the edge.\n# If text goes past the edge then it is highlighted.\nset void SetEdgeColumn=2361(position column,)\n\n# Retrieve the edge highlight mode.\nget EdgeVisualStyle GetEdgeMode=2362(,)\n\n# The edge may be displayed by a line (EDGE_LINE/EDGE_MULTILINE) or by highlighting text that\n# goes beyond it (EDGE_BACKGROUND) or not displayed at all (EDGE_NONE).\nset void SetEdgeMode=2363(EdgeVisualStyle edgeMode,)\n\n# Retrieve the colour used in edge indication.\nget colour GetEdgeColour=2364(,)\n\n# Change the colour used in edge indication.\nset void SetEdgeColour=2365(colour edgeColour,)\n\n# Add a new vertical edge to the view.\nfun void MultiEdgeAddLine=2694(position column, colour edgeColour)\n\n# Clear all vertical edges.\nfun void MultiEdgeClearAll=2695(,)\n\n# Get multi edge positions.\nget position GetMultiEdgeColumn=2749(int which,)\n\n# Sets the current caret position to be the search anchor.\nfun void SearchAnchor=2366(,)\n\n# Find some text starting at the search anchor.\n# Does not ensure the selection is visible.\nfun position SearchNext=2367(FindOption searchFlags, string text)\n\n# Find some text starting at the search anchor and moving backwards.\n# Does not ensure the selection is visible.\nfun position SearchPrev=2368(FindOption searchFlags, string text)\n\n# Retrieves the number of lines completely visible.\nget line LinesOnScreen=2370(,)\n\nenu PopUp=SC_POPUP_\nval SC_POPUP_NEVER=0\nval SC_POPUP_ALL=1\nval SC_POPUP_TEXT=2\n\n# Set whether a pop up menu is displayed automatically when the user presses\n# the wrong mouse button on certain areas.\nfun void UsePopUp=2371(PopUp popUpMode,)\n\n# Is the selection rectangular? The alternative is the more common stream selection.\nget bool SelectionIsRectangle=2372(,)\n\n# Set the zoom level. This number of points is added to the size of all fonts.\n# It may be positive to magnify or negative to reduce.\nset void SetZoom=2373(int zoomInPoints,)\n# Retrieve the zoom level.\nget int GetZoom=2374(,)\n\nenu DocumentOption=SC_DOCUMENTOPTION_\nval SC_DOCUMENTOPTION_DEFAULT=0\nval SC_DOCUMENTOPTION_STYLES_NONE=0x1\nval SC_DOCUMENTOPTION_TEXT_LARGE=0x100\n\n# Create a new document object.\n# Starts with reference count of 1 and not selected into editor.\nfun pointer CreateDocument=2375(position bytes, DocumentOption documentOptions)\n# Extend life of document.\nfun void AddRefDocument=2376(, pointer doc)\n# Release a reference to the document, deleting document if it fades to black.\nfun void ReleaseDocument=2377(, pointer doc)\n\n# Get which document options are set.\nget DocumentOption GetDocumentOptions=2379(,)\n\n# Get which document modification events are sent to the container.\nget ModificationFlags GetModEventMask=2378(,)\n\n# Set whether command events are sent to the container.\nset void SetCommandEvents=2717(bool commandEvents,)\n\n# Get whether command events are sent to the container.\nget bool GetCommandEvents=2718(,)\n\n# Change internal focus flag.\nset void SetFocus=2380(bool focus,)\n# Get internal focus flag.\nget bool GetFocus=2381(,)\n\nenu Status=SC_STATUS_\nval SC_STATUS_OK=0\nval SC_STATUS_FAILURE=1\nval SC_STATUS_BADALLOC=2\nval SC_STATUS_WARN_START=1000\nval SC_STATUS_WARN_REGEX=1001\n\nali SC_STATUS_BADALLOC=BAD_ALLOC\nali SC_STATUS_WARN_REGEX=REG_EX\n\n# Change error status - 0 = OK.\nset void SetStatus=2382(Status status,)\n# Get error status.\nget Status GetStatus=2383(,)\n\n# Set whether the mouse is captured when its button is pressed.\nset void SetMouseDownCaptures=2384(bool captures,)\n# Get whether mouse gets captured.\nget bool GetMouseDownCaptures=2385(,)\n\n# Set whether the mouse wheel can be active outside the window.\nset void SetMouseWheelCaptures=2696(bool captures,)\n# Get whether mouse wheel can be active outside the window.\nget bool GetMouseWheelCaptures=2697(,)\n\n# Sets the cursor to one of the SC_CURSOR* values.\nset void SetCursor=2386(CursorShape cursorType,)\n# Get cursor type.\nget CursorShape GetCursor=2387(,)\n\n# Change the way control characters are displayed:\n# If symbol is < 32, keep the drawn way, else, use the given character.\nset void SetControlCharSymbol=2388(int symbol,)\n# Get the way control characters are displayed.\nget int GetControlCharSymbol=2389(,)\n\n# Move to the previous change in capitalisation.\nfun void WordPartLeft=2390(,)\n# Move to the previous change in capitalisation extending selection\n# to new caret position.\nfun void WordPartLeftExtend=2391(,)\n# Move to the change next in capitalisation.\nfun void WordPartRight=2392(,)\n# Move to the next change in capitalisation extending selection\n# to new caret position.\nfun void WordPartRightExtend=2393(,)\n\n# Constants for use with SetVisiblePolicy, similar to SetCaretPolicy.\nenu VisiblePolicy=VISIBLE_\nval VISIBLE_SLOP=0x01\nval VISIBLE_STRICT=0x04\n\n# Set the way the display area is determined when a particular line\n# is to be moved to by Find, FindNext, GotoLine, etc.\nfun void SetVisiblePolicy=2394(VisiblePolicy visiblePolicy, int visibleSlop)\n\n# Delete back from the current position to the start of the line.\nfun void DelLineLeft=2395(,)\n\n# Delete forwards from the current position to the end of the line.\nfun void DelLineRight=2396(,)\n\n# Set the xOffset (ie, horizontal scroll position).\nset void SetXOffset=2397(int xOffset,)\n\n# Get the xOffset (ie, horizontal scroll position).\nget int GetXOffset=2398(,)\n\n# Set the last x chosen value to be the caret x position.\nfun void ChooseCaretX=2399(,)\n\n# Set the focus to this Scintilla widget.\nfun void GrabFocus=2400(,)\n\nenu CaretPolicy=CARET_\n# Caret policy, used by SetXCaretPolicy and SetYCaretPolicy.\n# If CARET_SLOP is set, we can define a slop value: caretSlop.\n# This value defines an unwanted zone (UZ) where the caret is... unwanted.\n# This zone is defined as a number of pixels near the vertical margins,\n# and as a number of lines near the horizontal margins.\n# By keeping the caret away from the edges, it is seen within its context,\n# so it is likely that the identifier that the caret is on can be completely seen,\n# and that the current line is seen with some of the lines following it which are\n# often dependent on that line.\nval CARET_SLOP=0x01\n# If CARET_STRICT is set, the policy is enforced... strictly.\n# The caret is centred on the display if slop is not set,\n# and cannot go in the UZ if slop is set.\nval CARET_STRICT=0x04\n# If CARET_JUMPS is set, the display is moved more energetically\n# so the caret can move in the same direction longer before the policy is applied again.\nval CARET_JUMPS=0x10\n# If CARET_EVEN is not set, instead of having symmetrical UZs,\n# the left and bottom UZs are extended up to right and top UZs respectively.\n# This way, we favour the displaying of useful information: the beginning of lines,\n# where most code reside, and the lines after the caret, eg. the body of a function.\nval CARET_EVEN=0x08\n\n# Set the way the caret is kept visible when going sideways.\n# The exclusion zone is given in pixels.\nfun void SetXCaretPolicy=2402(CaretPolicy caretPolicy, int caretSlop)\n\n# Set the way the line the caret is on is kept visible.\n# The exclusion zone is given in lines.\nfun void SetYCaretPolicy=2403(CaretPolicy caretPolicy, int caretSlop)\n\n# Set printing to line wrapped (SC_WRAP_WORD) or not line wrapped (SC_WRAP_NONE).\nset void SetPrintWrapMode=2406(Wrap wrapMode,)\n\n# Is printing line wrapped?\nget Wrap GetPrintWrapMode=2407(,)\n\n# Set a fore colour for active hotspots.\nset void SetHotspotActiveFore=2410(bool useSetting, colour fore)\n\n# Get the fore colour for active hotspots.\nget colour GetHotspotActiveFore=2494(,)\n\n# Set a back colour for active hotspots.\nset void SetHotspotActiveBack=2411(bool useSetting, colour back)\n\n# Get the back colour for active hotspots.\nget colour GetHotspotActiveBack=2495(,)\n\n# Enable / Disable underlining active hotspots.\nset void SetHotspotActiveUnderline=2412(bool underline,)\n\n# Get whether underlining for active hotspots.\nget bool GetHotspotActiveUnderline=2496(,)\n\n# Limit hotspots to single line so hotspots on two lines don't merge.\nset void SetHotspotSingleLine=2421(bool singleLine,)\n\n# Get the HotspotSingleLine property\nget bool GetHotspotSingleLine=2497(,)\n\n# Move caret down one paragraph (delimited by empty lines).\nfun void ParaDown=2413(,)\n\n# Extend selection down one paragraph (delimited by empty lines).\nfun void ParaDownExtend=2414(,)\n\n# Move caret up one paragraph (delimited by empty lines).\nfun void ParaUp=2415(,)\n\n# Extend selection up one paragraph (delimited by empty lines).\nfun void ParaUpExtend=2416(,)\n\n# Given a valid document position, return the previous position taking code\n# page into account. Returns 0 if passed 0.\nfun position PositionBefore=2417(position pos,)\n\n# Given a valid document position, return the next position taking code\n# page into account. Maximum value returned is the last position in the document.\nfun position PositionAfter=2418(position pos,)\n\n# Given a valid document position, return a position that differs in a number\n# of characters. Returned value is always between 0 and last position in document.\nfun position PositionRelative=2670(position pos, position relative)\n\n# Given a valid document position, return a position that differs in a number\n# of UTF-16 code units. Returned value is always between 0 and last position in document.\n# The result may point half way (2 bytes) inside a non-BMP character.\nfun position PositionRelativeCodeUnits=2716(position pos, position relative)\n\n# Copy a range of text to the clipboard. Positions are clipped into the document.\nfun void CopyRange=2419(position start, position end)\n\n# Copy argument text to the clipboard.\nfun void CopyText=2420(position length, string text)\n\nenu SelectionMode=SC_SEL_\nval SC_SEL_STREAM=0\nval SC_SEL_RECTANGLE=1\nval SC_SEL_LINES=2\nval SC_SEL_THIN=3\n\n# Set the selection mode to stream (SC_SEL_STREAM) or rectangular (SC_SEL_RECTANGLE/SC_SEL_THIN) or\n# by lines (SC_SEL_LINES).\nset void SetSelectionMode=2422(SelectionMode selectionMode,)\n\n# Set the selection mode to stream (SC_SEL_STREAM) or rectangular (SC_SEL_RECTANGLE/SC_SEL_THIN) or\n# by lines (SC_SEL_LINES) without changing MoveExtendsSelection.\nfun void ChangeSelectionMode=2659(SelectionMode selectionMode,)\n\n# Get the mode of the current selection.\nget SelectionMode GetSelectionMode=2423(,)\n\n# Set whether or not regular caret moves will extend or reduce the selection.\nset void SetMoveExtendsSelection=2719(bool moveExtendsSelection,)\n\n# Get whether or not regular caret moves will extend or reduce the selection.\nget bool GetMoveExtendsSelection=2706(,)\n\n# Retrieve the position of the start of the selection at the given line (INVALID_POSITION if no selection on this line).\nfun position GetLineSelStartPosition=2424(line line,)\n\n# Retrieve the position of the end of the selection at the given line (INVALID_POSITION if no selection on this line).\nfun position GetLineSelEndPosition=2425(line line,)\n\n## RectExtended rectangular selection moves\n# Move caret down one line, extending rectangular selection to new caret position.\nfun void LineDownRectExtend=2426(,)\n\n# Move caret up one line, extending rectangular selection to new caret position.\nfun void LineUpRectExtend=2427(,)\n\n# Move caret left one character, extending rectangular selection to new caret position.\nfun void CharLeftRectExtend=2428(,)\n\n# Move caret right one character, extending rectangular selection to new caret position.\nfun void CharRightRectExtend=2429(,)\n\n# Move caret to first position on line, extending rectangular selection to new caret position.\nfun void HomeRectExtend=2430(,)\n\n# Move caret to before first visible character on line.\n# If already there move to first character on line.\n# In either case, extend rectangular selection to new caret position.\nfun void VCHomeRectExtend=2431(,)\n\n# Move caret to last position on line, extending rectangular selection to new caret position.\nfun void LineEndRectExtend=2432(,)\n\n# Move caret one page up, extending rectangular selection to new caret position.\nfun void PageUpRectExtend=2433(,)\n\n# Move caret one page down, extending rectangular selection to new caret position.\nfun void PageDownRectExtend=2434(,)\n\n\n# Move caret to top of page, or one page up if already at top of page.\nfun void StutteredPageUp=2435(,)\n\n# Move caret to top of page, or one page up if already at top of page, extending selection to new caret position.\nfun void StutteredPageUpExtend=2436(,)\n\n# Move caret to bottom of page, or one page down if already at bottom of page.\nfun void StutteredPageDown=2437(,)\n\n# Move caret to bottom of page, or one page down if already at bottom of page, extending selection to new caret position.\nfun void StutteredPageDownExtend=2438(,)\n\n\n# Move caret left one word, position cursor at end of word.\nfun void WordLeftEnd=2439(,)\n\n# Move caret left one word, position cursor at end of word, extending selection to new caret position.\nfun void WordLeftEndExtend=2440(,)\n\n# Move caret right one word, position cursor at end of word.\nfun void WordRightEnd=2441(,)\n\n# Move caret right one word, position cursor at end of word, extending selection to new caret position.\nfun void WordRightEndExtend=2442(,)\n\n# Set the set of characters making up whitespace for when moving or selecting by word.\n# Should be called after SetWordChars.\nset void SetWhitespaceChars=2443(, string characters)\n\n# Get the set of characters making up whitespace for when moving or selecting by word.\nget int GetWhitespaceChars=2647(, stringresult characters)\n\n# Set the set of characters making up punctuation characters\n# Should be called after SetWordChars.\nset void SetPunctuationChars=2648(, string characters)\n\n# Get the set of characters making up punctuation characters\nget int GetPunctuationChars=2649(, stringresult characters)\n\n# Reset the set of characters for whitespace and word characters to the defaults.\nfun void SetCharsDefault=2444(,)\n\n# Get currently selected item position in the auto-completion list\nget int AutoCGetCurrent=2445(,)\n\n# Get currently selected item text in the auto-completion list\n# Returns the length of the item text\n# Result is NUL-terminated.\nget int AutoCGetCurrentText=2610(, stringresult text)\n\nenu CaseInsensitiveBehaviour=SC_CASEINSENSITIVEBEHAVIOUR_\nval SC_CASEINSENSITIVEBEHAVIOUR_RESPECTCASE=0\nval SC_CASEINSENSITIVEBEHAVIOUR_IGNORECASE=1\n\nali SC_CASEINSENSITIVEBEHAVIOUR_RESPECTCASE=RESPECT_CASE\nali SC_CASEINSENSITIVEBEHAVIOUR_IGNORECASE=IGNORE_CASE\n\n# Set auto-completion case insensitive behaviour to either prefer case-sensitive matches or have no preference.\nset void AutoCSetCaseInsensitiveBehaviour=2634(CaseInsensitiveBehaviour behaviour,)\n\n# Get auto-completion case insensitive behaviour.\nget CaseInsensitiveBehaviour AutoCGetCaseInsensitiveBehaviour=2635(,)\n\nenu MultiAutoComplete=SC_MULTIAUTOC_\nval SC_MULTIAUTOC_ONCE=0\nval SC_MULTIAUTOC_EACH=1\n\n# Change the effect of autocompleting when there are multiple selections.\nset void AutoCSetMulti=2636(MultiAutoComplete multi,)\n\n# Retrieve the effect of autocompleting when there are multiple selections.\nget MultiAutoComplete AutoCGetMulti=2637(,)\n\nenu Ordering=SC_ORDER_\nval SC_ORDER_PRESORTED=0\nval SC_ORDER_PERFORMSORT=1\nval SC_ORDER_CUSTOM=2\n\nali SC_ORDER_PRESORTED=PRE_SORTED\nali SC_ORDER_PERFORMSORT=PERFORM_SORT\n\n# Set the way autocompletion lists are ordered.\nset void AutoCSetOrder=2660(Ordering order,)\n\n# Get the way autocompletion lists are ordered.\nget Ordering AutoCGetOrder=2661(,)\n\n# Enlarge the document to a particular size of text bytes.\nfun void Allocate=2446(position bytes,)\n\n# Returns the target converted to UTF8.\n# Return the length in bytes.\nfun position TargetAsUTF8=2447(, stringresult s)\n\n# Set the length of the utf8 argument for calling EncodedFromUTF8.\n# Set to -1 and the string will be measured to the first nul.\nfun void SetLengthForEncode=2448(position bytes,)\n\n# Translates a UTF8 string into the document encoding.\n# Return the length of the result in bytes.\n# On error return 0.\nfun position EncodedFromUTF8=2449(string utf8, stringresult encoded)\n\n# Find the position of a column on a line taking into account tabs and\n# multi-byte characters. If beyond end of line, return line end position.\nfun position FindColumn=2456(line line, position column)\n\nenu CaretSticky=SC_CARETSTICKY_\nval SC_CARETSTICKY_OFF=0\nval SC_CARETSTICKY_ON=1\nval SC_CARETSTICKY_WHITESPACE=2\n\nali SC_CARETSTICKY_WHITESPACE=WHITE_SPACE\n\n# Can the caret preferred x position only be changed by explicit movement commands?\nget CaretSticky GetCaretSticky=2457(,)\n\n# Stop the caret preferred x position changing when the user types.\nset void SetCaretSticky=2458(CaretSticky useCaretStickyBehaviour,)\n\n# Switch between sticky and non-sticky: meant to be bound to a key.\nfun void ToggleCaretSticky=2459(,)\n\n# Enable/Disable convert-on-paste for line endings\nset void SetPasteConvertEndings=2467(bool convert,)\n\n# Get convert-on-paste setting\nget bool GetPasteConvertEndings=2468(,)\n\n# Replace the selection with text like a rectangular paste.\nfun void ReplaceRectangular=2771(position length, string text)\n\n# Duplicate the selection. If selection empty duplicate the line containing the caret.\nfun void SelectionDuplicate=2469(,)\n\n# Set background alpha of the caret line.\nset void SetCaretLineBackAlpha=2470(Alpha alpha,)\n\n# Get the background alpha of the caret line.\nget Alpha GetCaretLineBackAlpha=2471(,)\n\nenu CaretStyle=CARETSTYLE_\nval CARETSTYLE_INVISIBLE=0\nval CARETSTYLE_LINE=1\nval CARETSTYLE_BLOCK=2\nval CARETSTYLE_OVERSTRIKE_BAR=0\nval CARETSTYLE_OVERSTRIKE_BLOCK=0x10\nval CARETSTYLE_CURSES=0x20\nval CARETSTYLE_INS_MASK=0xF\nval CARETSTYLE_BLOCK_AFTER=0x100\n\n# Set the style of the caret to be drawn.\nset void SetCaretStyle=2512(CaretStyle caretStyle,)\n\n# Returns the current style of the caret.\nget CaretStyle GetCaretStyle=2513(,)\n\n# Set the indicator used for IndicatorFillRange and IndicatorClearRange\nset void SetIndicatorCurrent=2500(int indicator,)\n\n# Get the current indicator\nget int GetIndicatorCurrent=2501(,)\n\n# Set the value used for IndicatorFillRange\nset void SetIndicatorValue=2502(int value,)\n\n# Get the current indicator value\nget int GetIndicatorValue=2503(,)\n\n# Turn a indicator on over a range.\nfun void IndicatorFillRange=2504(position start, position lengthFill)\n\n# Turn a indicator off over a range.\nfun void IndicatorClearRange=2505(position start, position lengthClear)\n\n# Are any indicators present at pos?\nfun int IndicatorAllOnFor=2506(position pos,)\n\n# What value does a particular indicator have at a position?\nfun int IndicatorValueAt=2507(int indicator, position pos)\n\n# Where does a particular indicator start?\nfun position IndicatorStart=2508(int indicator, position pos)\n\n# Where does a particular indicator end?\nfun position IndicatorEnd=2509(int indicator, position pos)\n\n# Set number of entries in position cache\nset void SetPositionCache=2514(int size,)\n\n# How many entries are allocated to the position cache?\nget int GetPositionCache=2515(,)\n\n# Set maximum number of threads used for layout\nset void SetLayoutThreads=2775(int threads,)\n\n# Get maximum number of threads used for layout\nget int GetLayoutThreads=2776(,)\n\n# Copy the selection, if selection empty copy the line with the caret\nfun void CopyAllowLine=2519(,)\n\n# Cut the selection, if selection empty cut the line with the caret\nfun void CutAllowLine=2810(,)\n\n# Set the string to separate parts when copying a multiple selection.\nset void SetCopySeparator=2811(, string separator)\n\n# Get the string to separate parts when copying a multiple selection.\nget int GetCopySeparator=2812(, stringresult separator)\n\n# Compact the document buffer and return a read-only pointer to the\n# characters in the document.\nget pointer GetCharacterPointer=2520(,)\n\n# Return a read-only pointer to a range of characters in the document.\n# May move the gap so that the range is contiguous, but will only move up\n# to lengthRange bytes.\nget pointer GetRangePointer=2643(position start, position lengthRange)\n\n# Return a position which, to avoid performance costs, should not be within\n# the range of a call to GetRangePointer.\nget position GetGapPosition=2644(,)\n\n# Set the alpha fill colour of the given indicator.\nset void IndicSetAlpha=2523(int indicator, Alpha alpha)\n\n# Get the alpha fill colour of the given indicator.\nget Alpha IndicGetAlpha=2524(int indicator,)\n\n# Set the alpha outline colour of the given indicator.\nset void IndicSetOutlineAlpha=2558(int indicator, Alpha alpha)\n\n# Get the alpha outline colour of the given indicator.\nget Alpha IndicGetOutlineAlpha=2559(int indicator,)\n\n# Set extra ascent for each line\nset void SetExtraAscent=2525(int extraAscent,)\n\n# Get extra ascent for each line\nget int GetExtraAscent=2526(,)\n\n# Set extra descent for each line\nset void SetExtraDescent=2527(int extraDescent,)\n\n# Get extra descent for each line\nget int GetExtraDescent=2528(,)\n\n# Which symbol was defined for markerNumber with MarkerDefine\nfun MarkerSymbol MarkerSymbolDefined=2529(int markerNumber,)\n\n# Set the text in the text margin for a line\nset void MarginSetText=2530(line line, string text)\n\n# Get the text in the text margin for a line\nget int MarginGetText=2531(line line, stringresult text)\n\n# Set the style number for the text margin for a line\nset void MarginSetStyle=2532(line line, int style)\n\n# Get the style number for the text margin for a line\nget int MarginGetStyle=2533(line line,)\n\n# Set the style in the text margin for a line\nset void MarginSetStyles=2534(line line, string styles)\n\n# Get the styles in the text margin for a line\nget int MarginGetStyles=2535(line line, stringresult styles)\n\n# Clear the margin text on all lines\nfun void MarginTextClearAll=2536(,)\n\n# Get the start of the range of style numbers used for margin text\nset void MarginSetStyleOffset=2537(int style,)\n\n# Get the start of the range of style numbers used for margin text\nget int MarginGetStyleOffset=2538(,)\n\nenu MarginOption=SC_MARGINOPTION_\nval SC_MARGINOPTION_NONE=0\nval SC_MARGINOPTION_SUBLINESELECT=1\n\nali SC_MARGINOPTION_SUBLINESELECT=SUB_LINE_SELECT\n\n# Set the margin options.\nset void SetMarginOptions=2539(MarginOption marginOptions,)\n\n# Get the margin options.\nget MarginOption GetMarginOptions=2557(,)\n\n# Set the annotation text for a line\nset void AnnotationSetText=2540(line line, string text)\n\n# Get the annotation text for a line\nget int AnnotationGetText=2541(line line, stringresult text)\n\n# Set the style number for the annotations for a line\nset void AnnotationSetStyle=2542(line line, int style)\n\n# Get the style number for the annotations for a line\nget int AnnotationGetStyle=2543(line line,)\n\n# Set the annotation styles for a line\nset void AnnotationSetStyles=2544(line line, string styles)\n\n# Get the annotation styles for a line\nget int AnnotationGetStyles=2545(line line, stringresult styles)\n\n# Get the number of annotation lines for a line\nget int AnnotationGetLines=2546(line line,)\n\n# Clear the annotations from all lines\nfun void AnnotationClearAll=2547(,)\n\nenu AnnotationVisible=ANNOTATION_\nval ANNOTATION_HIDDEN=0\nval ANNOTATION_STANDARD=1\nval ANNOTATION_BOXED=2\nval ANNOTATION_INDENTED=3\n\n# Set the visibility for the annotations for a view\nset void AnnotationSetVisible=2548(AnnotationVisible visible,)\n\n# Get the visibility for the annotations for a view\nget AnnotationVisible AnnotationGetVisible=2549(,)\n\n# Get the start of the range of style numbers used for annotations\nset void AnnotationSetStyleOffset=2550(int style,)\n\n# Get the start of the range of style numbers used for annotations\nget int AnnotationGetStyleOffset=2551(,)\n\n# Release all extended (>255) style numbers\nfun void ReleaseAllExtendedStyles=2552(,)\n\n# Allocate some extended (>255) style numbers and return the start of the range\nfun int AllocateExtendedStyles=2553(int numberStyles,)\n\nenu UndoFlags=UNDO_\nval UNDO_NONE=0\nval UNDO_MAY_COALESCE=1\n\n# Add a container action to the undo stack\nfun void AddUndoAction=2560(int token, UndoFlags flags)\n\n# Find the position of a character from a point within the window.\nfun position CharPositionFromPoint=2561(int x, int y)\n\n# Find the position of a character from a point within the window.\n# Return INVALID_POSITION if not close to text.\nfun position CharPositionFromPointClose=2562(int x, int y)\n\n# Set whether switching to rectangular mode while selecting with the mouse is allowed.\nset void SetMouseSelectionRectangularSwitch=2668(bool mouseSelectionRectangularSwitch,)\n\n# Whether switching to rectangular mode while selecting with the mouse is allowed.\nget bool GetMouseSelectionRectangularSwitch=2669(,)\n\n# Set whether multiple selections can be made\nset void SetMultipleSelection=2563(bool multipleSelection,)\n\n# Whether multiple selections can be made\nget bool GetMultipleSelection=2564(,)\n\n# Set whether typing can be performed into multiple selections\nset void SetAdditionalSelectionTyping=2565(bool additionalSelectionTyping,)\n\n# Whether typing can be performed into multiple selections\nget bool GetAdditionalSelectionTyping=2566(,)\n\n# Set whether additional carets will blink\nset void SetAdditionalCaretsBlink=2567(bool additionalCaretsBlink,)\n\n# Whether additional carets will blink\nget bool GetAdditionalCaretsBlink=2568(,)\n\n# Set whether additional carets are visible\nset void SetAdditionalCaretsVisible=2608(bool additionalCaretsVisible,)\n\n# Whether additional carets are visible\nget bool GetAdditionalCaretsVisible=2609(,)\n\n# How many selections are there?\nget int GetSelections=2570(,)\n\n# Is every selected range empty?\nget bool GetSelectionEmpty=2650(,)\n\n# Clear selections to a single empty stream selection\nfun void ClearSelections=2571(,)\n\n# Set a simple selection\nfun void SetSelection=2572(position caret, position anchor)\n\n# Add a selection\nfun void AddSelection=2573(position caret, position anchor)\n\n# Find the selection index for a point. -1 when not at a selection.\nfun int SelectionFromPoint=2474(int x, int y)\n\n# Drop one selection\nfun void DropSelectionN=2671(int selection,)\n\n# Set the main selection\nset void SetMainSelection=2574(int selection,)\n\n# Which selection is the main selection\nget int GetMainSelection=2575(,)\n\n# Set the caret position of the nth selection.\nset void SetSelectionNCaret=2576(int selection, position caret)\n\n# Return the caret position of the nth selection.\nget position GetSelectionNCaret=2577(int selection,)\n\n# Set the anchor position of the nth selection.\nset void SetSelectionNAnchor=2578(int selection, position anchor)\n\n# Return the anchor position of the nth selection.\nget position GetSelectionNAnchor=2579(int selection,)\n\n# Set the virtual space of the caret of the nth selection.\nset void SetSelectionNCaretVirtualSpace=2580(int selection, position space)\n\n# Return the virtual space of the caret of the nth selection.\nget position GetSelectionNCaretVirtualSpace=2581(int selection,)\n\n# Set the virtual space of the anchor of the nth selection.\nset void SetSelectionNAnchorVirtualSpace=2582(int selection, position space)\n\n# Return the virtual space of the anchor of the nth selection.\nget position GetSelectionNAnchorVirtualSpace=2583(int selection,)\n\n# Sets the position that starts the selection - this becomes the anchor.\nset void SetSelectionNStart=2584(int selection, position anchor)\n\n# Returns the position at the start of the selection.\nget position GetSelectionNStart=2585(int selection,)\n\n# Returns the virtual space at the start of the selection.\nget position GetSelectionNStartVirtualSpace=2726(int selection,)\n\n# Sets the position that ends the selection - this becomes the currentPosition.\nset void SetSelectionNEnd=2586(int selection, position caret)\n\n# Returns the virtual space at the end of the selection.\nget position GetSelectionNEndVirtualSpace=2727(int selection,)\n\n# Returns the position at the end of the selection.\nget position GetSelectionNEnd=2587(int selection,)\n\n# Set the caret position of the rectangular selection.\nset void SetRectangularSelectionCaret=2588(position caret,)\n\n# Return the caret position of the rectangular selection.\nget position GetRectangularSelectionCaret=2589(,)\n\n# Set the anchor position of the rectangular selection.\nset void SetRectangularSelectionAnchor=2590(position anchor,)\n\n# Return the anchor position of the rectangular selection.\nget position GetRectangularSelectionAnchor=2591(,)\n\n# Set the virtual space of the caret of the rectangular selection.\nset void SetRectangularSelectionCaretVirtualSpace=2592(position space,)\n\n# Return the virtual space of the caret of the rectangular selection.\nget position GetRectangularSelectionCaretVirtualSpace=2593(,)\n\n# Set the virtual space of the anchor of the rectangular selection.\nset void SetRectangularSelectionAnchorVirtualSpace=2594(position space,)\n\n# Return the virtual space of the anchor of the rectangular selection.\nget position GetRectangularSelectionAnchorVirtualSpace=2595(,)\n\nenu VirtualSpace=SCVS_\nval SCVS_NONE=0\nval SCVS_RECTANGULARSELECTION=1\nval SCVS_USERACCESSIBLE=2\nval SCVS_NOWRAPLINESTART=4\n\nali SCVS_RECTANGULARSELECTION=RECTANGULAR_SELECTION\nali SCVS_USERACCESSIBLE=USER_ACCESSIBLE\nali SCVS_NOWRAPLINESTART=NO_WRAP_LINE_START\n\n# Set options for virtual space behaviour.\nset void SetVirtualSpaceOptions=2596(VirtualSpace virtualSpaceOptions,)\n\n# Return options for virtual space behaviour.\nget VirtualSpace GetVirtualSpaceOptions=2597(,)\n\n# On GTK, allow selecting the modifier key to use for mouse-based\n# rectangular selection. Often the window manager requires Alt+Mouse Drag\n# for moving windows.\n# Valid values are SCMOD_CTRL(default), SCMOD_ALT, or SCMOD_SUPER.\n\nset void SetRectangularSelectionModifier=2598(int modifier,)\n\n# Get the modifier key used for rectangular selection.\nget int GetRectangularSelectionModifier=2599(,)\n\n# Set the foreground colour of additional selections.\n# Must have previously called SetSelFore with non-zero first argument for this to have an effect.\nset void SetAdditionalSelFore=2600(colour fore,)\n\n# Set the background colour of additional selections.\n# Must have previously called SetSelBack with non-zero first argument for this to have an effect.\nset void SetAdditionalSelBack=2601(colour back,)\n\n# Set the alpha of the selection.\nset void SetAdditionalSelAlpha=2602(Alpha alpha,)\n\n# Get the alpha of the selection.\nget Alpha GetAdditionalSelAlpha=2603(,)\n\n# Set the foreground colour of additional carets.\nset void SetAdditionalCaretFore=2604(colour fore,)\n\n# Get the foreground colour of additional carets.\nget colour GetAdditionalCaretFore=2605(,)\n\n# Set the main selection to the next selection.\nfun void RotateSelection=2606(,)\n\n# Swap that caret and anchor of the main selection.\nfun void SwapMainAnchorCaret=2607(,)\n\n# Add the next occurrence of the main selection to the set of selections as main.\n# If the current selection is empty then select word around caret.\nfun void MultipleSelectAddNext=2688(,)\n\n# Add each occurrence of the main selection in the target to the set of selections.\n# If the current selection is empty then select word around caret.\nfun void MultipleSelectAddEach=2689(,)\n\n# Indicate that the internal state of a lexer has changed over a range and therefore\n# there may be a need to redraw.\nfun int ChangeLexerState=2617(position start, position end)\n\n# Find the next line at or after lineStart that is a contracted fold header line.\n# Return -1 when no more lines.\nfun line ContractedFoldNext=2618(line lineStart,)\n\n# Centre current line in window.\nfun void VerticalCentreCaret=2619(,)\n\n# Move the selected lines up one line, shifting the line above after the selection\nfun void MoveSelectedLinesUp=2620(,)\n\n# Move the selected lines down one line, shifting the line below before the selection\nfun void MoveSelectedLinesDown=2621(,)\n\n# Set the identifier reported as idFrom in notification messages.\nset void SetIdentifier=2622(int identifier,)\n\n# Get the identifier.\nget int GetIdentifier=2623(,)\n\n# Set the width for future RGBA image data.\nset void RGBAImageSetWidth=2624(int width,)\n\n# Set the height for future RGBA image data.\nset void RGBAImageSetHeight=2625(int height,)\n\n# Set the scale factor in percent for future RGBA image data.\nset void RGBAImageSetScale=2651(int scalePercent,)\n\n# Define a marker from RGBA data.\n# It has the width and height from RGBAImageSetWidth/Height\nfun void MarkerDefineRGBAImage=2626(int markerNumber, string pixels)\n\n# Register an RGBA image for use in autocompletion lists.\n# It has the width and height from RGBAImageSetWidth/Height\nfun void RegisterRGBAImage=2627(int type, string pixels)\n\n# Scroll to start of document.\nfun void ScrollToStart=2628(,)\n\n# Scroll to end of document.\nfun void ScrollToEnd=2629(,)\n\nenu Technology=SC_TECHNOLOGY_\nval SC_TECHNOLOGY_DEFAULT=0\nval SC_TECHNOLOGY_DIRECTWRITE=1\nval SC_TECHNOLOGY_DIRECTWRITERETAIN=2\nval SC_TECHNOLOGY_DIRECTWRITEDC=3\nval SC_TECHNOLOGY_DIRECT_WRITE_1=4\n\nali SC_TECHNOLOGY_DIRECTWRITE=DIRECT_WRITE\nali SC_TECHNOLOGY_DIRECTWRITERETAIN=DIRECT_WRITE_RETAIN\nali SC_TECHNOLOGY_DIRECTWRITEDC=DIRECT_WRITE_D_C\n\n# Set the technology used.\nset void SetTechnology=2630(Technology technology,)\n\n# Get the tech.\nget Technology GetTechnology=2631(,)\n\n# Create an ILoader*.\nfun pointer CreateLoader=2632(position bytes, DocumentOption documentOptions)\n\n# On macOS, show a find indicator.\nfun void FindIndicatorShow=2640(position start, position end)\n\n# On macOS, flash a find indicator, then fade out.\nfun void FindIndicatorFlash=2641(position start, position end)\n\n# On macOS, hide the find indicator.\nfun void FindIndicatorHide=2642(,)\n\n# Move caret to before first visible character on display line.\n# If already there move to first character on display line.\nfun void VCHomeDisplay=2652(,)\n\n# Like VCHomeDisplay but extending selection to new caret position.\nfun void VCHomeDisplayExtend=2653(,)\n\n# Is the caret line always visible?\nget bool GetCaretLineVisibleAlways=2654(,)\n\n# Sets the caret line to always visible.\nset void SetCaretLineVisibleAlways=2655(bool alwaysVisible,)\n\n# Line end types which may be used in addition to LF, CR, and CRLF\n# SC_LINE_END_TYPE_UNICODE includes U+2028 Line Separator,\n# U+2029 Paragraph Separator, and U+0085 Next Line\nenu LineEndType=SC_LINE_END_TYPE_\nval SC_LINE_END_TYPE_DEFAULT=0\nval SC_LINE_END_TYPE_UNICODE=1\n\n# Set the line end types that the application wants to use. May not be used if incompatible with lexer or encoding.\nset void SetLineEndTypesAllowed=2656(LineEndType lineEndBitSet,)\n\n# Get the line end types currently allowed.\nget LineEndType GetLineEndTypesAllowed=2657(,)\n\n# Get the line end types currently recognised. May be a subset of the allowed types due to lexer limitation.\nget LineEndType GetLineEndTypesActive=2658(,)\n\n# Set the way a character is drawn.\nset void SetRepresentation=2665(string encodedCharacter, string representation)\n\n# Get the way a character is drawn.\n# Result is NUL-terminated.\nget int GetRepresentation=2666(string encodedCharacter, stringresult representation)\n\n# Remove a character representation.\nfun void ClearRepresentation=2667(string encodedCharacter,)\n\n# Clear representations to default.\nfun void ClearAllRepresentations=2770(,)\n\n# Can draw representations in various ways\nenu RepresentationAppearance=SC_REPRESENTATION\nval SC_REPRESENTATION_PLAIN=0\nval SC_REPRESENTATION_BLOB=1\nval SC_REPRESENTATION_COLOUR=0x10\n\n# Set the appearance of a representation.\nset void SetRepresentationAppearance=2766(string encodedCharacter, RepresentationAppearance appearance)\n\n# Get the appearance of a representation.\nget RepresentationAppearance GetRepresentationAppearance=2767(string encodedCharacter,)\n\n# Set the colour of a representation.\nset void SetRepresentationColour=2768(string encodedCharacter, colouralpha colour)\n\n# Get the colour of a representation.\nget colouralpha GetRepresentationColour=2769(string encodedCharacter,)\n\n# Set the end of line annotation text for a line\nset void EOLAnnotationSetText=2740(line line, string text)\n\n# Get the end of line annotation text for a line\nget int EOLAnnotationGetText=2741(line line, stringresult text)\n\n# Set the style number for the end of line annotations for a line\nset void EOLAnnotationSetStyle=2742(line line, int style)\n\n# Get the style number for the end of line annotations for a line\nget int EOLAnnotationGetStyle=2743(line line,)\n\n# Clear the end of annotations from all lines\nfun void EOLAnnotationClearAll=2744(,)\n\nenu EOLAnnotationVisible=EOLANNOTATION_\nval EOLANNOTATION_HIDDEN=0x0\nval EOLANNOTATION_STANDARD=0x1\nval EOLANNOTATION_BOXED=0x2\nval EOLANNOTATION_STADIUM=0x100\nval EOLANNOTATION_FLAT_CIRCLE=0x101\nval EOLANNOTATION_ANGLE_CIRCLE=0x102\nval EOLANNOTATION_CIRCLE_FLAT=0x110\nval EOLANNOTATION_FLATS=0x111\nval EOLANNOTATION_ANGLE_FLAT=0x112\nval EOLANNOTATION_CIRCLE_ANGLE=0x120\nval EOLANNOTATION_FLAT_ANGLE=0x121\nval EOLANNOTATION_ANGLES=0x122\n\n# Set the visibility for the end of line annotations for a view\nset void EOLAnnotationSetVisible=2745(EOLAnnotationVisible visible,)\n\n# Get the visibility for the end of line annotations for a view\nget EOLAnnotationVisible EOLAnnotationGetVisible=2746(,)\n\n# Get the start of the range of style numbers used for end of line annotations\nset void EOLAnnotationSetStyleOffset=2747(int style,)\n\n# Get the start of the range of style numbers used for end of line annotations\nget int EOLAnnotationGetStyleOffset=2748(,)\n\nenu Supports=SC_SUPPORTS_\nval SC_SUPPORTS_LINE_DRAWS_FINAL=0\nval SC_SUPPORTS_PIXEL_DIVISIONS=1\nval SC_SUPPORTS_FRACTIONAL_STROKE_WIDTH=2\nval SC_SUPPORTS_TRANSLUCENT_STROKE=3\nval SC_SUPPORTS_PIXEL_MODIFICATION=4\nval SC_SUPPORTS_THREAD_SAFE_MEASURE_WIDTHS=5\n\n# Get whether a feature is supported\nget bool SupportsFeature=2750(Supports feature,)\n\nenu LineCharacterIndexType=SC_LINECHARACTERINDEX_\nval SC_LINECHARACTERINDEX_NONE=0\nval SC_LINECHARACTERINDEX_UTF32=1\nval SC_LINECHARACTERINDEX_UTF16=2\n\n# Retrieve line character index state.\nget LineCharacterIndexType GetLineCharacterIndex=2710(,)\n\n# Request line character index be created or its use count increased.\nfun void AllocateLineCharacterIndex=2711(LineCharacterIndexType lineCharacterIndex,)\n\n# Decrease use count of line character index and remove if 0.\nfun void ReleaseLineCharacterIndex=2712(LineCharacterIndexType lineCharacterIndex,)\n\n# Retrieve the document line containing a position measured in index units.\nfun line LineFromIndexPosition=2713(position pos, LineCharacterIndexType lineCharacterIndex)\n\n# Retrieve the position measured in index units at the start of a document line.\nfun position IndexPositionFromLine=2714(line line, LineCharacterIndexType lineCharacterIndex)\n\n# Start notifying the container of all key presses and commands.\nfun void StartRecord=3001(,)\n\n# Stop notifying the container of all key presses and commands.\nfun void StopRecord=3002(,)\n\n# Retrieve the lexing language of the document.\nget int GetLexer=4002(,)\n\n# Colourise a segment of the document using the current lexing language.\nfun void Colourise=4003(position start, position end)\n\n# Set up a value that may be used by a lexer for some optional feature.\nset void SetProperty=4004(string key, string value)\n\n# Maximum value of keywordSet parameter of SetKeyWords.\nval KEYWORDSET_MAX=8\n\n# Set up the key words used by the lexer.\nset void SetKeyWords=4005(int keyWordSet, string keyWords)\n\n# Retrieve a \"property\" value previously set with SetProperty.\n# Result is NUL-terminated.\nget int GetProperty=4008(string key, stringresult value)\n\n# Retrieve a \"property\" value previously set with SetProperty,\n# with \"$()\" variable replacement on returned buffer.\n# Result is NUL-terminated.\nget int GetPropertyExpanded=4009(string key, stringresult value)\n\n# Retrieve a \"property\" value previously set with SetProperty,\n# interpreted as an int AFTER any \"$()\" variable replacement.\nget int GetPropertyInt=4010(string key, int defaultValue)\n\n# Retrieve the name of the lexer.\n# Return the length of the text.\n# Result is NUL-terminated.\nget int GetLexerLanguage=4012(, stringresult language)\n\n# For private communication between an application and a known lexer.\nfun pointer PrivateLexerCall=4013(int operation, pointer pointer)\n\n# Retrieve a '\\n' separated list of properties understood by the current lexer.\n# Result is NUL-terminated.\nfun int PropertyNames=4014(, stringresult names)\n\nenu TypeProperty=SC_TYPE_\nval SC_TYPE_BOOLEAN=0\nval SC_TYPE_INTEGER=1\nval SC_TYPE_STRING=2\n\n# Retrieve the type of a property.\nfun TypeProperty PropertyType=4015(string name,)\n\n# Describe a property.\n# Result is NUL-terminated.\nfun int DescribeProperty=4016(string name, stringresult description)\n\n# Retrieve a '\\n' separated list of descriptions of the keyword sets understood by the current lexer.\n# Result is NUL-terminated.\nfun int DescribeKeyWordSets=4017(, stringresult descriptions)\n\n# Bit set of LineEndType enumertion for which line ends beyond the standard\n# LF, CR, and CRLF are supported by the lexer.\nget LineEndType GetLineEndTypesSupported=4018(,)\n\n# Allocate a set of sub styles for a particular base style, returning start of range\nfun int AllocateSubStyles=4020(int styleBase, int numberStyles)\n\n# The starting style number for the sub styles associated with a base style\nget int GetSubStylesStart=4021(int styleBase,)\n\n# The number of sub styles associated with a base style\nget int GetSubStylesLength=4022(int styleBase,)\n\n# For a sub style, return the base style, else return the argument.\nget int GetStyleFromSubStyle=4027(int subStyle,)\n\n# For a secondary style, return the primary style, else return the argument.\nget int GetPrimaryStyleFromStyle=4028(int style,)\n\n# Free allocated sub styles\nfun void FreeSubStyles=4023(,)\n\n# Set the identifiers that are shown in a particular style\nset void SetIdentifiers=4024(int style, string identifiers)\n\n# Where styles are duplicated by a feature such as active/inactive code\n# return the distance between the two types.\nget int DistanceToSecondaryStyles=4025(,)\n\n# Get the set of base styles that can be extended with sub styles\n# Result is NUL-terminated.\nget int GetSubStyleBases=4026(, stringresult styles)\n\n# Retrieve the number of named styles for the lexer.\nget int GetNamedStyles=4029(,)\n\n# Retrieve the name of a style.\n# Result is NUL-terminated.\nfun int NameOfStyle=4030(int style, stringresult name)\n\n# Retrieve a ' ' separated list of style tags like \"literal quoted string\".\n# Result is NUL-terminated.\nfun int TagsOfStyle=4031(int style, stringresult tags)\n\n# Retrieve a description of a style.\n# Result is NUL-terminated.\nfun int DescriptionOfStyle=4032(int style, stringresult description)\n\n# Set the lexer from an ILexer*.\nset void SetILexer=4033(, pointer ilexer)\n\n# Notifications\n# Type of modification and the action which caused the modification.\n# These are defined as a bit mask to make it easy to specify which notifications are wanted.\n# One bit is set from each of SC_MOD_* and SC_PERFORMED_*.\nenu ModificationFlags=SC_MOD_ SC_PERFORMED_ SC_MULTISTEPUNDOREDO SC_LASTSTEPINUNDOREDO SC_MULTILINEUNDOREDO SC_STARTACTION SC_MODEVENTMASKALL\nval SC_MOD_NONE=0x0\nval SC_MOD_INSERTTEXT=0x1\nval SC_MOD_DELETETEXT=0x2\nval SC_MOD_CHANGESTYLE=0x4\nval SC_MOD_CHANGEFOLD=0x8\nval SC_PERFORMED_USER=0x10\nval SC_PERFORMED_UNDO=0x20\nval SC_PERFORMED_REDO=0x40\nval SC_MULTISTEPUNDOREDO=0x80\nval SC_LASTSTEPINUNDOREDO=0x100\nval SC_MOD_CHANGEMARKER=0x200\nval SC_MOD_BEFOREINSERT=0x400\nval SC_MOD_BEFOREDELETE=0x800\nval SC_MULTILINEUNDOREDO=0x1000\nval SC_STARTACTION=0x2000\nval SC_MOD_CHANGEINDICATOR=0x4000\nval SC_MOD_CHANGELINESTATE=0x8000\nval SC_MOD_CHANGEMARGIN=0x10000\nval SC_MOD_CHANGEANNOTATION=0x20000\nval SC_MOD_CONTAINER=0x40000\nval SC_MOD_LEXERSTATE=0x80000\nval SC_MOD_INSERTCHECK=0x100000\nval SC_MOD_CHANGETABSTOPS=0x200000\nval SC_MOD_CHANGEEOLANNOTATION=0x400000\nval SC_MODEVENTMASKALL=0x7FFFFF\n\nali SC_MOD_INSERTTEXT=INSERT_TEXT\nali SC_MOD_DELETETEXT=DELETE_TEXT\nali SC_MOD_CHANGESTYLE=CHANGE_STYLE\nali SC_MOD_CHANGEFOLD=CHANGE_FOLD\nali SC_MULTISTEPUNDOREDO=MULTI_STEP_UNDO_REDO\nali SC_LASTSTEPINUNDOREDO=LAST_STEP_IN_UNDO_REDO\nali SC_MOD_CHANGEMARKER=CHANGE_MARKER\nali SC_MOD_BEFOREINSERT=BEFORE_INSERT\nali SC_MOD_BEFOREDELETE=BEFORE_DELETE\nali SC_MULTILINEUNDOREDO=MULTILINE_UNDO_REDO\nali SC_STARTACTION=START_ACTION\nali SC_MOD_CHANGEINDICATOR=CHANGE_INDICATOR\nali SC_MOD_CHANGELINESTATE=CHANGE_LINE_STATE\nali SC_MOD_CHANGEMARGIN=CHANGE_MARGIN\nali SC_MOD_CHANGEANNOTATION=CHANGE_ANNOTATION\nali SC_MOD_LEXERSTATE=LEXER_STATE\nali SC_MOD_INSERTCHECK=INSERT_CHECK\nali SC_MOD_CHANGETABSTOPS=CHANGE_TAB_STOPS\nali SC_MOD_CHANGEEOLANNOTATION=CHANGE_E_O_L_ANNOTATION\nali SC_MODEVENTMASKALL=EVENT_MASK_ALL\n\nenu Update=SC_UPDATE_\nval SC_UPDATE_NONE=0x0\nval SC_UPDATE_CONTENT=0x1\nval SC_UPDATE_SELECTION=0x2\nval SC_UPDATE_V_SCROLL=0x4\nval SC_UPDATE_H_SCROLL=0x8\n\n# For compatibility, these go through the COMMAND notification rather than NOTIFY\n# and should have had exactly the same values as the EN_* constants.\n# Unfortunately the SETFOCUS and KILLFOCUS are flipped over from EN_*\n# As clients depend on these constants, this will not be changed.\nenu FocusChange=SCEN_\nval SCEN_CHANGE=768\nval SCEN_SETFOCUS=512\nval SCEN_KILLFOCUS=256\n\n# Symbolic key codes and modifier flags.\n# ASCII and other printable characters below 256.\n# Extended keys above 300.\n\nenu Keys=SCK_\nval SCK_DOWN=300\nval SCK_UP=301\nval SCK_LEFT=302\nval SCK_RIGHT=303\nval SCK_HOME=304\nval SCK_END=305\nval SCK_PRIOR=306\nval SCK_NEXT=307\nval SCK_DELETE=308\nval SCK_INSERT=309\nval SCK_ESCAPE=7\nval SCK_BACK=8\nval SCK_TAB=9\nval SCK_RETURN=13\nval SCK_ADD=310\nval SCK_SUBTRACT=311\nval SCK_DIVIDE=312\nval SCK_WIN=313\nval SCK_RWIN=314\nval SCK_MENU=315\n\nali SCK_RWIN=R_WIN\n\nenu KeyMod=SCMOD_\nval SCMOD_NORM=0\nval SCMOD_SHIFT=1\nval SCMOD_CTRL=2\nval SCMOD_ALT=4\nval SCMOD_SUPER=8\nval SCMOD_META=16\n\nenu CompletionMethods=SC_AC_\nval SC_AC_FILLUP=1\nval SC_AC_DOUBLECLICK=2\nval SC_AC_TAB=3\nval SC_AC_NEWLINE=4\nval SC_AC_COMMAND=5\nval SC_AC_SINGLE_CHOICE=6\n\nali SC_AC_FILLUP=FILL_UP\nali SC_AC_DOUBLECLICK=DOUBLE_CLICK\n\n# characterSource for SCN_CHARADDED\nenu CharacterSource=SC_CHARACTERSOURCE_\n# Direct input characters.\nval SC_CHARACTERSOURCE_DIRECT_INPUT=0\n# IME (inline mode) or dead key tentative input characters.\nval SC_CHARACTERSOURCE_TENTATIVE_INPUT=1\n# IME (either inline or windowed mode) full composited string.\nval SC_CHARACTERSOURCE_IME_RESULT=2\n\n# Events\n\nevt void StyleNeeded=2000(int position)\nevt void CharAdded=2001(int ch, int characterSource)\nevt void SavePointReached=2002(void)\nevt void SavePointLeft=2003(void)\nevt void ModifyAttemptRO=2004(void)\n# GTK Specific to work around focus and accelerator problems:\nevt void Key=2005(int ch, int modifiers)\nevt void DoubleClick=2006(int modifiers, int position, int line)\nevt void UpdateUI=2007(int updated)\nevt void Modified=2008(int position, int modificationType, string text, int length, int linesAdded, int line, int foldLevelNow, int foldLevelPrev, int token, int annotationLinesAdded)\nevt void MacroRecord=2009(int message, int wParam, int lParam)\nevt void MarginClick=2010(int modifiers, int position, int margin)\nevt void NeedShown=2011(int position, int length)\nevt void Painted=2013(void)\nevt void UserListSelection=2014(int listType, string text, int position, int ch, CompletionMethods listCompletionMethod)\nevt void URIDropped=2015(string text)\nevt void DwellStart=2016(int position, int x, int y)\nevt void DwellEnd=2017(int position, int x, int y)\nevt void Zoom=2018(void)\nevt void HotSpotClick=2019(int modifiers, int position)\nevt void HotSpotDoubleClick=2020(int modifiers, int position)\nevt void CallTipClick=2021(int position)\nevt void AutoCSelection=2022(string text, int position, int ch, CompletionMethods listCompletionMethod)\nevt void IndicatorClick=2023(int modifiers, int position)\nevt void IndicatorRelease=2024(int modifiers, int position)\nevt void AutoCCancelled=2025(void)\nevt void AutoCCharDeleted=2026(void)\nevt void HotSpotReleaseClick=2027(int modifiers, int position)\nevt void FocusIn=2028(void)\nevt void FocusOut=2029(void)\nevt void AutoCCompleted=2030(string text, int position, int ch, CompletionMethods listCompletionMethod)\nevt void MarginRightClick=2031(int modifiers, int position, int margin)\nevt void AutoCSelectionChange=2032(int listType, string text, int position)\n\ncat Provisional\n\nenu Bidirectional=SC_BIDIRECTIONAL_\nval SC_BIDIRECTIONAL_DISABLED=0\nval SC_BIDIRECTIONAL_L2R=1\nval SC_BIDIRECTIONAL_R2L=2\n\n# Retrieve bidirectional text display state.\nget Bidirectional GetBidirectional=2708(,)\n\n# Set bidirectional text display state.\nset void SetBidirectional=2709(Bidirectional bidirectional,)\n\ncat Deprecated\n\n# Divide each styling byte into lexical class bits (default: 5) and indicator\n# bits (default: 3). If a lexer requires more than 32 lexical states, then this\n# is used to expand the possible states.\nset void SetStyleBits=2090(int bits,)\n\n# Retrieve number of bits in style bytes used to hold the lexical state.\nget int GetStyleBits=2091(,)\n\n# Retrieve the number of bits the current lexer needs for styling.\nget int GetStyleBitsNeeded=4011(,)\n\n# Deprecated in 3.5.5\n\n# Always interpret keyboard input as Unicode\nset void SetKeysUnicode=2521(bool keysUnicode,)\n\n# Are keys always interpreted as Unicode?\nget bool GetKeysUnicode=2522(,)\n\n# Is drawing done in two phases with backgrounds drawn before foregrounds?\nget bool GetTwoPhaseDraw=2283(,)\n\n# In twoPhaseDraw mode, drawing is performed in two phases, first the background\n# and then the foreground. This avoids chopping off characters that overlap the next run.\nset void SetTwoPhaseDraw=2284(bool twoPhase,)\n\nval INDIC0_MASK=0x20\nval INDIC1_MASK=0x40\nval INDIC2_MASK=0x80\nval INDICS_MASK=0xE0\n"
  },
  {
    "path": "Libraries/scintilla/include/ScintillaCall.h",
    "content": "// SciTE - Scintilla based Text Editor\n/** @file ScintillaCall.h\n ** Interface to calling a Scintilla instance.\n **/\n// Copyright 1998-2019 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n/* Most of this file is automatically generated from the Scintilla.iface interface definition\n * file which contains any comments about the definitions. APIFacer.py does the generation. */\n\n#ifndef SCINTILLACALL_H\n#define SCINTILLACALL_H\n\nnamespace Scintilla {\n\nenum class Message;\t// Declare in case ScintillaMessages.h not included\n\n// Declare in case ScintillaStructures.h not included\nstruct TextRangeFull;\nstruct TextToFindFull;\nstruct RangeToFormatFull;\n\nclass IDocumentEditable;\n\nusing FunctionDirect = intptr_t(*)(intptr_t ptr, unsigned int iMessage, uintptr_t wParam, intptr_t lParam, int *pStatus);\n\nstruct Failure {\n\tScintilla::Status status;\n\texplicit Failure(Scintilla::Status status_) noexcept : status(status_) {\n\t}\n};\n\nstruct Span {\n\t// An ordered range\n\t// end may be less than start when, for example, searching backwards\n\tPosition start;\n\tPosition end;\n\texplicit Span(Position position) noexcept : start(position), end(position) {\n\t}\n\tSpan(Position start_, Position end_) noexcept : start(start_), end(end_) {\n\t}\n\tPosition Length() const noexcept {\n\t\tif (end > start)\n\t\t\treturn end - start;\n\t\telse\n\t\t\treturn start - end;\n\t}\n\tbool operator==(const Span &other) const noexcept {\n\t\treturn (other.start == start) && (other.end == end);\n\t}\n};\n\nclass ScintillaCall {\n\tFunctionDirect fn;\n\tintptr_t ptr;\n\tintptr_t CallPointer(Message msg, uintptr_t wParam, void *s);\n\tintptr_t CallString(Message msg, uintptr_t wParam, const char *s);\n\tstd::string CallReturnString(Message msg, uintptr_t wParam);\npublic:\n\tScintilla::Status statusLastCall;\n\tScintillaCall() noexcept;\n\t// All standard methods are fine\n\n\tvoid SetFnPtr(FunctionDirect fn_, intptr_t ptr_) noexcept;\n\tbool IsValid() const noexcept;\n\tintptr_t Call(Message msg, uintptr_t wParam=0, intptr_t lParam=0);\n\n\t// Common APIs made more structured and type-safe\n\tPosition LineStart(Line line);\n\tPosition LineEnd(Line line);\n\tSpan SelectionSpan();\n\tSpan TargetSpan();\n\tvoid SetTarget(Span span);\n\tvoid ColouriseAll();\n\tchar CharacterAt(Position position);\n\tint UnsignedStyleAt(Position position);\n\tstd::string StringOfSpan(Span span);\n\tstd::string StringOfRange(Span span);\n\tPosition ReplaceTarget(std::string_view text);\n\tPosition ReplaceTargetRE(std::string_view text);\n\tPosition ReplaceTargetMinimal(std::string_view text);\n\tPosition SearchInTarget(std::string_view text);\n\tSpan SpanSearchInTarget(std::string_view text);\n\n\t// Generated APIs\n//++Autogenerated -- start of section automatically generated from Scintilla.iface\n//**\\(\\*\\n\\)\n\tvoid AddText(Position length, const char *text);\n\tvoid AddStyledText(Position length, const char *c);\n\tvoid InsertText(Position pos, const char *text);\n\tvoid ChangeInsertion(Position length, const char *text);\n\tvoid ClearAll();\n\tvoid DeleteRange(Position start, Position lengthDelete);\n\tvoid ClearDocumentStyle();\n\tPosition Length();\n\tint CharAt(Position pos);\n\tPosition CurrentPos();\n\tPosition Anchor();\n\tint StyleAt(Position pos);\n\tint StyleIndexAt(Position pos);\n\tvoid Redo();\n\tvoid SetUndoCollection(bool collectUndo);\n\tvoid SelectAll();\n\tvoid SetSavePoint();\n\tPosition GetStyledText(void *tr);\n\tPosition GetStyledTextFull(TextRangeFull *tr);\n\tbool CanRedo();\n\tLine MarkerLineFromHandle(int markerHandle);\n\tvoid MarkerDeleteHandle(int markerHandle);\n\tint MarkerHandleFromLine(Line line, int which);\n\tint MarkerNumberFromLine(Line line, int which);\n\tbool UndoCollection();\n\tScintilla::WhiteSpace ViewWS();\n\tvoid SetViewWS(Scintilla::WhiteSpace viewWS);\n\tScintilla::TabDrawMode TabDrawMode();\n\tvoid SetTabDrawMode(Scintilla::TabDrawMode tabDrawMode);\n\tPosition PositionFromPoint(int x, int y);\n\tPosition PositionFromPointClose(int x, int y);\n\tvoid GotoLine(Line line);\n\tvoid GotoPos(Position caret);\n\tvoid SetAnchor(Position anchor);\n\tPosition GetCurLine(Position length, char *text);\n\tstd::string GetCurLine(Position length);\n\tPosition EndStyled();\n\tvoid ConvertEOLs(Scintilla::EndOfLine eolMode);\n\tScintilla::EndOfLine EOLMode();\n\tvoid SetEOLMode(Scintilla::EndOfLine eolMode);\n\tvoid StartStyling(Position start, int unused);\n\tvoid SetStyling(Position length, int style);\n\tbool BufferedDraw();\n\tvoid SetBufferedDraw(bool buffered);\n\tvoid SetTabWidth(int tabWidth);\n\tint TabWidth();\n\tvoid SetTabMinimumWidth(int pixels);\n\tint TabMinimumWidth();\n\tvoid ClearTabStops(Line line);\n\tvoid AddTabStop(Line line, int x);\n\tint GetNextTabStop(Line line, int x);\n\tvoid SetCodePage(int codePage);\n\tvoid SetFontLocale(const char *localeName);\n\tint FontLocale(char *localeName);\n\tstd::string FontLocale();\n\tScintilla::IMEInteraction IMEInteraction();\n\tvoid SetIMEInteraction(Scintilla::IMEInteraction imeInteraction);\n\tvoid MarkerDefine(int markerNumber, Scintilla::MarkerSymbol markerSymbol);\n\tvoid MarkerSetFore(int markerNumber, Colour fore);\n\tvoid MarkerSetBack(int markerNumber, Colour back);\n\tvoid MarkerSetBackSelected(int markerNumber, Colour back);\n\tvoid MarkerSetForeTranslucent(int markerNumber, ColourAlpha fore);\n\tvoid MarkerSetBackTranslucent(int markerNumber, ColourAlpha back);\n\tvoid MarkerSetBackSelectedTranslucent(int markerNumber, ColourAlpha back);\n\tvoid MarkerSetStrokeWidth(int markerNumber, int hundredths);\n\tvoid MarkerEnableHighlight(bool enabled);\n\tint MarkerAdd(Line line, int markerNumber);\n\tvoid MarkerDelete(Line line, int markerNumber);\n\tvoid MarkerDeleteAll(int markerNumber);\n\tint MarkerGet(Line line);\n\tLine MarkerNext(Line lineStart, int markerMask);\n\tLine MarkerPrevious(Line lineStart, int markerMask);\n\tvoid MarkerDefinePixmap(int markerNumber, const char *pixmap);\n\tvoid MarkerAddSet(Line line, int markerSet);\n\tvoid MarkerSetAlpha(int markerNumber, Scintilla::Alpha alpha);\n\tScintilla::Layer MarkerGetLayer(int markerNumber);\n\tvoid MarkerSetLayer(int markerNumber, Scintilla::Layer layer);\n\tvoid SetMarginTypeN(int margin, Scintilla::MarginType marginType);\n\tScintilla::MarginType MarginTypeN(int margin);\n\tvoid SetMarginWidthN(int margin, int pixelWidth);\n\tint MarginWidthN(int margin);\n\tvoid SetMarginMaskN(int margin, int mask);\n\tint MarginMaskN(int margin);\n\tvoid SetMarginSensitiveN(int margin, bool sensitive);\n\tbool MarginSensitiveN(int margin);\n\tvoid SetMarginCursorN(int margin, Scintilla::CursorShape cursor);\n\tScintilla::CursorShape MarginCursorN(int margin);\n\tvoid SetMarginBackN(int margin, Colour back);\n\tColour MarginBackN(int margin);\n\tvoid SetMargins(int margins);\n\tint Margins();\n\tvoid StyleClearAll();\n\tvoid StyleSetFore(int style, Colour fore);\n\tvoid StyleSetBack(int style, Colour back);\n\tvoid StyleSetBold(int style, bool bold);\n\tvoid StyleSetItalic(int style, bool italic);\n\tvoid StyleSetSize(int style, int sizePoints);\n\tvoid StyleSetFont(int style, const char *fontName);\n\tvoid StyleSetEOLFilled(int style, bool eolFilled);\n\tvoid StyleResetDefault();\n\tvoid StyleSetUnderline(int style, bool underline);\n\tColour StyleGetFore(int style);\n\tColour StyleGetBack(int style);\n\tbool StyleGetBold(int style);\n\tbool StyleGetItalic(int style);\n\tint StyleGetSize(int style);\n\tint StyleGetFont(int style, char *fontName);\n\tstd::string StyleGetFont(int style);\n\tbool StyleGetEOLFilled(int style);\n\tbool StyleGetUnderline(int style);\n\tScintilla::CaseVisible StyleGetCase(int style);\n\tScintilla::CharacterSet StyleGetCharacterSet(int style);\n\tbool StyleGetVisible(int style);\n\tbool StyleGetChangeable(int style);\n\tbool StyleGetHotSpot(int style);\n\tvoid StyleSetCase(int style, Scintilla::CaseVisible caseVisible);\n\tvoid StyleSetSizeFractional(int style, int sizeHundredthPoints);\n\tint StyleGetSizeFractional(int style);\n\tvoid StyleSetWeight(int style, Scintilla::FontWeight weight);\n\tScintilla::FontWeight StyleGetWeight(int style);\n\tvoid StyleSetCharacterSet(int style, Scintilla::CharacterSet characterSet);\n\tvoid StyleSetHotSpot(int style, bool hotspot);\n\tvoid StyleSetCheckMonospaced(int style, bool checkMonospaced);\n\tbool StyleGetCheckMonospaced(int style);\n\tvoid StyleSetStretch(int style, Scintilla::FontStretch stretch);\n\tScintilla::FontStretch StyleGetStretch(int style);\n\tvoid StyleSetInvisibleRepresentation(int style, const char *representation);\n\tint StyleGetInvisibleRepresentation(int style, char *representation);\n\tstd::string StyleGetInvisibleRepresentation(int style);\n\tvoid SetElementColour(Scintilla::Element element, ColourAlpha colourElement);\n\tColourAlpha ElementColour(Scintilla::Element element);\n\tvoid ResetElementColour(Scintilla::Element element);\n\tbool ElementIsSet(Scintilla::Element element);\n\tbool ElementAllowsTranslucent(Scintilla::Element element);\n\tColourAlpha ElementBaseColour(Scintilla::Element element);\n\tvoid SetSelFore(bool useSetting, Colour fore);\n\tvoid SetSelBack(bool useSetting, Colour back);\n\tScintilla::Alpha SelAlpha();\n\tvoid SetSelAlpha(Scintilla::Alpha alpha);\n\tbool SelEOLFilled();\n\tvoid SetSelEOLFilled(bool filled);\n\tScintilla::Layer SelectionLayer();\n\tvoid SetSelectionLayer(Scintilla::Layer layer);\n\tScintilla::Layer CaretLineLayer();\n\tvoid SetCaretLineLayer(Scintilla::Layer layer);\n\tbool CaretLineHighlightSubLine();\n\tvoid SetCaretLineHighlightSubLine(bool subLine);\n\tvoid SetCaretFore(Colour fore);\n\tvoid AssignCmdKey(int keyDefinition, int sciCommand);\n\tvoid ClearCmdKey(int keyDefinition);\n\tvoid ClearAllCmdKeys();\n\tvoid SetStylingEx(Position length, const char *styles);\n\tvoid StyleSetVisible(int style, bool visible);\n\tint CaretPeriod();\n\tvoid SetCaretPeriod(int periodMilliseconds);\n\tvoid SetWordChars(const char *characters);\n\tint WordChars(char *characters);\n\tstd::string WordChars();\n\tvoid SetCharacterCategoryOptimization(int countCharacters);\n\tint CharacterCategoryOptimization();\n\tvoid BeginUndoAction();\n\tvoid EndUndoAction();\n\tint UndoSequence();\n\tint UndoActions();\n\tvoid SetUndoSavePoint(int action);\n\tint UndoSavePoint();\n\tvoid SetUndoDetach(int action);\n\tint UndoDetach();\n\tvoid SetUndoTentative(int action);\n\tint UndoTentative();\n\tvoid SetUndoCurrent(int action);\n\tint UndoCurrent();\n\tvoid PushUndoActionType(int type, Position pos);\n\tvoid ChangeLastUndoActionText(Position length, const char *text);\n\tint UndoActionType(int action);\n\tPosition UndoActionPosition(int action);\n\tint UndoActionText(int action, char *text);\n\tstd::string UndoActionText(int action);\n\tvoid IndicSetStyle(int indicator, Scintilla::IndicatorStyle indicatorStyle);\n\tScintilla::IndicatorStyle IndicGetStyle(int indicator);\n\tvoid IndicSetFore(int indicator, Colour fore);\n\tColour IndicGetFore(int indicator);\n\tvoid IndicSetUnder(int indicator, bool under);\n\tbool IndicGetUnder(int indicator);\n\tvoid IndicSetHoverStyle(int indicator, Scintilla::IndicatorStyle indicatorStyle);\n\tScintilla::IndicatorStyle IndicGetHoverStyle(int indicator);\n\tvoid IndicSetHoverFore(int indicator, Colour fore);\n\tColour IndicGetHoverFore(int indicator);\n\tvoid IndicSetFlags(int indicator, Scintilla::IndicFlag flags);\n\tScintilla::IndicFlag IndicGetFlags(int indicator);\n\tvoid IndicSetStrokeWidth(int indicator, int hundredths);\n\tint IndicGetStrokeWidth(int indicator);\n\tvoid SetWhitespaceFore(bool useSetting, Colour fore);\n\tvoid SetWhitespaceBack(bool useSetting, Colour back);\n\tvoid SetWhitespaceSize(int size);\n\tint WhitespaceSize();\n\tvoid SetLineState(Line line, int state);\n\tint LineState(Line line);\n\tint MaxLineState();\n\tbool CaretLineVisible();\n\tvoid SetCaretLineVisible(bool show);\n\tColour CaretLineBack();\n\tvoid SetCaretLineBack(Colour back);\n\tint CaretLineFrame();\n\tvoid SetCaretLineFrame(int width);\n\tvoid StyleSetChangeable(int style, bool changeable);\n\tvoid AutoCShow(Position lengthEntered, const char *itemList);\n\tvoid AutoCCancel();\n\tbool AutoCActive();\n\tPosition AutoCPosStart();\n\tvoid AutoCComplete();\n\tvoid AutoCStops(const char *characterSet);\n\tvoid AutoCSetSeparator(int separatorCharacter);\n\tint AutoCGetSeparator();\n\tvoid AutoCSelect(const char *select);\n\tvoid AutoCSetCancelAtStart(bool cancel);\n\tbool AutoCGetCancelAtStart();\n\tvoid AutoCSetFillUps(const char *characterSet);\n\tvoid AutoCSetChooseSingle(bool chooseSingle);\n\tbool AutoCGetChooseSingle();\n\tvoid AutoCSetIgnoreCase(bool ignoreCase);\n\tbool AutoCGetIgnoreCase();\n\tvoid UserListShow(int listType, const char *itemList);\n\tvoid AutoCSetAutoHide(bool autoHide);\n\tbool AutoCGetAutoHide();\n\tvoid AutoCSetOptions(Scintilla::AutoCompleteOption options);\n\tScintilla::AutoCompleteOption AutoCGetOptions();\n\tvoid AutoCSetDropRestOfWord(bool dropRestOfWord);\n\tbool AutoCGetDropRestOfWord();\n\tvoid RegisterImage(int type, const char *xpmData);\n\tvoid ClearRegisteredImages();\n\tint AutoCGetTypeSeparator();\n\tvoid AutoCSetTypeSeparator(int separatorCharacter);\n\tvoid AutoCSetMaxWidth(int characterCount);\n\tint AutoCGetMaxWidth();\n\tvoid AutoCSetMaxHeight(int rowCount);\n\tint AutoCGetMaxHeight();\n\tvoid AutoCSetStyle(int style);\n\tint AutoCGetStyle();\n\tvoid AutoCSetImageScale(int scalePercent);\n\tint AutoCGetImageScale();\n\tvoid SetIndent(int indentSize);\n\tint Indent();\n\tvoid SetUseTabs(bool useTabs);\n\tbool UseTabs();\n\tvoid SetLineIndentation(Line line, int indentation);\n\tint LineIndentation(Line line);\n\tPosition LineIndentPosition(Line line);\n\tPosition Column(Position pos);\n\tPosition CountCharacters(Position start, Position end);\n\tPosition CountCodeUnits(Position start, Position end);\n\tvoid SetHScrollBar(bool visible);\n\tbool HScrollBar();\n\tvoid SetIndentationGuides(Scintilla::IndentView indentView);\n\tScintilla::IndentView IndentationGuides();\n\tvoid SetHighlightGuide(Position column);\n\tPosition HighlightGuide();\n\tPosition LineEndPosition(Line line);\n\tint CodePage();\n\tColour CaretFore();\n\tbool ReadOnly();\n\tvoid SetCurrentPos(Position caret);\n\tvoid SetSelectionStart(Position anchor);\n\tPosition SelectionStart();\n\tvoid SetSelectionEnd(Position caret);\n\tPosition SelectionEnd();\n\tvoid SetEmptySelection(Position caret);\n\tvoid SetPrintMagnification(int magnification);\n\tint PrintMagnification();\n\tvoid SetPrintColourMode(Scintilla::PrintOption mode);\n\tScintilla::PrintOption PrintColourMode();\n\tPosition FindText(Scintilla::FindOption searchFlags, void *ft);\n\tPosition FindTextFull(Scintilla::FindOption searchFlags, TextToFindFull *ft);\n\tPosition FormatRange(bool draw, void *fr);\n\tPosition FormatRangeFull(bool draw, RangeToFormatFull *fr);\n\tvoid SetChangeHistory(Scintilla::ChangeHistoryOption changeHistory);\n\tScintilla::ChangeHistoryOption ChangeHistory();\n\tvoid SetUndoSelectionHistory(Scintilla::UndoSelectionHistoryOption undoSelectionHistory);\n\tScintilla::UndoSelectionHistoryOption UndoSelectionHistory();\n\tvoid SetSelectionSerialized(const char *selectionString);\n\tPosition SelectionSerialized(char *selectionString);\n\tstd::string SelectionSerialized();\n\tLine FirstVisibleLine();\n\tPosition GetLine(Line line, char *text);\n\tstd::string GetLine(Line line);\n\tLine LineCount();\n\tvoid AllocateLines(Line lines);\n\tvoid SetMarginLeft(int pixelWidth);\n\tint MarginLeft();\n\tvoid SetMarginRight(int pixelWidth);\n\tint MarginRight();\n\tbool Modify();\n\tvoid SetSel(Position anchor, Position caret);\n\tPosition GetSelText(char *text);\n\tstd::string GetSelText();\n\tPosition GetTextRange(void *tr);\n\tPosition GetTextRangeFull(TextRangeFull *tr);\n\tvoid HideSelection(bool hide);\n\tbool SelectionHidden();\n\tint PointXFromPosition(Position pos);\n\tint PointYFromPosition(Position pos);\n\tLine LineFromPosition(Position pos);\n\tPosition PositionFromLine(Line line);\n\tvoid LineScroll(Position columns, Line lines);\n\tvoid ScrollVertical(Line docLine, Line subLine);\n\tvoid ScrollCaret();\n\tvoid ScrollRange(Position secondary, Position primary);\n\tvoid ReplaceSel(const char *text);\n\tvoid SetReadOnly(bool readOnly);\n\tvoid Null();\n\tbool CanPaste();\n\tbool CanUndo();\n\tvoid EmptyUndoBuffer();\n\tvoid Undo();\n\tvoid Cut();\n\tvoid Copy();\n\tvoid Paste();\n\tvoid Clear();\n\tvoid SetText(const char *text);\n\tPosition GetText(Position length, char *text);\n\tstd::string GetText(Position length);\n\tPosition TextLength();\n\tvoid *DirectFunction();\n\tvoid *DirectStatusFunction();\n\tvoid *DirectPointer();\n\tvoid SetOvertype(bool overType);\n\tbool Overtype();\n\tvoid SetCaretWidth(int pixelWidth);\n\tint CaretWidth();\n\tvoid SetTargetStart(Position start);\n\tPosition TargetStart();\n\tvoid SetTargetStartVirtualSpace(Position space);\n\tPosition TargetStartVirtualSpace();\n\tvoid SetTargetEnd(Position end);\n\tPosition TargetEnd();\n\tvoid SetTargetEndVirtualSpace(Position space);\n\tPosition TargetEndVirtualSpace();\n\tvoid SetTargetRange(Position start, Position end);\n\tPosition TargetText(char *text);\n\tstd::string TargetText();\n\tvoid TargetFromSelection();\n\tvoid TargetWholeDocument();\n\tPosition ReplaceTarget(Position length, const char *text);\n\tPosition ReplaceTargetRE(Position length, const char *text);\n\tPosition ReplaceTargetMinimal(Position length, const char *text);\n\tPosition SearchInTarget(Position length, const char *text);\n\tvoid SetSearchFlags(Scintilla::FindOption searchFlags);\n\tScintilla::FindOption SearchFlags();\n\tvoid CallTipShow(Position pos, const char *definition);\n\tvoid CallTipCancel();\n\tbool CallTipActive();\n\tPosition CallTipPosStart();\n\tvoid CallTipSetPosStart(Position posStart);\n\tvoid CallTipSetHlt(Position highlightStart, Position highlightEnd);\n\tvoid CallTipSetBack(Colour back);\n\tvoid CallTipSetFore(Colour fore);\n\tvoid CallTipSetForeHlt(Colour fore);\n\tvoid CallTipUseStyle(int tabSize);\n\tvoid CallTipSetPosition(bool above);\n\tLine VisibleFromDocLine(Line docLine);\n\tLine DocLineFromVisible(Line displayLine);\n\tLine WrapCount(Line docLine);\n\tvoid SetFoldLevel(Line line, Scintilla::FoldLevel level);\n\tScintilla::FoldLevel FoldLevel(Line line);\n\tLine LastChild(Line line, Scintilla::FoldLevel level);\n\tLine FoldParent(Line line);\n\tvoid ShowLines(Line lineStart, Line lineEnd);\n\tvoid HideLines(Line lineStart, Line lineEnd);\n\tbool LineVisible(Line line);\n\tbool AllLinesVisible();\n\tvoid SetFoldExpanded(Line line, bool expanded);\n\tbool FoldExpanded(Line line);\n\tvoid ToggleFold(Line line);\n\tvoid ToggleFoldShowText(Line line, const char *text);\n\tvoid FoldDisplayTextSetStyle(Scintilla::FoldDisplayTextStyle style);\n\tScintilla::FoldDisplayTextStyle FoldDisplayTextGetStyle();\n\tvoid SetDefaultFoldDisplayText(const char *text);\n\tint GetDefaultFoldDisplayText(char *text);\n\tstd::string GetDefaultFoldDisplayText();\n\tvoid FoldLine(Line line, Scintilla::FoldAction action);\n\tvoid FoldChildren(Line line, Scintilla::FoldAction action);\n\tvoid ExpandChildren(Line line, Scintilla::FoldLevel level);\n\tvoid FoldAll(Scintilla::FoldAction action);\n\tvoid EnsureVisible(Line line);\n\tvoid SetAutomaticFold(Scintilla::AutomaticFold automaticFold);\n\tScintilla::AutomaticFold AutomaticFold();\n\tvoid SetFoldFlags(Scintilla::FoldFlag flags);\n\tvoid EnsureVisibleEnforcePolicy(Line line);\n\tvoid SetTabIndents(bool tabIndents);\n\tbool TabIndents();\n\tvoid SetBackSpaceUnIndents(bool bsUnIndents);\n\tbool BackSpaceUnIndents();\n\tvoid SetMouseDwellTime(int periodMilliseconds);\n\tint MouseDwellTime();\n\tPosition WordStartPosition(Position pos, bool onlyWordCharacters);\n\tPosition WordEndPosition(Position pos, bool onlyWordCharacters);\n\tbool IsRangeWord(Position start, Position end);\n\tvoid SetIdleStyling(Scintilla::IdleStyling idleStyling);\n\tScintilla::IdleStyling IdleStyling();\n\tvoid SetWrapMode(Scintilla::Wrap wrapMode);\n\tScintilla::Wrap WrapMode();\n\tvoid SetWrapVisualFlags(Scintilla::WrapVisualFlag wrapVisualFlags);\n\tScintilla::WrapVisualFlag WrapVisualFlags();\n\tvoid SetWrapVisualFlagsLocation(Scintilla::WrapVisualLocation wrapVisualFlagsLocation);\n\tScintilla::WrapVisualLocation WrapVisualFlagsLocation();\n\tvoid SetWrapStartIndent(int indent);\n\tint WrapStartIndent();\n\tvoid SetWrapIndentMode(Scintilla::WrapIndentMode wrapIndentMode);\n\tScintilla::WrapIndentMode WrapIndentMode();\n\tvoid SetLayoutCache(Scintilla::LineCache cacheMode);\n\tScintilla::LineCache LayoutCache();\n\tvoid SetScrollWidth(int pixelWidth);\n\tint ScrollWidth();\n\tvoid SetScrollWidthTracking(bool tracking);\n\tbool ScrollWidthTracking();\n\tint TextWidth(int style, const char *text);\n\tvoid SetEndAtLastLine(bool endAtLastLine);\n\tbool EndAtLastLine();\n\tint TextHeight(Line line);\n\tvoid SetVScrollBar(bool visible);\n\tbool VScrollBar();\n\tvoid AppendText(Position length, const char *text);\n\tScintilla::PhasesDraw PhasesDraw();\n\tvoid SetPhasesDraw(Scintilla::PhasesDraw phases);\n\tvoid SetFontQuality(Scintilla::FontQuality fontQuality);\n\tScintilla::FontQuality FontQuality();\n\tvoid SetFirstVisibleLine(Line displayLine);\n\tvoid SetMultiPaste(Scintilla::MultiPaste multiPaste);\n\tScintilla::MultiPaste MultiPaste();\n\tint Tag(int tagNumber, char *tagValue);\n\tstd::string Tag(int tagNumber);\n\tvoid LinesJoin();\n\tvoid LinesSplit(int pixelWidth);\n\tvoid SetFoldMarginColour(bool useSetting, Colour back);\n\tvoid SetFoldMarginHiColour(bool useSetting, Colour fore);\n\tvoid SetAccessibility(Scintilla::Accessibility accessibility);\n\tScintilla::Accessibility Accessibility();\n\tvoid LineDown();\n\tvoid LineDownExtend();\n\tvoid LineUp();\n\tvoid LineUpExtend();\n\tvoid CharLeft();\n\tvoid CharLeftExtend();\n\tvoid CharRight();\n\tvoid CharRightExtend();\n\tvoid WordLeft();\n\tvoid WordLeftExtend();\n\tvoid WordRight();\n\tvoid WordRightExtend();\n\tvoid Home();\n\tvoid HomeExtend();\n\tvoid LineEnd();\n\tvoid LineEndExtend();\n\tvoid DocumentStart();\n\tvoid DocumentStartExtend();\n\tvoid DocumentEnd();\n\tvoid DocumentEndExtend();\n\tvoid PageUp();\n\tvoid PageUpExtend();\n\tvoid PageDown();\n\tvoid PageDownExtend();\n\tvoid EditToggleOvertype();\n\tvoid Cancel();\n\tvoid DeleteBack();\n\tvoid Tab();\n\tvoid LineIndent();\n\tvoid BackTab();\n\tvoid LineDedent();\n\tvoid NewLine();\n\tvoid FormFeed();\n\tvoid VCHome();\n\tvoid VCHomeExtend();\n\tvoid ZoomIn();\n\tvoid ZoomOut();\n\tvoid DelWordLeft();\n\tvoid DelWordRight();\n\tvoid DelWordRightEnd();\n\tvoid LineCut();\n\tvoid LineDelete();\n\tvoid LineTranspose();\n\tvoid LineReverse();\n\tvoid LineDuplicate();\n\tvoid LowerCase();\n\tvoid UpperCase();\n\tvoid LineScrollDown();\n\tvoid LineScrollUp();\n\tvoid DeleteBackNotLine();\n\tvoid HomeDisplay();\n\tvoid HomeDisplayExtend();\n\tvoid LineEndDisplay();\n\tvoid LineEndDisplayExtend();\n\tvoid HomeWrap();\n\tvoid HomeWrapExtend();\n\tvoid LineEndWrap();\n\tvoid LineEndWrapExtend();\n\tvoid VCHomeWrap();\n\tvoid VCHomeWrapExtend();\n\tvoid LineCopy();\n\tvoid MoveCaretInsideView();\n\tPosition LineLength(Line line);\n\tvoid BraceHighlight(Position posA, Position posB);\n\tvoid BraceHighlightIndicator(bool useSetting, int indicator);\n\tvoid BraceBadLight(Position pos);\n\tvoid BraceBadLightIndicator(bool useSetting, int indicator);\n\tPosition BraceMatch(Position pos, int maxReStyle);\n\tPosition BraceMatchNext(Position pos, Position startPos);\n\tbool ViewEOL();\n\tvoid SetViewEOL(bool visible);\n\tIDocumentEditable *DocPointer();\n\tvoid SetDocPointer(IDocumentEditable *doc);\n\tvoid SetModEventMask(Scintilla::ModificationFlags eventMask);\n\tPosition EdgeColumn();\n\tvoid SetEdgeColumn(Position column);\n\tScintilla::EdgeVisualStyle EdgeMode();\n\tvoid SetEdgeMode(Scintilla::EdgeVisualStyle edgeMode);\n\tColour EdgeColour();\n\tvoid SetEdgeColour(Colour edgeColour);\n\tvoid MultiEdgeAddLine(Position column, Colour edgeColour);\n\tvoid MultiEdgeClearAll();\n\tPosition MultiEdgeColumn(int which);\n\tvoid SearchAnchor();\n\tPosition SearchNext(Scintilla::FindOption searchFlags, const char *text);\n\tPosition SearchPrev(Scintilla::FindOption searchFlags, const char *text);\n\tLine LinesOnScreen();\n\tvoid UsePopUp(Scintilla::PopUp popUpMode);\n\tbool SelectionIsRectangle();\n\tvoid SetZoom(int zoomInPoints);\n\tint Zoom();\n\tIDocumentEditable *CreateDocument(Position bytes, Scintilla::DocumentOption documentOptions);\n\tvoid AddRefDocument(IDocumentEditable *doc);\n\tvoid ReleaseDocument(IDocumentEditable *doc);\n\tScintilla::DocumentOption DocumentOptions();\n\tScintilla::ModificationFlags ModEventMask();\n\tvoid SetCommandEvents(bool commandEvents);\n\tbool CommandEvents();\n\tvoid SetFocus(bool focus);\n\tbool Focus();\n\tvoid SetStatus(Scintilla::Status status);\n\tScintilla::Status Status();\n\tvoid SetMouseDownCaptures(bool captures);\n\tbool MouseDownCaptures();\n\tvoid SetMouseWheelCaptures(bool captures);\n\tbool MouseWheelCaptures();\n\tvoid SetCursor(Scintilla::CursorShape cursorType);\n\tScintilla::CursorShape Cursor();\n\tvoid SetControlCharSymbol(int symbol);\n\tint ControlCharSymbol();\n\tvoid WordPartLeft();\n\tvoid WordPartLeftExtend();\n\tvoid WordPartRight();\n\tvoid WordPartRightExtend();\n\tvoid SetVisiblePolicy(Scintilla::VisiblePolicy visiblePolicy, int visibleSlop);\n\tvoid DelLineLeft();\n\tvoid DelLineRight();\n\tvoid SetXOffset(int xOffset);\n\tint XOffset();\n\tvoid ChooseCaretX();\n\tvoid GrabFocus();\n\tvoid SetXCaretPolicy(Scintilla::CaretPolicy caretPolicy, int caretSlop);\n\tvoid SetYCaretPolicy(Scintilla::CaretPolicy caretPolicy, int caretSlop);\n\tvoid SetPrintWrapMode(Scintilla::Wrap wrapMode);\n\tScintilla::Wrap PrintWrapMode();\n\tvoid SetHotspotActiveFore(bool useSetting, Colour fore);\n\tColour HotspotActiveFore();\n\tvoid SetHotspotActiveBack(bool useSetting, Colour back);\n\tColour HotspotActiveBack();\n\tvoid SetHotspotActiveUnderline(bool underline);\n\tbool HotspotActiveUnderline();\n\tvoid SetHotspotSingleLine(bool singleLine);\n\tbool HotspotSingleLine();\n\tvoid ParaDown();\n\tvoid ParaDownExtend();\n\tvoid ParaUp();\n\tvoid ParaUpExtend();\n\tPosition PositionBefore(Position pos);\n\tPosition PositionAfter(Position pos);\n\tPosition PositionRelative(Position pos, Position relative);\n\tPosition PositionRelativeCodeUnits(Position pos, Position relative);\n\tvoid CopyRange(Position start, Position end);\n\tvoid CopyText(Position length, const char *text);\n\tvoid SetSelectionMode(Scintilla::SelectionMode selectionMode);\n\tvoid ChangeSelectionMode(Scintilla::SelectionMode selectionMode);\n\tScintilla::SelectionMode SelectionMode();\n\tvoid SetMoveExtendsSelection(bool moveExtendsSelection);\n\tbool MoveExtendsSelection();\n\tPosition GetLineSelStartPosition(Line line);\n\tPosition GetLineSelEndPosition(Line line);\n\tvoid LineDownRectExtend();\n\tvoid LineUpRectExtend();\n\tvoid CharLeftRectExtend();\n\tvoid CharRightRectExtend();\n\tvoid HomeRectExtend();\n\tvoid VCHomeRectExtend();\n\tvoid LineEndRectExtend();\n\tvoid PageUpRectExtend();\n\tvoid PageDownRectExtend();\n\tvoid StutteredPageUp();\n\tvoid StutteredPageUpExtend();\n\tvoid StutteredPageDown();\n\tvoid StutteredPageDownExtend();\n\tvoid WordLeftEnd();\n\tvoid WordLeftEndExtend();\n\tvoid WordRightEnd();\n\tvoid WordRightEndExtend();\n\tvoid SetWhitespaceChars(const char *characters);\n\tint WhitespaceChars(char *characters);\n\tstd::string WhitespaceChars();\n\tvoid SetPunctuationChars(const char *characters);\n\tint PunctuationChars(char *characters);\n\tstd::string PunctuationChars();\n\tvoid SetCharsDefault();\n\tint AutoCGetCurrent();\n\tint AutoCGetCurrentText(char *text);\n\tstd::string AutoCGetCurrentText();\n\tvoid AutoCSetCaseInsensitiveBehaviour(Scintilla::CaseInsensitiveBehaviour behaviour);\n\tScintilla::CaseInsensitiveBehaviour AutoCGetCaseInsensitiveBehaviour();\n\tvoid AutoCSetMulti(Scintilla::MultiAutoComplete multi);\n\tScintilla::MultiAutoComplete AutoCGetMulti();\n\tvoid AutoCSetOrder(Scintilla::Ordering order);\n\tScintilla::Ordering AutoCGetOrder();\n\tvoid Allocate(Position bytes);\n\tPosition TargetAsUTF8(char *s);\n\tstd::string TargetAsUTF8();\n\tvoid SetLengthForEncode(Position bytes);\n\tPosition EncodedFromUTF8(const char *utf8, char *encoded);\n\tstd::string EncodedFromUTF8(const char *utf8);\n\tPosition FindColumn(Line line, Position column);\n\tScintilla::CaretSticky CaretSticky();\n\tvoid SetCaretSticky(Scintilla::CaretSticky useCaretStickyBehaviour);\n\tvoid ToggleCaretSticky();\n\tvoid SetPasteConvertEndings(bool convert);\n\tbool PasteConvertEndings();\n\tvoid ReplaceRectangular(Position length, const char *text);\n\tvoid SelectionDuplicate();\n\tvoid SetCaretLineBackAlpha(Scintilla::Alpha alpha);\n\tScintilla::Alpha CaretLineBackAlpha();\n\tvoid SetCaretStyle(Scintilla::CaretStyle caretStyle);\n\tScintilla::CaretStyle CaretStyle();\n\tvoid SetIndicatorCurrent(int indicator);\n\tint IndicatorCurrent();\n\tvoid SetIndicatorValue(int value);\n\tint IndicatorValue();\n\tvoid IndicatorFillRange(Position start, Position lengthFill);\n\tvoid IndicatorClearRange(Position start, Position lengthClear);\n\tint IndicatorAllOnFor(Position pos);\n\tint IndicatorValueAt(int indicator, Position pos);\n\tPosition IndicatorStart(int indicator, Position pos);\n\tPosition IndicatorEnd(int indicator, Position pos);\n\tvoid SetPositionCache(int size);\n\tint PositionCache();\n\tvoid SetLayoutThreads(int threads);\n\tint LayoutThreads();\n\tvoid CopyAllowLine();\n\tvoid CutAllowLine();\n\tvoid SetCopySeparator(const char *separator);\n\tint CopySeparator(char *separator);\n\tstd::string CopySeparator();\n\tvoid *CharacterPointer();\n\tvoid *RangePointer(Position start, Position lengthRange);\n\tPosition GapPosition();\n\tvoid IndicSetAlpha(int indicator, Scintilla::Alpha alpha);\n\tScintilla::Alpha IndicGetAlpha(int indicator);\n\tvoid IndicSetOutlineAlpha(int indicator, Scintilla::Alpha alpha);\n\tScintilla::Alpha IndicGetOutlineAlpha(int indicator);\n\tvoid SetExtraAscent(int extraAscent);\n\tint ExtraAscent();\n\tvoid SetExtraDescent(int extraDescent);\n\tint ExtraDescent();\n\tScintilla::MarkerSymbol MarkerSymbolDefined(int markerNumber);\n\tvoid MarginSetText(Line line, const char *text);\n\tint MarginGetText(Line line, char *text);\n\tstd::string MarginGetText(Line line);\n\tvoid MarginSetStyle(Line line, int style);\n\tint MarginGetStyle(Line line);\n\tvoid MarginSetStyles(Line line, const char *styles);\n\tint MarginGetStyles(Line line, char *styles);\n\tstd::string MarginGetStyles(Line line);\n\tvoid MarginTextClearAll();\n\tvoid MarginSetStyleOffset(int style);\n\tint MarginGetStyleOffset();\n\tvoid SetMarginOptions(Scintilla::MarginOption marginOptions);\n\tScintilla::MarginOption MarginOptions();\n\tvoid AnnotationSetText(Line line, const char *text);\n\tint AnnotationGetText(Line line, char *text);\n\tstd::string AnnotationGetText(Line line);\n\tvoid AnnotationSetStyle(Line line, int style);\n\tint AnnotationGetStyle(Line line);\n\tvoid AnnotationSetStyles(Line line, const char *styles);\n\tint AnnotationGetStyles(Line line, char *styles);\n\tstd::string AnnotationGetStyles(Line line);\n\tint AnnotationGetLines(Line line);\n\tvoid AnnotationClearAll();\n\tvoid AnnotationSetVisible(Scintilla::AnnotationVisible visible);\n\tScintilla::AnnotationVisible AnnotationGetVisible();\n\tvoid AnnotationSetStyleOffset(int style);\n\tint AnnotationGetStyleOffset();\n\tvoid ReleaseAllExtendedStyles();\n\tint AllocateExtendedStyles(int numberStyles);\n\tvoid AddUndoAction(int token, Scintilla::UndoFlags flags);\n\tPosition CharPositionFromPoint(int x, int y);\n\tPosition CharPositionFromPointClose(int x, int y);\n\tvoid SetMouseSelectionRectangularSwitch(bool mouseSelectionRectangularSwitch);\n\tbool MouseSelectionRectangularSwitch();\n\tvoid SetMultipleSelection(bool multipleSelection);\n\tbool MultipleSelection();\n\tvoid SetAdditionalSelectionTyping(bool additionalSelectionTyping);\n\tbool AdditionalSelectionTyping();\n\tvoid SetAdditionalCaretsBlink(bool additionalCaretsBlink);\n\tbool AdditionalCaretsBlink();\n\tvoid SetAdditionalCaretsVisible(bool additionalCaretsVisible);\n\tbool AdditionalCaretsVisible();\n\tint Selections();\n\tbool SelectionEmpty();\n\tvoid ClearSelections();\n\tvoid SetSelection(Position caret, Position anchor);\n\tvoid AddSelection(Position caret, Position anchor);\n\tint SelectionFromPoint(int x, int y);\n\tvoid DropSelectionN(int selection);\n\tvoid SetMainSelection(int selection);\n\tint MainSelection();\n\tvoid SetSelectionNCaret(int selection, Position caret);\n\tPosition SelectionNCaret(int selection);\n\tvoid SetSelectionNAnchor(int selection, Position anchor);\n\tPosition SelectionNAnchor(int selection);\n\tvoid SetSelectionNCaretVirtualSpace(int selection, Position space);\n\tPosition SelectionNCaretVirtualSpace(int selection);\n\tvoid SetSelectionNAnchorVirtualSpace(int selection, Position space);\n\tPosition SelectionNAnchorVirtualSpace(int selection);\n\tvoid SetSelectionNStart(int selection, Position anchor);\n\tPosition SelectionNStart(int selection);\n\tPosition SelectionNStartVirtualSpace(int selection);\n\tvoid SetSelectionNEnd(int selection, Position caret);\n\tPosition SelectionNEndVirtualSpace(int selection);\n\tPosition SelectionNEnd(int selection);\n\tvoid SetRectangularSelectionCaret(Position caret);\n\tPosition RectangularSelectionCaret();\n\tvoid SetRectangularSelectionAnchor(Position anchor);\n\tPosition RectangularSelectionAnchor();\n\tvoid SetRectangularSelectionCaretVirtualSpace(Position space);\n\tPosition RectangularSelectionCaretVirtualSpace();\n\tvoid SetRectangularSelectionAnchorVirtualSpace(Position space);\n\tPosition RectangularSelectionAnchorVirtualSpace();\n\tvoid SetVirtualSpaceOptions(Scintilla::VirtualSpace virtualSpaceOptions);\n\tScintilla::VirtualSpace VirtualSpaceOptions();\n\tvoid SetRectangularSelectionModifier(int modifier);\n\tint RectangularSelectionModifier();\n\tvoid SetAdditionalSelFore(Colour fore);\n\tvoid SetAdditionalSelBack(Colour back);\n\tvoid SetAdditionalSelAlpha(Scintilla::Alpha alpha);\n\tScintilla::Alpha AdditionalSelAlpha();\n\tvoid SetAdditionalCaretFore(Colour fore);\n\tColour AdditionalCaretFore();\n\tvoid RotateSelection();\n\tvoid SwapMainAnchorCaret();\n\tvoid MultipleSelectAddNext();\n\tvoid MultipleSelectAddEach();\n\tint ChangeLexerState(Position start, Position end);\n\tLine ContractedFoldNext(Line lineStart);\n\tvoid VerticalCentreCaret();\n\tvoid MoveSelectedLinesUp();\n\tvoid MoveSelectedLinesDown();\n\tvoid SetIdentifier(int identifier);\n\tint Identifier();\n\tvoid RGBAImageSetWidth(int width);\n\tvoid RGBAImageSetHeight(int height);\n\tvoid RGBAImageSetScale(int scalePercent);\n\tvoid MarkerDefineRGBAImage(int markerNumber, const char *pixels);\n\tvoid RegisterRGBAImage(int type, const char *pixels);\n\tvoid ScrollToStart();\n\tvoid ScrollToEnd();\n\tvoid SetTechnology(Scintilla::Technology technology);\n\tScintilla::Technology Technology();\n\tvoid *CreateLoader(Position bytes, Scintilla::DocumentOption documentOptions);\n\tvoid FindIndicatorShow(Position start, Position end);\n\tvoid FindIndicatorFlash(Position start, Position end);\n\tvoid FindIndicatorHide();\n\tvoid VCHomeDisplay();\n\tvoid VCHomeDisplayExtend();\n\tbool CaretLineVisibleAlways();\n\tvoid SetCaretLineVisibleAlways(bool alwaysVisible);\n\tvoid SetLineEndTypesAllowed(Scintilla::LineEndType lineEndBitSet);\n\tScintilla::LineEndType LineEndTypesAllowed();\n\tScintilla::LineEndType LineEndTypesActive();\n\tvoid SetRepresentation(const char *encodedCharacter, const char *representation);\n\tint Representation(const char *encodedCharacter, char *representation);\n\tstd::string Representation(const char *encodedCharacter);\n\tvoid ClearRepresentation(const char *encodedCharacter);\n\tvoid ClearAllRepresentations();\n\tvoid SetRepresentationAppearance(const char *encodedCharacter, Scintilla::RepresentationAppearance appearance);\n\tScintilla::RepresentationAppearance RepresentationAppearance(const char *encodedCharacter);\n\tvoid SetRepresentationColour(const char *encodedCharacter, ColourAlpha colour);\n\tColourAlpha RepresentationColour(const char *encodedCharacter);\n\tvoid EOLAnnotationSetText(Line line, const char *text);\n\tint EOLAnnotationGetText(Line line, char *text);\n\tstd::string EOLAnnotationGetText(Line line);\n\tvoid EOLAnnotationSetStyle(Line line, int style);\n\tint EOLAnnotationGetStyle(Line line);\n\tvoid EOLAnnotationClearAll();\n\tvoid EOLAnnotationSetVisible(Scintilla::EOLAnnotationVisible visible);\n\tScintilla::EOLAnnotationVisible EOLAnnotationGetVisible();\n\tvoid EOLAnnotationSetStyleOffset(int style);\n\tint EOLAnnotationGetStyleOffset();\n\tbool SupportsFeature(Scintilla::Supports feature);\n\tScintilla::LineCharacterIndexType LineCharacterIndex();\n\tvoid AllocateLineCharacterIndex(Scintilla::LineCharacterIndexType lineCharacterIndex);\n\tvoid ReleaseLineCharacterIndex(Scintilla::LineCharacterIndexType lineCharacterIndex);\n\tLine LineFromIndexPosition(Position pos, Scintilla::LineCharacterIndexType lineCharacterIndex);\n\tPosition IndexPositionFromLine(Line line, Scintilla::LineCharacterIndexType lineCharacterIndex);\n\tvoid StartRecord();\n\tvoid StopRecord();\n\tint Lexer();\n\tvoid Colourise(Position start, Position end);\n\tvoid SetProperty(const char *key, const char *value);\n\tvoid SetKeyWords(int keyWordSet, const char *keyWords);\n\tint Property(const char *key, char *value);\n\tstd::string Property(const char *key);\n\tint PropertyExpanded(const char *key, char *value);\n\tstd::string PropertyExpanded(const char *key);\n\tint PropertyInt(const char *key, int defaultValue);\n\tint LexerLanguage(char *language);\n\tstd::string LexerLanguage();\n\tvoid *PrivateLexerCall(int operation, void *pointer);\n\tint PropertyNames(char *names);\n\tstd::string PropertyNames();\n\tScintilla::TypeProperty PropertyType(const char *name);\n\tint DescribeProperty(const char *name, char *description);\n\tstd::string DescribeProperty(const char *name);\n\tint DescribeKeyWordSets(char *descriptions);\n\tstd::string DescribeKeyWordSets();\n\tScintilla::LineEndType LineEndTypesSupported();\n\tint AllocateSubStyles(int styleBase, int numberStyles);\n\tint SubStylesStart(int styleBase);\n\tint SubStylesLength(int styleBase);\n\tint StyleFromSubStyle(int subStyle);\n\tint PrimaryStyleFromStyle(int style);\n\tvoid FreeSubStyles();\n\tvoid SetIdentifiers(int style, const char *identifiers);\n\tint DistanceToSecondaryStyles();\n\tint SubStyleBases(char *styles);\n\tstd::string SubStyleBases();\n\tint NamedStyles();\n\tint NameOfStyle(int style, char *name);\n\tstd::string NameOfStyle(int style);\n\tint TagsOfStyle(int style, char *tags);\n\tstd::string TagsOfStyle(int style);\n\tint DescriptionOfStyle(int style, char *description);\n\tstd::string DescriptionOfStyle(int style);\n\tvoid SetILexer(void *ilexer);\n\tScintilla::Bidirectional Bidirectional();\n\tvoid SetBidirectional(Scintilla::Bidirectional bidirectional);\n\n//--Autogenerated -- end of section automatically generated from Scintilla.iface\n\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/include/ScintillaMessages.h",
    "content": "// Scintilla source code edit control\n/** @file ScintillaMessages.h\n ** Enumerate the messages that can be sent to Scintilla.\n **/\n// Copyright 1998-2019 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n/* Most of this file is automatically generated from the Scintilla.iface interface definition\n * file which contains any comments about the definitions. ScintillaAPIFacer.py does the generation. */\n\n#ifndef SCINTILLAMESSAGES_H\n#define SCINTILLAMESSAGES_H\n\nnamespace Scintilla {\n\n// Enumerations\n//++Autogenerated -- start of section automatically generated from Scintilla.iface\nenum class Message {\n\tAddText = 2001,\n\tAddStyledText = 2002,\n\tInsertText = 2003,\n\tChangeInsertion = 2672,\n\tClearAll = 2004,\n\tDeleteRange = 2645,\n\tClearDocumentStyle = 2005,\n\tGetLength = 2006,\n\tGetCharAt = 2007,\n\tGetCurrentPos = 2008,\n\tGetAnchor = 2009,\n\tGetStyleAt = 2010,\n\tGetStyleIndexAt = 2038,\n\tRedo = 2011,\n\tSetUndoCollection = 2012,\n\tSelectAll = 2013,\n\tSetSavePoint = 2014,\n\tGetStyledText = 2015,\n\tGetStyledTextFull = 2778,\n\tCanRedo = 2016,\n\tMarkerLineFromHandle = 2017,\n\tMarkerDeleteHandle = 2018,\n\tMarkerHandleFromLine = 2732,\n\tMarkerNumberFromLine = 2733,\n\tGetUndoCollection = 2019,\n\tGetViewWS = 2020,\n\tSetViewWS = 2021,\n\tGetTabDrawMode = 2698,\n\tSetTabDrawMode = 2699,\n\tPositionFromPoint = 2022,\n\tPositionFromPointClose = 2023,\n\tGotoLine = 2024,\n\tGotoPos = 2025,\n\tSetAnchor = 2026,\n\tGetCurLine = 2027,\n\tGetEndStyled = 2028,\n\tConvertEOLs = 2029,\n\tGetEOLMode = 2030,\n\tSetEOLMode = 2031,\n\tStartStyling = 2032,\n\tSetStyling = 2033,\n\tGetBufferedDraw = 2034,\n\tSetBufferedDraw = 2035,\n\tSetTabWidth = 2036,\n\tGetTabWidth = 2121,\n\tSetTabMinimumWidth = 2724,\n\tGetTabMinimumWidth = 2725,\n\tClearTabStops = 2675,\n\tAddTabStop = 2676,\n\tGetNextTabStop = 2677,\n\tSetCodePage = 2037,\n\tSetFontLocale = 2760,\n\tGetFontLocale = 2761,\n\tGetIMEInteraction = 2678,\n\tSetIMEInteraction = 2679,\n\tMarkerDefine = 2040,\n\tMarkerSetFore = 2041,\n\tMarkerSetBack = 2042,\n\tMarkerSetBackSelected = 2292,\n\tMarkerSetForeTranslucent = 2294,\n\tMarkerSetBackTranslucent = 2295,\n\tMarkerSetBackSelectedTranslucent = 2296,\n\tMarkerSetStrokeWidth = 2297,\n\tMarkerEnableHighlight = 2293,\n\tMarkerAdd = 2043,\n\tMarkerDelete = 2044,\n\tMarkerDeleteAll = 2045,\n\tMarkerGet = 2046,\n\tMarkerNext = 2047,\n\tMarkerPrevious = 2048,\n\tMarkerDefinePixmap = 2049,\n\tMarkerAddSet = 2466,\n\tMarkerSetAlpha = 2476,\n\tMarkerGetLayer = 2734,\n\tMarkerSetLayer = 2735,\n\tSetMarginTypeN = 2240,\n\tGetMarginTypeN = 2241,\n\tSetMarginWidthN = 2242,\n\tGetMarginWidthN = 2243,\n\tSetMarginMaskN = 2244,\n\tGetMarginMaskN = 2245,\n\tSetMarginSensitiveN = 2246,\n\tGetMarginSensitiveN = 2247,\n\tSetMarginCursorN = 2248,\n\tGetMarginCursorN = 2249,\n\tSetMarginBackN = 2250,\n\tGetMarginBackN = 2251,\n\tSetMargins = 2252,\n\tGetMargins = 2253,\n\tStyleClearAll = 2050,\n\tStyleSetFore = 2051,\n\tStyleSetBack = 2052,\n\tStyleSetBold = 2053,\n\tStyleSetItalic = 2054,\n\tStyleSetSize = 2055,\n\tStyleSetFont = 2056,\n\tStyleSetEOLFilled = 2057,\n\tStyleResetDefault = 2058,\n\tStyleSetUnderline = 2059,\n\tStyleGetFore = 2481,\n\tStyleGetBack = 2482,\n\tStyleGetBold = 2483,\n\tStyleGetItalic = 2484,\n\tStyleGetSize = 2485,\n\tStyleGetFont = 2486,\n\tStyleGetEOLFilled = 2487,\n\tStyleGetUnderline = 2488,\n\tStyleGetCase = 2489,\n\tStyleGetCharacterSet = 2490,\n\tStyleGetVisible = 2491,\n\tStyleGetChangeable = 2492,\n\tStyleGetHotSpot = 2493,\n\tStyleSetCase = 2060,\n\tStyleSetSizeFractional = 2061,\n\tStyleGetSizeFractional = 2062,\n\tStyleSetWeight = 2063,\n\tStyleGetWeight = 2064,\n\tStyleSetCharacterSet = 2066,\n\tStyleSetHotSpot = 2409,\n\tStyleSetCheckMonospaced = 2254,\n\tStyleGetCheckMonospaced = 2255,\n\tStyleSetStretch = 2258,\n\tStyleGetStretch = 2259,\n\tStyleSetInvisibleRepresentation = 2256,\n\tStyleGetInvisibleRepresentation = 2257,\n\tSetElementColour = 2753,\n\tGetElementColour = 2754,\n\tResetElementColour = 2755,\n\tGetElementIsSet = 2756,\n\tGetElementAllowsTranslucent = 2757,\n\tGetElementBaseColour = 2758,\n\tSetSelFore = 2067,\n\tSetSelBack = 2068,\n\tGetSelAlpha = 2477,\n\tSetSelAlpha = 2478,\n\tGetSelEOLFilled = 2479,\n\tSetSelEOLFilled = 2480,\n\tGetSelectionLayer = 2762,\n\tSetSelectionLayer = 2763,\n\tGetCaretLineLayer = 2764,\n\tSetCaretLineLayer = 2765,\n\tGetCaretLineHighlightSubLine = 2773,\n\tSetCaretLineHighlightSubLine = 2774,\n\tSetCaretFore = 2069,\n\tAssignCmdKey = 2070,\n\tClearCmdKey = 2071,\n\tClearAllCmdKeys = 2072,\n\tSetStylingEx = 2073,\n\tStyleSetVisible = 2074,\n\tGetCaretPeriod = 2075,\n\tSetCaretPeriod = 2076,\n\tSetWordChars = 2077,\n\tGetWordChars = 2646,\n\tSetCharacterCategoryOptimization = 2720,\n\tGetCharacterCategoryOptimization = 2721,\n\tBeginUndoAction = 2078,\n\tEndUndoAction = 2079,\n\tGetUndoSequence = 2799,\n\tGetUndoActions = 2790,\n\tSetUndoSavePoint = 2791,\n\tGetUndoSavePoint = 2792,\n\tSetUndoDetach = 2793,\n\tGetUndoDetach = 2794,\n\tSetUndoTentative = 2795,\n\tGetUndoTentative = 2796,\n\tSetUndoCurrent = 2797,\n\tGetUndoCurrent = 2798,\n\tPushUndoActionType = 2800,\n\tChangeLastUndoActionText = 2801,\n\tGetUndoActionType = 2802,\n\tGetUndoActionPosition = 2803,\n\tGetUndoActionText = 2804,\n\tIndicSetStyle = 2080,\n\tIndicGetStyle = 2081,\n\tIndicSetFore = 2082,\n\tIndicGetFore = 2083,\n\tIndicSetUnder = 2510,\n\tIndicGetUnder = 2511,\n\tIndicSetHoverStyle = 2680,\n\tIndicGetHoverStyle = 2681,\n\tIndicSetHoverFore = 2682,\n\tIndicGetHoverFore = 2683,\n\tIndicSetFlags = 2684,\n\tIndicGetFlags = 2685,\n\tIndicSetStrokeWidth = 2751,\n\tIndicGetStrokeWidth = 2752,\n\tSetWhitespaceFore = 2084,\n\tSetWhitespaceBack = 2085,\n\tSetWhitespaceSize = 2086,\n\tGetWhitespaceSize = 2087,\n\tSetLineState = 2092,\n\tGetLineState = 2093,\n\tGetMaxLineState = 2094,\n\tGetCaretLineVisible = 2095,\n\tSetCaretLineVisible = 2096,\n\tGetCaretLineBack = 2097,\n\tSetCaretLineBack = 2098,\n\tGetCaretLineFrame = 2704,\n\tSetCaretLineFrame = 2705,\n\tStyleSetChangeable = 2099,\n\tAutoCShow = 2100,\n\tAutoCCancel = 2101,\n\tAutoCActive = 2102,\n\tAutoCPosStart = 2103,\n\tAutoCComplete = 2104,\n\tAutoCStops = 2105,\n\tAutoCSetSeparator = 2106,\n\tAutoCGetSeparator = 2107,\n\tAutoCSelect = 2108,\n\tAutoCSetCancelAtStart = 2110,\n\tAutoCGetCancelAtStart = 2111,\n\tAutoCSetFillUps = 2112,\n\tAutoCSetChooseSingle = 2113,\n\tAutoCGetChooseSingle = 2114,\n\tAutoCSetIgnoreCase = 2115,\n\tAutoCGetIgnoreCase = 2116,\n\tUserListShow = 2117,\n\tAutoCSetAutoHide = 2118,\n\tAutoCGetAutoHide = 2119,\n\tAutoCSetOptions = 2638,\n\tAutoCGetOptions = 2639,\n\tAutoCSetDropRestOfWord = 2270,\n\tAutoCGetDropRestOfWord = 2271,\n\tRegisterImage = 2405,\n\tClearRegisteredImages = 2408,\n\tAutoCGetTypeSeparator = 2285,\n\tAutoCSetTypeSeparator = 2286,\n\tAutoCSetMaxWidth = 2208,\n\tAutoCGetMaxWidth = 2209,\n\tAutoCSetMaxHeight = 2210,\n\tAutoCGetMaxHeight = 2211,\n\tAutoCSetStyle = 2109,\n\tAutoCGetStyle = 2120,\n\tAutoCSetImageScale = 2815,\n\tAutoCGetImageScale = 2816,\n\tSetIndent = 2122,\n\tGetIndent = 2123,\n\tSetUseTabs = 2124,\n\tGetUseTabs = 2125,\n\tSetLineIndentation = 2126,\n\tGetLineIndentation = 2127,\n\tGetLineIndentPosition = 2128,\n\tGetColumn = 2129,\n\tCountCharacters = 2633,\n\tCountCodeUnits = 2715,\n\tSetHScrollBar = 2130,\n\tGetHScrollBar = 2131,\n\tSetIndentationGuides = 2132,\n\tGetIndentationGuides = 2133,\n\tSetHighlightGuide = 2134,\n\tGetHighlightGuide = 2135,\n\tGetLineEndPosition = 2136,\n\tGetCodePage = 2137,\n\tGetCaretFore = 2138,\n\tGetReadOnly = 2140,\n\tSetCurrentPos = 2141,\n\tSetSelectionStart = 2142,\n\tGetSelectionStart = 2143,\n\tSetSelectionEnd = 2144,\n\tGetSelectionEnd = 2145,\n\tSetEmptySelection = 2556,\n\tSetPrintMagnification = 2146,\n\tGetPrintMagnification = 2147,\n\tSetPrintColourMode = 2148,\n\tGetPrintColourMode = 2149,\n\tFindText = 2150,\n\tFindTextFull = 2196,\n\tFormatRange = 2151,\n\tFormatRangeFull = 2777,\n\tSetChangeHistory = 2780,\n\tGetChangeHistory = 2781,\n\tSetUndoSelectionHistory = 2782,\n\tGetUndoSelectionHistory = 2783,\n\tSetSelectionSerialized = 2784,\n\tGetSelectionSerialized = 2785,\n\tGetFirstVisibleLine = 2152,\n\tGetLine = 2153,\n\tGetLineCount = 2154,\n\tAllocateLines = 2089,\n\tSetMarginLeft = 2155,\n\tGetMarginLeft = 2156,\n\tSetMarginRight = 2157,\n\tGetMarginRight = 2158,\n\tGetModify = 2159,\n\tSetSel = 2160,\n\tGetSelText = 2161,\n\tGetTextRange = 2162,\n\tGetTextRangeFull = 2039,\n\tHideSelection = 2163,\n\tGetSelectionHidden = 2088,\n\tPointXFromPosition = 2164,\n\tPointYFromPosition = 2165,\n\tLineFromPosition = 2166,\n\tPositionFromLine = 2167,\n\tLineScroll = 2168,\n\tScrollVertical = 2817,\n\tScrollCaret = 2169,\n\tScrollRange = 2569,\n\tReplaceSel = 2170,\n\tSetReadOnly = 2171,\n\tNull = 2172,\n\tCanPaste = 2173,\n\tCanUndo = 2174,\n\tEmptyUndoBuffer = 2175,\n\tUndo = 2176,\n\tCut = 2177,\n\tCopy = 2178,\n\tPaste = 2179,\n\tClear = 2180,\n\tSetText = 2181,\n\tGetText = 2182,\n\tGetTextLength = 2183,\n\tGetDirectFunction = 2184,\n\tGetDirectStatusFunction = 2772,\n\tGetDirectPointer = 2185,\n\tSetOvertype = 2186,\n\tGetOvertype = 2187,\n\tSetCaretWidth = 2188,\n\tGetCaretWidth = 2189,\n\tSetTargetStart = 2190,\n\tGetTargetStart = 2191,\n\tSetTargetStartVirtualSpace = 2728,\n\tGetTargetStartVirtualSpace = 2729,\n\tSetTargetEnd = 2192,\n\tGetTargetEnd = 2193,\n\tSetTargetEndVirtualSpace = 2730,\n\tGetTargetEndVirtualSpace = 2731,\n\tSetTargetRange = 2686,\n\tGetTargetText = 2687,\n\tTargetFromSelection = 2287,\n\tTargetWholeDocument = 2690,\n\tReplaceTarget = 2194,\n\tReplaceTargetRE = 2195,\n\tReplaceTargetMinimal = 2779,\n\tSearchInTarget = 2197,\n\tSetSearchFlags = 2198,\n\tGetSearchFlags = 2199,\n\tCallTipShow = 2200,\n\tCallTipCancel = 2201,\n\tCallTipActive = 2202,\n\tCallTipPosStart = 2203,\n\tCallTipSetPosStart = 2214,\n\tCallTipSetHlt = 2204,\n\tCallTipSetBack = 2205,\n\tCallTipSetFore = 2206,\n\tCallTipSetForeHlt = 2207,\n\tCallTipUseStyle = 2212,\n\tCallTipSetPosition = 2213,\n\tVisibleFromDocLine = 2220,\n\tDocLineFromVisible = 2221,\n\tWrapCount = 2235,\n\tSetFoldLevel = 2222,\n\tGetFoldLevel = 2223,\n\tGetLastChild = 2224,\n\tGetFoldParent = 2225,\n\tShowLines = 2226,\n\tHideLines = 2227,\n\tGetLineVisible = 2228,\n\tGetAllLinesVisible = 2236,\n\tSetFoldExpanded = 2229,\n\tGetFoldExpanded = 2230,\n\tToggleFold = 2231,\n\tToggleFoldShowText = 2700,\n\tFoldDisplayTextSetStyle = 2701,\n\tFoldDisplayTextGetStyle = 2707,\n\tSetDefaultFoldDisplayText = 2722,\n\tGetDefaultFoldDisplayText = 2723,\n\tFoldLine = 2237,\n\tFoldChildren = 2238,\n\tExpandChildren = 2239,\n\tFoldAll = 2662,\n\tEnsureVisible = 2232,\n\tSetAutomaticFold = 2663,\n\tGetAutomaticFold = 2664,\n\tSetFoldFlags = 2233,\n\tEnsureVisibleEnforcePolicy = 2234,\n\tSetTabIndents = 2260,\n\tGetTabIndents = 2261,\n\tSetBackSpaceUnIndents = 2262,\n\tGetBackSpaceUnIndents = 2263,\n\tSetMouseDwellTime = 2264,\n\tGetMouseDwellTime = 2265,\n\tWordStartPosition = 2266,\n\tWordEndPosition = 2267,\n\tIsRangeWord = 2691,\n\tSetIdleStyling = 2692,\n\tGetIdleStyling = 2693,\n\tSetWrapMode = 2268,\n\tGetWrapMode = 2269,\n\tSetWrapVisualFlags = 2460,\n\tGetWrapVisualFlags = 2461,\n\tSetWrapVisualFlagsLocation = 2462,\n\tGetWrapVisualFlagsLocation = 2463,\n\tSetWrapStartIndent = 2464,\n\tGetWrapStartIndent = 2465,\n\tSetWrapIndentMode = 2472,\n\tGetWrapIndentMode = 2473,\n\tSetLayoutCache = 2272,\n\tGetLayoutCache = 2273,\n\tSetScrollWidth = 2274,\n\tGetScrollWidth = 2275,\n\tSetScrollWidthTracking = 2516,\n\tGetScrollWidthTracking = 2517,\n\tTextWidth = 2276,\n\tSetEndAtLastLine = 2277,\n\tGetEndAtLastLine = 2278,\n\tTextHeight = 2279,\n\tSetVScrollBar = 2280,\n\tGetVScrollBar = 2281,\n\tAppendText = 2282,\n\tGetPhasesDraw = 2673,\n\tSetPhasesDraw = 2674,\n\tSetFontQuality = 2611,\n\tGetFontQuality = 2612,\n\tSetFirstVisibleLine = 2613,\n\tSetMultiPaste = 2614,\n\tGetMultiPaste = 2615,\n\tGetTag = 2616,\n\tLinesJoin = 2288,\n\tLinesSplit = 2289,\n\tSetFoldMarginColour = 2290,\n\tSetFoldMarginHiColour = 2291,\n\tSetAccessibility = 2702,\n\tGetAccessibility = 2703,\n\tLineDown = 2300,\n\tLineDownExtend = 2301,\n\tLineUp = 2302,\n\tLineUpExtend = 2303,\n\tCharLeft = 2304,\n\tCharLeftExtend = 2305,\n\tCharRight = 2306,\n\tCharRightExtend = 2307,\n\tWordLeft = 2308,\n\tWordLeftExtend = 2309,\n\tWordRight = 2310,\n\tWordRightExtend = 2311,\n\tHome = 2312,\n\tHomeExtend = 2313,\n\tLineEnd = 2314,\n\tLineEndExtend = 2315,\n\tDocumentStart = 2316,\n\tDocumentStartExtend = 2317,\n\tDocumentEnd = 2318,\n\tDocumentEndExtend = 2319,\n\tPageUp = 2320,\n\tPageUpExtend = 2321,\n\tPageDown = 2322,\n\tPageDownExtend = 2323,\n\tEditToggleOvertype = 2324,\n\tCancel = 2325,\n\tDeleteBack = 2326,\n\tTab = 2327,\n\tLineIndent = 2813,\n\tBackTab = 2328,\n\tLineDedent = 2814,\n\tNewLine = 2329,\n\tFormFeed = 2330,\n\tVCHome = 2331,\n\tVCHomeExtend = 2332,\n\tZoomIn = 2333,\n\tZoomOut = 2334,\n\tDelWordLeft = 2335,\n\tDelWordRight = 2336,\n\tDelWordRightEnd = 2518,\n\tLineCut = 2337,\n\tLineDelete = 2338,\n\tLineTranspose = 2339,\n\tLineReverse = 2354,\n\tLineDuplicate = 2404,\n\tLowerCase = 2340,\n\tUpperCase = 2341,\n\tLineScrollDown = 2342,\n\tLineScrollUp = 2343,\n\tDeleteBackNotLine = 2344,\n\tHomeDisplay = 2345,\n\tHomeDisplayExtend = 2346,\n\tLineEndDisplay = 2347,\n\tLineEndDisplayExtend = 2348,\n\tHomeWrap = 2349,\n\tHomeWrapExtend = 2450,\n\tLineEndWrap = 2451,\n\tLineEndWrapExtend = 2452,\n\tVCHomeWrap = 2453,\n\tVCHomeWrapExtend = 2454,\n\tLineCopy = 2455,\n\tMoveCaretInsideView = 2401,\n\tLineLength = 2350,\n\tBraceHighlight = 2351,\n\tBraceHighlightIndicator = 2498,\n\tBraceBadLight = 2352,\n\tBraceBadLightIndicator = 2499,\n\tBraceMatch = 2353,\n\tBraceMatchNext = 2369,\n\tGetViewEOL = 2355,\n\tSetViewEOL = 2356,\n\tGetDocPointer = 2357,\n\tSetDocPointer = 2358,\n\tSetModEventMask = 2359,\n\tGetEdgeColumn = 2360,\n\tSetEdgeColumn = 2361,\n\tGetEdgeMode = 2362,\n\tSetEdgeMode = 2363,\n\tGetEdgeColour = 2364,\n\tSetEdgeColour = 2365,\n\tMultiEdgeAddLine = 2694,\n\tMultiEdgeClearAll = 2695,\n\tGetMultiEdgeColumn = 2749,\n\tSearchAnchor = 2366,\n\tSearchNext = 2367,\n\tSearchPrev = 2368,\n\tLinesOnScreen = 2370,\n\tUsePopUp = 2371,\n\tSelectionIsRectangle = 2372,\n\tSetZoom = 2373,\n\tGetZoom = 2374,\n\tCreateDocument = 2375,\n\tAddRefDocument = 2376,\n\tReleaseDocument = 2377,\n\tGetDocumentOptions = 2379,\n\tGetModEventMask = 2378,\n\tSetCommandEvents = 2717,\n\tGetCommandEvents = 2718,\n\tSetFocus = 2380,\n\tGetFocus = 2381,\n\tSetStatus = 2382,\n\tGetStatus = 2383,\n\tSetMouseDownCaptures = 2384,\n\tGetMouseDownCaptures = 2385,\n\tSetMouseWheelCaptures = 2696,\n\tGetMouseWheelCaptures = 2697,\n\tSetCursor = 2386,\n\tGetCursor = 2387,\n\tSetControlCharSymbol = 2388,\n\tGetControlCharSymbol = 2389,\n\tWordPartLeft = 2390,\n\tWordPartLeftExtend = 2391,\n\tWordPartRight = 2392,\n\tWordPartRightExtend = 2393,\n\tSetVisiblePolicy = 2394,\n\tDelLineLeft = 2395,\n\tDelLineRight = 2396,\n\tSetXOffset = 2397,\n\tGetXOffset = 2398,\n\tChooseCaretX = 2399,\n\tGrabFocus = 2400,\n\tSetXCaretPolicy = 2402,\n\tSetYCaretPolicy = 2403,\n\tSetPrintWrapMode = 2406,\n\tGetPrintWrapMode = 2407,\n\tSetHotspotActiveFore = 2410,\n\tGetHotspotActiveFore = 2494,\n\tSetHotspotActiveBack = 2411,\n\tGetHotspotActiveBack = 2495,\n\tSetHotspotActiveUnderline = 2412,\n\tGetHotspotActiveUnderline = 2496,\n\tSetHotspotSingleLine = 2421,\n\tGetHotspotSingleLine = 2497,\n\tParaDown = 2413,\n\tParaDownExtend = 2414,\n\tParaUp = 2415,\n\tParaUpExtend = 2416,\n\tPositionBefore = 2417,\n\tPositionAfter = 2418,\n\tPositionRelative = 2670,\n\tPositionRelativeCodeUnits = 2716,\n\tCopyRange = 2419,\n\tCopyText = 2420,\n\tSetSelectionMode = 2422,\n\tChangeSelectionMode = 2659,\n\tGetSelectionMode = 2423,\n\tSetMoveExtendsSelection = 2719,\n\tGetMoveExtendsSelection = 2706,\n\tGetLineSelStartPosition = 2424,\n\tGetLineSelEndPosition = 2425,\n\tLineDownRectExtend = 2426,\n\tLineUpRectExtend = 2427,\n\tCharLeftRectExtend = 2428,\n\tCharRightRectExtend = 2429,\n\tHomeRectExtend = 2430,\n\tVCHomeRectExtend = 2431,\n\tLineEndRectExtend = 2432,\n\tPageUpRectExtend = 2433,\n\tPageDownRectExtend = 2434,\n\tStutteredPageUp = 2435,\n\tStutteredPageUpExtend = 2436,\n\tStutteredPageDown = 2437,\n\tStutteredPageDownExtend = 2438,\n\tWordLeftEnd = 2439,\n\tWordLeftEndExtend = 2440,\n\tWordRightEnd = 2441,\n\tWordRightEndExtend = 2442,\n\tSetWhitespaceChars = 2443,\n\tGetWhitespaceChars = 2647,\n\tSetPunctuationChars = 2648,\n\tGetPunctuationChars = 2649,\n\tSetCharsDefault = 2444,\n\tAutoCGetCurrent = 2445,\n\tAutoCGetCurrentText = 2610,\n\tAutoCSetCaseInsensitiveBehaviour = 2634,\n\tAutoCGetCaseInsensitiveBehaviour = 2635,\n\tAutoCSetMulti = 2636,\n\tAutoCGetMulti = 2637,\n\tAutoCSetOrder = 2660,\n\tAutoCGetOrder = 2661,\n\tAllocate = 2446,\n\tTargetAsUTF8 = 2447,\n\tSetLengthForEncode = 2448,\n\tEncodedFromUTF8 = 2449,\n\tFindColumn = 2456,\n\tGetCaretSticky = 2457,\n\tSetCaretSticky = 2458,\n\tToggleCaretSticky = 2459,\n\tSetPasteConvertEndings = 2467,\n\tGetPasteConvertEndings = 2468,\n\tReplaceRectangular = 2771,\n\tSelectionDuplicate = 2469,\n\tSetCaretLineBackAlpha = 2470,\n\tGetCaretLineBackAlpha = 2471,\n\tSetCaretStyle = 2512,\n\tGetCaretStyle = 2513,\n\tSetIndicatorCurrent = 2500,\n\tGetIndicatorCurrent = 2501,\n\tSetIndicatorValue = 2502,\n\tGetIndicatorValue = 2503,\n\tIndicatorFillRange = 2504,\n\tIndicatorClearRange = 2505,\n\tIndicatorAllOnFor = 2506,\n\tIndicatorValueAt = 2507,\n\tIndicatorStart = 2508,\n\tIndicatorEnd = 2509,\n\tSetPositionCache = 2514,\n\tGetPositionCache = 2515,\n\tSetLayoutThreads = 2775,\n\tGetLayoutThreads = 2776,\n\tCopyAllowLine = 2519,\n\tCutAllowLine = 2810,\n\tSetCopySeparator = 2811,\n\tGetCopySeparator = 2812,\n\tGetCharacterPointer = 2520,\n\tGetRangePointer = 2643,\n\tGetGapPosition = 2644,\n\tIndicSetAlpha = 2523,\n\tIndicGetAlpha = 2524,\n\tIndicSetOutlineAlpha = 2558,\n\tIndicGetOutlineAlpha = 2559,\n\tSetExtraAscent = 2525,\n\tGetExtraAscent = 2526,\n\tSetExtraDescent = 2527,\n\tGetExtraDescent = 2528,\n\tMarkerSymbolDefined = 2529,\n\tMarginSetText = 2530,\n\tMarginGetText = 2531,\n\tMarginSetStyle = 2532,\n\tMarginGetStyle = 2533,\n\tMarginSetStyles = 2534,\n\tMarginGetStyles = 2535,\n\tMarginTextClearAll = 2536,\n\tMarginSetStyleOffset = 2537,\n\tMarginGetStyleOffset = 2538,\n\tSetMarginOptions = 2539,\n\tGetMarginOptions = 2557,\n\tAnnotationSetText = 2540,\n\tAnnotationGetText = 2541,\n\tAnnotationSetStyle = 2542,\n\tAnnotationGetStyle = 2543,\n\tAnnotationSetStyles = 2544,\n\tAnnotationGetStyles = 2545,\n\tAnnotationGetLines = 2546,\n\tAnnotationClearAll = 2547,\n\tAnnotationSetVisible = 2548,\n\tAnnotationGetVisible = 2549,\n\tAnnotationSetStyleOffset = 2550,\n\tAnnotationGetStyleOffset = 2551,\n\tReleaseAllExtendedStyles = 2552,\n\tAllocateExtendedStyles = 2553,\n\tAddUndoAction = 2560,\n\tCharPositionFromPoint = 2561,\n\tCharPositionFromPointClose = 2562,\n\tSetMouseSelectionRectangularSwitch = 2668,\n\tGetMouseSelectionRectangularSwitch = 2669,\n\tSetMultipleSelection = 2563,\n\tGetMultipleSelection = 2564,\n\tSetAdditionalSelectionTyping = 2565,\n\tGetAdditionalSelectionTyping = 2566,\n\tSetAdditionalCaretsBlink = 2567,\n\tGetAdditionalCaretsBlink = 2568,\n\tSetAdditionalCaretsVisible = 2608,\n\tGetAdditionalCaretsVisible = 2609,\n\tGetSelections = 2570,\n\tGetSelectionEmpty = 2650,\n\tClearSelections = 2571,\n\tSetSelection = 2572,\n\tAddSelection = 2573,\n\tSelectionFromPoint = 2474,\n\tDropSelectionN = 2671,\n\tSetMainSelection = 2574,\n\tGetMainSelection = 2575,\n\tSetSelectionNCaret = 2576,\n\tGetSelectionNCaret = 2577,\n\tSetSelectionNAnchor = 2578,\n\tGetSelectionNAnchor = 2579,\n\tSetSelectionNCaretVirtualSpace = 2580,\n\tGetSelectionNCaretVirtualSpace = 2581,\n\tSetSelectionNAnchorVirtualSpace = 2582,\n\tGetSelectionNAnchorVirtualSpace = 2583,\n\tSetSelectionNStart = 2584,\n\tGetSelectionNStart = 2585,\n\tGetSelectionNStartVirtualSpace = 2726,\n\tSetSelectionNEnd = 2586,\n\tGetSelectionNEndVirtualSpace = 2727,\n\tGetSelectionNEnd = 2587,\n\tSetRectangularSelectionCaret = 2588,\n\tGetRectangularSelectionCaret = 2589,\n\tSetRectangularSelectionAnchor = 2590,\n\tGetRectangularSelectionAnchor = 2591,\n\tSetRectangularSelectionCaretVirtualSpace = 2592,\n\tGetRectangularSelectionCaretVirtualSpace = 2593,\n\tSetRectangularSelectionAnchorVirtualSpace = 2594,\n\tGetRectangularSelectionAnchorVirtualSpace = 2595,\n\tSetVirtualSpaceOptions = 2596,\n\tGetVirtualSpaceOptions = 2597,\n\tSetRectangularSelectionModifier = 2598,\n\tGetRectangularSelectionModifier = 2599,\n\tSetAdditionalSelFore = 2600,\n\tSetAdditionalSelBack = 2601,\n\tSetAdditionalSelAlpha = 2602,\n\tGetAdditionalSelAlpha = 2603,\n\tSetAdditionalCaretFore = 2604,\n\tGetAdditionalCaretFore = 2605,\n\tRotateSelection = 2606,\n\tSwapMainAnchorCaret = 2607,\n\tMultipleSelectAddNext = 2688,\n\tMultipleSelectAddEach = 2689,\n\tChangeLexerState = 2617,\n\tContractedFoldNext = 2618,\n\tVerticalCentreCaret = 2619,\n\tMoveSelectedLinesUp = 2620,\n\tMoveSelectedLinesDown = 2621,\n\tSetIdentifier = 2622,\n\tGetIdentifier = 2623,\n\tRGBAImageSetWidth = 2624,\n\tRGBAImageSetHeight = 2625,\n\tRGBAImageSetScale = 2651,\n\tMarkerDefineRGBAImage = 2626,\n\tRegisterRGBAImage = 2627,\n\tScrollToStart = 2628,\n\tScrollToEnd = 2629,\n\tSetTechnology = 2630,\n\tGetTechnology = 2631,\n\tCreateLoader = 2632,\n\tFindIndicatorShow = 2640,\n\tFindIndicatorFlash = 2641,\n\tFindIndicatorHide = 2642,\n\tVCHomeDisplay = 2652,\n\tVCHomeDisplayExtend = 2653,\n\tGetCaretLineVisibleAlways = 2654,\n\tSetCaretLineVisibleAlways = 2655,\n\tSetLineEndTypesAllowed = 2656,\n\tGetLineEndTypesAllowed = 2657,\n\tGetLineEndTypesActive = 2658,\n\tSetRepresentation = 2665,\n\tGetRepresentation = 2666,\n\tClearRepresentation = 2667,\n\tClearAllRepresentations = 2770,\n\tSetRepresentationAppearance = 2766,\n\tGetRepresentationAppearance = 2767,\n\tSetRepresentationColour = 2768,\n\tGetRepresentationColour = 2769,\n\tEOLAnnotationSetText = 2740,\n\tEOLAnnotationGetText = 2741,\n\tEOLAnnotationSetStyle = 2742,\n\tEOLAnnotationGetStyle = 2743,\n\tEOLAnnotationClearAll = 2744,\n\tEOLAnnotationSetVisible = 2745,\n\tEOLAnnotationGetVisible = 2746,\n\tEOLAnnotationSetStyleOffset = 2747,\n\tEOLAnnotationGetStyleOffset = 2748,\n\tSupportsFeature = 2750,\n\tGetLineCharacterIndex = 2710,\n\tAllocateLineCharacterIndex = 2711,\n\tReleaseLineCharacterIndex = 2712,\n\tLineFromIndexPosition = 2713,\n\tIndexPositionFromLine = 2714,\n\tStartRecord = 3001,\n\tStopRecord = 3002,\n\tGetLexer = 4002,\n\tColourise = 4003,\n\tSetProperty = 4004,\n\tSetKeyWords = 4005,\n\tGetProperty = 4008,\n\tGetPropertyExpanded = 4009,\n\tGetPropertyInt = 4010,\n\tGetLexerLanguage = 4012,\n\tPrivateLexerCall = 4013,\n\tPropertyNames = 4014,\n\tPropertyType = 4015,\n\tDescribeProperty = 4016,\n\tDescribeKeyWordSets = 4017,\n\tGetLineEndTypesSupported = 4018,\n\tAllocateSubStyles = 4020,\n\tGetSubStylesStart = 4021,\n\tGetSubStylesLength = 4022,\n\tGetStyleFromSubStyle = 4027,\n\tGetPrimaryStyleFromStyle = 4028,\n\tFreeSubStyles = 4023,\n\tSetIdentifiers = 4024,\n\tDistanceToSecondaryStyles = 4025,\n\tGetSubStyleBases = 4026,\n\tGetNamedStyles = 4029,\n\tNameOfStyle = 4030,\n\tTagsOfStyle = 4031,\n\tDescriptionOfStyle = 4032,\n\tSetILexer = 4033,\n\tGetBidirectional = 2708,\n\tSetBidirectional = 2709,\n\n\t//Au\n\tSCI_MARGINSTYLENEXT = 9502,\n\tSCI_SETNOTIFYCALLBACK = 9503,\n\tSCI_SETANNOTATIONDRAWCALLBACK = 9504,\n\tSCI_SETMARGINDRAWCALLBACK = 9505,\n\tSCI_ISXINMARGIN = 9506,\n\tSCI_DRAGDROP = 9507,\n};\n//--Autogenerated -- end of section automatically generated from Scintilla.iface\n\n}\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/include/ScintillaStructures.h",
    "content": "// Scintilla source code edit control\n/** @file ScintillaStructures.h\n ** Structures used to communicate with Scintilla.\n ** The same structures are defined for C in Scintilla.h.\n ** Uses definitions from ScintillaTypes.h.\n **/\n// Copyright 2021 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#ifndef SCINTILLASTRUCTURES_H\n#define SCINTILLASTRUCTURES_H\n\nnamespace Scintilla {\n\nusing PositionCR = long;\n\nstruct CharacterRange {\n\tPositionCR cpMin;\n\tPositionCR cpMax;\n};\n\nstruct CharacterRangeFull {\n\tPosition cpMin;\n\tPosition cpMax;\n};\n\nstruct TextRange {\n\tCharacterRange chrg;\n\tchar *lpstrText;\n};\n\nstruct TextRangeFull {\n\tCharacterRangeFull chrg;\n\tchar *lpstrText;\n};\n\nstruct TextToFind {\n\tCharacterRange chrg;\n\tconst char *lpstrText;\n\tCharacterRange chrgText;\n};\n\nstruct TextToFindFull {\n\tCharacterRangeFull chrg;\n\tconst char *lpstrText;\n\tCharacterRangeFull chrgText;\n};\n\nusing SurfaceID = void *;\n\nstruct Rectangle {\n\tint left;\n\tint top;\n\tint right;\n\tint bottom;\n};\n\n/* This structure is used in printing. Not needed by most client code. */\n\nstruct RangeToFormat {\n\tSurfaceID hdc;\n\tSurfaceID hdcTarget;\n\tRectangle rc;\n\tRectangle rcPage;\n\tCharacterRange chrg;\n};\n\nstruct RangeToFormatFull {\n\tSurfaceID hdc;\n\tSurfaceID hdcTarget;\n\tRectangle rc;\n\tRectangle rcPage;\n\tCharacterRangeFull chrg;\n};\n\nstruct NotifyHeader {\n\t/* Compatible with Windows NMHDR.\n\t * hwndFrom is really an environment specific window handle or pointer\n\t * but most clients of Scintilla.h do not have this type visible. */\n\tvoid *hwndFrom;\n\tuptr_t idFrom;\n\tNotification code;\n};\n\nenum class Message;\t// Declare in case ScintillaMessages.h not included\n\nstruct NotificationData {\n\tNotifyHeader nmhdr;\n\tPosition position;\n\t/* SCN_STYLENEEDED, SCN_DOUBLECLICK, SCN_MODIFIED, SCN_MARGINCLICK, */\n\t/* SCN_MARGINRIGHTCLICK, SCN_NEEDSHOWN, SCN_DWELLSTART, SCN_DWELLEND, */\n\t/* SCN_CALLTIPCLICK, */\n\t/* SCN_HOTSPOTCLICK, SCN_HOTSPOTDOUBLECLICK, SCN_HOTSPOTRELEASECLICK, */\n\t/* SCN_INDICATORCLICK, SCN_INDICATORRELEASE, */\n\t/* SCN_USERLISTSELECTION, SCN_AUTOCCOMPLETED, SCN_AUTOCSELECTION, */\n\t/* SCN_AUTOCSELECTIONCHANGE */\n\n\tint ch;\n\t/* SCN_CHARADDED, SCN_KEY, SCN_AUTOCCOMPLETED, SCN_AUTOCSELECTION, */\n\t/* SCN_USERLISTSELECTION */\n\tKeyMod modifiers;\n\t/* SCN_KEY, SCN_DOUBLECLICK, SCN_HOTSPOTCLICK, SCN_HOTSPOTDOUBLECLICK, */\n\t/* SCN_HOTSPOTRELEASECLICK, SCN_INDICATORCLICK, SCN_INDICATORRELEASE, */\n\t/* SCN_MARGINCLICK, SCN_MARGINRIGHTCLICK */\n\n\tModificationFlags modificationType;\t/* SCN_MODIFIED */\n\tconst char *text;\n\t/* SCN_MODIFIED, SCN_USERLISTSELECTION, SCN_URIDROPPED, */\n\t/* SCN_AUTOCCOMPLETED, SCN_AUTOCSELECTION, SCN_AUTOCSELECTIONCHANGE */\n\n\tPosition length;\t\t/* SCN_MODIFIED */\n\tPosition linesAdded;\t/* SCN_MODIFIED */\n\tMessage message;\t/* SCN_MACRORECORD */\n\tuptr_t wParam;\t/* SCN_MACRORECORD */\n\tsptr_t lParam;\t/* SCN_MACRORECORD */\n\tPosition line;\t\t/* SCN_MODIFIED */\n\tFoldLevel foldLevelNow;\t/* SCN_MODIFIED */\n\tFoldLevel foldLevelPrev;\t/* SCN_MODIFIED */\n\tint margin;\t\t/* SCN_MARGINCLICK, SCN_MARGINRIGHTCLICK */\n\tint listType;\t/* SCN_USERLISTSELECTION, SCN_AUTOCSELECTIONCHANGE */\n\tint x;\t\t\t/* SCN_DWELLSTART, SCN_DWELLEND */\n\tint y;\t\t/* SCN_DWELLSTART, SCN_DWELLEND */\n\tint token;\t\t/* SCN_MODIFIED with SC_MOD_CONTAINER */\n\tPosition annotationLinesAdded;\t/* SCN_MODIFIED with SC_MOD_CHANGEANNOTATION */\n\tUpdate updated;\t/* SCN_UPDATEUI */\n\tCompletionMethods listCompletionMethod;\n\t/* SCN_AUTOCSELECTION, SCN_AUTOCCOMPLETED, SCN_USERLISTSELECTION */\n\tCharacterSource characterSource;\t/* SCN_CHARADDED */\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/include/ScintillaTypes.h",
    "content": "// Scintilla source code edit control\n/** @file ScintillaTypes.h\n ** Types used to communicate with Scintilla.\n **/\n// Copyright 1998-2019 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n/* Most of this file is automatically generated from the Scintilla.iface interface definition\n * file which contains any comments about the definitions. ScintillaAPIFacer.py does the generation. */\n\n#ifndef SCINTILLATYPES_H\n#define SCINTILLATYPES_H\n\nnamespace Scintilla {\n\n// Enumerations\n//++Autogenerated -- start of section automatically generated from Scintilla.iface\n\nenum class WhiteSpace {\n\tInvisible = 0,\n\tVisibleAlways = 1,\n\tVisibleAfterIndent = 2,\n\tVisibleOnlyInIndent = 3,\n};\n\nenum class TabDrawMode {\n\tLongArrow = 0,\n\tStrikeOut = 1,\n};\n\nenum class EndOfLine {\n\tCrLf = 0,\n\tCr = 1,\n\tLf = 2,\n};\n\nenum class IMEInteraction {\n\tWindowed = 0,\n\tInline = 1,\n};\n\nenum class Alpha {\n\tTransparent = 0,\n\tOpaque = 255,\n\tNoAlpha = 256,\n};\n\nenum class CursorShape {\n\tNormal = -1,\n\tArrow = 2,\n\tWait = 4,\n\tReverseArrow = 7,\n};\n\nenum class MarkerSymbol {\n\tCircle = 0,\n\tRoundRect = 1,\n\tArrow = 2,\n\tSmallRect = 3,\n\tShortArrow = 4,\n\tEmpty = 5,\n\tArrowDown = 6,\n\tMinus = 7,\n\tPlus = 8,\n\tVLine = 9,\n\tLCorner = 10,\n\tTCorner = 11,\n\tBoxPlus = 12,\n\tBoxPlusConnected = 13,\n\tBoxMinus = 14,\n\tBoxMinusConnected = 15,\n\tLCornerCurve = 16,\n\tTCornerCurve = 17,\n\tCirclePlus = 18,\n\tCirclePlusConnected = 19,\n\tCircleMinus = 20,\n\tCircleMinusConnected = 21,\n\tBackground = 22,\n\tDotDotDot = 23,\n\tArrows = 24,\n\tPixmap = 25,\n\tFullRect = 26,\n\tLeftRect = 27,\n\tAvailable = 28,\n\tUnderline = 29,\n\tRgbaImage = 30,\n\tBookmark = 31,\n\tVerticalBookmark = 32,\n\tBar = 33,\n\tCharacter = 10000,\n};\n\nenum class MarkerOutline {\n\tHistoryRevertedToOrigin = 21,\n\tHistorySaved = 22,\n\tHistoryModified = 23,\n\tHistoryRevertedToModified = 24,\n\tFolderEnd = 25,\n\tFolderOpenMid = 26,\n\tFolderMidTail = 27,\n\tFolderTail = 28,\n\tFolderSub = 29,\n\tFolder = 30,\n\tFolderOpen = 31,\n};\n\nenum class MarginType {\n\tSymbol = 0,\n\tNumber = 1,\n\tBack = 2,\n\tFore = 3,\n\tText = 4,\n\tRText = 5,\n\tColour = 6,\n};\n\nenum class StylesCommon {\n\tDefault = 32,\n\tLineNumber = 33,\n\tBraceLight = 34,\n\tBraceBad = 35,\n\tControlChar = 36,\n\tIndentGuide = 37,\n\tCallTip = 38,\n\tFoldDisplayText = 39,\n\tLastPredefined = 39,\n\tMax = 255,\n};\n\nenum class CharacterSet {\n\tAnsi = 0,\n\tDefault = 1,\n\tBaltic = 186,\n\tChineseBig5 = 136,\n\tEastEurope = 238,\n\tGB2312 = 134,\n\tGreek = 161,\n\tHangul = 129,\n\tMac = 77,\n\tOem = 255,\n\tRussian = 204,\n\tOem866 = 866,\n\tCyrillic = 1251,\n\tShiftJis = 128,\n\tSymbol = 2,\n\tTurkish = 162,\n\tJohab = 130,\n\tHebrew = 177,\n\tArabic = 178,\n\tVietnamese = 163,\n\tThai = 222,\n\tIso8859_15 = 1000,\n};\n\nenum class CaseVisible {\n\tMixed = 0,\n\tUpper = 1,\n\tLower = 2,\n\tCamel = 3,\n};\n\nenum class FontWeight {\n\tNormal = 400,\n\tSemiBold = 600,\n\tBold = 700,\n};\n\nenum class FontStretch {\n\tUltraCondensed = 1,\n\tExtraCondensed = 2,\n\tCondensed = 3,\n\tSemiCondensed = 4,\n\tNormal = 5,\n\tSemiExpanded = 6,\n\tExpanded = 7,\n\tExtraExpanded = 8,\n\tUltraExpanded = 9,\n};\n\nenum class Element {\n\tList = 0,\n\tListBack = 1,\n\tListSelected = 2,\n\tListSelectedBack = 3,\n\tSelectionText = 10,\n\tSelectionBack = 11,\n\tSelectionAdditionalText = 12,\n\tSelectionAdditionalBack = 13,\n\tSelectionSecondaryText = 14,\n\tSelectionSecondaryBack = 15,\n\tSelectionInactiveText = 16,\n\tSelectionInactiveBack = 17,\n\tSelectionInactiveAdditionalText = 18,\n\tSelectionInactiveAdditionalBack = 19,\n\tCaret = 40,\n\tCaretAdditional = 41,\n\tCaretLineBack = 50,\n\tWhiteSpace = 60,\n\tWhiteSpaceBack = 61,\n\tHotSpotActive = 70,\n\tHotSpotActiveBack = 71,\n\tFoldLine = 80,\n\tHiddenLine = 81,\n};\n\nenum class Layer {\n\tBase = 0,\n\tUnderText = 1,\n\tOverText = 2,\n};\n\nenum class IndicatorStyle {\n\tPlain = 0,\n\tSquiggle = 1,\n\tTT = 2,\n\tDiagonal = 3,\n\tStrike = 4,\n\tHidden = 5,\n\tBox = 6,\n\tRoundBox = 7,\n\tStraightBox = 8,\n\tDash = 9,\n\tDots = 10,\n\tSquiggleLow = 11,\n\tDotBox = 12,\n\tSquigglePixmap = 13,\n\tCompositionThick = 14,\n\tCompositionThin = 15,\n\tFullBox = 16,\n\tTextFore = 17,\n\tPoint = 18,\n\tPointCharacter = 19,\n\tGradient = 20,\n\tGradientCentre = 21,\n\tPointTop = 22,\n};\n\nenum class IndicatorNumbers {\n\tContainer = 8,\n\tIme = 32,\n\tImeMax = 35,\n\tHistoryRevertedToOriginInsertion = 36,\n\tHistoryRevertedToOriginDeletion = 37,\n\tHistorySavedInsertion = 38,\n\tHistorySavedDeletion = 39,\n\tHistoryModifiedInsertion = 40,\n\tHistoryModifiedDeletion = 41,\n\tHistoryRevertedToModifiedInsertion = 42,\n\tHistoryRevertedToModifiedDeletion = 43,\n\tMax = 43,\n};\n\nenum class IndicValue {\n\tBit = 0x1000000,\n\tMask = 0xFFFFFF,\n};\n\nenum class IndicFlag {\n\tNone = 0,\n\tValueFore = 1,\n};\n\nenum class AutoCompleteOption {\n\tNormal = 0,\n\tFixedSize = 1,\n\tSelectFirstItem = 2,\n};\n\nenum class IndentView {\n\tNone = 0,\n\tReal = 1,\n\tLookForward = 2,\n\tLookBoth = 3,\n};\n\nenum class PrintOption {\n\tNormal = 0,\n\tInvertLight = 1,\n\tBlackOnWhite = 2,\n\tColourOnWhite = 3,\n\tColourOnWhiteDefaultBG = 4,\n\tScreenColours = 5,\n};\n\nenum class FindOption {\n\tNone = 0x0,\n\tWholeWord = 0x2,\n\tMatchCase = 0x4,\n\tWordStart = 0x00100000,\n\tRegExp = 0x00200000,\n\tPosix = 0x00400000,\n\tCxx11RegEx = 0x00800000,\n};\n\nenum class ChangeHistoryOption {\n\tDisabled = 0,\n\tEnabled = 1,\n\tMarkers = 2,\n\tIndicators = 4,\n};\n\nenum class UndoSelectionHistoryOption {\n\tDisabled = 0,\n\tEnabled = 1,\n\tScroll = 2,\n};\n\nenum class FoldLevel {\n\tNone = 0x0,\n\tBase = 0x400,\n\tWhiteFlag = 0x1000,\n\tHeaderFlag = 0x2000,\n\tNumberMask = 0x0FFF,\n};\n\nenum class FoldDisplayTextStyle {\n\tHidden = 0,\n\tStandard = 1,\n\tBoxed = 2,\n};\n\nenum class FoldAction {\n\tContract = 0,\n\tExpand = 1,\n\tToggle = 2,\n\tContractEveryLevel = 4,\n};\n\nenum class AutomaticFold {\n\tNone = 0x0000,\n\tShow = 0x0001,\n\tClick = 0x0002,\n\tChange = 0x0004,\n};\n\nenum class FoldFlag {\n\tNone = 0x0000,\n\tLineBeforeExpanded = 0x0002,\n\tLineBeforeContracted = 0x0004,\n\tLineAfterExpanded = 0x0008,\n\tLineAfterContracted = 0x0010,\n\tLevelNumbers = 0x0040,\n\tLineState = 0x0080,\n};\n\nenum class IdleStyling {\n\tNone = 0,\n\tToVisible = 1,\n\tAfterVisible = 2,\n\tAll = 3,\n};\n\nenum class Wrap {\n\tNone = 0,\n\tWord = 1,\n\tChar = 2,\n\tWhiteSpace = 3,\n};\n\nenum class WrapVisualFlag {\n\tNone = 0x0000,\n\tEnd = 0x0001,\n\tStart = 0x0002,\n\tMargin = 0x0004,\n};\n\nenum class WrapVisualLocation {\n\tDefault = 0x0000,\n\tEndByText = 0x0001,\n\tStartByText = 0x0002,\n};\n\nenum class WrapIndentMode {\n\tFixed = 0,\n\tSame = 1,\n\tIndent = 2,\n\tDeepIndent = 3,\n};\n\nenum class LineCache {\n\tNone = 0,\n\tCaret = 1,\n\tPage = 2,\n\tDocument = 3,\n};\n\nenum class PhasesDraw {\n\tOne = 0,\n\tTwo = 1,\n\tMultiple = 2,\n};\n\nenum class FontQuality {\n\tQualityMask = 0xF,\n\tQualityDefault = 0,\n\tQualityNonAntialiased = 1,\n\tQualityAntialiased = 2,\n\tQualityLcdOptimized = 3,\n};\n\nenum class MultiPaste {\n\tOnce = 0,\n\tEach = 1,\n};\n\nenum class Accessibility {\n\tDisabled = 0,\n\tEnabled = 1,\n};\n\nenum class EdgeVisualStyle {\n\tNone = 0,\n\tLine = 1,\n\tBackground = 2,\n\tMultiLine = 3,\n};\n\nenum class PopUp {\n\tNever = 0,\n\tAll = 1,\n\tText = 2,\n};\n\nenum class DocumentOption {\n\tDefault = 0,\n\tStylesNone = 0x1,\n\tTextLarge = 0x100,\n};\n\nenum class Status {\n\tOk = 0,\n\tFailure = 1,\n\tBadAlloc = 2,\n\tWarnStart = 1000,\n\tRegEx = 1001,\n};\n\nenum class VisiblePolicy {\n\tSlop = 0x01,\n\tStrict = 0x04,\n};\n\nenum class CaretPolicy {\n\tSlop = 0x01,\n\tStrict = 0x04,\n\tJumps = 0x10,\n\tEven = 0x08,\n};\n\nenum class SelectionMode {\n\tStream = 0,\n\tRectangle = 1,\n\tLines = 2,\n\tThin = 3,\n};\n\nenum class CaseInsensitiveBehaviour {\n\tRespectCase = 0,\n\tIgnoreCase = 1,\n};\n\nenum class MultiAutoComplete {\n\tOnce = 0,\n\tEach = 1,\n};\n\nenum class Ordering {\n\tPreSorted = 0,\n\tPerformSort = 1,\n\tCustom = 2,\n};\n\nenum class CaretSticky {\n\tOff = 0,\n\tOn = 1,\n\tWhiteSpace = 2,\n};\n\nenum class CaretStyle {\n\tInvisible = 0,\n\tLine = 1,\n\tBlock = 2,\n\tOverstrikeBar = 0,\n\tOverstrikeBlock = 0x10,\n\tCurses = 0x20,\n\tInsMask = 0xF,\n\tBlockAfter = 0x100,\n};\n\nenum class MarginOption {\n\tNone = 0,\n\tSubLineSelect = 1,\n};\n\nenum class AnnotationVisible {\n\tHidden = 0,\n\tStandard = 1,\n\tBoxed = 2,\n\tIndented = 3,\n};\n\nenum class UndoFlags {\n\tNone = 0,\n\tMayCoalesce = 1,\n};\n\nenum class VirtualSpace {\n\tNone = 0,\n\tRectangularSelection = 1,\n\tUserAccessible = 2,\n\tNoWrapLineStart = 4,\n};\n\nenum class Technology {\n\tDefault = 0,\n\tDirectWrite = 1,\n\tDirectWriteRetain = 2,\n\tDirectWriteDC = 3,\n\tDirectWrite1 = 4,\n};\n\nenum class LineEndType {\n\t//Default = 0,\n\tDefault = 1, //Au\n\tUnicode = 1,\n};\n\nenum class RepresentationAppearance {\n\tPlain = 0,\n\tBlob = 1,\n\tColour = 0x10,\n};\n\nenum class EOLAnnotationVisible {\n\tHidden = 0x0,\n\tStandard = 0x1,\n\tBoxed = 0x2,\n\tStadium = 0x100,\n\tFlatCircle = 0x101,\n\tAngleCircle = 0x102,\n\tCircleFlat = 0x110,\n\tFlats = 0x111,\n\tAngleFlat = 0x112,\n\tCircleAngle = 0x120,\n\tFlatAngle = 0x121,\n\tAngles = 0x122,\n};\n\nenum class Supports {\n\tLineDrawsFinal = 0,\n\tPixelDivisions = 1,\n\tFractionalStrokeWidth = 2,\n\tTranslucentStroke = 3,\n\tPixelModification = 4,\n\tThreadSafeMeasureWidths = 5,\n};\n\nenum class LineCharacterIndexType {\n\tNone = 0,\n\tUtf32 = 1,\n\tUtf16 = 2,\n};\n\nenum class TypeProperty {\n\tBoolean = 0,\n\tInteger = 1,\n\tString = 2,\n};\n\nenum class ModificationFlags {\n\tNone = 0x0,\n\tInsertText = 0x1,\n\tDeleteText = 0x2,\n\tChangeStyle = 0x4,\n\tChangeFold = 0x8,\n\tUser = 0x10,\n\tUndo = 0x20,\n\tRedo = 0x40,\n\tMultiStepUndoRedo = 0x80,\n\tLastStepInUndoRedo = 0x100,\n\tChangeMarker = 0x200,\n\tBeforeInsert = 0x400,\n\tBeforeDelete = 0x800,\n\tMultilineUndoRedo = 0x1000,\n\tStartAction = 0x2000,\n\tChangeIndicator = 0x4000,\n\tChangeLineState = 0x8000,\n\tChangeMargin = 0x10000,\n\tChangeAnnotation = 0x20000,\n\tContainer = 0x40000,\n\tLexerState = 0x80000,\n\tInsertCheck = 0x100000,\n\tChangeTabStops = 0x200000,\n\tChangeEOLAnnotation = 0x400000,\n\tEventMaskAll = 0x7FFFFF,\n};\n\nenum class Update {\n\tNone = 0x0,\n\tContent = 0x1,\n\tSelection = 0x2,\n\tVScroll = 0x4,\n\tHScroll = 0x8,\n};\n\nenum class FocusChange {\n\tChange = 768,\n\tSetfocus = 512,\n\tKillfocus = 256,\n};\n\nenum class Keys {\n\tDown = 300,\n\tUp = 301,\n\tLeft = 302,\n\tRight = 303,\n\tHome = 304,\n\tEnd = 305,\n\tPrior = 306,\n\tNext = 307,\n\tDelete = 308,\n\tInsert = 309,\n\tEscape = 7,\n\tBack = 8,\n\tTab = 9,\n\tReturn = 13,\n\tAdd = 310,\n\tSubtract = 311,\n\tDivide = 312,\n\tWin = 313,\n\tRWin = 314,\n\tMenu = 315,\n};\n\nenum class KeyMod {\n\tNorm = 0,\n\tShift = 1,\n\tCtrl = 2,\n\tAlt = 4,\n\tSuper = 8,\n\tMeta = 16,\n};\n\nenum class CompletionMethods {\n\tFillUp = 1,\n\tDoubleClick = 2,\n\tTab = 3,\n\tNewline = 4,\n\tCommand = 5,\n\tSingleChoice = 6,\n};\n\nenum class CharacterSource {\n\tDirectInput = 0,\n\tTentativeInput = 1,\n\tImeResult = 2,\n};\n\nenum class Bidirectional {\n\tDisabled = 0,\n\tL2R = 1,\n\tR2L = 2,\n};\n\nenum class Notification {\n\tStyleNeeded = 2000,\n\tCharAdded = 2001,\n\tSavePointReached = 2002,\n\tSavePointLeft = 2003,\n\tModifyAttemptRO = 2004,\n\tKey = 2005,\n\tDoubleClick = 2006,\n\tUpdateUI = 2007,\n\tModified = 2008,\n\tMacroRecord = 2009,\n\tMarginClick = 2010,\n\tNeedShown = 2011,\n\tPainted = 2013,\n\tUserListSelection = 2014,\n\tURIDropped = 2015,\n\tDwellStart = 2016,\n\tDwellEnd = 2017,\n\tZoom = 2018,\n\tHotSpotClick = 2019,\n\tHotSpotDoubleClick = 2020,\n\tCallTipClick = 2021,\n\tAutoCSelection = 2022,\n\tIndicatorClick = 2023,\n\tIndicatorRelease = 2024,\n\tAutoCCancelled = 2025,\n\tAutoCCharDeleted = 2026,\n\tHotSpotReleaseClick = 2027,\n\tFocusIn = 2028,\n\tFocusOut = 2029,\n\tAutoCCompleted = 2030,\n\tMarginRightClick = 2031,\n\tAutoCSelectionChange = 2032,\n};\n//--Autogenerated -- end of section automatically generated from Scintilla.iface\n\nusing Position = intptr_t;\nusing Line = intptr_t;\nusing Colour = int;\nusing ColourAlpha = int;\nusing uptr_t = uintptr_t;\nusing sptr_t = intptr_t;\n\n//++Autogenerated -- start of section automatically generated from Scintilla.iface\n//**1 \\(\\*\\n\\)\nconstexpr Position InvalidPosition = -1;\nconstexpr int CpUtf8 = 65001;\nconstexpr int MarkerMax = 31;\nconstexpr int MaskHistory = 0x01E00000;\nconstexpr int MaskFolders = 0xFE000000;\nconstexpr int MaxMargin = 4;\nconstexpr int FontSizeMultiplier = 100;\nconstexpr int TimeForever = 10000000;\nconstexpr int KeywordsetMax = 8;\n\n//--Autogenerated -- end of section automatically generated from Scintilla.iface\n\nconstexpr int IndicatorMax = static_cast<int>(IndicatorNumbers::Max);\n\n// Functions to manipulate fields from a MarkerOutline\n\ninline int operator<<(int i, MarkerOutline marker) noexcept {\n\treturn i << static_cast<int>(marker);\n}\n\n// Functions to manipulate fields from a FindOption\n\nconstexpr FindOption operator|(FindOption a, FindOption b) noexcept {\n\treturn static_cast<FindOption>(static_cast<int>(a) | static_cast<int>(b));\n}\n\ninline FindOption &operator|=(FindOption &self, FindOption a) noexcept {\n\tself = self | a;\n\treturn self;\n}\n\n// Functions to retrieve and manipulate fields from a FoldLevel\n\nconstexpr FoldLevel operator&(FoldLevel lhs, FoldLevel rhs) noexcept {\n\treturn static_cast<FoldLevel>(static_cast<int>(lhs) & static_cast<int>(rhs));\n}\n\nconstexpr FoldLevel LevelNumberPart(FoldLevel level) noexcept {\n\treturn level & FoldLevel::NumberMask;\n}\n\nconstexpr int LevelNumber(FoldLevel level) noexcept {\n\treturn static_cast<int>(LevelNumberPart(level));\n}\n\nconstexpr bool LevelIsHeader(FoldLevel level) noexcept {\n\treturn (level & FoldLevel::HeaderFlag) == FoldLevel::HeaderFlag;\n}\n\nconstexpr bool LevelIsWhitespace(FoldLevel level) noexcept {\n\treturn (level & FoldLevel::WhiteFlag) == FoldLevel::WhiteFlag;\n}\n\n// Functions to manipulate fields from a FoldFlag\n\nconstexpr FoldFlag operator|(FoldFlag a, FoldFlag b) noexcept {\n\treturn static_cast<FoldFlag>(static_cast<int>(a) | static_cast<int>(b));\n}\n\n// Functions to manipulate fields from a FontQuality\n\nconstexpr FontQuality operator&(FontQuality a, FontQuality b) noexcept {\n\treturn static_cast<FontQuality>(static_cast<int>(a) & static_cast<int>(b));\n}\n\n// Functions to manipulate fields from a DocumentOption\n\nconstexpr DocumentOption operator|(DocumentOption a, DocumentOption b) noexcept {\n\treturn static_cast<DocumentOption>(static_cast<int>(a) | static_cast<int>(b));\n}\n\n// Functions to manipulate fields from a CaretPolicy\n\nconstexpr CaretPolicy operator|(CaretPolicy a, CaretPolicy b) noexcept {\n\treturn static_cast<CaretPolicy>(static_cast<int>(a) | static_cast<int>(b));\n}\n\n// Functions to manipulate fields from a CaretStyle\n\nconstexpr CaretStyle operator|(CaretStyle a, CaretStyle b) noexcept {\n\treturn static_cast<CaretStyle>(static_cast<int>(a) | static_cast<int>(b));\n}\n\nconstexpr CaretStyle operator&(CaretStyle a, CaretStyle b) noexcept {\n\treturn static_cast<CaretStyle>(static_cast<int>(a) & static_cast<int>(b));\n}\n\n// Functions to manipulate fields from a LineEndType\n\nconstexpr LineEndType operator&(LineEndType a, LineEndType b) noexcept {\n\treturn static_cast<LineEndType>(static_cast<int>(a) & static_cast<int>(b));\n}\n\n// Functions to manipulate fields from a RepresentationAppearance\n\nconstexpr RepresentationAppearance operator|(RepresentationAppearance a, RepresentationAppearance b) noexcept {\n\treturn static_cast<RepresentationAppearance>(static_cast<int>(a) | static_cast<int>(b));\n}\n\n// Functions to manipulate fields from a LineCharacterIndexType\n\nconstexpr LineCharacterIndexType operator|(LineCharacterIndexType a, LineCharacterIndexType b) noexcept {\n\treturn static_cast<LineCharacterIndexType>(static_cast<int>(a) | static_cast<int>(b));\n}\n\n// Functions to manipulate fields from a ModificationFlags\n\nconstexpr ModificationFlags operator|(ModificationFlags a, ModificationFlags b) noexcept {\n\treturn static_cast<ModificationFlags>(static_cast<int>(a) | static_cast<int>(b));\n}\n\nconstexpr ModificationFlags operator&(ModificationFlags a, ModificationFlags b) noexcept {\n\treturn static_cast<ModificationFlags>(static_cast<int>(a) & static_cast<int>(b));\n}\n\ninline ModificationFlags &operator|=(ModificationFlags &self, ModificationFlags a) noexcept {\n\tself = self | a;\n\treturn self;\n}\n\n// Functions to manipulate fields from a Update\n\nconstexpr Update operator|(Update a, Update b) noexcept {\n\treturn static_cast<Update>(static_cast<int>(a) | static_cast<int>(b));\n}\n\n// Functions to manipulate fields from a KeyMod\n\nconstexpr KeyMod operator|(KeyMod a, KeyMod b) noexcept {\n\treturn static_cast<KeyMod>(static_cast<int>(a) | static_cast<int>(b));\n}\n\nconstexpr KeyMod operator&(KeyMod a, KeyMod b) noexcept {\n\treturn static_cast<KeyMod>(static_cast<int>(a) & static_cast<int>(b));\n}\n\nconstexpr KeyMod ModifierFlags(bool shift, bool ctrl, bool alt, bool meta=false, bool super=false) noexcept {\n\treturn\n\t\t(shift ? KeyMod::Shift : KeyMod::Norm) |\n\t\t(ctrl ? KeyMod::Ctrl : KeyMod::Norm) |\n\t\t(alt ? KeyMod::Alt : KeyMod::Norm) |\n\t\t(meta ? KeyMod::Meta : KeyMod::Norm) |\n\t\t(super ? KeyMod::Super : KeyMod::Norm);\n}\n\n// Test if an enum class value has some bit flag(s) of test set.\ntemplate <typename T>\nconstexpr bool FlagSet(T value, T test) {\n\treturn (static_cast<int>(value) & static_cast<int>(test)) != 0;\n}\n\n//Au\n\ntypedef void(__stdcall* Sci_NotifyCallback)(void* cbParam, struct NotificationData& n);\n\nstruct _RECT { int left, top, right, bottom; };\n\nstruct Sci_AnnotationDrawCallbackData\n{\n\tint step; void* hdc; _RECT rect; const char* text; int textLen, line, annotLine;\n};\ntypedef int(__stdcall* Sci_AnnotationDrawCallback)(void* cbParam, Sci_AnnotationDrawCallbackData& c);\n\nstruct Sci_MarginDrawCallbackData\n{\n\tvoid* hdc; _RECT rect; int margin, firstLine, lastLine;\n};\ntypedef void(__stdcall* Sci_MarginDrawCallback)(Sci_MarginDrawCallbackData& c);\n\nstruct Sci_DragDropData\n{\n\tint x, y;\n\tchar* text;\n\tint len;\n\tint copy; //bool\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/include/ScintillaWidget.h",
    "content": "/* Scintilla source code edit control */\n/* @file ScintillaWidget.h\n * Definition of Scintilla widget for GTK+.\n * Only needed by GTK+ code but is harmless on other platforms.\n * This comment is not a doc-comment as that causes warnings from g-ir-scanner.\n */\n/* Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>\n * The License.txt file describes the conditions under which this software may be distributed. */\n\n#ifndef SCINTILLAWIDGET_H\n#define SCINTILLAWIDGET_H\n\n#if defined(GTK)\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define SCINTILLA(obj)          G_TYPE_CHECK_INSTANCE_CAST (obj, scintilla_get_type (), ScintillaObject)\n#define SCINTILLA_CLASS(klass)  G_TYPE_CHECK_CLASS_CAST (klass, scintilla_get_type (), ScintillaClass)\n#define IS_SCINTILLA(obj)       G_TYPE_CHECK_INSTANCE_TYPE (obj, scintilla_get_type ())\n\n#define SCINTILLA_TYPE_OBJECT             (scintilla_object_get_type())\n#define SCINTILLA_OBJECT(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), SCINTILLA_TYPE_OBJECT, ScintillaObject))\n#define SCINTILLA_IS_OBJECT(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SCINTILLA_TYPE_OBJECT))\n#define SCINTILLA_OBJECT_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), SCINTILLA_TYPE_OBJECT, ScintillaObjectClass))\n#define SCINTILLA_IS_OBJECT_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), SCINTILLA_TYPE_OBJECT))\n#define SCINTILLA_OBJECT_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS((obj), SCINTILLA_TYPE_OBJECT, ScintillaObjectClass))\n\ntypedef struct _ScintillaObject ScintillaObject;\ntypedef struct _ScintillaClass  ScintillaObjectClass;\n\nstruct _ScintillaObject {\n\tGtkContainer cont;\n\tvoid *pscin;\n};\n\nstruct _ScintillaClass {\n\tGtkContainerClass parent_class;\n\n\tvoid (* command) (ScintillaObject *sci, int cmd, GtkWidget *window);\n\tvoid (* notify) (ScintillaObject *sci, int id, SCNotification *scn);\n};\n\nGType\t\tscintilla_object_get_type\t\t(void);\nGtkWidget*\tscintilla_object_new\t\t\t(void);\ngintptr\t\tscintilla_object_send_message\t(ScintillaObject *sci, unsigned int iMessage, guintptr wParam, gintptr lParam);\n\n\nGType\t\tscnotification_get_type\t\t\t(void);\n#define SCINTILLA_TYPE_NOTIFICATION        (scnotification_get_type())\n\n#ifndef G_IR_SCANNING\n/* The legacy names confuse the g-ir-scanner program */\ntypedef struct _ScintillaClass  ScintillaClass;\n\nGType\t\tscintilla_get_type\t(void);\nGtkWidget*\tscintilla_new\t\t(void);\nvoid\t\tscintilla_set_id\t(ScintillaObject *sci, uptr_t id);\nsptr_t\t\tscintilla_send_message\t(ScintillaObject *sci,unsigned int iMessage, uptr_t wParam, sptr_t lParam);\nvoid\t\tscintilla_release_resources(void);\n#endif\n\n#define SCINTILLA_NOTIFY \"sci-notify\"\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/include/scintilla.h",
    "content": "/* Scintilla source code edit control */\n/** @file Scintilla.h\n ** Interface to the edit control.\n **/\n/* Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>\n * The License.txt file describes the conditions under which this software may be distributed. */\n\n/* Most of this file is automatically generated from the Scintilla.iface interface definition\n * file which contains any comments about the definitions. HFacer.py does the generation. */\n\n#ifndef SCINTILLA_H\n#define SCINTILLA_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#if defined(_WIN32)\n/* Return false on failure: */\nint Scintilla_RegisterClasses(void *hInstance);\nint Scintilla_ReleaseResources(void);\n#endif\n\n#ifdef __cplusplus\n}\n#endif\n\n// Include header that defines basic numeric types.\n#include <stdint.h>\n\n// Define uptr_t, an unsigned integer type large enough to hold a pointer.\ntypedef uintptr_t uptr_t;\n// Define sptr_t, a signed integer large enough to hold a pointer.\ntypedef intptr_t sptr_t;\n\n#include \"Sci_Position.h\"\n\ntypedef sptr_t (*SciFnDirect)(sptr_t ptr, unsigned int iMessage, uptr_t wParam, sptr_t lParam);\ntypedef sptr_t (*SciFnDirectStatus)(sptr_t ptr, unsigned int iMessage, uptr_t wParam, sptr_t lParam, int *pStatus);\n\n#ifndef SCI_DISABLE_AUTOGENERATED\n\n/* ++Autogenerated -- start of section automatically generated from Scintilla.iface */\n#define INVALID_POSITION -1\n#define SCI_START 2000\n#define SCI_OPTIONAL_START 3000\n#define SCI_LEXER_START 4000\n#define SCI_ADDTEXT 2001\n#define SCI_ADDSTYLEDTEXT 2002\n#define SCI_INSERTTEXT 2003\n#define SCI_CHANGEINSERTION 2672\n#define SCI_CLEARALL 2004\n#define SCI_DELETERANGE 2645\n#define SCI_CLEARDOCUMENTSTYLE 2005\n#define SCI_GETLENGTH 2006\n#define SCI_GETCHARAT 2007\n#define SCI_GETCURRENTPOS 2008\n#define SCI_GETANCHOR 2009\n#define SCI_GETSTYLEAT 2010\n#define SCI_GETSTYLEINDEXAT 2038\n#define SCI_REDO 2011\n#define SCI_SETUNDOCOLLECTION 2012\n#define SCI_SELECTALL 2013\n#define SCI_SETSAVEPOINT 2014\n#define SCI_GETSTYLEDTEXT 2015\n#define SCI_GETSTYLEDTEXTFULL 2778\n#define SCI_CANREDO 2016\n#define SCI_MARKERLINEFROMHANDLE 2017\n#define SCI_MARKERDELETEHANDLE 2018\n#define SCI_MARKERHANDLEFROMLINE 2732\n#define SCI_MARKERNUMBERFROMLINE 2733\n#define SCI_GETUNDOCOLLECTION 2019\n#define SCWS_INVISIBLE 0\n#define SCWS_VISIBLEALWAYS 1\n#define SCWS_VISIBLEAFTERINDENT 2\n#define SCWS_VISIBLEONLYININDENT 3\n#define SCI_GETVIEWWS 2020\n#define SCI_SETVIEWWS 2021\n#define SCTD_LONGARROW 0\n#define SCTD_STRIKEOUT 1\n#define SCI_GETTABDRAWMODE 2698\n#define SCI_SETTABDRAWMODE 2699\n#define SCI_POSITIONFROMPOINT 2022\n#define SCI_POSITIONFROMPOINTCLOSE 2023\n#define SCI_GOTOLINE 2024\n#define SCI_GOTOPOS 2025\n#define SCI_SETANCHOR 2026\n#define SCI_GETCURLINE 2027\n#define SCI_GETENDSTYLED 2028\n#define SC_EOL_CRLF 0\n#define SC_EOL_CR 1\n#define SC_EOL_LF 2\n#define SCI_CONVERTEOLS 2029\n#define SCI_GETEOLMODE 2030\n#define SCI_SETEOLMODE 2031\n#define SCI_STARTSTYLING 2032\n#define SCI_SETSTYLING 2033\n#define SCI_GETBUFFEREDDRAW 2034\n#define SCI_SETBUFFEREDDRAW 2035\n#define SCI_SETTABWIDTH 2036\n#define SCI_GETTABWIDTH 2121\n#define SCI_SETTABMINIMUMWIDTH 2724\n#define SCI_GETTABMINIMUMWIDTH 2725\n#define SCI_CLEARTABSTOPS 2675\n#define SCI_ADDTABSTOP 2676\n#define SCI_GETNEXTTABSTOP 2677\n#define SC_CP_UTF8 65001\n#define SCI_SETCODEPAGE 2037\n#define SCI_SETFONTLOCALE 2760\n#define SCI_GETFONTLOCALE 2761\n#define SC_IME_WINDOWED 0\n#define SC_IME_INLINE 1\n#define SCI_GETIMEINTERACTION 2678\n#define SCI_SETIMEINTERACTION 2679\n#define SC_ALPHA_TRANSPARENT 0\n#define SC_ALPHA_OPAQUE 255\n#define SC_ALPHA_NOALPHA 256\n#define SC_CURSORNORMAL -1\n#define SC_CURSORARROW 2\n#define SC_CURSORWAIT 4\n#define SC_CURSORREVERSEARROW 7\n#define MARKER_MAX 31\n#define SC_MARK_CIRCLE 0\n#define SC_MARK_ROUNDRECT 1\n#define SC_MARK_ARROW 2\n#define SC_MARK_SMALLRECT 3\n#define SC_MARK_SHORTARROW 4\n#define SC_MARK_EMPTY 5\n#define SC_MARK_ARROWDOWN 6\n#define SC_MARK_MINUS 7\n#define SC_MARK_PLUS 8\n#define SC_MARK_VLINE 9\n#define SC_MARK_LCORNER 10\n#define SC_MARK_TCORNER 11\n#define SC_MARK_BOXPLUS 12\n#define SC_MARK_BOXPLUSCONNECTED 13\n#define SC_MARK_BOXMINUS 14\n#define SC_MARK_BOXMINUSCONNECTED 15\n#define SC_MARK_LCORNERCURVE 16\n#define SC_MARK_TCORNERCURVE 17\n#define SC_MARK_CIRCLEPLUS 18\n#define SC_MARK_CIRCLEPLUSCONNECTED 19\n#define SC_MARK_CIRCLEMINUS 20\n#define SC_MARK_CIRCLEMINUSCONNECTED 21\n#define SC_MARK_BACKGROUND 22\n#define SC_MARK_DOTDOTDOT 23\n#define SC_MARK_ARROWS 24\n#define SC_MARK_PIXMAP 25\n#define SC_MARK_FULLRECT 26\n#define SC_MARK_LEFTRECT 27\n#define SC_MARK_AVAILABLE 28\n#define SC_MARK_UNDERLINE 29\n#define SC_MARK_RGBAIMAGE 30\n#define SC_MARK_BOOKMARK 31\n#define SC_MARK_VERTICALBOOKMARK 32\n#define SC_MARK_BAR 33\n#define SC_MARK_CHARACTER 10000\n#define SC_MARKNUM_HISTORY_REVERTED_TO_ORIGIN 21\n#define SC_MARKNUM_HISTORY_SAVED 22\n#define SC_MARKNUM_HISTORY_MODIFIED 23\n#define SC_MARKNUM_HISTORY_REVERTED_TO_MODIFIED 24\n#define SC_MARKNUM_FOLDEREND 25\n#define SC_MARKNUM_FOLDEROPENMID 26\n#define SC_MARKNUM_FOLDERMIDTAIL 27\n#define SC_MARKNUM_FOLDERTAIL 28\n#define SC_MARKNUM_FOLDERSUB 29\n#define SC_MARKNUM_FOLDER 30\n#define SC_MARKNUM_FOLDEROPEN 31\n#define SC_MASK_HISTORY 0x01E00000\n#define SC_MASK_FOLDERS 0xFE000000\n#define SCI_MARKERDEFINE 2040\n#define SCI_MARKERSETFORE 2041\n#define SCI_MARKERSETBACK 2042\n#define SCI_MARKERSETBACKSELECTED 2292\n#define SCI_MARKERSETFORETRANSLUCENT 2294\n#define SCI_MARKERSETBACKTRANSLUCENT 2295\n#define SCI_MARKERSETBACKSELECTEDTRANSLUCENT 2296\n#define SCI_MARKERSETSTROKEWIDTH 2297\n#define SCI_MARKERENABLEHIGHLIGHT 2293\n#define SCI_MARKERADD 2043\n#define SCI_MARKERDELETE 2044\n#define SCI_MARKERDELETEALL 2045\n#define SCI_MARKERGET 2046\n#define SCI_MARKERNEXT 2047\n#define SCI_MARKERPREVIOUS 2048\n#define SCI_MARKERDEFINEPIXMAP 2049\n#define SCI_MARKERADDSET 2466\n#define SCI_MARKERSETALPHA 2476\n#define SCI_MARKERGETLAYER 2734\n#define SCI_MARKERSETLAYER 2735\n#define SC_MAX_MARGIN 4\n#define SC_MARGIN_SYMBOL 0\n#define SC_MARGIN_NUMBER 1\n#define SC_MARGIN_BACK 2\n#define SC_MARGIN_FORE 3\n#define SC_MARGIN_TEXT 4\n#define SC_MARGIN_RTEXT 5\n#define SC_MARGIN_COLOUR 6\n#define SCI_SETMARGINTYPEN 2240\n#define SCI_GETMARGINTYPEN 2241\n#define SCI_SETMARGINWIDTHN 2242\n#define SCI_GETMARGINWIDTHN 2243\n#define SCI_SETMARGINMASKN 2244\n#define SCI_GETMARGINMASKN 2245\n#define SCI_SETMARGINSENSITIVEN 2246\n#define SCI_GETMARGINSENSITIVEN 2247\n#define SCI_SETMARGINCURSORN 2248\n#define SCI_GETMARGINCURSORN 2249\n#define SCI_SETMARGINBACKN 2250\n#define SCI_GETMARGINBACKN 2251\n#define SCI_SETMARGINS 2252\n#define SCI_GETMARGINS 2253\n#define STYLE_DEFAULT 32\n#define STYLE_LINENUMBER 33\n#define STYLE_BRACELIGHT 34\n#define STYLE_BRACEBAD 35\n#define STYLE_CONTROLCHAR 36\n#define STYLE_INDENTGUIDE 37\n#define STYLE_CALLTIP 38\n#define STYLE_FOLDDISPLAYTEXT 39\n#define STYLE_LASTPREDEFINED 39\n#define STYLE_MAX 255\n#define SC_CHARSET_ANSI 0\n#define SC_CHARSET_DEFAULT 1\n#define SC_CHARSET_BALTIC 186\n#define SC_CHARSET_CHINESEBIG5 136\n#define SC_CHARSET_EASTEUROPE 238\n#define SC_CHARSET_GB2312 134\n#define SC_CHARSET_GREEK 161\n#define SC_CHARSET_HANGUL 129\n#define SC_CHARSET_MAC 77\n#define SC_CHARSET_OEM 255\n#define SC_CHARSET_RUSSIAN 204\n#define SC_CHARSET_OEM866 866\n#define SC_CHARSET_CYRILLIC 1251\n#define SC_CHARSET_SHIFTJIS 128\n#define SC_CHARSET_SYMBOL 2\n#define SC_CHARSET_TURKISH 162\n#define SC_CHARSET_JOHAB 130\n#define SC_CHARSET_HEBREW 177\n#define SC_CHARSET_ARABIC 178\n#define SC_CHARSET_VIETNAMESE 163\n#define SC_CHARSET_THAI 222\n#define SC_CHARSET_8859_15 1000\n#define SCI_STYLECLEARALL 2050\n#define SCI_STYLESETFORE 2051\n#define SCI_STYLESETBACK 2052\n#define SCI_STYLESETBOLD 2053\n#define SCI_STYLESETITALIC 2054\n#define SCI_STYLESETSIZE 2055\n#define SCI_STYLESETFONT 2056\n#define SCI_STYLESETEOLFILLED 2057\n#define SCI_STYLERESETDEFAULT 2058\n#define SCI_STYLESETUNDERLINE 2059\n#define SC_CASE_MIXED 0\n#define SC_CASE_UPPER 1\n#define SC_CASE_LOWER 2\n#define SC_CASE_CAMEL 3\n#define SCI_STYLEGETFORE 2481\n#define SCI_STYLEGETBACK 2482\n#define SCI_STYLEGETBOLD 2483\n#define SCI_STYLEGETITALIC 2484\n#define SCI_STYLEGETSIZE 2485\n#define SCI_STYLEGETFONT 2486\n#define SCI_STYLEGETEOLFILLED 2487\n#define SCI_STYLEGETUNDERLINE 2488\n#define SCI_STYLEGETCASE 2489\n#define SCI_STYLEGETCHARACTERSET 2490\n#define SCI_STYLEGETVISIBLE 2491\n#define SCI_STYLEGETCHANGEABLE 2492\n#define SCI_STYLEGETHOTSPOT 2493\n#define SCI_STYLESETCASE 2060\n#define SC_FONT_SIZE_MULTIPLIER 100\n#define SCI_STYLESETSIZEFRACTIONAL 2061\n#define SCI_STYLEGETSIZEFRACTIONAL 2062\n#define SC_WEIGHT_NORMAL 400\n#define SC_WEIGHT_SEMIBOLD 600\n#define SC_WEIGHT_BOLD 700\n#define SCI_STYLESETWEIGHT 2063\n#define SCI_STYLEGETWEIGHT 2064\n#define SCI_STYLESETCHARACTERSET 2066\n#define SCI_STYLESETHOTSPOT 2409\n#define SCI_STYLESETCHECKMONOSPACED 2254\n#define SCI_STYLEGETCHECKMONOSPACED 2255\n#define SC_STRETCH_ULTRA_CONDENSED 1\n#define SC_STRETCH_EXTRA_CONDENSED 2\n#define SC_STRETCH_CONDENSED 3\n#define SC_STRETCH_SEMI_CONDENSED 4\n#define SC_STRETCH_NORMAL 5\n#define SC_STRETCH_SEMI_EXPANDED 6\n#define SC_STRETCH_EXPANDED 7\n#define SC_STRETCH_EXTRA_EXPANDED 8\n#define SC_STRETCH_ULTRA_EXPANDED 9\n#define SCI_STYLESETSTRETCH 2258\n#define SCI_STYLEGETSTRETCH 2259\n#define SCI_STYLESETINVISIBLEREPRESENTATION 2256\n#define SCI_STYLEGETINVISIBLEREPRESENTATION 2257\n#define SC_ELEMENT_LIST 0\n#define SC_ELEMENT_LIST_BACK 1\n#define SC_ELEMENT_LIST_SELECTED 2\n#define SC_ELEMENT_LIST_SELECTED_BACK 3\n#define SC_ELEMENT_SELECTION_TEXT 10\n#define SC_ELEMENT_SELECTION_BACK 11\n#define SC_ELEMENT_SELECTION_ADDITIONAL_TEXT 12\n#define SC_ELEMENT_SELECTION_ADDITIONAL_BACK 13\n#define SC_ELEMENT_SELECTION_SECONDARY_TEXT 14\n#define SC_ELEMENT_SELECTION_SECONDARY_BACK 15\n#define SC_ELEMENT_SELECTION_INACTIVE_TEXT 16\n#define SC_ELEMENT_SELECTION_INACTIVE_BACK 17\n#define SC_ELEMENT_SELECTION_INACTIVE_ADDITIONAL_TEXT 18\n#define SC_ELEMENT_SELECTION_INACTIVE_ADDITIONAL_BACK 19\n#define SC_ELEMENT_CARET 40\n#define SC_ELEMENT_CARET_ADDITIONAL 41\n#define SC_ELEMENT_CARET_LINE_BACK 50\n#define SC_ELEMENT_WHITE_SPACE 60\n#define SC_ELEMENT_WHITE_SPACE_BACK 61\n#define SC_ELEMENT_HOT_SPOT_ACTIVE 70\n#define SC_ELEMENT_HOT_SPOT_ACTIVE_BACK 71\n#define SC_ELEMENT_FOLD_LINE 80\n#define SC_ELEMENT_HIDDEN_LINE 81\n#define SCI_SETELEMENTCOLOUR 2753\n#define SCI_GETELEMENTCOLOUR 2754\n#define SCI_RESETELEMENTCOLOUR 2755\n#define SCI_GETELEMENTISSET 2756\n#define SCI_GETELEMENTALLOWSTRANSLUCENT 2757\n#define SCI_GETELEMENTBASECOLOUR 2758\n#define SCI_SETSELFORE 2067\n#define SCI_SETSELBACK 2068\n#define SCI_GETSELALPHA 2477\n#define SCI_SETSELALPHA 2478\n#define SCI_GETSELEOLFILLED 2479\n#define SCI_SETSELEOLFILLED 2480\n#define SC_LAYER_BASE 0\n#define SC_LAYER_UNDER_TEXT 1\n#define SC_LAYER_OVER_TEXT 2\n#define SCI_GETSELECTIONLAYER 2762\n#define SCI_SETSELECTIONLAYER 2763\n#define SCI_GETCARETLINELAYER 2764\n#define SCI_SETCARETLINELAYER 2765\n#define SCI_GETCARETLINEHIGHLIGHTSUBLINE 2773\n#define SCI_SETCARETLINEHIGHLIGHTSUBLINE 2774\n#define SCI_SETCARETFORE 2069\n#define SCI_ASSIGNCMDKEY 2070\n#define SCI_CLEARCMDKEY 2071\n#define SCI_CLEARALLCMDKEYS 2072\n#define SCI_SETSTYLINGEX 2073\n#define SCI_STYLESETVISIBLE 2074\n#define SCI_GETCARETPERIOD 2075\n#define SCI_SETCARETPERIOD 2076\n#define SCI_SETWORDCHARS 2077\n#define SCI_GETWORDCHARS 2646\n#define SCI_SETCHARACTERCATEGORYOPTIMIZATION 2720\n#define SCI_GETCHARACTERCATEGORYOPTIMIZATION 2721\n#define SCI_BEGINUNDOACTION 2078\n#define SCI_ENDUNDOACTION 2079\n#define SCI_GETUNDOSEQUENCE 2799\n#define SCI_GETUNDOACTIONS 2790\n#define SCI_SETUNDOSAVEPOINT 2791\n#define SCI_GETUNDOSAVEPOINT 2792\n#define SCI_SETUNDODETACH 2793\n#define SCI_GETUNDODETACH 2794\n#define SCI_SETUNDOTENTATIVE 2795\n#define SCI_GETUNDOTENTATIVE 2796\n#define SCI_SETUNDOCURRENT 2797\n#define SCI_GETUNDOCURRENT 2798\n#define SCI_PUSHUNDOACTIONTYPE 2800\n#define SCI_CHANGELASTUNDOACTIONTEXT 2801\n#define SCI_GETUNDOACTIONTYPE 2802\n#define SCI_GETUNDOACTIONPOSITION 2803\n#define SCI_GETUNDOACTIONTEXT 2804\n#define INDIC_PLAIN 0\n#define INDIC_SQUIGGLE 1\n#define INDIC_TT 2\n#define INDIC_DIAGONAL 3\n#define INDIC_STRIKE 4\n#define INDIC_HIDDEN 5\n#define INDIC_BOX 6\n#define INDIC_ROUNDBOX 7\n#define INDIC_STRAIGHTBOX 8\n#define INDIC_DASH 9\n#define INDIC_DOTS 10\n#define INDIC_SQUIGGLELOW 11\n#define INDIC_DOTBOX 12\n#define INDIC_SQUIGGLEPIXMAP 13\n#define INDIC_COMPOSITIONTHICK 14\n#define INDIC_COMPOSITIONTHIN 15\n#define INDIC_FULLBOX 16\n#define INDIC_TEXTFORE 17\n#define INDIC_POINT 18\n#define INDIC_POINTCHARACTER 19\n#define INDIC_GRADIENT 20\n#define INDIC_GRADIENTCENTRE 21\n#define INDIC_POINT_TOP 22\n#define INDIC_CONTAINER 8\n#define INDIC_IME 32\n#define INDIC_IME_MAX 35\n#define INDIC_MAX 35\n#define INDICATOR_CONTAINER 8\n#define INDICATOR_IME 32\n#define INDICATOR_IME_MAX 35\n#define INDICATOR_HISTORY_REVERTED_TO_ORIGIN_INSERTION 36\n#define INDICATOR_HISTORY_REVERTED_TO_ORIGIN_DELETION 37\n#define INDICATOR_HISTORY_SAVED_INSERTION 38\n#define INDICATOR_HISTORY_SAVED_DELETION 39\n#define INDICATOR_HISTORY_MODIFIED_INSERTION 40\n#define INDICATOR_HISTORY_MODIFIED_DELETION 41\n#define INDICATOR_HISTORY_REVERTED_TO_MODIFIED_INSERTION 42\n#define INDICATOR_HISTORY_REVERTED_TO_MODIFIED_DELETION 43\n#define INDICATOR_MAX 43\n#define SCI_INDICSETSTYLE 2080\n#define SCI_INDICGETSTYLE 2081\n#define SCI_INDICSETFORE 2082\n#define SCI_INDICGETFORE 2083\n#define SCI_INDICSETUNDER 2510\n#define SCI_INDICGETUNDER 2511\n#define SCI_INDICSETHOVERSTYLE 2680\n#define SCI_INDICGETHOVERSTYLE 2681\n#define SCI_INDICSETHOVERFORE 2682\n#define SCI_INDICGETHOVERFORE 2683\n#define SC_INDICVALUEBIT 0x1000000\n#define SC_INDICVALUEMASK 0xFFFFFF\n#define SC_INDICFLAG_NONE 0\n#define SC_INDICFLAG_VALUEFORE 1\n#define SCI_INDICSETFLAGS 2684\n#define SCI_INDICGETFLAGS 2685\n#define SCI_INDICSETSTROKEWIDTH 2751\n#define SCI_INDICGETSTROKEWIDTH 2752\n#define SCI_SETWHITESPACEFORE 2084\n#define SCI_SETWHITESPACEBACK 2085\n#define SCI_SETWHITESPACESIZE 2086\n#define SCI_GETWHITESPACESIZE 2087\n#define SCI_SETLINESTATE 2092\n#define SCI_GETLINESTATE 2093\n#define SCI_GETMAXLINESTATE 2094\n#define SCI_GETCARETLINEVISIBLE 2095\n#define SCI_SETCARETLINEVISIBLE 2096\n#define SCI_GETCARETLINEBACK 2097\n#define SCI_SETCARETLINEBACK 2098\n#define SCI_GETCARETLINEFRAME 2704\n#define SCI_SETCARETLINEFRAME 2705\n#define SCI_STYLESETCHANGEABLE 2099\n#define SCI_AUTOCSHOW 2100\n#define SCI_AUTOCCANCEL 2101\n#define SCI_AUTOCACTIVE 2102\n#define SCI_AUTOCPOSSTART 2103\n#define SCI_AUTOCCOMPLETE 2104\n#define SCI_AUTOCSTOPS 2105\n#define SCI_AUTOCSETSEPARATOR 2106\n#define SCI_AUTOCGETSEPARATOR 2107\n#define SCI_AUTOCSELECT 2108\n#define SCI_AUTOCSETCANCELATSTART 2110\n#define SCI_AUTOCGETCANCELATSTART 2111\n#define SCI_AUTOCSETFILLUPS 2112\n#define SCI_AUTOCSETCHOOSESINGLE 2113\n#define SCI_AUTOCGETCHOOSESINGLE 2114\n#define SCI_AUTOCSETIGNORECASE 2115\n#define SCI_AUTOCGETIGNORECASE 2116\n#define SCI_USERLISTSHOW 2117\n#define SCI_AUTOCSETAUTOHIDE 2118\n#define SCI_AUTOCGETAUTOHIDE 2119\n#define SC_AUTOCOMPLETE_NORMAL 0\n#define SC_AUTOCOMPLETE_FIXED_SIZE 1\n#define SC_AUTOCOMPLETE_SELECT_FIRST_ITEM 2\n#define SCI_AUTOCSETOPTIONS 2638\n#define SCI_AUTOCGETOPTIONS 2639\n#define SCI_AUTOCSETDROPRESTOFWORD 2270\n#define SCI_AUTOCGETDROPRESTOFWORD 2271\n#define SCI_REGISTERIMAGE 2405\n#define SCI_CLEARREGISTEREDIMAGES 2408\n#define SCI_AUTOCGETTYPESEPARATOR 2285\n#define SCI_AUTOCSETTYPESEPARATOR 2286\n#define SCI_AUTOCSETMAXWIDTH 2208\n#define SCI_AUTOCGETMAXWIDTH 2209\n#define SCI_AUTOCSETMAXHEIGHT 2210\n#define SCI_AUTOCGETMAXHEIGHT 2211\n#define SCI_AUTOCSETSTYLE 2109\n#define SCI_AUTOCGETSTYLE 2120\n#define SCI_AUTOCSETIMAGESCALE 2815\n#define SCI_AUTOCGETIMAGESCALE 2816\n#define SCI_SETINDENT 2122\n#define SCI_GETINDENT 2123\n#define SCI_SETUSETABS 2124\n#define SCI_GETUSETABS 2125\n#define SCI_SETLINEINDENTATION 2126\n#define SCI_GETLINEINDENTATION 2127\n#define SCI_GETLINEINDENTPOSITION 2128\n#define SCI_GETCOLUMN 2129\n#define SCI_COUNTCHARACTERS 2633\n#define SCI_COUNTCODEUNITS 2715\n#define SCI_SETHSCROLLBAR 2130\n#define SCI_GETHSCROLLBAR 2131\n#define SC_IV_NONE 0\n#define SC_IV_REAL 1\n#define SC_IV_LOOKFORWARD 2\n#define SC_IV_LOOKBOTH 3\n#define SCI_SETINDENTATIONGUIDES 2132\n#define SCI_GETINDENTATIONGUIDES 2133\n#define SCI_SETHIGHLIGHTGUIDE 2134\n#define SCI_GETHIGHLIGHTGUIDE 2135\n#define SCI_GETLINEENDPOSITION 2136\n#define SCI_GETCODEPAGE 2137\n#define SCI_GETCARETFORE 2138\n#define SCI_GETREADONLY 2140\n#define SCI_SETCURRENTPOS 2141\n#define SCI_SETSELECTIONSTART 2142\n#define SCI_GETSELECTIONSTART 2143\n#define SCI_SETSELECTIONEND 2144\n#define SCI_GETSELECTIONEND 2145\n#define SCI_SETEMPTYSELECTION 2556\n#define SCI_SETPRINTMAGNIFICATION 2146\n#define SCI_GETPRINTMAGNIFICATION 2147\n#define SC_PRINT_NORMAL 0\n#define SC_PRINT_INVERTLIGHT 1\n#define SC_PRINT_BLACKONWHITE 2\n#define SC_PRINT_COLOURONWHITE 3\n#define SC_PRINT_COLOURONWHITEDEFAULTBG 4\n#define SC_PRINT_SCREENCOLOURS 5\n#define SCI_SETPRINTCOLOURMODE 2148\n#define SCI_GETPRINTCOLOURMODE 2149\n#define SCFIND_NONE 0x0\n#define SCFIND_WHOLEWORD 0x2\n#define SCFIND_MATCHCASE 0x4\n#define SCFIND_WORDSTART 0x00100000\n#define SCFIND_REGEXP 0x00200000\n#define SCFIND_POSIX 0x00400000\n#define SCFIND_CXX11REGEX 0x00800000\n#define SCI_FINDTEXT 2150\n#define SCI_FINDTEXTFULL 2196\n#define SCI_FORMATRANGE 2151\n#define SCI_FORMATRANGEFULL 2777\n#define SC_CHANGE_HISTORY_DISABLED 0\n#define SC_CHANGE_HISTORY_ENABLED 1\n#define SC_CHANGE_HISTORY_MARKERS 2\n#define SC_CHANGE_HISTORY_INDICATORS 4\n#define SCI_SETCHANGEHISTORY 2780\n#define SCI_GETCHANGEHISTORY 2781\n#define SC_UNDO_SELECTION_HISTORY_DISABLED 0\n#define SC_UNDO_SELECTION_HISTORY_ENABLED 1\n#define SC_UNDO_SELECTION_HISTORY_SCROLL 2\n#define SCI_SETUNDOSELECTIONHISTORY 2782\n#define SCI_GETUNDOSELECTIONHISTORY 2783\n#define SCI_SETSELECTIONSERIALIZED 2784\n#define SCI_GETSELECTIONSERIALIZED 2785\n#define SCI_GETFIRSTVISIBLELINE 2152\n#define SCI_GETLINE 2153\n#define SCI_GETLINECOUNT 2154\n#define SCI_ALLOCATELINES 2089\n#define SCI_SETMARGINLEFT 2155\n#define SCI_GETMARGINLEFT 2156\n#define SCI_SETMARGINRIGHT 2157\n#define SCI_GETMARGINRIGHT 2158\n#define SCI_GETMODIFY 2159\n#define SCI_SETSEL 2160\n#define SCI_GETSELTEXT 2161\n#define SCI_GETTEXTRANGE 2162\n#define SCI_GETTEXTRANGEFULL 2039\n#define SCI_HIDESELECTION 2163\n#define SCI_GETSELECTIONHIDDEN 2088\n#define SCI_POINTXFROMPOSITION 2164\n#define SCI_POINTYFROMPOSITION 2165\n#define SCI_LINEFROMPOSITION 2166\n#define SCI_POSITIONFROMLINE 2167\n#define SCI_LINESCROLL 2168\n#define SCI_SCROLLVERTICAL 2817\n#define SCI_SCROLLCARET 2169\n#define SCI_SCROLLRANGE 2569\n#define SCI_REPLACESEL 2170\n#define SCI_SETREADONLY 2171\n#define SCI_NULL 2172\n#define SCI_CANPASTE 2173\n#define SCI_CANUNDO 2174\n#define SCI_EMPTYUNDOBUFFER 2175\n#define SCI_UNDO 2176\n#define SCI_CUT 2177\n#define SCI_COPY 2178\n#define SCI_PASTE 2179\n#define SCI_CLEAR 2180\n#define SCI_SETTEXT 2181\n#define SCI_GETTEXT 2182\n#define SCI_GETTEXTLENGTH 2183\n#define SCI_GETDIRECTFUNCTION 2184\n#define SCI_GETDIRECTSTATUSFUNCTION 2772\n#define SCI_GETDIRECTPOINTER 2185\n#define SCI_SETOVERTYPE 2186\n#define SCI_GETOVERTYPE 2187\n#define SCI_SETCARETWIDTH 2188\n#define SCI_GETCARETWIDTH 2189\n#define SCI_SETTARGETSTART 2190\n#define SCI_GETTARGETSTART 2191\n#define SCI_SETTARGETSTARTVIRTUALSPACE 2728\n#define SCI_GETTARGETSTARTVIRTUALSPACE 2729\n#define SCI_SETTARGETEND 2192\n#define SCI_GETTARGETEND 2193\n#define SCI_SETTARGETENDVIRTUALSPACE 2730\n#define SCI_GETTARGETENDVIRTUALSPACE 2731\n#define SCI_SETTARGETRANGE 2686\n#define SCI_GETTARGETTEXT 2687\n#define SCI_TARGETFROMSELECTION 2287\n#define SCI_TARGETWHOLEDOCUMENT 2690\n#define SCI_REPLACETARGET 2194\n#define SCI_REPLACETARGETRE 2195\n#define SCI_REPLACETARGETMINIMAL 2779\n#define SCI_SEARCHINTARGET 2197\n#define SCI_SETSEARCHFLAGS 2198\n#define SCI_GETSEARCHFLAGS 2199\n#define SCI_CALLTIPSHOW 2200\n#define SCI_CALLTIPCANCEL 2201\n#define SCI_CALLTIPACTIVE 2202\n#define SCI_CALLTIPPOSSTART 2203\n#define SCI_CALLTIPSETPOSSTART 2214\n#define SCI_CALLTIPSETHLT 2204\n#define SCI_CALLTIPSETBACK 2205\n#define SCI_CALLTIPSETFORE 2206\n#define SCI_CALLTIPSETFOREHLT 2207\n#define SCI_CALLTIPUSESTYLE 2212\n#define SCI_CALLTIPSETPOSITION 2213\n#define SCI_VISIBLEFROMDOCLINE 2220\n#define SCI_DOCLINEFROMVISIBLE 2221\n#define SCI_WRAPCOUNT 2235\n#define SC_FOLDLEVELNONE 0x0\n#define SC_FOLDLEVELBASE 0x400\n#define SC_FOLDLEVELWHITEFLAG 0x1000\n#define SC_FOLDLEVELHEADERFLAG 0x2000\n#define SC_FOLDLEVELNUMBERMASK 0x0FFF\n#define SCI_SETFOLDLEVEL 2222\n#define SCI_GETFOLDLEVEL 2223\n#define SCI_GETLASTCHILD 2224\n#define SCI_GETFOLDPARENT 2225\n#define SCI_SHOWLINES 2226\n#define SCI_HIDELINES 2227\n#define SCI_GETLINEVISIBLE 2228\n#define SCI_GETALLLINESVISIBLE 2236\n#define SCI_SETFOLDEXPANDED 2229\n#define SCI_GETFOLDEXPANDED 2230\n#define SCI_TOGGLEFOLD 2231\n#define SCI_TOGGLEFOLDSHOWTEXT 2700\n#define SC_FOLDDISPLAYTEXT_HIDDEN 0\n#define SC_FOLDDISPLAYTEXT_STANDARD 1\n#define SC_FOLDDISPLAYTEXT_BOXED 2\n#define SCI_FOLDDISPLAYTEXTSETSTYLE 2701\n#define SCI_FOLDDISPLAYTEXTGETSTYLE 2707\n#define SCI_SETDEFAULTFOLDDISPLAYTEXT 2722\n#define SCI_GETDEFAULTFOLDDISPLAYTEXT 2723\n#define SC_FOLDACTION_CONTRACT 0\n#define SC_FOLDACTION_EXPAND 1\n#define SC_FOLDACTION_TOGGLE 2\n#define SC_FOLDACTION_CONTRACT_EVERY_LEVEL 4\n#define SCI_FOLDLINE 2237\n#define SCI_FOLDCHILDREN 2238\n#define SCI_EXPANDCHILDREN 2239\n#define SCI_FOLDALL 2662\n#define SCI_ENSUREVISIBLE 2232\n#define SC_AUTOMATICFOLD_NONE 0x0000\n#define SC_AUTOMATICFOLD_SHOW 0x0001\n#define SC_AUTOMATICFOLD_CLICK 0x0002\n#define SC_AUTOMATICFOLD_CHANGE 0x0004\n#define SCI_SETAUTOMATICFOLD 2663\n#define SCI_GETAUTOMATICFOLD 2664\n#define SC_FOLDFLAG_NONE 0x0000\n#define SC_FOLDFLAG_LINEBEFORE_EXPANDED 0x0002\n#define SC_FOLDFLAG_LINEBEFORE_CONTRACTED 0x0004\n#define SC_FOLDFLAG_LINEAFTER_EXPANDED 0x0008\n#define SC_FOLDFLAG_LINEAFTER_CONTRACTED 0x0010\n#define SC_FOLDFLAG_LEVELNUMBERS 0x0040\n#define SC_FOLDFLAG_LINESTATE 0x0080\n#define SCI_SETFOLDFLAGS 2233\n#define SCI_ENSUREVISIBLEENFORCEPOLICY 2234\n#define SCI_SETTABINDENTS 2260\n#define SCI_GETTABINDENTS 2261\n#define SCI_SETBACKSPACEUNINDENTS 2262\n#define SCI_GETBACKSPACEUNINDENTS 2263\n#define SC_TIME_FOREVER 10000000\n#define SCI_SETMOUSEDWELLTIME 2264\n#define SCI_GETMOUSEDWELLTIME 2265\n#define SCI_WORDSTARTPOSITION 2266\n#define SCI_WORDENDPOSITION 2267\n#define SCI_ISRANGEWORD 2691\n#define SC_IDLESTYLING_NONE 0\n#define SC_IDLESTYLING_TOVISIBLE 1\n#define SC_IDLESTYLING_AFTERVISIBLE 2\n#define SC_IDLESTYLING_ALL 3\n#define SCI_SETIDLESTYLING 2692\n#define SCI_GETIDLESTYLING 2693\n#define SC_WRAP_NONE 0\n#define SC_WRAP_WORD 1\n#define SC_WRAP_CHAR 2\n#define SC_WRAP_WHITESPACE 3\n#define SCI_SETWRAPMODE 2268\n#define SCI_GETWRAPMODE 2269\n#define SC_WRAPVISUALFLAG_NONE 0x0000\n#define SC_WRAPVISUALFLAG_END 0x0001\n#define SC_WRAPVISUALFLAG_START 0x0002\n#define SC_WRAPVISUALFLAG_MARGIN 0x0004\n#define SCI_SETWRAPVISUALFLAGS 2460\n#define SCI_GETWRAPVISUALFLAGS 2461\n#define SC_WRAPVISUALFLAGLOC_DEFAULT 0x0000\n#define SC_WRAPVISUALFLAGLOC_END_BY_TEXT 0x0001\n#define SC_WRAPVISUALFLAGLOC_START_BY_TEXT 0x0002\n#define SCI_SETWRAPVISUALFLAGSLOCATION 2462\n#define SCI_GETWRAPVISUALFLAGSLOCATION 2463\n#define SCI_SETWRAPSTARTINDENT 2464\n#define SCI_GETWRAPSTARTINDENT 2465\n#define SC_WRAPINDENT_FIXED 0\n#define SC_WRAPINDENT_SAME 1\n#define SC_WRAPINDENT_INDENT 2\n#define SC_WRAPINDENT_DEEPINDENT 3\n#define SCI_SETWRAPINDENTMODE 2472\n#define SCI_GETWRAPINDENTMODE 2473\n#define SC_CACHE_NONE 0\n#define SC_CACHE_CARET 1\n#define SC_CACHE_PAGE 2\n#define SC_CACHE_DOCUMENT 3\n#define SCI_SETLAYOUTCACHE 2272\n#define SCI_GETLAYOUTCACHE 2273\n#define SCI_SETSCROLLWIDTH 2274\n#define SCI_GETSCROLLWIDTH 2275\n#define SCI_SETSCROLLWIDTHTRACKING 2516\n#define SCI_GETSCROLLWIDTHTRACKING 2517\n#define SCI_TEXTWIDTH 2276\n#define SCI_SETENDATLASTLINE 2277\n#define SCI_GETENDATLASTLINE 2278\n#define SCI_TEXTHEIGHT 2279\n#define SCI_SETVSCROLLBAR 2280\n#define SCI_GETVSCROLLBAR 2281\n#define SCI_APPENDTEXT 2282\n#define SC_PHASES_ONE 0\n#define SC_PHASES_TWO 1\n#define SC_PHASES_MULTIPLE 2\n#define SCI_GETPHASESDRAW 2673\n#define SCI_SETPHASESDRAW 2674\n#define SC_EFF_QUALITY_MASK 0xF\n#define SC_EFF_QUALITY_DEFAULT 0\n#define SC_EFF_QUALITY_NON_ANTIALIASED 1\n#define SC_EFF_QUALITY_ANTIALIASED 2\n#define SC_EFF_QUALITY_LCD_OPTIMIZED 3\n#define SCI_SETFONTQUALITY 2611\n#define SCI_GETFONTQUALITY 2612\n#define SCI_SETFIRSTVISIBLELINE 2613\n#define SC_MULTIPASTE_ONCE 0\n#define SC_MULTIPASTE_EACH 1\n#define SCI_SETMULTIPASTE 2614\n#define SCI_GETMULTIPASTE 2615\n#define SCI_GETTAG 2616\n#define SCI_LINESJOIN 2288\n#define SCI_LINESSPLIT 2289\n#define SCI_SETFOLDMARGINCOLOUR 2290\n#define SCI_SETFOLDMARGINHICOLOUR 2291\n#define SC_ACCESSIBILITY_DISABLED 0\n#define SC_ACCESSIBILITY_ENABLED 1\n#define SCI_SETACCESSIBILITY 2702\n#define SCI_GETACCESSIBILITY 2703\n#define SCI_LINEDOWN 2300\n#define SCI_LINEDOWNEXTEND 2301\n#define SCI_LINEUP 2302\n#define SCI_LINEUPEXTEND 2303\n#define SCI_CHARLEFT 2304\n#define SCI_CHARLEFTEXTEND 2305\n#define SCI_CHARRIGHT 2306\n#define SCI_CHARRIGHTEXTEND 2307\n#define SCI_WORDLEFT 2308\n#define SCI_WORDLEFTEXTEND 2309\n#define SCI_WORDRIGHT 2310\n#define SCI_WORDRIGHTEXTEND 2311\n#define SCI_HOME 2312\n#define SCI_HOMEEXTEND 2313\n#define SCI_LINEEND 2314\n#define SCI_LINEENDEXTEND 2315\n#define SCI_DOCUMENTSTART 2316\n#define SCI_DOCUMENTSTARTEXTEND 2317\n#define SCI_DOCUMENTEND 2318\n#define SCI_DOCUMENTENDEXTEND 2319\n#define SCI_PAGEUP 2320\n#define SCI_PAGEUPEXTEND 2321\n#define SCI_PAGEDOWN 2322\n#define SCI_PAGEDOWNEXTEND 2323\n#define SCI_EDITTOGGLEOVERTYPE 2324\n#define SCI_CANCEL 2325\n#define SCI_DELETEBACK 2326\n#define SCI_TAB 2327\n#define SCI_LINEINDENT 2813\n#define SCI_BACKTAB 2328\n#define SCI_LINEDEDENT 2814\n#define SCI_NEWLINE 2329\n#define SCI_FORMFEED 2330\n#define SCI_VCHOME 2331\n#define SCI_VCHOMEEXTEND 2332\n#define SCI_ZOOMIN 2333\n#define SCI_ZOOMOUT 2334\n#define SCI_DELWORDLEFT 2335\n#define SCI_DELWORDRIGHT 2336\n#define SCI_DELWORDRIGHTEND 2518\n#define SCI_LINECUT 2337\n#define SCI_LINEDELETE 2338\n#define SCI_LINETRANSPOSE 2339\n#define SCI_LINEREVERSE 2354\n#define SCI_LINEDUPLICATE 2404\n#define SCI_LOWERCASE 2340\n#define SCI_UPPERCASE 2341\n#define SCI_LINESCROLLDOWN 2342\n#define SCI_LINESCROLLUP 2343\n#define SCI_DELETEBACKNOTLINE 2344\n#define SCI_HOMEDISPLAY 2345\n#define SCI_HOMEDISPLAYEXTEND 2346\n#define SCI_LINEENDDISPLAY 2347\n#define SCI_LINEENDDISPLAYEXTEND 2348\n#define SCI_HOMEWRAP 2349\n#define SCI_HOMEWRAPEXTEND 2450\n#define SCI_LINEENDWRAP 2451\n#define SCI_LINEENDWRAPEXTEND 2452\n#define SCI_VCHOMEWRAP 2453\n#define SCI_VCHOMEWRAPEXTEND 2454\n#define SCI_LINECOPY 2455\n#define SCI_MOVECARETINSIDEVIEW 2401\n#define SCI_LINELENGTH 2350\n#define SCI_BRACEHIGHLIGHT 2351\n#define SCI_BRACEHIGHLIGHTINDICATOR 2498\n#define SCI_BRACEBADLIGHT 2352\n#define SCI_BRACEBADLIGHTINDICATOR 2499\n#define SCI_BRACEMATCH 2353\n#define SCI_BRACEMATCHNEXT 2369\n#define SCI_GETVIEWEOL 2355\n#define SCI_SETVIEWEOL 2356\n#define SCI_GETDOCPOINTER 2357\n#define SCI_SETDOCPOINTER 2358\n#define SCI_SETMODEVENTMASK 2359\n#define EDGE_NONE 0\n#define EDGE_LINE 1\n#define EDGE_BACKGROUND 2\n#define EDGE_MULTILINE 3\n#define SCI_GETEDGECOLUMN 2360\n#define SCI_SETEDGECOLUMN 2361\n#define SCI_GETEDGEMODE 2362\n#define SCI_SETEDGEMODE 2363\n#define SCI_GETEDGECOLOUR 2364\n#define SCI_SETEDGECOLOUR 2365\n#define SCI_MULTIEDGEADDLINE 2694\n#define SCI_MULTIEDGECLEARALL 2695\n#define SCI_GETMULTIEDGECOLUMN 2749\n#define SCI_SEARCHANCHOR 2366\n#define SCI_SEARCHNEXT 2367\n#define SCI_SEARCHPREV 2368\n#define SCI_LINESONSCREEN 2370\n#define SC_POPUP_NEVER 0\n#define SC_POPUP_ALL 1\n#define SC_POPUP_TEXT 2\n#define SCI_USEPOPUP 2371\n#define SCI_SELECTIONISRECTANGLE 2372\n#define SCI_SETZOOM 2373\n#define SCI_GETZOOM 2374\n#define SC_DOCUMENTOPTION_DEFAULT 0\n#define SC_DOCUMENTOPTION_STYLES_NONE 0x1\n#define SC_DOCUMENTOPTION_TEXT_LARGE 0x100\n#define SCI_CREATEDOCUMENT 2375\n#define SCI_ADDREFDOCUMENT 2376\n#define SCI_RELEASEDOCUMENT 2377\n#define SCI_GETDOCUMENTOPTIONS 2379\n#define SCI_GETMODEVENTMASK 2378\n#define SCI_SETCOMMANDEVENTS 2717\n#define SCI_GETCOMMANDEVENTS 2718\n#define SCI_SETFOCUS 2380\n#define SCI_GETFOCUS 2381\n#define SC_STATUS_OK 0\n#define SC_STATUS_FAILURE 1\n#define SC_STATUS_BADALLOC 2\n#define SC_STATUS_WARN_START 1000\n#define SC_STATUS_WARN_REGEX 1001\n#define SCI_SETSTATUS 2382\n#define SCI_GETSTATUS 2383\n#define SCI_SETMOUSEDOWNCAPTURES 2384\n#define SCI_GETMOUSEDOWNCAPTURES 2385\n#define SCI_SETMOUSEWHEELCAPTURES 2696\n#define SCI_GETMOUSEWHEELCAPTURES 2697\n#define SCI_SETCURSOR 2386\n#define SCI_GETCURSOR 2387\n#define SCI_SETCONTROLCHARSYMBOL 2388\n#define SCI_GETCONTROLCHARSYMBOL 2389\n#define SCI_WORDPARTLEFT 2390\n#define SCI_WORDPARTLEFTEXTEND 2391\n#define SCI_WORDPARTRIGHT 2392\n#define SCI_WORDPARTRIGHTEXTEND 2393\n#define VISIBLE_SLOP 0x01\n#define VISIBLE_STRICT 0x04\n#define SCI_SETVISIBLEPOLICY 2394\n#define SCI_DELLINELEFT 2395\n#define SCI_DELLINERIGHT 2396\n#define SCI_SETXOFFSET 2397\n#define SCI_GETXOFFSET 2398\n#define SCI_CHOOSECARETX 2399\n#define SCI_GRABFOCUS 2400\n#define CARET_SLOP 0x01\n#define CARET_STRICT 0x04\n#define CARET_JUMPS 0x10\n#define CARET_EVEN 0x08\n#define SCI_SETXCARETPOLICY 2402\n#define SCI_SETYCARETPOLICY 2403\n#define SCI_SETPRINTWRAPMODE 2406\n#define SCI_GETPRINTWRAPMODE 2407\n#define SCI_SETHOTSPOTACTIVEFORE 2410\n#define SCI_GETHOTSPOTACTIVEFORE 2494\n#define SCI_SETHOTSPOTACTIVEBACK 2411\n#define SCI_GETHOTSPOTACTIVEBACK 2495\n#define SCI_SETHOTSPOTACTIVEUNDERLINE 2412\n#define SCI_GETHOTSPOTACTIVEUNDERLINE 2496\n#define SCI_SETHOTSPOTSINGLELINE 2421\n#define SCI_GETHOTSPOTSINGLELINE 2497\n#define SCI_PARADOWN 2413\n#define SCI_PARADOWNEXTEND 2414\n#define SCI_PARAUP 2415\n#define SCI_PARAUPEXTEND 2416\n#define SCI_POSITIONBEFORE 2417\n#define SCI_POSITIONAFTER 2418\n#define SCI_POSITIONRELATIVE 2670\n#define SCI_POSITIONRELATIVECODEUNITS 2716\n#define SCI_COPYRANGE 2419\n#define SCI_COPYTEXT 2420\n#define SC_SEL_STREAM 0\n#define SC_SEL_RECTANGLE 1\n#define SC_SEL_LINES 2\n#define SC_SEL_THIN 3\n#define SCI_SETSELECTIONMODE 2422\n#define SCI_CHANGESELECTIONMODE 2659\n#define SCI_GETSELECTIONMODE 2423\n#define SCI_SETMOVEEXTENDSSELECTION 2719\n#define SCI_GETMOVEEXTENDSSELECTION 2706\n#define SCI_GETLINESELSTARTPOSITION 2424\n#define SCI_GETLINESELENDPOSITION 2425\n#define SCI_LINEDOWNRECTEXTEND 2426\n#define SCI_LINEUPRECTEXTEND 2427\n#define SCI_CHARLEFTRECTEXTEND 2428\n#define SCI_CHARRIGHTRECTEXTEND 2429\n#define SCI_HOMERECTEXTEND 2430\n#define SCI_VCHOMERECTEXTEND 2431\n#define SCI_LINEENDRECTEXTEND 2432\n#define SCI_PAGEUPRECTEXTEND 2433\n#define SCI_PAGEDOWNRECTEXTEND 2434\n#define SCI_STUTTEREDPAGEUP 2435\n#define SCI_STUTTEREDPAGEUPEXTEND 2436\n#define SCI_STUTTEREDPAGEDOWN 2437\n#define SCI_STUTTEREDPAGEDOWNEXTEND 2438\n#define SCI_WORDLEFTEND 2439\n#define SCI_WORDLEFTENDEXTEND 2440\n#define SCI_WORDRIGHTEND 2441\n#define SCI_WORDRIGHTENDEXTEND 2442\n#define SCI_SETWHITESPACECHARS 2443\n#define SCI_GETWHITESPACECHARS 2647\n#define SCI_SETPUNCTUATIONCHARS 2648\n#define SCI_GETPUNCTUATIONCHARS 2649\n#define SCI_SETCHARSDEFAULT 2444\n#define SCI_AUTOCGETCURRENT 2445\n#define SCI_AUTOCGETCURRENTTEXT 2610\n#define SC_CASEINSENSITIVEBEHAVIOUR_RESPECTCASE 0\n#define SC_CASEINSENSITIVEBEHAVIOUR_IGNORECASE 1\n#define SCI_AUTOCSETCASEINSENSITIVEBEHAVIOUR 2634\n#define SCI_AUTOCGETCASEINSENSITIVEBEHAVIOUR 2635\n#define SC_MULTIAUTOC_ONCE 0\n#define SC_MULTIAUTOC_EACH 1\n#define SCI_AUTOCSETMULTI 2636\n#define SCI_AUTOCGETMULTI 2637\n#define SC_ORDER_PRESORTED 0\n#define SC_ORDER_PERFORMSORT 1\n#define SC_ORDER_CUSTOM 2\n#define SCI_AUTOCSETORDER 2660\n#define SCI_AUTOCGETORDER 2661\n#define SCI_ALLOCATE 2446\n#define SCI_TARGETASUTF8 2447\n#define SCI_SETLENGTHFORENCODE 2448\n#define SCI_ENCODEDFROMUTF8 2449\n#define SCI_FINDCOLUMN 2456\n#define SC_CARETSTICKY_OFF 0\n#define SC_CARETSTICKY_ON 1\n#define SC_CARETSTICKY_WHITESPACE 2\n#define SCI_GETCARETSTICKY 2457\n#define SCI_SETCARETSTICKY 2458\n#define SCI_TOGGLECARETSTICKY 2459\n#define SCI_SETPASTECONVERTENDINGS 2467\n#define SCI_GETPASTECONVERTENDINGS 2468\n#define SCI_REPLACERECTANGULAR 2771\n#define SCI_SELECTIONDUPLICATE 2469\n#define SCI_SETCARETLINEBACKALPHA 2470\n#define SCI_GETCARETLINEBACKALPHA 2471\n#define CARETSTYLE_INVISIBLE 0\n#define CARETSTYLE_LINE 1\n#define CARETSTYLE_BLOCK 2\n#define CARETSTYLE_OVERSTRIKE_BAR 0\n#define CARETSTYLE_OVERSTRIKE_BLOCK 0x10\n#define CARETSTYLE_CURSES 0x20\n#define CARETSTYLE_INS_MASK 0xF\n#define CARETSTYLE_BLOCK_AFTER 0x100\n#define SCI_SETCARETSTYLE 2512\n#define SCI_GETCARETSTYLE 2513\n#define SCI_SETINDICATORCURRENT 2500\n#define SCI_GETINDICATORCURRENT 2501\n#define SCI_SETINDICATORVALUE 2502\n#define SCI_GETINDICATORVALUE 2503\n#define SCI_INDICATORFILLRANGE 2504\n#define SCI_INDICATORCLEARRANGE 2505\n#define SCI_INDICATORALLONFOR 2506\n#define SCI_INDICATORVALUEAT 2507\n#define SCI_INDICATORSTART 2508\n#define SCI_INDICATOREND 2509\n#define SCI_SETPOSITIONCACHE 2514\n#define SCI_GETPOSITIONCACHE 2515\n#define SCI_SETLAYOUTTHREADS 2775\n#define SCI_GETLAYOUTTHREADS 2776\n#define SCI_COPYALLOWLINE 2519\n#define SCI_CUTALLOWLINE 2810\n#define SCI_SETCOPYSEPARATOR 2811\n#define SCI_GETCOPYSEPARATOR 2812\n#define SCI_GETCHARACTERPOINTER 2520\n#define SCI_GETRANGEPOINTER 2643\n#define SCI_GETGAPPOSITION 2644\n#define SCI_INDICSETALPHA 2523\n#define SCI_INDICGETALPHA 2524\n#define SCI_INDICSETOUTLINEALPHA 2558\n#define SCI_INDICGETOUTLINEALPHA 2559\n#define SCI_SETEXTRAASCENT 2525\n#define SCI_GETEXTRAASCENT 2526\n#define SCI_SETEXTRADESCENT 2527\n#define SCI_GETEXTRADESCENT 2528\n#define SCI_MARKERSYMBOLDEFINED 2529\n#define SCI_MARGINSETTEXT 2530\n#define SCI_MARGINGETTEXT 2531\n#define SCI_MARGINSETSTYLE 2532\n#define SCI_MARGINGETSTYLE 2533\n#define SCI_MARGINSETSTYLES 2534\n#define SCI_MARGINGETSTYLES 2535\n#define SCI_MARGINTEXTCLEARALL 2536\n#define SCI_MARGINSETSTYLEOFFSET 2537\n#define SCI_MARGINGETSTYLEOFFSET 2538\n#define SC_MARGINOPTION_NONE 0\n#define SC_MARGINOPTION_SUBLINESELECT 1\n#define SCI_SETMARGINOPTIONS 2539\n#define SCI_GETMARGINOPTIONS 2557\n#define SCI_ANNOTATIONSETTEXT 2540\n#define SCI_ANNOTATIONGETTEXT 2541\n#define SCI_ANNOTATIONSETSTYLE 2542\n#define SCI_ANNOTATIONGETSTYLE 2543\n#define SCI_ANNOTATIONSETSTYLES 2544\n#define SCI_ANNOTATIONGETSTYLES 2545\n#define SCI_ANNOTATIONGETLINES 2546\n#define SCI_ANNOTATIONCLEARALL 2547\n#define ANNOTATION_HIDDEN 0\n#define ANNOTATION_STANDARD 1\n#define ANNOTATION_BOXED 2\n#define ANNOTATION_INDENTED 3\n#define SCI_ANNOTATIONSETVISIBLE 2548\n#define SCI_ANNOTATIONGETVISIBLE 2549\n#define SCI_ANNOTATIONSETSTYLEOFFSET 2550\n#define SCI_ANNOTATIONGETSTYLEOFFSET 2551\n#define SCI_RELEASEALLEXTENDEDSTYLES 2552\n#define SCI_ALLOCATEEXTENDEDSTYLES 2553\n#define UNDO_NONE 0\n#define UNDO_MAY_COALESCE 1\n#define SCI_ADDUNDOACTION 2560\n#define SCI_CHARPOSITIONFROMPOINT 2561\n#define SCI_CHARPOSITIONFROMPOINTCLOSE 2562\n#define SCI_SETMOUSESELECTIONRECTANGULARSWITCH 2668\n#define SCI_GETMOUSESELECTIONRECTANGULARSWITCH 2669\n#define SCI_SETMULTIPLESELECTION 2563\n#define SCI_GETMULTIPLESELECTION 2564\n#define SCI_SETADDITIONALSELECTIONTYPING 2565\n#define SCI_GETADDITIONALSELECTIONTYPING 2566\n#define SCI_SETADDITIONALCARETSBLINK 2567\n#define SCI_GETADDITIONALCARETSBLINK 2568\n#define SCI_SETADDITIONALCARETSVISIBLE 2608\n#define SCI_GETADDITIONALCARETSVISIBLE 2609\n#define SCI_GETSELECTIONS 2570\n#define SCI_GETSELECTIONEMPTY 2650\n#define SCI_CLEARSELECTIONS 2571\n#define SCI_SETSELECTION 2572\n#define SCI_ADDSELECTION 2573\n#define SCI_SELECTIONFROMPOINT 2474\n#define SCI_DROPSELECTIONN 2671\n#define SCI_SETMAINSELECTION 2574\n#define SCI_GETMAINSELECTION 2575\n#define SCI_SETSELECTIONNCARET 2576\n#define SCI_GETSELECTIONNCARET 2577\n#define SCI_SETSELECTIONNANCHOR 2578\n#define SCI_GETSELECTIONNANCHOR 2579\n#define SCI_SETSELECTIONNCARETVIRTUALSPACE 2580\n#define SCI_GETSELECTIONNCARETVIRTUALSPACE 2581\n#define SCI_SETSELECTIONNANCHORVIRTUALSPACE 2582\n#define SCI_GETSELECTIONNANCHORVIRTUALSPACE 2583\n#define SCI_SETSELECTIONNSTART 2584\n#define SCI_GETSELECTIONNSTART 2585\n#define SCI_GETSELECTIONNSTARTVIRTUALSPACE 2726\n#define SCI_SETSELECTIONNEND 2586\n#define SCI_GETSELECTIONNENDVIRTUALSPACE 2727\n#define SCI_GETSELECTIONNEND 2587\n#define SCI_SETRECTANGULARSELECTIONCARET 2588\n#define SCI_GETRECTANGULARSELECTIONCARET 2589\n#define SCI_SETRECTANGULARSELECTIONANCHOR 2590\n#define SCI_GETRECTANGULARSELECTIONANCHOR 2591\n#define SCI_SETRECTANGULARSELECTIONCARETVIRTUALSPACE 2592\n#define SCI_GETRECTANGULARSELECTIONCARETVIRTUALSPACE 2593\n#define SCI_SETRECTANGULARSELECTIONANCHORVIRTUALSPACE 2594\n#define SCI_GETRECTANGULARSELECTIONANCHORVIRTUALSPACE 2595\n#define SCVS_NONE 0\n#define SCVS_RECTANGULARSELECTION 1\n#define SCVS_USERACCESSIBLE 2\n#define SCVS_NOWRAPLINESTART 4\n#define SCI_SETVIRTUALSPACEOPTIONS 2596\n#define SCI_GETVIRTUALSPACEOPTIONS 2597\n#define SCI_SETRECTANGULARSELECTIONMODIFIER 2598\n#define SCI_GETRECTANGULARSELECTIONMODIFIER 2599\n#define SCI_SETADDITIONALSELFORE 2600\n#define SCI_SETADDITIONALSELBACK 2601\n#define SCI_SETADDITIONALSELALPHA 2602\n#define SCI_GETADDITIONALSELALPHA 2603\n#define SCI_SETADDITIONALCARETFORE 2604\n#define SCI_GETADDITIONALCARETFORE 2605\n#define SCI_ROTATESELECTION 2606\n#define SCI_SWAPMAINANCHORCARET 2607\n#define SCI_MULTIPLESELECTADDNEXT 2688\n#define SCI_MULTIPLESELECTADDEACH 2689\n#define SCI_CHANGELEXERSTATE 2617\n#define SCI_CONTRACTEDFOLDNEXT 2618\n#define SCI_VERTICALCENTRECARET 2619\n#define SCI_MOVESELECTEDLINESUP 2620\n#define SCI_MOVESELECTEDLINESDOWN 2621\n#define SCI_SETIDENTIFIER 2622\n#define SCI_GETIDENTIFIER 2623\n#define SCI_RGBAIMAGESETWIDTH 2624\n#define SCI_RGBAIMAGESETHEIGHT 2625\n#define SCI_RGBAIMAGESETSCALE 2651\n#define SCI_MARKERDEFINERGBAIMAGE 2626\n#define SCI_REGISTERRGBAIMAGE 2627\n#define SCI_SCROLLTOSTART 2628\n#define SCI_SCROLLTOEND 2629\n#define SC_TECHNOLOGY_DEFAULT 0\n#define SC_TECHNOLOGY_DIRECTWRITE 1\n#define SC_TECHNOLOGY_DIRECTWRITERETAIN 2\n#define SC_TECHNOLOGY_DIRECTWRITEDC 3\n#define SC_TECHNOLOGY_DIRECT_WRITE_1 4\n#define SCI_SETTECHNOLOGY 2630\n#define SCI_GETTECHNOLOGY 2631\n#define SCI_CREATELOADER 2632\n#define SCI_FINDINDICATORSHOW 2640\n#define SCI_FINDINDICATORFLASH 2641\n#define SCI_FINDINDICATORHIDE 2642\n#define SCI_VCHOMEDISPLAY 2652\n#define SCI_VCHOMEDISPLAYEXTEND 2653\n#define SCI_GETCARETLINEVISIBLEALWAYS 2654\n#define SCI_SETCARETLINEVISIBLEALWAYS 2655\n#define SC_LINE_END_TYPE_DEFAULT 0\n#define SC_LINE_END_TYPE_UNICODE 1\n#define SCI_SETLINEENDTYPESALLOWED 2656\n#define SCI_GETLINEENDTYPESALLOWED 2657\n#define SCI_GETLINEENDTYPESACTIVE 2658\n#define SCI_SETREPRESENTATION 2665\n#define SCI_GETREPRESENTATION 2666\n#define SCI_CLEARREPRESENTATION 2667\n#define SCI_CLEARALLREPRESENTATIONS 2770\n#define SC_REPRESENTATION_PLAIN 0\n#define SC_REPRESENTATION_BLOB 1\n#define SC_REPRESENTATION_COLOUR 0x10\n#define SCI_SETREPRESENTATIONAPPEARANCE 2766\n#define SCI_GETREPRESENTATIONAPPEARANCE 2767\n#define SCI_SETREPRESENTATIONCOLOUR 2768\n#define SCI_GETREPRESENTATIONCOLOUR 2769\n#define SCI_EOLANNOTATIONSETTEXT 2740\n#define SCI_EOLANNOTATIONGETTEXT 2741\n#define SCI_EOLANNOTATIONSETSTYLE 2742\n#define SCI_EOLANNOTATIONGETSTYLE 2743\n#define SCI_EOLANNOTATIONCLEARALL 2744\n#define EOLANNOTATION_HIDDEN 0x0\n#define EOLANNOTATION_STANDARD 0x1\n#define EOLANNOTATION_BOXED 0x2\n#define EOLANNOTATION_STADIUM 0x100\n#define EOLANNOTATION_FLAT_CIRCLE 0x101\n#define EOLANNOTATION_ANGLE_CIRCLE 0x102\n#define EOLANNOTATION_CIRCLE_FLAT 0x110\n#define EOLANNOTATION_FLATS 0x111\n#define EOLANNOTATION_ANGLE_FLAT 0x112\n#define EOLANNOTATION_CIRCLE_ANGLE 0x120\n#define EOLANNOTATION_FLAT_ANGLE 0x121\n#define EOLANNOTATION_ANGLES 0x122\n#define SCI_EOLANNOTATIONSETVISIBLE 2745\n#define SCI_EOLANNOTATIONGETVISIBLE 2746\n#define SCI_EOLANNOTATIONSETSTYLEOFFSET 2747\n#define SCI_EOLANNOTATIONGETSTYLEOFFSET 2748\n#define SC_SUPPORTS_LINE_DRAWS_FINAL 0\n#define SC_SUPPORTS_PIXEL_DIVISIONS 1\n#define SC_SUPPORTS_FRACTIONAL_STROKE_WIDTH 2\n#define SC_SUPPORTS_TRANSLUCENT_STROKE 3\n#define SC_SUPPORTS_PIXEL_MODIFICATION 4\n#define SC_SUPPORTS_THREAD_SAFE_MEASURE_WIDTHS 5\n#define SCI_SUPPORTSFEATURE 2750\n#define SC_LINECHARACTERINDEX_NONE 0\n#define SC_LINECHARACTERINDEX_UTF32 1\n#define SC_LINECHARACTERINDEX_UTF16 2\n#define SCI_GETLINECHARACTERINDEX 2710\n#define SCI_ALLOCATELINECHARACTERINDEX 2711\n#define SCI_RELEASELINECHARACTERINDEX 2712\n#define SCI_LINEFROMINDEXPOSITION 2713\n#define SCI_INDEXPOSITIONFROMLINE 2714\n#define SCI_STARTRECORD 3001\n#define SCI_STOPRECORD 3002\n#define SCI_GETLEXER 4002\n#define SCI_COLOURISE 4003\n#define SCI_SETPROPERTY 4004\n#define KEYWORDSET_MAX 8\n#define SCI_SETKEYWORDS 4005\n#define SCI_GETPROPERTY 4008\n#define SCI_GETPROPERTYEXPANDED 4009\n#define SCI_GETPROPERTYINT 4010\n#define SCI_GETLEXERLANGUAGE 4012\n#define SCI_PRIVATELEXERCALL 4013\n#define SCI_PROPERTYNAMES 4014\n#define SC_TYPE_BOOLEAN 0\n#define SC_TYPE_INTEGER 1\n#define SC_TYPE_STRING 2\n#define SCI_PROPERTYTYPE 4015\n#define SCI_DESCRIBEPROPERTY 4016\n#define SCI_DESCRIBEKEYWORDSETS 4017\n#define SCI_GETLINEENDTYPESSUPPORTED 4018\n#define SCI_ALLOCATESUBSTYLES 4020\n#define SCI_GETSUBSTYLESSTART 4021\n#define SCI_GETSUBSTYLESLENGTH 4022\n#define SCI_GETSTYLEFROMSUBSTYLE 4027\n#define SCI_GETPRIMARYSTYLEFROMSTYLE 4028\n#define SCI_FREESUBSTYLES 4023\n#define SCI_SETIDENTIFIERS 4024\n#define SCI_DISTANCETOSECONDARYSTYLES 4025\n#define SCI_GETSUBSTYLEBASES 4026\n#define SCI_GETNAMEDSTYLES 4029\n#define SCI_NAMEOFSTYLE 4030\n#define SCI_TAGSOFSTYLE 4031\n#define SCI_DESCRIPTIONOFSTYLE 4032\n#define SCI_SETILEXER 4033\n#define SC_MOD_NONE 0x0\n#define SC_MOD_INSERTTEXT 0x1\n#define SC_MOD_DELETETEXT 0x2\n#define SC_MOD_CHANGESTYLE 0x4\n#define SC_MOD_CHANGEFOLD 0x8\n#define SC_PERFORMED_USER 0x10\n#define SC_PERFORMED_UNDO 0x20\n#define SC_PERFORMED_REDO 0x40\n#define SC_MULTISTEPUNDOREDO 0x80\n#define SC_LASTSTEPINUNDOREDO 0x100\n#define SC_MOD_CHANGEMARKER 0x200\n#define SC_MOD_BEFOREINSERT 0x400\n#define SC_MOD_BEFOREDELETE 0x800\n#define SC_MULTILINEUNDOREDO 0x1000\n#define SC_STARTACTION 0x2000\n#define SC_MOD_CHANGEINDICATOR 0x4000\n#define SC_MOD_CHANGELINESTATE 0x8000\n#define SC_MOD_CHANGEMARGIN 0x10000\n#define SC_MOD_CHANGEANNOTATION 0x20000\n#define SC_MOD_CONTAINER 0x40000\n#define SC_MOD_LEXERSTATE 0x80000\n#define SC_MOD_INSERTCHECK 0x100000\n#define SC_MOD_CHANGETABSTOPS 0x200000\n#define SC_MOD_CHANGEEOLANNOTATION 0x400000\n#define SC_MODEVENTMASKALL 0x7FFFFF\n#define SC_UPDATE_NONE 0x0\n#define SC_UPDATE_CONTENT 0x1\n#define SC_UPDATE_SELECTION 0x2\n#define SC_UPDATE_V_SCROLL 0x4\n#define SC_UPDATE_H_SCROLL 0x8\n#define SCEN_CHANGE 768\n#define SCEN_SETFOCUS 512\n#define SCEN_KILLFOCUS 256\n#define SCK_DOWN 300\n#define SCK_UP 301\n#define SCK_LEFT 302\n#define SCK_RIGHT 303\n#define SCK_HOME 304\n#define SCK_END 305\n#define SCK_PRIOR 306\n#define SCK_NEXT 307\n#define SCK_DELETE 308\n#define SCK_INSERT 309\n#define SCK_ESCAPE 7\n#define SCK_BACK 8\n#define SCK_TAB 9\n#define SCK_RETURN 13\n#define SCK_ADD 310\n#define SCK_SUBTRACT 311\n#define SCK_DIVIDE 312\n#define SCK_WIN 313\n#define SCK_RWIN 314\n#define SCK_MENU 315\n#define SCMOD_NORM 0\n#define SCMOD_SHIFT 1\n#define SCMOD_CTRL 2\n#define SCMOD_ALT 4\n#define SCMOD_SUPER 8\n#define SCMOD_META 16\n#define SC_AC_FILLUP 1\n#define SC_AC_DOUBLECLICK 2\n#define SC_AC_TAB 3\n#define SC_AC_NEWLINE 4\n#define SC_AC_COMMAND 5\n#define SC_AC_SINGLE_CHOICE 6\n#define SC_CHARACTERSOURCE_DIRECT_INPUT 0\n#define SC_CHARACTERSOURCE_TENTATIVE_INPUT 1\n#define SC_CHARACTERSOURCE_IME_RESULT 2\n#define SCN_STYLENEEDED 2000\n#define SCN_CHARADDED 2001\n#define SCN_SAVEPOINTREACHED 2002\n#define SCN_SAVEPOINTLEFT 2003\n#define SCN_MODIFYATTEMPTRO 2004\n#define SCN_KEY 2005\n#define SCN_DOUBLECLICK 2006\n#define SCN_UPDATEUI 2007\n#define SCN_MODIFIED 2008\n#define SCN_MACRORECORD 2009\n#define SCN_MARGINCLICK 2010\n#define SCN_NEEDSHOWN 2011\n#define SCN_PAINTED 2013\n#define SCN_USERLISTSELECTION 2014\n#define SCN_URIDROPPED 2015\n#define SCN_DWELLSTART 2016\n#define SCN_DWELLEND 2017\n#define SCN_ZOOM 2018\n#define SCN_HOTSPOTCLICK 2019\n#define SCN_HOTSPOTDOUBLECLICK 2020\n#define SCN_CALLTIPCLICK 2021\n#define SCN_AUTOCSELECTION 2022\n#define SCN_INDICATORCLICK 2023\n#define SCN_INDICATORRELEASE 2024\n#define SCN_AUTOCCANCELLED 2025\n#define SCN_AUTOCCHARDELETED 2026\n#define SCN_HOTSPOTRELEASECLICK 2027\n#define SCN_FOCUSIN 2028\n#define SCN_FOCUSOUT 2029\n#define SCN_AUTOCCOMPLETED 2030\n#define SCN_MARGINRIGHTCLICK 2031\n#define SCN_AUTOCSELECTIONCHANGE 2032\n#ifndef SCI_DISABLE_PROVISIONAL\n#define SC_BIDIRECTIONAL_DISABLED 0\n#define SC_BIDIRECTIONAL_L2R 1\n#define SC_BIDIRECTIONAL_R2L 2\n#define SCI_GETBIDIRECTIONAL 2708\n#define SCI_SETBIDIRECTIONAL 2709\n#endif\n/* --Autogenerated -- end of section automatically generated from Scintilla.iface */\n\n#endif\n\n//Au -------------------\n#define SCI_MARGINSTYLENEXT 9502\n#define SCI_SETNOTIFYCALLBACK 9503\n#define SCI_SETANNOTATIONDRAWCALLBACK 9504\n#define SCI_SETMARGINDRAWCALLBACK 9505\n#define SCI_ISXINMARGIN 9506\n#define SCI_DRAGDROP 9507\n//#define SCI_DOCUMENT_USERDATA_OFFSET 9509 //was used by QM2\n\n//----------------------\n\n/* These structures are defined to be exactly the same shape as the Win32\n * CHARRANGE, TEXTRANGE, FINDTEXTEX, FORMATRANGE, and NMHDR structs.\n * So older code that treats Scintilla as a RichEdit will work. */\n\nstruct Sci_CharacterRange {\n\tSci_PositionCR cpMin;\n\tSci_PositionCR cpMax;\n};\n\nstruct Sci_CharacterRangeFull {\n\tSci_Position cpMin;\n\tSci_Position cpMax;\n};\n\nstruct Sci_TextRange {\n\tstruct Sci_CharacterRange chrg;\n\tchar *lpstrText;\n};\n\nstruct Sci_TextRangeFull {\n\tstruct Sci_CharacterRangeFull chrg;\n\tchar *lpstrText;\n};\n\nstruct Sci_TextToFind {\n\tstruct Sci_CharacterRange chrg;\n\tconst char *lpstrText;\n\tstruct Sci_CharacterRange chrgText;\n};\n\nstruct Sci_TextToFindFull {\n\tstruct Sci_CharacterRangeFull chrg;\n\tconst char *lpstrText;\n\tstruct Sci_CharacterRangeFull chrgText;\n};\n\ntypedef void *Sci_SurfaceID;\n\nstruct Sci_Rectangle {\n\tint left;\n\tint top;\n\tint right;\n\tint bottom;\n};\n\n/* This structure is used in printing and requires some of the graphics types\n * from Platform.h.  Not needed by most client code. */\n\nstruct Sci_RangeToFormat {\n\tSci_SurfaceID hdc;\n\tSci_SurfaceID hdcTarget;\n\tstruct Sci_Rectangle rc;\n\tstruct Sci_Rectangle rcPage;\n\tstruct Sci_CharacterRange chrg;\n};\n\nstruct Sci_RangeToFormatFull {\n\tSci_SurfaceID hdc;\n\tSci_SurfaceID hdcTarget;\n\tstruct Sci_Rectangle rc;\n\tstruct Sci_Rectangle rcPage;\n\tstruct Sci_CharacterRangeFull chrg;\n};\n\n#ifndef __cplusplus\n/* For the GTK+ platform, g-ir-scanner needs to have these typedefs. This\n * is not required in C++ code and has caused problems in the past. */\ntypedef struct Sci_NotifyHeader Sci_NotifyHeader;\ntypedef struct SCNotification SCNotification;\n#endif\n\nstruct Sci_NotifyHeader {\n\t/* Compatible with Windows NMHDR.\n\t * hwndFrom is really an environment specific window handle or pointer\n\t * but most clients of Scintilla.h do not have this type visible. */\n\tvoid *hwndFrom;\n\tuptr_t idFrom;\n\tunsigned int code;\n};\n\nstruct SCNotification {\n\tSci_NotifyHeader nmhdr;\n\tSci_Position position;\n\t/* SCN_STYLENEEDED, SCN_DOUBLECLICK, SCN_MODIFIED, SCN_MARGINCLICK, */\n\t/* SCN_MARGINRIGHTCLICK, SCN_NEEDSHOWN, SCN_DWELLSTART, SCN_DWELLEND, */\n\t/* SCN_CALLTIPCLICK, */\n\t/* SCN_HOTSPOTCLICK, SCN_HOTSPOTDOUBLECLICK, SCN_HOTSPOTRELEASECLICK, */\n\t/* SCN_INDICATORCLICK, SCN_INDICATORRELEASE, */\n\t/* SCN_USERLISTSELECTION, SCN_AUTOCCOMPLETED, SCN_AUTOCSELECTION, */\n\t/* SCN_AUTOCSELECTIONCHANGE */\n\n\tint ch;\n\t/* SCN_CHARADDED, SCN_KEY, SCN_AUTOCCOMPLETED, SCN_AUTOCSELECTION, */\n\t/* SCN_USERLISTSELECTION */\n\tint modifiers;\n\t/* SCN_KEY, SCN_DOUBLECLICK, SCN_HOTSPOTCLICK, SCN_HOTSPOTDOUBLECLICK, */\n\t/* SCN_HOTSPOTRELEASECLICK, SCN_INDICATORCLICK, SCN_INDICATORRELEASE, */\n\t/* SCN_MARGINCLICK, SCN_MARGINRIGHTCLICK */\n\n\tint modificationType;\t/* SCN_MODIFIED */\n\tconst char *text;\n\t/* SCN_MODIFIED, SCN_USERLISTSELECTION, SCN_URIDROPPED, */\n\t/* SCN_AUTOCCOMPLETED, SCN_AUTOCSELECTION, SCN_AUTOCSELECTIONCHANGE */\n\n\tSci_Position length;\t\t/* SCN_MODIFIED */\n\tSci_Position linesAdded;\t/* SCN_MODIFIED */\n\tint message;\t/* SCN_MACRORECORD */\n\tuptr_t wParam;\t/* SCN_MACRORECORD */\n\tsptr_t lParam;\t/* SCN_MACRORECORD */\n\tSci_Position line;\t\t/* SCN_MODIFIED */\n\tint foldLevelNow;\t/* SCN_MODIFIED */\n\tint foldLevelPrev;\t/* SCN_MODIFIED */\n\tint margin;\t\t/* SCN_MARGINCLICK, SCN_MARGINRIGHTCLICK */\n\tint listType;\t/* SCN_USERLISTSELECTION, SCN_AUTOCSELECTIONCHANGE */\n\tint x;\t\t\t/* SCN_DWELLSTART, SCN_DWELLEND */\n\tint y;\t\t/* SCN_DWELLSTART, SCN_DWELLEND */\n\tint token;\t\t/* SCN_MODIFIED with SC_MOD_CONTAINER */\n\tSci_Position annotationLinesAdded;\t/* SCN_MODIFIED with SC_MOD_CHANGEANNOTATION */\n\tint updated;\t/* SCN_UPDATEUI */\n\tint listCompletionMethod;\n\t/* SCN_AUTOCSELECTION, SCN_AUTOCCOMPLETED, SCN_USERLISTSELECTION */\n\tint characterSource;\t/* SCN_CHARADDED */\n};\n\n#ifdef INCLUDE_DEPRECATED_FEATURES\n\n#define SCI_SETKEYSUNICODE 2521\n#define SCI_GETKEYSUNICODE 2522\n\n#define SCI_GETTWOPHASEDRAW 2283\n#define SCI_SETTWOPHASEDRAW 2284\n\n#define CharacterRange Sci_CharacterRange\n#define TextRange Sci_TextRange\n#define TextToFind Sci_TextToFind\n#define RangeToFormat Sci_RangeToFormat\n#define NotifyHeader Sci_NotifyHeader\n\n#define SCI_SETSTYLEBITS 2090\n#define SCI_GETSTYLEBITS 2091\n#define SCI_GETSTYLEBITSNEEDED 4011\n\n#define INDIC0_MASK 0x20\n#define INDIC1_MASK 0x40\n#define INDIC2_MASK 0x80\n#define INDICS_MASK 0xE0\n\n#endif\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/src/AutoComplete.cxx",
    "content": "// Scintilla source code edit control\n/** @file AutoComplete.cxx\n ** Defines the auto completion list box.\n **/\n// Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#include <cstddef>\n#include <cstdlib>\n#include <cstdint>\n#include <cassert>\n#include <cstring>\n#include <cstdio>\n\n#include <stdexcept>\n#include <string>\n#include <string_view>\n#include <vector>\n#include <optional>\n#include <algorithm>\n#include <memory>\n\n#include \"ScintillaTypes.h\"\n#include \"ScintillaMessages.h\"\n\n#include \"Debugging.h\"\n#include \"Geometry.h\"\n#include \"Platform.h\"\n\n#include \"CharacterType.h\"\n#include \"Position.h\"\n#include \"AutoComplete.h\"\n\nusing namespace Scintilla;\nusing namespace Scintilla::Internal;\n\nAutoComplete::AutoComplete() :\n\tactive(false),\n\tseparator(' '),\n\ttypesep('?'),\n\tignoreCase(false),\n\tchooseSingle(false),\n\toptions(AutoCompleteOption::Normal),\n\timageScale(1.0),\n\tposStart(0),\n\tstartLen(0),\n\tcancelAtStartPos(true),\n\tautoHide(true),\n\tdropRestOfWord(false),\n\tignoreCaseBehaviour(CaseInsensitiveBehaviour::RespectCase),\n\twidthLBDefault(100),\n\theightLBDefault(100),\n\tautoSort(Ordering::PreSorted) {\n\tlb = ListBox::Allocate();\n}\n\nAutoComplete::~AutoComplete() {\n\tif (lb) {\n\t\tlb->Destroy();\n\t}\n}\n\nbool AutoComplete::Active() const noexcept {\n\treturn active;\n}\n\nvoid AutoComplete::Start(Window &parent, int ctrlID,\n\tSci::Position position, Point location, Sci::Position startLen_,\n\tint lineHeight, bool unicodeMode, Technology technology, ListOptions listOptions) {\n\tif (active) {\n\t\tCancel();\n\t}\n\tlb->SetOptions(listOptions);\n\tlb->Create(parent, ctrlID, location, lineHeight, unicodeMode, technology);\n\tlb->Clear();\n\tactive = true;\n\tstartLen = startLen_;\n\tposStart = position;\n}\n\nvoid AutoComplete::SetStopChars(const char *stopChars_) {\n\tstopChars = stopChars_;\n}\n\nbool AutoComplete::IsStopChar(char ch) const noexcept {\n\treturn ch && (stopChars.find(ch) != std::string::npos);\n}\n\nvoid AutoComplete::SetFillUpChars(const char *fillUpChars_) {\n\tfillUpChars = fillUpChars_;\n}\n\nbool AutoComplete::IsFillUpChar(char ch) const noexcept {\n\treturn ch && (fillUpChars.find(ch) != std::string::npos);\n}\n\nvoid AutoComplete::SetSeparator(char separator_) noexcept {\n\tseparator = separator_;\n}\n\nchar AutoComplete::GetSeparator() const noexcept {\n\treturn separator;\n}\n\nvoid AutoComplete::SetTypesep(char separator_) noexcept {\n\ttypesep = separator_;\n}\n\nchar AutoComplete::GetTypesep() const noexcept {\n\treturn typesep;\n}\n\nnamespace {\n\nstruct Sorter {\n\tconst bool ignoreCase;\n\tconst char *list;\n\tstd::vector<int> indices;\n\n\tSorter(const AutoComplete *ac, const char *list_) : ignoreCase(ac->ignoreCase), list(list_) {\n\t\tint i = 0;\n\t\tif (!list[i]) {\n\t\t\t// Empty list has a single empty member\n\t\t\tindices.push_back(i); // word start\n\t\t\tindices.push_back(i); // word end\n\t\t}\n\t\tconst char separator = ac->GetSeparator();\n\t\tconst char typesep = ac->GetTypesep();\n\t\twhile (list[i]) {\n\t\t\tindices.push_back(i); // word start\n\t\t\twhile (list[i] != typesep && list[i] != separator && list[i])\n\t\t\t\t++i;\n\t\t\tindices.push_back(i); // word end\n\t\t\tif (list[i] == typesep) {\n\t\t\t\twhile (list[i] != separator && list[i])\n\t\t\t\t\t++i;\n\t\t\t}\n\t\t\tif (list[i] == separator) {\n\t\t\t\t++i;\n\t\t\t\t// preserve trailing separator as blank entry\n\t\t\t\tif (!list[i]) {\n\t\t\t\t\tindices.push_back(i);\n\t\t\t\t\tindices.push_back(i);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tindices.push_back(i); // index of last position\n\t}\n\n\tbool operator()(int a, int b) const noexcept {\n\t\tconst unsigned indexA = a * 2;\n\t\tconst unsigned indexB = b * 2;\n\t\tconst int lenA = indices[indexA + 1] - indices[indexA];\n\t\tconst int lenB = indices[indexB + 1] - indices[indexB];\n\t\tconst int len  = std::min(lenA, lenB);\n\t\tint cmp;\n\t\tif (ignoreCase)\n\t\t\tcmp = CompareNCaseInsensitive(list + indices[indexA], list + indices[indexB], len);\n\t\telse\n\t\t\tcmp = strncmp(list + indices[indexA], list + indices[indexB], len);\n\t\tif (cmp == 0)\n\t\t\tcmp = lenA - lenB;\n\t\treturn cmp < 0;\n\t}\n};\n\nvoid FillSortMatrix(std::vector<int> &sortMatrix, int itemCount) {\n\tsortMatrix.clear();\n\tfor (int i = 0; i < itemCount; i++) {\n\t\tsortMatrix.push_back(i);\n\t}\n}\n\n}\n\nvoid AutoComplete::SetList(const char *list) {\n\tif (autoSort == Ordering::PreSorted) {\n\t\tlb->SetList(list, separator, typesep);\n\t\tFillSortMatrix(sortMatrix, lb->Length());\n\t\treturn;\n\t}\n\n\tconst Sorter IndexSort(this, list);\n\tFillSortMatrix(sortMatrix, static_cast<int>(IndexSort.indices.size() / 2));\n\tstd::sort(sortMatrix.begin(), sortMatrix.end(), IndexSort);\n\tif (autoSort == Ordering::Custom || sortMatrix.size() < 2) {\n\t\tlb->SetList(list, separator, typesep);\n\t\tPLATFORM_ASSERT(lb->Length() == static_cast<int>(sortMatrix.size()));\n\t\treturn;\n\t}\n\n\tstd::string sortedList;\n\tfor (size_t i = 0; i < sortMatrix.size(); ++i) {\n\t\tconst unsigned index = sortMatrix[i] * 2;\n\t\tsortMatrix[i] = static_cast<int>(i);\n\t\t// word length include trailing typesep and separator\n\t\tconst int wordLen = IndexSort.indices[index + 2] - IndexSort.indices[index];\n\t\tconst std::string_view item(list + IndexSort.indices[index], wordLen);\n\t\tsortedList += item;\n\t\tif ((i + 1) == sortMatrix.size()) {\n\t\t\t// Last item so remove separator if present\n\t\t\tif (!item.empty() && item.back() == separator) {\n\t\t\t\tsortedList.pop_back();\n\t\t\t}\n\t\t} else {\n\t\t\t// Item before last needs a separator\n\t\t\tif (item.empty() || item.back() != separator) {\n\t\t\t\tsortedList += separator;\n\t\t\t}\n\t\t}\n\t}\n\tlb->SetList(sortedList.c_str(), separator, typesep);\n}\n\nint AutoComplete::GetSelection() const {\n\treturn lb->GetSelection();\n}\n\nstd::string AutoComplete::GetValue(int item) const {\n\treturn lb->GetValue(item);\n}\n\nvoid AutoComplete::Show(bool show) {\n\tlb->Show(show);\n\tif (show)\n\t\tlb->Select(0);\n}\n\nvoid AutoComplete::Cancel() noexcept {\n\tif (lb->Created()) {\n\t\tlb->Clear();\n\t\tlb->Destroy();\n\t\tactive = false;\n\t}\n}\n\n\nvoid AutoComplete::Move(int delta) {\n\tconst int count = lb->Length();\n\tint current = lb->GetSelection();\n\tcurrent += delta;\n\tif (current >= count)\n\t\tcurrent = count - 1;\n\tif (current < 0)\n\t\tcurrent = 0;\n\tlb->Select(current);\n}\n\nvoid AutoComplete::Select(const char *word) {\n\tconst size_t lenWord = strlen(word);\n\tint location = -1;\n\tint start = 0; // lower bound of the api array block to search\n\tint end = lb->Length() - 1; // upper bound of the api array block to search\n\twhile ((start <= end) && (location == -1)) { // Binary searching loop\n\t\tint pivot = (start + end) / 2;\n\t\tstd::string item = GetValue(sortMatrix[pivot]);\n\t\tint cond;\n\t\tif (ignoreCase)\n\t\t\tcond = CompareNCaseInsensitive(word, item.c_str(), lenWord);\n\t\telse\n\t\t\tcond = strncmp(word, item.c_str(), lenWord);\n\t\tif (!cond) {\n\t\t\t// Find first match\n\t\t\twhile (pivot > start) {\n\t\t\t\titem = lb->GetValue(sortMatrix[pivot-1]);\n\t\t\t\tif (ignoreCase)\n\t\t\t\t\tcond = CompareNCaseInsensitive(word, item.c_str(), lenWord);\n\t\t\t\telse\n\t\t\t\t\tcond = strncmp(word, item.c_str(), lenWord);\n\t\t\t\tif (0 != cond)\n\t\t\t\t\tbreak;\n\t\t\t\t--pivot;\n\t\t\t}\n\t\t\tlocation = pivot;\n\t\t\tif (ignoreCase\n\t\t\t\t&& ignoreCaseBehaviour == CaseInsensitiveBehaviour::RespectCase) {\n\t\t\t\t// Check for exact-case match\n\t\t\t\tfor (; pivot <= end; pivot++) {\n\t\t\t\t\titem = lb->GetValue(sortMatrix[pivot]);\n\t\t\t\t\tif (!strncmp(word, item.c_str(), lenWord)) {\n\t\t\t\t\t\tlocation = pivot;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tif (CompareNCaseInsensitive(word, item.c_str(), lenWord))\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (cond < 0) {\n\t\t\tend = pivot - 1;\n\t\t} else { // cond > 0\n\t\t\tstart = pivot + 1;\n\t\t}\n\t}\n\tif (location == -1) {\n\t\tif (autoHide)\n\t\t\tCancel();\n\t\telse\n\t\t\tlb->Select(-1);\n\t} else {\n\t\tif (autoSort == Ordering::Custom) {\n\t\t\t// Check for a logically earlier match\n\t\t\tfor (int i = location + 1; i <= end; ++i) {\n\t\t\t\tconst std::string item = lb->GetValue(sortMatrix[i]);\n\t\t\t\tif (CompareNCaseInsensitive(word, item.c_str(), lenWord))\n\t\t\t\t\tbreak;\n\t\t\t\tif (sortMatrix[i] < sortMatrix[location] && !strncmp(word, item.c_str(), lenWord))\n\t\t\t\t\tlocation = i;\n\t\t\t}\n\t\t}\n\t\tlb->Select(sortMatrix[location]);\n\t}\n}\n\n"
  },
  {
    "path": "Libraries/scintilla/src/AutoComplete.h",
    "content": "// Scintilla source code edit control\n/** @file AutoComplete.h\n ** Defines the auto completion list box.\n **/\n// Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#ifndef AUTOCOMPLETE_H\n#define AUTOCOMPLETE_H\n\nnamespace Scintilla::Internal {\n\n/**\n */\nclass AutoComplete {\n\tbool active;\n\tstd::string stopChars;\n\tstd::string fillUpChars;\n\tchar separator;\n\tchar typesep; // Type separator\n\tstd::vector<int> sortMatrix;\n\npublic:\n\n\tbool ignoreCase;\n\tbool chooseSingle;\n\tAutoCompleteOption options;\n\tfloat imageScale;\n\tstd::unique_ptr<ListBox> lb;\n\tSci::Position posStart;\n\tSci::Position startLen;\n\t/// Should autocompletion be cancelled if editor's currentPos <= startPos?\n\tbool cancelAtStartPos;\n\tbool autoHide;\n\tbool dropRestOfWord;\n\tScintilla::CaseInsensitiveBehaviour ignoreCaseBehaviour;\n\tint widthLBDefault;\n\tint heightLBDefault;\n\t/** Ordering::PreSorted:   Assume the list is presorted; selection will fail if it is not alphabetical<br />\n\t *  Ordering::PerformSort: Sort the list alphabetically; start up performance cost for sorting<br />\n\t *  Ordering::Custom:      Handle non-alphabetical entries; start up performance cost for generating a sorted lookup table\n\t */\n\tScintilla::Ordering autoSort;\n\n\tAutoComplete();\n\t// Deleted so AutoComplete objects can not be copied.\n\tAutoComplete(const AutoComplete &) = delete;\n\tAutoComplete(AutoComplete &&) = delete;\n\tAutoComplete &operator=(const AutoComplete &) = delete;\n\tAutoComplete &operator=(AutoComplete &&) = delete;\n\t~AutoComplete();\n\n\t/// Is the auto completion list displayed?\n\tbool Active() const noexcept;\n\n\t/// Display the auto completion list positioned to be near a character position\n\tvoid Start(Window &parent, int ctrlID, Sci::Position position, Point location,\n\t\tSci::Position startLen_, int lineHeight, bool unicodeMode, Scintilla::Technology technology,\n\t\tListOptions listOptions);\n\n\t/// The stop chars are characters which, when typed, cause the auto completion list to disappear\n\tvoid SetStopChars(const char *stopChars_);\n\tbool IsStopChar(char ch) const noexcept;\n\n\t/// The fillup chars are characters which, when typed, fill up the selected word\n\tvoid SetFillUpChars(const char *fillUpChars_);\n\tbool IsFillUpChar(char ch) const noexcept;\n\n\t/// The separator character is used when interpreting the list in SetList\n\tvoid SetSeparator(char separator_) noexcept;\n\tchar GetSeparator() const noexcept;\n\n\t/// The typesep character is used for separating the word from the type\n\tvoid SetTypesep(char separator_) noexcept;\n\tchar GetTypesep() const noexcept;\n\n\t/// The list string contains a sequence of words separated by the separator character\n\tvoid SetList(const char *list);\n\n\t/// Return the position of the currently selected list item\n\tint GetSelection() const;\n\n\t/// Return the value of an item in the list\n\tstd::string GetValue(int item) const;\n\n\tvoid Show(bool show);\n\tvoid Cancel() noexcept;\n\n\t/// Move the current list element by delta, scrolling appropriately\n\tvoid Move(int delta);\n\n\t/// Select a list element that starts with word as the current element\n\tvoid Select(const char *word);\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/src/CallTip.cxx",
    "content": "// Scintilla source code edit control\n/** @file CallTip.cxx\n ** Code for displaying call tips.\n **/\n// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#include <cstddef>\n#include <cstdlib>\n#include <cstdint>\n#include <cassert>\n#include <cstring>\n#include <cstdio>\n#include <cmath>\n\n#include <stdexcept>\n#include <string>\n#include <string_view>\n#include <vector>\n#include <optional>\n#include <algorithm>\n#include <memory>\n\n#include \"ScintillaTypes.h\"\n#include \"ScintillaMessages.h\"\n\n#include \"Debugging.h\"\n#include \"Geometry.h\"\n#include \"Platform.h\"\n\n#include \"Position.h\"\n#include \"CallTip.h\"\n\nusing namespace Scintilla;\nusing namespace Scintilla::Internal;\n\nsize_t Chunk::Length() const noexcept {\n\treturn end - start;\n}\n\nnamespace {\n\n#ifdef __APPLE__\n// Archaic macOS colours for the default: black on light yellow\nconstexpr ColourRGBA colourTextAndArrow(black);\nconstexpr ColourRGBA colourBackground(0xff, 0xff, 0xc6);\n#else\n// Grey on white\nconstexpr ColourRGBA colourTextAndArrow(0x80, 0x80, 0x80);\nconstexpr ColourRGBA colourBackground(white);\n#endif\n\nconstexpr ColourRGBA silver(0xc0, 0xc0, 0xc0);\n\n}\n\nCallTip::CallTip() noexcept {\n\twCallTip = {};\n\tinCallTipMode = false;\n\tposStartCallTip = 0;\n\trectUp = PRectangle(0,0,0,0);\n\trectDown = PRectangle(0,0,0,0);\n\tlineHeight = 1;\n\toffsetMain = 0;\n\ttabSize = 0;\n\tabove = false;\n\tuseStyleCallTip = false;    // for backwards compatibility\n\n\tinsetX = 5;\n\twidthArrow = 14;\n\tborderHeight = 2; // Extra line for border and an empty line at top and bottom.\n\tverticalOffset = 1;\n\n\tcolourBG = colourBackground;\n\tcolourUnSel = colourTextAndArrow;\n\n\tcolourSel = ColourRGBA(0, 0, 0x80);\n\tcolourShade = black;\n\tcolourLight = silver;\n\tcodePage = 0;\n\tclickPlace = 0;\n}\n\nCallTip::~CallTip() {\n\twCallTip.Destroy();\n}\n\n// We ignore tabs unless a tab width has been set.\nbool CallTip::IsTabCharacter(char ch) const noexcept {\n\treturn (tabSize > 0) && (ch == '\\t');\n}\n\nint CallTip::NextTabPos(int x) const noexcept {\n\tif (tabSize > 0) {              // paranoia... not called unless this is true\n\t\tx -= insetX;                // position relative to text\n\t\tx = (x + tabSize) / tabSize;  // tab \"number\"\n\t\treturn tabSize*x + insetX;  // position of next tab\n\t}\n\treturn x + 1;                 // arbitrary\n}\n\nnamespace {\n\n// Although this test includes 0, we should never see a \\0 character.\nconstexpr bool IsArrowCharacter(char ch) noexcept {\n\treturn (ch == 0) || (ch == '\\001') || (ch == '\\002');\n}\n\nvoid DrawArrow(Surface *surface, const PRectangle &rc, bool upArrow, ColourRGBA colourBG, ColourRGBA colourUnSel) {\n\tsurface->FillRectangle(rc, colourBG);\n\tconst PRectangle rcClientInner = Clamp(rc.Inset(1), Edge::right, rc.right - 2);\n\tsurface->FillRectangle(rcClientInner, colourUnSel);\n\n\tconst XYPOSITION width = std::floor(rcClientInner.Width());\n\tconst XYPOSITION halfWidth = std::floor(width / 2) - 1;\n\tconst XYPOSITION quarterWidth = std::floor(halfWidth / 2);\n\tconst XYPOSITION centreX = rcClientInner.left + width / 2;\n\tconst XYPOSITION centreY = std::floor((rcClientInner.top + rcClientInner.bottom) / 2);\n\n\tconstexpr XYPOSITION pixelMove = 0.0f;\n\tif (upArrow) {      // Up arrow\n\t\tconst Point pts[] = {\n\t\t\tPoint(centreX - halfWidth + pixelMove, centreY + quarterWidth + 0.5f),\n\t\t\tPoint(centreX + halfWidth + pixelMove, centreY + quarterWidth + 0.5f),\n\t\t\tPoint(centreX + pixelMove, centreY - halfWidth + quarterWidth + 0.5f),\n\t\t};\n\t\tsurface->Polygon(pts, std::size(pts), FillStroke(colourBG));\n\t} else {            // Down arrow\n\t\tconst Point pts[] = {\n\t\t\tPoint(centreX - halfWidth + pixelMove, centreY - quarterWidth + 0.5f),\n\t\t\tPoint(centreX + halfWidth + pixelMove, centreY - quarterWidth + 0.5f),\n\t\t\tPoint(centreX + pixelMove, centreY + halfWidth - quarterWidth + 0.5f),\n\t\t};\n\t\tsurface->Polygon(pts, std::size(pts), FillStroke(colourBG));\n\t}\n}\n\n}\n\n// Draw a section of the call tip that does not include \\n in one colour.\n// The text may include tabs or arrow characters.\nint CallTip::DrawChunk(Surface *surface, int x, std::string_view sv,\n\tint ytext, PRectangle rcClient, bool asHighlight, bool draw) {\n\n\tif (sv.empty()) {\n\t\treturn x;\n\t}\n\n\t// Divide the text into sections that are all text, or that are\n\t// single arrows or single tab characters (if tabSize > 0).\n\t// Start with single element 0 to simplify append checks.\n\tstd::vector<size_t> ends(1);\n\tfor (size_t i=0; i<sv.length(); i++) {\n\t\tif (IsArrowCharacter(sv[i]) || IsTabCharacter(sv[i])) {\n\t\t\tif (ends.back() != i)\n\t\t\t\tends.push_back(i);\n\t\t\tends.push_back(i+1);\n\t\t}\n\t}\n\tif (ends.back() != sv.length())\n\t\tends.push_back(sv.length());\n\tends.erase(ends.begin());\t// Remove initial 0.\n\n\tsize_t startSeg = 0;\n\tfor (const size_t endSeg : ends) {\n\t\tassert(endSeg > 0);\n\t\tint xEnd = 0;\n\t\tif (IsArrowCharacter(sv[startSeg])) {\n\t\t\txEnd = x + widthArrow;\n\t\t\tconst bool upArrow = sv[startSeg] == '\\001';\n\t\t\trcClient.left = static_cast<XYPOSITION>(x);\n\t\t\trcClient.right = static_cast<XYPOSITION>(xEnd);\n\t\t\tif (draw) {\n\t\t\t\tDrawArrow(surface, rcClient, upArrow, colourBG, colourUnSel);\n\t\t\t}\n\t\t\toffsetMain = xEnd;\n\t\t\tif (upArrow) {\n\t\t\t\trectUp = rcClient;\n\t\t\t} else {\n\t\t\t\trectDown = rcClient;\n\t\t\t}\n\t\t} else if (IsTabCharacter(sv[startSeg])) {\n\t\t\txEnd = NextTabPos(x);\n\t\t} else {\n\t\t\tconst std::string_view segText = sv.substr(startSeg, endSeg - startSeg);\n\t\t\txEnd = x + static_cast<int>(std::lround(surface->WidthText(font.get(), segText)));\n\t\t\tif (draw) {\n\t\t\t\trcClient.left = static_cast<XYPOSITION>(x);\n\t\t\t\trcClient.right = static_cast<XYPOSITION>(xEnd);\n\t\t\t\tsurface->DrawTextTransparent(rcClient, font.get(), static_cast<XYPOSITION>(ytext),\n\t\t\t\t\t\t\t\t\tsegText, asHighlight ? colourSel : colourUnSel);\n\t\t\t}\n\t\t}\n\t\tx = xEnd;\n\t\tstartSeg = endSeg;\n\t}\n\treturn x;\n}\n\nint CallTip::PaintContents(Surface *surfaceWindow, bool draw) {\n\tconst PRectangle rcClientPos = wCallTip.GetClientPosition();\n\tconst PRectangle rcClientSize(0.0f, 0.0f, rcClientPos.right - rcClientPos.left,\n\t                        rcClientPos.bottom - rcClientPos.top);\n\tPRectangle rcClient(1.0f, 1.0f, rcClientSize.right - 1, rcClientSize.bottom - 1);\n\n\t// To make a nice small call tip window, it is only sized to fit most normal characters without accents\n\tconst int ascent = static_cast<int>(std::round(surfaceWindow->Ascent(font.get()) - surfaceWindow->InternalLeading(font.get())));\n\n\t// For each line...\n\t// Draw the definition in three parts: before highlight, highlighted, after highlight\n\tint ytext = static_cast<int>(rcClient.top) + ascent + 1;\n\trcClient.bottom = ytext + surfaceWindow->Descent(font.get()) + 1;\n\tstd::string_view remaining(val);\n\tint maxWidth = 0;\n\tsize_t lineStart = 0;\n\twhile (!remaining.empty()) {\n\t\tconst std::string_view chunkVal = remaining.substr(0, remaining.find_first_of('\\n'));\n\t\tremaining.remove_prefix(chunkVal.length());\n\t\tif (!remaining.empty()) {\n\t\t\tremaining.remove_prefix(1);\t// Skip \\n\n\t\t}\n\n\t\tconst Chunk chunkLine(lineStart, lineStart + chunkVal.length());\n\t\tChunk chunkHighlight(\n\t\t\tstd::clamp(highlight.start, chunkLine.start, chunkLine.end),\n\t\t\tstd::clamp(highlight.end, chunkLine.start, chunkLine.end)\n\t\t);\n\t\tchunkHighlight.start -= lineStart;\n\t\tchunkHighlight.end -= lineStart;\n\n\t\tconst int top = ytext - ascent - 1;\n\t\trcClient.top = top;\n\n\t\tint x = insetX;     // start each line at this inset\n\n\t\tx = DrawChunk(surfaceWindow, x,\n\t\t\tchunkVal.substr(0, chunkHighlight.start),\n\t\t\tytext, rcClient, false, draw);\n\t\tx = DrawChunk(surfaceWindow, x,\n\t\t\tchunkVal.substr(chunkHighlight.start, chunkHighlight.Length()),\n\t\t\tytext, rcClient, true, draw);\n\t\tx = DrawChunk(surfaceWindow, x,\n\t\t\tchunkVal.substr(chunkHighlight.end),\n\t\t\tytext, rcClient, false, draw);\n\n\t\tytext += lineHeight;\n\t\trcClient.bottom += lineHeight;\n\t\tmaxWidth = std::max(maxWidth, x);\n\t\tlineStart += chunkVal.length() + 1;\n\t}\n\treturn maxWidth;\n}\n\nvoid CallTip::PaintCT(Surface *surfaceWindow) {\n\tif (val.empty())\n\t\treturn;\n\tconst PRectangle rcClientPos = wCallTip.GetClientPosition();\n\tconst PRectangle rcClientSize(0.0f, 0.0f, rcClientPos.right - rcClientPos.left,\n\t                        rcClientPos.bottom - rcClientPos.top);\n\tconst PRectangle rcClient(1.0f, 1.0f, rcClientSize.right - 1, rcClientSize.bottom - 1);\n\n\tsurfaceWindow->FillRectangle(rcClient, colourBG);\n\n\toffsetMain = insetX;    // initial alignment assuming no arrows\n\tPaintContents(surfaceWindow, true);\n\n#if !defined(__APPLE__) && !PLAT_CURSES\n\t// OSX doesn't put borders on \"help tags\"\n\t// Draw a raised border around the edges of the window\n\tconstexpr XYPOSITION border = 1.0f;\n\tsurfaceWindow->FillRectangle(Side(rcClientSize, Edge::left, border), colourLight);\n\tsurfaceWindow->FillRectangle(Side(rcClientSize, Edge::right, border), colourShade);\n\tsurfaceWindow->FillRectangle(Side(rcClientSize, Edge::bottom, border), colourShade);\n\tsurfaceWindow->FillRectangle(Side(rcClientSize, Edge::top, border), colourLight);\n#endif\n}\n\nvoid CallTip::MouseClick(Point pt) noexcept {\n\tclickPlace = 0;\n\tif (rectUp.Contains(pt))\n\t\tclickPlace = 1;\n\tif (rectDown.Contains(pt))\n\t\tclickPlace = 2;\n}\n\nPRectangle CallTip::CallTipStart(Sci::Position pos, Point pt, int textHeight, const char *defn,\n                                 int codePage_, Surface *surfaceMeasure, const std::shared_ptr<Font> &font_) {\n\tclickPlace = 0;\n\tval = defn;\n\tcodePage = codePage_;\n\thighlight = Chunk();\n\tinCallTipMode = true;\n\tposStartCallTip = pos;\n\tfont = font_;\n\t// Look for multiple lines in the text\n\t// Only support \\n here - simply means container must avoid \\r!\n\tconst int numLines = 1 + static_cast<int>(std::count(val.begin(), val.end(), '\\n'));\n\trectUp = PRectangle(0,0,0,0);\n\trectDown = PRectangle(0,0,0,0);\n\toffsetMain = insetX;            // changed to right edge of any arrows\n\tlineHeight = static_cast<int>(std::lround(surfaceMeasure->Height(font.get())));\n#if !PLAT_CURSES\n\twidthArrow = lineHeight * 9 / 10;\n#endif\n\tconst int width = PaintContents(surfaceMeasure, false) + insetX;\n\n\t// The returned\n\t// rectangle is aligned to the right edge of the last arrow encountered in\n\t// the tip text, else to the tip text left edge.\n\tconst int height = lineHeight * numLines - static_cast<int>(surfaceMeasure->InternalLeading(font.get())) + borderHeight * 2;\n\tif (above) {\n\t\treturn PRectangle(pt.x - offsetMain, pt.y - verticalOffset - height, pt.x + width - offsetMain, pt.y - verticalOffset);\n\t} else {\n\t\treturn PRectangle(pt.x - offsetMain, pt.y + verticalOffset + textHeight, pt.x + width - offsetMain, pt.y + verticalOffset + textHeight + height);\n\t}\n}\n\nvoid CallTip::CallTipCancel() noexcept {\n\tinCallTipMode = false;\n\tif (wCallTip.Created()) {\n\t\twCallTip.Destroy();\n\t}\n}\n\nvoid CallTip::SetHighlight(size_t start, size_t end) {\n\t// Avoid flashing by checking something has really changed\n\tif ((start != highlight.start) || (end != highlight.end)) {\n\t\thighlight.start = start;\n\t\thighlight.end = (end > start) ? end : start;\n\t\tif (wCallTip.Created()) {\n\t\t\twCallTip.InvalidateAll();\n\t\t}\n\t}\n}\n\n// Set the tab size (sizes > 0 enable the use of tabs). This also enables the\n// use of the StyleCallTip.\nvoid CallTip::SetTabSize(int tabSz) noexcept {\n\ttabSize = tabSz;\n\tuseStyleCallTip = true;\n}\n\n// Set the calltip position, below the text by default or if above is false\n// else above the text.\nvoid CallTip::SetPosition(bool aboveText) noexcept {\n\tabove = aboveText;\n}\n\nbool CallTip::UseStyleCallTip() const noexcept {\n\treturn useStyleCallTip;\n}\n\n// It might be better to have two access functions for this and to use\n// them for all settings of colours.\nvoid CallTip::SetForeBack(ColourRGBA fore, ColourRGBA back) noexcept {\n\tcolourBG = back;\n\tcolourUnSel = fore;\n}\n"
  },
  {
    "path": "Libraries/scintilla/src/CallTip.h",
    "content": "// Scintilla source code edit control\n/** @file CallTip.h\n ** Interface to the call tip control.\n **/\n// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#ifndef CALLTIP_H\n#define CALLTIP_H\n\nnamespace Scintilla::Internal {\n\nstruct Chunk {\n\tsize_t start;\n\tsize_t end;\n\tconstexpr Chunk(size_t start_=0, size_t end_=0) noexcept : start(start_), end(end_) {\n\t\tassert(start <= end);\n\t}\n\tsize_t Length() const noexcept;\n};\n\n/**\n */\nclass CallTip {\n\tChunk highlight;    // character offset to start and end of highlighted text\n\tstd::string val;\n\tstd::shared_ptr<Font> font;\n\tPRectangle rectUp;      // rectangle of last up angle in the tip\n\tPRectangle rectDown;    // rectangle of last down arrow in the tip\n\tint lineHeight;         // vertical line spacing\n\tint offsetMain;         // The alignment point of the call tip\n\tint tabSize;            // Tab size in pixels, <=0 no TAB expand\n\tbool useStyleCallTip;   // if true, StyleCallTip should be used\n\tbool above;\t\t// if true, display calltip above text\n\n\tint DrawChunk(Surface *surface, int x, std::string_view sv,\n\t\tint ytext, PRectangle rcClient, bool asHighlight, bool draw);\n\tint PaintContents(Surface *surfaceWindow, bool draw);\n\tbool IsTabCharacter(char ch) const noexcept;\n\tint NextTabPos(int x) const noexcept;\n\npublic:\n\tWindow wCallTip;\n\tWindow wDraw;\n\tbool inCallTipMode;\n\tSci::Position posStartCallTip;\n\tColourRGBA colourBG;\n\tColourRGBA colourUnSel;\n\tColourRGBA colourSel;\n\tColourRGBA colourShade;\n\tColourRGBA colourLight;\n\tint codePage;\n\tint clickPlace;\n\n\tint insetX; // text inset in x from calltip border\n\tint widthArrow;\n\tint borderHeight;\n\tint verticalOffset; // pixel offset up or down of the calltip with respect to the line\n\n\tCallTip() noexcept;\n\t// Deleted so CallTip objects can not be copied.\n\tCallTip(const CallTip &) = delete;\n\tCallTip(CallTip &&) = delete;\n\tCallTip &operator=(const CallTip &) = delete;\n\tCallTip &operator=(CallTip &&) = delete;\n\t~CallTip();\n\n\tvoid PaintCT(Surface *surfaceWindow);\n\n\tvoid MouseClick(Point pt) noexcept;\n\n\t/// Setup the calltip and return a rectangle of the area required.\n\tPRectangle CallTipStart(Sci::Position pos, Point pt, int textHeight, const char *defn,\n\t\tint codePage_, Surface *surfaceMeasure, const std::shared_ptr<Font> &font_);\n\n\tvoid CallTipCancel() noexcept;\n\n\t/// Set a range of characters to be displayed in a highlight style.\n\t/// Commonly used to highlight the current parameter.\n\tvoid SetHighlight(size_t start, size_t end);\n\n\t/// Set the tab size in pixels for the call tip. 0 or -ve means no tab expand.\n\tvoid SetTabSize(int tabSz) noexcept;\n\n\t/// Set calltip position.\n\tvoid SetPosition(bool aboveText) noexcept;\n\n\t/// Used to determine which STYLE_xxxx to use for call tip information\n\tbool UseStyleCallTip() const noexcept;\n\n\t// Modify foreground and background colours\n\tvoid SetForeBack(ColourRGBA fore, ColourRGBA back) noexcept;\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/src/CaseConvert.cxx",
    "content": "// Scintilla source code edit control\n// Encoding: UTF-8\n/** @file CaseConvert.cxx\n ** Case fold characters and convert them to upper or lower case.\n ** Tables automatically regenerated by scripts/GenerateCaseConvert.py\n ** Should only be rarely regenerated for new versions of Unicode.\n **/\n// Copyright 2013 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#include <cassert>\n#include <cstring>\n\n#include <stdexcept>\n#include <string>\n#include <string_view>\n#include <vector>\n#include <algorithm>\n\n#include \"CaseConvert.h\"\n#include \"UniConversion.h\"\n\nusing namespace Scintilla::Internal;\n\nnamespace {\n\t// Use an unnamed namespace to protect the declarations from name conflicts\n\n// Unicode code points are ordered by groups and follow patterns.\n// Most characters (pitch==1) are in ranges for a particular alphabet and their\n// upper case forms are a fixed distance away.\n// Another pattern (pitch==2) is where each lower case letter is preceded by\n// the upper case form. These are also grouped into ranges.\n\nconstexpr int symmetricCaseConversionRanges[] = {\n//lower, upper, range length, range pitch\n//++Autogenerated -- start of section automatically generated\n//**\\(\\*\\n\\)\n97,65,26,1,\n224,192,23,1,\n248,216,7,1,\n257,256,24,2,\n314,313,8,2,\n331,330,23,2,\n462,461,8,2,\n479,478,9,2,\n505,504,20,2,\n547,546,9,2,\n583,582,5,2,\n945,913,17,1,\n963,931,9,1,\n985,984,12,2,\n1072,1040,32,1,\n1104,1024,16,1,\n1121,1120,17,2,\n1163,1162,27,2,\n1218,1217,7,2,\n1233,1232,48,2,\n1377,1329,38,1,\n4304,7312,43,1,\n7681,7680,75,2,\n7841,7840,48,2,\n7936,7944,8,1,\n7952,7960,6,1,\n7968,7976,8,1,\n7984,7992,8,1,\n8000,8008,6,1,\n8032,8040,8,1,\n8560,8544,16,1,\n9424,9398,26,1,\n11312,11264,48,1,\n11393,11392,50,2,\n11520,4256,38,1,\n42561,42560,23,2,\n42625,42624,14,2,\n42787,42786,7,2,\n42803,42802,31,2,\n42879,42878,5,2,\n42903,42902,10,2,\n42933,42932,8,2,\n65345,65313,26,1,\n66600,66560,40,1,\n66776,66736,36,1,\n66967,66928,11,1,\n66979,66940,15,1,\n66995,66956,7,1,\n68800,68736,51,1,\n71872,71840,32,1,\n93792,93760,32,1,\n125218,125184,34,1,\n\n//--Autogenerated -- end of section automatically generated\n};\n\n// Code points that are symmetric but don't fit into a range of similar characters\n// are listed here.\n\nconstexpr int symmetricCaseConversions[] = {\n//lower, upper\n//++Autogenerated -- start of section automatically generated\n//**1 \\(\\*\\n\\)\n255,376,\n307,306,\n309,308,\n311,310,\n378,377,\n380,379,\n382,381,\n384,579,\n387,386,\n389,388,\n392,391,\n396,395,\n402,401,\n405,502,\n409,408,\n410,573,\n414,544,\n417,416,\n419,418,\n421,420,\n424,423,\n429,428,\n432,431,\n436,435,\n438,437,\n441,440,\n445,444,\n447,503,\n454,452,\n457,455,\n460,458,\n477,398,\n499,497,\n501,500,\n572,571,\n575,11390,\n576,11391,\n578,577,\n592,11375,\n593,11373,\n594,11376,\n595,385,\n596,390,\n598,393,\n599,394,\n601,399,\n603,400,\n604,42923,\n608,403,\n609,42924,\n611,404,\n613,42893,\n614,42922,\n616,407,\n617,406,\n618,42926,\n619,11362,\n620,42925,\n623,412,\n625,11374,\n626,413,\n629,415,\n637,11364,\n640,422,\n642,42949,\n643,425,\n647,42929,\n648,430,\n649,580,\n650,433,\n651,434,\n652,581,\n658,439,\n669,42930,\n670,42928,\n881,880,\n883,882,\n887,886,\n891,1021,\n892,1022,\n893,1023,\n940,902,\n941,904,\n942,905,\n943,906,\n972,908,\n973,910,\n974,911,\n983,975,\n1010,1017,\n1011,895,\n1016,1015,\n1019,1018,\n1231,1216,\n4349,7357,\n4350,7358,\n4351,7359,\n7545,42877,\n7549,11363,\n7566,42950,\n8017,8025,\n8019,8027,\n8021,8029,\n8023,8031,\n8048,8122,\n8049,8123,\n8050,8136,\n8051,8137,\n8052,8138,\n8053,8139,\n8054,8154,\n8055,8155,\n8056,8184,\n8057,8185,\n8058,8170,\n8059,8171,\n8060,8186,\n8061,8187,\n8112,8120,\n8113,8121,\n8144,8152,\n8145,8153,\n8160,8168,\n8161,8169,\n8165,8172,\n8526,8498,\n8580,8579,\n11361,11360,\n11365,570,\n11366,574,\n11368,11367,\n11370,11369,\n11372,11371,\n11379,11378,\n11382,11381,\n11500,11499,\n11502,11501,\n11507,11506,\n11559,4295,\n11565,4301,\n42874,42873,\n42876,42875,\n42892,42891,\n42897,42896,\n42899,42898,\n42900,42948,\n42952,42951,\n42954,42953,\n42961,42960,\n42967,42966,\n42969,42968,\n42998,42997,\n43859,42931,\n67003,66964,\n67004,66965,\n\n//--Autogenerated -- end of section automatically generated\n};\n\n// Characters that have complex case conversions are listed here.\n// This includes cases where more than one character is needed for a conversion,\n// folding is different to lowering, or (as appropriate) upper(lower(x)) != x or\n// lower(upper(x)) != x.\n\nconstexpr std::string_view complexCaseConversions =\n// Original | Folded | Upper | Lower |\n//++Autogenerated -- start of section automatically generated\n//**2 \\(\\*\\n\\)\n\"\\xc2\\xb5|\\xce\\xbc|\\xce\\x9c||\"\n\"\\xc3\\x9f|ss|SS||\"\n\"\\xc4\\xb0|i\\xcc\\x87||i\\xcc\\x87|\"\n\"\\xc4\\xb1||I||\"\n\"\\xc5\\x89|\\xca\\xbcn|\\xca\\xbcN||\"\n\"\\xc5\\xbf|s|S||\"\n\"\\xc7\\x85|\\xc7\\x86|\\xc7\\x84|\\xc7\\x86|\"\n\"\\xc7\\x88|\\xc7\\x89|\\xc7\\x87|\\xc7\\x89|\"\n\"\\xc7\\x8b|\\xc7\\x8c|\\xc7\\x8a|\\xc7\\x8c|\"\n\"\\xc7\\xb0|j\\xcc\\x8c|J\\xcc\\x8c||\"\n\"\\xc7\\xb2|\\xc7\\xb3|\\xc7\\xb1|\\xc7\\xb3|\"\n\"\\xcd\\x85|\\xce\\xb9|\\xce\\x99||\"\n\"\\xce\\x90|\\xce\\xb9\\xcc\\x88\\xcc\\x81|\\xce\\x99\\xcc\\x88\\xcc\\x81||\"\n\"\\xce\\xb0|\\xcf\\x85\\xcc\\x88\\xcc\\x81|\\xce\\xa5\\xcc\\x88\\xcc\\x81||\"\n\"\\xcf\\x82|\\xcf\\x83|\\xce\\xa3||\"\n\"\\xcf\\x90|\\xce\\xb2|\\xce\\x92||\"\n\"\\xcf\\x91|\\xce\\xb8|\\xce\\x98||\"\n\"\\xcf\\x95|\\xcf\\x86|\\xce\\xa6||\"\n\"\\xcf\\x96|\\xcf\\x80|\\xce\\xa0||\"\n\"\\xcf\\xb0|\\xce\\xba|\\xce\\x9a||\"\n\"\\xcf\\xb1|\\xcf\\x81|\\xce\\xa1||\"\n\"\\xcf\\xb4|\\xce\\xb8||\\xce\\xb8|\"\n\"\\xcf\\xb5|\\xce\\xb5|\\xce\\x95||\"\n\"\\xd6\\x87|\\xd5\\xa5\\xd6\\x82|\\xd4\\xb5\\xd5\\x92||\"\n\"\\xe1\\x8e\\xa0|||\\xea\\xad\\xb0|\"\n\"\\xe1\\x8e\\xa1|||\\xea\\xad\\xb1|\"\n\"\\xe1\\x8e\\xa2|||\\xea\\xad\\xb2|\"\n\"\\xe1\\x8e\\xa3|||\\xea\\xad\\xb3|\"\n\"\\xe1\\x8e\\xa4|||\\xea\\xad\\xb4|\"\n\"\\xe1\\x8e\\xa5|||\\xea\\xad\\xb5|\"\n\"\\xe1\\x8e\\xa6|||\\xea\\xad\\xb6|\"\n\"\\xe1\\x8e\\xa7|||\\xea\\xad\\xb7|\"\n\"\\xe1\\x8e\\xa8|||\\xea\\xad\\xb8|\"\n\"\\xe1\\x8e\\xa9|||\\xea\\xad\\xb9|\"\n\"\\xe1\\x8e\\xaa|||\\xea\\xad\\xba|\"\n\"\\xe1\\x8e\\xab|||\\xea\\xad\\xbb|\"\n\"\\xe1\\x8e\\xac|||\\xea\\xad\\xbc|\"\n\"\\xe1\\x8e\\xad|||\\xea\\xad\\xbd|\"\n\"\\xe1\\x8e\\xae|||\\xea\\xad\\xbe|\"\n\"\\xe1\\x8e\\xaf|||\\xea\\xad\\xbf|\"\n\"\\xe1\\x8e\\xb0|||\\xea\\xae\\x80|\"\n\"\\xe1\\x8e\\xb1|||\\xea\\xae\\x81|\"\n\"\\xe1\\x8e\\xb2|||\\xea\\xae\\x82|\"\n\"\\xe1\\x8e\\xb3|||\\xea\\xae\\x83|\"\n\"\\xe1\\x8e\\xb4|||\\xea\\xae\\x84|\"\n\"\\xe1\\x8e\\xb5|||\\xea\\xae\\x85|\"\n\"\\xe1\\x8e\\xb6|||\\xea\\xae\\x86|\"\n\"\\xe1\\x8e\\xb7|||\\xea\\xae\\x87|\"\n\"\\xe1\\x8e\\xb8|||\\xea\\xae\\x88|\"\n\"\\xe1\\x8e\\xb9|||\\xea\\xae\\x89|\"\n\"\\xe1\\x8e\\xba|||\\xea\\xae\\x8a|\"\n\"\\xe1\\x8e\\xbb|||\\xea\\xae\\x8b|\"\n\"\\xe1\\x8e\\xbc|||\\xea\\xae\\x8c|\"\n\"\\xe1\\x8e\\xbd|||\\xea\\xae\\x8d|\"\n\"\\xe1\\x8e\\xbe|||\\xea\\xae\\x8e|\"\n\"\\xe1\\x8e\\xbf|||\\xea\\xae\\x8f|\"\n\"\\xe1\\x8f\\x80|||\\xea\\xae\\x90|\"\n\"\\xe1\\x8f\\x81|||\\xea\\xae\\x91|\"\n\"\\xe1\\x8f\\x82|||\\xea\\xae\\x92|\"\n\"\\xe1\\x8f\\x83|||\\xea\\xae\\x93|\"\n\"\\xe1\\x8f\\x84|||\\xea\\xae\\x94|\"\n\"\\xe1\\x8f\\x85|||\\xea\\xae\\x95|\"\n\"\\xe1\\x8f\\x86|||\\xea\\xae\\x96|\"\n\"\\xe1\\x8f\\x87|||\\xea\\xae\\x97|\"\n\"\\xe1\\x8f\\x88|||\\xea\\xae\\x98|\"\n\"\\xe1\\x8f\\x89|||\\xea\\xae\\x99|\"\n\"\\xe1\\x8f\\x8a|||\\xea\\xae\\x9a|\"\n\"\\xe1\\x8f\\x8b|||\\xea\\xae\\x9b|\"\n\"\\xe1\\x8f\\x8c|||\\xea\\xae\\x9c|\"\n\"\\xe1\\x8f\\x8d|||\\xea\\xae\\x9d|\"\n\"\\xe1\\x8f\\x8e|||\\xea\\xae\\x9e|\"\n\"\\xe1\\x8f\\x8f|||\\xea\\xae\\x9f|\"\n\"\\xe1\\x8f\\x90|||\\xea\\xae\\xa0|\"\n\"\\xe1\\x8f\\x91|||\\xea\\xae\\xa1|\"\n\"\\xe1\\x8f\\x92|||\\xea\\xae\\xa2|\"\n\"\\xe1\\x8f\\x93|||\\xea\\xae\\xa3|\"\n\"\\xe1\\x8f\\x94|||\\xea\\xae\\xa4|\"\n\"\\xe1\\x8f\\x95|||\\xea\\xae\\xa5|\"\n\"\\xe1\\x8f\\x96|||\\xea\\xae\\xa6|\"\n\"\\xe1\\x8f\\x97|||\\xea\\xae\\xa7|\"\n\"\\xe1\\x8f\\x98|||\\xea\\xae\\xa8|\"\n\"\\xe1\\x8f\\x99|||\\xea\\xae\\xa9|\"\n\"\\xe1\\x8f\\x9a|||\\xea\\xae\\xaa|\"\n\"\\xe1\\x8f\\x9b|||\\xea\\xae\\xab|\"\n\"\\xe1\\x8f\\x9c|||\\xea\\xae\\xac|\"\n\"\\xe1\\x8f\\x9d|||\\xea\\xae\\xad|\"\n\"\\xe1\\x8f\\x9e|||\\xea\\xae\\xae|\"\n\"\\xe1\\x8f\\x9f|||\\xea\\xae\\xaf|\"\n\"\\xe1\\x8f\\xa0|||\\xea\\xae\\xb0|\"\n\"\\xe1\\x8f\\xa1|||\\xea\\xae\\xb1|\"\n\"\\xe1\\x8f\\xa2|||\\xea\\xae\\xb2|\"\n\"\\xe1\\x8f\\xa3|||\\xea\\xae\\xb3|\"\n\"\\xe1\\x8f\\xa4|||\\xea\\xae\\xb4|\"\n\"\\xe1\\x8f\\xa5|||\\xea\\xae\\xb5|\"\n\"\\xe1\\x8f\\xa6|||\\xea\\xae\\xb6|\"\n\"\\xe1\\x8f\\xa7|||\\xea\\xae\\xb7|\"\n\"\\xe1\\x8f\\xa8|||\\xea\\xae\\xb8|\"\n\"\\xe1\\x8f\\xa9|||\\xea\\xae\\xb9|\"\n\"\\xe1\\x8f\\xaa|||\\xea\\xae\\xba|\"\n\"\\xe1\\x8f\\xab|||\\xea\\xae\\xbb|\"\n\"\\xe1\\x8f\\xac|||\\xea\\xae\\xbc|\"\n\"\\xe1\\x8f\\xad|||\\xea\\xae\\xbd|\"\n\"\\xe1\\x8f\\xae|||\\xea\\xae\\xbe|\"\n\"\\xe1\\x8f\\xaf|||\\xea\\xae\\xbf|\"\n\"\\xe1\\x8f\\xb0|||\\xe1\\x8f\\xb8|\"\n\"\\xe1\\x8f\\xb1|||\\xe1\\x8f\\xb9|\"\n\"\\xe1\\x8f\\xb2|||\\xe1\\x8f\\xba|\"\n\"\\xe1\\x8f\\xb3|||\\xe1\\x8f\\xbb|\"\n\"\\xe1\\x8f\\xb4|||\\xe1\\x8f\\xbc|\"\n\"\\xe1\\x8f\\xb5|||\\xe1\\x8f\\xbd|\"\n\"\\xe1\\x8f\\xb8|\\xe1\\x8f\\xb0|\\xe1\\x8f\\xb0||\"\n\"\\xe1\\x8f\\xb9|\\xe1\\x8f\\xb1|\\xe1\\x8f\\xb1||\"\n\"\\xe1\\x8f\\xba|\\xe1\\x8f\\xb2|\\xe1\\x8f\\xb2||\"\n\"\\xe1\\x8f\\xbb|\\xe1\\x8f\\xb3|\\xe1\\x8f\\xb3||\"\n\"\\xe1\\x8f\\xbc|\\xe1\\x8f\\xb4|\\xe1\\x8f\\xb4||\"\n\"\\xe1\\x8f\\xbd|\\xe1\\x8f\\xb5|\\xe1\\x8f\\xb5||\"\n\"\\xe1\\xb2\\x80|\\xd0\\xb2|\\xd0\\x92||\"\n\"\\xe1\\xb2\\x81|\\xd0\\xb4|\\xd0\\x94||\"\n\"\\xe1\\xb2\\x82|\\xd0\\xbe|\\xd0\\x9e||\"\n\"\\xe1\\xb2\\x83|\\xd1\\x81|\\xd0\\xa1||\"\n\"\\xe1\\xb2\\x84|\\xd1\\x82|\\xd0\\xa2||\"\n\"\\xe1\\xb2\\x85|\\xd1\\x82|\\xd0\\xa2||\"\n\"\\xe1\\xb2\\x86|\\xd1\\x8a|\\xd0\\xaa||\"\n\"\\xe1\\xb2\\x87|\\xd1\\xa3|\\xd1\\xa2||\"\n\"\\xe1\\xb2\\x88|\\xea\\x99\\x8b|\\xea\\x99\\x8a||\"\n\"\\xe1\\xba\\x96|h\\xcc\\xb1|H\\xcc\\xb1||\"\n\"\\xe1\\xba\\x97|t\\xcc\\x88|T\\xcc\\x88||\"\n\"\\xe1\\xba\\x98|w\\xcc\\x8a|W\\xcc\\x8a||\"\n\"\\xe1\\xba\\x99|y\\xcc\\x8a|Y\\xcc\\x8a||\"\n\"\\xe1\\xba\\x9a|a\\xca\\xbe|A\\xca\\xbe||\"\n\"\\xe1\\xba\\x9b|\\xe1\\xb9\\xa1|\\xe1\\xb9\\xa0||\"\n\"\\xe1\\xba\\x9e|ss||\\xc3\\x9f|\"\n\"\\xe1\\xbd\\x90|\\xcf\\x85\\xcc\\x93|\\xce\\xa5\\xcc\\x93||\"\n\"\\xe1\\xbd\\x92|\\xcf\\x85\\xcc\\x93\\xcc\\x80|\\xce\\xa5\\xcc\\x93\\xcc\\x80||\"\n\"\\xe1\\xbd\\x94|\\xcf\\x85\\xcc\\x93\\xcc\\x81|\\xce\\xa5\\xcc\\x93\\xcc\\x81||\"\n\"\\xe1\\xbd\\x96|\\xcf\\x85\\xcc\\x93\\xcd\\x82|\\xce\\xa5\\xcc\\x93\\xcd\\x82||\"\n\"\\xe1\\xbe\\x80|\\xe1\\xbc\\x80\\xce\\xb9|\\xe1\\xbc\\x88\\xce\\x99||\"\n\"\\xe1\\xbe\\x81|\\xe1\\xbc\\x81\\xce\\xb9|\\xe1\\xbc\\x89\\xce\\x99||\"\n\"\\xe1\\xbe\\x82|\\xe1\\xbc\\x82\\xce\\xb9|\\xe1\\xbc\\x8a\\xce\\x99||\"\n\"\\xe1\\xbe\\x83|\\xe1\\xbc\\x83\\xce\\xb9|\\xe1\\xbc\\x8b\\xce\\x99||\"\n\"\\xe1\\xbe\\x84|\\xe1\\xbc\\x84\\xce\\xb9|\\xe1\\xbc\\x8c\\xce\\x99||\"\n\"\\xe1\\xbe\\x85|\\xe1\\xbc\\x85\\xce\\xb9|\\xe1\\xbc\\x8d\\xce\\x99||\"\n\"\\xe1\\xbe\\x86|\\xe1\\xbc\\x86\\xce\\xb9|\\xe1\\xbc\\x8e\\xce\\x99||\"\n\"\\xe1\\xbe\\x87|\\xe1\\xbc\\x87\\xce\\xb9|\\xe1\\xbc\\x8f\\xce\\x99||\"\n\"\\xe1\\xbe\\x88|\\xe1\\xbc\\x80\\xce\\xb9|\\xe1\\xbc\\x88\\xce\\x99|\\xe1\\xbe\\x80|\"\n\"\\xe1\\xbe\\x89|\\xe1\\xbc\\x81\\xce\\xb9|\\xe1\\xbc\\x89\\xce\\x99|\\xe1\\xbe\\x81|\"\n\"\\xe1\\xbe\\x8a|\\xe1\\xbc\\x82\\xce\\xb9|\\xe1\\xbc\\x8a\\xce\\x99|\\xe1\\xbe\\x82|\"\n\"\\xe1\\xbe\\x8b|\\xe1\\xbc\\x83\\xce\\xb9|\\xe1\\xbc\\x8b\\xce\\x99|\\xe1\\xbe\\x83|\"\n\"\\xe1\\xbe\\x8c|\\xe1\\xbc\\x84\\xce\\xb9|\\xe1\\xbc\\x8c\\xce\\x99|\\xe1\\xbe\\x84|\"\n\"\\xe1\\xbe\\x8d|\\xe1\\xbc\\x85\\xce\\xb9|\\xe1\\xbc\\x8d\\xce\\x99|\\xe1\\xbe\\x85|\"\n\"\\xe1\\xbe\\x8e|\\xe1\\xbc\\x86\\xce\\xb9|\\xe1\\xbc\\x8e\\xce\\x99|\\xe1\\xbe\\x86|\"\n\"\\xe1\\xbe\\x8f|\\xe1\\xbc\\x87\\xce\\xb9|\\xe1\\xbc\\x8f\\xce\\x99|\\xe1\\xbe\\x87|\"\n\"\\xe1\\xbe\\x90|\\xe1\\xbc\\xa0\\xce\\xb9|\\xe1\\xbc\\xa8\\xce\\x99||\"\n\"\\xe1\\xbe\\x91|\\xe1\\xbc\\xa1\\xce\\xb9|\\xe1\\xbc\\xa9\\xce\\x99||\"\n\"\\xe1\\xbe\\x92|\\xe1\\xbc\\xa2\\xce\\xb9|\\xe1\\xbc\\xaa\\xce\\x99||\"\n\"\\xe1\\xbe\\x93|\\xe1\\xbc\\xa3\\xce\\xb9|\\xe1\\xbc\\xab\\xce\\x99||\"\n\"\\xe1\\xbe\\x94|\\xe1\\xbc\\xa4\\xce\\xb9|\\xe1\\xbc\\xac\\xce\\x99||\"\n\"\\xe1\\xbe\\x95|\\xe1\\xbc\\xa5\\xce\\xb9|\\xe1\\xbc\\xad\\xce\\x99||\"\n\"\\xe1\\xbe\\x96|\\xe1\\xbc\\xa6\\xce\\xb9|\\xe1\\xbc\\xae\\xce\\x99||\"\n\"\\xe1\\xbe\\x97|\\xe1\\xbc\\xa7\\xce\\xb9|\\xe1\\xbc\\xaf\\xce\\x99||\"\n\"\\xe1\\xbe\\x98|\\xe1\\xbc\\xa0\\xce\\xb9|\\xe1\\xbc\\xa8\\xce\\x99|\\xe1\\xbe\\x90|\"\n\"\\xe1\\xbe\\x99|\\xe1\\xbc\\xa1\\xce\\xb9|\\xe1\\xbc\\xa9\\xce\\x99|\\xe1\\xbe\\x91|\"\n\"\\xe1\\xbe\\x9a|\\xe1\\xbc\\xa2\\xce\\xb9|\\xe1\\xbc\\xaa\\xce\\x99|\\xe1\\xbe\\x92|\"\n\"\\xe1\\xbe\\x9b|\\xe1\\xbc\\xa3\\xce\\xb9|\\xe1\\xbc\\xab\\xce\\x99|\\xe1\\xbe\\x93|\"\n\"\\xe1\\xbe\\x9c|\\xe1\\xbc\\xa4\\xce\\xb9|\\xe1\\xbc\\xac\\xce\\x99|\\xe1\\xbe\\x94|\"\n\"\\xe1\\xbe\\x9d|\\xe1\\xbc\\xa5\\xce\\xb9|\\xe1\\xbc\\xad\\xce\\x99|\\xe1\\xbe\\x95|\"\n\"\\xe1\\xbe\\x9e|\\xe1\\xbc\\xa6\\xce\\xb9|\\xe1\\xbc\\xae\\xce\\x99|\\xe1\\xbe\\x96|\"\n\"\\xe1\\xbe\\x9f|\\xe1\\xbc\\xa7\\xce\\xb9|\\xe1\\xbc\\xaf\\xce\\x99|\\xe1\\xbe\\x97|\"\n\"\\xe1\\xbe\\xa0|\\xe1\\xbd\\xa0\\xce\\xb9|\\xe1\\xbd\\xa8\\xce\\x99||\"\n\"\\xe1\\xbe\\xa1|\\xe1\\xbd\\xa1\\xce\\xb9|\\xe1\\xbd\\xa9\\xce\\x99||\"\n\"\\xe1\\xbe\\xa2|\\xe1\\xbd\\xa2\\xce\\xb9|\\xe1\\xbd\\xaa\\xce\\x99||\"\n\"\\xe1\\xbe\\xa3|\\xe1\\xbd\\xa3\\xce\\xb9|\\xe1\\xbd\\xab\\xce\\x99||\"\n\"\\xe1\\xbe\\xa4|\\xe1\\xbd\\xa4\\xce\\xb9|\\xe1\\xbd\\xac\\xce\\x99||\"\n\"\\xe1\\xbe\\xa5|\\xe1\\xbd\\xa5\\xce\\xb9|\\xe1\\xbd\\xad\\xce\\x99||\"\n\"\\xe1\\xbe\\xa6|\\xe1\\xbd\\xa6\\xce\\xb9|\\xe1\\xbd\\xae\\xce\\x99||\"\n\"\\xe1\\xbe\\xa7|\\xe1\\xbd\\xa7\\xce\\xb9|\\xe1\\xbd\\xaf\\xce\\x99||\"\n\"\\xe1\\xbe\\xa8|\\xe1\\xbd\\xa0\\xce\\xb9|\\xe1\\xbd\\xa8\\xce\\x99|\\xe1\\xbe\\xa0|\"\n\"\\xe1\\xbe\\xa9|\\xe1\\xbd\\xa1\\xce\\xb9|\\xe1\\xbd\\xa9\\xce\\x99|\\xe1\\xbe\\xa1|\"\n\"\\xe1\\xbe\\xaa|\\xe1\\xbd\\xa2\\xce\\xb9|\\xe1\\xbd\\xaa\\xce\\x99|\\xe1\\xbe\\xa2|\"\n\"\\xe1\\xbe\\xab|\\xe1\\xbd\\xa3\\xce\\xb9|\\xe1\\xbd\\xab\\xce\\x99|\\xe1\\xbe\\xa3|\"\n\"\\xe1\\xbe\\xac|\\xe1\\xbd\\xa4\\xce\\xb9|\\xe1\\xbd\\xac\\xce\\x99|\\xe1\\xbe\\xa4|\"\n\"\\xe1\\xbe\\xad|\\xe1\\xbd\\xa5\\xce\\xb9|\\xe1\\xbd\\xad\\xce\\x99|\\xe1\\xbe\\xa5|\"\n\"\\xe1\\xbe\\xae|\\xe1\\xbd\\xa6\\xce\\xb9|\\xe1\\xbd\\xae\\xce\\x99|\\xe1\\xbe\\xa6|\"\n\"\\xe1\\xbe\\xaf|\\xe1\\xbd\\xa7\\xce\\xb9|\\xe1\\xbd\\xaf\\xce\\x99|\\xe1\\xbe\\xa7|\"\n\"\\xe1\\xbe\\xb2|\\xe1\\xbd\\xb0\\xce\\xb9|\\xe1\\xbe\\xba\\xce\\x99||\"\n\"\\xe1\\xbe\\xb3|\\xce\\xb1\\xce\\xb9|\\xce\\x91\\xce\\x99||\"\n\"\\xe1\\xbe\\xb4|\\xce\\xac\\xce\\xb9|\\xce\\x86\\xce\\x99||\"\n\"\\xe1\\xbe\\xb6|\\xce\\xb1\\xcd\\x82|\\xce\\x91\\xcd\\x82||\"\n\"\\xe1\\xbe\\xb7|\\xce\\xb1\\xcd\\x82\\xce\\xb9|\\xce\\x91\\xcd\\x82\\xce\\x99||\"\n\"\\xe1\\xbe\\xbc|\\xce\\xb1\\xce\\xb9|\\xce\\x91\\xce\\x99|\\xe1\\xbe\\xb3|\"\n\"\\xe1\\xbe\\xbe|\\xce\\xb9|\\xce\\x99||\"\n\"\\xe1\\xbf\\x82|\\xe1\\xbd\\xb4\\xce\\xb9|\\xe1\\xbf\\x8a\\xce\\x99||\"\n\"\\xe1\\xbf\\x83|\\xce\\xb7\\xce\\xb9|\\xce\\x97\\xce\\x99||\"\n\"\\xe1\\xbf\\x84|\\xce\\xae\\xce\\xb9|\\xce\\x89\\xce\\x99||\"\n\"\\xe1\\xbf\\x86|\\xce\\xb7\\xcd\\x82|\\xce\\x97\\xcd\\x82||\"\n\"\\xe1\\xbf\\x87|\\xce\\xb7\\xcd\\x82\\xce\\xb9|\\xce\\x97\\xcd\\x82\\xce\\x99||\"\n\"\\xe1\\xbf\\x8c|\\xce\\xb7\\xce\\xb9|\\xce\\x97\\xce\\x99|\\xe1\\xbf\\x83|\"\n\"\\xe1\\xbf\\x92|\\xce\\xb9\\xcc\\x88\\xcc\\x80|\\xce\\x99\\xcc\\x88\\xcc\\x80||\"\n\"\\xe1\\xbf\\x93|\\xce\\xb9\\xcc\\x88\\xcc\\x81|\\xce\\x99\\xcc\\x88\\xcc\\x81||\"\n\"\\xe1\\xbf\\x96|\\xce\\xb9\\xcd\\x82|\\xce\\x99\\xcd\\x82||\"\n\"\\xe1\\xbf\\x97|\\xce\\xb9\\xcc\\x88\\xcd\\x82|\\xce\\x99\\xcc\\x88\\xcd\\x82||\"\n\"\\xe1\\xbf\\xa2|\\xcf\\x85\\xcc\\x88\\xcc\\x80|\\xce\\xa5\\xcc\\x88\\xcc\\x80||\"\n\"\\xe1\\xbf\\xa3|\\xcf\\x85\\xcc\\x88\\xcc\\x81|\\xce\\xa5\\xcc\\x88\\xcc\\x81||\"\n\"\\xe1\\xbf\\xa4|\\xcf\\x81\\xcc\\x93|\\xce\\xa1\\xcc\\x93||\"\n\"\\xe1\\xbf\\xa6|\\xcf\\x85\\xcd\\x82|\\xce\\xa5\\xcd\\x82||\"\n\"\\xe1\\xbf\\xa7|\\xcf\\x85\\xcc\\x88\\xcd\\x82|\\xce\\xa5\\xcc\\x88\\xcd\\x82||\"\n\"\\xe1\\xbf\\xb2|\\xe1\\xbd\\xbc\\xce\\xb9|\\xe1\\xbf\\xba\\xce\\x99||\"\n\"\\xe1\\xbf\\xb3|\\xcf\\x89\\xce\\xb9|\\xce\\xa9\\xce\\x99||\"\n\"\\xe1\\xbf\\xb4|\\xcf\\x8e\\xce\\xb9|\\xce\\x8f\\xce\\x99||\"\n\"\\xe1\\xbf\\xb6|\\xcf\\x89\\xcd\\x82|\\xce\\xa9\\xcd\\x82||\"\n\"\\xe1\\xbf\\xb7|\\xcf\\x89\\xcd\\x82\\xce\\xb9|\\xce\\xa9\\xcd\\x82\\xce\\x99||\"\n\"\\xe1\\xbf\\xbc|\\xcf\\x89\\xce\\xb9|\\xce\\xa9\\xce\\x99|\\xe1\\xbf\\xb3|\"\n\"\\xe2\\x84\\xa6|\\xcf\\x89||\\xcf\\x89|\"\n\"\\xe2\\x84\\xaa|k||k|\"\n\"\\xe2\\x84\\xab|\\xc3\\xa5||\\xc3\\xa5|\"\n\"\\xea\\xad\\xb0|\\xe1\\x8e\\xa0|\\xe1\\x8e\\xa0||\"\n\"\\xea\\xad\\xb1|\\xe1\\x8e\\xa1|\\xe1\\x8e\\xa1||\"\n\"\\xea\\xad\\xb2|\\xe1\\x8e\\xa2|\\xe1\\x8e\\xa2||\"\n\"\\xea\\xad\\xb3|\\xe1\\x8e\\xa3|\\xe1\\x8e\\xa3||\"\n\"\\xea\\xad\\xb4|\\xe1\\x8e\\xa4|\\xe1\\x8e\\xa4||\"\n\"\\xea\\xad\\xb5|\\xe1\\x8e\\xa5|\\xe1\\x8e\\xa5||\"\n\"\\xea\\xad\\xb6|\\xe1\\x8e\\xa6|\\xe1\\x8e\\xa6||\"\n\"\\xea\\xad\\xb7|\\xe1\\x8e\\xa7|\\xe1\\x8e\\xa7||\"\n\"\\xea\\xad\\xb8|\\xe1\\x8e\\xa8|\\xe1\\x8e\\xa8||\"\n\"\\xea\\xad\\xb9|\\xe1\\x8e\\xa9|\\xe1\\x8e\\xa9||\"\n\"\\xea\\xad\\xba|\\xe1\\x8e\\xaa|\\xe1\\x8e\\xaa||\"\n\"\\xea\\xad\\xbb|\\xe1\\x8e\\xab|\\xe1\\x8e\\xab||\"\n\"\\xea\\xad\\xbc|\\xe1\\x8e\\xac|\\xe1\\x8e\\xac||\"\n\"\\xea\\xad\\xbd|\\xe1\\x8e\\xad|\\xe1\\x8e\\xad||\"\n\"\\xea\\xad\\xbe|\\xe1\\x8e\\xae|\\xe1\\x8e\\xae||\"\n\"\\xea\\xad\\xbf|\\xe1\\x8e\\xaf|\\xe1\\x8e\\xaf||\"\n\"\\xea\\xae\\x80|\\xe1\\x8e\\xb0|\\xe1\\x8e\\xb0||\"\n\"\\xea\\xae\\x81|\\xe1\\x8e\\xb1|\\xe1\\x8e\\xb1||\"\n\"\\xea\\xae\\x82|\\xe1\\x8e\\xb2|\\xe1\\x8e\\xb2||\"\n\"\\xea\\xae\\x83|\\xe1\\x8e\\xb3|\\xe1\\x8e\\xb3||\"\n\"\\xea\\xae\\x84|\\xe1\\x8e\\xb4|\\xe1\\x8e\\xb4||\"\n\"\\xea\\xae\\x85|\\xe1\\x8e\\xb5|\\xe1\\x8e\\xb5||\"\n\"\\xea\\xae\\x86|\\xe1\\x8e\\xb6|\\xe1\\x8e\\xb6||\"\n\"\\xea\\xae\\x87|\\xe1\\x8e\\xb7|\\xe1\\x8e\\xb7||\"\n\"\\xea\\xae\\x88|\\xe1\\x8e\\xb8|\\xe1\\x8e\\xb8||\"\n\"\\xea\\xae\\x89|\\xe1\\x8e\\xb9|\\xe1\\x8e\\xb9||\"\n\"\\xea\\xae\\x8a|\\xe1\\x8e\\xba|\\xe1\\x8e\\xba||\"\n\"\\xea\\xae\\x8b|\\xe1\\x8e\\xbb|\\xe1\\x8e\\xbb||\"\n\"\\xea\\xae\\x8c|\\xe1\\x8e\\xbc|\\xe1\\x8e\\xbc||\"\n\"\\xea\\xae\\x8d|\\xe1\\x8e\\xbd|\\xe1\\x8e\\xbd||\"\n\"\\xea\\xae\\x8e|\\xe1\\x8e\\xbe|\\xe1\\x8e\\xbe||\"\n\"\\xea\\xae\\x8f|\\xe1\\x8e\\xbf|\\xe1\\x8e\\xbf||\"\n\"\\xea\\xae\\x90|\\xe1\\x8f\\x80|\\xe1\\x8f\\x80||\"\n\"\\xea\\xae\\x91|\\xe1\\x8f\\x81|\\xe1\\x8f\\x81||\"\n\"\\xea\\xae\\x92|\\xe1\\x8f\\x82|\\xe1\\x8f\\x82||\"\n\"\\xea\\xae\\x93|\\xe1\\x8f\\x83|\\xe1\\x8f\\x83||\"\n\"\\xea\\xae\\x94|\\xe1\\x8f\\x84|\\xe1\\x8f\\x84||\"\n\"\\xea\\xae\\x95|\\xe1\\x8f\\x85|\\xe1\\x8f\\x85||\"\n\"\\xea\\xae\\x96|\\xe1\\x8f\\x86|\\xe1\\x8f\\x86||\"\n\"\\xea\\xae\\x97|\\xe1\\x8f\\x87|\\xe1\\x8f\\x87||\"\n\"\\xea\\xae\\x98|\\xe1\\x8f\\x88|\\xe1\\x8f\\x88||\"\n\"\\xea\\xae\\x99|\\xe1\\x8f\\x89|\\xe1\\x8f\\x89||\"\n\"\\xea\\xae\\x9a|\\xe1\\x8f\\x8a|\\xe1\\x8f\\x8a||\"\n\"\\xea\\xae\\x9b|\\xe1\\x8f\\x8b|\\xe1\\x8f\\x8b||\"\n\"\\xea\\xae\\x9c|\\xe1\\x8f\\x8c|\\xe1\\x8f\\x8c||\"\n\"\\xea\\xae\\x9d|\\xe1\\x8f\\x8d|\\xe1\\x8f\\x8d||\"\n\"\\xea\\xae\\x9e|\\xe1\\x8f\\x8e|\\xe1\\x8f\\x8e||\"\n\"\\xea\\xae\\x9f|\\xe1\\x8f\\x8f|\\xe1\\x8f\\x8f||\"\n\"\\xea\\xae\\xa0|\\xe1\\x8f\\x90|\\xe1\\x8f\\x90||\"\n\"\\xea\\xae\\xa1|\\xe1\\x8f\\x91|\\xe1\\x8f\\x91||\"\n\"\\xea\\xae\\xa2|\\xe1\\x8f\\x92|\\xe1\\x8f\\x92||\"\n\"\\xea\\xae\\xa3|\\xe1\\x8f\\x93|\\xe1\\x8f\\x93||\"\n\"\\xea\\xae\\xa4|\\xe1\\x8f\\x94|\\xe1\\x8f\\x94||\"\n\"\\xea\\xae\\xa5|\\xe1\\x8f\\x95|\\xe1\\x8f\\x95||\"\n\"\\xea\\xae\\xa6|\\xe1\\x8f\\x96|\\xe1\\x8f\\x96||\"\n\"\\xea\\xae\\xa7|\\xe1\\x8f\\x97|\\xe1\\x8f\\x97||\"\n\"\\xea\\xae\\xa8|\\xe1\\x8f\\x98|\\xe1\\x8f\\x98||\"\n\"\\xea\\xae\\xa9|\\xe1\\x8f\\x99|\\xe1\\x8f\\x99||\"\n\"\\xea\\xae\\xaa|\\xe1\\x8f\\x9a|\\xe1\\x8f\\x9a||\"\n\"\\xea\\xae\\xab|\\xe1\\x8f\\x9b|\\xe1\\x8f\\x9b||\"\n\"\\xea\\xae\\xac|\\xe1\\x8f\\x9c|\\xe1\\x8f\\x9c||\"\n\"\\xea\\xae\\xad|\\xe1\\x8f\\x9d|\\xe1\\x8f\\x9d||\"\n\"\\xea\\xae\\xae|\\xe1\\x8f\\x9e|\\xe1\\x8f\\x9e||\"\n\"\\xea\\xae\\xaf|\\xe1\\x8f\\x9f|\\xe1\\x8f\\x9f||\"\n\"\\xea\\xae\\xb0|\\xe1\\x8f\\xa0|\\xe1\\x8f\\xa0||\"\n\"\\xea\\xae\\xb1|\\xe1\\x8f\\xa1|\\xe1\\x8f\\xa1||\"\n\"\\xea\\xae\\xb2|\\xe1\\x8f\\xa2|\\xe1\\x8f\\xa2||\"\n\"\\xea\\xae\\xb3|\\xe1\\x8f\\xa3|\\xe1\\x8f\\xa3||\"\n\"\\xea\\xae\\xb4|\\xe1\\x8f\\xa4|\\xe1\\x8f\\xa4||\"\n\"\\xea\\xae\\xb5|\\xe1\\x8f\\xa5|\\xe1\\x8f\\xa5||\"\n\"\\xea\\xae\\xb6|\\xe1\\x8f\\xa6|\\xe1\\x8f\\xa6||\"\n\"\\xea\\xae\\xb7|\\xe1\\x8f\\xa7|\\xe1\\x8f\\xa7||\"\n\"\\xea\\xae\\xb8|\\xe1\\x8f\\xa8|\\xe1\\x8f\\xa8||\"\n\"\\xea\\xae\\xb9|\\xe1\\x8f\\xa9|\\xe1\\x8f\\xa9||\"\n\"\\xea\\xae\\xba|\\xe1\\x8f\\xaa|\\xe1\\x8f\\xaa||\"\n\"\\xea\\xae\\xbb|\\xe1\\x8f\\xab|\\xe1\\x8f\\xab||\"\n\"\\xea\\xae\\xbc|\\xe1\\x8f\\xac|\\xe1\\x8f\\xac||\"\n\"\\xea\\xae\\xbd|\\xe1\\x8f\\xad|\\xe1\\x8f\\xad||\"\n\"\\xea\\xae\\xbe|\\xe1\\x8f\\xae|\\xe1\\x8f\\xae||\"\n\"\\xea\\xae\\xbf|\\xe1\\x8f\\xaf|\\xe1\\x8f\\xaf||\"\n\"\\xef\\xac\\x80|ff|FF||\"\n\"\\xef\\xac\\x81|fi|FI||\"\n\"\\xef\\xac\\x82|fl|FL||\"\n\"\\xef\\xac\\x83|ffi|FFI||\"\n\"\\xef\\xac\\x84|ffl|FFL||\"\n\"\\xef\\xac\\x85|st|ST||\"\n\"\\xef\\xac\\x86|st|ST||\"\n\"\\xef\\xac\\x93|\\xd5\\xb4\\xd5\\xb6|\\xd5\\x84\\xd5\\x86||\"\n\"\\xef\\xac\\x94|\\xd5\\xb4\\xd5\\xa5|\\xd5\\x84\\xd4\\xb5||\"\n\"\\xef\\xac\\x95|\\xd5\\xb4\\xd5\\xab|\\xd5\\x84\\xd4\\xbb||\"\n\"\\xef\\xac\\x96|\\xd5\\xbe\\xd5\\xb6|\\xd5\\x8e\\xd5\\x86||\"\n\"\\xef\\xac\\x97|\\xd5\\xb4\\xd5\\xad|\\xd5\\x84\\xd4\\xbd||\"\n\n//--Autogenerated -- end of section automatically generated\n;\n\n// Maximum length of a case conversion result is 6 bytes in UTF-8\nconstexpr size_t maxConversionLength = 6;\n\nclass CaseConverter final : public ICaseConverter {\n\tstruct ConversionString {\n\t\tchar conversion[maxConversionLength+1]{};\n\t};\n\t// Conversions are initially store in a vector of structs but then decomposed into\n\t// parallel arrays as that is about 10% faster to search.\n\tstruct CharacterConversion {\n\t\tint character = 0;\n\t\tConversionString conversion;\n\t\t// Empty case: NUL -> \"\".\n\t\tCharacterConversion() noexcept = default;\n\t\tCharacterConversion(int character_, std::string_view conversion_) noexcept : character(character_) {\n\t\t\tassert(conversion_.length() <= maxConversionLength);\n\t\t\ttry {\n\t\t\t\t// This can never fail as std::string_view::copy should only throw\n\t\t\t\t// std::out_of_range if pos > size() and pos == 0 here\n\t\t\t\tconversion_.copy(conversion.conversion, conversion_.length());\n\t\t\t} catch (...) {\n\t\t\t\t// Ignore any exception\n\t\t\t}\n\t\t}\n\t\tbool operator<(const CharacterConversion &other) const noexcept {\n\t\t\treturn character < other.character;\n\t\t}\n\t};\n\tusing CharacterToConversion = std::vector<CharacterConversion>;\n\tCharacterToConversion characterToConversion;\n\t// The parallel arrays\n\tstd::vector<int> characters;\n\tstd::vector<ConversionString> conversions;\n\npublic:\n\tCaseConverter() noexcept = default;\n\t[[nodiscard]] bool Initialised() const noexcept {\n\t\treturn !characters.empty();\n\t}\n\tvoid Add(int character, std::string_view conversion_) {\n\t\tcharacterToConversion.emplace_back(character, conversion_);\n\t}\n\tconst char *Find(int character) {\n\t\tconst std::vector<int>::iterator it = std::lower_bound(characters.begin(), characters.end(), character);\n\t\tif (it == characters.end())\n\t\t\treturn nullptr;\n\t\telse if (*it == character)\n\t\t\treturn conversions[it - characters.begin()].conversion;\n\t\telse\n\t\t\treturn nullptr;\n\t}\n\tsize_t CaseConvertString(char *converted, size_t sizeConverted, const char *mixed, size_t lenMixed) override {\n\t\tsize_t lenConverted = 0;\n\t\tsize_t mixedPos = 0;\n\t\tunsigned char bytes[UTF8MaxBytes + 1]{};\n\t\twhile (mixedPos < lenMixed) {\n\t\t\tconst unsigned char leadByte = mixed[mixedPos];\n\t\t\tconst char *caseConverted = nullptr;\n\t\t\tsize_t lenMixedChar = 1;\n\t\t\tif (UTF8IsAscii(leadByte)) {\n\t\t\t\tcaseConverted = Find(leadByte);\n\t\t\t} else {\n\t\t\t\tbytes[0] = leadByte;\n\t\t\t\tconst int widthCharBytes = UTF8BytesOfLead[leadByte];\n\t\t\t\tfor (int b=1; b<widthCharBytes; b++) {\n\t\t\t\t\tbytes[b] = (mixedPos+b < lenMixed) ? mixed[mixedPos+b] : 0;\n\t\t\t\t}\n\t\t\t\tconst int classified = UTF8Classify(bytes, widthCharBytes);\n\t\t\t\tif (!(classified & UTF8MaskInvalid)) {\n\t\t\t\t\t// valid UTF-8\n\t\t\t\t\tlenMixedChar = classified & UTF8MaskWidth;\n\t\t\t\t\tconst int character = UnicodeFromUTF8(bytes);\n\t\t\t\t\tcaseConverted = Find(character);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (caseConverted) {\n\t\t\t\t// Character has a conversion so copy that conversion in\n\t\t\t\twhile (*caseConverted) {\n\t\t\t\t\tconverted[lenConverted++] = *caseConverted++;\n\t\t\t\t\tif (lenConverted >= sizeConverted)\n\t\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Character has no conversion so copy the input to output\n\t\t\t\tfor (size_t i=0; i<lenMixedChar; i++) {\n\t\t\t\t\tconverted[lenConverted++] = mixed[mixedPos+i];\n\t\t\t\t\tif (lenConverted >= sizeConverted)\n\t\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\tmixedPos += lenMixedChar;\n\t\t}\n\t\treturn lenConverted;\n\t}\n\tvoid FinishedAdding() {\n\t\tstd::sort(characterToConversion.begin(), characterToConversion.end());\n\t\tcharacters.reserve(characterToConversion.size());\n\t\tconversions.reserve(characterToConversion.size());\n\t\tfor (const CharacterConversion &chConv : characterToConversion) {\n\t\t\tcharacters.push_back(chConv.character);\n\t\t\tconversions.push_back(chConv.conversion);\n\t\t}\n\t\t// Empty the original calculated data completely\n\t\tCharacterToConversion().swap(characterToConversion);\n\t}\n\tvoid AddSymmetric(CaseConversion conversion, int lower, int upper);\n\tvoid SetupConversions(CaseConversion conversion);\n};\n\nCaseConverter caseConvList[3];\n\nvoid CaseConverter::AddSymmetric(CaseConversion conversion, int lower, int upper) {\n\tconst int character = (conversion == CaseConversion::upper) ? lower : upper;\n\tconst int source = (conversion == CaseConversion::upper) ? upper : lower;\n\tchar converted[maxConversionLength+1]{};\n\tUTF8FromUTF32Character(source, converted);\n\tAdd(character, converted);\n}\n\n// Return the next '|' separated field and remove from view.\nstd::string_view NextField(std::string_view &view) {\n\tconst size_t separatorPosition = view.find_first_of('|');\n\tconst std::string_view field = view.substr(0, separatorPosition);\n\tif (separatorPosition == std::string_view::npos) {\n\t\t// Reached the end so empty the view\n\t\tview.remove_prefix(view.length());\n\t} else {\n\t\t// Remove the '|' from the view as well as the field\n\t\tview.remove_prefix(separatorPosition + 1);\n\t}\n\treturn field;\n}\n\nvoid CaseConverter::SetupConversions(CaseConversion conversion) {\n\t// First initialize for the symmetric ranges\n\tfor (size_t i=0; i<std::size(symmetricCaseConversionRanges);) {\n\t\tconst int lower = symmetricCaseConversionRanges[i++];\n\t\tconst int upper = symmetricCaseConversionRanges[i++];\n\t\tconst int length = symmetricCaseConversionRanges[i++];\n\t\tconst int pitch = symmetricCaseConversionRanges[i++];\n\t\tfor (int j=0; j<length*pitch; j+=pitch) {\n\t\t\tAddSymmetric(conversion, lower+j, upper+j);\n\t\t}\n\t}\n\t// Add the symmetric singletons\n\tfor (size_t i=0; i<std::size(symmetricCaseConversions);) {\n\t\tconst int lower = symmetricCaseConversions[i++];\n\t\tconst int upper = symmetricCaseConversions[i++];\n\t\tAddSymmetric(conversion, lower, upper);\n\t}\n\t// Add the complex cases\n\tstd::string_view sComplex = complexCaseConversions;\n\twhile (!sComplex.empty()) {\n\t\tconst std::string_view originUTF8 = NextField(sComplex);\n\t\tconst std::string_view foldedUTF8 = NextField(sComplex);\n\t\tconst std::string_view upperUTF8 = NextField(sComplex);\n\t\tconst std::string_view lowerUTF8 = NextField(sComplex);\n\n\t\tstd::string_view converted;\n\t\tswitch (conversion) {\n\t\tcase CaseConversion::fold:\n\t\t\tconverted = foldedUTF8;\n\t\t\tbreak;\n\t\tcase CaseConversion::upper:\n\t\t\tconverted = upperUTF8;\n\t\t\tbreak;\n\t\tcase CaseConversion::lower:\n\t\tdefault:\n\t\t\tconverted = lowerUTF8;\n\t\t\tbreak;\n\t\t}\n\t\tif (!converted.empty()) {\n\t\t\tconst int character = UnicodeFromUTF8(originUTF8);\n\t\t\tAdd(character, converted);\n\t\t}\n\t}\n\n\tFinishedAdding();\n}\n\nCaseConverter *ConverterForConversion(CaseConversion conversion) {\n\tconst unsigned index = static_cast<unsigned>(conversion);\n\tassert(index < std::size(caseConvList));\n\tCaseConverter *pCaseConv = &caseConvList[index];\n\tif (!pCaseConv->Initialised()) {\n\t\tpCaseConv->SetupConversions(conversion);\n\t}\n\treturn pCaseConv;\n}\n\n}\n\nnamespace Scintilla::Internal {\n\nICaseConverter *ConverterFor(CaseConversion conversion) {\n\treturn ConverterForConversion(conversion);\n}\n\nconst char *CaseConvert(int character, CaseConversion conversion) {\n\tCaseConverter *pCaseConv = ConverterForConversion(conversion);\n\treturn pCaseConv->Find(character);\n}\n\nsize_t CaseConvertString(char *converted, size_t sizeConverted, const char *mixed, size_t lenMixed, CaseConversion conversion) {\n\tCaseConverter *pCaseConv = ConverterForConversion(conversion);\n\treturn pCaseConv->CaseConvertString(converted, sizeConverted, mixed, lenMixed);\n}\n\nstd::string CaseConvertString(const std::string &s, CaseConversion conversion) {\n\tstd::string retMapped(s.length() * maxExpansionCaseConversion, 0);\n\tconst size_t lenMapped = CaseConvertString(retMapped.data(), retMapped.length(), s.c_str(), s.length(),\n\t\tconversion);\n\tretMapped.resize(lenMapped);\n\treturn retMapped;\n}\n\n}\n"
  },
  {
    "path": "Libraries/scintilla/src/CaseConvert.h",
    "content": "// Scintilla source code edit control\n// Encoding: UTF-8\n/** @file CaseConvert.h\n ** Performs Unicode case conversions.\n ** Does not handle locale-sensitive case conversion.\n **/\n// Copyright 2013 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#ifndef CASECONVERT_H\n#define CASECONVERT_H\n\nnamespace Scintilla::Internal {\n\nenum class CaseConversion {\n\tfold,\n\tupper,\n\tlower\n};\n\nclass ICaseConverter {\npublic:\n\tvirtual size_t CaseConvertString(char *converted, size_t sizeConverted, const char *mixed, size_t lenMixed) = 0;\n};\n\nICaseConverter *ConverterFor(CaseConversion conversion);\n\n// Returns a UTF-8 string. Empty when no conversion\nconst char *CaseConvert(int character, CaseConversion conversion);\n\n// When performing CaseConvertString, the converted value may be up to 3 times longer than the input.\n// Ligatures are often decomposed into multiple characters and long cases include:\n// ΐ \"\\xce\\x90\" folds to ΐ \"\\xce\\xb9\\xcc\\x88\\xcc\\x81\"\nconstexpr size_t maxExpansionCaseConversion = 3;\n\n// Converts a mixed case string using a particular conversion.\n// Result may be a different length to input and the length is the return value.\n// If there is not enough space then 0 is returned.\nsize_t CaseConvertString(char *converted, size_t sizeConverted, const char *mixed, size_t lenMixed, CaseConversion conversion);\n\n// Converts a mixed case string using a particular conversion.\nstd::string CaseConvertString(const std::string &s, CaseConversion conversion);\n\n}\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/src/CaseFolder.cxx",
    "content": "// Scintilla source code edit control\n/** @file CaseFolder.cxx\n ** Classes for case folding.\n **/\n// Copyright 1998-2013 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#include <stdexcept>\n#include <vector>\n#include <algorithm>\n\n#include \"CharacterType.h\"\n#include \"CaseFolder.h\"\n#include \"CaseConvert.h\"\n\nusing namespace Scintilla::Internal;\n\nnamespace {\n\nconstexpr unsigned char IndexFromChar(char ch) {\n\treturn static_cast<unsigned char>(ch);\n}\n\n}\n\nCaseFolderTable::CaseFolderTable() noexcept : mapping{}  {\n\tStandardASCII();\n}\n\nsize_t CaseFolderTable::Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) {\n\tif (lenMixed > sizeFolded) {\n\t\treturn 0;\n\t}\n\tfor (size_t i=0; i<lenMixed; i++) {\n\t\tfolded[i] = mapping[IndexFromChar(mixed[i])];\n\t}\n\treturn lenMixed;\n}\n\nvoid CaseFolderTable::SetTranslation(char ch, char chTranslation) noexcept {\n\tmapping[IndexFromChar(ch)] = chTranslation;\n}\n\nvoid CaseFolderTable::StandardASCII() noexcept {\n\tfor (size_t iChar=0; iChar<std::size(mapping); iChar++) {\n\t\tmapping[iChar] = static_cast<char>(MakeLowerCase(iChar));\n\t}\n}\n\nCaseFolderUnicode::CaseFolderUnicode() {\n\tconverter = ConverterFor(CaseConversion::fold);\n}\n\nsize_t CaseFolderUnicode::Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) {\n\tif ((lenMixed == 1) && (sizeFolded > 0)) {\n\t\tfolded[0] = mapping[IndexFromChar(mixed[0])];\n\t\treturn 1;\n\t} else {\n\t\treturn converter->CaseConvertString(folded, sizeFolded, mixed, lenMixed);\n\t}\n}\n"
  },
  {
    "path": "Libraries/scintilla/src/CaseFolder.h",
    "content": "// Scintilla source code edit control\n/** @file CaseFolder.h\n ** Classes for case folding.\n **/\n// Copyright 1998-2013 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#ifndef CASEFOLDER_H\n#define CASEFOLDER_H\n\nnamespace Scintilla::Internal {\n\nclass CaseFolder {\npublic:\n\tCaseFolder() = default;\n\t// Deleted so CaseFolder objects can not be copied.\n\tCaseFolder(const CaseFolder &source) = delete;\n\tCaseFolder(CaseFolder &&) = delete;\n\tCaseFolder &operator=(const CaseFolder &) = delete;\n\tCaseFolder &operator=(CaseFolder &&) = delete;\n\tvirtual ~CaseFolder() = default;\n\tvirtual size_t Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) = 0;\n};\n\nclass CaseFolderTable : public CaseFolder {\nprotected:\n\tchar mapping[256];\npublic:\n\tCaseFolderTable() noexcept;\n\tsize_t Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) override;\n\tvoid SetTranslation(char ch, char chTranslation) noexcept;\n\tvoid StandardASCII() noexcept;\n};\n\nclass ICaseConverter;\n\nclass CaseFolderUnicode : public CaseFolderTable {\n\tICaseConverter *converter;\npublic:\n\tCaseFolderUnicode();\n\tsize_t Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) override;\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/src/CellBuffer.cxx",
    "content": "// Scintilla source code edit control\n/** @file CellBuffer.cxx\n ** Manages a buffer of cells.\n **/\n// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#include <cstddef>\n#include <cstdlib>\n#include <cstdint>\n#include <cassert>\n#include <cstring>\n#include <cstdio>\n#include <cstdarg>\n#include <climits>\n\n#include <stdexcept>\n#include <string>\n#include <string_view>\n#include <vector>\n#include <optional>\n#include <algorithm>\n#include <memory>\n\n#include \"ScintillaTypes.h\"\n\n#include \"Debugging.h\"\n\n#include \"Position.h\"\n#include \"SplitVector.h\"\n#include \"Partitioning.h\"\n#include \"RunStyles.h\"\n#include \"SparseVector.h\"\n#include \"ChangeHistory.h\"\n#include \"CellBuffer.h\"\n#include \"UndoHistory.h\"\n#include \"UniConversion.h\"\n\nnamespace Scintilla::Internal {\n\nstruct CountWidths {\n\t// Measures the number of characters in a string divided into those\n\t// from the Base Multilingual Plane and those from other planes.\n\tSci::Position countBasePlane;\n\tSci::Position countOtherPlanes;\n\texplicit CountWidths(Sci::Position countBasePlane_=0, Sci::Position countOtherPlanes_=0) noexcept :\n\t\tcountBasePlane(countBasePlane_),\n\t\tcountOtherPlanes(countOtherPlanes_) {\n\t}\n\tCountWidths operator-() const noexcept {\n\t\treturn CountWidths(-countBasePlane, -countOtherPlanes);\n\t}\n\tSci::Position WidthUTF32() const noexcept {\n\t\t// All code points take one code unit in UTF-32.\n\t\treturn countBasePlane + countOtherPlanes;\n\t}\n\tSci::Position WidthUTF16() const noexcept {\n\t\t// UTF-16 takes 2 code units for other planes\n\t\treturn countBasePlane + 2 * countOtherPlanes;\n\t}\n\tvoid CountChar(int lenChar) noexcept {\n\t\tif (lenChar == 4) {\n\t\t\tcountOtherPlanes++;\n\t\t} else {\n\t\t\tcountBasePlane++;\n\t\t}\n\t}\n};\n\nclass ILineVector {\npublic:\n\tvirtual void Init() = 0;\n\tvirtual void SetPerLine(PerLine *pl) noexcept = 0;\n\tvirtual void InsertText(Sci::Line line, Sci::Position delta) noexcept = 0;\n\tvirtual void InsertLine(Sci::Line line, Sci::Position position, bool lineStart) = 0;\n\tvirtual void InsertLines(Sci::Line line, const Sci::Position *positions, size_t lines, bool lineStart) = 0;\n\tvirtual void SetLineStart(Sci::Line line, Sci::Position position) noexcept = 0;\n\tvirtual void RemoveLine(Sci::Line line) = 0;\n\tvirtual Sci::Line Lines() const noexcept = 0;\n\tvirtual void AllocateLines(Sci::Line lines) = 0;\n\tvirtual Sci::Line LineFromPosition(Sci::Position pos) const noexcept = 0;\n\tvirtual Sci::Position LineStart(Sci::Line line) const noexcept = 0;\n\tvirtual void InsertCharacters(Sci::Line line, CountWidths delta) noexcept = 0;\n\tvirtual void SetLineCharactersWidth(Sci::Line line, CountWidths width) noexcept = 0;\n\tvirtual Scintilla::LineCharacterIndexType LineCharacterIndex() const noexcept = 0;\n\tvirtual bool AllocateLineCharacterIndex(Scintilla::LineCharacterIndexType lineCharacterIndex, Sci::Line lines) = 0;\n\tvirtual bool ReleaseLineCharacterIndex(Scintilla::LineCharacterIndexType lineCharacterIndex) = 0;\n\tvirtual Sci::Position IndexLineStart(Sci::Line line, Scintilla::LineCharacterIndexType lineCharacterIndex) const noexcept = 0;\n\tvirtual Sci::Line LineFromPositionIndex(Sci::Position pos, Scintilla::LineCharacterIndexType lineCharacterIndex) const noexcept = 0;\n\tvirtual ~ILineVector() {}\n};\n\n}\n\nusing namespace Scintilla;\nusing namespace Scintilla::Internal;\n\ntemplate <typename POS>\nclass LineStartIndex {\n\t// line_cast(): cast Sci::Line to either 32-bit or 64-bit value\n\t// This avoids warnings from Visual C++ Code Analysis and shortens code\n\tstatic constexpr POS line_cast(Sci::Line pos) noexcept {\n\t\treturn static_cast<POS>(pos);\n\t}\npublic:\n\tint refCount;\n\tPartitioning<POS> starts;\n\n\tLineStartIndex() : refCount(0), starts(4) {\n\t\t// Minimal initial allocation\n\t}\n\tbool Allocate(Sci::Line lines) {\n\t\trefCount++;\n\t\tSci::Position length = starts.PositionFromPartition(starts.Partitions());\n\t\tfor (Sci::Line line = starts.Partitions(); line < lines; line++) {\n\t\t\t// Produce an ascending sequence that will be filled in with correct widths later\n\t\t\tlength++;\n\t\t\tstarts.InsertPartition(line_cast(line), line_cast(length));\n\t\t}\n\t\treturn refCount == 1;\n\t}\n\tbool Release() {\n\t\tif (refCount == 1) {\n\t\t\tstarts.DeleteAll();\n\t\t}\n\t\trefCount--;\n\t\treturn refCount == 0;\n\t}\n\tbool Active() const noexcept {\n\t\treturn refCount > 0;\n\t}\n\tSci::Position LineWidth(Sci::Line line) const noexcept {\n\t\treturn starts.PositionFromPartition(line_cast(line) + 1) -\n\t\t\tstarts.PositionFromPartition(line_cast(line));\n\t}\n\tvoid SetLineWidth(Sci::Line line, Sci::Position width) noexcept {\n\t\tconst Sci::Position widthCurrent = LineWidth(line);\n\t\tstarts.InsertText(line_cast(line), line_cast(width - widthCurrent));\n\t}\n\tvoid AllocateLines(Sci::Line lines) {\n\t\tif (lines > starts.Partitions()) {\n\t\t\tstarts.ReAllocate(lines);\n\t\t}\n\t}\n\tvoid InsertLines(Sci::Line line, Sci::Line lines) {\n\t\t// Insert multiple lines with each temporarily 1 character wide.\n\t\t// The line widths will be fixed up by later measuring code.\n\t\tconst POS lineAsPos = line_cast(line);\n\t\tconst POS lineStart = starts.PositionFromPartition(lineAsPos - 1) + 1;\n\t\tfor (POS l = 0; l < line_cast(lines); l++) {\n\t\t\tstarts.InsertPartition(lineAsPos + l, lineStart + l);\n\t\t}\n\t}\n};\n\ntemplate <typename POS>\nclass LineVector : public ILineVector {\n\tPartitioning<POS> starts;\n\tPerLine *perLine;\n\tLineStartIndex<POS> startsUTF16;\n\tLineStartIndex<POS> startsUTF32;\n\tLineCharacterIndexType activeIndices;\n\n\tvoid SetActiveIndices() noexcept {\n\t\tactiveIndices =\n\t\t\t  (startsUTF32.Active() ? LineCharacterIndexType::Utf32 : LineCharacterIndexType::None)\n\t\t\t| (startsUTF16.Active() ? LineCharacterIndexType::Utf16 : LineCharacterIndexType::None);\n\t}\n\n\t// pos_cast(): cast Sci::Line and Sci::Position to either 32-bit or 64-bit value\n\t// This avoids warnings from Visual C++ Code Analysis and shortens code\n\tstatic constexpr POS pos_cast(Sci::Position pos) noexcept {\n\t\treturn static_cast<POS>(pos);\n\t}\n\n\t// line_from_pos_cast(): return 32-bit or 64-bit value as Sci::Line\n\t// This avoids warnings from Visual C++ Code Analysis and shortens code\n\tstatic constexpr Sci::Line line_from_pos_cast(POS line) noexcept {\n\t\treturn static_cast<Sci::Line>(line);\n\t}\n\npublic:\n\tLineVector() : starts(256), perLine(nullptr), activeIndices(LineCharacterIndexType::None) {\n\t}\n\tvoid Init() override {\n\t\tstarts.DeleteAll();\n\t\tif (perLine) {\n\t\t\tperLine->Init();\n\t\t}\n\t\tstartsUTF32.starts.DeleteAll();\n\t\tstartsUTF16.starts.DeleteAll();\n\t}\n\tvoid SetPerLine(PerLine *pl) noexcept override {\n\t\tperLine = pl;\n\t}\n\tvoid InsertText(Sci::Line line, Sci::Position delta) noexcept override {\n\t\tstarts.InsertText(pos_cast(line), pos_cast(delta));\n\t}\n\tvoid InsertLine(Sci::Line line, Sci::Position position, bool lineStart) override {\n\t\tconst POS lineAsPos = pos_cast(line);\n\t\tstarts.InsertPartition(lineAsPos, pos_cast(position));\n\t\tif (activeIndices != LineCharacterIndexType::None) {\n\t\t\tif (FlagSet(activeIndices, LineCharacterIndexType::Utf32)) {\n\t\t\t\tstartsUTF32.InsertLines(line, 1);\n\t\t\t}\n\t\t\tif (FlagSet(activeIndices, LineCharacterIndexType::Utf16)) {\n\t\t\t\tstartsUTF16.InsertLines(line, 1);\n\t\t\t}\n\t\t}\n\t\tif (perLine) {\n\t\t\tif ((line > 0) && lineStart)\n\t\t\t\tline--;\n\t\t\tperLine->InsertLine(line);\n\t\t}\n\t}\n\tvoid InsertLines(Sci::Line line, const Sci::Position *positions, size_t lines, bool lineStart) override {\n\t\tconst POS lineAsPos = pos_cast(line);\n\t\tif constexpr (sizeof(Sci::Position) == sizeof(POS)) {\n\t\t\tstarts.InsertPartitions(lineAsPos, positions, lines);\n\t\t} else {\n\t\t\tstarts.InsertPartitionsWithCast(lineAsPos, positions, lines);\n\t\t}\n\t\tif (activeIndices != LineCharacterIndexType::None) {\n\t\t\tif (FlagSet(activeIndices, LineCharacterIndexType::Utf32)) {\n\t\t\t\tstartsUTF32.InsertLines(line, lines);\n\t\t\t}\n\t\t\tif (FlagSet(activeIndices, LineCharacterIndexType::Utf16)) {\n\t\t\t\tstartsUTF16.InsertLines(line, lines);\n\t\t\t}\n\t\t}\n\t\tif (perLine) {\n\t\t\tif ((line > 0) && lineStart)\n\t\t\t\tline--;\n\t\t\tperLine->InsertLines(line, lines);\n\t\t}\n\t}\n\tvoid SetLineStart(Sci::Line line, Sci::Position position) noexcept override {\n\t\tstarts.SetPartitionStartPosition(pos_cast(line), pos_cast(position));\n\t}\n\tvoid RemoveLine(Sci::Line line) override {\n\t\tstarts.RemovePartition(pos_cast(line));\n\t\tif (FlagSet(activeIndices, LineCharacterIndexType::Utf32)) {\n\t\t\tstartsUTF32.starts.RemovePartition(pos_cast(line));\n\t\t}\n\t\tif (FlagSet(activeIndices, LineCharacterIndexType::Utf16)) {\n\t\t\tstartsUTF16.starts.RemovePartition(pos_cast(line));\n\t\t}\n\t\tif (perLine) {\n\t\t\tperLine->RemoveLine(line);\n\t\t}\n\t}\n\tSci::Line Lines() const noexcept override {\n\t\treturn line_from_pos_cast(starts.Partitions());\n\t}\n\tvoid AllocateLines(Sci::Line lines) override {\n\t\tif (lines > Lines()) {\n\t\t\tstarts.ReAllocate(lines);\n\t\t\tif (FlagSet(activeIndices, LineCharacterIndexType::Utf32)) {\n\t\t\t\tstartsUTF32.AllocateLines(lines);\n\t\t\t}\n\t\t\tif (FlagSet(activeIndices, LineCharacterIndexType::Utf16)) {\n\t\t\t\tstartsUTF16.AllocateLines(lines);\n\t\t\t}\n\t\t}\n\t}\n\tSci::Line LineFromPosition(Sci::Position pos) const noexcept override {\n\t\treturn line_from_pos_cast(starts.PartitionFromPosition(pos_cast(pos)));\n\t}\n\tSci::Position LineStart(Sci::Line line) const noexcept override {\n\t\treturn starts.PositionFromPartition(pos_cast(line));\n\t}\n\tvoid InsertCharacters(Sci::Line line, CountWidths delta) noexcept override {\n\t\tif (FlagSet(activeIndices, LineCharacterIndexType::Utf32)) {\n\t\t\tstartsUTF32.starts.InsertText(pos_cast(line), pos_cast(delta.WidthUTF32()));\n\t\t}\n\t\tif (FlagSet(activeIndices, LineCharacterIndexType::Utf16)) {\n\t\t\tstartsUTF16.starts.InsertText(pos_cast(line), pos_cast(delta.WidthUTF16()));\n\t\t}\n\t}\n\tvoid SetLineCharactersWidth(Sci::Line line, CountWidths width) noexcept override {\n\t\tif (FlagSet(activeIndices, LineCharacterIndexType::Utf32)) {\n\t\t\tassert(startsUTF32.starts.Partitions() == starts.Partitions());\n\t\t\tstartsUTF32.SetLineWidth(line, width.WidthUTF32());\n\t\t}\n\t\tif (FlagSet(activeIndices, LineCharacterIndexType::Utf16)) {\n\t\t\tassert(startsUTF16.starts.Partitions() == starts.Partitions());\n\t\t\tstartsUTF16.SetLineWidth(line, width.WidthUTF16());\n\t\t}\n\t}\n\n\tLineCharacterIndexType LineCharacterIndex() const noexcept override {\n\t\treturn activeIndices;\n\t}\n\tbool AllocateLineCharacterIndex(LineCharacterIndexType lineCharacterIndex, Sci::Line lines) override {\n\t\tconst LineCharacterIndexType activeIndicesStart = activeIndices;\n\t\tif (FlagSet(lineCharacterIndex, LineCharacterIndexType::Utf32)) {\n\t\t\tstartsUTF32.Allocate(lines);\n\t\t\tassert(startsUTF32.starts.Partitions() == starts.Partitions());\n\t\t}\n\t\tif (FlagSet(lineCharacterIndex, LineCharacterIndexType::Utf16)) {\n\t\t\tstartsUTF16.Allocate(lines);\n\t\t\tassert(startsUTF16.starts.Partitions() == starts.Partitions());\n\t\t}\n\t\tSetActiveIndices();\n\t\treturn activeIndicesStart != activeIndices;\n\t}\n\tbool ReleaseLineCharacterIndex(LineCharacterIndexType lineCharacterIndex) override {\n\t\tconst LineCharacterIndexType activeIndicesStart = activeIndices;\n\t\tif (FlagSet(lineCharacterIndex, LineCharacterIndexType::Utf32)) {\n\t\t\tstartsUTF32.Release();\n\t\t}\n\t\tif (FlagSet(lineCharacterIndex, LineCharacterIndexType::Utf16)) {\n\t\t\tstartsUTF16.Release();\n\t\t}\n\t\tSetActiveIndices();\n\t\treturn activeIndicesStart != activeIndices;\n\t}\n\tSci::Position IndexLineStart(Sci::Line line, LineCharacterIndexType lineCharacterIndex) const noexcept override {\n\t\tif (lineCharacterIndex == LineCharacterIndexType::Utf32) {\n\t\t\treturn startsUTF32.starts.PositionFromPartition(pos_cast(line));\n\t\t} else {\n\t\t\treturn startsUTF16.starts.PositionFromPartition(pos_cast(line));\n\t\t}\n\t}\n\tSci::Line LineFromPositionIndex(Sci::Position pos, LineCharacterIndexType lineCharacterIndex) const noexcept override {\n\t\tif (lineCharacterIndex == LineCharacterIndexType::Utf32) {\n\t\t\treturn line_from_pos_cast(startsUTF32.starts.PartitionFromPosition(pos_cast(pos)));\n\t\t} else {\n\t\t\treturn line_from_pos_cast(startsUTF16.starts.PartitionFromPosition(pos_cast(pos)));\n\t\t}\n\t}\n};\n\nCellBuffer::CellBuffer(bool hasStyles_, bool largeDocument_) :\n\thasStyles(hasStyles_), largeDocument(largeDocument_) {\n\treadOnly = false;\n\tutf8Substance = false;\n\tutf8LineEnds = LineEndType::Default;\n\tcollectingUndo = true;\n\tuh = std::make_unique<UndoHistory>();\n\tif (largeDocument)\n\t\tplv = std::make_unique<LineVector<Sci::Position>>();\n\telse\n\t\tplv = std::make_unique<LineVector<int>>();\n}\n\nCellBuffer::~CellBuffer() noexcept = default;\n\nchar CellBuffer::CharAt(Sci::Position position) const noexcept {\n\treturn substance.ValueAt(position);\n}\n\nunsigned char CellBuffer::UCharAt(Sci::Position position) const noexcept {\n\treturn substance.ValueAt(position);\n}\n\nvoid CellBuffer::GetCharRange(char *buffer, Sci::Position position, Sci::Position lengthRetrieve) const {\n\tif (lengthRetrieve <= 0)\n\t\treturn;\n\tif (position < 0)\n\t\treturn;\n\tif ((position + lengthRetrieve) > substance.Length()) {\n\t\tPlatform::DebugPrintf(\"Bad GetCharRange %.0f for %.0f of %.0f\\n\",\n\t\t\t\t      static_cast<double>(position),\n\t\t\t\t      static_cast<double>(lengthRetrieve),\n\t\t\t\t      static_cast<double>(substance.Length()));\n\t\treturn;\n\t}\n\tsubstance.GetRange(buffer, position, lengthRetrieve);\n}\n\nchar CellBuffer::StyleAt(Sci::Position position) const noexcept {\n\treturn hasStyles ? style.ValueAt(position) : '\\0';\n}\n\nvoid CellBuffer::GetStyleRange(unsigned char *buffer, Sci::Position position, Sci::Position lengthRetrieve) const {\n\tif (lengthRetrieve < 0)\n\t\treturn;\n\tif (position < 0)\n\t\treturn;\n\tif (!hasStyles) {\n\t\tstd::fill(buffer, buffer + lengthRetrieve, static_cast<unsigned char>(0));\n\t\treturn;\n\t}\n\tif ((position + lengthRetrieve) > style.Length()) {\n\t\tPlatform::DebugPrintf(\"Bad GetStyleRange %.0f for %.0f of %.0f\\n\",\n\t\t\t\t      static_cast<double>(position),\n\t\t\t\t      static_cast<double>(lengthRetrieve),\n\t\t\t\t      static_cast<double>(style.Length()));\n\t\treturn;\n\t}\n\tstyle.GetRange(reinterpret_cast<char *>(buffer), position, lengthRetrieve);\n}\n\nconst char *CellBuffer::BufferPointer() {\n\treturn substance.BufferPointer();\n}\n\nconst char *CellBuffer::RangePointer(Sci::Position position, Sci::Position rangeLength) noexcept {\n\treturn substance.RangePointer(position, rangeLength);\n}\n\nSci::Position CellBuffer::GapPosition() const noexcept {\n\treturn substance.GapPosition();\n}\n\nSplitView CellBuffer::AllView() const noexcept {\n\tconst size_t length = substance.Length();\n\tsize_t length1 = substance.GapPosition();\n\tif (length1 == 0) {\n\t\t// Assign segment2 to segment1 / length1 to avoid useless test against 0 length1\n\t\tlength1 = length;\n\t}\n\treturn SplitView {\n\t\tsubstance.ElementPointer(0),\n\t\tlength1,\n\t\tsubstance.ElementPointer(length1) - length1,\n\t\tlength\n\t};\n}\n\n// The char* returned is to an allocation owned by the undo history\nconst char *CellBuffer::InsertString(Sci::Position position, const char *s, Sci::Position insertLength, bool &startSequence) {\n\t// InsertString and DeleteChars are the bottleneck though which all changes occur\n\tconst char *data = s;\n\tif (!readOnly) {\n\t\tif (collectingUndo) {\n\t\t\t// Save into the undo/redo stack, but only the characters - not the formatting\n\t\t\t// This takes up about half load time\n\t\t\tdata = uh->AppendAction(ActionType::insert, position, s, insertLength, startSequence);\n\t\t}\n\n\t\tBasicInsertString(position, s, insertLength);\n\t\tif (changeHistory) {\n\t\t\tchangeHistory->Insert(position, insertLength, collectingUndo, uh->BeforeReachableSavePoint());\n\t\t}\n\t}\n\treturn data;\n}\n\nbool CellBuffer::SetStyleAt(Sci::Position position, char styleValue) noexcept {\n\tif (!hasStyles) {\n\t\treturn false;\n\t}\n\tconst char curVal = style.ValueAt(position);\n\tif (curVal != styleValue) {\n\t\tstyle.SetValueAt(position, styleValue);\n\t\treturn true;\n\t} else {\n\t\treturn false;\n\t}\n}\n\nbool CellBuffer::SetStyleFor(Sci::Position position, Sci::Position lengthStyle, char styleValue) noexcept {\n\tif (!hasStyles) {\n\t\treturn false;\n\t}\n\tbool changed = false;\n\tPLATFORM_ASSERT(lengthStyle == 0 ||\n\t\t(lengthStyle > 0 && lengthStyle + position <= style.Length()));\n\twhile (lengthStyle--) {\n\t\tconst char curVal = style.ValueAt(position);\n\t\tif (curVal != styleValue) {\n\t\t\tstyle.SetValueAt(position, styleValue);\n\t\t\tchanged = true;\n\t\t}\n\t\tposition++;\n\t}\n\treturn changed;\n}\n\n// The char* returned is to an allocation owned by the undo history\nconst char *CellBuffer::DeleteChars(Sci::Position position, Sci::Position deleteLength, bool &startSequence) {\n\t// InsertString and DeleteChars are the bottleneck though which all changes occur\n\tPLATFORM_ASSERT(deleteLength > 0);\n\tconst char *data = nullptr;\n\tif (!readOnly) {\n\t\tif (collectingUndo) {\n\t\t\t// Save into the undo/redo stack, but only the characters - not the formatting\n\t\t\t// The gap would be moved to position anyway for the deletion so this doesn't cost extra\n\t\t\tdata = substance.RangePointer(position, deleteLength);\n\t\t\tdata = uh->AppendAction(ActionType::remove, position, data, deleteLength, startSequence);\n\t\t}\n\n\t\tif (changeHistory) {\n\t\t\tchangeHistory->DeleteRangeSavingHistory(position, deleteLength,\n\t\t\t\tuh->BeforeReachableSavePoint(), uh->AfterOrAtDetachPoint());\n\t\t}\n\n\t\tBasicDeleteChars(position, deleteLength);\n\t}\n\treturn data;\n}\n\nSci::Position CellBuffer::Length() const noexcept {\n\treturn substance.Length();\n}\n\nvoid CellBuffer::Allocate(Sci::Position newSize) {\n\tif (!largeDocument && (newSize > INT32_MAX)) {\n\t\tthrow std::runtime_error(\"CellBuffer::Allocate: size of standard document limited to 2G.\");\n\t}\n\tsubstance.ReAllocate(newSize);\n\tif (hasStyles) {\n\t\tstyle.ReAllocate(newSize);\n\t}\n}\n\nvoid CellBuffer::SetUTF8Substance(bool utf8Substance_) noexcept {\n\tutf8Substance = utf8Substance_;\n}\n\nvoid CellBuffer::SetLineEndTypes(LineEndType utf8LineEnds_) {\n\tif (utf8LineEnds != utf8LineEnds_) {\n\t\tconst LineCharacterIndexType indexes = plv->LineCharacterIndex();\n\t\tutf8LineEnds = utf8LineEnds_;\n\t\tResetLineEnds();\n\t\tAllocateLineCharacterIndex(indexes);\n\t}\n}\n\nbool CellBuffer::ContainsLineEnd(const char *s, Sci::Position length) const noexcept {\n\tunsigned char chBeforePrev = 0;\n\tunsigned char chPrev = 0;\n\tfor (Sci::Position i = 0; i < length; i++) {\n\t\tconst unsigned char ch = s[i];\n\t\tif ((ch == '\\r') || (ch == '\\n')) {\n\t\t\treturn true;\n\t\t} else if (utf8LineEnds == LineEndType::Unicode) {\n\t\t\tif (UTF8IsMultibyteLineEnd(chBeforePrev, chPrev, ch)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\tchBeforePrev = chPrev;\n\t\tchPrev = ch;\n\t}\n\treturn false;\n}\n\nvoid CellBuffer::SetPerLine(PerLine *pl) noexcept {\n\tplv->SetPerLine(pl);\n}\n\nLineCharacterIndexType CellBuffer::LineCharacterIndex() const noexcept {\n\treturn plv->LineCharacterIndex();\n}\n\nvoid CellBuffer::AllocateLineCharacterIndex(LineCharacterIndexType lineCharacterIndex) {\n\tif (utf8Substance) {\n\t\tif (plv->AllocateLineCharacterIndex(lineCharacterIndex, Lines())) {\n\t\t\t// Changed so recalculate whole file\n\t\t\tRecalculateIndexLineStarts(0, Lines() - 1);\n\t\t}\n\t}\n}\n\nvoid CellBuffer::ReleaseLineCharacterIndex(LineCharacterIndexType lineCharacterIndex) {\n\tplv->ReleaseLineCharacterIndex(lineCharacterIndex);\n}\n\nSci::Line CellBuffer::Lines() const noexcept {\n\treturn plv->Lines();\n}\n\nvoid CellBuffer::AllocateLines(Sci::Line lines) {\n\tplv->AllocateLines(lines);\n}\n\nSci::Position CellBuffer::LineStart(Sci::Line line) const noexcept {\n\tif (line < 0)\n\t\treturn 0;\n\telse if (line >= Lines())\n\t\treturn Length();\n\telse\n\t\treturn plv->LineStart(line);\n}\n\nSci::Position CellBuffer::LineEnd(Sci::Line line) const noexcept {\n\tif (line >= Lines() - 1) {\n\t\treturn LineStart(line + 1);\n\t} else {\n\t\tSci::Position position = LineStart(line + 1);\n\t\tif (LineEndType::Unicode == GetLineEndTypes()) {\n\t\t\tconst unsigned char bytes[] = {\n\t\t\t\tUCharAt(position - 3),\n\t\t\t\tUCharAt(position - 2),\n\t\t\t\tUCharAt(position - 1),\n\t\t\t};\n\t\t\tif (UTF8IsSeparator(bytes)) {\n\t\t\t\treturn position - UTF8SeparatorLength;\n\t\t\t}\n\t\t\tif (UTF8IsNEL(bytes + 1)) {\n\t\t\t\treturn position - UTF8NELLength;\n\t\t\t}\n\t\t}\n\t\tposition--; // Back over CR or LF\n\t\t// When line terminator is CR+LF, may need to go back one more\n\t\tif ((position > LineStart(line)) && (CharAt(position - 1) == '\\r')) {\n\t\t\tposition--;\n\t\t}\n\t\treturn position;\n\t}\n}\n\nSci::Line CellBuffer::LineFromPosition(Sci::Position pos) const noexcept {\n\treturn plv->LineFromPosition(pos);\n}\n\nSci::Position CellBuffer::IndexLineStart(Sci::Line line, LineCharacterIndexType lineCharacterIndex) const noexcept {\n\treturn plv->IndexLineStart(line, lineCharacterIndex);\n}\n\nSci::Line CellBuffer::LineFromPositionIndex(Sci::Position pos, LineCharacterIndexType lineCharacterIndex) const noexcept {\n\treturn plv->LineFromPositionIndex(pos, lineCharacterIndex);\n}\n\nbool CellBuffer::IsReadOnly() const noexcept {\n\treturn readOnly;\n}\n\nvoid CellBuffer::SetReadOnly(bool set) noexcept {\n\treadOnly = set;\n}\n\nbool CellBuffer::IsLarge() const noexcept {\n\treturn largeDocument;\n}\n\nbool CellBuffer::HasStyles() const noexcept {\n\treturn hasStyles;\n}\n\nvoid CellBuffer::SetSavePoint() {\n\tuh->SetSavePoint();\n\tif (changeHistory) {\n\t\tchangeHistory->SetSavePoint();\n\t}\n}\n\nbool CellBuffer::IsSavePoint() const noexcept {\n\treturn uh->IsSavePoint();\n}\n\nvoid CellBuffer::TentativeStart() noexcept {\n\tuh->TentativeStart();\n}\n\nvoid CellBuffer::TentativeCommit() noexcept {\n\tuh->TentativeCommit();\n}\n\nint CellBuffer::TentativeSteps() noexcept {\n\treturn uh->TentativeSteps();\n}\n\nbool CellBuffer::TentativeActive() const noexcept {\n\treturn uh->TentativeActive();\n}\n\n// Without undo\n\nvoid CellBuffer::InsertLine(Sci::Line line, Sci::Position position, bool lineStart) {\n\tplv->InsertLine(line, position, lineStart);\n}\n\nvoid CellBuffer::RemoveLine(Sci::Line line) {\n\tplv->RemoveLine(line);\n}\n\nbool CellBuffer::UTF8LineEndOverlaps(Sci::Position position) const noexcept {\n\tconst unsigned char bytes[] = {\n\t\tstatic_cast<unsigned char>(substance.ValueAt(position-2)),\n\t\tstatic_cast<unsigned char>(substance.ValueAt(position-1)),\n\t\tstatic_cast<unsigned char>(substance.ValueAt(position)),\n\t\tstatic_cast<unsigned char>(substance.ValueAt(position+1)),\n\t};\n\treturn UTF8IsSeparator(bytes) || UTF8IsSeparator(bytes+1) || UTF8IsNEL(bytes+1);\n}\n\nbool CellBuffer::UTF8IsCharacterBoundary(Sci::Position position) const {\n\tassert(position >= 0 && position <= Length());\n\tif (position > 0) {\n\t\tstd::string back;\n\t\tfor (int i = 0; i < UTF8MaxBytes; i++) {\n\t\t\tconst Sci::Position posBack = position - i;\n\t\t\tif (posBack < 0) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tback.insert(0, 1, substance.ValueAt(posBack));\n\t\t\tif (!UTF8IsTrailByte(back.front())) {\n\t\t\t\tif (i > 0) {\n\t\t\t\t\t// Have reached a non-trail\n\t\t\t\t\tconst int cla = UTF8Classify(back);\n\t\t\t\t\tif ((cla & UTF8MaskInvalid) || (cla != i)) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\tif (position < Length()) {\n\t\tconst unsigned char fore = substance.ValueAt(position);\n\t\tif (UTF8IsTrailByte(fore)) {\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn true;\n}\n\nvoid CellBuffer::ResetLineEnds() {\n\t// Reinitialize line data -- too much work to preserve\n\tconst Sci::Line lines = plv->Lines();\n\tplv->Init();\n\tplv->AllocateLines(lines);\n\n\tconstexpr Sci::Position position = 0;\n\tconst Sci::Position length = Length();\n\tplv->InsertText(0, length);\n\tSci::Line lineInsert = 1;\n\tconstexpr bool atLineStart = true;\n\tunsigned char chBeforePrev = 0;\n\tunsigned char chPrev = 0;\n\tfor (Sci::Position i = 0; i < length; i++) {\n\t\tconst unsigned char ch = substance.ValueAt(position + i);\n\t\tif (ch == '\\r') {\n\t\t\tInsertLine(lineInsert, (position + i) + 1, atLineStart);\n\t\t\tlineInsert++;\n\t\t} else if (ch == '\\n') {\n\t\t\tif (chPrev == '\\r') {\n\t\t\t\t// Patch up what was end of line\n\t\t\t\tplv->SetLineStart(lineInsert - 1, (position + i) + 1);\n\t\t\t} else {\n\t\t\t\tInsertLine(lineInsert, (position + i) + 1, atLineStart);\n\t\t\t\tlineInsert++;\n\t\t\t}\n\t\t} else if (utf8LineEnds == LineEndType::Unicode) {\n\t\t\tif (UTF8IsMultibyteLineEnd(chBeforePrev, chPrev, ch)) {\n\t\t\t\tInsertLine(lineInsert, (position + i) + 1, atLineStart);\n\t\t\t\tlineInsert++;\n\t\t\t}\n\t\t}\n\t\tchBeforePrev = chPrev;\n\t\tchPrev = ch;\n\t}\n}\n\nnamespace {\n\nCountWidths CountCharacterWidthsUTF8(std::string_view sv) noexcept {\n\tCountWidths cw;\n\tsize_t remaining = sv.length();\n\twhile (remaining > 0) {\n\t\tconst int utf8Status = UTF8Classify(sv);\n\t\tconst int lenChar = utf8Status & UTF8MaskWidth;\n\t\tcw.CountChar(lenChar);\n\t\tsv.remove_prefix(lenChar);\n\t\tremaining -= lenChar;\n\t}\n\treturn cw;\n}\n\n}\n\nbool CellBuffer::MaintainingLineCharacterIndex() const noexcept {\n\treturn plv->LineCharacterIndex() != LineCharacterIndexType::None;\n}\n\nvoid CellBuffer::RecalculateIndexLineStarts(Sci::Line lineFirst, Sci::Line lineLast) {\n\tstd::string text;\n\tSci::Position posLineEnd = LineStart(lineFirst);\n\tfor (Sci::Line line = lineFirst; line <= lineLast; line++) {\n\t\t// Find line start and end, retrieve text of line, count characters and update line width\n\t\tconst Sci::Position posLineStart = posLineEnd;\n\t\tposLineEnd = LineStart(line+1);\n\t\tconst Sci::Position width = posLineEnd - posLineStart;\n\t\ttext.resize(width);\n\t\tGetCharRange(text.data(), posLineStart, width);\n\t\tconst CountWidths cw = CountCharacterWidthsUTF8(text);\n\t\tplv->SetLineCharactersWidth(line, cw);\n\t}\n}\n\nvoid CellBuffer::BasicInsertString(Sci::Position position, const char *s, Sci::Position insertLength) {\n\tif (insertLength == 0)\n\t\treturn;\n\tPLATFORM_ASSERT(insertLength > 0);\n\n\tconst unsigned char chAfter = substance.ValueAt(position);\n\tbool breakingUTF8LineEnd = false;\n\tif (utf8LineEnds == LineEndType::Unicode && UTF8IsTrailByte(chAfter)) {\n\t\tbreakingUTF8LineEnd = UTF8LineEndOverlaps(position);\n\t}\n\n\tconst Sci::Line linePosition = plv->LineFromPosition(position);\n\tSci::Line lineInsert = linePosition + 1;\n\n\t// A simple insertion is one that inserts valid text on a single line at a character boundary\n\tbool simpleInsertion = false;\n\n\tconst bool maintainingIndex = MaintainingLineCharacterIndex();\n\n\t// Check for breaking apart a UTF-8 sequence and inserting invalid UTF-8\n\tif (utf8Substance && maintainingIndex) {\n\t\t// Actually, don't need to check that whole insertion is valid just that there\n\t\t// are no potential fragments at ends.\n\t\tsimpleInsertion = UTF8IsCharacterBoundary(position) &&\n\t\t\tUTF8IsValid(std::string_view(s, insertLength));\n\t}\n\n\tsubstance.InsertFromArray(position, s, 0, insertLength);\n\tif (hasStyles) {\n\t\tstyle.InsertValue(position, insertLength, 0);\n\t}\n\n\tconst bool atLineStart = plv->LineStart(lineInsert-1) == position;\n\t// Point all the lines after the insertion point further along in the buffer\n\tplv->InsertText(lineInsert-1, insertLength);\n\tunsigned char chBeforePrev = substance.ValueAt(position - 2);\n\tunsigned char chPrev = substance.ValueAt(position - 1);\n\tif (chPrev == '\\r' && chAfter == '\\n') {\n\t\t// Splitting up a crlf pair at position\n\t\tInsertLine(lineInsert, position, false);\n\t\tlineInsert++;\n\t}\n\tif (breakingUTF8LineEnd) {\n\t\tRemoveLine(lineInsert);\n\t}\n\n\tconstexpr size_t PositionBlockSize = 128;\n\tSci::Position positions[PositionBlockSize]{};\n\tsize_t nPositions = 0;\n\tconst Sci::Line lineStart = lineInsert;\n\n\t// s may not NULL-terminated, ensure *ptr == '\\n' or *next == '\\n' is valid.\n\tconst char *const end = s + insertLength - 1;\n\tconst char *ptr = s;\n\tunsigned char ch = 0;\n\n\tif (chPrev == '\\r' && *ptr == '\\n') {\n\t\t++ptr;\n\t\t// Patch up what was end of line\n\t\tplv->SetLineStart(lineInsert - 1, (position + ptr - s));\n\t\tsimpleInsertion = false;\n\t}\n\n\tif (ptr < end) {\n\t\tuint8_t eolTable[256]{};\n\t\teolTable[static_cast<uint8_t>('\\n')] = 1;\n\t\teolTable[static_cast<uint8_t>('\\r')] = 2;\n\t\tif (utf8LineEnds == LineEndType::Unicode) {\n\t\t\t// see UniConversion.h for LS, PS and NEL\n\t\t\teolTable[0x85] = 4;\n\t\t\teolTable[0xa8] = 3;\n\t\t\teolTable[0xa9] = 3;\n\t\t}\n\n\t\tdo {\n\t\t\t// skip to line end\n\t\t\tch = *ptr++;\n\t\t\tuint8_t type;\n\t\t\twhile ((type = eolTable[ch]) == 0 && ptr < end) {\n\t\t\t\tchBeforePrev = chPrev;\n\t\t\t\tchPrev = ch;\n\t\t\t\tch = *ptr++;\n\t\t\t}\n\t\t\tswitch (type) {\n\t\t\tcase 2: // '\\r'\n\t\t\t\tif (*ptr == '\\n') {\n\t\t\t\t\t++ptr;\n\t\t\t\t}\n\t\t\t\t[[fallthrough]];\n\t\t\tcase 1: // '\\n'\n\t\t\t\tpositions[nPositions++] = position + ptr - s;\n\t\t\t\tif (nPositions == PositionBlockSize) {\n\t\t\t\t\tplv->InsertLines(lineInsert, positions, nPositions, atLineStart);\n\t\t\t\t\tlineInsert += nPositions;\n\t\t\t\t\tnPositions = 0;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 3:\n\t\t\tcase 4:\n\t\t\t\t// LS, PS and NEL\n\t\t\t\tif ((type == 3 && chPrev == 0x80 && chBeforePrev == 0xe2) || (type == 4 && chPrev == 0xc2)) {\n\t\t\t\t\tpositions[nPositions++] = position + ptr - s;\n\t\t\t\t\tif (nPositions == PositionBlockSize) {\n\t\t\t\t\t\tplv->InsertLines(lineInsert, positions, nPositions, atLineStart);\n\t\t\t\t\t\tlineInsert += nPositions;\n\t\t\t\t\t\tnPositions = 0;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tchBeforePrev = chPrev;\n\t\t\tchPrev = ch;\n\t\t} while (ptr < end);\n\t}\n\n\tif (nPositions != 0) {\n\t\tplv->InsertLines(lineInsert, positions, nPositions, atLineStart);\n\t\tlineInsert += nPositions;\n\t}\n\n\tch = *end;\n\tif (ptr == end) {\n\t\t++ptr;\n\t\tif (ch == '\\r' || ch == '\\n') {\n\t\t\tInsertLine(lineInsert, (position + ptr - s), atLineStart);\n\t\t\tlineInsert++;\n\t\t} else if (utf8LineEnds == LineEndType::Unicode && !UTF8IsAscii(ch)) {\n\t\t\tif (UTF8IsMultibyteLineEnd(chBeforePrev, chPrev, ch)) {\n\t\t\t\tInsertLine(lineInsert, (position + ptr - s), atLineStart);\n\t\t\t\tlineInsert++;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Joining two lines where last insertion is cr and following substance starts with lf\n\tif (chAfter == '\\n') {\n\t\tif (ch == '\\r') {\n\t\t\t// End of line already in buffer so drop the newly created one\n\t\t\tRemoveLine(lineInsert - 1);\n\t\t\tsimpleInsertion = false;\n\t\t}\n\t} else if (utf8LineEnds == LineEndType::Unicode && !UTF8IsAscii(chAfter)) {\n\t\tchBeforePrev = chPrev;\n\t\tchPrev = ch;\n\t\t// May have end of UTF-8 line end in buffer and start in insertion\n\t\tfor (int j = 0; j < UTF8SeparatorLength-1; j++) {\n\t\t\tconst unsigned char chAt = substance.ValueAt(position + insertLength + j);\n\t\t\tconst unsigned char back3[3] = {chBeforePrev, chPrev, chAt};\n\t\t\tif (UTF8IsSeparator(back3)) {\n\t\t\t\tInsertLine(lineInsert, (position + insertLength + j) + 1, atLineStart);\n\t\t\t\tlineInsert++;\n\t\t\t}\n\t\t\tif ((j == 0) && UTF8IsNEL(back3+1)) {\n\t\t\t\tInsertLine(lineInsert, (position + insertLength + j) + 1, atLineStart);\n\t\t\t\tlineInsert++;\n\t\t\t}\n\t\t\tchBeforePrev = chPrev;\n\t\t\tchPrev = chAt;\n\t\t}\n\t}\n\tif (maintainingIndex) {\n\t\tif (simpleInsertion && (lineInsert == lineStart)) {\n\t\t\tconst CountWidths cw = CountCharacterWidthsUTF8(std::string_view(s, insertLength));\n\t\t\tplv->InsertCharacters(linePosition, cw);\n\t\t} else {\n\t\t\tRecalculateIndexLineStarts(linePosition, lineInsert - 1);\n\t\t}\n\t}\n}\n\nvoid CellBuffer::BasicDeleteChars(Sci::Position position, Sci::Position deleteLength) {\n\tif (deleteLength == 0)\n\t\treturn;\n\n\tSci::Line lineRecalculateStart = Sci::invalidPosition;\n\n\tif ((position == 0) && (deleteLength == substance.Length())) {\n\t\t// If whole buffer is being deleted, faster to reinitialise lines data\n\t\t// than to delete each line.\n\t\tplv->Init();\n\t} else {\n\t\t// Have to fix up line positions before doing deletion as looking at text in buffer\n\t\t// to work out which lines have been removed\n\n\t\tconst Sci::Line linePosition = plv->LineFromPosition(position);\n\t\tSci::Line lineRemove = linePosition + 1;\n\n\t\tplv->InsertText(lineRemove-1, - (deleteLength));\n\t\tconst unsigned char chPrev = substance.ValueAt(position - 1);\n\t\tconst unsigned char chBefore = chPrev;\n\t\tunsigned char chNext = substance.ValueAt(position);\n\n\t\t// Check for breaking apart a UTF-8 sequence\n\t\t// Needs further checks that text is UTF-8 or that some other break apart is occurring\n\t\tif (utf8Substance && MaintainingLineCharacterIndex()) {\n\t\t\tconst Sci::Position posEnd = position + deleteLength;\n\t\t\tconst Sci::Line lineEndRemove = plv->LineFromPosition(posEnd);\n\t\t\tconst bool simpleDeletion =\n\t\t\t\t(linePosition == lineEndRemove) &&\n\t\t\t\tUTF8IsCharacterBoundary(position) && UTF8IsCharacterBoundary(posEnd);\n\t\t\tif (simpleDeletion) {\n\t\t\t\tstd::string text(deleteLength, '\\0');\n\t\t\t\tGetCharRange(text.data(), position, deleteLength);\n\t\t\t\tif (UTF8IsValid(text)) {\n\t\t\t\t\t// Everything is good\n\t\t\t\t\tconst CountWidths cw = CountCharacterWidthsUTF8(text);\n\t\t\t\t\tplv->InsertCharacters(linePosition, -cw);\n\t\t\t\t} else {\n\t\t\t\t\tlineRecalculateStart = linePosition;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tlineRecalculateStart = linePosition;\n\t\t\t}\n\t\t}\n\n\t\tbool ignoreNL = false;\n\t\tif (chPrev == '\\r' && chNext == '\\n') {\n\t\t\t// Move back one\n\t\t\tplv->SetLineStart(lineRemove, position);\n\t\t\tlineRemove++;\n\t\t\tignoreNL = true; \t// First \\n is not real deletion\n\t\t}\n\t\tif (utf8LineEnds == LineEndType::Unicode && UTF8IsTrailByte(chNext)) {\n\t\t\tif (UTF8LineEndOverlaps(position)) {\n\t\t\t\tRemoveLine(lineRemove);\n\t\t\t}\n\t\t}\n\n\t\tunsigned char ch = chNext;\n\t\tfor (Sci::Position i = 0; i < deleteLength; i++) {\n\t\t\tchNext = substance.ValueAt(position + i + 1);\n\t\t\tif (ch == '\\r') {\n\t\t\t\tif (chNext != '\\n') {\n\t\t\t\t\tRemoveLine(lineRemove);\n\t\t\t\t}\n\t\t\t} else if (ch == '\\n') {\n\t\t\t\tif (ignoreNL) {\n\t\t\t\t\tignoreNL = false; \t// Further \\n are real deletions\n\t\t\t\t} else {\n\t\t\t\t\tRemoveLine(lineRemove);\n\t\t\t\t}\n\t\t\t} else if (utf8LineEnds == LineEndType::Unicode) {\n\t\t\t\tif (!UTF8IsAscii(ch)) {\n\t\t\t\t\tconst unsigned char next3[3] = {ch, chNext,\n\t\t\t\t\t\tstatic_cast<unsigned char>(substance.ValueAt(position + i + 2))};\n\t\t\t\t\tif (UTF8IsSeparator(next3) || UTF8IsNEL(next3)) {\n\t\t\t\t\t\tRemoveLine(lineRemove);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tch = chNext;\n\t\t}\n\t\t// May have to fix up end if last deletion causes cr to be next to lf\n\t\t// or removes one of a crlf pair\n\t\tconst char chAfter = substance.ValueAt(position + deleteLength);\n\t\tif (chBefore == '\\r' && chAfter == '\\n') {\n\t\t\t// Using lineRemove-1 as cr ended line before start of deletion\n\t\t\tRemoveLine(lineRemove - 1);\n\t\t\tplv->SetLineStart(lineRemove - 1, position + 1);\n\t\t}\n\t}\n\tsubstance.DeleteRange(position, deleteLength);\n\tif (lineRecalculateStart >= 0) {\n\t\tRecalculateIndexLineStarts(lineRecalculateStart, lineRecalculateStart);\n\t}\n\tif (hasStyles) {\n\t\tstyle.DeleteRange(position, deleteLength);\n\t}\n}\n\nbool CellBuffer::SetUndoCollection(bool collectUndo) noexcept {\n\tcollectingUndo = collectUndo;\n\tuh->DropUndoSequence();\n\treturn collectingUndo;\n}\n\nbool CellBuffer::IsCollectingUndo() const noexcept {\n\treturn collectingUndo;\n}\n\nvoid CellBuffer::BeginUndoAction(bool mayCoalesce) noexcept {\n\tuh->BeginUndoAction(mayCoalesce);\n}\n\nvoid CellBuffer::EndUndoAction() noexcept {\n\tuh->EndUndoAction();\n}\n\nint CellBuffer::UndoSequenceDepth() const noexcept {\n\treturn uh->UndoSequenceDepth();\n}\n\nbool CellBuffer::AfterUndoSequenceStart() const noexcept {\n\treturn uh->AfterUndoSequenceStart();\n}\n\nvoid CellBuffer::AddUndoAction(Sci::Position token, bool mayCoalesce) {\n\tbool startSequence = false;\n\tuh->AppendAction(ActionType::container, token, nullptr, 0, startSequence, mayCoalesce);\n}\n\nvoid CellBuffer::DeleteUndoHistory() noexcept {\n\tuh->DeleteUndoHistory();\n}\n\nbool CellBuffer::CanUndo() const noexcept {\n\treturn uh->CanUndo();\n}\n\nint CellBuffer::StartUndo() noexcept {\n\treturn uh->StartUndo();\n}\n\nAction CellBuffer::GetUndoStep() const noexcept {\n\treturn uh->GetUndoStep();\n}\n\nvoid CellBuffer::PerformUndoStep() {\n\tconst Action previousStep = uh->GetUndoStep();\n\t// PreviousBeforeSavePoint and AfterDetachPoint are called since acting on the previous action,\n\t// that is currentAction-1\n\tif (changeHistory && uh->PreviousBeforeSavePoint()) {\n\t\tchangeHistory->StartReversion();\n\t}\n\tif (previousStep.at == ActionType::insert) {\n\t\tif (substance.Length() < previousStep.lenData) {\n\t\t\tthrow std::runtime_error(\n\t\t\t\t\"CellBuffer::PerformUndoStep: deletion must be less than document length.\");\n\t\t}\n\t\tif (changeHistory) {\n\t\t\tchangeHistory->DeleteRange(previousStep.position, previousStep.lenData,\n\t\t\t\tuh->PreviousBeforeSavePoint() && !uh->AfterDetachPoint());\n\t\t}\n\t\tBasicDeleteChars(previousStep.position, previousStep.lenData);\n\t} else if (previousStep.at == ActionType::remove) {\n\t\tBasicInsertString(previousStep.position, previousStep.data, previousStep.lenData);\n\t\tif (changeHistory) {\n\t\t\tchangeHistory->UndoDeleteStep(previousStep.position, previousStep.lenData, uh->AfterDetachPoint());\n\t\t}\n\t}\n\tuh->CompletedUndoStep();\n}\n\nbool CellBuffer::CanRedo() const noexcept {\n\treturn uh->CanRedo();\n}\n\nint CellBuffer::StartRedo() noexcept {\n\treturn uh->StartRedo();\n}\n\nAction CellBuffer::GetRedoStep() const noexcept {\n\treturn uh->GetRedoStep();\n}\n\nvoid CellBuffer::PerformRedoStep() {\n\tconst Action actionStep = uh->GetRedoStep();\n\tif (actionStep.at == ActionType::insert) {\n\t\tBasicInsertString(actionStep.position, actionStep.data, actionStep.lenData);\n\t\tif (changeHistory) {\n\t\t\tchangeHistory->Insert(actionStep.position, actionStep.lenData, collectingUndo,\n\t\t\t\tuh->BeforeSavePoint() && !uh->AfterOrAtDetachPoint());\n\t\t}\n\t} else if (actionStep.at == ActionType::remove) {\n\t\tif (changeHistory) {\n\t\t\tchangeHistory->DeleteRangeSavingHistory(actionStep.position, actionStep.lenData,\n\t\t\t\tuh->BeforeReachableSavePoint(), uh->AfterOrAtDetachPoint());\n\t\t}\n\t\tBasicDeleteChars(actionStep.position, actionStep.lenData);\n\t}\n\tif (changeHistory && uh->AfterSavePoint()) {\n\t\tchangeHistory->EndReversion();\n\t}\n\tuh->CompletedRedoStep();\n}\n\nint CellBuffer::UndoActions() const noexcept {\n\treturn uh->Actions();\n}\n\nvoid CellBuffer::SetUndoSavePoint(int action) noexcept {\n\tuh->SetSavePoint(action);\n}\n\nint CellBuffer::UndoSavePoint() const noexcept {\n\treturn uh->SavePoint();\n}\n\nvoid CellBuffer::SetUndoDetach(int action) noexcept {\n\tuh->SetDetachPoint(action);\n}\n\nint CellBuffer::UndoDetach() const noexcept {\n\treturn uh->DetachPoint();\n}\n\nvoid CellBuffer::SetUndoTentative(int action) noexcept {\n\tuh->SetTentative(action);\n}\n\nint CellBuffer::UndoTentative() const noexcept {\n\treturn uh->TentativePoint();\n}\n\nnamespace {\n\nvoid RestoreChangeHistory(const UndoHistory *uh, ChangeHistory *changeHistory) {\n\t// Replay all undo actions into changeHistory\n\tconst int savePoint = uh->SavePoint();\n\tconst int detachPoint = uh->DetachPoint();\n\tconst int currentPoint = uh->Current();\n\tfor (int act = 0; act < uh->Actions(); act++) {\n\t\tconst ActionType type = static_cast<ActionType>(uh->Type(act) & ~coalesceFlag);\n\t\tconst Sci::Position position = uh->Position(act);\n\t\tconst Sci::Position length = uh->Length(act);\n\t\tconst bool beforeSave = act < savePoint || ((detachPoint >= 0) && (detachPoint > act));\n\t\tconst bool afterDetach = (detachPoint >= 0) && (detachPoint < act);\n\t\tswitch (type) {\n\t\tcase ActionType::insert:\n\t\t\tchangeHistory->Insert(position, length, true, beforeSave);\n\t\t\tbreak;\n\t\tcase ActionType::remove:\n\t\t\tchangeHistory->DeleteRangeSavingHistory(position, length, beforeSave, afterDetach);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\t// Only insertions and deletions go into change history\n\t\t\tbreak;\n\t\t}\n\t\tchangeHistory->Check();\n\t}\n\t// Undo back to currentPoint, updating change history\n\tfor (int act = uh->Actions() - 1; act >= currentPoint; act--) {\n\t\tconst ActionType type = static_cast<ActionType>(uh->Type(act) & ~coalesceFlag);\n\t\tconst Sci::Position position = uh->Position(act);\n\t\tconst Sci::Position length = uh->Length(act);\n\t\tconst bool beforeSave = act < savePoint;\n\t\tconst bool afterDetach = (detachPoint >= 0) && (detachPoint < act);\n\t\tif (beforeSave) {\n\t\t\tchangeHistory->StartReversion();\n\t\t}\n\t\tswitch (type) {\n\t\tcase ActionType::insert:\n\t\t\tchangeHistory->DeleteRange(position, length, beforeSave && !afterDetach);\n\t\t\tbreak;\n\t\tcase ActionType::remove:\n\t\t\tchangeHistory->UndoDeleteStep(position, length, afterDetach);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\t// Only insertions and deletions go into change history\n\t\t\tbreak;\n\t\t}\n\t\tchangeHistory->Check();\n\t}\n}\n\n}\n\nvoid CellBuffer::SetUndoCurrent(int action) {\n\tuh->SetCurrent(action, Length());\n\tif (changeHistory) {\n\t\tif ((uh->DetachPoint() >= 0) && (uh->SavePoint() >= 0)) {\n\t\t\t// Can't have a valid save point and a valid detach point at same time\n\t\t\tuh->DeleteUndoHistory();\n\t\t\tchangeHistory.reset();\n\t\t\tthrow std::runtime_error(\"UndoHistory::SetCurrent: invalid undo history.\");\n\t\t}\n\t\tconst intptr_t sizeChange = uh->Delta(action);\n\t\tconst intptr_t lengthOriginal = Length() - sizeChange;\n\t\t// Recreate empty change history\n\t\tchangeHistory = std::make_unique<ChangeHistory>(lengthOriginal);\n\t\tRestoreChangeHistory(uh.get(), changeHistory.get());\n\t\tif (Length() != changeHistory->Length()) {\n\t\t\tuh->DeleteUndoHistory();\n\t\t\tchangeHistory.reset();\n\t\t\tthrow std::runtime_error(\"UndoHistory::SetCurrent: invalid undo history.\");\n\t\t}\n\t}\n}\n\nint CellBuffer::UndoCurrent() const noexcept {\n\treturn uh->Current();\n}\n\nint CellBuffer::UndoActionType(int action) const noexcept {\n\treturn uh->Type(action);\n}\n\nSci::Position CellBuffer::UndoActionPosition(int action) const noexcept {\n\treturn uh->Position(action);\n}\n\nstd::string_view CellBuffer::UndoActionText(int action) const noexcept {\n\treturn uh->Text(action);\n}\n\nvoid CellBuffer::PushUndoActionType(int type, Sci::Position position) {\n\tuh->PushUndoActionType(type, position);\n}\n\nvoid CellBuffer::ChangeLastUndoActionText(size_t length, const char *text) {\n\tuh->ChangeLastUndoActionText(length, text);\n}\n\nvoid CellBuffer::ChangeHistorySet(bool set) {\n\tif (set) {\n\t\tif (!changeHistory && !uh->CanUndo()) {\n\t\t\tchangeHistory = std::make_unique<ChangeHistory>(Length());\n\t\t}\n\t} else {\n\t\tchangeHistory.reset();\n\t}\n}\n\nint CellBuffer::EditionAt(Sci::Position pos) const noexcept {\n\tif (changeHistory) {\n\t\treturn changeHistory->EditionAt(pos);\n\t}\n\treturn 0;\n}\n\nSci::Position CellBuffer::EditionEndRun(Sci::Position pos) const noexcept {\n\tif (changeHistory) {\n\t\treturn changeHistory->EditionEndRun(pos);\n\t}\n\treturn Length();\n}\n\nunsigned int CellBuffer::EditionDeletesAt(Sci::Position pos) const noexcept {\n\tif (changeHistory) {\n\t\treturn changeHistory->EditionDeletesAt(pos);\n\t}\n\treturn 0;\n}\n\nSci::Position CellBuffer::EditionNextDelete(Sci::Position pos) const noexcept {\n\tif (changeHistory) {\n\t\treturn changeHistory->EditionNextDelete(pos);\n\t}\n\treturn Length() + 1;\n}\n"
  },
  {
    "path": "Libraries/scintilla/src/CellBuffer.h",
    "content": "// Scintilla source code edit control\n/** @file CellBuffer.h\n ** Manages the text of the document.\n **/\n// Copyright 1998-2004 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#ifndef CELLBUFFER_H\n#define CELLBUFFER_H\n\nnamespace Scintilla::Internal {\n\n// Interface to per-line data that wants to see each line insertion and deletion\nclass PerLine {\npublic:\n\tvirtual ~PerLine() {}\n\tvirtual void Init()=0;\n\tvirtual void InsertLine(Sci::Line line)=0;\n\tvirtual void InsertLines(Sci::Line line, Sci::Line lines) = 0;\n\tvirtual void RemoveLine(Sci::Line line)=0;\n};\n\nclass UndoHistory;\nclass ChangeHistory;\n\n/**\n * The line vector contains information about each of the lines in a cell buffer.\n */\nclass ILineVector;\n\nenum class ActionType : unsigned char { insert, remove, container };\n\n/**\n * Actions are used to return the information required to report one undo/redo step.\n */\nstruct Action {\n\tActionType at = ActionType::insert;\n\tbool mayCoalesce = false;\n\tSci::Position position = 0;\n\tconst char *data = nullptr;\n\tSci::Position lenData = 0;\n};\n\nstruct SplitView {\n\tconst char *segment1 = nullptr;\n\tsize_t length1 = 0;\n\tconst char *segment2 = nullptr;\n\tsize_t length = 0;\n\n\tbool operator==(const SplitView &other) const noexcept {\n\t\treturn segment1 == other.segment1 && length1 == other.length1 &&\n\t\t\tsegment2 == other.segment2 && length == other.length;\n\t}\n\tbool operator!=(const SplitView &other) const noexcept {\n\t\treturn !(*this == other);\n\t}\n\n\tchar CharAt(size_t position) const noexcept {\n\t\tif (position < length1) {\n\t\t\treturn segment1[position];\n\t\t}\n\t\tif (position < length) {\n\t\t\treturn segment2[position];\n\t\t}\n\t\treturn 0;\n\t}\n};\n\n\n/**\n * Holder for an expandable array of characters that supports undo and line markers.\n * Based on article \"Data Structures in a Bit-Mapped Text Editor\"\n * by Wilfred J. Hansen, Byte January 1987, page 183.\n */\nclass CellBuffer {\nprivate:\n\tbool hasStyles;\n\tbool largeDocument;\n\tSplitVector<char> substance;\n\tSplitVector<char> style;\n\tbool readOnly;\n\tbool utf8Substance;\n\tScintilla::LineEndType utf8LineEnds;\n\n\tbool collectingUndo;\n\tstd::unique_ptr<UndoHistory> uh;\n\n\tstd::unique_ptr<ChangeHistory> changeHistory;\n\n\tstd::unique_ptr<ILineVector> plv;\n\n\tbool UTF8LineEndOverlaps(Sci::Position position) const noexcept;\n\tbool UTF8IsCharacterBoundary(Sci::Position position) const;\n\tvoid ResetLineEnds();\n\tvoid RecalculateIndexLineStarts(Sci::Line lineFirst, Sci::Line lineLast);\n\tbool MaintainingLineCharacterIndex() const noexcept;\n\t/// Actions without undo\n\tvoid BasicInsertString(Sci::Position position, const char *s, Sci::Position insertLength);\n\tvoid BasicDeleteChars(Sci::Position position, Sci::Position deleteLength);\n\npublic:\n\n\tCellBuffer(bool hasStyles_, bool largeDocument_);\n\t// Deleted so CellBuffer objects can not be copied.\n\tCellBuffer(const CellBuffer &) = delete;\n\tCellBuffer(CellBuffer &&) = delete;\n\tCellBuffer &operator=(const CellBuffer &) = delete;\n\tCellBuffer &operator=(CellBuffer &&) = delete;\n\t~CellBuffer() noexcept;\n\n\t/// Retrieving positions outside the range of the buffer works and returns 0\n\tchar CharAt(Sci::Position position) const noexcept;\n\tunsigned char UCharAt(Sci::Position position) const noexcept;\n\tvoid GetCharRange(char *buffer, Sci::Position position, Sci::Position lengthRetrieve) const;\n\tchar StyleAt(Sci::Position position) const noexcept;\n\tvoid GetStyleRange(unsigned char *buffer, Sci::Position position, Sci::Position lengthRetrieve) const;\n\tconst char *BufferPointer();\n\tconst char *RangePointer(Sci::Position position, Sci::Position rangeLength) noexcept;\n\tSci::Position GapPosition() const noexcept;\n\tSplitView AllView() const noexcept;\n\n\tSci::Position Length() const noexcept;\n\tvoid Allocate(Sci::Position newSize);\n\tvoid SetUTF8Substance(bool utf8Substance_) noexcept;\n\tScintilla::LineEndType GetLineEndTypes() const noexcept { return utf8LineEnds; }\n\tvoid SetLineEndTypes(Scintilla::LineEndType utf8LineEnds_);\n\tbool ContainsLineEnd(const char *s, Sci::Position length) const noexcept;\n\tvoid SetPerLine(PerLine *pl) noexcept;\n\tScintilla::LineCharacterIndexType LineCharacterIndex() const noexcept;\n\tvoid AllocateLineCharacterIndex(Scintilla::LineCharacterIndexType lineCharacterIndex);\n\tvoid ReleaseLineCharacterIndex(Scintilla::LineCharacterIndexType lineCharacterIndex);\n\tSci::Line Lines() const noexcept;\n\tvoid AllocateLines(Sci::Line lines);\n\tSci::Position LineStart(Sci::Line line) const noexcept;\n\tSci::Position LineEnd(Sci::Line line) const noexcept;\n\tSci::Position IndexLineStart(Sci::Line line, Scintilla::LineCharacterIndexType lineCharacterIndex) const noexcept;\n\tSci::Line LineFromPosition(Sci::Position pos) const noexcept;\n\tSci::Line LineFromPositionIndex(Sci::Position pos, Scintilla::LineCharacterIndexType lineCharacterIndex) const noexcept;\n\tvoid InsertLine(Sci::Line line, Sci::Position position, bool lineStart);\n\tvoid RemoveLine(Sci::Line line);\n\tconst char *InsertString(Sci::Position position, const char *s, Sci::Position insertLength, bool &startSequence);\n\n\t/// Setting styles for positions outside the range of the buffer is safe and has no effect.\n\t/// @return true if the style of a character is changed.\n\tbool SetStyleAt(Sci::Position position, char styleValue) noexcept;\n\tbool SetStyleFor(Sci::Position position, Sci::Position lengthStyle, char styleValue) noexcept;\n\n\tconst char *DeleteChars(Sci::Position position, Sci::Position deleteLength, bool &startSequence);\n\n\tbool IsReadOnly() const noexcept;\n\tvoid SetReadOnly(bool set) noexcept;\n\tbool IsLarge() const noexcept;\n\tbool HasStyles() const noexcept;\n\n\t/// The save point is a marker in the undo stack where the container has stated that\n\t/// the buffer was saved. Undo and redo can move over the save point.\n\tvoid SetSavePoint();\n\tbool IsSavePoint() const noexcept;\n\n\tvoid TentativeStart() noexcept;\n\tvoid TentativeCommit() noexcept;\n\tbool TentativeActive() const noexcept;\n\tint TentativeSteps() noexcept;\n\n\tbool SetUndoCollection(bool collectUndo) noexcept;\n\tbool IsCollectingUndo() const noexcept;\n\tvoid BeginUndoAction(bool mayCoalesce=false) noexcept;\n\tvoid EndUndoAction() noexcept;\n\tint UndoSequenceDepth() const noexcept;\n\tbool AfterUndoSequenceStart() const noexcept;\n\tvoid AddUndoAction(Sci::Position token, bool mayCoalesce);\n\tvoid DeleteUndoHistory() noexcept;\n\n\t/// To perform an undo, StartUndo is called to retrieve the number of steps, then UndoStep is\n\t/// called that many times. Similarly for redo.\n\tbool CanUndo() const noexcept;\n\tint StartUndo() noexcept;\n\tAction GetUndoStep() const noexcept;\n\tvoid PerformUndoStep();\n\tbool CanRedo() const noexcept;\n\tint StartRedo() noexcept;\n\tAction GetRedoStep() const noexcept;\n\tvoid PerformRedoStep();\n\n\tint UndoActions() const noexcept;\n\tvoid SetUndoSavePoint(int action) noexcept;\n\tint UndoSavePoint() const noexcept;\n\tvoid SetUndoDetach(int action) noexcept;\n\tint UndoDetach() const noexcept;\n\tvoid SetUndoTentative(int action) noexcept;\n\tint UndoTentative() const noexcept;\n\tvoid SetUndoCurrent(int action);\n\tint UndoCurrent() const noexcept;\n\tint UndoActionType(int action) const noexcept;\n\tSci::Position UndoActionPosition(int action) const noexcept;\n\tstd::string_view UndoActionText(int action) const noexcept;\n\tvoid PushUndoActionType(int type, Sci::Position position);\n\tvoid ChangeLastUndoActionText(size_t length, const char *text);\n\n\tvoid ChangeHistorySet(bool set);\n\t[[nodiscard]] int EditionAt(Sci::Position pos) const noexcept;\n\t[[nodiscard]] Sci::Position EditionEndRun(Sci::Position pos) const noexcept;\n\t[[nodiscard]] unsigned int EditionDeletesAt(Sci::Position pos) const noexcept;\n\t[[nodiscard]] Sci::Position EditionNextDelete(Sci::Position pos) const noexcept;\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/src/ChangeHistory.cxx",
    "content": "// Scintilla source code edit control\n/** @file ChangeHistory.cxx\n ** Manages a history of changes in a document.\n **/\n// Copyright 2022 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#include <cstddef>\n#include <cstdlib>\n#include <cstdint>\n#include <cassert>\n\n#include <stdexcept>\n#include <vector>\n#include <set>\n#include <algorithm>\n#include <memory>\n\n#include \"ScintillaTypes.h\"\n\n#include \"Debugging.h\"\n\n#include \"Position.h\"\n#include \"SplitVector.h\"\n#include \"Partitioning.h\"\n#include \"RunStyles.h\"\n#include \"SparseVector.h\"\n#include \"ChangeHistory.h\"\n\nnamespace Scintilla::Internal {\n\nvoid ChangeStack::Clear() noexcept {\n\tsteps.clear();\n\tchanges.clear();\n}\n\nvoid ChangeStack::AddStep() {\n\tsteps.push_back(0);\n}\n\nnamespace {\n\nconstexpr bool InsertionSpanSameDeletion(const ChangeSpan &is, Sci::Position positionDeletion, int edition) {\n\t// Equal except for count\n\treturn\n\t\tis.direction == ChangeSpan::Direction::deletion &&\n\t\tis.start == positionDeletion &&\n\t\tis.length == 0 &&\n\t\tis.edition == edition;\n}\n\n}\n\nvoid ChangeStack::PushDeletion(Sci::Position positionDeletion, const EditionCount &ec) {\n\tsteps.back() += ec.count;\n\tif (changes.empty() || !InsertionSpanSameDeletion(changes.back(), positionDeletion, ec.edition)) {\n\t\tchanges.push_back({ positionDeletion, 0, ec.edition, ec.count, ChangeSpan::Direction::deletion });\n\t} else {\n\t\tchanges.back().count += ec.count;\n\t}\n}\n\nvoid ChangeStack::PushInsertion(Sci::Position positionInsertion, Sci::Position length, int edition) {\n\tsteps.back()++;\n\tchanges.push_back({ positionInsertion, length, edition, 1, ChangeSpan::Direction::insertion });\n}\n\nint ChangeStack::PopStep() noexcept {\n\tconst int spans = steps.back();\n\tsteps.pop_back();\n\treturn spans;\n}\n\nChangeSpan ChangeStack::PopSpan(int maxSteps) noexcept {\n\tChangeSpan span = changes.back();\n\tconst int remove = std::min(maxSteps, span.count);\n\tif (span.count == remove) {\n\t\tchanges.pop_back();\n\t} else {\n\t\tchanges.back().count -= remove;\n\t\tspan.count = remove;\n\t}\n\treturn span;\n}\n\nvoid ChangeStack::SetSavePoint() noexcept {\n\t// Switch changeUnsaved to changeSaved\n\tfor (ChangeSpan &x : changes) {\n\t\tif (x.edition == changeModified) {\n\t\t\tx.edition = changeSaved;\n\t\t}\n\t}\n}\n\nvoid ChangeStack::Check() const noexcept {\n#ifdef _DEBUG\n\t// Ensure count in steps same as insertions;\n\tint sizeSteps = 0;\n\tfor (const int c : steps) {\n\t\tsizeSteps += c;\n\t}\n\tint sizeInsertions = 0;\n\tfor (const ChangeSpan &is: changes) {\n\t\tsizeInsertions += is.count;\n\t}\n\tassert(sizeSteps == sizeInsertions);\n#endif\n}\n\nvoid ChangeLog::Clear(Sci::Position length) {\n\tchangeStack.Clear();\n\tinsertEdition.DeleteAll();\n\tdeleteEdition.DeleteAll();\n\tInsertSpace(0, length);\n}\n\nvoid ChangeLog::InsertSpace(Sci::Position position, Sci::Position insertLength) {\n\tassert(insertEdition.Length() == deleteEdition.Length());\n\tinsertEdition.InsertSpace(position, insertLength);\n\tdeleteEdition.InsertSpace(position, insertLength);\n}\n\nvoid ChangeLog::DeleteRange(Sci::Position position, Sci::Position deleteLength) {\n\tinsertEdition.DeleteRange(position, deleteLength);\n\tconst EditionSetOwned &editions = deleteEdition.ValueAt(position);\n\tif (editions) {\n\t\tconst EditionSet savedEditions = *editions;\n\t\tdeleteEdition.DeleteRange(position, deleteLength);\n\t\tEditionSetOwned reset = std::make_unique<EditionSet>(savedEditions);\n\t\tdeleteEdition.SetValueAt(position, std::move(reset));\n\t} else {\n\t\tdeleteEdition.DeleteRange(position, deleteLength);\n\t}\n\tassert(insertEdition.Length() == deleteEdition.Length());\n}\n\nvoid ChangeLog::Insert(Sci::Position start, Sci::Position length, int edition) {\n\tinsertEdition.FillRange(start, edition, length);\n}\n\nvoid ChangeLog::CollapseRange(Sci::Position position, Sci::Position deleteLength) {\n\tconst Sci::Position positionMax = position + deleteLength;\n\tSci::Position positionDeletion = position + 1;\n\twhile (positionDeletion <= positionMax) {\n\t\tconst EditionSetOwned &editions = deleteEdition.ValueAt(positionDeletion);\n\t\tif (editions) {\n\t\t\tfor (const EditionCount &ec : *editions) {\n\t\t\t\tPushDeletionAt(position, ec);\n\t\t\t}\n\t\t\tEditionSetOwned empty;\n\t\t\tdeleteEdition.SetValueAt(positionDeletion, std::move(empty));\n\t\t}\n\t\tpositionDeletion = deleteEdition.PositionNext(positionDeletion);\n\t}\n}\n\nnamespace {\n\n// EditionSets have repeat counts on items so push and pop may just\n// manipulate the count field or may push/pop items.\n\nvoid EditionSetPush(EditionSet &set, EditionCount ec) {\n\tif (set.empty() || (set.back().edition != ec.edition)) {\n\t\tset.push_back(ec);\n\t} else {\n\t\tset.back().count += ec.count;\n\t}\n}\n\nvoid EditionSetPop(EditionSet &set) noexcept {\n\tif (set.back().count == 1) {\n\t\tset.pop_back();\n\t} else {\n\t\tset.back().count--;\n\t}\n}\n\nint EditionSetCount(const EditionSet &set) noexcept {\n\tint count = 0;\n\tfor (const EditionCount &ec : set) {\n\t\tcount += ec.count;\n\t}\n\treturn count;\n}\n\n}\n\nvoid ChangeLog::PushDeletionAt(Sci::Position position, EditionCount ec) {\n\tif (!deleteEdition.ValueAt(position)) {\n\t\tdeleteEdition.SetValueAt(position, std::make_unique<EditionSet>());\n\t}\n\tEditionSetPush(*deleteEdition.ValueAt(position), ec);\n}\n\nvoid ChangeLog::InsertFrontDeletionAt(Sci::Position position, EditionCount ec) {\n\tif (!deleteEdition.ValueAt(position)) {\n\t\tdeleteEdition.SetValueAt(position, std::make_unique<EditionSet>());\n\t}\n\tconst EditionSetOwned &editions = deleteEdition.ValueAt(position);\n\teditions->insert(editions->begin(), ec);\n}\n\nvoid ChangeLog::SaveRange(Sci::Position position, Sci::Position length) {\n\t// Save insertEdition range into undo stack\n\tchangeStack.AddStep();\n\tSci::Position positionInsertion = position;\n\tconst ptrdiff_t editionStart = insertEdition.ValueAt(positionInsertion);\n\tif (editionStart == 0) {\n\t\tpositionInsertion = insertEdition.EndRun(positionInsertion);\n\t}\n\tconst Sci::Position positionMax = position + length;\n\twhile (positionInsertion < positionMax) {\n\t\tconst Sci::Position positionEndInsertion = insertEdition.EndRun(positionInsertion);\n\t\tchangeStack.PushInsertion(positionInsertion, std::min(positionEndInsertion, positionMax) - positionInsertion,\n\t\t\tinsertEdition.ValueAt(positionInsertion));\n\t\tpositionInsertion = insertEdition.EndRun(positionEndInsertion);\n\t}\n\tSci::Position positionDeletion = position + 1;\n\twhile (positionDeletion <= positionMax) {\n\t\tconst EditionSetOwned &editions = deleteEdition.ValueAt(positionDeletion);\n\t\tif (editions) {\n\t\t\tfor (const EditionCount &ec : *editions) {\n\t\t\t\tchangeStack.PushDeletion(positionDeletion, ec);\n\t\t\t}\n\t\t}\n\t\tpositionDeletion = deleteEdition.PositionNext(positionDeletion);\n\t}\n}\n\nvoid ChangeLog::PopDeletion(Sci::Position position, Sci::Position deleteLength) {\n\t// Just performed InsertSpace(position, deleteLength) so *this* element in\n\t// deleteEdition moved forward by deleteLength\n\tEditionSetOwned eso = deleteEdition.Extract(position + deleteLength);\n\tdeleteEdition.SetValueAt(position, std::move(eso));\n\tconst EditionSetOwned &editions = deleteEdition.ValueAt(position);\n\tassert(editions);\n\tEditionSetPop(*editions);\n\tconst int inserts = changeStack.PopStep();\n\tfor (int i = 0; i < inserts;) {\n\t\tconst ChangeSpan span = changeStack.PopSpan(inserts);\n\t\tif (span.direction == ChangeSpan::Direction::insertion) {\n\t\t\tassert(span.count == 1);\t// Insertions are never compressed\n\t\t\tinsertEdition.FillRange(span.start, span.edition, span.length);\n\t\t\ti++;\n\t\t} else {\n\t\t\tassert(editions);\n\t\t\tassert(editions->back().edition == span.edition);\n\t\t\tfor (int j = 0; j < span.count; j++) {\n\t\t\t\tEditionSetPop(*editions);\n\t\t\t}\n\t\t\t// Iterating backwards (pop) through changeStack, reverse order of insertion\n\t\t\t// and original deletion list.\n\t\t\t// Therefore need to insert at front to recreate original order.\n\t\t\tInsertFrontDeletionAt(span.start, { span.edition, span.count });\n\t\t\ti += span.count;\n\t\t}\n\t}\n\n\tif (editions->empty()) {\n\t\tdeleteEdition.SetValueAt(position, EditionSetOwned{});\n\t}\n}\n\nvoid ChangeLog::SaveHistoryForDelete(Sci::Position position, Sci::Position deleteLength) {\n\tassert(position >= 0);\n\tassert(deleteLength >= 0);\n\tassert(position + deleteLength <= Length());\n\tSaveRange(position, deleteLength);\n\tCollapseRange(position, deleteLength);\n}\n\nvoid ChangeLog::DeleteRangeSavingHistory(Sci::Position position, Sci::Position deleteLength) {\n\tSaveHistoryForDelete(position, deleteLength);\n\tDeleteRange(position, deleteLength);\n}\n\nvoid ChangeLog::SetSavePoint() {\n\t// Switch changeUnsaved to changeSaved\n\tchangeStack.SetSavePoint();\n\n\tconst Sci::Position length = insertEdition.Length();\n\n\tfor (Sci::Position startRun = 0; startRun < length;) {\n\t\tconst Sci::Position endRun = insertEdition.EndRun(startRun);\n\t\tif (insertEdition.ValueAt(startRun) == changeModified) {\n\t\t\tinsertEdition.FillRange(startRun, changeSaved, endRun - startRun);\n\t\t}\n\t\tstartRun = endRun;\n\t}\n\n\tfor (Sci::Position positionDeletion = 0; positionDeletion <= length;) {\n\t\tconst EditionSetOwned &editions = deleteEdition.ValueAt(positionDeletion);\n\t\tif (editions) {\n\t\t\tfor (EditionCount &ec : *editions) {\n\t\t\t\tif (ec.edition == changeModified) {\n\t\t\t\t\tec.edition = changeSaved;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tpositionDeletion = deleteEdition.PositionNext(positionDeletion);\n\t}\n}\n\nSci::Position ChangeLog::Length() const noexcept {\n\treturn insertEdition.Length();\n}\n\nsize_t ChangeLog::DeletionCount(Sci::Position start, Sci::Position length) const noexcept {\n\tconst Sci::Position end = start + length;\n\tsize_t count = 0;\n\twhile (start <= end) {\n\t\tconst EditionSetOwned &editions = deleteEdition.ValueAt(start);\n\t\tif (editions) {\n\t\t\tcount += EditionSetCount(*editions);\n\t\t}\n\t\tstart = deleteEdition.PositionNext(start);\n\t}\n\treturn count;\n}\n\nvoid ChangeLog::Check() const noexcept {\n\tassert(insertEdition.Length() == deleteEdition.Length());\n\tchangeStack.Check();\n}\n\nChangeHistory::ChangeHistory(Sci::Position length) {\n\tchangeLog.Clear(length);\n}\n\nvoid ChangeHistory::Insert(Sci::Position position, Sci::Position insertLength, bool collectingUndo, bool beforeSave) {\n\tCheck();\n\tchangeLog.InsertSpace(position, insertLength);\n\tconst int edition = collectingUndo ? (beforeSave ? changeSaved : changeModified) :\n\t\tchangeOriginal;\n\tchangeLog.Insert(position, insertLength, edition);\n\tif (changeLogReversions) {\n\t\tchangeLogReversions->InsertSpace(position, insertLength);\n\t\tif (beforeSave) {\n\t\t\tchangeLogReversions->PopDeletion(position, insertLength);\n\t\t}\n\t}\n\tCheck();\n}\n\nvoid ChangeHistory::DeleteRange(Sci::Position position, Sci::Position deleteLength, bool reverting) {\n\tCheck();\n\tassert(DeletionCount(position, deleteLength-1) == 0);\n\tchangeLog.DeleteRange(position, deleteLength);\n\tif (changeLogReversions) {\n\t\tchangeLogReversions->DeleteRangeSavingHistory(position, deleteLength);\n\t\tif (reverting) {\n\t\t\tchangeLogReversions->PushDeletionAt(position, { changeRevertedOriginal, 1 });\n\t\t}\n\t}\n\tCheck();\n}\n\nvoid ChangeHistory::DeleteRangeSavingHistory(Sci::Position position, Sci::Position deleteLength, bool beforeSave, bool isDetached) {\n\tchangeLog.DeleteRangeSavingHistory(position, deleteLength);\n\tchangeLog.PushDeletionAt(position, { beforeSave ? changeSaved : changeModified, 1 });\n\tif (changeLogReversions) {\n\t\tif (isDetached) {\n\t\t\tchangeLogReversions->SaveHistoryForDelete(position, deleteLength);\n\t\t}\n\t\tchangeLogReversions->DeleteRange(position, deleteLength);\n\t}\n\tCheck();\n}\n\nvoid ChangeHistory::StartReversion() {\n\tif (!changeLogReversions) {\n\t\tchangeLogReversions = std::make_unique<ChangeLog>();\n\t\tchangeLogReversions->Clear(changeLog.Length());\n\t}\n\tCheck();\n}\n\nvoid ChangeHistory::EndReversion() noexcept {\n\tchangeLogReversions.reset();\n\tCheck();\n}\n\nvoid ChangeHistory::SetSavePoint() {\n\tchangeLog.SetSavePoint();\n\tEndReversion();\n}\n\nvoid ChangeHistory::UndoDeleteStep(Sci::Position position, Sci::Position deleteLength, bool isDetached) {\n\tCheck();\n\tchangeLog.InsertSpace(position, deleteLength);\n\tchangeLog.PopDeletion(position, deleteLength);\n\tif (changeLogReversions) {\n\t\tchangeLogReversions->InsertSpace(position, deleteLength);\n\t\tif (!isDetached) {\n\t\t\tchangeLogReversions->Insert(position, deleteLength, 1);\n\t\t}\n\t}\n\tCheck();\n}\n\nSci::Position ChangeHistory::Length() const noexcept {\n\treturn changeLog.Length();\n}\n\nvoid ChangeHistory::SetEpoch(int epoch) noexcept {\n\thistoricEpoch = epoch;\n}\n\nvoid ChangeHistory::EditionCreateHistory(Sci::Position start, Sci::Position length) {\n\tif (start <= changeLog.Length()) {\n\t\tif (length) {\n\t\t\tchangeLog.insertEdition.FillRange(start, historicEpoch, length);\n\t\t} else {\n\t\t\tchangeLog.PushDeletionAt(start, { historicEpoch, 1 });\n\t\t}\n\t}\n}\n\n// Editions:\n// <0 History\n// 0 Original unchanged\n// 1 Reverted to origin\n// 2 Saved\n// 3 Unsaved\n// 4 Reverted to change\nint ChangeHistory::EditionAt(Sci::Position pos) const noexcept {\n\tconst int edition = changeLog.insertEdition.ValueAt(pos);\n\tif (changeLogReversions) {\n\t\tconst int editionReversion = changeLogReversions->insertEdition.ValueAt(pos);\n\t\tif (editionReversion) {\n\t\t\tif (edition < 0)\t// Historical revision\n\t\t\t\treturn changeRevertedOriginal;\n\t\t\treturn edition ? changeRevertedToChange : changeRevertedOriginal;\n\t\t}\n\t}\n\treturn edition;\n}\n\nSci::Position ChangeHistory::EditionEndRun(Sci::Position pos) const noexcept {\n\tif (changeLogReversions) {\n\t\tassert(changeLogReversions->Length() == changeLog.Length());\n\t\tconst Sci::Position nextReversion = changeLogReversions->insertEdition.EndRun(pos);\n\t\tconst Sci::Position next = changeLog.insertEdition.EndRun(pos);\n\t\treturn std::min(next, nextReversion);\n\t}\n\treturn changeLog.insertEdition.EndRun(pos);\n}\n\n// Produce a 4-bit value from the deletions at a position\nunsigned int ChangeHistory::EditionDeletesAt(Sci::Position pos) const noexcept {\n\tunsigned int editionSet = 0;\n\tconst EditionSetOwned &editionSetDeletions = changeLog.deleteEdition.ValueAt(pos);\n\tif (editionSetDeletions) {\n\t\tfor (const EditionCount &ec : *editionSetDeletions) {\n\t\t\teditionSet = editionSet | (1u << (ec.edition-1));\n\t\t}\n\t}\n\tif (changeLogReversions) {\n\t\tconst EditionSetOwned &editionSetReversions = changeLogReversions->deleteEdition.ValueAt(pos);\n\t\tif (editionSetReversions) {\n\t\t\t// If there is no saved or modified -> revertedToOrigin\n\t\t\tif (!(editionSet & (bitSaved | bitModified))) {\n\t\t\t\teditionSet = editionSet | bitRevertedToOriginal;\n\t\t\t} else {\n\t\t\t\teditionSet = editionSet | bitRevertedToModified;\n\t\t\t}\n\t\t}\n\t}\n\treturn editionSet;\n}\n\nSci::Position ChangeHistory::EditionNextDelete(Sci::Position pos) const noexcept {\n\tconst Sci::Position next = changeLog.deleteEdition.PositionNext(pos);\n\tif (changeLogReversions) {\n\t\tconst Sci::Position nextReversion = changeLogReversions->deleteEdition.PositionNext(pos);\n\t\treturn std::min(next, nextReversion);\n\t}\n\treturn next;\n}\n\nsize_t ChangeHistory::DeletionCount(Sci::Position start, Sci::Position length) const noexcept {\n\treturn changeLog.DeletionCount(start, length);\n}\n\nEditionSet ChangeHistory::DeletionsAt(Sci::Position pos) const {\n\tconst EditionSetOwned &editions = changeLog.deleteEdition.ValueAt(pos);\n\tif (editions) {\n\t\treturn *editions;\n\t}\n\treturn {};\n}\n\nvoid ChangeHistory::Check() noexcept {\n\tchangeLog.Check();\n\tif (changeLogReversions) {\n\t\tchangeLogReversions->Check();\n\t\tassert(changeLogReversions->Length() == changeLog.Length());\n\t}\n}\n\n}\n"
  },
  {
    "path": "Libraries/scintilla/src/ChangeHistory.h",
    "content": "// Scintilla source code edit control\n/** @file ChangeHistory.h\n ** Manages a history of changes in a document.\n **/\n// Copyright 2022 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#ifndef CHANGEHISTORY_H\n#define CHANGEHISTORY_H\n\nnamespace Scintilla::Internal {\n\nconstexpr int changeOriginal = 0;\nconstexpr int changeRevertedOriginal = 1;\nconstexpr int changeSaved = 2;\nconstexpr int changeModified = 3;\nconstexpr int changeRevertedToChange = 4;\n\n// As bit flags\nconstexpr unsigned int bitRevertedToOriginal = 1;\nconstexpr unsigned int bitSaved = 2;\nconstexpr unsigned int bitModified = 4;\nconstexpr unsigned int bitRevertedToModified = 8;\n\nstruct ChangeSpan {\n\tSci::Position start;\n\tSci::Position length;\n\tint edition;\n\tint count;\n\tenum class Direction { insertion, deletion } direction;\n};\n\nstruct EditionCount {\n\tint edition;\n\tint count;\n\t// Used in tests.\n\tconstexpr bool operator==(const EditionCount &other) const noexcept {\n\t\treturn (edition == other.edition) && (count == other.count);\n\t}\n};\n\n// EditionSet is ordered from oldest to newest, its not really a set\nusing EditionSet = std::vector<EditionCount>;\nusing EditionSetOwned = std::unique_ptr<EditionSet>;\n\nclass ChangeStack {\n\tstd::vector<int> steps;\n\tstd::vector<ChangeSpan> changes;\npublic:\n\tvoid Clear() noexcept;\n\tvoid AddStep();\n\tvoid PushDeletion(Sci::Position positionDeletion, const EditionCount &ec);\n\tvoid PushInsertion(Sci::Position positionInsertion, Sci::Position length, int edition);\n\t[[nodiscard]] int PopStep() noexcept;\n\t[[nodiscard]] ChangeSpan PopSpan(int maxSteps) noexcept;\n\tvoid SetSavePoint() noexcept;\n\tvoid Check() const noexcept;\n};\n\nstruct ChangeLog {\n\tChangeStack changeStack;\n\tRunStyles<Sci::Position, int> insertEdition;\n\tSparseVector<EditionSetOwned> deleteEdition;\n\n\tvoid Clear(Sci::Position length);\n\tvoid InsertSpace(Sci::Position position, Sci::Position insertLength);\n\tvoid DeleteRange(Sci::Position position, Sci::Position deleteLength);\n\tvoid Insert(Sci::Position start, Sci::Position length, int edition);\n\tvoid CollapseRange(Sci::Position position, Sci::Position deleteLength);\n\tvoid PushDeletionAt(Sci::Position position, EditionCount ec);\n\tvoid InsertFrontDeletionAt(Sci::Position position, EditionCount ec);\n\tvoid SaveRange(Sci::Position position, Sci::Position length);\n\tvoid PopDeletion(Sci::Position position, Sci::Position deleteLength);\n\tvoid SaveHistoryForDelete(Sci::Position position, Sci::Position deleteLength);\n\tvoid DeleteRangeSavingHistory(Sci::Position position, Sci::Position deleteLength);\n\tvoid SetSavePoint();\n\n\tSci::Position Length() const noexcept;\n\t[[nodiscard]] size_t DeletionCount(Sci::Position start, Sci::Position length) const noexcept;\n\tvoid Check() const noexcept;\n};\n\nenum class ReversionState { clear, reverting, detached };\n\nclass ChangeHistory {\n\tChangeLog changeLog;\n\tstd::unique_ptr<ChangeLog> changeLogReversions;\n\tint historicEpoch = -1;\n\npublic:\n\tChangeHistory(Sci::Position length=0);\n\n\tvoid Insert(Sci::Position position, Sci::Position insertLength, bool collectingUndo, bool beforeSave);\n\tvoid DeleteRange(Sci::Position position, Sci::Position deleteLength, bool reverting);\n\tvoid DeleteRangeSavingHistory(Sci::Position position, Sci::Position deleteLength, bool beforeSave, bool isDetached);\n\n\tvoid StartReversion();\n\tvoid EndReversion() noexcept;\n\n\tvoid SetSavePoint();\n\n\tvoid UndoDeleteStep(Sci::Position position, Sci::Position deleteLength, bool isDetached);\n\n\t[[nodiscard]] Sci::Position Length() const noexcept;\n\n\t// Setting up history before this session\n\tvoid SetEpoch(int epoch) noexcept;\n\tvoid EditionCreateHistory(Sci::Position start, Sci::Position length);\n\n\t// Queries for drawing\n\t[[nodiscard]] int EditionAt(Sci::Position pos) const noexcept;\n\t[[nodiscard]] Sci::Position EditionEndRun(Sci::Position pos) const noexcept;\n\t[[nodiscard]] unsigned int EditionDeletesAt(Sci::Position pos) const noexcept;\n\t[[nodiscard]] Sci::Position EditionNextDelete(Sci::Position pos) const noexcept;\n\n\t// Testing - not used by Scintilla\n\t[[nodiscard]] size_t DeletionCount(Sci::Position start, Sci::Position length) const noexcept;\n\tEditionSet DeletionsAt(Sci::Position pos) const;\n\tvoid Check() noexcept;\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/src/CharClassify.cxx",
    "content": "// Scintilla source code edit control\n/** @file CharClassify.cxx\n ** Character classifications used by Document and RESearch.\n **/\n// Copyright 2006 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#include <cstdlib>\n#include <cassert>\n\n#include <stdexcept>\n\n#include \"CharacterType.h\"\n#include \"CharClassify.h\"\n\nusing namespace Scintilla::Internal;\n\nCharClassify::CharClassify() : charClass{} {\n\tSetDefaultCharClasses(true);\n}\n\nvoid CharClassify::SetDefaultCharClasses(bool includeWordClass) {\n\t// Initialize all char classes to default values\n\tfor (int ch = 0; ch < maxChar; ch++) {\n\t\tif (ch == '\\r' || ch == '\\n')\n\t\t\tcharClass[ch] = CharacterClass::newLine;\n\t\telse if (IsControl(ch) || ch == ' ')\n\t\t\tcharClass[ch] = CharacterClass::space;\n\t\telse if (includeWordClass && (ch >= 0x80 || IsAlphaNumeric(ch) || ch == '_'))\n\t\t\tcharClass[ch] = CharacterClass::word;\n\t\telse\n\t\t\tcharClass[ch] = CharacterClass::punctuation;\n\t}\n}\n\nvoid CharClassify::SetCharClasses(const unsigned char *chars, CharacterClass newCharClass) {\n\t// Apply the newCharClass to the specified chars\n\tif (chars) {\n\t\twhile (*chars) {\n\t\t\tcharClass[*chars] = newCharClass;\n\t\t\tchars++;\n\t\t}\n\t}\n}\n\nint CharClassify::GetCharsOfClass(CharacterClass characterClass, unsigned char *buffer) const noexcept {\n\t// Get characters belonging to the given char class; return the number\n\t// of characters (if the buffer is NULL, don't write to it).\n\tint count = 0;\n\tfor (int ch = maxChar - 1; ch >= 0; --ch) {\n\t\tif (charClass[ch] == characterClass) {\n\t\t\t++count;\n\t\t\tif (buffer) {\n\t\t\t\t*buffer = static_cast<unsigned char>(ch);\n\t\t\t\tbuffer++;\n\t\t\t}\n\t\t}\n\t}\n\treturn count;\n}\n"
  },
  {
    "path": "Libraries/scintilla/src/CharClassify.h",
    "content": "// Scintilla source code edit control\n/** @file CharClassify.h\n ** Character classifications used by Document and RESearch.\n **/\n// Copyright 2006-2009 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#ifndef CHARCLASSIFY_H\n#define CHARCLASSIFY_H\n\nnamespace Scintilla::Internal {\n\nenum class CharacterClass : unsigned char { space, newLine, word, punctuation };\n\nclass CharClassify {\npublic:\n\tCharClassify();\n\n\tvoid SetDefaultCharClasses(bool includeWordClass);\n\tvoid SetCharClasses(const unsigned char *chars, CharacterClass newCharClass);\n\tint GetCharsOfClass(CharacterClass characterClass, unsigned char *buffer) const noexcept;\n\tCharacterClass GetClass(unsigned char ch) const noexcept { return charClass[ch];}\n\tbool IsWord(unsigned char ch) const noexcept { return charClass[ch] == CharacterClass::word;}\n\nprivate:\n\tstatic constexpr int maxChar=256;\n\tCharacterClass charClass[maxChar];\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/src/CharacterCategoryMap.cxx",
    "content": "// Scintilla source code edit control\n/** @file CharacterCategoryMap.cxx\n ** Returns the Unicode general category of a character.\n ** Table automatically regenerated by scripts/GenerateCharacterCategory.py\n ** Should only be rarely regenerated for new versions of Unicode.\n ** Similar code to Lexilla's lexilla/lexlib/CharacterCategory.cxx but renamed\n ** to avoid problems with builds that statically include both Scintilla and Lexilla.\n **/\n// Copyright 2013 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#include <vector>\n#include <algorithm>\n#include <iterator>\n\n#include \"CharacterCategoryMap.h\"\n\nnamespace Scintilla::Internal {\n\nnamespace {\n\t// Use an unnamed namespace to protect the declarations from name conflicts\n\nconst int catRanges[] = {\n//++Autogenerated -- start of section automatically generated\n// Created with Python 3.13.0,  Unicode 15.1.0\n25,\n1046,\n1073,\n1171,\n1201,\n1293,\n1326,\n1361,\n1394,\n1425,\n1452,\n1489,\n1544,\n1873,\n1938,\n2033,\n2080,\n2925,\n2961,\n2990,\n3028,\n3051,\n3092,\n3105,\n3949,\n3986,\n4014,\n4050,\n4089,\n5142,\n5169,\n5203,\n5333,\n5361,\n5396,\n5429,\n5444,\n5487,\n5522,\n5562,\n5589,\n5620,\n5653,\n5682,\n5706,\n5780,\n5793,\n5841,\n5908,\n5930,\n5956,\n6000,\n6026,\n6129,\n6144,\n6898,\n6912,\n7137,\n7922,\n7937,\n8192,\n8225,\n8256,\n8289,\n8320,\n8353,\n8384,\n8417,\n8448,\n8481,\n8512,\n8545,\n8576,\n8609,\n8640,\n8673,\n8704,\n8737,\n8768,\n8801,\n8832,\n8865,\n8896,\n8929,\n8960,\n8993,\n9024,\n9057,\n9088,\n9121,\n9152,\n9185,\n9216,\n9249,\n9280,\n9313,\n9344,\n9377,\n9408,\n9441,\n9472,\n9505,\n9536,\n9569,\n9600,\n9633,\n9664,\n9697,\n9728,\n9761,\n9792,\n9825,\n9856,\n9889,\n9920,\n9953,\n10016,\n10049,\n10080,\n10113,\n10144,\n10177,\n10208,\n10241,\n10272,\n10305,\n10336,\n10369,\n10400,\n10433,\n10464,\n10497,\n10560,\n10593,\n10624,\n10657,\n10688,\n10721,\n10752,\n10785,\n10816,\n10849,\n10880,\n10913,\n10944,\n10977,\n11008,\n11041,\n11072,\n11105,\n11136,\n11169,\n11200,\n11233,\n11264,\n11297,\n11328,\n11361,\n11392,\n11425,\n11456,\n11489,\n11520,\n11553,\n11584,\n11617,\n11648,\n11681,\n11712,\n11745,\n11776,\n11809,\n11840,\n11873,\n11904,\n11937,\n11968,\n12001,\n12032,\n12097,\n12128,\n12161,\n12192,\n12225,\n12320,\n12385,\n12416,\n12449,\n12480,\n12545,\n12576,\n12673,\n12736,\n12865,\n12896,\n12961,\n12992,\n13089,\n13184,\n13249,\n13280,\n13345,\n13376,\n13409,\n13440,\n13473,\n13504,\n13569,\n13600,\n13633,\n13696,\n13729,\n13760,\n13825,\n13856,\n13953,\n13984,\n14017,\n14048,\n14113,\n14180,\n14208,\n14241,\n14340,\n14464,\n14498,\n14529,\n14560,\n14594,\n14625,\n14656,\n14690,\n14721,\n14752,\n14785,\n14816,\n14849,\n14880,\n14913,\n14944,\n14977,\n15008,\n15041,\n15072,\n15105,\n15136,\n15169,\n15200,\n15233,\n15296,\n15329,\n15360,\n15393,\n15424,\n15457,\n15488,\n15521,\n15552,\n15585,\n15616,\n15649,\n15680,\n15713,\n15744,\n15777,\n15808,\n15841,\n15904,\n15938,\n15969,\n16000,\n16033,\n16064,\n16161,\n16192,\n16225,\n16256,\n16289,\n16320,\n16353,\n16384,\n16417,\n16448,\n16481,\n16512,\n16545,\n16576,\n16609,\n16640,\n16673,\n16704,\n16737,\n16768,\n16801,\n16832,\n16865,\n16896,\n16929,\n16960,\n16993,\n17024,\n17057,\n17088,\n17121,\n17152,\n17185,\n17216,\n17249,\n17280,\n17313,\n17344,\n17377,\n17408,\n17441,\n17472,\n17505,\n17536,\n17569,\n17600,\n17633,\n17664,\n17697,\n17728,\n17761,\n17792,\n17825,\n17856,\n17889,\n17920,\n17953,\n17984,\n18017,\n18240,\n18305,\n18336,\n18401,\n18464,\n18497,\n18528,\n18657,\n18688,\n18721,\n18752,\n18785,\n18816,\n18849,\n18880,\n18913,\n21124,\n21153,\n22019,\n22612,\n22723,\n23124,\n23555,\n23732,\n23939,\n23988,\n24003,\n24052,\n24581,\n28160,\n28193,\n28224,\n28257,\n28291,\n28340,\n28352,\n28385,\n28445,\n28483,\n28513,\n28625,\n28640,\n28701,\n28820,\n28864,\n28913,\n28928,\n29053,\n29056,\n29117,\n29120,\n29185,\n29216,\n29789,\n29792,\n30081,\n31200,\n31233,\n31296,\n31393,\n31488,\n31521,\n31552,\n31585,\n31616,\n31649,\n31680,\n31713,\n31744,\n31777,\n31808,\n31841,\n31872,\n31905,\n31936,\n31969,\n32000,\n32033,\n32064,\n32097,\n32128,\n32161,\n32192,\n32225,\n32384,\n32417,\n32466,\n32480,\n32513,\n32544,\n32609,\n32672,\n34305,\n35840,\n35873,\n35904,\n35937,\n35968,\n36001,\n36032,\n36065,\n36096,\n36129,\n36160,\n36193,\n36224,\n36257,\n36288,\n36321,\n36352,\n36385,\n36416,\n36449,\n36480,\n36513,\n36544,\n36577,\n36608,\n36641,\n36672,\n36705,\n36736,\n36769,\n36800,\n36833,\n36864,\n36897,\n36949,\n36965,\n37127,\n37184,\n37217,\n37248,\n37281,\n37312,\n37345,\n37376,\n37409,\n37440,\n37473,\n37504,\n37537,\n37568,\n37601,\n37632,\n37665,\n37696,\n37729,\n37760,\n37793,\n37824,\n37857,\n37888,\n37921,\n37952,\n37985,\n38016,\n38049,\n38080,\n38113,\n38144,\n38177,\n38208,\n38241,\n38272,\n38305,\n38336,\n38369,\n38400,\n38433,\n38464,\n38497,\n38528,\n38561,\n38592,\n38625,\n38656,\n38689,\n38720,\n38753,\n38784,\n38817,\n38848,\n38881,\n38912,\n38977,\n39008,\n39041,\n39072,\n39105,\n39136,\n39169,\n39200,\n39233,\n39264,\n39297,\n39328,\n39361,\n39424,\n39457,\n39488,\n39521,\n39552,\n39585,\n39616,\n39649,\n39680,\n39713,\n39744,\n39777,\n39808,\n39841,\n39872,\n39905,\n39936,\n39969,\n40000,\n40033,\n40064,\n40097,\n40128,\n40161,\n40192,\n40225,\n40256,\n40289,\n40320,\n40353,\n40384,\n40417,\n40448,\n40481,\n40512,\n40545,\n40576,\n40609,\n40640,\n40673,\n40704,\n40737,\n40768,\n40801,\n40832,\n40865,\n40896,\n40929,\n40960,\n40993,\n41024,\n41057,\n41088,\n41121,\n41152,\n41185,\n41216,\n41249,\n41280,\n41313,\n41344,\n41377,\n41408,\n41441,\n41472,\n41505,\n41536,\n41569,\n41600,\n41633,\n41664,\n41697,\n41728,\n41761,\n41792,\n41825,\n41856,\n41889,\n41920,\n41953,\n41984,\n42017,\n42048,\n42081,\n42112,\n42145,\n42176,\n42209,\n42240,\n42273,\n42304,\n42337,\n42368,\n42401,\n42432,\n42465,\n42525,\n42528,\n43773,\n43811,\n43857,\n44033,\n45361,\n45388,\n45437,\n45493,\n45555,\n45597,\n45605,\n47052,\n47077,\n47121,\n47141,\n47217,\n47237,\n47313,\n47333,\n47389,\n47620,\n48509,\n48612,\n48753,\n48829,\n49178,\n49362,\n49457,\n49523,\n49553,\n49621,\n49669,\n50033,\n50074,\n50097,\n50180,\n51203,\n51236,\n51557,\n52232,\n52561,\n52676,\n52741,\n52772,\n55953,\n55972,\n56005,\n56250,\n56277,\n56293,\n56483,\n56549,\n56629,\n56645,\n56772,\n56840,\n57156,\n57269,\n57316,\n57361,\n57821,\n57850,\n57860,\n57893,\n57924,\n58885,\n59773,\n59812,\n62661,\n63012,\n63069,\n63496,\n63812,\n64869,\n65155,\n65237,\n65265,\n65347,\n65405,\n65445,\n65491,\n65540,\n66245,\n66371,\n66405,\n66691,\n66725,\n66819,\n66853,\n67037,\n67089,\n67581,\n67588,\n68389,\n68509,\n68561,\n68605,\n68612,\n68989,\n69124,\n69908,\n69924,\n70141,\n70170,\n70237,\n70405,\n70660,\n71971,\n72005,\n72794,\n72805,\n73830,\n73860,\n75589,\n75622,\n75653,\n75684,\n75718,\n75813,\n76070,\n76197,\n76230,\n76292,\n76325,\n76548,\n76869,\n76945,\n77000,\n77329,\n77347,\n77380,\n77861,\n77894,\n77981,\n77988,\n78269,\n78308,\n78397,\n78436,\n79165,\n79172,\n79421,\n79428,\n79485,\n79556,\n79709,\n79749,\n79780,\n79814,\n79909,\n80061,\n80102,\n80189,\n80230,\n80293,\n80324,\n80381,\n80614,\n80669,\n80772,\n80861,\n80868,\n80965,\n81053,\n81096,\n81412,\n81491,\n81546,\n81749,\n81779,\n81796,\n81841,\n81861,\n81917,\n81957,\n82022,\n82077,\n82084,\n82301,\n82404,\n82493,\n82532,\n83261,\n83268,\n83517,\n83524,\n83613,\n83620,\n83709,\n83716,\n83805,\n83845,\n83901,\n83910,\n84005,\n84093,\n84197,\n84285,\n84325,\n84445,\n84517,\n84573,\n84772,\n84925,\n84932,\n84989,\n85192,\n85509,\n85572,\n85669,\n85713,\n85757,\n86053,\n86118,\n86173,\n86180,\n86493,\n86500,\n86621,\n86628,\n87357,\n87364,\n87613,\n87620,\n87709,\n87716,\n87901,\n87941,\n87972,\n88006,\n88101,\n88285,\n88293,\n88358,\n88413,\n88422,\n88485,\n88541,\n88580,\n88637,\n89092,\n89157,\n89245,\n89288,\n89617,\n89651,\n89693,\n89892,\n89925,\n90141,\n90149,\n90182,\n90269,\n90276,\n90557,\n90596,\n90685,\n90724,\n91453,\n91460,\n91709,\n91716,\n91805,\n91812,\n91997,\n92037,\n92068,\n92102,\n92133,\n92166,\n92197,\n92349,\n92390,\n92477,\n92518,\n92581,\n92637,\n92837,\n92902,\n92957,\n93060,\n93149,\n93156,\n93253,\n93341,\n93384,\n93717,\n93732,\n93770,\n93981,\n94277,\n94308,\n94365,\n94372,\n94589,\n94660,\n94781,\n94788,\n94941,\n95012,\n95101,\n95108,\n95165,\n95172,\n95261,\n95332,\n95421,\n95492,\n95613,\n95684,\n96093,\n96198,\n96261,\n96294,\n96381,\n96454,\n96573,\n96582,\n96677,\n96733,\n96772,\n96829,\n96998,\n97053,\n97480,\n97802,\n97909,\n98099,\n98133,\n98173,\n98309,\n98342,\n98437,\n98468,\n98749,\n98756,\n98877,\n98884,\n99645,\n99652,\n100189,\n100229,\n100260,\n100293,\n100390,\n100541,\n100549,\n100669,\n100677,\n100829,\n101029,\n101117,\n101124,\n101245,\n101284,\n101341,\n101380,\n101445,\n101533,\n101576,\n101917,\n102129,\n102154,\n102389,\n102404,\n102437,\n102470,\n102545,\n102564,\n102845,\n102852,\n102973,\n102980,\n103741,\n103748,\n104093,\n104100,\n104285,\n104325,\n104356,\n104390,\n104421,\n104454,\n104637,\n104645,\n104678,\n104765,\n104774,\n104837,\n104925,\n105126,\n105213,\n105380,\n105469,\n105476,\n105541,\n105629,\n105672,\n106013,\n106020,\n106086,\n106141,\n106501,\n106566,\n106628,\n106941,\n106948,\n107069,\n107076,\n108389,\n108452,\n108486,\n108581,\n108733,\n108742,\n108861,\n108870,\n108965,\n108996,\n109045,\n109085,\n109188,\n109286,\n109322,\n109540,\n109637,\n109725,\n109768,\n110090,\n110389,\n110404,\n110621,\n110629,\n110662,\n110749,\n110756,\n111357,\n111428,\n112221,\n112228,\n112541,\n112548,\n112605,\n112644,\n112893,\n112965,\n113021,\n113126,\n113221,\n113341,\n113349,\n113405,\n113414,\n113693,\n113864,\n114205,\n114246,\n114321,\n114365,\n114724,\n116261,\n116292,\n116357,\n116605,\n116723,\n116740,\n116931,\n116965,\n117233,\n117256,\n117585,\n117661,\n118820,\n118909,\n118916,\n118973,\n118980,\n119165,\n119172,\n119965,\n119972,\n120029,\n120036,\n120357,\n120388,\n120453,\n120740,\n120797,\n120836,\n121021,\n121027,\n121085,\n121093,\n121341,\n121352,\n121693,\n121732,\n121885,\n122884,\n122933,\n123025,\n123509,\n123537,\n123573,\n123653,\n123733,\n123912,\n124234,\n124565,\n124581,\n124629,\n124645,\n124693,\n124709,\n124749,\n124782,\n124813,\n124846,\n124870,\n124932,\n125213,\n125220,\n126397,\n126501,\n126950,\n126981,\n127153,\n127173,\n127236,\n127397,\n127773,\n127781,\n128957,\n128981,\n129221,\n129269,\n129469,\n129493,\n129553,\n129717,\n129841,\n129917,\n131076,\n132454,\n132517,\n132646,\n132677,\n132870,\n132901,\n132966,\n133029,\n133092,\n133128,\n133457,\n133636,\n133830,\n133893,\n133956,\n134085,\n134180,\n134214,\n134308,\n134374,\n134596,\n134693,\n134820,\n135237,\n135270,\n135333,\n135398,\n135589,\n135620,\n135654,\n135688,\n136006,\n136101,\n136149,\n136192,\n137437,\n137440,\n137501,\n137632,\n137693,\n137729,\n139121,\n139139,\n139169,\n139268,\n149821,\n149828,\n149981,\n150020,\n150269,\n150276,\n150333,\n150340,\n150493,\n150532,\n151869,\n151876,\n152029,\n152068,\n153149,\n153156,\n153309,\n153348,\n153597,\n153604,\n153661,\n153668,\n153821,\n153860,\n154365,\n154372,\n156221,\n156228,\n156381,\n156420,\n158589,\n158629,\n158737,\n159018,\n159677,\n159748,\n160277,\n160605,\n160768,\n163549,\n163585,\n163805,\n163852,\n163876,\n183733,\n183761,\n183780,\n184342,\n184356,\n185197,\n185230,\n185277,\n185348,\n187761,\n187849,\n187940,\n188221,\n188420,\n188997,\n189094,\n189149,\n189412,\n190021,\n190086,\n190129,\n190205,\n190468,\n191045,\n191133,\n191492,\n191933,\n191940,\n192061,\n192069,\n192157,\n192516,\n194181,\n194246,\n194277,\n194502,\n194757,\n194790,\n194853,\n195217,\n195299,\n195345,\n195443,\n195460,\n195493,\n195549,\n195592,\n195933,\n196106,\n196445,\n196625,\n196812,\n196849,\n196965,\n197082,\n197093,\n197128,\n197469,\n197636,\n198755,\n198788,\n200509,\n200708,\n200869,\n200932,\n202021,\n202052,\n202109,\n202244,\n204509,\n204804,\n205821,\n205829,\n205926,\n206053,\n206118,\n206237,\n206342,\n206405,\n206438,\n206629,\n206749,\n206869,\n206909,\n206993,\n207048,\n207364,\n208349,\n208388,\n208573,\n208900,\n210333,\n210436,\n211293,\n211464,\n211786,\n211837,\n211925,\n212996,\n213733,\n213798,\n213861,\n213917,\n213969,\n214020,\n215718,\n215749,\n215782,\n215813,\n216061,\n216069,\n216102,\n216133,\n216166,\n216229,\n216486,\n216677,\n217021,\n217061,\n217096,\n217437,\n217608,\n217949,\n218129,\n218339,\n218385,\n218589,\n218629,\n219079,\n219109,\n219645,\n221189,\n221318,\n221348,\n222853,\n222886,\n222917,\n223078,\n223109,\n223142,\n223301,\n223334,\n223396,\n223677,\n223752,\n224081,\n224309,\n224613,\n224917,\n225201,\n225277,\n225285,\n225350,\n225380,\n226342,\n226373,\n226502,\n226565,\n226630,\n226661,\n226756,\n226824,\n227140,\n228549,\n228582,\n228613,\n228678,\n228773,\n228806,\n228837,\n228934,\n229021,\n229265,\n229380,\n230534,\n230789,\n231046,\n231109,\n231197,\n231281,\n231432,\n231773,\n231844,\n231944,\n232260,\n233219,\n233425,\n233473,\n233789,\n233984,\n235389,\n235424,\n235537,\n235805,\n236037,\n236145,\n236165,\n236582,\n236613,\n236836,\n236965,\n236996,\n237189,\n237220,\n237286,\n237317,\n237380,\n237437,\n237569,\n238979,\n240993,\n241411,\n241441,\n242531,\n243717,\n245760,\n245793,\n245824,\n245857,\n245888,\n245921,\n245952,\n245985,\n246016,\n246049,\n246080,\n246113,\n246144,\n246177,\n246208,\n246241,\n246272,\n246305,\n246336,\n246369,\n246400,\n246433,\n246464,\n246497,\n246528,\n246561,\n246592,\n246625,\n246656,\n246689,\n246720,\n246753,\n246784,\n246817,\n246848,\n246881,\n246912,\n246945,\n246976,\n247009,\n247040,\n247073,\n247104,\n247137,\n247168,\n247201,\n247232,\n247265,\n247296,\n247329,\n247360,\n247393,\n247424,\n247457,\n247488,\n247521,\n247552,\n247585,\n247616,\n247649,\n247680,\n247713,\n247744,\n247777,\n247808,\n247841,\n247872,\n247905,\n247936,\n247969,\n248000,\n248033,\n248064,\n248097,\n248128,\n248161,\n248192,\n248225,\n248256,\n248289,\n248320,\n248353,\n248384,\n248417,\n248448,\n248481,\n248512,\n248545,\n248576,\n248609,\n248640,\n248673,\n248704,\n248737,\n248768,\n248801,\n248832,\n248865,\n248896,\n248929,\n248960,\n248993,\n249024,\n249057,\n249088,\n249121,\n249152,\n249185,\n249216,\n249249,\n249280,\n249313,\n249344,\n249377,\n249408,\n249441,\n249472,\n249505,\n249536,\n249569,\n249600,\n249633,\n249664,\n249697,\n249728,\n249761,\n249792,\n249825,\n249856,\n249889,\n249920,\n249953,\n249984,\n250017,\n250048,\n250081,\n250112,\n250145,\n250176,\n250209,\n250240,\n250273,\n250304,\n250337,\n250368,\n250401,\n250432,\n250465,\n250496,\n250529,\n250816,\n250849,\n250880,\n250913,\n250944,\n250977,\n251008,\n251041,\n251072,\n251105,\n251136,\n251169,\n251200,\n251233,\n251264,\n251297,\n251328,\n251361,\n251392,\n251425,\n251456,\n251489,\n251520,\n251553,\n251584,\n251617,\n251648,\n251681,\n251712,\n251745,\n251776,\n251809,\n251840,\n251873,\n251904,\n251937,\n251968,\n252001,\n252032,\n252065,\n252096,\n252129,\n252160,\n252193,\n252224,\n252257,\n252288,\n252321,\n252352,\n252385,\n252416,\n252449,\n252480,\n252513,\n252544,\n252577,\n252608,\n252641,\n252672,\n252705,\n252736,\n252769,\n252800,\n252833,\n252864,\n252897,\n252928,\n252961,\n252992,\n253025,\n253056,\n253089,\n253120,\n253153,\n253184,\n253217,\n253248,\n253281,\n253312,\n253345,\n253376,\n253409,\n253440,\n253473,\n253504,\n253537,\n253568,\n253601,\n253632,\n253665,\n253696,\n253729,\n253760,\n253793,\n253824,\n253857,\n253888,\n253921,\n254208,\n254465,\n254685,\n254720,\n254941,\n254977,\n255232,\n255489,\n255744,\n256001,\n256221,\n256256,\n256477,\n256513,\n256797,\n256800,\n256861,\n256864,\n256925,\n256928,\n256989,\n256992,\n257025,\n257280,\n257537,\n258013,\n258049,\n258306,\n258561,\n258818,\n259073,\n259330,\n259585,\n259773,\n259777,\n259840,\n259970,\n260020,\n260033,\n260084,\n260161,\n260285,\n260289,\n260352,\n260482,\n260532,\n260609,\n260765,\n260801,\n260864,\n261021,\n261044,\n261121,\n261376,\n261556,\n261661,\n261697,\n261821,\n261825,\n261888,\n262018,\n262068,\n262141,\n262166,\n262522,\n262668,\n262865,\n262927,\n262960,\n262989,\n263023,\n263088,\n263117,\n263151,\n263185,\n263447,\n263480,\n263514,\n263670,\n263697,\n263983,\n264016,\n264049,\n264171,\n264241,\n264338,\n264365,\n264398,\n264433,\n264786,\n264817,\n264843,\n264881,\n265206,\n265242,\n265405,\n265434,\n265738,\n265763,\n265821,\n265866,\n266066,\n266157,\n266190,\n266211,\n266250,\n266578,\n266669,\n266702,\n266749,\n266755,\n267197,\n267283,\n268349,\n268805,\n269223,\n269349,\n269383,\n269477,\n269885,\n270357,\n270400,\n270453,\n270560,\n270613,\n270657,\n270688,\n270785,\n270848,\n270945,\n270997,\n271008,\n271061,\n271122,\n271136,\n271317,\n271488,\n271541,\n271552,\n271605,\n271616,\n271669,\n271680,\n271829,\n271841,\n271872,\n272001,\n272036,\n272161,\n272213,\n272257,\n272320,\n272402,\n272544,\n272577,\n272725,\n272754,\n272789,\n272833,\n272885,\n272906,\n273417,\n274528,\n274561,\n274601,\n274730,\n274773,\n274845,\n274962,\n275125,\n275282,\n275349,\n275474,\n275509,\n275570,\n275605,\n275666,\n275701,\n275922,\n275957,\n276946,\n277013,\n277074,\n277109,\n277138,\n277173,\n278162,\n286741,\n286989,\n287022,\n287053,\n287086,\n287125,\n287762,\n287829,\n288045,\n288078,\n288117,\n290706,\n290741,\n291698,\n292501,\n293778,\n293973,\n296189,\n296981,\n297341,\n297994,\n299925,\n302410,\n303125,\n308978,\n309013,\n309298,\n309333,\n311058,\n311317,\n314866,\n314901,\n322829,\n322862,\n322893,\n322926,\n322957,\n322990,\n323021,\n323054,\n323085,\n323118,\n323149,\n323182,\n323213,\n323246,\n323274,\n324245,\n325650,\n325805,\n325838,\n325874,\n326861,\n326894,\n326925,\n326958,\n326989,\n327022,\n327053,\n327086,\n327117,\n327150,\n327186,\n327701,\n335890,\n340077,\n340110,\n340141,\n340174,\n340205,\n340238,\n340269,\n340302,\n340333,\n340366,\n340397,\n340430,\n340461,\n340494,\n340525,\n340558,\n340589,\n340622,\n340653,\n340686,\n340717,\n340750,\n340786,\n342797,\n342830,\n342861,\n342894,\n342930,\n343949,\n343982,\n344018,\n352277,\n353810,\n354485,\n354546,\n354741,\n355997,\n356053,\n357085,\n357109,\n360448,\n361985,\n363520,\n363553,\n363584,\n363681,\n363744,\n363777,\n363808,\n363841,\n363872,\n363905,\n363936,\n364065,\n364096,\n364129,\n364192,\n364225,\n364419,\n364480,\n364577,\n364608,\n364641,\n364672,\n364705,\n364736,\n364769,\n364800,\n364833,\n364864,\n364897,\n364928,\n364961,\n364992,\n365025,\n365056,\n365089,\n365120,\n365153,\n365184,\n365217,\n365248,\n365281,\n365312,\n365345,\n365376,\n365409,\n365440,\n365473,\n365504,\n365537,\n365568,\n365601,\n365632,\n365665,\n365696,\n365729,\n365760,\n365793,\n365824,\n365857,\n365888,\n365921,\n365952,\n365985,\n366016,\n366049,\n366080,\n366113,\n366144,\n366177,\n366208,\n366241,\n366272,\n366305,\n366336,\n366369,\n366400,\n366433,\n366464,\n366497,\n366528,\n366561,\n366592,\n366625,\n366656,\n366689,\n366720,\n366753,\n366784,\n366817,\n366848,\n366881,\n366912,\n366945,\n366976,\n367009,\n367040,\n367073,\n367104,\n367137,\n367168,\n367201,\n367232,\n367265,\n367296,\n367329,\n367360,\n367393,\n367424,\n367457,\n367488,\n367521,\n367552,\n367585,\n367616,\n367649,\n367680,\n367713,\n367797,\n367968,\n368001,\n368032,\n368065,\n368101,\n368192,\n368225,\n368285,\n368433,\n368554,\n368593,\n368641,\n369885,\n369889,\n369949,\n370081,\n370141,\n370180,\n371997,\n372195,\n372241,\n372285,\n372709,\n372740,\n373501,\n373764,\n374013,\n374020,\n374269,\n374276,\n374525,\n374532,\n374781,\n374788,\n375037,\n375044,\n375293,\n375300,\n375549,\n375556,\n375805,\n375813,\n376849,\n376911,\n376944,\n376975,\n377008,\n377041,\n377135,\n377168,\n377201,\n377231,\n377264,\n377297,\n377580,\n377617,\n377676,\n377713,\n377743,\n377776,\n377809,\n377871,\n377904,\n377933,\n377966,\n377997,\n378030,\n378061,\n378094,\n378125,\n378158,\n378193,\n378339,\n378385,\n378700,\n378769,\n378892,\n378929,\n378957,\n378993,\n379413,\n379473,\n379565,\n379598,\n379629,\n379662,\n379693,\n379726,\n379757,\n379790,\n379820,\n379869,\n380949,\n381789,\n381813,\n384669,\n385045,\n391901,\n392725,\n393238,\n393265,\n393365,\n393379,\n393412,\n393449,\n393485,\n393518,\n393549,\n393582,\n393613,\n393646,\n393677,\n393710,\n393741,\n393774,\n393813,\n393869,\n393902,\n393933,\n393966,\n393997,\n394030,\n394061,\n394094,\n394124,\n394157,\n394190,\n394261,\n394281,\n394565,\n394694,\n394764,\n394787,\n394965,\n395017,\n395107,\n395140,\n395185,\n395221,\n395293,\n395300,\n398077,\n398117,\n398196,\n398243,\n398308,\n398348,\n398372,\n401265,\n401283,\n401380,\n401437,\n401572,\n402973,\n402980,\n406013,\n406037,\n406090,\n406229,\n406532,\n407573,\n408733,\n409077,\n409092,\n409621,\n410621,\n410634,\n410965,\n411914,\n412181,\n412202,\n412693,\n413706,\n414037,\n415274,\n415765,\n425988,\n636949,\n638980,\n1311395,\n1311428,\n1348029,\n1348117,\n1349885,\n1350148,\n1351427,\n1351633,\n1351684,\n1360259,\n1360305,\n1360388,\n1360904,\n1361220,\n1361309,\n1361920,\n1361953,\n1361984,\n1362017,\n1362048,\n1362081,\n1362112,\n1362145,\n1362176,\n1362209,\n1362240,\n1362273,\n1362304,\n1362337,\n1362368,\n1362401,\n1362432,\n1362465,\n1362496,\n1362529,\n1362560,\n1362593,\n1362624,\n1362657,\n1362688,\n1362721,\n1362752,\n1362785,\n1362816,\n1362849,\n1362880,\n1362913,\n1362944,\n1362977,\n1363008,\n1363041,\n1363072,\n1363105,\n1363136,\n1363169,\n1363200,\n1363233,\n1363264,\n1363297,\n1363328,\n1363361,\n1363396,\n1363429,\n1363463,\n1363569,\n1363589,\n1363921,\n1363939,\n1363968,\n1364001,\n1364032,\n1364065,\n1364096,\n1364129,\n1364160,\n1364193,\n1364224,\n1364257,\n1364288,\n1364321,\n1364352,\n1364385,\n1364416,\n1364449,\n1364480,\n1364513,\n1364544,\n1364577,\n1364608,\n1364641,\n1364672,\n1364705,\n1364736,\n1364769,\n1364800,\n1364833,\n1364867,\n1364933,\n1364996,\n1367241,\n1367557,\n1367633,\n1367837,\n1368084,\n1368803,\n1369108,\n1369152,\n1369185,\n1369216,\n1369249,\n1369280,\n1369313,\n1369344,\n1369377,\n1369408,\n1369441,\n1369472,\n1369505,\n1369536,\n1369569,\n1369664,\n1369697,\n1369728,\n1369761,\n1369792,\n1369825,\n1369856,\n1369889,\n1369920,\n1369953,\n1369984,\n1370017,\n1370048,\n1370081,\n1370112,\n1370145,\n1370176,\n1370209,\n1370240,\n1370273,\n1370304,\n1370337,\n1370368,\n1370401,\n1370432,\n1370465,\n1370496,\n1370529,\n1370560,\n1370593,\n1370624,\n1370657,\n1370688,\n1370721,\n1370752,\n1370785,\n1370816,\n1370849,\n1370880,\n1370913,\n1370944,\n1370977,\n1371008,\n1371041,\n1371072,\n1371105,\n1371136,\n1371169,\n1371200,\n1371233,\n1371264,\n1371297,\n1371328,\n1371361,\n1371392,\n1371425,\n1371456,\n1371489,\n1371520,\n1371553,\n1371584,\n1371617,\n1371651,\n1371681,\n1371936,\n1371969,\n1372000,\n1372033,\n1372064,\n1372129,\n1372160,\n1372193,\n1372224,\n1372257,\n1372288,\n1372321,\n1372352,\n1372385,\n1372419,\n1372468,\n1372512,\n1372545,\n1372576,\n1372609,\n1372644,\n1372672,\n1372705,\n1372736,\n1372769,\n1372864,\n1372897,\n1372928,\n1372961,\n1372992,\n1373025,\n1373056,\n1373089,\n1373120,\n1373153,\n1373184,\n1373217,\n1373248,\n1373281,\n1373312,\n1373345,\n1373376,\n1373409,\n1373440,\n1373473,\n1373504,\n1373665,\n1373696,\n1373857,\n1373888,\n1373921,\n1373952,\n1373985,\n1374016,\n1374049,\n1374080,\n1374113,\n1374144,\n1374177,\n1374208,\n1374241,\n1374272,\n1374305,\n1374336,\n1374465,\n1374496,\n1374529,\n1374589,\n1374720,\n1374753,\n1374813,\n1374817,\n1374877,\n1374881,\n1374912,\n1374945,\n1374976,\n1375009,\n1375069,\n1375811,\n1375904,\n1375937,\n1375972,\n1376003,\n1376065,\n1376100,\n1376325,\n1376356,\n1376453,\n1376484,\n1376613,\n1376644,\n1377382,\n1377445,\n1377510,\n1377557,\n1377669,\n1377725,\n1377802,\n1378005,\n1378067,\n1378101,\n1378141,\n1378308,\n1379985,\n1380125,\n1380358,\n1380420,\n1382022,\n1382533,\n1382621,\n1382865,\n1382920,\n1383261,\n1383429,\n1384004,\n1384209,\n1384292,\n1384337,\n1384356,\n1384421,\n1384456,\n1384772,\n1385669,\n1385937,\n1385988,\n1386725,\n1387078,\n1387165,\n1387505,\n1387524,\n1388477,\n1388549,\n1388646,\n1388676,\n1390181,\n1390214,\n1390277,\n1390406,\n1390469,\n1390534,\n1390641,\n1391069,\n1391075,\n1391112,\n1391453,\n1391569,\n1391620,\n1391781,\n1391811,\n1391844,\n1392136,\n1392452,\n1392637,\n1392644,\n1393957,\n1394150,\n1394213,\n1394278,\n1394341,\n1394429,\n1394692,\n1394789,\n1394820,\n1395077,\n1395110,\n1395165,\n1395208,\n1395549,\n1395601,\n1395716,\n1396227,\n1396260,\n1396469,\n1396548,\n1396582,\n1396613,\n1396646,\n1396676,\n1398277,\n1398308,\n1398341,\n1398436,\n1398501,\n1398564,\n1398725,\n1398788,\n1398821,\n1398852,\n1398909,\n1399652,\n1399715,\n1399761,\n1399812,\n1400166,\n1400197,\n1400262,\n1400337,\n1400388,\n1400419,\n1400486,\n1400517,\n1400573,\n1400868,\n1401085,\n1401124,\n1401341,\n1401380,\n1401597,\n1401860,\n1402109,\n1402116,\n1402365,\n1402369,\n1403764,\n1403779,\n1403905,\n1404195,\n1404244,\n1404317,\n1404417,\n1406980,\n1408102,\n1408165,\n1408198,\n1408261,\n1408294,\n1408369,\n1408390,\n1408421,\n1408477,\n1408520,\n1408861,\n1409028,\n1766557,\n1766916,\n1767677,\n1767780,\n1769373,\n1769499,\n1835036,\n2039812,\n2051549,\n2051588,\n2055005,\n2056193,\n2056445,\n2056801,\n2056989,\n2057124,\n2057157,\n2057188,\n2057522,\n2057540,\n2057981,\n2057988,\n2058173,\n2058180,\n2058237,\n2058244,\n2058333,\n2058340,\n2058429,\n2058436,\n2061908,\n2062461,\n2062948,\n2074574,\n2074605,\n2074645,\n2075140,\n2077213,\n2077252,\n2079005,\n2079221,\n2079261,\n2080260,\n2080659,\n2080693,\n2080773,\n2081297,\n2081517,\n2081550,\n2081585,\n2081629,\n2081797,\n2082321,\n2082348,\n2082411,\n2082477,\n2082510,\n2082541,\n2082574,\n2082605,\n2082638,\n2082669,\n2082702,\n2082733,\n2082766,\n2082797,\n2082830,\n2082861,\n2082894,\n2082925,\n2082958,\n2082993,\n2083053,\n2083086,\n2083121,\n2083243,\n2083345,\n2083453,\n2083473,\n2083596,\n2083629,\n2083662,\n2083693,\n2083726,\n2083757,\n2083790,\n2083825,\n2083922,\n2083948,\n2083986,\n2084093,\n2084113,\n2084147,\n2084177,\n2084253,\n2084356,\n2084541,\n2084548,\n2088893,\n2088954,\n2088989,\n2089009,\n2089107,\n2089137,\n2089229,\n2089262,\n2089297,\n2089330,\n2089361,\n2089388,\n2089425,\n2089480,\n2089809,\n2089874,\n2089969,\n2090016,\n2090861,\n2090897,\n2090926,\n2090964,\n2090987,\n2091028,\n2091041,\n2091885,\n2091922,\n2091950,\n2091986,\n2092013,\n2092046,\n2092081,\n2092109,\n2092142,\n2092177,\n2092228,\n2092547,\n2092580,\n2094019,\n2094084,\n2095101,\n2095172,\n2095389,\n2095428,\n2095645,\n2095684,\n2095901,\n2095940,\n2096061,\n2096147,\n2096210,\n2096244,\n2096277,\n2096307,\n2096381,\n2096405,\n2096434,\n2096565,\n2096637,\n2096954,\n2097045,\n2097117,\n2097156,\n2097565,\n2097572,\n2098429,\n2098436,\n2099069,\n2099076,\n2099165,\n2099172,\n2099677,\n2099716,\n2100189,\n2101252,\n2105213,\n2105361,\n2105469,\n2105578,\n2107037,\n2107125,\n2107401,\n2109098,\n2109237,\n2109770,\n2109845,\n2109949,\n2109973,\n2110397,\n2110485,\n2110525,\n2112021,\n2113445,\n2113501,\n2117636,\n2118589,\n2118660,\n2120253,\n2120709,\n2120746,\n2121629,\n2121732,\n2122762,\n2122909,\n2123172,\n2123817,\n2123844,\n2124105,\n2124157,\n2124292,\n2125509,\n2125693,\n2125828,\n2126813,\n2126833,\n2126852,\n2128029,\n2128132,\n2128401,\n2128425,\n2128605,\n2129920,\n2131201,\n2132484,\n2135005,\n2135048,\n2135389,\n2135552,\n2136733,\n2136833,\n2138013,\n2138116,\n2139421,\n2139652,\n2141341,\n2141681,\n2141696,\n2142077,\n2142080,\n2142589,\n2142592,\n2142845,\n2142848,\n2142941,\n2142945,\n2143325,\n2143329,\n2143837,\n2143841,\n2144093,\n2144097,\n2144189,\n2146308,\n2156285,\n2156548,\n2157277,\n2157572,\n2157853,\n2158595,\n2158813,\n2158819,\n2160189,\n2160195,\n2160509,\n2162692,\n2162909,\n2162948,\n2163005,\n2163012,\n2164445,\n2164452,\n2164541,\n2164612,\n2164669,\n2164708,\n2165469,\n2165489,\n2165514,\n2165764,\n2166517,\n2166570,\n2166788,\n2167805,\n2168042,\n2168349,\n2169860,\n2170493,\n2170500,\n2170589,\n2170730,\n2170884,\n2171594,\n2171805,\n2171889,\n2171908,\n2172765,\n2172913,\n2172957,\n2174980,\n2176797,\n2176906,\n2176964,\n2177034,\n2177565,\n2177610,\n2179076,\n2179109,\n2179229,\n2179237,\n2179325,\n2179461,\n2179588,\n2179741,\n2179748,\n2179869,\n2179876,\n2180829,\n2180869,\n2180989,\n2181093,\n2181130,\n2181437,\n2181649,\n2181949,\n2182148,\n2183082,\n2183153,\n2183172,\n2184106,\n2184221,\n2185220,\n2185493,\n2185508,\n2186405,\n2186493,\n2186602,\n2186769,\n2187005,\n2187268,\n2189021,\n2189105,\n2189316,\n2190045,\n2190090,\n2190340,\n2190973,\n2191114,\n2191364,\n2191965,\n2192177,\n2192317,\n2192682,\n2192925,\n2195460,\n2197821,\n2199552,\n2201213,\n2201601,\n2203261,\n2203466,\n2203652,\n2204805,\n2204957,\n2205192,\n2205533,\n2214922,\n2215933,\n2215940,\n2217309,\n2217317,\n2217388,\n2217437,\n2217476,\n2217565,\n2219941,\n2220036,\n2220970,\n2221284,\n2221341,\n2221572,\n2222277,\n2222634,\n2222769,\n2222941,\n2223620,\n2224197,\n2224337,\n2224477,\n2225668,\n2226346,\n2226589,\n2227204,\n2227965,\n2228230,\n2228261,\n2228294,\n2228324,\n2230021,\n2230513,\n2230749,\n2230858,\n2231496,\n2231813,\n2231844,\n2231909,\n2231972,\n2232029,\n2232293,\n2232390,\n2232420,\n2233862,\n2233957,\n2234086,\n2234149,\n2234225,\n2234298,\n2234321,\n2234437,\n2234493,\n2234810,\n2234845,\n2234884,\n2235709,\n2235912,\n2236253,\n2236421,\n2236516,\n2237669,\n2237830,\n2237861,\n2238141,\n2238152,\n2238481,\n2238596,\n2238630,\n2238692,\n2238749,\n2238980,\n2240101,\n2240145,\n2240196,\n2240253,\n2240517,\n2240582,\n2240612,\n2242150,\n2242245,\n2242534,\n2242596,\n2242737,\n2242853,\n2242993,\n2243014,\n2243045,\n2243080,\n2243396,\n2243441,\n2243460,\n2243505,\n2243613,\n2243626,\n2244285,\n2244612,\n2245213,\n2245220,\n2246022,\n2246117,\n2246214,\n2246277,\n2246310,\n2246341,\n2246417,\n2246597,\n2246628,\n2246693,\n2246749,\n2248708,\n2248957,\n2248964,\n2249021,\n2249028,\n2249181,\n2249188,\n2249693,\n2249700,\n2250033,\n2250077,\n2250244,\n2251749,\n2251782,\n2251877,\n2252157,\n2252296,\n2252637,\n2252805,\n2252870,\n2252957,\n2252964,\n2253245,\n2253284,\n2253373,\n2253412,\n2254141,\n2254148,\n2254397,\n2254404,\n2254493,\n2254500,\n2254685,\n2254693,\n2254756,\n2254790,\n2254853,\n2254886,\n2255037,\n2255078,\n2255165,\n2255206,\n2255325,\n2255364,\n2255421,\n2255590,\n2255645,\n2255780,\n2255942,\n2256029,\n2256069,\n2256317,\n2256389,\n2256573,\n2260996,\n2262694,\n2262789,\n2263046,\n2263109,\n2263206,\n2263237,\n2263268,\n2263409,\n2263560,\n2263889,\n2263965,\n2263985,\n2264005,\n2264036,\n2264157,\n2265092,\n2266630,\n2266725,\n2266918,\n2266949,\n2266982,\n2267109,\n2267174,\n2267205,\n2267268,\n2267345,\n2267364,\n2267421,\n2267656,\n2267997,\n2273284,\n2274790,\n2274885,\n2275037,\n2275078,\n2275205,\n2275270,\n2275301,\n2275377,\n2276100,\n2276229,\n2276317,\n2277380,\n2278918,\n2279013,\n2279270,\n2279333,\n2279366,\n2279397,\n2279473,\n2279556,\n2279613,\n2279944,\n2280285,\n2280465,\n2280893,\n2281476,\n2282853,\n2282886,\n2282917,\n2282950,\n2283013,\n2283206,\n2283237,\n2283268,\n2283313,\n2283357,\n2283528,\n2283869,\n2285572,\n2286461,\n2286501,\n2286598,\n2286661,\n2286790,\n2286821,\n2287005,\n2287112,\n2287434,\n2287505,\n2287605,\n2287620,\n2287869,\n2293764,\n2295174,\n2295269,\n2295558,\n2295589,\n2295665,\n2295709,\n2298880,\n2299905,\n2300936,\n2301258,\n2301565,\n2301924,\n2302205,\n2302244,\n2302301,\n2302340,\n2302621,\n2302628,\n2302717,\n2302724,\n2303494,\n2303709,\n2303718,\n2303805,\n2303845,\n2303910,\n2303941,\n2303972,\n2304006,\n2304036,\n2304070,\n2304101,\n2304145,\n2304253,\n2304520,\n2304861,\n2307076,\n2307357,\n2307396,\n2308646,\n2308741,\n2308893,\n2308933,\n2308998,\n2309125,\n2309156,\n2309201,\n2309220,\n2309254,\n2309309,\n2310148,\n2310181,\n2310500,\n2311781,\n2311974,\n2312004,\n2312037,\n2312177,\n2312421,\n2312477,\n2312708,\n2312741,\n2312934,\n2312997,\n2313092,\n2314565,\n2314982,\n2315013,\n2315089,\n2315172,\n2315217,\n2315389,\n2315780,\n2318141,\n2318353,\n2318685,\n2326532,\n2326845,\n2326852,\n2328038,\n2328069,\n2328317,\n2328325,\n2328518,\n2328549,\n2328580,\n2328625,\n2328797,\n2329096,\n2329418,\n2330045,\n2330129,\n2330180,\n2331165,\n2331205,\n2331933,\n2331942,\n2331973,\n2332198,\n2332229,\n2332294,\n2332325,\n2332413,\n2334724,\n2334973,\n2334980,\n2335069,\n2335076,\n2336293,\n2336509,\n2336581,\n2336637,\n2336645,\n2336733,\n2336741,\n2336964,\n2336997,\n2337053,\n2337288,\n2337629,\n2337796,\n2338013,\n2338020,\n2338109,\n2338116,\n2339142,\n2339325,\n2339333,\n2339421,\n2339430,\n2339493,\n2339526,\n2339557,\n2339588,\n2339645,\n2339848,\n2340189,\n2350084,\n2350693,\n2350758,\n2350833,\n2350909,\n2351109,\n2351172,\n2351206,\n2351236,\n2351677,\n2351684,\n2352774,\n2352837,\n2353021,\n2353094,\n2353157,\n2353190,\n2353221,\n2353265,\n2353672,\n2354013,\n2356740,\n2356797,\n2357258,\n2357941,\n2358195,\n2358325,\n2358877,\n2359281,\n2359300,\n2388829,\n2392073,\n2395645,\n2395665,\n2395837,\n2396164,\n2402461,\n2486788,\n2489905,\n2489981,\n2490372,\n2524698,\n2525189,\n2525220,\n2525413,\n2525917,\n2654212,\n2672893,\n2949124,\n2967357,\n2967556,\n2968573,\n2968584,\n2968925,\n2969041,\n2969092,\n2971645,\n2971656,\n2971997,\n2972164,\n2973149,\n2973189,\n2973361,\n2973405,\n2973700,\n2975237,\n2975473,\n2975637,\n2975747,\n2975889,\n2975925,\n2975965,\n2976264,\n2976605,\n2976618,\n2976861,\n2976868,\n2977565,\n2977700,\n2978333,\n3000320,\n3001345,\n3002378,\n3003121,\n3003261,\n3006468,\n3008893,\n3008997,\n3009028,\n3009062,\n3010845,\n3011045,\n3011171,\n3011613,\n3013635,\n3013713,\n3013731,\n3013765,\n3013821,\n3014150,\n3014237,\n3014660,\n3211037,\n3211268,\n3250909,\n3252228,\n3252541,\n3538435,\n3538589,\n3538595,\n3538845,\n3538851,\n3538941,\n3538948,\n3548285,\n3548740,\n3548797,\n3549700,\n3549821,\n3549860,\n3549917,\n3550340,\n3550493,\n3550724,\n3563421,\n3637252,\n3640701,\n3640836,\n3641277,\n3641348,\n3641661,\n3641860,\n3642205,\n3642261,\n3642277,\n3642353,\n3642394,\n3642525,\n3792901,\n3794397,\n3794437,\n3795197,\n3795477,\n3799197,\n3801109,\n3808989,\n3809301,\n3810557,\n3810613,\n3812518,\n3812581,\n3812693,\n3812774,\n3812986,\n3813221,\n3813493,\n3813541,\n3813781,\n3814725,\n3814869,\n3816829,\n3817493,\n3819589,\n3819701,\n3819741,\n3823626,\n3824285,\n3824650,\n3825309,\n3825685,\n3828477,\n3828746,\n3829565,\n3833856,\n3834689,\n3835520,\n3836353,\n3836605,\n3836609,\n3837184,\n3838017,\n3838848,\n3838909,\n3838912,\n3839005,\n3839040,\n3839101,\n3839136,\n3839229,\n3839264,\n3839421,\n3839424,\n3839681,\n3839837,\n3839841,\n3839901,\n3839905,\n3840157,\n3840161,\n3840512,\n3841345,\n3842176,\n3842269,\n3842272,\n3842429,\n3842464,\n3842749,\n3842752,\n3843005,\n3843009,\n3843840,\n3843933,\n3843936,\n3844093,\n3844096,\n3844285,\n3844288,\n3844349,\n3844416,\n3844669,\n3844673,\n3845504,\n3846337,\n3847168,\n3848001,\n3848832,\n3849665,\n3850496,\n3851329,\n3852160,\n3852993,\n3853824,\n3854657,\n3855581,\n3855616,\n3856434,\n3856449,\n3857266,\n3857281,\n3857472,\n3858290,\n3858305,\n3859122,\n3859137,\n3859328,\n3860146,\n3860161,\n3860978,\n3860993,\n3861184,\n3862002,\n3862017,\n3862834,\n3862849,\n3863040,\n3863858,\n3863873,\n3864690,\n3864705,\n3864896,\n3864929,\n3864989,\n3865032,\n3866645,\n3883013,\n3884789,\n3884901,\n3886517,\n3886757,\n3886805,\n3887237,\n3887285,\n3887345,\n3887517,\n3887973,\n3888157,\n3888165,\n3888669,\n3923969,\n3924292,\n3924321,\n3924989,\n3925153,\n3925373,\n3932165,\n3932413,\n3932421,\n3932989,\n3933029,\n3933277,\n3933285,\n3933373,\n3933381,\n3933565,\n3933699,\n3935709,\n3936741,\n3936797,\n3940356,\n3941821,\n3941893,\n3942115,\n3942365,\n3942408,\n3942749,\n3942852,\n3942901,\n3942941,\n3953156,\n3954117,\n3954173,\n3954692,\n3956101,\n3956232,\n3956573,\n3956723,\n3956765,\n3971588,\n3972451,\n3972485,\n3972616,\n3972957,\n3996676,\n3996925,\n3996932,\n3997085,\n3997092,\n3997181,\n3997188,\n3997693,\n3997700,\n4004029,\n4004074,\n4004357,\n4004605,\n4005888,\n4006977,\n4008069,\n4008291,\n4008349,\n4008456,\n4008797,\n4008913,\n4008989,\n4034090,\n4035989,\n4036010,\n4036115,\n4036138,\n4036285,\n4038698,\n4040149,\n4040170,\n4040669,\n4046852,\n4047005,\n4047012,\n4047901,\n4047908,\n4047997,\n4048004,\n4048061,\n4048100,\n4048157,\n4048164,\n4048509,\n4048516,\n4048669,\n4048676,\n4048733,\n4048740,\n4048797,\n4048964,\n4049021,\n4049124,\n4049181,\n4049188,\n4049245,\n4049252,\n4049309,\n4049316,\n4049437,\n4049444,\n4049533,\n4049540,\n4049597,\n4049636,\n4049693,\n4049700,\n4049757,\n4049764,\n4049821,\n4049828,\n4049885,\n4049892,\n4049949,\n4049956,\n4050045,\n4050052,\n4050109,\n4050148,\n4050301,\n4050308,\n4050557,\n4050564,\n4050717,\n4050724,\n4050877,\n4050884,\n4050941,\n4050948,\n4051293,\n4051300,\n4051869,\n4052004,\n4052125,\n4052132,\n4052317,\n4052324,\n4052893,\n4054546,\n4054621,\n4063253,\n4064669,\n4064789,\n4067997,\n4068373,\n4068861,\n4068917,\n4069405,\n4069429,\n4069917,\n4069941,\n4071133,\n4071434,\n4071861,\n4077021,\n4078805,\n4079741,\n4080149,\n4081565,\n4081685,\n4081981,\n4082197,\n4082269,\n4082709,\n4082909,\n4087829,\n4095860,\n4096021,\n4119325,\n4119445,\n4119997,\n4120085,\n4120509,\n4120597,\n4124413,\n4124533,\n4127581,\n4127765,\n4128157,\n4128277,\n4128317,\n4128789,\n4129181,\n4129301,\n4131101,\n4131349,\n4131677,\n4131861,\n4133149,\n4133397,\n4134365,\n4134421,\n4134493,\n4136981,\n4147869,\n4148245,\n4148701,\n4148757,\n4149181,\n4149269,\n4149565,\n4149781,\n4151261,\n4151285,\n4151517,\n4151765,\n4152221,\n4152341,\n4152637,\n4152853,\n4153149,\n4153365,\n4158077,\n4158101,\n4159869,\n4161032,\n4161373,\n4194308,\n5561373,\n5562372,\n5695325,\n5695492,\n5702621,\n5702660,\n5887069,\n5887492,\n6126653,\n6127108,\n6147037,\n6225924,\n6243293,\n6291460,\n6449533,\n6449668,\n6583837,\n29360186,\n29360221,\n29361178,\n29364253,\n29368325,\n29376029,\n31457308,\n33554397,\n33554460,\n35651549,\n35651613,\n//--Autogenerated -- end of section automatically generated\n};\n\nconstexpr int maxUnicode = 0x10ffff;\nconstexpr int maskCategory = 0x1F;\n\n}\n\n// Each element in catRanges is the start of a range of Unicode characters in\n// one general category.\n// The value is comprised of a 21-bit character value shifted 5 bits and a 5 bit\n// category matching the CharacterCategory enumeration.\n// Initial version has 3249 entries and adds about 13K to the executable.\n// The array is in ascending order so can be searched using binary search.\n// Therefore the average call takes log2(3249) = 12 comparisons.\n// For speed, it may be useful to make a linear table for the common values,\n// possibly for 0..0xff for most Western European text or 0..0xfff for most\n// alphabetic languages.\n\nCharacterCategory CategoriseCharacter(int character) noexcept {\n\tif (character < 0 || character > maxUnicode)\n\t\treturn ccCn;\n\tconst int baseValue = character * (maskCategory+1) + maskCategory;\n\ttry {\n\t\t// lower_bound will never throw with these args but its not marked noexcept so add catch to pretend.\n\t\tconst int *placeAfter = std::lower_bound(catRanges, std::end(catRanges), baseValue);\n\t\treturn static_cast<CharacterCategory>(*(placeAfter - 1) & maskCategory);\n\t} catch (...) {\n\t\treturn ccCn;\n\t}\n}\n\n// Implementation of character sets recommended for identifiers in Unicode Standard Annex #31.\n// http://unicode.org/reports/tr31/\n\nnamespace {\n\nenum class OtherID { oidNone, oidStart, oidContinue };\n\n// Silence 'magic' number warning as these character values are not used in multiple places.\n\n// NOLINTBEGIN(*-magic-numbers)\n\n// Some characters are treated as valid for identifiers even\n// though most characters from their category are not.\n// Values copied from http://www.unicode.org/Public/9.0.0/ucd/PropList.txt\nOtherID OtherIDOfCharacter(int character) noexcept {\n\tif (\n\t\t(character == 0x1885) ||\t// MONGOLIAN LETTER ALI GALI BALUDA\n\t\t(character == 0x1886) ||\t// MONGOLIAN LETTER ALI GALI THREE BALUDA\n\t\t(character == 0x2118) ||\t// SCRIPT CAPITAL P\n\t\t(character == 0x212E) ||\t// ESTIMATED SYMBOL\n\t\t(character == 0x309B) ||\t// KATAKANA-HIRAGANA VOICED SOUND MARK\n\t\t(character == 0x309C)) {\t// KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK\n\t\treturn OtherID::oidStart;\n\t} else if (\n\t\t(character == 0x00B7) ||\t// MIDDLE DOT\n\t\t(character == 0x0387) ||\t// GREEK ANO TELEIA\n\t\t((character >= 0x1369) && (character <= 0x1371)) ||\t// ETHIOPIC DIGIT ONE..ETHIOPIC DIGIT NINE\n\t\t(character == 0x19DA)) {\t// NEW TAI LUE THAM DIGIT ONE\n\t\treturn OtherID::oidContinue;\n\t} else {\n\t\treturn OtherID::oidNone;\n\t}\n}\n\n// Determine if a character is in  Ll|Lu|Lt|Lm|Lo|Nl|Mn|Mc|Nd|Pc and has\n// Pattern_Syntax|Pattern_White_Space.\n// As of Unicode 9, only VERTICAL TILDE which is in Lm and has Pattern_Syntax matches.\n// Should really generate from PropList.txt a list of Pattern_Syntax and Pattern_White_Space.\nconstexpr bool IsIdPattern(int character) noexcept {\n\treturn character == 0x2E2F;\n}\n\nbool OmitXidStart(int character) noexcept {\n\tswitch (character) {\n\tcase 0x037A:\t// GREEK YPOGEGRAMMENI\n\tcase 0x0E33:\t// THAI CHARACTER SARA AM\n\tcase 0x0EB3:\t// LAO VOWEL SIGN AM\n\tcase 0x309B:\t// KATAKANA-HIRAGANA VOICED SOUND MARK\n\tcase 0x309C:\t// KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK\n\tcase 0xFC5E:\t// ARABIC LIGATURE SHADDA WITH DAMMATAN ISOLATED FORM\n\tcase 0xFC5F:\t// ARABIC LIGATURE SHADDA WITH KASRATAN ISOLATED FORM\n\tcase 0xFC60:\t// ARABIC LIGATURE SHADDA WITH FATHA ISOLATED FORM\n\tcase 0xFC61:\t// ARABIC LIGATURE SHADDA WITH DAMMA ISOLATED FORM\n\tcase 0xFC62:\t// ARABIC LIGATURE SHADDA WITH KASRA ISOLATED FORM\n\tcase 0xFC63:\t// ARABIC LIGATURE SHADDA WITH SUPERSCRIPT ALEF ISOLATED FORM\n\tcase 0xFDFA:\t// ARABIC LIGATURE SALLALLAHOU ALAYHE WASALLAM\n\tcase 0xFDFB:\t// ARABIC LIGATURE JALLAJALALOUHOU\n\tcase 0xFE70:\t// ARABIC FATHATAN ISOLATED FORM\n\tcase 0xFE72:\t// ARABIC DAMMATAN ISOLATED FORM\n\tcase 0xFE74:\t// ARABIC KASRATAN ISOLATED FORM\n\tcase 0xFE76:\t// ARABIC FATHA ISOLATED FORM\n\tcase 0xFE78:\t// ARABIC DAMMA ISOLATED FORM\n\tcase 0xFE7A:\t// ARABIC KASRA ISOLATED FORM\n\tcase 0xFE7C:\t// ARABIC SHADDA ISOLATED FORM\n\tcase 0xFE7E:\t// ARABIC SUKUN ISOLATED FORM\n\tcase 0xFF9E:\t// HALFWIDTH KATAKANA VOICED SOUND MARK\n\tcase 0xFF9F:\t// HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK\n\t\treturn true;\n\tdefault:\n\t\treturn false;\n\t}\n}\n\nbool OmitXidContinue(int character) noexcept {\n\tswitch (character) {\n\tcase 0x037A:\t// GREEK YPOGEGRAMMENI\n\tcase 0x309B:\t// KATAKANA-HIRAGANA VOICED SOUND MARK\n\tcase 0x309C:\t// KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK\n\tcase 0xFC5E:\t// ARABIC LIGATURE SHADDA WITH DAMMATAN ISOLATED FORM\n\tcase 0xFC5F:\t// ARABIC LIGATURE SHADDA WITH KASRATAN ISOLATED FORM\n\tcase 0xFC60:\t// ARABIC LIGATURE SHADDA WITH FATHA ISOLATED FORM\n\tcase 0xFC61:\t// ARABIC LIGATURE SHADDA WITH DAMMA ISOLATED FORM\n\tcase 0xFC62:\t// ARABIC LIGATURE SHADDA WITH KASRA ISOLATED FORM\n\tcase 0xFC63:\t// ARABIC LIGATURE SHADDA WITH SUPERSCRIPT ALEF ISOLATED FORM\n\tcase 0xFDFA:\t// ARABIC LIGATURE SALLALLAHOU ALAYHE WASALLAM\n\tcase 0xFDFB:\t// ARABIC LIGATURE JALLAJALALOUHOU\n\tcase 0xFE70:\t// ARABIC FATHATAN ISOLATED FORM\n\tcase 0xFE72:\t// ARABIC DAMMATAN ISOLATED FORM\n\tcase 0xFE74:\t// ARABIC KASRATAN ISOLATED FORM\n\tcase 0xFE76:\t// ARABIC FATHA ISOLATED FORM\n\tcase 0xFE78:\t// ARABIC DAMMA ISOLATED FORM\n\tcase 0xFE7A:\t// ARABIC KASRA ISOLATED FORM\n\tcase 0xFE7C:\t// ARABIC SHADDA ISOLATED FORM\n\tcase 0xFE7E:\t// ARABIC SUKUN ISOLATED FORM\n\t\treturn true;\n\tdefault:\n\t\treturn false;\n\t}\n}\n\n// NOLINTEND(*-magic-numbers)\n\n}\n\n// UAX #31 defines ID_Start as\n// [[:L:][:Nl:][:Other_ID_Start:]--[:Pattern_Syntax:]--[:Pattern_White_Space:]]\nbool IsIdStart(int character) noexcept {\n\tif (IsIdPattern(character)) {\n\t\treturn false;\n\t}\n\tconst OtherID oid = OtherIDOfCharacter(character);\n\tif (oid == OtherID::oidStart) {\n\t\treturn true;\n\t}\n\tconst CharacterCategory c = CategoriseCharacter(character);\n\treturn (c == ccLl || c == ccLu || c == ccLt || c == ccLm || c == ccLo\n\t\t|| c == ccNl);\n}\n\n// UAX #31 defines ID_Continue as\n// [[:ID_Start:][:Mn:][:Mc:][:Nd:][:Pc:][:Other_ID_Continue:]--[:Pattern_Syntax:]--[:Pattern_White_Space:]]\nbool IsIdContinue(int character) noexcept {\n\tif (IsIdPattern(character)) {\n\t\treturn false;\n\t}\n\tconst OtherID oid = OtherIDOfCharacter(character);\n\tif (oid != OtherID::oidNone) {\n\t\treturn true;\n\t}\n\tconst CharacterCategory c = CategoriseCharacter(character);\n\treturn (c == ccLl || c == ccLu || c == ccLt || c == ccLm || c == ccLo\n\t\t|| c == ccNl || c == ccMn || c == ccMc || c == ccNd || c == ccPc);\n}\n\n// XID_Start is ID_Start modified for Normalization Form KC in UAX #31\nbool IsXidStart(int character) noexcept {\n\tif (OmitXidStart(character)) {\n\t\treturn false;\n\t} else {\n\t\treturn IsIdStart(character);\n\t}\n}\n\n// XID_Continue is ID_Continue modified for Normalization Form KC in UAX #31\nbool IsXidContinue(int character) noexcept {\n\tif (OmitXidContinue(character)) {\n\t\treturn false;\n\t} else {\n\t\treturn IsIdContinue(character);\n\t}\n}\n\nCharacterCategoryMap::CharacterCategoryMap() {\n\tOptimize(256);\n}\n\nint CharacterCategoryMap::Size() const noexcept {\n\treturn static_cast<int>(dense.size());\n}\n\nvoid CharacterCategoryMap::Optimize(int countCharacters) {\n\tconst int characters = std::clamp(countCharacters, 256, maxUnicode + 1);\n\tdense.resize(characters);\n\n\tint end = 0;\n\tint index = 0;\n\tint current = catRanges[index];\n\t++index;\n\tdo {\n\t\tconst int next = catRanges[index];\n\t\tconst unsigned char category = current & maskCategory;\n\t\tcurrent >>= 5;\n\t\tend = std::min(characters, next >> 5);\n\t\twhile (current < end) {\n\t\t\tdense[current++] = category;\n\t\t}\n\t\tcurrent = next;\n\t\t++index;\n\t} while (characters > end);\n}\n\n}\n"
  },
  {
    "path": "Libraries/scintilla/src/CharacterCategoryMap.h",
    "content": "// Scintilla source code edit control\n/** @file CharacterCategoryMap.h\n ** Returns the Unicode general category of a character.\n ** Similar code to Lexilla's lexilla/lexlib/CharacterCategory.h but renamed\n ** to avoid problems with builds that statically include both Scintilla and Lexilla.\n **/\n// Copyright 2013 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#ifndef CHARACTERCATEGORYMAP_H\n#define CHARACTERCATEGORYMAP_H\n\nnamespace Scintilla::Internal {\n\nenum CharacterCategory {\n\tccLu, ccLl, ccLt, ccLm, ccLo,\n\tccMn, ccMc, ccMe,\n\tccNd, ccNl, ccNo,\n\tccPc, ccPd, ccPs, ccPe, ccPi, ccPf, ccPo,\n\tccSm, ccSc, ccSk, ccSo,\n\tccZs, ccZl, ccZp,\n\tccCc, ccCf, ccCs, ccCo, ccCn\n};\n\nCharacterCategory CategoriseCharacter(int character) noexcept;\n\n// Common definitions of allowable characters in identifiers from UAX #31.\nbool IsIdStart(int character) noexcept;\nbool IsIdContinue(int character) noexcept;\nbool IsXidStart(int character) noexcept;\nbool IsXidContinue(int character) noexcept;\n\nclass CharacterCategoryMap {\nprivate:\n\tstd::vector<unsigned char> dense;\npublic:\n\tCharacterCategoryMap();\n\tCharacterCategory CategoryFor(int character) const noexcept {\n\t\tif (static_cast<size_t>(character) < dense.size()) {\n\t\t\treturn static_cast<CharacterCategory>(dense[character]);\n\t\t} else {\n\t\t\t// binary search through ranges\n\t\t\treturn CategoriseCharacter(character);\n\t\t}\n\t}\n\tint Size() const noexcept;\n\tvoid Optimize(int countCharacters);\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/src/CharacterType.cxx",
    "content": "// Scintilla source code edit control\n/** @file CharacterType.cxx\n ** Tests for character type and case-insensitive comparisons.\n **/\n// Copyright 1998-2010 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#include <cstdlib>\n#include <cassert>\n\n#include \"CharacterType.h\"\n\nusing namespace Scintilla::Internal;\n\nnamespace Scintilla::Internal {\n\nint CompareCaseInsensitive(const char *a, const char *b) noexcept {\n\twhile (*a && *b) {\n\t\tif (*a != *b) {\n\t\t\tconst char upperA = MakeUpperCase(*a);\n\t\t\tconst char upperB = MakeUpperCase(*b);\n\t\t\tif (upperA != upperB)\n\t\t\t\treturn upperA - upperB;\n\t\t}\n\t\ta++;\n\t\tb++;\n\t}\n\t// Either *a or *b is nul\n\treturn *a - *b;\n}\n\nint CompareNCaseInsensitive(const char *a, const char *b, size_t len) noexcept {\n\twhile (*a && *b && len) {\n\t\tif (*a != *b) {\n\t\t\tconst char upperA = MakeUpperCase(*a);\n\t\t\tconst char upperB = MakeUpperCase(*b);\n\t\t\tif (upperA != upperB)\n\t\t\t\treturn upperA - upperB;\n\t\t}\n\t\ta++;\n\t\tb++;\n\t\tlen--;\n\t}\n\tif (len == 0)\n\t\treturn 0;\n\telse\n\t\t// Either *a or *b is nul\n\t\treturn *a - *b;\n}\n\n}\n"
  },
  {
    "path": "Libraries/scintilla/src/CharacterType.h",
    "content": "// Scintilla source code edit control\n/** @file CharacterType.h\n ** Tests for character type and case-insensitive comparisons.\n **/\n// Copyright 2007 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#ifndef CHARACTERTYPE_H\n#define CHARACTERTYPE_H\n\nnamespace Scintilla::Internal {\n\n// Functions for classifying characters\n\n/**\n * Check if a character is a space.\n * This is ASCII specific but is safe with chars >= 0x80.\n */\nconstexpr bool IsASpace(int ch) noexcept {\n    return (ch == ' ') || ((ch >= 0x09) && (ch <= 0x0d));\n}\n\nconstexpr bool IsSpaceOrTab(int ch) noexcept {\n\treturn (ch == ' ') || (ch == '\\t');\n}\n\nconstexpr bool IsControl(int ch) noexcept {\n\treturn ((ch >= 0) && (ch <= 0x1F)) || (ch == 0x7F);\n}\n\nconstexpr bool IsEOLCharacter(int ch) noexcept {\n\treturn ch == '\\r' || ch == '\\n';\n}\n\nconstexpr bool IsBreakSpace(int ch) noexcept {\n\t// used for text breaking, treat C0 control character as space.\n\t// by default C0 control character is handled as special representation,\n\t// so not appears in normal text. 0x7F DEL is omitted to simplify the code.\n\treturn ch >= 0 && ch <= ' ';\n}\n\nconstexpr bool IsADigit(int ch) noexcept {\n\treturn (ch >= '0') && (ch <= '9');\n}\n\nconstexpr bool IsADigit(int ch, int base) noexcept {\n\tif (base <= 10) {\n\t\treturn (ch >= '0') && (ch < '0' + base);\n\t} else {\n\t\treturn ((ch >= '0') && (ch <= '9')) ||\n\t\t       ((ch >= 'A') && (ch < 'A' + base - 10)) ||\n\t\t       ((ch >= 'a') && (ch < 'a' + base - 10));\n\t}\n}\n\nconstexpr bool IsASCII(int ch) noexcept {\n\treturn (ch >= 0) && (ch < 0x80);\n}\n\nconstexpr bool IsLowerCase(int ch) noexcept {\n\treturn (ch >= 'a') && (ch <= 'z');\n}\n\nconstexpr bool IsUpperCase(int ch) noexcept {\n\treturn (ch >= 'A') && (ch <= 'Z');\n}\n\nconstexpr bool IsUpperOrLowerCase(int ch) noexcept {\n\treturn IsUpperCase(ch) || IsLowerCase(ch);\n}\n\nconstexpr bool IsAlphaNumeric(int ch) noexcept {\n\treturn\n\t\t((ch >= '0') && (ch <= '9')) ||\n\t\t((ch >= 'a') && (ch <= 'z')) ||\n\t\t((ch >= 'A') && (ch <= 'Z'));\n}\n\nconstexpr bool IsPunctuation(int ch) noexcept {\n\tswitch (ch) {\n\tcase '!':\n\tcase '\"':\n\tcase '#':\n\tcase '$':\n\tcase '%':\n\tcase '&':\n\tcase '\\'':\n\tcase '(':\n\tcase ')':\n\tcase '*':\n\tcase '+':\n\tcase ',':\n\tcase '-':\n\tcase '.':\n\tcase '/':\n\tcase ':':\n\tcase ';':\n\tcase '<':\n\tcase '=':\n\tcase '>':\n\tcase '?':\n\tcase '@':\n\tcase '[':\n\tcase '\\\\':\n\tcase ']':\n\tcase '^':\n\tcase '_':\n\tcase '`':\n\tcase '{':\n\tcase '|':\n\tcase '}':\n\tcase '~':\n\t\treturn true;\n\tdefault:\n\t\treturn false;\n\t}\n}\n\n// Simple case functions for ASCII supersets.\n\ntemplate <typename T>\nconstexpr T MakeUpperCase(T ch) noexcept {\n\tif (ch < 'a' || ch > 'z')\n\t\treturn ch;\n\telse\n\t\treturn ch - 'a' + 'A';\n}\n\ntemplate <typename T>\nconstexpr T MakeLowerCase(T ch) noexcept {\n\tif (ch < 'A' || ch > 'Z')\n\t\treturn ch;\n\telse\n\t\treturn ch - 'A' + 'a';\n}\n\nint CompareCaseInsensitive(const char *a, const char *b) noexcept;\nint CompareNCaseInsensitive(const char *a, const char *b, size_t len) noexcept;\n\n}\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/src/ContractionState.cxx",
    "content": "// Scintilla source code edit control\n/** @file ContractionState.cxx\n ** Manages visibility of lines for folding and wrapping.\n **/\n// Copyright 1998-2007 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#include <cstddef>\n#include <cassert>\n#include <cstring>\n\n#include <stdexcept>\n#include <string_view>\n#include <vector>\n#include <optional>\n#include <algorithm>\n#include <memory>\n\n#include \"Debugging.h\"\n\n#include \"Position.h\"\n#include \"UniqueString.h\"\n#include \"SplitVector.h\"\n#include \"Partitioning.h\"\n#include \"RunStyles.h\"\n#include \"SparseVector.h\"\n#include \"ContractionState.h\"\n\nusing namespace Scintilla::Internal;\n\nnamespace {\n\ntemplate <typename LINE>\nclass ContractionState final : public IContractionState {\n\t// These contain 1 element for every document line.\n\tstd::unique_ptr<RunStyles<LINE, char>> visible;\n\tstd::unique_ptr<RunStyles<LINE, char>> expanded;\n\tstd::unique_ptr<RunStyles<LINE, int>> heights;\n\tstd::unique_ptr<SparseVector<UniqueString>> foldDisplayTexts;\n\tstd::unique_ptr<Partitioning<LINE>> displayLines;\n\tLINE linesInDocument;\n\n\tvoid EnsureData();\n\n\tbool OneToOne() const noexcept {\n\t\t// True when each document line is exactly one display line so need for\n\t\t// complex data structures.\n\t\treturn visible == nullptr;\n\t}\n\n\tvoid InsertLine(Sci::Line lineDoc);\n\tvoid DeleteLine(Sci::Line lineDoc);\n\n\t// line_cast(): cast Sci::Line to either 32-bit or 64-bit value\n\t// This avoids warnings from Visual C++ Code Analysis and shortens code\n\tstatic constexpr LINE line_cast(Sci::Line line) noexcept {\n\t\treturn static_cast<LINE>(line);\n\t}\n\npublic:\n\tContractionState() noexcept;\n\n\tvoid Clear() noexcept override;\n\n\tSci::Line LinesInDoc() const noexcept override;\n\tSci::Line LinesDisplayed() const noexcept override;\n\tSci::Line DisplayFromDoc(Sci::Line lineDoc) const noexcept override;\n\tSci::Line DisplayFromDocSub(Sci::Line lineDoc, Sci::Line lineSub) const noexcept override;\n\tSci::Line DisplayLastFromDoc(Sci::Line lineDoc) const noexcept override;\n\tSci::Line DocFromDisplay(Sci::Line lineDisplay) const noexcept override;\n\n\tvoid InsertLines(Sci::Line lineDoc, Sci::Line lineCount) override;\n\tvoid DeleteLines(Sci::Line lineDoc, Sci::Line lineCount) override;\n\n\tbool GetVisible(Sci::Line lineDoc) const noexcept override;\n\tbool SetVisible(Sci::Line lineDocStart, Sci::Line lineDocEnd, bool isVisible) override;\n\tbool HiddenLines() const noexcept override;\n\n\tconst char *GetFoldDisplayText(Sci::Line lineDoc) const noexcept override;\n\tbool SetFoldDisplayText(Sci::Line lineDoc, const char *text) override;\n\n\tbool GetExpanded(Sci::Line lineDoc) const noexcept override;\n\tbool SetExpanded(Sci::Line lineDoc, bool isExpanded) override;\n\tbool ExpandAll() override;\n\tSci::Line ContractedNext(Sci::Line lineDocStart) const noexcept override;\n\n\tint GetHeight(Sci::Line lineDoc) const noexcept override;\n\tbool SetHeight(Sci::Line lineDoc, int height) override;\n\n\tvoid ShowAll() noexcept override;\n\n\tvoid Check() const noexcept;\n};\n\ntemplate <typename LINE>\nContractionState<LINE>::ContractionState() noexcept : linesInDocument(1) {\n}\n\ntemplate <typename LINE>\nvoid ContractionState<LINE>::EnsureData() {\n\tif (OneToOne()) {\n\t\tvisible = std::make_unique<RunStyles<LINE, char>>();\n\t\texpanded = std::make_unique<RunStyles<LINE, char>>();\n\t\theights = std::make_unique<RunStyles<LINE, int>>();\n\t\tfoldDisplayTexts = std::make_unique<SparseVector<UniqueString>>();\n\t\tdisplayLines = std::make_unique<Partitioning<LINE>>(4);\n\t\tInsertLines(0, linesInDocument);\n\t}\n}\n\ntemplate <typename LINE>\nvoid ContractionState<LINE>::InsertLine(Sci::Line lineDoc) {\n\tif (OneToOne()) {\n\t\tlinesInDocument++;\n\t} else {\n\t\tconst LINE lineDocCast = line_cast(lineDoc);\n\t\tvisible->InsertSpace(lineDocCast, 1);\n\t\tvisible->SetValueAt(lineDocCast, 1);\n\t\texpanded->InsertSpace(lineDocCast, 1);\n\t\texpanded->SetValueAt(lineDocCast, 1);\n\t\theights->InsertSpace(lineDocCast, 1);\n\t\theights->SetValueAt(lineDocCast, 1);\n\t\tfoldDisplayTexts->InsertSpace(lineDocCast, 1);\n\t\tfoldDisplayTexts->SetValueAt(lineDocCast, nullptr);\n\t\tconst Sci::Line lineDisplay = DisplayFromDoc(lineDoc);\n\t\tdisplayLines->InsertPartition(lineDocCast, line_cast(lineDisplay));\n\t\tdisplayLines->InsertText(lineDocCast, 1);\n\t}\n}\n\ntemplate <typename LINE>\nvoid ContractionState<LINE>::DeleteLine(Sci::Line lineDoc) {\n\tif (OneToOne()) {\n\t\tlinesInDocument--;\n\t} else {\n\t\tconst LINE lineDocCast = line_cast(lineDoc);\n\t\tif (GetVisible(lineDoc)) {\n\t\t\tdisplayLines->InsertText(lineDocCast, -heights->ValueAt(lineDocCast));\n\t\t}\n\t\tdisplayLines->RemovePartition(lineDocCast);\n\t\tvisible->DeleteRange(lineDocCast, 1);\n\t\texpanded->DeleteRange(lineDocCast, 1);\n\t\theights->DeleteRange(lineDocCast, 1);\n\t\tfoldDisplayTexts->DeletePosition(lineDocCast);\n\t}\n}\n\ntemplate <typename LINE>\nvoid ContractionState<LINE>::Clear() noexcept {\n\tvisible.reset();\n\texpanded.reset();\n\theights.reset();\n\tfoldDisplayTexts.reset();\n\tdisplayLines.reset();\n\tlinesInDocument = 1;\n}\n\ntemplate <typename LINE>\nSci::Line ContractionState<LINE>::LinesInDoc() const noexcept {\n\tif (OneToOne()) {\n\t\treturn linesInDocument;\n\t} else {\n\t\treturn displayLines->Partitions() - 1;\n\t}\n}\n\ntemplate <typename LINE>\nSci::Line ContractionState<LINE>::LinesDisplayed() const noexcept {\n\tif (OneToOne()) {\n\t\treturn linesInDocument;\n\t} else {\n\t\treturn displayLines->PositionFromPartition(line_cast(LinesInDoc()));\n\t}\n}\n\ntemplate <typename LINE>\nSci::Line ContractionState<LINE>::DisplayFromDoc(Sci::Line lineDoc) const noexcept {\n\tif (OneToOne()) {\n\t\treturn (lineDoc <= linesInDocument) ? lineDoc : linesInDocument;\n\t} else {\n\t\tif (lineDoc > displayLines->Partitions())\n\t\t\tlineDoc = displayLines->Partitions();\n\t\treturn displayLines->PositionFromPartition(line_cast(lineDoc));\n\t}\n}\n\ntemplate <typename LINE>\nSci::Line ContractionState<LINE>::DisplayFromDocSub(Sci::Line lineDoc, Sci::Line lineSub) const noexcept {\n\treturn DisplayFromDoc(lineDoc) +\n\t\tstd::min(lineSub, static_cast<Sci::Line>(GetHeight(lineDoc) - 1));\n}\n\ntemplate <typename LINE>\nSci::Line ContractionState<LINE>::DisplayLastFromDoc(Sci::Line lineDoc) const noexcept {\n\treturn DisplayFromDoc(lineDoc) + GetHeight(lineDoc) - 1;\n}\n\ntemplate <typename LINE>\nSci::Line ContractionState<LINE>::DocFromDisplay(Sci::Line lineDisplay) const noexcept {\n\tif (OneToOne()) {\n\t\treturn lineDisplay;\n\t} else {\n\t\tif (lineDisplay < 0) {\n\t\t\treturn 0;\n\t\t}\n\t\tif (lineDisplay > LinesDisplayed()) {\n\t\t\treturn displayLines->PartitionFromPosition(line_cast(LinesDisplayed()));\n\t\t}\n\t\tconst Sci::Line lineDoc = displayLines->PartitionFromPosition(line_cast(lineDisplay));\n\t\tPLATFORM_ASSERT(GetVisible(lineDoc));\n\t\treturn lineDoc;\n\t}\n}\n\ntemplate <typename LINE>\nvoid ContractionState<LINE>::InsertLines(Sci::Line lineDoc, Sci::Line lineCount) {\n\tif (OneToOne()) {\n\t\tlinesInDocument += line_cast(lineCount);\n\t} else {\n\t\tfor (Sci::Line l = 0; l < lineCount; l++) {\n\t\t\tInsertLine(lineDoc + l);\n\t\t}\n\t}\n\tCheck();\n}\n\ntemplate <typename LINE>\nvoid ContractionState<LINE>::DeleteLines(Sci::Line lineDoc, Sci::Line lineCount) {\n\tif (OneToOne()) {\n\t\tlinesInDocument -= line_cast(lineCount);\n\t} else {\n\t\tfor (Sci::Line l = 0; l < lineCount; l++) {\n\t\t\tDeleteLine(lineDoc);\n\t\t}\n\t}\n\tCheck();\n}\n\ntemplate <typename LINE>\nbool ContractionState<LINE>::GetVisible(Sci::Line lineDoc) const noexcept {\n\tif (OneToOne()) {\n\t\treturn true;\n\t} else {\n\t\tif (lineDoc >= visible->Length())\n\t\t\treturn true;\n\t\treturn visible->ValueAt(line_cast(lineDoc)) == 1;\n\t}\n}\n\ntemplate <typename LINE>\nbool ContractionState<LINE>::SetVisible(Sci::Line lineDocStart, Sci::Line lineDocEnd, bool isVisible) {\n\tif (OneToOne() && isVisible) {\n\t\treturn false;\n\t} else {\n\t\tEnsureData();\n\t\tCheck();\n\t\tif ((lineDocStart <= lineDocEnd) && (lineDocStart >= 0) && (lineDocEnd < LinesInDoc())) {\n\t\t\tbool changed = false;\n\t\t\tfor (Sci::Line line = lineDocStart; line <= lineDocEnd; line++) {\n\t\t\t\tif (GetVisible(line) != isVisible) {\n\t\t\t\t\tchanged = true;\n\t\t\t\t\tconst int heightLine = heights->ValueAt(line_cast(line));\n\t\t\t\t\tconst int difference = isVisible ? heightLine : -heightLine;\n\t\t\t\t\tdisplayLines->InsertText(line_cast(line), difference);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (changed) {\n\t\t\t\tvisible->FillRange(line_cast(lineDocStart), isVisible ? 1 : 0,\n\t\t\t\t\tline_cast(lineDocEnd - lineDocStart) + 1);\n\t\t\t}\n\t\t\tCheck();\n\t\t\treturn changed;\n\t\t} else {\n\t\t\treturn false;\n\t\t}\n\t}\n}\n\ntemplate <typename LINE>\nbool ContractionState<LINE>::HiddenLines() const noexcept {\n\tif (OneToOne()) {\n\t\treturn false;\n\t} else {\n\t\treturn !visible->AllSameAs(1);\n\t}\n}\n\ntemplate <typename LINE>\nconst char *ContractionState<LINE>::GetFoldDisplayText(Sci::Line lineDoc) const noexcept {\n\tCheck();\n\treturn foldDisplayTexts->ValueAt(lineDoc).get();\n}\n\ntemplate <typename LINE>\nbool ContractionState<LINE>::SetFoldDisplayText(Sci::Line lineDoc, const char *text) {\n\tEnsureData();\n\tconst char *foldText = foldDisplayTexts->ValueAt(lineDoc).get();\n\tif (!foldText || !text || 0 != strcmp(text, foldText)) {\n\t\tUniqueString uns = IsNullOrEmpty(text) ? UniqueString() : UniqueStringCopy(text);\n\t\tfoldDisplayTexts->SetValueAt(lineDoc, std::move(uns));\n\t\tCheck();\n\t\treturn true;\n\t} else {\n\t\tCheck();\n\t\treturn false;\n\t}\n}\n\ntemplate <typename LINE>\nbool ContractionState<LINE>::GetExpanded(Sci::Line lineDoc) const noexcept {\n\tif (OneToOne()) {\n\t\treturn true;\n\t} else {\n\t\tCheck();\n\t\treturn expanded->ValueAt(line_cast(lineDoc)) == 1;\n\t}\n}\n\ntemplate <typename LINE>\nbool ContractionState<LINE>::SetExpanded(Sci::Line lineDoc, bool isExpanded) {\n\tif (OneToOne() && isExpanded) {\n\t\treturn false;\n\t} else {\n\t\tEnsureData();\n\t\tif (isExpanded != (expanded->ValueAt(line_cast(lineDoc)) == 1)) {\n\t\t\texpanded->SetValueAt(line_cast(lineDoc), isExpanded ? 1 : 0);\n\t\t\tCheck();\n\t\t\treturn true;\n\t\t} else {\n\t\t\tCheck();\n\t\t\treturn false;\n\t\t}\n\t}\n}\n\ntemplate <typename LINE>\nbool ContractionState<LINE>::ExpandAll() {\n\tif (OneToOne()) {\n\t\treturn false;\n\t} else {\n\t\tconst LINE lines = expanded->Length();\n\t\tconst bool changed = expanded->FillRange(0, 1, lines).changed;\n\t\tCheck();\n\t\treturn changed;\n\t}\n}\n\ntemplate <typename LINE>\nSci::Line ContractionState<LINE>::ContractedNext(Sci::Line lineDocStart) const noexcept {\n\tif (OneToOne()) {\n\t\treturn -1;\n\t} else {\n\t\tCheck();\n\t\tif (!expanded->ValueAt(line_cast(lineDocStart))) {\n\t\t\treturn lineDocStart;\n\t\t} else {\n\t\t\tconst Sci::Line lineDocNextChange = expanded->EndRun(line_cast(lineDocStart));\n\t\t\tif (lineDocNextChange < LinesInDoc())\n\t\t\t\treturn lineDocNextChange;\n\t\t\telse\n\t\t\t\treturn -1;\n\t\t}\n\t}\n}\n\ntemplate <typename LINE>\nint ContractionState<LINE>::GetHeight(Sci::Line lineDoc) const noexcept {\n\tif (OneToOne()) {\n\t\treturn 1;\n\t} else {\n\t\treturn heights->ValueAt(line_cast(lineDoc));\n\t}\n}\n\n// Set the number of display lines needed for this line.\n// Return true if this is a change.\ntemplate <typename LINE>\nbool ContractionState<LINE>::SetHeight(Sci::Line lineDoc, int height) {\n\tif (OneToOne() && (height == 1)) {\n\t\treturn false;\n\t} else if (lineDoc < LinesInDoc()) {\n\t\tEnsureData();\n\t\tif (GetHeight(lineDoc) != height) {\n\t\t\tif (GetVisible(lineDoc)) {\n\t\t\t\tdisplayLines->InsertText(line_cast(lineDoc), height - GetHeight(lineDoc));\n\t\t\t}\n\t\t\theights->SetValueAt(line_cast(lineDoc), height);\n\t\t\tCheck();\n\t\t\treturn true;\n\t\t} else {\n\t\t\tCheck();\n\t\t\treturn false;\n\t\t}\n\t} else {\n\t\treturn false;\n\t}\n}\n\ntemplate <typename LINE>\nvoid ContractionState<LINE>::ShowAll() noexcept {\n\tconst LINE lines = line_cast(LinesInDoc());\n\tClear();\n\tlinesInDocument = lines;\n}\n\n// Debugging checks\n\ntemplate <typename LINE>\nvoid ContractionState<LINE>::Check() const noexcept {\n#ifdef CHECK_CORRECTNESS\n\tfor (Sci::Line vline = 0; vline < LinesDisplayed(); vline++) {\n\t\tconst Sci::Line lineDoc = DocFromDisplay(vline);\n\t\tPLATFORM_ASSERT(GetVisible(lineDoc));\n\t}\n\tfor (Sci::Line lineDoc = 0; lineDoc < LinesInDoc(); lineDoc++) {\n\t\tconst Sci::Line displayThis = DisplayFromDoc(lineDoc);\n\t\tconst Sci::Line displayNext = DisplayFromDoc(lineDoc + 1);\n\t\tconst Sci::Line height = displayNext - displayThis;\n\t\tPLATFORM_ASSERT(height >= 0);\n\t\tif (GetVisible(lineDoc)) {\n\t\t\tPLATFORM_ASSERT(GetHeight(lineDoc) == height);\n\t\t} else {\n\t\t\tPLATFORM_ASSERT(0 == height);\n\t\t}\n\t}\n#endif\n}\n\n}\n\nnamespace Scintilla::Internal {\n\nstd::unique_ptr<IContractionState> ContractionStateCreate(bool largeDocument) {\n\tif (largeDocument)\n\t\treturn std::make_unique<ContractionState<Sci::Line>>();\n\telse\n\t\treturn std::make_unique<ContractionState<int>>();\n}\n\n}\n"
  },
  {
    "path": "Libraries/scintilla/src/ContractionState.h",
    "content": "// Scintilla source code edit control\n/** @file ContractionState.h\n ** Manages visibility of lines for folding and wrapping.\n **/\n// Copyright 1998-2007 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#ifndef CONTRACTIONSTATE_H\n#define CONTRACTIONSTATE_H\n\nnamespace Scintilla::Internal {\n\n/**\n*/\nclass IContractionState {\npublic:\n\tvirtual ~IContractionState() {};\n\n\tvirtual void Clear()=0;\n\n\tvirtual Sci::Line LinesInDoc() const noexcept=0;\n\tvirtual Sci::Line LinesDisplayed() const noexcept=0;\n\tvirtual Sci::Line DisplayFromDoc(Sci::Line lineDoc) const noexcept=0;\n\tvirtual Sci::Line DisplayFromDocSub(Sci::Line lineDoc, Sci::Line lineSub) const noexcept=0;\n\tvirtual Sci::Line DisplayLastFromDoc(Sci::Line lineDoc) const noexcept=0;\n\tvirtual Sci::Line DocFromDisplay(Sci::Line lineDisplay) const noexcept=0;\n\n\tvirtual void InsertLines(Sci::Line lineDoc, Sci::Line lineCount)=0;\n\tvirtual void DeleteLines(Sci::Line lineDoc, Sci::Line lineCount)=0;\n\n\tvirtual bool GetVisible(Sci::Line lineDoc) const noexcept=0;\n\tvirtual bool SetVisible(Sci::Line lineDocStart, Sci::Line lineDocEnd, bool isVisible)=0;\n\tvirtual bool HiddenLines() const noexcept=0;\n\n\tvirtual const char *GetFoldDisplayText(Sci::Line lineDoc) const noexcept=0;\n\tvirtual bool SetFoldDisplayText(Sci::Line lineDoc, const char *text)=0;\n\n\tvirtual bool GetExpanded(Sci::Line lineDoc) const noexcept=0;\n\tvirtual bool SetExpanded(Sci::Line lineDoc, bool isExpanded)=0;\n\tvirtual bool ExpandAll()=0;\n\tvirtual Sci::Line ContractedNext(Sci::Line lineDocStart) const noexcept =0;\n\n\tvirtual int GetHeight(Sci::Line lineDoc) const noexcept=0;\n\tvirtual bool SetHeight(Sci::Line lineDoc, int height)=0;\n\n\tvirtual void ShowAll() noexcept=0;\n};\n\nstd::unique_ptr<IContractionState> ContractionStateCreate(bool largeDocument);\n\n}\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/src/DBCS.cxx",
    "content": "// Scintilla source code edit control\n/** @file DBCS.cxx\n ** Functions to handle DBCS double byte encodings like Shift-JIS.\n **/\n// Copyright 2017 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#include <cstdint>\n\n#include <array>\n#include <map>\n\n#include \"DBCS.h\"\n\nusing namespace Scintilla::Internal;\n\nnamespace Scintilla::Internal {\n\n// Silence 'magic' number use since the set of DBCS lead and trail bytes differ\n// between encodings and would require many constant declarations that would just\n// obscure the behaviour.\n\n// NOLINTBEGIN(*-magic-numbers)\n\nbool DBCSIsLeadByte(int codePage, char ch) noexcept {\n\t// Byte ranges found in Wikipedia articles with relevant search strings in each case\n\tconst unsigned char uch = ch;\n\tswitch (codePage) {\n\tcase cp932:\n\t\t// Shift_jis\n\t\treturn ((uch >= 0x81) && (uch <= 0x9F)) ||\n\t\t\t((uch >= 0xE0) && (uch <= 0xFC));\n\t\t// Lead bytes F0 to FC may be a Microsoft addition.\n\tcase cp936:\n\t\t// GBK\n\t\treturn (uch >= 0x81) && (uch <= 0xFE);\n\tcase cp949:\n\t\t// Korean Wansung KS C-5601-1987\n\t\treturn (uch >= 0x81) && (uch <= 0xFE);\n\tcase cp950:\n\t\t// Big5\n\t\treturn (uch >= 0x81) && (uch <= 0xFE);\n\tcase cp1361:\n\t\t// Korean Johab KS C-5601-1992\n\t\treturn\n\t\t\t((uch >= 0x84) && (uch <= 0xD3)) ||\n\t\t\t((uch >= 0xD8) && (uch <= 0xDE)) ||\n\t\t\t((uch >= 0xE0) && (uch <= 0xF9));\n\tdefault:\n\t\tbreak;\n\t}\n\treturn false;\n}\n\nbool DBCSIsTrailByte(int codePage, char ch) noexcept {\n\tconst unsigned char trail = ch;\n\tswitch (codePage) {\n\tcase cp932:\n\t\t// Shift_jis\n\t\treturn (trail != 0x7F) &&\n\t\t\t((trail >= 0x40) && (trail <= 0xFC));\n\tcase cp936:\n\t\t// GBK\n\t\treturn (trail != 0x7F) &&\n\t\t\t((trail >= 0x40) && (trail <= 0xFE));\n\tcase cp949:\n\t\t// Korean Wansung KS C-5601-1987\n\t\treturn\n\t\t\t((trail >= 0x41) && (trail <= 0x5A)) ||\n\t\t\t((trail >= 0x61) && (trail <= 0x7A)) ||\n\t\t\t((trail >= 0x81) && (trail <= 0xFE));\n\tcase cp950:\n\t\t// Big5\n\t\treturn\n\t\t\t((trail >= 0x40) && (trail <= 0x7E)) ||\n\t\t\t((trail >= 0xA1) && (trail <= 0xFE));\n\tcase cp1361:\n\t\t// Korean Johab KS C-5601-1992\n\t\treturn\n\t\t\t((trail >= 0x31) && (trail <= 0x7E)) ||\n\t\t\t((trail >= 0x81) && (trail <= 0xFE));\n\tdefault:\n\t\tbreak;\n\t}\n\treturn false;\n}\n\nbool IsDBCSValidSingleByte(int codePage, int ch) noexcept {\n\tswitch (codePage) {\n\tcase cp932:\n\t\treturn ch == 0x80\n\t\t\t|| (ch >= 0xA0 && ch <= 0xDF)\n\t\t\t|| (ch >= 0xFD);\n\n\tdefault:\n\t\treturn false;\n\t}\n}\n\n// NOLINTEND(*-magic-numbers)\n\nusing CodePageToFoldMap = std::map<int, FoldMap>;\nCodePageToFoldMap cpToFoldMap;\n\nbool DBCSHasFoldMap(int codePage) {\n\tconst CodePageToFoldMap::const_iterator it = cpToFoldMap.find(codePage);\n\treturn it != cpToFoldMap.end();\n}\n\nvoid DBCSSetFoldMap(int codePage, const FoldMap &foldMap) {\n\tcpToFoldMap[codePage] = foldMap;\n}\n\nFoldMap *DBCSGetMutableFoldMap(int codePage) {\n\t// Constructs if needed\n\treturn &cpToFoldMap[codePage];\n}\n\nconst FoldMap *DBCSGetFoldMap(int codePage) {\n\treturn &cpToFoldMap[codePage];\n}\n\n}\n"
  },
  {
    "path": "Libraries/scintilla/src/DBCS.h",
    "content": "// Scintilla source code edit control\n/** @file DBCS.h\n ** Functions to handle DBCS double byte encodings like Shift-JIS.\n **/\n// Copyright 2017 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#ifndef DBCS_H\n#define DBCS_H\n\nnamespace Scintilla::Internal {\n\nconstexpr int cp932 = 932;\nconstexpr int cp936 = 936;\nconstexpr int cp949 = 949;\nconstexpr int cp950 = 950;\nconstexpr int cp1361 = 1361;\n\nconstexpr bool IsDBCSCodePage(int codePage) noexcept {\n\treturn codePage == cp932\n\t       || codePage == cp936\n\t       || codePage == cp949\n\t       || codePage == cp950\n\t       || codePage == cp1361;\n}\n\nbool DBCSIsLeadByte(int codePage, char ch) noexcept;\nbool DBCSIsTrailByte(int codePage, char ch) noexcept;\nbool IsDBCSValidSingleByte(int codePage, int ch) noexcept;\n\n// Calculate a number from a DBCS byte pair that can be used to index into an array or map.\n// Should only be called with genuine DBCS character pairs which means that ch1 has top bit set.\nconstexpr uint16_t DBCSIndex(char ch1, char ch2) noexcept {\n\tconst unsigned char uch1 = ch1 & 0x7F;\n\tconst unsigned char uch2 = ch2;\n\treturn (uch1 << 8) | uch2;\n}\n\nstruct DBCSPair {\n\tchar chars[2];\n};\nusing FoldMap = std::array<DBCSPair, 0x8000>;\n\nbool DBCSHasFoldMap(int codePage);\nvoid DBCSSetFoldMap(int codePage, const FoldMap &foldMap);\nFoldMap *DBCSGetMutableFoldMap(int codePage);\nconst FoldMap *DBCSGetFoldMap(int codePage);\n\n}\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/src/Debugging.h",
    "content": "// Scintilla source code edit control\n/** @file Debugging.h\n ** Assert and debug trace functions.\n ** Implemented in each platform layer.\n **/\n// Copyright 1998-2009 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#ifndef DEBUGGING_H\n#define DEBUGGING_H\n\nnamespace Scintilla::Internal {\n\n#if defined(__clang__)\n# if __has_feature(attribute_analyzer_noreturn)\n#  define CLANG_ANALYZER_NORETURN __attribute__((analyzer_noreturn))\n# else\n#  define CLANG_ANALYZER_NORETURN\n# endif\n#else\n# define CLANG_ANALYZER_NORETURN\n#endif\n\n/**\n * Platform namespace used to segregate debugging functions.\n */\nnamespace Platform {\n\nvoid DebugDisplay(const char *s) noexcept;\nvoid DebugPrintf(const char *format, ...) noexcept;\nbool ShowAssertionPopUps(bool assertionPopUps_) noexcept;\nvoid Assert(const char *c, const char *file, int line) noexcept CLANG_ANALYZER_NORETURN;\n\n}\n\n#ifdef  NDEBUG\n#define PLATFORM_ASSERT(c) ((void)0)\n#else\n#define PLATFORM_ASSERT(c) ((c) ? (void)(0) : Scintilla::Internal::Platform::Assert(#c, __FILE__, __LINE__))\n#endif\n\n}\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/src/Decoration.cxx",
    "content": "/** @file Decoration.cxx\n ** Visual elements added over text.\n **/\n// Copyright 1998-2007 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#include <cstddef>\n#include <cstdlib>\n#include <cstdint>\n#include <cstring>\n#include <cstdio>\n#include <cstdarg>\n\n#include <stdexcept>\n#include <string_view>\n#include <vector>\n#include <optional>\n#include <algorithm>\n#include <memory>\n\n#include \"ScintillaTypes.h\"\n\n#include \"Debugging.h\"\n\n#include \"Position.h\"\n#include \"SplitVector.h\"\n#include \"Partitioning.h\"\n#include \"RunStyles.h\"\n#include \"Decoration.h\"\n\nusing namespace Scintilla::Internal;\n\nnamespace {\n\ntemplate <typename POS>\nclass Decoration : public IDecoration {\n\tint indicator;\n\n\t// pos_cast(): cast Sci::Position to either 32-bit or 64-bit value\n\t// This avoids warnings from Visual C++ Code Analysis and shortens code\n\tstatic constexpr POS pos_cast(Sci::Position pos) noexcept {\n\t\treturn static_cast<POS>(pos);\n\t}\n\npublic:\n\tRunStyles<POS, int> rs;\n\n\texplicit Decoration(int indicator_) : indicator(indicator_) {\n\t}\n\n\tbool Empty() const noexcept override {\n\t\treturn (rs.Runs() == 1) && (rs.AllSameAs(0));\n\t}\n\tint Indicator() const noexcept override {\n\t\treturn indicator;\n\t}\n\tSci::Position Length() const noexcept override {\n\t\treturn rs.Length();\n\t}\n\tint ValueAt(Sci::Position position) const noexcept override {\n\t\treturn rs.ValueAt(pos_cast(position));\n\t}\n\tSci::Position StartRun(Sci::Position position) const noexcept override {\n\t\treturn rs.StartRun(pos_cast(position));\n\t}\n\tSci::Position EndRun(Sci::Position position) const noexcept override {\n\t\treturn rs.EndRun(pos_cast(position));\n\t}\n\tvoid SetValueAt(Sci::Position position, int value) override {\n\t\trs.SetValueAt(pos_cast(position), value);\n\t}\n\tvoid InsertSpace(Sci::Position position, Sci::Position insertLength) override {\n\t\trs.InsertSpace(pos_cast(position), pos_cast(insertLength));\n\t}\n\tSci::Position Runs() const noexcept override {\n\t\treturn rs.Runs();\n\t}\n};\n\ntemplate <typename POS>\nclass DecorationList : public IDecorationList {\n\tint currentIndicator;\n\tint currentValue;\n\tDecoration<POS> *current;\t// Non-owning. Cached so FillRange doesn't have to search for each call.\n\tSci::Position lengthDocument;\n\t// Ordered by indicator\n\tstd::vector<std::unique_ptr<Decoration<POS>>> decorationList;\n\tstd::vector<const IDecoration*> decorationView;\t// Read-only view of decorationList\n\tbool clickNotified;\n\n\tDecoration<POS> *DecorationFromIndicator(int indicator) noexcept;\n\tDecoration<POS> *Create(int indicator, Sci::Position length);\n\tvoid Delete(int indicator);\n\tvoid DeleteAnyEmpty();\n\tvoid SetView();\n\n\t// pos_cast(): cast Sci::Position to either 32-bit or 64-bit value\n\t// This avoids warnings from Visual C++ Code Analysis and shortens code\n\tstatic constexpr POS pos_cast(Sci::Position pos) noexcept {\n\t\treturn static_cast<POS>(pos);\n\t}\n\npublic:\n\n\tDecorationList();\n\n\tconst std::vector<const IDecoration*> &View() const noexcept override {\n\t\treturn decorationView;\n\t}\n\n\tvoid SetCurrentIndicator(int indicator) override;\n\tint GetCurrentIndicator() const noexcept override { return currentIndicator; }\n\n\tvoid SetCurrentValue(int value) noexcept override;\n\tint GetCurrentValue() const noexcept override { return currentValue; }\n\n\t// Returns changed=true if some values may have changed\n\tFillResult<Sci::Position> FillRange(Sci::Position position, int value, Sci::Position fillLength) override;\n\n\tvoid InsertSpace(Sci::Position position, Sci::Position insertLength) override;\n\tvoid DeleteRange(Sci::Position position, Sci::Position deleteLength) override;\n\n\tvoid DeleteLexerDecorations() override;\n\n\tint AllOnFor(Sci::Position position) const noexcept override;\n\tint ValueAt(int indicator, Sci::Position position) noexcept override;\n\tSci::Position Start(int indicator, Sci::Position position) noexcept override;\n\tSci::Position End(int indicator, Sci::Position position) noexcept override;\n\n\tbool ClickNotified() const noexcept override {\n\t\treturn clickNotified;\n\t}\n\tvoid SetClickNotified(bool notified) noexcept override {\n\t\tclickNotified = notified;\n\t}\n};\n\ntemplate <typename POS>\nDecorationList<POS>::DecorationList() : currentIndicator(0), currentValue(1), current(nullptr),\n\tlengthDocument(0), clickNotified(false) {\n}\n\ntemplate <typename POS>\nDecoration<POS> *DecorationList<POS>::DecorationFromIndicator(int indicator) noexcept {\n\tfor (const std::unique_ptr<Decoration<POS>> &deco : decorationList) {\n\t\tif (deco->Indicator() == indicator) {\n\t\t\treturn deco.get();\n\t\t}\n\t}\n\treturn nullptr;\n}\n\ntemplate <typename POS>\nDecoration<POS> *DecorationList<POS>::Create(int indicator, Sci::Position length) {\n\tcurrentIndicator = indicator;\n\tstd::unique_ptr<Decoration<POS>> decoNew = std::make_unique<Decoration<POS>>(indicator);\n\tdecoNew->rs.InsertSpace(0, pos_cast(length));\n\n\ttypename std::vector<std::unique_ptr<Decoration<POS>>>::iterator it = std::lower_bound(\n\t\tdecorationList.begin(), decorationList.end(), decoNew,\n\t\t[](const std::unique_ptr<Decoration<POS>> &a, const std::unique_ptr<Decoration<POS>> &b) noexcept {\n\t\treturn a->Indicator() < b->Indicator();\n\t});\n\ttypename std::vector<std::unique_ptr<Decoration<POS>>>::iterator itAdded =\n\t\tdecorationList.insert(it, std::move(decoNew));\n\n\tSetView();\n\n\treturn itAdded->get();\n}\n\ntemplate <typename POS>\nvoid DecorationList<POS>::Delete(int indicator) {\n\tdecorationList.erase(std::remove_if(decorationList.begin(), decorationList.end(),\n\t\t[indicator](const std::unique_ptr<Decoration<POS>> &deco) noexcept {\n\t\treturn deco->Indicator() == indicator;\n\t}), decorationList.end());\n\tcurrent = nullptr;\n\tSetView();\n}\n\ntemplate <typename POS>\nvoid DecorationList<POS>::SetCurrentIndicator(int indicator) {\n\tcurrentIndicator = indicator;\n\tcurrent = DecorationFromIndicator(indicator);\n\tcurrentValue = 1;\n}\n\ntemplate <typename POS>\nvoid DecorationList<POS>::SetCurrentValue(int value) noexcept {\n\tcurrentValue = value ? value : 1;\n}\n\ntemplate <typename POS>\nFillResult<Sci::Position> DecorationList<POS>::FillRange(Sci::Position position, int value, Sci::Position fillLength) {\n\tif (!current) {\n\t\tcurrent = DecorationFromIndicator(currentIndicator);\n\t\tif (!current) {\n\t\t\tcurrent = Create(currentIndicator, lengthDocument);\n\t\t}\n\t}\n\t// Converting result from POS to Sci::Position as callers not polymorphic.\n\tconst FillResult<POS> frInPOS = current->rs.FillRange(pos_cast(position), value, pos_cast(fillLength));\n\tconst FillResult<Sci::Position> fr { frInPOS.changed, frInPOS.position, frInPOS.fillLength };\n\tif (current->Empty()) {\n\t\tDelete(currentIndicator);\n\t}\n\treturn fr;\n}\n\ntemplate <typename POS>\nvoid DecorationList<POS>::InsertSpace(Sci::Position position, Sci::Position insertLength) {\n\tconst bool atEnd = position == lengthDocument;\n\tlengthDocument += insertLength;\n\tfor (const std::unique_ptr<Decoration<POS>> &deco : decorationList) {\n\t\tdeco->rs.InsertSpace(pos_cast(position), pos_cast(insertLength));\n\t\tif (atEnd) {\n\t\t\tdeco->rs.FillRange(pos_cast(position), 0, pos_cast(insertLength));\n\t\t}\n\t}\n}\n\ntemplate <typename POS>\nvoid DecorationList<POS>::DeleteRange(Sci::Position position, Sci::Position deleteLength) {\n\tlengthDocument -= deleteLength;\n\tfor (const std::unique_ptr<Decoration<POS>> &deco : decorationList) {\n\t\tdeco->rs.DeleteRange(pos_cast(position), pos_cast(deleteLength));\n\t}\n\tDeleteAnyEmpty();\n\tif (decorationList.size() != decorationView.size()) {\n\t\t// One or more empty decorations deleted so update view.\n\t\tcurrent = nullptr;\n\t\tSetView();\n\t}\n}\n\ntemplate <typename POS>\nvoid DecorationList<POS>::DeleteLexerDecorations() {\n\tdecorationList.erase(std::remove_if(decorationList.begin(), decorationList.end(),\n\t\t[](const std::unique_ptr<Decoration<POS>> &deco) noexcept {\n\t\treturn deco->Indicator() < static_cast<int>(Scintilla::IndicatorNumbers::Container);\n\t}), decorationList.end());\n\tcurrent = nullptr;\n\tSetView();\n}\n\ntemplate <typename POS>\nvoid DecorationList<POS>::DeleteAnyEmpty() {\n\tif (lengthDocument == 0) {\n\t\tdecorationList.clear();\n\t} else {\n\t\tdecorationList.erase(std::remove_if(decorationList.begin(), decorationList.end(),\n\t\t\t[](const std::unique_ptr<Decoration<POS>> &deco) noexcept {\n\t\t\treturn deco->Empty();\n\t\t}), decorationList.end());\n\t}\n}\n\ntemplate <typename POS>\nvoid DecorationList<POS>::SetView() {\n\tdecorationView.clear();\n\tfor (const std::unique_ptr<Decoration<POS>> &deco : decorationList) {\n\t\tdecorationView.push_back(deco.get());\n\t}\n}\n\ntemplate <typename POS>\nint DecorationList<POS>::AllOnFor(Sci::Position position) const noexcept {\n\tint mask = 0;\n\tfor (const std::unique_ptr<Decoration<POS>> &deco : decorationList) {\n\t\tif (deco->rs.ValueAt(pos_cast(position))) {\n\t\t\tif (deco->Indicator() < static_cast<int>(Scintilla::IndicatorNumbers::Ime)) {\n\t\t\t\tmask |= 1u << deco->Indicator();\n\t\t\t}\n\t\t}\n\t}\n\treturn mask;\n}\n\ntemplate <typename POS>\nint DecorationList<POS>::ValueAt(int indicator, Sci::Position position) noexcept {\n\tconst Decoration<POS> *deco = DecorationFromIndicator(indicator);\n\tif (deco) {\n\t\treturn deco->rs.ValueAt(pos_cast(position));\n\t}\n\treturn 0;\n}\n\ntemplate <typename POS>\nSci::Position DecorationList<POS>::Start(int indicator, Sci::Position position) noexcept {\n\tconst Decoration<POS> *deco = DecorationFromIndicator(indicator);\n\tif (deco) {\n\t\treturn deco->rs.StartRun(pos_cast(position));\n\t}\n\treturn 0;\n}\n\ntemplate <typename POS>\nSci::Position DecorationList<POS>::End(int indicator, Sci::Position position) noexcept {\n\tconst Decoration<POS> *deco = DecorationFromIndicator(indicator);\n\tif (deco) {\n\t\treturn deco->rs.EndRun(pos_cast(position));\n\t}\n\treturn 0;\n}\n\n}\n\nnamespace Scintilla::Internal {\n\nstd::unique_ptr<IDecoration> DecorationCreate(bool largeDocument, int indicator) {\n\tif (largeDocument)\n\t\treturn std::make_unique<Decoration<Sci::Position>>(indicator);\n\telse\n\t\treturn std::make_unique<Decoration<int>>(indicator);\n}\n\nstd::unique_ptr<IDecorationList> DecorationListCreate(bool largeDocument) {\n\tif (largeDocument)\n\t\treturn std::make_unique<DecorationList<Sci::Position>>();\n\telse\n\t\treturn std::make_unique<DecorationList<int>>();\n}\n\n}\n\n"
  },
  {
    "path": "Libraries/scintilla/src/Decoration.h",
    "content": "/** @file Decoration.h\n ** Visual elements added over text.\n **/\n// Copyright 1998-2007 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#ifndef DECORATION_H\n#define DECORATION_H\n\nnamespace Scintilla::Internal {\n\nclass IDecoration {\npublic:\n\tvirtual ~IDecoration() {}\n\tvirtual bool Empty() const noexcept = 0;\n\tvirtual int Indicator() const noexcept = 0;\n\tvirtual Sci::Position Length() const noexcept = 0;\n\tvirtual int ValueAt(Sci::Position position) const noexcept = 0;\n\tvirtual Sci::Position StartRun(Sci::Position position) const noexcept = 0;\n\tvirtual Sci::Position EndRun(Sci::Position position) const noexcept = 0;\n\tvirtual void SetValueAt(Sci::Position position, int value) = 0;\n\tvirtual void InsertSpace(Sci::Position position, Sci::Position insertLength) = 0;\n\tvirtual Sci::Position Runs() const noexcept = 0;\n};\n\nclass IDecorationList {\npublic:\n\tvirtual ~IDecorationList() {}\n\n\tvirtual const std::vector<const IDecoration*> &View() const noexcept = 0;\n\n\tvirtual void SetCurrentIndicator(int indicator) = 0;\n\tvirtual int GetCurrentIndicator() const noexcept = 0;\n\n\tvirtual void SetCurrentValue(int value) noexcept = 0;\n\tvirtual int GetCurrentValue() const noexcept = 0;\n\n\t// Returns with changed=true if some values may have changed\n\tvirtual FillResult<Sci::Position> FillRange(Sci::Position position, int value, Sci::Position fillLength) = 0;\n\tvirtual void InsertSpace(Sci::Position position, Sci::Position insertLength) = 0;\n\tvirtual void DeleteRange(Sci::Position position, Sci::Position deleteLength) = 0;\n\tvirtual void DeleteLexerDecorations() = 0;\n\n\tvirtual int AllOnFor(Sci::Position position) const noexcept = 0;\n\tvirtual int ValueAt(int indicator, Sci::Position position) noexcept = 0;\n\tvirtual Sci::Position Start(int indicator, Sci::Position position) noexcept = 0;\n\tvirtual Sci::Position End(int indicator, Sci::Position position) noexcept = 0;\n\n\tvirtual bool ClickNotified() const noexcept = 0;\n\tvirtual void SetClickNotified(bool notified) noexcept = 0;\n};\n\nstd::unique_ptr<IDecoration> DecorationCreate(bool largeDocument, int indicator);\n\nstd::unique_ptr<IDecorationList> DecorationListCreate(bool largeDocument);\n\n}\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/src/Document.cxx",
    "content": "// Scintilla source code edit control\n/** @file Document.cxx\n ** Text document that handles notifications, DBCS, styling, words and end of line.\n **/\n// Copyright 1998-2011 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#include <cstddef>\n#include <cstdlib>\n#include <cstdint>\n#include <cassert>\n#include <cstring>\n#include <cstdio>\n#include <cmath>\n\n#include <stdexcept>\n#include <string>\n#include <string_view>\n#include <vector>\n#include <array>\n#include <map>\n#include <forward_list>\n#include <optional>\n#include <algorithm>\n#include <memory>\n#include <chrono>\n\n#ifndef NO_CXX11_REGEX\n#include <regex>\n#endif\n\n#include \"ScintillaTypes.h\"\n#include \"ILoader.h\"\n#include \"ILexer.h\"\n\n#include \"Debugging.h\"\n\n#include \"CharacterType.h\"\n#include \"CharacterCategoryMap.h\"\n#include \"Position.h\"\n#include \"SplitVector.h\"\n#include \"Partitioning.h\"\n#include \"RunStyles.h\"\n#include \"CellBuffer.h\"\n#include \"PerLine.h\"\n#include \"CharClassify.h\"\n#include \"Decoration.h\"\n#include \"CaseFolder.h\"\n#include \"Document.h\"\n#include \"RESearch.h\"\n#include \"UniConversion.h\"\n#include \"ElapsedPeriod.h\"\n\nusing namespace Scintilla;\nusing namespace Scintilla::Internal;\n\n#if defined(__GNUC__) && !defined(__clang__)\n// False warnings from g++ 14.1 for UTF-8 accumulation code where UTF8MaxBytes allocated.\n#pragma GCC diagnostic ignored \"-Wstringop-overflow\"\n#endif\n\nLexInterface::LexInterface(Document *pdoc_) noexcept : pdoc(pdoc_), performingStyle(false) {\n}\n\nLexInterface::~LexInterface() noexcept = default;\n\nvoid LexInterface::SetInstance(ILexer5 *instance_) noexcept {\n\tinstance.reset(instance_);\n}\n\nvoid LexInterface::Colourise(Sci::Position start, Sci::Position end) {\n\tif (pdoc && instance && !performingStyle) {\n\t\t// Protect against reentrance, which may occur, for example, when\n\t\t// fold points are discovered while performing styling and the folding\n\t\t// code looks for child lines which may trigger styling.\n\t\tperformingStyle = true;\n\n\t\tconst Sci::Position lengthDoc = pdoc->Length();\n\t\tif (end == -1)\n\t\t\tend = lengthDoc;\n\t\tconst Sci::Position len = end - start;\n\n\t\tPLATFORM_ASSERT(len >= 0);\n\t\tPLATFORM_ASSERT(start + len <= lengthDoc);\n\n\t\tint styleStart = 0;\n\t\tif (start > 0)\n\t\t\tstyleStart = pdoc->StyleAt(start - 1);\n\n\t\tif (len > 0) {\n\t\t\tinstance->Lex(start, len, styleStart, pdoc);\n\t\t\tinstance->Fold(start, len, styleStart, pdoc);\n\t\t}\n\n\t\tperformingStyle = false;\n\t}\n}\n\nLineEndType LexInterface::LineEndTypesSupported() {\n\tif (instance) {\n\t\treturn static_cast<LineEndType>(instance->LineEndTypesSupported());\n\t}\n\treturn LineEndType::Default;\n}\n\nbool LexInterface::UseContainerLexing() const noexcept {\n\treturn !instance;\n}\n\nActionDuration::ActionDuration(double duration_, double minDuration_, double maxDuration_) noexcept :\n\tduration(duration_), minDuration(minDuration_), maxDuration(maxDuration_) {\n}\n\nvoid ActionDuration::AddSample(size_t numberActions, double durationOfActions) noexcept {\n\t// Only adjust for multiple actions to avoid instability\n\tif (numberActions < 8)\n\t\treturn;\n\n\t// Alpha value for exponential smoothing.\n\t// Most recent value contributes 25% to smoothed value.\n\tconstexpr double alpha = 0.25;\n\n\tconst double durationOne = durationOfActions / static_cast<double>(numberActions);\n\tduration = std::clamp(alpha * durationOne + (1.0 - alpha) * duration,\n\t\tminDuration, maxDuration);\n}\n\ndouble ActionDuration::Duration() const noexcept {\n\treturn duration;\n}\n\nsize_t ActionDuration::ActionsInAllowedTime(double secondsAllowed) const noexcept {\n\treturn std::lround(secondsAllowed / Duration());\n}\n\nconst CharacterExtracted characterEmpty(unicodeReplacementChar, 0);\nconst CharacterExtracted characterBadByte(unicodeReplacementChar, 1);\n\nCharacterExtracted::CharacterExtracted(const unsigned char *charBytes, size_t widthCharBytes) noexcept {\n\tconst int utf8status = UTF8Classify(charBytes, widthCharBytes);\n\tif (utf8status & UTF8MaskInvalid) {\n\t\t// Treat as invalid and use up just one byte\n\t\tcharacter = unicodeReplacementChar;\n\t\twidthBytes = 1;\n\t} else {\n\t\tcharacter = UnicodeFromUTF8(charBytes);\n\t\twidthBytes = utf8status & UTF8MaskWidth;\n\t}\n}\n\nDocument::Document(DocumentOption options) :\n\trefCount(0),\n\tcb(!FlagSet(options, DocumentOption::StylesNone), FlagSet(options, DocumentOption::TextLarge)),\n\tendStyled(0),\n\tstyleClock(0),\n\tenteredModification(0),\n\tenteredStyling(0),\n\tenteredReadOnlyCount(0),\n\tinsertionSet(false),\n#ifdef _WIN32\n\teolMode(EndOfLine::CrLf),\n#else\n\teolMode(EndOfLine::Lf),\n#endif\n\tdbcsCodePage(CpUtf8),\n\tlineEndBitSet(LineEndType::Default),\n\ttabInChars(8),\n\tindentInChars(0),\n\tactualIndentInChars(8),\n\tuseTabs(true),\n\ttabIndents(true),\n\tbackspaceUnindents(false),\n\tdurationStyleOneByte(0.000001, 0.0000001, 0.00001) {\n\n\tperLineData[ldMarkers] = std::make_unique<LineMarkers>();\n\tperLineData[ldLevels] = std::make_unique<LineLevels>();\n\tperLineData[ldState] = std::make_unique<LineState>();\n\tperLineData[ldMargin] = std::make_unique<LineAnnotation>();\n\tperLineData[ldAnnotation] = std::make_unique<LineAnnotation>();\n\tperLineData[ldEOLAnnotation] = std::make_unique<LineAnnotation>();\n\n\tdecorations = DecorationListCreate(IsLarge());\n\n\tcb.SetPerLine(this);\n\tcb.SetUTF8Substance(CpUtf8 == dbcsCodePage);\n}\n\nDocument::~Document() {\n\tfor (const WatcherWithUserData &watcher : watchers) {\n\t\twatcher.watcher->NotifyDeleted(this, watcher.userData);\n\t}\n}\n\n// Increase reference count and return its previous value.\nint SCI_METHOD Document::AddRef() noexcept {\n\treturn refCount++;\n}\n\n// Decrease reference count and return its previous value.\n// Delete the document if reference count reaches zero.\nint SCI_METHOD Document::Release() {\n\tconst int curRefCount = --refCount;\n\tif (curRefCount == 0)\n\t\tdelete this;\n\treturn curRefCount;\n}\n\nvoid Document::Init() {\n\tfor (const std::unique_ptr<PerLine> &pl : perLineData) {\n\t\tif (pl)\n\t\t\tpl->Init();\n\t}\n}\n\nvoid Document::InsertLine(Sci::Line line) {\n\tfor (const std::unique_ptr<PerLine> &pl : perLineData) {\n\t\tif (pl)\n\t\t\tpl->InsertLine(line);\n\t}\n}\n\nvoid Document::InsertLines(Sci::Line line, Sci::Line lines) {\n\tfor (const auto &pl : perLineData) {\n\t\tif (pl)\n\t\t\tpl->InsertLines(line, lines);\n\t}\n}\n\nvoid Document::RemoveLine(Sci::Line line) {\n\tfor (const std::unique_ptr<PerLine> &pl : perLineData) {\n\t\tif (pl)\n\t\t\tpl->RemoveLine(line);\n\t}\n}\n\nLineMarkers *Document::Markers() const noexcept {\n\treturn static_cast<LineMarkers *>(perLineData[ldMarkers].get());\n}\n\nLineLevels *Document::Levels() const noexcept {\n\treturn static_cast<LineLevels *>(perLineData[ldLevels].get());\n}\n\nLineState *Document::States() const noexcept {\n\treturn static_cast<LineState *>(perLineData[ldState].get());\n}\n\nLineAnnotation *Document::Margins() const noexcept {\n\treturn static_cast<LineAnnotation *>(perLineData[ldMargin].get());\n}\n\nLineAnnotation *Document::Annotations() const noexcept {\n\treturn static_cast<LineAnnotation *>(perLineData[ldAnnotation].get());\n}\n\nLineAnnotation *Document::EOLAnnotations() const noexcept {\n\treturn static_cast<LineAnnotation *>(perLineData[ldEOLAnnotation].get());\n}\n\nLineEndType Document::LineEndTypesSupported() const {\n\tif ((CpUtf8 == dbcsCodePage) && pli)\n\t\treturn pli->LineEndTypesSupported();\n\treturn LineEndType::Default;\n}\n\nbool Document::SetDBCSCodePage(int dbcsCodePage_) {\n\tif (dbcsCodePage != dbcsCodePage_) {\n\t\tdbcsCodePage = dbcsCodePage_;\n\t\tSetCaseFolder(nullptr);\n\t\tcb.SetLineEndTypes(lineEndBitSet & LineEndTypesSupported());\n\t\tcb.SetUTF8Substance(CpUtf8 == dbcsCodePage);\n\t\tModifiedAt(0);\t// Need to restyle whole document\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nbool Document::SetLineEndTypesAllowed(LineEndType lineEndBitSet_) {\n\tif (lineEndBitSet != lineEndBitSet_) {\n\t\tlineEndBitSet = lineEndBitSet_;\n\t\tconst LineEndType lineEndBitSetActive = lineEndBitSet & LineEndTypesSupported();\n\t\tif (lineEndBitSetActive != cb.GetLineEndTypes()) {\n\t\t\tModifiedAt(0);\n\t\t\tcb.SetLineEndTypes(lineEndBitSetActive);\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\nvoid Document::SetSavePoint() {\n\tcb.SetSavePoint();\n\tNotifySavePoint(true);\n}\n\nvoid Document::TentativeUndo() {\n\tif (!TentativeActive())\n\t\treturn;\n\tCheckReadOnly();\n\tif (enteredModification == 0) {\n\t\tenteredModification++;\n\t\tif (!cb.IsReadOnly()) {\n\t\t\tconst bool startSavePoint = cb.IsSavePoint();\n\t\t\tbool multiLine = false;\n\t\t\tconst int steps = cb.TentativeSteps();\n\t\t\t//Platform::DebugPrintf(\"Steps=%d\\n\", steps);\n\t\t\tfor (int step = 0; step < steps; step++) {\n\t\t\t\tconst Sci::Line prevLinesTotal = LinesTotal();\n\t\t\t\tconst Action action = cb.GetUndoStep();\n\t\t\t\tif (action.at == ActionType::remove) {\n\t\t\t\t\tNotifyModified(DocModification(\n\t\t\t\t\t\t\t\t\tModificationFlags::BeforeInsert | ModificationFlags::Undo, action));\n\t\t\t\t} else if (action.at == ActionType::container) {\n\t\t\t\t\tDocModification dm(ModificationFlags::Container | ModificationFlags::Undo);\n\t\t\t\t\tdm.token = action.position;\n\t\t\t\t\tNotifyModified(dm);\n\t\t\t\t} else {\n\t\t\t\t\tNotifyModified(DocModification(\n\t\t\t\t\t\t\t\t\tModificationFlags::BeforeDelete | ModificationFlags::Undo, action));\n\t\t\t\t}\n\t\t\t\tcb.PerformUndoStep();\n\t\t\t\tif (action.at != ActionType::container) {\n\t\t\t\t\tModifiedAt(action.position);\n\t\t\t\t}\n\n\t\t\t\tModificationFlags modFlags = ModificationFlags::Undo;\n\t\t\t\t// With undo, an insertion action becomes a deletion notification\n\t\t\t\tif (action.at == ActionType::remove) {\n\t\t\t\t\tmodFlags |= ModificationFlags::InsertText;\n\t\t\t\t} else if (action.at == ActionType::insert) {\n\t\t\t\t\tmodFlags |= ModificationFlags::DeleteText;\n\t\t\t\t}\n\t\t\t\tif (steps > 1)\n\t\t\t\t\tmodFlags |= ModificationFlags::MultiStepUndoRedo;\n\t\t\t\tconst Sci::Line linesAdded = LinesTotal() - prevLinesTotal;\n\t\t\t\tif (linesAdded != 0)\n\t\t\t\t\tmultiLine = true;\n\t\t\t\tif (step == steps - 1) {\n\t\t\t\t\tmodFlags |= ModificationFlags::LastStepInUndoRedo;\n\t\t\t\t\tif (multiLine)\n\t\t\t\t\t\tmodFlags |= ModificationFlags::MultilineUndoRedo;\n\t\t\t\t}\n\t\t\t\tNotifyModified(DocModification(modFlags, action.position, action.lenData,\n\t\t\t\t\t\t\t\t\t\t\t   linesAdded, action.data));\n\t\t\t}\n\n\t\t\tconst bool endSavePoint = cb.IsSavePoint();\n\t\t\tif (startSavePoint != endSavePoint)\n\t\t\t\tNotifySavePoint(endSavePoint);\n\n\t\t\tcb.TentativeCommit();\n\t\t}\n\t\tenteredModification--;\n\t}\n}\n\nint Document::UndoActions() const noexcept {\n\treturn cb.UndoActions();\n}\n\nvoid Document::SetUndoSavePoint(int action) noexcept {\n\tcb.SetUndoSavePoint(action);\n}\n\nint Document::UndoSavePoint() const noexcept {\n\treturn cb.UndoSavePoint();\n}\n\nvoid Document::SetUndoDetach(int action) noexcept {\n\tcb.SetUndoDetach(action);\n}\n\nint Document::UndoDetach() const noexcept {\n\treturn cb.UndoDetach();\n}\n\nvoid Document::SetUndoTentative(int action) noexcept {\n\tcb.SetUndoTentative(action);\n}\n\nint Document::UndoTentative() const noexcept {\n\treturn cb.UndoTentative();\n}\n\nvoid Document::SetUndoCurrent(int action) {\n\tcb.SetUndoCurrent(action);\n}\n\nint Document::UndoCurrent() const noexcept {\n\treturn cb.UndoCurrent();\n}\n\nint Document::UndoActionType(int action) const noexcept {\n\treturn cb.UndoActionType(action);\n}\n\nSci::Position Document::UndoActionPosition(int action) const noexcept {\n\treturn cb.UndoActionPosition(action);\n}\n\nstd::string_view Document::UndoActionText(int action) const noexcept {\n\treturn cb.UndoActionText(action);\n}\n\nvoid Document::PushUndoActionType(int type, Sci::Position position) {\n\tcb.PushUndoActionType(type, position);\n}\n\nvoid Document::ChangeLastUndoActionText(size_t length, const char *text) {\n\tcb.ChangeLastUndoActionText(length, text);\n}\n\nint Document::GetMark(Sci::Line line, bool includeChangeHistory) const {\n\tint marksHistory = 0;\n\tif (includeChangeHistory && (line < LinesTotal())) {\n\t\tint marksEdition = 0;\n\n\t\tconst Sci::Position start = LineStart(line);\n\t\tconst Sci::Position lineNext = LineStart(line + 1);\n\t\tfor (Sci::Position position = start; position < lineNext;) {\n\t\t\tconst int edition = EditionAt(position);\n\t\t\tif (edition) {\n\t\t\t\tmarksEdition |= 1 << (edition-1);\n\t\t\t}\n\t\t\tposition = EditionEndRun(position);\n\t\t}\n\t\tconst Sci::Position lineEnd = LineEnd(line);\n\t\tfor (Sci::Position position = start; position <= lineEnd;) {\n\t\t\tmarksEdition |= EditionDeletesAt(position);\n\t\t\tposition = EditionNextDelete(position);\n\t\t}\n\n\t\t/* Bits: RevertedToOrigin, Saved, Modified, RevertedToModified */\n\t\tconstexpr unsigned int editionShift = static_cast<unsigned int>(MarkerOutline::HistoryRevertedToOrigin);\n\t\tmarksHistory = marksEdition << editionShift;\n\t}\n\n\treturn marksHistory | Markers()->MarkValue(line);\n}\n\nSci::Line Document::MarkerNext(Sci::Line lineStart, int mask) const noexcept {\n\treturn Markers()->MarkerNext(lineStart, mask);\n}\n\nint Document::AddMark(Sci::Line line, int markerNum) {\n\tif (line >= 0 && line < LinesTotal()) {\n\t\tconst int prev = Markers()->AddMark(line, markerNum, LinesTotal());\n\t\tconst DocModification mh(ModificationFlags::ChangeMarker, LineStart(line), 0, 0, nullptr, line);\n\t\tNotifyModified(mh);\n\t\treturn prev;\n\t}\n\treturn -1;\n}\n\nvoid Document::AddMarkSet(Sci::Line line, int valueSet) {\n\tif (line < 0 || line >= LinesTotal()) {\n\t\treturn;\n\t}\n\tunsigned int m = valueSet;\n\tfor (int i = 0; m; i++, m >>= 1) {\n\t\tif (m & 1)\n\t\t\tMarkers()->AddMark(line, i, LinesTotal());\n\t}\n\tconst DocModification mh(ModificationFlags::ChangeMarker, LineStart(line), 0, 0, nullptr, line);\n\tNotifyModified(mh);\n}\n\nvoid Document::DeleteMark(Sci::Line line, int markerNum) {\n\tMarkers()->DeleteMark(line, markerNum, false);\n\tconst DocModification mh(ModificationFlags::ChangeMarker, LineStart(line), 0, 0, nullptr, line);\n\tNotifyModified(mh);\n}\n\nvoid Document::DeleteMarkFromHandle(int markerHandle) {\n\tMarkers()->DeleteMarkFromHandle(markerHandle);\n\tDocModification mh(ModificationFlags::ChangeMarker);\n\tmh.line = -1;\n\tNotifyModified(mh);\n}\n\nvoid Document::DeleteAllMarks(int markerNum) {\n\tbool someChanges = false;\n\tfor (Sci::Line line = 0; line < LinesTotal(); line++) {\n\t\tif (Markers()->DeleteMark(line, markerNum, true))\n\t\t\tsomeChanges = true;\n\t}\n\tif (someChanges) {\n\t\tDocModification mh(ModificationFlags::ChangeMarker);\n\t\tmh.line = -1;\n\t\tNotifyModified(mh);\n\t}\n}\n\nSci::Line Document::LineFromHandle(int markerHandle) const noexcept {\n\treturn Markers()->LineFromHandle(markerHandle);\n}\n\nint Document::MarkerNumberFromLine(Sci::Line line, int which) const noexcept {\n\treturn Markers()->NumberFromLine(line, which);\n}\n\nint Document::MarkerHandleFromLine(Sci::Line line, int which) const noexcept {\n\treturn Markers()->HandleFromLine(line, which);\n}\n\nSci_Position SCI_METHOD Document::LineStart(Sci_Position line) const {\n\treturn cb.LineStart(line);\n}\n\nRange Document::LineRange(Sci::Line line) const noexcept {\n\treturn {cb.LineStart(line), cb.LineStart(line + 1)};\n}\n\nbool Document::IsLineStartPosition(Sci::Position position) const noexcept {\n\treturn LineStartPosition(position) == position;\n}\n\nSci_Position SCI_METHOD Document::LineEnd(Sci_Position line) const {\n\treturn cb.LineEnd(line);\n}\n\nint SCI_METHOD Document::DEVersion() const noexcept {\n\treturn deRelease0;\n}\n\nvoid SCI_METHOD Document::SetErrorStatus(int status) {\n\t// Tell the watchers an error has occurred.\n\tfor (const WatcherWithUserData &watcher : watchers) {\n\t\twatcher.watcher->NotifyErrorOccurred(this, watcher.userData, static_cast<Status>(status));\n\t}\n}\n\nSci_Position SCI_METHOD Document::LineFromPosition(Sci_Position pos) const {\n\treturn cb.LineFromPosition(pos);\n}\n\nSci::Line Document::SciLineFromPosition(Sci::Position pos) const noexcept {\n\t// Avoids casting in callers for this very common function\n\treturn cb.LineFromPosition(pos);\n}\n\nSci::Position Document::LineStartPosition(Sci::Position position) const noexcept {\n\treturn cb.LineStart(cb.LineFromPosition(position));\n}\n\nSci::Position Document::LineEndPosition(Sci::Position position) const noexcept {\n\treturn cb.LineEnd(cb.LineFromPosition(position));\n}\n\nbool Document::IsLineEndPosition(Sci::Position position) const noexcept {\n\treturn LineEndPosition(position) == position;\n}\n\nbool Document::IsPositionInLineEnd(Sci::Position position) const noexcept {\n\treturn position >= LineEndPosition(position);\n}\n\nSci::Position Document::VCHomePosition(Sci::Position position) const {\n\tconst Sci::Line line = SciLineFromPosition(position);\n\tconst Sci::Position startPosition = LineStart(line);\n\tconst Sci::Position endLine = LineEnd(line);\n\tSci::Position startText = startPosition;\n\twhile (startText < endLine && IsSpaceOrTab(cb.CharAt(startText)))\n\t\tstartText++;\n\tif (position == startText)\n\t\treturn startPosition;\n\telse\n\t\treturn startText;\n}\n\nSci::Position Document::IndexLineStart(Sci::Line line, LineCharacterIndexType lineCharacterIndex) const noexcept {\n\treturn cb.IndexLineStart(line, lineCharacterIndex);\n}\n\nSci::Line Document::LineFromPositionIndex(Sci::Position pos, LineCharacterIndexType lineCharacterIndex) const noexcept {\n\treturn cb.LineFromPositionIndex(pos, lineCharacterIndex);\n}\n\nSci::Line Document::LineFromPositionAfter(Sci::Line line, Sci::Position length) const noexcept {\n\tconst Sci::Position posAfter = cb.LineStart(line) + length;\n\tif (posAfter >= LengthNoExcept()) {\n\t\treturn LinesTotal();\n\t}\n\tconst Sci::Line lineAfter = SciLineFromPosition(posAfter);\n\tif (lineAfter > line) {\n\t\treturn lineAfter;\n\t} else {\n\t\t// Want to make some progress so return next line\n\t\treturn lineAfter + 1;\n\t}\n}\n\nint SCI_METHOD Document::SetLevel(Sci_Position line, int level) {\n\tconst int prev = Levels()->SetLevel(line, level, LinesTotal());\n\tif (prev != level) {\n\t\tDocModification mh(ModificationFlags::ChangeFold | ModificationFlags::ChangeMarker,\n\t\t                   LineStart(line), 0, 0, nullptr, line);\n\t\tmh.foldLevelNow = static_cast<FoldLevel>(level);\n\t\tmh.foldLevelPrev = static_cast<FoldLevel>(prev);\n\t\tNotifyModified(mh);\n\t}\n\treturn prev;\n}\n\n//Au\nbool SCI_METHOD Document::Sci_SetFoldLevels(int line, int lineTo, int len, int* a) {\n\tbool R = false;\n\t//code like in LexCPP.cxx function Fold().\n\tauto levels = Levels();\n\tint levelCurrent = line == 0 ? (int)FoldLevel::Base : levels->GetLevel((Sci::Line)line - 1) >> 16;\n\tint levelNext = levelCurrent;\n\tint nLines = (int)LinesTotal();\n\tif(lineTo < 0) lineTo = nLines;\n\tfor(int i = 0; line < lineTo; line++) {\n\t\tint eol = (int)this->LineStart((Sci::Line)line + 1);\n\t\tif(a != nullptr) for(; i < len && (a[i] & 0x7fffffff) < eol; i++) levelNext += (a[i] & 0x80000000) ? -1 : 1;\n\t\tif(levelNext < (int)FoldLevel::Base) levelNext = (int)FoldLevel::Base;\n\t\tint lev = levelCurrent | levelNext << 16;\n\t\tif(levelNext > levelCurrent) lev |= (int)FoldLevel::HeaderFlag;\n\t\t//Print(line+1, (uint)lev, (uint)p->GetLevel(line));\n\t\tauto prevLevel = levels->GetLevel(line);\n\t\tif(lev != prevLevel) {\n\t\t\t//Platform::DebugPrintf(\"0x%x  0x%x\", (int)prevLevel, (int)lev);\n\t\t\tif(prevLevel <= (int)FoldLevel::Base)levels->SetLevel(line, lev, nLines); //first folding. Slightly faster, but does not show hidden lines when header unset, etc.\n\t\t\telse SetLevel(line, lev);\n\t\t\tR = true;\n\t\t}\n\t\tlevelCurrent = levelNext;\n\t}\n\treturn R;\n}\n\nint SCI_METHOD Document::GetLevel(Sci_Position line) const {\n\treturn Levels()->GetLevel(line);\n}\n\nFoldLevel Document::GetFoldLevel(Sci_Position line) const noexcept {\n\treturn Levels()->GetFoldLevel(line);\n}\n\nvoid Document::ClearLevels() {\n\tLevels()->ClearLevels();\n}\n\nnamespace {\n\nconstexpr bool IsSubordinate(FoldLevel levelStart, FoldLevel levelTry) noexcept {\n\tif (LevelIsWhitespace(levelTry))\n\t\treturn true;\n\treturn LevelNumber(levelStart) < LevelNumber(levelTry);\n}\n\n}\n\nSci::Line Document::GetLastChild(Sci::Line lineParent, std::optional<FoldLevel> level, Sci::Line lastLine) {\n\tconst FoldLevel levelStart = LevelNumberPart(level ? *level : GetFoldLevel(lineParent));\n\tconst Sci::Line maxLine = LinesTotal() - 1;\n\tconst Sci::Line lookLastLine = (lastLine != -1) ? std::min(maxLine, lastLine) : maxLine;\n\tSci::Line lineMaxSubord = lineParent;\n\twhile (lineMaxSubord < maxLine) {\n\t\tEnsureStyledTo(LineStart(lineMaxSubord + 2));\n\t\tif (!IsSubordinate(levelStart, GetFoldLevel(lineMaxSubord + 1)))\n\t\t\tbreak;\n\t\tif ((lineMaxSubord >= lookLastLine) && !LevelIsWhitespace(GetFoldLevel(lineMaxSubord)))\n\t\t\tbreak;\n\t\tlineMaxSubord++;\n\t}\n\tif (lineMaxSubord > lineParent) {\n\t\tif (levelStart > LevelNumberPart(GetFoldLevel(lineMaxSubord + 1))) {\n\t\t\t// Have chewed up some whitespace that belongs to a parent so seek back\n\t\t\tif (LevelIsWhitespace(GetFoldLevel(lineMaxSubord))) {\n\t\t\t\tlineMaxSubord--;\n\t\t\t}\n\t\t}\n\t}\n\treturn lineMaxSubord;\n}\n\nSci::Line Document::GetFoldParent(Sci::Line line) const noexcept {\n\treturn Levels()->GetFoldParent(line);\n}\n\nvoid Document::GetHighlightDelimiters(HighlightDelimiter &highlightDelimiter, Sci::Line line, Sci::Line lastLine) {\n\tconst FoldLevel level = GetFoldLevel(line);\n\tconst Sci::Line lookLastLine = std::max(line, lastLine) + 1;\n\n\tSci::Line lookLine = line;\n\tFoldLevel lookLineLevel = level;\n\tFoldLevel lookLineLevelNum = LevelNumberPart(lookLineLevel);\n\twhile ((lookLine > 0) && (LevelIsWhitespace(lookLineLevel) ||\n\t\t(LevelIsHeader(lookLineLevel) && (lookLineLevelNum >= LevelNumberPart(GetFoldLevel(lookLine + 1)))))) {\n\t\tlookLineLevel = GetFoldLevel(--lookLine);\n\t\tlookLineLevelNum = LevelNumberPart(lookLineLevel);\n\t}\n\n\tSci::Line beginFoldBlock = LevelIsHeader(lookLineLevel) ? lookLine : GetFoldParent(lookLine);\n\tif (beginFoldBlock == -1) {\n\t\thighlightDelimiter.Clear();\n\t\treturn;\n\t}\n\n\tSci::Line endFoldBlock = GetLastChild(beginFoldBlock, {}, lookLastLine);\n\tSci::Line firstChangeableLineBefore = -1;\n\tif (endFoldBlock < line) {\n\t\tlookLine = beginFoldBlock - 1;\n\t\tlookLineLevel = GetFoldLevel(lookLine);\n\t\tlookLineLevelNum = LevelNumberPart(lookLineLevel);\n\t\twhile ((lookLine >= 0) && (lookLineLevelNum >= FoldLevel::Base)) {\n\t\t\tif (LevelIsHeader(lookLineLevel)) {\n\t\t\t\tif (GetLastChild(lookLine, {}, lookLastLine) == line) {\n\t\t\t\t\tbeginFoldBlock = lookLine;\n\t\t\t\t\tendFoldBlock = line;\n\t\t\t\t\tfirstChangeableLineBefore = line - 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif ((lookLine > 0) && (lookLineLevelNum == FoldLevel::Base) && (LevelNumberPart(GetFoldLevel(lookLine - 1)) > lookLineLevelNum))\n\t\t\t\tbreak;\n\t\t\tlookLineLevel = GetFoldLevel(--lookLine);\n\t\t\tlookLineLevelNum = LevelNumberPart(lookLineLevel);\n\t\t}\n\t}\n\tif (firstChangeableLineBefore == -1) {\n\t\tfor (lookLine = line - 1, lookLineLevel = GetFoldLevel(lookLine), lookLineLevelNum = LevelNumberPart(lookLineLevel);\n\t\t\tlookLine >= beginFoldBlock;\n\t\t\tlookLineLevel = GetFoldLevel(--lookLine), lookLineLevelNum = LevelNumberPart(lookLineLevel)) {\n\t\t\tif (LevelIsWhitespace(lookLineLevel) || (lookLineLevelNum > LevelNumberPart(level))) {\n\t\t\t\tfirstChangeableLineBefore = lookLine;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\tif (firstChangeableLineBefore == -1)\n\t\tfirstChangeableLineBefore = beginFoldBlock - 1;\n\n\tSci::Line firstChangeableLineAfter = -1;\n\tfor (lookLine = line + 1, lookLineLevel = GetFoldLevel(lookLine), lookLineLevelNum = LevelNumberPart(lookLineLevel);\n\t\tlookLine <= endFoldBlock;\n\t\tlookLineLevel = GetFoldLevel(++lookLine), lookLineLevelNum = LevelNumberPart(lookLineLevel)) {\n\t\tif (LevelIsHeader(lookLineLevel) && (lookLineLevelNum < LevelNumberPart(GetFoldLevel(lookLine + 1)))) {\n\t\t\tfirstChangeableLineAfter = lookLine;\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (firstChangeableLineAfter == -1)\n\t\tfirstChangeableLineAfter = endFoldBlock + 1;\n\n\thighlightDelimiter.beginFoldBlock = beginFoldBlock;\n\thighlightDelimiter.endFoldBlock = endFoldBlock;\n\thighlightDelimiter.firstChangeableLineBefore = firstChangeableLineBefore;\n\thighlightDelimiter.firstChangeableLineAfter = firstChangeableLineAfter;\n}\n\nSci::Position Document::ClampPositionIntoDocument(Sci::Position pos) const noexcept {\n\treturn std::clamp<Sci::Position>(pos, 0, LengthNoExcept());\n}\n\nbool Document::IsCrLf(Sci::Position pos) const noexcept {\n\tif (pos < 0)\n\t\treturn false;\n\tif (pos >= (LengthNoExcept() - 1))\n\t\treturn false;\n\treturn (cb.CharAt(pos) == '\\r') && (cb.CharAt(pos + 1) == '\\n');\n}\n\nint Document::LenChar(Sci::Position pos) const noexcept {\n\tif (pos < 0 || pos >= LengthNoExcept()) {\n\t\t// Returning 1 instead of 0 to defend against hanging with a loop that goes (or starts) out of bounds.\n\t\treturn 1;\n\t} else if (IsCrLf(pos)) {\n\t\treturn 2;\n\t}\n\n\tconst unsigned char leadByte = cb.UCharAt(pos);\n\tif (!dbcsCodePage || UTF8IsAscii(leadByte)) {\n\t\t// Common case: ASCII character\n\t\treturn 1;\n\t}\n\tif (CpUtf8 == dbcsCodePage) {\n\t\tconst int widthCharBytes = UTF8BytesOfLead[leadByte];\n\t\tunsigned char charBytes[UTF8MaxBytes] = { leadByte, 0, 0, 0 };\n\t\tfor (int b = 1; b < widthCharBytes; b++) {\n\t\t\tcharBytes[b] = cb.UCharAt(pos + b);\n\t\t}\n\t\tconst int utf8status = UTF8Classify(charBytes, widthCharBytes);\n\t\tif (utf8status & UTF8MaskInvalid) {\n\t\t\t// Treat as invalid and use up just one byte\n\t\t\treturn 1;\n\t\t}\n\t\treturn utf8status & UTF8MaskWidth;\n\t}\n\tif (IsDBCSLeadByteNoExcept(leadByte) && IsDBCSTrailByteNoExcept(cb.CharAt(pos + 1))) {\n\t\treturn 2;\n\t} else {\n\t\treturn 1;\n\t}\n}\n\nbool Document::InGoodUTF8(Sci::Position pos, Sci::Position &start, Sci::Position &end) const noexcept {\n\tSci::Position trail = pos;\n\twhile ((trail>0) && (pos-trail < UTF8MaxBytes) && UTF8IsTrailByte(cb.UCharAt(trail-1)))\n\t\ttrail--;\n\tstart = (trail > 0) ? trail-1 : trail;\n\n\tconst unsigned char leadByte = cb.UCharAt(start);\n\tconst int widthCharBytes = UTF8BytesOfLead[leadByte];\n\tif (widthCharBytes == 1) {\n\t\treturn false;\n\t}\n\tconst int trailBytes = widthCharBytes - 1;\n\tconst Sci::Position len = pos - start;\n\tif (len > trailBytes)\n\t\t// pos too far from lead\n\t\treturn false;\n\tunsigned char charBytes[UTF8MaxBytes] = {leadByte,0,0,0};\n\tfor (Sci::Position b=1; b<widthCharBytes && ((start+b) < cb.Length()); b++)\n\t\tcharBytes[b] = cb.CharAt(start+b);\n\tconst int utf8status = UTF8Classify(charBytes, widthCharBytes);\n\tif (utf8status & UTF8MaskInvalid)\n\t\treturn false;\n\tend = start + widthCharBytes;\n\treturn true;\n}\n\n// Normalise a position so that it is not part way through a multi-byte character.\n// This can occur in two situations -\n// When lines are terminated with \\r\\n pairs which should be treated as one character.\n// When displaying DBCS text such as Japanese.\n// If moving, move the position in the indicated direction.\nSci::Position Document::MovePositionOutsideChar(Sci::Position pos, Sci::Position moveDir, bool checkLineEnd) const noexcept {\n\t//Platform::DebugPrintf(\"NoCRLF %d %d\\n\", pos, moveDir);\n\t// If out of range, just return minimum/maximum value.\n\tif (pos <= 0)\n\t\treturn 0;\n\tif (pos >= LengthNoExcept())\n\t\treturn LengthNoExcept();\n\n\t// PLATFORM_ASSERT(pos > 0 && pos < LengthNoExcept());\n\tif (checkLineEnd && IsCrLf(pos - 1)) {\n\t\tif (moveDir > 0)\n\t\t\treturn pos + 1;\n\t\telse\n\t\t\treturn pos - 1;\n\t}\n\n\tif (dbcsCodePage) {\n\t\tif (CpUtf8 == dbcsCodePage) {\n\t\t\tconst unsigned char ch = cb.UCharAt(pos);\n\t\t\t// If ch is not a trail byte then pos is valid intercharacter position\n\t\t\tif (UTF8IsTrailByte(ch)) {\n\t\t\t\tSci::Position startUTF = pos;\n\t\t\t\tSci::Position endUTF = pos;\n\t\t\t\tif (InGoodUTF8(pos, startUTF, endUTF)) {\n\t\t\t\t\t// ch is a trail byte within a UTF-8 character\n\t\t\t\t\tif (moveDir > 0)\n\t\t\t\t\t\tpos = endUTF;\n\t\t\t\t\telse\n\t\t\t\t\t\tpos = startUTF;\n\t\t\t\t}\n\t\t\t\t// Else invalid UTF-8 so return position of isolated trail byte\n\t\t\t}\n\t\t} else {\n\t\t\t// Step back until a non-lead-byte is found.\n\t\t\tSci::Position posCheck = pos;\n\t\t\twhile ((posCheck > 0) && IsDBCSLeadByteNoExcept(cb.CharAt(posCheck-1)))\n\t\t\t\tposCheck--;\n\n\t\t\t// Check from known start of character.\n\t\t\twhile (posCheck < pos) {\n\t\t\t\tconst int mbsize = IsDBCSDualByteAt(posCheck) ? 2 : 1;\n\t\t\t\tif (posCheck + mbsize == pos) {\n\t\t\t\t\treturn pos;\n\t\t\t\t} else if (posCheck + mbsize > pos) {\n\t\t\t\t\tif (moveDir > 0) {\n\t\t\t\t\t\treturn posCheck + mbsize;\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn posCheck;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tposCheck += mbsize;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn pos;\n}\n\n// NextPosition moves between valid positions - it can not handle a position in the middle of a\n// multi-byte character. It is used to iterate through text more efficiently than MovePositionOutsideChar.\n// A \\r\\n pair is treated as two characters.\nSci::Position Document::NextPosition(Sci::Position pos, int moveDir) const noexcept {\n\t// If out of range, just return minimum/maximum value.\n\tconst int increment = (moveDir > 0) ? 1 : -1;\n\tif (pos + increment <= 0)\n\t\treturn 0;\n\tif (pos + increment >= cb.Length())\n\t\treturn cb.Length();\n\n\tif (dbcsCodePage) {\n\t\tif (CpUtf8 == dbcsCodePage) {\n\t\t\tif (increment == 1) {\n\t\t\t\t// Simple forward movement case so can avoid some checks\n\t\t\t\tconst unsigned char leadByte = cb.UCharAt(pos);\n\t\t\t\tif (UTF8IsAscii(leadByte)) {\n\t\t\t\t\t// Single byte character or invalid\n\t\t\t\t\tpos++;\n\t\t\t\t} else {\n\t\t\t\t\tconst int widthCharBytes = UTF8BytesOfLead[leadByte];\n\t\t\t\t\tunsigned char charBytes[UTF8MaxBytes] = {leadByte,0,0,0};\n\t\t\t\t\tfor (int b=1; b<widthCharBytes; b++)\n\t\t\t\t\t\tcharBytes[b] = cb.CharAt(pos+b);\n\t\t\t\t\tconst int utf8status = UTF8Classify(charBytes, widthCharBytes);\n\t\t\t\t\tif (utf8status & UTF8MaskInvalid)\n\t\t\t\t\t\tpos++;\n\t\t\t\t\telse\n\t\t\t\t\t\tpos += utf8status & UTF8MaskWidth;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Examine byte before position\n\t\t\t\tpos--;\n\t\t\t\tconst unsigned char ch = cb.UCharAt(pos);\n\t\t\t\t// If ch is not a trail byte then pos is valid intercharacter position\n\t\t\t\tif (UTF8IsTrailByte(ch)) {\n\t\t\t\t\t// If ch is a trail byte in a valid UTF-8 character then return start of character\n\t\t\t\t\tSci::Position startUTF = pos;\n\t\t\t\t\tSci::Position endUTF = pos;\n\t\t\t\t\tif (InGoodUTF8(pos, startUTF, endUTF)) {\n\t\t\t\t\t\tpos = startUTF;\n\t\t\t\t\t}\n\t\t\t\t\t// Else invalid UTF-8 so return position of isolated trail byte\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tif (moveDir > 0) {\n\t\t\t\tconst int mbsize = IsDBCSDualByteAt(pos) ? 2 : 1;\n\t\t\t\tpos += mbsize;\n\t\t\t\tif (pos > cb.Length())\n\t\t\t\t\tpos = cb.Length();\n\t\t\t} else {\n\t\t\t\t// How to Go Backward in a DBCS String\n\t\t\t\t// https://msdn.microsoft.com/en-us/library/cc194792.aspx\n\t\t\t\t// DBCS-Enabled Programs vs. Non-DBCS-Enabled Programs\n\t\t\t\t// https://msdn.microsoft.com/en-us/library/cc194790.aspx\n\t\t\t\tif (IsDBCSLeadByteNoExcept(cb.CharAt(pos - 1))) {\n\t\t\t\t\t// Should actually be trail byte\n\t\t\t\t\tif (IsDBCSDualByteAt(pos - 2)) {\n\t\t\t\t\t\treturn pos - 2;\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Invalid byte pair so treat as one byte wide\n\t\t\t\t\t\treturn pos - 1;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// Otherwise, step back until a non-lead-byte is found.\n\t\t\t\t\tSci::Position posTemp = pos - 1;\n\t\t\t\t\twhile (--posTemp >= 0 && IsDBCSLeadByteNoExcept(cb.CharAt(posTemp)))\n\t\t\t\t\t\t;\n\t\t\t\t\t// Now posTemp+1 must point to the beginning of a character,\n\t\t\t\t\t// so figure out whether we went back an even or an odd\n\t\t\t\t\t// number of bytes and go back 1 or 2 bytes, respectively.\n\t\t\t\t\tconst Sci::Position widthLast = ((pos - posTemp) & 1) + 1;\n\t\t\t\t\tif ((widthLast == 2) && (IsDBCSDualByteAt(pos - widthLast))) {\n\t\t\t\t\t\treturn pos - widthLast;\n\t\t\t\t\t}\n\t\t\t\t\t// Byte before pos may be valid character or may be an invalid second byte\n\t\t\t\t\treturn pos - 1;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} else {\n\t\tpos += increment;\n\t}\n\n\treturn pos;\n}\n\nbool Document::NextCharacter(Sci::Position &pos, int moveDir) const noexcept {\n\t// Returns true if pos changed\n\tSci::Position posNext = NextPosition(pos, moveDir);\n\tif (posNext == pos) {\n\t\treturn false;\n\t}\n\tpos = posNext;\n\treturn true;\n}\n\nCharacterExtracted Document::CharacterAfter(Sci::Position position) const noexcept {\n\tif (position >= LengthNoExcept()) {\n\t\treturn characterEmpty;\n\t}\n\tconst unsigned char leadByte = cb.UCharAt(position);\n\tif (!dbcsCodePage || UTF8IsAscii(leadByte)) {\n\t\t// Common case: ASCII character\n\t\treturn CharacterExtracted(leadByte, 1);\n\t}\n\tif (CpUtf8 == dbcsCodePage) {\n\t\tconst int widthCharBytes = UTF8BytesOfLead[leadByte];\n\t\tunsigned char charBytes[UTF8MaxBytes] = { leadByte, 0, 0, 0 };\n\t\tfor (int b = 1; b<widthCharBytes; b++)\n\t\t\tcharBytes[b] = cb.UCharAt(position + b);\n\t\treturn CharacterExtracted(charBytes, widthCharBytes);\n\t} else {\n\t\tif (IsDBCSLeadByteNoExcept(leadByte)) {\n\t\t\tconst unsigned char trailByte = cb.UCharAt(position + 1);\n\t\t\tif (IsDBCSTrailByteNoExcept(trailByte)) {\n\t\t\t\treturn CharacterExtracted::DBCS(leadByte, trailByte);\n\t\t\t}\n\t\t}\n\t\treturn CharacterExtracted(leadByte, 1);\n\t}\n}\n\nCharacterExtracted Document::CharacterBefore(Sci::Position position) const noexcept {\n\tif (position <= 0) {\n\t\treturn characterEmpty;\n\t}\n\tconst unsigned char previousByte = cb.UCharAt(position - 1);\n\tif (0 == dbcsCodePage) {\n\t\treturn CharacterExtracted(previousByte, 1);\n\t}\n\tif (CpUtf8 == dbcsCodePage) {\n\t\tif (UTF8IsAscii(previousByte)) {\n\t\t\treturn CharacterExtracted(previousByte, 1);\n\t\t}\n\t\tposition--;\n\t\t// If previousByte is not a trail byte then its invalid\n\t\tif (UTF8IsTrailByte(previousByte)) {\n\t\t\t// If previousByte is a trail byte in a valid UTF-8 character then find start of character\n\t\t\tSci::Position startUTF = position;\n\t\t\tSci::Position endUTF = position;\n\t\t\tif (InGoodUTF8(position, startUTF, endUTF)) {\n\t\t\t\tconst Sci::Position widthCharBytes = endUTF - startUTF;\n\t\t\t\tunsigned char charBytes[UTF8MaxBytes] = { 0, 0, 0, 0 };\n\t\t\t\tfor (Sci::Position b = 0; b<widthCharBytes; b++)\n\t\t\t\t\tcharBytes[b] = cb.UCharAt(startUTF + b);\n\t\t\t\treturn CharacterExtracted(charBytes, widthCharBytes);\n\t\t\t}\n\t\t\t// Else invalid UTF-8 so return position of isolated trail byte\n\t\t}\n\t\treturn characterBadByte;\n\t} else {\n\t\t// Moving backwards in DBCS is complex so use NextPosition\n\t\tconst Sci::Position posStartCharacter = NextPosition(position, -1);\n\t\treturn CharacterAfter(posStartCharacter);\n\t}\n}\n\n// Return -1  on out-of-bounds\nSci_Position SCI_METHOD Document::GetRelativePosition(Sci_Position positionStart, Sci_Position characterOffset) const {\n\tSci::Position pos = positionStart;\n\tif (dbcsCodePage) {\n\t\tconst int increment = (characterOffset > 0) ? 1 : -1;\n\t\twhile (characterOffset != 0) {\n\t\t\tconst Sci::Position posNext = NextPosition(pos, increment);\n\t\t\tif (posNext == pos)\n\t\t\t\treturn Sci::invalidPosition;\n\t\t\tpos = posNext;\n\t\t\tcharacterOffset -= increment;\n\t\t}\n\t} else {\n\t\tpos = positionStart + characterOffset;\n\t\tif ((pos < 0) || (pos > Length()))\n\t\t\treturn Sci::invalidPosition;\n\t}\n\treturn pos;\n}\n\nSci::Position Document::GetRelativePositionUTF16(Sci::Position positionStart, Sci::Position characterOffset) const noexcept {\n\tSci::Position pos = positionStart;\n\tif (dbcsCodePage) {\n\t\tconst int increment = (characterOffset > 0) ? 1 : -1;\n\t\twhile (characterOffset != 0) {\n\t\t\tconst Sci::Position posNext = NextPosition(pos, increment);\n\t\t\tif (posNext == pos)\n\t\t\t\treturn Sci::invalidPosition;\n\t\t\tif (std::abs(pos-posNext) > 3)\t// 4 byte character = 2*UTF16.\n\t\t\t\tcharacterOffset -= increment;\n\t\t\tpos = posNext;\n\t\t\tcharacterOffset -= increment;\n\t\t}\n\t} else {\n\t\tpos = positionStart + characterOffset;\n\t\tif ((pos < 0) || (pos > LengthNoExcept()))\n\t\t\treturn Sci::invalidPosition;\n\t}\n\treturn pos;\n}\n\nint SCI_METHOD Document::GetCharacterAndWidth(Sci_Position position, Sci_Position *pWidth) const {\n\tint bytesInCharacter = 1;\n\tconst unsigned char leadByte = cb.UCharAt(position);\n\tint character = leadByte;\n\tif (dbcsCodePage && !UTF8IsAscii(leadByte)) {\n\t\tif (CpUtf8 == dbcsCodePage) {\n\t\t\tconst int widthCharBytes = UTF8BytesOfLead[leadByte];\n\t\t\tunsigned char charBytes[UTF8MaxBytes] = {leadByte,0,0,0};\n\t\t\tfor (int b=1; b<widthCharBytes; b++)\n\t\t\t\tcharBytes[b] = cb.UCharAt(position+b);\n\t\t\tconst int utf8status = UTF8Classify(charBytes, widthCharBytes);\n\t\t\tif (utf8status & UTF8MaskInvalid) {\n\t\t\t\t// Report as singleton surrogate values which are invalid Unicode\n\t\t\t\tcharacter =  0xDC80 + leadByte;\n\t\t\t} else {\n\t\t\t\tbytesInCharacter = utf8status & UTF8MaskWidth;\n\t\t\t\tcharacter = UnicodeFromUTF8(charBytes);\n\t\t\t}\n\t\t} else {\n\t\t\tif (IsDBCSLeadByteNoExcept(leadByte)) {\n\t\t\t\tconst unsigned char trailByte = cb.UCharAt(position + 1);\n\t\t\t\tif (IsDBCSTrailByteNoExcept(trailByte)) {\n\t\t\t\t\tbytesInCharacter = 2;\n\t\t\t\t\tcharacter = (leadByte << 8) | trailByte;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif (pWidth) {\n\t\t*pWidth = bytesInCharacter;\n\t}\n\treturn character;\n}\n\nint SCI_METHOD Document::CodePage() const {\n\treturn dbcsCodePage;\n}\n\nbool SCI_METHOD Document::IsDBCSLeadByte(char ch) const {\n\t// Used by lexers so must match IDocument method exactly\n\treturn IsDBCSLeadByteNoExcept(ch);\n}\n\n// Silence 'magic' number use since the set of DBCS lead and trail bytes differ\n// between encodings and would require many constant declarations that would just\n// obscure the behaviour.\n\n// NOLINTBEGIN(*-magic-numbers)\n\nbool Document::IsDBCSLeadByteNoExcept(char ch) const noexcept {\n\t// Used inside core Scintilla\n\t// Byte ranges found in Wikipedia articles with relevant search strings in each case\n\tconst unsigned char uch = ch;\n\tswitch (dbcsCodePage) {\n\t\tcase 932:\n\t\t\t// Shift_jis\n\t\t\treturn ((uch >= 0x81) && (uch <= 0x9F)) ||\n\t\t\t\t((uch >= 0xE0) && (uch <= 0xFC));\n\t\t\t\t// Lead bytes F0 to FC may be a Microsoft addition.\n\t\tcase 936:\n\t\t\t// GBK\n\t\t\treturn (uch >= 0x81) && (uch <= 0xFE);\n\t\tcase 949:\n\t\t\t// Korean Wansung KS C-5601-1987\n\t\t\treturn (uch >= 0x81) && (uch <= 0xFE);\n\t\tcase 950:\n\t\t\t// Big5\n\t\t\treturn (uch >= 0x81) && (uch <= 0xFE);\n\t\tcase 1361:\n\t\t\t// Korean Johab KS C-5601-1992\n\t\t\treturn\n\t\t\t\t((uch >= 0x84) && (uch <= 0xD3)) ||\n\t\t\t\t((uch >= 0xD8) && (uch <= 0xDE)) ||\n\t\t\t\t((uch >= 0xE0) && (uch <= 0xF9));\n\t}\n\treturn false;\n}\n\nbool Document::IsDBCSTrailByteNoExcept(char ch) const noexcept {\n\tconst unsigned char trail = ch;\n\tswitch (dbcsCodePage) {\n\tcase 932:\n\t\t// Shift_jis\n\t\treturn (trail != 0x7F) &&\n\t\t\t((trail >= 0x40) && (trail <= 0xFC));\n\tcase 936:\n\t\t// GBK\n\t\treturn (trail != 0x7F) &&\n\t\t\t((trail >= 0x40) && (trail <= 0xFE));\n\tcase 949:\n\t\t// Korean Wansung KS C-5601-1987\n\t\treturn\n\t\t\t((trail >= 0x41) && (trail <= 0x5A)) ||\n\t\t\t((trail >= 0x61) && (trail <= 0x7A)) ||\n\t\t\t((trail >= 0x81) && (trail <= 0xFE));\n\tcase 950:\n\t\t// Big5\n\t\treturn\n\t\t\t((trail >= 0x40) && (trail <= 0x7E)) ||\n\t\t\t((trail >= 0xA1) && (trail <= 0xFE));\n\tcase 1361:\n\t\t// Korean Johab KS C-5601-1992\n\t\treturn\n\t\t\t((trail >= 0x31) && (trail <= 0x7E)) ||\n\t\t\t((trail >= 0x81) && (trail <= 0xFE));\n\t}\n\treturn false;\n}\n\nunsigned char Document::DBCSMinTrailByte() const noexcept {\n\tswitch (dbcsCodePage) {\n\tcase 932:\n\t\t// Shift_jis\n\t\treturn 0x40;\n\tcase 936:\n\t\t// GBK\n\t\treturn 0x40;\n\tcase 949:\n\t\t// Korean Wansung KS C-5601-1987\n\t\treturn 0x41;\n\tcase 950:\n\t\t// Big5\n\t\treturn 0x40;\n\tcase 1361:\n\t\t// Korean Johab KS C-5601-1992\n\t\treturn 0x31;\n\tdefault:\n\t\t// UTF-8 or single byte, should not occur as not DBCS\n\t\treturn 0;\n\t}\n}\n\n// NOLINTEND(*-magic-numbers)\n\nint Document::DBCSDrawBytes(std::string_view text) const noexcept {\n\tif (text.length() <= 1) {\n\t\treturn static_cast<int>(text.length());\n\t}\n\tif (IsDBCSLeadByteNoExcept(text[0])) {\n\t\treturn IsDBCSTrailByteNoExcept(text[1]) ? 2 : 1;\n\t} else {\n\t\treturn 1;\n\t}\n}\n\nbool Document::IsDBCSDualByteAt(Sci::Position pos) const noexcept {\n\treturn IsDBCSLeadByteNoExcept(cb.CharAt(pos))\n\t\t&& IsDBCSTrailByteNoExcept(cb.CharAt(pos + 1));\n}\n\nnamespace {\n\n// Remove any extra bytes after the last valid character.\nvoid DiscardEndFragment(std::string_view &text) noexcept {\n\tif (!text.empty()) {\n\t\tif (UTF8IsFirstByte(text.back())) {\n\t\t\t// Ending with start of character byte is invalid\n\t\t\ttext.remove_suffix(1);\n\t\t} else if (UTF8IsTrailByte(text.back())) {\n\t\t\t// go back to the start of last character.\n\t\t\tconstexpr int UTF8MaxTrail = UTF8MaxBytes - 1;\n\t\t\tconst size_t maxTrail = std::max<size_t>(UTF8MaxTrail, text.length());\n\t\t\tsize_t trail = 1;\n\t\t\twhile (trail < maxTrail && UTF8IsTrailByte(text[text.length() - trail])) {\n\t\t\t\ttrail++;\n\t\t\t}\n\t\t\tconst std::string_view endPortion = text.substr(text.length() - trail);\n\t\t\tif (!UTF8IsValid(endPortion)) {\n\t\t\t\ttext.remove_suffix(trail);\n\t\t\t}\n\t\t}\n\t}\n}\n\nconstexpr bool IsBaseOfGrapheme(CharacterCategory cc) {\n\tswitch (cc) {\n\t\t// \\p{L}\\p{N}\\p{P}\\p{Sm}\\p{Sc}\\p{So}\\p{Zs}\n\tcase ccLu:\n\tcase ccLl:\n\tcase ccLt:\n\tcase ccLm:\n\tcase ccLo:\n\tcase ccNd:\n\tcase ccNl:\n\tcase ccNo:\n\tcase ccPc:\n\tcase ccPd:\n\tcase ccPs:\n\tcase ccPe:\n\tcase ccPi:\n\tcase ccPf:\n\tcase ccPo:\n\tcase ccSm:\n\tcase ccSc:\n\tcase ccSo:\n\tcase ccZs:\n\t\t// Control\n\tcase ccCc:\n\tcase ccCs:\n\tcase ccCo:\n\t\treturn true;\n\tdefault:\n\t\t// ccMn, ccMc, ccMe,\n\t\t// ccSk,\n\t\t// ccZl, ccZp,\n\t\t// ccCf, ccCn\n\t\treturn false;\n\t}\n}\n\nCharacterExtracted LastCharacter(std::string_view text) noexcept {\n\tif (text.empty())\n\t\treturn characterEmpty;\n\tconst size_t length = text.length();\n\tsize_t trail = length;\n\twhile ((trail > 0) && (length - trail < UTF8MaxBytes) && UTF8IsTrailByte(text[trail - 1]))\n\t\ttrail--;\n\tconst size_t start = (trail > 0) ? trail - 1 : trail;\n\tconst int utf8status = UTF8Classify(text.substr(start));\n\tif (utf8status & UTF8MaskInvalid) {\n\t\treturn characterBadByte;\n\t}\n\treturn { static_cast<unsigned int>(UnicodeFromUTF8(text.substr(start))),\n\t\tstatic_cast<unsigned int>(utf8status & UTF8MaskWidth) };\n}\n\nconstexpr Sci::Position NextTab(Sci::Position pos, Sci::Position tabSize) noexcept {\n\treturn ((pos / tabSize) + 1) * tabSize;\n}\n\nstd::string CreateIndentation(Sci::Position indent, int tabSize, bool insertSpaces) {\n\tstd::string indentation;\n\tif (!insertSpaces) {\n\t\twhile (indent >= tabSize) {\n\t\t\tindentation += '\\t';\n\t\t\tindent -= tabSize;\n\t\t}\n\t}\n\twhile (indent > 0) {\n\t\tindentation += ' ';\n\t\tindent--;\n\t}\n\treturn indentation;\n}\n\n}\n\nbool Scintilla::Internal::DiscardLastCombinedCharacter(std::string_view &text) noexcept {\n\t// Handle the simple common case where a base character may be followed by\n\t// accents and similar marks by discarding until start of base character.\n\t//\n\t// From Grapheme_Cluster_Boundaries\n\t// combining character sequence = ccs-base? ccs-extend+\n\t// ccs-base := [\\p{L}\\p{N}\\p{P}\\p{S}\\p{Zs}]\n\t// ccs-extend := [\\p{M}\\p{Join_Control}]\n\t// Modified to move Sk (Symbol Modifier) from ccs-base to ccs-extend to preserve modified emoji\n\t// May break before and after Control which is defined as most of ccC? but not some of ccCf and ccCn\n\t// so treat ccCc, ccCs, ccCo as base for now.\n\n\tstd::string_view truncated = text;\n\twhile (truncated.length() > UTF8MaxBytes) {\n\t\t// Give up when short\n\t\tconst CharacterExtracted ce = LastCharacter(truncated);\n\t\tconst CharacterCategory cc = CategoriseCharacter(static_cast<int>(ce.character));\n\t\ttruncated.remove_suffix(ce.widthBytes);\n\t\tif (IsBaseOfGrapheme(cc)) {\n\t\t\ttext = truncated;\n\t\t\treturn true;\n\t\t}\n\t}\n\t// No base character found so just leave as is\n\treturn false;\n}\n\n// Need to break text into segments near end but taking into account the\n// encoding to not break inside a UTF-8 or DBCS character and also trying\n// to avoid breaking inside a pair of combining characters, or inside\n// ligatures.\n// TODO: implement grapheme cluster boundaries,\n// see https://www.unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries.\n//\n// The segment length must always be long enough (more than 4 bytes)\n// so that there will be at least one whole character to make a segment.\n// For UTF-8, text must consist only of valid whole characters.\n// In preference order from best to worst:\n//   1) Break before or after spaces or controls\n//   2) Break at word and punctuation boundary for better kerning and ligature support\n//   3) Break before letter in UTF-8 to avoid breaking combining characters\n//   4) Break after whole character, this may break combining characters\n\nsize_t Document::SafeSegment(std::string_view text) const noexcept {\n\t// check space first as most written language use spaces.\n\tfor (std::string_view::iterator it = text.end() - 1; it != text.begin(); --it) {\n\t\tif (IsBreakSpace(*it)) {\n\t\t\treturn it - text.begin();\n\t\t}\n\t}\n\n\tif (!dbcsCodePage || dbcsCodePage == CpUtf8) {\n\t\tif (dbcsCodePage) {\n\t\t\t// UTF-8\n\t\t\tDiscardEndFragment(text);\n\t\t\tif (DiscardLastCombinedCharacter(text)) {\n\t\t\t\treturn text.length();\n\t\t\t}\n\t\t}\n\t\t// backward iterate for UTF-8 and single byte encoding to find word and punctuation boundary.\n\t\tstd::string_view::iterator it = text.end() - 1;\n\t\tconst bool punctuation = IsPunctuation(*it);\n\t\tdo {\n\t\t\t--it;\n\t\t\tif (punctuation != IsPunctuation(*it)) {\n\t\t\t\treturn it - text.begin() + 1;\n\t\t\t}\n\t\t} while (it != text.begin());\n\n\t\treturn text.length() - 1;\n\t}\n\n\t{\n\t\t// forward iterate for DBCS to find word and punctuation boundary.\n\t\tsize_t lastPunctuationBreak = 0;\n\t\tsize_t lastEncodingAllowedBreak = 0;\n\t\tCharacterClass ccPrev = CharacterClass::space;\n\t\tfor (size_t j = 0; j < text.length();) {\n\t\t\tconst unsigned char ch = text[j];\n\t\t\tlastEncodingAllowedBreak = j++;\n\n\t\t\tCharacterClass cc = CharacterClass::word;\n\t\t\tif (UTF8IsAscii(ch)) {\n\t\t\t\tif (IsPunctuation(ch)) {\n\t\t\t\t\tcc = CharacterClass::punctuation;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tj += IsDBCSLeadByteNoExcept(ch);\n\t\t\t}\n\t\t\tif (cc != ccPrev) {\n\t\t\t\tccPrev = cc;\n\t\t\t\tlastPunctuationBreak = lastEncodingAllowedBreak;\n\t\t\t}\n\t\t}\n\t\treturn lastPunctuationBreak ? lastPunctuationBreak : lastEncodingAllowedBreak;\n\t}\n}\n\nEncodingFamily Document::CodePageFamily() const noexcept {\n\tif (CpUtf8 == dbcsCodePage)\n\t\treturn EncodingFamily::unicode;\n\tif (dbcsCodePage)\n\t\treturn EncodingFamily::dbcs;\n\treturn EncodingFamily::eightBit;\n}\n\nvoid Document::ModifiedAt(Sci::Position pos) noexcept {\n\tif (endStyled > pos)\n\t\tendStyled = pos;\n}\n\nvoid Document::CheckReadOnly() {\n\tif (cb.IsReadOnly() && enteredReadOnlyCount == 0) {\n\t\tenteredReadOnlyCount++;\n\t\tNotifyModifyAttempt();\n\t\tenteredReadOnlyCount--;\n\t}\n}\n\nvoid Document::TrimReplacement(std::string_view &text, Range &range) const noexcept {\n\twhile (!text.empty() && !range.Empty() && (text.front() == CharAt(range.start))) {\n\t\ttext.remove_prefix(1);\n\t\trange.start++;\n\t}\n\twhile (!text.empty() && !range.Empty() && (text.back() == CharAt(range.end-1))) {\n\t\ttext.remove_suffix(1);\n\t\trange.end--;\n\t}\n}\n\n// Document only modified by gateways DeleteChars, InsertString, Undo, Redo, and SetStyleAt.\n// SetStyleAt does not change the persistent state of a document\n\nbool Document::DeleteChars(Sci::Position pos, Sci::Position len) {\n\tif (pos < 0)\n\t\treturn false;\n\tif (len <= 0)\n\t\treturn false;\n\tif ((pos + len) > LengthNoExcept())\n\t\treturn false;\n\tCheckReadOnly();\n\tif (enteredModification != 0) {\n\t\treturn false;\n\t}\n\tenteredModification++;\n\tif (!cb.IsReadOnly()) {\n\t\tif (cb.IsCollectingUndo() && cb.CanRedo()) {\n\t\t\t// Abandoning some undo actions so truncate any later selections\n\t\t\tTruncateUndoComments(cb.UndoCurrent());\n\t\t}\n\t\tNotifyModified(\n\t\t\tDocModification(\n\t\t\t    ModificationFlags::BeforeDelete | ModificationFlags::User,\n\t\t\t    pos, len,\n\t\t\t0, nullptr));\n\t\tconst Sci::Line prevLinesTotal = LinesTotal();\n\t\tconst bool startSavePoint = cb.IsSavePoint();\n\t\tbool startSequence = false;\n\t\tconst char *text = cb.DeleteChars(pos, len, startSequence);\n\t\tif (startSavePoint && cb.IsCollectingUndo())\n\t\t\tNotifySavePoint(false);\n\t\tif ((pos < LengthNoExcept()) || (pos == 0))\n\t\t\tModifiedAt(pos);\n\t\telse\n\t\t\tModifiedAt(pos-1);\n\t\tNotifyModified(\n\t\t\tDocModification(\n\t\t\t    ModificationFlags::DeleteText | ModificationFlags::User |\n\t\t\t\t(startSequence?ModificationFlags::StartAction:ModificationFlags::None),\n\t\t\t    pos, len,\n\t\t\t    LinesTotal() - prevLinesTotal, text));\n\t}\n\tenteredModification--;\n\treturn !cb.IsReadOnly();\n}\n\n/**\n * Insert a string with a length.\n */\nSci::Position Document::InsertString(Sci::Position position, const char *s, Sci::Position insertLength) {\n\tif (insertLength <= 0) {\n\t\treturn 0;\n\t}\n\tCheckReadOnly();\t// Application may change read only state here\n\tif (cb.IsReadOnly()) {\n\t\treturn 0;\n\t}\n\tif (enteredModification != 0) {\n\t\treturn 0;\n\t}\n\tenteredModification++;\n\tinsertionSet = false;\n\tinsertion.clear();\n\tNotifyModified(\n\t\tDocModification(\n\t\t\tModificationFlags::InsertCheck,\n\t\t\tposition, insertLength,\n\t\t\t0, s));\n\tif (insertionSet) {\n\t\ts = insertion.c_str();\n\t\tinsertLength = insertion.length();\n\t}\n\tif (cb.IsCollectingUndo() && cb.CanRedo()) {\n\t\t// Abandoning some undo actions so truncate any later selections\n\t\tTruncateUndoComments(cb.UndoCurrent());\n\t}\n\tNotifyModified(\n\t\tDocModification(\n\t\t\tModificationFlags::BeforeInsert | ModificationFlags::User,\n\t\t\tposition, insertLength,\n\t\t\t0, s));\n\tconst Sci::Line prevLinesTotal = LinesTotal();\n\tconst bool startSavePoint = cb.IsSavePoint();\n\tbool startSequence = false;\n\tconst char *text = cb.InsertString(position, s, insertLength, startSequence);\n\tif (startSavePoint && cb.IsCollectingUndo())\n\t\tNotifySavePoint(false);\n\tModifiedAt(position);\n\tNotifyModified(\n\t\tDocModification(\n\t\t\tModificationFlags::InsertText | ModificationFlags::User |\n\t\t\t(startSequence?ModificationFlags::StartAction:ModificationFlags::None),\n\t\t\tposition, insertLength,\n\t\t\tLinesTotal() - prevLinesTotal, text));\n\tif (insertionSet) {\t// Free memory as could be large\n\t\tstd::string().swap(insertion);\n\t}\n\tenteredModification--;\n\treturn insertLength;\n}\n\nSci::Position Document::InsertString(Sci::Position position, std::string_view sv) {\n\treturn InsertString(position, sv.data(), sv.length());\n}\n\nvoid Document::ChangeInsertion(const char *s, Sci::Position length) {\n\tinsertionSet = true;\n\tinsertion.assign(s, length);\n}\n\nint SCI_METHOD Document::AddData(const char *data, Sci_Position length) {\n\ttry {\n\t\tconst Sci::Position position = Length();\n\t\tInsertString(position, data, length);\n\t} catch (std::bad_alloc &) {\n\t\treturn static_cast<int>(Status::BadAlloc);\n\t} catch (...) {\n\t\treturn static_cast<int>(Status::Failure);\n\t}\n\treturn static_cast<int>(Status::Ok);\n}\n\nIDocumentEditable *Document::AsDocumentEditable() noexcept {\n\treturn static_cast<IDocumentEditable *>(this);\n}\n\nvoid *SCI_METHOD Document::ConvertToDocument() {\n\treturn AsDocumentEditable();\n}\n\nSci::Position Document::Undo() {\n\tSci::Position newPos = -1;\n\tCheckReadOnly();\n\tif ((enteredModification == 0) && (cb.IsCollectingUndo())) {\n\t\tenteredModification++;\n\t\tif (!cb.IsReadOnly()) {\n\t\t\tconst bool startSavePoint = cb.IsSavePoint();\n\t\t\tbool multiLine = false;\n\t\t\tconst int steps = cb.StartUndo();\n\t\t\t//Platform::DebugPrintf(\"Steps=%d\\n\", steps);\n\t\t\tRange coalescedRemove;\t// Default is empty at 0\n\t\t\tfor (int step = 0; step < steps; step++) {\n\t\t\t\tconst Sci::Line prevLinesTotal = LinesTotal();\n\t\t\t\tconst Action action = cb.GetUndoStep();\n\t\t\t\tif (action.at == ActionType::remove) {\n\t\t\t\t\tNotifyModified(DocModification(\n\t\t\t\t\t\t\t\t\tModificationFlags::BeforeInsert | ModificationFlags::Undo, action));\n\t\t\t\t} else if (action.at == ActionType::container) {\n\t\t\t\t\tDocModification dm(ModificationFlags::Container | ModificationFlags::Undo);\n\t\t\t\t\tdm.token = action.position;\n\t\t\t\t\tNotifyModified(dm);\n\t\t\t\t} else {\n\t\t\t\t\tNotifyModified(DocModification(\n\t\t\t\t\t\t\t\t\tModificationFlags::BeforeDelete | ModificationFlags::Undo, action));\n\t\t\t\t}\n\t\t\t\tcb.PerformUndoStep();\n\t\t\t\tif (action.at != ActionType::container) {\n\t\t\t\t\tModifiedAt(action.position);\n\t\t\t\t\tnewPos = action.position;\n\t\t\t\t}\n\n\t\t\t\tModificationFlags modFlags = ModificationFlags::Undo;\n\t\t\t\t// With undo, an insertion action becomes a deletion notification\n\t\t\t\tif (action.at == ActionType::remove) {\n\t\t\t\t\tnewPos += action.lenData;\n\t\t\t\t\tmodFlags |= ModificationFlags::InsertText;\n\t\t\t\t\tif (coalescedRemove.Contains(action.position)) {\n\t\t\t\t\t\tcoalescedRemove.end += action.lenData;\n\t\t\t\t\t\tnewPos = coalescedRemove.end;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tcoalescedRemove = Range(action.position, action.position + action.lenData);\n\t\t\t\t\t}\n\t\t\t\t} else if (action.at == ActionType::insert) {\n\t\t\t\t\tmodFlags |= ModificationFlags::DeleteText;\n\t\t\t\t\tcoalescedRemove = Range();\n\t\t\t\t}\n\t\t\t\tif (steps > 1)\n\t\t\t\t\tmodFlags |= ModificationFlags::MultiStepUndoRedo;\n\t\t\t\tconst Sci::Line linesAdded = LinesTotal() - prevLinesTotal;\n\t\t\t\tif (linesAdded != 0)\n\t\t\t\t\tmultiLine = true;\n\t\t\t\tif (step == steps - 1) {\n\t\t\t\t\tmodFlags |= ModificationFlags::LastStepInUndoRedo;\n\t\t\t\t\tif (multiLine)\n\t\t\t\t\t\tmodFlags |= ModificationFlags::MultilineUndoRedo;\n\t\t\t\t}\n\t\t\t\tNotifyModified(DocModification(modFlags, action.position, action.lenData,\n\t\t\t\t\t\t\t\t\t\t\t   linesAdded, action.data));\n\t\t\t}\n\n\t\t\tconst bool endSavePoint = cb.IsSavePoint();\n\t\t\tif (startSavePoint != endSavePoint)\n\t\t\t\tNotifySavePoint(endSavePoint);\n\t\t}\n\t\tenteredModification--;\n\t}\n\treturn newPos;\n}\n\nSci::Position Document::Redo() {\n\tSci::Position newPos = -1;\n\tCheckReadOnly();\n\tif ((enteredModification == 0) && (cb.IsCollectingUndo())) {\n\t\tenteredModification++;\n\t\tif (!cb.IsReadOnly()) {\n\t\t\tconst bool startSavePoint = cb.IsSavePoint();\n\t\t\tbool multiLine = false;\n\t\t\tconst int steps = cb.StartRedo();\n\t\t\tfor (int step = 0; step < steps; step++) {\n\t\t\t\tconst Sci::Line prevLinesTotal = LinesTotal();\n\t\t\t\tconst Action action = cb.GetRedoStep();\n\t\t\t\tif (action.at == ActionType::insert) {\n\t\t\t\t\tNotifyModified(DocModification(\n\t\t\t\t\t\t\t\t\tModificationFlags::BeforeInsert | ModificationFlags::Redo, action));\n\t\t\t\t} else if (action.at == ActionType::container) {\n\t\t\t\t\tDocModification dm(ModificationFlags::Container | ModificationFlags::Redo);\n\t\t\t\t\tdm.token = action.position;\n\t\t\t\t\tNotifyModified(dm);\n\t\t\t\t} else {\n\t\t\t\t\tNotifyModified(DocModification(\n\t\t\t\t\t\t\t\t\tModificationFlags::BeforeDelete | ModificationFlags::Redo, action));\n\t\t\t\t}\n\t\t\t\tcb.PerformRedoStep();\n\t\t\t\tif (action.at != ActionType::container) {\n\t\t\t\t\tModifiedAt(action.position);\n\t\t\t\t\tnewPos = action.position;\n\t\t\t\t}\n\n\t\t\t\tModificationFlags modFlags = ModificationFlags::Redo;\n\t\t\t\tif (action.at == ActionType::insert) {\n\t\t\t\t\tnewPos += action.lenData;\n\t\t\t\t\tmodFlags |= ModificationFlags::InsertText;\n\t\t\t\t} else if (action.at == ActionType::remove) {\n\t\t\t\t\tmodFlags |= ModificationFlags::DeleteText;\n\t\t\t\t}\n\t\t\t\tif (steps > 1)\n\t\t\t\t\tmodFlags |= ModificationFlags::MultiStepUndoRedo;\n\t\t\t\tconst Sci::Line linesAdded = LinesTotal() - prevLinesTotal;\n\t\t\t\tif (linesAdded != 0)\n\t\t\t\t\tmultiLine = true;\n\t\t\t\tif (step == steps - 1) {\n\t\t\t\t\tmodFlags |= ModificationFlags::LastStepInUndoRedo;\n\t\t\t\t\tif (multiLine)\n\t\t\t\t\t\tmodFlags |= ModificationFlags::MultilineUndoRedo;\n\t\t\t\t}\n\t\t\t\tNotifyModified(\n\t\t\t\t\tDocModification(modFlags, action.position, action.lenData,\n\t\t\t\t\t\t\t\t\tlinesAdded, action.data));\n\t\t\t}\n\n\t\t\tconst bool endSavePoint = cb.IsSavePoint();\n\t\t\tif (startSavePoint != endSavePoint)\n\t\t\t\tNotifySavePoint(endSavePoint);\n\t\t}\n\t\tenteredModification--;\n\t}\n\treturn newPos;\n}\n\nvoid Document::EndUndoAction() noexcept {\n\tcb.EndUndoAction();\n\tif (UndoSequenceDepth() == 0) {\n\t\t// Broadcast notification to views to allow end of group processing.\n\t\t// NotifyGroupCompleted may throw (for memory exhaustion) but this method\n\t\t// may not as it is called in UndoGroup destructor so ignore exception.\n\t\ttry {\n\t\t\tNotifyGroupCompleted();\n\t\t} catch (...) {\n\t\t\t// Ignore any exception\n\t\t}\n\t}\n}\n\nint Document::UndoSequenceDepth() const noexcept {\n\treturn cb.UndoSequenceDepth();\n}\n\nvoid Document::DelChar(Sci::Position pos) {\n\tDeleteChars(pos, LenChar(pos));\n}\n\nvoid Document::DelCharBack(Sci::Position pos) {\n\tif (pos <= 0) {\n\t\treturn;\n\t} else if (IsCrLf(pos - 2)) {\n\t\tDeleteChars(pos - 2, 2);\n\t} else if (dbcsCodePage) {\n\t\tconst Sci::Position startChar = NextPosition(pos, -1);\n\t\tDeleteChars(startChar, pos - startChar);\n\t} else {\n\t\tDeleteChars(pos - 1, 1);\n\t}\n}\n\nint SCI_METHOD Document::GetLineIndentation(Sci_Position line) {\n\tint indent = 0;\n\tif ((line >= 0) && (line < LinesTotal())) {\n\t\tconst Sci::Position lineStart = LineStart(line);\n\t\tconst Sci::Position length = Length();\n\t\tfor (Sci::Position i = lineStart; i < length; i++) {\n\t\t\tconst char ch = cb.CharAt(i);\n\t\t\tif (ch == ' ')\n\t\t\t\tindent++;\n\t\t\telse if (ch == '\\t')\n\t\t\t\tindent = static_cast<int>(NextTab(indent, tabInChars));\n\t\t\telse\n\t\t\t\treturn indent;\n\t\t}\n\t}\n\treturn indent;\n}\n\nSci::Position Document::SetLineIndentation(Sci::Line line, Sci::Position indent) {\n\tconst int indentOfLine = GetLineIndentation(line);\n\tif (indent < 0)\n\t\tindent = 0;\n\tif (indent != indentOfLine) {\n\t\tconst std::string linebuf = CreateIndentation(indent, tabInChars, !useTabs);\n\t\tconst Sci::Position thisLineStart = LineStart(line);\n\t\tconst Sci::Position indentPos = GetLineIndentPosition(line);\n\t\tUndoGroup ug(this);\n\t\tDeleteChars(thisLineStart, indentPos - thisLineStart);\n\t\treturn thisLineStart + InsertString(thisLineStart, linebuf);\n\t} else {\n\t\treturn GetLineIndentPosition(line);\n\t}\n}\n\nSci::Position Document::GetLineIndentPosition(Sci::Line line) const {\n\tif (line < 0)\n\t\treturn 0;\n\tSci::Position pos = LineStart(line);\n\tconst Sci::Position length = Length();\n\twhile ((pos < length) && IsSpaceOrTab(cb.CharAt(pos))) {\n\t\tpos++;\n\t}\n\treturn pos;\n}\n\nSci::Position Document::GetColumn(Sci::Position pos) const {\n\tSci::Position column = 0;\n\tconst Sci::Line line = SciLineFromPosition(pos);\n\tif ((line >= 0) && (line < LinesTotal())) {\n\t\tfor (Sci::Position i = LineStart(line); i < pos;) {\n\t\t\tconst char ch = cb.CharAt(i);\n\t\t\tif (ch == '\\t') {\n\t\t\t\tcolumn = NextTab(column, tabInChars);\n\t\t\t\ti++;\n\t\t\t} else if (ch == '\\r') {\n\t\t\t\treturn column;\n\t\t\t} else if (ch == '\\n') {\n\t\t\t\treturn column;\n\t\t\t} else if (i >= Length()) {\n\t\t\t\treturn column;\n\t\t\t} else if (UTF8IsAscii(ch)) {\n\t\t\t\tcolumn++;\n\t\t\t\ti++;\n\t\t\t} else {\n\t\t\t\tcolumn++;\n\t\t\t\ti = NextPosition(i, 1);\n\t\t\t}\n\t\t}\n\t}\n\treturn column;\n}\n\nSci::Position Document::CountCharacters(Sci::Position startPos, Sci::Position endPos) const noexcept {\n\tstartPos = MovePositionOutsideChar(startPos, 1, false);\n\tendPos = MovePositionOutsideChar(endPos, -1, false);\n\tSci::Position count = 0;\n\tSci::Position i = startPos;\n\twhile (i < endPos) {\n\t\tcount++;\n\t\ti = NextPosition(i, 1);\n\t}\n\treturn count;\n}\n\nSci::Position Document::CountUTF16(Sci::Position startPos, Sci::Position endPos) const noexcept {\n\tstartPos = MovePositionOutsideChar(startPos, 1, false);\n\tendPos = MovePositionOutsideChar(endPos, -1, false);\n\tSci::Position count = 0;\n\tSci::Position i = startPos;\n\twhile (i < endPos) {\n\t\tcount++;\n\t\tconst Sci::Position next = NextPosition(i, 1);\n\t\tif ((next - i) > 3)\n\t\t\tcount++;\n\t\ti = next;\n\t}\n\treturn count;\n}\n\nSci::Position Document::FindColumn(Sci::Line line, Sci::Position column) {\n\tSci::Position position = LineStart(line);\n\tif ((line >= 0) && (line < LinesTotal())) {\n\t\tSci::Position columnCurrent = 0;\n\t\twhile ((columnCurrent < column) && (position < Length())) {\n\t\t\tconst char ch = cb.CharAt(position);\n\t\t\tif (ch == '\\t') {\n\t\t\t\tcolumnCurrent = NextTab(columnCurrent, tabInChars);\n\t\t\t\tif (columnCurrent > column)\n\t\t\t\t\treturn position;\n\t\t\t\tposition++;\n\t\t\t} else if (ch == '\\r') {\n\t\t\t\treturn position;\n\t\t\t} else if (ch == '\\n') {\n\t\t\t\treturn position;\n\t\t\t} else {\n\t\t\t\tcolumnCurrent++;\n\t\t\t\tposition = NextPosition(position, 1);\n\t\t\t}\n\t\t}\n\t}\n\treturn position;\n}\n\nvoid Document::Indent(bool forwards, Sci::Line lineBottom, Sci::Line lineTop) {\n\t// Dedent - suck white space off the front of the line to dedent by equivalent of a tab\n\tfor (Sci::Line line = lineBottom; line >= lineTop; line--) {\n\t\tconst Sci::Position indentOfLine = GetLineIndentation(line);\n\t\tif (forwards) {\n\t\t\tif (LineStart(line) < LineEnd(line)) {\n\t\t\t\tSetLineIndentation(line, indentOfLine + IndentSize());\n\t\t\t}\n\t\t} else {\n\t\t\tSetLineIndentation(line, indentOfLine - IndentSize());\n\t\t}\n\t}\n}\n\nnamespace {\n\nconstexpr std::string_view EOLForMode(EndOfLine eolMode) noexcept {\n\tswitch (eolMode) {\n\tcase EndOfLine::CrLf:\n\t\treturn \"\\r\\n\";\n\tcase EndOfLine::Cr:\n\t\treturn \"\\r\";\n\tdefault:\n\t\treturn \"\\n\";\n\t}\n}\n\n}\n\n// Convert line endings for a piece of text to a particular mode.\n// Stop at len or when a NUL is found.\nstd::string Document::TransformLineEnds(const char *s, size_t len, EndOfLine eolModeWanted) {\n\tstd::string dest;\n\tconst std::string_view eol = EOLForMode(eolModeWanted);\n\tfor (size_t i = 0; (i < len) && (s[i]); i++) {\n\t\tif (s[i] == '\\n' || s[i] == '\\r') {\n\t\t\tdest.append(eol);\n\t\t\tif ((s[i] == '\\r') && (i+1 < len) && (s[i+1] == '\\n')) {\n\t\t\t\ti++;\n\t\t\t}\n\t\t} else {\n\t\t\tdest.push_back(s[i]);\n\t\t}\n\t}\n\treturn dest;\n}\n\nvoid Document::ConvertLineEnds(EndOfLine eolModeSet) {\n\tUndoGroup ug(this);\n\n\tfor (Sci::Position pos = 0; pos < Length(); pos++) {\n\t\tconst char ch = cb.CharAt(pos);\n\t\tif (ch == '\\r') {\n\t\t\tif (cb.CharAt(pos + 1) == '\\n') {\n\t\t\t\t// CRLF\n\t\t\t\tif (eolModeSet == EndOfLine::Cr) {\n\t\t\t\t\tDeleteChars(pos + 1, 1); // Delete the LF\n\t\t\t\t} else if (eolModeSet == EndOfLine::Lf) {\n\t\t\t\t\tDeleteChars(pos, 1); // Delete the CR\n\t\t\t\t} else {\n\t\t\t\t\tpos++;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// CR\n\t\t\t\tif (eolModeSet == EndOfLine::CrLf) {\n\t\t\t\t\tpos += InsertString(pos + 1, \"\\n\", 1); // Insert LF\n\t\t\t\t} else if (eolModeSet == EndOfLine::Lf) {\n\t\t\t\t\tpos += InsertString(pos, \"\\n\", 1); // Insert LF\n\t\t\t\t\tDeleteChars(pos, 1); // Delete CR\n\t\t\t\t\tpos--;\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (ch == '\\n') {\n\t\t\t// LF\n\t\t\tif (eolModeSet == EndOfLine::CrLf) {\n\t\t\t\tpos += InsertString(pos, \"\\r\", 1); // Insert CR\n\t\t\t} else if (eolModeSet == EndOfLine::Cr) {\n\t\t\t\tpos += InsertString(pos, \"\\r\", 1); // Insert CR\n\t\t\t\tDeleteChars(pos, 1); // Delete LF\n\t\t\t\tpos--;\n\t\t\t}\n\t\t}\n\t}\n\n}\n\nstd::string_view Document::EOLString() const noexcept {\n\treturn EOLForMode(eolMode);\n}\n\nDocumentOption Document::Options() const noexcept {\n\treturn (IsLarge() ? DocumentOption::TextLarge : DocumentOption::Default) |\n\t\t(cb.HasStyles() ? DocumentOption::Default : DocumentOption::StylesNone);\n}\n\nbool Document::IsWhiteLine(Sci::Line line) const {\n\tSci::Position currentChar = LineStart(line);\n\tconst Sci::Position endLine = LineEnd(line);\n\twhile (currentChar < endLine) {\n\t\tif (!IsSpaceOrTab(cb.CharAt(currentChar))) {\n\t\t\treturn false;\n\t\t}\n\t\t++currentChar;\n\t}\n\treturn true;\n}\n\nSci::Position Document::ParaUp(Sci::Position pos) const {\n\tSci::Line line = SciLineFromPosition(pos);\n\tconst Sci::Position start = LineStart(line);\n\tif (pos == start) {\n\t\tline--;\n\t}\n\twhile (line >= 0 && IsWhiteLine(line)) { // skip empty lines\n\t\tline--;\n\t}\n\twhile (line >= 0 && !IsWhiteLine(line)) { // skip non-empty lines\n\t\tline--;\n\t}\n\tline++;\n\treturn LineStart(line);\n}\n\nSci::Position Document::ParaDown(Sci::Position pos) const {\n\tSci::Line line = SciLineFromPosition(pos);\n\twhile (line < LinesTotal() && !IsWhiteLine(line)) { // skip non-empty lines\n\t\tline++;\n\t}\n\twhile (line < LinesTotal() && IsWhiteLine(line)) { // skip empty lines\n\t\tline++;\n\t}\n\tif (line < LinesTotal())\n\t\treturn LineStart(line);\n\telse // end of a document\n\t\treturn LineEnd(line-1);\n}\n\nCharacterClass Document::WordCharacterClass(unsigned int ch) const {\n\tif (dbcsCodePage && (ch >= 0x80)) {\n\t\tif (CpUtf8 == dbcsCodePage) {\n\t\t\t// Use hard coded Unicode class\n\t\t\tconst CharacterCategory cc = charMap.CategoryFor(ch);\n\t\t\tswitch (cc) {\n\n\t\t\t\t// Separator, Line/Paragraph\n\t\t\tcase ccZl:\n\t\t\tcase ccZp:\n\t\t\t\treturn CharacterClass::newLine;\n\n\t\t\t\t// Separator, Space\n\t\t\tcase ccZs:\n\t\t\t\t// Other\n\t\t\tcase ccCc:\n\t\t\tcase ccCf:\n\t\t\tcase ccCs:\n\t\t\tcase ccCo:\n\t\t\tcase ccCn:\n\t\t\t\treturn CharacterClass::space;\n\n\t\t\t\t// Letter\n\t\t\tcase ccLu:\n\t\t\tcase ccLl:\n\t\t\tcase ccLt:\n\t\t\tcase ccLm:\n\t\t\tcase ccLo:\n\t\t\t\t// Number\n\t\t\tcase ccNd:\n\t\t\tcase ccNl:\n\t\t\tcase ccNo:\n\t\t\t\t// Mark - includes combining diacritics\n\t\t\tcase ccMn:\n\t\t\tcase ccMc:\n\t\t\tcase ccMe:\n\t\t\t\treturn CharacterClass::word;\n\n\t\t\t\t// Punctuation\n\t\t\tcase ccPc:\n\t\t\tcase ccPd:\n\t\t\tcase ccPs:\n\t\t\tcase ccPe:\n\t\t\tcase ccPi:\n\t\t\tcase ccPf:\n\t\t\tcase ccPo:\n\t\t\t\t// Symbol\n\t\t\tcase ccSm:\n\t\t\tcase ccSc:\n\t\t\tcase ccSk:\n\t\t\tcase ccSo:\n\t\t\t\treturn CharacterClass::punctuation;\n\n\t\t\t}\n\t\t} else {\n\t\t\t// Asian DBCS\n\t\t\treturn CharacterClass::word;\n\t\t}\n\t}\n\treturn charClass.GetClass(static_cast<unsigned char>(ch));\n}\n\n/**\n * Used by commands that want to select whole words.\n * Finds the start of word at pos when delta < 0 or the end of the word when delta >= 0.\n */\nSci::Position Document::ExtendWordSelect(Sci::Position pos, int delta, bool onlyWordCharacters) const {\n\tCharacterClass ccStart = CharacterClass::word;\n\tif (delta < 0) {\n\t\tif (!onlyWordCharacters) {\n\t\t\tconst CharacterExtracted ce = CharacterBefore(pos);\n\t\t\tccStart = WordCharacterClass(ce.character);\n\t\t}\n\t\twhile (pos > 0) {\n\t\t\tconst CharacterExtracted ce = CharacterBefore(pos);\n\t\t\tif (WordCharacterClass(ce.character) != ccStart)\n\t\t\t\tbreak;\n\t\t\tpos -= ce.widthBytes;\n\t\t}\n\t} else {\n\t\tif (!onlyWordCharacters && pos < LengthNoExcept()) {\n\t\t\tconst CharacterExtracted ce = CharacterAfter(pos);\n\t\t\tccStart = WordCharacterClass(ce.character);\n\t\t}\n\t\twhile (pos < LengthNoExcept()) {\n\t\t\tconst CharacterExtracted ce = CharacterAfter(pos);\n\t\t\tif (WordCharacterClass(ce.character) != ccStart)\n\t\t\t\tbreak;\n\t\t\tpos += ce.widthBytes;\n\t\t}\n\t}\n\treturn MovePositionOutsideChar(pos, delta, true);\n}\n\n/**\n * Find the start of the next word in either a forward (delta >= 0) or backwards direction\n * (delta < 0).\n * This is looking for a transition between character classes although there is also some\n * additional movement to transit white space.\n * Used by cursor movement by word commands.\n */\nSci::Position Document::NextWordStart(Sci::Position pos, int delta) const {\n\tif (delta < 0) {\n\t\twhile (pos > 0) {\n\t\t\tconst CharacterExtracted ce = CharacterBefore(pos);\n\t\t\tif (WordCharacterClass(ce.character) != CharacterClass::space)\n\t\t\t\tbreak;\n\t\t\tpos -= ce.widthBytes;\n\t\t}\n\t\tif (pos > 0) {\n\t\t\tCharacterExtracted ce = CharacterBefore(pos);\n\t\t\tconst CharacterClass ccStart = WordCharacterClass(ce.character);\n\t\t\twhile (pos > 0) {\n\t\t\t\tce = CharacterBefore(pos);\n\t\t\t\tif (WordCharacterClass(ce.character) != ccStart)\n\t\t\t\t\tbreak;\n\t\t\t\tpos -= ce.widthBytes;\n\t\t\t}\n\t\t}\n\t} else {\n\t\tCharacterExtracted ce = CharacterAfter(pos);\n\t\tconst CharacterClass ccStart = WordCharacterClass(ce.character);\n\t\twhile (pos < LengthNoExcept()) {\n\t\t\tce = CharacterAfter(pos);\n\t\t\tif (WordCharacterClass(ce.character) != ccStart)\n\t\t\t\tbreak;\n\t\t\tpos += ce.widthBytes;\n\t\t}\n\t\twhile (pos < LengthNoExcept()) {\n\t\t\tce = CharacterAfter(pos);\n\t\t\tif (WordCharacterClass(ce.character) != CharacterClass::space)\n\t\t\t\tbreak;\n\t\t\tpos += ce.widthBytes;\n\t\t}\n\t}\n\treturn pos;\n}\n\n/**\n * Find the end of the next word in either a forward (delta >= 0) or backwards direction\n * (delta < 0).\n * This is looking for a transition between character classes although there is also some\n * additional movement to transit white space.\n * Used by cursor movement by word commands.\n */\nSci::Position Document::NextWordEnd(Sci::Position pos, int delta) const {\n\tif (delta < 0) {\n\t\tif (pos > 0) {\n\t\t\tCharacterExtracted ce = CharacterBefore(pos);\n\t\t\tconst CharacterClass ccStart = WordCharacterClass(ce.character);\n\t\t\tif (ccStart != CharacterClass::space) {\n\t\t\t\twhile (pos > 0) {\n\t\t\t\t\tce = CharacterBefore(pos);\n\t\t\t\t\tif (WordCharacterClass(ce.character) != ccStart)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tpos -= ce.widthBytes;\n\t\t\t\t}\n\t\t\t}\n\t\t\twhile (pos > 0) {\n\t\t\t\tce = CharacterBefore(pos);\n\t\t\t\tif (WordCharacterClass(ce.character) != CharacterClass::space)\n\t\t\t\t\tbreak;\n\t\t\t\tpos -= ce.widthBytes;\n\t\t\t}\n\t\t}\n\t} else {\n\t\twhile (pos < LengthNoExcept()) {\n\t\t\tconst CharacterExtracted ce = CharacterAfter(pos);\n\t\t\tif (WordCharacterClass(ce.character) != CharacterClass::space)\n\t\t\t\tbreak;\n\t\t\tpos += ce.widthBytes;\n\t\t}\n\t\tif (pos < LengthNoExcept()) {\n\t\t\tCharacterExtracted ce = CharacterAfter(pos);\n\t\t\tconst CharacterClass ccStart = WordCharacterClass(ce.character);\n\t\t\twhile (pos < LengthNoExcept()) {\n\t\t\t\tce = CharacterAfter(pos);\n\t\t\t\tif (WordCharacterClass(ce.character) != ccStart)\n\t\t\t\t\tbreak;\n\t\t\t\tpos += ce.widthBytes;\n\t\t\t}\n\t\t}\n\t}\n\treturn pos;\n}\n\nnamespace {\n\nconstexpr bool IsWordEdge(CharacterClass cc, CharacterClass ccNext) noexcept {\n\treturn (cc != ccNext) &&\n\t\t(cc == CharacterClass::word || cc == CharacterClass::punctuation);\n}\n\n}\n\n/**\n * Check that the character at the given position is a word or punctuation character and that\n * the previous character is of a different character class.\n */\nbool Document::IsWordStartAt(Sci::Position pos) const {\n\tif (pos >= LengthNoExcept())\n\t\treturn false;\n\tif (pos >= 0) {\n\t\tconst CharacterExtracted cePos = CharacterAfter(pos);\n\t\t// At start of document, treat as if space before so can be word start\n\t\tconst CharacterExtracted cePrev = (pos > 0) ?\n\t\t\tCharacterBefore(pos) : CharacterExtracted(' ', 1);\n\t\treturn IsWordEdge(WordCharacterClass(cePos.character), WordCharacterClass(cePrev.character));\n\t}\n\treturn true;\n}\n\n/**\n * Check that the character before the given position is a word or punctuation character and that\n * the next character is of a different character class.\n */\nbool Document::IsWordEndAt(Sci::Position pos) const {\n\tif (pos <= 0)\n\t\treturn false;\n\tif (pos <= LengthNoExcept()) {\n\t\t// At end of document, treat as if space after so can be word end\n\t\tconst CharacterExtracted cePos = (pos < LengthNoExcept()) ?\n\t\t\tCharacterAfter(pos) : CharacterExtracted(' ', 1);\n\t\tconst CharacterExtracted cePrev = CharacterBefore(pos);\n\t\treturn IsWordEdge(WordCharacterClass(cePrev.character), WordCharacterClass(cePos.character));\n\t}\n\treturn true;\n}\n\n/**\n * Check that the given range is has transitions between character classes at both\n * ends and where the characters on the inside are word or punctuation characters.\n */\nbool Document::IsWordAt(Sci::Position start, Sci::Position end) const {\n\treturn (start < end) && IsWordStartAt(start) && IsWordEndAt(end);\n}\n\nbool Document::MatchesWordOptions(bool word, bool wordStart, Sci::Position pos, Sci::Position length) const {\n\treturn (!word && !wordStart) ||\n\t\t\t(word && IsWordAt(pos, pos + length)) ||\n\t\t\t(wordStart && IsWordStartAt(pos));\n}\n\nbool Document::HasCaseFolder() const noexcept {\n\treturn pcf != nullptr;\n}\n\nvoid Document::SetCaseFolder(std::unique_ptr<CaseFolder> pcf_) noexcept {\n\tpcf = std::move(pcf_);\n}\n\nCharacterExtracted Document::ExtractCharacter(Sci::Position position) const noexcept {\n\tconst unsigned char leadByte = cb.UCharAt(position);\n\tif (UTF8IsAscii(leadByte)) {\n\t\t// Common case: ASCII character\n\t\treturn CharacterExtracted(leadByte, 1);\n\t}\n\tconst int widthCharBytes = UTF8BytesOfLead[leadByte];\n\tunsigned char charBytes[UTF8MaxBytes] = { leadByte, 0, 0, 0 };\n\tfor (int b=1; b<widthCharBytes; b++)\n\t\tcharBytes[b] = cb.UCharAt(position + b);\n\treturn CharacterExtracted(charBytes, widthCharBytes);\n}\n\nnamespace {\n\n// Equivalent of memchr over the split view\nptrdiff_t SplitFindChar(const SplitView &view, size_t start, size_t length, int ch) noexcept {\n\tsize_t range1Length = 0;\n\tif (start < view.length1) {\n\t\trange1Length = std::min(length, view.length1 - start);\n\t\tconst char *match = static_cast<const char *>(memchr(view.segment1 + start, ch, range1Length));\n\t\tif (match) {\n\t\t\treturn match - view.segment1;\n\t\t}\n\t\tstart += range1Length;\n\t}\n\tconst char *match2 = static_cast<const char *>(memchr(view.segment2 + start, ch, length - range1Length));\n\tif (match2) {\n\t\treturn match2 - view.segment2;\n\t}\n\treturn -1;\n}\n\n// Equivalent of memcmp over the split view\n// This does not call memcmp as search texts are commonly too short to overcome the\n// call overhead.\nbool SplitMatch(const SplitView &view, size_t start, std::string_view text) noexcept {\n\tfor (size_t i = 0; i < text.length(); i++) {\n\t\tif (view.CharAt(i + start) != text[i]) {\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn true;\n}\n\n}\n\n/**\n * Find text in document, supporting both forward and backward\n * searches (just pass minPos > maxPos to do a backward search)\n * Has not been tested with backwards DBCS searches yet.\n */\nSci::Position Document::FindText(Sci::Position minPos, Sci::Position maxPos, const char *search,\n                        FindOption flags, Sci::Position *length) {\n\tif (*length <= 0)\n\t\treturn minPos;\n\tconst bool caseSensitive = FlagSet(flags, FindOption::MatchCase);\n\tconst bool word = FlagSet(flags, FindOption::WholeWord);\n\tconst bool wordStart = FlagSet(flags, FindOption::WordStart);\n\tconst bool regExp = FlagSet(flags, FindOption::RegExp);\n\tif (regExp) {\n\t\tif (!regex)\n\t\t\tregex = std::unique_ptr<RegexSearchBase>(CreateRegexSearch(&charClass));\n\t\treturn regex->FindText(this, minPos, maxPos, search, caseSensitive, word, wordStart, flags, length);\n\t} else {\n\n\t\tconst bool forward = minPos <= maxPos;\n\t\tconst int increment = forward ? 1 : -1;\n\n\t\t// Range endpoints should not be inside DBCS characters, but just in case, move them.\n\t\tconst Sci::Position startPos = MovePositionOutsideChar(minPos, increment, false);\n\t\tconst Sci::Position endPos = MovePositionOutsideChar(maxPos, increment, false);\n\n\t\t// Compute actual search ranges needed\n\t\tconst Sci::Position lengthFind = *length;\n\n\t\t//Platform::DebugPrintf(\"Find %d %d %s %d\\n\", startPos, endPos, ft->lpstrText, lengthFind);\n\t\tconst Sci::Position limitPos = std::max(startPos, endPos);\n\t\tSci::Position pos = startPos;\n\t\tif (!forward) {\n\t\t\t// Back all of a character\n\t\t\tpos = NextPosition(pos, increment);\n\t\t}\n\t\tconst SplitView cbView = cb.AllView();\n\t\tif (caseSensitive) {\n\t\t\tconst Sci::Position endSearch = (startPos <= endPos) ? endPos - lengthFind + 1 : endPos;\n\t\t\tconst unsigned char charStartSearch =  search[0];\n\t\t\tif (forward && ((0 == dbcsCodePage) || (CpUtf8 == dbcsCodePage && !UTF8IsTrailByte(charStartSearch)))) {\n\t\t\t\t// This is a fast case where there is no need to test byte values to iterate\n\t\t\t\t// so becomes the equivalent of a memchr+memcmp loop.\n\t\t\t\t// UTF-8 search will not be self-synchronizing when starts with trail byte\n\t\t\t\tconst std::string_view suffix(search + 1, lengthFind - 1);\n\t\t\t\twhile (pos < endSearch) {\n\t\t\t\t\tpos = SplitFindChar(cbView, pos, limitPos - pos, charStartSearch);\n\t\t\t\t\tif (pos < 0) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tif (SplitMatch(cbView, pos + 1, suffix) && MatchesWordOptions(word, wordStart, pos, lengthFind)) {\n\t\t\t\t\t\treturn pos;\n\t\t\t\t\t}\n\t\t\t\t\tpos++;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\twhile (forward ? (pos < endSearch) : (pos >= endSearch)) {\n\t\t\t\t\tconst unsigned char leadByte = cbView.CharAt(pos);\n\t\t\t\t\tif (leadByte == charStartSearch) {\n\t\t\t\t\t\tbool found = (pos + lengthFind) <= limitPos;\n\t\t\t\t\t\t// SplitMatch could be called here but it is slower with g++ -O2\n\t\t\t\t\t\tfor (int indexSearch = 1; (indexSearch < lengthFind) && found; indexSearch++) {\n\t\t\t\t\t\t\tfound = cbView.CharAt(pos + indexSearch) == search[indexSearch];\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (found && MatchesWordOptions(word, wordStart, pos, lengthFind)) {\n\t\t\t\t\t\t\treturn pos;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (forward && UTF8IsAscii(leadByte)) {\n\t\t\t\t\t\tpos++;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (dbcsCodePage) {\n\t\t\t\t\t\t\tif (!NextCharacter(pos, increment)) {\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tpos += increment;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (CpUtf8 == dbcsCodePage) {\n\t\t\tconstexpr size_t maxFoldingExpansion = 4;\n\t\t\tstd::vector<char> searchThing((lengthFind+1) * UTF8MaxBytes * maxFoldingExpansion + 1);\n\t\t\tconst size_t lenSearch =\n\t\t\t\tpcf->Fold(searchThing.data(), searchThing.size(), search, lengthFind);\n\t\t\twhile (forward ? (pos < endPos) : (pos >= endPos)) {\n\t\t\t\tint widthFirstCharacter = 1;\n\t\t\t\tSci::Position posIndexDocument = pos;\n\t\t\t\tsize_t indexSearch = 0;\n\t\t\t\tbool characterMatches = true;\n\t\t\t\twhile (indexSearch < lenSearch) {\n\t\t\t\t\tconst unsigned char leadByte = cbView.CharAt(posIndexDocument);\n\t\t\t\t\tint widthChar = 1;\n\t\t\t\t\tsize_t lenFlat = 1;\n\t\t\t\t\tif (UTF8IsAscii(leadByte)) {\n\t\t\t\t\t\tif ((posIndexDocument + 1) > limitPos) {\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcharacterMatches = searchThing[indexSearch] == MakeLowerCase(leadByte);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tchar bytes[UTF8MaxBytes]{ static_cast<char>(leadByte) };\n\t\t\t\t\t\tconst int widthCharBytes = UTF8BytesOfLead[leadByte];\n\t\t\t\t\t\tfor (int b = 1; b < widthCharBytes; b++) {\n\t\t\t\t\t\t\tbytes[b] = cbView.CharAt(posIndexDocument + b);\n\t\t\t\t\t\t}\n\t\t\t\t\t\twidthChar = UTF8Classify(bytes, widthCharBytes) & UTF8MaskWidth;\n\t\t\t\t\t\tif (!indexSearch) {\t// First character\n\t\t\t\t\t\t\twidthFirstCharacter = widthChar;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif ((posIndexDocument + widthChar) > limitPos) {\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tchar folded[UTF8MaxBytes * maxFoldingExpansion + 1];\n\t\t\t\t\t\tlenFlat = pcf->Fold(folded, sizeof(folded), bytes, widthChar);\n\t\t\t\t\t\t// memcmp may examine lenFlat bytes in both arguments so assert it doesn't read past end of searchThing\n\t\t\t\t\t\tassert((indexSearch + lenFlat) <= searchThing.size());\n\t\t\t\t\t\t// Does folded match the buffer\n\t\t\t\t\t\tcharacterMatches = 0 == memcmp(folded, searchThing.data() + indexSearch, lenFlat);\n\t\t\t\t\t}\n\t\t\t\t\tif (!characterMatches) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tposIndexDocument += widthChar;\n\t\t\t\t\tindexSearch += lenFlat;\n\t\t\t\t}\n\t\t\t\tif (characterMatches && (indexSearch == lenSearch)) {\n\t\t\t\t\tif (MatchesWordOptions(word, wordStart, pos, posIndexDocument - pos)) {\n\t\t\t\t\t\t*length = posIndexDocument - pos;\n\t\t\t\t\t\treturn pos;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (forward) {\n\t\t\t\t\tpos += widthFirstCharacter;\n\t\t\t\t} else {\n\t\t\t\t\tif (!NextCharacter(pos, increment)) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (dbcsCodePage) {\n\t\t\tconstexpr size_t maxBytesCharacter = 2;\n\t\t\tconstexpr size_t maxFoldingExpansion = 4;\n\t\t\tstd::vector<char> searchThing((lengthFind+1) * maxBytesCharacter * maxFoldingExpansion + 1);\n\t\t\tconst size_t lenSearch = pcf->Fold(searchThing.data(), searchThing.size(), search, lengthFind);\n\t\t\twhile (forward ? (pos < endPos) : (pos >= endPos)) {\n\t\t\t\tint widthFirstCharacter = 0;\n\t\t\t\tSci::Position indexDocument = 0;\n\t\t\t\tsize_t indexSearch = 0;\n\t\t\t\tbool characterMatches = true;\n\t\t\t\twhile (((pos + indexDocument) < limitPos) &&\n\t\t\t\t\t(indexSearch < lenSearch)) {\n\t\t\t\t\tconst unsigned char leadByte = cbView.CharAt(pos + indexDocument);\n\t\t\t\t\tconst int widthChar = (!UTF8IsAscii(leadByte) && IsDBCSLeadByteNoExcept(leadByte)) ? 2 : 1;\n\t\t\t\t\tif (!widthFirstCharacter) {\n\t\t\t\t\t\twidthFirstCharacter = widthChar;\n\t\t\t\t\t}\n\t\t\t\t\tif ((pos + indexDocument + widthChar) > limitPos) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tsize_t lenFlat = 1;\n\t\t\t\t\tif (widthChar == 1) {\n\t\t\t\t\t\tcharacterMatches = searchThing[indexSearch] == MakeLowerCase(leadByte);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tconst char bytes[maxBytesCharacter + 1] {\n\t\t\t\t\t\t\tstatic_cast<char>(leadByte),\n\t\t\t\t\t\t\tcbView.CharAt(pos + indexDocument + 1)\n\t\t\t\t\t\t};\n\t\t\t\t\t\tchar folded[maxBytesCharacter * maxFoldingExpansion + 1];\n\t\t\t\t\t\tlenFlat = pcf->Fold(folded, sizeof(folded), bytes, widthChar);\n\t\t\t\t\t\t// memcmp may examine lenFlat bytes in both arguments so assert it doesn't read past end of searchThing\n\t\t\t\t\t\tassert((indexSearch + lenFlat) <= searchThing.size());\n\t\t\t\t\t\t// Does folded match the buffer\n\t\t\t\t\t\tcharacterMatches = 0 == memcmp(folded, searchThing.data() + indexSearch, lenFlat);\n\t\t\t\t\t}\n\t\t\t\t\tif (!characterMatches) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tindexDocument += widthChar;\n\t\t\t\t\tindexSearch += lenFlat;\n\t\t\t\t}\n\t\t\t\tif (characterMatches && (indexSearch == lenSearch)) {\n\t\t\t\t\tif (MatchesWordOptions(word, wordStart, pos, indexDocument)) {\n\t\t\t\t\t\t*length = indexDocument;\n\t\t\t\t\t\treturn pos;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (forward) {\n\t\t\t\t\tpos += widthFirstCharacter;\n\t\t\t\t} else {\n\t\t\t\t\tif (!NextCharacter(pos, increment)) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tconst Sci::Position endSearch = (startPos <= endPos) ? endPos - lengthFind + 1 : endPos;\n\t\t\tstd::vector<char> searchThing(lengthFind + 1);\n\t\t\tpcf->Fold(searchThing.data(), searchThing.size(), search, lengthFind);\n\t\t\twhile (forward ? (pos < endSearch) : (pos >= endSearch)) {\n\t\t\t\tbool found = (pos + lengthFind) <= limitPos;\n\t\t\t\tfor (int indexSearch = 0; (indexSearch < lengthFind) && found; indexSearch++) {\n\t\t\t\t\tconst char ch = cbView.CharAt(pos + indexSearch);\n\t\t\t\t\tconst char chTest = searchThing[indexSearch];\n\t\t\t\t\tif (UTF8IsAscii(ch)) {\n\t\t\t\t\t\tfound = chTest == MakeLowerCase(ch);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tchar folded[2];\n\t\t\t\t\t\tpcf->Fold(folded, sizeof(folded), &ch, 1);\n\t\t\t\t\t\tfound = folded[0] == chTest;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (found && MatchesWordOptions(word, wordStart, pos, lengthFind)) {\n\t\t\t\t\treturn pos;\n\t\t\t\t}\n\t\t\t\tpos += increment;\n\t\t\t}\n\t\t}\n\t}\n\t//Platform::DebugPrintf(\"Not found\\n\");\n\treturn -1;\n}\n\nconst char *Document::SubstituteByPosition(const char *text, Sci::Position *length) {\n\tif (regex) {\n\t\treturn regex->SubstituteByPosition(this, text, length);\n\t}\n\treturn nullptr;\n}\n\nLineCharacterIndexType Document::LineCharacterIndex() const noexcept {\n\treturn cb.LineCharacterIndex();\n}\n\nvoid Document::AllocateLineCharacterIndex(LineCharacterIndexType lineCharacterIndex) {\n\tcb.AllocateLineCharacterIndex(lineCharacterIndex);\n}\n\nvoid Document::ReleaseLineCharacterIndex(LineCharacterIndexType lineCharacterIndex) {\n\tcb.ReleaseLineCharacterIndex(lineCharacterIndex);\n}\n\nSci::Line Document::LinesTotal() const noexcept {\n\treturn cb.Lines();\n}\n\nvoid Document::AllocateLines(Sci::Line lines) {\n\tcb.AllocateLines(lines);\n}\n\nvoid Document::SetDefaultCharClasses(bool includeWordClass) {\n\tcharClass.SetDefaultCharClasses(includeWordClass);\n}\n\nvoid Document::SetCharClasses(const unsigned char *chars, CharacterClass newCharClass) {\n\tcharClass.SetCharClasses(chars, newCharClass);\n}\n\nint Document::GetCharsOfClass(CharacterClass characterClass, unsigned char *buffer) const {\n\treturn charClass.GetCharsOfClass(characterClass, buffer);\n}\n\nvoid Document::SetCharacterCategoryOptimization(int countCharacters) {\n\tcharMap.Optimize(countCharacters);\n}\n\nint Document::CharacterCategoryOptimization() const noexcept {\n\treturn charMap.Size();\n}\n\nvoid SCI_METHOD Document::StartStyling(Sci_Position position) {\n\tendStyled = position;\n}\n\nbool SCI_METHOD Document::SetStyleFor(Sci_Position length, char style) {\n\tif (enteredStyling != 0) {\n\t\treturn false;\n\t}\n\tenteredStyling++;\n\tconst Sci::Position prevEndStyled = endStyled;\n\tif (cb.SetStyleFor(endStyled, length, style)) {\n\t\tconst DocModification mh(ModificationFlags::ChangeStyle | ModificationFlags::User,\n\t\t\t                prevEndStyled, length);\n\t\tNotifyModified(mh);\n\t}\n\tendStyled += length;\n\tenteredStyling--;\n\treturn true;\n}\n\nbool SCI_METHOD Document::SetStyles(Sci_Position length, const char *styles) {\n\tif (enteredStyling != 0) {\n\t\treturn false;\n\t}\n\tenteredStyling++;\n\tbool didChange = false;\n\tSci::Position startMod = 0;\n\tSci::Position endMod = 0;\n\tfor (int iPos = 0; iPos < length; iPos++, endStyled++) {\n\t\tPLATFORM_ASSERT(endStyled < Length());\n\t\tif (cb.SetStyleAt(endStyled, styles[iPos])) {\n\t\t\tif (!didChange) {\n\t\t\t\tstartMod = endStyled;\n\t\t\t}\n\t\t\tdidChange = true;\n\t\t\tendMod = endStyled;\n\t\t}\n\t}\n\tif (didChange) {\n\t\tconst DocModification mh(ModificationFlags::ChangeStyle | ModificationFlags::User,\n\t\t\t                startMod, endMod - startMod + 1);\n\t\tNotifyModified(mh);\n\t}\n\tenteredStyling--;\n\treturn true;\n}\n\nvoid Document::EnsureStyledTo(Sci::Position pos) {\n\tif ((enteredStyling == 0) && (pos > GetEndStyled())) {\n\t\tIncrementStyleClock();\n\t\tif (pli && !pli->UseContainerLexing()) {\n\t\t\tconst Sci::Position endStyledTo = LineStartPosition(GetEndStyled());\n\t\t\tpli->Colourise(endStyledTo, pos);\n\t\t} else {\n\t\t\t// Ask the watchers to style, and stop as soon as one responds.\n\t\t\tfor (std::vector<WatcherWithUserData>::iterator it = watchers.begin();\n\t\t\t\t(pos > GetEndStyled()) && (it != watchers.end()); ++it) {\n\t\t\t\tit->watcher->NotifyStyleNeeded(this, it->userData, pos);\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid Document::StyleToAdjustingLineDuration(Sci::Position pos) {\n\tconst Sci::Position stylingStart = GetEndStyled();\n\tElapsedPeriod epStyling;\n\tEnsureStyledTo(pos);\n\tdurationStyleOneByte.AddSample(pos - stylingStart, epStyling.Duration());\n}\n\nLexInterface *Document::GetLexInterface() const noexcept {\n\treturn pli.get();\n}\n\nvoid Document::SetLexInterface(std::unique_ptr<LexInterface> pLexInterface) noexcept {\n\tpli = std::move(pLexInterface);\n}\n\nvoid Document::SetViewState(void *view, ViewStateShared pVSS) {\n\tif (pVSS) {\n\t\tviewData[view] = std::move(pVSS);\n\t} else {\n\t\tviewData.erase(view);\n\t}\n}\n\nViewStateShared Document::GetViewState(void *view) const noexcept {\n\ttry {\n\t\tconst std::map<void *, ViewStateShared>::const_iterator it = viewData.find(view);\n\n\t\tif (it != viewData.end()) {\n\t\t\treturn it->second;\n\t\t}\n\t} catch (...) {\n\t}\n\treturn {};\n}\n\nvoid Document::TruncateUndoComments(int action) {\n\tfor (auto &[key, value] : viewData) {\n\t\tvalue->TruncateUndo(action);\n\t}\n}\n\nint SCI_METHOD Document::SetLineState(Sci_Position line, int state) {\n\tconst int statePrevious = States()->SetLineState(line, state, LinesTotal());\n\tif (state != statePrevious) {\n\t\tconst DocModification mh(ModificationFlags::ChangeLineState, LineStart(line), 0, 0, nullptr,\n\t\t\tstatic_cast<Sci::Line>(line));\n\t\tNotifyModified(mh);\n\t}\n\treturn statePrevious;\n}\n\nint SCI_METHOD Document::GetLineState(Sci_Position line) const {\n\treturn States()->GetLineState(line);\n}\n\nSci::Line Document::GetMaxLineState() const noexcept {\n\treturn States()->GetMaxLineState();\n}\n\nvoid SCI_METHOD Document::ChangeLexerState(Sci_Position start, Sci_Position end) {\n\tconst DocModification mh(ModificationFlags::LexerState, start,\n\t\tend-start, 0, nullptr, 0);\n\tNotifyModified(mh);\n}\n\nStyledText Document::MarginStyledText(Sci::Line line) const noexcept {\n\tconst LineAnnotation *pla = Margins();\n\treturn StyledText(pla->Length(line), pla->Text(line),\n\t\tpla->MultipleStyles(line), pla->Style(line), pla->Styles(line));\n}\n\n//Au: new function\nSci::Line Document::MarginStyledTextNext(Sci::Line line, int style) {\n\tif(line < 0) return 0;\n\tconst LineAnnotation* pla = Margins();\n\tauto lt = LinesTotal();\n\tfor(; line < lt; line++) if(pla->Style(line) == style) return line;\n\treturn -1;\n\t//info: this is 10 times faster than repeatedly calling SCI_MARGINGETSTYLE\n}\n\nvoid Document::MarginSetText(Sci::Line line, const char *text) {\n\tMargins()->SetText(line, text);\n\tconst DocModification mh(ModificationFlags::ChangeMargin, LineStart(line),\n\t\t0, 0, nullptr, line);\n\tNotifyModified(mh);\n}\n\nvoid Document::MarginSetStyle(Sci::Line line, int style) {\n\tMargins()->SetStyle(line, style);\n\tNotifyModified(DocModification(ModificationFlags::ChangeMargin, LineStart(line),\n\t\t0, 0, nullptr, line));\n}\n\nvoid Document::MarginSetStyles(Sci::Line line, const unsigned char *styles) {\n\tMargins()->SetStyles(line, styles);\n\tNotifyModified(DocModification(ModificationFlags::ChangeMargin, LineStart(line),\n\t\t0, 0, nullptr, line));\n}\n\nvoid Document::MarginClearAll() {\n\tconst Sci::Line maxEditorLine = LinesTotal();\n\tfor (Sci::Line l=0; l<maxEditorLine; l++)\n\t\tMarginSetText(l, nullptr);\n\t// Free remaining data\n\tMargins()->ClearAll();\n}\n\nStyledText Document::AnnotationStyledText(Sci::Line line) const noexcept {\n\tconst LineAnnotation *pla = Annotations();\n\treturn StyledText(pla->Length(line), pla->Text(line),\n\t\tpla->MultipleStyles(line), pla->Style(line), pla->Styles(line));\n}\n\nvoid Document::AnnotationSetText(Sci::Line line, const char *text) {\n\tif (line >= 0 && line < LinesTotal()) {\n\t\tconst Sci::Line linesBefore = AnnotationLines(line);\n\t\tAnnotations()->SetText(line, text);\n\t\tconst int linesAfter = AnnotationLines(line);\n\t\tDocModification mh(ModificationFlags::ChangeAnnotation, LineStart(line),\n\t\t\t0, 0, nullptr, line);\n\t\tmh.annotationLinesAdded = linesAfter - linesBefore;\n\t\tNotifyModified(mh);\n\t}\n}\n\nvoid Document::AnnotationSetStyle(Sci::Line line, int style) {\n\tif (line >= 0 && line < LinesTotal()) {\n\t\tAnnotations()->SetStyle(line, style);\n\t\tconst DocModification mh(ModificationFlags::ChangeAnnotation, LineStart(line),\n\t\t\t0, 0, nullptr, line);\n\t\tNotifyModified(mh);\n\t}\n}\n\nvoid Document::AnnotationSetStyles(Sci::Line line, const unsigned char *styles) {\n\tif (line >= 0 && line < LinesTotal()) {\n\t\tAnnotations()->SetStyles(line, styles);\n\t}\n}\n\nint Document::AnnotationLines(Sci::Line line) const noexcept {\n\treturn Annotations()->Lines(line);\n}\n\nvoid Document::AnnotationClearAll() {\n\tif (Annotations()->Empty()) {\n\t\treturn;\n\t}\n\tconst Sci::Line maxEditorLine = LinesTotal();\n\tfor (Sci::Line l=0; l<maxEditorLine; l++)\n\t\tAnnotationSetText(l, nullptr);\n\t// Free remaining data\n\tAnnotations()->ClearAll();\n}\n\nStyledText Document::EOLAnnotationStyledText(Sci::Line line) const noexcept {\n\tconst LineAnnotation *pla = EOLAnnotations();\n\treturn StyledText(pla->Length(line), pla->Text(line),\n\t\tpla->MultipleStyles(line), pla->Style(line), pla->Styles(line));\n}\n\nvoid Document::EOLAnnotationSetText(Sci::Line line, const char *text) {\n\tif (line >= 0 && line < LinesTotal()) {\n\t\tEOLAnnotations()->SetText(line, text);\n\t\tconst DocModification mh(ModificationFlags::ChangeEOLAnnotation, LineStart(line),\n\t\t\t0, 0, nullptr, line);\n\t\tNotifyModified(mh);\n\t}\n}\n\nvoid Document::EOLAnnotationSetStyle(Sci::Line line, int style) {\n\tif (line >= 0 && line < LinesTotal()) {\n\t\tEOLAnnotations()->SetStyle(line, style);\n\t\tconst DocModification mh(ModificationFlags::ChangeEOLAnnotation, LineStart(line),\n\t\t\t0, 0, nullptr, line);\n\t\tNotifyModified(mh);\n\t}\n}\n\nvoid Document::EOLAnnotationClearAll() {\n\tif (EOLAnnotations()->Empty()) {\n\t\treturn;\n\t}\n\tconst Sci::Line maxEditorLine = LinesTotal();\n\tfor (Sci::Line l=0; l<maxEditorLine; l++)\n\t\tEOLAnnotationSetText(l, nullptr);\n\t// Free remaining data\n\tEOLAnnotations()->ClearAll();\n}\n\nvoid Document::IncrementStyleClock() noexcept {\n\tstyleClock = (styleClock + 1) % 0x100000;\n}\n\nvoid SCI_METHOD Document::DecorationSetCurrentIndicator(int indicator) {\n\tdecorations->SetCurrentIndicator(indicator);\n}\n\nvoid SCI_METHOD Document::DecorationFillRange(Sci_Position position, int value, Sci_Position fillLength) {\n\tconst FillResult<Sci::Position> fr = decorations->FillRange(\n\t\tposition, value, fillLength);\n\tif (fr.changed) {\n\t\tconst DocModification mh(ModificationFlags::ChangeIndicator | ModificationFlags::User,\n\t\t\t\t\t\t\tfr.position, fr.fillLength);\n\t\tNotifyModified(mh);\n\t}\n}\n\nbool Document::AddWatcher(DocWatcher *watcher, void *userData) {\n\tconst WatcherWithUserData wwud(watcher, userData);\n\tstd::vector<WatcherWithUserData>::iterator it =\n\t\tstd::find(watchers.begin(), watchers.end(), wwud);\n\tif (it != watchers.end())\n\t\treturn false;\n\twatchers.push_back(wwud);\n\treturn true;\n}\n\nbool Document::RemoveWatcher(DocWatcher *watcher, void *userData) noexcept {\n\ttry {\n\t\t// This can never fail as WatcherWithUserData constructor and == are noexcept\n\t\t// but std::find is not noexcept.\n\t\tstd::vector<WatcherWithUserData>::iterator it =\n\t\t\tstd::find(watchers.begin(), watchers.end(), WatcherWithUserData(watcher, userData));\n\t\tif (it != watchers.end()) {\n\t\t\twatchers.erase(it);\n\t\t\treturn true;\n\t\t}\n\t} catch (...) {\n\t\t// Ignore any exception\n\t}\n\treturn false;\n}\n\nvoid Document::NotifyModifyAttempt() {\n\tfor (const WatcherWithUserData &watcher : watchers) {\n\t\twatcher.watcher->NotifyModifyAttempt(this, watcher.userData);\n\t}\n}\n\nvoid Document::NotifySavePoint(bool atSavePoint) {\n\tfor (const WatcherWithUserData &watcher : watchers) {\n\t\twatcher.watcher->NotifySavePoint(this, watcher.userData, atSavePoint);\n\t}\n}\n\nvoid Document::NotifyGroupCompleted() noexcept {\n\tfor (const WatcherWithUserData &watcher : watchers) {\n\t\twatcher.watcher->NotifyGroupCompleted(this, watcher.userData);\n\t}\n}\n\nvoid Document::NotifyModified(DocModification mh) {\n\tif (FlagSet(mh.modificationType, ModificationFlags::InsertText)) {\n\t\tdecorations->InsertSpace(mh.position, mh.length);\n\t} else if (FlagSet(mh.modificationType, ModificationFlags::DeleteText)) {\n\t\tdecorations->DeleteRange(mh.position, mh.length);\n\t}\n\tfor (const WatcherWithUserData &watcher : watchers) {\n\t\twatcher.watcher->NotifyModified(this, mh, watcher.userData);\n\t}\n}\n\nbool Document::IsWordPartSeparator(unsigned int ch) const {\n\treturn (WordCharacterClass(ch) == CharacterClass::word) && IsPunctuation(ch);\n}\n\nSci::Position Document::WordPartLeft(Sci::Position pos) const {\n\tif (pos > 0) {\n\t\tpos -= CharacterBefore(pos).widthBytes;\n\t\tCharacterExtracted ceStart = CharacterAfter(pos);\n\t\tif (IsWordPartSeparator(ceStart.character)) {\n\t\t\twhile (pos > 0 && IsWordPartSeparator(CharacterAfter(pos).character)) {\n\t\t\t\tpos -= CharacterBefore(pos).widthBytes;\n\t\t\t}\n\t\t}\n\t\tif (pos > 0) {\n\t\t\tceStart = CharacterAfter(pos);\n\t\t\tpos -= CharacterBefore(pos).widthBytes;\n\t\t\tif (IsLowerCase(ceStart.character)) {\n\t\t\t\twhile (pos > 0 && IsLowerCase(CharacterAfter(pos).character))\n\t\t\t\t\tpos -= CharacterBefore(pos).widthBytes;\n\t\t\t\tif (!IsUpperCase(CharacterAfter(pos).character) && !IsLowerCase(CharacterAfter(pos).character))\n\t\t\t\t\tpos += CharacterAfter(pos).widthBytes;\n\t\t\t} else if (IsUpperCase(ceStart.character)) {\n\t\t\t\twhile (pos > 0 && IsUpperCase(CharacterAfter(pos).character))\n\t\t\t\t\tpos -= CharacterBefore(pos).widthBytes;\n\t\t\t\tif (!IsUpperCase(CharacterAfter(pos).character))\n\t\t\t\t\tpos += CharacterAfter(pos).widthBytes;\n\t\t\t} else if (IsADigit(ceStart.character)) {\n\t\t\t\twhile (pos > 0 && IsADigit(CharacterAfter(pos).character))\n\t\t\t\t\tpos -= CharacterBefore(pos).widthBytes;\n\t\t\t\tif (!IsADigit(CharacterAfter(pos).character))\n\t\t\t\t\tpos += CharacterAfter(pos).widthBytes;\n\t\t\t} else if (IsPunctuation(ceStart.character)) {\n\t\t\t\twhile (pos > 0 && IsPunctuation(CharacterAfter(pos).character))\n\t\t\t\t\tpos -= CharacterBefore(pos).widthBytes;\n\t\t\t\tif (!IsPunctuation(CharacterAfter(pos).character))\n\t\t\t\t\tpos += CharacterAfter(pos).widthBytes;\n\t\t\t} else if (IsASpace(ceStart.character)) {\n\t\t\t\twhile (pos > 0 && IsASpace(CharacterAfter(pos).character))\n\t\t\t\t\tpos -= CharacterBefore(pos).widthBytes;\n\t\t\t\tif (!IsASpace(CharacterAfter(pos).character))\n\t\t\t\t\tpos += CharacterAfter(pos).widthBytes;\n\t\t\t} else if (!IsASCII(ceStart.character)) {\n\t\t\t\twhile (pos > 0 && !IsASCII(CharacterAfter(pos).character))\n\t\t\t\t\tpos -= CharacterBefore(pos).widthBytes;\n\t\t\t\tif (IsASCII(CharacterAfter(pos).character))\n\t\t\t\t\tpos += CharacterAfter(pos).widthBytes;\n\t\t\t} else {\n\t\t\t\tpos += CharacterAfter(pos).widthBytes;\n\t\t\t}\n\t\t}\n\t}\n\treturn pos;\n}\n\nSci::Position Document::WordPartRight(Sci::Position pos) const {\n\tCharacterExtracted ceStart = CharacterAfter(pos);\n\tconst Sci::Position length = LengthNoExcept();\n\tif (IsWordPartSeparator(ceStart.character)) {\n\t\twhile (pos < length && IsWordPartSeparator(CharacterAfter(pos).character))\n\t\t\tpos += CharacterAfter(pos).widthBytes;\n\t\tceStart = CharacterAfter(pos);\n\t}\n\tif (!IsASCII(ceStart.character)) {\n\t\twhile (pos < length && !IsASCII(CharacterAfter(pos).character))\n\t\t\tpos += CharacterAfter(pos).widthBytes;\n\t} else if (IsLowerCase(ceStart.character)) {\n\t\twhile (pos < length && IsLowerCase(CharacterAfter(pos).character))\n\t\t\tpos += CharacterAfter(pos).widthBytes;\n\t} else if (IsUpperCase(ceStart.character)) {\n\t\tif (IsLowerCase(CharacterAfter(pos + ceStart.widthBytes).character)) {\n\t\t\tpos += CharacterAfter(pos).widthBytes;\n\t\t\twhile (pos < length && IsLowerCase(CharacterAfter(pos).character))\n\t\t\t\tpos += CharacterAfter(pos).widthBytes;\n\t\t} else {\n\t\t\twhile (pos < length && IsUpperCase(CharacterAfter(pos).character))\n\t\t\t\tpos += CharacterAfter(pos).widthBytes;\n\t\t}\n\t\tif (IsLowerCase(CharacterAfter(pos).character) && IsUpperCase(CharacterBefore(pos).character))\n\t\t\tpos -= CharacterBefore(pos).widthBytes;\n\t} else if (IsADigit(ceStart.character)) {\n\t\twhile (pos < length && IsADigit(CharacterAfter(pos).character))\n\t\t\tpos += CharacterAfter(pos).widthBytes;\n\t} else if (IsPunctuation(ceStart.character)) {\n\t\twhile (pos < length && IsPunctuation(CharacterAfter(pos).character))\n\t\t\tpos += CharacterAfter(pos).widthBytes;\n\t} else if (IsASpace(ceStart.character)) {\n\t\twhile (pos < length && IsASpace(CharacterAfter(pos).character))\n\t\t\tpos += CharacterAfter(pos).widthBytes;\n\t} else {\n\t\tpos += CharacterAfter(pos).widthBytes;\n\t}\n\treturn pos;\n}\n\nSci::Position Document::ExtendStyleRange(Sci::Position pos, int delta, bool singleLine) noexcept {\n\tconst char sStart = cb.StyleAt(pos);\n\tif (delta < 0) {\n\t\twhile (pos > 0 && (cb.StyleAt(pos) == sStart) && (!singleLine || !IsEOLCharacter(cb.CharAt(pos))))\n\t\t\tpos--;\n\t\tpos++;\n\t} else {\n\t\twhile (pos < (LengthNoExcept()) && (cb.StyleAt(pos) == sStart) && (!singleLine || !IsEOLCharacter(cb.CharAt(pos))))\n\t\t\tpos++;\n\t}\n\treturn pos;\n}\n\nnamespace {\n\nconstexpr char BraceOpposite(char ch) noexcept {\n\tswitch (ch) {\n\tcase '(':\n\t\treturn ')';\n\tcase ')':\n\t\treturn '(';\n\tcase '[':\n\t\treturn ']';\n\tcase ']':\n\t\treturn '[';\n\tcase '{':\n\t\treturn '}';\n\tcase '}':\n\t\treturn '{';\n\tcase '<':\n\t\treturn '>';\n\tcase '>':\n\t\treturn '<';\n\tdefault:\n\t\treturn '\\0';\n\t}\n}\n\n}\n\n// TODO: should be able to extend styled region to find matching brace\nSci::Position Document::BraceMatch(Sci::Position position, Sci::Position /*maxReStyle*/, Sci::Position startPos, bool useStartPos) noexcept {\n\tconst unsigned char chBrace = CharAt(position);\n\tconst unsigned char chSeek = BraceOpposite(chBrace);\n\tif (chSeek == '\\0')\n\t\treturn -1;\n\tconst int styBrace = StyleIndexAt(position);\n\tint direction = -1;\n\tif (chBrace == '(' || chBrace == '[' || chBrace == '{' || chBrace == '<')\n\t\tdirection = 1;\n\tint depth = 1;\n\tposition = useStartPos ? startPos : position + direction;\n\n\t// Avoid using MovePositionOutsideChar to check DBCS trail byte\n\tunsigned char maxSafeChar = 0xff;\n\tif (dbcsCodePage != 0 && dbcsCodePage != CpUtf8) {\n\t\tmaxSafeChar = std::max<unsigned char>(DBCSMinTrailByte(), 1) - 1;\n\t}\n\n\twhile ((position >= 0) && (position < LengthNoExcept())) {\n\t\tconst unsigned char chAtPos = CharAt(position);\n\t\tif (chAtPos == chBrace || chAtPos == chSeek) {\n\t\t\tif (((position > GetEndStyled()) || (StyleIndexAt(position) == styBrace)) &&\n\t\t\t\t(chAtPos <= maxSafeChar || position == MovePositionOutsideChar(position, direction, false))) {\n\t\t\t\tdepth += (chAtPos == chBrace) ? 1 : -1;\n\t\t\t\tif (depth == 0)\n\t\t\t\t\treturn position;\n\t\t\t}\n\t\t}\n\t\tposition += direction;\n\t}\n\treturn -1;\n}\n\n/**\n * Implementation of RegexSearchBase for the default built-in regular expression engine\n */\nclass BuiltinRegex : public RegexSearchBase {\npublic:\n\texplicit BuiltinRegex(CharClassify *charClassTable) : search(charClassTable) {}\n\n\tSci::Position FindText(Document *doc, Sci::Position minPos, Sci::Position maxPos, const char *s,\n                        bool caseSensitive, bool word, bool wordStart, FindOption flags,\n                        Sci::Position *length) override;\n\n\tconst char *SubstituteByPosition(Document *doc, const char *text, Sci::Position *length) override;\n\nprivate:\n\tRESearch search;\n\tstd::string substituted;\n};\n\nnamespace {\n\n/**\n* RESearchRange keeps track of search range.\n*/\nclass RESearchRange {\npublic:\n\tint increment;\n\tSci::Position startPos;\n\tSci::Position endPos;\n\tSci::Line lineRangeStart;\n\tSci::Line lineRangeEnd;\n\tSci::Line lineRangeBreak;\n\tRESearchRange(const Document *doc, Sci::Position minPos, Sci::Position maxPos) noexcept {\n\t\tincrement = (minPos <= maxPos) ? 1 : -1;\n\n\t\t// Range endpoints should not be inside DBCS characters or between a CR and LF,\n\t\t// but just in case, move them.\n\t\tstartPos = doc->MovePositionOutsideChar(minPos, 1, true);\n\t\tendPos = doc->MovePositionOutsideChar(maxPos, 1, true);\n\n\t\tlineRangeStart = doc->SciLineFromPosition(startPos);\n\t\tlineRangeEnd = doc->SciLineFromPosition(endPos);\n\t\tlineRangeBreak = lineRangeEnd + increment;\n\t}\n\t[[nodiscard]] Range LineRange(Sci::Line line, Sci::Position lineStartPos, Sci::Position lineEndPos) const noexcept {\n\t\tRange range(lineStartPos, lineEndPos);\n\t\tif (increment == 1) {\n\t\t\tif (line == lineRangeStart)\n\t\t\t\trange.start = startPos;\n\t\t\tif (line == lineRangeEnd)\n\t\t\t\trange.end = endPos;\n\t\t} else {\n\t\t\tif (line == lineRangeEnd)\n\t\t\t\trange.start = endPos;\n\t\t\tif (line == lineRangeStart)\n\t\t\t\trange.end = startPos;\n\t\t}\n\t\treturn range;\n\t}\n};\n\n// Define a way for the Regular Expression code to access the document\nclass DocumentIndexer final : public CharacterIndexer {\n\tDocument *pdoc;\n\tSci::Position end;\npublic:\n\tDocumentIndexer(Document *pdoc_, Sci::Position end_) noexcept :\n\t\tpdoc(pdoc_), end(end_) {\n\t}\n\n\t[[nodiscard]] char CharAt(Sci::Position index) const noexcept override {\n\t\tif (index < 0 || index >= end) {\n\t\t\treturn 0;\n\t\t}\n\t\treturn pdoc->CharAt(index);\n\t}\n\t[[nodiscard]] Sci::Position MovePositionOutsideChar(Sci::Position pos, Sci::Position moveDir) const noexcept override {\n\t\treturn pdoc->MovePositionOutsideChar(pos, moveDir, false);\n\t}\n};\n\n#ifndef NO_CXX11_REGEX\n\nclass ByteIterator {\npublic:\n\tusing iterator_category = std::bidirectional_iterator_tag;\n\tusing value_type = char;\n\tusing difference_type = ptrdiff_t;\n\tusing pointer = char*;\n\tusing reference = char&;\n\n\tconst Document *doc;\n\tSci::Position position;\n\n\texplicit ByteIterator(const Document *doc_=nullptr, Sci::Position position_=0) noexcept :\n\t\tdoc(doc_), position(position_) {\n\t}\n\tchar operator*() const noexcept {\n\t\treturn doc->CharAt(position);\n\t}\n\tByteIterator &operator++() noexcept {\n\t\tposition++;\n\t\treturn *this;\n\t}\n\tByteIterator operator++(int) noexcept {\n\t\tByteIterator retVal(*this);\n\t\tposition++;\n\t\treturn retVal;\n\t}\n\tByteIterator &operator--() noexcept {\n\t\tposition--;\n\t\treturn *this;\n\t}\n\tbool operator==(const ByteIterator &other) const noexcept {\n\t\treturn doc == other.doc && position == other.position;\n\t}\n\tbool operator!=(const ByteIterator &other) const noexcept {\n\t\treturn doc != other.doc || position != other.position;\n\t}\n\t[[nodiscard]] Sci::Position Pos() const noexcept {\n\t\treturn position;\n\t}\n\t[[nodiscard]] Sci::Position PosRoundUp() const noexcept {\n\t\treturn position;\n\t}\n};\n\n// On Windows, wchar_t is 16 bits wide and on Unix it is 32 bits wide.\n// Would be better to use sizeof(wchar_t) or similar to differentiate\n// but easier for now to hard-code platforms.\n// C++11 has char16_t and char32_t but neither Clang nor Visual C++\n// appear to allow specializing basic_regex over these.\n\n#ifdef _WIN32\n#define WCHAR_T_IS_16 1\n#else\n#define WCHAR_T_IS_16 0\n#endif\n\n#if WCHAR_T_IS_16\n\n// On Windows, report non-BMP characters as 2 separate surrogates as that\n// matches wregex since it is based on wchar_t.\nclass UTF8Iterator {\n\t// These 3 fields determine the iterator position and are used for comparisons\n\tconst Document *doc;\n\tSci::Position position;\n\tsize_t characterIndex = 0;\n\t// Remaining fields are derived from the determining fields so are excluded in comparisons\n\tunsigned int lenBytes = 0;\n\tsize_t lenCharacters = 0;\n\twchar_t buffered[2]{};\npublic:\n\tusing iterator_category = std::bidirectional_iterator_tag;\n\tusing value_type = wchar_t;\n\tusing difference_type = ptrdiff_t;\n\tusing pointer = wchar_t*;\n\tusing reference = wchar_t&;\n\n\texplicit UTF8Iterator(const Document *doc_=nullptr, Sci::Position position_=0) noexcept :\n\t\tdoc(doc_), position(position_) {\n\t\tif (doc) {\n\t\t\tReadCharacter();\n\t\t}\n\t}\n\twchar_t operator*() const noexcept {\n\t\tassert(lenCharacters != 0);\n\t\treturn buffered[characterIndex];\n\t}\n\tUTF8Iterator &operator++() noexcept {\n\t\tif ((characterIndex + 1) < (lenCharacters)) {\n\t\t\tcharacterIndex++;\n\t\t} else {\n\t\t\tposition += lenBytes;\n\t\t\tReadCharacter();\n\t\t\tcharacterIndex = 0;\n\t\t}\n\t\treturn *this;\n\t}\n\tUTF8Iterator operator++(int) noexcept {\n\t\tUTF8Iterator retVal(*this);\n\t\tif ((characterIndex + 1) < (lenCharacters)) {\n\t\t\tcharacterIndex++;\n\t\t} else {\n\t\t\tposition += lenBytes;\n\t\t\tReadCharacter();\n\t\t\tcharacterIndex = 0;\n\t\t}\n\t\treturn retVal;\n\t}\n\tUTF8Iterator &operator--() noexcept {\n\t\tif (characterIndex) {\n\t\t\tcharacterIndex--;\n\t\t} else {\n\t\t\tposition = doc->NextPosition(position, -1);\n\t\t\tReadCharacter();\n\t\t\tcharacterIndex = lenCharacters - 1;\n\t\t}\n\t\treturn *this;\n\t}\n\tbool operator==(const UTF8Iterator &other) const noexcept {\n\t\t// Only test the determining fields, not the character widths and values derived from this\n\t\treturn doc == other.doc &&\n\t\t\tposition == other.position &&\n\t\t\tcharacterIndex == other.characterIndex;\n\t}\n\tbool operator!=(const UTF8Iterator &other) const noexcept {\n\t\t// Only test the determining fields, not the character widths and values derived from this\n\t\treturn doc != other.doc ||\n\t\t\tposition != other.position ||\n\t\t\tcharacterIndex != other.characterIndex;\n\t}\n\t[[nodiscard]] Sci::Position Pos() const noexcept {\n\t\treturn position;\n\t}\n\t[[nodiscard]] Sci::Position PosRoundUp() const noexcept {\n\t\tif (characterIndex)\n\t\t\treturn position + lenBytes;\t// Force to end of character\n\t\telse\n\t\t\treturn position;\n\t}\nprivate:\n\tvoid ReadCharacter() noexcept {\n\t\tconst CharacterExtracted charExtracted = doc->ExtractCharacter(position);\n\t\tlenBytes = charExtracted.widthBytes;\n\t\tif (charExtracted.character == unicodeReplacementChar) {\n\t\t\tlenCharacters = 1;\n\t\t\tbuffered[0] = static_cast<wchar_t>(charExtracted.character);\n\t\t} else {\n\t\t\tlenCharacters = UTF16FromUTF32Character(charExtracted.character, buffered);\n\t\t}\n\t}\n};\n\n#else\n\n// On Unix, report non-BMP characters as single characters\n\nclass UTF8Iterator {\n\tconst Document *doc;\n\tSci::Position position;\npublic:\n\tusing iterator_category = std::bidirectional_iterator_tag;\n\tusing value_type = wchar_t;\n\tusing difference_type = ptrdiff_t;\n\tusing pointer = wchar_t*;\n\tusing reference = wchar_t&;\n\n\texplicit UTF8Iterator(const Document *doc_=nullptr, Sci::Position position_=0) noexcept :\n\t\tdoc(doc_), position(position_) {\n\t}\n\twchar_t operator*() const noexcept {\n\t\tconst CharacterExtracted charExtracted = doc->ExtractCharacter(position);\n\t\treturn charExtracted.character;\n\t}\n\tUTF8Iterator &operator++() noexcept {\n\t\tposition = doc->NextPosition(position, 1);\n\t\treturn *this;\n\t}\n\tUTF8Iterator operator++(int) noexcept {\n\t\tUTF8Iterator retVal(*this);\n\t\tposition = doc->NextPosition(position, 1);\n\t\treturn retVal;\n\t}\n\tUTF8Iterator &operator--() noexcept {\n\t\tposition = doc->NextPosition(position, -1);\n\t\treturn *this;\n\t}\n\tbool operator==(const UTF8Iterator &other) const noexcept {\n\t\treturn doc == other.doc && position == other.position;\n\t}\n\tbool operator!=(const UTF8Iterator &other) const noexcept {\n\t\treturn doc != other.doc || position != other.position;\n\t}\n\tSci::Position Pos() const noexcept {\n\t\treturn position;\n\t}\n\tSci::Position PosRoundUp() const noexcept {\n\t\treturn position;\n\t}\n};\n\n#endif\n\nstd::regex_constants::match_flag_type MatchFlags(const Document *doc, Sci::Position startPos, Sci::Position endPos, Sci::Position lineStartPos, Sci::Position lineEndPos) {\n\tstd::regex_constants::match_flag_type flagsMatch = std::regex_constants::match_default;\n\tif (startPos != lineStartPos) {\n#ifdef _LIBCPP_VERSION\n\t\tflagsMatch |= std::regex_constants::match_not_bol;\n\t\tif (!doc->IsWordStartAt(startPos)) {\n\t\t\tflagsMatch |= std::regex_constants::match_not_bow;\n\t\t}\n#else\n\t\tflagsMatch |= std::regex_constants::match_prev_avail;\n#endif\n\t}\n\tif (endPos != lineEndPos) {\n\t\tflagsMatch |= std::regex_constants::match_not_eol;\n\t\tif (!doc->IsWordEndAt(endPos)) {\n\t\t\tflagsMatch |= std::regex_constants::match_not_eow;\n\t\t}\n\t}\n\treturn flagsMatch;\n}\n\ntemplate<typename Iterator, typename Regex>\nbool MatchOnLines(const Document *doc, const Regex &regexp, const RESearchRange &resr, RESearch &search) {\n\tstd::match_results<Iterator> match;\n\n\t// MSVC and libc++ have problems with ^ and $ matching line ends inside a range.\n\t// CRLF line ends are also a problem as ^ and $ only treat LF as a line end.\n\t// The std::regex::multiline option was added to C++17 to improve behaviour but\n\t// has not been implemented by compiler runtimes with MSVC always in multiline\n\t// mode and libc++ and libstdc++ always in single-line mode.\n\t// If multiline regex worked well then the line by line iteration could be removed\n\t// for the forwards case and replaced with the following:\n#ifdef REGEX_MULTILINE\n\tconst Sci::Position lineStartPos = doc->LineStart(resr.lineRangeStart);\n\tconst Sci::Position lineEndPos = doc->LineEnd(resr.lineRangeEnd);\n\tIterator itStart(doc, resr.startPos);\n\tIterator itEnd(doc, resr.endPos);\n\tconst std::regex_constants::match_flag_type flagsMatch = MatchFlags(doc, resr.startPos, resr.endPos, lineStartPos, lineEndPos);\n\tconst bool matched = std::regex_search(itStart, itEnd, match, regexp, flagsMatch);\n#else\n\t// Line by line.\n\tbool matched = false;\n\tfor (Sci::Line line = resr.lineRangeStart; line != resr.lineRangeBreak; line += resr.increment) {\n\t\tconst Sci::Position lineStartPos = doc->LineStart(line);\n\t\tconst Sci::Position lineEndPos = doc->LineEnd(line);\n\t\tconst Range lineRange = resr.LineRange(line, lineStartPos, lineEndPos);\n\t\tconst Iterator itStart(doc, lineRange.start);\n\t\tconst Iterator itEnd(doc, lineRange.end);\n\t\tconst std::regex_constants::match_flag_type flagsMatch = MatchFlags(doc, lineRange.start, lineRange.end, lineStartPos, lineEndPos);\n\t\tstd::regex_iterator<Iterator> it(itStart, itEnd, regexp, flagsMatch);\n\t\tfor (const std::regex_iterator<Iterator> last; it != last; ++it) {\n\t\t\tmatch = *it;\n\t\t\tmatched = true;\n\t\t\tif (resr.increment > 0) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (matched) {\n\t\t\tbreak;\n\t\t}\n\t}\n#endif\n\tif (matched) {\n\t\tfor (size_t co = 0; co < match.size() && co < RESearch::MAXTAG; co++) {\n\t\t\tsearch.bopat[co] = match[co].first.Pos();\n\t\t\tsearch.eopat[co] = match[co].second.PosRoundUp();\n\t\t}\n\t}\n\treturn matched;\n}\n\nSci::Position Cxx11RegexFindText(const Document *doc, Sci::Position minPos, Sci::Position maxPos, const char *s,\n\tbool caseSensitive, Sci::Position *length, RESearch &search) {\n\tconst RESearchRange resr(doc, minPos, maxPos);\n\ttry {\n\t\t//ElapsedPeriod ep;\n\t\tstd::regex::flag_type flagsRe = std::regex::ECMAScript;\n\t\t// Flags that appear to have no effect:\n\t\t// | std::regex::collate | std::regex::extended;\n\t\tif (!caseSensitive)\n\t\t\tflagsRe = flagsRe | std::regex::icase;\n\n#if defined(REGEX_MULTILINE) && !defined(_MSC_VER)\n\t\tflagsRe = flagsRe | std::regex::multiline;\n#endif\n\n\t\t// Clear the RESearch so can fill in matches\n\t\tsearch.Clear();\n\n\t\tbool matched = false;\n\t\tif (CpUtf8 == doc->dbcsCodePage) {\n\t\t\tconst std::wstring ws = WStringFromUTF8(s);\n\t\t\tstd::wregex regexp;\n\t\t\tregexp.assign(ws, flagsRe);\n\t\t\tmatched = MatchOnLines<UTF8Iterator>(doc, regexp, resr, search);\n\t\t} else {\n\t\t\tstd::regex regexp;\n\t\t\tregexp.assign(s, flagsRe);\n\t\t\tmatched = MatchOnLines<ByteIterator>(doc, regexp, resr, search);\n\t\t}\n\n\t\tSci::Position posMatch = -1;\n\t\tif (matched) {\n\t\t\tposMatch = search.bopat[0];\n\t\t\t*length = search.eopat[0] - search.bopat[0];\n\t\t}\n\t\t// Example - search in doc/ScintillaHistory.html for\n\t\t// [[:upper:]]eta[[:space:]]\n\t\t// On MacBook, normally around 1 second but with locale imbued -> 14 seconds.\n\t\t//const double durSearch = ep.Duration(true);\n\t\t//Platform::DebugPrintf(\"Search:%9.6g \\n\", durSearch);\n\t\treturn posMatch;\n\t} catch (std::regex_error &) {\n\t\t// Failed to create regular expression\n\t\tthrow RegexError();\n\t} catch (...) {\n\t\t// Failed in some other way\n\t\treturn -1;\n\t}\n}\n\n#endif\n\n}\n\nSci::Position BuiltinRegex::FindText(Document *doc, Sci::Position minPos, Sci::Position maxPos, const char *s,\n                        bool caseSensitive, bool, bool, FindOption flags,\n                        Sci::Position *length) {\n\n#ifndef NO_CXX11_REGEX\n\tif (FlagSet(flags, FindOption::Cxx11RegEx)) {\n\t\t\treturn Cxx11RegexFindText(doc, minPos, maxPos, s,\n\t\t\tcaseSensitive, length, search);\n\t}\n#endif\n\n\tconst RESearchRange resr(doc, minPos, maxPos);\n\n\tconst bool posix = FlagSet(flags, FindOption::Posix);\n\n\tconst char *errmsg = search.Compile(s, *length, caseSensitive, posix);\n\tif (errmsg) {\n\t\treturn -1;\n\t}\n\t// Find a variable in a property file: \\$(\\([A-Za-z0-9_.]+\\))\n\t// Replace first '.' with '-' in each property file variable reference:\n\t//     Search: \\$(\\([A-Za-z0-9_-]+\\)\\.\\([A-Za-z0-9_.]+\\))\n\t//     Replace: $(\\1-\\2)\n\tSci::Position pos = -1;\n\tSci::Position lenRet = 0;\n\tconst bool searchforLineStart = s[0] == '^';\n\tconst char searchEnd = s[*length - 1];\n\tconst char searchEndPrev = (*length > 1) ? s[*length - 2] : '\\0';\n\tconst bool searchforLineEnd = (searchEnd == '$') && (searchEndPrev != '\\\\');\n\tfor (Sci::Line line = resr.lineRangeStart; line != resr.lineRangeBreak; line += resr.increment) {\n\t\tconst Sci::Position lineStartPos = doc->LineStart(line);\n\t\tconst Sci::Position lineEndPos = doc->LineEnd(line);\n\t\tSci::Position startOfLine = lineStartPos;\n\t\tSci::Position endOfLine = lineEndPos;\n\t\tif (resr.increment == 1) {\n\t\t\tif (line == resr.lineRangeStart) {\n\t\t\t\tif ((resr.startPos != startOfLine) && searchforLineStart)\n\t\t\t\t\tcontinue;\t// Can't match start of line if start position after start of line\n\t\t\t\tstartOfLine = resr.startPos;\n\t\t\t}\n\t\t\tif (line == resr.lineRangeEnd) {\n\t\t\t\tif ((resr.endPos != endOfLine) && searchforLineEnd)\n\t\t\t\t\tcontinue;\t// Can't match end of line if end position before end of line\n\t\t\t\tendOfLine = resr.endPos;\n\t\t\t}\n\t\t} else {\n\t\t\tif (line == resr.lineRangeEnd) {\n\t\t\t\tif ((resr.endPos != startOfLine) && searchforLineStart)\n\t\t\t\t\tcontinue;\t// Can't match start of line if end position after start of line\n\t\t\t\tstartOfLine = resr.endPos;\n\t\t\t}\n\t\t\tif (line == resr.lineRangeStart) {\n\t\t\t\tif ((resr.startPos != endOfLine) && searchforLineEnd)\n\t\t\t\t\tcontinue;\t// Can't match end of line if start position before end of line\n\t\t\t\tendOfLine = resr.startPos;\n\t\t\t}\n\t\t}\n\n\t\tconst DocumentIndexer di(doc, endOfLine);\n\t\tsearch.SetLineRange(lineStartPos, lineEndPos);\n\t\tint success = search.Execute(di, startOfLine, endOfLine);\n\t\tif (success) {\n\t\t\tSci::Position endPos = search.eopat[0];\n\t\t\t// There can be only one start of a line, so no need to look for last match in line\n\t\t\tif ((resr.increment == -1) && !searchforLineStart) {\n\t\t\t\t// Check for the last match on this line.\n\t\t\t\twhile (success && (endPos < endOfLine)) {\n\t\t\t\t\tconst RESearch::MatchPositions bopat = search.bopat;\n\t\t\t\t\tconst RESearch::MatchPositions eopat = search.eopat;\n\t\t\t\t\tpos = endPos;\n\t\t\t\t\tif (pos == bopat[0]) {\n\t\t\t\t\t\t// empty match\n\t\t\t\t\t\tpos = doc->NextPosition(pos, 1);\n\t\t\t\t\t}\n\t\t\t\t\tsuccess = search.Execute(di, pos, endOfLine);\n\t\t\t\t\tif (success) {\n\t\t\t\t\t\tendPos = search.eopat[0];\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsearch.bopat = bopat;\n\t\t\t\t\t\tsearch.eopat = eopat;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tpos = search.bopat[0];\n\t\t\tlenRet = endPos - pos;\n\t\t\tbreak;\n\t\t}\n\t}\n\t*length = lenRet;\n\treturn pos;\n}\n\nconst char *BuiltinRegex::SubstituteByPosition(Document *doc, const char *text, Sci::Position *length) {\n\tsubstituted.clear();\n\tfor (Sci::Position j = 0; j < *length; j++) {\n\t\tif (text[j] == '\\\\') {\n\t\t\tconst char chNext = text[++j];\n\t\t\tif (chNext >= '0' && chNext <= '9') {\n\t\t\t\tconst unsigned int patNum = chNext - '0';\n\t\t\t\tconst Sci::Position startPos = search.bopat[patNum];\n\t\t\t\tconst Sci::Position len = search.eopat[patNum] - startPos;\n\t\t\t\tif (len > 0) {\t// Will be null if try for a match that did not occur\n\t\t\t\t\tconst size_t size = substituted.length();\n\t\t\t\t\tsubstituted.resize(size + len);\n\t\t\t\t\tdoc->GetCharRange(substituted.data() + size, startPos, len);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tswitch (chNext) {\n\t\t\t\tcase 'a':\n\t\t\t\t\tsubstituted.push_back('\\a');\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'b':\n\t\t\t\t\tsubstituted.push_back('\\b');\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'f':\n\t\t\t\t\tsubstituted.push_back('\\f');\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'n':\n\t\t\t\t\tsubstituted.push_back('\\n');\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'r':\n\t\t\t\t\tsubstituted.push_back('\\r');\n\t\t\t\t\tbreak;\n\t\t\t\tcase 't':\n\t\t\t\t\tsubstituted.push_back('\\t');\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'v':\n\t\t\t\t\tsubstituted.push_back('\\v');\n\t\t\t\t\tbreak;\n\t\t\t\tcase '\\\\':\n\t\t\t\t\tsubstituted.push_back('\\\\');\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tsubstituted.push_back('\\\\');\n\t\t\t\t\tj--;\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tsubstituted.push_back(text[j]);\n\t\t}\n\t}\n\t*length = substituted.length();\n\treturn substituted.c_str();\n}\n\n#ifndef SCI_OWNREGEX\n\nRegexSearchBase *Scintilla::Internal::CreateRegexSearch(CharClassify *charClassTable) {\n\treturn new BuiltinRegex(charClassTable);\n}\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/src/Document.h",
    "content": "// Scintilla source code edit control\n/** @file Document.h\n ** Text document that handles notifications, DBCS, styling, words and end of line.\n **/\n// Copyright 1998-2011 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#ifndef DOCUMENT_H\n#define DOCUMENT_H\n\nnamespace Scintilla::Internal {\n\nclass DocWatcher;\nclass DocModification;\nclass Document;\nclass LineMarkers;\nclass LineLevels;\nclass LineState;\nclass LineAnnotation;\n\nenum class EncodingFamily { eightBit, unicode, dbcs };\n\n/**\n * The range class represents a range of text in a document.\n * The two values are not sorted as one end may be more significant than the other\n * as is the case for the selection where the end position is the position of the caret.\n * If either position is invalidPosition then the range is invalid and most operations will fail.\n */\nclass Range {\npublic:\n\tSci::Position start;\n\tSci::Position end;\n\n\texplicit Range(Sci::Position pos=0) noexcept :\n\t\tstart(pos), end(pos) {\n\t}\n\tRange(Sci::Position start_, Sci::Position end_) noexcept :\n\t\tstart(start_), end(end_) {\n\t}\n\n\tbool operator==(const Range &other) const noexcept {\n\t\treturn (start == other.start) && (end == other.end);\n\t}\n\n\tbool Valid() const noexcept {\n\t\treturn (start != Sci::invalidPosition) && (end != Sci::invalidPosition);\n\t}\n\n\t[[nodiscard]] bool Empty() const noexcept {\n\t\treturn start == end;\n\t}\n\n\t[[nodiscard]] Sci::Position Length() const noexcept {\n\t\treturn (start <= end) ? (end - start) : (start - end);\n\t}\n\n\tSci::Position First() const noexcept {\n\t\treturn (start <= end) ? start : end;\n\t}\n\n\tSci::Position Last() const noexcept {\n\t\treturn (start > end) ? start : end;\n\t}\n\n\t// Is the position within the range?\n\tbool Contains(Sci::Position pos) const noexcept {\n\t\tif (start < end) {\n\t\t\treturn (pos >= start && pos <= end);\n\t\t} else {\n\t\t\treturn (pos <= start && pos >= end);\n\t\t}\n\t}\n\n\t// Is the character after pos within the range?\n\tbool ContainsCharacter(Sci::Position pos) const noexcept {\n\t\tif (start < end) {\n\t\t\treturn (pos >= start && pos < end);\n\t\t} else {\n\t\t\treturn (pos < start && pos >= end);\n\t\t}\n\t}\n\n\tbool Contains(Range other) const noexcept {\n\t\treturn Contains(other.start) && Contains(other.end);\n\t}\n\n\tbool Overlaps(Range other) const noexcept {\n\t\treturn\n\t\tContains(other.start) ||\n\t\tContains(other.end) ||\n\t\tother.Contains(start) ||\n\t\tother.Contains(end);\n\t}\n};\n\n/**\n * Interface class for regular expression searching\n */\nclass RegexSearchBase {\npublic:\n\tvirtual ~RegexSearchBase() = default;\n\n\tvirtual Sci::Position FindText(Document *doc, Sci::Position minPos, Sci::Position maxPos, const char *s,\n                        bool caseSensitive, bool word, bool wordStart, Scintilla::FindOption flags, Sci::Position *length) = 0;\n\n\t///@return String with the substitutions, must remain valid until the next call or destruction\n\tvirtual const char *SubstituteByPosition(Document *doc, const char *text, Sci::Position *length) = 0;\n};\n\n/// Factory function for RegexSearchBase\nextern RegexSearchBase *CreateRegexSearch(CharClassify *charClassTable);\n\nstruct StyledText {\n\tsize_t length;\n\tconst char *text;\n\tbool multipleStyles;\n\tsize_t style;\n\tconst unsigned char *styles;\n\tStyledText(size_t length_, const char *text_, bool multipleStyles_, int style_, const unsigned char *styles_) noexcept :\n\t\tlength(length_), text(text_), multipleStyles(multipleStyles_), style(style_), styles(styles_) {\n\t}\n\t// Return number of bytes from start to before '\\n' or end of text.\n\t// Return 1 when start is outside text\n\tsize_t LineLength(size_t start) const noexcept {\n\t\tsize_t cur = start;\n\t\twhile ((cur < length) && (text[cur] != '\\n'))\n\t\t\tcur++;\n\t\treturn cur-start;\n\t}\n\tsize_t StyleAt(size_t i) const noexcept {\n\t\treturn multipleStyles ? styles[i] : style;\n\t}\n};\n\nclass HighlightDelimiter {\npublic:\n\tHighlightDelimiter() noexcept : isEnabled(false) {\n\t\tClear();\n\t}\n\n\tvoid Clear() noexcept {\n\t\tbeginFoldBlock = -1;\n\t\tendFoldBlock = -1;\n\t\tfirstChangeableLineBefore = -1;\n\t\tfirstChangeableLineAfter = -1;\n\t}\n\n\tbool NeedsDrawing(Sci::Line line) const noexcept {\n\t\treturn isEnabled && (line <= firstChangeableLineBefore || line >= firstChangeableLineAfter);\n\t}\n\n\tbool IsFoldBlockHighlighted(Sci::Line line) const noexcept {\n\t\treturn isEnabled && beginFoldBlock != -1 && beginFoldBlock <= line && line <= endFoldBlock;\n\t}\n\n\tbool IsHeadOfFoldBlock(Sci::Line line) const noexcept {\n\t\treturn beginFoldBlock == line && line < endFoldBlock;\n\t}\n\n\tbool IsBodyOfFoldBlock(Sci::Line line) const noexcept {\n\t\treturn beginFoldBlock != -1 && beginFoldBlock < line && line < endFoldBlock;\n\t}\n\n\tbool IsTailOfFoldBlock(Sci::Line line) const noexcept {\n\t\treturn beginFoldBlock != -1 && beginFoldBlock < line && line == endFoldBlock;\n\t}\n\n\tSci::Line beginFoldBlock;\t// Begin of current fold block\n\tSci::Line endFoldBlock;\t// End of current fold block\n\tSci::Line firstChangeableLineBefore;\t// First line that triggers repaint before starting line that determined current fold block\n\tSci::Line firstChangeableLineAfter;\t// First line that triggers repaint after starting line that determined current fold block\n\tbool isEnabled;\n};\n\n// Base class for view state that can be held and transferred without understanding the contents.\n// Declared here but real implementation subclass declared in EditModel\nstruct ViewState {\n\tViewState() noexcept = default;\n\t// Deleted so ViewState objects can not be copied\n\tViewState(const ViewState &) = delete;\n\tViewState(ViewState &&) = delete;\n\tViewState &operator=(const ViewState &) = delete;\n\tViewState &operator=(ViewState &&) = delete;\n\tvirtual ~ViewState() noexcept = default;\n\n\tvirtual void TruncateUndo(int index)=0;\n};\n\nusing ViewStateShared = std::shared_ptr<ViewState>;\n\nstruct LexerReleaser {\n\t// Called by unique_ptr to destroy/free the Resource\n\tvoid operator()(Scintilla::ILexer5 *pLexer) noexcept {\n\t\tif (pLexer) {\n\t\t\ttry {\n\t\t\t\tpLexer->Release();\n\t\t\t} catch (...) {\n\t\t\t\t// ILexer5::Release must not throw, ignore if it does.\n\t\t\t}\n\t\t}\n\t}\n};\n\nusing LexerInstance = std::unique_ptr<Scintilla::ILexer5, LexerReleaser>;\n\n// LexInterface defines the interface to ILexer used in Document.\n// The LexState subclass is actually created and that is used within ScintillaBase\n// to provide more methods that are exposed through Scintilla's external API.\nclass LexInterface {\nprotected:\n\tDocument *pdoc;\n\tLexerInstance instance;\n\tbool performingStyle;\t///< Prevent reentrance\npublic:\n\texplicit LexInterface(Document *pdoc_) noexcept;\n\t// Deleted so LexInterface objects can not be copied.\n\tLexInterface(const LexInterface &) = delete;\n\tLexInterface(LexInterface &&) = delete;\n\tLexInterface &operator=(const LexInterface &) = delete;\n\tLexInterface &operator=(LexInterface &&) = delete;\n\tvirtual ~LexInterface() noexcept;\n\tvoid SetInstance(ILexer5 *instance_) noexcept;\n\tvoid Colourise(Sci::Position start, Sci::Position end);\n\tvirtual Scintilla::LineEndType LineEndTypesSupported();\n\tbool UseContainerLexing() const noexcept;\n};\n\nstruct RegexError : public std::runtime_error {\n\tRegexError() : std::runtime_error(\"regex failure\") {}\n};\n\n/**\n * The ActionDuration class stores the average time taken for some action such as styling or\n * wrapping a line. It is used to decide how many repetitions of that action can be performed\n * on idle to maximize efficiency without affecting application responsiveness.\n * The duration changes if the time for the action changes. For example, if a simple lexer is\n * changed to a complex lexer. Changes are damped and clamped to avoid short periods of easy\n * or difficult processing moving the value too far leading to inefficiency or poor user\n * experience.\n */\n\nclass ActionDuration {\n\tdouble duration;\n\tconst double minDuration;\n\tconst double maxDuration;\npublic:\n\tActionDuration(double duration_, double minDuration_, double maxDuration_) noexcept;\n\tvoid AddSample(size_t numberActions, double durationOfActions) noexcept;\n\tdouble Duration() const noexcept;\n\tsize_t ActionsInAllowedTime(double secondsAllowed) const noexcept;\n};\n\n/**\n * A whole character (code point) with a value and width in bytes.\n * For UTF-8, the value is the code point value.\n * For DBCS, its jamming the lead and trail bytes together.\n * For 8 bit encodings, is just the byte value.\n */\nstruct CharacterExtracted {\n\tunsigned int character;\n\tunsigned int widthBytes;\n\n\tCharacterExtracted(unsigned int character_, unsigned int widthBytes_) noexcept :\n\t\tcharacter(character_), widthBytes(widthBytes_) {\n\t}\n\n\t// For UTF-8:\n\tCharacterExtracted(const unsigned char *charBytes, size_t widthCharBytes) noexcept;\n\n\t// For DBCS characters turn 2 bytes into an int\n\tstatic CharacterExtracted DBCS(unsigned char lead, unsigned char trail) noexcept {\n\t\treturn CharacterExtracted((lead << 8) | trail, 2);\n\t}\n};\n\nbool DiscardLastCombinedCharacter(std::string_view &text) noexcept;\n\n/**\n */\nclass Document : PerLine, public Scintilla::IDocument, public Scintilla::ILoader, public Scintilla::IDocumentEditable {\n\npublic:\n\t/** Used to pair watcher pointer with user data. */\n\tstruct WatcherWithUserData {\n\t\tDocWatcher *watcher;\n\t\tvoid *userData;\n\t\tWatcherWithUserData(DocWatcher *watcher_=nullptr, void *userData_=nullptr) noexcept :\n\t\t\twatcher(watcher_), userData(userData_) {\n\t\t}\n\t\tbool operator==(const WatcherWithUserData &other) const noexcept {\n\t\t\treturn (watcher == other.watcher) && (userData == other.userData);\n\t\t}\n\t};\n\nprivate:\n\tint refCount;\npublic: //Au: made public to simplify other modifications\n\tCellBuffer cb;\nprivate:\n\tCharClassify charClass;\n\tCharacterCategoryMap charMap;\n\tstd::unique_ptr<CaseFolder> pcf;\n\tSci::Position endStyled;\n\tint styleClock;\n\tint enteredModification;\n\tint enteredStyling;\n\tint enteredReadOnlyCount;\n\n\tbool insertionSet;\n\tstd::string insertion;\n\n\tstd::vector<WatcherWithUserData> watchers;\n\n\t// ldSize is not real data - it is for dimensions and loops\n\tenum lineData { ldMarkers, ldLevels, ldState, ldMargin, ldAnnotation, ldEOLAnnotation, ldSize };\n\tstd::unique_ptr<PerLine> perLineData[ldSize];\n\tLineMarkers *Markers() const noexcept;\n\tLineLevels *Levels() const noexcept;\n\tLineState *States() const noexcept;\n\tLineAnnotation *Margins() const noexcept;\n\tLineAnnotation *Annotations() const noexcept;\n\tLineAnnotation *EOLAnnotations() const noexcept;\n\n\tstd::unique_ptr<RegexSearchBase> regex;\n\tstd::unique_ptr<LexInterface> pli;\n\n\tstd::map<void *, ViewStateShared>viewData;\n\npublic:\n\n\tScintilla::EndOfLine eolMode;\n\t/// Can also be SC_CP_UTF8 to enable UTF-8 mode\n\tint dbcsCodePage;\n\tScintilla::LineEndType lineEndBitSet;\n\tint tabInChars;\n\tint indentInChars;\n\tint actualIndentInChars;\n\tbool useTabs;\n\tbool tabIndents;\n\tbool backspaceUnindents;\n\tActionDuration durationStyleOneByte;\n\n\tstd::unique_ptr<IDecorationList> decorations;\n\n\tDocument(Scintilla::DocumentOption options);\n\t// Deleted so Document objects can not be copied.\n\tDocument(const Document &) = delete;\n\tDocument(Document &&) = delete;\n\tvoid operator=(const Document &) = delete;\n\tDocument &operator=(Document &&) = delete;\n\t~Document() override;\n\n\tint SCI_METHOD AddRef() noexcept override;\n\tint SCI_METHOD Release() override;\n\n\t// From PerLine\n\tvoid Init() override;\n\tvoid InsertLine(Sci::Line line) override;\n\tvoid InsertLines(Sci::Line line, Sci::Line lines) override;\n\tvoid RemoveLine(Sci::Line line) override;\n\n\tScintilla::LineEndType LineEndTypesSupported() const;\n\tbool SetDBCSCodePage(int dbcsCodePage_);\n\tScintilla::LineEndType GetLineEndTypesAllowed() const noexcept { return cb.GetLineEndTypes(); }\n\tbool SetLineEndTypesAllowed(Scintilla::LineEndType lineEndBitSet_);\n\tScintilla::LineEndType GetLineEndTypesActive() const noexcept { return cb.GetLineEndTypes(); }\n\n\tint SCI_METHOD Version() const override {\n\t\treturn Scintilla::dvRelease4;\n\t}\n\tint SCI_METHOD DEVersion() const noexcept override;\n\n\tvoid SCI_METHOD SetErrorStatus(int status) override;\n\n\tSci_Position SCI_METHOD LineFromPosition(Sci_Position pos) const override;\n\tSci::Line SciLineFromPosition(Sci::Position pos) const noexcept;\t// Avoids casting LineFromPosition\n\tSci::Position ClampPositionIntoDocument(Sci::Position pos) const noexcept;\n\tbool ContainsLineEnd(const char *s, Sci::Position length) const noexcept { return cb.ContainsLineEnd(s, length); }\n\tbool IsCrLf(Sci::Position pos) const noexcept;\n\tint LenChar(Sci::Position pos) const noexcept;\n\tbool InGoodUTF8(Sci::Position pos, Sci::Position &start, Sci::Position &end) const noexcept;\n\tSci::Position MovePositionOutsideChar(Sci::Position pos, Sci::Position moveDir, bool checkLineEnd=true) const noexcept;\n\tSci::Position NextPosition(Sci::Position pos, int moveDir) const noexcept;\n\tbool NextCharacter(Sci::Position &pos, int moveDir) const noexcept;\t// Returns true if pos changed\n\tCharacterExtracted CharacterAfter(Sci::Position position) const noexcept;\n\tCharacterExtracted CharacterBefore(Sci::Position position) const noexcept;\n\tSci_Position SCI_METHOD GetRelativePosition(Sci_Position positionStart, Sci_Position characterOffset) const override;\n\tSci::Position GetRelativePositionUTF16(Sci::Position positionStart, Sci::Position characterOffset) const noexcept;\n\tint SCI_METHOD GetCharacterAndWidth(Sci_Position position, Sci_Position *pWidth) const override;\n\tint SCI_METHOD CodePage() const override;\n\tbool SCI_METHOD IsDBCSLeadByte(char ch) const override;\n\tbool IsDBCSLeadByteNoExcept(char ch) const noexcept;\n\tbool IsDBCSTrailByteNoExcept(char ch) const noexcept;\n\tunsigned char DBCSMinTrailByte() const noexcept;\n\tint DBCSDrawBytes(std::string_view text) const noexcept;\n\tbool IsDBCSDualByteAt(Sci::Position pos) const noexcept;\n\tsize_t SafeSegment(std::string_view text) const noexcept;\n\tEncodingFamily CodePageFamily() const noexcept;\n\n\t// Gateways to modifying document\n\tvoid ModifiedAt(Sci::Position pos) noexcept;\n\tvoid CheckReadOnly();\n\tvoid TrimReplacement(std::string_view &text, Range &range) const noexcept;\n\tbool DeleteChars(Sci::Position pos, Sci::Position len);\n\tSci::Position InsertString(Sci::Position position, const char *s, Sci::Position insertLength);\n\tSci::Position InsertString(Sci::Position position, std::string_view sv);\n\tvoid ChangeInsertion(const char *s, Sci::Position length);\n\tint SCI_METHOD AddData(const char *data, Sci_Position length) override;\n\tIDocumentEditable *AsDocumentEditable() noexcept;\n\tvoid *SCI_METHOD ConvertToDocument() override;\n\tSci::Position Undo();\n\tSci::Position Redo();\n\tbool CanUndo() const noexcept { return cb.CanUndo(); }\n\tbool CanRedo() const noexcept { return cb.CanRedo(); }\n\tvoid DeleteUndoHistory() noexcept { cb.DeleteUndoHistory(); }\n\tbool SetUndoCollection(bool collectUndo) noexcept {\n\t\treturn cb.SetUndoCollection(collectUndo);\n\t}\n\tbool IsCollectingUndo() const noexcept { return cb.IsCollectingUndo(); }\n\tvoid BeginUndoAction(bool coalesceWithPrior=false) noexcept { cb.BeginUndoAction(coalesceWithPrior); }\n\tvoid EndUndoAction() noexcept;\n\tint UndoSequenceDepth() const noexcept;\n\tbool AfterUndoSequenceStart() const noexcept { return cb.AfterUndoSequenceStart(); }\n\tvoid AddUndoAction(Sci::Position token, bool mayCoalesce) { cb.AddUndoAction(token, mayCoalesce); }\n\tvoid SetSavePoint();\n\tbool IsSavePoint() const noexcept { return cb.IsSavePoint(); }\n\n\tvoid TentativeStart() noexcept { cb.TentativeStart(); }\n\tvoid TentativeCommit() noexcept { cb.TentativeCommit(); }\n\tvoid TentativeUndo();\n\tbool TentativeActive() const noexcept { return cb.TentativeActive(); }\n\n\tint UndoActions() const noexcept;\n\tvoid SetUndoSavePoint(int action) noexcept;\n\tint UndoSavePoint() const noexcept;\n\tvoid SetUndoDetach(int action) noexcept;\n\tint UndoDetach() const noexcept;\n\tvoid SetUndoTentative(int action) noexcept;\n\tint UndoTentative() const noexcept;\n\tvoid SetUndoCurrent(int action);\n\tint UndoCurrent() const noexcept;\n\tint UndoActionType(int action) const noexcept;\n\tSci::Position UndoActionPosition(int action) const noexcept;\n\tstd::string_view UndoActionText(int action) const noexcept;\n\tvoid PushUndoActionType(int type, Sci::Position position);\n\tvoid ChangeLastUndoActionText(size_t length, const char *text);\n\n\tvoid ChangeHistorySet(bool set) { cb.ChangeHistorySet(set); }\n\t[[nodiscard]] int EditionAt(Sci::Position pos) const noexcept { return cb.EditionAt(pos); }\n\t[[nodiscard]] Sci::Position EditionEndRun(Sci::Position pos) const noexcept { return cb.EditionEndRun(pos); }\n\t[[nodiscard]] unsigned int EditionDeletesAt(Sci::Position pos) const noexcept { return cb.EditionDeletesAt(pos); }\n\t[[nodiscard]] Sci::Position EditionNextDelete(Sci::Position pos) const noexcept { return cb.EditionNextDelete(pos); }\n\n\tconst char *SCI_METHOD BufferPointer() override { return cb.BufferPointer(); }\n\tconst char *RangePointer(Sci::Position position, Sci::Position rangeLength) noexcept { return cb.RangePointer(position, rangeLength); }\n\tSci::Position GapPosition() const noexcept { return cb.GapPosition(); }\n\n\tint SCI_METHOD GetLineIndentation(Sci_Position line) override;\n\tSci::Position SetLineIndentation(Sci::Line line, Sci::Position indent);\n\tSci::Position GetLineIndentPosition(Sci::Line line) const;\n\tSci::Position GetColumn(Sci::Position pos) const;\n\tSci::Position CountCharacters(Sci::Position startPos, Sci::Position endPos) const noexcept;\n\tSci::Position CountUTF16(Sci::Position startPos, Sci::Position endPos) const noexcept;\n\tSci::Position FindColumn(Sci::Line line, Sci::Position column);\n\tvoid Indent(bool forwards, Sci::Line lineBottom, Sci::Line lineTop);\n\tstatic std::string TransformLineEnds(const char *s, size_t len, Scintilla::EndOfLine eolModeWanted);\n\tvoid ConvertLineEnds(Scintilla::EndOfLine eolModeSet);\n\tstd::string_view EOLString() const noexcept;\n\tvoid SetReadOnly(bool set) noexcept { cb.SetReadOnly(set); }\n\tbool IsReadOnly() const noexcept { return cb.IsReadOnly(); }\n\tbool IsLarge() const noexcept { return cb.IsLarge(); }\n\tScintilla::DocumentOption Options() const noexcept;\n\n\tvoid DelChar(Sci::Position pos);\n\tvoid DelCharBack(Sci::Position pos);\n\n\tchar CharAt(Sci::Position position) const noexcept { return cb.CharAt(position); }\n\tvoid SCI_METHOD GetCharRange(char *buffer, Sci_Position position, Sci_Position lengthRetrieve) const override {\n\t\tcb.GetCharRange(buffer, position, lengthRetrieve);\n\t}\n\tchar SCI_METHOD StyleAt(Sci_Position position) const override { return cb.StyleAt(position); }\n\tchar StyleAtNoExcept(Sci_Position position) const noexcept { return cb.StyleAt(position); }\n\tint StyleIndexAt(Sci_Position position) const noexcept { return static_cast<unsigned char>(cb.StyleAt(position)); }\n\tvoid GetStyleRange(unsigned char *buffer, Sci::Position position, Sci::Position lengthRetrieve) const {\n\t\tcb.GetStyleRange(buffer, position, lengthRetrieve);\n\t}\n\tint GetMark(Sci::Line line, bool includeChangeHistory) const;\n\tSci::Line MarkerNext(Sci::Line lineStart, int mask) const noexcept;\n\tint AddMark(Sci::Line line, int markerNum);\n\tvoid AddMarkSet(Sci::Line line, int valueSet);\n\tvoid DeleteMark(Sci::Line line, int markerNum);\n\tvoid DeleteMarkFromHandle(int markerHandle);\n\tvoid DeleteAllMarks(int markerNum);\n\tSci::Line LineFromHandle(int markerHandle) const noexcept;\n\tint MarkerNumberFromLine(Sci::Line line, int which) const noexcept;\n\tint MarkerHandleFromLine(Sci::Line line, int which) const noexcept;\n\tSci_Position SCI_METHOD LineStart(Sci_Position line) const override;\n\t[[nodiscard]] Range LineRange(Sci::Line line) const noexcept;\n\tbool IsLineStartPosition(Sci::Position position) const noexcept;\n\tSci_Position SCI_METHOD LineEnd(Sci_Position line) const override;\n\tSci::Position LineStartPosition(Sci::Position position) const noexcept;\n\tSci::Position LineEndPosition(Sci::Position position) const noexcept;\n\tbool IsLineEndPosition(Sci::Position position) const noexcept;\n\tbool IsPositionInLineEnd(Sci::Position position) const noexcept;\n\tSci::Position VCHomePosition(Sci::Position position) const;\n\tSci::Position IndexLineStart(Sci::Line line, Scintilla::LineCharacterIndexType lineCharacterIndex) const noexcept;\n\tSci::Line LineFromPositionIndex(Sci::Position pos, Scintilla::LineCharacterIndexType lineCharacterIndex) const noexcept;\n\tSci::Line LineFromPositionAfter(Sci::Line line, Sci::Position length) const noexcept;\n\n\tbool SCI_METHOD Sci_SetFoldLevels(int line, int lastLine, int len, int* a); //Au\n\tint SCI_METHOD SetLevel(Sci_Position line, int level) override;\n\tint SCI_METHOD GetLevel(Sci_Position line) const override;\n\tScintilla::FoldLevel GetFoldLevel(Sci_Position line) const noexcept;\n\tvoid ClearLevels();\n\tSci::Line GetLastChild(Sci::Line lineParent, std::optional<Scintilla::FoldLevel> level = {}, Sci::Line lastLine = -1);\n\tSci::Line GetFoldParent(Sci::Line line) const noexcept;\n\tvoid GetHighlightDelimiters(HighlightDelimiter &highlightDelimiter, Sci::Line line, Sci::Line lastLine);\n\n\tSci::Position ExtendWordSelect(Sci::Position pos, int delta, bool onlyWordCharacters=false) const;\n\tSci::Position NextWordStart(Sci::Position pos, int delta) const;\n\tSci::Position NextWordEnd(Sci::Position pos, int delta) const;\n\tSci_Position SCI_METHOD Length() const override { return cb.Length(); }\n\tSci::Position LengthNoExcept() const noexcept { return cb.Length(); }\n\tvoid Allocate(Sci::Position newSize) { cb.Allocate(newSize); }\n\n\tCharacterExtracted ExtractCharacter(Sci::Position position) const noexcept;\n\n\tbool IsWordStartAt(Sci::Position pos) const;\n\tbool IsWordEndAt(Sci::Position pos) const;\n\tbool IsWordAt(Sci::Position start, Sci::Position end) const;\n\n\tbool MatchesWordOptions(bool word, bool wordStart, Sci::Position pos, Sci::Position length) const;\n\tbool HasCaseFolder() const noexcept;\n\tvoid SetCaseFolder(std::unique_ptr<CaseFolder> pcf_) noexcept;\n\tSci::Position FindText(Sci::Position minPos, Sci::Position maxPos, const char *search, Scintilla::FindOption flags, Sci::Position *length);\n\tconst char *SubstituteByPosition(const char *text, Sci::Position *length);\n\tScintilla::LineCharacterIndexType LineCharacterIndex() const noexcept;\n\tvoid AllocateLineCharacterIndex(Scintilla::LineCharacterIndexType lineCharacterIndex);\n\tvoid ReleaseLineCharacterIndex(Scintilla::LineCharacterIndexType lineCharacterIndex);\n\tSci::Line LinesTotal() const noexcept;\n\tvoid AllocateLines(Sci::Line lines);\n\n\tvoid SetDefaultCharClasses(bool includeWordClass);\n\tvoid SetCharClasses(const unsigned char *chars, CharacterClass newCharClass);\n\tint GetCharsOfClass(CharacterClass characterClass, unsigned char *buffer) const;\n\tvoid SetCharacterCategoryOptimization(int countCharacters);\n\tint CharacterCategoryOptimization() const noexcept;\n\tvoid SCI_METHOD StartStyling(Sci_Position position) override;\n\tbool SCI_METHOD SetStyleFor(Sci_Position length, char style) override;\n\tbool SCI_METHOD SetStyles(Sci_Position length, const char *styles) override;\n\tSci::Position GetEndStyled() const noexcept { return endStyled; }\n\tvoid EnsureStyledTo(Sci::Position pos);\n\tvoid StyleToAdjustingLineDuration(Sci::Position pos);\n\tint GetStyleClock() const noexcept { return styleClock; }\n\tvoid IncrementStyleClock() noexcept;\n\tvoid SCI_METHOD DecorationSetCurrentIndicator(int indicator) override;\n\tvoid SCI_METHOD DecorationFillRange(Sci_Position position, int value, Sci_Position fillLength) override;\n\tLexInterface *GetLexInterface() const noexcept;\n\tvoid SetLexInterface(std::unique_ptr<LexInterface> pLexInterface) noexcept;\n\n\tvoid SetViewState(void *view, ViewStateShared pVSS);\n\tViewStateShared GetViewState(void *view) const noexcept;\n\tvoid TruncateUndoComments(int action);\n\n\tint SCI_METHOD SetLineState(Sci_Position line, int state) override;\n\tint SCI_METHOD GetLineState(Sci_Position line) const override;\n\tSci::Line GetMaxLineState() const noexcept;\n\tvoid SCI_METHOD ChangeLexerState(Sci_Position start, Sci_Position end) override;\n\n\tStyledText MarginStyledText(Sci::Line line) const noexcept;\n\tvoid MarginSetStyle(Sci::Line line, int style);\n\tvoid MarginSetStyles(Sci::Line line, const unsigned char *styles);\n\tSci::Line MarginStyledTextNext(Sci::Line line, int style); //Au: new function\n\tvoid MarginSetText(Sci::Line line, const char *text);\n\tvoid MarginClearAll();\n\n\tStyledText AnnotationStyledText(Sci::Line line) const noexcept;\n\tvoid AnnotationSetText(Sci::Line line, const char *text);\n\tvoid AnnotationSetStyle(Sci::Line line, int style);\n\tvoid AnnotationSetStyles(Sci::Line line, const unsigned char *styles);\n\tint AnnotationLines(Sci::Line line) const noexcept;\n\tvoid AnnotationClearAll();\n\n\tStyledText EOLAnnotationStyledText(Sci::Line line) const noexcept;\n\tvoid EOLAnnotationSetStyle(Sci::Line line, int style);\n\tvoid EOLAnnotationSetText(Sci::Line line, const char *text);\n\tvoid EOLAnnotationClearAll();\n\n\tbool AddWatcher(DocWatcher *watcher, void *userData);\n\tbool RemoveWatcher(DocWatcher *watcher, void *userData) noexcept;\n\n\tCharacterClass WordCharacterClass(unsigned int ch) const;\n\tbool IsWordPartSeparator(unsigned int ch) const;\n\tSci::Position WordPartLeft(Sci::Position pos) const;\n\tSci::Position WordPartRight(Sci::Position pos) const;\n\tSci::Position ExtendStyleRange(Sci::Position pos, int delta, bool singleLine) noexcept;\n\tbool IsWhiteLine(Sci::Line line) const;\n\tSci::Position ParaUp(Sci::Position pos) const;\n\tSci::Position ParaDown(Sci::Position pos) const;\n\tint IndentSize() const noexcept { return actualIndentInChars; }\n\tSci::Position BraceMatch(Sci::Position position, Sci::Position maxReStyle, Sci::Position startPos, bool useStartPos) noexcept;\n\nprivate:\n\tvoid NotifyModifyAttempt();\n\tvoid NotifySavePoint(bool atSavePoint);\n\tvoid NotifyGroupCompleted() noexcept;\n\tvoid NotifyModified(DocModification mh);\n};\n\nclass UndoGroup {\n\tDocument *pdoc;\n\tbool groupNeeded;\npublic:\n\tUndoGroup(Document *pdoc_, bool groupNeeded_=true) noexcept :\n\t\tpdoc(pdoc_), groupNeeded(groupNeeded_) {\n\t\tif (groupNeeded) {\n\t\t\tpdoc->BeginUndoAction();\n\t\t}\n\t}\n\t// Deleted so UndoGroup objects can not be copied.\n\tUndoGroup(const UndoGroup &) = delete;\n\tUndoGroup(UndoGroup &&) = delete;\n\tvoid operator=(const UndoGroup &) = delete;\n\tUndoGroup &operator=(UndoGroup &&) = delete;\n\t~UndoGroup() {\n\t\tif (groupNeeded) {\n\t\t\t// EndUndoAction can throw as it allocates but throw in destructor is fatal.\n\t\t\t// To fix this UndoHistory should allocate any memory needed by EndUndoAction\n\t\t\t// beforehand or change EndUndoAction to not require allocation.\n\t\t\tpdoc->EndUndoAction();\n\t\t}\n\t}\n\tbool Needed() const noexcept {\n\t\treturn groupNeeded;\n\t}\n};\n\n\n/**\n * To optimise processing of document modifications by DocWatchers, a hint is passed indicating the\n * scope of the change.\n * If the DocWatcher is a document view then this can be used to optimise screen updating.\n */\nclass DocModification {\npublic:\n\tScintilla::ModificationFlags modificationType;\n\tSci::Position position;\n\tSci::Position length;\n\tSci::Line linesAdded;\t/**< Negative if lines deleted. */\n\tconst char *text;\t/**< Only valid for changes to text, not for changes to style. */\n\tSci::Line line;\n\tScintilla::FoldLevel foldLevelNow;\n\tScintilla::FoldLevel foldLevelPrev;\n\tSci::Line annotationLinesAdded;\n\tSci::Position token;\n\n\tDocModification(Scintilla::ModificationFlags modificationType_, Sci::Position position_=0, Sci::Position length_=0,\n\t\tSci::Line linesAdded_=0, const char *text_=nullptr, Sci::Line line_=0) noexcept :\n\t\tmodificationType(modificationType_),\n\t\tposition(position_),\n\t\tlength(length_),\n\t\tlinesAdded(linesAdded_),\n\t\ttext(text_),\n\t\tline(line_),\n\t\tfoldLevelNow(Scintilla::FoldLevel::None),\n\t\tfoldLevelPrev(Scintilla::FoldLevel::None),\n\t\tannotationLinesAdded(0),\n\t\ttoken(0) {}\n\n\tDocModification(Scintilla::ModificationFlags modificationType_, const Action &act, Sci::Line linesAdded_=0) noexcept :\n\t\tmodificationType(modificationType_),\n\t\tposition(act.position),\n\t\tlength(act.lenData),\n\t\tlinesAdded(linesAdded_),\n\t\ttext(act.data),\n\t\tline(0),\n\t\tfoldLevelNow(Scintilla::FoldLevel::None),\n\t\tfoldLevelPrev(Scintilla::FoldLevel::None),\n\t\tannotationLinesAdded(0),\n\t\ttoken(0) {}\n};\n\n/**\n * A class that wants to receive notifications from a Document must be derived from DocWatcher\n * and implement the notification methods. It can then be added to the watcher list with AddWatcher.\n */\nclass DocWatcher {\npublic:\n\tvirtual ~DocWatcher() {}\n\n\tvirtual void NotifyModifyAttempt(Document *doc, void *userData) = 0;\n\tvirtual void NotifySavePoint(Document *doc, void *userData, bool atSavePoint) = 0;\n\tvirtual void NotifyModified(Document *doc, DocModification mh, void *userData) = 0;\n\tvirtual void NotifyDeleted(Document *doc, void *userData) noexcept = 0;\n\tvirtual void NotifyStyleNeeded(Document *doc, void *userData, Sci::Position endPos) = 0;\n\tvirtual void NotifyErrorOccurred(Document *doc, void *userData, Scintilla::Status status) = 0;\n\tvirtual void NotifyGroupCompleted(Document *doc, void *userData) noexcept = 0;\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/src/EditModel.cxx",
    "content": "// Scintilla source code edit control\n/** @file EditModel.cxx\n ** Defines the editor state that must be visible to EditorView.\n **/\n// Copyright 1998-2014 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#include <cstddef>\n#include <cstdlib>\n#include <cstdint>\n#include <cassert>\n#include <cstring>\n#include <cmath>\n\n#include <stdexcept>\n#include <string>\n#include <string_view>\n#include <vector>\n#include <map>\n#include <set>\n#include <optional>\n#include <algorithm>\n#include <memory>\n\n#include \"ScintillaTypes.h\"\n#include \"ILoader.h\"\n#include \"ILexer.h\"\n\n#include \"Debugging.h\"\n#include \"Geometry.h\"\n#include \"Platform.h\"\n\n#include \"CharacterCategoryMap.h\"\n\n#include \"Position.h\"\n#include \"UniqueString.h\"\n#include \"SplitVector.h\"\n#include \"Partitioning.h\"\n#include \"RunStyles.h\"\n#include \"ContractionState.h\"\n#include \"CellBuffer.h\"\n#include \"Indicator.h\"\n#include \"LineMarker.h\"\n#include \"Style.h\"\n#include \"ViewStyle.h\"\n#include \"CharClassify.h\"\n#include \"Decoration.h\"\n#include \"CaseFolder.h\"\n#include \"Document.h\"\n#include \"UniConversion.h\"\n#include \"Selection.h\"\n#include \"PositionCache.h\"\n#include \"EditModel.h\"\n\nusing namespace Scintilla;\nusing namespace Scintilla::Internal;\n\nCaret::Caret() noexcept :\n\tactive(false), on(false), period(500) {}\n\nvoid ModelState::RememberSelectionForUndo(int index, const Selection &sel) {\n\thistoryForUndo.indexCurrent = index;\n\thistoryForUndo.ssCurrent = sel.ToString();\n}\n\nvoid ModelState::ForgetSelectionForUndo() noexcept {\n\thistoryForUndo.indexCurrent = -1;\n}\n\nvoid ModelState::RememberSelectionOntoStack(int index, Sci::Line topLine) {\n\tif ((historyForUndo.indexCurrent >= 0) && (index == historyForUndo.indexCurrent + 1)) {\n\t\t// Don't overwrite initial selection save if most recent action was coalesced\n\t\thistoryForUndo.stack[index] = { historyForUndo.ssCurrent, topLine };\n\t}\n}\n\nvoid ModelState::RememberSelectionForRedoOntoStack(int index, const Selection &sel, Sci::Line topLine) {\n\thistoryForRedo.stack[index] = { sel.ToString(), topLine };\n}\n\nSelectionWithScroll ModelState::SelectionFromStack(int index, UndoRedo history) const {\n\tconst SelectionHistory &sh = history == UndoRedo::undo ? historyForUndo : historyForRedo;\n\tconst SelectionStack::const_iterator it = sh.stack.find(index);\n\tif (it != sh.stack.end()) {\n\t\treturn it->second;\n\t}\n\treturn {};\n}\n\nvoid ModelState::TruncateUndo(int index) {\n\tconst SelectionStack::const_iterator itUndo = historyForUndo.stack.find(index);\n\thistoryForUndo.stack.erase(itUndo, historyForUndo.stack.end());\n\tconst SelectionStack::const_iterator itRedo = historyForRedo.stack.find(index);\n\thistoryForRedo.stack.erase(itRedo, historyForRedo.stack.end());\n}\n\nEditModel::EditModel() : braces{} {\n\tinOverstrike = false;\n\txOffset = 0;\n\ttrackLineWidth = false;\n\tposDrag = SelectionPosition(Sci::invalidPosition);\n\tbraces[0] = Sci::invalidPosition;\n\tbraces[1] = Sci::invalidPosition;\n\tbracesMatchStyle = StyleBraceBad;\n\thighlightGuideColumn = 0;\n\thasFocus = false;\n\tprimarySelection = true;\n\timeInteraction = IMEInteraction::Windowed;\n\tbidirectional = Bidirectional::Disabled;\n\tfoldFlags = FoldFlag::None;\n\tfoldDisplayTextStyle = FoldDisplayTextStyle::Hidden;\n\thotspot = Range(Sci::invalidPosition);\n\thotspotSingleLine = true;\n\thoverIndicatorPos = Sci::invalidPosition;\n\twrapWidth = LineLayout::wrapWidthInfinite;\n\treprs = std::make_unique<SpecialRepresentations>();\n\n\t//Au\n\tcbNotify = nullptr; cbNotifyParam = nullptr;\n\tcbAnnotationDraw = nullptr; cbAnnotationDrawParam = nullptr;\n\tcbMarginDraw = nullptr; cbMarginsMask = 0;\n\n\tpdoc = new Document(DocumentOption::Default);\n\tpdoc->AddRef();\n\tpcs = ContractionStateCreate(pdoc->IsLarge());\n}\n\nEditModel::~EditModel() {\n\ttry {\n\t\t// Erasing the view state won't throw even though SetViewState\n\t\t// and the resulting map::erase aren't marked noexcept.\n\t\tpdoc->SetViewState(this, {});\n\t\t// This never throws but isn't marked noexcept for compatibility\n\t\tpdoc->Release();\n\t} catch (...) {\n\t\t// Ignore any exception\n\t}\n\tpdoc = nullptr;\n}\n\nbool EditModel::BidirectionalEnabled() const noexcept {\n\treturn (bidirectional != Bidirectional::Disabled) &&\n\t\t(CpUtf8 == pdoc->dbcsCodePage);\n}\n\nbool EditModel::BidirectionalR2L() const noexcept {\n\treturn bidirectional == Bidirectional::R2L;\n}\n\nSurfaceMode EditModel::CurrentSurfaceMode() const noexcept {\n\treturn SurfaceMode(pdoc->dbcsCodePage, BidirectionalR2L());\n}\n\nvoid EditModel::SetDefaultFoldDisplayText(const char *text) {\n\tdefaultFoldDisplayText = IsNullOrEmpty(text) ? UniqueString() : UniqueStringCopy(text);\n}\n\nconst char *EditModel::GetDefaultFoldDisplayText() const noexcept {\n\treturn defaultFoldDisplayText.get();\n}\n\nconst char *EditModel::GetFoldDisplayText(Sci::Line lineDoc) const noexcept {\n\tif (foldDisplayTextStyle == FoldDisplayTextStyle::Hidden || pcs->GetExpanded(lineDoc)) {\n\t\treturn nullptr;\n\t}\n\n\tconst char *text = pcs->GetFoldDisplayText(lineDoc);\n\treturn text ? text : defaultFoldDisplayText.get();\n}\n\nInSelection EditModel::LineEndInSelection(Sci::Line lineDoc) const {\n\tconst Sci::Position posAfterLineEnd = pdoc->LineStart(lineDoc + 1);\n\treturn sel.InSelectionForEOL(posAfterLineEnd);\n}\n\nint EditModel::GetMark(Sci::Line line) const {\n\treturn pdoc->GetMark(line, FlagSet(changeHistoryOption, ChangeHistoryOption::Markers));\n}\n\nvoid EditModel::EnsureModelState() {\n\tif (!modelState && (undoSelectionHistoryOption != UndoSelectionHistoryOption::Disabled)) {\n\t\tif (ViewStateShared vss = pdoc->GetViewState(this)) {\n\t\t\tmodelState = std::dynamic_pointer_cast<ModelState>(vss);\n\t\t} else {\n\t\t\tmodelState = std::make_shared<ModelState>();\n\t\t\tpdoc->SetViewState(this, std::static_pointer_cast<ViewState>(modelState));\n\t\t}\n\t}\n}\n\nvoid EditModel::ChangeUndoSelectionHistory(Scintilla::UndoSelectionHistoryOption undoSelectionHistoryOptionNew) {\n\tundoSelectionHistoryOption = undoSelectionHistoryOptionNew;\n\tif (undoSelectionHistoryOption == UndoSelectionHistoryOption::Disabled) {\n\t\tmodelState.reset();\n\t\tpdoc->SetViewState(this, {});\n\t}\n}\n"
  },
  {
    "path": "Libraries/scintilla/src/EditModel.h",
    "content": "// Scintilla source code edit control\n/** @file EditModel.h\n ** Defines the editor state that must be visible to EditorView.\n **/\n// Copyright 1998-2014 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#ifndef EDITMODEL_H\n#define EDITMODEL_H\n\nnamespace Scintilla::Internal {\n\n/**\n*/\nclass Caret {\npublic:\n\tbool active;\n\tbool on;\n\tint period;\n\n\tCaret() noexcept;\n};\n\nenum class UndoRedo { undo, redo };\n\n// Selection stack is sparse so use a map\n\nstruct SelectionWithScroll {\n\tstd::string selection;\n\tSci::Line topLine = 0;\n};\n\nusing SelectionStack = std::map<int, SelectionWithScroll>;\n\nstruct SelectionHistory {\n\tint indexCurrent = 0;\n\tstd::string ssCurrent;\n\tSelectionStack stack;\n};\n\nstruct ModelState : ViewState {\n\tSelectionHistory historyForUndo;\n\tSelectionHistory historyForRedo;\n\tvoid RememberSelectionForUndo(int index, const Selection &sel);\n\tvoid ForgetSelectionForUndo() noexcept;\n\tvoid RememberSelectionOntoStack(int index, Sci::Line topLine);\n\tvoid RememberSelectionForRedoOntoStack(int index, const Selection &sel, Sci::Line topLine);\n\tSelectionWithScroll SelectionFromStack(int index, UndoRedo history) const;\n\tvirtual void TruncateUndo(int index) final;\n};\n\nusing ModelStateShared = std::shared_ptr<ModelState>;\n\nclass EditModel {\npublic:\n\tbool inOverstrike;\n\tint xOffset;\t\t///< Horizontal scrolled amount in pixels\n\tbool trackLineWidth;\n\n\tstd::unique_ptr<SpecialRepresentations> reprs;\n\tCaret caret;\n\tSelectionPosition posDrag;\n\tSci::Position braces[2];\n\tint bracesMatchStyle;\n\tint highlightGuideColumn;\n\tbool hasFocus;\n\tSelection sel;\n\tbool primarySelection;\n\tstd::string copySeparator;\n\n\tScintilla::IMEInteraction imeInteraction;\n\tScintilla::Bidirectional bidirectional;\n\n\tScintilla::FoldFlag foldFlags;\n\tScintilla::FoldDisplayTextStyle foldDisplayTextStyle;\n\tUniqueString defaultFoldDisplayText;\n\tstd::unique_ptr<IContractionState> pcs;\n\t// Hotspot support\n\tRange hotspot;\n\tbool hotspotSingleLine;\n\tSci::Position hoverIndicatorPos;\n\n\tScintilla::ChangeHistoryOption changeHistoryOption = Scintilla::ChangeHistoryOption::Disabled;\n\n\t// Wrapping support\n\tint wrapWidth;\n\n\tDocument *pdoc;\n\n\tScintilla::UndoSelectionHistoryOption undoSelectionHistoryOption = UndoSelectionHistoryOption::Disabled;\n\tbool needRedoRemembered = false;\n\tModelStateShared modelState;\n\n\t//Au\n\tSci_NotifyCallback cbNotify; void* cbNotifyParam; //can use callback function instead of WM_NOTIFY.\n\tSci_AnnotationDrawCallback cbAnnotationDraw; void* cbAnnotationDrawParam; //to draw in annotation eg image instead of text.\n\tSci_MarginDrawCallback cbMarginDraw; int cbMarginsMask; //to draw eg images in margin.\n\n\tEditModel();\n\t// Deleted so EditModel objects can not be copied.\n\tEditModel(const EditModel &) = delete;\n\tEditModel(EditModel &&) = delete;\n\tEditModel &operator=(const EditModel &) = delete;\n\tEditModel &operator=(EditModel &&) = delete;\n\tvirtual ~EditModel();\n\tvirtual Sci::Line TopLineOfMain() const noexcept = 0;\n\tvirtual Point GetVisibleOriginInMain() const = 0;\n\tvirtual Sci::Line LinesOnScreen() const = 0;\n\tbool BidirectionalEnabled() const noexcept;\n\tbool BidirectionalR2L() const noexcept;\n\tSurfaceMode CurrentSurfaceMode() const noexcept;\n\tvoid SetDefaultFoldDisplayText(const char *text);\n\tconst char *GetDefaultFoldDisplayText() const noexcept;\n\tconst char *GetFoldDisplayText(Sci::Line lineDoc) const noexcept;\n\tInSelection LineEndInSelection(Sci::Line lineDoc) const;\n\t[[nodiscard]] int GetMark(Sci::Line line) const;\n\n\tvoid EnsureModelState();\n\tvoid ChangeUndoSelectionHistory(Scintilla::UndoSelectionHistoryOption undoSelectionHistoryOptionNew);\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/src/EditView.cxx",
    "content": "// Scintilla source code edit control\n/** @file EditView.cxx\n ** Defines the appearance of the main text area of the editor window.\n **/\n// Copyright 1998-2014 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#include <cstddef>\n#include <cstdlib>\n#include <cstdint>\n#include <cassert>\n#include <cstring>\n#include <cstdio>\n#include <cmath>\n\n#include <stdexcept>\n#include <string>\n#include <string_view>\n#include <vector>\n#include <map>\n#include <set>\n#include <forward_list>\n#include <optional>\n#include <algorithm>\n#include <iterator>\n#include <memory>\n#include <chrono>\n#include <atomic>\n#include <thread>\n#include <future>\n\n#include \"ScintillaTypes.h\"\n#include \"ScintillaMessages.h\"\n#include \"ScintillaStructures.h\"\n#include \"ILoader.h\"\n#include \"ILexer.h\"\n\n#include \"Debugging.h\"\n#include \"Geometry.h\"\n#include \"Platform.h\"\n\n#include \"CharacterType.h\"\n#include \"CharacterCategoryMap.h\"\n#include \"Position.h\"\n#include \"UniqueString.h\"\n#include \"SplitVector.h\"\n#include \"Partitioning.h\"\n#include \"RunStyles.h\"\n#include \"ContractionState.h\"\n#include \"CellBuffer.h\"\n#include \"PerLine.h\"\n#include \"KeyMap.h\"\n#include \"Indicator.h\"\n#include \"LineMarker.h\"\n#include \"Style.h\"\n#include \"ViewStyle.h\"\n#include \"CharClassify.h\"\n#include \"Decoration.h\"\n#include \"CaseFolder.h\"\n#include \"Document.h\"\n#include \"UniConversion.h\"\n#include \"Selection.h\"\n#include \"PositionCache.h\"\n#include \"EditModel.h\"\n#include \"MarginView.h\"\n#include \"EditView.h\"\n#include \"ElapsedPeriod.h\"\n\nusing namespace Scintilla;\nusing namespace Scintilla::Internal;\n\nPrintParameters::PrintParameters() noexcept {\n\tmagnification = 0;\n\tcolourMode = PrintOption::Normal;\n\twrapState = Wrap::Word;\n}\n\nnamespace {\n\nint WidthStyledText(Surface *surface, const ViewStyle &vs, int styleOffset,\n\tconst char *text, const unsigned char *styles, size_t len) {\n\tint width = 0;\n\tsize_t start = 0;\n\twhile (start < len) {\n\t\tconst unsigned char style = styles[start];\n\t\tsize_t endSegment = start;\n\t\twhile ((endSegment + 1 < len) && (styles[endSegment + 1] == style))\n\t\t\tendSegment++;\n\t\tconst Font *fontText = vs.styles[style + styleOffset].font.get();\n\t\tconst std::string_view sv(text + start, endSegment - start + 1);\n\t\twidth += static_cast<int>(surface->WidthText(fontText, sv));\n\t\tstart = endSegment + 1;\n\t}\n\treturn width;\n}\n\n}\n\nnamespace Scintilla::Internal {\n\nbool ValidStyledText(const ViewStyle &vs, size_t styleOffset, const StyledText &st) noexcept {\n\tif (st.multipleStyles) {\n\t\tfor (size_t iStyle = 0; iStyle<st.length; iStyle++) {\n\t\t\tif (!vs.ValidStyle(styleOffset + st.styles[iStyle]))\n\t\t\t\treturn false;\n\t\t}\n\t} else {\n\t\tif (!vs.ValidStyle(styleOffset + st.style))\n\t\t\treturn false;\n\t}\n\treturn true;\n}\n\nint WidestLineWidth(Surface *surface, const ViewStyle &vs, int styleOffset, const StyledText &st) {\n\tint widthMax = 0;\n\tsize_t start = 0;\n\twhile (start < st.length) {\n\t\tconst size_t lenLine = st.LineLength(start);\n\t\tint widthSubLine;\n\t\tif (st.multipleStyles) {\n\t\t\twidthSubLine = WidthStyledText(surface, vs, styleOffset, st.text + start, st.styles + start, lenLine);\n\t\t} else {\n\t\t\tconst Font *fontText = vs.styles[styleOffset + st.style].font.get();\n\t\t\tconst std::string_view text(st.text + start, lenLine);\n\t\t\twidthSubLine = static_cast<int>(surface->WidthText(fontText, text));\n\t\t}\n\t\tif (widthSubLine > widthMax)\n\t\t\twidthMax = widthSubLine;\n\t\tstart += lenLine + 1;\n\t}\n\treturn widthMax;\n}\n\nvoid DrawTextNoClipPhase(Surface *surface, PRectangle rc, const Style &style, XYPOSITION ybase,\n\tstd::string_view text, DrawPhase phase) {\n\tconst Font *fontText = style.font.get();\n\tif (FlagSet(phase, DrawPhase::back)) {\n\t\tif (FlagSet(phase, DrawPhase::text)) {\n\t\t\t// Drawing both\n\t\t\tsurface->DrawTextNoClip(rc, fontText, ybase, text,\n\t\t\t\tstyle.fore, style.back);\n\t\t} else {\n\t\t\tsurface->FillRectangleAligned(rc, Fill(style.back));\n\t\t}\n\t} else if (FlagSet(phase, DrawPhase::text)) {\n\t\tsurface->DrawTextTransparent(rc, fontText, ybase, text, style.fore);\n\t}\n}\n\nvoid DrawStyledText(Surface *surface, const ViewStyle &vs, int styleOffset, PRectangle rcText,\n\tconst StyledText &st, size_t start, size_t length, DrawPhase phase) {\n\n\tif (st.multipleStyles) {\n\t\tint x = static_cast<int>(rcText.left);\n\t\tsize_t i = 0;\n\t\twhile (i < length) {\n\t\t\tsize_t end = i;\n\t\t\tsize_t style = st.styles[i + start];\n\t\t\twhile (end < length - 1 && st.styles[start + end + 1] == style)\n\t\t\t\tend++;\n\t\t\tstyle += styleOffset;\n\t\t\tconst Font *fontText = vs.styles[style].font.get();\n\t\t\tconst std::string_view text(st.text + start + i, end - i + 1);\n\t\t\tconst int width = static_cast<int>(surface->WidthText(fontText, text));\n\t\t\tPRectangle rcSegment = rcText;\n\t\t\trcSegment.left = static_cast<XYPOSITION>(x);\n\t\t\trcSegment.right = static_cast<XYPOSITION>(x + width + 1);\n\t\t\tDrawTextNoClipPhase(surface, rcSegment, vs.styles[style],\n\t\t\t\trcText.top + vs.maxAscent, text, phase);\n\t\t\tx += width;\n\t\t\ti = end + 1;\n\t\t}\n\t} else {\n\t\tconst size_t style = st.style + styleOffset;\n\t\tDrawTextNoClipPhase(surface, rcText, vs.styles[style],\n\t\t\trcText.top + vs.maxAscent,\n\t\t\tstd::string_view(st.text + start, length), phase);\n\t}\n}\n\n}\n\nEditView::EditView() {\n\ttabWidthMinimumPixels = 2; // needed for calculating tab stops for fractional proportional fonts\n\tdrawOverstrikeCaret = true;\n\tbufferedDraw = true;\n\tphasesDraw = PhasesDraw::Two;\n\tlineWidthMaxSeen = 0;\n\tadditionalCaretsBlink = true;\n\tadditionalCaretsVisible = true;\n\timeCaretBlockOverride = false;\n\tllc.SetLevel(LineCache::Caret);\n\tposCache = CreatePositionCache();\n\tposCache->SetSize(0x400);\n\tmaxLayoutThreads = 1;\n\ttabArrowHeight = 4;\n\tcustomDrawTabArrow = nullptr;\n\tcustomDrawWrapMarker = nullptr;\n}\n\nEditView::~EditView() = default;\n\nbool EditView::SetTwoPhaseDraw(bool twoPhaseDraw) noexcept {\n\tconst PhasesDraw phasesDrawNew = twoPhaseDraw ? PhasesDraw::Two : PhasesDraw::One;\n\tconst bool redraw = phasesDraw != phasesDrawNew;\n\tphasesDraw = phasesDrawNew;\n\treturn redraw;\n}\n\nbool EditView::SetPhasesDraw(int phases) noexcept {\n\tconst PhasesDraw phasesDrawNew = static_cast<PhasesDraw>(phases);\n\tconst bool redraw = phasesDraw != phasesDrawNew;\n\tphasesDraw = phasesDrawNew;\n\treturn redraw;\n}\n\nbool EditView::LinesOverlap() const noexcept {\n\treturn phasesDraw == PhasesDraw::Multiple;\n}\n\nvoid EditView::SetLayoutThreads(unsigned int threads) noexcept {\n\tmaxLayoutThreads = std::clamp(threads, 1U, std::thread::hardware_concurrency());\n}\n\nunsigned int EditView::GetLayoutThreads() const noexcept {\n\treturn maxLayoutThreads;\n}\n\nvoid EditView::ClearAllTabstops() noexcept {\n\tldTabstops.reset();\n}\n\nXYPOSITION EditView::NextTabstopPos(Sci::Line line, XYPOSITION x, XYPOSITION tabWidth) const noexcept {\n\tconst int next = GetNextTabstop(line, static_cast<int>(x + tabWidthMinimumPixels));\n\tif (next > 0)\n\t\treturn static_cast<XYPOSITION>(next);\n\treturn (static_cast<int>((x + tabWidthMinimumPixels) / tabWidth) + 1) * tabWidth;\n}\n\nbool EditView::ClearTabstops(Sci::Line line) noexcept {\n\treturn ldTabstops && ldTabstops->ClearTabstops(line);\n}\n\nbool EditView::AddTabstop(Sci::Line line, int x) {\n\tif (!ldTabstops) {\n\t\tldTabstops = std::make_unique<LineTabstops>();\n\t}\n\treturn ldTabstops && ldTabstops->AddTabstop(line, x);\n}\n\nint EditView::GetNextTabstop(Sci::Line line, int x) const noexcept {\n\tif (ldTabstops) {\n\t\treturn ldTabstops->GetNextTabstop(line, x);\n\t} else {\n\t\treturn 0;\n\t}\n}\n\nvoid EditView::LinesAddedOrRemoved(Sci::Line lineOfPos, Sci::Line linesAdded) {\n\tif (ldTabstops) {\n\t\tif (linesAdded > 0) {\n\t\t\tfor (Sci::Line line = lineOfPos; line < lineOfPos + linesAdded; line++) {\n\t\t\t\tldTabstops->InsertLine(line);\n\t\t\t}\n\t\t} else {\n\t\t\tfor (Sci::Line line = (lineOfPos + -linesAdded) - 1; line >= lineOfPos; line--) {\n\t\t\t\tldTabstops->RemoveLine(line);\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid EditView::DropGraphics() noexcept {\n\tpixmapLine.reset();\n\tpixmapIndentGuide.reset();\n\tpixmapIndentGuideHighlight.reset();\n}\n\nvoid EditView::RefreshPixMaps(Surface *surfaceWindow, const ViewStyle &vsDraw) {\n\tif (!(pixmapIndentGuide && pixmapIndentGuideHighlight)) {\n\t\t// 1 extra pixel in height so can handle odd/even positions and so produce a continuous line\n\t\tpixmapIndentGuide = surfaceWindow->AllocatePixMap(1, vsDraw.lineHeight + 1);\n\t\tpixmapIndentGuideHighlight = surfaceWindow->AllocatePixMap(1, vsDraw.lineHeight + 1);\n\t\tconst PRectangle rcIG = PRectangle::FromInts(0, 0, 1, vsDraw.lineHeight);\n\t\tpixmapIndentGuide->FillRectangle(rcIG, vsDraw.styles[StyleIndentGuide].back);\n\t\tpixmapIndentGuideHighlight->FillRectangle(rcIG, vsDraw.styles[StyleBraceLight].back);\n\t\tfor (int stripe = 1; stripe < vsDraw.lineHeight + 1; stripe += 2) {\n\t\t\tconst PRectangle rcPixel = PRectangle::FromInts(0, stripe, 1, stripe + 1);\n\t\t\tpixmapIndentGuide->FillRectangle(rcPixel, vsDraw.styles[StyleIndentGuide].fore);\n\t\t\tpixmapIndentGuideHighlight->FillRectangle(rcPixel, vsDraw.styles[StyleBraceLight].fore);\n\t\t}\n\t\tpixmapIndentGuide->FlushDrawing();\n\t\tpixmapIndentGuideHighlight->FlushDrawing();\n\t}\n}\n\nstd::shared_ptr<LineLayout> EditView::RetrieveLineLayout(Sci::Line lineNumber, const EditModel &model) {\n\tconst Sci::Position posLineStart = model.pdoc->LineStart(lineNumber);\n\tconst Sci::Position posLineEnd = model.pdoc->LineStart(lineNumber + 1);\n\tPLATFORM_ASSERT(posLineEnd >= posLineStart);\n\tconst Sci::Line lineCaret = model.pdoc->SciLineFromPosition(model.sel.MainCaret());\n\treturn llc.Retrieve(lineNumber, lineCaret,\n\t\tstatic_cast<int>(posLineEnd - posLineStart), model.pdoc->GetStyleClock(),\n\t\tmodel.LinesOnScreen() + 1, model.pdoc->LinesTotal());\n}\n\nnamespace {\n\nconstexpr XYPOSITION epsilon = 0.0001f;\t// A small nudge to avoid floating point precision issues\n\n/**\n* Return the chDoc argument with case transformed as indicated by the caseForce argument.\n* chPrevious is needed for camel casing.\n* This only affects ASCII characters and is provided for languages with case-insensitive\n* ASCII keywords where the user wishes to view keywords in a preferred case.\n*/\ninline char CaseForce(Style::CaseForce caseForce, char chDoc, char chPrevious) noexcept {\n\tswitch (caseForce) {\n\tcase Style::CaseForce::mixed:\n\t\treturn chDoc;\n\tcase Style::CaseForce::lower:\n\t\treturn MakeLowerCase(chDoc);\n\tcase Style::CaseForce::upper:\n\t\treturn MakeUpperCase(chDoc);\n\tcase Style::CaseForce::camel:\n\tdefault:\t// default should not occur, included to avoid warnings\n\t\tif (IsUpperOrLowerCase(chDoc) && !IsUpperOrLowerCase(chPrevious)) {\n\t\t\treturn MakeUpperCase(chDoc);\n\t\t} else {\n\t\t\treturn MakeLowerCase(chDoc);\n\t\t}\n\t}\n}\n\nvoid LayoutSegments(IPositionCache *pCache,\n\tSurface *surface,\n\tconst ViewStyle &vstyle,\n\tLineLayout *ll,\n\tconst std::vector<TextSegment> &segments,\n\tstd::atomic<uint32_t> &nextIndex,\n\tconst bool textUnicode,\n\tconst bool multiThreaded) {\n\twhile (true) {\n\t\tconst uint32_t i = nextIndex.fetch_add(1, std::memory_order_acq_rel);\n\t\tif (i >= segments.size()) {\n\t\t\tbreak;\n\t\t}\n\t\tconst TextSegment &ts = segments[i];\n\t\tconst unsigned int styleSegment = ll->styles[ts.start];\n\t\tXYPOSITION *positions = &ll->positions[ts.start + 1];\n\t\tif (vstyle.styles[styleSegment].visible) {\n\t\t\tif (ts.representation) {\n\t\t\t\tXYPOSITION representationWidth = 0.0;\n\t\t\t\t// Tab is a special case of representation, taking a variable amount of space\n\t\t\t\t// which will be filled in later.\n\t\t\t\tif (ll->chars[ts.start] != '\\t') {\n\t\t\t\t\trepresentationWidth = vstyle.controlCharWidth;\n\t\t\t\t\tif (representationWidth <= 0.0) {\n\t\t\t\t\t\tassert(ts.representation->stringRep.length() <= Representation::maxLength);\n\t\t\t\t\t\tXYPOSITION positionsRepr[Representation::maxLength + 1];\n\t\t\t\t\t\t// ts.representation->stringRep is UTF-8.\n\t\t\t\t\t\tpCache->MeasureWidths(surface, vstyle, StyleControlChar, true, ts.representation->stringRep,\n\t\t\t\t\t\t\tpositionsRepr, multiThreaded);\n\t\t\t\t\t\trepresentationWidth = positionsRepr[ts.representation->stringRep.length() - 1];\n\t\t\t\t\t\tif (FlagSet(ts.representation->appearance, RepresentationAppearance::Blob)) {\n\t\t\t\t\t\t\trepresentationWidth += vstyle.ctrlCharPadding;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tstd::fill(positions, positions + ts.length, representationWidth);\n\t\t\t} else {\n\t\t\t\tif ((ts.length == 1) && (' ' == ll->chars[ts.start])) {\n\t\t\t\t\t// Over half the segments are single characters and of these about half are space characters.\n\t\t\t\t\tpositions[0] = vstyle.styles[styleSegment].spaceWidth;\n\t\t\t\t} else {\n\t\t\t\t\tpCache->MeasureWidths(surface, vstyle, styleSegment, textUnicode,\n\t\t\t\t\t\tstd::string_view(&ll->chars[ts.start], ts.length), positions, multiThreaded);\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (vstyle.styles[styleSegment].invisibleRepresentation[0]) {\n\t\t\tconst std::string_view text = vstyle.styles[styleSegment].invisibleRepresentation;\n\t\t\tXYPOSITION positionsRepr[Representation::maxLength + 1];\n\t\t\t// invisibleRepresentation is UTF-8.\n\t\t\tpCache->MeasureWidths(surface, vstyle, styleSegment, true, text, positionsRepr, multiThreaded);\n\t\t\tconst XYPOSITION representationWidth = positionsRepr[text.length() - 1];\n\t\t\tstd::fill(positions, positions + ts.length, representationWidth);\n\t\t}\n\t}\n}\n\n}\n\n/**\n* Fill in the LineLayout data for the given line.\n* Copy the given @a line and its styles from the document into local arrays.\n* Also determine the x position at which each character starts.\n*/\nvoid EditView::LayoutLine(const EditModel &model, Surface *surface, const ViewStyle &vstyle, LineLayout *ll, int width, bool callerMultiThreaded) {\n\tif (!ll)\n\t\treturn;\n\tconst Sci::Line line = ll->LineNumber();\n\tPLATFORM_ASSERT(line < model.pdoc->LinesTotal());\n\tPLATFORM_ASSERT(ll->chars);\n\tconst Sci::Position posLineStart = model.pdoc->LineStart(line);\n\tSci::Position posLineEnd = model.pdoc->LineStart(line + 1);\n\t// If the line is very long, limit the treatment to a length that should fit in the viewport\n\tif (posLineEnd >(posLineStart + ll->maxLineLength)) {\n\t\tposLineEnd = posLineStart + ll->maxLineLength;\n\t}\n\t// Hard to cope when too narrow, so just assume there is space\n\twidth = std::max(width, 20);\n\n\tif (ll->validity == LineLayout::ValidLevel::checkTextAndStyle) {\n\t\tSci::Position lineLength = posLineEnd - posLineStart;\n\t\tif (!vstyle.viewEOL) {\n\t\t\tlineLength = model.pdoc->LineEnd(line) - posLineStart;\n\t\t}\n\t\tif (lineLength == ll->numCharsInLine) {\n\t\t\t// See if chars, styles, indicators, are all the same\n\t\t\tbool allSame = true;\n\t\t\t// Check base line layout\n\t\t\tchar chPrevious = 0;\n\t\t\tfor (Sci::Position numCharsInLine = 0; numCharsInLine < lineLength; numCharsInLine++) {\n\t\t\t\tconst Sci::Position charInDoc = numCharsInLine + posLineStart;\n\t\t\t\tconst char chDoc = model.pdoc->CharAt(charInDoc);\n\t\t\t\tconst int styleByte = model.pdoc->StyleIndexAt(charInDoc);\n\t\t\t\tallSame = allSame &&\n\t\t\t\t\t(ll->styles[numCharsInLine] == styleByte);\n\t\t\t\tallSame = allSame &&\n\t\t\t\t\t(ll->chars[numCharsInLine] == CaseForce(vstyle.styles[styleByte].caseForce, chDoc, chPrevious));\n\t\t\t\tchPrevious = chDoc;\n\t\t\t}\n\t\t\tconst int styleByteLast = (posLineEnd > posLineStart) ? model.pdoc->StyleIndexAt(posLineEnd - 1) : 0;\n\t\t\tallSame = allSame && (ll->styles[lineLength] == styleByteLast);\t// For eolFilled\n\t\t\tif (allSame) {\n\t\t\t\tll->validity = (ll->widthLine != width) ? LineLayout::ValidLevel::positions : LineLayout::ValidLevel::lines;\n\t\t\t} else {\n\t\t\t\tll->validity = LineLayout::ValidLevel::invalid;\n\t\t\t}\n\t\t} else {\n\t\t\tll->validity = LineLayout::ValidLevel::invalid;\n\t\t}\n\t}\n\tif (ll->validity == LineLayout::ValidLevel::invalid) {\n\t\tll->widthLine = LineLayout::wrapWidthInfinite;\n\t\tll->lines = 1;\n\t\tif (vstyle.edgeState == EdgeVisualStyle::Background) {\n\t\t\tSci::Position edgePosition = model.pdoc->FindColumn(line, vstyle.theEdge.column);\n\t\t\tif (edgePosition >= posLineStart) {\n\t\t\t\tedgePosition -= posLineStart;\n\t\t\t}\n\t\t\tll->edgeColumn = static_cast<int>(edgePosition);\n\t\t} else {\n\t\t\tll->edgeColumn = -1;\n\t\t}\n\n\t\t// Fill base line layout\n\t\tconst int lineLength = static_cast<int>(posLineEnd - posLineStart);\n\t\tmodel.pdoc->GetCharRange(ll->chars.get(), posLineStart, lineLength);\n\t\tmodel.pdoc->GetStyleRange(ll->styles.get(), posLineStart, lineLength);\n\t\tconst int numCharsBeforeEOL = static_cast<int>(model.pdoc->LineEnd(line) - posLineStart);\n\t\tconst int numCharsInLine = (vstyle.viewEOL) ? lineLength : numCharsBeforeEOL;\n\t\tconst unsigned char styleByteLast = (lineLength > 0) ? ll->styles[lineLength - 1] : 0;\n\t\tif (vstyle.someStylesForceCase) {\n\t\t\tchar chPrevious = 0;\n\t\t\tfor (int charInLine = 0; charInLine<lineLength; charInLine++) {\n\t\t\t\tconst char chDoc = ll->chars[charInLine];\n\t\t\t\tll->chars[charInLine] = CaseForce(vstyle.styles[ll->styles[charInLine]].caseForce, chDoc, chPrevious);\n\t\t\t\tchPrevious = chDoc;\n\t\t\t}\n\t\t}\n\t\tll->xHighlightGuide = 0;\n\t\t// Extra element at the end of the line to hold end x position and act as\n\t\tll->chars[numCharsInLine] = 0;   // Also triggers processing in the loops as this is a control character\n\t\tll->styles[numCharsInLine] = styleByteLast;\t// For eolFilled\n\n\t\t// Layout the line, determining the position of each character,\n\t\t// with an extra element at the end for the end of the line.\n\t\tll->positions[0] = 0;\n\t\tbool lastSegItalics = false;\n\n\t\tstd::vector<TextSegment> segments;\n\t\tBreakFinder bfLayout(ll, nullptr, Range(0, numCharsInLine), posLineStart, 0, BreakFinder::BreakFor::Text, model.pdoc, model.reprs.get(), nullptr);\n\t\twhile (bfLayout.More()) {\n\t\t\tsegments.push_back(bfLayout.Next());\n\t\t}\n\n\t\tll->ClearPositions();\n\n\t\tif (!segments.empty()) {\n\n\t\t\tconst size_t threadsForLength = std::max(1, numCharsInLine / bytesPerLayoutThread);\n\t\t\tsize_t threads = std::min<size_t>({ segments.size(), threadsForLength, maxLayoutThreads });\n\t\t\tif (!surface->SupportsFeature(Supports::ThreadSafeMeasureWidths) || callerMultiThreaded) {\n\t\t\t\tthreads = 1;\n\t\t\t}\n\n\t\t\tstd::atomic<uint32_t> nextIndex = 0;\n\n\t\t\tconst bool textUnicode = CpUtf8 == model.pdoc->dbcsCodePage;\n\t\t\tconst bool multiThreaded = threads > 1;\n\t\t\tconst bool multiThreadedContext = multiThreaded || callerMultiThreaded;\n\t\t\tIPositionCache *pCache = posCache.get();\n\n\t\t\t// If only 1 thread needed then use the main thread, else spin up multiple\n\t\t\tconst std::launch policy = (multiThreaded) ? std::launch::async : std::launch::deferred;\n\n\t\t\tstd::vector<std::future<void>> futures;\n\t\t\tfor (size_t th = 0; th < threads; th++) {\n\t\t\t\t// Find relative positions of everything except for tabs\n\t\t\t\tstd::future<void> fut = std::async(policy,\n\t\t\t\t\t[pCache, surface, &vstyle, &ll, &segments, &nextIndex, textUnicode, multiThreadedContext]() {\n\t\t\t\t\tLayoutSegments(pCache, surface, vstyle, ll, segments, nextIndex, textUnicode, multiThreadedContext);\n\t\t\t\t});\n\t\t\t\tfutures.push_back(std::move(fut));\n\t\t\t}\n\t\t\tfor (const std::future<void> &f : futures) {\n\t\t\t\tf.wait();\n\t\t\t}\n\t\t}\n\n\t\t// Accumulate absolute positions from relative positions within segments and expand tabs\n\t\tXYPOSITION xPosition = 0.0;\n\t\tsize_t iByte = 0;\n\t\tll->positions[iByte++] = xPosition;\n\t\tfor (const TextSegment &ts : segments) {\n\t\t\tif (vstyle.styles[ll->styles[ts.start]].visible &&\n\t\t\t\tts.representation &&\n\t\t\t\t(ll->chars[ts.start] == '\\t')) {\n\t\t\t\t// Simple visible tab, go to next tab stop\n\t\t\t\tconst XYPOSITION startTab = ll->positions[ts.start];\n\t\t\t\tconst XYPOSITION nextTab = NextTabstopPos(line, startTab, vstyle.tabWidth);\n\t\t\t\txPosition += nextTab - startTab;\n\t\t\t}\n\t\t\tconst XYPOSITION xBeginSegment = xPosition;\n\t\t\tfor (int i = 0; i < ts.length; i++) {\n\t\t\t\txPosition = ll->positions[iByte] + xBeginSegment;\n\t\t\t\tll->positions[iByte++] = xPosition;\n\t\t\t}\n\t\t}\n\n\t\tif (!segments.empty()) {\n\t\t\t// Not quite the same as before which would effectively ignore trailing invisible segments\n\t\t\tconst TextSegment &ts = segments.back();\n\t\t\tlastSegItalics = (!ts.representation) && ((ll->chars[ts.end() - 1] != ' ') && vstyle.styles[ll->styles[ts.start]].italic);\n\t\t}\n\n\t\t// Small hack to make lines that end with italics not cut off the edge of the last character\n\t\tif (lastSegItalics) {\n\t\t\tll->positions[numCharsInLine] += vstyle.lastSegItalicsOffset;\n\t\t}\n\t\tll->numCharsInLine = numCharsInLine;\n\t\tll->numCharsBeforeEOL = numCharsBeforeEOL;\n\t\tll->validity = LineLayout::ValidLevel::positions;\n\t}\n\tif ((ll->validity == LineLayout::ValidLevel::positions) || (ll->widthLine != width)) {\n\t\tll->widthLine = width;\n\t\tif (width == LineLayout::wrapWidthInfinite) {\n\t\t\tll->lines = 1;\n\t\t} else if (width > ll->positions[ll->numCharsInLine]) {\n\t\t\t// Simple common case where line does not need wrapping.\n\t\t\tll->lines = 1;\n\t\t} else {\n\t\t\tif (FlagSet(vstyle.wrap.visualFlags, WrapVisualFlag::End)) {\n\t\t\t\twidth -= static_cast<int>(vstyle.aveCharWidth); // take into account the space for end wrap mark\n\t\t\t}\n\t\t\tXYPOSITION wrapAddIndent = 0; // This will be added to initial indent of line\n\t\t\tswitch (vstyle.wrap.indentMode) {\n\t\t\tcase WrapIndentMode::Fixed:\n\t\t\t\twrapAddIndent = vstyle.wrap.visualStartIndent * vstyle.aveCharWidth;\n\t\t\t\tbreak;\n\t\t\tcase WrapIndentMode::Indent:\n\t\t\t\twrapAddIndent = model.pdoc->IndentSize() * vstyle.spaceWidth;\n\t\t\t\tbreak;\n\t\t\tcase WrapIndentMode::DeepIndent:\n\t\t\t\twrapAddIndent = model.pdoc->IndentSize() * 2 * vstyle.spaceWidth;\n\t\t\t\tbreak;\n\t\t\tdefault:\t// No additional indent for WrapIndentMode::Fixed\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tll->wrapIndent = wrapAddIndent;\n\t\t\tif (vstyle.wrap.indentMode != WrapIndentMode::Fixed) {\n\t\t\t\tfor (int i = 0; i < ll->numCharsInLine; i++) {\n\t\t\t\t\tif (!IsSpaceOrTab(ll->chars[i])) {\n\t\t\t\t\t\tll->wrapIndent += ll->positions[i]; // Add line indent\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Check for text width minimum\n\t\t\tif (ll->wrapIndent > width - static_cast<int>(vstyle.aveCharWidth) * 15)\n\t\t\t\tll->wrapIndent = wrapAddIndent;\n\t\t\t// Check for wrapIndent minimum\n\t\t\tif ((FlagSet(vstyle.wrap.visualFlags, WrapVisualFlag::Start)) && (ll->wrapIndent < vstyle.aveCharWidth))\n\t\t\t\tll->wrapIndent = vstyle.aveCharWidth; // Indent to show start visual\n\t\t\tll->WrapLine(model.pdoc, posLineStart, vstyle.wrap.state, width);\n\t\t}\n\t\tll->validity = LineLayout::ValidLevel::lines;\n\t}\n}\n\n// Fill the LineLayout bidirectional data fields according to each char style\n\nvoid EditView::UpdateBidiData(const EditModel &model, const ViewStyle &vstyle, LineLayout *ll) {\n\tif (model.BidirectionalEnabled()) {\n\t\tll->EnsureBidiData();\n\t\tfor (int stylesInLine = 0; stylesInLine < ll->numCharsInLine; stylesInLine++) {\n\t\t\tll->bidiData->stylesFonts[stylesInLine] = vstyle.styles[ll->styles[stylesInLine]].font;\n\t\t}\n\t\tll->bidiData->stylesFonts[ll->numCharsInLine].reset();\n\n\t\tfor (int charsInLine = 0; charsInLine < ll->numCharsInLine; charsInLine++) {\n\t\t\tconst int charWidth = UTF8DrawBytes(&ll->chars[charsInLine], ll->numCharsInLine - charsInLine);\n\t\t\tconst Representation *repr = model.reprs->RepresentationFromCharacter(std::string_view(&ll->chars[charsInLine], charWidth));\n\n\t\t\tll->bidiData->widthReprs[charsInLine] = 0.0f;\n\t\t\tif (repr && ll->chars[charsInLine] != '\\t') {\n\t\t\t\tll->bidiData->widthReprs[charsInLine] = ll->positions[charsInLine + charWidth] - ll->positions[charsInLine];\n\t\t\t}\n\t\t\tif (charWidth > 1) {\n\t\t\t\tfor (int c = 1; c < charWidth; c++) {\n\t\t\t\t\tcharsInLine++;\n\t\t\t\t\tll->bidiData->widthReprs[charsInLine] = 0.0f;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tll->bidiData->widthReprs[ll->numCharsInLine] = 0.0f;\n\t} else {\n\t\tll->bidiData.reset();\n\t}\n}\n\nPoint EditView::LocationFromPosition(Surface *surface, const EditModel &model, SelectionPosition pos, Sci::Line topLine,\n\t\t\t\t     const ViewStyle &vs, PointEnd pe, const PRectangle rcClient) {\n\tPoint pt;\n\tif (pos.Position() == Sci::invalidPosition)\n\t\treturn pt;\n\tSci::Line lineDoc = model.pdoc->SciLineFromPosition(pos.Position());\n\tSci::Position posLineStart = model.pdoc->LineStart(lineDoc);\n\tif (FlagSet(pe, PointEnd::lineEnd) && (lineDoc > 0) && (pos.Position() == posLineStart)) {\n\t\t// Want point at end of first line\n\t\tlineDoc--;\n\t\tposLineStart = model.pdoc->LineStart(lineDoc);\n\t}\n\tconst Sci::Line lineVisible = model.pcs->DisplayFromDoc(lineDoc);\n\tstd::shared_ptr<LineLayout> ll = RetrieveLineLayout(lineDoc, model);\n\tif (surface && ll) {\n\t\tLayoutLine(model, surface, vs, ll.get(), model.wrapWidth);\n\t\tconst int posInLine = static_cast<int>(pos.Position() - posLineStart);\n\t\tpt = ll->PointFromPosition(posInLine, vs.lineHeight, pe);\n\t\tpt.x += vs.textStart - model.xOffset;\n\n\t\tif (model.BidirectionalEnabled()) {\n\t\t\t// Fill the line bidi data\n\t\t\tUpdateBidiData(model, vs, ll.get());\n\n\t\t\t// Find subLine\n\t\t\tconst int subLine = ll->SubLineFromPosition(posInLine, pe);\n\t\t\tconst int caretPosition = posInLine - ll->LineStart(subLine);\n\n\t\t\t// Get the point from current position\n\t\t\tconst ScreenLine screenLine(ll.get(), subLine, vs, rcClient.right, tabWidthMinimumPixels);\n\t\t\tstd::unique_ptr<IScreenLineLayout> slLayout = surface->Layout(&screenLine);\n\t\t\tpt.x = slLayout->XFromPosition(caretPosition);\n\n\t\t\tpt.x += vs.textStart - model.xOffset;\n\n\t\t\tpt.y = 0;\n\t\t\tif (posInLine >= ll->LineStart(subLine)) {\n\t\t\t\tpt.y = static_cast<XYPOSITION>(subLine*vs.lineHeight);\n\t\t\t}\n\t\t}\n\t\tpt.y += static_cast<XYPOSITION>((lineVisible - topLine) * vs.lineHeight);\n\t\tpt.x += pos.VirtualSpaceWidth(vs.styles[ll->EndLineStyle()].spaceWidth);\n\t}\n\treturn pt;\n}\n\nRange EditView::RangeDisplayLine(Surface *surface, const EditModel &model, Sci::Line lineVisible, const ViewStyle &vs) {\n\tRange rangeSubLine = Range(0, 0);\n\tif (lineVisible < 0) {\n\t\treturn rangeSubLine;\n\t}\n\tconst Sci::Line lineDoc = model.pcs->DocFromDisplay(lineVisible);\n\tconst Sci::Position positionLineStart = model.pdoc->LineStart(lineDoc);\n\tstd::shared_ptr<LineLayout> ll = RetrieveLineLayout(lineDoc, model);\n\tif (surface && ll) {\n\t\tLayoutLine(model, surface, vs, ll.get(), model.wrapWidth);\n\t\tconst Sci::Line lineStartSet = model.pcs->DisplayFromDoc(lineDoc);\n\t\tconst int subLine = static_cast<int>(lineVisible - lineStartSet);\n\t\tif (subLine < ll->lines) {\n\t\t\trangeSubLine = ll->SubLineRange(subLine, LineLayout::Scope::visibleOnly);\n\t\t\tif (subLine == ll->lines-1) {\n\t\t\t\trangeSubLine.end = model.pdoc->LineStart(lineDoc + 1) -\n\t\t\t\t\tpositionLineStart;\n\t\t\t}\n\t\t}\n\t}\n\trangeSubLine.start += positionLineStart;\n\trangeSubLine.end += positionLineStart;\n\treturn rangeSubLine;\n}\n\nSelectionPosition EditView::SPositionFromLocation(Surface *surface, const EditModel &model, PointDocument pt, bool canReturnInvalid,\n\tbool charPosition, bool virtualSpace, const ViewStyle &vs, const PRectangle rcClient) {\n\tpt.x = pt.x - vs.textStart;\n\tSci::Line visibleLine = static_cast<int>(std::floor(pt.y / vs.lineHeight));\n\tif (!canReturnInvalid && (visibleLine < 0))\n\t\tvisibleLine = 0;\n\tconst Sci::Line lineDoc = model.pcs->DocFromDisplay(visibleLine);\n\tif (canReturnInvalid && (lineDoc < 0))\n\t\treturn SelectionPosition(Sci::invalidPosition);\n\tif (lineDoc >= model.pdoc->LinesTotal())\n\t\treturn SelectionPosition(canReturnInvalid ? Sci::invalidPosition :\n\t\t\tmodel.pdoc->Length());\n\tconst Sci::Position posLineStart = model.pdoc->LineStart(lineDoc);\n\tstd::shared_ptr<LineLayout> ll = RetrieveLineLayout(lineDoc, model);\n\tif (surface && ll) {\n\t\tLayoutLine(model, surface, vs, ll.get(), model.wrapWidth);\n\t\tconst Sci::Line lineStartSet = model.pcs->DisplayFromDoc(lineDoc);\n\t\tint subLine = static_cast<int>(visibleLine - lineStartSet); //Au: no const\n\n\t\t//Au: prevent jumping caret to the end of line on click (or drag) on an annotation. It's very annoying, eg may scroll.\n\t\t//Platform::DebugPrintf(\"%i %i %i\", lineStartSet, subLine, ll->lines);\n\t\t//It seems that ll->lines is the number of text lines (including wrapped lines but not annotations).\n\t\tif(subLine >= ll->lines) subLine = ll->lines - 1; //if annotation, use the last text line\n\n\t\tif (subLine < ll->lines) {\n\t\t\tconst Range rangeSubLine = ll->SubLineRange(subLine, LineLayout::Scope::visibleOnly);\n\t\t\tconst XYPOSITION subLineStart = ll->positions[rangeSubLine.start];\n\t\t\tif (subLine > 0)\t// Wrapped\n\t\t\t\tpt.x -= ll->wrapIndent;\n\t\t\tSci::Position positionInLine = 0;\n\t\t\tif (model.BidirectionalEnabled()) {\n\t\t\t\t// Fill the line bidi data\n\t\t\t\tUpdateBidiData(model, vs, ll.get());\n\n\t\t\t\tconst ScreenLine screenLine(ll.get(), subLine, vs, rcClient.right, tabWidthMinimumPixels);\n\t\t\t\tstd::unique_ptr<IScreenLineLayout> slLayout = surface->Layout(&screenLine);\n\t\t\t\tpositionInLine = slLayout->PositionFromX(pt.x, charPosition) +\n\t\t\t\t\trangeSubLine.start;\n\t\t\t} else {\n\t\t\t\tpositionInLine = ll->FindPositionFromX(pt.x + subLineStart,\n\t\t\t\t\trangeSubLine, charPosition);\n\t\t\t}\n\t\t\tif (positionInLine < rangeSubLine.end) {\n\t\t\t\treturn SelectionPosition(model.pdoc->MovePositionOutsideChar(positionInLine + posLineStart, 1));\n\t\t\t}\n\t\t\tif (virtualSpace) {\n\t\t\t\tconst XYPOSITION spaceWidth = vs.styles[ll->EndLineStyle()].spaceWidth;\n\t\t\t\tconst int spaceOffset = static_cast<int>(\n\t\t\t\t\t(pt.x + subLineStart - ll->positions[rangeSubLine.end] + spaceWidth / 2) / spaceWidth);\n\t\t\t\treturn SelectionPosition(rangeSubLine.end + posLineStart, spaceOffset);\n\t\t\t} else if (canReturnInvalid) {\n\t\t\t\tif (pt.x < (ll->positions[rangeSubLine.end] - subLineStart)) {\n\t\t\t\t\treturn SelectionPosition(model.pdoc->MovePositionOutsideChar(rangeSubLine.end + posLineStart, 1));\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\treturn SelectionPosition(rangeSubLine.end + posLineStart);\n\t\t\t}\n\t\t}\n\t\tif (!canReturnInvalid)\n\t\t\treturn SelectionPosition(ll->numCharsInLine + posLineStart);\n\t}\n\treturn SelectionPosition(canReturnInvalid ? Sci::invalidPosition : posLineStart);\n}\n\n/**\n* Find the document position corresponding to an x coordinate on a particular document line.\n* Ensure is between whole characters when document is in multi-byte or UTF-8 mode.\n* This method is used for rectangular selections and does not work on wrapped lines.\n*/\nSelectionPosition EditView::SPositionFromLineX(Surface *surface, const EditModel &model, Sci::Line lineDoc, int x, const ViewStyle &vs) {\n\tstd::shared_ptr<LineLayout> ll = RetrieveLineLayout(lineDoc, model);\n\tif (surface && ll) {\n\t\tconst Sci::Position posLineStart = model.pdoc->LineStart(lineDoc);\n\t\tLayoutLine(model, surface, vs, ll.get(), model.wrapWidth);\n\t\tconst Range rangeSubLine = ll->SubLineRange(0, LineLayout::Scope::visibleOnly);\n\t\tconst XYPOSITION subLineStart = ll->positions[rangeSubLine.start];\n\t\tconst Sci::Position positionInLine = ll->FindPositionFromX(x + subLineStart, rangeSubLine, false);\n\t\tif (positionInLine < rangeSubLine.end) {\n\t\t\treturn SelectionPosition(model.pdoc->MovePositionOutsideChar(positionInLine + posLineStart, 1));\n\t\t}\n\t\tconst XYPOSITION spaceWidth = vs.styles[ll->EndLineStyle()].spaceWidth;\n\t\tconst int spaceOffset = static_cast<int>(\n\t\t\t(x + subLineStart - ll->positions[rangeSubLine.end] + spaceWidth / 2) / spaceWidth);\n\t\treturn SelectionPosition(rangeSubLine.end + posLineStart, spaceOffset);\n\t}\n\treturn SelectionPosition(0);\n}\n\nSci::Line EditView::DisplayFromPosition(Surface *surface, const EditModel &model, Sci::Position pos, const ViewStyle &vs) {\n\tconst Sci::Line lineDoc = model.pdoc->SciLineFromPosition(pos);\n\tSci::Line lineDisplay = model.pcs->DisplayFromDoc(lineDoc);\n\tstd::shared_ptr<LineLayout> ll = RetrieveLineLayout(lineDoc, model);\n\tif (surface && ll) {\n\t\tLayoutLine(model, surface, vs, ll.get(), model.wrapWidth);\n\t\tconst Sci::Position posLineStart = model.pdoc->LineStart(lineDoc);\n\t\tconst Sci::Position posInLine = pos - posLineStart;\n\t\tlineDisplay--; // To make up for first increment ahead.\n\t\tfor (int subLine = 0; subLine < ll->lines; subLine++) {\n\t\t\tif (posInLine >= ll->LineStart(subLine)) {\n\t\t\t\tlineDisplay++;\n\t\t\t}\n\t\t}\n\t}\n\treturn lineDisplay;\n}\n\nSci::Position EditView::StartEndDisplayLine(Surface *surface, const EditModel &model, Sci::Position pos, bool start, const ViewStyle &vs) {\n\tconst Sci::Line line = model.pdoc->SciLineFromPosition(pos);\n\tstd::shared_ptr<LineLayout> ll = RetrieveLineLayout(line, model);\n\tSci::Position posRet = Sci::invalidPosition;\n\tif (surface && ll) {\n\t\tconst Sci::Position posLineStart = model.pdoc->LineStart(line);\n\t\tLayoutLine(model, surface, vs, ll.get(), model.wrapWidth);\n\t\tconst Sci::Position posInLine = pos - posLineStart;\n\t\tif (posInLine <= ll->maxLineLength) {\n\t\t\tfor (int subLine = 0; subLine < ll->lines; subLine++) {\n\t\t\t\tif ((posInLine >= ll->LineStart(subLine)) &&\n\t\t\t\t    (posInLine <= ll->LineStart(subLine + 1)) &&\n\t\t\t\t    (posInLine <= ll->numCharsBeforeEOL)) {\n\t\t\t\t\tif (start) {\n\t\t\t\t\t\tposRet = ll->LineStart(subLine) + posLineStart;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (subLine == ll->lines - 1)\n\t\t\t\t\t\t\tposRet = ll->numCharsBeforeEOL + posLineStart;\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tposRet = model.pdoc->MovePositionOutsideChar(ll->LineStart(subLine + 1) + posLineStart - 1, -1, false);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn posRet;\n}\n\nnamespace {\n\nconstexpr ColourRGBA colourBug(0xff, 0, 0xfe, 0xf0);\n\n// Selection background colours are always defined, the value_or is to show if bug\n\nColourRGBA SelectionBackground(const EditModel &model, const ViewStyle &vsDraw, InSelection inSelection) {\n\tif (inSelection == InSelection::inNone)\n\t\treturn colourBug;\t// Not selected is a bug\n\n\tElement element = Element::SelectionBack;\n\tif (inSelection == InSelection::inAdditional)\n\t\telement = Element::SelectionAdditionalBack;\n\tif (!model.primarySelection)\n\t\telement = Element::SelectionSecondaryBack;\n\tif (!model.hasFocus) {\n\t\tif (inSelection == InSelection::inAdditional) {\n\t\t\tif (ColourOptional colour = vsDraw.ElementColour(Element::SelectionInactiveAdditionalBack)) {\n\t\t\t\treturn *colour;\n\t\t\t}\n\t\t}\n\t\tif (ColourOptional colour = vsDraw.ElementColour(Element::SelectionInactiveBack)) {\n\t\t\treturn *colour;\n\t\t}\n\t}\n\treturn vsDraw.ElementColour(element).value_or(colourBug);\n}\n\nColourOptional SelectionForeground(const EditModel &model, const ViewStyle &vsDraw, InSelection inSelection) {\n\tif (inSelection == InSelection::inNone)\n\t\treturn {};\n\tElement element = Element::SelectionText;\n\tif (inSelection == InSelection::inAdditional)\n\t\telement = Element::SelectionAdditionalText;\n\tif (!model.primarySelection)\t// Secondary selection\n\t\telement = Element::SelectionSecondaryText;\n\tif (!model.hasFocus) {\n\t\tif (inSelection == InSelection::inAdditional) {\n\t\t\tif (ColourOptional colour = vsDraw.ElementColour(Element::SelectionInactiveAdditionalText)) {\n\t\t\t\treturn colour;\n\t\t\t}\n\t\t}\n\t\telement = Element::SelectionInactiveText;\n\t}\n\treturn vsDraw.ElementColour(element);\n}\n\nColourRGBA TextBackground(const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,\n\tColourOptional background, InSelection inSelection, bool inHotspot, int styleMain, Sci::Position i) {\n\tif (inSelection && (vsDraw.selection.layer == Layer::Base)) {\n\t\treturn SelectionBackground(model, vsDraw, inSelection).Opaque();\n\t}\n\tif ((vsDraw.edgeState == EdgeVisualStyle::Background) &&\n\t\t(i >= ll->edgeColumn) &&\n\t\t(i < ll->numCharsBeforeEOL))\n\t\treturn vsDraw.theEdge.colour;\n\tif (inHotspot) {\n\t\tif (const ColourOptional colourHotSpotBack = vsDraw.ElementColour(Element::HotSpotActiveBack)) {\n\t\t\treturn colourHotSpotBack->Opaque();\n\t\t}\n\t}\n\tif (background && (styleMain != StyleBraceLight) && (styleMain != StyleBraceBad)) {\n\t\treturn *background;\n\t} else {\n\t\treturn vsDraw.styles[styleMain].back;\n\t}\n}\n\nvoid DrawTextBlob(Surface *surface, const ViewStyle &vsDraw, PRectangle rcSegment,\n\tstd::string_view text, ColourRGBA textBack, ColourRGBA textFore, bool fillBackground) {\n\tif (rcSegment.Empty())\n\t\treturn;\n\tif (fillBackground) {\n\t\tsurface->FillRectangleAligned(rcSegment, Fill(textBack));\n\t}\n\tconst Font *ctrlCharsFont = vsDraw.styles[StyleControlChar].font.get();\n\tconst int normalCharHeight = static_cast<int>(std::ceil(vsDraw.styles[StyleControlChar].capitalHeight));\n\tPRectangle rcCChar = rcSegment;\n\trcCChar.left = rcCChar.left + 1;\n\trcCChar.top = rcSegment.top + vsDraw.maxAscent - normalCharHeight;\n\trcCChar.bottom = rcSegment.top + vsDraw.maxAscent + 1;\n\tPRectangle rcCentral = rcCChar;\n\trcCentral.top++;\n\trcCentral.bottom--;\n\tsurface->FillRectangleAligned(rcCentral, Fill(textFore));\n\tPRectangle rcChar = rcCChar;\n\trcChar.left++;\n\trcChar.right--;\n\tsurface->DrawTextClippedUTF8(rcChar, ctrlCharsFont,\n\t\trcSegment.top + vsDraw.maxAscent, text,\n\t\ttextBack, textFore);\n}\n\nvoid FillLineRemainder(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,\n\tSci::Line line, PRectangle rcArea, int subLine) {\n\tInSelection eolInSelection = InSelection::inNone;\n\tif (vsDraw.selection.visible && (subLine == (ll->lines - 1))) {\n\t\teolInSelection = model.LineEndInSelection(line);\n\t}\n\n\tif (eolInSelection && vsDraw.selection.eolFilled && (line < model.pdoc->LinesTotal() - 1) && (vsDraw.selection.layer == Layer::Base)) {\n\t\tsurface->FillRectangleAligned(rcArea, Fill(SelectionBackground(model, vsDraw, eolInSelection).Opaque()));\n\t} else {\n\t\tconst ColourOptional background = vsDraw.Background(model.GetMark(line), model.caret.active, ll->containsCaret);\n\t\tif (background) {\n\t\t\tsurface->FillRectangleAligned(rcArea, Fill(*background));\n\t\t} else if (vsDraw.styles[ll->styles[ll->numCharsInLine]].eolFilled) {\n\t\t\tsurface->FillRectangleAligned(rcArea, Fill(vsDraw.styles[ll->styles[ll->numCharsInLine]].back));\n\t\t} else {\n\t\t\tsurface->FillRectangleAligned(rcArea, Fill(vsDraw.styles[StyleDefault].back));\n\t\t}\n\t\tif (eolInSelection && vsDraw.selection.eolFilled && (line < model.pdoc->LinesTotal() - 1) && (vsDraw.selection.layer != Layer::Base)) {\n\t\t\tsurface->FillRectangleAligned(rcArea, SelectionBackground(model, vsDraw, eolInSelection));\n\t\t}\n\t}\n}\n\n}\n\nvoid EditView::DrawEOL(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,\n\tSci::Line line, int xStart, PRectangle rcLine, int subLine, Sci::Position lineEnd, XYPOSITION subLineStart, ColourOptional background) {\n\n\tconst Sci::Position posLineStart = model.pdoc->LineStart(line);\n\tPRectangle rcSegment = rcLine;\n\n\tconst bool lastSubLine = subLine == (ll->lines - 1);\n\tXYPOSITION virtualSpace = 0;\n\tif (lastSubLine) {\n\t\tconst XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;\n\t\tvirtualSpace = static_cast<XYPOSITION>(model.sel.VirtualSpaceFor(model.pdoc->LineEnd(line))) * spaceWidth;\n\t}\n\tconst XYPOSITION xEol = ll->positions[lineEnd] - subLineStart;\n\n\t// Fill the virtual space and show selections within it\n\tif (virtualSpace > 0.0f) {\n\t\trcSegment.left = xEol + xStart;\n\t\trcSegment.right = xEol + xStart + virtualSpace;\n\t\tconst ColourRGBA backgroundFill = background.value_or(vsDraw.styles[ll->styles[ll->numCharsInLine]].back);\n\t\tsurface->FillRectangleAligned(rcSegment, backgroundFill);\n\t\tif (vsDraw.selection.visible && (vsDraw.selection.layer == Layer::Base)) {\n\t\t\tconst SelectionSegment virtualSpaceRange(SelectionPosition(model.pdoc->LineEnd(line)),\n\t\t\t\tSelectionPosition(model.pdoc->LineEnd(line),\n\t\t\t\t\tmodel.sel.VirtualSpaceFor(model.pdoc->LineEnd(line))));\n\t\t\tfor (size_t r = 0; r<model.sel.Count(); r++) {\n\t\t\t\tconst SelectionSegment portion = model.sel.Range(r).Intersect(virtualSpaceRange);\n\t\t\t\tif (!portion.Empty()) {\n\t\t\t\t\tconst XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;\n\t\t\t\t\trcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] -\n\t\t\t\t\t\tsubLineStart + portion.start.VirtualSpaceWidth(spaceWidth);\n\t\t\t\t\trcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] -\n\t\t\t\t\t\tsubLineStart + portion.end.VirtualSpaceWidth(spaceWidth);\n\t\t\t\t\trcSegment.left = (rcSegment.left > rcLine.left) ? rcSegment.left : rcLine.left;\n\t\t\t\t\trcSegment.right = (rcSegment.right < rcLine.right) ? rcSegment.right : rcLine.right;\n\t\t\t\t\tsurface->FillRectangleAligned(rcSegment, Fill(\n\t\t\t\t\t\tSelectionBackground(model, vsDraw, model.sel.RangeType(r)).Opaque()));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tInSelection eolInSelection = InSelection::inNone;\n\tif (vsDraw.selection.visible && lastSubLine) {\n\t\teolInSelection = model.LineEndInSelection(line);\n\t}\n\n\tconst ColourRGBA selectionBack = SelectionBackground(model, vsDraw, eolInSelection);\n\n\t// Draw the [CR], [LF], or [CR][LF] blobs if visible line ends are on\n\tXYPOSITION blobsWidth = 0;\n\tif (lastSubLine) {\n\t\tfor (Sci::Position eolPos = ll->numCharsBeforeEOL; eolPos<ll->numCharsInLine;) {\n\t\t\tconst int styleMain = ll->styles[eolPos];\n\t\t\tconst ColourOptional selectionFore = SelectionForeground(model, vsDraw, eolInSelection);\n\t\t\tColourRGBA textFore = selectionFore.value_or(vsDraw.styles[styleMain].fore);\n\t\t\tchar hexits[4] = \"\";\n\t\t\tstd::string_view ctrlChar;\n\t\t\tSci::Position widthBytes = 1;\n\t\t\tRepresentationAppearance appearance = RepresentationAppearance::Blob;\n\t\t\tconst Representation *repr = model.reprs->RepresentationFromCharacter(std::string_view(&ll->chars[eolPos], ll->numCharsInLine - eolPos));\n\t\t\tif (repr) {\n\t\t\t\t// Representation of whole text\n\t\t\t\twidthBytes = ll->numCharsInLine - eolPos;\n\t\t\t} else {\n\t\t\t\trepr = model.reprs->RepresentationFromCharacter(std::string_view(&ll->chars[eolPos], 1));\n\t\t\t}\n\t\t\tif (repr) {\n\t\t\t\tctrlChar = repr->stringRep;\n\t\t\t\tappearance = repr->appearance;\n\t\t\t\tif (FlagSet(appearance, RepresentationAppearance::Colour)) {\n\t\t\t\t\ttextFore = repr->colour;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tconst unsigned char chEOL = ll->chars[eolPos];\n\t\t\t\tif (UTF8IsAscii(chEOL)) {\n\t\t\t\t\tctrlChar = ControlCharacterString(chEOL);\n\t\t\t\t} else {\n\t\t\t\t\tHexits(hexits, chEOL);\n\t\t\t\t\tctrlChar = hexits;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\trcSegment.left = xStart + ll->positions[eolPos] - subLineStart + virtualSpace;\n\t\t\trcSegment.right = xStart + ll->positions[eolPos + widthBytes] - subLineStart + virtualSpace;\n\t\t\tblobsWidth += rcSegment.Width();\n\t\t\tconst ColourRGBA textBack = TextBackground(model, vsDraw, ll, background, eolInSelection, false, styleMain, eolPos);\n\t\t\tif (eolInSelection && (line < model.pdoc->LinesTotal() - 1)) {\n\t\t\t\tif (vsDraw.selection.layer == Layer::Base) {\n\t\t\t\t\tsurface->FillRectangleAligned(rcSegment, Fill(selectionBack.Opaque()));\n\t\t\t\t} else {\n\t\t\t\t\tsurface->FillRectangleAligned(rcSegment, Fill(textBack));\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tsurface->FillRectangleAligned(rcSegment, Fill(textBack));\n\t\t\t}\n\t\t\tconst bool drawEOLSelection = eolInSelection && (line < model.pdoc->LinesTotal() - 1);\n\t\t\tColourRGBA blobText = textBack;\n\t\t\tif (drawEOLSelection && (vsDraw.selection.layer == Layer::UnderText)) {\n\t\t\t\tsurface->FillRectangleAligned(rcSegment, selectionBack);\n\t\t\t\tblobText = textBack.MixedWith(selectionBack, selectionBack.GetAlphaComponent());\n\t\t\t}\n\t\t\tif (FlagSet(appearance, RepresentationAppearance::Blob)) {\n\t\t\t\tDrawTextBlob(surface, vsDraw, rcSegment, ctrlChar, blobText, textFore, phasesDraw == PhasesDraw::One);\n\t\t\t} else {\n\t\t\t\tsurface->DrawTextTransparentUTF8(rcSegment, vsDraw.styles[StyleControlChar].font.get(),\n\t\t\t\t\trcSegment.top + vsDraw.maxAscent, ctrlChar, textFore);\n\t\t\t}\n\t\t\tif (drawEOLSelection && (vsDraw.selection.layer == Layer::OverText)) {\n\t\t\t\tsurface->FillRectangleAligned(rcSegment, selectionBack);\n\t\t\t}\n\t\t\teolPos += widthBytes;\n\t\t}\n\t}\n\n\t// Draw the eol-is-selected rectangle\n\trcSegment.left = xEol + xStart + virtualSpace + blobsWidth;\n\trcSegment.right = rcSegment.left + vsDraw.aveCharWidth;\n\n\tif (eolInSelection && (line < model.pdoc->LinesTotal() - 1) && (vsDraw.selection.layer == Layer::Base)) {\n\t\tsurface->FillRectangleAligned(rcSegment, Fill(selectionBack.Opaque()));\n\t} else {\n\t\tif (background) {\n\t\t\tsurface->FillRectangleAligned(rcSegment, Fill(*background));\n\t\t} else if (line < model.pdoc->LinesTotal() - 1) {\n\t\t\tsurface->FillRectangleAligned(rcSegment, Fill(vsDraw.styles[ll->styles[ll->numCharsInLine]].back));\n\t\t} else if (vsDraw.styles[ll->styles[ll->numCharsInLine]].eolFilled) {\n\t\t\tsurface->FillRectangleAligned(rcSegment, Fill(vsDraw.styles[ll->styles[ll->numCharsInLine]].back));\n\t\t} else {\n\t\t\tsurface->FillRectangleAligned(rcSegment, Fill(vsDraw.styles[StyleDefault].back));\n\t\t}\n\t\tif (eolInSelection && (line < model.pdoc->LinesTotal() - 1) && (vsDraw.selection.layer != Layer::Base)) {\n\t\t\tsurface->FillRectangleAligned(rcSegment, selectionBack);\n\t\t}\n\t}\n\n\trcSegment.left = rcSegment.right;\n\tif (rcSegment.left < rcLine.left)\n\t\trcSegment.left = rcLine.left;\n\trcSegment.right = rcLine.right;\n\n\tconst bool drawEOLAnnotationStyledText = (vsDraw.eolAnnotationVisible != EOLAnnotationVisible::Hidden) && model.pdoc->EOLAnnotationStyledText(line).text;\n\tconst bool fillRemainder = (!lastSubLine || (!model.GetFoldDisplayText(line) && !drawEOLAnnotationStyledText));\n\tif (fillRemainder) {\n\t\t// Fill the remainder of the line\n\t\tFillLineRemainder(surface, model, vsDraw, ll, line, rcSegment, subLine);\n\t}\n\n\tbool drawWrapMarkEnd = false;\n\n\tif (subLine + 1 < ll->lines) {\n\t\tif (FlagSet(vsDraw.wrap.visualFlags, WrapVisualFlag::End)) {\n\t\t\tdrawWrapMarkEnd = ll->LineStart(subLine + 1) != 0;\n\t\t}\n\t\tif (vsDraw.IsLineFrameOpaque(model.caret.active, ll->containsCaret)) {\n\t\t\t// Draw right of frame under marker\n\t\t\tsurface->FillRectangleAligned(Side(rcLine, Edge::right, vsDraw.GetFrameWidth()),\n\t\t\t\tvsDraw.ElementColourForced(Element::CaretLineBack).Opaque());\n\t\t}\n\t}\n\n\tif (drawWrapMarkEnd) {\n\t\tPRectangle rcPlace = rcSegment;\n\t\tconst XYPOSITION maxLeft = rcPlace.right - vsDraw.aveCharWidth;\n\n\t\tif (FlagSet(vsDraw.wrap.visualFlagsLocation, WrapVisualLocation::EndByText)) {\n\t\t\trcPlace.left = std::min(xEol + xStart + virtualSpace, maxLeft);\n\t\t\trcPlace.right = rcPlace.left + vsDraw.aveCharWidth;\n\t\t} else {\n\t\t\t// rcLine is clipped to text area\n\t\t\trcPlace.right = rcLine.right;\n\t\t\trcPlace.left = maxLeft;\n\t\t}\n\t\tif (!customDrawWrapMarker) {\n\t\t\tDrawWrapMarker(surface, rcPlace, true, vsDraw.WrapColour());\n\t\t} else {\n\t\t\tcustomDrawWrapMarker(surface, rcPlace, true, vsDraw.WrapColour());\n\t\t}\n\t}\n}\n\nvoid EditView::DrawFoldDisplayText(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,\n\t\t\t\t\t\t\t  Sci::Line line, int xStart, PRectangle rcLine, int subLine, XYPOSITION subLineStart, DrawPhase phase) {\n\tconst bool lastSubLine = subLine == (ll->lines - 1);\n\tif (!lastSubLine)\n\t\treturn;\n\n\tconst char *text = model.GetFoldDisplayText(line);\n\tif (!text)\n\t\treturn;\n\n\tPRectangle rcSegment = rcLine;\n\tconst std::string_view foldDisplayText(text);\n\tconst Font *fontText = vsDraw.styles[StyleFoldDisplayText].font.get();\n\tconst int widthFoldDisplayText = static_cast<int>(surface->WidthText(fontText, foldDisplayText));\n\n\tInSelection eolInSelection = InSelection::inNone;\n\tif (vsDraw.selection.visible) {\n\t\teolInSelection = model.LineEndInSelection(line);\n\t}\n\n\tconst XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;\n\tconst XYPOSITION virtualSpace = static_cast<XYPOSITION>(model.sel.VirtualSpaceFor(\n\t\tmodel.pdoc->LineEnd(line))) * spaceWidth;\n\trcSegment.left = xStart + ll->positions[ll->numCharsInLine] - subLineStart + virtualSpace + vsDraw.aveCharWidth;\n\trcSegment.right = rcSegment.left + static_cast<XYPOSITION>(widthFoldDisplayText);\n\n\tconst ColourOptional background = vsDraw.Background(model.GetMark(line), model.caret.active, ll->containsCaret);\n\tconst ColourOptional selectionFore = SelectionForeground(model, vsDraw, eolInSelection);\n\tconst ColourRGBA textFore = selectionFore.value_or(vsDraw.styles[StyleFoldDisplayText].fore);\n\tconst ColourRGBA textBack = TextBackground(model, vsDraw, ll, background, eolInSelection,\n\t\t\t\t\t\t\t\t\t\t\tfalse, StyleFoldDisplayText, -1);\n\n\tif (model.trackLineWidth) {\n\t\tif (rcSegment.right + 1> lineWidthMaxSeen) {\n\t\t\t// Fold display text border drawn on rcSegment.right with width 1 is the last visible object of the line\n\t\t\tlineWidthMaxSeen = static_cast<int>(rcSegment.right + 1);\n\t\t}\n\t}\n\n\tif (FlagSet(phase, DrawPhase::back)) {\n\t\tsurface->FillRectangleAligned(rcSegment, Fill(textBack));\n\n\t\t// Fill Remainder of the line\n\t\tPRectangle rcRemainder = rcSegment;\n\t\trcRemainder.left = rcRemainder.right;\n\t\tif (rcRemainder.left < rcLine.left)\n\t\t\trcRemainder.left = rcLine.left;\n\t\trcRemainder.right = rcLine.right;\n\t\tFillLineRemainder(surface, model, vsDraw, ll, line, rcRemainder, subLine);\n\t}\n\n\tif (FlagSet(phase, DrawPhase::text)) {\n\t\tif (phasesDraw != PhasesDraw::One) {\n\t\t\tsurface->DrawTextTransparent(rcSegment, fontText,\n\t\t\t\trcSegment.top + vsDraw.maxAscent, foldDisplayText,\n\t\t\t\ttextFore);\n\t\t} else {\n\t\t\tsurface->DrawTextNoClip(rcSegment, fontText,\n\t\t\t\trcSegment.top + vsDraw.maxAscent, foldDisplayText,\n\t\t\t\ttextFore, textBack);\n\t\t}\n\t}\n\n\tif (FlagSet(phase, DrawPhase::indicatorsFore)) {\n\t\tif (model.foldDisplayTextStyle == FoldDisplayTextStyle::Boxed) {\n\t\t\tPRectangle rcBox = rcSegment;\n\t\t\trcBox.left = std::round(rcSegment.left);\n\t\t\trcBox.right = std::round(rcSegment.right);\n\t\t\tsurface->RectangleFrame(rcBox, Stroke(textFore));\n\t\t}\n\t}\n\n\tif (FlagSet(phase, DrawPhase::selectionTranslucent)) {\n\t\tif (eolInSelection && (line < model.pdoc->LinesTotal() - 1) && (vsDraw.selection.layer != Layer::Base)) {\n\t\t\tsurface->FillRectangleAligned(rcSegment, SelectionBackground(model, vsDraw, eolInSelection));\n\t\t}\n\t}\n}\n\nvoid EditView::DrawEOLAnnotationText(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,\n\tSci::Line line, int xStart, PRectangle rcLine, int subLine, XYPOSITION subLineStart, DrawPhase phase) {\n\n\tconst bool lastSubLine = subLine == (ll->lines - 1);\n\tif (!lastSubLine)\n\t\treturn;\n\n\tif (vsDraw.eolAnnotationVisible == EOLAnnotationVisible::Hidden) {\n\t\treturn;\n\t}\n\tconst StyledText stEOLAnnotation = model.pdoc->EOLAnnotationStyledText(line);\n\tif (!stEOLAnnotation.text || !ValidStyledText(vsDraw, vsDraw.eolAnnotationStyleOffset, stEOLAnnotation)) {\n\t\treturn;\n\t}\n\tconst std::string_view eolAnnotationText(stEOLAnnotation.text, stEOLAnnotation.length);\n\tconst size_t style = stEOLAnnotation.style + vsDraw.eolAnnotationStyleOffset;\n\n\tPRectangle rcSegment = rcLine;\n\tconst Font *fontText = vsDraw.styles[style].font.get();\n\n\tconst Surface::Ends ends = static_cast<Surface::Ends>(static_cast<int>(vsDraw.eolAnnotationVisible) & 0xff);\n\tconst Surface::Ends leftSide = static_cast<Surface::Ends>(static_cast<int>(ends) & 0xf);\n\tconst Surface::Ends rightSide = static_cast<Surface::Ends>(static_cast<int>(ends) & 0xf0);\n\n\tXYPOSITION leftBoxSpace = 0;\n\tXYPOSITION rightBoxSpace = 0;\n\tif (vsDraw.eolAnnotationVisible >= EOLAnnotationVisible::Boxed) {\n\t\tleftBoxSpace = 1;\n\t\trightBoxSpace = 1;\n\t\tif (vsDraw.eolAnnotationVisible != EOLAnnotationVisible::Boxed) {\n\t\t\tswitch (leftSide) {\n\t\t\tcase Surface::Ends::leftFlat:\n\t\t\t\tleftBoxSpace = 1;\n\t\t\t\tbreak;\n\t\t\tcase Surface::Ends::leftAngle:\n\t\t\t\tleftBoxSpace = rcLine.Height() / 2.0;\n\t\t\t\tbreak;\n\t\t\tcase Surface::Ends::semiCircles:\n\t\t\tdefault:\n\t\t\t\tleftBoxSpace = rcLine.Height() / 3.0;\n\t\t\t   break;\n\t\t\t}\n\t\t\tswitch (rightSide) {\n\t\t\tcase Surface::Ends::rightFlat:\n\t\t\t\trightBoxSpace = 1;\n\t\t\t\tbreak;\n\t\t\tcase Surface::Ends::rightAngle:\n\t\t\t\trightBoxSpace = rcLine.Height() / 2.0;\n\t\t\t\tbreak;\n\t\t\tcase Surface::Ends::semiCircles:\n\t\t\tdefault:\n\t\t\t\trightBoxSpace = rcLine.Height() / 3.0;\n\t\t\t   break;\n\t\t\t}\n\t\t}\n\t}\n\tconst int widthEOLAnnotationText = static_cast<int>(surface->WidthTextUTF8(fontText, eolAnnotationText) +\n\t\tleftBoxSpace + rightBoxSpace);\n\n\tconst XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;\n\tconst XYPOSITION virtualSpace = static_cast<XYPOSITION>(model.sel.VirtualSpaceFor(\n\t\tmodel.pdoc->LineEnd(line))) * spaceWidth;\n\trcSegment.left = xStart +\n\t\tll->positions[ll->numCharsInLine] - subLineStart\n\t\t+ virtualSpace + vsDraw.aveCharWidth;\n\n\tconst char *textFoldDisplay = model.GetFoldDisplayText(line);\n\tif (textFoldDisplay) {\n\t\tconst std::string_view foldDisplayText(textFoldDisplay);\n\t\trcSegment.left += static_cast<int>(\n\t\t\tsurface->WidthText(vsDraw.styles[StyleFoldDisplayText].font.get(), foldDisplayText)) +\n\t\t\tvsDraw.aveCharWidth;\n\t}\n\trcSegment.right = rcSegment.left + static_cast<XYPOSITION>(widthEOLAnnotationText);\n\n\tconst ColourOptional background = vsDraw.Background(model.GetMark(line), model.caret.active, ll->containsCaret);\n\tconst ColourRGBA textFore = vsDraw.styles[style].fore;\n\tconst ColourRGBA textBack = TextBackground(model, vsDraw, ll, background, InSelection::inNone,\n\t\t\t\t\t\t\t\t\t\t\tfalse, static_cast<int>(style), -1);\n\n\tif (model.trackLineWidth) {\n\t\tif (rcSegment.right + 1> lineWidthMaxSeen) {\n\t\t\t// EOL Annotation text border drawn on rcSegment.right with width 1 is the last visible object of the line\n\t\t\tlineWidthMaxSeen = static_cast<int>(rcSegment.right + 1);\n\t\t}\n\t}\n\n\tif (FlagSet(phase, DrawPhase::back)) {\n\t\t// This fills in the whole remainder of the line even though\n\t\t// it may be double drawing. This is to allow stadiums with\n\t\t// curved or angled ends to have the area outside in the correct\n\t\t// background colour.\n\t\tPRectangle rcRemainder = rcSegment;\n\t\trcRemainder.right = rcLine.right;\n\t\tFillLineRemainder(surface, model, vsDraw, ll, line, rcRemainder, subLine);\n\t}\n\n\tPRectangle rcText = rcSegment;\n\trcText.left += leftBoxSpace;\n\trcText.right -= rightBoxSpace;\n\n\t// For single phase drawing, draw the text then any box over it\n\tif (FlagSet(phase, DrawPhase::text)) {\n\t\tif (phasesDraw == PhasesDraw::One) {\n\t\t\tsurface->DrawTextNoClipUTF8(rcText, fontText,\n\t\t\trcText.top + vsDraw.maxAscent, eolAnnotationText,\n\t\t\ttextFore, textBack);\n\t\t}\n\t}\n\n\t// Draw any box or stadium shape\n\tif (FlagSet(phase, DrawPhase::indicatorsBack)) {\n\t\tconst PRectangle rcBox = PixelAlign(rcSegment, 1);\n\n\t\tswitch (vsDraw.eolAnnotationVisible) {\n\t\tcase EOLAnnotationVisible::Standard:\n\t\t\tif (phasesDraw != PhasesDraw::One) {\n\t\t\t\tsurface->FillRectangle(rcBox, textBack);\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase EOLAnnotationVisible::Boxed:\n\t\t\tif (phasesDraw == PhasesDraw::One) {\n\t\t\t\t// Draw a rectangular outline around the text\n\t\t\t\tsurface->RectangleFrame(rcBox, textFore);\n\t\t\t} else {\n\t\t\t\t// Draw with a fill to fill the edges of the rectangle.\n\t\t\t\tsurface->RectangleDraw(rcBox, FillStroke(textBack, textFore));\n\t\t\t}\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tif (phasesDraw == PhasesDraw::One) {\n\t\t\t\t// Draw an outline around the text\n\t\t\t\tsurface->Stadium(rcBox, FillStroke(ColourRGBA(textBack, 0), textFore), ends);\n\t\t\t} else {\n\t\t\t\t// Draw with a fill to fill the edges of the shape.\n\t\t\t\tsurface->Stadium(rcBox, FillStroke(textBack, textFore), ends);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t// For multi-phase drawing draw the text last as transparent over any box\n\tif (FlagSet(phase, DrawPhase::text)) {\n\t\tif (phasesDraw != PhasesDraw::One) {\n\t\t\tsurface->DrawTextTransparentUTF8(rcText, fontText,\n\t\t\t\trcText.top + vsDraw.maxAscent, eolAnnotationText,\n\t\t\t\ttextFore);\n\t\t}\n\t}\n}\n\nnamespace {\n\nconstexpr bool AnnotationBoxedOrIndented(AnnotationVisible annotationVisible) noexcept {\n\treturn annotationVisible == AnnotationVisible::Boxed || annotationVisible == AnnotationVisible::Indented;\n}\n\n}\n\nvoid EditView::DrawAnnotation(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,\n\tSci::Line line, int xStart, PRectangle rcLine, int subLine, DrawPhase phase) {\n\tconst int indent = static_cast<int>(model.pdoc->GetLineIndentation(line) * vsDraw.spaceWidth);\n\tPRectangle rcSegment = rcLine;\n\tconst int annotationLine = subLine - ll->lines;\n\tconst StyledText stAnnotation = model.pdoc->AnnotationStyledText(line);\n\tif (stAnnotation.text && ValidStyledText(vsDraw, vsDraw.annotationStyleOffset, stAnnotation)) {\n\t\tif (FlagSet(phase, DrawPhase::back)) {\n\t\t\tsurface->FillRectangleAligned(rcSegment, Fill(vsDraw.styles[0].back));\n\t\t}\n\t\trcSegment.left = static_cast<XYPOSITION>(xStart);\n\t\tif (model.trackLineWidth || AnnotationBoxedOrIndented(vsDraw.annotationVisible)) {\n\t\t\t// Only care about calculating width if tracking or need to draw indented box\n\t\t\tint widthAnnotation = WidestLineWidth(surface, vsDraw, vsDraw.annotationStyleOffset, stAnnotation);\n\t\t\tif (AnnotationBoxedOrIndented(vsDraw.annotationVisible)) {\n\t\t\t\twidthAnnotation += static_cast<int>(vsDraw.spaceWidth * 2); // Margins\n\t\t\t\trcSegment.left = static_cast<XYPOSITION>(xStart + indent);\n\t\t\t\trcSegment.right = rcSegment.left + widthAnnotation;\n\t\t\t}\n\t\t\tif (widthAnnotation > lineWidthMaxSeen)\n\t\t\t\tlineWidthMaxSeen = widthAnnotation;\n\t\t}\n\t\tconst int annotationLines = model.pdoc->AnnotationLines(line);\n\t\tsize_t start = 0;\n\t\tsize_t lengthAnnotation = stAnnotation.LineLength(start);\n\t\tint lineInAnnotation = 0;\n\t\twhile ((lineInAnnotation < annotationLine) && (start < stAnnotation.length)) {\n\t\t\tstart += lengthAnnotation + 1;\n\t\t\tlengthAnnotation = stAnnotation.LineLength(start);\n\t\t\tlineInAnnotation++;\n\t\t}\n\n\t\t//Au: call callback function, if it was set with SCI_SETANNOTATIONDRAWCALLBACK. Step 0 - get image width.\n\t\tSci_AnnotationDrawCallbackData c = {};\n\t\tint imageWidth = 0;\n\t\tif(model.cbAnnotationDraw) {\n\t\t\tc.text = stAnnotation.text; c.textLen = (int)stAnnotation.length;\n\t\t\tc.line = (int)line; c.annotLine = subLine - ll->lines;\n\t\t\tif(AnnotationBoxedOrIndented(vsDraw.annotationVisible)) {\n\t\t\t\timageWidth = model.cbAnnotationDraw(model.cbAnnotationDrawParam, c);\n\t\t\t\tif(imageWidth) {\n\t\t\t\t\timageWidth += (int)(rcSegment.left + vsDraw.spaceWidth * 2);\n\t\t\t\t\tif(imageWidth > (int)rcSegment.right) rcSegment.right = (XYPOSITION)imageWidth;\n\t\t\t\t}\n\t\t\t} else imageWidth = 1;\n\t\t}\n\n\t\tPRectangle rcText = rcSegment;\n\t\tif ((FlagSet(phase, DrawPhase::back)) && AnnotationBoxedOrIndented(vsDraw.annotationVisible)) {\n\t\t\tsurface->FillRectangleAligned(rcText,\n\t\t\t\tFill(vsDraw.styles[stAnnotation.StyleAt(start) + vsDraw.annotationStyleOffset].back));\n\t\t\trcText.left += vsDraw.spaceWidth;\n\t\t}\n\n\t\t//Au: call callback function, if it was set with SCI_SETANNOTATIONDRAWCALLBACK. Step 1 - draw image in this line.\n\t\tif(imageWidth) {\n\t\t\tif(!AnnotationBoxedOrIndented(vsDraw.annotationVisible)) rcText.left = (XYPOSITION)(xStart + indent);\n\t\t\t//Platform::DebugPrintf(\"%i %i %i\", subLine, ll->lines, annotationLines);\n\t\t\tc.step = 1;\n\t\t\tc.rect.left = (int)rcText.left;\n\t\t\tc.rect.top = (int)rcText.top;\n\t\t\tc.rect.right = (int)rcText.right;\n\t\t\tc.rect.bottom = (int)rcText.bottom;\n\t\t\tc.hdc = surface->get_hdc();\n\t\t\tint iw = model.cbAnnotationDraw(model.cbAnnotationDrawParam, c);\n\t\t\tif(!iw) imageWidth = 0; else if(iw > imageWidth) imageWidth = iw;\n\t\t\tif(imageWidth > lineWidthMaxSeen) lineWidthMaxSeen = imageWidth; //horz scroll bar\n\t\t}\n\t\tif(!imageWidth)\n\n\t\tDrawStyledText(surface, vsDraw, vsDraw.annotationStyleOffset, rcText,\n\t\t\tstAnnotation, start, lengthAnnotation, phase);\n\t\tif ((FlagSet(phase, DrawPhase::back)) && (vsDraw.annotationVisible == AnnotationVisible::Boxed)) {\n\t\t\tconst ColourRGBA colourBorder = vsDraw.styles[vsDraw.annotationStyleOffset].fore;\n\t\t\tconst PRectangle rcBorder = PixelAlignOutside(rcSegment, surface->PixelDivisions());\n\t\t\tsurface->FillRectangle(Side(rcBorder, Edge::left, 1), colourBorder);\n\t\t\tsurface->FillRectangle(Side(rcBorder, Edge::right, 1), colourBorder);\n\t\t\tif (subLine == ll->lines) {\n\t\t\t\tsurface->FillRectangle(Side(rcBorder, Edge::top, 1), colourBorder);\n\t\t\t}\n\t\t\tif (subLine == ll->lines + annotationLines - 1) {\n\t\t\t\tsurface->FillRectangle(Side(rcBorder, Edge::bottom, 1), colourBorder);\n\t\t\t}\n\t\t}\n\t} else {\n\t\t// No annotation to draw so show bug with colourBug\n\t\tif (FlagSet(phase, DrawPhase::back)) {\n\t\t\tsurface->FillRectangle(rcSegment, colourBug.Opaque());\n\t\t}\n\t}\n}\n\nnamespace {\n\nvoid DrawBlockCaret(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,\n\tint subLine, int xStart, Sci::Position offset, Sci::Position posCaret, PRectangle rcCaret, ColourRGBA caretColour) {\n\n\tconst Sci::Position lineStart = ll->LineStart(subLine);\n\tSci::Position posBefore = posCaret;\n\tSci::Position posAfter = model.pdoc->MovePositionOutsideChar(posCaret + 1, 1);\n\tSci::Position numCharsToDraw = posAfter - posCaret;\n\n\t// Work out where the starting and ending offsets are. We need to\n\t// see if the previous character shares horizontal space, such as a\n\t// glyph / combining character. If so we'll need to draw that too.\n\tSci::Position offsetFirstChar = offset;\n\tSci::Position offsetLastChar = offset + (posAfter - posCaret);\n\twhile ((posBefore > 0) && ((offsetLastChar - numCharsToDraw) >= lineStart)) {\n\t\tif ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - numCharsToDraw]) > 0) {\n\t\t\t// The char does not share horizontal space\n\t\t\tbreak;\n\t\t}\n\t\t// Char shares horizontal space, update the numChars to draw\n\t\t// Update posBefore to point to the prev char\n\t\tposBefore = model.pdoc->MovePositionOutsideChar(posBefore - 1, -1);\n\t\tnumCharsToDraw = posAfter - posBefore;\n\t\toffsetFirstChar = offset - (posCaret - posBefore);\n\t}\n\n\t// See if the next character shares horizontal space, if so we'll\n\t// need to draw that too.\n\tif (offsetFirstChar < 0)\n\t\toffsetFirstChar = 0;\n\tnumCharsToDraw = offsetLastChar - offsetFirstChar;\n\twhile ((offsetLastChar < ll->LineStart(subLine + 1)) && (offsetLastChar <= ll->numCharsInLine)) {\n\t\t// Update posAfter to point to the 2nd next char, this is where\n\t\t// the next character ends, and 2nd next begins. We'll need\n\t\t// to compare these two\n\t\tposBefore = posAfter;\n\t\tposAfter = model.pdoc->MovePositionOutsideChar(posAfter + 1, 1);\n\t\toffsetLastChar = offset + (posAfter - posCaret);\n\t\tif ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - (posAfter - posBefore)]) > 0) {\n\t\t\t// The char does not share horizontal space\n\t\t\tbreak;\n\t\t}\n\t\t// Char shares horizontal space, update the numChars to draw\n\t\tnumCharsToDraw = offsetLastChar - offsetFirstChar;\n\t}\n\n\t// We now know what to draw, update the caret drawing rectangle\n\trcCaret.left = ll->positions[offsetFirstChar] - ll->positions[lineStart] + xStart;\n\trcCaret.right = ll->positions[offsetFirstChar + numCharsToDraw] - ll->positions[lineStart] + xStart;\n\n\t// Adjust caret position to take into account any word wrapping symbols.\n\tif ((ll->wrapIndent != 0) && (lineStart != 0)) {\n\t\tconst XYPOSITION wordWrapCharWidth = ll->wrapIndent;\n\t\trcCaret.left += wordWrapCharWidth;\n\t\trcCaret.right += wordWrapCharWidth;\n\t}\n\n\t// This character is where the caret block is, we override the colours\n\t// (inversed) for drawing the caret here.\n\tconst int styleMain = ll->styles[offsetFirstChar];\n\tconst Font *fontText = vsDraw.styles[styleMain].font.get();\n\tconst std::string_view text(&ll->chars[offsetFirstChar], numCharsToDraw);\n\tsurface->DrawTextClipped(rcCaret, fontText,\n\t\trcCaret.top + vsDraw.maxAscent, text, vsDraw.styles[styleMain].back,\n\t\tcaretColour);\n}\n\n}\n\nvoid EditView::DrawCarets(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,\n\tSci::Line lineDoc, int xStart, PRectangle rcLine, int subLine) const {\n\t// When drag is active it is the only caret drawn\n\tconst bool drawDrag = model.posDrag.IsValid();\n\tif (!vsDraw.selection.visible && !drawDrag)\n\t\treturn;\n\tconst Sci::Position posLineStart = model.pdoc->LineStart(lineDoc);\n\t// For each selection draw\n\tfor (size_t r = 0; (r<model.sel.Count()) || drawDrag; r++) {\n\t\tconst bool mainCaret = r == model.sel.Main();\n\t\tSelectionPosition posCaret = (drawDrag ? model.posDrag : model.sel.Range(r).caret);\n\t\tif ((vsDraw.DrawCaretInsideSelection(model.inOverstrike, imeCaretBlockOverride)) &&\n\t\t\t!drawDrag &&\n\t\t\tposCaret > model.sel.Range(r).anchor) {\n\t\t\tif (posCaret.VirtualSpace() > 0)\n\t\t\t\tposCaret.SetVirtualSpace(posCaret.VirtualSpace() - 1);\n\t\t\telse\n\t\t\t\tposCaret.SetPosition(model.pdoc->MovePositionOutsideChar(posCaret.Position()-1, -1));\n\t\t}\n\t\tconst int offset = static_cast<int>(posCaret.Position() - posLineStart);\n\t\tconst XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;\n\t\tconst XYPOSITION virtualOffset = posCaret.VirtualSpaceWidth(spaceWidth);\n\t\tif (ll->InLine(offset, subLine) && offset <= ll->numCharsBeforeEOL) {\n\t\t\tXYPOSITION xposCaret = ll->positions[offset] + virtualOffset - ll->positions[ll->LineStart(subLine)];\n\t\t\tif (model.BidirectionalEnabled() && (posCaret.VirtualSpace() == 0)) {\n\t\t\t\t// Get caret point\n\t\t\t\tconst ScreenLine screenLine(ll, subLine, vsDraw, rcLine.right, tabWidthMinimumPixels);\n\n\t\t\t\tconst int caretPosition = offset - ll->LineStart(subLine);\n\n\t\t\t\tstd::unique_ptr<IScreenLineLayout> slLayout = surface->Layout(&screenLine);\n\t\t\t\tconst XYPOSITION caretLeft = slLayout->XFromPosition(caretPosition);\n\n\t\t\t\t// In case of start of line, the cursor should be at the right\n\t\t\t\txposCaret = caretLeft + virtualOffset;\n\t\t\t}\n\t\t\tif (ll->wrapIndent != 0) {\n\t\t\t\tconst Sci::Position lineStart = ll->LineStart(subLine);\n\t\t\t\tif (lineStart != 0)\t// Wrapped\n\t\t\t\t\txposCaret += ll->wrapIndent;\n\t\t\t}\n\t\t\tconst bool caretBlinkState = (model.caret.active && model.caret.on) || (!additionalCaretsBlink && !mainCaret);\n\t\t\tconst bool caretVisibleState = additionalCaretsVisible || mainCaret;\n\t\t\tif ((xposCaret >= 0) && vsDraw.IsCaretVisible(mainCaret) &&\n\t\t\t\t(drawDrag || (caretBlinkState && caretVisibleState))) {\n\t\t\t\tbool canDrawBlockCaret = true;\n\t\t\t\tbool drawBlockCaret = false;\n\t\t\t\tXYPOSITION widthOverstrikeCaret;\n\t\t\t\tXYPOSITION caretWidthOffset = 0;\n\t\t\t\tPRectangle rcCaret = rcLine;\n\n\t\t\t\tif (posCaret.Position() == model.pdoc->Length()) {   // At end of document\n\t\t\t\t\tcanDrawBlockCaret = false;\n\t\t\t\t\twidthOverstrikeCaret = vsDraw.aveCharWidth;\n\t\t\t\t} else if ((posCaret.Position() - posLineStart) >= ll->numCharsInLine) {\t// At end of line\n\t\t\t\t\tcanDrawBlockCaret = false;\n\t\t\t\t\twidthOverstrikeCaret = vsDraw.aveCharWidth;\n\t\t\t\t} else {\n\t\t\t\t\tconst int widthChar = model.pdoc->LenChar(posCaret.Position());\n\t\t\t\t\twidthOverstrikeCaret = ll->positions[offset + widthChar] - ll->positions[offset];\n\t\t\t\t}\n\t\t\t\tif (widthOverstrikeCaret < 3)\t// Make sure its visible\n\t\t\t\t\twidthOverstrikeCaret = 3;\n\n\t\t\t\tif (xposCaret > 0)\n\t\t\t\t\tcaretWidthOffset = 0.51f;\t// Move back so overlaps both character cells.\n\t\t\t\txposCaret += xStart;\n\t\t\t\tconst ViewStyle::CaretShape caretShape = drawDrag ? ViewStyle::CaretShape::line :\n\t\t\t\t\tvsDraw.CaretShapeForMode(model.inOverstrike, mainCaret);\n\t\t\t\tif (drawDrag) {\n\t\t\t\t\t/* Dragging text, use a line caret */\n\t\t\t\t\trcCaret.left = std::round(xposCaret - caretWidthOffset);\n\t\t\t\t\trcCaret.right = rcCaret.left + vsDraw.caret.width;\n\t\t\t\t} else if ((caretShape == ViewStyle::CaretShape::bar) && drawOverstrikeCaret) {\n\t\t\t\t\t/* Over-strike (insert mode), use a modified bar caret */\n\t\t\t\t\trcCaret.top = rcCaret.bottom - 2;\n\t\t\t\t\trcCaret.left = xposCaret + 1;\n\t\t\t\t\trcCaret.right = rcCaret.left + widthOverstrikeCaret - 1;\n\t\t\t\t} else if ((caretShape == ViewStyle::CaretShape::block) || imeCaretBlockOverride) {\n\t\t\t\t\t/* Block caret */\n\t\t\t\t\trcCaret.left = xposCaret;\n\t\t\t\t\tif (canDrawBlockCaret && !(IsControl(ll->chars[offset]))) {\n\t\t\t\t\t\tdrawBlockCaret = true;\n\t\t\t\t\t\trcCaret.right = xposCaret + widthOverstrikeCaret;\n\t\t\t\t\t} else {\n\t\t\t\t\t\trcCaret.right = xposCaret + vsDraw.aveCharWidth;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t/* Line caret */\n\t\t\t\t\trcCaret.left = std::round(xposCaret - caretWidthOffset);\n\t\t\t\t\trcCaret.right = rcCaret.left + vsDraw.caret.width;\n\t\t\t\t}\n\t\t\t\tconst Element elementCaret = mainCaret ? Element::Caret : Element::CaretAdditional;\n\t\t\t\tconst ColourRGBA caretColour = vsDraw.ElementColourForced(elementCaret);\n\t\t\t\t//assert(caretColour.IsOpaque());\n\t\t\t\tif (drawBlockCaret) {\n\t\t\t\t\tDrawBlockCaret(surface, model, vsDraw, ll, subLine, xStart, offset, posCaret.Position(), rcCaret, caretColour);\n\t\t\t\t} else {\n\t\t\t\t\tsurface->FillRectangleAligned(rcCaret, Fill(caretColour));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (drawDrag)\n\t\t\tbreak;\n\t}\n}\n\nnamespace {\n\nvoid DrawWrapIndentAndMarker(Surface *surface, const ViewStyle &vsDraw, const LineLayout *ll,\n\tint xStart, PRectangle rcLine, ColourOptional background, DrawWrapMarkerFn customDrawWrapMarker,\n\tbool caretActive) {\n\t// default background here..\n\tsurface->FillRectangleAligned(rcLine, Fill(background.value_or(vsDraw.styles[StyleDefault].back)));\n\n\tif (vsDraw.IsLineFrameOpaque(caretActive, ll->containsCaret)) {\n\t\t// Draw left of frame under marker\n\t\tsurface->FillRectangleAligned(Side(rcLine, Edge::left, vsDraw.GetFrameWidth()),\n\t\t\tvsDraw.ElementColourForced(Element::CaretLineBack).Opaque());\n\t}\n\n\tif (FlagSet(vsDraw.wrap.visualFlags, WrapVisualFlag::Start)) {\n\n\t\t// draw continuation rect\n\t\tPRectangle rcPlace = rcLine;\n\n\t\trcPlace.left = static_cast<XYPOSITION>(xStart);\n\t\trcPlace.right = rcPlace.left + ll->wrapIndent;\n\n\t\tif (FlagSet(vsDraw.wrap.visualFlagsLocation, WrapVisualLocation::StartByText))\n\t\t\trcPlace.left = rcPlace.right - vsDraw.aveCharWidth;\n\t\telse\n\t\t\trcPlace.right = rcPlace.left + vsDraw.aveCharWidth;\n\n\t\tif (!customDrawWrapMarker) {\n\t\t\tDrawWrapMarker(surface, rcPlace, false, vsDraw.WrapColour());\n\t\t} else {\n\t\t\tcustomDrawWrapMarker(surface, rcPlace, false, vsDraw.WrapColour());\n\t\t}\n\t}\n}\n\n// On the curses platform, the terminal is drawing its own caret, so if the caret is within\n// the main selection, do not draw the selection at that position.\n// Use iDoc from DrawBackground and DrawForeground here because TextSegment has been adjusted\n// such that, if the caret is inside the main selection, the beginning or end of that selection\n// is at the end of a text segment.\n// This function should only be called if iDoc is within the main selection.\nInSelection CharacterInCursesSelection(Sci::Position iDoc, const EditModel &model, const ViewStyle &vsDraw) noexcept {\n\tconst SelectionPosition &posCaret = model.sel.RangeMain().caret;\n\tconst bool caretAtStart = posCaret < model.sel.RangeMain().anchor && posCaret.Position() == iDoc;\n\tconst bool caretAtEnd = posCaret > model.sel.RangeMain().anchor &&\n\t\tvsDraw.DrawCaretInsideSelection(false, false) &&\n\t\tmodel.pdoc->MovePositionOutsideChar(posCaret.Position() - 1, -1) == iDoc;\n\treturn (caretAtStart || caretAtEnd) ? InSelection::inNone : InSelection::inMain;\n}\n\nvoid DrawBackground(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,\n\tint xStart, PRectangle rcLine, int subLine, Range lineRange, Sci::Position posLineStart,\n\tColourOptional background) {\n\n\tconst bool selBackDrawn = vsDraw.SelectionBackgroundDrawn();\n\tbool inIndentation = subLine == 0;\t// Do not handle indentation except on first subline.\n\tconst XYPOSITION subLineStart = ll->positions[lineRange.start];\n\tconst XYPOSITION horizontalOffset = xStart - subLineStart;\n\t// Does not take margin into account but not significant\n\tconst XYPOSITION xStartVisible = subLineStart - xStart;\n\n\tconst BreakFinder::BreakFor breakFor = selBackDrawn ? BreakFinder::BreakFor::Selection : BreakFinder::BreakFor::Text;\n\tBreakFinder bfBack(ll, &model.sel, lineRange, posLineStart, xStartVisible, breakFor, model.pdoc, model.reprs.get(), &vsDraw);\n\n\tconst bool drawWhitespaceBackground = vsDraw.WhitespaceBackgroundDrawn() && !background;\n\n\t// Background drawing loop\n\twhile (bfBack.More()) {\n\n\t\tconst TextSegment ts = bfBack.Next();\n\t\tconst Sci::Position i = ts.end() - 1;\n\t\tconst Sci::Position iDoc = i + posLineStart;\n\n\t\tconst Interval horizontal = ll->Span(ts.start, ts.end()).Offset(horizontalOffset);\n\t\t// Only try to draw if really visible - enhances performance by not calling environment to\n\t\t// draw strings that are completely past the right side of the window.\n\t\tif (!horizontal.Empty() && rcLine.Intersects(horizontal)) {\n\t\t\tconst PRectangle rcSegment = Intersection(rcLine, horizontal);\n\n\t\t\tInSelection inSelection = vsDraw.selection.visible ? model.sel.CharacterInSelection(iDoc) : InSelection::inNone;\n\t\t\tif (FlagSet(vsDraw.caret.style, CaretStyle::Curses) && (inSelection == InSelection::inMain))\n\t\t\t\tinSelection = CharacterInCursesSelection(iDoc, model, vsDraw);\n\t\t\tconst bool inHotspot = model.hotspot.Valid() && model.hotspot.ContainsCharacter(iDoc);\n\t\t\tColourRGBA textBack = TextBackground(model, vsDraw, ll, background, inSelection,\n\t\t\t\tinHotspot, ll->styles[i], i);\n\t\t\tif (ts.representation) {\n\t\t\t\tif (ll->chars[i] == '\\t') {\n\t\t\t\t\t// Tab display\n\t\t\t\t\tif (drawWhitespaceBackground && vsDraw.WhiteSpaceVisible(inIndentation)) {\n\t\t\t\t\t\ttextBack = vsDraw.ElementColourForced(Element::WhiteSpaceBack).Opaque();\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// Blob display\n\t\t\t\t\tinIndentation = false;\n\t\t\t\t}\n\t\t\t\tsurface->FillRectangleAligned(rcSegment, Fill(textBack));\n\t\t\t} else {\n\t\t\t\t// Normal text display\n\t\t\t\tsurface->FillRectangleAligned(rcSegment, Fill(textBack));\n\t\t\t\tif (vsDraw.viewWhitespace != WhiteSpace::Invisible) {\n\t\t\t\t\tfor (int cpos = 0; cpos <= i - ts.start; cpos++) {\n\t\t\t\t\t\tif (ll->chars[cpos + ts.start] == ' ') {\n\t\t\t\t\t\t\tif (drawWhitespaceBackground && vsDraw.WhiteSpaceVisible(inIndentation)) {\n\t\t\t\t\t\t\t\tconst PRectangle rcSpace = Intersection(rcLine,\n\t\t\t\t\t\t\t\t\tll->SpanByte(cpos + ts.start).Offset(horizontalOffset));\n\t\t\t\t\t\t\t\tsurface->FillRectangleAligned(rcSpace,\n\t\t\t\t\t\t\t\t\tvsDraw.ElementColourForced(Element::WhiteSpaceBack).Opaque());\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tinIndentation = false;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (horizontal.left > rcLine.right) {\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\nvoid DrawEdgeLine(Surface *surface, const ViewStyle &vsDraw, const LineLayout *ll,\n\tint xStart, PRectangle rcLine, Range lineRange) {\n\tif (vsDraw.edgeState == EdgeVisualStyle::Line) {\n\t\tPRectangle rcSegment = rcLine;\n\t\tconst int edgeX = static_cast<int>(vsDraw.theEdge.column * vsDraw.spaceWidth);\n\t\trcSegment.left = static_cast<XYPOSITION>(edgeX + xStart);\n\t\tif ((ll->wrapIndent != 0) && (lineRange.start != 0))\n\t\t\trcSegment.left -= ll->wrapIndent;\n\t\trcSegment.right = rcSegment.left + 1;\n\t\tsurface->FillRectangleAligned(rcSegment, Fill(vsDraw.theEdge.colour));\n\t} else if (vsDraw.edgeState == EdgeVisualStyle::MultiLine) {\n\t\tfor (size_t edge = 0; edge < vsDraw.theMultiEdge.size(); edge++) {\n\t\t\tif (vsDraw.theMultiEdge[edge].column >= 0) {\n\t\t\t\tPRectangle rcSegment = rcLine;\n\t\t\t\tconst int edgeX = static_cast<int>(vsDraw.theMultiEdge[edge].column * vsDraw.spaceWidth);\n\t\t\t\trcSegment.left = static_cast<XYPOSITION>(edgeX + xStart);\n\t\t\t\tif ((ll->wrapIndent != 0) && (lineRange.start != 0))\n\t\t\t\t\trcSegment.left -= ll->wrapIndent;\n\t\t\t\trcSegment.right = rcSegment.left + 1;\n\t\t\t\tsurface->FillRectangleAligned(rcSegment, Fill(vsDraw.theMultiEdge[edge].colour));\n\t\t\t}\n\t\t}\n\t}\n}\n\n// Draw underline mark as part of background if on base layer\nvoid DrawMarkUnderline(Surface *surface, const EditModel &model, const ViewStyle &vsDraw,\n\tSci::Line line, PRectangle rcLine) {\n\tint marks = model.GetMark(line);\n\tfor (int markBit = 0; (markBit <= MarkerMax) && marks; markBit++) {\n\t\tif ((marks & 1) && (vsDraw.markers[markBit].markType == MarkerSymbol::Underline) &&\n\t\t\t(vsDraw.markers[markBit].layer == Layer::Base)) {\n\t\t\tPRectangle rcUnderline = rcLine;\n\t\t\trcUnderline.top = rcUnderline.bottom - 1; //au: was 2\n\t\t\tsurface->FillRectangleAligned(rcUnderline, Fill(vsDraw.markers[markBit].back));\n\t\t}\n\t\tmarks >>= 1;\n\t}\n}\n\nvoid DrawTranslucentSelection(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,\n\tSci::Line line, int xStart, PRectangle rcLine, int subLine, Range lineRange, int tabWidthMinimumPixels, Layer layer) {\n\tif (vsDraw.selection.layer == layer) {\n\t\tconst Sci::Position posLineStart = model.pdoc->LineStart(line);\n\t\tconst XYPOSITION subLineStart = ll->positions[lineRange.start];\n\t\tconst XYPOSITION horizontalOffset = xStart - subLineStart;\n\t\t// For each selection draw\n\t\tSci::Position virtualSpaces = 0;\n\t\tif (subLine == (ll->lines - 1)) {\n\t\t\tvirtualSpaces = model.sel.VirtualSpaceFor(model.pdoc->LineEnd(line));\n\t\t}\n\t\tconst SelectionPosition posStart(posLineStart + lineRange.start);\n\t\tconst SelectionPosition posEnd(posLineStart + lineRange.end, virtualSpaces);\n\t\tconst SelectionSegment virtualSpaceRange(posStart, posEnd);\n\t\tfor (size_t r = 0; r < model.sel.Count(); r++) {\n\t\t\tconst SelectionSegment portion = model.sel.Range(r).Intersect(virtualSpaceRange);\n\t\t\tif (!portion.Empty()) {\n\t\t\t\tconst SelectionSegment portionInLine = portion.Subtract(posLineStart);\n\t\t\t\tconst ColourRGBA selectionBack = SelectionBackground(\n\t\t\t\t\tmodel, vsDraw, model.sel.RangeType(r));\n\t\t\t\tconst XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;\n\t\t\t\tconst Interval intervalVirtual{\n\t\t\t\t\tportion.start.VirtualSpaceWidth(spaceWidth),\n\t\t\t\t\tportion.end.VirtualSpaceWidth(spaceWidth) };\n\t\t\t\tif (model.BidirectionalEnabled()) {\n\t\t\t\t\tconst SelectionSegment portionInSubLine = portionInLine.Subtract(lineRange.start);\n\n\t\t\t\t\tconst ScreenLine screenLine(ll, subLine, vsDraw, rcLine.right, tabWidthMinimumPixels);\n\t\t\t\t\tstd::unique_ptr<IScreenLineLayout> slLayout = surface->Layout(&screenLine);\n\n\t\t\t\t\tif (slLayout) {\n\t\t\t\t\t\tconst std::vector<Interval> intervals = slLayout->FindRangeIntervals(\n\t\t\t\t\t\t\tportionInSubLine.start.Position(), portionInSubLine.end.Position());\n\t\t\t\t\t\tfor (const Interval &interval : intervals) {\n\t\t\t\t\t\t\tconst PRectangle rcSelection = rcLine.WithHorizontalBounds(interval.Offset(xStart));\n\t\t\t\t\t\t\tsurface->FillRectangleAligned(rcSelection, selectionBack);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (portion.end.VirtualSpace()) {\n\t\t\t\t\t\tconst XYPOSITION xStartVirtual = ll->positions[lineRange.end] + horizontalOffset;\n\t\t\t\t\t\tconst PRectangle rcSegment = rcLine.WithHorizontalBounds(intervalVirtual.Offset(xStartVirtual));\n\t\t\t\t\t\tsurface->FillRectangleAligned(rcSegment, selectionBack);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tInterval intervalSegment = ll->Span(\n\t\t\t\t\t\tstatic_cast<int>(portionInLine.start.Position()),\n\t\t\t\t\t\tstatic_cast<int>(portionInLine.end.Position()))\n\t\t\t\t\t\t.Offset(horizontalOffset);\n\t\t\t\t\tintervalSegment.left += intervalVirtual.left;\n\t\t\t\t\tintervalSegment.right += intervalVirtual.right;\n\t\t\t\t\tif ((ll->wrapIndent != 0) && (lineRange.start != 0)) {\n\t\t\t\t\t\tif ((portionInLine.start.Position() == lineRange.start) &&\n\t\t\t\t\t\t\tmodel.sel.Range(r).ContainsCharacter(portion.start.Position() - 1))\n\t\t\t\t\t\t\tintervalSegment.left -= static_cast<int>(ll->wrapIndent); // indentation added to xStart was truncated to int, so we do the same here\n\t\t\t\t\t}\n\t\t\t\t\tconst PRectangle rcSegment = Intersection(rcLine, intervalSegment);\n\t\t\t\t\tif (rcSegment.right > rcLine.left)\n\t\t\t\t\t\tsurface->FillRectangleAligned(rcSegment, selectionBack);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid DrawCaretLineFramed(Surface *surface, const ViewStyle &vsDraw, const LineLayout *ll,\n\tPRectangle rcLine, int subLine) {\n\tconst ColourOptional caretlineBack = vsDraw.ElementColour(Element::CaretLineBack);\n\tif (!caretlineBack) {\n\t\treturn;\n\t}\n\n\tconst ColourRGBA colourFrame = (vsDraw.caretLine.layer == Layer::Base) ?\n\t\tcaretlineBack->Opaque() : *caretlineBack;\n\n\tconst int width = vsDraw.GetFrameWidth();\n\n\t// Avoid double drawing the corners by removing the left and right sides when drawing top and bottom borders\n\tconst PRectangle rcWithoutLeftRight = rcLine.Inset(Point(width, 0.0));\n\n\tif (subLine == 0 || ll->wrapIndent == 0 || vsDraw.caretLine.layer != Layer::Base || vsDraw.caretLine.subLine) {\n\t\t// Left\n\t\tsurface->FillRectangleAligned(Side(rcLine, Edge::left, width), colourFrame);\n\t}\n\tif (subLine == 0 || vsDraw.caretLine.subLine) {\n\t\t// Top\n\t\tsurface->FillRectangleAligned(Side(rcWithoutLeftRight, Edge::top, width), colourFrame);\n\t}\n\tif (subLine == ll->lines - 1 || vsDraw.caretLine.layer != Layer::Base || vsDraw.caretLine.subLine) {\n\t\t// Right\n\t\tsurface->FillRectangleAligned(Side(rcLine, Edge::right, width), colourFrame);\n\t}\n\tif (subLine == ll->lines - 1 || vsDraw.caretLine.subLine) {\n\t\t// Bottom\n\t\tsurface->FillRectangleAligned(Side(rcWithoutLeftRight, Edge::bottom, width), colourFrame);\n\t}\n}\n\n// Draw any translucent whole line states\nvoid DrawTranslucentLineState(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,\n\tSci::Line line, PRectangle rcLine, int subLine, Layer layer) {\n\tif ((model.caret.active || vsDraw.caretLine.alwaysShow) && vsDraw.ElementColour(Element::CaretLineBack) && ll->containsCaret &&\n\t\tvsDraw.caretLine.layer == layer) {\n\t\tif (vsDraw.caretLine.frame) {\n\t\t\tDrawCaretLineFramed(surface, vsDraw, ll, rcLine, subLine);\n\t\t} else {\n\t\t\tsurface->FillRectangleAligned(rcLine, vsDraw.ElementColourForced(Element::CaretLineBack));\n\t\t}\n\t}\n\tconst int marksOfLine = model.GetMark(line);\n\tint marksDrawnInText = marksOfLine & vsDraw.maskDrawInText;\n\tfor (int markBit = 0; (markBit <= MarkerMax) && marksDrawnInText; markBit++) {\n\t\tif ((marksDrawnInText & 1) && (vsDraw.markers[markBit].layer == layer)) {\n\t\t\tif (vsDraw.markers[markBit].markType == MarkerSymbol::Background) {\n\t\t\t\tsurface->FillRectangleAligned(rcLine, vsDraw.markers[markBit].BackWithAlpha());\n\t\t\t} else if (vsDraw.markers[markBit].markType == MarkerSymbol::Underline) {\n\t\t\t\tPRectangle rcUnderline = rcLine;\n\t\t\t\trcUnderline.top = rcUnderline.bottom - 2;\n\t\t\t\tsurface->FillRectangleAligned(rcUnderline, vsDraw.markers[markBit].BackWithAlpha());\n\t\t\t}\n\t\t}\n\t\tmarksDrawnInText >>= 1;\n\t}\n\tint marksDrawnInLine = marksOfLine & vsDraw.maskInLine;\n\tfor (int markBit = 0; (markBit <= MarkerMax) && marksDrawnInLine; markBit++) {\n\t\tif ((marksDrawnInLine & 1) && (vsDraw.markers[markBit].layer == layer)) {\n\t\t\tsurface->FillRectangleAligned(rcLine, vsDraw.markers[markBit].BackWithAlpha());\n\t\t}\n\t\tmarksDrawnInLine >>= 1;\n\t}\n}\n\nvoid DrawTabArrow(Surface *surface, PRectangle rcTab, int ymid,\n\tconst ViewStyle &vsDraw, Stroke stroke) {\n\n\tconst XYPOSITION halfWidth = stroke.width / 2.0;\n\n\tconst XYPOSITION leftStroke = std::round(std::min(rcTab.left + 2, rcTab.right - 1)) + halfWidth;\n\tconst XYPOSITION rightStroke = std::max(leftStroke, std::round(rcTab.right) - 1.0f - halfWidth);\n\tconst XYPOSITION yMidAligned = ymid + halfWidth;\n\tconst Point arrowPoint(rightStroke, yMidAligned);\n\tif (rightStroke > leftStroke) {\n\t\t// When not enough room, don't draw the arrow shaft\n\t\tsurface->LineDraw(Point(leftStroke, yMidAligned), arrowPoint, stroke);\n\t}\n\n\t// Draw the arrow head if needed\n\tif (vsDraw.tabDrawMode == TabDrawMode::LongArrow) {\n\t\tXYPOSITION ydiff = std::floor(rcTab.Height() / 2.0f);\n\t\tXYPOSITION xhead = rightStroke - ydiff;\n\t\tif (xhead <= rcTab.left) {\n\t\t\tydiff -= rcTab.left - xhead;\n\t\t\txhead = rcTab.left;\n\t\t}\n\t\tconst Point ptsHead[] = {\n\t\t\tPoint(xhead, yMidAligned - ydiff),\n\t\t\tarrowPoint,\n\t\t\tPoint(xhead, yMidAligned + ydiff)\n\t\t};\n\t\tsurface->PolyLine(ptsHead, std::size(ptsHead), stroke);\n\t}\n}\n\nvoid DrawIndicator(int indicNum, Sci::Position startPos, Sci::Position endPos, Surface *surface, const ViewStyle &vsDraw,\n\tconst LineLayout *ll, int xStart, PRectangle rcLine, Sci::Position secondCharacter, int subLine, Indicator::State state,\n\tint value, bool bidiEnabled, int tabWidthMinimumPixels) {\n\n\tconst XYPOSITION subLineStart = ll->positions[ll->LineStart(subLine)];\n\tconst XYPOSITION horizontalOffset = xStart - subLineStart;\n\n\tstd::vector<PRectangle> rectangles;\n\n\tconst XYPOSITION left = ll->XInLine(startPos) + horizontalOffset;\n\tconst XYPOSITION right = ll->XInLine(endPos) + horizontalOffset;\n\tconst PRectangle rcIndic(left, rcLine.top + vsDraw.maxAscent, right,\n\t\tstd::max(rcLine.top + vsDraw.maxAscent + 3, rcLine.bottom));\n\n\tif (bidiEnabled) {\n\t\tScreenLine screenLine(ll, subLine, vsDraw, rcLine.right - xStart, tabWidthMinimumPixels);\n\t\tconst Range lineRange = ll->SubLineRange(subLine, LineLayout::Scope::visibleOnly);\n\n\t\tstd::unique_ptr<IScreenLineLayout> slLayout = surface->Layout(&screenLine);\n\t\tstd::vector<Interval> intervals = slLayout->FindRangeIntervals(\n\t\t\tstartPos - lineRange.start, endPos - lineRange.start);\n\t\tfor (const Interval &interval : intervals) {\n\t\t\tPRectangle rcInterval = rcIndic;\n\t\t\trcInterval.left = interval.left + xStart;\n\t\t\trcInterval.right = interval.right + xStart;\n\t\t\trectangles.push_back(rcInterval);\n\t\t}\n\t} else {\n\t\trectangles.push_back(rcIndic);\n\t}\n\n\tfor (const PRectangle &rc : rectangles) {\n\t\tPRectangle rcFirstCharacter = rc;\n\t\t// Allow full descent space for character indicators\n\t\trcFirstCharacter.bottom = rcLine.top + vsDraw.maxAscent + vsDraw.maxDescent;\n\t\tif (secondCharacter >= 0) {\n\t\t\trcFirstCharacter.right = ll->XInLine(secondCharacter) + horizontalOffset;\n\t\t} else {\n\t\t\t// Indicator continued from earlier line so make an empty box and don't draw\n\t\t\trcFirstCharacter.right = rcFirstCharacter.left;\n\t\t}\n\t\tvsDraw.indicators[indicNum].Draw(surface, rc, rcLine, rcFirstCharacter, state, value);\n\t}\n}\n\nvoid DrawIndicators(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,\n\tSci::Line line, int xStart, PRectangle rcLine, int subLine, Sci::Position lineEnd, bool under, int tabWidthMinimumPixels) {\n\t// Draw decorators\n\tconst Sci::Position posLineStart = model.pdoc->LineStart(line);\n\tconst Sci::Position lineStart = ll->LineStart(subLine);\n\tconst Sci::Position posLineEnd = posLineStart + lineEnd;\n\n\tfor (const IDecoration *deco : model.pdoc->decorations->View()) {\n\t\tif (under == vsDraw.indicators[deco->Indicator()].under) {\n\t\t\tSci::Position startPos = posLineStart + lineStart;\n\t\t\twhile (startPos < posLineEnd) {\n\t\t\t\tconst Range rangeRun(deco->StartRun(startPos), deco->EndRun(startPos));\n\t\t\t\tconst Sci::Position endPos = std::min(rangeRun.end, posLineEnd);\n\t\t\t\tconst int value = deco->ValueAt(startPos);\n\t\t\t\tif (value) {\n\t\t\t\t\tconst bool hover = vsDraw.indicators[deco->Indicator()].IsDynamic() &&\n\t\t\t\t\t\trangeRun.ContainsCharacter(model.hoverIndicatorPos);\n\t\t\t\t\tconst Indicator::State state = hover ? Indicator::State::hover : Indicator::State::normal;\n\t\t\t\t\tconst Sci::Position posSecond = model.pdoc->MovePositionOutsideChar(rangeRun.First() + 1, 1);\n\t\t\t\t\tDrawIndicator(deco->Indicator(), startPos - posLineStart, endPos - posLineStart,\n\t\t\t\t\t\tsurface, vsDraw, ll, xStart, rcLine, posSecond - posLineStart, subLine, state,\n\t\t\t\t\t\tvalue, model.BidirectionalEnabled(), tabWidthMinimumPixels);\n\t\t\t\t}\n\t\t\t\tstartPos = endPos;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Use indicators to highlight matching braces\n\tif ((vsDraw.braceHighlightIndicatorSet && (model.bracesMatchStyle == StyleBraceLight)) ||\n\t\t(vsDraw.braceBadLightIndicatorSet && (model.bracesMatchStyle == StyleBraceBad))) {\n\t\tconst int braceIndicator = (model.bracesMatchStyle == StyleBraceLight) ? vsDraw.braceHighlightIndicator : vsDraw.braceBadLightIndicator;\n\t\tif (under == vsDraw.indicators[braceIndicator].under) {\n\t\t\tconst Range rangeLine(posLineStart + lineStart, posLineEnd);\n\t\t\tfor (size_t brace = 0; brace <= 1; brace++) {\n\t\t\t\tif (rangeLine.ContainsCharacter(model.braces[brace])) {\n\t\t\t\t\tconst Sci::Position braceOffset = model.braces[brace] - posLineStart;\n\t\t\t\t\tif (braceOffset < ll->numCharsInLine) {\n\t\t\t\t\t\tconst Sci::Position braceEnd = model.pdoc->MovePositionOutsideChar(model.braces[brace] + 1, 1) - posLineStart;\n\t\t\t\t\t\tDrawIndicator(braceIndicator, braceOffset, braceEnd,\n\t\t\t\t\t\t\tsurface, vsDraw, ll, xStart, rcLine, braceEnd, subLine, Indicator::State::normal,\n\t\t\t\t\t\t\t1, model.BidirectionalEnabled(), tabWidthMinimumPixels);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif (FlagSet(model.changeHistoryOption, ChangeHistoryOption::Indicators)) {\n\t\t// Draw editions\n\t\tconstexpr int indexHistory = static_cast<int>(IndicatorNumbers::HistoryRevertedToOriginInsertion);\n\t\t{\n\t\t\t// Draw insertions\n\t\t\tSci::Position startPos = posLineStart + lineStart;\n\t\t\twhile (startPos < posLineEnd) {\n\t\t\t\tconst Range rangeRun(startPos, model.pdoc->EditionEndRun(startPos));\n\t\t\t\tconst Sci::Position endPos = std::min(rangeRun.end, posLineEnd);\n\t\t\t\tconst int edition = model.pdoc->EditionAt(startPos);\n\t\t\t\tif (edition != 0) {\n\t\t\t\t\tconst int indicator = (edition - 1) * 2 + indexHistory;\n\t\t\t\t\tconst Sci::Position posSecond = model.pdoc->MovePositionOutsideChar(rangeRun.First() + 1, 1);\n\t\t\t\t\tDrawIndicator(indicator, startPos - posLineStart, endPos - posLineStart,\n\t\t\t\t\t\tsurface, vsDraw, ll, xStart, rcLine, posSecond - posLineStart, subLine, Indicator::State::normal,\n\t\t\t\t\t\t1, model.BidirectionalEnabled(), tabWidthMinimumPixels);\n\t\t\t\t}\n\t\t\t\tstartPos = endPos;\n\t\t\t}\n\t\t}\n\t\t{\n\t\t\t// Draw deletions\n\t\t\tSci::Position startPos = posLineStart + lineStart;\n\t\t\twhile (startPos <= posLineEnd) {\n\t\t\t\tconst unsigned int editions = model.pdoc->EditionDeletesAt(startPos);\n\t\t\t\tconst Sci::Position posSecond = model.pdoc->MovePositionOutsideChar(startPos + 1, 1);\n\t\t\t\tfor (unsigned int edition = 0; edition < 4; edition++) {\n\t\t\t\t\tif (editions & (1 << edition)) {\n\t\t\t\t\t\tconst int indicator = edition * 2 + indexHistory + 1;\n\t\t\t\t\t\tDrawIndicator(indicator, startPos - posLineStart, posSecond - posLineStart,\n\t\t\t\t\t\t\tsurface, vsDraw, ll, xStart, rcLine, posSecond - posLineStart, subLine, Indicator::State::normal,\n\t\t\t\t\t\t\t1, model.BidirectionalEnabled(), tabWidthMinimumPixels);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tstartPos = model.pdoc->EditionNextDelete(startPos);\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid DrawFoldLines(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,\n\tSci::Line line, PRectangle rcLine, int subLine) {\n\tconst bool lastSubLine = subLine == (ll->lines - 1);\n\tconst bool expanded = model.pcs->GetExpanded(line);\n\tconst FoldLevel level = model.pdoc->GetFoldLevel(line);\n\tconst FoldLevel levelNext = model.pdoc->GetFoldLevel(line + 1);\n\tif (LevelIsHeader(level) &&\n\t\t(LevelNumber(level) < LevelNumber(levelNext))) {\n\t\tconst ColourRGBA foldLineColour = vsDraw.ElementColour(Element::FoldLine).value_or(\n\t\t\tvsDraw.styles[StyleDefault].fore);\n\t\t// Paint the line above the fold\n\t\tif ((subLine == 0) && FlagSet(model.foldFlags, (expanded ? FoldFlag::LineBeforeExpanded: FoldFlag::LineBeforeContracted))) {\n\t\t\tsurface->FillRectangleAligned(Side(rcLine, Edge::top, 1.0), foldLineColour);\n\t\t}\n\t\t// Paint the line below the fold\n\t\tif (lastSubLine && FlagSet(model.foldFlags, (expanded ? FoldFlag::LineAfterExpanded : FoldFlag::LineAfterContracted))) {\n\n\t\t\t//Au: draw dots line\n\t\t\tsurface->DrawLineDots(Side(rcLine, Edge::bottom, 1.0), ColourRGBA(0xc0c0c0));\n\t\t\t//surface->FillRectangleAligned(Side(rcLine, Edge::bottom, 1.0), foldLineColour);\n\n\t\t\t// If contracted fold line drawn then don't overwrite with hidden line\n\t\t\t// as fold lines are more specific then hidden lines.\n\t\t\tif (!expanded) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\tif (lastSubLine && model.pcs->GetVisible(line) && !model.pcs->GetVisible(line + 1)) {\n\t\tif (const ColourOptional hiddenLineColour = vsDraw.ElementColour(Element::HiddenLine)) {\n\t\t\tsurface->FillRectangleAligned(Side(rcLine, Edge::bottom, 1.0), *hiddenLineColour);\n\t\t}\n\t}\n}\n\nColourRGBA InvertedLight(ColourRGBA orig) noexcept {\n\tunsigned int r = orig.GetRed();\n\tunsigned int g = orig.GetGreen();\n\tunsigned int b = orig.GetBlue();\n\tconst unsigned int l = (r + g + b) / 3; \t// There is a better calculation for this that matches human eye\n\tconst unsigned int il = 0xff - l;\n\tif (l == 0)\n\t\treturn white;\n\tr = r * il / l;\n\tg = g * il / l;\n\tb = b * il / l;\n\treturn ColourRGBA(std::min(r, 0xffu), std::min(g, 0xffu), std::min(b, 0xffu));\n}\n\n}\n\nvoid EditView::DrawIndentGuide(Surface *surface, XYPOSITION start, PRectangle rcSegment, bool highlight, bool offset) {\n\tconst Point from = Point::FromInts(0, offset ? 1 : 0);\n\tconst PRectangle rcCopyArea(start + 1, rcSegment.top,\n\t\tstart + 2, rcSegment.bottom);\n\tsurface->Copy(rcCopyArea, from,\n\t\thighlight ? *pixmapIndentGuideHighlight : *pixmapIndentGuide);\n}\n\nvoid EditView::DrawForeground(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,\n\tint xStart, PRectangle rcLine, int subLine, Sci::Line lineVisible, Range lineRange, Sci::Position posLineStart,\n\tColourOptional background) {\n\n\tconst bool selBackDrawn = vsDraw.SelectionBackgroundDrawn();\n\tconst bool drawWhitespaceBackground = vsDraw.WhitespaceBackgroundDrawn() && !background;\n\tbool inIndentation = subLine == 0;\t// Do not handle indentation except on first subline.\n\n\tconst XYPOSITION subLineStart = ll->positions[lineRange.start];\n\tconst XYPOSITION horizontalOffset = xStart - subLineStart;\n\tconst XYPOSITION indentWidth = model.pdoc->IndentSize() * vsDraw.spaceWidth;\n\n\t// Does not take margin into account but not significant\n\tconst XYPOSITION xStartVisible = subLineStart - xStart;\n\n\t// When lineHeight is odd, dotted indent guides are drawn offset by 1 on odd lines to join together.\n\tconst bool offsetGuide = (lineVisible & 1) && (vsDraw.lineHeight & 1);\n\n\t// Same baseline used for all text\n\tconst XYPOSITION ybase = rcLine.top + vsDraw.maxAscent;\n\n\t// Foreground drawing loop\n\tconst BreakFinder::BreakFor breakFor = (((phasesDraw == PhasesDraw::One) && selBackDrawn) || vsDraw.SelectionTextDrawn())\n\t\t? BreakFinder::BreakFor::ForegroundAndSelection : BreakFinder::BreakFor::Foreground;\n\tBreakFinder bfFore(ll, &model.sel, lineRange, posLineStart, xStartVisible, breakFor, model.pdoc, model.reprs.get(), &vsDraw);\n\n\twhile (bfFore.More()) {\n\n\t\tconst TextSegment ts = bfFore.Next();\n\t\tconst Sci::Position i = ts.end() - 1;\n\t\tconst Sci::Position iDoc = i + posLineStart;\n\n\t\tconst Interval horizontal = ll->Span(ts.start, ts.end()).Offset(horizontalOffset);\n\t\t// Only try to draw if really visible - enhances performance by not calling environment to\n\t\t// draw strings that are completely past the right side of the window.\n\t\tif (rcLine.Intersects(horizontal)) {\n\t\t\tconst PRectangle rcSegment = rcLine.WithHorizontalBounds(horizontal);\n\t\t\tconst int styleMain = ll->styles[i];\n\t\t\tColourRGBA textFore = vsDraw.styles[styleMain].fore;\n\t\t\tconst Font *textFont = vsDraw.styles[styleMain].font.get();\n\t\t\t// Hot-spot foreground\n\t\t\tconst bool inHotspot = model.hotspot.Valid() && model.hotspot.ContainsCharacter(iDoc);\n\t\t\tif (inHotspot) {\n\t\t\t\tif (const ColourOptional colourHotSpot = vsDraw.ElementColour(Element::HotSpotActive)) {\n\t\t\t\t\ttextFore = *colourHotSpot;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (vsDraw.indicatorsSetFore) {\n\t\t\t\t// At least one indicator sets the text colour so see if it applies to this segment\n\t\t\t\tfor (const IDecoration *deco : model.pdoc->decorations->View()) {\n\t\t\t\t\tconst int indicatorValue = deco->ValueAt(ts.start + posLineStart);\n\t\t\t\t\tif (indicatorValue) {\n\t\t\t\t\t\tconst Indicator &indicator = vsDraw.indicators[deco->Indicator()];\n\t\t\t\t\t\tbool hover = false;\n\t\t\t\t\t\tif (indicator.IsDynamic()) {\n\t\t\t\t\t\t\tconst Sci::Position startPos = ts.start + posLineStart;\n\t\t\t\t\t\t\tconst Range rangeRun(deco->StartRun(startPos), deco->EndRun(startPos));\n\t\t\t\t\t\t\thover =\trangeRun.ContainsCharacter(model.hoverIndicatorPos);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (hover) {\n\t\t\t\t\t\t\tif (indicator.sacHover.style == IndicatorStyle::TextFore) {\n\t\t\t\t\t\t\t\ttextFore = indicator.sacHover.fore;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tif (indicator.sacNormal.style == IndicatorStyle::TextFore) {\n\t\t\t\t\t\t\t\tif (FlagSet(indicator.Flags(), IndicFlag::ValueFore))\n\t\t\t\t\t\t\t\t\ttextFore = ColourRGBA::FromRGB(indicatorValue & static_cast<int>(IndicValue::Mask));\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\ttextFore = indicator.sacNormal.fore;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tInSelection inSelection = vsDraw.selection.visible ? model.sel.CharacterInSelection(iDoc) : InSelection::inNone;\n\t\t\tif (FlagSet(vsDraw.caret.style, CaretStyle::Curses) && (inSelection == InSelection::inMain))\n\t\t\t\tinSelection = CharacterInCursesSelection(iDoc, model, vsDraw);\n\t\t\tif (const ColourOptional selectionFore = SelectionForeground(model, vsDraw, inSelection)) {\n\t\t\t\ttextFore = *selectionFore;\n\t\t\t}\n\t\t\tColourRGBA textBack = TextBackground(model, vsDraw, ll, background, inSelection, inHotspot, styleMain, i);\n\t\t\tif (ts.representation) {\n\t\t\t\tif (ll->chars[i] == '\\t') {\n\t\t\t\t\t// Tab display\n\t\t\t\t\tif (phasesDraw == PhasesDraw::One) {\n\t\t\t\t\t\tif (drawWhitespaceBackground && vsDraw.WhiteSpaceVisible(inIndentation))\n\t\t\t\t\t\t\ttextBack = vsDraw.ElementColourForced(Element::WhiteSpaceBack).Opaque();\n\t\t\t\t\t\tsurface->FillRectangleAligned(rcSegment, Fill(textBack));\n\t\t\t\t\t}\n\t\t\t\t\tif (inIndentation && vsDraw.viewIndentationGuides == IndentView::Real) {\n\t\t\t\t\t\tconst Interval intervalCharacter = ll->SpanByte(static_cast<int>(i));\n\t\t\t\t\t\tfor (int indentCount = static_cast<int>((intervalCharacter.left + epsilon) / indentWidth);\n\t\t\t\t\t\t\tindentCount <= (intervalCharacter.right - epsilon) / indentWidth;\n\t\t\t\t\t\t\tindentCount++) {\n\t\t\t\t\t\t\tif (indentCount > 0) {\n\t\t\t\t\t\t\t\tconst XYPOSITION xIndent = std::floor(indentCount * indentWidth);\n\t\t\t\t\t\t\t\tDrawIndentGuide(surface, xIndent + xStart, rcSegment, ll->xHighlightGuide == xIndent, offsetGuide);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (vsDraw.viewWhitespace != WhiteSpace::Invisible) {\n\t\t\t\t\t\tif (vsDraw.WhiteSpaceVisible(inIndentation)) {\n\t\t\t\t\t\t\tconst PRectangle rcTab(rcSegment.left + 1, rcSegment.top + tabArrowHeight,\n\t\t\t\t\t\t\t\trcSegment.right - 1, rcSegment.bottom - vsDraw.maxDescent);\n\t\t\t\t\t\t\tconst int segmentTop = static_cast<int>(rcSegment.top) + vsDraw.lineHeight / 2;\n\t\t\t\t\t\t\tconst ColourRGBA whiteSpaceFore = vsDraw.ElementColour(Element::WhiteSpace).value_or(textFore);\n\t\t\t\t\t\t\tif (!customDrawTabArrow)\n\t\t\t\t\t\t\t\tDrawTabArrow(surface, rcTab, segmentTop, vsDraw, Stroke(whiteSpaceFore, 1.0f));\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tcustomDrawTabArrow(surface, rcTab, segmentTop, vsDraw, Stroke(whiteSpaceFore, 1.0f));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tinIndentation = false;\n\t\t\t\t\tif (vsDraw.controlCharSymbol >= 32) {\n\t\t\t\t\t\t// Using one font for all control characters so it can be controlled independently to ensure\n\t\t\t\t\t\t// the box goes around the characters tightly. Seems to be no way to work out what height\n\t\t\t\t\t\t// is taken by an individual character - internal leading gives varying results.\n\t\t\t\t\t\tconst Font *ctrlCharsFont = vsDraw.styles[StyleControlChar].font.get();\n\t\t\t\t\t\tconst char cc[2] = { static_cast<char>(vsDraw.controlCharSymbol), '\\0' };\n\t\t\t\t\t\tsurface->DrawTextNoClip(rcSegment, ctrlCharsFont,\n\t\t\t\t\t\t\tybase, cc, textBack, textFore);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (FlagSet(ts.representation->appearance, RepresentationAppearance::Colour)) {\n\t\t\t\t\t\t\ttextFore = ts.representation->colour;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (FlagSet(ts.representation->appearance, RepresentationAppearance::Blob)) {\n\t\t\t\t\t\t\tDrawTextBlob(surface, vsDraw, rcSegment, ts.representation->stringRep,\n\t\t\t\t\t\t\t\ttextBack, textFore, phasesDraw == PhasesDraw::One);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tsurface->DrawTextTransparentUTF8(rcSegment, vsDraw.styles[StyleControlChar].font.get(),\n\t\t\t\t\t\t\t\tybase, ts.representation->stringRep, textFore);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Normal text display\n\t\t\t\tif (vsDraw.styles[styleMain].visible) {\n\t\t\t\t\tconst std::string_view text(&ll->chars[ts.start], i - ts.start + 1);\n\t\t\t\t\tif (phasesDraw != PhasesDraw::One) {\n\t\t\t\t\t\tsurface->DrawTextTransparent(rcSegment, textFont,\n\t\t\t\t\t\t\tybase, text, textFore);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsurface->DrawTextNoClip(rcSegment, textFont,\n\t\t\t\t\t\t\tybase, text, textFore, textBack);\n\t\t\t\t\t}\n\t\t\t\t} else if (vsDraw.styles[styleMain].invisibleRepresentation[0]) {\n\t\t\t\t\tconst std::string_view text = vsDraw.styles[styleMain].invisibleRepresentation;\n  \t\t\t\t\tif (phasesDraw != PhasesDraw::One) {\n\t\t\t\t\t\tsurface->DrawTextTransparentUTF8(rcSegment, textFont,\n\t\t\t\t\t\t\tybase, text, textFore);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsurface->DrawTextNoClipUTF8(rcSegment, textFont,\n\t\t\t\t\t\t\tybase, text, textFore, textBack);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (vsDraw.viewWhitespace != WhiteSpace::Invisible ||\n\t\t\t\t\t(inIndentation && vsDraw.viewIndentationGuides != IndentView::None)) {\n\t\t\t\t\tfor (int cpos = 0; cpos <= i - ts.start; cpos++) {\n\t\t\t\t\t\tif (ll->chars[cpos + ts.start] == ' ') {\n\t\t\t\t\t\t\tif (vsDraw.viewWhitespace != WhiteSpace::Invisible) {\n\t\t\t\t\t\t\t\tif (vsDraw.WhiteSpaceVisible(inIndentation)) {\n\t\t\t\t\t\t\t\t\tconst Interval intervalSpace = ll->SpanByte(cpos + ts.start).Offset(horizontalOffset);\n\t\t\t\t\t\t\t\t\tconst XYPOSITION xmid = (intervalSpace.left + intervalSpace.right) / 2;\n\t\t\t\t\t\t\t\t\tif ((phasesDraw == PhasesDraw::One) && drawWhitespaceBackground) {\n\t\t\t\t\t\t\t\t\t\ttextBack = vsDraw.ElementColourForced(Element::WhiteSpaceBack).Opaque();\n\t\t\t\t\t\t\t\t\t\tconst PRectangle rcSpace = rcLine.WithHorizontalBounds(intervalSpace);\n\t\t\t\t\t\t\t\t\t\tsurface->FillRectangleAligned(rcSpace, Fill(textBack));\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tconst int halfDotWidth = vsDraw.whitespaceSize / 2;\n\t\t\t\t\t\t\t\t\tPRectangle rcDot(xmid - halfDotWidth,\n\t\t\t\t\t\t\t\t\t\trcSegment.top + vsDraw.lineHeight / 2, 0.0f, 0.0f);\n\t\t\t\t\t\t\t\t\trcDot.right = rcDot.left + vsDraw.whitespaceSize;\n\t\t\t\t\t\t\t\t\trcDot.bottom = rcDot.top + vsDraw.whitespaceSize;\n\t\t\t\t\t\t\t\t\tconst ColourRGBA whiteSpaceFore = vsDraw.ElementColour(Element::WhiteSpace).value_or(textFore);\n\t\t\t\t\t\t\t\t\tsurface->FillRectangleAligned(rcDot, Fill(whiteSpaceFore));\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (inIndentation && vsDraw.viewIndentationGuides == IndentView::Real) {\n\t\t\t\t\t\t\t\tconst Interval intervalCharacter = ll->SpanByte(cpos + ts.start);\n\t\t\t\t\t\t\t\tfor (int indentCount = static_cast<int>((intervalCharacter.left + epsilon) / indentWidth);\n\t\t\t\t\t\t\t\t\tindentCount <= (intervalCharacter.right - epsilon) / indentWidth;\n\t\t\t\t\t\t\t\t\tindentCount++) {\n\t\t\t\t\t\t\t\t\tif (indentCount > 0) {\n\t\t\t\t\t\t\t\t\t\tconst XYPOSITION xIndent = std::floor(indentCount * indentWidth);\n\t\t\t\t\t\t\t\t\t\tDrawIndentGuide(surface, xIndent + xStart, rcSegment, ll->xHighlightGuide == xIndent, offsetGuide);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tinIndentation = false;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif ((inHotspot && vsDraw.hotspotUnderline) || vsDraw.styles[styleMain].underline) {\n\t\t\t\tPRectangle rcUL = rcSegment;\n\t\t\t\trcUL.top = ybase + 1;\n\t\t\t\trcUL.bottom = ybase + 2;\n\t\t\t\tColourRGBA colourUnderline = textFore;\n\t\t\t\tif (inHotspot && vsDraw.hotspotUnderline) {\n\t\t\t\t\tcolourUnderline = vsDraw.ElementColour(Element::HotSpotActive).value_or(textFore);\n\t\t\t\t}\n\t\t\t\tsurface->FillRectangleAligned(rcUL, colourUnderline);\n\t\t\t}\n\t\t} else if (horizontal.left > rcLine.right) {\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\nvoid EditView::DrawIndentGuidesOverEmpty(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,\n\tSci::Line line, int xStart, PRectangle rcLine, int subLine, Sci::Line lineVisible) {\n\tif ((vsDraw.viewIndentationGuides == IndentView::LookForward || vsDraw.viewIndentationGuides == IndentView::LookBoth)\n\t\t&& (subLine == 0)) {\n\t\tconst Sci::Position posLineStart = model.pdoc->LineStart(line);\n\t\tint indentSpace = model.pdoc->GetLineIndentation(line);\n\t\tint xStartText = static_cast<int>(ll->positions[model.pdoc->GetLineIndentPosition(line) - posLineStart]);\n\n\t\t// Find the most recent line with some text\n\n\t\tSci::Line lineLastWithText = line;\n\t\twhile (lineLastWithText > std::max(line - 20, static_cast<Sci::Line>(0)) && model.pdoc->IsWhiteLine(lineLastWithText)) {\n\t\t\tlineLastWithText--;\n\t\t}\n\t\tif (lineLastWithText < line) {\n\t\t\txStartText = 100000;\t// Don't limit to visible indentation on empty line\n\t\t\t// This line is empty, so use indentation of last line with text\n\t\t\tint indentLastWithText = model.pdoc->GetLineIndentation(lineLastWithText);\n\t\t\tconst int isFoldHeader = LevelIsHeader(model.pdoc->GetFoldLevel(lineLastWithText));\n\t\t\tif (isFoldHeader) {\n\t\t\t\t// Level is one more level than parent\n\t\t\t\tindentLastWithText += model.pdoc->IndentSize();\n\t\t\t}\n\t\t\tif (vsDraw.viewIndentationGuides == IndentView::LookForward) {\n\t\t\t\t// In viLookForward mode, previous line only used if it is a fold header\n\t\t\t\tif (isFoldHeader) {\n\t\t\t\t\tindentSpace = std::max(indentSpace, indentLastWithText);\n\t\t\t\t}\n\t\t\t} else {\t// viLookBoth\n\t\t\t\tindentSpace = std::max(indentSpace, indentLastWithText);\n\t\t\t}\n\t\t}\n\n\t\tSci::Line lineNextWithText = line;\n\t\twhile (lineNextWithText < std::min(line + 20, model.pdoc->LinesTotal()) && model.pdoc->IsWhiteLine(lineNextWithText)) {\n\t\t\tlineNextWithText++;\n\t\t}\n\t\tif (lineNextWithText > line) {\n\t\t\txStartText = 100000;\t// Don't limit to visible indentation on empty line\n\t\t\t// This line is empty, so use indentation of first next line with text\n\t\t\tindentSpace = std::max(indentSpace,\n\t\t\t\tmodel.pdoc->GetLineIndentation(lineNextWithText));\n\t\t}\n\n\t\tconst bool offsetGuide = (lineVisible & 1) && (vsDraw.lineHeight & 1);\n\t\tfor (int indentPos = model.pdoc->IndentSize(); indentPos < indentSpace; indentPos += model.pdoc->IndentSize()) {\n\t\t\tconst XYPOSITION xIndent = std::floor(indentPos * vsDraw.spaceWidth);\n\t\t\tif (xIndent < xStartText) {\n\t\t\t\tDrawIndentGuide(surface, xIndent + xStart, rcLine,\tll->xHighlightGuide == xIndent, offsetGuide);\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid EditView::DrawLine(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,\n\tSci::Line line, Sci::Line lineVisible, int xStart, PRectangle rcLine, int subLine, DrawPhase phase) {\n\n\tif (subLine >= ll->lines) {\n\t\tDrawAnnotation(surface, model, vsDraw, ll, line, xStart, rcLine, subLine, phase);\n\t\treturn; // No further drawing\n\t}\n\n\tconst bool clipLine = !bufferedDraw && !LinesOverlap();\n\tif (clipLine) {\n\t\tsurface->SetClip(rcLine);\n\t}\n\n\t// See if something overrides the line background colour.\n\tconst ColourOptional background = vsDraw.Background(model.GetMark(line), model.caret.active, ll->containsCaret);\n\n\tconst Sci::Position posLineStart = model.pdoc->LineStart(line);\n\n\tconst Range lineRange = ll->SubLineRange(subLine, LineLayout::Scope::visibleOnly);\n\tconst Range lineRangeIncludingEnd = ll->SubLineRange(subLine, LineLayout::Scope::includeEnd);\n\tconst XYPOSITION subLineStart = ll->positions[lineRange.start];\n\n\tif ((ll->wrapIndent != 0) && (subLine > 0)) {\n\t\tif (FlagSet(phase, DrawPhase::back)) {\n\t\t\tDrawWrapIndentAndMarker(surface, vsDraw, ll, xStart, rcLine, background, customDrawWrapMarker, model.caret.active);\n\t\t}\n\t\txStart += static_cast<int>(ll->wrapIndent);\n\t}\n\n\tif (phasesDraw != PhasesDraw::One) {\n\t\tif (FlagSet(phase, DrawPhase::back)) {\n\t\t\tDrawBackground(surface, model, vsDraw, ll,\n\t\t\t\txStart, rcLine, subLine, lineRange, posLineStart,\n\t\t\t\tbackground);\n\t\t\tDrawFoldDisplayText(surface, model, vsDraw, ll, line, xStart, rcLine, subLine, subLineStart, DrawPhase::back);\n\t\t\tDrawEOLAnnotationText(surface, model, vsDraw, ll, line, xStart, rcLine, subLine, subLineStart, DrawPhase::back);\n\t\t\t// Remove drawBack to not draw again in DrawFoldDisplayText\n\t\t\tphase = static_cast<DrawPhase>(static_cast<int>(phase) & ~static_cast<int>(DrawPhase::back));\n\t\t\tDrawEOL(surface, model, vsDraw, ll,\n\t\t\t\tline, xStart, rcLine, subLine, lineRange.end, subLineStart, background);\n\t\t\tif (vsDraw.IsLineFrameOpaque(model.caret.active, ll->containsCaret))\n\t\t\t\tDrawCaretLineFramed(surface, vsDraw, ll, rcLine, subLine);\n\t\t}\n\n\t\tif (FlagSet(phase, DrawPhase::indicatorsBack)) {\n\t\t\tDrawIndicators(surface, model, vsDraw, ll, line, xStart, rcLine, subLine,\n\t\t\t\tlineRangeIncludingEnd.end, true, tabWidthMinimumPixels);\n\t\t\tDrawEdgeLine(surface, vsDraw, ll, xStart, rcLine, lineRange);\n\t\t\tDrawMarkUnderline(surface, model, vsDraw, line, rcLine);\n\t\t}\n\t}\n\n\tif (FlagSet(phase, DrawPhase::text)) {\n\t\tif (vsDraw.selection.visible) {\n\t\t\tDrawTranslucentSelection(surface, model, vsDraw, ll,\n\t\t\t\tline, xStart, rcLine, subLine, lineRange, tabWidthMinimumPixels, Layer::UnderText);\n\t\t}\n\t\tDrawTranslucentLineState(surface, model, vsDraw, ll, line, rcLine, subLine, Layer::UnderText);\n\t\tDrawForeground(surface, model, vsDraw, ll,\n\t\t\txStart, rcLine, subLine, lineVisible, lineRange, posLineStart,\n\t\t\tbackground);\n\t}\n\n\tif (FlagSet(phase, DrawPhase::indentationGuides)) {\n\t\tDrawIndentGuidesOverEmpty(surface, model, vsDraw, ll, line, xStart, rcLine, subLine, lineVisible);\n\t}\n\n\tif (FlagSet(phase, DrawPhase::indicatorsFore)) {\n\t\tDrawIndicators(surface, model, vsDraw, ll, line, xStart, rcLine, subLine,\n\t\t\tlineRangeIncludingEnd.end, false, tabWidthMinimumPixels);\n\t}\n\n\tDrawFoldDisplayText(surface, model, vsDraw, ll, line, xStart, rcLine, subLine, subLineStart, phase);\n\tDrawEOLAnnotationText(surface, model, vsDraw, ll, line, xStart, rcLine, subLine, subLineStart, phase);\n\n\tif (phasesDraw == PhasesDraw::One) {\n\t\tDrawEOL(surface, model, vsDraw, ll,\n\t\t\tline, xStart, rcLine, subLine, lineRange.end, subLineStart, background);\n\t\tif (vsDraw.IsLineFrameOpaque(model.caret.active, ll->containsCaret))\n\t\t\tDrawCaretLineFramed(surface, vsDraw, ll, rcLine, subLine);\n\t\tDrawEdgeLine(surface, vsDraw, ll, xStart, rcLine, lineRange);\n\t\tDrawMarkUnderline(surface, model, vsDraw, line, rcLine);\n\t}\n\n\tif (vsDraw.selection.visible && FlagSet(phase, DrawPhase::selectionTranslucent)) {\n\t\tDrawTranslucentSelection(surface, model, vsDraw, ll,\n\t\t\tline, xStart, rcLine, subLine, lineRange, tabWidthMinimumPixels, Layer::OverText);\n\t}\n\n\tif (FlagSet(phase, DrawPhase::lineTranslucent)) {\n\t\tDrawTranslucentLineState(surface, model, vsDraw, ll, line, rcLine, subLine, Layer::OverText);\n\t}\n\n\tif (clipLine) {\n\t\tsurface->PopClip();\n\t}\n}\n\nvoid EditView::PaintText(Surface *surfaceWindow, const EditModel &model, const ViewStyle &vsDraw,\n\tPRectangle rcArea, PRectangle rcClient) {\n\t// Allow text at start of line to overlap 1 pixel into the margin as this displays\n\t// serifs and italic stems for aliased text.\n\tconst int leftTextOverlap = ((model.xOffset == 0) && (vsDraw.leftMarginWidth > 0)) ? 1 : 0;\n\n\t// Do the painting\n\tif (rcArea.right > vsDraw.textStart - leftTextOverlap) {\n\n\t\tSurface *surface = surfaceWindow;\n\t\tif (bufferedDraw) {\n\t\t\tsurface = pixmapLine.get();\n\t\t\tPLATFORM_ASSERT(pixmapLine->Initialised());\n\t\t}\n\t\tsurface->SetMode(model.CurrentSurfaceMode());\n\n\t\tconst Point ptOrigin = model.GetVisibleOriginInMain();\n\n\t\tconst int screenLinePaintFirst = static_cast<int>(rcArea.top) / vsDraw.lineHeight;\n\t\tconst int xStart = vsDraw.textStart - model.xOffset + static_cast<int>(ptOrigin.x);\n\n\t\tconst SelectionPosition posCaret = model.posDrag.IsValid() ? model.posDrag : model.sel.RangeMain().caret;\n\t\tconst Sci::Line lineCaret = model.pdoc->SciLineFromPosition(posCaret.Position());\n\t\tconst int caretOffset = static_cast<int>(posCaret.Position() - model.pdoc->LineStart(lineCaret));\n\n\t\tPRectangle rcTextArea = rcClient;\n\t\tif (vsDraw.marginInside) {\n\t\t\trcTextArea.left += vsDraw.textStart;\n\t\t\trcTextArea.right -= vsDraw.rightMarginWidth;\n\t\t} else {\n\t\t\trcTextArea = rcArea;\n\t\t}\n\n\t\t// Remove selection margin from drawing area so text will not be drawn\n\t\t// on it in unbuffered mode.\n\t\tconst bool clipping = !bufferedDraw && vsDraw.marginInside;\n\t\tif (clipping) {\n\t\t\tPRectangle rcClipText = rcTextArea;\n\t\t\trcClipText.left -= leftTextOverlap;\n\t\t\tsurfaceWindow->SetClip(rcClipText);\n\t\t}\n\n\t\t// Loop on visible lines\n#if defined(TIME_PAINTING)\n\t\tdouble durLayout = 0.0;\n\t\tdouble durPaint = 0.0;\n\t\tdouble durCopy = 0.0;\n\t\tElapsedPeriod epWhole;\n#endif\n\t\tconst bool bracesIgnoreStyle = ((vsDraw.braceHighlightIndicatorSet && (model.bracesMatchStyle == StyleBraceLight)) ||\n\t\t\t(vsDraw.braceBadLightIndicatorSet && (model.bracesMatchStyle == StyleBraceBad)));\n\n\t\tSci::Line lineDocPrevious = -1;\t// Used to avoid laying out one document line multiple times\n\t\tstd::shared_ptr<LineLayout> ll;\n\t\tDrawPhase phase = DrawPhase::all;\n\t\tif ((phasesDraw == PhasesDraw::Multiple) && !bufferedDraw) {\n\t\t\tphase = DrawPhase::back;\n\t\t}\n\t\tfor (;;) {\n\t\t\tint yposScreen = screenLinePaintFirst * vsDraw.lineHeight;\n\t\t\tint ypos = bufferedDraw ? 0 : yposScreen;\n\t\t\tSci::Line visibleLine = model.TopLineOfMain() + screenLinePaintFirst;\n\t\t\twhile (visibleLine < model.pcs->LinesDisplayed() && yposScreen < rcArea.bottom) {\n\n\t\t\t\tconst Sci::Line lineDoc = model.pcs->DocFromDisplay(visibleLine);\n\t\t\t\t// Only visible lines should be handled by the code within the loop\n\t\t\t\tPLATFORM_ASSERT(model.pcs->GetVisible(lineDoc));\n\t\t\t\tconst Sci::Line lineStartSet = model.pcs->DisplayFromDoc(lineDoc);\n\t\t\t\tconst int subLine = static_cast<int>(visibleLine - lineStartSet);\n\n\t\t\t\t// Copy this line and its styles from the document into local arrays\n\t\t\t\t// and determine the x position at which each character starts.\n#if defined(TIME_PAINTING)\n\t\t\t\tElapsedPeriod ep;\n#endif\n\t\t\t\tif (lineDoc != lineDocPrevious) {\n\t\t\t\t\tll = RetrieveLineLayout(lineDoc, model);\n\t\t\t\t\tLayoutLine(model, surface, vsDraw, ll.get(), model.wrapWidth);\n\t\t\t\t\tlineDocPrevious = lineDoc;\n\t\t\t\t\tif (ll && model.BidirectionalEnabled()) {\n\t\t\t\t\t\t// Fill the line bidi data\n\t\t\t\t\t\tUpdateBidiData(model, vsDraw, ll.get());\n\t\t\t\t\t}\n\t\t\t\t}\n#if defined(TIME_PAINTING)\n\t\t\t\tdurLayout += ep.Duration(true);\n#endif\n\t\t\t\tif (ll) {\n\t\t\t\t\tll->containsCaret = vsDraw.selection.visible && (lineDoc == lineCaret)\n\t\t\t\t\t\t&& (ll->lines == 1 || !vsDraw.caretLine.subLine || ll->InLine(caretOffset, subLine));\n\n\t\t\t\t\tPRectangle rcLine = rcTextArea;\n\t\t\t\t\trcLine.top = static_cast<XYPOSITION>(ypos);\n\t\t\t\t\trcLine.bottom = static_cast<XYPOSITION>(ypos + vsDraw.lineHeight);\n\n\t\t\t\t\tconst Range rangeLine(model.pdoc->LineStart(lineDoc),\n\t\t\t\t\t\tmodel.pdoc->LineStart(lineDoc + 1));\n\n\t\t\t\t\t// Highlight the current braces if any\n\t\t\t\t\tll->SetBracesHighlight(rangeLine, model.braces, static_cast<char>(model.bracesMatchStyle),\n\t\t\t\t\t\tstatic_cast<int>(model.highlightGuideColumn * vsDraw.spaceWidth), bracesIgnoreStyle);\n\n\t\t\t\t\tif (leftTextOverlap && (bufferedDraw || ((phasesDraw < PhasesDraw::Multiple) && (FlagSet(phase, DrawPhase::back))))) {\n\t\t\t\t\t\t// Clear the left margin\n\t\t\t\t\t\tPRectangle rcSpacer = rcLine;\n\t\t\t\t\t\trcSpacer.right = rcSpacer.left;\n\t\t\t\t\t\trcSpacer.left -= 1;\n\t\t\t\t\t\tsurface->FillRectangleAligned(rcSpacer, Fill(vsDraw.styles[StyleDefault].back));\n\t\t\t\t\t}\n\n\t\t\t\t\tDrawLine(surface, model, vsDraw, ll.get(), lineDoc, visibleLine, xStart, rcLine, subLine, phase);\n#if defined(TIME_PAINTING)\n\t\t\t\t\tdurPaint += ep.Duration(true);\n#endif\n\t\t\t\t\t// Restore the previous styles for the brace highlights in case layout is in cache.\n\t\t\t\t\tll->RestoreBracesHighlight(rangeLine, model.braces, bracesIgnoreStyle);\n\n\t\t\t\t\tif (FlagSet(phase, DrawPhase::foldLines)) {\n\t\t\t\t\t\tDrawFoldLines(surface, model, vsDraw, ll.get(), lineDoc, rcLine, subLine);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (FlagSet(phase, DrawPhase::carets)) {\n\t\t\t\t\t\tDrawCarets(surface, model, vsDraw, ll.get(), lineDoc, xStart, rcLine, subLine);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (bufferedDraw) {\n\t\t\t\t\t\tconst Point from = Point::FromInts(vsDraw.textStart - leftTextOverlap, 0);\n\t\t\t\t\t\tconst PRectangle rcCopyArea = PRectangle::FromInts(vsDraw.textStart - leftTextOverlap, yposScreen,\n\t\t\t\t\t\t\tstatic_cast<int>(rcClient.right - vsDraw.rightMarginWidth),\n\t\t\t\t\t\t\typosScreen + vsDraw.lineHeight);\n\t\t\t\t\t\tpixmapLine->FlushDrawing();\n\t\t\t\t\t\tsurfaceWindow->Copy(rcCopyArea, from, *pixmapLine);\n\t\t\t\t\t}\n\n\t\t\t\t\tlineWidthMaxSeen = std::max(\n\t\t\t\t\t\tlineWidthMaxSeen, static_cast<int>(ll->positions[ll->numCharsInLine]));\n#if defined(TIME_PAINTING)\n\t\t\t\t\tdurCopy += ep.Duration(true);\n#endif\n\t\t\t\t}\n\n\t\t\t\tif (!bufferedDraw) {\n\t\t\t\t\typos += vsDraw.lineHeight;\n\t\t\t\t}\n\n\t\t\t\typosScreen += vsDraw.lineHeight;\n\t\t\t\tvisibleLine++;\n\t\t\t}\n\n\t\t\tif (phase >= DrawPhase::carets) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tphase = static_cast<DrawPhase>(static_cast<int>(phase) * 2);\n\t\t}\n\t\tll.reset();\n#if defined(TIME_PAINTING)\n\t\tif (durPaint < 0.00000001)\n\t\t\tdurPaint = 0.00000001;\n#endif\n\t\t// Right column limit indicator\n\t\tPRectangle rcBeyondEOF = (vsDraw.marginInside) ? rcClient : rcArea;\n\t\trcBeyondEOF.left = static_cast<XYPOSITION>(vsDraw.textStart);\n\t\trcBeyondEOF.right = rcBeyondEOF.right - ((vsDraw.marginInside) ? vsDraw.rightMarginWidth : 0);\n\t\trcBeyondEOF.top = static_cast<XYPOSITION>((model.pcs->LinesDisplayed() - model.TopLineOfMain()) * vsDraw.lineHeight);\n\t\tif (rcBeyondEOF.top < rcBeyondEOF.bottom) {\n\t\t\tsurfaceWindow->FillRectangleAligned(rcBeyondEOF, Fill(vsDraw.styles[StyleDefault].back));\n\t\t\tif (vsDraw.edgeState == EdgeVisualStyle::Line) {\n\t\t\t\tconst int edgeX = static_cast<int>(vsDraw.theEdge.column * vsDraw.spaceWidth);\n\t\t\t\trcBeyondEOF.left = static_cast<XYPOSITION>(edgeX + xStart);\n\t\t\t\trcBeyondEOF.right = rcBeyondEOF.left + 1;\n\t\t\t\tsurfaceWindow->FillRectangleAligned(rcBeyondEOF, Fill(vsDraw.theEdge.colour));\n\t\t\t} else if (vsDraw.edgeState == EdgeVisualStyle::MultiLine) {\n\t\t\t\tfor (size_t edge = 0; edge < vsDraw.theMultiEdge.size(); edge++) {\n\t\t\t\t\tif (vsDraw.theMultiEdge[edge].column >= 0) {\n\t\t\t\t\t\tconst int edgeX = static_cast<int>(vsDraw.theMultiEdge[edge].column * vsDraw.spaceWidth);\n\t\t\t\t\t\trcBeyondEOF.left = static_cast<XYPOSITION>(edgeX + xStart);\n\t\t\t\t\t\trcBeyondEOF.right = rcBeyondEOF.left + 1;\n\t\t\t\t\t\tsurfaceWindow->FillRectangleAligned(rcBeyondEOF, Fill(vsDraw.theMultiEdge[edge].colour));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (clipping)\n\t\t\tsurfaceWindow->PopClip();\n\n\t\t//Platform::DebugPrintf(\"start display %d, offset = %d\\n\", model.pdoc->Length(), model.xOffset);\n#if defined(TIME_PAINTING)\n\t\tPlatform::DebugPrintf(\n\t\t\"Layout:%9.6g    Paint:%9.6g    Ratio:%9.6g   Copy:%9.6g   Total:%9.6g\\n\",\n\t\tdurLayout, durPaint, durLayout / durPaint, durCopy, epWhole.Duration());\n#endif\n\t}\n}\n\n// Space (3 space characters) between line numbers and text when printing.\n#define lineNumberPrintSpace \"   \"\n\nSci::Position EditView::FormatRange(bool draw, CharacterRangeFull chrg, Rectangle rc, Surface *surface, Surface *surfaceMeasure,\n\tconst EditModel &model, const ViewStyle &vs) {\n\t// Can't use measurements cached for screen\n\tposCache->Clear();\n\n\tViewStyle vsPrint(vs);\n\tvsPrint.technology = Technology::Default;\n\n\t// Modify the view style for printing as do not normally want any of the transient features to be printed\n\t// Printing supports only the line number margin.\n\tint lineNumberIndex = -1;\n\tfor (size_t margin = 0; margin < vs.ms.size(); margin++) {\n\t\tif ((vsPrint.ms[margin].style == MarginType::Number) && (vsPrint.ms[margin].width > 0)) {\n\t\t\tlineNumberIndex = static_cast<int>(margin);\n\t\t} else {\n\t\t\tvsPrint.ms[margin].width = 0;\n\t\t}\n\t}\n\tvsPrint.fixedColumnWidth = 0;\n\tvsPrint.zoomLevel = printParameters.magnification;\n\t// Don't show indentation guides\n\t// If this ever gets changed, cached pixmap would need to be recreated if technology != Technology::Default\n\tvsPrint.viewIndentationGuides = IndentView::None;\n\t// Don't show the selection when printing\n\tvsPrint.selection.visible = false;\n\tvsPrint.elementColours.clear();\n\tvsPrint.elementBaseColours.clear();\n\tvsPrint.caretLine.alwaysShow = false;\n\t// Don't highlight matching braces using indicators\n\tvsPrint.braceHighlightIndicatorSet = false;\n\tvsPrint.braceBadLightIndicatorSet = false;\n\n\t// Set colours for printing according to users settings\n\tconst PrintOption colourMode = printParameters.colourMode;\n\tconst std::vector<Style>::iterator endStyles = (colourMode == PrintOption::ColourOnWhiteDefaultBG) ?\n\t\tvsPrint.styles.begin() + StyleLineNumber : vsPrint.styles.end();\n\tfor (std::vector<Style>::iterator it = vsPrint.styles.begin(); it != endStyles; ++it) {\n\t\tif (colourMode == PrintOption::InvertLight) {\n\t\t\tit->fore = InvertedLight(it->fore);\n\t\t\tit->back = InvertedLight(it->back);\n\t\t} else if (colourMode == PrintOption::BlackOnWhite) {\n\t\t\tit->fore = black;\n\t\t\tit->back = white;\n\t\t} else if (colourMode == PrintOption::ColourOnWhite || colourMode == PrintOption::ColourOnWhiteDefaultBG) {\n\t\t\tit->back = white;\n\t\t}\n\t}\n\t// White background for the line numbers if PrintOption::ScreenColours isn't used\n\tif (colourMode != PrintOption::ScreenColours) {\n\t\tvsPrint.styles[StyleLineNumber].back = white;\n\t}\n\n\t// Printing uses different margins, so reset screen margins\n\tvsPrint.leftMarginWidth = 0;\n\tvsPrint.rightMarginWidth = 0;\n\n\tvsPrint.Refresh(*surfaceMeasure, model.pdoc->tabInChars);\n\t// Determining width must happen after fonts have been realised in Refresh\n\tint lineNumberWidth = 0;\n\tif (lineNumberIndex >= 0) {\n\t\tlineNumberWidth = static_cast<int>(surfaceMeasure->WidthText(vsPrint.styles[StyleLineNumber].font.get(),\n\t\t\t\"99999\" lineNumberPrintSpace));\n\t\tvsPrint.ms[lineNumberIndex].width = lineNumberWidth;\n\t\tvsPrint.Refresh(*surfaceMeasure, model.pdoc->tabInChars);\t// Recalculate fixedColumnWidth\n\t}\n\n\t// Turn off change history marker backgrounds\n\tconstexpr unsigned int changeMarkers =\n\t\t1u << static_cast<unsigned int>(MarkerOutline::HistoryRevertedToOrigin) |\n\t\t1u << static_cast<unsigned int>(MarkerOutline::HistorySaved) |\n\t\t1u << static_cast<unsigned int>(MarkerOutline::HistoryModified) |\n\t\t1u << static_cast<unsigned int>(MarkerOutline::HistoryRevertedToModified);\n\tvsPrint.maskInLine &= ~changeMarkers;\n\n\tconst Sci::Line linePrintStart = model.pdoc->SciLineFromPosition(chrg.cpMin);\n\tSci::Line linePrintLast = linePrintStart + (rc.bottom - rc.top) / vsPrint.lineHeight - 1;\n\tif (linePrintLast < linePrintStart)\n\t\tlinePrintLast = linePrintStart;\n\tconst Sci::Line linePrintMax = model.pdoc->SciLineFromPosition(chrg.cpMax);\n\tif (linePrintLast > linePrintMax)\n\t\tlinePrintLast = linePrintMax;\n\t//Platform::DebugPrintf(\"Formatting lines=[%0d,%0d,%0d] top=%0d bottom=%0d line=%0d %0d\\n\",\n\t//      linePrintStart, linePrintLast, linePrintMax, rc.top, rc.bottom, vsPrint.lineHeight,\n\t//      surfaceMeasure->Height(vsPrint.styles[StyleLineNumber].font));\n\tSci::Position endPosPrint = model.pdoc->Length();\n\tif (linePrintLast < model.pdoc->LinesTotal())\n\t\tendPosPrint = model.pdoc->LineStart(linePrintLast + 1);\n\n\t// Ensure we are styled to where we are formatting.\n\tmodel.pdoc->EnsureStyledTo(endPosPrint);\n\n\tconst int xStart = vsPrint.fixedColumnWidth + rc.left;\n\tint ypos = rc.top;\n\n\tSci::Line lineDoc = linePrintStart;\n\n\tSci::Position nPrintPos = chrg.cpMin;\n\tint visibleLine = 0;\n\tint widthPrint = rc.right - rc.left - vsPrint.fixedColumnWidth;\n\tif (printParameters.wrapState == Wrap::None)\n\t\twidthPrint = LineLayout::wrapWidthInfinite;\n\n\twhile (lineDoc <= linePrintLast && ypos < rc.bottom) {\n\n\t\t// When printing, the hdc and hdcTarget may be the same, so\n\t\t// changing the state of surfaceMeasure may change the underlying\n\t\t// state of surface. Therefore, any cached state is discarded before\n\t\t// using each surface.\n\t\tsurfaceMeasure->FlushCachedState();\n\n\t\t// Copy this line and its styles from the document into local arrays\n\t\t// and determine the x position at which each character starts.\n\t\tLineLayout ll(lineDoc, static_cast<int>(model.pdoc->LineStart(lineDoc + 1) - model.pdoc->LineStart(lineDoc) + 1));\n\t\tLayoutLine(model, surfaceMeasure, vsPrint, &ll, widthPrint);\n\n\t\tll.containsCaret = false;\n\n\t\tPRectangle rcLine = PRectangle::FromInts(\n\t\t\trc.left,\n\t\t\typos,\n\t\t\trc.right - 1,\n\t\t\typos + vsPrint.lineHeight);\n\n\t\t// When document line is wrapped over multiple display lines, find where\n\t\t// to start printing from to ensure a particular position is on the first\n\t\t// line of the page.\n\t\tif (visibleLine == 0) {\n\t\t\tconst Sci::Position startWithinLine = nPrintPos -\n\t\t\t\tmodel.pdoc->LineStart(lineDoc);\n\t\t\tfor (int iwl = 0; iwl < ll.lines - 1; iwl++) {\n\t\t\t\tif (ll.LineStart(iwl) <= startWithinLine && ll.LineStart(iwl + 1) >= startWithinLine) {\n\t\t\t\t\tvisibleLine = -iwl;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (ll.lines > 1 && startWithinLine >= ll.LineStart(ll.lines - 1)) {\n\t\t\t\tvisibleLine = -(ll.lines - 1);\n\t\t\t}\n\t\t}\n\n\t\tif (draw && lineNumberWidth &&\n\t\t\t(ypos + vsPrint.lineHeight <= rc.bottom) &&\n\t\t\t(visibleLine >= 0)) {\n\t\t\tconst std::string number = std::to_string(lineDoc + 1) + lineNumberPrintSpace;\n\t\t\tPRectangle rcNumber = rcLine;\n\t\t\trcNumber.right = rcNumber.left + lineNumberWidth;\n\t\t\t// Right justify\n\t\t\trcNumber.left = rcNumber.right - surfaceMeasure->WidthText(\n\t\t\t\tvsPrint.styles[StyleLineNumber].font.get(), number);\n\t\t\tsurface->FlushCachedState();\n\t\t\tsurface->DrawTextNoClip(rcNumber, vsPrint.styles[StyleLineNumber].font.get(),\n\t\t\t\typos + vsPrint.maxAscent, number,\n\t\t\t\tvsPrint.styles[StyleLineNumber].fore,\n\t\t\t\tvsPrint.styles[StyleLineNumber].back);\n\t\t}\n\n\t\t// Draw the line\n\t\tsurface->FlushCachedState();\n\n\t\tfor (int iwl = 0; iwl < ll.lines; iwl++) {\n\t\t\tif (ypos + vsPrint.lineHeight <= rc.bottom) {\n\t\t\t\tif (visibleLine >= 0) {\n\t\t\t\t\tif (draw) {\n\t\t\t\t\t\trcLine.top = static_cast<XYPOSITION>(ypos);\n\t\t\t\t\t\trcLine.bottom = static_cast<XYPOSITION>(ypos + vsPrint.lineHeight);\n\t\t\t\t\t\tDrawLine(surface, model, vsPrint, &ll, lineDoc, visibleLine, xStart, rcLine, iwl, DrawPhase::all);\n\t\t\t\t\t}\n\t\t\t\t\typos += vsPrint.lineHeight;\n\t\t\t\t}\n\t\t\t\tvisibleLine++;\n\t\t\t\tif (iwl == ll.lines - 1)\n\t\t\t\t\tnPrintPos = model.pdoc->LineStart(lineDoc + 1);\n\t\t\t\telse\n\t\t\t\t\tnPrintPos += ll.LineStart(iwl + 1) - ll.LineStart(iwl);\n\t\t\t}\n\t\t}\n\n\t\t++lineDoc;\n\t}\n\n\t// Clear cache so measurements are not used for screen\n\tposCache->Clear();\n\n\treturn nPrintPos;\n}\n"
  },
  {
    "path": "Libraries/scintilla/src/EditView.h",
    "content": "// Scintilla source code edit control\n/** @file EditView.h\n ** Defines the appearance of the main text area of the editor window.\n **/\n// Copyright 1998-2014 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#ifndef EDITVIEW_H\n#define EDITVIEW_H\n\nnamespace Scintilla::Internal {\n\nstruct PrintParameters {\n\tint magnification;\n\tScintilla::PrintOption colourMode;\n\tScintilla::Wrap wrapState;\n\tPrintParameters() noexcept;\n};\n\n/**\n* The view may be drawn in separate phases.\n*/\nenum class DrawPhase {\n\tnone = 0x0,\n\tback = 0x1,\n\tindicatorsBack = 0x2,\n\ttext = 0x4,\n\tindentationGuides = 0x8,\n\tindicatorsFore = 0x10,\n\tselectionTranslucent = 0x20,\n\tlineTranslucent = 0x40,\n\tfoldLines = 0x80,\n\tcarets = 0x100,\n\tall = 0x1FF\n};\n\nbool ValidStyledText(const ViewStyle &vs, size_t styleOffset, const StyledText &st) noexcept;\nint WidestLineWidth(Surface *surface, const ViewStyle &vs, int styleOffset, const StyledText &st);\nvoid DrawTextNoClipPhase(Surface *surface, PRectangle rc, const Style &style, XYPOSITION ybase,\n\tstd::string_view text, DrawPhase phase);\nvoid DrawStyledText(Surface *surface, const ViewStyle &vs, int styleOffset, PRectangle rcText,\n\tconst StyledText &st, size_t start, size_t length, DrawPhase phase);\n\ntypedef void (*DrawTabArrowFn)(Surface *surface, PRectangle rcTab, int ymid,\n\tconst ViewStyle &vsDraw, Stroke stroke);\n\nclass LineTabstops;\n\n/**\n* EditView draws the main text area.\n*/\nclass EditView {\npublic:\n\tPrintParameters printParameters;\n\tstd::unique_ptr<LineTabstops> ldTabstops;\n\tint tabWidthMinimumPixels;\n\n\tbool drawOverstrikeCaret; // used by the curses platform\n\n\t/** In bufferedDraw mode, graphics operations are drawn to a pixmap and then copied to\n\t* the screen. This avoids flashing but is about 30% slower. */\n\tbool bufferedDraw;\n\t/** In phasesTwo mode, drawing is performed in two phases, first the background\n\t* and then the foreground. This avoids chopping off characters that overlap the next run.\n\t* In multiPhaseDraw mode, drawing is performed in multiple phases with each phase drawing\n\t* one feature over the whole drawing area, instead of within one line. This allows text to\n\t* overlap from one line to the next. */\n\tScintilla::PhasesDraw phasesDraw;\n\n\tint lineWidthMaxSeen;\n\n\tbool additionalCaretsBlink;\n\tbool additionalCaretsVisible;\n\n\tbool imeCaretBlockOverride;\n\n\tstd::unique_ptr<Surface> pixmapLine;\n\tstd::unique_ptr<Surface> pixmapIndentGuide;\n\tstd::unique_ptr<Surface> pixmapIndentGuideHighlight;\n\n\tLineLayoutCache llc;\n\tstd::unique_ptr<IPositionCache> posCache;\n\n\tunsigned int maxLayoutThreads;\n\tstatic constexpr int bytesPerLayoutThread = 1000;\n\n\tint tabArrowHeight; // draw arrow heads this many pixels above/below line midpoint\n\t/** Some platforms, notably PLAT_CURSES, do not support Scintilla's native\n\t * DrawTabArrow function for drawing tab characters. Allow those platforms to\n\t * override it instead of creating a new method in the Surface class that\n\t * existing platforms must implement as empty. */\n\tDrawTabArrowFn customDrawTabArrow;\n\tDrawWrapMarkerFn customDrawWrapMarker;\n\n\tEditView();\n\t// Deleted so EditView objects can not be copied.\n\tEditView(const EditView &) = delete;\n\tEditView(EditView &&) = delete;\n\tvoid operator=(const EditView &) = delete;\n\tvoid operator=(EditView &&) = delete;\n\tvirtual ~EditView();\n\n\tbool SetTwoPhaseDraw(bool twoPhaseDraw) noexcept;\n\tbool SetPhasesDraw(int phases) noexcept;\n\tbool LinesOverlap() const noexcept;\n\n\tvoid SetLayoutThreads(unsigned int threads) noexcept;\n\tunsigned int GetLayoutThreads() const noexcept;\n\n\tvoid ClearAllTabstops() noexcept;\n\tXYPOSITION NextTabstopPos(Sci::Line line, XYPOSITION x, XYPOSITION tabWidth) const noexcept;\n\tbool ClearTabstops(Sci::Line line) noexcept;\n\tbool AddTabstop(Sci::Line line, int x);\n\tint GetNextTabstop(Sci::Line line, int x) const noexcept;\n\tvoid LinesAddedOrRemoved(Sci::Line lineOfPos, Sci::Line linesAdded);\n\n\tvoid DropGraphics() noexcept;\n\tvoid RefreshPixMaps(Surface *surfaceWindow, const ViewStyle &vsDraw);\n\n\tstd::shared_ptr<LineLayout> RetrieveLineLayout(Sci::Line lineNumber, const EditModel &model);\n\tvoid LayoutLine(const EditModel &model, Surface *surface, const ViewStyle &vstyle,\n\t\tLineLayout *ll, int width, bool callerMultiThreaded=false);\n\n\tstatic void UpdateBidiData(const EditModel &model, const ViewStyle &vstyle, LineLayout *ll);\n\n\tPoint LocationFromPosition(Surface *surface, const EditModel &model, SelectionPosition pos, Sci::Line topLine,\n\t\tconst ViewStyle &vs, PointEnd pe, const PRectangle rcClient);\n\tRange RangeDisplayLine(Surface *surface, const EditModel &model, Sci::Line lineVisible, const ViewStyle &vs);\n\tSelectionPosition SPositionFromLocation(Surface *surface, const EditModel &model, PointDocument pt, bool canReturnInvalid,\n\t\tbool charPosition, bool virtualSpace, const ViewStyle &vs, const PRectangle rcClient);\n\tSelectionPosition SPositionFromLineX(Surface *surface, const EditModel &model, Sci::Line lineDoc, int x, const ViewStyle &vs);\n\tSci::Line DisplayFromPosition(Surface *surface, const EditModel &model, Sci::Position pos, const ViewStyle &vs);\n\tSci::Position StartEndDisplayLine(Surface *surface, const EditModel &model, Sci::Position pos, bool start, const ViewStyle &vs);\n\nprivate:\n\tvoid DrawEOL(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,\n\t\tSci::Line line, int xStart, PRectangle rcLine, int subLine, Sci::Position lineEnd, XYPOSITION subLineStart, ColourOptional background);\n\tvoid DrawFoldDisplayText(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,\n\t\tSci::Line line, int xStart, PRectangle rcLine, int subLine, XYPOSITION subLineStart, DrawPhase phase);\n\tvoid DrawEOLAnnotationText(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,\n\t\tSci::Line line, int xStart, PRectangle rcLine, int subLine, XYPOSITION subLineStart, DrawPhase phase);\n\tvoid DrawAnnotation(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,\n\t\tSci::Line line, int xStart, PRectangle rcLine, int subLine, DrawPhase phase);\n\tvoid DrawCarets(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,\n\t\tSci::Line lineDoc, int xStart, PRectangle rcLine, int subLine) const;\n\tvoid DrawIndentGuide(Surface *surface, XYPOSITION start, PRectangle rcSegment, bool highlight, bool offset);\n\tvoid DrawForeground(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,\n\t\tint xStart, PRectangle rcLine, int subLine, Sci::Line lineVisible, Range lineRange, Sci::Position posLineStart,\n\t\tColourOptional background);\n\tvoid DrawIndentGuidesOverEmpty(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,\n\t\tSci::Line line, int xStart, PRectangle rcLine, int subLine, Sci::Line lineVisible);\n\tvoid DrawLine(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,\n\t\tSci::Line line, Sci::Line lineVisible, int xStart, PRectangle rcLine, int subLine, DrawPhase phase);\n\npublic:\n\tvoid PaintText(Surface *surfaceWindow, const EditModel &model, const ViewStyle &vsDraw,\n\t\tPRectangle rcArea, PRectangle rcClient);\n\tSci::Position FormatRange(bool draw, CharacterRangeFull chrg, Rectangle rc, Surface *surface, Surface *surfaceMeasure,\n\t\tconst EditModel &model, const ViewStyle &vs);\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/src/Editor.cxx",
    "content": "// Scintilla source code edit control\n/** @file Editor.cxx\n ** Main code for the edit control.\n **/\n// Copyright 1998-2011 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#include <cstddef>\n#include <cstdlib>\n#include <cstdint>\n#include <cassert>\n#include <cstring>\n#include <cstdio>\n#include <cmath>\n\n#include <stdexcept>\n#include <string>\n#include <string_view>\n#include <vector>\n#include <map>\n#include <set>\n#include <forward_list>\n#include <optional>\n#include <algorithm>\n#include <iterator>\n#include <memory>\n#include <chrono>\n#include <atomic>\n#include <mutex>\n#include <thread>\n#include <future>\n\n#include \"ScintillaTypes.h\"\n#include \"ScintillaMessages.h\"\n#include \"ScintillaStructures.h\"\n#include \"ILoader.h\"\n#include \"ILexer.h\"\n\n#include \"Debugging.h\"\n#include \"Geometry.h\"\n#include \"Platform.h\"\n\n#include \"CharacterType.h\"\n#include \"CharacterCategoryMap.h\"\n#include \"Position.h\"\n#include \"UniqueString.h\"\n#include \"SplitVector.h\"\n#include \"Partitioning.h\"\n#include \"RunStyles.h\"\n#include \"ContractionState.h\"\n#include \"CellBuffer.h\"\n#include \"PerLine.h\"\n#include \"KeyMap.h\"\n#include \"Indicator.h\"\n#include \"LineMarker.h\"\n#include \"Style.h\"\n#include \"ViewStyle.h\"\n#include \"CharClassify.h\"\n#include \"Decoration.h\"\n#include \"CaseFolder.h\"\n#include \"Document.h\"\n#include \"UniConversion.h\"\n#include \"DBCS.h\"\n#include \"Selection.h\"\n#include \"PositionCache.h\"\n#include \"EditModel.h\"\n#include \"MarginView.h\"\n#include \"EditView.h\"\n#include \"Editor.h\"\n#include \"ElapsedPeriod.h\"\n\nusing namespace Scintilla;\nusing namespace Scintilla::Internal;\n\nnamespace {\n\n/*\n\treturn whether this modification represents an operation that\n\tmay reasonably be deferred (not done now OR [possibly] at all)\n*/\nconstexpr bool CanDeferToLastStep(const DocModification &mh) noexcept {\n\tif (FlagSet(mh.modificationType, (ModificationFlags::BeforeInsert | ModificationFlags::BeforeDelete)))\n\t\treturn true;\t// CAN skip\n\tif (!FlagSet(mh.modificationType, (ModificationFlags::Undo | ModificationFlags::Redo)))\n\t\treturn false;\t// MUST do\n\tif (FlagSet(mh.modificationType, ModificationFlags::MultiStepUndoRedo))\n\t\treturn true;\t// CAN skip\n\treturn false;\t\t// PRESUMABLY must do\n}\n\nconstexpr bool CanEliminate(const DocModification &mh) noexcept {\n\treturn\n\t\tFlagSet(mh.modificationType, (ModificationFlags::BeforeInsert | ModificationFlags::BeforeDelete));\n}\n\n/*\n\treturn whether this modification represents the FINAL step\n\tin a [possibly lengthy] multi-step Undo/Redo sequence\n*/\nconstexpr bool IsLastStep(const DocModification &mh) noexcept {\n\tconstexpr ModificationFlags finalMask = ModificationFlags::MultiStepUndoRedo\n\t\t| ModificationFlags::LastStepInUndoRedo\n\t\t| ModificationFlags::MultilineUndoRedo;\n\treturn\n\t\tFlagSet(mh.modificationType, (ModificationFlags::Undo | ModificationFlags::Redo))\n\t    && ((mh.modificationType & finalMask) == finalMask);\n}\n\nconstexpr bool IsAllSpacesOrTabs(std::string_view sv) noexcept {\n\tfor (const char ch : sv) {\n\t\t// This is safe because IsSpaceOrTab() will return false for null terminators\n\t\tif (!IsSpaceOrTab(ch))\n\t\t\treturn false;\n\t}\n\treturn true;\n}\n\n}\n\nTimer::Timer() noexcept :\n\t\tticking(false), ticksToWait(0), tickerID{} {}\n\nIdler::Idler() noexcept :\n\t\tstate(false), idlerID(nullptr) {}\n\nEditor::Editor() : durationWrapOneByte(0.000001, 0.00000001, 0.00001) {\n\tctrlID = 0;\n\n\tstylesValid = false;\n\ttechnology = Technology::Default;\n\tscaleRGBAImage = 100.0f;\n\n\tcursorMode = CursorShape::Normal;\n\n\terrorStatus = Status::Ok;\n\tmouseDownCaptures = true;\n\tmouseWheelCaptures = true;\n\n\tlastClickTime = 0;\n\tdoubleClickCloseThreshold = Point(3, 3);\n\tdwellDelay = TimeForever;\n\tticksToDwell = TimeForever;\n\tdwelling = false;\n\tptMouseLast.x = 0;\n\tptMouseLast.y = 0;\n\tinDragDrop = DragDrop::none;\n\tdropWentOutside = false;\n\tposDrop = SelectionPosition(Sci::invalidPosition);\n\thotSpotClickPos = Sci::invalidPosition;\n\tselectionUnit = TextUnit::character;\n\n\tlastXChosen = 0;\n\tlineAnchorPos = 0;\n\toriginalAnchorPos = 0;\n\twordSelectAnchorStartPos = 0;\n\twordSelectAnchorEndPos = 0;\n\twordSelectInitialCaretPos = -1;\n\n\tcaretPolicies.x = { CaretPolicy::Slop | CaretPolicy::Even, 50 };\n\tcaretPolicies.y = { CaretPolicy::Even, 0 };\n\n\tvisiblePolicy = { 0, 0 };\n\n\tsearchAnchor = 0;\n\n\txCaretMargin = 50;\n\thorizontalScrollBarVisible = true;\n\tscrollWidth = 2000;\n\tverticalScrollBarVisible = true;\n\tendAtLastLine = true;\n\tcaretSticky = CaretSticky::Off;\n\tmarginOptions = MarginOption::None;\n\tmouseSelectionRectangularSwitch = false;\n\tmultipleSelection = false;\n\tadditionalSelectionTyping = false;\n\tmultiPasteMode = MultiPaste::Once;\n\tvirtualSpaceOptions = VirtualSpace::None;\n\n\ttargetRange = SelectionSegment();\n\tsearchFlags = FindOption::None;\n\n\ttopLine = 0;\n\tposTopLine = 0;\n\n\tlengthForEncode = -1;\n\n\tneedUpdateUI = Update::None;\n\tContainerNeedsUpdate(Update::Content);\n\n\tpaintState = PaintState::notPainting;\n\tpaintAbandonedByStyling = false;\n\tpaintingAllText = false;\n\twillRedrawAll = false;\n\tidleStyling = IdleStyling::None;\n\tneedIdleStyling = false;\n\n\tmodEventMask = ModificationFlags::EventMaskAll;\n\tcommandEvents = true;\n\n\tpdoc->AddWatcher(this, nullptr);\n\n\trecordingMacro = false;\n\tfoldAutomatic = AutomaticFold::None;\n\n\tinsideWrapScroll = false;\n\n\tconvertPastes = true;\n\n\tSetRepresentations();\n}\n\nEditor::~Editor() {\n\tpdoc->RemoveWatcher(this, nullptr);\n}\n\nvoid Editor::Finalise() {\n\tSetIdle(false);\n\tCancelModes();\n}\n\nvoid Editor::SetRepresentations() {\n\treprs->SetDefaultRepresentations(pdoc->dbcsCodePage);\n}\n\nvoid Editor::DropGraphics() noexcept {\n\tmarginView.DropGraphics();\n\tview.DropGraphics();\n}\n\nvoid Editor::InvalidateStyleData() noexcept {\n\tstylesValid = false;\n\tvs.technology = technology;\n\tDropGraphics();\n\tview.llc.Invalidate(LineLayout::ValidLevel::invalid);\n\tview.posCache->Clear();\n}\n\nvoid Editor::InvalidateStyleRedraw() {\n\tNeedWrapping();\n\tInvalidateStyleData();\n\tRedraw();\n}\n\nvoid Editor::RefreshStyleData() {\n\tif (!stylesValid) {\n\t\tstylesValid = true;\n\t\tAutoSurface surface(this);\n\t\tif (surface) {\n\t\t\tvs.Refresh(*surface, pdoc->tabInChars);\n\t\t}\n\t\tSetScrollBars();\n\t\tSetRectangularRange();\n\t}\n}\n\nbool Editor::HasMarginWindow() const noexcept {\n\treturn wMargin.Created();\n}\n\nPoint Editor::GetVisibleOriginInMain() const {\n\treturn Point(0, 0);\n}\n\nPointDocument Editor::DocumentPointFromView(Point ptView) const {\n\tPointDocument ptDocument(ptView);\n\tif (HasMarginWindow()) {\n\t\tconst Point ptOrigin = GetVisibleOriginInMain();\n\t\tptDocument.x += ptOrigin.x;\n\t\tptDocument.y += ptOrigin.y;\n\t} else {\n\t\tptDocument.x += xOffset;\n\t\tptDocument.y += static_cast<double>(topLine * vs.lineHeight);\n\t}\n\treturn ptDocument;\n}\n\nSci::Line Editor::TopLineOfMain() const noexcept {\n\tif (HasMarginWindow())\n\t\treturn 0;\n\treturn topLine;\n}\n\nPoint Editor::ClientSize() const {\n\tconst PRectangle rcClient = GetClientRectangle();\n\treturn Point(rcClient.Width(), rcClient.Height());\n}\n\nPRectangle Editor::GetClientRectangle() const {\n\treturn wMain.GetClientPosition();\n}\n\nPRectangle Editor::GetClientDrawingRectangle() {\n\treturn GetClientRectangle();\n}\n\nPRectangle Editor::GetTextRectangle() const {\n\tPRectangle rc = GetClientRectangle();\n\trc.left += vs.textStart;\n\trc.right -= vs.rightMarginWidth;\n\treturn rc;\n}\n\nSci::Line Editor::LinesOnScreen() const {\n\tconst Point sizeClient = ClientSize();\n\tconst int htClient = static_cast<int>(sizeClient.y);\n\t//Platform::DebugPrintf(\"lines on screen = %d\\n\", htClient / lineHeight + 1);\n\treturn htClient / vs.lineHeight;\n}\n\nSci::Line Editor::LinesToScroll() const {\n\tconst Sci::Line retVal = LinesOnScreen() - 1;\n\tif (retVal < 1)\n\t\treturn 1;\n\treturn retVal;\n}\n\nSci::Line Editor::MaxScrollPos() const {\n\t//Platform::DebugPrintf(\"Lines %d screen = %d maxScroll = %d\\n\",\n\t//LinesTotal(), LinesOnScreen(), LinesTotal() - LinesOnScreen() + 1);\n\tSci::Line retVal = pcs->LinesDisplayed();\n\tif (endAtLastLine) {\n\t\tretVal -= LinesOnScreen();\n\t} else {\n\t\tretVal--;\n\t}\n\tif (retVal < 0) {\n\t\treturn 0;\n\t}\n\treturn retVal;\n}\n\nSelectionPosition Editor::ClampPositionIntoDocument(SelectionPosition sp) const {\n\tif (sp.Position() < 0) {\n\t\treturn SelectionPosition(0);\n\t} else if (sp.Position() > pdoc->Length()) {\n\t\treturn SelectionPosition(pdoc->Length());\n\t} else {\n\t\t// If not at end of line then set offset to 0\n\t\tif (!pdoc->IsLineEndPosition(sp.Position()))\n\t\t\tsp.SetVirtualSpace(0);\n\t\treturn sp;\n\t}\n}\n\nPoint Editor::LocationFromPosition(SelectionPosition pos, PointEnd pe) {\n\tconst PRectangle rcClient = GetTextRectangle();\n\tRefreshStyleData();\n\tAutoSurface surface(this);\n\treturn view.LocationFromPosition(surface, *this, pos, topLine, vs, pe, rcClient);\n}\n\nPoint Editor::LocationFromPosition(Sci::Position pos, PointEnd pe) {\n\treturn LocationFromPosition(SelectionPosition(pos), pe);\n}\n\nint Editor::XFromPosition(SelectionPosition sp) {\n\tconst Point pt = LocationFromPosition(sp);\n\treturn static_cast<int>(pt.x) - vs.textStart + xOffset;\n}\n\nSelectionPosition Editor::SPositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition, bool virtualSpace) {\n\tRefreshStyleData();\n\tAutoSurface surface(this);\n\n\tPRectangle rcClient = GetTextRectangle();\n\t// May be in scroll view coordinates so translate back to main view\n\tconst Point ptOrigin = GetVisibleOriginInMain();\n\trcClient.Move(-ptOrigin.x, -ptOrigin.y);\n\n\tif (canReturnInvalid) {\n\t\tif (!rcClient.Contains(pt))\n\t\t\treturn SelectionPosition(Sci::invalidPosition);\n\t\tif (pt.x < vs.textStart)\n\t\t\treturn SelectionPosition(Sci::invalidPosition);\n\t\tif (pt.y < 0)\n\t\t\treturn SelectionPosition(Sci::invalidPosition);\n\t}\n\tconst PointDocument ptdoc = DocumentPointFromView(pt);\n\treturn view.SPositionFromLocation(surface, *this, ptdoc, canReturnInvalid,\n\t\tcharPosition, virtualSpace, vs, rcClient);\n}\n\nSci::Position Editor::PositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition) {\n\treturn SPositionFromLocation(pt, canReturnInvalid, charPosition, false).Position();\n}\n\n/**\n* Find the document position corresponding to an x coordinate on a particular document line.\n* Ensure is between whole characters when document is in multi-byte or UTF-8 mode.\n* This method is used for rectangular selections and does not work on wrapped lines.\n*/\nSelectionPosition Editor::SPositionFromLineX(Sci::Line lineDoc, int x) {\n\tRefreshStyleData();\n\tif (lineDoc >= pdoc->LinesTotal())\n\t\treturn SelectionPosition(pdoc->Length());\n\t//Platform::DebugPrintf(\"Position of (%d,%d) line = %d top=%d\\n\", pt.x, pt.y, line, topLine);\n\tAutoSurface surface(this);\n\treturn view.SPositionFromLineX(surface, *this, lineDoc, x, vs);\n}\n\nSci::Position Editor::PositionFromLineX(Sci::Line lineDoc, int x) {\n\treturn SPositionFromLineX(lineDoc, x).Position();\n}\n\nSci::Line Editor::LineFromLocation(Point pt) const noexcept {\n\treturn pcs->DocFromDisplay(static_cast<int>(pt.y) / vs.lineHeight + topLine);\n}\n\nvoid Editor::SetTopLine(Sci::Line topLineNew) {\n\tif ((topLine != topLineNew) && (topLineNew >= 0)) {\n\t\ttopLine = topLineNew;\n\t\tContainerNeedsUpdate(Update::VScroll);\n\t}\n\tposTopLine = pdoc->LineStart(pcs->DocFromDisplay(topLine));\n}\n\n/**\n * If painting then abandon the painting because a wider redraw is needed.\n * @return true if calling code should stop drawing.\n */\nbool Editor::AbandonPaint() {\n\tif ((paintState == PaintState::painting) && !paintingAllText) {\n\t\tpaintState = PaintState::abandoned;\n\t}\n\treturn paintState == PaintState::abandoned;\n}\n\nvoid Editor::RedrawRect(PRectangle rc) {\n\t//Platform::DebugPrintf(\"Redraw %0d,%0d - %0d,%0d\\n\", rc.left, rc.top, rc.right, rc.bottom);\n\n\t// Clip the redraw rectangle into the client area\n\tconst PRectangle rcClient = GetClientRectangle();\n\tif (rc.top < rcClient.top)\n\t\trc.top = rcClient.top;\n\tif (rc.bottom > rcClient.bottom)\n\t\trc.bottom = rcClient.bottom;\n\tif (rc.left < rcClient.left)\n\t\trc.left = rcClient.left;\n\tif (rc.right > rcClient.right)\n\t\trc.right = rcClient.right;\n\n\tif ((rc.bottom > rc.top) && (rc.right > rc.left)) {\n\t\twMain.InvalidateRectangle(rc);\n\t}\n}\n\nvoid Editor::DiscardOverdraw() {\n\t// Overridden on platforms that may draw outside visible area.\n}\n\nvoid Editor::Redraw() {\n\tif (redrawPendingText) {\n\t\treturn;\n\t}\n\t//Platform::DebugPrintf(\"Redraw all\\n\");\n\tconst PRectangle rcClient = GetClientRectangle();\n\twMain.InvalidateRectangle(rcClient);\n\tif (HasMarginWindow()) {\n\t\twMargin.InvalidateAll();\n\t} else if (paintState == PaintState::notPainting) {\n\t\tredrawPendingText = true;\n\t}\n}\n\nvoid Editor::RedrawSelMargin(Sci::Line line, bool allAfter) {\n\tconst bool markersInText = vs.maskInLine || vs.maskDrawInText;\n\tif (!HasMarginWindow() || markersInText) {\t// May affect text area so may need to abandon and retry\n\t\tif (AbandonPaint()) {\n\t\t\treturn;\n\t\t}\n\t}\n\tif (HasMarginWindow() && markersInText) {\n\t\tRedraw();\n\t\treturn;\n\t}\n\tif (redrawPendingMargin) {\n\t\treturn;\n\t}\n\tPRectangle rcMarkers = GetClientRectangle();\n\tif (!markersInText) {\n\t\t// Normal case: just draw the margin\n\t\trcMarkers.right = rcMarkers.left + vs.fixedColumnWidth;\n\t}\n\tconst PRectangle rcMarkersFull = rcMarkers;\n\tif (line != -1) {\n\t\tPRectangle rcLine = RectangleFromRange(Range(pdoc->LineStart(line)), 0);\n\n\t\t// Inflate line rectangle if there are image markers with height larger than line height\n\t\tif (vs.largestMarkerHeight > vs.lineHeight) {\n\t\t\tconst int delta = (vs.largestMarkerHeight - vs.lineHeight + 1) / 2;\n\t\t\trcLine.top -= delta;\n\t\t\trcLine.bottom += delta;\n\t\t\tif (rcLine.top < rcMarkers.top)\n\t\t\t\trcLine.top = rcMarkers.top;\n\t\t\tif (rcLine.bottom > rcMarkers.bottom)\n\t\t\t\trcLine.bottom = rcMarkers.bottom;\n\t\t}\n\n\t\trcMarkers.top = rcLine.top;\n\t\tif (!allAfter)\n\t\t\trcMarkers.bottom = rcLine.bottom;\n\t\tif (rcMarkers.Empty())\n\t\t\treturn;\n\t}\n\tif (HasMarginWindow()) {\n\t\tconst Point ptOrigin = GetVisibleOriginInMain();\n\t\trcMarkers.Move(-ptOrigin.x, -ptOrigin.y);\n\t\twMargin.InvalidateRectangle(rcMarkers);\n\t} else {\n\t\twMain.InvalidateRectangle(rcMarkers);\n\t\tif (rcMarkers == rcMarkersFull) {\n\t\t\tredrawPendingMargin = true;\n\t\t}\n\t}\n}\n\nPRectangle Editor::RectangleFromRange(Range r, int overlap) {\n\tconst Sci::Line docLineFirst = pdoc->SciLineFromPosition(r.First());\n\tconst Sci::Line minLine = pcs->DisplayFromDoc(docLineFirst);\n\tSci::Line docLineLast = docLineFirst;\t// Common case where range is wholly in one document line\n\tif (r.Last() >= pdoc->LineStart(docLineFirst + 1)) {\n\t\t// Range covers multiple lines so need last line\n\t\tdocLineLast = pdoc->SciLineFromPosition(r.Last());\n\t}\n\tconst Sci::Line maxLine = pcs->DisplayLastFromDoc(docLineLast);\n\tconst PRectangle rcClientDrawing = GetClientDrawingRectangle();\n\tPRectangle rc;\n\tconst int leftTextOverlap = ((xOffset == 0) && (vs.leftMarginWidth > 0)) ? 1 : 0;\n\trc.left = static_cast<XYPOSITION>(vs.textStart - leftTextOverlap);\n\trc.top = static_cast<XYPOSITION>((minLine - TopLineOfMain()) * vs.lineHeight - overlap);\n\tif (rc.top < rcClientDrawing.top)\n\t\trc.top = rcClientDrawing.top;\n\t// Extend to right of prepared area if any to prevent artifacts from caret line highlight\n\trc.right = rcClientDrawing.right;\n\trc.bottom = static_cast<XYPOSITION>((maxLine - TopLineOfMain() + 1) * vs.lineHeight + overlap);\n\n\treturn rc;\n}\n\nvoid Editor::InvalidateRange(Sci::Position start, Sci::Position end) {\n\tif (redrawPendingText) {\n\t\treturn;\n\t}\n\tRedrawRect(RectangleFromRange(Range(start, end), view.LinesOverlap() ? vs.lineOverlap : 0));\n}\n\nSci::Position Editor::CurrentPosition() const noexcept {\n\treturn sel.MainCaret();\n}\n\nbool Editor::SelectionEmpty() const noexcept {\n\treturn sel.Empty();\n}\n\nSelectionPosition Editor::SelectionStart() noexcept {\n\treturn sel.RangeMain().Start();\n}\n\nSelectionPosition Editor::SelectionEnd() noexcept {\n\treturn sel.RangeMain().End();\n}\n\nvoid Editor::SetRectangularRange() {\n\tif (sel.IsRectangular()) {\n\t\tconst int xAnchor = XFromPosition(sel.Rectangular().anchor);\n\t\tint xCaret = XFromPosition(sel.Rectangular().caret);\n\t\tif (sel.selType == Selection::SelTypes::thin) {\n\t\t\txCaret = xAnchor;\n\t\t}\n\t\tconst Sci::Line lineAnchorRect =\n\t\t\tpdoc->SciLineFromPosition(sel.Rectangular().anchor.Position());\n\t\tconst Sci::Line lineCaret =\n\t\t\tpdoc->SciLineFromPosition(sel.Rectangular().caret.Position());\n\t\tconst int increment = (lineCaret > lineAnchorRect) ? 1 : -1;\n\t\tAutoSurface surface(this);\n\t\tfor (Sci::Line line=lineAnchorRect; line != lineCaret+increment; line += increment) {\n\t\t\tSelectionRange range(\n\t\t\t\tview.SPositionFromLineX(surface, *this, line, xCaret, vs),\n\t\t\t\tview.SPositionFromLineX(surface, *this, line, xAnchor, vs));\n\t\t\tif (!FlagSet(virtualSpaceOptions, VirtualSpace::RectangularSelection))\n\t\t\t\trange.ClearVirtualSpace();\n\t\t\tif (line == lineAnchorRect)\n\t\t\t\tsel.SetSelection(range);\n\t\t\telse\n\t\t\t\tsel.AddSelectionWithoutTrim(range);\n\t\t}\n\t}\n}\n\nvoid Editor::ThinRectangularRange() {\n\tif (sel.IsRectangular()) {\n\t\tsel.selType = Selection::SelTypes::thin;\n\t\tif (sel.Rectangular().caret < sel.Rectangular().anchor) {\n\t\t\tsel.Rectangular() = SelectionRange(sel.Range(sel.Count()-1).caret, sel.Range(0).anchor);\n\t\t} else {\n\t\t\tsel.Rectangular() = SelectionRange(sel.Range(sel.Count()-1).anchor, sel.Range(0).caret);\n\t\t}\n\t\tSetRectangularRange();\n\t}\n}\n\nvoid Editor::InvalidateSelection(SelectionRange newMain, bool invalidateWholeSelection) {\n\tif (sel.Count() > 1 || !(sel.RangeMain().anchor == newMain.anchor) || sel.IsRectangular()) {\n\t\tinvalidateWholeSelection = true;\n\t}\n\tSci::Position firstAffected = std::min(sel.RangeMain().Start().Position(), newMain.Start().Position());\n\t// +1 for lastAffected ensures caret repainted\n\tSci::Position lastAffected = std::max(newMain.caret.Position()+1, newMain.anchor.Position());\n\tlastAffected = std::max(lastAffected, sel.RangeMain().End().Position());\n\tif (invalidateWholeSelection) {\n\t\tfor (size_t r=0; r<sel.Count(); r++) {\n\t\t\tfirstAffected = std::min(firstAffected, sel.Range(r).caret.Position());\n\t\t\tfirstAffected = std::min(firstAffected, sel.Range(r).anchor.Position());\n\t\t\tlastAffected = std::max(lastAffected, sel.Range(r).caret.Position()+1);\n\t\t\tlastAffected = std::max(lastAffected, sel.Range(r).anchor.Position());\n\t\t}\n\t}\n\tContainerNeedsUpdate(Update::Selection);\n\tInvalidateRange(firstAffected, lastAffected);\n}\n\nvoid Editor::InvalidateWholeSelection() {\n\tInvalidateSelection(sel.RangeMain(), true);\n}\n\n/* For Line selection - the anchor and caret are always\n   at the beginning and end of the region lines. */\nSelectionRange Editor::LineSelectionRange(SelectionPosition currentPos_, SelectionPosition anchor_) const noexcept {\n\tif (currentPos_ > anchor_) {\n\t\tanchor_ = SelectionPosition(pdoc->LineStartPosition(anchor_.Position()));\n\t\tcurrentPos_ = SelectionPosition(pdoc->LineEndPosition(currentPos_.Position()));\n\t} else {\n\t\tcurrentPos_ = SelectionPosition(pdoc->LineStartPosition(currentPos_.Position()));\n\t\tanchor_ = SelectionPosition(pdoc->LineEndPosition(anchor_.Position()));\n\t}\n\treturn SelectionRange(currentPos_, anchor_);\n}\n\nvoid Editor::SetSelection(SelectionPosition currentPos_, SelectionPosition anchor_) {\n\tcurrentPos_ = ClampPositionIntoDocument(currentPos_);\n\tanchor_ = ClampPositionIntoDocument(anchor_);\n\tconst Sci::Line currentLine = pdoc->SciLineFromPosition(currentPos_.Position());\n\tSelectionRange rangeNew(currentPos_, anchor_);\n\tif (sel.selType == Selection::SelTypes::lines) {\n\t\trangeNew = LineSelectionRange(currentPos_, anchor_);\n\t}\n\tif (sel.Count() > 1 || !(sel.RangeMain() == rangeNew)) {\n\t\tInvalidateSelection(rangeNew);\n\t}\n\tsel.RangeMain() = rangeNew;\n\tSetRectangularRange();\n\tClaimSelection();\n\tSetHoverIndicatorPosition(sel.MainCaret());\n\n\tif (marginView.highlightDelimiter.NeedsDrawing(currentLine)) {\n\t\tRedrawSelMargin();\n\t}\n\tQueueIdleWork(WorkItems::updateUI);\n}\n\nvoid Editor::SetSelection(Sci::Position currentPos_, Sci::Position anchor_) {\n\tSetSelection(SelectionPosition(currentPos_), SelectionPosition(anchor_));\n}\n\n// Just move the caret on the main selection\nvoid Editor::SetSelection(SelectionPosition currentPos_) {\n\tcurrentPos_ = ClampPositionIntoDocument(currentPos_);\n\tconst Sci::Line currentLine = pdoc->SciLineFromPosition(currentPos_.Position());\n\tif (sel.Count() > 1 || !(sel.RangeMain().caret == currentPos_)) {\n\t\tInvalidateSelection(SelectionRange(currentPos_));\n\t}\n\tif (sel.IsRectangular()) {\n\t\tsel.Rectangular() =\n\t\t\tSelectionRange(SelectionPosition(currentPos_), sel.Rectangular().anchor);\n\t\tSetRectangularRange();\n\t} else if (sel.selType == Selection::SelTypes::lines) {\n\t\tsel.RangeMain() = LineSelectionRange(currentPos_, sel.RangeMain().anchor);\n\t} else {\n\t\tsel.RangeMain() =\n\t\t\tSelectionRange(SelectionPosition(currentPos_), sel.RangeMain().anchor);\n\t}\n\tClaimSelection();\n\tSetHoverIndicatorPosition(sel.MainCaret());\n\n\tif (marginView.highlightDelimiter.NeedsDrawing(currentLine)) {\n\t\tRedrawSelMargin();\n\t}\n\tQueueIdleWork(WorkItems::updateUI);\n}\n\nvoid Editor::SetEmptySelection(SelectionPosition currentPos_) {\n\tconst Sci::Line currentLine = pdoc->SciLineFromPosition(currentPos_.Position());\n\tSelectionRange rangeNew(ClampPositionIntoDocument(currentPos_));\n\tif (sel.Count() > 1 || !(sel.RangeMain() == rangeNew)) {\n\t\tInvalidateSelection(rangeNew);\n\t}\n\tsel.Clear();\n\tsel.RangeMain() = rangeNew;\n\tSetRectangularRange();\n\tClaimSelection();\n\tSetHoverIndicatorPosition(sel.MainCaret());\n\n\tif (marginView.highlightDelimiter.NeedsDrawing(currentLine)) {\n\t\tRedrawSelMargin();\n\t}\n\tQueueIdleWork(WorkItems::updateUI);\n}\n\nvoid Editor::SetEmptySelection(Sci::Position currentPos_) {\n\tSetEmptySelection(SelectionPosition(currentPos_));\n}\n\nvoid Editor::SetSelectionFromSerialized(const char *serialized) {\n\tif (serialized) {\n\t\tsel = Selection(serialized);\n\t\tsel.Truncate(pdoc->Length());\n\t\tSetRectangularRange();\n\t\tInvalidateStyleRedraw();\n\t}\n}\n\nvoid Editor::MultipleSelectAdd(AddNumber addNumber) {\n\tif (SelectionEmpty() || !multipleSelection) {\n\t\t// Select word at caret\n\t\tconst Sci::Position startWord = pdoc->ExtendWordSelect(sel.MainCaret(), -1, true);\n\t\tconst Sci::Position endWord = pdoc->ExtendWordSelect(startWord, 1, true);\n\t\tTrimAndSetSelection(endWord, startWord);\n\n\t} else {\n\n\t\tif (!pdoc->HasCaseFolder())\n\t\t\tpdoc->SetCaseFolder(CaseFolderForEncoding());\n\n\t\tconst Range rangeMainSelection(sel.RangeMain().Start().Position(), sel.RangeMain().End().Position());\n\t\tconst std::string selectedText = RangeText(rangeMainSelection.start, rangeMainSelection.end);\n\n\t\tconst Range rangeTarget(targetRange.start.Position(), targetRange.end.Position());\n\t\tstd::vector<Range> searchRanges;\n\t\t// Search should be over the target range excluding the current selection so\n\t\t// may need to search 2 ranges, after the selection then before the selection.\n\t\tif (rangeTarget.Overlaps(rangeMainSelection)) {\n\t\t\t// Common case is that the selection is completely within the target but\n\t\t\t// may also have overlap at start or end.\n\t\t\tif (rangeMainSelection.end < rangeTarget.end)\n\t\t\t\tsearchRanges.emplace_back(rangeMainSelection.end, rangeTarget.end);\n\t\t\tif (rangeTarget.start < rangeMainSelection.start)\n\t\t\t\tsearchRanges.emplace_back(rangeTarget.start, rangeMainSelection.start);\n\t\t} else {\n\t\t\t// No overlap\n\t\t\tsearchRanges.push_back(rangeTarget);\n\t\t}\n\n\t\tfor (const Range range : searchRanges) {\n\t\t\tSci::Position searchStart = range.start;\n\t\t\tconst Sci::Position searchEnd = range.end;\n\t\t\tfor (;;) {\n\t\t\t\tSci::Position lengthFound = selectedText.length();\n\t\t\t\tconst Sci::Position pos = pdoc->FindText(searchStart, searchEnd,\n\t\t\t\t\tselectedText.c_str(), searchFlags, &lengthFound);\n\t\t\t\tif (pos >= 0) {\n\t\t\t\t\tsel.AddSelection(SelectionRange(pos + lengthFound, pos));\n\t\t\t\t\tContainerNeedsUpdate(Update::Selection);\n\t\t\t\t\tScrollRange(sel.RangeMain());\n\t\t\t\t\tRedraw();\n\t\t\t\t\tif (addNumber == AddNumber::one)\n\t\t\t\t\t\treturn;\n\t\t\t\t\tsearchStart = pos + lengthFound;\n\t\t\t\t} else {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nbool Editor::RangeContainsProtected(Sci::Position start, Sci::Position end) const noexcept {\n\tif (vs.ProtectionActive()) {\n\t\tif (start > end) {\n\t\t\tstd::swap(start, end);\n\t\t}\n\t\t//Au: allow to delete etc protected text (style \"hidden\" or \"readonly\"). But don't allow to delete part of it.\n\t\t//\tAlso the program (a Find tool etc) should never select or replace text inside protected. Users probably can't select.\n\t\tif(end > start) {\n\t\t\tif(vs.styles[pdoc->StyleIndexAt(start)].IsProtected())\n\t\t\t\treturn vs.styles[pdoc->StyleIndexAt(start - 1)].IsProtected()\n\t\t\t\t|| (vs.styles[pdoc->StyleIndexAt(end - 1)].IsProtected() && vs.styles[pdoc->StyleIndexAt(end)].IsProtected());\n\t\t\tif(vs.styles[pdoc->StyleIndexAt(end - 1)].IsProtected())\n\t\t\t\treturn vs.styles[pdoc->StyleIndexAt(end)].IsProtected();\n\t\t}\n\n\t\t//for (Sci::Position pos = start; pos < end; pos++) {\n\t\t//\tif (vs.styles[pdoc->StyleIndexAt(pos)].IsProtected())\n\t\t//\t\treturn true;\n\t\t//}\n\t}\n\treturn false;\n}\n\nbool Editor::RangeContainsProtected(const SelectionRange &range) const noexcept {\n\treturn RangeContainsProtected(range.Start().Position(), range.End().Position());\n}\n\nbool Editor::SelectionContainsProtected() const noexcept {\n\tfor (size_t r=0; r<sel.Count(); r++) {\n\t\tif (RangeContainsProtected(sel.Range(r))) {\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\n/**\n * Asks document to find a good position and then moves out of any invisible positions.\n */\nSci::Position Editor::MovePositionOutsideChar(Sci::Position pos, Sci::Position moveDir, bool checkLineEnd) const {\n\treturn MovePositionOutsideChar(SelectionPosition(pos), moveDir, checkLineEnd).Position();\n}\n\nSelectionPosition Editor::MovePositionOutsideChar(SelectionPosition pos, Sci::Position moveDir, bool checkLineEnd) const {\n\tconst Sci::Position posMoved = pdoc->MovePositionOutsideChar(pos.Position(), moveDir, checkLineEnd);\n\tif (posMoved != pos.Position())\n\t\tpos.SetPosition(posMoved);\n\tif (vs.ProtectionActive()) {\n\t\tif (moveDir > 0) {\n\t\t\tif ((pos.Position() > 0) && vs.styles[pdoc->StyleIndexAt(pos.Position() - 1)].IsProtected()) {\n\t\t\t\twhile ((pos.Position() < pdoc->Length()) &&\n\t\t\t\t        (vs.styles[pdoc->StyleIndexAt(pos.Position())].IsProtected()))\n\t\t\t\t\tpos.Add(1);\n\t\t\t}\n\t\t} else if (moveDir < 0) {\n\t\t\tif (vs.styles[pdoc->StyleIndexAt(pos.Position())].IsProtected()) {\n\t\t\t\twhile ((pos.Position() > 0) &&\n\t\t\t\t        (vs.styles[pdoc->StyleIndexAt(pos.Position() - 1)].IsProtected()))\n\t\t\t\t\tpos.Add(-1);\n\t\t\t}\n\t\t}\n\t}\n\treturn pos;\n}\n\nvoid Editor::MovedCaret(SelectionPosition newPos, SelectionPosition previousPos,\n\tbool ensureVisible, CaretPolicies policies) {\n\tconst Sci::Line currentLine = pdoc->SciLineFromPosition(newPos.Position());\n\tif (ensureVisible) {\n\t\t// In case in need of wrapping to ensure DisplayFromDoc works.\n\t\tif (currentLine >= wrapPending.start) {\n\t\t\tif (WrapLines(WrapScope::wsAll)) {\n\t\t\t\tRedraw();\n\t\t\t}\n\t\t}\n\t\tconst XYScrollPosition newXY = XYScrollToMakeVisible(\n\t\t\tSelectionRange(posDrag.IsValid() ? posDrag : newPos), XYScrollOptions::all, policies);\n\t\tif (previousPos.IsValid() && (newXY.xOffset == xOffset)) {\n\t\t\t// simple vertical scroll then invalidate\n\t\t\tScrollTo(newXY.topLine);\n\t\t\tInvalidateSelection(SelectionRange(previousPos), true);\n\t\t} else {\n\t\t\tSetXYScroll(newXY);\n\t\t}\n\t}\n\n\tShowCaretAtCurrentPosition();\n\tNotifyCaretMove();\n\n\tClaimSelection();\n\tSetHoverIndicatorPosition(sel.MainCaret());\n\tQueueIdleWork(WorkItems::updateUI);\n\n\tif (marginView.highlightDelimiter.NeedsDrawing(currentLine)) {\n\t\tRedrawSelMargin();\n\t}\n}\n\nvoid Editor::MovePositionTo(SelectionPosition newPos, Selection::SelTypes selt, bool ensureVisible) {\n\tconst SelectionPosition spCaret = ((sel.Count() == 1) && sel.Empty()) ?\n\t\tsel.Last() : SelectionPosition(Sci::invalidPosition);\n\n\tconst Sci::Position delta = newPos.Position() - sel.MainCaret();\n\tnewPos = ClampPositionIntoDocument(newPos);\n\tnewPos = MovePositionOutsideChar(newPos, delta);\n\tif (!multipleSelection && sel.IsRectangular() && (selt == Selection::SelTypes::stream)) {\n\t\t// Can't turn into multiple selection so clear additional selections\n\t\tInvalidateSelection(SelectionRange(newPos), true);\n\t\tsel.DropAdditionalRanges();\n\t}\n\tif (!sel.IsRectangular() && (selt == Selection::SelTypes::rectangle)) {\n\t\t// Switching to rectangular\n\t\tInvalidateSelection(sel.RangeMain(), false);\n\t\tSelectionRange rangeMain = sel.RangeMain();\n\t\tsel.Clear();\n\t\tsel.Rectangular() = rangeMain;\n\t}\n\tif (selt != Selection::SelTypes::none) {\n\t\tsel.selType = selt;\n\t}\n\tif (selt != Selection::SelTypes::none || sel.MoveExtends()) {\n\t\tSetSelection(newPos);\n\t} else {\n\t\tSetEmptySelection(newPos);\n\t}\n\n\tMovedCaret(newPos, spCaret, ensureVisible, caretPolicies);\n}\n\nvoid Editor::MovePositionTo(Sci::Position newPos, Selection::SelTypes selt, bool ensureVisible) {\n\tMovePositionTo(SelectionPosition(newPos), selt, ensureVisible);\n}\n\nSelectionPosition Editor::MovePositionSoVisible(SelectionPosition pos, int moveDir) {\n\tpos = ClampPositionIntoDocument(pos);\n\tpos = MovePositionOutsideChar(pos, moveDir);\n\tconst Sci::Line lineDoc = pdoc->SciLineFromPosition(pos.Position());\n\tif (pcs->GetVisible(lineDoc)) {\n\t\treturn pos;\n\t}\n\tSci::Line lineDisplay = pcs->DisplayFromDoc(lineDoc);\n\tif (moveDir > 0) {\n\t\t// lineDisplay is already line before fold as lines in fold use display line of line after fold\n\t\tlineDisplay = std::clamp<Sci::Line>(lineDisplay, 0, pcs->LinesDisplayed());\n\t\treturn SelectionPosition(\n\t\t\tpdoc->LineStart(pcs->DocFromDisplay(lineDisplay)));\n\t} else {\n\t\tlineDisplay = std::clamp<Sci::Line>(lineDisplay - 1, 0, pcs->LinesDisplayed());\n\t\treturn SelectionPosition(\n\t\t\tpdoc->LineEnd(pcs->DocFromDisplay(lineDisplay)));\n\t}\n}\n\nSelectionPosition Editor::MovePositionSoVisible(Sci::Position pos, int moveDir) {\n\treturn MovePositionSoVisible(SelectionPosition(pos), moveDir);\n}\n\nPoint Editor::PointMainCaret() {\n\treturn LocationFromPosition(sel.RangeMain().caret);\n}\n\n/**\n * Choose the x position that the caret will try to stick to\n * as it moves up and down.\n */\nvoid Editor::SetLastXChosen() {\n\tconst Point pt = PointMainCaret();\n\tlastXChosen = static_cast<int>(pt.x) + xOffset;\n}\n\nvoid Editor::RememberSelectionForUndo(int index) {\n\tEnsureModelState();\n\tif (modelState) {\n\t\tmodelState->RememberSelectionForUndo(index, sel);\n\t\tneedRedoRemembered = true;\n\t\t// Remember selection at end of processing current message\n\t}\n}\n\nvoid Editor::RememberSelectionOntoStack(int index) {\n\tEnsureModelState();\n\tif (modelState) {\n\t\t// Is undo currently inside a group?\n\t\tif (!pdoc->AfterUndoSequenceStart()) {\n\t\t\t// Don't remember selections inside a grouped sequence as can only\n\t\t\t// unto or redo to the start and end of the group.\n\t\t\tmodelState->RememberSelectionOntoStack(index, topLine);\n\t\t}\n\t}\n}\n\nvoid Editor::RememberCurrentSelectionForRedoOntoStack() {\n\tif (needRedoRemembered && (pdoc->UndoSequenceDepth() == 0)) {\n\t\tEnsureModelState();\n\t\tif (modelState) {\n\t\t\tmodelState->RememberSelectionForRedoOntoStack(pdoc->UndoCurrent(), sel, topLine);\n\t\t\tneedRedoRemembered = false;\n\t\t}\n\t}\n}\n\nvoid Editor::ScrollTo(Sci::Line line, bool moveThumb) {\n\tconst Sci::Line topLineNew = std::clamp<Sci::Line>(line, 0, MaxScrollPos());\n\tif (topLineNew != topLine) {\n\t\t// Try to optimise small scrolls\n#ifndef UNDER_CE\n\t\tconst Sci::Line linesToMove = topLine - topLineNew;\n\t\tconst bool performBlit = (std::abs(linesToMove) <= 10) && (paintState == PaintState::notPainting);\n\t\twillRedrawAll = !performBlit;\n#endif\n\t\tSetTopLine(topLineNew);\n\t\t// Optimize by styling the view as this will invalidate any needed area\n\t\t// which could abort the initial paint if discovered later.\n\t\tStyleAreaBounded(GetClientRectangle(), true);\n#ifndef UNDER_CE\n\t\t// Perform redraw rather than scroll if many lines would be redrawn anyway.\n\t\tif (performBlit) {\n\t\t\tScrollText(linesToMove);\n\t\t} else {\n\t\t\tRedraw();\n\t\t}\n\t\twillRedrawAll = false;\n#else\n\t\tRedraw();\n#endif\n\t\tif (moveThumb) {\n\t\t\tSetVerticalScrollPos();\n\t\t}\n\t}\n}\n\nvoid Editor::ScrollText(Sci::Line /* linesToMove */) {\n\t//Platform::DebugPrintf(\"Editor::ScrollText %d\\n\", linesToMove);\n\tRedraw();\n}\n\nvoid Editor::HorizontalScrollTo(int xPos) {\n\t//Platform::DebugPrintf(\"HorizontalScroll %d\\n\", xPos);\n\tif (xPos < 0)\n\t\txPos = 0;\n\tif (!Wrapping() && (xOffset != xPos)) {\n\t\txOffset = xPos;\n\t\tContainerNeedsUpdate(Update::HScroll);\n\t\tSetHorizontalScrollPos();\n\t\tRedrawRect(GetClientRectangle());\n\t}\n}\n\nvoid Editor::VerticalCentreCaret() {\n\tconst Sci::Line lineDoc =\n\t\tpdoc->SciLineFromPosition(sel.IsRectangular() ? sel.Rectangular().caret.Position() : sel.MainCaret());\n\tconst Sci::Line lineDisplay = pcs->DisplayFromDoc(lineDoc);\n\tconst Sci::Line newTop = lineDisplay - (LinesOnScreen() / 2);\n\tif (topLine != newTop) {\n\t\tSetTopLine(newTop > 0 ? newTop : 0);\n\t\tSetVerticalScrollPos();\n\t\tRedrawRect(GetClientRectangle());\n\t}\n}\n\nvoid Editor::MoveSelectedLines(int lineDelta) {\n\n\tif (sel.IsRectangular()) {\n\t\t// Convert to stream selection\n\t\tconst SelectionRange rangeRectangular = sel.Rectangular();\n\t\tsel.Clear();\n\t\tsel.SetSelection(rangeRectangular);\n\t}\n\n\t// if selection doesn't start at the beginning of the line, set the new start\n\tSci::Position selectionStart = SelectionStart().Position();\n\tconst Sci::Line startLine = pdoc->SciLineFromPosition(selectionStart);\n\tconst Sci::Position beginningOfStartLine = pdoc->LineStart(startLine);\n\tselectionStart = beginningOfStartLine;\n\n\t// if selection doesn't end at the beginning of a line greater than that of the start,\n\t// then set it at the beginning of the next one\n\tSci::Position selectionEnd = SelectionEnd().Position();\n\tSci::Line endLine = pdoc->SciLineFromPosition(selectionEnd);\n\tconst Sci::Position beginningOfEndLine = pdoc->LineStart(endLine);\n\tbool appendEol = false;\n\tif (selectionEnd > beginningOfEndLine\n\t\t|| selectionStart == selectionEnd) {\n\t\tselectionEnd = pdoc->LineStart(endLine + 1);\n\t\tappendEol = (selectionEnd == pdoc->Length() && pdoc->SciLineFromPosition(selectionEnd) == endLine);\n\t\tendLine = pdoc->SciLineFromPosition(selectionEnd);\n\t}\n\n\t// if there's nowhere for the selection to move\n\t// (i.e. at the beginning going up or at the end going down),\n\t// stop it right there!\n\tconst bool docEndLineEmpty = pdoc->LineStart(endLine) == pdoc->Length();\n\tif ((selectionStart == 0 && lineDelta < 0)\n\t\t|| (selectionEnd == pdoc->Length() && lineDelta > 0\n\t\t\t&& !docEndLineEmpty) // allow moving when end line of document is empty\n\t\t|| ((selectionStart == selectionEnd)\n\t\t\t&& !(lineDelta < 0 && docEndLineEmpty && selectionEnd == pdoc->Length()))) { // allow moving-up last empty line\n\t\treturn;\n\t}\n\n\tUndoGroup ug(pdoc);\n\n\tif (lineDelta > 0 && selectionEnd == pdoc->LineStart(pdoc->LinesTotal() - 1)) {\n\t\tSetSelection(pdoc->MovePositionOutsideChar(selectionEnd - 1, -1), selectionEnd);\n\t\tClearSelection();\n\t\tselectionEnd = CurrentPosition();\n\t}\n\tSetSelection(selectionStart, selectionEnd);\n\n\tconst std::string selectedText = RangeText(selectionStart, selectionEnd);\n\n\tconst Point currentLocation = LocationFromPosition(CurrentPosition());\n\tconst Sci::Line currentLine = LineFromLocation(currentLocation);\n\n\tif (appendEol)\n\t\tSetSelection(pdoc->MovePositionOutsideChar(selectionStart - 1, -1), selectionEnd);\n\tClearSelection();\n\n\tconst std::string_view eol = pdoc->EOLString();\n\tif (currentLine + lineDelta >= pdoc->LinesTotal())\n\t\tpdoc->InsertString(pdoc->Length(), eol);\n\tGoToLine(currentLine + lineDelta);\n\n\tSci::Position selectionLength = pdoc->InsertString(CurrentPosition(), selectedText);\n\tif (appendEol) {\n\t\tconst Sci::Position lengthInserted = pdoc->InsertString(CurrentPosition() + selectionLength, eol);\n\t\tselectionLength += lengthInserted;\n\t}\n\tSetSelection(CurrentPosition(), CurrentPosition() + selectionLength);\n}\n\nvoid Editor::MoveSelectedLinesUp() {\n\tMoveSelectedLines(-1);\n}\n\nvoid Editor::MoveSelectedLinesDown() {\n\tMoveSelectedLines(1);\n}\n\nvoid Editor::MoveCaretInsideView(bool ensureVisible) {\n\tconst PRectangle rcClient = GetTextRectangle();\n\tconst Point pt = PointMainCaret();\n\tif (pt.y < rcClient.top) {\n\t\tMovePositionTo(SPositionFromLocation(\n\t\t            Point::FromInts(lastXChosen - xOffset, static_cast<int>(rcClient.top)),\n\t\t\t\t\tfalse, false, UserVirtualSpace()),\n\t\t\t\t\tSelection::SelTypes::none, ensureVisible);\n\t} else if ((pt.y + vs.lineHeight - 1) > rcClient.bottom) {\n\t\tconst ptrdiff_t yOfLastLineFullyDisplayed = static_cast<ptrdiff_t>(rcClient.top) + ((LinesOnScreen() - 1) * vs.lineHeight);\n\t\tMovePositionTo(SPositionFromLocation(\n\t\t            Point::FromInts(lastXChosen - xOffset, static_cast<int>(rcClient.top + static_cast<XYPOSITION>(yOfLastLineFullyDisplayed))),\n\t\t\t\t\tfalse, false, UserVirtualSpace()),\n\t\t        Selection::SelTypes::none, ensureVisible);\n\t}\n}\n\nSci::Line Editor::DisplayFromPosition(Sci::Position pos) {\n\tAutoSurface surface(this);\n\treturn view.DisplayFromPosition(surface, *this, pos, vs);\n}\n\n/**\n * Ensure the caret is reasonably visible in context.\n *\nCaret policy in Scintilla\n\nIf slop is set, we can define a slop value.\nThis value defines an unwanted zone (UZ) where the caret is... unwanted.\nThis zone is defined as a number of pixels near the vertical margins,\nand as a number of lines near the horizontal margins.\nBy keeping the caret away from the edges, it is seen within its context,\nso it is likely that the identifier that the caret is on can be completely seen,\nand that the current line is seen with some of the lines following it which are\noften dependent on that line.\n\nIf strict is set, the policy is enforced... strictly.\nThe caret is centred on the display if slop is not set,\nand cannot go in the UZ if slop is set.\n\nIf jumps is set, the display is moved more energetically\nso the caret can move in the same direction longer before the policy is applied again.\n'3UZ' notation is used to indicate three time the size of the UZ as a distance to the margin.\n\nIf even is not set, instead of having symmetrical UZs,\nthe left and bottom UZs are extended up to right and top UZs respectively.\nThis way, we favour the displaying of useful information: the beginning of lines,\nwhere most code reside, and the lines after the caret, eg. the body of a function.\n\n     |        |       |      |                                            |\nslop | strict | jumps | even | Caret can go to the margin                 | When reaching limit (caret going out of\n     |        |       |      |                                            | visibility or going into the UZ) display is...\n-----+--------+-------+------+--------------------------------------------+--------------------------------------------------------------\n  0  |   0    |   0   |   0  | Yes                                        | moved to put caret on top/on right\n  0  |   0    |   0   |   1  | Yes                                        | moved by one position\n  0  |   0    |   1   |   0  | Yes                                        | moved to put caret on top/on right\n  0  |   0    |   1   |   1  | Yes                                        | centred on the caret\n  0  |   1    |   -   |   0  | Caret is always on top/on right of display | -\n  0  |   1    |   -   |   1  | No, caret is always centred                | -\n  1  |   0    |   0   |   0  | Yes                                        | moved to put caret out of the asymmetrical UZ\n  1  |   0    |   0   |   1  | Yes                                        | moved to put caret out of the UZ\n  1  |   0    |   1   |   0  | Yes                                        | moved to put caret at 3UZ of the top or right margin\n  1  |   0    |   1   |   1  | Yes                                        | moved to put caret at 3UZ of the margin\n  1  |   1    |   -   |   0  | Caret is always at UZ of top/right margin  | -\n  1  |   1    |   0   |   1  | No, kept out of UZ                         | moved by one position\n  1  |   1    |   1   |   1  | No, kept out of UZ                         | moved to put caret at 3UZ of the margin\n*/\n\nEditor::XYScrollPosition Editor::XYScrollToMakeVisible(const SelectionRange &range,\n\tconst XYScrollOptions options, CaretPolicies policies) {\n\tconst PRectangle rcClient = GetTextRectangle();\n\tconst Point ptOrigin = GetVisibleOriginInMain();\n\tconst Point pt = LocationFromPosition(range.caret) + ptOrigin;\n\tconst Point ptAnchor = LocationFromPosition(range.anchor) + ptOrigin;\n\tconst Point ptBottomCaret(pt.x, pt.y + vs.lineHeight - 1);\n\n\tXYScrollPosition newXY(xOffset, topLine);\n\tif (rcClient.Empty()) {\n\t\treturn newXY;\n\t}\n\n\t// Vertical positioning\n\tif (FlagSet(options, XYScrollOptions::vertical) &&\n\t\t(pt.y < rcClient.top || ptBottomCaret.y >= rcClient.bottom || FlagSet(policies.y.policy, CaretPolicy::Strict))) {\n\t\tconst Sci::Line lineCaret = DisplayFromPosition(range.caret.Position());\n\t\tconst Sci::Line linesOnScreen = LinesOnScreen();\n\t\tconst Sci::Line halfScreen = std::max(linesOnScreen - 1, static_cast<Sci::Line>(2)) / 2;\n\t\tconst bool bSlop = FlagSet(policies.y.policy, CaretPolicy::Slop);\n\t\tconst bool bStrict = FlagSet(policies.y.policy, CaretPolicy::Strict);\n\t\tconst bool bJump = FlagSet(policies.y.policy, CaretPolicy::Jumps);\n\t\tconst bool bEven = FlagSet(policies.y.policy, CaretPolicy::Even);\n\n\t\t// It should be possible to scroll the window to show the caret,\n\t\t// but this fails to remove the caret on GTK+\n\t\tif (bSlop) {\t// A margin is defined\n\t\t\tSci::Line yMoveT = 0;\n\t\t\tSci::Line yMoveB = 0;\n\t\t\tif (bStrict) {\n\t\t\t\tSci::Line yMarginT = 0;\n\t\t\t\tSci::Line yMarginB = 0;\n\t\t\t\tif (!FlagSet(options, XYScrollOptions::useMargin)) {\n\t\t\t\t\t// In drag mode, avoid moves\n\t\t\t\t\t// otherwise, a double click will select several lines.\n\t\t\t\t\tyMarginT = yMarginB = 0;\n\t\t\t\t} else {\n\t\t\t\t\t// yMarginT must equal to caretYSlop, with a minimum of 1 and\n\t\t\t\t\t// a maximum of slightly less than half the height of the text area.\n\t\t\t\t\tyMarginT = std::clamp<Sci::Line>(policies.y.slop, 1, halfScreen);\n\t\t\t\t\tif (bEven) {\n\t\t\t\t\t\tyMarginB = yMarginT;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tyMarginB = linesOnScreen - yMarginT - 1;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tyMoveT = yMarginT;\n\t\t\t\tif (bEven) {\n\t\t\t\t\tif (bJump) {\n\t\t\t\t\t\tyMoveT = std::clamp<Sci::Line>(policies.y.slop * 3, 1, halfScreen);\n\t\t\t\t\t}\n\t\t\t\t\tyMoveB = yMoveT;\n\t\t\t\t} else {\n\t\t\t\t\tyMoveB = linesOnScreen - yMoveT - 1;\n\t\t\t\t}\n\t\t\t\tif (lineCaret < topLine + yMarginT) {\n\t\t\t\t\t// Caret goes too high\n\t\t\t\t\tnewXY.topLine = lineCaret - yMoveT;\n\t\t\t\t} else if (lineCaret > topLine + linesOnScreen - 1 - yMarginB) {\n\t\t\t\t\t// Caret goes too low\n\t\t\t\t\tnewXY.topLine = lineCaret - linesOnScreen + 1 + yMoveB;\n\t\t\t\t}\n\t\t\t} else {\t// Not strict\n\t\t\t\tyMoveT = bJump ? policies.y.slop * 3 : policies.y.slop;\n\t\t\t\tyMoveT = std::clamp<Sci::Line>(yMoveT, 1, halfScreen);\n\t\t\t\tif (bEven) {\n\t\t\t\t\tyMoveB = yMoveT;\n\t\t\t\t} else {\n\t\t\t\t\tyMoveB = linesOnScreen - yMoveT - 1;\n\t\t\t\t}\n\t\t\t\tif (lineCaret < topLine) {\n\t\t\t\t\t// Caret goes too high\n\t\t\t\t\tnewXY.topLine = lineCaret - yMoveT;\n\t\t\t\t} else if (lineCaret > topLine + linesOnScreen - 1) {\n\t\t\t\t\t// Caret goes too low\n\t\t\t\t\tnewXY.topLine = lineCaret - linesOnScreen + 1 + yMoveB;\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\t// No slop\n\t\t\tif (!bStrict && !bJump) {\n\t\t\t\t// Minimal move\n\t\t\t\tif (lineCaret < topLine) {\n\t\t\t\t\t// Caret goes too high\n\t\t\t\t\tnewXY.topLine = lineCaret;\n\t\t\t\t} else if (lineCaret > topLine + linesOnScreen - 1) {\n\t\t\t\t\t// Caret goes too low\n\t\t\t\t\tif (bEven) {\n\t\t\t\t\t\tnewXY.topLine = lineCaret - linesOnScreen + 1;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tnewXY.topLine = lineCaret;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\t// Strict or going out of display\n\t\t\t\tif (bEven) {\n\t\t\t\t\t// Always centre caret\n\t\t\t\t\tnewXY.topLine = lineCaret - halfScreen;\n\t\t\t\t} else {\n\t\t\t\t\t// Always put caret on top of display\n\t\t\t\t\tnewXY.topLine = lineCaret;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (!(range.caret == range.anchor)) {\n\t\t\tconst Sci::Line lineAnchor = DisplayFromPosition(range.anchor.Position());\n\t\t\tif (lineAnchor < lineCaret) {\n\t\t\t\t// Shift up to show anchor or as much of range as possible\n\t\t\t\tnewXY.topLine = std::min(newXY.topLine, lineAnchor);\n\t\t\t\tnewXY.topLine = std::max(newXY.topLine, lineCaret - LinesOnScreen());\n\t\t\t} else {\n\t\t\t\t// Shift down to show anchor or as much of range as possible\n\t\t\t\tnewXY.topLine = std::max(newXY.topLine, lineAnchor - LinesOnScreen());\n\t\t\t\tnewXY.topLine = std::min(newXY.topLine, lineCaret);\n\t\t\t}\n\t\t}\n\t\tnewXY.topLine = std::clamp<Sci::Line>(newXY.topLine, 0, MaxScrollPos());\n\t}\n\n\t// Horizontal positioning\n\tif (FlagSet(options, XYScrollOptions::horizontal) && !Wrapping()) {\n\t\tconst int halfScreen = std::max(static_cast<int>(rcClient.Width()) - 4, 4) / 2;\n\t\tconst bool bSlop = FlagSet(policies.x.policy, CaretPolicy::Slop);\n\t\tconst bool bStrict = FlagSet(policies.x.policy, CaretPolicy::Strict);\n\t\tconst bool bJump = FlagSet(policies.x.policy, CaretPolicy::Jumps);\n\t\tconst bool bEven = FlagSet(policies.x.policy, CaretPolicy::Even);\n\n\t\tif (bSlop) {\t// A margin is defined\n\t\t\tint xMoveL = 0;\n\t\t\tint xMoveR = 0;\n\t\t\tif (bStrict) {\n\t\t\t\tint xMarginL = 0;\n\t\t\t\tint xMarginR = 0;\n\t\t\t\tif (!FlagSet(options, XYScrollOptions::useMargin)) {\n\t\t\t\t\t// In drag mode, avoid moves unless very near of the margin\n\t\t\t\t\t// otherwise, a simple click will select text.\n\t\t\t\t\txMarginL = xMarginR = 2;\n\t\t\t\t} else {\n\t\t\t\t\t// xMargin must equal to caretXSlop, with a minimum of 2 and\n\t\t\t\t\t// a maximum of slightly less than half the width of the text area.\n\t\t\t\t\txMarginR = std::clamp(policies.x.slop, 2, halfScreen);\n\t\t\t\t\tif (bEven) {\n\t\t\t\t\t\txMarginL = xMarginR;\n\t\t\t\t\t} else {\n\t\t\t\t\t\txMarginL = static_cast<int>(rcClient.Width()) - xMarginR - 4;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (bJump && bEven) {\n\t\t\t\t\t// Jump is used only in even mode\n\t\t\t\t\txMoveL = xMoveR = std::clamp(policies.x.slop * 3, 1, halfScreen);\n\t\t\t\t} else {\n\t\t\t\t\txMoveL = xMoveR = 0;\t// Not used, avoid a warning\n\t\t\t\t}\n\t\t\t\tif (pt.x < rcClient.left + xMarginL) {\n\t\t\t\t\t// Caret is on the left of the display\n\t\t\t\t\tif (bJump && bEven) {\n\t\t\t\t\t\tnewXY.xOffset -= xMoveL;\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Move just enough to allow to display the caret\n\t\t\t\t\t\tnewXY.xOffset -= static_cast<int>((rcClient.left + xMarginL) - pt.x);\n\t\t\t\t\t}\n\t\t\t\t} else if (pt.x >= rcClient.right - xMarginR) {\n\t\t\t\t\t// Caret is on the right of the display\n\t\t\t\t\tif (bJump && bEven) {\n\t\t\t\t\t\tnewXY.xOffset += xMoveR;\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Move just enough to allow to display the caret\n\t\t\t\t\t\tnewXY.xOffset += static_cast<int>(pt.x - (rcClient.right - xMarginR) + 1);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\t// Not strict\n\t\t\t\txMoveR = bJump ? policies.x.slop * 3 : policies.x.slop;\n\t\t\t\txMoveR = std::clamp(xMoveR, 1, halfScreen);\n\t\t\t\tif (bEven) {\n\t\t\t\t\txMoveL = xMoveR;\n\t\t\t\t} else {\n\t\t\t\t\txMoveL = static_cast<int>(rcClient.Width()) - xMoveR - 4;\n\t\t\t\t}\n\t\t\t\tif (pt.x < rcClient.left) {\n\t\t\t\t\t// Caret is on the left of the display\n\t\t\t\t\tnewXY.xOffset -= xMoveL;\n\t\t\t\t} else if (pt.x >= rcClient.right) {\n\t\t\t\t\t// Caret is on the right of the display\n\t\t\t\t\tnewXY.xOffset += xMoveR;\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\t// No slop\n\t\t\tif (bStrict ||\n\t\t\t        (bJump && (pt.x < rcClient.left || pt.x >= rcClient.right))) {\n\t\t\t\t// Strict or going out of display\n\t\t\t\tif (bEven) {\n\t\t\t\t\t// Centre caret\n\t\t\t\t\tnewXY.xOffset += static_cast<int>(pt.x - rcClient.left - halfScreen);\n\t\t\t\t} else {\n\t\t\t\t\t// Put caret on right\n\t\t\t\t\tnewXY.xOffset += static_cast<int>(pt.x - rcClient.right + 1);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Move just enough to allow to display the caret\n\t\t\t\tif (pt.x < rcClient.left) {\n\t\t\t\t\t// Caret is on the left of the display\n\t\t\t\t\tif (bEven) {\n\t\t\t\t\t\tnewXY.xOffset -= static_cast<int>(rcClient.left - pt.x);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tnewXY.xOffset += static_cast<int>(pt.x - rcClient.right) + 1;\n\t\t\t\t\t}\n\t\t\t\t} else if (pt.x >= rcClient.right) {\n\t\t\t\t\t// Caret is on the right of the display\n\t\t\t\t\tnewXY.xOffset += static_cast<int>(pt.x - rcClient.right) + 1;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// In case of a jump (find result) largely out of display, adjust the offset to display the caret\n\t\tif (pt.x + xOffset < rcClient.left + newXY.xOffset) {\n\t\t\tnewXY.xOffset = static_cast<int>(pt.x + xOffset - rcClient.left) - 2;\n\t\t} else if (pt.x + xOffset >= rcClient.right + newXY.xOffset) {\n\t\t\tnewXY.xOffset = static_cast<int>(pt.x + xOffset - rcClient.right) + 2;\n\t\t\tif (vs.IsBlockCaretStyle() || view.imeCaretBlockOverride) {\n\t\t\t\t// Ensure we can see a good portion of the block caret\n\t\t\t\tnewXY.xOffset += static_cast<int>(vs.aveCharWidth);\n\t\t\t}\n\t\t}\n\t\tif (!(range.caret == range.anchor)) {\n\t\t\tif (ptAnchor.x < pt.x) {\n\t\t\t\t// Shift to left to show anchor or as much of range as possible\n\t\t\t\tconst int maxOffset = static_cast<int>(ptAnchor.x + xOffset - rcClient.left) - 1;\n\t\t\t\tconst int minOffset = static_cast<int>(pt.x + xOffset - rcClient.right) + 1;\n\t\t\t\tnewXY.xOffset = std::min(newXY.xOffset, maxOffset);\n\t\t\t\tnewXY.xOffset = std::max(newXY.xOffset, minOffset);\n\t\t\t} else {\n\t\t\t\t// Shift to right to show anchor or as much of range as possible\n\t\t\t\tconst int minOffset = static_cast<int>(ptAnchor.x + xOffset - rcClient.right) + 1;\n\t\t\t\tconst int maxOffset = static_cast<int>(pt.x + xOffset - rcClient.left) - 1;\n\t\t\t\tnewXY.xOffset = std::max(newXY.xOffset, minOffset);\n\t\t\t\tnewXY.xOffset = std::min(newXY.xOffset, maxOffset);\n\t\t\t}\n\t\t}\n\t\tif (newXY.xOffset < 0) {\n\t\t\tnewXY.xOffset = 0;\n\t\t}\n\t}\n\n\treturn newXY;\n}\n\nvoid Editor::SetXYScroll(XYScrollPosition newXY) {\n\tif ((newXY.topLine != topLine) || (newXY.xOffset != xOffset)) {\n\t\tif (newXY.topLine != topLine) {\n\t\t\tSetTopLine(newXY.topLine);\n\t\t\tSetVerticalScrollPos();\n\t\t}\n\t\tif (newXY.xOffset != xOffset) {\n\t\t\txOffset = newXY.xOffset;\n\t\t\tContainerNeedsUpdate(Update::HScroll);\n\t\t\tif (newXY.xOffset > 0) {\n\t\t\t\tconst PRectangle rcText = GetTextRectangle();\n\t\t\t\tif (horizontalScrollBarVisible &&\n\t\t\t\t\trcText.Width() + xOffset > scrollWidth) {\n\t\t\t\t\tscrollWidth = xOffset + static_cast<int>(rcText.Width());\n\t\t\t\t\tSetScrollBars();\n\t\t\t\t}\n\t\t\t}\n\t\t\tSetHorizontalScrollPos();\n\t\t}\n\t\tRedraw();\n\t\tUpdateSystemCaret();\n\t}\n}\n\nvoid Editor::ScrollRange(SelectionRange range) {\n\tSetXYScroll(XYScrollToMakeVisible(range, XYScrollOptions::all, caretPolicies));\n}\n\nvoid Editor::EnsureCaretVisible(bool useMargin, bool vert, bool horiz) {\n\tSetXYScroll(XYScrollToMakeVisible(SelectionRange(posDrag.IsValid() ? posDrag : sel.RangeMain().caret),\n\t\t(useMargin?XYScrollOptions::useMargin:XYScrollOptions::none)|\n\t\t(vert?XYScrollOptions::vertical:XYScrollOptions::none)|\n\t\t(horiz?XYScrollOptions::horizontal:XYScrollOptions::none),\n\t\tcaretPolicies));\n}\n\nvoid Editor::ShowCaretAtCurrentPosition() {\n\tif (hasFocus) {\n\t\tcaret.active = true;\n\t\tcaret.on = true;\n\t\tFineTickerCancel(TickReason::caret);\n\t\tif (caret.period > 0)\n\t\t\tFineTickerStart(TickReason::caret, caret.period, caret.period/10);\n\t} else {\n\t\tcaret.active = false;\n\t\tcaret.on = false;\n\t\tFineTickerCancel(TickReason::caret);\n\t}\n\tInvalidateCaret();\n}\n\nvoid Editor::DropCaret() {\n\tcaret.active = false;\n\tFineTickerCancel(TickReason::caret);\n\tInvalidateCaret();\n}\n\nvoid Editor::CaretSetPeriod(int period) {\n\tif (caret.period != period) {\n\t\tcaret.period = period;\n\t\tcaret.on = true;\n\t\tFineTickerCancel(TickReason::caret);\n\t\tif ((caret.active) && (caret.period > 0))\n\t\t\tFineTickerStart(TickReason::caret, caret.period, caret.period/10);\n\t\tInvalidateCaret();\n\t}\n}\n\nvoid Editor::InvalidateCaret() {\n\tif (posDrag.IsValid()) {\n\t\tInvalidateRange(posDrag.Position(), posDrag.Position() + 1);\n\t} else {\n\t\tfor (size_t r=0; r<sel.Count(); r++) {\n\t\t\tInvalidateRange(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1);\n\t\t}\n\t}\n\tUpdateSystemCaret();\n}\n\nvoid Editor::NotifyCaretMove() {\n}\n\nvoid Editor::UpdateSystemCaret() {\n}\n\nbool Editor::Wrapping() const noexcept {\n\treturn vs.wrap.state != Wrap::None;\n}\n\nvoid Editor::NeedWrapping(Sci::Line docLineStart, Sci::Line docLineEnd) {\n//Platform::DebugPrintf(\"\\nNeedWrapping: %0d..%0d\\n\", docLineStart, docLineEnd);\n\tif (wrapPending.AddRange(docLineStart, docLineEnd)) {\n\t\tview.llc.Invalidate(LineLayout::ValidLevel::positions);\n\t}\n\t// Wrap lines during idle.\n\tif (Wrapping() && wrapPending.NeedsWrap()) {\n\t\tSetIdle(true);\n\t}\n}\n\nbool Editor::WrapOneLine(Surface *surface, Sci::Line lineToWrap) {\n\tstd::shared_ptr<LineLayout> ll = view.RetrieveLineLayout(lineToWrap, *this);\n\tint linesWrapped = 1;\n\tif (ll) {\n\t\tview.LayoutLine(*this, surface, vs, ll.get(), wrapWidth);\n\t\tlinesWrapped = ll->lines;\n\t}\n\tif (vs.annotationVisible != AnnotationVisible::Hidden) {\n\t\tlinesWrapped += pdoc->AnnotationLines(lineToWrap);\n\t}\n\treturn pcs->SetHeight(lineToWrap, linesWrapped);\n}\n\nnamespace {\n\n// Lines less than lengthToMultiThread are laid out in blocks in parallel.\n// Longer lines are multi-threaded inside LayoutLine.\n// This allows faster processing when lines differ greatly in length and thus time to lay out.\nconstexpr Sci::Position lengthToMultiThread = 4000;\n\n}\n\nbool Editor::WrapBlock(Surface *surface, Sci::Line lineToWrap, Sci::Line lineToWrapEnd) {\n\n\tconst size_t linesBeingWrapped = static_cast<size_t>(lineToWrapEnd - lineToWrap);\n\n\tstd::vector<int> linesAfterWrap(linesBeingWrapped);\n\n\tsize_t threads = std::min<size_t>(linesBeingWrapped, view.maxLayoutThreads);\n\tif (!surface->SupportsFeature(Supports::ThreadSafeMeasureWidths)) {\n\t\tthreads = 1;\n\t}\n\n\tconst bool multiThreaded = threads > 1;\n\n\tElapsedPeriod epWrapping;\n\n\t// Wrap all the short lines in multiple threads\n\n\t// If only 1 thread needed then use the main thread, else spin up multiple\n\tconst std::launch policy = multiThreaded ? std::launch::async : std::launch::deferred;\n\n\tstd::atomic<size_t> nextIndex = 0;\n\n\t// Lines that are less likely to be re-examined should not be read from or written to the cache.\n\tconst SignificantLines significantLines {\n\t\tpdoc->SciLineFromPosition(sel.MainCaret()),\n\t\tpcs->DocFromDisplay(topLine),\n\t\tLinesOnScreen() + 1,\n\t\tview.llc.GetLevel(),\n\t};\n\n\t// Protect the line layout cache from being accessed from multiple threads simultaneously\n\tstd::mutex mutexRetrieve;\n\n\tstd::vector<std::future<void>> futures;\n\tfor (size_t th = 0; th < threads; th++) {\n\t\tstd::future<void> fut = std::async(policy,\n\t\t\t[=, &surface, &nextIndex, &linesAfterWrap, &mutexRetrieve]() {\n\t\t\t// llTemporary is reused for non-significant lines, avoiding allocation costs.\n\t\t\tstd::shared_ptr<LineLayout> llTemporary = std::make_shared<LineLayout>(-1, 200);\n\t\t\twhile (true) {\n\t\t\t\tconst size_t i = nextIndex.fetch_add(1, std::memory_order_acq_rel);\n\t\t\t\tif (i >= linesBeingWrapped) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tconst Sci::Line lineNumber = lineToWrap + i;\n\t\t\t\tconst Range rangeLine = pdoc->LineRange(lineNumber);\n\t\t\t\tconst Sci::Position lengthLine = rangeLine.Length();\n\t\t\t\tif (lengthLine < lengthToMultiThread) {\n\t\t\t\t\tstd::shared_ptr<LineLayout> ll;\n\t\t\t\t\tif (significantLines.LineMayCache(lineNumber)) {\n\t\t\t\t\t\tstd::lock_guard<std::mutex> guard(mutexRetrieve);\n\t\t\t\t\t\tll = view.RetrieveLineLayout(lineNumber, *this);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tll = llTemporary;\n\t\t\t\t\t\tll->ReSet(lineNumber, lengthLine);\n\t\t\t\t\t}\n\t\t\t\t\tview.LayoutLine(*this, surface, vs, ll.get(), wrapWidth, multiThreaded);\n\t\t\t\t\tlinesAfterWrap[i] = ll->lines;\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tfutures.push_back(std::move(fut));\n\t}\n\tfor (const std::future<void> &f : futures) {\n\t\tf.wait();\n\t}\n\t// End of multiple threads\n\n\t// Multiply duration by number of threads to produce (near) equivalence to duration if single threaded\n\tconst double durationShortLines = epWrapping.Duration(true);\n\tconst double durationShortLinesThreads = durationShortLines * static_cast<double>(threads);\n\n\t// Wrap all the long lines in the main thread.\n\t// LayoutLine may then multi-thread over segments in each line.\n\n\tstd::shared_ptr<LineLayout> llLarge = std::make_shared<LineLayout>(-1, 200);\n\tfor (size_t indexLarge = 0; indexLarge < linesBeingWrapped; indexLarge++) {\n\t\tconst Sci::Line lineNumber = lineToWrap + indexLarge;\n\t\tconst Range rangeLine = pdoc->LineRange(lineNumber);\n\t\tconst Sci::Position lengthLine = rangeLine.Length();\n\t\tif (lengthLine >= lengthToMultiThread) {\n\t\t\tstd::shared_ptr<LineLayout> ll;\n\t\t\tif (significantLines.LineMayCache(lineNumber)) {\n\t\t\t\tll = view.RetrieveLineLayout(lineNumber, *this);\n\t\t\t} else {\n\t\t\t\tll = llLarge;\n\t\t\t\tll->ReSet(lineNumber, lengthLine);\n\t\t\t}\n\t\t\tview.LayoutLine(*this, surface, vs, ll.get(), wrapWidth);\n\t\t\tlinesAfterWrap[indexLarge] = ll->lines;\n\t\t}\n\t}\n\n\tconst double durationLongLines = epWrapping.Duration();\n\tconst size_t bytesBeingWrapped = pdoc->LineStart(lineToWrap + linesBeingWrapped) - pdoc->LineStart(lineToWrap);\n\n\tsize_t wrapsDone = 0;\n\n\tfor (size_t i = 0; i < linesBeingWrapped; i++) {\n\t\tconst Sci::Line lineNumber = lineToWrap + i;\n\t\tint linesWrapped = linesAfterWrap[i];\n\t\tif (vs.annotationVisible != AnnotationVisible::Hidden) {\n\t\t\tlinesWrapped += pdoc->AnnotationLines(lineNumber);\n\t\t}\n\t\tif (pcs->SetHeight(lineNumber, linesWrapped)) {\n\t\t\twrapsDone++;\n\t\t}\n\t\twrapPending.Wrapped(lineNumber);\n\t}\n\n\tdurationWrapOneByte.AddSample(bytesBeingWrapped, durationShortLinesThreads + durationLongLines);\n\n\treturn wrapsDone > 0;\n}\n\n// Perform  wrapping for a subset of the lines needing wrapping.\n// wsAll: wrap all lines which need wrapping in this single call\n// wsVisible: wrap currently visible lines\n// wsIdle: wrap one page + 100 lines\n// Return true if wrapping occurred.\nbool Editor::WrapLines(WrapScope ws) {\n\tSci::Line goodTopLine = topLine;\n\tbool wrapOccurred = false;\n\tif (!Wrapping()) {\n\t\tif (wrapWidth != LineLayout::wrapWidthInfinite) {\n\t\t\twrapWidth = LineLayout::wrapWidthInfinite;\n\t\t\tfor (Sci::Line lineDoc = 0; lineDoc < pdoc->LinesTotal(); lineDoc++) {\n\t\t\t\tint linesWrapped = 1;\n\t\t\t\tif (vs.annotationVisible != AnnotationVisible::Hidden) {\n\t\t\t\t\tlinesWrapped += pdoc->AnnotationLines(lineDoc);\n\t\t\t\t}\n\t\t\t\tpcs->SetHeight(lineDoc, linesWrapped);\n\t\t\t}\n\t\t\twrapOccurred = true;\n\t\t}\n\t\twrapPending.Reset();\n\n\t} else if (wrapPending.NeedsWrap()) {\n\t\twrapPending.start = std::min(wrapPending.start, pdoc->LinesTotal());\n\t\tif (!SetIdle(true)) {\n\t\t\t// Idle processing not supported so full wrap required.\n\t\t\tws = WrapScope::wsAll;\n\t\t}\n\t\t// Decide where to start wrapping\n\t\tSci::Line lineToWrap = wrapPending.start;\n\t\tSci::Line lineToWrapEnd = std::min(wrapPending.end, pdoc->LinesTotal());\n\n\t\tconst Sci::Line lineDocTop = pcs->DocFromDisplay(topLine);\n\t\tLineDocSub lineScrollTo;\n\t\tif (scrollToAfterWrap) {\n\t\t\tlineScrollTo = scrollToAfterWrap.value();\n\t\t} else {\n\t\t\tconst Sci::Line subLineTop = topLine - pcs->DisplayFromDoc(lineDocTop);\n\t\t\tlineScrollTo = { lineDocTop, subLineTop };\n\t\t}\n\t\tif (ws == WrapScope::wsVisible) {\n\t\t\tlineToWrap = std::clamp(lineDocTop-5, wrapPending.start, pdoc->LinesTotal());\n\t\t\t// Priority wrap to just after visible area.\n\t\t\t// Since wrapping could reduce display lines, treat each\n\t\t\t// as taking only one display line.\n\t\t\tlineToWrapEnd = lineDocTop;\n\t\t\tSci::Line lines = LinesOnScreen() + 1;\n\t\t\tconstexpr double secondsAllowed = 0.1;\n\t\t\tconst size_t actionsInAllowedTime = std::clamp<Sci::Line>(\n\t\t\t\tdurationWrapOneByte.ActionsInAllowedTime(secondsAllowed),\n\t\t\t\t0x2000, 0x200000);\n\t\t\tconst Sci::Line lineLast = pdoc->LineFromPositionAfter(lineToWrap, actionsInAllowedTime);\n\t\t\tconst Sci::Line maxLine = std::min(lineLast, pcs->LinesInDoc());\n\t\t\twhile ((lineToWrapEnd < maxLine) && (lines>0)) {\n\t\t\t\tif (pcs->GetVisible(lineToWrapEnd))\n\t\t\t\t\tlines--;\n\t\t\t\tlineToWrapEnd++;\n\t\t\t}\n\t\t\t// .. and if the paint window is outside pending wraps\n\t\t\tif ((lineToWrap > wrapPending.end) || (lineToWrapEnd < wrapPending.start)) {\n\t\t\t\t// Currently visible text does not need wrapping\n\t\t\t\treturn false;\n\t\t\t}\n\t\t} else if (ws == WrapScope::wsIdle) {\n\t\t\t// Try to keep time taken by wrapping reasonable so interaction remains smooth.\n\t\t\tconstexpr double secondsAllowed = 0.01;\n\t\t\tconst size_t actionsInAllowedTime = std::clamp<Sci::Line>(\n\t\t\t\tdurationWrapOneByte.ActionsInAllowedTime(secondsAllowed),\n\t\t\t\t0x200, 0x20000);\n\t\t\tlineToWrapEnd = pdoc->LineFromPositionAfter(lineToWrap, actionsInAllowedTime);\n\t\t}\n\t\tconst Sci::Line lineEndNeedWrap = std::min(wrapPending.end, pdoc->LinesTotal());\n\t\tlineToWrapEnd = std::min(lineToWrapEnd, lineEndNeedWrap);\n\n\t\t// Ensure all lines being wrapped are styled.\n\t\tpdoc->EnsureStyledTo(pdoc->LineStart(lineToWrapEnd));\n\n\t\tif (lineToWrap < lineToWrapEnd) {\n\n\t\t\tPRectangle rcTextArea = GetClientRectangle();\n\t\t\trcTextArea.left = static_cast<XYPOSITION>(vs.textStart);\n\t\t\trcTextArea.right -= vs.rightMarginWidth;\n\t\t\twrapWidth = static_cast<int>(rcTextArea.Width());\n\t\t\tRefreshStyleData();\n\t\t\tAutoSurface surface(this);\n\t\t\tif (surface) {\n//Platform::DebugPrintf(\"Wraplines: scope=%0d need=%0d..%0d perform=%0d..%0d\\n\", ws, wrapPending.start, wrapPending.end, lineToWrap, lineToWrapEnd);\n\n\t\t\t\twrapOccurred = WrapBlock(surface, lineToWrap, lineToWrapEnd);\n\n\t\t\t\tgoodTopLine = pcs->DisplayFromDocSub(lineScrollTo.lineDoc, lineScrollTo.subLine);\n\t\t\t}\n\t\t}\n\n\t\t// If wrapping is done, bring it to resting position\n\t\tif (wrapPending.start >= lineEndNeedWrap) {\n\t\t\twrapPending.Reset();\n\t\t\tscrollToAfterWrap.reset();\n\t\t}\n\t}\n\n\tif (wrapOccurred) {\n\t\tinsideWrapScroll = true;\n\t\tSetScrollBars();\n\t\tSetTopLine(std::clamp<Sci::Line>(goodTopLine, 0, MaxScrollPos()));\n\t\tSetVerticalScrollPos();\n\t\tinsideWrapScroll = false;\n\t}\n\n\treturn wrapOccurred;\n}\n\nvoid Editor::LinesJoin() {\n\tif (!RangeContainsProtected(targetRange.start.Position(), targetRange.end.Position())) {\n\t\tUndoGroup ug(pdoc);\n\t\tconst Sci::Line line = pdoc->SciLineFromPosition(targetRange.start.Position());\n\t\tfor (Sci::Position pos = pdoc->LineEnd(line); pos < targetRange.end.Position(); pos = pdoc->LineEnd(line)) {\n\t\t\tconst char chPrev = pdoc->CharAt(pos - 1);\n\t\t\tconst Sci::Position widthChar = pdoc->LenChar(pos);\n\t\t\ttargetRange.end.Add(-widthChar);\n\t\t\tpdoc->DeleteChars(pos, widthChar);\n\t\t\tif (chPrev != ' ') {\n\t\t\t\t// Ensure at least one space separating previous lines\n\t\t\t\tconst Sci::Position lengthInserted = pdoc->InsertString(pos, \" \", 1);\n\t\t\t\ttargetRange.end.Add(lengthInserted);\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid Editor::LinesSplit(int pixelWidth) {\n\tif (!RangeContainsProtected(targetRange.start.Position(), targetRange.end.Position())) {\n\t\tif (pixelWidth == 0) {\n\t\t\tconst PRectangle rcText = GetTextRectangle();\n\t\t\tpixelWidth = static_cast<int>(rcText.Width());\n\t\t}\n\t\tconst Sci::Line lineStart = pdoc->SciLineFromPosition(targetRange.start.Position());\n\t\tSci::Line lineEnd = pdoc->SciLineFromPosition(targetRange.end.Position());\n\t\tconst std::string_view eol = pdoc->EOLString();\n\t\tUndoGroup ug(pdoc);\n\t\tfor (Sci::Line line = lineStart; line <= lineEnd; line++) {\n\t\t\tAutoSurface surface(this);\n\t\t\tstd::shared_ptr<LineLayout> ll = view.RetrieveLineLayout(line, *this);\n\t\t\tif (surface && ll) {\n\t\t\t\tconst Sci::Position posLineStart = pdoc->LineStart(line);\n\t\t\t\tview.LayoutLine(*this, surface, vs, ll.get(), pixelWidth);\n\t\t\t\tSci::Position lengthInsertedTotal = 0;\n\t\t\t\tfor (int subLine = 1; subLine < ll->lines; subLine++) {\n\t\t\t\t\tconst Sci::Position lengthInserted = pdoc->InsertString(\n\t\t\t\t\t\tposLineStart + lengthInsertedTotal + ll->LineStart(subLine), eol);\n\t\t\t\t\ttargetRange.end.Add(lengthInserted);\n\t\t\t\t\tlengthInsertedTotal += lengthInserted;\n\t\t\t\t}\n\t\t\t}\n\t\t\tlineEnd = pdoc->SciLineFromPosition(targetRange.end.Position());\n\t\t}\n\t}\n}\n\nvoid Editor::PaintSelMargin(Surface *surfaceWindow, const PRectangle &rc) {\n\tif (vs.fixedColumnWidth == 0)\n\t\treturn;\n\n\tRefreshStyleData();\n\tRefreshPixMaps(surfaceWindow);\n\n\t// On GTK+ with Ubuntu overlay scroll bars, the surface may have been finished\n\t// at this point. The Initialised call checks for this case and sets the status\n\t// to be bad which avoids crashes in following calls.\n\tif (!surfaceWindow->Initialised()) {\n\t\treturn;\n\t}\n\n\tPRectangle rcMargin = GetClientRectangle();\n\tconst Point ptOrigin = GetVisibleOriginInMain();\n\trcMargin.Move(0, -ptOrigin.y);\n\trcMargin.left = 0;\n\trcMargin.right = static_cast<XYPOSITION>(vs.fixedColumnWidth);\n\n\tif (!rc.Intersects(rcMargin))\n\t\treturn;\n\n\tSurface *surface;\n\tif (view.bufferedDraw) {\n\t\tsurface = marginView.pixmapSelMargin.get();\n\t} else {\n\t\tsurface = surfaceWindow;\n\t}\n\tsurface->SetMode(CurrentSurfaceMode());\n\n\t// Clip vertically to paint area to avoid drawing line numbers\n\tif (rcMargin.bottom > rc.bottom)\n\t\trcMargin.bottom = rc.bottom;\n\tif (rcMargin.top < rc.top)\n\t\trcMargin.top = rc.top;\n\n\tmarginView.PaintMargin(surface, topLine, rc, rcMargin, *this, vs);\n\n\tif (view.bufferedDraw) {\n\t\tmarginView.pixmapSelMargin->FlushDrawing();\n\t\tsurfaceWindow->Copy(rcMargin, Point(rcMargin.left, rcMargin.top), *marginView.pixmapSelMargin);\n\t}\n}\n\nvoid Editor::RefreshPixMaps(Surface *surfaceWindow) {\n\tview.RefreshPixMaps(surfaceWindow, vs);\n\tmarginView.RefreshPixMaps(surfaceWindow, vs);\n\tif (view.bufferedDraw && !(view.pixmapLine && marginView.pixmapSelMargin)) {\n\t\tconst PRectangle rcClient = GetClientRectangle();\n\t\tview.pixmapLine = surfaceWindow->AllocatePixMap(static_cast<int>(rcClient.Width()), vs.lineHeight);\n\t\tmarginView.pixmapSelMargin = surfaceWindow->AllocatePixMap(vs.fixedColumnWidth,\n\t\t\tstatic_cast<int>(rcClient.Height()));\n\t}\n}\n\nvoid Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) {\n\tredrawPendingText = false;\n\tredrawPendingMargin = false;\n\n\t//Platform::DebugPrintf(\"Paint:%1d (%3d,%3d) ... (%3d,%3d)\\n\",\n\t//\tpaintingAllText, rcArea.left, rcArea.top, rcArea.right, rcArea.bottom);\n\n\tRefreshStyleData();\n\tif (paintState == PaintState::abandoned)\n\t\treturn;\t// Scroll bars may have changed so need redraw\n\n\tpaintAbandonedByStyling = false;\n\n\tStyleAreaBounded(rcArea, false);\n\n\tconst PRectangle rcClient = GetClientRectangle();\n\t//Platform::DebugPrintf(\"Client: (%3d,%3d) ... (%3d,%3d)   %d\\n\",\n\t//\trcClient.left, rcClient.top, rcClient.right, rcClient.bottom);\n\n\tif (NotifyUpdateUI()) {\n\t\tRefreshStyleData();\n\t}\n\n\t// Wrap the visible lines if needed.\n\tif (WrapLines(WrapScope::wsVisible)) {\n\t\t// The wrapping process has changed the height of some lines so\n\t\t// abandon this paint for a complete repaint.\n\t\tif (AbandonPaint()) {\n\t\t\treturn;\n\t\t}\n\t}\n\n\tRefreshPixMaps(surfaceWindow);\n\n\tif (!marginView.pixmapSelPattern->Initialised()) {\n\t\t// When Direct2D is used, pixmap creation may fail with D2DERR_RECREATE_TARGET so\n\t\t// abandon this paint to avoid further failures.\n\t\t// Main drawing surface and pixmaps should be recreated by next paint.\n\t\treturn;\n\t}\n\n\tif (!view.bufferedDraw)\n\t\tsurfaceWindow->SetClip(rcArea);\n\n\tif (paintState != PaintState::abandoned) {\n\t\tif (vs.marginInside) {\n\t\t\tPaintSelMargin(surfaceWindow, rcArea);\n\t\t\tPRectangle rcRightMargin = rcClient;\n\t\t\trcRightMargin.left = rcRightMargin.right - vs.rightMarginWidth;\n\t\t\tif (rcArea.Intersects(rcRightMargin)) {\n\t\t\t\tsurfaceWindow->FillRectangle(rcRightMargin, vs.styles[StyleDefault].back);\n\t\t\t}\n\t\t} else { // Else separate view so separate paint event but leftMargin included to allow overlap\n\t\t\tPRectangle rcLeftMargin = rcArea;\n\t\t\trcLeftMargin.left = 0;\n\t\t\trcLeftMargin.right = rcLeftMargin.left + vs.leftMarginWidth;\n\t\t\tif (rcArea.Intersects(rcLeftMargin)) {\n\t\t\t\tsurfaceWindow->FillRectangle(rcLeftMargin, vs.styles[StyleDefault].back);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (paintState == PaintState::abandoned) {\n\t\t// Either styling or NotifyUpdateUI noticed that painting is needed\n\t\t// outside the current painting rectangle\n\t\t//Platform::DebugPrintf(\"Abandoning paint\\n\");\n\t\tif (Wrapping()) {\n\t\t\tif (paintAbandonedByStyling) {\n\t\t\t\t// Styling has spilled over a line end, such as occurs by starting a multiline\n\t\t\t\t// comment. The width of subsequent text may have changed, so rewrap.\n\t\t\t\tNeedWrapping(pcs->DocFromDisplay(topLine));\n\t\t\t}\n\t\t}\n\t\tif (!view.bufferedDraw)\n\t\t\tsurfaceWindow->PopClip();\n\t\treturn;\n\t}\n\n\tview.PaintText(surfaceWindow, *this, vs, rcArea, rcClient);\n\n\tif (horizontalScrollBarVisible && trackLineWidth && (view.lineWidthMaxSeen > scrollWidth)) {\n\t\tscrollWidth = view.lineWidthMaxSeen;\n\t\tif (!FineTickerRunning(TickReason::widen)) {\n\t\t\tFineTickerStart(TickReason::widen, 50, 5);\n\t\t}\n\t}\n\n\tif (!view.bufferedDraw)\n\t\tsurfaceWindow->PopClip();\n\n\tNotifyPainted();\n}\n\n// This is mostly copied from the Paint method but with some things omitted\n// such as the margin markers, line numbers, selection and caret\n// Should be merged back into a combined Draw method.\nSci::Position Editor::FormatRange(Scintilla::Message iMessage, Scintilla::uptr_t wParam, Scintilla::sptr_t lParam) {\n\tif (!lParam)\n\t\treturn 0;\n\tconst bool draw = wParam != 0;\n\tvoid *ptr = PtrFromSPtr(lParam);\n\tif (iMessage == Message::FormatRange) {\n\t\tRangeToFormat *pfr = static_cast<RangeToFormat *>(ptr);\n\t\tconst CharacterRangeFull chrg{ pfr->chrg.cpMin, pfr->chrg.cpMax };\n\t\tAutoSurface surface(pfr->hdc, this, Technology::Default);\n\t\tAutoSurface surfaceMeasure(pfr->hdcTarget, this, Technology::Default);\n\t\tif (!surface || !surfaceMeasure) {\n\t\t\treturn 0;\n\t\t}\n\t\treturn view.FormatRange(draw, chrg, pfr->rc, surface, surfaceMeasure, *this, vs);\n\t} else {\n\t\t// FormatRangeFull\n\t\tRangeToFormatFull *pfr = static_cast<RangeToFormatFull *>(ptr);\n\t\tAutoSurface surface(pfr->hdc, this, Technology::Default);\n\t\tAutoSurface surfaceMeasure(pfr->hdcTarget, this, Technology::Default);\n\t\tif (!surface || !surfaceMeasure) {\n\t\t\treturn 0;\n\t\t}\n\t\treturn view.FormatRange(draw, pfr->chrg, pfr->rc, surface, surfaceMeasure, *this, vs);\n\t}\n}\n\nlong Editor::TextWidth(uptr_t style, const char *text) {\n\tRefreshStyleData();\n\tAutoSurface surface(this);\n\tif (surface) {\n\t\treturn std::lround(surface->WidthText(vs.styles[style].font.get(), text));\n\t}\n\treturn 1;\n}\n\nvoid Editor::SetVerticalScrollPos() {\n\tif (!insideWrapScroll) {\n\t\tscrollToAfterWrap.reset();\n\t}\n}\n\n// Empty method is overridden on GTK+ to show / hide scrollbars\nvoid Editor::ReconfigureScrollBars() {}\n\nvoid Editor::ChangeScrollBars() {\n\tRefreshStyleData();\n\n\tconst Sci::Line nMax = MaxScrollPos();\n\tconst Sci::Line nPage = LinesOnScreen();\n\tconst bool modified = ModifyScrollBars(nMax + nPage - 1, nPage);\n\tif (modified) {\n\t\tDwellEnd(true);\n\t}\n\n\t// TODO: ensure always showing as many lines as possible\n\t// May not be, if, for example, window made larger\n\tif (topLine > MaxScrollPos()) {\n\t\tSetTopLine(std::clamp<Sci::Line>(topLine, 0, MaxScrollPos()));\n\t\tSetVerticalScrollPos();\n\t\tRedraw();\n\t}\n\tif (modified) {\n\t\tif (!AbandonPaint())\n\t\t\tRedraw();\n\t}\n\t//Platform::DebugPrintf(\"end max = %d page = %d\\n\", nMax, nPage);\n}\n\nvoid Editor::SetScrollBars() {\n\t// Overridden on GTK to defer to idle\n\tChangeScrollBars();\n}\n\nvoid Editor::ChangeSize() {\n\tDropGraphics();\n\tSetScrollBars();\n\tif (Wrapping()) {\n\t\tPRectangle rcTextArea = GetClientRectangle();\n\t\trcTextArea.left = static_cast<XYPOSITION>(vs.textStart);\n\t\trcTextArea.right -= vs.rightMarginWidth;\n\t\tif (wrapWidth != rcTextArea.Width()) {\n\t\t\tNeedWrapping();\n\t\t\tRedraw();\n\t\t}\n\t}\n}\n\nSci::Position Editor::RealizeVirtualSpace(Sci::Position position, Sci::Position virtualSpace) {\n\tif (virtualSpace > 0) {\n\t\tconst Sci::Line line = pdoc->SciLineFromPosition(position);\n\t\tconst Sci::Position indent = pdoc->GetLineIndentPosition(line);\n\t\tif (indent == position) {\n\t\t\treturn pdoc->SetLineIndentation(line, pdoc->GetLineIndentation(line) + virtualSpace);\n\t\t}\n\t\tconst std::string spaceText(virtualSpace, ' ');\n\t\tconst Sci::Position lengthInserted = pdoc->InsertString(position, spaceText);\n\t\tposition += lengthInserted;\n\t}\n\treturn position;\n}\n\nSelectionPosition Editor::RealizeVirtualSpace(const SelectionPosition &position) {\n\t// Return the new position with no virtual space\n\treturn SelectionPosition(RealizeVirtualSpace(position.Position(), position.VirtualSpace()));\n}\n\nvoid Editor::AddChar(char ch) {\n\tconst char s[1] {ch};\n\tInsertCharacter(std::string_view(s, 1), CharacterSource::DirectInput);\n}\n\nvoid Editor::FilterSelections() {\n\tif (!additionalSelectionTyping && (sel.Count() > 1)) {\n\t\tInvalidateWholeSelection();\n\t\tsel.DropAdditionalRanges();\n\t}\n}\n\n// InsertCharacter inserts a character encoded in document code page.\nvoid Editor::InsertCharacter(std::string_view sv, CharacterSource charSource) {\n\tif (sv.empty()) {\n\t\treturn;\n\t}\n\n\t//Au: Fix scintilla bug where it makes 2 undo actions when you overtype selection. First step.\n\tbool wasSelection = !sel.Empty(); if(wasSelection) pdoc->BeginUndoAction();\n\n\tFilterSelections();\n\tbool wrapOccurred = false;\n\t{\n\t\tUndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty() || inOverstrike);\n\n\t\t// Vector elements point into selection in order to change selection.\n\t\tstd::vector<SelectionRange *> selPtrs;\n\t\tfor (size_t r = 0; r < sel.Count(); r++) {\n\t\t\tselPtrs.push_back(&sel.Range(r));\n\t\t}\n\t\t// Order selections by position in document.\n\t\tstd::sort(selPtrs.begin(), selPtrs.end(),\n\t\t\t[](const SelectionRange *a, const SelectionRange *b) noexcept {return *a < *b;});\n\n\t\t// Loop in reverse to avoid disturbing positions of selections yet to be processed.\n\t\tfor (std::vector<SelectionRange *>::reverse_iterator rit = selPtrs.rbegin();\n\t\t\trit != selPtrs.rend(); ++rit) {\n\t\t\tSelectionRange *currentSel = *rit;\n\t\t\tif (!RangeContainsProtected(*currentSel)) {\n\t\t\t\tSci::Position positionInsert = currentSel->Start().Position();\n\t\t\t\tif (!currentSel->Empty()) {\n\t\t\t\t\tClearSelectionRange(*currentSel);\n\t\t\t\t} else if (inOverstrike) {\n\t\t\t\t\tif (positionInsert < pdoc->Length()) {\n\t\t\t\t\t\tif (!pdoc->IsPositionInLineEnd(positionInsert)) {\n\t\t\t\t\t\t\tpdoc->DelChar(positionInsert);\n\t\t\t\t\t\t\tcurrentSel->ClearVirtualSpace();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tpositionInsert = RealizeVirtualSpace(positionInsert, currentSel->caret.VirtualSpace());\n\t\t\t\tconst Sci::Position lengthInserted = pdoc->InsertString(positionInsert, sv);\n\t\t\t\tif (lengthInserted > 0) {\n\t\t\t\t\t*currentSel = SelectionRange(positionInsert + lengthInserted);\n\t\t\t\t}\n\t\t\t\tcurrentSel->ClearVirtualSpace();\n\t\t\t\t// If in wrap mode rewrap current line so EnsureCaretVisible has accurate information\n\t\t\t\tif (Wrapping()) {\n\t\t\t\t\tAutoSurface surface(this);\n\t\t\t\t\tif (surface) {\n\t\t\t\t\t\tif (WrapOneLine(surface, pdoc->SciLineFromPosition(positionInsert))) {\n\t\t\t\t\t\t\twrapOccurred = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tThinRectangularRange();\n\t}\n\n\tif(wasSelection) pdoc->SetUndoCollection(true); //Au: Second step of the bug fix. Resets BeginUndoAction without starting new action.\n\t//This also could be fixed outside scintilla: on WM_CHAR call SCI_BEGINUNDOACTION before CallWindowProc and SCI_SETUNDOCOLLECTION after.\n\t//\tBut doing it in scintilla is faster and probably more reliable.\n\n\tif (wrapOccurred) {\n\t\tSetScrollBars();\n\t\tSetVerticalScrollPos();\n\t\tRedraw();\n\t}\n\t// If in wrap mode rewrap current line so EnsureCaretVisible has accurate information\n\tEnsureCaretVisible();\n\t// Avoid blinking during rapid typing:\n\tShowCaretAtCurrentPosition();\n\tif ((caretSticky == CaretSticky::Off) ||\n\t\t((caretSticky == CaretSticky::WhiteSpace) && !IsAllSpacesOrTabs(sv))) {\n\t\tSetLastXChosen();\n\t}\n\n\tint ch = static_cast<unsigned char>(sv[0]);\n\tif (pdoc->dbcsCodePage != CpUtf8) {\n\t\tif (sv.length() > 1) {\n\t\t\t// DBCS code page or DBCS font character set.\n\t\t\tch = (ch << 8) | static_cast<unsigned char>(sv[1]);\n\t\t}\n\t} else {\n\t\tif ((ch < 0xC0) || (1 == sv.length())) {\n\t\t\t// Handles UTF-8 characters between 0x01 and 0x7F and single byte\n\t\t\t// characters when not in UTF-8 mode.\n\t\t\t// Also treats \\0 and naked trail bytes 0x80 to 0xBF as valid\n\t\t\t// characters representing themselves.\n\t\t} else {\n\t\t\tunsigned int utf32[1] = { 0 };\n\t\t\tUTF32FromUTF8(sv, utf32, std::size(utf32));\n\t\t\tch = utf32[0];\n\t\t}\n\t}\n\tNotifyChar(ch, charSource);\n\n\tif (recordingMacro && charSource != CharacterSource::TentativeInput) {\n\t\tstd::string copy(sv); // ensure NUL-terminated\n\t\tNotifyMacroRecord(Message::ReplaceSel, 0, reinterpret_cast<sptr_t>(copy.data()));\n\t}\n}\n\nvoid Editor::ClearSelectionRange(SelectionRange &range) {\n\tif (!range.Empty()) {\n\t\tif (range.Length()) {\n\t\t\tpdoc->DeleteChars(range.Start().Position(), range.Length());\n\t\t\trange.ClearVirtualSpace();\n\t\t} else {\n\t\t\t// Range is all virtual so collapse to start of virtual space\n\t\t\trange.MinimizeVirtualSpace();\n\t\t}\n\t}\n}\n\nvoid Editor::ClearBeforeTentativeStart() {\n\t// Make positions for the first composition string.\n\tFilterSelections();\n\tUndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty() || inOverstrike);\n\tfor (size_t r = 0; r<sel.Count(); r++) {\n\t\tif (!RangeContainsProtected(sel.Range(r))) {\n\t\t\tClearSelectionRange(sel.Range(r));\n\t\t\tRealizeVirtualSpace(sel.Range(r).caret.Position(), sel.Range(r).caret.VirtualSpace());\n\t\t\tsel.Range(r).ClearVirtualSpace();\n\t\t}\n\t}\n}\n\nvoid Editor::InsertPaste(const char *text, Sci::Position len) {\n\tif (multiPasteMode == MultiPaste::Once) {\n\t\tSelectionPosition selStart = sel.Start();\n\t\tselStart = RealizeVirtualSpace(selStart);\n\t\tconst Sci::Position lengthInserted = pdoc->InsertString(selStart.Position(), text, len);\n\t\tif (lengthInserted > 0) {\n\t\t\tSetEmptySelection(selStart.Position() + lengthInserted);\n\t\t}\n\t} else {\n\t\t// MultiPaste::Each\n\t\tfor (size_t r=0; r<sel.Count(); r++) {\n\t\t\tif (!RangeContainsProtected(sel.Range(r))) {\n\t\t\t\tSci::Position positionInsert = sel.Range(r).Start().Position();\n\t\t\t\tClearSelectionRange(sel.Range(r));\n\t\t\t\tpositionInsert = RealizeVirtualSpace(positionInsert, sel.Range(r).caret.VirtualSpace());\n\t\t\t\tconst Sci::Position lengthInserted = pdoc->InsertString(positionInsert, text, len);\n\t\t\t\tif (lengthInserted > 0) {\n\t\t\t\t\tsel.Range(r) = SelectionRange(positionInsert + lengthInserted);\n\t\t\t\t}\n\t\t\t\tsel.Range(r).ClearVirtualSpace();\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid Editor::InsertPasteShape(const char *text, Sci::Position len, PasteShape shape) {\n\tstd::string convertedText;\n\tif (convertPastes) {\n\t\t// Convert line endings of the paste into our local line-endings mode\n\t\tconvertedText = Document::TransformLineEnds(text, len, pdoc->eolMode);\n\t\tlen = convertedText.length();\n\t\ttext = convertedText.c_str();\n\t}\n\tif (shape == PasteShape::rectangular) {\n\t\tPasteRectangular(sel.Start(), text, len);\n\t} else {\n\t\tif (shape == PasteShape::line) {\n\t\t\tconst Sci::Position insertPos = pdoc->LineStartPosition(sel.MainCaret());\n\t\t\tSci::Position lengthInserted = pdoc->InsertString(insertPos, text, len);\n\t\t\t// add the newline if necessary\n\t\t\tif ((len > 0) && (text[len - 1] != '\\n' && text[len - 1] != '\\r')) {\n\t\t\t\tconst std::string_view endline = pdoc->EOLString();\n\t\t\t\tlengthInserted += pdoc->InsertString(insertPos + lengthInserted, endline);\n\t\t\t}\n\t\t\tif (sel.MainCaret() == insertPos) {\n\t\t\t\tSetEmptySelection(sel.MainCaret() + lengthInserted);\n\t\t\t}\n\t\t} else {\n\t\t\tInsertPaste(text, len);\n\t\t}\n\t}\n}\n\nvoid Editor::ClearSelection(bool retainMultipleSelections) {\n\tif (!sel.IsRectangular() && !retainMultipleSelections)\n\t\tFilterSelections();\n\tUndoGroup ug(pdoc);\n\tfor (size_t r=0; r<sel.Count(); r++) {\n\t\tif (!sel.Range(r).Empty()) {\n\t\t\tif (!RangeContainsProtected(sel.Range(r))) {\n\t\t\t\tpdoc->DeleteChars(sel.Range(r).Start().Position(),\n\t\t\t\t\tsel.Range(r).Length());\n\t\t\t\tsel.Range(r) = SelectionRange(sel.Range(r).Start());\n\t\t\t}\n\t\t}\n\t}\n\tThinRectangularRange();\n\tsel.RemoveDuplicates();\n\tClaimSelection();\n\tSetHoverIndicatorPosition(sel.MainCaret());\n}\n\nvoid Editor::ClearAll() {\n\t{\n\t\tUndoGroup ug(pdoc);\n\t\tif (0 != pdoc->Length()) {\n\t\t\tpdoc->DeleteChars(0, pdoc->Length());\n\t\t}\n\t\tif (!pdoc->IsReadOnly()) {\n\t\t\tpcs->Clear();\n\t\t\tpdoc->AnnotationClearAll();\n\t\t\tpdoc->EOLAnnotationClearAll();\n\t\t\tpdoc->MarginClearAll();\n\t\t}\n\t}\n\n\tview.ClearAllTabstops();\n\n\tsel.Clear();\n\tSetTopLine(0);\n\tSetVerticalScrollPos();\n\tInvalidateStyleRedraw();\n}\n\nvoid Editor::ClearDocumentStyle() {\n\tpdoc->decorations->DeleteLexerDecorations();\n\tpdoc->StartStyling(0);\n\tpdoc->SetStyleFor(pdoc->Length(), 0);\n\tpcs->ShowAll();\n\tSetAnnotationHeights(0, pdoc->LinesTotal());\n\tpdoc->ClearLevels();\n}\n\nvoid Editor::CopyAllowLine() {\n\tSelectionText selectedText;\n\tCopySelectionRange(&selectedText, true);\n\tCopyToClipboard(selectedText);\n}\n\nvoid Editor::CutAllowLine() {\n\tif (sel.Empty()) {\n\t\tpdoc->CheckReadOnly();\n\t\tif (!pdoc->IsReadOnly()) {\n\t\t\tSelectionText selectedText;\n\t\t\tif (CopyLineRange(&selectedText, false)) {\n\t\t\t\tCopyToClipboard(selectedText);\n\t\t\t\tLineDelete();\n\t\t\t}\n\t\t}\n\t} else {\n\t\tCut();\n\t}\n}\n\nvoid Editor::Cut() {\n\tpdoc->CheckReadOnly();\n\tif (!pdoc->IsReadOnly() && !SelectionContainsProtected()) {\n\t\tCopy();\n\t\tClearSelection();\n\t}\n}\n\nvoid Editor::PasteRectangular(SelectionPosition pos, const char *ptr, Sci::Position len) {\n\tif (pdoc->IsReadOnly() || SelectionContainsProtected()) {\n\t\treturn;\n\t}\n\tsel.Clear();\n\tsel.RangeMain() = SelectionRange(pos);\n\tSci::Line line = pdoc->SciLineFromPosition(sel.MainCaret());\n\tUndoGroup ug(pdoc);\n\tsel.RangeMain().caret = RealizeVirtualSpace(sel.RangeMain().caret);\n\tconst int xInsert = XFromPosition(sel.RangeMain().caret);\n\tbool prevCr = false;\n\twhile ((len > 0) && IsEOLCharacter(ptr[len-1]))\n\t\tlen--;\n\tfor (Sci::Position i = 0; i < len; i++) {\n\t\tif (IsEOLCharacter(ptr[i])) {\n\t\t\tif ((ptr[i] == '\\r') || (!prevCr))\n\t\t\t\tline++;\n\t\t\tif (line >= pdoc->LinesTotal()) {\n\t\t\t\tconst std::string_view eol = pdoc->EOLString();\n\t\t\t\tpdoc->InsertString(pdoc->LengthNoExcept(), eol);\n\t\t\t}\n\t\t\t// Pad the end of lines with spaces if required\n\t\t\tsel.RangeMain().caret.SetPosition(PositionFromLineX(line, xInsert));\n\t\t\tif ((XFromPosition(sel.RangeMain().caret) < xInsert) && (i + 1 < len)) {\n\t\t\t\twhile (XFromPosition(sel.RangeMain().caret) < xInsert) {\n\t\t\t\t\tassert(pdoc);\n\t\t\t\t\tconst Sci::Position lengthInserted = pdoc->InsertString(sel.MainCaret(), \" \", 1);\n\t\t\t\t\tsel.RangeMain().caret.Add(lengthInserted);\n\t\t\t\t}\n\t\t\t}\n\t\t\tprevCr = ptr[i] == '\\r';\n\t\t} else {\n\t\t\tconst Sci::Position lengthInserted = pdoc->InsertString(sel.MainCaret(), ptr + i, 1);\n\t\t\tsel.RangeMain().caret.Add(lengthInserted);\n\t\t\tprevCr = false;\n\t\t}\n\t}\n\tSetEmptySelection(pos);\n}\n\nbool Editor::CanPaste() {\n\treturn !pdoc->IsReadOnly() && !SelectionContainsProtected();\n}\n\nvoid Editor::Clear() {\n\t// If multiple selections, don't delete EOLS\n\tif (sel.Empty()) {\n\t\tbool singleVirtual = false;\n\t\tif ((sel.Count() == 1) &&\n\t\t\t!RangeContainsProtected(sel.MainCaret(), sel.MainCaret() + 1) &&\n\t\t\tsel.RangeMain().Start().VirtualSpace()) {\n\t\t\tsingleVirtual = true;\n\t\t}\n\t\tUndoGroup ug(pdoc, (sel.Count() > 1) || singleVirtual);\n\t\tfor (size_t r=0; r<sel.Count(); r++) {\n\t\t\tif (!RangeContainsProtected(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1)) {\n\t\t\t\tif (sel.Range(r).Start().VirtualSpace()) {\n\t\t\t\t\tif (sel.Range(r).anchor < sel.Range(r).caret)\n\t\t\t\t\t\tsel.Range(r) = SelectionRange(RealizeVirtualSpace(sel.Range(r).anchor.Position(), sel.Range(r).anchor.VirtualSpace()));\n\t\t\t\t\telse\n\t\t\t\t\t\tsel.Range(r) = SelectionRange(RealizeVirtualSpace(sel.Range(r).caret.Position(), sel.Range(r).caret.VirtualSpace()));\n\t\t\t\t}\n\t\t\t\tif ((sel.Count() == 1) || !pdoc->IsPositionInLineEnd(sel.Range(r).caret.Position())) {\n\t\t\t\t\tpdoc->DelChar(sel.Range(r).caret.Position());\n\t\t\t\t\tsel.Range(r).ClearVirtualSpace();\n\t\t\t\t}  // else multiple selection so don't eat line ends\n\t\t\t} else {\n\t\t\t\tsel.Range(r).ClearVirtualSpace();\n\t\t\t}\n\t\t}\n\t} else {\n\t\tClearSelection();\n\t}\n\tsel.RemoveDuplicates();\n\tShowCaretAtCurrentPosition();\t\t// Avoid blinking\n}\n\nvoid Editor::SelectAll() {\n\tsel.Clear();\n\tSetSelection(0, pdoc->Length());\n\tRedraw();\n}\n\nvoid Editor::RestoreSelection(Sci::Position newPos, UndoRedo history) {\n\tEnsureModelState();\n\tif (FlagSet(undoSelectionHistoryOption, UndoSelectionHistoryOption::Enabled) && modelState) {\n\t\t// Undo wants the element after the current as it just undid it\n\t\tconst int index = pdoc->UndoCurrent() + (history == UndoRedo::undo ? 1 : 0);\n\t\tconst SelectionWithScroll selAndLine = modelState->SelectionFromStack(index, history);\n\t\tif (!selAndLine.selection.empty()) {\n\t\t\tif (FlagSet(undoSelectionHistoryOption, UndoSelectionHistoryOption::Scroll)) {\n\t\t\t\tScrollTo(selAndLine.topLine);\n\t\t\t}\n\t\t\tsel = Selection(selAndLine.selection);\n\t\t\tif (sel.IsRectangular()) {\n\t\t\t\tconst size_t mainForRectangular = sel.Main();\n\t\t\t\t// Reconstitute ranges from rectangular range\n\t\t\t\tSetRectangularRange();\n\t\t\t\t// Restore main if possible.\n\t\t\t\tif (mainForRectangular < sel.Count()) {\n\t\t\t\t\tsel.SetMain(mainForRectangular);\n\t\t\t\t}\n\t\t\t}\n\t\t\tnewPos = -1; // Used selection from stack so don't use position returned from undo/redo.\n\t\t\tRedraw();\n\t\t}\n\t}\n\tif (newPos >= 0)\n\t\tSetEmptySelection(newPos);\n\tEnsureCaretVisible();\n}\n\nvoid Editor::Undo(/*Au*/ bool dontChangePos) {\n\tif (pdoc->CanUndo()) {\n\t\tInvalidateCaret();\n\t\tconst Sci::Position newPos = pdoc->Undo();\n\t\tif (dontChangePos) return; //Au //also under `case Message::Undo:` was `SetLastXChosen();` (but somehow not under `case Message::Redo:`); it seems OK without it.\n\t\tRestoreSelection(newPos, UndoRedo::undo);\n\t}\n}\n\nvoid Editor::Redo(/*Au*/ bool dontChangePos) {\n\tif (pdoc->CanRedo()) {\n\t\tconst Sci::Position newPos = pdoc->Redo();\n\t\tif (dontChangePos) return; //Au\n\t\tRestoreSelection(newPos, UndoRedo::redo);\n\t}\n}\n\nvoid Editor::DelCharBack(bool allowLineStartDeletion) {\n\tRefreshStyleData();\n\tif (!sel.IsRectangular())\n\t\tFilterSelections();\n\tif (sel.IsRectangular())\n\t\tallowLineStartDeletion = false;\n\tUndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty());\n\tif (sel.Empty()) {\n\t\tfor (size_t r=0; r<sel.Count(); r++) {\n\t\t\tif (!RangeContainsProtected(sel.Range(r).caret.Position() - 1, sel.Range(r).caret.Position())) {\n\t\t\t\tif (sel.Range(r).caret.VirtualSpace()) {\n\t\t\t\t\tsel.Range(r).caret.SetVirtualSpace(sel.Range(r).caret.VirtualSpace() - 1);\n\t\t\t\t\tsel.Range(r).anchor.SetVirtualSpace(sel.Range(r).caret.VirtualSpace());\n\t\t\t\t} else {\n\t\t\t\t\tconst Sci::Line lineCurrentPos =\n\t\t\t\t\t\tpdoc->SciLineFromPosition(sel.Range(r).caret.Position());\n\t\t\t\t\tif (allowLineStartDeletion || (pdoc->LineStart(lineCurrentPos) != sel.Range(r).caret.Position())) {\n\t\t\t\t\t\tif (pdoc->GetColumn(sel.Range(r).caret.Position()) <= pdoc->GetLineIndentation(lineCurrentPos) &&\n\t\t\t\t\t\t\t\tpdoc->GetColumn(sel.Range(r).caret.Position()) > 0 && pdoc->backspaceUnindents) {\n\t\t\t\t\t\t\tUndoGroup ugInner(pdoc, !ug.Needed());\n\t\t\t\t\t\t\tconst int indentation = pdoc->GetLineIndentation(lineCurrentPos);\n\t\t\t\t\t\t\tconst int indentationStep = pdoc->IndentSize();\n\t\t\t\t\t\t\tint indentationChange = indentation % indentationStep;\n\t\t\t\t\t\t\tif (indentationChange == 0)\n\t\t\t\t\t\t\t\tindentationChange = indentationStep;\n\t\t\t\t\t\t\tconst Sci::Position posSelect = pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationChange);\n\t\t\t\t\t\t\t// SetEmptySelection\n\t\t\t\t\t\t\tsel.Range(r) = SelectionRange(posSelect);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tpdoc->DelCharBack(sel.Range(r).caret.Position());\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tsel.Range(r).ClearVirtualSpace();\n\t\t\t}\n\t\t}\n\t\tThinRectangularRange();\n\t} else {\n\t\tClearSelection();\n\t}\n\tsel.RemoveDuplicates();\n\tContainerNeedsUpdate(Update::Selection);\n\t// Avoid blinking during rapid typing:\n\tShowCaretAtCurrentPosition();\n}\n\nvoid Editor::NotifyFocus(bool focus) {\n\tNotificationData scn = {};\n\tscn.nmhdr.code = focus ? Notification::FocusIn : Notification::FocusOut;\n\tNotifyParent(scn);\n}\n\nvoid Editor::SetCtrlID(int identifier) {\n\tctrlID = identifier;\n}\n\nvoid Editor::NotifyStyleToNeeded(Sci::Position endStyleNeeded) {\n\tNotificationData scn = {};\n\tscn.nmhdr.code = Notification::StyleNeeded;\n\tscn.position = endStyleNeeded;\n\tNotifyParent(scn);\n}\n\nvoid Editor::NotifyStyleNeeded(Document *, void *, Sci::Position endStyleNeeded) {\n\tNotifyStyleToNeeded(endStyleNeeded);\n}\n\nvoid Editor::NotifyErrorOccurred(Document *, void *, Status status) {\n\terrorStatus = status;\n}\n\nvoid Editor::NotifyGroupCompleted(Document *, void *) noexcept {\n\t// RememberCurrentSelectionForRedoOntoStack may throw (for memory exhaustion)\n\t// but this method may not as it is called in UndoGroup destructor so ignore\n\t// exception.\n\ttry {\n\t\tRememberCurrentSelectionForRedoOntoStack();\n\t} catch (...) {\n\t\t// Ignore any exception\n\t}\n}\n\nvoid Editor::NotifyChar(int ch, CharacterSource charSource) {\n\tNotificationData scn = {};\n\tscn.nmhdr.code = Notification::CharAdded;\n\tscn.ch = ch;\n\tscn.characterSource = charSource;\n\tNotifyParent(scn);\n}\n\nvoid Editor::NotifySavePoint(bool isSavePoint) {\n\tNotificationData scn = {};\n\tif (isSavePoint) {\n\t\tscn.nmhdr.code = Notification::SavePointReached;\n\t\tif (changeHistoryOption != ChangeHistoryOption::Disabled) {\n\t\t\tRedraw();\n\t\t}\n\t} else {\n\t\tscn.nmhdr.code = Notification::SavePointLeft;\n\t}\n\tNotifyParent(scn);\n}\n\nvoid Editor::NotifyModifyAttempt() {\n\tNotificationData scn = {};\n\tscn.nmhdr.code = Notification::ModifyAttemptRO;\n\tNotifyParent(scn);\n}\n\nvoid Editor::NotifyDoubleClick(Point pt, KeyMod modifiers) {\n\tNotificationData scn = {};\n\tscn.nmhdr.code = Notification::DoubleClick;\n\tscn.line = LineFromLocation(pt);\n\tscn.position = PositionFromLocation(pt, true);\n\tscn.modifiers = modifiers;\n\tNotifyParent(scn);\n}\n\nvoid Editor::NotifyHotSpotDoubleClicked(Sci::Position position, KeyMod modifiers) {\n\tNotificationData scn = {};\n\tscn.nmhdr.code = Notification::HotSpotDoubleClick;\n\tscn.position = position;\n\tscn.modifiers = modifiers;\n\tNotifyParent(scn);\n}\n\nvoid Editor::NotifyHotSpotClicked(Sci::Position position, KeyMod modifiers) {\n\tNotificationData scn = {};\n\tscn.nmhdr.code = Notification::HotSpotClick;\n\tscn.position = position;\n\tscn.modifiers = modifiers;\n\tNotifyParent(scn);\n}\n\nvoid Editor::NotifyHotSpotReleaseClick(Sci::Position position, KeyMod modifiers) {\n\tNotificationData scn = {};\n\tscn.nmhdr.code = Notification::HotSpotReleaseClick;\n\tscn.position = position;\n\tscn.modifiers = modifiers;\n\tNotifyParent(scn);\n}\n\nbool Editor::NotifyUpdateUI() {\n\tif (needUpdateUI != Update::None) {\n\t\tNotificationData scn = {};\n\t\tscn.nmhdr.code = Notification::UpdateUI;\n\t\tscn.updated = needUpdateUI;\n\t\tNotifyParent(scn);\n\t\tneedUpdateUI = Update::None;\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nvoid Editor::NotifyPainted() {\n\tNotificationData scn = {};\n\tscn.nmhdr.code = Notification::Painted;\n\tNotifyParent(scn);\n}\n\nvoid Editor::NotifyIndicatorClick(bool click, Sci::Position position, KeyMod modifiers) {\n\tconst int mask = pdoc->decorations->AllOnFor(position);\n\tif ((click && mask) || pdoc->decorations->ClickNotified()) {\n\t\tNotificationData scn = {};\n\t\tpdoc->decorations->SetClickNotified(click);\n\t\tscn.nmhdr.code = click ? Notification::IndicatorClick : Notification::IndicatorRelease;\n\t\tscn.modifiers = modifiers;\n\t\tscn.position = position;\n\t\tNotifyParent(scn);\n\t}\n}\n\nbool Editor::NotifyMarginClick(Point pt, KeyMod modifiers) {\n\tconst int marginClicked = vs.MarginFromLocation(pt);\n\tif ((marginClicked >= 0) && vs.ms[marginClicked].sensitive) {\n\t\tconst Sci::Position position = pdoc->LineStart(LineFromLocation(pt));\n\t\tif ((vs.ms[marginClicked].mask & MaskFolders) && (FlagSet(foldAutomatic, AutomaticFold::Click))) {\n\t\t\tconst bool ctrl = FlagSet(modifiers, KeyMod::Ctrl);\n\t\t\tconst bool shift = FlagSet(modifiers, KeyMod::Shift);\n\t\t\tconst Sci::Line lineClick = pdoc->SciLineFromPosition(position);\n\t\t\tif (shift && ctrl) {\n\t\t\t\tFoldAll(FoldAction::Toggle);\n\t\t\t} else {\n\t\t\t\tconst FoldLevel levelClick = pdoc->GetFoldLevel(lineClick);\n\t\t\t\tif (LevelIsHeader(levelClick)) {\n\t\t\t\t\tif (shift) {\n\t\t\t\t\t\t// Ensure all children visible\n\t\t\t\t\t\tFoldExpand(lineClick, FoldAction::Expand, levelClick);\n\t\t\t\t\t} else if (ctrl) {\n\t\t\t\t\t\tFoldExpand(lineClick, FoldAction::Toggle, levelClick);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Toggle this line\n\t\t\t\t\t\tFoldLine(lineClick, FoldAction::Toggle);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\tNotificationData scn = {};\n\t\tscn.nmhdr.code = Notification::MarginClick;\n\t\tscn.modifiers = modifiers;\n\t\tscn.position = position;\n\t\tscn.margin = marginClicked;\n\t\tNotifyParent(scn);\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nbool Editor::NotifyMarginRightClick(Point pt, KeyMod modifiers) {\n\tconst int marginRightClicked = vs.MarginFromLocation(pt);\n\tif ((marginRightClicked >= 0) && vs.ms[marginRightClicked].sensitive) {\n\t\tconst Sci::Position position = pdoc->LineStart(LineFromLocation(pt));\n\t\tNotificationData scn = {};\n\t\tscn.nmhdr.code = Notification::MarginRightClick;\n\t\tscn.modifiers = modifiers;\n\t\tscn.position = position;\n\t\tscn.margin = marginRightClicked;\n\t\tNotifyParent(scn);\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nvoid Editor::NotifyNeedShown(Sci::Position pos, Sci::Position len) {\n\tNotificationData scn = {};\n\tscn.nmhdr.code = Notification::NeedShown;\n\tscn.position = pos;\n\tscn.length = len;\n\tNotifyParent(scn);\n}\n\nvoid Editor::NotifyDwelling(Point pt, bool state) {\n\tNotificationData scn = {};\n\tscn.nmhdr.code = state ? Notification::DwellStart : Notification::DwellEnd;\n\tscn.position = PositionFromLocation(pt, true);\n\tscn.x = static_cast<int>(pt.x + vs.ExternalMarginWidth());\n\tscn.y = static_cast<int>(pt.y);\n\tNotifyParent(scn);\n}\n\nvoid Editor::NotifyZoom() {\n\tNotificationData scn = {};\n\tscn.nmhdr.code = Notification::Zoom;\n\tNotifyParent(scn);\n}\n\n// Notifications from document\nvoid Editor::NotifyModifyAttempt(Document *, void *) {\n\t//Platform::DebugPrintf(\"** Modify Attempt\\n\");\n\tNotifyModifyAttempt();\n}\n\nvoid Editor::NotifySavePoint(Document *, void *, bool atSavePoint) {\n\t//Platform::DebugPrintf(\"** Save Point %s\\n\", atSavePoint ? \"On\" : \"Off\");\n\tNotifySavePoint(atSavePoint);\n}\n\nvoid Editor::CheckModificationForWrap(DocModification mh) {\n\tif (FlagSet(mh.modificationType, ModificationFlags::InsertText | ModificationFlags::DeleteText)) {\n\t\tview.llc.Invalidate(LineLayout::ValidLevel::checkTextAndStyle);\n\t\tconst Sci::Line lineDoc = pdoc->SciLineFromPosition(mh.position);\n\t\tconst Sci::Line lines = std::max(static_cast<Sci::Line>(0), mh.linesAdded);\n\t\tif (Wrapping()) {\n\t\t\t// Check if this modification crosses any of the wrap points\n\t\t\tif (wrapPending.NeedsWrap()) {\n\t\t\t\tif (lineDoc < wrapPending.end) { // Inserted/deleted before or inside wrap range\n\t\t\t\t\twrapPending.end += mh.linesAdded;\n\t\t\t\t}\n\t\t\t}\n\t\t\tNeedWrapping(lineDoc, lineDoc + lines + 1);\n\t\t}\n\t\tRefreshStyleData();\n\t\t// Fix up annotation heights\n\t\tSetAnnotationHeights(lineDoc, lineDoc + lines + 2);\n\t}\n}\n\nnamespace {\n\n// Move a position so it is still after the same character as before the insertion.\nconstexpr Sci::Position MovePositionForInsertion(Sci::Position position, Sci::Position startInsertion, Sci::Position length) noexcept {\n\tif (position > startInsertion) {\n\t\treturn position + length;\n\t}\n\treturn position;\n}\n\n// Move a position so it is still after the same character as before the deletion if that\n// character is still present else after the previous surviving character.\nconstexpr Sci::Position MovePositionForDeletion(Sci::Position position, Sci::Position startDeletion, Sci::Position length) noexcept {\n\tif (position > startDeletion) {\n\t\tconst Sci::Position endDeletion = startDeletion + length;\n\t\tif (position > endDeletion) {\n\t\t\treturn position - length;\n\t\t}\n\t\treturn startDeletion;\n\t}\n\treturn position;\n}\n\n}\n\nvoid Editor::NotifyModified(Document *, DocModification mh, void *) {\n\tContainerNeedsUpdate(Update::Content);\n\tif (paintState == PaintState::painting) {\n\t\tCheckForChangeOutsidePaint(Range(mh.position, mh.position + mh.length));\n\t}\n\tif (FlagSet(mh.modificationType, ModificationFlags::ChangeLineState)) {\n\t\tif (paintState == PaintState::painting) {\n\t\t\tCheckForChangeOutsidePaint(\n\t\t\t    Range(pdoc->LineStart(mh.line),\n\t\t\t\t\tpdoc->LineStart(mh.line + 1)));\n\t\t} else {\n\t\t\t// Could check that change is before last visible line.\n\t\t\tRedraw();\n\t\t}\n\t}\n\tif (FlagSet(mh.modificationType, ModificationFlags::ChangeTabStops)) {\n\t\tRedraw();\n\t}\n\tif (FlagSet(mh.modificationType, ModificationFlags::LexerState)) {\n\t\tif (paintState == PaintState::painting) {\n\t\t\tCheckForChangeOutsidePaint(\n\t\t\t    Range(mh.position, mh.position + mh.length));\n\t\t} else {\n\t\t\tRedraw();\n\t\t}\n\t}\n\tif (FlagSet(mh.modificationType, ModificationFlags::ChangeStyle | ModificationFlags::ChangeIndicator)) {\n\t\tif (FlagSet(mh.modificationType, ModificationFlags::ChangeStyle)) {\n\t\t\tpdoc->IncrementStyleClock();\n\t\t}\n\t\tif (paintState == PaintState::notPainting) {\n\t\t\tconst Sci::Line lineDocTop = pcs->DocFromDisplay(topLine);\n\t\t\tif (mh.position < pdoc->LineStart(lineDocTop)) {\n\t\t\t\t// Styling performed before this view\n\t\t\t\tRedraw();\n\t\t\t} else {\n\t\t\t\tInvalidateRange(mh.position, mh.position + mh.length);\n\t\t\t}\n\t\t}\n\t\tif (FlagSet(mh.modificationType, ModificationFlags::ChangeStyle)) {\n\t\t\tview.llc.Invalidate(LineLayout::ValidLevel::checkTextAndStyle);\n\t\t}\n\t} else {\n\t\tif (FlagSet(undoSelectionHistoryOption, UndoSelectionHistoryOption::Enabled) &&\n\t\t\tFlagSet(mh.modificationType, ModificationFlags::User)) {\n\t\t\tif (FlagSet(mh.modificationType, ModificationFlags::BeforeInsert | ModificationFlags::BeforeDelete)) {\n\t\t\t\tRememberSelectionForUndo(pdoc->UndoCurrent());\n\t\t\t}\n\t\t\tif (FlagSet(mh.modificationType, ModificationFlags::InsertText | ModificationFlags::DeleteText)) {\n\t\t\t\tRememberSelectionOntoStack(pdoc->UndoCurrent());\n\t\t\t}\n\t\t}\n\t\t// Move selection and brace highlights\n\t\tif (FlagSet(mh.modificationType, ModificationFlags::InsertText)) {\n\t\t\tsel.MovePositions(true, mh.position, mh.length);\n\t\t\tbraces[0] = MovePositionForInsertion(braces[0], mh.position, mh.length);\n\t\t\tbraces[1] = MovePositionForInsertion(braces[1], mh.position, mh.length);\n\t\t} else if (FlagSet(mh.modificationType, ModificationFlags::DeleteText)) {\n\t\t\tsel.MovePositions(false, mh.position, mh.length);\n\t\t\tbraces[0] = MovePositionForDeletion(braces[0], mh.position, mh.length);\n\t\t\tbraces[1] = MovePositionForDeletion(braces[1], mh.position, mh.length);\n\t\t}\n\t\tif (FlagSet(mh.modificationType, ModificationFlags::BeforeInsert | ModificationFlags::BeforeDelete) && pcs->HiddenLines()) {\n\t\t\t// Some lines are hidden so may need shown.\n\t\t\tconst Sci::Line lineOfPos = pdoc->SciLineFromPosition(mh.position);\n\t\t\tSci::Position endNeedShown = mh.position;\n\t\t\tif (FlagSet(mh.modificationType, ModificationFlags::BeforeInsert)) {\n\t\t\t\tif (pdoc->ContainsLineEnd(mh.text, mh.length) && (mh.position != pdoc->LineStart(lineOfPos)))\n\t\t\t\t\tendNeedShown = pdoc->LineStart(lineOfPos+1);\n\t\t\t} else {\n\t\t\t\t// If the deletion includes any EOL then we extend the need shown area.\n\t\t\t\tendNeedShown = mh.position + mh.length;\n\t\t\t\tSci::Line lineLast = pdoc->SciLineFromPosition(mh.position+mh.length);\n\t\t\t\tfor (Sci::Line line = lineOfPos + 1; line <= lineLast; line++) {\n\t\t\t\t\tconst Sci::Line lineMaxSubord = pdoc->GetLastChild(line, {}, -1);\n\t\t\t\t\tif (lineLast < lineMaxSubord) {\n\t\t\t\t\t\tlineLast = lineMaxSubord;\n\t\t\t\t\t\tendNeedShown = pdoc->LineEnd(lineLast);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tNeedShown(mh.position, endNeedShown - mh.position);\n\t\t}\n\t\tif (mh.linesAdded != 0) {\n\t\t\t// Update contraction state for inserted and removed lines\n\t\t\t// lineOfPos should be calculated in context of state before modification, shouldn't it\n\t\t\tSci::Line lineOfPos = pdoc->SciLineFromPosition(mh.position);\n\t\t\tif (mh.position > pdoc->LineStart(lineOfPos))\n\t\t\t\tlineOfPos++;\t// Affecting subsequent lines\n\t\t\tif (mh.linesAdded > 0) {\n\t\t\t\tpcs->InsertLines(lineOfPos, mh.linesAdded);\n\t\t\t} else {\n\t\t\t\tpcs->DeleteLines(lineOfPos, -mh.linesAdded);\n\t\t\t}\n\t\t\tview.LinesAddedOrRemoved(lineOfPos, mh.linesAdded);\n\t\t}\n\t\tif (FlagSet(mh.modificationType, ModificationFlags::ChangeAnnotation)) {\n\t\t\tconst Sci::Line lineDoc = pdoc->SciLineFromPosition(mh.position);\n\t\t\tif (vs.annotationVisible != AnnotationVisible::Hidden) {\n\t\t\t\tif (pcs->SetHeight(lineDoc, pcs->GetHeight(lineDoc) + static_cast<int>(mh.annotationLinesAdded))) {\n\t\t\t\t\tSetScrollBars();\n\t\t\t\t}\n\t\t\t\tRedraw();\n\t\t\t}\n\t\t}\n\t\tif (FlagSet(mh.modificationType, ModificationFlags::ChangeEOLAnnotation)) {\n\t\t\tif (vs.eolAnnotationVisible != EOLAnnotationVisible::Hidden) {\n\t\t\t\tRedraw();\n\t\t\t}\n\t\t}\n\t\tCheckModificationForWrap(mh);\n\t\tif (mh.linesAdded != 0) {\n\t\t\t// Avoid scrolling of display if change before current display\n\t\t\tif (mh.position < posTopLine && !CanDeferToLastStep(mh)) {\n\t\t\t\tconst Sci::Line newTop = std::clamp<Sci::Line>(topLine + mh.linesAdded, 0, MaxScrollPos());\n\t\t\t\tif (newTop != topLine) {\n\t\t\t\t\tSetTopLine(newTop);\n\t\t\t\t\tSetVerticalScrollPos();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (paintState == PaintState::notPainting && !CanDeferToLastStep(mh)) {\n\t\t\t\tif (SynchronousStylingToVisible()) {\n\t\t\t\t\tQueueIdleWork(WorkItems::style, pdoc->Length());\n\t\t\t\t}\n\t\t\t\tRedraw();\n\t\t\t}\n\t\t} else {\n\t\t\tif (paintState == PaintState::notPainting && mh.length && !CanEliminate(mh)) {\n\t\t\t\tif (SynchronousStylingToVisible()) {\n\t\t\t\t\tQueueIdleWork(WorkItems::style, mh.position + mh.length);\n\t\t\t\t}\n\t\t\t\tInvalidateRange(mh.position, mh.position + mh.length);\n\t\t\t\tif (FlagSet(changeHistoryOption, ChangeHistoryOption::Markers)) {\n\t\t\t\t\tRedrawSelMargin(pdoc->SciLineFromPosition(mh.position));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif (mh.linesAdded != 0 && !CanDeferToLastStep(mh)) {\n\t\tSetScrollBars();\n\t}\n\n\tif (FlagSet(mh.modificationType, (ModificationFlags::ChangeMarker | ModificationFlags::ChangeMargin))) {\n\t\tif ((!willRedrawAll) && ((paintState == PaintState::notPainting) || !PaintContainsMargin())) {\n\t\t\tif (FlagSet(mh.modificationType, ModificationFlags::ChangeFold)) {\n\t\t\t\t// Fold changes can affect the drawing of following lines so redraw whole margin\n\t\t\t\tRedrawSelMargin(marginView.highlightDelimiter.isEnabled ? -1 : mh.line - 1, true);\n\t\t\t} else {\n\t\t\t\tRedrawSelMargin(mh.line);\n\t\t\t}\n\t\t}\n\t}\n\tif ((FlagSet(mh.modificationType, ModificationFlags::ChangeFold)) && (FlagSet(foldAutomatic, AutomaticFold::Change))) {\n\t\tFoldChanged(mh.line, mh.foldLevelNow, mh.foldLevelPrev);\n\t}\n\n\t// NOW pay the piper WRT \"deferred\" visual updates\n\tif (IsLastStep(mh)) {\n\t\tSetScrollBars();\n\t\tRedraw();\n\t}\n\n\t// If client wants to see this modification\n\tif (FlagSet(mh.modificationType, modEventMask)) {\n\t\tif (commandEvents) {\n\t\t\tif ((mh.modificationType & (ModificationFlags::ChangeStyle | ModificationFlags::ChangeIndicator)) == ModificationFlags::None) {\n\t\t\t\t// Real modification made to text of document.\n\t\t\t\tNotifyChange();\t// Send EN_CHANGE\n\t\t\t}\n\t\t}\n\n\t\tNotificationData scn = {};\n\t\tscn.nmhdr.code = Notification::Modified;\n\t\tscn.position = mh.position;\n\t\tscn.modificationType = mh.modificationType;\n\t\tscn.text = mh.text;\n\t\tscn.length = mh.length;\n\t\tscn.linesAdded = mh.linesAdded;\n\t\tscn.line = mh.line;\n\t\tscn.foldLevelNow = mh.foldLevelNow;\n\t\tscn.foldLevelPrev = mh.foldLevelPrev;\n\t\tscn.token = static_cast<int>(mh.token);\n\t\tscn.annotationLinesAdded = mh.annotationLinesAdded;\n\t\tNotifyParent(scn);\n\t}\n}\n\nvoid Editor::NotifyDeleted(Document *, void *) noexcept {\n\t/* Do nothing */\n}\n\nvoid Editor::NotifyMacroRecord(Message iMessage, uptr_t wParam, sptr_t lParam) {\n\n\t// Enumerates all macroable messages\n\tswitch (iMessage) {\n\tcase Message::Cut:\n\tcase Message::Copy:\n\tcase Message::Paste:\n\tcase Message::Clear:\n\tcase Message::ReplaceSel:\n\tcase Message::AddText:\n\tcase Message::InsertText:\n\tcase Message::AppendText:\n\tcase Message::ClearAll:\n\tcase Message::SelectAll:\n\tcase Message::GotoLine:\n\tcase Message::GotoPos:\n\tcase Message::SearchAnchor:\n\tcase Message::SearchNext:\n\tcase Message::SearchPrev:\n\tcase Message::LineDown:\n\tcase Message::LineDownExtend:\n\tcase Message::ParaDown:\n\tcase Message::ParaDownExtend:\n\tcase Message::LineUp:\n\tcase Message::LineUpExtend:\n\tcase Message::ParaUp:\n\tcase Message::ParaUpExtend:\n\tcase Message::CharLeft:\n\tcase Message::CharLeftExtend:\n\tcase Message::CharRight:\n\tcase Message::CharRightExtend:\n\tcase Message::WordLeft:\n\tcase Message::WordLeftExtend:\n\tcase Message::WordRight:\n\tcase Message::WordRightExtend:\n\tcase Message::WordPartLeft:\n\tcase Message::WordPartLeftExtend:\n\tcase Message::WordPartRight:\n\tcase Message::WordPartRightExtend:\n\tcase Message::WordLeftEnd:\n\tcase Message::WordLeftEndExtend:\n\tcase Message::WordRightEnd:\n\tcase Message::WordRightEndExtend:\n\tcase Message::Home:\n\tcase Message::HomeExtend:\n\tcase Message::LineEnd:\n\tcase Message::LineEndExtend:\n\tcase Message::HomeWrap:\n\tcase Message::HomeWrapExtend:\n\tcase Message::LineEndWrap:\n\tcase Message::LineEndWrapExtend:\n\tcase Message::DocumentStart:\n\tcase Message::DocumentStartExtend:\n\tcase Message::DocumentEnd:\n\tcase Message::DocumentEndExtend:\n\tcase Message::StutteredPageUp:\n\tcase Message::StutteredPageUpExtend:\n\tcase Message::StutteredPageDown:\n\tcase Message::StutteredPageDownExtend:\n\tcase Message::PageUp:\n\tcase Message::PageUpExtend:\n\tcase Message::PageDown:\n\tcase Message::PageDownExtend:\n\tcase Message::EditToggleOvertype:\n\tcase Message::Cancel:\n\tcase Message::DeleteBack:\n\tcase Message::Tab:\n\tcase Message::LineIndent:\n\tcase Message::BackTab:\n\tcase Message::LineDedent:\n\tcase Message::FormFeed:\n\tcase Message::VCHome:\n\tcase Message::VCHomeExtend:\n\tcase Message::VCHomeWrap:\n\tcase Message::VCHomeWrapExtend:\n\tcase Message::VCHomeDisplay:\n\tcase Message::VCHomeDisplayExtend:\n\tcase Message::DelWordLeft:\n\tcase Message::DelWordRight:\n\tcase Message::DelWordRightEnd:\n\tcase Message::DelLineLeft:\n\tcase Message::DelLineRight:\n\tcase Message::LineCopy:\n\tcase Message::LineCut:\n\tcase Message::LineDelete:\n\tcase Message::LineTranspose:\n\tcase Message::LineReverse:\n\tcase Message::LineDuplicate:\n\tcase Message::LowerCase:\n\tcase Message::UpperCase:\n\tcase Message::LineScrollDown:\n\tcase Message::LineScrollUp:\n\tcase Message::DeleteBackNotLine:\n\tcase Message::HomeDisplay:\n\tcase Message::HomeDisplayExtend:\n\tcase Message::LineEndDisplay:\n\tcase Message::LineEndDisplayExtend:\n\tcase Message::SetSelectionMode:\n\tcase Message::LineDownRectExtend:\n\tcase Message::LineUpRectExtend:\n\tcase Message::CharLeftRectExtend:\n\tcase Message::CharRightRectExtend:\n\tcase Message::HomeRectExtend:\n\tcase Message::VCHomeRectExtend:\n\tcase Message::LineEndRectExtend:\n\tcase Message::PageUpRectExtend:\n\tcase Message::PageDownRectExtend:\n\tcase Message::SelectionDuplicate:\n\tcase Message::CopyAllowLine:\n\tcase Message::CutAllowLine:\n\tcase Message::VerticalCentreCaret:\n\tcase Message::MoveSelectedLinesUp:\n\tcase Message::MoveSelectedLinesDown:\n\tcase Message::ScrollToStart:\n\tcase Message::ScrollToEnd:\n\t\tbreak;\n\n\t\t// Filter out all others like display changes. Also, newlines are redundant\n\t\t// with char insert messages.\n\tcase Message::NewLine:\n\tdefault:\n\t\t//\t\tprintf(\"Filtered out %ld of macro recording\\n\", iMessage);\n\t\treturn;\n\t}\n\n\t// Send notification\n\tNotificationData scn = {};\n\tscn.nmhdr.code = Notification::MacroRecord;\n\tscn.message = iMessage;\n\tscn.wParam = wParam;\n\tscn.lParam = lParam;\n\tNotifyParent(scn);\n}\n\n// Something has changed that the container should know about\nvoid Editor::ContainerNeedsUpdate(Update flags) noexcept {\n\tneedUpdateUI = needUpdateUI | flags;\n}\n\n/**\n * Force scroll and keep position relative to top of window.\n *\n * If stuttered = true and not already at first/last row, move to first/last row of window.\n * If stuttered = true and already at first/last row, scroll as normal.\n */\nvoid Editor::PageMove(int direction, Selection::SelTypes selt, bool stuttered) {\n\tSci::Line topLineNew;\n\tSelectionPosition newPos;\n\n\tconst Sci::Line currentLine = pdoc->SciLineFromPosition(sel.MainCaret());\n\tconst Sci::Line topStutterLine = topLine + caretPolicies.y.slop;\n\tconst Sci::Line bottomStutterLine =\n\t    pdoc->SciLineFromPosition(PositionFromLocation(\n\t                Point::FromInts(lastXChosen - xOffset, direction * vs.lineHeight * static_cast<int>(LinesToScroll()))))\n\t    - caretPolicies.y.slop - 1;\n\n\tif (stuttered && (direction < 0 && currentLine > topStutterLine)) {\n\t\ttopLineNew = topLine;\n\t\tnewPos = SPositionFromLocation(Point::FromInts(lastXChosen - xOffset, vs.lineHeight * caretPolicies.y.slop),\n\t\t\tfalse, false, UserVirtualSpace());\n\n\t} else if (stuttered && (direction > 0 && currentLine < bottomStutterLine)) {\n\t\ttopLineNew = topLine;\n\t\tnewPos = SPositionFromLocation(Point::FromInts(lastXChosen - xOffset, vs.lineHeight * static_cast<int>(LinesToScroll() - caretPolicies.y.slop)),\n\t\t\tfalse, false, UserVirtualSpace());\n\n\t} else {\n\t\tconst Point pt = LocationFromPosition(sel.MainCaret());\n\n\t\ttopLineNew = std::clamp<Sci::Line>(\n\t\t            topLine + direction * LinesToScroll(), 0, MaxScrollPos());\n\t\tnewPos = SPositionFromLocation(\n\t\t\tPoint::FromInts(lastXChosen - xOffset, static_cast<int>(pt.y) +\n\t\t\t\tdirection * (vs.lineHeight * static_cast<int>(LinesToScroll()))),\n\t\t\tfalse, false, UserVirtualSpace());\n\t}\n\n\tif (topLineNew != topLine) {\n\t\tSetTopLine(topLineNew);\n\t\tMovePositionTo(newPos, selt);\n\t\tSetVerticalScrollPos();\n\t\tRedraw();\n\t} else {\n\t\tMovePositionTo(newPos, selt);\n\t}\n}\n\nvoid Editor::ChangeCaseOfSelection(CaseMapping caseMapping) {\n\tUndoGroup ug(pdoc);\n\tfor (size_t r=0; r<sel.Count(); r++) {\n\t\tSelectionRange current = sel.Range(r);\n\t\tSelectionRange currentNoVS = current;\n\t\tcurrentNoVS.ClearVirtualSpace();\n\t\tconst size_t rangeBytes = currentNoVS.Length();\n\t\tif (rangeBytes > 0 && !RangeContainsProtected(currentNoVS)) {\n\t\t\tstd::string sText = RangeText(currentNoVS.Start().Position(), currentNoVS.End().Position());\n\n\t\t\tstd::string sMapped = CaseMapString(sText, caseMapping);\n\n\t\t\tif (sMapped != sText) {\n\t\t\t\tsize_t firstDifference = 0;\n\t\t\t\twhile (sMapped[firstDifference] == sText[firstDifference])\n\t\t\t\t\tfirstDifference++;\n\t\t\t\tsize_t lastDifferenceText = sText.size() - 1;\n\t\t\t\tsize_t lastDifferenceMapped = sMapped.size() - 1;\n\t\t\t\twhile (sMapped[lastDifferenceMapped] == sText[lastDifferenceText]) {\n\t\t\t\t\tlastDifferenceText--;\n\t\t\t\t\tlastDifferenceMapped--;\n\t\t\t\t}\n\t\t\t\tconst size_t endDifferenceText = sText.size() - 1 - lastDifferenceText;\n\t\t\t\tpdoc->DeleteChars(\n\t\t\t\t\tcurrentNoVS.Start().Position() + firstDifference,\n\t\t\t\t\trangeBytes - firstDifference - endDifferenceText);\n\t\t\t\tconst Sci::Position lengthChange = lastDifferenceMapped - firstDifference + 1;\n\t\t\t\tconst Sci::Position lengthInserted = pdoc->InsertString(\n\t\t\t\t\tcurrentNoVS.Start().Position() + firstDifference,\n\t\t\t\t\tsMapped.c_str() + firstDifference,\n\t\t\t\t\tlengthChange);\n\t\t\t\t// Automatic movement changes selection so reset to exactly the same as it was.\n\t\t\t\tconst Sci::Position diffSizes = sMapped.size() - sText.size() + lengthInserted - lengthChange;\n\t\t\t\tif (diffSizes != 0) {\n\t\t\t\t\tif (current.anchor > current.caret)\n\t\t\t\t\t\tcurrent.anchor.Add(diffSizes);\n\t\t\t\t\telse\n\t\t\t\t\t\tcurrent.caret.Add(diffSizes);\n\t\t\t\t}\n\t\t\t\tsel.Range(r) = current;\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid Editor::LineDelete() {\n\tconst Sci::Line line = pdoc->SciLineFromPosition(sel.MainCaret());\n\tconst Sci::Position start = pdoc->LineStart(line);\n\tconst Sci::Position end = pdoc->LineStart(line + 1);\n\tpdoc->DeleteChars(start, end - start);\n}\n\nvoid Editor::LineTranspose() {\n\tconst Sci::Line line = pdoc->SciLineFromPosition(sel.MainCaret());\n\tif (line > 0) {\n\t\tUndoGroup ug(pdoc);\n\n\t\tconst Sci::Position startPrevious = pdoc->LineStart(line - 1);\n\t\tconst std::string linePrevious = RangeText(startPrevious, pdoc->LineEnd(line - 1));\n\n\t\tSci::Position startCurrent = pdoc->LineStart(line);\n\t\tconst std::string lineCurrent = RangeText(startCurrent, pdoc->LineEnd(line));\n\n\t\tpdoc->DeleteChars(startCurrent, lineCurrent.length());\n\t\tpdoc->DeleteChars(startPrevious, linePrevious.length());\n\t\tstartCurrent -= linePrevious.length();\n\n\t\tstartCurrent += pdoc->InsertString(startPrevious, lineCurrent);\n\t\tpdoc->InsertString(startCurrent, linePrevious);\n\t\t// Move caret to start of current line\n\t\tMovePositionTo(SelectionPosition(startCurrent));\n\t}\n}\n\nvoid Editor::LineReverse() {\n\tconst Sci::Line lineStart =\n\t\tpdoc->SciLineFromPosition(sel.RangeMain().Start().Position());\n\tconst Sci::Line lineEnd =\n\t\tpdoc->SciLineFromPosition(sel.RangeMain().End().Position()-1);\n\tconst Sci::Line lineDiff = lineEnd - lineStart;\n\tif (lineDiff <= 0)\n\t\treturn;\n\tUndoGroup ug(pdoc);\n\tfor (Sci::Line i=(lineDiff+1)/2-1; i>=0; --i) {\n\t\tconst Sci::Line lineNum2 = lineEnd - i;\n\t\tconst Sci::Line lineNum1 = lineStart + i;\n\t\tSci::Position lineStart2 = pdoc->LineStart(lineNum2);\n\t\tconst Sci::Position lineStart1 = pdoc->LineStart(lineNum1);\n\t\tconst std::string line2 = RangeText(lineStart2, pdoc->LineEnd(lineNum2));\n\t\tconst std::string line1 = RangeText(lineStart1, pdoc->LineEnd(lineNum1));\n\t\tconst Sci::Position lineLen2 = line2.length();\n\t\tconst Sci::Position lineLen1 = line1.length();\n\t\tpdoc->DeleteChars(lineStart2, lineLen2);\n\t\tpdoc->DeleteChars(lineStart1, lineLen1);\n\t\tlineStart2 -= lineLen1;\n\t\tpdoc->InsertString(lineStart2, line1);\n\t\tpdoc->InsertString(lineStart1, line2);\n\t}\n\t// Wholly select all affected lines\n\tsel.RangeMain() = SelectionRange(pdoc->LineStart(lineStart),\n\t\tpdoc->LineStart(lineEnd+1));\n}\n\nvoid Editor::Duplicate(bool forLine) {\n\tif (sel.Empty()) {\n\t\tforLine = true;\n\t}\n\tUndoGroup ug(pdoc);\n\tstd::string_view eol;\n\tif (forLine) {\n\t\teol = pdoc->EOLString();\n\t}\n\tfor (size_t r=0; r<sel.Count(); r++) {\n\t\tSelectionPosition start = sel.Range(r).Start();\n\t\tSelectionPosition end = sel.Range(r).End();\n\t\tif (forLine) {\n\t\t\tconst Sci::Line line = pdoc->SciLineFromPosition(sel.Range(r).caret.Position());\n\t\t\tstart = SelectionPosition(pdoc->LineStart(line));\n\t\t\tend = SelectionPosition(pdoc->LineEnd(line));\n\t\t}\n\t\tstd::string text = RangeText(start.Position(), end.Position());\n\t\tSci::Position lengthInserted = 0;\n\t\tif (forLine)\n\t\t\tlengthInserted = pdoc->InsertString(end.Position(), eol);\n\t\tpdoc->InsertString(end.Position() + lengthInserted, text);\n\t}\n\tif (sel.Count() && sel.IsRectangular()) {\n\t\tSelectionPosition last = sel.Last();\n\t\tif (forLine) {\n\t\t\tconst Sci::Line line = pdoc->SciLineFromPosition(last.Position());\n\t\t\tlast = SelectionPosition(last.Position() +\n\t\t\t\tpdoc->LineStart(line+1) - pdoc->LineStart(line));\n\t\t}\n\t\tif (sel.Rectangular().anchor > sel.Rectangular().caret)\n\t\t\tsel.Rectangular().anchor = last;\n\t\telse\n\t\t\tsel.Rectangular().caret = last;\n\t\tSetRectangularRange();\n\t}\n}\n\nvoid Editor::CancelModes() {\n\tsel.SetMoveExtends(false);\n}\n\nvoid Editor::NewLine() {\n\tInvalidateWholeSelection();\n\tif (sel.IsRectangular() || !additionalSelectionTyping) {\n\t\t// Remove non-main ranges\n\t\tsel.DropAdditionalRanges();\n\t}\n\n\tUndoGroup ug(pdoc, !sel.Empty() || (sel.Count() > 1));\n\n\t// Clear each range\n\tif (!sel.Empty()) {\n\t\tClearSelection();\n\t}\n\n\t// Insert each line end\n\tsize_t countInsertions = 0;\n\tconst std::string_view eol = pdoc->EOLString();\n\tfor (size_t r = 0; r < sel.Count(); r++) {\n\t\tsel.Range(r).ClearVirtualSpace();\n\t\tconst Sci::Position positionInsert = sel.Range(r).caret.Position();\n\t\tconst Sci::Position insertLength = pdoc->InsertString(positionInsert, eol);\n\t\tif (insertLength > 0) {\n\t\t\tsel.Range(r) = SelectionRange(positionInsert + insertLength);\n\t\t\tcountInsertions++;\n\t\t}\n\t}\n\n\t// Perform notifications after all the changes as the application may change the\n\t// selections in response to the characters.\n\tfor (size_t i = 0; i < countInsertions; i++) {\n\t\tfor (const char ch : eol) {\n\t\t\tNotifyChar(ch, CharacterSource::DirectInput);\n\t\t\tif (recordingMacro) {\n\t\t\t\tconst char txt[2] = { ch, '\\0' };\n\t\t\t\tNotifyMacroRecord(Message::ReplaceSel, 0, reinterpret_cast<sptr_t>(txt));\n\t\t\t}\n\t\t}\n\t}\n\n\tSetLastXChosen();\n\tSetScrollBars();\n\tEnsureCaretVisible();\n\t// Avoid blinking during rapid typing:\n\tShowCaretAtCurrentPosition();\n}\n\nSelectionPosition Editor::PositionUpOrDown(SelectionPosition spStart, int direction, int lastX) {\n\tconst Point pt = LocationFromPosition(spStart);\n\tint skipLines = 0;\n\n\tif (vs.annotationVisible != AnnotationVisible::Hidden) {\n\t\tconst Sci::Line lineDoc = pdoc->SciLineFromPosition(spStart.Position());\n\t\tconst Point ptStartLine = LocationFromPosition(pdoc->LineStart(lineDoc));\n\t\tconst int subLine = static_cast<int>(pt.y - ptStartLine.y) / vs.lineHeight;\n\n\t\tif (direction < 0 && subLine == 0) {\n\t\t\tconst Sci::Line lineDisplay = pcs->DisplayFromDoc(lineDoc);\n\t\t\tif (lineDisplay > 0) {\n\t\t\t\tskipLines = pdoc->AnnotationLines(pcs->DocFromDisplay(lineDisplay - 1));\n\t\t\t}\n\t\t} else if (direction > 0 && subLine >= (pcs->GetHeight(lineDoc) - 1 - pdoc->AnnotationLines(lineDoc))) {\n\t\t\tskipLines = pdoc->AnnotationLines(lineDoc);\n\t\t}\n\t}\n\n\tconst Sci::Line newY = static_cast<Sci::Line>(pt.y) + (1 + skipLines) * direction * vs.lineHeight;\n\tif (lastX < 0) {\n\t\tlastX = static_cast<int>(pt.x) + xOffset;\n\t}\n\tSelectionPosition posNew = SPositionFromLocation(\n\t\tPoint::FromInts(lastX - xOffset, static_cast<int>(newY)), false, false, UserVirtualSpace());\n\n\tif (direction < 0) {\n\t\t// Line wrapping may lead to a location on the same line, so\n\t\t// seek back if that is the case.\n\t\tPoint ptNew = LocationFromPosition(posNew.Position());\n\t\twhile ((posNew.Position() > 0) && (pt.y == ptNew.y)) {\n\t\t\tposNew.Add(-1);\n\t\t\tposNew.SetVirtualSpace(0);\n\t\t\tptNew = LocationFromPosition(posNew.Position());\n\t\t}\n\t} else if (direction > 0 && posNew.Position() != pdoc->Length()) {\n\t\t// There is an equivalent case when moving down which skips\n\t\t// over a line.\n\t\tPoint ptNew = LocationFromPosition(posNew.Position());\n\t\twhile ((posNew.Position() > spStart.Position()) && (ptNew.y > static_cast<XYPOSITION>(newY))) {\n\t\t\tposNew.Add(-1);\n\t\t\tposNew.SetVirtualSpace(0);\n\t\t\tptNew = LocationFromPosition(posNew.Position());\n\t\t}\n\t}\n\treturn posNew;\n}\n\nvoid Editor::CursorUpOrDown(int direction, Selection::SelTypes selt) {\n\tif ((selt == Selection::SelTypes::none) && sel.MoveExtends()) {\n\t\tselt = !sel.IsRectangular() ? Selection::SelTypes::stream : Selection::SelTypes::rectangle;\n\t}\n\tSelectionPosition caretToUse = sel.RangeMain().caret;\n\tif (sel.IsRectangular()) {\n\t\tif (selt ==  Selection::SelTypes::none) {\n\t\t\tcaretToUse = (direction > 0) ? sel.Limits().end : sel.Limits().start;\n\t\t} else {\n\t\t\tcaretToUse = sel.Rectangular().caret;\n\t\t}\n\t}\n\tif (selt == Selection::SelTypes::rectangle) {\n\t\tconst SelectionRange rangeBase = sel.IsRectangular() ? sel.Rectangular() : sel.RangeMain();\n\t\tif (!sel.IsRectangular()) {\n\t\t\tInvalidateWholeSelection();\n\t\t\tsel.DropAdditionalRanges();\n\t\t}\n\t\tconst SelectionPosition posNew = MovePositionSoVisible(\n\t\t\tPositionUpOrDown(caretToUse, direction, lastXChosen), direction);\n\t\tsel.selType = Selection::SelTypes::rectangle;\n\t\tsel.Rectangular() = SelectionRange(posNew, rangeBase.anchor);\n\t\tSetRectangularRange();\n\t\tMovedCaret(posNew, caretToUse, true, caretPolicies);\n\t} else if (sel.selType == Selection::SelTypes::lines && sel.MoveExtends()) {\n\t\t// Calculate new caret position and call SetSelection(), which will ensure whole lines are selected.\n\t\tconst SelectionPosition posNew = MovePositionSoVisible(\n\t\t\tPositionUpOrDown(caretToUse, direction, -1), direction);\n\t\tSetSelection(posNew, sel.RangeMain().anchor);\n\t} else {\n\t\tInvalidateWholeSelection();\n\t\tif (!additionalSelectionTyping || (sel.IsRectangular())) {\n\t\t\tsel.DropAdditionalRanges();\n\t\t}\n\t\tsel.selType = Selection::SelTypes::stream;\n\t\tfor (size_t r = 0; r < sel.Count(); r++) {\n\t\t\tconst int lastX = (r == sel.Main()) ? lastXChosen : -1;\n\t\t\tconst SelectionPosition spCaretNow = sel.Range(r).caret;\n\t\t\tconst SelectionPosition posNew = MovePositionSoVisible(\n\t\t\t\tPositionUpOrDown(spCaretNow, direction, lastX), direction);\n\t\t\tsel.Range(r) = selt == Selection::SelTypes::stream ?\n\t\t\t\tSelectionRange(posNew, sel.Range(r).anchor) : SelectionRange(posNew);\n\t\t}\n\t\tsel.RemoveDuplicates();\n\t\tMovedCaret(sel.RangeMain().caret, caretToUse, true, caretPolicies);\n\t}\n}\n\nvoid Editor::ParaUpOrDown(int direction, Selection::SelTypes selt) {\n\tSci::Line lineDoc;\n\tconst Sci::Position savedPos = sel.MainCaret();\n\tdo {\n\t\tMovePositionTo(SelectionPosition(direction > 0 ? pdoc->ParaDown(sel.MainCaret()) : pdoc->ParaUp(sel.MainCaret())), selt);\n\t\tlineDoc = pdoc->SciLineFromPosition(sel.MainCaret());\n\t\tif (direction > 0) {\n\t\t\tif (sel.MainCaret() >= pdoc->Length() && !pcs->GetVisible(lineDoc)) {\n\t\t\t\tif (selt == Selection::SelTypes::none) {\n\t\t\t\t\tMovePositionTo(SelectionPosition(pdoc->LineEndPosition(savedPos)));\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t} while (!pcs->GetVisible(lineDoc));\n}\n\nRange Editor::RangeDisplayLine(Sci::Line lineVisible) {\n\tRefreshStyleData();\n\tAutoSurface surface(this);\n\treturn view.RangeDisplayLine(surface, *this, lineVisible, vs);\n}\n\nSci::Position Editor::StartEndDisplayLine(Sci::Position pos, bool start) {\n\tRefreshStyleData();\n\tAutoSurface surface(this);\n\tconst Sci::Position posRet = view.StartEndDisplayLine(surface, *this, pos, start, vs);\n\tif (posRet == Sci::invalidPosition) {\n\t\treturn pos;\n\t}\n\treturn posRet;\n}\n\nnamespace {\n\nconstexpr short HighShortFromWParam(uptr_t x) {\n\treturn static_cast<short>(x >> 16);\n}\n\nconstexpr short LowShortFromWParam(uptr_t x) {\n\treturn static_cast<short>(x & 0xffff);\n}\n\nconstexpr Message WithExtends(Message iMessage) noexcept {\n\tswitch (iMessage) {\n\tcase Message::CharLeft: return Message::CharLeftExtend;\n\tcase Message::CharRight: return Message::CharRightExtend;\n\n\tcase Message::WordLeft: return Message::WordLeftExtend;\n\tcase Message::WordRight: return Message::WordRightExtend;\n\tcase Message::WordLeftEnd: return Message::WordLeftEndExtend;\n\tcase Message::WordRightEnd: return Message::WordRightEndExtend;\n\tcase Message::WordPartLeft: return Message::WordPartLeftExtend;\n\tcase Message::WordPartRight: return Message::WordPartRightExtend;\n\n\tcase Message::Home: return Message::HomeExtend;\n\tcase Message::HomeDisplay: return Message::HomeDisplayExtend;\n\tcase Message::HomeWrap: return Message::HomeWrapExtend;\n\tcase Message::VCHome: return Message::VCHomeExtend;\n\tcase Message::VCHomeDisplay: return Message::VCHomeDisplayExtend;\n\tcase Message::VCHomeWrap: return Message::VCHomeWrapExtend;\n\n\tcase Message::LineEnd: return Message::LineEndExtend;\n\tcase Message::LineEndDisplay: return Message::LineEndDisplayExtend;\n\tcase Message::LineEndWrap: return Message::LineEndWrapExtend;\n\n\tdefault:\treturn iMessage;\n\t}\n}\n\nconstexpr int NaturalDirection(Message iMessage) noexcept {\n\tswitch (iMessage) {\n\tcase Message::CharLeft:\n\tcase Message::CharLeftExtend:\n\tcase Message::CharLeftRectExtend:\n\tcase Message::WordLeft:\n\tcase Message::WordLeftExtend:\n\tcase Message::WordLeftEnd:\n\tcase Message::WordLeftEndExtend:\n\tcase Message::WordPartLeft:\n\tcase Message::WordPartLeftExtend:\n\tcase Message::Home:\n\tcase Message::HomeExtend:\n\tcase Message::HomeDisplay:\n\tcase Message::HomeDisplayExtend:\n\tcase Message::HomeWrap:\n\tcase Message::HomeWrapExtend:\n\t\t// VC_HOME* mostly goes back\n\tcase Message::VCHome:\n\tcase Message::VCHomeExtend:\n\tcase Message::VCHomeDisplay:\n\tcase Message::VCHomeDisplayExtend:\n\tcase Message::VCHomeWrap:\n\tcase Message::VCHomeWrapExtend:\n\t\treturn -1;\n\n\tdefault:\n\t\treturn 1;\n\t}\n}\n\nconstexpr bool IsRectExtend(Message iMessage, bool isRectMoveExtends) noexcept {\n\tswitch (iMessage) {\n\tcase Message::CharLeftRectExtend:\n\tcase Message::CharRightRectExtend:\n\tcase Message::HomeRectExtend:\n\tcase Message::VCHomeRectExtend:\n\tcase Message::LineEndRectExtend:\n\t\treturn true;\n\tdefault:\n\t\tif (isRectMoveExtends) {\n\t\t\t// Handle Message::SetSelectionMode(SelectionMode::Rectangle) and subsequent movements.\n\t\t\tswitch (iMessage) {\n\t\t\tcase Message::CharLeftExtend:\n\t\t\tcase Message::CharRightExtend:\n\t\t\tcase Message::HomeExtend:\n\t\t\tcase Message::VCHomeExtend:\n\t\t\tcase Message::LineEndExtend:\n\t\t\t\treturn true;\n\t\t\tdefault:\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n}\n\n}\n\nSci::Position Editor::HomeWrapPosition(Sci::Position position) {\n\tconst Sci::Position viewLineStart = StartEndDisplayLine(position, true);\n\tconst Sci::Position homePos = MovePositionSoVisible(viewLineStart, -1).Position();\n\tif (position <= homePos)\n\t\treturn pdoc->LineStartPosition(position);\n\treturn homePos;\n}\n\nSci::Position Editor::VCHomeDisplayPosition(Sci::Position position) {\n\tconst Sci::Position homePos = pdoc->VCHomePosition(position);\n\tconst Sci::Position viewLineStart = StartEndDisplayLine(position, true);\n\tif (viewLineStart > homePos) {\n\t\treturn viewLineStart;\n\t}\n\treturn homePos;\n}\n\nSci::Position Editor::VCHomeWrapPosition(Sci::Position position) {\n\tconst Sci::Position homePos = pdoc->VCHomePosition(position);\n\tconst Sci::Position viewLineStart = StartEndDisplayLine(position, true);\n\tif ((viewLineStart < position) && (viewLineStart > homePos)) {\n\t\treturn viewLineStart;\n\t}\n\treturn homePos;\n}\n\nSci::Position Editor::LineEndWrapPosition(Sci::Position position) {\n\tconst Sci::Position endPos = StartEndDisplayLine(position, false);\n\tconst Sci::Position realEndPos = pdoc->LineEndPosition(position);\n\tif (endPos > realEndPos      // if moved past visible EOLs\n\t\t|| position >= endPos) // if at end of display line already\n\t\treturn realEndPos;\n\treturn endPos;\n}\n\nSelectionPosition Editor::PositionMove(Message iMessage, SelectionPosition spCaret) {\n\tswitch (iMessage) {\n\tcase Message::CharLeft:\n\tcase Message::CharLeftExtend:\n\t\tif (spCaret.VirtualSpace()) {\n\t\t\tspCaret.AddVirtualSpace(-1);\n\t\t} else if (!FlagSet(virtualSpaceOptions, VirtualSpace::NoWrapLineStart) || pdoc->GetColumn(spCaret.Position()) > 0) {\n\t\t\tspCaret.Add(-1);\n\t\t}\n\t\treturn spCaret;\n\tcase Message::CharRight:\n\tcase Message::CharRightExtend:\n\t\tif (FlagSet(virtualSpaceOptions, VirtualSpace::UserAccessible) && pdoc->IsLineEndPosition(spCaret.Position())) {\n\t\t\tspCaret.AddVirtualSpace(1);\n\t\t} else {\n\t\t\tspCaret.Add(1);\n\t\t}\n\t\treturn spCaret;\n\tcase Message::WordLeft:\n\tcase Message::WordLeftExtend:\n\t\treturn SelectionPosition(pdoc->NextWordStart(spCaret.Position(), -1));\n\tcase Message::WordRight:\n\tcase Message::WordRightExtend:\n\t\treturn SelectionPosition(pdoc->NextWordStart(spCaret.Position(), 1));\n\tcase Message::WordLeftEnd:\n\tcase Message::WordLeftEndExtend:\n\t\treturn SelectionPosition(pdoc->NextWordEnd(spCaret.Position(), -1));\n\tcase Message::WordRightEnd:\n\tcase Message::WordRightEndExtend:\n\t\treturn SelectionPosition(pdoc->NextWordEnd(spCaret.Position(), 1));\n\tcase Message::WordPartLeft:\n\tcase Message::WordPartLeftExtend:\n\t\treturn SelectionPosition(pdoc->WordPartLeft(spCaret.Position()));\n\tcase Message::WordPartRight:\n\tcase Message::WordPartRightExtend:\n\t\treturn SelectionPosition(pdoc->WordPartRight(spCaret.Position()));\n\tcase Message::Home:\n\tcase Message::HomeExtend:\n\t\treturn SelectionPosition(pdoc->LineStartPosition(spCaret.Position()));\n\tcase Message::HomeDisplay:\n\tcase Message::HomeDisplayExtend:\n\t\treturn SelectionPosition(StartEndDisplayLine(spCaret.Position(), true));\n\tcase Message::HomeWrap:\n\tcase Message::HomeWrapExtend:\n\t\treturn SelectionPosition(HomeWrapPosition(spCaret.Position()));\n\tcase Message::VCHome:\n\tcase Message::VCHomeExtend:\n\t\t// VCHome alternates between beginning of line and beginning of text so may move back or forwards\n\t\treturn SelectionPosition(pdoc->VCHomePosition(spCaret.Position()));\n\tcase Message::VCHomeDisplay:\n\tcase Message::VCHomeDisplayExtend:\n\t\treturn SelectionPosition(VCHomeDisplayPosition(spCaret.Position()));\n\tcase Message::VCHomeWrap:\n\tcase Message::VCHomeWrapExtend:\n\t\treturn SelectionPosition(VCHomeWrapPosition(spCaret.Position()));\n\tcase Message::LineEnd:\n\tcase Message::LineEndExtend:\n\t\treturn SelectionPosition(pdoc->LineEndPosition(spCaret.Position()));\n\tcase Message::LineEndDisplay:\n\tcase Message::LineEndDisplayExtend:\n\t\treturn SelectionPosition(StartEndDisplayLine(spCaret.Position(), false));\n\tcase Message::LineEndWrap:\n\tcase Message::LineEndWrapExtend:\n\t\treturn SelectionPosition(LineEndWrapPosition(spCaret.Position()));\n\n\tdefault:\n\t\tbreak;\n\t}\n\t// Above switch should be exhaustive so this will never be reached.\n\tPLATFORM_ASSERT(false);\n\treturn spCaret;\n}\n\nSelectionRange Editor::SelectionMove(Scintilla::Message iMessage, size_t r) {\n\tconst SelectionPosition spCaretStart = sel.Range(r).caret;\n\tconst SelectionPosition spCaretMoved = PositionMove(iMessage, spCaretStart);\n\n\tconst int directionMove = (spCaretMoved < spCaretStart) ? -1 : 1;\n\tconst SelectionPosition spCaret = MovePositionSoVisible(spCaretMoved, directionMove);\n\n\t// Handle move versus extend, and special behaviour for non-empty left/right\n\tswitch (iMessage) {\n\tcase Message::CharLeft:\n\tcase Message::CharRight:\n\t\tif (sel.Range(r).Empty()) {\n\t\t\treturn SelectionRange(spCaret);\n\t\t}\n\t\tif (iMessage == Message::CharLeft) {\n\t\t\treturn SelectionRange(sel.Range(r).Start());\n\t\t}\n\t\treturn SelectionRange(sel.Range(r).End());\n\n\tcase Message::WordLeft:\n\tcase Message::WordRight:\n\tcase Message::WordLeftEnd:\n\tcase Message::WordRightEnd:\n\tcase Message::WordPartLeft:\n\tcase Message::WordPartRight:\n\tcase Message::Home:\n\tcase Message::HomeDisplay:\n\tcase Message::HomeWrap:\n\tcase Message::VCHome:\n\tcase Message::VCHomeDisplay:\n\tcase Message::VCHomeWrap:\n\tcase Message::LineEnd:\n\tcase Message::LineEndDisplay:\n\tcase Message::LineEndWrap:\n\t\treturn SelectionRange(spCaret);\n\n\tdefault:\n\t\tbreak;\n\t}\n\n\t// All remaining cases are *Extend\n\tconst SelectionRange rangeNew = SelectionRange(spCaret, sel.Range(r).anchor);\n\tsel.TrimOtherSelections(r, rangeNew);\n\treturn rangeNew;\n}\n\nint Editor::HorizontalMove(Message iMessage) {\n\tif (sel.selType == Selection::SelTypes::lines) {\n\t\treturn 0; // horizontal moves with line selection have no effect\n\t}\n\tif (sel.MoveExtends()) {\n\t\tiMessage = WithExtends(iMessage);\n\t}\n\n\tif (!multipleSelection && !sel.IsRectangular()) {\n\t\t// Simplify selection down to 1\n\t\tsel.SetSelection(sel.RangeMain());\n\t}\n\n\t// Invalidate each of the current selections\n\tInvalidateWholeSelection();\n\n\tif (IsRectExtend(iMessage, sel.IsRectangular() && sel.MoveExtends())) {\n\t\tconst SelectionRange rangeBase = sel.IsRectangular() ? sel.Rectangular() : sel.RangeMain();\n\t\tif (!sel.IsRectangular()) {\n\t\t\tsel.DropAdditionalRanges();\n\t\t}\n\t\t// Will change to rectangular if not currently rectangular\n\t\tSelectionPosition spCaret = rangeBase.caret;\n\t\tswitch (iMessage) {\n\t\tcase Message::CharLeftRectExtend:\n\t\tcase Message::CharLeftExtend: // only when sel.IsRectangular() && sel.MoveExtends()\n\t\t\tif (pdoc->IsLineEndPosition(spCaret.Position()) && spCaret.VirtualSpace()) {\n\t\t\t\tspCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);\n\t\t\t} else if (!FlagSet(virtualSpaceOptions, VirtualSpace::NoWrapLineStart) || pdoc->GetColumn(spCaret.Position()) > 0) {\n\t\t\t\tspCaret = SelectionPosition(spCaret.Position() - 1);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase Message::CharRightRectExtend:\n\t\tcase Message::CharRightExtend: // only when sel.IsRectangular() && sel.MoveExtends()\n\t\t\tif (FlagSet(virtualSpaceOptions, VirtualSpace::RectangularSelection) && pdoc->IsLineEndPosition(sel.MainCaret())) {\n\t\t\t\tspCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);\n\t\t\t} else {\n\t\t\t\tspCaret = SelectionPosition(spCaret.Position() + 1);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase Message::HomeRectExtend:\n\t\tcase Message::HomeExtend: // only when sel.IsRectangular() && sel.MoveExtends()\n\t\t\tspCaret = SelectionPosition(pdoc->LineStartPosition(spCaret.Position()));\n\t\t\tbreak;\n\t\tcase Message::VCHomeRectExtend:\n\t\tcase Message::VCHomeExtend: // only when sel.IsRectangular() && sel.MoveExtends()\n\t\t\tspCaret = SelectionPosition(pdoc->VCHomePosition(spCaret.Position()));\n\t\t\tbreak;\n\t\tcase Message::LineEndRectExtend:\n\t\tcase Message::LineEndExtend: // only when sel.IsRectangular() && sel.MoveExtends()\n\t\t\tspCaret = SelectionPosition(pdoc->LineEndPosition(spCaret.Position()));\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t\tconst int directionMove = (spCaret < rangeBase.caret) ? -1 : 1;\n\t\tspCaret = MovePositionSoVisible(spCaret, directionMove);\n\t\tsel.selType = Selection::SelTypes::rectangle;\n\t\tsel.Rectangular() = SelectionRange(spCaret, rangeBase.anchor);\n\t\tSetRectangularRange();\n\t} else if (sel.IsRectangular()) {\n\t\t// Not a rectangular extension so switch to stream.\n\t\tSelectionPosition selAtLimit = (NaturalDirection(iMessage) > 0) ? sel.Limits().end : sel.Limits().start;\n\t\tswitch (iMessage) {\n\t\tcase Message::Home:\n\t\t\tselAtLimit = SelectionPosition(pdoc->LineStartPosition(selAtLimit.Position()));\n\t\t\tbreak;\n\t\tcase Message::VCHome:\n\t\t\tselAtLimit = SelectionPosition(pdoc->VCHomePosition(selAtLimit.Position()));\n\t\t\tbreak;\n\t\tcase Message::LineEnd:\n\t\t\tselAtLimit = SelectionPosition(pdoc->LineEndPosition(selAtLimit.Position()));\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t\tsel.selType = Selection::SelTypes::stream;\n\t\tsel.SetSelection(SelectionRange(selAtLimit));\n\t} else {\n\t\tif (!additionalSelectionTyping) {\n\t\t\tInvalidateWholeSelection();\n\t\t\tsel.DropAdditionalRanges();\n\t\t}\n\t\tfor (size_t r = 0; r < sel.Count(); r++) {\n\t\t\tsel.Range(r) = SelectionMove(iMessage, r);\n\t\t}\n\t}\n\n\tsel.RemoveDuplicates();\n\n\tMovedCaret(sel.RangeMain().caret, SelectionPosition(Sci::invalidPosition), true, caretPolicies);\n\n\t// Invalidate the new state of the selection\n\tInvalidateWholeSelection();\n\n\tSetLastXChosen();\n\t// Need the line moving and so forth from MovePositionTo\n\treturn 0;\n}\n\nint Editor::DelWordOrLine(Message iMessage) {\n\t// Virtual space may be realised for Message::DelWordRight or Message::DelWordRightEnd\n\t// which means 2 actions so wrap in an undo group.\n\n\t// Rightwards and leftwards deletions differ in treatment of virtual space.\n\t// Clear virtual space for leftwards, realise for rightwards.\n\tconst bool leftwards = (iMessage == Message::DelWordLeft) || (iMessage == Message::DelLineLeft);\n\n\tif (!additionalSelectionTyping) {\n\t\tInvalidateWholeSelection();\n\t\tsel.DropAdditionalRanges();\n\t}\n\n\tUndoGroup ug0(pdoc, (sel.Count() > 1) || !leftwards);\n\n\tfor (size_t r = 0; r < sel.Count(); r++) {\n\t\tif (leftwards) {\n\t\t\t// Delete to the left so first clear the virtual space.\n\t\t\tsel.Range(r).ClearVirtualSpace();\n\t\t} else {\n\t\t\t// Delete to the right so first realise the virtual space.\n\t\t\tsel.Range(r) = SelectionRange(\n\t\t\t\tRealizeVirtualSpace(sel.Range(r).caret));\n\t\t}\n\n\t\tRange rangeDelete;\n\t\tswitch (iMessage) {\n\t\tcase Message::DelWordLeft:\n\t\t\trangeDelete = Range(\n\t\t\t\tpdoc->NextWordStart(sel.Range(r).caret.Position(), -1),\n\t\t\t\tsel.Range(r).caret.Position());\n\t\t\tbreak;\n\t\tcase Message::DelWordRight:\n\t\t\trangeDelete = Range(\n\t\t\t\tsel.Range(r).caret.Position(),\n\t\t\t\tpdoc->NextWordStart(sel.Range(r).caret.Position(), 1));\n\t\t\tbreak;\n\t\tcase Message::DelWordRightEnd:\n\t\t\trangeDelete = Range(\n\t\t\t\tsel.Range(r).caret.Position(),\n\t\t\t\tpdoc->NextWordEnd(sel.Range(r).caret.Position(), 1));\n\t\t\tbreak;\n\t\tcase Message::DelLineLeft:\n\t\t\trangeDelete = Range(\n\t\t\t\tpdoc->LineStartPosition(sel.Range(r).caret.Position()),\n\t\t\t\tsel.Range(r).caret.Position());\n\t\t\tbreak;\n\t\tcase Message::DelLineRight:\n\t\t\trangeDelete = Range(\n\t\t\t\tsel.Range(r).caret.Position(),\n\t\t\t\tpdoc->LineEndPosition(sel.Range(r).caret.Position()));\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t\tif (!RangeContainsProtected(rangeDelete.start, rangeDelete.end)) {\n\t\t\tpdoc->DeleteChars(rangeDelete.start, rangeDelete.end - rangeDelete.start);\n\t\t}\n\t}\n\n\t// May need something stronger here: can selections overlap at this point?\n\tsel.RemoveDuplicates();\n\n\tMovedCaret(sel.RangeMain().caret, SelectionPosition(Sci::invalidPosition), true, caretPolicies);\n\n\t// Invalidate the new state of the selection\n\tInvalidateWholeSelection();\n\n\tSetLastXChosen();\n\treturn 0;\n}\n\nint Editor::KeyCommand(Message iMessage) {\n\tswitch (iMessage) {\n\tcase Message::LineDown:\n\t\tCursorUpOrDown(1, Selection::SelTypes::none);\n\t\tbreak;\n\tcase Message::LineDownExtend:\n\t\tCursorUpOrDown(1, Selection::SelTypes::stream);\n\t\tbreak;\n\tcase Message::LineDownRectExtend:\n\t\tCursorUpOrDown(1, Selection::SelTypes::rectangle);\n\t\tbreak;\n\tcase Message::ParaDown:\n\t\tParaUpOrDown(1, Selection::SelTypes::none);\n\t\tbreak;\n\tcase Message::ParaDownExtend:\n\t\tParaUpOrDown(1, Selection::SelTypes::stream);\n\t\tbreak;\n\tcase Message::LineScrollDown:\n\t\tScrollTo(topLine + 1);\n\t\tMoveCaretInsideView(false);\n\t\tbreak;\n\tcase Message::LineUp:\n\t\tCursorUpOrDown(-1, Selection::SelTypes::none);\n\t\tbreak;\n\tcase Message::LineUpExtend:\n\t\tCursorUpOrDown(-1, Selection::SelTypes::stream);\n\t\tbreak;\n\tcase Message::LineUpRectExtend:\n\t\tCursorUpOrDown(-1, Selection::SelTypes::rectangle);\n\t\tbreak;\n\tcase Message::ParaUp:\n\t\tParaUpOrDown(-1, Selection::SelTypes::none);\n\t\tbreak;\n\tcase Message::ParaUpExtend:\n\t\tParaUpOrDown(-1, Selection::SelTypes::stream);\n\t\tbreak;\n\tcase Message::LineScrollUp:\n\t\tScrollTo(topLine - 1);\n\t\tMoveCaretInsideView(false);\n\t\tbreak;\n\n\tcase Message::CharLeft:\n\tcase Message::CharLeftExtend:\n\tcase Message::CharLeftRectExtend:\n\tcase Message::CharRight:\n\tcase Message::CharRightExtend:\n\tcase Message::CharRightRectExtend:\n\tcase Message::WordLeft:\n\tcase Message::WordLeftExtend:\n\tcase Message::WordRight:\n\tcase Message::WordRightExtend:\n\tcase Message::WordLeftEnd:\n\tcase Message::WordLeftEndExtend:\n\tcase Message::WordRightEnd:\n\tcase Message::WordRightEndExtend:\n\tcase Message::WordPartLeft:\n\tcase Message::WordPartLeftExtend:\n\tcase Message::WordPartRight:\n\tcase Message::WordPartRightExtend:\n\tcase Message::Home:\n\tcase Message::HomeExtend:\n\tcase Message::HomeRectExtend:\n\tcase Message::HomeDisplay:\n\tcase Message::HomeDisplayExtend:\n\tcase Message::HomeWrap:\n\tcase Message::HomeWrapExtend:\n\tcase Message::VCHome:\n\tcase Message::VCHomeExtend:\n\tcase Message::VCHomeRectExtend:\n\tcase Message::VCHomeDisplay:\n\tcase Message::VCHomeDisplayExtend:\n\tcase Message::VCHomeWrap:\n\tcase Message::VCHomeWrapExtend:\n\tcase Message::LineEnd:\n\tcase Message::LineEndExtend:\n\tcase Message::LineEndRectExtend:\n\tcase Message::LineEndDisplay:\n\tcase Message::LineEndDisplayExtend:\n\tcase Message::LineEndWrap:\n\tcase Message::LineEndWrapExtend:\n\t\treturn HorizontalMove(iMessage);\n\n\tcase Message::DocumentStart:\n\t\tMovePositionTo(0);\n\t\tSetLastXChosen();\n\t\tbreak;\n\tcase Message::DocumentStartExtend:\n\t\tMovePositionTo(0, Selection::SelTypes::stream);\n\t\tSetLastXChosen();\n\t\tbreak;\n\tcase Message::DocumentEnd:\n\t\tMovePositionTo(pdoc->Length());\n\t\tSetLastXChosen();\n\t\tbreak;\n\tcase Message::DocumentEndExtend:\n\t\tMovePositionTo(pdoc->Length(), Selection::SelTypes::stream);\n\t\tSetLastXChosen();\n\t\tbreak;\n\tcase Message::StutteredPageUp:\n\t\tPageMove(-1, Selection::SelTypes::none, true);\n\t\tbreak;\n\tcase Message::StutteredPageUpExtend:\n\t\tPageMove(-1, Selection::SelTypes::stream, true);\n\t\tbreak;\n\tcase Message::StutteredPageDown:\n\t\tPageMove(1, Selection::SelTypes::none, true);\n\t\tbreak;\n\tcase Message::StutteredPageDownExtend:\n\t\tPageMove(1, Selection::SelTypes::stream, true);\n\t\tbreak;\n\tcase Message::PageUp:\n\t\tPageMove(-1);\n\t\tbreak;\n\tcase Message::PageUpExtend:\n\t\tPageMove(-1, Selection::SelTypes::stream);\n\t\tbreak;\n\tcase Message::PageUpRectExtend:\n\t\tPageMove(-1, Selection::SelTypes::rectangle);\n\t\tbreak;\n\tcase Message::PageDown:\n\t\tPageMove(1);\n\t\tbreak;\n\tcase Message::PageDownExtend:\n\t\tPageMove(1, Selection::SelTypes::stream);\n\t\tbreak;\n\tcase Message::PageDownRectExtend:\n\t\tPageMove(1, Selection::SelTypes::rectangle);\n\t\tbreak;\n\tcase Message::EditToggleOvertype:\n\t\tinOverstrike = !inOverstrike;\n\t\tContainerNeedsUpdate(Update::Selection);\n\t\tShowCaretAtCurrentPosition();\n\t\tSetIdle(true);\n\t\tbreak;\n\tcase Message::Cancel:            \t// Cancel any modes - handled in subclass\n\t\t// Also unselect text\n\t\tCancelModes();\n\t\tif ((sel.Count() > 1) && !sel.IsRectangular()) {\n\t\t\t// Drop additional selections\n\t\t\tInvalidateWholeSelection();\n\t\t\tsel.DropAdditionalRanges();\n\t\t}\n\t\tbreak;\n\tcase Message::DeleteBack:\n\t\tDelCharBack(true);\n\t\tif ((caretSticky == CaretSticky::Off) || (caretSticky == CaretSticky::WhiteSpace)) {\n\t\t\tSetLastXChosen();\n\t\t}\n\t\tEnsureCaretVisible();\n\t\tbreak;\n\tcase Message::DeleteBackNotLine:\n\t\tDelCharBack(false);\n\t\tif ((caretSticky == CaretSticky::Off) || (caretSticky == CaretSticky::WhiteSpace)) {\n\t\t\tSetLastXChosen();\n\t\t}\n\t\tEnsureCaretVisible();\n\t\tbreak;\n\tcase Message::Tab:\n\tcase Message::LineIndent:\n\t\tIndent(true, iMessage == Message::LineIndent);\n\t\tif (caretSticky == CaretSticky::Off) {\n\t\t\tSetLastXChosen();\n\t\t}\n\t\tEnsureCaretVisible();\n\t\tShowCaretAtCurrentPosition();\t\t// Avoid blinking\n\t\tbreak;\n\tcase Message::BackTab:\n\tcase Message::LineDedent:\n\t\tIndent(false, iMessage == Message::LineDedent);\n\t\tif ((caretSticky == CaretSticky::Off) || (caretSticky == CaretSticky::WhiteSpace)) {\n\t\t\tSetLastXChosen();\n\t\t}\n\t\tEnsureCaretVisible();\n\t\tShowCaretAtCurrentPosition();\t\t// Avoid blinking\n\t\tbreak;\n\tcase Message::NewLine:\n\t\tNewLine();\n\t\tbreak;\n\tcase Message::FormFeed:\n\t\tAddChar('\\f');\n\t\tbreak;\n\tcase Message::ZoomIn:\n\t\tif (vs.zoomLevel < 60) {\n\t\t\tvs.zoomLevel++;\n\t\t\tInvalidateStyleRedraw();\n\t\t\tNotifyZoom();\n\t\t}\n\t\tbreak;\n\tcase Message::ZoomOut:\n\t\tif (vs.zoomLevel > -10) {\n\t\t\tvs.zoomLevel--;\n\t\t\tInvalidateStyleRedraw();\n\t\t\tNotifyZoom();\n\t\t}\n\t\tbreak;\n\n\tcase Message::DelWordLeft:\n\tcase Message::DelWordRight:\n\tcase Message::DelWordRightEnd:\n\tcase Message::DelLineLeft:\n\tcase Message::DelLineRight:\n\t\treturn DelWordOrLine(iMessage);\n\n\tcase Message::LineCopy: {\n\t\t\tconst Sci::Line lineStart = pdoc->SciLineFromPosition(SelectionStart().Position());\n\t\t\tconst Sci::Line lineEnd = pdoc->SciLineFromPosition(SelectionEnd().Position());\n\t\t\tCopyRangeToClipboard(pdoc->LineStart(lineStart),\n\t\t\t\tpdoc->LineStart(lineEnd + 1));\n\t\t}\n\t\tbreak;\n\tcase Message::LineCut: {\n\t\t\tconst Sci::Line lineStart = pdoc->SciLineFromPosition(SelectionStart().Position());\n\t\t\tconst Sci::Line lineEnd = pdoc->SciLineFromPosition(SelectionEnd().Position());\n\t\t\tconst Sci::Position start = pdoc->LineStart(lineStart);\n\t\t\tconst Sci::Position end = pdoc->LineStart(lineEnd + 1);\n\t\t\tSetSelection(start, end);\n\t\t\tCut();\n\t\t\tSetLastXChosen();\n\t\t}\n\t\tbreak;\n\tcase Message::LineDelete:\n\t\tLineDelete();\n\t\tbreak;\n\tcase Message::LineTranspose:\n\t\tLineTranspose();\n\t\tbreak;\n\tcase Message::LineReverse:\n\t\tLineReverse();\n\t\tbreak;\n\tcase Message::LineDuplicate:\n\t\tDuplicate(true);\n\t\tbreak;\n\tcase Message::SelectionDuplicate:\n\t\tDuplicate(false);\n\t\tbreak;\n\tcase Message::LowerCase:\n\t\tChangeCaseOfSelection(CaseMapping::lower);\n\t\tbreak;\n\tcase Message::UpperCase:\n\t\tChangeCaseOfSelection(CaseMapping::upper);\n\t\tbreak;\n\tcase Message::ScrollToStart:\n\t\tScrollTo(0);\n\t\tbreak;\n\tcase Message::ScrollToEnd:\n\t\tScrollTo(MaxScrollPos());\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n\treturn 0;\n}\n\nint Editor::KeyDefault(Keys, KeyMod) {\n\treturn 0;\n}\n\nint Editor::KeyDownWithModifiers(Keys key, KeyMod modifiers, bool *consumed) {\n\tDwellEnd(false);\n\tconst Message msg = kmap.Find(key, modifiers);\n\tif (msg != static_cast<Message>(0)) {\n\t\tif (consumed)\n\t\t\t*consumed = true;\n\t\treturn static_cast<int>(WndProc(msg, 0, 0));\n\t}\n\tif (consumed)\n\t\t*consumed = false;\n\treturn KeyDefault(key, modifiers);\n}\n\nvoid Editor::Indent(bool forwards, bool lineIndent) {\n\tUndoGroup ug(pdoc);\n\tfor (size_t r=0; r<sel.Count(); r++) {\n\t\tconst Sci::Line lineOfAnchor =\n\t\t\tpdoc->SciLineFromPosition(sel.Range(r).anchor.Position());\n\t\tSci::Position caretPosition = sel.Range(r).caret.Position();\n\t\tconst Sci::Line lineCurrentPos = pdoc->SciLineFromPosition(caretPosition);\n\t\tif (lineOfAnchor == lineCurrentPos && !lineIndent) {\n\t\t\tif (forwards) {\n\t\t\t\tpdoc->DeleteChars(sel.Range(r).Start().Position(), sel.Range(r).Length());\n\t\t\t\tcaretPosition = sel.Range(r).caret.Position();\n\t\t\t\tif (pdoc->GetColumn(caretPosition) <= pdoc->GetColumn(pdoc->GetLineIndentPosition(lineCurrentPos)) &&\n\t\t\t\t\t\tpdoc->tabIndents) {\n\t\t\t\t\tconst int indentation = pdoc->GetLineIndentation(lineCurrentPos);\n\t\t\t\t\tconst int indentationStep = pdoc->IndentSize();\n\t\t\t\t\tconst Sci::Position posSelect = pdoc->SetLineIndentation(\n\t\t\t\t\t\tlineCurrentPos, indentation + indentationStep - indentation % indentationStep);\n\t\t\t\t\tsel.Range(r) = SelectionRange(posSelect);\n\t\t\t\t} else {\n\t\t\t\t\tif (pdoc->useTabs) {\n\t\t\t\t\t\tconst Sci::Position lengthInserted = pdoc->InsertString(caretPosition, \"\\t\", 1);\n\t\t\t\t\t\tsel.Range(r) = SelectionRange(caretPosition + lengthInserted);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tint numSpaces = (pdoc->tabInChars) -\n\t\t\t\t\t\t\t\tstatic_cast<int>((pdoc->GetColumn(caretPosition) % (pdoc->tabInChars)));\n\t\t\t\t\t\tif (numSpaces < 1)\n\t\t\t\t\t\t\tnumSpaces = pdoc->tabInChars;\n\t\t\t\t\t\tconst std::string spaceText(numSpaces, ' ');\n\t\t\t\t\t\tconst Sci::Position lengthInserted = pdoc->InsertString(caretPosition, spaceText);\n\t\t\t\t\t\tsel.Range(r) = SelectionRange(caretPosition + lengthInserted);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (pdoc->GetColumn(caretPosition) <= pdoc->GetLineIndentation(lineCurrentPos) &&\n\t\t\t\t\t\tpdoc->tabIndents) {\n\t\t\t\t\tconst int indentation = pdoc->GetLineIndentation(lineCurrentPos);\n\t\t\t\t\tconst int indentationStep = pdoc->IndentSize();\n\t\t\t\t\tconst Sci::Position posSelect = pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationStep);\n\t\t\t\t\tsel.Range(r) = SelectionRange(posSelect);\n\t\t\t\t} else {\n\t\t\t\t\tSci::Position newColumn = ((pdoc->GetColumn(caretPosition) - 1) / pdoc->tabInChars) *\n\t\t\t\t\t\t\tpdoc->tabInChars;\n\t\t\t\t\tif (newColumn < 0)\n\t\t\t\t\t\tnewColumn = 0;\n\t\t\t\t\tSci::Position newPos = caretPosition;\n\t\t\t\t\twhile (pdoc->GetColumn(newPos) > newColumn)\n\t\t\t\t\t\tnewPos--;\n\t\t\t\t\tsel.Range(r) = SelectionRange(newPos);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\t// Multiline or LineIndent\n\t\t\tconst Sci::Position anchorPosOnLine = sel.Range(r).anchor.Position() -\n\t\t\t\tpdoc->LineStart(lineOfAnchor);\n\t\t\tconst Sci::Position currentPosPosOnLine = caretPosition -\n\t\t\t\tpdoc->LineStart(lineCurrentPos);\n\t\t\t// Multiple lines selected so indent / dedent\n\t\t\tconst Sci::Line lineTopSel = std::min(lineOfAnchor, lineCurrentPos);\n\t\t\tSci::Line lineBottomSel = std::max(lineOfAnchor, lineCurrentPos);\n\t\t\tif (pdoc->LineStart(lineBottomSel) == sel.Range(r).anchor.Position() || pdoc->LineStart(lineBottomSel) == caretPosition)\n\t\t\t\tlineBottomSel--;  \t// If not selecting any characters on a line, do not indent\n\t\t\tpdoc->Indent(forwards, lineBottomSel, lineTopSel);\n\t\t\tif (lineOfAnchor < lineCurrentPos) {\n\t\t\t\tif (currentPosPosOnLine == 0)\n\t\t\t\t\tsel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos),\n\t\t\t\t\t\tpdoc->LineStart(lineOfAnchor));\n\t\t\t\telse\n\t\t\t\t\tsel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos + 1),\n\t\t\t\t\t\tpdoc->LineStart(lineOfAnchor));\n\t\t\t} else {\n\t\t\t\tif (anchorPosOnLine == 0)\n\t\t\t\t\tsel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos),\n\t\t\t\t\t\tpdoc->LineStart(lineOfAnchor));\n\t\t\t\telse\n\t\t\t\t\tsel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos),\n\t\t\t\t\t\tpdoc->LineStart(lineOfAnchor + 1));\n\t\t\t}\n\t\t}\n\t}\n\tContainerNeedsUpdate(Update::Selection);\n}\n\nstd::unique_ptr<CaseFolder> Editor::CaseFolderForEncoding() {\n\t// Simple default that only maps ASCII upper case to lower case.\n\treturn std::make_unique<CaseFolderTable>();\n}\n\n/**\n * Search of a text in the document, in the given range.\n * @return The position of the found text, -1 if not found.\n */\nSci::Position Editor::FindText(\n    uptr_t wParam,\t\t///< Search modes : @c FindOption::MatchCase, @c FindOption::WholeWord,\n    ///< @c FindOption::WordStart, @c FindOption::RegExp or @c FindOption::Posix.\n    sptr_t lParam) {\t///< @c Sci_TextToFind structure: The text to search for in the given range.\n\n\tTextToFind *ft = static_cast<TextToFind *>(PtrFromSPtr(lParam));\n\tSci::Position lengthFound = strlen(ft->lpstrText);\n\tif (!pdoc->HasCaseFolder())\n\t\tpdoc->SetCaseFolder(CaseFolderForEncoding());\n\ttry {\n\t\tconst Sci::Position pos = pdoc->FindText(\n\t\t\tstatic_cast<Sci::Position>(ft->chrg.cpMin),\n\t\t\tstatic_cast<Sci::Position>(ft->chrg.cpMax),\n\t\t\tft->lpstrText,\n\t\t\tstatic_cast<FindOption>(wParam),\n\t\t\t&lengthFound);\n\t\tif (pos != -1) {\n\t\t\tft->chrgText.cpMin = static_cast<Sci_PositionCR>(pos);\n\t\t\tft->chrgText.cpMax = static_cast<Sci_PositionCR>(pos + lengthFound);\n\t\t}\n\t\treturn pos;\n\t} catch (RegexError &) {\n\t\terrorStatus = Status::RegEx;\n\t\treturn -1;\n\t}\n}\n\n/**\n * Search of a text in the document, in the given range.\n * @return The position of the found text, -1 if not found.\n */\nSci::Position Editor::FindTextFull(\n    uptr_t wParam,\t\t///< Search modes : @c FindOption::MatchCase, @c FindOption::WholeWord,\n    ///< @c FindOption::WordStart, @c FindOption::RegExp or @c FindOption::Posix.\n    sptr_t lParam) {\t///< @c Sci_TextToFindFull structure: The text to search for in the given range.\n\n\tTextToFindFull *ft = static_cast<TextToFindFull *>(PtrFromSPtr(lParam));\n\tSci::Position lengthFound = strlen(ft->lpstrText);\n\tif (!pdoc->HasCaseFolder())\n\t\tpdoc->SetCaseFolder(CaseFolderForEncoding());\n\ttry {\n\t\tconst Sci::Position pos = pdoc->FindText(\n\t\t\tft->chrg.cpMin,\n\t\t\tft->chrg.cpMax,\n\t\t\tft->lpstrText,\n\t\t\tstatic_cast<FindOption>(wParam),\n\t\t\t&lengthFound);\n\t\tif (pos != -1) {\n\t\t\tft->chrgText.cpMin = pos;\n\t\t\tft->chrgText.cpMax = pos + lengthFound;\n\t\t}\n\t\treturn pos;\n\t} catch (RegexError &) {\n\t\terrorStatus = Status::RegEx;\n\t\treturn -1;\n\t}\n}\n\n/**\n * Relocatable search support : Searches relative to current selection\n * point and sets the selection to the found text range with\n * each search.\n */\n/**\n * Anchor following searches at current selection start: This allows\n * multiple incremental interactive searches to be macro recorded\n * while still setting the selection to found text so the find/select\n * operation is self-contained.\n */\nvoid Editor::SearchAnchor() noexcept {\n\tsearchAnchor = SelectionStart().Position();\n}\n\n/**\n * Find text from current search anchor: Must call @c SearchAnchor first.\n * Used for next text and previous text requests.\n * @return The position of the found text, -1 if not found.\n */\nSci::Position Editor::SearchText(\n    Message iMessage,\t\t///< Accepts both @c Message::SearchNext and @c Message::SearchPrev.\n    uptr_t wParam,\t\t\t\t///< Search modes : @c FindOption::MatchCase, @c FindOption::WholeWord,\n    ///< @c FindOption::WordStart, @c FindOption::RegExp or @c FindOption::Posix.\n    sptr_t lParam) {\t\t\t///< The text to search for.\n\n\tconst char *txt = ConstCharPtrFromSPtr(lParam);\n\tSci::Position pos = Sci::invalidPosition;\n\tSci::Position lengthFound = strlen(txt);\n\tif (!pdoc->HasCaseFolder())\n\t\tpdoc->SetCaseFolder(CaseFolderForEncoding());\n\ttry {\n\t\tif (iMessage == Message::SearchNext) {\n\t\t\tpos = pdoc->FindText(searchAnchor, pdoc->Length(), txt,\n\t\t\t\t\tstatic_cast<FindOption>(wParam),\n\t\t\t\t\t&lengthFound);\n\t\t} else {\n\t\t\tpos = pdoc->FindText(searchAnchor, 0, txt,\n\t\t\t\t\tstatic_cast<FindOption>(wParam),\n\t\t\t\t\t&lengthFound);\n\t\t}\n\t} catch (RegexError &) {\n\t\terrorStatus = Status::RegEx;\n\t\treturn Sci::invalidPosition;\n\t}\n\tif (pos != Sci::invalidPosition) {\n\t\tSetSelection(pos, pos + lengthFound);\n\t}\n\n\treturn pos;\n}\n\nstd::string Editor::CaseMapString(const std::string &s, CaseMapping caseMapping) {\n\tstd::string ret(s);\n\tfor (char &ch : ret) {\n\t\tswitch (caseMapping) {\n\t\t\tcase CaseMapping::upper:\n\t\t\t\tch = MakeUpperCase(ch);\n\t\t\t\tbreak;\n\t\t\tcase CaseMapping::lower:\n\t\t\t\tch = MakeLowerCase(ch);\n\t\t\t\tbreak;\n\t\t\tdefault:\t// no action\n\t\t\t\tbreak;\n\t\t}\n\t}\n\treturn ret;\n}\n\n/**\n * Search for text in the target range of the document.\n * @return The position of the found text, -1 if not found.\n */\nSci::Position Editor::SearchInTarget(const char *text, Sci::Position length) {\n\tSci::Position lengthFound = length;\n\n\tif (!pdoc->HasCaseFolder())\n\t\tpdoc->SetCaseFolder(CaseFolderForEncoding());\n\ttry {\n\t\tconst Sci::Position pos = pdoc->FindText(targetRange.start.Position(), targetRange.end.Position(), text,\n\t\t\t\tsearchFlags,\n\t\t\t\t&lengthFound);\n\t\tif (pos != -1) {\n\t\t\ttargetRange.start.SetPosition(pos);\n\t\t\ttargetRange.end.SetPosition(pos + lengthFound);\n\t\t}\n\t\treturn pos;\n\t} catch (RegexError &) {\n\t\terrorStatus = Status::RegEx;\n\t\treturn -1;\n\t}\n}\n\nvoid Editor::GoToLine(Sci::Line lineNo) {\n\tif (lineNo > pdoc->LinesTotal())\n\t\tlineNo = pdoc->LinesTotal();\n\tif (lineNo < 0)\n\t\tlineNo = 0;\n\tSetEmptySelection(pdoc->LineStart(lineNo));\n\tShowCaretAtCurrentPosition();\n\tEnsureCaretVisible();\n}\n\nnamespace {\n\nbool Close(Point pt1, Point pt2, Point threshold) noexcept {\n\tconst Point ptDifference = pt2 - pt1;\n\tif (std::abs(ptDifference.x) > threshold.x)\n\t\treturn false;\n\tif (std::abs(ptDifference.y) > threshold.y)\n\t\treturn false;\n\treturn true;\n}\n\nconstexpr bool AllowVirtualSpace(VirtualSpace virtualSpaceOptions, bool rectangular) noexcept {\n\treturn FlagSet(virtualSpaceOptions, (rectangular ? VirtualSpace::RectangularSelection : VirtualSpace::UserAccessible));\n}\n\n}\n\nstd::string Editor::RangeText(Sci::Position start, Sci::Position end) const {\n\tif (start < end) {\n\t\tconst Sci::Position len = end - start;\n\t\tstd::string ret(len, '\\0');\n\t\tpdoc->GetCharRange(ret.data(), start, len);\n\t\treturn ret;\n\t}\n\treturn {};\n}\n\nbool Editor::CopyLineRange(SelectionText *ss, bool allowProtected) {\n\tconst Sci::Line currentLine = pdoc->SciLineFromPosition(sel.MainCaret());\n\tconst Sci::Position start = pdoc->LineStart(currentLine);\n\tconst Sci::Position end = pdoc->LineEnd(currentLine);\n\n\tif (allowProtected || !RangeContainsProtected(start, end)) {\n\t\tstd::string text = RangeText(start, end);\n\t\ttext.append(pdoc->EOLString());\n\t\tss->Copy(text, pdoc->dbcsCodePage,\n\t\t\tvs.styles[StyleDefault].characterSet, false, true);\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nvoid Editor::CopySelectionRange(SelectionText *ss, bool allowLineCopy) {\n\tif (sel.Empty()) {\n\t\tif (allowLineCopy) {\n\t\t\tCopyLineRange(ss);\n\t\t}\n\t} else {\n\t\tstd::string text;\n\t\tstd::vector<SelectionRange> rangesInOrder = sel.RangesCopy();\n\t\tif (sel.selType == Selection::SelTypes::rectangle)\n\t\t\tstd::sort(rangesInOrder.begin(), rangesInOrder.end());\n\t\tconst std::string_view separator = (sel.selType == Selection::SelTypes::rectangle) ? pdoc->EOLString() : copySeparator;\n\t\tfor (size_t part = 0; part < rangesInOrder.size(); part++) {\n\t\t\ttext.append(RangeText(rangesInOrder[part].Start().Position(), rangesInOrder[part].End().Position()));\n\t\t\tif ((sel.selType == Selection::SelTypes::rectangle) || (part < rangesInOrder.size() - 1)) {\n\t\t\t\t// Append unless simple selection or last part of multiple selection\n\t\t\t\ttext.append(separator);\n\t\t\t}\n\t\t}\n\t\tss->Copy(text, pdoc->dbcsCodePage,\n\t\t\tvs.styles[StyleDefault].characterSet, sel.IsRectangular(), sel.selType == Selection::SelTypes::lines);\n\t}\n}\n\nvoid Editor::CopyRangeToClipboard(Sci::Position start, Sci::Position end) {\n\tstart = pdoc->ClampPositionIntoDocument(start);\n\tend = pdoc->ClampPositionIntoDocument(end);\n\tSelectionText selectedText;\n\tstd::string text = RangeText(start, end);\n\tselectedText.Copy(text,\n\t\tpdoc->dbcsCodePage, vs.styles[StyleDefault].characterSet, false, false);\n\tCopyToClipboard(selectedText);\n}\n\nvoid Editor::CopyText(size_t length, const char *text) {\n\tSelectionText selectedText;\n\tselectedText.Copy(std::string(text, length),\n\t\tpdoc->dbcsCodePage, vs.styles[StyleDefault].characterSet, false, false);\n\tCopyToClipboard(selectedText);\n}\n\nvoid Editor::SetDragPosition(SelectionPosition newPos) {\n\tif (newPos.Position() >= 0) {\n\t\tnewPos = MovePositionOutsideChar(newPos, 1);\n\t\tposDrop = newPos;\n\t}\n\tif (!(posDrag == newPos)) {\n\t\tconst CaretPolicies dragCaretPolicies = {\n\t\t\tCaretPolicySlop(CaretPolicy::Slop | CaretPolicy::Strict | CaretPolicy::Even, 50),\n\t\t\tCaretPolicySlop(CaretPolicy::Slop | CaretPolicy::Strict | CaretPolicy::Even, 2)\n\t\t};\n\t\tMovedCaret(newPos, posDrag, true, dragCaretPolicies);\n\n\t\tcaret.on = true;\n\t\tFineTickerCancel(TickReason::caret);\n\t\tif ((caret.active) && (caret.period > 0) && (newPos.Position() < 0))\n\t\t\tFineTickerStart(TickReason::caret, caret.period, caret.period/10);\n\t\tInvalidateCaret();\n\t\tposDrag = newPos;\n\t\tInvalidateCaret();\n\t}\n}\n\nvoid Editor::DisplayCursor(Window::Cursor c) {\n\tif (cursorMode == CursorShape::Normal)\n\t\twMain.SetCursor(c);\n\telse\n\t\twMain.SetCursor(static_cast<Window::Cursor>(cursorMode));\n}\n\nbool Editor::DragThreshold(Point ptStart, Point ptNow) {\n\tconst Point ptDiff = ptStart - ptNow;\n\tconst XYPOSITION distanceSquared = ptDiff.x * ptDiff.x + ptDiff.y * ptDiff.y;\n\treturn distanceSquared > 16.0f;\n}\n\nvoid Editor::StartDrag() {\n\t// Always handled by subclasses\n}\n\nvoid Editor::DropAt(SelectionPosition position, const char *value, size_t lengthValue, bool moving, bool rectangular) {\n\t//Platform::DebugPrintf(\"DropAt %d %d\\n\", inDragDrop, position);\n\tif (inDragDrop == DragDrop::dragging)\n\t\tdropWentOutside = false;\n\n\tconst bool positionWasInSelection = PositionInSelection(position.Position());\n\n\tconst bool positionOnEdgeOfSelection =\n\t    (position == SelectionStart()) || (position == SelectionEnd());\n\n\tif ((inDragDrop != DragDrop::dragging) || !(positionWasInSelection) ||\n\t        (positionOnEdgeOfSelection && !moving)) {\n\n\t\tconst SelectionPosition selStart = SelectionStart();\n\t\tconst SelectionPosition selEnd = SelectionEnd();\n\n\t\tUndoGroup ug(pdoc);\n\n\t\tSelectionPosition positionAfterDeletion = position;\n\t\tif ((inDragDrop == DragDrop::dragging) && moving) {\n\t\t\t// Remove dragged out text\n\t\t\tif (rectangular || sel.selType == Selection::SelTypes::lines) {\n\t\t\t\tfor (size_t r=0; r<sel.Count(); r++) {\n\t\t\t\t\tif (position >= sel.Range(r).Start()) {\n\t\t\t\t\t\tif (position > sel.Range(r).End()) {\n\t\t\t\t\t\t\tpositionAfterDeletion.Add(-sel.Range(r).Length());\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tpositionAfterDeletion.Add(-SelectionRange(position, sel.Range(r).Start()).Length());\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (position > selStart) {\n\t\t\t\t\tpositionAfterDeletion.Add(-SelectionRange(selEnd, selStart).Length());\n\t\t\t\t}\n\t\t\t}\n\t\t\tClearSelection();\n\t\t}\n\t\tposition = positionAfterDeletion;\n\n\t\tstd::string convertedText = Document::TransformLineEnds(value, lengthValue, pdoc->eolMode);\n\n\t\tif (rectangular) {\n\t\t\tPasteRectangular(position, convertedText.c_str(), convertedText.length());\n\t\t\t// Should try to select new rectangle but it may not be a rectangle now so just select the drop position\n\t\t\tSetEmptySelection(position);\n\t\t} else {\n\t\t\tposition = MovePositionOutsideChar(position, sel.MainCaret() - position.Position());\n\t\t\tposition = RealizeVirtualSpace(position);\n\t\t\tconst Sci::Position lengthInserted = pdoc->InsertString(\n\t\t\t\tposition.Position(), convertedText);\n\t\t\tif (lengthInserted > 0) {\n\t\t\t\tSelectionPosition posAfterInsertion = position;\n\t\t\t\tposAfterInsertion.Add(lengthInserted);\n\t\t\t\tSetSelection(posAfterInsertion, position);\n\t\t\t}\n\t\t}\n\t} else if (inDragDrop == DragDrop::dragging) {\n\t\tSetEmptySelection(position);\n\t}\n}\n\nvoid Editor::DropAt(SelectionPosition position, const char *value, bool moving, bool rectangular) {\n\tDropAt(position, value, strlen(value), moving, rectangular);\n}\n\n/**\n * @return true if given position is inside the selection,\n */\nbool Editor::PositionInSelection(Sci::Position pos) {\n\tpos = MovePositionOutsideChar(pos, sel.MainCaret() - pos);\n\tfor (size_t r=0; r<sel.Count(); r++) {\n\t\tif (sel.Range(r).Contains(pos))\n\t\t\treturn true;\n\t}\n\treturn false;\n}\n\nbool Editor::PointInSelection(Point pt) {\n\tconst SelectionPosition pos = SPositionFromLocation(pt, false, true);\n\tconst Point ptPos = LocationFromPosition(pos);\n\tfor (size_t r=0; r<sel.Count(); r++) {\n\t\tconst SelectionRange &range = sel.Range(r);\n\t\tif (range.Contains(pos)) {\n\t\t\tbool hit = true;\n\t\t\tif (pos == range.Start()) {\n\t\t\t\t// see if just before selection\n\t\t\t\tif (pt.x < ptPos.x) {\n\t\t\t\t\thit = false;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (pos == range.End()) {\n\t\t\t\t// see if just after selection\n\t\t\t\tif (pt.x > ptPos.x) {\n\t\t\t\t\thit = false;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (hit)\n\t\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\nptrdiff_t Editor::SelectionFromPoint(Point pt) {\n\t// Prioritize checking inside non-empty selections since each character will be inside only 1\n\tconst SelectionPosition posChar = SPositionFromLocation(pt, true, true);\n\tfor (size_t r = 0; r < sel.Count(); r++) {\n\t\tif (sel.Range(r).ContainsCharacter(posChar)) {\n\t\t\treturn r;\n\t\t}\n\t}\n\n\t// Then check if near empty selections as may be near more than 1\n\tconst SelectionPosition pos = SPositionFromLocation(pt, true, false);\n\tfor (size_t r = 0; r < sel.Count(); r++) {\n\t\tconst SelectionRange &range = sel.Range(r);\n\t\tif ((range.Empty()) && (pos == range.caret)) {\n\t\t\treturn r;\n\t\t}\n\t}\n\n\t// No selection at point\n\treturn -1;\n}\n\nbool Editor::PointInSelMargin(Point pt) const {\n\t// Really means: \"Point in a margin\"\n\tif (vs.fixedColumnWidth > 0) {\t// There is a margin\n\t\tPRectangle rcSelMargin = GetClientRectangle();\n\t\trcSelMargin.right = static_cast<XYPOSITION>(vs.textStart - vs.leftMarginWidth);\n\t\trcSelMargin.left = static_cast<XYPOSITION>(vs.textStart - vs.fixedColumnWidth);\n\t\tconst Point ptOrigin = GetVisibleOriginInMain();\n\t\trcSelMargin.Move(0, -ptOrigin.y);\n\t\treturn rcSelMargin.ContainsWholePixel(pt);\n\t}\n\treturn false;\n}\n\nWindow::Cursor Editor::GetMarginCursor(Point pt) const noexcept {\n\tint x = 0;\n\tfor (const MarginStyle &m : vs.ms) {\n\t\tif ((pt.x >= x) && (pt.x < x + m.width))\n\t\t\treturn static_cast<Window::Cursor>(m.cursor);\n\t\tx += m.width;\n\t}\n\treturn Window::Cursor::reverseArrow;\n}\n\nvoid Editor::DropSelection(size_t part) {\n\tsel.DropSelection(part);\n\tContainerNeedsUpdate(Update::Selection);\n\tRedraw();\n}\n\nvoid Editor::TrimAndSetSelection(Sci::Position currentPos_, Sci::Position anchor_) {\n\tsel.TrimSelection(SelectionRange(currentPos_, anchor_));\n\tSetSelection(currentPos_, anchor_);\n}\n\nvoid Editor::LineSelection(Sci::Position lineCurrentPos_, Sci::Position lineAnchorPos_, bool wholeLine) {\n\tSci::Position selCurrentPos;\n\tSci::Position selAnchorPos;\n\tif (wholeLine) {\n\t\t//Au: if the last selected line is a fold-point, add hidden lines after it to the selection.\n\t\tSci::Line lineCurrent_ = pdoc->SciLineFromPosition(lineCurrentPos_);\n\t\tSci::Line lineAnchor_ = pdoc->SciLineFromPosition(lineAnchorPos_);\n\t\tif(lineAnchorPos_ > lineCurrentPos_) {\n\t\t\tlineAnchor_++;\n\t\t} else {\n\t\t\twhile(!pcs->GetVisible(++lineCurrent_)) { }\n\t\t}\n\t\tselCurrentPos = pdoc->LineStart(lineCurrent_);\n\t\tselAnchorPos = pdoc->LineStart(lineAnchor_);\n\n\t\t//const Sci::Line lineCurrent_ = pdoc->SciLineFromPosition(lineCurrentPos_);\n\t\t//const Sci::Line lineAnchor_ = pdoc->SciLineFromPosition(lineAnchorPos_);\n\t\t//if (lineAnchorPos_ < lineCurrentPos_) {\n\t\t//\tselCurrentPos = pdoc->LineStart(lineCurrent_ + 1);\n\t\t//\tselAnchorPos = pdoc->LineStart(lineAnchor_);\n\t\t//} else if (lineAnchorPos_ > lineCurrentPos_) {\n\t\t//\tselCurrentPos = pdoc->LineStart(lineCurrent_);\n\t\t//\tselAnchorPos = pdoc->LineStart(lineAnchor_ + 1);\n\t\t//} else { // Same line, select it\n\t\t//\tselCurrentPos = pdoc->LineStart(lineAnchor_ + 1);\n\t\t//\tselAnchorPos = pdoc->LineStart(lineAnchor_);\n\t\t//}\n\t} else {\n\t\tif (lineAnchorPos_ < lineCurrentPos_) {\n\t\t\tselCurrentPos = StartEndDisplayLine(lineCurrentPos_, false) + 1;\n\t\t\tselCurrentPos = pdoc->MovePositionOutsideChar(selCurrentPos, 1);\n\t\t\tselAnchorPos = StartEndDisplayLine(lineAnchorPos_, true);\n\t\t} else if (lineAnchorPos_ > lineCurrentPos_) {\n\t\t\tselCurrentPos = StartEndDisplayLine(lineCurrentPos_, true);\n\t\t\tselAnchorPos = StartEndDisplayLine(lineAnchorPos_, false) + 1;\n\t\t\tselAnchorPos = pdoc->MovePositionOutsideChar(selAnchorPos, 1);\n\t\t} else { // Same line, select it\n\t\t\tselCurrentPos = StartEndDisplayLine(lineAnchorPos_, false) + 1;\n\t\t\tselCurrentPos = pdoc->MovePositionOutsideChar(selCurrentPos, 1);\n\t\t\tselAnchorPos = StartEndDisplayLine(lineAnchorPos_, true);\n\t\t}\n\t}\n\tTrimAndSetSelection(selCurrentPos, selAnchorPos);\n}\n\nvoid Editor::WordSelection(Sci::Position pos) {\n\tif (pos < wordSelectAnchorStartPos) {\n\t\t// Extend backward to the word containing pos.\n\t\t// Skip ExtendWordSelect if the line is empty or if pos is after the last character.\n\t\t// This ensures that a series of empty lines isn't counted as a single \"word\".\n\t\tif (!pdoc->IsLineEndPosition(pos))\n\t\t\tpos = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(pos + 1, 1), -1);\n\t\tTrimAndSetSelection(pos, wordSelectAnchorEndPos);\n\t} else if (pos > wordSelectAnchorEndPos) {\n\t\t// Extend forward to the word containing the character to the left of pos.\n\t\t// Skip ExtendWordSelect if the line is empty or if pos is the first position on the line.\n\t\t// This ensures that a series of empty lines isn't counted as a single \"word\".\n\t\tif (pos > pdoc->LineStartPosition(pos))\n\t\t\tpos = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(pos - 1, -1), 1);\n\t\tTrimAndSetSelection(pos, wordSelectAnchorStartPos);\n\t} else {\n\t\t// Select only the anchored word\n\t\tif (pos >= originalAnchorPos)\n\t\t\tTrimAndSetSelection(wordSelectAnchorEndPos, wordSelectAnchorStartPos);\n\t\telse\n\t\t\tTrimAndSetSelection(wordSelectAnchorStartPos, wordSelectAnchorEndPos);\n\t}\n}\n\nvoid Editor::DwellEnd(bool mouseMoved) {\n\tif (mouseMoved)\n\t\tticksToDwell = dwellDelay;\n\telse\n\t\tticksToDwell = TimeForever;\n\tif (dwelling && (dwellDelay < TimeForever)) {\n\t\tdwelling = false;\n\t\tNotifyDwelling(ptMouseLast, dwelling);\n\t}\n\tFineTickerCancel(TickReason::dwell);\n}\n\nvoid Editor::MouseLeave() {\n\tSetHotSpotRange(nullptr);\n\tSetHoverIndicatorPosition(Sci::invalidPosition);\n\tif (!HaveMouseCapture()) {\n\t\tptMouseLast = Point(-1, -1);\n\t\tDwellEnd(true);\n\t}\n}\n\nvoid Editor::ButtonDownWithModifiers(Point pt, unsigned int curTime, KeyMod modifiers) {\n\tSetHoverIndicatorPoint(pt);\n\t//Platform::DebugPrintf(\"ButtonDown %d %d = %d alt=%d %d\\n\", curTime, lastClickTime, curTime - lastClickTime, alt, inDragDrop);\n\tptMouseLast = pt;\n\tconst bool ctrl = FlagSet(modifiers, KeyMod::Ctrl);\n\tconst bool shift = FlagSet(modifiers, KeyMod::Shift);\n\tconst bool alt = FlagSet(modifiers, KeyMod::Alt);\n\tconst SelectionPosition clickPos = SPositionFromLocation(pt, false, false, AllowVirtualSpace(virtualSpaceOptions, alt));\n\tconst SelectionPosition newPos = MovePositionOutsideChar(clickPos, sel.MainCaret() - clickPos.Position());\n\tconst SelectionPosition newCharPos = MovePositionOutsideChar(\n\t\tSPositionFromLocation(pt, false, true, false), -1);\n\tinDragDrop = DragDrop::none;\n\tsel.SetMoveExtends(false);\n\n\tif (NotifyMarginClick(pt, modifiers))\n\t\treturn;\n\n\tNotifyIndicatorClick(true, newPos.Position(), modifiers);\n\n\tconst bool multiClick = (curTime < (lastClickTime + Platform::DoubleClickTime())) && Close(pt, lastClick, doubleClickCloseThreshold);\n\tlastClickTime = curTime;\n\tlastClick = pt;\n\n\tconst bool inSelMargin = PointInSelMargin(pt);\n\t\n\t//Au: commented out. In the future LA may use Ctrl+click for multiple selections.\n\t//// In margin ctrl+(double)click should always select everything\n\t//if (ctrl && inSelMargin) {\n\t//\tSelectAll();\n\t//\treturn;\n\t//}\n\t\n\tif (shift && !inSelMargin) {\n\t\tSetSelection(newPos);\n\t}\n\tif (multiClick) {\n\t\t//Platform::DebugPrintf(\"Double click %d %d = %d\\n\", curTime, lastClickTime, curTime - lastClickTime);\n\t\tChangeMouseCapture(true);\n\t\tif (!ctrl || !multipleSelection || (selectionUnit != TextUnit::character && selectionUnit != TextUnit::word))\n\t\t\tSetEmptySelection(newPos.Position());\n\t\tbool doubleClick = false;\n\t\tif (inSelMargin) {\n\t\t\t// Inside margin selection type should be either subLine or wholeLine.\n\t\t\tif (selectionUnit == TextUnit::subLine) {\n\t\t\t\t// If it is subLine, we're inside a *double* click and word wrap is enabled,\n\t\t\t\t// so we switch to wholeLine in order to select whole line.\n\t\t\t\tselectionUnit = TextUnit::wholeLine;\n\t\t\t} else if (selectionUnit != TextUnit::subLine && selectionUnit != TextUnit::wholeLine) {\n\t\t\t\t// If it is neither, reset selection type to line selection.\n\t\t\t\tselectionUnit = (Wrapping() && (FlagSet(marginOptions, MarginOption::SubLineSelect))) ? TextUnit::subLine : TextUnit::wholeLine;\n\t\t\t}\n\t\t} else {\n\t\t\tif (selectionUnit == TextUnit::character) {\n\t\t\t\tselectionUnit = TextUnit::word;\n\t\t\t\tdoubleClick = true;\n\t\t\t} else if (selectionUnit == TextUnit::word) {\n\t\t\t\t// Since we ended up here, we're inside a *triple* click, which should always select\n\t\t\t\t// whole line regardless of word wrap being enabled or not.\n\t\t\t\tselectionUnit = TextUnit::wholeLine;\n\t\t\t} else {\n\t\t\t\tselectionUnit = TextUnit::character;\n\t\t\t\toriginalAnchorPos = sel.MainCaret();\n\t\t\t}\n\t\t}\n\n\t\tif (selectionUnit == TextUnit::word) {\n\t\t\tSci::Position charPos = originalAnchorPos;\n\t\t\tif (sel.MainCaret() == originalAnchorPos) {\n\t\t\t\tcharPos = PositionFromLocation(pt, false, true);\n\t\t\t\tcharPos = MovePositionOutsideChar(charPos, -1);\n\t\t\t}\n\n\t\t\tSci::Position startWord;\n\t\t\tSci::Position endWord;\n\t\t\tif ((sel.MainCaret() >= originalAnchorPos) && !pdoc->IsLineEndPosition(charPos)) {\n\t\t\t\tstartWord = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(charPos + 1, 1), -1);\n\t\t\t\tendWord = pdoc->ExtendWordSelect(charPos, 1);\n\t\t\t} else {\n\t\t\t\t// Selecting backwards, or anchor beyond last character on line. In these cases,\n\t\t\t\t// we select the word containing the character to the *left* of the anchor.\n\t\t\t\tif (charPos > pdoc->LineStartPosition(charPos)) {\n\t\t\t\t\tstartWord = pdoc->ExtendWordSelect(charPos, -1);\n\t\t\t\t\tendWord = pdoc->ExtendWordSelect(startWord, 1);\n\t\t\t\t} else {\n\t\t\t\t\t// Anchor at start of line; select nothing to begin with.\n\t\t\t\t\tstartWord = charPos;\n\t\t\t\t\tendWord = charPos;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\twordSelectAnchorStartPos = startWord;\n\t\t\twordSelectAnchorEndPos = endWord;\n\t\t\twordSelectInitialCaretPos = sel.MainCaret();\n\t\t\tWordSelection(wordSelectInitialCaretPos);\n\t\t} else if (selectionUnit == TextUnit::subLine || selectionUnit == TextUnit::wholeLine) {\n\t\t\tlineAnchorPos = newPos.Position();\n\t\t\tLineSelection(lineAnchorPos, lineAnchorPos, selectionUnit == TextUnit::wholeLine);\n\t\t\t//Platform::DebugPrintf(\"Triple click: %d - %d\\n\", anchor, currentPos);\n\t\t} else {\n\t\t\tSetEmptySelection(sel.MainCaret());\n\t\t}\n\t\t//Platform::DebugPrintf(\"Double click: %d - %d\\n\", anchor, currentPos);\n\t\tif (doubleClick) {\n\t\t\tNotifyDoubleClick(pt, modifiers);\n\t\t\tif (PositionIsHotspot(newCharPos.Position()))\n\t\t\t\tNotifyHotSpotDoubleClicked(newCharPos.Position(), modifiers);\n\t\t}\n\t} else {\t// Single click\n\t\tif (inSelMargin) {\n\t\t\tif (sel.IsRectangular() || (sel.Count() > 1)) {\n\t\t\t\tInvalidateWholeSelection();\n\t\t\t\tsel.Clear();\n\t\t\t}\n\t\t\tsel.selType = Selection::SelTypes::stream;\n\t\t\tif (!shift) {\n\t\t\t\t// Single click in margin: select wholeLine or only subLine if word wrap is enabled\n\t\t\t\tlineAnchorPos = newPos.Position();\n\n\t\t\t\t//Au: let Ctrl+click add line to multiple selections.\n\t\t\t\tif(ctrl && multipleSelection && !sel.Empty())\n\t\t\t\t\tsel.AddSelection(SelectionRange(lineAnchorPos, lineAnchorPos));\n\n\t\t\t\tselectionUnit = (Wrapping() && (FlagSet(marginOptions, MarginOption::SubLineSelect))) ? TextUnit::subLine : TextUnit::wholeLine;\n\t\t\t\tLineSelection(lineAnchorPos, lineAnchorPos, selectionUnit == TextUnit::wholeLine);\n\t\t\t} else {\n\t\t\t\t// Single shift+click in margin: select from line anchor to clicked line\n\t\t\t\tif (sel.MainAnchor() > sel.MainCaret())\n\t\t\t\t\tlineAnchorPos = sel.MainAnchor() - 1;\n\t\t\t\telse\n\t\t\t\t\tlineAnchorPos = sel.MainAnchor();\n\t\t\t\t// Reset selection type if there is an empty selection.\n\t\t\t\t// This ensures that we don't end up stuck in previous selection mode, which is no longer valid.\n\t\t\t\t// Otherwise, if there's a non empty selection, reset selection type only if it differs from selSubLine and selWholeLine.\n\t\t\t\t// This ensures that we continue selecting in the same selection mode.\n\t\t\t\tif (sel.Empty() || (selectionUnit != TextUnit::subLine && selectionUnit != TextUnit::wholeLine))\n\t\t\t\t\tselectionUnit = (Wrapping() && (FlagSet(marginOptions, MarginOption::SubLineSelect))) ? TextUnit::subLine : TextUnit::wholeLine;\n\t\t\t\tLineSelection(newPos.Position(), lineAnchorPos, selectionUnit == TextUnit::wholeLine);\n\t\t\t}\n\n\t\t\tSetDragPosition(SelectionPosition(Sci::invalidPosition));\n\t\t\tChangeMouseCapture(true);\n\t\t} else {\n\t\t\tif (PointIsHotspot(pt)) {\n\t\t\t\tNotifyHotSpotClicked(newCharPos.Position(), modifiers);\n\t\t\t\thotSpotClickPos = newCharPos.Position();\n\t\t\t}\n\t\t\tif (!shift) {\n\t\t\t\tconst ptrdiff_t selectionPart = SelectionFromPoint(pt);\n\t\t\t\tif (selectionPart >= 0) {\n\t\t\t\t\tif (multipleSelection && ctrl) {\n\t\t\t\t\t\t// Deselect\n\t\t\t\t\t\tif (sel.Count() > 1) {\n\t\t\t\t\t\t\tDropSelection(selectionPart);\n\t\t\t\t\t\t\t// Completed: don't want any more processing of this click\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Switch to just the click position\n\t\t\t\t\t\tSetSelection(newPos, newPos);\n\t\t\t\t\t}\n\t\t\t\t\tif (!sel.Range(selectionPart).Empty()) {\n\t\t\t\t\t\tinDragDrop = DragDrop::initial;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tChangeMouseCapture(true);\n\t\t\tif (inDragDrop != DragDrop::initial) {\n\t\t\t\tSetDragPosition(SelectionPosition(Sci::invalidPosition));\n\t\t\t\tif (!shift) {\n\t\t\t\t\tif (ctrl && multipleSelection) {\n\t\t\t\t\t\tconst SelectionRange range(newPos);\n\t\t\t\t\t\tsel.TentativeSelection(range);\n\t\t\t\t\t\tInvalidateSelection(range, true);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tInvalidateSelection(SelectionRange(newPos), true);\n\t\t\t\t\t\tif (sel.Count() > 1)\n\t\t\t\t\t\t\tRedraw();\n\t\t\t\t\t\tif ((sel.Count() > 1) || (sel.selType != Selection::SelTypes::stream))\n\t\t\t\t\t\t\tsel.Clear();\n\t\t\t\t\t\tsel.selType = alt ? Selection::SelTypes::rectangle : Selection::SelTypes::stream;\n\t\t\t\t\t\tSetSelection(newPos, newPos);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tSelectionPosition anchorCurrent = newPos;\n\t\t\t\tif (shift)\n\t\t\t\t\tanchorCurrent = sel.IsRectangular() ?\n\t\t\t\t\t\tsel.Rectangular().anchor : sel.RangeMain().anchor;\n\t\t\t\tsel.selType = alt ? Selection::SelTypes::rectangle : Selection::SelTypes::stream;\n\t\t\t\tselectionUnit = TextUnit::character;\n\t\t\t\toriginalAnchorPos = sel.MainCaret();\n\t\t\t\tsel.Rectangular() = SelectionRange(newPos, anchorCurrent);\n\t\t\t\tSetRectangularRange();\n\t\t\t}\n\t\t}\n\t}\n\tlastXChosen = static_cast<int>(pt.x) + xOffset;\n\tShowCaretAtCurrentPosition();\n}\n\nvoid Editor::RightButtonDownWithModifiers(Point pt, unsigned int, KeyMod modifiers) {\n\tif (NotifyMarginRightClick(pt, modifiers))\n\t\treturn;\n}\n\nbool Editor::PositionIsHotspot(Sci::Position position) const noexcept {\n\treturn vs.styles[pdoc->StyleIndexAt(position)].hotspot;\n}\n\nbool Editor::PointIsHotspot(Point pt) {\n\tconst Sci::Position pos = PositionFromLocation(pt, true, true);\n\tif (pos == Sci::invalidPosition)\n\t\treturn false;\n\treturn PositionIsHotspot(pos);\n}\n\nvoid Editor::SetHoverIndicatorPosition(Sci::Position position) {\n\tconst Sci::Position hoverIndicatorPosPrev = hoverIndicatorPos;\n\thoverIndicatorPos = Sci::invalidPosition;\n\tif (!vs.indicatorsDynamic)\n\t\treturn;\n\tif (position != Sci::invalidPosition) {\n\t\tfor (const IDecoration *deco : pdoc->decorations->View()) {\n\t\t\tif (vs.indicators[deco->Indicator()].IsDynamic()) {\n\t\t\t\tif (pdoc->decorations->ValueAt(deco->Indicator(), position)) {\n\t\t\t\t\thoverIndicatorPos = position;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif (hoverIndicatorPosPrev != hoverIndicatorPos) {\n\t\tRedraw();\n\t}\n}\n\nvoid Editor::SetHoverIndicatorPoint(Point pt) {\n\tif (!vs.indicatorsDynamic) {\n\t\tSetHoverIndicatorPosition(Sci::invalidPosition);\n\t} else {\n\t\t//Au: bug fix: the hover style/cursor is not shown when mouse is 1 pixel before the indicator (but SCN_INDICATORx sent), and shown when mouse is 1-2 pixels after the indicator (but SCN_INDICATORx not sent).\n\t\tSetHoverIndicatorPosition(PositionFromLocation(pt, true, false));\n\t\t//SetHoverIndicatorPosition(PositionFromLocation(pt, true, true));\n\t}\n}\n\nvoid Editor::SetHotSpotRange(const Point *pt) {\n\tif (pt) {\n\t\tconst Sci::Position pos = PositionFromLocation(*pt, false, true);\n\n\t\t// If we don't limit this to word characters then the\n\t\t// range can encompass more than the run range and then\n\t\t// the underline will not be drawn properly.\n\t\tRange hsNew;\n\t\thsNew.start = pdoc->ExtendStyleRange(pos, -1, hotspotSingleLine);\n\t\thsNew.end = pdoc->ExtendStyleRange(pos, 1, hotspotSingleLine);\n\n\t\t// Only invalidate the range if the hotspot range has changed...\n\t\tif (!(hsNew == hotspot)) {\n\t\t\tif (hotspot.Valid()) {\n\t\t\t\tInvalidateRange(hotspot.start, hotspot.end);\n\t\t\t}\n\t\t\thotspot = hsNew;\n\t\t\tInvalidateRange(hotspot.start, hotspot.end);\n\t\t}\n\t} else {\n\t\tif (hotspot.Valid()) {\n\t\t\tInvalidateRange(hotspot.start, hotspot.end);\n\t\t}\n\t\thotspot = Range(Sci::invalidPosition);\n\t}\n}\n\nvoid Editor::ButtonMoveWithModifiers(Point pt, unsigned int, KeyMod modifiers) {\n\tif (ptMouseLast != pt) {\n\t\tDwellEnd(true);\n\t}\n\n\tSelectionPosition movePos = SPositionFromLocation(pt, false, false,\n\t\tAllowVirtualSpace(virtualSpaceOptions, sel.IsRectangular()));\n\tmovePos = MovePositionOutsideChar(movePos, sel.MainCaret() - movePos.Position());\n\n\tif (inDragDrop == DragDrop::initial) {\n\t\tif (DragThreshold(ptMouseLast, pt)) {\n\t\t\tChangeMouseCapture(false);\n\t\t\tSetDragPosition(movePos);\n\t\t\tCopySelectionRange(&drag);\n\t\t\tStartDrag();\n\t\t}\n\t\treturn;\n\t}\n\n\tptMouseLast = pt;\n\tPRectangle rcClient = GetClientRectangle();\n\tconst Point ptOrigin = GetVisibleOriginInMain();\n\trcClient.Move(0, -ptOrigin.y);\n\tif ((dwellDelay < TimeForever) && rcClient.Contains(pt)) {\n\t\tFineTickerStart(TickReason::dwell, dwellDelay, dwellDelay/10);\n\t}\n\t//Platform::DebugPrintf(\"Move %d %d\\n\", pt.x, pt.y);\n\tif (HaveMouseCapture()) {\n\n\t\t// Slow down autoscrolling/selection\n\t\tautoScrollTimer.ticksToWait -= timer.tickSize;\n\t\tif (autoScrollTimer.ticksToWait > 0)\n\t\t\treturn;\n\t\tautoScrollTimer.ticksToWait = autoScrollDelay;\n\n\t\t// Adjust selection\n\t\tif (posDrag.IsValid()) {\n\t\t\tSetDragPosition(movePos);\n\t\t} else {\n\t\t\tif (selectionUnit == TextUnit::character) {\n\t\t\t\tif (sel.selType == Selection::SelTypes::stream && FlagSet(modifiers, KeyMod::Alt) && mouseSelectionRectangularSwitch) {\n\t\t\t\t\tsel.selType = Selection::SelTypes::rectangle;\n\t\t\t\t}\n\t\t\t\tif (sel.IsRectangular()) {\n\t\t\t\t\tsel.Rectangular() = SelectionRange(movePos, sel.Rectangular().anchor);\n\t\t\t\t\tSetSelection(movePos, sel.RangeMain().anchor);\n\t\t\t\t} else if (sel.Count() > 1) {\n\t\t\t\t\tInvalidateSelection(sel.RangeMain(), false);\n\t\t\t\t\tconst SelectionRange range(movePos, sel.RangeMain().anchor);\n\t\t\t\t\tsel.TentativeSelection(range);\n\t\t\t\t\tInvalidateSelection(range, true);\n\t\t\t\t} else {\n\t\t\t\t\tSetSelection(movePos, sel.RangeMain().anchor);\n\t\t\t\t}\n\t\t\t} else if (selectionUnit == TextUnit::word) {\n\t\t\t\t// Continue selecting by word\n\t\t\t\tif (movePos.Position() == wordSelectInitialCaretPos) {  // Didn't move\n\t\t\t\t\t// No need to do anything. Previously this case was lumped\n\t\t\t\t\t// in with \"Moved forward\", but that can be harmful in this\n\t\t\t\t\t// case: a handler for the NotifyDoubleClick re-adjusts\n\t\t\t\t\t// the selection for a fancier definition of \"word\" (for\n\t\t\t\t\t// example, in Perl it is useful to include the leading\n\t\t\t\t\t// '$', '%' or '@' on variables for word selection). In this\n\t\t\t\t\t// the ButtonMove() called via TickFor() for auto-scrolling\n\t\t\t\t\t// could result in the fancier word selection adjustment\n\t\t\t\t\t// being unmade.\n\t\t\t\t} else {\n\t\t\t\t\twordSelectInitialCaretPos = -1;\n\t\t\t\t\tWordSelection(movePos.Position());\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Continue selecting by line\n\t\t\t\tLineSelection(movePos.Position(), lineAnchorPos, selectionUnit == TextUnit::wholeLine);\n\t\t\t}\n\t\t}\n\n\t\t// Autoscroll\n\t\tconst Sci::Line lineMove = DisplayFromPosition(movePos.Position());\n\t\tif (pt.y >= rcClient.bottom) {\n\t\t\tScrollTo(lineMove - LinesOnScreen() + 1);\n\t\t\tRedraw();\n\t\t} else if (pt.y < rcClient.top) {\n\t\t\tScrollTo(lineMove);\n\t\t\tRedraw();\n\t\t}\n\t\tEnsureCaretVisible(false, false, true);\n\n\t\tif (hotspot.Valid() && !PointIsHotspot(pt))\n\t\t\tSetHotSpotRange(nullptr);\n\n\t\tif (hotSpotClickPos != Sci::invalidPosition && PositionFromLocation(pt, true, true) != hotSpotClickPos) {\n\t\t\tif (inDragDrop == DragDrop::none) {\n\t\t\t\tDisplayCursor(Window::Cursor::text);\n\t\t\t}\n\t\t\thotSpotClickPos = Sci::invalidPosition;\n\t\t}\n\n\t} else {\n\t\tif (vs.fixedColumnWidth > 0) {\t// There is a margin\n\t\t\tif (PointInSelMargin(pt)) {\n\t\t\t\tDisplayCursor(GetMarginCursor(pt));\n\t\t\t\tSetHotSpotRange(nullptr);\n\t\t\t\tSetHoverIndicatorPosition(Sci::invalidPosition);\n\t\t\t\treturn; \t// No need to test for selection\n\t\t\t}\n\t\t}\n\t\t// Display regular (drag) cursor over selection\n\t\tif (PointInSelection(pt) && !SelectionEmpty()) {\n\t\t\tDisplayCursor(Window::Cursor::arrow);\n\t\t\tSetHoverIndicatorPosition(Sci::invalidPosition);\n\t\t} else {\n\t\t\tSetHoverIndicatorPoint(pt);\n\t\t\tif (PointIsHotspot(pt)) {\n\t\t\t\tDisplayCursor(Window::Cursor::hand);\n\t\t\t\tSetHotSpotRange(&pt);\n\t\t\t} else {\n\t\t\t\tif (hoverIndicatorPos != Sci::invalidPosition)\n\t\t\t\t\tDisplayCursor(Window::Cursor::hand);\n\t\t\t\telse\n\t\t\t\t\tDisplayCursor(Window::Cursor::text);\n\t\t\t\tSetHotSpotRange(nullptr);\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid Editor::ButtonUpWithModifiers(Point pt, unsigned int curTime, KeyMod modifiers) {\n\t//Platform::DebugPrintf(\"ButtonUp %d %d\\n\", HaveMouseCapture(), inDragDrop);\n\tSelectionPosition newPos = SPositionFromLocation(pt, false, false,\n\t\tAllowVirtualSpace(virtualSpaceOptions, sel.IsRectangular()));\n\tif (hoverIndicatorPos != Sci::invalidPosition)\n\t\tInvalidateRange(newPos.Position(), newPos.Position() + 1);\n\tnewPos = MovePositionOutsideChar(newPos, sel.MainCaret() - newPos.Position());\n\tif (inDragDrop == DragDrop::initial) {\n\t\tinDragDrop = DragDrop::none;\n\t\tSetEmptySelection(newPos);\n\t\tselectionUnit = TextUnit::character;\n\t\toriginalAnchorPos = sel.MainCaret();\n\t}\n\tif (hotSpotClickPos != Sci::invalidPosition && PointIsHotspot(pt)) {\n\t\thotSpotClickPos = Sci::invalidPosition;\n\t\tSelectionPosition newCharPos = SPositionFromLocation(pt, false, true, false);\n\t\tnewCharPos = MovePositionOutsideChar(newCharPos, -1);\n\t\tNotifyHotSpotReleaseClick(newCharPos.Position(), modifiers & KeyMod::Ctrl);\n\t}\n\tif (HaveMouseCapture()) {\n\t\tif (PointInSelMargin(pt)) {\n\t\t\tDisplayCursor(GetMarginCursor(pt));\n\t\t} else {\n\t\t\tDisplayCursor(Window::Cursor::text);\n\t\t\tSetHotSpotRange(nullptr);\n\t\t}\n\t\tptMouseLast = pt;\n\t\tChangeMouseCapture(false);\n\t\tNotifyIndicatorClick(false, newPos.Position(), modifiers);\n\t\tif (inDragDrop == DragDrop::dragging) {\n\t\t\tconst SelectionPosition selStart = SelectionStart();\n\t\t\tconst SelectionPosition selEnd = SelectionEnd();\n\t\t\tif (selStart < selEnd) {\n\t\t\t\tif (drag.Length()) {\n\t\t\t\t\tconst Sci::Position length = drag.Length();\n\t\t\t\t\tif (FlagSet(modifiers, KeyMod::Ctrl)) {\n\t\t\t\t\t\tconst Sci::Position lengthInserted = pdoc->InsertString(\n\t\t\t\t\t\t\tnewPos.Position(), drag.Data(), length);\n\t\t\t\t\t\tif (lengthInserted > 0) {\n\t\t\t\t\t\t\tSetSelection(newPos.Position(), newPos.Position() + lengthInserted);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (newPos < selStart) {\n\t\t\t\t\t\tpdoc->DeleteChars(selStart.Position(), drag.Length());\n\t\t\t\t\t\tconst Sci::Position lengthInserted = pdoc->InsertString(\n\t\t\t\t\t\t\tnewPos.Position(), drag.Data(), length);\n\t\t\t\t\t\tif (lengthInserted > 0) {\n\t\t\t\t\t\t\tSetSelection(newPos.Position(), newPos.Position() + lengthInserted);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (newPos > selEnd) {\n\t\t\t\t\t\tpdoc->DeleteChars(selStart.Position(), drag.Length());\n\t\t\t\t\t\tnewPos.Add(-static_cast<Sci::Position>(drag.Length()));\n\t\t\t\t\t\tconst Sci::Position lengthInserted = pdoc->InsertString(\n\t\t\t\t\t\t\tnewPos.Position(), drag.Data(), length);\n\t\t\t\t\t\tif (lengthInserted > 0) {\n\t\t\t\t\t\t\tSetSelection(newPos.Position(), newPos.Position() + lengthInserted);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tSetEmptySelection(newPos.Position());\n\t\t\t\t\t}\n\t\t\t\t\tdrag.Clear();\n\t\t\t\t}\n\t\t\t\tselectionUnit = TextUnit::character;\n\t\t\t}\n\t\t} else {\n\t\t\tif (selectionUnit == TextUnit::character) {\n\t\t\t\tif (sel.Count() > 1) {\n\t\t\t\t\tsel.RangeMain() =\n\t\t\t\t\t\tSelectionRange(newPos, sel.Range(sel.Count() - 1).anchor);\n\t\t\t\t\tInvalidateWholeSelection();\n\t\t\t\t} else {\n\t\t\t\t\tSetSelection(newPos, sel.RangeMain().anchor);\n\t\t\t\t}\n\t\t\t}\n\t\t\tsel.CommitTentative();\n\t\t}\n\t\tSetRectangularRange();\n\t\tlastClickTime = curTime;\n\t\tlastClick = pt;\n\t\tlastXChosen = static_cast<int>(pt.x) + xOffset;\n\t\tif (sel.selType == Selection::SelTypes::stream) {\n\t\t\tSetLastXChosen();\n\t\t}\n\t\tinDragDrop = DragDrop::none;\n\t\tEnsureCaretVisible(false);\n\t}\n}\n\nbool Editor::Idle() {\n\tNotifyUpdateUI();\n\n\tbool needWrap = Wrapping() && wrapPending.NeedsWrap();\n\n\tif (needWrap) {\n\t\t// Wrap lines during idle.\n\t\tWrapLines(WrapScope::wsIdle);\n\t\t// No more wrapping\n\t\tneedWrap = wrapPending.NeedsWrap();\n\t} else if (needIdleStyling) {\n\t\tIdleStyle();\n\t}\n\n\t// Add more idle things to do here, but make sure idleDone is\n\t// set correctly before the function returns. returning\n\t// false will stop calling this idle function until SetIdle() is\n\t// called again.\n\n\tconst bool idleDone = !needWrap && !needIdleStyling; // && thatDone && theOtherThingDone...\n\n\treturn !idleDone;\n}\n\nvoid Editor::TickFor(TickReason reason) {\n\tswitch (reason) {\n\t\tcase TickReason::caret:\n\t\t\tcaret.on = !caret.on;\n\t\t\tif (caret.active) {\n\t\t\t\tInvalidateCaret();\n\t\t\t}\n\t\t\tbreak;\n\t\tcase TickReason::scroll:\n\t\t\t// Auto scroll\n\t\t\tif (HaveMouseCapture()) {\n\t\t\t\tButtonMoveWithModifiers(ptMouseLast, 0, KeyMod::Norm);\n\t\t\t} else {\n\t\t\t\t// Capture cancelled so cancel timer\n\t\t\t\tFineTickerCancel(TickReason::scroll);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase TickReason::widen:\n\t\t\tSetScrollBars();\n\t\t\tFineTickerCancel(TickReason::widen);\n\t\t\tbreak;\n\t\tcase TickReason::dwell:\n\t\t\tif ((!HaveMouseCapture()) &&\n\t\t\t\t(ptMouseLast.y >= 0)) {\n\t\t\t\tdwelling = true;\n\t\t\t\tNotifyDwelling(ptMouseLast, dwelling);\n\t\t\t}\n\t\t\tFineTickerCancel(TickReason::dwell);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\t// tickPlatform handled by subclass\n\t\t\tbreak;\n\t}\n}\n\n// FineTickerStart is be overridden by subclasses that support fine ticking so\n// this method should never be called.\nbool Editor::FineTickerRunning(TickReason) {\n\tassert(false);\n\treturn false;\n}\n\n// FineTickerStart is be overridden by subclasses that support fine ticking so\n// this method should never be called.\nvoid Editor::FineTickerStart(TickReason, int, int) {\n\tassert(false);\n}\n\n// FineTickerCancel is be overridden by subclasses that support fine ticking so\n// this method should never be called.\nvoid Editor::FineTickerCancel(TickReason) {\n\tassert(false);\n}\n\nvoid Editor::ChangeMouseCapture(bool on) {\n\tSetMouseCapture(on);\n\t// While mouse captured want timer to scroll automatically\n\tif (on) {\n\t\tFineTickerStart(TickReason::scroll, 100, 10);\n\t} else {\n\t\tFineTickerCancel(TickReason::scroll);\n\t}\n}\n\nvoid Editor::SetFocusState(bool focusState) {\n\tconst bool changing = hasFocus != focusState;\n\thasFocus = focusState;\n\tif (changing) {\n\t\tRedraw();\n\t}\n\tNotifyFocus(hasFocus);\n\tif (!hasFocus) {\n\t\tCancelModes();\n\t}\n\tShowCaretAtCurrentPosition();\n}\n\nvoid Editor::UpdateBaseElements() {\n\t// Overridden by subclasses\n}\n\nSci::Position Editor::PositionAfterArea(PRectangle rcArea) const {\n\t// The start of the document line after the display line after the area\n\t// This often means that the line after a modification is restyled which helps\n\t// detect multiline comment additions and heals single line comments\n\tconst Sci::Line lineAfter = TopLineOfMain() + static_cast<Sci::Line>(rcArea.bottom - 1) / vs.lineHeight + 1;\n\tif (lineAfter < pcs->LinesDisplayed()) {\n\t\treturn pdoc->LineStart(pcs->DocFromDisplay(lineAfter) + 1);\n\t}\n\treturn pdoc->Length();\n}\n\n// Style to a position within the view. If this causes a change at end of last line then\n// affects later lines so style all the viewed text.\nvoid Editor::StyleToPositionInView(Sci::Position pos) {\n\tSci::Position endWindow = PositionAfterArea(GetClientDrawingRectangle());\n\tif (pos > endWindow)\n\t\tpos = endWindow;\n\tconst int styleAtEnd = pdoc->StyleIndexAt(pos-1);\n\tpdoc->EnsureStyledTo(pos);\n\tif ((endWindow > pos) && (styleAtEnd != pdoc->StyleIndexAt(pos-1))) {\n\t\t// Style at end of line changed so is multi-line change like starting a comment\n\t\t// so require rest of window to be styled.\n\t\tDiscardOverdraw();\t// Prepared bitmaps may be invalid\n\t\t// DiscardOverdraw may have truncated client drawing area so recalculate endWindow\n\t\tendWindow = PositionAfterArea(GetClientDrawingRectangle());\n\t\tpdoc->EnsureStyledTo(endWindow);\n\t}\n}\n\nSci::Position Editor::PositionAfterMaxStyling(Sci::Position posMax, bool scrolling) const {\n\tif (SynchronousStylingToVisible()) {\n\t\t// Both states do not limit styling\n\t\treturn posMax;\n\t}\n\n\t// Try to keep time taken by styling reasonable so interaction remains smooth.\n\t// When scrolling, allow less time to ensure responsive\n\tconst double secondsAllowed = scrolling ? 0.005 : 0.02;\n\n\tconst size_t actionsInAllowedTime = std::clamp<Sci::Line>(\n\t\tpdoc->durationStyleOneByte.ActionsInAllowedTime(secondsAllowed),\n\t\t0x200, 0x20000);\n\tconst Sci::Line lineLast = pdoc->LineFromPositionAfter(pdoc->SciLineFromPosition(pdoc->GetEndStyled()), actionsInAllowedTime);\n\tconst Sci::Line stylingMaxLine = std::min(lineLast, pdoc->LinesTotal());\n\n\treturn std::min(pdoc->LineStart(stylingMaxLine), posMax);\n}\n\nvoid Editor::StartIdleStyling(bool truncatedLastStyling) {\n\tif ((idleStyling == IdleStyling::All) || (idleStyling == IdleStyling::AfterVisible)) {\n\t\tif (pdoc->GetEndStyled() < pdoc->Length()) {\n\t\t\t// Style remainder of document in idle time\n\t\t\tneedIdleStyling = true;\n\t\t}\n\t} else if (truncatedLastStyling) {\n\t\tneedIdleStyling = true;\n\t}\n\n\tif (needIdleStyling) {\n\t\tSetIdle(true);\n\t}\n}\n\n// Style for an area but bound the amount of styling to remain responsive\nvoid Editor::StyleAreaBounded(PRectangle rcArea, bool scrolling) {\n\tconst Sci::Position posAfterArea = PositionAfterArea(rcArea);\n\tconst Sci::Position posAfterMax = PositionAfterMaxStyling(posAfterArea, scrolling);\n\tif (posAfterMax < posAfterArea) {\n\t\t// Idle styling may be performed before current visible area\n\t\t// Style a bit now then style further in idle time\n\t\tpdoc->StyleToAdjustingLineDuration(posAfterMax);\n\t} else {\n\t\t// Can style all wanted now.\n\t\tStyleToPositionInView(posAfterArea);\n\t}\n\tStartIdleStyling(posAfterMax < posAfterArea);\n}\n\nvoid Editor::IdleStyle() {\n\tconst Sci::Position posAfterArea = PositionAfterArea(GetClientRectangle());\n\tconst Sci::Position endGoal = (idleStyling >= IdleStyling::AfterVisible) ?\n\t\tpdoc->Length() : posAfterArea;\n\tconst Sci::Position posAfterMax = PositionAfterMaxStyling(endGoal, false);\n\tpdoc->StyleToAdjustingLineDuration(posAfterMax);\n\tif (pdoc->GetEndStyled() >= endGoal) {\n\t\tneedIdleStyling = false;\n\t}\n}\n\nvoid Editor::IdleWork() {\n\t// Style the line after the modification as this allows modifications that change just the\n\t// line of the modification to heal instead of propagating to the rest of the window.\n\tif (FlagSet(workNeeded.items, WorkItems::style)) {\n\t\tStyleToPositionInView(pdoc->LineStart(pdoc->LineFromPosition(workNeeded.upTo) + 2));\n\t}\n\tNotifyUpdateUI();\n\tworkNeeded.Reset();\n}\n\nvoid Editor::QueueIdleWork(WorkItems items, Sci::Position upTo) {\n\tworkNeeded.Need(items, upTo);\n}\n\nint Editor::SupportsFeature(Supports feature) {\n\tAutoSurface surface(this);\n\treturn surface->SupportsFeature(feature);\n}\n\nbool Editor::PaintContains(PRectangle rc) {\n\tif (rc.Empty()) {\n\t\treturn true;\n\t}\n\treturn rcPaint.Contains(rc);\n}\n\nbool Editor::PaintContainsMargin() {\n\tif (HasMarginWindow()) {\n\t\t// With separate margin view, paint of text view\n\t\t// never contains margin.\n\t\treturn false;\n\t}\n\tPRectangle rcSelMargin = GetClientRectangle();\n\trcSelMargin.right = static_cast<XYPOSITION>(vs.textStart);\n\treturn PaintContains(rcSelMargin);\n}\n\nvoid Editor::CheckForChangeOutsidePaint(Range r) {\n\tif (paintState == PaintState::painting && !paintingAllText) {\n\t\t//Platform::DebugPrintf(\"Checking range in paint %d-%d\\n\", r.start, r.end);\n\t\tif (!r.Valid())\n\t\t\treturn;\n\n\t\tPRectangle rcRange = RectangleFromRange(r, 0);\n\t\tconst PRectangle rcText = GetTextRectangle();\n\t\tif (rcRange.top < rcText.top) {\n\t\t\trcRange.top = rcText.top;\n\t\t}\n\t\tif (rcRange.bottom > rcText.bottom) {\n\t\t\trcRange.bottom = rcText.bottom;\n\t\t}\n\n\t\tif (!PaintContains(rcRange)) {\n\t\t\tAbandonPaint();\n\t\t\tpaintAbandonedByStyling = true;\n\t\t}\n\t}\n}\n\nvoid Editor::SetBraceHighlight(Sci::Position pos0, Sci::Position pos1, int matchStyle) {\n\tif ((pos0 != braces[0]) || (pos1 != braces[1]) || (matchStyle != bracesMatchStyle)) {\n\t\tif ((braces[0] != pos0) || (matchStyle != bracesMatchStyle)) {\n\t\t\tCheckForChangeOutsidePaint(Range(braces[0]));\n\t\t\tCheckForChangeOutsidePaint(Range(pos0));\n\t\t\tbraces[0] = pos0;\n\t\t}\n\t\tif ((braces[1] != pos1) || (matchStyle != bracesMatchStyle)) {\n\t\t\tCheckForChangeOutsidePaint(Range(braces[1]));\n\t\t\tCheckForChangeOutsidePaint(Range(pos1));\n\t\t\tbraces[1] = pos1;\n\t\t}\n\t\tbracesMatchStyle = matchStyle;\n\t\tif (paintState == PaintState::notPainting) {\n\t\t\tRedraw();\n\t\t}\n\t}\n}\n\nvoid Editor::SetAnnotationHeights(Sci::Line start, Sci::Line end) {\n\tif (vs.annotationVisible != AnnotationVisible::Hidden) {\n\t\tRefreshStyleData();\n\t\tbool changedHeight = false;\n\t\tfor (Sci::Line line=start; line<end && line<pdoc->LinesTotal(); line++) {\n\t\t\tint linesWrapped = 1;\n\t\t\tif (Wrapping()) {\n\t\t\t\tAutoSurface surface(this);\n\t\t\t\tstd::shared_ptr<LineLayout> ll = view.RetrieveLineLayout(line, *this);\n\t\t\t\tif (surface && ll) {\n\t\t\t\t\tview.LayoutLine(*this, surface, vs, ll.get(), wrapWidth);\n\t\t\t\t\tlinesWrapped = ll->lines;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (pcs->SetHeight(line, pdoc->AnnotationLines(line) + linesWrapped))\n\t\t\t\tchangedHeight = true;\n\t\t}\n\t\tif (changedHeight) {\n\t\t\tSetScrollBars();\n\t\t\tSetVerticalScrollPos();\n\t\t\tRedraw();\n\t\t}\n\t}\n}\n\nvoid Editor::SetDocPointer(Document *document) {\n\t//Platform::DebugPrintf(\"** %x setdoc to %x\\n\", pdoc, document);\n\tpdoc->RemoveWatcher(this, nullptr);\n\tpdoc->Release();\n\tif (!document) {\n\t\tpdoc = new Document(DocumentOption::Default);\n\t} else {\n\t\tpdoc = document;\n\t}\n\tpdoc->AddRef();\n\tmodelState.reset();\n\tpcs = ContractionStateCreate(pdoc->IsLarge());\n\n\t// Ensure all positions within document\n\tsel.Clear();\n\ttargetRange = SelectionSegment();\n\n\tbraces[0] = Sci::invalidPosition;\n\tbraces[1] = Sci::invalidPosition;\n\n\tvs.ReleaseAllExtendedStyles();\n\n\tSetRepresentations();\n\n\tscrollToAfterWrap.reset();\n\n\t// Reset the contraction state to fully shown.\n\tpcs->Clear();\n\tpcs->InsertLines(0, pdoc->LinesTotal() - 1);\n\tSetAnnotationHeights(0, pdoc->LinesTotal());\n\tview.llc.Deallocate();\n\tNeedWrapping();\n\n\thotspot = Range(Sci::invalidPosition);\n\thoverIndicatorPos = Sci::invalidPosition;\n\n\tview.ClearAllTabstops();\n\n\tpdoc->AddWatcher(this, nullptr);\n\tSetScrollBars();\n\tRedraw();\n}\n\nvoid Editor::SetAnnotationVisible(AnnotationVisible visible) {\n\tif (vs.annotationVisible != visible) {\n\t\tconst bool changedFromOrToHidden = ((vs.annotationVisible != AnnotationVisible::Hidden) != (visible != AnnotationVisible::Hidden));\n\t\tvs.annotationVisible = visible;\n\t\tif (changedFromOrToHidden) {\n\t\t\tconst int dir = (vs.annotationVisible!= AnnotationVisible::Hidden) ? 1 : -1;\n\t\t\tfor (Sci::Line line=0; line<pdoc->LinesTotal(); line++) {\n\t\t\t\tconst int annotationLines = pdoc->AnnotationLines(line);\n\t\t\t\tif (annotationLines > 0) {\n\t\t\t\t\tpcs->SetHeight(line, pcs->GetHeight(line) + annotationLines * dir);\n\t\t\t\t}\n\t\t\t}\n\t\t\tSetScrollBars();\n\t\t}\n\t\tRedraw();\n\t}\n}\n\nvoid Editor::SetEOLAnnotationVisible(EOLAnnotationVisible visible) {\n\tif (vs.eolAnnotationVisible != visible) {\n\t\tvs.eolAnnotationVisible = visible;\n\t\tRedraw();\n\t}\n}\n\n/**\n * Recursively expand a fold, making lines visible except where they have an unexpanded parent.\n */\nSci::Line Editor::ExpandLine(Sci::Line line) {\n\tconst Sci::Line lineMaxSubord = pdoc->GetLastChild(line);\n\tline++;\n\tSci::Line lineStart = line;\n\twhile (line <= lineMaxSubord) {\n\t\tconst FoldLevel level = pdoc->GetFoldLevel(line);\n\t\tif (LevelIsHeader(level)) {\n\t\t\tpcs->SetVisible(lineStart, line, true);\n\t\t\tif (pcs->GetExpanded(line)) {\n\t\t\t\tline = ExpandLine(line);\n\t\t\t} else {\n\t\t\t\tline = pdoc->GetLastChild(line);\n\t\t\t}\n\t\t\tlineStart = line + 1;\n\t\t}\n\t\tline++;\n\t}\n\tif (lineStart <= lineMaxSubord) {\n\t\tpcs->SetVisible(lineStart, lineMaxSubord, true);\n\t}\n\treturn lineMaxSubord;\n}\n\nvoid Editor::SetFoldExpanded(Sci::Line lineDoc, bool expanded) {\n\tif (pcs->SetExpanded(lineDoc, expanded)) {\n\t\tRedrawSelMargin();\n\t}\n}\n\nvoid Editor::FoldLine(Sci::Line line, FoldAction action) {\n\tif (line >= 0) {\n\t\tif (action == FoldAction::Toggle) {\n\t\t\tif (!LevelIsHeader(pdoc->GetFoldLevel(line))) {\n\t\t\t\tline = pdoc->GetFoldParent(line);\n\t\t\t\tif (line < 0)\n\t\t\t\t\treturn;\n\t\t\t}\n\t\t\taction = (pcs->GetExpanded(line)) ? FoldAction::Contract : FoldAction::Expand;\n\t\t}\n\n\t\tif (action == FoldAction::Contract) {\n\t\t\tconst Sci::Line lineMaxSubord = pdoc->GetLastChild(line);\n\t\t\tif (lineMaxSubord > line) {\n\t\t\t\tpcs->SetExpanded(line, false);\n\t\t\t\tpcs->SetVisible(line + 1, lineMaxSubord, false);\n\n\t\t\t\tconst Sci::Line lineCurrent =\n\t\t\t\t\tpdoc->SciLineFromPosition(sel.MainCaret());\n\t\t\t\tif (lineCurrent > line && lineCurrent <= lineMaxSubord) {\n\t\t\t\t\t// This does not re-expand the fold\n\t\t\t\t\tEnsureCaretVisible();\n\t\t\t\t}\n\t\t\t}\n\n\t\t} else {\n\t\t\tif (!(pcs->GetVisible(line))) {\n\t\t\t\tEnsureLineVisible(line, false);\n\t\t\t\tGoToLine(line);\n\t\t\t}\n\t\t\tpcs->SetExpanded(line, true);\n\t\t\tExpandLine(line);\n\t\t}\n\n\t\tSetScrollBars();\n\t\tRedraw();\n\t}\n}\n\nvoid Editor::FoldExpand(Sci::Line line, FoldAction action, FoldLevel level) {\n\tbool expanding = action == FoldAction::Expand;\n\tif (action == FoldAction::Toggle) {\n\t\texpanding = !pcs->GetExpanded(line);\n\t}\n\t// Ensure child lines lexed and fold information extracted before\n\t// flipping the state.\n\tpdoc->GetLastChild(line, LevelNumberPart(level));\n\tSetFoldExpanded(line, expanding);\n\tif (expanding && (pcs->HiddenLines() == 0))\n\t\t// Nothing to do\n\t\treturn;\n\tconst Sci::Line lineMaxSubord = pdoc->GetLastChild(line, LevelNumberPart(level));\n\tline++;\n\tpcs->SetVisible(line, lineMaxSubord, expanding);\n\twhile (line <= lineMaxSubord) {\n\t\tconst FoldLevel levelLine = pdoc->GetFoldLevel(line);\n\t\tif (LevelIsHeader(levelLine)) {\n\t\t\tSetFoldExpanded(line, expanding);\n\t\t}\n\t\tline++;\n\t}\n\tSetScrollBars();\n\tRedraw();\n}\n\nSci::Line Editor::ContractedFoldNext(Sci::Line lineStart) const noexcept {\n\tfor (Sci::Line line = lineStart; line<pdoc->LinesTotal();) {\n\t\tif (!pcs->GetExpanded(line) && LevelIsHeader(pdoc->GetFoldLevel(line)))\n\t\t\treturn line;\n\t\tline = pcs->ContractedNext(line+1);\n\t\tif (line < 0)\n\t\t\treturn -1;\n\t}\n\n\treturn -1;\n}\n\n/**\n * Recurse up from this line to find any folds that prevent this line from being visible\n * and unfold them all.\n */\nvoid Editor::EnsureLineVisible(Sci::Line lineDoc, bool enforcePolicy) {\n\n\t// In case in need of wrapping to ensure DisplayFromDoc works.\n\tif (lineDoc >= wrapPending.start) {\n\t\tif (WrapLines(WrapScope::wsAll)) {\n\t\t\tRedraw();\n\t\t}\n\t}\n\n\tif (!pcs->GetVisible(lineDoc)) {\n\t\t// Back up to find a non-blank line\n\t\tSci::Line lookLine = lineDoc;\n\t\tFoldLevel lookLineLevel = pdoc->GetFoldLevel(lookLine);\n\t\twhile ((lookLine > 0) && LevelIsWhitespace(lookLineLevel)) {\n\t\t\tlookLineLevel = pdoc->GetFoldLevel(--lookLine);\n\t\t}\n\t\tSci::Line lineParent = pdoc->GetFoldParent(lookLine);\n\t\tif (lineParent < 0) {\n\t\t\t// Backed up to a top level line, so try to find parent of initial line\n\t\t\tlineParent = pdoc->GetFoldParent(lineDoc);\n\t\t}\n\t\tif (lineParent >= 0) {\n\t\t\tif (lineDoc != lineParent)\n\t\t\t\tEnsureLineVisible(lineParent, enforcePolicy);\n\t\t\tif (!pcs->GetExpanded(lineParent)) {\n\t\t\t\tpcs->SetExpanded(lineParent, true);\n\t\t\t\tExpandLine(lineParent);\n\t\t\t}\n\t\t}\n\t\tSetScrollBars();\n\t\tRedraw();\n\t}\n\tif (enforcePolicy) {\n\t\tconst Sci::Line lineDisplay = pcs->DisplayFromDoc(lineDoc);\n\t\tif (FlagSet(visiblePolicy.policy, VisiblePolicy::Slop)) {\n\t\t\tif ((topLine > lineDisplay) || ((FlagSet(visiblePolicy.policy, VisiblePolicy::Strict)) && (topLine + visiblePolicy.slop > lineDisplay))) {\n\t\t\t\tSetTopLine(std::clamp<Sci::Line>(lineDisplay - visiblePolicy.slop, 0, MaxScrollPos()));\n\t\t\t\tSetVerticalScrollPos();\n\t\t\t\tRedraw();\n\t\t\t} else if ((lineDisplay > topLine + LinesOnScreen() - 1) ||\n\t\t\t        ((FlagSet(visiblePolicy.policy, VisiblePolicy::Strict)) && (lineDisplay > topLine + LinesOnScreen() - 1 - visiblePolicy.slop))) {\n\t\t\t\tSetTopLine(std::clamp<Sci::Line>(lineDisplay - LinesOnScreen() + 1 + visiblePolicy.slop, 0, MaxScrollPos()));\n\t\t\t\tSetVerticalScrollPos();\n\t\t\t\tRedraw();\n\t\t\t}\n\t\t} else {\n\t\t\tif ((topLine > lineDisplay) || (lineDisplay > topLine + LinesOnScreen() - 1) || (FlagSet(visiblePolicy.policy, VisiblePolicy::Strict))) {\n\t\t\t\tSetTopLine(std::clamp<Sci::Line>(lineDisplay - LinesOnScreen() / 2 + 1, 0, MaxScrollPos()));\n\t\t\t\tSetVerticalScrollPos();\n\t\t\t\tRedraw();\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid Editor::FoldAll(FoldAction action) {\n\tconst Sci::Line maxLine = pdoc->LinesTotal();\n\tconst bool contractAll = FlagSet(action, FoldAction::ContractEveryLevel);\n\taction = static_cast<FoldAction>(static_cast<int>(action) & ~static_cast<int>(FoldAction::ContractEveryLevel));\n\tbool expanding = action == FoldAction::Expand;\n\tif (!expanding) {\n\t\tpdoc->EnsureStyledTo(pdoc->Length());\n\t}\n\tSci::Line line = 0;\n\tif (action == FoldAction::Toggle) {\n\t\t// Discover current state\n\t\tfor (; line < maxLine; line++) {\n\t\t\tif (LevelIsHeader(pdoc->GetFoldLevel(line))) {\n\t\t\t\texpanding = !pcs->GetExpanded(line);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\tif (expanding) {\n\t\tpcs->SetVisible(0, maxLine-1, true);\n\t\tpcs->ExpandAll();\n\t} else {\n\t\tfor (; line < maxLine; line++) {\n\t\t\tconst FoldLevel level = pdoc->GetFoldLevel(line);\n\t\t\tif (LevelIsHeader(level)) {\n\t\t\t\tif (FoldLevel::Base == LevelNumberPart(level)) {\n\t\t\t\t\tSetFoldExpanded(line, false);\n\t\t\t\t\tconst Sci::Line lineMaxSubord = pdoc->GetLastChild(line);\n\t\t\t\t\tif (lineMaxSubord > line) {\n\t\t\t\t\t\tpcs->SetVisible(line + 1, lineMaxSubord, false);\n\t\t\t\t\t\tif (!contractAll) {\n\t\t\t\t\t\t\tline = lineMaxSubord;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else if (contractAll) {\n\t\t\t\t\tSetFoldExpanded(line, false);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tSetScrollBars();\n\tRedraw();\n}\n\nvoid Editor::FoldChanged(Sci::Line line, FoldLevel levelNow, FoldLevel levelPrev) {\n\tif (LevelIsHeader(levelNow)) {\n\t\tif (!LevelIsHeader(levelPrev)) {\n\t\t\t// Adding a fold point.\n\t\t\tif (pcs->SetExpanded(line, true)) {\n\t\t\t\tRedrawSelMargin();\n\t\t\t}\n\t\t\tFoldExpand(line, FoldAction::Expand, levelPrev);\n\t\t}\n\t} else if (LevelIsHeader(levelPrev)) {\n\t\tconst Sci::Line prevLine = line - 1;\n\t\tconst FoldLevel prevLineLevel = pdoc->GetFoldLevel(prevLine);\n\n\t\t// Combining two blocks where the first block is collapsed (e.g. by deleting the line(s) which separate(s) the two blocks)\n\t\tif ((LevelNumber(prevLineLevel) == LevelNumber(levelNow)) && !pcs->GetVisible(prevLine))\n\t\t\tFoldLine(pdoc->GetFoldParent(prevLine), FoldAction::Expand);\n\n\t\tif (!pcs->GetExpanded(line)) {\n\t\t\t// Removing the fold from one that has been contracted so should expand\n\t\t\t// otherwise lines are left invisible with no way to make them visible\n\t\t\tif (pcs->SetExpanded(line, true)) {\n\t\t\t\tRedrawSelMargin();\n\t\t\t}\n\t\t\t// Combining two blocks where the second one is collapsed (e.g. by adding characters in the line which separates the two blocks)\n\t\t\tFoldExpand(line, FoldAction::Expand, levelPrev);\n\t\t}\n\t}\n\tif (!LevelIsWhitespace(levelNow) &&\n\t        (LevelNumber(levelPrev) > LevelNumber(levelNow))) {\n\t\tif (pcs->HiddenLines()) {\n\t\t\t// See if should still be hidden\n\t\t\tconst Sci::Line parentLine = pdoc->GetFoldParent(line);\n\t\t\tif ((parentLine < 0) || (pcs->GetExpanded(parentLine) && pcs->GetVisible(parentLine))) {\n\t\t\t\tpcs->SetVisible(line, line, true);\n\t\t\t\tSetScrollBars();\n\t\t\t\tRedraw();\n\t\t\t}\n\t\t}\n\t}\n\n\t// Combining two blocks where the first one is collapsed (e.g. by adding characters in the line which separates the two blocks)\n\tif (!LevelIsWhitespace(levelNow) && (LevelNumber(levelPrev) < LevelNumber(levelNow))) {\n\t\tif (pcs->HiddenLines()) {\n\t\t\tconst Sci::Line parentLine = pdoc->GetFoldParent(line);\n\t\t\tif (!pcs->GetExpanded(parentLine) && pcs->GetVisible(line))\n\t\t\t\tFoldLine(parentLine, FoldAction::Expand);\n\t\t}\n\t}\n}\n\nvoid Editor::NeedShown(Sci::Position pos, Sci::Position len) {\n\tif (FlagSet(foldAutomatic, AutomaticFold::Show)) {\n\t\tconst Sci::Line lineStart = pdoc->SciLineFromPosition(pos);\n\t\tconst Sci::Line lineEnd = pdoc->SciLineFromPosition(pos+len);\n\t\tfor (Sci::Line line = lineStart; line <= lineEnd; line++) {\n\t\t\tEnsureLineVisible(line, false);\n\t\t}\n\t} else {\n\t\tNotifyNeedShown(pos, len);\n\t}\n}\n\nSci::Position Editor::GetTag(char *tagValue, int tagNumber) {\n\tconst char *text = nullptr;\n\tSci::Position length = 0;\n\tif ((tagNumber >= 1) && (tagNumber <= 9)) {\n\t\tchar name[3] = \"\\\\?\";\n\t\tname[1] = static_cast<char>(tagNumber + '0');\n\t\tlength = 2;\n\t\ttext = pdoc->SubstituteByPosition(name, &length);\n\t}\n\tif (tagValue) {\n\t\tif (text)\n\t\t\tmemcpy(tagValue, text, length + 1);\n\t\telse\n\t\t\t*tagValue = '\\0';\n\t}\n\treturn length;\n}\n\nSci::Position Editor::ReplaceTarget(ReplaceType replaceType, std::string_view text) {\n\tUndoGroup ug(pdoc);\n\n\tstd::string substituted;\t// Copy in case of re-entrance\n\n\tif (replaceType == ReplaceType::patterns) {\n\t\tSci::Position length = text.length();\n\t\tconst char *p = pdoc->SubstituteByPosition(text.data(), &length);\n\t\tif (!p) {\n\t\t\treturn 0;\n\t\t}\n\t\tsubstituted.assign(p, length);\n\t\ttext = substituted;\n\t}\n\n\tif (replaceType == ReplaceType::minimal) {\n\t\t// Check for prefix and suffix and reduce text and target to match.\n\t\t// This is performed with Range which doesn't support virtual space.\n\t\tRange range(targetRange.start.Position(), targetRange.end.Position());\n\t\tpdoc->TrimReplacement(text, range);\n\t\t// Re-apply virtual space to start if start position didn't change.\n\t\t// Don't bother with end as its virtual space is not used\n\t\tconst SelectionPosition start(range.start == targetRange.start.Position() ?\n\t\t\ttargetRange.start : SelectionPosition(range.start));\n\t\ttargetRange = SelectionSegment(start, SelectionPosition(range.end));\n\t}\n\n\t// Make a copy of targetRange in case callbacks use target\n\tSelectionSegment replaceRange = targetRange;\n\n\t// Remove the text inside the range\n\tif (replaceRange.Length() > 0)\n\t\tpdoc->DeleteChars(replaceRange.start.Position(), replaceRange.Length());\n\n\t// Realize virtual space of target start\n\tconst Sci::Position startAfterSpaceInsertion = RealizeVirtualSpace(replaceRange.start.Position(), replaceRange.start.VirtualSpace());\n\treplaceRange.start.SetPosition(startAfterSpaceInsertion);\n\treplaceRange.end = replaceRange.start;\n\n\t// Insert the new text\n\tconst Sci::Position lengthInserted = pdoc->InsertString(replaceRange.start.Position(), text);\n\treplaceRange.end.SetPosition(replaceRange.start.Position() + lengthInserted);\n\n\t// Copy back to targetRange in case application is chaining modifications\n\ttargetRange = replaceRange;\n\n\treturn text.length();\n}\n\nbool Editor::IsUnicodeMode() const noexcept {\n\treturn pdoc && (CpUtf8 == pdoc->dbcsCodePage);\n}\n\nint Editor::CodePage() const noexcept {\n\tif (pdoc)\n\t\treturn pdoc->dbcsCodePage;\n\treturn 0;\n}\n\nstd::unique_ptr<Surface> Editor::CreateMeasurementSurface() const {\n\tif (!wMain.GetID()) {\n\t\treturn {};\n\t}\n\tstd::unique_ptr<Surface> surf = Surface::Allocate(technology);\n\tsurf->Init(wMain.GetID());\n\tsurf->SetMode(CurrentSurfaceMode());\n\treturn surf;\n}\n\nstd::unique_ptr<Surface> Editor::CreateDrawingSurface(SurfaceID sid, std::optional<Scintilla::Technology> technologyOpt) const {\n\tif (!wMain.GetID()) {\n\t\treturn {};\n\t}\n\tstd::unique_ptr<Surface> surf = Surface::Allocate(technologyOpt ? *technologyOpt : technology);\n\tsurf->Init(sid, wMain.GetID());\n\tsurf->SetMode(CurrentSurfaceMode());\n\treturn surf;\n}\n\nSci::Line Editor::WrapCount(Sci::Line line) {\n\tAutoSurface surface(this);\n\tstd::shared_ptr<LineLayout> ll = view.RetrieveLineLayout(line, *this);\n\n\tif (surface && ll) {\n\t\tview.LayoutLine(*this, surface, vs, ll.get(), wrapWidth);\n\t\treturn ll->lines;\n\t}\n\treturn 1;\n}\n\nvoid Editor::AddStyledText(const char *buffer, Sci::Position appendLength) {\n\t// The buffer consists of alternating character bytes and style bytes\n\tconst Sci::Position textLength = appendLength / 2;\n\tstd::string text(textLength, '\\0');\n\tfor (Sci::Position i = 0; i < textLength; i++) {\n\t\ttext[i] = buffer[i*2];\n\t}\n\tconst Sci::Position lengthInserted = pdoc->InsertString(CurrentPosition(), text);\n\tfor (Sci::Position i = 0; i < textLength; i++) {\n\t\ttext[i] = buffer[i*2+1];\n\t}\n\tpdoc->StartStyling(CurrentPosition());\n\tpdoc->SetStyles(textLength, text.c_str());\n\tSetEmptySelection(sel.MainCaret() + lengthInserted);\n}\n\nSci::Position Editor::GetStyledText(char *buffer, Sci::Position cpMin, Sci::Position cpMax) const noexcept {\n\tSci::Position iPlace = 0;\n\tfor (Sci::Position iChar = cpMin; iChar < cpMax; iChar++) {\n\t\tbuffer[iPlace++] = pdoc->CharAt(iChar);\n\t\tbuffer[iPlace++] = pdoc->StyleAtNoExcept(iChar);\n\t}\n\tbuffer[iPlace] = '\\0';\n\tbuffer[iPlace + 1] = '\\0';\n\treturn iPlace;\n}\n\nSci::Position Editor::GetTextRange(char *buffer, Sci::Position cpMin, Sci::Position cpMax) const {\n\tconst Sci::Position cpEnd = (cpMax == -1) ? pdoc->Length() : cpMax;\n\tPLATFORM_ASSERT(cpEnd <= pdoc->Length());\n\tconst Sci::Position len = cpEnd - cpMin; \t// No -1 as cpMin and cpMax are referring to inter character positions\n\tpdoc->GetCharRange(buffer, cpMin, len);\n\t// Spec says copied text is terminated with a NUL\n\tbuffer[len] = '\\0';\n\treturn len; \t// Not including NUL\n}\n\nbool Editor::ValidMargin(uptr_t wParam) const noexcept {\n\treturn wParam < vs.ms.size();\n}\n\nvoid Editor::StyleSetMessage(Message iMessage, uptr_t wParam, sptr_t lParam) {\n\tvs.EnsureStyle(wParam);\n\tswitch (iMessage) {\n\tcase Message::StyleSetFore:\n\t\tvs.styles[wParam].fore = ColourRGBA::FromIpRGB(lParam);\n\t\tbreak;\n\tcase Message::StyleSetBack:\n\t\tvs.styles[wParam].back = ColourRGBA::FromIpRGB(lParam);\n\t\tbreak;\n\tcase Message::StyleSetBold:\n\t\tvs.styles[wParam].weight = lParam != 0 ? FontWeight::Bold : FontWeight::Normal;\n\t\tbreak;\n\tcase Message::StyleSetWeight:\n\t\tvs.styles[wParam].weight = static_cast<FontWeight>(lParam);\n\t\tbreak;\n\tcase Message::StyleSetStretch:\n\t\tvs.styles[wParam].stretch = static_cast<FontStretch>(lParam);\n\t\tbreak;\n\tcase Message::StyleSetItalic:\n\t\tvs.styles[wParam].italic = lParam != 0;\n\t\tbreak;\n\tcase Message::StyleSetEOLFilled:\n\t\tvs.styles[wParam].eolFilled = lParam != 0;\n\t\tbreak;\n\tcase Message::StyleSetSize:\n\t\tvs.styles[wParam].size = static_cast<int>(lParam * FontSizeMultiplier);\n\t\tbreak;\n\tcase Message::StyleSetSizeFractional:\n\t\tvs.styles[wParam].size = static_cast<int>(lParam);\n\t\tbreak;\n\tcase Message::StyleSetFont:\n\t\tif (lParam != 0) {\n\t\t\tvs.SetStyleFontName(static_cast<int>(wParam), ConstCharPtrFromSPtr(lParam));\n\t\t}\n\t\tbreak;\n\tcase Message::StyleSetUnderline:\n\t\tvs.styles[wParam].underline = lParam != 0;\n\t\tbreak;\n\tcase Message::StyleSetCase:\n\t\tvs.styles[wParam].caseForce = static_cast<Style::CaseForce>(lParam);\n\t\tbreak;\n\tcase Message::StyleSetCharacterSet:\n\t\tvs.styles[wParam].characterSet = static_cast<CharacterSet>(lParam);\n\t\tpdoc->SetCaseFolder(nullptr);\n\t\tbreak;\n\tcase Message::StyleSetVisible:\n\t\tvs.styles[wParam].visible = lParam != 0;\n\t\tbreak;\n\tcase Message::StyleSetInvisibleRepresentation: {\n\t\tconst char *utf8 = ConstCharPtrFromSPtr(lParam);\n\t\tchar *rep = vs.styles[wParam].invisibleRepresentation;\n\t\tconst int classified = UTF8Classify(utf8);\n\t\tif (!(classified & UTF8MaskInvalid)) {\n\t\t\t// valid UTF-8\n\t\t\tconst int len = classified & UTF8MaskWidth;\n\t\t\tfor (int i=0; i<len && i<UTF8MaxBytes; i++)\n\t\t\t\t*rep++ = *utf8++;\n\t\t}\n\t\t*rep = 0;\n\t\tbreak;\n\t}\n\tcase Message::StyleSetChangeable:\n\t\tvs.styles[wParam].changeable = lParam != 0;\n\t\tbreak;\n\tcase Message::StyleSetHotSpot:\n\t\tvs.styles[wParam].hotspot = lParam != 0;\n\t\tbreak;\n\tcase Message::StyleSetCheckMonospaced:\n\t\tvs.styles[wParam].checkMonospaced = lParam != 0;\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n\tInvalidateStyleRedraw();\n}\n\nsptr_t Editor::StyleGetMessage(Message iMessage, uptr_t wParam, sptr_t lParam) {\n\tvs.EnsureStyle(wParam);\n\tswitch (iMessage) {\n\tcase Message::StyleGetFore:\n\t\treturn vs.styles[wParam].fore.OpaqueRGB();\n\tcase Message::StyleGetBack:\n\t\treturn vs.styles[wParam].back.OpaqueRGB();\n\tcase Message::StyleGetBold:\n\t\treturn vs.styles[wParam].weight > FontWeight::Normal;\n\tcase Message::StyleGetWeight:\n\t\treturn static_cast<sptr_t>(vs.styles[wParam].weight);\n\tcase Message::StyleGetStretch:\n\t\treturn static_cast<sptr_t>(vs.styles[wParam].stretch);\n\tcase Message::StyleGetItalic:\n\t\treturn vs.styles[wParam].italic ? 1 : 0;\n\tcase Message::StyleGetEOLFilled:\n\t\treturn vs.styles[wParam].eolFilled ? 1 : 0;\n\tcase Message::StyleGetSize:\n\t\treturn vs.styles[wParam].size / FontSizeMultiplier;\n\tcase Message::StyleGetSizeFractional:\n\t\treturn vs.styles[wParam].size;\n\tcase Message::StyleGetFont:\n\t\treturn StringResult(lParam, vs.styles[wParam].fontName);\n\tcase Message::StyleGetUnderline:\n\t\treturn vs.styles[wParam].underline ? 1 : 0;\n\tcase Message::StyleGetCase:\n\t\treturn static_cast<int>(vs.styles[wParam].caseForce);\n\tcase Message::StyleGetCharacterSet:\n\t\treturn static_cast<sptr_t>(vs.styles[wParam].characterSet);\n\tcase Message::StyleGetVisible:\n\t\treturn vs.styles[wParam].visible ? 1 : 0;\n\tcase Message::StyleGetChangeable:\n\t\treturn vs.styles[wParam].changeable ? 1 : 0;\n\tcase Message::StyleGetInvisibleRepresentation:\n\t\treturn StringResult(lParam, vs.styles[wParam].invisibleRepresentation);\n\tcase Message::StyleGetHotSpot:\n\t\treturn vs.styles[wParam].hotspot ? 1 : 0;\n\tcase Message::StyleGetCheckMonospaced:\n\t\treturn vs.styles[wParam].checkMonospaced ? 1 : 0;\n\tdefault:\n\t\tbreak;\n\t}\n\treturn 0;\n}\n\nvoid Editor::SetSelectionNMessage(Message iMessage, uptr_t wParam, sptr_t lParam) {\n\tif (wParam >= sel.Count()) {\n\t\treturn;\n\t}\n\tInvalidateRange(sel.Range(wParam).Start().Position(), sel.Range(wParam).End().Position());\n\n\tswitch (iMessage) {\n\tcase Message::SetSelectionNCaret:\n\t\tsel.Range(wParam).caret.SetPosition(lParam);\n\t\tbreak;\n\n\tcase Message::SetSelectionNAnchor:\n\t\tsel.Range(wParam).anchor.SetPosition(lParam);\n\t\tbreak;\n\n\tcase Message::SetSelectionNCaretVirtualSpace:\n\t\tsel.Range(wParam).caret.SetVirtualSpace(lParam);\n\t\tbreak;\n\n\tcase Message::SetSelectionNAnchorVirtualSpace:\n\t\tsel.Range(wParam).anchor.SetVirtualSpace(lParam);\n\t\tbreak;\n\n\tcase Message::SetSelectionNStart:\n\t\tsel.Range(wParam).anchor.SetPosition(lParam);\n\t\tbreak;\n\n\tcase Message::SetSelectionNEnd:\n\t\tsel.Range(wParam).caret.SetPosition(lParam);\n\t\tbreak;\n\n\tdefault:\n\t\tbreak;\n\n\t}\n\n\tInvalidateRange(sel.Range(wParam).Start().Position(), sel.Range(wParam).End().Position());\n\tContainerNeedsUpdate(Update::Selection);\n}\n\nnamespace {\n\nconstexpr Selection::SelTypes SelTypeFromMode(SelectionMode mode) {\n\tswitch (mode) {\n\tcase SelectionMode::Rectangle:\n\t\treturn Selection::SelTypes::rectangle;\n\tcase SelectionMode::Lines:\n\t\treturn Selection::SelTypes::lines;\n\tcase SelectionMode::Thin:\n\t\treturn Selection::SelTypes::thin;\n\tcase SelectionMode::Stream:\n\tdefault:\n\t\treturn Selection::SelTypes::stream;\n\t}\n}\n\nsptr_t SPtrFromPtr(void *ptr) noexcept {\n\treturn reinterpret_cast<sptr_t>(ptr);\n}\n\n}\n\nvoid Editor::SetSelectionMode(uptr_t wParam, bool setMoveExtends) {\n\tconst Selection::SelTypes newSelType = SelTypeFromMode(static_cast<SelectionMode>(wParam));\n\tif (setMoveExtends) {\n\t\tsel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != newSelType));\n\t}\n\tsel.selType = newSelType;\n\tswitch (sel.selType) {\n\tcase Selection::SelTypes::rectangle:\n\t\tsel.Rectangular() = sel.RangeMain(); // adjust current selection\n\t\tbreak;\n\tcase Selection::SelTypes::lines:\n\t\tSetSelection(sel.RangeMain().caret, sel.RangeMain().anchor); // adjust current selection\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n\tInvalidateWholeSelection();\n}\n\nsptr_t Editor::StringResult(sptr_t lParam, const char *val) noexcept {\n\tconst size_t len = val ? strlen(val) : 0;\n\tif (lParam) {\n\t\tchar *ptr = CharPtrFromSPtr(lParam);\n\t\tif (val)\n\t\t\tmemcpy(ptr, val, len+1);\n\t\telse\n\t\t\t*ptr = 0;\n\t}\n\treturn len;\t// Not including NUL\n}\n\nsptr_t Editor::BytesResult(sptr_t lParam, const unsigned char *val, size_t len) noexcept {\n\t// No NUL termination: len is number of valid/displayed bytes\n\tif ((lParam) && (len > 0)) {\n\t\tchar *ptr = CharPtrFromSPtr(lParam);\n\t\tif (val)\n\t\t\tmemcpy(ptr, val, len);\n\t\telse\n\t\t\t*ptr = 0;\n\t}\n\treturn val ? len : 0;\n}\n\nsptr_t Editor::BytesResult(Scintilla::sptr_t lParam, std::string_view sv) noexcept {\n\t// No NUL termination: sv.length() is number of valid/displayed bytes\n\tif (lParam && !sv.empty()) {\n\t\tchar *ptr = CharPtrFromSPtr(lParam);\n\t\tmemcpy(ptr, sv.data(), sv.length());\n\t}\n\treturn sv.length();\n}\n\nsptr_t Editor::WndProc(Message iMessage, uptr_t wParam, sptr_t lParam) {\n\t//Platform::DebugPrintf(\"S start wnd proc %d %d %d\\n\",iMessage, wParam, lParam);\n\n\t// Optional macro recording hook\n\tif (recordingMacro)\n\t\tNotifyMacroRecord(iMessage, wParam, lParam);\n\n\tswitch (iMessage) {\n\n\tcase Message::GetText: {\n\t\t\tif (lParam == 0)\n\t\t\t\treturn pdoc->Length();\n\t\t\tchar *ptr = CharPtrFromSPtr(lParam);\n\t\t\tconst Sci_Position len = std::min<Sci_Position>(wParam, pdoc->Length());\n\t\t\tpdoc->GetCharRange(ptr, 0, len);\n\t\t\tptr[len] = '\\0';\n\t\t\treturn len;\n\t\t}\n\n\tcase Message::SetText: {\n\t\t\tif (lParam == 0)\n\t\t\t\treturn 0;\n\t\t\tUndoGroup ug(pdoc);\n\t\t\tpdoc->DeleteChars(0, pdoc->Length());\n\t\t\tSetEmptySelection(0);\n\t\t\tconst char *text = ConstCharPtrFromSPtr(lParam);\n\t\t\tpdoc->InsertString(0, text, strlen(text));\n\t\t\treturn 1;\n\t\t}\n\n\tcase Message::GetTextLength:\n\t\treturn pdoc->Length();\n\n\tcase Message::Cut:\n\t\tCut();\n\t\tSetLastXChosen();\n\t\tbreak;\n\n\tcase Message::Copy:\n\t\tCopy();\n\t\tbreak;\n\n\tcase Message::CopyAllowLine:\n\t\tCopyAllowLine();\n\t\tbreak;\n\n\tcase Message::CutAllowLine:\n\t\tCutAllowLine();\n\t\tSetLastXChosen();\n\t\tbreak;\n\n\tcase Message::GetCopySeparator:\n\t\treturn StringResult(lParam, copySeparator.c_str());\n\n\tcase Message::SetCopySeparator:\n\t\tcopySeparator = ConstCharPtrFromSPtr(lParam);\n\t\tbreak;\n\n\tcase Message::VerticalCentreCaret:\n\t\tVerticalCentreCaret();\n\t\tbreak;\n\n\tcase Message::MoveSelectedLinesUp:\n\t\tMoveSelectedLinesUp();\n\t\tbreak;\n\n\tcase Message::MoveSelectedLinesDown:\n\t\tMoveSelectedLinesDown();\n\t\tbreak;\n\n\tcase Message::CopyRange:\n\t\tCopyRangeToClipboard(PositionFromUPtr(wParam), lParam);\n\t\tbreak;\n\n\tcase Message::CopyText:\n\t\tCopyText(wParam, ConstCharPtrFromSPtr(lParam));\n\t\tbreak;\n\n\tcase Message::Paste:\n\t\tPaste();\n\t\tif ((caretSticky == CaretSticky::Off) || (caretSticky == CaretSticky::WhiteSpace)) {\n\t\t\tSetLastXChosen();\n\t\t}\n\t\tEnsureCaretVisible();\n\t\tbreak;\n\n\tcase Message::ReplaceRectangular: {\n\t\tUndoGroup ug(pdoc);\n\t\tif (!sel.Empty()) {\n\t\t\tClearSelection(); // want to replace rectangular selection contents\n\t\t}\n\t\tInsertPasteShape(ConstCharPtrFromSPtr(lParam), PositionFromUPtr(wParam), PasteShape::rectangular);\n\t\tbreak;\n\t}\n\n\tcase Message::Clear:\n\t\tClear();\n\t\tSetLastXChosen();\n\t\tEnsureCaretVisible();\n\t\tbreak;\n\n\tcase Message::Undo:\n\t\tUndo(wParam & 1); //Au\n\t\t//Undo();\n\t\t//SetLastXChosen();\n\t\tbreak;\n\n\tcase Message::CanUndo:\n\t\treturn (pdoc->CanUndo() && !pdoc->IsReadOnly()) ? 1 : 0;\n\n\tcase Message::EmptyUndoBuffer:\n\t\tpdoc->DeleteUndoHistory();\n\t\treturn 0;\n\n\tcase Message::GetFirstVisibleLine:\n\t\treturn topLine;\n\n\tcase Message::SetFirstVisibleLine:\n\t\tScrollTo(LineFromUPtr(wParam));\n\t\tbreak;\n\n\tcase Message::GetLine: {\t// Risk of overwriting the end of the buffer\n\t\t\tconst Sci::Position lineStart =\n\t\t\t\tpdoc->LineStart(LineFromUPtr(wParam));\n\t\t\tconst Sci::Position lineEnd =\n\t\t\t\tpdoc->LineStart(LineFromUPtr(wParam + 1));\n\t\t\t// not NUL terminated\n\t\t\tconst Sci::Position len = lineEnd - lineStart;\n\t\t\tif (lParam == 0) {\n\t\t\t\treturn len;\n\t\t\t}\n\t\t\tchar *ptr = CharPtrFromSPtr(lParam);\n\t\t\tpdoc->GetCharRange(ptr, lineStart, len);\n\t\t\treturn len;\n\t\t}\n\n\tcase Message::GetLineCount:\n\t\tif (pdoc->LinesTotal() == 0)\n\t\t\treturn 1;\n\t\telse\n\t\t\treturn pdoc->LinesTotal();\n\n\tcase Message::AllocateLines:\n\t\tpdoc->AllocateLines(wParam);\n\t\tbreak;\n\n\tcase Message::GetModify:\n\t\treturn !pdoc->IsSavePoint();\n\n\tcase Message::SetSel: {\n\t\t\tSci::Position nStart = PositionFromUPtr(wParam);\n\t\t\tSci::Position nEnd = lParam;\n\t\t\tif (nEnd < 0)\n\t\t\t\tnEnd = pdoc->Length();\n\t\t\tif (nStart < 0)\n\t\t\t\tnStart = nEnd; \t// Remove selection\n\t\t\tInvalidateSelection(SelectionRange(nStart, nEnd));\n\t\t\tsel.Clear();\n\t\t\tsel.selType = Selection::SelTypes::stream;\n\t\t\tSetSelection(nEnd, nStart);\n\t\t\tEnsureCaretVisible();\n\t\t}\n\t\tbreak;\n\n\tcase Message::GetSelText: {\n\t\t\tSelectionText selectedText;\n\t\t\tCopySelectionRange(&selectedText);\n\t\t\tif (lParam) {\n\t\t\t\tchar *ptr = CharPtrFromSPtr(lParam);\n\t\t\t\tsize_t iChar = selectedText.Length();\n\t\t\t\tif (iChar) {\n\t\t\t\t\tmemcpy(ptr, selectedText.Data(), iChar);\n\t\t\t\t}\n\t\t\t\tptr[iChar] = '\\0';\n\t\t\t}\n\t\t\treturn selectedText.Length();\n\t}\n\n\tcase Message::LineFromPosition:\n\t\tif (PositionFromUPtr(wParam) < 0)\n\t\t\treturn 0;\n\t\treturn pdoc->LineFromPosition(PositionFromUPtr(wParam));\n\n\tcase Message::PositionFromLine:\n\t\tif (LineFromUPtr(wParam) < 0)\n\t\t\twParam = pdoc->LineFromPosition(SelectionStart().Position());\n\t\tif (wParam == 0)\n\t\t\treturn 0; \t// Even if there is no text, there is a first line that starts at 0\n\t\tif (LineFromUPtr(wParam) > pdoc->LinesTotal())\n\t\t\treturn -1;\n\t\t//if (wParam > pdoc->LineFromPosition(pdoc->Length()))\t// Useful test, anyway...\n\t\t//\treturn -1;\n\t\treturn pdoc->LineStart(LineFromUPtr(wParam));\n\n\t\t// Replacement of the old Scintilla interpretation of EM_LINELENGTH\n\tcase Message::LineLength:\n\t\tif ((LineFromUPtr(wParam) < 0) ||\n\t\t        (LineFromUPtr(wParam) > pdoc->LineFromPosition(pdoc->Length())))\n\t\t\treturn 0;\n\t\treturn pdoc->LineStart(LineFromUPtr(wParam) + 1) - pdoc->LineStart(LineFromUPtr(wParam));\n\n\tcase Message::ReplaceSel: {\n\t\t\tif (lParam == 0)\n\t\t\t\treturn 0;\n\t\t\tUndoGroup ug(pdoc);\n\t\t\tClearSelection();\n\t\t\tconst char *replacement = ConstCharPtrFromSPtr(lParam);\n\t\t\tconst Sci::Position lengthInserted = pdoc->InsertString(\n\t\t\t\tsel.MainCaret(), replacement, strlen(replacement));\n\t\t\tSetEmptySelection(sel.MainCaret() + lengthInserted);\n\t\t\tSetLastXChosen();\n\t\t\tEnsureCaretVisible();\n\t\t}\n\t\tbreak;\n\n\tcase Message::SetTargetStart:\n\t\ttargetRange.start.SetPosition(PositionFromUPtr(wParam));\n\t\tbreak;\n\n\tcase Message::GetTargetStart:\n\t\treturn targetRange.start.Position();\n\n\tcase Message::SetTargetStartVirtualSpace:\n\t\ttargetRange.start.SetVirtualSpace(PositionFromUPtr(wParam));\n\t\tbreak;\n\n\tcase Message::GetTargetStartVirtualSpace:\n\t\treturn targetRange.start.VirtualSpace();\n\n\tcase Message::SetTargetEnd:\n\t\ttargetRange.end.SetPosition(PositionFromUPtr(wParam));\n\t\tbreak;\n\n\tcase Message::GetTargetEnd:\n\t\treturn targetRange.end.Position();\n\n\tcase Message::SetTargetEndVirtualSpace:\n\t\ttargetRange.end.SetVirtualSpace(PositionFromUPtr(wParam));\n\t\tbreak;\n\n\tcase Message::GetTargetEndVirtualSpace:\n\t\treturn targetRange.end.VirtualSpace();\n\n\tcase Message::SetTargetRange:\n\t\ttargetRange.start.SetPosition(PositionFromUPtr(wParam));\n\t\ttargetRange.end.SetPosition(lParam);\n\t\tbreak;\n\n\tcase Message::TargetWholeDocument:\n\t\ttargetRange.start.SetPosition(0);\n\t\ttargetRange.end.SetPosition(pdoc->Length());\n\t\tbreak;\n\n\tcase Message::TargetFromSelection:\n\t\ttargetRange.start = sel.RangeMain().Start();\n\t\ttargetRange.end = sel.RangeMain().End();\n\t\tbreak;\n\n\tcase Message::GetTargetText: {\n\t\t\tconst std::string text = RangeText(targetRange.start.Position(), targetRange.end.Position());\n\t\t\treturn BytesResult(lParam, text);\n\t\t}\n\n\tcase Message::ReplaceTarget:\n\t\tPLATFORM_ASSERT(lParam);\n\t\treturn ReplaceTarget(ReplaceType::basic, ViewFromParams(lParam, wParam));\n\n\tcase Message::ReplaceTargetRE:\n\t\tPLATFORM_ASSERT(lParam);\n\t\treturn ReplaceTarget(ReplaceType::patterns, ViewFromParams(lParam, wParam));\n\n\tcase Message::ReplaceTargetMinimal:\n\t\tPLATFORM_ASSERT(lParam);\n\t\treturn ReplaceTarget(ReplaceType::minimal, ViewFromParams(lParam, wParam));\n\n\tcase Message::SearchInTarget:\n\t\tPLATFORM_ASSERT(lParam);\n\t\treturn SearchInTarget(ConstCharPtrFromSPtr(lParam), PositionFromUPtr(wParam));\n\n\tcase Message::SetSearchFlags:\n\t\tsearchFlags = static_cast<FindOption>(wParam);\n\t\tbreak;\n\n\tcase Message::GetSearchFlags:\n\t\treturn static_cast<sptr_t>(searchFlags);\n\n\tcase Message::GetTag:\n\t\treturn GetTag(CharPtrFromSPtr(lParam), static_cast<int>(wParam));\n\n\tcase Message::PositionBefore:\n\t\treturn pdoc->MovePositionOutsideChar(PositionFromUPtr(wParam) - 1, -1, true);\n\n\tcase Message::PositionAfter:\n\t\treturn pdoc->MovePositionOutsideChar(PositionFromUPtr(wParam) + 1, 1, true);\n\n\tcase Message::PositionRelative:\n\t\treturn std::clamp<Sci::Position>(pdoc->GetRelativePosition(\n\t\t\tPositionFromUPtr(wParam), lParam),\n\t\t\t0, pdoc->Length());\n\n\tcase Message::PositionRelativeCodeUnits:\n\t\treturn std::clamp<Sci::Position>(pdoc->GetRelativePositionUTF16(\n\t\t\tPositionFromUPtr(wParam), lParam),\n\t\t\t0, pdoc->Length());\n\n\tcase Message::LineScroll:\n\t\tScrollTo(topLine + lParam);\n\t\tHorizontalScrollTo(xOffset + static_cast<int>(static_cast<int>(wParam) * vs.spaceWidth));\n\t\treturn 1;\n\n\tcase Message::ScrollVertical:\n\t\tif (Wrapping()) {\n\t\t\tscrollToAfterWrap = { LineFromUPtr(wParam), lParam };\n\t\t} else {\n\t\t\tscrollToAfterWrap.reset();\n\t\t}\n\t\tScrollTo(pcs->DisplayFromDocSub(LineFromUPtr(wParam), lParam));\n\t\tbreak;\n\n\tcase Message::SetXOffset:\n\t\txOffset = static_cast<int>(wParam);\n\t\tContainerNeedsUpdate(Update::HScroll);\n\t\tSetHorizontalScrollPos();\n\t\tRedraw();\n\t\tbreak;\n\n\tcase Message::GetXOffset:\n\t\treturn xOffset;\n\n\tcase Message::ChooseCaretX:\n\t\tSetLastXChosen();\n\t\tbreak;\n\n\tcase Message::ScrollCaret:\n\t\tEnsureCaretVisible();\n\t\tbreak;\n\n\tcase Message::SetReadOnly:\n\t\tpdoc->SetReadOnly(wParam != 0);\n\t\treturn 1;\n\n\tcase Message::GetReadOnly:\n\t\treturn pdoc->IsReadOnly();\n\n\tcase Message::CanPaste:\n\t\treturn CanPaste();\n\n\tcase Message::PointXFromPosition:\n\t\tif (lParam < 0) {\n\t\t\treturn 0;\n\t\t} else {\n\t\t\tconst Point pt = LocationFromPosition(lParam);\n\t\t\t// Convert to view-relative\n\t\t\treturn static_cast<int>(pt.x) - vs.textStart + vs.fixedColumnWidth;\n\t\t}\n\n\tcase Message::PointYFromPosition:\n\t\tif (lParam < 0) {\n\t\t\treturn 0;\n\t\t} else {\n\t\t\tconst Point pt = LocationFromPosition(lParam);\n\t\t\treturn static_cast<int>(pt.y);\n\t\t}\n\n\tcase Message::FindText:\n\t\treturn FindText(wParam, lParam);\n\n\tcase Message::FindTextFull:\n\t\treturn FindTextFull(wParam, lParam);\n\n\tcase Message::GetTextRange:\n\t\tif (TextRange *tr = static_cast<TextRange *>(PtrFromSPtr(lParam))) {\n\t\t\treturn GetTextRange(tr->lpstrText, tr->chrg.cpMin, tr->chrg.cpMax);\n\t\t}\n\t\treturn 0;\n\n\tcase Message::GetTextRangeFull:\n\t\tif (TextRangeFull *tr = static_cast<TextRangeFull *>(PtrFromSPtr(lParam))) {\n\t\t\treturn GetTextRange(tr->lpstrText, tr->chrg.cpMin, tr->chrg.cpMax);\n\t\t}\n\t\treturn 0;\n\n\tcase Message::HideSelection:\n\t\tvs.selection.visible = wParam == 0;\n\t\tRedraw();\n\t\tbreak;\n\n\tcase Message::GetSelectionHidden:\n\t\treturn !vs.selection.visible;\n\t\tbreak;\n\n\tcase Message::FormatRange:\n\tcase Message::FormatRangeFull:\n\t\treturn FormatRange(iMessage, wParam, lParam);\n\n\tcase Message::GetMarginLeft:\n\t\treturn vs.leftMarginWidth;\n\n\tcase Message::GetMarginRight:\n\t\treturn vs.rightMarginWidth;\n\n\tcase Message::SetMarginLeft:\n\t\tlastXChosen += static_cast<int>(lParam) - vs.leftMarginWidth;\n\t\tvs.leftMarginWidth = static_cast<int>(lParam);\n\t\tInvalidateStyleRedraw();\n\t\tbreak;\n\n\tcase Message::SetMarginRight:\n\t\tvs.rightMarginWidth = static_cast<int>(lParam);\n\t\tInvalidateStyleRedraw();\n\t\tbreak;\n\n\t\t// Control specific messages\n\n\tcase Message::AddText: {\n\t\t\tif (lParam == 0)\n\t\t\t\treturn 0;\n\t\t\tconst Sci::Position lengthInserted = pdoc->InsertString(\n\t\t\t\tCurrentPosition(), ConstCharPtrFromSPtr(lParam), PositionFromUPtr(wParam));\n\t\t\tSetEmptySelection(sel.MainCaret() + lengthInserted);\n\t\t\treturn 0;\n\t\t}\n\n\tcase Message::AddStyledText:\n\t\tif (lParam)\n\t\t\tAddStyledText(ConstCharPtrFromSPtr(lParam), PositionFromUPtr(wParam));\n\t\treturn 0;\n\n\tcase Message::InsertText: {\n\t\t\tif (lParam == 0)\n\t\t\t\treturn 0;\n\t\t\tSci::Position insertPos = PositionFromUPtr(wParam);\n\t\t\tif (insertPos == -1)\n\t\t\t\tinsertPos = CurrentPosition();\n\t\t\tSci::Position newCurrent = CurrentPosition();\n\t\t\tconst char *sz = ConstCharPtrFromSPtr(lParam);\n\t\t\tconst Sci::Position lengthInserted = pdoc->InsertString(insertPos, sz, strlen(sz));\n\t\t\tif (newCurrent > insertPos)\n\t\t\t\tnewCurrent += lengthInserted;\n\t\t\tSetEmptySelection(newCurrent);\n\t\t\treturn 0;\n\t\t}\n\n\tcase Message::ChangeInsertion:\n\t\tPLATFORM_ASSERT(lParam);\n\t\tpdoc->ChangeInsertion(ConstCharPtrFromSPtr(lParam), PositionFromUPtr(wParam));\n\t\treturn 0;\n\n\tcase Message::AppendText:\n\t\tpdoc->InsertString(pdoc->Length(),\n\t\t\tConstCharPtrFromSPtr(lParam), PositionFromUPtr(wParam));\n\t\treturn 0;\n\n\tcase Message::ClearAll:\n\t\tClearAll();\n\t\treturn 0;\n\n\tcase Message::DeleteRange:\n\t\tpdoc->DeleteChars(PositionFromUPtr(wParam), lParam);\n\t\treturn 0;\n\n\tcase Message::ClearDocumentStyle:\n\t\tClearDocumentStyle();\n\t\treturn 0;\n\n\tcase Message::SetUndoCollection:\n\t\tpdoc->SetUndoCollection(wParam != 0);\n\t\treturn 0;\n\n\tcase Message::GetUndoCollection:\n\t\treturn pdoc->IsCollectingUndo();\n\n\tcase Message::BeginUndoAction:\n\t\tpdoc->BeginUndoAction();\n\t\treturn 0;\n\n\tcase Message::EndUndoAction:\n\t\tpdoc->EndUndoAction();\n\t\treturn 0;\n\n\tcase Message::GetUndoSequence:\n\t\treturn pdoc->UndoSequenceDepth();\n\n\tcase Message::GetUndoActions:\n\t\treturn pdoc->UndoActions();\n\n\tcase Message::SetUndoSavePoint:\n\t\tpdoc->SetUndoSavePoint(static_cast<int>(wParam));\n\t\tbreak;\n\n\tcase Message::GetUndoSavePoint:\n\t\treturn pdoc->UndoSavePoint();\n\n\tcase Message::SetUndoDetach:\n\t\tpdoc->SetUndoDetach(static_cast<int>(wParam));\n\t\tbreak;\n\n\tcase Message::GetUndoDetach:\n\t\treturn pdoc->UndoDetach();\n\n\tcase Message::SetUndoTentative:\n\t\tpdoc->SetUndoTentative(static_cast<int>(wParam));\n\t\tbreak;\n\n\tcase Message::GetUndoTentative:\n\t\treturn pdoc->UndoTentative();\n\n\tcase Message::SetUndoCurrent:\n\t\tpdoc->SetUndoCurrent(static_cast<int>(wParam));\n\t\tbreak;\n\n\tcase Message::GetUndoCurrent:\n\t\treturn pdoc->UndoCurrent();\n\n\tcase Message::GetUndoActionType:\n\t\treturn pdoc->UndoActionType(static_cast<int>(wParam));\n\n\tcase Message::GetUndoActionPosition:\n\t\treturn pdoc->UndoActionPosition(static_cast<int>(wParam));\n\n\tcase Message::GetUndoActionText: {\n\t\tconst std::string_view text = pdoc->UndoActionText(static_cast<int>(wParam));\n\t\treturn BytesResult(lParam, text);\n\t}\n\n\tcase Message::PushUndoActionType:\n\t\tpdoc->PushUndoActionType(static_cast<int>(wParam), lParam);\n\t\tbreak;\n\n\tcase Message::ChangeLastUndoActionText:\n\t\tpdoc->ChangeLastUndoActionText(wParam, CharPtrFromSPtr(lParam));\n\t\tbreak;\n\n\tcase Message::GetCaretPeriod:\n\t\treturn caret.period;\n\n\tcase Message::SetCaretPeriod:\n\t\tCaretSetPeriod(static_cast<int>(wParam));\n\t\tbreak;\n\n\tcase Message::GetWordChars:\n\t\treturn pdoc->GetCharsOfClass(CharacterClass::word, UCharPtrFromSPtr(lParam));\n\n\tcase Message::SetWordChars: {\n\t\t\tpdoc->SetDefaultCharClasses(false);\n\t\t\tif (lParam == 0)\n\t\t\t\treturn 0;\n\t\t\tpdoc->SetCharClasses(ConstUCharPtrFromSPtr(lParam), CharacterClass::word);\n\t\t}\n\t\tbreak;\n\n\tcase Message::GetWhitespaceChars:\n\t\treturn pdoc->GetCharsOfClass(CharacterClass::space, UCharPtrFromSPtr(lParam));\n\n\tcase Message::SetWhitespaceChars: {\n\t\t\tif (lParam == 0)\n\t\t\t\treturn 0;\n\t\t\tpdoc->SetCharClasses(ConstUCharPtrFromSPtr(lParam), CharacterClass::space);\n\t\t}\n\t\tbreak;\n\n\tcase Message::GetPunctuationChars:\n\t\treturn pdoc->GetCharsOfClass(CharacterClass::punctuation, UCharPtrFromSPtr(lParam));\n\n\tcase Message::SetPunctuationChars: {\n\t\t\tif (lParam == 0)\n\t\t\t\treturn 0;\n\t\t\tpdoc->SetCharClasses(ConstUCharPtrFromSPtr(lParam), CharacterClass::punctuation);\n\t\t}\n\t\tbreak;\n\n\tcase Message::SetCharsDefault:\n\t\tpdoc->SetDefaultCharClasses(true);\n\t\tbreak;\n\n\tcase Message::SetCharacterCategoryOptimization:\n\t\tpdoc->SetCharacterCategoryOptimization(static_cast<int>(wParam));\n\t\tbreak;\n\n\tcase Message::GetCharacterCategoryOptimization:\n\t\treturn pdoc->CharacterCategoryOptimization();\n\n\tcase Message::GetLength:\n\t\treturn pdoc->Length();\n\n\tcase Message::Allocate:\n\t\tpdoc->Allocate(PositionFromUPtr(wParam));\n\t\tbreak;\n\n\tcase Message::GetCharAt:\n\t\treturn pdoc->CharAt(PositionFromUPtr(wParam));\n\n\tcase Message::SetCurrentPos:\n\t\tif (sel.IsRectangular()) {\n\t\t\tsel.Rectangular().caret.SetPosition(PositionFromUPtr(wParam));\n\t\t\tSetRectangularRange();\n\t\t\tRedraw();\n\t\t} else {\n\t\t\tSetSelection(PositionFromUPtr(wParam), sel.MainAnchor());\n\t\t}\n\t\tbreak;\n\n\tcase Message::GetCurrentPos:\n\t\treturn sel.IsRectangular() ? sel.Rectangular().caret.Position() : sel.MainCaret();\n\n\tcase Message::SetAnchor:\n\t\tif (sel.IsRectangular()) {\n\t\t\tsel.Rectangular().anchor.SetPosition(PositionFromUPtr(wParam));\n\t\t\tSetRectangularRange();\n\t\t\tRedraw();\n\t\t} else {\n\t\t\tSetSelection(sel.MainCaret(), PositionFromUPtr(wParam));\n\t\t}\n\t\tbreak;\n\n\tcase Message::GetAnchor:\n\t\treturn sel.IsRectangular() ? sel.Rectangular().anchor.Position() : sel.MainAnchor();\n\n\tcase Message::SetSelectionStart:\n\t\tSetSelection(std::max(sel.MainCaret(), PositionFromUPtr(wParam)), PositionFromUPtr(wParam));\n\t\tbreak;\n\n\tcase Message::GetSelectionStart:\n\t\treturn sel.LimitsForRectangularElseMain().start.Position();\n\n\tcase Message::SetSelectionEnd:\n\t\tSetSelection(PositionFromUPtr(wParam), std::min(sel.MainAnchor(), PositionFromUPtr(wParam)));\n\t\tbreak;\n\n\tcase Message::GetSelectionEnd:\n\t\treturn sel.LimitsForRectangularElseMain().end.Position();\n\n\tcase Message::SetEmptySelection:\n\t\tSetEmptySelection(PositionFromUPtr(wParam));\n\t\tbreak;\n\n\tcase Message::SetPrintMagnification:\n\t\tview.printParameters.magnification = static_cast<int>(wParam);\n\t\tbreak;\n\n\tcase Message::GetPrintMagnification:\n\t\treturn view.printParameters.magnification;\n\n\tcase Message::SetPrintColourMode:\n\t\tview.printParameters.colourMode = static_cast<PrintOption>(wParam);\n\t\tbreak;\n\n\tcase Message::GetPrintColourMode:\n\t\treturn static_cast<sptr_t>(view.printParameters.colourMode);\n\n\tcase Message::SetPrintWrapMode:\n\t\tview.printParameters.wrapState = (static_cast<Wrap>(wParam) == Wrap::Word) ? Wrap::Word : Wrap::None;\n\t\tbreak;\n\n\tcase Message::GetPrintWrapMode:\n\t\treturn static_cast<sptr_t>(view.printParameters.wrapState);\n\n\tcase Message::GetStyleAt:\n\t\tif (PositionFromUPtr(wParam) >= pdoc->Length())\n\t\t\treturn 0;\n\t\telse\n\t\t\treturn pdoc->StyleAt(PositionFromUPtr(wParam));\n\n\tcase Message::GetStyleIndexAt:\n\t\tif (PositionFromUPtr(wParam) >= pdoc->Length())\n\t\t\treturn 0;\n\t\telse\n\t\t\treturn pdoc->StyleIndexAt(PositionFromUPtr(wParam));\n\n\tcase Message::Redo:\n\t\tRedo(wParam & 1); //Au\n\t\t//Redo();\n\t\tbreak;\n\n\tcase Message::SelectAll:\n\t\tSelectAll();\n\t\tbreak;\n\n\tcase Message::SetSavePoint:\n\t\tpdoc->SetSavePoint();\n\t\tbreak;\n\n\tcase Message::GetStyledText:\n\t\tif (TextRange *tr = static_cast<TextRange *>(PtrFromSPtr(lParam))) {\n\t\t\treturn GetStyledText(tr->lpstrText, tr->chrg.cpMin, tr->chrg.cpMax);\n\t\t}\n\t\treturn 0;\n\n\tcase Message::GetStyledTextFull:\n\t\tif (TextRangeFull *tr = static_cast<TextRangeFull *>(PtrFromSPtr(lParam))) {\n\t\t\treturn GetStyledText(tr->lpstrText, tr->chrg.cpMin, tr->chrg.cpMax);\n\t\t}\n\t\treturn 0;\n\n\tcase Message::CanRedo:\n\t\treturn (pdoc->CanRedo() && !pdoc->IsReadOnly()) ? 1 : 0;\n\n\tcase Message::MarkerLineFromHandle:\n\t\treturn pdoc->LineFromHandle(static_cast<int>(wParam));\n\n\tcase Message::MarkerDeleteHandle:\n\t\tpdoc->DeleteMarkFromHandle(static_cast<int>(wParam));\n\t\tbreak;\n\n\tcase Message::MarkerHandleFromLine:\n\t\treturn pdoc->MarkerHandleFromLine(LineFromUPtr(wParam), static_cast<int>(lParam));\n\n\tcase Message::MarkerNumberFromLine:\n\t\treturn pdoc->MarkerNumberFromLine(LineFromUPtr(wParam), static_cast<int>(lParam));\n\n\tcase Message::GetViewWS:\n\t\treturn static_cast<sptr_t>(vs.viewWhitespace);\n\n\tcase Message::SetViewWS:\n\t\tvs.viewWhitespace = static_cast<WhiteSpace>(wParam);\n\t\tRedraw();\n\t\tbreak;\n\n\tcase Message::GetTabDrawMode:\n\t\treturn static_cast<sptr_t>(vs.tabDrawMode);\n\n\tcase Message::SetTabDrawMode:\n\t\tvs.tabDrawMode = static_cast<TabDrawMode>(wParam);\n\t\tRedraw();\n\t\tbreak;\n\n\tcase Message::GetWhitespaceSize:\n\t\treturn vs.whitespaceSize;\n\n\tcase Message::SetWhitespaceSize:\n\t\tvs.whitespaceSize = static_cast<int>(wParam);\n\t\tRedraw();\n\t\tbreak;\n\n\tcase Message::PositionFromPoint:\n\t\treturn PositionFromLocation(PointFromParameters(wParam, lParam), false, false);\n\n\tcase Message::PositionFromPointClose:\n\t\treturn PositionFromLocation(PointFromParameters(wParam, lParam), true, false);\n\n\tcase Message::CharPositionFromPoint:\n\t\treturn PositionFromLocation(PointFromParameters(wParam, lParam), false, true);\n\n\tcase Message::CharPositionFromPointClose:\n\t\treturn PositionFromLocation(PointFromParameters(wParam, lParam), true, true);\n\n\tcase Message::GotoLine:\n\t\tGoToLine(LineFromUPtr(wParam));\n\t\tbreak;\n\n\tcase Message::GotoPos:\n\t\tSetEmptySelection(PositionFromUPtr(wParam));\n\t\tEnsureCaretVisible();\n\t\tbreak;\n\n\tcase Message::GetCurLine: {\n\t\t\tconst Sci::Line lineCurrentPos = pdoc->SciLineFromPosition(sel.MainCaret());\n\t\t\tconst Sci::Position lineStart = pdoc->LineStart(lineCurrentPos);\n\t\t\tconst Sci::Position lineEnd = pdoc->LineStart(lineCurrentPos + 1);\n\t\t\tif (lParam == 0) {\n\t\t\t\treturn lineEnd - lineStart;\n\t\t\t}\n\t\t\tchar *ptr = CharPtrFromSPtr(lParam);\n\t\t\tconst Sci::Position len = std::min<uptr_t>(lineEnd - lineStart, wParam);\n\t\t\tpdoc->GetCharRange(ptr, lineStart, len);\n\t\t\tptr[len] = '\\0';\n\t\t\treturn sel.MainCaret() - lineStart;\n\t\t}\n\n\tcase Message::GetEndStyled:\n\t\treturn pdoc->GetEndStyled();\n\n\tcase Message::GetEOLMode:\n\t\treturn static_cast<sptr_t>(pdoc->eolMode);\n\n\tcase Message::SetEOLMode:\n\t\tpdoc->eolMode = static_cast<EndOfLine>(wParam);\n\t\tbreak;\n\n\tcase Message::SetLineEndTypesAllowed:\n\t\tif (pdoc->SetLineEndTypesAllowed(static_cast<LineEndType>(wParam))) {\n\t\t\tpcs->Clear();\n\t\t\tpcs->InsertLines(0, pdoc->LinesTotal() - 1);\n\t\t\tSetAnnotationHeights(0, pdoc->LinesTotal());\n\t\t\tInvalidateStyleRedraw();\n\t\t}\n\t\tbreak;\n\n\tcase Message::GetLineEndTypesAllowed:\n\t\treturn static_cast<sptr_t>(pdoc->GetLineEndTypesAllowed());\n\n\tcase Message::GetLineEndTypesActive:\n\t\treturn static_cast<sptr_t>(pdoc->GetLineEndTypesActive());\n\n\tcase Message::StartStyling:\n\t\tpdoc->StartStyling(PositionFromUPtr(wParam));\n\t\tbreak;\n\n\tcase Message::SetStyling:\n\t\tif (PositionFromUPtr(wParam) < 0)\n\t\t\terrorStatus = Status::Failure;\n\t\telse\n\t\t\tpdoc->SetStyleFor(PositionFromUPtr(wParam), static_cast<char>(lParam));\n\t\tbreak;\n\n\tcase Message::SetStylingEx:             // Specify a complete styling buffer\n\t\tif (lParam == 0)\n\t\t\treturn 0;\n\t\tpdoc->SetStyles(PositionFromUPtr(wParam), ConstCharPtrFromSPtr(lParam));\n\t\tbreak;\n\n\tcase Message::SetBufferedDraw:\n\t\tview.bufferedDraw = wParam != 0;\n\t\tbreak;\n\n\tcase Message::GetBufferedDraw:\n\t\treturn view.bufferedDraw;\n\n#ifdef INCLUDE_DEPRECATED_FEATURES\n\tcase SCI_GETTWOPHASEDRAW:\n\t\treturn view.phasesDraw == EditView::phasesTwo;\n\n\tcase SCI_SETTWOPHASEDRAW:\n\t\tif (view.SetTwoPhaseDraw(wParam != 0))\n\t\t\tInvalidateStyleRedraw();\n\t\tbreak;\n#endif\n\n\tcase Message::GetPhasesDraw:\n\t\treturn static_cast<sptr_t>(view.phasesDraw);\n\n\tcase Message::SetPhasesDraw:\n\t\tif (view.SetPhasesDraw(static_cast<int>(wParam)))\n\t\t\tInvalidateStyleRedraw();\n\t\tbreak;\n\n\tcase Message::SetFontQuality:\n\t\tvs.extraFontFlag = static_cast<FontQuality>(\n\t\t\t(static_cast<int>(vs.extraFontFlag) & ~static_cast<int>(FontQuality::QualityMask)) |\n\t\t\t(wParam & static_cast<int>(FontQuality::QualityMask)));\n\t\tInvalidateStyleRedraw();\n\t\tbreak;\n\n\tcase Message::GetFontQuality:\n\t\treturn static_cast<int>(vs.extraFontFlag) & static_cast<int>(FontQuality::QualityMask);\n\n\tcase Message::SetTabWidth:\n\t\tif (wParam > 0) {\n\t\t\tpdoc->tabInChars = static_cast<int>(wParam);\n\t\t\tif (pdoc->indentInChars == 0)\n\t\t\t\tpdoc->actualIndentInChars = pdoc->tabInChars;\n\t\t}\n\t\tInvalidateStyleRedraw();\n\t\tbreak;\n\n\tcase Message::GetTabWidth:\n\t\treturn pdoc->tabInChars;\n\n\tcase Message::SetTabMinimumWidth:\n\t\tSetAppearance(view.tabWidthMinimumPixels, static_cast<int>(wParam));\n\t\tbreak;\n\n\tcase Message::GetTabMinimumWidth:\n\t\treturn view.tabWidthMinimumPixels;\n\n\tcase Message::ClearTabStops:\n\t\tif (view.ClearTabstops(LineFromUPtr(wParam))) {\n\t\t\tconst DocModification mh(ModificationFlags::ChangeTabStops, 0, 0, 0, nullptr, LineFromUPtr(wParam));\n\t\t\tNotifyModified(pdoc, mh, nullptr);\n\t\t}\n\t\tbreak;\n\n\tcase Message::AddTabStop:\n\t\tif (view.AddTabstop(LineFromUPtr(wParam), static_cast<int>(lParam))) {\n\t\t\tconst DocModification mh(ModificationFlags::ChangeTabStops, 0, 0, 0, nullptr, LineFromUPtr(wParam));\n\t\t\tNotifyModified(pdoc, mh, nullptr);\n\t\t}\n\t\tbreak;\n\n\tcase Message::GetNextTabStop:\n\t\treturn view.GetNextTabstop(LineFromUPtr(wParam), static_cast<int>(lParam));\n\n\tcase Message::SetIndent:\n\t\tpdoc->indentInChars = static_cast<int>(wParam);\n\t\tif (pdoc->indentInChars != 0)\n\t\t\tpdoc->actualIndentInChars = pdoc->indentInChars;\n\t\telse\n\t\t\tpdoc->actualIndentInChars = pdoc->tabInChars;\n\t\tInvalidateStyleRedraw();\n\t\tbreak;\n\n\tcase Message::GetIndent:\n\t\treturn pdoc->indentInChars;\n\n\tcase Message::SetUseTabs:\n\t\tpdoc->useTabs = wParam != 0;\n\t\tInvalidateStyleRedraw();\n\t\tbreak;\n\n\tcase Message::GetUseTabs:\n\t\treturn pdoc->useTabs;\n\n\tcase Message::SetLineIndentation:\n\t\tpdoc->SetLineIndentation(LineFromUPtr(wParam), lParam);\n\t\tbreak;\n\n\tcase Message::GetLineIndentation:\n\t\treturn pdoc->GetLineIndentation(LineFromUPtr(wParam));\n\n\tcase Message::GetLineIndentPosition:\n\t\treturn pdoc->GetLineIndentPosition(LineFromUPtr(wParam));\n\n\tcase Message::SetTabIndents:\n\t\tpdoc->tabIndents = wParam != 0;\n\t\tbreak;\n\n\tcase Message::GetTabIndents:\n\t\treturn pdoc->tabIndents;\n\n\tcase Message::SetBackSpaceUnIndents:\n\t\tpdoc->backspaceUnindents = wParam != 0;\n\t\tbreak;\n\n\tcase Message::GetBackSpaceUnIndents:\n\t\treturn pdoc->backspaceUnindents;\n\n\tcase Message::SetMouseDwellTime:\n\t\tdwellDelay = static_cast<int>(wParam);\n\t\tticksToDwell = dwellDelay;\n\t\tbreak;\n\n\tcase Message::GetMouseDwellTime:\n\t\treturn dwellDelay;\n\n\tcase Message::WordStartPosition:\n\t\treturn pdoc->ExtendWordSelect(PositionFromUPtr(wParam), -1, lParam != 0);\n\n\tcase Message::WordEndPosition:\n\t\treturn pdoc->ExtendWordSelect(PositionFromUPtr(wParam), 1, lParam != 0);\n\n\tcase Message::IsRangeWord:\n\t\treturn pdoc->IsWordAt(PositionFromUPtr(wParam), lParam);\n\n\tcase Message::SetIdleStyling:\n\t\tidleStyling = static_cast<IdleStyling>(wParam);\n\t\tbreak;\n\n\tcase Message::GetIdleStyling:\n\t\treturn static_cast<sptr_t>(idleStyling);\n\n\tcase Message::SetWrapMode:\n\t\tif (vs.SetWrapState(static_cast<Wrap>(wParam))) {\n\t\t\txOffset = 0;\n\t\t\tContainerNeedsUpdate(Update::HScroll);\n\t\t\tInvalidateStyleRedraw();\n\t\t\tReconfigureScrollBars();\n\t\t}\n\t\tbreak;\n\n\tcase Message::GetWrapMode:\n\t\treturn static_cast<sptr_t>(vs.wrap.state);\n\n\tcase Message::SetWrapVisualFlags:\n\t\tif (vs.SetWrapVisualFlags(static_cast<WrapVisualFlag>(wParam))) {\n\t\t\tInvalidateStyleRedraw();\n\t\t\tReconfigureScrollBars();\n\t\t}\n\t\tbreak;\n\n\tcase Message::GetWrapVisualFlags:\n\t\treturn static_cast<sptr_t>(vs.wrap.visualFlags);\n\n\tcase Message::SetWrapVisualFlagsLocation:\n\t\tif (vs.SetWrapVisualFlagsLocation(static_cast<WrapVisualLocation>(wParam))) {\n\t\t\tInvalidateStyleRedraw();\n\t\t}\n\t\tbreak;\n\n\tcase Message::GetWrapVisualFlagsLocation:\n\t\treturn static_cast<sptr_t>(vs.wrap.visualFlagsLocation);\n\n\tcase Message::SetWrapStartIndent:\n\t\tif (vs.SetWrapVisualStartIndent(static_cast<int>(wParam))) {\n\t\t\tInvalidateStyleRedraw();\n\t\t\tReconfigureScrollBars();\n\t\t}\n\t\tbreak;\n\n\tcase Message::GetWrapStartIndent:\n\t\treturn vs.wrap.visualStartIndent;\n\n\tcase Message::SetWrapIndentMode:\n\t\tif (vs.SetWrapIndentMode(static_cast<WrapIndentMode>(wParam))) {\n\t\t\tInvalidateStyleRedraw();\n\t\t\tReconfigureScrollBars();\n\t\t}\n\t\tbreak;\n\n\tcase Message::GetWrapIndentMode:\n\t\treturn static_cast<sptr_t>(vs.wrap.indentMode);\n\n\tcase Message::SetLayoutCache:\n\t\tif (static_cast<LineCache>(wParam) <= LineCache::Document) {\n\t\t\tview.llc.SetLevel(static_cast<LineCache>(wParam));\n\t\t}\n\t\tbreak;\n\n\tcase Message::GetLayoutCache:\n\t\treturn static_cast<sptr_t>(view.llc.GetLevel());\n\n\tcase Message::SetPositionCache:\n\t\tview.posCache->SetSize(wParam);\n\t\tbreak;\n\n\tcase Message::GetPositionCache:\n\t\treturn view.posCache->GetSize();\n\n\tcase Message::SetLayoutThreads:\n\t\tview.SetLayoutThreads(static_cast<unsigned int>(wParam));\n\t\tbreak;\n\n\tcase Message::GetLayoutThreads:\n\t\treturn view.GetLayoutThreads();\n\n\tcase Message::SetScrollWidth:\n\t\tPLATFORM_ASSERT(wParam > 0);\n\t\tif ((wParam > 0) && (wParam != static_cast<unsigned int>(scrollWidth))) {\n\t\t\tview.lineWidthMaxSeen = 0;\n\t\t\tscrollWidth = static_cast<int>(wParam);\n\t\t\tSetScrollBars();\n\t\t}\n\t\tbreak;\n\n\tcase Message::GetScrollWidth:\n\t\treturn scrollWidth;\n\n\tcase Message::SetScrollWidthTracking:\n\t\ttrackLineWidth = wParam != 0;\n\t\tbreak;\n\n\tcase Message::GetScrollWidthTracking:\n\t\treturn trackLineWidth;\n\n\tcase Message::LinesJoin:\n\t\tLinesJoin();\n\t\tbreak;\n\n\tcase Message::LinesSplit:\n\t\tLinesSplit(static_cast<int>(wParam));\n\t\tbreak;\n\n\tcase Message::TextWidth:\n\t\tPLATFORM_ASSERT(wParam < vs.styles.size());\n\t\tPLATFORM_ASSERT(lParam);\n\t\treturn TextWidth(wParam, ConstCharPtrFromSPtr(lParam));\n\n\tcase Message::TextHeight:\n\t\tRefreshStyleData();\n\t\treturn vs.lineHeight;\n\n\tcase Message::SetEndAtLastLine:\n\t\tPLATFORM_ASSERT((wParam == 0) || (wParam == 1));\n\t\tif (endAtLastLine != (wParam != 0)) {\n\t\t\tendAtLastLine = wParam != 0;\n\t\t\tSetScrollBars();\n\t\t}\n\t\tbreak;\n\n\tcase Message::GetEndAtLastLine:\n\t\treturn endAtLastLine;\n\n\tcase Message::SetCaretSticky:\n\t\tPLATFORM_ASSERT(static_cast<CaretSticky>(wParam) <= CaretSticky::WhiteSpace);\n\t\tif (static_cast<CaretSticky>(wParam) <= CaretSticky::WhiteSpace) {\n\t\t\tcaretSticky = static_cast<CaretSticky>(wParam);\n\t\t}\n\t\tbreak;\n\n\tcase Message::GetCaretSticky:\n\t\treturn static_cast<sptr_t>(caretSticky);\n\n\tcase Message::ToggleCaretSticky:\n\t\tcaretSticky = (caretSticky == CaretSticky::Off) ? CaretSticky::On : CaretSticky::Off;\n\t\tbreak;\n\n\tcase Message::GetColumn:\n\t\treturn pdoc->GetColumn(PositionFromUPtr(wParam));\n\n\tcase Message::FindColumn:\n\t\treturn pdoc->FindColumn(LineFromUPtr(wParam), lParam);\n\n\tcase Message::SetHScrollBar :\n\t\tif (horizontalScrollBarVisible != (wParam != 0)) {\n\t\t\thorizontalScrollBarVisible = wParam != 0;\n\t\t\tSetScrollBars();\n\t\t\tReconfigureScrollBars();\n\t\t}\n\t\tbreak;\n\n\tcase Message::GetHScrollBar:\n\t\treturn horizontalScrollBarVisible;\n\n\tcase Message::SetVScrollBar:\n\t\tif (verticalScrollBarVisible != (wParam != 0)) {\n\t\t\tverticalScrollBarVisible = wParam != 0;\n\t\t\tSetScrollBars();\n\t\t\tReconfigureScrollBars();\n\t\t\tif (verticalScrollBarVisible)\n\t\t\t\tSetVerticalScrollPos();\n\t\t}\n\t\tbreak;\n\n\tcase Message::GetVScrollBar:\n\t\treturn verticalScrollBarVisible;\n\n\tcase Message::SetIndentationGuides:\n\t\tvs.viewIndentationGuides = static_cast<IndentView>(wParam);\n\t\tRedraw();\n\t\tbreak;\n\n\tcase Message::GetIndentationGuides:\n\t\treturn static_cast<sptr_t>(vs.viewIndentationGuides);\n\n\tcase Message::SetHighlightGuide:\n\t\tif ((highlightGuideColumn != static_cast<int>(wParam)) || (wParam > 0)) {\n\t\t\thighlightGuideColumn = static_cast<int>(wParam);\n\t\t\tRedraw();\n\t\t}\n\t\tbreak;\n\n\tcase Message::GetHighlightGuide:\n\t\treturn highlightGuideColumn;\n\n\tcase Message::GetLineEndPosition:\n\t\treturn pdoc->LineEnd(LineFromUPtr(wParam));\n\n\tcase Message::SetCodePage:\n\t\tif (ValidCodePage(static_cast<int>(wParam))) {\n\t\t\tif (pdoc->SetDBCSCodePage(static_cast<int>(wParam))) {\n\t\t\t\tpcs->Clear();\n\t\t\t\tpcs->InsertLines(0, pdoc->LinesTotal() - 1);\n\t\t\t\tSetAnnotationHeights(0, pdoc->LinesTotal());\n\t\t\t\tInvalidateStyleRedraw();\n\t\t\t\tSetRepresentations();\n\t\t\t}\n\t\t}\n\t\tbreak;\n\n\tcase Message::GetCodePage:\n\t\treturn pdoc->dbcsCodePage;\n\n\tcase Message::SetIMEInteraction:\n\t\timeInteraction = static_cast<IMEInteraction>(wParam);\n\t\tbreak;\n\n\tcase Message::GetIMEInteraction:\n\t\treturn static_cast<sptr_t>(imeInteraction);\n\n\tcase Message::SetBidirectional:\n\t\t// Message::SetBidirectional is implemented on platform subclasses if they support bidirectional text.\n\t\tbreak;\n\n\tcase Message::GetBidirectional:\n\t\treturn static_cast<sptr_t>(bidirectional);\n\n\tcase Message::GetLineCharacterIndex:\n\t\treturn static_cast<sptr_t>(pdoc->LineCharacterIndex());\n\n\tcase Message::AllocateLineCharacterIndex:\n\t\tpdoc->AllocateLineCharacterIndex(static_cast<LineCharacterIndexType>(wParam));\n\t\tbreak;\n\n\tcase Message::ReleaseLineCharacterIndex:\n\t\tpdoc->ReleaseLineCharacterIndex(static_cast<LineCharacterIndexType>(wParam));\n\t\tbreak;\n\n\tcase Message::LineFromIndexPosition:\n\t\treturn pdoc->LineFromPositionIndex(PositionFromUPtr(wParam), static_cast<LineCharacterIndexType>(lParam));\n\n\tcase Message::IndexPositionFromLine:\n\t\treturn pdoc->IndexLineStart(LineFromUPtr(wParam), static_cast<LineCharacterIndexType>(lParam));\n\n\t\t// Marker definition and setting\n\tcase Message::MarkerDefine:\n\t\tif (wParam <= MarkerMax) {\n\t\t\tvs.markers[wParam].markType = static_cast<MarkerSymbol>(lParam);\n\t\t\tvs.CalcLargestMarkerHeight();\n\t\t}\n\t\tInvalidateStyleData();\n\t\tRedrawSelMargin();\n\t\tbreak;\n\n\tcase Message::MarkerSymbolDefined:\n\t\tif (wParam <= MarkerMax)\n\t\t\treturn static_cast<sptr_t>(vs.markers[wParam].markType);\n\t\telse\n\t\t\treturn 0;\n\n\tcase Message::MarkerSetFore:\n\t\tif (wParam <= MarkerMax)\n\t\t\tvs.markers[wParam].fore = ColourRGBA::FromIpRGB(lParam);\n\t\tInvalidateStyleData();\n\t\tRedrawSelMargin();\n\t\tbreak;\n\tcase Message::MarkerSetBack:\n\t\tif (wParam <= MarkerMax)\n\t\t\tvs.markers[wParam].back = ColourRGBA::FromIpRGB(lParam);\n\t\tInvalidateStyleData();\n\t\tRedrawSelMargin();\n\t\tbreak;\n\tcase Message::MarkerSetBackSelected:\n\t\tif (wParam <= MarkerMax)\n\t\t\tvs.markers[wParam].backSelected = ColourRGBA::FromIpRGB(lParam);\n\t\tInvalidateStyleData();\n\t\tRedrawSelMargin();\n\t\tbreak;\n\tcase Message::MarkerSetForeTranslucent:\n\t\tif (wParam <= MarkerMax)\n\t\t\tvs.markers[wParam].fore = ColourRGBA(static_cast<int>(lParam));\n\t\tInvalidateStyleData();\n\t\tRedrawSelMargin();\n\t\tbreak;\n\tcase Message::MarkerSetBackTranslucent:\n\t\tif (wParam <= MarkerMax)\n\t\t\tvs.markers[wParam].back = ColourRGBA(static_cast<int>(lParam));\n\t\tInvalidateStyleData();\n\t\tRedrawSelMargin();\n\t\tbreak;\n\tcase Message::MarkerSetBackSelectedTranslucent:\n\t\tif (wParam <= MarkerMax)\n\t\t\tvs.markers[wParam].backSelected = ColourRGBA(static_cast<int>(lParam));\n\t\tInvalidateStyleData();\n\t\tRedrawSelMargin();\n\t\tbreak;\n\tcase Message::MarkerSetStrokeWidth:\n\t\tif (wParam <= MarkerMax)\n\t\t\tvs.markers[wParam].strokeWidth = static_cast<XYPOSITION>(lParam) / 100.0f;\n\t\tInvalidateStyleData();\n\t\tRedrawSelMargin();\n\t\tbreak;\n\tcase Message::MarkerEnableHighlight:\n\t\tmarginView.highlightDelimiter.isEnabled = wParam == 1;\n\t\tRedrawSelMargin();\n\t\tbreak;\n\tcase Message::MarkerSetAlpha:\n\t\tif (wParam <= MarkerMax) {\n\t\t\tif (static_cast<Alpha>(lParam) == Alpha::NoAlpha) {\n\t\t\t\tSetAppearance(vs.markers[wParam].alpha, Alpha::Opaque);\n\t\t\t\tSetAppearance(vs.markers[wParam].layer, Layer::Base);\n\t\t\t} else {\n\t\t\t\tSetAppearance(vs.markers[wParam].alpha, static_cast<Alpha>(lParam));\n\t\t\t\tSetAppearance(vs.markers[wParam].layer, Layer::OverText);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase Message::MarkerSetLayer:\n\t\tif (wParam <= MarkerMax) {\n\t\t\tSetAppearance(vs.markers[wParam].layer, static_cast<Layer>(lParam));\n\t\t}\n\t\tbreak;\n\tcase Message::MarkerGetLayer:\n\t\tif (wParam <= MarkerMax) {\n\t\t\treturn static_cast<sptr_t>(vs.markers[wParam].layer);\n\t\t}\n\t\treturn 0;\n\tcase Message::MarkerAdd: {\n\t\t\tconst int markerID = pdoc->AddMark(LineFromUPtr(wParam), static_cast<int>(lParam));\n\t\t\treturn markerID;\n\t\t}\n\tcase Message::MarkerAddSet:\n\t\tif (lParam != 0)\n\t\t\tpdoc->AddMarkSet(LineFromUPtr(wParam), static_cast<int>(lParam));\n\t\tbreak;\n\n\tcase Message::MarkerDelete:\n\t\tpdoc->DeleteMark(LineFromUPtr(wParam), static_cast<int>(lParam));\n\t\tbreak;\n\n\tcase Message::MarkerDeleteAll:\n\t\tpdoc->DeleteAllMarks(static_cast<int>(wParam));\n\t\tbreak;\n\n\tcase Message::MarkerGet:\n\t\treturn GetMark(LineFromUPtr(wParam));\n\n\tcase Message::MarkerNext:\n\t\treturn pdoc->MarkerNext(LineFromUPtr(wParam), static_cast<int>(lParam));\n\n\tcase Message::MarkerPrevious: {\n\t\t\tfor (Sci::Line iLine = LineFromUPtr(wParam); iLine >= 0; iLine--) {\n\t\t\t\tif ((GetMark(iLine) & lParam) != 0)\n\t\t\t\t\treturn iLine;\n\t\t\t}\n\t\t}\n\t\treturn -1;\n\n\tcase Message::MarkerDefinePixmap:\n\t\tif (wParam <= MarkerMax) {\n\t\t\tvs.markers[wParam].SetXPM(ConstCharPtrFromSPtr(lParam));\n\t\t\tvs.CalcLargestMarkerHeight();\n\t\t}\n\t\tInvalidateStyleData();\n\t\tRedrawSelMargin();\n\t\tbreak;\n\n\tcase Message::RGBAImageSetWidth:\n\t\tsizeRGBAImage.x = static_cast<XYPOSITION>(wParam);\n\t\tbreak;\n\n\tcase Message::RGBAImageSetHeight:\n\t\tsizeRGBAImage.y = static_cast<XYPOSITION>(wParam);\n\t\tbreak;\n\n\tcase Message::RGBAImageSetScale:\n\t\tscaleRGBAImage = static_cast<float>(wParam);\n\t\tbreak;\n\n\tcase Message::MarkerDefineRGBAImage:\n\t\tif (wParam <= MarkerMax) {\n\t\t\tvs.markers[wParam].SetRGBAImage(sizeRGBAImage, scaleRGBAImage / 100.0f, ConstUCharPtrFromSPtr(lParam));\n\t\t\tvs.CalcLargestMarkerHeight();\n\t\t}\n\t\tInvalidateStyleData();\n\t\tRedrawSelMargin();\n\t\tbreak;\n\n\tcase Message::SetMarginTypeN:\n\t\tif (ValidMargin(wParam)) {\n\t\t\tvs.ms[wParam].style = static_cast<MarginType>(lParam);\n\t\t\tInvalidateStyleRedraw();\n\t\t}\n\t\tbreak;\n\n\tcase Message::GetMarginTypeN:\n\t\tif (ValidMargin(wParam))\n\t\t\treturn static_cast<sptr_t>(vs.ms[wParam].style);\n\t\telse\n\t\t\treturn 0;\n\n\tcase Message::SetMarginWidthN:\n\t\tif (ValidMargin(wParam)) {\n\t\t\t// Short-circuit if the width is unchanged, to avoid unnecessary redraw.\n\t\t\tif (vs.ms[wParam].width != lParam) {\n\t\t\t\tlastXChosen += static_cast<int>(lParam) - vs.ms[wParam].width;\n\t\t\t\tvs.ms[wParam].width = static_cast<int>(lParam);\n\t\t\t\tInvalidateStyleRedraw();\n\t\t\t}\n\t\t}\n\t\tbreak;\n\n\tcase Message::GetMarginWidthN:\n\t\tif (ValidMargin(wParam))\n\t\t\treturn vs.ms[wParam].width;\n\t\telse\n\t\t\treturn 0;\n\n\tcase Message::SetMarginMaskN:\n\t\tif (ValidMargin(wParam)) {\n\t\t\tvs.ms[wParam].mask = static_cast<int>(lParam);\n\t\t\tInvalidateStyleRedraw();\n\t\t}\n\t\tbreak;\n\n\tcase Message::GetMarginMaskN:\n\t\tif (ValidMargin(wParam))\n\t\t\treturn vs.ms[wParam].mask;\n\t\telse\n\t\t\treturn 0;\n\n\tcase Message::SetMarginSensitiveN:\n\t\tif (ValidMargin(wParam)) {\n\t\t\tvs.ms[wParam].sensitive = lParam != 0;\n\t\t\tInvalidateStyleRedraw();\n\t\t}\n\t\tbreak;\n\n\tcase Message::GetMarginSensitiveN:\n\t\tif (ValidMargin(wParam))\n\t\t\treturn vs.ms[wParam].sensitive ? 1 : 0;\n\t\telse\n\t\t\treturn 0;\n\n\tcase Message::SetMarginCursorN:\n\t\tif (ValidMargin(wParam))\n\t\t\tvs.ms[wParam].cursor = static_cast<CursorShape>(lParam);\n\t\tbreak;\n\n\tcase Message::GetMarginCursorN:\n\t\tif (ValidMargin(wParam))\n\t\t\treturn static_cast<sptr_t>(vs.ms[wParam].cursor);\n\t\telse\n\t\t\treturn 0;\n\n\tcase Message::SetMarginBackN:\n\t\tif (ValidMargin(wParam)) {\n\t\t\tvs.ms[wParam].back = ColourRGBA::FromIpRGB(lParam);\n\t\t\tInvalidateStyleRedraw();\n\t\t}\n\t\tbreak;\n\n\tcase Message::GetMarginBackN:\n\t\tif (ValidMargin(wParam))\n\t\t\treturn vs.ms[wParam].back.OpaqueRGB();\n\t\telse\n\t\t\treturn 0;\n\n\tcase Message::SetMargins:\n\t\tif (wParam < 1000)\n\t\t\tvs.ms.resize(wParam);\n\t\tbreak;\n\n\tcase Message::GetMargins:\n\t\treturn vs.ms.size();\n\n\tcase Message::StyleClearAll:\n\t\tvs.ClearStyles((size_t)wParam, (size_t)lParam); //Au: added wParam, lParam (from/to)\n\t\tInvalidateStyleRedraw();\n\t\tbreak;\n\n\tcase Message::StyleSetFore:\n\tcase Message::StyleSetBack:\n\tcase Message::StyleSetBold:\n\tcase Message::StyleSetWeight:\n\tcase Message::StyleSetStretch:\n\tcase Message::StyleSetItalic:\n\tcase Message::StyleSetEOLFilled:\n\tcase Message::StyleSetSize:\n\tcase Message::StyleSetSizeFractional:\n\tcase Message::StyleSetFont:\n\tcase Message::StyleSetUnderline:\n\tcase Message::StyleSetCase:\n\tcase Message::StyleSetCharacterSet:\n\tcase Message::StyleSetVisible:\n\tcase Message::StyleSetChangeable:\n\tcase Message::StyleSetHotSpot:\n\tcase Message::StyleSetCheckMonospaced:\n\tcase Message::StyleSetInvisibleRepresentation:\n\t\tStyleSetMessage(iMessage, wParam, lParam);\n\t\tbreak;\n\n\tcase Message::StyleGetFore:\n\tcase Message::StyleGetBack:\n\tcase Message::StyleGetBold:\n\tcase Message::StyleGetWeight:\n\tcase Message::StyleGetStretch:\n\tcase Message::StyleGetItalic:\n\tcase Message::StyleGetEOLFilled:\n\tcase Message::StyleGetSize:\n\tcase Message::StyleGetSizeFractional:\n\tcase Message::StyleGetFont:\n\tcase Message::StyleGetUnderline:\n\tcase Message::StyleGetCase:\n\tcase Message::StyleGetCharacterSet:\n\tcase Message::StyleGetVisible:\n\tcase Message::StyleGetChangeable:\n\tcase Message::StyleGetHotSpot:\n\tcase Message::StyleGetCheckMonospaced:\n\tcase Message::StyleGetInvisibleRepresentation:\n\t\treturn StyleGetMessage(iMessage, wParam, lParam);\n\n\tcase Message::StyleResetDefault:\n\t\tvs.ResetDefaultStyle();\n\t\tInvalidateStyleRedraw();\n\t\tbreak;\n\n\tcase Message::SetElementColour:\n\t\tif (vs.SetElementColour(static_cast<Element>(wParam), ColourRGBA(static_cast<int>(lParam)))) {\n\t\t\tInvalidateStyleRedraw();\n\t\t}\n\t\tbreak;\n\n\tcase Message::GetElementColour:\n\t\treturn vs.ElementColour(static_cast<Element>(wParam)).value_or(ColourRGBA()).AsInteger();\n\n\tcase Message::ResetElementColour:\n\t\tif (vs.ResetElement(static_cast<Element>(wParam))) {\n\t\t\tInvalidateStyleRedraw();\n\t\t}\n\t\tbreak;\n\n\tcase Message::GetElementIsSet:\n\t\treturn vs.ElementColour(static_cast<Element>(wParam)).has_value();\n\n\tcase Message::GetElementAllowsTranslucent:\n\t\treturn vs.ElementAllowsTranslucent(static_cast<Element>(wParam));\n\n\tcase Message::GetElementBaseColour:\n\t\treturn vs.elementBaseColours[static_cast<Element>(wParam)].value_or(ColourRGBA()).AsInteger();\n\n\tcase Message::SetFontLocale:\n\t\tif (lParam) {\n\t\t\tvs.SetFontLocaleName(ConstCharPtrFromSPtr(lParam));\n\t\t\tInvalidateStyleRedraw();\n\t\t}\n\t\tbreak;\n\n\tcase Message::GetFontLocale:\n\t\treturn StringResult(lParam, vs.localeName.c_str());\n\n#ifdef INCLUDE_DEPRECATED_FEATURES\n\tcase SCI_SETSTYLEBITS:\n\t\tvs.EnsureStyle(0xff);\n\t\tbreak;\n\n\tcase SCI_GETSTYLEBITS:\n\t\treturn 8;\n#endif\n\n\tcase Message::SetLineState:\n\t\treturn pdoc->SetLineState(LineFromUPtr(wParam), static_cast<int>(lParam));\n\n\tcase Message::GetLineState:\n\t\treturn pdoc->GetLineState(LineFromUPtr(wParam));\n\n\tcase Message::GetMaxLineState:\n\t\treturn pdoc->GetMaxLineState();\n\n\tcase Message::GetCaretLineVisible:\n\t\treturn vs.ElementColour(Element::CaretLineBack) ? 1 : 0;\n\tcase Message::SetCaretLineVisible:\n\t\tif (wParam) {\n\t\t\tif (!vs.elementColours.count(Element::CaretLineBack)) {\n\t\t\t\t// Yellow default\n\t\t\t\tvs.elementColours[Element::CaretLineBack] = ColourRGBA(maximumByte, maximumByte, 0);\n\t\t\t\tInvalidateStyleRedraw();\n\t\t\t}\n\t\t} else {\n\t\t\tif (vs.ResetElement(Element::CaretLineBack)) {\n\t\t\t\tInvalidateStyleRedraw();\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase Message::GetCaretLineVisibleAlways:\n\t\treturn vs.caretLine.alwaysShow;\n\tcase Message::SetCaretLineVisibleAlways:\n\t\tvs.caretLine.alwaysShow = wParam != 0;\n\t\tInvalidateStyleRedraw();\n\t\tbreak;\n\n\tcase Message::GetCaretLineHighlightSubLine:\n\t\treturn vs.caretLine.subLine;\n\tcase Message::SetCaretLineHighlightSubLine:\n\t\tvs.caretLine.subLine = wParam != 0;\n\t\tInvalidateStyleRedraw();\n\t\tbreak;\n\n\tcase Message::GetCaretLineFrame:\n\t\treturn vs.caretLine.frame;\n\tcase Message::SetCaretLineFrame:\n\t\tvs.caretLine.frame = static_cast<int>(wParam);\n\t\tInvalidateStyleRedraw();\n\t\tbreak;\n\tcase Message::GetCaretLineBack:\n\t\treturn vs.ElementColourForced(Element::CaretLineBack).OpaqueRGB();\n\n\tcase Message::SetCaretLineBack:\n\t\tvs.SetElementRGB(Element::CaretLineBack, static_cast<int>(wParam));\n\t\tInvalidateStyleRedraw();\n\t\tbreak;\n\n\tcase Message::GetCaretLineLayer:\n\t\treturn static_cast<sptr_t>(vs.caretLine.layer);\n\n\tcase Message::SetCaretLineLayer:\n\t\tif (vs.caretLine.layer != static_cast<Layer>(wParam)) {\n\t\t\tvs.caretLine.layer = static_cast<Layer>(wParam);\n\t\t\tUpdateBaseElements();\n\t\t\tInvalidateStyleRedraw();\n\t\t}\n\t\tbreak;\n\n\tcase Message::GetCaretLineBackAlpha:\n\t\tif (vs.caretLine.layer == Layer::Base)\n\t\t\treturn static_cast<sptr_t>(Alpha::NoAlpha);\n\t\treturn vs.ElementColour(Element::CaretLineBack).value_or(ColourRGBA()).GetAlpha();\n\n\tcase Message::SetCaretLineBackAlpha: {\n\t\t\tconst Layer layerNew = (static_cast<Alpha>(wParam) == Alpha::NoAlpha) ? Layer::Base : Layer::OverText;\n\t\t\tvs.caretLine.layer = layerNew;\n\t\t\tif (vs.ElementColour(Element::CaretLineBack)) {\n\t\t\t\tvs.SetElementAlpha(Element::CaretLineBack, static_cast<int>(wParam));\n\t\t\t}\n\t\t\tInvalidateStyleRedraw();\n\t\t}\n\t\tbreak;\n\n\t\t// Folding messages\n\n\tcase Message::VisibleFromDocLine:\n\t\treturn pcs->DisplayFromDoc(LineFromUPtr(wParam));\n\n\tcase Message::DocLineFromVisible:\n\t\treturn pcs->DocFromDisplay(LineFromUPtr(wParam));\n\n\tcase Message::WrapCount:\n\t\treturn WrapCount(LineFromUPtr(wParam));\n\n\tcase Message::SetFoldLevel: {\n\t\t\tconst int prev = pdoc->SetLevel(LineFromUPtr(wParam), static_cast<int>(lParam));\n\t\t\tif (prev != static_cast<int>(lParam))\n\t\t\t\tRedrawSelMargin();\n\t\t\treturn prev;\n\t\t}\n\n\tcase Message::GetFoldLevel:\n\t\treturn pdoc->GetLevel(LineFromUPtr(wParam));\n\n\tcase Message::GetLastChild:\n\t\treturn pdoc->GetLastChild(LineFromUPtr(wParam), OptionalFoldLevel(lParam));\n\n\tcase Message::GetFoldParent:\n\t\treturn pdoc->GetFoldParent(LineFromUPtr(wParam));\n\n\tcase Message::ShowLines:\n\t\tpcs->SetVisible(LineFromUPtr(wParam), lParam, true);\n\t\tSetScrollBars();\n\t\tRedraw();\n\t\tbreak;\n\n\tcase Message::HideLines:\n\t\tpcs->SetVisible(LineFromUPtr(wParam), lParam, false);\n\t\tSetScrollBars();\n\t\tRedraw();\n\t\tbreak;\n\n\tcase Message::GetLineVisible:\n\t\treturn pcs->GetVisible(LineFromUPtr(wParam));\n\n\tcase Message::GetAllLinesVisible:\n\t\treturn pcs->HiddenLines() ? 0 : 1;\n\n\tcase Message::SetFoldExpanded:\n\t\tSetFoldExpanded(LineFromUPtr(wParam), lParam != 0);\n\t\tbreak;\n\n\tcase Message::GetFoldExpanded:\n\t\treturn pcs->GetExpanded(LineFromUPtr(wParam));\n\n\tcase Message::SetAutomaticFold:\n\t\tfoldAutomatic = static_cast<AutomaticFold>(wParam);\n\t\tbreak;\n\n\tcase Message::GetAutomaticFold:\n\t\treturn static_cast<sptr_t>(foldAutomatic);\n\n\tcase Message::SetFoldFlags:\n\t\tfoldFlags = static_cast<FoldFlag>(wParam);\n\t\tRedraw();\n\t\tbreak;\n\n\tcase Message::ToggleFoldShowText:\n\t\tpcs->SetFoldDisplayText(LineFromUPtr(wParam), ConstCharPtrFromSPtr(lParam));\n\t\tFoldLine(LineFromUPtr(wParam), FoldAction::Toggle);\n\t\tbreak;\n\n\tcase Message::FoldDisplayTextSetStyle:\n\t\tfoldDisplayTextStyle = static_cast<FoldDisplayTextStyle>(wParam);\n\t\tRedraw();\n\t\tbreak;\n\n\tcase Message::FoldDisplayTextGetStyle:\n\t\treturn static_cast<sptr_t>(foldDisplayTextStyle);\n\n\tcase Message::SetDefaultFoldDisplayText:\n\t\tSetDefaultFoldDisplayText(ConstCharPtrFromSPtr(lParam));\n\t\tRedraw();\n\t\tbreak;\n\n\tcase Message::GetDefaultFoldDisplayText:\n\t\treturn StringResult(lParam, GetDefaultFoldDisplayText());\n\n\tcase Message::ToggleFold:\n\t\tFoldLine(LineFromUPtr(wParam), FoldAction::Toggle);\n\t\tbreak;\n\n\tcase Message::FoldLine:\n\t\tFoldLine(LineFromUPtr(wParam), static_cast<FoldAction>(lParam));\n\t\tbreak;\n\n\tcase Message::FoldChildren:\n\t\tFoldExpand(LineFromUPtr(wParam), static_cast<FoldAction>(lParam), pdoc->GetFoldLevel(LineFromUPtr(wParam)));\n\t\tbreak;\n\n\tcase Message::FoldAll:\n\t\tFoldAll(static_cast<FoldAction>(wParam));\n\t\tbreak;\n\n\tcase Message::ExpandChildren:\n\t\tFoldExpand(LineFromUPtr(wParam), FoldAction::Expand, static_cast<FoldLevel>(lParam));\n\t\tbreak;\n\n\tcase Message::ContractedFoldNext:\n\t\treturn ContractedFoldNext(LineFromUPtr(wParam));\n\n\tcase Message::EnsureVisible:\n\t\tEnsureLineVisible(LineFromUPtr(wParam), false);\n\t\tbreak;\n\n\tcase Message::EnsureVisibleEnforcePolicy:\n\t\tEnsureLineVisible(LineFromUPtr(wParam), true);\n\t\tbreak;\n\n\tcase Message::ScrollRange:\n\t\tScrollRange(SelectionRange(PositionFromUPtr(wParam), lParam));\n\t\tbreak;\n\n\tcase Message::SearchAnchor:\n\t\tSearchAnchor();\n\t\tbreak;\n\n\tcase Message::SearchNext:\n\tcase Message::SearchPrev:\n\t\treturn SearchText(iMessage, wParam, lParam);\n\n\tcase Message::SetXCaretPolicy:\n\t\tcaretPolicies.x = CaretPolicySlop(wParam, lParam);\n\t\tbreak;\n\n\tcase Message::SetYCaretPolicy:\n\t\tcaretPolicies.y = CaretPolicySlop(wParam, lParam);\n\t\tbreak;\n\n\tcase Message::SetVisiblePolicy:\n\t\tvisiblePolicy = VisiblePolicySlop(wParam, lParam);\n\t\tbreak;\n\n\tcase Message::LinesOnScreen:\n\t\treturn LinesOnScreen();\n\n\tcase Message::SetSelFore:\n\t\tvs.elementColours[Element::SelectionText] = OptionalColour(wParam, lParam);\n\t\tvs.elementColours[Element::SelectionAdditionalText] = OptionalColour(wParam, lParam);\n\t\tInvalidateStyleRedraw();\n\t\tbreak;\n\n\tcase Message::SetSelBack:\n\t\tif (wParam) {\n\t\t\tvs.SetElementRGB(Element::SelectionBack, static_cast<int>(lParam));\n\t\t\tvs.SetElementRGB(Element::SelectionAdditionalBack, static_cast<int>(lParam));\n\t\t} else {\n\t\t\tvs.ResetElement(Element::SelectionBack);\n\t\t\tvs.ResetElement(Element::SelectionAdditionalBack);\n\t\t}\n\t\tInvalidateStyleRedraw();\n\t\tbreak;\n\n\tcase Message::SetSelAlpha: {\n\t\t\tconst Layer layerNew = (static_cast<Alpha>(wParam) == Alpha::NoAlpha) ? Layer::Base : Layer::OverText;\n\t\t\tif (vs.selection.layer != layerNew) {\n\t\t\t    vs.selection.layer = layerNew;\n\t\t\t    UpdateBaseElements();\n\t\t\t}\n\t\t\tconst int alpha = static_cast<int>(wParam);\n\t\t\tvs.SetElementAlpha(Element::SelectionBack, alpha);\n\t\t\tvs.SetElementAlpha(Element::SelectionAdditionalBack, alpha);\n\t\t\tvs.SetElementAlpha(Element::SelectionSecondaryBack, alpha);\n\t\t\tvs.SetElementAlpha(Element::SelectionInactiveBack, alpha);\n\t\t\tInvalidateStyleRedraw();\n\t\t}\n\t\tbreak;\n\n\tcase Message::GetSelAlpha:\n\t\tif (vs.selection.layer == Layer::Base)\n\t\t\treturn static_cast<sptr_t>(Alpha::NoAlpha);\n\t\treturn vs.ElementColourForced(Element::SelectionBack).GetAlpha();\n\n\tcase Message::GetSelEOLFilled:\n\t\treturn vs.selection.eolFilled;\n\n\tcase Message::SetSelEOLFilled:\n\t\tvs.selection.eolFilled = wParam != 0;\n\t\tInvalidateStyleRedraw();\n\t\tbreak;\n\n\tcase Message::SetWhitespaceFore:\n\t\tif (vs.SetElementColourOptional(Element::WhiteSpace, wParam, lParam)) {\n\t\t\tInvalidateStyleRedraw();\n\t\t}\n\t\tbreak;\n\n\tcase Message::SetWhitespaceBack:\n\t\tif (vs.SetElementColourOptional(Element::WhiteSpaceBack, wParam, lParam)) {\n\t\t\tInvalidateStyleRedraw();\n\t\t}\n\t\tbreak;\n\n\tcase Message::SetSelectionLayer:\n\t\tif (vs.selection.layer != static_cast<Layer>(wParam)) {\n\t\t\tvs.selection.layer = static_cast<Layer>(wParam);\n\t\t\tUpdateBaseElements();\n\t\t\tInvalidateStyleRedraw();\n\t\t}\n\t\tbreak;\n\n\tcase Message::GetSelectionLayer:\n\t\treturn static_cast<sptr_t>(vs.selection.layer);\n\n\tcase Message::SetCaretFore:\n\t\tvs.elementColours[Element::Caret] = ColourRGBA::FromIpRGB(SPtrFromUPtr(wParam));\n\t\tInvalidateStyleRedraw();\n\t\tbreak;\n\n\tcase Message::GetCaretFore:\n\t\treturn vs.ElementColourForced(Element::Caret).OpaqueRGB();\n\n\tcase Message::SetCaretStyle:\n\t\tif (static_cast<CaretStyle>(wParam) <= (CaretStyle::Block | CaretStyle::OverstrikeBlock | CaretStyle::Curses | CaretStyle::BlockAfter))\n\t\t\tvs.caret.style = static_cast<CaretStyle>(wParam);\n\t\telse\n\t\t\t/* Default to the line caret */\n\t\t\tvs.caret.style = CaretStyle::Line;\n\t\tInvalidateStyleRedraw();\n\t\tbreak;\n\n\tcase Message::GetCaretStyle:\n\t\treturn static_cast<sptr_t>(vs.caret.style);\n\n\tcase Message::SetCaretWidth:\n\t\tvs.caret.width = std::clamp(static_cast<int>(wParam), 0, 20);\n\t\tInvalidateStyleRedraw();\n\t\tbreak;\n\n\tcase Message::GetCaretWidth:\n\t\treturn vs.caret.width;\n\n\tcase Message::AssignCmdKey:\n\t\tkmap.AssignCmdKey(static_cast<Keys>(LowShortFromWParam(wParam)),\n\t\t\tstatic_cast<KeyMod>(HighShortFromWParam(wParam)), static_cast<Message>(lParam));\n\t\tbreak;\n\n\tcase Message::ClearCmdKey:\n\t\tkmap.AssignCmdKey(static_cast<Keys>(LowShortFromWParam(wParam)),\n\t\t\tstatic_cast<KeyMod>(HighShortFromWParam(wParam)), Message::Null);\n\t\tbreak;\n\n\tcase Message::ClearAllCmdKeys:\n\t\tkmap.Clear();\n\t\tbreak;\n\n\tcase Message::IndicSetStyle:\n\t\tif (wParam <= IndicatorMax) {\n\t\t\tvs.indicators[wParam].sacNormal.style = static_cast<IndicatorStyle>(lParam);\n\t\t\tvs.indicators[wParam].sacHover.style = static_cast<IndicatorStyle>(lParam);\n\t\t\tInvalidateStyleRedraw();\n\t\t}\n\t\tbreak;\n\n\tcase Message::IndicGetStyle:\n\t\treturn (wParam <= IndicatorMax) ?\n\t\t\tstatic_cast<sptr_t>(vs.indicators[wParam].sacNormal.style) : 0;\n\n\tcase Message::IndicSetFore:\n\t\tif (wParam <= IndicatorMax) {\n\t\t\tvs.indicators[wParam].sacNormal.fore = ColourRGBA::FromIpRGB(lParam);\n\t\t\tvs.indicators[wParam].sacHover.fore = ColourRGBA::FromIpRGB(lParam);\n\t\t\tInvalidateStyleRedraw();\n\t\t}\n\t\tbreak;\n\n\tcase Message::IndicGetFore:\n\t\treturn (wParam <= IndicatorMax) ?\n\t\t\tvs.indicators[wParam].sacNormal.fore.OpaqueRGB() : 0;\n\n\tcase Message::IndicSetHoverStyle:\n\t\tif (wParam <= IndicatorMax) {\n\t\t\tvs.indicators[wParam].sacHover.style = static_cast<IndicatorStyle>(lParam);\n\t\t\tInvalidateStyleRedraw();\n\t\t}\n\t\tbreak;\n\n\tcase Message::IndicGetHoverStyle:\n\t\treturn (wParam <= IndicatorMax) ?\n\t\t\tstatic_cast<sptr_t>(vs.indicators[wParam].sacHover.style) : 0;\n\n\tcase Message::IndicSetHoverFore:\n\t\tif (wParam <= IndicatorMax) {\n\t\t\tvs.indicators[wParam].sacHover.fore = ColourRGBA::FromIpRGB(lParam);\n\t\t\tInvalidateStyleRedraw();\n\t\t}\n\t\tbreak;\n\n\tcase Message::IndicGetHoverFore:\n\t\treturn (wParam <= IndicatorMax) ?\n\t\t\tvs.indicators[wParam].sacHover.fore.OpaqueRGB() : 0;\n\n\tcase Message::IndicSetFlags:\n\t\tif (wParam <= IndicatorMax) {\n\t\t\tvs.indicators[wParam].SetFlags(static_cast<IndicFlag>(lParam));\n\t\t\tInvalidateStyleRedraw();\n\t\t}\n\t\tbreak;\n\n\tcase Message::IndicGetFlags:\n\t\treturn (wParam <= IndicatorMax) ?\n\t\t\tstatic_cast<sptr_t>(vs.indicators[wParam].Flags()) : 0;\n\n\tcase Message::IndicSetUnder:\n\t\tif (wParam <= IndicatorMax) {\n\t\t\tvs.indicators[wParam].under = lParam != 0;\n\t\t\tInvalidateStyleRedraw();\n\t\t}\n\t\tbreak;\n\n\tcase Message::IndicGetUnder:\n\t\treturn (wParam <= IndicatorMax) ?\n\t\t\tvs.indicators[wParam].under : 0;\n\n\tcase Message::IndicSetAlpha:\n\t\tif (wParam <= IndicatorMax && lParam >=0 && lParam <= 255) {\n\t\t\tvs.indicators[wParam].fillAlpha = static_cast<int>(lParam);\n\t\t\tInvalidateStyleRedraw();\n\t\t}\n\t\tbreak;\n\n\tcase Message::IndicGetAlpha:\n\t\treturn (wParam <= IndicatorMax)\n\t\t\t? vs.indicators[wParam].fillAlpha : 0;\n\n\tcase Message::IndicSetOutlineAlpha:\n\t\tif (wParam <= IndicatorMax && lParam >=0 && lParam <= 255) {\n\t\t\tvs.indicators[wParam].outlineAlpha = static_cast<int>(lParam);\n\t\t\tInvalidateStyleRedraw();\n\t\t}\n\t\tbreak;\n\n\tcase Message::IndicGetOutlineAlpha:\n\t\treturn (wParam <= IndicatorMax) ? vs.indicators[wParam].outlineAlpha : 0;\n\n\tcase Message::IndicSetStrokeWidth:\n\t\tif (wParam <= IndicatorMax && lParam >= 0 && lParam <= 1000) {\n\t\t\tvs.indicators[wParam].strokeWidth = static_cast<XYPOSITION>(lParam) / 100.0;\n\t\t\tInvalidateStyleRedraw();\n\t\t}\n\t\tbreak;\n\n\tcase Message::IndicGetStrokeWidth:\n\t\tif (wParam <= IndicatorMax) {\n\t\t\treturn std::lround(vs.indicators[wParam].strokeWidth * 100);\n\t\t}\n\t\tbreak;\n\n\tcase Message::SetIndicatorCurrent:\n\t\tpdoc->DecorationSetCurrentIndicator(static_cast<int>(wParam));\n\t\tbreak;\n\tcase Message::GetIndicatorCurrent:\n\t\treturn pdoc->decorations->GetCurrentIndicator();\n\tcase Message::SetIndicatorValue:\n\t\tpdoc->decorations->SetCurrentValue(static_cast<int>(wParam));\n\t\tbreak;\n\tcase Message::GetIndicatorValue:\n\t\treturn pdoc->decorations->GetCurrentValue();\n\n\tcase Message::IndicatorFillRange:\n\t\tpdoc->DecorationFillRange(PositionFromUPtr(wParam),\n\t\t\tpdoc->decorations->GetCurrentValue(), lParam);\n\t\tbreak;\n\n\tcase Message::IndicatorClearRange:\n\t\tpdoc->DecorationFillRange(PositionFromUPtr(wParam), 0,\n\t\t\tlParam);\n\t\tbreak;\n\n\tcase Message::IndicatorAllOnFor:\n\t\treturn pdoc->decorations->AllOnFor(PositionFromUPtr(wParam));\n\n\tcase Message::IndicatorValueAt:\n\t\treturn pdoc->decorations->ValueAt(static_cast<int>(wParam), lParam);\n\n\tcase Message::IndicatorStart:\n\t\treturn pdoc->decorations->Start(static_cast<int>(wParam), lParam);\n\n\tcase Message::IndicatorEnd:\n\t\treturn pdoc->decorations->End(static_cast<int>(wParam), lParam);\n\n\tcase Message::LineDown:\n\tcase Message::LineDownExtend:\n\tcase Message::ParaDown:\n\tcase Message::ParaDownExtend:\n\tcase Message::LineUp:\n\tcase Message::LineUpExtend:\n\tcase Message::ParaUp:\n\tcase Message::ParaUpExtend:\n\tcase Message::CharLeft:\n\tcase Message::CharLeftExtend:\n\tcase Message::CharRight:\n\tcase Message::CharRightExtend:\n\tcase Message::WordLeft:\n\tcase Message::WordLeftExtend:\n\tcase Message::WordRight:\n\tcase Message::WordRightExtend:\n\tcase Message::WordLeftEnd:\n\tcase Message::WordLeftEndExtend:\n\tcase Message::WordRightEnd:\n\tcase Message::WordRightEndExtend:\n\tcase Message::Home:\n\tcase Message::HomeExtend:\n\tcase Message::LineEnd:\n\tcase Message::LineEndExtend:\n\tcase Message::HomeWrap:\n\tcase Message::HomeWrapExtend:\n\tcase Message::LineEndWrap:\n\tcase Message::LineEndWrapExtend:\n\tcase Message::DocumentStart:\n\tcase Message::DocumentStartExtend:\n\tcase Message::DocumentEnd:\n\tcase Message::DocumentEndExtend:\n\tcase Message::ScrollToStart:\n\tcase Message::ScrollToEnd:\n\n\tcase Message::StutteredPageUp:\n\tcase Message::StutteredPageUpExtend:\n\tcase Message::StutteredPageDown:\n\tcase Message::StutteredPageDownExtend:\n\n\tcase Message::PageUp:\n\tcase Message::PageUpExtend:\n\tcase Message::PageDown:\n\tcase Message::PageDownExtend:\n\tcase Message::EditToggleOvertype:\n\tcase Message::Cancel:\n\tcase Message::DeleteBack:\n\tcase Message::Tab:\n\tcase Message::LineIndent:\n\tcase Message::BackTab:\n\tcase Message::LineDedent:\n\tcase Message::NewLine:\n\tcase Message::FormFeed:\n\tcase Message::VCHome:\n\tcase Message::VCHomeExtend:\n\tcase Message::VCHomeWrap:\n\tcase Message::VCHomeWrapExtend:\n\tcase Message::VCHomeDisplay:\n\tcase Message::VCHomeDisplayExtend:\n\tcase Message::ZoomIn:\n\tcase Message::ZoomOut:\n\tcase Message::DelWordLeft:\n\tcase Message::DelWordRight:\n\tcase Message::DelWordRightEnd:\n\tcase Message::DelLineLeft:\n\tcase Message::DelLineRight:\n\tcase Message::LineCopy:\n\tcase Message::LineCut:\n\tcase Message::LineDelete:\n\tcase Message::LineTranspose:\n\tcase Message::LineReverse:\n\tcase Message::LineDuplicate:\n\tcase Message::LowerCase:\n\tcase Message::UpperCase:\n\tcase Message::LineScrollDown:\n\tcase Message::LineScrollUp:\n\tcase Message::WordPartLeft:\n\tcase Message::WordPartLeftExtend:\n\tcase Message::WordPartRight:\n\tcase Message::WordPartRightExtend:\n\tcase Message::DeleteBackNotLine:\n\tcase Message::HomeDisplay:\n\tcase Message::HomeDisplayExtend:\n\tcase Message::LineEndDisplay:\n\tcase Message::LineEndDisplayExtend:\n\tcase Message::LineDownRectExtend:\n\tcase Message::LineUpRectExtend:\n\tcase Message::CharLeftRectExtend:\n\tcase Message::CharRightRectExtend:\n\tcase Message::HomeRectExtend:\n\tcase Message::VCHomeRectExtend:\n\tcase Message::LineEndRectExtend:\n\tcase Message::PageUpRectExtend:\n\tcase Message::PageDownRectExtend:\n\tcase Message::SelectionDuplicate:\n\t\treturn KeyCommand(iMessage);\n\n\tcase Message::BraceHighlight:\n\t\tSetBraceHighlight(PositionFromUPtr(wParam), lParam, StyleBraceLight);\n\t\tbreak;\n\n\tcase Message::BraceHighlightIndicator:\n\t\tif (lParam >= 0 && static_cast<size_t>(lParam) <= IndicatorMax) {\n\t\t\tvs.braceHighlightIndicatorSet = wParam != 0;\n\t\t\tvs.braceHighlightIndicator = static_cast<int>(lParam);\n\t\t}\n\t\tbreak;\n\n\tcase Message::BraceBadLight:\n\t\tSetBraceHighlight(PositionFromUPtr(wParam), -1, StyleBraceBad);\n\t\tbreak;\n\n\tcase Message::BraceBadLightIndicator:\n\t\tif (lParam >= 0 && static_cast<size_t>(lParam) <= IndicatorMax) {\n\t\t\tvs.braceBadLightIndicatorSet = wParam != 0;\n\t\t\tvs.braceBadLightIndicator = static_cast<int>(lParam);\n\t\t}\n\t\tbreak;\n\n\tcase Message::BraceMatch:\n\t\t// wParam is position of char to find brace for,\n\t\t// lParam is maximum amount of text to restyle to find it\n\t\treturn pdoc->BraceMatch(PositionFromUPtr(wParam), lParam, 0, false);\n\n\tcase Message::BraceMatchNext:\n\t\treturn pdoc->BraceMatch(PositionFromUPtr(wParam), 0, lParam, true);\n\n\tcase Message::GetViewEOL:\n\t\treturn vs.viewEOL;\n\n\tcase Message::SetViewEOL:\n\t\tvs.viewEOL = wParam != 0;\n\t\tInvalidateStyleRedraw();\n\t\tbreak;\n\n\tcase Message::SetZoom:\n\t\tif (SetAppearance(vs.zoomLevel, static_cast<int>(wParam))) {\n\t\t\tNotifyZoom();\n\t\t}\n\t\tbreak;\n\n\tcase Message::GetZoom:\n\t\treturn vs.zoomLevel;\n\n\tcase Message::GetEdgeColumn:\n\t\treturn vs.theEdge.column;\n\n\tcase Message::SetEdgeColumn:\n\t\tvs.theEdge.column = static_cast<int>(wParam);\n\t\tInvalidateStyleRedraw();\n\t\tbreak;\n\n\tcase Message::GetEdgeMode:\n\t\treturn static_cast<sptr_t>(vs.edgeState);\n\n\tcase Message::SetEdgeMode:\n\t\tvs.edgeState = static_cast<EdgeVisualStyle>(wParam);\n\t\tInvalidateStyleRedraw();\n\t\tbreak;\n\n\tcase Message::GetEdgeColour:\n\t\treturn vs.theEdge.colour.OpaqueRGB();\n\n\tcase Message::SetEdgeColour:\n\t\tvs.theEdge.colour = ColourRGBA::FromIpRGB(SPtrFromUPtr(wParam));\n\t\tInvalidateStyleRedraw();\n\t\tbreak;\n\n\tcase Message::MultiEdgeAddLine:\n\t\tvs.AddMultiEdge(static_cast<int>(wParam), ColourRGBA::FromIpRGB(lParam));\n\t\tInvalidateStyleRedraw();\n\t\tbreak;\n\n\tcase Message::MultiEdgeClearAll:\n\t\tstd::vector<EdgeProperties>().swap(vs.theMultiEdge); // Free vector and memory, C++03 compatible\n\t\tInvalidateStyleRedraw();\n\t\tbreak;\n\n\tcase Message::GetMultiEdgeColumn: {\n\t\t\tconst size_t which = wParam;\n\t\t\t// size_t is unsigned so this also handles negative inputs.\n\t\t\tif (which >= vs.theMultiEdge.size()) {\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\treturn vs.theMultiEdge[which].column;\n\t\t}\n\n\tcase Message::GetAccessibility:\n\t\treturn static_cast<sptr_t>(Accessibility::Disabled);\n\n\tcase Message::SetAccessibility:\n\t\t// May be implemented by platform code.\n\t\tbreak;\n\n\tcase Message::GetDocPointer:\n\t\treturn SPtrFromPtr(pdoc->AsDocumentEditable());\n\n\tcase Message::SetDocPointer:\n\t\tCancelModes();\n\t\tSetDocPointer(static_cast<Document *>(static_cast<IDocumentEditable *>(PtrFromSPtr(lParam))));\n\t\treturn 0;\n\n\tcase Message::CreateDocument: {\n\t\t\tDocument *doc = new Document(static_cast<DocumentOption>(lParam));\n\t\t\tdoc->AddRef();\n\t\t\tdoc->Allocate(PositionFromUPtr(wParam));\n\t\t\tpcs = ContractionStateCreate(pdoc->IsLarge());\n\t\t\treturn SPtrFromPtr(doc->AsDocumentEditable());\n\t\t}\n\n\tcase Message::AddRefDocument:\n\t\t(static_cast<IDocumentEditable *>(PtrFromSPtr(lParam)))->AddRef();\n\t\tbreak;\n\n\tcase Message::ReleaseDocument:\n\t\t(static_cast<IDocumentEditable *>(PtrFromSPtr(lParam)))->Release();\n\t\tbreak;\n\n\tcase Message::GetDocumentOptions:\n\t\treturn static_cast<sptr_t>(pdoc->Options());\n\n\tcase Message::CreateLoader: {\n\t\t\tDocument *doc = new Document(static_cast<DocumentOption>(lParam));\n\t\t\tdoc->AddRef();\n\t\t\tdoc->Allocate(PositionFromUPtr(wParam));\n\t\t\tdoc->SetUndoCollection(false);\n\t\t\tpcs = ContractionStateCreate(pdoc->IsLarge());\n\t\t\tILoader *loader = doc;\n\t\t\treturn reinterpret_cast<sptr_t>(loader);\n\t\t}\n\n\tcase Message::SetModEventMask:\n\t\tmodEventMask = static_cast<ModificationFlags>(wParam);\n\t\treturn 0;\n\n\tcase Message::GetModEventMask:\n\t\treturn static_cast<sptr_t>(modEventMask);\n\n\tcase Message::SetCommandEvents:\n\t\tcommandEvents = static_cast<bool>(wParam);\n\t\treturn 0;\n\n\tcase Message::GetCommandEvents:\n\t\treturn commandEvents;\n\n\tcase Message::ConvertEOLs:\n\t\tpdoc->ConvertLineEnds(static_cast<EndOfLine>(wParam));\n\t\tSetSelection(sel.MainCaret(), sel.MainAnchor());\t// Ensure selection inside document\n\t\treturn 0;\n\n\tcase Message::SetLengthForEncode:\n\t\tlengthForEncode = PositionFromUPtr(wParam);\n\t\treturn 0;\n\n\tcase Message::SelectionIsRectangle:\n\t\treturn sel.selType == Selection::SelTypes::rectangle ? 1 : 0;\n\n\tcase Message::SetSelectionMode:\n\t\tSetSelectionMode(wParam, true);\n\t\tbreak;\n\tcase Message::ChangeSelectionMode:\n\t\tSetSelectionMode(wParam, false);\n\t\tbreak;\n\tcase Message::GetSelectionMode:\n\t\tswitch (sel.selType) {\n\t\tcase Selection::SelTypes::stream:\n\t\t\treturn static_cast<sptr_t>(SelectionMode::Stream);\n\t\tcase Selection::SelTypes::rectangle:\n\t\t\treturn static_cast<sptr_t>(SelectionMode::Rectangle);\n\t\tcase Selection::SelTypes::lines:\n\t\t\treturn static_cast<sptr_t>(SelectionMode::Lines);\n\t\tcase Selection::SelTypes::thin:\n\t\t\treturn static_cast<sptr_t>(SelectionMode::Thin);\n\t\tdefault:\t// ?!\n\t\t\treturn static_cast<sptr_t>(SelectionMode::Stream);\n\t\t}\n\tcase Message::SetMoveExtendsSelection:\n\t\tsel.SetMoveExtends(wParam != 0);\n\t\tbreak;\n\tcase Message::GetMoveExtendsSelection:\n\t\treturn sel.MoveExtends();\n\tcase Message::GetLineSelStartPosition:\n\tcase Message::GetLineSelEndPosition: {\n\t\t\tconst SelectionSegment segmentLine(\n\t\t\t\tpdoc->LineStart(LineFromUPtr(wParam)),\n\t\t\t\tpdoc->LineEnd(LineFromUPtr(wParam)));\n\t\t\tfor (size_t r=0; r<sel.Count(); r++) {\n\t\t\t\tconst SelectionSegment portion = sel.Range(r).Intersect(segmentLine);\n\t\t\t\tif (portion.start.IsValid()) {\n\t\t\t\t\treturn (iMessage == Message::GetLineSelStartPosition) ? portion.start.Position() : portion.end.Position();\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn Sci::invalidPosition;\n\t\t}\n\n\tcase Message::SetOvertype:\n\t\tif (inOverstrike != (wParam != 0)) {\n\t\t\tinOverstrike = wParam != 0;\n\t\t\tContainerNeedsUpdate(Update::Selection);\n\t\t\tShowCaretAtCurrentPosition();\n\t\t\tSetIdle(true);\n\t\t}\n\t\tbreak;\n\n\tcase Message::GetOvertype:\n\t\treturn inOverstrike ? 1 : 0;\n\n\tcase Message::SetFocus:\n\t\tSetFocusState(wParam != 0);\n\t\tbreak;\n\n\tcase Message::GetFocus:\n\t\treturn hasFocus;\n\n\tcase Message::SetStatus:\n\t\terrorStatus = static_cast<Status>(wParam);\n\t\tbreak;\n\n\tcase Message::GetStatus:\n\t\treturn static_cast<sptr_t>(errorStatus);\n\n\tcase Message::SetMouseDownCaptures:\n\t\tmouseDownCaptures = wParam != 0;\n\t\tbreak;\n\n\tcase Message::GetMouseDownCaptures:\n\t\treturn mouseDownCaptures;\n\n\tcase Message::SetMouseWheelCaptures:\n\t\tmouseWheelCaptures = wParam != 0;\n\t\tbreak;\n\n\tcase Message::GetMouseWheelCaptures:\n\t\treturn mouseWheelCaptures;\n\n\tcase Message::SetCursor:\n\t\tcursorMode = static_cast<CursorShape>(wParam);\n\t\tDisplayCursor(Window::Cursor::text);\n\t\tbreak;\n\n\tcase Message::GetCursor:\n\t\treturn static_cast<sptr_t>(cursorMode);\n\n\tcase Message::SetControlCharSymbol:\n\t\tvs.controlCharSymbol = static_cast<int>(wParam);\n\t\tInvalidateStyleRedraw();\n\t\tbreak;\n\n\tcase Message::GetControlCharSymbol:\n\t\treturn vs.controlCharSymbol;\n\n\tcase Message::SetRepresentation:\n\t\treprs->SetRepresentation(ConstCharPtrFromUPtr(wParam), ConstCharPtrFromSPtr(lParam));\n\t\tbreak;\n\n\tcase Message::GetRepresentation: {\n\t\t\tconst Representation *repr = reprs->RepresentationFromCharacter(\n\t\t\t\tConstCharPtrFromUPtr(wParam));\n\t\t\tif (repr) {\n\t\t\t\treturn StringResult(lParam, repr->stringRep.c_str());\n\t\t\t}\n\t\t\treturn 0;\n\t\t}\n\n\tcase Message::ClearRepresentation:\n\t\treprs->ClearRepresentation(ConstCharPtrFromUPtr(wParam));\n\t\tbreak;\n\n\tcase Message::ClearAllRepresentations:\n\t\tSetRepresentations();\n\t\tbreak;\n\n\tcase Message::SetRepresentationAppearance:\n\t\treprs->SetRepresentationAppearance(ConstCharPtrFromUPtr(wParam), static_cast<RepresentationAppearance>(lParam));\n\t\tbreak;\n\n\tcase Message::GetRepresentationAppearance: {\n\t\t\tconst Representation *repr = reprs->RepresentationFromCharacter(\n\t\t\t\tConstCharPtrFromUPtr(wParam));\n\t\t\tif (repr) {\n\t\t\t\treturn static_cast<sptr_t>(repr->appearance);\n\t\t\t}\n\t\t\treturn 0;\n\t\t}\n\tcase Message::SetRepresentationColour:\n\t\treprs->SetRepresentationColour(ConstCharPtrFromUPtr(wParam), ColourRGBA(static_cast<int>(lParam)));\n\t\tbreak;\n\n\tcase Message::GetRepresentationColour: {\n\t\t\tconst Representation *repr = reprs->RepresentationFromCharacter(\n\t\t\t\tConstCharPtrFromUPtr(wParam));\n\t\t\tif (repr) {\n\t\t\t\treturn repr->colour.AsInteger();\n\t\t\t}\n\t\t\treturn 0;\n\t\t}\n\n\tcase Message::StartRecord:\n\t\trecordingMacro = true;\n\t\treturn 0;\n\n\tcase Message::StopRecord:\n\t\trecordingMacro = false;\n\t\treturn 0;\n\n\tcase Message::MoveCaretInsideView:\n\t\tMoveCaretInsideView();\n\t\tbreak;\n\n\tcase Message::SetFoldMarginColour:\n\t\tvs.foldmarginColour = OptionalColour(wParam, lParam);\n\t\tInvalidateStyleRedraw();\n\t\tbreak;\n\n\tcase Message::SetFoldMarginHiColour:\n\t\tvs.foldmarginHighlightColour = OptionalColour(wParam, lParam);\n\t\tInvalidateStyleRedraw();\n\t\tbreak;\n\n\tcase Message::SetHotspotActiveFore:\n\t\tif (vs.SetElementColourOptional(Element::HotSpotActive, wParam, lParam)) {\n\t\t\tInvalidateStyleRedraw();\n\t\t}\n\t\tbreak;\n\n\tcase Message::GetHotspotActiveFore:\n\t\treturn vs.ElementColour(Element::HotSpotActive).value_or(ColourRGBA()).OpaqueRGB();\n\n\tcase Message::SetHotspotActiveBack:\n\t\tif (vs.SetElementColourOptional(Element::HotSpotActiveBack, wParam, lParam)) {\n\t\t\tInvalidateStyleRedraw();\n\t\t}\n\t\tbreak;\n\n\tcase Message::GetHotspotActiveBack:\n\t\treturn vs.ElementColour(Element::HotSpotActiveBack).value_or(ColourRGBA()).OpaqueRGB();\n\n\tcase Message::SetHotspotActiveUnderline:\n\t\tvs.hotspotUnderline = wParam != 0;\n\t\tInvalidateStyleRedraw();\n\t\tbreak;\n\n\tcase Message::GetHotspotActiveUnderline:\n\t\treturn vs.hotspotUnderline ? 1 : 0;\n\n\tcase Message::SetHotspotSingleLine:\n\t\thotspotSingleLine = wParam != 0;\n\t\tInvalidateStyleRedraw();\n\t\tbreak;\n\n\tcase Message::GetHotspotSingleLine:\n\t\treturn hotspotSingleLine ? 1 : 0;\n\n\tcase Message::SetPasteConvertEndings:\n\t\tconvertPastes = wParam != 0;\n\t\tbreak;\n\n\tcase Message::GetPasteConvertEndings:\n\t\treturn convertPastes ? 1 : 0;\n\n\tcase Message::GetCharacterPointer:\n\t\treturn reinterpret_cast<sptr_t>(pdoc->BufferPointer());\n\n\tcase Message::GetRangePointer:\n\t\treturn reinterpret_cast<sptr_t>(pdoc->RangePointer(\n\t\t\tPositionFromUPtr(wParam), lParam));\n\n\tcase Message::GetGapPosition:\n\t\treturn pdoc->GapPosition();\n\n\tcase Message::SetChangeHistory:\n\t\tchangeHistoryOption = static_cast<ChangeHistoryOption>(wParam);\n\t\tpdoc->ChangeHistorySet(wParam & 1);\n\t\tbreak;\n\n\tcase Message::GetChangeHistory:\n\t\treturn static_cast<sptr_t>(changeHistoryOption);\n\n\tcase Message::SetUndoSelectionHistory:\n\t\tChangeUndoSelectionHistory(static_cast<UndoSelectionHistoryOption>(wParam));\n\t\tbreak;\n\n\tcase Message::GetUndoSelectionHistory:\n\t\treturn static_cast<sptr_t>(undoSelectionHistoryOption);\n\n\tcase Message::SetSelectionSerialized:\n\t\tSetSelectionFromSerialized(ConstCharPtrFromSPtr(lParam));\n\t\tbreak;\n\n\tcase Message::GetSelectionSerialized: {\n\t\tconst std::string serialized = sel.ToString();\n\t\treturn BytesResult(lParam, serialized);\n\t}\n\n\tcase Message::SetExtraAscent:\n\t\tvs.extraAscent = static_cast<int>(wParam);\n\t\tInvalidateStyleRedraw();\n\t\tbreak;\n\n\tcase Message::GetExtraAscent:\n\t\treturn vs.extraAscent;\n\n\tcase Message::SetExtraDescent:\n\t\tvs.extraDescent = static_cast<int>(wParam);\n\t\tInvalidateStyleRedraw();\n\t\tbreak;\n\n\tcase Message::GetExtraDescent:\n\t\treturn vs.extraDescent;\n\n\tcase Message::MarginSetStyleOffset:\n\t\tvs.marginStyleOffset = static_cast<int>(wParam);\n\t\tInvalidateStyleRedraw();\n\t\tbreak;\n\n\tcase Message::MarginGetStyleOffset:\n\t\treturn vs.marginStyleOffset;\n\n\tcase Message::SetMarginOptions:\n\t\tmarginOptions = static_cast<MarginOption>(wParam);\n\t\tbreak;\n\n\tcase Message::GetMarginOptions:\n\t\treturn static_cast<sptr_t>(marginOptions);\n\n\tcase Message::MarginSetText:\n\t\tpdoc->MarginSetText(LineFromUPtr(wParam), ConstCharPtrFromSPtr(lParam));\n\t\tbreak;\n\n\tcase Message::MarginGetText: {\n\t\t\tconst StyledText st = pdoc->MarginStyledText(LineFromUPtr(wParam));\n\t\t\treturn BytesResult(lParam, reinterpret_cast<const unsigned char *>(st.text), st.length);\n\t\t}\n\n\tcase Message::MarginSetStyle:\n\t\tpdoc->MarginSetStyle(LineFromUPtr(wParam), static_cast<int>(lParam));\n\t\tbreak;\n\n\tcase Message::MarginGetStyle: {\n\t\t\tconst StyledText st = pdoc->MarginStyledText(LineFromUPtr(wParam));\n\t\t\treturn st.style;\n\t\t}\n\n\tcase Message::SCI_MARGINSTYLENEXT: //Au: fast find next line that has certain style in text margin\n\t\treturn pdoc->MarginStyledTextNext((Sci::Line)wParam, (int)lParam);\n\n\tcase Message::MarginSetStyles:\n\t\tpdoc->MarginSetStyles(LineFromUPtr(wParam), ConstUCharPtrFromSPtr(lParam));\n\t\tbreak;\n\n\tcase Message::MarginGetStyles: {\n\t\t\tconst StyledText st = pdoc->MarginStyledText(LineFromUPtr(wParam));\n\t\t\treturn BytesResult(lParam, st.styles, st.length);\n\t\t}\n\n\tcase Message::MarginTextClearAll:\n\t\tpdoc->MarginClearAll();\n\t\tbreak;\n\n\tcase Message::AnnotationSetText:\n\t\tpdoc->AnnotationSetText(LineFromUPtr(wParam), ConstCharPtrFromSPtr(lParam));\n\t\tbreak;\n\n\tcase Message::AnnotationGetText: {\n\t\t\tconst StyledText st = pdoc->AnnotationStyledText(LineFromUPtr(wParam));\n\t\t\treturn BytesResult(lParam, reinterpret_cast<const unsigned char *>(st.text), st.length);\n\t\t}\n\n\tcase Message::AnnotationGetStyle: {\n\t\t\tconst StyledText st = pdoc->AnnotationStyledText(LineFromUPtr(wParam));\n\t\t\treturn st.style;\n\t\t}\n\n\tcase Message::AnnotationSetStyle:\n\t\tpdoc->AnnotationSetStyle(LineFromUPtr(wParam), static_cast<int>(lParam));\n\t\tbreak;\n\n\tcase Message::AnnotationSetStyles:\n\t\tpdoc->AnnotationSetStyles(LineFromUPtr(wParam), ConstUCharPtrFromSPtr(lParam));\n\t\tbreak;\n\n\tcase Message::AnnotationGetStyles: {\n\t\t\tconst StyledText st = pdoc->AnnotationStyledText(LineFromUPtr(wParam));\n\t\t\treturn BytesResult(lParam, st.styles, st.length);\n\t\t}\n\n\tcase Message::AnnotationGetLines:\n\t\treturn pdoc->AnnotationLines(LineFromUPtr(wParam));\n\n\tcase Message::AnnotationClearAll:\n\t\tpdoc->AnnotationClearAll();\n\t\tbreak;\n\n\tcase Message::AnnotationSetVisible:\n\t\tSetAnnotationVisible(static_cast<AnnotationVisible>(wParam));\n\t\tbreak;\n\n\tcase Message::AnnotationGetVisible:\n\t\treturn static_cast<sptr_t>(vs.annotationVisible);\n\n\tcase Message::AnnotationSetStyleOffset:\n\t\tvs.annotationStyleOffset = static_cast<int>(wParam);\n\t\tInvalidateStyleRedraw();\n\t\tbreak;\n\n\tcase Message::AnnotationGetStyleOffset:\n\t\treturn vs.annotationStyleOffset;\n\n\tcase Message::EOLAnnotationSetText:\n\t\tpdoc->EOLAnnotationSetText(LineFromUPtr(wParam), ConstCharPtrFromSPtr(lParam));\n\t\tbreak;\n\n\tcase Message::EOLAnnotationGetText: {\n\t\t\tconst StyledText st = pdoc->EOLAnnotationStyledText(LineFromUPtr(wParam));\n\t\t\treturn BytesResult(lParam, reinterpret_cast<const unsigned char *>(st.text), st.length);\n\t\t}\n\n\tcase Message::EOLAnnotationGetStyle: {\n\t\t\tconst StyledText st = pdoc->EOLAnnotationStyledText(LineFromUPtr(wParam));\n\t\t\treturn st.style;\n\t\t}\n\n\tcase Message::EOLAnnotationSetStyle:\n\t\tpdoc->EOLAnnotationSetStyle(LineFromUPtr(wParam), static_cast<int>(lParam));\n\t\tbreak;\n\n\tcase Message::EOLAnnotationClearAll:\n\t\tpdoc->EOLAnnotationClearAll();\n\t\tbreak;\n\n\tcase Message::EOLAnnotationSetVisible:\n\t\tSetEOLAnnotationVisible(static_cast<EOLAnnotationVisible>(wParam));\n\t\tbreak;\n\n\tcase Message::EOLAnnotationGetVisible:\n\t\treturn static_cast<sptr_t>(vs.eolAnnotationVisible);\n\n\tcase Message::EOLAnnotationSetStyleOffset:\n\t\tvs.eolAnnotationStyleOffset = static_cast<int>(wParam);\n\t\tInvalidateStyleRedraw();\n\t\tbreak;\n\n\tcase Message::EOLAnnotationGetStyleOffset:\n\t\treturn vs.eolAnnotationStyleOffset;\n\n\tcase Message::ReleaseAllExtendedStyles:\n\t\tvs.ReleaseAllExtendedStyles();\n\t\tbreak;\n\n\tcase Message::AllocateExtendedStyles:\n\t\treturn vs.AllocateExtendedStyles(static_cast<int>(wParam));\n\n\tcase Message::SupportsFeature:\n\t\treturn SupportsFeature(static_cast<Supports>(wParam));\n\n\tcase Message::AddUndoAction:\n\t\tpdoc->AddUndoAction(PositionFromUPtr(wParam),\n\t\t\tFlagSet(static_cast<UndoFlags>(lParam), UndoFlags::MayCoalesce));\n\t\tbreak;\n\n\tcase Message::SetMouseSelectionRectangularSwitch:\n\t\tmouseSelectionRectangularSwitch = wParam != 0;\n\t\tbreak;\n\n\tcase Message::GetMouseSelectionRectangularSwitch:\n\t\treturn mouseSelectionRectangularSwitch;\n\n\tcase Message::SetMultipleSelection:\n\t\tmultipleSelection = wParam != 0;\n\t\tInvalidateCaret();\n\t\tbreak;\n\n\tcase Message::GetMultipleSelection:\n\t\treturn multipleSelection;\n\n\tcase Message::SetAdditionalSelectionTyping:\n\t\tadditionalSelectionTyping = wParam != 0;\n\t\tInvalidateCaret();\n\t\tbreak;\n\n\tcase Message::GetAdditionalSelectionTyping:\n\t\treturn additionalSelectionTyping;\n\n\tcase Message::SetMultiPaste:\n\t\tmultiPasteMode = static_cast<MultiPaste>(wParam);\n\t\tbreak;\n\n\tcase Message::GetMultiPaste:\n\t\treturn static_cast<sptr_t>(multiPasteMode);\n\n\tcase Message::SetAdditionalCaretsBlink:\n\t\tview.additionalCaretsBlink = wParam != 0;\n\t\tInvalidateCaret();\n\t\tbreak;\n\n\tcase Message::GetAdditionalCaretsBlink:\n\t\treturn view.additionalCaretsBlink;\n\n\tcase Message::SetAdditionalCaretsVisible:\n\t\tview.additionalCaretsVisible = wParam != 0;\n\t\tInvalidateCaret();\n\t\tbreak;\n\n\tcase Message::GetAdditionalCaretsVisible:\n\t\treturn view.additionalCaretsVisible;\n\n\tcase Message::GetSelections:\n\t\treturn sel.Count();\n\n\tcase Message::GetSelectionEmpty:\n\t\treturn sel.Empty();\n\n\tcase Message::ClearSelections:\n\t\tsel.Clear();\n\t\tContainerNeedsUpdate(Update::Selection);\n\t\tRedraw();\n\t\tbreak;\n\n\tcase Message::SetSelection:\n\t\tsel.SetSelection(SelectionRange(PositionFromUPtr(wParam), lParam));\n\t\tRedraw();\n\t\tbreak;\n\n\tcase Message::AddSelection:\n\t\tsel.AddSelection(SelectionRange(PositionFromUPtr(wParam), lParam));\n\t\tContainerNeedsUpdate(Update::Selection);\n\t\tRedraw();\n\t\tbreak;\n\n\tcase Message::SelectionFromPoint:\n\t\treturn SelectionFromPoint(PointFromParameters(wParam, lParam));\n\n\tcase Message::DropSelectionN:\n\t\tDropSelection(wParam);\n\t\tbreak;\n\n\tcase Message::SetMainSelection:\n\t\tsel.SetMain(wParam);\n\t\tContainerNeedsUpdate(Update::Selection);\n\t\tRedraw();\n\t\tbreak;\n\n\tcase Message::GetMainSelection:\n\t\treturn sel.Main();\n\n\tcase Message::SetSelectionNCaret:\n\tcase Message::SetSelectionNAnchor:\n\tcase Message::SetSelectionNCaretVirtualSpace:\n\tcase Message::SetSelectionNAnchorVirtualSpace:\n\tcase Message::SetSelectionNStart:\n\tcase Message::SetSelectionNEnd:\n\t\tSetSelectionNMessage(iMessage, wParam, lParam);\n\t\tbreak;\n\n\tcase Message::GetSelectionNCaret:\n\t\treturn sel.Range(wParam).caret.Position();\n\n\tcase Message::GetSelectionNAnchor:\n\t\treturn sel.Range(wParam).anchor.Position();\n\n\tcase Message::GetSelectionNCaretVirtualSpace:\n\t\treturn sel.Range(wParam).caret.VirtualSpace();\n\n\tcase Message::GetSelectionNAnchorVirtualSpace:\n\t\treturn sel.Range(wParam).anchor.VirtualSpace();\n\n\tcase Message::GetSelectionNStart:\n\t\treturn sel.Range(wParam).Start().Position();\n\n\tcase Message::GetSelectionNStartVirtualSpace:\n\t\treturn sel.Range(wParam).Start().VirtualSpace();\n\n\tcase Message::GetSelectionNEnd:\n\t\treturn sel.Range(wParam).End().Position();\n\n\tcase Message::GetSelectionNEndVirtualSpace:\n\t\treturn sel.Range(wParam).End().VirtualSpace();\n\n\tcase Message::SetRectangularSelectionCaret:\n\t\tif (!sel.IsRectangular())\n\t\t\tsel.Clear();\n\t\tsel.selType = Selection::SelTypes::rectangle;\n\t\tsel.Rectangular().caret.SetPosition(PositionFromUPtr(wParam));\n\t\tSetRectangularRange();\n\t\tRedraw();\n\t\tbreak;\n\n\tcase Message::GetRectangularSelectionCaret:\n\t\treturn sel.Rectangular().caret.Position();\n\n\tcase Message::SetRectangularSelectionAnchor:\n\t\tif (!sel.IsRectangular())\n\t\t\tsel.Clear();\n\t\tsel.selType = Selection::SelTypes::rectangle;\n\t\tsel.Rectangular().anchor.SetPosition(PositionFromUPtr(wParam));\n\t\tSetRectangularRange();\n\t\tRedraw();\n\t\tbreak;\n\n\tcase Message::GetRectangularSelectionAnchor:\n\t\treturn sel.Rectangular().anchor.Position();\n\n\tcase Message::SetRectangularSelectionCaretVirtualSpace:\n\t\tif (!sel.IsRectangular())\n\t\t\tsel.Clear();\n\t\tsel.selType = Selection::SelTypes::rectangle;\n\t\tsel.Rectangular().caret.SetVirtualSpace(PositionFromUPtr(wParam));\n\t\tSetRectangularRange();\n\t\tRedraw();\n\t\tbreak;\n\n\tcase Message::GetRectangularSelectionCaretVirtualSpace:\n\t\treturn sel.Rectangular().caret.VirtualSpace();\n\n\tcase Message::SetRectangularSelectionAnchorVirtualSpace:\n\t\tif (!sel.IsRectangular())\n\t\t\tsel.Clear();\n\t\tsel.selType = Selection::SelTypes::rectangle;\n\t\tsel.Rectangular().anchor.SetVirtualSpace(PositionFromUPtr(wParam));\n\t\tSetRectangularRange();\n\t\tRedraw();\n\t\tbreak;\n\n\tcase Message::GetRectangularSelectionAnchorVirtualSpace:\n\t\treturn sel.Rectangular().anchor.VirtualSpace();\n\n\tcase Message::SetVirtualSpaceOptions:\n\t\tvirtualSpaceOptions = static_cast<VirtualSpace>(wParam);\n\t\tbreak;\n\n\tcase Message::GetVirtualSpaceOptions:\n\t\treturn static_cast<sptr_t>(virtualSpaceOptions);\n\n\tcase Message::SetAdditionalSelFore:\n\t\tvs.elementColours[Element::SelectionAdditionalText] = ColourRGBA::FromIpRGB(SPtrFromUPtr(wParam));\n\t\tInvalidateStyleRedraw();\n\t\tbreak;\n\n\tcase Message::SetAdditionalSelBack:\n\t\tvs.SetElementRGB(Element::SelectionAdditionalBack, static_cast<int>(wParam));\n\t\tInvalidateStyleRedraw();\n\t\tbreak;\n\n\tcase Message::SetAdditionalSelAlpha:\n\t\tvs.SetElementAlpha(Element::SelectionAdditionalBack, static_cast<int>(wParam));\n\t\tInvalidateStyleRedraw();\n\t\tbreak;\n\n\tcase Message::GetAdditionalSelAlpha:\n\t\tif (vs.selection.layer == Layer::Base)\n\t\t\treturn static_cast<sptr_t>(Alpha::NoAlpha);\n\t\treturn vs.ElementColourForced(Element::SelectionAdditionalBack).GetAlpha();\n\n\tcase Message::SetAdditionalCaretFore:\n\t\tvs.elementColours[Element::CaretAdditional] = ColourRGBA::FromIpRGB(SPtrFromUPtr(wParam));\n\t\tInvalidateStyleRedraw();\n\t\tbreak;\n\n\tcase Message::GetAdditionalCaretFore:\n\t\treturn vs.ElementColourForced(Element::CaretAdditional).OpaqueRGB();\n\n\tcase Message::RotateSelection:\n\t\tsel.RotateMain();\n\t\tInvalidateWholeSelection();\n\t\tbreak;\n\n\tcase Message::SwapMainAnchorCaret:\n\t\tInvalidateSelection(sel.RangeMain());\n\t\tsel.RangeMain().Swap();\n\t\tbreak;\n\n\tcase Message::MultipleSelectAddNext:\n\t\tMultipleSelectAdd(AddNumber::one);\n\t\tbreak;\n\n\tcase Message::MultipleSelectAddEach:\n\t\tMultipleSelectAdd(AddNumber::each);\n\t\tbreak;\n\n\tcase Message::ChangeLexerState:\n\t\tpdoc->ChangeLexerState(PositionFromUPtr(wParam), lParam);\n\t\tbreak;\n\n\tcase Message::SetIdentifier:\n\t\tSetCtrlID(static_cast<int>(wParam));\n\t\tbreak;\n\n\tcase Message::GetIdentifier:\n\t\treturn GetCtrlID();\n\n\tcase Message::SetTechnology:\n\t\t// No action by default\n\t\tbreak;\n\n\tcase Message::GetTechnology:\n\t\treturn static_cast<sptr_t>(technology);\n\n\tcase Message::CountCharacters:\n\t\treturn pdoc->CountCharacters(PositionFromUPtr(wParam), lParam);\n\n\tcase Message::CountCodeUnits:\n\t\treturn pdoc->CountUTF16(PositionFromUPtr(wParam), lParam);\n\n\tdefault:\n\t\treturn DefWndProc(iMessage, wParam, lParam);\n\t}\n\n\t// If there was a change that needs its selection saved and it wasn't explicity saved\n\t// then do that here.\n\tRememberCurrentSelectionForRedoOntoStack();\n\n\t//Platform::DebugPrintf(\"end wnd proc\\n\");\n\treturn 0;\n}\n"
  },
  {
    "path": "Libraries/scintilla/src/Editor.h",
    "content": "// Scintilla source code edit control\n/** @file Editor.h\n ** Defines the main editor class.\n **/\n// Copyright 1998-2011 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#ifndef EDITOR_H\n#define EDITOR_H\n\nnamespace Scintilla::Internal {\n\n/**\n */\nclass Timer {\npublic:\n\tbool ticking;\n\tint ticksToWait;\n\tenum {tickSize = 100};\n\tTickerID tickerID;\n\n\tTimer() noexcept;\n};\n\n/**\n */\nclass Idler {\npublic:\n\tbool state;\n\tIdlerID idlerID;\n\n\tIdler() noexcept;\n};\n\n/**\n * When platform has a way to generate an event before painting,\n * accumulate needed styling range and other work items in\n * WorkNeeded to avoid unnecessary work inside paint handler\n */\n\nenum class WorkItems {\n\tnone = 0,\n\tstyle = 1,\n\tupdateUI = 2\n};\n\nclass WorkNeeded {\npublic:\n\tenum WorkItems items;\n\tSci::Position upTo;\n\n\tWorkNeeded() noexcept : items(WorkItems::none), upTo(0) {}\n\tvoid Reset() noexcept {\n\t\titems = WorkItems::none;\n\t\tupTo = 0;\n\t}\n\tvoid Need(WorkItems items_, Sci::Position pos) noexcept {\n\t\tif (Scintilla::FlagSet(items_, WorkItems::style) && (upTo < pos))\n\t\t\tupTo = pos;\n\t\titems = static_cast<WorkItems>(static_cast<int>(items) | static_cast<int>(items_));\n\t}\n};\n\n/**\n * Hold a piece of text selected for copying or dragging, along with encoding and selection format information.\n */\nclass SelectionText {\n\tstd::string s;\npublic:\n\tbool rectangular;\n\tbool lineCopy;\n\tint codePage;\n\tScintilla::CharacterSet characterSet;\n\tSelectionText() noexcept : rectangular(false), lineCopy(false), codePage(0), characterSet(Scintilla::CharacterSet::Ansi) {}\n\tvoid Clear() noexcept {\n\t\ts.clear();\n\t\trectangular = false;\n\t\tlineCopy = false;\n\t\tcodePage = 0;\n\t\tcharacterSet = Scintilla::CharacterSet::Ansi;\n\t}\n\tvoid Copy(const std::string &s_, int codePage_, Scintilla::CharacterSet characterSet_, bool rectangular_, bool lineCopy_) {\n\t\ts = s_;\n\t\tcodePage = codePage_;\n\t\tcharacterSet = characterSet_;\n\t\trectangular = rectangular_;\n\t\tlineCopy = lineCopy_;\n\t\tFixSelectionForClipboard();\n\t}\n\tvoid Copy(const SelectionText &other) {\n\t\tCopy(other.s, other.codePage, other.characterSet, other.rectangular, other.lineCopy);\n\t}\n\tconst char *Data() const noexcept {\n\t\treturn s.c_str();\n\t}\n\tsize_t Length() const noexcept {\n\t\treturn s.length();\n\t}\n\tsize_t LengthWithTerminator() const noexcept {\n\t\treturn s.length() + 1;\n\t}\n\tbool Empty() const noexcept {\n\t\treturn s.empty();\n\t}\nprivate:\n\tvoid FixSelectionForClipboard() {\n\t\t// To avoid truncating the contents of the clipboard when pasted where the\n\t\t// clipboard contains NUL characters, replace NUL characters by spaces.\n\t\tstd::replace(s.begin(), s.end(), '\\0', ' ');\n\t}\n};\n\nstruct WrapPending {\n\t// The range of lines that need to be wrapped\n\tenum { lineLarge = 0x7ffffff };\n\tSci::Line start;\t// When there are wraps pending, will be in document range\n\tSci::Line end;\t// May be lineLarge to indicate all of document after start\n\tWrapPending() noexcept {\n\t\tstart = lineLarge;\n\t\tend = lineLarge;\n\t}\n\tvoid Reset() noexcept {\n\t\tstart = lineLarge;\n\t\tend = lineLarge;\n\t}\n\tvoid Wrapped(Sci::Line line) noexcept {\n\t\tif (start == line)\n\t\t\tstart++;\n\t}\n\tbool NeedsWrap() const noexcept {\n\t\treturn start < end;\n\t}\n\tbool AddRange(Sci::Line lineStart, Sci::Line lineEnd) noexcept {\n\t\tconst bool neededWrap = NeedsWrap();\n\t\tbool changed = false;\n\t\tif (start > lineStart) {\n\t\t\tstart = lineStart;\n\t\t\tchanged = true;\n\t\t}\n\t\tif ((end < lineEnd) || !neededWrap) {\n\t\t\tend = lineEnd;\n\t\t\tchanged = true;\n\t\t}\n\t\treturn changed;\n\t}\n};\n\nstruct CaretPolicySlop {\n\tScintilla::CaretPolicy policy;\t// Combination from CaretPolicy::Slop, CaretPolicy::Strict, CaretPolicy::Jumps, CaretPolicy::Even\n\tint slop;\t// Pixels for X, lines for Y\n\tCaretPolicySlop(Scintilla::CaretPolicy policy_, intptr_t slop_) noexcept :\n\t\tpolicy(policy_), slop(static_cast<int>(slop_)) {}\n\tCaretPolicySlop(uintptr_t policy_=0, intptr_t slop_=0) noexcept :\n\t\tpolicy(static_cast<Scintilla::CaretPolicy>(policy_)), slop(static_cast<int>(slop_)) {}\n};\n\nstruct CaretPolicies {\n\tCaretPolicySlop x;\n\tCaretPolicySlop y;\n};\n\nstruct VisiblePolicySlop {\n\tScintilla::VisiblePolicy policy;\t// Combination from VisiblePolicy::Slop, VisiblePolicy::Strict\n\tint slop;\t// Pixels for X, lines for Y\n\tVisiblePolicySlop(uintptr_t policy_ = 0, intptr_t slop_ = 0) noexcept :\n\t\tpolicy(static_cast<Scintilla::VisiblePolicy>(policy_)), slop(static_cast<int>(slop_)) {}\n};\n\nenum class XYScrollOptions {\n\tnone = 0x0,\n\tuseMargin = 0x1,\n\tvertical = 0x2,\n\thorizontal = 0x4,\n\tall = useMargin | vertical | horizontal\n};\n\nconstexpr XYScrollOptions operator|(XYScrollOptions a, XYScrollOptions b) noexcept {\n\treturn static_cast<XYScrollOptions>(static_cast<int>(a) | static_cast<int>(b));\n}\n\n/**\n */\nclass Editor : public EditModel, public DocWatcher {\nprotected:\t// ScintillaBase subclass needs access to much of Editor\n\n\t/** On GTK+, Scintilla is a container widget holding two scroll bars\n\t * whereas on Windows there is just one window with both scroll bars turned on. */\n\tWindow wMain;\t///< The Scintilla parent window\n\tWindow wMargin;\t///< May be separate when using a scroll view for wMain\n\n\t// Optimization that avoids superfluous invalidations\n\tbool redrawPendingText = false;\n\tbool redrawPendingMargin = false;\n\n\t/** Style resources may be expensive to allocate so are cached between uses.\n\t * When a style attribute is changed, this cache is flushed. */\n\tbool stylesValid;\n\tViewStyle vs;\n\tScintilla::Technology technology;\n\tPoint sizeRGBAImage;\n\tfloat scaleRGBAImage;\n\n\tMarginView marginView;\n\tEditView view;\n\n\tScintilla::CursorShape cursorMode;\n\n\tbool mouseDownCaptures;\n\tbool mouseWheelCaptures;\n\n\tint xCaretMargin;\t///< Ensure this many pixels visible on both sides of caret\n\tbool horizontalScrollBarVisible;\n\tint scrollWidth;\n\tbool verticalScrollBarVisible;\n\tbool endAtLastLine;\n\tScintilla::CaretSticky caretSticky;\n\tScintilla::MarginOption marginOptions;\n\tbool mouseSelectionRectangularSwitch;\n\tbool multipleSelection;\n\tbool additionalSelectionTyping;\n\tScintilla::MultiPaste multiPasteMode;\n\n\tScintilla::VirtualSpace virtualSpaceOptions;\n\n\tKeyMap kmap;\n\n\tTimer timer;\n\tTimer autoScrollTimer;\n\tenum { autoScrollDelay = 200 };\n\n\tIdler idler;\n\n\tPoint lastClick;\n\tunsigned int lastClickTime;\n\tPoint doubleClickCloseThreshold;\n\tint dwellDelay;\n\tint ticksToDwell;\n\tbool dwelling;\n\tenum class TextUnit { character, word, subLine, wholeLine } selectionUnit;\n\tPoint ptMouseLast;\n\tenum class DragDrop { none, initial, dragging } inDragDrop;\n\tbool dropWentOutside;\n\tSelectionPosition posDrop;\n\tSci::Position hotSpotClickPos;\n\tint lastXChosen;\n\tSci::Position lineAnchorPos;\n\tSci::Position originalAnchorPos;\n\tSci::Position wordSelectAnchorStartPos;\n\tSci::Position wordSelectAnchorEndPos;\n\tSci::Position wordSelectInitialCaretPos;\n\tSelectionSegment targetRange;\n\tScintilla::FindOption searchFlags;\n\tSci::Line topLine;\n\tSci::Position posTopLine;\n\tSci::Position lengthForEncode;\n\n\tScintilla::Update needUpdateUI;\n\n\tenum class PaintState { notPainting, painting, abandoned } paintState;\n\tbool paintAbandonedByStyling;\n\tPRectangle rcPaint;\n\tbool paintingAllText;\n\tbool willRedrawAll;\n\tWorkNeeded workNeeded;\n\tScintilla::IdleStyling idleStyling;\n\tbool needIdleStyling;\n\n\tScintilla::ModificationFlags modEventMask;\n\tbool commandEvents;\n\n\tSelectionText drag;\n\n\tCaretPolicies caretPolicies;\n\n\tVisiblePolicySlop visiblePolicy;\n\n\tSci::Position searchAnchor;\n\n\tbool recordingMacro;\n\n\tScintilla::AutomaticFold foldAutomatic;\n\n\t// Wrapping support\n\tWrapPending wrapPending;\n\tActionDuration durationWrapOneByte;\n\tbool insideWrapScroll;\n\tstruct LineDocSub {\n\t\tScintilla::Line lineDoc = 0;\n\t\tScintilla::Line subLine = 0;\n\t};\n\tstd::optional<LineDocSub> scrollToAfterWrap;\n\n\tbool convertPastes;\n\n\tEditor();\n\t// Deleted so Editor objects can not be copied.\n\tEditor(const Editor &) = delete;\n\tEditor(Editor &&) = delete;\n\tEditor &operator=(const Editor &) = delete;\n\tEditor &operator=(Editor &&) = delete;\n\t// ~Editor() in public section\n\tvirtual void Initialise() = 0;\n\tvirtual void Finalise();\n\n\tvoid InvalidateStyleData() noexcept;\n\tvoid InvalidateStyleRedraw();\n\tvoid RefreshStyleData();\n\tvoid SetRepresentations();\n\tvoid DropGraphics() noexcept;\n\n\tbool HasMarginWindow() const noexcept;\n\t// The top left visible point in main window coordinates. Will be 0,0 except for\n\t// scroll views where it will be equivalent to the current scroll position.\n\tPoint GetVisibleOriginInMain() const override;\n\tPointDocument DocumentPointFromView(Point ptView) const;  // Convert a point from view space to document\n\tSci::Line TopLineOfMain() const noexcept final;   // Return the line at Main's y coordinate 0\n\tvirtual Point ClientSize() const;\n\tvirtual PRectangle GetClientRectangle() const;\n\tvirtual PRectangle GetClientDrawingRectangle();\n\tPRectangle GetTextRectangle() const;\n\n\tSci::Line LinesOnScreen() const override;\n\tSci::Line LinesToScroll() const;\n\tSci::Line MaxScrollPos() const;\n\tSelectionPosition ClampPositionIntoDocument(SelectionPosition sp) const;\n\tPoint LocationFromPosition(SelectionPosition pos, PointEnd pe=PointEnd::start);\n\tPoint LocationFromPosition(Sci::Position pos, PointEnd pe=PointEnd::start);\n\tint XFromPosition(SelectionPosition sp);\n\tSelectionPosition SPositionFromLocation(Point pt, bool canReturnInvalid=false, bool charPosition=false, bool virtualSpace=true);\n\tSci::Position PositionFromLocation(Point pt, bool canReturnInvalid = false, bool charPosition = false);\n\tSelectionPosition SPositionFromLineX(Sci::Line lineDoc, int x);\n\tSci::Position PositionFromLineX(Sci::Line lineDoc, int x);\n\tSci::Line LineFromLocation(Point pt) const noexcept;\n\tvoid SetTopLine(Sci::Line topLineNew);\n\n\tvirtual bool AbandonPaint();\n\tvirtual void RedrawRect(PRectangle rc);\n\tvirtual void DiscardOverdraw();\n\tvirtual void Redraw();\n\tvoid RedrawSelMargin(Sci::Line line=-1, bool allAfter=false);\n\tPRectangle RectangleFromRange(Range r, int overlap);\n\tvoid InvalidateRange(Sci::Position start, Sci::Position end);\n\n\tbool UserVirtualSpace() const noexcept {\n\t\treturn (FlagSet(virtualSpaceOptions, Scintilla::VirtualSpace::UserAccessible));\n\t}\n\tSci::Position CurrentPosition() const noexcept;\n\tbool SelectionEmpty() const noexcept;\n\tSelectionPosition SelectionStart() noexcept;\n\tSelectionPosition SelectionEnd() noexcept;\n\tvoid SetRectangularRange();\n\tvoid ThinRectangularRange();\n\tvoid InvalidateSelection(SelectionRange newMain, bool invalidateWholeSelection=false);\n\tvoid InvalidateWholeSelection();\n\tSelectionRange LineSelectionRange(SelectionPosition currentPos_, SelectionPosition anchor_) const noexcept;\n\tvoid SetSelection(SelectionPosition currentPos_, SelectionPosition anchor_);\n\tvoid SetSelection(Sci::Position currentPos_, Sci::Position anchor_);\n\tvoid SetSelection(SelectionPosition currentPos_);\n\tvoid SetEmptySelection(SelectionPosition currentPos_);\n\tvoid SetEmptySelection(Sci::Position currentPos_);\n\tvoid SetSelectionFromSerialized(const char *serialized);\n\tenum class AddNumber { one, each };\n\tvoid MultipleSelectAdd(AddNumber addNumber);\n\tbool RangeContainsProtected(Sci::Position start, Sci::Position end) const noexcept;\n\tbool RangeContainsProtected(const SelectionRange &range) const noexcept;\n\tbool SelectionContainsProtected() const noexcept;\n\tSci::Position MovePositionOutsideChar(Sci::Position pos, Sci::Position moveDir, bool checkLineEnd=true) const;\n\tSelectionPosition MovePositionOutsideChar(SelectionPosition pos, Sci::Position moveDir, bool checkLineEnd=true) const;\n\tvoid MovedCaret(SelectionPosition newPos, SelectionPosition previousPos,\n\t\tbool ensureVisible, CaretPolicies policies);\n\tvoid MovePositionTo(SelectionPosition newPos, Selection::SelTypes selt=Selection::SelTypes::none, bool ensureVisible=true);\n\tvoid MovePositionTo(Sci::Position newPos, Selection::SelTypes selt=Selection::SelTypes::none, bool ensureVisible=true);\n\tSelectionPosition MovePositionSoVisible(SelectionPosition pos, int moveDir);\n\tSelectionPosition MovePositionSoVisible(Sci::Position pos, int moveDir);\n\tPoint PointMainCaret();\n\tvoid SetLastXChosen();\n\tvoid RememberSelectionForUndo(int index);\n\tvoid RememberSelectionOntoStack(int index);\n\tvoid RememberCurrentSelectionForRedoOntoStack();\n\n\tvoid ScrollTo(Sci::Line line, bool moveThumb=true);\n\tvirtual void ScrollText(Sci::Line linesToMove);\n\tvoid HorizontalScrollTo(int xPos);\n\tvoid VerticalCentreCaret();\n\tvoid MoveSelectedLines(int lineDelta);\n\tvoid MoveSelectedLinesUp();\n\tvoid MoveSelectedLinesDown();\n\tvoid MoveCaretInsideView(bool ensureVisible=true);\n\tSci::Line DisplayFromPosition(Sci::Position pos);\n\n\tstruct XYScrollPosition {\n\t\tint xOffset;\n\t\tSci::Line topLine;\n\t\tXYScrollPosition(int xOffset_, Sci::Line topLine_) noexcept : xOffset(xOffset_), topLine(topLine_) {}\n\t\tbool operator==(const XYScrollPosition &other) const noexcept {\n\t\t\treturn (xOffset == other.xOffset) && (topLine == other.topLine);\n\t\t}\n\t};\n\tXYScrollPosition XYScrollToMakeVisible(const SelectionRange &range,\n\t\tconst XYScrollOptions options, CaretPolicies policies);\n\tvoid SetXYScroll(XYScrollPosition newXY);\n\tvoid EnsureCaretVisible(bool useMargin=true, bool vert=true, bool horiz=true);\n\tvoid ScrollRange(SelectionRange range);\n\tvoid ShowCaretAtCurrentPosition();\n\tvoid DropCaret();\n\tvoid CaretSetPeriod(int period);\n\tvoid InvalidateCaret();\n\tvirtual void NotifyCaretMove();\n\tvirtual void UpdateSystemCaret();\n\n\tbool Wrapping() const noexcept;\n\tvoid NeedWrapping(Sci::Line docLineStart=0, Sci::Line docLineEnd=WrapPending::lineLarge);\n\tbool WrapOneLine(Surface *surface, Sci::Line lineToWrap);\n\tbool WrapBlock(Surface *surface, Sci::Line lineToWrap, Sci::Line lineToWrapEnd);\n\tenum class WrapScope {wsAll, wsVisible, wsIdle};\n\tbool WrapLines(WrapScope ws);\n\tvoid LinesJoin();\n\tvoid LinesSplit(int pixelWidth);\n\n\tvoid PaintSelMargin(Surface *surfaceWindow, const PRectangle &rc);\n\tvoid RefreshPixMaps(Surface *surfaceWindow);\n\tvoid Paint(Surface *surfaceWindow, PRectangle rcArea);\n\tSci::Position FormatRange(Scintilla::Message iMessage, Scintilla::uptr_t wParam, Scintilla::sptr_t lParam);\n\tlong TextWidth(Scintilla::uptr_t style, const char *text);\n\n\tvirtual void SetVerticalScrollPos();\n\tvirtual void SetHorizontalScrollPos() = 0;\n\tvirtual bool ModifyScrollBars(Sci::Line nMax, Sci::Line nPage) = 0;\n\tvirtual void ReconfigureScrollBars();\n\tvoid ChangeScrollBars();\n\tvirtual void SetScrollBars();\n\tvoid ChangeSize();\n\n\tvoid FilterSelections();\n\tSci::Position RealizeVirtualSpace(Sci::Position position, Sci::Position virtualSpace);\n\tSelectionPosition RealizeVirtualSpace(const SelectionPosition &position);\n\tvoid AddChar(char ch);\n\tvirtual void InsertCharacter(std::string_view sv, Scintilla::CharacterSource charSource);\n\tvoid ClearSelectionRange(SelectionRange &range);\n\tvoid ClearBeforeTentativeStart();\n\tvoid InsertPaste(const char *text, Sci::Position len);\n\tenum class PasteShape { stream=0, rectangular = 1, line = 2 };\n\tvoid InsertPasteShape(const char *text, Sci::Position len, PasteShape shape);\n\tvoid ClearSelection(bool retainMultipleSelections = false);\n\tvoid ClearAll();\n\tvoid ClearDocumentStyle();\n\tvirtual void Cut();\n\tvoid PasteRectangular(SelectionPosition pos, const char *ptr, Sci::Position len);\n\tvirtual void Copy() = 0;\n\tvoid CopyAllowLine();\n\tvoid CutAllowLine();\n\tvirtual bool CanPaste();\n\tvirtual void Paste() = 0;\n\tvoid Clear();\n\tvirtual void SelectAll();\n\tvoid RestoreSelection(Sci::Position newPos, UndoRedo history);\n\tvirtual void Undo(bool dontChangePos); //Au: added dontChangePos\n\tvirtual void Redo(bool dontChangePos); //Au: added dontChangePos\n\tvoid DelCharBack(bool allowLineStartDeletion);\n\tvirtual void ClaimSelection() = 0;\n\n\tvirtual void NotifyChange() = 0;\n\tvirtual void NotifyFocus(bool focus);\n\tvirtual void SetCtrlID(int identifier);\n\tvirtual int GetCtrlID() { return ctrlID; }\n\tvirtual void NotifyParent(Scintilla::NotificationData scn) = 0;\n\tvirtual void NotifyStyleToNeeded(Sci::Position endStyleNeeded);\n\tvoid NotifyChar(int ch, Scintilla::CharacterSource charSource);\n\tvoid NotifySavePoint(bool isSavePoint);\n\tvoid NotifyModifyAttempt();\n\tvirtual void NotifyDoubleClick(Point pt, Scintilla::KeyMod modifiers);\n\tvoid NotifyHotSpotClicked(Sci::Position position, Scintilla::KeyMod modifiers);\n\tvoid NotifyHotSpotDoubleClicked(Sci::Position position, Scintilla::KeyMod modifiers);\n\tvoid NotifyHotSpotReleaseClick(Sci::Position position, Scintilla::KeyMod modifiers);\n\tbool NotifyUpdateUI();\n\tvoid NotifyPainted();\n\tvoid NotifyIndicatorClick(bool click, Sci::Position position, Scintilla::KeyMod modifiers);\n\tbool NotifyMarginClick(Point pt, Scintilla::KeyMod modifiers);\n\tbool NotifyMarginRightClick(Point pt, Scintilla::KeyMod modifiers);\n\tvoid NotifyNeedShown(Sci::Position pos, Sci::Position len);\n\tvoid NotifyDwelling(Point pt, bool state);\n\tvoid NotifyZoom();\n\n\tvoid NotifyModifyAttempt(Document *document, void *userData) override;\n\tvoid NotifySavePoint(Document *document, void *userData, bool atSavePoint) override;\n\tvoid CheckModificationForWrap(DocModification mh);\n\tvoid NotifyModified(Document *document, DocModification mh, void *userData) override;\n\tvoid NotifyDeleted(Document *document, void *userData) noexcept override;\n\tvoid NotifyStyleNeeded(Document *doc, void *userData, Sci::Position endStyleNeeded) override;\n\tvoid NotifyErrorOccurred(Document *doc, void *userData, Scintilla::Status status) override;\n\tvoid NotifyGroupCompleted(Document *, void *) noexcept override;\n\tvoid NotifyMacroRecord(Scintilla::Message iMessage, Scintilla::uptr_t wParam, Scintilla::sptr_t lParam);\n\n\tvoid ContainerNeedsUpdate(Scintilla::Update flags) noexcept;\n\tvoid PageMove(int direction, Selection::SelTypes selt=Selection::SelTypes::none, bool stuttered = false);\n\tenum class CaseMapping { same, upper, lower };\n\tvirtual std::string CaseMapString(const std::string &s, CaseMapping caseMapping);\n\tvoid ChangeCaseOfSelection(CaseMapping caseMapping);\n\tvoid LineDelete();\n\tvoid LineTranspose();\n\tvoid LineReverse();\n\tvoid Duplicate(bool forLine);\n\tvirtual void CancelModes();\n\tvoid NewLine();\n\tSelectionPosition PositionUpOrDown(SelectionPosition spStart, int direction, int lastX);\n\tvoid CursorUpOrDown(int direction, Selection::SelTypes selt);\n\tvoid ParaUpOrDown(int direction, Selection::SelTypes selt);\n\tRange RangeDisplayLine(Sci::Line lineVisible);\n\tSci::Position StartEndDisplayLine(Sci::Position pos, bool start);\n\tSci::Position HomeWrapPosition(Sci::Position position);\n\tSci::Position VCHomeDisplayPosition(Sci::Position position);\n\tSci::Position VCHomeWrapPosition(Sci::Position position);\n\tSci::Position LineEndWrapPosition(Sci::Position position);\n\tSelectionPosition PositionMove(Scintilla::Message iMessage, SelectionPosition spCaretNow);\n\tSelectionRange SelectionMove(Scintilla::Message iMessage, size_t r);\n\tint HorizontalMove(Scintilla::Message iMessage);\n\tint DelWordOrLine(Scintilla::Message iMessage);\n\tvirtual int KeyCommand(Scintilla::Message iMessage);\n\tvirtual int KeyDefault(Scintilla::Keys /* key */, Scintilla::KeyMod /*modifiers*/);\n\tint KeyDownWithModifiers(Scintilla::Keys key, Scintilla::KeyMod modifiers, bool *consumed);\n\n\tvoid Indent(bool forwards, bool lineIndent);\n\n\tvirtual std::unique_ptr<CaseFolder> CaseFolderForEncoding();\n\tSci::Position FindText(Scintilla::uptr_t wParam, Scintilla::sptr_t lParam);\n\tSci::Position FindTextFull(Scintilla::uptr_t wParam, Scintilla::sptr_t lParam);\n\tvoid SearchAnchor() noexcept;\n\tSci::Position SearchText(Scintilla::Message iMessage, Scintilla::uptr_t wParam, Scintilla::sptr_t lParam);\n\tSci::Position SearchInTarget(const char *text, Sci::Position length);\n\tvoid GoToLine(Sci::Line lineNo);\n\n\tvirtual void CopyToClipboard(const SelectionText &selectedText) = 0;\n\tstd::string RangeText(Sci::Position start, Sci::Position end) const;\n\tbool CopyLineRange(SelectionText *ss, bool allowProtected=true);\n\tvoid CopySelectionRange(SelectionText *ss, bool allowLineCopy=false);\n\tvoid CopyRangeToClipboard(Sci::Position start, Sci::Position end);\n\tvoid CopyText(size_t length, const char *text);\n\tvoid SetDragPosition(SelectionPosition newPos);\n\tvirtual void DisplayCursor(Window::Cursor c);\n\tvirtual bool DragThreshold(Point ptStart, Point ptNow);\n\tvirtual void StartDrag();\n\tvoid DropAt(SelectionPosition position, const char *value, size_t lengthValue, bool moving, bool rectangular);\n\tvoid DropAt(SelectionPosition position, const char *value, bool moving, bool rectangular);\n\t/** PositionInSelection returns true if position in selection. */\n\tbool PositionInSelection(Sci::Position pos);\n\tbool PointInSelection(Point pt);\n\tptrdiff_t SelectionFromPoint(Point pt);\n\tbool PointInSelMargin(Point pt) const;\n\tWindow::Cursor GetMarginCursor(Point pt) const noexcept;\n\tvoid DropSelection(size_t part);\n\tvoid TrimAndSetSelection(Sci::Position currentPos_, Sci::Position anchor_);\n\tvoid LineSelection(Sci::Position lineCurrentPos_, Sci::Position lineAnchorPos_, bool wholeLine);\n\tvoid WordSelection(Sci::Position pos);\n\tvoid DwellEnd(bool mouseMoved);\n\tvoid MouseLeave();\n\tvirtual void ButtonDownWithModifiers(Point pt, unsigned int curTime, Scintilla::KeyMod modifiers);\n\tvirtual void RightButtonDownWithModifiers(Point pt, unsigned int curTime, Scintilla::KeyMod modifiers);\n\tvoid ButtonMoveWithModifiers(Point pt, unsigned int curTime, Scintilla::KeyMod modifiers);\n\tvoid ButtonUpWithModifiers(Point pt, unsigned int curTime, Scintilla::KeyMod modifiers);\n\n\tbool Idle();\n\tenum class TickReason { caret, scroll, widen, dwell, platform };\n\tvirtual void TickFor(TickReason reason);\n\tvirtual bool FineTickerRunning(TickReason reason);\n\tvirtual void FineTickerStart(TickReason reason, int millis, int tolerance);\n\tvirtual void FineTickerCancel(TickReason reason);\n\tvirtual bool SetIdle(bool) { return false; }\n\tvoid ChangeMouseCapture(bool on);\n\tvirtual void SetMouseCapture(bool on) = 0;\n\tvirtual bool HaveMouseCapture() = 0;\n\tvoid SetFocusState(bool focusState);\n\tvirtual void UpdateBaseElements();\n\n\tSci::Position PositionAfterArea(PRectangle rcArea) const;\n\tvoid StyleToPositionInView(Sci::Position pos);\n\tSci::Position PositionAfterMaxStyling(Sci::Position posMax, bool scrolling) const;\n\tvoid StartIdleStyling(bool truncatedLastStyling);\n\tvoid StyleAreaBounded(PRectangle rcArea, bool scrolling);\n\tconstexpr bool SynchronousStylingToVisible() const noexcept {\n\t\treturn (idleStyling == Scintilla::IdleStyling::None) || (idleStyling == Scintilla::IdleStyling::AfterVisible);\n\t}\n\tvoid IdleStyle();\n\tvirtual void IdleWork();\n\tvirtual void QueueIdleWork(WorkItems items, Sci::Position upTo=0);\n\n\tvirtual int SupportsFeature(Scintilla::Supports feature);\n\tvirtual bool PaintContains(PRectangle rc);\n\tbool PaintContainsMargin();\n\tvoid CheckForChangeOutsidePaint(Range r);\n\tvoid SetBraceHighlight(Sci::Position pos0, Sci::Position pos1, int matchStyle);\n\n\tvoid SetAnnotationHeights(Sci::Line start, Sci::Line end);\n\tvirtual void SetDocPointer(Document *document);\n\n\tvoid SetAnnotationVisible(Scintilla::AnnotationVisible visible);\n\tvoid SetEOLAnnotationVisible(Scintilla::EOLAnnotationVisible visible);\n\n\tSci::Line ExpandLine(Sci::Line line);\n\tvoid SetFoldExpanded(Sci::Line lineDoc, bool expanded);\n\tvoid FoldLine(Sci::Line line, Scintilla::FoldAction action);\n\tvoid FoldExpand(Sci::Line line, Scintilla::FoldAction action, Scintilla::FoldLevel level);\n\tSci::Line ContractedFoldNext(Sci::Line lineStart) const noexcept;\n\tvoid EnsureLineVisible(Sci::Line lineDoc, bool enforcePolicy);\n\tvoid FoldChanged(Sci::Line line, Scintilla::FoldLevel levelNow, Scintilla::FoldLevel levelPrev);\n\tvoid NeedShown(Sci::Position pos, Sci::Position len);\n\tvoid FoldAll(Scintilla::FoldAction action);\n\n\tSci::Position GetTag(char *tagValue, int tagNumber);\n\tenum class ReplaceType {basic, patterns, minimal};\n\tSci::Position ReplaceTarget(ReplaceType replaceType, std::string_view text);\n\n\tbool PositionIsHotspot(Sci::Position position) const noexcept;\n\tbool PointIsHotspot(Point pt);\n\tvoid SetHotSpotRange(const Point *pt);\n\tvoid SetHoverIndicatorPosition(Sci::Position position);\n\tvoid SetHoverIndicatorPoint(Point pt);\n\n\tint CodePage() const noexcept;\n\tvirtual bool ValidCodePage(int /* codePage */) const { return true; }\n\tvirtual std::string UTF8FromEncoded(std::string_view encoded) const = 0;\n\tvirtual std::string EncodedFromUTF8(std::string_view utf8) const = 0;\n\tvirtual std::unique_ptr<Surface> CreateMeasurementSurface() const;\n\tvirtual std::unique_ptr<Surface> CreateDrawingSurface(SurfaceID sid, std::optional<Scintilla::Technology> technologyOpt = {}) const;\n\n\tSci::Line WrapCount(Sci::Line line);\n\tvoid AddStyledText(const char *buffer, Sci::Position appendLength);\n\tSci::Position GetStyledText(char *buffer, Sci::Position cpMin, Sci::Position cpMax) const noexcept;\n\tSci::Position GetTextRange(char *buffer, Sci::Position cpMin, Sci::Position cpMax) const;\n\n\tvirtual Scintilla::sptr_t DefWndProc(Scintilla::Message iMessage, Scintilla::uptr_t wParam, Scintilla::sptr_t lParam) = 0;\n\tbool ValidMargin(Scintilla::uptr_t wParam) const noexcept;\n\tvoid StyleSetMessage(Scintilla::Message iMessage, Scintilla::uptr_t wParam, Scintilla::sptr_t lParam);\n\tScintilla::sptr_t StyleGetMessage(Scintilla::Message iMessage, Scintilla::uptr_t wParam, Scintilla::sptr_t lParam);\n\tvoid SetSelectionNMessage(Scintilla::Message iMessage, Scintilla::uptr_t wParam, Scintilla::sptr_t lParam);\n\tvoid SetSelectionMode(uptr_t wParam, bool setMoveExtends);\n\n\t// Coercion functions for transforming WndProc parameters into pointers\n\tstatic void *PtrFromSPtr(Scintilla::sptr_t lParam) noexcept {\n\t\treturn reinterpret_cast<void *>(lParam);\n\t}\n\tstatic const char *ConstCharPtrFromSPtr(Scintilla::sptr_t lParam) noexcept {\n\t\treturn static_cast<const char *>(PtrFromSPtr(lParam));\n\t}\n\tstatic const unsigned char *ConstUCharPtrFromSPtr(Scintilla::sptr_t lParam) noexcept {\n\t\treturn static_cast<const unsigned char *>(PtrFromSPtr(lParam));\n\t}\n\tstatic char *CharPtrFromSPtr(Scintilla::sptr_t lParam) noexcept {\n\t\treturn static_cast<char *>(PtrFromSPtr(lParam));\n\t}\n\tstatic unsigned char *UCharPtrFromSPtr(Scintilla::sptr_t lParam) noexcept {\n\t\treturn static_cast<unsigned char *>(PtrFromSPtr(lParam));\n\t}\n\tstatic std::string_view ViewFromParams(Scintilla::sptr_t lParam, Scintilla::uptr_t wParam) noexcept {\n\t\tif (SPtrFromUPtr(wParam) == -1) {\n\t\t\treturn std::string_view(CharPtrFromSPtr(lParam));\n\t\t}\n\t\treturn std::string_view(CharPtrFromSPtr(lParam), wParam);\n\t}\n\tstatic void *PtrFromUPtr(Scintilla::uptr_t wParam) noexcept {\n\t\treturn reinterpret_cast<void *>(wParam);\n\t}\n\tstatic const char *ConstCharPtrFromUPtr(Scintilla::uptr_t wParam) noexcept {\n\t\treturn static_cast<const char *>(PtrFromUPtr(wParam));\n\t}\n\n\tstatic constexpr Scintilla::sptr_t SPtrFromUPtr(Scintilla::uptr_t wParam) noexcept {\n\t\treturn static_cast<Scintilla::sptr_t>(wParam);\n\t}\n\tstatic constexpr Sci::Position PositionFromUPtr(Scintilla::uptr_t wParam) noexcept {\n\t\treturn SPtrFromUPtr(wParam);\n\t}\n\tstatic constexpr Sci::Line LineFromUPtr(Scintilla::uptr_t wParam) noexcept {\n\t\treturn SPtrFromUPtr(wParam);\n\t}\n\tPoint PointFromParameters(Scintilla::uptr_t wParam, Scintilla::sptr_t lParam) const noexcept {\n\t\treturn Point(static_cast<XYPOSITION>(wParam) - vs.ExternalMarginWidth(), static_cast<XYPOSITION>(lParam));\n\t}\n\n\tstatic constexpr std::optional<FoldLevel> OptionalFoldLevel(Scintilla::sptr_t lParam) {\n\t\tif (lParam >= 0) {\n\t\t\treturn static_cast<FoldLevel>(lParam);\n\t\t}\n\t\treturn std::nullopt;\n\t}\n\n\tstatic Scintilla::sptr_t StringResult(Scintilla::sptr_t lParam, const char *val) noexcept;\n\tstatic Scintilla::sptr_t BytesResult(Scintilla::sptr_t lParam, const unsigned char *val, size_t len) noexcept;\n\tstatic Scintilla::sptr_t BytesResult(Scintilla::sptr_t lParam, std::string_view sv) noexcept;\n\n\t// Set a variable controlling appearance to a value and invalidates the display\n\t// if a change was made. Avoids extra text and the possibility of mistyping.\n\ttemplate <typename T>\n\tbool SetAppearance(T &variable, T value) {\n\t\t// Using ! and == as more types have == defined than !=.\n\t\tconst bool changed = !(variable == value);\n\t\tif (changed) {\n\t\t\tvariable = value;\n\t\t\tInvalidateStyleRedraw();\n\t\t}\n\t\treturn changed;\n\t}\n\npublic:\n\t~Editor() override;\n\n\t// Public so the COM thunks can access it.\n\tbool IsUnicodeMode() const noexcept;\n\t// Public so scintilla_send_message can use it.\n\tvirtual Scintilla::sptr_t WndProc(Scintilla::Message iMessage, Scintilla::uptr_t wParam, Scintilla::sptr_t lParam);\n\t// Public so scintilla_set_id can use it.\n\tint ctrlID;\n\t// Public so COM methods for drag and drop can set it.\n\tScintilla::Status errorStatus;\n\tfriend class AutoSurface;\n};\n\n/**\n * A smart pointer class to ensure Surfaces are set up and deleted correctly.\n */\nclass AutoSurface {\nprivate:\n\tstd::unique_ptr<Surface> surf;\npublic:\n\tAutoSurface(const Editor *ed) :\n\t\tsurf(ed->CreateMeasurementSurface())  {\n\t}\n\tAutoSurface(SurfaceID sid, const Editor *ed, std::optional<Scintilla::Technology> technology = {}) :\n\t\tsurf(ed->CreateDrawingSurface(sid, technology)) {\n\t}\n\t// Deleted so AutoSurface objects can not be copied.\n\tAutoSurface(const AutoSurface &) = delete;\n\tAutoSurface(AutoSurface &&) = delete;\n\tvoid operator=(const AutoSurface &) = delete;\n\tvoid operator=(AutoSurface &&) = delete;\n\t~AutoSurface() {\n\t}\n\tSurface *operator->() const noexcept {\n\t\treturn surf.get();\n\t}\n\toperator Surface *() const noexcept {\n\t\treturn surf.get();\n\t}\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/src/ElapsedPeriod.h",
    "content": "// Scintilla source code edit control\n/** @file ElapsedPeriod.h\n ** Encapsulate C++ <chrono> to simplify use.\n **/\n// Copyright 2018 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#ifndef ELAPSEDPERIOD_H\n#define ELAPSEDPERIOD_H\n\nnamespace Scintilla::Internal {\n\n// Simplified access to high precision timing.\nclass ElapsedPeriod {\n\tusing ElapsedClock = std::chrono::steady_clock;\n\tElapsedClock::time_point tp;\npublic:\n\t/// Capture the moment\n\tElapsedPeriod() noexcept : tp(ElapsedClock::now()) {\n\t}\n\t/// Return duration as floating point seconds\n\tdouble Duration(bool reset=false) noexcept {\n\t\tconst ElapsedClock::time_point tpNow = ElapsedClock::now();\n\t\tconst std::chrono::duration<double> duration =\n\t\t\tstd::chrono::duration_cast<std::chrono::duration<double>>(tpNow - tp);\n\t\tif (reset) {\n\t\t\ttp = tpNow;\n\t\t}\n\t\treturn duration.count();\n\t}\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/src/Geometry.cxx",
    "content": "// Scintilla source code edit control\n/** @file Geometry.cxx\n ** Helper functions for geometric calculations.\n **/\n// Copyright 2020 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#include <cstdint>\n#include <cmath>\n\n#include <algorithm>\n\n#include \"Geometry.h\"\n\nnamespace {\n\nconstexpr unsigned int Mixed(unsigned char a, unsigned char b, double proportion) noexcept {\n\treturn static_cast<unsigned int>(a + proportion * (b - a));\n}\n\n}\n\nnamespace Scintilla::Internal {\n\nPRectangle Clamp(PRectangle rc, Edge edge, XYPOSITION position) noexcept {\n\tswitch (edge) {\n\tcase Edge::left:\n\t\treturn PRectangle(std::clamp(position, rc.left, rc.right), rc.top, rc.right, rc.bottom);\n\tcase Edge::top:\n\t\treturn PRectangle(rc.left, std::clamp(position, rc.top, rc.bottom), rc.right, rc.bottom);\n\tcase Edge::right:\n\t\treturn PRectangle(rc.left, rc.top, std::clamp(position, rc.left, rc.right), rc.bottom);\n\tcase Edge::bottom:\n\tdefault:\n\t\treturn PRectangle(rc.left, rc.top, rc.right, std::clamp(position, rc.top, rc.bottom));\n\t}\n}\n\nPRectangle Side(PRectangle rc, Edge edge, XYPOSITION size) noexcept {\n\tswitch (edge) {\n\tcase Edge::left:\n\t\treturn PRectangle(rc.left, rc.top, std::min(rc.left + size, rc.right), rc.bottom);\n\tcase Edge::top:\n\t\treturn PRectangle(rc.left, rc.top, rc.right, std::min(rc.top + size, rc.bottom));\n\tcase Edge::right:\n\t\treturn PRectangle(std::max(rc.left, rc.right - size), rc.top, rc.right, rc.bottom);\n\tcase Edge::bottom:\n\tdefault:\n\t\treturn PRectangle(rc.left, std::max(rc.top, rc.bottom - size), rc.right, rc.bottom);\n\t}\n}\n\nInterval Intersection(Interval a, Interval b) noexcept {\n\tconst XYPOSITION leftMax = std::max(a.left, b.left);\n\tconst XYPOSITION rightMin = std::min(a.right, b.right);\n\t// If the result would have a negative width. make empty instead.\n\tconst XYPOSITION rightResult = (rightMin >= leftMax) ? rightMin : leftMax;\n\treturn { leftMax, rightResult };\n}\n\nPRectangle Intersection(PRectangle rc, Interval horizontalBounds) noexcept {\n\tconst Interval intersection = Intersection(HorizontalBounds(rc), horizontalBounds);\n\treturn PRectangle(intersection.left, rc.top, intersection.right, rc.bottom);\n}\n\nInterval HorizontalBounds(PRectangle rc) noexcept {\n\treturn { rc.left, rc.right };\n}\n\nXYPOSITION PixelAlign(XYPOSITION xy, int pixelDivisions) noexcept {\n\treturn std::round(xy * pixelDivisions) / pixelDivisions;\n}\n\nXYPOSITION PixelAlignFloor(XYPOSITION xy, int pixelDivisions) noexcept {\n\treturn std::floor(xy * pixelDivisions) / pixelDivisions;\n}\n\nXYPOSITION PixelAlignCeil(XYPOSITION xy, int pixelDivisions) noexcept {\n\treturn std::ceil(xy * pixelDivisions) / pixelDivisions;\n}\n\nPoint PixelAlign(const Point &pt, int pixelDivisions) noexcept {\n\treturn Point(\n\t\tPixelAlign(pt.x, pixelDivisions),\n\t\tPixelAlign(pt.y, pixelDivisions));\n}\n\nPRectangle PixelAlign(const PRectangle &rc, int pixelDivisions) noexcept {\n\t// Move left and right side to nearest pixel to avoid blurry visuals.\n\t// The top and bottom should be integers but floor them to make sure.\n\t// `pixelDivisions` is commonly 1 except for 'retina' displays where it is 2.\n\t// On retina displays, the positions should be moved to the nearest device\n\t// pixel which is the nearest half logical pixel.\n\treturn PRectangle(\n\t\tPixelAlign(rc.left, pixelDivisions),\n\t\tPixelAlignFloor(rc.top, pixelDivisions),\n\t\tPixelAlign(rc.right, pixelDivisions),\n\t\tPixelAlignFloor(rc.bottom, pixelDivisions));\n}\n\nPRectangle PixelAlignOutside(const PRectangle &rc, int pixelDivisions) noexcept {\n\t// Move left and right side to extremes (floor(left) ceil(right)) to avoid blurry visuals.\n\treturn PRectangle(\n\t\tPixelAlignFloor(rc.left, pixelDivisions),\n\t\tPixelAlignFloor(rc.top, pixelDivisions),\n\t\tPixelAlignCeil(rc.right, pixelDivisions),\n\t\tPixelAlignFloor(rc.bottom, pixelDivisions));\n}\n\nColourRGBA ColourRGBA::MixedWith(ColourRGBA other) const noexcept {\n\tconst unsigned int red = (GetRed() + other.GetRed()) / 2;\n\tconst unsigned int green = (GetGreen() + other.GetGreen()) / 2;\n\tconst unsigned int blue = (GetBlue() + other.GetBlue()) / 2;\n\tconst unsigned int alpha = (GetAlpha() + other.GetAlpha()) / 2;\n\treturn ColourRGBA(red, green, blue, alpha);\n}\n\nColourRGBA ColourRGBA::MixedWith(ColourRGBA other, double proportion) const noexcept {\n\treturn ColourRGBA(\n\t\tMixed(GetRed(), other.GetRed(), proportion),\n\t\tMixed(GetGreen(), other.GetGreen(), proportion),\n\t\tMixed(GetBlue(), other.GetBlue(), proportion),\n\t\tMixed(GetAlpha(), other.GetAlpha(), proportion));\n}\n\n}\n"
  },
  {
    "path": "Libraries/scintilla/src/Geometry.h",
    "content": "// Scintilla source code edit control\n/** @file Geometry.h\n ** Classes and functions for geometric and colour calculations.\n **/\n// Copyright 2020 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#ifndef GEOMETRY_H\n#define GEOMETRY_H\n\nnamespace Scintilla::Internal {\n\ntypedef double XYPOSITION;\ntypedef double XYACCUMULATOR;\n\n/**\n * A geometric point class.\n * Point is similar to the Win32 POINT and GTK+ GdkPoint types.\n */\nclass Point {\npublic:\n\tXYPOSITION x;\n\tXYPOSITION y;\n\n\tconstexpr explicit Point(XYPOSITION x_=0, XYPOSITION y_=0) noexcept : x(x_), y(y_) {\n\t}\n\n\tstatic constexpr Point FromInts(int x_, int y_) noexcept {\n\t\treturn Point(static_cast<XYPOSITION>(x_), static_cast<XYPOSITION>(y_));\n\t}\n\n\tconstexpr bool operator==(Point other) const noexcept {\n\t\treturn (x == other.x) && (y == other.y);\n\t}\n\n\tconstexpr bool operator!=(Point other) const noexcept {\n\t\treturn (x != other.x) || (y != other.y);\n\t}\n\n\tconstexpr Point operator+(Point other) const noexcept {\n\t\treturn Point(x + other.x, y + other.y);\n\t}\n\n\tconstexpr Point operator-(Point other) const noexcept {\n\t\treturn Point(x - other.x, y - other.y);\n\t}\n\n\t// Other automatically defined methods (assignment, copy constructor, destructor) are fine\n};\n\n\n/**\n * A geometric interval class.\n */\nclass Interval {\npublic:\n\tXYPOSITION left;\n\tXYPOSITION right;\n\tconstexpr bool operator==(const Interval &other) const noexcept {\n\t\treturn (left == other.left) && (right == other.right);\n\t}\n\tconstexpr XYPOSITION Width() const noexcept { return right - left; }\n\tconstexpr bool Empty() const noexcept {\n\t\treturn Width() <= 0;\n\t}\n\tconstexpr bool Intersects(Interval other) const noexcept {\n\t\treturn (right > other.left) && (left < other.right);\n\t}\n\tconstexpr Interval Offset(XYPOSITION offset) const noexcept {\n\t\treturn {left + offset, right + offset};\n\t}\n};\n\n/**\n * A geometric rectangle class.\n * PRectangle is similar to Win32 RECT.\n * PRectangles contain their top and left sides, but not their right and bottom sides.\n */\nclass PRectangle {\npublic:\n\tXYPOSITION left;\n\tXYPOSITION top;\n\tXYPOSITION right;\n\tXYPOSITION bottom;\n\n\tconstexpr explicit PRectangle(XYPOSITION left_=0, XYPOSITION top_=0, XYPOSITION right_=0, XYPOSITION bottom_ = 0) noexcept :\n\t\tleft(left_), top(top_), right(right_), bottom(bottom_) {\n\t}\n\n\tstatic constexpr PRectangle FromInts(int left_, int top_, int right_, int bottom_) noexcept {\n\t\treturn PRectangle(static_cast<XYPOSITION>(left_), static_cast<XYPOSITION>(top_),\n\t\t\tstatic_cast<XYPOSITION>(right_), static_cast<XYPOSITION>(bottom_));\n\t}\n\n\t// Other automatically defined methods (assignment, copy constructor, destructor) are fine\n\n\tconstexpr bool operator==(const PRectangle &rc) const noexcept {\n\t\treturn (rc.left == left) && (rc.right == right) &&\n\t\t\t(rc.top == top) && (rc.bottom == bottom);\n\t}\n\tconstexpr bool Contains(Point pt) const noexcept {\n\t\treturn (pt.x >= left) && (pt.x <= right) &&\n\t\t\t(pt.y >= top) && (pt.y <= bottom);\n\t}\n\tconstexpr bool ContainsWholePixel(Point pt) const noexcept {\n\t\t// Does the rectangle contain all of the pixel to left/below the point\n\t\treturn (pt.x >= left) && ((pt.x+1) <= right) &&\n\t\t\t(pt.y >= top) && ((pt.y+1) <= bottom);\n\t}\n\tconstexpr bool Contains(PRectangle rc) const noexcept {\n\t\treturn (rc.left >= left) && (rc.right <= right) &&\n\t\t\t(rc.top >= top) && (rc.bottom <= bottom);\n\t}\n\tconstexpr bool Intersects(PRectangle other) const noexcept {\n\t\treturn (right > other.left) && (left < other.right) &&\n\t\t\t(bottom > other.top) && (top < other.bottom);\n\t}\n\tconstexpr bool Intersects(Interval horizontalBounds) const noexcept {\n\t\treturn (right > horizontalBounds.left) && (left < horizontalBounds.right);\n\t}\n\n\tvoid Move(XYPOSITION xDelta, XYPOSITION yDelta) noexcept {\n\t\tleft += xDelta;\n\t\ttop += yDelta;\n\t\tright += xDelta;\n\t\tbottom += yDelta;\n\t}\n\n\tPRectangle WithHorizontalBounds(Interval horizontal) const noexcept {\n\t\treturn PRectangle(horizontal.left, top, horizontal.right, bottom);\n\t}\n\n\tconstexpr PRectangle Inset(XYPOSITION delta) const noexcept {\n\t\treturn PRectangle(left + delta, top + delta, right - delta, bottom - delta);\n\t}\n\n\tconstexpr PRectangle Inset(Point delta) const noexcept {\n\t\treturn PRectangle(left + delta.x, top + delta.y, right - delta.x, bottom - delta.y);\n\t}\n\n\tconstexpr Point Centre() const noexcept {\n\t\treturn Point((left + right) / 2, (top + bottom) / 2);\n\t}\n\n\tconstexpr XYPOSITION Width() const noexcept { return right - left; }\n\tconstexpr XYPOSITION Height() const noexcept { return bottom - top; }\n\tconstexpr bool Empty() const noexcept {\n\t\treturn (Height() <= 0) || (Width() <= 0);\n\t}\n};\n\nenum class Edge { left, top, bottom, right };\n\nPRectangle Clamp(PRectangle rc, Edge edge, XYPOSITION position) noexcept;\nPRectangle Side(PRectangle rc, Edge edge, XYPOSITION size) noexcept;\n\nInterval Intersection(Interval a, Interval b) noexcept;\nPRectangle Intersection(PRectangle rc, Interval horizontalBounds) noexcept;\nInterval HorizontalBounds(PRectangle rc) noexcept;\n\nXYPOSITION PixelAlign(XYPOSITION xy, int pixelDivisions) noexcept;\nXYPOSITION PixelAlignFloor(XYPOSITION xy, int pixelDivisions) noexcept;\nXYPOSITION PixelAlignCeil(XYPOSITION xy, int pixelDivisions) noexcept;\n\nPoint PixelAlign(const Point &pt, int pixelDivisions) noexcept;\n\nPRectangle PixelAlign(const PRectangle &rc, int pixelDivisions) noexcept;\nPRectangle PixelAlignOutside(const PRectangle &rc, int pixelDivisions) noexcept;\n\n/**\n* Holds an RGBA colour with 8 bits for each component.\n*/\nconstexpr float componentMaximum = 255.0F;\nconstexpr unsigned int maximumByte = 0xffU;\nclass ColourRGBA {\n\tstatic constexpr float ComponentAsFloat(unsigned char component) {\n\t\treturn component / componentMaximum;\n\t}\n\tstatic constexpr int rgbMask = 0xffffff;\n\tint co;\npublic:\n\tconstexpr explicit ColourRGBA(int co_ = 0) noexcept : co(co_) {\n\t}\n\n\tconstexpr ColourRGBA(unsigned int red, unsigned int green, unsigned int blue, unsigned int alpha=maximumByte) noexcept :\n\t\tColourRGBA(red | (green << 8) | (blue << 16) | (alpha << 24)) {\n\t}\n\n\tconstexpr ColourRGBA(ColourRGBA cd, unsigned int alpha) noexcept :\n\t\tColourRGBA(cd.OpaqueRGB() | (alpha << 24)) {\n\t}\n\n\tstatic constexpr ColourRGBA FromRGB(int co_) noexcept {\n\t\treturn ColourRGBA(co_ | (maximumByte << 24));\n\t}\n\n\tstatic constexpr ColourRGBA Grey(unsigned int grey, unsigned int alpha=maximumByte) noexcept {\n\t\treturn ColourRGBA(grey, grey, grey, alpha);\n\t}\n\n\tstatic constexpr ColourRGBA FromIpRGB(intptr_t co_) noexcept {\n\t\tconst int rgb = co_ & rgbMask;\n\t\treturn ColourRGBA(rgb | (maximumByte << 24));\n\t}\n\n\tconstexpr ColourRGBA WithoutAlpha() const noexcept {\n\t\treturn ColourRGBA(co & rgbMask);\n\t}\n\n\tconstexpr ColourRGBA Opaque() const noexcept {\n\t\treturn ColourRGBA(co | (maximumByte << 24));\n\t}\n\n\tconstexpr int AsInteger() const noexcept {\n\t\treturn co;\n\t}\n\n\tconstexpr int OpaqueRGB() const noexcept {\n\t\treturn co & rgbMask;\n\t}\n\n\t// Red, green and blue values as bytes 0..255\n\tconstexpr unsigned char GetRed() const noexcept {\n\t\treturn co & maximumByte;\n\t}\n\tconstexpr unsigned char GetGreen() const noexcept {\n\t\treturn (co >> 8) & maximumByte;\n\t}\n\tconstexpr unsigned char GetBlue() const noexcept {\n\t\treturn (co >> 16) & maximumByte;\n\t}\n\tconstexpr unsigned char GetAlpha() const noexcept {\n\t\t// Use a temporary here to prevent a 'Wconversion' warning from GCC\n\t\tconst int shifted = co >> 24;\n\t\treturn shifted & maximumByte;\n\t}\n\n\t// Red, green, blue, and alpha values as float 0..1.0\n\tconstexpr float GetRedComponent() const noexcept {\n\t\treturn ComponentAsFloat(GetRed());\n\t}\n\tconstexpr float GetGreenComponent() const noexcept {\n\t\treturn ComponentAsFloat(GetGreen());\n\t}\n\tconstexpr float GetBlueComponent() const noexcept {\n\t\treturn ComponentAsFloat(GetBlue());\n\t}\n\tconstexpr float GetAlphaComponent() const noexcept {\n\t\treturn ComponentAsFloat(GetAlpha());\n\t}\n\n\tconstexpr bool operator==(const ColourRGBA &other) const noexcept {\n\t\treturn co == other.co;\n\t}\n\n\tconstexpr bool IsOpaque() const noexcept {\n\t\treturn GetAlpha() == maximumByte;\n\t}\n\n\tColourRGBA MixedWith(ColourRGBA other) const noexcept;\n\tColourRGBA MixedWith(ColourRGBA other, double proportion) const noexcept;\n};\n\nconstexpr ColourRGBA white(maximumByte, maximumByte, maximumByte);\nconstexpr ColourRGBA black(0x0, 0x0, 0x0);\n\n/**\n* Holds an RGBA colour and stroke width to stroke a shape.\n*/\nclass Stroke {\npublic:\n\tColourRGBA colour;\n\tXYPOSITION width;\n\tconstexpr Stroke(ColourRGBA colour_, XYPOSITION width_=1.0) noexcept :\n\t\tcolour(colour_), width(width_) {\n\t}\n\tconstexpr float WidthF() const noexcept {\n\t\treturn static_cast<float>(width);\n\t}\n};\n\n/**\n* Holds an RGBA colour to fill a shape.\n*/\nclass Fill {\npublic:\n\tColourRGBA colour;\n\tconstexpr Fill(ColourRGBA colour_) noexcept :\n\t\tcolour(colour_) {\n\t}\n};\n\n/**\n* Holds a pair of RGBA colours and stroke width to fill and stroke a shape.\n*/\nclass FillStroke {\npublic:\n\tFill fill;\n\tStroke stroke;\n\tconstexpr FillStroke(ColourRGBA colourFill_, ColourRGBA colourStroke_, XYPOSITION widthStroke_=1.0) noexcept :\n\t\tfill(colourFill_), stroke(colourStroke_, widthStroke_) {\n\t}\n\tconstexpr FillStroke(ColourRGBA colourBoth, XYPOSITION widthStroke_=1.0) noexcept :\n\t\tfill(colourBoth), stroke(colourBoth, widthStroke_) {\n\t}\n};\n\n/**\n* Holds an element of a gradient with an RGBA colour and a relative position.\n*/\nclass ColourStop {\npublic:\n\tXYPOSITION position;\n\tColourRGBA colour;\n\tconstexpr ColourStop(XYPOSITION position_, ColourRGBA colour_) noexcept :\n\t\tposition(position_), colour(colour_) {\n\t}\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/src/Indicator.cxx",
    "content": "// Scintilla source code edit control\n/** @file Indicator.cxx\n ** Defines the style of indicators which are text decorations such as underlining.\n **/\n// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#include <cstdint>\n#include <cmath>\n\n#include <stdexcept>\n#include <string_view>\n#include <vector>\n#include <map>\n#include <optional>\n#include <algorithm>\n#include <memory>\n\n#include \"ScintillaTypes.h\"\n\n#include \"Debugging.h\"\n#include \"Geometry.h\"\n#include \"Platform.h\"\n\n#include \"Indicator.h\"\n#include \"XPM.h\"\n\nusing namespace Scintilla;\nusing namespace Scintilla::Internal;\n\nvoid Indicator::Draw(Surface *surface, const PRectangle &rc, const PRectangle &rcLine, const PRectangle &rcCharacter, State state, int value) const {\n\tStyleAndColour sacDraw = sacNormal;\n\tif (FlagSet(Flags(), IndicFlag::ValueFore)) {\n\t\tsacDraw.fore = ColourRGBA::FromRGB(value & static_cast<int>(IndicValue::Mask));\n\t}\n\tif (state == State::hover) {\n\t\tsacDraw = sacHover;\n\t}\n\n\tconst int pixelDivisions = surface->PixelDivisions();\n\n\tconst XYPOSITION halfWidth = strokeWidth / 2.0f;\n\n\tconst PRectangle rcAligned(PixelAlignOutside(rc, pixelDivisions));\n\tPRectangle rcFullHeightAligned = PixelAlignOutside(rcLine, pixelDivisions);\n\trcFullHeightAligned.left = rcAligned.left;\n\trcFullHeightAligned.right = rcAligned.right;\n\n\tconst XYPOSITION ymid = PixelAlign(rc.Centre().y, pixelDivisions);\n\n\t// This is a reasonable clip for indicators beneath text like underlines\n\tPRectangle rcClip = rcAligned;\n\trcClip.bottom = rcFullHeightAligned.bottom;\n\n\tswitch (sacDraw.style) {\n\tcase IndicatorStyle::Squiggle: {\n\t\t\tsurface->SetClip(rcClip);\n\t\t\tXYPOSITION x = rcAligned.left + halfWidth;\n\t\t\tconst XYPOSITION top = rcAligned.top + halfWidth;\n\t\t\tconst XYPOSITION xLast = rcAligned.right + halfWidth;\n\t\t\tXYPOSITION y = 0;\n\t\t\tstd::vector<Point> pts;\n\t\t\tconst XYPOSITION pitch = 1 + strokeWidth;\n\t\t\tpts.emplace_back(x, top + y);\n\t\t\twhile (x < xLast) {\n\t\t\t\tx += pitch;\n\t\t\t\ty = pitch - y;\n\t\t\t\tpts.emplace_back(x, top + y);\n\t\t\t\t}\n\t\t\tsurface->PolyLine(pts.data(), std::size(pts), Stroke(sacDraw.fore, strokeWidth));\n\t\t\tsurface->PopClip();\n\t\t}\n\t\tbreak;\n\n\tcase IndicatorStyle::SquigglePixmap: {\n\t\t\tconst PRectangle rcSquiggle = PixelAlign(rc, 1);\n\n\t\t\tconst int width = std::min(4000, static_cast<int>(rcSquiggle.Width()));\n\t\t\tRGBAImage image(width, 3, 1.0, nullptr);\n\t\t\tconstexpr unsigned int alphaFull = 0xff;\n\t\t\tconstexpr unsigned int alphaSide = 0x2f;\n\t\t\tconstexpr unsigned int alphaSide2 = 0x5f;\n\t\t\tfor (int x = 0; x < width; x++) {\n\t\t\t\tif (x%2) {\n\t\t\t\t\t// Two halfway columns have a full pixel in middle flanked by light pixels\n\t\t\t\t\timage.SetPixel(x, 0, ColourRGBA(sacDraw.fore, alphaSide));\n\t\t\t\t\timage.SetPixel(x, 1, ColourRGBA(sacDraw.fore, alphaFull));\n\t\t\t\t\timage.SetPixel(x, 2, ColourRGBA(sacDraw.fore, alphaSide));\n\t\t\t\t} else {\n\t\t\t\t\t// Extreme columns have a full pixel at bottom or top and a mid-tone pixel in centre\n\t\t\t\t\timage.SetPixel(x, (x % 4) ? 0 : 2, ColourRGBA(sacDraw.fore, alphaFull));\n\t\t\t\t\timage.SetPixel(x, 1, ColourRGBA(sacDraw.fore, alphaSide2));\n\t\t\t\t}\n\t\t\t}\n\t\t\tsurface->DrawRGBAImage(rcSquiggle, image.GetWidth(), image.GetHeight(), image.Pixels());\n\t\t}\n\t\tbreak;\n\n\tcase IndicatorStyle::SquiggleLow: {\n\t\t\tstd::vector<Point> pts;\n\t\t\tconst XYPOSITION top = rcAligned.top + halfWidth;\n\t\t\tint y = 0;\n\t\t\tXYPOSITION x = std::round(rcAligned.left) + halfWidth;\n\t\t\tpts.emplace_back(x, top + y);\n\t\t\tconst XYPOSITION pitch = 2 + strokeWidth;\n\t\t\tx += pitch;\n\t\t\twhile (x < rcAligned.right) {\n\t\t\t\tpts.emplace_back(x - 1, top + y);\n\t\t\t\ty = 1 - y;\n\t\t\t\tpts.emplace_back(x, top + y);\n\t\t\t\tx += pitch;\n\t\t\t}\n\t\t\tpts.emplace_back(rcAligned.right, top + y);\n\t\t\tsurface->PolyLine(pts.data(), std::size(pts), Stroke(sacDraw.fore, strokeWidth));\n\t\t}\n\t\tbreak;\n\n\tcase IndicatorStyle::TT: {\n\t\t\tsurface->SetClip(rcClip);\n\t\t\tconst XYPOSITION yLine = ymid;\n\t\t\tXYPOSITION x = rcAligned.left + 5.0f;\n\t\t\tconst XYPOSITION pitch = 4 + strokeWidth;\n\t\t\twhile (x < rc.right + pitch) {\n\t\t\t\tconst PRectangle line(x-pitch, yLine, x, yLine + strokeWidth);\n\t\t\t\tsurface->FillRectangle(line, sacDraw.fore);\n\t\t\t\tconst PRectangle tail(x - 2 - strokeWidth, yLine + strokeWidth, x - 2, yLine + strokeWidth * 2);\n\t\t\t\tsurface->FillRectangle(tail, sacDraw.fore);\n\t\t\t\tx++;\n\t\t\t\tx += pitch;\n\t\t\t}\n\t\t\tsurface->PopClip();\n\t\t}\n\t\tbreak;\n\n\tcase IndicatorStyle::Diagonal: {\n\t\t\tsurface->SetClip(rcClip);\n\t\t\tXYPOSITION x = rcAligned.left + halfWidth;\n\t\t\tconst XYPOSITION top = rcAligned.top + halfWidth;\n\t\t\tconst XYPOSITION pitch = 3 + strokeWidth;\n\t\t\twhile (x < rc.right) {\n\t\t\t\tconst XYPOSITION endX = x+3;\n\t\t\t\tconst XYPOSITION endY = top - 1;\n\t\t\t\tsurface->LineDraw(Point(x, top + 2), Point(endX, endY), Stroke(sacDraw.fore, strokeWidth));\n\t\t\t\tx += pitch;\n\t\t\t}\n\t\t\tsurface->PopClip();\n\t\t}\n\t\tbreak;\n\n\tcase IndicatorStyle::Strike: {\n\t\t\tconst XYPOSITION yStrike = std::round(rcLine.Centre().y);\n\t\t\tconst PRectangle rcStrike(\n\t\t\t\trcAligned.left, yStrike, rcAligned.right, yStrike + strokeWidth);\n\t\t\tsurface->FillRectangle(rcStrike, sacDraw.fore);\n\t\t}\n\t\tbreak;\n\n\tcase IndicatorStyle::Hidden:\n\tcase IndicatorStyle::TextFore:\n\t\t// Draw nothing\n\t\tbreak;\n\n\tcase IndicatorStyle::Box: {\n\t\t\tPRectangle rcBox = rcFullHeightAligned;\n\t\t\trcBox.top = rcBox.top + 1.0f;\n\t\t\trcBox.bottom = ymid + 1.0f;\n\t\t\tsurface->RectangleFrame(rcBox, Stroke(ColourRGBA(sacDraw.fore, outlineAlpha), strokeWidth));\n\t\t}\n\t\tbreak;\n\n\tcase IndicatorStyle::RoundBox:\n\tcase IndicatorStyle::StraightBox:\n\tcase IndicatorStyle::FullBox: {\n\t\t\tPRectangle rcBox = rcFullHeightAligned;\n\t\t\tif (sacDraw.style != IndicatorStyle::FullBox)\n\t\t\t\trcBox.top = rcBox.top + 1;\n\t\t\tsurface->AlphaRectangle(rcBox, (sacDraw.style == IndicatorStyle::RoundBox) ? 1.0f : 0.0f,\n\t\t\t\t\t\tFillStroke(ColourRGBA(sacDraw.fore, fillAlpha), ColourRGBA(sacDraw.fore, outlineAlpha), strokeWidth));\n\t\t}\n\t\tbreak;\n\n\tcase IndicatorStyle::Gradient:\n\tcase IndicatorStyle::GradientCentre: {\n\t\t\tPRectangle rcBox = rcFullHeightAligned;\n\t\t\trcBox.top = rcBox.top + 1;\n\t\t\tconst Surface::GradientOptions options = Surface::GradientOptions::topToBottom;\n\t\t\tconst ColourRGBA start(sacDraw.fore, fillAlpha);\n\t\t\tconst ColourRGBA end(sacDraw.fore, 0);\n\t\t\tstd::vector<ColourStop> stops;\n\t\t\tswitch (sacDraw.style) {\n\t\t\tcase IndicatorStyle::Gradient:\n\t\t\t\tstops.push_back(ColourStop(0.0, start));\n\t\t\t\tstops.push_back(ColourStop(1.0, end));\n\t\t\t\tbreak;\n\t\t\tcase IndicatorStyle::GradientCentre:\n\t\t\t\tstops.push_back(ColourStop(0.0, end));\n\t\t\t\tstops.push_back(ColourStop(0.5, start));\n\t\t\t\tstops.push_back(ColourStop(1.0, end));\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tsurface->GradientRectangle(rcBox, stops, options);\n\t\t}\n\t\tbreak;\n\n\tcase IndicatorStyle::DotBox: {\n\t\t\tPRectangle rcBox = rcFullHeightAligned;\n\t\t\trcBox.top = rcBox.top + 1;\n\t\t\t// Cap width at 4000 to avoid large allocations when mistakes made\n\t\t\tconst int width = std::min(static_cast<int>(rcBox.Width()), 4000);\n\t\t\tconst int height = static_cast<int>(rcBox.Height());\n\t\t\tRGBAImage image(width, height, 1.0, nullptr);\n\t\t\t// Draw horizontal lines top and bottom\n\t\t\tfor (int x=0; x<width; x++) {\n\t\t\t\tfor (int y = 0; y< height; y += height - 1) {\n\t\t\t\t\timage.SetPixel(x, y, ColourRGBA(sacDraw.fore, ((x + y) % 2) ? outlineAlpha : fillAlpha));\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Draw vertical lines left and right\n\t\t\tfor (int y = 1; y<height; y++) {\n\t\t\t\tfor (int x=0; x<width; x += width-1) {\n\t\t\t\t\timage.SetPixel(x, y, ColourRGBA(sacDraw.fore, ((x + y) % 2) ? outlineAlpha : fillAlpha));\n\t\t\t\t}\n\t\t\t}\n\t\t\tsurface->DrawRGBAImage(rcBox, image.GetWidth(), image.GetHeight(), image.Pixels());\n\t\t}\n\t\tbreak;\n\n\tcase IndicatorStyle::Dash: {\n\t\t\tXYPOSITION x = std::floor(rc.left);\n\t\t\tconst XYPOSITION widthDash = 3 + std::round(strokeWidth);\n\t\t\twhile (x < rc.right) {\n\t\t\t\tconst PRectangle rcDash = PRectangle(x, ymid,\n\t\t\t\t\tx + widthDash, ymid + std::round(strokeWidth));\n\t\t\t\tsurface->FillRectangle(rcDash, sacDraw.fore);\n\t\t\t\tx += 3 + widthDash;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\n\tcase IndicatorStyle::Dots: {\n\t\t\tconst XYPOSITION widthDot = std::round(strokeWidth);\n\t\t\tXYPOSITION x = std::floor(rc.left);\n\t\t\twhile (x < rc.right) {\n\t\t\t\tconst PRectangle rcDot = PRectangle(x, ymid,\n\t\t\t\t\tx + widthDot, ymid + widthDot);\n\t\t\t\tsurface->FillRectangle(rcDot, sacDraw.fore);\n\t\t\t\tx += widthDot * 2;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\n\tcase IndicatorStyle::CompositionThick: {\n\t\t\tconst PRectangle rcComposition(rc.left+1, rcLine.bottom-2, rc.right-1, rcLine.bottom);\n\t\t\tsurface->FillRectangle(rcComposition, ColourRGBA(sacDraw.fore, outlineAlpha));\n\t\t}\n\t\tbreak;\n\n\tcase IndicatorStyle::CompositionThin: {\n\t\t\tconst PRectangle rcComposition(rc.left+1, rcLine.bottom-2, rc.right-1, rcLine.bottom-1);\n\t\t\tsurface->FillRectangle(rcComposition, sacDraw.fore);\n\t\t}\n\t\tbreak;\n\n\tcase IndicatorStyle::Point:\n\tcase IndicatorStyle::PointCharacter:\n\t\tif (rcCharacter.Width() >= 0.1) {\n\t\t\tconst XYPOSITION pixelHeight = std::floor(rc.Height());\t// 1 pixel onto next line if multiphase\n\t\t\tconst XYPOSITION x = (sacDraw.style == IndicatorStyle::Point) ? (rcCharacter.left) : ((rcCharacter.right + rcCharacter.left) / 2);\n\t\t\t// 0.5f is to hit midpoint of pixels:\n\t\t\tconst XYPOSITION ix = std::round(x) + 0.5f;\n\t\t\tconst XYPOSITION iy = std::ceil(rc.bottom) + 0.5f;\n\t\t\tconst Point pts[] = {\n\t\t\t\tPoint(ix - pixelHeight, iy),\t// Left\n\t\t\t\tPoint(ix + pixelHeight, iy),\t// Right\n\t\t\t\tPoint(ix, iy - pixelHeight)\t\t\t\t\t\t\t\t// Top\n\t\t\t};\n\t\t\tsurface->Polygon(pts, std::size(pts), FillStroke(sacDraw.fore));\n\t\t}\n\t\tbreak;\n\n\tcase IndicatorStyle::PointTop:\n\t\tif (rcCharacter.Width() >= 0.1) {\n\t\t\tconst XYPOSITION pixelHeight = std::floor(rc.Height());\t// 1 pixel onto previous line if multiphase\n\t\t\tconst XYPOSITION x = rcCharacter.left;\n\t\t\t// 0.5f is to hit midpoint of pixels:\n\t\t\tconst XYPOSITION ix = std::round(x) + 0.5f;\n\t\t\tconst XYPOSITION iy = std::floor(rcLine.top) - 0.5f;\n\t\t\tconst Point pts[] = {\n\t\t\t\tPoint(ix - pixelHeight, iy),\t// Left\n\t\t\t\tPoint(ix + pixelHeight, iy),\t// Right\n\t\t\t\tPoint(ix, iy + pixelHeight)\t\t// Bottom\n\t\t\t};\n\t\t\tsurface->Polygon(pts, std::size(pts), FillStroke(sacDraw.fore));\n\t\t}\n\t\tbreak;\n\n\tdefault:\n\t\t// Either IndicatorStyle::Plain or unknown\n\t\tsurface->FillRectangle(PRectangle(rcAligned.left, ymid,\n\t\t\trcAligned.right, ymid + std::round(strokeWidth)), sacDraw.fore);\n\t}\n}\n\nvoid Indicator::SetFlags(IndicFlag attributes_) noexcept {\n\tattributes = attributes_;\n}\n"
  },
  {
    "path": "Libraries/scintilla/src/Indicator.h",
    "content": "// Scintilla source code edit control\n/** @file Indicator.h\n ** Defines the style of indicators which are text decorations such as underlining.\n **/\n// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#ifndef INDICATOR_H\n#define INDICATOR_H\n\nnamespace Scintilla::Internal {\n\nstruct StyleAndColour {\n\tScintilla::IndicatorStyle style;\n\tColourRGBA fore;\n\tStyleAndColour() noexcept : style(Scintilla::IndicatorStyle::Plain), fore(black) {\n\t}\n\tStyleAndColour(Scintilla::IndicatorStyle style_, ColourRGBA fore_ = black) noexcept : style(style_), fore(fore_) {\n\t}\n\tbool operator==(const StyleAndColour &other) const noexcept {\n\t\treturn (style == other.style) && (fore == other.fore);\n\t}\n};\n\n/**\n */\nclass Indicator {\npublic:\n\tenum class State { normal, hover };\n\tStyleAndColour sacNormal;\n\tStyleAndColour sacHover;\n\tbool under;\n\tint fillAlpha;\n\tint outlineAlpha;\n\tScintilla::IndicFlag attributes;\n\tXYPOSITION strokeWidth = 1.0f;\n\tIndicator() noexcept : under(false), fillAlpha(30), outlineAlpha(50), attributes(Scintilla::IndicFlag::None) {\n\t}\n\tIndicator(Scintilla::IndicatorStyle style_, ColourRGBA fore_= black, bool under_=false, int fillAlpha_=30, int outlineAlpha_=50) noexcept :\n\t\tsacNormal(style_, fore_), sacHover(style_, fore_), under(under_), fillAlpha(fillAlpha_), outlineAlpha(outlineAlpha_), attributes(Scintilla::IndicFlag::None) {\n\t}\n\tvoid Draw(Surface *surface, const PRectangle &rc, const PRectangle &rcLine, const PRectangle &rcCharacter, State drawState, int value) const;\n\tbool IsDynamic() const noexcept {\n\t\treturn !(sacNormal == sacHover);\n\t}\n\tbool OverridesTextFore() const noexcept {\n\t\treturn sacNormal.style == Scintilla::IndicatorStyle::TextFore || sacHover.style == Scintilla::IndicatorStyle::TextFore;\n\t}\n\tScintilla::IndicFlag Flags() const noexcept {\n\t\treturn attributes;\n\t}\n\tvoid SetFlags(Scintilla::IndicFlag attributes_) noexcept;\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/src/KeyMap.cxx",
    "content": "// Scintilla source code edit control\n/** @file KeyMap.cxx\n ** Defines a mapping between keystrokes and commands.\n **/\n// Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#include <cstdlib>\n#include <cstdint>\n\n#include <stdexcept>\n#include <string_view>\n#include <vector>\n#include <map>\n#include <optional>\n#include <memory>\n\n#include \"ScintillaTypes.h\"\n#include \"ScintillaMessages.h\"\n\n#include \"Debugging.h\"\n#include \"Geometry.h\"\n#include \"Platform.h\"\n\n#include \"KeyMap.h\"\n\nusing namespace Scintilla;\nusing namespace Scintilla::Internal;\n\nKeyMap::KeyMap() {\n\tfor (int i = 0; static_cast<int>(MapDefault[i].key); i++) {\n\t\tAssignCmdKey(MapDefault[i].key,\n\t\t\tMapDefault[i].modifiers,\n\t\t\tMapDefault[i].msg);\n\t}\n}\n\nvoid KeyMap::Clear() noexcept {\n\tkmap.clear();\n}\n\nvoid KeyMap::AssignCmdKey(Keys key, KeyMod modifiers, Message msg) {\n\tkmap[KeyModifiers(key, modifiers)] = msg;\n}\n\nMessage KeyMap::Find(Keys key, KeyMod modifiers) const {\n\tstd::map<KeyModifiers, Message>::const_iterator it = kmap.find(KeyModifiers(key, modifiers));\n\treturn (it == kmap.end()) ? static_cast<Message>(0) : it->second;\n}\n\nconst std::map<KeyModifiers, Message> &KeyMap::GetKeyMap() const noexcept {\n\treturn kmap;\n}\n\n#if PLAT_GTK_MACOSX\n#define OS_X_KEYS 1\n#else\n#define OS_X_KEYS 0\n#endif\n\n// Define a modifier that is exactly Ctrl key on all platforms\n// Most uses of Ctrl map to Cmd on macOS but some can't so use SCI_[S]CTRL_META\n#if OS_X_KEYS\n#define SCI_CTRL_META SCI_META\n#define SCI_SCTRL_META (SCI_META | SCI_SHIFT)\n#else\n#define SCI_CTRL_META SCI_CTRL\n#define SCI_SCTRL_META (SCI_CTRL | SCI_SHIFT)\n#endif\n\nnamespace {\n\nconstexpr Keys Key(char ch) noexcept {\n    return static_cast<Keys>(ch);\n}\n\n}\n\nconst KeyToCommand KeyMap::MapDefault[] = {\n\n#if OS_X_KEYS\n    {Keys::Down,\t\tSCI_CTRL,\tMessage::DocumentEnd},\n    {Keys::Down,\t\tSCI_CSHIFT,\tMessage::DocumentEndExtend},\n    {Keys::Up,\t\t    SCI_CTRL,\tMessage::DocumentStart},\n    {Keys::Up,\t\t    SCI_CSHIFT,\tMessage::DocumentStartExtend},\n    {Keys::Left,\t\tSCI_CTRL,\tMessage::VCHome},\n    {Keys::Left,\t\tSCI_CSHIFT,\tMessage::VCHomeExtend},\n    {Keys::Right,\t\tSCI_CTRL,\tMessage::LineEnd},\n    {Keys::Right,\t\tSCI_CSHIFT,\tMessage::LineEndExtend},\n#endif\n\n    {Keys::Down,\t\tSCI_NORM,\tMessage::LineDown},\n    {Keys::Down,\t\tSCI_SHIFT,\tMessage::LineDownExtend},\n    {Keys::Down,\t\tSCI_CTRL_META,\tMessage::LineScrollDown},\n    {Keys::Down,\t\tSCI_ASHIFT,\tMessage::LineDownRectExtend},\n    {Keys::Up,\t\t    SCI_NORM,\tMessage::LineUp},\n    {Keys::Up,\t\t\tSCI_SHIFT,\tMessage::LineUpExtend},\n    {Keys::Up,\t\t\tSCI_CTRL_META,\tMessage::LineScrollUp},\n    {Keys::Up,\t\t    SCI_ASHIFT,\tMessage::LineUpRectExtend},\n    {Key('['),\t\t\tSCI_CTRL,\tMessage::ParaUp},\n    {Key('['),\t\t\tSCI_CSHIFT,\tMessage::ParaUpExtend},\n    {Key(']'),\t\t\tSCI_CTRL,\tMessage::ParaDown},\n    {Key(']'),\t\t\tSCI_CSHIFT,\tMessage::ParaDownExtend},\n    {Keys::Left,\t\tSCI_NORM,\tMessage::CharLeft},\n    {Keys::Left,\t\tSCI_SHIFT,\tMessage::CharLeftExtend},\n    {Keys::Left,\t\tSCI_CTRL_META,\tMessage::WordLeft},\n    {Keys::Left,\t\tSCI_SCTRL_META,\tMessage::WordLeftExtend},\n    {Keys::Left,\t\tSCI_ASHIFT,\tMessage::CharLeftRectExtend},\n    {Keys::Right,\t\tSCI_NORM,\tMessage::CharRight},\n    {Keys::Right,\t\tSCI_SHIFT,\tMessage::CharRightExtend},\n    {Keys::Right,\t\tSCI_CTRL_META,\tMessage::WordRight},\n    {Keys::Right,\t\tSCI_SCTRL_META,\tMessage::WordRightExtend},\n    {Keys::Right,\t\tSCI_ASHIFT,\tMessage::CharRightRectExtend},\n    {Key('/'),\t\t    SCI_CTRL,\tMessage::WordPartLeft},\n    {Key('/'),\t\t    SCI_CSHIFT,\tMessage::WordPartLeftExtend},\n    {Key('\\\\'),\t\t    SCI_CTRL,\tMessage::WordPartRight},\n    {Key('\\\\'),\t\t    SCI_CSHIFT,\tMessage::WordPartRightExtend},\n    {Keys::Home,\t\tSCI_NORM,\tMessage::VCHome},\n    {Keys::Home, \t\tSCI_SHIFT, \tMessage::VCHomeExtend},\n    {Keys::Home, \t\tSCI_CTRL, \tMessage::DocumentStart},\n    {Keys::Home, \t\tSCI_CSHIFT, Message::DocumentStartExtend},\n    {Keys::Home, \t\tSCI_ALT, \tMessage::HomeDisplay},\n    {Keys::Home,\t\tSCI_ASHIFT,\tMessage::VCHomeRectExtend},\n    {Keys::End,\t \t    SCI_NORM,\tMessage::LineEnd},\n    {Keys::End,\t \t    SCI_SHIFT, \tMessage::LineEndExtend},\n    {Keys::End, \t\tSCI_CTRL, \tMessage::DocumentEnd},\n    {Keys::End, \t\tSCI_CSHIFT, Message::DocumentEndExtend},\n    {Keys::End, \t\tSCI_ALT, \tMessage::LineEndDisplay},\n    {Keys::End,\t\t    SCI_ASHIFT,\tMessage::LineEndRectExtend},\n    {Keys::Prior,\t\tSCI_NORM,\tMessage::PageUp},\n    {Keys::Prior,\t\tSCI_SHIFT, \tMessage::PageUpExtend},\n    {Keys::Prior,\t\tSCI_ASHIFT,\tMessage::PageUpRectExtend},\n    {Keys::Next, \t\tSCI_NORM, \tMessage::PageDown},\n    {Keys::Next, \t\tSCI_SHIFT, \tMessage::PageDownExtend},\n    {Keys::Next,\t\tSCI_ASHIFT,\tMessage::PageDownRectExtend},\n    {Keys::Delete,      SCI_NORM,\tMessage::Clear},\n    {Keys::Delete, \t    SCI_SHIFT,\tMessage::Cut},\n    {Keys::Delete, \t    SCI_CTRL,\tMessage::DelWordRight},\n    {Keys::Delete,\t    SCI_CSHIFT,\tMessage::DelLineRight},\n    {Keys::Insert, \t\tSCI_NORM,\tMessage::EditToggleOvertype},\n    {Keys::Insert, \t\tSCI_SHIFT,\tMessage::Paste},\n    {Keys::Insert, \t\tSCI_CTRL,\tMessage::Copy},\n    {Keys::Escape,  \tSCI_NORM,\tMessage::Cancel},\n    {Keys::Back,\t\tSCI_NORM, \tMessage::DeleteBack},\n    {Keys::Back,\t\tSCI_SHIFT, \tMessage::DeleteBack},\n    {Keys::Back,\t\tSCI_CTRL, \tMessage::DelWordLeft},\n    {Keys::Back, \t\tSCI_ALT,\tMessage::Undo},\n    {Keys::Back,\t\tSCI_CSHIFT,\tMessage::DelLineLeft},\n    {Key('Z'), \t\t\tSCI_CTRL,\tMessage::Undo},\n#if OS_X_KEYS\n    {Key('Z'), \t\t\tSCI_CSHIFT,\tMessage::Redo},\n#else\n    {Key('Y'), \t\t\tSCI_CTRL,\tMessage::Redo},\n#endif\n    {Key('X'), \t\t\tSCI_CTRL,\tMessage::Cut},\n    {Key('C'), \t\t\tSCI_CTRL,\tMessage::Copy},\n    {Key('V'), \t\t\tSCI_CTRL,\tMessage::Paste},\n    {Key('A'), \t\t\tSCI_CTRL,\tMessage::SelectAll},\n    {Keys::Tab,\t\t    SCI_NORM,\tMessage::Tab},\n    {Keys::Tab,\t\t    SCI_SHIFT,\tMessage::BackTab},\n    {Keys::Return, \t    SCI_NORM,\tMessage::NewLine},\n    {Keys::Return, \t    SCI_SHIFT,\tMessage::NewLine},\n    {Keys::Add, \t\tSCI_CTRL,\tMessage::ZoomIn},\n    {Keys::Subtract,\tSCI_CTRL,\tMessage::ZoomOut},\n    {Keys::Divide,\t    SCI_CTRL,\tMessage::SetZoom},\n    {Key('L'), \t\t\tSCI_CTRL,\tMessage::LineCut},\n    {Key('L'), \t\t\tSCI_CSHIFT,\tMessage::LineDelete},\n    {Key('T'), \t\t\tSCI_CSHIFT,\tMessage::LineCopy},\n    {Key('T'), \t\t\tSCI_CTRL,\tMessage::LineTranspose},\n    {Key('D'), \t\t\tSCI_CTRL,\tMessage::SelectionDuplicate},\n    {Key('U'), \t\t\tSCI_CTRL,\tMessage::LowerCase},\n    {Key('U'), \t\t\tSCI_CSHIFT,\tMessage::UpperCase},\n    {Key(0),SCI_NORM,static_cast<Message>(0)},\n};\n\n"
  },
  {
    "path": "Libraries/scintilla/src/KeyMap.h",
    "content": "// Scintilla source code edit control\n/** @file KeyMap.h\n ** Defines a mapping between keystrokes and commands.\n **/\n// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#ifndef KEYMAP_H\n#define KEYMAP_H\n\nnamespace Scintilla::Internal {\n\n#define SCI_NORM KeyMod::Norm\n#define SCI_SHIFT KeyMod::Shift\n#define SCI_CTRL KeyMod::Ctrl\n#define SCI_ALT KeyMod::Alt\n#define SCI_META KeyMod::Meta\n#define SCI_SUPER KeyMod::Super\n#define SCI_CSHIFT (KeyMod::Ctrl | KeyMod::Shift)\n#define SCI_ASHIFT (KeyMod::Alt | KeyMod::Shift)\n\n/**\n */\nclass KeyModifiers {\npublic:\n\tScintilla::Keys key;\n\tScintilla::KeyMod modifiers;\n\tKeyModifiers() noexcept : key{}, modifiers(KeyMod::Norm) {\n\t};\n\tKeyModifiers(Scintilla::Keys key_, Scintilla::KeyMod modifiers_) noexcept : key(key_), modifiers(modifiers_) {\n\t}\n\tbool operator<(const KeyModifiers &other) const noexcept {\n\t\tif (key == other.key)\n\t\t\treturn modifiers < other.modifiers;\n\t\telse\n\t\t\treturn key < other.key;\n\t}\n};\n\n/**\n */\nclass KeyToCommand {\npublic:\n\tScintilla::Keys key;\n\tScintilla::KeyMod modifiers;\n\tScintilla::Message msg;\n};\n\n/**\n */\nclass KeyMap {\n\tstd::map<KeyModifiers, Scintilla::Message> kmap;\n\tstatic const KeyToCommand MapDefault[];\n\npublic:\n\tKeyMap();\n\tvoid Clear() noexcept;\n\tvoid AssignCmdKey(Scintilla::Keys key, Scintilla::KeyMod modifiers, Scintilla::Message msg);\n\tScintilla::Message Find(Scintilla::Keys key, Scintilla::KeyMod modifiers) const;\t// 0 returned on failure\n\tconst std::map<KeyModifiers, Scintilla::Message> &GetKeyMap() const noexcept;\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/src/LineMarker.cxx",
    "content": "// Scintilla source code edit control\n/** @file LineMarker.cxx\n ** Defines the look of a line marker in the margin.\n **/\n// Copyright 1998-2011 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#include <cstdint>\n#include <cstring>\n#include <cmath>\n\n#include <stdexcept>\n#include <string>\n#include <string_view>\n#include <vector>\n#include <map>\n#include <optional>\n#include <algorithm>\n#include <iterator>\n#include <memory>\n\n#include \"ScintillaTypes.h\"\n\n#include \"Debugging.h\"\n#include \"Geometry.h\"\n\n#include \"Platform.h\"\n\n#include \"XPM.h\"\n#include \"LineMarker.h\"\n#include \"UniConversion.h\"\n\nusing namespace Scintilla;\nusing namespace Scintilla::Internal;\n\nLineMarker::LineMarker(const LineMarker &other) {\n\t// Defined to avoid pxpm and image being blindly copied, not as a complete copy constructor.\n\tmarkType = other.markType;\n\tfore = other.fore;\n\tback = other.back;\n\tbackSelected = other.backSelected;\n\tstrokeWidth = other.strokeWidth;\n\tlayer = other.layer;\n\talpha = other.alpha;\n\tif (other.pxpm)\n\t\tpxpm = std::make_unique<XPM>(*other.pxpm);\n\telse\n\t\tpxpm = nullptr;\n\tif (other.image)\n\t\timage = std::make_unique<RGBAImage>(*other.image);\n\telse\n\t\timage = nullptr;\n\tcustomDraw = other.customDraw;\n}\n\nLineMarker &LineMarker::operator=(const LineMarker &other) {\n\t// Defined to avoid pxpm and image being blindly copied, not as a complete assignment operator.\n\tif (this != &other) {\n\t\tmarkType = other.markType;\n\t\tfore = other.fore;\n\t\tback = other.back;\n\t\tbackSelected = other.backSelected;\n\t\tstrokeWidth = other.strokeWidth;\n\t\tlayer = other.layer;\n\t\talpha = other.alpha;\n\t\tif (other.pxpm)\n\t\t\tpxpm = std::make_unique<XPM>(*other.pxpm);\n\t\telse\n\t\t\tpxpm = nullptr;\n\t\tif (other.image)\n\t\t\timage = std::make_unique<RGBAImage>(*other.image);\n\t\telse\n\t\t\timage = nullptr;\n\t\tcustomDraw = other.customDraw;\n\t}\n\treturn *this;\n}\n\nColourRGBA LineMarker::BackWithAlpha() const noexcept {\n\treturn ColourRGBA(back, static_cast<int>(alpha));\n}\n\nvoid LineMarker::SetXPM(const char *textForm) {\n\tpxpm = std::make_unique<XPM>(textForm);\n\tmarkType = MarkerSymbol::Pixmap;\n}\n\nvoid LineMarker::SetXPM(const char *const *linesForm) {\n\tpxpm = std::make_unique<XPM>(linesForm);\n\tmarkType = MarkerSymbol::Pixmap;\n}\n\nvoid LineMarker::SetRGBAImage(Point sizeRGBAImage, float scale, const unsigned char *pixelsRGBAImage) {\n\timage = std::make_unique<RGBAImage>(static_cast<int>(sizeRGBAImage.x), static_cast<int>(sizeRGBAImage.y), scale, pixelsRGBAImage);\n\tmarkType = MarkerSymbol::RgbaImage;\n}\n\nnamespace {\n\nenum class Expansion { Minus, Plus };\nenum class Shape { Square, Circle };\n\nvoid DrawSymbol(Surface *surface, Shape shape, Expansion expansion, PRectangle rcSymbol, XYPOSITION widthStroke,\n\tColourRGBA colourFill, ColourRGBA colourFrame, ColourRGBA colourFrameRight, ColourRGBA colourExpansion) {\n\n\tconst FillStroke fillStroke(colourFill, colourFrame, widthStroke);\n\tconst PRectangle rcSymbolLeft = Side(rcSymbol, Edge::left, (rcSymbol.Width() + widthStroke) / 2.0f);\n\tsurface->SetClip(rcSymbolLeft);\n\tif (shape == Shape::Square) {\n\t\t// Hollowed square\n\t\tsurface->RectangleDraw(rcSymbol, fillStroke);\n\t} else {\n\t\tsurface->Ellipse(rcSymbol, fillStroke);\n\t}\n\tsurface->PopClip();\n\n\tconst FillStroke fillStrokeRight(colourFill, colourFrameRight, widthStroke);\n\tconst PRectangle rcSymbolRight = Side(rcSymbol, Edge::right, (rcSymbol.Width() - widthStroke) / 2.0f);\n\tsurface->SetClip(rcSymbolRight);\n\tif (shape == Shape::Square) {\n\t\tsurface->RectangleDraw(rcSymbol, fillStrokeRight);\n\t} else {\n\t\tsurface->Ellipse(rcSymbol, fillStrokeRight);\n\t}\n\tsurface->PopClip();\n\n\tconst PRectangle rcPlusMinus = rcSymbol.Inset(widthStroke + 1.0f);\n\tconst XYPOSITION armWidth = (rcPlusMinus.Width() - widthStroke) / 2.0f;\n\tconst XYPOSITION top = rcPlusMinus.top + armWidth;\n\tconst PRectangle rcH = PRectangle(\n\t\trcPlusMinus.left, top,\n\t\trcPlusMinus.right, top + widthStroke);\n\tsurface->FillRectangle(rcH, colourExpansion);\n\tif (expansion == Expansion::Plus) {\n\t\tconst XYPOSITION left = rcPlusMinus.left + armWidth;\n\t\tconst PRectangle rcV = PRectangle(\n\t\t\tleft, rcPlusMinus.top,\n\t\t\tleft + widthStroke, rcPlusMinus.bottom);\n\t\tsurface->FillRectangle(rcV, colourExpansion);\n\t}\n}\n\nvoid DrawTail(Surface *surface, XYPOSITION leftLine, XYPOSITION rightTail, XYPOSITION centreY, XYPOSITION widthSymbolStroke, ColourRGBA fill) {\n\tconst XYPOSITION slopeLength = 2.0f + widthSymbolStroke;\n\tconst XYPOSITION strokeTop = centreY + slopeLength;\n\tconst XYPOSITION halfWidth = widthSymbolStroke / 2.0f;\n\tconst XYPOSITION strokeMiddle = strokeTop + halfWidth;\n\tconst Point lines[] = {\n\t\t// Stick\n\t\tPoint(rightTail, strokeMiddle),\n\t\tPoint(leftLine + halfWidth + slopeLength, strokeMiddle),\n\t\t// Slope\n\t\tPoint(leftLine + widthSymbolStroke / 2.0f, centreY + halfWidth),\n\t};\n\tsurface->PolyLine(lines, std::size(lines), Stroke(fill, widthSymbolStroke));\n}\n\n}\n\nvoid LineMarker::DrawFoldingMark(Surface *surface, const PRectangle &rcWhole, FoldPart part) const {\n\t// Assume: edges of rcWhole are integers.\n\t// Code can only really handle integer strokeWidth.\n\n\tColourRGBA colourHead = back;\n\tColourRGBA colourBody = back;\n\tColourRGBA colourTail = back;\n\n\tswitch (part) {\n\tcase FoldPart::head:\n\tcase FoldPart::headWithTail:\n\t\tcolourHead = backSelected;\n\t\tcolourTail = backSelected;\n\t\tbreak;\n\tcase FoldPart::body:\n\t\tcolourHead = backSelected;\n\t\tcolourBody = backSelected;\n\t\tbreak;\n\tcase FoldPart::tail:\n\t\tcolourBody = backSelected;\n\t\tcolourTail = backSelected;\n\t\tbreak;\n\tdefault:\n\t\t// LineMarker::undefined\n\t\tbreak;\n\t}\n\n\tconst int pixelDivisions = surface->PixelDivisions();\n\n\t// Folding symbols should have equal height and width to be either a circle or square.\n\t// So find the minimum of width and height.\n\tconst XYPOSITION minDimension = std::floor(std::min(rcWhole.Width(), rcWhole.Height() - 2)) - 1;\n\n\t// If strokeWidth would take up too much of area reduce to reasonable width.\n\tconst XYPOSITION widthStroke = PixelAlignFloor(std::min(strokeWidth, minDimension / 5.0f), pixelDivisions);\n\n\t// To centre +/-, odd strokeWidth -> odd symbol width, even -> even\n\tconst XYPOSITION widthSymbol =\n\t\t((std::lround(minDimension * pixelDivisions) % 2) == (std::lround(widthStroke * pixelDivisions) % 2)) ?\n\t\tminDimension : minDimension - (1.0 / static_cast<XYPOSITION>(pixelDivisions));\n\n\tconst Point centre = PixelAlign(rcWhole.Centre(), pixelDivisions);\n\n\t// Folder symbols and lines follow some rules to join up, fit the pixel grid,\n\t// and avoid over-painting.\n\n\tconst XYPOSITION halfSymbol = std::round(widthSymbol / 2);\n\tconst Point topLeft(centre.x - halfSymbol, centre.y - halfSymbol);\n\tconst PRectangle rcSymbol(\n\t\ttopLeft.x, topLeft.y,\n\t\ttopLeft.x + widthSymbol, topLeft.y + widthSymbol);\n\tconst XYPOSITION leftLine = rcSymbol.Centre().x - widthStroke / 2.0f;\n\tconst XYPOSITION rightLine = leftLine + widthStroke;\n\n\t// This is the vertical line through the whole area which is subdivided\n\t// when there is a symbol on the line or the colour changes for highlighting.\n\tconst PRectangle rcVLine(leftLine, rcWhole.top, rightLine, rcWhole.bottom);\n\n\t// Portions of rcVLine above and below the symbol.\n\tconst PRectangle rcAboveSymbol = Clamp(rcVLine, Edge::bottom, rcSymbol.top);\n\tconst PRectangle rcBelowSymbol = Clamp(rcVLine, Edge::top, rcSymbol.bottom);\n\n\t// Projection to right.\n\tconst PRectangle rcStick(\n\t\trcVLine.right, centre.y + 1.0f - widthStroke,\n\t\trcWhole.right - 1, centre.y + 1.0f);\n\n\tswitch (markType) {\n\n\tcase MarkerSymbol::VLine:\n\t\tsurface->FillRectangle(rcVLine, colourBody);\n\t\tbreak;\n\n\tcase MarkerSymbol::LCorner:\n\t\tsurface->FillRectangle(Clamp(rcVLine, Edge::bottom, centre.y + 1.0f), colourTail);\n\t\tsurface->FillRectangle(rcStick, colourTail);\n\t\tbreak;\n\n\tcase MarkerSymbol::TCorner:\n\t\tsurface->FillRectangle(Clamp(rcVLine, Edge::bottom, centre.y + 1.0f), colourBody);\n\t\tsurface->FillRectangle(Clamp(rcVLine, Edge::top, centre.y + 1.0f), colourHead);\n\t\tsurface->FillRectangle(rcStick, colourTail);\n\t\tbreak;\n\n\t// CORNERCURVE cases divide slightly lower than CORNER to accommodate the curve\n\tcase MarkerSymbol::LCornerCurve:\n\t\tsurface->FillRectangle(Clamp(rcVLine, Edge::bottom, centre.y), colourTail);\n\t\tDrawTail(surface, leftLine, rcWhole.right - 1.0f, centre.y - widthStroke,\n\t\t\twidthStroke, colourTail);\n\t\tbreak;\n\n\tcase MarkerSymbol::TCornerCurve:\n\t\tsurface->FillRectangle(Clamp(rcVLine, Edge::bottom, centre.y), colourBody);\n\t\tsurface->FillRectangle(Clamp(rcVLine, Edge::top, centre.y), colourHead);\n\t\tDrawTail(surface, leftLine, rcWhole.right - 1.0f, centre.y - widthStroke,\n\t\t\twidthStroke, colourTail);\n\t\tbreak;\n\n\tcase MarkerSymbol::BoxPlus:\n\t\tDrawSymbol(surface, Shape::Square, Expansion::Plus, rcSymbol, widthStroke,\n\t\t\tfore, colourHead, colourHead, colourTail);\n\t\tbreak;\n\n\tcase MarkerSymbol::BoxPlusConnected: {\n\t\t\tconst ColourRGBA colourBelow = (part == FoldPart::headWithTail) ? colourTail : colourBody;\n\t\t\tsurface->FillRectangle(rcBelowSymbol, colourBelow);\n\t\t\tsurface->FillRectangle(rcAboveSymbol, colourBody);\n\n\t\t\tconst ColourRGBA colourRight = (part == FoldPart::body) ? colourTail : colourHead;\n\t\t\tDrawSymbol(surface, Shape::Square, Expansion::Plus, rcSymbol, widthStroke,\n\t\t\t\tfore, colourHead, colourRight, colourTail);\n\t\t}\n\t\tbreak;\n\n\tcase MarkerSymbol::BoxMinus:\n\t\tsurface->FillRectangle(rcBelowSymbol, colourHead);\n\t\tDrawSymbol(surface, Shape::Square, Expansion::Minus, rcSymbol, widthStroke,\n\t\t\tfore, colourHead, colourHead, colourTail);\n\t\tbreak;\n\n\tcase MarkerSymbol::BoxMinusConnected: {\n\t\t\tsurface->FillRectangle(rcBelowSymbol, colourHead);\n\t\t\tsurface->FillRectangle(rcAboveSymbol, colourBody);\n\n\t\t\tconst ColourRGBA colourRight = (part == FoldPart::body) ? colourTail : colourHead;\n\t\t\tDrawSymbol(surface, Shape::Square, Expansion::Minus, rcSymbol, widthStroke,\n\t\t\t\tfore, colourHead, colourRight, colourTail);\n\t\t}\n\t\tbreak;\n\n\tcase MarkerSymbol::CirclePlus:\n\t\tDrawSymbol(surface, Shape::Circle, Expansion::Plus, rcSymbol, widthStroke,\n\t\t\tfore, colourHead, colourHead, colourTail);\n\t\tbreak;\n\n\tcase MarkerSymbol::CirclePlusConnected: {\n\t\t\tconst ColourRGBA colourBelow = (part == FoldPart::headWithTail) ? colourTail : colourBody;\n\t\t\tsurface->FillRectangle(rcBelowSymbol, colourBelow);\n\t\t\tsurface->FillRectangle(rcAboveSymbol, colourBody);\n\n\t\t\tconst ColourRGBA colourRight = (part == FoldPart::body) ? colourTail : colourHead;\n\t\t\tDrawSymbol(surface, Shape::Circle, Expansion::Plus, rcSymbol, widthStroke,\n\t\t\t\tfore, colourHead, colourRight, colourTail);\n\t\t}\n\t\tbreak;\n\n\tcase MarkerSymbol::CircleMinus:\n\t\tsurface->FillRectangle(rcBelowSymbol, colourHead);\n\t\tDrawSymbol(surface, Shape::Circle, Expansion::Minus, rcSymbol, widthStroke,\n\t\t\tfore, colourHead, colourHead, colourTail);\n\t\tbreak;\n\n\tcase MarkerSymbol::CircleMinusConnected: {\n\t\t\tsurface->FillRectangle(rcBelowSymbol, colourHead);\n\t\t\tsurface->FillRectangle(rcAboveSymbol, colourBody);\n\t\t\tconst ColourRGBA colourRight = (part == FoldPart::body) ? colourTail : colourHead;\n\t\t\tDrawSymbol(surface, Shape::Circle, Expansion::Minus, rcSymbol, widthStroke,\n\t\t\t\tfore, colourHead, colourRight, colourTail);\n\t\t}\n\t\tbreak;\n\n\tdefault:\n\t\tbreak;\n\n\t}\n}\n\nvoid LineMarker::AlignedPolygon(Surface *surface, const Point *pts, size_t npts) const {\n\tconst XYPOSITION move = strokeWidth / 2.0;\n\tstd::vector<Point> points;\n\tstd::transform(pts, pts + npts, std::back_inserter(points), [=](Point pt) noexcept ->Point {\n\t\treturn Point(pt.x + move, pt.y + move);\n\t});\n\tsurface->Polygon(points.data(), std::size(points), FillStroke(back, fore, strokeWidth));\n}\n\nvoid LineMarker::Draw(Surface *surface, const PRectangle &rcWhole, const Font *fontForCharacter, FoldPart part, MarginType marginStyle) const {\n\t// This is to satisfy the changed API - eventually the stroke width will be exposed to clients\n\n\tif (customDraw) {\n\t\tcustomDraw(surface, rcWhole, fontForCharacter, static_cast<int>(part), marginStyle, this);\n\t\treturn;\n\t}\n\n\tif ((markType == MarkerSymbol::Pixmap) && (pxpm)) {\n\t\tpxpm->Draw(surface, rcWhole);\n\t\treturn;\n\t}\n\tif ((markType == MarkerSymbol::RgbaImage) && (image)) {\n\t\t// Make rectangle just large enough to fit image centred on centre of rcWhole\n\t\tPRectangle rcImage;\n\t\trcImage.top = ((rcWhole.top + rcWhole.bottom) - image->GetScaledHeight()) / 2;\n\t\trcImage.bottom = rcImage.top + image->GetScaledHeight();\n\t\trcImage.left = ((rcWhole.left + rcWhole.right) - image->GetScaledWidth()) / 2;\n\t\trcImage.right = rcImage.left + image->GetScaledWidth();\n\t\tsurface->DrawRGBAImage(rcImage, image->GetWidth(), image->GetHeight(), image->Pixels());\n\t\treturn;\n\t}\n\n\tif ((markType >= MarkerSymbol::VLine) && markType <= (MarkerSymbol::CircleMinusConnected)) {\n\t\tDrawFoldingMark(surface, rcWhole, part);\n\t\treturn;\n\t}\n\n\t// Restrict most shapes a bit\n\tconst PRectangle rc(rcWhole.left, rcWhole.top + 1, rcWhole.right, rcWhole.bottom - 1);\n\t// Ensure does not go beyond edge\n\tconst XYPOSITION minDim = std::min(rcWhole.Width(), rcWhole.Height() - 2) - 1;\n\n\tconst Point centre = rcWhole.Centre();\n\tXYPOSITION centreX = std::floor(centre.x);\n\tconst XYPOSITION centreY = std::floor(centre.y);\n\tconst XYPOSITION dimOn2 = std::floor(minDim / 2);\n\tconst XYPOSITION dimOn4 = std::floor(minDim / 4);\n\tconst XYPOSITION armSize = dimOn2 - 2;\n\tif (marginStyle == MarginType::Number || marginStyle == MarginType::Text || marginStyle == MarginType::RText) {\n\t\t// On textual margins move marker to the left to try to avoid overlapping the text\n\t\tcentreX = rcWhole.left + dimOn2 + 1;\n\t}\n\n\tswitch (markType) {\n\tcase MarkerSymbol::RoundRect: {\n\t\t\tPRectangle rcRounded = rc;\n\t\t\trcRounded.left = rc.left + 1;\n\t\t\trcRounded.right = rc.right - 1;\n\t\t\tsurface->RoundedRectangle(rcRounded, FillStroke(back, fore, strokeWidth));\n\t\t}\n\t\tbreak;\n\n\tcase MarkerSymbol::Circle: {\n\t\t\tconst PRectangle rcCircle = PRectangle(\n\t\t\t\t\t\t\t    centreX - dimOn2,\n\t\t\t\t\t\t\t    centreY - dimOn2,\n\t\t\t\t\t\t\t    centreX + dimOn2,\n\t\t\t\t\t\t\t    centreY + dimOn2);\n\t\t\tsurface->Ellipse(rcCircle, FillStroke(back, fore, strokeWidth));\n\t\t}\n\t\tbreak;\n\n\tcase MarkerSymbol::Arrow: {\n\t\t\tconst Point pts[] = {\n\t\t\t\tPoint(centreX - dimOn4, centreY - dimOn2),\n\t\t\t\tPoint(centreX - dimOn4, centreY + dimOn2),\n\t\t\t\tPoint(centreX + dimOn2 - dimOn4, centreY),\n\t\t\t};\n\t\t\tAlignedPolygon(surface, pts, std::size(pts));\n\t\t}\n\t\tbreak;\n\n\tcase MarkerSymbol::ArrowDown: {\n\t\t\tconst Point pts[] = {\n\t\t\t\tPoint(centreX - dimOn2, centreY - dimOn4),\n\t\t\t\tPoint(centreX + dimOn2, centreY - dimOn4),\n\t\t\t\tPoint(centreX, centreY + dimOn2 - dimOn4),\n\t\t\t};\n\t\t\tAlignedPolygon(surface, pts, std::size(pts));\n\t\t}\n\t\tbreak;\n\n\tcase MarkerSymbol::Plus: {\n\t\t\tconst Point pts[] = {\n\t\t\t\tPoint(centreX - armSize, centreY - 1),\n\t\t\t\tPoint(centreX - 1, centreY - 1),\n\t\t\t\tPoint(centreX - 1, centreY - armSize),\n\t\t\t\tPoint(centreX + 1, centreY - armSize),\n\t\t\t\tPoint(centreX + 1, centreY - 1),\n\t\t\t\tPoint(centreX + armSize, centreY - 1),\n\t\t\t\tPoint(centreX + armSize, centreY + 1),\n\t\t\t\tPoint(centreX + 1, centreY + 1),\n\t\t\t\tPoint(centreX + 1, centreY + armSize),\n\t\t\t\tPoint(centreX - 1, centreY + armSize),\n\t\t\t\tPoint(centreX - 1, centreY + 1),\n\t\t\t\tPoint(centreX - armSize, centreY + 1),\n\t\t\t};\n\t\t\tAlignedPolygon(surface, pts, std::size(pts));\n\t\t}\n\t\tbreak;\n\n\tcase MarkerSymbol::Minus: {\n\t\t\tconst Point pts[] = {\n\t\t\t\tPoint(centreX - armSize, centreY - 1),\n\t\t\t\tPoint(centreX + armSize, centreY - 1),\n\t\t\t\tPoint(centreX + armSize, centreY + 1),\n\t\t\t\tPoint(centreX - armSize, centreY + 1),\n\t\t\t};\n\t\t\tAlignedPolygon(surface, pts, std::size(pts));\n\t\t}\n\t\tbreak;\n\n\tcase MarkerSymbol::SmallRect: {\n\t\t\tPRectangle rcSmall;\n\t\t\trcSmall.left = rc.left + 1;\n\t\t\trcSmall.top = rc.top + 2;\n\t\t\trcSmall.right = rc.right - 1;\n\t\t\trcSmall.bottom = rc.bottom - 2;\n\t\t\tsurface->RectangleDraw(rcSmall, FillStroke(back, fore, strokeWidth));\n\t\t}\n\t\tbreak;\n\n\tcase MarkerSymbol::Empty:\n\tcase MarkerSymbol::Background:\n\tcase MarkerSymbol::Underline:\n\tcase MarkerSymbol::Available:\n\t\t// An invisible marker so don't draw anything\n\t\tbreak;\n\n\tcase MarkerSymbol::DotDotDot: {\n\t\t\t// 3 2x2 dots with 3 pixels between, margin must be 12 wide to show all\n\t\t\tconstexpr XYPOSITION pitchDots = 5.0;\n\t\t\tXYPOSITION xBlob = std::floor(centreX - (pitchDots + 1));\n\t\t\tfor (int b = 0; b < 3; b++) {\n\t\t\t\tconst PRectangle rcBlob(xBlob, rc.bottom - 4, xBlob + 2, rc.bottom - 2);\n\t\t\t\tsurface->FillRectangle(rcBlob, fore);\n\t\t\t\txBlob += pitchDots;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\n\tcase MarkerSymbol::Arrows: {\n\t\t\tXYPOSITION right = centreX - 4.0f + strokeWidth / 2.0f;\n\t\t\tconst XYPOSITION midY = centreY + strokeWidth / 2.0f;\n\t\t\tconst XYPOSITION armLength = std::round(dimOn2 - strokeWidth);\n\t\t\tfor (int b = 0; b < 3; b++) {\n\t\t\t\tconst Point pts[] = {\n\t\t\t\t\tPoint(right - armLength, midY - armLength),\n\t\t\t\t\tPoint(right, midY),\n\t\t\t\t\tPoint(right - armLength, midY + armLength)\n\t\t\t\t};\n\t\t\t\tsurface->PolyLine(pts, std::size(pts), Stroke(fore, strokeWidth));\n\t\t\t\tright += strokeWidth + 3.0f;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\n\tcase MarkerSymbol::ShortArrow: {\n\t\t\tconst Point pts[] = {\n\t\t\t\tPoint(centreX, centreY + dimOn2),\n\t\t\t\tPoint(centreX + dimOn2, centreY),\n\t\t\t\tPoint(centreX, centreY - dimOn2),\n\t\t\t\tPoint(centreX, centreY - dimOn4),\n\t\t\t\tPoint(centreX - dimOn4, centreY - dimOn4),\n\t\t\t\tPoint(centreX - dimOn4, centreY + dimOn4),\n\t\t\t\tPoint(centreX, centreY + dimOn4),\n\t\t\t\tPoint(centreX, centreY + dimOn2),\n\t\t\t};\n\t\t\tAlignedPolygon(surface, pts, std::size(pts));\n\t\t}\n\t\tbreak;\n\n\tcase MarkerSymbol::FullRect:\n\t\tsurface->FillRectangle(rcWhole, back);\n\t\tbreak;\n\n\tcase MarkerSymbol::LeftRect: {\n\t\t\tPRectangle rcLeft = rcWhole;\n\t\t\trcLeft.right = rcLeft.left + 4;\n\t\t\tsurface->FillRectangle(rcLeft, back);\n\t\t}\n\t\tbreak;\n\n\tcase MarkerSymbol::Bar: {\n\t\t\t// Hide cap by continuing a bit.\n\t\t\tconstexpr XYPOSITION continueLength = 5.0;\n\t\t\tPRectangle rcBar = rcWhole;\n\t\t\tconst XYPOSITION widthBar = std::ceil(rcWhole.Width() / 3.0);\n\t\t\trcBar.left = centreX - std::floor(widthBar / 2.0);\n\t\t\trcBar.right = rcBar.left + widthBar;\n\t\t\tsurface->SetClip(rcWhole);\t// Hide continued caps\n\t\t\tswitch (part) {\n\t\t\tcase LineMarker::FoldPart::headWithTail:\n\t\t\t\tsurface->RectangleDraw(rcBar, FillStroke(back, fore, strokeWidth));\n\t\t\t\tbreak;\n\t\t\tcase LineMarker::FoldPart::head:\n\t\t\t\trcBar.bottom += continueLength;\n\t\t\t\tsurface->RectangleDraw(rcBar, FillStroke(back, fore, strokeWidth));\n\t\t\t\tbreak;\n\t\t\tcase LineMarker::FoldPart::tail:\n\t\t\t\trcBar.top -= continueLength;\n\t\t\t\tsurface->RectangleDraw(rcBar, FillStroke(back, fore, strokeWidth));\n\t\t\t\tbreak;\n\t\t\tcase LineMarker::FoldPart::body:\n\t\t\t\trcBar.top -= continueLength;\n\t\t\t\trcBar.bottom += continueLength;\n\t\t\t\tsurface->RectangleDraw(rcBar, FillStroke(back, fore, strokeWidth));\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tsurface->PopClip();\n\t\t}\n\t\tbreak;\n\n\n\tcase MarkerSymbol::Bookmark: {\n\t\t\tconst XYPOSITION halfHeight = std::floor(minDim / 3);\n\t\t\tconst Point pts[] = {\n\t\t\t\tPoint(rcWhole.left, centreY - halfHeight),\n\t\t\t\tPoint(rcWhole.right - strokeWidth - 2, centreY - halfHeight),\n\t\t\t\tPoint(rcWhole.right - strokeWidth - 2 - halfHeight, centreY),\n\t\t\t\tPoint(rcWhole.right - strokeWidth - 2, centreY + halfHeight),\n\t\t\t\tPoint(rcWhole.left, centreY + halfHeight),\n\t\t\t};\n\t\t\tAlignedPolygon(surface, pts, std::size(pts));\n\t\t}\n\t\tbreak;\n\n\tcase MarkerSymbol::VerticalBookmark: {\n\t\t\tconst XYPOSITION halfWidth = std::floor(minDim / 3);\n\t\t\tconst Point pts[] = {\n\t\t\t\tPoint(centreX - halfWidth, centreY - dimOn2),\n\t\t\t\tPoint(centreX + halfWidth, centreY - dimOn2),\n\t\t\t\tPoint(centreX + halfWidth, centreY + dimOn2),\n\t\t\t\tPoint(centreX, centreY + dimOn2 - halfWidth),\n\t\t\t\tPoint(centreX - halfWidth, centreY + dimOn2),\n\t\t\t};\n\t\t\tAlignedPolygon(surface, pts, std::size(pts));\n\t\t}\n\t\tbreak;\n\n\tdefault:\n\t\tif (markType >= MarkerSymbol::Character) {\n\t\t\tchar character[UTF8MaxBytes + 1] {};\n\t\t\tconst int uch = static_cast<int>(markType) - static_cast<int>(MarkerSymbol::Character);\n\t\t\tUTF8FromUTF32Character(uch, character);\n\t\t\tconst XYPOSITION width = surface->WidthTextUTF8(fontForCharacter, character);\n\t\t\tPRectangle rcText = rc;\n\t\t\trcText.left += (rc.Width() - width) / 2;\n\t\t\trcText.right = rcText.left + width;\n\t\t\tsurface->DrawTextNoClipUTF8(rcText, fontForCharacter, rcText.bottom - 2,\n\t\t\t\t\t\t character, fore, back);\n\t\t} else {\n\t\t\t// treat as MarkerSymbol::FullRect\n\t\t\tsurface->FillRectangle(rcWhole, back);\n\t\t}\n\t\tbreak;\n\t}\n}\n"
  },
  {
    "path": "Libraries/scintilla/src/LineMarker.h",
    "content": "// Scintilla source code edit control\n/** @file LineMarker.h\n ** Defines the look of a line marker in the margin .\n **/\n// Copyright 1998-2011 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#ifndef LINEMARKER_H\n#define LINEMARKER_H\n\nnamespace Scintilla::Internal {\n\nclass XPM;\nclass RGBAImage;\n\ntypedef void (*DrawLineMarkerFn)(Surface *surface, const PRectangle &rcWhole, const Font *fontForCharacter, int tFold, Scintilla::MarginType marginStyle, const void *lineMarker);\n\n/**\n */\nclass LineMarker {\npublic:\n\tenum class FoldPart { undefined, head, body, tail, headWithTail };\n\n\tScintilla::MarkerSymbol markType = Scintilla::MarkerSymbol::Circle;\n\tColourRGBA fore = black;\n\tColourRGBA back = white;\n\tColourRGBA backSelected = ColourRGBA(maximumByte, 0, 0);\t// Red default\n\tScintilla::Layer layer = Scintilla::Layer::Base;\n\tScintilla::Alpha alpha = Scintilla::Alpha::NoAlpha;\n\tXYPOSITION strokeWidth = 1.0f;\n\tstd::unique_ptr<XPM> pxpm;\n\tstd::unique_ptr<RGBAImage> image;\n\t/** Some platforms, notably PLAT_CURSES, do not support Scintilla's native\n\t * Draw function for drawing line markers. Allow those platforms to override\n\t * it instead of creating a new method(s) in the Surface class that existing\n\t * platforms must implement as empty. */\n\tDrawLineMarkerFn customDraw = nullptr;\n\n\tLineMarker() noexcept = default;\n\tLineMarker(const LineMarker &other);\n\tLineMarker(LineMarker &&) noexcept = default;\n\tLineMarker &operator=(const LineMarker& other);\n\tLineMarker &operator=(LineMarker&&) noexcept = default;\n\tvirtual ~LineMarker() = default;\n\n\tColourRGBA BackWithAlpha() const noexcept;\n\n\tvoid SetXPM(const char *textForm);\n\tvoid SetXPM(const char *const *linesForm);\n\tvoid SetRGBAImage(Point sizeRGBAImage, float scale, const unsigned char *pixelsRGBAImage);\n\tvoid AlignedPolygon(Surface *surface, const Point *pts, size_t npts) const;\n\tvoid Draw(Surface *surface, const PRectangle &rcWhole, const Font *fontForCharacter, FoldPart part, Scintilla::MarginType marginStyle) const;\n\tvoid DrawFoldingMark(Surface *surface, const PRectangle &rcWhole, FoldPart part) const;\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/src/MarginView.cxx",
    "content": "// Scintilla source code edit control\n/** @file MarginView.cxx\n ** Defines the appearance of the editor margin.\n **/\n// Copyright 1998-2014 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#include <cstddef>\n#include <cstdlib>\n#include <cstdint>\n#include <cassert>\n#include <cstring>\n#include <cstdio>\n#include <cmath>\n\n#include <stdexcept>\n#include <string>\n#include <string_view>\n#include <vector>\n#include <map>\n#include <set>\n#include <optional>\n#include <algorithm>\n#include <memory>\n\n#include \"ScintillaTypes.h\"\n#include \"ScintillaMessages.h\"\n#include \"ScintillaStructures.h\"\n#include \"ILoader.h\"\n#include \"ILexer.h\"\n\n#include \"Debugging.h\"\n#include \"Geometry.h\"\n#include \"Platform.h\"\n\n#include \"CharacterCategoryMap.h\"\n#include \"Position.h\"\n#include \"UniqueString.h\"\n#include \"SplitVector.h\"\n#include \"Partitioning.h\"\n#include \"RunStyles.h\"\n#include \"ContractionState.h\"\n#include \"CellBuffer.h\"\n#include \"KeyMap.h\"\n#include \"Indicator.h\"\n#include \"LineMarker.h\"\n#include \"Style.h\"\n#include \"ViewStyle.h\"\n#include \"CharClassify.h\"\n#include \"Decoration.h\"\n#include \"CaseFolder.h\"\n#include \"Document.h\"\n#include \"UniConversion.h\"\n#include \"Selection.h\"\n#include \"PositionCache.h\"\n#include \"EditModel.h\"\n#include \"MarginView.h\"\n#include \"EditView.h\"\n\nusing namespace Scintilla;\n\nnamespace Scintilla::Internal {\n\nvoid DrawWrapMarker(Surface *surface, PRectangle rcPlace,\n\tbool isEndMarker, ColourRGBA wrapColour) {\n\n\tconst XYPOSITION extraFinalPixel = surface->SupportsFeature(Supports::LineDrawsFinal) ? 0.0f : 1.0f;\n\n\tconst PRectangle rcAligned = PixelAlignOutside(rcPlace, surface->PixelDivisions());\n\n\tconst XYPOSITION widthStroke = std::floor(rcAligned.Width() / 6);\n\n\tconstexpr XYPOSITION xa = 1; // gap before start\n\tconst XYPOSITION w = rcAligned.Width() - xa - widthStroke;\n\n\t// isEndMarker -> x-mirrored symbol for start marker\n\n\tconst XYPOSITION x0 = isEndMarker ? rcAligned.left : rcAligned.right - widthStroke;\n\tconst XYPOSITION y0 = rcAligned.top;\n\n\tconst XYPOSITION dy = std::floor(rcAligned.Height() / 5);\n\tconst XYPOSITION y = std::floor(rcAligned.Height() / 2) + dy;\n\n\tstruct Relative {\n\t\tXYPOSITION xBase;\n\t\tint xDir;\n\t\tXYPOSITION yBase;\n\t\tint yDir;\n\t\tXYPOSITION halfWidth;\n\t\tPoint At(XYPOSITION xRelative, XYPOSITION yRelative) const noexcept {\n\t\t\treturn Point(xBase + xDir * xRelative + halfWidth, yBase + yDir * yRelative + halfWidth);\n\t\t}\n\t};\n\n\tconst Relative rel = { x0, isEndMarker ? 1 : -1, y0, 1, widthStroke / 2.0f };\n\n\t// arrow head\n\tconst Point head[] = {\n\t\trel.At(xa + dy, y - dy),\n\t\trel.At(xa, y),\n\t\trel.At(xa + dy + extraFinalPixel, y + dy + extraFinalPixel)\n\t};\n\tsurface->PolyLine(head, std::size(head), Stroke(wrapColour, widthStroke));\n\n\t// arrow body\n\tconst Point body[] = {\n\t\trel.At(xa, y),\n\t\trel.At(xa + w, y),\n\t\trel.At(xa + w, y - 2 * dy),\n\t\trel.At(xa, y - 2 * dy),\n\t};\n\tsurface->PolyLine(body, std::size(body), Stroke(wrapColour, widthStroke));\n}\n\nMarginView::MarginView() noexcept {\n\twrapMarkerPaddingRight = 3;\n\tcustomDrawWrapMarker = nullptr;\n}\n\nvoid MarginView::DropGraphics() noexcept {\n\tpixmapSelMargin.reset();\n\tpixmapSelPattern.reset();\n\tpixmapSelPatternOffset1.reset();\n}\n\nvoid MarginView::RefreshPixMaps(Surface *surfaceWindow, const ViewStyle &vsDraw) {\n\tif (!(pixmapSelPattern && pixmapSelPatternOffset1)) {\n\t\tconstexpr int patternSize = 8;\n\t\tpixmapSelPattern = surfaceWindow->AllocatePixMap(patternSize, patternSize);\n\t\tpixmapSelPatternOffset1 = surfaceWindow->AllocatePixMap(patternSize, patternSize);\n\t\t// This complex procedure is to reproduce the checkerboard dithered pattern used by windows\n\t\t// for scroll bars and Visual Studio for its selection margin. The colour of this pattern is half\n\t\t// way between the chrome colour and the chrome highlight colour making a nice transition\n\t\t// between the window chrome and the content area. And it works in low colour depths.\n\t\tconst PRectangle rcPattern = PRectangle::FromInts(0, 0, patternSize, patternSize);\n\n\t\t// Initialize default colours based on the chrome colour scheme.  Typically the highlight is white.\n\t\tColourRGBA colourFMFill = vsDraw.selbar;\n\t\tColourRGBA colourFMStripes = vsDraw.selbarlight;\n\n\t\tif (!(vsDraw.selbarlight == white)) {\n\t\t\t// User has chosen an unusual chrome colour scheme so just use the highlight edge colour.\n\t\t\t// (Typically, the highlight colour is white.)\n\t\t\tcolourFMFill = vsDraw.selbarlight;\n\t\t}\n\n\t\tif (vsDraw.foldmarginColour) {\n\t\t\t// override default fold margin colour\n\t\t\tcolourFMFill = *vsDraw.foldmarginColour;\n\t\t}\n\t\tif (vsDraw.foldmarginHighlightColour) {\n\t\t\t// override default fold margin highlight colour\n\t\t\tcolourFMStripes = *vsDraw.foldmarginHighlightColour;\n\t\t}\n\n\t\tpixmapSelPattern->FillRectangle(rcPattern, colourFMFill);\n\t\tpixmapSelPatternOffset1->FillRectangle(rcPattern, colourFMStripes);\n\t\tfor (int y = 0; y < patternSize; y++) {\n\t\t\tfor (int x = y % 2; x < patternSize; x += 2) {\n\t\t\t\tconst PRectangle rcPixel = PRectangle::FromInts(x, y, x + 1, y + 1);\n\t\t\t\tpixmapSelPattern->FillRectangle(rcPixel, colourFMStripes);\n\t\t\t\tpixmapSelPatternOffset1->FillRectangle(rcPixel, colourFMFill);\n\t\t\t}\n\t\t}\n\t\tpixmapSelPattern->FlushDrawing();\n\t\tpixmapSelPatternOffset1->FlushDrawing();\n\t}\n}\n\nnamespace {\n\nMarkerOutline SubstituteMarkerIfEmpty(MarkerOutline markerCheck, MarkerOutline markerDefault, const ViewStyle &vs) noexcept {\n\tif (vs.markers[static_cast<size_t>(markerCheck)].markType == MarkerSymbol::Empty)\n\t\treturn markerDefault;\n\treturn markerCheck;\n}\n\nconstexpr MarkerOutline TailFromNextLevel(FoldLevel levelNextNum) noexcept {\n\treturn (levelNextNum > FoldLevel::Base) ? MarkerOutline::FolderMidTail : MarkerOutline::FolderTail;\n}\n\nint FoldingMark(FoldLevel level, FoldLevel levelNext, bool firstSubLine, bool lastSubLine,\n\tbool isExpanded, bool needWhiteClosure, MarkerOutline folderOpenMid, MarkerOutline folderEnd) noexcept {\n\n\tconst FoldLevel levelNum = LevelNumberPart(level);\n\tconst FoldLevel levelNextNum = LevelNumberPart(levelNext);\n\n\tif (LevelIsHeader(level)) {\n\t\tif (firstSubLine) {\n\t\t\tif (levelNum < levelNextNum) {\n\t\t\t\tif (levelNum == FoldLevel::Base) {\n\t\t\t\t\treturn 1 << (isExpanded ? MarkerOutline::FolderOpen : MarkerOutline::Folder);\n\t\t\t\t} else {\n\t\t\t\t\treturn 1 << (isExpanded ? folderOpenMid : folderEnd);\n\t\t\t\t}\n\t\t\t} else if (levelNum > FoldLevel::Base) {\n\t\t\t\treturn 1 << MarkerOutline::FolderSub;\n\t\t\t}\n\t\t} else {\n\t\t\tif (levelNum < levelNextNum) {\n\t\t\t\tif (isExpanded) {\n\t\t\t\t\treturn 1 << MarkerOutline::FolderSub;\n\t\t\t\t} else if (levelNum > FoldLevel::Base) {\n\t\t\t\t\treturn 1 << MarkerOutline::FolderSub;\n\t\t\t\t}\n\t\t\t} else if (levelNum > FoldLevel::Base) {\n\t\t\t\treturn 1 << MarkerOutline::FolderSub;\n\t\t\t}\n\t\t}\n\t} else if (LevelIsWhitespace(level)) {\n\t\tif (needWhiteClosure) {\n\t\t\tif (LevelIsWhitespace(levelNext)) {\n\t\t\t\treturn 1 << MarkerOutline::FolderSub;\n\t\t\t} else {\n\t\t\t\treturn 1 << TailFromNextLevel(levelNextNum);\n\t\t\t}\n\t\t} else if (levelNum > FoldLevel::Base) {\n\t\t\tif (levelNextNum < levelNum) {\n\t\t\t\treturn 1 << TailFromNextLevel(levelNextNum);\n\t\t\t} else {\n\t\t\t\treturn 1 << MarkerOutline::FolderSub;\n\t\t\t}\n\t\t}\n\t} else if (levelNum > FoldLevel::Base) {\n\t\tif (levelNextNum < levelNum) {\n\t\t\tif (LevelIsWhitespace(levelNext)) {\n\t\t\t\treturn 1 << MarkerOutline::FolderSub;\n\t\t\t} else if (lastSubLine) {\n\t\t\t\treturn 1 << TailFromNextLevel(levelNextNum);\n\t\t\t} else {\n\t\t\t\treturn 1 << MarkerOutline::FolderSub;\n\t\t\t}\n\t\t} else {\n\t\t\treturn 1 << MarkerOutline::FolderSub;\n\t\t}\n\t}\n\n\t// No folding mark on this line\n\treturn 0;\n}\n\nLineMarker::FoldPart PartForFoldHighlight(const HighlightDelimiter &highlightDelimiter, Sci::Line lineDoc, bool firstSubLine, bool headWithTail, bool isExpanded) noexcept {\n\tif (highlightDelimiter.IsFoldBlockHighlighted(lineDoc)) {\n\t\tif (highlightDelimiter.IsBodyOfFoldBlock(lineDoc)) {\n\t\t\treturn LineMarker::FoldPart::body;\n\t\t}\n\t\tif (highlightDelimiter.IsHeadOfFoldBlock(lineDoc)) {\n\t\t\tif (firstSubLine) {\n\t\t\t\treturn headWithTail ? LineMarker::FoldPart::headWithTail : LineMarker::FoldPart::head;\n\t\t\t}\n\t\t\tif (isExpanded || headWithTail) {\n\t\t\t\treturn LineMarker::FoldPart::body;\n\t\t\t}\n\t\t} else if (highlightDelimiter.IsTailOfFoldBlock(lineDoc)) {\n\t\t\treturn LineMarker::FoldPart::tail;\n\t\t}\n\t}\n\treturn LineMarker::FoldPart::undefined;\n}\n\nconstexpr LineMarker::FoldPart PartForBar(bool markBefore, bool markAfter) {\n\tif (markBefore) {\n\t\tif (markAfter) {\n\t\t\treturn LineMarker::FoldPart::body;\n\t\t}\n\t\treturn LineMarker::FoldPart::tail;\n\t}\n\tif (markAfter) {\n\t\treturn LineMarker::FoldPart::head;\n\t}\n\treturn LineMarker::FoldPart::headWithTail;\n}\n\n}\n\nvoid MarginView::PaintOneMargin(Surface *surface, PRectangle rc, PRectangle rcOneMargin, const MarginStyle &marginStyle,\n\tconst EditModel &model, const ViewStyle &vs /*Au*/, int margin) const {\n\n\t//Au:\n\tint cdFirstLine = -1, cdLastLine = 0;\n\n\tconst Point ptOrigin = model.GetVisibleOriginInMain();\n\tconst Sci::Line lineStartPaint = static_cast<Sci::Line>(rcOneMargin.top + ptOrigin.y) / vs.lineHeight;\n\tSci::Line visibleLine = model.TopLineOfMain() + lineStartPaint;\n\tXYPOSITION yposScreen = static_cast<XYPOSITION>(lineStartPaint * vs.lineHeight) - ptOrigin.y;\n\t// Work out whether the top line is whitespace located after a\n\t// lessening of fold level which implies a 'fold tail' but which should not\n\t// be displayed until the last of a sequence of whitespace.\n\tbool needWhiteClosure = false;\n\tif (marginStyle.ShowsFolding()) {\n\t\tconst FoldLevel level = model.pdoc->GetFoldLevel(model.pcs->DocFromDisplay(visibleLine));\n\t\tif (LevelIsWhitespace(level)) {\n\t\t\tSci::Line lineBack = model.pcs->DocFromDisplay(visibleLine);\n\t\t\tFoldLevel levelPrev = level;\n\t\t\twhile ((lineBack > 0) && LevelIsWhitespace(levelPrev)) {\n\t\t\t\tlineBack--;\n\t\t\t\tlevelPrev = model.pdoc->GetFoldLevel(lineBack);\n\t\t\t}\n\t\t\tif (!LevelIsHeader(levelPrev)) {\n\t\t\t\tif (LevelNumber(level) < LevelNumber(levelPrev))\n\t\t\t\t\tneedWhiteClosure = true;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Old code does not know about new markers needed to distinguish all cases\n\tconst MarkerOutline folderOpenMid = SubstituteMarkerIfEmpty(MarkerOutline::FolderOpenMid,\n\t\tMarkerOutline::FolderOpen, vs);\n\tconst MarkerOutline folderEnd = SubstituteMarkerIfEmpty(MarkerOutline::FolderEnd,\n\t\tMarkerOutline::Folder, vs);\n\n\twhile ((visibleLine < model.pcs->LinesDisplayed()) && yposScreen < rc.bottom) {\n\n\t\tPLATFORM_ASSERT(visibleLine < model.pcs->LinesDisplayed());\n\t\tconst Sci::Line lineDoc = model.pcs->DocFromDisplay(visibleLine);\n\t\tPLATFORM_ASSERT((lineDoc == 0) || model.pcs->GetVisible(lineDoc));\n\t\tconst Sci::Line firstVisibleLine = model.pcs->DisplayFromDoc(lineDoc);\n\t\tconst Sci::Line lastVisibleLine = model.pcs->DisplayLastFromDoc(lineDoc);\n\t\tconst bool firstSubLine = visibleLine == firstVisibleLine;\n\t\tconst bool lastSubLine = visibleLine == lastVisibleLine;\n\n\t\t//Au:\n\t\tif(firstSubLine) {\n\t\t\tcdLastLine = (int)lineDoc; if(cdFirstLine < 0) cdFirstLine = cdLastLine;\n\t\t}\n\n\t\tint marks = model.GetMark(lineDoc);\n\t\tif (!firstSubLine) {\n\t\t\t// Mask off non-continuing marks\n\t\t\tmarks = marks & vs.maskDrawWrapped;\n\t\t}\n\n\t\tbool headWithTail = false;\n\t\tbool isExpanded = false;\n\n\t\tif (marginStyle.ShowsFolding()) {\n\t\t\t// Decide which fold indicator should be displayed\n\t\t\tconst FoldLevel level = model.pdoc->GetFoldLevel(lineDoc);\n\t\t\tconst FoldLevel levelNext = model.pdoc->GetFoldLevel(lineDoc + 1);\n\t\t\tisExpanded = model.pcs->GetExpanded(lineDoc);\n\n\t\t\tmarks |= FoldingMark(level, levelNext, firstSubLine, lastSubLine,\n\t\t\t\tisExpanded, needWhiteClosure, folderOpenMid, folderEnd);\n\n\t\t\tconst FoldLevel levelNum = LevelNumberPart(level);\n\t\t\tconst FoldLevel levelNextNum = LevelNumberPart(levelNext);\n\n\t\t\t// Change needWhiteClosure and headWithTail if needed\n\t\t\tif (LevelIsHeader(level)) {\n\t\t\t\tneedWhiteClosure = false;\n\t\t\t\tif (!isExpanded) {\n\t\t\t\t\tconst Sci::Line firstFollowupLine = model.pcs->DocFromDisplay(model.pcs->DisplayFromDoc(lineDoc + 1));\n\t\t\t\t\tconst FoldLevel firstFollowupLineLevel = model.pdoc->GetFoldLevel(firstFollowupLine);\n\t\t\t\t\tconst FoldLevel secondFollowupLineLevelNum = LevelNumberPart(model.pdoc->GetFoldLevel(firstFollowupLine + 1));\n\t\t\t\t\tif (LevelIsWhitespace(firstFollowupLineLevel) &&\n\t\t\t\t\t\t(levelNum > secondFollowupLineLevelNum))\n\t\t\t\t\t\tneedWhiteClosure = true;\n\n\t\t\t\t\tif (highlightDelimiter.IsFoldBlockHighlighted(firstFollowupLine))\n\t\t\t\t\t\theadWithTail = true;\n\t\t\t\t}\n\t\t\t} else if (LevelIsWhitespace(level)) {\n\t\t\t\tif (needWhiteClosure) {\n\t\t\t\t\tneedWhiteClosure = LevelIsWhitespace(levelNext);\n\t\t\t\t}\n\t\t\t} else if (levelNum > FoldLevel::Base) {\n\t\t\t\tif (levelNextNum < levelNum) {\n\t\t\t\t\tneedWhiteClosure = LevelIsWhitespace(levelNext);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst PRectangle rcMarker(\n\t\t\trcOneMargin.left,\n\t\t\typosScreen,\n\t\t\trcOneMargin.right,\n\t\t\typosScreen + vs.lineHeight);\n\t\tif (marginStyle.style == MarginType::Number) {\n\t\t\tif (firstSubLine) {\n\t\t\t\tstd::string sNumber;\n\t\t\t\tif (lineDoc >= 0) {\n\t\t\t\t\tsNumber = std::to_string(lineDoc + 1);\n\t\t\t\t}\n\t\t\t\tif (FlagSet(model.foldFlags, (FoldFlag::LevelNumbers | FoldFlag::LineState))) {\n\t\t\t\t\tchar number[100] = \"\";\n\t\t\t\t\tif (FlagSet(model.foldFlags, FoldFlag::LevelNumbers)) {\n\t\t\t\t\t\tconst FoldLevel lev = model.pdoc->GetFoldLevel(lineDoc);\n\t\t\t\t\t\tsnprintf(number,std::size(number), \"%c%c %03X %03X\",\n\t\t\t\t\t\t\tLevelIsHeader(lev) ? 'H' : '_',\n\t\t\t\t\t\t\tLevelIsWhitespace(lev) ? 'W' : '_',\n\t\t\t\t\t\t\tLevelNumber(lev),\n\t\t\t\t\t\t\tstatic_cast<unsigned int>(lev) >> 16\n\t\t\t\t\t\t);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tconst int state = model.pdoc->GetLineState(lineDoc);\n\t\t\t\t\t\tsnprintf(number, std::size(number), \"%0X\", state);\n\t\t\t\t\t}\n\t\t\t\t\tsNumber = number;\n\t\t\t\t}\n\t\t\t\tPRectangle rcNumber = rcMarker;\n\t\t\t\t// Right justify\n\t\t\t\tconst XYPOSITION width = surface->WidthText(vs.styles[StyleLineNumber].font.get(), sNumber);\n\t\t\t\tconst XYPOSITION xpos = rcNumber.right - width - vs.marginNumberPadding;\n\t\t\t\trcNumber.left = xpos;\n\t\t\t\tDrawTextNoClipPhase(surface, rcNumber, vs.styles[StyleLineNumber],\n\t\t\t\t\trcNumber.top + vs.maxAscent, sNumber, DrawPhase::all);\n\t\t\t} else if (FlagSet(vs.wrap.visualFlags, WrapVisualFlag::Margin)) {\n\t\t\t\tPRectangle rcWrapMarker = rcMarker;\n\t\t\t\trcWrapMarker.right -= wrapMarkerPaddingRight;\n\t\t\t\trcWrapMarker.left = rcWrapMarker.right - vs.styles[StyleLineNumber].aveCharWidth;\n\t\t\t\tif (!customDrawWrapMarker) {\n\t\t\t\t\tDrawWrapMarker(surface, rcWrapMarker, false, vs.styles[StyleLineNumber].fore);\n\t\t\t\t} else {\n\t\t\t\t\tcustomDrawWrapMarker(surface, rcWrapMarker, false, vs.styles[StyleLineNumber].fore);\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (marginStyle.style == MarginType::Text || marginStyle.style == MarginType::RText) {\n\t\t\tconst StyledText stMargin = model.pdoc->MarginStyledText(lineDoc);\n\t\t\tif (stMargin.text && ValidStyledText(vs, vs.marginStyleOffset, stMargin)) {\n\t\t\t\tif (firstSubLine) {\n\t\t\t\t\tsurface->FillRectangle(rcMarker,\n\t\t\t\t\t\tvs.styles[stMargin.StyleAt(0) + vs.marginStyleOffset].back);\n\t\t\t\t\tPRectangle rcText = rcMarker;\n\t\t\t\t\tif (marginStyle.style == MarginType::RText) {\n\t\t\t\t\t\tconst int width = WidestLineWidth(surface, vs, vs.marginStyleOffset, stMargin);\n\t\t\t\t\t\trcText.left = rcText.right - width - 3;\n\t\t\t\t\t}\n\t\t\t\t\tDrawStyledText(surface, vs, vs.marginStyleOffset, rcText,\n\t\t\t\t\t\tstMargin, 0, stMargin.length, DrawPhase::all);\n\t\t\t\t} else {\n\t\t\t\t\t// if we're displaying annotation lines, colour the margin to match the associated document line\n\t\t\t\t\tconst int annotationLines = model.pdoc->AnnotationLines(lineDoc);\n\t\t\t\t\tif (annotationLines && (visibleLine > lastVisibleLine - annotationLines)) {\n\t\t\t\t\t\tsurface->FillRectangle(rcMarker, vs.styles[stMargin.StyleAt(0) + vs.marginStyleOffset].back);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tmarks &= marginStyle.mask;\n\n\t\tif (marks) {\n\t\t\t// Draw all the bar markers first so they are underneath as they often cover\n\t\t\t// multiple lines for change history and other markers mark individual lines.\n\t\t\tint marksBar = marks;\n\t\t\tfor (int markBit = 0; (markBit <= MarkerMax) && marksBar; markBit++) {\n\t\t\t\tif ((marksBar & 1) && (vs.markers[markBit].markType == MarkerSymbol::Bar)) {\n\t\t\t\t\tconst int mask = 1 << markBit;\n\t\t\t\t\tconst bool markBefore = firstSubLine ? (model.GetMark(lineDoc - 1) & mask) : true;\n\t\t\t\t\tconst bool markAfter = lastSubLine ? (model.GetMark(lineDoc + 1) & mask) : true;\n\t\t\t\t\tvs.markers[markBit].Draw(surface, rcMarker, vs.styles[StyleLineNumber].font.get(),\n\t\t\t\t\t\tPartForBar(markBefore, markAfter), marginStyle.style);\n\t\t\t\t}\n\t\t\t\tmarksBar >>= 1;\n\t\t\t}\n\t\t\t// Draw all the other markers over the bar markers\n\t\t\tfor (int markBit = 0; (markBit <= MarkerMax) && marks; markBit++) {\n\t\t\t\tif ((marks & 1) && (vs.markers[markBit].markType != MarkerSymbol::Bar)) {\n\t\t\t\t\tconst LineMarker::FoldPart part = marginStyle.ShowsFolding() ?\n\t\t\t\t\t\tPartForFoldHighlight(highlightDelimiter, lineDoc, firstSubLine, headWithTail, isExpanded) :\n\t\t\t\t\t\tLineMarker::FoldPart::undefined;\n\t\t\t\t\tvs.markers[markBit].Draw(surface, rcMarker, vs.styles[StyleLineNumber].font.get(), part, marginStyle.style);\n\t\t\t\t}\n\t\t\t\tmarks >>= 1;\n\t\t\t}\n\t\t}\n\n\t\tvisibleLine++;\n\t\typosScreen += vs.lineHeight;\n\t}\n\n\t//Au: call callback\n\tif(model.cbMarginDraw && 0 != ((model.cbMarginsMask >> (int)margin) & 1) && cdFirstLine >= 0) {\n\t\tSci_MarginDrawCallbackData c = {};\n\t\tc.margin = (int)margin;\n\t\tc.firstLine = cdFirstLine;\n\t\tc.lastLine = cdLastLine;\n\t\tc.rect.left = (int)rcOneMargin.left;\n\t\tc.rect.top = (int)rcOneMargin.top;\n\t\tc.rect.right = (int)rcOneMargin.right;\n\t\tc.rect.bottom = (int)rcOneMargin.bottom;\n\t\tc.hdc = surface->get_hdc();\n\t\tmodel.cbMarginDraw(c);\n\t}\n}\n\nvoid MarginView::PaintMargin(Surface *surface, Sci::Line topLine, PRectangle rc, PRectangle rcMargin,\n\tconst EditModel &model, const ViewStyle &vs) {\n\n\tPRectangle rcOneMargin = rcMargin;\n\trcOneMargin.right = rcMargin.left;\n\tif (rcOneMargin.bottom < rc.bottom)\n\t\trcOneMargin.bottom = rc.bottom;\n\n\tconst Point ptOrigin = model.GetVisibleOriginInMain();\n\tint margin = -1; //Au\n\tfor (const MarginStyle &marginStyle : vs.ms) {\n\t\tmargin++; //Au\n\t\tif (marginStyle.width > 0) {\n\n\t\t\trcOneMargin.left = rcOneMargin.right;\n\t\t\trcOneMargin.right = rcOneMargin.left + marginStyle.width;\n\n\t\t\tif (marginStyle.style != MarginType::Number) {\n\t\t\t\tif (marginStyle.ShowsFolding()) {\n\t\t\t\t\t// Required because of special way brush is created for selection margin\n\t\t\t\t\t// Ensure patterns line up when scrolling with separate margin view\n\t\t\t\t\t// by choosing correctly aligned variant.\n\t\t\t\t\tconst bool invertPhase = static_cast<int>(ptOrigin.y) & 1;\n\t\t\t\t\tsurface->FillRectangle(rcOneMargin,\n\t\t\t\t\t\tinvertPhase ? *pixmapSelPattern : *pixmapSelPatternOffset1);\n\t\t\t\t} else {\n\t\t\t\t\tColourRGBA colour;\n\t\t\t\t\tswitch (marginStyle.style) {\n\t\t\t\t\tcase MarginType::Back:\n\t\t\t\t\t\tcolour = vs.styles[StyleDefault].back;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase MarginType::Fore:\n\t\t\t\t\t\tcolour = vs.styles[StyleDefault].fore;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase MarginType::Colour:\n\t\t\t\t\t\tcolour = marginStyle.back;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tcolour = vs.styles[StyleLineNumber].back;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tsurface->FillRectangle(rcOneMargin, colour);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tsurface->FillRectangle(rcOneMargin, vs.styles[StyleLineNumber].back);\n\t\t\t}\n\n\t\t\tif (marginStyle.ShowsFolding() && highlightDelimiter.isEnabled) {\n\t\t\t\tconst Sci::Line lastLine = model.pcs->DocFromDisplay(topLine + model.LinesOnScreen()) + 1;\n\t\t\t\tmodel.pdoc->GetHighlightDelimiters(highlightDelimiter,\n\t\t\t\t\tmodel.pdoc->SciLineFromPosition(model.sel.MainCaret()), lastLine);\n\t\t\t}\n\n\t\t\tPaintOneMargin(surface, rc, rcOneMargin, marginStyle, model, vs /*Au*/, margin); //Au\n\t\t}\n\t}\n\n\tPRectangle rcBlankMargin = rcMargin;\n\trcBlankMargin.left = rcOneMargin.right;\n\tsurface->FillRectangle(rcBlankMargin, vs.styles[StyleDefault].back);\n}\n\n}\n\n"
  },
  {
    "path": "Libraries/scintilla/src/MarginView.h",
    "content": "// Scintilla source code edit control\n/** @file MarginView.h\n ** Defines the appearance of the editor margin.\n **/\n// Copyright 1998-2014 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#ifndef MARGINVIEW_H\n#define MARGINVIEW_H\n\nnamespace Scintilla::Internal {\n\nvoid DrawWrapMarker(Surface *surface, PRectangle rcPlace, bool isEndMarker, ColourRGBA wrapColour);\n\ntypedef void (*DrawWrapMarkerFn)(Surface *surface, PRectangle rcPlace, bool isEndMarker, ColourRGBA wrapColour);\n\n/**\n* MarginView draws the margins.\n*/\nclass MarginView {\npublic:\n\tstd::unique_ptr<Surface> pixmapSelMargin;\n\tstd::unique_ptr<Surface> pixmapSelPattern;\n\tstd::unique_ptr<Surface> pixmapSelPatternOffset1;\n\t// Highlight current folding block\n\tHighlightDelimiter highlightDelimiter;\n\n\tint wrapMarkerPaddingRight; // right-most pixel padding of wrap markers\n\t/** Some platforms, notably PLAT_CURSES, do not support Scintilla's native\n\t * DrawWrapMarker function for drawing wrap markers. Allow those platforms to\n\t * override it instead of creating a new method in the Surface class that\n\t * existing platforms must implement as empty. */\n\tDrawWrapMarkerFn customDrawWrapMarker;\n\n\tMarginView() noexcept;\n\n\tvoid DropGraphics() noexcept;\n\tvoid RefreshPixMaps(Surface *surfaceWindow, const ViewStyle &vsDraw);\n\tvoid PaintOneMargin(Surface *surface, PRectangle rc, PRectangle rcOneMargin, const MarginStyle &marginStyle,\n\t\tconst EditModel &model, const ViewStyle &vs /*Au*/, int margin) const;\n\tvoid PaintMargin(Surface *surface, Sci::Line topLine, PRectangle rc, PRectangle rcMargin,\n\t\tconst EditModel &model, const ViewStyle &vs);\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/src/Partitioning.h",
    "content": "// Scintilla source code edit control\n/** @file Partitioning.h\n ** Data structure used to partition an interval. Used for holding line start/end positions.\n **/\n// Copyright 1998-2007 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#ifndef PARTITIONING_H\n#define PARTITIONING_H\n\nnamespace Scintilla::Internal {\n\n/// Divide an interval into multiple partitions.\n/// Useful for breaking a document down into sections such as lines.\n/// A 0 length interval has a single 0 length partition, numbered 0\n/// If interval not 0 length then each partition non-zero length\n/// When needed, positions after the interval are considered part of the last partition\n/// but the end of the last partition can be found with PositionFromPartition(last+1).\n\ntemplate <typename T>\nclass Partitioning {\nprivate:\n\t// To avoid calculating all the partition positions whenever any text is inserted\n\t// there may be a step somewhere in the list.\n\tT stepPartition;\n\tT stepLength;\n\tSplitVector<T> body;\n\n\t// Deleted so SplitVectorWithRangeAdd objects can not be copied.\n\tvoid RangeAddDelta(T start, T end, T delta) noexcept {\n\t\t// end is 1 past end, so end-start is number of elements to change\n\t\tconst ptrdiff_t position = start;\n\t\tptrdiff_t i = 0;\n\t\tconst ptrdiff_t rangeLength = end - position;\n\t\tptrdiff_t range1Length = rangeLength;\n\t\tconst ptrdiff_t part1Left = body.GapPosition() - position;\n\t\tif (range1Length > part1Left)\n\t\t\trange1Length = part1Left;\n\t\tT *writer = &body[position];\n\t\twhile (i < range1Length) {\n\t\t\t*writer += delta;\n\t\t\twriter++;\n\t\t\ti++;\n\t\t}\n\t\tif (i < rangeLength) {\n\t\t\tT *writer2 = &body[position + i];\n\t\t\twhile (i < rangeLength) {\n\t\t\t\t*writer2 += delta;\n\t\t\t\twriter2++;\n\t\t\t\ti++;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Move step forward\n\tvoid ApplyStep(T partitionUpTo) noexcept {\n\t\tif (stepLength != 0) {\n\t\t\tRangeAddDelta(stepPartition+1, partitionUpTo + 1, stepLength);\n\t\t}\n\t\tstepPartition = partitionUpTo;\n\t\tif (stepPartition >= Partitions()) {\n\t\t\tstepPartition = Partitions();\n\t\t\tstepLength = 0;\n\t\t}\n\t}\n\n\t// Move step backward\n\tvoid BackStep(T partitionDownTo) noexcept {\n\t\tif (stepLength != 0) {\n\t\t\tRangeAddDelta(partitionDownTo+1, stepPartition+1, -stepLength);\n\t\t}\n\t\tstepPartition = partitionDownTo;\n\t}\n\npublic:\n\texplicit Partitioning(size_t growSize=8) : stepPartition(0), stepLength(0), body(growSize) {\n\t\tbody.Insert(0, 0);\t// This value stays 0 for ever\n\t\tbody.Insert(1, 0);\t// This is the end of the first partition and will be the start of the second\n\t}\n\n\tT Partitions() const noexcept {\n\t\treturn static_cast<T>(body.Length())-1;\n\t}\n\n\tvoid ReAllocate(ptrdiff_t newSize) {\n\t\t// + 1 accounts for initial element that is always 0.\n\t\tbody.ReAllocate(newSize + 1);\n\t}\n\n\tT Length() const noexcept {\n\t\treturn PositionFromPartition(Partitions());\n\t}\n\n\tvoid InsertPartition(T partition, T pos) {\n\t\tif (stepPartition < partition) {\n\t\t\tApplyStep(partition);\n\t\t}\n\t\tbody.Insert(partition, pos);\n\t\tstepPartition++;\n\t}\n\n\tvoid InsertPartitions(T partition, const T *positions, size_t length) {\n\t\tif (stepPartition < partition) {\n\t\t\tApplyStep(partition);\n\t\t}\n\t\tbody.InsertFromArray(partition, positions, 0, length);\n\t\tstepPartition += static_cast<T>(length);\n\t}\n\n\tvoid InsertPartitionsWithCast(T partition, const ptrdiff_t *positions, size_t length) {\n\t\t// Used for 64-bit builds when T is 32-bits\n\t\tif (stepPartition < partition) {\n\t\t\tApplyStep(partition);\n\t\t}\n\t\tT *pInsertion = body.InsertEmpty(partition, length);\n\t\tfor (size_t i = 0; i < length; i++) {\n\t\t\tpInsertion[i] = static_cast<T>(positions[i]);\n\t\t}\n\t\tstepPartition += static_cast<T>(length);\n\t}\n\n\tvoid SetPartitionStartPosition(T partition, T pos) noexcept {\n\t\tApplyStep(partition+1);\n\t\tif ((partition < 0) || (partition >= body.Length())) {\n\t\t\treturn;\n\t\t}\n\t\tbody.SetValueAt(partition, pos);\n\t}\n\n\tvoid InsertText(T partitionInsert, T delta) noexcept {\n\t\t// Point all the partitions after the insertion point further along in the buffer\n\t\tif (stepLength != 0) {\n\t\t\tif (partitionInsert >= stepPartition) {\n\t\t\t\t// Fill in up to the new insertion point\n\t\t\t\tApplyStep(partitionInsert);\n\t\t\t\tstepLength += delta;\n\t\t\t} else if (partitionInsert >= (stepPartition - body.Length() / 10)) {\n\t\t\t\t// Close to step but before so move step back\n\t\t\t\tBackStep(partitionInsert);\n\t\t\t\tstepLength += delta;\n\t\t\t} else {\n\t\t\t\tApplyStep(Partitions());\n\t\t\t\tstepPartition = partitionInsert;\n\t\t\t\tstepLength = delta;\n\t\t\t}\n\t\t} else {\n\t\t\tstepPartition = partitionInsert;\n\t\t\tstepLength = delta;\n\t\t}\n\t}\n\n\tvoid RemovePartition(T partition) {\n\t\tif (partition > stepPartition) {\n\t\t\tApplyStep(partition);\n\t\t\tstepPartition--;\n\t\t} else {\n\t\t\tstepPartition--;\n\t\t}\n\t\tbody.Delete(partition);\n\t}\n\n\tT PositionFromPartition(T partition) const noexcept {\n\t\tPLATFORM_ASSERT(partition >= 0);\n\t\tPLATFORM_ASSERT(partition < body.Length());\n\t\tconst ptrdiff_t lengthBody = body.Length();\n\t\tif ((partition < 0) || (partition >= lengthBody)) {\n\t\t\treturn 0;\n\t\t}\n\t\tT pos = body.ValueAt(partition);\n\t\tif (partition > stepPartition)\n\t\t\tpos += stepLength;\n\t\treturn pos;\n\t}\n\n\t/// Return value in range [0 .. Partitions() - 1] even for arguments outside interval\n\tT PartitionFromPosition(T pos) const noexcept {\n\t\tif (body.Length() <= 1)\n\t\t\treturn 0;\n\t\tif (pos >= (PositionFromPartition(Partitions())))\n\t\t\treturn Partitions() - 1;\n\t\tT lower = 0;\n\t\tT upper = Partitions();\n\t\tdo {\n\t\t\tconst T middle = (upper + lower + 1) / 2; \t// Round high\n\t\t\tT posMiddle = body[middle];\n\t\t\tif (middle > stepPartition)\n\t\t\t\tposMiddle += stepLength;\n\t\t\tif (pos < posMiddle) {\n\t\t\t\tupper = middle - 1;\n\t\t\t} else {\n\t\t\t\tlower = middle;\n\t\t\t}\n\t\t} while (lower < upper);\n\t\treturn lower;\n\t}\n\n\tvoid DeleteAll() {\n\t\tbody.DeleteAll();\n\t\tstepPartition = 0;\n\t\tstepLength = 0;\n\t\tbody.Insert(0, 0);\t// This value stays 0 for ever\n\t\tbody.Insert(1, 0);\t// This is the end of the first partition and will be the start of the second\n\t}\n\n\tvoid Check() const {\n#ifdef CHECK_CORRECTNESS\n\t\tif (Length() < 0) {\n\t\t\tthrow std::runtime_error(\"Partitioning: Length can not be negative.\");\n\t\t}\n\t\tif (Partitions() < 1) {\n\t\t\tthrow std::runtime_error(\"Partitioning: Must always have 1 or more partitions.\");\n\t\t}\n\t\tif (Length() == 0) {\n\t\t\tif ((PositionFromPartition(0) != 0) || (PositionFromPartition(1) != 0)) {\n\t\t\t\tthrow std::runtime_error(\"Partitioning: Invalid empty partitioning.\");\n\t\t\t}\n\t\t} else {\n\t\t\t// Positions should be a strictly ascending sequence\n\t\t\tfor (T i = 0; i < Partitions(); i++) {\n\t\t\t\tconst T pos = PositionFromPartition(i);\n\t\t\t\tconst T posNext = PositionFromPartition(i+1);\n\t\t\t\tif (pos > posNext) {\n\t\t\t\t\tthrow std::runtime_error(\"Partitioning: Negative partition.\");\n\t\t\t\t} else if (pos == posNext) {\n\t\t\t\t\tthrow std::runtime_error(\"Partitioning: Empty partition.\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n#endif\n\t}\n\n};\n\n\n}\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/src/PerLine.cxx",
    "content": "// Scintilla source code edit control\n/** @file PerLine.cxx\n ** Manages data associated with each line of the document\n **/\n// Copyright 1998-2009 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#include <cstddef>\n#include <cstdint>\n#include <cassert>\n#include <cstring>\n\n#include <stdexcept>\n#include <string_view>\n#include <vector>\n#include <forward_list>\n#include <optional>\n#include <algorithm>\n#include <memory>\n\n#include \"ScintillaTypes.h\"\n\n#include \"Debugging.h\"\n#include \"Geometry.h\"\n#include \"Platform.h\"\n\n#include \"Position.h\"\n#include \"SplitVector.h\"\n#include \"Partitioning.h\"\n#include \"CellBuffer.h\"\n#include \"PerLine.h\"\n\nusing namespace Scintilla::Internal;\n\nMarkerHandleSet::MarkerHandleSet() {\n}\n\nbool MarkerHandleSet::Empty() const noexcept {\n\treturn mhList.empty();\n}\n\nint MarkerHandleSet::MarkValue() const noexcept {\n\tunsigned int m = 0;\n\tfor (const MarkerHandleNumber &mhn : mhList) {\n\t\tm |= (1 << mhn.number);\n\t}\n\treturn m;\n}\n\nbool MarkerHandleSet::Contains(int handle) const noexcept {\n\tfor (const MarkerHandleNumber &mhn : mhList) {\n\t\tif (mhn.handle == handle) {\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\nMarkerHandleNumber const *MarkerHandleSet::GetMarkerHandleNumber(int which) const noexcept {\n\tfor (const MarkerHandleNumber &mhn : mhList) {\n\t\tif (which == 0)\n\t\t\treturn &mhn;\n\t\twhich--;\n\t}\n\treturn nullptr;\n}\n\nbool MarkerHandleSet::InsertHandle(int handle, int markerNum) {\n\tmhList.push_front(MarkerHandleNumber(handle, markerNum));\n\treturn true;\n}\n\nvoid MarkerHandleSet::RemoveHandle(int handle) {\n\tmhList.remove_if([handle](const MarkerHandleNumber &mhn) noexcept { return mhn.handle == handle; });\n}\n\nbool MarkerHandleSet::RemoveNumber(int markerNum, bool all) {\n\tbool performedDeletion = false;\n\tmhList.remove_if([&](const MarkerHandleNumber &mhn) noexcept {\n\t\tif ((all || !performedDeletion) && (mhn.number == markerNum)) {\n\t\t\tperformedDeletion = true;\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t});\n\treturn performedDeletion;\n}\n\nvoid MarkerHandleSet::CombineWith(MarkerHandleSet *other) noexcept {\n\tmhList.splice_after(mhList.before_begin(), other->mhList);\n}\n\nvoid LineMarkers::Init() {\n\tmarkers.DeleteAll();\n}\n\nvoid LineMarkers::InsertLine(Sci::Line line) {\n\tif (markers.Length()) {\n\t\tmarkers.Insert(line, nullptr);\n\t}\n}\n\nvoid LineMarkers::InsertLines(Sci::Line line, Sci::Line lines) {\n\tif (markers.Length()) {\n\t\tmarkers.InsertEmpty(line, lines);\n\t}\n}\n\nvoid LineMarkers::RemoveLine(Sci::Line line) {\n\t// Retain the markers from the deleted line by oring them into the previous line\n\tif (markers.Length()) {\n\t\tif (line > 0) {\n\t\t\tMergeMarkers(line - 1);\n\t\t}\n\t\tmarkers.Delete(line);\n\t}\n}\n\nSci::Line LineMarkers::LineFromHandle(int markerHandle) const noexcept {\n\tfor (Sci::Line line = 0; line < markers.Length(); line++) {\n\t\tif (markers[line] && markers[line]->Contains(markerHandle)) {\n\t\t\treturn line;\n\t\t}\n\t}\n\treturn -1;\n}\n\nint LineMarkers::HandleFromLine(Sci::Line line, int which) const noexcept {\n\tif (markers.Length() && (line >= 0) && (line < markers.Length()) && markers[line]) {\n\t\tMarkerHandleNumber const *pnmh = markers[line]->GetMarkerHandleNumber(which);\n\t\treturn pnmh ? pnmh->handle : -1;\n\t}\n\treturn -1;\n}\n\nint LineMarkers::NumberFromLine(Sci::Line line, int which) const noexcept {\n\tif (markers.Length() && (line >= 0) && (line < markers.Length()) && markers[line]) {\n\t\tMarkerHandleNumber const *pnmh = markers[line]->GetMarkerHandleNumber(which);\n\t\treturn pnmh ? pnmh->number : -1;\n\t}\n\treturn -1;\n}\n\nvoid LineMarkers::MergeMarkers(Sci::Line line) {\n\tif (markers[line + 1]) {\n\t\tif (!markers[line])\n\t\t\tmarkers[line] = std::make_unique<MarkerHandleSet>();\n\t\tmarkers[line]->CombineWith(markers[line + 1].get());\n\t\tmarkers[line + 1].reset();\n\t}\n}\n\nint LineMarkers::MarkValue(Sci::Line line) const noexcept {\n\tif (markers.Length() && (line >= 0) && (line < markers.Length()) && markers[line])\n\t\treturn markers[line]->MarkValue();\n\telse\n\t\treturn 0;\n}\n\nSci::Line LineMarkers::MarkerNext(Sci::Line lineStart, int mask) const noexcept {\n\tif (lineStart < 0)\n\t\tlineStart = 0;\n\tconst Sci::Line length = markers.Length();\n\tfor (Sci::Line iLine = lineStart; iLine < length; iLine++) {\n\t\tconst MarkerHandleSet *onLine = markers[iLine].get();\n\t\tif (onLine && ((onLine->MarkValue() & mask) != 0))\n\t\t\treturn iLine;\n\t}\n\treturn -1;\n}\n\nint LineMarkers::AddMark(Sci::Line line, int markerNum, Sci::Line lines) {\n\thandleCurrent++;\n\tif (!markers.Length()) {\n\t\t// No existing markers so allocate one element per line\n\t\tmarkers.InsertEmpty(0, lines);\n\t}\n\tif (line >= markers.Length()) {\n\t\treturn -1;\n\t}\n\tif (!markers[line]) {\n\t\t// Need new structure to hold marker handle\n\t\tmarkers[line] = std::make_unique<MarkerHandleSet>();\n\t}\n\tmarkers[line]->InsertHandle(handleCurrent, markerNum);\n\n\treturn handleCurrent;\n}\n\nbool LineMarkers::DeleteMark(Sci::Line line, int markerNum, bool all) {\n\tbool someChanges = false;\n\tif (markers.Length() && (line >= 0) && (line < markers.Length()) && markers[line]) {\n\t\tif (markerNum == -1) {\n\t\t\tsomeChanges = true;\n\t\t\tmarkers[line].reset();\n\t\t} else {\n\t\t\tsomeChanges = markers[line]->RemoveNumber(markerNum, all);\n\t\t\tif (markers[line]->Empty()) {\n\t\t\t\tmarkers[line].reset();\n\t\t\t}\n\t\t}\n\t}\n\treturn someChanges;\n}\n\nvoid LineMarkers::DeleteMarkFromHandle(int markerHandle) {\n\tconst Sci::Line line = LineFromHandle(markerHandle);\n\tif (line >= 0) {\n\t\tmarkers[line]->RemoveHandle(markerHandle);\n\t\tif (markers[line]->Empty()) {\n\t\t\tmarkers[line].reset();\n\t\t}\n\t}\n}\n\nvoid LineLevels::Init() {\n\tlevels.DeleteAll();\n}\n\nvoid LineLevels::InsertLine(Sci::Line line) {\n\tif (levels.Length()) {\n\t\tconst int level = (line < levels.Length()) ? levels[line] : static_cast<int>(Scintilla::FoldLevel::Base);\n\t\tlevels.Insert(line, level);\n\t}\n}\n\nvoid LineLevels::InsertLines(Sci::Line line, Sci::Line lines) {\n\tif (levels.Length()) {\n\t\tconst int level = (line < levels.Length()) ? levels[line] : static_cast<int>(Scintilla::FoldLevel::Base);\n\t\tlevels.InsertValue(line, lines, level);\n\t}\n}\n\nvoid LineLevels::RemoveLine(Sci::Line line) {\n\tif (levels.Length()) {\n\t\t// Move up following lines but merge header flag from this line\n\t\t// to line before to avoid a temporary disappearance causing expansion.\n\t\tint firstHeader = levels[line] & static_cast<int>(Scintilla::FoldLevel::HeaderFlag);\n\t\tlevels.Delete(line);\n\t\tif (line == levels.Length()-1) // Last line loses the header flag\n\t\t\tlevels[line-1] &= ~static_cast<int>(Scintilla::FoldLevel::HeaderFlag);\n\t\telse if (line > 0)\n\t\t\tlevels[line-1] |= firstHeader;\n\t}\n}\n\nvoid LineLevels::ExpandLevels(Sci::Line sizeNew) {\n\tlevels.InsertValue(levels.Length(), sizeNew - levels.Length(), static_cast<int>(Scintilla::FoldLevel::Base));\n}\n\nvoid LineLevels::ClearLevels() {\n\tlevels.DeleteAll();\n}\n\nint LineLevels::SetLevel(Sci::Line line, int level, Sci::Line lines) {\n\tint prev = level;\n\tif ((line >= 0) && (line < lines)) {\n\t\tif (!levels.Length()) {\n\t\t\tExpandLevels(lines + 1);\n\t\t}\n\t\tprev = levels[line];\n\t\tlevels[line] = level;\n\t}\n\treturn prev;\n}\n\nint LineLevels::GetLevel(Sci::Line line) const noexcept {\n\tif ((line >= 0) && (line < levels.Length())) {\n\t\treturn levels[line];\n\t}\n\treturn static_cast<int>(Scintilla::FoldLevel::Base);\n}\n\nScintilla::FoldLevel LineLevels::GetFoldLevel(Sci::Line line) const noexcept {\n\tif ((line >= 0) && (line < levels.Length())) {\n\t\treturn static_cast<FoldLevel>(levels[line]);\n\t}\n\treturn Scintilla::FoldLevel::Base;\n}\n\nSci::Line LineLevels::GetFoldParent(Sci::Line line) const noexcept {\n\tconst FoldLevel level = LevelNumberPart(GetFoldLevel(line));\n\tfor (Sci::Line lineLook = line - 1; lineLook >= 0; lineLook--) {\n\t\tconst FoldLevel levelTry = GetFoldLevel(lineLook);\n\t\tif (LevelIsHeader(levelTry) && LevelNumberPart(levelTry) < level) {\n\t\t\treturn lineLook;\n\t\t}\n\t}\n\treturn -1;\n}\n\nvoid LineState::Init() {\n\tlineStates.DeleteAll();\n}\n\nvoid LineState::InsertLine(Sci::Line line) {\n\tif (lineStates.Length()) {\n\t\tlineStates.EnsureLength(line);\n\t\tconst int val = (line < lineStates.Length()) ? lineStates[line] : 0;\n\t\tlineStates.Insert(line, val);\n\t}\n}\n\nvoid LineState::InsertLines(Sci::Line line, Sci::Line lines) {\n\tif (lineStates.Length()) {\n\t\tlineStates.EnsureLength(line);\n\t\tconst int val = (line < lineStates.Length()) ? lineStates[line] : 0;\n\t\tlineStates.InsertValue(line, lines, val);\n\t}\n}\n\nvoid LineState::RemoveLine(Sci::Line line) {\n\tif (lineStates.Length() > line) {\n\t\tlineStates.Delete(line);\n\t}\n}\n\nint LineState::SetLineState(Sci::Line line, int state, Sci::Line lines) {\n\tint stateOld = state;\n\tif ((line >= 0) && (line < lines)) {\n\t\tlineStates.EnsureLength(lines + 1);\n\t\tstateOld = lineStates[line];\n\t\tlineStates[line] = state;\n\t}\n\treturn stateOld;\n}\n\nint LineState::GetLineState(Sci::Line line) {\n\tif (line < 0)\n\t\treturn 0;\n\tlineStates.EnsureLength(line + 1);\n\treturn lineStates[line];\n}\n\nSci::Line LineState::GetMaxLineState() const noexcept {\n\treturn lineStates.Length();\n}\n\n// Each allocated LineAnnotation is a char array which starts with an AnnotationHeader\n// and then has text and optional styles.\n\nstruct AnnotationHeader {\n\tshort style;\t// Style IndividualStyles implies array of styles\n\tshort lines;\n\tint length;\n};\n\nnamespace {\n\nconstexpr int IndividualStyles = 0x100;\n\nsize_t NumberLines(std::string_view sv) {\n\treturn std::count(sv.begin(), sv.end(), '\\n') + 1;\n}\n\nstd::unique_ptr<char[]>AllocateAnnotation(size_t length, int style) {\n\tconst size_t len = sizeof(AnnotationHeader) + length + ((style == IndividualStyles) ? length : 0);\n\treturn std::make_unique<char[]>(len);\n}\n\n}\n\nbool LineAnnotation::Empty() const noexcept {\n\treturn annotations.Length() == 0;\n}\n\nvoid LineAnnotation::Init() {\n\tClearAll();\n}\n\nvoid LineAnnotation::InsertLine(Sci::Line line) {\n\tif (annotations.Length()) {\n\t\tannotations.EnsureLength(line);\n\t\tannotations.Insert(line, std::unique_ptr<char []>());\n\t}\n}\n\nvoid LineAnnotation::InsertLines(Sci::Line line, Sci::Line lines) {\n\tif (annotations.Length()) {\n\t\tannotations.EnsureLength(line);\n\t\tannotations.InsertEmpty(line, lines);\n\t}\n}\n\nvoid LineAnnotation::RemoveLine(Sci::Line line) {\n\tif (annotations.Length() && (line > 0) && (line <= annotations.Length())) {\n\t\tannotations[line-1].reset();\n\t\tannotations.Delete(line-1);\n\t}\n}\n\nbool LineAnnotation::MultipleStyles(Sci::Line line) const noexcept {\n\tif (annotations.Length() && (line >= 0) && (line < annotations.Length()) && annotations[line])\n\t\treturn reinterpret_cast<AnnotationHeader *>(annotations[line].get())->style == IndividualStyles;\n\telse\n\t\treturn false;\n}\n\nint LineAnnotation::Style(Sci::Line line) const noexcept {\n\tif (annotations.Length() && (line >= 0) && (line < annotations.Length()) && annotations[line])\n\t\treturn reinterpret_cast<AnnotationHeader *>(annotations[line].get())->style;\n\telse\n\t\treturn 0;\n}\n\nconst char *LineAnnotation::Text(Sci::Line line) const noexcept {\n\tif (annotations.Length() && (line >= 0) && (line < annotations.Length()) && annotations[line])\n\t\treturn annotations[line].get()+sizeof(AnnotationHeader);\n\telse\n\t\treturn nullptr;\n}\n\nconst unsigned char *LineAnnotation::Styles(Sci::Line line) const noexcept {\n\tif (annotations.Length() && (line >= 0) && (line < annotations.Length()) && annotations[line] && MultipleStyles(line))\n\t\treturn reinterpret_cast<unsigned char *>(annotations[line].get() + sizeof(AnnotationHeader) + Length(line));\n\telse\n\t\treturn nullptr;\n}\n\nvoid LineAnnotation::SetText(Sci::Line line, const char *text) {\n\tif (text && (line >= 0)) {\n\t\tannotations.EnsureLength(line+1);\n\t\tconst int style = Style(line);\n\t\tannotations[line] = AllocateAnnotation(strlen(text), style);\n\t\tchar *pa = annotations[line].get();\n\t\tassert(pa);\n\t\tAnnotationHeader *pah = reinterpret_cast<AnnotationHeader *>(pa);\n\t\tpah->style = static_cast<short>(style);\n\t\tpah->length = static_cast<int>(strlen(text));\n\t\tpah->lines = static_cast<short>(NumberLines(text));\n\t\tmemcpy(pa+sizeof(AnnotationHeader), text, pah->length);\n\t} else {\n\t\tif (annotations.Length() && (line >= 0) && (line < annotations.Length()) && annotations[line]) {\n\t\t\tannotations[line].reset();\n\t\t}\n\t}\n}\n\nvoid LineAnnotation::ClearAll() {\n\tannotations.DeleteAll();\n}\n\nvoid LineAnnotation::SetStyle(Sci::Line line, int style) {\n\tannotations.EnsureLength(line+1);\n\tif (!annotations[line]) {\n\t\tannotations[line] = AllocateAnnotation(0, style);\n\t}\n\treinterpret_cast<AnnotationHeader *>(annotations[line].get())->style = static_cast<short>(style);\n}\n\nvoid LineAnnotation::SetStyles(Sci::Line line, const unsigned char *styles) {\n\tif (line >= 0) {\n\t\tannotations.EnsureLength(line+1);\n\t\tif (!annotations[line]) {\n\t\t\tannotations[line] = AllocateAnnotation(0, IndividualStyles);\n\t\t} else {\n\t\t\tconst AnnotationHeader *pahSource = reinterpret_cast<AnnotationHeader *>(annotations[line].get());\n\t\t\tif (pahSource->style != IndividualStyles) {\n\t\t\t\tstd::unique_ptr<char[]>allocation = AllocateAnnotation(pahSource->length, IndividualStyles);\n\t\t\t\tAnnotationHeader *pahAlloc = reinterpret_cast<AnnotationHeader *>(allocation.get());\n\t\t\t\tpahAlloc->length = pahSource->length;\n\t\t\t\tpahAlloc->lines = pahSource->lines;\n\t\t\t\tmemcpy(allocation.get() + sizeof(AnnotationHeader), annotations[line].get() + sizeof(AnnotationHeader), pahSource->length);\n\t\t\t\tannotations[line] = std::move(allocation);\n\t\t\t}\n\t\t}\n\t\tAnnotationHeader *pah = reinterpret_cast<AnnotationHeader *>(annotations[line].get());\n\t\tpah->style = IndividualStyles;\n\t\tmemcpy(annotations[line].get() + sizeof(AnnotationHeader) + pah->length, styles, pah->length);\n\t}\n}\n\nint LineAnnotation::Length(Sci::Line line) const noexcept {\n\tif (annotations.Length() && (line >= 0) && (line < annotations.Length()) && annotations[line])\n\t\treturn reinterpret_cast<AnnotationHeader *>(annotations[line].get())->length;\n\telse\n\t\treturn 0;\n}\n\nint LineAnnotation::Lines(Sci::Line line) const noexcept {\n\tif (annotations.Length() && (line >= 0) && (line < annotations.Length()) && annotations[line])\n\t\treturn reinterpret_cast<AnnotationHeader *>(annotations[line].get())->lines;\n\telse\n\t\treturn 0;\n}\n\nvoid LineTabstops::Init() {\n\ttabstops.DeleteAll();\n}\n\nvoid LineTabstops::InsertLine(Sci::Line line) {\n\tif (tabstops.Length()) {\n\t\ttabstops.EnsureLength(line);\n\t\ttabstops.Insert(line, nullptr);\n\t}\n}\n\nvoid LineTabstops::InsertLines(Sci::Line line, Sci::Line lines) {\n\tif (tabstops.Length()) {\n\t\ttabstops.EnsureLength(line);\n\t\ttabstops.InsertEmpty(line, lines);\n\t}\n}\n\nvoid LineTabstops::RemoveLine(Sci::Line line) {\n\tif (tabstops.Length() > line) {\n\t\ttabstops[line].reset();\n\t\ttabstops.Delete(line);\n\t}\n}\n\nbool LineTabstops::ClearTabstops(Sci::Line line) noexcept {\n\tif (line < tabstops.Length()) {\n\t\tTabstopList *tl = tabstops[line].get();\n\t\tif (tl) {\n\t\t\ttl->clear();\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\nbool LineTabstops::AddTabstop(Sci::Line line, int x) {\n\ttabstops.EnsureLength(line + 1);\n\tif (!tabstops[line]) {\n\t\ttabstops[line] = std::make_unique<TabstopList>();\n\t}\n\n\tTabstopList *tl = tabstops[line].get();\n\tif (tl) {\n\t\t// tabstop positions are kept in order - insert in the right place\n\t\tstd::vector<int>::iterator it = std::lower_bound(tl->begin(), tl->end(), x);\n\t\t// don't insert duplicates\n\t\tif (it == tl->end() || *it != x) {\n\t\t\ttl->insert(it, x);\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\nint LineTabstops::GetNextTabstop(Sci::Line line, int x) const noexcept {\n\tif (line < tabstops.Length()) {\n\t\tconst TabstopList *tl = tabstops[line].get();\n\t\tif (tl) {\n\t\t\tfor (const int i : *tl) {\n\t\t\t\tif (i > x) {\n\t\t\t\t\treturn i;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn 0;\n}\n"
  },
  {
    "path": "Libraries/scintilla/src/PerLine.h",
    "content": "// Scintilla source code edit control\n/** @file PerLine.h\n ** Manages data associated with each line of the document\n **/\n// Copyright 1998-2009 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#ifndef PERLINE_H\n#define PERLINE_H\n\nnamespace Scintilla::Internal {\n\n/**\n * This holds the marker identifier and the marker type to display.\n * MarkerHandleNumbers are members of lists.\n */\nstruct MarkerHandleNumber {\n\tint handle;\n\tint number;\n\tMarkerHandleNumber(int handle_, int number_) noexcept : handle(handle_), number(number_) {}\n};\n\n/**\n * A marker handle set contains any number of MarkerHandleNumbers.\n */\nclass MarkerHandleSet {\n\tstd::forward_list<MarkerHandleNumber> mhList;\n\npublic:\n\tMarkerHandleSet();\n\tbool Empty() const noexcept;\n\tint MarkValue() const noexcept;\t///< Bit set of marker numbers.\n\tbool Contains(int handle) const noexcept;\n\tbool InsertHandle(int handle, int markerNum);\n\tvoid RemoveHandle(int handle);\n\tbool RemoveNumber(int markerNum, bool all);\n\tvoid CombineWith(MarkerHandleSet *other) noexcept;\n\tMarkerHandleNumber const *GetMarkerHandleNumber(int which) const noexcept;\n};\n\nclass LineMarkers : public PerLine {\n\tSplitVector<std::unique_ptr<MarkerHandleSet>> markers;\n\t/// Handles are allocated sequentially and should never have to be reused as 32 bit ints are very big.\n\tint handleCurrent;\npublic:\n\tLineMarkers() : handleCurrent(0) {\n\t}\n\tvoid Init() override;\n\tvoid InsertLine(Sci::Line line) override;\n\tvoid InsertLines(Sci::Line line, Sci::Line lines) override;\n\tvoid RemoveLine(Sci::Line line) override;\n\n\tint MarkValue(Sci::Line line) const noexcept;\n\tSci::Line MarkerNext(Sci::Line lineStart, int mask) const noexcept;\n\tint AddMark(Sci::Line line, int markerNum, Sci::Line lines);\n\tvoid MergeMarkers(Sci::Line line);\n\tbool DeleteMark(Sci::Line line, int markerNum, bool all);\n\tvoid DeleteMarkFromHandle(int markerHandle);\n\tSci::Line LineFromHandle(int markerHandle) const noexcept;\n\tint HandleFromLine(Sci::Line line, int which) const noexcept;\n\tint NumberFromLine(Sci::Line line, int which) const noexcept;\n};\n\nclass LineLevels : public PerLine {\n\tSplitVector<int> levels;\npublic:\n\tLineLevels() {\n\t}\n\tvoid Init() override;\n\tvoid InsertLine(Sci::Line line) override;\n\tvoid InsertLines(Sci::Line line, Sci::Line lines) override;\n\tvoid RemoveLine(Sci::Line line) override;\n\n\tvoid ExpandLevels(Sci::Line sizeNew=-1);\n\tvoid ClearLevels();\n\tint SetLevel(Sci::Line line, int level, Sci::Line lines);\n\tint GetLevel(Sci::Line line) const noexcept;\n\tFoldLevel GetFoldLevel(Sci::Line line) const noexcept;\n\tSci::Line GetFoldParent(Sci::Line line) const noexcept;\n};\n\nclass LineState : public PerLine {\n\tSplitVector<int> lineStates;\npublic:\n\tLineState() {\n\t}\n\tvoid Init() override;\n\tvoid InsertLine(Sci::Line line) override;\n\tvoid InsertLines(Sci::Line line, Sci::Line lines) override;\n\tvoid RemoveLine(Sci::Line line) override;\n\n\tint SetLineState(Sci::Line line, int state, Sci::Line lines);\n\tint GetLineState(Sci::Line line);\n\tSci::Line GetMaxLineState() const noexcept;\n};\n\nclass LineAnnotation : public PerLine {\n\tSplitVector<std::unique_ptr<char []>> annotations;\npublic:\n\tLineAnnotation() {\n\t}\n\n\t[[nodiscard]] bool Empty() const noexcept;\n\tvoid Init() override;\n\tvoid InsertLine(Sci::Line line) override;\n\tvoid InsertLines(Sci::Line line, Sci::Line lines) override;\n\tvoid RemoveLine(Sci::Line line) override;\n\n\tbool MultipleStyles(Sci::Line line) const noexcept;\n\tint Style(Sci::Line line) const noexcept;\n\tconst char *Text(Sci::Line line) const noexcept;\n\tconst unsigned char *Styles(Sci::Line line) const noexcept;\n\tvoid SetText(Sci::Line line, const char *text);\n\tvoid ClearAll();\n\tvoid SetStyle(Sci::Line line, int style);\n\tvoid SetStyles(Sci::Line line, const unsigned char *styles);\n\tint Length(Sci::Line line) const noexcept;\n\tint Lines(Sci::Line line) const noexcept;\n};\n\ntypedef std::vector<int> TabstopList;\n\nclass LineTabstops : public PerLine {\n\tSplitVector<std::unique_ptr<TabstopList>> tabstops;\npublic:\n\tLineTabstops() {\n\t}\n\tvoid Init() override;\n\tvoid InsertLine(Sci::Line line) override;\n\tvoid InsertLines(Sci::Line line, Sci::Line lines) override;\n\tvoid RemoveLine(Sci::Line line) override;\n\n\tbool ClearTabstops(Sci::Line line) noexcept;\n\tbool AddTabstop(Sci::Line line, int x);\n\tint GetNextTabstop(Sci::Line line, int x) const noexcept;\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/src/Platform.h",
    "content": "// Scintilla source code edit control\n/** @file Platform.h\n ** Interface to platform facilities. Also includes some basic utilities.\n ** Implemented in PlatGTK.cxx for GTK+/Linux, PlatWin.cxx for Windows, and PlatWX.cxx for wxWindows.\n **/\n// Copyright 1998-2009 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#ifndef PLATFORM_H\n#define PLATFORM_H\n\n// PLAT_GTK = GTK+ on Linux or Win32\n// PLAT_GTK_WIN32 is defined additionally when running PLAT_GTK under Win32\n// PLAT_WIN = Win32 API on Win32 OS\n// PLAT_WX is wxWindows on any supported platform\n// PLAT_TK = Tcl/TK on Linux or Win32\n\n#define PLAT_GTK 0\n#define PLAT_GTK_WIN32 0\n#define PLAT_GTK_MACOSX 0\n#define PLAT_MACOSX 0\n#define PLAT_WIN 0\n#define PLAT_WX  0\n#define PLAT_QT 0\n#define PLAT_QT_QML 0\n#define PLAT_FOX 0\n#define PLAT_CURSES 0\n#define PLAT_TK 0\n#define PLAT_HAIKU 0\n\n#if defined(FOX)\n#undef PLAT_FOX\n#define PLAT_FOX 1\n\n#elif defined(__WX__)\n#undef PLAT_WX\n#define PLAT_WX  1\n\n#elif defined(CURSES)\n#undef PLAT_CURSES\n#define PLAT_CURSES 1\n\n#elif defined(__HAIKU__)\n#undef PLAT_HAIKU\n#define PLAT_HAIKU 1\n\n#elif defined(SCINTILLA_QT)\n#undef PLAT_QT\n#define PLAT_QT 1\n\n#elif defined(SCINTILLA_QT_QML)\n#undef PLAT_QT_QML\n#define PLAT_QT_QML 1\n\n#elif defined(TK)\n#undef PLAT_TK\n#define PLAT_TK 1\n\n#elif defined(GTK)\n#undef PLAT_GTK\n#define PLAT_GTK 1\n\n#if defined(__WIN32__) || defined(_MSC_VER)\n#undef PLAT_GTK_WIN32\n#define PLAT_GTK_WIN32 1\n#endif\n\n#if defined(__APPLE__)\n#undef PLAT_GTK_MACOSX\n#define PLAT_GTK_MACOSX 1\n#endif\n\n#elif defined(__APPLE__)\n\n#undef PLAT_MACOSX\n#define PLAT_MACOSX 1\n\n#else\n#undef PLAT_WIN\n#define PLAT_WIN 1\n\n#endif\n\nnamespace Scintilla::Internal {\n\n// Underlying the implementation of the platform classes are platform specific types.\n// Sometimes these need to be passed around by client code so they are defined here\n\ntypedef void *SurfaceID;\ntypedef void *WindowID;\ntypedef void *MenuID;\ntypedef void *TickerID;\ntypedef void *Function;\ntypedef void *IdlerID;\n\n/**\n * Font management.\n */\n\nconstexpr const char *localeNameDefault = \"en-us\";\n\nstruct FontParameters {\n\tconst char *faceName;\n\tXYPOSITION size;\n\tScintilla::FontWeight weight;\n\tbool italic;\n\tScintilla::FontQuality extraFontFlag;\n\tScintilla::Technology technology;\n\tScintilla::CharacterSet characterSet;\n\tconst char *localeName;\n\tScintilla::FontStretch stretch;\n\n\tconstexpr FontParameters(\n\t\tconst char *faceName_,\n\t\tXYPOSITION size_=10,\n\t\tScintilla::FontWeight weight_= Scintilla::FontWeight::Normal,\n\t\tbool italic_=false,\n\t\tScintilla::FontQuality extraFontFlag_= Scintilla::FontQuality::QualityDefault,\n\t\tScintilla::Technology technology_= Scintilla::Technology::Default,\n\t\tScintilla::CharacterSet characterSet_= Scintilla::CharacterSet::Ansi,\n\t\tconst char *localeName_=localeNameDefault,\n\t\tScintilla::FontStretch stretch_=Scintilla::FontStretch::Normal) noexcept :\n\n\t\tfaceName(faceName_),\n\t\tsize(size_),\n\t\tweight(weight_),\n\t\titalic(italic_),\n\t\textraFontFlag(extraFontFlag_),\n\t\ttechnology(technology_),\n\t\tcharacterSet(characterSet_),\n\t\tlocaleName(localeName_),\n\t\tstretch(stretch_)\n\t{\n\t}\n\n};\n\nclass Font {\npublic:\n\tFont() noexcept = default;\n\t// Deleted so Font objects can not be copied\n\tFont(const Font &) = delete;\n\tFont(Font &&) = delete;\n\tFont &operator=(const Font &) = delete;\n\tFont &operator=(Font &&) = delete;\n\tvirtual ~Font() noexcept = default;\n\n\tstatic std::shared_ptr<Font> Allocate(const FontParameters &fp);\n};\n\nclass IScreenLine {\npublic:\n\tvirtual std::string_view Text() const = 0;\n\tvirtual size_t Length() const = 0;\n\tvirtual size_t RepresentationCount() const = 0;\n\tvirtual XYPOSITION Width() const = 0;\n\tvirtual XYPOSITION Height() const = 0;\n\tvirtual XYPOSITION TabWidth() const = 0;\n\tvirtual XYPOSITION TabWidthMinimumPixels() const = 0;\n\tvirtual const Font *FontOfPosition(size_t position) const = 0;\n\tvirtual XYPOSITION RepresentationWidth(size_t position) const = 0;\n\tvirtual XYPOSITION TabPositionAfter(XYPOSITION xPosition) const = 0;\n};\n\nclass IScreenLineLayout {\npublic:\n\tvirtual ~IScreenLineLayout() noexcept = default;\n\tvirtual size_t PositionFromX(XYPOSITION xDistance, bool charPosition) = 0;\n\tvirtual XYPOSITION XFromPosition(size_t caretPosition) = 0;\n\tvirtual std::vector<Interval> FindRangeIntervals(size_t start, size_t end) = 0;\n};\n\n/**\n * Parameters for surfaces.\n */\nstruct SurfaceMode {\n\tint codePage = 0;\n\tbool bidiR2L = false;\n\tSurfaceMode() = default;\n\texplicit SurfaceMode(int codePage_, bool bidiR2L_) noexcept : codePage(codePage_), bidiR2L(bidiR2L_) {\n\t}\n};\n\n/**\n * A surface abstracts a place to draw.\n */\nclass Surface {\npublic:\n\tSurface() noexcept = default;\n\tSurface(const Surface &) = delete;\n\tSurface(Surface &&) = delete;\n\tSurface &operator=(const Surface &) = delete;\n\tSurface &operator=(Surface &&) = delete;\n\tvirtual ~Surface() noexcept = default;\n\tstatic std::unique_ptr<Surface> Allocate(Scintilla::Technology technology);\n\n\tvirtual void Init(WindowID wid)=0;\t// For measuring text\n\tvirtual void Init(SurfaceID sid, WindowID wid)=0;\t// For drawing\n\tvirtual std::unique_ptr<Surface> AllocatePixMap(int width, int height)=0;\n\n\tvirtual void SetMode(SurfaceMode mode)=0;\n\n\tenum class Ends {\n\t\tsemiCircles = 0x0,\n\t\tleftFlat = 0x1,\n\t\tleftAngle = 0x2,\n\t\trightFlat = 0x10,\n\t\trightAngle = 0x20,\n\t};\n\n\tvirtual void Release() noexcept=0;\n\tvirtual int SupportsFeature(Scintilla::Supports feature) noexcept=0;\n\tvirtual bool Initialised()=0;\n\tvirtual int LogPixelsY()=0;\n\tvirtual int PixelDivisions()=0;\n\tvirtual int DeviceHeightFont(int points)=0;\n\tvirtual void LineDraw(Point start, Point end, Stroke stroke)=0;\n\tvirtual void PolyLine(const Point *pts, size_t npts, Stroke stroke)=0;\n\tvirtual void Polygon(const Point *pts, size_t npts, FillStroke fillStroke)=0;\n\tvirtual void RectangleDraw(PRectangle rc, FillStroke fillStroke)=0;\n\tvirtual void RectangleFrame(PRectangle rc, Stroke stroke)=0;\n\tvirtual void FillRectangle(PRectangle rc, Fill fill)=0;\n\tvirtual void FillRectangleAligned(PRectangle rc, Fill fill)=0;\n\tvirtual void FillRectangle(PRectangle rc, Surface &surfacePattern)=0;\n\tvirtual void RoundedRectangle(PRectangle rc, FillStroke fillStroke)=0;\n\tvirtual void AlphaRectangle(PRectangle rc, XYPOSITION cornerSize, FillStroke fillStroke)=0;\n\tenum class GradientOptions { leftToRight, topToBottom };\n\tvirtual void GradientRectangle(PRectangle rc, const std::vector<ColourStop> &stops, GradientOptions options)=0;\n\tvirtual void DrawRGBAImage(PRectangle rc, int width, int height, const unsigned char *pixelsImage) = 0;\n\tvirtual void Ellipse(PRectangle rc, FillStroke fillStroke)=0;\n\tvirtual void Stadium(PRectangle rc, FillStroke fillStroke, Ends ends)=0;\n\tvirtual void Copy(PRectangle rc, Point from, Surface &surfaceSource)=0;\n\n\tvirtual std::unique_ptr<IScreenLineLayout> Layout(const IScreenLine *screenLine) = 0;\n\n\tvirtual void DrawTextNoClip(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourRGBA fore, ColourRGBA back) = 0;\n\tvirtual void DrawTextClipped(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourRGBA fore, ColourRGBA back) = 0;\n\tvirtual void DrawTextTransparent(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourRGBA fore) = 0;\n\tvirtual void MeasureWidths(const Font *font_, std::string_view text, XYPOSITION *positions) = 0;\n\tvirtual XYPOSITION WidthText(const Font *font_, std::string_view text) = 0;\n\n\tvirtual void DrawTextNoClipUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourRGBA fore, ColourRGBA back) = 0;\n\tvirtual void DrawTextClippedUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourRGBA fore, ColourRGBA back) = 0;\n\tvirtual void DrawTextTransparentUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourRGBA fore) = 0;\n\tvirtual void MeasureWidthsUTF8(const Font *font_, std::string_view text, XYPOSITION *positions) = 0;\n\tvirtual XYPOSITION WidthTextUTF8(const Font *font_, std::string_view text) = 0;\n\n\tvirtual XYPOSITION Ascent(const Font *font_)=0;\n\tvirtual XYPOSITION Descent(const Font *font_)=0;\n\tvirtual XYPOSITION InternalLeading(const Font *font_)=0;\n\tvirtual XYPOSITION Height(const Font *font_)=0;\n\tvirtual XYPOSITION AverageCharWidth(const Font *font_)=0;\n\n\tvirtual void SetClip(PRectangle rc)=0;\n\tvirtual void PopClip()=0;\n\tvirtual void FlushCachedState()=0;\n\tvirtual void FlushDrawing()=0;\n\n\tvirtual void DrawLineDots(PRectangle rc, ColourRGBA color)=0; //Au\n\tvirtual void* get_hdc() = 0; //Au\n};\n\n/**\n * Class to hide the details of window manipulation.\n * Does not own the window which will normally have a longer life than this object.\n */\nclass Window {\nprotected:\n\tWindowID wid;\npublic:\n\tWindow() noexcept : wid(nullptr), cursorLast(Cursor::invalid) {\n\t}\n\tWindow(const Window &source) = delete;\n\tWindow(Window &&) = delete;\n\tWindow &operator=(WindowID wid_) noexcept {\n\t\twid = wid_;\n\t\tcursorLast = Cursor::invalid;\n\t\treturn *this;\n\t}\n\tWindow &operator=(const Window &) = delete;\n\tWindow &operator=(Window &&) = delete;\n\tvirtual ~Window() noexcept;\n\tWindowID GetID() const noexcept { return wid; }\n\tbool Created() const noexcept { return wid != nullptr; }\n\tvoid Destroy() noexcept;\n\tPRectangle GetPosition() const;\n\tvoid SetPosition(PRectangle rc);\n\tvoid SetPositionRelative(PRectangle rc, const Window *relativeTo);\n\tPRectangle GetClientPosition() const;\n\tvoid Show(bool show=true);\n\tvoid InvalidateAll();\n\tvoid InvalidateRectangle(PRectangle rc);\n\tenum class Cursor { invalid, text, arrow, up, wait, horizontal, vertical, reverseArrow, hand };\n\tvoid SetCursor(Cursor curs);\n\tPRectangle GetMonitorRect(Point pt);\nprivate:\n\tCursor cursorLast;\n};\n\n/**\n * Listbox management.\n */\n\n// ScintillaBase implements IListBoxDelegate to receive ListBoxEvents from a ListBox\n\nstruct ListBoxEvent {\n\tenum class EventType { selectionChange, doubleClick } event;\n\tListBoxEvent(EventType event_) noexcept : event(event_) {\n\t}\n};\n\nclass IListBoxDelegate {\npublic:\n\tvirtual void ListNotify(ListBoxEvent *plbe)=0;\n};\n\nstruct ListOptions {\n\tstd::optional<ColourRGBA> fore;\n\tstd::optional<ColourRGBA> back;\n\tstd::optional<ColourRGBA> foreSelected;\n\tstd::optional<ColourRGBA> backSelected;\n\tAutoCompleteOption options=AutoCompleteOption::Normal;\n\tfloat imageScale=1.0f;\n};\n\nclass ListBox : public Window {\npublic:\n\tListBox() noexcept;\n\t~ListBox() noexcept override;\n\tstatic std::unique_ptr<ListBox> Allocate();\n\n\tvirtual void SetFont(const Font *font)=0;\n\tvirtual void Create(Window &parent, int ctrlID, Point location, int lineHeight_, bool unicodeMode_, Scintilla::Technology technology_)=0;\n\tvirtual void SetAverageCharWidth(int width)=0;\n\tvirtual void SetVisibleRows(int rows)=0;\n\tvirtual int GetVisibleRows() const=0;\n\tvirtual PRectangle GetDesiredRect()=0;\n\tvirtual int CaretFromEdge()=0;\n\tvirtual void Clear() noexcept=0;\n\tvirtual void Append(char *s, int type = -1)=0;\n\tvirtual int Length()=0;\n\tvirtual void Select(int n)=0;\n\tvirtual int GetSelection()=0;\n\tvirtual int Find(const char *prefix)=0;\n\tvirtual std::string GetValue(int n)=0;\n\tvirtual void RegisterImage(int type, const char *xpm_data)=0;\n\tvirtual void RegisterRGBAImage(int type, int width, int height, const unsigned char *pixelsImage) = 0;\n\tvirtual void ClearRegisteredImages()=0;\n\tvirtual void SetDelegate(IListBoxDelegate *lbDelegate)=0;\n\tvirtual void SetList(const char* list, char separator, char typesep)=0;\n\tvirtual void SetOptions(ListOptions options_)=0;\n};\n\n/**\n * Menu management.\n */\nclass Menu {\n\tMenuID mid;\npublic:\n\tMenu() noexcept;\n\tMenuID GetID() const noexcept { return mid; }\n\tvoid CreatePopUp();\n\tvoid Destroy() noexcept;\n\tvoid Show(Point pt, const Window &w);\n};\n\n/**\n * Platform namespace used to retrieve system wide parameters such as double click speed\n * and chrome colour.\n */\nnamespace Platform {\n\nColourRGBA Chrome();\nColourRGBA ChromeHighlight();\nconst char *DefaultFont();\nint DefaultFontSize();\nunsigned int DoubleClickTime();\nconstexpr long LongFromTwoShorts(short a,short b) noexcept {\n\treturn (a) | ((b) << 16);\n}\n\n}\n\n}\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/src/Position.h",
    "content": "// Scintilla source code edit control\n/** @file Position.h\n ** Defines global type name Position in the Sci internal namespace.\n **/\n// Copyright 2015 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#ifndef POSITION_H\n#define POSITION_H\n\n/**\n * A Position is a position within a document between two characters or at the beginning or end.\n * Sometimes used as a character index where it identifies the character after the position.\n * A Line is a document or screen line.\n */\n\nnamespace Sci {\n\ntypedef ptrdiff_t Position;\ntypedef ptrdiff_t Line;\n\ninline constexpr Position invalidPosition = -1;\n\n}\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/src/PositionCache.cxx",
    "content": "// Scintilla source code edit control\n/** @file PositionCache.cxx\n ** Classes for caching layout information.\n **/\n// Copyright 1998-2007 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#include <cstddef>\n#include <cstdlib>\n#include <cstdint>\n#include <cstring>\n#include <cmath>\n\n#include <stdexcept>\n#include <string>\n#include <string_view>\n#include <vector>\n#include <map>\n#include <set>\n#include <optional>\n#include <algorithm>\n#include <iterator>\n#include <memory>\n#include <mutex>\n\n#include \"ScintillaTypes.h\"\n#include \"ScintillaMessages.h\"\n#include \"ILoader.h\"\n#include \"ILexer.h\"\n\n#include \"Debugging.h\"\n#include \"Geometry.h\"\n#include \"Platform.h\"\n\n#include \"CharacterType.h\"\n#include \"CharacterCategoryMap.h\"\n#include \"Position.h\"\n#include \"UniqueString.h\"\n#include \"SplitVector.h\"\n#include \"Partitioning.h\"\n#include \"RunStyles.h\"\n#include \"ContractionState.h\"\n#include \"CellBuffer.h\"\n#include \"KeyMap.h\"\n#include \"Indicator.h\"\n#include \"LineMarker.h\"\n#include \"Style.h\"\n#include \"ViewStyle.h\"\n#include \"CharClassify.h\"\n#include \"Decoration.h\"\n#include \"CaseFolder.h\"\n#include \"Document.h\"\n#include \"UniConversion.h\"\n#include \"DBCS.h\"\n#include \"Selection.h\"\n#include \"PositionCache.h\"\n\nusing namespace Scintilla;\nusing namespace Scintilla::Internal;\n\nvoid BidiData::Resize(size_t maxLineLength_) {\n\tstylesFonts.resize(maxLineLength_ + 1);\n\twidthReprs.resize(maxLineLength_ + 1);\n}\n\nLineLayout::LineLayout(Sci::Line lineNumber_, int maxLineLength_) :\n\tlenLineStarts(0),\n\tlineNumber(lineNumber_),\n\tmaxLineLength(-1),\n\tnumCharsInLine(0),\n\tnumCharsBeforeEOL(0),\n\tvalidity(ValidLevel::invalid),\n\txHighlightGuide(0),\n\thighlightColumn(false),\n\tcontainsCaret(false),\n\tedgeColumn(0),\n\tbracePreviousStyles{},\n\twidthLine(wrapWidthInfinite),\n\tlines(1),\n\twrapIndent(0) {\n\tResize(maxLineLength_);\n}\n\nvoid LineLayout::Resize(int maxLineLength_) {\n\tif (maxLineLength_ > maxLineLength) {\n\t\tconst size_t lineAllocation = maxLineLength_ + 1;\n\t\tchars = std::make_unique<char[]>(lineAllocation);\n\t\tstyles = std::make_unique<unsigned char []>(lineAllocation);\n\t\t// Extra position allocated as sometimes the Windows\n\t\t// GetTextExtentExPoint API writes an extra element.\n\t\tpositions = std::make_unique<XYPOSITION []>(lineAllocation + 1);\n\t\tlineStarts.reset();\n\t\tbidiData.reset();\n\t\tlenLineStarts = 0;\n\t\tmaxLineLength = maxLineLength_;\n\t}\n}\n\nvoid LineLayout::ReSet(Sci::Line lineNumber_, Sci::Position maxLineLength_) {\n\tlineNumber = lineNumber_;\n\tResize(static_cast<int>(maxLineLength_));\n\tlines = 0;\n\tInvalidate(ValidLevel::invalid);\n}\n\nvoid LineLayout::EnsureBidiData() {\n\tif (!bidiData) {\n\t\tbidiData = std::make_unique<BidiData>();\n\t\tbidiData->Resize(maxLineLength);\n\t}\n}\n\nvoid LineLayout::ClearPositions() {\n\tstd::fill(&positions[0], &positions[maxLineLength + 2], 0.0f);\n}\n\nvoid LineLayout::Invalidate(ValidLevel validity_) noexcept {\n\tif (validity > validity_)\n\t\tvalidity = validity_;\n}\n\nSci::Line LineLayout::LineNumber() const noexcept {\n\treturn lineNumber;\n}\n\nbool LineLayout::CanHold(Sci::Line lineDoc, int lineLength_) const noexcept {\n\treturn (lineNumber == lineDoc) && (lineLength_ <= maxLineLength);\n}\n\nint LineLayout::LineStart(int line) const noexcept {\n\tif (line <= 0) {\n\t\treturn 0;\n\t} else if ((line >= lines) || !lineStarts) {\n\t\treturn numCharsInLine;\n\t} else {\n\t\treturn lineStarts[line];\n\t}\n}\n\nint LineLayout::LineLength(int line) const noexcept {\n\tif (!lineStarts) {\n\t\treturn numCharsInLine;\n\t} else if (line >= lines - 1) {\n\t\treturn numCharsInLine - lineStarts[line];\n\t} else {\n\t\treturn lineStarts[line + 1] - lineStarts[line];\n\t}\n}\n\nint LineLayout::LineLastVisible(int line, Scope scope) const noexcept {\n\tif (line < 0) {\n\t\treturn 0;\n\t} else if ((line >= lines-1) || !lineStarts) {\n\t\treturn scope == Scope::visibleOnly ? numCharsBeforeEOL : numCharsInLine;\n\t} else {\n\t\treturn lineStarts[line+1];\n\t}\n}\n\nRange LineLayout::SubLineRange(int subLine, Scope scope) const noexcept {\n\treturn Range(LineStart(subLine), LineLastVisible(subLine, scope));\n}\n\nbool LineLayout::InLine(int offset, int line) const noexcept {\n\treturn ((offset >= LineStart(line)) && (offset < LineStart(line + 1))) ||\n\t\t((offset == numCharsInLine) && (line == (lines-1)));\n}\n\nint LineLayout::SubLineFromPosition(int posInLine, PointEnd pe) const noexcept {\n\tif (!lineStarts || (posInLine > maxLineLength)) {\n\t\treturn lines - 1;\n\t}\n\n\tfor (int line = 0; line < lines; line++) {\n\t\tif (FlagSet(pe, PointEnd::subLineEnd)) {\n\t\t\t// Return subline not start of next\n\t\t\tif (lineStarts[line + 1] <= posInLine + 1)\n\t\t\t\treturn line;\n\t\t} else {\n\t\t\tif (lineStarts[line + 1] <= posInLine)\n\t\t\t\treturn line;\n\t\t}\n\t}\n\n\treturn lines - 1;\n}\n\nvoid LineLayout::AddLineStart(Sci::Position start) {\n\tlines++;\n\tif (lines >= lenLineStarts) {\n\t\tconst int newMaxLines = lines * 2 + 4;\n\t\tstd::unique_ptr<int[]> newLineStarts = std::make_unique<int[]>(newMaxLines);\n\t\tif (lenLineStarts) {\n\t\t\tstd::copy(lineStarts.get(), lineStarts.get() + lenLineStarts, newLineStarts.get());\n\t\t}\n\t\tlineStarts = std::move(newLineStarts);\n\t\tlenLineStarts = newMaxLines;\n\t}\n\tlineStarts[lines] = static_cast<int>(start);\n}\n\nvoid LineLayout::SetBracesHighlight(Range rangeLine, const Sci::Position braces[],\n                                    char bracesMatchStyle, int xHighlight, bool ignoreStyle) {\n\tif (!ignoreStyle && rangeLine.ContainsCharacter(braces[0])) {\n\t\tconst Sci::Position braceOffset = braces[0] - rangeLine.start;\n\t\tif (braceOffset < numCharsInLine) {\n\t\t\tbracePreviousStyles[0] = styles[braceOffset];\n\t\t\tstyles[braceOffset] = bracesMatchStyle;\n\t\t}\n\t}\n\tif (!ignoreStyle && rangeLine.ContainsCharacter(braces[1])) {\n\t\tconst Sci::Position braceOffset = braces[1] - rangeLine.start;\n\t\tif (braceOffset < numCharsInLine) {\n\t\t\tbracePreviousStyles[1] = styles[braceOffset];\n\t\t\tstyles[braceOffset] = bracesMatchStyle;\n\t\t}\n\t}\n\tif ((braces[0] >= rangeLine.start && braces[1] <= rangeLine.end) ||\n\t        (braces[1] >= rangeLine.start && braces[0] <= rangeLine.end)) {\n\t\txHighlightGuide = xHighlight;\n\t}\n}\n\nvoid LineLayout::RestoreBracesHighlight(Range rangeLine, const Sci::Position braces[], bool ignoreStyle) {\n\tif (!ignoreStyle && rangeLine.ContainsCharacter(braces[0])) {\n\t\tconst Sci::Position braceOffset = braces[0] - rangeLine.start;\n\t\tif (braceOffset < numCharsInLine) {\n\t\t\tstyles[braceOffset] = bracePreviousStyles[0];\n\t\t}\n\t}\n\tif (!ignoreStyle && rangeLine.ContainsCharacter(braces[1])) {\n\t\tconst Sci::Position braceOffset = braces[1] - rangeLine.start;\n\t\tif (braceOffset < numCharsInLine) {\n\t\t\tstyles[braceOffset] = bracePreviousStyles[1];\n\t\t}\n\t}\n\txHighlightGuide = 0;\n}\n\nint LineLayout::FindBefore(XYPOSITION x, Range range) const noexcept {\n\tSci::Position lower = range.start;\n\tSci::Position upper = range.end;\n\tdo {\n\t\tconst Sci::Position middle = (upper + lower + 1) / 2; \t// Round high\n\t\tconst XYPOSITION posMiddle = positions[middle];\n\t\tif (x < posMiddle) {\n\t\t\tupper = middle - 1;\n\t\t} else {\n\t\t\tlower = middle;\n\t\t}\n\t} while (lower < upper);\n\treturn static_cast<int>(lower);\n}\n\n\nint LineLayout::FindPositionFromX(XYPOSITION x, Range range, bool charPosition) const noexcept {\n\tint pos = FindBefore(x, range);\n\twhile (pos < range.end) {\n\t\tif (charPosition) {\n\t\t\tif (x < (positions[pos + 1])) {\n\t\t\t\treturn pos;\n\t\t\t}\n\t\t} else {\n\t\t\tif (x < ((positions[pos] + positions[pos + 1]) / 2)) {\n\t\t\t\treturn pos;\n\t\t\t}\n\t\t}\n\t\tpos++;\n\t}\n\treturn static_cast<int>(range.end);\n}\n\nPoint LineLayout::PointFromPosition(int posInLine, int lineHeight, PointEnd pe) const noexcept {\n\tPoint pt;\n\t// In case of very long line put x at arbitrary large position\n\tif (posInLine > maxLineLength) {\n\t\tpt.x = positions[maxLineLength] - positions[LineStart(lines)];\n\t}\n\n\tfor (int subLine = 0; subLine < lines; subLine++) {\n\t\tconst Range rangeSubLine = SubLineRange(subLine, Scope::visibleOnly);\n\t\tif (posInLine >= rangeSubLine.start) {\n\t\t\tpt.y = static_cast<XYPOSITION>(subLine*lineHeight);\n\t\t\tif (posInLine <= rangeSubLine.end) {\n\t\t\t\tpt.x = positions[posInLine] - positions[rangeSubLine.start];\n\t\t\t\tif (rangeSubLine.start != 0)\t// Wrapped lines may be indented\n\t\t\t\t\tpt.x += wrapIndent;\n\t\t\t\tif (FlagSet(pe, PointEnd::subLineEnd))\t// Return end of first subline not start of next\n\t\t\t\t\tbreak;\n\t\t\t} else if (FlagSet(pe, PointEnd::lineEnd) && (subLine == (lines-1))) {\n\t\t\t\tpt.x = positions[numCharsInLine] - positions[rangeSubLine.start];\n\t\t\t\tif (rangeSubLine.start != 0)\t// Wrapped lines may be indented\n\t\t\t\t\tpt.x += wrapIndent;\n\t\t\t}\n\t\t} else {\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn pt;\n}\n\nXYPOSITION LineLayout::XInLine(Sci::Position index) const noexcept {\n\t// For positions inside line return value from positions\n\t// For positions after line return last position + 1.0\n\tif (index <= numCharsInLine) {\n\t\treturn positions[index];\n\t}\n\treturn positions[numCharsInLine] + 1.0;\n}\n\nInterval LineLayout::Span(int start, int end) const noexcept {\n\treturn { positions[start], positions[end] };\n}\n\nInterval LineLayout::SpanByte(int index) const noexcept {\n\treturn Span(index, index+1);\n}\n\nint LineLayout::EndLineStyle() const noexcept {\n\treturn styles[numCharsBeforeEOL > 0 ? numCharsBeforeEOL-1 : 0];\n}\n\nvoid LineLayout::WrapLine(const Document *pdoc, Sci::Position posLineStart, Wrap wrapState, XYPOSITION wrapWidth) {\n\t// Document wants document positions but simpler to work in line positions\n\t// so take care of adding and subtracting line start in a lambda.\n\tauto CharacterBoundary = [=](Sci::Position i, int moveDir) noexcept -> Sci::Position {\n\t\treturn pdoc->NextPosition(i + posLineStart, moveDir) - posLineStart;\n\t};\n\tlines = 0;\n\t// Calculate line start positions based upon width.\n\tSci::Position lastLineStart = 0;\n\tXYPOSITION startOffset = wrapWidth;\n\tSci::Position p = 0;\n\twhile (p < numCharsInLine) {\n\t\twhile (p < numCharsInLine && positions[p + 1] < startOffset) {\n\t\t\tp++;\n\t\t}\n\t\tif (p < numCharsInLine) {\n\t\t\t// backtrack to find lastGoodBreak\n\t\t\tSci::Position lastGoodBreak = p;\n\t\t\t// Try moving to start of last character\n\t\t\tif (p > 0) {\n\t\t\t\tlastGoodBreak = pdoc->MovePositionOutsideChar(p + posLineStart, -1) - posLineStart;\n\t\t\t}\n\t\t\tbool foundBreak = false;\n\t\t\tif (wrapState != Wrap::Char) {\n\t\t\t\tSci::Position pos = lastGoodBreak;\n\t\t\t\twhile (pos > lastLineStart) {\n\t\t\t\t\t// style boundary and space\n\t\t\t\t\tif (wrapState != Wrap::WhiteSpace && (styles[pos - 1] != styles[pos])) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tif (IsBreakSpace(chars[pos - 1]) && !IsBreakSpace(chars[pos])) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tpos = CharacterBoundary(pos, -1);\n\t\t\t\t}\n\t\t\t\tif (pos > lastLineStart) {\n\t\t\t\t\tlastGoodBreak = pos;\n\t\t\t\t\tfoundBreak = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!foundBreak) {\n\t\t\t\tif (CpUtf8 == pdoc->dbcsCodePage) {\n\t\t\t\t\t// Go back before a base character, commonly a letter as modifiers are after the letter they modify\n\t\t\t\t\tconst Sci::Position afterWrap = CharacterBoundary(lastGoodBreak, 1);\n\t\t\t\t\tstd::string_view svWithoutLast(&chars[lastLineStart], afterWrap - lastLineStart);\n\t\t\t\t\tif (DiscardLastCombinedCharacter(svWithoutLast) && !svWithoutLast.empty()) {\n\t\t\t\t\t\tlastGoodBreak = lastLineStart + static_cast<Sci::Position>(svWithoutLast.length());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (lastGoodBreak == lastLineStart) {\n\t\t\t\t\t// Ensure at least one character on line.\n\t\t\t\t\tlastGoodBreak = CharacterBoundary(lastGoodBreak, 1);\n\t\t\t\t}\n\t\t\t}\n\t\t\tAddLineStart(lastGoodBreak);\n\t\t\tlastLineStart = lastGoodBreak;\n\t\t\tstartOffset = positions[lastLineStart];\n\t\t\t// take into account the space for start wrap mark and indent\n\t\t\tstartOffset += wrapWidth - wrapIndent;\n\t\t\tp = lastLineStart + 1;\n\t\t}\n\t}\n\tlines++;\n}\n\nScreenLine::ScreenLine(\n\tconst LineLayout *ll_,\n\tint subLine,\n\tconst ViewStyle &vs,\n\tXYPOSITION width_,\n\tint tabWidthMinimumPixels_) :\n\tll(ll_),\n\tstart(ll->LineStart(subLine)),\n\tlen(ll->LineLength(subLine)),\n\twidth(width_),\n\theight(static_cast<float>(vs.lineHeight)),\n\tctrlCharPadding(vs.ctrlCharPadding),\n\ttabWidth(vs.tabWidth),\n\ttabWidthMinimumPixels(tabWidthMinimumPixels_) {\n}\n\nScreenLine::~ScreenLine() = default;\n\nstd::string_view ScreenLine::Text() const {\n\treturn std::string_view(&ll->chars[start], len);\n}\n\nsize_t ScreenLine::Length() const {\n\treturn len;\n}\n\nsize_t ScreenLine::RepresentationCount() const {\n\treturn std::count_if(&ll->bidiData->widthReprs[start],\n\t\t&ll->bidiData->widthReprs[start + len],\n\t\t[](XYPOSITION w) noexcept { return w > 0.0f; });\n}\n\nXYPOSITION ScreenLine::Width() const {\n\treturn width;\n}\n\nXYPOSITION ScreenLine::Height() const {\n\treturn height;\n}\n\nXYPOSITION ScreenLine::TabWidth() const {\n\treturn tabWidth;\n}\n\nXYPOSITION ScreenLine::TabWidthMinimumPixels() const {\n\treturn static_cast<XYPOSITION>(tabWidthMinimumPixels);\n}\n\nconst Font *ScreenLine::FontOfPosition(size_t position) const {\n\treturn ll->bidiData->stylesFonts[start + position].get();\n}\n\nXYPOSITION ScreenLine::RepresentationWidth(size_t position) const {\n\treturn ll->bidiData->widthReprs[start + position];\n}\n\nXYPOSITION ScreenLine::TabPositionAfter(XYPOSITION xPosition) const {\n\treturn (std::floor((xPosition + TabWidthMinimumPixels()) / TabWidth()) + 1) * TabWidth();\n}\n\nbool SignificantLines::LineMayCache(Sci::Line line) const noexcept {\n\tswitch (level) {\n\tcase LineCache::None:\n\t\treturn false;\n\tcase LineCache::Caret:\n\t\treturn line == lineCaret;\n\tcase LineCache::Page:\n\t\treturn (std::abs(line - lineCaret) < linesOnScreen) ||\n\t\t\t((line >= lineTop) && (line <= (lineTop + linesOnScreen)));\n\tcase LineCache::Document:\n\tdefault:\n\t\treturn true;\n\t}\n}\n\nLineLayoutCache::LineLayoutCache() :\n\tlevel(LineCache::None),\n\tmaxValidity(LineLayout::ValidLevel::invalid), styleClock(-1) {\n}\n\nLineLayoutCache::~LineLayoutCache() = default;\n\nnamespace {\n\nconstexpr size_t AlignUp(size_t value, size_t alignment) noexcept {\n\treturn ((value - 1) / alignment + 1) * alignment;\n}\n\nconstexpr size_t alignmentLLC = 20;\n\nconstexpr bool GraphicASCII(char ch) noexcept {\n\treturn ch >= ' ' && ch <= '~';\n}\n\nbool AllGraphicASCII(std::string_view text) {\n\treturn std::all_of(text.cbegin(), text.cend(), GraphicASCII);\n}\n\n}\n\n\nsize_t LineLayoutCache::EntryForLine(Sci::Line line) const noexcept {\n\tswitch (level) {\n\tcase LineCache::None:\n\tcase LineCache::Caret:\n\t\treturn 0;\n\tcase LineCache::Page:\n\t\treturn 1 + (line % (cache.size() - 1));\n\tcase LineCache::Document:\n\t\treturn line;\n\t}\n\treturn 0;\n}\n\nvoid LineLayoutCache::AllocateForLevel(Sci::Line linesOnScreen, Sci::Line linesInDoc) {\n\tsize_t lengthForLevel = 0;\n\tif (level == LineCache::Caret) {\n\t\tlengthForLevel = 1;\n\t} else if (level == LineCache::Page) {\n\t\tlengthForLevel = AlignUp(linesOnScreen + 1, alignmentLLC);\n\t} else if (level == LineCache::Document) {\n\t\tlengthForLevel = AlignUp(linesInDoc, alignmentLLC);\n\t}\n\n\tif (lengthForLevel != cache.size()) {\n\t\tmaxValidity = LineLayout::ValidLevel::lines;\n\t\tcache.resize(lengthForLevel);\n\t\t// Cache::none -> no entries\n\t\t// Cache::caret -> 1 entry can take any line\n\t\t// Cache::document -> entry per line so each line in correct entry after resize\n\t\tif (level == LineCache::Page) {\n\t\t\t// Cache::page -> locates lines in particular entries which may be incorrect after\n\t\t\t// a resize so move them to correct entries.\n\t\t\tfor (size_t i = 1; i < cache.size();) {\n\t\t\t\tsize_t increment = 1;\n\t\t\t\tif (cache[i]) {\n\t\t\t\t\tconst size_t posForLine = EntryForLine(cache[i]->LineNumber());\n\t\t\t\t\tif (posForLine != i) {\n\t\t\t\t\t\tif (cache[posForLine]) {\n\t\t\t\t\t\t\tif (EntryForLine(cache[posForLine]->LineNumber()) == posForLine) {\n\t\t\t\t\t\t\t\t// [posForLine] already holds line that is in correct place\n\t\t\t\t\t\t\t\tcache[i].reset();\t// This line has nowhere to go so reset it.\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tstd::swap(cache[i], cache[posForLine]);\n\t\t\t\t\t\t\t\tincrement = 0;\n\t\t\t\t\t\t\t\t// Don't increment as newly swapped in value may have to move\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tcache[posForLine] = std::move(cache[i]);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\ti += increment;\n\t\t\t}\n\n#ifdef CHECK_LLC\n\t\t\tfor (size_t i = 1; i < cache.size(); i++) {\n\t\t\t\tif (cache[i]) {\n\t\t\t\t\tPLATFORM_ASSERT(EntryForLine(cache[i]->LineNumber()) == i);\n\t\t\t\t}\n\t\t\t}\n#endif\n\t\t}\n\t}\n\tPLATFORM_ASSERT(cache.size() == lengthForLevel);\n}\n\nvoid LineLayoutCache::Deallocate() noexcept {\n\tmaxValidity = LineLayout::ValidLevel::invalid;\n\tcache.clear();\n}\n\nvoid LineLayoutCache::Invalidate(LineLayout::ValidLevel validity_) noexcept {\n\tif (maxValidity > validity_) {\n\t\tmaxValidity = validity_;\n\t\tfor (const std::shared_ptr<LineLayout> &ll : cache) {\n\t\t\tif (ll) {\n\t\t\t\tll->Invalidate(validity_);\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid LineLayoutCache::SetLevel(LineCache level_) noexcept {\n\tif (level != level_) {\n\t\tlevel = level_;\n\t\tmaxValidity = LineLayout::ValidLevel::invalid;\n\t\tcache.clear();\n\t}\n}\n\nstd::shared_ptr<LineLayout> LineLayoutCache::Retrieve(Sci::Line lineNumber, Sci::Line lineCaret, int maxChars, int styleClock_,\n                                      Sci::Line linesOnScreen, Sci::Line linesInDoc) {\n\tAllocateForLevel(linesOnScreen, linesInDoc);\n\tif (styleClock != styleClock_) {\n\t\tInvalidate(LineLayout::ValidLevel::checkTextAndStyle);\n\t\tstyleClock = styleClock_;\n\t}\n\tmaxValidity = LineLayout::ValidLevel::lines;\n\tsize_t pos = 0;\n\tif (level == LineCache::Page) {\n\t\t// If first entry is this line then just reuse it.\n\t\tif (!(cache[0] && (cache[0]->LineNumber() == lineNumber))) {\n\t\t\tconst size_t posForLine = EntryForLine(lineNumber);\n\t\t\tif (lineNumber == lineCaret) {\n\t\t\t\t// Use position 0 for caret line.\n\t\t\t\tif (cache[0]) {\n\t\t\t\t\t// Another line is currently in [0] so move it out to its normal position.\n\t\t\t\t\t// Since it was recently the caret line its likely to be needed soon.\n\t\t\t\t\tconst size_t posNewForEntry0 = EntryForLine(cache[0]->LineNumber());\n\t\t\t\t\tif (posForLine == posNewForEntry0) {\n\t\t\t\t\t\tstd::swap(cache[0], cache[posNewForEntry0]);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tcache[posNewForEntry0] = std::move(cache[0]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (cache[posForLine] && (cache[posForLine]->LineNumber() == lineNumber)) {\n\t\t\t\t\t// Caret line is currently somewhere else so move it to [0].\n\t\t\t\t\tcache[0] = std::move(cache[posForLine]);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tpos = posForLine;\n\t\t\t}\n\t\t}\n\t} else if (level == LineCache::Document) {\n\t\tpos = lineNumber;\n\t}\n\n\tif (pos < cache.size()) {\n\t\tif (cache[pos] && !cache[pos]->CanHold(lineNumber, maxChars)) {\n\t\t\tcache[pos].reset();\n\t\t}\n\t\tif (!cache[pos]) {\n\t\t\tcache[pos] = std::make_shared<LineLayout>(lineNumber, maxChars);\n\t\t}\n#ifdef CHECK_LLC\n\t\t// Expensive check that there is only one entry for any line number\n\t\tstd::vector<bool> linesInCache(linesInDoc);\n\t\tfor (const auto &entry : cache) {\n\t\t\tif (entry) {\n\t\t\t\tPLATFORM_ASSERT(!linesInCache[entry->LineNumber()]);\n\t\t\t\tlinesInCache[entry->LineNumber()] = true;\n\t\t\t}\n\t\t}\n#endif\n\t\treturn cache[pos];\n\t}\n\n\t// Only reach here for level == Cache::none\n\treturn std::make_shared<LineLayout>(lineNumber, maxChars);\n}\n\nnamespace {\n\n// Simply pack the (maximum 4) character bytes into an int\nconstexpr unsigned int KeyFromString(std::string_view charBytes) noexcept {\n\tPLATFORM_ASSERT(charBytes.length() <= 4);\n\tconstexpr int byteMultiplier = 0x100;\n\tunsigned int k=0;\n\tfor (const unsigned char uc : charBytes) {\n\t\tk = k * byteMultiplier + uc;\n\t}\n\treturn k;\n}\n\nconstexpr unsigned int representationKeyCrLf = KeyFromString(\"\\r\\n\");\n\nconst char *const repsC0[] = {\n\t\"NUL\", \"SOH\", \"STX\", \"ETX\", \"EOT\", \"ENQ\", \"ACK\", \"BEL\",\n\t\"BS\", \"HT\", \"LF\", \"VT\", \"FF\", \"CR\", \"SO\", \"SI\",\n\t\"DLE\", \"DC1\", \"DC2\", \"DC3\", \"DC4\", \"NAK\", \"SYN\", \"ETB\",\n\t\"CAN\", \"EM\", \"SUB\", \"ESC\", \"FS\", \"GS\", \"RS\", \"US\"\n};\n\nconst char *const repsC1[] = {\n\t\"PAD\", \"HOP\", \"BPH\", \"NBH\", \"IND\", \"NEL\", \"SSA\", \"ESA\",\n\t\"HTS\", \"HTJ\", \"VTS\", \"PLD\", \"PLU\", \"RI\", \"SS2\", \"SS3\",\n\t\"DCS\", \"PU1\", \"PU2\", \"STS\", \"CCH\", \"MW\", \"SPA\", \"EPA\",\n\t\"SOS\", \"SGCI\", \"SCI\", \"CSI\", \"ST\", \"OSC\", \"PM\", \"APC\"\n};\n\n}\n\nnamespace Scintilla::Internal {\n\nconst char *ControlCharacterString(unsigned char ch) noexcept {\n\tif (ch < std::size(repsC0)) {\n\t\treturn repsC0[ch];\n\t}\n\treturn \"BAD\";\n}\n\n\nvoid Hexits(char *hexits, int ch) noexcept {\n\tconstexpr int hexDivisor = 0x10;\n\thexits[0] = 'x';\n\thexits[1] = \"0123456789ABCDEF\"[ch / hexDivisor];\n\thexits[2] = \"0123456789ABCDEF\"[ch % hexDivisor];\n\thexits[3] = 0;\n}\n\n}\n\nvoid SpecialRepresentations::SetRepresentation(std::string_view charBytes, std::string_view value) {\n\tif ((charBytes.length() <= 4) && (value.length() <= Representation::maxLength)) {\n\t\tconst unsigned int key = KeyFromString(charBytes);\n\t\tconst bool inserted = mapReprs.insert_or_assign(key, Representation(value)).second;\n\t\tif (inserted) {\n\t\t\t// New entry so increment for first byte\n\t\t\tconst unsigned char ucStart = charBytes.empty() ? 0 : charBytes[0];\n\t\t\tstartByteHasReprs[ucStart]++;\n\t\t\tif (key > maxKey) {\n\t\t\t\tmaxKey = key;\n\t\t\t}\n\t\t\tif (key == representationKeyCrLf) {\n\t\t\t\tcrlf = true;\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid SpecialRepresentations::SetRepresentationAppearance(std::string_view charBytes, RepresentationAppearance appearance) {\n\tif (charBytes.length() <= 4) {\n\t\tconst unsigned int key = KeyFromString(charBytes);\n\t\tconst MapRepresentation::iterator it = mapReprs.find(key);\n\t\tif (it == mapReprs.end()) {\n\t\t\t// Not present so fail\n\t\t\treturn;\n\t\t}\n\t\tit->second.appearance = appearance;\n\t}\n}\n\nvoid SpecialRepresentations::SetRepresentationColour(std::string_view charBytes, ColourRGBA colour) {\n\tif (charBytes.length() <= 4) {\n\t\tconst unsigned int key = KeyFromString(charBytes);\n\t\tconst MapRepresentation::iterator it = mapReprs.find(key);\n\t\tif (it == mapReprs.end()) {\n\t\t\t// Not present so fail\n\t\t\treturn;\n\t\t}\n\t\tit->second.appearance = it->second.appearance | RepresentationAppearance::Colour;\n\t\tit->second.colour = colour;\n\t}\n}\n\nvoid SpecialRepresentations::ClearRepresentation(std::string_view charBytes) {\n\tif (charBytes.length() <= 4) {\n\t\tconst unsigned int key = KeyFromString(charBytes);\n\t\tconst MapRepresentation::iterator it = mapReprs.find(key);\n\t\tif (it != mapReprs.end()) {\n\t\t\tmapReprs.erase(it);\n\t\t\tconst unsigned char ucStart = charBytes.empty() ? 0 : charBytes[0];\n\t\t\tstartByteHasReprs[ucStart]--;\n\t\t\tif (key == maxKey && startByteHasReprs[ucStart] == 0) {\n\t\t\t\tmaxKey = mapReprs.empty() ? 0 : mapReprs.crbegin()->first;\n\t\t\t}\n\t\t\tif (key == representationKeyCrLf) {\n\t\t\t\tcrlf = false;\n\t\t\t}\n\t\t}\n\t}\n}\n\nconst Representation *SpecialRepresentations::GetRepresentation(std::string_view charBytes) const {\n\tconst unsigned int key = KeyFromString(charBytes);\n\tif (key > maxKey) {\n\t\treturn nullptr;\n\t}\n\tconst MapRepresentation::const_iterator it = mapReprs.find(key);\n\tif (it != mapReprs.end()) {\n\t\treturn &(it->second);\n\t}\n\treturn nullptr;\n}\n\nconst Representation *SpecialRepresentations::RepresentationFromCharacter(std::string_view charBytes) const {\n\tif (charBytes.length() <= 4) {\n\t\tconst unsigned char ucStart = charBytes.empty() ? 0 : charBytes[0];\n\t\tif (!startByteHasReprs[ucStart])\n\t\t\treturn nullptr;\n\t\treturn GetRepresentation(charBytes);\n\t}\n\treturn nullptr;\n}\n\nvoid SpecialRepresentations::Clear() {\n\tmapReprs.clear();\n\tconstexpr unsigned short none = 0;\n\tstd::fill(startByteHasReprs, std::end(startByteHasReprs), none);\n\tmaxKey = 0;\n\tcrlf = false;\n}\n\nvoid SpecialRepresentations::SetDefaultRepresentations(int dbcsCodePage) {\n\tClear();\n\n\t// C0 control set\n\tfor (size_t j = 0; j < std::size(repsC0); j++) {\n\t\tconst char c[2] = { static_cast<char>(j), 0 };\n\t\tSetRepresentation(std::string_view(c, 1), repsC0[j]);\n\t}\n\tSetRepresentation(\"\\x7f\", \"DEL\");\n\n\t// C1 control set\n\t// As well as Unicode mode, ISO-8859-1 should use these\n\tif (CpUtf8 == dbcsCodePage) {\n\t\tfor (size_t j = 0; j < std::size(repsC1); j++) {\n\t\t\tconst char c1[3] = { '\\xc2',  static_cast<char>(0x80 + j), 0 };\n\t\t\tSetRepresentation(c1, repsC1[j]);\n\t\t}\n\t\tSetRepresentation(\"\\xe2\\x80\\xa8\", \"LS\");\n\t\tSetRepresentation(\"\\xe2\\x80\\xa9\", \"PS\");\n\t}\n\n\t// Invalid as single bytes in multi-byte encodings\n\tif (dbcsCodePage) {\n\t\tfor (int k = 0x80; k < 0x100; k++) {\n\t\t\tif ((CpUtf8 == dbcsCodePage) || !IsDBCSValidSingleByte(dbcsCodePage, k)) {\n\t\t\t\tconst char hiByte[2] = { static_cast<char>(k), 0 };\n\t\t\t\tchar hexits[4];\n\t\t\t\tHexits(hexits, k);\n\t\t\t\tSetRepresentation(hiByte, hexits);\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid BreakFinder::Insert(Sci::Position val) {\n\tconst int posInLine = static_cast<int>(val);\n\tif (posInLine > nextBreak) {\n\t\tconst std::vector<int>::iterator it = std::lower_bound(selAndEdge.begin(), selAndEdge.end(), posInLine);\n\t\tif (it == selAndEdge.end()) {\n\t\t\tselAndEdge.push_back(posInLine);\n\t\t} else if (*it != posInLine) {\n\t\t\tselAndEdge.insert(it, 1, posInLine);\n\t\t}\n\t}\n}\n\nBreakFinder::BreakFinder(const LineLayout *ll_, const Selection *psel, Range lineRange_, Sci::Position posLineStart,\n\tXYPOSITION xStart, BreakFor breakFor, const Document *pdoc_, const SpecialRepresentations *preprs_, const ViewStyle *pvsDraw) :\n\tll(ll_),\n\tlineRange(lineRange_),\n\tnextBreak(static_cast<int>(lineRange_.start)),\n\tsaeCurrentPos(0),\n\tsaeNext(0),\n\tsubBreak(-1),\n\tpdoc(pdoc_),\n\tencodingFamily(pdoc_->CodePageFamily()),\n\tpreprs(preprs_) {\n\n\t// Search for first visible break\n\t// First find the first visible character\n\tif (xStart > 0.0f)\n\t\tnextBreak = ll->FindBefore(xStart, lineRange);\n\t// Now back to a style break\n\twhile ((nextBreak > lineRange.start) && (ll->styles[nextBreak] == ll->styles[nextBreak - 1])) {\n\t\tnextBreak--;\n\t}\n\n\tif (FlagSet(breakFor, BreakFor::Selection)) {\n\t\tconst SelectionSegment segmentLine(posLineStart, posLineStart + lineRange.end);\n\t\tfor (size_t r=0; r<psel->Count(); r++) {\n\t\t\tconst SelectionSegment portion = psel->Range(r).Intersect(segmentLine);\n\t\t\tif (!portion.Empty()) {\n\t\t\t\tInsert(portion.start.Position() - posLineStart);\n\t\t\t\tInsert(portion.end.Position() - posLineStart);\n\t\t\t}\n\t\t}\n\t\t// On the curses platform, the terminal is drawing its own caret, so add breaks around the\n\t\t// caret in the main selection in order to help prevent the selection from being drawn in\n\t\t// the caret's cell.\n\t\tif (FlagSet(pvsDraw->caret.style, CaretStyle::Curses) && !psel->RangeMain().Empty()) {\n\t\t\tconst Sci::Position caretPos = psel->RangeMain().caret.Position();\n\t\t\tconst Sci::Position anchorPos = psel->RangeMain().anchor.Position();\n\t\t\tif (caretPos < anchorPos) {\n\t\t\t\tconst Sci::Position nextPos = pdoc->MovePositionOutsideChar(caretPos + 1, 1);\n\t\t\t\tInsert(nextPos - posLineStart);\n\t\t\t} else if (caretPos > anchorPos && pvsDraw->DrawCaretInsideSelection(false, false)) {\n\t\t\t\tconst Sci::Position prevPos = pdoc->MovePositionOutsideChar(caretPos - 1, -1);\n\t\t\t\tif (prevPos > anchorPos)\n\t\t\t\t\tInsert(prevPos - posLineStart);\n\t\t\t}\n\t\t}\n\t}\n\tif (FlagSet(breakFor, BreakFor::Foreground) && pvsDraw->indicatorsSetFore) {\n\t\tfor (const IDecoration *deco : pdoc->decorations->View()) {\n\t\t\tif (pvsDraw->indicators[deco->Indicator()].OverridesTextFore()) {\n\t\t\t\tSci::Position startPos = deco->EndRun(posLineStart);\n\t\t\t\twhile (startPos < (posLineStart + lineRange.end)) {\n\t\t\t\t\tInsert(startPos - posLineStart);\n\t\t\t\t\tstartPos = deco->EndRun(startPos);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tInsert(ll->edgeColumn);\n\tInsert(lineRange.end);\n\tsaeNext = (!selAndEdge.empty()) ? selAndEdge[0] : -1;\n}\n\nBreakFinder::~BreakFinder() noexcept = default;\n\nTextSegment BreakFinder::Next() {\n\tif (subBreak < 0) {\n\t\tconst int prev = nextBreak;\n\t\tconst Representation *repr = nullptr;\n\t\twhile (nextBreak < lineRange.end) {\n\t\t\tint charWidth = 1;\n\t\t\tconst char * const chars = &ll->chars[nextBreak];\n\t\t\tconst unsigned char ch = chars[0];\n\t\t\tbool characterStyleConsistent = true;\t// All bytes of character in same style?\n\t\t\tif (!UTF8IsAscii(ch) && encodingFamily != EncodingFamily::eightBit) {\n\t\t\t\tif (encodingFamily == EncodingFamily::unicode) {\n\t\t\t\t\tcharWidth = UTF8DrawBytes(chars, lineRange.end - nextBreak);\n\t\t\t\t} else {\n\t\t\t\t\tcharWidth = pdoc->DBCSDrawBytes(std::string_view(chars, lineRange.end - nextBreak));\n\t\t\t\t}\n\t\t\t\tfor (int trail = 1; trail < charWidth; trail++) {\n\t\t\t\t\tif (ll->styles[nextBreak] != ll->styles[nextBreak + trail]) {\n\t\t\t\t\t\tcharacterStyleConsistent = false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!characterStyleConsistent) {\n\t\t\t\tif (nextBreak == prev) {\n\t\t\t\t\t// Show first character representation bytes since it has inconsistent styles.\n\t\t\t\t\tcharWidth = 1;\n\t\t\t\t} else {\n\t\t\t\t\t// Return segment before nextBreak but allow to be split up if too long\n\t\t\t\t\t// If not split up, next call will hit the above 'charWidth = 1;' and display bytes.\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\trepr = nullptr;\n\t\t\tif (preprs->MayContain(ch)) {\n\t\t\t\t// Special case \\r\\n line ends if there is a representation\n\t\t\t\tif (ch == '\\r' && preprs->ContainsCrLf() && chars[1] == '\\n') {\n\t\t\t\t\tcharWidth = 2;\n\t\t\t\t}\n\t\t\t\trepr = preprs->GetRepresentation(std::string_view(chars, charWidth));\n\t\t\t}\n\t\t\tif (((nextBreak > 0) && (ll->styles[nextBreak] != ll->styles[nextBreak - 1])) ||\n\t\t\t\t\trepr ||\n\t\t\t\t\t(nextBreak == saeNext)) {\n\t\t\t\twhile ((nextBreak >= saeNext) && (saeNext < lineRange.end)) {\n\t\t\t\t\tsaeCurrentPos++;\n\t\t\t\t\tsaeNext = static_cast<int>((saeCurrentPos < selAndEdge.size()) ? selAndEdge[saeCurrentPos] : lineRange.end);\n\t\t\t\t}\n\t\t\t\tif ((nextBreak > prev) || repr) {\n\t\t\t\t\t// Have a segment to report\n\t\t\t\t\tif (nextBreak == prev) {\n\t\t\t\t\t\tnextBreak += charWidth;\n\t\t\t\t\t} else {\n\t\t\t\t\t\trepr = nullptr;\t// Optimize -> should remember repr\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tnextBreak += charWidth;\n\t\t}\n\n\t\tconst int lengthSegment = nextBreak - prev;\n\t\tif (lengthSegment < lengthStartSubdivision) {\n\t\t\treturn TextSegment(prev, lengthSegment, repr);\n\t\t}\n\t\tsubBreak = prev;\n\t}\n\n\t// Splitting up a long run from prev to nextBreak in lots of approximately lengthEachSubdivision.\n\tconst int startSegment = subBreak;\n\tconst int remaining = nextBreak - startSegment;\n\tint lengthSegment = remaining;\n\tif (lengthSegment > lengthEachSubdivision) {\n\t\tlengthSegment = static_cast<int>(pdoc->SafeSegment(std::string_view(&ll->chars[startSegment], lengthEachSubdivision)));\n\t}\n\tif (lengthSegment < remaining) {\n\t\tsubBreak += lengthSegment;\n\t} else {\n\t\tsubBreak = -1;\n\t}\n\treturn TextSegment(startSegment, lengthSegment);\n}\n\nbool BreakFinder::More() const noexcept {\n\treturn (subBreak >= 0) || (nextBreak < lineRange.end);\n}\n\nclass PositionCacheEntry {\n\tuint16_t styleNumber = 0;\n\tuint16_t len = 0;\n\tuint16_t clock = 0;\n\tbool unicode = false;\n\tstd::unique_ptr<XYPOSITION[]> positions;\npublic:\n\tPositionCacheEntry() noexcept;\n\t// Copy constructor not currently used, but needed for being element in std::vector.\n\tPositionCacheEntry(const PositionCacheEntry &);\n\tPositionCacheEntry(PositionCacheEntry &&) noexcept = default;\n\t// Deleted so PositionCacheEntry objects can not be assigned.\n\tvoid operator=(const PositionCacheEntry &) = delete;\n\tvoid operator=(PositionCacheEntry &&) = delete;\n\t~PositionCacheEntry();\n\tvoid Set(unsigned int styleNumber_, bool unicode_, std::string_view sv, const XYPOSITION *positions_, uint16_t clock_);\n\tvoid Clear() noexcept;\n\tbool Retrieve(unsigned int styleNumber_, bool unicode_, std::string_view sv, XYPOSITION *positions_) const noexcept;\n\tstatic size_t Hash(unsigned int styleNumber_, bool unicode_, std::string_view sv) noexcept;\n\t[[nodiscard]] bool NewerThan(const PositionCacheEntry &other) const noexcept;\n\tvoid ResetClock() noexcept;\n};\n\nclass PositionCache : public IPositionCache {\n\tstatic constexpr size_t defaultCacheSize = 0x400;\n\tstd::vector<PositionCacheEntry> pces{ defaultCacheSize };\n\tstd::mutex mutex;\n\tuint16_t clock = 1;\n\tbool allClear = true;\npublic:\n\tPositionCache();\n\t// Deleted so LineAnnotation objects can not be copied.\n\tPositionCache(const PositionCache &) = delete;\n\tPositionCache(PositionCache &&) = delete;\n\tvoid operator=(const PositionCache &) = delete;\n\tvoid operator=(PositionCache &&) = delete;\n\t~PositionCache() override = default;\n\n\tvoid Clear() noexcept override;\n\tvoid SetSize(size_t size_) override;\n\t[[nodiscard]] size_t GetSize() const noexcept override;\n\tvoid MeasureWidths(Surface *surface, const ViewStyle &vstyle, unsigned int styleNumber,\n\t\tbool unicode, std::string_view sv, XYPOSITION *positions, bool needsLocking) override;\n};\n\nPositionCacheEntry::PositionCacheEntry() noexcept = default;\n\n// Copy constructor not currently used, but needed for being element in std::vector.\nPositionCacheEntry::PositionCacheEntry(const PositionCacheEntry &other) :\n\tstyleNumber(other.styleNumber), len(other.len), clock(other.clock), unicode(other.unicode) {\n\tif (other.positions) {\n\t\tconst size_t lenData = len + (len / sizeof(XYPOSITION)) + 1;\n\t\tpositions = std::make_unique<XYPOSITION[]>(lenData);\n\t\tmemcpy(positions.get(), other.positions.get(), lenData * sizeof(XYPOSITION));\n\t}\n}\n\nvoid PositionCacheEntry::Set(unsigned int styleNumber_, bool unicode_, std::string_view sv,\n\tconst XYPOSITION *positions_, uint16_t clock_) {\n\tClear();\n\tstyleNumber = static_cast<uint16_t>(styleNumber_);\n\tlen = static_cast<uint16_t>(sv.length());\n\tclock = clock_;\n\tunicode = unicode_;\n\tif (sv.data() && positions_) {\n\t\tpositions = std::make_unique<XYPOSITION[]>(len + (len / sizeof(XYPOSITION)) + 1);\n\t\tfor (unsigned int i=0; i<len; i++) {\n\t\t\tpositions[i] = positions_[i];\n\t\t}\n\t\tmemcpy(&positions[len], sv.data(), sv.length());\n\t}\n}\n\nPositionCacheEntry::~PositionCacheEntry() {\n\tClear();\n}\n\nvoid PositionCacheEntry::Clear() noexcept {\n\tpositions.reset();\n\tstyleNumber = 0;\n\tlen = 0;\n\tclock = 0;\n}\n\nbool PositionCacheEntry::Retrieve(unsigned int styleNumber_, bool unicode_, std::string_view sv, XYPOSITION *positions_) const noexcept {\n\tif ((styleNumber == styleNumber_) && (unicode == unicode_) && (len == sv.length()) &&\n\t\t(memcmp(&positions[len], sv.data(), sv.length())== 0)) {\n\t\tfor (unsigned int i=0; i<len; i++) {\n\t\t\tpositions_[i] = positions[i];\n\t\t}\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nsize_t PositionCacheEntry::Hash(unsigned int styleNumber_, bool unicode_, std::string_view sv) noexcept {\n\tconst size_t h1 = std::hash<std::string_view>{}(sv);\n\tconst size_t h2 = std::hash<unsigned int>{}(styleNumber_);\n\treturn h1 ^ (h2 << 1) ^ static_cast<size_t>(unicode_);\n}\n\nbool PositionCacheEntry::NewerThan(const PositionCacheEntry &other) const noexcept {\n\treturn clock > other.clock;\n}\n\nvoid PositionCacheEntry::ResetClock() noexcept {\n\tif (clock > 0) {\n\t\tclock = 1;\n\t}\n}\n\nPositionCache::PositionCache() = default;\n\nvoid PositionCache::Clear() noexcept {\n\tif (!allClear) {\n\t\tfor (PositionCacheEntry &pce : pces) {\n\t\t\tpce.Clear();\n\t\t}\n\t}\n\tclock = 1;\n\tallClear = true;\n}\n\nvoid PositionCache::SetSize(size_t size_) {\n\tClear();\n\tpces.resize(size_);\n}\n\nsize_t PositionCache::GetSize() const noexcept {\n\treturn pces.size();\n}\n\nvoid PositionCache::MeasureWidths(Surface *surface, const ViewStyle &vstyle, unsigned int styleNumber,\n\tbool unicode, std::string_view sv, XYPOSITION *positions, bool needsLocking) {\n\tconst Style &style = vstyle.styles[styleNumber];\n\tif (style.monospaceASCII) {\n\t\tif (AllGraphicASCII(sv)) {\n\t\t\tconst XYPOSITION monospaceCharacterWidth = style.monospaceCharacterWidth;\n\t\t\tfor (size_t i = 0; i < sv.length(); i++) {\n\t\t\t\tpositions[i] = monospaceCharacterWidth * static_cast<XYPOSITION>(i+1);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t}\n\n\tsize_t probe = pces.size();\t// Out of bounds\n\tif ((!pces.empty()) && (sv.length() < 30)) {\n\t\t// Only store short strings in the cache so it doesn't churn with\n\t\t// long comments with only a single comment.\n\n\t\t// Two way associative: try two probe positions.\n\t\tconst size_t hashValue = PositionCacheEntry::Hash(styleNumber, unicode, sv);\n\t\tprobe = hashValue % pces.size();\n\t\tstd::unique_lock<std::mutex> guard(mutex, std::defer_lock);\n\t\tif (needsLocking) {\n\t\t\tguard.lock();\n\t\t}\n\t\tif (pces[probe].Retrieve(styleNumber, unicode, sv, positions)) {\n\t\t\treturn;\n\t\t}\n\t\tconst size_t probe2 = (hashValue * 37) % pces.size();\n\t\tif (pces[probe2].Retrieve(styleNumber, unicode, sv, positions)) {\n\t\t\treturn;\n\t\t}\n\t\t// Not found. Choose the oldest of the two slots to replace\n\t\tif (pces[probe].NewerThan(pces[probe2])) {\n\t\t\tprobe = probe2;\n\t\t}\n\t}\n\n\tconst Font *fontStyle = style.font.get();\n\tif (unicode) {\n\t\tsurface->MeasureWidthsUTF8(fontStyle, sv, positions);\n\t} else {\n\t\tsurface->MeasureWidths(fontStyle, sv, positions);\n\t}\n\tif (probe < pces.size()) {\n\t\t// Store into cache\n\t\tstd::unique_lock<std::mutex> guard(mutex, std::defer_lock);\n\t\tif (needsLocking) {\n\t\t\tguard.lock();\n\t\t}\n\t\tclock++;\n\t\tif (clock > 60000) {\n\t\t\t// Since there are only 16 bits for the clock, wrap it round and\n\t\t\t// reset all cache entries so none get stuck with a high clock.\n\t\t\tfor (PositionCacheEntry &pce : pces) {\n\t\t\t\tpce.ResetClock();\n\t\t\t}\n\t\t\tclock = 2;\n\t\t}\n\t\tallClear = false;\n\t\tpces[probe].Set(styleNumber, unicode, sv, positions, clock);\n\t}\n}\n\nstd::unique_ptr<IPositionCache> Scintilla::Internal::CreatePositionCache() {\n\treturn std::make_unique<PositionCache>();\n}\n"
  },
  {
    "path": "Libraries/scintilla/src/PositionCache.h",
    "content": "// Scintilla source code edit control\n/** @file PositionCache.h\n ** Classes for caching layout information.\n **/\n// Copyright 1998-2009 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#ifndef POSITIONCACHE_H\n#define POSITIONCACHE_H\n\nnamespace Scintilla::Internal {\n\n/**\n* A point in document space.\n* Uses double for sufficient resolution in large (>20,000,000 line) documents.\n*/\nclass PointDocument {\npublic:\n\tdouble x;\n\tdouble y;\n\n\texplicit PointDocument(double x_ = 0, double y_ = 0) noexcept : x(x_), y(y_) {\n\t}\n\n\t// Conversion from Point.\n\texplicit PointDocument(Point pt) noexcept : x(pt.x), y(pt.y) {\n\t}\n};\n\n// There are two points for some positions and this enumeration\n// can choose between the end of the first line or subline\n// and the start of the next line or subline.\nenum class PointEnd {\n\tstart = 0x0,\n\tlineEnd = 0x1,\n\tsubLineEnd = 0x2,\n\tendEither = lineEnd | subLineEnd,\n};\n\nclass BidiData {\npublic:\n\tstd::vector<std::shared_ptr<Font>> stylesFonts;\n\tstd::vector<XYPOSITION> widthReprs;\n\tvoid Resize(size_t maxLineLength_);\n};\n\n/**\n */\nclass LineLayout {\nprivate:\n\tstd::unique_ptr<int []>lineStarts;\n\tint lenLineStarts;\n\t/// Drawing is only performed for @a maxLineLength characters on each line.\n\tSci::Line lineNumber;\npublic:\n\tenum { wrapWidthInfinite = 0x7ffffff };\n\n\tint maxLineLength;\n\tint numCharsInLine;\n\tint numCharsBeforeEOL;\n\tenum class ValidLevel { invalid, checkTextAndStyle, positions, lines } validity;\n\tint xHighlightGuide;\n\tbool highlightColumn;\n\tbool containsCaret;\n\tint edgeColumn;\n\tstd::unique_ptr<char[]> chars;\n\tstd::unique_ptr<unsigned char[]> styles;\n\tstd::unique_ptr<XYPOSITION[]> positions;\n\tunsigned char bracePreviousStyles[2];\n\n\tstd::unique_ptr<BidiData> bidiData;\n\n\t// Wrapped line support\n\tint widthLine;\n\tint lines;\n\tXYPOSITION wrapIndent; // In pixels\n\n\tLineLayout(Sci::Line lineNumber_, int maxLineLength_);\n\tvoid Resize(int maxLineLength_);\n\tvoid ReSet(Sci::Line lineNumber_, Sci::Position maxLineLength_);\n\tvoid EnsureBidiData();\n\tvoid ClearPositions();\n\tvoid Invalidate(ValidLevel validity_) noexcept;\n\tSci::Line LineNumber() const noexcept;\n\tbool CanHold(Sci::Line lineDoc, int lineLength_) const noexcept;\n\tint LineStart(int line) const noexcept;\n\tint LineLength(int line) const noexcept;\n\tenum class Scope { visibleOnly, includeEnd };\n\tint LineLastVisible(int line, Scope scope) const noexcept;\n\tRange SubLineRange(int subLine, Scope scope) const noexcept;\n\tbool InLine(int offset, int line) const noexcept;\n\tint SubLineFromPosition(int posInLine, PointEnd pe) const noexcept;\n\tvoid AddLineStart(Sci::Position start);\n\tvoid SetBracesHighlight(Range rangeLine, const Sci::Position braces[],\n\t\tchar bracesMatchStyle, int xHighlight, bool ignoreStyle);\n\tvoid RestoreBracesHighlight(Range rangeLine, const Sci::Position braces[], bool ignoreStyle);\n\tint FindBefore(XYPOSITION x, Range range) const noexcept;\n\tint FindPositionFromX(XYPOSITION x, Range range, bool charPosition) const noexcept;\n\tPoint PointFromPosition(int posInLine, int lineHeight, PointEnd pe) const noexcept;\n\tXYPOSITION XInLine(Sci::Position index) const noexcept;\n\tInterval Span(int start, int end) const noexcept;\n\tInterval SpanByte(int index) const noexcept;\n\tint EndLineStyle() const noexcept;\n\tvoid WrapLine(const Document *pdoc, Sci::Position posLineStart, Wrap wrapState, XYPOSITION wrapWidth);\n};\n\nstruct ScreenLine : public IScreenLine {\n\tconst LineLayout *ll;\n\tsize_t start;\n\tsize_t len;\n\tXYPOSITION width;\n\tXYPOSITION height;\n\tint ctrlCharPadding;\n\tXYPOSITION tabWidth;\n\tint tabWidthMinimumPixels;\n\n\tScreenLine(const LineLayout *ll_, int subLine, const ViewStyle &vs, XYPOSITION width_, int tabWidthMinimumPixels_);\n\t// Deleted so ScreenLine objects can not be copied.\n\tScreenLine(const ScreenLine &) = delete;\n\tScreenLine(ScreenLine &&) = delete;\n\tvoid operator=(const ScreenLine &) = delete;\n\tvoid operator=(ScreenLine &&) = delete;\n\tvirtual ~ScreenLine();\n\n\tstd::string_view Text() const override;\n\tsize_t Length() const override;\n\tsize_t RepresentationCount() const override;\n\tXYPOSITION Width() const override;\n\tXYPOSITION Height() const override;\n\tXYPOSITION TabWidth() const override;\n\tXYPOSITION TabWidthMinimumPixels() const override;\n\tconst Font *FontOfPosition(size_t position) const override;\n\tXYPOSITION RepresentationWidth(size_t position) const override;\n\tXYPOSITION TabPositionAfter(XYPOSITION xPosition) const override;\n};\n\nstruct SignificantLines {\n\tSci::Line lineCaret;\n\tSci::Line lineTop;\n\tSci::Line linesOnScreen;\n\tScintilla::LineCache level;\n\tbool LineMayCache(Sci::Line line) const noexcept;\n};\n\n/**\n */\nclass LineLayoutCache {\npublic:\nprivate:\n\tScintilla::LineCache level;\n\tstd::vector<std::shared_ptr<LineLayout>>cache;\n\tLineLayout::ValidLevel maxValidity;\n\tint styleClock;\n\tsize_t EntryForLine(Sci::Line line) const noexcept;\n\tvoid AllocateForLevel(Sci::Line linesOnScreen, Sci::Line linesInDoc);\npublic:\n\tLineLayoutCache();\n\t// Deleted so LineLayoutCache objects can not be copied.\n\tLineLayoutCache(const LineLayoutCache &) = delete;\n\tLineLayoutCache(LineLayoutCache &&) = delete;\n\tvoid operator=(const LineLayoutCache &) = delete;\n\tvoid operator=(LineLayoutCache &&) = delete;\n\tvirtual ~LineLayoutCache();\n\tvoid Deallocate() noexcept;\n\tvoid Invalidate(LineLayout::ValidLevel validity_) noexcept;\n\tvoid SetLevel(Scintilla::LineCache level_) noexcept;\n\tScintilla::LineCache GetLevel() const noexcept { return level; }\n\tstd::shared_ptr<LineLayout> Retrieve(Sci::Line lineNumber, Sci::Line lineCaret, int maxChars, int styleClock_,\n\t\tSci::Line linesOnScreen, Sci::Line linesInDoc);\n};\n\nclass Representation {\npublic:\n\tstatic constexpr size_t maxLength = 200;\n\tstd::string stringRep;\n\tRepresentationAppearance appearance;\n\tColourRGBA colour;\n\texplicit Representation(std::string_view value=\"\", RepresentationAppearance appearance_= RepresentationAppearance::Blob) :\n\t\tstringRep(value), appearance(appearance_) {\n\t}\n};\n\ntypedef std::map<unsigned int, Representation> MapRepresentation;\n\nconst char *ControlCharacterString(unsigned char ch) noexcept;\nvoid Hexits(char *hexits, int ch) noexcept;\n\nclass SpecialRepresentations {\n\tMapRepresentation mapReprs;\n\tunsigned short startByteHasReprs[0x100] {};\n\tunsigned int maxKey = 0;\n\tbool crlf = false;\npublic:\n\tvoid SetRepresentation(std::string_view charBytes, std::string_view value);\n\tvoid SetRepresentationAppearance(std::string_view charBytes, RepresentationAppearance appearance);\n\tvoid SetRepresentationColour(std::string_view charBytes, ColourRGBA colour);\n\tvoid ClearRepresentation(std::string_view charBytes);\n\tconst Representation *GetRepresentation(std::string_view charBytes) const;\n\tconst Representation *RepresentationFromCharacter(std::string_view charBytes) const;\n\tbool ContainsCrLf() const noexcept {\n\t\treturn crlf;\n\t}\n\tbool MayContain(unsigned char ch) const noexcept {\n\t\treturn startByteHasReprs[ch] != 0;\n\t}\n\tvoid Clear();\n\tvoid SetDefaultRepresentations(int dbcsCodePage);\n};\n\nstruct TextSegment {\n\tint start;\n\tint length;\n\tconst Representation *representation;\n\tTextSegment(int start_=0, int length_=0, const Representation *representation_=nullptr) noexcept :\n\t\tstart(start_), length(length_), representation(representation_) {\n\t}\n\tint end() const noexcept {\n\t\treturn start + length;\n\t}\n};\n\n// Class to break a line of text into shorter runs at sensible places.\nclass BreakFinder {\n\tconst LineLayout *ll;\n\tconst Range lineRange;\n\tint nextBreak;\n\tstd::vector<int> selAndEdge;\n\tunsigned int saeCurrentPos;\n\tint saeNext;\n\tint subBreak;\n\tconst Document *pdoc;\n\tconst EncodingFamily encodingFamily;\n\tconst SpecialRepresentations *preprs;\n\tvoid Insert(Sci::Position val);\npublic:\n\t// If a whole run is longer than lengthStartSubdivision then subdivide\n\t// into smaller runs at spaces or punctuation.\n\tenum { lengthStartSubdivision = 300 };\n\t// Try to make each subdivided run lengthEachSubdivision or shorter.\n\tenum { lengthEachSubdivision = 100 };\n\tenum class BreakFor {\n\t\tText = 0,\n\t\tSelection = 1,\n\t\tForeground = 2,\n\t\tForegroundAndSelection = 3,\n\t};\n\tBreakFinder(const LineLayout *ll_, const Selection *psel, Range lineRange_, Sci::Position posLineStart,\n\t\tXYPOSITION xStart, BreakFor breakFor, const Document *pdoc_, const SpecialRepresentations *preprs_, const ViewStyle *pvsDraw);\n\t// Deleted so BreakFinder objects can not be copied.\n\tBreakFinder(const BreakFinder &) = delete;\n\tBreakFinder(BreakFinder &&) = delete;\n\tvoid operator=(const BreakFinder &) = delete;\n\tvoid operator=(BreakFinder &&) = delete;\n\t~BreakFinder() noexcept;\n\tTextSegment Next();\n\tbool More() const noexcept;\n};\n\nclass IPositionCache {\npublic:\n\tvirtual ~IPositionCache() = default;\n\tvirtual void Clear() noexcept = 0;\n\tvirtual void SetSize(size_t size_) = 0;\n\tvirtual size_t GetSize() const noexcept = 0;\n\tvirtual void MeasureWidths(Surface *surface, const ViewStyle &vstyle, unsigned int styleNumber,\n\t\tbool unicode, std::string_view sv, XYPOSITION *positions, bool needsLocking) = 0;\n};\n\nstd::unique_ptr<IPositionCache> CreatePositionCache();\n\n}\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/src/RESearch.cxx",
    "content": "// Scintilla source code edit control\n/** @file RESearch.cxx\n ** Regular expression search library.\n **/\n\n/*\n * regex - Regular expression pattern matching and replacement\n *\n * By:  Ozan S. Yigit (oz)\n *      Dept. of Computer Science\n *      York University\n *\n * Original code available from http://www.cs.yorku.ca/~oz/\n * Translation to C++ by Neil Hodgson neilh@scintilla.org\n * Removed all use of register.\n * Converted to modern function prototypes.\n * Put all global/static variables into an object so this code can be\n * used from multiple threads, etc.\n * Some extensions by Philippe Lhoste PhiLho(a)GMX.net\n * '?' extensions by Michael Mullin masmullin@gmail.com\n *\n * These routines are the PUBLIC DOMAIN equivalents of regex\n * routines as found in 4.nBSD UN*X, with minor extensions.\n *\n * These routines are derived from various implementations found\n * in software tools books, and Conroy's grep. They are NOT derived\n * from licensed/restricted software.\n * For more interesting/academic/complicated implementations,\n * see Henry Spencer's regexp routines, or GNU Emacs pattern\n * matching module.\n *\n * Modification history removed.\n *\n * Interfaces:\n *  RESearch::Compile:      compile a regular expression into a NFA.\n *\n *          const char *RESearch::Compile(const char *pattern, int length,\n *                                        bool caseSensitive, bool posix)\n *\n * Returns a short error string if they fail.\n *\n *  RESearch::Execute:      execute the NFA to match a pattern.\n *\n *          int RESearch::Execute(characterIndexer &ci, int lp, int endp)\n *\n *  re_fail:                failure routine for RESearch::Execute. (no longer used)\n *\n *          void re_fail(char *msg, char op)\n *\n * Regular Expressions:\n *\n *      [1]     char    matches itself, unless it is a special\n *                      character (metachar): . \\ [ ] * + ? ^ $\n *                      and ( ) if posix option.\n *\n *      [2]     .       matches any character.\n *\n *      [3]     \\       matches the character following it, except:\n *                      - \\a, \\b, \\f, \\n, \\r, \\t, \\v match the corresponding C\n *                      escape char, respectively BEL, BS, FF, LF, CR, TAB and VT;\n *                      Note that \\r and \\n are never matched because Scintilla\n *                      regex searches are made line per line\n *                      (stripped of end-of-line chars).\n *                      - if not in posix mode, when followed by a\n *                      left or right round bracket (see [8]);\n *                      - when followed by a digit 1 to 9 (see [9]);\n *                      - when followed by a left or right angle bracket\n *                      (see [10]);\n *                      - when followed by d, D, s, S, w or W (see [11]);\n *                      - when followed by x and two hexa digits (see [12].\n *                      Backslash is used as an escape character for all\n *                      other meta-characters, and itself.\n *\n *      [4]     [set]   matches one of the characters in the set.\n *                      If the first character in the set is \"^\",\n *                      it matches the characters NOT in the set, i.e.\n *                      complements the set. A shorthand S-E (start dash end)\n *                      is used to specify a set of characters S up to\n *                      E, inclusive. S and E must be characters, otherwise\n *                      the dash is taken literally (eg. in expression [\\d-a]).\n *                      The special characters \"]\" and \"-\" have no special\n *                      meaning if they appear as the first chars in the set.\n *                      To include both, put - first: [-]A-Z]\n *                      (or just backslash them).\n *                      examples:        match:\n *\n *                              [-]|]    matches these 3 chars,\n *\n *                              []-|]    matches from ] to | chars\n *\n *                              [a-z]    any lowercase alpha\n *\n *                              [^-]]    any char except - and ]\n *\n *                              [^A-Z]   any char except uppercase\n *                                       alpha\n *\n *                              [a-zA-Z] any alpha\n *\n *      [5]     *       any regular expression form [1] to [4]\n *                      (except [8], [9] and [10] forms of [3]),\n *                      followed by closure char (*)\n *                      matches zero or more matches of that form.\n *\n *      [6]     +       same as [5], except it matches one or more.\n *\n *      [5-6]           Both [5] and [6] are greedy (they match as much as possible).\n *                      Unless they are followed by the 'lazy' quantifier (?)\n *                      In which case both [5] and [6] try to match as little as possible\n *\n *      [7]     ?       same as [5] except it matches zero or one.\n *\n *      [8]             a regular expression in the form [1] to [13], enclosed\n *                      as \\(form\\) (or (form) with posix flag) matches what\n *                      form matches. The enclosure creates a set of tags,\n *                      used for [9] and for pattern substitution.\n *                      The tagged forms are numbered starting from 1.\n *\n *      [9]             a \\ followed by a digit 1 to 9 matches whatever a\n *                      previously tagged regular expression ([8]) matched.\n *\n *      [10]    \\<      a regular expression starting with a \\< construct\n *              \\>      and/or ending with a \\> construct, restricts the\n *                      pattern matching to the beginning of a word, and/or\n *                      the end of a word. A word is defined to be a character\n *                      string beginning and/or ending with the characters\n *                      A-Z a-z 0-9 and _. Scintilla extends this definition\n *                      by user setting. The word must also be preceded and/or\n *                      followed by any character outside those mentioned.\n *\n *      [11]    \\l      a backslash followed by d, D, s, S, w or W,\n *                      becomes a character class (both inside and\n *                      outside sets []).\n *                        d: decimal digits\n *                        D: any char except decimal digits\n *                        s: whitespace (space, \\t \\n \\r \\f \\v)\n *                        S: any char except whitespace (see above)\n *                        w: alphanumeric & underscore (changed by user setting)\n *                        W: any char except alphanumeric & underscore (see above)\n *\n *      [12]    \\xHH    a backslash followed by x and two hexa digits,\n *                      becomes the character whose ASCII code is equal\n *                      to these digits. If not followed by two digits,\n *                      it is 'x' char itself.\n *\n *      [13]            a composite regular expression xy where x and y\n *                      are in the form [1] to [12] matches the longest\n *                      match of x followed by a match for y.\n *\n *      [14]    ^       a regular expression starting with a ^ character\n *              $       and/or ending with a $ character, restricts the\n *                      pattern matching to the beginning of the line,\n *                      or the end of line. [anchors] Elsewhere in the\n *                      pattern, ^ and $ are treated as ordinary characters.\n *\n *\n * Acknowledgements:\n *\n *  HCR's Hugh Redelmeier has been most helpful in various\n *  stages of development. He convinced me to include BOW\n *  and EOW constructs, originally invented by Rob Pike at\n *  the University of Toronto.\n *\n * References:\n *              Software tools                  Kernighan & Plauger\n *              Software tools in Pascal        Kernighan & Plauger\n *              Grep [rsx-11 C dist]            David Conroy\n *              ed - text editor                Un*x Programmer's Manual\n *              Advanced editing on Un*x        B. W. Kernighan\n *              RegExp routines                 Henry Spencer\n *\n * Notes:\n *\n *  This implementation uses a bit-set representation for character\n *  classes for speed and compactness. Each character is represented\n *  by one bit in a 256-bit block. Thus, CCL always takes a\n *\tconstant 32 bytes in the internal nfa, and RESearch::Execute does a single\n *  bit comparison to locate the character in the set.\n *\n * Examples:\n *\n *  pattern:    foo*.*\n *  compile:    CHR f CHR o CLO CHR o END CLO ANY END END\n *  matches:    fo foo fooo foobar fobar foxx ...\n *\n *  pattern:    fo[ob]a[rz]\n *  compile:    CHR f CHR o CCL bitset CHR a CCL bitset END\n *  matches:    fobar fooar fobaz fooaz\n *\n *  pattern:    foo\\\\+\n *  compile:    CHR f CHR o CHR o CHR \\ CLO CHR \\ END END\n *  matches:    foo\\ foo\\\\ foo\\\\\\  ...\n *\n *  pattern:    \\(foo\\)[1-3]\\1  (same as foo[1-3]foo)\n *  compile:    BOT 1 CHR f CHR o CHR o EOT 1 CCL bitset REF 1 END\n *  matches:    foo1foo foo2foo foo3foo\n *\n *  pattern:    \\(fo.*\\)-\\1\n *  compile:    BOT 1 CHR f CHR o CLO ANY END EOT 1 CHR - REF 1 END\n *  matches:    foo-foo fo-fo fob-fob foobar-foobar ...\n */\n\n#include <cstddef>\n#include <cstdlib>\n\n#include <stdexcept>\n#include <string>\n#include <array>\n#include <algorithm>\n#include <iterator>\n\n#include \"Position.h\"\n#include \"CharClassify.h\"\n#include \"RESearch.h\"\n\nusing namespace Scintilla::Internal;\n\n#define OKP     1\n#define NOP     0\n\n#define CHR     1\n#define ANY     2\n#define CCL     3\n#define BOL     4\n#define EOL     5\n#define BOT     6\n#define EOT     7\n#define BOW     8\n#define EOW     9\n#define REF     10\n#define CLO     11\n#define CLQ     12 /* 0 to 1 closure */\n#define LCLO    13 /* lazy closure */\n\n#define END     0\n\n/*\n * The following defines are not meant to be changeable.\n * They are for readability only.\n */\n#define BITIND  07\n\n#define badpat(x)\t(*nfa = END, x)\n\n/*\n * Character classification table for word boundary operators BOW\n * and EOW is passed in by the creator of this object (Scintilla\n * Document). The Document default state is that word chars are:\n * 0-9, a-z, A-Z and _\n */\n\nRESearch::RESearch(CharClassify *charClassTable) {\n\tfailure = 0;\n\tcharClass = charClassTable;\n\tsta = NOP;                  /* status of lastpat */\n\tlineStartPos = 0;\n\tlineEndPos = 0;\n\tnfa[0] = END;\n\tClear();\n}\n\nvoid RESearch::Clear() {\n\tbopat.fill(NOTFOUND);\n\teopat.fill(NOTFOUND);\n}\n\nvoid RESearch::ChSet(unsigned char c) noexcept {\n\tbittab[c >> 3] |= 1 << (c & BITIND);\n}\n\nvoid RESearch::ChSetWithCase(unsigned char c, bool caseSensitive) noexcept {\n\tChSet(c);\n\tif (!caseSensitive) {\n\t\tif ((c >= 'a') && (c <= 'z')) {\n\t\t\tChSet(c - 'a' + 'A');\n\t\t} else if ((c >= 'A') && (c <= 'Z')) {\n\t\t\tChSet(c - 'A' + 'a');\n\t\t}\n\t}\n}\n\nnamespace {\n\nconstexpr unsigned char escapeValue(unsigned char ch) noexcept {\n\tswitch (ch) {\n\tcase 'a':\treturn '\\a';\n\tcase 'b':\treturn '\\b';\n\tcase 'f':\treturn '\\f';\n\tcase 'n':\treturn '\\n';\n\tcase 'r':\treturn '\\r';\n\tcase 't':\treturn '\\t';\n\tcase 'v':\treturn '\\v';\n\tdefault:\tbreak;\n\t}\n\treturn 0;\n}\n\nconstexpr int GetHexaChar(unsigned char hd1, unsigned char hd2) noexcept {\n\tint hexValue = 0;\n\tif (hd1 >= '0' && hd1 <= '9') {\n\t\thexValue += 16 * (hd1 - '0');\n\t} else if (hd1 >= 'A' && hd1 <= 'F') {\n\t\thexValue += 16 * (hd1 - 'A' + 10);\n\t} else if (hd1 >= 'a' && hd1 <= 'f') {\n\t\thexValue += 16 * (hd1 - 'a' + 10);\n\t} else {\n\t\treturn -1;\n\t}\n\tif (hd2 >= '0' && hd2 <= '9') {\n\t\thexValue += hd2 - '0';\n\t} else if (hd2 >= 'A' && hd2 <= 'F') {\n\t\thexValue += hd2 - 'A' + 10;\n\t} else if (hd2 >= 'a' && hd2 <= 'f') {\n\t\thexValue += hd2 - 'a' + 10;\n\t} else {\n\t\treturn -1;\n\t}\n\treturn hexValue;\n}\n\nconstexpr int isinset(const char *ap, unsigned char c) noexcept {\n\treturn ap[c >> 3] & (1 << (c & BITIND));\n}\n\n}\n\n/**\n * Called when the parser finds a backslash not followed\n * by a valid expression (like \\( in non-Posix mode).\n * @param pattern : pointer on the char after the backslash.\n * @param incr : (out) number of chars to skip after expression evaluation.\n * @return the char if it resolves to a simple char,\n * or -1 for a char class. In this case, bittab is changed.\n */\nint RESearch::GetBackslashExpression(const char *pattern, int &incr) noexcept {\n\t// Since error reporting is primitive and messages are not used anyway,\n\t// I choose to interpret unexpected syntax in a logical way instead\n\t// of reporting errors. Otherwise, we can stick on, eg., PCRE behaviour.\n\tincr = 0;\t// Most of the time, will skip the char \"naturally\".\n\tint result = -1;\n\tconst unsigned char bsc = *pattern;\n\tif (!bsc) {\n\t\t// Avoid overrun\n\t\tresult = '\\\\';\t// \\ at end of pattern, take it literally\n\t\treturn result;\n\t}\n\n\tswitch (bsc) {\n\tcase 'a':\n\tcase 'b':\n\tcase 'n':\n\tcase 'f':\n\tcase 'r':\n\tcase 't':\n\tcase 'v':\n\t\tresult = escapeValue(bsc);\n\t\tbreak;\n\tcase 'x': {\n\t\t\tconst unsigned char hd1 = *(pattern + 1);\n\t\t\tconst unsigned char hd2 = *(pattern + 2);\n\t\t\tconst int hexValue = GetHexaChar(hd1, hd2);\n\t\t\tif (hexValue >= 0) {\n\t\t\t\tresult = hexValue;\n\t\t\t\tincr = 2;\t// Must skip the digits\n\t\t\t} else {\n\t\t\t\tresult = 'x';\t// \\x without 2 digits: see it as 'x'\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase 'd':\n\t\tfor (int c = '0'; c <= '9'; c++) {\n\t\t\tChSet(static_cast<unsigned char>(c));\n\t\t}\n\t\tbreak;\n\tcase 'D':\n\t\tfor (int c = 0; c < MAXCHR; c++) {\n\t\t\tif (c < '0' || c > '9') {\n\t\t\t\tChSet(static_cast<unsigned char>(c));\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase 's':\n\t\tChSet(' ');\n\t\tChSet('\\t');\n\t\tChSet('\\n');\n\t\tChSet('\\r');\n\t\tChSet('\\f');\n\t\tChSet('\\v');\n\t\tbreak;\n\tcase 'S':\n\t\tfor (int c = 0; c < MAXCHR; c++) {\n\t\t\tif (c != ' ' && !(c >= 0x09 && c <= 0x0D)) {\n\t\t\t\tChSet(static_cast<unsigned char>(c));\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase 'w':\n\t\tfor (int c = 0; c < MAXCHR; c++) {\n\t\t\tif (iswordc(static_cast<unsigned char>(c))) {\n\t\t\t\tChSet(static_cast<unsigned char>(c));\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase 'W':\n\t\tfor (int c = 0; c < MAXCHR; c++) {\n\t\t\tif (!iswordc(static_cast<unsigned char>(c))) {\n\t\t\t\tChSet(static_cast<unsigned char>(c));\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\tresult = bsc;\n\t}\n\treturn result;\n}\n\nconst char *RESearch::Compile(const char *pattern, Sci::Position length, bool caseSensitive, bool posix) {\n\tif (!pattern || !length) {\n\t\tif (sta)\n\t\t\treturn nullptr;\n\t\telse\n\t\t\treturn badpat(\"No previous regular expression\");\n\t}\n\n\tbittab.fill(0);\n\tnfa[0] = END;\n\n\tchar *mp=nfa;          /* nfa pointer       */\n\tchar *sp=nfa;          /* another saved pointer */\n\tconst char * const mpMax = mp + MAXNFA - BITBLK - 10;\n\n\tint tagstk[MAXTAG]{};  /* subpat tag stack */\n\tint tagi = 0;          /* tag stack index   */\n\tint tagc = 1;          /* actual tag count  */\n\n\tsta = NOP;\n\n\tconst char *p=pattern;     /* pattern pointer   */\n\tfor (int i=0; i<length; i++, p++) {\n\t\tif (mp > mpMax)\n\t\t\treturn badpat(\"Pattern too long\");\n\t\tchar *lp = mp;\t\t\t/* saved pointer     */\n\t\tswitch (*p) {\n\n\t\tcase '.':               /* match any char  */\n\t\t\t*mp++ = ANY;\n\t\t\tbreak;\n\n\t\tcase '^':               /* match beginning */\n\t\t\tif (p == pattern) {\n\t\t\t\t*mp++ = BOL;\n\t\t\t} else {\n\t\t\t\t*mp++ = CHR;\n\t\t\t\t*mp++ = *p;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase '$':               /* match endofline */\n\t\t\tif (!p[1]) {\n\t\t\t\t*mp++ = EOL;\n\t\t\t} else {\n\t\t\t\t*mp++ = CHR;\n\t\t\t\t*mp++ = *p;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase '[': {               /* match char class */\n\t\t\tint prevChar = 0;\n\t\t\tchar mask = 0;      /* xor mask -CCL/NCL */\n\n\t\t\ti++;\n\t\t\tif (*++p == '^') {\n\t\t\t\tmask = '\\377';\n\t\t\t\ti++;\n\t\t\t\tp++;\n\t\t\t}\n\n\t\t\tif (*p == '-') {\t/* real dash */\n\t\t\t\ti++;\n\t\t\t\tprevChar = *p;\n\t\t\t\tChSet(*p++);\n\t\t\t}\n\t\t\tif (*p == ']') {\t/* real brace */\n\t\t\t\ti++;\n\t\t\t\tprevChar = *p;\n\t\t\t\tChSet(*p++);\n\t\t\t}\n\t\t\twhile (*p && *p != ']') {\n\t\t\t\tif (*p == '-') {\n\t\t\t\t\tif (prevChar < 0) {\n\t\t\t\t\t\t// Previous def. was a char class like \\d, take dash literally\n\t\t\t\t\t\tprevChar = *p;\n\t\t\t\t\t\tChSet(*p);\n\t\t\t\t\t} else if (p[1]) {\n\t\t\t\t\t\tif (p[1] != ']') {\n\t\t\t\t\t\t\tint c1 = prevChar + 1;\n\t\t\t\t\t\t\ti++;\n\t\t\t\t\t\t\tint c2 = static_cast<unsigned char>(*++p);\n\t\t\t\t\t\t\tif (c2 == '\\\\') {\n\t\t\t\t\t\t\t\tif (!p[1]) {\t// End of RE\n\t\t\t\t\t\t\t\t\treturn badpat(\"Missing ]\");\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\ti++;\n\t\t\t\t\t\t\t\t\tp++;\n\t\t\t\t\t\t\t\t\tint incr = 0;\n\t\t\t\t\t\t\t\t\tc2 = GetBackslashExpression(p, incr);\n\t\t\t\t\t\t\t\t\ti += incr;\n\t\t\t\t\t\t\t\t\tp += incr;\n\t\t\t\t\t\t\t\t\tif (c2 >= 0) {\n\t\t\t\t\t\t\t\t\t\t// Convention: \\c (c is any char) is case sensitive, whatever the option\n\t\t\t\t\t\t\t\t\t\tChSet(static_cast<unsigned char>(c2));\n\t\t\t\t\t\t\t\t\t\tprevChar = c2;\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t// bittab is already changed\n\t\t\t\t\t\t\t\t\t\tprevChar = -1;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (prevChar < 0) {\n\t\t\t\t\t\t\t\t// Char after dash is char class like \\d, take dash literally\n\t\t\t\t\t\t\t\tprevChar = '-';\n\t\t\t\t\t\t\t\tChSet('-');\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t// Put all chars between c1 and c2 included in the char set\n\t\t\t\t\t\t\t\twhile (c1 <= c2) {\n\t\t\t\t\t\t\t\t\tChSetWithCase(static_cast<unsigned char>(c1++), caseSensitive);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// Dash before the ], take it literally\n\t\t\t\t\t\t\tprevChar = *p;\n\t\t\t\t\t\t\tChSet(*p);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn badpat(\"Missing ]\");\n\t\t\t\t\t}\n\t\t\t\t} else if (*p == '\\\\' && p[1]) {\n\t\t\t\t\ti++;\n\t\t\t\t\tp++;\n\t\t\t\t\tint incr = 0;\n\t\t\t\t\tconst int c = GetBackslashExpression(p, incr);\n\t\t\t\t\ti += incr;\n\t\t\t\t\tp += incr;\n\t\t\t\t\tif (c >= 0) {\n\t\t\t\t\t\t// Convention: \\c (c is any char) is case sensitive, whatever the option\n\t\t\t\t\t\tChSet(static_cast<unsigned char>(c));\n\t\t\t\t\t\tprevChar = c;\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// bittab is already changed\n\t\t\t\t\t\tprevChar = -1;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tprevChar = static_cast<unsigned char>(*p);\n\t\t\t\t\tChSetWithCase(*p, caseSensitive);\n\t\t\t\t}\n\t\t\t\ti++;\n\t\t\t\tp++;\n\t\t\t}\n\t\t\tif (!*p)\n\t\t\t\treturn badpat(\"Missing ]\");\n\n\t\t\t*mp++ = CCL;\n\t\t\tfor (const unsigned char byte : bittab) {\n\t\t\t\t*mp++ = mask ^ byte;\n\t\t\t}\n\t\t\tbittab.fill(0);\n\t\t} break;\n\n\t\tcase '*':               /* match 0 or more... */\n\t\tcase '+':               /* match 1 or more... */\n\t\tcase '?':\n\t\t\tif (p == pattern)\n\t\t\t\treturn badpat(\"Empty closure\");\n\t\t\tlp = sp;\t\t/* previous opcode */\n\t\t\tif (*lp == CLO || *lp == LCLO)\t\t/* equivalence... */\n\t\t\t\tbreak;\n\t\t\tswitch (*lp) {\n\n\t\t\tcase BOL:\n\t\t\tcase BOT:\n\t\t\tcase EOT:\n\t\t\tcase BOW:\n\t\t\tcase EOW:\n\t\t\tcase REF:\n\t\t\t\treturn badpat(\"Illegal closure\");\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (*p == '+')\n\t\t\t\tfor (sp = mp; lp < sp; lp++)\n\t\t\t\t\t*mp++ = *lp;\n\n\t\t\t*mp++ = END;\n\t\t\t*mp++ = END;\n\t\t\tsp = mp;\n\n\t\t\twhile (--mp > lp)\n\t\t\t\t*mp = mp[-1];\n\t\t\tif (*p == '?')          *mp = CLQ;\n\t\t\telse if (p[1] == '?') *mp = LCLO;\n\t\t\telse                    *mp = CLO;\n\n\t\t\tmp = sp;\n\t\t\tbreak;\n\n\t\tcase '\\\\':              /* tags, backrefs... */\n\t\t\ti++;\n\t\t\tswitch (*++p) {\n\t\t\tcase '<':\n\t\t\t\t*mp++ = BOW;\n\t\t\t\tbreak;\n\t\t\tcase '>':\n\t\t\t\tif (*sp == BOW)\n\t\t\t\t\treturn badpat(\"Null pattern inside \\\\<\\\\>\");\n\t\t\t\t*mp++ = EOW;\n\t\t\t\tbreak;\n\t\t\tcase '1':\n\t\t\tcase '2':\n\t\t\tcase '3':\n\t\t\tcase '4':\n\t\t\tcase '5':\n\t\t\tcase '6':\n\t\t\tcase '7':\n\t\t\tcase '8':\n\t\t\tcase '9': {\n\t\t\t\tconst int n = *p-'0';\n\t\t\t\tif (tagi > 0 && tagstk[tagi] == n)\n\t\t\t\t\treturn badpat(\"Cyclical reference\");\n\t\t\t\tif (tagc > n) {\n\t\t\t\t\t*mp++ = REF;\n\t\t\t\t\t*mp++ = static_cast<char>(n);\n\t\t\t\t} else {\n\t\t\t\t\treturn badpat(\"Undetermined reference\");\n\t\t\t\t}\n\t\t\t} break;\n\t\t\tdefault:\n\t\t\t\tif (!posix && *p == '(') {\n\t\t\t\t\tif (tagc < MAXTAG) {\n\t\t\t\t\t\ttagstk[++tagi] = tagc;\n\t\t\t\t\t\t*mp++ = BOT;\n\t\t\t\t\t\t*mp++ = static_cast<char>(tagc++);\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn badpat(\"Too many \\\\(\\\\) pairs\");\n\t\t\t\t\t}\n\t\t\t\t} else if (!posix && *p == ')') {\n\t\t\t\t\tif (*sp == BOT)\n\t\t\t\t\t\treturn badpat(\"Null pattern inside \\\\(\\\\)\");\n\t\t\t\t\tif (tagi > 0) {\n\t\t\t\t\t\t*mp++ = EOT;\n\t\t\t\t\t\t*mp++ = static_cast<char>(tagstk[tagi--]);\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn badpat(\"Unmatched \\\\)\");\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tint incr = 0;\n\t\t\t\t\tconst int c = GetBackslashExpression(p, incr);\n\t\t\t\t\ti += incr;\n\t\t\t\t\tp += incr;\n\t\t\t\t\tif (c >= 0) {\n\t\t\t\t\t\t*mp++ = CHR;\n\t\t\t\t\t\t*mp++ = static_cast<unsigned char>(c);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t*mp++ = CCL;\n\t\t\t\t\t\tfor (const unsigned char byte : bittab) {\n\t\t\t\t\t\t\t*mp++ = byte;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbittab.fill(0);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tdefault :               /* an ordinary char */\n\t\t\tif (posix && *p == '(') {\n\t\t\t\tif (tagc < MAXTAG) {\n\t\t\t\t\ttagstk[++tagi] = tagc;\n\t\t\t\t\t*mp++ = BOT;\n\t\t\t\t\t*mp++ = static_cast<char>(tagc++);\n\t\t\t\t} else {\n\t\t\t\t\treturn badpat(\"Too many () pairs\");\n\t\t\t\t}\n\t\t\t} else if (posix && *p == ')') {\n\t\t\t\tif (*sp == BOT)\n\t\t\t\t\treturn badpat(\"Null pattern inside ()\");\n\t\t\t\tif (tagi > 0) {\n\t\t\t\t\t*mp++ = EOT;\n\t\t\t\t\t*mp++ = static_cast<char>(tagstk[tagi--]);\n\t\t\t\t} else {\n\t\t\t\t\treturn badpat(\"Unmatched )\");\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tunsigned char c = *p;\n\t\t\t\tif (!c)\t// End of RE\n\t\t\t\t\tc = '\\\\';\t// We take it as raw backslash\n\t\t\t\tif (caseSensitive || !iswordc(c)) {\n\t\t\t\t\t*mp++ = CHR;\n\t\t\t\t\t*mp++ = c;\n\t\t\t\t} else {\n\t\t\t\t\tChSetWithCase(c, false);\n\t\t\t\t\t*mp++ = CCL;\n\t\t\t\t\tfor (const unsigned char byte : bittab) {\n\t\t\t\t\t\t*mp++ = byte;\n\t\t\t\t\t}\n\t\t\t\t\tbittab.fill(0);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tsp = lp;\n\t}\n\tif (tagi > 0)\n\t\treturn badpat((posix ? \"Unmatched (\" : \"Unmatched \\\\(\"));\n\t*mp = END;\n\tsta = OKP;\n\treturn nullptr;\n}\n\n/*\n * RESearch::Execute:\n *   execute nfa to find a match.\n *\n *  special cases: (nfa[0])\n *      BOL\n *          Match only once, starting from the\n *          beginning.\n *      CHR\n *          First locate the character without\n *          calling PMatch, and if found, call\n *          PMatch for the remaining string.\n *      END\n *          RESearch::Compile failed, poor luser did not\n *          check for it. Fail fast.\n *\n *  If a match is found, bopat[0] and eopat[0] are set\n *  to the beginning and the end of the matched fragment,\n *  respectively.\n *\n */\nint RESearch::Execute(const CharacterIndexer &ci, Sci::Position lp, Sci::Position endp) {\n\tSci::Position ep = NOTFOUND;\n\tconst char * const ap = nfa;\n\n\tfailure = 0;\n\n\tClear();\n\n\tswitch (*ap) {\n\n\tcase BOL:\t\t\t/* anchored: match from BOL only */\n\t\tep = PMatch(ci, lp, endp, ap);\n\t\tbreak;\n\tcase EOL:\t\t\t/* just searching for end of line normal path doesn't work */\n\t\tif (endp == lineEndPos && ap[1] == END) {\n\t\t\tlp = endp;\n\t\t\tep = lp;\n\t\t\tbreak;\n\t\t} else {\n\t\t\treturn 0;\n\t\t}\n\tcase CHR: {\t\t\t/* ordinary char: locate it fast */\n\t\tconst unsigned char c = ap[1];\n\t\twhile ((lp < endp) && (static_cast<unsigned char>(ci.CharAt(lp)) != c))\n\t\t\tlp++;\n\t\tif (lp >= endp)\t/* if EOS, fail, else fall through. */\n\t\t\treturn 0;\n\t}\n\t[[fallthrough]];\n\tdefault:\t\t\t/* regular matching all the way. */\n\t\twhile (lp < endp) {\n\t\t\tep = PMatch(ci, lp, endp, ap);\n\t\t\tif (ep != NOTFOUND) {\n\t\t\t\t// fix match started from middle of character like DBCS trailing ASCII byte\n\t\t\t\tconst Sci::Position pos = ci.MovePositionOutsideChar(lp, -1);\n\t\t\t\tif (pos != lp) {\n\t\t\t\t\tep = NOTFOUND;\n\t\t\t\t} else {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tlp++;\n\t\t}\n\t\tbreak;\n\tcase END:\t\t\t/* munged automaton. fail always */\n\t\treturn 0;\n\t}\n\tif (ep == NOTFOUND) {\n\t\t/* similar to EOL, match EOW at line end */\n\t\tif (endp == lineEndPos && *ap == EOW) {\n\t\t\tif ((ap[1] == END || ((ap[1] == EOL && ap[2] == END))) && iswordc(ci.CharAt(lp - 1))) {\n\t\t\t\tlp = endp;\n\t\t\t\tep = lp;\n\t\t\t} else {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t} else {\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\tep = ci.MovePositionOutsideChar(ep, 1);\n\tbopat[0] = lp;\n\teopat[0] = ep;\n\treturn 1;\n}\n\n/*\n * PMatch: internal routine for the hard part\n *\n *  This code is partly snarfed from an early grep written by\n *  David Conroy. The backref and tag stuff, and various other\n *  innovations are by oz.\n *\n *  special case optimizations: (nfa[n], nfa[n+1])\n *      CLO ANY\n *          We KNOW .* will match everything up to the\n *          end of line. Thus, directly go to the end of\n *          line, without recursive PMatch calls. As in\n *          the other closure cases, the remaining pattern\n *          must be matched by moving backwards on the\n *          string recursively, to find a match for xy\n *          (x is \".*\" and y is the remaining pattern)\n *          where the match satisfies the LONGEST match for\n *          x followed by a match for y.\n *      CLO CHR\n *          We can again scan the string forward for the\n *          single char and at the point of failure, we\n *          execute the remaining nfa recursively, same as\n *          above.\n *\n *  At the end of a successful match, bopat[n] and eopat[n]\n *  are set to the beginning and end of subpatterns matched\n *  by tagged expressions (n = 1 to 9).\n */\n\n//extern void re_fail(char *,char);\n\n/*\n * skip values for CLO XXX to skip past the closure\n */\n\n#define ANYSKIP 2 \t/* [CLO] ANY END          */\n#define CHRSKIP 3\t/* [CLO] CHR chr END      */\n#define CCLSKIP 34\t/* [CLO] CCL 32 bytes END */\n\nSci::Position RESearch::PMatch(const CharacterIndexer &ci, Sci::Position lp, Sci::Position endp, const char *ap) {\n\tunsigned char op = 0;\n\n\twhile ((op = *ap++) != END)\n\t\tswitch (op) {\n\n\t\tcase CHR:\n\t\t\tif (ci.CharAt(lp++) != *ap++)\n\t\t\t\treturn NOTFOUND;\n\t\t\tbreak;\n\t\tcase ANY:\n\t\t\tif (lp++ >= endp)\n\t\t\t\treturn NOTFOUND;\n\t\t\tbreak;\n\t\tcase CCL:\n\t\t\tif (lp >= endp)\n\t\t\t\treturn NOTFOUND;\n\t\t\tif (!isinset(ap, ci.CharAt(lp++)))\n\t\t\t\treturn NOTFOUND;\n\t\t\tap += BITBLK;\n\t\t\tbreak;\n\t\tcase BOL:\n\t\t\tif (lp != lineStartPos)\n\t\t\t\treturn NOTFOUND;\n\t\t\tbreak;\n\t\tcase EOL:\n\t\t\tif (lp < lineEndPos)\n\t\t\t\treturn NOTFOUND;\n\t\t\tbreak;\n\t\tcase BOT:\n\t\t\tif (lp != ci.MovePositionOutsideChar(lp, -1)) {\n\t\t\t\treturn NOTFOUND;\n\t\t\t}\n\t\t\tbopat[static_cast<unsigned char>(*ap++)] = lp;\n\t\t\tbreak;\n\t\tcase EOT:\n\t\t\tlp = ci.MovePositionOutsideChar(lp, 1);\n\t\t\teopat[static_cast<unsigned char>(*ap++)] = lp;\n\t\t\tbreak;\n\t\tcase BOW:\n\t\t\tif ((lp!=lineStartPos && iswordc(ci.CharAt(lp-1))) || !iswordc(ci.CharAt(lp)))\n\t\t\t\treturn NOTFOUND;\n\t\t\tbreak;\n\t\tcase EOW:\n\t\t\tif (lp==lineStartPos || !iswordc(ci.CharAt(lp-1)) || iswordc(ci.CharAt(lp)))\n\t\t\t\treturn NOTFOUND;\n\t\t\tbreak;\n\t\tcase REF: {\n\t\t\tconst int n = static_cast<unsigned char>(*ap++);\n\t\t\tSci::Position bp = bopat[n];\t\t/* beginning of subpat... */\n\t\t\tconst Sci::Position ep = eopat[n];\t/* ending of subpat...    */\n\t\t\twhile (bp < ep)\n\t\t\t\tif (ci.CharAt(bp++) != ci.CharAt(lp++))\n\t\t\t\t\treturn NOTFOUND;\n\t\t} break;\n\t\tcase LCLO:\n\t\tcase CLQ:\n\t\tcase CLO: {\n\t\t\tint n = 0;\n\t\t\tconst Sci::Position are = lp;\t/* to save the line ptr.  */\n\t\t\tswitch (*ap) {\n\n\t\t\tcase ANY:\n\t\t\t\tif (op == CLO || op == LCLO)\n\t\t\t\t\twhile (lp < endp)\n\t\t\t\t\t\tlp++;\n\t\t\t\telse if (lp < endp)\n\t\t\t\t\tlp++;\n\n\t\t\t\tn = ANYSKIP;\n\t\t\t\tbreak;\n\t\t\tcase CHR: {\n\t\t\t\tconst char c = ap[1];\n\t\t\t\tif (op == CLO || op == LCLO)\n\t\t\t\t\twhile ((lp < endp) && (c == ci.CharAt(lp)))\n\t\t\t\t\t\tlp++;\n\t\t\t\telse if ((lp < endp) && (c == ci.CharAt(lp)))\n\t\t\t\t\tlp++;\n\t\t\t\tn = CHRSKIP;\n\t\t\t} break;\n\t\t\tcase CCL:\n\t\t\t\twhile ((lp < endp) && isinset(ap+1, ci.CharAt(lp)))\n\t\t\t\t\tlp++;\n\t\t\t\tn = CCLSKIP;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tfailure = true;\n\t\t\t\t//re_fail(\"closure: bad nfa.\", *ap);\n\t\t\t\treturn NOTFOUND;\n\t\t\t}\n\t\t\tap += n;\n\n\t\t\tSci::Position llp = lp;\t\t/* lazy lp for LCLO       */\n\t\t\tSci::Position e = NOTFOUND; /* extra pointer for CLO  */\n\t\t\twhile (llp >= are) {\n\t\t\t\tconst Sci::Position q = PMatch(ci, llp, endp, ap);\n\t\t\t\tif (q != NOTFOUND) {\n\t\t\t\t\te = q;\n\t\t\t\t\tlp = llp;\n\t\t\t\t\tif (op != LCLO) return e;\n\t\t\t\t}\n\t\t\t\tif (*ap == END) return e;\n\t\t\t\t--llp;\n\t\t\t}\n\t\t\tif (*ap == EOT)\n\t\t\t\tPMatch(ci, lp, endp, ap);\n\t\t\treturn e;\n\t\t}\n\t\tdefault:\n\t\t\t//re_fail(\"RESearch::Execute: bad nfa.\", static_cast<char>(op));\n\t\t\treturn NOTFOUND;\n\t\t}\n\treturn lp;\n}\n"
  },
  {
    "path": "Libraries/scintilla/src/RESearch.h",
    "content": "// Scintilla source code edit control\n/** @file RESearch.h\n ** Interface to the regular expression search library.\n **/\n// Written by Neil Hodgson <neilh@scintilla.org>\n// Based on the work of Ozan S. Yigit.\n// This file is in the public domain.\n\n#ifndef RESEARCH_H\n#define RESEARCH_H\n\nnamespace Scintilla::Internal {\n\nclass CharacterIndexer {\npublic:\n\tvirtual char CharAt(Sci::Position index) const=0;\n\tvirtual Sci::Position MovePositionOutsideChar(Sci::Position pos, [[maybe_unused]] Sci::Position moveDir) const noexcept=0;\n};\n\nclass RESearch {\n\npublic:\n\texplicit RESearch(CharClassify *charClassTable);\n\t// No dynamic allocation so default copy constructor and assignment operator are OK.\n\tvoid Clear();\n\tconst char *Compile(const char *pattern, Sci::Position length, bool caseSensitive, bool posix);\n\tint Execute(const CharacterIndexer &ci, Sci::Position lp, Sci::Position endp);\n\tvoid SetLineRange(Sci::Position startPos, Sci::Position endPos) noexcept {\n\t\tlineStartPos = startPos;\n\t\tlineEndPos = endPos;\n\t}\n\n\tstatic constexpr int MAXTAG = 10;\n\tstatic constexpr int NOTFOUND = -1;\n\n\tusing MatchPositions = std::array<Sci::Position, MAXTAG>;\n\tMatchPositions bopat;\n\tMatchPositions eopat;\n\nprivate:\n\n\tstatic constexpr int MAXNFA = 4096;\n\t// The following constants are not meant to be changeable.\n\t// They are for readability only.\n\tstatic constexpr int MAXCHR = 256;\n\tstatic constexpr int CHRBIT = 8;\n\tstatic constexpr int BITBLK = MAXCHR / CHRBIT;\n\n\tvoid ChSet(unsigned char c) noexcept;\n\tvoid ChSetWithCase(unsigned char c, bool caseSensitive) noexcept;\n\tint GetBackslashExpression(const char *pattern, int &incr) noexcept;\n\n\tSci::Position PMatch(const CharacterIndexer &ci, Sci::Position lp, Sci::Position endp, const char *ap);\n\n\t// positions to match line start and line end\n\tSci::Position lineStartPos;\n\tSci::Position lineEndPos;\n\tchar nfa[MAXNFA];    /* automaton */\n\tint sta;\n\tint failure;\n\tstd::array<unsigned char, BITBLK> bittab {}; /* bit table for CCL pre-set bits */\n\tCharClassify *charClass;\n\tbool iswordc(unsigned char x) const noexcept {\n\t\treturn charClass->IsWord(x);\n\t}\n};\n\n}\n\n#endif\n\n"
  },
  {
    "path": "Libraries/scintilla/src/RunStyles.cxx",
    "content": "/** @file RunStyles.cxx\n ** Data structure used to store sparse styles.\n **/\n// Copyright 1998-2007 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#include <cstddef>\n#include <cstdlib>\n#include <cstdint>\n#include <cstring>\n#include <cstdio>\n#include <cstdarg>\n#include <climits>\n\n#include <stdexcept>\n#include <string_view>\n#include <vector>\n#include <optional>\n#include <algorithm>\n#include <memory>\n\n#include \"Debugging.h\"\n\n#include \"Position.h\"\n#include \"SplitVector.h\"\n#include \"Partitioning.h\"\n#include \"RunStyles.h\"\n\nusing namespace Scintilla::Internal;\n\n// Find the first run at a position\ntemplate <typename DISTANCE, typename STYLE>\nDISTANCE RunStyles<DISTANCE, STYLE>::RunFromPosition(DISTANCE position) const noexcept {\n\tDISTANCE run = starts.PartitionFromPosition(position);\n\t// Go to first element with this position\n\twhile ((run > 0) && (position == starts.PositionFromPartition(run-1))) {\n\t\trun--;\n\t}\n\treturn run;\n}\n\n// If there is no run boundary at position, insert one continuing style.\ntemplate <typename DISTANCE, typename STYLE>\nDISTANCE RunStyles<DISTANCE, STYLE>::SplitRun(DISTANCE position) {\n\tDISTANCE run = RunFromPosition(position);\n\tconst DISTANCE posRun = starts.PositionFromPartition(run);\n\tif (posRun < position) {\n\t\tSTYLE runStyle = ValueAt(position);\n\t\trun++;\n\t\tstarts.InsertPartition(run, position);\n\t\tstyles.InsertValue(run, 1, runStyle);\n\t}\n\treturn run;\n}\n\ntemplate <typename DISTANCE, typename STYLE>\nvoid RunStyles<DISTANCE, STYLE>::RemoveRun(DISTANCE run) {\n\tstarts.RemovePartition(run);\n\tstyles.DeleteRange(run, 1);\n}\n\ntemplate <typename DISTANCE, typename STYLE>\nvoid RunStyles<DISTANCE, STYLE>::RemoveRunIfEmpty(DISTANCE run) {\n\tif ((run < starts.Partitions()) && (starts.Partitions() > 1)) {\n\t\tif (starts.PositionFromPartition(run) == starts.PositionFromPartition(run+1)) {\n\t\t\tRemoveRun(run);\n\t\t}\n\t}\n}\n\ntemplate <typename DISTANCE, typename STYLE>\nvoid RunStyles<DISTANCE, STYLE>::RemoveRunIfSameAsPrevious(DISTANCE run) {\n\tif ((run > 0) && (run < starts.Partitions())) {\n\t\tconst DISTANCE runBefore = run - 1;\n\t\tif (styles.ValueAt(runBefore) == styles.ValueAt(run)) {\n\t\t\tRemoveRun(run);\n\t\t}\n\t}\n}\n\ntemplate <typename DISTANCE, typename STYLE>\nRunStyles<DISTANCE, STYLE>::RunStyles() {\n\tstyles.InsertValue(0, 2, 0);\n}\n\ntemplate <typename DISTANCE, typename STYLE>\nDISTANCE RunStyles<DISTANCE, STYLE>::Length() const noexcept {\n\treturn starts.PositionFromPartition(starts.Partitions());\n}\n\ntemplate <typename DISTANCE, typename STYLE>\nSTYLE RunStyles<DISTANCE, STYLE>::ValueAt(DISTANCE position) const noexcept {\n\treturn styles.ValueAt(starts.PartitionFromPosition(position));\n}\n\ntemplate <typename DISTANCE, typename STYLE>\nDISTANCE RunStyles<DISTANCE, STYLE>::FindNextChange(DISTANCE position, DISTANCE end) const noexcept {\n\tconst DISTANCE run = starts.PartitionFromPosition(position);\n\tif (run < starts.Partitions()) {\n\t\tconst DISTANCE runChange = starts.PositionFromPartition(run);\n\t\tif (runChange > position)\n\t\t\treturn runChange;\n\t\tconst DISTANCE nextChange = starts.PositionFromPartition(run + 1);\n\t\tif (nextChange > position) {\n\t\t\treturn nextChange;\n\t\t} else if (position < end) {\n\t\t\treturn end;\n\t\t} else {\n\t\t\treturn end + 1;\n\t\t}\n\t} else {\n\t\treturn end + 1;\n\t}\n}\n\ntemplate <typename DISTANCE, typename STYLE>\nDISTANCE RunStyles<DISTANCE, STYLE>::StartRun(DISTANCE position) const noexcept {\n\treturn starts.PositionFromPartition(starts.PartitionFromPosition(position));\n}\n\ntemplate <typename DISTANCE, typename STYLE>\nDISTANCE RunStyles<DISTANCE, STYLE>::EndRun(DISTANCE position) const noexcept {\n\treturn starts.PositionFromPartition(starts.PartitionFromPosition(position) + 1);\n}\n\ntemplate <typename DISTANCE, typename STYLE>\nFillResult<DISTANCE> RunStyles<DISTANCE, STYLE>::FillRange(DISTANCE position, STYLE value, DISTANCE fillLength) {\n\tconst FillResult<DISTANCE> resultNoChange{false, position, fillLength};\n\tif (fillLength <= 0) {\n\t\treturn resultNoChange;\n\t}\n\tDISTANCE end = position + fillLength;\n\tif (end > Length()) {\n\t\treturn resultNoChange;\n\t}\n\tDISTANCE runEnd = RunFromPosition(end);\n\tconst STYLE valueCurrent = styles.ValueAt(runEnd);\n\tif (valueCurrent == value) {\n\t\t// End already has value so trim range.\n\t\tend = starts.PositionFromPartition(runEnd);\n\t\tif (position >= end) {\n\t\t\t// Whole range is already same as value so no action\n\t\t\treturn resultNoChange;\n\t\t}\n\t\tfillLength = end - position;\n\t} else {\n\t\tconst DISTANCE startRun = starts.PositionFromPartition(runEnd);\n\t\tif (position > startRun) {\n\t\t\tconst DISTANCE runNext = runEnd + 1;\n\t\t\tconst DISTANCE endRun = starts.PositionFromPartition(runNext);\n\t\t\tif (end < endRun) {\n\t\t\t\t// New piece is completely inside a run with a different value so its a simple\n\t\t\t\t// insertion of two points [ (position, value), (end, valueCurrent) ]\n\t\t\t\tconst DISTANCE range[] { position, end};\n\t\t\t\tstarts.InsertPartitions(runEnd + 1, range, 2);\n\t\t\t\t// Temporary runEndIndex silences non-useful arithmetic overflow warnings\n\t\t\t\tconst ptrdiff_t runEndIndex = runEnd;\n\t\t\t\tstyles.Insert(runEndIndex + 1, value);\n\t\t\t\tstyles.Insert(runEndIndex + 2, valueCurrent);\n\t\t\t\treturn { true, position, fillLength };\n\t\t\t}\n\t\t}\n\t\trunEnd = SplitRun(end);\n\t}\n\tDISTANCE runStart = RunFromPosition(position);\n\tif (styles.ValueAt(runStart) == value) {\n\t\t// Start is in expected value so trim range.\n\t\trunStart++;\n\t\tposition = starts.PositionFromPartition(runStart);\n\t\tfillLength = end - position;\n\t} else {\n\t\tif (starts.PositionFromPartition(runStart) < position) {\n\t\t\trunStart = SplitRun(position);\n\t\t\trunEnd++;\n\t\t}\n\t}\n\tif (runStart < runEnd) {\n\t\tconst FillResult<DISTANCE> result{ true, position, fillLength };\n\t\tstyles.SetValueAt(runStart, value);\n\t\t// Remove each old run over the range\n\t\tfor (DISTANCE run=runStart+1; run<runEnd; run++) {\n\t\t\tRemoveRun(runStart+1);\n\t\t}\n\t\trunEnd = RunFromPosition(end);\n\t\tRemoveRunIfSameAsPrevious(runEnd);\n\t\tRemoveRunIfSameAsPrevious(runStart);\n\t\trunEnd = RunFromPosition(end);\n\t\tRemoveRunIfEmpty(runEnd);\n\t\treturn result;\n\t}\n\treturn resultNoChange;\n}\n\ntemplate <typename DISTANCE, typename STYLE>\nvoid RunStyles<DISTANCE, STYLE>::SetValueAt(DISTANCE position, STYLE value) {\n\tFillRange(position, value, 1);\n}\n\ntemplate <typename DISTANCE, typename STYLE>\nvoid RunStyles<DISTANCE, STYLE>::InsertSpace(DISTANCE position, DISTANCE insertLength) {\n\tDISTANCE runStart = RunFromPosition(position);\n\tif (starts.PositionFromPartition(runStart) == position) {\n\t\tSTYLE runStyle = ValueAt(position);\n\t\t// Inserting at start of run so make previous longer\n\t\tif (runStart == 0) {\n\t\t\t// Inserting at start of document so ensure 0\n\t\t\tif (runStyle) {\n\t\t\t\tstyles.SetValueAt(0, STYLE());\n\t\t\t\tstarts.InsertPartition(1, 0);\n\t\t\t\tstyles.InsertValue(1, 1, runStyle);\n\t\t\t\tstarts.InsertText(0, insertLength);\n\t\t\t} else {\n\t\t\t\tstarts.InsertText(runStart, insertLength);\n\t\t\t}\n\t\t} else {\n\t\t\tif (runStyle) {\n\t\t\t\tstarts.InsertText(runStart-1, insertLength);\n\t\t\t} else {\n\t\t\t\t// Insert at end of run so do not extend style\n\t\t\t\tstarts.InsertText(runStart, insertLength);\n\t\t\t}\n\t\t}\n\t} else {\n\t\tstarts.InsertText(runStart, insertLength);\n\t}\n}\n\ntemplate <typename DISTANCE, typename STYLE>\nvoid RunStyles<DISTANCE, STYLE>::DeleteAll() {\n\tstarts = Partitioning<DISTANCE>();\n\tstyles = SplitVector<STYLE>();\n\tstyles.InsertValue(0, 2, 0);\n}\n\ntemplate <typename DISTANCE, typename STYLE>\nvoid RunStyles<DISTANCE, STYLE>::DeleteRange(DISTANCE position, DISTANCE deleteLength) {\n\tDISTANCE end = position + deleteLength;\n\tDISTANCE runStart = RunFromPosition(position);\n\tDISTANCE runEnd = RunFromPosition(end);\n\tif (runStart == runEnd) {\n\t\t// Deleting from inside one run\n\t\tstarts.InsertText(runStart, -deleteLength);\n\t\tRemoveRunIfEmpty(runStart);\n\t} else {\n\t\trunStart = SplitRun(position);\n\t\trunEnd = SplitRun(end);\n\t\tstarts.InsertText(runStart, -deleteLength);\n\t\t// Remove each old run over the range\n\t\tfor (DISTANCE run=runStart; run<runEnd; run++) {\n\t\t\tRemoveRun(runStart);\n\t\t}\n\t\tRemoveRunIfEmpty(runStart);\n\t\tRemoveRunIfSameAsPrevious(runStart);\n\t}\n}\n\ntemplate <typename DISTANCE, typename STYLE>\nDISTANCE RunStyles<DISTANCE, STYLE>::Runs() const noexcept {\n\treturn starts.Partitions();\n}\n\ntemplate <typename DISTANCE, typename STYLE>\nbool RunStyles<DISTANCE, STYLE>::AllSame() const noexcept {\n\tfor (DISTANCE run = 1; run < starts.Partitions(); run++) {\n\t\tconst DISTANCE runBefore = run - 1;\n\t\tif (styles.ValueAt(run) != styles.ValueAt(runBefore))\n\t\t\treturn false;\n\t}\n\treturn true;\n}\n\ntemplate <typename DISTANCE, typename STYLE>\nbool RunStyles<DISTANCE, STYLE>::AllSameAs(STYLE value) const noexcept {\n\treturn AllSame() && (styles.ValueAt(0) == value);\n}\n\ntemplate <typename DISTANCE, typename STYLE>\nDISTANCE RunStyles<DISTANCE, STYLE>::Find(STYLE value, DISTANCE start) const noexcept {\n\tif (start < Length()) {\n\t\tDISTANCE run = start ? RunFromPosition(start) : 0;\n\t\tif (styles.ValueAt(run) == value)\n\t\t\treturn start;\n\t\trun++;\n\t\twhile (run < starts.Partitions()) {\n\t\t\tif (styles.ValueAt(run) == value)\n\t\t\t\treturn starts.PositionFromPartition(run);\n\t\t\trun++;\n\t\t}\n\t}\n\treturn -1;\n}\n\ntemplate <typename DISTANCE, typename STYLE>\nvoid RunStyles<DISTANCE, STYLE>::Check() const {\n\tif (Length() < 0) {\n\t\tthrow std::runtime_error(\"RunStyles: Length can not be negative.\");\n\t}\n\tif (starts.Partitions() < 1) {\n\t\tthrow std::runtime_error(\"RunStyles: Must always have 1 or more partitions.\");\n\t}\n\tif (starts.Partitions() != styles.Length()-1) {\n\t\tthrow std::runtime_error(\"RunStyles: Partitions and styles different lengths.\");\n\t}\n\tDISTANCE start=0;\n\twhile (start < Length()) {\n\t\tconst DISTANCE end = EndRun(start);\n\t\tif (start >= end) {\n\t\t\tthrow std::runtime_error(\"RunStyles: Partition is 0 length.\");\n\t\t}\n\t\tstart = end;\n\t}\n\tif (styles.ValueAt(styles.Length()-1) != 0) {\n\t\tthrow std::runtime_error(\"RunStyles: Unused style at end changed.\");\n\t}\n\tfor (ptrdiff_t j=1; j<styles.Length()-1; j++) {\n\t\tif (styles.ValueAt(j) == styles.ValueAt(j-1)) {\n\t\t\tthrow std::runtime_error(\"RunStyles: Style of a partition same as previous.\");\n\t\t}\n\t}\n}\n\ntemplate class Scintilla::Internal::RunStyles<int, int>;\ntemplate class Scintilla::Internal::RunStyles<int, char>;\n#if (PTRDIFF_MAX != INT_MAX) || defined(__HAIKU__)\ntemplate class Scintilla::Internal::RunStyles<ptrdiff_t, int>;\ntemplate class Scintilla::Internal::RunStyles<ptrdiff_t, char>;\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/src/RunStyles.h",
    "content": "/** @file RunStyles.h\n ** Data structure used to store sparse styles.\n **/\n// Copyright 1998-2007 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n/// Styling buffer using one element for each run rather than using\n/// a filled buffer.\n\n#ifndef RUNSTYLES_H\n#define RUNSTYLES_H\n\nnamespace Scintilla::Internal {\n\n// Return for RunStyles::FillRange reports if anything was changed and the\n// range that was changed. This may be trimmed from the requested range\n// when some of the requested range already had the requested value.\ntemplate <typename DISTANCE>\nstruct FillResult {\n\tbool changed;\n\tDISTANCE position;\n\tDISTANCE fillLength;\n};\n\ntemplate <typename DISTANCE, typename STYLE>\nclass RunStyles {\nprivate:\n\tPartitioning<DISTANCE> starts;\n\tSplitVector<STYLE> styles;\n\tDISTANCE RunFromPosition(DISTANCE position) const noexcept;\n\tDISTANCE SplitRun(DISTANCE position);\n\tvoid RemoveRun(DISTANCE run);\n\tvoid RemoveRunIfEmpty(DISTANCE run);\n\tvoid RemoveRunIfSameAsPrevious(DISTANCE run);\npublic:\n\tRunStyles();\n\tDISTANCE Length() const noexcept;\n\tSTYLE ValueAt(DISTANCE position) const noexcept;\n\tDISTANCE FindNextChange(DISTANCE position, DISTANCE end) const noexcept;\n\tDISTANCE StartRun(DISTANCE position) const noexcept;\n\tDISTANCE EndRun(DISTANCE position) const noexcept;\n\t// Returns changed=true if some values may have changed\n\tFillResult<DISTANCE> FillRange(DISTANCE position, STYLE value, DISTANCE fillLength);\n\tvoid SetValueAt(DISTANCE position, STYLE value);\n\tvoid InsertSpace(DISTANCE position, DISTANCE insertLength);\n\tvoid DeleteAll();\n\tvoid DeleteRange(DISTANCE position, DISTANCE deleteLength);\n\tDISTANCE Runs() const noexcept;\n\tbool AllSame() const noexcept;\n\tbool AllSameAs(STYLE value) const noexcept;\n\tDISTANCE Find(STYLE value, DISTANCE start) const noexcept;\n\n\tvoid Check() const;\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/src/ScintillaBase.cxx",
    "content": "// Scintilla source code edit control\n/** @file ScintillaBase.cxx\n ** An enhanced subclass of Editor with calltips, autocomplete and context menu.\n **/\n// Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#include <cstddef>\n#include <cstdlib>\n#include <cstdint>\n#include <cassert>\n#include <cstring>\n#include <cmath>\n\n#include <stdexcept>\n#include <string>\n#include <string_view>\n#include <vector>\n#include <map>\n#include <set>\n#include <optional>\n#include <algorithm>\n#include <memory>\n\n#include \"ScintillaTypes.h\"\n#include \"ScintillaMessages.h\"\n#include \"ScintillaStructures.h\"\n#include \"ILoader.h\"\n#include \"ILexer.h\"\n\n#include \"Debugging.h\"\n#include \"Geometry.h\"\n#include \"Platform.h\"\n\n#include \"CharacterCategoryMap.h\"\n\n#include \"Position.h\"\n#include \"UniqueString.h\"\n#include \"SplitVector.h\"\n#include \"Partitioning.h\"\n#include \"RunStyles.h\"\n#include \"ContractionState.h\"\n#include \"CellBuffer.h\"\n#include \"CallTip.h\"\n#include \"KeyMap.h\"\n#include \"Indicator.h\"\n#include \"LineMarker.h\"\n#include \"Style.h\"\n#include \"ViewStyle.h\"\n#include \"CharClassify.h\"\n#include \"Decoration.h\"\n#include \"CaseFolder.h\"\n#include \"Document.h\"\n#include \"Selection.h\"\n#include \"PositionCache.h\"\n#include \"EditModel.h\"\n#include \"MarginView.h\"\n#include \"EditView.h\"\n#include \"Editor.h\"\n#include \"AutoComplete.h\"\n#include \"ScintillaBase.h\"\n\nusing namespace Scintilla;\nusing namespace Scintilla::Internal;\n\nScintillaBase::ScintillaBase() {\n\tdisplayPopupMenu = PopUp::All;\n\tlistType = 0;\n\tmaxListWidth = 0;\n\tmultiAutoCMode = MultiAutoComplete::Once;\n}\n\nScintillaBase::~ScintillaBase() = default;\n\nvoid ScintillaBase::Finalise() {\n\tEditor::Finalise();\n\tpopup.Destroy();\n}\n\nvoid ScintillaBase::InsertCharacter(std::string_view sv, CharacterSource charSource) {\n\tconst bool acActive = ac.Active();\n\tconst bool isFillUp = acActive && ac.IsFillUpChar(sv[0]);\n\tif (!isFillUp) {\n\t\tEditor::InsertCharacter(sv, charSource);\n\t}\n\tif (acActive && ac.Active()) { // if it was and still is active\n\t\tAutoCompleteCharacterAdded(sv[0]);\n\t\t// For fill ups add the character after the autocompletion has\n\t\t// triggered so containers see the key so can display a calltip.\n\t\tif (isFillUp) {\n\t\t\tEditor::InsertCharacter(sv, charSource);\n\t\t}\n\t}\n}\n\nvoid ScintillaBase::Command(int cmdId) {\n\n\tswitch (cmdId) {\n\n\tcase idAutoComplete:  \t// Nothing to do\n\n\t\tbreak;\n\n\tcase idCallTip:  \t// Nothing to do\n\n\t\tbreak;\n\n\tcase idcmdUndo:\n\t\tWndProc(Message::Undo, 0, 0);\n\t\tbreak;\n\n\tcase idcmdRedo:\n\t\tWndProc(Message::Redo, 0, 0);\n\t\tbreak;\n\n\tcase idcmdCut:\n\t\tWndProc(Message::Cut, 0, 0);\n\t\tbreak;\n\n\tcase idcmdCopy:\n\t\tWndProc(Message::Copy, 0, 0);\n\t\tbreak;\n\n\tcase idcmdPaste:\n\t\tWndProc(Message::Paste, 0, 0);\n\t\tbreak;\n\n\tcase idcmdDelete:\n\t\tWndProc(Message::Clear, 0, 0);\n\t\tbreak;\n\n\tcase idcmdSelectAll:\n\t\tWndProc(Message::SelectAll, 0, 0);\n\t\tbreak;\n\n\tdefault:\n\t\tbreak;\n\t}\n}\n\nint ScintillaBase::KeyCommand(Message iMessage) {\n\t// Most key commands cancel autocompletion mode\n\tif (ac.Active()) {\n\t\tswitch (iMessage) {\n\t\t\t// Except for these\n\t\tcase Message::LineDown:\n\t\t\tAutoCompleteMove(1);\n\t\t\treturn 0;\n\t\tcase Message::LineUp:\n\t\t\tAutoCompleteMove(-1);\n\t\t\treturn 0;\n\t\tcase Message::PageDown:\n\t\t\tAutoCompleteMove(ac.lb->GetVisibleRows());\n\t\t\treturn 0;\n\t\tcase Message::PageUp:\n\t\t\tAutoCompleteMove(-ac.lb->GetVisibleRows());\n\t\t\treturn 0;\n\t\tcase Message::VCHome:\n\t\t\tAutoCompleteMove(-5000);\n\t\t\treturn 0;\n\t\tcase Message::LineEnd:\n\t\t\tAutoCompleteMove(5000);\n\t\t\treturn 0;\n\t\tcase Message::DeleteBack:\n\t\t\tDelCharBack(true);\n\t\t\tAutoCompleteCharacterDeleted();\n\t\t\tEnsureCaretVisible();\n\t\t\treturn 0;\n\t\tcase Message::DeleteBackNotLine:\n\t\t\tDelCharBack(false);\n\t\t\tAutoCompleteCharacterDeleted();\n\t\t\tEnsureCaretVisible();\n\t\t\treturn 0;\n\t\tcase Message::Tab:\n\t\t\tAutoCompleteCompleted(0, CompletionMethods::Tab);\n\t\t\treturn 0;\n\t\tcase Message::NewLine:\n\t\t\tAutoCompleteCompleted(0, CompletionMethods::Newline);\n\t\t\treturn 0;\n\n\t\tdefault:\n\t\t\tAutoCompleteCancel();\n\t\t}\n\t}\n\n\tif (ct.inCallTipMode) {\n\t\tif (\n\t\t    (iMessage != Message::CharLeft) &&\n\t\t    (iMessage != Message::CharLeftExtend) &&\n\t\t    (iMessage != Message::CharRight) &&\n\t\t    (iMessage != Message::CharRightExtend) &&\n\t\t    (iMessage != Message::EditToggleOvertype) &&\n\t\t    (iMessage != Message::DeleteBack) &&\n\t\t    (iMessage != Message::DeleteBackNotLine)\n\t\t) {\n\t\t\tct.CallTipCancel();\n\t\t}\n\t\tif ((iMessage == Message::DeleteBack) || (iMessage == Message::DeleteBackNotLine)) {\n\t\t\tif (sel.MainCaret() <= ct.posStartCallTip) {\n\t\t\t\tct.CallTipCancel();\n\t\t\t}\n\t\t}\n\t}\n\treturn Editor::KeyCommand(iMessage);\n}\n\nvoid ScintillaBase::ListNotify(ListBoxEvent *plbe) {\n\tswitch (plbe->event) {\n\tcase ListBoxEvent::EventType::selectionChange:\n\t\tAutoCompleteSelection();\n\t\tbreak;\n\tcase ListBoxEvent::EventType::doubleClick:\n\t\tAutoCompleteCompleted(0, CompletionMethods::DoubleClick);\n\t\tbreak;\n\t}\n}\n\nvoid ScintillaBase::MoveImeCarets(Sci::Position offset) noexcept {\n\t// Move carets relatively by bytes.\n\tfor (size_t r = 0; r < sel.Count(); r++) {\n\t\tconst Sci::Position positionInsert = sel.Range(r).Start().Position();\n\t\tsel.Range(r) = SelectionRange(positionInsert + offset);\n\t}\n}\n\nvoid ScintillaBase::DrawImeIndicator(int indicator, Sci::Position len) {\n\t// Emulate the visual style of IME characters with indicators.\n\t// Draw an indicator on the character before caret by the character bytes of len\n\t// so it should be called after InsertCharacter().\n\t// It does not affect caret positions.\n\tconst IndicatorNumbers ind = static_cast<IndicatorNumbers>(indicator);\n\tif (ind < IndicatorNumbers::Container || ind > IndicatorNumbers::Max) {\n\t\treturn;\n\t}\n\tpdoc->DecorationSetCurrentIndicator(indicator);\n\tfor (size_t r = 0; r < sel.Count(); r++) {\n\t\tconst Sci::Position positionInsert = sel.Range(r).Start().Position();\n\t\tpdoc->DecorationFillRange(positionInsert - len, 1, len);\n\t}\n}\n\nvoid ScintillaBase::AutoCompleteInsert(Sci::Position startPos, Sci::Position removeLen, std::string_view text) {\n\tUndoGroup ug(pdoc);\n\tif (multiAutoCMode == MultiAutoComplete::Once) {\n\t\tpdoc->DeleteChars(startPos, removeLen);\n\t\tconst Sci::Position lengthInserted = pdoc->InsertString(startPos, text);\n\t\tSetEmptySelection(startPos + lengthInserted);\n\t} else {\n\t\t// MultiAutoComplete::Each\n\t\tfor (size_t r=0; r<sel.Count(); r++) {\n\t\t\tif (!RangeContainsProtected(sel.Range(r))) {\n\t\t\t\tSci::Position positionInsert = sel.Range(r).Start().Position();\n\t\t\t\tpositionInsert = RealizeVirtualSpace(positionInsert, sel.Range(r).caret.VirtualSpace());\n\t\t\t\tif (positionInsert - removeLen >= 0) {\n\t\t\t\t\tpositionInsert -= removeLen;\n\t\t\t\t\tpdoc->DeleteChars(positionInsert, removeLen);\n\t\t\t\t}\n\t\t\t\tconst Sci::Position lengthInserted = pdoc->InsertString(positionInsert, text);\n\t\t\t\tif (lengthInserted > 0) {\n\t\t\t\t\tsel.Range(r) = SelectionRange(positionInsert + lengthInserted);\n\t\t\t\t}\n\t\t\t\tsel.Range(r).ClearVirtualSpace();\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid ScintillaBase::AutoCompleteStart(Sci::Position lenEntered, const char *list) {\n\t//Platform::DebugPrintf(\"AutoComplete %s\\n\", list);\n\tct.CallTipCancel();\n\n\tif (ac.chooseSingle && (listType == 0)) {\n\t\tif (list && !strchr(list, ac.GetSeparator())) {\n\t\t\t// list contains just one item so choose it\n\t\t\tconst std::string_view item(list);\n\t\t\tconst std::string_view choice = item.substr(0, item.find_first_of(ac.GetTypesep()));\n\t\t\tif (ac.ignoreCase) {\n\t\t\t\t// May need to convert the case before invocation, so remove lenEntered characters\n\t\t\t\tAutoCompleteInsert(sel.MainCaret() - lenEntered, lenEntered, choice);\n\t\t\t} else {\n\t\t\t\tAutoCompleteInsert(sel.MainCaret(), 0, choice.substr(lenEntered));\n\t\t\t}\n\t\t\tconst Sci::Position firstPos = sel.MainCaret() - lenEntered;\n\t\t\t// Construct a string with a NUL at end as that is expected by applications\n\t\t\tconst std::string selected(choice);\n\t\t\tAutoCompleteNotifyCompleted('\\0', CompletionMethods::SingleChoice, firstPos, selected.c_str());\n\n\t\t\tac.Cancel();\n\t\t\treturn;\n\t\t}\n\t}\n\n\tconst ListOptions options{\n\t\tvs.ElementColour(Element::List),\n\t\tvs.ElementColour(Element::ListBack),\n\t\tvs.ElementColour(Element::ListSelected),\n\t\tvs.ElementColour(Element::ListSelectedBack),\n\t\tac.options,\n\t\tac.imageScale,\n\t};\n\n\tint lineHeight;\n\tif (vs.autocStyle != StyleDefault) {\n\t\tAutoSurface surfaceMeasure(this);\n\t\tlineHeight = static_cast<int>(std::lround(surfaceMeasure->Height(vs.styles[vs.autocStyle].font.get())));\n\t} else {\n\t\tlineHeight = vs.lineHeight;\n\t}\n\n\tac.Start(wMain, idAutoComplete, sel.MainCaret(), PointMainCaret(),\n\t\t\t\tlenEntered, lineHeight, IsUnicodeMode(), technology, options);\n\n\tconst PRectangle rcClient = GetClientRectangle();\n\tPoint pt = LocationFromPosition(sel.MainCaret() - lenEntered);\n\tPRectangle rcPopupBounds = wMain.GetMonitorRect(pt);\n\tif (rcPopupBounds.Height() == 0)\n\t\trcPopupBounds = rcClient;\n\n\tint heightLB = ac.heightLBDefault;\n\tint widthLB = ac.widthLBDefault;\n\tif (pt.x >= rcClient.right - widthLB) {\n\t\tHorizontalScrollTo(static_cast<int>(xOffset + pt.x - rcClient.right + widthLB));\n\t\tRedraw();\n\t\tpt = PointMainCaret();\n\t}\n\tif (wMargin.Created()) {\n\t\tpt = pt + GetVisibleOriginInMain();\n\t}\n\tPRectangle rcac;\n\trcac.left = pt.x - ac.lb->CaretFromEdge();\n\tif (pt.y >= rcPopupBounds.bottom - heightLB &&  // Won't fit below.\n\t        pt.y >= (rcPopupBounds.bottom + rcPopupBounds.top) / 2) { // and there is more room above.\n\t\trcac.top = pt.y - heightLB;\n\t\tif (rcac.top < rcPopupBounds.top) {\n\t\t\theightLB -= static_cast<int>(rcPopupBounds.top - rcac.top);\n\t\t\trcac.top = rcPopupBounds.top;\n\t\t}\n\t} else {\n\t\trcac.top = pt.y + vs.lineHeight;\n\t}\n\trcac.right = rcac.left + widthLB;\n\trcac.bottom = static_cast<XYPOSITION>(std::min(static_cast<int>(rcac.top) + heightLB, static_cast<int>(rcPopupBounds.bottom)));\n\tac.lb->SetPositionRelative(rcac, &wMain);\n\tac.lb->SetFont(vs.styles[vs.autocStyle].font.get());\n\tconst int aveCharWidth = static_cast<int>(vs.styles[vs.autocStyle].aveCharWidth);\n\tac.lb->SetAverageCharWidth(aveCharWidth);\n\tac.lb->SetDelegate(this);\n\n\tac.SetList(list ? list : \"\");\n\n\t// Fiddle the position of the list so it is right next to the target and wide enough for all its strings\n\tPRectangle rcList = ac.lb->GetDesiredRect();\n\tconst int heightAlloced = static_cast<int>(rcList.bottom - rcList.top);\n\twidthLB = std::max(widthLB, static_cast<int>(rcList.right - rcList.left));\n\tif (maxListWidth != 0)\n\t\twidthLB = std::min(widthLB, aveCharWidth*maxListWidth);\n\t// Make an allowance for large strings in list\n\trcList.left = pt.x - ac.lb->CaretFromEdge();\n\trcList.right = rcList.left + widthLB;\n\tif (((pt.y + vs.lineHeight) >= (rcPopupBounds.bottom - heightAlloced)) &&  // Won't fit below.\n\t        ((pt.y + vs.lineHeight / 2) >= (rcPopupBounds.bottom + rcPopupBounds.top) / 2)) { // and there is more room above.\n\t\trcList.top = pt.y - heightAlloced;\n\t} else {\n\t\trcList.top = pt.y + vs.lineHeight;\n\t}\n\trcList.bottom = rcList.top + heightAlloced;\n\tac.lb->SetPositionRelative(rcList, &wMain);\n\tac.Show(true);\n\tif (lenEntered != 0) {\n\t\tAutoCompleteMoveToCurrentWord();\n\t}\n}\n\nvoid ScintillaBase::AutoCompleteCancel() {\n\tif (ac.Active()) {\n\t\tNotificationData scn = {};\n\t\tscn.nmhdr.code = Notification::AutoCCancelled;\n\t\tscn.wParam = 0;\n\t\tscn.listType = 0;\n\t\tNotifyParent(scn);\n\t}\n\tac.Cancel();\n}\n\nvoid ScintillaBase::AutoCompleteMove(int delta) {\n\tac.Move(delta);\n}\n\nvoid ScintillaBase::AutoCompleteMoveToCurrentWord() {\n\tif (FlagSet(ac.options, AutoCompleteOption::SelectFirstItem))\n\t\treturn;\n\tstd::string wordCurrent = RangeText(ac.posStart - ac.startLen, sel.MainCaret());\n\tac.Select(wordCurrent.c_str());\n}\n\nvoid ScintillaBase::AutoCompleteSelection() {\n\tconst int item = ac.GetSelection();\n\tstd::string selected;\n\tif (item != -1) {\n\t\tselected = ac.GetValue(item);\n\t}\n\n\tNotificationData scn = {};\n\tscn.nmhdr.code = Notification::AutoCSelectionChange;\n\tscn.message = static_cast<Message>(0);\n\tscn.wParam = listType;\n\tscn.listType = listType;\n\tconst Sci::Position firstPos = ac.posStart - ac.startLen;\n\tscn.position = firstPos;\n\tscn.lParam = firstPos;\n\tscn.text = selected.c_str();\n\tNotifyParent(scn);\n}\n\nvoid ScintillaBase::AutoCompleteCharacterAdded(char ch) {\n\tif (ac.IsFillUpChar(ch)) {\n\t\tAutoCompleteCompleted(ch, CompletionMethods::FillUp);\n\t} else if (ac.IsStopChar(ch)) {\n\t\tAutoCompleteCancel();\n\t} else {\n\t\tAutoCompleteMoveToCurrentWord();\n\t}\n}\n\nvoid ScintillaBase::AutoCompleteCharacterDeleted() {\n\tif (sel.MainCaret() < ac.posStart - ac.startLen) {\n\t\tAutoCompleteCancel();\n\t} else if (ac.cancelAtStartPos && (sel.MainCaret() <= ac.posStart)) {\n\t\tAutoCompleteCancel();\n\t} else {\n\t\tAutoCompleteMoveToCurrentWord();\n\t}\n\tNotificationData scn = {};\n\tscn.nmhdr.code = Notification::AutoCCharDeleted;\n\tscn.wParam = 0;\n\tscn.listType = 0;\n\tNotifyParent(scn);\n}\n\nvoid ScintillaBase::AutoCompleteNotifyCompleted(char ch, CompletionMethods completionMethod, Sci::Position firstPos, const char *text) {\n\tNotificationData scn = {};\n\tscn.nmhdr.code = Notification::AutoCCompleted;\n\tscn.message = static_cast<Message>(0);\n\tscn.ch = ch;\n\tscn.listCompletionMethod = completionMethod;\n\tscn.wParam = listType;\n\tscn.listType = listType;\n\tscn.position = firstPos;\n\tscn.lParam = firstPos;\n\tscn.text = text;\n\tNotifyParent(scn);\n}\n\nvoid ScintillaBase::AutoCompleteCompleted(char ch, CompletionMethods completionMethod) {\n\tconst int item = ac.GetSelection();\n\tif (item == -1) {\n\t\tAutoCompleteCancel();\n\t\treturn;\n\t}\n\tconst std::string selected = ac.GetValue(item);\n\n\tac.Show(false);\n\n\tNotificationData scn = {};\n\tscn.nmhdr.code = listType > 0 ? Notification::UserListSelection : Notification::AutoCSelection;\n\tscn.message = static_cast<Message>(0);\n\tscn.ch = ch;\n\tscn.listCompletionMethod = completionMethod;\n\tscn.wParam = listType;\n\tscn.listType = listType;\n\tconst Sci::Position firstPos = ac.posStart - ac.startLen;\n\tscn.position = firstPos;\n\tscn.lParam = firstPos;\n\tscn.text = selected.c_str();\n\tNotifyParent(scn);\n\n\tif (!ac.Active())\n\t\treturn;\n\tac.Cancel();\n\n\tif (listType > 0)\n\t\treturn;\n\n\tSci::Position endPos = sel.MainCaret();\n\tif (ac.dropRestOfWord)\n\t\tendPos = pdoc->ExtendWordSelect(endPos, 1, true);\n\tif (endPos < firstPos)\n\t\treturn;\n\tAutoCompleteInsert(firstPos, endPos - firstPos, selected);\n\tSetLastXChosen();\n\n\tAutoCompleteNotifyCompleted(ch, completionMethod, firstPos, selected.c_str());\n}\n\nint ScintillaBase::AutoCompleteGetCurrent() const {\n\tif (!ac.Active())\n\t\treturn -1;\n\treturn ac.GetSelection();\n}\n\nint ScintillaBase::AutoCompleteGetCurrentText(char *buffer) const {\n\tif (ac.Active()) {\n\t\tconst int item = ac.GetSelection();\n\t\tif (item != -1) {\n\t\t\tconst std::string selected = ac.GetValue(item);\n\t\t\tif (buffer)\n\t\t\t\tmemcpy(buffer, selected.c_str(), selected.length()+1);\n\t\t\treturn static_cast<int>(selected.length());\n\t\t}\n\t}\n\tif (buffer)\n\t\t*buffer = '\\0';\n\treturn 0;\n}\n\nvoid ScintillaBase::CallTipShow(Point pt, const char *defn) {\n\tac.Cancel();\n\t// If container knows about StyleCallTip then use it in place of the\n\t// StyleDefault for the face name, size and character set. Also use it\n\t// for the foreground and background colour.\n\tconst int ctStyle = ct.UseStyleCallTip() ? StyleCallTip : StyleDefault;\n\tconst Style &style = vs.styles[ctStyle];\n\tif (ct.UseStyleCallTip()) {\n\t\tct.SetForeBack(style.fore, style.back);\n\t}\n\tif (wMargin.Created()) {\n\t\tpt = pt + GetVisibleOriginInMain();\n\t}\n\tAutoSurface surfaceMeasure(this);\n\tPRectangle rc = ct.CallTipStart(sel.MainCaret(), pt,\n\t\tvs.lineHeight,\n\t\tdefn,\n\t\tCodePage(),\n\t\tsurfaceMeasure,\n\t\tstyle.font);\n\t// If the call-tip window would be out of the client\n\t// space\n\tconst PRectangle rcClient = GetClientRectangle();\n\tconst int offset = vs.lineHeight + static_cast<int>(rc.Height());\n\t// adjust so it displays above the text.\n\tif (rc.bottom > rcClient.bottom && rc.Height() < rcClient.Height()) {\n\t\trc.top -= offset;\n\t\trc.bottom -= offset;\n\t}\n\t// adjust so it displays below the text.\n\tif (rc.top < rcClient.top && rc.Height() < rcClient.Height()) {\n\t\trc.top += offset;\n\t\trc.bottom += offset;\n\t}\n\t// Now display the window.\n\tCreateCallTipWindow(rc);\n\tct.wCallTip.SetPositionRelative(rc, &wMain);\n\tct.wCallTip.Show();\n\tct.wCallTip.InvalidateAll();\n}\n\nvoid ScintillaBase::CallTipClick() {\n\tNotificationData scn = {};\n\tscn.nmhdr.code = Notification::CallTipClick;\n\tscn.position = ct.clickPlace;\n\tNotifyParent(scn);\n}\n\nbool ScintillaBase::ShouldDisplayPopup(Point ptInWindowCoordinates) const {\n\treturn (displayPopupMenu == PopUp::All ||\n\t\t(displayPopupMenu == PopUp::Text && !PointInSelMargin(ptInWindowCoordinates)));\n}\n\nvoid ScintillaBase::ContextMenu(Point pt) {\n\tif (displayPopupMenu != PopUp::Never) {\n\t\tconst bool writable = !WndProc(Message::GetReadOnly, 0, 0);\n\t\tpopup.CreatePopUp();\n\t\tAddToPopUp(\"Undo\", idcmdUndo, writable && pdoc->CanUndo());\n\t\tAddToPopUp(\"Redo\", idcmdRedo, writable && pdoc->CanRedo());\n\t\tAddToPopUp(\"\");\n\t\tAddToPopUp(\"Cut\", idcmdCut, writable && !sel.Empty());\n\t\tAddToPopUp(\"Copy\", idcmdCopy, !sel.Empty());\n\t\tAddToPopUp(\"Paste\", idcmdPaste, writable && WndProc(Message::CanPaste, 0, 0));\n\t\tAddToPopUp(\"Delete\", idcmdDelete, writable && !sel.Empty());\n\t\tAddToPopUp(\"\");\n\t\tAddToPopUp(\"Select All\", idcmdSelectAll);\n\t\tpopup.Show(pt, wMain);\n\t}\n}\n\nvoid ScintillaBase::CancelModes() {\n\tAutoCompleteCancel();\n\tct.CallTipCancel();\n\tEditor::CancelModes();\n}\n\nvoid ScintillaBase::ButtonDownWithModifiers(Point pt, unsigned int curTime, KeyMod modifiers) {\n\tCancelModes();\n\tEditor::ButtonDownWithModifiers(pt, curTime, modifiers);\n}\n\nvoid ScintillaBase::RightButtonDownWithModifiers(Point pt, unsigned int curTime, KeyMod modifiers) {\n\tCancelModes();\n\tEditor::RightButtonDownWithModifiers(pt, curTime, modifiers);\n}\n\nnamespace Scintilla::Internal {\n\nclass LexState : public LexInterface {\npublic:\n\texplicit LexState(Document *pdoc_) noexcept;\n\n\t// LexInterface deleted the standard operators and defined the virtual destructor so don't need to here.\n\n\tconst char *DescribeWordListSets();\n\tvoid SetWordList(int n, const char *wl);\n\t[[nodiscard]] int GetIdentifier() const;\n\t[[nodiscard]] const char *GetName() const;\n\tvoid *PrivateCall(int operation, void *pointer);\n\tconst char *PropertyNames();\n\tTypeProperty PropertyType(const char *name);\n\tconst char *DescribeProperty(const char *name);\n\tvoid PropSet(const char *key, const char *val);\n\tconst char *PropGet(const char *key) const;\n\tint PropGetInt(const char *key, int defaultValue=0) const;\n\n\tLineEndType LineEndTypesSupported() override;\n\tint AllocateSubStyles(int styleBase, int numberStyles);\n\tint SubStylesStart(int styleBase);\n\tint SubStylesLength(int styleBase);\n\tint StyleFromSubStyle(int subStyle);\n\tint PrimaryStyleFromStyle(int style);\n\tvoid FreeSubStyles();\n\tvoid SetIdentifiers(int style, const char *identifiers);\n\tint DistanceToSecondaryStyles();\n\tconst char *GetSubStyleBases();\n\tint NamedStyles();\n\tconst char *NameOfStyle(int style);\n\tconst char *TagsOfStyle(int style);\n\tconst char *DescriptionOfStyle(int style);\n};\n\n}\n\nLexState::LexState(Document *pdoc_) noexcept : LexInterface(pdoc_) {\n}\n\nLexState *ScintillaBase::DocumentLexState() {\n\tif (!pdoc->GetLexInterface()) {\n\t\tpdoc->SetLexInterface(std::make_unique<LexState>(pdoc));\n\t}\n\treturn dynamic_cast<LexState *>(pdoc->GetLexInterface());\n}\n\nconst char *LexState::DescribeWordListSets() {\n\tif (instance) {\n\t\treturn instance->DescribeWordListSets();\n\t}\n\treturn nullptr;\n}\n\nvoid LexState::SetWordList(int n, const char *wl) {\n\tif (instance) {\n\t\tconst Sci_Position firstModification = instance->WordListSet(n, wl);\n\t\tif (firstModification >= 0) {\n\t\t\tpdoc->ModifiedAt(firstModification);\n\t\t}\n\t}\n}\n\nint LexState::GetIdentifier() const {\n\tif (instance) {\n\t\treturn instance->GetIdentifier();\n\t}\n\treturn 0;\n}\n\nconst char *LexState::GetName() const {\n\tif (instance) {\n\t\treturn instance->GetName();\n\t}\n\treturn \"\";\n}\n\nvoid *LexState::PrivateCall(int operation, void *pointer) {\n\tif (instance) {\n\t\treturn instance->PrivateCall(operation, pointer);\n\t}\n\treturn nullptr;\n}\n\nconst char *LexState::PropertyNames() {\n\tif (instance) {\n\t\treturn instance->PropertyNames();\n\t}\n\treturn nullptr;\n}\n\nTypeProperty LexState::PropertyType(const char *name) {\n\tif (instance) {\n\t\treturn static_cast<TypeProperty>(instance->PropertyType(name));\n\t}\n\treturn TypeProperty::Boolean;\n}\n\nconst char *LexState::DescribeProperty(const char *name) {\n\tif (instance) {\n\t\treturn instance->DescribeProperty(name);\n\t}\n\treturn nullptr;\n}\n\nvoid LexState::PropSet(const char *key, const char *val) {\n\tif (instance) {\n\t\tconst Sci_Position firstModification = instance->PropertySet(key, val);\n\t\tif (firstModification >= 0) {\n\t\t\tpdoc->ModifiedAt(firstModification);\n\t\t}\n\t}\n}\n\nconst char *LexState::PropGet(const char *key) const {\n\tif (instance) {\n\t\treturn instance->PropertyGet(key);\n\t}\n\treturn nullptr;\n}\n\nint LexState::PropGetInt(const char *key, int defaultValue) const {\n\tif (instance) {\n\t\tconst char *value = instance->PropertyGet(key);\n\t\tif (value && *value) {\n\t\t\treturn atoi(value);\n\t\t}\n\t}\n\treturn defaultValue;\n}\n\nLineEndType LexState::LineEndTypesSupported() {\n\tif (instance) {\n\t\treturn static_cast<LineEndType>(instance->LineEndTypesSupported());\n\t}\n\treturn LineEndType::Default;\n}\n\nint LexState::AllocateSubStyles(int styleBase, int numberStyles) {\n\tif (instance) {\n\t\treturn instance->AllocateSubStyles(styleBase, numberStyles);\n\t}\n\treturn -1;\n}\n\nint LexState::SubStylesStart(int styleBase) {\n\tif (instance) {\n\t\treturn instance->SubStylesStart(styleBase);\n\t}\n\treturn -1;\n}\n\nint LexState::SubStylesLength(int styleBase) {\n\tif (instance) {\n\t\treturn instance->SubStylesLength(styleBase);\n\t}\n\treturn 0;\n}\n\nint LexState::StyleFromSubStyle(int subStyle) {\n\tif (instance) {\n\t\treturn instance->StyleFromSubStyle(subStyle);\n\t}\n\treturn 0;\n}\n\nint LexState::PrimaryStyleFromStyle(int style) {\n\tif (instance) {\n\t\treturn instance->PrimaryStyleFromStyle(style);\n\t}\n\treturn 0;\n}\n\nvoid LexState::FreeSubStyles() {\n\tif (instance) {\n\t\tinstance->FreeSubStyles();\n\t}\n}\n\nvoid LexState::SetIdentifiers(int style, const char *identifiers) {\n\tif (instance) {\n\t\tinstance->SetIdentifiers(style, identifiers);\n\t\tpdoc->ModifiedAt(0);\n\t}\n}\n\nint LexState::DistanceToSecondaryStyles() {\n\tif (instance) {\n\t\treturn instance->DistanceToSecondaryStyles();\n\t}\n\treturn 0;\n}\n\nconst char *LexState::GetSubStyleBases() {\n\tif (instance) {\n\t\treturn instance->GetSubStyleBases();\n\t}\n\treturn \"\";\n}\n\nint LexState::NamedStyles() {\n\tif (instance) {\n\t\treturn instance->NamedStyles();\n\t}\n\treturn -1;\n}\n\nconst char *LexState::NameOfStyle(int style) {\n\tif (instance) {\n\t\treturn instance->NameOfStyle(style);\n\t}\n\treturn nullptr;\n}\n\nconst char *LexState::TagsOfStyle(int style) {\n\tif (instance) {\n\t\treturn instance->TagsOfStyle(style);\n\t}\n\treturn nullptr;\n}\n\nconst char *LexState::DescriptionOfStyle(int style) {\n\tif (instance) {\n\t\treturn instance->DescriptionOfStyle(style);\n\t}\n\treturn nullptr;\n}\n\nvoid ScintillaBase::NotifyStyleToNeeded(Sci::Position endStyleNeeded) {\n\tif (!DocumentLexState()->UseContainerLexing()) {\n\t\tconst Sci::Position startStyling = pdoc->LineStartPosition(pdoc->GetEndStyled());\n\t\tDocumentLexState()->Colourise(startStyling, endStyleNeeded);\n\t\treturn;\n\t}\n\tEditor::NotifyStyleToNeeded(endStyleNeeded);\n}\n\nsptr_t ScintillaBase::WndProc(Message iMessage, uptr_t wParam, sptr_t lParam) {\n\tswitch (iMessage) {\n\tcase Message::AutoCShow:\n\t\tlistType = 0;\n\t\tAutoCompleteStart(PositionFromUPtr(wParam), ConstCharPtrFromSPtr(lParam));\n\t\tbreak;\n\n\tcase Message::AutoCCancel:\n\t\tac.Cancel();\n\t\tbreak;\n\n\tcase Message::AutoCActive:\n\t\treturn ac.Active();\n\n\tcase Message::AutoCPosStart:\n\t\treturn ac.posStart;\n\n\tcase Message::AutoCComplete:\n\t\tAutoCompleteCompleted(0, CompletionMethods::Command);\n\t\tbreak;\n\n\tcase Message::AutoCSetSeparator:\n\t\tac.SetSeparator(static_cast<char>(wParam));\n\t\tbreak;\n\n\tcase Message::AutoCGetSeparator:\n\t\treturn ac.GetSeparator();\n\n\tcase Message::AutoCStops:\n\t\tac.SetStopChars(ConstCharPtrFromSPtr(lParam));\n\t\tbreak;\n\n\tcase Message::AutoCSelect:\n\t\tac.Select(ConstCharPtrFromSPtr(lParam));\n\t\tbreak;\n\n\tcase Message::AutoCGetCurrent:\n\t\treturn AutoCompleteGetCurrent();\n\n\tcase Message::AutoCGetCurrentText:\n\t\treturn AutoCompleteGetCurrentText(CharPtrFromSPtr(lParam));\n\n\tcase Message::AutoCSetCancelAtStart:\n\t\tac.cancelAtStartPos = wParam != 0;\n\t\tbreak;\n\n\tcase Message::AutoCGetCancelAtStart:\n\t\treturn ac.cancelAtStartPos;\n\n\tcase Message::AutoCSetFillUps:\n\t\tac.SetFillUpChars(ConstCharPtrFromSPtr(lParam));\n\t\tbreak;\n\n\tcase Message::AutoCSetChooseSingle:\n\t\tac.chooseSingle = wParam != 0;\n\t\tbreak;\n\n\tcase Message::AutoCGetChooseSingle:\n\t\treturn ac.chooseSingle;\n\n\tcase Message::AutoCSetIgnoreCase:\n\t\tac.ignoreCase = wParam != 0;\n\t\tbreak;\n\n\tcase Message::AutoCGetIgnoreCase:\n\t\treturn ac.ignoreCase;\n\n\tcase Message::AutoCSetCaseInsensitiveBehaviour:\n\t\tac.ignoreCaseBehaviour = static_cast<CaseInsensitiveBehaviour>(wParam);\n\t\tbreak;\n\n\tcase Message::AutoCGetCaseInsensitiveBehaviour:\n\t\treturn static_cast<sptr_t>(ac.ignoreCaseBehaviour);\n\n\tcase Message::AutoCSetMulti:\n\t\tmultiAutoCMode = static_cast<MultiAutoComplete>(wParam);\n\t\tbreak;\n\n\tcase Message::AutoCGetMulti:\n\t\treturn static_cast<sptr_t>(multiAutoCMode);\n\n\tcase Message::AutoCSetOrder:\n\t\tac.autoSort = static_cast<Ordering>(wParam);\n\t\tbreak;\n\n\tcase Message::AutoCGetOrder:\n\t\treturn static_cast<sptr_t>(ac.autoSort);\n\n\tcase Message::UserListShow:\n\t\tlistType = static_cast<int>(wParam);\n\t\tAutoCompleteStart(0, ConstCharPtrFromSPtr(lParam));\n\t\tbreak;\n\n\tcase Message::AutoCSetAutoHide:\n\t\tac.autoHide = wParam != 0;\n\t\tbreak;\n\n\tcase Message::AutoCGetAutoHide:\n\t\treturn ac.autoHide;\n\n\tcase Message::AutoCSetOptions:\n\t\tac.options = static_cast<AutoCompleteOption>(wParam);\n\t\tbreak;\n\n\tcase Message::AutoCGetOptions:\n\t\treturn static_cast<sptr_t>(ac.options);\n\n\tcase Message::AutoCSetDropRestOfWord:\n\t\tac.dropRestOfWord = wParam != 0;\n\t\tbreak;\n\n\tcase Message::AutoCGetDropRestOfWord:\n\t\treturn ac.dropRestOfWord;\n\n\tcase Message::AutoCSetMaxHeight:\n\t\tac.lb->SetVisibleRows(static_cast<int>(wParam));\n\t\tbreak;\n\n\tcase Message::AutoCGetMaxHeight:\n\t\treturn ac.lb->GetVisibleRows();\n\n\tcase Message::AutoCSetMaxWidth:\n\t\tmaxListWidth = static_cast<int>(wParam);\n\t\tbreak;\n\n\tcase Message::AutoCGetMaxWidth:\n\t\treturn maxListWidth;\n\n\tcase Message::AutoCSetStyle:\n\t\tvs.autocStyle = static_cast<int>(wParam);\n\t\tInvalidateStyleRedraw();\n\t\tbreak;\n\n\tcase Message::AutoCGetStyle:\n\t\treturn vs.autocStyle;\n\n\tcase Message::AutoCSetImageScale:\n\t\tac.imageScale = static_cast<float>(wParam) / 100.0f;\n\t\tbreak;\n\n\tcase Message::AutoCGetImageScale:\n\t\treturn static_cast<int>(ac.imageScale * 100);\n\n\tcase Message::RegisterImage:\n\t\tac.lb->RegisterImage(static_cast<int>(wParam), ConstCharPtrFromSPtr(lParam));\n\t\tbreak;\n\n\tcase Message::RegisterRGBAImage:\n\t\tac.lb->RegisterRGBAImage(static_cast<int>(wParam), static_cast<int>(sizeRGBAImage.x), static_cast<int>(sizeRGBAImage.y),\n\t\t\tConstUCharPtrFromSPtr(lParam));\n\t\tbreak;\n\n\tcase Message::ClearRegisteredImages:\n\t\tac.lb->ClearRegisteredImages();\n\t\tbreak;\n\n\tcase Message::AutoCSetTypeSeparator:\n\t\tac.SetTypesep(static_cast<char>(wParam));\n\t\tbreak;\n\n\tcase Message::AutoCGetTypeSeparator:\n\t\treturn ac.GetTypesep();\n\n\tcase Message::CallTipShow:\n\t\tCallTipShow(LocationFromPosition(wParam),\n\t\t\tConstCharPtrFromSPtr(lParam));\n\t\tbreak;\n\n\tcase Message::CallTipCancel:\n\t\tct.CallTipCancel();\n\t\tbreak;\n\n\tcase Message::CallTipActive:\n\t\treturn ct.inCallTipMode;\n\n\tcase Message::CallTipPosStart:\n\t\treturn ct.posStartCallTip;\n\n\tcase Message::CallTipSetPosStart:\n\t\tct.posStartCallTip = wParam;\n\t\tbreak;\n\n\tcase Message::CallTipSetHlt:\n\t\tct.SetHighlight(wParam, lParam);\n\t\tbreak;\n\n\tcase Message::CallTipSetBack:\n\t\tct.colourBG = ColourRGBA::FromIpRGB(SPtrFromUPtr(wParam));\n\t\tvs.styles[StyleCallTip].back = ct.colourBG;\n\t\tInvalidateStyleRedraw();\n\t\tbreak;\n\n\tcase Message::CallTipSetFore:\n\t\tct.colourUnSel = ColourRGBA::FromIpRGB(SPtrFromUPtr(wParam));\n\t\tvs.styles[StyleCallTip].fore = ct.colourUnSel;\n\t\tInvalidateStyleRedraw();\n\t\tbreak;\n\n\tcase Message::CallTipSetForeHlt:\n\t\tct.colourSel = ColourRGBA::FromIpRGB(SPtrFromUPtr(wParam));\n\t\tInvalidateStyleRedraw();\n\t\tbreak;\n\n\tcase Message::CallTipUseStyle:\n\t\tct.SetTabSize(static_cast<int>(wParam));\n\t\tInvalidateStyleRedraw();\n\t\tbreak;\n\n\tcase Message::CallTipSetPosition:\n\t\tct.SetPosition(wParam != 0);\n\t\tInvalidateStyleRedraw();\n\t\tbreak;\n\n\tcase Message::UsePopUp:\n\t\tdisplayPopupMenu = static_cast<PopUp>(wParam);\n\t\tbreak;\n\n\tcase Message::GetLexer:\n\t\treturn DocumentLexState()->GetIdentifier();\n\n\tcase Message::SetILexer:\n\t\tDocumentLexState()->SetInstance(static_cast<ILexer5 *>(PtrFromSPtr(lParam)));\n\t\treturn 0;\n\n\tcase Message::Colourise:\n\t\tif (DocumentLexState()->UseContainerLexing()) {\n\t\t\tpdoc->ModifiedAt(PositionFromUPtr(wParam));\n\t\t\tNotifyStyleToNeeded((lParam == -1) ? pdoc->Length() : lParam);\n\t\t} else {\n\t\t\tDocumentLexState()->Colourise(PositionFromUPtr(wParam), lParam);\n\t\t}\n\t\tRedraw();\n\t\tbreak;\n\n\tcase Message::SetProperty:\n\t\tDocumentLexState()->PropSet(ConstCharPtrFromUPtr(wParam),\n\t\t          ConstCharPtrFromSPtr(lParam));\n\t\tbreak;\n\n\tcase Message::GetProperty:\n\t\treturn StringResult(lParam, DocumentLexState()->PropGet(ConstCharPtrFromUPtr(wParam)));\n\n\tcase Message::GetPropertyExpanded:\n\t\treturn StringResult(lParam, DocumentLexState()->PropGet(ConstCharPtrFromUPtr(wParam)));\n\n\tcase Message::GetPropertyInt:\n\t\treturn DocumentLexState()->PropGetInt(ConstCharPtrFromUPtr(wParam), static_cast<int>(lParam));\n\n\tcase Message::SetKeyWords:\n\t\tDocumentLexState()->SetWordList(static_cast<int>(wParam), ConstCharPtrFromSPtr(lParam));\n\t\tbreak;\n\n\tcase Message::GetLexerLanguage:\n\t\treturn StringResult(lParam, DocumentLexState()->GetName());\n\n\tcase Message::PrivateLexerCall:\n\t\treturn reinterpret_cast<sptr_t>(\n\t\t\tDocumentLexState()->PrivateCall(static_cast<int>(wParam), PtrFromSPtr(lParam)));\n\n#ifdef INCLUDE_DEPRECATED_FEATURES\n\tcase SCI_GETSTYLEBITSNEEDED:\n\t\treturn 8;\n#endif\n\n\tcase Message::PropertyNames:\n\t\treturn StringResult(lParam, DocumentLexState()->PropertyNames());\n\n\tcase Message::PropertyType:\n\t\treturn static_cast<sptr_t>(DocumentLexState()->PropertyType(ConstCharPtrFromUPtr(wParam)));\n\n\tcase Message::DescribeProperty:\n\t\treturn StringResult(lParam,\n\t\t\t\t    DocumentLexState()->DescribeProperty(ConstCharPtrFromUPtr(wParam)));\n\n\tcase Message::DescribeKeyWordSets:\n\t\treturn StringResult(lParam, DocumentLexState()->DescribeWordListSets());\n\n\tcase Message::GetLineEndTypesSupported:\n\t\treturn static_cast<sptr_t>(DocumentLexState()->LineEndTypesSupported());\n\n\tcase Message::AllocateSubStyles:\n\t\treturn DocumentLexState()->AllocateSubStyles(static_cast<int>(wParam), static_cast<int>(lParam));\n\n\tcase Message::GetSubStylesStart:\n\t\treturn DocumentLexState()->SubStylesStart(static_cast<int>(wParam));\n\n\tcase Message::GetSubStylesLength:\n\t\treturn DocumentLexState()->SubStylesLength(static_cast<int>(wParam));\n\n\tcase Message::GetStyleFromSubStyle:\n\t\treturn DocumentLexState()->StyleFromSubStyle(static_cast<int>(wParam));\n\n\tcase Message::GetPrimaryStyleFromStyle:\n\t\treturn DocumentLexState()->PrimaryStyleFromStyle(static_cast<int>(wParam));\n\n\tcase Message::FreeSubStyles:\n\t\tDocumentLexState()->FreeSubStyles();\n\t\tbreak;\n\n\tcase Message::SetIdentifiers:\n\t\tDocumentLexState()->SetIdentifiers(static_cast<int>(wParam),\n\t\t\t\t\t\t   ConstCharPtrFromSPtr(lParam));\n\t\tbreak;\n\n\tcase Message::DistanceToSecondaryStyles:\n\t\treturn DocumentLexState()->DistanceToSecondaryStyles();\n\n\tcase Message::GetSubStyleBases:\n\t\treturn StringResult(lParam, DocumentLexState()->GetSubStyleBases());\n\n\tcase Message::GetNamedStyles:\n\t\treturn DocumentLexState()->NamedStyles();\n\n\tcase Message::NameOfStyle:\n\t\treturn StringResult(lParam, DocumentLexState()->\n\t\t\t\t    NameOfStyle(static_cast<int>(wParam)));\n\n\tcase Message::TagsOfStyle:\n\t\treturn StringResult(lParam, DocumentLexState()->\n\t\t\t\t    TagsOfStyle(static_cast<int>(wParam)));\n\n\tcase Message::DescriptionOfStyle:\n\t\treturn StringResult(lParam, DocumentLexState()->\n\t\t\t\t    DescriptionOfStyle(static_cast<int>(wParam)));\n\n\tdefault:\n\t\treturn Editor::WndProc(iMessage, wParam, lParam);\n\t}\n\treturn 0;\n}\n"
  },
  {
    "path": "Libraries/scintilla/src/ScintillaBase.h",
    "content": "// Scintilla source code edit control\n/** @file ScintillaBase.h\n ** Defines an enhanced subclass of Editor with calltips, autocomplete and context menu.\n **/\n// Copyright 1998-2002 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#ifndef SCINTILLABASE_H\n#define SCINTILLABASE_H\n\nnamespace Scintilla::Internal {\n\n// For most platforms (not Cocoa) all IME indicators are drawn in same colour,\n// blue, with different patterns.\nconstexpr ColourRGBA colourIME(0x0, 0x0, 0xffU);\n\nconstexpr int IndicatorInput = static_cast<int>(Scintilla::IndicatorNumbers::Ime);\nconstexpr int IndicatorTarget = IndicatorInput + 1;\nconstexpr int IndicatorConverted = IndicatorInput + 2;\nconstexpr int IndicatorUnknown = IndicatorInput + 3;\n\nclass LexState;\n/**\n */\nclass ScintillaBase : public Editor, IListBoxDelegate {\nprotected:\n\t/** Enumeration of commands and child windows. */\n\tenum {\n\t\tidCallTip=1,\n\t\tidAutoComplete=2,\n\n\t\tidcmdUndo=10,\n\t\tidcmdRedo=11,\n\t\tidcmdCut=12,\n\t\tidcmdCopy=13,\n\t\tidcmdPaste=14,\n\t\tidcmdDelete=15,\n\t\tidcmdSelectAll=16\n\t};\n\n\tScintilla::PopUp displayPopupMenu;\n\tMenu popup;\n\tScintilla::Internal::AutoComplete ac;\n\n\tCallTip ct;\n\n\tint listType;\t\t\t///< 0 is an autocomplete list\n\tint maxListWidth;\t\t/// Maximum width of list, in average character widths\n\tScintilla::MultiAutoComplete multiAutoCMode; /// Mode for autocompleting when multiple selections are present\n\n\tLexState *DocumentLexState();\n\tvoid Colourise(int start, int end);\n\n\tScintillaBase();\n\t// Deleted so ScintillaBase objects can not be copied.\n\tScintillaBase(const ScintillaBase &) = delete;\n\tScintillaBase(ScintillaBase &&) = delete;\n\tScintillaBase &operator=(const ScintillaBase &) = delete;\n\tScintillaBase &operator=(ScintillaBase &&) = delete;\n\t// ~ScintillaBase() in public section\n\tvoid Initialise() override {}\n\tvoid Finalise() override;\n\n\tvoid InsertCharacter(std::string_view sv, Scintilla::CharacterSource charSource) override;\n\tvoid Command(int cmdId);\n\tvoid CancelModes() override;\n\tint KeyCommand(Scintilla::Message iMessage) override;\n\n\tvoid MoveImeCarets(Sci::Position offset) noexcept;\n\tvoid DrawImeIndicator(int indicator, Sci::Position len);\n\n\tvoid AutoCompleteInsert(Sci::Position startPos, Sci::Position removeLen, std::string_view text);\n\tvoid AutoCompleteStart(Sci::Position lenEntered, const char *list);\n\tvoid AutoCompleteCancel();\n\tvoid AutoCompleteMove(int delta);\n\tint AutoCompleteGetCurrent() const;\n\tint AutoCompleteGetCurrentText(char *buffer) const;\n\tvoid AutoCompleteCharacterAdded(char ch);\n\tvoid AutoCompleteCharacterDeleted();\n\tvoid AutoCompleteNotifyCompleted(char ch, CompletionMethods completionMethod, Sci::Position firstPos, const char *text);\n\tvoid AutoCompleteCompleted(char ch, Scintilla::CompletionMethods completionMethod);\n\tvoid AutoCompleteMoveToCurrentWord();\n\tvoid AutoCompleteSelection();\n\tvoid ListNotify(ListBoxEvent *plbe) override;\n\n\tvoid CallTipClick();\n\tvoid CallTipShow(Point pt, const char *defn);\n\tvirtual void CreateCallTipWindow(PRectangle rc) = 0;\n\n\tvirtual void AddToPopUp(const char *label, int cmd=0, bool enabled=true) = 0;\n\tbool ShouldDisplayPopup(Point ptInWindowCoordinates) const;\n\tvoid ContextMenu(Point pt);\n\n\tvoid ButtonDownWithModifiers(Point pt, unsigned int curTime, Scintilla::KeyMod modifiers) override;\n\tvoid RightButtonDownWithModifiers(Point pt, unsigned int curTime, Scintilla::KeyMod modifiers) override;\n\n\tvoid NotifyStyleToNeeded(Sci::Position endStyleNeeded) override;\n\npublic:\n\t~ScintillaBase() override;\n\n\t// Public so scintilla_send_message can use it\n\tScintilla::sptr_t WndProc(Scintilla::Message iMessage, Scintilla::uptr_t wParam, Scintilla::sptr_t lParam) override;\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/src/Selection.cxx",
    "content": "// Scintilla source code edit control\n/** @file Selection.cxx\n ** Classes maintaining the selection.\n **/\n// Copyright 2009 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#include <cstddef>\n#include <cstdlib>\n\n#include <stdexcept>\n#include <string>\n#include <string_view>\n#include <vector>\n#include <optional>\n#include <algorithm>\n#include <memory>\n#include <charconv>\n\n#include \"Debugging.h\"\n\n#include \"Position.h\"\n#include \"Selection.h\"\n\nusing namespace Scintilla::Internal;\n\nnamespace {\n\n// Generically convert a string to a integer value throwing if the conversion failed.\n// Failures include values that are out of range for the destination variable.\ntemplate <typename T>\nvoid ValueFromString(std::string_view sv, T &value) {\n\tconst std::from_chars_result res = std::from_chars(sv.data(), sv.data() + sv.size(), value);\n\tif (res.ec != std::errc{}) {\n\t\tif (res.ec == std::errc::result_out_of_range)\n\t\t\tthrow std::runtime_error(\"from_chars out of range.\");\n\t\tthrow std::runtime_error(\"from_chars failed.\");\n\t}\n}\n\n}\n\nSelectionPosition::SelectionPosition(std::string_view sv) : position(0) {\n\tif (const size_t v = sv.find('v'); v != std::string_view::npos) {\n\t\tValueFromString(sv.substr(v + 1), virtualSpace);\n\t\tsv = sv.substr(0, v);\n\t}\n\tValueFromString(sv, position);\n}\n\nvoid SelectionPosition::MoveForInsertDelete(bool insertion, Sci::Position startChange, Sci::Position length, bool moveForEqual) noexcept {\n\tif (insertion) {\n\t\tif (position == startChange) {\n\t\t\t// Always consume virtual space\n\t\t\tconst Sci::Position virtualLengthRemove = std::min(length, virtualSpace);\n\t\t\tvirtualSpace -= virtualLengthRemove;\n\t\t\tposition += virtualLengthRemove;\n\t\t\tif (moveForEqual) {\n\t\t\t\tconst Sci::Position lengthAfterVirtualRemove = length - virtualLengthRemove;\n\t\t\t\tposition += lengthAfterVirtualRemove;\n\t\t\t}\n\t\t} else if (position > startChange) {\n\t\t\tposition += length;\n\t\t}\n\t} else {\n\t\tif (position == startChange) {\n\t\t\tvirtualSpace = 0;\n\t\t}\n\t\tif (position > startChange) {\n\t\t\tconst Sci::Position endDeletion = startChange + length;\n\t\t\tif (position > endDeletion) {\n\t\t\t\tposition -= length;\n\t\t\t} else {\n\t\t\t\tposition = startChange;\n\t\t\t\tvirtualSpace = 0;\n\t\t\t}\n\t\t}\n\t}\n}\n\nbool SelectionPosition::operator >(const SelectionPosition &other) const noexcept {\n\tif (position == other.position)\n\t\treturn virtualSpace > other.virtualSpace;\n\treturn position > other.position;\n}\n\nbool SelectionPosition::operator <=(const SelectionPosition &other) const noexcept {\n\tif (other == *this)\n\t\treturn true;\n\treturn other > *this;\n}\n\nbool SelectionPosition::operator >=(const SelectionPosition &other) const noexcept {\n\tif (other == *this)\n\t\treturn true;\n\treturn *this > other;\n}\n\ndouble SelectionPosition::VirtualSpaceWidth(double spaceWidth) const noexcept {\n\treturn static_cast<double>(virtualSpace) * spaceWidth;\n}\n\nstd::string SelectionPosition::ToString() const {\n\tstd::string result = std::to_string(position);\n\tif (virtualSpace) {\n\t\tresult += 'v';\n\t\tresult += std::to_string(virtualSpace);\n\t}\n\treturn result;\n}\n\nSelectionRange::SelectionRange(std::string_view sv) {\n\tconst size_t dash = sv.find('-');\n\tif (dash == std::string_view::npos) {\n\t\tanchor = SelectionPosition(sv);\n\t\tcaret = anchor;\n\t} else {\n\t\tanchor = SelectionPosition(sv.substr(0, dash));\n\t\tcaret = SelectionPosition(sv.substr(dash + 1));\n\t}\n}\n\nSci::Position SelectionRange::Length() const noexcept {\n\tif (anchor > caret) {\n\t\treturn anchor.Position() - caret.Position();\n\t} else {\n\t\treturn caret.Position() - anchor.Position();\n\t}\n}\n\nvoid SelectionRange::MoveForInsertDelete(bool insertion, Sci::Position startChange, Sci::Position length) noexcept {\n\t// For insertions that occur at the start of the selection move both the start\n\t// and end of the selection to preserve the selected length.\n\t// The end will automatically move since it is after the insertion, so determine\n\t// which position is the start and pass this into\n\t// SelectionPosition::MoveForInsertDelete.\n\t// There isn't any reason to move an empty selection so don't move it.\n\tconst bool caretStart = caret.Position() < anchor.Position();\n\tconst bool anchorStart = anchor.Position() < caret.Position();\n\n\tcaret.MoveForInsertDelete(insertion, startChange, length, caretStart);\n\tanchor.MoveForInsertDelete(insertion, startChange, length, anchorStart);\n}\n\nbool SelectionRange::Contains(Sci::Position pos) const noexcept {\n\tif (anchor > caret)\n\t\treturn (pos >= caret.Position()) && (pos <= anchor.Position());\n\telse\n\t\treturn (pos >= anchor.Position()) && (pos <= caret.Position());\n}\n\nbool SelectionRange::Contains(SelectionPosition sp) const noexcept {\n\tif (anchor > caret)\n\t\treturn (sp >= caret) && (sp <= anchor);\n\telse\n\t\treturn (sp >= anchor) && (sp <= caret);\n}\n\nbool SelectionRange::ContainsCharacter(Sci::Position posCharacter) const noexcept {\n\tif (anchor > caret)\n\t\treturn (posCharacter >= caret.Position()) && (posCharacter < anchor.Position());\n\telse\n\t\treturn (posCharacter >= anchor.Position()) && (posCharacter < caret.Position());\n}\n\nbool SelectionRange::ContainsCharacter(SelectionPosition spCharacter) const noexcept {\n\tif (anchor > caret)\n\t\treturn (spCharacter >= caret) && (spCharacter < anchor);\n\telse\n\t\treturn (spCharacter >= anchor) && (spCharacter < caret);\n}\n\nSelectionSegment SelectionRange::Intersect(SelectionSegment check) const noexcept {\n\tconst SelectionSegment inOrder = AsSegment();\n\tif ((inOrder.start > check.end) || (inOrder.end < check.start)) {\n\t\t// Nothing in common, not even touching so return empty *invalid* segment\n\t\treturn {};\n\t}\n\treturn {\n\t\tstd::max(check.start, inOrder.start),\n\t\tstd::min(check.end, inOrder.end)\n\t};\n}\n\nvoid SelectionRange::Swap() noexcept {\n\tstd::swap(caret, anchor);\n}\n\nbool SelectionRange::Trim(SelectionRange range) noexcept {\n\tconst SelectionPosition startRange = range.Start();\n\tconst SelectionPosition endRange = range.End();\n\tSelectionPosition start = Start();\n\tSelectionPosition end = End();\n\tPLATFORM_ASSERT(start <= end);\n\tPLATFORM_ASSERT(startRange <= endRange);\n\tif ((startRange <= end) && (endRange >= start)) {\n\t\tif ((start > startRange) && (end < endRange)) {\n\t\t\t// Completely covered by range -> empty at start\n\t\t\tend = start;\n\t\t} else if ((start < startRange) && (end > endRange)) {\n\t\t\t// Completely covers range -> empty at start\n\t\t\tend = start;\n\t\t} else if (start <= startRange) {\n\t\t\t// Trim end\n\t\t\tend = startRange;\n\t\t} else { //\n\t\t\tPLATFORM_ASSERT(end >= endRange);\n\t\t\t// Trim start\n\t\t\tstart = endRange;\n\t\t}\n\t\tif (anchor > caret) {\n\t\t\tcaret = start;\n\t\t\tanchor = end;\n\t\t} else {\n\t\t\tanchor = start;\n\t\t\tcaret = end;\n\t\t}\n\t\treturn Empty();\n\t}\n\treturn false;\n}\n\nvoid SelectionRange::Truncate(Sci::Position length) noexcept {\n\tif (anchor.Position() > length)\n\t\tanchor.SetPosition(length);\n\tif (caret.Position() > length)\n\t\tcaret.SetPosition(length);\n}\n\n// If range is all virtual collapse to start of virtual space\nvoid SelectionRange::MinimizeVirtualSpace() noexcept {\n\tif (caret.Position() == anchor.Position()) {\n\t\tSci::Position virtualSpace = caret.VirtualSpace();\n\t\tif (virtualSpace > anchor.VirtualSpace())\n\t\t\tvirtualSpace = anchor.VirtualSpace();\n\t\tcaret.SetVirtualSpace(virtualSpace);\n\t\tanchor.SetVirtualSpace(virtualSpace);\n\t}\n}\n\nstd::string SelectionRange::ToString() const {\n\tstd::string result = anchor.ToString();\n\tif (!(caret == anchor)) {\n\t\tresult += '-';\n\t\tresult += caret.ToString();\n\t}\n\treturn result;\n}\n\nSelection::Selection() : mainRange(0), moveExtends(false), tentativeMain(false), selType(SelTypes::stream) {\n\tAddSelection(SelectionRange(SelectionPosition(0)));\n}\n\nSelection::Selection(std::string_view sv) : mainRange(0), moveExtends(false), tentativeMain(false), selType(SelTypes::stream) {\n\tif (sv.empty()) {\n\t\treturn;\n\t}\n\ttry {\n\t\t// Decode initial letter prefix if any\n\t\tswitch (sv.front()) {\n\t\tcase 'R':\n\t\t\tselType = SelTypes::rectangle;\n\t\t\tbreak;\n\t\tcase 'L':\n\t\t\tselType = SelTypes::lines;\n\t\t\tbreak;\n\t\tcase 'T':\n\t\t\tselType = SelTypes::thin;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t\tif (selType != SelTypes::stream) {\n\t\t\tsv.remove_prefix(1);\n\t\t}\n\n\t\t// Non-zero main index at end after '#'\n\t\tif (const size_t hash = sv.find('#'); hash != std::string_view::npos) {\n\t\t\tValueFromString(sv.substr(hash + 1), mainRange);\n\t\t\tsv = sv.substr(0, hash);\n\t\t}\n\n\t\t// Remainder is list of ranges\n\t\tif (selType == SelTypes::rectangle || selType == SelTypes::thin) {\n\t\t\trangeRectangular = SelectionRange(sv);\n\t\t\t// Ensure enough ranges exist for mainRange to be in bounds\n\t\t\tfor (size_t i = 0; i <= mainRange; i++) {\n\t\t\t\tranges.emplace_back(SelectionPosition(0));\n\t\t\t}\n\t\t} else {\n\t\t\tsize_t comma = sv.find(',');\n\t\t\twhile (comma != std::string_view::npos) {\n\t\t\t\tranges.emplace_back(sv.substr(0, comma));\n\t\t\t\tsv.remove_prefix(comma + 1);\n\t\t\t\tcomma = sv.find(',');\n\t\t\t}\n\t\t\tranges.emplace_back(sv);\n\t\t\tif (mainRange >= ranges.size()) {\n\t\t\t\tmainRange = ranges.size() - 1;\n\t\t\t}\n\t\t}\n\t} catch (std::runtime_error &) {\n\t\t// On failure, produce an empty selection.\n\t\tClear();\n\t}\n}\n\nbool Selection::IsRectangular() const noexcept {\n\treturn (selType == SelTypes::rectangle) || (selType == SelTypes::thin);\n}\n\nSci::Position Selection::MainCaret() const noexcept {\n\treturn ranges[mainRange].caret.Position();\n}\n\nSci::Position Selection::MainAnchor() const noexcept {\n\treturn ranges[mainRange].anchor.Position();\n}\n\nSelectionRange &Selection::Rectangular() noexcept {\n\treturn rangeRectangular;\n}\n\nSelectionRange Selection::RectangularCopy() const noexcept {\n\treturn rangeRectangular;\n}\n\nSelectionSegment Selection::Limits() const noexcept {\n\tPLATFORM_ASSERT(!ranges.empty());\n\tSelectionSegment sr = ranges[0].AsSegment();\n\tfor (size_t i=1; i<ranges.size(); i++) {\n\t\tsr.Extend(ranges[i].anchor);\n\t\tsr.Extend(ranges[i].caret);\n\t}\n\treturn sr;\n}\n\nSelectionSegment Selection::LimitsForRectangularElseMain() const noexcept {\n\tif (IsRectangular()) {\n\t\treturn Limits();\n\t}\n\treturn ranges[mainRange].AsSegment();\n}\n\nsize_t Selection::Count() const noexcept {\n\treturn ranges.size();\n}\n\nsize_t Selection::Main() const noexcept {\n\treturn mainRange;\n}\n\nvoid Selection::SetMain(size_t r) noexcept {\n\tPLATFORM_ASSERT(r < ranges.size());\n\tmainRange = r;\n}\n\nSelectionRange &Selection::Range(size_t r) noexcept {\n\treturn ranges[r];\n}\n\nconst SelectionRange &Selection::Range(size_t r) const noexcept {\n\treturn ranges[r];\n}\n\nSelectionRange &Selection::RangeMain() noexcept {\n\treturn ranges[mainRange];\n}\n\nconst SelectionRange &Selection::RangeMain() const noexcept {\n\treturn ranges[mainRange];\n}\n\nSelectionPosition Selection::Start() const noexcept {\n\tif (IsRectangular()) {\n\t\treturn rangeRectangular.Start();\n\t}\n\treturn ranges[mainRange].Start();\n}\n\nbool Selection::MoveExtends() const noexcept {\n\treturn moveExtends;\n}\n\nvoid Selection::SetMoveExtends(bool moveExtends_) noexcept {\n\tmoveExtends = moveExtends_;\n}\n\nbool Selection::Empty() const noexcept {\n\tfor (const SelectionRange &range : ranges) {\n\t\tif (!range.Empty())\n\t\t\treturn false;\n\t}\n\treturn true;\n}\n\nSelectionPosition Selection::Last() const noexcept {\n\tSelectionPosition lastPosition;\n\tfor (const SelectionRange &range : ranges) {\n\t\tif (lastPosition < range.caret)\n\t\t\tlastPosition = range.caret;\n\t\tif (lastPosition < range.anchor)\n\t\t\tlastPosition = range.anchor;\n\t}\n\treturn lastPosition;\n}\n\nSci::Position Selection::Length() const noexcept {\n\tSci::Position len = 0;\n\tfor (const SelectionRange &range : ranges) {\n\t\tlen += range.Length();\n\t}\n\treturn len;\n}\n\nvoid Selection::MovePositions(bool insertion, Sci::Position startChange, Sci::Position length) noexcept {\n\tfor (SelectionRange &range : ranges) {\n\t\trange.MoveForInsertDelete(insertion, startChange, length);\n\t}\n\tif (selType == SelTypes::rectangle) {\n\t\trangeRectangular.MoveForInsertDelete(insertion, startChange, length);\n\t}\n}\n\nvoid Selection::TrimSelection(SelectionRange range) noexcept {\n\tfor (size_t i=0; i<ranges.size();) {\n\t\tif ((i != mainRange) && (ranges[i].Trim(range))) {\n\t\t\t// Trimmed to empty so remove\n\t\t\tfor (size_t j=i; j<ranges.size()-1; j++) {\n\t\t\t\tranges[j] = ranges[j+1];\n\t\t\t\tif (j == mainRange-1)\n\t\t\t\t\tmainRange--;\n\t\t\t}\n\t\t\tranges.pop_back();\n\t\t} else {\n\t\t\ti++;\n\t\t}\n\t}\n}\n\nvoid Selection::TrimOtherSelections(size_t r, SelectionRange range) noexcept {\n\tfor (size_t i = 0; i<ranges.size(); ++i) {\n\t\tif (i != r) {\n\t\t\tranges[i].Trim(range);\n\t\t}\n\t}\n}\n\nvoid Selection::SetSelection(SelectionRange range) noexcept {\n\tif (ranges.size() > 1) {\n\t\tranges.erase(ranges.begin() + 1, ranges.end());\n\t}\n\tranges[0] = range;\n\tmainRange = 0;\n}\n\nvoid Selection::AddSelection(SelectionRange range) {\n\tTrimSelection(range);\n\tranges.push_back(range);\n\tmainRange = ranges.size() - 1;\n}\n\nvoid Selection::AddSelectionWithoutTrim(SelectionRange range) {\n\tranges.push_back(range);\n\tmainRange = ranges.size() - 1;\n}\n\nvoid Selection::DropSelection(size_t r) noexcept {\n\tif ((ranges.size() > 1) && (r < ranges.size())) {\n\t\tsize_t mainNew = mainRange;\n\t\tif (mainNew >= r) {\n\t\t\tif (mainNew == 0) {\n\t\t\t\tmainNew = ranges.size() - 2;\n\t\t\t} else {\n\t\t\t\tmainNew--;\n\t\t\t}\n\t\t}\n\t\tranges.erase(ranges.begin() + r);\n\t\tmainRange = mainNew;\n\t}\n}\n\nvoid Selection::DropAdditionalRanges() noexcept {\n\tSetSelection(RangeMain());\n}\n\nvoid Selection::TentativeSelection(SelectionRange range) {\n\tif (!tentativeMain) {\n\t\trangesSaved = ranges;\n\t}\n\tranges = rangesSaved;\n\tAddSelection(range);\n\tTrimSelection(ranges[mainRange]);\n\ttentativeMain = true;\n}\n\nvoid Selection::CommitTentative() noexcept {\n\trangesSaved.clear();\n\ttentativeMain = false;\n}\n\nInSelection Selection::RangeType(size_t r) const noexcept {\n\treturn r == Main() ? InSelection::inMain : InSelection::inAdditional;\n}\n\nInSelection Selection::CharacterInSelection(Sci::Position posCharacter) const noexcept {\n\tfor (size_t i=0; i<ranges.size(); i++) {\n\t\tif (ranges[i].ContainsCharacter(posCharacter))\n\t\t\treturn RangeType(i);\n\t}\n\treturn InSelection::inNone;\n}\n\nInSelection Selection::InSelectionForEOL(Sci::Position pos) const noexcept {\n\tfor (size_t i=0; i<ranges.size(); i++) {\n\t\tif (!ranges[i].Empty() && (pos > ranges[i].Start().Position()) && (pos <= ranges[i].End().Position()))\n\t\t\treturn RangeType(i);\n\t}\n\treturn InSelection::inNone;\n}\n\nSci::Position Selection::VirtualSpaceFor(Sci::Position pos) const noexcept {\n\tSci::Position virtualSpace = 0;\n\tfor (const SelectionRange &range : ranges) {\n\t\tif ((range.caret.Position() == pos) && (virtualSpace < range.caret.VirtualSpace()))\n\t\t\tvirtualSpace = range.caret.VirtualSpace();\n\t\tif ((range.anchor.Position() == pos) && (virtualSpace < range.anchor.VirtualSpace()))\n\t\t\tvirtualSpace = range.anchor.VirtualSpace();\n\t}\n\treturn virtualSpace;\n}\n\nvoid Selection::Clear() noexcept {\n\tif (ranges.size() > 1) {\n\t\tranges.erase(ranges.begin() + 1, ranges.end());\n\t}\n\tranges[0].Reset();\n\trangesSaved.clear();\n\trangeRectangular.Reset();\n\tmainRange = 0;\n\tmoveExtends = false;\n\ttentativeMain = false;\n\tselType = SelTypes::stream;\n}\n\nvoid Selection::RemoveDuplicates() noexcept {\n\tfor (size_t i=0; i<ranges.size()-1; i++) {\n\t\tif (ranges[i].Empty()) {\n\t\t\tsize_t j=i+1;\n\t\t\twhile (j<ranges.size()) {\n\t\t\t\tif (ranges[i] == ranges[j]) {\n\t\t\t\t\tranges.erase(ranges.begin() + j);\n\t\t\t\t\tif (mainRange >= j)\n\t\t\t\t\t\tmainRange--;\n\t\t\t\t} else {\n\t\t\t\t\tj++;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid Selection::RotateMain() noexcept {\n\tmainRange = (mainRange + 1) % ranges.size();\n}\n\nvoid Selection::SetRanges(const Ranges &rangesToSet) {\n\tranges = rangesToSet;\n}\n\nvoid Selection::Truncate(Sci::Position length) noexcept {\n\t// This may be needed when applying a persisted selection onto a document that has been shortened.\n\tfor (SelectionRange &range : ranges) {\n\t\trange.Truncate(length);\n\t}\n\t// Above may have made some non-unique empty ranges.\n\tRemoveDuplicates();\n\trangeRectangular.Truncate(length);\n}\n\nstd::string Selection::ToString() const {\n\tstd::string result;\n\tswitch (selType) {\n\tcase SelTypes::rectangle:\n\t\tresult += 'R';\n\t\tbreak;\n\tcase SelTypes::lines:\n\t\tresult += 'L';\n\t\tbreak;\n\tcase SelTypes::thin:\n\t\tresult += 'T';\n\t\tbreak;\n\tdefault:\n\t\t// No handling of none as not a real value of enumeration, just used for empty arguments\n\t\t// No prefix.\n\t\tbreak;\n\t}\n\tif (selType == SelTypes::rectangle || selType == SelTypes::thin) {\n\t\tresult += rangeRectangular.ToString();\n\t} else {\n\t\tfor (size_t r = 0; r < ranges.size(); r++) {\n\t\t\tif (r > 0) {\n\t\t\t\tresult += ',';\n\t\t\t}\n\t\t\tresult += ranges[r].ToString();\n\t\t}\n\t}\n\n\tif (mainRange > 0) {\n\t\tresult += '#';\n\t\tresult += std::to_string(mainRange);\n\t}\n\n\treturn result;\n}\n"
  },
  {
    "path": "Libraries/scintilla/src/Selection.h",
    "content": "// Scintilla source code edit control\n/** @file Selection.h\n ** Classes maintaining the selection.\n **/\n// Copyright 2009 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#ifndef SELECTION_H\n#define SELECTION_H\n\nnamespace Scintilla::Internal {\n\nclass SelectionPosition {\n\tSci::Position position = Sci::invalidPosition;\n\tSci::Position virtualSpace = 0;\npublic:\n\tconstexpr SelectionPosition() noexcept = default;\n\tconstexpr explicit SelectionPosition(Sci::Position position_, Sci::Position virtualSpace_=0) noexcept : position(position_), virtualSpace(virtualSpace_) {\n\t\tPLATFORM_ASSERT(virtualSpace < 800000000);\n\t\tif (virtualSpace < 0)\n\t\t\tvirtualSpace = 0;\n\t}\n\texplicit SelectionPosition(std::string_view sv);\n\tvoid Reset() noexcept {\n\t\tposition = 0;\n\t\tvirtualSpace = 0;\n\t}\n\tvoid MoveForInsertDelete(bool insertion, Sci::Position startChange, Sci::Position length, bool moveForEqual) noexcept;\n\t[[nodiscard]] constexpr bool operator ==(const SelectionPosition &other) const noexcept {\n\t\treturn (position == other.position) && (virtualSpace == other.virtualSpace);\n\t}\n\t[[nodiscard]] constexpr bool operator !=(const SelectionPosition &other) const noexcept {\n\t\treturn (position != other.position) || (virtualSpace != other.virtualSpace);\n\t}\n\t[[nodiscard]] constexpr bool operator <(const SelectionPosition &other) const noexcept {\n\t\tif (position == other.position)\n\t\t\treturn virtualSpace < other.virtualSpace;\n\t\treturn position < other.position;\n\t}\n\t[[nodiscard]] bool operator >(const SelectionPosition &other) const noexcept;\n\t[[nodiscard]] bool operator <=(const SelectionPosition &other) const noexcept;\n\t[[nodiscard]] bool operator >=(const SelectionPosition &other) const noexcept;\n\tSci::Position Position() const noexcept {\n\t\treturn position;\n\t}\n\tvoid SetPosition(Sci::Position position_) noexcept {\n\t\tposition = position_;\n\t\tvirtualSpace = 0;\n\t}\n\tSci::Position VirtualSpace() const noexcept {\n\t\treturn virtualSpace;\n\t}\n\t[[nodiscard]] double VirtualSpaceWidth(double spaceWidth) const noexcept;\n\tvoid SetVirtualSpace(Sci::Position virtualSpace_) noexcept {\n\t\tPLATFORM_ASSERT(virtualSpace_ < 800000000);\n\t\tif (virtualSpace_ >= 0)\n\t\t\tvirtualSpace = virtualSpace_;\n\t}\n\tvoid Add(Sci::Position increment) noexcept {\n\t\tposition = position + increment;\n\t}\n\tvoid AddVirtualSpace(Sci::Position increment) noexcept {\n\t\tSetVirtualSpace(virtualSpace + increment);\n\t}\n\tbool IsValid() const noexcept {\n\t\treturn position >= 0;\n\t}\n\tstd::string ToString() const;\n};\n\n// Ordered range to make drawing simpler\nstruct SelectionSegment {\n\tSelectionPosition start;\n\tSelectionPosition end;\n\tconstexpr SelectionSegment() noexcept = default;\n\tconstexpr SelectionSegment(SelectionPosition a, SelectionPosition b) noexcept {\n\t\tif (a < b) {\n\t\t\tstart = a;\n\t\t\tend = b;\n\t\t} else {\n\t\t\tstart = b;\n\t\t\tend = a;\n\t\t}\n\t}\n\tconstexpr SelectionSegment(Sci::Position a, Sci::Position b) :\n\t\tSelectionSegment(SelectionPosition(a), SelectionPosition(b)) {\n\t}\n\t[[nodiscard]] constexpr bool operator ==(const SelectionSegment &other) const noexcept {\n\t\treturn (start == other.start) && (end == other.end);\n\t}\n\tbool Empty() const noexcept {\n\t\treturn start == end;\n\t}\n\tSci::Position Length() const noexcept {\n\t\treturn end.Position() - start.Position();\n\t}\n\tvoid Extend(SelectionPosition p) noexcept {\n\t\tif (start > p)\n\t\t\tstart = p;\n\t\tif (end < p)\n\t\t\tend = p;\n\t}\n\tSelectionSegment Subtract(Sci::Position increment) const noexcept {\n\t\tSelectionSegment ret(start, end);\n\t\tret.start.Add(-increment);\n\t\tret.end.Add(-increment);\n\t\treturn ret;\n\t}\n};\n\nstruct SelectionRange {\n\tSelectionPosition caret;\n\tSelectionPosition anchor;\n\n\tconstexpr SelectionRange() noexcept = default;\n\tconstexpr explicit SelectionRange(SelectionPosition single) noexcept : caret(single), anchor(single) {\n\t}\n\tconstexpr explicit SelectionRange(Sci::Position single) noexcept : caret(single), anchor(single) {\n\t}\n\tconstexpr SelectionRange(SelectionPosition caret_, SelectionPosition anchor_) noexcept : caret(caret_), anchor(anchor_) {\n\t}\n\tconstexpr SelectionRange(Sci::Position caret_, Sci::Position anchor_) noexcept : caret(caret_), anchor(anchor_) {\n\t}\n\texplicit SelectionRange(std::string_view sv);\n\tSelectionSegment AsSegment() const noexcept {\n\t\treturn {caret, anchor};\n\t}\n\tbool Empty() const noexcept {\n\t\treturn anchor == caret;\n\t}\n\tSci::Position Length() const noexcept;\n\t// Sci::Position Width() const;\t// Like Length but takes virtual space into account\n\tbool operator ==(const SelectionRange &other) const noexcept {\n\t\treturn caret == other.caret && anchor == other.anchor;\n\t}\n\tbool operator <(const SelectionRange &other) const noexcept {\n\t\treturn caret < other.caret || ((caret == other.caret) && (anchor < other.anchor));\n\t}\n\tvoid Reset() noexcept {\n\t\tanchor.Reset();\n\t\tcaret.Reset();\n\t}\n\tvoid ClearVirtualSpace() noexcept {\n\t\tanchor.SetVirtualSpace(0);\n\t\tcaret.SetVirtualSpace(0);\n\t}\n\tvoid MoveForInsertDelete(bool insertion, Sci::Position startChange, Sci::Position length) noexcept;\n\tbool Contains(Sci::Position pos) const noexcept;\n\tbool Contains(SelectionPosition sp) const noexcept;\n\tbool ContainsCharacter(Sci::Position posCharacter) const noexcept;\n\tbool ContainsCharacter(SelectionPosition spCharacter) const noexcept;\n\tSelectionSegment Intersect(SelectionSegment check) const noexcept;\n\tSelectionPosition Start() const noexcept {\n\t\treturn (anchor < caret) ? anchor : caret;\n\t}\n\tSelectionPosition End() const noexcept {\n\t\treturn (anchor < caret) ? caret : anchor;\n\t}\n\tvoid Swap() noexcept;\n\tbool Trim(SelectionRange range) noexcept;\n\tvoid Truncate(Sci::Position length) noexcept;\n\t// If range is all virtual collapse to start of virtual space\n\tvoid MinimizeVirtualSpace() noexcept;\n\tstd::string ToString() const;\n};\n\n// Deliberately an enum rather than an enum class to allow treating as bool\nenum InSelection { inNone, inMain, inAdditional };\n\nclass Selection {\n\tusing Ranges = std::vector<SelectionRange>;\n\tRanges ranges;\n\tRanges rangesSaved;\n\tSelectionRange rangeRectangular;\n\tsize_t mainRange;\n\tbool moveExtends;\n\tbool tentativeMain;\npublic:\n\tenum class SelTypes { none, stream, rectangle, lines, thin };\n\tSelTypes selType;\n\n\tSelection();\t// Allocates so may throw.\n\texplicit Selection(std::string_view sv);\n\n\tbool IsRectangular() const noexcept;\n\tSci::Position MainCaret() const noexcept;\n\tSci::Position MainAnchor() const noexcept;\n\tSelectionRange &Rectangular() noexcept;\n\tSelectionRange RectangularCopy() const noexcept;\n\tSelectionSegment Limits() const noexcept;\n\t// This is for when you want to move the caret in response to a\n\t// user direction command - for rectangular selections, use the range\n\t// that covers all selected text otherwise return the main selection.\n\tSelectionSegment LimitsForRectangularElseMain() const noexcept;\n\tsize_t Count() const noexcept;\n\tsize_t Main() const noexcept;\n\tvoid SetMain(size_t r) noexcept;\n\tSelectionRange &Range(size_t r) noexcept;\n\tconst SelectionRange &Range(size_t r) const noexcept;\n\tSelectionRange &RangeMain() noexcept;\n\tconst SelectionRange &RangeMain() const noexcept;\n\tSelectionPosition Start() const noexcept;\n\tbool MoveExtends() const noexcept;\n\tvoid SetMoveExtends(bool moveExtends_) noexcept;\n\tbool Empty() const noexcept;\n\tSelectionPosition Last() const noexcept;\n\tSci::Position Length() const noexcept;\n\tvoid MovePositions(bool insertion, Sci::Position startChange, Sci::Position length) noexcept;\n\tvoid TrimSelection(SelectionRange range) noexcept;\n\tvoid TrimOtherSelections(size_t r, SelectionRange range) noexcept;\n\tvoid SetSelection(SelectionRange range) noexcept;\n\tvoid AddSelection(SelectionRange range);\n\tvoid AddSelectionWithoutTrim(SelectionRange range);\n\tvoid DropSelection(size_t r) noexcept;\n\tvoid DropAdditionalRanges() noexcept;\n\tvoid TentativeSelection(SelectionRange range);\n\tvoid CommitTentative() noexcept;\n\tInSelection RangeType(size_t r) const noexcept;\n\tInSelection CharacterInSelection(Sci::Position posCharacter) const noexcept;\n\tInSelection InSelectionForEOL(Sci::Position pos) const noexcept;\n\tSci::Position VirtualSpaceFor(Sci::Position pos) const noexcept;\n\tvoid Clear() noexcept;\n\tvoid RemoveDuplicates() noexcept;\n\tvoid RotateMain() noexcept;\n\tbool Tentative() const noexcept { return tentativeMain; }\n\tRanges RangesCopy() const {\n\t\treturn ranges;\n\t}\n\tvoid SetRanges(const Ranges &rangesToSet);\n\tvoid Truncate(Sci::Position length) noexcept;\n\tstd::string ToString() const;\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/src/SparseVector.h",
    "content": "// Scintilla source code edit control\n/** @file SparseVector.h\n ** Hold data sparsely associated with elements in a range.\n **/\n// Copyright 2016 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#ifndef SPARSEVECTOR_H\n#define SPARSEVECTOR_H\n\nnamespace Scintilla::Internal {\n\n// SparseVector is similar to RunStyles but is more efficient for cases where values occur\n// for one position instead of over a range of positions.\n// There are always elements at the start and end, so the element type should have\n// a reasonable empty value that will cause no problems.\n// The element type should have a noexcept default constructor as that allows methods to\n// be noexcept.\ntemplate <typename T>\nclass SparseVector {\nprivate:\n\tPartitioning<Sci::Position> starts;\n\tSplitVector<T> values;\n\tT empty;\t// Return from ValueAt when no element at a position.\n\tvoid ClearValue(Sci::Position partition) noexcept {\n\t\tvalues.SetValueAt(partition, T());\n\t}\npublic:\n\tSparseVector() : empty() {\n\t\tvalues.InsertEmpty(0, 2);\n\t}\n\tSci::Position Length() const noexcept {\n\t\treturn starts.Length();\n\t}\n\tSci::Position Elements() const noexcept {\n\t\treturn starts.Partitions();\n\t}\n\tSci::Position PositionOfElement(Sci::Position element) const noexcept {\n\t\treturn starts.PositionFromPartition(element);\n\t}\n\tSci::Position ElementFromPosition(Sci::Position position) const noexcept {\n\t\tif (position < Length()) {\n\t\t\treturn starts.PartitionFromPosition(position);\n\t\t} else {\n\t\t\treturn starts.Partitions();\n\t\t}\n\t}\n\tconst T& ValueAt(Sci::Position position) const noexcept {\n\t\tassert(position <= Length());\n\t\tconst Sci::Position partition = ElementFromPosition(position);\n\t\tconst Sci::Position startPartition = starts.PositionFromPartition(partition);\n\t\tif (startPartition == position) {\n\t\t\treturn values.ValueAt(partition);\n\t\t} else {\n\t\t\treturn empty;\n\t\t}\n\t}\n\tT Extract(Sci::Position position) {\n\t\t// Move value currently at position; clear and remove position; return value.\n\t\t// Doesn't remove position at start or end.\n\t\tassert(position <= Length());\n\t\tconst Sci::Position partition = ElementFromPosition(position);\n\t\tassert(partition >= 0);\n\t\tassert(partition <= starts.Partitions());\n\t\tassert(starts.PositionFromPartition(partition) == position);\n\t\tT value = std::move(values.operator[](partition));\n\t\tif ((partition > 0) && (partition < starts.Partitions())) {\n\t\t\tstarts.RemovePartition(partition);\n\t\t\tvalues.Delete(partition);\n\t\t}\n\t\tCheck();\n\t\treturn value;\n\t}\n\ttemplate <typename ParamType>\n\tvoid SetValueAt(Sci::Position position, ParamType &&value) {\n\t\tassert(position <= Length());\n\t\tconst Sci::Position partition = ElementFromPosition(position);\n\t\tconst Sci::Position startPartition = starts.PositionFromPartition(partition);\n\t\tif (value == T()) {\n\t\t\t// Setting the empty value is equivalent to deleting the position\n\t\t\tif (position == 0 || position == Length()) {\n\t\t\t\tClearValue(partition);\n\t\t\t} else if (position == startPartition) {\n\t\t\t\t// Currently an element at this position, so remove\n\t\t\t\tClearValue(partition);\n\t\t\t\tstarts.RemovePartition(partition);\n\t\t\t\tvalues.Delete(partition);\n\t\t\t}\n\t\t\t// Else element remains empty\n\t\t} else {\n\t\t\tif (position == startPartition) {\n\t\t\t\t// Already a value at this position, so replace\n\t\t\t\tClearValue(partition);\n\t\t\t\tvalues.SetValueAt(partition, std::forward<ParamType>(value));\n\t\t\t} else {\n\t\t\t\t// Insert a new element\n\t\t\t\tstarts.InsertPartition(partition + 1, position);\n\t\t\t\tvalues.Insert(partition + 1, std::forward<ParamType>(value));\n\t\t\t}\n\t\t}\n\t}\n\tvoid InsertSpace(Sci::Position position, Sci::Position insertLength) {\n\t\tassert(position <= Length());\n\t\tconst Sci::Position partition = starts.PartitionFromPosition(position);\n\t\tconst Sci::Position startPartition = starts.PositionFromPartition(partition);\n\t\tif (startPartition == position) {\n\t\t\tconst bool positionOccupied = values.ValueAt(partition) != T();\n\t\t\t// Inserting at start of run so make previous longer\n\t\t\tif (partition == 0) {\n\t\t\t\t// Inserting at start of document so ensure start empty\n\t\t\t\tif (positionOccupied) {\n\t\t\t\t\tstarts.InsertPartition(1, 0);\n\t\t\t\t\tvalues.InsertEmpty(0, 1);\n\t\t\t\t}\n\t\t\t\tstarts.InsertText(partition, insertLength);\n\t\t\t} else {\n\t\t\t\tif (positionOccupied) {\n\t\t\t\t\tstarts.InsertText(partition - 1, insertLength);\n\t\t\t\t} else {\n\t\t\t\t\t// Insert at end of run so do not extend style\n\t\t\t\t\tstarts.InsertText(partition, insertLength);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tstarts.InsertText(partition, insertLength);\n\t\t}\n\t}\n\tvoid DeletePosition(Sci::Position position) {\n\t\tassert(position < Length());\n\t\tSci::Position partition = starts.PartitionFromPosition(position);\n\t\tconst Sci::Position startPartition = starts.PositionFromPartition(partition);\n\t\tif (startPartition == position) {\n\t\t\tif (partition == 0) {\n\t\t\t\tClearValue(0);\n\t\t\t\tif (starts.PositionFromPartition(1) == 1) {\n\t\t\t\t\t// Removing all space of first partition, so remove next partition\n\t\t\t\t\t// and move value if not last\n\t\t\t\t\tif (Elements() > 1) {\n\t\t\t\t\t\tstarts.RemovePartition(partition + 1);\n\t\t\t\t\t\tvalues.Delete(partition);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (partition == starts.Partitions()) {\n\t\t\t\t// This should not be possible\n\t\t\t\tClearValue(partition);\n\t\t\t\tthrow std::runtime_error(\"SparseVector: deleting end partition.\");\n\t\t\t} else {\n\t\t\t\tClearValue(partition);\n\t\t\t\tstarts.RemovePartition(partition);\n\t\t\t\tvalues.Delete(partition);\n\t\t\t\t// Its the previous partition now that gets smaller\n\t\t\t\tpartition--;\n\t\t\t}\n\t\t}\n\t\tstarts.InsertText(partition, -1);\n\t\tCheck();\n\t}\n\tvoid DeleteAll() {\n\t\tstarts = Partitioning<Sci::Position>();\n\t\tvalues = SplitVector<T>();\n\t\tvalues.InsertEmpty(0, 2);\n\t}\n\tvoid DeleteRange(Sci::Position position, Sci::Position deleteLength) {\n\t\t// For now, delete elements in range - may want to leave value at start\n\t\t// or combine onto position.\n\t\tif (position > Length() || (deleteLength == 0)) {\n\t\t\treturn;\n\t\t}\n\t\tconst Sci::Position positionEnd = position + deleteLength;\n\t\tassert(positionEnd <= Length());\n\t\tif (position == 0) {\n\t\t\t// Remove all partitions in range, moving values to start\n\t\t\twhile ((Elements() > 1) && (starts.PositionFromPartition(1) <= deleteLength)) {\n\t\t\t\tstarts.RemovePartition(1);\n\t\t\t\tvalues.Delete(0);\n\t\t\t}\n\t\t\tstarts.InsertText(0, -deleteLength);\n\t\t\tif (Length() == 0) {\n\t\t\t\tClearValue(0);\n\t\t\t}\n\t\t} else {\n\t\t\tconst Sci::Position partition = starts.PartitionFromPosition(position);\n\t\t\tconst bool atPartitionStart = position == starts.PositionFromPartition(partition);\n\t\t\tconst Sci::Position partitionDelete = partition + (atPartitionStart ? 0 : 1);\n\t\t\tassert(partitionDelete > 0);\n\t\t\tfor (;;) {\n\t\t\t\tconst Sci::Position positionAtIndex = starts.PositionFromPartition(partitionDelete);\n\t\t\t\tassert(position <= positionAtIndex);\n\t\t\t\tif (positionAtIndex >= positionEnd) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tassert(partitionDelete <= Elements());\n\t\t\t\tstarts.RemovePartition(partitionDelete);\n\t\t\t\tvalues.Delete(partitionDelete);\n\t\t\t}\n\t\t\tstarts.InsertText(partition - (atPartitionStart ? 1 : 0), -deleteLength);\n\t\t}\n\t\tCheck();\n\t}\n\tSci::Position PositionNext(Sci::Position start) const noexcept {\n\t\tconst Sci::Position element = ElementFromPosition(start);\n\t\tif (element < Elements()) {\n\t\t\treturn PositionOfElement(element + 1);\n\t\t}\n\t\treturn Length() + 1;\t// Out of bounds to terminate\n\t}\n\tSci::Position IndexAfter(Sci::Position position) const noexcept {\n\t\tassert(position < Length());\n\t\tif (position < 0)\n\t\t\treturn 0;\n\t\tconst Sci::Position partition = starts.PartitionFromPosition(position);\n\t\treturn partition + 1;\n\t}\n\tvoid Check() const {\n#ifdef CHECK_CORRECTNESS\n\t\tstarts.Check();\n\t\tif (starts.Partitions() != values.Length() - 1) {\n\t\t\tthrow std::runtime_error(\"SparseVector: Partitions and values different lengths.\");\n\t\t}\n#endif\n\t}\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/src/SplitVector.h",
    "content": "// Scintilla source code edit control\n/** @file SplitVector.h\n ** Main data structure for holding arrays that handle insertions\n ** and deletions efficiently.\n **/\n// Copyright 1998-2007 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#ifndef SPLITVECTOR_H\n#define SPLITVECTOR_H\n\nnamespace Scintilla::Internal {\n\ntemplate <typename T>\nclass SplitVector {\nprotected:\n\tstd::vector<T> body;\n\tT empty;\t/// Returned as the result of out-of-bounds access.\n\tptrdiff_t lengthBody;\n\tptrdiff_t part1Length;\n\tptrdiff_t gapLength;\t/// invariant: gapLength == body.size() - lengthBody\n\tsize_t growSize;\n\n\t/// Move the gap to a particular position so that insertion and\n\t/// deletion at that point will not require much copying and\n\t/// hence be fast.\n\tvoid GapTo(ptrdiff_t position) noexcept {\n\t\tif (position != part1Length) {\n\t\t\ttry {\n\t\t\t\tif (gapLength > 0) {\t// If gap to move\n\t\t\t\t\t// This can never fail but std::move and std::move_backward are not noexcept.\n\t\t\t\t\tif (position < part1Length) {\n\t\t\t\t\t\t// Moving the gap towards start so moving elements towards end\n\t\t\t\t\t\tstd::move_backward(\n\t\t\t\t\t\t\tbody.data() + position,\n\t\t\t\t\t\t\tbody.data() + part1Length,\n\t\t\t\t\t\t\tbody.data() + gapLength + part1Length);\n\t\t\t\t\t} else {\t// position > part1Length\n\t\t\t\t\t\t// Moving the gap towards end so moving elements towards start\n\t\t\t\t\t\tstd::move(\n\t\t\t\t\t\t\tbody.data() + part1Length + gapLength,\n\t\t\t\t\t\t\tbody.data() + gapLength + position,\n\t\t\t\t\t\t\tbody.data() + part1Length);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tpart1Length = position;\n\t\t\t} catch (...) {\n\t\t\t\t// Ignore any exception\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Check that there is room in the buffer for an insertion,\n\t/// reallocating if more space needed.\n\tvoid RoomFor(ptrdiff_t insertionLength) {\n\t\tif (gapLength < insertionLength) {\n\t\t\twhile (growSize < body.size() / 6)\n\t\t\t\tgrowSize *= 2;\n\t\t\tReAllocate(body.size() + insertionLength + growSize);\n\t\t}\n\t}\n\n\tvoid Init() {\n\t\tbody.clear();\n\t\tbody.shrink_to_fit();\n\t\tlengthBody = 0;\n\t\tpart1Length = 0;\n\t\tgapLength = 0;\n\t\tgrowSize = 8;\n\t}\n\npublic:\n\t/// Construct a split buffer.\n\tSplitVector(size_t growSize_=8) : empty(), lengthBody(0), part1Length(0), gapLength(0), growSize(growSize_) {\n\t}\n\n\tsize_t GetGrowSize() const noexcept {\n\t\treturn growSize;\n\t}\n\n\tvoid SetGrowSize(size_t growSize_) noexcept {\n\t\tgrowSize = growSize_;\n\t}\n\n\t/// Reallocate the storage for the buffer to be newSize and\n\t/// copy existing contents to the new buffer.\n\t/// Must not be used to decrease the size of the buffer.\n\tvoid ReAllocate(size_t newSize) {\n\t\tif (newSize > body.size()) {\n\t\t\t// Move the gap to the end\n\t\t\tGapTo(lengthBody);\n\t\t\tgapLength += newSize - body.size();\n\t\t\t// RoomFor implements a growth strategy but so does vector::resize so\n\t\t\t// ensure vector::resize allocates exactly the amount wanted by\n\t\t\t// calling reserve first.\n\t\t\tbody.reserve(newSize);\n\t\t\tbody.resize(newSize);\n\t\t}\n\t}\n\n\t/// Retrieve the element at a particular position.\n\t/// Retrieving positions outside the range of the buffer returns empty or 0.\n\tconst T& ValueAt(ptrdiff_t position) const noexcept {\n\t\tif (position < part1Length) {\n\t\t\tif (position < 0) {\n\t\t\t\treturn empty;\n\t\t\t} else {\n\t\t\t\treturn body[position];\n\t\t\t}\n\t\t} else {\n\t\t\tif (position >= lengthBody) {\n\t\t\t\treturn empty;\n\t\t\t} else {\n\t\t\t\treturn body[gapLength + position];\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Set the element at a particular position.\n\t/// Setting positions outside the range of the buffer performs no assignment\n\t/// but asserts in debug builds.\n\ttemplate <typename ParamType>\n\tvoid SetValueAt(ptrdiff_t position, ParamType&& v) noexcept {\n\t\tif (position < part1Length) {\n\t\t\tPLATFORM_ASSERT(position >= 0);\n\t\t\tif (position < 0) {\n\t\t\t\t;\n\t\t\t} else {\n\t\t\t\tbody[position] = std::forward<ParamType>(v);\n\t\t\t}\n\t\t} else {\n\t\t\tPLATFORM_ASSERT(position < lengthBody);\n\t\t\tif (position >= lengthBody) {\n\t\t\t\t;\n\t\t\t} else {\n\t\t\t\tbody[gapLength + position] = std::forward<ParamType>(v);\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Retrieve the element at a particular position.\n\t/// The position must be within bounds or an assertion is triggered.\n\tconst T &operator[](ptrdiff_t position) const noexcept {\n\t\tPLATFORM_ASSERT(position >= 0 && position < lengthBody);\n\t\tif (position < part1Length) {\n\t\t\treturn body[position];\n\t\t} else {\n\t\t\treturn body[gapLength + position];\n\t\t}\n\t}\n\n\t/// Retrieve reference to the element at a particular position.\n\t/// This, instead of the const variant, can be used to mutate in-place.\n\t/// The position must be within bounds or an assertion is triggered.\n\tT &operator[](ptrdiff_t position) noexcept {\n\t\tPLATFORM_ASSERT(position >= 0 && position < lengthBody);\n\t\tif (position < part1Length) {\n\t\t\treturn body[position];\n\t\t} else {\n\t\t\treturn body[gapLength + position];\n\t\t}\n\t}\n\n\t/// Retrieve the length of the buffer.\n\tptrdiff_t Length() const noexcept {\n\t\treturn lengthBody;\n\t}\n\n\t/// Insert a single value into the buffer.\n\t/// Inserting at positions outside the current range fails.\n\tvoid Insert(ptrdiff_t position, T v) {\n\t\tPLATFORM_ASSERT((position >= 0) && (position <= lengthBody));\n\t\tif ((position < 0) || (position > lengthBody)) {\n\t\t\treturn;\n\t\t}\n\t\tRoomFor(1);\n\t\tGapTo(position);\n\t\tbody[part1Length] = std::move(v);\n\t\tlengthBody++;\n\t\tpart1Length++;\n\t\tgapLength--;\n\t}\n\n\t/// Insert a number of elements into the buffer setting their value.\n\t/// Inserting at positions outside the current range fails.\n\tvoid InsertValue(ptrdiff_t position, ptrdiff_t insertLength, T v) {\n\t\tPLATFORM_ASSERT((position >= 0) && (position <= lengthBody));\n\t\tif (insertLength > 0) {\n\t\t\tif ((position < 0) || (position > lengthBody)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tRoomFor(insertLength);\n\t\t\tGapTo(position);\n\t\t\tstd::fill_n(body.data() + part1Length, insertLength, v);\n\t\t\tlengthBody += insertLength;\n\t\t\tpart1Length += insertLength;\n\t\t\tgapLength -= insertLength;\n\t\t}\n\t}\n\n\t/// Add some new empty elements.\n\t/// InsertValue is good for value objects but not for unique_ptr objects\n\t/// since they can only be moved from once.\n\t/// Callers can write to the returned pointer to transform inputs without copies.\n\tT *InsertEmpty(ptrdiff_t position, ptrdiff_t insertLength) {\n\t\tPLATFORM_ASSERT((position >= 0) && (position <= lengthBody));\n\t\tif (insertLength > 0) {\n\t\t\tif ((position < 0) || (position > lengthBody)) {\n\t\t\t\treturn nullptr;\n\t\t\t}\n\t\t\tRoomFor(insertLength);\n\t\t\tGapTo(position);\n\t\t\tT *ptr = body.data() + part1Length;\n\t\t\tfor (ptrdiff_t elem = 0; elem < insertLength; elem++, ptr++) {\n\t\t\t\tT emptyOne = {};\n\t\t\t\t*ptr = std::move(emptyOne);\n\t\t\t}\n\t\t\tlengthBody += insertLength;\n\t\t\tpart1Length += insertLength;\n\t\t\tgapLength -= insertLength;\n\t\t}\n\t\treturn body.data() + position;\n\t}\n\n\t/// Ensure at least length elements allocated,\n\t/// appending zero valued elements if needed.\n\tvoid EnsureLength(ptrdiff_t wantedLength) {\n\t\tif (Length() < wantedLength) {\n\t\t\tInsertEmpty(Length(), wantedLength - Length());\n\t\t}\n\t}\n\n\t/// Insert text into the buffer from an array.\n\tvoid InsertFromArray(ptrdiff_t positionToInsert, const T s[], ptrdiff_t positionFrom, ptrdiff_t insertLength) {\n\t\tPLATFORM_ASSERT((positionToInsert >= 0) && (positionToInsert <= lengthBody));\n\t\tif (insertLength > 0) {\n\t\t\tif ((positionToInsert < 0) || (positionToInsert > lengthBody)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tRoomFor(insertLength);\n\t\t\tGapTo(positionToInsert);\n\t\t\tstd::copy_n(s + positionFrom, insertLength, body.data() + part1Length);\n\t\t\tlengthBody += insertLength;\n\t\t\tpart1Length += insertLength;\n\t\t\tgapLength -= insertLength;\n\t\t}\n\t}\n\n\t/// Delete one element from the buffer.\n\tvoid Delete(ptrdiff_t position) {\n\t\tPLATFORM_ASSERT((position >= 0) && (position < lengthBody));\n\t\tDeleteRange(position, 1);\n\t}\n\n\t/// Delete a range from the buffer.\n\t/// Deleting positions outside the current range fails.\n\t/// Cannot be noexcept as vector::shrink_to_fit may be called and it may throw.\n\tvoid DeleteRange(ptrdiff_t position, ptrdiff_t deleteLength) {\n\t\tPLATFORM_ASSERT((position >= 0) && (position + deleteLength <= lengthBody));\n\t\tif ((position < 0) || ((position + deleteLength) > lengthBody)) {\n\t\t\treturn;\n\t\t}\n\t\tif ((position == 0) && (deleteLength == lengthBody)) {\n\t\t\t// Full deallocation returns storage and is faster\n\t\t\tInit();\n\t\t} else if (deleteLength > 0) {\n\t\t\tGapTo(position);\n\t\t\tlengthBody -= deleteLength;\n\t\t\tgapLength += deleteLength;\n\t\t}\n\t}\n\n\t/// Delete all the buffer contents.\n\tvoid DeleteAll() {\n\t\tDeleteRange(0, lengthBody);\n\t}\n\n\t/// Retrieve a range of elements into an array\n\tvoid GetRange(T *buffer, ptrdiff_t position, ptrdiff_t retrieveLength) const {\n\t\t// Split into up to 2 ranges, before and after the split then use memcpy on each.\n\t\tptrdiff_t range1Length = 0;\n\t\tif (position < part1Length) {\n\t\t\tconst ptrdiff_t part1AfterPosition = part1Length - position;\n\t\t\trange1Length = retrieveLength;\n\t\t\tif (range1Length > part1AfterPosition)\n\t\t\t\trange1Length = part1AfterPosition;\n\t\t}\n\t\tstd::copy_n(body.data() + position, range1Length, buffer);\n\t\tbuffer += range1Length;\n\t\tposition = position + range1Length + gapLength;\n\t\tconst ptrdiff_t range2Length = retrieveLength - range1Length;\n\t\tstd::copy_n(body.data() + position, range2Length, buffer);\n\t}\n\n\t/// Compact the buffer and return a pointer to the first element.\n\t/// Also ensures there is an empty element beyond logical end in case its\n\t/// passed to a function expecting a NUL terminated string.\n\tT *BufferPointer() {\n\t\tRoomFor(1);\n\t\tGapTo(lengthBody);\n\t\tT emptyOne = {};\n\t\tbody[lengthBody] = std::move(emptyOne);\n\t\treturn body.data();\n\t}\n\n\t/// Return a pointer to a range of elements, first rearranging the buffer if\n\t/// needed to make that range contiguous.\n\tT *RangePointer(ptrdiff_t position, ptrdiff_t rangeLength) noexcept {\n\t\tif (position < part1Length) {\n\t\t\tif ((position + rangeLength) > part1Length) {\n\t\t\t\t// Range overlaps gap, so move gap to start of range.\n\t\t\t\tGapTo(position);\n\t\t\t\treturn body.data() + position + gapLength;\n\t\t\t} else {\n\t\t\t\treturn body.data() + position;\n\t\t\t}\n\t\t} else {\n\t\t\treturn body.data() + position + gapLength;\n\t\t}\n\t}\n\n\t/// Return a pointer to a single element.\n\t/// Does not rearrange the buffer.\n\tconst T *ElementPointer(ptrdiff_t position) const noexcept {\n\t\tif (position < part1Length) {\n\t\t\treturn body.data() + position;\n\t\t} else {\n\t\t\treturn body.data() + position + gapLength;\n\t\t}\n\t}\n\n\t/// Return the position of the gap within the buffer.\n\tptrdiff_t GapPosition() const noexcept {\n\t\treturn part1Length;\n\t}\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/src/Style.cxx",
    "content": "// Scintilla source code edit control\n/** @file Style.cxx\n ** Defines the font and colour style for a class of text.\n **/\n// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#include <cstdint>\n\n#include <stdexcept>\n#include <string_view>\n#include <vector>\n#include <optional>\n#include <memory>\n\n#include \"ScintillaTypes.h\"\n\n#include \"Debugging.h\"\n#include \"Geometry.h\"\n#include \"Platform.h\"\n\n#include \"Style.h\"\n\nusing namespace Scintilla;\nusing namespace Scintilla::Internal;\n\nbool FontSpecification::operator==(const FontSpecification &other) const noexcept {\n\treturn fontName == other.fontName &&\n\t       weight == other.weight &&\n\t       italic == other.italic &&\n\t       size == other.size &&\n\t       stretch == other.stretch &&\n\t       characterSet == other.characterSet &&\n\t       extraFontFlag == other.extraFontFlag &&\n\t       checkMonospaced == other.checkMonospaced;\n}\n\nbool FontSpecification::operator<(const FontSpecification &other) const noexcept {\n\tif (fontName != other.fontName)\n\t\treturn fontName < other.fontName;\n\tif (weight != other.weight)\n\t\treturn weight < other.weight;\n\tif (italic != other.italic)\n\t\treturn !italic;\n\tif (size != other.size)\n\t\treturn size < other.size;\n\tif (stretch != other.stretch)\n\t\treturn stretch < other.stretch;\n\tif (characterSet != other.characterSet)\n\t\treturn characterSet < other.characterSet;\n\tif (extraFontFlag != other.extraFontFlag)\n\t\treturn extraFontFlag < other.extraFontFlag;\n\tif (checkMonospaced != other.checkMonospaced)\n\t\treturn checkMonospaced < other.checkMonospaced;\n\treturn false;\n}\n\nnamespace {\n\n// noexcept Platform::DefaultFontSize\nint DefaultFontSize() noexcept {\n\ttry {\n\t\treturn Platform::DefaultFontSize();\n\t} catch (...) {\n\t\treturn 10;\n\t}\n}\n\n}\n\nStyle::Style(const char *fontName_) noexcept :\n\tFontSpecification(fontName_, DefaultFontSize() * FontSizeMultiplier),\n\tfore(black),\n\tback(white),\n\teolFilled(false),\n\tunderline(false),\n\tcaseForce(CaseForce::mixed),\n\tvisible(true),\n\tchangeable(true),\n\thotspot(false),\n\tinvisibleRepresentation{} {\n}\n\nvoid Style::Copy(std::shared_ptr<Font> font_, const FontMeasurements &fm_) noexcept {\n\tfont = std::move(font_);\n\t(FontMeasurements &)(*this) = fm_;\n}\n"
  },
  {
    "path": "Libraries/scintilla/src/Style.h",
    "content": "// Scintilla source code edit control\n/** @file Style.h\n ** Defines the font and colour style for a class of text.\n **/\n// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#ifndef STYLE_H\n#define STYLE_H\n\nnamespace Scintilla::Internal {\n\nstruct FontSpecification {\n\t// fontName is allocated by a ViewStyle container object and may be null\n\tconst char *fontName;\n\tint size;\n\tScintilla::FontWeight weight = Scintilla::FontWeight::Normal;\n\tScintilla::FontStretch stretch = Scintilla::FontStretch::Normal;\n\tbool italic = false;\n\tScintilla::CharacterSet characterSet = Scintilla::CharacterSet::Default;\n\tScintilla::FontQuality extraFontFlag = Scintilla::FontQuality::QualityDefault;\n\tbool checkMonospaced = false;\n\n\tconstexpr FontSpecification(const char *fontName_=nullptr, int size_=10*Scintilla::FontSizeMultiplier) noexcept :\n\t\tfontName(fontName_), size(size_) {\n\t}\n\tbool operator==(const FontSpecification &other) const noexcept;\n\tbool operator<(const FontSpecification &other) const noexcept;\n};\n\nstruct FontMeasurements {\n\tXYPOSITION ascent = 1;\n\tXYPOSITION descent = 1;\n\tXYPOSITION capitalHeight = 1;\t// Top of capital letter to baseline: ascent - internal leading\n\tXYPOSITION aveCharWidth = 1;\n\tXYPOSITION monospaceCharacterWidth = 1;\n\tXYPOSITION spaceWidth = 1;\n\tbool monospaceASCII = false;\n\tint sizeZoomed = 2;\n};\n\n/**\n */\nclass Style : public FontSpecification, public FontMeasurements {\npublic:\n\tColourRGBA fore;\n\tColourRGBA back;\n\tbool eolFilled;\n\tbool underline;\n\tenum class CaseForce {mixed, upper, lower, camel};\n\tCaseForce caseForce;\n\tbool visible;\n\tbool changeable;\n\tbool hotspot;\n\tchar invisibleRepresentation[5];\n\n\tstd::shared_ptr<Font> font;\n\n\tStyle(const char *fontName_=nullptr) noexcept;\n\tvoid Copy(std::shared_ptr<Font> font_, const FontMeasurements &fm_) noexcept;\n\tbool IsProtected() const noexcept { return !(changeable && visible);}\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/src/UndoHistory.cxx",
    "content": "// Scintilla source code edit control\n/** @file UndoHistory.cxx\n ** Manages undo for the document.\n **/\n// Copyright 1998-2024 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#include <cstddef>\n#include <cstdlib>\n#include <cstdint>\n#include <cassert>\n#include <cstring>\n#include <cstdio>\n#include <cstdarg>\n#include <climits>\n\n#include <stdexcept>\n#include <string>\n#include <string_view>\n#include <vector>\n#include <optional>\n#include <algorithm>\n#include <memory>\n\n#include \"ScintillaTypes.h\"\n\n#include \"Debugging.h\"\n\n#include \"Position.h\"\n#include \"SplitVector.h\"\n#include \"Partitioning.h\"\n#include \"RunStyles.h\"\n#include \"SparseVector.h\"\n#include \"ChangeHistory.h\"\n#include \"CellBuffer.h\"\n#include \"UndoHistory.h\"\n\nnamespace Scintilla::Internal {\n\ntemplate <typename T>\nvoid VectorTruncate(std::vector<T> &v, size_t length) noexcept {\n\tv.erase(v.begin() + length, v.end());\n}\n\nconstexpr size_t byteMask = UINT8_MAX;\nconstexpr size_t byteBits = 8;\n\nsize_t ReadValue(const uint8_t *bytes, size_t length) noexcept {\n\tsize_t value = 0;\n\tfor (size_t i = 0; i < length; i++) {\n\t\tvalue = (value << byteBits) + bytes[i];\n\t}\n\treturn value;\n}\n\nvoid WriteValue(uint8_t *bytes, size_t length, size_t value) noexcept {\n\twhile (length != 0) {\n\t\t--length;\n\t\tbytes[length] = value & byteMask;\n\t\tvalue = value >> byteBits;\n\t}\n}\n\nsize_t ScaledVector::Size() const noexcept {\n\treturn bytes.size() / element.size;\n}\n\nsize_t ScaledVector::ValueAt(size_t index) const noexcept {\n\treturn ReadValue(bytes.data() + index * element.size, element.size);\n}\n\nintptr_t ScaledVector::SignedValueAt(size_t index) const noexcept {\n\treturn ReadValue(bytes.data() + index * element.size, element.size);\n}\n\nconstexpr SizeMax ElementForValue(size_t value) noexcept {\n\tsize_t maxN = byteMask;\n\tsize_t i = 1;\n\twhile (value > byteMask) {\n\t\ti++;\n\t\tvalue >>= byteBits;\n\t\tmaxN = (maxN << byteBits) + byteMask;\n\t}\n\treturn { i, maxN };\n}\n\nvoid ScaledVector::SetValueAt(size_t index, size_t value) {\n\t// Check if value fits, if not then expand\n\tif (value > element.maxValue) {\n\t\tconst SizeMax elementForValue = ElementForValue(value);\n\t\tconst size_t length = bytes.size() / element.size;\n\t\tstd::vector<uint8_t> bytesNew(elementForValue.size * length);\n\t\tfor (size_t i = 0; i < length; i++) {\n\t\t\tconst uint8_t *source = bytes.data() + i * element.size;\n\t\t\tuint8_t *destination = bytesNew.data() + (i+1) * elementForValue.size - element.size;\n\t\t\tmemcpy(destination, source, element.size);\n\t\t}\n\t\tstd::swap(bytes, bytesNew);\n\t\telement = elementForValue;\n\t}\n\tWriteValue(bytes.data() + index * element.size, element.size, value);\n}\n\nvoid ScaledVector::ClearValueAt(size_t index) noexcept {\n\t// 0 fits in any size element so no expansion needed so no exceptions\n\tWriteValue(bytes.data() + index * element.size, element.size, 0);\n}\n\nvoid ScaledVector::Clear() noexcept {\n\tbytes.clear();\n}\n\nvoid ScaledVector::Truncate(size_t length) noexcept {\n\tVectorTruncate(bytes, length * element.size);\n\tassert(bytes.size() == length * element.size);\n}\n\nvoid ScaledVector::ReSize(size_t length) {\n\tbytes.resize(length * element.size);\n}\n\nvoid ScaledVector::PushBack() {\n\tbytes.resize(bytes.size() + element.size);\n}\n\nsize_t ScaledVector::SizeInBytes() const noexcept {\n\treturn bytes.size();\n}\n\nUndoActionType::UndoActionType() noexcept : at(ActionType::insert), mayCoalesce(false) {\n}\n\nUndoActions::UndoActions() noexcept = default;\n\nvoid UndoActions::Truncate(size_t length) noexcept {\n\tVectorTruncate(types, length);\n\tassert(types.size() == length);\n\tpositions.Truncate(length);\n\tlengths.Truncate(length);\n}\n\nvoid UndoActions::PushBack() {\n\ttypes.emplace_back();\n\tpositions.PushBack();\n\tlengths.PushBack();\n}\n\nvoid UndoActions::Clear() noexcept {\n\ttypes.clear();\n\tpositions.Clear();\n\tlengths.Clear();\n}\n\nintptr_t UndoActions::SSize() const noexcept {\n\treturn types.size();\n}\n\nvoid UndoActions::Create(size_t index, ActionType at_, Sci::Position position_, Sci::Position lenData_, bool mayCoalesce_) {\n\ttypes[index].at = at_;\n\ttypes[index].mayCoalesce = mayCoalesce_;\n\tpositions.SetValueAt(index, position_);\n\tlengths.SetValueAt(index, lenData_);\n}\n\nbool UndoActions::AtStart(size_t index) const noexcept {\n\tif (index == 0) {\n\t\treturn true;\n\t}\n\treturn !types[index-1].mayCoalesce;\n}\n\nsize_t UndoActions::LengthTo(size_t index) const noexcept {\n\tsize_t sum = 0;\n\tfor (size_t act = 0; act < index; act++) {\n\t\tsum += lengths.ValueAt(act);\n\t}\n\treturn sum;\n}\n\nSci::Position UndoActions::Position(int action) const noexcept {\n\treturn positions.SignedValueAt(action);\n}\nSci::Position UndoActions::Length(int action) const noexcept {\n\treturn lengths.SignedValueAt(action);\n}\n\nvoid ScrapStack::Clear() noexcept {\n\tstack.clear();\n\tcurrent = 0;\n}\n\nconst char *ScrapStack::Push(const char *text, size_t length) {\n\tif (current < stack.length()) {\n\t\tstack.resize(current);\n\t}\n\tstack.append(text, length);\n\tcurrent = stack.length();\n\treturn stack.data() + current - length;\n}\n\nvoid ScrapStack::SetCurrent(size_t position) noexcept {\n\tcurrent = position;\n}\n\nvoid ScrapStack::MoveForward(size_t length) noexcept {\n\tif ((current + length) <= stack.length()) {\n\t\tcurrent += length;\n\t}\n}\n\nvoid ScrapStack::MoveBack(size_t length) noexcept {\n\tif (current >= length) {\n\t\tcurrent -= length;\n\t}\n}\n\nconst char *ScrapStack::CurrentText() const noexcept {\n\treturn stack.data() + current;\n}\n\nconst char *ScrapStack::TextAt(size_t position) const noexcept {\n\treturn stack.data() + position;\n}\n\n// The undo history stores a sequence of user operations that represent the user's view of the\n// commands executed on the text.\n// Each user operation contains a sequence of text insertion and text deletion actions.\n// All the user operations are stored in a list of individual actions.\n// A false 'mayCoalesce' flag acts as an end to a user operation.\n// Initially there are no actions in the history.\n// As each action is performed, it is recorded in the history. The action may either become\n// part of the current user operation or may start a new user operation. If it is to be part of the\n// current operation, then 'mayCoalesce' is true. If it is to be part of a new operation, the\n// 'mayCoalesce' flag of the previous action is set false.\n// The decision of whether to start a new user operation is based upon two factors. If a\n// compound operation has been explicitly started by calling BeginUndoAction and no matching\n// EndUndoAction (these calls nest) has been called, then the action is coalesced into the current\n// operation. If there is no outstanding BeginUndoAction call then a new operation is started\n// unless it looks as if the new action is caused by the user typing or deleting a stream of text.\n// Sequences that look like typing or deletion are coalesced into a single user operation.\n\nint UndoHistory::PreviousAction() const noexcept {\n\treturn currentAction - 1;\n}\n\nUndoHistory::UndoHistory() {\n\tscraps = std::make_unique<ScrapStack>();\n}\n\nUndoHistory::~UndoHistory() noexcept = default;\n\nconst char *UndoHistory::AppendAction(ActionType at, Sci::Position position, const char *data, Sci::Position lengthData,\n\tbool &startSequence, bool mayCoalesce) {\n\t//Platform::DebugPrintf(\"%% %d action %d %d %d\\n\", at, position, lengthData, currentAction);\n\t//Platform::DebugPrintf(\"^ %d action %d %d\\n\", actions[currentAction - 1].at,\n\t//\tactions[currentAction - 1].position, actions[currentAction - 1].lenData);\n\tif (currentAction < savePoint) {\n\t\tsavePoint = -1;\n\t\tif (!detach) {\n\t\t\tdetach = currentAction;\n\t\t}\n\t} else if (detach && (*detach > currentAction)) {\n\t\tdetach = currentAction;\n\t}\n\tif (undoSequenceDepth > 0) {\n\t\t// Actions not at top level are always coalesced unless this is after return to top level\n\t\tmayCoalesce = true;\n\t}\n\tbool coalesce = true;\n\tif (currentAction >= 1) {\n\t\tint targetAct = currentAction - 1;\n\t\tif (0 == undoSequenceDepth) {\n\t\t\t// Top level actions may not always be coalesced\n\t\t\t// Container actions may forward the coalesce state of Scintilla Actions.\n\t\t\twhile ((targetAct > 0) && (actions.types[targetAct].at == ActionType::container) && actions.types[targetAct].mayCoalesce) {\n\t\t\t\ttargetAct--;\n\t\t\t}\n\t\t\t// See if current action can be coalesced into previous action\n\t\t\t// Will work if both are inserts or deletes and position is same\n\t\t\tif ((currentAction == savePoint) || (currentAction == tentativePoint)) {\n\t\t\t\tcoalesce = false;\n\t\t\t} else if (!mayCoalesce || !actions.types[targetAct].mayCoalesce) {\n\t\t\t\tcoalesce = false;\n\t\t\t} else if (at == ActionType::container || actions.types[targetAct].at == ActionType::container) {\n\t\t\t\t;\t// A coalescible containerAction\n\t\t\t} else if ((at != actions.types[targetAct].at)) { // } && (!actions.AtStart(targetAct))) {\n\t\t\t\tcoalesce = false;\n\t\t\t} else if ((at == ActionType::insert) &&\n\t\t\t           (position != (actions.Position(targetAct) + actions.Length(targetAct)))) {\n\t\t\t\t// Insertions must be immediately after to coalesce\n\t\t\t\tcoalesce = false;\n\t\t\t} else if (at == ActionType::remove) {\n\t\t\t\tif ((lengthData == 1) || (lengthData == 2)) {\n\t\t\t\t\tif ((position + lengthData) == actions.Position(targetAct)) {\n\t\t\t\t\t\t; // Backspace -> OK\n\t\t\t\t\t} else if (position == actions.Position(targetAct)) {\n\t\t\t\t\t\t; // Delete -> OK\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Removals must be at same position to coalesce\n\t\t\t\t\t\tcoalesce = false;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// Removals must be of one character to coalesce\n\t\t\t\t\tcoalesce = false;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Action coalesced.\n\t\t\t}\n\n\t\t} else {\n\t\t\t// Actions not at top level are always coalesced unless this is after return to top level\n\t\t\tif (!actions.types[targetAct].mayCoalesce)\n\t\t\t\tcoalesce = false;\n\t\t}\n\t} else {\n\t\tcoalesce = false;\n\t}\n\tstartSequence = !coalesce;\n\t// Maybe close previous action\n\tif ((currentAction > 0) && startSequence) {\n\t\tactions.types[PreviousAction()].mayCoalesce = false;\n\t}\n\tconst char *dataNew = lengthData ? scraps->Push(data, lengthData) : nullptr;\n\tif (currentAction >= actions.SSize()) {\n\t\tactions.PushBack();\n\t} else {\n\t\tactions.Truncate(currentAction+1);\n\t}\n\tactions.Create(currentAction, at, position, lengthData, mayCoalesce);\n\tcurrentAction++;\n\treturn dataNew;\n}\n\nvoid UndoHistory::BeginUndoAction(bool mayCoalesce) noexcept {\n\tif (undoSequenceDepth == 0) {\n\t\tif (currentAction > 0) {\n\t\t\tactions.types[PreviousAction()].mayCoalesce = mayCoalesce;\n\t\t}\n\t}\n\tundoSequenceDepth++;\n}\n\nvoid UndoHistory::EndUndoAction() noexcept {\n\tPLATFORM_ASSERT(undoSequenceDepth > 0);\n\tundoSequenceDepth--;\n\tif (0 == undoSequenceDepth) {\n\t\tif (currentAction > 0) {\n\t\t\tactions.types[PreviousAction()].mayCoalesce = false;\n\t\t}\n\t}\n}\n\nint UndoHistory::UndoSequenceDepth() const noexcept {\n\treturn undoSequenceDepth;\n}\n\nbool UndoHistory::AfterUndoSequenceStart() const noexcept {\n\tif (currentAction == 0) {\n\t\treturn false;\n\t}\n\t// Count back to last sequence start?\n\treturn !actions.AtStart(currentAction-1);\n}\n\nvoid UndoHistory::DropUndoSequence() noexcept {\n\tundoSequenceDepth = 0;\n}\n\nvoid UndoHistory::DeleteUndoHistory() noexcept {\n\tactions.Clear();\n\tcurrentAction = 0;\n\tsavePoint = 0;\n\ttentativePoint = -1;\n\tscraps->Clear();\n\tmemory = {};\n}\n\nint UndoHistory::Actions() const noexcept {\n\treturn static_cast<int>(actions.SSize());\n}\n\nvoid UndoHistory::SetSavePoint(int action) noexcept {\n\tsavePoint = action;\n}\n\nint UndoHistory::SavePoint() const noexcept {\n\treturn savePoint;\n}\n\nvoid UndoHistory::SetSavePoint() noexcept {\n\tsavePoint = currentAction;\n\tdetach.reset();\n}\n\nbool UndoHistory::IsSavePoint() const noexcept {\n\treturn savePoint == currentAction;\n}\n\nbool UndoHistory::BeforeSavePoint() const noexcept {\n\treturn (savePoint < 0) || (savePoint > currentAction);\n}\n\nbool UndoHistory::PreviousBeforeSavePoint() const noexcept {\n\treturn (savePoint < 0) || (savePoint >= currentAction);\n}\n\nbool UndoHistory::BeforeReachableSavePoint() const noexcept {\n\treturn (savePoint > 0) && (savePoint > currentAction);\n}\n\nbool UndoHistory::AfterSavePoint() const noexcept {\n\treturn (savePoint >= 0) && (savePoint <= currentAction);\n}\n\nvoid UndoHistory::SetDetachPoint(int action) noexcept {\n\tif (action == -1) {\n\t\tdetach = {};\n\t} else {\n\t\tdetach = action;\n\t}\n}\n\nint UndoHistory::DetachPoint() const noexcept {\n\treturn detach.value_or(-1);\n}\n\nbool UndoHistory::AfterDetachPoint() const noexcept {\n\treturn detach && (*detach < currentAction);\n}\n\nbool UndoHistory::AfterOrAtDetachPoint() const noexcept {\n\treturn detach && (*detach <= currentAction);\n}\n\nintptr_t UndoHistory::Delta(int action) const noexcept {\n\tintptr_t sizeChange = 0;\n\tfor (int act = 0; act < action; act++) {\n\t\tconst intptr_t lengthChange = actions.Length(act);\n\t\tsizeChange += (actions.types[act].at == ActionType::insert) ? lengthChange : -lengthChange;\n\t}\n\treturn sizeChange;\n}\n\nbool UndoHistory::Validate(intptr_t lengthDocument) const noexcept {\n\t// Check history for validity\n\tconst intptr_t sizeChange = Delta(currentAction);\n\tif (sizeChange > lengthDocument) {\n\t\t// Current document size too small for changes made in undo history.\n\t\treturn false;\n\t}\n\tconst intptr_t lengthOriginal = lengthDocument - sizeChange;\n\tintptr_t lengthCurrent = lengthOriginal;\n\tfor (int act = 0; act < actions.SSize(); act++) {\n\t\tconst intptr_t lengthChange = actions.Length(act);\n\t\tif (actions.Position(act) > lengthCurrent) {\n\t\t\t// Change outside document.\n\t\t\treturn false;\n\t\t}\n\t\tlengthCurrent += (actions.types[act].at == ActionType::insert) ? lengthChange : -lengthChange;\n\t\tif (lengthCurrent < 0) {\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn true;\n}\n\nvoid UndoHistory::SetCurrent(int action, intptr_t lengthDocument) {\n\t// Find position in scraps for action\n\tmemory = {};\n\tconst size_t lengthSum = actions.LengthTo(action);\n\tscraps->SetCurrent(lengthSum);\n\tcurrentAction = action;\n\tif (!Validate(lengthDocument)) {\n\t\tcurrentAction = 0;\n\t\tDeleteUndoHistory();\n\t\tthrow std::runtime_error(\"UndoHistory::SetCurrent: invalid undo history.\");\n\t}\n}\n\nint UndoHistory::Current() const noexcept {\n\treturn currentAction;\n}\n\nint UndoHistory::Type(int action) const noexcept {\n\tconst int baseType = static_cast<int>(actions.types[action].at);\n\tconst int open = actions.types[action].mayCoalesce ? coalesceFlag : 0;\n\treturn baseType | open;\n}\n\nSci::Position UndoHistory::Position(int action) const noexcept {\n\treturn actions.Position(action);\n}\n\nSci::Position UndoHistory::Length(int action) const noexcept {\n\treturn actions.Length(action);\n}\n\nstd::string_view UndoHistory::Text(int action) noexcept {\n\t// Assumes first call after any changes is for action 0.\n\t// TODO: may need to invalidate memory in other circumstances\n\tif (action == 0) {\n\t\tmemory = {};\n\t}\n\tint act = 0;\n\tsize_t position = 0;\n\tif (memory && memory->act <= action) {\n\t\tact = memory->act;\n\t\tposition = memory->position;\n\t}\n\tfor (; act < action; act++) {\n\t\tposition += actions.Length(act);\n\t}\n\tconst size_t length = actions.Length(action);\n\tconst char *scrap = scraps->TextAt(position);\n\tmemory = {action, position};\n\treturn {scrap, length};\n}\n\nvoid UndoHistory::PushUndoActionType(int type, Sci::Position position) {\n\tactions.PushBack();\n\tactions.Create(actions.SSize()-1, static_cast<ActionType>(type & byteMask),\n\t\tposition, 0, type & coalesceFlag);\n}\n\nvoid UndoHistory::ChangeLastUndoActionText(size_t length, const char *text) {\n\tassert(actions.lengths.ValueAt(actions.SSize()-1) == 0);\n\tactions.lengths.SetValueAt(actions.SSize()-1, length);\n\tscraps->Push(text, length);\n}\n\nvoid UndoHistory::SetTentative(int action) noexcept {\n\ttentativePoint = action;\n}\n\nint UndoHistory::TentativePoint() const noexcept {\n\treturn tentativePoint;\n}\n\nvoid UndoHistory::TentativeStart() noexcept {\n\ttentativePoint = currentAction;\n}\n\nvoid UndoHistory::TentativeCommit() noexcept {\n\ttentativePoint = -1;\n\t// Truncate undo history\n\tactions.Truncate(currentAction);\n}\n\nbool UndoHistory::TentativeActive() const noexcept {\n\treturn tentativePoint >= 0;\n}\n\nint UndoHistory::TentativeSteps() const noexcept {\n\t// Drop any trailing startAction\n\tif (tentativePoint >= 0)\n\t\treturn currentAction - tentativePoint;\n\treturn -1;\n}\n\nbool UndoHistory::CanUndo() const noexcept {\n\treturn (currentAction > 0) && (actions.SSize() != 0);\n}\n\nint UndoHistory::StartUndo() const noexcept {\n\tassert(currentAction >= 0);\n\n\t// Count the steps in this action\n\tif (currentAction == 0) {\n\t\treturn 0;\n\t}\n\n\tint act = currentAction - 1;\n\n\twhile (act > 0 && !actions.AtStart(act)) {\n\t\tact--;\n\t}\n\treturn currentAction - act;\n}\n\nAction UndoHistory::GetUndoStep() const noexcept {\n\tconst int previousAction = PreviousAction();\n\tAction acta {\n\t\tactions.types[previousAction].at,\n\t\tactions.types[previousAction].mayCoalesce,\n\t\tactions.Position(previousAction),\n\t\tnullptr,\n\t\tactions.Length(previousAction)\n\t};\n\tif (acta.lenData) {\n\t\tacta.data = scraps->CurrentText() - acta.lenData;\n\t}\n\treturn acta;\n}\n\nvoid UndoHistory::CompletedUndoStep() noexcept {\n\tscraps->MoveBack(actions.Length(PreviousAction()));\n\tcurrentAction--;\n}\n\nbool UndoHistory::CanRedo() const noexcept {\n\treturn actions.SSize() > currentAction;\n}\n\nint UndoHistory::StartRedo() const noexcept {\n\t// Count the steps in this action\n\n\tif (currentAction >= actions.SSize()) {\n\t\t// Already at end so can't redo\n\t\treturn 0;\n\t}\n\n\t// Slightly unusual logic handles case where last action still has mayCoalesce.\n\t// Could set mayCoalesce of last action to false in StartUndo but this state is\n\t// visible to applications so should not be changed.\n\tconst int maxAction = Actions() - 1;\n\tint act = currentAction;\n\twhile (act <= maxAction && actions.types[act].mayCoalesce) {\n\t\tact++;\n\t}\n\tact = std::min(act, maxAction);\n\treturn act - currentAction + 1;\n}\n\nAction UndoHistory::GetRedoStep() const noexcept {\n\tAction acta{\n\t\tactions.types[currentAction].at,\n\t\tactions.types[currentAction].mayCoalesce,\n\t\tactions.Position(currentAction),\n\t\tnullptr,\n\t\tactions.Length(currentAction)\n\t};\n\tif (acta.lenData) {\n\t\tacta.data = scraps->CurrentText();\n\t}\n\treturn acta;\n}\n\nvoid UndoHistory::CompletedRedoStep() noexcept {\n\tscraps->MoveForward(actions.Length(currentAction));\n\tcurrentAction++;\n}\n\n}\n"
  },
  {
    "path": "Libraries/scintilla/src/UndoHistory.h",
    "content": "// Scintilla source code edit control\n/** @file UndoHistory.h\n ** Manages undo for the document.\n **/\n// Copyright 1998-2024 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#ifndef UNDOHISTORY_H\n#define UNDOHISTORY_H\n\nnamespace Scintilla::Internal {\n\n// ScaledVector is a vector of unsigned integers that uses elements sized to hold the largest value.\n// Thus, if an undo history only contains short insertions and deletions the lengths vector may\n// only use 2 bytes or even 1 byte for each length.\n// This saves much memory often reducing by 50% for 32-bit builds and 75% for 64-bit builds.\n\nstruct SizeMax {\n\tsize_t size = 1;\n\tsize_t maxValue = UINT8_MAX;\n};\n\nclass ScaledVector {\n\tSizeMax element;\n\tstd::vector<uint8_t> bytes;\npublic:\n\t[[nodiscard]] size_t Size() const noexcept;\n\t[[nodiscard]] size_t ValueAt(size_t index) const noexcept;\n\t[[nodiscard]] intptr_t SignedValueAt(size_t index) const noexcept;\n\tvoid SetValueAt(size_t index, size_t value);\n\tvoid ClearValueAt(size_t index) noexcept;\n\tvoid Clear() noexcept;\n\tvoid Truncate(size_t length) noexcept;\n\tvoid ReSize(size_t length);\n\tvoid PushBack();\n\n\t// For testing\n\t[[nodiscard]] size_t SizeInBytes() const noexcept;\n};\n\nclass UndoActionType {\npublic:\n\tActionType at : 4;\n\tbool mayCoalesce : 1;\n\tUndoActionType() noexcept;\n};\n\nstruct UndoActions {\n\tstd::vector<UndoActionType> types;\n\tScaledVector positions;\n\tScaledVector lengths;\n\n\tUndoActions() noexcept;\n\tvoid Truncate(size_t length) noexcept;\n\tvoid PushBack();\n\tvoid Clear() noexcept;\n\t[[nodiscard]] intptr_t SSize() const noexcept;\n\tvoid Create(size_t index, ActionType at_, Sci::Position position_, Sci::Position lenData_, bool mayCoalesce_);\n\t[[nodiscard]] bool AtStart(size_t index) const noexcept;\n\t[[nodiscard]] size_t LengthTo(size_t index) const noexcept;\n\t[[nodiscard]] Sci::Position Position(int action) const noexcept;\n\t[[nodiscard]] Sci::Position Length(int action) const noexcept;\n};\n\nclass ScrapStack {\n\tstd::string stack;\n\tsize_t current = 0;\npublic:\n\tvoid Clear() noexcept;\n\tconst char *Push(const char *text, size_t length);\n\tvoid SetCurrent(size_t position) noexcept;\n\tvoid MoveForward(size_t length) noexcept;\n\tvoid MoveBack(size_t length) noexcept;\n\t[[nodiscard]] const char *CurrentText() const noexcept;\n\t[[nodiscard]] const char *TextAt(size_t position) const noexcept;\n};\n\nconstexpr int coalesceFlag = 0x100;\n\n/**\n *\n */\nclass UndoHistory {\n\tUndoActions actions;\n\tint currentAction = 0;\n\tint undoSequenceDepth = 0;\n\tint savePoint = 0;\n\tint tentativePoint = -1;\n\tstd::optional<int> detach;\t// Never set if savePoint set (>= 0)\n\tstd::unique_ptr<ScrapStack> scraps;\n\tstruct actPos { int act; size_t position; };\n\tstd::optional<actPos> memory;\n\n\tint PreviousAction() const noexcept;\n\npublic:\n\tUndoHistory();\n\t~UndoHistory() noexcept;\n\n\tconst char *AppendAction(ActionType at, Sci::Position position, const char *data, Sci::Position lengthData, bool &startSequence, bool mayCoalesce=true);\n\n\tvoid BeginUndoAction(bool mayCoalesce=false) noexcept;\n\tvoid EndUndoAction() noexcept;\n\tint UndoSequenceDepth() const noexcept;\n\tbool AfterUndoSequenceStart() const noexcept;\n\tvoid DropUndoSequence() noexcept;\n\tvoid DeleteUndoHistory() noexcept;\n\n\t[[nodiscard]] int Actions() const noexcept;\n\n\t/// The save point is a marker in the undo stack where the container has stated that\n\t/// the buffer was saved. Undo and redo can move over the save point.\n\tvoid SetSavePoint(int action) noexcept;\n\t[[nodiscard]] int SavePoint() const noexcept;\n\tvoid SetSavePoint() noexcept;\n\tbool IsSavePoint() const noexcept;\n\tbool BeforeSavePoint() const noexcept;\n\tbool PreviousBeforeSavePoint() const noexcept;\n\tbool BeforeReachableSavePoint() const noexcept;\n\tbool AfterSavePoint() const noexcept;\n\n\t/// The detach point is the last action that was before an inaccessible missing save point.\n\tvoid SetDetachPoint(int action) noexcept;\n\t[[nodiscard]] int DetachPoint() const noexcept;\n\tbool AfterDetachPoint() const noexcept;\n\tbool AfterOrAtDetachPoint() const noexcept;\n\n\t[[nodiscard]] intptr_t Delta(int action) const noexcept;\n\t[[nodiscard]] bool Validate(intptr_t lengthDocument) const noexcept;\n\tvoid SetCurrent(int action, intptr_t lengthDocument);\n\t[[nodiscard]] int Current() const noexcept;\n\t[[nodiscard]] int Type(int action) const noexcept;\n\t[[nodiscard]] Sci::Position Position(int action) const noexcept;\n\t[[nodiscard]] Sci::Position Length(int action) const noexcept;\n\t[[nodiscard]] std::string_view Text(int action) noexcept;\n\tvoid PushUndoActionType(int type, Sci::Position position);\n\tvoid ChangeLastUndoActionText(size_t length, const char *text);\n\n\t// Tentative actions are used for input composition so that it can be undone cleanly\n\tvoid SetTentative(int action) noexcept;\n\t[[nodiscard]] int TentativePoint() const noexcept;\n\tvoid TentativeStart() noexcept;\n\tvoid TentativeCommit() noexcept;\n\tbool TentativeActive() const noexcept;\n\tint TentativeSteps() const noexcept;\n\n\t/// To perform an undo, StartUndo is called to retrieve the number of steps, then UndoStep is\n\t/// called that many times. Similarly for redo.\n\tbool CanUndo() const noexcept;\n\tint StartUndo() const noexcept;\n\tAction GetUndoStep() const noexcept;\n\tvoid CompletedUndoStep() noexcept;\n\tbool CanRedo() const noexcept;\n\tint StartRedo() const noexcept;\n\tAction GetRedoStep() const noexcept;\n\tvoid CompletedRedoStep() noexcept;\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/src/UniConversion.cxx",
    "content": "// Scintilla source code edit control\n/** @file UniConversion.cxx\n ** Functions to handle UTF-8 and UTF-16 strings.\n **/\n// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#include <cstdlib>\n\n#include <stdexcept>\n#include <string>\n#include <string_view>\n\n#include \"UniConversion.h\"\n\nnamespace Scintilla::Internal {\n\nnamespace {\n\nconstexpr int first2Byte = 0x80;\nconstexpr int first3Byte = 0x800;\n\nconstexpr unsigned int maskSurrogate = 0x3FF;\nconstexpr unsigned int shiftSurrogate = 10;\n\n// The top 2 bits are 0b10 to indicate a trail byte.\n// The lower 6 bits contain the value.\nconstexpr unsigned int trailByteFlag = 0b1000'0000;\nconstexpr unsigned int trailByteMask = 0b0011'1111;\n\n// With each UTF-8 length the bits are divided into length indicator\n// bits and value bits separated by a 0 bit.\nconstexpr unsigned int leadByte2 = 0b1100'0000;\nconstexpr unsigned int leadBits2 = 0b0001'1111;\n\nconstexpr unsigned int leadByte3 = 0b1110'0000;\nconstexpr unsigned int leadBits3 = 0b0000'1111;\n\nconstexpr unsigned int leadByte4 = 0b1111'0000;\nconstexpr unsigned int leadBits4 = 0b0000'0111;\n\nconstexpr unsigned int shiftByte2 = 6;\nconstexpr unsigned int shiftByte3 = 12;\nconstexpr unsigned int shiftByte4 = 18;\n\nconstexpr char LeadByte(unsigned int lengthValue, unsigned int uch) noexcept {\n\treturn static_cast<unsigned char>(lengthValue | uch);\n}\n\nconstexpr char TrailByte(unsigned int uch) noexcept {\n\treturn trailByteFlag | (uch & trailByteMask);\n}\n\nconstexpr unsigned char TrailByteValue(unsigned char c) noexcept {\n\treturn c & trailByteMask;\n}\n\nconstexpr wchar_t SurrogateLead(int val) noexcept {\n\treturn static_cast<wchar_t>(((val - SUPPLEMENTAL_PLANE_FIRST) >> shiftSurrogate) + SURROGATE_LEAD_FIRST);\n}\n\nconstexpr wchar_t SurrogateTrail(int val) noexcept {\n\treturn (val & maskSurrogate) + SURROGATE_TRAIL_FIRST;\n}\n\nvoid UTF8AppendCharacter(int uch, char *putf, size_t &k) noexcept {\n\tif (uch < first2Byte) {\n\t\tputf[k++] = LeadByte(0, uch);\n\t} else if (uch < first3Byte) {\n\t\tputf[k++] = LeadByte(leadByte2, uch >> shiftByte2);\n\t\tputf[k++] = TrailByte(uch);\n\t} else if (uch < SUPPLEMENTAL_PLANE_FIRST) {\n\t\tputf[k++] = LeadByte(leadByte3, uch >> shiftByte3);\n\t\tputf[k++] = TrailByte(uch >> shiftByte2);\n\t\tputf[k++] = TrailByte(uch);\n\t} else {\n\t\tputf[k++] = LeadByte(leadByte4, uch >> shiftByte4);\n\t\tputf[k++] = TrailByte(uch >> shiftByte3);\n\t\tputf[k++] = TrailByte(uch >> shiftByte2);\n\t\tputf[k++] = TrailByte(uch);\n\t}\n}\n\n}\n\nsize_t UTF8Length(std::wstring_view wsv) noexcept {\n\tsize_t len = 0;\n\tfor (size_t i = 0; i < wsv.length() && wsv[i];) {\n\t\tconst wchar_t uch = wsv[i];\n\t\tif (uch < first2Byte) {\n\t\t\tlen++;\n\t\t} else if (uch < first3Byte) {\n\t\t\tlen += 2;\n\t\t} else if (IsSurrogate(uch)) {\n\t\t\tlen += 4;\n\t\t\ti++;\n\t\t} else {\n\t\t\tlen += 3;\n\t\t}\n\t\ti++;\n\t}\n\treturn len;\n}\n\nsize_t UTF8PositionFromUTF16Position(std::string_view u8Text, size_t positionUTF16) noexcept {\n\tsize_t positionUTF8 = 0;\n\tfor (size_t lengthUTF16 = 0; (positionUTF8 < u8Text.length()) && (lengthUTF16 < positionUTF16);) {\n\t\tconst unsigned char uch = u8Text[positionUTF8];\n\t\tconst unsigned int byteCount = UTF8BytesOfLead[uch];\n\t\tlengthUTF16 += UTF16LengthFromUTF8ByteCount(byteCount);\n\t\tpositionUTF8 += byteCount;\n\t}\n\n\treturn positionUTF8;\n}\n\nvoid UTF8FromUTF16(std::wstring_view wsv, char *putf, size_t len) noexcept {\n\tsize_t k = 0;\n\tfor (size_t i = 0; i < wsv.length() && wsv[i];) {\n\t\tunsigned int uch = wsv[i];\n\t\tif (IsSurrogate(wsv[i])) {\n\t\t\t// Half a surrogate pair\n\t\t\ti++;\n\t\t\tuch = SUPPLEMENTAL_PLANE_FIRST + ((uch & maskSurrogate) << shiftSurrogate) + (wsv[i] & maskSurrogate);\n\t\t}\n\t\tUTF8AppendCharacter(uch, putf, k);\n\t\ti++;\n\t}\n\tif (k < len)\n\t\tputf[k] = '\\0';\n}\n\nvoid UTF8FromUTF32Character(int uch, char *putf) noexcept {\n\tsize_t k = 0;\n\tUTF8AppendCharacter(uch, putf, k);\n\tputf[k] = '\\0';\n}\n\nsize_t UTF16Length(std::string_view svu8) noexcept {\n\tsize_t ulen = 0;\n\tfor (size_t i = 0; i< svu8.length();) {\n\t\tconst unsigned char ch = svu8[i];\n\t\tconst unsigned int byteCount = UTF8BytesOfLead[ch];\n\t\tconst unsigned int utf16Len = UTF16LengthFromUTF8ByteCount(byteCount);\n\t\ti += byteCount;\n\t\tulen += (i > svu8.length()) ? 1 : utf16Len;\n\t}\n\treturn ulen;\n}\n\nsize_t UTF16FromUTF8(std::string_view svu8, wchar_t *tbuf, size_t tlen) {\n\tsize_t ui = 0;\n\tfor (size_t i = 0; i < svu8.length();) {\n\t\tunsigned char ch = svu8[i];\n\t\tconst unsigned int byteCount = UTF8BytesOfLead[ch];\n\t\tunsigned int value = 0;\n\n\t\tif (i + byteCount > svu8.length()) {\n\t\t\t// Trying to read past end but still have space to write\n\t\t\tif (ui < tlen) {\n\t\t\t\ttbuf[ui] = ch;\n\t\t\t\tui++;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\tconst size_t outLen = UTF16LengthFromUTF8ByteCount(byteCount);\n\t\tif (ui + outLen > tlen) {\n\t\t\tthrow std::runtime_error(\"UTF16FromUTF8: attempted write beyond end\");\n\t\t}\n\n\t\ti++;\n\t\tswitch (byteCount) {\n\t\tcase 1:\n\t\t\ttbuf[ui] = ch;\n\t\t\tbreak;\n\t\tcase 2:\n\t\t\tvalue = (ch & leadBits2) << shiftByte2;\n\t\t\tch = svu8[i++];\n\t\t\tvalue += TrailByteValue(ch);\n\t\t\ttbuf[ui] = static_cast<wchar_t>(value);\n\t\t\tbreak;\n\t\tcase 3:\n\t\t\tvalue = (ch & leadBits3) << shiftByte3;\n\t\t\tch = svu8[i++];\n\t\t\tvalue += (TrailByteValue(ch) << shiftByte2);\n\t\t\tch = svu8[i++];\n\t\t\tvalue += TrailByteValue(ch);\n\t\t\ttbuf[ui] = static_cast<wchar_t>(value);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\t// Outside the BMP so need two surrogates\n\t\t\tvalue = (ch & leadBits4) << shiftByte4;\n\t\t\tch = svu8[i++];\n\t\t\tvalue += TrailByteValue(ch) << shiftByte3;\n\t\t\tch = svu8[i++];\n\t\t\tvalue += TrailByteValue(ch) << shiftByte2;\n\t\t\tch = svu8[i++];\n\t\t\tvalue += TrailByteValue(ch);\n\t\t\ttbuf[ui] = SurrogateLead(value);\n\t\t\tui++;\n\t\t\ttbuf[ui] = SurrogateTrail(value);\n\t\t\tbreak;\n\t\t}\n\t\tui++;\n\t}\n\treturn ui;\n}\n\nsize_t UTF32Length(std::string_view svu8) noexcept {\n\tsize_t ulen = 0;\n\tfor (size_t i = 0; i < svu8.length();) {\n\t\tconst unsigned char ch = svu8[i];\n\t\tconst unsigned int byteCount = UTF8BytesOfLead[ch];\n\t\ti += byteCount;\n\t\tulen++;\n\t}\n\treturn ulen;\n}\n\nsize_t UTF32FromUTF8(std::string_view svu8, unsigned int *tbuf, size_t tlen) {\n\tsize_t ui = 0;\n\tfor (size_t i = 0; i < svu8.length();) {\n\t\tunsigned char ch = svu8[i];\n\t\tconst unsigned int byteCount = UTF8BytesOfLead[ch];\n\t\tunsigned int value = 0;\n\n\t\tif (i + byteCount > svu8.length()) {\n\t\t\t// Trying to read past end but still have space to write\n\t\t\tif (ui < tlen) {\n\t\t\t\ttbuf[ui] = ch;\n\t\t\t\tui++;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\tif (ui == tlen) {\n\t\t\tthrow std::runtime_error(\"UTF32FromUTF8: attempted write beyond end\");\n\t\t}\n\n\t\ti++;\n\t\tswitch (byteCount) {\n\t\tcase 1:\n\t\t\tvalue = ch;\n\t\t\tbreak;\n\t\tcase 2:\n\t\t\tvalue = (ch & leadBits2) << shiftByte2;\n\t\t\tch = svu8[i++];\n\t\t\tvalue += TrailByteValue(ch);\n\t\t\tbreak;\n\t\tcase 3:\n\t\t\tvalue = (ch & leadBits3) << shiftByte3;\n\t\t\tch = svu8[i++];\n\t\t\tvalue += TrailByteValue(ch) << shiftByte2;\n\t\t\tch = svu8[i++];\n\t\t\tvalue += TrailByteValue(ch);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tvalue = (ch & leadBits4) << shiftByte4;\n\t\t\tch = svu8[i++];\n\t\t\tvalue += TrailByteValue(ch) << shiftByte3;\n\t\t\tch = svu8[i++];\n\t\t\tvalue += TrailByteValue(ch) << shiftByte2;\n\t\t\tch = svu8[i++];\n\t\t\tvalue += TrailByteValue(ch);\n\t\t\tbreak;\n\t\t}\n\t\ttbuf[ui] = value;\n\t\tui++;\n\t}\n\treturn ui;\n}\n\nstd::wstring WStringFromUTF8(std::string_view svu8) {\n\tif constexpr (sizeof(wchar_t) == 2) {\n\t\tconst size_t len16 = UTF16Length(svu8);\n\t\tstd::wstring ws(len16, 0);\n\t\tUTF16FromUTF8(svu8, ws.data(), len16);\n\t\treturn ws;\n\t} else {\n\t\tconst size_t len32 = UTF32Length(svu8);\n\t\tstd::wstring ws(len32, 0);\n\t\tUTF32FromUTF8(svu8, reinterpret_cast<unsigned int *>(ws.data()), len32);\n\t\treturn ws;\n\t}\n}\n\nunsigned int UTF16FromUTF32Character(unsigned int val, wchar_t *tbuf) noexcept {\n\tif (val < SUPPLEMENTAL_PLANE_FIRST) {\n\t\ttbuf[0] = static_cast<wchar_t>(val);\n\t\treturn 1;\n\t}\n\ttbuf[0] = SurrogateLead(val);\n\ttbuf[1] = SurrogateTrail(val);\n\treturn 2;\n}\n\nint UnicodeFromUTF8(std::string_view sv) noexcept {\n\tif (!sv.empty()) {\n\t\tconst unsigned char uch = sv.front();\n\t\tconst unsigned int byteCount = UTF8BytesOfLead[uch];\n\t\tif (sv.length() >= byteCount) {\n\t\t\treturn UnicodeFromUTF8(reinterpret_cast<const unsigned char *>(sv.data()));\n\t\t}\n\t}\n\t// Failure so let the caller know\n\treturn unicodeReplacementChar;\n}\n\nconst unsigned char UTF8BytesOfLead[256] = {\n1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 00 - 0F\n1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 10 - 1F\n1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 20 - 2F\n1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 30 - 3F\n1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 40 - 4F\n1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 50 - 5F\n1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 60 - 6F\n1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 70 - 7F\n1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80 - 8F\n1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 90 - 9F\n1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A0 - AF\n1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B0 - BF\n1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C0 - CF\n2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // D0 - DF\n3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // E0 - EF\n4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // F0 - FF\n};\n\n// Silence 'magic' number warning as UTF-8 needs to distinguish byte values and byte value ranges.\n\n// NOLINTBEGIN(*-magic-numbers)\n\n// Return both the width of the first character in the string and a status\n// saying whether it is valid or invalid.\n// Most invalid sequences return a width of 1 so are treated as isolated bytes but\n// the non-characters *FFFE, *FFFF and FDD0 .. FDEF return 3 or 4 as they can be\n// reasonably treated as code points in some circumstances. They will, however,\n// not have associated glyphs.\nint UTF8Classify(const unsigned char *us, size_t len) noexcept {\n\t// For the rules: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8\n\tif (us[0] < 0x80) {\n\t\t// ASCII\n\t\treturn 1;\n\t}\n\n\tconst size_t byteCount = UTF8BytesOfLead[us[0]];\n\tif (byteCount == 1 || byteCount > len) {\n\t\t// Invalid lead byte\n\t\treturn UTF8MaskInvalid | 1;\n\t}\n\n\tif (!UTF8IsTrailByte(us[1])) {\n\t\t// Invalid trail byte\n\t\treturn UTF8MaskInvalid | 1;\n\t}\n\n\tswitch (byteCount) {\n\tcase 2:\n\t\treturn 2;\n\n\tcase 3:\n\t\tif (UTF8IsTrailByte(us[2])) {\n\t\t\tif ((*us == 0xe0) && ((us[1] & 0xe0) == 0x80)) {\n\t\t\t\t// Overlong\n\t\t\t\treturn UTF8MaskInvalid | 1;\n\t\t\t}\n\t\t\tif ((*us == 0xed) && ((us[1] & 0xe0) == 0xa0)) {\n\t\t\t\t// Surrogate\n\t\t\t\treturn UTF8MaskInvalid | 1;\n\t\t\t}\n\t\t\tif ((*us == 0xef) && (us[1] == 0xbf) && (us[2] == 0xbe)) {\n\t\t\t\t// U+FFFE non-character - 3 bytes long\n\t\t\t\treturn UTF8MaskInvalid | 3;\n\t\t\t}\n\t\t\tif ((*us == 0xef) && (us[1] == 0xbf) && (us[2] == 0xbf)) {\n\t\t\t\t// U+FFFF non-character - 3 bytes long\n\t\t\t\treturn UTF8MaskInvalid | 3;\n\t\t\t}\n\t\t\tif ((*us == 0xef) && (us[1] == 0xb7) && (((us[2] & 0xf0) == 0x90) || ((us[2] & 0xf0) == 0xa0))) {\n\t\t\t\t// U+FDD0 .. U+FDEF\n\t\t\t\treturn UTF8MaskInvalid | 3;\n\t\t\t}\n\t\t\treturn 3;\n\t\t}\n\t\tbreak;\n\n\tdefault:\n\t\tif (UTF8IsTrailByte(us[2]) && UTF8IsTrailByte(us[3])) {\n\t\t\tif (((us[1] & 0xf) == 0xf) && (us[2] == 0xbf) && ((us[3] == 0xbe) || (us[3] == 0xbf))) {\n\t\t\t\t// *FFFE or *FFFF non-character\n\t\t\t\treturn UTF8MaskInvalid | 4;\n\t\t\t}\n\t\t\tif (*us == 0xf4) {\n\t\t\t\t// Check if encoding a value beyond the last Unicode character 10FFFF\n\t\t\t\tif (us[1] > 0x8f) {\n\t\t\t\t\treturn UTF8MaskInvalid | 1;\n\t\t\t\t}\n\t\t\t} else if ((*us == 0xf0) && ((us[1] & 0xf0) == 0x80)) {\n\t\t\t\t// Overlong\n\t\t\t\treturn UTF8MaskInvalid | 1;\n\t\t\t}\n\t\t\treturn 4;\n\t\t}\n\t\tbreak;\n\t}\n\n\treturn UTF8MaskInvalid | 1;\n}\n\n// NOLINTEND(*-magic-numbers)\n\nint UTF8Classify(const char *s, size_t len) noexcept {\n\treturn UTF8Classify(reinterpret_cast<const unsigned char *>(s), len);\n}\n\nint UTF8DrawBytes(const char *s, size_t len) noexcept {\n\tconst int utf8StatusNext = UTF8Classify(s, len);\n\treturn (utf8StatusNext & UTF8MaskInvalid) ? 1 : (utf8StatusNext & UTF8MaskWidth);\n}\n\nbool UTF8IsValid(std::string_view svu8) noexcept {\n\tconst char *s = svu8.data();\n\tsize_t remaining = svu8.length();\n\twhile (remaining > 0) {\n\t\tconst int utf8Status = UTF8Classify(s, remaining);\n\t\tif (utf8Status & UTF8MaskInvalid) {\n\t\t\treturn false;\n\t\t}\n\t\tconst int lenChar = utf8Status & UTF8MaskWidth;\n\t\ts += lenChar;\n\t\tremaining -= lenChar;\n\t}\n\treturn true;\n}\n\n// Replace invalid bytes in UTF-8 with the replacement character\nstd::string FixInvalidUTF8(const std::string &text) {\n\tstd::string result;\n\tconst char *s = text.c_str();\n\tsize_t remaining = text.size();\n\twhile (remaining > 0) {\n\t\tconst int utf8Status = UTF8Classify(s, remaining);\n\t\tif (utf8Status & UTF8MaskInvalid) {\n\t\t\t// Replacement character 0xFFFD = UTF8:\"efbfbd\".\n\t\t\tresult.append(\"\\xef\\xbf\\xbd\");\n\t\t\ts++;\n\t\t\tremaining--;\n\t\t} else {\n\t\t\tconst size_t len = utf8Status & UTF8MaskWidth;\n\t\t\tresult.append(s, len);\n\t\t\ts += len;\n\t\t\tremaining -= len;\n\t\t}\n\t}\n\treturn result;\n}\n\n}\n"
  },
  {
    "path": "Libraries/scintilla/src/UniConversion.h",
    "content": "// Scintilla source code edit control\n/** @file UniConversion.h\n ** Functions to handle UTF-8 and UTF-16 strings.\n **/\n// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#ifndef UNICONVERSION_H\n#define UNICONVERSION_H\n\nnamespace Scintilla::Internal {\n\nconstexpr int UTF8MaxBytes = 4;\n\nconstexpr int unicodeReplacementChar = 0xFFFD;\n\nsize_t UTF8Length(std::wstring_view wsv) noexcept;\nsize_t UTF8PositionFromUTF16Position(std::string_view u8Text, size_t positionUTF16) noexcept;\nvoid UTF8FromUTF16(std::wstring_view wsv, char *putf, size_t len) noexcept;\nvoid UTF8FromUTF32Character(int uch, char *putf) noexcept;\nsize_t UTF16Length(std::string_view svu8) noexcept;\nsize_t UTF16FromUTF8(std::string_view svu8, wchar_t *tbuf, size_t tlen);\nsize_t UTF32Length(std::string_view svu8) noexcept;\nsize_t UTF32FromUTF8(std::string_view svu8, unsigned int *tbuf, size_t tlen);\n// WStringFromUTF8 does the right thing when wchar_t is 2 or 4 bytes so\n// works on both Windows and Unix.\nstd::wstring WStringFromUTF8(std::string_view svu8);\nunsigned int UTF16FromUTF32Character(unsigned int val, wchar_t *tbuf) noexcept;\nbool UTF8IsValid(std::string_view svu8) noexcept;\nstd::string FixInvalidUTF8(const std::string &text);\n\nextern const unsigned char UTF8BytesOfLead[256];\n\ninline int UnicodeFromUTF8(const unsigned char *us) noexcept {\n\tswitch (UTF8BytesOfLead[us[0]]) {\n\tcase 1:\n\t\treturn us[0];\n\tcase 2:\n\t\treturn ((us[0] & 0x1F) << 6) + (us[1] & 0x3F);\n\tcase 3:\n\t\treturn ((us[0] & 0xF) << 12) + ((us[1] & 0x3F) << 6) + (us[2] & 0x3F);\n\tdefault:\n\t\treturn ((us[0] & 0x7) << 18) + ((us[1] & 0x3F) << 12) + ((us[2] & 0x3F) << 6) + (us[3] & 0x3F);\n\t}\n}\nint UnicodeFromUTF8(std::string_view sv) noexcept;\n\nconstexpr bool UTF8IsTrailByte(unsigned char ch) noexcept {\n\treturn (ch >= 0x80) && (ch < 0xc0);\n}\n\nconstexpr bool UTF8IsFirstByte(unsigned char ch) noexcept {\n\treturn (ch >= 0xc2) && (ch <= 0xf4);\n}\n\nconstexpr bool UTF8IsAscii(unsigned char ch) noexcept {\n\treturn ch < 0x80;\n}\n\nconstexpr bool UTF8IsAscii(char ch) noexcept {\n\tconst unsigned char uch = ch;\n\treturn uch < 0x80;\n}\n\nenum { UTF8MaskWidth=0x7, UTF8MaskInvalid=0x8 };\nint UTF8Classify(const unsigned char *us, size_t len) noexcept;\nint UTF8Classify(const char *s, size_t len) noexcept;\ninline int UTF8Classify(std::string_view sv) noexcept {\n\treturn UTF8Classify(sv.data(), sv.length());\n}\n\n// Similar to UTF8Classify but returns a length of 1 for invalid bytes\n// instead of setting the invalid flag\nint UTF8DrawBytes(const char *s, size_t len) noexcept;\n\n// Line separator is U+2028 \\xe2\\x80\\xa8\n// Paragraph separator is U+2029 \\xe2\\x80\\xa9\nconstexpr int UTF8SeparatorLength = 3;\nconstexpr bool UTF8IsSeparator(const unsigned char *us) noexcept {\n\treturn (us[0] == 0xe2) && (us[1] == 0x80) && ((us[2] == 0xa8) || (us[2] == 0xa9));\n}\n\n// NEL is U+0085 \\xc2\\x85\nconstexpr int UTF8NELLength = 2;\nconstexpr bool UTF8IsNEL(const unsigned char *us) noexcept {\n\treturn (us[0] == 0xc2) && (us[1] == 0x85);\n}\n\n// Is the sequence of 3 char a UTF-8 line end? Only the last two char are tested for a NEL.\nconstexpr bool UTF8IsMultibyteLineEnd(unsigned char ch0, unsigned char ch1, unsigned char ch2) noexcept {\n\treturn\n\t\t((ch0 == 0xe2) && (ch1 == 0x80) && ((ch2 == 0xa8) || (ch2 == 0xa9))) ||\n\t\t((ch1 == 0xc2) && (ch2 == 0x85));\n}\n\nenum { SURROGATE_LEAD_FIRST = 0xD800 };\nenum { SURROGATE_LEAD_LAST = 0xDBFF };\nenum { SURROGATE_TRAIL_FIRST = 0xDC00 };\nenum { SURROGATE_TRAIL_LAST = 0xDFFF };\nenum { SUPPLEMENTAL_PLANE_FIRST = 0x10000 };\n\nconstexpr bool IsSurrogate(wchar_t uch) noexcept {\n\treturn (uch >= SURROGATE_LEAD_FIRST) && (uch <= SURROGATE_TRAIL_LAST);\n}\n\nconstexpr unsigned int UTF16CharLength(wchar_t uch) noexcept {\n\treturn IsSurrogate(uch) ? 2 : 1;\n}\n\nconstexpr unsigned int UTF16LengthFromUTF8ByteCount(unsigned int byteCount) noexcept {\n\treturn (byteCount < 4) ? 1 : 2;\n}\n\n}\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/src/UniqueString.cxx",
    "content": "// Scintilla source code edit control\n/** @file UniqueString.cxx\n ** Define an allocator for UniqueString.\n **/\n// Copyright 2017 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#include <string_view>\n#include <vector>\n#include <algorithm>\n#include <memory>\n\n#include \"UniqueString.h\"\n\nnamespace Scintilla::Internal {\n\n/// Equivalent to strdup but produces a std::unique_ptr<const char[]> allocation to go\n/// into collections.\nUniqueString UniqueStringCopy(const char *text) {\n\tif (!text) {\n\t\treturn UniqueString();\n\t}\n\tconst std::string_view sv(text);\n\tstd::unique_ptr<char[]> upcNew = std::make_unique<char[]>(sv.length() + 1);\n\tsv.copy(upcNew.get(), sv.length());\n\treturn UniqueString(upcNew.release());\n}\n\n// A set of strings that always returns the same pointer for each string.\n\nUniqueStringSet::UniqueStringSet() = default;\n\nvoid UniqueStringSet::Clear() noexcept {\n\tstrings.clear();\n}\n\nconst char *UniqueStringSet::Save(const char *text) {\n\tif (!text)\n\t\treturn nullptr;\n\n\tconst std::string_view sv(text);\n\tfor (const UniqueString &us : strings) {\n\t\tif (sv == us.get()) {\n\t\t\treturn us.get();\n\t\t}\n\t}\n\n\tstrings.push_back(UniqueStringCopy(text));\n\treturn strings.back().get();\n}\n\n}\n"
  },
  {
    "path": "Libraries/scintilla/src/UniqueString.h",
    "content": "// Scintilla source code edit control\n/** @file UniqueString.h\n ** Define UniqueString, a unique_ptr based string type for storage in containers\n ** and an allocator for UniqueString.\n ** Define UniqueStringSet which holds a set of strings, used to avoid holding many copies\n ** of font names.\n **/\n// Copyright 2017 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#ifndef UNIQUESTRING_H\n#define UNIQUESTRING_H\n\nnamespace Scintilla::Internal {\n\nconstexpr bool IsNullOrEmpty(const char *text) noexcept {\n\treturn text == nullptr || *text == '\\0';\n}\n\nusing UniqueString = std::unique_ptr<const char[]>;\n\n/// Equivalent to strdup but produces a std::unique_ptr<const char[]> allocation to go\n/// into collections.\nUniqueString UniqueStringCopy(const char *text);\n\n// A set of strings that always returns the same pointer for each string.\n\nclass UniqueStringSet {\nprivate:\n\tstd::vector<UniqueString> strings;\npublic:\n\tUniqueStringSet();\n\tvoid Clear() noexcept;\n\tconst char *Save(const char *text);\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/src/ViewStyle.cxx",
    "content": "// Scintilla source code edit control\n/** @file ViewStyle.cxx\n ** Store information on how the document is to be viewed.\n **/\n// Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#include <cstddef>\n#include <cstdint>\n#include <cassert>\n#include <cstring>\n#include <cmath>\n\n#include <stdexcept>\n#include <string>\n#include <string_view>\n#include <vector>\n#include <array>\n#include <map>\n#include <set>\n#include <optional>\n#include <algorithm>\n#include <memory>\n#include <numeric>\n\n#include \"ScintillaTypes.h\"\n\n#include \"Debugging.h\"\n#include \"Geometry.h\"\n#include \"Platform.h\"\n\n#include \"Position.h\"\n#include \"UniqueString.h\"\n#include \"Indicator.h\"\n#include \"XPM.h\"\n#include \"LineMarker.h\"\n#include \"Style.h\"\n#include \"ViewStyle.h\"\n\nusing namespace Scintilla;\nusing namespace Scintilla::Internal;\n\nnamespace {\n\n// Colour component proportions of maximum 0xffU\nconstexpr unsigned int light = 0xc0U;\n// The middle point of 0..0xff is between 0x7fU and 0x80U and both are used\nconstexpr unsigned int mid = 0x80U;\nconstexpr unsigned int half = 0x7fU;\nconstexpr unsigned int quarter = 0x3fU;\n\n}\n\nMarginStyle::MarginStyle(MarginType style_, int width_, int mask_) noexcept :\n\tstyle(style_), width(width_), mask(mask_), sensitive(false), cursor(CursorShape::ReverseArrow) {\n}\n\nbool MarginStyle::ShowsFolding() const noexcept {\n\treturn (mask & MaskFolders) != 0;\n}\n\nvoid FontRealised::Realise(Surface &surface, int zoomLevel, Technology technology, const FontSpecification &fs, const char *localeName) {\n\tPLATFORM_ASSERT(fs.fontName);\n\tmeasurements.sizeZoomed = fs.size + zoomLevel * FontSizeMultiplier;\n\tif (measurements.sizeZoomed <= FontSizeMultiplier)\t// May fail if sizeZoomed < 1\n\t\tmeasurements.sizeZoomed = FontSizeMultiplier;\n\n\tconst float deviceHeight = static_cast<float>(surface.DeviceHeightFont(measurements.sizeZoomed));\n\tconst FontParameters fp(fs.fontName, deviceHeight / FontSizeMultiplier, fs.weight,\n\t\tfs.italic, fs.extraFontFlag, technology, fs.characterSet, localeName, fs.stretch);\n\tfont = Font::Allocate(fp);\n\n\t// floor here is historical as platform layers have tweaked their values to match.\n\t// ceil would likely be better to ensure (nearly) all of the ink of a character is seen\n\t// but that would require platform layer changes.\n\tmeasurements.ascent = std::floor(surface.Ascent(font.get()));\n\tmeasurements.descent = std::floor(surface.Descent(font.get()));\n\n\tmeasurements.capitalHeight = surface.Ascent(font.get()) - surface.InternalLeading(font.get());\n\tmeasurements.aveCharWidth = surface.AverageCharWidth(font.get());\n\tmeasurements.monospaceCharacterWidth = measurements.aveCharWidth;\n\tmeasurements.spaceWidth = surface.WidthText(font.get(), \" \");\n\n\tif (fs.checkMonospaced) {\n\t\t// \"Ay\" is normally strongly kerned and \"fi\" may be a ligature\n\t\tconstexpr std::string_view allASCIIGraphic(\"Ayfi\"\n\t\t// python: ''.join(chr(ch) for ch in range(32, 127))\n\t\t\" !\\\"#$%&\\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\");\n\t\tstd::array<XYPOSITION, allASCIIGraphic.length()> positions {};\n\t\tsurface.MeasureWidthsUTF8(font.get(), allASCIIGraphic, positions.data());\n\t\tstd::adjacent_difference(positions.begin(), positions.end(), positions.begin());\n\t\tconst XYPOSITION maxWidth = *std::max_element(positions.begin(), positions.end());\n\t\tconst XYPOSITION minWidth = *std::min_element(positions.begin(), positions.end());\n\t\tconst XYPOSITION variance = maxWidth - minWidth;\n\t\tconst XYPOSITION scaledVariance = variance / measurements.aveCharWidth;\n\t\tconstexpr XYPOSITION monospaceWidthEpsilon = 0.000001;\t// May need tweaking if monospace fonts vary more\n\t\tmeasurements.monospaceASCII = scaledVariance < monospaceWidthEpsilon;\n\t\tmeasurements.monospaceCharacterWidth = minWidth;\n\t} else {\n\t\tmeasurements.monospaceASCII = false;\n\t}\n}\n\nViewStyle::ViewStyle(size_t stylesSize_) :\n\tstyles(stylesSize_),\n\tmarkers(MarkerMax + 1),\n\tindicators(static_cast<size_t>(IndicatorNumbers::Max) + 1),\n\tms(MaxMargin + 1) {\n\n\tnextExtendedStyle = 256;\n\tResetDefaultStyle();\n\n\t// There are no image markers by default, so no need for calling CalcLargestMarkerHeight()\n\tlargestMarkerHeight = 0;\n\n\tindicators[0] = Indicator(IndicatorStyle::Squiggle, ColourRGBA(0, half, 0));\t// Green\n\tindicators[1] = Indicator(IndicatorStyle::TT, ColourRGBA(0, 0, maximumByte));\t// Blue\n\tindicators[2] = Indicator(IndicatorStyle::Plain, ColourRGBA(maximumByte, 0, 0));\t// Red\n\n\t// Reverted to origin\n\tconstexpr ColourRGBA revertedToOrigin(0x40, 0xA0, 0xBF);\n\t// Saved\n\tconstexpr ColourRGBA saved(0x0, 0xA0, 0x0);\n\t// Modified\n\tconstexpr ColourRGBA modified(0xFF, 0x80, 0x0);\n\t// Reverted to change\n\tconstexpr ColourRGBA revertedToChange(0xA0, 0xC0, 0x0);\n\n\t// Edition indicators\n\tconstexpr size_t indexHistory = static_cast<size_t>(IndicatorNumbers::HistoryRevertedToOriginInsertion);\n\n\t// Default indicators are moderately intense so they don't overwhelm text\n\tconstexpr int alphaFill = 30;\n\tconstexpr int alphaOutline = 50;\n\tindicators[indexHistory+0] = Indicator(IndicatorStyle::CompositionThick, revertedToOrigin, false, alphaFill, alphaOutline);\n\tindicators[indexHistory+1] = Indicator(IndicatorStyle::Point, revertedToOrigin);\n\tindicators[indexHistory+2] = Indicator(IndicatorStyle::CompositionThick, saved, false, alphaFill, alphaOutline);\n\tindicators[indexHistory+3] = Indicator(IndicatorStyle::Point, saved);\n\tindicators[indexHistory+4] = Indicator(IndicatorStyle::CompositionThick, modified, false, alphaFill, alphaOutline);\n\tindicators[indexHistory+5] = Indicator(IndicatorStyle::PointTop, modified);\n\tindicators[indexHistory+6] = Indicator(IndicatorStyle::CompositionThick, revertedToChange, false, alphaFill, alphaOutline);\n\tindicators[indexHistory+7] = Indicator(IndicatorStyle::Point, revertedToChange);\n\n\t// Edition markers\n\t// Reverted to origin\n\tconstexpr size_t indexHistoryRevertedToOrigin = static_cast<size_t>(MarkerOutline::HistoryRevertedToOrigin);\n\tmarkers[indexHistoryRevertedToOrigin].back = revertedToOrigin;\n\tmarkers[indexHistoryRevertedToOrigin].fore = revertedToOrigin;\n\tmarkers[indexHistoryRevertedToOrigin].markType = MarkerSymbol::Bar;\n\t// Saved\n\tconstexpr size_t indexHistorySaved = static_cast<size_t>(MarkerOutline::HistorySaved);\n\tmarkers[indexHistorySaved].back = saved;\n\tmarkers[indexHistorySaved].fore = saved;\n\tmarkers[indexHistorySaved].markType = MarkerSymbol::Bar;\n\t// Modified\n\tconstexpr size_t indexHistoryModified = static_cast<size_t>(MarkerOutline::HistoryModified);\n\tmarkers[indexHistoryModified].back = Platform::Chrome();\n\tmarkers[indexHistoryModified].fore = modified;\n\tmarkers[indexHistoryModified].markType = MarkerSymbol::Bar;\n\t// Reverted to change\n\tconstexpr size_t indexHistoryRevertedToModified = static_cast<size_t>(MarkerOutline::HistoryRevertedToModified);\n\tmarkers[indexHistoryRevertedToModified].back = revertedToChange;\n\tmarkers[indexHistoryRevertedToModified].fore = revertedToChange;\n\tmarkers[indexHistoryRevertedToModified].markType = MarkerSymbol::Bar;\n\n\ttechnology = Technology::Default;\n\tindicatorsDynamic = false;\n\tindicatorsSetFore = false;\n\tlineHeight = 1;\n\tlineOverlap = 0;\n\tmaxAscent = 1;\n\tmaxDescent = 1;\n\taveCharWidth = 8;\n\tspaceWidth = 8;\n\ttabWidth = spaceWidth * 8;\n\n\t// Default is for no selection foregrounds\n\t// Shades of grey for selection backgrounds\n\telementBaseColours[Element::SelectionBack] = ColourRGBA::Grey(light);\n\tconstexpr unsigned int veryLight = 0xd7U;\n\telementBaseColours[Element::SelectionAdditionalBack] = ColourRGBA::Grey(veryLight);\n\tconstexpr unsigned int halfLight = 0xb0;\n\telementBaseColours[Element::SelectionSecondaryBack] = ColourRGBA::Grey(halfLight);\n\telementBaseColours[Element::SelectionInactiveBack] = ColourRGBA::Grey(mid, quarter);\n\telementAllowsTranslucent.insert({\n\t\tElement::SelectionText,\n\t\tElement::SelectionBack,\n\t\tElement::SelectionAdditionalText,\n\t\tElement::SelectionAdditionalBack,\n\t\tElement::SelectionSecondaryText,\n\t\tElement::SelectionSecondaryBack,\n\t\tElement::SelectionInactiveText,\n\t\tElement::SelectionInactiveBack,\n\t\tElement::SelectionInactiveAdditionalText,\n\t\tElement::SelectionInactiveAdditionalBack,\n\t\t});\n\n\tcontrolCharSymbol = 0;\t/* Draw the control characters */\n\tcontrolCharWidth = 0;\n\tselbar = Platform::Chrome();\n\tselbarlight = Platform::ChromeHighlight();\n\tstyles[StyleLineNumber].fore = black;\n\tstyles[StyleLineNumber].back = Platform::Chrome();\n\n\telementBaseColours[Element::Caret] = black;\n\telementBaseColours[Element::CaretAdditional] = ColourRGBA::Grey(half);\n\telementAllowsTranslucent.insert({\n\t\tElement::Caret,\n\t\tElement::CaretAdditional,\n\t\t});\n\n\telementAllowsTranslucent.insert(Element::CaretLineBack);\n\n\tsomeStylesProtected = false;\n\tsomeStylesForceCase = false;\n\n\thotspotUnderline = true;\n\telementAllowsTranslucent.insert(Element::HotSpotActive);\n\n\tleftMarginWidth = 1;\n\trightMarginWidth = 1;\n\tms[0] = MarginStyle(MarginType::Number);\n\tms[1] = MarginStyle(MarginType::Symbol, 16, ~MaskFolders);\n\tms[2] = MarginStyle(MarginType::Symbol);\n\tmarginInside = true;\n\tCalculateMarginWidthAndMask();\n\ttextStart = marginInside ? fixedColumnWidth : leftMarginWidth;\n\tzoomLevel = 0;\n\tviewWhitespace = WhiteSpace::Invisible;\n\ttabDrawMode = TabDrawMode::LongArrow;\n\twhitespaceSize = 1;\n\telementAllowsTranslucent.insert(Element::WhiteSpace);\n\n\tviewIndentationGuides = IndentView::None;\n\tviewEOL = false;\n\textraFontFlag = FontQuality::QualityDefault;\n\textraAscent = 0;\n\textraDescent = 0;\n\tmarginStyleOffset = 0;\n\tannotationVisible = AnnotationVisible::Hidden;\n\tannotationStyleOffset = 0;\n\teolAnnotationVisible = EOLAnnotationVisible::Hidden;\n\teolAnnotationStyleOffset = 0;\n\tbraceHighlightIndicatorSet = false;\n\tbraceHighlightIndicator = 0;\n\tbraceBadLightIndicatorSet = false;\n\tbraceBadLightIndicator = 0;\n\n\tedgeState = EdgeVisualStyle::None;\n\ttheEdge = EdgeProperties(0, ColourRGBA::Grey(light));\n\n\tmarginNumberPadding = 3;\n\tctrlCharPadding = 3; // +3 For a blank on front and rounded edge each side\n\tlastSegItalicsOffset = 2;\n\n\tautocStyle = StyleDefault;\n\n\tlocaleName = localeNameDefault;\n}\n\n// Copy constructor only called when printing copies the screen ViewStyle so it can be\n// modified for printing styles.\nViewStyle::ViewStyle(const ViewStyle &source) : ViewStyle(source.styles.size()) {\n\tstyles = source.styles;\n\tfor (Style &style : styles) {\n\t\t// Can't just copy fontName as its lifetime is relative to its owning ViewStyle\n\t\tstyle.fontName = fontNames.Save(style.fontName);\n\t}\n\tnextExtendedStyle = source.nextExtendedStyle;\n\tmarkers = source.markers;\n\tCalcLargestMarkerHeight();\n\n\tindicators = source.indicators;\n\n\tindicatorsDynamic = source.indicatorsDynamic;\n\tindicatorsSetFore = source.indicatorsSetFore;\n\n\tselection = source.selection;\n\n\tfoldmarginColour = source.foldmarginColour;\n\tfoldmarginHighlightColour = source.foldmarginHighlightColour;\n\n\thotspotUnderline = source.hotspotUnderline;\n\n\tcontrolCharSymbol = source.controlCharSymbol;\n\tcontrolCharWidth = source.controlCharWidth;\n\tselbar = source.selbar;\n\tselbarlight = source.selbarlight;\n\tcaret = source.caret;\n\tcaretLine = source.caretLine;\n\tsomeStylesProtected = false;\n\tsomeStylesForceCase = false;\n\tleftMarginWidth = source.leftMarginWidth;\n\trightMarginWidth = source.rightMarginWidth;\n\tms = source.ms;\n\tmaskInLine = source.maskInLine;\n\tmaskDrawInText = source.maskDrawInText;\n\tmaskDrawWrapped = source.maskDrawWrapped;\n\tfixedColumnWidth = source.fixedColumnWidth;\n\tmarginInside = source.marginInside;\n\ttextStart = source.textStart;\n\tzoomLevel = source.zoomLevel;\n\tviewWhitespace = source.viewWhitespace;\n\ttabDrawMode = source.tabDrawMode;\n\twhitespaceSize = source.whitespaceSize;\n\tviewIndentationGuides = source.viewIndentationGuides;\n\tviewEOL = source.viewEOL;\n\textraFontFlag = source.extraFontFlag;\n\textraAscent = source.extraAscent;\n\textraDescent = source.extraDescent;\n\tmarginStyleOffset = source.marginStyleOffset;\n\tannotationVisible = source.annotationVisible;\n\tannotationStyleOffset = source.annotationStyleOffset;\n\teolAnnotationVisible = source.eolAnnotationVisible;\n\teolAnnotationStyleOffset = source.eolAnnotationStyleOffset;\n\tbraceHighlightIndicatorSet = source.braceHighlightIndicatorSet;\n\tbraceHighlightIndicator = source.braceHighlightIndicator;\n\tbraceBadLightIndicatorSet = source.braceBadLightIndicatorSet;\n\tbraceBadLightIndicator = source.braceBadLightIndicator;\n\n\tedgeState = source.edgeState;\n\ttheEdge = source.theEdge;\n\ttheMultiEdge = source.theMultiEdge;\n\n\tmarginNumberPadding = source.marginNumberPadding;\n\tctrlCharPadding = source.ctrlCharPadding;\n\tlastSegItalicsOffset = source.lastSegItalicsOffset;\n\n\twrap = source.wrap;\n\n\tlocaleName = source.localeName;\n}\n\nViewStyle::~ViewStyle() = default;\n\nvoid ViewStyle::CalculateMarginWidthAndMask() noexcept {\n\tfixedColumnWidth = marginInside ? leftMarginWidth : 0;\n\tmaskInLine = 0xffffffff;\n\tint maskDefinedMarkers = 0;\n\tfor (const MarginStyle &m : ms) {\n\t\tfixedColumnWidth += m.width;\n\t\tif (m.width > 0)\n\t\t\tmaskInLine &= ~m.mask;\n\t\tmaskDefinedMarkers |= m.mask;\n\t}\n\tmaskDrawInText = 0;\n\tfor (int markBit = 0; markBit <= MarkerMax; markBit++) {\n\t\tconst int maskBit = 1U << markBit;\n\t\tswitch (markers[markBit].markType) {\n\t\tcase MarkerSymbol::Empty:\n\t\t\tmaskInLine &= ~maskBit;\n\t\t\tbreak;\n\t\tcase MarkerSymbol::Background:\n\t\tcase MarkerSymbol::Underline:\n\t\t\tmaskInLine &= ~maskBit;\n\t\t\tmaskDrawInText |= maskDefinedMarkers & maskBit;\n\t\t\tbreak;\n\t\tdefault:\t// Other marker types do not affect the masks\n\t\t\tbreak;\n\t\t}\n\t}\n\tmaskDrawWrapped = 0;\n\tfor (int markBit = 0; markBit <= MarkerMax; markBit++) {\n\t\tconst int maskBit = 1U << markBit;\n\t\tswitch (markers[markBit].markType) {\n\t\tcase MarkerSymbol::Bar:\n\t\t\tmaskDrawWrapped |= maskBit;\n\t\t\tbreak;\n\t\tdefault:\t// Other marker types do not affect the masks\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\nvoid ViewStyle::Refresh(Surface &surface, int tabInChars) {\n\tfonts.clear();\n\n\tselbar = Platform::Chrome();\n\tselbarlight = Platform::ChromeHighlight();\n\n\t// Apply the extra font flag which controls text drawing quality to each style.\n\tfor (Style &style : styles) {\n\t\tstyle.extraFontFlag = extraFontFlag;\n\t}\n\n\t// Create a FontRealised object for each unique font in the styles.\n\tCreateAndAddFont(styles[StyleDefault]);\n\tfor (const Style &style : styles) {\n\t\tCreateAndAddFont(style);\n\t}\n\n\t// Ask platform to allocate each unique font.\n\tfor (const std::pair<const FontSpecification, std::unique_ptr<FontRealised>> &font : fonts) {\n\t\tfont.second->Realise(surface, zoomLevel, technology, font.first, localeName.c_str());\n\t}\n\n\t// Set the platform font handle and measurements for each style.\n\tfor (Style &style : styles) {\n\t\tconst FontRealised *fr = Find(style);\n\t\tstyle.Copy(fr->font, fr->measurements);\n\t}\n\n\tindicatorsDynamic = std::any_of(indicators.cbegin(), indicators.cend(),\n\t\t[](const Indicator &indicator) noexcept { return indicator.IsDynamic(); });\n\n\tindicatorsSetFore = std::any_of(indicators.cbegin(), indicators.cend(),\n\t\t[](const Indicator &indicator) noexcept { return indicator.OverridesTextFore(); });\n\n\tmaxAscent = 1;\n\tmaxDescent = 1;\n\tFindMaxAscentDescent();\n\t// Ensure reasonable values: lines less than 1 pixel high will not work\n\tmaxAscent = std::max(1.0, maxAscent + extraAscent);\n\tmaxDescent = std::max(0.0, maxDescent + extraDescent);\n\tlineHeight = static_cast<int>(std::lround(maxAscent + maxDescent));\n\tlineOverlap = lineHeight / 10;\n\tif (lineOverlap < 2)\n\t\tlineOverlap = 2;\n\tif (lineOverlap > lineHeight)\n\t\tlineOverlap = lineHeight;\n\n\tsomeStylesProtected = std::any_of(styles.cbegin(), styles.cend(),\n\t\t[](const Style &style) noexcept { return style.IsProtected(); });\n\n\tsomeStylesForceCase = std::any_of(styles.cbegin(), styles.cend(),\n\t\t[](const Style &style) noexcept { return style.caseForce != Style::CaseForce::mixed; });\n\n\taveCharWidth = styles[StyleDefault].aveCharWidth;\n\tspaceWidth = styles[StyleDefault].spaceWidth;\n\ttabWidth = spaceWidth * tabInChars;\n\n\tcontrolCharWidth = 0.0;\n\tif (controlCharSymbol >= 32) {\n\t\tconst char cc[2] = { static_cast<char>(controlCharSymbol), '\\0' };\n\t\tcontrolCharWidth = surface.WidthText(styles[StyleControlChar].font.get(), cc);\n\t}\n\n\tCalculateMarginWidthAndMask();\n\ttextStart = marginInside ? fixedColumnWidth : leftMarginWidth;\n}\n\nvoid ViewStyle::ReleaseAllExtendedStyles() noexcept {\n\tnextExtendedStyle = 256;\n}\n\nint ViewStyle::AllocateExtendedStyles(int numberStyles) {\n\tconst int startRange = nextExtendedStyle;\n\tnextExtendedStyle += numberStyles;\n\tEnsureStyle(nextExtendedStyle);\n\treturn startRange;\n}\n\nvoid ViewStyle::EnsureStyle(size_t index) {\n\tif (index >= styles.size()) {\n\t\tAllocStyles(index+1);\n\t}\n}\n\nvoid ViewStyle::ResetDefaultStyle() {\n\tstyles[StyleDefault] = Style(fontNames.Save(Platform::DefaultFont()));\n}\n\n//Au: added from and to. Replaced whole function.\n//info: to is not included.\nvoid ViewStyle::ClearStyles(size_t from, size_t to) {\n\tsize_t n = styles.size();\n\tif(to == 0 || to > n) to = n;\n\t// Reset all styles to be like the default style\n\tfor(size_t i = from; i < to; i++) {\n\t\tif(i != StyleDefault) {\n\t\t\tstyles[i] = styles[StyleDefault];\n\t\t}\n\t}\n\tstyles[StyleLineNumber].back = Platform::Chrome();\n\n\t// Set call tip fore/back to match the values previously set for call tips\n\tstyles[StyleCallTip].back = white;\n\tstyles[StyleCallTip].fore = ColourRGBA::Grey(mid);\n}\n//void ViewStyle::ClearStyles() {\n//\t// Reset all styles to be like the default style\n//\tfor (size_t i=0; i<styles.size(); i++) {\n//\t\tif (i != StyleDefault) {\n//\t\t\tstyles[i] = styles[StyleDefault];\n//\t\t}\n//\t}\n//\tstyles[StyleLineNumber].back = Platform::Chrome();\n//\n//\t// Set call tip fore/back to match the values previously set for call tips\n//\tstyles[StyleCallTip].back = white;\n//\tstyles[StyleCallTip].fore = ColourRGBA::Grey(mid);\n//}\n\nvoid ViewStyle::SetStyleFontName(int styleIndex, const char *name) {\n\tstyles[styleIndex].fontName = fontNames.Save(name);\n}\n\nvoid ViewStyle::SetFontLocaleName(const char *name) {\n\tlocaleName = name;\n}\n\nbool ViewStyle::ProtectionActive() const noexcept {\n\treturn someStylesProtected;\n}\n\nint ViewStyle::ExternalMarginWidth() const noexcept {\n\treturn marginInside ? 0 : fixedColumnWidth;\n}\n\nint ViewStyle::MarginFromLocation(Point pt) const noexcept {\n\tXYPOSITION x = marginInside ? 0 : -fixedColumnWidth;\n\tfor (size_t i = 0; i < ms.size(); i++) {\n\t\tif ((pt.x >= x) && (pt.x < x + ms[i].width))\n\t\t\treturn static_cast<int>(i);\n\t\tx += ms[i].width;\n\t}\n\treturn -1;\n}\n\nbool ViewStyle::ValidStyle(size_t styleIndex) const noexcept {\n\treturn styleIndex < styles.size();\n}\n\nvoid ViewStyle::CalcLargestMarkerHeight() noexcept {\n\tlargestMarkerHeight = 0;\n\tfor (const LineMarker &marker : markers) {\n\t\tswitch (marker.markType) {\n\t\tcase MarkerSymbol::Pixmap:\n\t\t\tif (marker.pxpm && marker.pxpm->GetHeight() > largestMarkerHeight)\n\t\t\t\tlargestMarkerHeight = marker.pxpm->GetHeight();\n\t\t\tbreak;\n\t\tcase MarkerSymbol::RgbaImage:\n\t\t\tif (marker.image && marker.image->GetHeight() > largestMarkerHeight)\n\t\t\t\tlargestMarkerHeight = marker.image->GetHeight();\n\t\t\tbreak;\n\t\tcase MarkerSymbol::Bar:\n\t\t\tlargestMarkerHeight = lineHeight + 2;\n\t\t\tbreak;\n\t\tdefault:\t// Only images have their own natural heights\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\nint ViewStyle::GetFrameWidth() const noexcept {\n\treturn std::clamp(caretLine.frame, 1, lineHeight / 3);\n}\n\nbool ViewStyle::IsLineFrameOpaque(bool caretActive, bool lineContainsCaret) const {\n\treturn caretLine.frame && (caretActive || caretLine.alwaysShow) &&\n\t\tElementColour(Element::CaretLineBack) &&\n\t\t(caretLine.layer == Layer::Base) && lineContainsCaret;\n}\n\n// See if something overrides the line background colour:  Either if caret is on the line\n// and background colour is set for that, or if a marker is defined that forces its background\n// colour onto the line, or if a marker is defined but has no selection margin in which to\n// display itself (as long as it's not an MarkerSymbol::Empty marker).  These are checked in order\n// with the earlier taking precedence.  When multiple markers cause background override,\n// the colour for the highest numbered one is used.\nColourOptional ViewStyle::Background(int marksOfLine, bool caretActive, bool lineContainsCaret) const {\n\tColourOptional background;\n\tif (!caretLine.frame && (caretActive || caretLine.alwaysShow) &&\n\t\t(caretLine.layer == Layer::Base) && lineContainsCaret) {\n\t\tbackground = ElementColour(Element::CaretLineBack);\n\t}\n\tif (!background && marksOfLine) {\n\t\tint marks = marksOfLine;\n\t\tfor (int markBit = 0; (markBit <= MarkerMax) && marks; markBit++) {\n\t\t\tif ((marks & 1) && (markers[markBit].markType == MarkerSymbol::Background) &&\n\t\t\t\t(markers[markBit].layer == Layer::Base)) {\n\t\t\t\tbackground = markers[markBit].back;\n\t\t\t}\n\t\t\tmarks >>= 1;\n\t\t}\n\t}\n\tif (!background && maskInLine) {\n\t\tint marksMasked = marksOfLine & maskInLine;\n\t\tif (marksMasked) {\n\t\t\tfor (int markBit = 0; (markBit <= MarkerMax) && marksMasked; markBit++) {\n\t\t\t\tif ((marksMasked & 1) &&\n\t\t\t\t\t(markers[markBit].layer == Layer::Base)) {\n\t\t\t\t\tbackground = markers[markBit].back;\n\t\t\t\t}\n\t\t\t\tmarksMasked >>= 1;\n\t\t\t}\n\t\t}\n\t}\n\tif (background) {\n\t\treturn background->Opaque();\n\t} else {\n\t\treturn {};\n\t}\n}\n\nbool ViewStyle::SelectionBackgroundDrawn() const noexcept {\n\treturn selection.layer == Layer::Base;\n}\n\nbool ViewStyle::SelectionTextDrawn() const {\n\treturn\n\t\tElementIsSet(Element::SelectionText) ||\n\t\tElementIsSet(Element::SelectionAdditionalText) ||\n\t\tElementIsSet(Element::SelectionSecondaryText) ||\n\t\tElementIsSet(Element::SelectionInactiveText) ||\n\t\tElementIsSet(Element::SelectionInactiveAdditionalText);\n}\n\nbool ViewStyle::WhitespaceBackgroundDrawn() const {\n\treturn (viewWhitespace != WhiteSpace::Invisible) && (ElementIsSet(Element::WhiteSpaceBack));\n}\n\nbool ViewStyle::WhiteSpaceVisible(bool inIndent) const noexcept {\n\treturn (!inIndent && viewWhitespace == WhiteSpace::VisibleAfterIndent) ||\n\t\t(inIndent && viewWhitespace == WhiteSpace::VisibleOnlyInIndent) ||\n\t\tviewWhitespace == WhiteSpace::VisibleAlways;\n}\n\nColourRGBA ViewStyle::WrapColour() const {\n\treturn ElementColour(Element::WhiteSpace).value_or(styles[StyleDefault].fore);\n}\n\n// Insert new edge in sorted order.\nvoid ViewStyle::AddMultiEdge(int column, ColourRGBA colour) {\n\ttheMultiEdge.insert(\n\t\tstd::upper_bound(theMultiEdge.begin(), theMultiEdge.end(), column,\n\t\t\t[](const EdgeProperties &a, const EdgeProperties &b) noexcept {\n\t\t\t\treturn a.column < b.column;\n\t\t\t}),\n\t\tEdgeProperties(column, colour));\n}\n\nColourOptional ViewStyle::ElementColour(Element element) const {\n\tconst ElementMap::const_iterator search = elementColours.find(element);\n\tif (search != elementColours.end()) {\n\t\tif (search->second.has_value()) {\n\t\t\treturn search->second;\n\t\t}\n\t}\n\tconst ElementMap::const_iterator searchBase = elementBaseColours.find(element);\n\tif (searchBase != elementBaseColours.end()) {\n\t\tif (searchBase->second.has_value()) {\n\t\t\treturn searchBase->second;\n\t\t}\n\t}\n\treturn {};\n}\n\nColourRGBA ViewStyle::ElementColourForced(Element element) const {\n\t// Like ElementColour but never returns empty - when not found return opaque black.\n\t// This method avoids warnings for unwrapping potentially empty optionals from\n\t// Visual C++ Code Analysis\n\tconst ColourOptional colour = ElementColour(element);\n\treturn colour.value_or(black);\n}\n\nbool ViewStyle::ElementAllowsTranslucent(Element element) const {\n\treturn elementAllowsTranslucent.count(element) > 0;\n}\n\nbool ViewStyle::ResetElement(Element element) {\n\tconst ElementMap::const_iterator search = elementColours.find(element);\n\tconst bool changed = (search != elementColours.end()) && (search->second.has_value());\n\telementColours.erase(element);\n\treturn changed;\n}\n\nnamespace {\n\nbool IsDifferentColour(const ColourOptional &colour, const ColourRGBA &test) noexcept {\n\treturn colour.has_value() && !(*colour == test);\n}\n\nbool SetElementMapColour(ViewStyle::ElementMap &elements, Element element, ColourRGBA colour) {\n\tconst ViewStyle::ElementMap::const_iterator search = elements.find(element);\n\tconst bool changed = (search == elements.end()) ||\n\t\tIsDifferentColour(search->second, colour);\n\telements[element] = colour;\n\treturn changed;\n}\n\n}\n\nbool ViewStyle::SetElementColour(Element element, ColourRGBA colour) {\n\treturn SetElementMapColour(elementColours, element, colour);\n}\n\nbool ViewStyle::SetElementColourOptional(Element element, uptr_t wParam, sptr_t lParam) {\n\tif (wParam) {\n\t\treturn SetElementColour(element, ColourRGBA::FromIpRGB(lParam));\n\t} else {\n\t\treturn ResetElement(element);\n\t}\n}\n\nvoid ViewStyle::SetElementRGB(Element element, int rgb) {\n\tconst ColourRGBA current = ElementColour(element).value_or(ColourRGBA(0, 0, 0, 0));\n\telementColours[element] = ColourRGBA(ColourRGBA(rgb), current.GetAlpha());\n}\n\nvoid ViewStyle::SetElementAlpha(Element element, int alpha) {\n\tconst ColourRGBA current = ElementColour(element).value_or(ColourRGBA(0, 0, 0, 0));\n\telementColours[element] = ColourRGBA(current, std::min<unsigned int>(alpha, maximumByte));\n}\n\nbool ViewStyle::ElementIsSet(Element element) const {\n\tconst ElementMap::const_iterator search = elementColours.find(element);\n\tif (search != elementColours.end()) {\n\t\treturn search->second.has_value();\n\t}\n\treturn false;\n}\n\nbool ViewStyle::SetElementBase(Element element, ColourRGBA colour) {\n\treturn SetElementMapColour(elementBaseColours, element, colour);\n}\n\nbool ViewStyle::SetWrapState(Wrap wrapState_) noexcept {\n\tconst bool changed = wrap.state != wrapState_;\n\twrap.state = wrapState_;\n\treturn changed;\n}\n\nbool ViewStyle::SetWrapVisualFlags(WrapVisualFlag wrapVisualFlags_) noexcept {\n\tconst bool changed = wrap.visualFlags != wrapVisualFlags_;\n\twrap.visualFlags = wrapVisualFlags_;\n\treturn changed;\n}\n\nbool ViewStyle::SetWrapVisualFlagsLocation(WrapVisualLocation wrapVisualFlagsLocation_) noexcept {\n\tconst bool changed = wrap.visualFlagsLocation != wrapVisualFlagsLocation_;\n\twrap.visualFlagsLocation = wrapVisualFlagsLocation_;\n\treturn changed;\n}\n\nbool ViewStyle::SetWrapVisualStartIndent(int wrapVisualStartIndent_) noexcept {\n\tconst bool changed = wrap.visualStartIndent != wrapVisualStartIndent_;\n\twrap.visualStartIndent = wrapVisualStartIndent_;\n\treturn changed;\n}\n\nbool ViewStyle::SetWrapIndentMode(WrapIndentMode wrapIndentMode_) noexcept {\n\tconst bool changed = wrap.indentMode != wrapIndentMode_;\n\twrap.indentMode = wrapIndentMode_;\n\treturn changed;\n}\n\nbool ViewStyle::IsBlockCaretStyle() const noexcept {\n\treturn ((caret.style & CaretStyle::InsMask) == CaretStyle::Block) ||\n\t\tFlagSet(caret.style, (CaretStyle::OverstrikeBlock | CaretStyle::Curses));\n}\n\nbool ViewStyle::IsCaretVisible(bool isMainSelection) const noexcept {\n\treturn caret.width > 0 &&\n\t\t((caret.style & CaretStyle::InsMask) != CaretStyle::Invisible ||\n\t\t(FlagSet(caret.style, CaretStyle::Curses) && !isMainSelection)); // only draw additional selections in curses mode\n}\n\nbool ViewStyle::DrawCaretInsideSelection(bool inOverstrike, bool imeCaretBlockOverride) const noexcept {\n\tif (FlagSet(caret.style, CaretStyle::BlockAfter))\n\t\treturn false;\n\treturn ((caret.style & CaretStyle::InsMask) == CaretStyle::Block) ||\n\t\t(inOverstrike && FlagSet(caret.style, CaretStyle::OverstrikeBlock)) ||\n\t\timeCaretBlockOverride ||\n\t\tFlagSet(caret.style, CaretStyle::Curses);\n}\n\nViewStyle::CaretShape ViewStyle::CaretShapeForMode(bool inOverstrike, bool isMainSelection) const noexcept {\n\tif (inOverstrike) {\n\t\treturn (FlagSet(caret.style, CaretStyle::OverstrikeBlock)) ? CaretShape::block : CaretShape::bar;\n\t}\n\n\tif (FlagSet(caret.style, CaretStyle::Curses) && !isMainSelection) {\n\t\treturn CaretShape::block;\n\t}\n\n\tconst CaretStyle caretStyle = caret.style & CaretStyle::InsMask;\n\treturn (caretStyle <= CaretStyle::Block) ? static_cast<CaretShape>(caretStyle) : CaretShape::line;\n}\n\nvoid ViewStyle::AllocStyles(size_t sizeNew) {\n\tsize_t i=styles.size();\n\tstyles.resize(sizeNew);\n\tif (styles.size() > StyleDefault) {\n\t\tfor (; i<sizeNew; i++) {\n\t\t\tif (i != StyleDefault) {\n\t\t\t\tstyles[i] = styles[StyleDefault];\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid ViewStyle::CreateAndAddFont(const FontSpecification &fs) {\n\tif (fs.fontName) {\n\t\tconst FontMap::iterator it = fonts.find(fs);\n\t\tif (it == fonts.end()) {\n\t\t\tfonts[fs] = std::make_unique<FontRealised>();\n\t\t}\n\t}\n}\n\nFontRealised *ViewStyle::Find(const FontSpecification &fs) {\n\tif (!fs.fontName)\t// Invalid specification so return arbitrary object\n\t\treturn fonts.begin()->second.get();\n\tconst FontMap::iterator it = fonts.find(fs);\n\tif (it != fonts.end()) {\n\t\t// Should always reach here since map was just set for all styles\n\t\treturn it->second.get();\n\t}\n\treturn nullptr;\n}\n\nvoid ViewStyle::FindMaxAscentDescent() noexcept {\n\tfor (size_t i = 0; i < styles.size(); i++) {\n\t\tif (i == StyleCallTip ||\n\t\t   (autocStyle != StyleDefault && i == static_cast<size_t>(autocStyle)))\n\t\t\tcontinue;\n\n\t\tconst auto &style = styles[i];\n\n\t\tif (maxAscent < style.ascent)\n\t\t\tmaxAscent = style.ascent;\n\t\tif (maxDescent < style.descent)\n\t\t\tmaxDescent = style.descent;\n\t}\n}\n"
  },
  {
    "path": "Libraries/scintilla/src/ViewStyle.h",
    "content": "// Scintilla source code edit control\n/** @file ViewStyle.h\n ** Store information on how the document is to be viewed.\n **/\n// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#ifndef VIEWSTYLE_H\n#define VIEWSTYLE_H\n\nnamespace Scintilla::Internal {\n\n/**\n */\nclass MarginStyle {\npublic:\n\tScintilla::MarginType style;\n\tColourRGBA back;\n\tint width;\n\tint mask;\n\tbool sensitive;\n\tScintilla::CursorShape cursor;\n\tMarginStyle(Scintilla::MarginType style_= Scintilla::MarginType::Symbol, int width_=0, int mask_=0) noexcept;\n\tbool ShowsFolding() const noexcept;\n};\n\n/**\n */\n\n\nclass FontRealised {\npublic:\n\tFontMeasurements measurements;\n\tstd::shared_ptr<Font> font;\n\tvoid Realise(Surface &surface, int zoomLevel, Scintilla::Technology technology, const FontSpecification &fs, const char *localeName);\n};\n\ntypedef std::map<FontSpecification, std::unique_ptr<FontRealised>> FontMap;\n\nusing ColourOptional = std::optional<ColourRGBA>;\n\ninline ColourOptional OptionalColour(Scintilla::uptr_t wParam, Scintilla::sptr_t lParam) noexcept {\n\tif (wParam) {\n\t\treturn ColourRGBA::FromIpRGB(lParam);\n\t} else {\n\t\treturn {};\n\t}\n}\n\nstruct SelectionAppearance {\n\t// Is the selection visible?\n\tbool visible = true;\n\t// Whether to draw on base layer or over text\n\tScintilla::Layer layer = Layer::Base;\n\t// Draw selection past line end characters up to right border\n\tbool eolFilled = false;\n};\n\nstruct CaretLineAppearance {\n\t// Whether to draw on base layer or over text\n\tScintilla::Layer layer = Layer::Base;\n\t// Also show when non-focused\n\tbool alwaysShow = false;\n\t// highlight sub line instead of whole line\n\tbool subLine = false;\n\t// Non-0: draw a rectangle around line instead of filling line. Value is pixel width of frame\n\tint frame = 0;\n};\n\nstruct CaretAppearance {\n\t// Line, block, over-strike bar ...\n\tScintilla::CaretStyle style = CaretStyle::Line;\n\t// Width in pixels\n\tint width = 1;\n};\n\nstruct WrapAppearance {\n\t// No wrapping, word, character, whitespace appearance\n\tScintilla::Wrap state = Wrap::None;\n\t// Show indication of wrap at line end, line start, or in margin\n\tScintilla::WrapVisualFlag visualFlags = WrapVisualFlag::None;\n\t// Show indication near margin or near text\n\tScintilla::WrapVisualLocation visualFlagsLocation = WrapVisualLocation::Default;\n\t// How much indentation to show wrapping\n\tint visualStartIndent = 0;\n\t// WrapIndentMode::Fixed, Same, Indent, DeepIndent\n\tScintilla::WrapIndentMode indentMode = WrapIndentMode::Fixed;\n};\n\nstruct EdgeProperties {\n\tint column = 0;\n\tColourRGBA colour;\n\tconstexpr EdgeProperties(int column_ = 0, ColourRGBA colour_ = ColourRGBA::FromRGB(0)) noexcept :\n\t\tcolumn(column_), colour(colour_) {\n\t}\n};\n\n// This is an old style enum so that its members can be used directly as indices without casting\nenum StyleIndices {\n\tStyleDefault = static_cast<int>(Scintilla::StylesCommon::Default),\n\tStyleLineNumber = static_cast<int>(Scintilla::StylesCommon::LineNumber),\n\tStyleBraceLight = static_cast<int>(Scintilla::StylesCommon::BraceLight),\n\tStyleBraceBad = static_cast<int>(Scintilla::StylesCommon::BraceBad),\n\tStyleControlChar = static_cast<int>(Scintilla::StylesCommon::ControlChar),\n\tStyleIndentGuide = static_cast<int>(Scintilla::StylesCommon::IndentGuide),\n\tStyleCallTip = static_cast<int>(Scintilla::StylesCommon::CallTip),\n\tStyleFoldDisplayText = static_cast<int>(Scintilla::StylesCommon::FoldDisplayText),\n};\n\n/**\n */\nclass ViewStyle {\n\tUniqueStringSet fontNames;\n\tFontMap fonts;\npublic:\n\tstd::vector<Style> styles;\n\tint nextExtendedStyle;\n\tstd::vector<LineMarker> markers;\n\tint largestMarkerHeight;\n\tstd::vector<Indicator> indicators;\n\tbool indicatorsDynamic;\n\tbool indicatorsSetFore;\n\tScintilla::Technology technology;\n\tint lineHeight;\n\tint lineOverlap;\n\tXYPOSITION maxAscent;\n\tXYPOSITION maxDescent;\n\tXYPOSITION aveCharWidth;\n\tXYPOSITION spaceWidth;\n\tXYPOSITION tabWidth;\n\n\tSelectionAppearance selection;\n\n\tint controlCharSymbol;\n\tXYPOSITION controlCharWidth;\n\tColourRGBA selbar;\n\tColourRGBA selbarlight;\n\tColourOptional foldmarginColour;\n\tColourOptional foldmarginHighlightColour;\n\tbool hotspotUnderline;\n\t/// Margins are ordered: Line Numbers, Selection Margin, Spacing Margin\n\tint leftMarginWidth;\t///< Spacing margin on left of text\n\tint rightMarginWidth;\t///< Spacing margin on right of text\n\tint maskInLine = 0;\t///< Mask for markers to be put into text because there is nowhere for them to go in margin\n\tint maskDrawInText = 0;\t///< Mask for markers that always draw in text\n\tint maskDrawWrapped = 0;\t///< Mask for markers that draw on wrapped lines\n\tstd::vector<MarginStyle> ms;\n\tint fixedColumnWidth = 0;\t///< Total width of margins\n\tbool marginInside;\t///< true: margin included in text view, false: separate views\n\tint textStart;\t///< Starting x position of text within the view\n\tint zoomLevel;\n\tScintilla::WhiteSpace viewWhitespace;\n\tScintilla::TabDrawMode tabDrawMode;\n\tint whitespaceSize;\n\tScintilla::IndentView viewIndentationGuides;\n\tbool viewEOL;\n\n\tCaretAppearance caret;\n\n\tCaretLineAppearance caretLine;\n\n\tbool someStylesProtected;\n\tbool someStylesForceCase;\n\tScintilla::FontQuality extraFontFlag;\n\tint extraAscent;\n\tint extraDescent;\n\tint marginStyleOffset;\n\tScintilla::AnnotationVisible annotationVisible;\n\tint annotationStyleOffset;\n\tScintilla::EOLAnnotationVisible eolAnnotationVisible;\n\tint eolAnnotationStyleOffset;\n\tbool braceHighlightIndicatorSet;\n\tint braceHighlightIndicator;\n\tbool braceBadLightIndicatorSet;\n\tint braceBadLightIndicator;\n\tScintilla::EdgeVisualStyle edgeState;\n\tEdgeProperties theEdge;\n\tstd::vector<EdgeProperties> theMultiEdge;\n\tint marginNumberPadding; // the right-side padding of the number margin\n\tint ctrlCharPadding; // the padding around control character text blobs\n\tint lastSegItalicsOffset; // the offset so as not to clip italic characters at EOLs\n\tint autocStyle;\n\n\tusing ElementMap = std::map<Scintilla::Element, ColourOptional>;\n\tElementMap elementColours;\n\tElementMap elementBaseColours;\n\tstd::set<Scintilla::Element> elementAllowsTranslucent;\n\n\tWrapAppearance wrap;\n\n\tstd::string localeName;\n\n\tViewStyle(size_t stylesSize_=256);\n\tViewStyle(const ViewStyle &source);\n\tViewStyle(ViewStyle &&) = delete;\n\t// Can only be copied through copy constructor which ensures font names initialised correctly\n\tViewStyle &operator=(const ViewStyle &) = delete;\n\tViewStyle &operator=(ViewStyle &&) = delete;\n\t~ViewStyle();\n\tvoid CalculateMarginWidthAndMask() noexcept;\n\tvoid Refresh(Surface &surface, int tabInChars);\n\tvoid ReleaseAllExtendedStyles() noexcept;\n\tint AllocateExtendedStyles(int numberStyles);\n\tvoid EnsureStyle(size_t index);\n\tvoid ResetDefaultStyle();\n\tvoid ClearStyles(size_t from, size_t to); //Au\n\tvoid SetStyleFontName(int styleIndex, const char *name);\n\tvoid SetFontLocaleName(const char *name);\n\tbool ProtectionActive() const noexcept;\n\tint ExternalMarginWidth() const noexcept;\n\tint MarginFromLocation(Point pt) const noexcept;\n\tbool ValidStyle(size_t styleIndex) const noexcept;\n\tvoid CalcLargestMarkerHeight() noexcept;\n\tint GetFrameWidth() const noexcept;\n\tbool IsLineFrameOpaque(bool caretActive, bool lineContainsCaret) const;\n\tColourOptional Background(int marksOfLine, bool caretActive, bool lineContainsCaret) const;\n\tbool SelectionBackgroundDrawn() const noexcept;\n\tbool SelectionTextDrawn() const;\n\tbool WhitespaceBackgroundDrawn() const;\n\tColourRGBA WrapColour() const;\n\n\tvoid AddMultiEdge(int column, ColourRGBA colour);\n\n\tColourOptional ElementColour(Scintilla::Element element) const;\n\tColourRGBA ElementColourForced(Scintilla::Element element) const;\n\tbool ElementAllowsTranslucent(Scintilla::Element element) const;\n\tbool ResetElement(Scintilla::Element element);\n\tbool SetElementColour(Scintilla::Element element, ColourRGBA colour);\n\tbool SetElementColourOptional(Scintilla::Element element, Scintilla::uptr_t wParam, Scintilla::sptr_t lParam);\n\tvoid SetElementRGB(Scintilla::Element element, int rgb);\n\tvoid SetElementAlpha(Scintilla::Element element, int alpha);\n\tbool ElementIsSet(Scintilla::Element element) const;\n\tbool SetElementBase(Scintilla::Element element, ColourRGBA colour);\n\n\tbool SetWrapState(Scintilla::Wrap wrapState_) noexcept;\n\tbool SetWrapVisualFlags(Scintilla::WrapVisualFlag wrapVisualFlags_) noexcept;\n\tbool SetWrapVisualFlagsLocation(Scintilla::WrapVisualLocation wrapVisualFlagsLocation_) noexcept;\n\tbool SetWrapVisualStartIndent(int wrapVisualStartIndent_) noexcept;\n\tbool SetWrapIndentMode(Scintilla::WrapIndentMode wrapIndentMode_) noexcept;\n\n\tbool WhiteSpaceVisible(bool inIndent) const noexcept;\n\n\tenum class CaretShape { invisible, line, block, bar };\n\tbool IsBlockCaretStyle() const noexcept;\n\tbool IsCaretVisible(bool isMainSelection) const noexcept;\n\tbool DrawCaretInsideSelection(bool inOverstrike, bool imeCaretBlockOverride) const noexcept;\n\tCaretShape CaretShapeForMode(bool inOverstrike, bool isMainSelection) const noexcept;\n\nprivate:\n\tvoid AllocStyles(size_t sizeNew);\n\tvoid CreateAndAddFont(const FontSpecification &fs);\n\tFontRealised *Find(const FontSpecification &fs);\n\tvoid FindMaxAscentDescent() noexcept;\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/src/XPM.cxx",
    "content": "// Scintilla source code edit control\n/** @file XPM.cxx\n ** Define a class that holds data in the X Pixmap (XPM) format.\n **/\n// Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#include <cstdlib>\n#include <cstdint>\n#include <cstring>\n#include <climits>\n\n#include <stdexcept>\n#include <string_view>\n#include <vector>\n#include <map>\n#include <optional>\n#include <algorithm>\n#include <iterator>\n#include <memory>\n\n#include \"ScintillaTypes.h\"\n\n#include \"Debugging.h\"\n#include \"Geometry.h\"\n#include \"Platform.h\"\n\n#include \"XPM.h\"\n\nusing namespace Scintilla;\nusing namespace Scintilla::Internal;\n\nnamespace {\n\nconst char *NextField(const char *s) noexcept {\n\t// In case there are leading spaces in the string\n\twhile (*s == ' ') {\n\t\ts++;\n\t}\n\twhile (*s && *s != ' ') {\n\t\ts++;\n\t}\n\twhile (*s == ' ') {\n\t\ts++;\n\t}\n\treturn s;\n}\n\n// Data lines in XPM can be terminated either with NUL or \"\nsize_t MeasureLength(const char *s) noexcept {\n\tsize_t i = 0;\n\twhile (s[i] && (s[i] != '\\\"'))\n\t\ti++;\n\treturn i;\n}\n\nunsigned int ValueOfHex(const char ch) noexcept {\n\tif (ch >= '0' && ch <= '9')\n\t\treturn ch - '0';\n\telse if (ch >= 'A' && ch <= 'F')\n\t\treturn ch - 'A' + 10;\n\telse if (ch >= 'a' && ch <= 'f')\n\t\treturn ch - 'a' + 10;\n\telse\n\t\treturn 0;\n}\n\nColourRGBA ColourFromHex(const char *val) noexcept {\n\tconst unsigned int r = ValueOfHex(val[0]) * 16 + ValueOfHex(val[1]);\n\tconst unsigned int g = ValueOfHex(val[2]) * 16 + ValueOfHex(val[3]);\n\tconst unsigned int b = ValueOfHex(val[4]) * 16 + ValueOfHex(val[5]);\n\treturn ColourRGBA(r, g, b);\n}\n\n}\n\n\nColourRGBA XPM::ColourFromCode(int ch) const noexcept {\n\treturn colourCodeTable[ch];\n}\n\nvoid XPM::FillRun(Surface *surface, int code, int startX, int y, int x) const {\n\tif ((code != codeTransparent) && (startX != x)) {\n\t\tconst PRectangle rc = PRectangle::FromInts(startX, y, x, y + 1);\n\t\tsurface->FillRectangle(rc, ColourFromCode(code));\n\t}\n}\n\nXPM::XPM(const char *textForm) {\n\tInit(textForm);\n}\n\nXPM::XPM(const char *const *linesForm) {\n\tInit(linesForm);\n}\n\nvoid XPM::Init(const char *textForm) {\n\t// Test done is two parts to avoid possibility of overstepping the memory\n\t// if memcmp implemented strangely. Must be 4 bytes at least at destination.\n\tif ((0 == memcmp(textForm, \"/* X\", 4)) && (0 == memcmp(textForm, \"/* XPM */\", 9))) {\n\t\t// Build the lines form out of the text form\n\t\tstd::vector<const char *> linesForm = LinesFormFromTextForm(textForm);\n\t\tif (!linesForm.empty()) {\n\t\t\tInit(linesForm.data());\n\t\t}\n\t} else {\n\t\t// It is really in line form\n\t\tInit(reinterpret_cast<const char * const *>(textForm));\n\t}\n}\n\nvoid XPM::Init(const char *const *linesForm) {\n\theight = 1;\n\twidth = 1;\n\tnColours = 1;\n\tpixels.clear();\n\tcodeTransparent = ' ';\n\tif (!linesForm)\n\t\treturn;\n\n\tstd::fill(colourCodeTable, std::end(colourCodeTable), black);\n\tconst char *line0 = linesForm[0];\n\twidth = atoi(line0);\n\tline0 = NextField(line0);\n\theight = atoi(line0);\n\tpixels.resize(width*height);\n\tline0 = NextField(line0);\n\tnColours = atoi(line0);\n\tline0 = NextField(line0);\n\tif (atoi(line0) != 1) {\n\t\t// Only one char per pixel is supported\n\t\treturn;\n\t}\n\n\tfor (int c=0; c<nColours; c++) {\n\t\tconst char *colourDef = linesForm[c+1];\n\t\tconst char code = colourDef[0];\n\t\tcolourDef += 4;\n\t\tColourRGBA colour(0, 0, 0, 0);\n\t\tif (*colourDef == '#') {\n\t\t\tcolour = ColourFromHex(colourDef+1);\n\t\t} else {\n\t\t\tcodeTransparent = code;\n\t\t}\n\t\tcolourCodeTable[static_cast<unsigned char>(code)] = colour;\n\t}\n\n\tfor (ptrdiff_t y=0; y<height; y++) {\n\t\tconst char *lform = linesForm[y+nColours+1];\n\t\tconst size_t len = MeasureLength(lform);\n\t\tfor (size_t x = 0; x<len; x++)\n\t\t\tpixels[y * width + x] = lform[x];\n\t}\n}\n\nvoid XPM::Draw(Surface *surface, const PRectangle &rc) {\n\tif (pixels.empty()) {\n\t\treturn;\n\t}\n\t// Centre the pixmap\n\tconst int startY = static_cast<int>(rc.top + (rc.Height() - height) / 2);\n\tconst int startX = static_cast<int>(rc.left + (rc.Width() - width) / 2);\n\tfor (int y=0; y<height; y++) {\n\t\tint prevCode = 0;\n\t\tint xStartRun = 0;\n\t\tfor (int x=0; x<width; x++) {\n\t\t\tconst int code = pixels[y * width + x];\n\t\t\tif (code != prevCode) {\n\t\t\t\tFillRun(surface, prevCode, startX + xStartRun, startY + y, startX + x);\n\t\t\t\txStartRun = x;\n\t\t\t\tprevCode = code;\n\t\t\t}\n\t\t}\n\t\tFillRun(surface, prevCode, startX + xStartRun, startY + y, startX + width);\n\t}\n}\n\nColourRGBA XPM::PixelAt(int x, int y) const noexcept {\n\tif (pixels.empty() || (x < 0) || (x >= width) || (y < 0) || (y >= height)) {\n\t\t// Out of bounds -> transparent black\n\t\treturn ColourRGBA(0, 0, 0, 0);\n\t}\n\tconst int code = pixels[y * width + x];\n\treturn ColourFromCode(code);\n}\n\nstd::vector<const char *> XPM::LinesFormFromTextForm(const char *textForm) {\n\t// Build the lines form out of the text form\n\tstd::vector<const char *> linesForm;\n\tint countQuotes = 0;\n\tint strings=1;\n\tint j=0;\n\tfor (; countQuotes < (2*strings) && textForm[j] != '\\0'; j++) {\n\t\tif (textForm[j] == '\\\"') {\n\t\t\tif (countQuotes == 0) {\n\t\t\t\t// First field: width, height, number of colours, chars per pixel\n\t\t\t\tconst char *line0 = textForm + j + 1;\n\t\t\t\t// Skip width\n\t\t\t\tline0 = NextField(line0);\n\t\t\t\t// Add 1 line for each pixel of height\n\t\t\t\tstrings += atoi(line0);\n\t\t\t\tline0 = NextField(line0);\n\t\t\t\t// Add 1 line for each colour\n\t\t\t\tstrings += atoi(line0);\n\t\t\t}\n\t\t\tif (countQuotes / 2 >= strings) {\n\t\t\t\tbreak;\t// Bad height or number of colours!\n\t\t\t}\n\t\t\tif ((countQuotes & 1) == 0) {\n\t\t\t\tlinesForm.push_back(textForm + j + 1);\n\t\t\t}\n\t\t\tcountQuotes++;\n\t\t}\n\t}\n\tif (textForm[j] == '\\0' || countQuotes / 2 > strings) {\n\t\t// Malformed XPM! Height + number of colours too high or too low\n\t\tlinesForm.clear();\n\t}\n\treturn linesForm;\n}\n\nRGBAImage::RGBAImage(int width_, int height_, float scale_, const unsigned char *pixels_) :\n\theight(height_), width(width_), scale(scale_) {\n\tif (pixels_) {\n\t\tpixelBytes.assign(pixels_, pixels_ + CountBytes());\n\t} else {\n\t\tpixelBytes.resize(CountBytes());\n\t}\n}\n\nRGBAImage::RGBAImage(const XPM &xpm) {\n\theight = xpm.GetHeight();\n\twidth = xpm.GetWidth();\n\tscale = 1;\n\tpixelBytes.resize(CountBytes());\n\tfor (int y=0; y<height; y++) {\n\t\tfor (int x=0; x<width; x++) {\n\t\t\tSetPixel(x, y, xpm.PixelAt(x, y));\n\t\t}\n\t}\n}\n\nfloat RGBAImage::GetScaledHeight() const noexcept {\n\treturn static_cast<float>(height) / scale;\n}\n\nfloat RGBAImage::GetScaledWidth() const noexcept {\n\treturn static_cast<float>(width) / scale;\n}\n\nint RGBAImage::CountBytes() const noexcept {\n\treturn width * height * 4;\n}\n\nconst unsigned char *RGBAImage::Pixels() const noexcept {\n\treturn pixelBytes.data();\n}\n\nvoid RGBAImage::SetPixel(int x, int y, ColourRGBA colour) noexcept {\n\tunsigned char *pixel = pixelBytes.data() + (y * width + x) * 4;\n\t// RGBA\n\tpixel[0] = colour.GetRed();\n\tpixel[1] = colour.GetGreen();\n\tpixel[2] = colour.GetBlue();\n\tpixel[3] = colour.GetAlpha();\n}\n\nnamespace {\n\nconstexpr unsigned char AlphaMultiplied(unsigned char value, unsigned char alpha) noexcept {\n\treturn (value * alpha / UCHAR_MAX) & 0xffU;\n}\n\n}\n\n// Transform a block of pixels from RGBA to BGRA with premultiplied alpha.\n// Used for DrawRGBAImage on some platforms.\nvoid RGBAImage::BGRAFromRGBA(unsigned char *pixelsBGRA, const unsigned char *pixelsRGBA, size_t count) noexcept {\n\tfor (size_t i = 0; i < count; i++) {\n\t\tconst unsigned char alpha = pixelsRGBA[3];\n\t\t// Input is RGBA, output is BGRA with premultiplied alpha\n\t\tpixelsBGRA[2] = AlphaMultiplied(pixelsRGBA[0], alpha);\n\t\tpixelsBGRA[1] = AlphaMultiplied(pixelsRGBA[1], alpha);\n\t\tpixelsBGRA[0] = AlphaMultiplied(pixelsRGBA[2], alpha);\n\t\tpixelsBGRA[3] = alpha;\n\t\tpixelsRGBA += bytesPerPixel;\n\t\tpixelsBGRA += bytesPerPixel;\n\t}\n}\n\nRGBAImageSet::RGBAImageSet() : height(-1), width(-1) {\n}\n\n/// Remove all images.\nvoid RGBAImageSet::Clear() noexcept {\n\timages.clear();\n\theight = -1;\n\twidth = -1;\n}\n\n/// Add an image.\nvoid RGBAImageSet::AddImage(int ident, std::unique_ptr<RGBAImage> image) {\n\timages[ident] = std::move(image);\n\theight = -1;\n\twidth = -1;\n}\n\n/// Get image by id.\nRGBAImage *RGBAImageSet::Get(int ident) {\n\tImageMap::iterator it = images.find(ident);\n\tif (it != images.end()) {\n\t\treturn it->second.get();\n\t}\n\treturn nullptr;\n}\n\n/// Give the largest height of the set.\nint RGBAImageSet::GetHeight() const noexcept {\n\tif (height < 0) {\n\t\tfor (const std::pair<const int, std::unique_ptr<RGBAImage>> &image : images) {\n\t\t\tif (height < image.second->GetHeight()) {\n\t\t\t\theight = image.second->GetHeight();\n\t\t\t}\n\t\t}\n\t}\n\treturn (height > 0) ? height : 0;\n}\n\n/// Give the largest width of the set.\nint RGBAImageSet::GetWidth() const noexcept {\n\tif (width < 0) {\n\t\tfor (const std::pair<const int, std::unique_ptr<RGBAImage>> &image : images) {\n\t\t\tif (width < image.second->GetWidth()) {\n\t\t\t\twidth = image.second->GetWidth();\n\t\t\t}\n\t\t}\n\t}\n\treturn (width > 0) ? width : 0;\n}\n"
  },
  {
    "path": "Libraries/scintilla/src/XPM.h",
    "content": "// Scintilla source code edit control\n/** @file XPM.h\n ** Define a classes to hold image data in the X Pixmap (XPM) and RGBA formats.\n **/\n// Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#ifndef XPM_H\n#define XPM_H\n\nnamespace Scintilla::Internal {\n\n/**\n * Hold a pixmap in XPM format.\n */\nclass XPM {\n\tint height=1;\n\tint width=1;\n\tint nColours=1;\n\tstd::vector<unsigned char> pixels;\n\tColourRGBA colourCodeTable[256];\n\tchar codeTransparent=' ';\n\tColourRGBA ColourFromCode(int ch) const noexcept;\n\tvoid FillRun(Surface *surface, int code, int startX, int y, int x) const;\npublic:\n\texplicit XPM(const char *textForm);\n\texplicit XPM(const char *const *linesForm);\n\tvoid Init(const char *textForm);\n\tvoid Init(const char *const *linesForm);\n\t/// Decompose image into runs and use FillRectangle for each run\n\tvoid Draw(Surface *surface, const PRectangle &rc);\n\tint GetHeight() const noexcept { return height; }\n\tint GetWidth() const noexcept { return width; }\n\tColourRGBA PixelAt(int x, int y) const noexcept;\nprivate:\n\tstatic std::vector<const char *>LinesFormFromTextForm(const char *textForm);\n};\n\n/**\n * A translucent image stored as a sequence of RGBA bytes.\n */\nclass RGBAImage {\n\tint height;\n\tint width;\n\tfloat scale;\n\tstd::vector<unsigned char> pixelBytes;\npublic:\n\tstatic constexpr size_t bytesPerPixel = 4;\n\tRGBAImage(int width_, int height_, float scale_, const unsigned char *pixels_);\n\texplicit RGBAImage(const XPM &xpm);\n\tint GetHeight() const noexcept { return height; }\n\tint GetWidth() const noexcept { return width; }\n\tfloat GetScale() const noexcept { return scale; }\n\tfloat GetScaledHeight() const noexcept;\n\tfloat GetScaledWidth() const noexcept;\n\tint CountBytes() const noexcept;\n\tconst unsigned char *Pixels() const noexcept;\n\tvoid SetPixel(int x, int y, ColourRGBA colour) noexcept;\n\tstatic void BGRAFromRGBA(unsigned char *pixelsBGRA, const unsigned char *pixelsRGBA, size_t count) noexcept;\n};\n\n/**\n * A collection of RGBAImage pixmaps indexed by integer id.\n */\nclass RGBAImageSet {\n\ttypedef std::map<int, std::unique_ptr<RGBAImage>> ImageMap;\n\tImageMap images;\n\tmutable int height;\t///< Memorize largest height of the set.\n\tmutable int width;\t///< Memorize largest width of the set.\npublic:\n\tRGBAImageSet();\n\t/// Remove all images.\n\tvoid Clear() noexcept;\n\t/// Add an image.\n\tvoid AddImage(int ident, std::unique_ptr<RGBAImage> image);\n\t/// Get image by id.\n\tRGBAImage *Get(int ident);\n\t/// Give the largest height of the set.\n\tint GetHeight() const noexcept;\n\t/// Give the largest width of the set.\n\tint GetWidth() const noexcept;\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/version.txt",
    "content": "557\n"
  },
  {
    "path": "Libraries/scintilla/win32/HanjaDic.cxx",
    "content": "// Scintilla source code edit control\n/** @file HanjaDic.cxx\n ** Korean Hanja Dictionary\n ** Convert between Korean Hanja and Hangul by COM interface.\n **/\n// Copyright 2015 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#include <string>\n#include <string_view>\n#include <memory>\n\n#define WIN32_LEAN_AND_MEAN 1\n#include <windows.h>\n#include <ole2.h>\n\n#include \"WinTypes.h\"\n#include \"HanjaDic.h\"\n\nnamespace Scintilla::Internal::HanjaDict {\n\ninterface IRadical;\ninterface IHanja;\ninterface IStrokes;\n\nenum HANJA_TYPE { HANJA_UNKNOWN = 0, HANJA_K0 = 1, HANJA_K1 = 2, HANJA_OTHER = 3 };\n\ninterface IHanjaDic : IUnknown {\n\tSTDMETHOD(OpenMainDic)();\n\tSTDMETHOD(CloseMainDic)();\n\tSTDMETHOD(GetHanjaWords)(BSTR bstrHangul, SAFEARRAY* ppsaHanja, VARIANT_BOOL* pfFound);\n\tSTDMETHOD(GetHanjaChars)(unsigned short wchHangul, BSTR* pbstrHanjaChars, VARIANT_BOOL* pfFound);\n\tSTDMETHOD(HanjaToHangul)(BSTR bstrHanja, BSTR* pbstrHangul);\n\tSTDMETHOD(GetHanjaType)(unsigned short wchHanja, HANJA_TYPE* pHanjaType);\n\tSTDMETHOD(GetHanjaSense)(unsigned short wchHanja, BSTR* pbstrSense);\n\tSTDMETHOD(GetRadicalID)(short SeqNumOfRadical, short* pRadicalID, unsigned short* pwchRadical);\n\tSTDMETHOD(GetRadical)(short nRadicalID, IRadical** ppIRadical);\n\tSTDMETHOD(RadicalIDToHanja)(short nRadicalID, unsigned short* pwchRadical);\n\tSTDMETHOD(GetHanja)(unsigned short wchHanja, IHanja** ppIHanja);\n\tSTDMETHOD(GetStrokes)(short nStrokes, IStrokes** ppIStrokes);\n\tSTDMETHOD(OpenDefaultCustomDic)();\n\tSTDMETHOD(OpenCustomDic)(BSTR bstrPath, long* plUdr);\n\tSTDMETHOD(CloseDefaultCustomDic)();\n\tSTDMETHOD(CloseCustomDic)(long lUdr);\n\tSTDMETHOD(CloseAllCustomDics)();\n\tSTDMETHOD(GetDefaultCustomHanjaWords)(BSTR bstrHangul, SAFEARRAY** ppsaHanja, VARIANT_BOOL* pfFound);\n\tSTDMETHOD(GetCustomHanjaWords)(long lUdr, BSTR bstrHangul, SAFEARRAY** ppsaHanja, VARIANT_BOOL* pfFound);\n\tSTDMETHOD(PutDefaultCustomHanjaWord)(BSTR bstrHangul, BSTR bstrHanja);\n\tSTDMETHOD(PutCustomHanjaWord)(long lUdr, BSTR bstrHangul, BSTR bstrHanja);\n\tSTDMETHOD(MaxNumOfRadicals)(short* pVal);\n\tSTDMETHOD(MaxNumOfStrokes)(short* pVal);\n\tSTDMETHOD(DefaultCustomDic)(long* pVal);\n\tSTDMETHOD(DefaultCustomDic)(long pVal);\n\tSTDMETHOD(MaxHanjaType)(HANJA_TYPE* pHanjaType);\n\tSTDMETHOD(MaxHanjaType)(HANJA_TYPE pHanjaType);\n};\n\nextern \"C\" const GUID __declspec(selectany) IID_IHanjaDic =\n{ 0xad75f3ac, 0x18cd, 0x48c6, { 0xa2, 0x7d, 0xf1, 0xe9, 0xa7, 0xdc, 0xe4, 0x32 } };\n\nclass ScopedBSTR {\n\tBSTR bstr = nullptr;\npublic:\n\tScopedBSTR() noexcept = default;\n\texplicit ScopedBSTR(const OLECHAR *psz) noexcept :\n\t\tbstr(SysAllocString(psz)) {\n\t}\n\texplicit ScopedBSTR(OLECHAR character) noexcept :\n\t\tbstr(SysAllocStringLen(&character, 1)) {\n\t}\n\t// Deleted so ScopedBSTR objects can not be copied. Moves are OK.\n\tScopedBSTR(const ScopedBSTR &) = delete;\n\tScopedBSTR &operator=(const ScopedBSTR &) = delete;\n\t// Moves are OK.\n\tScopedBSTR(ScopedBSTR &&) = default;\n\tScopedBSTR &operator=(ScopedBSTR &&) = default;\n\t~ScopedBSTR() {\n\t\tSysFreeString(bstr);\n\t}\n\n\tBSTR get() const noexcept {\n\t\treturn bstr;\n\t}\n\tvoid reset(BSTR value=nullptr) noexcept {\n\t\t// https://en.cppreference.com/w/cpp/memory/unique_ptr/reset\n\t\tBSTR const old = bstr;\n\t\tbstr = value;\n\t\tSysFreeString(old);\n\t}\n};\n\nclass HanjaDic {\n\tstd::unique_ptr<IHanjaDic, UnknownReleaser> HJinterface;\n\n\tbool OpenHanjaDic(LPCOLESTR lpszProgID) noexcept {\n\t\tCLSID CLSID_HanjaDic;\n\t\tHRESULT hr = CLSIDFromProgID(lpszProgID, &CLSID_HanjaDic);\n\t\tif (SUCCEEDED(hr)) {\n\t\t\tIHanjaDic *instance = nullptr;\n\t\t\thr = CoCreateInstance(CLSID_HanjaDic, nullptr,\n\t\t\t\tCLSCTX_INPROC_SERVER, IID_IHanjaDic,\n\t\t\t\treinterpret_cast<LPVOID *>(&instance));\n\t\t\tif (SUCCEEDED(hr) && instance) {\n\t\t\t\tHJinterface.reset(instance);\n\t\t\t\thr = instance->OpenMainDic();\n\t\t\t\treturn SUCCEEDED(hr);\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\npublic:\n\tbool Open() noexcept {\n\t\treturn OpenHanjaDic(OLESTR(\"imkrhjd.hanjadic\"))\n\t\t\t|| OpenHanjaDic(OLESTR(\"mshjdic.hanjadic\"));\n\t}\n\n\tvoid Close() const noexcept {\n\t\tHJinterface->CloseMainDic();\n\t}\n\n\tbool IsHanja(wchar_t hanja) const noexcept {\n\t\tHANJA_TYPE hanjaType = HANJA_UNKNOWN;\n\t\tconst HRESULT hr = HJinterface->GetHanjaType(hanja, &hanjaType);\n\t\treturn SUCCEEDED(hr) && hanjaType > HANJA_UNKNOWN;\n\t}\n\n\tbool HanjaToHangul(const ScopedBSTR &bstrHanja, ScopedBSTR &bstrHangul) const noexcept {\n\t\tBSTR result = nullptr;\n\t\tconst HRESULT hr = HJinterface->HanjaToHangul(bstrHanja.get(), &result);\n\t\tbstrHangul.reset(result);\n\t\treturn SUCCEEDED(hr);\n\t}\n};\n\nbool GetHangulOfHanja(std::wstring &inout) noexcept {\n\t// Convert every hanja to hangul.\n\t// Return whether any character been converted.\n\t// Hanja linked to different notes in Hangul have different codes,\n\t// so current character based conversion is enough.\n\t// great thanks for BLUEnLIVE.\n\tbool changed = false;\n\tHanjaDic dict;\n\tif (dict.Open()) {\n\t\tfor (wchar_t &character : inout) {\n\t\t\tif (dict.IsHanja(character)) { // Pass hanja only!\n\t\t\t\tScopedBSTR bstrHangul;\n\t\t\t\tif (dict.HanjaToHangul(ScopedBSTR(character), bstrHangul)) {\n\t\t\t\t\tchanged = true;\n\t\t\t\t\tcharacter = *(bstrHangul.get());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tdict.Close();\n\t}\n\treturn changed;\n}\n\n}\n"
  },
  {
    "path": "Libraries/scintilla/win32/HanjaDic.h",
    "content": "// Scintilla source code edit control\n/** @file HanjaDic.h\n ** Korean Hanja Dictionary\n ** Convert between Korean Hanja and Hangul by COM interface.\n **/\n// Copyright 2015 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#ifndef HANJADIC_H\n#define HANJADIC_H\n\nnamespace Scintilla::Internal {\n\nnamespace HanjaDict {\n\nbool GetHangulOfHanja(std::wstring &inout) noexcept;\n\n}\n\n}\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/win32/ListBox.cxx",
    "content": "// Scintilla source code edit control\n/** @file ListBox.cxx\n ** Implementation of list box on Windows.\n **/\n// Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#include <cstddef>\n#include <cstdlib>\n#include <cstdint>\n#include <cstring>\n#include <cstdio>\n#include <cstdarg>\n#include <ctime>\n#include <cmath>\n#include <climits>\n\n#include <string_view>\n#include <vector>\n#include <map>\n#include <optional>\n#include <algorithm>\n#include <iterator>\n#include <memory>\n#include <mutex>\n\n// Want to use std::min and std::max so don't want Windows.h version of min and max\n#if !defined(NOMINMAX)\n#define NOMINMAX\n#endif\n#undef _WIN32_WINNT\n#define _WIN32_WINNT 0x0A00\n#undef WINVER\n#define WINVER 0x0A00\n#define WIN32_LEAN_AND_MEAN 1\n#include <windows.h>\n#include <commctrl.h>\n#include <richedit.h>\n#include <windowsx.h>\n#include <shellscalingapi.h>\n\n#include <wrl.h>\nusing Microsoft::WRL::ComPtr;\n\n#if !defined(DISABLE_D2D)\n#define USE_D2D 1\n#endif\n\n#if defined(USE_D2D)\n#include <d2d1_1.h>\n#include <d3d11_1.h>\n#include <dwrite_1.h>\n#endif\n\n#include \"ScintillaTypes.h\"\n\n#include \"Debugging.h\"\n#include \"Geometry.h\"\n#include \"Platform.h\"\n#include \"XPM.h\"\n#include \"UniConversion.h\"\n#include \"DBCS.h\"\n\n#include \"WinTypes.h\"\n#include \"PlatWin.h\"\n#include \"ListBox.h\"\n#if defined(USE_D2D)\n#include \"SurfaceD2D.h\"\n#endif\n\nusing namespace Scintilla;\nusing namespace Scintilla::Internal;\n\nnamespace {\n\nvoid *PtrFromLParam(Scintilla::sptr_t lParam) noexcept {\n\treturn reinterpret_cast<void *>(lParam);\n}\n\nstruct ListItemData {\n\tconst char *text;\n\tint pixId;\n};\n\nclass LineToItem {\n\tstd::vector<char> words;\n\n\tstd::vector<ListItemData> data;\n\npublic:\n\tvoid Clear() noexcept {\n\t\twords.clear();\n\t\tdata.clear();\n\t}\n\n\t[[nodiscard]] ListItemData Get(size_t index) const noexcept {\n\t\tif (index < data.size()) {\n\t\t\treturn data[index];\n\t\t}\n\t\tListItemData missing = {\"\", -1};\n\t\treturn missing;\n\t}\n\t[[nodiscard]] int Count() const noexcept {\n\t\treturn static_cast<int>(data.size());\n\t}\n\n\tvoid AllocItem(const char *text, int pixId) {\n\t\tconst ListItemData lid = { text, pixId };\n\t\tdata.push_back(lid);\n\t}\n\n\tchar *SetWords(const char *s) {\n\t\twords = std::vector<char>(s, s+strlen(s)+1);\n\t\treturn words.data();\n\t}\n};\n\nconst TCHAR ListBoxX_ClassName[] = TEXT(\"ListBoxX\");\n\nColourRGBA ColourElement(std::optional<ColourRGBA> colour, int nIndex) {\n\tif (colour.has_value()) {\n\t\treturn colour.value();\n\t}\n\treturn ColourFromSys(nIndex);\n}\n\nstruct LBGraphics {\n\tGDIBitMap bm;\n\tstd::unique_ptr<Surface> pixmapLine;\n#if defined(USE_D2D)\n\tDCRenderTarget pBMDCTarget;\n#endif\n\n\tvoid Release() noexcept {\n\t\tpixmapLine.reset();\n#if defined(USE_D2D)\n\t\tpBMDCTarget = nullptr;\n#endif\n\t\tbm.Release();\n\t}\n};\n\n}\n\nclass ListBoxX : public ListBox {\n\tint lineHeight = 10;\n\tHFONT fontCopy {};\n\tstd::unique_ptr<FontWin> fontWin;\n\tTechnology technology = Technology::Default;\n\tRGBAImageSet images;\n\tLineToItem lti;\n\tHWND lb {};\n\tbool unicodeMode = false;\n\tint codePage = 0;\n\tint desiredVisibleRows = 9;\n\tint maxItemCharacters = 0;\n\tunsigned int aveCharWidth = 8;\n\tWindow *parent = nullptr;\n\tWNDPROC prevWndProc{};\n\tint ctrlID = 0;\n\tUINT dpi = USER_DEFAULT_SCREEN_DPI;\n\tIListBoxDelegate *delegate = nullptr;\n\tunsigned int maxCharWidth = 1;\n\tWPARAM resizeHit = 0;\n\tPRectangle rcPreSize;\n\tPoint dragOffset;\n\tPoint location;\t// Caret location at which the list is opened\n\tMouseWheelDelta wheelDelta;\n\tListOptions options;\n\tDWORD frameStyle = WS_THICKFRAME;\n\n\tLBGraphics graphics;\n\n\tHWND GetHWND() const noexcept;\n\tvoid AppendListItem(const char *text, const char *numword);\n\tvoid AdjustWindowRect(PRectangle *rc, UINT dpiAdjust) const noexcept;\n\tint ItemHeight() const noexcept;\n\tint MinClientWidth() const noexcept;\n\tint TextOffset() const noexcept;\n\tPOINT GetClientExtent() const noexcept;\n\tPOINT MinTrackSize() const noexcept;\n\tPOINT MaxTrackSize() const noexcept;\n\tvoid SetRedraw(bool on) noexcept;\n\tvoid OnDoubleClick();\n\tvoid OnSelChange();\n\tvoid ResizeToCursor();\n\tvoid StartResize(WPARAM);\n\tLRESULT NcHitTest(WPARAM, LPARAM) const;\n\tvoid CentreItem(int n);\n\tvoid AllocateBitMap();\n\tLRESULT PASCAL ListProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam);\n\tstatic LRESULT PASCAL ControlWndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam);\n\n\tstatic constexpr POINT ItemInset {0, 0};\t// Padding around whole item\n\tstatic constexpr POINT TextInset {2, 0};\t// Padding around text\n\tstatic constexpr POINT ImageInset {1, 0};\t// Padding around image\n\npublic:\n\tListBoxX() = default;\n\tListBoxX(const ListBoxX &) = delete;\n\tListBoxX(ListBoxX &&) = delete;\n\tListBoxX &operator=(const ListBoxX &) = delete;\n\tListBoxX &operator=(ListBoxX &&) = delete;\n\t~ListBoxX() noexcept override {\n\t\tif (fontCopy) {\n\t\t\t::DeleteObject(fontCopy);\n\t\t\tfontCopy = {};\n\t\t}\n\t\tgraphics.Release();\n\t}\n\tvoid SetFont(const Font *font) override;\n\tvoid Create(Window &parent_, int ctrlID_, Point location_, int lineHeight_, bool unicodeMode_, Technology technology_) override;\n\tvoid SetAverageCharWidth(int width) override;\n\tvoid SetVisibleRows(int rows) override;\n\tint GetVisibleRows() const override;\n\tPRectangle GetDesiredRect() override;\n\tint CaretFromEdge() override;\n\tvoid Clear() noexcept override;\n\tvoid Append(char *s, int type) override;\n\tint Length() override;\n\tvoid Select(int n) override;\n\tint GetSelection() override;\n\tint Find(const char *prefix) override;\n\tstd::string GetValue(int n) override;\n\tvoid RegisterImage(int type, const char *xpm_data) override;\n\tvoid RegisterRGBAImage(int type, int width, int height, const unsigned char *pixelsImage) override;\n\tvoid ClearRegisteredImages() override;\n\tvoid SetDelegate(IListBoxDelegate *lbDelegate) override;\n\tvoid SetList(const char *list, char separator, char typesep) override;\n\tvoid SetOptions(ListOptions options_) override;\n\tvoid Draw(DRAWITEMSTRUCT *pDrawItem);\n\tLRESULT WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam);\n\tstatic LRESULT PASCAL StaticWndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam);\n};\n\nstd::unique_ptr<ListBox> ListBox::Allocate() {\n\treturn std::make_unique<ListBoxX>();\n}\n\nvoid ListBoxX::Create(Window &parent_, int ctrlID_, Point location_, int lineHeight_, bool unicodeMode_, Technology technology_) {\n\tparent = &parent_;\n\tctrlID = ctrlID_;\n\tlocation = location_;\n\tlineHeight = lineHeight_;\n\tunicodeMode = unicodeMode_;\n\tcodePage = unicodeMode ? CpUtf8 : 0;\n\ttechnology = technology_;\n\tHWND hwndParent = HwndFromWindow(*parent);\n\tHINSTANCE hinstanceParent = GetWindowInstance(hwndParent);\n\t// Window created as popup so not clipped within parent client area\n\twid = ::CreateWindowEx(\n\t\tWS_EX_WINDOWEDGE, ListBoxX_ClassName, TEXT(\"\"),\n\t\tWS_POPUP | frameStyle,\n\t\t100,100, 150,80, hwndParent,\n\t\t{},\n\t\thinstanceParent,\n\t\tthis);\n\n\tdpi = DpiForWindow(hwndParent);\n\tPOINT locationw = POINTFromPoint(location);\n\t::MapWindowPoints(hwndParent, {}, &locationw, 1);\n\tlocation = PointFromPOINT(locationw);\n}\n\nvoid ListBoxX::SetFont(const Font *font) {\n\tconst FontWin *pfm = dynamic_cast<const FontWin *>(font);\n\tif (pfm) {\n\t\tif (fontCopy) {\n\t\t\t::DeleteObject(fontCopy);\n\t\t\tfontCopy = {};\n\t\t}\n\t\tfontCopy = pfm->HFont();\n\t\tSetWindowFont(lb, fontCopy, 0);\n\t\tfontWin = pfm->Duplicate();\n\t\tcodePage = unicodeMode ? CpUtf8 : CodePageFromCharSet(fontWin->GetCharacterSet(), 1252);\n\t\tgraphics.Release();\n\t}\n}\n\nvoid ListBoxX::SetAverageCharWidth(int width) {\n\taveCharWidth = width;\n}\n\nvoid ListBoxX::SetVisibleRows(int rows) {\n\tdesiredVisibleRows = rows;\n}\n\nint ListBoxX::GetVisibleRows() const {\n\treturn desiredVisibleRows;\n}\n\nHWND ListBoxX::GetHWND() const noexcept {\n\treturn HwndFromWindowID(GetID());\n}\n\nPRectangle ListBoxX::GetDesiredRect() {\n\tPRectangle rcDesired = GetPosition();\n\n\tint rows = Length();\n\tif ((rows == 0) || (rows > desiredVisibleRows))\n\t\trows = desiredVisibleRows;\n\trcDesired.bottom = rcDesired.top + ItemHeight() * rows;\n\n\tint width = MinClientWidth();\n\tint textSize = 0;\n\tint averageCharWidth = 8;\n\n\t// Make a measuring surface\n\tstd::unique_ptr<Surface> surfaceItem(Surface::Allocate(technology));\n\tsurfaceItem->Init(GetID());\n\tsurfaceItem->SetMode(SurfaceMode(codePage, false));\n\n\t// Find the widest item in pixels\n\tconst int items = lti.Count();\n\tfor (int i = 0; i < items; i++) {\n\t\tconst ListItemData item = lti.Get(i);\n\t\tconst int itemTextSize = static_cast<int>(std::ceil(\n\t\t\tsurfaceItem->WidthText(fontWin.get(), item.text)));\n\t\ttextSize = std::max(textSize, itemTextSize);\n\t}\n\n\tmaxCharWidth = static_cast<int>(std::ceil(surfaceItem->WidthText(fontWin.get(), \"W\")));\n\taverageCharWidth = static_cast<int>(surfaceItem->AverageCharWidth(fontWin.get()));\n\n\twidth = std::max({ width, textSize, (maxItemCharacters + 1) * averageCharWidth });\n\n\trcDesired.right = rcDesired.left + TextOffset() + width + (TextInset.x * 2);\n\tif (Length() > rows)\n\t\trcDesired.right += SystemMetricsForDpi(SM_CXVSCROLL, dpi);\n\n\tAdjustWindowRect(&rcDesired, dpi);\n\treturn rcDesired;\n}\n\nint ListBoxX::TextOffset() const noexcept {\n\tconst int pixWidth = images.GetWidth();\n\treturn pixWidth == 0 ? ItemInset.x : ItemInset.x + pixWidth + (ImageInset.x * 2);\n}\n\nint ListBoxX::CaretFromEdge() {\n\tPRectangle rc;\n\tAdjustWindowRect(&rc, dpi);\n\treturn TextOffset() + static_cast<int>(TextInset.x + (0 - rc.left) - 1);\n}\n\nvoid ListBoxX::Clear() noexcept {\n\tListBox_ResetContent(lb);\n\tmaxItemCharacters = 0;\n\tlti.Clear();\n}\n\nvoid ListBoxX::Append(char *, int) {\n\t// This method is no longer called in Scintilla\n\tPLATFORM_ASSERT(false);\n}\n\nint ListBoxX::Length() {\n\treturn lti.Count();\n}\n\nvoid ListBoxX::Select(int n) {\n\t// We are going to scroll to centre on the new selection and then select it, so disable\n\t// redraw to avoid flicker caused by a painting new selection twice in unselected and then\n\t// selected states\n\tSetRedraw(false);\n\tCentreItem(n);\n\tListBox_SetCurSel(lb, n);\n\tOnSelChange();\n\tSetRedraw(true);\n}\n\nint ListBoxX::GetSelection() {\n\treturn ListBox_GetCurSel(lb);\n}\n\n// This is not actually called at present\nint ListBoxX::Find(const char *) {\n\treturn LB_ERR;\n}\n\nstd::string ListBoxX::GetValue(int n) {\n\tconst ListItemData item = lti.Get(n);\n\treturn item.text;\n}\n\nvoid ListBoxX::RegisterImage(int type, const char *xpm_data) {\n\tXPM xpmImage(xpm_data);\n\timages.AddImage(type, std::make_unique<RGBAImage>(xpmImage));\n}\n\nvoid ListBoxX::RegisterRGBAImage(int type, int width, int height, const unsigned char *pixelsImage) {\n\timages.AddImage(type, std::make_unique<RGBAImage>(width, height, 1.0f, pixelsImage));\n}\n\nvoid ListBoxX::ClearRegisteredImages() {\n\timages.Clear();\n}\n\nvoid ListBoxX::Draw(DRAWITEMSTRUCT *pDrawItem) {\n\tif ((pDrawItem->itemAction != ODA_SELECT) && (pDrawItem->itemAction != ODA_DRAWENTIRE)) {\n\t\treturn;\n\t}\n\tif (!graphics.pixmapLine) {\n\t\tAllocateBitMap();\n\t\tif (!graphics.pixmapLine) {\n\t\t\t// Failed to allocate, so release fully and give up\n\t\t\tgraphics.Release();\n\t\t\treturn;\n\t\t}\n\t}\n#if defined(USE_D2D)\n\tif (graphics.pBMDCTarget) {\n\t\tgraphics.pBMDCTarget->BeginDraw();\n\t}\n#endif\n\n\tconst PRectangle rcItemBase = PRectangleFromRECT(pDrawItem->rcItem);\n\tconst PRectangle rcItem(0, 0, rcItemBase.Width(), rcItemBase.Height());\n\tPRectangle rcBox = rcItem;\n\trcBox.left += TextOffset();\n\tColourRGBA colourFore;\n\tColourRGBA colourBack;\n\tif (pDrawItem->itemState & ODS_SELECTED) {\n\t\tPRectangle rcImage = rcItem;\n\t\trcImage.right = rcBox.left;\n\t\t// The image is not highlighted\n\t\tgraphics.pixmapLine->FillRectangle(rcImage, ColourElement(options.back, COLOR_WINDOW));\n\t\tcolourBack = ColourElement(options.backSelected, COLOR_HIGHLIGHT);\n\t\tgraphics.pixmapLine->FillRectangle(rcBox, colourBack);\n\t\tcolourFore = ColourElement(options.foreSelected, COLOR_HIGHLIGHTTEXT);\n\t} else {\n\t\tcolourBack = ColourElement(options.back, COLOR_WINDOW);\n\t\tgraphics.pixmapLine->FillRectangle(rcItem, colourBack);\n\t\tcolourFore = ColourElement(options.fore, COLOR_WINDOWTEXT);\n\t}\n\n\tconst ListItemData item = lti.Get(pDrawItem->itemID);\n\tconst int pixId = item.pixId;\n\tconst char *text = item.text;\n\n\tconst PRectangle rcText = rcBox.Inset(Point(TextInset.x, TextInset.y));\n\n\tconst double ascent = graphics.pixmapLine->Ascent(fontWin.get());\n\tgraphics.pixmapLine->DrawTextClipped(rcText, fontWin.get(), rcText.top + ascent, text, colourFore, colourBack);\n\n\t// Draw the image, if any\n\tconst RGBAImage *pimage = images.Get(pixId);\n\tif (pimage) {\n\t\tconst XYPOSITION left = rcItem.left + ItemInset.x + ImageInset.x;\n\t\tPRectangle rcImage = rcItem;\n\t\trcImage.left = left;\n\t\trcImage.right = rcImage.left + images.GetWidth();\n\t\tgraphics.pixmapLine->DrawRGBAImage(rcImage,\n\t\t\tpimage->GetWidth(), pimage->GetHeight(), pimage->Pixels());\n\t}\n\n#if defined(USE_D2D)\n\tif (graphics.pBMDCTarget) {\n\t\tconst HRESULT hrEnd = graphics.pBMDCTarget->EndDraw();\n\t\tif (FAILED(hrEnd)) {\n\t\t\treturn;\n\t\t}\n\t}\n#endif\n\n\t// Blit from hMemDC to hDC\n\tconst SIZE extent = SizeOfRect(pDrawItem->rcItem);\n\t::BitBlt(pDrawItem->hDC, pDrawItem->rcItem.left, pDrawItem->rcItem.top, extent.cx, extent.cy, graphics.bm.DC(), 0, 0, SRCCOPY);\n}\n\nvoid ListBoxX::AppendListItem(const char *text, const char *numword) {\n\tint pixId = -1;\n\tif (numword) {\n\t\tpixId = 0;\n\t\tchar ch;\n\t\twhile ((ch = *++numword) != '\\0') {\n\t\t\tpixId = 10 * pixId + (ch - '0');\n\t\t}\n\t}\n\n\tlti.AllocItem(text, pixId);\n\tmaxItemCharacters = std::max(maxItemCharacters, static_cast<int>(strlen(text)));\n}\n\nvoid ListBoxX::SetDelegate(IListBoxDelegate *lbDelegate) {\n\tdelegate = lbDelegate;\n}\n\nvoid ListBoxX::SetList(const char *list, char separator, char typesep) {\n\t// Turn off redraw while populating the list - this has a significant effect, even if\n\t// the listbox is not visible.\n\tSetRedraw(false);\n\tClear();\n\tconst size_t size = strlen(list);\n\tchar *words = lti.SetWords(list);\n\tconst char *startword = words;\n\tchar *numword = nullptr;\n\tfor (size_t i=0; i < size; i++) {\n\t\tif (words[i] == separator) {\n\t\t\twords[i] = '\\0';\n\t\t\tif (numword)\n\t\t\t\t*numword = '\\0';\n\t\t\tAppendListItem(startword, numword);\n\t\t\tstartword = words + i + 1;\n\t\t\tnumword = nullptr;\n\t\t} else if (words[i] == typesep) {\n\t\t\tnumword = words + i;\n\t\t}\n\t}\n\tif (startword) {\n\t\tif (numword)\n\t\t\t*numword = '\\0';\n\t\tAppendListItem(startword, numword);\n\t}\n\n\t// Finally populate the listbox itself with the correct number of items\n\tconst int count = lti.Count();\n\t::SendMessage(lb, LB_INITSTORAGE, count, 0);\n\tfor (intptr_t j=0; j<count; j++) {\n\t\tListBox_AddItemData(lb, j+1);\n\t}\n\tSetRedraw(true);\n}\n\nvoid ListBoxX::SetOptions(ListOptions options_) {\n\toptions = options_;\n\tframeStyle = FlagSet(options.options, AutoCompleteOption::FixedSize) ? WS_BORDER : WS_THICKFRAME;\n}\n\nvoid ListBoxX::AdjustWindowRect(PRectangle *rc, UINT dpiAdjust) const noexcept {\n\tRECT rcw = RectFromPRectangle(*rc);\n\tAdjustWindowRectForDpi(&rcw, frameStyle, dpiAdjust);\n\t*rc = PRectangleFromRECT(rcw);\n}\n\nint ListBoxX::ItemHeight() const noexcept {\n\tint itemHeight = lineHeight + (TextInset.y * 2);\n\tconst int pixHeight = images.GetHeight() + (ImageInset.y * 2);\n\tif (itemHeight < pixHeight) {\n\t\titemHeight = pixHeight;\n\t}\n\treturn itemHeight;\n}\n\nint ListBoxX::MinClientWidth() const noexcept {\n\treturn 12 * (aveCharWidth+aveCharWidth/3);\n}\n\nPOINT ListBoxX::MinTrackSize() const noexcept {\n\tPRectangle rc = PRectangle::FromInts(0, 0, MinClientWidth(), ItemHeight());\n\tAdjustWindowRect(&rc, dpi);\n\tPOINT ret = {static_cast<LONG>(rc.Width()), static_cast<LONG>(rc.Height())};\n\treturn ret;\n}\n\nPOINT ListBoxX::MaxTrackSize() const noexcept {\n\tPRectangle rc = PRectangle::FromInts(0, 0,\n\t\tstd::max<int>(static_cast<unsigned int>(MinClientWidth()),\n\t\tmaxCharWidth * maxItemCharacters + TextInset.x * 2 +\n\t\t TextOffset() + SystemMetricsForDpi(SM_CXVSCROLL, dpi)),\n\t\tItemHeight() * lti.Count());\n\tAdjustWindowRect(&rc, dpi);\n\tPOINT ret = {static_cast<LONG>(rc.Width()), static_cast<LONG>(rc.Height())};\n\treturn ret;\n}\n\nvoid ListBoxX::SetRedraw(bool on) noexcept {\n\t::SendMessage(lb, WM_SETREDRAW, on, 0);\n\tif (on) {\n\t\t::RedrawWindow(lb, {}, {}, RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN);\n\t}\n}\n\nvoid ListBoxX::ResizeToCursor() {\n\tPRectangle rc = GetPosition();\n\tPOINT ptw;\n\t::GetCursorPos(&ptw);\n\tconst Point pt = PointFromPOINT(ptw) + dragOffset;\n\n\tswitch (resizeHit) {\n\t\tcase HTLEFT:\n\t\t\trc.left = pt.x;\n\t\t\tbreak;\n\t\tcase HTRIGHT:\n\t\t\trc.right = pt.x;\n\t\t\tbreak;\n\t\tcase HTTOP:\n\t\t\trc.top = pt.y;\n\t\t\tbreak;\n\t\tcase HTTOPLEFT:\n\t\t\trc.top = pt.y;\n\t\t\trc.left = pt.x;\n\t\t\tbreak;\n\t\tcase HTTOPRIGHT:\n\t\t\trc.top = pt.y;\n\t\t\trc.right = pt.x;\n\t\t\tbreak;\n\t\tcase HTBOTTOM:\n\t\t\trc.bottom = pt.y;\n\t\t\tbreak;\n\t\tcase HTBOTTOMLEFT:\n\t\t\trc.bottom = pt.y;\n\t\t\trc.left = pt.x;\n\t\t\tbreak;\n\t\tcase HTBOTTOMRIGHT:\n\t\t\trc.bottom = pt.y;\n\t\t\trc.right = pt.x;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t}\n\n\tconst POINT ptMin = MinTrackSize();\n\tconst POINT ptMax = MaxTrackSize();\n\t// We don't allow the left edge to move at present, but just in case\n\trc.left = std::clamp(rc.left, rcPreSize.right - ptMax.x, rcPreSize.right - ptMin.x);\n\trc.top = std::clamp(rc.top, rcPreSize.bottom - ptMax.y, rcPreSize.bottom - ptMin.y);\n\trc.right = std::clamp(rc.right, rcPreSize.left + ptMin.x, rcPreSize.left + ptMax.x);\n\trc.bottom = std::clamp(rc.bottom, rcPreSize.top + ptMin.y, rcPreSize.top + ptMax.y);\n\n\tSetPosition(rc);\n}\n\nvoid ListBoxX::StartResize(WPARAM hitCode) {\n\trcPreSize = GetPosition();\n\tPOINT cursorPos;\n\t::GetCursorPos(&cursorPos);\n\n\tswitch (hitCode) {\n\t\tcase HTRIGHT:\n\t\tcase HTBOTTOM:\n\t\tcase HTBOTTOMRIGHT:\n\t\t\tdragOffset.x = rcPreSize.right - cursorPos.x;\n\t\t\tdragOffset.y = rcPreSize.bottom - cursorPos.y;\n\t\t\tbreak;\n\n\t\tcase HTTOPRIGHT:\n\t\t\tdragOffset.x = rcPreSize.right - cursorPos.x;\n\t\t\tdragOffset.y = rcPreSize.top - cursorPos.y;\n\t\t\tbreak;\n\n\t\t// Note that the current hit test code prevents the left edge cases ever firing\n\t\t// as we don't want the left edge to be movable\n\t\tcase HTLEFT:\n\t\tcase HTTOP:\n\t\tcase HTTOPLEFT:\n\t\t\tdragOffset.x = rcPreSize.left - cursorPos.x;\n\t\t\tdragOffset.y = rcPreSize.top - cursorPos.y;\n\t\t\tbreak;\n\t\tcase HTBOTTOMLEFT:\n\t\t\tdragOffset.x = rcPreSize.left - cursorPos.x;\n\t\t\tdragOffset.y = rcPreSize.bottom - cursorPos.y;\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\treturn;\n\t}\n\n\t::SetCapture(GetHWND());\n\tresizeHit = hitCode;\n}\n\nLRESULT ListBoxX::NcHitTest(WPARAM wParam, LPARAM lParam) const {\n\tconst PRectangle rc = GetPosition();\n\n\tLRESULT hit = ::DefWindowProc(GetHWND(), WM_NCHITTEST, wParam, lParam);\n\t// There is an apparent bug in the DefWindowProc hit test code whereby it will\n\t// return HTTOPXXX if the window in question is shorter than the default\n\t// window caption height + frame, even if one is hovering over the bottom edge of\n\t// the frame, so workaround that here\n\tif (hit >= HTTOP && hit <= HTTOPRIGHT) {\n\t\tconst int minHeight = SystemMetricsForDpi(SM_CYMINTRACK, dpi);\n\t\tconst int yPos = GET_Y_LPARAM(lParam);\n\t\tif ((rc.Height() < minHeight) && (yPos > ((rc.top + rc.bottom)/2))) {\n\t\t\thit += HTBOTTOM - HTTOP;\n\t\t}\n\t}\n\n\t// Never permit resizing that moves the left edge. Allow movement of top or bottom edge\n\t// depending on whether the list is above or below the caret\n\tswitch (hit) {\n\t\tcase HTLEFT:\n\t\tcase HTTOPLEFT:\n\t\tcase HTBOTTOMLEFT:\n\t\t\thit = HTERROR;\n\t\t\tbreak;\n\n\t\tcase HTTOP:\n\t\tcase HTTOPRIGHT: {\n\t\t\t\t// Valid only if caret below list\n\t\t\t\tif (location.y < rc.top)\n\t\t\t\t\thit = HTERROR;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase HTBOTTOM:\n\t\tcase HTBOTTOMRIGHT: {\n\t\t\t\t// Valid only if caret above list\n\t\t\t\tif (rc.bottom <= location.y)\n\t\t\t\t\thit = HTERROR;\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t}\n\n\treturn hit;\n}\n\nvoid ListBoxX::OnDoubleClick() {\n\tif (delegate) {\n\t\tListBoxEvent event(ListBoxEvent::EventType::doubleClick);\n\t\tdelegate->ListNotify(&event);\n\t}\n}\n\nvoid ListBoxX::OnSelChange() {\n\tif (delegate) {\n\t\tListBoxEvent event(ListBoxEvent::EventType::selectionChange);\n\t\tdelegate->ListNotify(&event);\n\t}\n}\n\nPOINT ListBoxX::GetClientExtent() const noexcept {\n\tRECT rc;\n\t::GetWindowRect(HwndFromWindowID(wid), &rc);\n\tPOINT ret { rc.right - rc.left, rc.bottom - rc.top };\n\treturn ret;\n}\n\nvoid ListBoxX::CentreItem(int n) {\n\t// If below mid point, scroll up to centre, but with more items below if uneven\n\tif (n >= 0) {\n\t\tconst POINT extent = GetClientExtent();\n\t\tconst int visible = extent.y/ItemHeight();\n\t\tif (visible < Length()) {\n\t\t\tconst int top = ListBox_GetTopIndex(lb);\n\t\t\tconst int half = (visible - 1) / 2;\n\t\t\tif (n > (top + half))\n\t\t\t\tListBox_SetTopIndex(lb, n - half);\n\t\t}\n\t}\n}\n\nvoid ListBoxX::AllocateBitMap() {\n\tconst SIZE extent { GetClientExtent().x, lineHeight };\n\n\tgraphics.bm.Create({}, extent.cx, -extent.cy, nullptr);\n\tif (!graphics.bm) {\n\t\treturn;\n\t}\n\n\t// Make a surface\n\tgraphics.pixmapLine = Surface::Allocate(technology);\n\tgraphics.pixmapLine->SetMode(SurfaceMode(codePage, false));\n\n#if defined(USE_D2D)\n\tif (technology != Technology::Default) {\n\t\tif (!LoadD2D()) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst D2D1_RENDER_TARGET_PROPERTIES drtp = D2D1::RenderTargetProperties(\n\t\t\tD2D1_RENDER_TARGET_TYPE_DEFAULT,\n\t\t\t{ DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED });\n\n\t\tHRESULT hr = CreateDCRenderTarget(&drtp, graphics.pBMDCTarget);\n\t\tif (FAILED(hr) || !graphics.pBMDCTarget) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst RECT rcExtent = { 0, 0, extent.cx, extent.cy };\n\t\thr = graphics.pBMDCTarget->BindDC(graphics.bm.DC(), &rcExtent);\n\t\tif (SUCCEEDED(hr)) {\n\t\t\tgraphics.pixmapLine->Init(graphics.pBMDCTarget.Get(), GetID());\n\t\t}\n\t\treturn;\n\t}\n#endif\n\n\t// Either technology == Technology::Default or USE_D2D turned off\n\tgraphics.pixmapLine->Init(graphics.bm.DC(), GetID());\n}\n\nLRESULT PASCAL ListBoxX::ListProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) {\n\ttry {\n\t\tswitch (iMessage) {\n\t\tcase WM_ERASEBKGND:\n\t\t\treturn TRUE;\n\n\t\tcase WM_MOUSEACTIVATE:\n\t\t\t// This prevents the view activating when the scrollbar is clicked\n\t\t\treturn MA_NOACTIVATE;\n\n\t\tcase WM_LBUTTONDOWN: {\n\t\t\t\t// We must take control of selection to prevent the ListBox activating\n\t\t\t\t// the popup\n\t\t\t\tconst LRESULT lResult = ::SendMessage(hWnd, LB_ITEMFROMPOINT, 0, lParam);\n\t\t\t\tif (HIWORD(lResult) == 0) {\n\t\t\t\t\tListBox_SetCurSel(hWnd, LOWORD(lResult));\n\t\t\t\t\tOnSelChange();\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn 0;\n\n\t\tcase WM_LBUTTONUP:\n\t\t\treturn 0;\n\n\t\tcase WM_LBUTTONDBLCLK:\n\t\t\tOnDoubleClick();\n\t\t\treturn 0;\n\n\t\tcase WM_MBUTTONDOWN:\n\t\t\t// disable the scroll wheel button click action\n\t\t\treturn 0;\n\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\n\t\tif (prevWndProc) {\n\t\t\treturn ::CallWindowProc(prevWndProc, hWnd, iMessage, wParam, lParam);\n\t\t}\n\t} catch (...) {\n\t}\n\treturn ::DefWindowProc(hWnd, iMessage, wParam, lParam);\n}\n\nLRESULT PASCAL ListBoxX::ControlWndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) {\n\tif (ListBoxX *lbx = static_cast<ListBoxX *>(PointerFromWindow(::GetParent(hWnd)))) {\n\t\treturn lbx->ListProc(hWnd, iMessage, wParam, lParam);\n\t}\n\treturn ::DefWindowProc(hWnd, iMessage, wParam, lParam);\n}\n\nLRESULT ListBoxX::WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) {\n\tswitch (iMessage) {\n\tcase WM_CREATE: {\n\t\t\tHINSTANCE hinstanceParent = GetWindowInstance(HwndFromWindow(*parent));\n\t\t\t// Note that LBS_NOINTEGRALHEIGHT is specified to fix cosmetic issue when resizing the list\n\t\t\t// but has useful side effect of speeding up list population significantly\n\t\t\tlb = ::CreateWindowExW(\n\t\t\t\t0, WC_LISTBOXW, L\"\",\n\t\t\t\tWS_CHILD | WS_VSCROLL | WS_VISIBLE |\n\t\t\t\tLBS_OWNERDRAWFIXED | LBS_NODATA | LBS_NOINTEGRALHEIGHT,\n\t\t\t\t0, 0, 150,80, hWnd,\n\t\t\t\treinterpret_cast<HMENU>(static_cast<ptrdiff_t>(ctrlID)),\n\t\t\t\thinstanceParent,\n\t\t\t\tnullptr);\n\t\t\tprevWndProc = SubclassWindow(lb, ControlWndProc);\n\t\t}\n\t\tbreak;\n\n\tcase WM_SIZE:\n\t\tif (lb) {\n\t\t\tgraphics.Release();\t// Bitmap must be reallocated to new size.\n\t\t\tSetRedraw(false);\n\t\t\t::SetWindowPos(lb, {}, 0, 0, LOWORD(lParam), HIWORD(lParam), SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE);\n\t\t\t// Ensure the selection remains visible\n\t\t\tCentreItem(GetSelection());\n\t\t\tSetRedraw(true);\n\t\t}\n\t\tbreak;\n\n\tcase WM_PAINT: {\n\t\t\tPainter painter(hWnd);\n\t\t}\n\t\tbreak;\n\n\tcase WM_COMMAND:\n\t\t// This is not actually needed now - the registered double click action is used\n\t\t// directly to action a choice from the list.\n\t\t::SendMessage(HwndFromWindow(*parent), iMessage, wParam, lParam);\n\t\tbreak;\n\n\tcase WM_MEASUREITEM: {\n\t\t\tMEASUREITEMSTRUCT *pMeasureItem = static_cast<MEASUREITEMSTRUCT *>(PtrFromLParam(lParam));\n\t\t\tpMeasureItem->itemHeight = ItemHeight();\n\t\t}\n\t\tbreak;\n\n\tcase WM_DRAWITEM:\n\t\tDraw(static_cast<DRAWITEMSTRUCT *>(PtrFromLParam(lParam)));\n\t\tbreak;\n\n\tcase WM_DESTROY:\n\t\tlb = {};\n\t\tSetWindowPointer(hWnd, nullptr);\n\t\treturn ::DefWindowProc(hWnd, iMessage, wParam, lParam);\n\n\tcase WM_ERASEBKGND:\n\t\t// To reduce flicker we can elide background erasure since this window is\n\t\t// completely covered by its child.\n\t\treturn TRUE;\n\n\tcase WM_GETMINMAXINFO: {\n\t\t\tMINMAXINFO *minMax = static_cast<MINMAXINFO*>(PtrFromLParam(lParam));\n\t\t\tminMax->ptMaxTrackSize = MaxTrackSize();\n\t\t\tminMax->ptMinTrackSize = MinTrackSize();\n\t\t}\n\t\tbreak;\n\n\tcase WM_MOUSEACTIVATE:\n\t\treturn MA_NOACTIVATE;\n\n\tcase WM_NCHITTEST:\n\t\treturn NcHitTest(wParam, lParam);\n\n\tcase WM_NCLBUTTONDOWN:\n\t\t// We have to implement our own window resizing because the DefWindowProc\n\t\t// implementation insists on activating the resized window\n\t\tStartResize(wParam);\n\t\treturn 0;\n\n\tcase WM_MOUSEMOVE: {\n\t\t\tif (resizeHit == 0) {\n\t\t\t\treturn ::DefWindowProc(hWnd, iMessage, wParam, lParam);\n\t\t\t}\n\t\t\tResizeToCursor();\n\t\t}\n\t\tbreak;\n\n\tcase WM_LBUTTONUP:\n\tcase WM_CANCELMODE:\n\t\tif (resizeHit != 0) {\n\t\t\tresizeHit = 0;\n\t\t\t::ReleaseCapture();\n\t\t}\n\t\treturn ::DefWindowProc(hWnd, iMessage, wParam, lParam);\n\tcase WM_MOUSEWHEEL:\n\t\tif (wheelDelta.Accumulate(wParam)) {\n\t\t\tconst int nRows = GetVisibleRows();\n\t\t\tint linesToScroll = std::clamp(nRows - 1, 1, 3);\n\t\t\tlinesToScroll *= wheelDelta.Actions();\n\t\t\tint top = ListBox_GetTopIndex(lb) + linesToScroll;\n\t\t\tif (top < 0) {\n\t\t\t\ttop = 0;\n\t\t\t}\n\t\t\tListBox_SetTopIndex(lb, top);\n\t\t}\n\t\tbreak;\n\n\tdefault:\n\t\treturn ::DefWindowProc(hWnd, iMessage, wParam, lParam);\n\t}\n\n\treturn 0;\n}\n\nLRESULT PASCAL ListBoxX::StaticWndProc(\n    HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) {\n\tif (iMessage == WM_CREATE) {\n\t\tCREATESTRUCT *pCreate = static_cast<CREATESTRUCT *>(PtrFromLParam(lParam));\n\t\tSetWindowPointer(hWnd, pCreate->lpCreateParams);\n\t}\n\t// Find C++ object associated with window.\n\tif (ListBoxX *lbx = static_cast<ListBoxX *>(PointerFromWindow(hWnd))) {\n\t\treturn lbx->WndProc(hWnd, iMessage, wParam, lParam);\n\t}\n\treturn ::DefWindowProc(hWnd, iMessage, wParam, lParam);\n}\n\nnamespace Scintilla::Internal {\n\nbool ListBoxX_Register() noexcept {\n\tWNDCLASSEX wndclassc {};\n\twndclassc.cbSize = sizeof(wndclassc);\n\t// We need CS_HREDRAW and CS_VREDRAW because of the ellipsis that might be drawn for\n\t// truncated items in the list and the appearance/disappearance of the vertical scroll bar.\n\t// The list repaint is double-buffered to avoid the flicker this would otherwise cause.\n\twndclassc.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;\n\twndclassc.cbWndExtra = sizeof(ListBoxX *);\n\twndclassc.hInstance = hinstPlatformRes;\n\twndclassc.lpfnWndProc = ListBoxX::StaticWndProc;\n\twndclassc.hCursor = ::LoadCursor({}, IDC_ARROW);\n\twndclassc.lpszClassName = ListBoxX_ClassName;\n\n\treturn ::RegisterClassEx(&wndclassc) != 0;\n}\n\nvoid ListBoxX_Unregister() noexcept {\n\tif (hinstPlatformRes) {\n\t\t::UnregisterClass(ListBoxX_ClassName, hinstPlatformRes);\n\t}\n}\n\nListBox::ListBox() noexcept = default;\n\nListBox::~ListBox() noexcept = default;\n\n}\n"
  },
  {
    "path": "Libraries/scintilla/win32/ListBox.h",
    "content": "// Scintilla source code edit control\n/** @file ListBox.h\n ** Definitions for list box on Windows.\n **/\n// Copyright 2025 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#ifndef LISTBOX_H\n#define LISTBOX_H\n\nnamespace Scintilla::Internal {\n\nbool ListBoxX_Register() noexcept;\nvoid ListBoxX_Unregister() noexcept;\n\n}\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/win32/PlatWin.cxx",
    "content": "// Scintilla source code edit control\n/** @file PlatWin.cxx\n ** Implementation of platform facilities on Windows.\n **/\n// Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#include <cstddef>\n#include <cstdlib>\n#include <cstdint>\n#include <cstring>\n#include <cstdio>\n#include <cstdarg>\n#include <ctime>\n#include <cmath>\n#include <climits>\n\n#include <string_view>\n#include <vector>\n#include <map>\n#include <optional>\n#include <algorithm>\n#include <iterator>\n#include <memory>\n#include <mutex>\n\n// Want to use std::min and std::max so don't want Windows.h version of min and max\n#if !defined(NOMINMAX)\n#define NOMINMAX\n#endif\n#undef _WIN32_WINNT\n#define _WIN32_WINNT 0x0A00\n#undef WINVER\n#define WINVER 0x0A00\n#define WIN32_LEAN_AND_MEAN 1\n#include <windows.h>\n#include <commctrl.h>\n#include <richedit.h>\n#include <windowsx.h>\n#include <shellscalingapi.h>\n\n#include <wrl.h>\nusing Microsoft::WRL::ComPtr;\n\n#if !defined(DISABLE_D2D)\n#define USE_D2D 1\n#endif\n\n#if defined(USE_D2D)\n#include <d2d1_1.h>\n#include <d3d11_1.h>\n#include <dwrite_1.h>\n#endif\n\n#include \"ScintillaTypes.h\"\n\n#include \"Debugging.h\"\n#include \"Geometry.h\"\n#include \"Platform.h\"\n#include \"XPM.h\"\n#include \"UniConversion.h\"\n#include \"DBCS.h\"\n\n#include \"WinTypes.h\"\n#include \"PlatWin.h\"\n#include \"ListBox.h\"\n#if defined(USE_D2D)\n#include \"SurfaceD2D.h\"\n#endif\n\nusing namespace Scintilla;\n\nnamespace Scintilla::Internal {\n\nHINSTANCE hinstPlatformRes{};\n\nvoid *PointerFromWindow(HWND hWnd) noexcept {\n\treturn reinterpret_cast<void *>(::GetWindowLongPtr(hWnd, 0));\n}\n\nvoid SetWindowPointer(HWND hWnd, void *ptr) noexcept {\n\t::SetWindowLongPtr(hWnd, 0, reinterpret_cast<LONG_PTR>(ptr));\n}\n\nnamespace {\n\n// system DPI, same for all monitor.\nUINT uSystemDPI = USER_DEFAULT_SCREEN_DPI;\n\nusing GetDpiForWindowSig = UINT(WINAPI *)(HWND hwnd);\nGetDpiForWindowSig fnGetDpiForWindow = nullptr;\n\nHMODULE hDLLShcore{};\nusing GetDpiForMonitorSig = HRESULT(WINAPI *)(HMONITOR hmonitor, /*MONITOR_DPI_TYPE*/int dpiType, UINT *dpiX, UINT *dpiY);\nGetDpiForMonitorSig fnGetDpiForMonitor = nullptr;\n\nusing GetSystemMetricsForDpiSig = int(WINAPI *)(int nIndex, UINT dpi);\nGetSystemMetricsForDpiSig fnGetSystemMetricsForDpi = nullptr;\n\nusing AdjustWindowRectExForDpiSig = BOOL(WINAPI *)(LPRECT lpRect, DWORD dwStyle, BOOL bMenu, DWORD dwExStyle, UINT dpi);\nAdjustWindowRectExForDpiSig fnAdjustWindowRectExForDpi = nullptr;\n\nusing AreDpiAwarenessContextsEqualSig = BOOL(WINAPI *)(DPI_AWARENESS_CONTEXT, DPI_AWARENESS_CONTEXT);\nAreDpiAwarenessContextsEqualSig fnAreDpiAwarenessContextsEqual = nullptr;\n\nusing GetWindowDpiAwarenessContextSig = DPI_AWARENESS_CONTEXT(WINAPI *)(HWND);\nGetWindowDpiAwarenessContextSig fnGetWindowDpiAwarenessContext = nullptr;\n\nusing GetScaleFactorForMonitorSig = HRESULT(WINAPI *)(HMONITOR, DEVICE_SCALE_FACTOR *);\nGetScaleFactorForMonitorSig fnGetScaleFactorForMonitor = nullptr;\n\nusing SetThreadDpiAwarenessContextSig = DPI_AWARENESS_CONTEXT(WINAPI *)(DPI_AWARENESS_CONTEXT);\nSetThreadDpiAwarenessContextSig fnSetThreadDpiAwarenessContext = nullptr;\n\nvoid LoadDpiForWindow() noexcept {\n\tHMODULE user32 = ::GetModuleHandleW(L\"user32.dll\");\n\tfnGetDpiForWindow = DLLFunction<GetDpiForWindowSig>(user32, \"GetDpiForWindow\");\n\tfnGetSystemMetricsForDpi = DLLFunction<GetSystemMetricsForDpiSig>(user32, \"GetSystemMetricsForDpi\");\n\tfnAdjustWindowRectExForDpi = DLLFunction<AdjustWindowRectExForDpiSig>(user32, \"AdjustWindowRectExForDpi\");\n\tfnSetThreadDpiAwarenessContext = DLLFunction<SetThreadDpiAwarenessContextSig>(user32, \"SetThreadDpiAwarenessContext\");\n\n\tusing GetDpiForSystemSig = UINT(WINAPI *)(void);\n\tGetDpiForSystemSig fnGetDpiForSystem = DLLFunction<GetDpiForSystemSig>(user32, \"GetDpiForSystem\");\n\tif (fnGetDpiForSystem) {\n\t\tuSystemDPI = fnGetDpiForSystem();\n\t} else {\n\t\tHDC hdcMeasure = ::CreateCompatibleDC({});\n\t\tuSystemDPI = ::GetDeviceCaps(hdcMeasure, LOGPIXELSY);\n\t\t::DeleteDC(hdcMeasure);\n\t}\n\n\tfnGetWindowDpiAwarenessContext = DLLFunction<GetWindowDpiAwarenessContextSig>(user32, \"GetWindowDpiAwarenessContext\");\n\tfnAreDpiAwarenessContextsEqual = DLLFunction<AreDpiAwarenessContextsEqualSig>(user32, \"AreDpiAwarenessContextsEqual\");\n\n\thDLLShcore = ::LoadLibraryExW(L\"shcore.dll\", {}, LOAD_LIBRARY_SEARCH_SYSTEM32);\n\tif (hDLLShcore) {\n\t\tfnGetScaleFactorForMonitor = DLLFunction<GetScaleFactorForMonitorSig>(hDLLShcore, \"GetScaleFactorForMonitor\");\n\t\tfnGetDpiForMonitor = DLLFunction<GetDpiForMonitorSig>(hDLLShcore, \"GetDpiForMonitor\");\n\t}\n}\n\n}\n\nHMONITOR MonitorFromWindowHandleScaling(HWND hWnd) noexcept {\n\tconstexpr DWORD monitorFlags = MONITOR_DEFAULTTONEAREST;\n\n\tif (!fnSetThreadDpiAwarenessContext) {\n\t\treturn ::MonitorFromWindow(hWnd, monitorFlags);\n\t}\n\n\t// Temporarily switching to PerMonitorV2 to retrieve correct monitor via MonitorFromRect() in case of active GDI scaling.\n\tconst DPI_AWARENESS_CONTEXT oldContext = fnSetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);\n\tPLATFORM_ASSERT(oldContext != nullptr);\n\n\tRECT rect;\n\t::GetWindowRect(hWnd, &rect);\n\tconst HMONITOR monitor = ::MonitorFromRect(&rect, monitorFlags);\n\n\tfnSetThreadDpiAwarenessContext(oldContext);\n\treturn monitor;\n}\n\nfloat GetDeviceScaleFactorWhenGdiScalingActive(HWND hWnd) noexcept {\n\tif (fnAreDpiAwarenessContextsEqual) {\n\t\tPLATFORM_ASSERT(fnGetWindowDpiAwarenessContext && fnGetScaleFactorForMonitor);\n\t\tif (fnAreDpiAwarenessContextsEqual(DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED, fnGetWindowDpiAwarenessContext(hWnd))) {\n\t\t\tconst HWND hRootWnd = ::GetAncestor(hWnd, GA_ROOT); // Scale factor applies to entire (root) window.\n\t\t\tconst HMONITOR hMonitor = MonitorFromWindowHandleScaling(hRootWnd);\n\t\t\tDEVICE_SCALE_FACTOR deviceScaleFactor;\n\t\t\tif (S_OK == fnGetScaleFactorForMonitor(hMonitor, &deviceScaleFactor))\n\t\t\t\treturn static_cast<float>(static_cast<int>(deviceScaleFactor)) / 100.f;\n\t\t}\n\t}\n\treturn 1.f;\n}\n\nUINT DpiForWindow(WindowID wid) noexcept {\n\tif (fnGetDpiForWindow) {\n\t\treturn fnGetDpiForWindow(HwndFromWindowID(wid));\n\t}\n\tif (fnGetDpiForMonitor) {\n\t\tHMONITOR hMonitor = ::MonitorFromWindow(HwndFromWindowID(wid), MONITOR_DEFAULTTONEAREST);\n\t\tUINT dpiX = 0;\n\t\tUINT dpiY = 0;\n\t\tif (fnGetDpiForMonitor(hMonitor, 0 /*MDT_EFFECTIVE_DPI*/, &dpiX, &dpiY) == S_OK) {\n\t\t\treturn dpiY;\n\t\t}\n\t}\n\treturn uSystemDPI;\n}\n\nint SystemMetricsForDpi(int nIndex, UINT dpi) noexcept {\n\tif (fnGetSystemMetricsForDpi) {\n\t\treturn fnGetSystemMetricsForDpi(nIndex, dpi);\n\t}\n\n\tint value = ::GetSystemMetrics(nIndex);\n\tvalue = (dpi == uSystemDPI) ? value : ::MulDiv(value, dpi, uSystemDPI);\n\treturn value;\n}\n\nvoid AdjustWindowRectForDpi(LPRECT lpRect, DWORD dwStyle, UINT dpi) noexcept {\n\tif (fnAdjustWindowRectExForDpi) {\n\t\tfnAdjustWindowRectExForDpi(lpRect, dwStyle, false, WS_EX_WINDOWEDGE, dpi);\n\t} else {\n\t\t::AdjustWindowRectEx(lpRect, dwStyle, false, WS_EX_WINDOWEDGE);\n\t}\n}\n\nnamespace {\n\nconstexpr BITMAPV5HEADER BitMapHeader(int width, int height) noexcept {\n\tconstexpr int pixelBits = 32;\n\n\t// Divide each pixel up in the expected BGRA manner.\n\t// Compatible with DXGI_FORMAT_B8G8R8A8_UNORM.\n\tconstexpr DWORD maskRed = 0x00FF0000U;\n\tconstexpr DWORD maskGreen = 0x0000FF00U;\n\tconstexpr DWORD maskBlue = 0x000000FFU;\n\tconstexpr DWORD maskAlpha = 0xFF000000U;\n\n\tBITMAPV5HEADER bi{};\n\tbi.bV5Size = sizeof(BITMAPV5HEADER);\n\tbi.bV5Width = width;\n\tbi.bV5Height = height;\n\tbi.bV5Planes = 1;\n\tbi.bV5BitCount = pixelBits;\n\tbi.bV5Compression = BI_BITFIELDS;\n\t// The following mask specification specifies a supported 32 BPP alpha format for Windows XP.\n\tbi.bV5RedMask = maskRed;\n\tbi.bV5GreenMask = maskGreen;\n\tbi.bV5BlueMask = maskBlue;\n\tbi.bV5AlphaMask = maskAlpha;\n\treturn bi;\n}\n\nHBITMAP BitMapSection(HDC hdc, int width, int height, DWORD **pixels) noexcept {\n\tconst BITMAPV5HEADER bi = BitMapHeader(width, height);\n\tvoid *image = nullptr;\n\tHBITMAP hbm = ::CreateDIBSection(hdc, reinterpret_cast<const BITMAPINFO *>(&bi), DIB_RGB_COLORS, &image, {}, 0);\n\tif (pixels) {\n\t\t*pixels = static_cast<DWORD *>(image);\n\t}\n\treturn hbm;\n}\n\n}\n\nGDIBitMap::~GDIBitMap() noexcept {\n\tRelease();\n}\n\nvoid GDIBitMap::Create(HDC hdcBase, int width, int height, DWORD **pixels) noexcept {\n\tRelease();\n\n\thdc = CreateCompatibleDC(hdcBase);\n\tif (!hdc) {\n\t\treturn;\n\t}\n\n\thbm = BitMapSection(hdc, width, height, pixels);\n\tif (!hbm) {\n\t\treturn;\n\t}\n\thbmOriginal = SelectBitmap(hdc, hbm);\n}\n\nvoid GDIBitMap::Release() noexcept {\n\tif (hbmOriginal) {\n\t\t// Deselect HBITMAP from HDC so it may be deleted.\n\t\tSelectBitmap(hdc, hbmOriginal);\n\t}\n\thbmOriginal = {};\n\tif (hbm) {\n\t\t::DeleteObject(hbm);\n\t}\n\thbm = {};\n\tif (hdc) {\n\t\t::DeleteDC(hdc);\n\t}\n\thdc = {};\n}\n\nHBITMAP GDIBitMap::Extract() noexcept {\n\t// Deselect HBITMAP from HDC but keep so can delete.\n\t// The caller will make a copy, not take ownership.\n\tHBITMAP ret = hbm;\n\tif (hbmOriginal) {\n\t\tSelectBitmap(hdc, hbmOriginal);\n\t\thbmOriginal = {};\n\t}\n\treturn ret;\n}\n\nWindow::~Window() noexcept = default;\n\nvoid Window::Destroy() noexcept {\n\tif (wid)\n\t\t::DestroyWindow(HwndFromWindowID(wid));\n\twid = nullptr;\n}\n\nPRectangle Window::GetPosition() const {\n\tRECT rc;\n\t::GetWindowRect(HwndFromWindowID(wid), &rc);\n\treturn PRectangleFromRECT(rc);\n}\n\nvoid Window::SetPosition(PRectangle rc) {\n\t::SetWindowPos(HwndFromWindowID(wid),\n\t\t{}, static_cast<int>(rc.left), static_cast<int>(rc.top),\n\t\tstatic_cast<int>(rc.Width()), static_cast<int>(rc.Height()), SWP_NOZORDER | SWP_NOACTIVATE);\n}\n\nnamespace {\n\nRECT RectFromMonitor(HMONITOR hMonitor) noexcept {\n\tMONITORINFO mi = {};\n\tmi.cbSize = sizeof(mi);\n\tif (GetMonitorInfo(hMonitor, &mi)) {\n\t\treturn mi.rcWork;\n\t}\n\tRECT rc = {0, 0, 0, 0};\n\tif (::SystemParametersInfoA(SPI_GETWORKAREA, 0, &rc, 0) == 0) {\n\t\trc.left = 0;\n\t\trc.top = 0;\n\t\trc.right = 0;\n\t\trc.bottom = 0;\n\t}\n\treturn rc;\n}\n\n}\n\nvoid Window::SetPositionRelative(PRectangle rc, const Window *relativeTo) {\n\tconst DWORD style = GetWindowStyle(HwndFromWindowID(wid));\n\tif (style & WS_POPUP) {\n\t\tPOINT ptOther = {0, 0};\n\t\t::ClientToScreen(HwndFromWindow(*relativeTo), &ptOther);\n\t\trc.Move(static_cast<XYPOSITION>(ptOther.x), static_cast<XYPOSITION>(ptOther.y));\n\n\t\tconst RECT rcMonitor = RectFromPRectangle(rc);\n\n\t\tHMONITOR hMonitor = MonitorFromRect(&rcMonitor, MONITOR_DEFAULTTONEAREST);\n\t\t// If hMonitor is NULL, that's just the main screen anyways.\n\t\tconst RECT rcWork = RectFromMonitor(hMonitor);\n\n\t\tif (rcWork.left < rcWork.right) {\n\t\t\t// Now clamp our desired rectangle to fit inside the work area\n\t\t\t// This way, the menu will fit wholly on one screen. An improvement even\n\t\t\t// if you don't have a second monitor on the left... Menu's appears half on\n\t\t\t// one screen and half on the other are just U.G.L.Y.!\n\t\t\tif (rc.right > rcWork.right)\n\t\t\t\trc.Move(rcWork.right - rc.right, 0);\n\t\t\tif (rc.bottom > rcWork.bottom)\n\t\t\t\trc.Move(0, rcWork.bottom - rc.bottom);\n\t\t\tif (rc.left < rcWork.left)\n\t\t\t\trc.Move(rcWork.left - rc.left, 0);\n\t\t\tif (rc.top < rcWork.top)\n\t\t\t\trc.Move(0, rcWork.top - rc.top);\n\t\t}\n\t}\n\tSetPosition(rc);\n}\n\nPRectangle Window::GetClientPosition() const {\n\tRECT rc={0,0,0,0};\n\tif (wid)\n\t\t::GetClientRect(HwndFromWindowID(wid), &rc);\n\treturn PRectangleFromRECT(rc);\n}\n\nvoid Window::Show(bool show) {\n\tif (show)\n\t\t::ShowWindow(HwndFromWindowID(wid), SW_SHOWNOACTIVATE);\n\telse\n\t\t::ShowWindow(HwndFromWindowID(wid), SW_HIDE);\n}\n\nvoid Window::InvalidateAll() {\n\t::InvalidateRect(HwndFromWindowID(wid), nullptr, FALSE);\n}\n\nvoid Window::InvalidateRectangle(PRectangle rc) {\n\tconst RECT rcw = RectFromPRectangle(rc);\n\t::InvalidateRect(HwndFromWindowID(wid), &rcw, FALSE);\n}\n\nnamespace {\n\nstd::optional<DWORD> RegGetDWORD(HKEY hKey, LPCWSTR valueName) noexcept {\n\tDWORD value = 0;\n\tDWORD type = REG_NONE;\n\tDWORD size = sizeof(DWORD);\n\tconst LSTATUS status = ::RegQueryValueExW(hKey, valueName, nullptr, &type, reinterpret_cast<LPBYTE>(&value), &size);\n\tif (status == ERROR_SUCCESS && type == REG_DWORD) {\n\t\treturn value;\n\t}\n\treturn {};\n}\n\nclass CursorHelper {\n\tGDIBitMap bm;\n\tDWORD *pixels = nullptr;\n\tconst int width;\n\tconst int height;\n\tconst float scale;\n\tstatic constexpr float baseSize = 32.0f;\n\n\tstatic constexpr float arrow[][2] = {\n\t\t{ 32.0f - 12.73606f,32.0f - 19.04075f },\n\t\t{ 32.0f - 7.80159f, 32.0f - 19.04075f },\n\t\t{ 32.0f - 9.82813f, 32.0f - 14.91828f },\n\t\t{ 32.0f - 6.88341f, 32.0f - 13.42515f },\n\t\t{ 32.0f - 4.62301f, 32.0f - 18.05872f },\n\t\t{ 32.0f - 1.26394f, 32.0f - 14.78295f },\n\t\t{ 32.0f - 1.26394f, 32.0f - 30.57485f },\n\t};\n\npublic:\n\t~CursorHelper() = default;\n\n\tCursorHelper(int width_, int height_) noexcept : width{width_}, height{height_}, scale{ static_cast<float>(width) / baseSize } {\n\t\t// https://learn.microsoft.com/en-us/windows/win32/menurc/using-cursors#creating-a-cursor\n\t\tbm.Create({}, width, height, &pixels);\n\t}\n\n\t[[nodiscard]] explicit operator bool() const noexcept {\n\t\treturn static_cast<bool>(bm);\n\t}\n\n\tHCURSOR Create() noexcept {\n\t\tHCURSOR cursor {};\n\t\t// Create an empty mask bitmap.\n\t\tHBITMAP hMonoBitmap = ::CreateBitmap(width, height, 1, 1, nullptr);\n\t\tif (hMonoBitmap) {\n\t\t\t// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createiconindirect\n\t\t\t// hBitmap should not already be selected into a device context\n\t\t\tHBITMAP hBitmap = bm.Extract();\n\t\t\tICONINFO info = {false, static_cast<DWORD>(width - 1), 0, hMonoBitmap, hBitmap};\n\t\t\tcursor = ::CreateIconIndirect(&info);\n\t\t\t::DeleteObject(hMonoBitmap);\n\t\t}\n\t\treturn cursor;\n\t}\n\n#if defined(USE_D2D)\n\n\tbool DrawD2D(COLORREF fillColour, COLORREF strokeColour) noexcept {\n\t\tif (!LoadD2D()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst D2D1_RENDER_TARGET_PROPERTIES drtp = D2D1::RenderTargetProperties(\n\t\t\tD2D1_RENDER_TARGET_TYPE_DEFAULT,\n\t\t\t{ DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED });\n\n\t\tDCRenderTarget pTarget;\n\t\tHRESULT hr = CreateDCRenderTarget(&drtp, pTarget);\n\t\tif (FAILED(hr) || !pTarget) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst RECT rc = {0, 0, width, height};\n\t\thr = pTarget->BindDC(bm.DC(), &rc);\n\t\tif (FAILED(hr)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tpTarget->BeginDraw();\n\n\t\t// Draw something on the bitmap section.\n\t\tconstexpr size_t nPoints = std::size(arrow);\n\t\tD2D1_POINT_2F points[nPoints]{};\n\t\tfor (size_t i = 0; i < nPoints; i++) {\n\t\t\tpoints[i].x = arrow[i][0] * scale;\n\t\t\tpoints[i].y = arrow[i][1] * scale;\n\t\t}\n\n\t\tconst Geometry geometry = GeometryCreate();\n\t\tif (!geometry) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst GeometrySink sink = GeometrySinkCreate(geometry.Get());\n\t\tif (!sink) {\n\t\t\treturn false;\n\t\t}\n\n\t\tsink->BeginFigure(points[0], D2D1_FIGURE_BEGIN_FILLED);\n\t\tfor (size_t i = 1; i < nPoints; i++) {\n\t\t\tsink->AddLine(points[i]);\n\t\t}\n\t\tsink->EndFigure(D2D1_FIGURE_END_CLOSED);\n\t\thr = sink->Close();\n\t\tif (FAILED(hr)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (const BrushSolid pBrushFill = BrushSolidCreate(pTarget.Get(), fillColour)) {\n\t\t\tpTarget->FillGeometry(geometry.Get(), pBrushFill.Get());\n\t\t}\n\n\t\tif (const BrushSolid pBrushStroke = BrushSolidCreate(pTarget.Get(), strokeColour)) {\n\t\t\tpTarget->DrawGeometry(geometry.Get(), pBrushStroke.Get(), scale);\n\t\t}\n\n\t\thr = pTarget->EndDraw();\n\t\treturn SUCCEEDED(hr);\n\t}\n#endif\n\n\tvoid Draw(COLORREF fillColour, COLORREF strokeColour) noexcept {\n#if defined(USE_D2D)\n\t\tif (DrawD2D(fillColour, strokeColour)) {\n\t\t\treturn;\n\t\t}\n#endif\n\n\t\t// Draw something on the DIB section.\n\t\tconstexpr size_t nPoints = std::size(arrow);\n\t\tPOINT points[nPoints]{};\n\t\tfor (size_t i = 0; i < nPoints; i++) {\n\t\t\tpoints[i].x = std::lround(arrow[i][0] * scale);\n\t\t\tpoints[i].y = std::lround(arrow[i][1] * scale);\n\t\t}\n\n\t\tconst DWORD penWidth = std::lround(scale);\n\t\tHPEN pen{};\n\t\tif (penWidth > 1) {\n\t\t\tconst LOGBRUSH brushParameters { BS_SOLID, strokeColour, 0 };\n\t\t\tpen = ::ExtCreatePen(PS_GEOMETRIC | PS_ENDCAP_ROUND | PS_JOIN_MITER,\n\t\t\t\tpenWidth,\n\t\t\t\t&brushParameters,\n\t\t\t\t0,\n\t\t\t\tnullptr);\n\t\t} else {\n\t\t\tpen = ::CreatePen(PS_INSIDEFRAME, 1, strokeColour);\n\t\t}\n\n\t\tHPEN penOld = SelectPen(bm.DC(), pen);\n\t\tHBRUSH brush = ::CreateSolidBrush(fillColour);\n\t\tHBRUSH brushOld = SelectBrush(bm.DC(), brush);\n\t\t::Polygon(bm.DC(), points, static_cast<int>(nPoints));\n\t\tSelectPen(bm.DC(), penOld);\n\t\tSelectBrush(bm.DC(), brushOld);\n\t\t::DeleteObject(pen);\n\t\t::DeleteObject(brush);\n\n\t\t// Set the alpha values for each pixel in the cursor.\n\t\tconstexpr DWORD opaque = 0xFF000000U;\n\t\tfor (int i = 0; i < width*height; i++) {\n\t\t\tif (*pixels != 0) {\n\t\t\t\t*pixels |= opaque;\n\t\t\t}\n\t\t\tpixels++;\n\t\t}\n\t}\n};\n\nvoid ChooseCursor(LPCTSTR cursor) noexcept {\n\t::SetCursor(::LoadCursor({}, cursor));\n}\n\nvoid ChooseCursor(Window::Cursor curs) noexcept {\n\tswitch (curs) {\n\tcase Window::Cursor::text:\n\t\tChooseCursor(IDC_IBEAM);\n\t\tbreak;\n\tcase Window::Cursor::up:\n\t\tChooseCursor(IDC_UPARROW);\n\t\tbreak;\n\tcase Window::Cursor::wait:\n\t\tChooseCursor(IDC_WAIT);\n\t\tbreak;\n\tcase Window::Cursor::horizontal:\n\t\tChooseCursor(IDC_SIZEWE);\n\t\tbreak;\n\tcase Window::Cursor::vertical:\n\t\tChooseCursor(IDC_SIZENS);\n\t\tbreak;\n\tcase Window::Cursor::hand:\n\t\tChooseCursor(IDC_HAND);\n\t\tbreak;\n\tcase Window::Cursor::reverseArrow:\n\tcase Window::Cursor::arrow:\n\tcase Window::Cursor::invalid:\t// Should not occur, but just in case.\n\tdefault:\n\t\tChooseCursor(IDC_ARROW);\n\t\tbreak;\n\t}\n}\n\n}\n\nHCURSOR LoadReverseArrowCursor(UINT dpi) noexcept {\n\t// https://learn.microsoft.com/en-us/answers/questions/815036/windows-cursor-size\n\tconstexpr DWORD defaultCursorBaseSize = 32;\n\tconstexpr DWORD maxCursorBaseSize = 16*(1 + 15); // 16*(1 + CursorSize)\n\tDWORD cursorBaseSize = 0;\n\tHKEY hKey {};\n\tLSTATUS status = ::RegOpenKeyExW(HKEY_CURRENT_USER, L\"Control Panel\\\\Cursors\", 0, KEY_QUERY_VALUE, &hKey);\n\tif (status == ERROR_SUCCESS) {\n\t\tif (std::optional<DWORD> baseSize = RegGetDWORD(hKey, L\"CursorBaseSize\")) {\n\t\t\t// CursorBaseSize is multiple of 16\n\t\t\tcursorBaseSize = std::min(*baseSize & ~15, maxCursorBaseSize);\n\t\t}\n\t\t::RegCloseKey(hKey);\n\t}\n\n\tint width = 0;\n\tint height = 0;\n\tif (cursorBaseSize > defaultCursorBaseSize) {\n\t\twidth = ::MulDiv(cursorBaseSize, dpi, USER_DEFAULT_SCREEN_DPI);\n\t\theight = width;\n\t} else {\n\t\twidth = SystemMetricsForDpi(SM_CXCURSOR, dpi);\n\t\theight = SystemMetricsForDpi(SM_CYCURSOR, dpi);\n\t\tPLATFORM_ASSERT(width == height);\n\t}\n\n\tCursorHelper cursorHelper(width, height);\n\tif (!cursorHelper) {\n\t\treturn {};\n\t}\n\n\tCOLORREF fillColour = RGB(0xff, 0xff, 0xfe);\n\tCOLORREF strokeColour = RGB(0, 0, 1);\n\tstatus = ::RegOpenKeyExW(HKEY_CURRENT_USER, L\"Software\\\\Microsoft\\\\Accessibility\", 0, KEY_QUERY_VALUE, &hKey);\n\tif (status == ERROR_SUCCESS) {\n\t\tif (std::optional<DWORD> cursorType = RegGetDWORD(hKey, L\"CursorType\")) {\n\t\t\tswitch (*cursorType) {\n\t\t\tcase 1: // black\n\t\t\tcase 4: // black\n\t\t\t\tstd::swap(fillColour, strokeColour);\n\t\t\t\tbreak;\n\t\t\tcase 6: // custom\n\t\t\t\tif (std::optional<DWORD> cursorColor = RegGetDWORD(hKey, L\"CursorColor\")) {\n\t\t\t\t\tfillColour = *cursorColor;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tdefault: // 0, 3 white, 2, 5 invert\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\t::RegCloseKey(hKey);\n\t}\n\n\tcursorHelper.Draw(fillColour, strokeColour);\n\tHCURSOR cursor = cursorHelper.Create();\n\treturn cursor;\n}\n\nvoid Window::SetCursor(Cursor curs) {\n\tChooseCursor(curs);\n}\n\n/* Returns rectangle of monitor pt is on, both rect and pt are in Window's\n   coordinates */\nPRectangle Window::GetMonitorRect(Point pt) {\n\tconst PRectangle rcPosition = GetPosition();\n\tconst POINT ptDesktop = {static_cast<LONG>(pt.x + rcPosition.left),\n\t\tstatic_cast<LONG>(pt.y + rcPosition.top)};\n\tHMONITOR hMonitor = MonitorFromPoint(ptDesktop, MONITOR_DEFAULTTONEAREST);\n\n\tconst RECT rcWork = RectFromMonitor(hMonitor);\n\tif (rcWork.left < rcWork.right) {\n\t\tPRectangle rcMonitor(\n\t\t\trcWork.left - rcPosition.left,\n\t\t\trcWork.top - rcPosition.top,\n\t\t\trcWork.right - rcPosition.left,\n\t\t\trcWork.bottom - rcPosition.top);\n\t\treturn rcMonitor;\n\t}\n\treturn PRectangle();\n}\n\nMenu::Menu() noexcept : mid{} {\n}\n\nvoid Menu::CreatePopUp() {\n\tDestroy();\n\tmid = ::CreatePopupMenu();\n}\n\nvoid Menu::Destroy() noexcept {\n\tif (mid)\n\t\t::DestroyMenu(static_cast<HMENU>(mid));\n\tmid = {};\n}\n\nvoid Menu::Show(Point pt, const Window &w) {\n\t::TrackPopupMenu(static_cast<HMENU>(mid),\n\t\tTPM_RIGHTBUTTON, static_cast<int>(pt.x - 4), static_cast<int>(pt.y), 0,\n\t\tHwndFromWindow(w), nullptr);\n\tDestroy();\n}\n\nColourRGBA ColourFromSys(int nIndex) noexcept {\n\tconst DWORD colourValue = ::GetSysColor(nIndex);\n\treturn ColourRGBA::FromRGB(colourValue);\n}\n\nColourRGBA Platform::Chrome() {\n\treturn ColourFromSys(COLOR_3DFACE);\n}\n\nColourRGBA Platform::ChromeHighlight() {\n\treturn ColourFromSys(COLOR_3DHIGHLIGHT);\n}\n\nconst char *Platform::DefaultFont() {\n\treturn \"Verdana\";\n}\n\nint Platform::DefaultFontSize() {\n\treturn 8;\n}\n\nunsigned int Platform::DoubleClickTime() {\n\treturn ::GetDoubleClickTime();\n}\n\nvoid Platform::DebugDisplay(const char *s) noexcept {\n\t::OutputDebugStringA(s);\n\t//Au:\n\tSendMessageA(FindWindowA(\"QM_Editor\", nullptr), WM_SETTEXT, (WPARAM)(-1), (LPARAM)s);\n}\n\n//#define TRACE\n\n#ifdef TRACE\nvoid Platform::DebugPrintf(const char *format, ...) noexcept {\n\tchar buffer[2000];\n\tva_list pArguments;\n\tva_start(pArguments, format);\n\tvsnprintf(buffer, std::size(buffer), format, pArguments);\n\tva_end(pArguments);\n\tPlatform::DebugDisplay(buffer);\n}\n#else\nvoid Platform::DebugPrintf(const char *, ...) noexcept {\n}\n#endif\n\nnamespace {\n\nbool assertionPopUps = true;\n\n}\n\nbool Platform::ShowAssertionPopUps(bool assertionPopUps_) noexcept {\n\tconst bool ret = assertionPopUps;\n\tassertionPopUps = assertionPopUps_;\n\treturn ret;\n}\n\nvoid Platform::Assert(const char *c, const char *file, int line) noexcept {\n\tchar buffer[2000] {};\n\tsnprintf(buffer, std::size(buffer), \"Assertion [%s] failed at %s %d%s\", c, file, line, assertionPopUps ? \"\" : \"\\r\\n\");\n\tif (assertionPopUps) {\n\t\tconst int idButton = ::MessageBoxA({}, buffer, \"Assertion failure\",\n\t\t\tMB_ABORTRETRYIGNORE|MB_ICONHAND|MB_SETFOREGROUND|MB_TASKMODAL);\n\t\tif (idButton == IDRETRY) {\n\t\t\t::DebugBreak();\n\t\t} else if (idButton == IDIGNORE) {\n\t\t\t// all OK\n\t\t} else {\n\t\t\tabort();\n\t\t}\n\t} else {\n\t\tPlatform::DebugDisplay(buffer);\n\t\t//Au: ignore, don't break/abort\n\t\t//::DebugBreak();\n\t\t//abort();\n\t}\n}\n\nvoid Platform_Initialise(void *hInstance) noexcept {\n\thinstPlatformRes = static_cast<HINSTANCE>(hInstance);\n\tLoadDpiForWindow();\n\tListBoxX_Register();\n}\n\nvoid Platform_Finalise(bool fromDllMain) noexcept {\n\tif (!fromDllMain) {\n#if defined(USE_D2D)\n\t\tReleaseD2D();\n#endif\n\t\tReleaseLibrary(hDLLShcore);\n\t}\n\tListBoxX_Unregister();\n}\n\n}\n"
  },
  {
    "path": "Libraries/scintilla/win32/PlatWin.h",
    "content": "// Scintilla source code edit control\n/** @file PlatWin.h\n ** Implementation of platform facilities on Windows.\n **/\n// Copyright 1998-2011 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#ifndef PLATWIN_H\n#define PLATWIN_H\n\nnamespace Scintilla::Internal {\n\n#ifndef USER_DEFAULT_SCREEN_DPI\n#define USER_DEFAULT_SCREEN_DPI\t\t96\n#endif\nconstexpr FLOAT dpiDefault = USER_DEFAULT_SCREEN_DPI;\n\n// Used for defining font size with LOGFONT\nconstexpr int pointsPerInch = 72;\n\nextern void Platform_Initialise(void *hInstance) noexcept;\n\nextern void Platform_Finalise(bool fromDllMain) noexcept;\n\nconstexpr RECT RectFromPRectangle(PRectangle prc) noexcept {\n\tconst RECT rc = { static_cast<LONG>(prc.left), static_cast<LONG>(prc.top),\n\t\tstatic_cast<LONG>(prc.right), static_cast<LONG>(prc.bottom) };\n\treturn rc;\n}\n\nconstexpr PRectangle PRectangleFromRECT(RECT rc) noexcept {\n\treturn PRectangle::FromInts(rc.left, rc.top, rc.right, rc.bottom);\n}\n\nconstexpr POINT POINTFromPoint(Point pt) noexcept {\n\treturn POINT{ static_cast<LONG>(pt.x), static_cast<LONG>(pt.y) };\n}\n\nconstexpr Point PointFromPOINT(POINT pt) noexcept {\n\treturn Point::FromInts(pt.x, pt.y);\n}\n\nconstexpr SIZE SizeOfRect(RECT rc) noexcept {\n\treturn { rc.right - rc.left, rc.bottom - rc.top };\n}\n\nColourRGBA ColourFromSys(int nIndex) noexcept;\n\nconstexpr HWND HwndFromWindowID(WindowID wid) noexcept {\n\treturn static_cast<HWND>(wid);\n}\n\ninline HWND HwndFromWindow(const Window &w) noexcept {\n\treturn HwndFromWindowID(w.GetID());\n}\n\nextern HINSTANCE hinstPlatformRes;\n\nUINT CodePageFromCharSet(CharacterSet characterSet, UINT documentCodePage) noexcept;\n\nvoid *PointerFromWindow(HWND hWnd) noexcept;\nvoid SetWindowPointer(HWND hWnd, void *ptr) noexcept;\n\nHMONITOR MonitorFromWindowHandleScaling(HWND hWnd) noexcept;\n\nUINT DpiForWindow(WindowID wid) noexcept;\nfloat GetDeviceScaleFactorWhenGdiScalingActive(HWND hWnd) noexcept;\n\nint SystemMetricsForDpi(int nIndex, UINT dpi) noexcept;\n\nvoid AdjustWindowRectForDpi(LPRECT lpRect, DWORD dwStyle, UINT dpi) noexcept;\n\nHCURSOR LoadReverseArrowCursor(UINT dpi) noexcept;\n\n// Encapsulate WM_PAINT handling so that EndPaint is always called even with unexpected returns or exceptions.\nstruct Painter {\n\tHWND hWnd{};\n\tPAINTSTRUCT ps{};\n\texplicit Painter(HWND hWnd_) noexcept : hWnd(hWnd_) {\n\t\t::BeginPaint(hWnd, &ps);\n\t}\n\t~Painter() {\n\t\t::EndPaint(hWnd, &ps);\n\t}\n};\n\nclass MouseWheelDelta {\n\tint wheelDelta = 0;\npublic:\n\tbool Accumulate(WPARAM wParam) noexcept {\n\t\twheelDelta -= GET_WHEEL_DELTA_WPARAM(wParam);\n\t\treturn std::abs(wheelDelta) >= WHEEL_DELTA;\n\t}\n\tint Actions() noexcept {\n\t\tconst int actions = wheelDelta / WHEEL_DELTA;\n\t\twheelDelta = wheelDelta % WHEEL_DELTA;\n\t\treturn actions;\n\t}\n};\n\n// Both GDI and DirectWrite can produce a HFONT for use in list boxes\nstruct FontWin : public Font {\n\t[[nodiscard]] virtual HFONT HFont() const noexcept = 0;\n\t[[nodiscard]] virtual std::unique_ptr<FontWin> Duplicate() const = 0;\n\t[[nodiscard]] virtual CharacterSet GetCharacterSet() const noexcept = 0;\n};\n\n// Buffer to hold strings and string position arrays without always allocating on heap.\n// May sometimes have string too long to allocate on stack. So use a fixed stack-allocated buffer\n// when less than safe size otherwise allocate on heap and free automatically.\ntemplate<typename T, int lengthStandard>\nclass VarBuffer {\n\tT bufferStandard[lengthStandard];\npublic:\n\tT *buffer;\n\texplicit VarBuffer(size_t length) : buffer(nullptr) {\n\t\tif (length > lengthStandard) {\n\t\t\tbuffer = new T[length];\n\t\t} else {\n\t\t\tbuffer = bufferStandard;\n\t\t}\n\t}\n\t// Deleted so VarBuffer objects can not be copied.\n\tVarBuffer(const VarBuffer &) = delete;\n\tVarBuffer(VarBuffer &&) = delete;\n\tVarBuffer &operator=(const VarBuffer &) = delete;\n\tVarBuffer &operator=(VarBuffer &&) = delete;\n\n\t~VarBuffer() noexcept {\n\t\tif (buffer != bufferStandard) {\n\t\t\tdelete[]buffer;\n\t\t\tbuffer = nullptr;\n\t\t}\n\t}\n};\n\nconstexpr int stackBufferLength = 400;\nclass TextWide : public VarBuffer<wchar_t, stackBufferLength> {\npublic:\n\tint tlen;\t// Using int instead of size_t as most Win32 APIs take int.\n\tTextWide(std::string_view text, int codePage) :\n\t\tVarBuffer<wchar_t, stackBufferLength>(text.length()) {\n\t\tif (codePage == CpUtf8) {\n\t\t\ttlen = static_cast<int>(UTF16FromUTF8(text, buffer, text.length()));\n\t\t} else {\n\t\t\t// Support Asian string display in 9x English\n\t\t\ttlen = ::MultiByteToWideChar(codePage, 0, text.data(), static_cast<int>(text.length()),\n\t\t\t\tbuffer, static_cast<int>(text.length()));\n\t\t}\n\t}\n\t[[nodiscard]] std::wstring_view AsView() const noexcept {\n\t\treturn std::wstring_view(buffer, tlen);\n\t}\n};\nusing TextPositions = VarBuffer<XYPOSITION, stackBufferLength>;\n\n// Manage the lifetime of a memory HBITMAP and its HDC so there are no leaks.\nclass GDIBitMap {\n\tHDC hdc{};\n\tHBITMAP hbm{};\n\tHBITMAP hbmOriginal{};\n\npublic:\n\tGDIBitMap() noexcept = default;\n\t// Deleted so GDIBitMap objects can not be copied.\n\tGDIBitMap(const GDIBitMap &) = delete;\n\tGDIBitMap(GDIBitMap &&) = delete;\n\t// Move would be OK but not needed yet\n\tGDIBitMap &operator=(const GDIBitMap &) = delete;\n\tGDIBitMap &operator=(GDIBitMap &&) = delete;\n\t~GDIBitMap() noexcept;\n\n\tvoid Create(HDC hdcBase, int width, int height, DWORD **pixels) noexcept;\n\tvoid Release() noexcept;\n\tHBITMAP Extract() noexcept;\n\n\t[[nodiscard]] HDC DC() const noexcept {\n\t\treturn hdc;\n\t}\n\t[[nodiscard]] explicit operator bool() const noexcept {\n\t\treturn hdc && hbm;\n\t}\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/win32/ScintRes.rc",
    "content": "// Resource file for Scintilla\n// Copyright 1998-2010 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#include <windows.h>\n\n#define VERSION_SCINTILLA \"5.5.7\"\n#define VERSION_WORDS 5, 5, 7, 0\n\nVS_VERSION_INFO VERSIONINFO\nFILEVERSION\tVERSION_WORDS\nPRODUCTVERSION\tVERSION_WORDS\nFILEFLAGSMASK\t0x3fL\nFILEFLAGS 0\nFILEOS VOS_NT_WINDOWS32\nFILETYPE VFT_APP\nFILESUBTYPE VFT2_UNKNOWN\nBEGIN\n\tBLOCK\t\"VarFileInfo\"\n\tBEGIN\n\t\tVALUE\t\"Translation\",\t0x409,\t1200\n\tEND\n\tBLOCK\t\"StringFileInfo\"\n\tBEGIN\n\t\tBLOCK \"040904b0\"\n\t\tBEGIN\n\t\t\tVALUE\t\"CompanyName\",\t\"Neil Hodgson neilh@scintilla.org\\0\"\n\t\t\tVALUE\t\"FileDescription\",\t\"Scintilla.DLL - a Source Editing Component\\0\"\n\t\t\tVALUE\t\"FileVersion\",\tVERSION_SCINTILLA \"\\0\"\n\t\t\tVALUE\t\"InternalName\",\t\"Scintilla\\0\"\n\t\t\tVALUE\t\"LegalCopyright\",\t\"Copyright 1998-2012 by Neil Hodgson\\0\"\n\t\t\tVALUE\t\"OriginalFilename\",\t\"Scintilla.DLL\\0\"\n\t\t\tVALUE\t\"ProductName\",\t\"Scintilla\\0\"\n\t\t\tVALUE\t\"ProductVersion\",\tVERSION_SCINTILLA \"\\0\"\n\t\tEND\n\tEND\nEND\n"
  },
  {
    "path": "Libraries/scintilla/win32/Scintilla.def",
    "content": "EXPORTS\n\tScintilla_DirectFunction"
  },
  {
    "path": "Libraries/scintilla/win32/Scintilla.vcxproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project DefaultTargets=\"Build\" ToolsVersion=\"14.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup Label=\"ProjectConfigurations\">\n    <ProjectConfiguration Include=\"Debug|ARM64\">\n      <Configuration>Debug</Configuration>\n      <Platform>ARM64</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Debug|x64\">\n      <Configuration>Debug</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release|ARM64\">\n      <Configuration>Release</Configuration>\n      <Platform>ARM64</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release|x64\">\n      <Configuration>Release</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n  </ItemGroup>\n  <PropertyGroup Label=\"Globals\">\n    <ProjectGuid>{19CCA8B8-46B9-4609-B7CE-198DA19F07BD}</ProjectGuid>\n    <Keyword>Win32Proj</Keyword>\n    <RootNamespace>Scintilla</RootNamespace>\n    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\n  <PropertyGroup>\n    <ConfigurationType>DynamicLibrary</ConfigurationType>\n    <CharacterSet>Unicode</CharacterSet>\n    <PlatformToolset>v143</PlatformToolset>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"Configuration\">\n    <UseDebugLibraries>true</UseDebugLibraries>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|ARM64'\" Label=\"Configuration\">\n    <UseDebugLibraries>true</UseDebugLibraries>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|ARM64'\" Label=\"Configuration\">\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\n  <ImportGroup Label=\"ExtensionSettings\">\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <PropertyGroup Label=\"UserMacros\" />\n  <PropertyGroup>\n    <LinkIncremental>false</LinkIncremental>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <IntDir>obj\\$(Configuration)\\$(Platform)\\</IntDir>\n    <OutDir>bin\\$(Configuration)\\$(Platform)\\</OutDir>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|ARM64'\">\n    <IntDir>obj\\$(Configuration)\\$(Platform)\\</IntDir>\n    <OutDir>bin\\$(Configuration)\\$(Platform)\\</OutDir>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <IntDir>obj\\$(Configuration)\\$(Platform)\\</IntDir>\n    <OutDir>bin\\$(Configuration)\\$(Platform)\\</OutDir>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|ARM64'\">\n    <IntDir>obj\\$(Configuration)\\$(Platform)\\</IntDir>\n    <OutDir>bin\\$(Configuration)\\$(Platform)\\</OutDir>\n  </PropertyGroup>\n  <ItemDefinitionGroup>\n    <ClCompile>\n      <WarningLevel>Level4</WarningLevel>\n      <PreprocessorDefinitions>SCI_EMPTYCATALOGUE;_CRT_SECURE_NO_DEPRECATE;_SCL_SECURE_NO_WARNINGS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <AdditionalIncludeDirectories>..\\include;..\\src;</AdditionalIncludeDirectories>\n      <BrowseInformation>true</BrowseInformation>\n      <MultiProcessorCompilation>true</MultiProcessorCompilation>\n      <MinimalRebuild>false</MinimalRebuild>\n      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>\n      <AdditionalOptions>/source-charset:utf-8 %(AdditionalOptions)</AdditionalOptions>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <AdditionalDependencies>gdi32.lib;imm32.lib;ole32.lib;oleaut32.lib;advapi32.lib;%(AdditionalDependencies)</AdditionalDependencies>\n      <CETCompat>false</CETCompat>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <ClCompile>\n      <PreprocessorDefinitions>_DEBUG;DISABLE_D2D;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <LanguageStandard>stdcpp17</LanguageStandard>\n      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>\n    </ClCompile>\n    <Link>\n      <LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>\n    </Link>\n    <PostBuildEvent>\n      <Command>$(SolutionDir)Other\\BuildEvents\\bin\\Debug\\BuildEvents.exe dllPostBuild \"$(TargetPath)\" $(Platform)</Command>\n    </PostBuildEvent>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|ARM64'\">\n    <ClCompile>\n      <PreprocessorDefinitions>_DEBUG;DISABLE_D2D;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <LanguageStandard>stdcpp17</LanguageStandard>\n      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>\n    </ClCompile>\n    <Link>\n      <LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>\n    </Link>\n    <PostBuildEvent>\n      <Command>$(SolutionDir)Other\\BuildEvents\\bin\\Debug\\BuildEvents.exe dllPostBuild \"$(TargetPath)\" $(Platform)</Command>\n    </PostBuildEvent>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <ClCompile>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <PreprocessorDefinitions>NDEBUG;DISABLE_D2D;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <LanguageStandard>stdcpp17</LanguageStandard>\n      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\n    </ClCompile>\n    <Link>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n    </Link>\n    <PostBuildEvent>\n      <Command>$(SolutionDir)Other\\BuildEvents\\bin\\Debug\\BuildEvents.exe dllPostBuild \"$(TargetPath)\" $(Platform)</Command>\n    </PostBuildEvent>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|ARM64'\">\n    <ClCompile>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <PreprocessorDefinitions>NDEBUG;DISABLE_D2D;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <LanguageStandard>stdcpp17</LanguageStandard>\n      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\n    </ClCompile>\n    <Link>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n    </Link>\n    <PostBuildEvent>\n      <Command>$(SolutionDir)Other\\BuildEvents\\bin\\Debug\\BuildEvents.exe dllPostBuild \"$(TargetPath)\" $(Platform)</Command>\n    </PostBuildEvent>\n  </ItemDefinitionGroup>\n  <ItemGroup>\n    <ClCompile Include=\"..\\src\\*.cxx\" />\n    <ClCompile Include=\"..\\win32\\HanjaDic.cxx\" />\n    <ClCompile Include=\"..\\win32\\PlatWin.cxx\" />\n    <ClCompile Include=\"..\\win32\\ListBox.cxx\" />\n    <ClCompile Include=\"..\\win32\\SurfaceD2D.cxx\" />\n    <ClCompile Include=\"..\\win32\\SurfaceGDI.cxx\" />\n    <ClCompile Include=\"..\\win32\\ScintillaWin.cxx\" />\n    <ClCompile Include=\"..\\win32\\ScintillaDLL.cxx\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ClInclude Include=\"..\\include\\*.h\" />\n    <ClInclude Include=\"..\\src\\*.h\" />\n    <ClInclude Include=\"..\\win32\\*.h\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ResourceCompile Include=\"..\\win32\\ScintRes.rc\" />\n  </ItemGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\n  <ImportGroup Label=\"ExtensionTargets\">\n  </ImportGroup>\n</Project>"
  },
  {
    "path": "Libraries/scintilla/win32/ScintillaDLL.cxx",
    "content": "// Scintilla source code edit control\n/** @file ScintillaDLL.cxx\n ** DLL entry point for Scintilla.\n **/\n// Copyright 1998-2018 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#include <cstdint>\n\n#undef _WIN32_WINNT\n#define _WIN32_WINNT 0x0500\n#undef WINVER\n#define WINVER 0x0500\n#include <windows.h>\n\n#include \"ScintillaTypes.h\"\n#include \"ScintillaWin.h\"\n\nusing namespace Scintilla;\n\nextern \"C\"\n__declspec(dllexport)\nsptr_t __stdcall Scintilla_DirectFunction(\n    Internal::ScintillaWin *sci, UINT iMessage, uptr_t wParam, sptr_t lParam) {\n\treturn Internal::DirectFunction(sci, iMessage, wParam, lParam);\n}\n\nextern \"C\" int APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpvReserved) {\n\t//Platform::DebugPrintf(\"Scintilla::DllMain %d %d\\n\", hInstance, dwReason);\n\tif (dwReason == DLL_PROCESS_ATTACH) {\n\t\tif (!Internal::RegisterClasses(hInstance))\n\t\t\treturn FALSE;\n\t} else if (dwReason == DLL_PROCESS_DETACH) {\n\t\tif (lpvReserved == NULL) {\n\t\t\tInternal::ResourcesRelease(true);\n\t\t}\n\t}\n\treturn TRUE;\n}\n"
  },
  {
    "path": "Libraries/scintilla/win32/ScintillaWin.cxx",
    "content": "// Scintilla source code edit control\n/** @file ScintillaWin.cxx\n ** Windows specific subclass of ScintillaBase.\n **/\n// Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#include <cstddef>\n#include <cstdlib>\n#include <cstdint>\n#include <cassert>\n#include <cstring>\n#include <cstdio>\n#include <cmath>\n#include <climits>\n\n#include <stdexcept>\n#include <new>\n#include <string>\n#include <string_view>\n#include <vector>\n#include <array>\n#include <map>\n#include <set>\n#include <optional>\n#include <algorithm>\n#include <memory>\n#include <chrono>\n#include <mutex>\n\n// Want to use std::min and std::max so don't want Windows.h version of min and max\n#if !defined(NOMINMAX)\n#define NOMINMAX\n#endif\n#undef _WIN32_WINNT\n#define _WIN32_WINNT 0x0A00\n#undef WINVER\n#define WINVER 0x0A00\n#define WIN32_LEAN_AND_MEAN 1\n#include <windows.h>\n#include <commctrl.h>\n#include <richedit.h>\n#include <windowsx.h>\n#include <zmouse.h>\n#include <ole2.h>\n\n#include <wrl.h>\nusing Microsoft::WRL::ComPtr;\n\n#if !defined(DISABLE_D2D)\n#define USE_D2D 1\n#endif\n\n#if defined(USE_D2D)\n#include <d2d1_1.h>\n#include <d3d11_1.h>\n#include <dwrite_1.h>\n#endif\n\n#include \"ScintillaTypes.h\"\n#include \"ScintillaMessages.h\"\n#include \"ScintillaStructures.h\"\n#include \"ILoader.h\"\n#include \"ILexer.h\"\n\n#include \"Debugging.h\"\n#include \"Geometry.h\"\n#include \"Platform.h\"\n\n#include \"CharacterType.h\"\n#include \"CharacterCategoryMap.h\"\n#include \"Position.h\"\n#include \"UniqueString.h\"\n#include \"SplitVector.h\"\n#include \"Partitioning.h\"\n#include \"RunStyles.h\"\n#include \"ContractionState.h\"\n#include \"CellBuffer.h\"\n#include \"CallTip.h\"\n#include \"KeyMap.h\"\n#include \"Indicator.h\"\n#include \"LineMarker.h\"\n#include \"Style.h\"\n#include \"ViewStyle.h\"\n#include \"CharClassify.h\"\n#include \"Decoration.h\"\n#include \"CaseFolder.h\"\n#include \"Document.h\"\n#include \"CaseConvert.h\"\n#include \"UniConversion.h\"\n#include \"DBCS.h\"\n#include \"Selection.h\"\n#include \"PositionCache.h\"\n#include \"EditModel.h\"\n#include \"MarginView.h\"\n#include \"EditView.h\"\n#include \"Editor.h\"\n#include \"ElapsedPeriod.h\"\n\n#include \"AutoComplete.h\"\n#include \"ScintillaBase.h\"\n\n#include \"WinTypes.h\"\n#include \"PlatWin.h\"\n#if defined(USE_D2D)\n#include \"SurfaceD2D.h\"\n#endif\n#include \"HanjaDic.h\"\n#include \"ScintillaWin.h\"\n\n// __uuidof is a Microsoft extension but makes COM code neater, so disable warning\n#if defined(__clang__)\n#pragma clang diagnostic ignored \"-Wlanguage-extension-token\"\n#endif\n\nnamespace {\n\n// Two idle messages SC_WIN_IDLE and SC_WORK_IDLE.\n\n// SC_WIN_IDLE is low priority so should occur after the next WM_PAINT\n// It is for lengthy actions like wrapping and background styling\nconstexpr UINT SC_WIN_IDLE = 5001;\n// SC_WORK_IDLE is high priority and should occur before the next WM_PAINT\n// It is for shorter actions like restyling the text just inserted\n// and delivering SCN_UPDATEUI\nconstexpr UINT SC_WORK_IDLE = 5002;\n\nusing SetCoalescableTimerSig = UINT_PTR (WINAPI *)(HWND hwnd, UINT_PTR nIDEvent,\n\tUINT uElapse, TIMERPROC lpTimerFunc, ULONG uToleranceDelay);\n\n}\n\n// GCC has trouble with the standard COM ABI so do it the old C way with explicit vtables.\n\nusing namespace Scintilla;\nusing namespace Scintilla::Internal;\n\nnamespace {\n\nconst TCHAR callClassName[] = TEXT(\"CallTip\");\n\nvoid SetWindowID(HWND hWnd, int identifier) noexcept {\n\t::SetWindowLongPtr(hWnd, GWLP_ID, identifier);\n}\n\nconstexpr POINT POINTFromLParam(sptr_t lParam) noexcept {\n\treturn { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };\n};\n\nconstexpr Point PointFromLParam(sptr_t lpoint) noexcept {\n\treturn Point::FromInts(GET_X_LPARAM(lpoint), GET_Y_LPARAM(lpoint));\n}\n\nbool KeyboardIsKeyDown(int key) noexcept {\n\treturn (::GetKeyState(key) & 0x80000000) != 0;\n}\n\n// Bit 24 is the extended keyboard flag and the numeric keypad is non-extended\nconstexpr sptr_t extendedKeyboard = 1 << 24;\n\nconstexpr bool KeyboardIsNumericKeypadFunction(uptr_t wParam, sptr_t lParam) {\n\tif ((lParam & extendedKeyboard) != 0) {\n\t\t// Not from the numeric keypad\n\t\treturn false;\n\t}\n\n\tswitch (wParam) {\n\tcase VK_INSERT:\t// 0\n\tcase VK_END:\t// 1\n\tcase VK_DOWN:\t// 2\n\tcase VK_NEXT:\t// 3\n\tcase VK_LEFT:\t// 4\n\tcase VK_CLEAR:\t// 5\n\tcase VK_RIGHT:\t// 6\n\tcase VK_HOME:\t// 7\n\tcase VK_UP:\t\t// 8\n\tcase VK_PRIOR:\t// 9\n\t\treturn true;\n\tdefault:\n\t\treturn false;\n\t}\n}\n\n/**\n */\nclass FormatEnumerator final : public IEnumFORMATETC {\npublic:\n\tULONG ref;\n\tULONG pos;\n\tstd::vector<CLIPFORMAT> formats;\n\tFormatEnumerator(ULONG pos_, const CLIPFORMAT formats_[], size_t formatsLen_);\n\n\t// IUnknown\n\tSTDMETHODIMP QueryInterface(REFIID riid, PVOID *ppv) override;\n\tSTDMETHODIMP_(ULONG)AddRef() override;\n\tSTDMETHODIMP_(ULONG)Release() override;\n\n\t// IEnumFORMATETC\n\tSTDMETHODIMP Next(ULONG celt, FORMATETC *rgelt, ULONG *pceltFetched) override;\n\tSTDMETHODIMP Skip(ULONG celt) override;\n\tSTDMETHODIMP Reset() override;\n\tSTDMETHODIMP Clone(IEnumFORMATETC **ppenum) override;\n};\n\n/**\n */\nclass DropSource final : public IDropSource {\npublic:\n\tScintillaWin *sci = nullptr;\n\n\t// IUnknown\n\tSTDMETHODIMP QueryInterface(REFIID riid, PVOID *ppv) override;\n\tSTDMETHODIMP_(ULONG)AddRef() override;\n\tSTDMETHODIMP_(ULONG)Release() override;\n\n\t// IDropSource\n\tSTDMETHODIMP QueryContinueDrag(BOOL fEsc, DWORD grfKeyState) override;\n\tSTDMETHODIMP GiveFeedback(DWORD) override;\n};\n\n/**\n */\nclass DataObject final : public IDataObject {\npublic:\n\tScintillaWin *sci = nullptr;\n\n\t// IUnknown\n\tSTDMETHODIMP QueryInterface(REFIID riid, PVOID *ppv) override;\n\tSTDMETHODIMP_(ULONG)AddRef() override;\n\tSTDMETHODIMP_(ULONG)Release() override;\n\n\t// IDataObject\n\tSTDMETHODIMP GetData(FORMATETC *pFEIn, STGMEDIUM *pSTM) override;\n\tSTDMETHODIMP GetDataHere(FORMATETC *, STGMEDIUM *) override;\n\tSTDMETHODIMP QueryGetData(FORMATETC *pFE) override;\n\tSTDMETHODIMP GetCanonicalFormatEtc(FORMATETC *, FORMATETC *pFEOut)  override;\n\tSTDMETHODIMP SetData(FORMATETC *, STGMEDIUM *, BOOL) override;\n\tSTDMETHODIMP EnumFormatEtc(DWORD dwDirection, IEnumFORMATETC **ppEnum) override;\n\tSTDMETHODIMP DAdvise(FORMATETC *, DWORD, IAdviseSink *, PDWORD) override;\n\tSTDMETHODIMP DUnadvise(DWORD) override;\n\tSTDMETHODIMP EnumDAdvise(IEnumSTATDATA **) override;\n};\n\n/**\n */\nclass DropTarget final : public IDropTarget {\npublic:\n\tScintillaWin *sci = nullptr;\n\n\t// IUnknown\n\tSTDMETHODIMP QueryInterface(REFIID riid, PVOID *ppv) override;\n\tSTDMETHODIMP_(ULONG)AddRef() override;\n\tSTDMETHODIMP_(ULONG)Release() override;\n\n\t// IDropTarget\n\tSTDMETHODIMP DragEnter(LPDATAOBJECT pIDataSource, DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) override;\n\tSTDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) override;\n\tSTDMETHODIMP DragLeave() override;\n\tSTDMETHODIMP Drop(LPDATAOBJECT pIDataSource, DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) override;\n};\n\nclass IMContext {\n\tHWND hwnd;\n\tHIMC hIMC;\npublic:\n\n\texplicit IMContext(HWND hwnd_) noexcept :\n\t\thwnd(hwnd_), hIMC(::ImmGetContext(hwnd_)) {\n\t}\n\n\t// Deleted so IMContext objects can not be copied.\n\tIMContext(const IMContext &) = delete;\n\tIMContext(IMContext &&) = delete;\n\tIMContext &operator=(const IMContext &) = delete;\n\tIMContext &operator=(IMContext &&) = delete;\n\n\t~IMContext() {\n\t\tif (hIMC)\n\t\t\t::ImmReleaseContext(hwnd, hIMC);\n\t}\n\n\t[[nodiscard]] explicit operator bool() const noexcept {\n\t\treturn hIMC != HIMC{};\n\t}\n\n\t[[nodiscard]] unsigned int GetImeCaretPos() const noexcept {\n\t\treturn ::ImmGetCompositionStringW(hIMC, GCS_CURSORPOS, nullptr, 0);\n\t}\n\n\t[[nodiscard]] std::vector<BYTE> GetImeAttributes() const {\n\t\tconst int attrLen = ::ImmGetCompositionStringW(hIMC, GCS_COMPATTR, nullptr, 0);\n\t\tstd::vector<BYTE> attr(attrLen, 0);\n\t\t::ImmGetCompositionStringW(hIMC, GCS_COMPATTR, attr.data(), static_cast<DWORD>(attr.size()));\n\t\treturn attr;\n\t}\n\n\t[[nodiscard]] LONG GetCompositionStringLength(DWORD dwIndex) const noexcept {\n\t\tconst LONG byteLen = ::ImmGetCompositionStringW(hIMC, dwIndex, nullptr, 0);\n\t\treturn byteLen / sizeof(wchar_t);\n\t}\n\n\t[[nodiscard]] std::wstring GetCompositionString(DWORD dwIndex) const {\n\t\tconst LONG byteLen = ::ImmGetCompositionStringW(hIMC, dwIndex, nullptr, 0);\n\t\tstd::wstring wcs(byteLen / 2, 0);\n\t\t::ImmGetCompositionStringW(hIMC, dwIndex, wcs.data(), byteLen);\n\t\treturn wcs;\n\t}\n\n\tBOOL SetCompositionString(DWORD dwIndex, LPVOID lpComp, DWORD dwCompLen) const noexcept {\n\t\treturn ::ImmSetCompositionStringW(hIMC, dwIndex, lpComp, dwCompLen, nullptr, 0);\n\t}\n\n\tvoid SetCompositionWindow(Point pos) const noexcept {\n\t\tCOMPOSITIONFORM CompForm{};\n\t\tCompForm.dwStyle = CFS_POINT;\n\t\tCompForm.ptCurrentPos = POINTFromPoint(pos);\n\t\t::ImmSetCompositionWindow(hIMC, &CompForm);\n\t}\n\n\tvoid SetCandidateWindowPos(Point pos, PRectangle rcClient, int lineHeight) const noexcept {\n\t\tCANDIDATEFORM CandForm{};\n\t\tCandForm.dwIndex = 0;\n\t\tCandForm.dwStyle = CFS_EXCLUDE;\n\t\tCandForm.ptCurrentPos.x = static_cast<int>(pos.x);\n\t\tCandForm.ptCurrentPos.y = static_cast<int>(pos.y + std::max(4, lineHeight / 4));\n\t\t// Exclude the area of the whole caret line\n\t\tCandForm.rcArea.top = static_cast<int>(pos.y);\n\t\tCandForm.rcArea.bottom = static_cast<int>(pos.y + lineHeight);\n\t\tCandForm.rcArea.left = static_cast<int>(rcClient.left);\n\t\tCandForm.rcArea.right = static_cast<int>(rcClient.right);\n\t\t::ImmSetCandidateWindow(hIMC, &CandForm);\n\t}\n\n\tvoid SetCompositionFont(const ViewStyle &vs, int style, UINT dpi) const {\n\t\tLOGFONTW lf{};\n\t\tint sizeZoomed = vs.styles[style].size + (vs.zoomLevel * FontSizeMultiplier);\n\t\tif (sizeZoomed <= 2 * FontSizeMultiplier)\t// Hangs if sizeZoomed <= 1\n\t\t\tsizeZoomed = 2 * FontSizeMultiplier;\n\t\t// The negative is to allow for leading\n\t\tlf.lfHeight = -::MulDiv(sizeZoomed, dpi, pointsPerInch * FontSizeMultiplier);\n\t\tlf.lfWeight = static_cast<LONG>(vs.styles[style].weight);\n\t\tlf.lfItalic = vs.styles[style].italic ? 1 : 0;\n\t\tlf.lfCharSet = DEFAULT_CHARSET;\n\t\tlf.lfFaceName[0] = L'\\0';\n\t\tif (vs.styles[style].fontName) {\n\t\t\tconst char *fontName = vs.styles[style].fontName;\n\t\t\tUTF16FromUTF8(std::string_view(fontName), lf.lfFaceName, LF_FACESIZE);\n\t\t}\n\n\t\t::ImmSetCompositionFontW(hIMC, &lf);\n\t}\n\n\tvoid Notify(bool complete) const noexcept {\n\t\t::ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, complete ? CPS_COMPLETE : CPS_CANCEL, 0);\n\t}\n\n\tLRESULT Escape(HKL hkl, UINT uEscape, LPVOID lpv) const noexcept {\n\t\treturn ::ImmEscapeW(hkl, hIMC, uEscape, lpv);\n\t}\n\n};\n\nclass GlobalMemory;\n\nclass ReverseArrowCursor {\n\tHCURSOR cursor {};\n\tbool valid = false;\n\npublic:\n\tReverseArrowCursor() noexcept = default;\n\t// Deleted so ReverseArrowCursor objects can not be copied.\n\tReverseArrowCursor(const ReverseArrowCursor &) = delete;\n\tReverseArrowCursor(ReverseArrowCursor &&) = delete;\n\tReverseArrowCursor &operator=(const ReverseArrowCursor &) = delete;\n\tReverseArrowCursor &operator=(ReverseArrowCursor &&) = delete;\n\t~ReverseArrowCursor() {\n\t\tif (cursor) {\n\t\t\t::DestroyCursor(cursor);\n\t\t}\n\t}\n\n\tvoid Invalidate() noexcept {\n\t\tvalid = false;\n\t}\n\n\tHCURSOR Load([[maybe_unused]] UINT dpi) noexcept {\n\t\tif (cursor)\t {\n\t\t\tif (valid) {\n\t\t\t\treturn cursor;\n\t\t\t}\n\t\t\t::DestroyCursor(cursor);\n\t\t}\n\n\t\tvalid = true;\n\t\t//cursor = LoadReverseArrowCursor(dpi); //Au: the reverse arrow cursor is ugly, even if drawn with D2D. Eg VSCode uses normal arrow too.\n\t\treturn cursor ? cursor : ::LoadCursor({}, IDC_ARROW);\n\t}\n};\n\nstruct HorizontalScrollRange {\n\tint pageWidth;\n\tint documentWidth;\n};\n\nCLIPFORMAT RegisterClipboardType(LPCWSTR lpszFormat) noexcept {\n\t// Registered clipboard format values are 0xC000 through 0xFFFF.\n\t// RegisterClipboardFormatW returns 32-bit unsigned and CLIPFORMAT is 16-bit\n\t// unsigned so choose the low 16-bits with &.\n\treturn ::RegisterClipboardFormatW(lpszFormat) & 0xFFFF;\n}\n\nRECT GetClientRect(HWND hwnd) noexcept {\n\tRECT rect{};\n\t::GetClientRect(hwnd, &rect);\n\treturn rect;\n}\n\n#if defined(USE_D2D)\n\nD2D1_SIZE_U GetSizeUFromRect(const RECT &rc, const int scaleFactor) noexcept {\n\tconst SIZE size = SizeOfRect(rc);\n\tconst UINT32 scaledWidth = size.cx * scaleFactor;\n\tconst UINT32 scaledHeight = size.cy * scaleFactor;\n\treturn D2D1::SizeU(scaledWidth, scaledHeight);\n}\n\n#endif\n\n}\n\nnamespace Scintilla::Internal {\n\n#if defined(USE_D2D)\n\nusing HwndRenderTarget = ComPtr<ID2D1HwndRenderTarget>;\n\n// There may be either a Hwnd or DC render target\nstruct RenderTargets {\n\tHwndRenderTarget pHwndRT;\n\tDCRenderTarget pDCRT;\n\tComPtr<ID2D1DeviceContext> pDeviceContext;\n\tbool valid = true;\n\t[[nodiscard]] ID2D1RenderTarget *RenderTarget() const noexcept {\n\t\tif (pHwndRT)\n\t\t\treturn pHwndRT.Get();\n\t\tif (pDCRT)\n\t\t\treturn pDCRT.Get();\n\t\tif (pDeviceContext)\n\t\t\treturn pDeviceContext.Get();\n\t\treturn nullptr;\n\t}\n\tvoid Release() noexcept {\n\t\tpHwndRT = nullptr;\n\t\tpDCRT = nullptr;\n\t\tpDeviceContext = nullptr;\n\t}\n};\n\n// These resources are device-dependent but not window-dependent\nstruct DirectDevice {\n\tD3D11Device pDirect3DDevice;\n\tComPtr<ID2D1Device> pDirect2DDevice;\n\tComPtr<IDXGIDevice> pDXGIDevice;\n\n\tvoid Release() noexcept;\n\tHRESULT CreateDevice() noexcept;\n};\n\nvoid DirectDevice::Release() noexcept {\n\tpDirect3DDevice = nullptr;\n\tpDirect2DDevice = nullptr;\n\tpDXGIDevice = nullptr;\n}\n\nHRESULT DirectDevice::CreateDevice() noexcept {\n\tif (pDirect2DDevice) {\t// Must be released before creation\n\t\treturn E_FAIL;\n\t}\n\n\tHRESULT hr = CreateD3D(pDirect3DDevice);\n\tif (FAILED(hr)) {\n\t\treturn hr;\n\t}\n\n\thr = pDirect3DDevice.As(&pDXGIDevice);\n\tif (FAILED(hr)) {\n\t\tPlatform::DebugPrintf(\"Failed to create DXGI device 0x%lx\\n\", hr);\n\t\treturn hr;\n\t}\n\n\thr = pD2DFactory->CreateDevice(pDXGIDevice.Get(), pDirect2DDevice.ReleaseAndGetAddressOf());\n\tif (FAILED(hr)) {\n\t\tPlatform::DebugPrintf(\"Failed to create D2D device 0x%lx\\n\", hr);\n\t\treturn hr;\n\t}\n\n\treturn S_OK;\n}\n\n#endif\n\n/**\n */\nclass ScintillaWin :\n\tpublic ScintillaBase {\n\n\tbool lastKeyDownConsumed;\n\twchar_t lastHighSurrogateChar;\n\n\tbool capturedMouse;\n\tbool trackedMouseLeave;\n\tBOOL typingWithoutCursor;\n\tbool cursorIsHidden;\n\tSetCoalescableTimerSig SetCoalescableTimerFn;\n\n\tunsigned int linesPerScroll;\t///< Intellimouse support\n\tunsigned int charsPerScroll;\t///< Intellimouse support\n\tMouseWheelDelta verticalWheelDelta;\n\tMouseWheelDelta horizontalWheelDelta;\n\n\tUINT dpi = USER_DEFAULT_SCREEN_DPI;\n\tReverseArrowCursor reverseArrowCursor;\n\n\tPRectangle rectangleClient;\n\tHRGN hRgnUpdate;\n\n\tbool hasOKText;\n\n\tCLIPFORMAT cfColumnSelect;\n\tCLIPFORMAT cfBorlandIDEBlockType;\n\tCLIPFORMAT cfLineSelect;\n\tCLIPFORMAT cfVSLineTag;\n\n\tHRESULT hrOle;\n\tDropSource ds;\n\tDataObject dob;\n\tDropTarget dt;\n\n\tstatic HINSTANCE hInstance;\n\tstatic ATOM scintillaClassAtom;\n\tstatic ATOM callClassAtom;\n\n\tfloat deviceScaleFactor = 1.f;\n\t[[nodiscard]] int GetFirstIntegralMultipleDeviceScaleFactor() const noexcept {\n\t\t return static_cast<int>(std::ceil(deviceScaleFactor));\n\t}\n\n#if defined(USE_D2D)\n\tDirectDevice device;\n\tComPtr<IDXGISwapChain1> pDXGISwapChain;\n\tRenderTargets targets;\n\t// rendering parameters for current monitor\n\tHMONITOR hCurrentMonitor;\n\tstd::shared_ptr<RenderingParams> renderingParams;\n#endif\n\n\texplicit ScintillaWin(HWND hwnd);\n\n\tvoid Finalise() override;\n#if defined(USE_D2D)\n\tbool UpdateRenderingParams(bool force) noexcept;\n\tHRESULT Create3D() noexcept;\n\tvoid CreateRenderTarget();\n\tHRESULT SetBackBuffer(HWND hwnd, IDXGISwapChain1 *pSwapChain);\n\tHRESULT CreateSwapChain(HWND hwnd);\n\tvoid EnsureRenderTarget(HDC hdc);\n#endif\n\tvoid DropRenderTarget() noexcept;\n\t[[nodiscard]] HWND MainHWND() const noexcept;\n\n\tstatic sptr_t DirectFunction(\n\t\t    sptr_t ptr, UINT iMessage, uptr_t wParam, sptr_t lParam);\n\tstatic sptr_t DirectStatusFunction(\n\t\t    sptr_t ptr, UINT iMessage, uptr_t wParam, sptr_t lParam, int *pStatus);\n\tstatic LRESULT PASCAL SWndProc(\n\t\t    HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam);\n\n\tvoid CTPaint(HWND hWnd);\n\tLRESULT CTProcessMessage(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam);\n\tstatic LRESULT PASCAL CTWndProc(\n\t\t    HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam);\n\n\tenum : UINT_PTR { invalidTimerID, standardTimerID, idleTimerID, fineTimerStart };\n\n\tvoid DisplayCursor(Window::Cursor c) override;\n\tbool DragThreshold(Point ptStart, Point ptNow) override;\n\tvoid StartDrag() override;\n\tstatic KeyMod MouseModifiers(uptr_t wParam) noexcept;\n\n\tSci::Position TargetAsUTF8(char *text) const;\n\tSci::Position EncodedFromUTF8(const char *utf8, char *encoded) const;\n\n\tvoid SetRenderingParams(Surface *psurf) const;\n\n\tbool PaintDC(HDC hdc);\n\tsptr_t WndPaint();\n\n\t// DBCS\n\tvoid ImeStartComposition();\n\tvoid ImeEndComposition();\n\tLRESULT ImeOnReconvert(LPARAM lParam);\n\t[[nodiscard]] LRESULT ImeOnDocumentFeed(LPARAM lParam) const;\n\tsptr_t HandleCompositionWindowed(uptr_t wParam, sptr_t lParam);\n\tsptr_t HandleCompositionInline(uptr_t wParam, sptr_t lParam);\n\tstatic bool KoreanIME() noexcept;\n\tvoid SetCandidateWindowPos(const IMContext &imc);\n\tvoid SelectionToHangul();\n\tvoid EscapeHanja();\n\tvoid ToggleHanja();\n\tvoid AddWString(std::wstring_view wsv, CharacterSource charSource);\n\n\t[[nodiscard]] UINT CodePageOfDocument() const noexcept;\n\t[[nodiscard]] bool ValidCodePage(int codePage) const override;\n\t[[nodiscard]] std::string UTF8FromEncoded(std::string_view encoded) const override;\n\t[[nodiscard]] std::string EncodedFromUTF8(std::string_view utf8) const override;\n\n\tstd::string EncodeWString(std::wstring_view wsv);\n\tsptr_t DefWndProc(Message iMessage, uptr_t wParam, sptr_t lParam) override;\n\tvoid IdleWork() override;\n\tvoid QueueIdleWork(WorkItems items, Sci::Position upTo) override;\n\tbool SetIdle(bool on) override;\n\tUINT_PTR timers[static_cast<int>(TickReason::dwell)+1] {};\n\tbool FineTickerRunning(TickReason reason) override;\n\tvoid FineTickerStart(TickReason reason, int millis, int tolerance) override;\n\tvoid FineTickerCancel(TickReason reason) override;\n\tvoid SetMouseCapture(bool on) override;\n\tbool HaveMouseCapture() override;\n\tvoid SetTrackMouseLeaveEvent(bool on) noexcept;\n\tvoid HideCursorIfPreferred() noexcept;\n\tvoid UpdateBaseElements() override;\n\tbool PaintContains(PRectangle rc) override;\n\tvoid ScrollText(Sci::Line linesToMove) override;\n\tvoid NotifyCaretMove() override;\n\tvoid UpdateSystemCaret() override;\n\tvoid SetVerticalScrollPos() override;\n\tvoid SetHorizontalScrollPos() override;\n\tvoid HorizontalScrollToClamped(int xPos);\n\t[[nodiscard]] HorizontalScrollRange GetHorizontalScrollRange() const;\n\tbool ModifyScrollBars(Sci::Line nMax, Sci::Line nPage) override;\n\tvoid NotifyChange() override;\n\tvoid NotifyFocus(bool focus) override;\n\tvoid SetCtrlID(int identifier) override;\n\tint GetCtrlID() override;\n\tvoid NotifyParent(NotificationData scn) override;\n\tvoid NotifyDoubleClick(Point pt, KeyMod modifiers) override;\n\tstd::unique_ptr<CaseFolder> CaseFolderForEncoding() override;\n\tstd::string CaseMapString(const std::string &s, CaseMapping caseMapping) override;\n\tvoid Copy() override;\n\tbool CanPaste() override;\n\tvoid Paste() override;\n\tvoid CreateCallTipWindow(PRectangle rc) override;\n\tvoid AddToPopUp(const char *label, int cmd = 0, bool enabled = true) override;\n\tvoid ClaimSelection() override;\n\n\tvoid GetMouseParameters() noexcept;\n\tvoid CopyToGlobal(GlobalMemory &gmUnicode, const SelectionText &selectedText);\n\tvoid CopyToClipboard(const SelectionText &selectedText) override;\n\tvoid ScrollMessage(WPARAM wParam);\n\tvoid HorizontalScrollMessage(WPARAM wParam);\n\tvoid FullPaint();\n\tvoid FullPaintDC(HDC hdc);\n\tbool IsCompatibleDC(HDC hOtherDC) noexcept;\n\t[[nodiscard]] DWORD EffectFromState(DWORD grfKeyState) const noexcept;\n\n\t[[nodiscard]] bool IsVisible() const noexcept;\n\tint SetScrollInfo(int nBar, LPCSCROLLINFO lpsi, BOOL bRedraw) noexcept;\n\tbool GetScrollInfo(int nBar, LPSCROLLINFO lpsi) noexcept;\n\tbool ChangeScrollRange(int nBar, int nMin, int nMax, UINT nPage) noexcept;\n\tvoid ChangeScrollPos(int barType, Sci::Position pos);\n\tsptr_t GetTextLength();\n\tsptr_t GetText(uptr_t wParam, sptr_t lParam);\n\tWindow::Cursor ContextCursor(Point pt);\n\tsptr_t ShowContextMenu(unsigned int iMessage, uptr_t wParam, sptr_t lParam);\n\t[[nodiscard]] PRectangle GetClientRectangle() const override;\n\tvoid SizeWindow();\n\tsptr_t MouseMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam);\n\tsptr_t KeyMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam);\n\tsptr_t FocusMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam);\n\tsptr_t IMEMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam);\n\tsptr_t EditMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam);\n\tsptr_t IdleMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam);\n\tsptr_t SciMessage(Message iMessage, uptr_t wParam, sptr_t lParam);\n\npublic:\n\t// Deleted so ScintillaWin objects can not be copied.\n\tScintillaWin(const ScintillaWin &) = delete;\n\tScintillaWin(ScintillaWin &&) = delete;\n\tScintillaWin &operator=(const ScintillaWin &) = delete;\n\tScintillaWin &operator=(ScintillaWin &&) = delete;\n\t~ScintillaWin() override;\n\n\t// Public for benefit of Scintilla_DirectFunction\n\tsptr_t WndProc(Message iMessage, uptr_t wParam, sptr_t lParam) override;\n\n\t/// Implement IUnknown\n\tSTDMETHODIMP QueryInterface(REFIID riid, PVOID *ppv);\n\tSTDMETHODIMP_(ULONG)AddRef();\n\tSTDMETHODIMP_(ULONG)Release();\n\n\t/// Implement IDropTarget\n\tSTDMETHODIMP DragEnter(LPDATAOBJECT pIDataSource, DWORD grfKeyState,\n\t                       POINTL pt, PDWORD pdwEffect);\n\tSTDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, PDWORD pdwEffect);\n\tSTDMETHODIMP DragLeave();\n\tSTDMETHODIMP Drop(LPDATAOBJECT pIDataSource, DWORD grfKeyState,\n\t                  POINTL pt, PDWORD pdwEffect);\n\n\tvoid AuDragDrop(int action, Sci_DragDropData* r); //Au\n\n\t/// Implement important part of IDataObject\n\tSTDMETHODIMP GetData(FORMATETC *pFEIn, STGMEDIUM *pSTM);\n\n\tstatic void Prepare() noexcept;\n\tstatic bool Register(HINSTANCE hInstance_) noexcept;\n\tstatic bool Unregister() noexcept;\n\n\t[[nodiscard]] bool DragIsRectangularOK(CLIPFORMAT fmt) const noexcept {\n\t\treturn drag.rectangular && (fmt == cfColumnSelect);\n\t}\n\nprivate:\n\t// For use in creating a system caret\n\t[[nodiscard]] bool HasCaretSizeChanged() const noexcept;\n\tBOOL CreateSystemCaret();\n\tBOOL DestroySystemCaret() noexcept;\n\tHBITMAP sysCaretBitmap;\n\tint sysCaretWidth;\n\tint sysCaretHeight;\n\tbool styleIdleInQueue;\n\n\t//Au:\npublic:\n\tvoid Sci_SetFoldLevels(int line, int lastLine, int len, int* a) {\n\t\tif(pdoc->Sci_SetFoldLevels(line, lastLine, len, a))\n\t\t\tRedrawSelMargin();\n\t}\n\n\tstruct Sci_VisibleRange {\n\t\tint dlineFrom, dlineTo, vlineFrom, vlineTo, posFrom, posTo;\n\t};\n\tvoid Sci_GetVisibleRange(Sci_VisibleRange& r) {\n\t\tr.vlineFrom = (int)topLine;\n\t\tr.dlineFrom = (int)pcs->DocFromDisplay(topLine);\n\t\tr.posFrom = (int)posTopLine;\n\n\t\tauto vlines = (int)pcs->LinesDisplayed();\n\t\tauto vlineTo = topLine + LinesOnScreen() + 1; if(vlineTo > vlines) vlineTo = vlines;\n\t\tr.vlineTo = (int)vlineTo;\n\t\tif(vlineTo < vlines) {\n\t\t\tauto dlineTo = pcs->DocFromDisplay(vlineTo) + 1;\n\t\t\tr.dlineTo = (int)dlineTo;\n\t\t\tr.posTo = (int)pdoc->LineStart(dlineTo);\n\t\t} else {\n\t\t\tr.dlineTo = (int)pdoc->LinesTotal();\n\t\t\tr.posTo = (int)pdoc->Length();\n\t\t}\n\t}\n};\n\nHINSTANCE ScintillaWin::hInstance {};\nATOM ScintillaWin::scintillaClassAtom = 0;\nATOM ScintillaWin::callClassAtom = 0;\n\nScintillaWin::ScintillaWin(HWND hwnd) {\n\n\tlastKeyDownConsumed = false;\n\tlastHighSurrogateChar = 0;\n\n\tcapturedMouse = false;\n\ttrackedMouseLeave = false;\n\ttypingWithoutCursor = false;\n\tcursorIsHidden = false;\n\tSetCoalescableTimerFn = nullptr;\n\n\tlinesPerScroll = 0;\n\tcharsPerScroll = 0;\n\n\tdpi = DpiForWindow(hwnd);\n\n\thRgnUpdate = {};\n\n\thasOKText = false;\n\n\t// There does not seem to be a real standard for indicating that the clipboard\n\t// contains a rectangular selection, so copy Developer Studio and Borland Delphi.\n\tcfColumnSelect = RegisterClipboardType(L\"MSDEVColumnSelect\");\n\tcfBorlandIDEBlockType = RegisterClipboardType(L\"Borland IDE Block Type\");\n\n\t// Likewise for line-copy or line-cut (copies or cuts a full line when no text is selected)\n\tcfLineSelect = RegisterClipboardType(L\"MSDEVLineSelect\");\n\tcfVSLineTag = RegisterClipboardType(L\"VisualStudioEditorOperationsLineCutCopyClipboardTag\");\n\thrOle = E_FAIL;\n\n\twMain = hwnd;\n\n\tdob.sci = this;\n\tds.sci = this;\n\tdt.sci = this;\n\n\tsysCaretBitmap = {};\n\tsysCaretWidth = 0;\n\tsysCaretHeight = 0;\n\n\tstyleIdleInQueue = false;\n\n#if defined(USE_D2D)\n\thCurrentMonitor = {};\n#endif\n\n\tcaret.period = ::GetCaretBlinkTime();\n\tif (caret.period < 0)\n\t\tcaret.period = 0;\n\n\t// Initialize COM.  If the app has already done this it will have\n\t// no effect.  If the app hasn't, we really shouldn't ask them to call\n\t// it just so this internal feature works.\n\thrOle = ::OleInitialize(nullptr);\n\n\t// Find SetCoalescableTimer which is only available from Windows 8+\n\tHMODULE user32 = ::GetModuleHandleW(L\"user32.dll\");\n\tSetCoalescableTimerFn = DLLFunction<SetCoalescableTimerSig>(user32, \"SetCoalescableTimer\");\n\n\tvs.indicators[IndicatorUnknown] = Indicator(IndicatorStyle::Hidden, colourIME);\n\tvs.indicators[IndicatorInput] = Indicator(IndicatorStyle::Dots, colourIME);\n\tvs.indicators[IndicatorConverted] = Indicator(IndicatorStyle::CompositionThick, colourIME);\n\tvs.indicators[IndicatorTarget] = Indicator(IndicatorStyle::StraightBox, colourIME);\n}\n\nScintillaWin::~ScintillaWin() {\n\tif (sysCaretBitmap) {\n\t\t::DeleteObject(sysCaretBitmap);\n\t\tsysCaretBitmap = {};\n\t}\n}\n\nvoid ScintillaWin::Finalise() {\n\tScintillaBase::Finalise();\n\tfor (TickReason tr = TickReason::caret; tr <= TickReason::dwell;\n\t\ttr = static_cast<TickReason>(static_cast<int>(tr) + 1)) {\n\t\tFineTickerCancel(tr);\n\t}\n\tSetIdle(false);\n\tDropRenderTarget();\n\t::RevokeDragDrop(MainHWND());\n\tif (SUCCEEDED(hrOle)) {\n\t\t::OleUninitialize();\n\t}\n}\n\n#if defined(USE_D2D)\n\nnamespace {\n\nHRESULT CreateHwndRenderTarget(const D2D1_RENDER_TARGET_PROPERTIES *renderTargetProperties,\n\tconst D2D1_HWND_RENDER_TARGET_PROPERTIES *hwndRenderTargetProperties, HwndRenderTarget &hwndRT) noexcept {\n\treturn pD2DFactory->CreateHwndRenderTarget(renderTargetProperties, hwndRenderTargetProperties, hwndRT.ReleaseAndGetAddressOf());\n}\n\n}\n\nbool ScintillaWin::UpdateRenderingParams(bool force) noexcept {\n\tif (!renderingParams) {\n\t\ttry {\n\t\t\trenderingParams = std::make_shared<RenderingParams>();\n\t\t} catch (const std::bad_alloc &) {\n\t\t\treturn false;\n\t\t}\n\t}\n\tconst HWND hRootWnd = ::GetAncestor(MainHWND(), GA_ROOT);\n\tconst HMONITOR monitor = Internal::MonitorFromWindowHandleScaling(hRootWnd);\n\tif (!force && monitor == hCurrentMonitor && renderingParams->defaultRenderingParams) {\n\t\treturn false;\n\t}\n\n\tComPtr<IDWriteRenderingParams> upMrp;\n\tHRESULT hr = pIDWriteFactory->CreateMonitorRenderingParams(monitor, upMrp.GetAddressOf());\n\tif (FAILED(hr)) {\n\t\treturn false;\n\t}\n\n\t// Cast to IDWriteRenderingParams1 so can call GetGrayscaleEnhancedContrast\n\tWriteRenderingParams monitorRenderingParams{};\n\thr = upMrp.As(&monitorRenderingParams);\n\n\tWriteRenderingParams customClearTypeRenderingParams;\n\tUINT clearTypeContrast = 0;\n\tif (SUCCEEDED(hr) && monitorRenderingParams &&\n\t\t::SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST, 0, &clearTypeContrast, 0) != 0) {\n\t\tif (clearTypeContrast >= 1000 && clearTypeContrast <= 2200) {\n\t\t\tconst FLOAT gamma = static_cast<FLOAT>(clearTypeContrast) / 1000.0f;\n\t\t\tpIDWriteFactory->CreateCustomRenderingParams(gamma,\n\t\t\t\tmonitorRenderingParams->GetEnhancedContrast(),\n\t\t\t\tmonitorRenderingParams->GetGrayscaleEnhancedContrast(),\n\t\t\t\tmonitorRenderingParams->GetClearTypeLevel(),\n\t\t\t\tmonitorRenderingParams->GetPixelGeometry(),\n\t\t\t\tmonitorRenderingParams->GetRenderingMode(),\n\t\t\t\tcustomClearTypeRenderingParams.GetAddressOf());\n\t\t}\n\t}\n\n\thCurrentMonitor = monitor;\n\tdeviceScaleFactor = Internal::GetDeviceScaleFactorWhenGdiScalingActive(hRootWnd);\n\trenderingParams->defaultRenderingParams = std::move(monitorRenderingParams);\n\trenderingParams->customRenderingParams = std::move(customClearTypeRenderingParams);\n\treturn true;\n}\n\nHRESULT ScintillaWin::Create3D() noexcept {\n\tif (device.pDirect2DDevice) {\n\t\treturn S_OK;\n\t}\n\ttargets.Release();\n\tpDXGISwapChain = nullptr;\n\tdevice.Release();\n\tconst HRESULT hr = device.CreateDevice();\n\tif (FAILED(hr)) {\n\t\tdevice.Release();\n\t}\n\treturn hr;\n}\n\nvoid ScintillaWin::CreateRenderTarget() {\n\tHWND hw = MainHWND();\n\n\t// Create a Direct2D render target.\n\n\tif (technology == Technology::DirectWriteDC) {\n\t\tconst D2D1_RENDER_TARGET_PROPERTIES drtp = D2D1::RenderTargetProperties(\n\t\t\tD2D1_RENDER_TARGET_TYPE_DEFAULT,\n\t\t\t{ DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE },\n\t\t\tdpiDefault, dpiDefault);\n\n\t\tconst HRESULT hr = CreateDCRenderTarget(&drtp, targets.pDCRT);\n\t\tif (FAILED(hr)) {\n\t\t\tPlatform::DebugPrintf(\"Failed CreateDCRenderTarget 0x%lx\\n\", hr);\n\t\t\ttargets.Release();\n\t\t}\n\n\t} else if (technology == Technology::DirectWrite1) {\n\t\tHRESULT hr = Create3D();\t// Need pDirect2DDevice\n\t\tif (SUCCEEDED(hr)) {\n\t\t\thr = device.pDirect2DDevice->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE,\n\t\t\t\ttargets.pDeviceContext.ReleaseAndGetAddressOf());\n\t\t\tif (FAILED(hr)) {\n\t\t\t\tPlatform::DebugPrintf(\"Failed CreateDeviceContext 0x%lx\\n\", hr);\n\t\t\t} else {\n\t\t\t\thr = CreateSwapChain(hw);\n\t\t\t\tif (FAILED(hr)) {\n\t\t\t\t\tPlatform::DebugPrintf(\"Failed CreateSwapChain 0x%lx\\n\", hr);\n\t\t\t\t\ttargets.Release();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t} else { // DirectWrite or DirectWriteRetain\n\n\t\tconst RECT rc = GetClientRect(hw);\n\n\t\tconst int integralDeviceScaleFactor = GetFirstIntegralMultipleDeviceScaleFactor();\n\t\tconst FLOAT dpiTarget = dpiDefault * static_cast<float>(integralDeviceScaleFactor);\n\n\t\tconst D2D1_RENDER_TARGET_PROPERTIES drtp = D2D1::RenderTargetProperties(\n\t\t\tD2D1_RENDER_TARGET_TYPE_DEFAULT,\n\t\t\t{ DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_UNKNOWN },\n\t\t\tdpiTarget, dpiTarget);\n\n\t\tconst D2D1_PRESENT_OPTIONS presentOptions = (technology == Technology::DirectWriteRetain) ?\n\t\t\tD2D1_PRESENT_OPTIONS_RETAIN_CONTENTS : D2D1_PRESENT_OPTIONS_NONE;\n\n\t\tconst D2D1_HWND_RENDER_TARGET_PROPERTIES dhrtp = D2D1::HwndRenderTargetProperties(\n\t\t\thw,\n\t\t\t::GetSizeUFromRect(rc, integralDeviceScaleFactor),\n\t\t\tpresentOptions);\n\n\t\tconst HRESULT hr = CreateHwndRenderTarget(&drtp, &dhrtp, targets.pHwndRT);\n\t\tif (FAILED(hr)) {\n\t\t\tPlatform::DebugPrintf(\"Failed CreateHwndRenderTarget 0x%lx\\n\", hr);\n\t\t\ttargets.Release();\n\t\t}\n\n\t}\n}\n\nHRESULT ScintillaWin::SetBackBuffer(HWND hwnd, IDXGISwapChain1 *pSwapChain) {\n\tassert(targets.pDeviceContext);\n\t// Back buffer as an IDXGISurface\n\tComPtr<IDXGISurface> dxgiBackBuffer;\n\tHRESULT hr = pSwapChain->GetBuffer(0, IID_PPV_ARGS(dxgiBackBuffer.GetAddressOf()));\n\tif (FAILED(hr))\n\t\treturn hr;\n\n\tconst FLOAT dpiX = static_cast<FLOAT>(DpiForWindow(hwnd));\n\tconst FLOAT dpiY = dpiX;\n\n\t// Direct2D bitmap linked to Direct3D texture through DXGI back buffer\n\tconst D2D1_BITMAP_PROPERTIES1 bitmapProperties =\n\t\tD2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW,\n\t\t\tD2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE), dpiX, dpiY);\n\tComPtr<ID2D1Bitmap1> pDirect2DBackBuffer;\n\thr = targets.pDeviceContext->CreateBitmapFromDxgiSurface(dxgiBackBuffer.Get(), &bitmapProperties, pDirect2DBackBuffer.GetAddressOf());\n\tif (FAILED(hr))\n\t\treturn hr;\n\n\t// Bitmap is render target\n\ttargets.pDeviceContext->SetTarget(pDirect2DBackBuffer.Get());\n\n\treturn S_OK;\n}\n\nHRESULT ScintillaWin::CreateSwapChain(HWND hwnd) {\n\t// Sets pDXGISwapChain but only when each call succeeds\n\t// Needs pDXGIDevice, pDirect3DDevice\n\tpDXGISwapChain = nullptr;\n\tassert(device.pDXGIDevice);\n\n\t// At each stage, place object in a unique_ptr to ensure release occurs\n\n\tComPtr<IDXGIAdapter> dxgiAdapter;\n\tHRESULT hr = device.pDXGIDevice->GetAdapter(dxgiAdapter.GetAddressOf());\n\tif (FAILED(hr))\n\t\treturn hr;\n\n\tComPtr<IDXGIFactory2> dxgiFactory;\n\thr = dxgiAdapter->GetParent(IID_PPV_ARGS(dxgiFactory.GetAddressOf()));\n\tif (FAILED(hr))\n\t\treturn hr;\n\n\tDXGI_SWAP_CHAIN_DESC1 swapChainDesc{};\n\tswapChainDesc.Width = 0;\n\tswapChainDesc.Height = 0;\n\tswapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;\n\tswapChainDesc.Stereo = false;\n\tswapChainDesc.SampleDesc.Count = 1;\n\tswapChainDesc.SampleDesc.Quality = 0;\n\tswapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;\n\tswapChainDesc.BufferCount = 2;\n\tswapChainDesc.Scaling = DXGI_SCALING_STRETCH;\n\tswapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;\n\tswapChainDesc.Flags = 0;\n\n\t// DXGI swap chain for window\n\tComPtr<IDXGISwapChain1> pSwapChain;\n\thr = dxgiFactory->CreateSwapChainForHwnd(device.pDirect3DDevice.Get(), hwnd, &swapChainDesc,\n\t\tnullptr, nullptr, pSwapChain.GetAddressOf());\n\tif (FAILED(hr))\n\t\treturn hr;\n\n\thr = SetBackBuffer(hwnd, pSwapChain.Get());\n\tif (FAILED(hr))\n\t\treturn hr;\n\n\t// All successful so export swap chain for later presentation\n\tpDXGISwapChain = std::move(pSwapChain);\n\treturn S_OK;\n}\n\nvoid ScintillaWin::EnsureRenderTarget(HDC hdc) {\n\tif (!targets.valid) {\n\t\tDropRenderTarget();\n\t\ttargets.valid = true;\n\t}\n\tif (!targets.RenderTarget()) {\n\t\tCreateRenderTarget();\n\t\t// Pixmaps were created to be compatible with previous render target so\n\t\t// need to be recreated.\n\t\tDropGraphics();\n\t}\n\n\tif ((technology == Technology::DirectWriteDC) && targets.pDCRT) {\t// DC RenderTarget needs binding\n\t\tconst RECT rcWindow = GetClientRect(MainHWND());\n\t\tconst HRESULT hr = targets.pDCRT->BindDC(hdc, &rcWindow);\n\t\tif (FAILED(hr)) {\n\t\t\tPlatform::DebugPrintf(\"BindDC failed 0x%lx\\n\", hr);\n\t\t\tDropRenderTarget();\n\t\t}\n\t}\n}\n#endif\n\nvoid ScintillaWin::DropRenderTarget() noexcept {\n#if defined(USE_D2D)\n\ttargets.Release();\n#endif\n}\n\nHWND ScintillaWin::MainHWND() const noexcept {\n\treturn HwndFromWindow(wMain);\n}\n\nvoid ScintillaWin::DisplayCursor(Window::Cursor c) {\n\tif (cursorMode != CursorShape::Normal) {\n\t\tc = static_cast<Window::Cursor>(cursorMode);\n\t}\n\tif (c == Window::Cursor::reverseArrow) {\n\t\t::SetCursor(reverseArrowCursor.Load(static_cast<UINT>(static_cast<float>(dpi) * deviceScaleFactor)));\n\t} else {\n\t\twMain.SetCursor(c);\n\t}\n}\n\nbool ScintillaWin::DragThreshold(Point ptStart, Point ptNow) {\n\tconst Point ptDifference = ptStart - ptNow;\n\tconst XYPOSITION xMove = std::trunc(std::abs(ptDifference.x));\n\tconst XYPOSITION yMove = std::trunc(std::abs(ptDifference.y));\n\treturn (xMove > SystemMetricsForDpi(SM_CXDRAG, dpi)) ||\n\t\t(yMove > SystemMetricsForDpi(SM_CYDRAG, dpi));\n}\n\nvoid ScintillaWin::StartDrag() {\n\tinDragDrop = DragDrop::dragging;\n\tDWORD dwEffect = 0;\n\tdropWentOutside = true;\n\tIDataObject *pDataObject = &dob;\n\tIDropSource *pDropSource = &ds;\n\t//Platform::DebugPrintf(\"About to DoDragDrop %x %x\\n\", pDataObject, pDropSource);\n\tconst HRESULT hr = ::DoDragDrop(\n\t                 pDataObject,\n\t                 pDropSource,\n\t                 DROPEFFECT_COPY | DROPEFFECT_MOVE, &dwEffect);\n\t//Platform::DebugPrintf(\"DoDragDrop = %x\\n\", hr);\n\tif (SUCCEEDED(hr)) {\n\t\tif ((hr == DRAGDROP_S_DROP) && (dwEffect == DROPEFFECT_MOVE) && dropWentOutside) {\n\t\t\t// Remove dragged out text\n\t\t\tClearSelection();\n\t\t}\n\t}\n\tinDragDrop = DragDrop::none;\n\tSetDragPosition(SelectionPosition(Sci::invalidPosition));\n}\n\nKeyMod ScintillaWin::MouseModifiers(uptr_t wParam) noexcept {\n\treturn ModifierFlags(\n\t\t(wParam & MK_SHIFT) != 0,\n\t\t(wParam & MK_CONTROL) != 0,\n\t\tKeyboardIsKeyDown(VK_MENU));\n}\n\n}\n\nnamespace {\n\nint InputCodePage() noexcept {\n\tHKL inputLocale = ::GetKeyboardLayout(0);\n\tconst LANGID inputLang = LOWORD(inputLocale);\n\tchar sCodePage[10];\n\tconst int res = ::GetLocaleInfoA(MAKELCID(inputLang, SORT_DEFAULT),\n\t  LOCALE_IDEFAULTANSICODEPAGE, sCodePage, sizeof(sCodePage));\n\tif (!res)\n\t\treturn 0;\n\treturn atoi(sCodePage);\n}\n\n/** Map the key codes to their equivalent Keys:: form. */\nKeys KeyTranslate(uptr_t keyIn) noexcept {\n\tswitch (keyIn) {\n\tcase VK_DOWN:\t\treturn Keys::Down;\n\t\tcase VK_UP:\t\treturn Keys::Up;\n\t\tcase VK_LEFT:\t\treturn Keys::Left;\n\t\tcase VK_RIGHT:\t\treturn Keys::Right;\n\t\tcase VK_HOME:\t\treturn Keys::Home;\n\t\tcase VK_END:\t\treturn Keys::End;\n\t\tcase VK_PRIOR:\t\treturn Keys::Prior;\n\t\tcase VK_NEXT:\t\treturn Keys::Next;\n\t\tcase VK_DELETE:\treturn Keys::Delete;\n\t\tcase VK_INSERT:\t\treturn Keys::Insert;\n\t\tcase VK_ESCAPE:\treturn Keys::Escape;\n\t\tcase VK_BACK:\t\treturn Keys::Back;\n\t\tcase VK_TAB:\t\treturn Keys::Tab;\n\t\tcase VK_RETURN:\treturn Keys::Return;\n\t\tcase VK_ADD:\t\treturn Keys::Add;\n\t\tcase VK_SUBTRACT:\treturn Keys::Subtract;\n\t\tcase VK_DIVIDE:\t\treturn Keys::Divide;\n\t\tcase VK_LWIN:\t\treturn Keys::Win;\n\t\tcase VK_RWIN:\t\treturn Keys::RWin;\n\t\tcase VK_APPS:\t\treturn Keys::Menu;\n\t\tcase VK_OEM_2:\t\treturn static_cast<Keys>('/');\n\t\tcase VK_OEM_3:\t\treturn static_cast<Keys>('`');\n\t\tcase VK_OEM_4:\t\treturn static_cast<Keys>('[');\n\t\tcase VK_OEM_5:\t\treturn static_cast<Keys>('\\\\');\n\t\tcase VK_OEM_6:\t\treturn static_cast<Keys>(']');\n\t\tdefault:\t\t\treturn static_cast<Keys>(keyIn);\n\t}\n}\n\nbool BoundsContains(PRectangle rcBounds, HRGN hRgnBounds, PRectangle rcCheck) noexcept {\n\tbool contains = true;\n\tif (!rcCheck.Empty()) {\n\t\tif (!rcBounds.Contains(rcCheck)) {\n\t\t\tcontains = false;\n\t\t} else if (hRgnBounds) {\n\t\t\t// In bounding rectangle so check more accurately using region\n\t\t\tconst RECT rcw = RectFromPRectangle(rcCheck);\n\t\t\tHRGN hRgnCheck = ::CreateRectRgnIndirect(&rcw);\n\t\t\tif (hRgnCheck) {\n\t\t\t\tHRGN hRgnDifference = ::CreateRectRgn(0, 0, 0, 0);\n\t\t\t\tif (hRgnDifference) {\n\t\t\t\t\tconst int combination = ::CombineRgn(hRgnDifference, hRgnCheck, hRgnBounds, RGN_DIFF);\n\t\t\t\t\tif (combination != NULLREGION) {\n\t\t\t\t\t\tcontains = false;\n\t\t\t\t\t}\n\t\t\t\t\t::DeleteRgn(hRgnDifference);\n\t\t\t\t}\n\t\t\t\t::DeleteRgn(hRgnCheck);\n\t\t\t}\n\t\t}\n\t}\n\treturn contains;\n}\n\n// Simplify calling WideCharToMultiByte and MultiByteToWideChar by providing default parameters and using string view.\n\nint MultiByteFromWideChar(UINT codePage, std::wstring_view wsv, LPSTR lpMultiByteStr, ptrdiff_t cbMultiByte) noexcept {\n\treturn ::WideCharToMultiByte(codePage, 0, wsv.data(), static_cast<int>(wsv.length()), lpMultiByteStr, static_cast<int>(cbMultiByte), nullptr, nullptr);\n}\n\nint MultiByteLenFromWideChar(UINT codePage, std::wstring_view wsv) noexcept {\n\treturn MultiByteFromWideChar(codePage, wsv, nullptr, 0);\n}\n\nint WideCharFromMultiByte(UINT codePage, std::string_view sv, LPWSTR lpWideCharStr, ptrdiff_t cchWideChar) noexcept {\n\treturn ::MultiByteToWideChar(codePage, 0, sv.data(), static_cast<int>(sv.length()), lpWideCharStr, static_cast<int>(cchWideChar));\n}\n\nint WideCharLenFromMultiByte(UINT codePage, std::string_view sv) noexcept {\n\treturn WideCharFromMultiByte(codePage, sv, nullptr, 0);\n}\n\nstd::string StringEncode(std::wstring_view wsv, int codePage) {\n\tconst int cchMulti = wsv.empty() ? 0 : MultiByteLenFromWideChar(codePage, wsv);\n\tstd::string sMulti(cchMulti, 0);\n\tif (cchMulti) {\n\t\tMultiByteFromWideChar(codePage, wsv, sMulti.data(), cchMulti);\n\t}\n\treturn sMulti;\n}\n\nstd::wstring StringDecode(std::string_view sv, int codePage) {\n\tconst int cchWide = sv.empty() ? 0 : WideCharLenFromMultiByte(codePage, sv);\n\tstd::wstring sWide(cchWide, 0);\n\tif (cchWide) {\n\t\tWideCharFromMultiByte(codePage, sv, sWide.data(), cchWide);\n\t}\n\treturn sWide;\n}\n\nstd::wstring StringMapCase(std::wstring_view wsv, DWORD mapFlags) {\n\tconst int charsConverted = ::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags,\n\t\twsv.data(), static_cast<int>(wsv.length()), nullptr, 0);\n\tstd::wstring wsConverted(charsConverted, 0);\n\tif (charsConverted) {\n\t\t::LCMapStringW(LOCALE_SYSTEM_DEFAULT, mapFlags,\n\t\t\twsv.data(), static_cast<int>(wsv.length()), wsConverted.data(), charsConverted);\n\t}\n\treturn wsConverted;\n}\n\n}\n\n// Returns the target converted to UTF8.\n// Return the length in bytes.\nSci::Position ScintillaWin::TargetAsUTF8(char *text) const {\n\tconst Sci::Position targetLength = targetRange.Length();\n\tif (IsUnicodeMode()) {\n\t\tif (text) {\n\t\t\tpdoc->GetCharRange(text, targetRange.start.Position(), targetLength);\n\t\t}\n\t} else {\n\t\t// Need to convert\n\t\tconst std::string s = RangeText(targetRange.start.Position(), targetRange.end.Position());\n\t\tconst std::wstring characters = StringDecode(s, CodePageOfDocument());\n\t\tconst int utf8Len = MultiByteLenFromWideChar(CpUtf8, characters);\n\t\tif (text) {\n\t\t\tMultiByteFromWideChar(CpUtf8, characters, text, utf8Len);\n\t\t\ttext[utf8Len] = '\\0';\n\t\t}\n\t\treturn utf8Len;\n\t}\n\treturn targetLength;\n}\n\n// Translates a nul terminated UTF8 string into the document encoding.\n// Return the length of the result in bytes.\nSci::Position ScintillaWin::EncodedFromUTF8(const char *utf8, char *encoded) const {\n\tconst Sci::Position inputLength = (lengthForEncode >= 0) ? lengthForEncode : strlen(utf8);\n\tif (IsUnicodeMode()) {\n\t\tif (encoded) {\n\t\t\tmemcpy(encoded, utf8, inputLength);\n\t\t}\n\t\treturn inputLength;\n\t}\n\t// Need to convert\n\tconst std::string_view utf8Input(utf8, inputLength);\n\tconst int charsLen = WideCharLenFromMultiByte(CpUtf8, utf8Input);\n\tstd::wstring characters(charsLen, L'\\0');\n\tWideCharFromMultiByte(CpUtf8, utf8Input, characters.data(), charsLen);\n\n\tconst int encodedLen = MultiByteLenFromWideChar(CodePageOfDocument(), characters);\n\tif (encoded) {\n\t\tMultiByteFromWideChar(CodePageOfDocument(), characters, encoded, encodedLen);\n\t\tencoded[encodedLen] = '\\0';\n\t}\n\treturn encodedLen;\n}\n\nvoid ScintillaWin::SetRenderingParams([[maybe_unused]] Surface *psurf) const {\n#if defined(USE_D2D)\n\tif (psurf) {\n\t\tISetRenderingParams *setDrawingParams = dynamic_cast<ISetRenderingParams *>(psurf);\n\t\tif (setDrawingParams) {\n\t\t\tsetDrawingParams->SetRenderingParams(renderingParams);\n\t\t}\n\t}\n#endif\n}\n\nbool ScintillaWin::PaintDC(HDC hdc) {\n\tif (technology == Technology::Default) {\n\t\tAutoSurface surfaceWindow(hdc, this);\n\t\tif (surfaceWindow) {\n\t\t\tPaint(surfaceWindow, rcPaint);\n\t\t\tsurfaceWindow->Release();\n\t\t}\n\t} else {\n#if defined(USE_D2D)\n\t\t// RefreshStyleData may set scroll bars and resize the window.\n\t\t// Avoid issues resizing inside Paint when calling IDXGISwapChain1->ResizeBuffers\n\t\t// with committed resources by refreshing the style data first.\n\t\tRefreshStyleData();\n\n\t\tEnsureRenderTarget(hdc);\n\t\tif (ID2D1RenderTarget *pRenderTarget = targets.RenderTarget()) {\n\t\t\tAutoSurface surfaceWindow(pRenderTarget, this);\n\t\t\tif (surfaceWindow) {\n\t\t\t\tSetRenderingParams(surfaceWindow);\n\t\t\t\tpRenderTarget->BeginDraw();\n\t\t\t\tPaint(surfaceWindow, rcPaint);\n\t\t\t\tsurfaceWindow->Release();\n\t\t\t\tconst HRESULT hr = pRenderTarget->EndDraw();\n\t\t\t\tif (hr == static_cast<HRESULT>(D2DERR_RECREATE_TARGET)) {\n\t\t\t\t\tDropRenderTarget();\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tif ((technology == Technology::DirectWrite1) && pDXGISwapChain) {\n\t\t\t\t\tconst DXGI_PRESENT_PARAMETERS parameters{};\n\t\t\t\t\tconst HRESULT hrPresent = pDXGISwapChain->Present1(1, 0, &parameters);\n\t\t\t\t\tif (FAILED(hrPresent)) {\n\t\t\t\t\t\tDropRenderTarget();\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n#endif\n\t}\n\n\treturn true;\n}\n\nsptr_t ScintillaWin::WndPaint() {\n\t//ElapsedPeriod ep;\n\n\t// Redirect assertions to debug output and save current state\n\tconst bool assertsPopup = Platform::ShowAssertionPopUps(false);\n\tpaintState = PaintState::painting;\n\n\t// Removed since this interferes with reporting other assertions as it occurs repeatedly\n\t//PLATFORM_ASSERT(hRgnUpdate == NULL);\n\thRgnUpdate = ::CreateRectRgn(0, 0, 0, 0);\n\t::GetUpdateRgn(MainHWND(), hRgnUpdate, FALSE);\n\t{\n\t\tPainter painter(MainHWND());\n\t\trcPaint = PRectangleFromRECT(painter.ps.rcPaint);\n\t\tconst PRectangle rcClient = GetClientRectangle();\n\t\tpaintingAllText = BoundsContains(rcPaint, hRgnUpdate, rcClient);\n\t\tif (!PaintDC(painter.ps.hdc)) {\n\t\t\tpaintState = PaintState::abandoned;\n\t\t}\n\t}\n\tif (hRgnUpdate) {\n\t\t::DeleteRgn(hRgnUpdate);\n\t\thRgnUpdate = {};\n\t}\n\tif (paintState == PaintState::abandoned) {\n\t\t// Painting area was insufficient to cover new styling or brace highlight positions\n\t\tFullPaint();\n\t\t::ValidateRect(MainHWND(), nullptr);\n\t}\n\tpaintState = PaintState::notPainting;\n\n\t// Restore debug output state\n\tPlatform::ShowAssertionPopUps(assertsPopup);\n\n\t//Platform::DebugPrintf(\"Paint took %g\\n\", ep.Duration());\n\treturn 0;\n}\n\nsptr_t ScintillaWin::HandleCompositionWindowed(uptr_t wParam, sptr_t lParam) {\n\tif (lParam & GCS_RESULTSTR) {\n\t\tif (IMContext imc{ MainHWND() }) {\n\t\t\tAddWString(imc.GetCompositionString(GCS_RESULTSTR), CharacterSource::ImeResult);\n\n\t\t\t// Set new position after converted\n\t\t\timc.SetCompositionWindow(PointMainCaret());\n\t\t}\n\t\treturn 0;\n\t}\n\treturn ::DefWindowProc(MainHWND(), WM_IME_COMPOSITION, wParam, lParam);\n}\n\nbool ScintillaWin::KoreanIME() noexcept {\n\tconst int codePage = InputCodePage();\n\treturn codePage == cp949 || codePage == cp1361;\n}\n\nvoid ScintillaWin::SetCandidateWindowPos(const IMContext &imc) {\n\timc.SetCandidateWindowPos(PointMainCaret(), GetTextRectangle(), vs.lineHeight);\n}\n\nvoid ScintillaWin::SelectionToHangul() {\n\t// Convert every hanja to hangul within the main range.\n\tconst Sci::Position selStart = sel.RangeMain().Start().Position();\n\tconst Sci::Position documentStrLen = sel.RangeMain().Length();\n\tconst Sci::Position selEnd = selStart + documentStrLen;\n\tconst Sci::Position utf16Len = pdoc->CountUTF16(selStart, selEnd);\n\n\tif (utf16Len > 0) {\n\t\tconst std::string documentStr = RangeText(selStart, selStart + documentStrLen);\n\n\t\tstd::wstring uniStr = StringDecode(documentStr, CodePageOfDocument());\n\t\tconst bool converted = HanjaDict::GetHangulOfHanja(uniStr);\n\n\t\tif (converted) {\n\t\t\tconst std::string hangul = StringEncode(uniStr, CodePageOfDocument());\n\t\t\tUndoGroup ug(pdoc);\n\t\t\tClearSelection();\n\t\t\tInsertPaste(hangul.data(), hangul.size());\n\t\t}\n\t}\n}\n\nvoid ScintillaWin::EscapeHanja() {\n\t// The candidate box pops up to user to select a hanja.\n\t// It comes into WM_IME_COMPOSITION with GCS_RESULTSTR.\n\t// The existing hangul or hanja is replaced with it.\n\n\tconst Sci::Position currentPos = CurrentPosition();\n\tconst int oneCharLen = pdoc->LenChar(currentPos);\n\n\tif (oneCharLen < 2) {\n\t\treturn; // No need to handle SBCS.\n\t}\n\n\tconst std::string oneChar = RangeText(currentPos, currentPos + oneCharLen);\n\n\tstd::wstring uniChar = StringDecode(oneChar, CodePageOfDocument());\n\t// ImmEscapeW() may overwrite uniChar[] with a null terminated string.\n\t// So enlarge it enough to Maximum 4 as in UTF-8.\n\tuniChar.resize(UTF8MaxBytes);\n\n\tif (IMContext imc{ MainHWND() }) {\n\t\t// Set the candidate box position since IME may show it.\n\t\tSetCandidateWindowPos(imc);\n\t\t// IME_ESC_HANJA_MODE appears to receive the first character only.\n\t\tif (imc.Escape(GetKeyboardLayout(0), IME_ESC_HANJA_MODE, uniChar.data())) {\n\t\t\tSetSelection(currentPos, currentPos + oneCharLen);\n\t\t}\n\t}\n}\n\nvoid ScintillaWin::ToggleHanja() {\n\t// If selection, convert every hanja to hangul within the main range.\n\t// If no selection, commit to IME.\n\tif (sel.Count() > 1) {\n\t\treturn; // Do not allow multi carets.\n\t}\n\n\tif (sel.Empty()) {\n\t\tEscapeHanja();\n\t} else {\n\t\tSelectionToHangul();\n\t}\n}\n\nnamespace {\n\nstd::vector<int> MapImeIndicators(std::vector<BYTE> inputStyle) {\n\tstd::vector<int> imeIndicator(inputStyle.size(), IndicatorUnknown);\n\tfor (size_t i = 0; i < inputStyle.size(); i++) {\n\t\tswitch (static_cast<int>(inputStyle.at(i))) {\n\t\tcase ATTR_INPUT:\n\t\t\timeIndicator[i] = IndicatorInput;\n\t\t\tbreak;\n\t\tcase ATTR_TARGET_NOTCONVERTED:\n\t\tcase ATTR_TARGET_CONVERTED:\n\t\t\timeIndicator[i] = IndicatorTarget;\n\t\t\tbreak;\n\t\tcase ATTR_CONVERTED:\n\t\t\timeIndicator[i] = IndicatorConverted;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\timeIndicator[i] = IndicatorUnknown;\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn imeIndicator;\n}\n\n}\n\nvoid ScintillaWin::AddWString(std::wstring_view wsv, CharacterSource charSource) {\n\tif (wsv.empty())\n\t\treturn;\n\n\tconst int codePage = CodePageOfDocument();\n\tfor (size_t i = 0; i < wsv.size(); ) {\n\t\tconst size_t ucWidth = UTF16CharLength(wsv[i]);\n\t\tconst std::string docChar = StringEncode(wsv.substr(i, ucWidth), codePage);\n\n\t\tInsertCharacter(docChar, charSource);\n\t\ti += ucWidth;\n\t}\n}\n\nsptr_t ScintillaWin::HandleCompositionInline(uptr_t, sptr_t lParam) {\n\t// Copy & paste by johnsonj with a lot of helps of Neil.\n\t// Great thanks for my foreruners, jiniya and BLUEnLIVE.\n\n\tIMContext imc(MainHWND());\n\tif (!imc)\n\t\treturn 0;\n\tif (pdoc->IsReadOnly() || SelectionContainsProtected()) {\n\t\timc.Notify(false);\n\t\treturn 0;\n\t}\n\n\tbool initialCompose = false;\n\tif (pdoc->TentativeActive()) {\n\t\tpdoc->TentativeUndo();\n\t} else {\n\t\t// No tentative undo means start of this composition so\n\t\t// fill in any virtual spaces.\n\t\tinitialCompose = true;\n\t}\n\n\tview.imeCaretBlockOverride = false;\n\tHideCursorIfPreferred();\n\n\tif (lParam & GCS_RESULTSTR) {\n\t\tAddWString(imc.GetCompositionString(GCS_RESULTSTR), CharacterSource::ImeResult);\n\t}\n\n\tif (lParam & GCS_COMPSTR) {\n\t\tconst std::wstring wcs = imc.GetCompositionString(GCS_COMPSTR);\n\t\tif (wcs.empty()) {\n\t\t\tShowCaretAtCurrentPosition();\n\t\t\treturn 0;\n\t\t}\n\n\t\tif (initialCompose) {\n\t\t\tClearBeforeTentativeStart();\n\t\t}\n\n\t\t// Set candidate window left aligned to beginning of preedit string.\n\t\tSetCandidateWindowPos(imc);\n\t\tpdoc->TentativeStart(); // TentativeActive from now on.\n\n\t\tstd::vector<int> imeIndicator = MapImeIndicators(imc.GetImeAttributes());\n\n\t\tconst int codePage = CodePageOfDocument();\n\t\tconst std::wstring_view wsv = wcs;\n\t\tfor (size_t i = 0; i < wsv.size(); ) {\n\t\t\tconst size_t ucWidth = UTF16CharLength(wsv[i]);\n\t\t\tconst std::string docChar = StringEncode(wsv.substr(i, ucWidth), codePage);\n\n\t\t\tInsertCharacter(docChar, CharacterSource::TentativeInput);\n\n\t\t\tDrawImeIndicator(imeIndicator[i], docChar.size());\n\t\t\ti += ucWidth;\n\t\t}\n\n\t\t// Japanese IME after pressing Tab replaces input string with first candidate item (target string);\n\t\t// when selecting other candidate item, previous item will be replaced with current one.\n\t\t// After candidate item been added, it's looks like been full selected, it's better to keep caret\n\t\t// at end of \"selection\" (end of input) instead of jump to beginning of input (\"selection\").\n\t\tconst bool onlyTarget = std::all_of(imeIndicator.begin(), imeIndicator.end(), [](int i) noexcept {\n\t\t\treturn i == IndicatorTarget;\n\t\t});\n\t\tif (!onlyTarget) {\n\t\t\t// CS_NOMOVECARET: keep caret at beginning of composition string which already moved in InsertCharacter().\n\t\t\t// GCS_CURSORPOS: current caret position is provided by IME.\n\t\t\tSci::Position imeEndToImeCaretU16 = -static_cast<Sci::Position>(wcs.size());\n\t\t\tif (!(lParam & CS_NOMOVECARET) && (lParam & GCS_CURSORPOS)) {\n\t\t\t\timeEndToImeCaretU16 += imc.GetImeCaretPos();\n\t\t\t}\n\t\t\tif (imeEndToImeCaretU16 != 0) {\n\t\t\t\t// Move back IME caret from current last position to imeCaretPos.\n\t\t\t\tconst Sci::Position currentPos = CurrentPosition();\n\t\t\t\tconst Sci::Position imeCaretPosDoc = pdoc->GetRelativePositionUTF16(currentPos, imeEndToImeCaretU16);\n\n\t\t\t\tMoveImeCarets(-currentPos + imeCaretPosDoc);\n\t\t\t}\n\t\t}\n\n\t\tif (KoreanIME()) {\n\t\t\tview.imeCaretBlockOverride = true;\n\t\t}\n\t}\n\tEnsureCaretVisible();\n\tShowCaretAtCurrentPosition();\n\treturn 0;\n}\n\nnamespace {\n\n// Translate message IDs from WM_* and EM_* to Message::* so can partly emulate Windows Edit control\nMessage SciMessageFromEM(unsigned int iMessage) noexcept {\n\tswitch (iMessage) {\n\tcase EM_CANPASTE: return Message::CanPaste;\n\tcase EM_CANUNDO: return Message::CanUndo;\n\tcase EM_EMPTYUNDOBUFFER: return Message::EmptyUndoBuffer;\n\tcase EM_FINDTEXTEX: return Message::FindText;\n\tcase EM_FORMATRANGE: return Message::FormatRange;\n\tcase EM_GETFIRSTVISIBLELINE: return Message::GetFirstVisibleLine;\n\tcase EM_GETLINECOUNT: return Message::GetLineCount;\n\tcase EM_GETSELTEXT: return Message::GetSelText;\n\tcase EM_GETTEXTRANGE: return Message::GetTextRange;\n\tcase EM_HIDESELECTION: return Message::HideSelection;\n\tcase EM_LINEINDEX: return Message::PositionFromLine;\n\tcase EM_LINESCROLL: return Message::LineScroll;\n\tcase EM_REPLACESEL: return Message::ReplaceSel;\n\tcase EM_SCROLLCARET: return Message::ScrollCaret;\n\tcase EM_SETREADONLY: return Message::SetReadOnly;\n\tcase WM_CLEAR: return Message::Clear;\n\tcase WM_COPY: return Message::Copy;\n\tcase WM_CUT: return Message::Cut;\n\tcase WM_SETTEXT: return Message::SetText;\n\tcase WM_PASTE: return Message::Paste;\n\tcase WM_UNDO: return Message::Undo;\n\t}\n\treturn static_cast<Message>(iMessage);\n}\n\nconstexpr bool IsVisualCharacter(wchar_t charCode) noexcept {\n\tconstexpr wchar_t lastAscii = INT8_MAX;\n\treturn (charCode > lastAscii) || !IsControl(charCode);\n}\n\n}\n\nnamespace Scintilla::Internal {\n\n// Removing 'magic' numbers here would not help here.\n\n// NOLINTBEGIN(*-magic-numbers)\n\nUINT CodePageFromCharSet(CharacterSet characterSet, UINT documentCodePage) noexcept {\n\tif (documentCodePage == CpUtf8) {\n\t\treturn CpUtf8;\n\t}\n\tswitch (characterSet) {\n\tcase CharacterSet::Ansi: return 1252;\n\tcase CharacterSet::Default: return documentCodePage ? documentCodePage : 1252;\n\tcase CharacterSet::Baltic: return 1257;\n\tcase CharacterSet::ChineseBig5: return 950;\n\tcase CharacterSet::EastEurope: return 1250;\n\tcase CharacterSet::GB2312: return 936;\n\tcase CharacterSet::Greek: return 1253;\n\tcase CharacterSet::Hangul: return 949;\n\tcase CharacterSet::Mac: return 10000;\n\tcase CharacterSet::Oem: return 437;\n\tcase CharacterSet::Russian: return 1251;\n\tcase CharacterSet::ShiftJis: return 932;\n\tcase CharacterSet::Turkish: return 1254;\n\tcase CharacterSet::Johab: return 1361;\n\tcase CharacterSet::Hebrew: return 1255;\n\tcase CharacterSet::Arabic: return 1256;\n\tcase CharacterSet::Vietnamese: return 1258;\n\tcase CharacterSet::Thai: return 874;\n\tcase CharacterSet::Iso8859_15: return 28605;\n\t// Not supported\n\tcase CharacterSet::Cyrillic: return documentCodePage;\n\tcase CharacterSet::Symbol: return documentCodePage;\n\tdefault: break;\n\t}\n\treturn documentCodePage;\n}\n\n// NOLINTEND(*-magic-numbers)\n\n}\n\nUINT ScintillaWin::CodePageOfDocument() const noexcept {\n\treturn CodePageFromCharSet(vs.styles[StyleDefault].characterSet, pdoc->dbcsCodePage);\n}\n\nstd::string ScintillaWin::EncodeWString(std::wstring_view wsv) {\n\tif (IsUnicodeMode()) {\n\t\tconst size_t len = UTF8Length(wsv);\n\t\tstd::string putf(len, 0);\n\t\tUTF8FromUTF16(wsv, putf.data(), len);\n\t\treturn putf;\n\t}\n\t// Not in Unicode mode so convert from Unicode to current Scintilla code page\n\treturn StringEncode(wsv, CodePageOfDocument());\n}\n\nsptr_t ScintillaWin::GetTextLength() {\n\tif (pdoc->dbcsCodePage == 0 || pdoc->dbcsCodePage == CpUtf8) {\n\t\treturn pdoc->CountUTF16(0, pdoc->Length());\n\t}\n\t// Count the number of UTF-16 code units line by line\n\tconst UINT cpSrc = CodePageOfDocument();\n\tconst Sci::Line lines = pdoc->LinesTotal();\n\tSci::Position codeUnits = 0;\n\tstd::string lineBytes;\n\tfor (Sci::Line line = 0; line < lines; line++) {\n\t\tconst Sci::Position start = pdoc->LineStart(line);\n\t\tconst Sci::Position width = pdoc->LineStart(line+1) - start;\n\t\tlineBytes.resize(width);\n\t\tpdoc->GetCharRange(lineBytes.data(), start, width);\n\t\tcodeUnits += WideCharLenFromMultiByte(cpSrc, lineBytes);\n\t}\n\treturn codeUnits;\n}\n\nsptr_t ScintillaWin::GetText(uptr_t wParam, sptr_t lParam) {\n\tif (lParam == 0) {\n\t\treturn GetTextLength();\n\t}\n\tif (wParam == 0) {\n\t\treturn 0;\n\t}\n\twchar_t *ptr = static_cast<wchar_t *>(PtrFromSPtr(lParam));\n\tif (pdoc->Length() == 0) {\n\t\t*ptr = L'\\0';\n\t\treturn 0;\n\t}\n\tconst Sci::Position lengthWanted = wParam - 1;\n\tif (IsUnicodeMode()) {\n\t\tSci::Position sizeRequestedRange = pdoc->GetRelativePositionUTF16(0, lengthWanted);\n\t\tif (sizeRequestedRange < 0) {\n\t\t\t// Requested more text than there is in the document.\n\t\t\tsizeRequestedRange = pdoc->Length();\n\t\t}\n\t\tstd::string docBytes(sizeRequestedRange, '\\0');\n\t\tpdoc->GetCharRange(docBytes.data(), 0, sizeRequestedRange);\n\t\tconst size_t uLen = UTF16FromUTF8(docBytes, ptr, lengthWanted);\n\t\tptr[uLen] = L'\\0';\n\t\treturn uLen;\n\t}\n\t// Not Unicode mode\n\t// Convert to Unicode using the current Scintilla code page\n\t// Retrieve as UTF-16 line by line\n\tconst UINT cpSrc = CodePageOfDocument();\n\tconst Sci::Line lines = pdoc->LinesTotal();\n\tSci::Position codeUnits = 0;\n\tstd::string lineBytes;\n\tstd::wstring lineAsUTF16;\n\tfor (Sci::Line line = 0; line < lines && codeUnits < lengthWanted; line++) {\n\t\tconst Sci::Position start = pdoc->LineStart(line);\n\t\tconst Sci::Position width = pdoc->LineStart(line + 1) - start;\n\t\tlineBytes.resize(width);\n\t\tpdoc->GetCharRange(lineBytes.data(), start, width);\n\t\tconst Sci::Position codeUnitsLine = WideCharLenFromMultiByte(cpSrc, lineBytes);\n\t\tlineAsUTF16.resize(codeUnitsLine);\n\t\tconst Sci::Position lengthLeft = lengthWanted - codeUnits;\n\t\tWideCharFromMultiByte(cpSrc, lineBytes, lineAsUTF16.data(), lineAsUTF16.length());\n\t\tconst Sci::Position lengthToCopy = std::min(lengthLeft, codeUnitsLine);\n\t\tlineAsUTF16.copy(ptr + codeUnits, lengthToCopy);\n\t\tcodeUnits += lengthToCopy;\n\t}\n\tptr[codeUnits] = L'\\0';\n\treturn codeUnits;\n}\n\nWindow::Cursor ScintillaWin::ContextCursor(Point pt) {\n\tif (inDragDrop == DragDrop::dragging) {\n\t\treturn Window::Cursor::up;\n\t}\n\t// Display regular (drag) cursor over selection\n\tif (PointInSelMargin(pt)) {\n\t\treturn GetMarginCursor(pt);\n\t} else if (!SelectionEmpty() && PointInSelection(pt)) {\n\t\treturn Window::Cursor::arrow;\n\t} else if (PointIsHotspot(pt)) {\n\t\treturn Window::Cursor::hand;\n\t} else if (hoverIndicatorPos != Sci::invalidPosition) {\n\t\tconst Sci::Position pos = PositionFromLocation(pt, true, true);\n\t\tif (pos != Sci::invalidPosition) {\n\t\t\treturn Window::Cursor::hand;\n\t\t}\n\t}\n\treturn Window::Cursor::text;\n}\n\nsptr_t ScintillaWin::ShowContextMenu(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {\n\tPoint ptScreen = PointFromLParam(lParam);\n\tPoint ptClient;\n\tPOINT point = POINTFromLParam(lParam);\n\tif ((point.x == -1) && (point.y == -1)) {\n\t\t// Caused by keyboard so display menu near caret\n\t\tptClient = PointMainCaret();\n\t\tpoint = POINTFromPoint(ptClient);\n\t\t::ClientToScreen(MainHWND(), &point);\n\t\tptScreen = PointFromPOINT(point);\n\t} else {\n\t\t::ScreenToClient(MainHWND(), &point);\n\t\tptClient = PointFromPOINT(point);\n\t}\n\tif (ShouldDisplayPopup(ptClient)) {\n\t\tContextMenu(ptScreen);\n\t\treturn 0;\n\t}\n\treturn ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);\n}\n\nPRectangle ScintillaWin::GetClientRectangle() const {\n\treturn rectangleClient;\n}\n\nvoid ScintillaWin::SizeWindow() {\n\trectangleClient = wMain.GetClientPosition();\n#if defined(USE_D2D)\n\tHRESULT hrResize = E_FAIL;\n\tif (((technology == Technology::DirectWrite) || (technology == Technology::DirectWriteRetain)) && targets.pHwndRT) {\n\t\t// May be able to just resize the HWND render target\n\t\tconst int scaleFactor = GetFirstIntegralMultipleDeviceScaleFactor();\n\t\tconst D2D1_SIZE_U pixelSize = ::GetSizeUFromRect(GetClientRect(MainHWND()), scaleFactor);\n\t\thrResize = targets.pHwndRT->Resize(pixelSize);\n\t\tif (FAILED(hrResize)) {\n\t\t\tPlatform::DebugPrintf(\"Failed to Resize ID2D1HwndRenderTarget 0x%lx\\n\", hrResize);\n\t\t}\n\t}\n\tif ((technology == Technology::DirectWrite1) && pDXGISwapChain && targets.pDeviceContext &&\n\t\t(paintState == PaintState::notPainting)) {\n\t\ttargets.pDeviceContext->SetTarget(nullptr);\t// ResizeBuffers fails if bitmap still owned by swap chain\n\t\thrResize = pDXGISwapChain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN, 0);\n\t\tif (SUCCEEDED(hrResize)) {\n\t\t\thrResize = SetBackBuffer(MainHWND(), pDXGISwapChain.Get());\n\t\t} else {\n\t\t\tPlatform::DebugPrintf(\"Failed ResizeBuffers 0x%lx\\n\", hrResize);\n\t\t}\n\t}\n\tif (FAILED(hrResize)) {\n\t\tif (paintState == PaintState::notPainting) {\n\t\t\tDropRenderTarget();\n\t\t} else {\n\t\t\ttargets.valid = false;\n\t\t}\n\t}\n#endif\n\t//Platform::DebugPrintf(\"Scintilla WM_SIZE %d %d\\n\", LOWORD(lParam), HIWORD(lParam));\n\tChangeSize();\n}\n\nsptr_t ScintillaWin::MouseMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {\n\tswitch (iMessage) {\n\tcase WM_LBUTTONDOWN:\n\t\t// For IME, set the composition string as the result string.\n\t\tif (IMContext imc{ MainHWND() }) {\n\t\t\timc.Notify(true);\n\t\t}\n\n\t\t//Au: KScintilla will set focus when need, and in a better way.\n\t\t//::SetFocus(MainHWND());\n\n\t\tButtonDownWithModifiers(PointFromLParam(lParam), ::GetMessageTime(),\n\t\t\t\t\tMouseModifiers(wParam));\n\t\tbreak;\n\n\tcase WM_LBUTTONUP:\n\t\tButtonUpWithModifiers(PointFromLParam(lParam),\n\t\t\t\t      ::GetMessageTime(), MouseModifiers(wParam));\n\t\tbreak;\n\n\tcase WM_RBUTTONDOWN: {\n\t\t\t//::SetFocus(MainHWND()); //Au\n\t\t\tconst Point pt = PointFromLParam(lParam);\n\t\t\tif (!PointInSelection(pt)) {\n\t\t\t\tCancelModes();\n\t\t\t\tSetEmptySelection(PositionFromLocation(PointFromLParam(lParam)));\n\t\t\t}\n\n\t\t\tRightButtonDownWithModifiers(pt, ::GetMessageTime(), MouseModifiers(wParam));\n\t\t}\n\t\tbreak;\n\n\tcase WM_MOUSEMOVE: {\n\t\t\tcursorIsHidden = false; // to be shown by ButtonMoveWithModifiers\n\t\t\tconst Point pt = PointFromLParam(lParam);\n\n\t\t\t// Windows might send WM_MOUSEMOVE even though the mouse has not been moved:\n\t\t\t// http://blogs.msdn.com/b/oldnewthing/archive/2003/10/01/55108.aspx\n\t\t\tif (ptMouseLast != pt) {\n\t\t\t\tSetTrackMouseLeaveEvent(true);\n\t\t\t\tButtonMoveWithModifiers(pt, ::GetMessageTime(), MouseModifiers(wParam));\n\t\t\t}\n\t\t}\n\t\tbreak;\n\n\tcase WM_MOUSELEAVE:\n\t\tSetTrackMouseLeaveEvent(false);\n\t\tMouseLeave();\n\t\treturn ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);\n\n\tcase WM_MOUSEWHEEL:\n\tcase WM_MOUSEHWHEEL:\n\t\tif (!mouseWheelCaptures) {\n\t\t\t// if the mouse wheel is not captured, test if the mouse\n\t\t\t// pointer is over the editor window and if not, don't\n\t\t\t// handle the message but pass it on.\n\t\t\tRECT rc;\n\t\t\tGetWindowRect(MainHWND(), &rc);\n\t\t\tconst POINT pt = POINTFromLParam(lParam);\n\t\t\tif (!PtInRect(&rc, pt))\n\t\t\t\treturn ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);\n\t\t}\n\t\t// if autocomplete list active then send mousewheel message to it\n\t\tif (ac.Active()) {\n\t\t\tHWND hWnd = HwndFromWindow(*(ac.lb));\n\t\t\t::SendMessage(hWnd, iMessage, wParam, lParam);\n\t\t\tbreak;\n\t\t}\n\n\t\t// Treat Shift+WM_MOUSEWHEEL as horizontal scrolling, not data-zoom.\n\t\tif (iMessage == WM_MOUSEHWHEEL || (wParam & MK_SHIFT)) {\n\t\t\tif (vs.wrap.state != Wrap::None || charsPerScroll == 0) {\n\t\t\t\treturn ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);\n\t\t\t}\n\n\t\t\tMouseWheelDelta &wheelDelta = (iMessage == WM_MOUSEHWHEEL) ? horizontalWheelDelta : verticalWheelDelta;\n\t\t\tif (wheelDelta.Accumulate(wParam)) {\n\t\t\t\tint charsToScroll = charsPerScroll * wheelDelta.Actions();\n\t\t\t\tif (iMessage == WM_MOUSEHWHEEL) {\n\t\t\t\t\t// horizontal scroll is in reverse direction\n\t\t\t\t\tcharsToScroll = -charsToScroll;\n\t\t\t\t}\n\t\t\t\tconst int widthToScroll = static_cast<int>(std::lround(charsToScroll * vs.aveCharWidth));\n\t\t\t\tHorizontalScrollToClamped(xOffset + widthToScroll);\n\t\t\t}\n\t\t\t// return 1 for Logitech mouse, https://www.pretentiousname.com/setpoint_hwheel/index.html\n\t\t\treturn (iMessage == WM_MOUSEHWHEEL) ? 1 : 0;\n\t\t}\n\n\t\t// Either SCROLL vertically or ZOOM. We handle the wheel steppings calculation\n\t\tif (linesPerScroll != 0 && verticalWheelDelta.Accumulate(wParam)) {\n\t\t\tSci::Line linesToScroll = linesPerScroll;\n\t\t\tif (linesPerScroll == WHEEL_PAGESCROLL)\n\t\t\t\tlinesToScroll = LinesOnScreen() - 1;\n\t\t\tif (linesToScroll == 0) {\n\t\t\t\tlinesToScroll = 1;\n\t\t\t}\n\t\t\tlinesToScroll *= verticalWheelDelta.Actions();\n\n\t\t\tif (wParam & MK_CONTROL) {\n\t\t\t\t// Zoom! We play with the font sizes in the styles.\n\t\t\t\t// Number of steps/line is ignored, we just care if sizing up or down\n\t\t\t\tif (linesToScroll < 0) {\n\t\t\t\t\tKeyCommand(Message::ZoomIn);\n\t\t\t\t} else {\n\t\t\t\t\tKeyCommand(Message::ZoomOut);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Scroll\n\t\t\t\tScrollTo(topLine + linesToScroll);\n\t\t\t}\n\t\t}\n\t\treturn 0;\n\t}\n\treturn 0;\n}\n\nsptr_t ScintillaWin::KeyMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {\n\tswitch (iMessage) {\n\n\tcase WM_SYSKEYDOWN:\n\tcase WM_KEYDOWN: {\n\t\t\t// Platform::DebugPrintf(\"Keydown %c %c%c%c%c %x %x\\n\",\n\t\t\t// iMessage == WM_KEYDOWN ? 'K' : 'S',\n\t\t\t// (lParam & (1 << 24)) ? 'E' : '-',\n\t\t\t// KeyboardIsKeyDown(VK_SHIFT) ? 'S' : '-',\n\t\t\t// KeyboardIsKeyDown(VK_CONTROL) ? 'C' : '-',\n\t\t\t// KeyboardIsKeyDown(VK_MENU) ? 'A' : '-',\n\t\t\t// wParam, lParam);\n\t\t\tlastKeyDownConsumed = false;\n\t\t\tconst bool altDown = KeyboardIsKeyDown(VK_MENU);\n\t\t\tif (altDown && KeyboardIsNumericKeypadFunction(wParam, lParam)) {\n\t\t\t\t// Don't interpret these as they may be characters entered by number.\n\t\t\t\treturn ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);\n\t\t\t}\n\t\t\tconst int ret = KeyDownWithModifiers(\n\t\t\t\t\t\t\t\t KeyTranslate(wParam),\n\t\t\t\t\t\t\t     ModifierFlags(KeyboardIsKeyDown(VK_SHIFT),\n\t\t\t\t\t\t\t\t\t     KeyboardIsKeyDown(VK_CONTROL),\n\t\t\t\t\t\t\t\t\t     altDown),\n\t\t\t\t\t\t\t     &lastKeyDownConsumed);\n\t\t\tif (!ret && !lastKeyDownConsumed) {\n\t\t\t\treturn ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\tcase WM_KEYUP:\n\t\t//Platform::DebugPrintf(\"S keyup %d %x %x\\n\",iMessage, wParam, lParam);\n\t\treturn ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);\n\n\tcase WM_CHAR:\n\t\tHideCursorIfPreferred();\n\t\tif (const wchar_t charCode = static_cast<wchar_t>(wParam);\n\t\t\tIsVisualCharacter(charCode) || !lastKeyDownConsumed) {\n\t\t\tif (IS_HIGH_SURROGATE(charCode)) {\n\t\t\t\t// If this is a high surrogate character, we need a second one\n\t\t\t\tlastHighSurrogateChar = charCode;\n\t\t\t} else {\n\t\t\t\tstd::wstring wcs({ charCode });\n\t\t\t\tif (IS_LOW_SURROGATE(charCode)) {\n\t\t\t\t\twcs.insert(wcs.begin(), lastHighSurrogateChar);\n\t\t\t\t}\n\t\t\t\tAddWString(wcs, CharacterSource::DirectInput);\n\t\t\t\tlastHighSurrogateChar = 0;\n\t\t\t}\n\t\t}\n\t\treturn 0;\n\n\tcase WM_UNICHAR:\n\t\tif (wParam == UNICODE_NOCHAR) {\n\t\t\treturn TRUE;\n\t\t} else if (lastKeyDownConsumed) {\n\t\t\treturn 1;\n\t\t} else {\n\t\t\twchar_t wcs[3] = { 0 };\n\t\t\tconst size_t wclen = UTF16FromUTF32Character(static_cast<unsigned int>(wParam), wcs);\n\t\t\tAddWString(std::wstring_view(wcs, wclen), CharacterSource::DirectInput);\n\t\t\treturn FALSE;\n\t\t}\n\t}\n\n\treturn 0;\n}\n\nsptr_t ScintillaWin::FocusMessage(unsigned int iMessage, uptr_t wParam, sptr_t) {\n\tswitch (iMessage) {\n\tcase WM_KILLFOCUS: {\n\t\tHWND wOther = reinterpret_cast<HWND>(wParam);\n\t\tHWND wThis = MainHWND();\n\t\tconst HWND wCT = HwndFromWindow(ct.wCallTip);\n\t\tif (!wParam ||\n\t\t\t!(::IsChild(wThis, wOther) || (wOther == wCT))) {\n\t\t\tSetFocusState(false);\n\t\t\tDestroySystemCaret();\n\t\t}\n\t\t// Explicitly complete any IME composition\n\t\tif (IMContext imc{ MainHWND() }) {\n\t\t\timc.Notify(true);\n\t\t}\n\t\tbreak;\n\t}\n\n\tcase WM_SETFOCUS:\n\t\tSetFocusState(true);\n\t\tDestroySystemCaret();\n\t\tCreateSystemCaret();\n\t\tbreak;\n\t}\n\treturn 0;\n}\n\nsptr_t ScintillaWin::IMEMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {\n\tswitch (iMessage) {\n\n\tcase WM_INPUTLANGCHANGE:\n\t\treturn ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);\n\n\tcase WM_INPUTLANGCHANGEREQUEST:\n\t\treturn ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);\n\n\tcase WM_IME_KEYDOWN: {\n\t\t\tif (wParam == VK_HANJA) {\n\t\t\t\t// On US keyboards with Korean Microsoft IME, VK_HANJA is right Ctrl\n\t\t\t\tToggleHanja();\n\t\t\t}\n\t\t\treturn ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);\n\t\t}\n\n\tcase WM_IME_REQUEST: {\n\t\t\tif (wParam == IMR_RECONVERTSTRING) {\n\t\t\t\treturn ImeOnReconvert(lParam);\n\t\t\t}\n\t\t\tif (wParam == IMR_DOCUMENTFEED) {\n\t\t\t\treturn ImeOnDocumentFeed(lParam);\n\t\t\t}\n\t\t\treturn ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);\n\t\t}\n\n\tcase WM_IME_STARTCOMPOSITION:\n\t\tif (KoreanIME() || imeInteraction == IMEInteraction::Inline) {\n\t\t\treturn 0;\n\t\t} else {\n\t\t\tImeStartComposition();\n\t\t\treturn ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);\n\t\t}\n\n\tcase WM_IME_ENDCOMPOSITION:\n\t\tImeEndComposition();\n\t\treturn ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);\n\n\tcase WM_IME_COMPOSITION:\n\t\tif (KoreanIME() || imeInteraction == IMEInteraction::Inline) {\n\t\t\treturn HandleCompositionInline(wParam, lParam);\n\t\t} else {\n\t\t\treturn HandleCompositionWindowed(wParam, lParam);\n\t\t}\n\n\tcase WM_IME_SETCONTEXT:\n\t\tif (KoreanIME() || imeInteraction == IMEInteraction::Inline) {\n\t\t\tif (wParam) {\n\t\t\t\tLPARAM NoImeWin = lParam;\n\t\t\t\tNoImeWin = NoImeWin & (~ISC_SHOWUICOMPOSITIONWINDOW);\n\t\t\t\treturn ::DefWindowProc(MainHWND(), iMessage, wParam, NoImeWin);\n\t\t\t}\n\t\t}\n\t\treturn ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);\n\n\tcase WM_IME_NOTIFY:\n\t\treturn ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);\n\n\t}\n\treturn 0;\n}\n\nsptr_t ScintillaWin::EditMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {\n\tswitch (iMessage) {\n\n\tcase EM_LINEFROMCHAR:\n\t\tif (PositionFromUPtr(wParam) < 0) {\n\t\t\twParam = SelectionStart().Position();\n\t\t}\n\t\treturn pdoc->LineFromPosition(wParam);\n\n\tcase EM_EXLINEFROMCHAR:\n\t\treturn pdoc->LineFromPosition(lParam);\n\n\tcase EM_GETSEL:\n\t\tif (wParam) {\n\t\t\t*static_cast<DWORD *>(PtrFromUPtr(wParam)) = static_cast<DWORD>(SelectionStart().Position());\n\t\t}\n\t\tif (lParam) {\n\t\t\t*static_cast<DWORD *>(PtrFromSPtr(lParam)) = static_cast<DWORD>(SelectionEnd().Position());\n\t\t}\n\t\treturn MAKELRESULT(SelectionStart().Position(), SelectionEnd().Position());\n\n\tcase EM_EXGETSEL: {\n\t\t\tif (lParam == 0) {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tCHARRANGE *pCR = static_cast<CHARRANGE *>(PtrFromSPtr(lParam));\n\t\t\tpCR->cpMin = static_cast<LONG>(SelectionStart().Position());\n\t\t\tpCR->cpMax = static_cast<LONG>(SelectionEnd().Position());\n\t\t}\n\t\tbreak;\n\n\tcase EM_SETSEL: {\n\t\t\tSci::Position nStart = wParam;\n\t\t\tSci::Position nEnd = lParam;\n\t\t\tif (nStart == 0 && nEnd == -1) {\n\t\t\t\tnEnd = pdoc->Length();\n\t\t\t}\n\t\t\tif (nStart == -1) {\n\t\t\t\tnStart = nEnd;\t// Remove selection\n\t\t\t}\n\t\t\tSetSelection(nEnd, nStart);\n\t\t\tEnsureCaretVisible();\n\t\t}\n\t\tbreak;\n\n\tcase EM_EXSETSEL: {\n\t\t\tif (lParam == 0) {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tconst CHARRANGE *pCR = static_cast<const CHARRANGE *>(PtrFromSPtr(lParam));\n\t\t\tsel.selType = Selection::SelTypes::stream;\n\t\t\tif (pCR->cpMin == 0 && pCR->cpMax == -1) {\n\t\t\t\tSetSelection(pCR->cpMin, pdoc->Length());\n\t\t\t} else {\n\t\t\t\tSetSelection(pCR->cpMin, pCR->cpMax);\n\t\t\t}\n\t\t\tEnsureCaretVisible();\n\t\t\treturn pdoc->LineFromPosition(SelectionStart().Position());\n\t\t}\n\t}\n\treturn 0;\n}\n\nsptr_t ScintillaWin::IdleMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {\n\tswitch (iMessage) {\n\tcase SC_WIN_IDLE:\n\t\t// wParam=dwTickCountInitial, or 0 to initialize.  lParam=bSkipUserInputTest\n\t\tif (idler.state) {\n\t\t\tif (lParam || (WAIT_TIMEOUT == MsgWaitForMultipleObjects(0, nullptr, 0, 0, QS_INPUT | QS_HOTKEY))) {\n\t\t\t\tif (Idle()) {\n\t\t\t\t\t// User input was given priority above, but all events do get a turn.  Other\n\t\t\t\t\t// messages, notifications, etc. will get interleaved with the idle messages.\n\n\t\t\t\t\t// However, some things like WM_PAINT are a lower priority, and will not fire\n\t\t\t\t\t// when there's a message posted.  So, several times a second, we stop and let\n\t\t\t\t\t// the low priority events have a turn (after which the timer will fire again).\n\n\t\t\t\t\t// Suppress a warning from Code Analysis that the GetTickCount function\n\t\t\t\t\t// wraps after 49 days. The WM_TIMER will kick off another SC_WIN_IDLE\n\t\t\t\t\t// after the wrap.\n#ifdef _MSC_VER\n#pragma warning(suppress: 28159)\n#endif\n\t\t\t\t\tconst DWORD dwCurrent = GetTickCount();\n\t\t\t\t\tconst DWORD dwStart = wParam ? static_cast<DWORD>(wParam) : dwCurrent;\n\t\t\t\t\tconstexpr DWORD maxWorkTime = 50;\n\n\t\t\t\t\tif (dwCurrent >= dwStart && dwCurrent > maxWorkTime &&dwCurrent - maxWorkTime < dwStart)\n\t\t\t\t\t\tPostMessage(MainHWND(), SC_WIN_IDLE, dwStart, 0);\n\t\t\t\t} else {\n\t\t\t\t\tSetIdle(false);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tbreak;\n\n\tcase SC_WORK_IDLE:\n\t\tIdleWork();\n\t\tbreak;\n\t}\n\treturn 0;\n}\n\nsptr_t ScintillaWin::SciMessage(Message iMessage, uptr_t wParam, sptr_t lParam) {\n\tswitch (iMessage) {\n\tcase Message::GetDirectFunction:\n\t\treturn reinterpret_cast<sptr_t>(DirectFunction);\n\n\tcase Message::GetDirectStatusFunction:\n\t\treturn reinterpret_cast<sptr_t>(DirectStatusFunction);\n\n\tcase Message::GetDirectPointer:\n\t\treturn reinterpret_cast<sptr_t>(this);\n\n\tcase Message::GrabFocus:\n\t\t::SetFocus(MainHWND());\n\t\tbreak;\n\n#ifdef INCLUDE_DEPRECATED_FEATURES\n\tcase Message::SETKEYSUNICODE:\n\t\tbreak;\n\n\tcase Message::GETKEYSUNICODE:\n\t\treturn true;\n#endif\n\n\tcase Message::SetTechnology:\n\t\tif (const Technology technologyNew = static_cast<Technology>(wParam);\n\t\t\t(technologyNew == Technology::Default) ||\n\t\t\t(technologyNew == Technology::DirectWriteRetain) ||\n\t\t\t(technologyNew == Technology::DirectWriteDC) ||\n\t\t\t(technologyNew == Technology::DirectWrite) ||\n\t\t\t(technologyNew == Technology::DirectWrite1)) {\n\t\t\tif (technology != technologyNew) {\n\t\t\t\tif (technologyNew > Technology::Default) {\n#if defined(USE_D2D)\n\t\t\t\t\tif (!LoadD2D()) {\n\t\t\t\t\t\t// Failed to load Direct2D or DirectWrite so no effect\n\t\t\t\t\t\treturn 0;\n\t\t\t\t\t}\n\t\t\t\t\tUpdateRenderingParams(true);\n#else\n\t\t\t\t\treturn 0;\n#endif\n\t\t\t\t} else {\n\t\t\t\t\tbidirectional = Bidirectional::Disabled;\n\t\t\t\t}\n\t\t\t\tDropRenderTarget();\n\t\t\t\ttechnology = technologyNew;\n\t\t\t\tview.bufferedDraw = technologyNew == Technology::Default;\n\t\t\t\t// Invalidate all cached information including layout.\n\t\t\t\tInvalidateStyleRedraw();\n\t\t\t}\n\t\t}\n\t\tbreak;\n\n\tcase Message::SetBidirectional:\n\t\tif (technology == Technology::Default) {\n\t\t\tbidirectional = Bidirectional::Disabled;\n\t\t} else if (static_cast<Bidirectional>(wParam) <= Bidirectional::R2L) {\n\t\t\tbidirectional = static_cast<Bidirectional>(wParam);\n\t\t}\n\t\t// Invalidate all cached information including layout.\n\t\tInvalidateStyleRedraw();\n\t\tbreak;\n\n\tcase Message::TargetAsUTF8:\n\t\treturn TargetAsUTF8(CharPtrFromSPtr(lParam));\n\n\tcase Message::EncodedFromUTF8:\n\t\treturn EncodedFromUTF8(ConstCharPtrFromUPtr(wParam),\n\t\t\tCharPtrFromSPtr(lParam));\n\n\tdefault:\n\t\tbreak;\n\n\t}\n\treturn 0;\n}\n\nsptr_t ScintillaWin::WndProc(Message iMessage, uptr_t wParam, sptr_t lParam) {\n\ttry {\n\t\t//Platform::DebugPrintf(\"S M:%x WP:%x L:%x\\n\", iMessage, wParam, lParam);\n\t\tconst unsigned int msg = static_cast<unsigned int>(iMessage);\n\t\tswitch (msg) {\n\n\t\tcase WM_CREATE:\n\t\t\tctrlID = ::GetDlgCtrlID(HwndFromWindow(wMain));\n\t\t\tUpdateBaseElements();\n\t\t\t// Get Intellimouse scroll line parameters\n\t\t\tGetMouseParameters();\n\t\t\t::RegisterDragDrop(MainHWND(), &dt);\n\t\t\tbreak;\n\n\t\tcase WM_COMMAND:\n\t\t\tCommand(LOWORD(wParam));\n\t\t\tbreak;\n\n\t\tcase WM_PAINT:\n\t\t\treturn WndPaint();\n\n\t\tcase WM_PRINTCLIENT: {\n\t\t\t\tHDC hdc = reinterpret_cast<HDC>(wParam);\n\t\t\t\tif (!IsCompatibleDC(hdc)) {\n\t\t\t\t\treturn ::DefWindowProc(MainHWND(), msg, wParam, lParam);\n\t\t\t\t}\n\t\t\t\tFullPaintDC(hdc);\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase WM_VSCROLL:\n\t\t\tScrollMessage(wParam);\n\t\t\tbreak;\n\n\t\tcase WM_HSCROLL:\n\t\t\tHorizontalScrollMessage(wParam);\n\t\t\tbreak;\n\n\t\tcase WM_SIZE:\n\t\t\tSizeWindow();\n\t\t\tbreak;\n\n\t\tcase WM_TIMER:\n\t\t\tif (wParam == idleTimerID && idler.state) {\n\t\t\t\tSendMessage(MainHWND(), SC_WIN_IDLE, 0, 1);\n\t\t\t} else {\n\t\t\t\tTickFor(static_cast<TickReason>(wParam - fineTimerStart));\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase SC_WIN_IDLE:\n\t\tcase SC_WORK_IDLE:\n\t\t\treturn IdleMessage(msg, wParam, lParam);\n\n\t\tcase WM_GETMINMAXINFO:\n\t\t\treturn ::DefWindowProc(MainHWND(), msg, wParam, lParam);\n\n\t\tcase WM_LBUTTONDOWN:\n\t\tcase WM_LBUTTONUP:\n\t\tcase WM_RBUTTONDOWN:\n\t\tcase WM_MOUSEMOVE:\n\t\tcase WM_MOUSELEAVE:\n\t\tcase WM_MOUSEWHEEL:\n\t\tcase WM_MOUSEHWHEEL:\n\t\t\treturn MouseMessage(msg, wParam, lParam);\n\n\t\tcase WM_SETCURSOR:\n\t\t\tif (LOWORD(lParam) == HTCLIENT) {\n\t\t\t\tif (!cursorIsHidden) {\n\t\t\t\t\tPOINT pt;\n\t\t\t\t\tif (::GetCursorPos(&pt)) {\n\t\t\t\t\t\t::ScreenToClient(MainHWND(), &pt);\n\t\t\t\t\t\tDisplayCursor(ContextCursor(PointFromPOINT(pt)));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn TRUE;\n\t\t\t} else {\n\t\t\t\treturn ::DefWindowProc(MainHWND(), msg, wParam, lParam);\n\t\t\t}\n\n\t\tcase WM_SYSKEYDOWN:\n\t\tcase WM_KEYDOWN:\n\t\tcase WM_KEYUP:\n\t\tcase WM_CHAR:\n\t\tcase WM_UNICHAR:\n\t\t\treturn KeyMessage(msg, wParam, lParam);\n\n\t\tcase WM_SETTINGCHANGE:\n\t\t\t//Platform::DebugPrintf(\"Setting Changed\\n\");\n#if defined(USE_D2D)\n\t\t\tif (technology != Technology::Default) {\n\t\t\t\tUpdateRenderingParams(true);\n\t\t\t}\n#endif\n\t\t\tUpdateBaseElements();\n\t\t\t// Get Intellimouse scroll line parameters\n\t\t\tGetMouseParameters();\n\t\t\tInvalidateStyleRedraw();\n\t\t\tbreak;\n\n\t\tcase WM_GETDLGCODE:\n\t\t\treturn DLGC_HASSETSEL | DLGC_WANTALLKEYS;\n\n\t\tcase WM_KILLFOCUS:\n\t\tcase WM_SETFOCUS:\n\t\t\treturn FocusMessage(msg, wParam, lParam);\n\n\t\tcase WM_SYSCOLORCHANGE:\n\t\t\t//Platform::DebugPrintf(\"Setting Changed\\n\");\n\t\t\tUpdateBaseElements();\n\t\t\tInvalidateStyleData();\n\t\t\tbreak;\n\n\t\t\t//Au: must use WM_DPICHANGED_BEFOREPARENT, like all controls. Parent on WM_DPICHANGED expects the control is updated.\n#define WM_DPICHANGED_BEFOREPARENT       0x02E2\n\t\tcase WM_DPICHANGED_BEFOREPARENT: //was WM_DPICHANGED\n\t\t\tdpi = HIWORD(wParam);\n\t\t\treverseArrowCursor.Invalidate();\n\t\t\tInvalidateStyleRedraw();\n\t\t\tbreak;\n\n\t\tcase WM_DPICHANGED_AFTERPARENT: {\n\t\t\t\tconst UINT dpiNow = DpiForWindow(wMain.GetID());\n\t\t\t\tif (dpi != dpiNow) {\n\t\t\t\t\tdpi = dpiNow;\n\t\t\t\t\treverseArrowCursor.Invalidate();\n\t\t\t\t\tInvalidateStyleRedraw();\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase WM_CONTEXTMENU:\n\t\t\treturn ShowContextMenu(msg, wParam, lParam);\n\n\t\tcase WM_ERASEBKGND:\n\t\t\treturn 1;   // Avoid any background erasure as whole window painted.\n\n\t\tcase WM_SETREDRAW:\n\t\t\t::DefWindowProc(MainHWND(), msg, wParam, lParam);\n\t\t\tif (wParam) {\n\t\t\t\tSetScrollBars();\n\t\t\t\tSetVerticalScrollPos();\n\t\t\t\tSetHorizontalScrollPos();\n\t\t\t}\n\t\t\treturn 0;\n\n\t\tcase WM_CAPTURECHANGED:\n\t\t\tcapturedMouse = false;\n\t\t\treturn 0;\n\n\t\t// These are not handled in Scintilla and its faster to dispatch them here.\n\t\t// Also moves time out to here so profile doesn't count lots of empty message calls.\n\n\t\tcase WM_MOVE:\n\t\tcase WM_MOUSEACTIVATE:\n\t\tcase WM_NCHITTEST:\n\t\tcase WM_NCCALCSIZE:\n\t\tcase WM_NCPAINT:\n\t\tcase WM_NCMOUSEMOVE:\n\t\tcase WM_NCLBUTTONDOWN:\n\t\tcase WM_SYSCOMMAND:\n\t\tcase WM_WINDOWPOSCHANGING:\n\t\t\treturn ::DefWindowProc(MainHWND(), msg, wParam, lParam);\n\n\t\tcase WM_WINDOWPOSCHANGED:\n#if defined(USE_D2D)\n\t\t\tif (technology != Technology::Default) {\n\t\t\t\tif (UpdateRenderingParams(false)) {\n\t\t\t\t\tDropGraphics();\n\t\t\t\t\tRedraw();\n\t\t\t\t}\n\t\t\t}\n#endif\n\t\t\treturn ::DefWindowProc(MainHWND(), msg, wParam, lParam);\n\n\t\t//Au: MSAA (or WPF?) uses wm_gettext for Name property\n\t\t//case WM_GETTEXTLENGTH:\n\t\t//\treturn GetTextLength();\n\n\t\t//case WM_GETTEXT:\n\t\t//\treturn GetText(wParam, lParam);\n\n\t\tcase WM_INPUTLANGCHANGE:\n\t\tcase WM_INPUTLANGCHANGEREQUEST:\n\t\tcase WM_IME_KEYDOWN:\n\t\tcase WM_IME_REQUEST:\n\t\tcase WM_IME_STARTCOMPOSITION:\n\t\tcase WM_IME_ENDCOMPOSITION:\n\t\tcase WM_IME_COMPOSITION:\n\t\tcase WM_IME_SETCONTEXT:\n\t\tcase WM_IME_NOTIFY:\n\t\t\treturn IMEMessage(msg, wParam, lParam);\n\n\t\tcase EM_LINEFROMCHAR:\n\t\tcase EM_EXLINEFROMCHAR:\n\t\tcase EM_GETSEL:\n\t\tcase EM_EXGETSEL:\n\t\tcase EM_SETSEL:\n\t\tcase EM_EXSETSEL:\n\t\t\treturn EditMessage(msg, wParam, lParam);\n\t\t}\n\n\t\tiMessage = SciMessageFromEM(msg);\n\t\tswitch (iMessage) {\n\t\tcase Message::GetDirectFunction:\n\t\tcase Message::GetDirectStatusFunction:\n\t\tcase Message::GetDirectPointer:\n\t\tcase Message::GrabFocus:\n\t\tcase Message::SetTechnology:\n\t\tcase Message::SetBidirectional:\n\t\tcase Message::TargetAsUTF8:\n\t\tcase Message::EncodedFromUTF8:\n\t\t\treturn SciMessage(iMessage, wParam, lParam);\n\n#pragma region //Au\n\t\t\t//can use callback function instead of WM_NOTIFY.\n\t\tcase Message::SCI_SETNOTIFYCALLBACK:\n\t\t\tcbNotify = (Sci_NotifyCallback)lParam; cbNotifyParam = (void*)wParam;\n\t\t\tbreak;\n\n\t\t\t//to draw in annotation eg image instead of text.\n\t\tcase Message::SCI_SETANNOTATIONDRAWCALLBACK:\n\t\t\tcbAnnotationDraw = (Sci_AnnotationDrawCallback)lParam; cbAnnotationDrawParam = (void*)wParam;\n\t\t\tbreak;\n\n\t\t\t//to draw in annotation eg image instead of text.\n\t\tcase Message::SCI_SETMARGINDRAWCALLBACK:\n\t\t\tcbMarginDraw = (Sci_MarginDrawCallback)lParam;\n\t\t\tcbMarginsMask = (int)wParam;\n\t\t\tbreak;\n\n\t\tcase Message::SCI_ISXINMARGIN:\n\t\t\treturn PointInSelMargin(Point((XYPOSITION)(int)wParam, (XYPOSITION)(int)lParam));\n\n\t\tcase Message::SCI_DRAGDROP:\n\t\t\tAuDragDrop((int)wParam, (Sci_DragDropData*)lParam);\n\t\t\tbreak;\n#pragma endregion\n\n\t\tdefault:\n\t\t\treturn ScintillaBase::WndProc(iMessage, wParam, lParam);\n\t\t}\n\t} catch (std::bad_alloc &) {\n\t\terrorStatus = Status::BadAlloc;\n\t} catch (...) {\n\t\terrorStatus = Status::Failure;\n\t}\n\treturn 0;\n}\n\nbool ScintillaWin::ValidCodePage(int codePage) const {\n\treturn codePage == 0 || codePage == CpUtf8 || IsDBCSCodePage(codePage);\n}\n\nstd::string ScintillaWin::UTF8FromEncoded(std::string_view encoded) const {\n\tif (IsUnicodeMode()) {\n\t\treturn std::string(encoded);\n\t}\n\t// Pivot through wide string\n\tstd::wstring ws = StringDecode(encoded, CodePageOfDocument());\n\treturn StringEncode(ws, CpUtf8);\n}\n\nstd::string ScintillaWin::EncodedFromUTF8(std::string_view utf8) const {\n\tif (IsUnicodeMode()) {\n\t\treturn std::string(utf8);\n\t}\n\t// Pivot through wide string\n\tstd::wstring ws = StringDecode(utf8, CpUtf8);\n\treturn StringEncode(ws, CodePageOfDocument());\n}\n\nsptr_t ScintillaWin::DefWndProc(Message iMessage, uptr_t wParam, sptr_t lParam) {\n\treturn ::DefWindowProc(MainHWND(), static_cast<unsigned int>(iMessage), wParam, lParam);\n}\n\nbool ScintillaWin::FineTickerRunning(TickReason reason) {\n\treturn timers[static_cast<size_t>(reason)] != 0;\n}\n\nvoid ScintillaWin::FineTickerStart(TickReason reason, int millis, int tolerance) {\n\tFineTickerCancel(reason);\n\tconst UINT_PTR reasonIndex = static_cast<UINT_PTR>(reason);\n\tconst UINT_PTR eventID = static_cast<UINT_PTR>(fineTimerStart) + reasonIndex;\n\tif (SetCoalescableTimerFn && tolerance) {\n\t\ttimers[reasonIndex] = SetCoalescableTimerFn(MainHWND(), eventID, millis, nullptr, tolerance);\n\t} else {\n\t\ttimers[reasonIndex] = ::SetTimer(MainHWND(), eventID, millis, nullptr);\n\t}\n}\n\nvoid ScintillaWin::FineTickerCancel(TickReason reason) {\n\tconst UINT_PTR reasonIndex = static_cast<UINT_PTR>(reason);\n\tif (timers[reasonIndex]) {\n\t\t::KillTimer(MainHWND(), timers[reasonIndex]);\n\t\ttimers[reasonIndex] = 0;\n\t}\n}\n\n\nbool ScintillaWin::SetIdle(bool on) {\n\t// On Win32 the Idler is implemented as a Timer on the Scintilla window.  This\n\t// takes advantage of the fact that WM_TIMER messages are very low priority,\n\t// and are only posted when the message queue is empty, i.e. during idle time.\n\tif (idler.state != on) {\n\t\tif (on) {\n\t\t\tconstexpr UINT waitTimeMillis = 10;\n\t\t\tidler.idlerID = ::SetTimer(MainHWND(), idleTimerID, waitTimeMillis, nullptr)\n\t\t\t\t? reinterpret_cast<IdlerID>(idleTimerID) : nullptr;\n\t\t} else {\n\t\t\t::KillTimer(MainHWND(), reinterpret_cast<uptr_t>(idler.idlerID));\n\t\t\tidler.idlerID = nullptr;\n\t\t}\n\t\tidler.state = idler.idlerID != nullptr;\n\t}\n\treturn idler.state;\n}\n\nvoid ScintillaWin::IdleWork() {\n\tstyleIdleInQueue = false;\n\tEditor::IdleWork();\n}\n\nvoid ScintillaWin::QueueIdleWork(WorkItems items, Sci::Position upTo) {\n\tEditor::QueueIdleWork(items, upTo);\n\tif (!styleIdleInQueue) {\n\t\tif (PostMessage(MainHWND(), SC_WORK_IDLE, 0, 0)) {\n\t\t\tstyleIdleInQueue = true;\n\t\t}\n\t}\n}\n\nvoid ScintillaWin::SetMouseCapture(bool on) {\n\tif (mouseDownCaptures) {\n\t\tif (on) {\n\t\t\t::SetCapture(MainHWND());\n\t\t} else {\n\t\t\t::ReleaseCapture();\n\t\t}\n\t}\n\tcapturedMouse = on;\n}\n\nbool ScintillaWin::HaveMouseCapture() {\n\t// Cannot just see if GetCapture is this window as the scroll bar also sets capture for the window\n\treturn capturedMouse;\n\t//return capturedMouse && (::GetCapture() == MainHWND());\n}\n\nvoid ScintillaWin::SetTrackMouseLeaveEvent(bool on) noexcept {\n\tif (on && !trackedMouseLeave) {\n\t\tTRACKMOUSEEVENT tme {};\n\t\ttme.cbSize = sizeof(tme);\n\t\ttme.dwFlags = TME_LEAVE;\n\t\ttme.hwndTrack = MainHWND();\n\t\ttme.dwHoverTime = HOVER_DEFAULT;\t// Unused but triggers Dr. Memory if not initialized\n\t\tTrackMouseEvent(&tme);\n\t}\n\ttrackedMouseLeave = on;\n}\n\nvoid ScintillaWin::HideCursorIfPreferred() noexcept {\n\t// SPI_GETMOUSEVANISH from OS.\n\tif (typingWithoutCursor && !cursorIsHidden) {\n\t\t::SetCursor({});\n\t\tcursorIsHidden = true;\n\t}\n}\n\nvoid ScintillaWin::UpdateBaseElements() {\n\tstruct ElementToIndex { Element element; int nIndex; };\n\tconst ElementToIndex eti[] = {\n\t\t{ Element::List, COLOR_WINDOWTEXT },\n\t\t{ Element::ListBack, COLOR_WINDOW },\n\t\t{ Element::ListSelected, COLOR_HIGHLIGHTTEXT },\n\t\t{ Element::ListSelectedBack, COLOR_HIGHLIGHT },\n\t};\n\tbool changed = false;\n\tfor (const ElementToIndex &ei : eti) {\n\t\tchanged = vs.SetElementBase(ei.element, ColourFromSys(ei.nIndex)) || changed;\n\t}\n\tif (changed) {\n\t\tRedraw();\n\t}\n}\n\nbool ScintillaWin::PaintContains(PRectangle rc) {\n\tif (paintState == PaintState::painting) {\n\t\treturn BoundsContains(rcPaint, hRgnUpdate, rc);\n\t}\n\treturn true;\n}\n\nvoid ScintillaWin::ScrollText(Sci::Line /* linesToMove */) {\n\t//Platform::DebugPrintf(\"ScintillaWin::ScrollText %d\\n\", linesToMove);\n\t//::ScrollWindow(MainHWND(), 0,\n\t//\tvs.lineHeight * linesToMove, 0, 0);\n\t//::UpdateWindow(MainHWND());\n\tRedraw();\n\tUpdateSystemCaret();\n}\n\nvoid ScintillaWin::NotifyCaretMove() {\n\tNotifyWinEvent(EVENT_OBJECT_LOCATIONCHANGE, MainHWND(), OBJID_CARET, CHILDID_SELF);\n}\n\nvoid ScintillaWin::UpdateSystemCaret() {\n\tif (hasFocus) {\n\t\tif (pdoc->TentativeActive()) {\n\t\t\t// ongoing inline mode IME composition, don't inform IME of system caret position.\n\t\t\t// fix candidate window for Google Japanese IME moved on typing on Win7.\n\t\t\treturn;\n\t\t}\n\t\tif (HasCaretSizeChanged()) {\n\t\t\tDestroySystemCaret();\n\t\t\tCreateSystemCaret();\n\t\t}\n\t\tconst Point pos = PointMainCaret();\n\t\t::SetCaretPos(static_cast<int>(pos.x), static_cast<int>(pos.y));\n\t}\n}\n\nbool ScintillaWin::IsVisible() const noexcept {\n\treturn GetWindowStyle(MainHWND()) & WS_VISIBLE;\n}\n\nint ScintillaWin::SetScrollInfo(int nBar, LPCSCROLLINFO lpsi, BOOL bRedraw) noexcept {\n\treturn ::SetScrollInfo(MainHWND(), nBar, lpsi, bRedraw);\n}\n\nbool ScintillaWin::GetScrollInfo(int nBar, LPSCROLLINFO lpsi) noexcept {\n\treturn ::GetScrollInfo(MainHWND(), nBar, lpsi);\n}\n\n// Change the scroll position but avoid repaint if changing to same value\nvoid ScintillaWin::ChangeScrollPos(int barType, Sci::Position pos) {\n\tif (!IsVisible()) {\n\t\treturn;\n\t}\n\n\tSCROLLINFO sci = {\n\t\tsizeof(sci), 0, 0, 0, 0, 0, 0\n\t};\n\tsci.fMask = SIF_POS;\n\tGetScrollInfo(barType, &sci);\n\tif (sci.nPos != pos) {\n\t\tDwellEnd(true);\n\t\tsci.nPos = static_cast<int>(pos);\n\t\tSetScrollInfo(barType, &sci, TRUE);\n\t}\n}\n\nvoid ScintillaWin::SetVerticalScrollPos() {\n\tEditor::SetVerticalScrollPos();\n\tChangeScrollPos(SB_VERT, topLine);\n}\n\nvoid ScintillaWin::SetHorizontalScrollPos() {\n\tChangeScrollPos(SB_HORZ, xOffset);\n}\n\nbool ScintillaWin::ChangeScrollRange(int nBar, int nMin, int nMax, UINT nPage) noexcept {\n\tSCROLLINFO sci = { sizeof(sci), SIF_PAGE | SIF_RANGE, 0, 0, 0, 0, 0 };\n\tGetScrollInfo(nBar, &sci);\n\tif ((sci.nMin != nMin) || (sci.nMax != nMax) ||\t(sci.nPage != nPage)) {\n\t\tsci.nMin = nMin;\n\t\tsci.nMax = nMax;\n\t\tsci.nPage = nPage;\n\t\tSetScrollInfo(nBar, &sci, TRUE);\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nvoid ScintillaWin::HorizontalScrollToClamped(int xPos) {\n\tconst HorizontalScrollRange range = GetHorizontalScrollRange();\n\tHorizontalScrollTo(std::clamp(xPos, 0, range.documentWidth - range.pageWidth + 1));\n}\n\nHorizontalScrollRange ScintillaWin::GetHorizontalScrollRange() const {\n\tconst PRectangle rcText = GetTextRectangle();\n\tint pageWidth = static_cast<int>(rcText.Width());\n\tconst int horizEndPreferred = std::max({ scrollWidth, pageWidth - 1, 0 });\n\tif (!horizontalScrollBarVisible || Wrapping())\n\t\tpageWidth = horizEndPreferred + 1;\n\treturn { pageWidth, horizEndPreferred };\n}\n\nbool ScintillaWin::ModifyScrollBars(Sci::Line nMax, Sci::Line nPage) {\n\tif (!IsVisible()) {\n\t\treturn false;\n\t}\n\n\tbool modified = false;\n\tconst Sci::Line vertEndPreferred = nMax;\n\tif (!verticalScrollBarVisible)\n\t\tnPage = vertEndPreferred + 1;\n\tif (ChangeScrollRange(SB_VERT, 0, static_cast<int>(vertEndPreferred), static_cast<unsigned int>(nPage))) {\n\t\tmodified = true;\n\t}\n\tconst HorizontalScrollRange range = GetHorizontalScrollRange();\n\tif (ChangeScrollRange(SB_HORZ, 0, range.documentWidth, range.pageWidth)) {\n\t\tmodified = true;\n\t\tif (scrollWidth < range.pageWidth) {\n\t\t\tHorizontalScrollTo(0);\n\t\t}\n\t}\n\n\treturn modified;\n}\n\nvoid ScintillaWin::NotifyChange() {\n\t//Au: don't need it. Also, WinForms would reflect it.\n\t//::SendMessage(::GetParent(MainHWND()), WM_COMMAND,\n\t//        MAKEWPARAM(GetCtrlID(), FocusChange::Change),\n\t//\treinterpret_cast<LPARAM>(MainHWND()));\n}\n\nvoid ScintillaWin::NotifyFocus(bool focus) {\n\t//Au: don't need it. Also, WinForms would reflect it.\n\t//if (commandEvents) {\n\t//\t::SendMessage(::GetParent(MainHWND()), WM_COMMAND,\n\t//\t\tMAKEWPARAM(GetCtrlID(), focus ? FocusChange::Setfocus : FocusChange::Killfocus),\n\t//\t\treinterpret_cast<LPARAM>(MainHWND()));\n\t//}\n\tEditor::NotifyFocus(focus);\n}\n\nvoid ScintillaWin::SetCtrlID(int identifier) {\n\t::SetWindowID(HwndFromWindow(wMain), identifier);\n}\n\nint ScintillaWin::GetCtrlID() {\n\treturn ::GetDlgCtrlID(HwndFromWindow(wMain));\n}\n\nvoid ScintillaWin::NotifyParent(NotificationData scn) {\n\tscn.nmhdr.hwndFrom = MainHWND();\n\tscn.nmhdr.idFrom = GetCtrlID();\n\tif(cbNotify) (cbNotify)(cbNotifyParam, scn); else //Au: can use callback function instead of WM_NOTIFY.\n\t::SendMessage(::GetParent(MainHWND()), WM_NOTIFY,\n\t              GetCtrlID(), reinterpret_cast<LPARAM>(&scn));\n}\n\nvoid ScintillaWin::NotifyDoubleClick(Point pt, KeyMod modifiers) {\n\t//Platform::DebugPrintf(\"ScintillaWin Double click 0\\n\");\n\tScintillaBase::NotifyDoubleClick(pt, modifiers);\n\t// Send myself a WM_LBUTTONDBLCLK, so the container can handle it too.\n\tconst POINT point = POINTFromPoint(pt);\n\t::SendMessage(MainHWND(),\n\t\t\t  WM_LBUTTONDBLCLK,\n\t\t\t  FlagSet(modifiers, KeyMod::Shift) ? MK_SHIFT : 0,\n\t\t\t  MAKELPARAM(point.x, point.y));\n}\n\nnamespace {\n\nconstexpr unsigned int safeFoldingSize = 20;\nconstexpr int highByteFirst = 0x80;\nconstexpr int highByteLast = 0xFF;\nconstexpr int minTrailByte = 0x30;\n\n// CreateFoldMap creates a fold map by calling platform APIs so will differ between platforms.\nvoid CreateFoldMap(int codePage, FoldMap *foldingMap) {\n\tfor (unsigned char byte1 = highByteLast; byte1 >= highByteFirst; byte1--) {\n\t\tconst char ch1 = byte1;\n\t\tif (DBCSIsLeadByte(codePage, ch1)) {\n\t\t\tfor (unsigned char byte2 = highByteLast; byte2 >= minTrailByte; byte2--) {\n\t\t\t\tconst char ch2 = byte2;\n\t\t\t\tif (DBCSIsTrailByte(codePage, ch2)) {\n\t\t\t\t\tconst DBCSPair pair{ ch1, ch2 };\n\t\t\t\t\tconst uint16_t index = DBCSIndex(ch1, ch2);\n\t\t\t\t\t(*foldingMap)[index] = pair;\n\t\t\t\t\tconst std::string_view svBytes(pair.chars, 2);\n\t\t\t\t\tconst int lenUni = WideCharLenFromMultiByte(codePage, svBytes);\n\t\t\t\t\tif (lenUni == 1) {\n\t\t\t\t\t\t// DBCS pair must produce a single Unicode BMP code point\n\t\t\t\t\t\twchar_t codePoint = 0;\n\t\t\t\t\t\tWideCharFromMultiByte(codePage, svBytes, &codePoint, 1);\n\t\t\t\t\t\tif (codePoint) {\n\t\t\t\t\t\t\t// Could create a DBCS -> Unicode conversion map here\n\t\t\t\t\t\t\tconst char *foldedUTF8 = CaseConvert(codePoint, CaseConversion::fold);\n\t\t\t\t\t\t\tif (foldedUTF8) {\n\t\t\t\t\t\t\t\twchar_t wFolded[safeFoldingSize]{};\n\t\t\t\t\t\t\t\tconst size_t charsConverted = UTF16FromUTF8(foldedUTF8, wFolded, std::size(wFolded));\n\t\t\t\t\t\t\t\tchar back[safeFoldingSize]{};\n\t\t\t\t\t\t\t\tconst int lengthBack = MultiByteFromWideChar(codePage, std::wstring(wFolded, charsConverted),\n\t\t\t\t\t\t\t\t\tback, std::size(back));\n\t\t\t\t\t\t\t\tif (lengthBack == 2) {\n\t\t\t\t\t\t\t\t\t// Only allow cases where input length and folded length are both 2\n\t\t\t\t\t\t\t\t\t(*foldingMap)[index] = { back[0], back[1] };\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nclass CaseFolderDBCS : public CaseFolderTable {\n\t// Allocate the expandable storage here so that it does not need to be reallocated\n\t// for each call to Fold.\n\tconst FoldMap *foldingMap;\n\tUINT cp;\npublic:\n\texplicit CaseFolderDBCS(UINT cp_) : cp(cp_) {\n\t\tif (!DBCSHasFoldMap(cp)) {\n\t\t\tCreateFoldMap(cp, DBCSGetMutableFoldMap(cp));\n\t\t}\n\t\tfoldingMap = DBCSGetFoldMap(cp);\n\t}\n\tsize_t Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) override;\n};\n\nsize_t CaseFolderDBCS::Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) {\n\t// This loop outputs the same length as input as for each char 1-byte -> 1-byte; 2-byte -> 2-byte\n\tsize_t lenOut = 0;\n\tfor (size_t i = 0; i < lenMixed; i++) {\n\t\tconst ptrdiff_t lenLeft = lenMixed - i;\n\t\tconst char ch = mixed[i];\n\t\tif ((lenLeft >= 2) && DBCSIsLeadByte(cp, ch) && ((lenOut + 2) <= sizeFolded)) {\n\t\t\ti++;\n\t\t\tconst char ch2 = mixed[i];\n\t\t\tconst uint16_t ind = DBCSIndex(ch, ch2);\n\t\t\tconst char *pair = foldingMap->at(ind).chars;\n\t\t\tfolded[lenOut++] = pair[0];\n\t\t\tfolded[lenOut++] = pair[1];\n\t\t} else if ((lenOut + 1) <= sizeFolded) {\n\t\t\tconst unsigned char uch = ch;\n\t\t\tfolded[lenOut++] = mapping[uch];\n\t\t}\n\t}\n\treturn lenOut;\n}\n\n}\n\nstd::unique_ptr<CaseFolder> ScintillaWin::CaseFolderForEncoding() {\n\tconst UINT cpDest = CodePageOfDocument();\n\tif (cpDest == CpUtf8) {\n\t\treturn std::make_unique<CaseFolderUnicode>();\n\t}\n\tif (pdoc->dbcsCodePage) {\n\t\treturn std::make_unique<CaseFolderDBCS>(cpDest);\n\t}\n\tstd::unique_ptr<CaseFolderTable> pcf = std::make_unique<CaseFolderTable>();\n\t// Only for single byte encodings\n\tfor (int i=highByteFirst; i<=highByteLast; i++) {\n\t\tchar sCharacter[2] = \"A\";\n\t\tsCharacter[0] = static_cast<char>(i);\n\t\twchar_t wCharacter[safeFoldingSize];\n\t\tconst unsigned int lengthUTF16 = WideCharFromMultiByte(cpDest, sCharacter,\n\t\t\twCharacter, std::size(wCharacter));\n\t\tif (lengthUTF16 == 1) {\n\t\t\tconst char *caseFolded = CaseConvert(wCharacter[0], CaseConversion::fold);\n\t\t\tif (caseFolded) {\n\t\t\t\twchar_t wLower[safeFoldingSize];\n\t\t\t\tconst size_t charsConverted = UTF16FromUTF8(std::string_view(caseFolded),\n\t\t\t\t\twLower, std::size(wLower));\n\t\t\t\tif (charsConverted == 1) {\n\t\t\t\t\tchar sCharacterLowered[safeFoldingSize];\n\t\t\t\t\tconst unsigned int lengthConverted = MultiByteFromWideChar(cpDest,\n\t\t\t\t\t\tstd::wstring_view(wLower, charsConverted),\n\t\t\t\t\t\tsCharacterLowered, std::size(sCharacterLowered));\n\t\t\t\t\tif ((lengthConverted == 1) && (sCharacter[0] != sCharacterLowered[0])) {\n\t\t\t\t\t\tpcf->SetTranslation(sCharacter[0], sCharacterLowered[0]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn pcf;\n}\n\nstd::string ScintillaWin::CaseMapString(const std::string &s, CaseMapping caseMapping) {\n\tif (s.empty() || (caseMapping == CaseMapping::same))\n\t\treturn s;\n\n\tconst UINT cpDoc = CodePageOfDocument();\n\tif (cpDoc == CpUtf8) {\n\t\treturn CaseConvertString(s, (caseMapping == CaseMapping::upper) ? CaseConversion::upper : CaseConversion::lower);\n\t}\n\n\t// Change text to UTF-16\n\tconst std::wstring wsText = StringDecode(s, cpDoc);\n\n\tconst DWORD mapFlags = LCMAP_LINGUISTIC_CASING |\n\t\t((caseMapping == CaseMapping::upper) ? LCMAP_UPPERCASE : LCMAP_LOWERCASE);\n\n\t// Change case\n\tconst std::wstring wsConverted = StringMapCase(wsText, mapFlags);\n\n\t// Change back to document encoding\n\tstd::string sConverted = StringEncode(wsConverted, cpDoc);\n\n\treturn sConverted;\n}\n\nvoid ScintillaWin::Copy() {\n\t//Platform::DebugPrintf(\"Copy\\n\");\n\tif (!sel.Empty()) {\n\t\tSelectionText selectedText;\n\t\tCopySelectionRange(&selectedText);\n\t\tCopyToClipboard(selectedText);\n\t}\n}\n\nbool ScintillaWin::CanPaste() {\n\tif (!Editor::CanPaste())\n\t\treturn false;\n\treturn ::IsClipboardFormatAvailable(CF_UNICODETEXT) != FALSE;\n}\n\nnamespace {\n\nclass GlobalMemory {\n\tHGLOBAL hand {};\npublic:\n\tvoid *ptr {};\n\tGlobalMemory() noexcept = default;\n\texplicit GlobalMemory(HGLOBAL hand_) noexcept : hand(hand_) {\n\t\tif (hand) {\n\t\t\tptr = ::GlobalLock(hand);\n\t\t}\n\t}\n\t// Deleted so GlobalMemory objects can not be copied.\n\tGlobalMemory(const GlobalMemory &) = delete;\n\tGlobalMemory(GlobalMemory &&) = delete;\n\tGlobalMemory &operator=(const GlobalMemory &) = delete;\n\tGlobalMemory &operator=(GlobalMemory &&) = delete;\n\t~GlobalMemory() {\n\t\tassert(!ptr);\n\t\tassert(!hand);\n\t}\n\tvoid Allocate(size_t bytes) noexcept {\n\t\tassert(!hand);\n\t\thand = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, bytes);\n\t\tif (hand) {\n\t\t\tptr = ::GlobalLock(hand);\n\t\t}\n\t}\n\tHGLOBAL Unlock() noexcept {\n\t\tassert(ptr);\n\t\tHGLOBAL handCopy = hand;\n\t\t::GlobalUnlock(hand);\n\t\tptr = nullptr;\n\t\thand = {};\n\t\treturn handCopy;\n\t}\n\tvoid SetClip(UINT uFormat) noexcept {\n\t\t::SetClipboardData(uFormat, Unlock());\n\t}\n\texplicit operator bool() const noexcept {\n\t\treturn ptr != nullptr;\n\t}\n\t[[nodiscard]] SIZE_T Size() const noexcept {\n\t\treturn ::GlobalSize(hand);\n\t}\n};\n\n// OpenClipboard may fail if another application has opened the clipboard.\n// Try up to 8 times, with an initial delay of 1 ms and an exponential back off\n// for a maximum total delay of 127 ms (1+2+4+8+16+32+64).\nbool OpenClipboardRetry(HWND hwnd) noexcept {\n\tconstexpr int attempts = 8;\n\tfor (int attempt=0; attempt<attempts; attempt++) {\n\t\tif (attempt > 0) {\n\t\t\t::Sleep(1 << (attempt-1));\n\t\t}\n\t\tif (::OpenClipboard(hwnd)) {\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\n// Ensure every successful OpenClipboard is followed by a CloseClipboard.\nclass Clipboard {\n\tbool opened = false;\npublic:\n\texplicit Clipboard(HWND hwnd) noexcept : opened(::OpenClipboardRetry(hwnd)) {\n\t}\n\t// Deleted so Clipboard objects can not be copied.\n\tClipboard(const Clipboard &) = delete;\n\tClipboard(Clipboard &&) = delete;\n\tClipboard &operator=(const Clipboard &) = delete;\n\tClipboard &operator=(Clipboard &&) = delete;\n\t~Clipboard() noexcept {\n\t\tif (opened) {\n\t\t\t::CloseClipboard();\n\t\t}\n\t}\n\texplicit constexpr operator bool() const noexcept {\n\t\treturn opened;\n\t}\n};\n\nbool IsValidFormatEtc(const FORMATETC *pFE) noexcept {\n\treturn pFE->ptd == nullptr &&\n\t\t(pFE->dwAspect & DVASPECT_CONTENT) != 0 &&\n\t\tpFE->lindex == -1 &&\n\t\t(pFE->tymed & TYMED_HGLOBAL) != 0;\n}\n\nbool SupportedFormat(const FORMATETC *pFE) noexcept {\n\treturn pFE->cfFormat == CF_UNICODETEXT &&\n\t\tIsValidFormatEtc(pFE);\n}\n\n}\n\nvoid ScintillaWin::Paste() {\n\tClipboard clipboard(MainHWND());\n\tif (!clipboard) {\n\t\treturn;\n\t}\n\tUndoGroup ug(pdoc);\n\tconst bool isLine = SelectionEmpty() &&\n\t\t(::IsClipboardFormatAvailable(cfLineSelect) || ::IsClipboardFormatAvailable(cfVSLineTag));\n\tClearSelection(multiPasteMode == MultiPaste::Each);\n\tbool isRectangular = (::IsClipboardFormatAvailable(cfColumnSelect) != 0);\n\n\tif (!isRectangular) {\n\t\t// Evaluate \"Borland IDE Block Type\" explicitly\n\t\tGlobalMemory memBorlandSelection(::GetClipboardData(cfBorlandIDEBlockType));\n\t\tif (memBorlandSelection) {\n\t\t\tisRectangular = (memBorlandSelection.Size() == 1) && (static_cast<BYTE *>(memBorlandSelection.ptr)[0] == 0x02);\n\t\t\tmemBorlandSelection.Unlock();\n\t\t}\n\t}\n\tconst PasteShape pasteShape = isRectangular ? PasteShape::rectangular : (isLine ? PasteShape::line : PasteShape::stream);\n\n\t// Use CF_UNICODETEXT if available\n\tGlobalMemory memUSelection(::GetClipboardData(CF_UNICODETEXT));\n\tif (const wchar_t *uptr = static_cast<const wchar_t *>(memUSelection.ptr)) {\n\t\tconst std::string putf = EncodeWString(uptr);\n\t\tInsertPasteShape(putf.c_str(), putf.length(), pasteShape);\n\t\tmemUSelection.Unlock();\n\t}\n\tRedraw();\n}\n\nvoid ScintillaWin::CreateCallTipWindow(PRectangle) {\n\tif (!ct.wCallTip.Created()) {\n\t\tHWND wnd = ::CreateWindow(callClassName, TEXT(\"ACallTip\"),\n\t\t\t\t\t     WS_POPUP, 100, 100, 150, 20,\n\t\t\t\t\t     MainHWND(), {},\n\t\t\t\t\t     GetWindowInstance(MainHWND()),\n\t\t\t\t\t     this);\n\t\tct.wCallTip = wnd;\n\t\tct.wDraw = wnd;\n\t}\n}\n\nvoid ScintillaWin::AddToPopUp(const char *label, int cmd, bool enabled) {\n\tHMENU hmenuPopup = static_cast<HMENU>(popup.GetID());\n\tif (!label[0])\n\t\t::AppendMenuA(hmenuPopup, MF_SEPARATOR, 0, \"\");\n\telse if (enabled)\n\t\t::AppendMenuA(hmenuPopup, MF_STRING, cmd, label);\n\telse\n\t\t::AppendMenuA(hmenuPopup, MF_STRING | MF_DISABLED | MF_GRAYED, cmd, label);\n}\n\nvoid ScintillaWin::ClaimSelection() {\n\t// Windows does not have a primary selection\n}\n\n/// Implement IUnknown\nSTDMETHODIMP FormatEnumerator::QueryInterface(REFIID riid, PVOID *ppv) {\n\t//Platform::DebugPrintf(\"EFE QI\");\n\t*ppv = nullptr;\n\tif (riid == IID_IUnknown || riid == IID_IEnumFORMATETC) {\n\t\t*ppv = this;\n\t} else {\n\t\treturn E_NOINTERFACE;\n\t}\n\tAddRef();\n\treturn S_OK;\n}\nSTDMETHODIMP_(ULONG)FormatEnumerator::AddRef() {\n\treturn ++ref;\n}\nSTDMETHODIMP_(ULONG)FormatEnumerator::Release() {\n\tconst ULONG refs = --ref;\n\tif (refs == 0) {\n\t\tdelete this;\n\t}\n\treturn refs;\n}\n/// Implement IEnumFORMATETC\nSTDMETHODIMP FormatEnumerator::Next(ULONG celt, FORMATETC *rgelt, ULONG *pceltFetched) {\n\tif (!rgelt) return E_POINTER;\n\tULONG putPos = 0;\n\twhile ((pos < formats.size()) && (putPos < celt)) {\n\t\trgelt->cfFormat = formats[pos];\n\t\trgelt->ptd = nullptr;\n\t\trgelt->dwAspect = DVASPECT_CONTENT;\n\t\trgelt->lindex = -1;\n\t\trgelt->tymed = TYMED_HGLOBAL;\n\t\trgelt++;\n\t\tpos++;\n\t\tputPos++;\n\t}\n\tif (pceltFetched)\n\t\t*pceltFetched = putPos;\n\treturn putPos ? S_OK : S_FALSE;\n}\nSTDMETHODIMP FormatEnumerator::Skip(ULONG celt) {\n\tpos += celt;\n\treturn S_OK;\n}\nSTDMETHODIMP FormatEnumerator::Reset() {\n\tpos = 0;\n\treturn S_OK;\n}\nSTDMETHODIMP FormatEnumerator::Clone(IEnumFORMATETC **ppenum) {\n\tFormatEnumerator *pfe;\n\ttry {\n\t\tpfe = new FormatEnumerator(pos, formats.data(), formats.size());\n\t} catch (...) {\n\t\treturn E_OUTOFMEMORY;\n\t}\n\treturn pfe->QueryInterface(IID_IEnumFORMATETC, reinterpret_cast<void **>(ppenum));\n}\n\nFormatEnumerator::FormatEnumerator(ULONG pos_, const CLIPFORMAT formats_[], size_t formatsLen_) {\n\tref = 0;   // First QI adds first reference...\n\tpos = pos_;\n\tformats.insert(formats.begin(), formats_, formats_+formatsLen_);\n}\n\n/// Implement IUnknown\nSTDMETHODIMP DropSource::QueryInterface(REFIID riid, PVOID *ppv) {\n\treturn sci->QueryInterface(riid, ppv);\n}\nSTDMETHODIMP_(ULONG)DropSource::AddRef() {\n\treturn sci->AddRef();\n}\nSTDMETHODIMP_(ULONG)DropSource::Release() {\n\treturn sci->Release();\n}\n\n/// Implement IDropSource\nSTDMETHODIMP DropSource::QueryContinueDrag(BOOL fEsc, DWORD grfKeyState) {\n\tif (fEsc)\n\t\treturn DRAGDROP_S_CANCEL;\n\tif (!(grfKeyState & MK_LBUTTON))\n\t\treturn DRAGDROP_S_DROP;\n\treturn S_OK;\n}\n\nSTDMETHODIMP DropSource::GiveFeedback(DWORD) {\n\treturn DRAGDROP_S_USEDEFAULTCURSORS;\n}\n\n/// Implement IUnkown\nSTDMETHODIMP DataObject::QueryInterface(REFIID riid, PVOID *ppv) {\n\t//Platform::DebugPrintf(\"DO QI %p\\n\", this);\n\treturn sci->QueryInterface(riid, ppv);\n}\nSTDMETHODIMP_(ULONG)DataObject::AddRef() {\n\treturn sci->AddRef();\n}\nSTDMETHODIMP_(ULONG)DataObject::Release() {\n\treturn sci->Release();\n}\n/// Implement IDataObject\nSTDMETHODIMP DataObject::GetData(FORMATETC *pFEIn, STGMEDIUM *pSTM) {\n\treturn sci->GetData(pFEIn, pSTM);\n}\n\nSTDMETHODIMP DataObject::GetDataHere(FORMATETC *, STGMEDIUM *) {\n\t//Platform::DebugPrintf(\"DOB GetDataHere\\n\");\n\treturn E_NOTIMPL;\n}\n\nSTDMETHODIMP DataObject::QueryGetData(FORMATETC *pFE) {\n\tif (sci->DragIsRectangularOK(pFE->cfFormat) && IsValidFormatEtc(pFE)) {\n\t\treturn S_OK;\n\t}\n\n\treturn SupportedFormat(pFE) ? S_OK : S_FALSE;\n}\n\nSTDMETHODIMP DataObject::GetCanonicalFormatEtc(FORMATETC *, FORMATETC *pFEOut) {\n\t//Platform::DebugPrintf(\"DOB GetCanon\\n\");\n\tpFEOut->cfFormat = CF_UNICODETEXT;\n\tpFEOut->ptd = nullptr;\n\tpFEOut->dwAspect = DVASPECT_CONTENT;\n\tpFEOut->lindex = -1;\n\tpFEOut->tymed = TYMED_HGLOBAL;\n\treturn S_OK;\n}\n\nSTDMETHODIMP DataObject::SetData(FORMATETC *, STGMEDIUM *, BOOL) {\n\t//Platform::DebugPrintf(\"DOB SetData\\n\");\n\treturn E_FAIL;\n}\n\nSTDMETHODIMP DataObject::EnumFormatEtc(DWORD dwDirection, IEnumFORMATETC **ppEnum) {\n\ttry {\n\t\t//Platform::DebugPrintf(\"DOB EnumFormatEtc %d\\n\", dwDirection);\n\t\tif (dwDirection != DATADIR_GET) {\n\t\t\t*ppEnum = nullptr;\n\t\t\treturn E_FAIL;\n\t\t}\n\n\t\tconst CLIPFORMAT formats[] = {CF_UNICODETEXT};\n\t\tFormatEnumerator *pfe = new FormatEnumerator(0, formats, std::size(formats));\n\t\treturn pfe->QueryInterface(IID_IEnumFORMATETC, reinterpret_cast<void **>(ppEnum));\n\t} catch (std::bad_alloc &) {\n\t\tsci->errorStatus = Status::BadAlloc;\n\t\treturn E_OUTOFMEMORY;\n\t} catch (...) {\n\t\tsci->errorStatus = Status::Failure;\n\t\treturn E_FAIL;\n\t}\n}\n\nSTDMETHODIMP DataObject::DAdvise(FORMATETC *, DWORD, IAdviseSink *, PDWORD) {\n\t//Platform::DebugPrintf(\"DOB DAdvise\\n\");\n\treturn E_FAIL;\n}\n\nSTDMETHODIMP DataObject::DUnadvise(DWORD) {\n\t//Platform::DebugPrintf(\"DOB DUnadvise\\n\");\n\treturn E_FAIL;\n}\n\nSTDMETHODIMP DataObject::EnumDAdvise(IEnumSTATDATA **) {\n\t//Platform::DebugPrintf(\"DOB EnumDAdvise\\n\");\n\treturn E_FAIL;\n}\n\n/// Implement IUnknown\nSTDMETHODIMP DropTarget::QueryInterface(REFIID riid, PVOID *ppv) {\n\t//Platform::DebugPrintf(\"DT QI %p\\n\", this);\n\treturn sci->QueryInterface(riid, ppv);\n}\nSTDMETHODIMP_(ULONG)DropTarget::AddRef() {\n\treturn sci->AddRef();\n}\nSTDMETHODIMP_(ULONG)DropTarget::Release() {\n\treturn sci->Release();\n}\n\n/// Implement IDropTarget by forwarding to Scintilla\nSTDMETHODIMP DropTarget::DragEnter(LPDATAOBJECT pIDataSource, DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) {\n\ttry {\n\t\treturn sci->DragEnter(pIDataSource, grfKeyState, pt, pdwEffect);\n\t} catch (...) {\n\t\tsci->errorStatus = Status::Failure;\n\t}\n\treturn E_FAIL;\n}\nSTDMETHODIMP DropTarget::DragOver(DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) {\n\ttry {\n\t\treturn sci->DragOver(grfKeyState, pt, pdwEffect);\n\t} catch (...) {\n\t\tsci->errorStatus = Status::Failure;\n\t}\n\treturn E_FAIL;\n}\nSTDMETHODIMP DropTarget::DragLeave() {\n\ttry {\n\t\treturn sci->DragLeave();\n\t} catch (...) {\n\t\tsci->errorStatus = Status::Failure;\n\t}\n\treturn E_FAIL;\n}\nSTDMETHODIMP DropTarget::Drop(LPDATAOBJECT pIDataSource, DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) {\n\ttry {\n\t\treturn sci->Drop(pIDataSource, grfKeyState, pt, pdwEffect);\n\t} catch (...) {\n\t\tsci->errorStatus = Status::Failure;\n\t}\n\treturn E_FAIL;\n}\n\n/**\n * DBCS: support Input Method Editor (IME).\n * Called when IME Window opened.\n */\nvoid ScintillaWin::ImeStartComposition() {\n\tif (caret.active) {\n\t\t// Move IME Window to current caret position\n\t\tIMContext imc(MainHWND());\n\t\tif (!imc)\n\t\t\treturn;\n\n\t\timc.SetCompositionWindow(PointMainCaret());\n\n\t\t// Set font of IME window to same as surrounded text.\n\t\tif (stylesValid) {\n\t\t\t// Since the style creation code has been made platform independent,\n\t\t\t// The logfont for the IME is recreated here.\n\t\t\timc.SetCompositionFont(vs, pdoc->StyleIndexAt(sel.MainCaret()), dpi);\n\t\t}\n\t\t// Caret is displayed in IME window. So, caret in Scintilla is useless.\n\t\tDropCaret();\n\t}\n}\n\n/** Called when IME Window closed. */\nvoid ScintillaWin::ImeEndComposition() {\n\t// clear IME composition state.\n\tview.imeCaretBlockOverride = false;\n\tpdoc->TentativeUndo();\n\tShowCaretAtCurrentPosition();\n}\n\nLRESULT ScintillaWin::ImeOnReconvert(LPARAM lParam) {\n\t// Reconversion on windows limits within one line without eol.\n\t// Look around:   baseStart  <--  (|mainStart|  -- mainEnd)  --> baseEnd.\n\tconst Sci::Position mainStart = sel.RangeMain().Start().Position();\n\tconst Sci::Position mainEnd = sel.RangeMain().End().Position();\n\tconst Sci::Line curLine = pdoc->SciLineFromPosition(mainStart);\n\tif (curLine != pdoc->LineFromPosition(mainEnd))\n\t\treturn 0;\n\tconst Sci::Position baseStart = pdoc->LineStart(curLine);\n\tconst Sci::Position baseEnd = pdoc->LineEnd(curLine);\n\tif ((baseStart == baseEnd) || (mainEnd > baseEnd))\n\t\treturn 0;\n\n\tconst int codePage = CodePageOfDocument();\n\tconst std::wstring rcFeed = StringDecode(RangeText(baseStart, baseEnd), codePage);\n\tconst int rcFeedLen = static_cast<int>(rcFeed.length()) * sizeof(wchar_t);\n\tconst int rcSize = sizeof(RECONVERTSTRING) + rcFeedLen + sizeof(wchar_t);\n\n\tRECONVERTSTRING *rc = static_cast<RECONVERTSTRING *>(PtrFromSPtr(lParam));\n\tif (!rc)\n\t\treturn rcSize; // Immediately be back with rcSize of memory block.\n\n\twchar_t *rcFeedStart = reinterpret_cast<wchar_t*>(rc + 1);\n\tmemcpy(rcFeedStart, rcFeed.data(), rcFeedLen);\n\n\tstd::string rcCompString = RangeText(mainStart, mainEnd);\n\tstd::wstring rcCompWstring = StringDecode(rcCompString, codePage);\n\tstd::string rcCompStart = RangeText(baseStart, mainStart);\n\tstd::wstring rcCompWstart = StringDecode(rcCompStart, codePage);\n\n\t// Map selection to dwCompStr.\n\t// No selection assumes current caret as rcCompString without length.\n\trc->dwVersion = 0; // It should be absolutely 0.\n\trc->dwStrLen = static_cast<DWORD>(rcFeed.length());\n\trc->dwStrOffset = sizeof(RECONVERTSTRING);\n\trc->dwCompStrLen = static_cast<DWORD>(rcCompWstring.length());\n\trc->dwCompStrOffset = static_cast<DWORD>(rcCompWstart.length()) * sizeof(wchar_t);\n\trc->dwTargetStrLen = rc->dwCompStrLen;\n\trc->dwTargetStrOffset =rc->dwCompStrOffset;\n\n\tIMContext imc(MainHWND());\n\tif (!imc)\n\t\treturn 0;\n\n\tif (!imc.SetCompositionString(SCS_QUERYRECONVERTSTRING, rc, rcSize))\n\t\treturn 0;\n\n\t// No selection asks IME to fill target fields with its own value.\n\tconst size_t tgWlen = rc->dwTargetStrLen;\n\tconst size_t tgWstart = rc->dwTargetStrOffset / sizeof(wchar_t);\n\n\tstd::string tgCompStart = StringEncode(rcFeed.substr(0, tgWstart), codePage);\n\tstd::string tgComp = StringEncode(rcFeed.substr(tgWstart, tgWlen), codePage);\n\n\t// No selection needs to adjust reconvert start position for IME set.\n\tconst int adjust = static_cast<int>(tgCompStart.length() - rcCompStart.length());\n\tconst int docCompLen = static_cast<int>(tgComp.length());\n\n\t// Make place for next composition string to sit in.\n\tfor (size_t r=0; r<sel.Count(); r++) {\n\t\tconst Sci::Position rBase = sel.Range(r).Start().Position();\n\t\tconst Sci::Position docCompStart = rBase + adjust;\n\n\t\tif (inOverstrike) { // the docCompLen of bytes will be overstriked.\n\t\t\tsel.Range(r) = SelectionRange(docCompStart);\n\t\t} else {\n\t\t\t// Ensure docCompStart+docCompLen be not beyond lineEnd.\n\t\t\t// since docCompLen by byte might break eol.\n\t\t\tconst Sci::Position lineEnd = pdoc->LineEndPosition(rBase);\n\t\t\tconst Sci::Position overflow = (docCompStart + docCompLen) - lineEnd;\n\t\t\tif (overflow > 0) {\n\t\t\t\tpdoc->DeleteChars(docCompStart, docCompLen - overflow);\n\t\t\t} else {\n\t\t\t\tpdoc->DeleteChars(docCompStart, docCompLen);\n\t\t\t}\n\t\t}\n\t}\n\t// Immediately Target Input or candidate box choice with GCS_COMPSTR.\n\treturn rcSize;\n}\n\nLRESULT ScintillaWin::ImeOnDocumentFeed(LPARAM lParam) const {\n\t// This is called while typing preedit string in.\n\t// So there is no selection.\n\t// Limit feed within one line without EOL.\n\t// Look around:   lineStart |<--  |compStart| - caret - compEnd|  -->| lineEnd.\n\n\tconst Sci::Position curPos = CurrentPosition();\n\tconst Sci::Line curLine = pdoc->SciLineFromPosition(curPos);\n\tconst Sci::Position lineStart = pdoc->LineStart(curLine);\n\tconst Sci::Position lineEnd = pdoc->LineEnd(curLine);\n\n\tconst std::wstring rcFeed = StringDecode(RangeText(lineStart, lineEnd), CodePageOfDocument());\n\tconst int rcFeedLen = static_cast<int>(rcFeed.length()) * sizeof(wchar_t);\n\tconst int rcSize = sizeof(RECONVERTSTRING) + rcFeedLen + sizeof(wchar_t);\n\n\tRECONVERTSTRING *rc = static_cast<RECONVERTSTRING *>(PtrFromSPtr(lParam));\n\tif (!rc)\n\t\treturn rcSize;\n\n\twchar_t *rcFeedStart = reinterpret_cast<wchar_t*>(rc + 1);\n\tmemcpy(rcFeedStart, rcFeed.data(), rcFeedLen);\n\n\tIMContext imc(MainHWND());\n\tif (!imc)\n\t\treturn 0;\n\n\tDWORD compStrLen = 0;\n\tSci::Position compStart = curPos;\n\tif (pdoc->TentativeActive()) {\n\t\t// rcFeed contains current composition string\n\t\tcompStrLen = imc.GetCompositionStringLength(GCS_COMPSTR);\n\t\tconst int imeCaretPos = imc.GetImeCaretPos();\n\t\tcompStart = pdoc->GetRelativePositionUTF16(curPos, -imeCaretPos);\n\t}\n\tconst Sci::Position compStrOffset = pdoc->CountUTF16(lineStart, compStart);\n\n\t// Fill in reconvert structure.\n\t// Let IME to decide what the target is.\n\trc->dwVersion = 0; //constant\n\trc->dwStrLen = static_cast<DWORD>(rcFeed.length());\n\trc->dwStrOffset = sizeof(RECONVERTSTRING); //constant\n\trc->dwCompStrLen = compStrLen;\n\trc->dwCompStrOffset = static_cast<DWORD>(compStrOffset) * sizeof(wchar_t);\n\trc->dwTargetStrLen = rc->dwCompStrLen;\n\trc->dwTargetStrOffset = rc->dwCompStrOffset;\n\n\treturn rcSize; // MS API says reconv structure to be returned.\n}\n\nvoid ScintillaWin::GetMouseParameters() noexcept {\n\t// mouse pointer size and colour may changed\n\treverseArrowCursor.Invalidate();\n\t// This retrieves the number of lines per scroll as configured in the Mouse Properties sheet in Control Panel\n\t::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &linesPerScroll, 0);\n\tif (!::SystemParametersInfo(SPI_GETWHEELSCROLLCHARS, 0, &charsPerScroll, 0)) {\n\t\t// no horizontal scrolling configuration on Windows XP\n\t\tcharsPerScroll = (linesPerScroll == WHEEL_PAGESCROLL) ? 3 : linesPerScroll;\n\t}\n\t::SystemParametersInfo(SPI_GETMOUSEVANISH, 0, &typingWithoutCursor, 0);\n}\n\nvoid ScintillaWin::CopyToGlobal(GlobalMemory &gmUnicode, const SelectionText &selectedText) {\n\tconst std::string_view svSelected(selectedText.Data(), selectedText.LengthWithTerminator());\n\tif (IsUnicodeMode()) {\n\t\tconst size_t uchars = UTF16Length(svSelected);\n\t\tgmUnicode.Allocate(2 * uchars);\n\t\tif (gmUnicode) {\n\t\t\tUTF16FromUTF8(svSelected,\n\t\t\t\tstatic_cast<wchar_t *>(gmUnicode.ptr), uchars);\n\t\t}\n\t} else {\n\t\t// Not Unicode mode\n\t\t// Convert to Unicode using the current Scintilla code page\n\t\tconst UINT cpSrc = CodePageFromCharSet(\n\t\t\tselectedText.characterSet, selectedText.codePage);\n\t\tconst size_t uLen = WideCharLenFromMultiByte(cpSrc, svSelected);\n\t\tgmUnicode.Allocate(2 * uLen);\n\t\tif (gmUnicode) {\n\t\t\tWideCharFromMultiByte(cpSrc, svSelected,\n\t\t\t\tstatic_cast<wchar_t *>(gmUnicode.ptr), uLen);\n\t\t}\n\t}\n}\n\nvoid ScintillaWin::CopyToClipboard(const SelectionText &selectedText) {\n\tClipboard clipboard(MainHWND());\n\tif (!clipboard) {\n\t\treturn;\n\t}\n\t::EmptyClipboard();\n\n\tGlobalMemory uniText;\n\tCopyToGlobal(uniText, selectedText);\n\tif (uniText) {\n\t\tuniText.SetClip(CF_UNICODETEXT);\n\t}\n\n\tif (selectedText.rectangular) {\n\t\t::SetClipboardData(cfColumnSelect, {});\n\n\t\tGlobalMemory borlandSelection;\n\t\tborlandSelection.Allocate(1);\n\t\tif (borlandSelection) {\n\t\t\tstatic_cast<BYTE *>(borlandSelection.ptr)[0] = 0x02;\n\t\t\tborlandSelection.SetClip(cfBorlandIDEBlockType);\n\t\t}\n\t}\n\n\tif (selectedText.lineCopy) {\n\t\t::SetClipboardData(cfLineSelect, {});\n\t\t::SetClipboardData(cfVSLineTag, {});\n\t}\n}\n\nvoid ScintillaWin::ScrollMessage(WPARAM wParam) {\n\t//DWORD dwStart = timeGetTime();\n\t//Platform::DebugPrintf(\"Scroll %x %d\\n\", wParam, lParam);\n\n\tSCROLLINFO sci = {};\n\tsci.cbSize = sizeof(sci);\n\tsci.fMask = SIF_ALL;\n\n\tGetScrollInfo(SB_VERT, &sci);\n\n\t//Platform::DebugPrintf(\"ScrollInfo %d mask=%x min=%d max=%d page=%d pos=%d track=%d\\n\", b,sci.fMask,\n\t//sci.nMin, sci.nMax, sci.nPage, sci.nPos, sci.nTrackPos);\n\tSci::Line topLineNew = topLine;\n\tswitch (LOWORD(wParam)) {\n\tcase SB_LINEUP:\n\t\ttopLineNew -= 1;\n\t\tbreak;\n\tcase SB_LINEDOWN:\n\t\ttopLineNew += 1;\n\t\tbreak;\n\tcase SB_PAGEUP:\n\t\ttopLineNew -= LinesToScroll(); break;\n\tcase SB_PAGEDOWN: topLineNew += LinesToScroll(); break;\n\tcase SB_TOP: topLineNew = 0; break;\n\tcase SB_BOTTOM: topLineNew = MaxScrollPos(); break;\n\tcase SB_THUMBPOSITION: topLineNew = sci.nTrackPos; break;\n\tcase SB_THUMBTRACK: topLineNew = sci.nTrackPos; break;\n\t}\n\tScrollTo(topLineNew);\n}\n\nvoid ScintillaWin::HorizontalScrollMessage(WPARAM wParam) {\n\tint xPos = xOffset;\n\tconst PRectangle rcText = GetTextRectangle();\n\tconst int pageWidth = static_cast<int>(rcText.Width() * 2 / 3);\n\tconstexpr int pixelsPerArrow = 20;\n\tswitch (LOWORD(wParam)) {\n\tcase SB_LINEUP:\n\t\txPos -= pixelsPerArrow;\n\t\tbreak;\n\tcase SB_LINEDOWN:\t// May move past the logical end\n\t\txPos += pixelsPerArrow;\n\t\tbreak;\n\tcase SB_PAGEUP:\n\t\txPos -= pageWidth;\n\t\tbreak;\n\tcase SB_PAGEDOWN:\n\t\txPos += pageWidth;\n\t\tbreak;\n\tcase SB_TOP:\n\t\txPos = 0;\n\t\tbreak;\n\tcase SB_BOTTOM:\n\t\txPos = scrollWidth;\n\t\tbreak;\n\tcase SB_THUMBPOSITION:\n\tcase SB_THUMBTRACK: {\n\t\t\t// Do NOT use wParam, its 16 bit and not enough for very long lines. Its still possible to overflow the 32 bit but you have to try harder =]\n\t\t\tSCROLLINFO si {};\n\t\t\tsi.cbSize = sizeof(si);\n\t\t\tsi.fMask = SIF_TRACKPOS;\n\t\t\tif (GetScrollInfo(SB_HORZ, &si)) {\n\t\t\t\txPos = si.nTrackPos;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\t}\n\tHorizontalScrollToClamped(xPos);\n}\n\n/**\n * Redraw all of text area.\n * This paint will not be abandoned.\n */\nvoid ScintillaWin::FullPaint() {\n\tif ((technology == Technology::Default) || (technology == Technology::DirectWriteDC)) {\n\t\tHDC hdc = ::GetDC(MainHWND());\n\t\tFullPaintDC(hdc);\n\t\t::ReleaseDC(MainHWND(), hdc);\n\t} else {\n\t\tFullPaintDC({});\n\t}\n}\n\n/**\n * Redraw all of text area on the specified DC.\n * This paint will not be abandoned.\n */\nvoid ScintillaWin::FullPaintDC(HDC hdc) {\n\tpaintState = PaintState::painting;\n\trcPaint = GetClientRectangle();\n\tpaintingAllText = true;\n\tPaintDC(hdc);\n\tpaintState = PaintState::notPainting;\n}\n\nnamespace {\n\nbool CompareDevCap(HDC hdc, HDC hOtherDC, int nIndex) noexcept {\n\treturn ::GetDeviceCaps(hdc, nIndex) == ::GetDeviceCaps(hOtherDC, nIndex);\n}\n\n}\n\nbool ScintillaWin::IsCompatibleDC(HDC hOtherDC) noexcept {\n\tHDC hdc = ::GetDC(MainHWND());\n\tconst bool isCompatible =\n\t\tCompareDevCap(hdc, hOtherDC, TECHNOLOGY) &&\n\t\tCompareDevCap(hdc, hOtherDC, LOGPIXELSY) &&\n\t\tCompareDevCap(hdc, hOtherDC, LOGPIXELSX) &&\n\t\tCompareDevCap(hdc, hOtherDC, BITSPIXEL) &&\n\t\tCompareDevCap(hdc, hOtherDC, PLANES);\n\t::ReleaseDC(MainHWND(), hdc);\n\treturn isCompatible;\n}\n\nDWORD ScintillaWin::EffectFromState(DWORD grfKeyState) const noexcept {\n\t// These are the Wordpad semantics.\n\tDWORD dwEffect;\n\tif (inDragDrop == DragDrop::dragging)\t// Internal defaults to move\n\t\tdwEffect = DROPEFFECT_MOVE;\n\telse\n\t\tdwEffect = DROPEFFECT_COPY;\n\tif (grfKeyState & MK_ALT)\n\t\tdwEffect = DROPEFFECT_MOVE;\n\tif (grfKeyState & MK_CONTROL)\n\t\tdwEffect = DROPEFFECT_COPY;\n\treturn dwEffect;\n}\n\n/// Implement IUnknown\nSTDMETHODIMP ScintillaWin::QueryInterface(REFIID riid, PVOID *ppv) {\n\t*ppv = nullptr;\n\tif (riid == IID_IUnknown) {\n\t\t*ppv = &dt;\n\t} else if (riid == IID_IDropSource) {\n\t\t*ppv = &ds;\n\t} else if (riid == IID_IDropTarget) {\n\t\t*ppv = &dt;\n\t} else if (riid == IID_IDataObject) {\n\t\t*ppv = &dob;\n\t}\n\tif (!*ppv)\n\t\treturn E_NOINTERFACE;\n\treturn S_OK;\n}\n\nSTDMETHODIMP_(ULONG) ScintillaWin::AddRef() {\n\treturn 1;\n}\n\nSTDMETHODIMP_(ULONG) ScintillaWin::Release() {\n\treturn 1;\n}\n\n/// Implement IDropTarget\nSTDMETHODIMP ScintillaWin::DragEnter(LPDATAOBJECT pIDataSource, DWORD grfKeyState,\n                                     POINTL, PDWORD pdwEffect) {\n\tif (!pIDataSource )\n\t\treturn E_POINTER;\n\tFORMATETC fmtu = {CF_UNICODETEXT, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };\n\tconst HRESULT hrHasUText = pIDataSource->QueryGetData(&fmtu);\n\thasOKText = (hrHasUText == S_OK);\n\tif (hasOKText) {\n\t\t*pdwEffect = EffectFromState(grfKeyState);\n\t} else {\n\t\t*pdwEffect = DROPEFFECT_NONE;\n\t}\n\n\treturn S_OK;\n}\n\nSTDMETHODIMP ScintillaWin::DragOver(DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) {\n\ttry {\n\t\tif (!hasOKText || pdoc->IsReadOnly()) {\n\t\t\t*pdwEffect = DROPEFFECT_NONE;\n\t\t\treturn S_OK;\n\t\t}\n\n\t\t*pdwEffect = EffectFromState(grfKeyState);\n\n\t\t// Update the cursor.\n\t\tPOINT rpt = {pt.x, pt.y};\n\t\t::ScreenToClient(MainHWND(), &rpt);\n\t\tSetDragPosition(SPositionFromLocation(PointFromPOINT(rpt), false, false, UserVirtualSpace()));\n\n\t\treturn S_OK;\n\t} catch (...) {\n\t\terrorStatus = Status::Failure;\n\t}\n\treturn E_FAIL;\n}\n\nSTDMETHODIMP ScintillaWin::DragLeave() {\n\ttry {\n\t\tSetDragPosition(SelectionPosition(Sci::invalidPosition));\n\t\treturn S_OK;\n\t} catch (...) {\n\t\terrorStatus = Status::Failure;\n\t}\n\treturn E_FAIL;\n}\n\n//Au\nvoid ScintillaWin::AuDragDrop(int action, Sci_DragDropData* r) {\n\tSelectionPosition pos; if(r != nullptr) pos = SPositionFromLocation(Point::FromInts(r->x, r->y), false, false, UserVirtualSpace());\n\tswitch(action) {\n\tcase 1: //over\n\t\tSetDragPosition(pos);\n\t\tbreak;\n\tcase 2: //drop\n\t\tSetDragPosition(SelectionPosition(Sci::invalidPosition));\n\t\tif (r->len > 0) DropAt(pos, r->text, r->len, !r->copy, false);\n\t\telse SetEmptySelection(pos);\n\t\tbreak;\n\tcase 3: //leave\n\t\tSetDragPosition(SelectionPosition(Sci::invalidPosition));\n\t\tbreak;\n\t}\n}\n\nSTDMETHODIMP ScintillaWin::Drop(LPDATAOBJECT pIDataSource, DWORD grfKeyState,\n                                POINTL pt, PDWORD pdwEffect) {\n\ttry {\n\t\t*pdwEffect = EffectFromState(grfKeyState);\n\n\t\tif (!pIDataSource)\n\t\t\treturn E_POINTER;\n\n\t\tSetDragPosition(SelectionPosition(Sci::invalidPosition));\n\n\t\tstd::string putf;\n\t\tFORMATETC fmtu = {CF_UNICODETEXT, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};\n\t\tSTGMEDIUM medium{};\n\t\tconst HRESULT hr = pIDataSource->GetData(&fmtu, &medium);\n\t\tif (!SUCCEEDED(hr)) {\n\t\t\treturn hr;\n\t\t}\n\t\tif (medium.hGlobal) {\n\t\t\tGlobalMemory memUDrop(medium.hGlobal);\n\t\t\tif (const wchar_t *uptr = static_cast<const wchar_t *>(memUDrop.ptr)) {\n\t\t\t\tputf = EncodeWString(uptr);\n\t\t\t}\n\t\t\tmemUDrop.Unlock();\n\t\t}\n\n\t\tif (putf.empty()) {\n\t\t\treturn S_OK;\n\t\t}\n\n\t\tFORMATETC fmtr = {cfColumnSelect, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};\n\t\tconst bool isRectangular = S_OK == pIDataSource->QueryGetData(&fmtr);\n\n\t\tPOINT rpt = {pt.x, pt.y};\n\t\t::ScreenToClient(MainHWND(), &rpt);\n\t\tconst SelectionPosition movePos = SPositionFromLocation(PointFromPOINT(rpt), false, false, UserVirtualSpace());\n\n\t\tDropAt(movePos, putf.c_str(), putf.size(), *pdwEffect == DROPEFFECT_MOVE, isRectangular);\n\n\t\t// Free data\n\t\t::ReleaseStgMedium(&medium);\n\n\t\treturn S_OK;\n\t} catch (...) {\n\t\terrorStatus = Status::Failure;\n\t}\n\treturn E_FAIL;\n}\n\n/// Implement important part of IDataObject\nSTDMETHODIMP ScintillaWin::GetData(FORMATETC *pFEIn, STGMEDIUM *pSTM) {\n\tif (!SupportedFormat(pFEIn)) {\n\t\t//Platform::DebugPrintf(\"DOB GetData No %d %x %x fmt=%x\\n\", lenDrag, pFEIn, pSTM, pFEIn->cfFormat);\n\t\treturn DATA_E_FORMATETC;\n\t}\n\n\tpSTM->tymed = TYMED_HGLOBAL;\n\t//Platform::DebugPrintf(\"DOB GetData OK %d %x %x\\n\", lenDrag, pFEIn, pSTM);\n\n\tGlobalMemory uniText;\n\tCopyToGlobal(uniText, drag);\n\tpSTM->hGlobal = uniText ? uniText.Unlock() : HGLOBAL{};\n\tpSTM->pUnkForRelease = nullptr;\n\treturn S_OK;\n}\n\nvoid ScintillaWin::Prepare() noexcept {\n\tPlatform_Initialise(hInstance);\n\n\t// Register the CallTip class\n\tWNDCLASSEX wndclassc{};\n\twndclassc.cbSize = sizeof(wndclassc);\n\twndclassc.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;\n\twndclassc.cbWndExtra = sizeof(ScintillaWin *);\n\twndclassc.hInstance = hInstance;\n\twndclassc.lpfnWndProc = ScintillaWin::CTWndProc;\n\twndclassc.hCursor = ::LoadCursor({}, IDC_ARROW);\n\twndclassc.lpszClassName = callClassName;\n\n\tcallClassAtom = ::RegisterClassEx(&wndclassc);\n}\n\nbool ScintillaWin::Register(HINSTANCE hInstance_) noexcept {\n\n\thInstance = hInstance_;\n\n\t// Register the Scintilla class\n\t// Register Scintilla as a wide character window\n\tWNDCLASSEXW wndclass {};\n\twndclass.cbSize = sizeof(wndclass);\n\twndclass.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;\n\twndclass.lpfnWndProc = ScintillaWin::SWndProc;\n\twndclass.cbWndExtra = sizeof(ScintillaWin *);\n\twndclass.hInstance = hInstance;\n\twndclass.lpszClassName = L\"Scintilla\";\n\tscintillaClassAtom = ::RegisterClassExW(&wndclass);\n\tconst bool result = 0 != scintillaClassAtom;\n\n\treturn result;\n}\n\nbool ScintillaWin::Unregister() noexcept {\n\tbool result = true;\n\tif (0 != scintillaClassAtom) {\n\t\tif (::UnregisterClass(MAKEINTATOM(scintillaClassAtom), hInstance) == 0) {\n\t\t\tresult = false;\n\t\t}\n\t\tscintillaClassAtom = 0;\n\t}\n\tif (0 != callClassAtom) {\n\t\tif (::UnregisterClass(MAKEINTATOM(callClassAtom), hInstance) == 0) {\n\t\t\tresult = false;\n\t\t}\n\t\tcallClassAtom = 0;\n\t}\n\treturn result;\n}\n\nbool ScintillaWin::HasCaretSizeChanged() const noexcept {\n\treturn\n\t\t((0 != vs.caret.width) && (sysCaretWidth != vs.caret.width))\n\t\t|| ((0 != vs.lineHeight) && (sysCaretHeight != vs.lineHeight));\n}\n\nBOOL ScintillaWin::CreateSystemCaret() {\n\tsysCaretWidth = vs.caret.width;\n\tif (0 == sysCaretWidth) {\n\t\tsysCaretWidth = 1;\n\t}\n\tsysCaretHeight = vs.lineHeight;\n\tconst int bitmapSize = (((sysCaretWidth + 15) & ~15) >> 3) *\n\t\tsysCaretHeight;\n\tstd::vector<BYTE> bits(bitmapSize);\n\tsysCaretBitmap = ::CreateBitmap(sysCaretWidth, sysCaretHeight, 1,\n\t\t1, bits.data());\n\tconst BOOL retval = ::CreateCaret(\n\t\tMainHWND(), sysCaretBitmap,\n\t\tsysCaretWidth, sysCaretHeight);\n\tif (technology == Technology::Default) {\n\t\t// System caret interferes with Direct2D drawing so only show it for GDI.\n\t\t::ShowCaret(MainHWND());\n\t}\n\treturn retval;\n}\n\nBOOL ScintillaWin::DestroySystemCaret() noexcept {\n\t::HideCaret(MainHWND());\n\tconst BOOL retval = ::DestroyCaret();\n\tif (sysCaretBitmap) {\n\t\t::DeleteObject(sysCaretBitmap);\n\t\tsysCaretBitmap = {};\n\t}\n\treturn retval;\n}\n\nvoid ScintillaWin::CTPaint(HWND hWnd) {\n\tPainter painter(hWnd);\n\tstd::unique_ptr<Surface> surfaceWindow(Surface::Allocate(technology));\n#if defined(USE_D2D)\n\tHwndRenderTarget pCTRenderTarget;\n#endif\n\tconst RECT rc = GetClientRect(hWnd);\n\tif (technology == Technology::Default) {\n\t\tsurfaceWindow->Init(painter.ps.hdc, hWnd);\n\t} else {\n#if defined(USE_D2D)\n\t\tconst int scaleFactor = GetFirstIntegralMultipleDeviceScaleFactor();\n\n\t\t// Create a Direct2D render target.\n\n\t\tconst FLOAT dpiTarget = dpiDefault * static_cast<float>(scaleFactor);\n\n\t\tconst D2D1_RENDER_TARGET_PROPERTIES drtp = D2D1::RenderTargetProperties(\n\t\t\tD2D1_RENDER_TARGET_TYPE_DEFAULT,\n\t\t\t{ DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_UNKNOWN },\n\t\t\tdpiTarget, dpiTarget);\n\n\t\tconst D2D1_PRESENT_OPTIONS presentOptions = (technology == Technology::DirectWriteRetain) ?\n\t\t\tD2D1_PRESENT_OPTIONS_RETAIN_CONTENTS : D2D1_PRESENT_OPTIONS_NONE;\n\n\t\tconst D2D1_HWND_RENDER_TARGET_PROPERTIES dhrtp = D2D1::HwndRenderTargetProperties(\n\t\t\thWnd,\n\t\t\t::GetSizeUFromRect(rc, scaleFactor),\n\t\t\tpresentOptions);\n\n\t\tconst HRESULT hr = CreateHwndRenderTarget(&drtp, &dhrtp, pCTRenderTarget);\n\t\tif (!SUCCEEDED(hr)) {\n\t\t\tsurfaceWindow->Release();\n\t\t\treturn;\n\t\t}\n\t\t// If above SUCCEEDED, then pCTRenderTarget not nullptr\n\t\tassert(pCTRenderTarget);\n\t\tif (pCTRenderTarget) {\n\t\t\tsurfaceWindow->Init(pCTRenderTarget.Get(), hWnd);\n\t\t\tpCTRenderTarget->BeginDraw();\n\t\t}\n#endif\n\t}\n\tsurfaceWindow->SetMode(CurrentSurfaceMode());\n\tSetRenderingParams(surfaceWindow.get());\n\tct.PaintCT(surfaceWindow.get());\n#if defined(USE_D2D)\n\tif (pCTRenderTarget)\n\t\tpCTRenderTarget->EndDraw();\n#endif\n\tsurfaceWindow->Release();\n}\n\nLRESULT ScintillaWin::CTProcessMessage(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) {\n\ttry {\n\t\tswitch (iMessage) {\n\t\tcase WM_NCDESTROY:\n\t\t\tSetWindowPointer(hWnd, nullptr);\n\t\t\tbreak;\n\t\tcase WM_PAINT:\n\t\t\tCTPaint(hWnd);\n\t\t\treturn 0;\n\t\tcase WM_NCLBUTTONDOWN:\n\t\tcase WM_NCLBUTTONDBLCLK: {\n\t\t\t\tPOINT pt = POINTFromLParam(lParam);\n\t\t\t\t::ScreenToClient(hWnd, &pt);\n\t\t\t\tct.MouseClick(PointFromPOINT(pt));\n\t\t\t\tCallTipClick();\n\t\t\t\treturn 0;\n\t\t\t}\n\t\tcase WM_LBUTTONDOWN:\n\t\t\t// This does not fire due to the hit test code\n\t\t\tct.MouseClick(PointFromLParam(lParam));\n\t\t\tCallTipClick();\n\t\t\treturn 0;\n\t\tcase WM_SETCURSOR:\n\t\t\t::SetCursor(::LoadCursor({}, IDC_ARROW));\n\t\t\treturn 0;\n\t\tcase WM_NCHITTEST:\n\t\t\treturn HTCAPTION;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t} catch (...) {\n\t\terrorStatus = Status::Failure;\n\t}\n\n\treturn ::DefWindowProc(hWnd, iMessage, wParam, lParam);\n}\n\nLRESULT PASCAL ScintillaWin::CTWndProc(\n\tHWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) {\n\t// Find C++ object associated with window.\n\tScintillaWin *sciThis = static_cast<ScintillaWin *>(PointerFromWindow(hWnd));\n\t// sciThis will be zero if WM_CREATE not seen yet\n\tif (sciThis == nullptr) {\n\t\tif (iMessage == WM_CREATE) {\n\t\t\t// Associate CallTip object with window\n\t\t\tCREATESTRUCT *pCreate = static_cast<CREATESTRUCT *>(PtrFromSPtr(lParam));\n\t\t\tSetWindowPointer(hWnd, pCreate->lpCreateParams);\n\t\t\treturn 0;\n\t\t}\n\t\treturn ::DefWindowProc(hWnd, iMessage, wParam, lParam);\n\t}\n\treturn sciThis->CTProcessMessage(hWnd, iMessage, wParam, lParam);\n}\n\nsptr_t ScintillaWin::DirectFunction(\n    sptr_t ptr, UINT iMessage, uptr_t wParam, sptr_t lParam) {\n\tScintillaWin *sci = static_cast<ScintillaWin *>(PtrFromSPtr(ptr));\n\tPLATFORM_ASSERT(::GetCurrentThreadId() == ::GetWindowThreadProcessId(sci->MainHWND(), nullptr));\n\treturn sci->WndProc(static_cast<Message>(iMessage), wParam, lParam);\n}\n\nsptr_t ScintillaWin::DirectStatusFunction(\n    sptr_t ptr, UINT iMessage, uptr_t wParam, sptr_t lParam, int *pStatus) {\n\tScintillaWin *sci = static_cast<ScintillaWin *>(PtrFromSPtr(ptr));\n\tPLATFORM_ASSERT(::GetCurrentThreadId() == ::GetWindowThreadProcessId(sci->MainHWND(), nullptr));\n\tconst sptr_t returnValue = sci->WndProc(static_cast<Message>(iMessage), wParam, lParam);\n\t*pStatus = static_cast<int>(sci->errorStatus);\n\treturn returnValue;\n}\n\nnamespace Scintilla::Internal {\n\nsptr_t DirectFunction(\n    ScintillaWin *sci, UINT iMessage, uptr_t wParam, sptr_t lParam) {\n\treturn sci->WndProc(static_cast<Message>(iMessage), wParam, lParam);\n}\n\n}\n\nLRESULT PASCAL ScintillaWin::SWndProc(\n\tHWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) {\n\t//Platform::DebugPrintf(\"S W:%x M:%x WP:%x L:%x\\n\", hWnd, iMessage, wParam, lParam);\n\n\t// Find C++ object associated with window.\n\tScintillaWin *sci = static_cast<ScintillaWin *>(PointerFromWindow(hWnd));\n\t// sci will be zero if WM_CREATE not seen yet\n\tif (sci == nullptr) {\n\t\ttry {\n\t\t\tif (iMessage == WM_CREATE) {\n\t\t\t\tstatic std::once_flag once;\n\t\t\t\tstd::call_once(once, Prepare);\n\t\t\t\t// Create C++ object associated with window\n\t\t\t\tsci = new ScintillaWin(hWnd);\n\t\t\t\tSetWindowPointer(hWnd, sci);\n\t\t\t\treturn sci->WndProc(static_cast<Message>(iMessage), wParam, lParam);\n\t\t\t}\n\t\t} catch (...) {\n\t\t}\n\t\treturn ::DefWindowProc(hWnd, iMessage, wParam, lParam);\n\t}\n\tif (iMessage == WM_NCDESTROY) {\n\t\ttry {\n\t\t\tsci->Finalise();\n\t\t\tdelete sci;\n\t\t} catch (...) {\n\t\t}\n\t\tSetWindowPointer(hWnd, nullptr);\n\t\treturn ::DefWindowProc(hWnd, iMessage, wParam, lParam);\n\t}\n\treturn sci->WndProc(static_cast<Message>(iMessage), wParam, lParam);\n}\n\n// This function is externally visible so it can be called from container when building statically.\n// Must be called once only.\nextern \"C\" int Scintilla_RegisterClasses(void *hInstance) {\n\tconst bool result = ScintillaWin::Register(static_cast<HINSTANCE>(hInstance));\n\treturn result;\n}\n\nnamespace Scintilla::Internal {\n\nint ResourcesRelease(bool fromDllMain) noexcept {\n\tconst bool result = ScintillaWin::Unregister();\n\tPlatform_Finalise(fromDllMain);\n\treturn result;\n}\n\nint RegisterClasses(void *hInstance) noexcept {\n\tconst bool result = ScintillaWin::Register(static_cast<HINSTANCE>(hInstance));\n\treturn result;\n}\n\n}\n\n// This function is externally visible so it can be called from container when building statically.\nextern \"C\" int Scintilla_ReleaseResources() {\n\treturn Scintilla::Internal::ResourcesRelease(false);\n}\n\n//Au: for some mods use functions instead of messages. Eg when need as fast as possible.\nnamespace Scintilla {\n\n#define EXPORT extern \"C\" __declspec(dllexport)\n\n//Gets data for C# function _RangeText etc.\nEXPORT int __stdcall Sci_Range(ScintillaWin* sci, int start8, int end8, const char*& p1, const char*& p2, int* length) {\n\tauto pdoc = sci->pdoc;\n\tauto start = static_cast<Sci::Position>(start8);\n\tauto end = end8 < 0 ? pdoc->Length() : static_cast<Sci::Position>(end8);\n\tauto gap = pdoc->GapPosition();\n\tif(gap > start && gap < end) {\n\t\tp1 = pdoc->RangePointer(start, gap - start);\n\t\tp2 = pdoc->RangePointer(gap, end - gap);\n\t} else {\n\t\tp1 = pdoc->RangePointer(start, end - start);\n\t\tp2 = nullptr;\n\t}\n\tif(length != nullptr) *length = (int)pdoc->Length();\n\treturn (int)gap;\n}\n\nEXPORT void __stdcall Sci_SetFoldLevels(ScintillaWin* sci, int line, int lastLine, int len, int* a) {\n\tsci->Sci_SetFoldLevels(line, lastLine, len, a);\n}\n\nEXPORT void __stdcall Sci_GetVisibleRange(ScintillaWin* sci, struct ScintillaWin::Sci_VisibleRange& r) {\n\tsci->Sci_GetVisibleRange(r);\n}\n\n}\n"
  },
  {
    "path": "Libraries/scintilla/win32/ScintillaWin.h",
    "content": "// Scintilla source code edit control\n/** @file ScintillaWin.h\n ** Define functions from ScintillaWin.cxx that can be called from ScintillaDLL.cxx.\n **/\n// Copyright 1998-2018 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#ifndef SCINTILLAWIN_H\n#define SCINTILLAWIN_H\n\nnamespace Scintilla::Internal {\n\nclass ScintillaWin;\n\nint ResourcesRelease(bool fromDllMain) noexcept;\nint RegisterClasses(void *hInstance) noexcept;\nScintilla::sptr_t DirectFunction(ScintillaWin *sci, UINT iMessage, Scintilla::uptr_t wParam, Scintilla::sptr_t lParam);\n\n}\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/win32/SurfaceD2D.cxx",
    "content": "// Scintilla source code edit control\n/** @file SurfaceD2D.cxx\n ** Implementation of drawing to Direct2D on Windows.\n **/\n// Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#include <cstddef>\n#include <cstdlib>\n#include <cstdint>\n#include <cstring>\n#include <cstdio>\n#include <cstdarg>\n#include <ctime>\n#include <cmath>\n#include <climits>\n\n#include <string_view>\n#include <vector>\n#include <map>\n#include <optional>\n#include <algorithm>\n#include <iterator>\n#include <memory>\n#include <mutex>\n\n// Want to use std::min and std::max so don't want Windows.h version of min and max\n#if !defined(NOMINMAX)\n#define NOMINMAX\n#endif\n#undef _WIN32_WINNT\n#define _WIN32_WINNT 0x0A00\n#undef WINVER\n#define WINVER 0x0A00\n#define WIN32_LEAN_AND_MEAN 1\n#include <windows.h>\n#include <commctrl.h>\n#include <richedit.h>\n#include <windowsx.h>\n#include <shellscalingapi.h>\n\n#include <wrl.h>\nusing Microsoft::WRL::ComPtr;\n\n#if !defined(DISABLE_D2D)\n#define USE_D2D 1\n#endif\n\n#if defined(USE_D2D)\n#include <d2d1_1.h>\n#include <d3d11_1.h>\n#include <dwrite_1.h>\n#endif\n\n#include \"ScintillaTypes.h\"\n\n#include \"Debugging.h\"\n#include \"Geometry.h\"\n#include \"Platform.h\"\n#include \"XPM.h\"\n#include \"UniConversion.h\"\n#include \"DBCS.h\"\n\n#include \"WinTypes.h\"\n#include \"PlatWin.h\"\n#include \"SurfaceGDI.h\"\n#if defined(USE_D2D)\n#include \"SurfaceD2D.h\"\n#endif\n\n// __uuidof is a Microsoft extension but makes COM code neater, so disable warning\n#if defined(__clang__)\n#pragma clang diagnostic ignored \"-Wlanguage-extension-token\"\n#endif\n\nusing namespace Scintilla;\nusing namespace Scintilla::Internal;\n\nnamespace {\n\n#if defined(USE_D2D)\n\nD2D1_DRAW_TEXT_OPTIONS d2dDrawTextOptions = D2D1_DRAW_TEXT_OPTIONS_NONE;\n\nHMODULE hDLLD2D{};\nHMODULE hDLLD3D{};\nHMODULE hDLLDWrite{};\n\nPFN_D3D11_CREATE_DEVICE fnDCD{};\n\nvoid LoadD2DOnce() noexcept {\n\tDWORD loadLibraryFlags = 0;\n\tHMODULE kernel32 = ::GetModuleHandleW(L\"kernel32.dll\");\n\tif (kernel32) {\n\t\tif (::GetProcAddress(kernel32, \"SetDefaultDllDirectories\")) {\n\t\t\t// Availability of SetDefaultDllDirectories implies Windows 8+ or\n\t\t\t// that KB2533623 has been installed so LoadLibraryEx can be called\n\t\t\t// with LOAD_LIBRARY_SEARCH_SYSTEM32.\n\t\t\tloadLibraryFlags = LOAD_LIBRARY_SEARCH_SYSTEM32;\n\t\t}\n\t}\n\n\tusing D2D1CFSig = HRESULT(WINAPI *)(D2D1_FACTORY_TYPE factoryType, REFIID riid,\n\t\tCONST D2D1_FACTORY_OPTIONS *pFactoryOptions, IUnknown **factory);\n\tusing DWriteCFSig = HRESULT(WINAPI *)(DWRITE_FACTORY_TYPE factoryType, REFIID iid,\n\t\tIUnknown **factory);\n\n\thDLLD2D = ::LoadLibraryEx(TEXT(\"D2D1.DLL\"), {}, loadLibraryFlags);\n\tD2D1CFSig fnD2DCF = DLLFunction<D2D1CFSig>(hDLLD2D, \"D2D1CreateFactory\");\n\tif (fnD2DCF) {\n\t\tconst D2D1_FACTORY_OPTIONS options{};\n\t\t// A multi threaded factory in case Scintilla is used with multiple GUI threads\n\t\tfnD2DCF(D2D1_FACTORY_TYPE_MULTI_THREADED,\n\t\t\t__uuidof(ID2D1Factory1),\n\t\t\t&options,\n\t\t\treinterpret_cast<IUnknown **>(&pD2DFactory));\n\t}\n\thDLLDWrite = ::LoadLibraryEx(TEXT(\"DWRITE.DLL\"), {}, loadLibraryFlags);\n\tDWriteCFSig fnDWCF = DLLFunction<DWriteCFSig>(hDLLDWrite, \"DWriteCreateFactory\");\n\tif (fnDWCF) {\n\t\tconst GUID IID_IDWriteFactory2 = // 0439fc60-ca44-4994-8dee-3a9af7b732ec\n\t\t{ 0x0439fc60, 0xca44, 0x4994, { 0x8d, 0xee, 0x3a, 0x9a, 0xf7, 0xb7, 0x32, 0xec } };\n\n\t\tconst HRESULT hr = fnDWCF(DWRITE_FACTORY_TYPE_SHARED,\n\t\t\tIID_IDWriteFactory2,\n\t\t\treinterpret_cast<IUnknown **>(&pIDWriteFactory));\n\t\tif (SUCCEEDED(hr)) {\n\t\t\t// D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT\n\t\t\td2dDrawTextOptions = static_cast<D2D1_DRAW_TEXT_OPTIONS>(0x00000004);\n\t\t} else {\n\t\t\tfnDWCF(DWRITE_FACTORY_TYPE_SHARED,\n\t\t\t\t__uuidof(IDWriteFactory1),\n\t\t\t\treinterpret_cast<IUnknown **>(&pIDWriteFactory));\n\t\t}\n\t}\n\n\thDLLD3D = ::LoadLibraryEx(TEXT(\"D3D11.DLL\"), {}, loadLibraryFlags);\n\tif (!hDLLD3D) {\n\t\tPlatform::DebugPrintf(\"Direct3D not loaded\\n\");\n\t}\n\tfnDCD = DLLFunction<PFN_D3D11_CREATE_DEVICE>(hDLLD3D, \"D3D11CreateDevice\");\n\tif (!fnDCD) {\n\t\tPlatform::DebugPrintf(\"Direct3D does not have D3D11CreateDevice\\n\");\n\t}\n}\n\nconstexpr D2D1_TEXT_ANTIALIAS_MODE DWriteMapFontQuality(FontQuality extraFontFlag) noexcept {\n\tswitch (extraFontFlag & FontQuality::QualityMask) {\n\n\t\tcase FontQuality::QualityNonAntialiased:\n\t\t\treturn D2D1_TEXT_ANTIALIAS_MODE_ALIASED;\n\n\t\tcase FontQuality::QualityAntialiased:\n\t\t\treturn D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE;\n\n\t\tcase FontQuality::QualityLcdOptimized:\n\t\t\treturn D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE;\n\n\t\tdefault:\n\t\t\treturn D2D1_TEXT_ANTIALIAS_MODE_DEFAULT;\n\t}\n}\n\nstruct FontDirectWrite : public FontWin {\n\tComPtr<IDWriteTextFormat> pTextFormat;\n\tFontQuality extraFontFlag = FontQuality::QualityDefault;\n\tCharacterSet characterSet = CharacterSet::Ansi;\n\tstatic constexpr FLOAT minimalAscent = 2.0f;\n\tFLOAT yAscent = minimalAscent;\n\tFLOAT yDescent = 1.0f;\n\tFLOAT yInternalLeading = 0.0f;\n\n\texplicit FontDirectWrite(const FontParameters &fp) :\n\t\textraFontFlag(fp.extraFontFlag),\n\t\tcharacterSet(fp.characterSet) {\n\t\tconst std::wstring wsFace = WStringFromUTF8(fp.faceName);\n\t\tconst std::wstring wsLocale = WStringFromUTF8(fp.localeName);\n\t\tconst FLOAT fHeight = static_cast<FLOAT>(fp.size);\n\t\tconst DWRITE_FONT_STYLE style = fp.italic ? DWRITE_FONT_STYLE_ITALIC : DWRITE_FONT_STYLE_NORMAL;\n\t\tHRESULT hr = pIDWriteFactory->CreateTextFormat(wsFace.c_str(), nullptr,\n\t\t\tstatic_cast<DWRITE_FONT_WEIGHT>(fp.weight),\n\t\t\tstyle,\n\t\t\tstatic_cast<DWRITE_FONT_STRETCH>(fp.stretch),\n\t\t\t\tfHeight, wsLocale.c_str(), pTextFormat.GetAddressOf());\n\t\tif (hr == E_INVALIDARG) {\n\t\t\t// Possibly a bad locale name like \"/\" so try \"en-us\".\n\t\t\thr = pIDWriteFactory->CreateTextFormat(wsFace.c_str(), nullptr,\n\t\t\t\tstatic_cast<DWRITE_FONT_WEIGHT>(fp.weight),\n\t\t\t\tstyle,\n\t\t\t\tstatic_cast<DWRITE_FONT_STRETCH>(fp.stretch),\n\t\t\t\tfHeight, L\"en-us\", pTextFormat.ReleaseAndGetAddressOf());\n\t\t}\n\t\tif (SUCCEEDED(hr)) {\n\t\t\tpTextFormat->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP);\n\n\t\t\tif (TextLayout pTextLayout = LayoutCreate(L\"X\", pTextFormat.Get())) {\n\t\t\t\tconstexpr int maxLines = 2;\n\t\t\t\tDWRITE_LINE_METRICS lineMetrics[maxLines]{};\n\t\t\t\tUINT32 lineCount = 0;\n\t\t\t\thr = pTextLayout->GetLineMetrics(lineMetrics, maxLines, &lineCount);\n\t\t\t\tif (SUCCEEDED(hr)) {\n\t\t\t\t\tyAscent = lineMetrics[0].baseline;\n\t\t\t\t\tyDescent = lineMetrics[0].height - lineMetrics[0].baseline;\n\n\t\t\t\t\tFLOAT emHeight;\n\t\t\t\t\thr = pTextLayout->GetFontSize(0, &emHeight);\n\t\t\t\t\tif (SUCCEEDED(hr)) {\n\t\t\t\t\t\tyInternalLeading = lineMetrics[0].height - emHeight;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tpTextFormat->SetLineSpacing(DWRITE_LINE_SPACING_METHOD_UNIFORM, lineMetrics[0].height, lineMetrics[0].baseline);\n\t\t\t}\n\t\t}\n\t}\n\t// Allow copy constructor. Has to explicitly copy each field since can't use =default as deleted in Font.\n\tFontDirectWrite(const FontDirectWrite &other) noexcept {\n\t\tpTextFormat = other.pTextFormat;\n\t\textraFontFlag = other.extraFontFlag;\n\t\tcharacterSet = other.characterSet;\n\t\tyAscent = other.yAscent;\n\t\tyDescent = other.yDescent;\n\t\tyInternalLeading = other.yInternalLeading;\n\t}\n\t// Deleted so FontDirectWrite objects can not be copied.\n\tFontDirectWrite(FontDirectWrite &&) = delete;\n\tFontDirectWrite &operator=(const FontDirectWrite &) = delete;\n\tFontDirectWrite &operator=(FontDirectWrite &&) = delete;\n\t~FontDirectWrite() noexcept override = default;\n\t[[nodiscard]] HFONT HFont() const noexcept override {\n\t\tLOGFONTW lf = {};\n\t\tconst HRESULT hr = pTextFormat->GetFontFamilyName(lf.lfFaceName, LF_FACESIZE);\n\t\tif (!SUCCEEDED(hr)) {\n\t\t\treturn {};\n\t\t}\n\t\tlf.lfWeight = pTextFormat->GetFontWeight();\n\t\tlf.lfItalic = pTextFormat->GetFontStyle() == DWRITE_FONT_STYLE_ITALIC;\n\t\tlf.lfHeight = -static_cast<int>(pTextFormat->GetFontSize());\n\t\treturn ::CreateFontIndirectW(&lf);\n\t}\n\n\t[[nodiscard]] int CodePageText(int codePage) const noexcept {\n\t\tif (!(codePage == CpUtf8) && (characterSet != CharacterSet::Ansi)) {\n\t\t\tcodePage = CodePageFromCharSet(characterSet, codePage);\n\t\t}\n\t\treturn codePage;\n\t}\n\n\tstatic const FontDirectWrite *Cast(const Font *font_) {\n\t\tconst FontDirectWrite *pfm = dynamic_cast<const FontDirectWrite *>(font_);\n\t\tPLATFORM_ASSERT(pfm);\n\t\tif (!pfm) {\n\t\t\tthrow std::runtime_error(\"SurfaceD2D::SetFont: wrong Font type.\");\n\t\t}\n\t\treturn pfm;\n\t}\n\n\t[[nodiscard]] std::unique_ptr<FontWin> Duplicate() const override {\n\t\treturn std::make_unique<FontDirectWrite>(*this);\n\t}\n\n\t[[nodiscard]] CharacterSet GetCharacterSet() const noexcept override {\n\t\treturn characterSet;\n\t}\n};\n\nconstexpr D2D1_RECT_F RectangleFromPRectangle(PRectangle rc) noexcept {\n\treturn {\n\t\tstatic_cast<FLOAT>(rc.left),\n\t\tstatic_cast<FLOAT>(rc.top),\n\t\tstatic_cast<FLOAT>(rc.right),\n\t\tstatic_cast<FLOAT>(rc.bottom)\n\t};\n}\n\nconstexpr D2D1_POINT_2F DPointFromPoint(Point point) noexcept {\n\treturn { static_cast<FLOAT>(point.x), static_cast<FLOAT>(point.y) };\n}\n\nconstexpr Supports SupportsD2D[] = {\n\tSupports::LineDrawsFinal,\n\tSupports::FractionalStrokeWidth,\n\tSupports::TranslucentStroke,\n\tSupports::PixelModification,\n\tSupports::ThreadSafeMeasureWidths,\n};\n\nconstexpr D2D1_RECT_F RectangleInset(D2D1_RECT_F rect, FLOAT inset) noexcept {\n\treturn D2D1_RECT_F{\n\t\trect.left + inset,\n\t\trect.top + inset,\n\t\trect.right - inset,\n\t\trect.bottom - inset };\n}\n\nconstexpr D2D_COLOR_F ColorFromColourAlpha(ColourRGBA colour) noexcept {\n\treturn D2D_COLOR_F{\n\t\tcolour.GetRedComponent(),\n\t\tcolour.GetGreenComponent(),\n\t\tcolour.GetBlueComponent(),\n\t\tcolour.GetAlphaComponent()\n\t};\n}\n\nclass BlobInline;\n\nclass SurfaceD2D : public Surface, public ISetRenderingParams {\n\tSurfaceMode mode;\n\n\t// Text measuring surface: both pRenderTarget and pBitmapRenderTarget are null.\n\t// Window surface: pRenderTarget is valid but not pBitmapRenderTarget.\n\t// Bitmap drawing surface: both pRenderTarget and pBitmapRenderTarget are valid and the same.\n\tComPtr<ID2D1RenderTarget> pRenderTarget;\n\tComPtr<ID2D1BitmapRenderTarget> pBitmapRenderTarget;\n\tint clipsActive = 0;\n\n\tBrushSolid pBrush = nullptr;\n\n\tstatic constexpr FontQuality invalidFontQuality = FontQuality::QualityMask;\n\tFontQuality fontQuality = invalidFontQuality;\n\tint logPixelsY = USER_DEFAULT_SCREEN_DPI;\n\tint deviceScaleFactor = 1;\n\tstd::shared_ptr<RenderingParams> renderingParams;\n\n\tvoid Clear() noexcept;\n\tvoid SetFontQuality(FontQuality extraFontFlag);\n\tHRESULT GetBitmap(ID2D1Bitmap **ppBitmap);\n\tvoid SetDeviceScaleFactor(const ID2D1RenderTarget *const pD2D1RenderTarget) noexcept;\n\npublic:\n\tSurfaceD2D() noexcept = default;\n\tSurfaceD2D(ID2D1RenderTarget *pRenderTargetCompatible, int width, int height, SurfaceMode mode_, int logPixelsY_) noexcept;\n\t// Deleted so SurfaceD2D objects can not be copied.\n\tSurfaceD2D(const SurfaceD2D &) = delete;\n\tSurfaceD2D(SurfaceD2D &&) = delete;\n\tSurfaceD2D &operator=(const SurfaceD2D &) = delete;\n\tSurfaceD2D &operator=(SurfaceD2D &&) = delete;\n\t~SurfaceD2D() noexcept override;\n\n\tvoid SetScale(WindowID wid) noexcept;\n\tvoid Init(WindowID wid) override;\n\tvoid Init(SurfaceID sid, WindowID wid) override;\n\tstd::unique_ptr<Surface> AllocatePixMap(int width, int height) override;\n\n\tvoid SetMode(SurfaceMode mode_) override;\n\n\tvoid Release() noexcept override;\n\tint SupportsFeature(Supports feature) noexcept override;\n\tbool Initialised() override;\n\n\tvoid D2DPenColourAlpha(ColourRGBA fore) noexcept;\n\tint LogPixelsY() override;\n\tint PixelDivisions() override;\n\tint DeviceHeightFont(int points) override;\n\tvoid LineDraw(Point start, Point end, Stroke stroke) override;\n\tstatic Geometry GeometricFigure(const Point *pts, size_t npts, D2D1_FIGURE_BEGIN figureBegin) noexcept;\n\tvoid PolyLine(const Point *pts, size_t npts, Stroke stroke) override;\n\tvoid Polygon(const Point *pts, size_t npts, FillStroke fillStroke) override;\n\tvoid RectangleDraw(PRectangle rc, FillStroke fillStroke) override;\n\tvoid RectangleFrame(PRectangle rc, Stroke stroke) override;\n\tvoid FillRectangle(PRectangle rc, Fill fill) override;\n\tvoid FillRectangleAligned(PRectangle rc, Fill fill) override;\n\tvoid FillRectangle(PRectangle rc, Surface &surfacePattern) override;\n\tvoid RoundedRectangle(PRectangle rc, FillStroke fillStroke) override;\n\tvoid AlphaRectangle(PRectangle rc, XYPOSITION cornerSize, FillStroke fillStroke) override;\n\tvoid GradientRectangle(PRectangle rc, const std::vector<ColourStop> &stops, GradientOptions options) override;\n\tvoid DrawRGBAImage(PRectangle rc, int width, int height, const unsigned char *pixelsImage) override;\n\tvoid Ellipse(PRectangle rc, FillStroke fillStroke) override;\n\tvoid Stadium(PRectangle rc, FillStroke fillStroke, Ends ends) override;\n\tvoid Copy(PRectangle rc, Point from, Surface &surfaceSource) override;\n\n\tstd::unique_ptr<IScreenLineLayout> Layout(const IScreenLine *screenLine) override;\n\n\tvoid DrawTextCommon(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, int codePageOverride, UINT fuOptions);\n\n\tvoid DrawTextNoClip(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourRGBA fore, ColourRGBA back) override;\n\tvoid DrawTextClipped(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourRGBA fore, ColourRGBA back) override;\n\tvoid DrawTextTransparent(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourRGBA fore) override;\n\tvoid MeasureWidths(const Font *font_, std::string_view text, XYPOSITION *positions) override;\n\tXYPOSITION WidthText(const Font *font_, std::string_view text) override;\n\n\tvoid DrawTextNoClipUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourRGBA fore, ColourRGBA back) override;\n\tvoid DrawTextClippedUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourRGBA fore, ColourRGBA back) override;\n\tvoid DrawTextTransparentUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourRGBA fore) override;\n\tvoid MeasureWidthsUTF8(const Font *font_, std::string_view text, XYPOSITION *positions) override;\n\tXYPOSITION WidthTextUTF8(const Font *font_, std::string_view text) override;\n\n\tXYPOSITION Ascent(const Font *font_) override;\n\tXYPOSITION Descent(const Font *font_) override;\n\tXYPOSITION InternalLeading(const Font *font_) override;\n\tXYPOSITION Height(const Font *font_) override;\n\tXYPOSITION AverageCharWidth(const Font *font_) override;\n\n\tvoid SetClip(PRectangle rc) override;\n\tvoid PopClip() override;\n\tvoid FlushCachedState() override;\n\tvoid FlushDrawing() override;\n\n\tvoid DrawLineDots(PRectangle rc, ColourRGBA color) { } //Au\n\tvoid* get_hdc() { return nullptr; } //Au\n\n\tvoid SetRenderingParams(std::shared_ptr<RenderingParams> renderingParams_) override;\n};\n\nSurfaceD2D::SurfaceD2D(ID2D1RenderTarget *pRenderTargetCompatible, int width, int height, SurfaceMode mode_, int logPixelsY_) noexcept {\n\tconst D2D1_SIZE_F desiredSize = D2D1::SizeF(static_cast<float>(width), static_cast<float>(height));\n\tD2D1_PIXEL_FORMAT desiredFormat;\n#ifdef __MINGW32__\n\tdesiredFormat.format = DXGI_FORMAT_UNKNOWN;\n#else\n\tdesiredFormat = pRenderTargetCompatible->GetPixelFormat();\n#endif\n\tdesiredFormat.alphaMode = D2D1_ALPHA_MODE_IGNORE;\n\tconst HRESULT hr = pRenderTargetCompatible->CreateCompatibleRenderTarget(\n\t\t&desiredSize, nullptr, &desiredFormat, D2D1_COMPATIBLE_RENDER_TARGET_OPTIONS_NONE, pBitmapRenderTarget.GetAddressOf());\n\tif (SUCCEEDED(hr)) {\n\t\tpRenderTarget = pBitmapRenderTarget;\n\t\tSetDeviceScaleFactor(pRenderTarget.Get());\n\t\tpRenderTarget->BeginDraw();\n\t}\n\tmode = mode_;\n\tlogPixelsY = logPixelsY_;\n}\n\nSurfaceD2D::~SurfaceD2D() noexcept {\n\tClear();\n}\n\nvoid SurfaceD2D::Clear() noexcept {\n\tpBrush = nullptr;\n\tif (pRenderTarget) {\n\t\twhile (clipsActive) {\n\t\t\tpRenderTarget->PopAxisAlignedClip();\n\t\t\tclipsActive--;\n\t\t}\n\t\tif (pBitmapRenderTarget) {\n\t\t\tpRenderTarget->EndDraw();\n\t\t}\n\t}\n\tpRenderTarget = nullptr;\n\tpBitmapRenderTarget = nullptr;\n}\n\nvoid SurfaceD2D::Release() noexcept {\n\tClear();\n}\n\nvoid SurfaceD2D::SetScale(WindowID wid) noexcept {\n\tfontQuality = invalidFontQuality;\n\tlogPixelsY = DpiForWindow(wid);\n}\n\nint SurfaceD2D::SupportsFeature(Supports feature) noexcept {\n\tfor (const Supports f : SupportsD2D) {\n\t\tif (f == feature)\n\t\t\treturn 1;\n\t}\n\treturn 0;\n}\n\nbool SurfaceD2D::Initialised() {\n\treturn pRenderTarget;\n}\n\nvoid SurfaceD2D::Init(WindowID wid) {\n\tRelease();\n\tSetScale(wid);\n}\n\nvoid SurfaceD2D::Init(SurfaceID sid, WindowID wid) {\n\tRelease();\n\tSetScale(wid);\n\tpRenderTarget = static_cast<ID2D1RenderTarget *>(sid);\n\tSetDeviceScaleFactor(pRenderTarget.Get());\n}\n\nstd::unique_ptr<Surface> SurfaceD2D::AllocatePixMap(int width, int height) {\n\tstd::unique_ptr<SurfaceD2D> surf = std::make_unique<SurfaceD2D>(pRenderTarget.Get(), width, height, mode, logPixelsY);\n\tsurf->SetRenderingParams(renderingParams);\n\treturn surf;\n}\n\nvoid SurfaceD2D::SetMode(SurfaceMode mode_) {\n\tmode = mode_;\n}\n\nHRESULT SurfaceD2D::GetBitmap(ID2D1Bitmap **ppBitmap) {\n\tPLATFORM_ASSERT(pBitmapRenderTarget);\n\treturn pBitmapRenderTarget->GetBitmap(ppBitmap);\n}\n\nvoid SurfaceD2D::D2DPenColourAlpha(ColourRGBA fore) noexcept {\n\tif (pRenderTarget) {\n\t\tconst D2D_COLOR_F col = ColorFromColourAlpha(fore);\n\t\tif (pBrush) {\n\t\t\tpBrush->SetColor(col);\n\t\t} else {\n\t\t\tconst HRESULT hr = pRenderTarget->CreateSolidColorBrush(col, &pBrush);\n\t\t\tif (!SUCCEEDED(hr)) {\n\t\t\t\tpBrush = nullptr;\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid SurfaceD2D::SetFontQuality(FontQuality extraFontFlag) {\n\tif ((fontQuality != extraFontFlag) && renderingParams) {\n\t\tfontQuality = extraFontFlag;\n\t\tconst D2D1_TEXT_ANTIALIAS_MODE aaMode = DWriteMapFontQuality(extraFontFlag);\n\t\tif (aaMode == D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE && renderingParams->customRenderingParams) {\n\t\t\tpRenderTarget->SetTextRenderingParams(renderingParams->customRenderingParams.Get());\n\t\t} else if (renderingParams->defaultRenderingParams) {\n\t\t\tpRenderTarget->SetTextRenderingParams(renderingParams->defaultRenderingParams.Get());\n\t\t}\n\t\tpRenderTarget->SetTextAntialiasMode(aaMode);\n\t}\n}\n\nint SurfaceD2D::LogPixelsY() {\n\treturn logPixelsY;\n}\n\nvoid SurfaceD2D::SetDeviceScaleFactor(const ID2D1RenderTarget *const pD2D1RenderTarget) noexcept {\n\tFLOAT dpiX = 0.f;\n\tFLOAT dpiY = 0.f;\n\tpD2D1RenderTarget->GetDpi(&dpiX, &dpiY);\n\tdeviceScaleFactor = static_cast<int>(dpiX / dpiDefault);\n}\n\nint SurfaceD2D::PixelDivisions() {\n\treturn deviceScaleFactor;\n}\n\nint SurfaceD2D::DeviceHeightFont(int points) {\n\treturn ::MulDiv(points, LogPixelsY(), pointsPerInch);\n}\n\nconstexpr FLOAT mitreLimit = 4.0f;\n\nvoid SurfaceD2D::LineDraw(Point start, Point end, Stroke stroke) {\n\tD2DPenColourAlpha(stroke.colour);\n\n\tD2D1_STROKE_STYLE_PROPERTIES strokeProps {};\n\tstrokeProps.startCap = D2D1_CAP_STYLE_SQUARE;\n\tstrokeProps.endCap = D2D1_CAP_STYLE_SQUARE;\n\tstrokeProps.dashCap = D2D1_CAP_STYLE_FLAT;\n\tstrokeProps.lineJoin = D2D1_LINE_JOIN_MITER;\n\tstrokeProps.miterLimit = mitreLimit;\n\tstrokeProps.dashStyle = D2D1_DASH_STYLE_SOLID;\n\tstrokeProps.dashOffset = 0;\n\n\t// get the stroke style to apply\n\tif (const StrokeStyle pStrokeStyle = StrokeStyleCreate(strokeProps)) {\n\t\tpRenderTarget->DrawLine(\n\t\t\tDPointFromPoint(start),\n\t\t\tDPointFromPoint(end), pBrush.Get(), stroke.WidthF(), pStrokeStyle.Get());\n\t}\n}\n\nGeometry SurfaceD2D::GeometricFigure(const Point *pts, size_t npts, D2D1_FIGURE_BEGIN figureBegin) noexcept {\n\tGeometry geometry = GeometryCreate();\n\tif (geometry) {\n\t\tif (const GeometrySink sink = GeometrySinkCreate(geometry.Get())) {\n\t\t\tsink->BeginFigure(DPointFromPoint(pts[0]), figureBegin);\n\t\t\tfor (size_t i = 1; i < npts; i++) {\n\t\t\t\tsink->AddLine(DPointFromPoint(pts[i]));\n\t\t\t}\n\t\t\tsink->EndFigure((figureBegin == D2D1_FIGURE_BEGIN_FILLED) ?\n\t\t\t\tD2D1_FIGURE_END_CLOSED : D2D1_FIGURE_END_OPEN);\n\t\t\tsink->Close();\n\t\t}\n\t}\n\treturn geometry;\n}\n\nvoid SurfaceD2D::PolyLine(const Point *pts, size_t npts, Stroke stroke) {\n\tPLATFORM_ASSERT(pRenderTarget && (npts > 1));\n\tif (!pRenderTarget || (npts <= 1)) {\n\t\treturn;\n\t}\n\n\tconst Geometry geometry = GeometricFigure(pts, npts, D2D1_FIGURE_BEGIN_HOLLOW);\n\tPLATFORM_ASSERT(geometry);\n\tif (!geometry) {\n\t\treturn;\n\t}\n\n\tD2DPenColourAlpha(stroke.colour);\n\tD2D1_STROKE_STYLE_PROPERTIES strokeProps {};\n\tstrokeProps.startCap = D2D1_CAP_STYLE_ROUND;\n\tstrokeProps.endCap = D2D1_CAP_STYLE_ROUND;\n\tstrokeProps.dashCap = D2D1_CAP_STYLE_FLAT;\n\tstrokeProps.lineJoin = D2D1_LINE_JOIN_MITER;\n\tstrokeProps.miterLimit = mitreLimit;\n\tstrokeProps.dashStyle = D2D1_DASH_STYLE_SOLID;\n\tstrokeProps.dashOffset = 0;\n\n\t// get the stroke style to apply\n\tif (const StrokeStyle pStrokeStyle = StrokeStyleCreate(strokeProps)) {\n\t\tpRenderTarget->DrawGeometry(geometry.Get(), pBrush.Get(), stroke.WidthF(), pStrokeStyle.Get());\n\t}\n}\n\nvoid SurfaceD2D::Polygon(const Point *pts, size_t npts, FillStroke fillStroke) {\n\tPLATFORM_ASSERT(pRenderTarget && (npts > 2));\n\tif (pRenderTarget) {\n\t\tconst Geometry geometry = GeometricFigure(pts, npts, D2D1_FIGURE_BEGIN_FILLED);\n\t\tPLATFORM_ASSERT(geometry);\n\t\tif (geometry) {\n\t\t\tD2DPenColourAlpha(fillStroke.fill.colour);\n\t\t\tpRenderTarget->FillGeometry(geometry.Get(), pBrush.Get());\n\t\t\tD2DPenColourAlpha(fillStroke.stroke.colour);\n\t\t\tpRenderTarget->DrawGeometry(geometry.Get(), pBrush.Get(), fillStroke.stroke.WidthF());\n\t\t}\n\t}\n}\n\nvoid SurfaceD2D::RectangleDraw(PRectangle rc, FillStroke fillStroke) {\n\tif (!pRenderTarget)\n\t\treturn;\n\tconst D2D1_RECT_F rect = RectangleFromPRectangle(rc);\n\tconst D2D1_RECT_F rectFill = RectangleInset(rect, fillStroke.stroke.WidthF());\n\tconst float halfStroke = fillStroke.stroke.WidthF() / 2.0f;\n\tconst D2D1_RECT_F rectOutline = RectangleInset(rect, halfStroke);\n\n\tD2DPenColourAlpha(fillStroke.fill.colour);\n\tpRenderTarget->FillRectangle(&rectFill, pBrush.Get());\n\tD2DPenColourAlpha(fillStroke.stroke.colour);\n\tpRenderTarget->DrawRectangle(&rectOutline, pBrush.Get(), fillStroke.stroke.WidthF());\n}\n\nvoid SurfaceD2D::RectangleFrame(PRectangle rc, Stroke stroke) {\n\tif (pRenderTarget) {\n\t\tconst XYPOSITION halfStroke = stroke.width / 2.0f;\n\t\tconst D2D1_RECT_F rectangle1 = RectangleFromPRectangle(rc.Inset(halfStroke));\n\t\tD2DPenColourAlpha(stroke.colour);\n\t\tpRenderTarget->DrawRectangle(&rectangle1, pBrush.Get(), stroke.WidthF());\n\t}\n}\n\nvoid SurfaceD2D::FillRectangle(PRectangle rc, Fill fill) {\n\tif (pRenderTarget) {\n\t\tD2DPenColourAlpha(fill.colour);\n\t\tconst D2D1_RECT_F rectangle = RectangleFromPRectangle(rc);\n\t\tpRenderTarget->FillRectangle(&rectangle, pBrush.Get());\n\t}\n}\n\nvoid SurfaceD2D::FillRectangleAligned(PRectangle rc, Fill fill) {\n\tFillRectangle(PixelAlign(rc, PixelDivisions()), fill);\n}\n\nvoid SurfaceD2D::FillRectangle(PRectangle rc, Surface &surfacePattern) {\n\tSurfaceD2D *psurfOther = dynamic_cast<SurfaceD2D *>(&surfacePattern);\n\tPLATFORM_ASSERT(psurfOther);\n\tif (!psurfOther) {\n\t\tthrow std::runtime_error(\"SurfaceD2D::FillRectangle: wrong Surface type.\");\n\t}\n\tComPtr<ID2D1Bitmap> pBitmap;\n\tHRESULT hr = psurfOther->GetBitmap(pBitmap.GetAddressOf());\n\tif (SUCCEEDED(hr) && pBitmap) {\n\t\tComPtr<ID2D1BitmapBrush> pBitmapBrush;\n\t\tconst D2D1_BITMAP_BRUSH_PROPERTIES brushProperties =\n\t        D2D1::BitmapBrushProperties(D2D1_EXTEND_MODE_WRAP, D2D1_EXTEND_MODE_WRAP,\n\t\t\tD2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR);\n\t\t// Create the bitmap brush.\n\t\thr = pRenderTarget->CreateBitmapBrush(pBitmap.Get(), brushProperties, pBitmapBrush.GetAddressOf());\n\t\tif (SUCCEEDED(hr) && pBitmapBrush) {\n\t\t\tpRenderTarget->FillRectangle(\n\t\t\t\tRectangleFromPRectangle(rc),\n\t\t\t\tpBitmapBrush.Get());\n\t\t}\n\t}\n}\n\nvoid SurfaceD2D::RoundedRectangle(PRectangle rc, FillStroke fillStroke) {\n\tif (pRenderTarget) {\n\t\tconst FLOAT minDimension = static_cast<FLOAT>(std::min(rc.Width(), rc.Height())) / 2.0f;\n\t\tconst FLOAT radius = std::min(4.0f, minDimension);\n\t\tif (fillStroke.fill.colour == fillStroke.stroke.colour) {\n\t\t\tconst D2D1_ROUNDED_RECT roundedRectFill = {\n\t\t\t\tRectangleFromPRectangle(rc),\n\t\t\t\tradius, radius };\n\t\t\tD2DPenColourAlpha(fillStroke.fill.colour);\n\t\t\tpRenderTarget->FillRoundedRectangle(roundedRectFill, pBrush.Get());\n\t\t} else {\n\t\t\tconst D2D1_ROUNDED_RECT roundedRectFill = {\n\t\t\t\tRectangleFromPRectangle(rc.Inset(1.0)),\n\t\t\t\tradius-1, radius-1 };\n\t\t\tD2DPenColourAlpha(fillStroke.fill.colour);\n\t\t\tpRenderTarget->FillRoundedRectangle(roundedRectFill, pBrush.Get());\n\n\t\t\tconst D2D1_ROUNDED_RECT roundedRect = {\n\t\t\t\tRectangleFromPRectangle(rc.Inset(0.5)),\n\t\t\t\tradius, radius };\n\t\t\tD2DPenColourAlpha(fillStroke.stroke.colour);\n\t\t\tpRenderTarget->DrawRoundedRectangle(roundedRect, pBrush.Get(), fillStroke.stroke.WidthF());\n\t\t}\n\t}\n}\n\nvoid SurfaceD2D::AlphaRectangle(PRectangle rc, XYPOSITION cornerSize, FillStroke fillStroke) {\n\tconst D2D1_RECT_F rect = RectangleFromPRectangle(rc);\n\tconst D2D1_RECT_F rectFill = RectangleInset(rect, fillStroke.stroke.WidthF());\n\tconst float halfStroke = fillStroke.stroke.WidthF() / 2.0f;\n\tconst D2D1_RECT_F rectOutline = RectangleInset(rect, halfStroke);\n\tif (pRenderTarget) {\n\t\tif (cornerSize == 0) {\n\t\t\t// When corner size is zero, draw square rectangle to prevent blurry pixels at corners\n\t\t\tD2DPenColourAlpha(fillStroke.fill.colour);\n\t\t\tpRenderTarget->FillRectangle(rectFill, pBrush.Get());\n\n\t\t\tD2DPenColourAlpha(fillStroke.stroke.colour);\n\t\t\tpRenderTarget->DrawRectangle(rectOutline, pBrush.Get(), fillStroke.stroke.WidthF());\n\t\t} else {\n\t\t\tconst float cornerSizeF = static_cast<float>(cornerSize);\n\t\t\tconst D2D1_ROUNDED_RECT roundedRectFill = {\n\t\t\t\trectFill, cornerSizeF - 1.0f, cornerSizeF - 1.0f };\n\t\t\tD2DPenColourAlpha(fillStroke.fill.colour);\n\t\t\tpRenderTarget->FillRoundedRectangle(roundedRectFill, pBrush.Get());\n\n\t\t\tconst D2D1_ROUNDED_RECT roundedRect = {\n\t\t\t\trectOutline, cornerSizeF, cornerSizeF};\n\t\t\tD2DPenColourAlpha(fillStroke.stroke.colour);\n\t\t\tpRenderTarget->DrawRoundedRectangle(roundedRect, pBrush.Get(), fillStroke.stroke.WidthF());\n\t\t}\n\t}\n}\n\nvoid SurfaceD2D::GradientRectangle(PRectangle rc, const std::vector<ColourStop> &stops, GradientOptions options) {\n\tif (pRenderTarget) {\n\t\tD2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES lgbp {\n\t\t\tDPointFromPoint(Point(rc.left, rc.top)), {}\n\t\t};\n\t\tswitch (options) {\n\t\tcase GradientOptions::leftToRight:\n\t\t\tlgbp.endPoint = DPointFromPoint(Point(rc.right, rc.top));\n\t\t\tbreak;\n\t\tcase GradientOptions::topToBottom:\n\t\tdefault:\n\t\t\tlgbp.endPoint = DPointFromPoint(Point(rc.left, rc.bottom));\n\t\t\tbreak;\n\t\t}\n\n\t\tstd::vector<D2D1_GRADIENT_STOP> gradientStops;\n\t\tfor (const ColourStop &stop : stops) {\n\t\t\tgradientStops.push_back({ static_cast<FLOAT>(stop.position), ColorFromColourAlpha(stop.colour) });\n\t\t}\n\t\tComPtr<ID2D1GradientStopCollection> pGradientStops;\n\t\tHRESULT hr = pRenderTarget->CreateGradientStopCollection(\n\t\t\tgradientStops.data(), static_cast<UINT32>(gradientStops.size()), pGradientStops.GetAddressOf());\n\t\tif (FAILED(hr) || !pGradientStops) {\n\t\t\treturn;\n\t\t}\n\t\tComPtr<ID2D1LinearGradientBrush> pBrushLinear;\n\t\thr = pRenderTarget->CreateLinearGradientBrush(\n\t\t\tlgbp, pGradientStops.Get(), pBrushLinear.GetAddressOf());\n\t\tif (SUCCEEDED(hr) && pBrushLinear) {\n\t\t\tconst D2D1_RECT_F rectangle = RectangleFromPRectangle(PRectangle(\n\t\t\t\tstd::round(rc.left), rc.top, std::round(rc.right), rc.bottom));\n\t\t\tpRenderTarget->FillRectangle(&rectangle, pBrushLinear.Get());\n\t\t}\n\t}\n}\n\nvoid SurfaceD2D::DrawRGBAImage(PRectangle rc, int width, int height, const unsigned char *pixelsImage) {\n\tif (pRenderTarget) {\n\t\tif (rc.Width() > width)\n\t\t\trc.left += std::floor((rc.Width() - width) / 2);\n\t\trc.right = rc.left + width;\n\t\tif (rc.Height() > height)\n\t\t\trc.top += std::floor((rc.Height() - height) / 2);\n\t\trc.bottom = rc.top + height;\n\n\t\tstd::vector<unsigned char> image(RGBAImage::bytesPerPixel * height * width);\n\t\tRGBAImage::BGRAFromRGBA(image.data(), pixelsImage, static_cast<ptrdiff_t>(height) * width);\n\n\t\tComPtr<ID2D1Bitmap> bitmap;\n\t\tconst D2D1_SIZE_U size = D2D1::SizeU(width, height);\n\t\tconst D2D1_BITMAP_PROPERTIES props = {{DXGI_FORMAT_B8G8R8A8_UNORM,\n\t\t    D2D1_ALPHA_MODE_PREMULTIPLIED}, 72.0, 72.0};\n\t\tconst HRESULT hr = pRenderTarget->CreateBitmap(size, image.data(),\n                  width * 4, &props, bitmap.GetAddressOf());\n\t\tif (SUCCEEDED(hr)) {\n\t\t\tconst D2D1_RECT_F rcDestination = RectangleFromPRectangle(rc);\n\t\t\tpRenderTarget->DrawBitmap(bitmap.Get(), rcDestination);\n\t\t}\n\t}\n}\n\nvoid SurfaceD2D::Ellipse(PRectangle rc, FillStroke fillStroke) {\n\tif (!pRenderTarget)\n\t\treturn;\n\tconst D2D1_POINT_2F centre = DPointFromPoint(rc.Centre());\n\n\tconst FLOAT radiusFill = static_cast<FLOAT>((rc.Width() / 2.0f) - fillStroke.stroke.width);\n\tconst D2D1_ELLIPSE ellipseFill = { centre, radiusFill, radiusFill };\n\n\tD2DPenColourAlpha(fillStroke.fill.colour);\n\tpRenderTarget->FillEllipse(ellipseFill, pBrush.Get());\n\n\tconst FLOAT radiusOutline = static_cast<FLOAT>((rc.Width() / 2.0f) - (fillStroke.stroke.width / 2.0f));\n\tconst D2D1_ELLIPSE ellipseOutline = { centre, radiusOutline, radiusOutline };\n\n\tD2DPenColourAlpha(fillStroke.stroke.colour);\n\tpRenderTarget->DrawEllipse(ellipseOutline, pBrush.Get(), fillStroke.stroke.WidthF());\n}\n\nvoid SurfaceD2D::Stadium(PRectangle rc, FillStroke fillStroke, Ends ends) {\n\tif (!pRenderTarget)\n\t\treturn;\n\tif (rc.Width() < rc.Height()) {\n\t\t// Can't draw nice ends so just draw a rectangle\n\t\tRectangleDraw(rc, fillStroke);\n\t\treturn;\n\t}\n\tconst FLOAT radius = static_cast<FLOAT>(rc.Height() / 2.0);\n\tconst FLOAT radiusFill = radius - fillStroke.stroke.WidthF();\n\tconst FLOAT halfStroke = fillStroke.stroke.WidthF() / 2.0f;\n\tif (ends == Surface::Ends::semiCircles) {\n\t\tconst D2D1_RECT_F rect = RectangleFromPRectangle(rc);\n\t\tconst D2D1_ROUNDED_RECT roundedRectFill = { RectangleInset(rect, fillStroke.stroke.WidthF()),\n\t\t\tradiusFill, radiusFill };\n\t\tD2DPenColourAlpha(fillStroke.fill.colour);\n\t\tpRenderTarget->FillRoundedRectangle(roundedRectFill, pBrush.Get());\n\n\t\tconst D2D1_ROUNDED_RECT roundedRect = { RectangleInset(rect, halfStroke),\n\t\t\tradius, radius };\n\t\tD2DPenColourAlpha(fillStroke.stroke.colour);\n\t\tpRenderTarget->DrawRoundedRectangle(roundedRect, pBrush.Get(), fillStroke.stroke.WidthF());\n\t} else {\n\t\tconst Ends leftSide = static_cast<Ends>(static_cast<int>(ends) & 0xf);\n\t\tconst Ends rightSide = static_cast<Ends>(static_cast<int>(ends) & 0xf0);\n\t\tPRectangle rcInner = rc;\n\t\trcInner.left += radius;\n\t\trcInner.right -= radius;\n\t\tconst Geometry pathGeometry = GeometryCreate();\n\t\tif (!pathGeometry)\n\t\t\treturn;\n\t\tif (const GeometrySink pSink = GeometrySinkCreate(pathGeometry.Get())) {\n\t\t\tswitch (leftSide) {\n\t\t\t\tcase Ends::leftFlat:\n\t\t\t\t\tpSink->BeginFigure(DPointFromPoint(Point(rc.left + halfStroke, rc.top + halfStroke)), D2D1_FIGURE_BEGIN_FILLED);\n\t\t\t\t\tpSink->AddLine(DPointFromPoint(Point(rc.left + halfStroke, rc.bottom - halfStroke)));\n\t\t\t\t\tbreak;\n\t\t\t\tcase Ends::leftAngle:\n\t\t\t\t\tpSink->BeginFigure(DPointFromPoint(Point(rcInner.left + halfStroke, rc.top + halfStroke)), D2D1_FIGURE_BEGIN_FILLED);\n\t\t\t\t\tpSink->AddLine(DPointFromPoint(Point(rc.left + halfStroke, rc.Centre().y)));\n\t\t\t\t\tpSink->AddLine(DPointFromPoint(Point(rcInner.left + halfStroke, rc.bottom - halfStroke)));\n\t\t\t\t\tbreak;\n\t\t\t\tcase Ends::semiCircles:\n\t\t\t\tdefault: {\n\t\t\t\t\t\tpSink->BeginFigure(DPointFromPoint(Point(rcInner.left + halfStroke, rc.top + halfStroke)), D2D1_FIGURE_BEGIN_FILLED);\n\t\t\t\t\t\tD2D1_ARC_SEGMENT segment{};\n\t\t\t\t\t\tsegment.point = DPointFromPoint(Point(rcInner.left + halfStroke, rc.bottom - halfStroke));\n\t\t\t\t\t\tsegment.size = D2D1::SizeF(radiusFill, radiusFill);\n\t\t\t\t\t\tsegment.rotationAngle = 0.0f;\n\t\t\t\t\t\tsegment.sweepDirection = D2D1_SWEEP_DIRECTION_COUNTER_CLOCKWISE;\n\t\t\t\t\t\tsegment.arcSize = D2D1_ARC_SIZE_SMALL;\n\t\t\t\t\t\tpSink->AddArc(segment);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tswitch (rightSide) {\n\t\t\tcase Ends::rightFlat:\n\t\t\t\tpSink->AddLine(DPointFromPoint(Point(rc.right - halfStroke, rc.bottom - halfStroke)));\n\t\t\t\tpSink->AddLine(DPointFromPoint(Point(rc.right - halfStroke, rc.top + halfStroke)));\n\t\t\t\tbreak;\n\t\t\tcase Ends::rightAngle:\n\t\t\t\tpSink->AddLine(DPointFromPoint(Point(rcInner.right - halfStroke, rc.bottom - halfStroke)));\n\t\t\t\tpSink->AddLine(DPointFromPoint(Point(rc.right - halfStroke, rc.Centre().y)));\n\t\t\t\tpSink->AddLine(DPointFromPoint(Point(rcInner.right - halfStroke, rc.top + halfStroke)));\n\t\t\t\tbreak;\n\t\t\tcase Ends::semiCircles:\n\t\t\tdefault: {\n\t\t\t\t\tpSink->AddLine(DPointFromPoint(Point(rcInner.right - halfStroke, rc.bottom - halfStroke)));\n\t\t\t\t\tD2D1_ARC_SEGMENT segment{};\n\t\t\t\t\tsegment.point = DPointFromPoint(Point(rcInner.right - halfStroke, rc.top + halfStroke));\n\t\t\t\t\tsegment.size = D2D1::SizeF(radiusFill, radiusFill);\n\t\t\t\t\tsegment.rotationAngle = 0.0f;\n\t\t\t\t\tsegment.sweepDirection = D2D1_SWEEP_DIRECTION_COUNTER_CLOCKWISE;\n\t\t\t\t\tsegment.arcSize = D2D1_ARC_SIZE_SMALL;\n\t\t\t\t\tpSink->AddArc(segment);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tpSink->EndFigure(D2D1_FIGURE_END_CLOSED);\n\n\t\t\tpSink->Close();\n\t\t}\n\t\tD2DPenColourAlpha(fillStroke.fill.colour);\n\t\tpRenderTarget->FillGeometry(pathGeometry.Get(), pBrush.Get());\n\t\tD2DPenColourAlpha(fillStroke.stroke.colour);\n\t\tpRenderTarget->DrawGeometry(pathGeometry.Get(), pBrush.Get(), fillStroke.stroke.WidthF());\n\t}\n}\n\nvoid SurfaceD2D::Copy(PRectangle rc, Point from, Surface &surfaceSource) {\n\tSurfaceD2D &surfOther = dynamic_cast<SurfaceD2D &>(surfaceSource);\n\tComPtr<ID2D1Bitmap> pBitmap;\n\tconst HRESULT hr = surfOther.GetBitmap(pBitmap.GetAddressOf());\n\tif (SUCCEEDED(hr) && pBitmap) {\n\t\tconst D2D1_RECT_F rcDestination = RectangleFromPRectangle(rc);\n\t\tconst D2D1_RECT_F rcSource = RectangleFromPRectangle(PRectangle(\n\t\t\tfrom.x, from.y, from.x + rc.Width(), from.y + rc.Height()));\n\t\tpRenderTarget->DrawBitmap(pBitmap.Get(), rcDestination, 1.0f,\n\t\t\tD2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR, rcSource);\n\t}\n}\n\nclass BlobInline final : public IDWriteInlineObject {\n\tXYPOSITION width;\n\n\t// IUnknown\n\tSTDMETHODIMP QueryInterface(REFIID riid, PVOID *ppv) override;\n\tSTDMETHODIMP_(ULONG)AddRef() override;\n\tSTDMETHODIMP_(ULONG)Release() override;\n\n\t// IDWriteInlineObject\n\tCOM_DECLSPEC_NOTHROW STDMETHODIMP Draw(\n\t\tvoid *clientDrawingContext,\n\t\tIDWriteTextRenderer *renderer,\n\t\tFLOAT originX,\n\t\tFLOAT originY,\n\t\tBOOL isSideways,\n\t\tBOOL isRightToLeft,\n\t\tIUnknown *clientDrawingEffect\n\t\t) override;\n\tCOM_DECLSPEC_NOTHROW STDMETHODIMP GetMetrics(DWRITE_INLINE_OBJECT_METRICS *metrics) override;\n\tCOM_DECLSPEC_NOTHROW STDMETHODIMP GetOverhangMetrics(DWRITE_OVERHANG_METRICS *overhangs) override;\n\tCOM_DECLSPEC_NOTHROW STDMETHODIMP GetBreakConditions(\n\t\tDWRITE_BREAK_CONDITION *breakConditionBefore,\n\t\tDWRITE_BREAK_CONDITION *breakConditionAfter) override;\npublic:\n\texplicit BlobInline(XYPOSITION width_=0.0f) noexcept : width(width_) {\n\t}\n};\n\n/// Implement IUnknown\nSTDMETHODIMP BlobInline::QueryInterface(REFIID riid, PVOID *ppv) {\n\tif (!ppv)\n\t\treturn E_POINTER;\n\t// Never called so not checked.\n\t*ppv = nullptr;\n\tif (riid == IID_IUnknown)\n\t\t*ppv = this;\n\tif (riid == __uuidof(IDWriteInlineObject))\n\t\t*ppv = this;\n\tif (!*ppv)\n\t\treturn E_NOINTERFACE;\n\treturn S_OK;\n}\n\nSTDMETHODIMP_(ULONG) BlobInline::AddRef() {\n\t// Lifetime tied to Platform methods so ignore any reference operations.\n\treturn 1;\n}\n\nSTDMETHODIMP_(ULONG) BlobInline::Release() {\n\t// Lifetime tied to Platform methods so ignore any reference operations.\n\treturn 1;\n}\n\n/// Implement IDWriteInlineObject\nCOM_DECLSPEC_NOTHROW HRESULT STDMETHODCALLTYPE BlobInline::Draw(\n\tvoid*,\n\tIDWriteTextRenderer*,\n\tFLOAT,\n\tFLOAT,\n\tBOOL,\n\tBOOL,\n\tIUnknown*) {\n\t// Since not performing drawing, not necessary to implement\n\t// Could be implemented by calling back into platform-independent code.\n\t// This would allow more of the drawing to be mediated through DirectWrite.\n\treturn S_OK;\n}\n\nCOM_DECLSPEC_NOTHROW HRESULT STDMETHODCALLTYPE BlobInline::GetMetrics(\n\tDWRITE_INLINE_OBJECT_METRICS *metrics\n) {\n\tif (!metrics)\n\t\treturn E_POINTER;\n\tmetrics->width = static_cast<FLOAT>(width);\n\tmetrics->height = 2;\n\tmetrics->baseline = 1;\n\tmetrics->supportsSideways = FALSE;\n\treturn S_OK;\n}\n\nCOM_DECLSPEC_NOTHROW HRESULT STDMETHODCALLTYPE BlobInline::GetOverhangMetrics(\n\tDWRITE_OVERHANG_METRICS *overhangs\n) {\n\tif (!overhangs)\n\t\treturn E_POINTER;\n\toverhangs->left = 0;\n\toverhangs->top = 0;\n\toverhangs->right = 0;\n\toverhangs->bottom = 0;\n\treturn S_OK;\n}\n\nCOM_DECLSPEC_NOTHROW HRESULT STDMETHODCALLTYPE BlobInline::GetBreakConditions(\n\tDWRITE_BREAK_CONDITION *breakConditionBefore,\n\tDWRITE_BREAK_CONDITION *breakConditionAfter\n) {\n\tif (!breakConditionBefore || !breakConditionAfter)\n\t\treturn E_POINTER;\n\t// Since not performing 2D layout, not necessary to implement\n\t*breakConditionBefore = DWRITE_BREAK_CONDITION_NEUTRAL;\n\t*breakConditionAfter = DWRITE_BREAK_CONDITION_NEUTRAL;\n\treturn S_OK;\n}\n\nclass ScreenLineLayout : public IScreenLineLayout {\n\tstd::string text;\n\tstd::wstring buffer;\n\tstd::vector<BlobInline> blobs;\n\t// textLayout depends on blobs so must be declared after blobs so it is destroyed before blobs.\n\tTextLayout textLayout;\n\tstatic void FillTextLayoutFormats(const IScreenLine *screenLine, IDWriteTextLayout *textLayout, std::vector<BlobInline> &blobs);\n\tstatic std::wstring ReplaceRepresentation(std::string_view text);\n\tstatic UINT32 GetPositionInLayout(std::string_view text, size_t position);\npublic:\n\texplicit ScreenLineLayout(const IScreenLine *screenLine);\n\t// Deleted so ScreenLineLayout objects can not be copied\n\tScreenLineLayout(const ScreenLineLayout &) = delete;\n\tScreenLineLayout(ScreenLineLayout &&) = delete;\n\tScreenLineLayout &operator=(const ScreenLineLayout &) = delete;\n\tScreenLineLayout &operator=(ScreenLineLayout &&) = delete;\n\t~ScreenLineLayout() noexcept override = default;\n\tsize_t PositionFromX(XYPOSITION xDistance, bool charPosition) override;\n\tXYPOSITION XFromPosition(size_t caretPosition) override;\n\tstd::vector<Interval> FindRangeIntervals(size_t start, size_t end) override;\n};\n\n// Each char can have its own style, so we fill the textLayout with the textFormat of each char\n\nvoid ScreenLineLayout::FillTextLayoutFormats(const IScreenLine *screenLine, IDWriteTextLayout *textLayout, std::vector<BlobInline> &blobs) {\n\t// Reserve enough entries up front so they are not moved and the pointers handed\n\t// to textLayout remain valid.\n\tconst ptrdiff_t numRepresentations = screenLine->RepresentationCount();\n\tstd::string_view text = screenLine->Text();\n\tconst ptrdiff_t numTabs = std::count(std::begin(text), std::end(text), '\\t');\n\tblobs.reserve(numRepresentations + numTabs);\n\n\tUINT32 layoutPosition = 0;\n\n\tfor (size_t bytePosition = 0; bytePosition < screenLine->Length();) {\n\t\tconst unsigned char uch = screenLine->Text()[bytePosition];\n\t\tconst unsigned int byteCount = UTF8BytesOfLead[uch];\n\t\tconst UINT32 codeUnits = UTF16LengthFromUTF8ByteCount(byteCount);\n\t\tconst DWRITE_TEXT_RANGE textRange = { layoutPosition, codeUnits };\n\n\t\tXYPOSITION representationWidth = screenLine->RepresentationWidth(bytePosition);\n\t\tif ((representationWidth == 0.0f) && (screenLine->Text()[bytePosition] == '\\t')) {\n\t\t\tD2D1_POINT_2F realPt {};\n\t\t\tDWRITE_HIT_TEST_METRICS realCaretMetrics {};\n\t\t\ttextLayout->HitTestTextPosition(\n\t\t\t\tlayoutPosition,\n\t\t\t\tfalse, // trailing if false, else leading edge\n\t\t\t\t&realPt.x,\n\t\t\t\t&realPt.y,\n\t\t\t\t&realCaretMetrics\n\t\t\t);\n\n\t\t\tconst XYPOSITION nextTab = screenLine->TabPositionAfter(realPt.x);\n\t\t\trepresentationWidth = nextTab - realPt.x;\n\t\t}\n\t\tif (representationWidth > 0.0f) {\n\t\t\tblobs.emplace_back(representationWidth);\n\t\t\ttextLayout->SetInlineObject(&blobs.back(), textRange);\n\t\t};\n\n\t\tconst FontDirectWrite *pfm =\n\t\t\tdynamic_cast<const FontDirectWrite *>(screenLine->FontOfPosition(bytePosition));\n\t\tif (!pfm) {\n\t\t\tthrow std::runtime_error(\"FillTextLayoutFormats: wrong Font type.\");\n\t\t}\n\n\t\tconst unsigned int fontFamilyNameSize = pfm->pTextFormat->GetFontFamilyNameLength();\n\t\tstd::wstring fontFamilyName(fontFamilyNameSize, 0);\n\t\tconst HRESULT hrFamily = pfm->pTextFormat->GetFontFamilyName(fontFamilyName.data(), fontFamilyNameSize + 1);\n\t\tif (SUCCEEDED(hrFamily)) {\n\t\t\ttextLayout->SetFontFamilyName(fontFamilyName.c_str(), textRange);\n\t\t}\n\n\t\ttextLayout->SetFontSize(pfm->pTextFormat->GetFontSize(), textRange);\n\t\ttextLayout->SetFontWeight(pfm->pTextFormat->GetFontWeight(), textRange);\n\t\ttextLayout->SetFontStyle(pfm->pTextFormat->GetFontStyle(), textRange);\n\n\t\tconst unsigned int localeNameSize = pfm->pTextFormat->GetLocaleNameLength();\n\t\tstd::wstring localeName(localeNameSize, 0);\n\t\tconst HRESULT hrLocale = pfm->pTextFormat->GetLocaleName(localeName.data(), localeNameSize + 1);\n\t\tif (SUCCEEDED(hrLocale)) {\n\t\t\ttextLayout->SetLocaleName(localeName.c_str(), textRange);\n\t\t}\n\n\t\ttextLayout->SetFontStretch(pfm->pTextFormat->GetFontStretch(), textRange);\n\n\t\tIDWriteFontCollection *fontCollection = nullptr;\n\t\tif (SUCCEEDED(pfm->pTextFormat->GetFontCollection(&fontCollection))) {\n\t\t\ttextLayout->SetFontCollection(fontCollection, textRange);\n\t\t}\n\n\t\tbytePosition += byteCount;\n\t\tlayoutPosition += codeUnits;\n\t}\n\n}\n\n/* Convert to a wide character string and replace tabs with X to stop DirectWrite tab expansion */\n\nstd::wstring ScreenLineLayout::ReplaceRepresentation(std::string_view text) {\n\tconst TextWide wideText(text, CpUtf8);\n\tstd::wstring ws(wideText.buffer, wideText.tlen);\n\tstd::replace(ws.begin(), ws.end(), L'\\t', L'X');\n\treturn ws;\n}\n\n// Finds the position in the wide character version of the text.\n\nUINT32 ScreenLineLayout::GetPositionInLayout(std::string_view text, size_t position) {\n\tconst std::string_view textUptoPosition = text.substr(0, position);\n\treturn static_cast<UINT32>(UTF16Length(textUptoPosition));\n}\n\nScreenLineLayout::ScreenLineLayout(const IScreenLine *screenLine) {\n\t// If the text is empty, then no need to go through this function\n\tif (!screenLine || !screenLine->Length())\n\t\treturn;\n\n\ttext = screenLine->Text();\n\n\t// Get textFormat\n\tconst FontDirectWrite *pfm = FontDirectWrite::Cast(screenLine->FontOfPosition(0));\n\tif (!pfm->pTextFormat) {\n\t\treturn;\n\t}\n\n\t// Convert the string to wstring and replace the original control characters with their representative chars.\n\tbuffer = ReplaceRepresentation(screenLine->Text());\n\n\t// Create a text layout\n\ttextLayout = LayoutCreate(\n\t\tbuffer,\n\t\tpfm->pTextFormat.Get(),\n\t\tstatic_cast<FLOAT>(screenLine->Width()),\n\t\tstatic_cast<FLOAT>(screenLine->Height()));\n\tif (!textLayout) {\n\t\treturn;\n\t}\n\n\t// Fill the textLayout chars with their own formats\n\tFillTextLayoutFormats(screenLine, textLayout.Get(), blobs);\n}\n\n// Get the position from the provided x\n\nsize_t ScreenLineLayout::PositionFromX(XYPOSITION xDistance, bool charPosition) {\n\tif (!textLayout) {\n\t\treturn 0;\n\t}\n\n\t// Returns the text position corresponding to the mouse x,y.\n\t// If hitting the trailing side of a cluster, return the\n\t// leading edge of the following text position.\n\n\tBOOL isTrailingHit = FALSE;\n\tBOOL isInside = FALSE;\n\tDWRITE_HIT_TEST_METRICS caretMetrics {};\n\n\ttextLayout->HitTestPoint(\n\t\tstatic_cast<FLOAT>(xDistance),\n\t\t0.0f,\n\t\t&isTrailingHit,\n\t\t&isInside,\n\t\t&caretMetrics\n\t);\n\n\tDWRITE_HIT_TEST_METRICS hitTestMetrics {};\n\tif (isTrailingHit) {\n\t\tFLOAT caretX = 0.0f;\n\t\tFLOAT caretY = 0.0f;\n\n\t\t// Uses hit-testing to align the current caret position to a whole cluster,\n\t\t// rather than residing in the middle of a base character + diacritic,\n\t\t// surrogate pair, or character + UVS.\n\n\t\t// Align the caret to the nearest whole cluster.\n\t\ttextLayout->HitTestTextPosition(\n\t\t\tcaretMetrics.textPosition,\n\t\t\tfalse,\n\t\t\t&caretX,\n\t\t\t&caretY,\n\t\t\t&hitTestMetrics\n\t\t);\n\t}\n\n\tsize_t pos;\n\tif (charPosition) {\n\t\tpos = isTrailingHit ? hitTestMetrics.textPosition : caretMetrics.textPosition;\n\t} else {\n\t\tpos = isTrailingHit ? static_cast<size_t>(hitTestMetrics.textPosition) + hitTestMetrics.length : caretMetrics.textPosition;\n\t}\n\n\t// Get the character position in original string\n\treturn UTF8PositionFromUTF16Position(text, pos);\n}\n\n// Finds the point of the caret position\n\nXYPOSITION ScreenLineLayout::XFromPosition(size_t caretPosition) {\n\tif (!textLayout) {\n\t\treturn 0.0;\n\t}\n\t// Convert byte positions to wchar_t positions\n\tconst UINT32 position = GetPositionInLayout(text, caretPosition);\n\n\t// Translate text character offset to point x,y.\n\tDWRITE_HIT_TEST_METRICS caretMetrics {};\n\tD2D1_POINT_2F pt {};\n\n\ttextLayout->HitTestTextPosition(\n\t\tposition,\n\t\tfalse, // trailing if false, else leading edge\n\t\t&pt.x,\n\t\t&pt.y,\n\t\t&caretMetrics\n\t);\n\n\treturn pt.x;\n}\n\n// Find the selection range rectangles\n\nstd::vector<Interval> ScreenLineLayout::FindRangeIntervals(size_t start, size_t end) {\n\tstd::vector<Interval> ret;\n\n\tif (!textLayout || (start == end)) {\n\t\treturn ret;\n\t}\n\n\t// Convert byte positions to wchar_t positions\n\tconst UINT32 startPos = GetPositionInLayout(text, start);\n\tconst UINT32 endPos = GetPositionInLayout(text, end);\n\n\t// Find selection range length\n\tconst UINT32 rangeLength = (endPos > startPos) ? (endPos - startPos) : (startPos - endPos);\n\n\t// Determine actual number of hit-test ranges\n\tUINT32 hitTestCount = 2;\t// Simple selection often produces just 2 hits\n\n\t// First try with 2 elements and if more needed, allocate.\n\tstd::vector<DWRITE_HIT_TEST_METRICS> hitTestMetrics(hitTestCount);\n\ttextLayout->HitTestTextRange(\n\t\tstartPos,\n\t\trangeLength,\n\t\t0, // x\n\t\t0, // y\n\t\thitTestMetrics.data(),\n\t\thitTestCount,\n\t\t&hitTestCount\n\t);\n\n\tif (hitTestCount == 0) {\n\t\treturn ret;\n\t}\n\n\tif (hitTestMetrics.size() < hitTestCount) {\n\t\t// Allocate enough room to return all hit-test metrics.\n\t\thitTestMetrics.resize(hitTestCount);\n\t\ttextLayout->HitTestTextRange(\n\t\t\tstartPos,\n\t\t\trangeLength,\n\t\t\t0, // x\n\t\t\t0, // y\n\t\t\thitTestMetrics.data(),\n\t\t\thitTestCount,\n\t\t\t&hitTestCount\n\t\t);\n\t}\n\n\t// Get the selection ranges behind the text.\n\n\tfor (size_t i = 0; i < hitTestCount; ++i) {\n\t\t// Store selection rectangle\n\t\tconst DWRITE_HIT_TEST_METRICS &htm = hitTestMetrics[i];\n\t\tconst Interval selectionInterval { htm.left, htm.left + htm.width };\n\t\tret.push_back(selectionInterval);\n\t}\n\n\treturn ret;\n}\n\nstd::unique_ptr<IScreenLineLayout> SurfaceD2D::Layout(const IScreenLine *screenLine) {\n\treturn std::make_unique<ScreenLineLayout>(screenLine);\n}\n\nvoid SurfaceD2D::DrawTextCommon(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, int codePageOverride, UINT fuOptions) {\n\tconst FontDirectWrite *pfm = FontDirectWrite::Cast(font_);\n\tif (pfm->pTextFormat && pRenderTarget && pBrush) {\n\t\t// Use Unicode calls\n\t\tconst int codePageDraw = codePageOverride ? codePageOverride : pfm->CodePageText(mode.codePage);\n\t\tconst TextWide tbuf(text, codePageDraw);\n\n\t\tSetFontQuality(pfm->extraFontFlag);\n\t\tif (fuOptions & ETO_CLIPPED) {\n\t\t\tconst D2D1_RECT_F rcClip = RectangleFromPRectangle(rc);\n\t\t\tpRenderTarget->PushAxisAlignedClip(rcClip, D2D1_ANTIALIAS_MODE_ALIASED);\n\t\t}\n\n\t\t// Explicitly creating a text layout appears a little faster\n\t\tTextLayout pTextLayout = LayoutCreate(\n\t\t\t\ttbuf.AsView(),\n\t\t\t\tpfm->pTextFormat.Get(),\n\t\t\t\tstatic_cast<FLOAT>(rc.Width()),\n\t\t\t\tstatic_cast<FLOAT>(rc.Height()));\n\t\tif (pTextLayout) {\n\t\t\tconst D2D1_POINT_2F origin = DPointFromPoint(Point(rc.left, ybase - pfm->yAscent));\n\t\t\tpRenderTarget->DrawTextLayout(origin, pTextLayout.Get(), pBrush.Get(), d2dDrawTextOptions);\n\t\t}\n\n\t\tif (fuOptions & ETO_CLIPPED) {\n\t\t\tpRenderTarget->PopAxisAlignedClip();\n\t\t}\n\t}\n}\n\nvoid SurfaceD2D::DrawTextNoClip(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text,\n\tColourRGBA fore, ColourRGBA back) {\n\tif (pRenderTarget) {\n\t\tFillRectangleAligned(rc, back);\n\t\tD2DPenColourAlpha(fore);\n\t\tDrawTextCommon(rc, font_, ybase, text, 0, ETO_OPAQUE);\n\t}\n}\n\nvoid SurfaceD2D::DrawTextClipped(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text,\n\tColourRGBA fore, ColourRGBA back) {\n\tif (pRenderTarget) {\n\t\tFillRectangleAligned(rc, back);\n\t\tD2DPenColourAlpha(fore);\n\t\tDrawTextCommon(rc, font_, ybase, text, 0, ETO_OPAQUE | ETO_CLIPPED);\n\t}\n}\n\nvoid SurfaceD2D::DrawTextTransparent(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text,\n\tColourRGBA fore) {\n\t// Avoid drawing spaces in transparent mode\n\tfor (const char ch : text) {\n\t\tif (ch != ' ') {\n\t\t\tif (pRenderTarget) {\n\t\t\t\tD2DPenColourAlpha(fore);\n\t\t\t\tDrawTextCommon(rc, font_, ybase, text, 0, 0);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t}\n}\n\nHRESULT MeasurePositions(TextPositions &poses, const TextWide &tbuf, IDWriteTextFormat *pTextFormat) {\n\tif (!pTextFormat) {\n\t\t// Unexpected failure like no access to DirectWrite so give up.\n\t\treturn E_FAIL;\n\t}\n\n\t// Initialize poses for safety.\n\tstd::fill(poses.buffer, poses.buffer + tbuf.tlen, 0.0f);\n\t// Create a layout\n\tTextLayout pTextLayout = LayoutCreate(tbuf.AsView(), pTextFormat);\n\tif (!pTextLayout) {\n\t\treturn E_FAIL;\n\t}\n\tVarBuffer<DWRITE_CLUSTER_METRICS, stackBufferLength> cm(tbuf.tlen);\n\tUINT32 count = 0;\n\tconst HRESULT hrGetCluster = pTextLayout->GetClusterMetrics(cm.buffer, tbuf.tlen, &count);\n\tif (!SUCCEEDED(hrGetCluster)) {\n\t\treturn hrGetCluster;\n\t}\n\tconst DWRITE_CLUSTER_METRICS * const clusterMetrics = cm.buffer;\n\t// A cluster may be more than one WCHAR, such as for \"ffi\" which is a ligature in the Candara font\n\tXYPOSITION position = 0.0;\n\tint ti=0;\n\tfor (unsigned int ci=0; ci<count; ci++) {\n\t\tfor (unsigned int inCluster=0; inCluster<clusterMetrics[ci].length; inCluster++) {\n\t\t\tconst float proportion = static_cast<float>(inCluster + 1) / clusterMetrics[ci].length;\n\t\t\tposes.buffer[ti++] = position + clusterMetrics[ci].width * proportion;\n\t\t}\n\t\tposition += clusterMetrics[ci].width;\n\t}\n\tPLATFORM_ASSERT(ti == tbuf.tlen);\n\treturn S_OK;\n}\n\nvoid SurfaceD2D::MeasureWidths(const Font *font_, std::string_view text, XYPOSITION *positions) {\n\tconst FontDirectWrite *pfm = FontDirectWrite::Cast(font_);\n\tconst int codePageText = pfm->CodePageText(mode.codePage);\n\tconst TextWide tbuf(text, codePageText);\n\tTextPositions poses(tbuf.tlen);\n\tif (FAILED(MeasurePositions(poses, tbuf, pfm->pTextFormat.Get()))) {\n\t\treturn;\n\t}\n\tif (codePageText == CpUtf8) {\n\t\t// Map the widths given for UTF-16 characters back onto the UTF-8 input string\n\t\tsize_t i = 0;\n\t\tfor (int ui = 0; ui < tbuf.tlen; ui++) {\n\t\t\tconst unsigned char uch = text[i];\n\t\t\tconst unsigned int byteCount = UTF8BytesOfLead[uch];\n\t\t\tif (byteCount == 4) {\t// Non-BMP\n\t\t\t\tui++;\n\t\t\t}\n\t\t\tfor (unsigned int bytePos=0; (bytePos<byteCount) && (i<text.length()) && (ui<tbuf.tlen); bytePos++) {\n\t\t\t\tpositions[i++] = poses.buffer[ui];\n\t\t\t}\n\t\t}\n\t\tconst XYPOSITION lastPos = (i > 0) ? positions[i - 1] : 0.0;\n\t\twhile (i<text.length()) {\n\t\t\tpositions[i++] = lastPos;\n\t\t}\n\t} else if (!IsDBCSCodePage(codePageText)) {\n\n\t\t// One char per position\n\t\tPLATFORM_ASSERT(text.length() == static_cast<size_t>(tbuf.tlen));\n\t\tfor (int kk=0; kk<tbuf.tlen; kk++) {\n\t\t\tpositions[kk] = poses.buffer[kk];\n\t\t}\n\n\t} else {\n\n\t\t// May be one or two bytes per position\n\t\tint ui = 0;\n\t\tfor (size_t i=0; i<text.length() && ui<tbuf.tlen;) {\n\t\t\tpositions[i] = poses.buffer[ui];\n\t\t\tif (DBCSIsLeadByte(codePageText, text[i])) {\n\t\t\t\tpositions[i+1] = poses.buffer[ui];\n\t\t\t\ti += 2;\n\t\t\t} else {\n\t\t\t\ti++;\n\t\t\t}\n\n\t\t\tui++;\n\t\t}\n\t}\n}\n\nXYPOSITION SurfaceD2D::WidthText(const Font *font_, std::string_view text) {\n\tFLOAT width = 1.0;\n\tconst FontDirectWrite *pfm = FontDirectWrite::Cast(font_);\n\tif (pfm->pTextFormat) {\n\t\tconst TextWide tbuf(text, pfm->CodePageText(mode.codePage));\n\t\t// Create a layout\n\t\tif (TextLayout pTextLayout = LayoutCreate(tbuf.AsView(), pfm->pTextFormat.Get())) {\n\t\t\tDWRITE_TEXT_METRICS textMetrics;\n\t\t\tif (SUCCEEDED(pTextLayout->GetMetrics(&textMetrics)))\n\t\t\t\twidth = textMetrics.widthIncludingTrailingWhitespace;\n\t\t}\n\t}\n\treturn width;\n}\n\nvoid SurfaceD2D::DrawTextNoClipUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text,\n\tColourRGBA fore, ColourRGBA back) {\n\tif (pRenderTarget) {\n\t\tFillRectangleAligned(rc, back);\n\t\tD2DPenColourAlpha(fore);\n\t\tDrawTextCommon(rc, font_, ybase, text, CpUtf8, ETO_OPAQUE);\n\t}\n}\n\nvoid SurfaceD2D::DrawTextClippedUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text,\n\tColourRGBA fore, ColourRGBA back) {\n\tif (pRenderTarget) {\n\t\tFillRectangleAligned(rc, back);\n\t\tD2DPenColourAlpha(fore);\n\t\tDrawTextCommon(rc, font_, ybase, text, CpUtf8, ETO_OPAQUE | ETO_CLIPPED);\n\t}\n}\n\nvoid SurfaceD2D::DrawTextTransparentUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text,\n\tColourRGBA fore) {\n\t// Avoid drawing spaces in transparent mode\n\tfor (const char ch : text) {\n\t\tif (ch != ' ') {\n\t\t\tif (pRenderTarget) {\n\t\t\t\tD2DPenColourAlpha(fore);\n\t\t\t\tDrawTextCommon(rc, font_, ybase, text, CpUtf8, 0);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t}\n}\n\nvoid SurfaceD2D::MeasureWidthsUTF8(const Font *font_, std::string_view text, XYPOSITION *positions) {\n\tconst FontDirectWrite *pfm = FontDirectWrite::Cast(font_);\n\tconst TextWide tbuf(text, CpUtf8);\n\tTextPositions poses(tbuf.tlen);\n\tif (FAILED(MeasurePositions(poses, tbuf, pfm->pTextFormat.Get()))) {\n\t\treturn;\n\t}\n\t// Map the widths given for UTF-16 characters back onto the UTF-8 input string\n\tsize_t i = 0;\n\tfor (int ui = 0; ui < tbuf.tlen; ui++) {\n\t\tconst unsigned char uch = text[i];\n\t\tconst unsigned int byteCount = UTF8BytesOfLead[uch];\n\t\tif (byteCount == 4) {\t// Non-BMP\n\t\t\tui++;\n\t\t\tPLATFORM_ASSERT(ui < tbuf.tlen);\n\t\t}\n\t\tfor (unsigned int bytePos=0; (bytePos<byteCount) && (i<text.length()) && (ui < tbuf.tlen); bytePos++) {\n\t\t\tpositions[i++] = poses.buffer[ui];\n\t\t}\n\t}\n\tconst XYPOSITION lastPos = (i > 0) ? positions[i - 1] : 0.0;\n\twhile (i < text.length()) {\n\t\tpositions[i++] = lastPos;\n\t}\n}\n\nXYPOSITION SurfaceD2D::WidthTextUTF8(const Font * font_, std::string_view text) {\n\tFLOAT width = 1.0;\n\tconst FontDirectWrite *pfm = FontDirectWrite::Cast(font_);\n\tif (pfm->pTextFormat) {\n\t\tconst TextWide tbuf(text, CpUtf8);\n\t\t// Create a layout\n\t\tif (TextLayout pTextLayout = LayoutCreate(tbuf.AsView(), pfm->pTextFormat.Get())) {\n\t\t\tDWRITE_TEXT_METRICS textMetrics;\n\t\t\tif (SUCCEEDED(pTextLayout->GetMetrics(&textMetrics)))\n\t\t\t\twidth = textMetrics.widthIncludingTrailingWhitespace;\n\t\t}\n\t}\n\treturn width;\n}\n\nXYPOSITION SurfaceD2D::Ascent(const Font *font_) {\n\tconst FontDirectWrite *pfm = FontDirectWrite::Cast(font_);\n\treturn std::ceil(pfm->yAscent);\n}\n\nXYPOSITION SurfaceD2D::Descent(const Font *font_) {\n\tconst FontDirectWrite *pfm = FontDirectWrite::Cast(font_);\n\treturn std::ceil(pfm->yDescent);\n}\n\nXYPOSITION SurfaceD2D::InternalLeading(const Font *font_) {\n\tconst FontDirectWrite *pfm = FontDirectWrite::Cast(font_);\n\treturn std::floor(pfm->yInternalLeading);\n}\n\nXYPOSITION SurfaceD2D::Height(const Font *font_) {\n\tconst FontDirectWrite *pfm = FontDirectWrite::Cast(font_);\n\treturn std::ceil(pfm->yAscent) + std::ceil(pfm->yDescent);\n}\n\nXYPOSITION SurfaceD2D::AverageCharWidth(const Font *font_) {\n\tFLOAT width = 1.0;\n\tconst FontDirectWrite *pfm = FontDirectWrite::Cast(font_);\n\tif (pfm->pTextFormat) {\n\t\t// Create a layout\n\t\tstatic constexpr std::wstring_view wsvAllAlpha = L\"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\";\n\t\tif (TextLayout pTextLayout = LayoutCreate(wsvAllAlpha, pfm->pTextFormat.Get())) {\n\t\t\tDWRITE_TEXT_METRICS textMetrics;\n\t\t\tif (SUCCEEDED(pTextLayout->GetMetrics(&textMetrics)))\n\t\t\t\twidth = textMetrics.width / wsvAllAlpha.length();\n\t\t}\n\t}\n\treturn width;\n}\n\nvoid SurfaceD2D::SetClip(PRectangle rc) {\n\tif (pRenderTarget) {\n\t\tconst D2D1_RECT_F rcClip = RectangleFromPRectangle(rc);\n\t\tpRenderTarget->PushAxisAlignedClip(rcClip, D2D1_ANTIALIAS_MODE_ALIASED);\n\t\tclipsActive++;\n\t}\n}\n\nvoid SurfaceD2D::PopClip() {\n\tif (pRenderTarget) {\n\t\tPLATFORM_ASSERT(clipsActive > 0);\n\t\tpRenderTarget->PopAxisAlignedClip();\n\t\tclipsActive--;\n\t}\n}\n\nvoid SurfaceD2D::FlushCachedState() {\n}\n\nvoid SurfaceD2D::FlushDrawing() {\n\tif (pRenderTarget) {\n\t\tpRenderTarget->Flush();\n\t}\n}\n\nvoid SurfaceD2D::SetRenderingParams(std::shared_ptr<RenderingParams> renderingParams_) {\n\trenderingParams = std::move(renderingParams_);\n}\n\n#endif\n\n}\n\nnamespace Scintilla::Internal {\n\n#if defined(USE_D2D)\nIDWriteFactory1 *pIDWriteFactory = nullptr;\nID2D1Factory1 *pD2DFactory = nullptr;\n\nHRESULT CreateD3D(D3D11Device &device) noexcept {\n\tdevice = nullptr;\n\tif (!fnDCD) {\n\t\treturn E_FAIL;\n\t}\n\n\tconst D3D_FEATURE_LEVEL featureLevels[] = {\n\t\tD3D_FEATURE_LEVEL_11_1,\n\t\tD3D_FEATURE_LEVEL_11_0,\n\t\tD3D_FEATURE_LEVEL_10_1,\n\t\tD3D_FEATURE_LEVEL_10_0,\n\t\tD3D_FEATURE_LEVEL_9_3,\n\t\tD3D_FEATURE_LEVEL_9_2,\n\t\tD3D_FEATURE_LEVEL_9_1\n\t};\n\n\t// Create device.\n\t// Try for a hardware device but, if that fails, fall back to the Warp software rasterizer.\n\tComPtr<ID3D11Device> upDevice;\n\tHRESULT hr = S_OK;\n\tconst D3D_DRIVER_TYPE typesToTry[] = { D3D_DRIVER_TYPE_HARDWARE, D3D_DRIVER_TYPE_WARP };\n\tfor (const D3D_DRIVER_TYPE type : typesToTry) {\n\t\thr = fnDCD(nullptr,\n\t\t\ttype,\n\t\t\t{},\n\t\t\tD3D11_CREATE_DEVICE_BGRA_SUPPORT,\n\t\t\tfeatureLevels,\n\t\t\tARRAYSIZE(featureLevels),\n\t\t\tD3D11_SDK_VERSION,\n\t\t\tupDevice.GetAddressOf(),\n\t\t\tnullptr,\n\t\t\tnullptr);\n\t\tif (SUCCEEDED(hr))\n\t\t\tbreak;\n\t}\n\tif (FAILED(hr)) {\n\t\tPlatform::DebugPrintf(\"Failed to create D3D11 device 0x%lx\\n\", hr);\n\t\treturn hr;\n\t}\n\n\t// Convert from D3D11 to D3D11.1\n\thr = upDevice.As(&device);\n\tif (FAILED(hr)) {\n\t\tPlatform::DebugPrintf(\"Failed to create D3D11.1 device 0x%lx\\n\", hr);\n\t}\n\treturn hr;\n}\n\nbool LoadD2D() noexcept {\n\tstatic std::once_flag once;\n\ttry {\n\t\tstd::call_once(once, LoadD2DOnce);\n\t}\n\tcatch (...) {\n\t\t// ignore\n\t}\n\treturn pIDWriteFactory && pD2DFactory;\n}\n\nvoid ReleaseD2D() noexcept {\n\tReleaseUnknown(pIDWriteFactory);\n\tReleaseUnknown(pD2DFactory);\n\tReleaseLibrary(hDLLDWrite);\n\tReleaseLibrary(hDLLD3D);\n\tReleaseLibrary(hDLLD2D);\n}\n\nHRESULT CreateDCRenderTarget(const D2D1_RENDER_TARGET_PROPERTIES *renderTargetProperties, DCRenderTarget &dcRT) noexcept {\n\treturn pD2DFactory->CreateDCRenderTarget(renderTargetProperties, dcRT.ReleaseAndGetAddressOf());\n}\n\nBrushSolid BrushSolidCreate(ID2D1RenderTarget *pTarget, COLORREF colour) noexcept {\n\tBrushSolid brush;\n\tconst D2D_COLOR_F col = ColorFromColourAlpha(ColourRGBA::FromRGB(colour));\n\tif (FAILED(pTarget->CreateSolidColorBrush(col, brush.GetAddressOf()))) {\n\t\treturn {};\n\t}\n\treturn brush;\n}\n\nGeometry GeometryCreate() noexcept {\n\tGeometry geometry;\n\tif (FAILED(pD2DFactory->CreatePathGeometry(geometry.GetAddressOf()))) {\n\t\treturn {};\n\t}\n\treturn geometry;\n}\n\nGeometrySink GeometrySinkCreate(ID2D1PathGeometry *geometry) noexcept {\n\tGeometrySink sink;\n\tif (FAILED(geometry->Open(sink.GetAddressOf()))) {\n\t\treturn {};\n\t}\n\treturn sink;\n}\n\nStrokeStyle StrokeStyleCreate(const D2D1_STROKE_STYLE_PROPERTIES &strokeStyleProperties) noexcept {\n\tStrokeStyle strokeStyle;\n\tconst HRESULT hr = pD2DFactory->CreateStrokeStyle(\n\t\tstrokeStyleProperties, nullptr, 0, strokeStyle.GetAddressOf());\n\tif (FAILED(hr)) {\n\t\treturn {};\n\t}\n\treturn strokeStyle;\n}\n\nTextLayout LayoutCreate(std::wstring_view wsv, IDWriteTextFormat *pTextFormat, FLOAT maxWidth, FLOAT maxHeight) noexcept {\n\tTextLayout layout;\n\tconst HRESULT hr = pIDWriteFactory->CreateTextLayout(wsv.data(), static_cast<UINT32>(wsv.length()),\n\t\tpTextFormat, maxWidth, maxHeight, layout.GetAddressOf());\n\tif (FAILED(hr)) {\n\t\treturn {};\n\t}\n\treturn layout;\n}\n\n#endif\n\nstd::shared_ptr<Font> Font::Allocate(const FontParameters &fp) {\n#if defined(USE_D2D)\n\tif (fp.technology != Technology::Default) {\n\t\treturn std::make_shared<FontDirectWrite>(fp);\n\t}\n#endif\n\treturn FontGDI_Allocate(fp);\n}\n\nstd::unique_ptr<Surface> Surface::Allocate([[maybe_unused]] Technology technology) {\n#if defined(USE_D2D)\n\tif (technology != Technology::Default) {\n\t\treturn std::make_unique<SurfaceD2D>();\n\t}\n#endif\n\treturn SurfaceGDI_Allocate();\n}\n\n}\n"
  },
  {
    "path": "Libraries/scintilla/win32/SurfaceD2D.h",
    "content": "// Scintilla source code edit control\n/** @file SurfaceD2D.h\n ** Definitions for drawing to Direct2D on Windows.\n **/\n// Copyright 1998-2011 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#ifndef SURFACED2D_H\n#define SURFACED2D_H\n\nnamespace Scintilla::Internal {\n\nextern bool LoadD2D() noexcept;\nextern void ReleaseD2D() noexcept;\nextern ID2D1Factory1 *pD2DFactory;\nextern IDWriteFactory1 *pIDWriteFactory;\n\nusing DCRenderTarget = ComPtr<ID2D1DCRenderTarget>;\n\nusing D3D11Device = ComPtr<ID3D11Device1>;\n\nHRESULT CreateDCRenderTarget(const D2D1_RENDER_TARGET_PROPERTIES *renderTargetProperties, DCRenderTarget &dcRT) noexcept;\nextern HRESULT CreateD3D(D3D11Device &device) noexcept;\n\nusing WriteRenderingParams = ComPtr<IDWriteRenderingParams1>;\n\nstruct RenderingParams {\n\tWriteRenderingParams defaultRenderingParams;\n\tWriteRenderingParams customRenderingParams;\n};\n\nstruct ISetRenderingParams {\n\tvirtual void SetRenderingParams(std::shared_ptr<RenderingParams> renderingParams_) = 0;\n};\n\nusing BrushSolid = ComPtr<ID2D1SolidColorBrush>;\nusing Geometry = ComPtr<ID2D1PathGeometry>;\nusing GeometrySink = ComPtr<ID2D1GeometrySink>;\nusing StrokeStyle = ComPtr<ID2D1StrokeStyle>;\nusing TextLayout = ComPtr<IDWriteTextLayout>;\n\nBrushSolid BrushSolidCreate(ID2D1RenderTarget *pTarget, COLORREF colour) noexcept;\nGeometry GeometryCreate() noexcept;\nGeometrySink GeometrySinkCreate(ID2D1PathGeometry *geometry) noexcept;\nStrokeStyle StrokeStyleCreate(const D2D1_STROKE_STYLE_PROPERTIES &strokeStyleProperties) noexcept;\nTextLayout LayoutCreate(std::wstring_view wsv, IDWriteTextFormat *pTextFormat, FLOAT maxWidth=10000.0F, FLOAT maxHeight=1000.0F) noexcept;\n\n}\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/win32/SurfaceGDI.cxx",
    "content": "// Scintilla source code edit control\n/** @file SurfaceGDI.cxx\n ** Implementation of drawing to GDI on Windows.\n **/\n// Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#include <cstddef>\n#include <cstdlib>\n#include <cstdint>\n#include <cstring>\n#include <cstdio>\n#include <cstdarg>\n#include <ctime>\n#include <cmath>\n#include <climits>\n\n#include <string_view>\n#include <vector>\n#include <map>\n#include <optional>\n#include <algorithm>\n#include <iterator>\n#include <memory>\n#include <mutex>\n\n// Want to use std::min and std::max so don't want Windows.h version of min and max\n#if !defined(NOMINMAX)\n#define NOMINMAX\n#endif\n#undef _WIN32_WINNT\n#define _WIN32_WINNT 0x0A00\n#undef WINVER\n#define WINVER 0x0A00\n#define WIN32_LEAN_AND_MEAN 1\n#include <windows.h>\n#include <commctrl.h>\n#include <richedit.h>\n#include <windowsx.h>\n#include <shellscalingapi.h>\n\n#include \"ScintillaTypes.h\"\n\n#include \"Debugging.h\"\n#include \"Geometry.h\"\n#include \"Platform.h\"\n#include \"XPM.h\"\n#include \"UniConversion.h\"\n#include \"DBCS.h\"\n\n#include \"WinTypes.h\"\n#include \"PlatWin.h\"\n#include \"SurfaceGDI.h\"\n\nusing namespace Scintilla;\nusing namespace Scintilla::Internal;\n\n// All file hidden in unnamed namespace except for FontGDI_Allocate and SurfaceGDI_Allocate\nnamespace {\n\nconstexpr Supports SupportsGDI[] = {\n\tSupports::PixelModification,\n};\n\nconstexpr BYTE Win32MapFontQuality(FontQuality extraFontFlag) noexcept {\n\tswitch (extraFontFlag & FontQuality::QualityMask) {\n\n\tcase FontQuality::QualityNonAntialiased:\n\t\treturn NONANTIALIASED_QUALITY;\n\n\tcase FontQuality::QualityAntialiased:\n\t\treturn ANTIALIASED_QUALITY;\n\n\tcase FontQuality::QualityLcdOptimized:\n\t\treturn CLEARTYPE_QUALITY;\n\n\tdefault:\n\t\treturn DEFAULT_QUALITY;\n\t}\n}\n\nvoid SetLogFont(LOGFONTW &lf, const char *faceName, CharacterSet characterSet, XYPOSITION size, FontWeight weight, bool italic, FontQuality extraFontFlag) {\n\tlf = LOGFONTW();\n\t// The negative is to allow for leading\n\tlf.lfHeight = -(std::abs(std::lround(size)));\n\tlf.lfWeight = static_cast<LONG>(weight);\n\tlf.lfItalic = italic ? 1 : 0;\n\tlf.lfCharSet = static_cast<BYTE>(characterSet);\n\tlf.lfQuality = Win32MapFontQuality(extraFontFlag);\n\tUTF16FromUTF8(faceName, lf.lfFaceName, LF_FACESIZE);\n}\n\nstruct FontGDI : public FontWin {\n\tHFONT hfont = {};\n\tCharacterSet characterSet = CharacterSet::Ansi;\n\tFontGDI(HFONT hfont_, CharacterSet characterSet_) noexcept : hfont(hfont_), characterSet(characterSet_) {\n\t\t// Takes ownership and deletes the font\n\t}\n\texplicit FontGDI(const FontParameters &fp) : characterSet(fp.characterSet) {\n\t\tLOGFONTW lf;\n\t\tSetLogFont(lf, fp.faceName, fp.characterSet, fp.size, fp.weight, fp.italic, fp.extraFontFlag);\n\t\thfont = ::CreateFontIndirectW(&lf);\n\t}\n\t// Deleted so FontGDI objects can not be copied.\n\tFontGDI(const FontGDI &) = delete;\n\tFontGDI(FontGDI &&) = delete;\n\tFontGDI &operator=(const FontGDI &) = delete;\n\tFontGDI &operator=(FontGDI &&) = delete;\n\t~FontGDI() noexcept override {\n\t\tif (hfont)\n\t\t\t::DeleteObject(hfont);\n\t}\n\t[[nodiscard]] HFONT HFont() const noexcept override {\n\t\t// Duplicating hfont\n\t\tLOGFONTW lf = {};\n\t\tif (0 == ::GetObjectW(hfont, sizeof(lf), &lf)) {\n\t\t\treturn {};\n\t\t}\n\t\treturn ::CreateFontIndirectW(&lf);\n\t}\n\t[[nodiscard]] std::unique_ptr<FontWin> Duplicate() const override {\n\t\tHFONT hfontCopy = HFont();\n\t\treturn std::make_unique<FontGDI>(hfontCopy, characterSet);\n\t}\n\t[[nodiscard]] CharacterSet GetCharacterSet() const noexcept override {\n\t\treturn characterSet;\n\t}\n};\n\nclass SurfaceGDI : public Surface {\n\tSurfaceMode mode;\n\tHDC hdc{};\n\tbool hdcOwned = false;\n\tHPEN pen{};\n\tHPEN penOld{};\n\tHBRUSH brush{};\n\tHBRUSH brushOld{};\n\tHFONT fontOld{};\n\tHBITMAP bitmap{};\n\tHBITMAP bitmapOld{};\n\n\tint logPixelsY = USER_DEFAULT_SCREEN_DPI;\n\n\tstatic constexpr int maxWidthMeasure = INT_MAX;\n\t// There appears to be a 16 bit string length limit in GDI on NT.\n\tstatic constexpr int maxLenText = 65535;\n\n\tvoid PenColour(ColourRGBA fore, XYPOSITION widthStroke) noexcept;\n\n\tvoid BrushColour(ColourRGBA back) noexcept;\n\tvoid SetFont(const Font *font_);\n\tvoid Clear() noexcept;\n\npublic:\n\tSurfaceGDI() noexcept = default;\n\tSurfaceGDI(HDC hdcCompatible, int width, int height, SurfaceMode mode_, int logPixelsY_) noexcept;\n\t// Deleted so SurfaceGDI objects can not be copied.\n\tSurfaceGDI(const SurfaceGDI &) = delete;\n\tSurfaceGDI(SurfaceGDI &&) = delete;\n\tSurfaceGDI &operator=(const SurfaceGDI &) = delete;\n\tSurfaceGDI &operator=(SurfaceGDI &&) = delete;\n\n\t~SurfaceGDI() noexcept override;\n\n\tvoid Init(WindowID wid) override;\n\tvoid Init(SurfaceID sid, WindowID wid) override;\n\tstd::unique_ptr<Surface> AllocatePixMap(int width, int height) override;\n\n\tvoid SetMode(SurfaceMode mode_) override;\n\n\tvoid Release() noexcept override;\n\tint SupportsFeature(Supports feature) noexcept override;\n\tbool Initialised() override;\n\tint LogPixelsY() override;\n\tint PixelDivisions() override;\n\tint DeviceHeightFont(int points) override;\n\tvoid LineDraw(Point start, Point end, Stroke stroke) override;\n\tvoid PolyLine(const Point *pts, size_t npts, Stroke stroke) override;\n\tvoid Polygon(const Point *pts, size_t npts, FillStroke fillStroke) override;\n\tvoid RectangleDraw(PRectangle rc, FillStroke fillStroke) override;\n\tvoid RectangleFrame(PRectangle rc, Stroke stroke) override;\n\tvoid FillRectangle(PRectangle rc, Fill fill) override;\n\tvoid FillRectangleAligned(PRectangle rc, Fill fill) override;\n\tvoid FillRectangle(PRectangle rc, Surface &surfacePattern) override;\n\tvoid RoundedRectangle(PRectangle rc, FillStroke fillStroke) override;\n\tvoid AlphaRectangle(PRectangle rc, XYPOSITION cornerSize, FillStroke fillStroke) override;\n\tvoid GradientRectangle(PRectangle rc, const std::vector<ColourStop> &stops, GradientOptions options) override;\n\tvoid DrawRGBAImage(PRectangle rc, int width, int height, const unsigned char *pixelsImage) override;\n\tvoid Ellipse(PRectangle rc, FillStroke fillStroke) override;\n\tvoid Stadium(PRectangle rc, FillStroke fillStroke, Ends ends) override;\n\tvoid Copy(PRectangle rc, Point from, Surface &surfaceSource) override;\n\n\tstd::unique_ptr<IScreenLineLayout> Layout(const IScreenLine *screenLine) override;\n\n\tvoid DrawTextCommon(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, UINT fuOptions);\n\tvoid DrawTextNoClip(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourRGBA fore, ColourRGBA back) override;\n\tvoid DrawTextClipped(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourRGBA fore, ColourRGBA back) override;\n\tvoid DrawTextTransparent(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourRGBA fore) override;\n\tvoid MeasureWidths(const Font *font_, std::string_view text, XYPOSITION *positions) override;\n\tXYPOSITION WidthText(const Font *font_, std::string_view text) override;\n\n\tvoid DrawTextCommonUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, UINT fuOptions);\n\tvoid DrawTextNoClipUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourRGBA fore, ColourRGBA back) override;\n\tvoid DrawTextClippedUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourRGBA fore, ColourRGBA back) override;\n\tvoid DrawTextTransparentUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourRGBA fore) override;\n\tvoid MeasureWidthsUTF8(const Font *font_, std::string_view text, XYPOSITION *positions) override;\n\tXYPOSITION WidthTextUTF8(const Font *font_, std::string_view text) override;\n\n\tXYPOSITION Ascent(const Font *font_) override;\n\tXYPOSITION Descent(const Font *font_) override;\n\tXYPOSITION InternalLeading(const Font *font_) override;\n\tXYPOSITION Height(const Font *font_) override;\n\tXYPOSITION AverageCharWidth(const Font *font_) override;\n\n\tvoid SetClip(PRectangle rc) override;\n\tvoid PopClip() override;\n\tvoid FlushCachedState() override;\n\tvoid FlushDrawing() override;\n\n\tvoid DrawLineDots(PRectangle rc, ColourRGBA color) override; //Au\n\tvoid* get_hdc() { return hdc; } //Au\n};\n\nSurfaceGDI::SurfaceGDI(HDC hdcCompatible, int width, int height, SurfaceMode mode_, int logPixelsY_) noexcept {\n\thdc = ::CreateCompatibleDC(hdcCompatible);\n\thdcOwned = true;\n\tbitmap = ::CreateCompatibleBitmap(hdcCompatible, width, height);\n\tbitmapOld = SelectBitmap(hdc, bitmap);\n\t::SetTextAlign(hdc, TA_BASELINE);\n\tmode = mode_;\n\tlogPixelsY = logPixelsY_;\n}\n\nSurfaceGDI::~SurfaceGDI() noexcept {\n\tClear();\n}\n\nvoid SurfaceGDI::Clear() noexcept {\n\tif (penOld) {\n\t\t::SelectObject(hdc, penOld);\n\t\t::DeleteObject(pen);\n\t\tpenOld = {};\n\t}\n\tpen = {};\n\tif (brushOld) {\n\t\t::SelectObject(hdc, brushOld);\n\t\t::DeleteObject(brush);\n\t\tbrushOld = {};\n\t}\n\tbrush = {};\n\tif (fontOld) {\n\t\t// Fonts are not deleted as they are owned by a Font object\n\t\t::SelectObject(hdc, fontOld);\n\t\tfontOld = {};\n\t}\n\tif (bitmapOld) {\n\t\t::SelectObject(hdc, bitmapOld);\n\t\t::DeleteObject(bitmap);\n\t\tbitmapOld = {};\n\t}\n\tbitmap = {};\n\tif (hdcOwned) {\n\t\t::DeleteDC(hdc);\n\t\thdc = {};\n\t\thdcOwned = false;\n\t}\n}\n\nvoid SurfaceGDI::Release() noexcept {\n\tClear();\n}\n\nint SurfaceGDI::SupportsFeature(Supports feature) noexcept {\n\tfor (const Supports f : SupportsGDI) {\n\t\tif (f == feature)\n\t\t\treturn 1;\n\t}\n\treturn 0;\n}\n\nbool SurfaceGDI::Initialised() {\n\treturn hdc;\n}\n\nvoid SurfaceGDI::Init(WindowID wid) {\n\tRelease();\n\thdc = ::CreateCompatibleDC({});\n\thdcOwned = true;\n\t::SetTextAlign(hdc, TA_BASELINE);\n\tlogPixelsY = DpiForWindow(wid);\n}\n\nvoid SurfaceGDI::Init(SurfaceID sid, WindowID wid) {\n\tRelease();\n\thdc = static_cast<HDC>(sid);\n\t::SetTextAlign(hdc, TA_BASELINE);\n\t// Windows on screen are scaled but printers are not.\n\tconst bool printing = ::GetDeviceCaps(hdc, TECHNOLOGY) != DT_RASDISPLAY;\n\tlogPixelsY = printing ? ::GetDeviceCaps(hdc, LOGPIXELSY) : DpiForWindow(wid);\n}\n\nstd::unique_ptr<Surface> SurfaceGDI::AllocatePixMap(int width, int height) {\n\treturn std::make_unique<SurfaceGDI>(hdc, width, height, mode, logPixelsY);\n}\n\nvoid SurfaceGDI::SetMode(SurfaceMode mode_) {\n\tmode = mode_;\n}\n\nvoid SurfaceGDI::PenColour(ColourRGBA fore, XYPOSITION widthStroke) noexcept {\n\tif (pen) {\n\t\t::SelectObject(hdc, penOld);\n\t\t::DeleteObject(pen);\n\t\tpen = {};\n\t\tpenOld = {};\n\t}\n\tconst DWORD penWidth = std::lround(widthStroke);\n\tconst COLORREF penColour = fore.OpaqueRGB();\n\tif (widthStroke > 1) {\n\t\tconst LOGBRUSH brushParameters{ BS_SOLID, penColour, 0 };\n\t\tpen = ::ExtCreatePen(PS_GEOMETRIC | PS_ENDCAP_ROUND | PS_JOIN_MITER,\n\t\t\tpenWidth,\n\t\t\t&brushParameters,\n\t\t\t0,\n\t\t\tnullptr);\n\t} else {\n\t\tpen = ::CreatePen(PS_INSIDEFRAME, penWidth, penColour);\n\t}\n\tpenOld = SelectPen(hdc, pen);\n}\n\nvoid SurfaceGDI::BrushColour(ColourRGBA back) noexcept {\n\tif (brush) {\n\t\t::SelectObject(hdc, brushOld);\n\t\t::DeleteObject(brush);\n\t\tbrush = {};\n\t\tbrushOld = {};\n\t}\n\tbrush = ::CreateSolidBrush(back.OpaqueRGB());\n\tbrushOld = SelectBrush(hdc, brush);\n}\n\nvoid SurfaceGDI::SetFont(const Font *font_) {\n\tconst FontGDI *pfm = dynamic_cast<const FontGDI *>(font_);\n\tPLATFORM_ASSERT(pfm);\n\tif (!pfm) {\n\t\tthrow std::runtime_error(\"SurfaceGDI::SetFont: wrong Font type.\");\n\t}\n\tif (fontOld) {\n\t\tSelectFont(hdc, pfm->hfont);\n\t} else {\n\t\tfontOld = SelectFont(hdc, pfm->hfont);\n\t}\n}\n\nint SurfaceGDI::LogPixelsY() {\n\treturn logPixelsY;\n}\n\nint SurfaceGDI::PixelDivisions() {\n\t// Win32 uses device pixels.\n\treturn 1;\n}\n\nint SurfaceGDI::DeviceHeightFont(int points) {\n\treturn ::MulDiv(points, LogPixelsY(), pointsPerInch);\n}\n\nvoid SurfaceGDI::LineDraw(Point start, Point end, Stroke stroke) {\n\tPenColour(stroke.colour, stroke.width);\n\t::MoveToEx(hdc, std::lround(std::floor(start.x)), std::lround(std::floor(start.y)), nullptr);\n\t::LineTo(hdc, std::lround(std::floor(end.x)), std::lround(std::floor(end.y)));\n}\n\nvoid SurfaceGDI::PolyLine(const Point *pts, size_t npts, Stroke stroke) {\n\tPLATFORM_ASSERT(npts > 1);\n\tif (npts <= 1) {\n\t\treturn;\n\t}\n\tPenColour(stroke.colour, stroke.width);\n\tstd::vector<POINT> outline;\n\tstd::transform(pts, pts + npts, std::back_inserter(outline), POINTFromPoint);\n\t::Polyline(hdc, outline.data(), static_cast<int>(npts));\n}\n\nvoid SurfaceGDI::Polygon(const Point *pts, size_t npts, FillStroke fillStroke) {\n\tPenColour(fillStroke.stroke.colour.WithoutAlpha(), fillStroke.stroke.width);\n\tBrushColour(fillStroke.fill.colour.WithoutAlpha());\n\tstd::vector<POINT> outline;\n\tstd::transform(pts, pts + npts, std::back_inserter(outline), POINTFromPoint);\n\t::Polygon(hdc, outline.data(), static_cast<int>(npts));\n}\n\nvoid SurfaceGDI::RectangleDraw(PRectangle rc, FillStroke fillStroke) {\n\tRectangleFrame(rc, fillStroke.stroke);\n\tFillRectangle(rc.Inset(fillStroke.stroke.width), fillStroke.fill.colour);\n}\n\nvoid SurfaceGDI::RectangleFrame(PRectangle rc, Stroke stroke) {\n\tBrushColour(stroke.colour);\n\tconst RECT rcw = RectFromPRectangle(rc);\n\t::FrameRect(hdc, &rcw, brush);\n}\n\nvoid SurfaceGDI::FillRectangle(PRectangle rc, Fill fill) {\n\tif (fill.colour.IsOpaque()) {\n\t\t// Using ExtTextOut rather than a FillRect ensures that no dithering occurs.\n\t\t// There is no need to allocate a brush either.\n\t\tconst RECT rcw = RectFromPRectangle(rc);\n\t\t::SetBkColor(hdc, fill.colour.OpaqueRGB());\n\t\t::ExtTextOut(hdc, rcw.left, rcw.top, ETO_OPAQUE, &rcw, TEXT(\"\"), 0, nullptr);\n\t} else {\n\t\tAlphaRectangle(rc, 0, FillStroke(fill.colour));\n\t}\n}\n\nvoid SurfaceGDI::FillRectangleAligned(PRectangle rc, Fill fill) {\n\tFillRectangle(PixelAlign(rc, 1), fill);\n}\n\n//Au: draw dots line\nvoid SurfaceGDI::DrawLineDots(PRectangle rc, ColourRGBA color) {\n\tconst RECT r = RectFromPRectangle(rc);\n\tauto oldPen = ::SelectObject(hdc, ::CreatePen(PS_DOT, 1, color.AsInteger()));\n\t::MoveToEx(hdc, r.left, r.top, nullptr); ::LineTo(hdc, r.right, r.top);\n\t::DeleteObject(::SelectObject(hdc, oldPen));\n}\n\nvoid SurfaceGDI::FillRectangle(PRectangle rc, Surface &surfacePattern) {\n\tHBRUSH br{};\n\tif (SurfaceGDI *psgdi = dynamic_cast<SurfaceGDI *>(&surfacePattern); psgdi && psgdi->bitmap) {\n\t\tbr = ::CreatePatternBrush(psgdi->bitmap);\n\t} else {\t// Something is wrong so display in red\n\t\tbr = ::CreateSolidBrush(RGB(0xff, 0, 0));\n\t}\n\tconst RECT rcw = RectFromPRectangle(rc);\n\t::FillRect(hdc, &rcw, br);\n\t::DeleteObject(br);\n}\n\nvoid SurfaceGDI::RoundedRectangle(PRectangle rc, FillStroke fillStroke) {\n\tPenColour(fillStroke.stroke.colour, fillStroke.stroke.width);\n\tBrushColour(fillStroke.fill.colour);\n\tconst RECT rcw = RectFromPRectangle(rc);\n\tconstexpr int cornerSize = 8;\n\t::RoundRect(hdc,\n\t\trcw.left + 1, rcw.top,\n\t\trcw.right - 1, rcw.bottom,\n\t\tcornerSize, cornerSize);\n}\n\n// DIBSection is bitmap with some drawing operations used by SurfaceGDI.\nclass DIBSection {\n\tGDIBitMap bm;\n\tSIZE size{};\n\tDWORD *pixels = nullptr;\npublic:\n\tDIBSection(HDC hdc, SIZE size_) noexcept;\n\texplicit operator bool() const noexcept {\n\t\treturn bm && pixels;\n\t}\n\t[[nodiscard]] DWORD *Pixels() const noexcept {\n\t\treturn pixels;\n\t}\n\t[[nodiscard]] unsigned char *Bytes() const noexcept {\n\t\treturn reinterpret_cast<unsigned char *>(pixels);\n\t}\n\t[[nodiscard]] HDC DC() const noexcept {\n\t\treturn bm.DC();\n\t}\n\tvoid SetPixel(LONG x, LONG y, DWORD value) noexcept {\n\t\tPLATFORM_ASSERT(x >= 0);\n\t\tPLATFORM_ASSERT(y >= 0);\n\t\tPLATFORM_ASSERT(x < size.cx);\n\t\tPLATFORM_ASSERT(y < size.cy);\n\t\tpixels[(y * size.cx) + x] = value;\n\t}\n\tvoid SetSymmetric(LONG x, LONG y, DWORD value) noexcept;\n};\n\nDIBSection::DIBSection(HDC hdc, SIZE size_) noexcept : size(size_) {\n\t// -size.y makes bitmap start from top\n\tbm.Create(hdc, size.cx, -size.cy, &pixels);\n}\n\nvoid DIBSection::SetSymmetric(LONG x, LONG y, DWORD value) noexcept {\n\t// Plot a point symmetrically to all 4 quadrants\n\tconst LONG xSymmetric = size.cx - 1 - x;\n\tconst LONG ySymmetric = size.cy - 1 - y;\n\tSetPixel(x, y, value);\n\tSetPixel(xSymmetric, y, value);\n\tSetPixel(x, ySymmetric, value);\n\tSetPixel(xSymmetric, ySymmetric, value);\n}\n\nColourRGBA GradientValue(const std::vector<ColourStop> &stops, XYPOSITION proportion) noexcept {\n\tfor (size_t stop = 0; stop < stops.size() - 1; stop++) {\n\t\t// Loop through each pair of stops\n\t\tconst XYPOSITION positionStart = stops[stop].position;\n\t\tconst XYPOSITION positionEnd = stops[stop + 1].position;\n\t\tif ((proportion >= positionStart) && (proportion <= positionEnd)) {\n\t\t\tconst XYPOSITION proportionInPair = (proportion - positionStart) /\n\t\t\t\t(positionEnd - positionStart);\n\t\t\treturn stops[stop].colour.MixedWith(stops[stop + 1].colour, proportionInPair);\n\t\t}\n\t}\n\t// Loop should always find a value\n\treturn ColourRGBA();\n}\n\nconstexpr DWORD dwordFromBGRA(byte b, byte g, byte r, byte a) noexcept {\n\tconstexpr int aShift = 24;\n\tconstexpr int rShift = 16;\n\tconstexpr int gShift = 8;\n\treturn (a << aShift) | (r << rShift) | (g << gShift) | b;\n}\n\nconstexpr byte AlphaScaled(unsigned char component, unsigned int alpha) noexcept {\n\tconstexpr byte maxByte = 0xFFU;\n\treturn (component * alpha / maxByte) & maxByte;\n}\n\nconstexpr DWORD dwordMultiplied(ColourRGBA colour) noexcept {\n\treturn dwordFromBGRA(\n\t\tAlphaScaled(colour.GetBlue(), colour.GetAlpha()),\n\t\tAlphaScaled(colour.GetGreen(), colour.GetAlpha()),\n\t\tAlphaScaled(colour.GetRed(), colour.GetAlpha()),\n\t\tcolour.GetAlpha());\n}\n\nconstexpr BLENDFUNCTION mergeAlpha = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };\n\nvoid SurfaceGDI::AlphaRectangle(PRectangle rc, XYPOSITION cornerSize, FillStroke fillStroke) {\n\t// TODO: Implement strokeWidth\n\tconst RECT rcw = RectFromPRectangle(rc);\n\tconst SIZE size = SizeOfRect(rcw);\n\n\tif (size.cx > 0) {\n\n\t\tDIBSection section(hdc, size);\n\n\t\tif (section) {\n\n\t\t\t// Ensure not distorted too much by corners when small\n\t\t\tconst LONG corner = std::min(static_cast<LONG>(cornerSize), (std::min(size.cx, size.cy) / 2) - 2);\n\n\t\t\tconstexpr DWORD valEmpty = dwordFromBGRA(0, 0, 0, 0);\n\t\t\tconst DWORD valFill = dwordMultiplied(fillStroke.fill.colour);\n\t\t\tconst DWORD valOutline = dwordMultiplied(fillStroke.stroke.colour);\n\n\t\t\t// Draw a framed rectangle\n\t\t\tfor (int y = 0; y < size.cy; y++) {\n\t\t\t\tfor (int x = 0; x < size.cx; x++) {\n\t\t\t\t\tif ((x == 0) || (x == size.cx - 1) || (y == 0) || (y == size.cy - 1)) {\n\t\t\t\t\t\tsection.SetPixel(x, y, valOutline);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsection.SetPixel(x, y, valFill);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Make the corners transparent\n\t\t\tfor (LONG c = 0; c < corner; c++) {\n\t\t\t\tfor (LONG x = 0; x < c + 1; x++) {\n\t\t\t\t\tsection.SetSymmetric(x, c - x, valEmpty);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Draw the corner frame pieces\n\t\t\tfor (LONG x = 1; x < corner; x++) {\n\t\t\t\tsection.SetSymmetric(x, corner - x, valOutline);\n\t\t\t}\n\n\t\t\tGdiAlphaBlend(hdc, rcw.left, rcw.top, size.cx, size.cy, section.DC(), 0, 0, size.cx, size.cy, mergeAlpha);\n\t\t}\n\t} else {\n\t\tBrushColour(fillStroke.stroke.colour);\n\t\tFrameRect(hdc, &rcw, brush);\n\t}\n}\n\nvoid SurfaceGDI::GradientRectangle(PRectangle rc, const std::vector<ColourStop> &stops, GradientOptions options) {\n\n\tconst RECT rcw = RectFromPRectangle(rc);\n\tconst SIZE size = SizeOfRect(rcw);\n\n\tDIBSection section(hdc, size);\n\n\tif (section) {\n\n\t\tif (options == GradientOptions::topToBottom) {\n\t\t\tfor (LONG y = 0; y < size.cy; y++) {\n\t\t\t\t// Find y/height proportional colour\n\t\t\t\tconst XYPOSITION proportion = y / (rc.Height() - 1.0f);\n\t\t\t\tconst ColourRGBA mixed = GradientValue(stops, proportion);\n\t\t\t\tconst DWORD valFill = dwordMultiplied(mixed);\n\t\t\t\tfor (LONG x = 0; x < size.cx; x++) {\n\t\t\t\t\tsection.SetPixel(x, y, valFill);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tfor (LONG x = 0; x < size.cx; x++) {\n\t\t\t\t// Find x/width proportional colour\n\t\t\t\tconst XYPOSITION proportion = x / (rc.Width() - 1.0f);\n\t\t\t\tconst ColourRGBA mixed = GradientValue(stops, proportion);\n\t\t\t\tconst DWORD valFill = dwordMultiplied(mixed);\n\t\t\t\tfor (LONG y = 0; y < size.cy; y++) {\n\t\t\t\t\tsection.SetPixel(x, y, valFill);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tGdiAlphaBlend(hdc, rcw.left, rcw.top, size.cx, size.cy, section.DC(), 0, 0, size.cx, size.cy, mergeAlpha);\n\t}\n}\n\nvoid SurfaceGDI::DrawRGBAImage(PRectangle rc, int width, int height, const unsigned char *pixelsImage) {\n\tif (rc.Width() > 0) {\n\t\tif (rc.Width() > width)\n\t\t\trc.left += std::floor((rc.Width() - width) / 2);\n\t\trc.right = rc.left + width;\n\t\tif (rc.Height() > height)\n\t\t\trc.top += std::floor((rc.Height() - height) / 2);\n\t\trc.bottom = rc.top + height;\n\n\t\tconst SIZE size{ width, height };\n\t\tDIBSection section(hdc, size);\n\t\tif (section) {\n\t\t\tRGBAImage::BGRAFromRGBA(section.Bytes(), pixelsImage, static_cast<size_t>(width) * height);\n\t\t\tGdiAlphaBlend(hdc, static_cast<int>(rc.left), static_cast<int>(rc.top),\n\t\t\t\tstatic_cast<int>(rc.Width()), static_cast<int>(rc.Height()), section.DC(),\n\t\t\t\t0, 0, width, height, mergeAlpha);\n\t\t}\n\t}\n}\n\nvoid SurfaceGDI::Ellipse(PRectangle rc, FillStroke fillStroke) {\n\tPenColour(fillStroke.stroke.colour, fillStroke.stroke.width);\n\tBrushColour(fillStroke.fill.colour);\n\tconst RECT rcw = RectFromPRectangle(rc);\n\t::Ellipse(hdc, rcw.left, rcw.top, rcw.right, rcw.bottom);\n}\n\nvoid SurfaceGDI::Stadium(PRectangle rc, FillStroke fillStroke, [[maybe_unused]] Ends ends) {\n\t// TODO: Implement properly - the rectangle is just a placeholder\n\tRectangleDraw(rc, fillStroke);\n}\n\nvoid SurfaceGDI::Copy(PRectangle rc, Point from, Surface &surfaceSource) {\n\t::BitBlt(hdc,\n\t\tstatic_cast<int>(rc.left), static_cast<int>(rc.top),\n\t\tstatic_cast<int>(rc.Width()), static_cast<int>(rc.Height()),\n\t\tdynamic_cast<SurfaceGDI &>(surfaceSource).hdc,\n\t\tstatic_cast<int>(from.x), static_cast<int>(from.y), SRCCOPY);\n}\n\nstd::unique_ptr<IScreenLineLayout> SurfaceGDI::Layout(const IScreenLine *) {\n\treturn {};\n}\n\nusing TextPositionsI = VarBuffer<int, stackBufferLength>;\n\nvoid SurfaceGDI::DrawTextCommon(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, UINT fuOptions) {\n\tSetFont(font_);\n\tconst RECT rcw = RectFromPRectangle(rc);\n\tconst int x = static_cast<int>(rc.left);\n\tconst int yBaseInt = static_cast<int>(ybase);\n\n\tif (mode.codePage == CpUtf8) {\n\t\tconst TextWide tbuf(text, mode.codePage);\n\t\t::ExtTextOutW(hdc, x, yBaseInt, fuOptions, &rcw, tbuf.buffer, tbuf.tlen, nullptr);\n\t} else {\n\t\t::ExtTextOutA(hdc, x, yBaseInt, fuOptions, &rcw, text.data(), static_cast<UINT>(text.length()), nullptr);\n\t}\n}\n\nvoid SurfaceGDI::DrawTextNoClip(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text,\n\tColourRGBA fore, ColourRGBA back) {\n\t::SetTextColor(hdc, fore.OpaqueRGB());\n\t::SetBkColor(hdc, back.OpaqueRGB());\n\tDrawTextCommon(rc, font_, ybase, text, ETO_OPAQUE);\n}\n\nvoid SurfaceGDI::DrawTextClipped(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text,\n\tColourRGBA fore, ColourRGBA back) {\n\t::SetTextColor(hdc, fore.OpaqueRGB());\n\t::SetBkColor(hdc, back.OpaqueRGB());\n\tDrawTextCommon(rc, font_, ybase, text, ETO_OPAQUE | ETO_CLIPPED);\n}\n\nvoid SurfaceGDI::DrawTextTransparent(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text,\n\tColourRGBA fore) {\n\t// Avoid drawing spaces in transparent mode\n\tfor (const char ch : text) {\n\t\tif (ch != ' ') {\n\t\t\t::SetTextColor(hdc, fore.OpaqueRGB());\n\t\t\t::SetBkMode(hdc, TRANSPARENT);\n\t\t\tDrawTextCommon(rc, font_, ybase, text, 0);\n\t\t\t::SetBkMode(hdc, OPAQUE);\n\t\t\treturn;\n\t\t}\n\t}\n}\n\nvoid SurfaceGDI::MeasureWidths(const Font *font_, std::string_view text, XYPOSITION *positions) {\n\t// Zero positions to avoid random behaviour on failure.\n\tstd::fill(positions, positions + text.length(), 0.0f);\n\tSetFont(font_);\n\tSIZE sz = { 0,0 };\n\tint fit = 0;\n\tint i = 0;\n\tconst int len = static_cast<int>(text.length());\n\tif (mode.codePage == CpUtf8) {\n\t\tconst TextWide tbuf(text, mode.codePage);\n\t\tTextPositionsI poses(tbuf.tlen);\n\t\tif (!::GetTextExtentExPointW(hdc, tbuf.buffer, tbuf.tlen, maxWidthMeasure, &fit, poses.buffer, &sz)) {\n\t\t\t// Failure\n\t\t\treturn;\n\t\t}\n\t\t// Map the widths given for UTF-16 characters back onto the UTF-8 input string\n\t\tfor (int ui = 0; ui < fit; ui++) {\n\t\t\tconst unsigned char uch = text[i];\n\t\t\tconst unsigned int byteCount = UTF8BytesOfLead[uch];\n\t\t\tif (byteCount == 4) {\t// Non-BMP\n\t\t\t\tui++;\n\t\t\t}\n\t\t\tfor (unsigned int bytePos = 0; (bytePos < byteCount) && (i < len); bytePos++) {\n\t\t\t\tpositions[i++] = static_cast<XYPOSITION>(poses.buffer[ui]);\n\t\t\t}\n\t\t}\n\t} else {\n\t\tTextPositionsI poses(len);\n\t\tif (!::GetTextExtentExPointA(hdc, text.data(), len, maxWidthMeasure, &fit, poses.buffer, &sz)) {\n\t\t\t// Eeek - a NULL DC or other foolishness could cause this.\n\t\t\treturn;\n\t\t}\n\t\twhile (i < fit) {\n\t\t\tpositions[i] = static_cast<XYPOSITION>(poses.buffer[i]);\n\t\t\ti++;\n\t\t}\n\t}\n\t// If any positions not filled in then use the last position for them\n\tconst XYPOSITION lastPos = (fit > 0) ? positions[fit - 1] : 0.0f;\n\tstd::fill(positions + i, positions + text.length(), lastPos);\n}\n\nXYPOSITION SurfaceGDI::WidthText(const Font *font_, std::string_view text) {\n\tSetFont(font_);\n\tSIZE sz = { 0,0 };\n\tif (!(mode.codePage == CpUtf8)) {\n\t\t::GetTextExtentPoint32A(hdc, text.data(), std::min(static_cast<int>(text.length()), maxLenText), &sz);\n\t} else {\n\t\tconst TextWide tbuf(text, mode.codePage);\n\t\t::GetTextExtentPoint32W(hdc, tbuf.buffer, tbuf.tlen, &sz);\n\t}\n\treturn static_cast<XYPOSITION>(sz.cx);\n}\n\nvoid SurfaceGDI::DrawTextCommonUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, UINT fuOptions) {\n\tSetFont(font_);\n\tconst RECT rcw = RectFromPRectangle(rc);\n\tconst int x = static_cast<int>(rc.left);\n\tconst int yBaseInt = static_cast<int>(ybase);\n\n\tconst TextWide tbuf(text, CpUtf8);\n\t::ExtTextOutW(hdc, x, yBaseInt, fuOptions, &rcw, tbuf.buffer, tbuf.tlen, nullptr);\n}\n\nvoid SurfaceGDI::DrawTextNoClipUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text,\n\tColourRGBA fore, ColourRGBA back) {\n\t::SetTextColor(hdc, fore.OpaqueRGB());\n\t::SetBkColor(hdc, back.OpaqueRGB());\n\tDrawTextCommonUTF8(rc, font_, ybase, text, ETO_OPAQUE);\n}\n\nvoid SurfaceGDI::DrawTextClippedUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text,\n\tColourRGBA fore, ColourRGBA back) {\n\t::SetTextColor(hdc, fore.OpaqueRGB());\n\t::SetBkColor(hdc, back.OpaqueRGB());\n\tDrawTextCommonUTF8(rc, font_, ybase, text, ETO_OPAQUE | ETO_CLIPPED);\n}\n\nvoid SurfaceGDI::DrawTextTransparentUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text,\n\tColourRGBA fore) {\n\t// Avoid drawing spaces in transparent mode\n\tfor (const char ch : text) {\n\t\tif (ch != ' ') {\n\t\t\t::SetTextColor(hdc, fore.OpaqueRGB());\n\t\t\t::SetBkMode(hdc, TRANSPARENT);\n\t\t\tDrawTextCommonUTF8(rc, font_, ybase, text, 0);\n\t\t\t::SetBkMode(hdc, OPAQUE);\n\t\t\treturn;\n\t\t}\n\t}\n}\n\nvoid SurfaceGDI::MeasureWidthsUTF8(const Font *font_, std::string_view text, XYPOSITION *positions) {\n\t// Zero positions to avoid random behaviour on failure.\n\tstd::fill(positions, positions + text.length(), 0.0f);\n\tSetFont(font_);\n\tSIZE sz = { 0,0 };\n\tint fit = 0;\n\tint i = 0;\n\tconst int len = static_cast<int>(text.length());\n\tconst TextWide tbuf(text, CpUtf8);\n\tTextPositionsI poses(tbuf.tlen);\n\tif (!::GetTextExtentExPointW(hdc, tbuf.buffer, tbuf.tlen, maxWidthMeasure, &fit, poses.buffer, &sz)) {\n\t\t// Failure\n\t\treturn;\n\t}\n\t// Map the widths given for UTF-16 characters back onto the UTF-8 input string\n\tfor (int ui = 0; ui < fit; ui++) {\n\t\tconst unsigned char uch = text[i];\n\t\tconst unsigned int byteCount = UTF8BytesOfLead[uch];\n\t\tif (byteCount == 4) {\t// Non-BMP\n\t\t\tui++;\n\t\t}\n\t\tfor (unsigned int bytePos = 0; (bytePos < byteCount) && (i < len); bytePos++) {\n\t\t\tpositions[i++] = static_cast<XYPOSITION>(poses.buffer[ui]);\n\t\t}\n\t}\n\t// If any positions not filled in then use the last position for them\n\tconst XYPOSITION lastPos = (fit > 0) ? positions[fit - 1] : 0.0f;\n\tstd::fill(positions + i, positions + text.length(), lastPos);\n}\n\nXYPOSITION SurfaceGDI::WidthTextUTF8(const Font *font_, std::string_view text) {\n\tSetFont(font_);\n\tSIZE sz = { 0,0 };\n\tconst TextWide tbuf(text, CpUtf8);\n\t::GetTextExtentPoint32W(hdc, tbuf.buffer, tbuf.tlen, &sz);\n\treturn static_cast<XYPOSITION>(sz.cx);\n}\n\nXYPOSITION SurfaceGDI::Ascent(const Font *font_) {\n\tSetFont(font_);\n\tTEXTMETRIC tm;\n\t::GetTextMetrics(hdc, &tm);\n\treturn static_cast<XYPOSITION>(tm.tmAscent);\n}\n\nXYPOSITION SurfaceGDI::Descent(const Font *font_) {\n\tSetFont(font_);\n\tTEXTMETRIC tm;\n\t::GetTextMetrics(hdc, &tm);\n\treturn static_cast<XYPOSITION>(tm.tmDescent);\n}\n\nXYPOSITION SurfaceGDI::InternalLeading(const Font *font_) {\n\tSetFont(font_);\n\tTEXTMETRIC tm;\n\t::GetTextMetrics(hdc, &tm);\n\treturn static_cast<XYPOSITION>(tm.tmInternalLeading);\n}\n\nXYPOSITION SurfaceGDI::Height(const Font *font_) {\n\tSetFont(font_);\n\tTEXTMETRIC tm;\n\t::GetTextMetrics(hdc, &tm);\n\treturn static_cast<XYPOSITION>(tm.tmHeight);\n}\n\nXYPOSITION SurfaceGDI::AverageCharWidth(const Font *font_) {\n\tSetFont(font_);\n\tTEXTMETRIC tm;\n\t::GetTextMetrics(hdc, &tm);\n\treturn static_cast<XYPOSITION>(tm.tmAveCharWidth);\n}\n\nvoid SurfaceGDI::SetClip(PRectangle rc) {\n\t::SaveDC(hdc);\n\t::IntersectClipRect(hdc, static_cast<int>(rc.left), static_cast<int>(rc.top),\n\t\tstatic_cast<int>(rc.right), static_cast<int>(rc.bottom));\n}\n\nvoid SurfaceGDI::PopClip() {\n\t::RestoreDC(hdc, -1);\n}\n\nvoid SurfaceGDI::FlushCachedState() {\n\tpen = {};\n\tbrush = {};\n}\n\nvoid SurfaceGDI::FlushDrawing() {\n}\n\n}\n\nnamespace Scintilla::Internal {\n\nstd::shared_ptr<Font> FontGDI_Allocate(const FontParameters &fp) {\n\treturn std::make_shared<FontGDI>(fp);\n}\n\nstd::unique_ptr<Surface> SurfaceGDI_Allocate() {\n\treturn std::make_unique<SurfaceGDI>();\n}\n\n}\n"
  },
  {
    "path": "Libraries/scintilla/win32/SurfaceGDI.h",
    "content": "// Scintilla source code edit control\n/** @file SurfaceGDI.h\n ** Definitions for drawing to GDI on Windows.\n **/\n// Copyright 2025 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#ifndef SURFACEGDI_H\n#define SURFACEGDI_H\n\nnamespace Scintilla::Internal {\n\nstd::shared_ptr<Font> FontGDI_Allocate(const FontParameters &fp);\nstd::unique_ptr<Surface> SurfaceGDI_Allocate();\n\n}\n\n#endif\n"
  },
  {
    "path": "Libraries/scintilla/win32/WinTypes.h",
    "content": "// Scintilla source code edit control\n/** @file WinTypes.h\n ** Implement safe release of COM objects and access to functions in DLLs.\n ** Header contains all implementation - there is no .cxx file.\n **/\n// Copyright 2020-2021 by Neil Hodgson <neilh@scintilla.org>\n// The License.txt file describes the conditions under which this software may be distributed.\n\n#ifndef WINTYPES_H\n#define WINTYPES_H\n\nnamespace Scintilla::Internal {\n\n// Release an IUnknown* and set to nullptr.\n// While IUnknown::Release must be noexcept, it isn't marked as such so produces\n// warnings which are avoided by the catch.\ntemplate <class T>\ninline void ReleaseUnknown(T *&ppUnknown) noexcept {\n\tif (ppUnknown) {\n\t\ttry {\n\t\t\tppUnknown->Release();\n\t\t} catch (...) {\n\t\t\t// Never occurs\n\t\t}\n\t\tppUnknown = nullptr;\n\t}\n}\n\nstruct UnknownReleaser {\n\t// Called by unique_ptr to destroy/free the resource\n\ttemplate <class T>\n\tvoid operator()(T *pUnknown) noexcept {\n\t\ttry {\n\t\t\tpUnknown->Release();\n\t\t} catch (...) {\n\t\t\t// IUnknown::Release must not throw, ignore if it does.\n\t\t}\n\t}\n};\n\n/// Find a function in a DLL and convert to a function pointer.\n/// This avoids undefined and conditionally defined behaviour.\ntemplate<typename T>\ninline T DLLFunction(HMODULE hModule, LPCSTR lpProcName) noexcept {\n\tif (!hModule) {\n\t\treturn nullptr;\n\t}\n\tFARPROC function = ::GetProcAddress(hModule, lpProcName);\n\tstatic_assert(sizeof(T) == sizeof(function));\n\tT fp {};\n\tmemcpy(&fp, &function, sizeof(T));\n\treturn fp;\n}\n\ninline void ReleaseLibrary(HMODULE &hLib) noexcept {\n\tif (hLib) {\n\t\tFreeLibrary(hLib);\n\t\thLib = {};\n\t}\n}\n\n}\n\n#endif\n"
  },
  {
    "path": "Notes.txt",
    "content": "VERSION INFO\n\nThe program version format is major.minor.build, like 1.20.5.\n\tMinor: changed when added some significant features.\n\tBuild: changed when fixed bugs between minor versions. Also may have some new features.\n\nTo update version info of dll files (Au, Au.Editor, Au.Controls):\n\tOpen global2.cs and edit Au_.Version. Single global2.cs is used by all these projects.\n\tIt's important to update Au version info whenever something changed in Au or its dependencies (AuCpp etc).\n\tAlso may need to update year in AssemblyCopyright attribute (it's displayed in About info).\nTo update version info of the setup file:\n\tOpen the .iss file in Inno Script Studio and edit MyAppVersion.\nAlso in index.md update version, date, filesize. It is part of the help website.\nAlso may need to update year in LICENSE.txt.\n\nAll significant changes of that version must be documented in Other/DocFX/_doc/changes/vMajor.Minor.md. Do it whenever making changes.\n\n\nHOW TO RELEASE A NEW VERSION\n\nReview all TODO. Occasionally review TODO2, FUTURE, CONSIDER.\n\tTODO2 means \"low priority TODO\". If after a long time it still seems low priority, delete the comment or replace with TODO3.\n\nMay need to test something. Eg some changes could break some main features.\n\tAlso test new and some main features on other OS. Test on ARM-64.\n\nMay need to undefine TRACE in some C++ projects.\n\tUsually not using TRACE in Release config, but sometimes may use it to test Release speed etc.\n\tDon't use TRACE in C# projects; it's defined by default in Debug and Release config.\n\nMay need to delete test code. Or use #if DEBUG, like in Tests.cs.\n\tReview perf and print.\n\nMay need to add new OS version to class osVersion. Usually once/year.\n\nChange version info (see above) in global2.cs and index.md.\n\nBuild solution in config Release, all platforms.\n\tNOTE: use the \"Rebuild solution\" command, else VS may not update something.\n\nReview menu Help -> About. May need to update C# version, .NET version, used libraries.\n\nRun script \"Au docs\".\n\tFix errors if need. More info in AuDocs.txt.\n\tDon't upload now.\n\tIt also runs other scripts that create files for local docs.\n\nCreate database for AI. To find the script: main toolbar > \"Docs\" menu. Also the \"Au docs\" script prints the link.\n\nMay need to update some info in README.md for github. Eg the screenshot and \"how to build\".\n\tWhen an image updated, github still shows the old cached. Workaround: in the image link append #x and change x for each image version. If using CloudFlare (currently not): before run script \"Purge Cloudflare CDN cache\".\n\nReview the md file of current version.\n\tIf it's still \"future.md\", rename ( RENAME!!! ) to \"v1.x.md\".\n\nOpen the .iss file in Inno Script Studio. Change MyAppVersion. Review. Compile.\n\tRun the setup file to test. Let it run LA. Test on other OS too. Occasionally uninstall/install, not just upgrade.\n\tMay need to update the setup file size in the download webpage (index.md).\n\nGithub commit all and push. The message could be like v1.2.3.\n\tNote: do it now. Need for upload to GitHub. Also uploads the \"changes\" doc.\n\nCreate a GitHub Release. Upload the setup file.\n\nUpload the setup file to https://www.libreautomate.com. For example with WinSCP.\n\tBackup the old setup file at first: in WinSCP move the old file to the \"download\" folder and rename (add version).\n\t\tDon't backup if changed only patch version. Simply replace.\n\t\tOptional if using GitHub Releases.\n\nUpload the website created by script \"Au docs\". To find the script: main toolbar > \"Docs\" menu.\n\nReview web pages. Github too.\n\nDownload and test the setup file.\n\tOccasionally test it on all OS. Observe SmartScreen/antivirus behavior.\n\nSometimes test whether others can build and run the repo.\n\tEg when some new files added to the `_` dir.\n\tGenerated or downloaded files in `_` dir/subdirs must be gitignored, unless very small non-binary. Instead specify them in `GitBinaryFiles.cs` in project `BuildEvents`.\n\tMake sure this file exists: `.git\\hooks\\pre-push`. More info in `GitBinaryFiles.cs`.\n\nTest the setup file at virustotal.com. And separately other files (even data files).\n\tAlways will be several false positives. Even with an empty Inno Setup project. Never mind.\n\nRelease NuGet package:\n\tRun script \"Create NuGet package.cs\". It builds Au using a modified temp csproj, creates package and adds native dlls.\n\tUpload to NuGet. The script prints links.\n\nRun script \"Update version.txt.cs\". It updates _\\version.txt and uploads to public_html.\n\nNo more:\n\tAnnounce in forum.\n\tIf not beta, occasionally review/update in some download websites. Edit and upload the PAD file.\n\n\n---------------------------------------------------------\n\nUPDATING .NET\n\nVS must support that .NET version. Install the .NET SDK if need.\n\nUpdate .NET version in all C# projects (.csproj) except Au.Net4.\n\nMay need to update .NET SDK version in global.json (in solution folder).\n\tUsed it to force VS to use .NET 8 SDK.\n\tBecause: if installed .NET 9 Preview SDK, VS uses it to build projects that use .NET 8. Then app crashes when starting.\n\tNote: some scripts build just the Au project. To use correct SDK, I created a symlink to the solution's global.json in the project folder. Adding as a link in VS (in csproj) does not work.\n\nIn project DatabasesEtc, in file Program.cs:\n\tEnable only the call to RefTxt.Create, and run.\n\tEnable only the call to RefAndDoc.Create, and run.\n\tAlso now it's a good time to update icons: update the NuGet package, enable only Icons.CreateDB, and run.\n\tNOTE: also need this when upgraded SDK RC -> final. Eg in .NET 8 SDK final some API changed.\n\nUpdate .NET version in the AppHost project (the #define). Build.\n\nUpdate MetaComments.Defines.\n\nBuild solution.\n\nRun editor.\n\nIn LibreAutomate.iss (Inno Setup) replace the .NET Desktop Runtime download URL. Also .NET major version in several places.\n\nFind-replace \"NET 8\", \"NET8\", \"net8\" and \"8\" everywhere, including:\n- LibreAutomate.iss (Inno Setup).\n- README.md (GitHub).\n\nCreate-upload https://www.libreautomate.com/download/net-X-url.txt. Here X is .NET major version, eg 10.\n\tThe file must contain the .NET Desktop Runtime download URLs for x64 and arm64 (the same as in LibreAutomate.iss).\n\nRun script \"Minimal .NET SDK\". Upload.\n\nTest DNuget. Test package Microsoft.PowerShell.SDK (because it's the most complex I know). Test how it works in miniProgram and exeProgram.\n\nTest Publish.\n\nMaybe more.\n"
  },
  {
    "path": "Other/Au.DllHost/Au.DllHost.vcxproj",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup Label=\"ProjectConfigurations\">\n    <ProjectConfiguration Include=\"Release|ARM64\">\n      <Configuration>Release</Configuration>\n      <Platform>ARM64</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release|Win32\">\n      <Configuration>Release</Configuration>\n      <Platform>Win32</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release|x64\">\n      <Configuration>Release</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n  </ItemGroup>\n  <PropertyGroup Label=\"Globals\">\n    <VCProjectVersion>17.0</VCProjectVersion>\n    <Keyword>Win32Proj</Keyword>\n    <ProjectGuid>{e9d9f078-bf4f-437e-be31-3af89f3c6548}</ProjectGuid>\n    <RootNamespace>AuDllHost</RootNamespace>\n    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v143</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v143</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|ARM64'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v143</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\n  <ImportGroup Label=\"ExtensionSettings\">\n  </ImportGroup>\n  <ImportGroup Label=\"Shared\">\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|ARM64'\" Label=\"PropertySheets\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <PropertyGroup Label=\"UserMacros\" />\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|ARM64'\">\n    <OutDir>bin\\$(Configuration)\\$(Platform)\\</OutDir>\n    <IntDir>obj\\$(Configuration)\\$(Platform)\\</IntDir>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <OutDir>bin\\$(Configuration)\\$(Platform)\\</OutDir>\n    <IntDir>obj\\$(Configuration)\\$(Platform)\\</IntDir>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <OutDir>bin\\$(Configuration)\\$(Platform)\\</OutDir>\n    <IntDir>obj\\$(Configuration)\\$(Platform)\\</IntDir>\n  </PropertyGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <SDLCheck>true</SDLCheck>\n      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <ConformanceMode>true</ConformanceMode>\n      <DebugInformationFormat>None</DebugInformationFormat>\n      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <EntryPointSymbol>main</EntryPointSymbol>\n    </Link>\n    <PostBuildEvent>\n      <Command>xcopy $(TargetPath) $(SolutionDir)_\\32\\ /Y</Command>\n    </PostBuildEvent>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <SDLCheck>true</SDLCheck>\n      <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <ConformanceMode>true</ConformanceMode>\n      <DebugInformationFormat>None</DebugInformationFormat>\n      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <EntryPointSymbol>main</EntryPointSymbol>\n    </Link>\n    <PostBuildEvent>\n      <Command>xcopy $(TargetPath) $(SolutionDir)_\\64\\ /Y</Command>\n    </PostBuildEvent>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|ARM64'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <SDLCheck>true</SDLCheck>\n      <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <ConformanceMode>true</ConformanceMode>\n      <DebugInformationFormat>None</DebugInformationFormat>\n      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\n    </ClCompile>\n    <Link>\n      <SubSystem>Windows</SubSystem>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n      <EntryPointSymbol>main</EntryPointSymbol>\n    </Link>\n    <PostBuildEvent>\n      <Command>xcopy $(TargetPath) $(SolutionDir)_\\64\\ARM\\ /Y</Command>\n    </PostBuildEvent>\n  </ItemDefinitionGroup>\n  <ItemGroup>\n    <ClCompile Include=\"DllHost.c\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ProjectReference Include=\"..\\..\\Cpp\\Cpp.vcxproj\">\n      <Project>{10559875-c9a1-484c-bcfa-8dc53a428f1c}</Project>\n    </ProjectReference>\n  </ItemGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\n  <ImportGroup Label=\"ExtensionTargets\">\n  </ImportGroup>\n</Project>"
  },
  {
    "path": "Other/Au.DllHost/Au.DllHost.vcxproj.filters",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup>\n    <ClCompile Include=\"DllHost.c\" />\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "Other/Au.DllHost/DllHost.c",
    "content": "#define WIN32_LEAN_AND_MEAN\n#include <windows.h>\n\n//#define EXPORT extern \"C\" __declspec(dllimport) //C++\n#define EXPORT __declspec(dllimport) //C\nEXPORT void Cpp_Arch(LPCWSTR a0, LPCWSTR a1);\nEXPORT void Cpp_Unload(DWORD flags);\n\n#if 1 //small exe file. Use .c file. Project properties > Linker > Advanced > Entry point = main.\nvoid main() {\n\tLPCWSTR a = GetCommandLine();\n\tif (*a == '\"') {\n\t\twhile (*(++a) != '\"') if (*a == 0) return;\n\t\ta++;\n\t} else {\n\t\twhile (*a != ' ' && *a != 0) a++;\n\t}\n\twhile (*a == ' ') a++;\n\t//MessageBox(0, a, L\"test\", 0);\n\n\tif (*a == 0) {\n\t\tCpp_Unload(1);\n\t} else {\n\t\tLPCWSTR a1 = a; while (*a1 != 0 && *a1 != ' ') a1++;\n\t\tif (a1 > a && *a1 == ' ') a1++; else return;\n\t\tCpp_Arch(a, a1);\n\t}\n\n\tExitProcess(0);\n}\n#else\nint APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR pCmdLine, int nCmdShow) {\n\n\t//MessageBox(0, pCmdLine, L\"test\", 0);\n\n\tif (*pCmdLine == 0) {\n\t\tCpp_Unload(1);\n\t} else {\n\t\tLPCWSTR a1 = pCmdLine; while (*a1 != 0 && *a1 != ' ') a1++;\n\t\tif (a1 > pCmdLine && *a1 == ' ') a1++; else return 1;\n\t\tCpp_Arch(pCmdLine, a1);\n\t}\n\n\treturn 0;\n}\n#endif\n"
  },
  {
    "path": "Other/Au.Net4/App.config",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<configuration>\n    <startup> \n        <supportedRuntime version=\"v4.0\" sku=\".NETFramework,Version=v4.8\"/>\n    </startup>\n</configuration>\n"
  },
  {
    "path": "Other/Au.Net4/Au.Net4.csproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <Import Project=\"$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props\" Condition=\"Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props')\" />\n  <PropertyGroup>\n    <Configuration Condition=\" '$(Configuration)' == '' \">Debug</Configuration>\n    <Platform Condition=\" '$(Platform)' == '' \">AnyCPU</Platform>\n    <ProjectGuid>{2157BC47-A5E1-405C-A003-F44970DC3718}</ProjectGuid>\n    <OutputType>Exe</OutputType>\n    <RootNamespace>Au.Net4</RootNamespace>\n    <AssemblyName>Au.Net4</AssemblyName>\n    <TargetFrameworkVersion>v4.8</TargetFrameworkVersion>\n    <FileAlignment>512</FileAlignment>\n    <Deterministic>true</Deterministic>\n    <TargetFrameworkProfile />\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' \">\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <DebugSymbols>true</DebugSymbols>\n    <DebugType>full</DebugType>\n    <Optimize>false</Optimize>\n    <OutputPath>..\\..\\_\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' \">\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <DebugType>pdbonly</DebugType>\n    <Optimize>true</Optimize>\n    <OutputPath>..\\..\\_\\</OutputPath>\n    <DefineConstants>TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n    <Prefer32Bit>false</Prefer32Bit>\n  </PropertyGroup>\n  <ItemGroup>\n    <Reference Include=\"System\" />\n    <Reference Include=\"System.Core\" />\n    <Reference Include=\"System.Xml.Linq\" />\n    <Reference Include=\"System.Data.DataSetExtensions\" />\n    <Reference Include=\"Microsoft.CSharp\" />\n    <Reference Include=\"System.Data\" />\n    <Reference Include=\"System.Net.Http\" />\n    <Reference Include=\"System.Xml\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Compile Include=\"Net4.cs\" />\n    <Compile Include=\"Properties\\AssemblyInfo.cs\" />\n    <Compile Include=\"TypelibConverter.cs\" />\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"App.config\" />\n  </ItemGroup>\n  <Import Project=\"$(MSBuildToolsPath)\\Microsoft.CSharp.targets\" />\n</Project>"
  },
  {
    "path": "Other/Au.Net4/Net4.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Runtime.InteropServices;\nusing System.Text;\n\n[module: DefaultCharSet(CharSet.Unicode)]\n\nclass Net4 {\n\t[STAThread]\n\tstatic int Main(string[] args) {\n\t\tConsole.InputEncoding = Encoding.UTF8;\n\t\tConsole.OutputEncoding = Encoding.UTF8;\n\n\t\tswitch (args[0]) {\n\t\tcase \"/typelib\":\n\t\t\treturn TypelibConverter.Convert(args[1]);\n\t\t}\n\t\treturn 1;\n\t}\n}\n"
  },
  {
    "path": "Other/Au.Net4/Properties/AssemblyInfo.cs",
    "content": "using System.Reflection;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\n\n// General Information about an assembly is controlled through the following\n// set of attributes. Change these attribute values to modify the information\n// associated with an assembly.\n[assembly: AssemblyTitle(\"Au.Net4\")]\n[assembly: AssemblyDescription(\"\")]\n[assembly: AssemblyConfiguration(\"\")]\n[assembly: AssemblyCompany(\"\")]\n[assembly: AssemblyProduct(\"Au.Net4\")]\n[assembly: AssemblyCopyright(\"Copyright © 2025\")]\n[assembly: AssemblyTrademark(\"\")]\n[assembly: AssemblyCulture(\"\")]\n\n// Setting ComVisible to false makes the types in this assembly not visible\n// to COM components.  If you need to access a type in this assembly from\n// COM, set the ComVisible attribute to true on that type.\n[assembly: ComVisible(false)]\n\n// The following GUID is for the ID of the typelib if this project is exposed to COM\n[assembly: Guid(\"2157bc47-a5e1-405c-a003-f44970dc3718\")]\n\n// Version information for an assembly consists of the following four values:\n//\n//      Major Version\n//      Minor Version\n//      Build Number\n//      Revision\n//\n// You can specify all the values or you can default the Build and Revision Numbers\n// by using the '*' as shown below:\n// [assembly: AssemblyVersion(\"1.0.*\")]\n[assembly: AssemblyVersion(\"1.0.0.0\")]\n[assembly: AssemblyFileVersion(\"1.0.0.0\")]\n"
  },
  {
    "path": "Other/Au.Net4/TypelibConverter.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.IO;\nusing System.Reflection;\nusing System.Reflection.Emit;\nusing System.Runtime.InteropServices;\nusing System.Runtime.InteropServices.ComTypes;\nusing System.Text;\n\nusing _TYPELIBATTR = System.Runtime.InteropServices.ComTypes.TYPELIBATTR;\n\nunsafe class TypelibConverter {\n\n\tpublic static int Convert(string param) {\n\t\tvar a = param.Split('|');\n\t\tstring asmDir = a[0], comDll = a[1];\n\n\t\tint hr = LoadTypeLibEx(comDll, 2, out var tl);\n\t\tif (hr != 0) {\n\t\t\tPrint($\"Failed to load type library '{comDll}'.\\r\\n\\t{new Win32Exception(hr).Message}\");\n\t\t\treturn -1;\n\t\t}\n\n\t\ttry {\n\t\t\tvar c = new _TypelibConverter { saveDir = asmDir };\n\t\t\tc.Convert(tl);\n\t\t}\n\t\tcatch (Exception ex) { Print($\"Failed to convert type library '{comDll}'.\\r\\n\\t{ex.Message}\"); return -2; }\n\t\treturn 0;\n\t}\n\n\t[DllImport(\"oleaut32.dll\", EntryPoint = \"#183\", PreserveSig = true)]\n\tstatic extern int LoadTypeLibEx(string szFile, int regkind, out ITypeLib pptlib);\n\n\tclass _TypelibConverter : ITypeLibImporterNotifySink {\n\t\tstatic Dictionary<string, AssemblyBuilder> s_converted = new Dictionary<string, AssemblyBuilder>();\n\n\t\tpublic string saveDir;\n\n\t\tpublic Assembly Convert(ITypeLib tl) {\n\t\t\ttl.GetLibAttr(out IntPtr ipta);\n\t\t\tvar ta = *(_TYPELIBATTR*)ipta;\n\t\t\ttl.ReleaseTLibAttr(ipta);\n\t\t\tvar hash = _Fnv1((byte*)&ta, sizeof(_TYPELIBATTR) - 2).ToString(\"x\");\n\n\t\t\ttl.GetDocumentation(-1, out var tlName, out var tlDescription, out var _, out var _);\n\t\t\tvar fileName = $\"{tlName} {ta.wMajorVerNum}.{ta.wMinorVerNum} #{hash}.dll\";\n\t\t\tvar netPath = saveDir + fileName;\n\n\t\t\tif (!s_converted.TryGetValue(fileName, out var asm) || !File.Exists(netPath)) {\n\t\t\t\tPrint($\"<>Converted: {tlName} ({tlDescription}) to <explore {netPath}>{fileName}<>\");\n\n\t\t\t\tvar converter = new TypeLibConverter();\n\t\t\t\tasm = converter.ConvertTypeLibToAssembly(tl, netPath,\n\t\t\t\t\tTypeLibImporterFlags.ReflectionOnlyLoading | TypeLibImporterFlags.TransformDispRetVals | TypeLibImporterFlags.UnsafeInterfaces,\n\t\t\t\t\tthis, null, null, tlName, null);\n\t\t\t\tasm.Save(fileName);\n\t\t\t\ts_converted[fileName] = asm;\n\t\t\t}\n\t\t\treturn asm;\n\t\t}\n\n\t\tvoid ITypeLibImporterNotifySink.ReportEvent(ImporterEventKind eventKind, int eventCode, string eventMsg) {\n\t\t\tif (eventKind != ImporterEventKind.NOTIF_TYPECONVERTED) Print(\"Warning: \" + eventMsg);\n\t\t}\n\n\t\tAssembly ITypeLibImporterNotifySink.ResolveRef(object typeLib) => Convert(typeLib as ITypeLib);\n\t}\n\n\tstatic void Print(object o) => Console.WriteLine(o?.ToString());\n\n\t//code copied from Hash\n\tstatic int _Fnv1(byte* data, int lengthBytes) {\n\t\tuint hash = 2166136261;\n\n\t\tfor (int i = 0; i < lengthBytes; i++)\n\t\t\thash = (hash * 16777619) ^ data[i];\n\n\t\treturn (int)hash;\n\t}\n}\n"
  },
  {
    "path": "Other/BuildEvents/Au.TestInternal.cs",
    "content": "using Microsoft.CodeAnalysis.Collections;\nusing System;\nusing System.Collections.Generic;\nusing System.Collections.Immutable;\n\n//no, then many errors when compiling that project\n//[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(\"Microsoft.CodeAnalysis.Workspaces, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9\")]\n\nnamespace RoslynMod;\n\npublic static class TestInternal\n{\n    internal static Func<string, string, bool>? IsInternalsVisibleCallback;\n    internal static Action<string, HashSet<string>>? AppendInternalsVisibleCallback;\n    static ImmutableArray<byte>[]? s_a1;\n\n    public static IEnumerable<ImmutableArray<byte>> IsInternalsVisible(string thisName, string toName)\n    {\n        if (IsInternalsVisibleCallback?.Invoke(thisName, toName) ?? false)\n            return s_a1 ??= new ImmutableArray<byte>[1] { default };\n        return SpecializedCollections.EmptyEnumerable<ImmutableArray<byte>>();\n    }\n\n    //public static bool IsInternalsVisible(string thisName, string toName)\n    //{\n    //    return IsInternalsVisibleCallback1?.Invoke(thisName, toName) ?? false;\n    //}\n\n    public static void AppendInternalsVisible(string thisName, HashSet<string> toNames)\n    {\n        AppendInternalsVisibleCallback?.Invoke(thisName, toNames);\n    }\n}\n\npublic static class Print\n{\n    internal static Action<object>? PrintItCallback;\n\n    public static void it(object o)\n    {\n        PrintItCallback?.Invoke(o);\n    }\n}\n"
  },
  {
    "path": "Other/BuildEvents/BuildEvents.cs",
    "content": "// Build event script for Au.Editor and Cpp projects.\n\nusing Microsoft.Win32;\n\nscript.setup(exception: UExcept.Dialog | UExcept.Print);\n\n//print.ignoreConsole = true;\n//print.qm2.use = true;\n//print.it(args);\n\n//if (args.Length == 0) { //dev\n//\tEnvironment.CurrentDirectory = @\"C:\\code\\au\";\n//\t//return GitBinaryFiles.PrePushHook();\n//\treturn GitBinaryFiles.Restore(Environment.CurrentDirectory + \"\\\\\", true);\n//}\n\nstring solutionDirBS = folders.ThisAppBS[..^28];\n\nreturn args[0] switch {\n\t\"cppPostBuild\" => CppPostBuild(), //$(SolutionDir)Other\\BuildEvents\\bin\\Debug\\BuildEvents.exe cppPostBuild $(Configuration) $(Platform)\n\t\"preBuild\" => EditorPreBuild(), //$(SolutionDir)Other\\BuildEvents\\bin\\Debug\\BuildEvents.exe preBuild $(Configuration)\n\t\"postBuild\" => EditorPostBuild(), //$(SolutionDir)Other\\BuildEvents\\bin\\Debug\\BuildEvents.exe postBuild $(Configuration)\n\t\"dllPostBuild\" => DllPostBuild(), //$(SolutionDir)Other\\BuildEvents\\bin\\Debug\\BuildEvents.exe dllPostBuild \"$(TargetPath)\" $(Platform)\n\t\"roslynPostBuild\" => RoslynPostBuild(),\n\t\"gitPrePushHook\" => GitBinaryFiles.PrePushHook(),\n\t_ => 1\n};\n\n/// Exits editor. Copies AuCpp.dll and unloads the old dll from processes.\nint CppPostBuild() {\n\t_ExitEditor();\n\tif (!_CopyAuCppDllIfNeed(args[2], false)) return 1;\n\treturn 0;\n}\n\n/// Exits editor. If need, copies AuCpp.dll and unloads the old dll from processes.\nint EditorPreBuild() {\n\t_ExitEditor();\n\t_CopyAuCppDllIfNeed(\"Win32\", true);\n\t_CopyAuCppDllIfNeed(\"x64\", true);\n\t_CopyAuCppDllIfNeed(\"ARM64\", true);\n\treturn GitBinaryFiles.Restore(solutionDirBS);\n}\n\n/// Exits editor. Copies the dll (eg Scintilla).\nint DllPostBuild() {\n\t_ExitEditor();\n\tvar toDir = $@\"{solutionDirBS}_\\{args[2] switch { \"x64\" => \"64\", \"ARM64\" => @\"64\\ARM\", _ => throw new ArgumentException(\"platform\") }}\";\n\tfilesystem.copyTo(args[1], toDir, FIfExists.Delete);\n\treturn 0;\n}\n\nvoid _ExitEditor() {\n\tfor (int i = 2; --i >= 0;) {\n\t\tvar w = wnd.findFast(cn: \"Au.Editor.TrayNotify\");\n\t\tif (!w.Is0) {\n\t\t\tw.Close(noWait: true);\n\t\t\tw.WaitForClosed(-2, waitUntilProcessEnds: true);\n\t\t}\n\t}\n}\n\nbool _CopyAuCppDllIfNeed(string platform, bool editor) {\n\tstring src = $@\"{solutionDirBS}Cpp\\bin\\{args[1]}\\{platform}\\AuCpp.dll\";\n\tstring dest = $@\"{solutionDirBS}_\\{platform switch { \"Win32\" => \"32\", \"x64\" => \"64\", \"ARM64\" => @\"64\\ARM\", _ => throw new ArgumentException(\"platform\") }}\\AuCpp.dll\";\n\tif (!filesystem.getProperties(src, out var p1)) { if (!editor) print.it(\"Failed `filesystem.getProperties(src)`\"); return false; }\n\tfilesystem.getProperties(dest, out var p2);\n\tif (p1.LastWriteTimeUtc != p2.LastWriteTimeUtc || p1.Size != p2.Size) {\n\t\tprint.it($\"Updating {dest}\");\n\t\tif (p2.Size != 0 && !_Api.DeleteFile(dest)) {\n\t\t\t_Api.Cpp_Unload(1);\n\t\t\twait.until(-3, () => filesystem.delete(dest, FDFlags.CanFail) != false);\n\t\t}\n\t\tfilesystem.copy(src, dest);\n\t}\n\treturn true;\n}\n\n/// Creates Au.Editor.exe and Au.Task.exe for x64 and ARM64.\n/// Uses our Au.AppHost.exe as template. Adds resources.\n/// Deletes Au.Editor.*.json files.\nint EditorPostBuild() {\n\t//perf.first();\n\tstring exe1 = \"Au.Editor.exe\", exe2 = \"Au.Task.exe\";\n\tif (script.testing) {\n\t\texe1 = \"Au.Editor2.exe\"; exe2 = \"Au.Task2.exe\";\n\t\tprint.ignoreConsole = true;\n\t\t//todo: Environment.CurrentDirector = \n\t}\n\tvar dirOut = solutionDirBS + @\"_\\\";\n\n#if !true //copy output files from `$(ProjectDir)$(OutDir)` to dirOut. Bad: can't start LA from VS (no program path setting in UI; VS ignores executablePath in launchsettings.json).\n\tvar dirBin = args[2][..^1];\n\tint rce = run.console(out string rco, \"robocopy.exe\", $\"{dirBin} {dirOut} /s /xd runtimes /xf *.json *.exe\");\n\tif (rce >= 8) { print.it(rco); return 10; }\n\tforeach (var rtd in new string[] { @\"\\runtimes\\win-x64\", @\"\\runtimes\\win-arm64\" }) {\n\t\tvar sd1 = dirBin + rtd;\n\t\tif (filesystem.exists(sd1)) run.console(\"robocopy.exe\", $\"{sd1} {dirOut + rtd} /s /xf *.json *.exe\");\n\t}\n#else //use `<OutDir>`. Bad: VS adds unwanted files to dirOut. Eg from NuGet packages may add native dlls for many unused OS/platforms.\n\tfilesystem.delete(Directory.GetFiles(dirOut, \"Au.Editor.*.json\"));\n#endif\n\n\tusing var rk = Registry.CurrentUser.CreateSubKey(@\"Software\\Au\\BuildEvents\");\n\tvar verResFile1 = folders.ThisApp + $\"{exe1}.res\";\n\tvar verResFile2 = folders.ThisApp + $\"{exe2}.res\";\n\n\tvar v = FileVersionInfo.GetVersionInfo(dirOut + \"Au.Editor.dll\");\n\tbool verChanged = !(rk.GetValue(\"version\") is string s1 && s1 == v.FileVersion);\n\t//verChanged = true;\n\tif (verChanged || !filesystem.exists(verResFile1)) if (!_VersionInfo(verResFile1, exe1, \"LibreAutomate\")) return 1;\n\tif (verChanged || !filesystem.exists(verResFile2)) if (!_VersionInfo(verResFile2, exe2, \"LibreAutomate miniProgram\")) return 2;\n\n\t//TODO3: test https://github.com/resourcelib/resourcelib\n\n\tfor (int i = 0; i < 2; i++) {\n\t\tstring appHost = $@\"64\\{(i == 1 ? \"ARM\" : \"\")}\\Au.AppHost.exe\";\n\t\tstring exe1Arch = exe1.Insert(^4, i == 0 ? \"\" : \"-arm\"), exe2Arch = exe2.Insert(^4, i == 0 ? \"-x64\" : \"-arm\");\n\t\tvar s = $\"\"\"\n[FILENAMES]\nExe={appHost}\nSaveAs={exe1Arch}\n[COMMANDS]\n-add ..\\Au.Editor\\Resources\\ico\\app.ico, ICONGROUP,32512,0\n-add ..\\Au.Editor\\Resources\\ico\\app_disabled.ico, ICONGROUP,32513,0\n-add ..\\Au.Editor\\Resources\\ico\\PictureInPicture.ico, ICONGROUP,32514,0\n-addoverwrite ..\\Au.Editor\\Resources\\Au.manifest, MANIFEST,1,0\n-add dotnet_ref_editor.txt, 220,1,0\n-add \"{verResFile1}\", VERSIONINFO,1,0\n\n\"\"\";\n\t\tif (!_RunRhScript(s, exe1Arch)) return 3;\n\n\t\tfilesystem.getProperties(dirOut + @\"64\\Au.AppHost.exe\", out var p1);\n\t\tif (verChanged || !filesystem.getProperties(dirOut + exe2Arch, out var p2) || p1.LastWriteTimeUtc > p2.LastWriteTimeUtc) {\n\t\t\tprint.it($\"Creating {exe2Arch}\");\n\t\t\ts = $\"\"\"\n[FILENAMES]\nExe={appHost}\nSaveAs={exe2Arch}\n[COMMANDS]\n-add ..\\Au.Editor\\Resources\\ico\\Script.ico, ICONGROUP,32512,0\n-addoverwrite ..\\Au.Editor\\Resources\\Au.manifest, MANIFEST,1,0\n-add dotnet_ref_task.txt, 220,1,0\n-add \"{verResFile2}\", VERSIONINFO,1,0\n\n\"\"\";\n\t\t\tif (!_RunRhScript(s, exe2Arch)) return 4;\n\t\t}\n\n\t\t_WriteEditorAssemblyName(dirOut + exe1Arch);\n\t}\n\n\tif (verChanged) {\n\t\trk.SetValue(\"version\", v.FileVersion);\n\t}\n\n\t//perf.nw();\n\treturn 0;\n\n\tbool _RunRhScript(string s, string fileName) {\n\t\tvar exePath = dirOut + fileName;\n\t\tfilesystem.delete(exePath);\n\t\tusing var tf = new TempFile();\n\t\tfilesystem.saveText(tf, s);\n\t\tif (!_RunCL($\"-script \\\"{tf.File}\\\"\")) return false;\n\t\tvar dt = DateTime.UtcNow;\n\t\tFile.SetCreationTimeUtc(exePath, dt);\n\t\tFile.SetLastWriteTimeUtc(exePath, dt);\n\t\treturn true;\n\t}\n\n\tbool _RunCL(string cl) {\n\t\tvar rh = solutionDirBS + @\"Other\\BuildEvents\\.tools\\ResourceHacker\";\n\t\tint r = run.console(rh + \".exe\", cl, dirOut);\n\t\tif (r == 0) return true;\n\t\tprint.it(File.ReadAllText(rh + \".log\", Encoding.Unicode)); //RH is not a console program and we cannot capture its console output.\n\t\treturn false;\n\t}\n\n\tstatic void _WriteEditorAssemblyName(string path) {\n\t\tvar b = File.ReadAllBytes(path);\n\t\tint i = b.AsSpan().IndexOf(\"hi7yl8kJNk+gqwTDFi7ekQ\"u8);\n\t\tb[i - 1] = 1; //1 for Au.Editor.exe, 0 for Au.Task.exe (and no assembly filename), else first char of exeProgram assembly filename\n\t\tstring asmName = \"Au.Editor.dll\";\n\t\ti += Encoding.UTF8.GetBytes(asmName, 0, asmName.Length, b, i);\n\t\tb[i] = 0;\n\t\tfilesystem.saveBytes(path, b);\n\t}\n\n\tbool _VersionInfo(string resFile, string fileName, string fileDesc) {\n\t\tprint.it($\"Creating version resource for {fileName}\");\n\t\tvar rc = $$\"\"\"\n\n1 VERSIONINFO\nFILEVERSION {{v.FileMajorPart}},{{v.FileMinorPart}},{{v.FileBuildPart}},{{v.FilePrivatePart}}\nPRODUCTVERSION {{v.ProductMajorPart}},{{v.ProductMinorPart}},{{v.ProductBuildPart}},{{v.ProductPrivatePart}}\nFILEOS 0x4\nFILETYPE 0x1\n{\nBLOCK \"StringFileInfo\"\n{\n\tBLOCK \"000004b0\"\n\t{\n\t\tVALUE \"CompanyName\", \"{{v.CompanyName}}\"\n\t\tVALUE \"FileDescription\", \"{{fileDesc}}\"\n\t\tVALUE \"FileVersion\", \"{{v.FileVersion}}\"\n\t\tVALUE \"InternalName\", \"{{fileName[..^4]}}\"\n\t\tVALUE \"LegalCopyright\", \"Copyright 2020-{{DateTime.UtcNow.Year}} Gintaras Didžgalvis\"\n\t\tVALUE \"OriginalFilename\", \"{{fileName}}\"\n\t\tVALUE \"ProductName\", \"{{v.ProductName}}\"\n\t\tVALUE \"ProductVersion\", \"{{v.ProductVersion}}\"\n\t}\n}\n\nBLOCK \"VarFileInfo\"\n{\n\tVALUE \"Translation\", 0x0000 0x04B0  \n}\n}\n\n\"\"\";\n\t\tusing var rcFile = new TempFile(\".rc\");\n\t\tfilesystem.saveText(rcFile, rc, encoding: Encoding.UTF8);\n\t\treturn _RunCL($\"\"\"-open \"{rcFile}\" -save \"{resFile}\" -action compile\"\"\");\n\t}\n}\n\n//Exits editor. Copies dlls etc.\nint RoslynPostBuild() {\n\t_ExitEditor();\n\n\tvar from = args[1].Trim();\n\tvar to = $@\"{solutionDirBS}_\\Roslyn\";\n\t\n\tforeach (var f in filesystem.enumFiles(to)) {\n\t\tfilesystem.delete(f.FullPath, FDFlags.CanFail);\n\t}\n\tforeach (var f in filesystem.enumFiles(from)) {\n\t\tif (0 == f.Name.Ends(true, \".dll\", \".xml\")) continue;\n\t\tif (0 != f.Name.Starts(true, \"System.Configuration.\", \"System.Security.\")) continue;\n\t\tfilesystem.copyTo(f.FullPath, to);\n\t}\n\treturn 0;\n}\n\nunsafe class _Api : NativeApi {\n\t[DllImport(\"kernel32.dll\", EntryPoint = \"DeleteFileW\", SetLastError = true)]\n\tinternal static extern bool DeleteFile(string lpFileName);\n\n\t/// <param name=\"flags\">1 - wait less.</param>\n\t[DllImport(\"AuCpp.dll\", CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern void Cpp_Unload(uint flags);\n}\n"
  },
  {
    "path": "Other/BuildEvents/BuildEvents.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n\t<PropertyGroup>\n\t\t<OutputType>Exe</OutputType>\n\t\t<TargetFramework>net10.0-windows</TargetFramework>\n\t\t<Configurations>Debug</Configurations>\n\t\t<GenerateAssemblyInfo>false</GenerateAssemblyInfo>\n\t\t<AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n\t\t<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>\n\t\t<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>\n\t\t<LangVersion>preview</LangVersion>\n\t\t<NoDefaultLaunchSettingsFile>true</NoDefaultLaunchSettingsFile>\n\t\t<SignAssembly>true</SignAssembly>\n\t\t<AssemblyOriginatorKeyFile>..\\..\\Au.snk</AssemblyOriginatorKeyFile>\n\t</PropertyGroup>\n\n\t<ItemGroup>\n\t  <Compile Remove=\"Au.TestInternal.cs\" />\n\t</ItemGroup>\n\n\t<ItemGroup>\n\t\t<Compile Include=\"..\\..\\Au\\resources\\global2.cs\" Link=\"global2.cs\" />\n\t</ItemGroup>\n\n\t<ItemGroup>\n\t  <None Include=\"Au.TestInternal.cs\" />\n\t</ItemGroup>\n\n\t<ItemGroup>\n\t  <PackageReference Include=\"LibreAutomate\" Version=\"1.12.0\" />\n\t  <PackageReference Include=\"SSH.NET\" Version=\"2024.2.0\" />\n\t</ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "Other/BuildEvents/GitBinaryFiles.cs",
    "content": "static class GitBinaryFiles {\n\t/// <summary>\n\t/// To call this when pushing LA to GitHub, add file `.git\\hooks\\pre-push`.\n\t/// <code><![CDATA[\n\t/// #!/bin/sh\n\t/// \n\t/// \"Other/BuildEvents/bin/Debug/BuildEvents.exe\" \"gitPrePushHook\"\n\t/// exit $?\n\t/// ]]></code>\n\t/// </summary>\n\tpublic static int PrePushHook() {\n\t\tvar laDir = Environment.CurrentDirectory + @\"\\_\\\";\n\n\t\t(string dir, string files, bool subdirs)[] enumFiles = [\n\t\t\t\t(null, \"*.db\", false),\n\t\t\t\t(null, \"*MSTSCLib.dll\", false),\n\t\t\t\t(null, \"toc.json\", false),\n\t\t\t\t(null, \"toc-ai.yml\", false),\n\t\t\t\t(null, \"xrefmap.yml\", false),\n\t\t\t\t(\"Roslyn\", \"*.dll\", false),\n\t\t\t\t(\"Debugger\", \"**m *.dll||*.exe\", true),\n\t\t\t\t(@\"..\\Other\\BuildEvents\\.tools\", \"ResourceHacker.exe\", false),\n\t\t\t];\n\n\t\tList<FEFile> aFiles = new();\n\t\tforeach (var v in enumFiles) {\n\t\t\tforeach (var f in filesystem.enumFiles(laDir + v.dir, v.files, (v.subdirs ? FEFlags.AllDescendants : 0) | FEFlags.UseRawPath)) {\n\t\t\t\t//if (f.Name.Like(\"doc-*.db\")) continue; //BAD: changes too frequently\n\t\t\t\taFiles.Add(f);\n\t\t\t}\n\t\t}\n\t\t//print.it(aFiles);\n\n\t\tbool update = true;\n\t\tvar csvFile = laDir + \"gitBinary.csv\";\n\t\tif (filesystem.exists(csvFile)) {\n\t\t\tupdate = false;\n\t\t\tvar t = csvTable.load(csvFile);\n\t\t\tvar d = t.ToDictionary(ignoreCase: true, ignoreDuplicates: false);\n\t\t\tforeach (var f in aFiles) {\n\t\t\t\tvar rel = f.FullPath[laDir.Length..];\n\t\t\t\tif (filesystem.getProperties(f.FullPath, out var p) && d.TryGetValue(rel, out var prevTime)) {\n\t\t\t\t\tif (f.LastWriteTimeUtc.ToString(\"s\") == prevTime) continue;\n\t\t\t\t}\n\t\t\t\tupdate = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (update) {\n\t\t\tvar d = dialog.showProgress(true, \"Updating LaBinary.7z\", \".\");\n\t\t\td.Destroyed += k => { Environment.Exit(2); };\n\n\t\t\tvar t = new csvTable();\n\t\t\tvar b = new StringBuilder();\n\t\t\tforeach (var f in aFiles) {\n\t\t\t\tvar rel = f.FullPath[laDir.Length..];\n\t\t\t\tt.AddRow(rel, f.LastWriteTimeUtc.ToString(\"s\"));\n\t\t\t\tb.AppendLine(rel);\n\t\t\t}\n\t\t\tt.Save(csvFile);\n\n\t\t\tusing var listFile = new TempFile();\n\t\t\tfilesystem.saveText(listFile, b.ToString());\n\n\t\t\td.Send.ChangeText2(\"Compressing...\", false);\n\t\t\tvar zipFile = folders.ThisAppTemp + \"LaBinary.7z\";\n\t\t\tfilesystem.delete(zipFile);\n\t\t\tif (0 != run.console(out string s1, laDir + @\"32\\7za.exe\", $@\"a \"\"{zipFile}\"\" @\"\"{listFile}\"\"\", laDir)) {\n\t\t\t\tprint.it(s1);\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\t//run.it(zipFile); dialog.show(\"zip OK\");\n\n\t\t\td.Send.ChangeText2(\"Uploading...\", false);\n\t\t\tSftp.UploadToLA(\"domains/libreautomate.com/public_html/download\", zipFile);\n\n\t\t\tfilesystem.delete(zipFile, FDFlags.CanFail);\n\t\t}\n\n\t\treturn 0;\n\t}\n\n\tpublic static int Restore(string solutionDirBS, bool test = false) {\n\t\tvar laDir = solutionDirBS + @\"_\\\";\n\t\tif (test) {\n\t\t\tlaDir += @\"test\\\";\n\t\t\tfilesystem.saveText(laDir + \"gitBinaryRestore.csv\", \"restore\");\n\t\t} else {\n\t\t\tif (laDir.Eqi(@\"C:\\code\\au\\_\\\") && Directory.Exists(@\"C:\\code-lib\\roslyn\")) return 0; //we at home\n\t\t}\n\n\t\tstring restoreFile = laDir + \"gitBinaryRestore.csv\";\n\t\tif (filesystem.exists(restoreFile)) {\n\t\t\tvar zipFile = folders.ThisAppTemp + \"LaBinary.7z\";\n\t\t\tinternet.http.Get(\"https://www.libreautomate.com/download/LaBinary.7z\", true).Download(zipFile);\n\n\t\t\tint r = run.console(out string so, solutionDirBS + @\"_\\32\\7za.exe\", $@\"x \"\"{zipFile}\"\" -aoa\", laDir);\n\t\t\tif (r != 0) throw new AuException(\"Failed to extract LaBinary.7z. \" + so);\n\n\t\t\tfilesystem.moveTo(laDir + \"ResourceHacker.exe\", solutionDirBS + @\"Other\\BuildEvents\\.tools\");\n\n\t\t\tfilesystem.delete(restoreFile);\n\t\t}\n\n\t\treturn 0;\n\t}\n}\n"
  },
  {
    "path": "Other/BuildEvents/Readme - Roslyn.txt",
    "content": "LA uses modified Roslyn dlls.\nHow to install, update and modify:\n\nOnce: Build this project. Don't run; it will run automatically when building Roslyn project Microsoft.CodeAnalysis.CSharp.Features.\n\nFork and clone Roslyn:\n    1. Delete the old fork repo `https://github.com/qgindi/roslyn`.\n    2. Fork `https://github.com/dotnet/roslyn`. In repo settings set default branch = the latest release, like `release/dev18.3`.\n    3. Git-clone `https://github.com/qgindi/roslyn.git` to `C:\\code-lib\\roslyn`. Do shallow clone of default branch (full >2 GB). I clone with TortoiseGit.\n    Another way to update, not tested: in the GitHub fork repo page click `Sync fork`. It discards modifications. Then git-sync eg in VS. Bad: bloats .git.\nOpen `global.json`. May need to edit SDK version. Usually no. May need to install a newer SDK.\nOpen `Roslyn.sln`.\nTo make VS not so slow, select all folders and unload projects. Then load Microsoft.CodeAnalysis.CSharp.Features with entire dependency tree. Make it the startup project.\n  It loads projects we need:\n    In folder Compilers: Core\\Microsoft.CodeAnalysis, CSharp\\Microsoft.CodeAnalysis.CSharp.\n    In folder Features: Microsoft.CodeAnalysis.CSharp.Features, Microsoft.CodeAnalysis.Features.\n    In folder Workspaces: Microsoft.CodeAnalysis.CSharp.Workspaces, Microsoft.CodeAnalysis.Workspaces.\n    Several other.\nEdit as described below after _________.\nBuild Microsoft.CodeAnalysis.CSharp.Features. It also builds all dependency projects. It runs this exe.\nGit commit/push (if git-cloned).\n\nOnce: In editor project add references to the main 6 dlls in _\\Roslyn, with 'Copy local' false.\n  For Microsoft.CodeAnalysis.Workspaces set aliases CAW.\n\nRejected: to make editor startup faster, publish Microsoft.CodeAnalysis.CSharp.Features with <PublishReadyToRun>.\n  Tested, works, but: adds ~14 MB to the setup file; makes just ~350 ms faster, barely noticeable.\n\n_________________________________________________________________\n\n//Edit these manually. Often Roslyn source in new version is changed in some of these places.\n//Add only internal members (where possible). If public, need to declare it in PublicApi.Shipped.txt. Roslyn's internals are visible to the editor project.\n\n// - In all 6 projects + Scripting.csproj (but not CSharpSyntaxGenerator.csproj):\n//   From <TargetFrameworks> remove netstandard2.0 etc (replace with eg net10.0). Later will compile faster.\n\n// - Set Release config. Try to build Microsoft.CodeAnalysis.CSharp.Features (it builds all).\n\n// - In all 6 projects add:\n    <InternalsVisibleTo Include=\"Au.Editor\" Key=\"0024000004800000940000000602000000240000525341310004000001000100095c6b7a0fe60fbe4a77e52dd10a09331ee3c3a7399aa9cc17db8a015647469a19784d5e33a2450a0a49c37bf17c0c3223674f64104eae649ba27c51a90c24989faec87d59217d7850efc8151109bbf9b027b7714fc01788317d2b991b2c2669836a7725e942f76607efde5cdacd8c497a45c5f9673fcf102fdbf92237a524a4\" />\n\n// - In project Microsoft.CodeAnalysis add link to Au.TestInternal.cs. It is in this project.\n    <Compile Include=\"C:\\code\\au\\Other\\BuildEvents\\Au.TestInternal.cs\" Link=\"Au.TestInternal.cs\" />\n\n// - In Microsoft.CodeAnalysis.CSharp.Features.csproj:\n// -- In <PropertyGroup> add:\n    <OutputPath>$(SolutionDir)au\\output</OutputPath>\n    <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>\n    <AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>\n    <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>\n    <AccelerateBuildsInVisualStudio>false</AccelerateBuildsInVisualStudio>\n\n// -- In project references add <Private>True</Private>. Also add the Scripting project (just to copy the dll).\n    <ProjectReference Include=\"..\\..\\Core\\Portable\\Microsoft.CodeAnalysis.Features.csproj\">\n      <Private>True</Private>\n    </ProjectReference>\n    <ProjectReference Include=\"..\\..\\..\\Workspaces\\CSharp\\Portable\\Microsoft.CodeAnalysis.CSharp.Workspaces.csproj\">\n      <Private>True</Private>\n    </ProjectReference>\n    <ProjectReference Include=\"..\\..\\..\\Workspaces\\Core\\Portable\\Microsoft.CodeAnalysis.Workspaces.csproj\">\n      <Private>True</Private>\n    </ProjectReference>\n    <ProjectReference Include=\"..\\..\\..\\Compilers\\CSharp\\Portable\\Microsoft.CodeAnalysis.CSharp.csproj\">\n      <Private>True</Private>\n    </ProjectReference>\n    <ProjectReference Include=\"..\\..\\..\\Compilers\\Core\\Portable\\Microsoft.CodeAnalysis.csproj\">\n      <Private>True</Private>\n    </ProjectReference>\n    <ProjectReference Include=\"..\\..\\..\\Scripting\\Core\\Microsoft.CodeAnalysis.Scripting.csproj\">\n      <Private>True</Private>\n    </ProjectReference>\n\n// -- Add postbuild:\n  <Target Name=\"PostBuild\" AfterTargets=\"PostBuildEvent\">\n    <Exec Command=\"C:\\code\\au\\Other\\BuildEvents\\bin\\Debug\\BuildEvents.exe roslynPostBuild $(OutDir)\" />\n  </Target>\n\n// - In .gitignore add:\nau/\n\n// Run menu Build > Clean solution.\n\n\n// - Add Symbols property to the CompletionItem class:\n//1. Open CompletionItem.cs in project Microsoft.CodeAnalysis.Features.\n//2. Find method private CompletionItem With(...). In it find: return new CompletionItem...{\n//3. In the { } add line:\n            Symbols = Symbols, //au\n//4. Below the method add properties:\n    internal IReadOnlyList<ISymbol>? Symbols { get; set; } //au\n    internal object? Attach { get; set; } //au\n//5. Open Features\\Core\\Portable\\Completion\\Providers\\SymbolCompletionItem.cs.\n//6. In method CreateWorker find statement that starts with: var item = CommonCompletionItem.Create(\n//7. Below that statement add: item.Symbols = symbols; //au\n\n// - Add Symbol property to the SymbolKeySignatureHelpItem class:\n//1. Open Features\\Core\\Portable\\SignatureHelp\\AbstractSignatureHelpProvider.SymbolKeySignatureHelpItem.cs.\n//2. Add property: internal ISymbol? Symbol { get; } = symbol; //au\n\n// - Let it don't try to load VB assemblies, because then exception when debugging:\n//In MefHostServices.cs, in DefaultAssemblyNames init list, remove the 2 VisualBasic assemblies.\n\n// - In project Microsoft.CodeAnalysis, in file PublicAPI.Shipped.txt, append:\nRoslynMod.TestInternal\nstatic RoslynMod.TestInternal.IsInternalsVisible(string thisName, string toName) -> System.Collections.Generic.IEnumerable<System.Collections.Immutable.ImmutableArray<byte>>\nstatic RoslynMod.TestInternal.AppendInternalsVisible(string thisName, System.Collections.Generic.HashSet<string> toNames) -> void\nRoslynMod.Print\nstatic RoslynMod.Print.it(object o) -> void\n\n// - In project Microsoft.CodeAnalysis, in MetadataReader\\PEAssembly.cs, in GetInternalsVisibleToPublicKeys:\n//\treplace\n            return result ?? SpecializedCollections.EmptyEnumerable<ImmutableArray<byte>>();\n//\twith\n            //au\n            return result ?? RoslynMod.TestInternal.IsInternalsVisible(this.Identity.Name, simpleName);\n\n// - In project Microsoft.CodeAnalysis.CSharp, in Symbols\\Source\\SourceAssemblySymbol.cs, replace GetInternalsVisibleToPublicKeys with:\n\n        //au\n        internal override IEnumerable<ImmutableArray<byte>> GetInternalsVisibleToPublicKeys(string simpleName)\n        {\n            EnsureAttributesAreBound();\n\n            if (_lazyInternalsVisibleToMap != null && _lazyInternalsVisibleToMap.TryGetValue(simpleName, out var result))\n                return result.Keys;\n\n            return RoslynMod.TestInternal.IsInternalsVisible(this.Name, simpleName);\n        }\n        //internal override IEnumerable<ImmutableArray<byte>> GetInternalsVisibleToPublicKeys(string simpleName)\n        //{\n        //    //EDMAURER assume that if EnsureAttributesAreBound() returns, then the internals visible to map has been populated.\n        //    //Do not optimize by checking if m_lazyInternalsVisibleToMap is Nothing. It may be non-null yet still\n        //    //incomplete because another thread is in the process of building it.\n\n        //    EnsureAttributesAreBound();\n\n        //    if (_lazyInternalsVisibleToMap == null)\n        //        return SpecializedCollections.EmptyEnumerable<ImmutableArray<byte>>();\n\n        //    ConcurrentDictionary<ImmutableArray<byte>, Tuple<Location, string>> result = null;\n\n        //    _lazyInternalsVisibleToMap.TryGetValue(simpleName, out result);\n\n        //    return (result != null) ? result.Keys : SpecializedCollections.EmptyEnumerable<ImmutableArray<byte>>();\n        //}\n\n// - In project Microsoft.CodeAnalysis.Workspaces, in file DependentProjectsFinder.cs, in GetInternalsVisibleToSet, insert before 'return':\n        //au:\n        RoslynMod.TestInternal.AppendInternalsVisible(assembly.Name, set);\n"
  },
  {
    "path": "Other/BuildEvents/Sftp.cs",
    "content": "using Microsoft.Win32;\nusing Renci.SshNet;\nusing System.Text.Json;\nusing System.Windows.Controls;\n\nstatic class Sftp {\n\tpublic static SftpConnectionInfo GetConnectionInfo(string where = \"libreautomate.com\") {\n\t\tstring rk = @\"HKEY_CURRENT_USER\\Software\\Au\";\n\t\tvar j = JsonSerializer.Deserialize<SftpConnectionInfo>(Registry.GetValue(rk, where, \"{}\") as string);\n\t\tg1:\n\t\tif (j.host.NE() || j.port == 0 || j.user.NE() || j.pass.NE()) {\n\t\t\tvar b = new wpfBuilder(\"SSH connection\").WinSize(400);\n\t\t\tb.R.Add(\"IP\", out TextBox tIp, j.host).Focus(); //note: use IP. With hostname fails when using CloudFlare CDN.\n\t\t\tb.R.Add(\"Port\", out TextBox tPort, j.port.ToS());\n\t\t\tb.R.Add(\"User\", out TextBox tUser, j.user);\n\t\t\tb.R.Add(\"Password\", out TextBox tPass, j.pass.NE() ? null : Convert2.AesDecryptS(j.pass, \"8470\"));\n\t\t\tb.R.AddOkCancel();\n\t\t\tb.End();\n\t\t\tif (!b.ShowDialog()) return null;\n\t\t\tj.host = tIp.Text;\n\t\t\tj.port = tPort.Text.ToInt();\n\t\t\tj.user = tUser.Text;\n\t\t\tj.pass = Convert2.AesEncryptS(tPass.Text, \"8470\");\n\t\t\tRegistry.SetValue(rk, where, JsonSerializer.Serialize(j));\n\t\t\tgoto g1;\n\t\t}\n\t\tj.pass = Convert2.AesDecryptS(j.pass, \"8470\");\n\t\treturn j;\n\t}\n\t\n\tpublic static void ConnectAndUpload(this SftpClient ftp, string ftpDir, string localFile) {\n\t\tftp.Connect();\n\t\tftp.ChangeDirectory(ftpDir);\n\t\tusing var stream = File.OpenRead(localFile);\n\t\tftp.UploadFile(stream, pathname.getName(localFile));\n\t}\n\t\n\t/// <summary>\n\t/// Uploads a file to libreautomate.com (IP 194.5.156.231).\n\t/// </summary>\n\t/// <param name=\"ftpDir\">Eg <c>\"domains/libreautomate.com/public_html\"</c></param>\n\t/// <param name=\"localFile\"></param>\n\tpublic static void UploadToLA(string ftpDir, string localFile) {\n\t\tvar ci = Sftp.GetConnectionInfo(); if (ci == null) return;\n\t\tusing var ftp = new SftpClient(ci.host, ci.port, ci.user, ci.pass);\n\t\tConnectAndUpload(ftp, ftpDir, localFile);\n\t}\n}\n\nrecord SftpConnectionInfo {\n\tpublic string host { get; set; }\n\tpublic int port { get; set; }\n\tpublic string user { get; set; }\n\tpublic string pass { get; set; }\n}\n"
  },
  {
    "path": "Other/DatabasesEtc/DatabasesEtc.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n\t<PropertyGroup>\n\t\t<OutputType>WinExe</OutputType>\n\t\t<TargetFramework>net10.0-windows</TargetFramework>\n\t\t<Configurations>Debug</Configurations>\n\t\t<GenerateAssemblyInfo>false</GenerateAssemblyInfo>\n\t\t<AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n\t\t<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>\n\t\t<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>\n\t\t<LangVersion>preview</LangVersion>\n\t\t<NoDefaultLaunchSettingsFile>true</NoDefaultLaunchSettingsFile>\n\t\t<Platforms>AnyCPU</Platforms>\n\t</PropertyGroup>\n\n\t<ItemGroup>\n\t\t<Compile Include=\"..\\..\\Au\\resources\\global2.cs\" Link=\"global2.cs\" />\n\t</ItemGroup>\n\n\t<ItemGroup>\n\t\t<PackageReference Include=\"MahApps.Metro.IconPacks\" Version=\"5.1.0\" />\n\t</ItemGroup>\n\n\t<ItemGroup>\n\t\t<ProjectReference Include=\"..\\..\\Au\\Au.csproj\" />\n\t</ItemGroup>\n\n\t<Target Name=\"PostBuild\" AfterTargets=\"PostBuildEvent\">\n\t\t<Exec Command=\"robocopy.exe $(SolutionDir)_\\64 $(OutDir)\\64 AuCpp.dll /mir &gt;nul || exit 0\" />\n\t</Target>\n\n</Project>\n"
  },
  {
    "path": "Other/DatabasesEtc/Icons.cs",
    "content": "//In VS set this as startup project, and run. It creates file \"icons-new.db\" in \"_\" dir.\n//If everything OK: exit LA, delete \"icons.db\", rename \"icons-new.db\" -> \"icons.db\", run LA.\n\n//Also need to update and upload the AI embedding storage file.\n//\tThe \"AI search\" button in the Icons tool should auto-update.\n//\tAlso it should auto-run script \"Upload AI embeddings\", or print a link to run it.\n\n#define DB //undefine when debugging, to skip database code\n\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Runtime.Loader;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Markup;\nusing System.Xml.Linq;\n//using MahApps.Metro.IconPacks;\n\nstatic class Icons {\n\tpublic static void CreateDB() {\n\t\tnew Application();\n\n#if DB\n\t\tstring dbFile = Program.c_outputDirBS + \"icons-new.db\";\n\t\tfilesystem.delete(dbFile);\n\n\t\tusing var d = new sqlite(dbFile);\n\t\t//using var d = new sqlite(dbFile, sql: \"PRAGMA journal_mode=WAL\"); //no. Does not make select faster.\n\t\tusing var trans = d.Transaction();\n\t\td.Execute(\"CREATE TABLE _tables (name TEXT COLLATE NOCASE, template TEXT)\");\n#endif\n\n\t\tDictionary<string, HashSet<string>> dict = new(StringComparer.OrdinalIgnoreCase);\n\t\t//var duplData = new Dictionary<string, string>(); int nDupl = 0;\n\n\t\tvar alc = AssemblyLoadContext.Default;\n\t\tvar asmCore = alc.LoadFromAssemblyPath(folders.ThisApp + \"MahApps.Metro.IconPacks.Core.dll\");\n\t\tvar factoryGen = asmCore.GetType(\"MahApps.Metro.IconPacks.PackIconDataFactory`1\");\n\n\t\tint nTables = 0, nIcons = 0, nSkipped = 0;\n\t\tforeach (var dll in Directory.EnumerateFiles(folders.ThisApp, \"MahApps.Metro.IconPacks.?*.dll\")) {\n\t\t\tif (dll.Ends(\".Core.dll\", true)) continue;\n\t\t\t//print.it(dll);\n\t\t\tvar asm = alc.LoadFromAssemblyPath(dll);\n\t\t\tforeach (var ty in asm.ExportedTypes.Where(o => o.Name.Like(\"PackIcon*Kind\"))) {\n\t\t\t\tvar table = ty.Name[8..^4];\n\t\t\t\tvar templ = _GetTemplate(asm);\n\t\t\t\t//print.it(table, templ);\n\t\t\t\t//return;\n\t\t\t\tnTables++;\n\n#if DB\n\t\t\t\td.Execute(\"INSERT INTO _tables VALUES (?, ?)\", table, templ);\n\t\t\t\td.Execute($\"CREATE TABLE {table} (name TEXT PRIMARY KEY COLLATE NOCASE, data TEXT)\");\n\t\t\t\tusing var statInsert = d.Statement($\"INSERT INTO {table} VALUES (?, ?)\");\n#endif\n\n\t\t\t\tvar hNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);\n\t\t\t\tdict.Add(table, hNames);\n\n\t\t\t\tvar factory = factoryGen.MakeGenericType(ty);\n\t\t\t\tforeach (DictionaryEntry k in factory.GetMethod(\"Create\").Invoke(null, null) as IDictionary) {\n\t\t\t\t\tstring name = k.Key.ToString(), data = k.Value.ToString();\n\t\t\t\t\tif (data.NE()) continue; //icon \"None\"\n\t\t\t\t\tif (data.Length > 10000) {\n\t\t\t\t\t\t//print.it(name, data.Length);\n\t\t\t\t\t\tnSkipped++;\n\t\t\t\t\t\tcontinue; //nothing very useful\n\t\t\t\t\t}\n\n\t\t\t\t\t//if (!duplData.TryAdd(data, table + \".\" + name)) {\n\t\t\t\t\t//\t//1.2%. All from same assemblies. Often similar name, but often different.\n\t\t\t\t\t//\tnDupl++;\n\t\t\t\t\t//\tprint.it(\"DUPL DATA\", duplData[data], table + \".\" + name);\n\t\t\t\t\t//\tcontinue;\n\t\t\t\t\t//}\n\n\t\t\t\t\tif (!hNames.Add(name)) { //duplicate name like Snowflake and SnowFlake\n\t\t\t\t\t\t\t\t\t\t\t //print.it(\"DUPL NAME\", table + \".\" + name);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t//print.it(table, name);\n\n#if DB\n\t\t\t\t\tstatInsert.Bind(1, name).Bind(2, data);\n\t\t\t\t\tstatInsert.Step();\n\t\t\t\t\tstatInsert.Reset();\n#endif\n\t\t\t\t\tnIcons++;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n#if DB\n\t\tint nMissing = _AddMissingFromOldDB(d, dict);\n\n\t\ttrans.Commit();\n\t\td.Execute(\"VACUUM\");\n#else\n\t\tint nMissing = 0;\n#endif\n\n\t\tprint.it($\"Done. {nTables} tables, {nIcons} icons, skipped {nSkipped}. Also added {nMissing} missing old icons.\"/*, $\"{nDupl} duplicate\"*/); //29 tables, 25877 icons, skipped 37\n\t}\n\n\tstatic string _GetTemplate(Assembly asm) {\n\t\tvar asmname = asm.GetName().Name;\n\t\tint i = asmname.LastIndexOf('.') + 1;\n\t\tstring rn = \"PackIcon\" + asmname[i..];\n\t\tvar rd = new ResourceDictionary() { Source = new Uri($@\"pack://application:,,,/{asmname};component/themes/{rn.Lower()}.xaml\") };\n\t\t//print.it(rd);\n\t\tvar template = rd[\"MahApps.Templates.\" + rn] as ControlTemplate;\n\t\tstring xaml = XamlWriter.Save(template);\n\t\txaml = xaml.RxReplace(@\" xmlns(?::\\w+)?=\"\".+?\"\"\", \"\");\n\t\tvar x = XElement.Parse(xaml);\n\t\tx = x.Descendants(\"Path\").First();\n\t\treturn x.ToString();\n\t}\n\n#if DB\n\t//In new IconPacks versions some icons are renamed. For backward compatibility we need icons with old names too.\n\tstatic int _AddMissingFromOldDB(sqlite d, Dictionary<string, HashSet<string>> dict) {\n\t\tint nMissing = 0;\n\t\tusing var dOld = new sqlite(Program.c_outputDirBS + \"icons.db\", SLFlags.SQLITE_OPEN_READONLY);\n\t\tusing var stTables = dOld.Statement(\"SELECT name FROM _tables\");\n\t\twhile (stTables.Step()) {\n\t\t\tvar table = stTables.GetText(0);\n\t\t\tvar hsNamesNew = dict[table];\n\t\t\tusing var statInsert = d.Statement($\"INSERT INTO {table} VALUES (?, ?)\");\n\t\t\tusing var stNames = dOld.Statement($\"SELECT * FROM {table}\");\n\t\t\twhile (stNames.Step()) {\n\t\t\t\tvar name = stNames.GetText(0);\n\t\t\t\tif (!hsNamesNew.Contains(name)) {\n\t\t\t\t\t//print.it(table, name);\n\t\t\t\t\tnMissing++;\n\t\t\t\t\tstatInsert.Bind(1, name).Bind(2, stNames.GetText(1));\n\t\t\t\t\tstatInsert.Step();\n\t\t\t\t\tstatInsert.Reset();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn nMissing;\n\t}\n#endif\n}\n"
  },
  {
    "path": "Other/DatabasesEtc/Program.cs",
    "content": "static class Program {\n\tpublic const string c_outputDirBS = @\"C:\\code\\au\\_\\\";\n\n\t[STAThread]\n\tstatic void Main(string[] args) {\n\t\tscript.setup();\n\t\tprint.qm2.use = true;\n\t\tprint.clear();\n\n\t\tRefTxt.Create();\n\t\t//RefAndDoc.Create();\n\t\t//Icons.CreateDB();\n\t}\n}\n"
  },
  {
    "path": "Other/DatabasesEtc/RefAndDoc.cs",
    "content": "static class RefAndDoc {\n\t/// <summary>\n\t/// Creates SQLite databases containing design-time assemblies (ref.db) and XML doc files (doc.db) of a .NET runtime. The SDK must be installed.\n\t/// Run after changing .NET version of C# projects (<TargetFramework>...</TargetFramework>).\n\t/// Without it scripts cannot be compiled.\n\t/// Later can be called at any time if need to update something. At first exit editor (it locks databases).\n\t/// </summary>\n\t/// <remarks>\n\t/// Shows a list dialog.\n\t/// \tIf selected All, creates for all runtime versions starting from 6.0, with names ref.version.db (eg ref.6.0.0.db) and doc.version.db, in folder _.\n\t/// \tElse creates only for the selected runtime version, with names ref.db and doc.db, in folder _. Editor locks them therefore must not be running.\n\t/// We ship and at run time load databases of single version, named ref.db and doc.db. In the future could allow to download and use multiple versions.\n\t/// Also this function could allow users to create databases from SDKs installed on their PC, but currently this feature is not exposed. Would need to add UI and exception handling.\n\t/// ref.db contains dlls from 'dotnet\\packs' folder. They contain only metadata of public API, not all code like dlls in the 'dotnet\\shared' folder.\n\t/// \tWhy need it when we can load PortableExecutableReference from 'dotnet\\shared' folder? Because:\n\t/// \t\t1. They are big and may add 100 MB of process memory. We need to load all, because cannot know which are actually used in various stages of compilation.\n\t/// \t\t2. When loading from dll files, Windows Defender makes it as slow as 2.5 s or more, unless the files already are in OS file buffers.\n\t/// \t\t3. Better compatibility. See https://github.com/dotnet/standard/blob/master/docs/history/evolution-of-design-time-assemblies.md\n\t/// doc.db contains XML documentation files of .NET runtime assemblies. From the same 'dotnet\\packs' folder.\n\t/// \tWhy need it:\n\t/// \t\t1. Else users would have to download whole .NET SDK. Now need only runtimes.\n\t/// \t\t2. Parsed XML files can use eg 200 MB of process memory. Now we get doc of a single type/method/etc from database only when need; all other data is not in memory.\n\t/// </remarks>\n\tpublic static void Create() {\n\t\tstring dirPacks = @\"C:\\Program Files\\dotnet\\packs\";\n\t\tstring dirCore = dirPacks + @\"\\Microsoft.NETCore.App.Ref\\\";\n\t\tvar a = new List<string>();\n\t\tforeach (var f in filesystem.enumerate(dirCore)) { //for each version\n\t\t\tif (!f.IsDirectory) continue;\n\t\t\tvar s = f.Name;\n\t\t\tint v1 = s.ToInt(0, out int ne), v2 = s.ToInt(ne + 1);\n\t\t\tif (v1 < 6 || (v1 == 6 && v2 < 0)) continue; //must be 6.0 or later\n\t\t\ta.Add(s);\n\t\t}\n\t\ta.Add(\"All\");\n\t\tint i = dialog.showList(a, \"Create database\", \"For runtime\", footer: \"Note: These are .NET SDK reference assembly directories. Versions may not match versions of runtime and even SDK.\") - 1;\n\t\tif (i < 0) return;\n\t\tint n = a.Count - 1;\n\t\tif (i < n) {\n\t\t\t_CreateRefAndDoc(dirPacks, dirCore, a[i], false);\n\t\t} else {\n\t\t\tfor (i = 0; i < n; i++) _CreateRefAndDoc(dirPacks, dirCore, a[i], true);\n\t\t}\n\t\tprint.it(\"RefAndDoc.Create done.\");\n\t}\n\n\tstatic void _CreateRefAndDoc(string dirPacks, string dirCore, string version, bool all) {\n\t\tstring subdirRN = @\"\\ref\\net\" + version.RxReplace(@\"^\\d+\\.\\d+\\K.+\", @\"\\\", 1);\n\n\t\tvar dir1 = dirCore + version + subdirRN;\n\t\tif (!filesystem.exists(dir1, true).Directory) throw new DirectoryNotFoundException(\"Not found: \" + dir1);\n\n\t\t//find WindowsDesktop folder. Must have same X.X.X version. The preview or rc version may be different.\n\t\tbool preview; int i = version.IndexOf('-');\n\t\tif (preview = i > 0) version = version[..(i + 3)]; //eg \"8.0.0-rc\"\n\t\tstring verDesktop = null;\n\t\tstring dirDesktop = dirPacks + @\"\\Microsoft.WindowsDesktop.App.Ref\\\";\n\t\tforeach (var f in filesystem.enumerate(dirDesktop, FEFlags.UseRawPath)) { //for each version\n\t\t\tif (!f.IsDirectory) continue;\n\t\t\tvar s = f.Name;\n\t\t\tif (preview ? s.Starts(version, true) : s == version) { verDesktop = s; break; }\n\t\t}\n\t\tif (verDesktop == null) throw new DirectoryNotFoundException(\"Not found: WindowsDesktop SDK\");\n\t\tvar dir2 = dirDesktop + verDesktop + subdirRN;\n\t\tif (!filesystem.exists(dir2, true).Directory) throw new DirectoryNotFoundException(\"Not found: \" + dir2);\n\n\t\tstring dbRef, dbDoc;\n\t\tif (all) {\n\t\t\tdbRef = Program.c_outputDirBS + \"ref.\" + version + \".db\";\n\t\t\tdbDoc = Program.c_outputDirBS + \"doc.\" + version + \".db\";\n\t\t} else {\n\t\t\tdbRef = Program.c_outputDirBS + \"ref.db\";\n\t\t\tdbDoc = Program.c_outputDirBS + \"doc.db\";\n\t\t}\n\t\t_CreateRef(dbRef, dir1, dir2);\n\t\t_CreateDoc(dbDoc, dir1, dir2);\n\t}\n\n\tstatic void _CreateRef(string dbFile, string dir1, string dir2) {\n\t\tfilesystem.delete(dbFile);\n\t\tusing var d = new sqlite(dbFile);\n\t\tusing var trans = d.Transaction();\n\t\td.Execute(\"CREATE TABLE ref (name TEXT PRIMARY KEY, data BLOB)\");\n\t\tusing var statInsert = d.Statement(\"INSERT OR REPLACE INTO ref VALUES (?, ?)\");\n\n\t\t_AddDir(dir1, \"WindowsBase\", \"System.Drawing\");\n\t\t_AddDir(dir2);\n\n\t\ttrans.Commit();\n\t\td.Execute(\"VACUUM\");\n\n\t\tprint.it(\"Created \" + dbFile);\n\n\t\tvoid _AddDir(string dir, params string[] skip) {\n\t\t\tforeach (var f in filesystem.enumerate(dir)) {\n\t\t\t\tif (f.IsDirectory) continue;\n\t\t\t\tif (!f.Name.Ends(\".dll\", true)) continue;\n\t\t\t\tvar asmName = f.Name[..^4];\n\t\t\t\tif (skip.Contains(asmName)) continue;\n\t\t\t\t_AddFile(asmName, f.FullPath);\n\t\t\t\t//break;\n\t\t\t}\n\t\t}\n\n\t\tvoid _AddFile(string asmName, string asmFile) {\n\t\t\t//print.it(asmName);\n\t\t\tstatInsert.Bind(1, asmName);\n\t\t\tstatInsert.Bind(2, File.ReadAllBytes(asmFile));\n\t\t\tstatInsert.Step();\n\t\t\tstatInsert.Reset();\n\t\t}\n\t}\n\n\tstatic void _CreateDoc(string dbFile, string dir1, string dir2) {\n\t\tfilesystem.delete(dbFile);\n\t\tusing var d = new sqlite(dbFile, sql: \"PRAGMA page_size = 8192;\"); //8192 makes file smaller by 2-3 MB.\n\t\tusing var trans = d.Transaction();\n\t\td.Execute(\"CREATE TABLE doc (name TEXT PRIMARY KEY, xml TEXT)\");\n\t\tusing var statInsert = d.Statement(\"INSERT INTO doc VALUES (?, ?)\");\n\t\tusing var statDupl = d.Statement(\"SELECT xml FROM doc WHERE name=?\");\n\t\tvar haveRefs = new List<string>();\n\t\tvar uniq = new Dictionary<string, string>(); //name -> asmName\n\n\t\t//using var textFile = File.CreateText(Path.ChangeExtension(dbFile, \"txt\")); //test. Compresses almost 2 times better than db.\n\n\t\t_AddDir(dir1, \"WindowsBase\");\n\t\t_AddDir(dir2);\n\n\t\tstatInsert.BindAll(\".\", string.Join(\"\\n\", haveRefs)).Step();\n\n\t\ttrans.Commit();\n\t\td.Execute(\"VACUUM\");\n\n\t\tprint.it(\"Created \" + dbFile);\n\n\t\tvoid _AddDir(string dir, params string[] skip) {\n\t\t\tforeach (var f in filesystem.enumerate(dir)) {\n\t\t\t\tif (f.IsDirectory) continue;\n\t\t\t\tif (!f.Name.Ends(\".xml\", true)) continue;\n\t\t\t\tvar asmName = f.Name[..^4];\n\t\t\t\tif (skip.Contains(asmName)) continue;\n\t\t\t\tif (!filesystem.exists(dir + asmName + \".dll\").File) {\n\t\t\t\t\tprint.it(\"<><c 0x808080>\" + f.Name + \"</c>\");\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t_AddFile(asmName, f.FullPath);\n\t\t\t\t//break;\n\t\t\t}\n\t\t}\n\n\t\tvoid _AddFile(string asmName, string xmlFile) {\n\t\t\t//print.it(asmName);\n\t\t\thaveRefs.Add(asmName);\n\t\t\tvar xr = XmlUtil.LoadElem(xmlFile);\n\t\t\tforeach (var e in xr.Descendants(\"member\")) {\n\t\t\t\tvar name = e.Attr(\"name\");\n\n\t\t\t\t//remove <remarks> and <example>. Does not save much space, because .NET xmls don't have it.\n\t\t\t\tforeach (var v in e.Descendants(\"remarks\").ToArray()) v.Remove();\n\t\t\t\tforeach (var v in e.Descendants(\"example\").ToArray()) v.Remove();\n\n\t\t\t\tusing var reader = e.CreateReader();\n\t\t\t\treader.MoveToContent();\n\t\t\t\tvar xml = reader.ReadInnerXml();\n\t\t\t\t//print.it(name, xml);\n\n\t\t\t\t//textFile.WriteLine(name); textFile.WriteLine(xml); textFile.WriteLine(\"\\f\");\n\n\t\t\t\tif (uniq.TryGetValue(name, out var prevRef)) {\n\t\t\t\t\tif (!statDupl.Bind(1, name).Step()) throw new AuException();\n\t\t\t\t\tvar prev = statDupl.GetText(0);\n\t\t\t\t\tif (xml != prev && asmName != \"System.Linq\") print.it($\"<>\\t{name} already defined in {prevRef}\\r\\n<c 0xc000>{prev}</c>\\r\\n<c 0xff0000>{xml}</c>\");\n\t\t\t\t\tstatDupl.Reset();\n\t\t\t\t} else {\n\t\t\t\t\tstatInsert.BindAll(name, xml).Step();\n\t\t\t\t\tuniq.Add(name, asmName);\n\t\t\t\t}\n\t\t\t\tstatInsert.Reset();\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Other/DatabasesEtc/RefTxt.cs",
    "content": "static class RefTxt {\n\t/// <summary>\n\t/// Creates dotnet_ref_editor.txt and dotnet_ref_task.txt.\n\t/// Run immediately after changing .NET version of C# projects (<TargetFramework>...</TargetFramework>).\n\t/// Then update .NET version in AppHost.cpp and build.\n\t/// Without it editor cannot start.\n\t/// Later can be called at any time if need to update something.\n\t/// </summary>\n\t/// <exception cref=\"AuException\"></exception>\n\tpublic static void Create() {\n\t\tvar b = new StringBuilder();\n\t\tvar hd = new HashSet<string>(StringComparer.OrdinalIgnoreCase);\n\t\t_Dir(folders.NetRuntimeDesktopBS + \"Microsoft.WindowsDesktop.App.deps.json\", \"*d|\"); //must be first, because several assemblies are in both folders, and need to use from Desktop\n\t\t_Dir(folders.NetRuntimeBS + \"Microsoft.NETCore.App.deps.json\", \"|*c|\");\n\n\t\t//Au.Task\n\t\tb.Append(\"|*a|Au\");\n\t\tFile.WriteAllText(Program.c_outputDirBS + \"dotnet_ref_task.txt\", b.ToString());\n\n\t\t//Au.Editor\n\t\tb.Append(\"|Au.Controls\");\n\t\tprint.it(b);\n\t\tFile.WriteAllText(Program.c_outputDirBS + \"dotnet_ref_editor.txt\", b.ToString());\n\n\t\tvoid _Dir(string jsonFile, string prefix) {\n\t\t\t//rejected: parse with JsonNode.\n\t\t\tvar a = new List<string>();\n\t\t\tvar s = File.ReadAllText(jsonFile);\n\t\t\t//print.it(s);\n\t\t\tint from = s.Find(\"\\\"runtime\\\": {\"), to = s.Find(\"\\\"native\\\": {\", from);\n\t\t\tif (!s.RxFindAll(@\"(?m)\\h*\"\"([^\"\"]+)\\.dll\"\": {\", 1, out string[] k, range: from..to)) throw new AuException();\n\t\t\tforeach (var v in k) {\n\t\t\t\tif (!hd.Add(v)) {\n\t\t\t\t\t//print.it(\"duplicate\", v);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\ta.Add(v);\n\t\t\t}\n\t\t\tb.Append(prefix);\n\t\t\tb.AppendJoin('|', a.OrderBy(o => o));\n\t\t}\n\t\t//note: cannot use AssemblyDependencyResolver._assemblyPaths or corehost_resolve_component_dependencies.\n\t\t//\tError \"Hostpolicy must be initialized and corehost_main must have been called before\". Our apphost does not use corehost_main etc.\n\t}\n}\n"
  },
  {
    "path": "Other/DocFX/DocFX.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <OutputType>WinExe</OutputType>\n    <TargetFramework>net10.0-windows</TargetFramework>\n    <UseWindowsForms>true</UseWindowsForms>\n    <AssemblyName>Au.DocFX</AssemblyName>\n    <RootNamespace>Au.DocFX</RootNamespace>\n    <SignAssembly>true</SignAssembly>\n    <AssemblyOriginatorKeyFile>..\\..\\Au.snk</AssemblyOriginatorKeyFile>\n    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n    <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>\n    <OutDir>bin</OutDir>\n\t  <LangVersion>preview</LangVersion>\n\t  <Platforms>AnyCPU</Platforms>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <Content Remove=\"_doc\\**\" />\n    <Compile Remove=\"_doc\\**\" />\n    <EmbeddedResource Remove=\"_doc\\**\" />\n    <None Include=\"_doc\\**\" />\n    <None Remove=\"_doc\\_site\\**\" />\n    <None Remove=\"_doc\\_exported_templates\\**\" />\n    <None Remove=\"_doc\\cookbook\\**\" />\n    <None Remove=\"_doc\\api\\*.yml\" />\n    <None Remove=\"_doc\\api\\.manifest\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "Other/DocFX/_doc/.gitignore",
    "content": "###############\n#    folder   #\n###############\n/**/DROP/\n/**/TEMP/\n/**/packages/\n/**/bin/\n/**/obj/\n_site/\n_exported_templates/\n*.tar\n*.gz\n/test/\n"
  },
  {
    "path": "Other/DocFX/_doc/api/.gitignore",
    "content": "###############\n#  temp file  #\n###############\n*.yml\n.manifest\n"
  },
  {
    "path": "Other/DocFX/_doc/api/index.md",
    "content": "# Library\n\n[Library files and namespaces](xref:library)\n"
  },
  {
    "path": "Other/DocFX/_doc/articles/Caller info parameter.md",
    "content": "---\nuid: caller_info\n---\n\n# Caller info parameter\n\nSome functions have optional parameters *f_* and/or *l_* with attribute `[CallerFilePath]` or `[CallerLineNumber]`. They automatically pass source file/line info to the function. Usually the caller should not use them.\n\nFor example, [popupMenu]() and [toolbar]() use it for the **Edit** command (context menu). Triggers use it for menu **Run > Recent > trigger**.\n\nIf to add menu/toolbar items or triggers you create an intermediate function, the function should have *f_* and/or *l_* parameters and pass them to the menu/toolbar/trigger function. Example script:\n\n```csharp\nvar m = new popupMenu(\"431beeab-e190-495a-be8f-05732ef6a74f\");\nm[\"Right click me to edit\"] = o => print.it(1);\n_AddMenuItem(\"You can edit me too\", o => print.it(2));\n_AddMenuItemWrongWay(\"But can be difficult to edit me\", o => print.it(3));\nm.Show();\n\nvoid _AddMenuItem(string label, Action<PMItem> action, [CallerLineNumber] int l_ = 0) {\n\tm[label, l_: l_] = action;\n\tm.Last.TextColor = 0x0000ff;\n}\n\nvoid _AddMenuItemWrongWay(string label, Action<PMItem> action) {\n\tm[label] = action;\n\tm.Last.TextColor = 0xff0000;\n}\n```"
  },
  {
    "path": "Other/DocFX/_doc/articles/Key names and operators.md",
    "content": "---\nuid: key_names\n---\n\n# Key names and operators\n\nThis string syntax is used with [keys.send]() and other keyboard functions of this library. A string can contain one or more US keyboard key names separated with a space or operator like `+`.\n\nExample:\n\n```csharp\nkeys.send(\"A F2 Ctrl+Shift+A Enter*2\"); //keys A, F2, Ctrl+Shift+A, Enter Enter\n```\n\n## Key names\n\n### Named keys\n- **Modifier:** `Alt`, `Ctrl`, `Shift`, `Win`, `RAlt`, `RCtrl`, `RShift`, `RWin`\n- **Navigate:** `Esc`, `End`, `Home`, `PgDn`, `PgUp`, `Down`, `Left`, `Right`, `Up`\n- **Other:** `Back`, `Delete`, `Enter`, `Apps`, `Pause`, `PrtSc`, `Space`, `Tab`\n- **Function:** `F1`-`F24`\n- **Lock:** `CapsLock`, `NumLock`, `ScrollLock`, `Insert`\n\nA key name must start with an uppercase character. Other characters - any case. Only the first 3 characters are significant. For example, for `\"Back\"` you can also use `\"Bac\"`, `\"Backspace\"` or `\"BACK\"`.\n\nAlias: `AltGr`=`RAlt`, `Menu`=`Apps`, `PageDown`=`PgDn`, `PD`=`PgDn`, `PageUp`=`PgUp`, `PU`=`PgUp`, `PrintScreen`=`PrtSc`, `PS`=`PrtSc`, `BS`=`Back`, `PB`=`Pause`, `CL`=`CapsLock`, `NL`=`NumLock`, `SL`=`ScrollLock`, `HM`=`Home`.\n\n### Text keys\n- **Alphabetic:** `A`-`Z` or `a`-`z` (case-insensitive)\n- **Number:** `0`-`9`\n- **Numeric keypad:** `#/`, `#*`, `#-`, `#+`, `#.`, `#0`-`#9`\n- **Other:** `` ` ``, `-`, `=`, `[`, `]`, `\\`, `;`, `'`, `,`, `.`, `/`\n\nNote: these are key names on US keyboard. They may not match key names on your keyboard. To specify characters instead, use operators (see below). Also in LibreAutomate you can use the input recorder.\n\nOnly uppercase A-Z must be separated with spaces. Example of a valid sequence: `\"A B ab\"`.\n\nAlias: `~`=`` ` ``, `{`=`[`, `}`=`]`, `|`=`\\`, `:`=`;`, `\"`=`'`, `<`=`,`, `>`=`.`, `?`=`/`.\n\n### Other keys\n- Names of enum [KKey]() members. Example: `keys.send(\"BrowserBack MediaNextTrack VolumeUp IMEKanaMode\");`\n- Virtual-key codes. Prefix `VK` or `Vk`. Example: `keys.send(\"VK65 VK0x42\");`\n- Unavailable: `Fn` (hardware-only key).\n\n### Special characters\n- **Operator:** `+`, `*`, `(`, `)`, `_`, `^`\n- **Numpad key prefix:** `#`\n- **Text/HTML argument prefix:** `!`, `%`\n- **Reserved:** `@`, `$`, `&`\n\nThese characters cannot be used as key names. Instead use key names listed in the \"Text keys\" section.\n\n## Operators\n\n| Operator | Examples | Description |\n| --- | --- | --- |\n| `+` | `\"Ctrl+Shift+A\"`<br>`\"Alt+E+P\"` | Hotkey. Like `\"Ctrl*down Shift*down A Shift*up Ctrl*up\"` and `\"Alt*down E*down P E*up Alt*up\"`. |\n| `+()` | `\"Alt+(E P)\"` | Hotkey. Like `\"Alt*down E P Alt*up\"`.<br>Inside `()` cannot be used operators `+`, `+()` and `^`. |\n| `*down` | `\"Ctrl*down\"` | Press key and don't release. |\n| `*up` | `\"Ctrl*up\"` | Release key. |\n| `*n` | `\"Left*3\"`<br>`$\"Left*{i}\"` | Press key n times, like `\"Left Left Left\"`.<br>See [keys.AddRepeat](). |\n| `_` | `\"Tab _A_b Tab\"`<br>`\"Alt+_e_a\"`<br>`\"_**20\"` | Send next character like text with option [OKeyText.KeysOrChar]().<br>Can be used to `Alt`-select items in menus, ribbons and dialogs regardless of current keyboard layout. |\n| `^` | `\"Alt+^ea\"` | Send all remaining characters and whitespace like text with option [OKeyText.KeysOrChar]().<br>For example `\"Alt+^ed b\"` is the same as `\"Alt+_e_d Space _b\"`.<br>`Alt` is released after the first character. Don't use other modifiers. |\n\nOperators and related keys can be in separate arguments. Examples: `keys.send(\"Shift+\", KKey.A); keys.send(KKey.A, \"*3\");`.\n\n### Raw text\n\nThe primary way to send raw text using `keys.send` - use a separate string argument with prefix `!`. Example: `keys.send(\"!User\", \"Tab\", \"!\" + clipboard.text, \"Tab Enter\");`.\n\nUnlike operators `^` and `_`:\n- Uses option `opt.key.TextHow` (keys/characters/paste). The default is characters.\n- Supports 2-char Unicode characters.\n\nTo paste HTML, use prefix `%` instead.\n"
  },
  {
    "path": "Other/DocFX/_doc/articles/Library.md",
    "content": "---\nuid: library\n---\n\n# Library\n\n## Namespaces\n- `Au` - main classes of this library, except triggers.\n- `Au.Types` - types of function parameters, exceptions, etc.\n- `Au.Triggers` - triggers: hotkeys, autotext, mouse, window.\n- `Au.More` - classes that are rarely used in automation scripts.\n\n## Files\n### .NET assembly files\n- `Au.dll` - contains code of the above namespaces.\n\n### Native code files\n- `AuCpp.dll`, `Au.DllHost.exe` - used by `Au.dll`.\n\nThese files are in LibreAutomate subfolders `64` and `32`. The exe compiler copies them to the exe folder. When using the library via NuGet, they are in subfolder `runtimes`.\n\nOther dll files in the LibreAutomate folder are not part of the library. They are undocumented.\n\n## Using the library without LibreAutomate\nTo get the dlls use NuGet package [LibreAutomate](https://www.nuget.org/packages/LibreAutomate). Or copy from the LibreAutomate folder. Or build from source code.\n\nYour project settings:\n- Target OS = Windows.\n- Supported OS version >= 7.0.\n- Use a manifest like [this](https://github.com/qgindi/LibreAutomate/blob/master/_/default.exe.manifest). It enables common controls 6, all OS versions, full DPI awareness, `disableWindowFiltering`.\n\nIf not using NuGet, add the native code files to the project. Add them as links, and in dll **Properties** set **Content** and **Copy if newer**.\n\nIf some library functions throw `DllNotFoundException` (missing `AuCpp.dll` etc), add environment variable `Au.Path` with value = `Au.dll` folder path. May need this when the host program copies `Au.dll` somewhere without the native dll folders, for example in some scripting environments and GUI designers.\n\nSee also: [unloading AuCpp.dll from other processes](https://www.libreautomate.com/forum/showthread.php?tid=7557)\n"
  },
  {
    "path": "Other/DocFX/_doc/articles/Output tags.md",
    "content": "---\nuid: output_tags\n---\n\n# Output tags\n\nFunction [print.it]() supports links, colors, images, etc. For it use tags in text. Similar to HTML tags. Also text must start with `<>`. It works only when text is displayed in the editor's output panel, not when in console.\n\nFor most tags use this format: `<tag>text<>` or `<tag attribute>text<>`.\n\nFor these tags use `<tag>text</tag>`: `<code>`, `<_>`, `<\\a>`, `<fold>`.\n\nThese tags don't have a closing tag: `<image \"attribute\">`, `<nonl>`.\n\nAttribute can be enclosed in `'` or `\"`. If attribute omitted, text is used as attribute if need for that tag.\n\nTags can be nested, like `<b><c green>text<><>` or `<b>text <c green>text<> text<>`.\n\nExamples:\n```csharp\nprint.it(\"<>Text <i>italic<>, <c green>color<>, <link http://www.example.com>Link<>.\");\nprint.it(\"<>Code example:\\r\\n<code>mouse.click(10, 20); //comments</code>\");\n```\n\n### Simple formatting tags\n| Examples | Comments\n| - | -\n| `<b>text<>` | Bold text.\n| `<i>text<>` | Italic text.\n| `<bi>text<>` | Bold italic.\n| `<u>text<>` | Underline.\n| `<c 0xE0A000>text<>`<br>`<c #E0A000>text<>`<br>`<c green>text<>` | Text color.<br>Can be 0xRRGGBB, #RRGGBB or .NET color name.\n| `<bc yellow>text<>` | Text background color.\n| `<lc wheat>line text<>` | Line background color.\n| `<size 10>text<>` |  Font height.<br>Note: it can increase height of all lines.\n| `<mono>text<>` | Monospace font.\n\n### Links\n| Examples | Comments\n| - | -\n| `<link http://www.example.com>text<>`<br>`<link C:\\files\\example.exe>text<>`<br>`<link>http://www.example.com<>`<br>`<link>C:\\files\\example.exe<>`<br>`<link C:\\example.exe|args>text<>` | Opens a web page or runs a program, file, folder.<br>Calls function [run.itSafe]().\n| `<explore>C:\\files\\example<>` | Selects a file or folder in File Explorer.<br>Calls function [run.selectInExplorer]().\n| `<google s1>text<>`<br>`<google>s1<>`<br>`<google s1|s2>text<>` | Google. Opens this URL:<br>`$\"http://www.google.com/search?q={s1}{s2}\"`<br>Don't need to URL-encode.\n| `<help>Class.Function<>`<br>`<help Au.Namespace.Class>text<>`<br>`<help articles/Output tags>text<>` | Opens a help page of this library.\n| `<open>Script5.cs<>`<br>`<open \\Folder\\Script5.cs>text<>`<br>`<open Script5.cs|10>text<>`<br>`<open Script5.cs|10|15>text<>`<br>`<open Script5.cs||100>text<>`<br>`<open Script5.cs|||word>text<>`<br>`<open Folder|expand>text<>` | Opens a script or other file of current workspace in the code editor. Optionally moves the text cursor.<br>Can be file name, relative path in workspace, or full path.<br>10 is 1-based line index.<br>15 is 1-based character index in line.<br>100 is 0-based character index in text.<br>The word is text to find, whole word(s).<br>Selects and expands a folder.\n| `<script>Script5.cs<>`<br>`<script \\Folder\\Script5.cs>text<>`<br>`<script Script5.cs|args0|args1>text<>` | Runs a script.\n\n### Other tags\n| Examples | Comments\n| - | -\n| `<_>text</_>` or `<\\a>text</\\a>` | Literal text. Tags in it are ignored.<br>Here `\\a` is escape sequence for character code 7.\n| `<code>var s=\"example\";</code>` | Colored C# code. Tags in it are ignored.\n| `<fold>text</fold>` | Folded (hidden) lines. Adds a link to unfold (show).\n| `<fold link text>text</fold>` | Folded lines with custom link text.\n| `<nonl>` | No new line. Next time will write in the same line.<br>Must be at the end of string.\n\n### Images\nImages are displayed below current line. Examples:\n\n`<image \"c:\\images\\example.png\">`\\\n`<image \"c:\\files\\example.txt\">`\\\n`<image \"image:PngBase64\">`\n\nNote: image path must be enclosed in `\"\"`.\n\nSupports images of formats: png, bmp, jpg, gif, ico (only 16x16). For other file types and folders displays small file icon.\n\nSupports icon names (see menu **Tools > Icons**) and XAML images.\n\nSupports Base64 encoded image file data. To create such string use dialog **Find image or color in window** or function `Au.Controls.KImageUtil.ImageToString` (in `Au.Controls.dll`).\n\n"
  },
  {
    "path": "Other/DocFX/_doc/articles/UAC.md",
    "content": "---\nuid: uac\ntitle: UAC\n---\n\n# User Account Control (UAC)\n\nUAC is a Windows security feature. It does not allow most programs to change important files and Windows settings. Only processes that run as administrator can do it. Also non-admin processes cannot interact with windows of admin processes.\n\nThe script editor should run as administrator (recommended but not necessary). It can always start as administrator without UAC consent. If started not as administrator, it prints some information and a link to enable this feature. If later you want to disable this feature, delete or disable Windows Task Scheduler task `\\Au\\Au.Editor`. To disable temporarily, start the program with command line `/n`.\n\nScripts run in separate processes. They inherit the editor's UAC integrity level (admin/nonadmin), unless changed in **Properties > uac** or the exe script is launched not by the editor.\n\nClasses and functions that can be used to get UAC-related info: [uacInfo](), [wnd.Uac](), [wnd.UacAccessDenied]().\n\n```csharp\nprint.it(uacInfo.isAdmin, uacInfo.ofThisProcess.IntegrityLevel);\n```\n\nWhen an admin process creates a new process, normally the new process is admin too. However [run.it]() by default creates a non-admin process.\n\nNon-admin processes have these limitations (and more).\n- Can't write to some folders, including `Windows` and `Program Files`.\n- Can't write to some registry parts, including `HKEY_LOCAL_SYSTEM` and `HKEY_CLASSES_ROOT`.\n- Can't manipulate services.\n- Can't use some Windows API.\n- Can't interact with admin windows: send keys and mouse clicks, use UI elements, manipulate windows, send Windows messages, etc.\n- While an admin window is active, non-admin processes can't use most triggers, hooks and hotkeys, get key/mouse states.\n\nAdmin processes have some UAC-related problems too.\n- Admin processes can't easily start non-admin processes. It is possible with workarounds only.\n- You can't drag and drop files etc from a non-admin to addmin process.\n- Admin processes can't get active COM objects from non-admin processes.\n- Admin processes don't see drive letter mappings done by non-admin processes. In scripts use network path, like `\"\\\\server\\share\\file\"` instead of `\"X:\"`.\n"
  },
  {
    "path": "Other/DocFX/_doc/articles/UI element issues.md",
    "content": "---\nuid: ui_element_issues\n---\n\n# UI element issues\n\nUI elements are implemented and live in their applications. Classes [elm]() and [elmFinder]() just communicate with them.\n\nMany applications have various problems with their UI elements: bugs, incorrect/nonstandard/partial implementation, or initially disabled. This library implements workarounds for known problems, where possible.\n\n### Known issues in various applications\n\n**Application:** Chrome web browser. Also Edge, Brave, Opera and other apps that use Chromium. Window class name `\"Chrome_WidgetWin_1\"`.  \n1. Web page UI elements initially are disabled (missing).\n   Workarounds:\n   - Functions [elmFinder.Find](), [elmFinder.Exists](), [elmFinder.Wait]() and [elmFinder.FindAll]() enable it if used role prefix `\"web:\"` or `\"chrome:\"`. Functions [elm.fromXY](), [elm.fromMouse]() and [elm.focused]() enable it if window class name starts with `\"Chrome\"`. However Chrome does it lazily, therefore shortly after enabling something still may not work. Note: this auto-enabling may fail with future Chrome versions.\n   - Start Chrome with command line `--force-renderer-accessibility`.\n2. Sometimes [elmFinder.Find]() etc may not find the element if parameter *wait* not used, especially after auto-enabling UI elements. Use *wait*, like `var e1 = w1.Elm[\"web:LINK\", \"Example\"].Find(5);`.\n3. Some new web browser versions add new features or bugs that break something.\n\n**Application:** Firefox web browser.  \n1. When Firefox starts, its web page UI elements are unavailable. Creates them only when something tries to find or get an element, but does it lazily, and the find/get function at first fails. Workaround: with [elmFinder.Find]() use parameter *wait*, like `var e1 = w1.Elm[\"web:LINK\", \"Example\"].Find(5);`.\n2. Occasionally Firefox briefly turns off its web page UI elements. Workaround: use parameter *wait*. With other web browsers also it's better to use *wait*.\n3. Some new web browser versions add new features or bugs that break something.\n\n**Application:** Applications (or just some windows) that don't have accessible objects but have UI Automation elements.\n1. To find UI elements in these applications, need flag [EFFlags.UIA]().\n\n**Application:** Java applications that use AWT/Swing. Window class name starts with `\"SunAwt\"`.\n1. Must be enabled Java Access Bridge (JAB).  \nIf JAB is missing/disabled/broken, the **Find UI element** tool shows an \"enable\" link when you try to capture something in a Java window. Or you can enable JAB in **Options > OS**. Or use `jabswitch.exe`. Then restart Java apps. Also may need to restart apps that tried to use Java UI elements.\n2. JAB is part of Java. Install Java 64-bit x64 or ARM64 (as your OS).\n3. If you'll use JAB in 32-bit script processes (unlikely), also install Java 32-bit. If you'll use JAB in x64 script processes on Windows ARM64 (unlikely), also install Java x64.\n4. Not supported on 32-bit OS.\n5. JAB bug: briefly shows a console window at PC startup, after every sleep, etc. Or opens an invalid Windows Terminal window. Workaround: in Terminal settings set Default Terminal Application = Windows Console Host.\n\n**Application:** Some controls.\n1. UI elements of some controls are not connected to the UI element of the parent control. Then cannot find them if searching in whole window.  \nWorkaround: search only in that control. For example, use *prop* `\"class\"` or `\"id\"`. Or find the control ([wnd.Child]() etc) and search in it.\n\n**Application:** Some controls with flag [EFFlags.NotInProc]().\n\nUI elements of many standard Windows controls have bugs when they are retrieved without loading dll into the target process (see [EFFlags.NotInProc]()). Known bugs:\n1. Toolbar buttons don't have `Name` in some cases.\n2. [elm.Focus]() and [elm.Select]() often don't work properly.\n\nWorkarounds: Don't use [EFFlags.NotInProc](). Or use [EFFlags.UIA]().\n\n**Application:** Where cannot load dll into the target process. For example Windows Store apps.\n1. Function [elmFinder.Find]() is much slower, and uses much more CPU when waiting. More info: [EFFlags.NotInProc]().\n\n**Application:** Processes of a different CPU architecture (32/64/ARM64) than this process.\n1. To load the dll is used `Au.DllHost.exe`, which makes slower first time.\n\n**Application:** DPI-scaled windows (see [Dpi.IsWindowVirtualized]()).\n1. In some cases \"element from point\" and \"get rectangle\" functions may not work correctly with such windows. This process must be per-monitor-DPI-aware (LA script processes are).\n"
  },
  {
    "path": "Other/DocFX/_doc/articles/Wait timeout.md",
    "content": "---\nuid: wait_timeout\n---\n\n# Wait timeout\n\nMost \"wait for\" functions have a *timeout* parameter. It is the maximal time to wait, in seconds. If 0, waits indefinitely. If > 0, after that time interval throws `TimeoutException`. If < 0, after that time interval returns the default value of the return type (`false`, `null`, `0`, `default`).\n\nSome \"find\" functions have a *wait* parameter. It is like *timeout*, but 0 means \"don't wait\". To wait indefinitely, use some large value, for example `8e88`. Also, \"find\" functions throw `NotFoundException`, not `TimeoutException`.\n\nThe type of these parameters is [Seconds](). It allows to specify wait options.\n\nExamples:\n```csharp\n//wait for Notepad window\nvar w = wnd.wait(0, true, \"* Notepad\");\nprint.it(w);\n\n//wait for Notepad window max 5 seconds. Then throw exception.\nvar w = wnd.wait(5, true, \"* Notepad\");\nprint.it(w);\n\n//wait for Notepad window max 5 seconds. Then exit.\nvar w = wnd.wait(-5, true, \"* Notepad\");\nif(w.Is0) { print.it(\"timeout\"); return; }\nprint.it(w);\n\n//wait for hotkey max 5 seconds. Then exit.\nif(!keys.waitForHotkey(-5, \"Ctrl+Shift+K\")) return;\nprint.it(\"hotkey\");\n\n//specify wait options\nwait.until(new Seconds(0) { Period = 100, MaxPeriod = 100 }, () => keys.isCtrl);\n```\n"
  },
  {
    "path": "Other/DocFX/_doc/articles/Wildcard expression.md",
    "content": "---\nuid: wildcard_expression\n---\n\n# Wildcard expression\n\n*Wildcard expression* is a simple text format that supports wildcard characters, regular expression, \"match case\", \"text1 or text2\" and \"not text\". Used with \"find\" functions, for example [wnd.find]().\n\nWildcard characters:\n\n| Character | Will match | Examples |\n| :- | :- | :- |\n| `*` | Zero or more of any characters. | `\"start*\"`, `\"*end\"`, `\"*middle*\"` |\n| `?` | Any single character. | `\"date ????-??-??\"` |\n\nThere are no escape sequences for `*` and `?` characters, unless you use regular expression.\n\nBy default case-insensitive. Always culture-insensitive.\n\nCan start with `**options `:\n\n| Option | Description | Examples |\n| :- | :- | :- |\n| `t` | Literal text (`*` and `?` are not wildcard characters). | `\"**t text\"` |\n| `r` | Text is PCRE regular expression ([regexp]()).<br>Syntax: [full](https://www.pcre.org/current/doc/html/pcre2pattern.html), [short](https://www.pcre.org/current/doc/html/pcre2syntax.html). | `\"**r regex\"` |\n| `R` | Text is .NET regular expression (`Regex`).<br>Cannot be used with [elm]() and [elmFinder](). | `\"**R regex\"` |\n| `c` | Must match case. | `\"**tc text\"`, `\"**rc regex\"` |\n| `m` | Multi-part (match any part). Separator `||`. | `\"**m findAAA||orBBB||**r orCCC\"` |\n| `m(sep)` | Multi-part. Separator `sep`. | `\"**m(^^^) findAAA^^^orBBB\"` |\n| `n` | Must not match. | `\"**mn notAAA||andNotBBB\"` |\n\n Only one of `t`, `r`, `R`, `m` can be specified. Option `c` specified with `m` is applied to all parts. Option `n` is applied finally.\n\n If the function argument is `null` or omitted, it usually means \"match any\". Wildcard expression `\"\"` matches only `\"\"`. Exception `ArgumentException` if invalid `**options ` or regular expression.\n\nExamples:\n```csharp\n//Find window. Its name ends with \"- Notepad\" and program is \"notepad.exe\".\nvar w = wnd.find(\"*- Notepad\", program: \"notepad.exe\");\n\n//Find item in x. Its property 1 is \"example\" (case-insensitive), property 2 starts with \"2017-\" and property 3 matches a case-sensitive regular expression.\nvar item = x.FindItem(\"example\", \"2017-*\", \"**rc regex\");\n```\n\n### See also\n\n[wildex]()<br>[ExtString.Like]()\n"
  },
  {
    "path": "Other/DocFX/_doc/articles/index.md",
    "content": ""
  },
  {
    "path": "Other/DocFX/_doc/articles/toc.yml",
    "content": "- name: Library\n  href: Library.md\n\n- name: Key names and operators\n  href: Key names and operators.md\n\n- name: Output tags\n  href: Output tags.md\n\n- name: UAC\n  href: UAC.md\n\n- name: Wildcard expression\n  href: Wildcard expression.md\n"
  },
  {
    "path": "Other/DocFX/_doc/changes/future.md",
    "content": "# Version 1..0 (2025-)\n\n## Editor\n\nUsing .NET 10.\n\nUpdated Roslyn dlls (C# compiler).\n\nNew tools:\n- .\n\nNew cookbook recipes:\n- .\n\nImproved in **NuGet** tool:\n- Restores old folder contents if failed to install/update.\n- Can update dlls that are currently loaded in script processes.\n\nImproved:\n- .\n\nFixed bugs:\n- Was defined `NET8_0`.\n\n## Library\n\nNew classes:\n- .\n\nNew members:\n- .\n\nNew parameters:\n- .\n\nImproved:\n- .\n\nFixed bugs:\n- .\n\n### Breaking changes\n"
  },
  {
    "path": "Other/DocFX/_doc/changes/old/v0.1.md",
    "content": "## Version 0.1.0\n\n### Breaking changes\n\n**elm** and **elmFinder**:\n- Removed functions **elm.find**, **elm.wait**, **elm.printAll**, **elm.Find**, **elm.Wait**. Now use **elmFinder.Find** etc with the new syntax.\n- Now *role* parameter cannot contain path. Instead use the new path syntax.\n- The *prop* parameter type now is **Strings**, and separator is `|` (was `\\0`). If substrings contain `|`, use `new(\"substring\", \"substring\")`.\n- In *prop* use `desc` instead of `description`.\n- Removed property **elmFinder.NavigFailed**.\n- Renamed **elm.Navigate** parameter *secondsToWait* to *waitS*.\n- **elm.VirtualClick** renamed to **elm.PostClick** and added parameters *x y*.\n- **elm.SimpleElementId** renamed to **elm.Item**. In *prop* use `item` instead of `elem`.\n\nNow functions **Find** and **Wait** of all finders (**wndFinder**, **wndChildFinder**, **elmFiner**, **uiimageFinder**) return the found object, not bool. Functions **Exists** return bool.\n\nRemoved **wndFinder** conversion from string.\n\nFrom **wnd**, **elm** and **uiimage** removed operator + that was used to throw **NotFoundException**. Instead use \"find\" function overloads with parameter *waitS*.\n\n**DStringList** renamed to **Strings**.\n\nIn **dialog** functions, the *buttons* parameter type now is **Strings**.\n\nString extension methods **FindAny**, **FindNot**, **FindLastAny** and **FindLastNot** now have parameter *range* instead of *startOfRange* and *endOfRange*.\n\n**IFArea** tuple casts replaced with constructors. Eg instead of `uiimage.find((w, (1, 2, 3, 4)), \"image\")` use `uiimage.find(new(w, (1, 2, 3, 4)), \"image\")`.\n\nIn **uiimage** and **uiimageFinder** functions renamed parameter *colorDiff* to *diff*. Also its range now is 0 - 100.\n\nRenamed **wnd.WaitForCondition** to **wnd.WaitFor**.\n\nIn **wnd.Child** added parameter *id* instead of `***id` in *name*. Same in **wndChildFinder** constructor etc.\n\nRemoved **wnd.ChildById**. Instead use code like `w.Child(id: 15)`.\n\n**elm.RoleInt** for custom roles returns **ERole.Custom**, not 0.\n\nRemoved **WButton** and **wnd.AsButton**.\n\nChanged **wnd.ButtonClick** parameters and how it works.\n\nRemoved **wnd.OwnerWindow**. Use `w.Get.Owner` and **WndUtil.SetOwnerWindow**.\n\nFrom **wnd.Activate** removed parameter waitMS.\n\nRenamed classes: **timerm** -> **timer**; **timert** -> **timer2**.\n\nRemoved **EXYFlags.NoThrow**. Now **elm.fromXY** and **elm.fromMouse** don't throw.\n\nClass **Dpi**: removed property **SupportPMOnWin8_1**. Instead added parameter *supportWin81* in functions. Removed **OfWindow** parameter *ofScreen*.\n\n\n### Other changes\nMany changes in **elm** and **elmFinder** classes and in the \"Find UI element\" dialog. The **Find** function can use intermediate elements (path) and can wait and throw exception if not found. Better supports high DPI screens.\n\nReplaced syntax of finding UI elements (**elm**). Now instead of code `var e = elm.find(w, ...)` use code `var e = w.Elm[...].Find()` or `var e = w.Elm[...][...][...].Find()`.\n\nUses .NET 6.\n"
  },
  {
    "path": "Other/DocFX/_doc/changes/old/v0.10.md",
    "content": "## Version 0.10.0 (2022-10-19)\n\n### Editor\nNew tools:\n- Snippets. Add and edit code snippets.\n- Customize. Customize menus, toolbars and hotkeys of the editor window.\n\n\n### Library\nNew features:\n- Several new functions or parameters in **wpfBuilder** class: **Name**, **Watermark**, **Text**, **Border**.\n\n\n### Bug fixes and improvements\nFixed: **ExplorerFolder.Of** may throw exception.\n"
  },
  {
    "path": "Other/DocFX/_doc/changes/old/v0.11.md",
    "content": "## Version 0.11.0 (2022-11-08)\n\n### Editor\n**Properties > Add file > Other file**. Use to copy files to .exe output or find unmanaged dlls at run time.\n\nMenu **File > Workspace > Repair workspace**. Finds missing and orphaned files.\n\nNew Cookbook recipes:\n- Editor extension - modify UI.\n\n\n### Library\nNew features:\n- Class **ExtWpf**: several new functions and parameters.\n- **ScriptEditor.Open**: new parameters *line* and *offset*.\n- New function **print.scrollToTop**.\n\n\n### Bug fixes and improvements\n\nEditor:\n- Fixed: in Properties dialog cannot specify preBuild/postBuild arguments.\n- Fixed: in editorExtension script does not work unmanaged dll resolving at run time.\n- Fixed several other bugs.\n- Improved: now in `/*/ r A.dll; /*/` don't need to specify dlls used by A.dll but not used in code.\n\nLibrary:\n- Fixed: **print** class functions may not work in non-console program started from a console program (cmd, .bat, etc).\n"
  },
  {
    "path": "Other/DocFX/_doc/changes/old/v0.12.md",
    "content": "## Version 0.12.0 (2022-12-04)\n\n### Editor\nCan run as a portable app.\n\nAllows to test-execute class files without a test script etc.\n\n\n### Library\nNew functions:\n- **ScriptEditor.IsPortable**.\n- **script.isRunning**.\n- **AutotextTriggers.MenuOptions**.\n- **filesystem.more.comparePaths**.\n- **filesystem.more.isSameFile**.\n- **filesystem.more.createSymbolicLink**.\n- **folders.Editor**.\n- **folders.noAutoCreate**.\n- **folders.unexpandPath**: added 1 overload.\n- **clipboard.copyData**: added 1 overload.\n\nNew classes:\n- **TempFile**.\n- **TAMenuOptions** (for autotext triggers).\n\nNew parameters:\n- **clipboard** functions: *timeoutMS*.\n- **filesystem.more.getFinalPath**: *format*.\n\nAdded flag **PMFlags.WindowCenter** to show popup menu in the center of the active window.\n\n**folders.getFolder** and functions that support paths like `@\"%folders.Documents%\\file.txt\"` now also support properties that return string.\n\n### Bug fixes and improvements\n\nEditor:\n- Several small improvements.\n- Fixed several bugs.\n\nLibrary:\n- Improved: **miscInfo.getTextCursorRect** supports more windows. It is used to get menu position, for example with autotext triggers.\n- Fixed regression: **pathname.unprefixLongPath** and functions that use it don't support paths like \"\\\\?\\UNC\\...\".\n- Improved: **pathname.getRootLength** supports more path types.\n\n\n### Breaking changes\n\nDefault **folders.ThisAppX** subfolders used in scripts changed from `\"Au\"` to `@\"LibreAutomate\\_script\"`. If the old folder exists and the new doesn't, editor when starting copies files and prints a note.\n"
  },
  {
    "path": "Other/DocFX/_doc/changes/old/v0.13.md",
    "content": "## Version 0.13.0 (2023-01-29)\n\n### Editor\n- Menu **Code > Find OCR text**.\n- Added class **EditorExtension**. Can be used in scripts that start with `/*/ role editorExtension; r Au.Editor.dll; /*/`. Helps to properly restart scripts etc.\n- `/*/ ifRunning end; /*/`.\n\n### Library\nNew classes:\n- OCR: **ocr**, **ocrFinder**, etc.\n- **CaptureScreen**, **CaptureScreenImage**.\n\nNew parameters:\n- **HttpClient.Post**, **HttpClient.TryPost**: *dontWait*.\n- **clipboardData** image functions now support PNG format and have new parameter *png*.\n\n\n### Bug fixes and improvements\n\nEditor:\n- Fixed: tree/list view item height too small when big system font.\n- Improvements and bug fixes in the \"Find image...\" tool.\n- Improvements and bug fixes in `/*/ resource ... /*/`.\n\nLibrary:\n- Fixed: in some cases does not find native dlls. Then some NuGet packages cannot be used.\n- Fixed: **uiimage** functions don't work if used both flag **PrintWindow** and rectangle.\n- Fixed: **popupMenu.Show** flag **PMFlags.AlignCenterH** ignored.\n\n\n### Breaking changes\n\n**uiimage** functions no longer use logical (non-scaled) rectangle/point coordinates when the window is DPI-scaled and capturing its non-scaled view. Now always use physical coordinates.\n"
  },
  {
    "path": "Other/DocFX/_doc/changes/old/v0.14.md",
    "content": "## Version 0.14.0 (2023-04-07)\n\n### Editor\nMenu **Edit > Find symbol**.\n\nMenu **Edit > Find references**.\n\nMenu **Edit > Find implementations**.\n\nMenu **Edit > Rename symbol**.\n\nMenu **Edit > Go to base**.\n\nHighlights references of the symbol at the text cursor.\n\nHighlights matching braces and directives of the brace/directive at the text cursor.\n\nMultiple pages in the **Found** panel.\n\nNow `/*/ pr /*/` can be used in any file of a project, not just in the main file.\n\nImproved:\n- Replacing text in multiple files. Multi-file undo/redo.\n- Implement interface.\n- Folding margin context menu.\n- And more.\n\nNew options:\n- **Options > Font, colors > highlight, selection**.\n- **Options > Code editor > Formatting**.\n- **Options > Hotkeys > Capture image**.\n\n### Library\nNew members:\n- Event **computer.suspendResumeEvent**.\n- Flag **IFFlags.Parallel** for **uiimage.find**.\n\nNew parameters:\n- **string.FindWord**: *isWordChar* (callback).\n- **wpfBuilder.Options**: *showToolTipOnKeyboardFocus*. And changed the default behavior of tooltips on keyboard focus.\n\n### Bug fixes\n\nEditor:\n- Fixed: cannot use NuGet packages that depend on .NET 7 assemblies.\n- Fixed: error message box on exit.\n- Fixed several memory leaks and other bugs.\n\nLibrary:\n- Fixed \"TextForFind\" bug in documentation.\n- Several improvements.\n\n\n### Breaking changes\nWhen converting **int**/**uint** to **ColorInt**, now by default sets alpha 255 only if it is 0 in the specified value. It allows to specify alpha in implicit conversion.\n"
  },
  {
    "path": "Other/DocFX/_doc/changes/old/v0.15.md",
    "content": "## Versions 0.15.0 (2023-05-17) to 0.15.1 (2023-05-20)\n\n### Editor\nNew tools:\n- Get files in folder.\n\nNew cookbook recipes:\n- End script task.\n- Run script on another computer; HTTP server.\n\nImproved:\n- Improvemets in \"drop files\", \"surround code\", completion list sorting and more.\n\n### Library\nNew classes:\n- **HttpServerSession**.\n- **print.util**.\n- **InputDesktopException**. All functions that send keyboard/mouse input or activate/focus a window now throw this exception if failed because of changed input desktop. It allows to use Ctrl+Alt+Delete or Win+L to end scripts that use these functions even without `script.setup(lockExit: true)`, unless the script handles these exceptions.\n\nNew members:\n- **script.pause**.\n- **script.paused**.\n- **print.list**.\n- **print.noHex**.\n- **print.it** overload for interpolated strings.\n- **ExplorerFolder.All**.\n- Extension methods **string.ToUTF8**, **byte[].ToStringUTF8**.\n- **Convert2.GzipCompress**.\n- **Convert2.GzipDecompress**.\n- (v0.15.1) **TriggerOptions.ThreadThis**.\n\nNew parameters:\n- **script.setup**: *exitKey*, *pauseKey*.\n- **RegisteredHotkey.Register**: *noRepeat*.\n- **HttpClient.Get** etc: *auth* (user name and password), *also*.\n- (v0.15.1) **wpfBuilder.Options**: *bindLabelVisibility*.\n\nImproved:\n- **script.setup** flag **UExcept.Dialog**: the dialog now has links to functions in the call stack.\n- **internet.http**: the **HttpClient** now accepts compressed content; decompresses automatically.\n\n### Bug fixes\n\nEditor:\n- Fixed several exceptions and other bugs.\n- Fixed in v0.15.1: some \"go to position in text\" commands set wrong position if there are non-ASCII characters.\n\nLibrary:\n- Fixed: **script.trayIcon** and some **script.setup** features don't work in exeProgram script started not from editor.\n- Fixed: **popupMenu** check/radio menu item click delegate not invoked if **CheckDontClose** true.\n- Fixed: **Convert2.BrotliCompress** fails if already compressed.\n- Fixed in v0.15.1: **elmFinder.FindAll** does not work with path.\n"
  },
  {
    "path": "Other/DocFX/_doc/changes/old/v0.16.md",
    "content": "## Version 0.16.0 (2023-06-08)\n\n### Editor\n\nNew features:\n- **Options > Check** for updates.\n- Menu **Edit > View > Customize this menu**.\n- Tool **Find UI element** can create \"find all\" code.\n\nNew cookbook recipes:\n- Extract table data using UI elements.\n\n### Library\nNew members:\n- **osdRect.SetRects**.\n- **Triggers.RunThread**.\n- **run.thread** overload.\n\nImproved:\n- **HSMessage.Text** and **HSContentPart.Text** now get text even if encoding is unknown. Previously returned null.\n\n### Bug fixes\n\nEditor:\n- Fixed some bugs.\n\nLibrary:\n- Fixed some bugs.\n"
  },
  {
    "path": "Other/DocFX/_doc/changes/old/v0.17.md",
    "content": "## Versions 0.17.0 (2023-07-31) to 0.17.2 (2023-08-14)\n\n### Editor\nImproved:\n- Source code search (the \"Go to definition\" command when the symbol is not in your code).\n- Does not use NTFS links for folders imported as links.\n- Does not use locked files (state.db) in workspace folder.\n- Improved some fonts.\n- And more.\n\n**Options > Font, colors: output font, italic**.\n\n### Library\nNew classes:\n- **ComUtil**.\n- **WBLink**. Can be used with **wpfBuilder.Text**.\n- (0.17.1) **consoleProcess**.\n\nNew members:\n- **elm.uiaCN**. Also **elm** and **elmFinder** functions that support `uiaid` now also support `uiacn`.\n- **HttpResponseMessage** extension methods: **Download**, **DownloadAsync**.\n- **wpfBuilder**: **Wrap**, and several new overloads of other functions.\n- **folders.sourceCodeMain**.\n- **script.sourcePath**.\n- **JSettings** properties: **NoAutoSave**, **NoAutoSaveTimer**.\n\nNew parameters:\n- **filesystem.more.isSameFile**: *useSymlink**.\n\nImproved:\n- **internet.http** adds default header `\"User-Agent: Au\"`.\n- **AutotextTriggerArgs.Menu**: null adds separator.\n- **wpfBuilder.Padding** and **.Brush** support more element types.\n- **filesystem.more.createSymbolicLink** with *deleteOld* true now does not delete the old link if fails to create new.\n- **filesystem.more.createSymbolicLink** can create symbolic links without admin rights if in Windows Settings enabled developer mode.\n- **Hash.MD5Context.Add**: no exception when data is empty.\n\n### Bug fixes\n\nEditor:\n- Fixed: May crash when disabling triggers.\n- Fixed: Cannot convert some COM type libraries.\n- Fixed: After opening another workspace stops working the Files panel's context menu.\n- Fixed: Exception when formatting code that starts with space.\n- Fixed in 0.17.1: Cannot load program settings if using an old .NET 6 version, eg 6.0.3.\n- Fixed in 0.17.1: If the program stars hidden, it crashes when the tray icon double-clicked.\n\nLibrary:\n- Fixed the _r bug in documentation.\n- Fixed: **print.it** afraids some COM types.\n- Fixed: when **ExtString.ToInt** fails, *numberEndIndex* isn't 0 if used flag **IsHexWithout0x**.\n- Fixed in 0.17.1: Some protected void Dispose(bool disposing) were not virtual.\n- Fixed in 0.17.2: Memory leak in regular expression functions.\n\n### Breaking changes\n\n(0.17.1) **run.console** default encoding now is UTF-8 or UTF-16. Was **Console.OutputEncoding**.\n"
  },
  {
    "path": "Other/DocFX/_doc/changes/old/v0.18.md",
    "content": "## Version 0.18.0 (2023-10-10)\n\n### Editor\nMenu **File > Git**. Use Git and GitHub to backup and sync workspace files (scripts etc).\n\nIn autocompletion list you can press the [+] button or Ctrl+Space to include all types or extension methods, not only those from 'using' namespaces.\n\nNew cookbook recipes:\n- Dialog - use triggers.\n\nSmall improvements.\n\nFixed bugs:\n- Incorrectly formats code if it contains multiline block comments.\n\n### Library\nNew classes and structs:\n- **Seconds** (wait timeout and options).\n- **AppSingleInstance**.\n- **FileTree**.\n\nNew members:\n- **script.restart**.\n- **filesystem.setAttributes**.\n- **wpfBuilder.FormatText** and **FormattedText**.\n- **wait.until** (replaces **forCondition**) and **doEventsUntil** (replaces **forMessagesAndCondition**).\n\nImproved:\n- In most wait functions the timeout parameter now is of type **Seconds** (was **double**). It allows to specify wait options and cancellation token.\n\nFixed bugs:\n- **dialog**: default button isn't the first in the list as documented.\n\n### Breaking changes\n**OWait** and **opt.wait** now are obsolete. The new way to set wait options is like `wnd.wait(new Seconds(3) { Period = 100, DoEvents = true }, ...)`. For backward compatibility, wait functions still use **opt.wait.DoEvents** if **Seconds.DoEvents** not specified.\n\nIn wait functions renamed parameter *secondsTimeout* to *timeout*, and *waitS* to *wait*. Because the type now is **Seconds**.\n"
  },
  {
    "path": "Other/DocFX/_doc/changes/old/v0.19.md",
    "content": "## Versions 0.19.0 (2023-11-15) to 0.19.3 (2023-11-22)\n\n### Editor\n.NET 8.\n\nC# 12.\n\nBookmarks.\n\nMenu **Run > Publish**. Can create single-file programs.\n\n**Options > General > Auto-backup (Git commit)**.\n\n**Properties > nullable**.\n\nMenu **Tools > Update icons**. Moved from the Icons tool. Now also updates custom toolbars.\n\nNew context menu items in the Recipe panel: Copy code, New script.\n\nSeveral bug fixes and improvements.\n\n#### In v0.19.1\n- Fixed some v0.19 bugs.\n- Menu **Code > Color**.\n- New version of the icons library.\n- Case-insensitive icon names.\n\n#### In v0.19.2\n- Fixed some v0.19 bugs.\n\n#### In v0.19.3\n- Fixed v0.19 bug: difficult to uncomment multiple lines containing empty lines.\n\n### Library\nNew classes:\n- **CheckListDialog**.\n\nSeveral improvements.\n\n### Breaking changes\n\nNow uses .NET 8. Previously .NET 6.\n"
  },
  {
    "path": "Other/DocFX/_doc/changes/old/v0.2.md",
    "content": "## Version 0.2.0 - 0.2.5\n\n### Breaking changes\nCharacter _ in keys strings cannot be used as a key name.\n\n\n### Bug fixes\nDoes not work **Options > hide when closing**.\n\nFixed in 0.2.3:\n- Mouse click and wheel triggers stop working.\n\nFixed in 0.2.4:\n- Keyboard triggers stop working temporarily.\n\nFixed in 0.2.5:\n- Bugs in mouse and keyboard triggers.\n\n\n### Other changes\nIn keys strings (**keys.send** etc) with key names now also can be used text characters, like \"Alt+_e_a or \"Alt+^ea\".\n\nSome improvements in **elm.Navigate** and **elm.ScrollTo**.\n"
  },
  {
    "path": "Other/DocFX/_doc/changes/old/v0.3.md",
    "content": "## Version 0.3.0 - 0.3.1\n\n### Breaking changes\nRenamed: **mouse.moveRelative** and **mouse.moveRecorded** to **mouse.moveBy**.\n\nChanged some functions of struct **WOwner**.\n\n\n### Bug fixes\nEditor does not see updated r class libraries.\n\nFixed in 0.3.1\n- A compilation cannot contain duplicate r.\n\n\n### Other changes\nAdded keyboard/mouse recorder.\n\nNew function **mouse.drag**.\n\nNew **elm** functions: **SendKeys**, **Check**, **Expand**.\n\nVarious improvements.\n"
  },
  {
    "path": "Other/DocFX/_doc/changes/old/v0.4.md",
    "content": "## Version 0.4.0\n\n### Breaking changes\nSee bug fixes.\n\nFrom enum **UExcept** removed **Exit** and **PrintDialogExit**.\n\nChanged the type of **dialog** combo box values parameters and of the property. Renamed the property.\n\nReplaced **filesystem.enumerate** parameter *filter* with 2 parameters *fileFilter* and *dirFilter*. The same in **filesystem.copy** and **filesystem.copyTo**.\n\nRemoved **FEFile.SkipThisDirectory**.\n\nRenamed some **FEFlags** members.\n\nRenamed **FAttr** members (used with **filesystem.exists**).\n\nRenamed **filesystem.delete** parameter *tryRecycleBin*.\n\nChanged type of **shortcutFile.Hotkey**.\n\nChanged **elm.focused** parameter type.\n\nChanged **string.ReplaceAt** parameter type.\n\nFrom clipboard classes removed RTF format functions.\n\nRenamed some functions of class **ProcessMemory**.\n\n\n### Bug fixes\nWildcard regular expressions in some cases aren't case-insensitive as documented.\n\nWrong return type of **wildex.RegexPcre**.\n\nOn unhandled exception don't run finally code blocks.\n\nSeveral bugs in editor.\n\n\n### Other changes\nAdded panels Cookbook and Recipe.\n\nAdded panel Outline.\n\nAdded NuGet package manager: menu **Tools > NuGet**.\n\nNew class **computer**. Shutdown etc.\n\nNew class **sound**. Play, speak.\n\nNew functions: **elm.ComboSelect**, **wnd.runAndFind**, **filesystem.enumFiles**, **filesystem.enumDirectories**, **filesystem.more.emptyRecycleBin**, **script.debug**.\n\nAdded UI to create \"run script\" command line, shortcut or scheduled task easily. Menu **TT > Script triggers**.\n\nVarious improvements.\n\nChanged program name.\n"
  },
  {
    "path": "Other/DocFX/_doc/changes/old/v0.5.md",
    "content": "﻿## Version 0.5.0\n\n### Breaking changes\nChanged **filesystem.delete** parameters.\n\nChanged some **ProcessMemory** functions.\n\nFrom **wpfBuilder** removed indexers and **Also**.\n\nRenamed:\n- **keys.more.Hotkey** -> **RegisteredHotkey**.\n- **filesystem.more.DisableRedirection** -> **FileSystemRedirection**.\n\n\n### Bug fixes\nSilently crashes if installed an older .NET 6 version. It's a .NET bug. Now asks to upgrade .NET.\n\nToolbars of some windows are hidden when the window is active.\n\nIn some cases the script process does not exit when using **osdText.showText**.\n\nIncorrect parsing of menu item text with hotkey and tooltip.\n\nSeveral bugs in library and editor.\n\n\n### Other changes\nChanged program name again.\n\nNew functions: **process.triggers**, **process.is32Bit**, **process.getCommandLine**, **process.waitForExit**.\n\nAdded recipes:\n- Many in folder \"Custom dialog window\".\n- Filesystem events and process start/end events.\n- Using LINQ functions with collections.\n\nC# 11 raw strings.\n"
  },
  {
    "path": "Other/DocFX/_doc/changes/old/v0.6.md",
    "content": "## Version 0.6.0\n\n### Bug fixes and improvements\nImprovements and bug fixes in auto-hide toolbars.\n\nImprovements in **wnd.ZorderX** functions.\n\nFixed: **elm** functions may kill Chrome on Windows 7.\n\nAnd more.\n\n\n### Other changes\nMenu **TT > New toolbar**. Makes easier to create new toolbars, set trigger, attach to window or auto-hide at screen edge.\n\nMenu **TT > New trigger**.\n\nNow icons like `\"*Pack.Name color\"` can be used in exe and dll too.\n"
  },
  {
    "path": "Other/DocFX/_doc/changes/old/v0.7.md",
    "content": "﻿## Version 0.7.0\n\n### Library\nNew classes:\n- **internet**, **ExtInternet**.\n- **ExplorerFolder**.\n- **screen.at**.\n\nNew functions:\n- **AutotextTriggerArgs.Menu**.\n- **WindowTriggerArgs.ShowToolbarWhenWindowName**.\n- **toolbar.Metrics**, **popupMenu.Metrics**.\n- And more.\n\n\n### Editor\nNew features (all in menus and toolbars):\n- \"WPF preview\".\n- \"Go back\", \"Go forward\".\n- \"Format document\", \"Format selection\".\n- \"Create delegate\".\n- \"Surround with for\", \"Surround with try catch\".\n\nVarious improvements.\n\n\n### Bug fixes and improvements\n\nImprovements in toolbars.\n\n\n### Breaking changes\n**string.Lines**: Removes the last empty line. Removed one overload.\n\nRemoved **wait.forVariable**. Instead use **wait.forCondition**.\n\nMouse triggers instead of screen index now use a **screen** object.\n"
  },
  {
    "path": "Other/DocFX/_doc/changes/old/v0.8.md",
    "content": "## Version 0.8.0 - 0.8.1\n\n### Library\nNew functions:\n- **script.end**, **script.testing**.\n- **computer.isOnBattery**.\n- AES encryption functions in class **Convert2**.\n- And more.\n\n\n### Editor\nSupports more C# 11 features.\n\nCookbook recipes about:\n- Other triggers.\n- C# compiler.\n- WebView2 web browser control.\n\n\n### Bug fixes and improvements\nFixed some bugs in editor.\n\nImproved NuGet features.\n\nImproved UI element find/get functions when web content is in an embedded Chrome/Edge based control.\n\nImprovements on Windows 11. For example, WPF would disable the unhandled exception info.\n\nImprovements in toolbars.\n\nFixed: **script.setup** *sleepExit* does not work on computers with Modern Suspend.\n\nAnd more.\n\n(0.8.1) Fixed 0.8.0 bug: can't compile scripts with role exeProgram.\n\n\n### Breaking changes\nChanged program name: C# Uiscripter -> LibreAutomate C#. Program folder name LibreAutomate. Program file names unchanged.\n\nRenamed and moved:\n- Class **Au.script.editor** -> **Au.More.ScriptEditor**.\n- Class **Au.print.Server** -> **Au.More.PrintServer**.\n- Class **Au.popupMenu.MenuItem** -> **Au.Types.PMItem**.\n- Class **Au.toolbar.ToolbarItem** -> **Au.Types.TBItem**.\n- Class **Au.Types.MTBase.MTItem** -> **Au.Types.MTItem**.\n\nRenamed:\n- **MMetrics** -> **PMMetrics**.\n- **MSFlags** -> **PMFlags**.\n- **MKHook** -> **PMKHook**.\n\nReplaced:\n- **computer.sleep** -> **computer.suspend**.\n"
  },
  {
    "path": "Other/DocFX/_doc/changes/old/v0.9.md",
    "content": "## Version 0.9.0 (2022-10-05)\n\n### Library\nNew functions:\n- **filesystem.more.getFinalPath**.\n- In class **ScriptEditor**: **InvokeCommand**, **GetCommandState**, **ShowMainWindow**.\n- In class **osVersion**: several new properties.\n\nNew features:\n- **clipboard** copy and paste functions: added parameter *hotkey*.\n\n\n### Editor\nCan import folders as links.\n\nCan set default icon of file types (script, class, folder).\n\nNew output tag <nonl> - don't add newline.\n\nCookbook recipes about:\n- **ListView** and **DataGrid** controls.\n\n\n### Bug fixes and improvements\nImproved documentation.\n\nFixed some bugs in editor.\n\n\n### Breaking changes\nRenamed and moved:\n- Struct **Au.wait.Loop** -> **Au.More.WaitLoop**.\n\nRenamed:\n- **keys.SendIt** -> **keys.Send**.\n\nRemoved:\n- One **wnd.HasChild** overload.\n- One **wnd.getwnd.allWindows** overload.\n- One **wnd.getwnd.threadWindows** overload.\n- One **wnd.getwnd.Children** overload.\n\nWeb pages moved to https://www.libreautomate.com/. Old URLs redirected there.\n"
  },
  {
    "path": "Other/DocFX/_doc/changes/old/v1.0.md",
    "content": "## Versions 1.0.0 (2024-01-22) to 1.0.1 (2024-01-28)\n\n### Editor\nDebugger.\n\nIn icon names can be specified size, like `\"*Pack.Icon color @12\"`. See [ImageUtil.LoadWpfImageElement]().\n\nMenu **Edit > Generate > Create event handlers, overrides**.\n\nFixed bugs:\n- When compiling a library, incorrectly adds icons specified in code like `\"*Pack.Icon color\"`. Then the library may fail to load these icons.\n- Incorrectly installs some NuGet packages.\n\nNew in 1.0.1:\n- Debugger command **Jump to here**.\n- Debugger can print assembly unload events.\n\n### Library\nNew members:\n- **wpfBuilder**: **Last2**, **LabeledBy**.\n\n### Breaking changes\nRemoved the \"debugger script\" feature.\n"
  },
  {
    "path": "Other/DocFX/_doc/changes/old/v1.1.md",
    "content": "## Version 1.1.0 (2024-02-10) to 1.1.8 (2024-03-10)\n\n### Editor\nNew features:\n- Automatically updates environment variables in LA and task processes whenever they are changed in the registry.\n- Editor extensions can use **Panels.Recipe.OpeningRecipe** to intercept and replace cookbook recipe text.\n- (1.1.8) **Options > Compiler > Always print \"Compiled\"**.\n- (1.1.8) The output tag `<fold>` can have custom link text, like `<><fold link text>...</fold>`.\n\nImproved:\n- Several improvements.\n\nFixed bugs:\n- Cannot set exe version with **Publish**.\n- IME does not work when editing a file name.\n- (1.1.1) Exception when trying to end a script task if the main window never was visible.\n- (1.1.1) LA crashes when called **script.debug** if the main window never was visible.\n- (1.1.2) When editing a file name, cannot use some hotkeys, eg `Ctrl+C`, `Delete`.\n- (1.1.3) LA crashes when using Git.\n- (1.1.4) \"Auto-backup\" does not work.\n- (1.1.7) Sometimes LA crashes when closing a tool like \"find window\".\n- (1.1.8) Exception when formatting certain code without option \"Compact\".\n\nRemoved features:\n- Preload **miniProgram** script task process to make task startup faster. Sometimes this feature caused problems. Inactive windows, old environment variables etc. Now the startup speed is the same as when role is **exeProgram**.\n- (1.1.4) Middle-click the tray icon to disable/enable triggers (it didn't work on Windows 11). Instead use the right-click menu.\n\n### Library\nNew members:\n- (1.1.8) **OKey.TextShiftEnter** (`opt.key.TextShiftEnter`).\n\nNew parameters:\n- **toolbar** constructor: *settingsFile*.\n\nImproved:\n- **miscInfo.getTextCursorRect** works with more apps.\n\nFixed bugs:\n- Console functions in some cases incorrectly detect encoding UTF-16.\n- (1.1.6) **ExplorerFolder.GetFolderPath** returns incorrect string if the path contains certain non-ASCII characters.\n- (1.1.7) Code like `e.Elm.FindAll()` throws **InvalidOperationException**.\n\nRemoved features:\n- (1.1.4) Middle-click the tray icon to end the task (it didn't work on Windows 11). Instead use the right-click menu.\n\n### Breaking changes\n(1.1.3) Console functions now don't try to detect encoding UTF-16. Because impossible to detect reliably.\n"
  },
  {
    "path": "Other/DocFX/_doc/changes/old/v1.2.md",
    "content": "## Version 1.2 (2024-04-18)\n\n### Editor\nNew small features:\n- Panel **Recipe**: you can set font in **Options**.\n- Panel **Mouse**: context menu with some options.\n- **Options > Other > Internet search URL**.\n- Menu **Edit > Tidy code > Deduplicate wnd.find**.\n- Hotkeys displayed in tooltips of toolbar buttons.\n- To unload `AuCpp.dll`: `rundll32.exe \"C:\\path\\to\\AuCpp.dll\",UnloadAuCppDll 0`.\n\nImproved:\n- Tool **Find UI object**: improved feature \"capture smaller element\". Use hotkey `Shift+F3`.\n- Tool **Find UI object**: option \"Use role in elm variable name\".\n\nFixed bugs:\n- Fixed several bugs.\n\n### Library\nNew members:\n- **dialog.options.timeoutTextFormat**.\n- **popupMenu.caretRectFunc**.\n- **wait.retry**.\n- **ScriptEditor.GetFileInfo**.\n- **ScriptEditor.TestCurrentFileInProject**.\n\nNew parameters:\n- **wnd.Move**, **wnd.Resize**, **wnd.MoveL**: *visibleRect* (exclude the transparent frame).\n- **elm** functions: in the *prop* parameter can be specified **url**. Use to search in side panels, developer tools, settings etc. The **Find UI element** tool now adds it when need.\n\nImproved:\n- **elm** with Chrome and other Chromium-based web browsers.\n\n### Breaking changes\nRenamed functions:\n- **wpfBuider.FormatText** -> **formatTextOf**,\n- **wpfBuider.FormattedText** -> **formattedText**,\n- **popupMenu.DefaultFont** -> **defaultFont**, \n- **popupMenu.DefaultMetrics** -> **defaultMetrics**, \n- **toolbar.DefaultMetrics** -> **defaultMetrics**.\n"
  },
  {
    "path": "Other/DocFX/_doc/changes/old/v1.3.md",
    "content": "## Versions 1.3.0 (2024-06-27) to 1.3.2 (2024-07-03)\n\n### Editor\nMore features in code snippets:\n- Snippet code can contain multiple fields that can be selected with `Tab` or `Enter`. When leaving an edited field, updates text of related fields.\n- Formats the inserted snippet code.\n- The **Snippets** tool can import Visual Studio and VSCode snippets.\n- Surround with snippet (menu **Edit > Selection > Surround** or toolbar button).\n- Snippet context detected automatically.\n- A snippet can add `/*/ meta comments /*/`.\n\nFormats code when completing current statement (adding `;`, `{  }` etc). This feature can be disabled in **Options**.\n\nAdds `;` when starting a statement. Later deletes if don't need. This feature can be disabled in **Options**.\n\nYou can use `Backspace` to exit current multiline `{ block }` when the text cursor is in the last line of the block and that line is blank. Previously `Ctrl+Enter` worked in a similar way.\n\nChanges in code editor feature \"statement completion on `Enter` before `)` or `]`\":\n- Can be disabled: **Options > Code editor > Statement completion > Enter before )**.\n- Removed: `Esc` key prevents it.\n- Added: space before prevents it. Other ways: comma before; `Shift+Enter`.\n- After statement completion, next **Undo** command undoes the change and adds new line without statement completion.\n- Related: Now the \"complete statement anywhere\" hotkey (`Ctrl+Enter` etc) can be set in **Options**.\n\nIf there is selected text and you type `(`, `[` or `{`, surrounds that text like `(selected)` or `{ selected }`.\n\nColored regular expression text.\n\nCan fold namespaces, local functions, lambda, initializer lists, strings.\n\nDrag and drop a non-script file from the **Files** panel to the code editor: can add `/*/ meta comments /*/`.\n\nAdded floating panel options **Topmost** and **Unowned**. Right-click panel header...\n\nSeveral small improvements. Removed some low-value features.\n\nFixed bugs:\n- Debugger settings: does not work **not**.\n- And more.\n\n##### v1.3.1:\n- Fixed several bugs in new features.\n- Several small improvements.\n- Added **Options > Program > Visible if not auto-started**, and command line `/a` which indicates that the program started automatically.\n\n##### v1.3.2:\n- Fixed bug: some editor font styles incorrectly saved.\n\n### Library\nNew members:\n- **wnd.IsMatch** overload for multiple windows.\n- **string.Split**, **ReadOnlySpan\\<char\\>.Split** and **ReadOnlySpan\\<char\\>.SplitAny** overloads that return **StartEnd[]**.\n- **ReadOnlySpan\\<char\\>.Split** and **ReadOnlySpan\\<char\\>.SplitAny** overloads that return **string[]**.\n- **regexp.Match** and **IsMatch**: overloads with **ReadOnlySpan\\<char\\>**.\n\nNew parameters:\n- **string.Lines**: *preferMore* (include the last empty line), *rareNewlines*.\n- **string.LineCount**: *rareNewlines*.\n\nFixed bugs:\n- When used in a single-file app or portable LA, **folders.NetRuntimeDesktopBS** returns `null`, and **folders.NetRuntimeDesktop** throws exception.\n- **regexp** functions always return `false` if the subject string is empty, even if the regular expression can match empty string.\n\nv1.3.2:\n- Improved: **print.redirectConsoleOutput** is initially `true` in `exeProgram` scripts started from LA (previously only `miniProgram`). Also **Console.ReadLine** uses **dialog.showInput**.\n"
  },
  {
    "path": "Other/DocFX/_doc/changes/old/v1.4.md",
    "content": "## Versions 1.4.0 (2024-07-29) to 1.4.1 (2024-08-03)\n\n### Editor\n\nUses C# 13.\n\nThe **Files** panel reflects workspace changes made not in the editor (added/deleted files). See also **Options > Workspace > Hide/ignore files and folders**.\n\nDark themes. See **Options > Font, colors**.\n\nImprovements in the **Portable LibreAutomate setup** tool.\n\n**editorExtension** scripts can use `await`. Previously would hang.\n\nRemoved feature \"surround selected text when typed `(` etc\".\n\nFixed several bugs.\n\n##### v1.4.1:\n- Several bug fixes and improvements.\n- Improvements in portable setup tool UI.\n\n### Library\n\nFixed bugs:\n- **print.it** and **print.list** skip first empty-string items of **IEnumerable**.\n\n### Breaking changes\n\nThe **Files** panel now is synchronized with the filesystem. Because of this, some unwanted files/folders may be added. The program prints links to the added files."
  },
  {
    "path": "Other/DocFX/_doc/changes/old/v1.5.md",
    "content": "## Version 1.5.0 (2024-09-12)\n\n### Editor\nNew much bigger Windows API database.\n\nChanges in `/*/ meta comments /*/`:\n- Options can be separated by newlines without semicolon.\n- Options can be commented out like `//option value;`.\n\nNew cookbook recipes:\n- Passwords in scripts.\n\nImproved:\n- Several improvements.\n\nFixed bugs:\n- Possible exception when a script is starting. Rare.\n- And more.\n\n### Library\nNew members:\n- **print.it** and **print.util.toString** overloads for binary data displayed in hex editor format.\n\nFixed bugs:\n- **consoleProcess** functions **Write** and **Prompt** don't auto-append newline if input encoding is **Encoding.Unicode**.\n"
  },
  {
    "path": "Other/DocFX/_doc/changes/old/v1.6.md",
    "content": "## Version 1.6.0 (2024-11-04)\n\n### Editor\nImproved **New trigger** tool.\n\nMenu **TT > Schedule**. Now you can edit scheduled task properties in LibreAutomate.\n\nMenu **TT > Find triggers**. Finds triggers of current script.\n\nSeveral bug fixes and improvements.\n\n### Library\nNew members:\n- **wpfBuilder.StartPanel** (adds panel of any type).\n\nNew parameters:\n- **wpfBuilder.LabeledBy**: *bindVisibility*.\n- **wpfBuilder.Validation**: *linkClick* callback.\n\nImproved:\n- **ExplorerFolder** supports multiple tabs. Added more functions and parameters.\n- **EnumUI** supports more panel types.\n- Improvements in **wpfBuilder.FormatText** and similar functions.\n- And more.\n\nFixed bugs:\n- Some functions may throw **InputDesktopException**. Including **WndUtil.EnableActivate**, **dialog.show**, **run.it**, and some **elm** functions.\n"
  },
  {
    "path": "Other/DocFX/_doc/changes/old/v1.7.md",
    "content": "## Version 1.7.0 (2024-12-07) to 1.7.1 (2024-12-21)\n\n### Editor\nNew cookbook recipes:\n- Web browser automation with Playwright.\n\nImproved:\n- Menu **Run > Publish**: added **Self-extract**.\n\n#### v1.7.1:\n- Fixed bug in the **Publish** tool: error if assembly attributes like `AssemblyVersion` are not in `AssemblyInfo.cs`.\n\n### Library\nNew classes:\n- **CpuUsage**.\n\nNew members:\n- **opt.PasteSleep**. Makes \"paste\" functions slower (default 100 ms) but more reliable.\n\nImproved:\n- **opt.key/mouse/warnings** now are managed differently, and work well with `await` and thread pool tasks. Each new thread and task inherits a copy of **opt.key/mouse/warnings** of the parent thread/task, which then is isolated from parent and other threads/tasks. Now **opt.init** is obsolete and is the same as **opt**.\n- **mouse.save/restore/lastXY** now works well with `await`. Now the saved data is shared by all threads.\n- **clipboard.paste** and other \"paste\" functions now can work with apps where previously failed. Change **opt.PasteSleep** where still fails.\n- Auto-enabling UI elements in Chrome/Edge works better. With new browser versions it used to fail sometimes.\n\n### Breaking changes\nBecause of the **opt.key/mouse/warnings** management change, initial options in threads and tasks now may be different.\n\nWhen auto-enabling UI elements in Chrome/Edge, some rarely used features are enabled later (asynchronously). HTML attributes, **DefaultAction**, maybe more. Your code that uses them may have to wait or retry. To enable everything instantly, run browser with command line `--force-renderer-accessibility`."
  },
  {
    "path": "Other/DocFX/_doc/changes/old/v1.8.md",
    "content": "## Version 1.8.0 (2025-01-16)\n\n### Editor\nCan run on Windows ARM64.\n\nImproved:\n- Menu **File > Export, import > Export**: can include files specified in `/*/ c /*/` etc.\n- If in `/*/ c /*/` etc used filename without path, and multiple files with the given filename exist in the workspace, can use the one from subfolders or ancestor folders (if single exists there). Previously would be error, unless one is in the same folder.\n\nFixed bugs:\n- Git does not work when using the newest version of private Git.\n- Image text incorrectly hidden in non-ASCII code.\n\n### Library\nCan be used on Windows ARM64.\n"
  },
  {
    "path": "Other/DocFX/_doc/changes/old/v1.9.md",
    "content": "## Versions 1.9.0 (2025-03-21) - 1.9.2 (2025-04-06)\n\n### Editor\nFixed bugs:\n- Empty list in window **Active toolbars**.\n- (1.9.1) 1.9.0 installed debug versions of Roslyn dlls, which caused program errors in some cases.\n\nNew options:\n- **Options > Other > Documentation**. You can use local documentation of the installed program version instead of the online documentation of the latest program version.\n\n### Library\nNew members:\n- **keys.waitForHotkeys**.\n- **HelpUtil.AuHelpBaseUrl**.\n- **popupMenu.ImageSize**, **toolbar.ImageSize**.\n- **IconImageCache.CommonOfSize**.\n- (1.9.2) **toolbar.MaximizedWindowTopPlus**.\n\nNew parameters:\n- **HttpServerSession.Listen** parameter *started* (callback).\n\nFixed:\n- Cannot get the description property of some UI elements in UIA mode.\n- (1.9.2) Cannot find UI element if an HTML attribute is specified (recent browser versions).\n\nOther changes:\n- Removed file `sqlite3.dll`. Now class `sqlite` uses `winsqlite3.dll` of Windows 10+; on older OS auto-downloads.\n"
  },
  {
    "path": "Other/DocFX/_doc/changes/template.md",
    "content": "# Version 1..0 (2025-)\n\n## Editor\n\nNew tools:\n- .\n\nNew cookbook recipes:\n- .\n\nImproved:\n- .\n\nFixed bugs:\n- .\n\n## Library\n\nNew classes:\n- .\n\nNew members:\n- .\n\nNew parameters:\n- .\n\nImproved:\n- .\n\nFixed bugs:\n- .\n\n### Breaking changes\n"
  },
  {
    "path": "Other/DocFX/_doc/changes/v1.10.md",
    "content": "## Version 1.10.0 (2025-04-29) - 1.10.1 (2025-04-30)\n\n### Editor\nTriggers list window. More info: menu **TT > Triggers list info**.\n\nUpdated Roslyn C# compiler/analyzer dlls. Supports C# 14 `extension` etc.\n\nMenu **Edit > Generate > Convert \\[PreserveSig\\] methods**.\n\nFixed bugs:\n- Debugger: no variables when stops on exception.\n- And more.\n\n### Library\nNew members:\n- **ActionTriggers.ShowTriggersListWindow**.\n- **TKFlags.Numpad**, **TKFlags.NumpadNot**.\n- More properties in trigger type classes.\n\nImproved:\n- **ActionTrigger.RunAction** can be called from any thread.\n\nFixed bugs:\n- **AutotextTriggers** properties **PostfixKey** and **WordCharsPlus** don't work in some cases.\n- **OcrGoogleCloud**: exception if no text detected.\n- **OcrMicrosoftAzure**: exception if image too small.\n- (1.10.1) **NativeApi.BOOL** size 8 (must be 4).\n"
  },
  {
    "path": "Other/DocFX/_doc/changes/v1.11.md",
    "content": "## Version 1.11.0 (2025-05-14)\n\n### Editor\nNew tools:\n- Menu **TT > Script lauchers > Shell menu**.\n\n### Library\nFixed bugs:\n- In programs compiled with LibreAutomate fails to load assemblies in subfolder `runtimes\\win`.\n"
  },
  {
    "path": "Other/DocFX/_doc/changes/v1.12.md",
    "content": "## Version 1.12.0 (2025-06-21)\n\n### Editor\n[PiP session](https://www.libreautomate.com/editor/PiP%20session.html). See menu **Run > PiP session** and **Run in PiP**. Creates a separate session of the same user in a Picture-in-Picture window, where UI automation scripts can run in the background without interfering with your mouse and keyboard. Requires Windows 8.1 or later, a non-Home edition.\n\nThe **Files** view now is better synchronized with the filesystem.\n\nUpdated the icons database. Many new icons.\n\nIcon strings: margin and layers. See menu **Tools > Icons > Custom icon string**.\n\nFixed bugs:\n- Menu **Tools > Update icons** may not clear caches of image size != 16.\n- Autocompletion list sometimes disappears when pasting.\n\n### Library\nNew classes:\n- **FileWatcher**.\n\nNew members:\n- **script.runInPip**, **script.isInPip**.\n- **miscInfo.isChildSession**.\n- **JSettings**: **ModifiedExternally** (event), **Reload**.\n\nNew parameters:\n- **JSettings.Load**: *useDefaultOnError*.\n\nFixed bugs:\n- **filesystem.enumDirectories**: flag **AllDescendants** does not work.\n"
  },
  {
    "path": "Other/DocFX/_doc/changes/v1.13.md",
    "content": "## Version 1.13.0 (2025-07-06) - 1.13.1 (2025-07-08)\n\n### Editor\nDoes not require the .NET SDK for NuGet and Publish. If it isn't installed, automatically downloads (~26 MB) and uses a minimal SDK.\n\nRemoved the \" C#\" from the full name of the program. Now just \"LibreAutomate\".\n\nSeveral improvements.\n\nIn 1.13.1 fixed bug in the LibreAutomate setup program: Windows UI shows two LibreAutomate programs installed.\n\n### Library\nNo changes\n"
  },
  {
    "path": "Other/DocFX/_doc/changes/v1.14.md",
    "content": "## Version 1.14.0 (2025-08-06)\n\n### Editor\n**NuGet** tool improvements:\n- Check for updates.\n- Easier to update to a selected version.\n- Right-click an unused folder to delete.\n- Supports multiple sources better.\n- Displays package icons.\n- And more.\n\nPanel **Find > Saved searches**.\n\nFixed bugs:\n- Does not work with .NET 9.0.8. Crashes at startup etc.\n- When print text contains `<image \"image\">`, may hang or not display the image.\n- And more.\n\n### Library\nNew members:\n- **osVersion.thisLibrary**.\n"
  },
  {
    "path": "Other/DocFX/_doc/changes/v1.15.md",
    "content": "# Version 1.15.0 (2025-12-08)\n\n## Editor\n\nCreated internal infrastructure for using AI in LA.\n\nFind LA documentation using AI in the **Help** panel. Copy-paste results in ChatGPT etc to get accurate answer.\n\nMCP server. Provides LA documentation for local AI tools such as Visual Studio Copilot.\n\nUse AI to find icons by name or/and image in the **Icons** tool.\n\nChanges in LA window:\n- Panel **Cookbook** renamed to **Help**. Added full LA documentation.\n- Panel **Recipe** renamed to **Read**. Displays LA documentation articles. See **Options > Other > Documentation**.\n- The default location of panels **Help**, **Read** and **Tasks** now is at the right side. If want to use it, exit LA and delete `Documents\\LibreAutomate\\.settings\\Layout.xml`.\n- The **Read** panel now is always hidden at startup.\n- Removed toolbar **Help**.\n\nChanges in tools like **Find UI element**:\n- They run in separate process.\n- Improved UI element capturing in some apps and modes.\n\nNew code editor features:\n- Menu **Edit > Assist > Insert new line before ) ] ;** (and toolbar button). Temporarily disables statement auto-completion on `Enter`.\n- Continue a vertical method chain by typing `.` on the next line after the `;`.\n\nNew in menu **Edit > Find**:\n- **Find next** (`F3` or click the **Find** button).\n- **Find previous** (`Shift+F3` or right-click the **Find** button).\n- **Next found** (`F4`). Selects next item in the **Found** panel.\n- **Previous found** (`Shift+F4`).\n\nQuickly hide toolbar buttons using the context menu (in LA window).\n\nSeveral other improvements.\n\nFixed some bugs.\n\n### Breaking changes\n\nChanged namespaces in `Au.Editor` project. Edit your `editorExtension` scripts that use its internal classes (unlikely).\n\n## Library\nUpdated PCRE regex library to v10.46.\n\nNew members:\n- `dialog`: fluent API methods for setting dialog properties. The old functions now are hidden.\n- `wpfBuilder.Add` overloads without parameter `flags`. The old overloads now are hidden. Flags replaced with: function `Child`; parameter *raw* of one `Add` overload.\n- And more.\n\nNew parameters or paramater features:\n- `script.end(\"\");` ends other processes of current script.\n\nNew `dialog` features:\n- Easier to add links.\n- Supports icons like `\"*Pack.Name color\"`.\n\nFixed bugs:\n- `script.restart` does not work.\n- `JSettings` can deadlock.\n- `WTaskbarButton` exception if used in multiple threads.\n- Owned floating toolbars remain topmost after the owner that was topmost becomes non-topmost.\n\nRestored the \"auto-enable browser UI elements\" feature that was broken in the latest Chromium version."
  },
  {
    "path": "Other/DocFX/_doc/docfx.json",
    "content": "{\n  \"metadata\": [\n    {\n      \"src\": [\n        {\n          \"src\": \"C:/Temp/Au/DocFX/source/\",\n          \"files\": [\n            \"Au.csproj\"\n          ]\n        }\n      ],\n      \"dest\": \"api\",\n      \"shouldSkipMarkup\": true,\n      \"filter\": \"filter.yml\",\n      \"disableGitFeatures\": false,\n      \"disableDefaultFilter\": false,\n      \"memberLayout\": \"separatePages\",\n      \"noRestore\": true\n    }\n  ],\n  \"build\": {\n    \"content\": [\n      {\n        \"files\": [\n          \"api/**.yml\",\n          \"api/index.md\"\n        ]\n      },\n      {\n        \"files\": [\n          \"articles/**.md\",\n          \"articles/**/toc.yml\",\n          \"editor/**.md\",\n          \"editor/**/toc.yml\",\n          \"cookbook/**.md\",\n          \"misc/**.md\",\n          \"toc.yml\",\n          \"*.md\"\n        ]\n      }\n    ],\n    \"resource\": [\n      \"images/window.png\"\n    ],\n    \"globalMetadata\": {\n      \"_appTitle\": \"LibreAutomate\",\n      \"_enableNewTab\": true,\n      \"_disableBreadcrumb\": true,\n      \"_disableAffix\": true,\n      \"_disableContribution\": true\n    },\n    \"dest\": \"C:/Temp/Au/DocFX/site-temp\",\n    \"globalMetadataFiles\": [],\n    \"fileMetadataFiles\": [],\n    \"template\": [\n      \"default\",\n      \"template1\",\n      \"template2\"\n    ],\n    \"postProcessors\": [],\n    \"markdownEngineName\": \"markdig\",\n    \"noLangKeyword\": false,\n    \"keepFileLink\": false,\n    \"cleanupCacheHistory\": false,\n    \"disableGitFeatures\": false,\n    \"sitemap\": {\n      \"baseUrl\": \"https://www.libreautomate.com/\",\n      \"changefreq\": \"weekly\"\n    }\n  }\n}\n\n//https://dotnet.github.io/docfx/tutorial/docfx.exe_user_manual.html\n"
  },
  {
    "path": "Other/DocFX/_doc/editor/Application.md",
    "content": "---\nuid: Application\n---\n\n# LibreAutomate\n\nThe application is an integrated scripting environment for creating and executing automation scripts using its automation library and C# programming language.\n\n![window](../images/window.png \"Editor window\")\n\nIn the main window: code editor, several panels, toolbars and menu bar. Also there is a tray icon. The main window can be hidden (it depends on app settings).\n\n## Code editor\nHere you edit automation scripts and other text files.\n[Features](xref:code_editor).\n\nMultiple files can be open, but only one is visible. The list of open files is in the **Open** panel.\n\nAll changes are saved automatically on these events: app deactivated, main window closed, timer. The **Save** button saves all changes immediately.\n\n## Panels\n\n### Files\nAll files and folders of current workspace.\n\nClick an item to open in the code editor. Right-click to show context menu. Middle-click to close. Drag and drop to reorder or add new files.\n\nAll changes are saved automatically.\n\nDeleted files and folders are moved to the Recycle Bin if possible.\n\nYellow background if the file is open in the code editor.\n\nC# code files have `.cs` extension, but it is hidden.\n\nSee [file properties](xref:file_properties).\n\nFiles are of these types:\n- Script - C# code file that can be executed directly, like a program.\n- Class - C# code file that contains classes and functions used by other files.\n- Text files - txt, xml and other files that can be edited in the code editor.\n- Other files - files that cannot be edited. Images etc. For example can be added to exe program resources.\n\nFolders are of these types:\n- Simple folders.\n- Folders named like `@Name` are [project](xref:class_project) folders.\n\nThe collection of files and folders is a *workspace*. It is a folder on disk. In the folder:\n- Folder `files` contains files and folders of the workspace.\n- File `files.xml` is the list of files and folders of the workspace.\n- File `settings.json` - workspace settings.\n- File `bookmarks.csv` - saved bookmarks.\n- Folder `.state` contains code editor states for files, such as folding, markers and current position.\n- The `.xxx` folders are created and used by the app for various purposes.\n- Folders `exe` and `dll` - default folders for files created when compiling code files with role `exeProgram` or `classLibrary`.\n\n### Open\nList of files open in the code editor.\n\nRight-click - context menu. Middle-click - close.\n\n### Tasks\nRunning scripts.\n\nRight-click - context menu. Middle-click - close.\n\n### Find\nFind and replace text in the code editor or in multiple files.\n\nRight-click an edit field - context menu. Middle-click - clear text; if empty - show recent.\n\n### Found\nResults of **Find in files**, **Find file**, **Find references** and some other commands.\n\n### Output\nThe app writes various info here. For example script compilation errors. Scripts use [print.it]() for it.\n\nRight-click - context menu. Middle-click - clear.\n\n### Mouse\nMouse x y, window name/class/program, control id/class/name, menu item id.\n\n### Outline\nHelps to find types, functions, fields, namespaces and regions in current C# code file.\n\n### Help\nDocumentation - table of contents.\n\n### Read\nDocumentation - article.\n\n### Bookmarks\nList of bookmarks.\n\nRight-click - context menu.\n\n### Debug, Breakpoints\n[Debugger](xref:debugger).\n\n## Menus\nSee [Menu commands](xref:menu_commands).\n\n## Tray icon\nClick - show and activate editor window. Right-click - show context menu.\n\nGray when triggers are disabled.\n\n## Customizing\n\n### Customize the panel layout\n\nPanels and toolbars can be resized with splitters, moved to another place, grouped in stacks and tab groups, docked, floating, hidden.\n\nEach panel, tab group and toolbar has a header bar. Right-click it to show a customization menu. Also use the menu to show hidden panels. Double-click the bar to toggle floating/docked state; drag to make floating; middle-click to hide.\n\nThe splitters between panels have a right-click menu to set splitter width and float/dock/move that stack of panels.\n\nIt's saved in `%folders.Documents%\\LibreAutomate\\.settings\\Layout.xml`. To reset to the default layout, exit LibreAutomate and delete the file.\n\n### Customize toolbars, menus and hotkeys\n\nUse the **Customize** tool. Open it via the **Tools** menu or right-click a toolbar.\n\nYou can add buttons to any toolbar, and change button properties. Default buttons cannot be removed, but can be hidden. To access hidden buttons, click the drop-down arrow in the toolbar. New toolbars can't be added, but there are two initially empty toolbars just for user buttons.\n\nMenu items can't be added/removed/moved, but you can change menu item properties. You can set hotkeys for menu commands.\n\nIt's saved in `%folders.Documents%\\LibreAutomate\\.settings\\Commands.xml`. You can edit the file in an XML editor. To reset everything, delete the file. To reset an item or toolbar, remove it from XML. Restart LibreAutomate.\n\nSee also cookbook article **Editor extension - modify UI**.\n"
  },
  {
    "path": "Other/DocFX/_doc/editor/Class files, projects.md",
    "content": "---\nuid: class_project\n---\n\n# Class files, projects\nA [script](xref:script) can contain functions and classes. Also can use those from class files and libraries.\n\n### Class files\nA class file contains C# code of one or more classes with functions that can be used in other C# files (script, class). By default it cannot run when you click the **Run** button.\n\nThere are several ways to use class files in a C# file `X`:\n- In `X` **Properties** click **Add file > Class file**.\n- If the class file is in a library (role `classLibrary`), in `X` **Properties** click **Add reference > Project**.\n- Create a project and add class files to the project folder. Used when the classes are used only in that project.\n\n### Projects\nA folder named like `@Project` (starts with `@`) is a project folder. To create: menu **File > New > New project**.\n\nProjects are used to compile multiple C# files together. The compilation creates an assembly file that can be executed as a script or exe program or used as a dll library.\n\nThe first code file in the project folder is the project's main file. All class files are compiled together with it when you try to compile or run any file of the project.\n\nThe main file can be a script or a class file. Most of its properties are applied to whole compilation. If it's a script, it runs when you click **Run**; such project is a *script project* and also can be used to create exe programs. Else the project is a *library project* and produces a dll file.\n\nThe folder can contain more scripts, but they are not part of the project. If they want to use project's class files, add them explicitly: **Properties > Add file > Class file**.\n\nUsually class files that are in a project are used only in files of that project, therefore they are not included in the drop-down list of **Properties > Add file > Class file**, unless the project folder name starts with `@@`.\n\n### Libraries\nA library is a dll file. It contains compiled classes with functions that can be used anywhere.\n\nA library can be created from a class file, usually the main file of a library project. In **Properties** select role `classLibrary`.\n\n### Project references\nAny C# file can use libraries. You can add library references in the **Properties** dialog. If it's a library whose source files are in current workspace, click **Add reference > Project**. It is known as *project reference*. It adds a reference to the assembly created by the library, auto-compiles the library when need, and enables [code editor features](xref:code_editor) such as \"go to definition\".\n\n### Test scripts\nBy default a class file cannot be executed directly when you click the **Run** button. But you'll want to test its functions etc while creating it. For it create a *test script* that is executed instead when you click the **Run** button. Let the script call functions of the class file. To create a test script for a class file: try to run the class file and then click the link in the error text in the output panel. And below is another way.\n\n### Run a class file without a test script or project\nSpecify role `miniProgram`, `exeProgram` or `editorExtension`.\n\nThen at the start add code that uses the class. By default class files containing such code cannot be used in other scripts (error \"Only one compilation unit can have top-level statements\"). Workaround: define a preprocessor symbol, and use `#if` to disable that code when the class file is compiled as part of another script etc. Example:\n\n```csharp\n/*/ role miniProgram; define TEST; /*/\n\n#if TEST\nClass1.Function1();\n#endif\n\nclass Class1 {\n\tpublic static void Function1() {\n\t\tprint.it(1);\n\t}\n}\n```\n\nWhen the first option in `/*/ ... /*/` is `role` other than `classFile` (like in the example), `define TEST` and other incompatible options are ignored when the class file is compiled as part of another script etc (for example the `TEST` symbol isn't added to the compilation). Else such options then cannot be used (error).\n\nThis does not work if the class file is in a project. See [ScriptEditor.TestCurrentFileInProject]().\n\n### File `global.cs`\nClass file `global.cs` is automatically included in the compilation of every script, as if the script begins with `/*/ c global.cs /*/`. To exclude it where not needed, add `/*/ define NO_GLOBAL; /*/`.\n\n By default, `global.cs` contains `global using` directives for commonly used namespaces.\n \n You can edit this file. For example:\n- Add/remove global usings, classes, attributes.\n- Add more class files and library references, like `/*/ c \\Classes\\file.cs; r Lib.dll; nuget folder\\Package; /*/`.\n- Edit completion list filters (see examples in the default code).\n\nNote: editing this file affects all C# code files, not only files created afterward.\n\nIf the `global.cs` file is missing, scripts cannot compile, and the app prints a warning with a link to create a new `global.cs` file with the default content. If multiple class files named `global.cs` exist in the workspace, the app uses the one in the default location: `\\Classes\\global.cs`. If that file is missing, it uses a non-external `global.cs` file if a single such file exists.\n"
  },
  {
    "path": "Other/DocFX/_doc/editor/Code editor.md",
    "content": "---\nuid: code_editor\n---\n\n# Code editor\nIn the code editor you edit automation scripts and other C# code.\n\nC# code example:\n```csharp\nmouse.click(10, 20);\nif (keys.isCtrl) {\n\tprint.it(\"text\");\n}\n```\n\nTo create the above code, you can type this text:\n```csharp\nmo.c 10, 20\nif ke.isct\n\npi \"text\n```\n\nWhile typing, editor completes words, inserts code snippets, adds `()`, `;`, `{}` and indentation. The result is the first code.\n\n## Features\n\n<!--\nTo generate TOC with #links, use the VS context menu command \"Generate TOC\". Then remove the indentation.\nBAD: now the Back button does not scroll to TOC. And the same in all DocFX-generated pages.\n-->\n- [List of symbols, autocompletion](#list-of-symbols-autocompletion)\n- [Bracket completion](#bracket-completion)\n- [Statement completion](#statement-completion)\n- [Auto indentation](#auto-indentation)\n- [Parameter info](#parameter-info)\n- [Quick info](#quick-info)\n- [Error info](#error-info)\n- [XML documentation comments](#xml-documentation-comments)\n- [Go to symbol documentation](#go-to-symbol-documentation)\n- [Go to symbol definition (source code), base](#go-to-symbol-definition-source-code-base)\n- [Go to script, file, URL](#go-to-script-file-url)\n- [Find and replace text](#find-and-replace-text)\n- [Find symbol references](#find-symbol-references)\n- [Highlight symbol references and matching braces](#highlight-symbol-references-and-matching-braces)\n- [Find symbol](#find-symbol)\n- [Rename symbol](#rename-symbol)\n- [Outline of current file](#outline-of-current-file)\n- [Navigate back/forward](#navigate-backforward)\n- [Bookmarks](#bookmarks)\n- [Code coloring](#code-coloring)\n- [Text folding](#text-folding)\n- [Separators between functions/types](#separators-between-functionstypes)\n- [Snippets](#snippets)\n- [Images in code](#images-in-code)\n- [Format code](#format-code)\n- [Comment/uncomment/indent/unindent lines](#commentuncommentindentunindent-lines)\n- [Capture UI elements, insert regex etc, implement interface](#capture-ui-elements-insert-regex-etc-implement-interface)\n- [Find Windows API and insert declarations](#find-windows-api-and-insert-declarations)\n- [Drag and drop files to insert path](#drag-and-drop-files-to-insert-path)\n- [Focus](#focus)\n- [WPF window preview](#wpf-window-preview)\n\n### List of symbols, autocompletion\nWhen you start typing a word, editor shows a list of symbols (classes, functions, variables, C# keywords, etc) available there. Or you can press `Ctrl+Space` to show the list anywhere.\n\nThe list shows only items that contain the partially typed text. The text in list items is highlighted. Auto-selects an item that is considered the first best match. On `Ctrl+Space` shows all. You can use the vertical toolbar to filter and group list items, for example to show only methods.\n\nWhile the list is visible, when you double-click an item or press `Enter`, `Tab`, `Spacebar` or type a non-word character, the partially or fully typed word is replaced with the text of the selected list item. Also may add `()`, for example if the word is a function name or a keyword like `if`. By default adds `()` only if completed with `Spacebar`; see **Options > Code**.\n\nTo select list items you also can click or press arrow or page keys. It does not hide the list. The tooltip-like window next to the list shows more info about the selected item, including links to online documentation and source code if available. Shows all overloads (functions with same name but different parameters), and you can click them to view their info.\n\nTo hide the list without inserting item text you can press `Esc` or click somewhere in code.\n\nWhile the list is visible, you can press the **[+]** button or `Ctrl+Space` to show another list that includes all types or extension methods, not only those from `using` namespaces.\n\n### Bracket completion\nWhen you type `(`, `[`, `{`, `<`, `\"` or `'`, editor adds the closing `)`, `]`, `}`, `>`, `\"` or `'`. Then, while the text cursor is before the added `)` etc, typing another `)` or `Tab` just leaves the enclosed area. Also then `Backspace` erases both characters.\n\n### Statement completion\nWhen you press `Enter` immediately before `)`, `]` or `;`, editor adds missing `;` or `{  }`, adds new line, moves the text cursor, and formats the statement. Except if before is `,` or space character.\n\nHowever the above feature interferes with writing multi-line statements. Ways to bypass it:\n- Disable temporarily: toggle **Raw Enter before ) ] ;** on the toolbar.\n- Use `Shift+Enter`. Or `Ctrl+Enter`, depending on settings.\n- Type space before `Enter`.\n- `Enter` and `Ctrl+Z` (undo). It undoes the change and adds new line inside the statement.\n- You can continue a vertical method chain by typing `.` on the next line after the `;`.\n- If you never need this feature, disable it in **Options**.\n\nUse hotkey `Ctrl+Enter` (can be changed in **Options**) to complete current statement when the text cursor is anywhere in it. Also it can add `{  }` to `class C` etc.\n\nAlso completes statement on `Enter` in a single-line `\"string\"` or `\"\"\"string\"\"\"`, unless the text cursor is at `\"\"\"|text\"\"\"`.\n\nTo complete statement without new line, use `;` instead of `Enter`. It adds `;` if missing, moves the text cursor, and formats the statement. With `Ctrl` it works anywhere in the statement; without - only before `)` or `]`.\n\nAs you begin typing a statement (eg a word in a blank line), editor adds `;`. Later deletes if don't need (eg when completing with `{  }`) or overtypes `;` with `;`. This feature can be disabled in **Options**.\n\nYou can use `Backspace` to exit current multiline `{ block }` when the text cursor is in the last line of the block and that line is blank.\n\n### Auto indentation\nOn `Enter` editor adds new line with correct indentation.\n\n### Parameter info\nWhen you type a function name and `(`, editor shows a tooltip-like window with info about the function and current parameter. To show the window from anywhere in an argument list, press `Ctrl+Shift+Space`. You can select oveloads with arrow keys or the mouse.\n\n### Quick info\nWhenever the mouse dwells on a symbol etc in the editor, a tooltip displays some info about the symbol, including documented exceptions the function may throw.\n\n### Error info\nErrors are detected in editor, as well as when compiling the code. Code parts with errors have red squiggly underlines, warnings green. A tooltip shows error/warning description. Also can contain links to fix the error, for example add a missing `using namespace` or Windows API declaration.\n\n### XML documentation comments\nEditor gets data for quick info, parameter info and other info about symbols from their assemblies and XML documentation comments. You can write XML documentation comments for your functions and types; look for how to on the internet. Documentation of the automation library and .NET is installed with the app. Documentation of other assemblies comes from their `assembly.dll` + `assembly.xml` files.\n\nEditor also helps to write XML documentation comments. Adds empty summary and parameters when you type `///` above a class, method etc. Adds `///` on `Enter`, shows a list of tags, autocompletes tags, color-highlights tags, text and `see` references.\n\n### Go to symbol documentation\nTo show symbol documentation if available, press `F1` when the text cursor is in it. Or click the **more info** link in the autocompletion item info or parameter info window.\n\nIf the symbol is from the automation library, it opens the online documentation page in your web browser. If the symbol is from .NET runtime or other assembly or unmanaged code (`[DllImport]` or `[ComImport]`), it opens the Google search page.\n\n### Go to symbol definition (source code), base\nClick a symbol and press `F12`. Or click the **source code** link in the autocompletion item info or parameter info window.\n\nIf the symbol is defined in your code, it opens that file and moves the text cursor. Else it can help to find the source code online.\n\nThe **Go to base** command finds the base class or implemented/overriden method definition.\n\n### Go to script, file, URL\nClick a file path string and press `F12` to select it in File Explorer. In the same way you can open folders, script files and web pages. It also works in meta comments, other comments and in code like `folders.System + @\"notepad.exe\"`. If the path etc does not start and end with `\"`, at first select it.\n\n### Find and replace text\nUse the **Find** panel to find and replace text in editor. It marks all matches in editor with yellow. Also can find files by name and files containing text. Can replace text in multiple files.\n\n### Find symbol references\nThe **Find references** command takes the symbol (class name, function name, variable, etc) at the current position and finds all places in current and related files where the symbol is used. Ignores matching words in comments, strings and `#if`-disabled code. Displays results in the **Found** panel. You can click a result line to go to that place. You can middle-click to close the file if it was opened from that results.\n\nThe **Find implementations** command is similar. If the symbol is an interface or class, finds types that implement or inherit it. If the symbol is an interface/abstract/virtual function, finds functions that implement or override it.\n\nWhat is \"related files\":\n- If current file is part of a @Project folder - all files of that project.\n- Files used by current file/project through `/*/ c /*/`.\n- If current file or project uses project references (`/*/ pr /*/`) and that project can use that symbol - all files of that projects.\n- If the symbol definition file or project is used by other files or projects through `c` or `pr` - all that files/projects.\n- If the symbol is defined in a dll or `global.cs`, does not search in entire workspace (it could be slow or gather many unwanted results), but searches in files/projects that use current file/project through `c`/`pr`.\n\n### Highlight symbol references and matching braces\nWhen the text cursor is in a symbol name, the editor automatically highlights that name everywhere in current document.\n\nWhen the text cursor is before an opening brace `({[<`, highlights the matching closing brace `)}]>`. When after a closing brace, highlights the matching opening brace. When at the `#` of `#if` etc, highlights matching `#endif` etc.\n\nTo turn off these features: **Options > Font, colors > Symbol/brace highlight**, set color white and alpha 0.\n\n### Find symbol\nThe **Find symbol** command shows a temporary window to find a symbol by name and go to its definition.\n\nThe search query can contain one or several parts of symbol name, like `part1 part2`. Or include the containing type, like `Type.name`. Or like `FM` to find `FindMe`.\n\nTo find only types, use prefix `t`, like `t name` or `t:name`. Prefix `m` is for members (function, field), `n` for namespaces.\n\nTo select a symbol, click or use arrow/page keys and `Enter`.\n\nFinds symbols defined in current file or project + files and projects included through `/*/ c /*/` and `/*/ pr /*/`.\n\n### Rename symbol\nThe **Rename symbol** command finds symbol definition/references like the **Find references** command, and replaces all with the specified new name. It is similar to the \"Find and replace text in files\" feature, but more precise.\n\nIf checked **Include comments/disabled/strings** and finds the word in comments/disabled/strings, at first displays these results in the **Found** panel. You can right-click some of them to exclude. Finally click the **Replace** link. The same if finds symbol usages that are ambiguous or error, for example a method name without parameters used in documentation comments `cref`.\n\nIt also automatically resolves name conflicts where possible, for example prepends namespace name.\n\n### Outline of current file\nThe **Outline** panel shows functions and fields defined in current file. Also types, if there are multiple. And regions. Click to go to the definition.\n\n### Navigate back/forward\nThe **Back** and **Forward** buttons work like in web browsers.\n\n### Bookmarks\nYou can mark lines in code, and later go there. Use menu **Edit > Navigate**, panel **Bookmarks** and the markers margin.\n\nMenu commands **Previous bookmark** and **Next bookmark** visit only active bookmarks. If there are no active bookmarks - only bookmarks in current document.\n\n### Code coloring\nDifferent kinds of code parts have different colors. Comments, strings, keywords, types, functions, etc.\n\n### Text folding\nYou can hide and show code regions like in a tree view control: click the **[-]** or **[+]** in the left margin. Folding is available for functions, events, types, multiline comments, disabled code (`#if`), `#region` ... `#endregion`, `//.` ... `//..`, namespaces, local functions, lambda, initializer lists and strings.\n\n`Ctrl`+click to show/hide descendant folds as well. `Shift`+click to show descendants. For more options right-click the folding margin.\n\n### Separators between functions/types\nEditor draws horizontal lines at the end of each function and type definition.\n\n### Snippets\nThe autocompletion list also contains [snippets](xref:snippets). For example the `outSnippet` inserts code `print.it();` when you type `out` and space or `Tab` or `Enter` or click it. Some snippets are in the **Surround** menu.\n\n### Images in code\nIf a code line contains a string or comment with a file path, embedded image (screenshot, `\"image:\"`) or `\"*icon\"`, editor draws the image at the left. Also captures screenshots when recording etc. Embedded image data usually is hidden.\n\nThis feature can be enabled/disabled with the toolbar button.\n\n### Format code\nThe **Format document/selection** commands insert/remove spaces, indentation and newlines to make code uniformly formatted. See also **Options > Code editor > Formatting**.\n\n### Comment/uncomment/indent/unindent lines\nTo disable or enable a line of code by converting it to/from comments, you can use the toolbar button or **Edit** menu or right-click the selection margin. If multiple lines are selected, it converts all. If not full line(s) selected, the button uses a `/*block comment*/`.\n\nPress `Tab` or `Shift+Tab` to indent or unindent all selected lines. It adds or removes one tab character before each line.\n\n### Capture UI elements, insert regex etc, implement interface\nThe **Code** and **Edit** menus contain tools for creating code to find a window, UI element or image, for inserting parts of regular expression or keys string, generating interface/abstract implementation methods, and more.\n\n### Find Windows API and insert declarations\nThe app comes with a database of Windows API declarations, and helps to find and insert them. More info: menu **Code > Windows API**, click **[?]**.\n\n### Drag and drop files to insert path\nYou can drag and drop files from File Explorer etc to the code editor. It inserts code with file path. Links too.\n\nYou can also drag and drop scripts etc from the Files panel.\n\n### Focus\nTo focus the code editor control without changing selection: middle-click.\n\n### WPF window preview\nThe app does not have a dialog window editor/designer, but it's easy to create windows in code, using class [wpfBuilder](). The app can automatically show/update the window while you edit its code, if it contains code like this:\n\n```csharp\n#if WPF_PREVIEW\nb.Window.Preview();\n#endif\n```\n\nThis code must be after building the window but before showing it ([wpfBuilder.ShowDialog]() etc).\n\nTo activate this feature for current document, check toolbar button **WPF preview** or menu **Edit > View > WPF preview**.\n\nHow it works: If **WPF preview** is checked and current script contains `#if WPF_PREVIEW`, the app launches/restarts the script whenever you make changes in its code, unless there are errors. The script runs like when clicked the **Run** button, with these changes:\n- Defined `WPF_PREVIEW`. In script you use `#if WPF_PREVIEW` to include preview-specific code. Or use [script.isWpfPreview]().\n- Function **Preview** shows the window without activating. Also changes some its properties. Ends the process when the window closed.\n- Some `/*/ properties /*/` are ignored: `role`, `ifRunning`, `uac`, `platform`, `console`, `optimize`, `outputPath`, `preBuild`, `postBuild`, `xmlDoc`.\n- If current file (or its main project file) is a class file, runs it as a script; ignores the test script. Therefore need `#if WPF_PREVIEW` code that runs at startup and calls the function that contains the window code.\n- Function [script.setup]() does nothing.\n\nIn preview mode the script must go straight to the window code. If normally it doesn't, add code like this somewhere at the start:\n```csharp\n#if WPF_PREVIEW\nFunctionContainingWindowCode();\n#endif\n```\n\nIf it's a dialog class, add code like this:\n```csharp\n#if WPF_PREVIEW\nnew DialogClass().Preview();\n#endif\n```\n\nor this:\n```csharp\n#if WPF_PREVIEW\nclass Program { static void Main() { new DialogClass().Preview(); }}\n#endif\n```\n\nIn preview mode the script must not activate or move the window. Should skip slow/expensive operations that aren't necessary for preview. Should not show the tray icon.\n"
  },
  {
    "path": "Other/DocFX/_doc/editor/Command line.md",
    "content": "---\nuid: command_line\n---\n\n# Command line of `Au.Editor.exe`\n\n- `/v` - show the main window when started, regardless of app settings.\n- `/a` - indicates that the app started automatically and therefore must ignore **Options > App > Visible if not auto-started**. Used by **Options > App > Start with Windows**.\n- `/reload` - if the app is running, reload current workspace.\n- `/n` (the first argument) - don't restart as administrator when started not as administrator. See [UAC](xref:uac).\n- `\"script name or relative path in current workspace\"` - run the script. Can be followed by script's command line arguments (the *args* variable).\n- `\"full path of a file or folder\"` - import it into the current workspace (shows a dialog). Can be multiple files, like `\"file1\" \"file2\" \"file3\"`.\n- `\"workspace folder path\"` - open or import the workspace (shows a dialog).\n\nWith the last 3 cannot be used other arguments except **/n**.\n\n### About \"run script\"\nThe started `Au.Editor.exe` process is an intermediate temporary process, not the regular editor process with UI. It just relays the command line to the regular process, waits if need, and exits. Also it starts the regular process if not running. The regular process compiles and starts the script.\n\nUse prefix `*` to wait until the script ends. While waiting, the parent process can read script's [script.writeResult]() text from standard output of the child process. If parent process is console, it automatically displays the text. Or it can redirect standard output, like [run.console]() does.\n\nFor Task Scheduler tasks the **Schedule** tool sets prefix `>`. Then Task Scheduler knows when the script process is running and can end it when need.\n\nThe exit code of this process when it waits is the script's exit code. The script can simply return it, like `return 1;` or call `Environment.Exit`. When does not wait, the exit code is the process id of the script. When fails to run (script not found, contains errors, etc) or wait, the exit code is < 0 (can be -1 to -7).\n\n### Examples\n\n- `Au.Editor.exe Script5.cs`\n- `Au.Editor.exe \"Script name with spaces.cs\"`\n- `Au.Editor.exe Script5.cs /example \"argument with spaces\"`\n- `Au.Editor.exe *Script5.cs`\n- `Au.Editor.exe /v`\n"
  },
  {
    "path": "Other/DocFX/_doc/editor/Compared with QM.md",
    "content": "---\nuid: qm2\n---\n\n# Compared with Quick Macros\nLibreAutomate is like a new major version of [Quick Macros](https://www.quickmacros.com) (QM). It has most of QM features. But it is very different.\n\nThe script language now is C#. It is one of most popular programming languages.\n\nLibreAutomate cannot execute or convert QM scripts. If you have many QM scripts, probably not worth to convert them. Let both programs run at the same time.\n\nLibreAutomate is free and open-source. Its open-source automation library can be used in other programs too.\n\nLibreAutomate can run on Windows 7-11 with .NET runtime. More info [here](xref:index).\n\nQM (Quick Macros) will not have more features in the future. Only bug fixes. But it will be available and supported for long time.\n\nLet's compare QM and LibreAutomate code.\n\nA script in QM:\n```csharp\nint w=win(\"- Mozilla Firefox\")\nact w ;;comment\nlef 331 115 w 1\n2\nkey L \"text\" Y\nint i\nfor i 0 5\n\tout i\n\tout i*i\n```\n\nThe same script in LibreAutomate:\n```csharp\nvar w = wnd.find(\"*- Mozilla Firefox\");\nw.Activate(); //comment\nmouse.click(w, 331, 115);\n2.s();\nkeys.send(\"Left\", \"!text\", \"Enter\");\nfor(int i=0; i<5; i++) {\n\tprint.it(i);\n\tprint.it(i*i);\n}\n```\n\nAs you see, C# code is longer, but usually it is easier to understand. The [code editor](xref:code_editor) can add `; () { }` automatically as you type, and has other code editing features that are much better than in QM.\n\nLibreAutomate has triggers to execute parts of a running script. Trigger types: hotkey, autotext, mouse, window, process, filesystem. Triggers also can be used to launch scripts, but differently than in QM.\n\nLibreAutomate does not have item types like \"menu\", \"toolbar\" and \"autotext\". Instead use classes [popupMenu](), [toolbar]() and [AutotextTriggers]().\n\nTo create dialogs now can be used class [wpfBuilder]() and snippet `wpfDialogSnippet`.\n\nCurrently LibreAutomate has only the most important tools for creating code.\n\nIn LibreAutomate each script is a separate .cs file.\n\nIn LibreAutomate each script runs in a separate process.\n\nIn LibreAutomate each script can use only namespaces it wants to use. Namespaces contain classes; classes contain functions and fields (variables). In QM all scripts share all classes, global functions and global variables.\n\nLibreAutomate can create .NET class libraries (dll files).\n\nIn the main LibreAutomate window you can resize and dock all panels and toolbars where you want, or make floating.\n\nLibreAutomate saves all settings in files, not in the Registry.\n\n### Some important features missing\n- Dialog editor. Instead use class [wpfBuilder]().\n\n### Some features LibreAutomate will never have\n- Encrypt scripts.\n- Unlock computer.\n"
  },
  {
    "path": "Other/DocFX/_doc/editor/Creating exe programs.md",
    "content": "---\nuid: publish\n---\n\n# Creating exe programs\nTwo ways of creating programs that can run without LibreAutomate:\n- Script role `exeProgram`.\n- Menu **Run > Publish**.\n\n## Script role exeProgram\nIn **Properties** set role `exeProgram`. When compiling the script, LibreAutomate creates/copies program files (exe, dll) to the output directory and prints a link to it. To compile, click the **Compile** button or run the script.\n\nThe program consists of several files. To add them to a single executable file you can create an installer, for example with Inno Setup.\n\nThe program uses .NET Runtime of version displayed in menu **Help > About**. On computers without .NET it can't run; it prompts to download .NET.\n\nIf `optimize true` (checked **optimize** in **Properties**), the program is created with optimized code (fast and small), aka \"Release\".\n\nCreates program files for the selected platform. If `optimize true` and not `platform x86`, also creates program files for other 64-bit platform. If `platform x86`, always creates only x86 files. More info below in the **Common info** section.\n\n## Menu Run > Publish\nThe **Publish** tool can create single-file programs and programs that can run on computers without installed .NET.\n\nIt creates a temporary .csproj file and executes [dotnet publish](https://www.google.com/search?q=dotnet+publish), which uses .NET SDK to compile the script.\n\nOptions:\n- **Single file** - create single-file program. Adds all program files to single exe file.\n- **Single file > Self extract** - read below.\n- **Add .NET Runtime** - add .NET Runtime files too. Then the program can run on computers without installed .NET. If unchecked, on computers without .NET the program just prompts to install .NET. A single-file exe is compressed, ~70 MB.\n- **ReadyToRun** - compile to native code.\n- **Platform** - CPU instruction set. You can publish for multiple platforms (run the publish tool several times). Files of each platform are created in a separate folder. More info below in the **Common info** section.\n\nAlways compiles the program and its `/*/ pr /*/` libraries like with `/*/ optimize true; /*/` (aka \"Release\").\n\nRole can be `exeProgram` or `miniProgram`.\n\nUnsupported features:\n- `/*/ icon folder /*/` (multiple native icons).\n- `/*/ testInternal, noRef /*/`.\n\n### Self-extract\n\nThis 3-state checkbox is enabled when **Single file** is checked.\n- Checked - use `IncludeAllContentForSelfExtract`. Adds all files to exe. Will extract all (including .NET dlls) to a temporary directory. The program will start slower, but will not have issues like unavailable [Assembly.Location](ms:).\n- Unchecked - adds only .NET dlls to exe. Native dlls and other files (if any) will live in the exe's directory. Will use .NET dlls without extracting.\n- Indeterminate - use `IncludeNativeLibrariesForSelfExtract`. Adds all dlls to exe. Will extract native dlls, and use .NET dlls without extracting. This is the best for most scripts, but can't be used with some scripts (the **Publish** tool will print a warning). \n\n[More info](https://www.google.com/search?q=dotnet+publish+single-file+IncludeAllContentForSelfExtract+IncludeNativeLibrariesForSelfExtract)\n\n## Common info\n\nWhere the program can run, depends on the selected platform:\n- x64 - runs on all modern Windows computers (x64, Windows11+ ARM64).\n- arm64 - runs only on Windows ARM64. Used because x64 and x86 programs are slow there.\n- x86 - runs on almost all Windows computers (x64, x86, all ARM64), as 32-bit process.\n\nThese script properties are not applied to exe programs launched not from LibreAutomate:\n- `ifRunning`. Multiple instances (processes) of the program can run simultaneously. To prevent it can be used [script.single]().\n- `uac`. By default programs run not as administrator (unlike when launched from admin LibreAutomate) and therefore can't automate admin windows etc. See [UAC](xref:uac).\n\nProgram files don't contain the source code, but can be decompiled into equivalent source code.\n\n[print.it]() text is displayed in LibreAutomate if it is running, unless it's a console program. See also [PrintServer]().\n\nIf you want to use action triggers (hotkeys etc) in exe program, add them to the script like in the [ActionTriggers]() example.\n\nTo get program/OS/computer info can be used classes [script](), [process](), [uacInfo](), [osVersion](), [folders](), [Environment](ms:), [RuntimeInformation](ms:), [OperatingSystem](ms:).\n\nAntivirus programs and OS may block or block-scan-restart unknown (new) program files. To avoid it on the development computer, in the AV program add the output directory to the list of exclusions. To avoid it anywhere, need to sign program files with a code signing certificate, and it must have a good reputation; it isn't cheap and isn't easy to get.\n\n## See also\n- [Scripts](xref:script)\n- [File properties](xref:file_properties)\n\n\n"
  },
  {
    "path": "Other/DocFX/_doc/editor/Debugger.md",
    "content": "---\nuid: debugger\n---\n\n# Debugging C# scripts\n## Useful functions\nSee cookbook article [Script testing and debugging](/cookbook/Script+testing+and+debugging.html).\n\n## Debugger\nDebugger in LibreAutomate is similar to that of [Visual Studio](https://www.google.com/search?q=Visual+Studio+debugger), [VSCode](https://www.google.com/search?q=VSCode+debugger), [Rider](https://www.google.com/search?q=Rider+debugger) etc.\n\nTwo panels are for debugging: **Debug** and **Breakpoints**. If the **Debug** panel was hidden when debugging starts, it becomes visible temporarily while debugging. Both panels initially are hidden; to show always, right-click any panel header.\n\nTo run current script with the debugger, click the **Debug run** button or menu item, or use the toolbar in the **Debug** panel. Usually at first you add one or more breakpoints in code (click the white margin). The script stops at a breakpoint line, or on exception, or in [Debugger.Break](ms:), [Debug.Assert](ms:), or when clicked **Pause**. Then you can use the **Debug** toolbar or hotkeys to execute the script in steps. Also you can click the white margin and select **Run to here**.\n\nTo attach the debugger to an already running script, use the **Tasks** panel or [script.debug](). Or [Debug.Assert](ms:), if the script starts with code like `script.setup(debug: true);`.\n\n### Features\n- Standard stepping features.\n- Breakpoints, logpoints.\n- Break on exception.\n- Call stack.\n- List of variables. Click a variable to print it.\n- Hover the mouse on a variable in code to display its value.\n- Supports [Debugger.Break](ms:), [Debug.Assert](ms:), [Debug.Print](ms:) etc.\n- Run to here.\n- Jump to here.\n\n### Some missing features\n- \"Hot reload\" or \"Edit and continue\".\n- Evaluate any C# expression.\n- Set variable's value.\n- Open and debug source files that don't exist in the workspace.\n- Attach the debugger to unknown processes.\n\n### Notes\nThanks to [Samsung/netcoredbg](https://github.com/Samsung/netcoredbg).\n\nAntivirus software may quarantine `netcoredbg.exe` (debugger). Add it to the exclusions list of the antivirus.\n\nTo debug optimized code (`/*/ optimize true; /*/`), temporarily check **Debug optimized code** in the options menu. Normally you don't debug optimized code, it may not work well.\n\nCannot debug 32-bit processes.\n\n## Other debuggers\nYou may have Visual Studio or other IDE with a better .NET debugger. You can attach that debugger to a running script. Let the script call one of these functions:\n- [Debugger.Launch](ms:). Example: `Debugger.Launch(); Debugger.Break();`. Note: it shows several dialogs before you can start debugging.\n- [script.debug](). Example: `script.debug(true); Debugger.Break();`. It shows a dialog with the script process id and waits for a debugger attached. Then you can attach a debugger. To automate it, create a window-triggered script.\n\n```csharp\nTriggers.Window[TWEvent.ActiveNew, \"Attach debugger\", \"#32770\"] = o => script.run(\"Attach debugger.cs\", o.Window.ProcessId.ToS());\n```\n\nScript `Attach debugger.cs`:\n```csharp\n// Attaches the Visual Studio debugger to the process id = args[0].\n// If run from editor (args empty), attaches to this process for testing this script.\n\nbool test = script.testing; //test this script\nif (test) print.clear();\nint id = test ? process.thisProcessId : args[0].ToInt();\n\nbool canAttach = false;\nvar w = wnd.find(\"*Visual Studio*\", \"HwndWrapper[DefaultDomain;*\");\nif (w.Is0) {\n\tdialog.showInfo(\"Debugger window not found\", \"Supported debuggers: Visual Studio, VSCode, dnSpy.\");\n} else if (uacInfo.ofProcess(id).Elevation == UacElevation.Full && uacInfo.ofProcess(w.ProcessId).Elevation != UacElevation.Full) {\n\tdialog.showInfo(\"Debugger isn't admin\", \"The debugger process must be running as administrator.\");\n} else canAttach = true;\nif (!canAttach) {\n\tif (!test) process.terminate(id); //because it's waiting in script.debug\n\treturn;\n}\n\nw.Activate();\n600.ms();\nkeys.send(\"Ctrl+Alt+P\");\nvar w2 = wnd.find(5, \"Attach to Process\", \"**m HwndWrapper[DefaultDomain;*||#32770\");\n300.ms();\nif (w2.ClassNameIs(\"H*\")) { //new VS\n\tw2.Elm[\"STATICTEXT\", id.ToS()].Find(5).Parent.Focus(true);\n} else { //old VS\n\tw2.Elm[\"LISTITEM\", null, [\"id=4102\", $\"desc=ID: {id}? *\"]].Find(5).Focus(true); //note: use '?' because can be ',' or ';' etc depending on regional settings\n}\nw2.Elm[\"BUTTON\", \"Attach\", \"state=!DISABLED\"].Find(3).Invoke();\n\nif (test) {\n\twait.until(0, () => Debugger.IsAttached);\n\tDebugger.Break();\n\tprint.it(\"Debugger script.\");\n}\n```\n"
  },
  {
    "path": "Other/DocFX/_doc/editor/File properties.md",
    "content": "---\nuid: file_properties\n---\n\n# C# file properties\n\nC# file properties in LibreAutomate are similar to C# project properties in Visual Studio.\nMost properties are stored in `/*/ meta comments /*/` at the start of code. Can be edited in the **Properties** window or in code.\n\nMeta comments syntax:\n- The block of meta comments starts and ends with `/*/`. Only the first such block is used.\n- The block of meta comments must be at the start. Can be preceded by comments and empty lines.\n- Property name and value are separated by space.\n- Multiple properties separated by `;` or/and newlines.\n- Property names must match case.\n- Property values are not enclosed in quotes etc.\n- There are no escape sequences.\n\nExample - single line:\n```csharp\n/*/ role exeProgram; outputPath %folders.Workspace%\\exe\\Script1; /*/\n```\n\nExample - multiple lines:\n```csharp\n/*/\nrole exeProgram\noutputPath %folders.Workspace%\\exe\\Script1\n/*/\n```\n\nThe compilation may contain multiple C# files (files in the project folder and files added using meta comment `c`). Properties are applied to the entire compilation, not only to the file where specified. Most properties can be specified only in the main file of the compilation (error if not); others can be anywhere (`c`, `r`, `pr`, `nuget`, `com`, `resource`, `file`). Most properties can be specified once (error if not); others can be multiple (`c`, `r`, `pr`, `nuget`, `com`, `resource`, `file`, `noRef`). Some properties are available only for some roles.\n\nTerms used this article:\n- *property* and *meta comment* are synonyms.\n- *editor* and *LibreAutomate* are synonyms.\n- *script file* - a C# file that can be executed directly (like a program).\n- *class file* - a C# file that contains classes/functions that can be used in other files.\n- *project folder* or *project* - folder named like `@Example` (starts with `@`). The first C# file (*main file*) and all class files are automatically added to the compilation.\n- *compilation* - one or more C# files that are compiled together to create a program or library.\n\nProperties in this article are grouped like in the **Properties** window.\n\n## Table of contents\n\n  - [Role (group of properties)](#role-group-of-properties)\n    - [`role`](#role) - `miniProgram`, `exeProgram`, `editorExtension`, `classFile` or `classLibrary`.\n  - [Run (group of properties)](#run-group-of-properties)\n    - [`testScript`](#testscript) - a script to run to test this not-directly-executable class file.\n    - [`ifRunning`](#ifrunning) - whether/how to launch this script if it's already running.\n    - [`uac`](#uac) - run this script as admin or not.\n  - [Compile (group of properties)](#compile-group-of-properties)\n    - [`optimize`](#optimize) - release/debug configuration.\n    - [`define`](#define) - preprocessor symbols.\n    - [`warningLevel`](#warninglevel) - disable groups of warnings.\n    - [`noWarnings`](#nowarnings) - disable specified warnings.\n    - [`nullable`](#nullable) - C# nullable context.\n    - [`testInternal`](#testinternal) - use internal members of specified assemblies.\n    - [`preBuild`](#prebuild) - a script to run before compiling.\n    - [`postBuild`](#postbuild) - a script to run after compiling.\n  - [Assembly (group of properties)](#assembly-group-of-properties)\n    - [`outputPath`](#outputpath) - output directory of compiled exe/dll files.\n    - [`icon`](#icon) - icon of compiled exe file.\n    - [`manifest`](#manifest) - manifest of compiled exe file.\n    - [`sign`](#sign) - strong-name signing file.\n    - [`console`](#console) - create console app.\n    - [`platform`](#platform) - platform of compiled exe file (`x64`, `arm64` or `x86`).\n    - [`xmlDoc`](#xmldoc) - whether to create an XML documentation file when compiling this library.\n  - [Add reference (group of properties)](#add-reference-group-of-properties)\n    - [`r` (button **Library**)](#r-button-library) - .NET assembly reference.\n    - [`nuget` (button **NuGet**)](#nuget-button-nuget) - NuGet package reference.\n    - [`com` (buttons **COM** and **...**)](#com-buttons-com-and-) - COM interop assembly reference.\n    - [`pr` (button **Project**)](#pr-button-project) - library project reference.\n  - [Add file (group of properties)](#add-file-group-of-properties)\n    - [`c` (button **Class file**)](#c-button-class-file) - add a C# file to the compilation.\n    - [`resource` (button **Resource**)](#resource-button-resource) - add a resource from a file.\n    - [`file` (button **Other file**)](#file-button-other-file) - make a file available at run time.\n  - [Rarely used properties](#rarely-used-properties)\n    - [`miscFlags`](#miscflags) - miscellaneous rarely used flags.\n    - [`noRef`](#noref) - remove a default reference assembly.\n  - [Version info](#version-info) - how to add version info.\n\n## Role (group of properties)\n\n### `role`\nThe purpose of this C# code file. What kind of assembly to create and how to execute.\n- `miniProgram` - execute in a separate host process started from editor.\n- `exeProgram` - create/execute exe file, which can run on any computer, without editor installed.\n- `editorExtension` - execute in the editor's UI thread. Rarely used. Incorrect code can kill editor.\n- `classLibrary` - create dll file, which can be used in C# scripts and other .NET-based programs.\n- `classFile` - don't create/execute. The C# code file can be compiled together with other C# code files in the project or using meta comment `c`. Inherits meta comments of the main file of the compilation.\n\nDefault role for scripts is `miniProgram`; cannot be the last two. Default for class files is `classFile`; can be any.\n\nSee also: [Scripts](xref:script), [Class files](xref:class_project), [Shared classes and functions, libraries](/cookbook/Shared classes and functions, libraries.html)\n\n## Run (group of properties)\n\nLibreAutomate uses these properties when launching the script task.\n\n### `testScript`\nA script to run when you click the **Run** button.\n\nThis property is for testing class files with role `classFile` or `classLibrary`. Such files cannot be executed directly.\n\nThe test script can contain meta comment `/*/ c this file.cs; /*/` that adds this file to the compilation, or `/*/ pr this file.cs; /*/` that adds the output dll file as a reference assembly. An easy way to add this property correctly is to try to run this file and click a link that is then printed in error text in the output.\n\nThe value can be:\n- Path in the workspace. Examples: `\\Script5.cs`, `\\Folder\\Script5.cs`.\n- Path relative to this file. Examples: `Folder\\Script5.cs`, `.\\Script5.cs`, `..\\Folder\\Script5.cs`.\n- Filename. Error if multiple exist, unless single exists in the same folder.\n\nThis property is saved in `files.xml`, not in meta comments.\n\n### `ifRunning`\nWhen trying to start this script, what to do if it is already running.\n- `warn` - print warning and don't run.\n- `cancel` - don't run.\n- `wait` - run later, when it ends.\n- `run` - run simultaneously.\n- `restart` - end it and run.\n- `end` - end it and don't run.\n\nSuffix `_restart` (`warn_restart`, `cancel_restart`, `wait_restart`, `run_restart`, `end_restart`) changes the behavior: when using the **Run** button/menu to start the script, use `restart`; else use the value as without the suffix.\n\nDefault is `warn_restart`.\n\nThis property is ignored when the script runs as exe program started not from editor. Then it's an ordinary program (works like with `ifRunning run`). To allow single instance you can use [script.single](), like `script.single(\"unique string\");`.\n\n### `uac`\n[UAC](xref:uac) integrity level (IL) of the task process.\n- `inherit` (default) - the same as of the editor process (High IL recommended).\n- `user` - Medium IL, like most applications. The task cannot automate high IL process windows, write some files, change some settings, etc.\n- `admin` - High IL, aka \"administrator\", \"elevated\". The task has many rights, but cannot automate some apps through COM, etc.\n\nThis property is ignored when the script runs as exe program started not from editor. Then it's an ordinary program.\n\n## Compile (group of properties)\n\nLibreAutomate uses these properties when compiling the script or library.\n\n### `optimize`\nWhether to make the compiled code as fast as possible.\n- `false` (default) - don't optimize. Define preprocessor symbols `DEBUG` and `TRACE` that can be used with `#if`. Aka \"Debug configuration\".\n- `true` (checked) - optimize. Aka \"Release configuration\".\n\nDefault is `false`, because optimization makes difficult to debug. Optimization makes noticeably faster only some types of code, for example processing of large/many arrays. Before deploying class libraries and exe programs you usually compile with `optimize true`.\n\n### `define`\nSymbols that can be used with `#if`. Example: `ONE,TWO,d:THREE,r:FOUR`.\n\nA symbol here can have a prefix:\n- `r:` - define the symbol only if `optimize true` (checked).\n- `d:` - define the symbol only if no `optimize true`.\n\nIf no `optimize true`, symbols `DEBUG` and `TRACE` are added implicitly.\n\nSee also [C# #define](ms:).\n\n### `warningLevel`\n[warning level](ms:C#+Compiler+Options,+WarningLevel). Default 8.\n- 0 - no warnings.\n- 1 - only severe warnings.\n- 2 - level 1 plus some less-severe warnings.\n- 3 - most warnings.\n- 4 - all warnings of C# 1-8.\n- 5-9999 - level 4 plus warnings added in C# 9+.\n\n### `noWarnings`\nDon't show these warnings. Example: `151,3001,CS1234`.\n\nSee also [#pragma warning](ms:C#+#pragma+warning).\n\n### `nullable`\n[nullable context](ms:C#+Nullable+reference+types).\n- `disable` - no warnings; code does not use nullable syntax (`Type? variable`).\n- `enable` - print warnings; code uses nullable syntax.\n- `warnings` - print warnings; code does not use nullable syntax.\n- `annotations` - no warnings; code uses nullable syntax.\n\nAlternatively use `#nullable` directive.\n\n### `testInternal`\nCan use internal symbols of these assemblies, like with `InternalsVisibleToAttribute`.\nExample: `Assembly1,Assembly2`.\n\n### `preBuild`\nA script to run before compiling.\n\nThe script must have role `editorExtension`. It runs in the editor's main thread.  \nTo get compilation info: `var c = PrePostBuild.Info;`  \nTo specify command line arguments: `/*/ preBuild Script5.cs /arguments; /*/`  \nTo stop the compilation, let the script throw an exception.  \nTo create new preBuild/postBuild script: menu **File > New > More**.\n\nThe value can be:\n- Path in the workspace. Examples: `\\Script5.cs`, `\\Folder\\Script5.cs`.\n- Path relative to this file. Examples: `Folder\\Script5.cs`, `.\\Script5.cs`, `..\\Folder\\Script5.cs`.\n- Filename. Error if multiple exist, unless single exists in the same folder.\n\n### `postBuild`\nA script to run after successfully compiling and creating output files.\n\nEverything is like with `preBuild`.\n\n## Assembly (group of properties)\n\nLibreAutomate uses these properties when creating program files after compiling the script/library code.\n\n### `outputPath`\nDirectory for the output assembly file and related files (used dlls, etc).\n\nFull path. Can start with `%environmentVariable%` or `%folders.SomeFolder%` (see class [folders]()). Can be path relative to this file or workspace, like with other properties.\n\nDefault if role `exeProgram`: `%folders.Workspace%\\exe\\filename`. Default if role `classLibrary`: `%folders.Workspace%\\dll`. The compiler creates the folder if does not exist.\n\n### `icon`\nIcon of the output exe file.\n\nThe icon will be added as a native resource and displayed in File Explorer etc. Native resources can be used with [icon.ofThisApp]() etc and [dialog]() class functions.\n\nThe file must be in this workspace. Import files if need (eg drag-drop). Can be a link.\nThe value can be:\n- Path in the workspace. Examples: `\\App.ico`, `\\Folder\\App.ico`.\n- Path relative to this file. Examples: `Folder\\App.ico`, `.\\App.ico`, `..\\Folder\\App.ico`.\n- Filename. Error if multiple exist, unless single exists in the same folder.\n\nIf role `exeProgram`, you can specify a folder instead. Will add all `.ico` and `.xaml` icons from it. Resource ids start from `IDI_APPLICATION` (32512).\n\nAnother way to set exe icon - assign a custom icon to the main C# file. See menu **Tools > Icons**.\n\n### `manifest`\n[manifest file](ms:) of the output exe file.\n\nThe file must be in this workspace. Import files if need (eg drag-drop). Can be a link.\nThe value can be:\n- Path in the workspace. Examples: `\\App.manifest`, `\\Folder\\App.manifest`.\n- Path relative to this file. Examples: `Folder\\App.manifest`, `.\\App.manifest`, `..\\Folder\\App.manifest`.\n- Filename. Error if multiple exist, unless single exists in the same folder.\n\nThe manifest will be added as a native resource.\n\n### `sign`\nStrong-name signing key file, to sign the output assembly.\n\nThe file must be in this workspace. Import files if need (eg drag-drop). Can be a link.\nThe value can be:\n- Path in the workspace. Examples: `\\App.snk`, `\\Folder\\App.snk`.\n- Path relative to this file. Examples: `Folder\\App.snk`, `.\\App.snk`, `..\\Folder\\App.snk`.\n- Filename. Error if multiple exist, unless single exists in the same folder.\n\n### `console`\nLet the program run with console.\n\n### `platform`\nCPU instruction set.\n- `x64` - runs on all modern Windows computers (x64, Windows11+ ARM64).\n- `arm64` - runs only on Windows ARM64. Used because x64 and x86 programs are slow there.\n- `x86` - runs on almost all Windows computers (x64, x86, all ARM64), as 32-bit process.\n\nDefault - as LibreAutomate and the Windows OS on this computer (x64 or arm64).\n\nCreates program files for this platform. If `optimize true` and `platform` not `x86`, creates for both x64 and arm64. In any case, the process uses this platform when launched from editor.\n\nMost .NET dlls can be used by programs of any platform. But native dlls can be used only in programs of the same platform as of the dll. Usually libraries that use native dlls have dll files for multiple platforms. If a dll for some platform is missing, you can't use that platform for your script/program that will use that library. Workaround: use role `exeProgram` and platform of an available dll.\n\n### `xmlDoc`\nCreate XML documentation file from `///` comments. And print errors in `///` comments when compiling.\n\nXML documentation files are used by code editors to display class/function/parameter info. Also can be used to create HTML documentation.\n\n## Add reference (group of properties)\n\nThis is a group of buttons in the **Properties** window. The buttons add meta comments for adding library references to the compilation.\n\n### `r` (button **Library**)\nAdd a .NET assembly reference.\nMeta comment `/*/ r File.dll; /*/`.\n\nDon't need to add `Au.dll` and .NET runtime dlls.  \nTo remove this meta comment, edit the code in the code editor.  \nIf script role is `editorExtension`, may need to restart editor.\n\nIn code you can add some properties, like `/*/ r DllFile /property1|property2; /*/`:\n- To not copy the dll to the output directory: `noCopy`\n- To use `extern alias Abc;`: `alias=Abc`\n\n### `nuget` (button **NuGet**)\nUse a NuGet package reference (see menu **Tools > NuGet**).\nMeta comment `/*/ nuget Folder\\Package; /*/`.\n\nTo remove this meta comment, edit the code in the code editor.  \nTo use `extern alias Abc;`, edit the code: `nuget Folder\\Package /alias=Abc`\n\n### `com` (buttons **COM** and **...**)\nUse a COM interop assembly.\nMeta comment `/*/ com File.dll; /*/`.\n\nIn the **Properties** window select a COM component. It immediately converts the type library to a COM interop assembly and saves the assembly file in `%folders.Workspace%\\.interop`.\n\nAn interop assembly is a .NET assembly without real code. Not used at run time. At run time is used the COM component (registered unmanaged dll or exe file). If a COM dll for current platform unavailable, try to set role `exeProgram` and change platform.\n\nTo remove this meta comment, edit the code. Optionally delete unused interop assemblies.  \nTo use `extern alias Abc;`, edit the code: `/*/ com File.dll /alias=Abc; /*/`\n\n### `pr` (button **Project**)\nAdd a reference to a class library created in this workspace.\nMeta comment `/*/ pr File.cs; /*/`.\n\nThe compiler will compile it if need and use the created dll file as a reference.\n\nTo remove this meta comment, edit the code. Optionally delete unused dll files.\n\n## Add file (group of properties)\n\nThis is a group of buttons in the **Properties** window. The buttons add meta comments for adding various files to the compilation.\n\n### `c` (button **Class file**)\nAdd a C# code file that contains some classes/functions used by this file.\nMeta comment `/*/ c File.cs; /*/`.\n\nThe compiler will compile all code files and create single assembly.\n\nThe file must be in this workspace. Import files if need (eg drag-drop). Can be a link.  \nIf folder, will compile all its descendant class files.  \nThe value can be:\n- Path in the workspace. Examples: `\\Class5.cs`, `\\Folder\\Class5.cs`.\n- Path relative to this file. Examples: `Folder\\Class5.cs`, `.\\Class5.cs`, `..\\Folder\\Class5.cs`.\n- Filename. Error if multiple exist, unless single exists in the same folder.\n\nDon't use this meta comment to add class files that are in the same project folder. They are added automatically.\n\nTo remove this meta comment, edit the code.\n\n### `resource` (button **Resource**)\nAdd image etc file(s) as managed resources.\nMeta comment `/*/ resource File; /*/`.\n\nDefault resource type is `Stream`. You can append `/byte[]` or `/string`, like `/*/ resource file.txt /string; /*/`. Or `/strings`, to add multiple strings from a 2-column CSV file (name, value). Or `/embedded`, to add as a separate top-level stream that can be loaded with [Assembly.GetManifestResourceStream](ms:) (others are in top-level stream `AssemblyName.g.resources`).\n\nThe file must be in this workspace. Import files if need (eg drag-drop). Can be a link.  \nIf folder, will add all its descendant files.  \nThe value can be:\n- Path in the workspace. Examples: `\\File.png`, `\\Folder\\File.png`.  \n- Path relative to this file. Examples: `Folder\\File.png`, `.\\File.png`, `..\\Folder\\File.png`.  \n- Filename. Error if multiple exist, unless single exists in the same folder.\n\nTo remove this meta comment, edit the code.\n\nTo load resources at run time can be used class [ResourceUtil](), like `var s = ResourceUtil.GetString(\"file.txt\");`. Or [ResourceManager](ms:). To load WPF resources can be used `\"pack:...\"` URI; if role `miniProgram`, assembly name is like `*ScriptName`.\n\nResource names in assembly by default are like `file.png`. When adding a folder with subfolders, may be path relative to that folder, like `subfolder/file.png`. If need path relative to this C# file, append space and `/path`. Resource names are lowercase, except `/embedded` and `/strings`. LibreAutomate does not URL-encode resource names; WPF `pack:...` URI does not work if resource name contains spaces, non-ASCII or other URL-unsafe characters. Also LibreAutomate does not convert XAML to BAML.\n\nTo browse .NET assembly resources, types, etc can be used for example [ILSpy](google:).\n\n### `file` (button **Other file**)\nMake a file available at run time.\nMeta comment `/*/ file File; /*/`.\n\nIt can be any file, for example an unmanaged dll.\n\nThe compiler behavior depends on the role of the main file of the compilation:\n- `exeProgram` - copy the file to the output folder. Or subfolder: `/*/ file file.dll /sub; /*/`.\n- `miniProgram` or `editorExtension` - store the dll path in the assembly in order to find it at run time.\n- `classLibrary` - the above actions are used when compiling scripts that use it as a project reference. The compiler does not copy these files to the output folder of the library.\n\nThe file must be in this workspace. Import files if need (eg drag-drop). Can be a link.  \nThe value can be:\n- Path in the workspace. Examples: `\\File.png`, `\\Folder\\File.png`.\n- Path relative to this file. Examples: `Folder\\File.png`, `.\\File.png`, `..\\Folder\\File.png`.\n- Filename. Error if multiple exist, unless single exists in the same folder.\n\nIf folder, will include all its descendant files. Will copy them into folders like in the workspace. If a folder name ends with `-`, will copy its contents only (will not create the folder).\n\nIf an `exeProgram` script uses unmanaged dll files, consider placing them in subfolders named `64`, `64\\ARM` and `32`. Then at run time will be loaded the dll for that platform.\n\nTo remove this meta comment, edit the code.\n\n## Rarely used properties\n\n### `miscFlags`\nMiscellaneous flags. May be set by some controls of the **Properties** window.\n\n- 1 - don't add XAML icons from code strings like `\"*name color\"` to exe resources.\n\n### `noRef`\nRemove a default reference assembly (.NET or `Au.dll`).\nMeta comment: `/*/ noRef AssemblyNameOrAuPathWildex; /*/`.\n\nCan be specified only in code, not in the **Properties** window.\n\n## **Version info**\nTo add version info to your exe program, insert and edit this code near the start of any C# file of the compilation.\n\n```\nusing System.Reflection;\n\n[assembly: AssemblyVersion(\"1.0.0.0\")]\n//[assembly: AssemblyFileVersion(\"1.0.0.0\")] //if missing, uses AssemblyVersion\n//[assembly: AssemblyTitle(\"File description\")]\n//[assembly: AssemblyDescription(\"Comments\")]\n//[assembly: AssemblyCompany(\"Company name\")]\n//[assembly: AssemblyProduct(\"Product name\")]\n//[assembly: AssemblyInformationalVersion(\"1.0.0.0\")] //product version\n//[assembly: AssemblyCopyright(\"Copyright © {{DateTime.Now.Year}} \")]\n//[assembly: AssemblyTrademark(\"Legal trademarks\")]\n```\n"
  },
  {
    "path": "Other/DocFX/_doc/editor/Git, backup, sync.md",
    "content": "---\nuid: git\n---\n\n# Git, backup, sync\n\n## Git and GitHub\n\nGit software can be used to make multiple backups of workspace files (scripts etc) and store them in local and cloud repositories.\n\nGitHub is the cloud service where most programmers have their public and private Git repositories.\n\nTo backup workspace files can be used LibreAutomate or/and any Git software.\n\n## How to start\n\nYou need a [GitHub](https://github.com/) account, and a private repository there. It's free.\n\n1. In your GitHub account [create new repository](https://github.com/new). It must be private and empty (no readme etc). Copy its URL.\n2. In LibreAutomate click menu **File > Git > Git setup**. Paste the URL.\n3. If Git not installed, click the **Install** button. It installs MinGit (minimal Git) in the LibreAutomate folder (subfolder `Git`). Or you can download and install Git for Windows (not in LA folder); let it set *PATH* variable.\n4. Click OK. What it does:\n    - Connects to your GitHub account to verify the repository.\n    - Creates/initializes local Git repository (hidden subfolder `.git` in the workspace folder).\n    - If local repository already exists, just updates its settings (URL).\n    - Creates files `.gitignore` and `.gitattributes` if missing.\n5. Now you can use other **Git** menu commands. To access it faster, you can unhide the **Git** button in the **File** toolbar.\n\n## How to use\n\nUse menu **File > Git**. Menu commands:\n\n- **Git status** - print Git status info for this repository. It helps you to decide whether you want to commit, push, pull or do nothing.\n- **Commit** - make local backup. Adds it to the local Git repository (hidden subfolder `.git` in the workspace folder). Does nothing if there are no changes since the last commit.\n- **Push to GitHub** - commit if need, and upload new local commits to the GitHub repository.\n- **Pull from GitHub** - if the GitHub repository contains new commits that are not in the local repository, downloads them to the local repository and updates workspace files (scripts etc).\n- **GitHub Desktop** - run GitHub Desktop (if installed).\n- **Cmd** - run `cmd.exe`. Then you can execute any Git commands.\n- **Workspace folder** - open the workspace folder.\n- **Reload workspace** - reload current workspace. Need it after an external program (GitHub Desktop, Git, etc) modified workspace files.\n- **GitHub sign out** - delete GitHub credentials saved on this computer. You may want it when using portable LibreAutomate.\n- **Maintenance** - tools to compact the Git repository.\n- **Git setup** - Git setup dialog.\n\n## Remarks\n\nLibreAutomate uses Git command line programs to perform most of the work. Therefore Git must be installed. Either private Git (in LibreAutomate folder) or regular Git. Often LibreAutomate prints the used command line (blue text), and sometimes also the output text (red if failed).\n\nFiles and folders imported as links usually are not in the workspace folder and therefore are not included in backup.\n\nThe current branch must be `main`. Else **Git** menu commands don't work.\n\nDefault Git credential helper is Git Credential Manager. If it does not work on your computer, use another helper.\n\n### Other Git software\n\nGit has many features not included in the **Git** menu. To name a few:\n- See the commit history.\n- See workspace changes since the last commit, and changes in each commit.\n- Restore files from a backup (ie from a commit).\n- Pull and merge diverged commits.\n- Use branches.\n\nGit can be used via command line. Or you can install a [Git GUI client](https://git-scm.com/downloads/guis). Try [GitHub Desktop](https://desktop.github.com/), it's free and easy to use. In its **File** menu select **Add local repository** and add the workspace folder. If you need a GUI client with more features, try TortoiseGit.\n\nWhen using external Git software to manage the current LibreAutomate workspace, you can temporarily exit LibreAutomate. It isn't necessary, however you should know that:\n- If external Git software made changes in the workspace while LibreAutomate is running, click **Reload workspace** in the **Git** menu.\n- LibreAutomate saves everything when another app becomes active (active window). Still there is a small possibility that it may change some workspace files while you use Git.\n\nUse external Git software carefully. Else it can modify workspace files not as you wanted. Consider making a temporary copy of entire workspace folder. The Git menu is safer; the **Pull** command can modify workspace files, but the old version isn't lost anyway.\n\n### Sync\n\nWhen using the same workspace on multiple computers (for example with portable LibreAutomate), use the **Pull** and **Push** commands to sync its copies (make identical). Use **Pull** when started to work with the workspace. Use **Push** when ending to work with the workspace.\n\n```\nComputer1 ending    --push-->  GitHub\nComputer2 starting  <--pull--  GitHub\nComputer2 ending    --push-->  GitHub\nComputer1 starting  <--pull--  GitHub\n```\n\nWhen not using **Pull** and **Push** strictly in this way, the commit histories in the local and GitHub repositories may diverge. It meens, both repositories contain new different commits. The **Pull** command detects it and you'll have to choose which new changes you want to keep - local or remote (on GitHub). Git software also supports merging, but it is an advanced operation that can damage some files, and is not recommended to use with LibreAutomate workspaces.\n\n### Excluded files\n\nGit ignores (does not backup etc) files and directories specified in file named `.gitignore` in the workspace directory. The Git setup dialog creates it if missing. You can edit it ([how](https://www.google.com/search?q=.gitignore)). Default text:\n\n```\n/.nuget/*/*\n/.interop/\n/exe/\n/dll/\n\n# Don't change these defaults\n!/.nuget/*/*.csproj\n/.compiled/\n/.temp/\n\n```\n\nThe first 2 lines exclude directories that contain many big binary files (dll etc) than can be easily restored. You may want to delete or comment out these lines if cannot easily restore, for example if using portable LibreAutomate. But then your local Git repository (folder `.git`) can become very big after some time. Next 2 lines exclude default directories used for your created programs and libraries. The last 2 lines exclude directories where LibreAutomate stores temporary files. Lines starting with `!` reinclude some files in directories excluded by other lines.\n\nIf using the same workspace on multiple computers, you can exclude these small folders if don't want to sync:\n- `/.state/` - saved foldings, caret position, lists of open files and expanded folders, etc.\n- `/.toolbars/` - saved toolbar settings (position, context menu, etc).\n\nIf you don't exclude some big files that are unnecessary to backup, after some time the `.git` folder can become very big. Then you can try **Maintenance** in the **Git** menu. Or install and use git-filter-repo or similar tool. Afterwards you may want to add these files to .gitignore.\n\n### Restoring workspace files from backup\n\nIf you have the workspace folder with Git repository on current computer, you can restore workspace files to the state saved at the time of the last or some older commit. A commit is a backup; you make it with **Git** menu command **Commit** or **Push to GitHub**. To restore, use [Git software](#other-git-software).\n\nIn GitHub Desktop:\n- If there are files displayed in the **Changes** tab and you want to restore them from the last commit:\n    - To restore all files: menu **Branch > Discard all changes**.\n    - To restore a file: right click it and select **Discard changes**. With `Ctrl` or `Shift` you can select multiple files.\n    - Alternative way to restore all files: commit and then revert (read below).\n- Or you can restore the workspace from an older commit. In **History** right click the top commit and select **Revert**. It retains all commits and adds 1 new commit. If need, repeat this for other commits made after the wanted commit, in top-down order.\n\nAfter restoring, if LibreAutomate is running, in its **Git** menu select **Reload workspace**. Or exit LibreAutomate before restoring.\n\nIf you don't have the workspace folder with Git repository on current computer, you can clone (download) it from a GitHub repository. Run LibreAutomate, and it will open the last used or new workspace. In the **Git** menu click **Git setup**, enter repository URL, select **Clone**, **OK**. Then you can open and use the cloned workspace. Or you can clone using any Git software.\n\n### Auto-backup\n\n**Options > Workspace > Auto-backup (Git commit)**.\n"
  },
  {
    "path": "Other/DocFX/_doc/editor/Icons.md",
    "content": "---\nuid: icons\n---\n\n# Icons\n\nIcons can be used in C# scripts:\n- Toolbars and menus (classes `toolbar`, `popupMenu`)\n- WPF windows (classes `wpfBuilder`, `ImageUtil`)\n- Standard dialog windows (class `dialog`)\n- Tray icon (classes `trayIcon`, `script`)\n- Icon in OSD (class `osdText`)\n\nAlso in LibreAutomate UI etc:\n- Icons of scripts etc displayed in the **Files** panel and elsewhere\n- Icons of LA toolbar buttons (menu **Tools > Customize**)\n- Icons of generated EXE files (menu **File > Properties**)\n\nMost of the above features support vector icons found in menu **Tools > Icons**.\n\nSome of the above features support classic icons (eg from ICO or EXE files). To get them use class [icon](). Menus and toolbars can automatically get icons for scripts and files used in button/item action code.\n\nMany of the above features use class [IconImageCache]() to cache icons, because finding/extracting them is slow. To clear the cache: menu **Tools > Update icons**. It updates icons in toolbars etc.\n\n## The **Icons** tool\n\nLibreAutomate installs a database file `icons.db` with about 60000 vector icons from [MahApps.Metro.IconPacks](https://github.com/MahApps/MahApps.Metro.IconPacks). You can see them all in the **Icons** tool (menu **Tools > Icons**). A list item displays icon image, name, and icon pack name in parentheses.\n\nUse the search field to find icons. Type a word, and the list displays icons with the word in icon name. Case-sensitive if the search text contains upper-case characters. Also supports [wildcard expression](xref:wildcard_expression) (if contains `*` or `?`) and `Pack.Name`.\n\nYou can use the **AI search** button to find icons by name or image using AI. More info: [LA and AI](xref:ai).\n\nAll these icons are monochrome. Select a color in the color tool.\n\nUse the **Set file icon** section to assign the selected icon to the file (script etc) or folder currently selected in the `Files` panel. Or you can assign the icon to all files of that kind.\n\nUse the **Menu/toolbar/etc icon** section to assign the selected icon to the toolbar/menu item at the text cursor position in the code editor. Or copy the icon name or XAML to the clipboard. Also use it when working with the **Customize** tool.\n\nUse the **Export to current workspace folder** section to save the selected icon as an ICO or XAML file in the current folder in the workspace (**Files** panel).\n\nUse the **Custom icon string** section to adjust some parameters of the selected icon or/and compose a multi-layer multi-color icon from 2 or more icons.\n\nIn scripts these icons are referenced by an *icon name* string like `\"*Pack.Name color\"`. It includes the pack name, icon name, optionally color (default black). Also optionally can include size, margins, multiple icons (as layers), etc. The syntax is documented in article [ImageUtil.LoadWpfImageElement](). The **Name** button in the **Menu/toolbar/etc icon** section copies the string to the clipboard.\n\nUse the **Options** section to set options for the **Icons** tool:\n- **High contrast color** - append the specified color to icon names. It will be used in high-contrast dark mode.\n- **List background** - background color for icon images in the list.\n- **List image size** - change the size (default 16) of icons in the list. Note, it does not change the size of the copied icon; it's always 16; the run-time size depends on where and how the icon is used."
  },
  {
    "path": "Other/DocFX/_doc/editor/LA and AI.md",
    "content": "---\nuid: ai\n---\n\n# LibreAutomate and AI\n\nThis article is about LA features that use AI.\n\n## LA documentation search\n\nIn the **Help** panel you can use AI-based semantic search to find LA documentation articles. Type a short meaningful search phrase or question in the search field, and press `Enter` or click the **AI search** button. The panel will display a list of LA documentation articles that contain the requested information. The results are much better than a keyword search. The list is ordered by semantic similarity to the search phrase.\n\nThe list of results often is not perfect. At the top are the best matches. Items at the bottom may even be not relevant to the search phrase. The tool does not uses slow and expensive AI LLM chat models. It uses AI embedding and reranking models. Normally it takes about 2 s.\n\nFor best results, use a short and meaningful phrase or question, like `paste text` or `how to copy selected text into a variable`. It should contain a *single* task, action or concept.\n\nThis feature uses AI models from Voyage or Gemini, accessed via web API. They will cost you a few cents per month, depending on usage. Create an account on that AI company's website (Voyage, Google GCP), and generate an API key there. In LibreAutomate, in **Options > AI** enter the API key and select AI models you want to use.\n\nThe reranker model is optional, but recommended. It improves the results.\n\nLibreAutomate uses AI models that worked best when testing. But it also allows users to add other models to the list; ask about it in the LA forum or in the Dicsussions of the GitHub repository.\n\n### Improving AI chat answers and generated code\n\nYou probably noticed that AI chat bots/apps like ChatGPT, Gemini or Claude usually generate incorrect code when they are asked to use LibreAutomate API. It is because they know too little about the LA API and other LA features. To make AI answers much better, give the AI relevant LA documentation.\n\nWhen the **Help** panel shows AI search results, the **AI search** button is replaced by button **Copy results for AI chat**. It copies the found articles to the clipboard. Also adds the search phrase and instructions for AI. Paste in an AI chat message (ChatGPT etc).\n\nIf your AI chat message is just that text, the AI will assume the search phrase is your question, and will answer it. But the AI answer likely will be better if you write a longer question in the AI chat message, and paste the AI search results below it (or anywhere in the message). You can even paste results of several AI searches in various places in the message.\n\n## MCP server\n\nExternal AI apps and tools, installed on your computer, can automatically retrieve LA documentation via MCP (Model Context Protocol). Examples: Copilot in Visual Studio, Claude Desktop, n8n. It improves AI chat answers, generated code, AI decisions, etc.\n\nLA has an MCP server that includes tools to get LA documentation. Whenever AI needs information about LA automation API or other features, it calls a tool with a search query (AI-generated). The tool finds relevant LA documentation articles and returns them to the AI. The tool uses the same AI search engine as in the **Help** panel, and the **Options > AI** settings.\n\nLA MCP server details:\n- Type: `stdio`.\n- Command path: the main LA program file. Usually `C:/Program Files/LibreAutomate/Au.Editor.exe`.\n- Command arguments: `/mcp`.\n\nAI apps/tools have a MCP configuration UI or file. Add the LA MCP server there. Example MCP server entry in file `C:\\Users\\Me\\.mcp.json` used by VS Copilot:\n\n```json\n    \"LibreAutomate\": {\n      \"type\": \"stdio\",\n      \"command\": \"C:/Program Files/LibreAutomate/Au.Editor.exe /mcp\",\n      \"disabled\": false\n    }\n```\n\nA MCP client (AI app/tool) runs the program and communicates with it via standard input/output streams. It discovers the tools, and calls them when AI asks.\n\nTell AI to use the MCP tools. For example add \"Use LibreAutomate MCP\" in your chat message. Also AI apps like VS Copilot usually have an \"AI instructions\" file where you can add information about the MCP tools. For example VS Copilot uses file `.github\\copilot-instructions.md` in the solution directory. Tell AI to use the file.\n\n<details>\n<summary>Example instructions file</summary>\n\n```markdown\n# Custom Instructions\n\n## C# Instructions\nApplies to: `**/*.cs`\n\nYou help LibreAutomate users write C# code and use the app.\n\nEnvironment: Windows 11, C# 14, .NET 10.\n\nC# code that uses LibreAutomate usually is a script.\n\nScript code should be concise and easy to understand even for non-programmers. Top-level statements; type definitions below. No try/catch at the top level of code.\n\nPrefer LibreAutomate APIs. They're optimized for reliability, simplicity and concise code. Many methods are high-level; just find the right one.\n\nTo get information about LibreAutomate API or IDE, use MCP server `LibreAutomate`. You can use it in two ways:\n- Call tool `find_la_docs`. It finds and returns the requested information.\n- Call tool `get_la_docs_toc` to get the table of contents of the LibreAutomation documentation. Select up to 100 article names, and call tool `get_la_docs` to get the articles.\n\nDon't need `using` directives for LibreAutomate API.\n\nTo create WPF windows, use class `wpfBuilder` from LibreAutomate. XAML is used rarely and requires `XamlReader`.\n\nMany LibreAutomate parameter types have implicit conversion operators. Use it to make the code more concise.\n\nLibreAutomate users also can use:\n- Libraries from NuGet. Tool to install: menu **Tools > NuGet**.\n- Windows API. Add declarations in `unsafe class api : NativeApi { }`.\n\nIn your messages prefer ASCII punctuation (`'`, `-`). Don't use emoji in code.\n\nWhen user asks \"do something\", usually it actually means \"create code to do something\", or \"explain how to do something\".\n\nWhen user message contains a file path or URL, don't try to load/fetch the file, unless the user asks to do it. The path/URL is likely meant to use in generated code.\n\nIn agent mode you may edit files, and build if need. Don't run the program.\n\n```\n</details>\n\nTo see how AI uses LA MCP tools, check **Options > AI > MCP > Print tool calls**. When a tool is called, it prints the tool name, arguments and results.\n\nBefore installing a new LibreAutomate version, close apps that use the MCP server.\n\n## Find icons by name or image\n\nAI also can help you find icons in the **Icons** tool. It can search by name or/and image.\n\nHow to use it:\n- Type a short search phrase in the search field.\n- Or/and copy an icon image (PNG format) to the clipboard. You can find icons on the internet.\n- Then click the **AI search** button. After a second it displays icons whose names are similar to the search phrase or/and images are similar to the image in the clipboard.\n\nThis feature uses an AI model provided by Voyage (an AI company). You probably already have a Voyage API key and use it for LA documentation search. If not, create a Voyage account, generate an API key, and enter it in **Options > AI**.\n"
  },
  {
    "path": "Other/DocFX/_doc/editor/Menu commands.md",
    "content": "---\nuid: menu_commands\n---\n\n<!-- auto-generated -->\n\n# Menu commands\n\n- File\n  - New\n    - New script  \n      A script is a C# code file that can be executed like a program.\n    - New class  \n      Class files contain C# classes/functions that can be used in other C# files.\n    - New text file  \n      Text files contain any text except C# code.  \n      To change the file type, edit the \"txt\" in the filename.\n    - New folder\n    - New project\n      - @Script\n      - @Library\n    - Dialogs\n      - Dialog.cs\n      - Dialog with tabs.cs\n      - Dialog class.cs\n      - Dialog with tabs class.cs\n    - More\n      - preBuild postBuild script.cs\n    - Default\n      - global.cs\n      - @Triggers and toolbars\n  - Delete...\n  - Rename\n  - Properties\n  - Copy, paste\n    - Multi-select  (option)  \n      If checked:  \n          - You can select multiple items with Ctrl or Shift.  \n          - Double-click to open.\n    - Cut\n    - Copy\n    - Paste\n    - Cancel Cut/Copy\n    - Copy relative path  \n      Path in this workspace (in the Files list), like \"\\Folder\\File.cs\".\n    - Copy full path  \n      Full path of the file.\n  - Open, close\n    - Open\n    - Open in default app\n    - Select in Explorer\n    - Close\n    - Close all\n    - Collapse all folders\n    - Collapse inactive folders\n  - Export, import\n    - Export...\n    - Import .zip...\n    - Import workspace...\n    - Import files...\n    - Import folder...\n  - Workspace\n    - Reload this workspace\n    - Open workspace...\n    - New workspace...\n    - Repair workspace...\n    - Save now  \n      Save all changes now (don't wait for auto-save). Editor text, files, settings etc.\n  - Git\n    - Git status\n    - Commit\n    - Push to GitHub\n    - Pull from GitHub\n    - GitHub Desktop\n    - Cmd\n    - Workspace folder\n    - Reload workspace\n    - GitHub sign out\n    - Maintenance...\n    - Git setup...\n  - Close window\n  - Exit\n- Edit\n  - Clipboard\n    - Cut\n    - Copy\n    - Paste\n    - Copy forum code\n    - Copy HTML \\<span style>\n    - Copy HTML \\<span class> and CSS\n    - Copy HTML \\<span class>\n    - Copy markdown\n    - Copy without screenshots\n  - Undo\n    - Undo\n    - Redo\n    - Undo in files  \n      Undo a multi-file operation, such as renaming a symbol in multiple files.\n    - Redo in files  \n      Redo a multi-file operation.\n  - Find\n    - Find text\n    - Find symbol...\n    - Find references\n    - Find implementations\n    - Go to definition\n    - Go to base\n    - Rename symbol\n    - Find next\n    - Find previous\n    - Next found\n    - Previous found\n  - Assist\n    - Autocompletion list\n    - Parameter info\n    - Raw Enter before ) ] ;  (option)  \n      Temporarily disables the \"auto-complete statement on Enter\" features that are enabled in Options > Code editor.  \n      Other ways to insert new line before ) or ] or ; when the auto-completion features are enabled:  \n      - Shift+Enter. Or Ctrl+Enter, depending on settings.  \n      - Space, then Enter.  \n      - Enter, then Ctrl+Z (undo).  \n      - Continue a vertical method chain by typing . on the next line after the ;.  \n      To exit statement when the features are disabled, use Ctrl+Enter. Or Shift+Enter, depending on settings.  \n      More info in app help topic \"Code editor\".\n  - Navigate\n    - Toggle bookmark\n    - Previous bookmark\n    - Next bookmark\n    - Go back\n    - Go forward\n    - Previous document\n  - Selection\n    - Toggle comment\n    - Toggle line comment\n    - Comment selection\n    - Uncomment selection\n    - Surround...\n    - Documentation tags...\n    - Select all\n  - Tidy code\n    - Format document\n    - Format selection\n    - Disable format selection\n    - Indent selected lines\n    - Unindent selected lines\n    - Deduplicate wnd.find  \n      Remove duplicate `wnd.find` statements in all or selected text.\n    - Remove screenshots  \n      Remove screenshots in all or selected text.  \n      Screenshot images are saved in text as hidden comments.\n  - Generate\n    - Create delegate\n    - Implement interface/abstract  \n      Implement members of interface or abstract base class.\n    - Create event handlers\n    - Create overrides\n    - Create GUID\n    - Add function Main\n    - Convert [PreserveSig] methods\n  - View\n    - Wrap lines  (option)\n    - Images in code  (option)  \n      If checked, displays images (file icons, screenshots, `\"image:\"`, `\"*icon\"`).  \n      Also captures screenshots when recording etc.\n    - WPF preview  (option)\n    - Customize...\n- Code  \n  Tools for creating code\n  - Input recorder\n  - Find window\n  - Find UI element\n  - Find image\n  - Find OCR text\n  - Get files in folder\n  - Keys\n  - Regex\n  - Color\n  - Windows API\n  - Quick capturing info\n- T T  \n  Triggers and toolbars\n  - Hotkey triggers\n  - Autotext triggers\n  - Mouse triggers\n  - Window triggers\n  - New trigger...\n  - Other triggers\n  - Toolbars\n  - New toolbar...\n  - Toolbar trigger...\n  - Active toolbars\n  - Disable triggers\n  - Restart TT script\n  - Find triggers  \n    Finds triggers of current script.  \n    Finds its name in '@Triggers and toolbars' files in trigger actions, toolbar button actions and other code.  \n    Also finds scheduled tasks, workspace startup scripts and class file test scripts.\n  - Triggers list info\n  - Script launchers\n    - Command line...\n    - Schedule...\n    - Shortcut...\n    - Shell menu...\n    - Run at startup...\n- Run\n  - Compile\n  - Run\n  - End task\n  - Recent...\n  - Debug run\n  - Publish...\n  - PiP session  \n    Another session of this user inside a Picture-in-Picture window\n  - Run in PiP\n- Tools\n  - Options\n  - Icons\n  - Update icons  \n    Clear icon caches\n  - NuGet\n  - Snippets\n  - Customize\n  - Portable\n  - Output\n    - Clear\n    - Copy\n    - History\n    - Wrap lines  (option)\n    - White space  (option)\n- Help\n  - LA docs\n  - C# docs\n  - Context help\n  - Website\n  - Forum\n  - What's new\n  - Email\n  - Donate\n  - About\n"
  },
  {
    "path": "Other/DocFX/_doc/editor/NuGet.md",
    "content": "---\nuid: nuget\n---\n\n# NuGet\nIn C# scripts you use classes and functions all the time. These come from various libraries, also known as assemblies or dll files. Many libraries are already installed (.NET, LibreAutomate), but there are many more great libraries available on the internet.\n\nTwo main places where you can find libraries are [GitHub](https://github.com/) and [NuGet](https://www.nuget.org/). GitHub mostly hosts source code, which you usually don't use directly. NuGet is a repository of compiled libraries, called packages. They're easy to install, update, and use in C# code.\n\nIn LibreAutomate, use the **NuGet packages** tool to install and manage NuGet packages.\n\n## How to install a NuGet package\nFind a package on the [NuGet](https://www.nuget.org/) website. Click the **Copy** button.\n\n    Note: If the package is only for **.NET Framework**, it is outdated and incompatible.\n\n    Tip: There is a link to the source repository on the right side. Visit it to assess the library better. Avoid abandoned libraries (last commit years ago).\n\nPaste the copied text in the **NuGet packages** window. Or you can enter just the package name, and the tool will get the latest version. Or enter `Name --version 1.2.3`.\n\nIn the combo box you can select a folder or type a name for a new folder. Or use the default folder `-`. Finally click the **Install** button.\n\nThe **Add code** button inserts a meta-comment like `/*/ nuget folder\\Package; /*/` in the current script. Another way - **Properties > NuGet**. You can use an installed package in multiple scripts (just add the meta-comment).\n\nMany code examples in the Cookbook use NuGet packages.\n\n## How and where are packages installed (TL;DR)\nLibreAutomate and other NuGet package managers download and extract package files into the global/shared package cache folder `folders.Profile + @\".nuget\\packages\"`. Different versions of a package are installed side by side. The folder is safe to delete.\n\nWhen installing a package, LibreAutomate copies its files from the cache to `workspace\\.nuget\\folder specified in the combo box`. Then scripts and the code editor can use these files. When compiling an `exeProgram` script, runtime files are copied to the output folder.\n\nYou can install multiple packages in a folder. You may want to install large libraries (many dll files) each in its own folder. Folders also can be used to isolate incompatible packages if need (rarely). For example, `PackageX` version 1 in `FolderA`, and `PackageX` version 2 in `FolderB`. An installed package can be used by all scripts of the workspace. A script can use packages from multiple folders if they are compatible.\n\nWhen a package depends on other packages, those are installed too. The list contains only the top-level package, but in scripts you can use all of them.\n\nLibreAutomate uses standard, well-documented commands like `dotnet add package`. Prints them when installing. Uses `nuget.config` files (sources etc) from standard locations and `workspace\\.nuget`.\n\nSome NuGet packages don't install all required files, for example native dlls. Try this:\n- Often the missing files are in other NuGet packages. Install these packages.\n- Else you may have to download the missing files. In some cases they are in the `.nupkg` file (it is a zip file) in the packages cache folder. Click the **Folder** button and copy these files there. Set read-only attribute to prevent deleting them when managing packages. If the missing files are managed assemblies, in scripts that use them will need `/*/ r Dll; /*/` (use **Properties > Library**). If used in scripts with role `exeProgram`, copy these files to the output folder.\n\n## Used software\nInstalling packages requires the .NET SDK. By default, LibreAutomate uses it if installed; else downloads (~30 MB) and uses a private minimal SDK. See also **Options > Other > Use minimal SDK**. Portable LibreAutomate requires the full .NET SDK.\n\nTo ensure compatibility with all .NET Runtime versions, the minimal SDK uses the oldest build of the current major .NET SDK. Because of this, the Publish feature may fail to build code that uses the latest C# preview features. Solution: install the latest full SDK and ucheck **Options > Other > Use minimal SDK**. Note: the Publish feature is not used to install NuGet packages; it just uses the same minimal SDK.\n"
  },
  {
    "path": "Other/DocFX/_doc/editor/PiP session.md",
    "content": "---\nuid: pip_session\n---\n\n# PiP session\n\nA PiP session, also known as child session, is a separate session of the same user in a Picture-in-Picture window, where UI automation scripts can run in the background without interfering with the mouse, keyboard, clipboard and windows in the main desktop.\n\nThis feature is available on Windows 8.1 and later, except on Windows Home editions.\n\nMenu **Run > PiP session** - creates a PiP window where the child session runs. You can click the system icon of the PiP window to change some settings, for example clipboard sharing. The **Maximize** button makes the window full-screen.\n\nSimply closing the window just disconnects from the child session and puts it into a no-UI state (UI automation scripts can't run). To end the child session, click **Start > Sign out** in the PiP window.\n\nThe **Run > PiP session** command starts a child session if not running, or reconnects if disconnected, or just activates the PiP window if connected. When starting a child session, the command also sets to run LibreAutomate in it, because probably you'll want to run scripts there. Multiple child sessions not supported.\n\nMenu **Run in PiP** - tells the LibreAutomate instance running in PiP to run the script that is currently active in the main LibreAutomate instance. It creates a PiP window if need, like the above command, and puts the new PiP window behind other windows. And waits until UI automation scripts can run.\n\nSee also: [script.runInPip](), [script.isInPip](), [miscInfo.isChildSession]()\n\n## App settings\nA PiP session runs under the same Windows user account as the main session, so programs in both sessions use the same user files and settings.\n\nOnly these LibreAutomate settings are separate:\n- **App > Start hidden**, **Visible if**, **Check for updates**\n- **Workspace > Startup scripts**, **Auto-backup**\n- Hotkeys (all in the **Hotkeys** page)\n- Window positions (main window, tool windows)\n- Bookmarks, breakpoints, expanded folders, open files, folding.\n\nIgnored in a PiP session started by LibreAutomate:\n- **App > Start with Windows** (in PiP always starts)\n\nWhile LibreAutomate is running in both PiP and main session:\n- Don't change other settings, panel/toolbar layout, snippets.\n- You can manage and edit workspace files (scripts etc) in both sessions.\n- You can run any scrits in the PiP session as well as in the main session.\n- Use the same workspace in both sessions.\n\n## Issues and other info\nLibreAutomate uses the Windows [child sessions](https://learn.microsoft.com/en-us/windows/win32/termserv/child-sessions) feature, which uses the Remote Desktop technology. Instead of connecting to another computer it creates a new session of the current user of this computer. You can work with the PiP window like with another computer in a Remote Desktop Connection window.\n\nWhen the child sessions feature is used the first time on a computer, it asks for credentials. Enter the user name and password of your Windows user account. If asks for credentials every time when connecting: sign off the child session, then sign off the main session, and sign in again. If your account does not have a password, you can either create new password or suffer the credentials prompt every time.\n\nAlso the first time need to enable the child sessions feature. LibreAutomate automatically enables it. If you'll ever want to disable it, run this in Windows Terminal or cmd running as administrator: `Au.Editor.exe /pip /disablecs`.\n\nYou can use PiP without LibreAutomate running in the main session: `Au.Editor.exe /pip`. Also, you can exit and start LibreAutomate in the main or PiP session at any time.\n\nHotkey, autotext and mouse triggers work only in the main session. Probably you'll even don't want to run your triggers script in PiP. And it is not set to run automatically by default.\n\nThe PiP window does not capture the `Win` key. In PiP you can't use hotkeys with it. If a hotkey without the `Win` key is used in both sessions (main and PiP) and you press `Win` + the hotkey, the hotkey is ignored in the main session but works in PiP. Without `Win` - vice versa. This info was about hotkeys other than LibreAutomate triggers.\n\nBecause the PiP session uses the same user account and its files/settings as the main session, some programs running in PiP may not work seamlessly. Some apps may refuse to run or open the same files in both sessions (main and PiP) simultaneously. Web browsers may refuse to use the same profile.\n\nThere are many other known issues. You can find more info in docs of other programs that use the child sessions (PiP) feature, for example UiPath and Power Automate.\n"
  },
  {
    "path": "Other/DocFX/_doc/editor/Portable app.md",
    "content": "---\nuid: portable\n---\n\n# Portable mode\nLibreAutomate can run as a [portable application](https://en.wikipedia.org/wiki/Portable_application). In portable mode its data (scripts, settings, caches, temporary files, etc) is inside subfolder `data` of its program folder. The presence of `data` folder tells LibreAutomate to use portable mode.\n\n## Setup\nIn non-portable LibreAutomate use menu **Tools > Portable**. The tool installs portable LibreAutomate, for example in a USB drive.\n\nThe tool copies LibreAutomate program files, .NET runtime, current workspace (scripts etc), settings, script data and the user documents folder of LibreAutomate.\n\n## Run\nRun `Au.Editor.exe` (it may be displayed as `LibreAutomate`).\n\nThe portable app can run on any Windows 7/8/10/11 64-bit computer. Don't need to install .NET runtime.\n\nThe portable app does not have the \"always run as administrator\" feature, but you can right-click and select **Run as administrator**. See [UAC](xref:uac).\n\nThe portable app does not write files outside of its folder. And does not write to the Registry.\n\nLibreAutomate uses these special folders: [folders.ThisAppDocuments](), [folders.ThisAppDataLocal](), [folders.ThisAppTemp](). In portable mode these folders are in subfolder `data` of the portable program folder.\n\nScripts can write anywhere, but should use only the above special folders. Other useful functions: [ScriptEditor.IsPortable](), [folders.ThisAppDriveBS]().\n\nIf you want to store data in an external folder, replace the `data` subfolder with a symbolic link. NTFS filesystem supports it; exFAT doesn't. Example: `filesystem.more.createSymbolicLink(@\"D:\\PortableApps\\LibreAutomate\\data\", @\"..\\..\\Documents\\LibreAutomate\", CSLink.Directory);`.\n\nIf non-portable LibreAutomate is installed on that computer:\n- Portable and non-portable programs cannot run simultaneously.\n- Portable and non-portable programs don't share data and settings.\n\n## PortableApps.com\nIf you use the PortableApps.com platform, install portable LibreAutomate in its folder, for example `D:\\PortableApps\\LibreAutomate`. Then **LibreAutomate** will be in its menu (the first time may need to click **Apps > Refresh**). By default it is in category **Other**. You can right click it and move to an existing or new category. You can right click it and click **Run as administrator** or check **Start automatically**. The menu contains all exe files found in the LibreAutomate folder, but other files are not useful in the menu, and you can hide them.\n"
  },
  {
    "path": "Other/DocFX/_doc/editor/Scripts.md",
    "content": "---\nuid: script\n---\n\n# Scripts\n\nTo automate something, you create a script. Click the **New** button on the toolbar. A script is a text file containing C# code. It's a small program that runs when you click the **Run** button. Also there are other ways to launch scripts; look in the Cookbook.\n\nC# is a programming language, one of the most popular. You can find a C# tutorial in the Cookbook and many info on the internet, for example [here](https://learn.microsoft.com/en-us/dotnet/csharp/).\n\nWhen you click the **Run** button, LibreAutomate at first compiles the script if not already compiled. Cannot run if the C# code contains errors.\n\nEach script task is executed in a separate process, unless its role is `editorExtension`.\n\nIn scripts you can use classes/functions of the automation library provided by this program. Also .NET, other libraries and everything that can be used in C#. Also you can create and use new functions, classes, libraries and exe programs.\n\nIn the [code editor](xref:code_editor) you can press `Ctrl+Space` to show a list of available functions, classes etc.\n\nA script can contain these parts, in this order. All optional.\n- Comments or documentation comments. Example: ```/// File description.```.\n- ```/*/ properties /*/```. You can edit it in the **Properties** dialog.\n- `using` directives. Don't need those specified in file `global.cs`.\n- [script.setup]() or/and other code that sets run-time properties.\n- Script code. It can contain local functions anywhere.\n- Classes and other types used in the script.\n\nThis syntax is known as \"C# top-level statements\". It is simple and concise, but has some limitations. You can instead use a class with function **Main**. Try menu **Code > Simple > Add function Main**.\n\nThe ```//.``` and ```//..``` are used to fold (hide) code. Click the small **[+]** box at the top-left to see and edit that code when need. \n\nTo change default properties and code for new scripts: **Options > Templates**.\n\nIf role is `miniProgram` (default), the script is executed in `Au.Task.exe` process. Each script instance runs in separate process. The editor program compiles the script to an assembly file (dll) and saves in folder `.compiled` in the workspace folder. Then `Au.Task.exe` process loads it from there. The process uses `Au.dll` etc from the editor program folder. The `.compiled` folder is a cache containing only temporary assemblies.\n\nIf role is `exeProgram`, the editor program creates an exe program in a separate folder, and also copies used dlls etc there. Launches it when you click **Run** etc. You can also run it from File Explorer etc, and copy to other computers. It's an independent program; don't need LibreAutomate to execute it. [More info](xref:publish).\n\nIf role is `editorExtension`, the script is executed in editor process (`Au.Editor.exe`). It starts in the UI thread, and can create own thread(s). Must be programmed carefully, else the editor process may hang, crash, become slow, etc. You can't stop the script with the **End task** button; it must be programmed to exit itself. This feature is intended to create editor extensions (see Cookbook article \"Editor extension\"), run `preBuild`/`postBuild` scripts, and sometimes for other purposes when you want to execute some code in editor process. The assembly file is in folder `.compiled`.\n\nThe editor program compiles the script before launching it, but only if need, for example if the compiled assembly file is missing or after editing the C# file.\n"
  },
  {
    "path": "Other/DocFX/_doc/editor/Settings.md",
    "content": "---\nuid: program_settings\n---\n\n# App settings and the Options dialog\n\n## App settings\n\nMost app settings are saved in files in folder `Documents\\LibreAutomate\\.settings`. They are user-specific.\n\nWorkspace settings are saved in the workspace folder. Default workspace: `Documents\\LibreAutomate\\Main`. A workspace is a collection of scripts and other files; you manage them in the **Files** panel.\n\nIf you want to copy settings and workspaces to another computer, copy folder `Documents\\LibreAutomate`.\n\nSee also: [PiP session](xref:pip_session)\n\n## Options dialog\n\n### App\n\n#### Start with Windows\nRun this app at Windows startup. This setting is saved in the Registry.\n\n#### Start hidden; let X hide\nDon't show the main window when the app starts. To show the window, you can click the tray icon or run the app again. When you close the window, the app process does not exit; just hides the window.\n\nIf unchecked, shows the window at startup. The app process exits when you close the window.\n\n#### Visible if not auto-started\nApply the **Start hidden** part of the above setting only if the app started with command line `/a`. Note: `/a` is used by **Start with Windows**.\n\n#### Check for updates\nEvery day connect to `libreautomate.com` to get program version info. If a new version available, print it in the output panel.\n\n### Workspace\nThese settings are workspace-specific. Security: the scripts will not run on other computers, unless the user settings file copied there too.\n\n#### Startup scripts\nList of scripts to run when this app started and/or loaded this workspace. \nThe format is CSV. A delay can be specified in second column. Example:\n\n```\nScript1.cs\n\\Folder\\Script2.cs\n//Disabled.sc\n\"Script, name, with, commas.cs\"\nScript with delay.cs, 3s\nAnother script with delay.cs, 300ms\n```\n\n#### Hide/ignore files and folders\nList of ignored files and folders. Matching files/folders are not displayed in the **Files** panel, and the app ignores them (cannot find, compile, etc). Line format: wildcard, not case-sensitive; use `//Line` for comments. Compared is file path in workspace (like `\\Folder\\File.ext`). The synchronization with the filesystem occurs whenever the app becomes active (active window).\n\n```\nExample:\n*.bak\n*\\FolderAnywhere\n\\Folder\n\\Folder1\\Folder2\n//Comment\n```\n\n#### Auto backup (Git commit)\nSilently run [Git](xref:git) commit when LibreAutomate is visible the first time after loading this workspace or activated later after several hours from the last backup. It creates a local backup of workspace files (scripts etc). To upload etc, you can use menu **File > Git**.\n\n### Font, colors\nFont and colors of various code elements displayed in the code editor area. Also fonts of some other UI parts.\n\n#### Theme\nSelect a predefined set of code editor font/colors, aka *theme*.\n\nDefault themes are read-only. Changes to a default theme are saved as a separate theme \"Theme \\[customized\\]\"; it's a csv file in the user settings folder. Changes are saved when you click **OK** or **Apply**.\n\nYou can add more theme files to the default themes folder of the app. For example copy a customized theme file and rename.\n\n### Code editor\nCode formatting and intellisense options.\n\n#### Completion list > Append ()\nWhen you select a function in the completion list, whether/when to append `()`. Can append always, never or only when selected with the `Spacebar` key (but not `Tab`, doubleclick, etc).\n\n### Templates\nInitial code of new scripts and class files. Can be empty.\n\n### AI\nSelect AI models for LibreAutomate features that use AI. Set API keys.\n\nMore info: [LA and AI](xref:ai).\n\n### Other\n#### Documentation\nWhich version of documentation to use and where to display.\n- **Local** - documentation installed with the app. Displays articles in the **Read** panel.\n- **Online** - online documentation. Displays articles in the web browser. The documentation is of the latest program version, which may be not the same as your installed program version.\n\nIn any case, the **Help** panel displays the table of contents of the local documentation.\n\n#### Internet search URL\nThe Internet search URL used by this app, without the search string. Default: `https://www.google.com/search?q=`.\n\n#### Use minimal .NET SDK\nWhat .NET SDK to use for NuGet and Publish:\n- Checked - use a minimal SDK that contains .NET SDK files used by the above features. The app automatically installs/updates it when need. Location: subfolder `SDK`.\n- Unchecked - use the full .NET SDK, which must be installed manually.\n- Indeterminate (default) - use the full SDK if installed, else the minimal.\n\n#### Always print \"Compiled\"\nAlways print a \\\"Compiled\\\" message when a script etc compiled successfully.\nIf unchecked, prints only if role is `exeProgram` or `classLibrary`.\nIf 3-rd state, prints when executing the **Compile** command, but not when compiling implicitly (for example before launching the script).\n\n### OS\nThese are Windows settings, not settings of this app. They are applied to all programs. This app will not restore them when uninstalling.\n\n#### Key/mouse hook timeout\nMax time in milliseconds given for hook procedures. If this time exceeded, the hook does not work well, and after several times is disabled. This setting is important for this app, because small values can make triggers and some other its features unreliable. Default 300 ms, max 1000 ms, recommended 1000 ms.\n\n#### Disable \"lock active window\"\nThe Windows \"lock active window\" feature, also known as \"foreground window timeout\", does not allow apps to activate windows after keyboard/mouse input etc. If scripts sometimes fail to activate windows, try to check this.\n\n#### Underline menu/dialog item access keys\nAlways underline keyboard shortcut characters in text of menu items and dialog controls. It makes easier to create scripts that use the keyboard to select menu items or dialog controls. If unchecked, underlines only when used the `Alt` key.\n"
  },
  {
    "path": "Other/DocFX/_doc/editor/Snippets.md",
    "content": "---\nuid: snippets\n---\n\n# Snippets\n\nCode snippets are small blocks of reusable code that you can quickly insert into your C# code. For example, the `piPrintItSnippet` inserts code `print.it();` and moves the text cursor into the `()`. Snippets appear in the completion list, together with types, functions, etc. To show the list, in the code editor start typing a snippet name or press `Ctrl+Space`. Snippets containing `${SELECTED_TEXT}` also can be used to surround selected code (toolbar button **Surround** or menu **Edit > Selection > Surround**).\n\nIn the **Snippets** window (menu **Tools > Snippets**) you can add/delete/edit/hide/unhide your snippets and hide/unhide default snippets. Right-click to add/delete. Uncheck to hide (don't show in the completion list).\n\nA snippet can show a menu of sub-snippets. Use it to group similar snippets together, to reduce the number of snippets in the completion list. To create a menu of sub-snippets, at first create a snippet (without code or with code of the first menu item), then right-click it and add more menu items.\n\nSnippet properties:\n- **Name** - is displayed in the completion list. Also it is the text shortcut. If ends with `Surround`, the snippet is available only for surround.\n- **Context** - where the snippet can be used. Read more below. If not specified, the app auto-detects it from snippet code.\n- **Info** - short text displayed in the completion list item flyout (info window). Also menu item text; use `&` for keyboard shortcuts.\n- **Info+** - text displayed at the bottom of the flyout.\n- **Print** - text to print in the output panel when inserting the snippet. Can contain [output tags](xref:output_tags) if starts with `<>`.\n- **using** - namespaces to add as `using` directives if need. Example: `System.Windows; System.Windows.Controls`.\n- **Meta** - file properties to add as `/*/ meta comments /*/` if need. Example: `c A.cs; nuget -\\B`.\n- **\\${VAR}** - `${VAR}` variable type and default name, like `Au.popupMenu,m`. When inserting the snippet, the app looks for a local variable of the specified type, and replaces `${VAR}` in snippet code with the variable name, or with the default name if not found. To see how it works, in code editor insert `menuSnippet` and then `menuItemSnippet` (it contains `${VAR}`).\n- **Code** - snippet code.\n\nSnippet code can contain fields and variables, like in [VSCode](https://code.visualstudio.com/docs/editor/userdefinedsnippets).\n\nExample:\n```csharp\nfor (int ${1:i} = 0; $1 < ${2:count}; $1++) {\n\t${SELECTED_TEXT}$0\n}\n```\n\nFields:\n- `$0` - final text cursor position when the user presses `Enter` or several `Tab`.\n- `${n:text}` - field with tab stop index `n` (`1`, `2` ...) and default text. After inserting the snippet code, fields can be selected with `Tab`. Default text can be a snippet variable without `{}`, like `${1:$RANDOM}`.\n- `$n` or `${n}` - another instance of a `${n:text}` field. Its text is updated together. If there is no matching `${n:text}` field, it's a field without default text.\n\nVariables:\n- `${SELECTED_TEXT}` - selected text. Used only by the \"surround\" feature; else no text is added.\n- `${RANDOM}` - random number in decimal format.\n- `${RANDOM_HEX}` - random number in hexadecimal format.\n- `${GUID}` - new GUID.\n- `${VAR}` - local variable of type specified in the `${VAR}` field.\n\nEscape sequences: `\\$`, `\\\\`, `\\}`.\n\nOther VSCode snippet variables and field features are not supported.\n\nContext specifies where in code the snippet can be used (appears in the completion list and/or surround list). Several contexts can be combined, like `Function|Type`.\n- **Function** - inside function body. Also in the main script code (top-level statements).\n- **Type** - inside a class, struct or interface but not inside functions. Use for snippets that insert entire methods, properties, etc.\n- **Namespace** - outside of types. Use for snippets that insert entire types.\n- **Attributes** - use for snippets that insert an attribute inside `[]`. This context is not auto-detected.\n- **Line** - at the start of a line. For example, `#directive` snippets have context `Any|Line`.\n- **Any** - anywhere.\n\nThe app loads all snippet files with names that end with `Snippets.xml` from the app settings folder in the Documents folder. You can add/delete snippets files there. You can edit them in an XML editor too.\n\nDefault snippets are read-only, but you can clone a default snippet (right click, copy, paste) and edit the clone. If both snippets are checked and have the same name, the clone will be the first in the completion list."
  },
  {
    "path": "Other/DocFX/_doc/editor/toc.yml",
    "content": "- name: Application\n  href: Application.md\n\n- name: Code editor\n  href: Code editor.md\n\n- name: Scripts\n  href: Scripts.md\n\n- name: Class files, projects\n  href: Class files, projects.md\n\n- name: File properties\n  href: File properties.md\n\n- name: Menu commands\n  href: Menu commands.md\n\n- name: Settings\n  href: Settings.md\n\n- name: Command line\n  href: Command line.md\n\n- name: Creating exe programs\n  href: Creating exe programs.md\n\n- name: NuGet\n  href: NuGet.md\n\n- name: Snippets\n  href: Snippets.md\n\n- name: Icons\n  href: Icons.md\n\n- name: Debugger\n  href: Debugger.md\n\n- name: PiP session\n  href: PiP session.md\n\n- name: LA and AI\n  href: LA and AI.md\n\n- name: Portable app\n  href: Portable app.md\n\n- name: Git, backup, sync\n  href: Git, backup, sync.md\n\n- name: Compared with QM\n  href: Compared with QM.md\n"
  },
  {
    "path": "Other/DocFX/_doc/filter.yml",
    "content": "﻿apiRules:\n- exclude:\n      uidRegex: ^System\\.Object$\n      type: Type\n- exclude:\n      uidRegex: ^System\\.ValueType$\n      type: Type\n- exclude:\n      uidRegex: ^System\\.Attribute$\n      type: Type\n- exclude:\n      uidRegex: ^System\\.Collections\\.IEnumerable$\n      type: Type\n- exclude:\n    hasAttribute:\n      uid: Au.Types.NoDoc\n- exclude:\n      uidRegex: ^Au\\.Types\\.NoDoc$\n      type: Type\n- exclude:\n      uidRegex: \\.Finalize$\n      type: Method\n"
  },
  {
    "path": "Other/DocFX/_doc/index.md",
    "content": "---\nuid: index\n---\n\n# LibreAutomate\n\nLibreAutomate is a C# script editor and library for automation of tasks of any kind and complexity on Windows.\n\nSome features:\n- Automate desktop and web UI using keys, mouse and API. Find and click buttons, links, images.\n- Launch programs. Manage files and windows. Transfer and process text and other data.\n- Hotkeys, autotext and other triggers. Auto-replace/expand text when typing. Auto-close windows. Remap keys.\n- Custom toolbars that can be attached to windows or screen edges. And menus.\n- Custom dialog windows of any complexity.\n- The scripting language is C#. The app is a good way to learn it.\n- C# code editor with intellisense. Script manager, cookbook, debugger.\n- Tools for recording keyboard/mouse and selecting UI objects such as buttons, links and images.\n- Also you can use .NET, libraries from NuGet, Windows API, etc.\n- Can create independent exe programs and dll libraries.\n- Free and open-source. The automation library can be used in other programs too.\n\nWith all the libraries and tools, code-based automation can be easier and faster than no-code RPA.\n\nThis is an example of code generated by a tool. It finds and clicks a link.\n\n<pre style='background-color:#FFFFFF;border:#D1D7DC;border-style:solid;border-width:1px;padding-left:2px;line-height:normal;color:black;tab-size:4;font-family:\"Consolas\";'>\n<span style='color:#0000FF'>var</span> <span style='color:#204020'>w</span> <span style='color:#0000FF'>=</span> <span style='color:#0080C0'>wnd</span><span style='color:#0000FF'>.</span><span style='color:#000000;font-weight: bold'>find</span><span style='color:#000000'>(</span><span style='color:#804000'>1</span><span style='color:#000000'>,</span> <span style='color:#A07040'>&quot;LA/QM forum - Google Chrome&quot;</span><span style='color:#000000'>,</span> <span style='color:#A07040'>&quot;Chrome_WidgetWin_1&quot;</span><span style='color:#000000'>);</span>\n<span style='color:#0000FF'>var</span> <span style='color:#204020'>e</span> <span style='color:#0000FF'>=</span> <span style='color:#204020'>w</span><span style='color:#0000FF'>.</span><span style='color:#000000;font-weight: bold'>Elm</span><span style='color:#000000'>[</span><span style='color:#A07040'>&quot;web:LINK&quot;</span><span style='color:#000000'>,</span> <span style='color:#A07040'>&quot;Shared C# code&quot;</span><span style='color:#000000'>]</span><span style='color:#0000FF'>.</span><span style='color:#000000;font-weight: bold'>Find</span><span style='color:#000000'>(</span><span style='color:#804000'>1</span><span style='color:#000000'>);</span>\n<span style='color:#204020'>e</span><span style='color:#0000FF'>.</span><span style='color:#000000;font-weight: bold'>Invoke</span><span style='color:#000000'>();</span>\n</pre>\n\nAnother example of code created using tools. A hotkey trigger.\n\n<pre style='background-color:#FFFFFF;border:#D1D7DC;border-style:solid;border-width:1px;padding-left:2px;line-height:normal;color:black;tab-size:4;font-family:\"Consolas\";'>\nhk<span style='color:#000000'>[</span><span style='color:#A07040'>&quot;F7&quot;</span><span style='color:#000000'>]</span> <span style='color:#0000FF'>=</span> <span style='color:#204020'>o</span> <span style='color:#0000FF'>=&gt;</span> <span style='color:#0080C0'>script</span><span style='color:#0000FF'>.</span><span style='color:#000000;font-weight: bold'>run</span><span style='color:#000000'>(</span><span style='color:#A07040'>@&quot;\\My\\Youtube play random.cs&quot;</span><span style='color:#000000'>);</span>\n</pre>\n\n### Where can run\nWindows 7, 8, 8.1, 10, 11.\n\n### Download\n[Download](https://www.libreautomate.com/LibreAutomateSetup.exe) v1.15.0, 2025-12-08\n\nOr download from [GitHub](https://github.com/qgindi/LibreAutomate) Releases.\n\nThe setup program is ~40 MB. It may also install .NET 10 (~60 MB download).\n\nThe setup may be blocked by security software because it is new, not commonly downloaded, and not signed.\n\n### Links\n- [Forum](https://www.libreautomate.com/forum/)\n- <a href=\"mailto:info@llibreautomate.com\" onmousemove=\"if (this.dataset.mm == 1) this.href=this.href.replace(/ll/g, 'l'); this.dataset.mm = (this.dataset.mm || 0) + 1;\">Email</a>\n- [Source code](https://github.com/qgindi/LibreAutomate)\n- [Changes](https://github.com/qgindi/LibreAutomate/blob/master/Other/DocFX/_doc/changes/)\n- [Donate](https://github.com/sponsors/qgindi)\n- [Privacy policy](https://www.libreautomate.com/misc/privacy-policy.html)\n\n### Developers\n\nGintaras Didžgalvis, an independent software developer from Lithuania.\n\n### Why?\n\nThis software replaces [Quick Macros](xref:qm2). The same developer.\n\nThe goal is to provide automation software for Windows with these features:\n- Powerful library that includes UI automation, triggers, toolbars.\n- Script editor/manager application with tools for automation.\n- In scripts use the C# language and .NET libraries.\n- Free and open-source.\n\n"
  },
  {
    "path": "Other/DocFX/_doc/md-styles.css",
    "content": "\nhtml, body {\n    margin: 0;\n    padding-bottom: 10px\n}\n/* From https://github.com/sindresorhus/github-markdown-css */\n\n.markdown-body {\n    -ms-text-size-adjust: 100%;\n    -webkit-text-size-adjust: 100%;\n    margin: 0;\n    color: #24292f;\n    background-color: #fff;\n    font-family: -apple-system,BlinkMacSystemFont,\"Segoe UI\",Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\";\n    font-size: 14px;\n    line-height: 1.3;\n    word-wrap: break-word\n}\n\n    .markdown-body .octicon {\n        display: inline-block;\n        fill: currentColor;\n        vertical-align: text-bottom\n    }\n\n    .markdown-body h1:hover .anchor .octicon-link:before, .markdown-body h2:hover .anchor .octicon-link:before, .markdown-body h3:hover .anchor .octicon-link:before, .markdown-body h4:hover .anchor .octicon-link:before, .markdown-body h5:hover .anchor .octicon-link:before, .markdown-body h6:hover .anchor .octicon-link:before {\n        width: 16px;\n        height: 16px;\n        content: ' ';\n        display: inline-block;\n        background-color: currentColor;\n        -webkit-mask-image: url(\"data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' version='1.1' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg>\");\n        mask-image: url(\"data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' version='1.1' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg>\")\n    }\n\n    .markdown-body details, .markdown-body figcaption, .markdown-body figure {\n        display: block\n    }\n\n    .markdown-body summary {\n        display: list-item\n    }\n\n    .markdown-body [hidden] {\n        display: none !important\n    }\n\n    .markdown-body a {\n        background-color: transparent;\n        color: #0969da;\n        text-decoration: none\n    }\n\n        .markdown-body a:active, .markdown-body a:hover {\n            outline-width: 0\n        }\n\n    .markdown-body abbrMarkdown Preview {\n        border-bottom: none;\n        text-decoration: underline dotted\n    }\n\n    .markdown-body b, .markdown-body strong {\n        font-weight: 600\n    }\n\n    .markdown-body dfn {\n        font-style: italic\n    }\n\n    .markdown-body h1 {\n        margin: .67em 0;\n        font-weight: 600;\n        padding-bottom: .3em;\n        font-size: 2em;\n        border-bottom: 1px solid hsla(210,18%,87%,1)\n    }\n\n    .markdown-body mark {\n        background-color: #fff8c5;\n        color: #24292f\n    }\n\n    .markdown-body small {\n        font-size: 90%\n    }\n\n    .markdown-body sub, .markdown-body sup {\n        font-size: 75%;\n        line-height: 0;\n        position: relative;\n        vertical-align: baseline\n    }\n\n    .markdown-body sub {\n        bottom: -.25em\n    }\n\n    .markdown-body sup {\n        top: -.5em\n    }\n\n    .markdown-body img {\n        border-style: none;\n        max-width: 100%;\n        box-sizing: content-box;\n        background-color: #fff\n    }\n\n    .markdown-body code, .markdown-body kbd, .markdown-body pre, .markdown-body samp {\n        font-family: monospace,monospace;\n        font-size: 1em\n    }\n\n    .markdown-body figure {\n        margin: 1em 40px\n    }\n\n    .markdown-body hr {\n        box-sizing: content-box;\n        overflow: hidden;\n        background: 0 0;\n        border-bottom: 1px solid hsla(210,18%,87%,1);\n        height: .25em;\n        padding: 0;\n        margin: 24px 0;\n        background-color: #d0d7de;\n        border: 0\n    }\n\n    .markdown-body input {\n        font: inherit;\n        margin: 0;\n        overflow: visible;\n        font-family: inherit;\n        font-size: inherit;\n        line-height: inherit\n    }\n\n    .markdown-body [type=button], .markdown-body [type=reset], .markdown-body [type=submit] {\n        -webkit-appearance: button\n    }\n\n        .markdown-body [type=button]::-moz-focus-inner, .markdown-body [type=reset]::-moz-focus-inner, .markdown-body [type=submit]::-moz-focus-inner {\n            border-style: none;\n            padding: 0\n        }\n\n        .markdown-body [type=button]:-moz-focusring, .markdown-body [type=reset]:-moz-focusring, .markdown-body [type=submit]:-moz-focusring {\n            outline: 1px dotted ButtonText\n        }\n\n    .markdown-body [type=checkbox], .markdown-body [type=radio] {\n        box-sizing: border-box;\n        padding: 0\n    }\n\n    .markdown-body [type=number]::-webkit-inner-spin-button, .markdown-body [type=number]::-webkit-outer-spin-button {\n        height: auto\n    }\n\n    .markdown-body [type=search] {\n        -webkit-appearance: textfield;\n        outline-offset: -2px\n    }\n\n        .markdown-body [type=search]::-webkit-search-cancel-button, .markdown-body [type=search]::-webkit-search-decoration {\n            -webkit-appearance: none\n        }\n\n    .markdown-body ::-webkit-input-placeholder {\n        color: inherit;\n        opacity: .54\n    }\n\n    .markdown-body ::-webkit-file-upload-button {\n        -webkit-appearance: button;\n        font: inherit\n    }\n\n    .markdown-body a:hover {\n        text-decoration: underline\n    }\n\n    .markdown-body hr:before {\n        display: table;\n        content: \"\"\n    }\n\n    .markdown-body hr:after {\n        display: table;\n        clear: both;\n        content: \"\"\n    }\n\n    .markdown-body table {\n        border-spacing: 0;\n        border-collapse: collapse;\n        display: block;\n        width: max-content;\n        max-width: 100%;\n        overflow: auto\n    }\n\n    .markdown-body td, .markdown-body th {\n        padding: 0\n    }\n\n    .markdown-body details summary {\n        cursor: pointer\n    }\n\n    .markdown-body details:not([open]) > *:not(summary) {\n        display: none !important\n    }\n\n    .markdown-body kbd {\n        display: inline-block;\n        padding: 3px 5px;\n        font: 11px ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;\n        line-height: 10px;\n        color: #24292f;\n        vertical-align: middle;\n        background-color: #f6f8fa;\n        border: solid 1px rgba(175,184,193,.2);\n        border-bottom-color: rgba(175,184,193,.2);\n        border-radius: 6px;\n        box-shadow: inset 0 -1px 0 rgba(175,184,193,.2)\n    }\n\n    .markdown-body h1, .markdown-body h2, .markdown-body h3, .markdown-body h4, .markdown-body h5, .markdown-body h6 {\n        margin-top: 24px;\n        margin-bottom: 16px;\n        font-weight: 600;\n        line-height: 1.25\n    }\n\n    .markdown-body h2 {\n        font-weight: 600;\n        padding-bottom: .3em;\n        font-size: 1.5em;\n        border-bottom: 1px solid hsla(210,18%,87%,1)\n    }\n\n    .markdown-body h3 {\n        font-weight: 600;\n        font-size: 1.25em\n    }\n\n    .markdown-body h4 {\n        font-weight: 600;\n        font-size: 1em\n    }\n\n    .markdown-body h5 {\n        font-weight: 600;\n        font-size: .875em\n    }\n\n    .markdown-body h6 {\n        font-weight: 600;\n        font-size: .85em;\n        color: #57606a\n    }\n\n    .markdown-body p {\n        margin-top: 0;\n        margin-bottom: 10px\n    }\n\n    .markdown-body blockquote {\n        margin: 0;\n        padding: 0 1em;\n        color: #57606a;\n        border-left: .25em solid #d0d7de\n    }\n\n    .markdown-body ul {\n        list-style: disc\n    }\n\n    .markdown-body ul, .markdown-body ol {\n        margin-top: 0;\n        margin-bottom: 0;\n        padding-left: 2em\n    }\n\n        .markdown-body ol ol, .markdown-body ul ol {\n            list-style-type: lower-roman\n        }\n\n            .markdown-body ul ul ol, .markdown-body ul ol ol, .markdown-body ol ul ol, .markdown-body ol ol ol {\n                list-style-type: lower-alpha\n            }\n\n    .markdown-body dd {\n        margin-left: 0\n    }\n\n    .markdown-body tt, .markdown-body code {\n        font-family: ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;\n        font-size: 12px\n    }\n\n    .markdown-body pre {\n        margin-top: 0;\n        margin-bottom: 0;\n        font-family: ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;\n        font-size: 12px;\n        word-wrap: normal\n    }\n\n    .markdown-body .octicon {\n        display: inline-block;\n        overflow: visible !important;\n        vertical-align: text-bottom;\n        fill: currentColor\n    }\n\n    .markdown-body ::placeholder {\n        color: #6e7781;\n        opacity: 1\n    }\n\n    .markdown-body input::-webkit-outer-spin-button, .markdown-body input::-webkit-inner-spin-button {\n        margin: 0;\n        -webkit-appearance: none;\n        appearance: none\n    }\n\n    .markdown-body .pl-c {\n        color: #6e7781\n    }\n\n    .markdown-body .pl-c1, .markdown-body .pl-s .pl-v {\n        color: #0550ae\n    }\n\n    .markdown-body .pl-e, .markdown-body .pl-en {\n        color: #8250df\n    }\n\n    .markdown-body .pl-smi, .markdown-body .pl-s .pl-s1 {\n        color: #24292f\n    }\n\n    .markdown-body .pl-ent {\n        color: #116329\n    }\n\n    .markdown-body .pl-k {\n        color: #cf222e\n    }\n\n    .markdown-body .pl-s, .markdown-body .pl-pds, .markdown-body .pl-s .pl-pse .pl-s1, .markdown-body .pl-sr, .markdown-body .pl-sr .pl-cce, .markdown-body .pl-sr .pl-sre, .markdown-body .pl-sr .pl-sra {\n        color: #0a3069\n    }\n\n    .markdown-body .pl-v, .markdown-body .pl-smw {\n        color: #953800\n    }\n\n    .markdown-body .pl-bu {\n        color: #82071e\n    }\n\n    .markdown-body .pl-ii {\n        color: #f6f8fa;\n        background-color: #82071e\n    }\n\n    .markdown-body .pl-c2 {\n        color: #f6f8fa;\n        background-color: #cf222e\n    }\n\n    .markdown-body .pl-sr .pl-cce {\n        font-weight: 700;\n        color: #116329\n    }\n\n    .markdown-body .pl-ml {\n        color: #3b2300\n    }\n\n    .markdown-body .pl-mh, .markdown-body .pl-mh .pl-en, .markdown-body .pl-ms {\n        font-weight: 700;\n        color: #0550ae\n    }\n\n    .markdown-body .pl-mi {\n        font-style: italic;\n        color: #24292f\n    }\n\n    .markdown-body .pl-mb {\n        font-weight: 700;\n        color: #24292f\n    }\n\n    .markdown-body .pl-md {\n        color: #82071e;\n        background-color: #ffebe9\n    }\n\n    .markdown-body .pl-mi1 {\n        color: #116329;\n        background-color: #dafbe1\n    }\n\n    .markdown-body .pl-mc {\n        color: #953800;\n        background-color: #ffd8b5\n    }\n\n    .markdown-body .pl-mi2 {\n        color: #eaeef2;\n        background-color: #0550ae\n    }\n\n    .markdown-body .pl-mdr {\n        font-weight: 700;\n        color: #8250df\n    }\n\n    .markdown-body .pl-ba {\n        color: #57606a\n    }\n\n    .markdown-body .pl-sg {\n        color: #8c959f\n    }\n\n    .markdown-body .pl-corl {\n        text-decoration: underline;\n        color: #0a3069\n    }\n\n    .markdown-body [data-catalyst] {\n        display: block\n    }\n\n    .markdown-body g-emoji {\n        font-family: \"Apple Color Emoji\",\"Segoe UI Emoji\",\"Segoe UI Symbol\";\n        font-size: 1em;\n        font-style: normal !important;\n        font-weight: 400;\n        line-height: 1;\n        vertical-align: -.075em\n    }\n\n        .markdown-body g-emoji img {\n            width: 1em;\n            height: 1em\n        }\n\n    .markdown-body:before {\n        display: table;\n        content: \"\"\n    }\n\n    .markdown-body:after {\n        display: table;\n        clear: both;\n        content: \"\"\n    }\n\n    .markdown-body > *:first-child {\n        margin-top: 0 !important\n    }\n\n    .markdown-body > *:last-child {\n        margin-bottom: 0 !important\n    }\n\n    .markdown-body a:not([href]) {\n        color: inherit;\n        text-decoration: none\n    }\n\n    .markdown-body .absent {\n        color: #cf222e\n    }\n\n    .markdown-body .anchor {\n        float: left;\n        padding-right: 4px;\n        margin-left: -20px;\n        line-height: 1\n    }\n\n        .markdown-body .anchor:focus {\n            outline: none\n        }\n\n    .markdown-body p, .markdown-body blockquote, .markdown-body ul, .markdown-body ol, .markdown-body dl, .markdown-body table, .markdown-body pre, .markdown-body details {\n        margin-top: 0;\n        margin-bottom: 16px\n    }\n\n        .markdown-body blockquote > :first-child {\n            margin-top: 0\n        }\n\n        .markdown-body blockquote > :last-child {\n            margin-bottom: 0\n        }\n\n    .markdown-body sup > a:before {\n        content: \"[\"\n    }\n\n    .markdown-body sup > a:after {\n        content: \"]\"\n    }\n\n    .markdown-body h1 .octicon-link, .markdown-body h2 .octicon-link, .markdown-body h3 .octicon-link, .markdown-body h4 .octicon-link, .markdown-body h5 .octicon-link, .markdown-body h6 .octicon-link {\n        color: #24292f;\n        vertical-align: middle;\n        visibility: hidden\n    }\n\n    .markdown-body h1:hover .anchor, .markdown-body h2:hover .anchor, .markdown-body h3:hover .anchor, .markdown-body h4:hover .anchor, .markdown-body h5:hover .anchor, .markdown-body h6:hover .anchor {\n        text-decoration: none\n    }\n\n        .markdown-body h1:hover .anchor .octicon-link, .markdown-body h2:hover .anchor .octicon-link, .markdown-body h3:hover .anchor .octicon-link, .markdown-body h4:hover .anchor .octicon-link, .markdown-body h5:hover .anchor .octicon-link, .markdown-body h6:hover .anchor .octicon-link {\n            visibility: visible\n        }\n\n    .markdown-body h1 tt, .markdown-body h1 code, .markdown-body h2 tt, .markdown-body h2 code, .markdown-body h3 tt, .markdown-body h3 code, .markdown-body h4 tt, .markdown-body h4 code, .markdown-body h5 tt, .markdown-body h5 code, .markdown-body h6 tt, .markdown-body h6 code {\n        padding: 0 .2em;\n        font-size: inherit\n    }\n\n    .markdown-body ul.no-list, .markdown-body ol.no-list {\n        padding: 0;\n        list-style-type: none\n    }\n\n    .markdown-body ol[type=\"1\"] {\n        list-style-type: decimal\n    }\n\n    .markdown-body ol[type=a] {\n        list-style-type: lower-alpha\n    }\n\n    .markdown-body ol[type=i] {\n        list-style-type: lower-roman\n    }\n\n    .markdown-body div > ol:not([type]) {\n        list-style-type: decimal\n    }\n\n    .markdown-body ul ul, .markdown-body ul ol, .markdown-body ol ol, .markdown-body ol ul {\n        margin-top: 0;\n        margin-bottom: 0\n    }\n\n    .markdown-body li > p {\n        margin-top: 16px\n    }\n\n    .markdown-body li + li {\n        margin-top: .25em\n    }\n\n    .markdown-body dl {\n        padding: 0\n    }\n\n        .markdown-body dl dt {\n            padding: 0;\n            margin-top: 16px;\n            font-size: 1em;\n            font-style: italic;\n            font-weight: 600\n        }\n\n        .markdown-body dl dd {\n            padding: 0 16px;\n            margin-bottom: 16px\n        }\n\n    .markdown-body table th {\n        font-weight: 600\n    }\n\n    .markdown-body table th, .markdown-body table td {\n        padding: 6px 13px;\n        border: 1px solid #d0d7de\n    }\n\n    .markdown-body table tr {\n        background-color: #fff;\n        border-top: 1px solid hsla(210,18%,87%,1)\n    }\n\n        .markdown-body table tr:nth-child(2n) {\n            background-color: #f6f8fa\n        }\n\n    .markdown-body table img {\n        background-color: transparent\n    }\n\n    .markdown-body img[align=right] {\n        padding-left: 20px\n    }\n\n    .markdown-body img[align=left] {\n        padding-right: 20px\n    }\n\n    .markdown-body .emoji {\n        max-width: none;\n        vertical-align: text-top;\n        background-color: transparent\n    }\n\n    .markdown-body span.frame {\n        display: block;\n        overflow: hidden\n    }\n\n        .markdown-body span.frame > span {\n            display: block;\n            float: left;\n            width: auto;\n            padding: 7px;\n            margin: 13px 0 0;\n            overflow: hidden;\n            border: 1px solid #d0d7de\n        }\n\n        .markdown-body span.frame span img {\n            display: block;\n            float: left\n        }\n\n        .markdown-body span.frame span span {\n            display: block;\n            padding: 5px 0 0;\n            clear: both;\n            color: #24292f\n        }\n\n    .markdown-body span.align-center {\n        display: block;\n        overflow: hidden;\n        clear: both\n    }\n\n        .markdown-body span.align-center > span {\n            display: block;\n            margin: 13px auto 0;\n            overflow: hidden;\n            text-align: center\n        }\n\n        .markdown-body span.align-center span img {\n            margin: 0 auto;\n            text-align: center\n        }\n\n    .markdown-body span.align-right {\n        display: block;\n        overflow: hidden;\n        clear: both\n    }\n\n        .markdown-body span.align-right > span {\n            display: block;\n            margin: 13px 0 0;\n            overflow: hidden;\n            text-align: right\n        }\n\n        .markdown-body span.align-right span img {\n            margin: 0;\n            text-align: right\n        }\n\n    .markdown-body span.float-left {\n        display: block;\n        float: left;\n        margin-right: 13px;\n        overflow: hidden\n    }\n\n        .markdown-body span.float-left span {\n            margin: 13px 0 0\n        }\n\n    .markdown-body span.float-right {\n        display: block;\n        float: right;\n        margin-left: 13px;\n        overflow: hidden\n    }\n\n        .markdown-body span.float-right > span {\n            display: block;\n            margin: 13px auto 0;\n            overflow: hidden;\n            text-align: right\n        }\n\n    .markdown-body code, .markdown-body tt {\n        padding: .2em .4em;\n        margin: 0;\n        font-size: 85%;\n        background-color: rgba(175,184,193,.2);\n        border-radius: 6px\n    }\n\n        .markdown-body code br, .markdown-body tt br {\n            display: none\n        }\n\n    .markdown-body del code {\n        text-decoration: inherit\n    }\n\n    .markdown-body pre code {\n        font-size: 100%\n    }\n\n    .markdown-body pre > code {\n        padding: 0;\n        margin: 0;\n        word-break: normal;\n        white-space: pre;\n        background: 0 0;\n        border: 0\n    }\n\n    .markdown-body .highlight {\n        margin-bottom: 16px\n    }\n\n        .markdown-body .highlight pre {\n            margin-bottom: 0;\n            word-break: normal\n        }\n\n        .markdown-body .highlight pre, .markdown-body pre {\n            padding: 16px;\n            overflow: auto;\n            font-size: 85%;\n            line-height: 1.45;\n            background-color: #f6f8fa;\n            border-radius: 6px\n        }\n\n            .markdown-body pre code, .markdown-body pre tt {\n                display: inline;\n                max-width: auto;\n                padding: 0;\n                margin: 0;\n                overflow: visible;\n                line-height: inherit;\n                word-wrap: normal;\n                background-color: transparent;\n                border: 0\n            }\n\n    .markdown-body .csv-data td, .markdown-body .csv-data th {\n        padding: 5px;\n        overflow: hidden;\n        font-size: 12px;\n        line-height: 1;\n        text-align: left;\n        white-space: nowrap\n    }\n\n    .markdown-body .csv-data .blob-num {\n        padding: 10px 8px 9px;\n        text-align: right;\n        background: #fff;\n        border: 0\n    }\n\n    .markdown-body .csv-data tr {\n        border-top: 0\n    }\n\n    .markdown-body .csv-data th {\n        font-weight: 600;\n        background: #f6f8fa;\n        border-top: 0\n    }\n\n    .markdown-body .footnotes {\n        font-size: 12px;\n        color: #57606a;\n        border-top: 1px solid #d0d7de\n    }\n\n        .markdown-body .footnotes ol {\n            padding-left: 16px\n        }\n\n        .markdown-body .footnotes li {\n            position: relative\n        }\n\n            .markdown-body .footnotes li:target:before {\n                position: absolute;\n                top: -8px;\n                right: -8px;\n                bottom: -8px;\n                left: -24px;\n                pointer-events: none;\n                content: \"\";\n                border: 2px solid #0969da;\n                border-radius: 6px\n            }\n\n            .markdown-body .footnotes li:target {\n                color: #24292f\n            }\n\n        .markdown-body .footnotes .data-footnote-backref g-emoji {\n            font-family: monospace\n        }\n\n    .markdown-body .task-list-item {\n        list-style-type: none\n    }\n\n        .markdown-body .task-list-item label {\n            font-weight: 400\n        }\n\n        .markdown-body .task-list-item.enabled label {\n            cursor: pointer\n        }\n\n        .markdown-body .task-list-item + .task-list-item {\n            margin-top: 3px\n        }\n\n        .markdown-body .task-list-item .handle {\n            display: none\n        }\n\n    .markdown-body .task-list-item-checkbox {\n        margin: 0 .2em .25em -1.6em;\n        vertical-align: middle\n    }\n\n    .markdown-body .contains-task-list:dir(rtl) .task-list-item-checkbox {\n        margin: 0 -1.6em .25em .2em\n    }\n\n    .markdown-body ::-webkit-calendar-picker-indicator {\n        filter: invert(50%)\n    }\n/* PrismJS 1.25.0\nhttps://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript+aspnet+basic+c+csharp+cpp+clojure+dart+docker+fsharp+go+graphql+java+json+kotlin+markdown+markup-templating+perl+php+powershell+protobuf+python+qsharp+r+cshtml+ruby+rust+scss+sql+typescript+vbnet+visual-basic+yaml */\ncode[class*=language-], pre[class*=language-] {\n    color: #000;\n    background: 0 0;\n    text-shadow: 0 1px #fff;\n    font-family: Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;\n    font-size: 1em;\n    text-align: left;\n    white-space: pre;\n    word-spacing: normal;\n    word-break: normal;\n    word-wrap: normal;\n    line-height: 1.5;\n    -moz-tab-size: 4;\n    -o-tab-size: 4;\n    tab-size: 4;\n    -webkit-hyphens: none;\n    -moz-hyphens: none;\n    -ms-hyphens: none;\n    hyphens: none\n}\n\n    code[class*=language-] ::-moz-selection, code[class*=language-]::-moz-selection, pre[class*=language-] ::-moz-selection, pre[class*=language-]::-moz-selection {\n        text-shadow: none;\n        background: #b3d4fc\n    }\n\n    code[class*=language-] ::selection, code[class*=language-]::selection, pre[class*=language-] ::selection, pre[class*=language-]::selection {\n        text-shadow: none;\n        background: #b3d4fc\n    }\n\n@media print {\n    code[class*=language-], pre[class*=language-] {\n        text-shadow: none\n    }\n}\n\npre[class*=language-] {\n    padding: 1em;\n    margin: .5em 0;\n    overflow: auto\n}\n\n:not(pre) > code[class*=language-], pre[class*=language-] {\n    background: #f5f2f0\n}\n\n:not(pre) > code[class*=language-] {\n    padding: .1em;\n    border-radius: .3em;\n    white-space: normal\n}\n\n.token.cdata, .token.comment, .token.doctype, .token.prolog {\n    color: #708090\n}\n\n.token.punctuation {\n    color: #999\n}\n\n.token.namespace {\n    opacity: .7\n}\n\n.token.boolean, .token.constant, .token.deleted, .token.number, .token.property, .token.symbol, .token.tag {\n    color: #905\n}\n\n.token.attr-name, .token.builtin, .token.char, .token.inserted, .token.selector, .token.string {\n    color: #690\n}\n\n.language-css .token.string, .style .token.string, .token.entity, .token.operator, .token.url {\n    color: #9a6e3a;\n    background: hsla(0,0%,100%,.5)\n}\n\n.token.atrule, .token.attr-value, .token.keyword {\n    color: #07a\n}\n\n.token.class-name, .token.function {\n    color: #dd4a68\n}\n\n.token.important, .token.regex, .token.variable {\n    color: #e90\n}\n\n.token.bold, .token.important {\n    font-weight: 700\n}\n\n.token.italic {\n    font-style: italic\n}\n\n.token.entity {\n    cursor: help\n}\n"
  },
  {
    "path": "Other/DocFX/_doc/misc/privacy-policy.md",
    "content": "# Privacy Policy\n\nThis Privacy Policy explains how libreautomate.com (\"we\", \"us\", \"our\") collects, uses, and protects your personal information.\n\n### Information We Collect\n\n#### Forum accounts\nWhen you create an account on our forum, we collect:\n- Username\n- Email address\n- Password (stored securely)\n- If you choose to register or log in through a social provider (for example, Google), we also receive your name and email address from that provider\n\n#### Cookies\nWe use cookies to:\n- Keep you signed in to your forum account\n- Remember your forum settings\n- Maintain forum session security\n\n### How We Use Information\n\nWe use collected data to:\n- Provide access to the forum\n- Maintain user accounts and preferences on the forum\n- Notify users about new posts on the forum, if they have opted in\n\n### Sharing of Information\n\nThe website is hosted by Hostinger and uses the MyBB forum software to operate the forum. These services may have technical access to data but do not use it for their own purposes.\n\nWe do not share personal data with third parties for marketing purposes.\n\nIf required by law, we may disclose information to comply with legal obligations or lawful requests from authorities.\n\n### Data Retention and Your Rights\n\nWe retain forum account information as long as your account is active.\nYou can request deletion of your account and associated personal data at any time by contacting us.\nYou can edit your personal information in the forum's User Control Panel.\n\nDepending on your location, you may have additional rights under data-protection laws, such as the right to access your data, request correction, or withdraw consent.\n\n### Security\n\nWe take reasonable measures to protect personal data from unauthorized access, alteration, disclosure, or destruction.\n\n### Software Downloads\n\nThe software available for download on this website does not collect any personal information from users.\n\n### Changes to This Privacy Policy\n\nWe may update this Privacy Policy from time to time. The latest version will always be available on this page.\n\nLast updated: 2025-10-15\n\n### Contact\n\nFor any questions regarding this Privacy Policy or any requests regarding your personal data, please contact us at \n[qmgindi@gmail.com](mailto:qmgindi@gmail.com).\n\nThe website is owned and operated by Gintaras Didžgalvis.\n"
  },
  {
    "path": "Other/DocFX/_doc/template1/layout/_master.tmpl",
    "content": "{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}}\n{{!include(/^styles/.*/)}}\n{{!include(/^fonts/.*/)}}\n{{!include(favicon.ico)}}\n{{!include(logo.svg)}}\n{{!include(search-stopwords.json)}}\n<!DOCTYPE html>\n<!--[if IE]><![endif]-->\n<html>\n  {{>partials/head}}\n  <body data-spy=\"scroll\" data-target=\"#affix\" data-offset=\"80\">\n    <a name=\"top\"></a>\n    <div id=\"wrapper\">\n      <header>\n        {{^_disableNavbar}}\n        {{>partials/navbar}}\n        {{/_disableNavbar}}\n        {{^_disableBreadcrumb}}\n        {{>partials/breadcrumb}}\n        {{/_disableBreadcrumb}}\n      </header>\n      {{#_enableSearch}}\n      <div class=\"container body-content\">\n        {{>partials/searchResults}}\n      </div>\n      {{/_enableSearch}}\n      <div role=\"main\" class=\"container-fluid body-content hide-when-search\">\n      {{^_disableToc}}\n        {{>partials/toc}}\n        <div class=\"article row grid-right\">\n      {{/_disableToc}}\n      {{#_disableToc}}\n        <div class=\"article row grid\">\n        {{/_disableToc}}\n          {{#_disableAffix}}\n          <div class=\"col-md-12\">\n          {{/_disableAffix}}\n          {{^_disableAffix}}\n          <div class=\"col-md-10\">\n          {{/_disableAffix}}\n            <article class=\"content wrap\" id=\"_content\" data-uid=\"{{uid}}\">\n                {{!body}}\n            </article>\n          </div>\n          {{^_disableAffix}}\n          {{>partials/affix}}\n          {{/_disableAffix}}\n        </div>\n      </div>\n      {{^_disableFooter}}\n      {{>partials/footer}}\n      {{/_disableFooter}}\n    </div>\n    {{>partials/scripts}}\n  </body>\n</html>\n"
  },
  {
    "path": "Other/DocFX/_doc/template1/mod.txt",
    "content": "Using 3 templates:\ntemplate1 - modified \"mathew\" template. It is included in the DocFX \"Templates\" webpage. Downloaded from github. Mostly changes style.\ntemplate2 - styles to change in member pages. Copied from mathew and modified.\n\nCurrently not using the affix. It is the dynamic links at the right.\n\nNote: after copy/paste in VS, undo autoformatting done by VS. It damages <pre>.\n\nUnsuccessfully tried to create a plugin.\n\tFollowed the tutorial, created the plugin, copied the output files to the plugins folder of template1.\n\tError when DocFX runs: Error:[ImportPlugins]Error when get composition container: Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information., loader exceptions: Could not load file\n\n"
  },
  {
    "path": "Other/DocFX/_doc/template1/partials/class.header.tmpl.partial",
    "content": "<h1 id=\"{{id}}\" data-uid=\"{{uid}}\" class=\"text-break\">{{>partials/title}}</h1>\n\n<div class=\"markdown level0 summary\">{{{summary}}}</div>\n<div class=\"markdown level0 conceptual\">{{{conceptual}}}</div>\n\n<div class=\"codewrapper\">\n  <pre><code class=\"lang-{{_lang}} hljs\">{{syntax.content.0.value}}</code></pre>\n</div>\n\n{{#syntax.typeParameters.0}}\n<h5 class=\"typeParameters\">{{__global.typeParameters}}</h5>\n<table class=\"table table-bordered table-condensed\">\n  <thead>\n    <tr>\n      <th>{{__global.name}}</th>\n      <th>{{__global.description}}</th>\n    </tr>\n  </thead>\n  <tbody>\n{{/syntax.typeParameters.0}}\n{{#syntax.typeParameters}}\n    <tr>\n      <td><span class=\"parametername\">{{{id}}}</span></td>\n      <td>{{{description}}}</td>\n    </tr>\n{{/syntax.typeParameters}}\n{{#syntax.typeParameters.0}}\n  </tbody>\n</table>\n{{/syntax.typeParameters.0}}\n\n{{#remarks}}\n<h5 id=\"{{id}}_remarks\"><strong>{{__global.remarks}}</strong></h5>\n<div class=\"markdown level0 remarks\">{{{remarks}}}</div>\n{{/remarks}}\n{{#example.0}}\n<h5 id=\"{{id}}_examples\"><strong>{{__global.examples}}</strong></h5>\n{{/example.0}}\n{{#example}}\n{{{.}}}\n{{/example}}\n\n<hr/>\n\n<h6><strong>{{__global.namespace}}</strong>: {{{namespace.specName.0.value}}}</h6>\n<h6><strong>{{__global.assembly}}</strong>: {{assemblies.0}}.dll</h6>\n\n{{#inClass}}\n<div class=\"inheritance\">\n  <h5>{{__global.inheritance}}</h5>\n  {{#inheritance}}\n  <div class=\"level{{index}}\">{{{specName.0.value}}}</div>\n  {{/inheritance}}\n  <div class=\"level{{level}}\"><span class=\"xref\">{{name.0.value}}</span></div>\n  {{#derivedClasses}}\n    <div class=\"level{{index}}\">{{{specName.0.value}}}</div>\n  {{/derivedClasses}}\n</div>\n\n{{#inheritedMembers.0}}\n<div class=\"inheritedMembers\">\n  <h5>{{__global.inheritedMembers}}</h5>\n{{/inheritedMembers.0}}\n{{#inheritedMembers}}\n  <div>\n  {{#definition}}\n    <xref uid=\"{{definition}}\" text=\"{{nameWithType.0.value}}\" alt=\"{{fullName.0.value}}\"/>\n  {{/definition}}\n  {{^definition}}\n    <xref uid=\"{{uid}}\" text=\"{{nameWithType.0.value}}\" alt=\"{{fullName.0.value}}\"/>\n  {{/definition}}\n  </div>\n{{/inheritedMembers}}\n{{#inheritedMembers.0}}\n</div>\n{{/inheritedMembers.0}}\n{{/inClass}}\n"
  },
  {
    "path": "Other/DocFX/_doc/template1/partials/footer.tmpl.partial",
    "content": ""
  },
  {
    "path": "Other/DocFX/_doc/template1/partials/logo.tmpl.partial",
    "content": ""
  },
  {
    "path": "Other/DocFX/_doc/template1/partials/namespace.tmpl.partial",
    "content": "{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}}\n\n<h1 id=\"{{id}}\" data-uid=\"{{uid}}\" class=\"text-break\">{{>partials/title}}</h1>\n<div class=\"markdown level0 summary\">{{{summary}}}</div>\n<div class=\"markdown level0 conceptual\">{{{conceptual}}}</div>\n<div class=\"markdown level0 remarks\">{{{remarks}}}</div>\n{{#children}}\n  <h2 id=\"{{id}}\">{{>partials/namespaceSubtitle}}</h2>\n  {{#children}}\n    <h5 class=\"ns\"><xref uid=\"{{uid}}\" altProperty=\"fullName\" displayProperty=\"name\"/></h5>\n    <section>{{{summary}}}</section>\n  {{/children}}\n{{/children}}\n"
  },
  {
    "path": "Other/DocFX/_doc/template1/partials/navbar.tmpl.partial",
    "content": "<nav id=\"autocollapse\" class=\"navbar navbar-inverse ng-scope\" role=\"navigation\">\n\t<div class=\"container-fluid\">\n\t\t<div class=\"navbar-header\">\n\t\t\t<button type=\"button\" class=\"navbar-toggle\" data-toggle=\"collapse\" data-target=\"#navbar\">\n\t\t\t\t<span class=\"sr-only\">Toggle navigation</span>\n\t\t\t\t<span class=\"icon-bar\"></span>\n\t\t\t\t<span class=\"icon-bar\"></span>\n\t\t\t\t<span class=\"icon-bar\"></span>\n\t\t\t</button>\n\t\t\t{{>partials/logo}}\n\t\t</div>\n\t\t<div class=\"collapse navbar-collapse\" id=\"navbar\">\n\t\t</div>\n\t</div>\n</nav>\n"
  },
  {
    "path": "Other/DocFX/_doc/template1/styles/main.css",
    "content": "/* Clickability fix for selector on sm devices */\n@media (min-width: 768px) and (max-width: 991px) {\n  article h1:first-of-type:before {\n    height: 0;\n    margin-top: 0;\n  }  \n}\n\n.sidetoc,\nbody .toc,\n.sidefilter,\n.sidetoggle { \n  background-color: #e5ead3; \n}\n.toc .nav > li > a {\n    color: #444;\n}\n\n.sidenav,\n.toc-toggle {\n  padding: 0;\n}\n\narticle h4 {\n  border-bottom: none;\n}\n\n@media (min-width: 768px) {\n  .sidetoc, .sidefilter {\n    margin-left: -15px;\n  }\n}\n\n@media (max-width: 767px) {\n  .navbar-collapse {\n    text-align: center !important;\n  }\n\n  .navbar-collapse li .active {\n    border-radius: 20px;\n  }\n}\n\n\n/* Navbar Hamburger\n   ------------------------------------------------------- */\n.icon-bar {\n    transition: 0.4s;\n}\n\n/* Rotate first bar */\n.change .icon-bar:nth-of-type(2) {\n    transform: rotate(-45deg) translate(-4px, 5px) ;\n}\n\n/* Fade out the second bar */\n.change .icon-bar:nth-of-type(3) {\n    opacity: 0;\n}\n\n/* Rotate last bar */\n.change .icon-bar:nth-of-type(4) {\n    transform: rotate(45deg) translate(-4px, -5px) ;\n}\n\n\n/* Au\n   ------------------------------------------------------- */\n\n.sidefilter,\n.sidetoggle {\n    top: 40px;\n}\n.sidetoc {\n    top: 94px;\n}\n.sidetoc.shiftup {\n    bottom: 0;\n}\n.toc {\n    margin-bottom: 10px;\n}\n\n.toc-filter > .filter-icon {\n    top: 6px;\n}\n\n.toc-filter > .clear-icon {\n    top: 6px;\n}\n.toc-filter {\n    border-radius: 2px;\n    padding: 0px 2px 2px 2px;\n    min-height: 24px;\n}\n\n.article {\n    margin-top: 40px;\n    margin-bottom: 40px;\n}\n.article.grid-right {\n    margin-left: 260px;\n\tmargin-right: 0px;\n}\n@media only screen and (max-width: 768px) {\n  .article.grid-right {\n    margin-top: 0 !important;\n    margin-bottom: 20px;\n    margin-left: 0;\n  }\n}\n\narticle h1 {\n    margin-top: 14px;\n    margin-bottom: 16px;\n\tborder-bottom: 1px solid #eaecef;\n\tpadding-bottom: 8px;\n    font-size: 2em;\n    font-weight: 600;\n}\n\narticle h2 {\n    font-size: 1.5em;\n    font-weight: 600;\n\tborder-bottom: 1px solid #eaecef;\n\tpadding-bottom: 4px;\n}\n\narticle h3 {\n    font-size: 1.5em;\n    font-weight: 600;\n}\n\narticle h4 {\n    font-size: 1.2em;\n    font-weight: bold;\n}\n\narticle h5 {\n    border-bottom: none;\n    font-weight: bold;\n    margin-top: 14px;\n    margin-bottom: 7px;\n}\n\n/* See namespace.tmpl.partial. Initially the links were <h3>, but does not look good after changing <h3> style. */\nh5.ns {\n    margin-top: 10px;\n    margin-bottom: 2px;\n}\n\n/* See collection.tmpl.partial. Before each overload we insert a tall <hr> and <h2>Overload</h2>, but hide the first. */\nhr.overload {\n\tmargin-top: 50px;\n}\nhr.overload:first-of-type,h2.overload:first-of-type {\n\tdisplay: none;\n}\n\ntd p { margin-bottom: 10px; }\n\ntd > p:last-child, td > div > p:last-child, td > ul:last-child, td > ol:last-child, td > div > ul:last-child, td > div > ol:last-child {\n  margin-bottom: 0;\n}\n\n/* in 2-column tables let the first column have width 25% */\ntr td:first-child+td:last-child {\n\twidth: 75%;\n}\n\n/* break adjacent lines like in XML comments, but collapse multiple spaces and tabs (eg indentation made by XML formatting) */\np { white-space: pre-line; }\n\npre {\n    white-space: pre;\n\ttab-size: 4;\n}\n\ndiv.codewrapper pre {\n    background-color: #ffffe7;\n    white-space: pre-line;\n    word-wrap: normal;\n    word-break: normal;\n}\n\ncode {\n    color: #208020;\n    background-color: #f8f8f8;\n    border: 1px solid #eee;\n    border-radius: 2px;\n    padding: 1px 4px;\n}\npre code {\n    color: inherit;\n    background-color: transparent;\n    border: none;\n    border-radius: 0;\n    padding: 0;\n    white-space: inherit;\n}\n\nspan.parametername {\n    font-style: italic;\n    font-weight: bold;\n}\n\n.toc ul {\n    font-size: inherit;\n}\n.nav.level3 {\n    margin-left: 14px;\n}\n\n.navbar-inverse {\n    background-color: #175499;\n    border-color: #083868;\n}\n.navbar {\n    min-height: 40px;\n}\n.navbar-nav > li > a {\n    padding-top: 10px;\n    padding-bottom: 10px;\n}\n.navbar-inverse .navbar-nav > li > a, .navbar-inverse .navbar-text {\n    color: #e0f0e8;\n    font-size: 16px;\n}\n.navbar-inverse .navbar-nav > .active > a, .navbar-inverse .navbar-nav > .active > a:focus, .navbar-inverse .navbar-nav > .active > a:hover {\n    color: #fff;\n    background-color: #3770b1;\n}\n.navbar-inverse .navbar-toggle:focus {\n    background-color: inherit;\n}\n.navbar-inverse .navbar-toggle:hover {\n    background-color: #3770b1;\n}\n@media (min-width:768px){\n.navbar-form {\n    margin-bottom: 0;\n}\n}\n@media (max-width:767px){\n.navbar-form {\n    border-top: 0;\n    margin-top: 0;\n}\n}\n\n.googleButton {\n    padding: 1px 10px 2px 10px;\n    background-color: #3770b1;\n    border: 1px solid #333;\n    border-radius: 4px;\n    color: #e0f0e8;\n}\n.googleButton:hover {\n    color: #fff;\n    background-color: #4784cb;\n    border-color: #333;\n}\n.googleEdit {\n    border:none;\n    border-radius: 2px;\n\tpadding-bottom: 2px;\n\tmin-height: 23px;\n}\n\n.alert {\n    padding: 10px;\n    margin-bottom: 10px;\n}\n\n/* remove table striping */\n.table-striped>tbody>tr:nth-of-type(odd){background-color:#ffffff}\n\n.collapse.in, .collapsing {\n    text-align: left;\n}\n\n.table-bordered > thead > tr > th {\n  background-color: #f0f0f0;\n  border-bottom-width: 1px;\n}\n\n/* after upgrading DocFX, don't know who modifies default docfx.css and makes Parameters etc gray. Change it. */\n.decalaration, .fieldValue, .parameters, .returns, .exceptions {\n    color: #333;\n}\n\n/* Summary of classes. Was 2em. */\n.level0.summary {\n    margin: 1em 0 1em 0;\n}\n"
  },
  {
    "path": "Other/DocFX/_doc/template1/styles/main.js",
    "content": "// Anchor scroll workaround.\n$(document).ready(function () {\n    if (window.location.hash) {\n        $('html, body').animate({\n            scrollTop: $(window.location.hash).offset().top - 50\n        }, 1);\n    }\n});\n\n// Navbar Hamburger\n$(function () {\n    $(\".navbar-toggle\").click(function () {\n        $(this).toggleClass(\"change\");\n    })\n})\n"
  },
  {
    "path": "Other/DocFX/_doc/template2/partials/collection.tmpl.partial",
    "content": "{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}}\n\n<h1 id=\"{{id}}\" data-uid=\"{{uid}}\">{{>partials/title}}</h1>\n\n{{#children}}\n{{#children}}\n<hr class=\"overload\"/>\n<h2 class=\"overload\" id=\"{{id}}\" data-uid=\"{{uid}}\">Overload</h2>\n{{^_disableContribution}}\n{{#docurl}}\n<span class=\"small pull-right mobile-hide\">\n  <span class=\"divider\">|</span>\n  <a href=\"{{docurl}}\">{{__global.improveThisDoc}}</a>\n</span>{{/docurl}}\n{{#sourceurl}}\n<span class=\"small pull-right mobile-hide\">\n  <a href=\"{{sourceurl}}\">{{__global.viewSource}}</a>\n</span>{{/sourceurl}}\n{{/_disableContribution}}\n\n<div class=\"markdown level1 summary\">{{{summary}}}</div>\n<div class=\"markdown level1 conceptual\">{{{conceptual}}}</div>\n\n<div class=\"codewrapper\">\n  <pre><code class=\"lang-{{_lang}} hljs\">{{syntax.content.0.value}}</code></pre>\n</div>\n\n{{#syntax}}\n{{#parameters.0}}\n<h5 class=\"parameters\">{{__global.parameters}}</h5>\n<table>\n{{/parameters.0}}\n{{#parameters}}\n  <tr>\n    <td>\n      <span class=\"parametername\">{{{id}}}</span>&nbsp;&nbsp;({{{type.specName.0.value}}}</span>)\n      <div>{{{description}}}</div>\n    </td>\n  </tr>\n{{/parameters}}\n{{#parameters.0}}\n</table>\n{{/parameters.0}}\n\n{{#return}}\n<h5 class=\"returns\">{{__global.returns}}</h5>\n<table>\n  <tr>\n    <td>\n      {{{type.specName.0.value}}}\n      <div>{{{description}}}</div>\n    </td>\n  </tr>\n</table>\n{{/return}}\n\n{{#exceptions.0}}\n<h5 class=\"exceptions\">{{__global.exceptions}}</h5>\n<table>\n{{/exceptions.0}}\n{{#exceptions}}\n  <tr>\n    <td>\n      {{{type.specName.0.value}}}\n      <div>{{{description}}}</div>\n    </td>\n  </tr>\n{{/exceptions}}\n{{#exceptions.0}}\n</table>\n{{/exceptions.0}}\n\n{{#typeParameters.0}}\n<h5 class=\"typeParameters\">{{__global.typeParameters}}</h5>\n<table>\n{{/typeParameters.0}}\n{{#typeParameters}}\n  <tr>\n    <td>\n      <span class=\"parametername\">{{{id}}}</span>\n      <div>{{{description}}}</div>\n    </td>\n  </tr>\n{{/typeParameters}}\n{{#typeParameters.0}}\n</table>\n{{/typeParameters.0}}\n\n{{#fieldValue}}\n<h5 class=\"fieldValue\">{{__global.fieldValue}}</h5>\n<table>\n  <tr>\n    <td>\n      {{{type.specName.0.value}}}\n      <div>{{{description}}}</div>\n    </td>\n  </tr>\n</table>\n{{/fieldValue}}\n\n{{#propertyValue}}\n<h5 class=\"propertyValue\">{{__global.propertyValue}}</h5>\n<table>\n  <tr>\n    <td>\n      {{{type.specName.0.value}}}\n      <div>{{{description}}}</div>\n    </td>\n  </tr>\n</table>\n{{/propertyValue}}\n\n{{#eventType}}\n<h5 class=\"eventType\">{{__global.eventType}}</h5>\n<table>\n  <tr>\n    <td>\n      {{{type.specName.0.value}}}\n      <div>{{{description}}}</div>\n    </td>\n  </tr>\n</table>\n{{/eventType}}\n{{/syntax}}\n\n{{#overridden}}\n<h5 class=\"overrides\">{{__global.overrides}}</h5>\n<div><xref uid=\"{{uid}}\" altProperty=\"fullName\" displayProperty=\"nameWithType\"/></div>\n{{/overridden}}\n\n{{#implements.0}}\n<h5 class=\"implements\">{{__global.implements}}</h5>\n{{/implements.0}}\n{{#implements}}\n  {{#definition}}\n    <div><xref uid=\"{{definition}}\" altProperty=\"fullName\" displayProperty=\"nameWithType\"/></div>\n  {{/definition}}\n  {{^definition}}\n    <div><xref uid=\"{{uid}}\" altProperty=\"fullName\" displayProperty=\"nameWithType\"/></div>\n  {{/definition}}\n{{/implements}}\n\n{{#remarks}}\n<h4 id=\"{{id}}_remarks\">{{__global.remarks}}</h4>\n<div class=\"markdown level1 remarks\">{{{remarks}}}</div>\n{{/remarks}}\n\n{{#example.0}}\n<h4 id=\"{{id}}_examples\">{{__global.examples}}</h4>\n{{/example.0}}\n{{#example}}\n{{{.}}}\n{{/example}}\n\n{{#seealso.0}}\n<h3 id=\"{{id}}_seealso\">{{__global.seealso}}</h3>\n<div class=\"seealso\">\n{{/seealso.0}}\n{{#seealso}}\n  {{#isCref}}\n    <div>{{{type.specName.0.value}}}</div>\n  {{/isCref}}\n  {{^isCref}}\n    <div>{{{url}}}</div>\n  {{/isCref}}\n{{/seealso}}\n{{#seealso.0}}\n</div>\n{{/seealso.0}}\n{{/children}}\n{{/children}}\n{{#extensionMethods.0}}\n<h3 id=\"extensionmethods\">{{__global.extensionMethods}}</h3>\n{{/extensionMethods.0}}\n{{#extensionMethods}}\n<div>\n  {{#definition}}\n    <xref uid=\"{{definition}}\" altProperty=\"fullName\" displayProperty=\"nameWithType\"/>\n  {{/definition}}\n  {{^definition}}\n    <xref uid=\"{{uid}}\" altProperty=\"fullName\" displayProperty=\"nameWithType\"/>\n  {{/definition}}\n</div>\n{{/extensionMethods}}\n{{#seealso.0}}\n<h3 id=\"seealso\">{{__global.seealso}}</h3>\n<div class=\"seealso\">\n{{/seealso.0}}\n{{#seealso}}\n  {{#isCref}}\n    <div>{{{type.specName.0.value}}}</div>\n  {{/isCref}}\n  {{^isCref}}\n    <div>{{{url}}}</div>\n  {{/isCref}}\n{{/seealso}}\n{{#seealso.0}}\n</div>\n{{/seealso.0}}\n"
  },
  {
    "path": "Other/DocFX/_doc/template2/partials/item.tmpl.partial",
    "content": "<h1 id=\"{{id}}\" data-uid=\"{{uid}}\" class=\"text-break\">{{>partials/title}}</h1>\n\n<div class=\"markdown level0 summary\">{{{summary}}}</div>\n<div class=\"markdown level0 conceptual\">{{{conceptual}}}</div>\n\n<div class=\"codewrapper\">\n  <pre><code class=\"lang-{{_lang}} hljs\">{{syntax.content.0.value}}</code></pre>\n</div>\n\n{{#remarks}}\n<h4 id=\"{{id}}_remarks\"><strong>{{__global.remarks}}</strong></h4>\n<div class=\"markdown level0 remarks\">{{{remarks}}}</div>\n{{/remarks}}\n{{#example.0}}\n<h4 id=\"{{id}}_examples\"><strong>{{__global.examples}}</strong></h4>\n{{/example.0}}\n{{#example}}\n{{{.}}}\n{{/example}}\n"
  },
  {
    "path": "Other/DocFX/_doc/toc.yml",
    "content": "- name: Home\n  href: index.md\n- name: Library\n  href: api/\n  topicHref: api/index.md\n- name: Editor\n  href: editor/\n  topicUid: Application\n- name: Articles\n  href: articles/\n  topicHref: articles/index.md\n- name: Cookbook\n  href: cookbook/\n  topicHref: cookbook/index.md\n"
  },
  {
    "path": "README.md",
    "content": "# LibreAutomate\n\nC# script editor and automation library for Windows.\n\nSome features of the automation library:\n- Automate desktop and web UI using keys, mouse and API. Find and click buttons, links, images.\n- Launch programs. Manage files and windows. Transfer and process text and other data.\n- Hotkeys, autotext and other triggers. Auto-replace/expand text when typing. Auto-close windows. Remap keys.\n- Custom toolbars that can be attached to windows or screen edges. And menus.\n- Custom dialog windows of any complexity can be created easily in code.\n- All classes/functions are documented.\n- The library can be used in other programs too. [More info](https://www.libreautomate.com/api/index.html), [NuGet](https://www.nuget.org/packages/LibreAutomate).\n- Uses .NET 10.\n\nSome features of the script editor app:\n- The scripting language is C#. The app is a good way to learn it.\n- C# code editor with intellisense. Script manager, cookbook, debugger.\n- Tools for recording keyboard/mouse and selecting UI objects such as buttons, links and images.\n- Also you can use .NET and other libraries. Tools and databases for NuGet, Windows API, icons.\n- Can create independent .exe programs and .NET libraries.\n\nMore info and download: https://www.libreautomate.com/\n\nEditor window\n\n![window](https://www.libreautomate.com/images/window.png#1 \"Editor window\")\n\n## How to build\nYou need Visual Studio 2026. When installing, select these workloads: .NET desktop development; Desktop development with C++. It also installs .NET 10 SDK and Windows 11 SDK; or install them separately.\n\n1. Clone or download/extract the source code.\n2. Open `Au.sln` in Visual Studio.\n3. Switch to platform x86. Build solution.\n4. Switch to platform ARM64. Build solution.\n5. Switch to platform AnyCPU. Build solution.\n6. Run `Au.Editor` project. It should open the editor window.\n"
  },
  {
    "path": "Scripts/@Au docs/Au docs.cs",
    "content": "/*/\ndefine AUDOCS\nnoWarnings CS8632\ntestInternal Au.Editor,Au,Microsoft.CodeAnalysis,Microsoft.CodeAnalysis.CSharp,Microsoft.CodeAnalysis.Features,Microsoft.CodeAnalysis.CSharp.Features,Microsoft.CodeAnalysis.Workspaces,Microsoft.CodeAnalysis.CSharp.Workspaces\npr AuDocsLib.cs\nr Au.Editor.dll\nr Roslyn\\Microsoft.CodeAnalysis.dll\nr Roslyn\\Microsoft.CodeAnalysis.CSharp.dll\nr Roslyn\\Microsoft.CodeAnalysis.Features.dll\nr Roslyn\\Microsoft.CodeAnalysis.CSharp.Features.dll\nr Roslyn\\Microsoft.CodeAnalysis.Workspaces.dll /alias=CAW\nr Roslyn\\Microsoft.CodeAnalysis.CSharp.Workspaces.dll\nnuget -\\Markdig\nnuget -\\WeCantSpell.Hunspell\n/*/\n\n//args = new[] { \"/upload\" };\nvar siteDir = @\"C:\\Temp\\Au\\DocFX\\site\";\n\ntry {\n\tif (args.Length == 0) {\n\t\tLA.App.Settings = new(); //need internetSearchUrl\n\t\t\n\t\t//ADL.AuDocsShared.Test();\n\t\t_Build();\n\t} else if (args[0] == \"/upload\") {\n\t\tAuDocs.CompressAndUpload(siteDir);\n\t}\n}\ncatch (Exception e1) {\n\tprint.it(e1);\n\t_KillDocfxProcesses();\n}\n\nvoid _Build() {\n\tprint.clear();\n\tvar time0 = perf.ms;\n\t\n\tbool testSmall = !true;\n\tbool cookbook = false, preprocess = false, postprocess = false, build = false, serve = false;\n\t//cookbook = true;\n\t//preprocess = true;\n\t//postprocess = true;\n\t//serve = true;\n\t//postprocess = serve = true;\n\tif (!(cookbook | preprocess | postprocess | build | serve)) preprocess = postprocess = build = serve = cookbook = true;\n\tbool onlyMetadata = !true;\n\t//preprocess = true; build = true; onlyMetadata = true;\n\t\n\tvar sourceDir = testSmall ? @\"C:\\code\\au\\Test Projects\\TestDocFX\" : @\"C:\\code\\au\\Au\";\n\tvar sourceDirPreprocessed = @\"C:\\Temp\\Au\\DocFX\\source\";\n\tvar docDir = testSmall ? @\"C:\\code\\au\\Test Projects\\TestDocFX\\docfx_project\" : @\"C:\\code\\au\\Other\\DocFX\\_doc\";\n\tvar siteDirTemp = siteDir + \"-temp\";\n\t\n\tif (cookbook) {\n\t\tAuDocs.Cookbook(docDir);\n\t\tprint.it(\"DONE cookbook\");\n\t\t\n\t\tscript.runWait(\"LA menu doc.cs\");\n\t}\n\t\n\tvar d = new AuDocs();\n\tif (preprocess) {\n\t\td.Preprocess(sourceDir, sourceDirPreprocessed, testSmall);\n\t\tprint.it(\"DONE preprocessing\");\n\t}\n\t\n\tvar docfx = folders.Downloads + @\"docfx\\docfx.exe\";\n\tint r;\n\tif (build || serve) {\n\t\t_KillDocfxProcesses();\n\t\tEnvironment.CurrentDirectory = docDir;\n\t}\n\t\n\tif (build) {\n\t\tfilesystem.delete(siteDirTemp);\n\t\tr = run.console(o => { print.it(o); }, docfx, \"metadata\");\n\t\tif (r != 0) { print.it(\"docfx metadata\", r); return; }\n\t\tif (onlyMetadata) { print.it(\"metadata ok\"); return; }\n\t\tr = run.console(o => { print.it(o); }, docfx, $@\"build\");\n\t\tif (r != 0) { print.it(\"docfx build\", r); return; }\n\t\t//print.it(\"build ok\");\n\t\tpostprocess |= serve;\n\t\tfilesystem.delete(Directory.EnumerateFiles(docDir + @\"\\api\", \"*.yml\")); //garbage for VS search\n\t\tfilesystem.delete(docDir + @\"\\api\\.manifest\");\n\t}\n\t\n\tif (postprocess) {\n\t\td.Postprocess(siteDirTemp, siteDir);\n\t\tprint.it(\"DONE postprocessing\");\n\t\t\n\t\tif (!testSmall && build) {\n\t\t\tscript.runWait(\"LA docs toc.json.cs\");\n\t\t\tscript.runWait(\"LA docs doc-html.db.cs\");\n\t\t\tprint.it(\"DONE LA docs scripts\");\n\t\t\t\n\t\t\t//print.it($\"<><script Au docs.cs|/upload>Upload Au docs...<>\");\n\t\t\tprint.it(\"\"\"\n<>DONE. Now <script LA docs doc-ai.db.cs>create doc-ai.db<>.\nTo upload the created website, click `Upload...` in the main toolbar.\n\"\"\");\n\t\t}\n\t}\n\t\n\tprint.it((perf.ms - time0) / 1000d);\n\t\n\tif (serve) {\n\t\t//r = run.console(o => { print.it(o); }, docfx, $\"serve \"\"{siteDir}\"\"\");\n\t\t//if (r != 0) { print.it(\"docfx serve\", r); return; } //no, it prints -1 when process killed\n\t\trun.it(docfx, $@\"serve \"\"{siteDir}\"\"\", flags: RFlags.InheritAdmin, dirEtc: new() { WindowState = ProcessWindowStyle.Hidden });\n\t}\n\t\n\t//print.scrollToTop();\n}\n\nvoid _KillDocfxProcesses() {\n\tforeach (var v in process.getProcessIds(\"docfx.exe\")) process.terminate(v);\n}\n"
  },
  {
    "path": "Scripts/@Au docs/AuDocs analyze.cs",
    "content": "//#define DUPLICATE_TEXT\n//#define MISSING_PARAM\n//#define PARAM_WITH_JUST_SEE\n//#define RETURNS_IN_SUMMARY\n//#define LIST_NOT_ENDED\n//#define STRING_NOT_IN_C\n//#define PARAM_NOT_IN_I\n//#define SYMBOL_NOT_BOLD\n//#define SPELL_CHECK\n\nusing Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp;\nusing Microsoft.CodeAnalysis.Text;\nusing Microsoft.CodeAnalysis.CSharp.Syntax;\nusing Microsoft.CodeAnalysis.Shared.Extensions;\nusing Microsoft.CodeAnalysis.CSharp.Extensions;\nusing System.Collections.Immutable;\nusing System.Xml.Linq;\nusing Markdig;\nusing System.Xml;\n\n#pragma warning disable CS0162, CS8321, CS0169 //unreachable code, unused func/field\n\nclass _Analyze {\n\t//AuDocs _main;\n\tint _currentFileIndex, _currentFileIndexPrev;\n\tCSharpSemanticModel _semo;\n\tISymbol _currentSym;\n#if SPELL_CHECK\n\tWeCantSpell.Hunspell.WordList _spellCheck = WeCantSpell.Hunspell.WordList.CreateFromFiles(@\"C:\\Program Files\\LibreOffice\\share\\extensions\\dict-en\\en_US.dic\");\n#endif\n\tregexp _rxTriple = new(@\"(?m)^\\h*///\\h?\");\n\tregexp _rxListNotEnded;\n\t\n\t//public _Analyze(AuDocs main) {\n\t//\t_main = main;\n\t//}\n\t\n\tpublic void StartingFile(CSharpSemanticModel semo, int currentFileIndex) {\n\t\t_semo = semo;\n\t\t_currentFileIndex = currentFileIndex;\n\t}\n\t\n\t/// <summary>\n\t/// Prints or collects data for an XML doc to find what can be improved in it.\n\t/// </summary>\n\t/// <param name=\"sdoc\">Raw XML doc (with ///).</param>\n\t/// <param name=\"sym\"></param>\n\tpublic void AnalyzeDoc(string sdoc, ISymbol sym) {\n\t\treturn;\n\t\tif (_currentFileIndex is not (>= 0 and < 200)) return;\n\t\t\n\t\tif (_currentFileIndex > _currentFileIndexPrev) {\n\t\t\t_currentFileIndexPrev = _currentFileIndex;\n\t\t\tprint.it($\"<><bc yellowgreen>{_currentFileIndex}<>\");\n\t\t}\n\t\t\n\t\t_currentSym = sym;\n\t\tsdoc = \"<x>\" + _rxTriple.Replace(sdoc) + \"</x>\";\n\t\tvar xdoc = XElement.Parse(sdoc, LoadOptions.PreserveWhitespace);\n\t\t\n\t\t#region child elements\n\t\tforeach (var x in xdoc.Elements()) {\n\t\t\tvar s = x.ToString();\n#if DUPLICATE_TEXT\n\t\t\tif (s.Length > 200) {\n\t\t\t\tif (!_dDupText.TryAdd(s, _currentSym)) {\n\t\t\t\t\tvar o = _dDupText[s];\n\t\t\t\t\tif (o is List<ISymbol> a) a.Add(_currentSym);\n\t\t\t\t\telse if (o is ISymbol sym) _dDupText[s] = new List<ISymbol> { sym, _currentSym };\n\t\t\t\t}\n\t\t\t}\n#endif\n\t\t\t\n#if PARAM_WITH_JUST_SEE\n\t\t\tif(x.Name.LocalName==\"param\" && s.Contains(\"<see cref\") && s.Ends(false, \">.</param>\", \"></param>\") > 0 && x.Elements().Count() == 1) {\n\t\t\t\t//if (x.FirstNode is XElement || (x.FirstNode is XText xt && xt.Value.Length < 10)) {\n\t\t\t\tif (x.FirstNode is XElement || (x.FirstNode is XText xt && xt.Value == \"See \")) {\n\t\t\t\t\t_Print(s);\n\t\t\t\t}\n\t\t\t}\n#endif\n\t\t\t\n#if RETURNS_IN_SUMMARY\n\t\t\tif (x.Name.LocalName == \"summary\" && s.Contains(\"\\nReturns\") && !s.Starts(\"<summary>\\r\\nReturns\")) {\n\t\t\t\t_Print(s);\n\t\t\t}\n\t\t\t\n\t\t\t//this hotkey script moves /// Returns ... line from <summary> to <returns>.\n\t\t\t//hk[\"F8\"] = o => {\n\t\t\t//\tif (!clipboard.tryCopy(out var s)) return;\n\t\t\t//\tif (s != null && 0 != s.RxReplace(@\"///\\h(?:Return[s]? )?(.+)\", \"/// <returns>$1</returns>\", out s, 1)) {\n\t\t\t//\t\tkeys.send(\"Ctrl+X Down Home*2\");\n\t\t\t//\t\tclipboard.paste(s);\n\t\t\t//\t\tkeys.send(\"Up\");\n\t\t\t//\t}\n\t\t\t//};\n#endif\n\t\t\t\n#if LIST_NOT_ENDED\n\t\t\t_rxListNotEnded ??= new(@\"(?m)^\\h*-.+\\R\\t*(?![\\- \\r\\n]|</|<br\\b)\");\n\t\t\tif (_rxListNotEnded.IsMatch(s)) {\n\t\t\t\t_Print(s);\n\t\t\t}\n#endif\n\t\t}\n\t\t#endregion\n\t\t\n\t\t#region child nodes\n\t\tforeach (var n in xdoc.Nodes()) {\n\t\t\tif (n is XElement) continue;\n\t\t\t\n\t\t\t//all xdoc child nodes must be XElement\n\t\t\tif (n is XText t && n is not XCData) {\n\t\t\t\tvar s = t.Value;\n\t\t\t\tif (!string.IsNullOrWhiteSpace(s)) _Print(s);\n\t\t\t} else {\n\t\t\t\t_Print(n);\n\t\t\t}\n\t\t}\n\t\t#endregion\n\t\t\n#if STRING_NOT_IN_C\n\t\t//if text contains \" or ', in most cases it is code and should be in <c>\n\t\tforeach (var s in _DescendantTexts()) {\n\t\t\t//if (s.Contains('\"')) {\n\t\t\t//\t_Print(s);\n\t\t\t//}\n\t\t\tif (s.RxIsMatch(@\"'.'\")) {\n\t\t\t\t//if (s.RxIsMatch(@\"'\\\\.'\")) {\n\t\t\t\t//if (s.RxIsMatch(@\"'\\\\x\\w+'\")) {\n\t\t\t\t_Print(s);\n\t\t\t}\n\t\t}\n#endif\n\t\t\n#if PARAM_NOT_IN_I //parameter references must be in <i>\n\t\tImmutableArray<IParameterSymbol> ap = default;\n\t\tif (_currentSym is IMethodSymbol ims) {\n\t\t\tap = ims.Parameters;\n\t\t} else if (_currentSym is IPropertySymbol ips && ips.IsIndexer) {\n\t\t\tap = ips.Parameters;\n\t\t}\n\t\tif (!ap.IsDefaultOrEmpty) {\n\t\t\t//print.it(_currentSym);\n\t\t\tvar at = _DescendantTexts();\n\t\t\tif (at.Any()) {\n\t\t\t\tbool sep=false;\n\t\t\t\tforeach (var p in ap) {\n\t\t\t\t\tvar rx = new regexp($@\"(?<![\\.'])\\b{p.Name}\\b\");\n\t\t\t\t\tforeach (var s in at) {\n\t\t\t\t\t\tif (rx.Replace(s, \"</_><bc yellow>$0<><_>\", out var s2) > 0) {\n\t\t\t\t\t\t\tif(!sep) { sep=true; print.it(\"-------------------\"); }\n\t\t\t\t\t\t\t_Print(s2);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n#endif\n\t\t\n#if SYMBOL_NOT_BOLD //names of types, functions, enum members etc should be in <b> or <see>, <ms>.\n\t\tvar hs = _stackSymbols.Peek();\n\t\tforeach (var s in _DescendantTexts()) {\n\t\t\tbool found = false;\n\t\t\t_rxWord.Replace(s, m => {\n\t\t\t\tvar v = m.Value;\n\t\t\t\t\n\t\t\t\t//found = v is not (\"ANSI\" or \"PCRE\" or \"NOTE\" or \"IMPORTANT\" or \"MSDN\" or \"XAML\" or \"HTML\" or \"ASCII\" or \"JSON\" or \"HTTP\");\n\t\t\t\t//if (found) return \"</_><bc yellow>\" + v + \"<><_>\";\n\t\t\t\t\n\t\t\t\tbool skip = true; for (int j = 1; j < v.Length; j++) if (char.IsUpper(v[j])) { skip = false; break; }\n\t\t\t\tif (skip) {\n\t\t\t\t\tskip = false;\n\t\t\t\t\t//skip if start of sentence\n\t\t\t\t\tif ((m.Start == 0 || m.Subject.Eq(m.Start - 1, '\\n') || m.Subject.Eq(m.Start - 2, \". \")) && !m.Subject.Eq(m.End, '<') && !m.Subject.Eq(m.End, '[')) {\n\t\t\t\t\t\t//skip = v is \"More\" or \"System\" or \"Object\" or \"Enum\" or \"Single\" or \"Double\" or \"Missing\" or \"Type\" or \"Delegate\" or \"Attribute\" or \"String\" or \"Array\" or \"List\" or \"Exception\" or \"Action\" or \"File\" or \"Path\" or \"Icon\" or \"Image\" or \"Process\" or \"Thread\" or \"Font\" or \"Timeout\" or \"Timer\" or \"Rectangle\" or \"Group\";\n\t\t\t\t\t\tskip = true;\n\t\t\t\t\t} else {\n\t\t\t\t\t\t//skip = v is \"More\" or \"System\" or \"Object\" or \"Enum\" or \"Single\" or \"Double\" or \"Missing\" or \"Type\" or \"Delegate\" or \"Attribute\" or \"String\" or \"Array\" or \"List\" or \"Exception\" or \"Action\" or \"File\" or \"Path\" or \"Process\" or \"Thread\" or \"Timeout\" or \"Timer\" or \"Task\" or \"Debug\" or \"Au\";\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tskip = v is \"GC\" or \"MD5\";\n\t\t\t\t}\n\t\t\t\tif (skip) return v;\n\t\t\t\t\n\t\t\t\tif (hs.Contains(v)) {\n\t\t\t\t\tfound = true;\n\t\t\t\t\treturn \"</_><bc yellow>\" + v + \"<><_>\";\n\t\t\t\t} else {\n\t\t\t\t\t\n\t\t\t\t}\n\t\t\t\treturn v;\n\t\t\t}, out var s2);\n\t\t\t\n\t\t\tif (found) {\n\t\t\t\t_Print(s2);\n\t\t\t}\n\t\t}\n#endif\n\t\t\n#if SPELL_CHECK\n\t\tforeach (var s in _DescendantTexts()) {\n\t\t\tbool found = false;\n\t\t\t_rxWord.Replace(s, m => {\n\t\t\t\tvar v = m.Value;\n\t\t\t\tif (!_spellCheck.Check(v)) {\n\t\t\t\t\tif (v is \"eg\" or \"ie\" || v.Starts(true, \"substring\", \"unmanaged\") > 0) return v;\n\t\t\t\t\tfound = true;\n\t\t\t\t\treturn \"</_><bc yellow>\" + v + \"<><_>\";\n\t\t\t\t}\n\t\t\t\treturn v;\n\t\t\t}, out var s2);\n\t\t\t\n\t\t\tif (found) {\n\t\t\t\t_Print(s2);\n\t\t\t}\n\t\t}\n#endif\n\t\t\n\t\tstring[] _DescendantTexts()\n\t\t\t=> xdoc.DescendantNodes().OfType<XText>().Where(t => t is not XCData && t.Parent.Name.LocalName is not (\"c\" or \"i\" or \"b\" or \"google\" or \"ms\" or \"sqlite\" or \"code\"))\n\t\t\t.Select(t => t.Value.Trim(\"\\r\\n\")).Where(s => !string.IsNullOrWhiteSpace(s)).ToArray();\n\t\t\n\t\tvoid _Print(object s) {\n\t\t\tif (_currentSym != _prevPrintSym) {\n\t\t\t\t_prevPrintSym = _currentSym;\n\t\t\t\tprint.it($\"<>{AuDocs.LaGoto(_currentSym)}\");\n\t\t\t}\n\t\t\tprint.it($\"<><_>{s}</_>\");\n\t\t}\n\t}\n\tDictionary<string, object> _dDupText = new();\n\tISymbol _prevPrintSym;\n\t\n#if SYMBOL_NOT_BOLD\n\tStack<HashSet<string>> _stackSymbols = new();\n\tpublic void AnalyzeType(CSharpSyntaxNode sn) {\n\t\tif (sn != null) {\n\t\t\tint pos = sn switch { BaseTypeDeclarationSyntax btd => btd.CloseBraceToken.SpanStart, NamespaceDeclarationSyntax nd => nd.CloseBraceToken.SpanStart, FileScopedNamespaceDeclarationSyntax fsnd => fsnd.SemicolonToken.Span.End, _ => 0 };\n\t\t\t_stackSymbols.Push(_semo.LookupSymbols(pos).Select(o => o.Name).ToHashSet());\n\t\t\t//_stackSymbols.Push(_semo.LookupSymbols(pos).Where(o => o is not INamespaceSymbol).Select(o => o.Name).ToHashSet());\n\t\t\t//_stackSymbols.Push(_semo.LookupSymbols(pos).Where(o => o is not INamespaceOrTypeSymbol).Select(o => o.Name).ToHashSet());\n\t\t} else _stackSymbols.Pop();\n\t}\n#else\n\tpublic void AnalyzeType(CSharpSyntaxNode sn) { }\n#endif\n\t\n\t//regexp _rxWord = new(@\"\\b(?<![\\.\"\"])[A-Z]\\w+\\b(?!</|\"\")\"); //Abc\n\t//regexp _rxWord = new(@\"\\b(?<![\"\"])[A-Z]\\w+\\b(?!</|\"\"|\\.)\"); //Abc\n\t//regexp _rxWord = new(@\"\\b(?<![\\.\"\"])[a-z][A-Za-z]+\\d?\\b(?!</|\"\")\"); //aBc\n\t//regexp _rxWord = new(@\"\\b(?<![\\.\"\"])[A-Z]+_[A-Z_]+\\d?\\b(?!</|\"\")\"); //AB_C\n\t//regexp _rxWord = new(@\"\\b(?<![\\.\"\"])[A-Z_]+_\\b(?!</|\"\")\"); //AB_\n\t//regexp _rxWord = new(@\"\\b(?<![\\.\"\"])[A-Z]{4,}\\d?\\b(?!</|\"\")\"); //ABC\n\t//regexp _rxWord = new(@\"\\b(?<![\\.\"\"])[A-Z]+[a-z]*[A-Z]+[a-z]+[A-Za-z]*\\d?\\b(?!</|\"\")\"); //AbCd\n\t//regexp _rxWord = new(@\"\\bflags? [A-Z]\\w+\\b(?!</|\"\")\"); //flag Abc\n\t//regexp _rxWord = new(@\"\\b(?<![\\.\"\"])[A-Z]\\w+X\\b(?!</|\"\")\"); //SymbolX\n\t//regexp _rxWord = new(@\"\\b(?<![\\.\"\"])[A-Z]\\w+x\\b(?!</|\"\")\"); //SYMBOLx\n\t//regexp _rxWord = new(@\"\\b(?<![\\.\"\"])(?i)[A-Z]\\w*(\\.[A-Z]\\w*)+\\b(?!</|\"\")\"); //Abc.Def\n\t//regexp _rxWord = new(@\"\\b(?<![\\.\"\"])[a-z]+[A-Z]+[A-Za-z]*\\d?\\b(?!</|\"\")\"); //aBc\n\t//regexp _rxWord = new(@\"\\b(?<![\"\"])(?i)[a-z]\\w+<[a-z]\\w+\\b(?!</|\"\")\"); //abc<def>\n\t//regexp _rxWord = new(@\"\\b(?<![\"\"])(?i)[a-z]\\w+\\[(?!</|\"\")\"); //abc[] //rejected\n\t//regexp _rxWord = new(@\"\\b(?<![\\.\"\"])[a-z]+[a-z_\\d]*\\b(?!</|\"\")\"); //abc\n\tregexp _rxWord = new(@\"\\b[A-Za-z][a-z']*\\b\"); //word for spell check\n\t\n\t/// <summary>\n\t/// Prints data collected by _AnalyzeDoc.\n\t/// </summary>\n\tpublic void Finally() {\n\t\tforeach (var (s, o) in _dDupText) {\n\t\t\tif (o is not List<ISymbol> a) continue;\n\t\t\tprint.it($\"\"\"\n<><lc green>Duplicate text:<>\n<lc wheat><_>{s}</_><>\n{string.Join('\\n', a.Select(o => AuDocs.LaGoto(o)))}\n\"\"\");\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Prints or collects data for an XML doc to find what can be improved in it.\n\t/// xdoc, sxml - final doc XML (after expanding inheritdoc and modifying).\n\t/// </summary>\n\tpublic void AnalyzeDoc2(XElement xdoc, string sxml) {\n\t\tif (_currentFileIndex is not (>= 0 and < 200)) return;\n\t\t\n\t\tvar sym = _currentSym;\n\t\t\n\t\tif (sxml.RxIsMatch(@\"\\[.*?\\]\\(.*?\\)\")) _Warning(\"Unprocessed []() link\", sxml);\n\t}\n\t\n\tpublic void MissingParam(IParameterSymbol p, XElement x) {\n#if MISSING_PARAM\n\t\tif (_currentFileIndex is not (>= 0 and < 200)) return;\n\t\t\n\t\tvar sym = _currentSym;\n\t\tif(p.Type.IsEnumType()) return;\n\t\t//if(p.IsThis) return; //does not work. Also tested p.OriginalDefinition.IsThis and p.CustomModifiers.\n\t\t//if (x==null || x.IsEmpty) return;\n\t\t\n\t\t//bool hasOverloads = sym.ContainingType.GetMembers(sym.Name).Length > 1;\n\t\t//if (!hasOverloads) return;\n\t\t\n\t\tif (sym != _prevMethod) {\n\t\t\t_prevMethod = sym;\n\t\t\tprint.it(\"<>\" + AuDocs.VsGoto(sym));\n\t\t}\n\t\tprint.it(p.Name);\n\t}\n\tISymbol _prevMethod;\n#else\n\t}\n#endif\n\t\n\tvoid _Warning(string warning, object text) {\n\t\tprint.warning($\"<_>{warning}</_>\\r\\n\\tMember: {AuDocs.LaGoto(_currentSym)} in {_semo.SyntaxTree.FilePath}\\r\\n\\tText: <_>{text}</_>\", 1);\n\t}\n}\n"
  },
  {
    "path": "Scripts/@Au docs/AuDocs cookbook.cs",
    "content": "using System.Xml.Linq;\nusing ADL;\n\npartial class AuDocs {\n\t\n\tpublic static void Cookbook(string docDir) {\n\t\tvar sbToc = new StringBuilder();\n\t\tList<(string name, string path)> aFiles = new();\n\t\t\n\t\tstring dirTo = @\"C:\\Temp\\Au\\DocFX\\cookbook\", dirToLink = docDir + @\"\\cookbook\";\n\t\tif (filesystem.exists(dirTo)) filesystem.delete(Directory.GetFiles(dirTo));\n\t\telse filesystem.createDirectory(dirTo);\n\t\tif (!filesystem.exists(dirToLink).IsNtfsLink) filesystem.more.createSymbolicLink(dirToLink, dirTo, CSLink.Directory);\n\t\t\n\t\tvar dirFrom = folders.ThisAppBS + @\"..\\Cookbook\\files\";\n\t\tvar xr = XmlUtil.LoadElem(dirFrom + \".xml\");\n\t\t\n\t\t_AddItems(xr, 1, dirFrom);\n\t\t\n\t\tvoid _AddItems(XElement xp, int level, string path) {\n\t\t\t//see PanelCookbook._Load().\n\t\t\tforeach (var x in xp.Elements()) {\n\t\t\t\tvar name = x.Attr(\"n\");\n\t\t\t\tif (name[0] == '-') continue;\n\t\t\t\tvar tag = x.Name.LocalName;\n\t\t\t\tbool dir = tag == \"d\";\n\t\t\t\tif (dir) {\n\t\t\t\t\tsbToc.Append('#', level).AppendFormat(\" {0}\\r\\n\", name);\n\t\t\t\t\t_AddItems(x, level + 1, path + \"\\\\\" + name);\n\t\t\t\t} else {\n\t\t\t\t\tif (tag != \"s\") continue;\n\t\t\t\t\tvar cspath = path + \"\\\\\" + name;\n\t\t\t\t\tname = name[..^3];\n\t\t\t\t\tsbToc.Append('#', level).AppendFormat(\" [{0}](/cookbook/{1})\\r\\n\", name, Uri.EscapeDataString(name) + \".html\");\n\t\t\t\t\taFiles.Add((name, cspath));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\t//print.it(sbToc.ToString());\n\t\tfilesystem.saveText(dirTo + @\"\\toc.md\", sbToc.ToString());\n\t\tfilesystem.saveText(dirTo + @\"\\index.md\", \"\");\n\t\t\n\t\tforeach (var (name, path) in aFiles) {\n\t\t\tvar code = filesystem.loadText(path);\n\t\t\tbool test = false;\n\t\t\t//test = name == \"test\";\n\t\t\t//if (test) {\n\t\t\t//\tprint.it($\"<><lc #B3DF00>{name}<>\");\n\t\t\t//\tprint.it(code);\n\t\t\t//\tprint.it(\"-------------\");\n\t\t\t//}\n\t\t\t\n\t\t\tvar md = AuDocsShared.RecipeCodeToMd(name, code, test);\n\t\t\tif (test) print.it(md);\n\t\t\tfilesystem.saveText($@\"{dirTo}\\{name}.md\", md);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Scripts/@Au docs/AuDocs other tasks.cs",
    "content": "/*/ nuget -\\SSH.NET; c Sftp.cs; c Passwords.cs; /*/\nusing Renci.SshNet;\nusing System.Text.Json;\n\npartial class AuDocs {\n\tpublic static void CompressAndUpload(string siteDir) {\n\t\tif (1 != dialog.show(\"Upload?\", \"Will replace the LA home page and all docs.\", \"1 Yes|2 No\")) return;\n\t\tvar tarDir = pathname.getDirectory(siteDir);\n\t\t_Compress(siteDir, tarDir);\n\t\t_Upload(tarDir);\n\t\t//_CloudflarePurgeCache(); //currently not using\n\t}\n\t\n\tstatic void _Compress(string siteDir, string tarDir) {\n\t\tvar sevenZip = folders.ThisAppBS + @\"32\\7za.exe\";\n\t\t\n\t\tvar tar = tarDir + @\"\\site.tar\";\n\t\tfilesystem.delete(tar);\n\t\tfilesystem.delete(tarDir + @\"\\site.tar.bz2\"); //info: bz2 compression is much better than gz/zip. xz/7z slightly better than bz2, but can't SSH-extract.\n\t\t\n\t\tint r1 = run.console(out var s, sevenZip, $@\"a \"\"{tar}\"\"\", siteDir); //add files to the root in the archive, not to \"site\" dir\n\t\tif (r1 != 0) { print.it(r1, s); return; }\n\t\tint r2 = run.console(out s, sevenZip, $@\"a site.tar.bz2 site.tar\", tarDir);\n\t\tif (r2 != 0) { print.it(r2, s); return; }\n\t\t\n\t\tfilesystem.delete(tarDir + @\"\\site.tar\");\n\t\t\n\t\tprint.it(\"Compressed\");\n\t}\n\t\n\tstatic void _Upload(string tarDir) {\n\t\tvar ci = Sftp.GetConnectionInfo(); if (ci == null) return;\n\t\t\n\t\tconst string ftpDir = \"domains/libreautomate.com/public_html\";\n\t\tvar localFile = tarDir + @\"/site.tar.bz2\";\n\t\t\n\t\t//upload\n\t\tusing var ftp = new SftpClient(ci.host, ci.port, ci.user, ci.pass);\n\t\tftp.ConnectAndUpload(ftpDir, localFile);\n\t\tprint.it(\"Uploaded\");\n\t\t\n\t\t//extract\n\t\tusing var ssh = new SshClient(ci.host, ci.port, ci.user, ci.pass);\n\t\tssh.Connect();\n\t\t_Cmd2(\"tar -xf site.tar.bz2\");\n\t\t\n\t\tvoid _Cmd(string s, bool silent = false) {\n\t\t\tvar c = ssh.RunCommand(s);\n\t\t\t//print.it($\"ec={c.ExitStatus}, result={c.Result}, error={c.Error}\");\n\t\t\tif (!silent && c.ExitStatus != 0) throw new Exception(c.Error);\n\t\t}\n\t\t\n\t\t//cd does not work when separate command\n\t\tvoid _Cmd2(string s, bool silent = false) => _Cmd($\"cd {ftpDir} && {s}\", silent);\n\t\t\n\t\tfilesystem.delete(localFile);\n\t\tvar downl = $\"download/doc/{Au_.Version}.tar.bz2\";\n\t\tif(ftp.Exists(downl)) ftp.DeleteFile(downl);\n\t\tftp.RenameFile(\"site.tar.bz2\", downl);\n\t\t\n\t\tprint.it(\"<>Extracted to <link>https://www.libreautomate.com/</link>\");\n\t}\n\t\n\t//file: path to a file in https://www.libreautomate.com/. If null, purges all.\n\tstatic void _CloudflarePurgeCache(string file = null) {\n\t\tvar token = Passwords.Get(\"Cloudflare API\");\n\t\tvar content = internet.jsonContent(file != null ? $$\"\"\"{\"files\":[\"https://www.libreautomate.com/{{file}}\"]}\"\"\" : $$\"\"\"{\"purge_everything\": true}\"\"\");\n\t\tvar j = internet.http.Post(\"https://api.cloudflare.com/client/v4/zones/238431a81c22e29834a04ce68574988c/purge_cache\", content, [$\"Authorization: Bearer {token}\"]).Json();\n\t\tvar ok = (bool)j[\"success\"];\n\t\tif (!ok) print.warning(\"Failed to purge Cloudflare cache.\\r\\nTo purge manually: Cloudflare -> Caching -> Configuration -> Purge Everything.\\r\\nResult:\\r\\n\" + j);\n\t}\n}\n"
  },
  {
    "path": "Scripts/@Au docs/AuDocs text.cs",
    "content": "//In this file: functions that preprocess or postprocess file text without Roslyn.\n\n//#define DISQUS\n\nusing System.Xml.Linq;\nusing System.Xml.XPath;\nusing System.Net;\n\npartial class AuDocs {\n\tregexp _rxRecord, _rxRecordParam, _rxRecordDocLine, _rxRecordDocParam, _rxSeealso1, _rxSeealso2, _rxSeealso3, _rxSeealso4, _rxSeealso5;\n\t\n\tstring _PreprocessFileAsText(string path, string s) {\n\t\t//if (!path.Ends(\"param types.cs\")) return s;\n\t\t\n\t\t//Convert 'record class X(...)' to the classic format, because DocFX:\n\t\t//\t1. Ignores <param>.\n\t\t//\t2. Copies summary etc from type to ctor.\n\t\tif (s.Contains(\"record \")) {\n\t\t\t//print.it($\"<><lc greenyellow>{path}\");\n\t\t\t_rxRecord ??= new(@\"(?m)^\\h*(public [\\w ]*\\brecord) (class|struct) (\\w+)(\\(([^()]++|(?-2))+\\))[^\\{;]*[\\{;]\");\n\t\t\ts = _rxRecord.Replace(s, m => {\n\t\t\t\t//print.it($\"<><c blue>{m.Value}<>\");\n\t\t\t\t\n\t\t\t\t_b.Clear();\n\t\t\t\tvar mod = m[1].Value;\n\t\t\t\tvar name = m[3].Value;\n\t\t\t\tvar par = s[(m[4].Start + 1)..(m[4].End - 1)];\n\t\t\t\tvar sub = m.Subject;\n\t\t\t\tbool noBody = sub[m.End - 1] == ';';\n\t\t\t\t\n\t\t\t\t_b.AppendFormat(\"{0} {1} {2} {{\", mod, m[2].Value, name);\n\t\t\t\t//doc\n\t\t\t\t_rxRecordDocLine ??= new(@\"\\G\\h*///[^/]\");\n\t\t\t\tint docStart = 0;\n\t\t\t\tfor (int i = m.Start; i > 3;) {\n\t\t\t\t\ti = sub.LastIndexOf('\\n', i - 2); if (i < 0) break;\n\t\t\t\t\tif (!_rxRecordDocLine.IsMatch(sub, ++i..)) break;\n\t\t\t\t\tdocStart = i;\n\t\t\t\t}\n\t\t\t\tRXMatch[] ap = null;\n\t\t\t\tif (docStart > 0) {\n\t\t\t\t\t//print.it($\"<><c green><_>{sub[docStart..m.Start]}</_><>\");\n\t\t\t\t\t_rxRecordDocParam ??= new(@\"(?ms)^\\h*///\\h*<param name=\"\"(\\w+)\"\">(.+?)</param>\\h*\\R\");\n\t\t\t\t\tif (!_rxRecordDocParam.FindAll(sub, out ap, docStart..m.Start)) ap = null;\n\t\t\t\t\t//if(ap!=null) foreach (var v in ap) print.it($\"<><c brown><_>{v}</_><>\");\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t//ctor\n\t\t\t\t_b.AppendLine();\n\t\t\t\tif (ap != null) foreach (var p in ap) _b.Append(p.Value);\n\t\t\t\t_b.AppendFormat(\"public {0}({1}){{}}\\n\", name, par);\n\t\t\t\t\n\t\t\t\t//properties\n\t\t\t\t_rxRecordParam ??= new(@\"\\G((?:[\\w\\.]+|\\([^)]+\\))(<([^<>]++|(?-2))+>)?(?:\\[\\])?) (\\w+)(?:,\\s*|$)\");\n\t\t\t\tforeach (var k in _rxRecordParam.FindAll(par)) {\n\t\t\t\t\tstring pType = k[1].Value, pName = k[4].Value;\n\t\t\t\t\t//print.it($\"type='{pType}', name='{pName}'\");\n\t\t\t\t\t_b.AppendLine();\n\t\t\t\t\tvar mp = ap?.FirstOrDefault(o => o[1].Value == pName);\n\t\t\t\t\tif (mp != null) _b.Append(\"/// <summary>\").Append(mp[2].Value).AppendLine(\"</summary>\");\n\t\t\t\t\t_b.AppendFormat(\"public {0} {1} {{ get; init; }}\\n\", pType, pName);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (noBody) _b.Append(\"}\");\n\t\t\t\t//print.it(_b);\n\t\t\t\t\n\t\t\t\t//return m.Value;\n\t\t\t\treturn _b.ToString();\n\t\t\t});\n\t\t\t//if(s.RxMatch(@\"(?m)^[\\h\\w]*\\brecord (?:class|struct)(\\w+)\\(\", out var m2)) print.it(\"regex failed\", path);\n\t\t}\n\t\t\n\t\t//if (path.Ends(\"param types.cs\")) print.it(s);\n\t\treturn s;\n\t}\n\t\n\tpublic void Postprocess(string siteDirTemp, string siteDir) {\n\t\tADL.AuDocsShared.ResolveAuApiLink = s => {\n\t\t\tforeach (var ns in _auNamespaces) {\n\t\t\t\tif (filesystem.exists(siteDirTemp + \"/api/\" + ns + s + \".html\", true).File) return \"/api/\" + ns + s + \".html\";\n\t\t\t}\n\t\t\treturn null;\n\t\t};\n\t\t\n\t\tfilesystem.delete(siteDir);\n\t\tfilesystem.createDirectory(siteDir);\n\t\tvar files = filesystem.enumFiles(siteDirTemp, flags: FEFlags.AllDescendants | FEFlags.NeedRelativePaths | FEFlags.UseRawPath).ToArray();\n\t\tParallel.ForEach(files, f => { //faster: 26 s -> 14 s; without code styling 8 s -> 3 s\n\t\t\tvar name = f.Name;\n\t\t\tvar file2 = siteDir + name;\n\t\t\tif (name.Ends(\".html\") && !name.Ends(@\"\\toc.html\")) {\n\t\t\t\t_PostprocessFile(f, file2, siteDirTemp);\n\t\t\t} else if (name.Eqi(@\"\\styles\\docfx.js\")) {\n\t\t\t\t_ProcessJs(f.FullPath, file2);\n\t\t\t} else {\n\t\t\t\tfilesystem.copy(f.FullPath, file2);\n\t\t\t\tif (name.Eqi(@\"\\xrefmap.yml\")) _XrefMap(f.FullPath);\n\t\t\t}\n\t\t});\n\t\tfilesystem.saveText(siteDir + @\"\\styles\\code.css\", ADL.AuDocsShared.CreateCodeCss());\n\t}\n\t\n\tvoid _PostprocessFile(FEFile f, string file2, string siteDirTemp) {\n\t\t//print.it($\"<><lc green>{f.Name}<>\");\n\t\tstring name = f.Name, s = filesystem.loadText(f.FullPath);\n\t\tbool isApi = name.Starts(@\"\\api\") && name != @\"\\api\\index.html\";\n\t\t//print.it(name, isApi);\n\t\t\n\t\tint nr;\n\t\tif (isApi) {\n\t\t\t//In class member pages, in title insert a link to the type.\n\t\t\tnr = s.RxReplace(@\"<h1\\b[^>]* data-uid=\"\"(Au\\.(?:Types\\.|Triggers\\.|More\\.)?+([\\w\\.`]+))\\.#?\\w+\\*?\"\"[^>]*>\\K(Method|Property|Field|Event|Operator|Constructor) (\\w+)\",\n\t\t\t\tm => {\n\t\t\t\t\tstring fileName = m[1].Value, className = m[2].Value, memberName = m[4].Value;\n\t\t\t\t\tif (className.Contains('`')) { //generic\n\t\t\t\t\t\tfileName = fileName.Replace(\"`\", \"-\");\n\t\t\t\t\t\tif (className.Ends(\"`1\")) className = className.ReplaceAt(^2.., \"&lt;T&gt;\");\n\t\t\t\t\t\telse if (className.Ends(\"`2\")) className = className.ReplaceAt(^2.., \"&lt;T1, T2&gt;\");\n\t\t\t\t\t\telse if (className.Ends(\"`3\")) className = className.ReplaceAt(^2.., \"&lt;T1, T2, T3&gt;\");\n\t\t\t\t\t\telse throw new NotImplementedException();\n\t\t\t\t\t}\n\t\t\t\t\tif (m.Subject[m[1].End + 1] == '#') return $@\"Constructor of <a href=\"\"{fileName}.html\"\">{className}</a>\"; //ctor\n\t\t\t\t\tif (memberName == \"this\") return $@\"Indexer of <a href=\"\"{fileName}.html\"\">{className}</a>\"; //indexer\n\t\t\t\t\treturn $@\"{m[3].Value} <a href=\"\"{fileName}.html\"\">{className}</a>.{memberName}\";\n\t\t\t\t},\n\t\t\t\tout s, 1);\n\t\t\t\n\t\t\t//Add \"(+ n overloads)\" link in h1 and \"(next/top)\" links in h2 if need.\n\t\t\tif (s.RxFindAll(@\"<h2 class=\"\"overload\"\" id=\"\"(.+?)\"\".*?>Overload\", out var a) && a.Length > 1) {\n\t\t\t\tvar b = new StringBuilder();\n\t\t\t\tint jPrev = 0;\n\t\t\t\tfor (int i = 0; i < a.Length; i++) {\n\t\t\t\t\tbool first = i == 0, last = i == a.Length - 1;\n\t\t\t\t\tint j = first ? s.Find(\"</h1>\") : a[i].End;\n\t\t\t\t\tb.Append(s, jPrev, j - jPrev);\n\t\t\t\t\tjPrev = j;\n\t\t\t\t\tb.Append(\"<span style='font-size:14px; font-weight: 400; margin-left:20px;'>(\");\n\t\t\t\t\tif (first) b.Append(\"+ \").Append(a.Length - 1).Append(\" \");\n\t\t\t\t\tvar href = last ? \"top\" : a[i + 1][1].Value;\n\t\t\t\t\tb.Append(\"<a href='#\").Append(href).Append(\"'>\");\n\t\t\t\t\tif (first) b.Append(\"overload\").Append(a.Length == 2 ? \"\" : \"s\");\n\t\t\t\t\telse b.Append(last ? \"top\" : \"next\");\n\t\t\t\t\tb.Append(\"</a>)</span>\");\n\t\t\t\t}\n\t\t\t\tb.Append(s, jPrev, s.Length - jPrev);\n\t\t\t\ts = b.ToString();\n\t\t\t}\n\t\t\t\n\t\t\t//Remove anchor from the first hidden overload <h2>, and add at the very top (before <header>).\n\t\t\t//\tWithout it would not work links to the top overload from others overloads in the same page.\n\t\t\t//\tIn the past needed this to prevent incorrect scrolling. It seems current web browsers don't scroll.\n\t\t\tif (s.RxMatch(@\"(<h2 class=\"\"overload\"\")( id=\"\".+?\"\" data-uid=\"\".+?\"\")\", out var m)) {\n\t\t\t\ts = s.ReplaceAt(m[2], \"\");\n\t\t\t\ts = s.RxReplace(@\"<a name=\"\"top\"\"\\K\", m[2].Value, 1);\n\t\t\t}\n\t\t\t\n\t\t\t//Replace <seealso> link. DocFX sets incorrect text and incorrect URL.\n\t\t\t//\tFor <see> we specify correct text when preprocessing, but for <seealso> DocFX ignores that text.\n\t\t\t//\tWorkaround: when preprocessing, before <seealso cref> insert <seealso href> with correct text. Now regex replace.\n\t\t\t_rxSeealso1 ??= new(\"\"\"(?ms)^\\h*<div class=\"seealso\">\\R(.+?)\\R\\h*</div>\"\"\");\n\t\t\ts = _rxSeealso1.Replace(s, m1 => {\n\t\t\t\tvar v = m1.Value;\n\t\t\t\t//bool debug = v.Contains(\"run.console\");\n\t\t\t\t//if (debug) print.it(\"-----\");\n\t\t\t\t\n\t\t\t\t//current docfx in method link text adds parameters with links to types\n\t\t\t\t_rxSeealso3 ??= new(\"\"\"(?><div><a class=\"xref\".+?</a>)\\K.+(?=</div>)\"\"\");\n\t\t\t\tv = _rxSeealso3.Replace(v, \"\");\n\t\t\t\t//if (debug) print.it(v);\n\t\t\t\t\n\t\t\t\t//bug in current docfx: method href ends with `(raw parameters).html` instead of `.html#ns_type_name_para_meters_`. Also, generic method page name ends like `--2`.\n\t\t\t\t_rxSeealso4 ??= new(\"\"\"<a class=\"xref\" href=\"\\K((.+?)(?:--\\d+)?\\(.+?\\))\\.html\"\"\");\n\t\t\t\t_rxSeealso5 ??= new(@\"\\W\");\n\t\t\t\tv = _rxSeealso4.Replace(v, m2 => {\n\t\t\t\t\t//if (debug) print.it(\"-\", m2.Value);\n\t\t\t\t\treturn m2[2].Value + \".html#\" + _rxSeealso5.Replace(m2[1].Value, \"_\");\n\t\t\t\t});\n\t\t\t\t//if (debug) print.it(v);\n\t\t\t\t\n\t\t\t\t//replace link text\n\t\t\t\t_rxSeealso2 ??= new(\"\"\"(?m)^.+?<a href=\"https://text\">(.+?)</a>.+\\R(.+?<a class=\"xref\".+?>)(.+?)(?=</a>)\"\"\");\n\t\t\t\tv = _rxSeealso2.Replace(v, m3 => m3[2].Value + System.Net.WebUtility.HtmlEncode(Encoding.UTF8.GetString(Convert2.HexDecode(m3[1].Value))));\n\t\t\t\t//if (debug) print.it(v);\n\t\t\t\t\n\t\t\t\treturn v;\n\t\t\t});\n\t\t\t\n\t\t\t//ungroup Classes, Structs etc in namespace pages. Eg would be at first class screen.at and then separately struct screen.\n\t\t\tif (0 != name.Ends(true, @\"\\Au.html\", @\"\\Au.More.html\", @\"\\Au.Types.html\", @\"\\Au.Triggers.html\") && s.RxMatch(@\"(?sm)(^\\h*<h2 .+?)</article>\", 1, out RXGroup g)) {\n\t\t\t\tvar k = s.RxFindAll(\"\"\"(?ms)^\\h*<h5 class=\"ns\"><a .+?>(.+?)</a>.+?</section>\\R\"\"\", 0, g).OrderBy(o => o[1].Value);\n\t\t\t\ts = s.ReplaceAt(g, string.Join(\"\", k));\n\t\t\t}\n\t\t\t\n\t\t\t//second part of the damaged spaces workaround\n\t\t\ts = s.Replace('\\u202F', ' ');\n\t\t} else {\n\t\t\ts = ADL.AuDocsShared.PostprocessHtmlNonApi(name, s);\n\t\t}\n\t\t\n\t\ts = ADL.AuDocsShared.PostprocessHtmlCommon(f.Name, s, isApi);\n\t\t\n#if DISQUS\n\t\t//TODO3: Now shows Disqus content when page loaded (if small) or scrolled to the bottom. Should show only when clicked <h2>User comments</h2>.\n\t\t\n\t\t//add this at the bottom of help pages\n\t\tvar disqus = \"\"\"\n\n<hr/>\n<h2>User comments</h2>\n<div id=\"disqus_thread\"></div>\n<script>\n    /**\n    *  RECOMMENDED CONFIGURATION VARIABLES: EDIT AND UNCOMMENT THE SECTION BELOW TO INSERT DYNAMIC VALUES FROM YOUR PLATFORM OR CMS.\n    *  LEARN WHY DEFINING THESE VARIABLES IS IMPORTANT: https://disqus.com/admin/universalcode/#configuration-variables    */\n    /*\n    var disqus_config = function () {\n    this.page.url = PAGE_URL;  // Replace PAGE_URL with your page's canonical URL variable\n    this.page.identifier = PAGE_IDENTIFIER; // Replace PAGE_IDENTIFIER with your page's unique identifier variable\n    };\n    */\n    (function() { // DON'T EDIT BELOW THIS LINE\n    var d = document, s = d.createElement('script');\n    s.src = 'https://libreautomate.disqus.com/embed.js';\n    s.setAttribute('data-timestamp', +new Date());\n    (d.head || d.body).appendChild(s);\n    })();\n</script>\n\n\"\"\";\n\t\tint i1=s.LastIndexOf(\"</article>\");\n\t\tif(i1>0) s = s.Insert(i1, disqus);\n\t\telse print.warning(\"no </article> in \" + name);\n#endif\n\t\t\n\t\t//print.it(s);\n\t\tfilesystem.saveText(file2, s);\n\t}\n\t\n\tstatic void _ProcessJs(string file, string file2) {\n\t\tvar s = filesystem.loadText(file);\n\t\t\n\t\t//don't need to highlight code. We do it at build time.\n\t\ts = s.Replace(\"highlight();\", \"\");\n\t\t\n\t\t//prevent adding <wbr> in link text\n\t\ts = s.Replace(\"breakText();\", \"\").Replace(\"$(e).breakWord();\", \"\");\n\t\t\n\t\t//we process tables and alerts in HTML at build time. At run time the repainting is visible, and it slows down page loading, + possible scrolling problems.\n\t\ts = s.Replace(\"renderTables();\", \"\").Replace(\"renderAlerts();\", \"\");\n\t\t\n\t\t//prevent adding footer when scrolled to the bottom\n\t\ts = s.Replace(\"renderFooter();\", \"\");\n\t\t//Somehow adds anyway. Need to add empty footer.tmpl.partial. But this code line does not harm.\n\t\t\n\t\tfilesystem.saveText(file2, s);\n\t}\n\t\n\t//From xrefmap.yml extracts conceptual topics and writes to _\\xrefmap.yml.\n\t//Could simply copy the file, but it is ~2 MB, and we don't need api topics.\n\t//Editor uses it to resolve links in code info.\n\tstatic void _XrefMap(string file) {\n\t\tvar b = new StringBuilder();\n\t\tvar s = filesystem.loadText(file);\n\t\tforeach (var m in s.RxFindAll(@\"(?m)^- uid:.+\\R.+\\R  href: (?!api/).+\\R\", (RXFlags)0)) {\n\t\t\t//print.it(m);\n\t\t\tb.Append(m);\n\t\t}\n\t\t\n\t\tfilesystem.saveText(folders.ThisAppBS + \"xrefmap.yml\", b.ToString());\n\t}\n}\n"
  },
  {
    "path": "Scripts/@Au docs/AuDocs.cs",
    "content": "extern alias CAW;\n\nusing Microsoft.CodeAnalysis;\nusing CAW::Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp;\nusing Microsoft.CodeAnalysis.Text;\nusing Microsoft.CodeAnalysis.CSharp.Syntax;\nusing Microsoft.CodeAnalysis.Shared.Extensions;\nusing CAW::Microsoft.CodeAnalysis.Shared.Extensions;\nusing Microsoft.CodeAnalysis.CSharp.Extensions;\nusing System.Collections.Immutable;\nusing System.Xml.Linq;\nusing Markdig;\nusing System.Xml;\n\npartial class AuDocs {\n\tCSharpCompilation _compilation;\n\tCSharpSemanticModel _semo;\n\tStringBuilder _b = new(), _bFile = new();\n\tint _filePos;\n\tstring _code;\n\tint _currentFileIndex;\n\tISymbol _currentSym;\n\tMarkdownPipeline _pipeline;\n\t_Analyze _analyze;\n\t\n\tstring[] _auNamespaces = { \"Au.Types.\", \"Au.More.\", \"Au.Triggers.\", \"Au.\" };\n\tHashSet<string> _hsException = new(), _hsSeealso = new();\n\tregexp _rxLineStart = new(@\"(?m)^\");\n\tregexp _rxEmptyLine = new(@\"\\R\\h*\\R\");\n\tregexp _rxC = new(@\"(?s)<c><!\\[CDATA\\[(<c.+?</c>)\\]\\]></c>\");\n\tregexp _rxLi = new(@\"(?m)^(\\h*)<br /> *•\");\n\tregexp _rxLi2 = new(@\"(?m)^\\h*-\");\n\tregexp _rxIndentation, _rxIndentation2;\n\tregexp _rxParams;\n\tregexp _rxFixInherited = new(@\"(</(summary|param|typeparam|remarks|example|returns|value|exception|seealso|code|note|para|table|div|h\\d|ul|ol)>)(<(?2)\\b)\");\n\tregexp _rxFixInheritedBr = new(@\"(</[a-z]+>)(?=<br\\b)\");\n\tregexp _rxParaNote = new(@\"<(para|note)\\b.*>((?s).+?)</\\1>\");\n\tregexp _rxLineBreak = new(@\"(?m)\\\\(?!$)\");\n\t\n\tpublic AuDocs() {\n\t\tvar p = new MarkdownPipelineBuilder();\n\t\tp.BlockParsers.RemoveAll(o => o is Markdig.Parsers.IndentedCodeBlockParser or Markdig.Parsers.HeadingBlockParser);\n\t\t\n\t\t//rejected: disable EmphasisInlineParser (*, _), CodeInlineParser (`), EscapeInlineParser (\\). It also disables the pipe tables extension and maybe some other. Instead we'll temporarily escape these characters.\n\t\t//p.InlineParsers.RemoveAll(o => o is not (Markdig.Parsers.Inlines.HtmlEntityParser or Markdig.Parsers.Inlines.LinkInlineParser or Markdig.Parsers.Inlines.AutolinkInlineParser or Markdig.Parsers.Inlines.LineBreakInlineParser or Markdig.Parsers.Inlines.LiteralInlineParser));\n\t\t\n\t\tp.DisableHeadings();\n\t\tp.UsePipeTables();\n\t\t_pipeline = p.Build();\n\t\t\n\t\t_analyze = new();\n\t\t\n\t\topt.warnings.Verbose = true;\n\t}\n\t\n\tpublic void Preprocess(string sourceDir, string destDir, bool testSmall) {\n\t\tfilesystem.delete(destDir);\n\t\tfilesystem.createDirectory(destDir);\n\t\t\n\t\t//info: currently there are no excluded .cs files. Would need to parse Au.csproj.\n\t\t\n\t\tvar parseOpt = new CSharpParseOptions(LanguageVersion.Preview, DocumentationMode.Parse);\n\t\tvar trees = new List<CSharpSyntaxTree>();\n\t\tvar files = new List<(string path, string code)>();\n\t\t\n\t\tforeach (var f in filesystem.enumerate(sourceDir, FEFlags.NeedRelativePaths, dirFilter: o => o.Level == 0 && 0 != o.Name.Eq(true, @\"\\obj\", @\"\\bin\", @\"\\docfx_project\") ? 0 : 2)) {\n\t\t\tvar path = f.FullPath;\n\t\t\tif (f.Name.Ends(\".cs\", true)) {\n\t\t\t\tif (testSmall && f.Name == @\"\\_Aaa.cs\") continue;\n\t\t\t\tvar code = filesystem.loadText(path);\n\t\t\t\tcode = _PreprocessFileAsText(path, code);\n\t\t\t\tvar tree = CSharpSyntaxTree.ParseText(code, parseOpt, f.Name) as CSharpSyntaxTree;\n\t\t\t\ttrees.Add(tree);\n\t\t\t\tfiles.Add((f.Name, code));\n\t\t\t} else if (f.Name.Eqi(@\"\\Au.csproj\")) {\n\t\t\t\tvar s = filesystem.loadText(path);\n\t\t\t\ts = s.RxReplace(@\"(?ms)^\\h*<COMReference\\b.+?</COMReference>\", \"\", 1); //mute docfx warning\n\t\t\t\ts = s.Replace(\"<SignAssembly>true</SignAssembly>\", \"\");\n\t\t\t\ts = s.RxReplace(\"<AssemblyOriginatorKeyFile>.+?</AssemblyOriginatorKeyFile>\", \"\");\n\t\t\t\tfilesystem.saveText(destDir + f.Name, s);\n\t\t\t} else {\n\t\t\t\tfilesystem.copy(path, destDir + f.Name);\n\t\t\t}\n\t\t}\n\t\t\n\t\tvar refs = LA.MetaReferences.DefaultReferences.Values;\n\t\tvar compOpt = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, allowUnsafe: true);\n\t\t_compilation = CSharpCompilation.Create(\"doc\", trees, refs, compOpt);\n\t\t\n\t\t//note: cannot get all declared symbols directly from Compilation. Somehow _compilation.GetSymbolsWithName skips ctors etc. Also tried other classes, unsuccessfully.\n\t\t//print.it(_compilation.GetSymbolsWithName(o=>true).Where(o=>!o.IsImplicitlyDeclared && o.HasPublicResultantVisibility()).Select(o=>o.ToString()).OrderBy(o=>o));\n\t\t//Microsoft.CodeAnalysis.FindSymbols.SymbolFinder.FindSourceDeclarationsAsync //not tested\n\t\t\n\t\tfor (int i = 0; i < trees.Count; i++) {\n\t\t\t_currentFileIndex = i;\n\t\t\tvar tree = trees[i];\n\t\t\t_semo = _compilation.GetSemanticModel(tree) as CSharpSemanticModel;\n\t\t\t\n\t\t\t//if(!tree.FilePath.Ends(\"\\\\filesystem.cs\")) continue;\n\t\t\t_bFile.Clear();\n\t\t\t_filePos = 0;\n\t\t\tvar f = files[i];\n\t\t\t_code = f.code;\n\t\t\t_PreprocessFile(tree);\n\t\t\t_bFile.Append(f.code, _filePos, f.code.Length - _filePos);\n\t\t\tvar s = _bFile.ToString();\n\t\t\tfilesystem.saveText(destDir + f.path, s);\n\t\t\t\n\t\t\t//print.it($\"<><lc green>{f.path}<>\");\n\t\t\t//print.it(s);\n\t\t\t//if(f.path.Ends(\"param types.cs\")) print.it(s);\n\t\t}\n\t\t\n\t\t_analyze.Finally();\n\t}\n\t\n\tvoid _PreprocessFile(CSharpSyntaxTree tree) {\n\t\t_analyze.StartingFile(_semo, _currentFileIndex);\n\t\t\n\t\tvar root = tree.GetCompilationUnitRoot();\n\t\tforeach (var m1 in root.Members) {\n\t\t\tif (m1 is not BaseNamespaceDeclarationSyntax ns) throw new Exception(\"not namespace: \" + m1);\n\t\t\t_Namespace(ns);\n\t\t}\n\t}\n\t\n\tvoid _Namespace(BaseNamespaceDeclarationSyntax ns) {\n\t\t_analyze.AnalyzeType(ns);\n\t\t\n\t\tforeach (var m2 in ns.Members) {\n\t\t\tswitch (m2) {\n\t\t\tcase NamespaceDeclarationSyntax ns2:\n\t\t\t\t_Namespace(ns2);\n\t\t\t\tbreak;\n\t\t\tcase BaseTypeDeclarationSyntax type:\n\t\t\t\t_Type(type);\n\t\t\t\tbreak;\n\t\t\tcase DelegateDeclarationSyntax del:\n\t\t\t\t_DocComments(del);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tthrow new Exception(\"not type or namespace: \" + m2);\n\t\t\t}\n\t\t}\n\t\t\n\t\t_analyze.AnalyzeType(null);\n\t}\n\t\n\tvoid _Type(BaseTypeDeclarationSyntax type) {\n\t\tif (!_DocComments(type)) return;\n\t\tif (type is TypeDeclarationSyntax td) {\n\t\t\t\n\t\t\t//remove some attributes\n\t\t\tforeach (var al in td.AttributeLists) {\n\t\t\t\tif (al.ToString() is \"[ComVisible(true)]\") _SkipSource(al.Span);\n\t\t\t}\n\t\t\t\n\t\t\tforeach (var m in td.Members) {\n\t\t\t\tif (m is BaseTypeDeclarationSyntax t2) _Type(t2);\n\t\t\t\telse _DocComments(m);\n\t\t\t}\n\t\t} else if (type is EnumDeclarationSyntax ed) {\n\t\t\tforeach (var m in ed.Members) {\n\t\t\t\t_DocComments(m, _semo.GetDeclaredSymbol(m));\n\t\t\t}\n\t\t} else throw new Exception(\"type: \" + type);\n\t\t\n\t\t_analyze.AnalyzeType(null);\n\t}\n\t\n\t//returns true if public\n\tbool _DocComments(MemberDeclarationSyntax m, ISymbol sym = null) {\n\t\t//is public?\n\t\tif (sym == null) { //else enum member\n\t\t\tsym = m is BaseFieldDeclarationSyntax f ? _semo.GetDeclaredSymbol(f.Declaration.Variables[0]) : _semo.GetDeclaredSymbol(m);\n\t\t\tif (sym == null) { _Warning(\"Declared symbol not found\", m); return false; }\n\t\t\tif (!sym.HasPublicResultantVisibility()) return false;\n\t\t\t\n\t\t\tif (m is BaseTypeDeclarationSyntax btd) _analyze.AnalyzeType(btd);\n\t\t}\n\t\t\n\t\tvar lt = m.GetLeadingTrivia();\n\t\tvar trivia = lt.LastOrDefault(o => o.IsKind(SyntaxKind.SingleLineDocumentationCommentTrivia));\n\t\tvar span = trivia.Span;\n\t\tbool autoInheritdoc = false;\n\t\tif (trivia.RawKind == 0) {\n\t\t\tautoInheritdoc = _IsEligibleForAutomaticInheritdoc(sym);\n\t\t\tif (!autoInheritdoc) return true;\n\t\t\t//tested: DocFX does not get method documentation from overriden base method or implemented interface method.\n\t\t} else if (span.Length < 10) return true; //length 2 if just ///\n\t\t\n\t\tvar sdoc = autoInheritdoc ? \"\" : _code[(span.Start + (_code[span.Start] == ' ' ? 1 : 0))..span.End];\n\t\t_currentSym = sym;\n\t\t_analyze.AnalyzeDoc(sdoc, sym);\n\t\t\n\t\tif (span.Length > 0) {\n\t\t\t_SkipSource(span.Start - 3, span.End);\n\t\t} else {\n\t\t\tint i = m.SpanStart;\n\t\t\t_SkipSource(i, i);\n\t\t\t_bFile.AppendLine();\n\t\t}\n\t\t\n\t\t//print.it($\"<><c brown>{AuDocs.VsGoto(sym)}<>\");\n\t\t\n\t\tstring sxml;\n\t\tif (autoInheritdoc || sdoc.Contains(\"<inheritdoc\")) {\n\t\t\tsxml = sym.GetDocumentationComment(_compilation, expandIncludes: true, expandInheritdoc: true).FullXmlFragment;\n\t\t\t\n\t\t\t//inherited XML now is without newlines between block tags. Restore.\n\t\t\tif (0 != _rxFixInherited.Replace(sxml, \"$1\\n$3\", out sxml)) {\n\t\t\t\t//print.it($\"<><c brown>{sym}<>\");\n\t\t\t\t//print.it(sxml);\n\t\t\t}\n\t\t\tif (0 != _rxFixInheritedBr.Replace(sxml, \"$1\\n\", out sxml)) {\n\t\t\t\t//print.it($\"<><c brown>{sym}<>\");\n\t\t\t\t//print.it(sxml);\n\t\t\t}\n\t\t} else { //faster without GetDocumentationComment, which calls GetDocumentationCommentXml and then creates DocumentationComment, which parses XML again\n\t\t\tsxml = sym.GetDocumentationCommentXml(expandIncludes: true);\n\t\t}\n\t\t\n\t\tif (sxml.Starts(\"<!-- Badly formed XML comment ignored\")) { //eg EFileInfo.kind (record) if <param> text empty (add space to avoid this warning)\n\t\t\tprint.it(sxml);\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\t//remove indentation added by GetDocumentationCommentXml etc\n\t\t_rxIndentation ??= new(@\"(?>\\n) +\");\n\t\tif (!sxml.Starts(\"<doc>\") && _rxIndentation.Match(sxml, 0, out string si)) {\n\t\t\tif (si.Length is not (5 or 6)) throw new InvalidOperationException(); //\\n and 4-5 spaces\n\t\t\t_rxIndentation2 ??= new(@\"(?m)^     ?(?=[^ ])\");\n\t\t\tsxml = _rxIndentation2.Replace(sxml, \"\");\n\t\t\t//print.it(\"-----\");\n\t\t\t//print.it(sxml);\n\t\t}\n\t\t//print.it(sxml);\n\t\t\n\t\t_b.Clear();\n\t\t\n\t\tvar xr = XElement.Parse(sxml, LoadOptions.PreserveWhitespace);\n\t\t\n\t\tvoid _Typeparam(ImmutableArray<ITypeParameterSymbol> a) {\n\t\t\tforeach (var p in a) {\n\t\t\t\tif (xr.Elem(\"typeparam\", \"name\", p.Name) is XElement x) _Append(x);\n\t\t\t}\n\t\t}\n\t\tvoid _Param(ImmutableArray<IParameterSymbol> a) {\n\t\t\tforeach (var p in a) {\n\t\t\t\tvar x = xr.Elem(\"param\", \"name\", p.Name);\n\t\t\t\tif (x == null || _IsXElementWithoutValue(x)) { //if enum, add enum members. Useful for AI.\n\t\t\t\t\tif (p.Type is INamedTypeSymbol ptype && ptype.IsEnumType()) {\n\t\t\t\t\t\tvar mn = ptype.MemberNames;\n\t\t\t\t\t\tif (!mn.TryGetNonEnumeratedCount(out int n)) throw null;\n\t\t\t\t\t\tif (n <= 16) {\n\t\t\t\t\t\t\tx ??= new(\"param\", new XAttribute(\"name\", p.Name));\n\t\t\t\t\t\t\tx.Value = $\"Enum: {string.Join(\", \", mn)}.\";\n\t\t\t\t\t\t\t//bool isFlags = ptype.GetAttributes().Any(a => a.AttributeClass?.ToDisplayString() == \"System.FlagsAttribute\");\n\t\t\t\t\t\t\t//x.Value = $\"Enum{(isFlags ? \" (flags)\" : \"\")}: {string.Join(\", \", mn)}.\"; //rejected. Most parameters are named `flags` etc.\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (x == null || !_Append(x)) {\n\t\t\t\t\t_analyze.MissingParam(p, x);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\t//add only parameters of this symbol (ignore inherited)\n\t\tif (sym is INamedTypeSymbol nt) {\n\t\t\tif (nt.IsGenericType) _Typeparam(nt.TypeParameters);\n\t\t} else if (sym is IMethodSymbol ims) {\n\t\t\tif (ims.IsGenericMethod) _Typeparam(ims.TypeParameters);\n\t\t\t_Param(ims.Parameters);\n\t\t} else if (sym is IPropertySymbol ips && ips.IsIndexer) {\n\t\t\t_Param(ips.Parameters);\n\t\t}\n\t\t\n\t\tint nSummary = 0, nRemarks = 0, nExample = 0, nReturns = 0, nValue = 0; _hsException.Clear(); _hsSeealso.Clear(); //ignore inherited duplicates\n\t\tforeach (var x in xr.Elements()) {\n\t\t\tvar tag = x.Name.LocalName;\n\t\t\t\n\t\t\tif (sym.Kind is SymbolKind.Field && sym.ContainingType.TypeKind is TypeKind.Enum) {\n\t\t\t\tif (tag != \"summary\") _Warning($\"Enum member with <{tag}>. Only <summary> will be used.\", sym);\n\t\t\t}\n\t\t\t\n\t\t\tswitch (tag) {\n\t\t\tcase \"param\" or \"typeparam\":\n\t\t\t\tbreak;\n\t\t\tcase \"summary\":\n\t\t\t\tif (nSummary++ == 0) _Append(x);\n\t\t\t\tbreak;\n\t\t\tcase \"remarks\":\n\t\t\t\tif (nRemarks++ == 0) _Append(x);\n\t\t\t\tbreak;\n\t\t\tcase \"example\":\n\t\t\t\tif (nExample++ == 0) _Append(x);\n\t\t\t\tbreak;\n\t\t\tcase \"returns\":\n\t\t\t\tif (nReturns++ == 0) _Append(x);\n\t\t\t\t//if (sym is not IMethodSymbol) _Warning(\"<returns> for a non-method.\", x); //it's OK, for properties DocFX interprets it as <value>\n\t\t\t\tbreak;\n\t\t\tcase \"value\":\n\t\t\t\tif (nValue++ == 0) _Append(x);\n\t\t\t\tbreak;\n\t\t\tcase \"exception\":\n\t\t\t\tif (_hsException.Add(x.Attr(\"cref\"))) _Append(x, true);\n\t\t\t\tbreak;\n\t\t\tcase \"seealso\":\n\t\t\t\tif (_hsSeealso.Add(x.Attr(\"cref\"))) _Append(x, true);\n\t\t\t\tbreak;\n\t\t\tcase \"inheritdoc\":\n\t\t\t\tif (!autoInheritdoc) _Warning(\"Non-expanded <inheritdoc>. Try to fully qualify a parameter type.\", sxml);\n\t\t\t\tcontinue;\n\t\t\tcase \"exclude\":\n\t\t\t\t_Warning(\"Use [NoDoc] instead.\", x);\n\t\t\t\t//_b.Append(\"<exclude/>\");\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\t_Warning(\"unsupported tag\", x);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\t\n\t\tsxml = _b.ToString();\n\t\tsdoc = _rxLineStart.Replace(sxml, \"/// \");\n\t\t//print.it(sdoc);\n\t\t_bFile.Append(sdoc);\n\t\t\n\t\t_analyze.AnalyzeDoc2(xr, sxml);\n\t\t\n\t\treturn true;\n\t\t\n\t\tstatic bool _IsXElementWithoutValue(XElement x) => x.IsEmpty || x.FirstNode == null || (!x.HasElements && string.IsNullOrWhiteSpace(x.Value));\n\t\t\n\t\tbool _Append(XElement xp, bool hasCref = false) {\n\t\t\tif (hasCref) _Cref(xp);\n\t\t\t\n\t\t\tif (_IsXElementWithoutValue(xp)) {\n\t\t\t\t_b.AppendLine(xp.ToString());\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t\n\t\t\t//catch some mistakes of using HTML blocks in text that will be parsed by Markdig\n\t\t\tbool hasParaNote = false;\n\t\t\tforeach (var x in xp.Elements()) {\n\t\t\t\tvar tag = x.Name.LocalName;\n\t\t\t\tswitch (tag) {\n\t\t\t\tcase \"b\" or \"i\" or \"br\" or \"a\" or \"see\" or \"c\" or \"google\" or \"ms\" or \"sqlite\":\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"code\":\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"note\" or \"para\":\n\t\t\t\t\t//if <note> in param, in intellisense it is displayed as simple text without NOTE etc in the same line. Instead use <para>NOTE: text</para>, and here replace with <note type=\"note\">Text</note>.\n\t\t\t\t\tif (tag[0] == 'n') {\n\t\t\t\t\t\tif (xp.Name.LocalName is \"param\" or \"typeparam\" or \"summary\") _Warning(\"<note> in param or summary. Bad for intellisense. Use <para>NOTE: Text</para>. The NOTE is text for note type attribute, and can be any uppercase text.\", xp);\n\t\t\t\t\t} else if (x.FirstNode is XText t1 && t1 is not XCData) {\n\t\t\t\t\t\tvar s2 = t1.Value;\n\t\t\t\t\t\tif (s2.Contains(':') && s2.RxMatch(@\"\\s*([A-Z]+):\\s+((?s).+)\", out var m2)) {\n\t\t\t\t\t\t\tt1.Value = m2[2].Value;\n\t\t\t\t\t\t\tx.Name = \"note\";\n\t\t\t\t\t\t\tx.SetAttributeValue(\"type\", m2[1].Value.Lower());\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t_EnsureSeparatedWithEmptyLine(x, false); _EnsureSeparatedWithEmptyLine(x, true); //prevent joining with the adjacent <p> etc\n\t\t\t\t\tx.AddFirst(\"\\n\\n\"); x.Add(\"\\n\\n\"); //enable parsing markdown inside (don't interpret it as a HTML block)\n\t\t\t\t\thasParaNote = true;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"list\":\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"table\" or \"div\" or \"h3\" or \"h4\" or \"h5\" or \"h6\" or \"ul\" or \"ol\":\n\t\t\t\t\t_AssertLine(x, false);\n\t\t\t\t\t_EnsureSeparatedWithEmptyLine(x, true); //after a HTML block need an empty line to turn on markdown parsing\n\t\t\t\t\tvar s1 = x.ToString();\n\t\t\t\t\tif (0 != (1 & _rxEmptyLine.FindAllG(s1, 0).Count())) _Warning(\"A HTML block element contains odd count of empty lines. Possibly will be incorrectly parsed markdown.\", s1);\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\t//print.it(x.Name.LocalName, sym);\n\t\t\t\t\t_Warning(\"Please add this tag to the switch block: \" + tag, xp);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tbool hasC = false;\n\t\t\tforeach (var x in xp.Descendants()) {\n\t\t\t\tvar tag = x.Name.LocalName;\n\t\t\t\tswitch (tag) {\n\t\t\t\tcase \"see\":\n\t\t\t\t\t_Cref(x);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"google\" or \"ms\" or \"sqlite\":\n\t\t\t\t\tx.Name = \"a\";\n\t\t\t\t\tvar site = tag[0] switch { 'm' => \"+site:microsoft.com\", 's' => \"+site:sqlite.org\", _ => \"\" };\n\t\t\t\t\tx.SetAttributeValue(\"href\", $\"https://www.google.com/search?q={System.Net.WebUtility.UrlEncode(x.Value)}{site}\");\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"c\":\n\t\t\t\t\tif (x.FirstNode is XCData) break;\n\t\t\t\t\tx.ReplaceAll(new XCData(x.ToString()));\n\t\t\t\t\thasC = true;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"code\":\n\t\t\t\t\t_AssertLine(x, false);\n\t\t\t\t\t_AssertLine(x, true);\n\t\t\t\t\t//replace <code> -> <pre>, and Base64-encode.\n\t\t\t\t\t//\tMarkdig skips <pre>...</pre>, even if contains empty lines.\n\t\t\t\t\t//\tDocFX will not replace with <pre><code> which then would be parsed by the DocFX code highlighter at run time.\n\t\t\t\t\t//\tWhen postprocessing, we'll set colors using Roslyn.\n\t\t\t\t\tstring code = null;\n\t\t\t\t\tswitch (x.FirstNode) {\n\t\t\t\t\tcase XCData xx: code = xx.Value; break;\n\t\t\t\t\tcase XText xx: code = xx.Value; break; //must be without html entities\n\t\t\t\t\tdefault: print.it(x.FirstNode.NodeType); continue;\n\t\t\t\t\t}\n\t\t\t\t\tx.Name = \"pre\";\n\t\t\t\t\tx.Value = \"%%\" + Convert.ToBase64String(Encoding.UTF8.GetBytes(code.TrimStart(\"\\r\\n\"))) + \"%%\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"seealso\":\n\t\t\t\t\t_Warning(\"<seealso> in text.\", x);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"list\":\n\t\t\t\t\t_Warning(\"Don't use <list>. Instead use <ul> or <ol> or markdown '- item' or '<br/>• item'.\", x);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tuint escaped = 0;\n\t\t\tforeach (var t in xp.DescendantNodes().OfType<XText>()) {\n\t\t\t\tif (t is XCData) continue;\n\t\t\t\tvar v = t.Value;\n\t\t\t\t//workaround for DocFX bug: removes space between two tags. In some cases breaks line instead. Tested: currently no such text in cookbook and markdown articles.\n\t\t\t\tif (v == \" \") {\n\t\t\t\t\t//print.it(\"<>\" + t.Parent.ToString().RxReplace(@\"/\\w+> <\\w+\", \"<bc yellow>$0<>\"));\n\t\t\t\t\tt.Value = \"\\u202F\"; //narrow non-breaking space; will replace with normal space when postprocessing\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t//escape some markdown inline characters\n\t\t\t\tif (v.Contains('*')) { escaped |= 1; v = v.Replace(\"*\", \",.1.,\"); }\n\t\t\t\tif (v.Contains('_')) { escaped |= 2; v = v.Replace(\"_\", \",.2.,\"); }\n\t\t\t\tif (v.Contains('`')) { escaped |= 4; v = v.Replace(\"`\", \",.3.,\"); }\n\t\t\t\tif (v.Contains('\\\\') && _rxLineBreak.Replace(v, \",.4.,\", out v) > 0) escaped |= 8; //escape \\char but not \\endofline\n\t\t\t\tif (escaped > 0) t.Value = v;\n\t\t\t}\n\t\t\t\n\t\t\t//print.it(\"---\");\n\t\t\tvar sx = xp.ToString();\n\t\t\t//print.it(sx);\n\t\t\tint from = sx.IndexOf('>') + 1, to = sx.LastIndexOf('<');\n\t\t\t//print.it(from, to);\n\t\t\tvar s = sx[from..to];\n\t\t\t//print.it(s);\n\t\t\t_b.Append(sx, 0, from);\n\t\t\t\n\t\t\t//list items should start with <br/>• to display in intellisense as list. Else would need <list ...><item><description>...</description></item>...\n\t\t\t//\tnote: by default VS saves not as UTF-8. To make it save as UTF-8, create or edit file \".editorconfig\" in the solution dir, and add this:\n\t\t\t//\t\t[*]\n\t\t\t//\t\tcharset = utf-8\n\t\t\tif (0 == _rxLi.Replace(s, \"$1-\", out s))\n\t\t\t\tif (xp.Name.LocalName is \"param\" or \"returns\" or \"summary\" && _rxLi2.IsMatch(s)) _Warning(\"Markdown list items in <summary>, <param> and <returns> should start with <br/>• (Alt+L) to display correctly in intellisense.\", s);\n\t\t\t\n\t\t\ts = Markdig.Markdown.ToHtml(s, _pipeline);\n\t\t\t\n\t\t\t//if (s.Starts(\"<p>\") && s.Ends(\"</p>\\n\") && s.Find(\"<p>\", 3) < 0) s = s[3..^5]; //no, need <p> for CSS white-space: pre-line\n\t\t\tif (hasParaNote) s = _rxParaNote.Replace(s, m => m.Value.Replace(\"<p>\", \"\").Replace(\"</p>\", \"\")); //remove <p> from <para> and <note>\n\t\t\tif (hasC) s = _rxC.Replace(s, \"$1\");\n\t\t\tif (0 != (escaped & 1)) s = s.Replace(\",.1.,\", \"*\");\n\t\t\tif (0 != (escaped & 2)) s = s.Replace(\",.2.,\", \"_\");\n\t\t\tif (0 != (escaped & 4)) s = s.Replace(\",.3.,\", \"`\");\n\t\t\tif (0 != (escaped & 8)) s = s.Replace(\",.4.,\", \"\\\\\");\n\t\t\t\n\t\t\t_b.Append(s);\n\t\t\t_b.Append(sx, to, sx.Length - to);\n\t\t\t_b.Append('\\n');\n\t\t\t\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\t//from Roslyn method GetDocumentationComment\n\t\tstatic bool _IsEligibleForAutomaticInheritdoc(ISymbol symbol) {\n\t\t\tif (symbol.IsOverride) return true;\n\t\t\tif (symbol.ContainingType is null) return false;\n\t\t\tif (symbol.Kind is SymbolKind.Method or SymbolKind.Property or SymbolKind.Event) return symbol.ExplicitOrImplicitInterfaceImplementations().Any();\n\t\t\treturn false;\n\t\t}\n\t}\n\t\n\tvoid _Cref(XElement x) {\n\t\tstring s = x.Attr(\"cref\");\n\t\tif (s == null) return; //href\n\t\t//print.it(s);\n\t\tvar kind = s[0];\n\t\tbool removeParameters = false, overload = false;\n\t\t\n\t\t//restore cref modified by GetDocumentationComment\n\t\tif (s.FindAny(\"`([\") < 0) { //simple. Also GetFirstSymbolForDeclarationId can't find indexers where s ends with \".Item\"\n\t\t\ts = s[2..];\n\t\t} else {\n\t\t\tvar syms = DocumentationCommentId.GetSymbolsForDeclarationId(s, _compilation);\n\t\t\tif (syms.Length == 0) {\n\t\t\t\t_Warning(\"Can't find cref symbol \" + s, x); //none\n\t\t\t\ts = s[2..].Replace(\"@\", \"\"); //never mind: should replace ``1 etc for generic. Difficult etc.\n\t\t\t\tremoveParameters = kind is 'M' or 'P'; //method, indexer\n\t\t\t} else {\n\t\t\t\tvar sym = syms[0];\n\t\t\t\ts = sym.ToDisplayString(_formatCref); //same as ToString except: tuple (...) -> ValueTuple<...>. DocFX does not support tuple (...).\n\t\t\t\t\n\t\t\t\tremoveParameters = sym is IMethodSymbol || (sym is IPropertySymbol ips && ips.IsIndexer);\n\t\t\t\toverload = removeParameters && sym.Name == _currentSym.Name && sym.ContainingType == _currentSym.ContainingType;\n\t\t\t\t//don't remove parameters if sym and _currentSym are overloads of the same method or indexer\n\t\t\t}\n\t\t\ts = s.Replace('<', '{').Replace('>', '}');\n\t\t\t\n\t\t\tif (removeParameters) {\n\t\t\t\t_rxParams ??= new(@\"\\bparams \");\n\t\t\t\ts = _rxParams.Replace(s);\n\t\t\t}\n\t\t}\n\t\tx.SetAttributeValue(\"cref\", s);\n\t\t\n\t\t//set link text if DocFX would do it incorrectly\n\t\tif (!overload && x.IsEmpty && x.Name.LocalName.Starts(\"see\")) {\n\t\t\t//print.it(s);\n\t\t\tint from = 0, to = s.Length, i; string append = null;\n\t\t\tforeach (var v in _auNamespaces) if (s.Starts(v)) { from = v.Length; break; }\n\t\t\tif (removeParameters) {\n\t\t\t\tswitch (kind) {\n\t\t\t\tcase 'M':\n\t\t\t\t\ti = s.IndexOf('(');\n\t\t\t\t\tif (i > 0) to = i;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'P':\n\t\t\t\t\ti = s.IndexOf('[');\n\t\t\t\t\tif (i > 0) { to = i; append = \"[]\"; }\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\ts = s[from..to];\n\t\t\ts = s.Replace('{', '<');\n\t\t\ts = s.Replace('}', '>');\n\t\t\ts += append;\n\t\t\t//print.it(s);\n\t\t\tx.Value = s;\n\t\t\tif (x.Name.LocalName == \"seealso\") { //DocFX ignores value. Let's insert it as <seealso href> for postprocessing.\n\t\t\t\t_b.AppendLine($\"\"\"<seealso href=\"https://text\">{Convert2.HexEncode(Encoding.UTF8.GetBytes(s))}</seealso>\"\"\");\n\t\t\t}\n\t\t}\n\t}\n\t\n\tSymbolDisplayFormat _formatCref = new SymbolDisplayFormat(\n\t\tglobalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted,\n\t\ttypeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,\n\t\tgenericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,\n\t\tmemberOptions: SymbolDisplayMemberOptions.IncludeContainingType | SymbolDisplayMemberOptions.IncludeParameters | SymbolDisplayMemberOptions.IncludeExplicitInterface,\n\t\tparameterOptions: SymbolDisplayParameterOptions.IncludeType | SymbolDisplayParameterOptions.IncludeParamsRefOut,\n\t\tdelegateStyle: SymbolDisplayDelegateStyle.NameAndSignature,\n\t\textensionMethodStyle: SymbolDisplayExtensionMethodStyle.Default,\n\t\tpropertyStyle: SymbolDisplayPropertyStyle.NameOnly,\n\t\tmiscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes | SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers | SymbolDisplayMiscellaneousOptions.ExpandValueTuple | SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier\n\t\t);\n\t\n\tvoid _SkipSource(int from, int to) {\n\t\t_bFile.Append(_code, _filePos, from - _filePos);\n\t\t_filePos = to;\n\t}\n\t\n\tvoid _SkipSource(TextSpan span) => _SkipSource(span.Start, span.End);\n\t\n\t/// <summary>prints warning if x is not at the start or end of a line</summary>\n\tvoid _AssertLine(XElement x, bool end) {\n\t\tvar n = end ? x.NextNode : x.PreviousNode;\n\t\tif (n == null) return;\n\t\tif (n is XText t && n is not XCData) {\n\t\t\tvar v = t.Value;\n\t\t\tif (end ? v.Starts(\"\\n\") : v.Ends(\"\\n\")) return;\n\t\t}\n\t\t//if(end) x.AddAfterSelf(\"\\n\"); else x.AddBeforeSelf(\"\\n\");\n\t\tvar tag = x.Name.LocalName;\n\t\t_Warning(end ? $\"</{tag}> must end a line.\" : $\"<{tag}> must start a line.\", x.Parent);\n\t}\n\t\n\tvoid _EnsureSeparatedWithEmptyLine(XElement x, bool end) {\n\t\tvar n = end ? x.NextNode : x.PreviousNode;\n\t\tif (n == null) return;\n\t\tif (n is XText t && n is not XCData) {\n\t\t\tvar v = t.Value;\n\t\t\tif (end ? v.Starts(\"\\n\\n\") : v.Ends(\"\\n\\n\")) return;\n\t\t\tif (v == \"\\n\") if ((end ? n.NextNode : n.PreviousNode) == null) return;\n\t\t\tif (end ? v.Starts(\"\\n\") : v.Ends(\"\\n\")) {\n\t\t\t\tt.Value = end ? \"\\n\" + v : v + \"\\n\";\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\t_AssertLine(x, end);\n\t}\n\t\n\tvoid _Warning(string warning, object text) {\n\t\tprint.warning($\"<_>{warning}</_>\\r\\n\\tMember: {LaGoto(_currentSym)} in {_semo.SyntaxTree.FilePath}\\r\\n\\tText: <_>{text}</_>\", 1);\n\t}\n\t\n\tpublic static string LaGoto(ISymbol sym) {\n\t\tvar loc = sym.Locations[0];\n\t\treturn $@\"<open C:\\code\\au\\Au{loc.SourceTree.FilePath}||{loc.SourceSpan.Start}>{sym}<>\";\n\t}\n\t\n\t//static readonly PortableExecutableReference[] s_refs = Au.Compiler.MetaReferences.DefaultReferences.Values.ToArray();\n\t//static readonly CSharpParseOptions s_parseOpt = new CSharpParseOptions(LanguageVersion.Preview, DocumentationMode.Parse);\n\t//static readonly CSharpCompilationOptions s_compOpt = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, allowUnsafe: true);\n}\n"
  },
  {
    "path": "Scripts/@Au docs/LA docs doc-html.db.cs",
    "content": "/// Creates doc-html.db from files created by script \"Au docs\". It will be used in LA Read panel.\n/// Executed by `Au docs.cs`.\n\n/*/ testInternal Au; nuget html\\HtmlAgilityPack; /*/\n\n//#define IEWB\n\nusing HtmlAgilityPack; //tested: AngleSharp slower\n\nif (script.testing) print.clear();\nbool dev = !true;\n\n//Debug_.MemorySetAnchor();\nif (!GC.TryStartNoGCRegion(2_000_000_000)) throw null; //makes faster > 2 times\n\nconst string c_rootDir = @\"C:\\Temp\\Au\\DocFX\\site\\\";\nstring[] subDirs = [\"api\", \"articles\", \"editor\", \"cookbook\"]; //note: don't add the root index.html (let users open it in their web browser)\n\nvar dbFile = folders.ThisAppBS + \"doc-html.db\";\nsqliteStatement dbInsert;\n\nfilesystem.delete(dbFile);\nusing (var db = new sqlite(dbFile)) {\n\tdb.Execute(\"CREATE TABLE doc (name TEXT PRIMARY KEY, text TEXT)\");\n\tusing var dbInsert_ = dbInsert = db.Statement(\"INSERT INTO doc VALUES (?, ?)\");\n\tusing var dbTrans = db.Transaction();\n\t//perf.first();\n\t_Convert();\n\t_AddOtherFiles();\n\t//perf.nw();\n\tdbTrans.Commit();\n\tdb.Execute(\"VACUUM\");\n}\nfilesystem.copyTo(dbFile, @\"C:\\code\\Au.Editor\", FIfExists.Delete);\n\nprint.scrollToTop();\n//Debug_.MemoryPrint();\n\nvoid _Convert() {\n\tList<_FileHtml> a = [];\n\tforeach (var subDir in subDirs) {\n\t\tforeach (var f in filesystem.enumFiles(c_rootDir + subDir, \"*.html\", FEFlags.AllDescendants | FEFlags.NeedRelativePaths)) {\n\t\t\tvar name = f.Name[1..];\n\t\t\tif (name is \"toc.html\") continue;\n\t\t\ta.Add(new() { name = $\"{subDir}/{name.Replace('\\\\', '/')}\", html = f.FullPath });\n\t\t}\n\t}\n\t//perf.next();\n\tif (dev) {\n\t\tforeach (var f in a) {\n\t\t\tf.html = _ProcessHtml(f.html, f.name);\n\t\t}\n\t} else {\n\t\tParallel.ForEach(a, f => { //with no-GC region 3 times faster, else 20% faster\n\t\t\tf.html = _ProcessHtml(f.html, f.name);\n\t\t});\n\t}\n\t//perf.next();\n\tforeach (var f in a) {\n\t\t_AddRow(f.name, f.html);\n\t}\n}\n\nvoid _AddRow(string name, object data) {\n\tdbInsert.BindAll(name, data).Step();\n\tdbInsert.Reset();\n}\n\nvoid _AddOtherFiles() {\n\tforeach (var f in filesystem.enumFiles(c_rootDir + \"styles\")) {\n\t\tvar path = f.FullPath;\n\t\tobject data = pathname.getExtension(f.Name) switch { \".css\" => filesystem.loadText(path), \".eot\" => filesystem.loadBytes(path), _ => null };\n\t\t\n#if IEWB\n\t\tif (f.Name == \"docfx.vendor.min.css\") { //fix the hidden scrollbar issue\n\t\t\tvar s1 = (string)data;\n\t\t\tif (0 == s1.RxReplace(\"\"\"@-ms-viewport\\s*\\{\\s*width:\\s*device-width\\s*}\"\"\", \"\", out s1, 1)) throw null;\n\t\t\tdata = s1;\n\t\t}\n#endif\n\t\t\n\t\tif (data != null) _AddRow($\"styles/{f.Name}\", data);\n\t\t//if (data == null) print.it(f.Name);\n\t}\n\t\n\t_AddRow(\"styles/la.css\", \"\"\"\nh1 { background-color: #9ACD32 !important; margin: 0 -2px 8px -2px !important; padding: 1px 2px 3px 2px !important; }\nh1, h2, h3 { font-size: 110% !important; }\nh4, h5 { font-size: 100% !important; }\ndiv.col-md-12,div.container-fluid { padding-left: 2px !important; padding-right: 2px !important; }\ntd, th { white-space: normal !important; word-wrap: break-word; }\npre { word-wrap: normal !important; }\n\"\"\");\n\t\n#if IEWB\n\t_AddRow(\"styles/la.js\", \"\"\"\nfunction ancestorOrThis(el, tag) {\n    while (el && el.nodeName !== tag) el = el.parentElement;\n    return el;\n}\n\ndocument.addEventListener('contextmenu', function(e) {\n\te.preventDefault(); \n\tvar contextFlag = 0; // 0: no selection, 1: selection, 2: inside <pre> (and no selection)\n\n\tvar selectedText = window.getSelection().toString();\n\t\n\tif (selectedText.length > 0) {\n\t\tcontextFlag = 1;\n\t} else {\n\t\tvar preElement = ancestorOrThis(e.target, 'PRE');\n\t\tif (preElement && preElement.parentElement.className !== 'codewrapper') {\n\t\t\tcontextFlag = 2;\n\t\t\tselectedText = preElement.textContent;\n\t\t}\n\t}\n\n\twindow.external.ShowContextMenu(e.clientX, e.clientY, contextFlag, selectedText);\n\treturn false;\n});\n\ndocument.addEventListener('click', function(e) {\n\tvar targetElement = e.target;\n\n\tvar aElement = ancestorOrThis(e.target, 'A');\n\tif (aElement && aElement.classList.contains('nuget')) {\n\t\te.preventDefault();\n\t\twindow.external.NugetLinkClicked(targetElement.textContent);\n\t}\n});\n\"\"\");\n#endif\n\t\n\tforeach (var f in filesystem.enumFiles(c_rootDir + \"images\", \"*.png\")) {\n\t\t_AddRow($\"images/{f.Name}\", filesystem.loadBytes(f.FullPath));\n\t}\n}\n\nstring _ProcessHtml(string path, string uri) {\n\tif (dev) print.clear();\n\t\n\tvar doc = new HtmlDocument();\n\tdoc.Load(path, Encoding.Default);\n\tvar h = doc.DocumentNode.Element(\"html\");\n\tvar head = h.Element(\"head\");\n\tvar body = h.Element(\"body\");\n\tvar relPath = head.Element(\"link\").GetAttributeValue(\"href\", \"\"); relPath = relPath[..relPath.FindNot(\"./\")];\n\t\n\tList<HtmlNode> are = [];\n\tList<HtmlAttribute> ara = [];\n\tvoid _Remove(HtmlNode n) { if (n != null) are.Add(n); }\n\tvoid _RemoveAttr(HtmlAttribute n) { if (n != null) ara.Add(n); }\n\t\n\tforeach (var v in head.Elements(\"meta\")) {\n\t\tif (v.ChildAttributes(\"property\").Any()) _Remove(v);\n\t}\n\t\n\t_Remove(head.SelectSingleNode(\"link[@rel='shortcut icon']\"));\n\t_Remove(head.SelectSingleNode(\"meta[@name='title']\"));\n\t\n\thead.AppendChild(HtmlNode.CreateNode($\"\"\"<link rel=\"stylesheet\" href=\"{relPath}styles/la.css\">\"\"\"));\n\thead.AppendChild(doc.CreateTextNode(\"\\n\"));\n\t\n\t_Remove(body.SelectSingleNode(\".//header\"));\n\t_Remove(body.SelectSingleNode(\".//div[@class='sidenav hide-when-search']\"));\n\tif (body.SelectSingleNode(\".//div[@class='article row grid-right']\") is { } div2) div2.RemoveClass();\n\t\n\tif (h.SelectNodes(\"//script\") is { } scripts)\n\t\tforeach (var v in scripts) _Remove(v);\n\t\n#if IEWB\n\tbody.AppendChild(HtmlNode.CreateNode($\"\"\"<script type=\"text/javascript\" src=\"{relPath}styles/la.js\"></script>\"\"\"));\n#endif\n\tbody.AppendChild(doc.CreateTextNode(\"\\n\"));\n\t\n\tforeach (var vn in body.Descendants(\"div\")) {\n\t\t//if (!vn.HasChildNodes) print.it(vn.OuterHtml);\n\t\tif (!vn.HasChildNodes) _Remove(vn);\n\t}\n\t\n\tforeach (var v in are) v.Remove();\n\t\n\tif (uri.Starts(@\"api\")) {\n\t\tforeach (var vn in body.Descendants())\n\t\t\tif (vn.NodeType is HtmlNodeType.Element) {\n\t\t\t\tforeach (var va in vn.GetAttributes()) {\n\t\t\t\t\tswitch (va.Name) {\n\t\t\t\t\tcase \"data-uid\":\n\t\t\t\t\tcase \"id\" when vn.Name is \"h1\" or \"h3\" or \"h4\" or \"h5\" or \"h6\" or \"a\" or \"td\": _RemoveAttr(va); continue;\n\t\t\t\t\tcase \"id\" when vn.Name is \"h2\": continue; //overload\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\t//if (va.Name is not (\"class\" or \"href\" or \"style\") && va.Value.Length > 20) print.it(vn.Name, va.Name, va.Value);\n\t\t\t\t}\n\t\t\t}\n\t}\n\t\n\tforeach (var va in body.GetDataAttributes()) _RemoveAttr(va);\n\t\n\tforeach (var v in ara) v.Remove();\n\t\n#if IEWB\n\t//IE displays too wide tabs in pre code, and does not support CSS tab-size. Workaround: replace tabs with spaces.\n\tif (body.SelectNodes(\".//pre[not(parent::div[@class='codewrapper'])]//code\") is { } preCodes) {\n\t\tforeach (var v in preCodes) {\n\t\t\tvar s = v.InnerHtml;\n\t\t\tif (s.Contains('\\t')) {\n\t\t\t\ts = s.RxReplace(@\"(?m)^\\t+\", m => new string(' ', m.Length * 4));\n\t\t\t\tv.InnerHtml = s;\n\t\t\t}\n\t\t}\n\t}\n#endif\n\t\n\t//links that LA handles\n\tif (uri.Starts(\"cookbook\") && body.SelectNodes(\".//span[@title='Paste the underlined text in menu > Tools > NuGet']\") is { } nugetLinks) {\n\t\tforeach (var v in nugetLinks) {\n\t\t\tv.Attributes.RemoveAll();\n\t\t\tv.Name = \"a\";\n#if IEWB\n\t\t\tv.AddClass(\"nuget\");\n#else\n\t\t\tv.SetAttributeValue(\"href\", \"nuget:\" + v.InnerText);\n#endif\n\t\t}\n\t}\n\t\n\t//remove the LA window image\n\tif (uri == \"editor/Application.html\") {\n\t\tbody.SelectSingleNode(\".//p/img\").Remove();\n\t}\n\t\n\tif (dev) {\n\t\tprint.it(head.OuterHtml);\n\t\tprint.it(body.OuterHtml.RxReplace(@\"\\R(?:\\R\\h*)+\\R\", \"\\n\\n\"));\n\t\tprint.scrollToTop();\n\t\tif (!dialog.showYesNo(\"Continue\", path[c_rootDir.Length..])) Environment.Exit(0);\n\t}\n\t\n\treturn doc.DocumentNode.OuterHtml;\n}\n\nclass _FileHtml {\n\tpublic string name, html;\n}\n"
  },
  {
    "path": "Scripts/@Au docs/LA docs toc.json.cs",
    "content": "/// Creates toc.json from files created by script \"Au docs\". It will be used in LA Help panel.\n/// Executed by `Au docs.cs`.\n\nusing System.Text.Json.Nodes;\nusing System.Xml.Linq;\n\nif (script.testing) print.clear();\n\n//HashSet<string> hsDbNames = [];\n//using (var db = new sqlite(folders.ThisAppBS + \"doc-ai.db\", SLFlags.SQLITE_OPEN_READONLY)) {\n//\tusing var sta = db.Statement(\"SELECT name FROM doc\");\n//\twhile (sta.Step()) {\n//\t\thsDbNames.Add(sta.GetText(0));\n//\t}\n//}\n\nvar root = new JsonArray();\n_Cookbook();\n_Api(@\"C:\\Temp\\Au\\DocFX\\site\\api\\toc.json\");\n_Other(@\"C:\\Temp\\Au\\DocFX\\site\\articles\", \"Articles\");\n_Other(@\"C:\\Temp\\Au\\DocFX\\site\\editor\", \"Editor\");\nvar json = root.ToJsonString(new() { WriteIndented = true, Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping });\nfilesystem.saveText(folders.ThisAppBS + \"toc.json\", json);\nfilesystem.saveText(@\"C:\\code\\Au.Editor\\toc.json\", json);\n\nvoid _Cookbook() {\n\tvar xr = XmlUtil.LoadElem(folders.ThisAppBS + @\"..\\Cookbook\\files.xml\");\n\t\n\tvar jdk = new JsonObject();\n\tjdk.Add(\"name\", \"Cookbook\");\n\troot.Add(jdk);\n\t_AddItems(xr, jdk);\n\t\n\tstatic void _AddItems(XElement xp, JsonObject jp) {\n\t\tvar ja = new JsonArray();\n\t\tjp.Add(\"items\", ja);\n\t\tforeach (var x in xp.Elements()) {\n\t\t\tstring type = x.Name.LocalName, name = x.Attr(\"n\");\n\t\t\tif (name[0] == '-' || type == \"o\") continue;\n\t\t\tvar j = new JsonObject();\n\t\t\tja.Add(j);\n\t\t\tif (type == \"d\") {\n\t\t\t\tj[\"name\"] = name;\n\t\t\t\t_AddItems(x, j);\n\t\t\t} else {\n\t\t\t\tj[\"name\"] = name[..^3];\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid _Api(string sourcePath) {\n\tvar json = filesystem.loadText(sourcePath);\n\tvar j1 = JsonNode.Parse(json) as JsonObject;\n#if !true\n\tforeach (JsonObject ns in j1[\"items\"].AsArray()) {\n\t\tvar j2 = new JsonObject();\n\t\troot.Add(j2);\n\t\t_Tidy(ns, j2, 1);\n\t}\n#else\n\tvar j2 = new JsonObject();\n\tj2[\"name\"] = \"Library API\";\n\troot.Add(j2);\n\t_Tidy(j1, j2, 0);\n#endif\n\t\n\tstatic void _Tidy(JsonObject j1, JsonObject j2, int level) {\n\t\tstring name = null, href = null, kind = null;\n\t\tforeach (var (k, v) in j1) {\n\t\t\tswitch (k) {\n\t\t\tcase \"name\":\n\t\t\t\tname = (string)v;\n\t\t\t\tbreak;\n\t\t\tcase \"href\":\n\t\t\t\tif (level == 1) {\n\t\t\t\t\tkind = \"Namespace\";\n\t\t\t\t} else if (level is 2 or 3) {\n\t\t\t\t\thref = (string)v;\n\t\t\t\t\tvar file = @\"C:\\Temp\\Au\\DocFX\\site\\api\\\" + href;\n\t\t\t\t\tif (!filesystem.exists(file).File) throw new AuException(href);\n\t\t\t\t\tvar html = filesystem.loadText(file);\n\t\t\t\t\t\n\t\t\t\t\t//kind\n\t\t\t\t\tif (!html.RxMatch(@\"<h1 .+?>(\\w+)\", 1, out kind)) throw null;\n\t\t\t\t\t\n\t\t\t\t\t//rename operators to match DB\n\t\t\t\t\tif (kind == \"Operator\") {\n\t\t\t\t\t\tif (name.Starts(\"operator \")) name = name[9..]; else if (name.Ends(\" operator\")) name = name[..^9];\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\t//check whether the full name == the DB name\n\t\t\t\t\t//if (kind != \"Field\") {\n\t\t\t\t\t//\tif (level == 2) {\n\t\t\t\t\t//\t\ts = $\"{(string)j.Parent.Parent[\"name\"]}.{s}\";\n\t\t\t\t\t//\t\t//print.it(s);\n\t\t\t\t\t//\t} else {\n\t\t\t\t\t//\t\tvar jType = j.Parent.Parent.Parent.Parent;\n\t\t\t\t\t//\t\ts = $\"{(string)jType.Parent.Parent[\"name\"]}.{(string)jType[\"name\"]}.{s}\";\n\t\t\t\t\t//\t}\n\t\t\t\t\t//\tif (!hsDbNames.Remove(s)) print.it(s);\n\t\t\t\t\t//}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \"items\" when v is JsonArray { Count: > 0 } ja1:\n\t\t\t\tvar ja2 = new JsonArray();\n\t\t\t\tif (level == 2) { //remove the member kind level\n\t\t\t\t\tforeach (var jk in ja1) {\n\t\t\t\t\t\t_Items(jk[\"items\"].AsArray(), ja2);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t_Items(ja1, ja2);\n\t\t\t\t}\n\t\t\t\tj2[\"items\"] = ja2;\n\t\t\t\t\n\t\t\t\tvoid _Items(JsonArray ja1, JsonArray ja2) {\n\t\t\t\t\tforeach (JsonObject jj1 in ja1) {\n\t\t\t\t\t\tvar jj2 = new JsonObject();\n\t\t\t\t\t\tja2.Add(jj2);\n\t\t\t\t\t\t_Tidy(jj1, jj2, level + 1);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (kind != null) j2.Insert(0, \"kind\", kind);\n\t\tif (href != null) j2.Insert(0, \"href\", href);\n\t\tif (name != null) j2.Insert(0, \"name\", name);\n\t}\n}\n\nvoid _Other(string dir, string docKind) {\n\tvar jdk = new JsonObject();\n\tjdk.Add(\"name\", docKind);\n\troot.Add(jdk);\n\t_AddItems(dir, jdk);\n\t\n\tstatic void _AddItems(string dir, JsonObject jp, string hrefPath = null) {\n\t\tvar ja = new JsonArray();\n\t\tjp.Add(\"items\", ja);\n\t\tforeach (var f in filesystem.enumerate(dir)) {\n\t\t\tstring name = f.Name;\n\t\t\tif (!f.IsDirectory) {\n\t\t\t\tif (!name.Ends(\".html\")) continue;\n\t\t\t\tname = f.Name[..^5];\n\t\t\t\tif (name is \"toc\" or \"index\") continue;\n\t\t\t}\n\t\t\tvar j = new JsonObject();\n\t\t\tj.Add(\"name\", name);\n\t\t\tja.Add(j);\n\t\t\tvar href = Uri.EscapeDataString(f.Name);\n\t\t\tif (f.IsDirectory) {\n\t\t\t\t_AddItems(f.FullPath, j, hrefPath + href + \"/\");\n\t\t\t} else {\n\t\t\t\tj.Add(\"href\", hrefPath + href);\n\t\t\t}\n\t\t}\n\t}\n\t\n}\n"
  },
  {
    "path": "Scripts/@Au docs/LA menu doc.cs",
    "content": "/// Generates article \"Menu commands\" from current LA UI (Panels.Menu).\n/// Executed by `Au docs.cs`.\n\n/*/ role editorExtension; testInternal Au.Editor; r Au.Editor.dll; r Au.Controls.dll; /*/\n\nusing LA;\nusing System.Windows.Controls;\n\n//print.clear();\n_BuidlDynamicMenus();\nstring title = \"Menu commands\";\nStringBuilder b = new($\"\"\"\n---\nuid: menu_commands\n---\n\n<!-- auto-generated -->\n\n# {title}\n\n\n\"\"\");\nforeach (MenuItem v in Panels.Menu.Items) {\n\tif (v.Role is not MenuItemRole.TopLevelHeader) break;\n\t_MenuItem(v, 0);\n}\n//print.it(b);\n//print.scrollToTop();\n_Save();\n\nvoid _MenuItem(MenuItem mi, int level) {\n\tvar text = mi.Header.ToString().Replace(\"_\", \"\");\n\tb.Append(' ', level * 2).Append(\"- \").Append(_Escape(text));\n\tif (mi.IsCheckable) b.Append(\"  (option)\");\n\tif (mi.ToolTip?.ToString() is string tt) {\n\t\tvar lines = tt.Lines();\n\t\tfor (int i = 0; i < lines.Length; i++) {\n\t\t\tvar line = lines[i];\n\t\t\tif (i == 0 && line.Starts(text)) continue;\n\t\t\tline = _Escape(line);\n\t\t\tb.AppendLine(\"  \").Append(' ', (level + 1) * 2).Append(line);\n\t\t}\n\t}\n\tb.AppendLine();\n\tif (mi.Role is MenuItemRole.SubmenuHeader or MenuItemRole.TopLevelHeader) {\n\t\tforeach (var v in mi.Items.OfType<MenuItem>()) {\n\t\t\t_MenuItem(v, level + 1);\n\t\t}\n\t}\n}\n\nstatic string _Escape(string s) {\n\ts = s.Replace(\"<\", @\"\\<\");\n\tif (s.Contains('\"')) {\n\t\ts = s.RxReplace(@\"\"\"(image:|\\*icon)\"\"\", \"`$0`\");\n\t\t//print.it(s);\n\t}\n\treturn s;\n}\n\nvoid _BuidlDynamicMenus() {\n\tvar mi = App.Commands[\"New\"].MenuItem;\n\tFilesModel.FillMenuNew(mi);\n}\n\nvoid _Save() {\n\tvar dir = @\"C:\\code\\au\\Other\\DocFX\\_doc\\editor\\\";\n\tvar menusMd = dir + title + \".md\";\n\tfilesystem.saveText(menusMd, b.ToString());\n\t//var tocYml = dir + \"toc.yml\"; //added manually\n}\n"
  },
  {
    "path": "Scripts/@Au docs/Readme.txt",
    "content": "Us the latest DocFX. Older version may not support new C# features.\nIf there are some strange errors, likely DocFX uses wrong SDK version. Try to temporarily rename folders of SDKs that should not be used.\n\ninfo: we don't use the full text search feature.\n\tUses CPU 10-20 s after each page [re]load.\n\tResults are poorly sorted.\n\tThe Google site search does it much better, and often faster.\n\tinfo: To enable it, add \"_enableSearch\": true in \"globalMetadata\".\n"
  },
  {
    "path": "Scripts/@WinAPI converter/WinAPI converter.cs",
    "content": "/// For Windows API declarations LA uses data from:\n/// \tSource: https://github.com/microsoft/win32metadata\n/// \tData: https://www.nuget.org/packages/Microsoft.Windows.SDK.Win32Metadata/\n/// LA cannot use the .winmd file directly. This script converte it to winapi.db file that will be used by LA.\n\n//#define SMALL\n\nscript.setup(debug: true);\nprint.clear();\n\nHashSet<string> onlyNamespaces = null;\n#if SMALL //for testing, convert only some apis, to make smaller and faster\nonlyNamespaces = [\n\t\"Foundation\",\n\t\"Graphics.Dwm\",\n\t\"Graphics.Gdi\",\n\t\"Security\",\n\t\"System.Com\",\n\t\"System.Com.StructuredStorage\",\n\t\"System.Console\",\n\t\"System.Environment\",\n\t\"System.IO\",\n\t\"System.Kernel\",\n\t\"System.LibraryLoader\",\n\t\"System.Memory\",\n\t\"System.Ole\",\n\t\"System.ProcessStatus\",\n\t\"System.Registry\",\n\t\"System.Services\",\n\t\"System.SystemInformation\",\n\t\"System.SystemServices\",\n\t\"System.TaskScheduler\",\n\t\"System.Threading\",\n\t\"System.Variant\",\n\t\"System.WinRT\",\n\t\"Storage.FileSystem\",\n\t\"UI.Accessibility\",\n\t\"UI.Controls\",\n\t\"UI.Controls.Dialogs\",\n\t\"UI.HiDpi\",\n\t\"UI.Input\",\n\t\"UI.Input.KeyboardAndMouse\",\n\t\"UI.Shell\",\n\t\"UI.Shell.Common\",\n\t\"UI.Shell.PropertiesSystem\",\n\t\"UI.WindowsAndMessaging\",\n\t];\n#endif\n\nvar c = new WinApiConverter();\n//perf.first();\nc.Convert(onlyNamespaces);\n//perf.nw();\n_SavePreviewText();\n_SaveDatabase();\nprint.scrollToTop();\n\nvoid _SavePreviewText() {\n\tregexp rx1 = new(@\"(?m)^\");\n\t\n\tusing var w = File.CreateText(folders.Workspace + @\"files\\winapi converter results.cs\");\n\tw.WriteLine(\"/// Created by \\\"WinAPI converter\\\" script. For preview/debug only. Delete when no longer needed.\\r\\n\\r\\n/*/ noWarnings CS0649,CS8500,CS0618; /*/\\r\\nunsafe class api : NativeApi {\\r\\n//.\");\n\t\n\tbool prevMultiline = false;\n\tforeach (var (k, v) in c.Result) {\n\t\tbool multiline = v.Contains('\\n');\n\t\tif (multiline != prevMultiline) w.WriteLine(multiline ? \"//..\" : \"//.\");\n\t\tif (multiline || prevMultiline) w.WriteLine();\n\t\tprevMultiline = multiline;\n\t\t\n\t\tvar s = rx1.Replace(v, \"\\t\");\n\t\tw.WriteLine(s);\n\t}\n\t\n\tw.WriteLine(\"}\");\n\t\n\t//print.it(\"SAVED\");\n}\n\nvoid _SaveDatabase() {\n\tregexp rx = new(@\"(?m)^internal \\K(struct|enum|interface|class|static extern|delegate|const|static|record)\\b\");\n\tvar a = c.Result.Select(o => new _NCK(o.Key, o.Value, _Kind(o.Value))).ToList();\n#if !SMALL\n\t_AddOld(a);\n#endif\n\t\n\tstring dbFile = folders.ThisAppBS + @\"winapi.db\";\n\tfilesystem.delete(dbFile);\n\t\n\tusing var d = new sqlite(dbFile);\n\tusing var trans = d.Transaction();\n\td.Execute(\"CREATE TABLE api (name TEXT, code TEXT, kind INTEGER)\"); //note: no PRIMARY KEY. Don't need index.\n\tusing var stat = d.Statement(\"INSERT INTO api VALUES (?, ?, ?)\");\n\t\n\tforeach (var (name, code, kind) in a.OrderBy(o => o.kind).ThenBy(o => o.name, StringComparer.OrdinalIgnoreCase)) {\n\t\t//print.it(kind, name);\n\t\tstat.Bind(1, name);\n\t\tstat.Bind(2, code);\n\t\tstat.Bind(3, (int)kind);\n\t\tstat.Step();\n\t\tstat.Reset();\n\t}\n\t\n\ttrans.Commit();\n\td.Execute(\"VACUUM\");\n\t\n\tprint.it(\"DONE\");\n\t\n\tCiItemKind _Kind(string code) {\n\t\tif (!rx.Match(code, 0, out string sk)) throw null;\n\t\treturn sk switch {\n\t\t\t\"struct\" or \"record\" => CiItemKind.Structure,\n\t\t\t\"enum\" => CiItemKind.Enum,\n\t\t\t\"interface\" => CiItemKind.Interface,\n\t\t\t\"class\" => CiItemKind.Class,\n\t\t\t\"static extern\" => CiItemKind.Method,\n\t\t\t\"delegate\" => CiItemKind.Delegate,\n\t\t\t\"const\" => CiItemKind.Constant,\n\t\t\t\"static\" => CiItemKind.Field,\n\t\t\t_ => throw null\n\t\t};\n\t}\n}\n\n//From the old database adds APIs that are missing in winmd. Also replaces come incorrect declarations.\nvoid _AddOld(List<_NCK> a) {\n\tvar dNew = a.ToDictionary(o => (name: o.name, kind: o.kind), o => o.code);\n\tvar aOld = _Load(folders.Editor + @\"..\\Other\\Api\\winapi.db\", true);\n\t\n\tforeach (var nck in aOld) {\n\t\tvar (name, code, kind) = nck;\n\t\tif (_GetCode(name, out var s)) {\n\t\t\tif (kind == CiItemKind.Constant && code != s) {\n\t\t\t\tvar st = s.AsSpan(15..18);\n\t\t\t\tif (st is \"int\" or \"uin\" or \"sho\" or \"ush\" or \"byt\" or \"sby\") {\n\t\t\t\t\tint vOld = _ConstIntValue(code), vNew = _ConstIntValue(s);\n\t\t\t\t\tif (vOld != vNew) {\n\t\t\t\t\t\t//print.it($\"<><bc greenyellow>{name}<>\\r\\n{code}\\r\\n{s}\");\n\t\t\t\t\t\tif (name is \"VARIANT_TRUE\" || code.Contains(\"= null;\")) continue; //else error in winmd; mostly because it uses wrong preprocessor symbols, eg for oldest Windows versions or 32-bit.\n\t\t\t\t\t\tif (vOld < 0 && s.Starts(\"internal const int \") && code.Starts(\"internal const uint \")) code = code.RxReplace(@\"\\buint (\\w+) = \\w+;$\", $\"int $1 = unchecked((int)0x{vOld:X});\");\n\t\t\t\t\t\tint i = a.FindIndex(o => o.name == name);\n\t\t\t\t\t\ta[i] = a[i] with { code = code };\n\t\t\t\t\t}\n\t\t\t\t} else if (st is \"lon\" or \"ulo\") { //`uint` in old\n\t\t\t\t\t//print.it($\"<><bc greenyellow>{name}<>\\r\\n{code}\\r\\n{s}\");\n\t\t\t\t} else if (st is \"dou\" or \"flo\") {\n\t\t\t\t\t//print.it($\"<><bc greenyellow>{name}<>\\r\\n{code}\\r\\n{s}\");\n\t\t\t\t} else if (st is \"str\") {\n\t\t\t\t\t//print.it($\"<><bc greenyellow>{name}<>\\r\\n{code}\\r\\n{s}\");\n\t\t\t\t} else {\n\t\t\t\t\tprint.it($\"<><bc greenyellow>{name}<>\\r\\n{code}\\r\\n{s}\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tstatic int _ConstIntValue(string s) {\n\t\t\t\tint i = s.IndexOf('=') + 1;\n\t\t\t\tif (s[i] == ' ') i++;\n\t\t\t\tif (s[i] is '\"' or '\\'') return 0;\n\t\t\t\tif (s.Eq(i, \"unchecked((int)\")) i += 15;\n\t\t\t\t//if (!s.ToInt(out long r, i)) print.it(s[i..]);\n\t\t\t\ts.ToInt(out int r, i);\n\t\t\t\treturn r;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif (_AltName()) continue;\n\t\ta.Add(nck);\n\t\t\n\t\tbool _Cont(string s) => dNew.ContainsKey((s, kind));\n\t\tbool _ContK(string s, CiItemKind kind) => dNew.ContainsKey((s, kind));\n\t\tbool _GetCode(string s, out string code) => dNew.TryGetValue((s, kind), out code);\n\t\t//bool _GetCodeK(string s, CiItemKind kind, out string code) => dNew.TryGetValue((s, kind), out code);\n\t\t\n\t\tbool _AltName() {\n\t\t\tif (name.Ends(\"__32\")) {\n\t\t\t\tif (_Cont(name.Insert(^4, \"W\"))) return true;\n\t\t\t\tif (kind is CiItemKind.Constant) {\n\t\t\t\t\tif (_GetCode(name[..^4], out var s1)) {\n\t\t\t\t\t\tif (s1 == code.RxReplace(@\"__32\\b\", \"\", 1)) return true;\n\t\t\t\t\t\t//print.it($\"<>{name}, old32='<c gray>{code}<>', new='<c green>{s1}<>'\");\n\t\t\t\t\t} //else print.it(name);\n\t\t\t\t} else {\n\t\t\t\t\tif (kind is CiItemKind.Delegate && name is \"FARPROC__32\" or \"NEARPROC__32\" or \"PROC__32\") return true;\n\t\t\t\t\t//print.it(kind, name);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif ((int)kind <= 5) { //types, methods\n\t\t\t\t\tif (_Cont(name + \"W\") || _Cont(name + \"_W\")) return true;\n\t\t\t\t\telse if (_A()) return true;\n\t\t\t\t\telse if (kind == CiItemKind.Method && _W()) return true;\n\t\t\t\t\telse if (kind == CiItemKind.Structure) {\n\t\t\t\t\t\tif (name is \"GUID\" or \"FILE\" or \"POINT\" or \"POINTL\" or \"RECT\" or \"RECTL\" or \"SIZE\") return true;\n\t\t\t\t\t} else if (kind is CiItemKind.Delegate) {\n\t\t\t\t\t\tif (name is \"FARPROC\" or \"NEARPROC\" or \"PROC\") return true;\n\t\t\t\t\t\tif (_Cont(\"P\" + name) || _Cont(\"LP\" + name)) return true;\n\t\t\t\t\t}\n\t\t\t\t\t//if (kind == CiItemKind.Structure) print.it(name);\n\t\t\t\t\t//if (kind == CiItemKind.Delegate) print.it(name);\n\t\t\t\t\t//if (kind is CiItemKind.Class or CiItemKind.Interface) print.it(name);\n\t\t\t\t\t//if (kind == CiItemKind.Method) print.it(name);\n\t\t\t\t} else if (kind == CiItemKind.Field) {\n\t\t\t\t\tif (_TypeGuid(\"CLSID_\", CiItemKind.Class)) return true;\n\t\t\t\t\tif (_TypeGuid(\"IID_\", CiItemKind.Interface)) return true;\n\t\t\t\t\tif (_TypeGuid(\"DIID_\", CiItemKind.Interface)) return true;\n\t\t\t\t\t//print.it(name);\n\t\t\t\t\t\n\t\t\t\t\tbool _TypeGuid(string prefix, CiItemKind kind) => name.Starts(prefix) && name[prefix.Length..] is var s && (_ContK(s, kind) || _ContK(s + \"W\", kind));\n\t\t\t\t} else { //constant\n\t\t\t\t\tif (_A()) return true;\n\t\t\t\t\tif (_Cont(name + \"W\")) return true;\n\t\t\t\t\t//print.it(name, code);\n\t\t\t\t\t\n\t\t\t\t\t//In winmd missing all #define where the value is not a simple number/string/constant. Why they didn't calculate eg `(A | B)` or `(TEXT(\"text\"))`?\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tbool _A() => name.Ends('A') && (_Cont(name.ReplaceAt(^1.., \"W\")) || _Cont(name[..^1]));\n\t\t\t\tbool _W() => name.Ends('W') && _Cont(name[..^1]);\n\t\t\t}\n\t\t\t\n\t\t\treturn false;\n\t\t}\n\t}\n\t\n\tstatic _NCK[] _Load(string file, bool old) {\n\t\tvar a = new List<_NCK>();\n\t\tusing var db = new sqlite(file, SLFlags.SQLITE_OPEN_READONLY);\n\t\tusing var stat = db.Statement(\"SELECT name, code, kind FROM api\");\n\t\twhile (stat.Step()) {\n\t\t\tstring name = stat.GetText(0);\n\t\t\tstring code = stat.GetText(1);\n\t\t\tvar kind = (CiItemKind)stat.GetInt(2);\n\t\t\ta.Add(new(name, code, kind));\n\t\t}\n\t\treturn a.OrderBy(o => o.kind).ThenBy(o => o.name, StringComparer.OrdinalIgnoreCase).ToArray();\n\t}\n}\n\n//Copied from LA project.\nenum CiItemKind : sbyte {\n\t//types\n\tClass, Structure, Interface, Enum, Delegate,\n\t//functions, events\n\tMethod, ExtensionMethod, Property, Operator, Event,\n\t//data\n\tField, LocalVariable, Constant, EnumMember,\n\t//other\n\tNamespace, Keyword, Label, Snippet, TypeParameter,\n\t//not in autocomplete. Not in CiUtil.ItemKindNames.\n\tLocalMethod, Region,\n\tNone\n}\n\nrecord _NCK(string name, string code, CiItemKind kind);\n"
  },
  {
    "path": "Scripts/@WinAPI converter/WinApiConverter+.cs",
    "content": "using dnlib.DotNet;\n\npartial class WinApiConverter {\n\tvoid _AddToDict(string name) {\n\t\t_nFiltered++;\n\t\tvar v = b.ToString();\n\t\tif (!Result.TryAdd(name, v)) {\n\t\t\tResult[name] = Result[name] + \"\\r\\n//or\\r\\n\" + v;\n\t\t\t//print.it(\"<><lc green><>\"); print.it(Result[name]);\n\t\t}\n\t\t\n\t\t#region debug\n\t\t\n\t\t//if (v.Contains(\" struct \")) {\n\t\t//\t//if (v.Contains(\"_bitfield\")) print.it(v);\n\t\t//} else {\n\t\t//\t//if (v.Contains(\" enum \")) print.it(v);\n\t\t\n\t\t//}\n\t\t\n\t\t#endregion\n\t}\n\t\n\tvoid _AddSomeTypes() {\n\t\t_typedefs.Add(\"Guid\", \"Guid\"); //defined not in .winmd\n\t\tResult[\"PROPERTYKEY\"] = \"\"\"internal record struct PROPERTYKEY(Guid fmtid, uint pid);\"\"\"; //need ctor\n\t\tResult[\"DEVPROPKEY\"] = \"\"\"internal record struct DEVPROPKEY(Guid fmtid, uint pid);\"\"\"; //need ctor\n\t\tResult[\"SID_IDENTIFIER_AUTHORITY\"] = \"\"\"internal record struct SID_IDENTIFIER_AUTHORITY(byte v0, byte v1, byte v2, byte v3, byte v4, byte v5);\"\"\"; //need ctor\n\t}\n\t\n\t(string type, bool isManaged, ArraySig arraySig) _GetTypeName_Field(FieldDef fd, in _FieldAttributes attr, _TypeDefEtc tdeTopLevel) {\n\t\tstring fieldTypeWithoutBrackets = fd.FieldType.TypeName;\n\t\tbool isArray = fieldTypeWithoutBrackets.Ends(\"[*]\"); if (isArray) fieldTypeWithoutBrackets = fieldTypeWithoutBrackets[..^3];\n\t\tDebug.Assert(!(isArray && attr.isConst));\n\t\tbool isNestedType = fd.DeclaringType.HasNestedTypes && fd.DeclaringType.NestedTypes.Any(o => o.Name == fieldTypeWithoutBrackets);\n\t\t\n\t\tvar (s, marshal, sNoMarshal) = _GetTypeName(fd.FieldType, out int ptr, false, (attr.isConst, true), tdeTopLevel.attr.bit32, fd, fieldTypeWithoutBrackets, tdeTopLevel);\n\t\t\n\t\tif (marshal != null) {\n\t\t\t//if (ptr > 0 || attr.isFlexibleArray || _InUnion()) (marshal, s) = (null, sNoMarshal); //no, all nested non-union structs (few, rare) are or should be blittable\n\t\t\tif (ptr > 0 || attr.isFlexibleArray || fd.DeclaringType.IsExplicitLayout || fd.DeclaringType.IsNested) (marshal, s) = (null, sNoMarshal);\n\t\t\t\n\t\t\tif (!marshal.NE()) b.AppendFormat(\"[MarshalAs(UnmanagedType.{0})] \", marshal);\n\t\t}\n\t\t\n\t\tif (ptr > 0) s = s.PadRight(s.Length + ptr, '*');\n\t\t\n\t\treturn (s, marshal != null, isArray ? (ArraySig)fd.FieldSig.Type : null);\n\t\t\n\t\tvoid _PI(params object[] more) {\n\t\t\tprint.it(fd.DebugToString(), fd.FieldType.TypeName, s, marshal);\n\t\t\tif (more.Length > 0) print.it($\"\\t{string.Join(\", \", more)}\");\n\t\t}\n\t\t\n\t\t//bool _InUnion() {\n\t\t//\tfor (var td = fd.DeclaringType; td != null && td.IsNested; td = td.DeclaringType) {\n\t\t//\t\tif (td.IsExplicitLayout) return true;\n\t\t//\t}\n\t\t//\treturn false;\n\t\t//}\n\t}\n\t\n\tstring _GetTypeName_Return(MethodDef md, bool inCOM, int bStart, bool bit32) {\n\t\tvar (s, marshal, sNoMarshal) = _GetTypeName(md.ReturnType, out int ptr, inCOM, (false, inCOM), bit32, md);\n\t\t\n\t\tif (marshal != null) {\n\t\t\tif (ptr > 0) (marshal, s) = (null, sNoMarshal);\n\t\t\t//if (!(s is \"bool\")) _PI();\n\t\t}\n\t\t\n\t\tif (!marshal.NE()) b.Insert(bStart, $\"[return: MarshalAs(UnmanagedType.{marshal})]\\r\\n{(inCOM ? \"\\t\" : null)}\");\n\t\t\n\t\tif (ptr > 0) s = s.PadRight(s.Length + ptr, '*');\n\t\t\n\t\treturn s;\n\t\t\n\t\tvoid _PI() {\n\t\t\tprint.it(md.DebugToString(), md.ReturnType.TypeName, s, marshal);\n\t\t}\n\t}\n\t\n\tstring _GetTypeName_Param(Parameter p, in _ParamAttributes attr, bool inCOM, bool inDelegate, bool bit32) {\n\t\tParamDef pd = p.ParamDef;\n\t\t\n\t\tbool isOut = pd.IsOut, isIn = pd.IsIn;\n\t\tbool isConst = attr.isConst && !isOut;\n\t\tbool allowString = !inDelegate && !isOut && (isConst || !(attr.isArrayPtr || attr.hasMemorySize));\n\t\t\n\t\tvar (s, marshal, sNoMarshal) = _GetTypeName(p.Type, out int ptr, inCOM, (allowString, true), bit32, pd);\n\t\t\n\t\t//if (isOut && attr.isConst) _PI();\n\t\t//if (isConst && ptr > 0) _PI();\n\t\t//if (attr.isComOut) _PI();\n\t\t//if (attr.isReturn) _PI();\n\t\t\n\t\tif (attr.isReserved) {\n\t\t\tif (ptr > 0 || (marshal != null && sNoMarshal is \"nint\" or [.., '*'])) { ptr = 0; s = \"nint\"; marshal = null; }\n\t\t\t//else if (marshal != null) _PI();\n\t\t}\n\t\t\n\t\t//if (ptr > 0 && !attr.isArrayPtr && !inDelegate && attr.hasMemorySize) {\n\t\t//\t_PI();\n\t\t//}\n\t\t\n\t\tbool arr = attr.isArrayPtr && ptr > 0 && !inDelegate;\n\t\tif (arr) {\n\t\t\tDebug.Assert(ptr > 0);\n\t\t\tif (ptr > 1 && (marshal != null || isOut)) {\n\t\t\t\t//Let users edit it if need. We can't safely make eg `ref object[]`.\n\t\t\t\t//Eg we don't know whether it is a pointer to an array or array of pointers, and how to free callee-allocated array memory, etc.\n\t\t\t\t//_PI(); //not too many\n\t\t\t\ts = sNoMarshal;\n\t\t\t\tarr = false;\n\t\t\t} else {\n\t\t\t\tif (!marshal.NE()) b.AppendFormat(\"[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.{0})] \", marshal);\n\t\t\t\telse if (inCOM) b.Append(\"[MarshalAs(UnmanagedType.LPArray)] \");\n\t\t\t\t\n\t\t\t\tptr--;\n\t\t\t}\n\t\t\t\n\t\t\tif (isOut) b.Append(isIn ? \"[In, Out] \" : \"[Out] \"); //for arrays it's recommended to specify these attributes even if it won't change anything; default is [In], although blittable type arrays are pinned and not marshalled.\n\t\t} else {\n\t\t\tbool isComOut = ptr == 1 && s == \"void*\" && !attr.isArrayPtr && !attr.hasMemorySize && (attr.isComOut || (p.MethodSigIndex > 0 && p.Method.Parameters[p.Index - 1].Type.TypeName == \"Guid*\")); //if the comout attribute is missing, assume it's comout if this param is void** and previous param is Guid*\n\t\t\t\n\t\t\tbool roi = ptr > 0 && !attr.isArrayPtr && !attr.hasMemorySize && !(pd.Attributes.Has(ParamAttributes.Optional) && marshal == null && !isComOut);\n\t\t\tif (roi && ptr == 1 && s is \"byte\" or \"sbyte\" or \"char\" && !pd.DeclaringMethod.Name.String.Like(\"Var*From*\")) roi = false; //rarely pointer to a single value. Tested: ushort and short usually pointer to single value.\n\t\t\tif (roi) ptr--;\n\t\t\t\n\t\t\tif (!marshal.NE()) {\n\t\t\t\tif (ptr > 0) (s, ptr) = (\"nint\", 0); //can't marshal if pointer (few, rare)\n\t\t\t\telse b.AppendFormat(\"[MarshalAs(UnmanagedType.{0})] \", marshal);\n\t\t\t} else if (isComOut) {\n\t\t\t\tDebug.Assert(roi && ptr == 0);\n\t\t\t\t(s, ptr) = (\"object\", 0);\n\t\t\t\tb.Append(\"[MarshalAs(UnmanagedType.IUnknown)] \");\n\t\t\t}\n\t\t\t\n\t\t\tif (roi) {\n\t\t\t\tb.Append(!isOut ? \"in \" : isIn ? \"ref \" : \"out \");\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (ptr > 0) s = s.PadRight(s.Length + ptr, '*');\n\t\tif (arr) s += \"[]\";\n\t\t\n\t\treturn s;\n\t\t\n\t\tvoid _PI() {\n\t\t\tprint.it(pd.DebugToString(), p.Type.TypeName, s, marshal, pd.Attributes);\n\t\t}\n\t}\n\t\n\t(string type, string marshal, string sNoMarshal) _GetTypeName(TypeSig t, out int ptr, bool com, (bool @string, bool bstr) allow, bool bit32, object def, string fieldTypeWithoutBrackets = null, _TypeDefEtc tdeTopLevel = null) {\n\t\tstring s = fieldTypeWithoutBrackets ?? t.TypeName;\n\t\tstring s0 = s;\n\t\tptr = s.Length; s = s.TrimEnd('*'); ptr -= s.Length;\n\t\t\n\t\tvar fieldStruct = (def as FieldDef)?.DeclaringType;\n\t\tbool field = fieldStruct != null;\n\t\t\n\t\tif (field && fieldStruct.HasNestedTypes && fieldStruct.NestedTypes.Any(o => o.Name == s)) {\n\t\t\t//the field type is a nested type in the same struct\n\t\t} else if (_typedefs.TryGetValue(s, out var k)) {\n\t\t\ts = k;\n\t\t\tif (s[^1] == '*' && allow.@string && ptr == 0) {\n\t\t\t\tif (s == \"char*\") return (\"string\", com ? \"LPWStr\" : \"\", s);\n\t\t\t\tDebug.Assert(s0 == \"PSTR\");\n\t\t\t\treturn (\"string\", \"LPStr\", s);\n\t\t\t\t\n\t\t\t\t//note: with some (few) functions must be `char*`, not `string`. Eg FreeEnvironmentStringsW. Users will figure out it, because `string` makes no sense there.\n\t\t\t}\n\t\t} else {\n\t\t\tvar s1 = s;\n\t\t\ts = _SystemTypeNameToKeyword(s);\n\t\t\tif (s != s1) {\n\t\t\t\tif (s == \"bool\") return (s, \"U1\", \"byte\"); //rare\n\t\t\t\tif (ptr > 0 && s == \"void\") { ptr--; s = \"void*\"; }\n\t\t\t} else {\n\t\t\t\tswitch (s) {\n\t\t\t\tcase \"BOOL\": return field || com ? (s, null, s) : (\"bool\", \"\", \"BOOL\");\n\t\t\t\tcase \"BSTR\":\n\t\t\t\t\tif (!allow.bstr || (!com && def is ParamDef pd && pd.DeclaringMethod.Name.String is string smn && (smn.Starts(\"Sys\") || smn.Starts(\"BSTR_\")))) return (\"char*\", null, \"char*\");\n\t\t\t\t\treturn (\"string\", com ? \"\" : \"BStr\", \"char*\");\n\t\t\t\tcase \"VARIANT\": return (\"object\", field ? \"Struct\" : \"\", s);\n\t\t\t\tcase \"IUnknown\": return (\"object\", field ? \"\" : s, \"nint\");\n\t\t\t\tcase \"IDispatch\": return (\"object\", s, \"nint\");\n\t\t\t\tcase \"DECIMAL\": return (\"decimal\", \"\", s);\n\t\t\t\tcase \"CY\": return (\"decimal\", \"Currency\", s);\n\t\t\t\tcase \"VARIANT_BOOL\": return (\"bool\", \"VariantBool\", \"short\");\n\t\t\t\tcase \"ITEMIDLIST\" when ptr > 0: ptr--; return (\"nint\", null, \"nint\");\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvar nn = t.Namespace + \".\" + s;\n\t\t\t\t\n\t\t\t\tif (bit32 && _types.TryGetValue(nn + \"__32\", out var v32)) { //if in 32-bit context, prefer 32-bit type\n\t\t\t\t\ts += \"__32\";\n\t\t\t\t\tnn += \"__32\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (!_types.TryGetValue(nn, out var v)) { //some non-Ansi types have a field of Ansi type. In few cases it's a metadata bug (can't fix).\n\t\t\t\t\tif (_ansiTypes.Remove(nn, out v)) {\n\t\t\t\t\t\t_types.Add(nn, v);\n\t\t\t\t\t\t_Type(v, true);\n\t\t\t\t\t} //else _PI(); //few, missing in metadata\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (v != null) {\n\t\t\t\t\tif (!v.td.IsValueType) return (s, \"\", \"nint\");\n\t\t\t\t\t\n\t\t\t\t\tif (ptr == 0 && !v.isProcessed) { //need v.isManagedStruct\n\t\t\t\t\t\t_Type(v, true);\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tif (v.isManagedStruct) return (s, \"\", s);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\treturn (s, null, s);\n\t\t\n\t\tvoid _PI() {\n\t\t\tprint.it(def.DebugToString(), s, s0);\n\t\t}\n\t}\n\t\n\tstatic string _SystemTypeNameToKeyword(string s) {\n\t\treturn s switch {\n\t\t\t\"Int32\" => \"int\",\n\t\t\t\"UInt32\" => \"uint\",\n\t\t\t\"SByte\" => \"sbyte\",\n\t\t\t\"Byte\" => \"byte\",\n\t\t\t\"Int16\" => \"short\",\n\t\t\t\"UInt16\" => \"ushort\",\n\t\t\t\"Int64\" => \"long\",\n\t\t\t\"UInt64\" => \"ulong\",\n\t\t\t\"IntPtr\" => \"nint\",\n\t\t\t\"UIntPtr\" => \"nuint\",\n\t\t\t\"Double\" => \"double\",\n\t\t\t\"Single\" => \"float\",\n\t\t\t\"Char\" => \"char\",\n\t\t\t\"Boolean\" => \"bool\",\n\t\t\t\"String\" => \"string\", //not used in metadata, but our code may use\n\t\t\t//\"Decimal\" => \"decimal\", //not used in metadata\n\t\t\t\"Void\" => \"void\",\n\t\t\t_ => s\n\t\t};\n\t}\n\t\n\tstatic (bool isUlong, long l, ulong u) _ObjectToLongOrUlong(object o) {\n\t\treturn o switch {\n\t\t\tint v => (false, v, 0),\n\t\t\tlong v => (false, v, 0),\n\t\t\tshort v => (false, v, 0),\n\t\t\tsbyte v => (false, v, 0),\n\t\t\tuint v => (true, 0, v),\n\t\t\tulong v => (true, 0, v),\n\t\t\tushort v => (true, 0, v),\n\t\t\tbyte v => (true, 0, v),\n\t\t\t_ => throw new Exception(\"bad object type\")\n\t\t};\n\t}\n\t\n\tstring _RenameIfKeyword(string s) {\n\t\tif (!_keywordsReserved.Contains(s)) return s;\n\t\treturn \"@\" + s;\n\t}\n\tHashSet<string> _keywordsReserved = [\n\t\t\"abstract\", \"as\", \"base\", \"bool\", \"break\", \"byte\", \"case\", \"catch\", \"char\", \"checked\", \"class\", \"const\",\n\t\t\"continue\", \"decimal\", \"default\", \"delegate\", \"do\", \"double\", \"else\", \"enum\", \"event\", \"explicit\",\n\t\t\"extern\", \"false\", \"finally\", \"fixed\", \"float\", \"for\", \"foreach\", \"goto\", \"if\", \"implicit\", \"in\",\n\t\t\"int\", \"interface\", \"internal\", \"is\", \"lock\", \"long\", \"namespace\", \"new\", \"null\", \"object\", \"operator\",\n\t\t\"out\", \"override\", \"params\", \"private\", \"protected\", \"public\", \"readonly\", \"ref\", \"return\", \"sbyte\",\n\t\t\"sealed\", \"short\", \"sizeof\", \"stackalloc\", \"static\", \"string\", \"struct\", \"switch\", \"this\", \"throw\",\n\t\t\"true\", \"try\", \"typeof\", \"uint\", \"ulong\", \"unchecked\", \"unsafe\", \"ushort\", \"using\", \"virtual\", \"void\",\n\t\t\"volatile\", \"while\"\n\t];\n\t\n\tvoid _AppendString(string s) {\n\t\tb.Append('\"').Append(s.Escape()).Append('\"');\n\t}\n\t\n\t//StringBuilder _bPrint = new();\n\t//void _Print(int level, string name, string color) {\n\t//\t_bPrint.Clear();\n\t//\t_bPrint.Append(\"<><c \").Append(color).Append(\"><\\a>\");\n\t//\t_bPrint.Append(' ', level).Append(name);\n\t//\t_bPrint.Append(\"</\\a><>\");\n\t//\tprint.it(_bPrint);\n\t//}\n}\n\nstatic class Ext {\n\tpublic static void Start(this StringBuilder t, int level) {\n\t\tif (level == 0) t.Clear();\n\t\telse t.AppendLine().Append('\\t', level);\n\t}\n\t\n\tpublic static void EndAttributes(this StringBuilder t, int level, int lenBefore) {\n\t\tif (t.Length > lenBefore) t.AppendLine().Append('\\t', level);\n\t}\n\t\n\tpublic static string ValueString(this Constant t) {\n\t\tvar o = t.Value;\n\t\treturn o as string ?? throw new Exception(\"not string\");\n\t}\n\t\n\tpublic static string ArgString(this CustomAttribute t, int index) {\n\t\tvar o = t.ConstructorArguments[index].Value;\n\t\treturn o as UTF8String ?? throw new Exception(\"not UTF8String\");\n\t}\n\t\n\tpublic static int ArgInt(this CustomAttribute t, int index) {\n\t\tvar o = t.ConstructorArguments[index].Value;\n\t\treturn Convert.ToInt32(o);\n\t}\n\t\n\tpublic static string ArgGuid(this CustomAttribute t) {\n\t\tvar a = t.ConstructorArguments;\n\t\tvar guid = new Guid((uint)a[0].Value, (ushort)a[1].Value, (ushort)a[2].Value, (byte)a[3].Value, (byte)a[4].Value, (byte)a[5].Value, (byte)a[6].Value, (byte)a[7].Value, (byte)a[8].Value, (byte)a[9].Value, (byte)a[10].Value);\n\t\treturn guid.ToString();\n\t}\n\t\n\tpublic static string ToKeywordString(this ElementType t) {\n\t\treturn t switch {\n\t\t\tElementType.I4 => \"int\",\n\t\t\tElementType.U4 => \"uint\",\n\t\t\tElementType.I1 => \"sbyte\",\n\t\t\tElementType.U1 => \"byte\",\n\t\t\tElementType.I2 => \"short\",\n\t\t\tElementType.U2 => \"ushort\",\n\t\t\tElementType.I8 => \"long\",\n\t\t\tElementType.U8 => \"ulong\",\n\t\t\tElementType.I => \"nint\",\n\t\t\tElementType.U => \"nuint\",\n\t\t\tElementType.R8 => \"double\",\n\t\t\tElementType.R4 => \"float\",\n\t\t\tElementType.String => \"string\",\n\t\t\t_ => throw new Exception(t.ToString())\n\t\t};\n\t}\n\t\n\tpublic static string DebugToString(this object o) {\n\t\tswitch (o) {\n\t\tcase string k: return k;\n\t\tcase ParamDef k: return $\"{_Meth(k.DeclaringMethod)}({k.Name})\";\n\t\tcase MethodDef k: return _Meth(k);\n\t\tcase TypeDef k: return _Typ(k);\n\t\tcase FieldDef k: return _Typ(k.DeclaringType) + \".\" + k.Name;\n\t\t}\n\t\tthrow null;\n\t\t\n\t\tstatic string _Meth(MethodDef m) {\n\t\t\tif (m.IsStatic) return m.Name;\n\t\t\treturn _Typ(m.DeclaringType) + \".\" + m.Name;\n\t\t}\n\t\t\n\t\tstatic string _Typ(TypeDef t) {\n\t\t\tstring s = t.Name;\n\t\t\twhile ((t = t.DeclaringType) != null) s = t.Name + \".\" + s;\n\t\t\treturn s;\n\t\t}\n\t}\n}\n\n//of types (struct, delegate, interface, not enum)\nstruct _TypeAttributes {\n\tpublic bool skip, obsolete, ansi, unicode, bit32, cdeclDelegate, typedef;\n\tpublic string guid;\n\t\n\tpublic _TypeAttributes(TypeDef td) {\n\t\tforeach (var ca in td.CustomAttributes) {\n\t\t\tstring name = ca.AttributeType.Name;\n\t\t\tswitch (name.AsSpan(..^9)) {\n\t\t\tcase \"Obsolete\": obsolete = true; break;\n\t\t\tcase \"Ansi\": ansi = true; break;\n\t\t\tcase \"Unicode\": unicode = true; break;\n\t\t\tcase \"SupportedArchitecture\":\n\t\t\t\tint arch = ca.ArgInt(0);\n\t\t\t\tif ((arch & 2) == 0) { //no x64\n\t\t\t\t\tif ((arch & 1) != 0) bit32 = true; //X86\n\t\t\t\t\telse { skip = true; return; } //Arm64\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \"UnmanagedFunctionPointer\":\n\t\t\t\tif (ca.ArgInt(0) is int cc && cc != 1) {\n\t\t\t\t\tif (cc == 2) cdeclDelegate = true;\n\t\t\t\t\telse { print.it(cc); skip = true; return; }\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \"NativeTypedef\" or \"MetadataTypedef\":\n\t\t\t\ttypedef = true;\n\t\t\t\tbreak;\n\t\t\tcase \"Guid\":\n\t\t\t\tguid = ca.ArgGuid();\n\t\t\t\tbreak;\n\t\t\tcase \"StructSizeField\":\n\t\t\t\t//don't use, because many structs don't have this attribute (eg STARTUPINFOEXW). \n\t\t\t\t//\tUsers would assume all struct ctors auto-init the field, and this way make bugs.\n\t\t\t\t//print.it(ca.ArgString(0), itmd.DebugToString()); //all cbSize\n\t\t\t\tbreak;\n\t\t\tcase \"Documentation\"\n\t\t\tor \"SupportedOSPlatform\"\n\t\t\tor \"RAIIFree\"\n\t\t\tor \"InvalidHandleValue\"\n\t\t\tor \"Agile\"\n\t\t\tor \"AlsoUsableFor\"\n\t\t\t:\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tprint.it(name[..^9], td.DebugToString());\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n}\n\n//of extern methods\nstruct _MethodAttributes {\n\tpublic bool skip, obsolete, unicode, bit32, inline;\n\tpublic int constant;\n\t\n\tpublic _MethodAttributes(MethodDef md) {\n\t\tforeach (var ca in md.CustomAttributes) {\n\t\t\tstring name = ca.AttributeType.Name;\n\t\t\tswitch (name.AsSpan(..^9)) {\n\t\t\tcase \"Obsolete\": obsolete = true; break;\n\t\t\tcase \"Ansi\": skip = true; return;\n\t\t\tcase \"Unicode\": unicode = true; break;\n\t\t\tcase \"SupportedArchitecture\":\n\t\t\t\tint arch = ca.ArgInt(0);\n\t\t\t\tif ((arch & 2) == 0) { //no x64\n\t\t\t\t\tif ((arch & 1) != 0) bit32 = true; //X86\n\t\t\t\t\telse { skip = true; return; } //Arm64\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \"Constant\":\n\t\t\t\t(inline, constant) = (true, ca.ArgString(0).ToInt());\n\t\t\t\tbreak;\n\t\t\tcase \"Documentation\"\n\t\t\tor \"SupportedOSPlatform\"\n\t\t\tor \"CanReturnMultipleSuccessValues\"\n\t\t\tor \"CanReturnErrorsAsSuccess\"\n\t\t\tor \"DoesNotReturn\"\n\t\t\t:\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tprint.it(name[..^9], md.DebugToString());\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n}\n\nstruct _FieldAttributes {\n\tpublic bool isConst, isBitfield, isFlexibleArray;\n\t//public string assocEnum;\n\t\n\tpublic _FieldAttributes(FieldDef fd) {\n\t\tforeach (var ca in fd.CustomAttributes) {\n\t\t\tstring name = ca.AttributeType.Name;\n\t\t\tswitch (name.AsSpan(..^9)) {\n\t\t\tcase \"Const\": isConst = true; break;\n\t\t\tcase \"NativeBitfield\": isBitfield = true; break;\n\t\t\tcase \"FlexibleArray\": isFlexibleArray = true; break;\n\t\t\tcase \"AssociatedEnum\": /*assocEnum = ca.ArgString(0);*/ break; //not used. We convert most enums to const.\n\t\t\tcase \"Documentation\": break; //few\n\t\t\tcase \"Obsolete\" or \"NotNullTerminated\" or \"NullNullTerminated\": break;\n\t\t\tcase \"NativeArrayInfo\": break; //few, not useful\n\t\t\tdefault:\n\t\t\t\tprint.it(name[..^9], fd.DebugToString());\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n}\n\nstruct _ParamAttributes {\n\tpublic bool isConst, isReturn, isComOut, isArrayPtr, isReserved, hasMemorySize;\n\t//public string assocEnum;\n\t\n\tpublic _ParamAttributes(ParamDef pd) {\n\t\tforeach (var ca in pd.CustomAttributes) {\n\t\t\tstring name = ca.AttributeType.Name;\n\t\t\tswitch (name.AsSpan(..^9)) {\n\t\t\tcase \"Const\": isConst = true; break;\n\t\t\tcase \"RetVal\": isReturn = true; break;\n\t\t\tcase \"ComOutPtr\": isComOut = true; break;\n\t\t\tcase \"NativeArrayInfo\": isArrayPtr = true; break;\n\t\t\tcase \"Reserved\": isReserved = true; break;\n\t\t\tcase \"MemorySize\": hasMemorySize = true; break; //can be array or normal struct or variable-size struct, we can't know, therefore use pointer, not in/ref/out\n\t\t\tcase \"AssociatedEnum\": /*assocEnum = ca.ArgString(0);*/ break; //not used. We convert most enums to const.\n\t\t\tcase \"NullNullTerminated\": break;\n\t\t\tcase \"NotNullTerminated\": break;\n\t\t\tcase \"IgnoreIfReturn\" or \"DoNotRelease\" or \"Retained\" or \"FreeWith\" or \"RAIIFree\": break;\n\t\t\tcase \"Documentation\": break; //few\n\t\t\tdefault:\n\t\t\t\tprint.it(name[..^9], pd.DebugToString());\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n}\n\nrecord class _TypeDefEtc(TypeDef td, string name, _TypeAttributes attr) {\n\tpublic bool isManagedStruct;\n\tpublic bool isProcessed;\n}\n"
  },
  {
    "path": "Scripts/@WinAPI converter/WinApiConverter.cs",
    "content": "/*/ nuget -\\dnlib; /*/\nusing dnlib.DotNet;\nusing System.Numerics;\n\npartial class WinApiConverter {\n\tModuleDefMD _module;\n\tStringBuilder b = new();\n\tDictionary<string, string> _typedefs = new(500);\n\tDictionary<string, _TypeDefEtc> _types = new(20_000);\n\tDictionary<string, _TypeDefEtc> _ansiTypes = new(1000);\n\tDictionary<string, (string members, bool isUnk, bool isDisp, string name)> _iBase = new(10_000);\n\tint _nAll = 0, _nFiltered = 0;\n\t\n\tpublic Dictionary<string, string> Result { get; private set; } = new(250_000);\n\t\n\tpublic void Convert(HashSet<string> onlyNamespaces = null) {\n\t\t_module = ModuleDefMD.Load(folders.Downloads + @\"Windows.Win32.winmd\");\n\t\tList<TypeDef> apis = new(50);\n\t\t_AddSomeTypes();\n\t\t\n\t\tforeach (var group in _module.Types.GroupBy(o => o.Namespace.String).Skip(1).OrderBy(o => o.Key)) { //group and order types by namespace\n\t\t\tstring ns = group.Key, ns2 = ns[14..];\n\t\t\tif (ns2 is\n\t\t\t\t\"Foundation.Metadata\" //not Windows API. Attributes etc.\n\t\t\t\tor \"NetworkManagement.P2P\" //all API deprecated, but in metadata only some have [Obsolete], which causes some problems\n\t\t\t\t) continue; //CONSIDER: skip more namespaces.\n\t\t\tif (onlyNamespaces != null && !onlyNamespaces.Contains(ns2)) continue;\n\t\t\t//print.it(ns2, group.Count());\n\t\t\t\n\t\t\tforeach (var td in group) {\n\t\t\t\tif (td.IsEnum) {\n\t\t\t\t\t_Enum(td);\n\t\t\t\t} else if (td.IsValueType || td.IsInterface || td.IsDelegate) {\n\t\t\t\t\t_TypeAttributes attr = td.HasCustomAttributes ? new(td) : default;\n\t\t\t\t\tif (attr.skip) continue;\n\t\t\t\t\t//if (attr.obsolete) print.it(td.Name, td.IsValueType); //few, all ansi struct\n\t\t\t\t\t\n\t\t\t\t\t//if (attr.guid != null && !td.IsInterface && td.HasFields) print.it(td.IsValueType, td.Name);\n\t\t\t\t\t\n\t\t\t\t\tif (attr.typedef) {\n\t\t\t\t\t\t_Typedef(td);\n\t\t\t\t\t} else if (attr.guid != null && td.IsValueType && td.Fields.Count == 0) {\n\t\t\t\t\t\t_CoClass(td, attr);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tstring name = td.Name; //note: don't remove suffix \"W\", or then also would need to find and rename references or add to typedef etc\n\t\t\t\t\t\t\n\t\t\t\t\t\tstring typedef = name switch {\n\t\t\t\t\t\t\t\"POINT\" or \"SIZE\" or \"RECT\" or \"PROPERTYKEY\" or \"DEVPROPKEY\" or \"SID_IDENTIFIER_AUTHORITY\" => name,\n\t\t\t\t\t\t\t\"POINTL\" or \"RECTL\" => name[..^1],\n\t\t\t\t\t\t\t\"FARPROC\" or \"NEARPROC\" or \"PROC\" => \"nint\",\n\t\t\t\t\t\t\t_ => null\n\t\t\t\t\t\t};\n\t\t\t\t\t\tif (typedef != null) { _typedefs.Add(name, typedef); continue; }\n\t\t\t\t\t\t\n\t\t\t\t\t\tif (!attr.ansi && name.Starts(\"PROPSHEETPAGEA_V\") || name.Starts(\"PROPSHEETHEADERA_V\")) attr.ansi = true;\n\t\t\t\t\t\t\n\t\t\t\t\t\t_TypeDefEtc tde = new(td, name, attr);\n\t\t\t\t\t\tvar d = attr.ansi ? _ansiTypes : _types;\n\t\t\t\t\t\tif (!d.TryAdd(ns + \".\" + name, tde)) {\n\t\t\t\t\t\t\tDebug.Assert(attr.bit32); //good: all 32-bit types are after 64-bit types with same name.\n\t\t\t\t\t\t\tname += \"__32\";\n\t\t\t\t\t\t\td.Add(ns + \".\" + name, tde with { name = name });\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tDebug.Assert(td.Name == \"Apis\");\n\t\t\t\t\tapis.Add(td);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tforeach (var t in _types.Values.ToArray()) {\n\t\t\tif (t.isProcessed) continue;\n\t\t\t_Type(t, false);\n\t\t}\n\t\t\n\t\tforeach (var td in apis) {\n\t\t\t_Apis(td);\n\t\t}\n\t\t\n\t\tprint.it($\"//all={_nAll}, -ansi={_nFiltered}, -dup={Result.Count}\");\n\t}\n\t\n\t//class \"Apis\"\n\tvoid _Apis(TypeDef td) {\n\t\tif (td.HasMethods) {\n\t\t\tforeach (var md in td.Methods) {\n\t\t\t\t_nAll++;\n\t\t\t\t_ExternMethod(md);\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (td.HasFields) {\n\t\t\tforeach (var fd in td.Fields) {\n\t\t\t\t_nAll++;\n\t\t\t\t_ConstOrStaticReadonly(fd);\n\t\t\t}\n\t\t}\n\t\t\n\t\tDebug.Assert(!td.HasNestedTypes);\n\t}\n\t\n\t//top-level struct, delegate, interface, not enum\n\tvoid _Type(_TypeDefEtc t, bool recurse) {\n\t\tDebug.Assert(!t.isProcessed);\n\t\tif (recurse) { _bStack.Push(b); b = new(); } //not many\n\t\t\n\t\t_nAll++;\n\t\tb.Start(0);\n\t\tvar (td, name, attr) = t;\n\t\t\n\t\tif (t.td.IsDelegate) {\n\t\t\t_Delegate(td, name, attr);\n\t\t} else if (td.IsInterface) {\n\t\t\t_Interface(td, name, attr);\n\t\t} else { //struct\n\t\t\tif (attr.guid != null) b.AppendLine($\"[Guid(\\\"{attr.guid}\\\")]\"); //few\n\t\t\t_Struct(0, td, t, name);\n\t\t\t_AddToDict(name);\n\t\t}\n\t\t\n\t\tt.isProcessed = true;\n\t\tif (recurse) b = _bStack.Pop();\n\t}\n\tStack<StringBuilder> _bStack = new();\n\t\n\tvoid _Struct(int level, TypeDef td, _TypeDefEtc tdeTopLevel, string name = null) {\n\t\tname ??= td.Name;\n\t\t\n\t\tint len = b.Length;\n\t\tif (td.Layout == TypeAttributes.ExplicitLayout || td.HasClassLayout) {\n\t\t\tb.Append(\"[StructLayout(LayoutKind.\").Append(td.Layout == TypeAttributes.ExplicitLayout ? \"Explicit\" : \"Sequential\");\n\t\t\tif (td.HasClassLayout && td.ClassLayout is var l) {\n\t\t\t\tif (l.ClassSize > 0) b.Append(\", Size = \").Append(l.ClassSize);\n\t\t\t\tif (l.PackingSize > 0) b.Append(\", Pack = \").Append(l.PackingSize);\n\t\t\t}\n\t\t\tb.Append(\")]\");\n\t\t}\n\t\tb.EndAttributes(level, len);\n\t\t\n\t\tb.AppendFormat(\"{0} struct {1} {{\", level > 0 ? \"public\" : \"internal\", name);\n\t\t\n\t\tDebug.Assert(!td.HasMethods);\n\t\t\n\t\tif (td.HasFields) {\n\t\t\tfor (int i = 0, last = td.Fields.Count - 1; i <= last; i++) {\n\t\t\t\t_Field(level + 1, td.Fields[i], i == last, tdeTopLevel);\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (td.HasNestedTypes) {\n\t\t\tforeach (var ntd in td.NestedTypes) {\n\t\t\t\tb.AppendLine();\n\t\t\t\tb.Start(level + 1);\n\t\t\t\t//if (ntd.HasCustomAttributes) {\n\t\t\t\t//\tprint.it(name, ntd.CustomAttributes); //several StructSizeFieldAttribute\n\t\t\t\t//}\n\t\t\t\t_Struct(level + 1, ntd, tdeTopLevel);\n\t\t\t}\n\t\t}\n\t\t\n\t\tb.AppendLine().Append('\\t', level).Append('}');\n\t}\n\t\n\tvoid _Field(int level, FieldDef fd, bool last, _TypeDefEtc tdeTopLevel) {\n\t\tb.Start(level);\n\t\t\n\t\tstring name = _RenameIfKeyword(fd.Name);\n\t\tif (fd.HasConstant) {\n\t\t\tb.Append(\"public \");\n\t\t\t_Const(fd, name);\n\t\t\tb.Append(';');\n\t\t} else {\n\t\t\t_FieldAttributes attr = fd.HasCustomAttributes ? new(fd) : default;\n\t\t\t\n\t\t\t//Not all flexible arrays have the attribute. It seems `field[1]` have but `field[]` don't.\n\t\t\t//\tWe can either comment out the field or use FlexibleArray. Let's use FlexibleArray to make it easy to use, although sizeof will be incorrect.\n\t\t\tif (last && fd.FieldType.ElementType == ElementType.Array && !attr.isFlexibleArray && fd.FieldSig.Type is ArraySig { Sizes: [1] }) {\n\t\t\t\t//print.it(fd.DebugToString());\n\t\t\t\tattr.isFlexibleArray = true;\n\t\t\t\tb.Append(\"\\r\\n#warning This field makes sizeof struct incorrect, because in C the array has 0 elements (zero size). Comment out this warning if it's OK.\\r\\n\\t\");\n\t\t\t}\n\t\t\t\n\t\t\tif (fd.FieldOffset.HasValue) b.Append(\"[FieldOffset(\").Append(fd.FieldOffset.Value).Append(\")] \");\n\t\t\t\n\t\t\tvar (st, isManaged, arraySig) = _GetTypeName_Field(fd, attr, tdeTopLevel);\n\t\t\t\n\t\t\tDebug.Assert(!(isManaged && level > 1));\n\t\t\tif (isManaged && level == 1) tdeTopLevel.isManagedStruct = true;\n\t\t\t\n\t\t\tif (arraySig != null) {\n\t\t\t\tif (attr.isFlexibleArray) {\n\t\t\t\t\tif (st.Ends('*')) b.AppendFormat(\"public FlexibleArray<nint> {1}; //{0}\", st, name);\n\t\t\t\t\telse b.AppendFormat(\"public FlexibleArray<{0}> {1};\", st, name);\n\t\t\t\t} else {\n\t\t\t\t\tuint size = arraySig.Sizes[0];\n\t\t\t\t\t//if (size == 1) print.it(fd.DebugToString()); //not many. Some look like should be a flexible array, some structs have 2 flexible arrays, some are just to fill unused gaps.\n\t\t\t\t\t\n\t\t\t\t\tif (st is \"char\" or \"byte\" or \"short\" or \"int\" or \"long\" or \"sbyte\" or \"ushort\" or \"uint\" or \"ulong\" or \"float\" or \"double\" or \"bool\") {\n\t\t\t\t\t\tb.AppendFormat(\"public fixed {0} {1}[{2}];\", st, name, size);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tb.AppendFormat(\"public {0}_{1} {0}; [InlineArray({1})] public struct {0}_{1} {{ {2} _; }}\", name, size, st[^1] == '*' ? \"nint\" : st);\n\t\t\t\t\t}\n\t\t\t\t\tb.AppendFormat(\" //[MarshalAs(UnmanagedType.{0}, SizeConst = {1})] public {2}[] {3};\", st == \"char\" ? \"ByValTStr\" : \"ByValArray\", size, st, name);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (!attr.isBitfield) b.Append(\"public \");\n\t\t\t\tif (fd.IsStatic) b.Append(\"static \"); //none\n\t\t\t\t\n\t\t\t\tif (name.Starts(\"Anonymous\") && st[0] == '_' && st.Eq(1, name)) {\n\t\t\t\t\tif (st.Ends(\"_e__Union\")) name = \"u_\" + name[9..];\n\t\t\t\t\telse if (st.Ends(\"_e__Struct\")) name = \"n_\" + name[9..];\n\t\t\t\t\t//else print.it(name, st);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tb.AppendFormat(\"{0} {1};\", st, name);\n\t\t\t}\n\t\t\t\n\t\t\tif (attr.isBitfield) _Bitfields();\n\t\t}\n\t\t\n\t\tvoid _Bitfields() {\n\t\t\tstring type = fd.FieldType.ElementType.ToKeywordString();\n\t\t\tstring index = name[9..]; //_bitfield1 -> 1\n\t\t\tstring s1 = null, s2 = null; if (type is \"byte\" or \"sbyte\" or \"short\" or \"ushort\") (s1, s2) = (\"(\" + type + \")(\", \")\");\n\t\t\tb.Start(level);\n\t\t\tb.AppendFormat(\"{0} _{1}G(int o, {0} m) => {3}{2} >> o & m{4};\", type, index, name, s1, s2);\n\t\t\tb.Start(level);\n\t\t\tb.AppendFormat(\"void _{1}S(int o, {0} m, {0} v) => {2} = {3}({2} & ~(m << o)) | ((v & m) << o){4};\", type, index, name, s1, s2);\n\t\t\t\n\t\t\tforeach (var ca in fd.CustomAttributes) {\n\t\t\t\tif (ca.AttributeType.Name == \"NativeBitfieldAttribute\") {\n\t\t\t\t\tb.Start(level);\n\t\t\t\t\tb.AppendFormat(\"public {0} {1} {{ get => _{4}G({2}, {3}); set => _{4}S({2}, {3}, value); }}\", type, ca.ArgString(0), ca.ArgInt(1), (1 << ca.ArgInt(2)) - 1, index);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t\n\t//global constant (#define, const) or variable (eg GUID)\n\tvoid _ConstOrStaticReadonly(FieldDef fd) {\n\t\tb.Start(0);\n\t\tstring name = fd.Name;\n\t\tb.Append(\"internal \");\n\t\tif (fd.HasConstant) {\n\t\t\tif (name[^1] == 'A' && fd.DeclaringType.FindField(name.Ends(\"_A\") ? name[..^2] : name.ReplaceAt(^1.., \"W\")) != null) return; //info: don't use [NativeEncodingAttribute(\"ansi\")]. Only string constants have it, and maybe not all.\n\t\t\t_Const(fd, name);\n\t\t\t//print.it(b);\n\t\t} else {\n\t\t\tif (!fd.HasCustomAttributes) throw new Exception(\"no attributes\");\n\t\t\tb.Append(\"static readonly \");\n\t\t\t\n\t\t\tvar t = fd.FieldType;\n\t\t\tvar st = t.TypeName;\n\t\t\t//if (!(st is \"Guid\" or \"PROPERTYKEY\" or \"DEVPROPKEY\" or \"SID_IDENTIFIER_AUTHORITY\")) print.it(st);\n\t\t\tb.Append(st).Append(' ').Append(name).Append(\" = new(\");\n\t\t\t\n\t\t\t//if (fd.HasCustomAttributes) {\n\t\t\t//\tforeach (var v in fd.CustomAttributes) {\n\t\t\t//\t\tstring s = v.AttributeType.Name;\n\t\t\t//\t\tif (s is \"DocumentationAttribute\" or \"NativeEncodingAttribute\") continue;\n\t\t\t//\t\tif (s is \"GuidAttribute\" or \"ConstantAttribute\") continue;\n\t\t\t//\t\tprint.it(s, name); //none\n\t\t\t//\t}\n\t\t\t//}\n\t\t\t\n\t\t\t//print.it(st, name);\n\t\t\tif (st == \"Guid\") {\n\t\t\t\tvar ca = fd.CustomAttributes.Find(\"Windows.Win32.Foundation.Metadata.GuidAttribute\") ?? throw new Exception(\"no Guid attr\");\n\t\t\t\t_AppendString(ca.ArgGuid());\n\t\t\t} else { //see _AddSomeTypes()\n\t\t\t\tvar ca = fd.CustomAttributes.Find(\"Windows.Win32.Foundation.Metadata.ConstantAttribute\") ?? throw new Exception(\"no ConstantAttribute\");\n\t\t\t\tstring s = ca.ArgString(0);\n\t\t\t\tif (0 != s.RxReplace(@\"^\\{((?:\\d+, ){10}\\d+)\\}(, \\d+)$\", \"new($1)$2\", out s, 1)) b.Append(s); //eg PROPERTYKEY\n\t\t\t\telse if (s.Like(\"{*}\")) b.Append(s, 1, s.Length - 2); //SID_IDENTIFIER_AUTHORITY\n\t\t\t\telse if (s != \"0\") print.warning(s);\n\t\t\t}\n\t\t\t\n\t\t\tb.Append(')');\n\t\t\t//print.it(b);\n\t\t}\n\t\tb.Append(';');\n\t\t_AddToDict(name);\n\t}\n\t\n\tvoid _Const(FieldDef fd, string name) {\n\t\t//if (fd.HasCustomAttributes) {\n\t\t//\tforeach (var v in fd.CustomAttributes) {\n\t\t//\t\tstring s = v.AttributeType.Name;\n\t\t//\t\tif (s is \"DocumentationAttribute\") continue;\n\t\t//\t\tif (s is \"NativeEncodingAttribute\") continue;\n\t\t//\t\tprint.it(s, name); //only several ConstAttribute\n\t\t//\t}\n\t\t//}\n\t\t\n\t\tb.Append(\"const \");\n\t\tvar c = fd.Constant;\n\t\tvar ct = c.Type;\n\t\tvar st = ct.ToKeywordString();\n\t\t\n\t\t//var st2 = fd.FieldType.TypeName;\n\t\t//if (_typedefs.TryGetValue(st2, out var st3)) st2 = st3; else st2 = _SystemTypeNameToKeyword(st2);\n\t\t//if (st2 != st) print.it(st, st2, name);\n\t\t\n\t\tif (ct is ElementType.U4 && name.Starts(\"WM_\") && (uint)c.Value <= 0xffff) st = \"ushort\";\n\t\t\n\t\tb.Append(st).Append(' ').Append(name).Append(\" = \");\n\t\tif (ct == ElementType.String) {\n\t\t\t_AppendString(c.ValueString());\n\t\t} else if (ct is ElementType.U4 or ElementType.U1 or ElementType.U2 or ElementType.U8) {\n\t\t\tb.AppendFormat(\"0x{0:X}\", c.Value);\n\t\t} else {\n\t\t\tvar v = c.Value;\n\t\t\tif (v is int iv && iv < 0 && fd.FieldType.TypeName is \"HRESULT\" or \"NTSTATUS\") b.AppendFormat(\"unchecked((int)0x{0:X})\", v);\n\t\t\telse b.Append(c.Value);\n\t\t\t\n\t\t\tif (ct is ElementType.R4) b.Append('f');\n\t\t}\n\t}\n\t\n\tvoid _Enum(TypeDef td) {\n\t\tstring name = td.Name;\n\t\t\n\t\tbool isFlags = false, isScoped = false;\n\t\tif (td.HasCustomAttributes) {\n\t\t\tforeach (var ca in td.CustomAttributes) {\n\t\t\t\tstring s = ca.AttributeType.Name;\n\t\t\t\tswitch (s) {\n\t\t\t\tcase \"FlagsAttribute\": isFlags = true; break;\n\t\t\t\tcase \"ScopedEnumAttribute\": isScoped = true; break;\n\t\t\t\tcase \"DocumentationAttribute\": break;\n\t\t\t\tcase \"AssociatedConstantAttribute\": break; //few\n\t\t\t\tdefault: print.it(s); break;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (!isFlags) isFlags = _IsFlagsEnum();\n\t\tif (!isScoped) isScoped = _IsScopedEnum();\n\t\t\n\t\t//In metadata, many #define constants are added to enums. Most of these enums are not Windows API.\n\t\t//It is bad, because:\n\t\t//\t- Many parameters etc where these constants are used are not of these enum types. Then how users can find these enums? Also need to cast always.\n\t\t//\t- Many other constants aren't added to enums. It seems the work is unfinished.\n\t\t//\t- Some enums are large (eg WIN32_ERROR 3335). Usually users need only few constants. And in LA it's super easy to find them and add to the code.\n\t\t//\t- Noticed some errors, eg in enum SHARE_INFO_PERMISSIONS wrong ACCESS_ALL and missing ACCESS_GROUP.\n\t\t//\t- I don't like to write arguments like `api.ENUM_TYPE.ET_CONSTANT1 | api.ENUM_TYPE.ET_CONSTANT2`. Better use Windows API more like in C++. Most constants have a prefix. And normally they are in an `api` class.\n\t\t//Instead add enum members as const. Unless the enum is marked as scoped or members don't have a common prefix etc.\n\t\t\n\t\tif (isScoped) { //add as enum\n\t\t\t_nAll++;\n\t\t\tb.Start(0);\n\t\t\tif (isFlags) b.AppendLine(\"[Flags]\");\n\t\t\tb.Append(\"internal enum \").Append(name);\n\t\t\tvar ut = td.GetEnumUnderlyingType().ElementType;\n\t\t\tif (ut != ElementType.I4) b.Append(\" : \").Append(ut.ToKeywordString());\n\t\t\tb.Append(\" {\\r\\n\\t\");\n\t\t\t\n\t\t\tlong lNext = 0; ulong ulNext = 0;\n\t\t\tbool once = false;\n\t\t\tforeach (var fd in td.Fields) {\n\t\t\t\tif (fd.Constant is { } c) {\n\t\t\t\t\tif (!once) once = true; else b.Append(\",\\r\\n\\t\");\n\t\t\t\t\tb.Append(fd.Name);\n\t\t\t\t\tvar (isUlong, l, u) = _ObjectToLongOrUlong(c.Value);\n\t\t\t\t\tif (isUlong) {\n\t\t\t\t\t\tif (u != ulNext || isFlags) b.Append(\" = \").AppendFormat(\"0x{0:X}\", u);\n\t\t\t\t\t\tulNext = u + 1;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (l != lNext || isFlags) {\n\t\t\t\t\t\t\tb.Append(\" = \");\n\t\t\t\t\t\t\tif (isFlags && l >= 0) b.AppendFormat(\"0x{0:X}\", l); else b.Append(l);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tlNext = l + 1;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tb.Append(\"\\r\\n}\");\n\t\t\t_AddToDict(name);\n\t\t\t_types.Add(td.Namespace + \".\" + name, new(td, name, default) { isProcessed = true });\n\t\t} else { //add members as const\n\t\t\tstring st = null; bool hexInt = false;\n\t\t\tswitch (name) {\n\t\t\tcase \"WIN32_ERROR\": (st, hexInt) = (\"int\", true); break; //uint\n\t\t\tcase \"VIRTUAL_KEY\": break; //ushort\n\t\t\t}\n\t\t\t\n\t\t\tst ??= _SystemTypeNameToKeyword(td.GetEnumUnderlyingType().TypeName);\n\t\t\t\n\t\t\tforeach (var fd in td.Fields) {\n\t\t\t\tif (fd.Constant is { } c) {\n\t\t\t\t\tb.Start(0);\n\t\t\t\t\tstring cName = fd.Name;\n\t\t\t\t\tb.Append(\"internal const \").Append(st).Append(' ').Append(cName).Append(\" = \");\n\t\t\t\t\tvar (isUlong, l, u) = _ObjectToLongOrUlong(c.Value);\n\t\t\t\t\tif (hexInt) {\n\t\t\t\t\t\tDebug.Assert(isUlong);\n\t\t\t\t\t\tif ((int)u < 0) b.AppendFormat(\"unchecked((int)0x{0:X})\", u); else b.Append(u);\n\t\t\t\t\t} else if (isUlong) {\n\t\t\t\t\t\tb.AppendFormat(\"0x{0:X}\", u);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (isFlags && l >= 0) b.AppendFormat(\"0x{0:X}\", l); else b.Append(l);\n\t\t\t\t\t}\n\t\t\t\t\tb.Append(';');\n\t\t\t\t\t//print.it(b);\n\t\t\t\t\t_AddToDict(cName);\n\t\t\t\t\t_nAll++;\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tif (!_typedefs.TryAdd(name, st)) {\n\t\t\t\tif (_typedefs[name] != st) print.warning($\"{name}, {st}, {_typedefs[name]}\");\n\t\t\t}\n\t\t}\n\t\t\n\t\tbool _IsFlagsEnum() { //if no [Flags], assume it's a flags enum if contains only values like 1 2 4 8...\n\t\t\tulong max = 0;\n\t\t\tforeach (var fd in td.Fields) {\n\t\t\t\tif (fd.Constant is { } c) {\n\t\t\t\t\tvar (isUlong, l, u) = _ObjectToLongOrUlong(c.Value);\n\t\t\t\t\tif (!isUlong) u = (ulong)l;\n\t\t\t\t\tif (u != 0 && !BitOperations.IsPow2(u)) return false;\n\t\t\t\t\tmax = Math.Max(max, u);\n\t\t\t\t}\n\t\t\t}\n\t\t\t//if (max<4 && name.Ends(\"FLAGS\")) print.it(max, name);\n\t\t\treturn max >= 4 || name.Ends(\"FLAGS\");\n\t\t}\n\t\t\n\t\tbool _IsScopedEnum() { //if no [ScopedEnum], let it be enum if some member names don't contain '_' and contain lowercase chars, unless most such names have a common prefix of length >= 2.\n\t\t\tvar a = td.Fields;\n\t\t\tReadOnlySpan<char> span1 = null;\n\t\t\tint minPrefixLength = int.MaxValue, nDiffPrefix = 0, nCommonPrefix = 0;\n\t\t\tfor (int i = 1; i < a.Count; i++) {\n\t\t\t\tvar s8 = a[i].Name;\n\t\t\t\tif (s8.Length < 4) return true;\n\t\t\t\tif (s8.IndexOf('_', 1) < 0 && s8.String.Any(o => char.IsAsciiLetterLower(o))) {\n\t\t\t\t\tif (span1 == null) span1 = s8.String;\n\t\t\t\t\telse {\n\t\t\t\t\t\tvar p = span1.CommonPrefixLength(s8.String);\n\t\t\t\t\t\tif (p < 2) {\n\t\t\t\t\t\t\tif (++nDiffPrefix > 1) break; //allow single member without common prefix; usually `MaxX`\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tnCommonPrefix++;\n\t\t\t\t\t\t\tminPrefixLength = Math.Min(minPrefixLength, p);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (nDiffPrefix > 0) {\n\t\t\t\t//if (nDiffPrefix > 1 || nCommonPrefix == 0) print.it(name, td.Fields.Skip(1).Select(o => o.Name));\n\t\t\t\tif (nDiffPrefix > 1 || nCommonPrefix == 0) return true;\n\t\t\t\t//print.it(name, td.Fields.Skip(1).Select(o => o.Name));\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t}\n\t\n\tvoid _Method2(string name, MethodDef md, bool inCOM, bool inDelegate, int bStart, bool bit32) {\n\t\tif (md.HasReturnType) {\n\t\t\tvar st = _GetTypeName_Return(md, inCOM, bStart, bit32);\n\t\t\tb.Append(st).Append(' ');\n\t\t} else b.Append(\"void \");\n\t\t\n\t\t//print.it($\"<><lc yellowgreen>{name}  ({md.DeclaringType.Name})<>\");\n\t\t\n\t\tb.Append(name).Append('(');\n\t\tstring sep = null;\n\t\tforeach (var p in md.Parameters) {\n\t\t\tif (!p.IsNormalMethodParameter) continue; //hidden `this` parameter\n\t\t\tb.Append(sep); sep = \", \";\n\t\t\t//print.it(p.Index, p.Type.TypeName, p.Name);\n\t\t\t\n\t\t\tvar pName = _RenameIfKeyword(p.Name);\n\t\t\tvar pd = p.ParamDef;\n\t\t\t_ParamAttributes attr = pd.HasCustomAttributes ? new(pd) : default;\n\t\t\t\n\t\t\tvar st = _GetTypeName_Param(p, attr, inCOM, inDelegate, bit32);\n\t\t\t\n\t\t\tb.Append(st).Append(' ').Append(pName);\n\t\t}\n\t\tif (md.CallingConvention is dnlib.DotNet.CallingConvention.VarArg) b.Append(\", __arglist\");\n\t\tb.Append(\");\");\n\t}\n\t\n\tvoid _ExternMethod(MethodDef md) {\n\t\t//if (md.Attributes is not (MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig | MethodAttributes.PinvokeImpl)) print.it(md.Attributes, md.Name);\n\t\t\n\t\tb.Start(0);\n\t\tstring name = md.Name, name0 = name;\n\t\tvar attr = md.HasCustomAttributes ? new _MethodAttributes(md) : default;\n\t\tif (attr.skip) return;\n\t\tif (attr.unicode && name[^1] == 'W') name = name[..^1];\n\t\t//if (attr.bit32) print.it(name, Result.ContainsKey(name));\n\t\tif (attr.bit32) name += \"__32\"; //even if the 32-bit version is named differently, eg with suffix \"64\"\n\t\t\n\t\tif (attr.inline) {\n\t\t\tDebug.Assert(md.Parameters.Count == 0);\n\t\t\tvar st = _GetTypeName_Return(md, false, 0, attr.bit32);\n\t\t\t//print.it(name, attr.constant, md.ReturnType.TypeName, st);\n\t\t\tDebug.Assert(st is \"nint\" or \"int\" or \"uint\");\n\t\t\tb.AppendFormat(\"internal static {0} {1}() => {2};\", st, name, attr.constant);\n\t\t} else {\n\t\t\tif (attr.obsolete) b.AppendLine(\"[Obsolete]\");\n\t\t\t\n\t\t\t//[DllImport]\n\t\t\tvar im = md.ImplMap;\n\t\t\tb.Append(\"[DllImport(\\\"\").Append(im.Module.Name.String.Lower()).Append('\"');\n\t\t\t//if (name0!=im.Name) print.it(im.Name, md.Name); //few, although many ordinal-only functions exist in Windows API (not all exist in winmd). Never mind.\n\t\t\tif (name != name0) b.AppendFormat(\", EntryPoint = \\\"{0}\\\"\", name0);\n\t\t\tif (im.IsCallConvCdecl) b.Append(\", CallingConvention = CallingConvention.Cdecl\");\n\t\t\tif (im.SupportsLastError) b.Append(\", SetLastError = true\");\n\t\t\tif (0 != (im.Attributes & ~(PInvokeAttributes.CallConvWinapi | PInvokeAttributes.CallConvCdecl | PInvokeAttributes.SupportsLastError | PInvokeAttributes.NoMangle))) print.warning(name);\n\t\t\tb.AppendLine(\")]\");\n\t\t\t\n\t\t\tb.Append(\"internal static extern \");\n\t\t\t_Method2(name, md, false, false, 0, attr.bit32);\n\t\t}\n\t\t\n\t\t_AddToDict(name);\n\t\t//print.it(b);\n\t}\n\t\n\tvoid _Delegate(TypeDef td, string name, in _TypeAttributes attr) {\n\t\tif (attr.cdeclDelegate) b.AppendLine(\"[UnmanagedFunctionPointer(CallingConvention.Cdecl)]\");\n\t\t\n\t\tb.Append(\"internal delegate \");\n\t\tvar md = td.Methods[1];\n\t\t//if (md is var md && md.Attributes is not (MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.VtableLayoutMask)) print.it(md.Attributes, td.Name);\n\t\t_Method2(name, md, false, true, 0, attr.bit32);\n\t\t_FunctionPointer(attr.cdeclDelegate, attr.bit32);\n\t\t\n\t\t_AddToDict(name);\n\t\t\n\t\t//appends `//delegate*<...>`.\n\t\tvoid _FunctionPointer(bool cdecl, bool bit32) {\n\t\t\tb.Append(\"\\r\\n//delegate* unmanaged\");\n\t\t\tif (cdecl) b.Append(\"[Cdecl]\");\n\t\t\tb.Append('<');\n\t\t\tstring sep = null;\n\t\t\tforeach (var p in md.Parameters) {\n\t\t\t\tif (!p.IsNormalMethodParameter) continue; //hidden `this` parameter\n\t\t\t\tb.Append(sep); sep = \", \";\n\t\t\t\tb.Append(_GetTN(p.Type, bit32, p.ParamDef));\n\t\t\t}\n\t\t\tif (md.HasReturnType) b.Append(\", \").Append(_GetTN(md.ReturnType, bit32, md)); else b.Append(\", void\");\n\t\t\tb.Append('>');\n\t\t\t\n\t\t\tstring _GetTN(TypeSig t, bool bit32, object def) {\n\t\t\t\tvar (s2, marshal, s) = _GetTypeName(t, out int ptr, false, (false, false), bit32, def);\n\t\t\t\t//if (marshal != null) print.it(s, s2, marshal, def.DebugToString());\n\t\t\t\tif (ptr > 0 && def is ParamDef pd) {\n\t\t\t\t\tbool isArray = pd.HasCustomAttributes && new _ParamAttributes(pd).isArrayPtr;\n\t\t\t\t\tif (!isArray) { ptr--; b.Append(!pd.IsOut ? \"in \" : pd.IsIn ? \"ref \" : \"out \"); }\n\t\t\t\t}\n\t\t\t\tif (ptr > 0) s = s.PadRight(s.Length + ptr, '*');\n\t\t\t\treturn s;\n\t\t\t}\n\t\t}\n\t}\n\t\n\tvoid _InterfaceMethod(MethodDef md, bool bit32) {\n\t\t//if (md.Attributes is not (MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.VtableLayoutMask | MethodAttributes.Abstract)) print.it(md.Attributes, md.Name, md.DeclaringType.Name);\n\t\tDebug.Assert(md.CallingConvention is 0 or dnlib.DotNet.CallingConvention.ThisCall);\n\t\t\n\t\tb.Start(1);\n\t\tint bStart = b.Length;\n\t\tstring name = md.Name;\n\t\t\n\t\tb.Append(\"[PreserveSig] \");\n\t\t\n\t\t_Method2(name, md, true, false, bStart, bit32);\n\t}\n\t\n\tvoid _Interface(TypeDef td, string name, in _TypeAttributes attr) {\n\t\tstring name0 = td.Name; //because name may be mangled\n\t\tbool isUnk = false, isDisp = false;\n\t\tstring bName = null, bMembers = null;\n\t\t\n\t\tif (!td.HasInterfaces) {\n\t\t\tif (name == \"IUnknown\") {\n\t\t\t\tb.Append($$\"\"\"\n[ComImport, Guid(\"{{attr.guid}}\"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\ninternal interface IUnknown {}\n\"\"\");\n\t\t\t\t_AddToDict(name);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\t//not a COM interface. Difficult to use in C#. Not many. Add anyway, without [ComImport].\n\t\t\t//print.it(name, attr.guid);\n\t\t\t//TODO2: declare as struct with vtbl. Eg IMemoryAllocator.\n\t\t} else {\n\t\t\t//if (td.Interfaces.Count != 1) print.it(name);\n\t\t\t//if (td.Attributes != (TypeAttributes.Public | TypeAttributes.ClassSemanticMask | TypeAttributes.Abstract)) print.it(td.Attributes);\n\t\t\t\n\t\t\tvar ii = td.Interfaces[0].Interface;\n\t\t\tbName = ii.Name;\n\t\t\tif (bName == \"IUnknown\") isUnk = true;\n\t\t\telse if (bName == \"IDispatch\") isDisp = true;\n\t\t\telse {\n\t\t\t\tstring nn = ii.Namespace + \".\" + bName;\n\t\t\t\tg1:\n\t\t\t\tif (_iBase.TryGetValue(nn, out var v)) {\n\t\t\t\t\t(bMembers, isUnk, isDisp, bName) = v;\n\t\t\t\t} else {\n\t\t\t\t\t//print.it(\"interface base\", name, bName);\n\t\t\t\t\t_Type(_types[nn], true);\n\t\t\t\t\tgoto g1;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (isUnk || isDisp) {\n\t\t\tb.AppendFormat(\"[ComImport, Guid(\\\"{0}\\\")\", attr.guid);\n\t\t\tif (!isDisp) b.Append(\", InterfaceType(ComInterfaceType.InterfaceIsIUnknown)\");\n\t\t\tb.AppendLine(\"]\");\n\t\t} else {\n\t\t\tif (attr.guid != null) b.AppendFormat(\"[Guid(\\\"{0}\\\")]\\r\\n\", attr.guid);\n\t\t}\n\t\t\n\t\tb.Append(\"internal interface \").Append(name);\n\t\tif (bMembers != null) b.Append(\" : \").Append(bName);\n\t\t\n\t\tb.Append(\" {\");\n\t\tint membersStart = b.Length;\n\t\t\n\t\tif (bMembers != null) {\n\t\t\tif (!bMembers.Starts(\"\\r\\n\\t//\")) b.Append(\"\\r\\n\\t// \").Append(bName);\n\t\t\tb.Append(bMembers.RxReplace(@\"(?m)^\\t\\[PreserveSig\\] \\K(?!new )\", \"new \"));\n\t\t\tb.Append(\"\\r\\n\\t// \").Append(name);\n\t\t}\n\t\t\n\t\tforeach (var md in td.Methods) {\n\t\t\t_InterfaceMethod(md, attr.bit32);\n\t\t}\n\t\t\n\t\tint membersLen = b.Length - membersStart;\n\t\t_iBase.Add(td.Namespace + \".\" + name0, (string.Create(membersLen, 0, (s, _) => b.CopyTo(membersStart, s, membersLen)), isUnk, isDisp, name));\n\t\t\n\t\tb.Append(b.Length > membersStart ? \"\\r\\n}\" : \" }\");\n\t\t_AddToDict(name);\n\t\t\n\t\t//print.it(b);\n\t\t//if (bMembers != null) print.it(b);\n\t}\n\t\n\tvoid _Typedef(TypeDef td) {\n\t\tstring name = td.Name;\n\t\t//print.it(name);\n\t\tstring r = null;\n\t\t\n\t\tif (name == \"HWND\") {\n\t\t\tr = \"wnd\";\n\t\t} else if (name is \"BOOL\" or \"BSTR\") {\n\t\t\treturn;\n\t\t} else {\n\t\t\tvar f = td.Fields[0];\n\t\t\tvar tn = f.FieldType.TypeName;\n\t\t\tr = tn switch {\n\t\t\t\t\"Void*\" or \"IntPtr\" or \"UIntPtr\" => \"nint\",\n\t\t\t\t\"Char*\" => \"char*\",\n\t\t\t\t\"Byte*\" => \"byte*\",\n\t\t\t\t_ => _SystemTypeNameToKeyword(tn)\n\t\t\t};\n\t\t\tif (r == null) {\n\t\t\t\tprint.warning(tn);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tDebug.Assert(r != \"bool\");\n\t\t}\n\t\t_typedefs.Add(name, r);\n\t}\n\t\n\tvoid _CoClass(TypeDef td, in _TypeAttributes attr) {\n\t\tDebug.Assert(!td.HasMethods && !td.HasNestedTypes);\n\t\tb.Clear();\n\t\tstring name = td.Name;\n\t\tb.Append($$\"\"\"\n[ComImport, Guid(\"{{attr.guid}}\"), ClassInterface(ClassInterfaceType.None)]\ninternal class {{name}} {}\n\"\"\");\n\t\t_AddToDict(name);\n\t}\n}\n"
  },
  {
    "path": "Scripts/AuDocsLib.cs",
    "content": "/*/\nrole classLibrary\noutputPath %folders.Workspace%\\dll\ntestInternal Au.Editor,Au,Microsoft.CodeAnalysis,Microsoft.CodeAnalysis.CSharp,Microsoft.CodeAnalysis.Features,Microsoft.CodeAnalysis.CSharp.Features,Microsoft.CodeAnalysis.Workspaces,Microsoft.CodeAnalysis.CSharp.Workspaces;\nr Au.Editor.dll;\nr Roslyn\\Microsoft.CodeAnalysis.dll;\nr Roslyn\\Microsoft.CodeAnalysis.CSharp.dll;\nr Roslyn\\Microsoft.CodeAnalysis.Features.dll;\nr Roslyn\\Microsoft.CodeAnalysis.CSharp.Features.dll;\nr Roslyn\\Microsoft.CodeAnalysis.Workspaces.dll /alias=CAW\nr Roslyn\\Microsoft.CodeAnalysis.CSharp.Workspaces.dll;\nnuget -\\Markdig;\n/*/\n\nextern alias CAW;\n\nusing Microsoft.CodeAnalysis;\nusing CAW::Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp;\nusing Microsoft.CodeAnalysis.Text;\nusing Microsoft.CodeAnalysis.CSharp.Syntax;\nusing Microsoft.CodeAnalysis.Shared.Extensions;\nusing Microsoft.CodeAnalysis.CSharp.Extensions;\nusing CAW::Microsoft.CodeAnalysis.Classification;\nusing CAW::Microsoft.CodeAnalysis.Shared.Extensions;\n\nusing System.Net;\nusing EStyle = LA.SciTheme.EStyle;\nusing Markdig;\n\nnamespace ADL;\n\n#pragma warning disable CS1591 //Missing XML comment for publicly visible type or member\n\n/// <summary>\n/// Used by the \"Au docs\" script.\n/// Also RecipeCodeToHtml used by the Read panel to create preview HTML of the current cookbook source script. In DEBUG mode only.\n/// </summary>\npublic static class AuDocsShared {\n\tpublic static Func<string, string> ResolveAuApiLink;\n\t\n\tpublic static void Test() {\n\t\tprint.clear();\n\t\t//var mdFile = folders.Editor + @\"..\\Cookbook\\files\\Filesystem\\Create folder.cs\";\n\t\tvar mdFile = folders.ThisAppBS + @\"..\\Cookbook\\files\\Filesystem\\Zip files (compress, extract).cs\";\n\t\tvar code = filesystem.loadText(mdFile);\n\t\tvar name = pathname.getNameNoExt(mdFile);\n\t\tvar html = RecipeCodeToHtml(name, code);\n\t\t//print.it(html);\n\t}\n\t\n\tstatic MarkdownPipeline _pipeline;\n\t\n\tpublic static string RecipeCodeToHtml(string name, string code) {\n\t\tif (_pipeline == null) {\n\t\t\tvar p = new MarkdownPipelineBuilder();\n\t\t\tp.BlockParsers.RemoveAll(o => o is Markdig.Parsers.IndentedCodeBlockParser);\n\t\t\tp.UsePipeTables();\n\t\t\t_pipeline = p.Build();\n\t\t}\n\t\t\n\t\tvar md = RecipeCodeToMd(name, code);\n\t\tvar s = Markdig.Markdown.ToHtml(md, _pipeline);\n\t\t//print.it(s);\n\t\ts = PostprocessHtmlNonApi(name, s);\n\t\treturn PostprocessHtmlCommon(name, s, isApi: false);\n\t}\n\t\n\tpublic static string RecipeCodeToMd(string name, string code, bool test = false) {\n\t\tvar b = new StringBuilder();\n\t\tb.Append(\"# \").AppendLine(name);\n\t\tstring usings;\n\t\tforeach (var (isText, s) in _ParseRecipe(name, code, out usings)) {\n\t\t\tif (isText) {\n\t\t\t\t//CONSIDER: markdown-escape (replace * with \\* etc).\n\t\t\t\t//\tNow escapes only in several tags, where noticed bad Type<T> etc.\n\t\t\t\t//\tOr will need to review each new recipe as webpage. All initial recipes are reviewed.\n\t\t\t\t//\tWhen reviewing, if something is bad, usually that text must be in <mono> (inline code) etc.\n\t\t\t\t\n\t\t\t\tif (test) print.it(s);\n\t\t\t\tb.AppendLine(_rxTag.Replace(s, _Repl));\n\t\t\t} else {\n\t\t\t\tb.AppendLine();\n\t\t\t\tb.AppendLine(\"```csharp\");\n\t\t\t\tb.Append(s);\n\t\t\t\tif (!s.Ends('\\n')) b.AppendLine();\n\t\t\t\tb.AppendLine(\"```\");\n\t\t\t\tb.AppendLine();\n\t\t\t}\n\t\t}\n\t\t\n\t\treturn b.ToString();\n\t\t\n\t\tstring _Repl(RXMatch m) {\n\t\t\tif (test) print.it(m);\n\t\t\tstring tag = m[1].Value, s = m[3].Value;\n\t\t\t\n\t\t\tif (tag == \"_\") return _MarkdownEscape(s); //raw text\n\t\t\t\n\t\t\tif (tag is \".k\" or \".x\" or \".c\" or \"mono\") {\n\t\t\t\tDebug_.PrintIf(s.Contains('<') && m.Value.Ends(\"<>\")); //if contains <, must end with </.c> etc\n\t\t\t\tDebug_.PrintIf(s.Contains('\\n'), s);\n\t\t\t}\n\t\t\t\n\t\t\tbool onlyRawText = s.Like(\"<_>*</_>\");\n\t\t\tif (onlyRawText) s = _MarkdownEscape(s[3..^4]);\n\t\t\telse if (s.Contains('<')) s = _rxTag.Replace(s, _Repl);\n\t\t\t\n\t\t\t//non-link tags\n\t\t\tswitch (tag) {\n\t\t\tcase \"b\" or \"i\" or \"u\":\n\t\t\t\treturn $\"<{tag}>{s}</{tag}>\";\n\t\t\tcase \"bi\":\n\t\t\t\treturn $\"<b><i>{s}</i></b>\";\n\t\t\tcase \".k\": //C# keyword\n\t\t\t\treturn $\"<code style='color:#00f'>{s}</code>\";\n\t\t\tcase \".x\": //API name\n\t\t\t\treturn $\"<code style='color:#e06060'>{s}</code>\";\n\t\t\tcase \"+nuget\":\n\t\t\t\treturn $\"<span style='color:#080;text-decoration:underline' title='Paste the underlined text in menu > Tools > NuGet'>{s}</span>\";\n\t\t\tcase \"open\":\n\t\t\t\treturn $\"<code>{s}</code>\";\n\t\t\tcase \"c\" or \".c\" or \"mono\":\n\t\t\t\tthrow new ArgumentException(\"Don't use tags c, .c, mono. Use `code`. \" + m);\n\t\t\t}\n\t\t\t\n\t\t\tvar attr = m[2].Value;\n\t\t\tif (attr == null) attr = s;\n\t\t\telse {\n\t\t\t\tif (attr is ['\\'', .., '\\''] or ['\"', .., '\"']) attr = attr[1..^1];\n\t\t\t\tDebug_.PrintIf(attr.Contains('|'));\n\t\t\t}\n\t\t\t\n\t\t\t//links\n\t\t\tswitch (tag) {\n\t\t\tcase \"help\":\n\t\t\t\t//print.it(name, attr);\n\t\t\t\tif (attr.Contains('/')) { //info: <help> is used only for \"articles\\x\" and \"editor\\x\"\n\t\t\t\t\tif (!filesystem.exists(folders.ThisAppBS + @\"..\\Other\\DocFX\\_doc\\\" + attr + \".md\")) break;\n\t\t\t\t\treturn $\"<a href=\\\"/{UrlEscapePath(attr)}.html\\\">{s}</a>\";\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \"link\":\n\t\t\t\tif (!attr.Starts(\"http\")) return s; //eg %folders.Workspace%\n\t\t\t\treturn $\"<a href=\\\"{attr}\\\">{s}</a>\";\n\t\t\tcase \"google\":\n\t\t\t\treturn $\"<a href=\\\"https://www.google.com/search?q={System.Net.WebUtility.UrlEncode(attr)}\\\">{s}</a>\";\n\t\t\tcase \"+lang\":\n\t\t\t\tattr += \", C# reference\";\n\t\t\t\tgoto case \"google\";\n\t\t\tcase \"+ms\":\n\t\t\t\tattr += \" site:microsoft.com\";\n\t\t\t\tgoto case \"google\";\n\t\t\tcase \"+recipe\":\n\t\t\t\tif (_FindRecipe(attr) is string rec) return $\"<a href=\\\"/cookbook/{Uri.EscapeDataString(rec)}\\\">{s}</a>\";\n\t\t\t\tbreak;\n\t\t\tcase \"+see\": //was <see cref=\"attr\"/>, now <+see 'attr'>attr<>. Then could not replace with <a> because usings still were not collected from codes.\n\t\t\t\tif (_GetSeeUrl(attr, usings, out bool isAu) is string url2) {\n\t\t\t\t\tif (isAu) url2 = $\"/api/{url2}.html\";\n\t\t\t\t\tif (!onlyRawText) s = _MarkdownEscape(s);\n\t\t\t\t\treturn $\"<a href=\\\"{url2}\\\">{s}</a>\";\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t\n\t\t\tprint.it($\"<>{name}: <c red>{tag}, {attr}<>\");\n\t\t\treturn s;\n\t\t}\n\t\t\n\t\tstring _MarkdownEscape(string s) => _rxEscape.Replace(s, @\"\\$0\");\n\t}\n\t\n\tstatic regexp _rxEscape = new(@\"[\\!\\#\\$\\%\\&\\'\\(\\)\\*\\+\\-\\/\\:\\<\\=\\>\\?\\@\\[\\\\\\]\\^\\_\\`\\{\\|\\}\\~]\");\n\tstatic regexp _rxTag = new(@\"<([\\+\\.]?\\w+)(?: ([^>\\r\\n]+))?>((?:[^<]++|(?R)|<\\w+(?:, \\w+)*>)*)<(?:/\\1)?>\");\n\tconst string c_website = \"https://www.libreautomate.com\";\n\t\n\tstatic string _FindRecipe(string s) {\n\t\tif (_aFR == null) {\n\t\t\tvar xr = XmlUtil.LoadElem(folders.ThisAppBS + @\"..\\Cookbook\\files.xml\");\n\t\t\t_aFR = xr.Descendants(\"s\").Select(x => {\n\t\t\t\tvar name = x.Attr(\"n\")[..^3];\n\t\t\t\treturn (name, name + \".html\");\n\t\t\t}).ToArray();\n\t\t}\n\t\tforeach (var v in _aFR) if (v.name.Like(s, true)) return v.filenameHtml;\n\t\tforeach (var v in _aFR) if (v.name.Starts(s, true)) return v.filenameHtml;\n\t\tforeach (var v in _aFR) if (v.name.Find(s, true) >= 0) return v.filenameHtml;\n\t\tprint.warning(\"recipe not found: \" + s);\n\t\treturn null;\n\t\t//see PanelHelp._FindRecipe.\n\t}\n\tstatic (string name, string filenameHtml)[] _aFR;\n\t\n\tpublic static string _GetSeeUrl(string s, string usings, out bool isAu) {\n\t\tisAu = false;\n\t\t//add same namespaces as in default global.cs. Don't include global.cs because it may be modified.\n\t\tstring code = usings + $\"///<see cref='{s}'/>\";\n\t\tusing var ws = new AdhocWorkspace();\n\t\tvar document = LA.CiUtil.CreateDocumentFromCode(ws, code, needSemantic: true);\n\t\tvar syn = document.GetSyntaxRootSynchronously(default);\n\t\tvar node = syn.FindToken(code.Length - 3 - s.Length, true).Parent.FirstAncestorOrSelf<CrefSyntax>();\n\t\tif (node != null) {\n\t\t\tvar semo = document.GetSemanticModelAsync().Result_();\n\t\t\tif (semo.GetSymbolInfo(node).GetAnySymbol() is ISymbol sym)\n\t\t\t\treturn LA.CiUtil.GetSymbolHelpUrl(sym, out isAu);\n\t\t}\n\t\treturn null;\n\t}\n\t\n\t/// <summary>\n\t/// Splits a recipe source code into text and code parts.\n\t/// From text parts removes /// and replaces 'see' with '+see'.\n\t/// </summary>\n\t/// <param name=\"code\">C# code.</param>\n\t/// <param name=\"usings\">null or all using directives found in all codes.</param>\n\tstatic List<(bool isText, string s)> _ParseRecipe(string name, string code, out string usings) {\n\t\t//rejected:\n\t\t//\t1. Ignore code before the first ///. Not really useful, just forces to always start with ///.\n\t\t//\t2. Use {  } for scopes of variables. Its' better to use unique names.\n\t\t//\t3. Use if(...) {  } to enclose code examples to select which code to test.\n\t\t//\t\tCan accidentally damage real 'if' code. I didn't use it; it's better to test codes in other script.\n\t\t\n\t\tList<(bool isText, string s)> r = new();\n\t\tStringBuilder sbUsings = null;\n\t\tvar ac = new List<(string code, int offset8, int len8)>();\n\t\tint iCode = 0;\n\t\tforeach (var m in code.RxFindAll(@\"(?ms)^(?:///(?!=/)\\N*\\R*)+|^/\\*\\*.+?\\*/\\R*\")) {\n\t\t\t//print.it(\"--------\");\n\t\t\t//print.it(m);\n\t\t\tif (code.Eq(m.Start, \"/// <summary>\")) continue;\n\t\t\tint textTo = m.End, i = code.Find(\"\\n/// <summary>\", m.Start..m.End); if (i >= 0) textTo = i + 1;\n\t\t\t\n\t\t\t_Code(iCode, m.Start);\n\t\t\t_Text(m.Start, iCode = textTo);\n\t\t}\n\t\t_Code(iCode, code.Length);\n\t\tusings = sbUsings?.ToString();\n\t\treturn r;\n\t\t\n\t\tvoid _Text(int start, int end) {\n\t\t\twhile (code[end - 1] <= ' ') end--;\n\t\t\tbool ml = code[start + 1] == '*';\n\t\t\tif (ml) {\n\t\t\t\tstart += 3; while (code[start] <= ' ') start++;\n\t\t\t\tend -= 2; while (end > start && code[end - 1] <= ' ') end--;\n\t\t\t}\n\t\t\tvar s = code[start..end];\n\t\t\tif (!ml) s = s.RxReplace(@\"(?m)^/// ?\", \"\");\n\t\t\ts = s.RxReplace(@\"<see cref=\"\"(.+?)\"\"(?:/|>(.+?)<[^>]*)>\", m => {\n\t\t\t\tstring v = m[1].Value, t = v;\n\t\t\t\tif (m[2].Exists) t = m[2].Value;\n\t\t\t\telse if (t.Contains('{')) t = \"<_>\" + t.Replace('{', '<').Replace('}', '>') + \"</_>\";\n\t\t\t\t//if (m[2].Exists) print.it(name, v, t);\n\t\t\t\treturn $\"<+see '{v}'>{t}<>\";\n\t\t\t});\n\t\t\t//print.it(\"TEXT\"); print.it(s);\n\t\t\tr.Add((true, s));\n\t\t}\n\t\t\n\t\tvoid _Code(int start, int end) {\n\t\t\twhile (end > start && code[end - 1] <= ' ') end--;\n\t\t\tif (end == start) return;\n\t\t\tvar s = code[start..end];\n\t\t\t//print.it(\"CODE\"); print.it(s);\n\t\t\tr.Add((false, s));\n\t\t\t\n\t\t\tforeach (var m in s.RxFindAll(@\"(?m)^using [\\w\\.]+;\")) {\n\t\t\t\t(sbUsings ??= new()).AppendLine(m.Value);\n\t\t\t}\n\t\t}\n\t}\n\t\n\tpublic static string PostprocessHtmlNonApi(string name, string s) {\n\t\tint nr;\n\t\t//in .md we use this for links to our API: [Class]() or [Class.Member]().\n\t\t//\tDocFX converts it to <a href=\"\">Class</a> etc without warning.\n\t\t//\tNow convert it to a working link.\n\t\tnr = s.RxReplace(@\"<a href=\"\"\"\">(.+?)</a>\", m => {\n\t\t\tvar k = m[1].Value;\n\t\t\tstring href = ResolveAuApiLink(k);\n\t\t\tif (href == null && k.LastIndexOf('.') is var i && i > 0) href = ResolveAuApiLink(k[..i]); //enum member\n\t\t\tif (href == null) { print.it($\"cannot resolve link: [{k}]()\"); return m.Value; }\n\t\t\treturn $@\"<a href=\"\"{href}\"\">{k}</a>\";\n\t\t}, out s);\n\t\t\n\t\t//in .md we use this for Google search links: [text](google:) or [text](google:urlencoded+google+query).\n\t\t//\tTo search only in microsoft.com (.NET/Windows API etc): [text](ms:) or [text](ms:urlencoded+google+query).\n\t\t//\tThe colon is to avoid DocFX warnings.\n\t\t//\tNow convert it to a google search link.\n\t\tnr = s.RxReplace(@\"<a href=\"\"(ms|google):([^\"\"]+)?\"\">(.+?)</a>\", m => {\n\t\t\tstring linkText = m[3].Value;\n\t\t\tstring q = m[2].Value ?? WebUtility.UrlEncode(WebUtility.HtmlDecode(linkText));\n\t\t\tstring site = m.Subject[m[1].Start] is 'm' ? \"site:microsoft.com+\" : null;\n\t\t\treturn $@\"<a href=\"\"https://www.google.com/search?q={site}{q}\"\">{linkText}</a>\";\n\t\t}, out s);\n\t\tif (s.Contains(\"<google>\")) print.warning(\"<google> in .md files not supported. Use [text](google:) or [text](google:urlencoded+google+query)\");\n\t\tif (s.Contains(\"<ms>\")) print.warning(\"<ms> in .md files not supported. Use [text](ms:) or [text](ms:urlencoded+google+query)\");\n\t\t\n\t\treturn s;\n\t}\n\t\n\tpublic static string PostprocessHtmlCommon(string name, string s, bool isApi) {\n\t\tint nr;\n\t\t//javascript renderTables() replacement, to avoid it at run time. Also remove class table-striped.\n\t\tnr = s.RxReplace(@\"(?s)<table(>.+?</table>)\", @\"<div class=\"\"table-responsive\"\"><table class=\"\"table table-bordered table-condensed\"\"$1</div>\", out s);\n\t\t\n\t\t//the same for renderAlerts\n\t\tnr = s.RxReplace(@\"<div class=\"\"(NOTE|TIP|WARNING|IMPORTANT|CAUTION)\\b\",\n\t\t\to => {\n\t\t\t\tstring k = \"info\"; switch (o[1].Value[0]) { case 'W': k = \"warning\"; break; case 'I': case 'C': k = \"danger\"; break; }\n\t\t\t\treturn o.Value + \" alert alert-\" + k;\n\t\t\t},\n\t\t\tout s);\n\t\t\n\t\tnr = s.RxReplace(@\"<p>\\s+\", \"<p>\", out s); //<p>\\n makes new line before. This is in notes only.\n\t\t\n\t\t_rxCss ??= new(\"\"\"(?m)(\\h*)(\\Q<link rel=\"stylesheet\" href=\"../styles/main.css\">\\E)\"\"\");\n\t\t//if(!_rxCss.IsMatch(s)) print.it(f.Name);\n\t\ts = _rxCss.Replace(s, \"$1$2\\n$1<link rel=\\\"stylesheet\\\" href=\\\"../styles/code.css\\\">\", 1);\n\t\t\n\t\t_rxCode2 ??= new(\"\"\"(?s)<code class=\"lang-(?: hljs|csharp)\">(.+?)</code>\"\"\");\n\t\ts = _rxCode2.Replace(s, m => _Code(m[1].Value, isApi ? 1 : 2)); //syntax in api, and ```code``` in conceptual\n\t\t\n\t\tif (isApi) {\n\t\t\t_rxCode ??= new(\"\"\"(?<=<pre>)%%(.+?)%%(?=</pre>)\"\"\");\n\t\t\ts = _rxCode.Replace(s, m => _Code(m[1].Value, 0)); //<code> in api\n\t\t}\n\t\t\n\t\treturn s;\n\t}\n\t\n\tstatic regexp _rxCode, _rxCode2, _rxCss;\n\t\n\tpublic static string CreateCodeCss() {\n\t\tvar s = LA.SciTheme.Default;\n\t\tvar b = new StringBuilder();\n\t\t\n\t\t_Style(\"c\", s.Comment);\n\t\t_Style(\"const\", s.Constant);\n\t\t_Style(\"ex\", s.Excluded);\n\t\t_Style(\"f\", s.Function);\n\t\t_Style(\"k\", s.Keyword);\n\t\t_Style(\"goto\", s.Label);\n\t\t_Style(\"v\", s.LocalVariable);\n\t\t_Style(\"ns\", s.Namespace);\n\t\t_Style(\"n\", s.Number);\n\t\t_Style(\"o\", s.Operator);\n\t\t_Style(\"pre\", s.Preprocessor);\n\t\t_Style(\"p\", s.Punctuation);\n\t\t_Style(\"s\", s.String);\n\t\t_Style(\"se\", s.StringEscape);\n\t\t_Style(\"t\", s.Type);\n\t\t_Style(\"x1\", s.XmlDocTag);\n\t\t_Style(\"x2\", s.XmlDocText);\n\t\t\n\t\tvoid _Style(string name, LA.SciTheme.TStyle k) {\n\t\t\tb.AppendFormat(\"pre span.{0}{{color:#{1:X6};\", name, k.color);\n\t\t\tif (k.bold) b.Append(\"font-weight: bold;\");\n\t\t\tb.AppendLine(\"}\");\n\t\t}\n\t\t\n\t\tb.AppendLine(\"\"\"\n\nsummary { display: list-item; }\ndetails { margin-bottom: 10px; }\n\"\"\");\n\t\t\n\t\treturn b.ToString();\n\t}\n\t\n\t//note: runs in parallel threads.\n\t//where: 0 api <code>, 1 api syntax, 2 non-api\n\tstatic string _Code(string s, int where) {\n\t\tif (where == 0) {\n\t\t\ts = Encoding.UTF8.GetString(Convert.FromBase64String(s));\n\t\t} else {\n\t\t\ts = System.Net.WebUtility.HtmlDecode(s); //eg &lt; in generic parameters\n\t\t\t\n\t\t\t//remove/fix something in parameters\n\t\t\tif (where == 1) {\n\t\t\t\ts = s.RxReplace(@\"\\(\\w+\\)0\", \"0\"); //(Enum)0 => 0\n\t\t\t\ts = s.RxReplace(@\"\\bdefault\\([^)?]+\\? *\\)\", \"null\"); //default(Nullable?) => null\n\t\t\t\ts = s.RxReplace(@\"\\bdefault\\(.+?\\)\", \"default\"); //default(Struct) => default\n\t\t\t\ts = s.RxReplace(@\"\\[ParamString\\(PSFormat\\.\\w+\\)\\] \", \"\");\n\t\t\t\ts = s.RxReplace(@\" ?\\*(?=\\w)\", \"* \");\n\t\t\t}\n\t\t}\n\t\t\n\t\tusing var ws = new AdhocWorkspace();\n\t\tvar document = LA.CiUtil.CreateDocumentFromCode(ws, s, needSemantic: true);\n\t\t//var semo = document.GetSemanticModelAsync().Result;\n\t\t\n\t\t//at first set byte[] styles.\n\t\t//\tCan't format text directly because GetClassifiedSpansAsync results may be overlapped, eg at first entire string and then its escape sequences.\n\t\t//\tAnd it simplifies formatting.\n\t\tvar a = new byte[s.Length];\n\t\tint prevEnd = 0; EStyle prevStyle = 0;\n\t\tforeach (var v in Classifier.GetClassifiedSpansAsync(document, TextSpan.FromBounds(0, s.Length)).Result) {\n\t\t\tvar ct = v.ClassificationType;\n\t\t\tif (ct == ClassificationTypeNames.StaticSymbol) continue;\n\t\t\tEStyle style = LA.CiStyling.StyleFromClassifiedSpan(v);\n\t\t\tint start = v.TextSpan.Start, end = v.TextSpan.End;\n\t\t\t//print.it(style, s[start..end]);\n\t\t\tif (style == prevStyle && start > prevEnd && a[prevEnd] == 0) start = prevEnd; //join adjacent styles separated by whitespace\n\t\t\tprevEnd = end; prevStyle = style;\n\t\t\ta.AsSpan(start..end).Fill((byte)style);\n\t\t}\n\t\t\n\t\tvar b = new StringBuilder(\"<code>\"); //<code> isn't necessary; it disables Google translate (or would need attribute class=\"notranslate\" or translate=\"no\") and maybe better in some other cases I don't know\n\t\tfor (int i = 0; i < a.Length;) {\n\t\t\tint start = i; byte u = a[i]; while (i < a.Length && a[i] == u) i++;\n\t\t\tstring text = System.Net.WebUtility.HtmlEncode(s[start..i]);\n\t\t\tif (u == 0) {\n\t\t\t\tb.Append(text);\n\t\t\t} else {\n\t\t\t\tvar k = (EStyle)u switch {\n\t\t\t\t\tEStyle.Comment => \"c\",\n\t\t\t\t\tEStyle.Constant => \"const\",\n\t\t\t\t\tEStyle.Excluded => \"ex\",\n\t\t\t\t\tEStyle.Function or EStyle.Event => \"f\",\n\t\t\t\t\tEStyle.Keyword => \"k\",\n\t\t\t\t\tEStyle.Label => \"goto\", //not \"label\", it is used in DocFX CSS\n\t\t\t\t\tEStyle.LocalVariable or EStyle.Field => \"v\",\n\t\t\t\t\tEStyle.Namespace => \"ns\",\n\t\t\t\t\tEStyle.Number => \"n\",\n\t\t\t\t\tEStyle.Operator => \"o\",\n\t\t\t\t\tEStyle.Preprocessor => \"pre\",\n\t\t\t\t\tEStyle.Punctuation => \"p\",\n\t\t\t\t\tEStyle.String => \"s\",\n\t\t\t\t\tEStyle.StringEscape => \"se\",\n\t\t\t\t\tEStyle.Type => \"t\",\n\t\t\t\t\tEStyle.XmlDocTag => \"x1\",\n\t\t\t\t\tEStyle.XmlDocText => \"x2\",\n\t\t\t\t\t_ => null,\n\t\t\t\t};\n\t\t\t\tb.AppendFormat(\"<span class=\\\"{0}\\\">\", k);\n\t\t\t\tb.Append(text);\n\t\t\t\tb.Append(\"</span>\");\n\t\t\t}\n\t\t}\n\t\ts = b.Append(\"</code>\").ToString();\n\t\t\n\t\t//print.it(\"--------------\");\n\t\t//print.it(s);\n\t\ttry { System.Xml.Linq.XElement.Parse(\"<x>\" + s + \"</x>\"); }\n\t\tcatch (Exception e1) { print.warning(e1.ToStringWithoutStack()); print.it(s); }\n\t\t\n\t\treturn s;\n\t}\n\t\n\t//static CSharpSemanticModel _CreateSemanticModelForCode(string code) {\n\t//\tvar trees = new CSharpSyntaxTree[] {\n\t//\t\tCSharpSyntaxTree.ParseText(code, s_parseOpt) as CSharpSyntaxTree,\n\t//\t\tCSharpSyntaxTree.ParseText(CiUtil.c_globalUsingsText, s_parseOpt) as CSharpSyntaxTree\n\t//\t};\n\t//\tvar compilation = CSharpCompilation.Create(\"doc\", trees, s_refs, s_compOpt);\n\t//\treturn compilation.GetSemanticModel(trees[0]) as CSharpSemanticModel;\n\t//}\n\t\n\tpublic static string UrlEscapePath(string s) {\n\t\ts = s.Replace('\\\\', '/');\n\t\tif (!s.Contains('/')) return Uri.EscapeDataString(s);\n\t\treturn string.Join('/', s.Split('/').Select(o => Uri.EscapeDataString(o)));\n\t}\n}\n"
  },
  {
    "path": "Scripts/Create NuGet package.cs",
    "content": "/// 1. Clones Au.csproj to a temp file, and modifies the clone: adds more target frameworks, updates version, etc.\n/// 2. Deletes obj folder, builds the clone project and creates nuget package.\n/// 3. Adds the native dlls to the package.\n/// 4. Prints links.\n\nusing System.Xml.Linq;\nusing System.Xml.XPath;\n\nprint.clear();\nvar auDir = @\"C:\\code\\au\\Au\";\nvar auProj_nuget = auDir + @\"\\+Au.csproj\";\n\nvar x = XElement.Load(auDir + @\"\\Au.csproj\");\nvar tf = x.XPathSelectElement(\"/PropertyGroup/TargetFramework\");\ntf.ReplaceWith(new XElement(\"TargetFrameworks\", tf.Value + \";net8.0-windows\"));\n//tf.ReplaceWith(new XElement(\"TargetFrameworks\", tf.Value + \";net8.0-windows;net6.0-windows\")); //rejected. Too many places in code use API that are unavailable in .NET 6. And .NET 6 LTS ended. If somebody needs it, can find an older version of the library on NuGet.\nx.XPathSelectElement(\"/PropertyGroup/Version\").Value = typeof(osVersion).Assembly.GetName().Version.ToString(3);\nx.XPathSelectElement(\"/PropertyGroup/Copyright\").Value = $\"Copyright (c) Gintaras Didžgalvis {DateTime.Now.Year}\";\n//x.XPathSelectElement(\"/Target[@Name='PreBuild']/Exec\").Remove();\n//print.it(x);\n//return;\nfilesystem.saveText(auProj_nuget, x.ToString()); //not x.Save, it adds xml decl\n\nvar od = @\"C:\\code\\au\\Au\\bin\\Release\";\nfilesystem.createDirectory(od);\nforeach (var f in filesystem.enumFiles(od, \"LibreAutomate.*.nupkg\")) filesystem.delete(f.FullPath); //dotnet pack does nothing if the nupkg file exists\nfilesystem.delete(auDir + @\"\\obj\");\nint r = run.console(out var s, \"dotnet.exe\", $@\"pack \"\"{auProj_nuget}\"\" -o {od} -c Release --nologo\", auDir); //builds the clone project and creates nuget package\nprint.it(s);\n//return;\nif (r != 0) return;\n\nfilesystem.delete(auProj_nuget);\nfilesystem.delete(auDir + @\"\\obj\");\nfilesystem.delete(auDir + @\"\\bin\\Release\\net9.0-windows\");\nfilesystem.delete(auDir + @\"\\bin\\Release\\net8.0-windows\");\n//filesystem.delete(auDir + @\"\\bin\\Release\\net6.0-windows\");\n\ns.RxMatch(@\"Successfully created package '(.+?)'\", 1, out string path);\nif (!filesystem.exists(path)) throw null;\n\nusing var za = ZipFile.Open(path, ZipArchiveMode.Update);\nza.CreateEntryFromFile(@\"C:\\code\\au\\_\\64\\AuCpp.dll\", @\"runtimes\\win-x64\\native\\AuCpp.dll\");\nza.CreateEntryFromFile(@\"C:\\code\\au\\_\\64\\ARM\\AuCpp.dll\", @\"runtimes\\win-arm64\\native\\AuCpp.dll\");\nza.CreateEntryFromFile(@\"C:\\code\\au\\_\\32\\AuCpp.dll\", @\"runtimes\\win-x86\\native\\AuCpp.dll\");\nza.CreateEntryFromFile(@\"C:\\code\\au\\_\\64\\Au.DllHost.exe\", @\"runtimes\\win-x64\\native\\Au.DllHost.exe\");\nza.CreateEntryFromFile(@\"C:\\code\\au\\_\\64\\ARM\\Au.DllHost.exe\", @\"runtimes\\win-arm64\\native\\Au.DllHost.exe\");\nza.CreateEntryFromFile(@\"C:\\code\\au\\_\\32\\Au.DllHost.exe\", @\"runtimes\\win-x86\\native\\Au.DllHost.exe\");\n\nprint.it($\"<><explore>{path}<>\");\nprint.it($\"<><link>https://www.nuget.org/packages/manage/upload<>\");\n"
  },
  {
    "path": "Scripts/LA docs for AI/AI summaries.cs",
    "content": "/// Creates/updates summaries in doc-ai.db. They will be used in semantic search, to improve results.\n/// How: Takes texts from doc-ai.db, generates summaries using Gemini chat model, and stores in an intermediate DB. Finally copies the summaries to doc-ai.db.\n/// Generates only for articles where the summary is missing or need to update. Skips short articles.\n\n/*/\ndefine SCRIPT\ntestInternal Au,Au.Editor\nc AI search.cs;\nc AiModel.cs;\nc Ed util shared.cs;\nc AI script common.cs;\n/*/\n\nusing System.Security.Cryptography;\nusing System.Text.Json;\nusing AI;\n\n//print.clear();\nAiModel.ApiKeys = App.Settings.ai_ak;\n\n#if !true\nUserDefinedAiModels.Add();\n//var model = new ModelGeminiChat(\"gemini-2.5-flash\"); //good, compact; 3-10 s; 30/250 cents\nvar model = new ModelGeminiChat(\"gemini-2.5-flash-lite\"); //good, compact, but often too compact, especially for non-API; 1.5 s; 10/40 cents\n\n//var model = new ModelOpenaiChat(\"gpt-5-nano\"); //\n//var model = new ModelOpenaiChat(\"gpt-5-nano\") { reasoning = \"minimal\" }; //good, less compact, sometimes too many details; 3-6 s; 5/40 cents\n//var model = new ModelOpenaiChat(\"gpt-5-mini\"); //\n//var model = new ModelOpenaiChat(\"gpt-5-mini\") { reasoning = \"minimal\" }; //good, less compact, sometimes too many details; 3-7 s; 25/200 cents\n//var model = new ModelOpenaiChat(\"gpt-5\"); //\n//var model = new ModelOpenaiChat(\"gpt-4.1-nano\"); //good format but sometimes with some understanding errors; 3 s; 10/40 cents\n//var model = new ModelOpenaiChat(\"gpt-4.1-mini\"); //good; 3 s; 40/160 cents\n\n//var model = new ModelDeepseekChat(); //good, compact; 7.5 s\n\n//var model = new ModelClaudeChat(\"claude-sonnet-4-0\"); //good but minimal count; 3.5 s; with \"prefer more\" instruction bad (all) and slow\n//var model = new ModelClaudeChat(\"claude-opus-4-1\"); //bad; 9 s, sometimes hangs\n\n//var model = new ModelMistralChat(\"mistral-medium-latest\"); //like gpt-5-nano but even more details, 10 s\n\nperf.first();\n_Test();\nperf.nw();\n//print.scrollToTop();\n\nvoid _Test() {\n\tstring s;\n\t\n\ts = @\"C:\\Temp\\Au\\markdown\\articles\\[articles] About the automation library (files, namespaces).md\";\n\ts = @\"C:\\Temp\\Au\\markdown\\articles\\[articles] UI element issues.md\";\n\ts = @\"C:\\Temp\\Au\\markdown\\articles\\[articles] UAC.md\";\n\ts = @\"C:\\Temp\\Au\\markdown\\articles\\[articles] Key names and operators.md\";\n\ts = @\"C:\\Temp\\Au\\markdown\\articles\\[articles] Wildcard expression.md\";\n\ts = @\"C:\\Temp\\Au\\markdown\\articles\\[articles] Output tags.md\";\n\t//s = @\"\";\n\t\n\ts = @\"C:\\Temp\\Au\\markdown\\editor\\[editor] Code editor.md\";\n\ts = @\"C:\\Temp\\Au\\markdown\\editor\\[editor] Command line.md\";\n\ts = @\"C:\\Temp\\Au\\markdown\\editor\\[editor] Class files, projects.md\";\n\ts = @\"C:\\Temp\\Au\\markdown\\editor\\[editor] Compared with QM.md\";\n\ts = @\"C:\\Temp\\Au\\markdown\\editor\\[editor] Debugger.md\";\n\ts = @\"C:\\Temp\\Au\\markdown\\editor\\[editor] File properties.md\";\n\t//s = @\"\";\n\t\n\ts = @\"C:\\Temp\\Au\\markdown\\cookbook\\[cookbook] Autotext triggers, expand text.md\";\n\ts = @\"C:\\Temp\\Au\\markdown\\cookbook\\[cookbook] Callback functions, lambda, delegate, event.md\";\n\ts = @\"C:\\Temp\\Au\\markdown\\cookbook\\[cookbook] Clipboard copy, paste, set-get text etc.md\";\n\ts = @\"C:\\Temp\\Au\\markdown\\cookbook\\[cookbook] Dialog - add elements, show, get values.md\";\n\ts = @\"C:\\Temp\\Au\\markdown\\cookbook\\[cookbook] Http post web form, JSON.md\";\n\t//s = @\"\";\n\t\n\ts = @\"C:\\Temp\\Au\\markdown\\api\\Au\\dialog\\Au.dialog.show.md\";\n\ts = @\"C:\\Temp\\Au\\markdown\\api\\Au\\dialog\\Au.dialog.md\";\n\ts = @\"C:\\Temp\\Au\\markdown\\api\\Au\\clipboard\\Au.clipboard.copyData.md\";\n\ts = @\"C:\\Temp\\Au\\markdown\\api\\Au\\clipboard\\Au.clipboard.paste.md\";\n\ts = @\"C:\\Temp\\Au\\markdown\\api\\Au\\clipboard\\Au.clipboard.text.md\";\n\ts = @\"C:\\Temp\\Au\\markdown\\api\\Au\\clipboard\\Au.clipboard.clear.md\";\n\ts = @\"C:\\Temp\\Au\\markdown\\api\\Au\\clipboard\\Au.clipboard.md\";\n\ts = @\"C:\\Temp\\Au\\markdown\\api\\Au\\consoleProcess\\Au.consoleProcess.consoleProcess.md\";\n\ts = @\"C:\\Temp\\Au\\markdown\\api\\Au\\consoleProcess\\Au.consoleProcess.Prompt.md\";\n\ts = @\"C:\\Temp\\Au\\markdown\\api\\Au\\mouse\\Au.mouse.click.md\";\n\ts = @\"C:\\Temp\\Au\\markdown\\api\\Au\\keys\\Au.keys.send.md\";\n\t//s = @\"\";\n\t\n\tstring name = pathname.getNameNoExt(s);\n\tstring text = filesystem.loadText(s);\n\t\n\t_ProcessText(ref text);\n\t_ShowTextInOutput2(text);\n\tif (text.Length < 400) {\n\t\tprint.it(\"SKIPPED\", text.Length, text);\n\t\treturn;\n\t}\n\t\n\ts = _GenerateSummary(name, text, model, true);\n\tprint.it(s);\n}\n\n#else\n\nvar model = new ModelGeminiChat(\"gemini-2.5-flash-lite\");\n\nvar dbFile = folders.ThisAppBS + \"doc-ai.db\";\nusing (var dbDoc = new sqlite(folders.ThisApp + \"doc-ai.db\")) {\n\tusing var staDocSelect = dbDoc.Statement(\"SELECT name,text FROM doc\");\n\t\n\tstring tempDbPath = folders.LocalAppData + @\"LibreAutomate\\_dev\\doc-ai-summary.db\";\n\tusing var dbTemp = new sqlite(tempDbPath);\n\tdbTemp.Execute(\"CREATE TABLE IF NOT EXISTS doc (name TEXT PRIMARY KEY, summary TEXT, hash BLOB)\");\n\tusing var staTempInsert = dbTemp.Statement(\"INSERT OR REPLACE INTO doc VALUES (?, ?, ?)\");\n\tusing var transTemp = dbTemp.Transaction(sqlOfDispose: \"COMMIT\");\n\t\n\tDictionary<string, byte[]> dHash = [];\n\tusing (var staTempSelectHash = dbTemp.Statement(\"SELECT name,hash FROM doc\")) {\n\t\twhile (staTempSelectHash.Step()) {\n\t\t\tdHash.Add(staTempSelectHash.GetText(0), staTempSelectHash.GetArray<byte>(1));\n\t\t}\n\t}\n\t\n\twhile (staDocSelect.Step()) {\n\t\tstring name = staDocSelect.GetText(0), text = staDocSelect.GetText(1);\n\t\t\n\t\t_ProcessText(ref text);\n\t\tif (text.Length < 400) {\n\t\t\t//print.it(\"SKIPPED\", text.Length, text);\n\t\t\tcontinue;\n\t\t}\n\t\t\n\t\t//summary exists and is up to date?\n\t\tvar hash = SHA256.HashData(text.ToUTF8());\n\t\tif (dHash.TryGetValue(name, out var hash2) && hash2.SequenceEqual(hash)) continue;\n\t\t\n\t\tprint.it(name);\n\t\tperf.first();\n\t\tvar sum = _GenerateSummary(name, text, model, false);\n\t\tperf.nw();\n\t\t//print.it(sum);\n\t\t\n\t\tstaTempInsert.BindAll(name, sum, hash);\n\t\tstaTempInsert.Step();\n\t\tstaTempInsert.Reset();\n\t\t\n\t\t//if (!dialog.showYesNo(\"Continue?\")) return;\n\t\tif (keys.isCapsLock) return;\n\t}\n\t\n\t//if (!dialog.showOkCancel(\"Finished\", $\"Copy temp summaries to doc-ai.db?\")) return;\n\tusing var staDocUpdate = dbDoc.Statement(\"UPDATE doc SET summary = ? WHERE name = ?\");\n\tusing var transDoc = dbDoc.Transaction();\n\tusing var staTempSelect = dbTemp.Statement(\"SELECT name,summary FROM doc\");\n\twhile (staTempSelect.Step()) {\n\t\tstring name = staTempSelect.GetText(0), sum = staTempSelect.GetText(1);\n\t\tstaDocUpdate.BindAll(sum, name);\n\t\tstaDocUpdate.Step();\n\t\tstaDocUpdate.Reset();\n\t}\n\ttransDoc.Commit();\n\tdbDoc.Execute(\"VACUUM\");\n}\nfilesystem.copyTo(dbFile, @\"C:\\code\\Au.Editor\", FIfExists.Delete);\n\nprint.it(\"DONE. Added summaries to doc-ai.db.\");\n\n#endif\n\nstatic string _GenerateSummary(string name, string text, AiChatModel model, bool test) {\n\tbool isApi = !name.Starts('[');\n\t//var modelVerbosity = model.model switch { \"gemini-2.5-flash-lite\" => ModelVerbosity.Low, \"gemini-2.5-flash\" => ModelVerbosity.Medium, _ => ModelVerbosity.High };\n\t\n\tstring system;\n\tconst string c_footer = \"The generated text will be used **only** to genarate AI embeddings for semantic search. Don't need details, because the original text will be included in the search too (it contains details).\";\n\tif (isApi) {\n\t\tsystem = $$\"\"\"\nThe user message is a LibreAutomate API member documentation.\nNeed to prepare it for semantic search using AI embedding. Here is how:\n\n1. Generate a concise list of documented features. Or a 1-sentence summary, if the documentation is short.\n2. Then append a short comma-separated list of likely user search phrases and important tokens that users might query.\n\nDon't include details, such as parameter names, default values, examples, notes, warnings, tips, alternatives.\n{{c_footer}}\n\n---\n\nOutput example for method `clipboard.paste(string text, string html = null, OKey options = null, KHotkey hotkey = default, int timeoutMS = 0)`:\n\n- Pastes text or HTML into the focused app using the clipboard\n- Sets clipboard data, sends keys `Ctrl+V`, waits until the target app gets clipboard data, and restores old clipboard data\n- You can specify a custom hotkey, timeout, and other options\n\nSearch keywords: clipboard, paste, text, html, Ctrl+V, restore clipboard\n\"\"\";\n\t} else if (name.Starts(@\"[cookbook]\")) {\n\t\tsystem = $$\"\"\"\nThe user message is a LibreAutomate cookbook article.\nNeed to prepare it for semantic search using AI embedding. Here is how:\n\n1. Generate a summary.\n2. Then append a short comma-separated list of likely user search phrases and important tokens that users might query.\n\n{{c_footer}}\n\"\"\";\n\t} else {\n\t\tsystem = $$\"\"\"\nThe user message is a LibreAutomate documentation article.\nNeed to prepare it for semantic search using AI embedding. Here is how:\n\n1. Generate a summary. One or two sentences.\n2. Then append a concise list of documented features, without details or examples. Skip this if there is nothing that is not already in the generated summary.\n3. Then append a short comma-separated list of likely user search phrases and important tokens that users might query.\n\n{{c_footer}}\n\"\"\";\n\t\tif (name is \"[articles] Key names and operators\") system += \"\\r\\nIMPORTANT: also add the list of keys from the \\\"Named keys\\\" section.\";\n\t}\n\t//print.it(system);return;\n\t\n\tvar post = model.GetPostData(system, [new(ACMRole.user, text)], 0.2);\n\tvar r = model.Post(post, model.GetHeaders());\n\tvar json = r.Json();\n\t//print.it(json.ToJsonString(new(System.Text.Json.JsonSerializerDefaults.Web) { WriteIndented = true }));\n\treturn model.GetAnswer(json).text;\n}\n\nstatic void _ProcessText(ref string s) {\n\t//s = s.RxReplace(@\"\\R##(?:## Examples|### Exceptions|# See Also)(\\R(?!##).*)+\", \"\");\n\t//s = s.RxReplace(@\"^(# (?:Method|Operator|Constructor|Indexer).+\\R(\\R.+)+\\R)\\R```\\R.+\\R```\\R\", \"$1\", 1);\n\ts = s.RxReplace(@\"\\R###+ (?:Exceptions|See [Aa]lso)(\\R(?!##).*)+\", \"\");\n}\n\n//static void _ShowTextInOutput2(string s) {\n//\tvar w1 = wnd.find(\"Output2\");\n//\tif (!w1.Is0) w1.SetText(s);\n//}\n\n//enum ModelVerbosity { Low, Medium, High }\n"
  },
  {
    "path": "Scripts/LA docs for AI/Upload AI embeddings.cs",
    "content": "/// Uploads an AI embeddings storage file to the LA website.\n/// Currently used only for icons embeddings.\n/// To run, click link printed by Embeddings._GetEmbeddings. Can't run directly because need a hash etc.\n/// It prints the link when created new embeddings. To print always when UI-searching, temporarily enable the `//emFile.PrintUploadIfAtHome`.\n\n/*/ c Sftp.cs; c Ed util shared.cs; /*/\n\nstring file = args[0], zipName = args[1];\n//print.it(file, zipName);\n\nif (!dialog.showOkCancel(\"Upload AI embedding vectors\", zipName)) return;\n\nstring zipFile = folders.ThisAppTemp + zipName;\ntry {\n\tif (!LA.SevenZip.Compress(out var errors, zipFile, file)) { print.it(errors); return; }\n\t\n\t//run.selectInExplorer(zipFile);\n\tSftp.UploadToLA(\"domains/libreautomate.com/public_html/download/ai/embedding\", zipFile);\n\t\n\tprint.it($\"Uploaded: {file} -> https://www.libreautomate.com/download/ai/embedding/{zipName}\");\n}\nfinally { filesystem.delete(zipFile, FDFlags.CanFail); }\n"
  },
  {
    "path": "Scripts/Minimal .NET SDK.cs",
    "content": "/// Creates minimal .NET SDK for LA NuGet and Publish features.\n/// Downloads full SDK to a temp folder, and gets only folders used by these features (tested).\n/// Each platform folder is less than 100 MB; compressed less than 30 MB (full SDK is 200).\n/// Finally prints an Upload link (https://www.libreautomate.com/download/sdk).\n/// LA on user computers will auto-download when need.\n/// Also installs the minimal SDK in `folders.Editor + \"SDK\"`. Must be x64 computer.\n\n/*/ nuget -\\SSH.NET; c Sftp.cs; c Passwords.cs; /*/\n\n//#define DEV\n\nusing Renci.SshNet;\n\nif (args is [\"upload\"]) { //clicked link 'Upload'\n\tMinimalSDK.Upload();\n\treturn;\n}\n\nprint.clear();\n\nvar m = new MinimalSDK();\nm.CreateX64();\n#if !DEV\nm.CreateArm64();\n#endif\nm.Finally();\n\n#if DEV\nMiscScripts.CreateListOfMinSdkFilesForDiff(@\"SDK\\sdk\\10.0.100\");\nMiscScripts.CreateListOfMinSdkFilesForDiff(@\"SDK-9\\sdk\\9.0.101\");\n#endif\n\nclass MinimalSDK {\n\tconst string c_version = \"10.0.100\"; //edit this if need. The SDK must use the first Runtime build, eg 9.0.0 and not 9.0.1 etc. Because the SDK can't be used on computers with older Runtime.\n#if !NET10_0\n#error please update c_version string\n#endif\n\tbool _arm64;\n\tstring _dirDotnet1, _dirDotnet2;\n\t\n\tstatic readonly string _TempDirBS = folders.ThisAppTemp + @\"net-sdk-script-data\\\";\n\t\n\tpublic void CreateX64() {\n\t\tif (osVersion.isArm64OS) throw new PlatformNotSupportedException(\"Must be x64\");\n\t\tprint.it(\"-- Creating x64 --\");\n\t\t_arm64 = false;\n\t\t_dirDotnet1 = $\"{_TempDirBS}full {c_version} x64\";\n\t\t_dirDotnet2 = folders.Editor + \"SDK\";\n\t\t_Create();\n\t}\n\t\n\tpublic void CreateArm64() {\n\t\tprint.it(\"-- Creating arm64 --\");\n\t\t_arm64 = true;\n\t\t_dirDotnet1 = $\"{_TempDirBS}full {c_version} arm64\";\n\t\t_dirDotnet2 = $\"{_TempDirBS}minimal {c_version} arm64\";\n\t\t_Create();\n\t}\n\t\n\tpublic void Finally() {\n\t\t//var site = \"https://dash.cloudflare.com/\";\n\t\tvar site = \"https://www.libreautomate.com/download/sdk\";\n\t\tprint.it($\"<>-- DONE --\\r\\n<script {script.name}.cs|upload>Upload<> the zip files to {site} and delete temp folder <link {_TempDirBS}>net-sdk-script-data<>.\");\n\t}\n\t\n\tpublic static void Upload() {\n\t\tprint.it(\"Uploading. Wait until DONE.\");\n\t\tvar zip1 = $\"{_TempDirBS}sdk-{Environment.Version.ToString(2)}-x64.zip\";\n\t\tvar zip2 = $\"{_TempDirBS}sdk-{Environment.Version.ToString(2)}-arm64.zip\";\n\t\tSftp.UploadToLA(\"domains/libreautomate.com/public_html/download/sdk\", zip1, zip2);\n\t\tprint.it(\"DONE\");\n\t\tif (dialog.showYesNo(\"Delete temp folder?\", _TempDirBS)) filesystem.delete(_TempDirBS);\n\t}\n\t\n\tvoid _Create() {\n\t\t_DownloadSdk();\n\t\t_CreateFolder();\n\t\t_CopyRootFiles();\n#if !DEV\n\t\t_Create7z();\n#endif\n\t\tif (!_arm64) {\n\t\t\tfilesystem.more.createSymbolicLink(_dirDotnet2 + @\"\\host\", @\"C:\\Program Files\\dotnet\\host\", CSLink.Junction);\n\t\t\tfilesystem.more.createSymbolicLink(_dirDotnet2 + @\"\\shared\", @\"C:\\Program Files\\dotnet\\shared\", CSLink.Junction);\n\t\t}\n\t}\n\t\n\tvoid _DownloadSdk() {\n\t\tif (filesystem.exists(_dirDotnet1)) return;\n\t\tstring zipName = $\"dotnet-sdk-{c_version}-win-{(_arm64 ? \"arm64\" : \"x64\")}.zip\";\n\t\tvar sevenzip = folders.Editor + @\"32\\7za.exe\";\n\t\tstring zipFile = _TempDirBS + zipName;\n\t\tif (!filesystem.exists(zipFile)) {\n\t\t\tprint.it(\"Downloading SDK...\");\n\t\t\tstring url = $\"https://builds.dotnet.microsoft.com/dotnet/Sdk/{c_version}/{zipName}\";\n\t\t\tinternet.http.Get(url, zipFile + \"~\");\n\t\t\tfilesystem.rename(zipFile + \"~\", zipName);\n\t\t}\n\t\t\n\t\tprint.it(\"Extracting SDK...\");\n\t\tif (0 != run.console(out var s, sevenzip, $@\"x -aoa -o\"\"{_dirDotnet1}\"\" \"\"{zipFile}\"\"\")) {\n\t\t\tfilesystem.delete(_dirDotnet1);\n\t\t\tthrow new AuException($\"*extract. {s}\");\n\t\t}\n\t}\n\t\n\tvoid _CreateFolder() {\n\t\tprint.it(\"Creating folder\");\n\t\t\n\t\tstring dirSdk1 = $@\"{_dirDotnet1}\\sdk\\{c_version}\";\n\t\tstring dirSdk2 = $@\"{_dirDotnet2}\\sdk\\{c_version}\";\n\t\t\n\t\tif (!filesystem.exists(dirSdk1)) throw new ArgumentException(\"Please edit SDK version in this script\");\n\t\t\n\t\tfilesystem.delete(_dirDotnet2);\n\t\t\n\t\tlong size = 0;\n\t\tvar a1 = filesystem.enumFiles(dirSdk1, flags: FEFlags.AllDescendants | FEFlags.NeedRelativePaths).ToArray();\n\t\tforeach (var f in a1) {\n\t\t\tvar path = f.FullPath;\n\t\t\tvar rel = f.Name;\n\t\t\t\n\t\t\tif (rel.Ends(\".resources.dll\", true)) continue;\n\t\t\tif (rel.Find(@\"\\net4\", true) > 0) continue;\n\t\t\tif (rel.Find(@\"\\netframework\", true) > 0) continue;\n\t\t\tif (rel.Find(\"FSharp\", true) > 0) continue;\n\t\t\tif (rel.Find(\"VisualBasic\", true) > 0) continue;\n\t\t\t\n\t\t\tvar a = rel.Lower().Split('\\\\', StringSplitOptions.RemoveEmptyEntries);\n\t\t\tif (a.Length > 1) {\n\t\t\t\tswitch (a[0]) {\n\t\t\t\tcase \"dotnettools\" or \"extensions\" or \"fsharp\" or \"testhostnetframework\": continue; //not used, big\n\t\t\t\tcase \"apphosttemplate\" or \"ref\" or \"runtimes\" or \"trustedroots\": break; //maybe not used, but small\n\t\t\t\tcase \"current\" or \"microsoft\" or \"sdkresolvers\": break; //used, small\n\t\t\t\tcase \"containers\":\n\t\t\t\t\tif (!(a[1] is \"build\")) continue;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"roslyn\":\n\t\t\t\t\tif (a[^1].Ends(\".visualbasic.dll\")) continue;\n\t\t\t\t\t//tested: does not work with the 2 *CodeAnalysis* dlls replaced with those from the LA Roslyn folder. Would be 7z 26 -> 18 MB.\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"sdks\" when a.Length > 2:\n\t\t\t\t\tif (a[1] is \"microsoft.net.sdk\" or \"microsoft.net.sdk.windowsdesktop\" or \"microsoft.net.sdk.publish\") {\n\t\t\t\t\t\tif (a[^2] is \"vb\") continue;\n\t\t\t\t\t} else if (a[1] is \"microsoft.net.sdk.razor\" or \"microsoft.net.sdk.staticwebassets\" or \"microsoft.net.sdk.blazorwebassembly\" or \"fsharp.net.sdk\") {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (!(a[2] is \"build\" or \"tools\" or \"targets\")) continue;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tprint.it(rel);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (a[0] is \"microsoft.codeanalysis.dll\" or \"microsoft.codeanalysis.csharp.dll\") continue; //duplicates of those in `Roslyn\\bincore`. Large. Used only by dotnet.dll (see script `.NET SDK dll dependencies`), but not for nuget/build/publish.\n\t\t\t\t//print.it(a[0]);\n\t\t\t}\n\t\t\t\n\t\t\tfilesystem.copy(path, dirSdk2 + rel);\n\t\t\t\n\t\t\tsize += f.Size;\n\t\t}\n\t\tprint.it($\"Folder size: {double.Round(size / (1024 * 1024d), 1)}\");\n\t}\n\t\n\tvoid _CopyRootFiles() {\n\t\t////copy dotnet.exe and license txt files.\n\t\t//foreach (var f in filesystem.enumFiles(_dirDotnet1)) {\n\t\t//\tvar path = f.FullPath;\n\t\t//\t//print.it(path);\n\t\t//\tfilesystem.copyTo(path, _dirDotnet2);\n\t\t//}\n\t\t\n\t\tfilesystem.copyTo(_dirDotnet1 + @\"\\dotnet.exe\", _dirDotnet2);\n\t}\n\t\n\tvoid _Create7z() {\n\t\tprint.it(\"Compressing...\");\n\t\t\n\t\tvar filename = $\"sdk-{Environment.Version.ToString(2)}-{(_arm64 ? \"arm64\" : \"x64\")}.zip\"; //note: it's 7z, but Hostinger for 7z does not set `content-length` header (no progress); also `content-type: text/plain`\n\t\tvar zipFile = _TempDirBS + filename;\n\t\tfilesystem.delete(zipFile);\n\t\t\n\t\tvar sevenzip = folders.Editor + @\"32\\7za.exe\";\n\t\tif (0 != run.console(out var s, sevenzip, $@\"a \"\"{zipFile}\"\" \"\"{_dirDotnet2}\\*\"\" -t7z -mx=7\")) throw new AuException(s);\n\t}\n}\n\nstatic class MiscScripts {\n\tpublic static void CreateListOfMinSdkFilesForDiff(string dirName) {\n\t\tvar dir = folders.ThisAppBS + dirName;\n\t\tList<string> a = [];\n\t\tforeach (var f in filesystem.enumerate(dir, FEFlags.AllDescendants | FEFlags.NeedRelativePaths)) {\n\t\t\ta.Add(f.Name);\n\t\t}\n\t\tvar s = string.Join('\\n', a);\n\t\tdirName = dirName[..dirName.IndexOf('\\\\')];\n\t\tfilesystem.saveText($@\"C:\\Test\\{dirName}.txt\", s);\n\t}\n}\n"
  },
  {
    "path": "Scripts/Update version.txt.cs",
    "content": "/*/ role editorExtension; c Sftp.cs; /*/\nusing System.Runtime.Loader;\n\nvar ver = typeof(osVersion).Assembly.GetName().Version.ToString(3);\nvar ver2 = AssemblyLoadContext.Default.Assemblies.First(o => o.GetName().Name == \"Au.Editor\").GetName().Version.ToString(3);\nif (ver2 != ver) throw new InvalidOperationException(\"editor project not compiled\");\n\nvar file = folders.ThisAppBS + \"version.txt\";\nfilesystem.saveText(file, ver);\nSftp.UploadToLA(\"domains/libreautomate.com/public_html\", file);\n\nprint.it(\"Uploaded: version.txt\", ver);\n"
  },
  {
    "path": "Scripts/old/VS goto.cs",
    "content": "var s = args.Length == 0 ? \"Au.clipboard.copy\" : args[0];\n\nif (s.Ends(\"..ctor\")) s=s[..^6];\nelse {\n\tint i=s.Find(\".op_\");\n\tif(i>0) s=s[..i];\n}\n\nvar w = wnd.find(0, \"Au -* Microsoft Visual Studio*\", \"HwndWrapper[DefaultDomain;*\");\nw.Activate();\n//keys.send(\"Ctrl+T\");\nkeys.send(\"Ctrl+(1 S)\");\nclipboard.paste(s);\nmouse.move(screen.primary);\n//if (miscInfo.getTextCursorRect(out var r, out _)) mouse.move(r); else mouse.move(screen.primary);\n"
  },
  {
    "path": "Scripts/old/Windows SDK to C#/@SDK converter/Classes.cs",
    "content": "﻿namespace SdkConverter;\n\nunsafe partial class Converter {\n\tenum _KeywordT : byte {\n\t\tAny,\n\t\tNormal,\n\t\tTypeDecl, //enum, struct, union, class, __interface, typedef\n\t\tCallConv, //__stdcall etc\n\t\tPubPrivProt, //public, private, protected\n\t\tIgnoreFuncEtc, //inline, template, operator, static_assert\n\t\t\n\t\t//Declspec, //__declspec(...) //removed in script\n\t\t//Ignore, //volatile etc //removed in script\n\t}\n\t\n\tclass _Namespace {\n\t\t/// <summary>\n\t\t/// Symbols of current namespace. Types, typedef etc. Not C++ keywords/types.\n\t\t/// </summary>\n\t\tpublic Dictionary<_Token, _Symbol> sym = new Dictionary<_Token, _Symbol>();\n\t\t\n\t\t/// <summary>\n\t\t/// Formats members and nested types of current namespace. Cleared/reused for each type.\n\t\t/// </summary>\n\t\tpublic StringBuilder sb = new StringBuilder();\n\t\t\n\t\t/// <summary>\n\t\t/// Clears sym and sb.\n\t\t/// </summary>\n\t\tpublic void Clear() {\n\t\t\tsym.Clear();\n\t\t\tsb.Clear();\n\t\t}\n\t}\n\t\n\tclass _Symbol {\n\t\tpublic string csTypename;\n\t\tpublic bool forwardDecl;\n\t}\n\t\n\tclass _Keyword : _Symbol {\n\t\tpublic _KeywordT kwType;\n\t\tpublic bool cannotStartStatement;\n\t\t\n\t\tpublic _Keyword(_KeywordT kwType = _KeywordT.Normal, bool cannotStartStatement = false) {\n\t\t\tthis.kwType = kwType;\n\t\t\tthis.cannotStartStatement = cannotStartStatement;\n\t\t}\n\t}\n\t\n\tclass _CppType : _Symbol {\n\t\tpublic byte sizeBytesCpp;\n\t\tpublic bool isUnsigned;\n\t\t\n\t\tpublic _CppType(string csTypename, int sizeBytesCpp, bool isUnsigned) {\n\t\t\tthis.csTypename = csTypename;\n\t\t\tthis.sizeBytesCpp = (byte)sizeBytesCpp;\n\t\t\tthis.isUnsigned = isUnsigned;\n\t\t}\n\t}\n\t\n\tclass _Enum : _Symbol {\n\t\tpublic char[] defAfterName;\n\t\tpublic bool isFlags;\n\t\t\n\t\tpublic _Enum(string csTypename, bool forwardDecl) {\n\t\t\tthis.csTypename = csTypename;\n\t\t\tthis.forwardDecl = forwardDecl;\n\t\t}\n\t\t\n\t\tpublic static _Enum Copy(_Enum x, string name) {\n\t\t\tvar r = new _Enum(name, x.forwardDecl);\n\t\t\tr.defAfterName = x.defAfterName;\n\t\t\tr.isFlags = x.isFlags;\n\t\t\treturn r;\n\t\t}\n\t}\n\t\n\tclass _Struct : _Symbol {\n\t\tpublic bool isInterface, isDualInterface, isClass;\n\t\tpublic char[] attributes, members;\n\t\t\n\t\tpublic _Struct(string csTypename, bool forwardDecl) {\n\t\t\tthis.csTypename = csTypename;\n\t\t\tthis.forwardDecl = forwardDecl;\n\t\t}\n\t\t\n\t\tpublic static _Struct Copy(_Struct x, string name) {\n\t\t\tvar r = new _Struct(name, x.forwardDecl);\n\t\t\tr.isInterface = x.isInterface;\n\t\t\tr.isDualInterface = x.isDualInterface;\n\t\t\tr.attributes = x.attributes;\n\t\t\tr.members = x.members;\n\t\t\treturn r;\n\t\t}\n\t}\n\t\n\tclass _Callback : _Symbol {\n\t\tpublic _Callback(string csTypename) {\n\t\t\tthis.csTypename = csTypename;\n\t\t}\n\t}\n\t\n\tclass _Typedef : _Symbol {\n\t\t/// <summary>\n\t\t/// pointer (1 if *, 2 if ** and so on)\n\t\t/// </summary>\n\t\tpublic byte ptr;\n\t\t/// <summary>\n\t\t/// Is const.\n\t\t/// </summary>\n\t\tpublic bool isConst;\n\t\t/// <summary>\n\t\t/// Used when swapping tagX with X in 'typedef tagX{...}X'. Then true if tagX was forward-declared. Later used mostly to make conversion faster.\n\t\t/// </summary>\n\t\tpublic bool wasForwardDecl;\n\t\t/// <summary>\n\t\t/// struct etc for which this typedef is alias.\n\t\t/// </summary>\n\t\tpublic _Symbol aliasOf;\n\t\t\n\t\tpublic _Typedef(_Symbol aliasOf, int ptr, bool isConst, bool wasForwardDecl = false) {\n\t\t\tthis.aliasOf = aliasOf;\n\t\t\tthis.ptr = (byte)ptr;\n\t\t\tthis.isConst = isConst;\n\t\t\tthis.wasForwardDecl = wasForwardDecl;\n\t\t}\n\t}\n\t\n\t//class _Func\n\t//{\n\t\n\t//}\n\t\n\tstruct _Token : IEquatable<_Token> {\n\t\tpublic char* s { get; private set; }\n\t\tpublic int len { get; private set; }\n\t\t\n\t\tpublic _Token(char* S, int length) {\n\t\t\ts = S;\n\t\t\tlen = length;\n\t\t}\n\t\t\n\t\tpublic bool Equals(_Token t) {\n\t\t\tif (t.len != len) return false;\n\t\t\tfor (int i = 0; i < len; i++) if (t.s[i] != s[i]) return false;\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\tpublic bool Equals(string s) {\n\t\t\tif (s.Length != len) return false;\n\t\t\tfor (int i = 0; i < len; i++) if (s[i] != this.s[i]) return false;\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\tpublic bool StartsWith(string s) {\n\t\t\tint n = s.Length;\n\t\t\tif (n > len) return false;\n\t\t\tfor (int i = 0; i < n; i++) if (s[i] != this.s[i]) return false;\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\tpublic override int GetHashCode() {\n\t\t\treturn (int)_HashFnv1(s, len);\n\t\t}\n\t\t\n\t\tpublic override string ToString() {\n\t\t\treturn new string(s, 0, len);\n\t\t}\n\t\t\n\t\tpublic static implicit operator char*(_Token t) { return t.s; }\n\t\t\n\t\t//FNV-1 hash.\n\t\t//2 times faster than Crc, but less accurate.\n\t\t//If length<0, data must be \\0-terminated string. Then same speed (does not call Len).\n\t\tstatic uint _HashFnv1(char* data, int length = -1) {\n\t\t\tuint hash = 2166136261;\n\t\t\t\n\t\t\tif (length >= 0) {\n\t\t\t\tfor (int i = 0; i < length; i++)\n\t\t\t\t\thash = (hash * 16777619) ^ data[i];\n\t\t\t\t//hash = (hash ^ p[i]) * 16777619; //FNV-1a, slower\n\t\t\t} else {\n\t\t\t\tchar ch;\n\t\t\t\twhile ((ch = *data++) != '\\0') hash = (hash * 16777619) ^ ch;\n\t\t\t}\n\t\t\t\n\t\t\treturn hash;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Scripts/old/Windows SDK to C#/@SDK converter/Constants.cs",
    "content": "﻿namespace SdkConverter;\n\nunsafe partial class Converter {\n\tvoid _DefineUndef() {\n\t\tchar* s = T(++_i);\n\t\tchar c = *s; //was like `d$$$_REALNAME, now s is without `\n\t\t\n\t\tif (c == 'c') { //`cx \"C# code converted by the preprocessor script\", where x tells what it is\n\t\t\tif (!_TokIsChar(++_i, '\\x2')) _Err(_i, \"unexpected 1\");\n\t\t\t_tok[_i] = new _Token(T(_i) + 1, _tok[_i].len - 2);\n\t\t\tif (s[1] == 'p') {\n\t\t\t\t_sbVar.AppendLine(_TokToString(_i));\n\t\t\t} else _Err(_i - 1, \"unexpected 2\");\n\t\t\treturn;\n\t\t}\n\t\t\n\t\ts += 5; int lenName = _tok[_i].len - 5; //skip prefix 'd$$$_' that was added to avoid unexpanding names\n\t\t_tok[_i] = new _Token(s, lenName);\n\t\tint iName = _i;\n\t\t\n\t\t//is function-style?\n\t\tchar* s2 = T(++_i);\n\t\tbool isFunc = c == 'd' && *s2 == '(' && s2 == s + lenName;\n\t\t\n\t\t//find value\n\t\tint iValue = _i, iParamOrValue = _i;\n\t\tif (isFunc) iValue = _SkipEnclosed(_i) + 1; //name(parameters)[ value]\n\t\t\n\t\t//find next line\n\t\tint iNext = iValue;\n\t\tfor (; ; iNext++) {\n\t\t\tchar k = *T(iNext);\n\t\t\tif (k == '`' || k == '\\x0') break;\n\t\t}\n\t\t\n\t\tstring name = new string(s, 0, lenName);\n\t\tif (c == 'u') { //#undef\n\t\t\tif (!_defineConst.Remove(name) && !_defineOther.Remove(name)) _defineW.Remove(name);\n\t\t\t//print.it($\"#undef {name}\");\n\t\t} else if (iValue < iNext) { //preprocessor removes some #define values, it's ok\n\t\t\tif (isFunc) { //info: for func-style get parameters as part of value\n\t\t\t\t__DefineAddToOther(iName, name, _TokToString(iParamOrValue, iNext));\n\t\t\t} else if (*s2 == '\\x2') { //ANSI string constant, when tokenizing replaced the first '\"' to '\\x2'\n\t\t\t\t*s2 = '\"';\n\t\t\t\t__DefineAddToOther(iName, name, \" \" + _TokToString(iParamOrValue, iNext) + \" //ANSI string\");\n\t\t\t} else {\n\t\t\t\t_ExpressionResult r = _Expression(iParamOrValue, iNext, name);\n\t\t\t\tif (r.typeS == null) {\n\t\t\t\t\tbool isFuncWA = (iNext - iValue == 1) && __DefineWA(name, r.valueS);\n\t\t\t\t\tif (!isFuncWA) {\n\t\t\t\t\t\t//don't add '#define NAME1 NAME2'\n\t\t\t\t\t\tif (iNext - iValue == 1 && _TokIsIdent(iValue)) {\n\t\t\t\t\t\t\t//print.it(name, r.valueS);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t//print.it(name, iNext-iValue);\n\t\t\t\t\t\t\t__DefineAddToOther(iName, name, \" \" + r.valueS);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t__sbDef.Clear();\n\t\t\t\t\tif (name.Starts(\"WM_\") && r.typeS == \"uint\") r.typeS = \"int\";\n\t\t\t\t\t__sbDef.AppendFormat(\"internal {2} {3} {0} = {1};\", name, r.valueS, r.notConst ? \"readonly\" : \"const\", r.typeS);\n\t\t\t\t\t_defineConst[name] = __sbDef.ToString();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\t_i = iNext - 1;\n\t}\n\t\n\tvoid __DefineAddToOther(int iName, string name, string value) {\n\t\t//remove those that match other identifiers\n\t\tif (_SymbolExists(iName, false) || _func.ContainsKey(name)) {\n\t\t\t//print.it(name);\n\t\t\treturn;\n\t\t}\n\t\t\n\t\t_defineOther[name] = value;\n\t}\n\t\n\tStringBuilder __sbDef = new StringBuilder();\n\t\n\tbool __DefineWA(string name, string value) {\n\t\tint suffixLen = 0;\n\t\tif (value.Length == name.Length + 1 && value.Ends(\"W\")) suffixLen = 1;\n\t\telse if (value.Length == name.Length + 2 && value.Ends(\"_W\")) suffixLen = 2; //some struct\n\t\tif (!(suffixLen > 0 && value.Starts(name))) {\n\t\t\t//print.it($\"<><c 0xff>{name}    {value}</c>\");\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\tstring def;\n\t\tif (suffixLen == 1 && _func.TryGetValue(value, out def)) {\n\t\t\t_func.Remove(name + \"A\");\n\t\t\t//most are FuncA+FuncW, but some Func/FuncW and even Func/FuncA/FuncW. Some just FuncW.\n\t\t\tif (_func.Remove(name)) {\n\t\t\t\t//print.it(1, def); //6 in SDK\n\t\t\t}\n\t\t\t\n\t\t\tdef = def.Replace(\"W(\", \"(\");\n\t\t\t\n\t\t\tif (!def.Contains(\", EntryPoint=\\\"\")) {\n\t\t\t\tdef = def.Replace(\")]\", \", EntryPoint=\\\"\" + value + \"\\\")]\");\n\t\t\t}\n\t\t\t\n\t\t\t_func[value] = def;\n\t\t\t\n\t\t\t//print.it($\"<><c 0xff0000>{name}    {value}</c>\");\n\t\t\t//print.it(def);\n\t\t\treturn true;\n\t\t} else {\n\t\t\t_Symbol x;\n\t\t\tif (_ns[0].sym.TryGetValue(_TokenFromString(value), out x)) {\n\t\t\t\tvar t = x as _Struct;\n\t\t\t\tif (t != null || x is _Callback) {\n\t\t\t\t\t//if(x is _Struct) print.it($\"<><c 0xff0000>{name}    {value}</c>\");\n\t\t\t\t\t//else print.it($\"<><c 0x8000>{name}    {value}</c>\");\n\t\t\t\t\tint v = (t == null) ? 2 : (t.isInterface ? 1 : 0);\n\t\t\t\t\tif (suffixLen == 2) v |= 0x10000;\n\t\t\t\t\t_defineW[name] = v; //later will replace all STRUCTW to STRUCT in the final string of struct/func/delegate/interface\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\t//else if(x is _Typedef) {\n\t\t\t\t//\tprint.it($\"<><c 0x80>{name}    {value}</c>\");\n\t\t\t\t//} else {\n\t\t\t\t//\tprint.it($\"<><c 0xFF>{name}    {value}</c>\"); //0\n\t\t\t\t//}\n\t\t\t\t\n\t\t\t} else {\n\t\t\t\t//print.it($\"<><c 0xff>{name}    {value}</c>\");\n\t\t\t\t\n\t\t\t}\n\t\t}\n\t\t\n\t\treturn false;\n\t}\n\t\n\tvoid _ConstantsFinally(StreamWriter writer) {\n\t\t//'#define' constants\n\t\tforeach (var v in _defineConst) {\n\t\t\t//if string constant name ends with \"W\", remove this if non-W version exists, and remove A version\n\t\t\tstring s = v.Value;\n\t\t\tif (s.Ends(\"\\\";\") && v.Key.Ends(\"W\")) {\n\t\t\t\t//print.it($\"{v.Key} = {s}\");\n\t\t\t\tstring k = v.Key.Remove(v.Key.Length - 1); //name without \"W\"\n\t\t\t\t\n\t\t\t\t//remove A version from _defineOther\n\t\t\t\tif (_defineOther.Remove(k + \"A\")) {\n\t\t\t\t\t//print.it($\"removed A version: {v.Key} = {s}\");\n\t\t\t\t}\n\t\t\t\t//remove this W version if non-W version exists\n\t\t\t\tstring s2;\n\t\t\t\tif (_defineConst.TryGetValue(k, out s2) && s2.Length == s.Length - 1) {\n\t\t\t\t\tint i = s.Find(\"W = \");\n\t\t\t\t\tif (s2 == s.Remove(i, 1)) {\n\t\t\t\t\t\t//print.it($\"removed W version because non-W exists: {v.Key} = {s}\");\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\twriter.WriteLine(s);\n\t\t}\n\t\t\n\t\t//'const' constants\n\t\tforeach (var con in _cppConst) { writer.WriteLine(con); }\n\t\t\n\t\t//'#define' function-style macros and other macros that cannot convert to C#\n\t\twriter.Write(\"\\r\\n// CANNOT CONVERT\\r\\n\\r\\n\");\n\t\tforeach (var v in _defineOther) {\n\t\t\t//if(v.Value.Starts(\" \\\"\")) print.it($\"<><c 0xff>{v.Key} = {v.Value}</c>\"); //11 in SDK (more removed by the above code)\n\t\t\t\n\t\t\twriter.Write(\"internal const string {0} = null; //#define {0}{1};\\r\\n\\r\\n\", v.Key, v.Value);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Scripts/old/Windows SDK to C#/@SDK converter/Converter.cs",
    "content": "namespace SdkConverter;\n\nunsafe partial class Converter {\n\tstring _cppFile;\n\tbool _is32bit;\n\t\n\tchar[] _src; //C/C++ source code\n\tchar* _s0; //_src start\n\t\n\tStringBuilder _sbInterface = new();\n\t//StringBuilder _sbCoclass = new();\n\tStringBuilder _sbVar = new();\n\tStringBuilder _sbInlineDelegate = new(); //from callback function types defined in parameter list or member list\n\t\n\tList<_Token> _tok = new();\n\tint _nTok; //token count, except the last '\\0' tokens\n\tint _nTokUntilDefUndef; //token count until first `d or `u (was #define/#undef), except the separating '\\0' token\n\tint _i; //current token\n\t\n\t_Namespace[] _ns = new _Namespace[10]; //stack of namespaces\n\tint _nsCurrent; //index in _ns\n\tStringBuilder _sbType; //_ns[_nsCurrent].sb\n\tDictionary<_Token, _Symbol> _keywords = new();\n\t\n\tDictionary<string, string> _func = new(); //function declaration\n\tStringBuilder _sbFuncTemp = new(); //used to format _func values\n\t\n\tList<string> _cppConst = new(); //const CONSTANT\n\t\n\tDictionary<string, string> _defineConst = new(); //#define CONSTANT\n\tDictionary<string, int> _defineW = new(); //#define STRUCT STRUCTW\n\tDictionary<string, string> _defineOther = new(); //#define MACRO(...)\n\t\n\tStack<int> _packStack = new(); int _pack = 8; //#pragma pack\n\t\n\tint _anonymousStructSuffixCounter; //used to create unique name for anonymous struct\n\t\n\t/// <summary>\n\t/// Converts C++ header-list file cppFile to C# declarations and saves in file csFile.\n\t/// </summary>\n\t/// <param name=\"is32bit\">Prefer 32-bit. Although the converter tries to create declarations that don't depend on 32/64 bit, in some places it will create different declaration if this is true, for example IntPtr size will be 4 bytes.</param>\n\tpublic void Convert(string cppFile, string csFile, bool is32bit) {\n\t\t_cppFile = cppFile;\n\t\t_is32bit = is32bit;\n\t\t\n\t\ttry {\n\t\t\tif (_src != null) throw new Exception(\"cannot call Convert multiple times. Create new Converter instance.\");\n\t\t\t\n\t\t\tusing (var reader = new StreamReader(_cppFile)) {\n\t\t\t\tlong n = reader.BaseStream.Length + 4; if (n > int.MaxValue / 4) throw new Exception(\"file too big\");\n\t\t\t\treader.Read(_src = new char[n], 0, (int)n);\n\t\t\t}\n\t\t\t\n\t\t\t_InitTables();\n\t\t\t_InitSymbols();\n\t\t\t_InitMaps();\n\t\t\t_InitCsKeywords();\n\t\t\t\n\t\t\tstring stf = null;\n\t\t\t\n\t\t\tfixed (char* p = _src) {\n\t\t\t\t_s0 = p;\n\t\t\t\t_Tokenize(p);\n\t\t\t\t_InitInterfaces();\n\t\t\t\t\n\t\t\t\t//Test(); return;\n\t\t\t\t\n\t\t\t\t_i = 1;\n\t\t\t\t_ConvertAll(0);\n\t\t\t\t\n\t\t\t\t//_ConvertTypedefAndTag();\n\t\t\t\t\n\t\t\t\t//print.it(\"#define CONST:\");\n\t\t\t\t//print.it(_defineConst);\n\t\t\t\t//print.it(\"#define other:\");\n\t\t\t\t//print.it(_defineOther);\n\t\t\t\t//print.it(_defineConst.Count, _defineOther.Count);\n\t\t\t\t//27890, 1061\n\t\t\t\t//27659, 998\n\t\t\t\t//27605, 983\n\t\t\t\t\n\t\t\t\t_FunctionsFinally();\n\t\t\t\t\n\t\t\t\tstf = _PostProcessTypesFunctionsInterfaces();\n\t\t\t\t//print.it(stf);\n\t\t\t}\n\t\t\t\n\t\t\tstring sh = @\"// Windows API declarations for C#.\n// Don't add this file to your project. Copy-paste only declarations that you need.\n// Not all declarations can be compiled without editing.\n//    For example, cannot declare some struct pointer (use IntPtr instead, or in struct replace non-blittable types with IntPtr etc), cannot use undefined struct pointer/ref/out (use IntPtr instead).\n// Not all declarations are correct, usually because declarations in Windows SDK files from which they have been automatically converted lack some info.\n//    For example, some function parameters that should be 'out' or '[Out]' or '[In]' or array now are just 'ref', because SDK declarations didn't have proper in/out annotations. Also for this reason some parameters that should be 'string' now are 'char*'.\n//    You may want to create overloads where parameters can be of more than one type.\n//    You may want to add 'SetLastError=true' to DllImport attribute parameters.\n// Some declarations contain pointers and therefore can be used only in 'unsafe' context, in some cases with 'fixed'. Or you can replace pointers to IntPtr.\n// These declarations are for Windows 10. Some Windows API are different or missing on other Windows versions.\n// In some cases need to use different declarations in 32-bit and 64-bit process. This file contains everything that is not different, + 64-bit versions, + 32-bit versions with name suffix \"\"__32\"\".\n\nusing System;\nusing System.Runtime.InteropServices;\n#if true\nusing Au;\nusing Au.Types;\n#else\nusing wnd = System.IntPtr; //HWND (window handle)\n#endif\n\n//add this to projects that will use these API\n[module: DefaultCharSet(CharSet.Unicode)]\n\nstatic unsafe class API\n{\n\";\n\t\t\t\n\t\t\tusing (var writer = new StreamWriter(csFile)) {\n\t\t\t\twriter.Write(sh);\n\t\t\t\twriter.Write(stf);\n\t\t\t\t//if(_sbType.Length > 0 || _sbInterface.Length > 0) {\n\t\t\t\t//\twriter.Write(\"\\r\\n// USED TAG-TYPES\\r\\n\");\n\t\t\t\t//\tif(_sbType.Length > 0) writer.Write(_sbType.ToString());\n\t\t\t\t//\tif(_sbInterface.Length > 0) writer.Write(_sbInterface.ToString());\n\t\t\t\t//}\n\t\t\t\t//writer.WriteLine(\"\\r\\n// COCLASS\");\n\t\t\t\t//writer.Write(_sbCoclass.ToString());\n\t\t\t\twriter.WriteLine(\"\\r\\n// VARIABLE\");\n\t\t\t\twriter.Write(_sbVar.ToString());\n\t\t\t\twriter.WriteLine(\"\\r\\n// CONSTANT\\r\\n\");\n\t\t\t\t_ConstantsFinally(writer);\n\t\t\t\twriter.Write(\"\\r\\n}\\r\\n\");\n\t\t\t}\n\t\t}\n\t\t//#if !TEST_SMALL\n\t\tcatch (Exception e) { _OnCatchException(e); throw; }\n\t\t//#endif\n\t\tfinally {\n\t\t\tMarshal.FreeHGlobal((IntPtr)_keywordMemory);\n\t\t}\n\t\t\n\t\tprint.it($\"DONE {(is32bit ? 32 : 64)}-bit\");\n\t}\n\t\n\tchar* _keywordMemory;\n\tchar* _km; //current keyword\n\t\n\tvoid _AddKeyword(string name, _Symbol sym) {\n\t\tchar* n = _km;\n\t\tfor (int i = 0; i < name.Length; i++) *_km++ = name[i];\n\t\t_keywords.Add(new _Token(n, (int)(_km - n)), sym);\n\t\t*_km++ = '\\0';\n\t}\n\t\n\tvoid _AddKeywords(_Symbol sym, params string[] names) {\n\t\tforeach (string s in names) {\n\t\t\t_AddKeyword(s, sym);\n\t\t}\n\t}\n\t\n\tvoid _InitSymbols() {\n\t\tfor (int i = 0; i < _ns.Length; i++) _ns[i] = new _Namespace();\n\t\t_sbType = _ns[0].sb;\n\t\t\n\t\t_km = _keywordMemory = (char*)Marshal.AllocHGlobal(4000);\n\t\t\n\t\t//Add C++ keywords and types, except those that can only be in function body or other places where we skip.\n\t\t//The commented keywords/types are currently not supported and not expected to be in C/C++ headers that we'll parse.\n\t\t\n\t\t_Keyword k;\n\t\t\n\t\t//_AddKeywords(new _Keyword(cannotStartStatement:true), \"__based\", \"_based\"); //0 in SDK\n\t\t//_AddKeyword(\"__event\", new _Keyword()); //0 in SDK\n\t\t//_AddKeyword(\"__identifier\", new _Keyword()); //0 in SDK\n\t\t//_AddKeywords(new _Keyword(), \"__if_exists\", \"_if_exists\", \"__if_not_exists\", \"_if_not_exists\"); //0 in SDK. Usually used only in function body.\n\t\t//_AddKeywords(new _Keyword(cannotStartStatement: true), \"__multiple_inheritance\", \"_multiple_inheritance\", \"__single_inheritance\", \"_single_inheritance\", \"__virtual_inheritance\", \"_virtual_inheritance\"); //0 in SDK\n\t\t//_AddKeyword(\"__uuidof\", new _Keyword(cannotStartStatement: true)); //should be only in function body\n\t\t//_AddKeyword(\"alignas\", k = new _Keyword(cannotStartStatement: true)); //removed in script\n\t\t//_AddKeywords(new _Keyword(cannotStartStatement: true), \"alignof\", \"__alignof\"); //should be only in function body\n\t\t//_AddKeywords(new _Keyword(cannotStartStatement: true), \"false\", \"true\", \"nullptr\"); //we add enum values for it\n\t\t//_AddKeyword(\"property\", k = new _Keyword()); //0 in SDK\n\t\t//_AddKeyword(\"signed\", k = new _Keyword()); //replaced in script\n\t\t//_AddKeywords(new _Keyword(cannotStartStatement: true), \"throw\", \"noexcept\"); //eg void f() throw(int); //will be skipped together with functions\n\t\t//_AddKeyword(\"unsigned\", k = new _Keyword()); //replaced in script\n\t\t\n\t\tk = new _Keyword(cannotStartStatement: true);\n\t\t_AddKeyword(\"sizeof\", k); //can be like 'member[sizeof(X)]' or '#define X sizeof(Y)'\n\t\t\n\t\tk = new _Keyword();\n\t\t_AddKeyword(\"const\", k);\n\t\t_AddKeyword(\"extern\", k);\n\t\t_AddKeyword(\"namespace\", k); //0 in SDK (1 in CRT, which is removed)\n\t\t_AddKeyword(\"using\", k);\n\t\t_AddKeyword(\"virtual\", k);\n\t\t\n\t\tk = new _Keyword(_KeywordT.TypeDecl);\n\t\t_AddKeyword(\"__interface\", k);\n\t\t_AddKeyword(\"class\", k);\n\t\t_AddKeyword(\"enum\", k);\n\t\t_AddKeyword(\"struct\", k);\n\t\t_AddKeyword(\"typedef\", k);\n\t\t_AddKeyword(\"union\", k);\n\t\t\n\t\t_AddKeywords(new _Keyword(_KeywordT.CallConv, cannotStartStatement: true), \"__cdecl\", \"_cdecl\", \"__fastcall\", \"_fastcall\", \"__stdcall\", \"_stdcall\", \"__thiscall\");\n\t\t\n\t\t_AddKeywords(new _Keyword(_KeywordT.IgnoreFuncEtc), \"__forceinline\", \"__inline\", \"_inline\", \"inline\", \"template\", \"operator\", \"static_assert\");\n\t\t\n\t\t_AddKeywords(new _Keyword(_KeywordT.PubPrivProt), \"private\", \"protected\", \"public\");\n\t\t\n\t\t//_AddKeywords(new _Keyword(_KeywordT.Ignore), \"__unaligned\", \"__w64\", \"_w64\", \"volatile\", \"explicit\", \"friend\", \"mutable\", \"static\", and so on); //removed in script\n\t\t//_AddKeywords(k = new _Keyword(_KeywordT.Declspec), \"__declspec\", \"_declspec\"); //removed in script\n\t\t\n\t\t_AddKeyword(\"uuid\", k = new _Keyword(cannotStartStatement: true)); //was '__declspec(uuid', replaced in script\n\t\t\n\t\t_AddKeywords(new _CppType(\"sbyte\", 1, false), \"__int8\", \"char\");\n\t\t_AddKeywords(new _CppType(\"short\", 2, false), \"__int16\", \"short\");\n\t\t_AddKeywords(new _CppType(\"int\", 4, false), \"__int32\", \"int\", \"long\");\n\t\t_AddKeyword(\"__int64\", new _CppType(\"long\", 8, false));\n\t\t_AddKeyword(\"double\", new _CppType(\"double\", 8, false));\n\t\t_AddKeyword(\"float\", new _CppType(\"float\", 4, false));\n\t\t_AddKeywords(new _CppType(\"char\", 2, true), \"__wchar_t\", \"wchar_t\", \"char16_t\");\n\t\t//our preprocessor script replaced 'unsigned type' to 'u$type'\n\t\t//also the script replaced 'signed type' to 'type, 'long long' to '__int64', 'long double' to 'double'\n\t\t_AddKeywords(new _CppType(\"byte\", 1, true), \"u$__int8\", \"u$char\");\n\t\t_AddKeywords(new _CppType(\"ushort\", 2, true), \"u$__int16\", \"u$short\");\n\t\t_AddKeywords(new _CppType(\"uint\", 4, true), \"u$__int32\", \"u$int\", \"u$long\", \"char32_t\");\n\t\t_AddKeyword(\"u$__int64\", new _CppType(\"ulong\", 8, true));\n\t\t_AddKeyword(\"bool\", new _CppType(\"bool\", 1, false));\n\t\t_AddKeyword(\"void\", new _CppType(\"void\", 0, false));\n\t\t//_AddKeywords(new _Keyword(), \"__m128\", \"__m128d\", \"__m128i\"); //our script defined these as struct, because C# does not have a matching type\n\t\t//_AddKeywords(new _Keyword(), \"__m64\"); //not used in SDK\n\t\t_AddKeyword(\"auto\", new _CppType(\"var\", 1, false)); //has two meanings depending on compiler options. The old auto is a local variable, and we will not encounter it because we skip function bodies. The new auto is like C# var; it can also be applied to global variables too, unlike in C#; we'll ignore all variable declarations.\n\t\t_AddKeyword(\"IntPtr\", new _CppType(\"IntPtr\", _is32bit ? 4 : 8, false));\n\t\t\n\t\t_AddSymbol(\"LPARAM\", _sym_LPARAM = new _Struct(\"LPARAM\", false), 0);\n\t\t_AddSymbol(\"HWND\", _sym_wnd = new _Struct(\"wnd\", false), 0);\n\t}\n\t\n\t_Struct _sym_LPARAM, _sym_wnd;\n\t\n\tvoid _ConvertAll(int nestLevel) {\n\t\tfor (; _i < _nTokUntilDefUndef; _i++) {\n\t\t\t//print.it(_TokToString(_i));\n\t\t\tchar* s = T(_i); char c = *s;\n\t\t\tif (_IsCharIdentStart(c)) {\n\t\t\t\t_Statement();\n\t\t\t} else if (c == '`') { //was #pragma pack\n\t\t\t\t_PragmaPack();\n\t\t\t} else if (c != ';' && c != '}') {\n#if TEST_SMALL\n\t\t\t\t\tif(c == '/' && s[1] == '/') return;\n#endif\n\t\t\t\t//print.it(_i);\n\t\t\t\t_Err(_i, $\"unexpected: {_TokToString(_i)}\");\n\t\t\t}\n\t\t}\n\t\t\n\t\t//info: script converted all #define/#undef to `d/`u and placed at the end\n\t\t_i++; //skip '\\0' token inserted before first `d/`u token\n\t\tfor (; _i < _nTok; _i++) {\n\t\t\tchar* s = T(_i);\n\t\t\tif (*s == '`') { //was #define, #undef\n\t\t\t\t_DefineUndef();\n\t\t\t} else {\n#if TEST_SMALL\n\t\t\t\t\tif(*s == '/' && s[1] == '/') return;\n#endif\n\t\t\t\t//print.it(_i);\n\t\t\t\t_Err(_i, $\"unexpected: {_TokToString(_i)}\");\n\t\t\t}\n\t\t}\n\t}\n\t\n\tvoid _Statement() {\n\t\t//try {\n\t\tg0:\n\t\t_Symbol x = _FindSymbol(_i, true);\n\t\tvar k = x as _Keyword;\n\t\tif (k != null) {\n\t\t\tif (k.cannotStartStatement) _Err(_i, \"unexpected\");\n\t\t\t\n\t\t\tif (k.kwType == _KeywordT.TypeDecl) {\n\t\t\t\t//is forward decl like 'struct X* Func(...);'?\n\t\t\t\tif (!_TokIsChar(_i, 't') && _TokIsChar(_i + 2, '*', '&')) {\n\t\t\t\t\t_InlineForwardDeclaration();\n\t\t\t\t\tgoto g0;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvar ftd = new _FINDTYPEDATA(); //just for ref parameter\n\t\t\t\t_DeclareType(ref ftd, true);\n\t\t\t} else if (k.kwType == _KeywordT.IgnoreFuncEtc) {\n\t\t\t\t_SkipStatement();\n\t\t\t\treturn;\n\t\t\t} else if (_TokIs(_i, \"extern\")) {\n\t\t\t\tif (_TokIsChar(_i + 1, '\"')) { //extern \"C\"\n\t\t\t\t\t_i++;\n\t\t\t\t\tif (_TokIsChar(_i + 1, '{')) _i++;\n\t\t\t\t\treturn;\n\t\t\t\t} else { //extern T X\n\t\t\t\t\tif (_TokIs(++_i, \"const\")) _i++;\n\t\t\t\t\tif (!_ExternConst()) _SkipStatement();\n\t\t\t\t}\n\t\t\t} else if (_TokIs(_i, \"const\")) {\n\t\t\t\t_i++;\n\t\t\t\tif (!_ExternConst(true)) _SkipStatement();\n\t\t\t} else {\n\t\t\t\t//can be:\n\t\t\t\t//namespace\n\t\t\t\t\n\t\t\t\t_Err(_i, \"unexpected\");\n\t\t\t}\n\t\t\tif (!_TokIsChar(_i, ';')) _Err(_i, \"unexpected\");\n\t\t\t\n\t\t} else { //a type\n\t\t\t_DeclareFunction();\n\t\t}\n\t\t//}\n\t\t//catch(ConverterException e) {\n\t\t//\t//print.it(e);\n\t\t//\twnd.findFast(null, \"QM_Editor\").SendS(Api.WM_SETTEXT, 1, $\"M \\\"api_converter_error\\\" A(||) {e.Message}||{_cppFile}||{e.Offset}\");\n\t\t//\tif(dialog.show(\"Error. Skip statement and continue?\", e.Message, \"Yes|No\").ButtonName != \"Yes\") throw e as Exception;\n\t\t//\t_SkipStatement();\n\t\t//         }\n\t}\n\t\n\t/// <summary>\n\t/// Skips current statement.\n\t/// _i can be at any place in the statement except in parts enclosed in (), [] or {}.\n\t/// Finally _i will be at the ending ';' or '}' (if '}' is not followed by ';').\n\t/// </summary>\n\t/// <param name=\"debugShow\"></param>\n\tvoid _SkipStatement(bool debugShow = false) {\n#if DEBUG\n\t\tint i0 = _i;\n#endif\n\t\tgk1:\n\t\twhile (!_TokIsChar(_i, \"({;[\")) _i++;\n\t\tif (!_TokIsChar(_i, ';')) {\n\t\t\t_SkipEnclosed();\n\t\t\tif (!_TokIsChar(_i, '}')) goto gk1; //skip any number of (enclosed) or [enclosed] parts, else skip single {enclosed} part\n\t\t\tif (_TokIsChar(_i + 1, ';')) _i++;\n\t\t}\n#if DEBUG\n\t\tif (debugShow) {\n\t\t\tstring s = new(T(i0), 0, (int)(T(_i + 1) - T(i0)));\n\t\t\tprint.it($\"<><c 0xff>skipped:</c>\\r\\n{s}\");\n\t\t}\n#endif\n\t}\n\t\n\tvoid _PragmaPack() {\n\t\tconst string sErr1 = \"expected (push), (pop), (n), (push, n), (pop, n) or ()\";\n\t\tchar* s0 = T(++_i);\n\t\tint nArg = _GetParameters(_params), pack = 0, pushPop = 0;\n\t\t\n\t\tif (nArg == 0) {\n\t\t\tpack = 8;\n\t\t} else if (nArg > 2) {\n\t\t\t_Err(s0, sErr1);\n\t\t} else {\n\t\t\tif (_params[0].nTok != 1 || (nArg > 1 && _params[1].nTok != 1)) _Err(s0, sErr1);\n\t\t\tint i = _params[0].iTok;\n\t\t\tchar* s = T(i);\n\t\t\t\n\t\t\tif (_TokIs(i, \"push\")) pushPop = 1;\n\t\t\telse if (_TokIs(i, \"pop\")) pushPop = -1;\n\t\t\telse if (nArg == 2) _Err(s0, sErr1);\n\t\t\telse pack = -1;\n\t\t\t\n\t\t\tif (nArg == 2) {\n\t\t\t\ts = T(_params[1].iTok);\n\t\t\t\tpack = -1;\n\t\t\t}\n\t\t\t\n\t\t\tif (pack < 0) { //a pack value must be specified\n\t\t\t\tpack = Api.strtoi(s);\n\t\t\t\tif (pack != 1 && pack != 2 && pack != 4 && pack != 8 && pack != 16)\n\t\t\t\t\t_Err(s, \"expected 1, 2, 4, 8 or 16\");\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (pushPop > 0) _packStack.Push(_pack); else if (pushPop < 0 && _packStack.Count > 0) _pack = _packStack.Pop();\n\t\tif (pack != 0) _pack = pack;\n\t\t//print.it($\"pushPop={pushPop}, pack={pack},    _pack={_pack}, _packStack.Count={_packStack.Count}\");\n\t}\n\t\n\tbool _ExternConst(bool isJustConst = false) {\n\t\tif (!(_TokIsIdent(_i) && _TokIsIdent(_i + 1))) return false; //must be TYPE name\n\t\tint what = 0;\n\t\tif (_TokIsChar(_i + 2, ';')) what = 1;\n\t\telse if (isJustConst && _TokIsChar(_i + 2, '=')) what = 2;\n\t\telse return false;\n\t\t\n\t\t//get type\n\t\t_Symbol x;\n\t\tif (!_TryFindSymbol(_i, out x, false)) return false;\n\t\tif (0 != _Unalias(_i, ref x)) return false;\n\t\t_i++;\n\t\t\n\t\tif (what == 1) {\n\t\t\t//we need only GUID\n\t\t\tif (x.csTypename != \"GUID\") return false;\n\t\t\t\n\t\t\tstring name = _TokToString(_i), data;\n\t\t\tif (!_guids.TryGetValue(name, out data)) {\n\t\t\t\t//print.it(name);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t\n\t\t\tif (name.Ends(\"A\") && char.IsLower(name[name.Length - 2])) {\n\t\t\t\t//print.it(name);\n\t\t\t\treturn false;\n\t\t\t} else if (name.Ends(\"W\") && char.IsLower(name[name.Length - 2])) {\n\t\t\t\t//print.it(name);\n\t\t\t\tname = name.Remove(name.Length - 1);\n\t\t\t}\n\t\t\t\n\t\t\tif (!_guidsAdded.Add(name)) return false; //prevent duplicates\n\t\t\t\n\t\t\t_sbVar.AppendFormat(\"\\r\\ninternal static Guid {0} = new({1});\\r\\n\", name, data);\n\t\t\t\n\t\t\t_i++;\n\t\t} else { //C++ const constant\n\t\t\tvar ct = x as _CppType;\n\t\t\tif (ct == null) {\n\t\t\t\t//_Err(_i, \"example\");\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t\n\t\t\tint iName = _i++, iValue = ++_i;\n\t\t\twhile (!_TokIsChar(_i, ';')) _i++;\n\t\t\tstring name = _TokToString(iName);\n\t\t\t\n\t\t\t_ExpressionResult r = _Expression(iValue, _i, name);\n\t\t\t//print.it(ct.csTypename, r.typeS, r.valueS);\n\t\t\tif (r.typeS == null) return false;\n\t\t\t\n\t\t\t_enumValues[_tok[iName]] = r.valueI;\n\t\t\t\n\t\t\t__sbDef.Clear();\n\t\t\t__sbDef.AppendFormat(\"internal const {0} {1} = {2};\", ct.csTypename, name, r.valueS);\n\t\t\t//print.it(__sbDef);\n\t\t\t_cppConst.Add(__sbDef.ToString());\n\t\t}\n\t\treturn true;\n\t}\n\t\n\tDictionary<string, string> _guids; //all GUID extracted from .lib files\n\tHashSet<string> _guidsAdded = new(); //already declared GUID names, to prevent duplicate declarations\n\t\n\tvoid _InitMaps() {\n\t\t//get dll function names extracted from SDK lib files and system dlls\n\t\t_funcDllMap = new Dictionary<string, string>();\n\t\tstring[] a = File.ReadAllLines(@\"C:\\code\\au\\Other\\Api\\DllMap.txt\");\n\t\tforeach (var s in a) {\n\t\t\tint i = s.IndexOf(' ');\n\t\t\t_funcDllMap.Add(s.Substring(0, i), s.Substring(i + 1));\n\t\t}\n\t\t\n\t\t//get GUIDs extracted from SDK lib files\n\t\t_guids = new Dictionary<string, string>();\n\t\tforeach (var s in File.ReadAllLines(@\"C:\\code\\au\\Other\\Api\\GuidMap.txt\")) {\n\t\t\t//print.it(s);\n\t\t\tint i = s.IndexOf(' ');\n\t\t\tstring sn = s.Substring(0, i), sd = s.Substring(i + 1), sOld;\n\t\t\tif (_guids.TryGetValue(sn, out sOld)) {\n\t\t\t\tif (sd == sOld) continue;\n\t\t\t\t//print.it(name, sOld, sd);\n\t\t\t\t//continue;\n\t\t\t\t//several in SDK, the second ones are correct\n\t\t\t\t_guids.Remove(sn);\n\t\t\t}\n\t\t\t_guids.Add(sn, sd);\n\t\t}\n\t\t\n\t\t//add enum values for some keyword literals etc\n\t\t_enumValues.Add(_TokenFromString(\"false\"), 0);\n\t\t_enumValues.Add(_TokenFromString(\"true\"), 1);\n\t\t_enumValues.Add(_TokenFromString(\"nullptr\"), 0);\n\t\t_enumValues.Add(_TokenFromString(\"NULL\"), 0); //clang ignores #define NULL\n\t}\n\t\n\t//used to recognize forward-declared interfaces\n\tHashSet<string> _interfaces = new();\n\t\n\tvoid _InitInterfaces() {\n\t\t_interfaces.Add(\"IUnknown\");\n\t\t_interfaces.Add(\"IDispatch\");\n\t\tfor (int i = 2; i < _nTokUntilDefUndef; i++) {\n\t\t\tif (_TokIsChar(i, 'u') && _TokIs(i, \"uuid\")) {\n\t\t\t\tif (!_TokIsChar(i + 5, ':')) continue; //class or IUnknown\n\t\t\t\tif (!_TokIs(i - 1, \"struct\") && !_TokIs(i - 1, \"__interface\")) continue; //unexpected\n\t\t\t\ti += 4;\n\t\t\t\t//print.it(_tok[i]);\n\t\t\t\t_interfaces.Add(_TokToString(i));\n\t\t\t}\n\t\t}\n\t}\n\t\n\tHashSet<string> _csKeywords;\n\t\n\tvoid _InitCsKeywords() {\n\t\t//we need only those that are not C/C++ keywords. Also don't need contextual keywords.\n\t\t_csKeywords = new HashSet<string>() { \"abstract\", \"as\", \"base\", \"byte\", \"checked\", \"decimal\", \"delegate\", \"event\", \"explicit\", \"finally\", \"fixed\", \"foreach\", \"implicit\", \"in\", \"interface\", \"internal\", \"is\", \"lock\", \"null\", \"object\", \"out\", \"override\", \"params\", \"readonly\", \"ref\", \"sbyte\", \"sealed\", \"stackalloc\", \"string\", \"typeof\", \"uint\", \"ulong\", \"unchecked\", \"unsafe\", \"ushort\" };\n\t}\n}\n"
  },
  {
    "path": "Scripts/old/Windows SDK to C#/@SDK converter/Expr.cs",
    "content": "﻿namespace SdkConverter;\n\nunsafe partial class Converter {\n\t_ExpressionResult _Expression(int iFrom, int iTo, string debugConstName = null) {\n\t\tDebug.Assert(iTo > iFrom);\n\t\t\n\t\tint nTok = iTo - iFrom, iLast = iTo - 1;\n\t\t\n\t\tstring sValue, sType; bool isHex = false;\n\t\t\n\t\t//if(debugConstName == \"[]\") return new _ExpressionResult(_TokToString(iFrom, iTo), null, true);\n\t\t//print.it($\"{debugConstName} {_TokToString(iFrom, iTo)}\");\n\t\t\n\t\t//Unenclose whole expression (not parts).\n\t\t\n\t\twhile (nTok >= 3 && _TokIsChar(iFrom, '(') && _TokIsChar(iLast, ')')) {\n\t\t\tif (nTok == 3 || _SkipEnclosed(iFrom) == iLast) { iFrom++; iLast--; iTo--; nTok -= 2; } else break;\n\t\t}\n\t\t\n\t\t//Simple cases.\n\t\t\n\t\tif (nTok == 1) {\n\t\t\tulong value; _OP type;\n\t\t\t\n\t\t\tif (_TokIsNumber(iFrom)) {\n\t\t\t\tif (__ExprNumber(iFrom, out value, out type, ref isHex)) {\n\t\t\t\t\ttype = __ExprMakeUintIfHex(type, isHex);\n\t\t\t\t\tsValue = __ExprValueToString(value, type);\n\t\t\t\t\tsType = __ExprTypeToString(type);\n\t\t\t\t\treturn new _ExpressionResult(sValue, sType, false, value);\n\t\t\t\t} else {\n\t\t\t\t\tsValue = _TokToString(iFrom);\n\t\t\t\t\tsType = sValue.Ends(\"f\", true) ? \"float\" : \"double\";\n\t\t\t\t\treturn new _ExpressionResult(sValue, sType, false);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t}\n\t\t\t\n\t\t\tif (_TokIsChar(iFrom, '\"')) {\n\t\t\t\treturn new _ExpressionResult(_TokToString(iFrom), \"string\", false);\n\t\t\t}\n\t\t\tif (_TokIsChar(iFrom, '\\'')) {\n\t\t\t\t//is like 'ABCD'?\n\t\t\t\tint len = _tok[iFrom].len - 2;\n\t\t\t\tif (len > 1 && len <= 4) {\n\t\t\t\t\tchar* s = T(iFrom) + 1;\n\t\t\t\t\tif (*s != '\\\\') {\n\t\t\t\t\t\t//print.it(_DebugGetLine(iFrom));\n\t\t\t\t\t\tuint k = 0; for (int j = 0; j < len; j++) k = (k << 8) | ((uint)s[j] & 0xff);\n\t\t\t\t\t\treturn new _ExpressionResult($\"0x{k:X}\", \"uint\", false, k);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t//string k=_TokToString(iFrom).Re\n\t\t\t\treturn new _ExpressionResult(_TokToString(iFrom), \"char\", false);\n\t\t\t}\n\t\t\t\n\t\t\tif (_TokIsIdent(iFrom)) {\n\t\t\t\tif (_EnumFindValue(iFrom, out value, out type)) {\n\t\t\t\t\tsValue = __ExprValueToString(value, type);\n\t\t\t\t\tsType = __ExprTypeToString(type);\n\t\t\t\t\treturn new _ExpressionResult(sValue, sType, false, value);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t//print.it($\"<><c 0xff0000>{_TokToString(iFrom, iTo)}</c>\");\n\t\t\t} else {\n\t\t\t\t//print.it($\"<><c 0xff0000>{_TokToString(iFrom, iTo)}</c>\"); //0 in SDK\n\t\t\t}\n\t\t\t\n\t\t\treturn new _ExpressionResult(_TokToString(iFrom), null, true);\n\t\t}\n\t\t\n\t\t//Calculate expression.\n\t\t\n\t\t//print.it(debugConstName, _TokToString(iFrom, iTo));\n\t\t\n\t\t//Part 1 - convert the expression from normal infix form to RPN form which is easy to calculate.\n\t\t//Use the shunting-yard algorithm: https://en.wikipedia.org/wiki/Shunting-yard_algorithm\n\t\t//Put operands and operators in _eRPN, which is the \"output queue\" of the algorithm.\n\t\t\n\t\tint nRPN = 0; //number of elements in the output queue\n\t\tint nOp = 0; //number of elements in the operator stack\n\t\tint i;\n\t\tbool wasOperand = false;\n\t\t\n\t\t////Debug\n\t\t//if(debugConstName == \"WTS_CURRENT_SERVER\") {\n\t\t//\tprint.it(debugConstName);\n\t\t//\tint stop = 0;\n\t\t//}\n\t\t\n\t\tfor (i = iFrom; i < iTo; i++) {\n\t\t\tint iTok = i;\n\t\t\t_OP op;\n\t\t\tchar c = *T(i);\n\t\t\t\n\t\t\tif (_IsCharIdent(c) || c == '\\'') { //digit, identifier or 'character'\n\t\t\t\tif (wasOperand) goto gFail; //cannot be 2 operands\n\t\t\t\twasOperand = true;\n\t\t\t\t\n\t\t\t\t//If the token is a number, then add it to the output queue.\n\t\t\t\t\n\t\t\t\tulong value; _OP type = _OP.OperandInt;\n\t\t\t\t\n\t\t\t\tif (_IsCharDigit(c)) { //number\n\t\t\t\t\tif (!__ExprNumber(i, out value, out type, ref isHex)) {\n\t\t\t\t\t\t//_Err(i, \"bad number\"); //0 in SDK\n\t\t\t\t\t\tgoto gFail;\n\t\t\t\t\t}\n\t\t\t\t} else if (c == '\\'') {\n\t\t\t\t\tchar* s = T(i) + 1;\n\t\t\t\t\tint len = _tok[i].len - 2;\n\t\t\t\t\tif (len > 1) {\n\t\t\t\t\t\tif (len == 2 && *s == '\\\\') s++;\n\t\t\t\t\t\telse goto gFail;\n\t\t\t\t\t}\n\t\t\t\t\tvalue = *s;\n\t\t\t\t} else { //identifier\n\t\t\t\t\tif (_TokIs(i, \"sizeof\")) {\n\t\t\t\t\t\tif (!_TokIsChar(++i, '(')) goto gFail;\n\t\t\t\t\t\tint rightParen = __ExprIsType(++i);\n\t\t\t\t\t\tif (rightParen != i + 1) goto gFail; //if not like a type, or with *\n\t\t\t\t\t\tif (!__ExprSizeof(i, out value)) goto gFail;\n\t\t\t\t\t\ti++;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (!_EnumFindValue(i, out value, out type)) goto gFail;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t_eRPN[nRPN++] = new _EVAL(i, type, value);\n\t\t\t\t\n\t\t\t} else if (_IsCharOperator(c) || c == '(') { //operator or cast or (\n\t\t\t\tif (c == '(') {\n\t\t\t\t\t//is cast?\n\t\t\t\t\tint rightParen = __ExprIsType(i + 1);\n\t\t\t\t\tbool isCast = rightParen > 0;\n\t\t\t\t\tif (isCast && rightParen - i == 2 && _enumValues.ContainsKey(_tok[i + 1])) isCast = false; //is '(enumValue)'?\n\t\t\t\t\tif (isCast) { //cast\n\t\t\t\t\t\tiTok = i + 1;\n\t\t\t\t\t\ti = rightParen;\n\t\t\t\t\t\top = _OP.Cast;\n\t\t\t\t\t\t\n\t\t\t\t\t\t//cast is an operator\n\t\t\t\t\t} else {\n\t\t\t\t\t\tiTok = i;\n\t\t\t\t\t\top = _OP.LeftParen;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tchar c2 = *T(i + 1);\n\t\t\t\t\top = 0;\n\t\t\t\t\t\n\t\t\t\t\tif (wasOperand) //this must be a binary operator\n\t\t\t\t\t{\n\t\t\t\t\t\t//note: cannot be +=, ++ etc because they can be used only with variables, not with constants.\n\t\t\t\t\t\t\n\t\t\t\t\t\twasOperand = false;\n\t\t\t\t\t\tswitch (c) {\n\t\t\t\t\t\tcase '+': op = _OP.Plus; break;\n\t\t\t\t\t\tcase '-': op = _OP.Minus; break;\n\t\t\t\t\t\tcase '*': op = _OP.Mul; break;\n\t\t\t\t\t\tcase '/': op = _OP.Div; break;\n\t\t\t\t\t\tcase '%': op = _OP.Mod; break;\n\t\t\t\t\t\tcase '&':\n\t\t\t\t\t\t\tif (c2 == '&') { i++; op = _OP.LogAnd; } else op = _OP.BitAnd;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase '|':\n\t\t\t\t\t\t\tif (c2 == '|') { i++; op = _OP.LogOr; } else op = _OP.BitOr;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase '^': op = _OP.BitXor; break;\n\t\t\t\t\t\tcase '<':\n\t\t\t\t\t\t\tif (c2 == '<') { i++; op = _OP.BitLshift; } else if (c2 == '=') { i++; op = _OP.LtEq; } else op = _OP.Lt;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase '>':\n\t\t\t\t\t\t\tif (c2 == '>') { i++; op = _OP.BitRshift; } else if (c2 == '=') { i++; op = _OP.GtEq; } else op = _OP.Gt;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase '=':\n\t\t\t\t\t\t\tif (c2 == '=') { i++; op = _OP.CompareEq; } else goto gFail;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase '!':\n\t\t\t\t\t\t\tif (c2 == '=') { i++; op = _OP.CompareNotEq; } else goto gFail;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t//case '?':\n\t\t\t\t\t\t//\top = _OPERATOR.Ternary;\n\t\t\t\t\t\t//\tbreak;\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tgoto gFail;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else { //this must be a prefix operator\n\t\t\t\t\t\t\n\t\t\t\t\t\t//note: cannot be ++, & etc because they can be used only with variables, not with constants.\n\t\t\t\t\t\t\n\t\t\t\t\t\tswitch (c) {\n\t\t\t\t\t\tcase '+': continue;\n\t\t\t\t\t\tcase '-': op = _OP.UnaryMinus; break;\n\t\t\t\t\t\tcase '!': op = _OP.UnaryLogNot; break;\n\t\t\t\t\t\tcase '~': op = _OP.UnaryBitNot; break;\n\t\t\t\t\t\tdefault: goto gFail;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t//If the token is an operator (op), then:\n\t\t\t\tif (op != _OP.LeftParen) {\n\t\t\t\t\t//While there is an operator (op2) at the top of the stack, and either\n\t\t\t\t\t//\top is left-associative and its precedence is less than or equal to that of op2, or\n\t\t\t\t\t//\top is right associative, and has precedence less than that of o2,\n\t\t\t\t\t//\t\tpop op2 off the stack, onto the output queue.\n\t\t\t\t\tfor (; nOp > 0; nOp--) {\n\t\t\t\t\t\tint n = nOp - 1;\n\t\t\t\t\t\t_OP op2 = _eOp[n].op;\n\t\t\t\t\t\tif (op2 == _OP.LeftParen) break; //is an operator at the top of the stack?\n\t\t\t\t\t\tbool pop = false, opRTL = (op & _OP._FlagRightToLeft) != 0;\n\t\t\t\t\t\tint prec1 = (int)(op & _OP._MaskPrecedence), prec2 = (int)(op2 & _OP._MaskPrecedence);\n\t\t\t\t\t\t//print.it(\"  \", op, prec1, opRTL, op2, prec2);\n\t\t\t\t\t\t\n\t\t\t\t\t\tif (!opRTL) pop = prec1 >= prec2; //op is left-associative and its precedence is less than or equal to that of op2\n\t\t\t\t\t\telse pop = prec1 > prec2; //op is right associative, and has precedence less than that of o2\n\t\t\t\t\t\t\n\t\t\t\t\t\tif (!pop) break;\n\t\t\t\t\t\t_eRPN[nRPN++] = _eOp[n];\n\t\t\t\t\t}\n\t\t\t\t} //Else if the token is a left parenthesis (i.e. \"(\"), then push it onto the stack.\n\t\t\t\t\n\t\t\t\t//push op to the stack\n\t\t\t\t_eOp[nOp++] = new _EVAL(iTok, op);\n\t\t\t\t\n\t\t\t} else if (c == ')') {\n\t\t\t\t//If the token is a right parenthesis (i.e. \")\"):\n\t\t\t\t//\tUntil the token at the top of the stack is a left parenthesis, pop operators off the stack onto the output queue.\n\t\t\t\tfor (; nOp > 0; nOp--) {\n\t\t\t\t\tint n = nOp - 1;\n\t\t\t\t\tif (_eOp[n].op == _OP.LeftParen) break;\n\t\t\t\t\t_eRPN[nRPN++] = _eOp[n];\n\t\t\t\t}\n\t\t\t\t//If the stack runs out without finding a left parenthesis, then there are mismatched parentheses.\n\t\t\t\tif (nOp == 0) goto gFail;\n\t\t\t\t//Pop the left parenthesis from the stack, but not onto the output queue.\n\t\t\t\tnOp--;\n\t\t\t} else {\n\t\t\t\t//\"string\" //0 in SDK\n\t\t\t\tgoto gFail;\n\t\t\t}\n\t\t}\n\t\t\n\t\t//When there are no more tokens to read:\n\t\t//\tWhile there are still operator tokens in the stack:\n\t\t//\t\tIf the operator token on the top of the stack is a parenthesis: error, there are mismatched parentheses.\n\t\t//\t\tElse pop the operator onto the output queue.\n\t\tfor (; nOp > 0; nOp--) {\n\t\t\tint n = nOp - 1;\n\t\t\tif (_eOp[n].op == _OP.LeftParen) goto gFail;\n\t\t\t_eRPN[nRPN++] = _eOp[n];\n\t\t}\n\t\t\n\t\t////Debug\n\t\t//if(debugConstName == \"WTS_CURRENT_SERVER\") {\n\t\t//\tvar deb = new StringBuilder(debugConstName);\n\t\t//\tdeb.Append(\" = \");\n\t\t//\tdeb.Append(_TokToString(iFrom, iTo));\n\t\t//\tdeb.Append(\"  -> \");\n\t\t//\tfor(i = 0; i < nRPN; i++) {\n\t\t//\t\tdeb.Append(\" \");\n\t\t//\t\tif(_eRPN[i].op < _OP._FirstOperator) deb.Append(_eRPN[i].value);\n\t\t//\t\telse deb.Append(_eRPN[i].op);\n\t\t//\t}\n\t\t//\tprint.it(deb);\n\t\t\n\t\t//\tint stop = 0;\n\t\t//}\n\t\t\n\t\t//Part 2 - calculate the RPN expression.\n\t\t//Use the postfix algorithm: https://en.wikipedia.org/wiki/Reverse_Polish_notation\n\t\t\n\t\tint nCalc = 0; //_eCalc stack length\n\t\tfor (i = 0; i < nRPN; i++) {\n\t\t\tif (_eRPN[i].op < _OP._FirstOperator) { //operand\n\t\t\t\t\n\t\t\t\t//If the token is a value (operand): push it onto the stack.\n\t\t\t\t_eCalc[nCalc++] = _eRPN[i];\n\t\t\t} else { //operator\n\t\t\t\t\n\t\t\t\t//It is already known that the operator takes n arguments.\n\t\t\t\tint nOperands = (int)(_eRPN[i].op & _OP._MaskNOperands) >> 8;\n\t\t\t\t//If there are fewer than n values on the stack: error, the user has not input sufficient values in the expression.\n\t\t\t\tif (nCalc < nOperands) goto gFail;\n\t\t\t\t\n\t\t\t\tif (nOperands == 1) {\n\t\t\t\t\t\n\t\t\t\t\t//Pop the top n values from the stack.\n\t\t\t\t\t//A = _eCalc[--nCalc]; //operand (we change it in-place without pop/push)\n\t\t\t\t\t\n\t\t\t\t\t//Evaluate the operator, with the values as arguments.\n\t\t\t\t\t_OP op = _eRPN[i].op;\n\t\t\t\t\tif (op == _OP.Cast) {\n\t\t\t\t\t\tif (!__ExprCalcCast(_eRPN[i].iTok, ref _eCalc[nCalc - 1])) goto gFail;\n\t\t\t\t\t} else {\n\t\t\t\t\t\t__ExprCalcOperatorUnary(op, ref _eCalc[nCalc - 1]);\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t} else { //2 operands\n\t\t\t\t\t\n\t\t\t\t\t//Pop the top n values from the stack.\n\t\t\t\t\t_EVAL B = _eCalc[--nCalc]; //rigt operand\n\t\t\t\t\t//A = _eCalc[--nCalc]; //left operand (we change it in-place without pop/push)\n\t\t\t\t\t\n\t\t\t\t\t//Evaluate the operator, with the values as arguments.\n\t\t\t\t\t__ExprCalcOperatorBinary(_eRPN[i].op, ref _eCalc[nCalc - 1], B);\n\t\t\t\t\t\n\t\t\t\t\t//__ExprDebugOutValue(_eCalc[nCalc - 1]);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t//Push the returned results, if any, back onto the stack.\n\t\t\t\t//_eCalc[nCalc++] = A; //we change it in-place without pop/push\n\t\t\t}\n\t\t}\n\t\t\n\t\t//If there is only one value in the stack, it is the result of the calculation.\n\t\t//Otherwise, there are more values in the stack: error, the user input has too many values.\n\t\tif (nCalc != 1) goto gFail;\n\t\t_OP rType = __ExprMakeUintIfHex(_eCalc[0].op, isHex);\n\t\tsValue = __ExprValueToString(_eCalc[0].value, rType);\n\t\tsType = __ExprTypeToString(rType);\n\t\t\n\t\treturn new _ExpressionResult(sValue, sType, false, _eCalc[0].value);\n\t\t\n\t\tgFail:\n\t\t//print.it($\"<><c 0xff>{debugConstName}    {_TokToString(iFrom, iTo)}</c>\");\n\t\t//if(++_debugNFailed > 10) _Err(iFrom, \"debug\");\n\t\treturn new _ExpressionResult(_TokToString(iFrom, iTo), null, true);\n\t\t\n\t\t//never mind: need functions:\n\t\t//LongToHandle\n\t\t//HRESULT_FROM_WIN32\n\t\t\n\t\t//never mind: sizeof pointers, IntPtr, nint and wnd.\n\t}\n\t\n\tvoid __ExprDebugOutValue(_EVAL v) {\n\t\tprint.it($\"    {v.op}, { __ExprValueToString(v.value, v.op)}\");\n\t}\n\t\n\t//int _debugNFailed;\n\t\n\t#region CALC_UNARY\n\t\n\tvoid __ExprCalcOperatorUnary(_OP op, ref _EVAL A) {\n\t\tswitch (A.op) {\n\t\tcase _OP.OperandInt: A.value = (ulong)__ExprCalcOperatorUnary(op, (int)A.value); break;\n\t\tcase _OP.OperandUint: A.value = __ExprCalcOperatorUnary(op, (uint)A.value); break;\n\t\tcase _OP.OperandLong: A.value = (ulong)__ExprCalcOperatorUnary(op, (long)A.value); break;\n\t\tcase _OP.OperandUlong: A.value = __ExprCalcOperatorUnary(op, A.value); break;\n\t\t}\n\t}\n\t\n\tint __ExprCalcOperatorUnary(_OP op, int A) {\n\t\tswitch (op) {\n\t\tcase _OP.UnaryMinus: return -A;\n\t\tcase _OP.UnaryLogNot: return A == 0 ? 0 : 1;\n\t\tcase _OP.UnaryBitNot: return ~A;\n\t\t}\n\t\tDebug.Assert(false);\n\t\treturn 0;\n\t}\n\t\n\tuint __ExprCalcOperatorUnary(_OP op, uint A) {\n\t\tswitch (op) {\n\t\tcase _OP.UnaryMinus: return (uint)-(int)A;\n\t\tcase _OP.UnaryLogNot: return A == 0U ? 0U : 1U;\n\t\tcase _OP.UnaryBitNot: return ~A;\n\t\t}\n\t\tDebug.Assert(false);\n\t\treturn 0;\n\t}\n\t\n\tlong __ExprCalcOperatorUnary(_OP op, long A) {\n\t\tswitch (op) {\n\t\tcase _OP.UnaryMinus: return -A;\n\t\tcase _OP.UnaryLogNot: return A == 0 ? 0 : 1;\n\t\tcase _OP.UnaryBitNot: return ~A;\n\t\t}\n\t\tDebug.Assert(false);\n\t\treturn 0;\n\t}\n\t\n\tulong __ExprCalcOperatorUnary(_OP op, ulong A) {\n\t\tswitch (op) {\n\t\tcase _OP.UnaryMinus: return (ulong)-(long)A;\n\t\tcase _OP.UnaryLogNot: return A == 0U ? 0U : 1U;\n\t\tcase _OP.UnaryBitNot: return ~A;\n\t\t}\n\t\tDebug.Assert(false);\n\t\treturn 0;\n\t}\n\t\n\t#endregion\n\t\n\t#region CALC_BINARY\n\t\n\tvoid __ExprCalcOperatorBinary(_OP op, ref _EVAL A, _EVAL B) {\n\t\tif (A.op < B.op) A.op = B.op;\n\t\tswitch (A.op) {\n\t\tcase _OP.OperandInt:\n\t\t\tA.value = (ulong)__ExprCalcOperatorBinary(op, (int)A.value, (int)B.value);\n\t\t\tbreak;\n\t\tcase _OP.OperandUint:\n\t\t\tA.value = __ExprCalcOperatorBinary(op, (uint)A.value, (uint)B.value);\n\t\t\tbreak;\n\t\tcase _OP.OperandLong:\n\t\t\tA.value = (ulong)__ExprCalcOperatorBinary(op, (long)A.value, (long)B.value);\n\t\t\tbreak;\n\t\tcase _OP.OperandUlong:\n\t\t\tA.value = __ExprCalcOperatorBinary(op, A.value, B.value);\n\t\t\tbreak;\n\t\t}\n\t}\n\t\n\tint __ExprCalcOperatorBinary(_OP op, int A, int B) {\n\t\tswitch (op) {\n\t\tcase _OP.Plus: return A + B;\n\t\tcase _OP.Minus: return A - B;\n\t\tcase _OP.Mul: return A * B;\n\t\tcase _OP.Div: return A / B;\n\t\tcase _OP.Mod: return A % B;\n\t\tcase _OP.BitLshift: return A << B;\n\t\tcase _OP.BitRshift: return A >> B;\n\t\tcase _OP.Lt: return A < B ? 1 : 0;\n\t\tcase _OP.LtEq: return A <= B ? 1 : 0;\n\t\tcase _OP.Gt: return A > B ? 1 : 0;\n\t\tcase _OP.GtEq: return A >= B ? 1 : 0;\n\t\tcase _OP.CompareEq: return A == B ? 1 : 0;\n\t\tcase _OP.CompareNotEq: return A != B ? 1 : 0;\n\t\tcase _OP.BitAnd: return A & B;\n\t\tcase _OP.BitOr: return A | B;\n\t\tcase _OP.BitXor: return A ^ B;\n\t\tcase _OP.LogAnd: return A != 0 && B != 0 ? 1 : 0;\n\t\tcase _OP.LogOr: return A != 0 || B != 0 ? 1 : 0;\n\t\t}\n\t\tDebug.Assert(false);\n\t\treturn 0;\n\t}\n\t\n\tuint __ExprCalcOperatorBinary(_OP op, uint A, uint B) {\n\t\tswitch (op) {\n\t\tcase _OP.Plus: return A + B;\n\t\tcase _OP.Minus: return A - B;\n\t\tcase _OP.Mul: return A * B;\n\t\tcase _OP.Div: return A / B;\n\t\tcase _OP.Mod: return A % B;\n\t\tcase _OP.BitLshift: return A << (int)B;\n\t\tcase _OP.BitRshift: return A >> (int)B;\n\t\tcase _OP.Lt: return A < B ? 1U : 0U;\n\t\tcase _OP.LtEq: return A <= B ? 1U : 0U;\n\t\tcase _OP.Gt: return A > B ? 1U : 0U;\n\t\tcase _OP.GtEq: return A >= B ? 1U : 0U;\n\t\tcase _OP.CompareEq: return A == B ? 1U : 0U;\n\t\tcase _OP.CompareNotEq: return A != B ? 1U : 0U;\n\t\tcase _OP.BitAnd: return A & B;\n\t\tcase _OP.BitOr: return A | B;\n\t\tcase _OP.BitXor: return A ^ B;\n\t\tcase _OP.LogAnd: return A != 0U && B != 0U ? 1U : 0U;\n\t\tcase _OP.LogOr: return A != 0U || B != 0U ? 1U : 0U;\n\t\t}\n\t\tDebug.Assert(false);\n\t\treturn 0;\n\t}\n\t\n\tlong __ExprCalcOperatorBinary(_OP op, long A, long B) {\n\t\tswitch (op) {\n\t\tcase _OP.Plus: return A + B;\n\t\tcase _OP.Minus: return A - B;\n\t\tcase _OP.Mul: return A * B;\n\t\tcase _OP.Div: return A / B;\n\t\tcase _OP.Mod: return A % B;\n\t\tcase _OP.BitLshift: return A << (int)B;\n\t\tcase _OP.BitRshift: return A >> (int)B;\n\t\tcase _OP.Lt: return A < B ? 1 : 0;\n\t\tcase _OP.LtEq: return A <= B ? 1 : 0;\n\t\tcase _OP.Gt: return A > B ? 1 : 0;\n\t\tcase _OP.GtEq: return A >= B ? 1 : 0;\n\t\tcase _OP.CompareEq: return A == B ? 1 : 0;\n\t\tcase _OP.CompareNotEq: return A != B ? 1 : 0;\n\t\tcase _OP.BitAnd: return A & B;\n\t\tcase _OP.BitOr: return A | B;\n\t\tcase _OP.BitXor: return A ^ B;\n\t\tcase _OP.LogAnd: return A != 0 && B != 0 ? 1 : 0;\n\t\tcase _OP.LogOr: return A != 0 || B != 0 ? 1 : 0;\n\t\t}\n\t\tDebug.Assert(false);\n\t\treturn 0;\n\t}\n\t\n\tulong __ExprCalcOperatorBinary(_OP op, ulong A, ulong B) {\n\t\tswitch (op) {\n\t\tcase _OP.Plus: return A + B;\n\t\tcase _OP.Minus: return A - B;\n\t\tcase _OP.Mul: return A * B;\n\t\tcase _OP.Div: return A / B;\n\t\tcase _OP.Mod: return A % B;\n\t\tcase _OP.BitLshift: return A << (int)B;\n\t\tcase _OP.BitRshift: return A >> (int)B;\n\t\tcase _OP.Lt: return A < B ? 1U : 0U;\n\t\tcase _OP.LtEq: return A <= B ? 1U : 0U;\n\t\tcase _OP.Gt: return A > B ? 1U : 0U;\n\t\tcase _OP.GtEq: return A >= B ? 1U : 0U;\n\t\tcase _OP.CompareEq: return A == B ? 1U : 0U;\n\t\tcase _OP.CompareNotEq: return A != B ? 1U : 0U;\n\t\tcase _OP.BitAnd: return A & B;\n\t\tcase _OP.BitOr: return A | B;\n\t\tcase _OP.BitXor: return A ^ B;\n\t\tcase _OP.LogAnd: return A != 0U && B != 0U ? 1U : 0U;\n\t\tcase _OP.LogOr: return A != 0U || B != 0U ? 1U : 0U;\n\t\t}\n\t\tDebug.Assert(false);\n\t\treturn 0;\n\t}\n\t\n\t#endregion\n\t\n\tbool __ExprCalcCast(int i, ref _EVAL A) {\n\t\t//print.it(_tok[i]);\n\t\t\n\t\t_Symbol x;\n\t\tif (!_TryFindSymbol(i, out x, false)) {\n\t\t\t//print.it(_tok[i]); //0 in SDK\n\t\t\t//_Err(i, \"unknown\");\n\t\t\treturn false;\n\t\t}\n\t\tif (0 != _Unalias(i, ref x) || _TokIsChar(i + 1, '*')) {\n\t\t\t//support expressions like '#define RT_BITMAP (LPWSTR)((ULONG_PTR)((WORD)(2)))'\n\t\t\tif (A.op <= _OP.OperandUint) {\n\t\t\t\tA.op = _OP.OperandInt;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\t//_Err(i, \"stop\"); //0 in SDK\n\t\t\treturn false;\n\t\t}\n\t\tvar ct = x as _CppType;\n\t\tif (ct == null) {\n\t\t\t//support LPARAM, HWND\n\t\t\tif (A.op > _OP.OperandUint) return false; //0 in SDK\n\t\t\tif (x == _sym_LPARAM || x == _sym_wnd) return true;\n\t\t\t//support enum, callback\n\t\t\tif (x is _Enum || x is _Callback) return true;\n\t\t\t//_Err(i, \"stop 2\"); //0 in SDK\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\tswitch (ct.sizeBytesCpp) {\n\t\tcase 8:\n\t\t\tif (ct.csTypename[0] == 'd') return false; //double\n\t\t\tif (ct.csTypename[0] == 'I') { //IntPtr\n\t\t\t\t//print.it(_DebugGetLine(i));\n\t\t\t\tif (A.op > _OP.OperandUint) return false; //0 in SDK\n\t\t\t\tbreak; //let be the same type as in 32-bit mode (no long). All in SDK are like #define HKEY_CURRENT_USER (IntPtr)0x80000001.\n\t\t\t}\n\t\t\tA.op = ct.isUnsigned ? _OP.OperandUlong : _OP.OperandLong;\n\t\t\treturn true;\n\t\tcase 4:\n\t\t\tif (ct.csTypename[0] == 'f') return false; //float\n\t\t\tA.value = (uint)A.value;\n\t\t\tbreak;\n\t\tcase 2:\n\t\t\tA.value = (ushort)A.value;\n\t\t\tbreak;\n\t\tcase 1:\n\t\t\tif (ct.csTypename == \"bool\") A.value = A.value == 0U ? 0U : 1U;\n\t\t\telse A.value = (byte)A.value;\n\t\t\tbreak;\n\t\tdefault: return false;\n\t\t}\n\t\t\n\t\tA.op = ct.isUnsigned ? _OP.OperandUint : _OP.OperandInt;\n\t\treturn true;\n\t}\n\t\n\t/// <summary>\n\t/// If token i is an identifier and is followed by 0 or more '*' characters and character ')', returns token index of the ')'.\n\t/// Else returns 0.\n\t/// </summary>\n\tint __ExprIsType(int i) {\n\t\tif (!_TokIsIdent(i)) return 0;\n\t\ti++;\n\t\twhile (_TokIsChar(i, '*')) i++;\n\t\tif (!_TokIsChar(i, ')')) return 0;\n\t\treturn i;\n\t}\n\t\n\tbool __ExprNumber(int i, out ulong value, out _OP type, ref bool isHex) {\n\t\tvalue = 0; type = _OP.OperandInt;\n\t\t\n\t\tchar* s = T(i), se;\n\t\tif (*s == '0') {\n\t\t\tif (s[1] == 'x' || s[1] == 'X') { value = strtoui64(s, &se); isHex = true; } //hex\n\t\t\telse if (s[1] == 'b' || s[1] == 'B') { value = strtoui64(s + 2, &se, 2); isHex = true; } //binary constant like 0b10101010\n\t\t\telse value = strtoui64(s, &se, 8); //oct\n\t\t} else value = strtoui64(s, &se);\n\t\t\n\t\tbool isUnsigned = false, isLong = false;\n\t\t\n\t\tint tlen = _tok[i].len;\n\t\tif (se < s + tlen) {\n\t\t\tif (*se == 'U' || *se == 'u') { se++; isUnsigned = true; }\n\t\t\tif (se < s + tlen) {\n\t\t\t\tif (*se == 'L' || *se == 'l') { se++; isLong = true; }\n\t\t\t\tif (se < s + tlen) return false; //eg a floating-point number\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (!isLong && value > uint.MaxValue) isLong = true;\n\t\tif (!isUnsigned) {\n\t\t\tif (isLong) isUnsigned = value > long.MaxValue;\n\t\t\telse isUnsigned = value > int.MaxValue;\n\t\t}\n\t\t\n\t\tif (isLong) type = isUnsigned ? _OP.OperandUlong : _OP.OperandLong; else if (isUnsigned) type = _OP.OperandUint;\n\t\treturn true;\n\t}\n\t\n\t[DllImport(\"msvcrt.dll\", EntryPoint = \"_wcstoui64\", CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern ulong strtoui64(char* s, char** endPtr = null, int radix = 0);\n\t\n\tstring __ExprTypeToString(_OP type) {\n\t\tDebug.Assert(type <= _OP.OperandUlong);\n\t\tswitch (type) {\n\t\tcase _OP.OperandInt: return \"int\";\n\t\tcase _OP.OperandUint: return \"uint\";\n\t\tcase _OP.OperandLong: return \"long\";\n\t\tdefault: return \"ulong\";\n\t\t}\n\t}\n\t\n\tstring __ExprValueToString(ulong value, _OP type) {\n\t\tDebug.Assert(type <= _OP.OperandUlong);\n\t\tswitch (type) {\n\t\tcase _OP.OperandInt: return ((int)value).ToString();\n\t\tcase _OP.OperandUint: return \"0x\" + ((uint)value).ToString(\"X\");\n\t\tcase _OP.OperandLong: return ((long)value).ToString();\n\t\tdefault: return \"0x\" + value.ToString(\"X\");\n\t\t}\n\t}\n\t\n\t_OP __ExprMakeUintIfHex(_OP type, bool isHex) {\n\t\tDebug.Assert(type <= _OP.OperandUlong);\n\t\tif (isHex) {\n\t\t\tif (type == _OP.OperandInt) return _OP.OperandUint;\n\t\t\tif (type == _OP.OperandLong) return _OP.OperandUlong;\n\t\t}\n\t\treturn type;\n\t}\n\t\n\tbool __ExprSizeof(int i, out ulong value) {\n\t\tvalue = 0;\n\t\t_Symbol x;\n\t\tif (!_TryFindSymbol(i, out x, true) || x is _Keyword) return false;\n\t\tif (0 != _Unalias(i, ref x)) return false;\n\t\t\n\t\tvar ct = x as _CppType;\n\t\tif (ct != null) {\n\t\t\tif (ct.csTypename[0] == 'I') return false; //IntPtr (variable size)\n\t\t\tvalue = ct.sizeBytesCpp;\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\tvar t = x as _Struct; if (t == null) return false;\n\t\tswitch (_TokToString(i)) {\n\t\tcase \"SID\": value = 12; break;\n\t\tcase \"IMAGE_SYMBOL_EX\": value = 20; break; //in SDK used as array size\n\t\tdefault: return false; //most have pointer members (variable size)\n\t\t}\n\t\treturn true;\n\t}\n\t\n\t/// <summary>\n\t/// C operator constants, sorted by precedence.\n\t/// Flags: 0x100 - 1 operand, 0x200 - 2 operands, 0x1000 - right-to-left.\n\t/// </summary>\n\t[Flags]\n\tenum _OP //:ushort\n\t{\n\t\tOperandInt, OperandUint, OperandLong, OperandUlong,\n\t\t\n\t\t//OperandDouble, OperandFloat, OperandBool, OperandString, //not supported\n\t\t\n\t\t_FirstOperator = 0x10,\n\t\t\n\t\t//FuncCall = 0x110, //f()\n\t\t//SuffixInc, SuffixDec, ArrElem, Member, MemberPtr, //x++, x--, a[], x.m, x->m  //only with variables\n\t\t\n\t\tUnaryMinus = 0x1120, UnaryLogNot, UnaryBitNot, Cast, //-x, !x, ~x, (t)x  //RTL\n\t\t\n\t\t//PrefixInc, PrefixDec, Deref, Address, //++x, --x, *x, &x  //RTL  //only with variables\n\t\t\n\t\tMul = 0x230, Div, Mod, //x*y, x/y, x%y\n\t\tPlus = 0x240, Minus, //x+y, x-y\n\t\tBitLshift = 0x250, BitRshift, //x<<n, x>>n\n\t\tLt = 0x260, LtEq, Gt, GtEq, //x<y, x<=y, x>y, x>=y\n\t\tCompareEq = 0x270, CompareNotEq, //==, !=\n\t\tBitAnd = 0x280, //x&y\n\t\tBitXor = 0x290, //x^y\n\t\tBitOr = 0x2A0, //x|y\n\t\tLogAnd = 0x2B0, //x&&y\n\t\tLogOr = 0x2C0, //x||y\n\t\t\n\t\t//Ternary = 0x13D0, //x?y:z  //RTL  //0 or few in SDK\n\t\t//Assign = 0x12E0, PlusAssign, MinusAssign, MulAssign, DivAssign, ModAssign, BitLshiftAssign, BitRshiftAssign, BitAndAssign, BitXorAssign, BitOrAssign, //=, +=, ...  //RTL  //only with variables\n\t\t\n\t\tLeftParen = 0xF0,\n\t\t\n\t\t_MaskPrecedence = 0xF0,\n\t\t_MaskNOperands = 0x300,\n\t\t_FlagRightToLeft = 0x1000,\n\t}\n\t\n\tstruct _EVAL {\n\t\tpublic int iTok; //token\n\t\tpublic _OP op;\n\t\tpublic ulong value;\n\t\t\n\t\tpublic _EVAL(int iTok, _OP op) {\n\t\t\tthis.iTok = iTok;\n\t\t\tthis.op = op;\n\t\t\tvalue = 0;\n\t\t}\n\t\t\n\t\tpublic _EVAL(int iTok, _OP op, ulong value) {\n\t\t\tthis.iTok = iTok;\n\t\t\tthis.op = op;\n\t\t\tthis.value = value;\n\t\t}\n\t};\n\t\n\t_EVAL[] _eRPN = new _EVAL[200]; //RPN output queue\n\t_EVAL[] _eOp = new _EVAL[200]; //operator stack used when building the RPN\n\t_EVAL[] _eCalc = new _EVAL[100]; //final stack used to calculate the RPN\n\t\n\tclass _ExpressionResult {\n\t\tpublic string valueS;\n\t\tpublic string typeS;\n\t\tpublic bool notConst;\n\t\tpublic uint valueI;\n\t\t\n\t\tpublic _ExpressionResult(string valueS, string typeS, bool notConst, ulong valueI = 0) {\n\t\t\tthis.valueS = valueS;\n\t\t\tthis.typeS = typeS;\n\t\t\tthis.notConst = notConst;\n\t\t\tthis.valueI = (uint)valueI;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Scripts/old/Windows SDK to C#/@SDK converter/Functions.cs",
    "content": "﻿namespace SdkConverter;\n\nunsafe partial class Converter {\n\tvoid _DeclareFunction() {\n\t\t//now we expect one of:\n\t\t//\tfunction declaration, like TYPE __stdcall Func(TYPE a, TYPE b);\n\t\t//\tglobal variable, which can be:\n\t\t//\t\tpreviously-defined type, like TYPE var;\n\t\t//\t\tnow-defined function type, like TYPE (*var)(TYPE a, TYPE b);\n\t\t\n\t\t\n\t\tvar d = new _FINDTYPEDATA();\n\t\t_FindTypename(false, ref d);\n\t\t_i++;\n\t\t\n\t\t//pointer\n\t\tint ptr = 0;\n\t\twhile (_TokIsChar(_i, '*', '&')) { _i++; ptr++; }\n\t\t\n\t\tif (!_TokIsIdent(_i)) {\n\t\t\t//could be 'T (*var)(...);' //0 in SDK\n\t\t\t//_Err(_i, \"unexpected\");\n\t\t\t_SkipStatement(true);\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tint iCallConv = 0;\n\t\tif (_TokIsIdent(_i + 1)) iCallConv = _i++;\n\t\t\n\t\tif (!_TokIsChar(_i + 1, '(')) {\n\t\t\t//could be 'T var;' //1 in SDK\n\t\t\t//_Err(_i, \"unexpected\");\n\t\t\t_i = d.outTypenameToken;\n\t\t\tif (!_ExternConst()) _SkipStatement(true);\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tstring name = _TokToString(_i++);\n\t\t\n\t\t//skip some\n\t\tif (name.IndexOf('_') > 0) {\n\t\t\tif (name.Ends(\"_UserSize\")\n\t\t\t\t|| name.Ends(\"_UserMarshal\")\n\t\t\t\t|| name.Ends(\"_UserUnmarshal\")\n\t\t\t\t|| name.Ends(\"_UserFree\")\n\t\t\t\t|| name.Ends(\"_UserSize64\")\n\t\t\t\t|| name.Ends(\"_UserMarshal64\")\n\t\t\t\t|| name.Ends(\"_UserUnmarshal64\")\n\t\t\t\t|| name.Ends(\"_UserFree64\")\n\t\t\t\t) {\n\t\t\t\t_SkipStatement(false);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\t\n\t\t//string callConv = iCallConv == 0 ? \"Cdecl\" : _ConvertCallConv(iCallConv); //no. Now many __stdcall functions are declared without __stdcall (WINAPI etc). I could't find where it is documented, but it means now __stdcall is default.\n\t\tstring callConv = iCallConv == 0 ? null : _ConvertCallConv(iCallConv);\n\t\tstring returnType, returnAttr = null; bool isHRESULT = false;\n\t\tif (ptr == 0 && _TokIs(d.outTypenameToken, \"HRESULT\")) {\n\t\t\tisHRESULT = true;\n\t\t\treturnType = \"int\";\n\t\t} else {\n\t\t\treturnType = _ConvertTypeName(d.outSym, ref ptr, d.outIsConst, d.outTypenameToken, _TypeContext.Return, out returnAttr);\n\t\t}\n\t\t\n\t\tstring dll, nameInDll = null;\n\t\tif (_funcDllMap.TryGetValue(name, out dll)) {\n\t\t\tint i = dll.IndexOf('|');\n\t\t\tif (i > 0) {\n\t\t\t\tnameInDll = dll.Substring(i + 1);\n\t\t\t\tdll = dll.Substring(0, i);\n\t\t\t\t//print.it(nameInDll);\n\t\t\t} else if (name.Starts(\"K32\")) {\n\t\t\t\t//print.it(name);\n\t\t\t\tnameInDll = name;\n\t\t\t\tname = name.Substring(3);\n\t\t\t\tif (name.Ends(\"W\")) name = name.Remove(name.Length - 1);\n\t\t\t\telse if (name.Ends(\"A\")) {\n\t\t\t\t\t_SkipStatement(false);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tbool skip = false;\n\t\t\tif (name.Contains(\"_\")) skip = true; //mostly CRT library functions, X_Proxy/X_Stub, X_UserMarshal and similar\n\t\t\telse if (name.Starts(\"Dll\")) skip = true; //DllInstall, DllRegisterServer etc\n\t\t\telse if (name.Starts(\"Ndr\") || name.Starts(\"Rpc\")) skip = true; //undocumented\n\t\t\telse {\n\t\t\t\t//print.it(name);\n\t\t\t\tskip = true; //all these in SDK others are undocumented, or documented as deprecated/removed\n\t\t\t\t_funcUnknownDll.Add(name);\n\t\t\t}\n\t\t\t\n\t\t\tif (skip) {\n\t\t\t\t_SkipStatement();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tdll = \"?\";\n\t\t}\n\t\t\n\t\tvar sb = _sbFuncTemp;\n\t\tsb.Clear();\n\t\tsb.Append(\"\\r\\n[DllImport(\\\"\");\n\t\tsb.Append(dll);\n\t\tsb.Append('\"');\n\t\tif (nameInDll != null) sb.AppendFormat(\", EntryPoint=\\\"{0}\\\"\", nameInDll);\n\t\tif (callConv != null) sb.AppendFormat(\", CallingConvention=CallingConvention.{0}\", callConv);\n\t\tif (isHRESULT) sb.Append(\", PreserveSig=true\"); //default, but makes clear that it returns HRESULT and easier to change to 'false'\n\t\tsb.AppendLine(\")]\");\n\t\tif (returnAttr != null) sb.AppendLine(returnAttr);\n\t\tsb.Append(\"internal static extern \");\n\t\tsb.Append(returnType);\n\t\tsb.Append(' ');\n\t\tsb.Append(name);\n\t\t\n\t\t_ConvertParameters(sb, name, _TypeContext.Parameter);\n\t\t\n\t\tstring decl = sb.ToString();\n\t\t_func[name] = decl;\n\t\t//try { _func.Add(name, decl); }\n\t\t//catch { //about 10 in SDK. The second declarations are identical or better (without tagSTRUCT).\n\t\t//\tprint.it(\"----\");\n\t\t//\tprint.it(name);\n\t\t//\tprint.it(_func[name]);\n\t\t//\tprint.it(decl);\n\t\t//}\n\t\t\n\t\tif (!_TokIsChar(_i, ';')) _Err(_i, \"unexpected\");\n\t}\n\t\n\tDictionary<string, string> _funcDllMap;\n\tList<string> _funcUnknownDll = new List<string>();\n\t\n\tvoid _FunctionsFinally() {\n\t\tif (_funcUnknownDll.Count > 100) {\n\t\t\tprint.it(\"Warning: too many unknown dll:\");\n\t\t\tprint.it(_funcUnknownDll);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Scripts/old/Windows SDK to C#/@SDK converter/Parameters.cs",
    "content": "﻿namespace SdkConverter;\n\nunsafe partial class Converter {\n\t\n\tstruct _PARAMETER {\n\t\tpublic int iTok;\n\t\tpublic int nTok;\n\t\tpublic _PARAMETER(int iTok, int nTok) { this.iTok = iTok; this.nTok = nTok; }\n\t}\n\t\n\t/// <summary>\n\t/// Used with all non-nested _GetParameters().\n\t/// </summary>\n\tList<_PARAMETER> _params = new();\n\t\n\t/// <summary>\n\t/// Parses parameters and stores their iTok/nTok in p.\n\t/// Error if _i is not at the starting '('. Finally _i will be at the ending ')'.\n\t/// Returns the number of parameters.\n\t/// Currently used only with pragma.\n\t/// </summary>\n\tint _GetParameters(List<_PARAMETER> p) {\n\t\tp.Clear();\n\t\t\n\t\tchar* s = T(_i);\n\t\tif (*s != '(') _Err(s, \"expected (\");\n\t\t\n\t\tfor (int paramStart = 0, iVeryStart = _i++; ; _i++) {\n\t\t\ts = T(_i);\n\t\t\tchar c = *s;\n\t\t\tswitch (c) {\n\t\t\tcase ',':\n\t\t\tcase ')':\n\t\t\t\tif (paramStart == 0) {\n\t\t\t\t\tif (c == ')' && p.Count == 0) return 0; //()\n\t\t\t\t\t_Err(s, \"unexpected\");\n\t\t\t\t}\n\t\t\t\tp.Add(new _PARAMETER(paramStart, _i - paramStart));\n\t\t\t\tif (c == ')') return p.Count;\n\t\t\t\tparamStart = 0;\n\t\t\t\tcontinue;\n\t\t\tcase '\\0':\n\t\t\t\t_Err(iVeryStart, \"no )\");\n\t\t\t\tbreak;\n\t\t\tcase ';':\n\t\t\t\t_Err(s, \"unexpected\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t\n\t\t\tif (paramStart == 0) paramStart = _i;\n\t\t\t\n\t\t\tswitch (c) {\n\t\t\tcase '(':\n\t\t\tcase '[':\n\t\t\t//case '<': //can be operator\n\t\t\tcase '{':\n\t\t\t\t_SkipEnclosed();\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Converts function parameters to C# and appends to sb like \"(...);\\r\\n\".\n\t/// _i must be at '('. Finally will be after ')'.\n\t/// </summary>\n\tvoid _ConvertParameters(StringBuilder sb, string parentName, _TypeContext context) {\n\t\tif (!_TokIsChar(_i, '(')) _Err(_i, \"unexpected\");\n\t\t_i++;\n\t\tsb.Append('(');\n\t\t\n\t\tif (!_TokIsChar(_i, ')')) {\n\t\t\tfor (int iParam = 0; ; iParam++, _i++) {\n\t\t\t\t\n\t\t\t\t//ignore ...\n\t\t\t\tif (_TokIsChar(_i, '.') && _TokIsChar(_i + 1, '.') && _TokIsChar(_i + 2, '.') && _TokIsChar(_i + 3, ')')) {\n\t\t\t\t\t_i += 3;\n\t\t\t\t\tsb.Remove(sb.Length - 2, 2); //\", \"\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t_PARAMDATA t;\n\t\t\t\t_ParseParamOrMember(context, out t, parentName, iParam + 1);\n\t\t\t\t\n\t\t\t\t//skip default value of optional parameter\n\t\t\t\tif (_TokIsChar(_i, '=')) {\n\t\t\t\t\twhile (!_TokIsChar(++_i, ',', ')')) { }\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t//is ',' or ')'?\n\t\t\t\tbool isLast = _TokIsChar(_i, ')');\n\t\t\t\tif (!isLast && !_TokIsChar(_i, ',')) _Err(_i, \"unexpected\");\n\t\t\t\t\n\t\t\t\tif (t.attributes != null) { sb.Append(t.attributes); sb.Append(' '); }\n\t\t\t\tsb.Append(t.typeName); sb.Append(' '); sb.Append(t.name);\n\t\t\t\tif (!isLast) sb.Append(\", \");\n\t\t\t\t\n\t\t\t\tif (isLast) break;\n\t\t\t}\n\t\t}\n\t\t_i++; //skip ')'\n\t\tsb.Append(\");\\r\\n\");\n\t}\n\t\n\tstruct _PARAMDATA {\n\t\tpublic string typeName; //C# typename\n\t\tpublic string name; //parameter name\n\t\tpublic string attributes; //C# attributes\n\t\tpublic string comment; //comment after member\n\t\tpublic bool isNestedTypeDefinitionWithoutVariables;\n\t\tpublic bool isAnonymousTypeDefinitionWithoutVariables;\n\t}\n\t\n\t/// <summary>\n\t/// Converts C++ function parameter or struct member definition (type, name, const etc) to C# typename/name/etc.\n\t/// _i must be at its start. Finally it will be after the parsed part. Does not check whether it is followed by ',' etc.\n\t/// </summary>\n\t/// <param name=\"context\">Member, Parameter or DelegateParameter.</param>\n\t/// <param name=\"t\">Receives C# typename, name etc.</param>\n\t/// <param name=\"parentName\">The function/struct name. Used to auto-create names for nameless parameters.</param>\n\t/// <param name=\"iParam\">1-based index of parameter/member. Used to auto-create names for nameless parameters.</param>\n\tvoid _ParseParamOrMember(_TypeContext context, out _PARAMDATA t, string parentName, int iParam) {\n\t\tbool isMember = context == _TypeContext.Member;\n\t\t\n\t\tt = new _PARAMDATA();\n\t\t\n\t\tint iSAL = 0;\n\t\tif (_TokIsChar(_i, '^') && _TokIsChar(_i + 1, '\"')) {\n\t\t\t_i++;\n\t\t\tif (!isMember) {\n\t\t\t\tiSAL = _i;\n\t\t\t\t_tok[iSAL] = new _Token(_tok[iSAL].s + 1, _tok[iSAL].len - 2);\n\t\t\t}\n\t\t\t_i++;\n\t\t}\n\t\t\n\t\tvar d = new _FINDTYPEDATA();\n\t\t_FindTypename(isMember, ref d);\n\t\tif (d.outIsNestedTypeDefinition) {\n\t\t\tif (_TokIsChar(_i, ';')) {\n\t\t\t\tt.isNestedTypeDefinitionWithoutVariables = true;\n\t\t\t\tt.isAnonymousTypeDefinitionWithoutVariables = d.outIsAnonymousTypeDefinition;\n\t\t\t\tt.typeName = d.outSym.csTypename;\n\t\t\t\treturn;\n\t\t\t}\n\t\t} else _i++;\n\t\t\n\t\t//pointer\n\t\tint ptr = 0;\n\t\twhile (_TokIsChar(_i, '*', '&')) { _i++; ptr++; }\n\t\t\n\t\t//suport inline function type definition\n\t\tbool isFunc = !d.outIsNestedTypeDefinition && _DetectIsFuncType(_i);\n\t\tif (isFunc) {\n\t\t\tvar f = new _INLINEFUNCTYPEDATA(parentName, iParam);\n\t\t\t_DeclareTypedefFunc(d.outSym, ptr, d.outIsConst, d.outTypenameToken, f);\n\t\t\tt.typeName = f.typeName;\n\t\t\tt.name = f.paramName;\n\t\t} else {\n\t\t\t//typename, param/member name\n\t\t\tt.typeName = _ConvertTypeName(d.outSym, ref ptr, d.outIsConst, d.outTypenameToken, context, out t.attributes, iSAL);\n\t\t\t\n\t\t\tif (_TokIsIdent(_i)) t.name = _TokToString(_i++);\n\t\t\telse if (!isMember && _TokIsChar(_i, \",)[=\")) t.name = \"param\" + iParam;\n\t\t\telse _Err(_i, \"no name\");\n\t\t}\n\t\t\n\t\tif (_TokIsChar(_i, '[')) {\n\t\t\t_ConvertCArray(ref t, !isMember);\n\t\t}\n\t\t\n\t\t//escape names that are C# keywords\n\t\tif (_csKeywords.Contains(t.name)) {\n\t\t\t//print.it(t.name);\n\t\t\tt.name = \"@\" + t.name;\n\t\t}\n\t}\n\t\n\tstruct _FINDTYPEDATA {\n\t\tpublic _Symbol outSym;\n\t\tpublic bool outIsConst;\n\t\tpublic bool outIsAnonymousTypeDefinition;\n\t\tpublic bool outIsNestedTypeDefinition;\n\t\tpublic int outTypenameToken;\n\t\tpublic int inTypedefNameToken;\n\t}\n\t\n\t/// <summary>\n\t/// Finds typename. Gets its _Symbol etc.\n\t/// Starts searching from _i. Finally _i will be typename token index.\n\t/// Error if not found.\n\t/// Initially sets all outX=0/false/null.\n\t/// If there is 'const', sets outIsConst=true.\n\t/// If there is 'struct' etc as nested definition, converts the definition, sets outIsNestedStruct=true; then _i finally will be after }, which can be semicolon, variable name or * variable name.\n\t/// If there is 'struct' etc as forward declaration, adds forward declaration.\n\t/// </summary>\n\tvoid _FindTypename(bool isMember, ref _FINDTYPEDATA d) {\n\t\td.outSym = null;\n\t\td.outIsConst = false;\n\t\td.outIsNestedTypeDefinition = false;\n\t\td.outTypenameToken = 0;\n\t\t\n\t\t//process keywords until typename\n\t\tfor (; _TokIsIdent(_i); _i++) {\n\t\t\td.outSym = _FindSymbol(_i, true);\n\t\t\tvar k = d.outSym as _Keyword;\n\t\t\tif (k == null) {\n\t\t\t\td.outTypenameToken = _i;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\tswitch (k.kwType) {\n\t\t\tcase _KeywordT.TypeDecl:\n\t\t\t\t//is nested struct/enum/typedef definition?\n\t\t\t\tif (isMember) {\n\t\t\t\t\tbool isDef = false;\n\t\t\t\t\tif (_TokIsChar(_i, 't')) isDef = true;\n\t\t\t\t\telse {\n\t\t\t\t\t\tint j = _i + 1; if (_TokIsIdent(j)) j++;\n\t\t\t\t\t\tif (_TokIsChar(j, \"{:;\")) isDef = true;\n\t\t\t\t\t}\n\t\t\t\t\tif (isDef) {\n\t\t\t\t\t\t_DeclareType(ref d);\n\t\t\t\t\t\td.outIsNestedTypeDefinition = true;\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t//inline forward declaration\n\t\t\t\td.outSym = _InlineForwardDeclaration();\n\t\t\t\td.outTypenameToken = _i;\n\t\t\t\treturn;\n\t\t\tcase _KeywordT.Normal:\n\t\t\t\tif (_TokIs(_i, \"const\")) { d.outIsConst = true; continue; }\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t\n\t\t\t_Err(_i, \"unexpected\");\n\t\t}\n\t\t_Err(_i, \"no type\");\n\t}\n\t\n\tenum _TypeContext {\n\t\tParameter, DelegateParameter, Return, Member, ComParameter, ComReturn\n\t}\n\t\n\t/// <summary>\n\t/// Converts type name/pointer to C# type name.\n\t/// The return value is type name, possibly with * or []. If parameter, also can have ref/out/in and [In]/[Out].\n\t/// </summary>\n\t/// <param name=\"t\">The type.</param>\n\t/// <param name=\"ptr\">Pointer level. The function may adjust it depending on typedef pointer level etc.</param>\n\t/// <param name=\"isConst\">Is const.</param>\n\t/// <param name=\"iTokTypename\">Type name token index. Can be 0 if don't need to unalias etc; then just adds pointer/ref if need.</param>\n\t/// <param name=\"context\">Depending on it char* will be converted to \"string\" etc.</param>\n\t/// <param name=\"attributes\">Receives null or MarshalAs attribute.</param>\n\t/// <param name=\"iSAL\">SAL token index, or 0 if no SAL.</param>\n\tstring _ConvertTypeName(_Symbol t, ref int ptr, bool isConst, int iTokTypename, _TypeContext context, out string attributes, int iSAL = 0) {\n\t\tattributes = null;\n\t\tstring name, marshalAs = null;\n\t\t\n\t\tbool isLangType = false, isParameter = false, isCOM = false, isBlittable = false, isRawPtr = false;\n\t\tswitch (context) {\n\t\tcase _TypeContext.Parameter: case _TypeContext.DelegateParameter: isParameter = true; break;\n\t\tcase _TypeContext.ComParameter: isParameter = true; isCOM = true; break;\n\t\tcase _TypeContext.ComReturn: isCOM = true; break;\n\t\t}\n\t\t\n\t\tbool _In_ = false, _Out_ = false, _Inout_ = false;\n\t\tif (iSAL > 0) {\n\t\t\t//print.it(_tok[iSAL]);\n\t\t\tif (_TokStarts(iSAL, \"_In_\")) _In_ = true;\n\t\t\telse if (_TokStarts(iSAL, \"_Out\")) _Out_ = true;\n\t\t\telse if (_TokStarts(iSAL, \"_Inout_\")) _Inout_ = true;\n\t\t\telse _Err(iSAL, \"unknown SAL\");\n\t\t}\n\t\t\n\t\tif (iTokTypename != 0) {\n\t\t\tptr += _Unalias(iTokTypename, ref t, ref isConst);\n\t\t\tname = t.csTypename;\n\t\t\t\n\t\t\tvar c = t as _CppType;\n\t\t\tif (c != null) {\n\t\t\t\tisLangType = true;\n\t\t\t\tisBlittable = true;\n\t\t\t\tswitch (name) {\n\t\t\t\tcase \"char\":\n\t\t\t\t\tisBlittable = false;\n\t\t\t\t\tif (ptr is 1 or 2) {\n\t\t\t\t\t\tbool isBSTR = _TokIs(iTokTypename, \"BSTR\");\n\t\t\t\t\t\tif (ptr == 1 || (isParameter && isBSTR)) {\n\t\t\t\t\t\t\t//why '&& isBSTR':\n\t\t\t\t\t\t\t//\tIf non-BSTR char** parameter, cannot be 'out string' because .NET does not know how to free the string; often it is even undocumented.\n\t\t\t\t\t\t\t//\tAnd ref in many cases is bad.\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tptr--;\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tswitch (context) {\n\t\t\t\t\t\t\tcase _TypeContext.Member:\n\t\t\t\t\t\t\t\tif (isConst || isBSTR) {\n\t\t\t\t\t\t\t\t\tname = \"string\";\n\t\t\t\t\t\t\t\t\tif (isBSTR) marshalAs = \"BStr\";\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t//string dangerous, because if the callee changes member pointer, .NET tries to free the new string with CoTaskMemFree.\n\t\t\t\t\t\t\t\t\tptr = 1;\n\t\t\t\t\t\t\t\t\t//print.it(_DebugGetLine(iTokTypename));\n\t\t\t\t\t\t\t\t\tisRawPtr = true;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase _TypeContext.Return:\n\t\t\t\t\t\t\tcase _TypeContext.ComReturn:\n\t\t\t\t\t\t\t\tptr = 1; //if string, .NET tries to free the return value\n\t\t\t\t\t\t\t\tisRawPtr = true;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase _TypeContext.DelegateParameter:\n\t\t\t\t\t\t\t\tptr = 1; //because some are \"LPSTR or WORD\". Rarely used.\n\t\t\t\t\t\t\t\tisRawPtr = true;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase _TypeContext.Parameter:\n\t\t\t\t\t\t\tcase _TypeContext.ComParameter:\n\t\t\t\t\t\t\t\tDebug.Assert(!(isConst && (_Out_ || _Inout_)));\n\t\t\t\t\t\t\t\tif (isConst || _In_ || isBSTR) {\n\t\t\t\t\t\t\t\t\tname = \"string\";\n\t\t\t\t\t\t\t\t\t//if (ptr == 1) print.it(_DebugGetLine(iTokTypename));\n\t\t\t\t\t\t\t\t\tif (isCOM) {\n\t\t\t\t\t\t\t\t\t\tif (!isBSTR) marshalAs = \"LPWStr\";\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tif (isBSTR) marshalAs = \"BStr\";\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tptr = 1;\n\t\t\t\t\t\t\t\t\tisRawPtr = true;\n\t\t\t\t\t\t\t\t\t//Could be StringBuilder, but Au library does not use it, it is slow. Or [Out] char[]. But Au library uses char*.\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (ptr > 1) {\n\t\t\t\t\t\tisRawPtr = true;\n\t\t\t\t\t\tisConst = false;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"int\":\n\t\t\t\t\tif (_TokIs(iTokTypename, \"BOOL\")) {\n\t\t\t\t\t\tif (ptr == 0 || (ptr == 1 && isParameter)) {\n\t\t\t\t\t\t\tname = \"bool\";\n\t\t\t\t\t\t\tif (isCOM) marshalAs = \"Bool\";\n\t\t\t\t\t\t\tisBlittable = false;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"bool\":\n\t\t\t\t\tmarshalAs = \"U1\";\n\t\t\t\t\tisBlittable = false;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"void\":\n\t\t\t\tcase \"byte\":\n\t\t\t\tcase \"sbyte\":\n\t\t\t\t\tif (ptr > 0) isRawPtr = true;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"double\":\n\t\t\t\t\tif (_TokIs(iTokTypename, \"DATE\")) {\n\t\t\t\t\t\tif (ptr == 0 || (ptr == 1 && isParameter)) {\n\t\t\t\t\t\t\tname = \"DateTime\";\n\t\t\t\t\t\t\tisBlittable = false;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (isRawPtr) isBlittable = true;\n\t\t\t}\n\t\t} else {\n\t\t\tname = t.csTypename;\n\t\t}\n\t\t\n\t\tif (!isLangType) {\n\t\t\tvar ts = t as _Struct;\n\t\t\tif (ts != null) {\n\t\t\t\tif (ts.isInterface) {\n\t\t\t\t\t//print.it(ptr, ts.csTypename);\n\t\t\t\t\tif (ptr == 0) _Err(iTokTypename, \"unexpected\");\n\t\t\t\t\tif (--ptr > 0 && context == _TypeContext.Member) {\n\t\t\t\t\t\tname = \"IntPtr\";\n\t\t\t\t\t\tisBlittable = true;\n\t\t\t\t\t} else if (name is \"IUnknown\" or \"IDispatch\") {\n\t\t\t\t\t\tDebug_.PrintIf(ptr > 1, context);\n\t\t\t\t\t\tmarshalAs = name;\n\t\t\t\t\t\tname = \"object\";\n\t\t\t\t\t}\n\t\t\t\t} else if (!ts.isClass) {\n\t\t\t\t\tswitch (name) {\n\t\t\t\t\tcase \"wnd\": case \"LPARAM\": isBlittable = true; break;\n\t\t\t\t\tcase \"GUID\": name = \"Guid\"; break;\n\t\t\t\t\tcase \"DECIMAL\": name = \"decimal\"; break;\n\t\t\t\t\tcase \"VARIANT\":\n\t\t\t\t\t\tif ((isParameter && ptr <= 1) || (context == _TypeContext.Member && ptr == 0)) {\n\t\t\t\t\t\t\tname = \"object\";\n\t\t\t\t\t\t\tif (context != _TypeContext.ComParameter) marshalAs = \"Struct\";\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t//case \"SAFEARRAY\": //in SDK used only with SafeArrayX functions, with several other not-important functions and as [PROP]VARIANT members\n\t\t\t\t\t//print.it(ptr);\n\t\t\t\t\t//break;\n\t\t\t\t\tcase \"ITEMIDLIST\":\n\t\t\t\t\t\tif (ptr > 0) { ptr--; name = \"IntPtr\"; isBlittable = true; }\n\t\t\t\t\t\t//else _Err(iTokTypename, \"example\"); //0 in SDK\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"POINTL\": name = \"POINT\"; break;\n\t\t\t\t\tcase \"RECTL\": name = \"RECT\"; break;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (t is _Enum) {\n\t\t\t\tisBlittable = true;\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (ptr > 0) {\n\t\t\t__ctn.Clear();\n\t\t\tbool isArray = false;\n\t\t\tif (!isRawPtr && isParameter) {\n\t\t\t\tstring sal = null;\n\t\t\t\tif (iSAL > 0) sal = _TokToString(iSAL);\n\t\t\t\t\n\t\t\t\tif (iSAL > 0 && ptr == 1) { //if ptr>1, can be TYPE[]* or TYPE*[]\n\t\t\t\t\tif (_In_ && (sal.Contains(\"_reads_\") || sal.Contains(\"count\"))) {\n\t\t\t\t\t\t//print.it(_tok[iTokTypename], name, _DebugGetLine(iTokTypename));\n\t\t\t\t\t\tisArray = true;\n\t\t\t\t\t\t__ctn.Append(\"[In] \");\n\t\t\t\t\t}\n\t\t\t\t\tif (_Inout_ && (sal.Contains(\"_updates_\") || sal.Contains(\"count\"))) {\n\t\t\t\t\t\t//print.it(_tok[iTokTypename], name, _DebugGetLine(iTokTypename));\n\t\t\t\t\t\tisArray = true;\n\t\t\t\t\t\t__ctn.Append(\"[In,Out] \");\n\t\t\t\t\t}\n\t\t\t\t\tif (_Out_ && (sal.Contains(\"_writes_\") || sal.Contains(\"count\"))) {\n\t\t\t\t\t\t//print.it(_tok[iTokTypename], name, _DebugGetLine(iTokTypename));\n\t\t\t\t\t\tisArray = true;\n\t\t\t\t\t\t__ctn.Append(\"[Out] \");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (_Inout_) isConst = false;\n\t\t\t\t\n\t\t\t\tif (isArray) {\n\t\t\t\t\tptr--;\n\t\t\t\t} else {\n\t\t\t\t\tptr--;\n\t\t\t\t\t\n\t\t\t\t\tif (_Out_) {\n\t\t\t\t\t\t__ctn.Append(\"out \");\n\t\t\t\t\t} else if (_In_ || isConst) {\n\t\t\t\t\t\tif (isBlittable) {\n\t\t\t\t\t\t\tif (isConst && ptr == 0 && name != \"IntPtr\") {\n\t\t\t\t\t\t\t\t//print.it(_tok[iTokTypename], name, _DebugGetLine(iTokTypename));\n\t\t\t\t\t\t\t\tisArray = true; //usually array, because there is no sense for eg 'const int* param', unless it is a 64-bit value (SDK usually then uses LARGE_INTEGER etc, not __int64). Those with just _In_ usually are not arrays, because for arrays are used _In_reads_ etc.\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t__ctn.Append(\"in \");\n\t\t\t\t\t\t\t\t//print.it(_tok[iTokTypename], name, _DebugGetLine(iTokTypename));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tif (isConst) {\n\t\t\t\t\t\t\t\t__ctn.Append(\"in \"); //prevents copying non-blittable types back to the caller when don't need\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t//print.it(_tok[iTokTypename], name, _DebugGetLine(iTokTypename));\n\t\t\t\t\t\t\t\t//__ctn.Append(\"in \"); //no, because there are many errors in SDK where inout parameters have _In_\n\t\t\t\t\t\t\t\t__ctn.Append(\"ref \");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t__ctn.Append(\"ref \");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (isArray) {\n\t\t\t\t\tbool useMarshalAsSubtype = false;\n\t\t\t\t\tif (isCOM) {\n\t\t\t\t\t\t//by default marshals as SAFEARRAY\n\t\t\t\t\t\tif (marshalAs == null) marshalAs = \"LPArray\";\n\t\t\t\t\t\telse useMarshalAsSubtype = true;\n\t\t\t\t\t} else if (context == _TypeContext.DelegateParameter) {\n\t\t\t\t\t\tisArray = false;\n\t\t\t\t\t\tptr++;\n\t\t\t\t\t\t__ctn.Clear();\n\t\t\t\t\t\t//maybe can be array, not tested. Never mind, in SDK only 4 rarely used.\n\t\t\t\t\t\t//print.it(_tok[iTokTypename], name, _DebugGetLine(iTokTypename));\n\t\t\t\t\t} else if (marshalAs != null) useMarshalAsSubtype = true;\n\t\t\t\t\t\n\t\t\t\t\tif (useMarshalAsSubtype) marshalAs = \"LPArray, ArraySubType = UnmanagedType.\" + marshalAs;\n\t\t\t\t}\n\t\t\t} //if(isParameter)\n\t\t\t\n\t\t\t__ctn.Append(name);\n\t\t\tif (ptr > 0) __ctn.Append('*', ptr);\n\t\t\tif (isArray) __ctn.Append(\"[]\");\n\t\t\t\n\t\t\tname = __ctn.ToString();\n\t\t}\n\t\t\n\t\tif (marshalAs != null) {\n\t\t\t__ctn.Clear();\n\t\t\tstring ret = null; if (context == _TypeContext.Return || context == _TypeContext.ComReturn) ret = \"return: \";\n\t\t\tattributes = $\"[{ret}MarshalAs(UnmanagedType.{marshalAs})]\";\n\t\t}\n\t\treturn name;\n\t}\n\tStringBuilder __ctn = new StringBuilder();\n\t\n\t/// <summary>\n\t/// Converts C-style fixed-size array to C#. Gets MarshalAs attribute, appends \"[]\" to typeName etc.\n\t/// Supports \"[y][x]\" etc.\n\t/// _i must be at '['. Finally it will be after ']'.\n\t/// If empty array (like TYPE x[] or TYPE x[0]), attributes receives \"//...\". If 1-element array, receives \"/*...*/\".\n\t/// </summary>\n\t/// <param name=\"t.typeName\">This function appends \"[]\" if need, or convertes \"char\" to \"string\".</param>\n\t/// <param name=\"t.name\">If less than 8 elements, and t.name looks like a \"Reserved\" etc, splits into multiple members like \"Reserved_0, Reserved_1, ...\".</param>\n\t/// <param name=\"t.attributes\">If !isParameter, receives attribute like \"[MarshalAs(UnmanagedType.ByValArray, SizeConst = {x})]\". If already is MarshalAs attribute, uses it as ArraySubType.</param>\n\t/// <param name=\"isParameter\">Just skip [..] or [...][...] and append \"[]\" to t.typeName. Used for parameters, because the attribute can be used only with struct fields; C++ arguments for parameters 'TYPE param[n]' are passed like 'TYPE* param', and n is ignored, can be empty.</param>\n\tvoid _ConvertCArray(ref _PARAMDATA t, bool isParameter = false) {\n\t\t//if (!isParameter) print.it(t.typeName);\n\t\t\n\t\tuint elemCount = 1;\n\t\tfor (; _TokIsChar(_i, '['); _i++) { //support [a][b]\n\t\t\tint i0 = _i + 1;\n\t\t\t_SkipEnclosed();\n\t\t\tif (isParameter) continue;\n\t\t\tuint ec = 0;\n\t\t\tif (_i > i0) { //else TYPE x[]\n\t\t\t\t_ExpressionResult r = _Expression(i0, _i, \"[]\");\n\t\t\t\tswitch (r.typeS) { case \"int\": case \"uint\": break; default: _Err(i0, \"cannot calculate\"); break; }\n\t\t\t\tec = r.valueI;\n\t\t\t}\n\t\t\telemCount *= ec;\n\t\t}\n\t\t\n\t\tif (isParameter) {\n\t\t\tt.typeName += \"[]\";\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tif (t.typeName.Ends(\"[]\")) t.typeName = t.typeName.Remove(t.typeName.Length - 2); //'TYPE a[n], b[m]'\n\t\t\n\t\tstring marshalAs = \"ByValArray\", comment = null, comment2 = null;\n\t\tif (elemCount == 0) { //variable-length array of >= 0 elements\n\t\t\tcomment = \"//\"; //disable the attribute together with member, and let the member be not array\n\t\t} else if (elemCount == 1) { //variable-length array of >= 1 elements\n\t\t\tcomment = \"/*\"; comment2 = \"*/\"; //disable the attribute, and let the member be not array\n\t\t} else if (t.typeName == \"char\") {\n\t\t\tt.comment = $\"//public fixed char {t.name}[{elemCount}];\";\n\t\t\tt.typeName = \"string\";\n\t\t\tmarshalAs = \"ByValTStr\";\n\t\t} else {\n\t\t\tif (elemCount < 8 && (t.name.Find(\"Reserved\", true) >= 0 || t.name.Find(\"pad\", true) >= 0 || t.name.Starts(\"Spare\", true))) {\n\t\t\t\t//print.it(t.name);\n\t\t\t\tvar sb = new StringBuilder();\n\t\t\t\tfor (int i = 0; i < elemCount; i++) {\n\t\t\t\t\tif (i > 0) sb.Append(\", \");\n\t\t\t\t\tsb.Append(t.name);\n\t\t\t\t\tsb.Append('_');\n\t\t\t\t\tsb.Append(i);\n\t\t\t\t}\n\t\t\t\tt.name = sb.ToString();\n\t\t\t\tcomment = \"/*\"; comment2 = \"*/\";\n\t\t\t} else {\n\t\t\t\t//if(elemCount<8) print.it(t.name);\n\t\t\t\tif (t.typeName is \"byte\" or \"sbyte\" or \"int\" or \"uint\" or \"long\" or \"ulong\" or \"short\" or \"ushort\" or \"float\" or \"double\")\n\t\t\t\t\tt.comment = $\"//public fixed {t.typeName} {t.name}[{elemCount}];\";\n\t\t\t\tt.typeName += \"[]\";\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (t.attributes != null) _Err(_i, \"todo\"); //0 in SDK. Should extract its type from [MarshalAs(UnmanagedType.(\\w+) and insert in the new attributes.\n\t\tt.attributes = $\"{comment}[MarshalAs(UnmanagedType.{marshalAs}, SizeConst = {elemCount})]{comment2}\";\n\t\t\t//CONSIDER: also add commented properties to get Span. Then also can add properties to get/set managed string or array from the Span.\n\t\t//\tThe MarshalAs way often cannot be used (makes the type managed and then cannot get pointer) or undesirable. The fixed way can be used only with some types.\n\t\t//\tTested, in Release almost same speed as raw pointer. If used in script that isn't optimized, much slower, but not too slow.\n\t\t//\tExamples:\n\t\t/*\n[Winapi]\nstatic unsafe class api {\n\ninternal struct Example {\n\t\t//string\n\t\tfixed char _c[5];\n\t\tpublic Span<char> s => MemoryMarshal.CreateSpan(ref _c[0], 5);\n\t\tpublic string s_String { get => _ToString(s); set => _ToSpan(s, value); }\n\t\t\n\t\t//array of fixable type\n\t\tfixed byte _b[5];\n\t\tpublic Span<byte> b => MemoryMarshal.CreateSpan(ref _b[0], 5);\n\t\tpublic byte[] b_Array { get => b.ToArray(); set => value.AsSpan().CopyTo(b); }\n\t\t\n\t\t//array of other type\n\t\tPOINT _a, _a1, _a2, _a3, _a4;\n\t\t//POINT _a; fixed byte _ab[32]; //or this, if can calculate size easily\n\t\tpublic Span<POINT> a => MemoryMarshal.CreateSpan(ref _a, 5);\n\t\tpublic POINT[] a_Array { get => a.ToArray(); set => value.AsSpan().CopyTo(a); }\n\t\t\n\t\t//array of size 1 or 0 at the end\n\t\tPOINT _p;\n\t\tpublic Span<POINT> p(int count) => MemoryMarshal.CreateSpan(ref _p, count);\n\t}\n\t\n\tstatic string _ToString(Span<char> p) => new(p[..p.IndexOf('\\0')]);\n\t\n\tstatic void _ToSpan(Span<char> p, RStr s) {\n\t\tif (s.Length >= p.Length) throw new ArgumentException($\"Destination is too short. Max string length = {p.Length - 1}.\");\n\t\tp[s.Length] = '\\0';\n\t\ts.CopyTo(p);\n\t}\n}\n\t\t\t*/\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Converts C++ calling convention keyword to C# enum CallingConvention member name.\n\t\t/// Returns null if __stdcall.\n\t\t/// </summary>\n\t\t/// <param name=\"iTok\">C++ calling convention keyword token. Error if other token.</param>\n\t\tstring _ConvertCallConv(int iTok) {\n\t_FindKeyword(iTok, _KeywordT.CallConv);\n\t\n\tchar* s = T(iTok);\n\twhile (*s == '_') s++;\n\tswitch (*s) {\n\tcase 'c': return \"Cdecl\";\n\tcase 'f': return \"FastCall\";\n\tcase 't': return \"ThisCall\";\n\t}\n\treturn null; //StdCall is default\n}\n\t}\n"
  },
  {
    "path": "Scripts/old/Windows SDK to C#/@SDK converter/SDK converter.cs",
    "content": "/*/ testInternal Au; /*/\nprint.clear();\nvar x = new SdkConverter.Converter();\n\n#if !true\n//x.Convert(\n//\t@\"C:\\code\\au\\Other\\SdkConverter\\Data\\Header.h\",\n//\t//@\"C:\\code\\au\\Other\\SdkPreprocess\\Cpp.cpp\",\n//\t@\"C:\\code\\au\\Other\\Api\\Api-small.cs\", false);\n\nx.Convert(@\"C:\\code\\au\\Other\\Api\\Api-preprocessed-64.cpp\", @\"C:\\code\\au\\Other\\Api\\Api-64.cs\", false);\nprint.scrollToTop();\n#else\nx.Convert(@\"C:\\code\\au\\Other\\Api\\Api-preprocessed-64.cpp\", @\"C:\\code\\au\\Other\\Api\\Api-64.cs\", false);\n\nif (true) {\n\tnew SdkConverter.Converter().Convert(@\"C:\\code\\au\\Other\\Api\\Api-preprocessed-32.cpp\", @\"C:\\code\\au\\Other\\Api\\Api-32.cs\", true);\n}\n#endif\n"
  },
  {
    "path": "Scripts/old/Windows SDK to C#/@SDK converter/Tokenize.cs",
    "content": "namespace SdkConverter;\n\nunsafe partial class Converter {\n\tvoid _Tokenize(char* s) {\n\t\t_tok.Capacity = _src.Length / 8;\n\t\t_tok.Add(new _Token());\n\t\t\n\t\tbool isNewLine = true;\n\t\tint len;\n\t\tchar c;\n\t\t\n\t\tfor (; (c = *s) != 0; s++) {\n\t\t\tswitch (c) {\n\t\t\tcase '\\r':\n\t\t\tcase '\\n':\n\t\t\t\tisNewLine = true;\n\t\t\t\tbreak;\n\t\t\tcase ' ':\n\t\t\tcase '\\t':\n\t\t\tcase '\\v':\n\t\t\tcase '\\f':\n\t\t\t\tbreak;\n\t\t\tcase '`': //was #define, #undef or #pragma pack\n\t\t\t\tif (isNewLine) { //else it is preprocessor operator\n\t\t\t\t\tif (_nTokUntilDefUndef == 0 && s[1] != '(') {\n\t\t\t\t\t\t_nTokUntilDefUndef = _tok.Count;\n\t\t\t\t\t\ts[-1] = '\\0'; //was '\\n\n\t\t\t\t\t\t_tok.Add(new _Token(s - 1, 1));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t_tok.Add(new _Token(s, 1));\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\t//TODO3: replace '$' with '_'. Although SDK does not have such identifiers when removed CRT headers.\n\t\t\t\tif (_IsCharIdentStart(c)) {\n\t\t\t\t\tlen = _LenIdent(s);\n\t\t\t\t\t\n\t\t\t\t\t////is prefix\"string\" or prefix'char'?\n\t\t\t\t\t//bool isPrefix = false;\n\t\t\t\t\t//switch(s[len]) {\n\t\t\t\t\t//case '\"':\n\t\t\t\t\t//\tif(len == 1) isPrefix = (c == 'L' || c == 'u' || c == 'U');\n\t\t\t\t\t//\telse if(len == 2) isPrefix = (c == 'u' && s[1] == '8');\n\t\t\t\t\t//\t//info: raw strings replaced to escaped strings when preprocessing\n\t\t\t\t\t//\tbreak;\n\t\t\t\t\t//case '\\'':\n\t\t\t\t\t//\tif(len == 1) isPrefix = (c == 'L' || c == 'u' || c == 'U');\n\t\t\t\t\t//\tbreak;\n\t\t\t\t\t//}\n\t\t\t\t\t//if(isPrefix) { //remove prefix\n\t\t\t\t\t//\tfor(; len > 0; len--) *s++ = ' ';\n\t\t\t\t\t//\ts--;\n\t\t\t\t\t//\tcontinue;\n\t\t\t\t\t//}\n\t\t\t\t\t\n\t\t\t\t} else if (_IsCharDigit(c)) len = _LenNumber(s);\n\t\t\t\telse if (c == '\"') len = _SkipString(s);\n\t\t\t\telse if (c == '\\'') len = _SkipApos(s);\n\t\t\t\telse len = 1;\n\t\t\t\t_tok.Add(new _Token(s, len));\n\t\t\t\ts += len - 1;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\t\n\t\t_nTok = _tok.Count;\n\t\tif (_nTokUntilDefUndef == 0) _nTokUntilDefUndef = _nTok;\n\t\t\n\t\tfor (int i = 0; i < 10; i++) _tok.Add(new _Token(s, 1)); //to safely query next token\n\t\t_tok[0] = new _Token(s, 1); //to safely query previous token. Also used as an empty token. The empty list item added before tokenizing.\n\t}\n\t\n\t/// <summary>\n\t/// Parses and folds string constant, starting from s, which must be at the starting \".\n\t/// Returns folded string length, including both \".\n\t/// For example, if s is '\"a\" \"b\"', makes it '\"ab\"   ' and returns 4.\n\t/// Error if the ending \" is missing.\n\t/// </summary>\n\t//#if NEWSTRING\n\tint _SkipString(char* s) {\n\t\tDebug.Assert(*s == '\"');\n\t\t\n\t\t//is prefix?\n\t\tbool isPrefix = false;\n\t\tint iPrevTok = _tok.Count - 1;\n\t\t_Token pt = _tok[iPrevTok];\n\t\tchar c = *pt.s;\n\t\tif (_IsCharIdentStart(c) && pt.len == (int)(s - pt.s)) {\n\t\t\tif (pt.len == 1) {\n\t\t\t\tisPrefix = (c == 'L' || c == 'u' || c == 'U');\n\t\t\t} else if (pt.len == 2) {\n\t\t\t\tisPrefix = (c == 'u' && pt.s[1] == '8');\n\t\t\t}\n\t\t\tif (isPrefix) _tok.RemoveAt(_tok.Count - 1);\n\t\t\t//info: raw strings replaced to escaped strings in script.\n\t\t}\n\t\t\n\t\tchar* s0 = s++, d = s; //d used for folding\n\t\tg0:\n\t\tfor (; ; s++) {\n\t\t\t*d++ = *s; //folding\n\t\t\tif (*s == '\"') {\n\t\t\t\tif (s[-1] == '\\\\') {\n\t\t\t\t\t//is \\\" or \\\\\"?\n\t\t\t\t\tint k = -1; while (s[k - 1] == '\\\\') k--;\n\t\t\t\t\tif ((k & 1) != 0) continue;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (*s <= '\\r') {\n\t\t\t\tif (*s == 0 || *s == '\\r' || *s == '\\n') _Err(s0, \"missing \\\"\");\n\t\t\t}\n\t\t}\n\t\t\n\t\t//fold strings\n\t\tchar* f = ++s; //skip \"\n\t\twhile (_IsCharSpaceOrRN(*f)) f++;\n\t\tif (isPrefix) {\n\t\t\tif (*f == *pt.s && (pt.len == 1 || f[1] == pt.s[1]) && f[pt.len] == '\"') { s = f + pt.len + 1; d--; goto g0; }\n\t\t} else {\n\t\t\tif (*f == '\"') { s = f + 1; d--; goto g0; }\n\t\t}\n\t\t\n\t\tfor (f = d; f < s; f++) *f = ' '; //erase what is moved to the left\n\t\t\n\t\t//ANSI string constant?\n\t\tif (!isPrefix && _nTokUntilDefUndef != 0 && _TokIsChar(iPrevTok - 1, '`')) {\n\t\t\t//print.it(new string(s0, 0, (int)(d - s0)));\n\t\t\t//_Err(iPrevTok, \"ANSI string\");\n\t\t\t*s0 = '\\x2'; //later will restore '\"' and add to \"FAILED TO CONVERT\"\n\t\t}\n\t\t\n\t\treturn (int)(d - s0);\n\t\t\n\t\t//info: could also give parsed length, but this is simpler. Caller will have to skip spaces in any case.\n\t}\n\t\n\t/// <summary>\n\t/// Parses character constant, starting from s, which must be at the starting '.\n\t/// Returns its length, including both '.\n\t/// Error if the ending ' is missing.\n\t/// </summary>\n\tint _SkipApos(char* s) {\n\t\tDebug.Assert(*s == '\\'');\n\t\t\n\t\t//is prefix?\n\t\t_Token pt = _tok[_tok.Count - 1];\n\t\tchar c = *pt.s;\n\t\tif (_IsCharIdentStart(c) && pt.len == (int)(s - pt.s) && pt.len == 1) {\n\t\t\tif (c == 'L' || c == 'u' || c == 'U') _tok.RemoveAt(_tok.Count - 1);\n\t\t}\n\t\t\n\t\tfor (char* s0 = s++; ; s++) {\n\t\t\tif (*s == '\\'') {\n\t\t\t\tif (s[-1] == '\\\\') {\n\t\t\t\t\t//is \\' or \\\\'?\n\t\t\t\t\tint k = -1; while (s[k - 1] == '\\\\') k--;\n\t\t\t\t\tif ((k & 1) != 0) continue;\n\t\t\t\t}\n\t\t\t\treturn (int)(s + 1 - s0);\n\t\t\t}\n\t\t\tif (*s <= '\\r') {\n\t\t\t\tif (*s == 0 || *s == '\\r' || *s == '\\n') _Err(s0, \"missing '\");\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Parses a number literal and returns its length, including suffix.\n\t/// Error if invalid.\n\t/// </summary>\n\tint _LenNumber(char* s) {\n\t\tif (!_IsCharDigit(*s)) return 0;\n\t\tchar* s0 = s++;\n\t\t\n\t\t//hex, bin, oct\n\t\tint hexEtc = 0;\n\t\tif (*s0 == '0') {\n\t\t\tif (*s == 'x' || *s == 'X') {\n\t\t\t\thexEtc = 2;\n\t\t\t\tfor (++s; _IsCharHexDigit(*s); s++) { }\n\t\t\t} else if (*s == 'b' || *s == 'B') {\n\t\t\t\thexEtc = 3;\n\t\t\t\tfor (++s; *s == '0' || *s == '1'; s++) { }\n\t\t\t} else if (_IsCharDigit(*s)) hexEtc = 1; //or can be double like 01.2\n\t\t\t\n\t\t\tif (hexEtc >= 2 && (s - s0) == 2) _Err(s, \"unexpected\");\n\t\t}\n\t\t\n\t\tbool dbl = false;\n\t\tif (hexEtc < 2) {\n\t\t\twhile (_IsCharDigit(*s)) s++;\n\t\t\t//double fraction\n\t\t\tif (*s == '.') {\n\t\t\t\tdbl = true;\n\t\t\t\tfor (++s; _IsCharDigit(*s); s++) { }\n\t\t\t}\n\t\t}\n\t\t\n\t\t//double exponent\n\t\tif (*s == 'E' || *s == 'e') {\n\t\t\tdbl = true;\n\t\t\ts++;\n\t\t\tif (*s == '-' || *s == '+') s++;\n\t\t\tif (!_IsCharDigit(*s)) _Err(s, \"unexpected\");\n\t\t\twhile (_IsCharDigit(*s)) s++;\n\t\t}\n\t\t\n\t\tif (hexEtc == 1 && !dbl) { //validate oct digits now\n\t\t\tfor (char* t = s0 + 2; t < s; t++) if (*t > '7') _Err(t, \"unexpected\");\n\t\t}\n\t\t\n\t\t//suffix\n\t\tuint suffixType;\n\t\ts += _NumberSuffix(s, dbl, out suffixType);\n\t\t\n\t\treturn (int)(s - s0);\n\t}\n\t\n\t/// <summary>\n\t/// Scans integer or floating-point constant suffix and returns its length.\n\t/// Error if invalid suffix.\n\t/// Converts C++ suffix to C#.\n\t/// </summary>\n\t/// <param name=\"s\"></param>\n\t/// <param name=\"type\">Receives flags: 1 unsigned, 2 __int64, 4 float.</param>\n\tint _NumberSuffix(char* s, bool floatingPoint, out uint type) {\n\t\ttype = 0;\n\t\tchar* s0 = s;\n\t\tint lenTrim = 0;\n\t\t\n\t\tif (floatingPoint) {\n\t\t\tif (*s == 'f' || *s == 'F') {\n\t\t\t\ts++; type |= 4;\n\t\t\t} else if (*s == 'l' || *s == 'L') *s++ = ' ';\n\t\t} else {\n\t\t\tif (*s == 'u' || *s == 'U') { s++; type |= 1; }\n\t\t\t\n\t\t\tif (*s == 'l' || *s == 'L') {\n\t\t\t\ts++; lenTrim = 1;\n\t\t\t\tif (*s == 'l' || *s == 'L') {\n\t\t\t\t\t*s++ = ' ';\n\t\t\t\t\ttype |= 2;\n\t\t\t\t} else s[-1] = ' ';\n\t\t\t} else if (*s == 'i' || *s == 'I') {\n\t\t\t\tif (s[1] == '8') {\n\t\t\t\t\ts[0] = s[1] = ' ';\n\t\t\t\t\ts += 2; lenTrim = 2;\n\t\t\t\t} else {\n\t\t\t\t\tif (s[1] == '6' && s[2] == '4') {\n\t\t\t\t\t\ttype |= 2;\n\t\t\t\t\t\ts[0] = 'L'; lenTrim = 2;\n\t\t\t\t\t} else if ((s[1] == '3' && s[2] == '2') || (s[1] == '1' && s[2] == '6')) {\n\t\t\t\t\t\ts[0] = ' '; lenTrim = 3;\n\t\t\t\t\t} else _Err(s, \"unexpected\");\n\t\t\t\t\t\n\t\t\t\t\ts[1] = s[2] = ' ';\n\t\t\t\t\ts += 3;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (_IsCharIdent(*s)) _Err(s, \"unexpected\");\n\t\treturn (int)(s - lenTrim - s0);\n\t}\n}\n"
  },
  {
    "path": "Scripts/old/Windows SDK to C#/@SDK converter/Types.cs",
    "content": "﻿namespace SdkConverter;\n\nunsafe partial class Converter {\n\t/// <summary>\n\t/// Converts simple typedef or function typedef declaration.\n\t/// _i must be at 'typedef'. Finally it will be at semicolon.\n\t/// </summary>\n\tvoid _DeclareTypedef() {\n\t\t//detect typedef type\n\t\tint typedefType = 0;\n\t\t//0 - typedef [struct|enum|etc] X Y...\n\t\t//1 - typedef ... (FUNCTYPE)(...)\n\t\t//2 - typedef struct|enum|etc [X] [:base] {...} Y...\n\t\tint i = ++_i;\n\t\tfor (; ; i++) {\n\t\t\tchar c = *T(i);\n\t\t\tif (c == '{') typedefType = 2; else if (c == '(') typedefType = 1; else if (c != ';') continue;\n\t\t\tbreak;\n\t\t}\n\t\t//print.it(_tok[_i], typedefType);\n\t\t\n\t\tvar d = new _FINDTYPEDATA();\n\t\t\n\t\tif (typedefType == 2) {\n\t\t\tvar k = _FindKeyword(_i, _KeywordT.TypeDecl);\n\t\t\t\n\t\t\t//find alias name. _DeclareType() will use it.\n\t\t\ti = _SkipEnclosed(i) + 1;\n\t\t\tif (_TokIsIdent(i) && _TokIsChar(i + 1, ';', ',')) d.inTypedefNameToken = i;\n\t\t\t\n\t\t\t_DeclareType(ref d);\n\t\t} else {\n\t\t\t_FindTypename(false, ref d);\n\t\t\tint iTypeName = _i++;\n\t\t\t\n\t\t\tif (typedefType == 1) {\n\t\t\t\tint rtPtr = 0;\n\t\t\t\tfor (; _TokIsChar(_i, '*', '&'); _i++) rtPtr++;\n\t\t\t\t\n\t\t\t\t_DeclareTypedefFunc(d.outSym, rtPtr, d.outIsConst, iTypeName);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\t\n\t\t_Symbol aliasOf = d.outSym;\n\t\tint ptr = 0; //0 - start or after comma, >0 pointer level, <0 after identifier\n\t\tint ptrBase = 0;\n\t\t\n\t\t//don't declare typedef to typedef\n\t\tif (typedefType == 0 && aliasOf is _Typedef) {\n\t\t\tvar t = aliasOf as _Typedef;\n\t\t\t//print.it($\"unaliased: {_tok[_i-1]} -> {t.aliasOf.csTypename}\");\n\t\t\taliasOf = t.aliasOf;\n\t\t\tptrBase = t.ptr;\n\t\t}\n\t\t\n\t\tfor (; ; _i++) {\n\t\t\tif (_TokIsIdent(_i)) {\n\t\t\t\tif (ptr < 0) goto ge;\n\t\t\t\tif (_TokIsChar(_i + 1, '[')) { //typedef X Y[n];\n\t\t\t\t\tstring attr, typ = _ConvertTypeName(aliasOf, ref ptr, false, 0, _TypeContext.Member, out attr);\n\t\t\t\t\tDebug.Assert(attr == null); //never mind, 0 in SDK\n\t\t\t\t\t__DeclareTypedef_Array(typ);\n\t\t\t\t} else {\n\t\t\t\t\tbool defined = false;\n\t\t\t\t\tif (aliasOf is _CppType) {\n\t\t\t\t\t\t\n\t\t\t\t\t\t//convert LONG_PTR etc to nint, not to long/ulong\n\t\t\t\t\t\tif (_is32bit ? (aliasOf.csTypename == \"int\" || aliasOf.csTypename == \"uint\") : (aliasOf.csTypename == \"long\" || aliasOf.csTypename == \"ulong\")) {\n\t\t\t\t\t\t\tstring name = _TokToString(_i);\n\t\t\t\t\t\t\tif (name.Ends(\"_PTR\") || name == \"POINTER_64_INT\") {\n\t\t\t\t\t\t\t\t//print.it(_tok[_i], aliasOf.csTypename, ptr);\n\t\t\t\t\t\t\t\taliasOf = _sym_LPARAM;\n\t\t\t\t\t\t\t} //else print.it(_tok[_i], aliasOf.csTypename, ptr);\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t} else if (_TokIs(_i, aliasOf.csTypename)) {\n\t\t\t\t\t\tdefined = true;\n\t\t\t\t\t} else if (_nsCurrent == 0 && aliasOf.forwardDecl && ptrBase + ptr == 0) {\n\t\t\t\t\t\tstring alias = _TokToString(_i);\n\t\t\t\t\t\t//if(!(aliasOf is _Struct)) print.it(aliasOf); //0 in SDK\n\t\t\t\t\t\t//print.it(aliasOf.csTypename, alias, ptr != 0);\n\t\t\t\t\t\ttry { _forwardDeclTypedefs.Add(aliasOf.csTypename, _i); } catch { } //info: try/catch because SDK contains 1 duplicate typedef\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tif (!defined) _AddSymbol(_i, new _Typedef(aliasOf, ptr + ptrBase, d.outIsConst));\n\t\t\t\t}\n\t\t\t\tptr = -1;\n\t\t\t} else {\n\t\t\t\tswitch (*T(_i)) {\n\t\t\t\tcase '*':\n\t\t\t\tcase '&':\n\t\t\t\t\tif (ptr < 0) goto ge;\n\t\t\t\t\tptr++;\n\t\t\t\t\tbreak;\n\t\t\t\tcase ',':\n\t\t\t\t\tif (ptr >= 0) goto ge;\n\t\t\t\t\tptr = 0;\n\t\t\t\t\tbreak;\n\t\t\t\tcase ';':\n\t\t\t\t\tif (ptr >= 0) goto ge;\n\t\t\t\t\treturn;\n\t\t\t\tdefault: goto ge;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tge:\n\t\t_Err(_i, \"unexpected\");\n\t}\n\t\n\tvoid __DeclareTypedef_Array(string type) {\n\t\tint iName = _i++;\n\t\tstring name = _TokToString(iName);\n\t\t//print.it(name, type);\n\t\tvar t = new _PARAMDATA { typeName = type, name = \"a\" };\n\t\t_ConvertCArray(ref t); _i--;\n\t\t_sbType.AppendFormat(\"\\r\\ninternal struct {0} {{\\r\\n\\t{1} public {2} {3};\\r\\n\", name, t.attributes, type, t.name);\n\t\tif (t.comment != null) _sbType.Append('\\t').AppendLine(t.comment);\n\t\t_sbType.AppendLine(\"}\");\n\t\t_AddSymbol(iName, new _Struct(name, false));\n\t\t//info:\n\t\t//SDK has ~15 such typedefs. Some of them are [1] ie used as variable-length array.\n\t\t//We convert to struct, because would be difficult to have this as typedef.\n\t}\n\t\n\t//_Symbol _CopyStruct(_Symbol xFrom, string name) {\n\t//\tvar t = _Struct.Copy(xFrom as _Struct, name);\n\t//\ttry { _AddSymbol(_i, t); } catch { return xFrom; } //info: 3 in SDK already defined\n\t//\t_FormatStruct(t);\n\t//\treturn t;\n\t//}\n\t\n\t//_Symbol _CopyEnum(_Symbol xFrom, string name) {\n\t//\tvar t = _Enum.Copy(xFrom as _Enum, name);\n\t//\ttry { _AddSymbol(_i, t); } catch { return xFrom; } //info: 0 in SDK already defined\n\t//\t_FormatEnum(t);\n\t//\treturn t;\n\t//}\n\t\n\t//When 'typedef struct X Y;', and X is forward declaration, we add X=Y to this map. Then, when defining X, we swap names Y with X.\n\t//Without this, everywhere would be used X, which usually is a tag name such as tagVARIANT.\n\t//Dictionary<string, List<_Token>> _forwardTypedeclMap = new Dictionary<string, List<_Token>>();\n\tDictionary<string, int> _forwardDeclTypedefs = new Dictionary<string, int>();\n\t\n\t/// <summary>\n\t/// Converts function typedef to C# delegate.\n\t/// _i must be at the first '(' (after typename and * etc). Finally it will be at semicolon.\n\t/// Info: script replaced 'typedef TYPE [callConv] FUNCTYPE(' to 'typedef TYPE ([callConv]*FUNCTYPE)('.\n\t/// </summary>\n\t/// <param name=\"t\">Return type.</param>\n\t/// <param name=\"ptr\">Pointer level.</param>\n\t/// <param name=\"isConst\">Is const.</param>\n\t/// <param name=\"iTokTypename\">Type name token index.</param>\n\t/// <param name=\"inl\">Use for inline function type definition.</param>\n\tvoid _DeclareTypedefFunc(_Symbol t, int ptr, bool isConst, int iTokTypename, _INLINEFUNCTYPEDATA inl = null) {\n\t\t//return type\n\t\tstring returnAttr, returnType = _ConvertTypeName(t, ref ptr, isConst, iTokTypename, _TypeContext.Return, out returnAttr);\n\t\t\n\t\tDebug.Assert(_TokIsChar(_i, '('));\n\t\t_i++;\n\t\t\n\t\t//calling convention\n\t\tint iCallConv = 0;\n\t\tif (_TokIsIdent(_i)) iCallConv = _i++;\n\t\tif (!_TokIsChar(_i, '*')) _Err(_i, \"unexpected\"); //todo: maybe also can be & (use _TokIsPtr)\n\t\t_i++;\n\t\tstring callConv = iCallConv != 0 ? _ConvertCallConv(iCallConv) : \"Cdecl\";\n\t\t\n\t\t//name\n\t\tint iName = _i;\n\t\tstring name = null;\n\t\tif (inl == null) {\n\t\t\tif (_SymbolExists(_i, false)) _Err(_i, \"name already exists\");\n\t\t\tname = _TokToString(_i);\n\t\t} else {\n\t\t\tif (_TokIsIdent(_i)) inl.paramName = _TokToString(_i);\n\t\t\telse { _i--; inl.paramName = \"param\" + inl.paramIndex; }\n\t\t\t\n\t\t\tinl.typeName = name = (inl.parentName + \"_\" + inl.paramName);\n\t\t}\n\t\tif (!_TokIsChar(++_i, ')')) _Err(_i, \"unexpected\");\n\t\t_i++;\n\t\t\n\t\t//start formatting\n\t\tStringBuilder sb = inl != null ? _sbInlineDelegate : _sbType;\n\t\tsb.AppendLine();\n\t\tif (callConv != null) sb.AppendFormat(\"[UnmanagedFunctionPointer(CallingConvention.{0})]\\r\\n\", callConv);\n\t\tif (returnAttr != null) sb.AppendLine(returnAttr);\n\t\tsb.AppendFormat(\"internal delegate {0} {1}\", returnType, name);\n\t\t\n\t\t//parameters\n\t\t_ConvertParameters(sb, name, _TypeContext.DelegateParameter);\n\t\t\n\t\tvar x = new _Callback(name);\n\t\tif (inl == null) _AddSymbol(iName, x);\n\t\telse _AddSymbol(name, x, iName);\n\t\t\n\t\tif (inl != null) {\n\t\t\tinl.t = x;\n\t\t}\n\t}\n\t\n\tclass _INLINEFUNCTYPEDATA {\n\t\tpublic readonly string parentName; //in\n\t\tpublic int paramIndex; //in\n\t\tpublic string typeName; //out\n\t\tpublic string paramName; //out\n\t\tpublic _Callback t; //out\n\t\t\n\t\tpublic _INLINEFUNCTYPEDATA(string parentName, int paramIndex) {\n\t\t\tthis.parentName = parentName;\n\t\t\tthis.paramIndex = paramIndex;\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Converts enum definition.\n\t/// _i must be at 'enum'. Finally it will be at semicolon.\n\t/// </summary>\n\tvoid __DeclareEnum(ref _FINDTYPEDATA d) {\n\t\t//is in global namespace?\n\t\tbool isScoped = _nsCurrent > 0;\n\t\tif (_TokIs(++_i, \"class\") || _TokIs(_i, \"struct\")) { isScoped = true; _i++; }\n\t\t\n\t\t_Enum x = null;\n\t\t\n\t\t//name\n\t\tstring name = null;\n\t\tint iName = 0;\n\t\tif (_TokIsIdent(_i)) {\n\t\t\tbool wasForwardDecl;\n\t\t\tif (__MustSwapTagWithAlias(d.inTypedefNameToken, out wasForwardDecl)) {\n\t\t\t\tiName = d.inTypedefNameToken;\n\t\t\t\tname = _TokToString(iName);\n\t\t\t\t_AddSymbol(iName, x = new _Enum(name, false));\n\t\t\t\t_AddSymbol(_i++, new _Typedef(x, 0, false, wasForwardDecl));\n\t\t\t} else {\n\t\t\t\tiName = _i++;\n\t\t\t\tname = _TokToString(iName);\n\t\t\t\t\n\t\t\t\t//is forward declaration? Rare but allowed.\n\t\t\t\tif (_TokIsChar(_i, ';')) {\n\t\t\t\t\t_AddSymbol(iName, d.outSym = new _Enum(name, true));\n\t\t\t\t\t//info: don't need name in this case\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\t_ns[_nsCurrent + 1].Clear();\n\t\tStringBuilder sb = _ns[_nsCurrent + 1].sb;\n\t\t\n\t\t//base type\n\t\tstring sBaseType = null;\n\t\tif (_TokIsChar(_i, ':')) {\n\t\t\t_Symbol baseType = _FindType(++_i, true);\n\t\t\tif (0 != _Unalias(_i, ref baseType) || !(baseType is _CppType)) _Err(_i, \"unexpected\");\n\t\t\tsBaseType = baseType.csTypename;\n\t\t\t_i++;\n\t\t}\n\t\t\n\t\tif (!_TokIsChar(_i, '{')) _Err(_i, \"unexpected\");\n\t\t_i++;\n\t\t\n\t\tif (iName == 0) {\n\t\t\tiName = d.inTypedefNameToken;\n\t\t\tif (iName == 0) {\n\t\t\t\t//borrow enum name from first member. Benefits: easier for users to find the enum; will not be a name conflict; easy.\n\t\t\t\tif (!_TokIsIdent(_i)) _Err(_i, \"unexpected\");\n\t\t\t\tiName = _i;\n\t\t\t\t//print.it(_TokToString(iName));\n\t\t\t}\n\t\t\tname = _TokToString(iName);\n\t\t}\n\t\t\n\t\t//body\n\t\tsb.AppendLine(\" {\");\n\t\tbool isFlags = false;\n\t\tuint value = 0, nextValue = 0;\n\t\tfor (; ; nextValue++) {\n\t\t\tif (!_TokIsIdent(_i)) _Err(_i, \"unexpected\");\n\t\t\tint iMember = _i++;\n\t\t\tstring memberName = _TokToString(iMember);\n\t\t\tsb.Append('\\t'); sb.Append(memberName);\n\t\t\tchar c = *T(_i);\n\t\t\tif (c == '=') {\n\t\t\t\tint i0 = ++_i;\n\t\t\t\twhile (!_TokIsChar(_i, ',', '}')) _i++;\n\t\t\t\tif (_i == i0) _Err(_i, \"unexpected\");\n\t\t\t\t\n\t\t\t\t_ExpressionResult r = _Expression(i0, _i, memberName);\n\t\t\t\tswitch (r.typeS) {\n\t\t\t\tcase \"int\":\n\t\t\t\t\tvalue = r.valueI;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"uint\":\n\t\t\t\t\tvalue = r.valueI;\n\t\t\t\t\tif (!isFlags) { isFlags = true; sBaseType = \"uint\"; }\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\t_Err(i0, \"cannot convert\");\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (value != nextValue) {\n\t\t\t\t\tsb.Append(\" = \");\n\t\t\t\t\tsb.Append(r.valueS);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tnextValue = value;\n\t\t\t} else if (c == ',' || c == '}') {\n\t\t\t\tvalue = nextValue;\n\t\t\t} else _Err(_i, \"unexpected\");\n\t\t\t\n\t\t\t//add to a dictionary, to resolve other enum and #define\n\t\t\tif (!isScoped) {\n\t\t\t\tulong v = value; if (isFlags) v |= 0x8000000000000000UL;\n\t\t\t\t//print.it(\"add\", _tok[iMember]);\n\t\t\t\t_enumValues[_tok[iMember]] = v;\n\t\t\t}\n\t\t\t\n\t\t\tif (_TokIsChar(_i, ',')) _i++;\n\t\t\tif (_TokIsChar(_i, '}')) break;\n\t\t\tsb.AppendLine(\",\");\n\t\t}\n\t\tsb.AppendLine(\"\\r\\n}\");\n\t\t_i++;\n\t\t\n\t\tif (sBaseType != null) sb.Insert(0, \" :\" + sBaseType);\n\t\t\n\t\tif (x == null) {\n\t\t\tif (_TryFindSymbolAs(iName, out x, false) && x.forwardDecl == true) x.forwardDecl = false;\n\t\t\telse _AddSymbol(iName, x = new _Enum(name, false));\n\t\t}\n\t\t\n\t\tsb.CopyTo(0, x.defAfterName = new char[sb.Length], 0, sb.Length);\n\t\tx.isFlags = isFlags;\n\t\t\n\t\td.outSym = x;\n\t\t\n\t\t_FormatEnum(x);\n\t}\n\t\n\tDictionary<_Token, ulong> _enumValues = new Dictionary<_Token, ulong>();\n\tbool _EnumFindValue(int iTokName, out ulong value, out _OP type) {\n\t\ttype = _OP.OperandInt;\n\t\t//print.it(\"find\", _tok[iTokName]);\n\t\tif (!_enumValues.TryGetValue(_tok[iTokName], out value)) return false;\n\t\tif ((value & 0x8000000000000000UL) != 0) { value = (uint)value; type = _OP.OperandUint; }\n\t\treturn true;\n\t}\n\t\n\tvoid _FormatEnum(_Enum x) {\n\t\tStringBuilder sb = _sbType;\n\t\tsb.AppendLine();\n\t\tif (x.isFlags) sb.AppendLine(\"[Flags]\");\n\t\tsb.Append(\"internal enum \");\n\t\tsb.Append(x.csTypename);\n\t\tsb.Append(x.defAfterName);\n\t}\n\t\n\t/// <summary>\n\t/// Converts struct/union/class/__interface/enum/typedef definition or forward declaration.\n\t/// _i must be at 'struct' etc. Finally it will be after }. If skipVariablesUntilSemicolon - at ;.\n\t/// </summary>\n\tvoid _DeclareType(ref _FINDTYPEDATA d, bool skipVariablesUntilSemicolon = false) {\n\t\t//struct, union, class, or __interface?\n\t\tbool isUnion = false, isInterface = false, isDualInterface = false, isClass = false;\n\t\tstring access = \"public\", inheritInterface = null;\n\t\tchar keyword = *T(_i);\n\t\tswitch (keyword) {\n\t\tcase 't': _DeclareTypedef(); return;\n\t\tcase 'e': __DeclareEnum(ref d); return;\n\t\tcase 'u': isUnion = true; break;\n\t\tcase 'c': isClass = true; access = \"private\"; break;\n\t\tcase '_': isInterface = true; break;\n\t\t}\n\t\t_i++;\n\t\t\n\t\t//uuid (was '__declspec(uuid', replaced in script)\n\t\tstring uuid = null;\n\t\tif (_TokIs(_i, \"uuid\")) {\n\t\t\tif (!_TokIsChar(++_i, '(') || !_TokIsChar(++_i, '\"') || !_TokIsChar(_i + 1, ')')) _Err(_i, \"unexpected\");\n\t\t\tuuid = _TokToString(_i);\n\t\t\t_i += 2;\n\t\t}\n\t\t\n\t\t//name\n\t\t_Struct x = null;\n\t\tint iName = 0;\n\t\tstring name = null;\n\t\tif (_TokIsIdent(_i)) {\n\t\t\tbool isThisForwardDecl = _TokIsChar(_i + 1, ';');\n\t\t\t\n\t\t\t//look in _forwardDeclTypedefs, maybe need to swap this tag with previously declared alias\n\t\t\tif (d.inTypedefNameToken == 0 && _nsCurrent == 0 && !isThisForwardDecl) {\n\t\t\t\tstring tag = _TokToString(_i);\n\t\t\t\tif (_forwardDeclTypedefs.TryGetValue(tag, out int k)) {\n\t\t\t\t\t//print.it($\"<><c 0xff0000>{tag}, {_TokToString(k)}</c>\");\n\t\t\t\t\td.inTypedefNameToken = k;\n\t\t\t\t\t_forwardDeclTypedefs.Remove(tag);\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tif (!isThisForwardDecl && __MustSwapTagWithAlias(d.inTypedefNameToken, out bool wasForwardDecl)) {\n\t\t\t\tiName = d.inTypedefNameToken;\n\t\t\t\tname = _TokToString(iName);\n\t\t\t\t_AddSymbol(iName, x = new _Struct(name, false));\n\t\t\t\t_AddSymbol(_i++, new _Typedef(x, 0, false, wasForwardDecl));\n\t\t\t} else {\n\t\t\t\tiName = _i++;\n\t\t\t\tname = _TokToString(iName);\n\t\t\t\t\n\t\t\t\t//forward-declare this because may contain a member of this pointer type\n\t\t\t\tif (!(_TryFindSymbolAs(iName, out x, false) && x.forwardDecl)) //info: if !x.forwardDecl _AddSymbol will throw\n\t\t\t\t\t_AddSymbol(iName, x = new _Struct(name, isThisForwardDecl));\n\t\t\t\t\n\t\t\t\tif (isThisForwardDecl) {\n\t\t\t\t\td.outSym = x;\n\t\t\t\t\tx.isInterface = isInterface;\n\t\t\t\t\tx.isClass = isClass;\n\t\t\t\t\t//info: don't need name in this case\n\t\t\t\t\t//if(uuid != null) print.it(isClass); //all True\n\t\t\t\t\tif (isClass && uuid != null && _nsCurrent == 0) { //coclass\n\t\t\t\t\t\t//print.it(keyword, name, uuid);\n\t\t\t\t\t\t_DeclareGuid(\"CLSID_\", name, uuid);\n\t\t\t\t\t\t_sbInterface.AppendFormat(\n\t\t\t\t\t\t\t\"\\r\\n[ComImport, Guid({0}), ClassInterface(ClassInterfaceType.None)]\"\n\t\t\t\t\t\t\t+ \"\\r\\ninternal class {1} {{}}\\r\\n\"\n\t\t\t\t\t\t\t, uuid, name);\n\t\t\t\t\t} else if (!isInterface && _interfaces.Contains(name)) {\n\t\t\t\t\t\tx.isInterface = true;\n\t\t\t\t\t}\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (d.inTypedefNameToken != 0) {\n\t\t\tiName = d.inTypedefNameToken;\n\t\t\tname = _TokToString(iName);\n\t\t} //later will get name from variable\n\t\t\n\t\t//skip IUnknown and IDispatch\n\t\tif (isInterface || uuid != null) {\n\t\t\tbool skip = false;\n\t\t\tif (!__haveIUnknown && name == \"IUnknown\") __haveIUnknown = skip = true;\n\t\t\telse if (!__haveIDispatch && name == \"IDispatch\") __haveIDispatch = skip = true;\n\t\t\tif (skip) {\n\t\t\t\t_SkipStatement();\n\t\t\t\tx.forwardDecl = false;\n\t\t\t\tx.isInterface = true;\n\t\t\t\tx.isDualInterface = name == \"IDispatch\";\n\t\t\t\t_DeclareGuid(\"IID_\", name, uuid);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\t\n\t\t//is interface?\n\t\tif (__haveIUnknown && (isInterface || uuid != null) && _TokIsChar(_i, ':')) {\n\t\t\t//info: SDK has only few non-interface struct that have a base, and they don't have uuid.\n\t\t\tDebug.Assert(_interfaces.Contains(name));\n\t\t\tisInterface = true;\n\t\t\t_DeclareGuid(\"IID_\", name, uuid);\n\t\t}\n\t\t\n\t\t//get empty namespace (symbols + StringBuilder) from stack\n\t\t_ns[_nsCurrent + 1].Clear();\n\t\tStringBuilder sb = _ns[_nsCurrent + 1].sb;\n\t\t\n\t\t//base type\n\t\tif (_TokIsChar(_i, ':')) {\n\t\t\t_i++;\n\t\t\tif (_TokIsIdent(_i + 1) && _FindKeyword(_i).kwType == _KeywordT.PubPrivProt) _i++;\n\t\t\t\n\t\t\tbool addedBaseMembers = false;\n\t\t\tfor (int iBase = 0; ; iBase++, _i++) { //support multiple inheritance, although currently none in SDK\n\t\t\t\t_Symbol baseType = _FindType(_i, true);\n\t\t\t\tint ptr = _Unalias(_i, ref baseType);\n\t\t\t\tvar bt = baseType as _Struct;\n\t\t\t\tif (bt == null || bt.forwardDecl || ptr != 0) _Err(_i, \"unexpected\");\n\t\t\t\tif (bt.members != null) { //null if IUnknown/IDispatch or no members\n\t\t\t\t\tif (bt.members[0] != '/') {\n\t\t\t\t\t\tsb.Append(\"// \");\n\t\t\t\t\t\tsb.AppendLine(bt.csTypename);\n\t\t\t\t\t}\n\t\t\t\t\tif (isInterface) {\n\t\t\t\t\t\t//print.it(iBase, bt.csTypename); //iBase all 0\n\t\t\t\t\t\tinheritInterface = inheritInterface == null ? bt.csTypename : inheritInterface + \", \" + bt.csTypename;\n\t\t\t\t\t\tsb.Append(new string(bt.members).RxReplace(@\"\\[PreserveSig\\] (?!new )\\K\", \"new \"));\n\t\t\t\t\t\t//info: for COM interfaces need to add base members, even if base interface specified.\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsb.Append(bt.members);\n\t\t\t\t\t}\n\t\t\t\t\taddedBaseMembers = true;\n\t\t\t\t}\n\t\t\t\tif (isInterface) isDualInterface = bt.isDualInterface; //inherits IDispatch or another dual interface\n\t\t\t\tif (!_TokIsChar(++_i, ',')) break;\n\t\t\t}\n\t\t\tif (addedBaseMembers) { sb.Append(\"// \"); sb.AppendLine(name); }\n\t\t}\n\t\t\n\t\t//members\n\t\tif (!_TokIsChar(_i, '{')) _Err(_i, \"unexpected\");\n\t\tint iBodyStart = _i++;\n\t\tstring memberAttr = isUnion ? \"[FieldOffset(0)] \" : null;\n\t\tbool hasBitfields = false;\n\t\t\n\t\tif (_nsCurrent == 0) _anonymousStructSuffixCounter = 0;\n\t\tif (!isInterface) _sbType = _ns[++_nsCurrent].sb;\n\t\t\n\t\tfor (int iMember = 1, iBitfields = 1, iInterfaceFunc = 1; !_TokIsChar(_i, '}'); _i++, iMember++) {\n\t\t\t\n\t\t\t//public/private/protected?\n\t\t\tif (_TokIsChar(_i + 1, ':') && 0 != _TokIs(_i, \"public\", \"private\", \"protected\")) {\n\t\t\t\taccess = _TokToString(_i);\n\t\t\t\t_i++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t\n\t\t\t//template etc?\n\t\t\tif (_TokIs(_i, \"template\")) {\n\t\t\t\t_SkipStatement();\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t\n\t\t\t//interface function?\n\t\t\tif (isInterface) {\n\t\t\t\t_InterfaceFunction(sb, iInterfaceFunc++);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t\n\t\t\t//bitfield?\n\t\t\tif (__IsBitfield()) {\n\t\t\t\t_Bitfields(ref iBitfields, memberAttr);\n\t\t\t\thasBitfields = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t\n\t\t\t//type\n\t\t\t_ParseParamOrMember(_TypeContext.Member, out _PARAMDATA t, name ?? \"TYPEOF\", iMember);\n\t\t\t\n\t\t\tif (t.isNestedTypeDefinitionWithoutVariables) {\n\t\t\t\t//if anonymous type definition without a variable, its members are part of parent in C++, therefore in C# need to add a variable of that type\n\t\t\t\tif (t.isAnonymousTypeDefinitionWithoutVariables) {\n\t\t\t\t\tsb.AppendFormat(\"{3}{2} {0} {1};\\r\\n\", t.typeName, \"_\" + iMember, access, memberAttr);\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t\n\t\t\t//member variable\n\t\t\tfor (; ; ) { //support TYPE a, b;\n\t\t\t\tswitch (*T(_i)) {\n\t\t\t\tcase ';': case ',': break;\n\t\t\t\tcase '\\0': _Err(iBodyStart, \"no }\"); break;\n\t\t\t\tdefault: _Err(_i, \"unexpected\"); break;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (memberAttr != null) {\n\t\t\t\t\tif (t.attributes != null && t.attributes.Starts(\"//\")) sb.Append(\"//\"); //could instead place memberAttr after attributes, but then looks not good visually\n\t\t\t\t\tsb.Append(memberAttr);\n\t\t\t\t}\n\t\t\t\tif (t.attributes != null) { sb.Append(t.attributes); sb.Append(' '); }\n\t\t\t\tsb.AppendFormat(\"{2} {0} {1};\\r\\n\", t.typeName, t.name, access);\n\t\t\t\tif (t.comment != null) {\n\t\t\t\t\tsb.AppendLine(t.comment);\n\t\t\t\t\tt.comment = null;\n\t\t\t\t}\n\t\t\t\tif (!_TokIsChar(_i, ',')) break;\n\t\t\t\t_i++;\n\t\t\t\t\n\t\t\t\t//ignore *. Assume never will be 'TYPE *a, b;' or 'TYPE a, *b;.\n\t\t\t\twhile (_TokIsChar(_i, '*', '&')) _i++;\n\t\t\t\t//int ptr = 0;\n\t\t\t\t//while(_TokIsChar(_i, '*', '&')) { _i++; ptr++; }\n\t\t\t\t//print.it(ptr);\n\t\t\t\t\n\t\t\t\tif (!_TokIsIdent(_i)) _Err(_i, \"unexpected\");\n\t\t\t\tt.name = _TokToString(_i++);\n\t\t\t\t\n\t\t\t\tif (_TokIsChar(_i, '[')) {\n\t\t\t\t\t//_Err(_i, \"example\"); //0 in SDK\n\t\t\t\t\tt.attributes = null;\n\t\t\t\t\t_ConvertCArray(ref t);\n\t\t\t\t} else if (t.attributes != null) {\n\t\t\t\t\t_Err(_i, \"unexpected\"); //assume never will be 'TYPE a[n], b'\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (!isInterface) _sbType = _ns[--_nsCurrent].sb;\n\t\t\n\t\t_i++; //skip }\n\t\tif (skipVariablesUntilSemicolon) {\n\t\t\t//skip variables after }\n\t\t\twhile (!_TokIsChar(_i, ';', '\\0')) _i++;\n\t\t}\n\t\t\n\t\tif (iName == 0) {\n\t\t\tif (skipVariablesUntilSemicolon) return; //skip this struct/var definition (it is global 'struct {...} var;')\n\t\t\t\n\t\t\t//use variable name from 'struct{...}var;', or _anonymousStructSuffixCounter if just 'struct{...};'\n\t\t\tbool isVar = _TokIsIdent(_i);\n\t\t\tname = isVar ? (\"TYPEOF_\" + _TokToString(_i)) : (\"TYPE_\" + (++_anonymousStructSuffixCounter).ToString());\n\t\t\t_AddSymbol(name, x = new _Struct(name, false), _i);\n\t\t\t\n\t\t\td.outIsAnonymousTypeDefinition = true;\n\t\t} else {\n\t\t\tif (x == null) _AddSymbol(name, x = new _Struct(name, false), iName); //typedef struct {...} name;\n\t\t\telse x.forwardDecl = false;\n\t\t}\n\t\t\n\t\td.outSym = x;\n\t\t\n\t\tif (sb.Length > 0) {\n\t\t\tsb.CopyTo(0, x.members = new char[sb.Length], 0, sb.Length);\n\t\t\tsb.Clear();\n\t\t}\n\t\t\n\t\t//attributes\n\t\tif (isInterface) {\n\t\t\tsb.AppendFormat(\"[ComImport, Guid({0}){1}\\r\\n\", uuid, isDualInterface ? \"] //dual\" : \", InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\");\n\t\t\tx.isInterface = true;\n\t\t\tx.isDualInterface = isDualInterface;\n\t\t} else {\n\t\t\tif (hasBitfields) sb.AppendLine(\"[DebuggerStepThrough]\");\n\t\t\tif (_pack != 8) sb.AppendFormat(\"[StructLayout(LayoutKind.{0}, Pack={1})]\\r\\n\", isUnion ? \"Explicit\" : \"Sequential\", _pack);\n\t\t\telse if (isUnion) sb.AppendLine(\"[StructLayout(LayoutKind.Explicit)]\");\n\t\t}\n\t\tif (sb.Length > 0) sb.CopyTo(0, x.attributes = new char[sb.Length], 0, sb.Length);\n\t\t\n\t\t_FormatStruct(x, inheritInterface);\n\t}\n\t\n\tvoid _FormatStruct(_Struct x, string inheritInterface) {\n\t\tStringBuilder sb = x.isInterface ? _sbInterface : _sbType;\n\t\tsb.AppendLine();\n\t\tif (x.attributes != null) sb.Append(x.attributes);\n\t\tsb.Append(x.isInterface ? \"internal interface \" : \"internal struct \");\n\t\tsb.Append(x.csTypename);\n\t\tif (inheritInterface != null) sb.Append(\" : \").Append(inheritInterface);\n\t\tif (x.members != null) {\n\t\t\tsb.Append(\" {\\r\\n\");\n\t\t\tint offs = sb.Length;\n\t\t\tsb.Append(x.members);\n\t\t\tsb.Replace(\"\\n\", \"\\n\\t\", offs - 2, x.members.Length); //tab-indent members\n\t\t\tsb.AppendLine(\"}\");\n\t\t} else sb.AppendLine(\" { }\");\n\t}\n\t\n\t//Returns true if must declare aliasToken as this struct|enum, and this struct|enum as aliasToken.\n\t//For example, it changes 'typedef struct tagX{...} X;' to 'typedef struct X{...} tagX;'.\n\tbool __MustSwapTagWithAlias(int aliasToken, out bool wasForwardDecl) {\n\t\twasForwardDecl = false;\n\t\tif (aliasToken == 0) return false;\n\t\tif (_nsCurrent != 0) return false;\n\t\tif (_tok[_i].Equals(_tok[aliasToken])) return false; //typedef struct X{...}X\n\t\t\n\t\t__RemoveForwardDeclIfExists(aliasToken, false);\n\t\twasForwardDecl = __RemoveForwardDeclIfExists(_i, true);\n\t\t\n\t\treturn true;\n\t}\n\t\n\tbool __RemoveForwardDeclIfExists(int iTok, bool tag) {\n\t\tDebug.Assert(_nsCurrent == 0);\n\t\tvar d = _ns[0].sym;\n\t\t_Token token = _tok[iTok];\n\t\t_Symbol x;\n\t\tif (!d.TryGetValue(token, out x)) return false;\n\t\tint ptr = _Unalias(iTok, ref x);\n\t\t//print.it(tag, x.GetType(), _TokToString(iTok), x.csTypename, ptr, x.forwardDecl);\n\t\tif (!x.forwardDecl || ptr != 0) _Err(iTok, \"already exists\");\n\t\td.Remove(token);\n\t\treturn true;\n\t}\n\t\n\t#region bitfields\n\t\n\tvoid _Bitfields(ref int iBitfields, string memberAttr) {\n\t\tint nBitsType = 0, nBitsTypePrev = 0, fieldOffset = 0;\n\t\tstring csTypeName = null, bitHolderVar = null;\n\t\tbool isUnsigned;\n\t\t\n\t\tfor (; ; ) {\n\t\t\t//type\n\t\t\tnBitsType = __BitfieldGetType(out csTypeName, out isUnsigned);\n\t\t\tif (nBitsType != nBitsTypePrev) { nBitsTypePrev = nBitsType; bitHolderVar = null; }\n\t\t\t_i++;\n\t\t\tg1:\n\t\t\t//name\n\t\t\tstring varName = _TokIsChar(_i, ':') ? null : _TokToString(_i++);\n\t\t\t_i++;\n\t\t\t//field size\n\t\t\tint fieldSize = Api.strtoi(T(_i));\n\t\t\tif (fieldSize < 1 || fieldSize > nBitsType) _Err(_i, \"unexpected\");\n\t\t\t_i++;\n\t\t\t//add private member variable that holds the bitfields\n\t\t\tif (bitHolderVar == null || fieldOffset + fieldSize > nBitsType) {\n\t\t\t\tfieldOffset = 0;\n\t\t\t\tbitHolderVar = \"__bf_\" + iBitfields++;\n\t\t\t\t_sbType.AppendFormat(\"{2}private {0} {1};\\r\\n\", csTypeName, bitHolderVar, memberAttr);\n\t\t\t}\n\t\t\t//add property that get/set the bitfield\n\t\t\tif (varName != null) {\n\t\t\t\tulong mask = (1UL << fieldSize) - 1;\n\t\t\t\tulong maskWithOffset = mask << fieldOffset;\n\t\t\t\t\n\t\t\t\t//work around C# problems with signed/unsigned conversion\n\t\t\t\tstring op = \"~\";\n\t\t\t\tif (nBitsType > 32) {\n\t\t\t\t\tif (maskWithOffset > long.MaxValue) { maskWithOffset = ~maskWithOffset; op = null; }\n\t\t\t\t} else {\n\t\t\t\t\tif (maskWithOffset > int.MaxValue) { maskWithOffset = ~maskWithOffset & 0xffffffff; op = null; }\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t_sbType.AppendFormat(\n\t\t\t\t\t\"public {0} {1} {{ get {{ return {8}({2} >> {3} & 0x{4:X}{7}); }} \" +\n\t\t\t\t\t\"set {{ {2} = {8}(({2} & {6}0x{5:X}{7}) | ((value & 0x{4:X}{7}) << {3})); }} }}\\r\\n\",\n\t\t\t\t\tcsTypeName, varName, bitHolderVar, fieldOffset, mask, maskWithOffset,\n\t\t\t\t\top, isUnsigned ? \"U\" : \"\", nBitsType < 32 ? \"(\" + csTypeName + \")\" : \"\");\n\t\t\t}\n\t\t\t\n\t\t\tfieldOffset += fieldSize;\n\t\t\t\n\t\t\t//next bitfield?\n\t\t\tif (_TokIsChar(_i++, ',')) {\n\t\t\t\tif (!(_TokIsChar(_i + 1, ':') && _TokIsIdent(_i) && _TokIsChar(_i + 3, ';', ','))) _Err(_i, \"unexpected\");\n\t\t\t\tgoto g1;\n\t\t\t}\n\t\t\tif (!__IsBitfield()) break;\n\t\t}\n\t\t\n\t\t_i--;\n\t}\n\t\n\tbool __IsBitfield() {\n\t\tint i = _i;\n\t\tif (_TokIsChar(i + 2, ':')) {\n\t\t\tif (!_TokIsIdent(i++)) return false;\n\t\t} else if (!_TokIsChar(i + 1, ':')) return false; //nameless?\n\t\t\n\t\treturn _TokIsIdent(i) && _IsCharDigit(*T(i + 2)) && _TokIsChar(i + 3, ';', ',');\n\t}\n\t\n\t/// <summary>\n\t/// Returns bitfield size in bits. Gets C# type name.\n\t/// </summary>\n\tint __BitfieldGetType(out string csTypename, out bool isUnsigned) {\n\t\tcsTypename = null;\n\t\tisUnsigned = false;\n\t\t_Symbol t = _FindType(_i, true);\n\t\tif (0 == _Unalias(_i, ref t)) {\n\t\t\tvar ct = t as _CppType;\n\t\t\tif (ct != null && ct.sizeBytesCpp != 0) {\n\t\t\t\tcsTypename = ct.csTypename;\n\t\t\t\tisUnsigned = ct.isUnsigned;\n\t\t\t\treturn ct.sizeBytesCpp * 8;\n\t\t\t} else if (t == _sym_LPARAM) {\n\t\t\t\tcsTypename = _is32bit ? \"int\" : \"long\";\n\t\t\t\treturn _is32bit ? 32 : 64;\n\t\t\t\t//SDK has 1 such struct (union PSAPI_WORKING_SET_BLOCK), and there the bitfield is at the end, so in most cases it is safe to use C# long instead. Using nint is difficult because of its variable size.\n\t\t\t} else if(t is _Enum te) {\n\t\t\t\tcsTypename = \"int\";\n\t\t\t\treturn 32;\n\t\t\t}\n\t\t}\n\t\t_Err(_i, \"unexpected\");\n\t\treturn 0;\n\t}\n\t\n\t#endregion\n\t\n\tbool __haveIUnknown, __haveIDispatch;\n\t\n\tvoid _DeclareGuid(string prefix, string name, string uuid) {\n\t\tif (uuid == null) return;\n\t\tstring fullName = prefix + name;\n\t\tif (_guids.ContainsKey(fullName)) return;\n\t\tif (prefix == \"IID_\" && _guids.ContainsKey(\"DIID_\" + name)) return;\n\t\t//print.it(fullName);\n\t\t_sbVar.AppendFormat(\"\\r\\ninternal static Guid {0} = new({1});\\r\\n\", fullName, uuid);\n\t\t//note: not 'readonly' because then could not be passed as ref.\n\t}\n\t\n\tvoid _InterfaceFunction(StringBuilder sb, int index) {\n\t\tif (_TokIs(_i, \"virtual\")) _i++;\n\t\t\n\t\tvar d = new _FINDTYPEDATA();\n\t\t_FindTypename(false, ref d);\n\t\t_i++;\n\t\t\n\t\t//pointer\n\t\tint ptr = 0;\n\t\twhile (_TokIsChar(_i, '*', '&')) { _i++; ptr++; }\n\t\t\n\t\tif (_TokIs(_i, \"__stdcall\")) _i++;\n\t\t\n\t\tstring name = _TokToString(_i++);\n\t\t\n\t\t//some interfaces have IUnknown members\n\t\tbool skip = false;\n\t\tif (index <= 3) {\n\t\t\tif (index == 1 && name == \"QueryInterface\") skip = true;\n\t\t\tif (index == 2 && name == \"AddRef\") skip = true;\n\t\t\tif (index == 3 && name == \"Release\") skip = true;\n\t\t}\n\t\tif (skip) {\n\t\t\t_SkipEnclosed();\n\t\t\t_i++;\n\t\t} else {\n\t\t\tstring returnAttr, returnType = _ConvertTypeName(d.outSym, ref ptr, d.outIsConst, d.outTypenameToken, _TypeContext.ComReturn, out returnAttr);\n\t\t\t\n\t\t\t//correct preprocessor-replaced method names that matched #define Func FuncW. Also there are several such struct members, never mind.\n\t\t\tif (name.Ends(\"W\") && name.Length > 2 && char.IsLower(name[name.Length - 2])) {\n\t\t\t\t//print.it(name);\n\t\t\t\tname = name.Remove(name.Length - 1);\n\t\t\t}\n\t\t\t\n\t\t\t//start formatting\n\t\t\tsb.Append(\"[PreserveSig] \");\n\t\t\tif (returnAttr != null) { sb.Append(returnAttr); sb.Append(' '); }\n\t\t\tsb.Append(returnType); sb.Append(' '); sb.Append(name);\n\t\t\t\n\t\t\t//parameters\n\t\t\t_ConvertParameters(sb, name, _TypeContext.ComParameter);\n\t\t}\n\t\t\n\t\t//skip '= 0'\n\t\tif (_TokIsChar(_i, '=')) _i += 2;\n\t}\n\t\n\t/// <summary>\n\t/// Processes inline forward declaration such as 'struct X' in 'struct X * param' or 'struct X * Func(..)'.\n\t/// _i must be at 'struct' etc. Finally will be after it (_i++).\n\t/// Returns _Struct or _Enum, unaliased if was _Typedef.\n\t/// </summary>\n\t_Symbol _InlineForwardDeclaration() {\n\t\tbool en = *T(_i++) == 'e';\n\t\t_Symbol x;\n\t\tif (_TryFindSymbol(_i, out x, true) && 0 == _Unalias(_i, ref x)) {\n\t\t\tif (en) { if (x is _Enum) return x; } else if (x is _Struct) return x;\n\t\t}\n\t\tstring name = _TokToString(_i);\n\t\tif (en) {\n\t\t\tx = new _Enum(name, true);\n\t\t} else {\n\t\t\t_Struct ts = new _Struct(name, true);\n\t\t\tx = ts;\n\t\t\tif (_interfaces.Contains(name)) ts.isInterface = true;\n\t\t}\n\t\t_AddSymbol(_i, x, true);\n\t\treturn x;\n\t}\n\t\n\t/// <summary>\n\t/// Joins _sbType, _sbInlineDelegate, _func, and _sbInterface into single string.\n\t/// Replaces some identifiers in it etc.\n\t/// Clears the stringbuilders.\n\t/// </summary>\n\tstring _PostProcessTypesFunctionsInterfaces() {\n\t\t//join types etc into single string, for replacements\n\t\tvar sb = new StringBuilder();\n\t\tsb.Append(\"\\r\\n// TYPE\\r\\n\");\n\t\tsb.Append(_sbType); _sbType.Clear();\n\t\tsb.Append(_sbInlineDelegate); _sbInlineDelegate.Clear();\n\t\tsb.Append(\"\\r\\n// FUNCTION\\r\\n\");\n\t\tforeach (var v in _func) { sb.Append(v.Value); }\n\t\tsb.Append(\"\\r\\n// INTERFACE\\r\\n\");\n\t\tsb.Append(_sbInterface); _sbInterface.Clear();\n\t\tstring R = sb.ToString(); sb.Clear();\n\t\t\n\t\t//perf.first();\n\t\tforeach (var v in _ns[0].sym) {\n\t\t\tvar sym = v.Value;\n\t\t\t\n\t\t\t//most forward declarations were converted to definitions, but some don't have definitions; replace their pointers to IntPtr\n\t\t\tif (sym.forwardDecl) {\n\t\t\t\tvar ts = sym as _Struct;\n\t\t\t\t//if(ts==null) print.it(v.Key); //0 in SDK\n\t\t\t\tif (ts != null && ts.isClass) continue; //all in SDK are with GUID, converted to coclass\n\t\t\t\tstring s = v.Key.ToString();\n\t\t\t\t\n\t\t\t\t//Replace all found TYPE* and ref TYPE to IntPtr.\n\t\t\t\t//Call RxReplace 2 times because regex (a|b) is very slow.\n\t\t\t\tstring repl = \"IntPtr\";\n\t\t\t\tint n;\n\t\t\t\tif (ts != null && ts.isInterface) n = R.RxReplace($@\"\\b{s}\\b\", repl, out R); //decremented pointer level. 0 in SDK\n\t\t\t\telse n = R.RxReplace($@\"\\b{s}\\*\", repl, out R) + R.RxReplace($@\"\\bref {s}\\b\", repl, out R);\n\t\t\t\tif (n != 0) {\n\t\t\t\t\t//print.it($\"<><c 0xff0000>{n}  {s}* = IntPtr</c>\");\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t//if(_FindIdentifierInString(R, s) < 0) { print.it(\"no\", s); continue; } //5 in SDK\n\t\t\t\t//print.it(sym.csTypename, sym); //0 in SDK\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t\n\t\t\t//process typedefs\n\t\t\tif (sym is _Typedef td) {\n\t\t\t\tif (!td.wasForwardDecl) continue; //don't need to replace. Makes this replacement code 10 times faster.\n\t\t\t\tif (td.ptr != 0) continue; //the type is or will be resolved in some way\n\t\t\t\t_Symbol t = td.aliasOf;\n\t\t\t\tif (t.forwardDecl) continue; //the type is already resolved in some way\n\t\t\t\tif (!((t is _Struct) || (t is _Enum))) continue;\n\t\t\t\tstring s = v.Key.ToString();\n\t\t\t\t//if(_FindIdentifierInString(R, s) >= 0) { //makes slower\n\t\t\t\t//if(ts!=null) _FormatStruct(ts); else _FormatEnum(te);\n\t\t\t\tif (0 != R.RxReplace($@\"\\b{s}\\b\", t.csTypename, out R)) {\n\t\t\t\t\t//print.it($\"<><c 0xff0000>{s} = {t.csTypename}    {t}</c>\"); //all struct (0 enum) in SDK\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t//print.it($\"<><c 0x8000>{s} = {t.csTypename}    {t}</c>\"); //OK, just not used between typedef and struct definition\n\t\t\t}\n\t\t}\n\t\t//perf.nw();\n\t\t\n\t\t//remove W from struct/interface/delegate names\n\t\t//perf.first();\n\t\tforeach (var v in _defineW) {\n\t\t\t__RemoveAW(ref R, v.Key, v.Value);\n\t\t}\n\t\t//perf.next();\n\t\tforeach (var v in _ns[0].sym) { //some A/W are not #define but typedef, eg 'typedef MIXERCAPSW MIXERCAPS;'\n\t\t\tvar td = v.Value as _Typedef; if (td == null) continue;\n\t\t\tif (td.forwardDecl || td.ptr != 0) continue;\n\t\t\tstring name, nameW = td.aliasOf.csTypename;\n\t\t\tint suffixLen = 0;\n\t\t\tif (v.Key.len == nameW.Length - 1 && nameW.Ends(\"W\")) suffixLen = 1;\n\t\t\telse if (v.Key.len == nameW.Length - 2 && nameW.Ends(\"_W\")) suffixLen = 2;\n\t\t\telse continue;\n\t\t\tname = v.Key.ToString(); if (!nameW.Starts(name)) continue;\n\t\t\tint what; //struct/interface/delegate\n\t\t\tvar t = td.aliasOf as _Struct;\n\t\t\tif (t != null) what = t.isInterface ? 1 : 0; else if (td.aliasOf is _Callback) what = 2; else continue;\n\t\t\tif (suffixLen == 2) what |= 0x10000;\n\t\t\t__RemoveAW(ref R, name, what);\n\t\t}\n\t\t//perf.nw(); //800 1000 ms\n\t\t\n\t\t//remove other known A and old versions\n\t\tR.RxReplace(@\"(?ms)^internal struct PROPSHEETPAGE(?!W_V4\\b).+?^\\}\\r\\n\", \"\", out R);\n\t\tR.RxReplace(@\"(?ms)^internal struct PROPSHEETHEADER(?!W_V2\\b).+?^\\}\\r\\n\", \"\", out R);\n\t\tR.RxReplace(@\"\\bPROPSHEETPAGEW_V4\\b\", \"PROPSHEETPAGE\", out R);\n\t\tR.RxReplace(@\"\\bPROPSHEETHEADERW_V2\\b\", \"PROPSHEETHEADER\", out R);\n\t\tR.RxReplace(@\"(?ms)^internal struct OPENFILENAME_NT4\\b.+?^\\}\\r\\n\", \"\", out R, 1);\n\t\t\n\t\t//replace some types\n\t\tR = R.RxReplace(@\"\\bLPARAM\\b\", \"nint\");\n\t\tR = R.RxReplace(@\"(?<!\\bstruct )\\bU?LARGE_INTEGER\\b\", \"long\");\n\t\t\n\t\t//in VARIANT, PROPVARIANT, _wireVARIANT members replace non-blittable types with IntPtr. Also replace some other types.\n\t\tR = R.RxReplace(@\"(?ms)^internal struct (?:PROP|_wire)?VARIANT \\{\\R\\K.+?\\R\\}\", __VariantMembers);\n\t\t\n\t\treturn R;\n\t\t\n\t\t//what: 0 struct, 1 interface, 2 delegate, flag 0x10000 _W\n\t\tstatic void __RemoveAW(ref string R, string name, int what) {\n\t\t\tstring sW = \"W\", sA = \"A\";\n\t\t\tif ((what & 0x10000) != 0) { sW = \"_W\"; sA = \"_A\"; }\n\t\t\twhat &= 0xff;\n\t\t\t\n\t\t\tif (0 != R.RxReplace($@\"\\b{name}{sW}\\b\", name, out R)) {\n\t\t\t\t//print.it($\"<><c 0xff0000>{name}</c>\");\n\t\t\t\t\n\t\t\t\t//remove STRUCTA\n\t\t\t\t\n\t\t\t\tg1:\n\t\t\t\tstring rx = null;\n\t\t\t\tswitch (what) {\n\t\t\t\tcase 0: rx = $@\"(?ms)^internal struct {name}{sA} .+?^\\}}\\r\\n\\r\\n\"; break;\n\t\t\t\tcase 1: rx = $@\"(?ms)^internal interface {name}{sA} .+?^\\}}\\r\\n\\r\\n\"; break;\n\t\t\t\tcase 2: rx = $@\"(?m)^internal delegate \\w+\\** {name}{sA}\\(.+\\r\\n\\r\\n\"; break;\n\t\t\t\t}\n\t\t\t\tif (R.RxMatch(rx, out var m)) {\n\t\t\t\t\t//find attributes (regex with attributes would be very slow)\n\t\t\t\t\tint i = m.Start - 3;\n\t\t\t\t\twhile (0 == string.Compare(R, i, \"]\\r\\n\", 0, 3)) {\n\t\t\t\t\t\t//print.it(\"attr\");\n\t\t\t\t\t\ti = R.LastIndexOf('\\n', i) - 2;\n\t\t\t\t\t}\n\t\t\t\t\ti += 3;\n\t\t\t\t\t\n\t\t\t\t\tR = R.Remove(i, m.Start - i + m.Length);\n\t\t\t\t\t//print.it(what, name, R.RegexIs_($\"\\b{name}\\b\")); //all False\n\t\t\t\t} else { //2 in SDK have TYPE/TYPEW instead of TYPEA/TYPEW\n\t\t\t\t\t//print.it(what, name);\n\t\t\t\t\tif (sA != null) {\n\t\t\t\t\t\tsA = null;\n\t\t\t\t\t\tgoto g1;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t//print.it($\"<><c 0xff>{name}</c>\"); //0\n\t\t\t\t//print.it(R.Find($\"internal struct {name}A \"));\n\t\t\t}\n\t\t}\n\t\t\n\t\tstatic string __VariantMembers(RXMatch m) {\n\t\t\tvar s = m.Value;\n\t\t\t//print.it(s);\n\t\t\ts = s.RxReplace(@\"\\[MarshalAs.+?\\] \", \"\");\n\t\t\ts = s.RxReplace(@\"\\b(string|object|I[A-Z]\\w+)\\b\", \"IntPtr\");\n\t\t\ts = s.RxReplace(@\"\\bDateTime\\b\", \"double\");\n\t\t\ts = s.RxReplace(@\"\\b(CY|FILETIME)\\b\", \"long\");\n\t\t\ts = s.RxReplace(@\"\\bVERSIONEDSTREAM\\*\", \"IntPtr\");\n\t\t\t//print.it(s);\n\t\t\treturn s;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Scripts/old/Windows SDK to C#/@SDK converter/Util.cs",
    "content": "﻿namespace SdkConverter;\n\nunsafe partial class Converter {\n\t#region SYMBOL\n\t\n\t/// <summary>\n\t/// If x is _Typedef, replaces it with its deepest used type.\n\t/// Error if x is not a type (is _Keyword).\n\t/// Returns pointer level.\n\t/// </summary>\n\t/// <param name=\"iTok\">Used only for error place.</param>\n\tint _Unalias(int iTok, ref _Symbol x) {\n\t\tint ptr = 0;\n\t\tvar t = x as _Typedef;\n\t\tif (t != null) {\n\t\t\tx = t.aliasOf;\n\t\t\tptr = t.ptr;\n\t\t} else if (x is _Keyword) _Err(iTok, \"unexpected\");\n\t\treturn ptr;\n\t}\n\t\n\t/// <summary>\n\t/// This overload has isConst parameter. If x is const _Typedef sets the isConst variable = true, else isConst unchanged.\n\t/// </summary>\n\tint _Unalias(int iTok, ref _Symbol x, ref bool isConst) {\n\t\tint ptr = 0;\n\t\tvar t = x as _Typedef;\n\t\tif (t != null) {\n\t\t\tx = t.aliasOf;\n\t\t\tptr = t.ptr;\n\t\t\tif (t.isConst) isConst = true;\n\t\t} else if (x is _Keyword) _Err(iTok, \"unexpected\");\n\t\treturn ptr;\n\t}\n\t\n\t/// <summary>\n\t/// Adds x to _ns[_nsCurrent].sym.\n\t/// Error if already exists, unless it is a forward decl of same type or a typedef of same type.\n\t/// </summary>\n\t/// <param name=\"iTokName\">Token index of symbol name.</param>\n\t/// <param name=\"addToGlobal\">Add to _ns[0].sym.</param>\n\tvoid _AddSymbol(int iTokName, _Symbol x, bool addToGlobal = false) {\n\t\t__AddSymbol(_tok[iTokName], x, iTokName, addToGlobal);\n\t}\n\t\n\t/// <summary>\n\t/// Adds x to _ns[_nsCurrent].sym.\n\t/// Error if already exists, unless it is a forward decl of same type or a typedef of same type.\n\t/// </summary>\n\t/// <param name=\"iTokError\">Where to show error if need.</param>\n\t/// <param name=\"addToGlobal\">Add to _ns[0].sym.</param>\n\tvoid _AddSymbol(string name, _Symbol x, int iTokError, bool addToGlobal = false) {\n\t\t//print.it(name);\n\t\t__AddSymbol(_TokenFromString(name), x, iTokError, addToGlobal);\n\t}\n\t\n\t/// <summary>\n\t/// Adds x to _ns[_nsCurrent].sym.\n\t/// Error if already exists, unless it is a forward decl of same type or a typedef of same type.\n\t/// </summary>\n\t/// <param name=\"iTokError\">Where to show error if need.</param>\n\t/// <param name=\"addToGlobal\">Add to _ns[0].sym.</param>\n\tvoid __AddSymbol(_Token name, _Symbol x, int iTokError, bool addToGlobal) {\n\t\tDebug.Assert(_IsCharIdentStart(*name.s));\n\t\tif (_keywords.ContainsKey(name)) _Err(iTokError, \"name already exists (keyword)\");\n\t\tint ns = addToGlobal ? 0 : _nsCurrent;\n\t\t//print.it(name);\n\t\ttry {\n\t\t\t_ns[ns].sym.Add(name, x);\n\t\t}\n\t\tcatch (ArgumentException) {\n\t\t\t_Symbol p = _ns[ns].sym[name];\n\t\t\tg1:\n\t\t\tif (x.GetType() != p.GetType()) {\n\t\t\t\tif (((p is _Typedef) && 0 == _Unalias(iTokError, ref p)) || ((x is _Typedef) && 0 == _Unalias(iTokError, ref x))) goto g1;\n\t\t\t\t_Err(iTokError, \"name already exists\");\n\t\t\t}\n\t\t\tif (!x.forwardDecl) {\n\t\t\t\tif (!(x is _Typedef) && !(x is _Callback)) _Err(iTokError, \"already defined\");\n\t\t\t\t//info: C++ allows multiple identical typedef. We don't check identity, it is quite difficult, assume the header file is without such errors.\n\t\t\t\t//info: before adding struct or enum, the caller checks whether it is forward-declaraed. If so, overwrites the forward-declared object and does not call this function, because cannot change object associated with that name because forward-declared object pointer can be used in a typedef.\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Finds symbol of token iTok in _keywords, _ns[_nsCurrent].sym and optionally in _ns[nsCurrent-1 ... 0].sym.\n\t/// Error if not found or the token is not an identifier.\n\t/// </summary>\n\t_Symbol _FindSymbol(int iTok, bool includingAncestorNamespaces) {\n\t\t_Symbol x;\n\t\tif (!_TryFindSymbol(iTok, out x, includingAncestorNamespaces)) _Err(iTok, \"unknown identifier\");\n\t\treturn x;\n\t}\n\t\n\t/// <summary>\n\t/// Finds symbol of token iTok in _keywords, _ns[_nsCurrent].sym and optionally in _ns[nsCurrent-1 ... 0].sym.\n\t/// Error if the token is not an identifier.\n\t/// </summary>\n\tbool _TryFindSymbol(int iTok, out _Symbol x, bool includingAncestorNamespaces) {\n\t\t_Token token = _tok[iTok];\n\t\tif (_keywords.TryGetValue(token, out x)) return true;\n\t\tfor (int i = _nsCurrent; i >= 0; i--) {\n\t\t\tif (_ns[i].sym.TryGetValue(token, out x)) return true;\n\t\t\tif (!includingAncestorNamespaces) break;\n\t\t}\n\t\tif (!_TokIsIdent(iTok)) _Err(iTok, \"unexpected\");\n\t\treturn false;\n\t}\n\t\n#if DEBUG\n\t/// <summary>\n\t/// Finds symbol by string name.\n\t/// </summary>\n\tbool _TryFindSymbol(string name, out _Symbol x, bool includingAncestorNamespaces) {\n\t\tfixed (char* n = name) {\n\t\t\t_Token token = new _Token(n, name.Length);\n\t\t\tif (_keywords.TryGetValue(token, out x)) return true;\n\t\t\tfor (int i = _nsCurrent; i >= 0; i--) {\n\t\t\t\tif (_ns[i].sym.TryGetValue(token, out x)) return true;\n\t\t\t\tif (!includingAncestorNamespaces) break;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n#endif\n\t\n\t/// <summary>\n\t/// Finds symbol of T type of token iTok in _keywords, _ns[_nsCurrent].sym and optionally in _ns[nsCurrent-1 ... 0].sym.\n\t/// Error if the token is not an identifier.\n\t/// </summary>\n\tbool _TryFindSymbolAs<T>(int iTok, out T x, bool includingAncestorNamespaces) where T : _Symbol {\n\t\tx = null;\n\t\t_Symbol t;\n\t\tif (!_TryFindSymbol(iTok, out t, includingAncestorNamespaces)) return false;\n\t\tx = t as T;\n\t\treturn x != null;\n\t}\n\t\n\t/// <summary>\n\t/// Finds symbol of token iTok everywhere.\n\t/// Error if not found or not a keyword or the token is not an identifier.\n\t/// </summary>\n\t/// <param name=\"kwType\">If not _KeywordT.Any, error if the keyword is not of this type.</param>\n\t_Keyword _FindKeyword(int iTok, _KeywordT kwType = _KeywordT.Any) {\n\t\t_Symbol x = _FindSymbol(iTok, true);\n\t\tvar k = x as _Keyword;\n\t\tif (k == null) _Err(iTok, \"unexpected\");\n\t\tif (kwType != _KeywordT.Any && k.kwType != kwType) _Err(iTok, \"unexpected\");\n\t\treturn k;\n\t}\n\t\n\t/// <summary>\n\t/// Finds symbol of token iTok in _keywords, _ns[_nsCurrent].sym and optionally in _ns[nsCurrent-1 ... 0].sym.\n\t/// Error if not found or not a type (struct, enum etc) or the token is not an identifier.\n\t/// </summary>\n\t_Symbol _FindType(int iTok, bool includingAncestorNamespaces) {\n\t\t_Symbol x = _FindSymbol(iTok, includingAncestorNamespaces);\n\t\tif (x is _Keyword) _Err(iTok, \"unexpected\");\n\t\treturn x;\n\t}\n\t\n\t/// <summary>\n\t/// Returns true if symbol of token iTok exists in _keywords, _ns[_nsCurrent].sym and optionally in _ns[nsCurrent-1 ... 0].sym.\n\t/// Error if the token is not an identifier.\n\t/// </summary>\n\tbool _SymbolExists(int iTok, bool includingAncestorNamespaces) {\n\t\t_Symbol t;\n\t\treturn _TryFindSymbol(iTok, out t, includingAncestorNamespaces);\n\t}\n\t\n\t#endregion\n\t\n\t#region TOKEN\n\t\n\t/// <summary>\n\t/// Returns _tok[i].s.\n\t/// </summary>\n\tchar* T(int i) { return _tok[i].s; }\n\t\n\t/// <summary>\n\t/// Returns true if token iTok string is equal to s.\n\t/// </summary>\n\tbool _TokIs(int iTok, string s) {\n\t\treturn _tok[iTok].Equals(s);\n\t}\n\t\n\t/// <summary>\n\t/// Returns 1-based index of matching string at token iTok.\n\t/// </summary>\n\tint _TokIs(int iTok, params string[] a) {\n\t\tfor (int i = 0; i < a.Length; i++) if (_tok[iTok].Equals(a[i])) return i + 1;\n\t\treturn 0;\n\t}\n\t\n\t/// <summary>\n\t/// Returns true if token iTok string starts with s.\n\t/// </summary>\n\tbool _TokStarts(int iTok, string s) {\n\t\treturn _tok[iTok].StartsWith(s);\n\t}\n\t\n\t/// <summary>\n\t/// Returns true if token iTok is an identifier.\n\t/// </summary>\n\tbool _TokIsIdent(int iTok) {\n\t\treturn _IsCharIdentStart(*T(iTok));\n\t}\n\t\n\t/// <summary>\n\t/// Returns true if token iTok is a number.\n\t/// </summary>\n\tbool _TokIsNumber(int iTok) {\n\t\treturn _IsCharDigit(*T(iTok));\n\t}\n\t\n\t/// <summary>\n\t/// Returns true if token iTok is an identifier or number.\n\t/// </summary>\n\tbool _TokIsIdentOrNumber(int iTok) {\n\t\treturn _IsCharIdent(*T(iTok));\n\t}\n\t\n\t/// <summary>\n\t/// Returns true if token iTok is character c.\n\t/// </summary>\n\tbool _TokIsChar(int iTok, char c) {\n\t\treturn *T(iTok) == c;\n\t}\n\t\n\t/// <summary>\n\t/// Returns true if token iTok is character c1 or c2.\n\t/// </summary>\n\tbool _TokIsChar(int iTok, char c1, char c2) {\n\t\tchar c = *T(iTok);\n\t\treturn c == c1 || c == c2;\n\t}\n\t\n\t/// <summary>\n\t/// Returns true if token iTok is a character in chars.\n\t/// </summary>\n\tbool _TokIsChar(int iTok, string chars) {\n\t\tchar c = *T(iTok);\n\t\tfor (int i = 0; i < chars.Length; i++) if (c == chars[i]) return true;\n\t\treturn false;\n\t}\n\t\n\t/// <summary>\n\t/// Converts token iTok from char* to string.\n\t/// </summary>\n\tstring _TokToString(int iTok) {\n\t\treturn _tok[iTok].ToString();\n\t}\n\t\n\t/// <summary>\n\t/// Converts raw tokens iTokFrom to (not including) iTokTo from char* to string.\n\t/// </summary>\n\tstring _TokToString(int iTokFrom, int iTokTo) {\n\t\tiTokTo--;\n\t\tchar* s = T(iTokFrom), se = T(iTokTo) + _tok[iTokTo].len;\n\t\treturn new string(s, 0, (int)(se - s));\n\t}\n\t\n\t/// <summary>\n\t/// Returns true, if at token iTokAfterTypeNameAndPtr is '([callConv]*[funcTypeOrVariable])('.\n\t/// </summary>\n\tbool _DetectIsFuncType(int iTokAfterTypeNameAndPtr) {\n\t\tint i = iTokAfterTypeNameAndPtr;\n\t\tif (!_TokIsChar(i, '(')) return false;\n\t\tif (_TokIsIdent(++i)) i++; //eg __stdcall\n\t\tif (!_TokIsChar(i, '*')) return false;\n\t\tif (_TokIsIdent(++i)) i++; //func type name or optional parameter/member name\n\t\tif (!_TokIsChar(i, ')')) return false;\n\t\treturn _TokIsChar(i + 1, '(');\n\t}\n\t\n\t/// <summary>\n\t/// Skips code block enclosed in {}, (), [] or ˂˃.\n\t/// i must be token index at the starting { etc. Returns token index at } etc.\n\t/// Inside the block:\n\t///\t\tSkips nested enclosed blocks of the same type.\n\t///\t\tIgnores nested enclosed blocks of other types, eg (), [] etc blocks when this block is {}.\n\t/// Error if i character is not {([˂.\n\t/// Error if the ending })]˃ is missing.\n\t/// </summary>\n\tint _SkipEnclosed(int i) {\n\t\tchar cStart = *T(i);\n\t\tif (!(cStart == '{' || cStart == '(' || cStart == '[' || cStart == '<')) _Err(i, \"unexpected\");\n\t\t\n\t\tchar cEnd = cStart; cEnd++; if (cEnd != ')') cEnd++; //')' is '(' + 1, others + 2\n\t\tint level = 1;\n\t\t\n\t\tfor (++i; ; i++) {\n\t\t\tchar c = *T(i);\n\t\t\tif (c == cStart) {\n\t\t\t\tlevel++;\n\t\t\t} else if (c == cEnd) {\n\t\t\t\tlevel--;\n\t\t\t\tif (level == 0) return i;\n\t\t\t} else if (c == 0) {\n\t\t\t\t_Err(i, $\"missing {cEnd}\");\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/// <summary>\n\t/// Skips code block enclosed in {}, (), [] or ˂˃.\n\t/// _i must be at the starting { etc. Finally it will be at } etc.\n\t/// Inside the block:\n\t///\t\tSkips nested enclosed blocks of the same type.\n\t///\t\tIgnores nested enclosed blocks of other types, eg (), [] etc blocks when this block is {}.\n\t/// Error if _i character is not {([˂.\n\t/// Error if the ending })]˃ is missing.\n\t/// </summary>\n\tvoid _SkipEnclosed() {\n\t\t_i = _SkipEnclosed(_i);\n\t}\n\t\n\t_Token _TokenFromString(string s) {\n\t\tchar* p = (char*)Marshal.StringToHGlobalUni(s); //never mind: finally release\n\t\treturn new _Token(p, s.Length);\n\t}\n\t\n\t#endregion\n\t\n\t#region STRING\n\t\n\t//Some of these currently are not used.\n\t\n\t/// <summary>\n\t/// Gets line length.\n\t/// Scans string until first '\\r', '\\n' or '\\0'.\n\t/// </summary>\n\tstatic int _LenLine(char* s) {\n\t\tchar* s0 = s;\n\t\twhile (!_IsCharRN0(*s)) s++;\n\t\treturn (int)(s - s0);\n\t}\n\t\n\t/// <summary>\n\t/// Gets identifier length.\n\t/// Returns 0 if s does not start with an identifier.\n\t/// </summary>\n\tstatic int _LenIdent(char* s) {\n\t\tif (!_IsCharIdentStart(*s)) return 0;\n\t\tchar* s0 = s++;\n\t\twhile (_IsCharIdent(*s)) s++;\n\t\treturn (int)(s - s0);\n\t}\n\t\n\t/// <summary>\n\t/// Returns true if s starts with word and is not followed by a C++ identifier character.\n\t/// </summary>\n\tstatic bool _IsWord(char* s, string word) {\n\t\tint i = 0;\n\t\tfor (; i < word.Length; i++) if (s[i] != word[i]) return false;\n\t\treturn !_IsCharIdent(s[i]);\n\t}\n\t\n\t/// <summary>\n\t/// Returns true if s starts with prefix.\n\t/// </summary>\n\tstatic bool _IsPrefix(char* s, string prefix) {\n\t\tfor (int i = 0; i < prefix.Length; i++) if (s[i] != prefix[i]) return false;\n\t\treturn true;\n\t}\n\t\n\t/// <summary>\n\t/// Finds ident as whole word.\n\t/// Returns index or -1.\n\t/// </summary>\n\tint _FindIdentifierInString(string s, string ident) {\n\t\tint len = ident.Length;\n\t\tfor (int i = 0; (i = s.Find(ident, i)) >= 0; i += len) {\n\t\t\tif (i > 0 && _IsCharIdent(s[i - 1])) continue;\n\t\t\tif (i < s.Length - len && _IsCharIdent(s[i + len])) continue;\n\t\t\treturn i;\n\t\t}\n\t\treturn -1;\n\t}\n\t\n\t#endregion\n\t\n\t#region CHARACTER\n\t/// <summary>\n\t/// Character type table for _IsCharIdent etc.\n\t/// </summary>\n\tstatic readonly byte[] _ctt = new byte[0x1000];\n\t\n\tstatic void _InitTables() {\n\t\t_ctt['_'] = 1; _ctt['$'] = 1;\n\t\tfor (int i = 'a'; i <= 'z'; i++) _ctt[i] = 1;\n\t\tfor (int i = 'A'; i <= 'Z'; i++) _ctt[i] = 1;\n\t\tfor (int i = '0'; i <= '9'; i++) _ctt[i] = 2;\n\t\t_ctt[' '] = _ctt['\\t'] = _ctt['\\v'] = _ctt['\\f'] = 4;\n\t\t_ctt['\\r'] = _ctt['\\n'] = 8;\n\t\t_ctt['!'] = _ctt['%'] = _ctt['&'] /*= _ctt['('] = _ctt[')']*/ = _ctt['*'] = _ctt['+'] /*= _ctt[',']*/ = _ctt['-'] = _ctt['.'] = _ctt['/']\n\t\t\t= _ctt[':'] = _ctt['<'] = _ctt['='] = _ctt['>'] = _ctt['?'] /*= _ctt['['] = _ctt[']']*/ = _ctt['^'] = _ctt['|'] = _ctt['~']\n\t\t\t/*= _ctt['{'] = _ctt['}']*/ = 16;\n\t\t//_ctt['('] = _ctt[')'] = _ctt['{'] = _ctt['}'] = _ctt['['] = _ctt[']'] = _ctt[';'] = _ctt['\\''] = _ctt[':'] = _ctt['\"'] = _ctt[''] = _ctt[''] = _ctt[''] = _ctt[''] = _ctt[''] = _ctt[''] = _ctt[''] = _ctt[''] = _ctt[''] = _ctt[''] = _ctt[''] = _ctt[''] = 32;\n\t\tfor (int i = 'a'; i <= 'f'; i++) _ctt[i] |= 64;\n\t\tfor (int i = 'A'; i <= 'F'; i++) _ctt[i] |= 64;\n\t\t_ctt[0] = 128;\n\t\t//others 0\n\t}\n\t\n\t/// <summary>\n\t/// Returns true if c is a C++ identifier character: alphanumeric, '_', '$'.\n\t/// </summary>\n\t[MethodImpl(MethodImplOptions.AggressiveInlining)]\n\tstatic bool _IsCharIdent(char c) {\n\t\treturn (_ctt[c] & 3) != 0;\n\t}\n\t\n\t/// <summary>\n\t/// Returns true if c is a C++ identifier start character: alpha, '_', '$'.\n\t/// </summary>\n\t[MethodImpl(MethodImplOptions.AggressiveInlining)]\n\tstatic bool _IsCharIdentStart(char c) {\n\t\treturn (_ctt[c] & 1) != 0;\n\t}\n\t\n\t/// <summary>\n\t/// Returns true if c is a number digit '0' to '9'.\n\t/// </summary>\n\t[MethodImpl(MethodImplOptions.AggressiveInlining)]\n\tstatic bool _IsCharDigit(char c) {\n\t\treturn (_ctt[c] & 2) != 0;\n\t}\n\t\n\t/// <summary>\n\t/// Returns true if c is a number digit '0' to '9'.\n\t/// </summary>\n\t[MethodImpl(MethodImplOptions.AggressiveInlining)]\n\tstatic bool _IsCharHexDigit(char c) {\n\t\treturn (_ctt[c] & (2 | 64)) != 0;\n\t}\n\t\n\t/// <summary>\n\t/// Returns true if c is a C++ space character, including new line characters: ' ', '\\t', '\\r', '\\n', '\\v', '\\f'.\n\t/// </summary>\n\t[MethodImpl(MethodImplOptions.AggressiveInlining)]\n\tstatic bool _IsCharSpaceOrRN(char c) {\n\t\treturn (_ctt[c] & 12) != 0;\n\t}\n\t\n\t/// <summary>\n\t/// Returns true if c is a C++ space character, except new line characters: ' ', '\\t', '\\v', '\\f'.\n\t/// </summary>\n\t[MethodImpl(MethodImplOptions.AggressiveInlining)]\n\tstatic bool _IsCharSpaceNoRN(char c) {\n\t\treturn _ctt[c] == 4;\n\t}\n\t\n\t/// <summary>\n\t/// Returns true if c is a new line character: '\\r', '\\n'.\n\t/// </summary>\n\t[MethodImpl(MethodImplOptions.AggressiveInlining)]\n\tstatic bool _IsCharRN(char c) {\n\t\treturn _ctt[c] == 8;\n\t}\n\t\n\t/// <summary>\n\t/// Returns true if c is a new line character or end of string: '\\r', '\\n', '\\0'.\n\t/// </summary>\n\t[MethodImpl(MethodImplOptions.AggressiveInlining)]\n\tstatic bool _IsCharRN0(char c) {\n\t\treturn (_ctt[c] & (8 | 128)) != 0;\n\t}\n\t\n\t/// <summary>\n\t/// Returns true if c is a C++ operator character, not including ,[](){}.\n\t/// </summary>\n\t[MethodImpl(MethodImplOptions.AggressiveInlining)]\n\tstatic bool _IsCharOperator(char c) {\n\t\treturn (_ctt[c] & 16) != 0;\n\t}\n\t\n\t#endregion\n}\n\n#region ERROR\n\nunsafe partial class Converter {\n\t/// <summary>\n\t/// Gets offset of token iTok in _src.\n\t/// </summary>\n\tint _Pos(int iTok) { return iTok < _tok.Count ? (int)(T(iTok) - _s0) : 0; }\n\t\n\t/// <summary>\n\t/// Throws exception indicating error at token iTok position.\n\t/// </summary>\n\tvoid _Err(int iTok, string errorText, [CallerMemberName] string m_ = null, [CallerFilePath] string f_ = null, [CallerLineNumber] int l_ = 0) {\n\t\t__ThrowConverterException(_Pos(iTok), errorText, m_, f_, l_);\n\t}\n\t\n\t/// <summary>\n\t/// Throws exception indicating error at s position (s must be in _src).\n\t/// </summary>\n\tvoid _Err(char* s, string errorText, [CallerMemberName] string m_ = null, [CallerFilePath] string f_ = null, [CallerLineNumber] int l_ = 0) {\n\t\t__ThrowConverterException((int)(s - _s0), errorText, m_, f_, l_);\n\t}\n\t\n\tvoid __ThrowConverterException(int pos, string errorText, string callerName, string callerFile, int callerLine) {\n\t\tthrow new ConverterException(\n\t\t\t$\"{errorText}\\r\\n\\tin <open {callerFile}|{callerLine}>{callerName}<>\"\n\t\t\t, pos);\n\t}\n\t\n\tvoid _OnCatchException(Exception e) {\n\t\t//print.it(e);\n\t\tint i = e is ConverterException ce ? ce.Offset : _Pos(_i);\n\t\tScriptEditor.Open(_cppFile, offset: i);\n\t}\n}\n\nclass ConverterException : Exception {\n\tpublic int Offset { get; private set; }\n\t\n\tpublic ConverterException(string s, int offset) : base(s) {\n\t\tOffset = offset;\n#if TEST_SMALL\n\t\t\t//Offset += 3; //VS saves some files with BOM, and QM2 loads with that BOM, but we get file data without BOM\n#endif\n\t}\n}\n\n#endregion\n\n#region debug\n\nunsafe partial class Converter {\n\tstring _DebugGetLine(int iTok) {\n\t\tif (iTok == 0) return null;\n\t\tchar* s = T(iTok), se = s;\n\t\twhile (s[-1] != '\\n') s--;\n\t\twhile (*se != '\\r' && *se != '\\n') se++;\n\t\treturn new string(s, 0, (int)(se - s));\n\t}\n}\n\n#endregion\n"
  },
  {
    "path": "Scripts/old/Windows SDK to C#/Readme.txt",
    "content": "Note: now the created db file is not used by LA directly. It is used by the new WinApiConverter to get declarations that are missing/incorrect in winmd.\n\n1. Once per SDK version: run scripts \"SDK get dll names\" and \"SDK get GUID\". They create some files used by the converter. Don't need to run again after adding/removing header files.\n2. Run script \"SDK preprocessor\". It creates two files for the converter project: Api-preprocessed-64.cpp, Api-preprocessed-32.cpp.\n3. Run the converter project. It creates two files: Api-64.cs, Api-32.cs.\n4. Run script \"SDK append 32-bit diff\". It creates final .cs file Api.cs.\n5. Run script \"SDK create database\".\n\nExternal tools:\n1. dumpbin.exe from Visual Studio. Used by scripts \"SDK get dll names\" and \"SDK get GUID\".\n2. clang.exe from LLVM (setup file LLVM-3.9.1-win64.exe). Used by script \"SDK preprocessor\".\n"
  },
  {
    "path": "Scripts/old/Windows SDK to C#/Scripts/SDK append 32-bit diff.cs",
    "content": "/// Compares converter output files (64-bit and 32-bit) and saves in final .cs file with added differences.\n\nprint.clear();\n\nstring s64 = File.ReadAllText(@\"C:\\code\\au\\Other\\Api\\Api-64.cs\");\nstring s32 = File.ReadAllText(@\"C:\\code\\au\\Other\\Api\\Api-32.cs\");\n\nvar d = new Dictionary<string, string>();\nvar d32 = new HashSet<string>();\nvar b = new StringBuilder();\n\n_ProcessCsFile(false, s64);\n_ProcessCsFile(true, s32);\n\nvar diff = b.ToString();\ndiff = diff.RxReplace(@\"\\b(\" + string.Join('|', d32) + @\")\\b\", \"$0__32\");\n\nstring commentsEtc = \"\"\"\n // 32-BIT\n // Declarations that are different (or exist only) when the process is 32-bit.\n\n\"\"\";\ns64 = s64[..s64.LastIndexOf('}')] + commentsEtc + diff + \"\\r\\n}\\r\\n\";\n\nfilesystem.saveText(@\"C:\\code\\au\\Other\\Api\\Api.cs\", s64);\nprint.it($\"DONE, {d.Count} declarations, size = {s64.Length / 1024} KB, API32 size = {diff.Length / 1024} KB\");\n\n\nvoid _ProcessCsFile(bool is32bit, string s) {\n\tint iKind = 0;\n\tstring sType = _GetOfKind(@\"(?ms)^// TYPE\\R(.+?)^\\R//\");\n\tstring sFunc = _GetOfKind(@\"(?ms)^// FUNCTION\\R(.+?)^\\R//\");\n\tstring sInterf = _GetOfKind(@\"(?ms)^// INTERFACE\\R(.+?)^\\R//\");\n\tstring sVar = _GetOfKind(@\"(?ms)^// VARIABLE\\R(.+?)^\\R//\");\n\tstring sConst = _GetOfKind(@\"(?ms)^// CONSTANT\\R(.+?)^\\R//\");\n\tstring sOther = _GetOfKind(@\"(?ms)^// CANNOT CONVERT\\R(.+?)^\\R\\R\\}\");\n\t\n\tstring _GetOfKind(string rx) {\n\t\tif (!s.RxMatch(rx, 1, out RXGroup g, 0, iKind..)) throw new AuException();\n\t\tiKind = g.End;\n\t\treturn g.Value;\n\t}\n\t\n\tregexp rxType = new(@\"(?ms)^\\r\\n(?:\\[[^\\r\\n]+\\r\\n)*internal (?:struct|enum|interface|class) (\\w+)[^\\r\\n\\{]+\\{(?:\\}$|.+?^\\})\"),\n\t\trxFunc = new(@\"(?m)^\\r\\n(?:\\[[^\\r\\n]+\\r\\n)*internal (?:static extern|delegate) \\w+\\** (\\w+)\\(.+;$\"),\n\t\trxVar = new(@\"(?m)^internal static \\w+ (\\w+) =.+;$\"),\n\t\trxConst = new(@\"(?m)^internal (?:const|readonly) \\w+ (\\w+) =.+;$\");\n\t\n\t//TYPE\n\tif (!rxType.FindAll(sType, out var a)) throw new AuException(); //struct/enum\n\t_AddToDict(is32bit, \"struct/enum\");\n\tif (!rxFunc.FindAll(sType, out a)) throw new AuException(); //delegate\n\t_AddToDict(is32bit, \"delegate\");\n\t//FUNCTION\n\tif (!rxFunc.FindAll(sFunc, out a)) throw new AuException();\n\t_AddToDict(is32bit, \"function\");\n\t//INTERFACE\n\tif (!rxType.FindAll(sInterf, out a)) throw new AuException();\n\t_AddToDict(is32bit, \"interface\");\n\t//VARIABLE\n\tif (!rxVar.FindAll(sVar, out a)) throw new AuException();\n\t_AddToDict(is32bit, \"variable\");\n\tif (b.Length > 0) b.AppendLine(\"\\r\\n\");\n\t//CONST\n\tif (!rxConst.FindAll(sConst, out a)) throw new AuException();\n\t_AddToDict(is32bit, \"const\");\n\tif (b.Length > 0) b.AppendLine();\n\t//CANNOT CONVERT\n\tif (!rxConst.FindAll(sOther, out a)) throw new AuException();\n\t_AddToDict(is32bit, \"other\");\n\t\n\t\n\tvoid _AddToDict(bool is32bit, string debugType) {\n\t\tforeach (var m in a) {\n\t\t\tstring name = m[1].Value, value = m.Value;\n\t\t\tif (!is32bit) {\n\t\t\t\t//print.it(debugType, name);\n\t\t\t\tif (!d.TryAdd(name, value)) print.it($\"<><c red>duplicate: {name} ({debugType}, 64-bit)<>\"); //0 is SDK\n\t\t\t} else {\n\t\t\t\tif (d.TryGetValue(name, out var v) && v == value) continue;\n\t\t\t\t\n\t\t\t\tprint.it($\"<>--------------------------------\\r\\n64-bit:  {v}\\r\\n<c blue>32-bit:  {value}<>\");\n\t\t\t\t\n\t\t\t\tb.AppendLine(value);\n\t\t\t\tif (!d32.Add(name)) print.it($\"<><c red>duplicate: {name} ({debugType}, 32-bit)<>\"); //0 is SDK\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Scripts/old/Windows SDK to C#/Scripts/SDK create database.cs",
    "content": "/*/ testInternal Au,Au.Editor; r Au.Editor.dll; /*/\n\n//string dbFile = folders.ThisAppBS + @\"winapi.db\";\nstring dbFile = @\"C:\\code\\au\\Other\\Api\\winapi.db\";\nfilesystem.delete(dbFile);\n\nstring s = File.ReadAllText(@\"C:\\code\\au\\Other\\Api\\Api.cs\");\n\nusing var d = new sqlite(dbFile);\nusing var trans = d.Transaction();\nd.Execute(\"CREATE TABLE api (name TEXT, code TEXT, kind INTEGER)\"); //note: no PRIMARY KEY. Don't need index.\nusing var statInsert = d.Statement(\"INSERT INTO api VALUES (?, ?, ?)\");\n\nstring rxType = @\"(?ms)^(?:\\[\\N+\\R)*internal (struct|enum|interface|class) (\\w+)[^\\r\\n\\{]+\\{(?: *\\}$|.+?^\\})\";\nstring rxFunc = @\"(?m)^(?:\\[\\N+\\R)*internal (static extern|delegate) \\w+\\** (\\w+)\\(.+;$\";\nstring rxVarConst = @\"(?m)^internal (const|readonly|static) \\w+ (\\w+) =.+;$\";\n\nforeach (var m in s.RxFindAll(rxType)) _Add(m);\nforeach (var m in s.RxFindAll(rxFunc)) _Add(m);\nforeach (var m in s.RxFindAll(rxVarConst)) _Add(m);\n\nvoid _Add(RXMatch m) {\n\tstatInsert.Bind(1, m[2].Value);\n\tvar code = m.Value;\n\tDebug.Assert(!code.Contains(\"\\r\\n\\r\")); //if contains newlines, the regex is bad, spans multiple definitions\n\tstatInsert.Bind(2, code);\n\t\n\tCiItemKind kind = m[1].Value switch {\n\t\t\"struct\" => CiItemKind.Structure,\n\t\t\"enum\" => CiItemKind.Enum,\n\t\t\"interface\" => CiItemKind.Interface,\n\t\t\"class\" => CiItemKind.Class,\n\t\t\"static extern\" => CiItemKind.Method,\n\t\t\"delegate\" => CiItemKind.Delegate,\n\t\t\"const\" => CiItemKind.Constant,\n\t\t\"static\" or \"readonly\" => CiItemKind.Field,\n\t\t_ => CiItemKind.None\n\t};\n\tDebug_.PrintIf(kind == CiItemKind.None, m[1].Value);\n\tstatInsert.Bind(3, (int)kind);\n\t\n\tstatInsert.Step();\n\tstatInsert.Reset();\n}\n\ntrans.Commit();\nd.Execute(\"VACUUM\");\n\nprint.it(\"DONE\");\n"
  },
  {
    "path": "Scripts/old/Windows SDK to C#/Scripts/SDK headers.h",
    "content": "//we'll remove everything at the start of the intermediate file (added by clang) until this\n#define AU\n\n//#define TEST_PREPROCESSING\n#ifdef TEST_PREPROCESSING\n\n/*\n*/\n\n#endif\n\n\n//#define __cplusplus 201103L //clang defines this\n#define _MSC_VER 1900 //VS2015\n#define _MSC_FULL_VER 190023026\n#define _MSC_EXTENSIONS 1\n\n#define _UNICODE 1\n#define UNICODE 1\n#define _WCHAR_T_DEFINED 1\n#define _MT 1\n#define _DLL 1\n#define _WIN32 1 //defined for 32 and 64\n\n#ifdef USE64BIT\n#define _WIN64 1\n#define _M_AMD64 100\n#define _M_X64 100\n#else\n#define _M_IX86 600\n#endif\n#define _INTEGRAL_MAX_BITS 64 //need if 32\n\n#define WINVER 0x0A00 //Win10\n#define _WIN32_WINNT 0x0A00\n#define _WIN32_IE 0x0B00 //IE11\n\n//add __int128 struct, because C# does not have a matching type\nstruct __int128 { float a, b, c, d; };\nstruct __int128d { float a, b, c, d; };\nstruct __int128i { float a, b, c, d; };\n\n//prevent adding XML interfaces, but need some for parameters\n#define __msxml_h__\n#define __msxml2_h__\n#define __msxml6_h__\ntypedef IntPtr IXMLElement;\ntypedef IntPtr IXMLDOMDocument;\n\n//prevent adding D3D9 interfaces\n#define _D3D9_H_\ntypedef IntPtr IDirect3DDevice9;\n\n//add some that are in headers that we remove\n#define size_t LPARAM\n#define intptr_t LPARAM\n#define uintptr_t LPARAM\n#define time_t LPARAM\ntypedef void* va_list;\ntypedef struct _iobuf { char *_ptr; int _cnt; char *_base; int _flag; int _file; int _charbuf; int _bufsiz; char *_tmpfname; } FILE;\n\n#define WIN32_LEAN_AND_MEAN //don't include come rarely used headers in windows.h\n#define USE_COM_CONTEXT_DEF //IContext etc\n\n#include <windows.h>\n\n#include <regstr.h> //registry strings\n#include <ole2.h> //OLE. Includes objbase.h, oleauto.h, urlmon.h, ...\n#include <commctrl.h> //common controls\n#include <shlobj.h> //shell\n#include <shellapi.h> //shell\n#include <shlwapi.h> //misc string functions etc\n#include <olectl.h> //some OLE API\n#include <oleacc.h> //MSAA accessible objects\n#include <UIAutomation.h> //UI Automation\n#include <mmsystem.h> //multimedia etc\n#include <tlhelp32.h> //process info etc\n#include <psapi.h> //process info etc\n#include <wtsapi32.h> //user sessions etc\n#include <winperf.h> //something used with PDH\n#include <pdh.h> //performance counters //future: test System.Diagnostics.PerformanceCounter\n#include <uxtheme.h> //visual styles\n#include <vsstyle.h> //visual styles\n#include <dwmapi.h> //DWM\n#include <htmlhelp.h> //HTML help\n#include <winioctl.h> //file IO low-level\n#include <aclapi.h> //security //future: test System.Security.AccessControl etc\n#include <sddl.h> //security strings\n#include <taskschd.h> //Task Scheduler\n#include <ShellScalingAPI.h> //high DPI\n#include <appmodel.h> //Windows Store apps\n#include <winternl.h> //Windows semi-internal API, eg NtX, RtlX\n#include <iepmapi.h> //IE protected mode\n#include <userenv.h> //user profiles, CreateEnvironmentBlock\n#include <netlistmgr.h> //network list\n#include <winnetwk.h> //networking\n#include <propkey.h> //property key GUIDs\n#include <cderr.h> //commdlg errors\n#include <commdlg.h> //common dialogs\n#include <dde.h> //DDE\n#include <ddeml.h> //DDE\n#define _WINSOCK_DEPRECATED_NO_WARNINGS\n#include <winsock2.h> //sockets (TCP etc) //future: test System.Net.Sockets\n#include <nspapi.h> //MS extensions for sockets\n#include <richedit.h> //rich edit control\n#include <usp10.h> //text drawing low-level\n#include <dbt.h> //const/struct for WM_DEVICECHANGE\n#include <ktmw32.h> //transactions\n#define _MSI_NO_CRYPTO //don't include cryptography\n#include <msi.h> //installer. MsiGetShortcutTarget, MsiGetComponentPath.\n#include <commoncontrols.h> //IImageList\n#include <powrprof.h> //power profiles, SetSuspendState\n#define _LMAUDIT_ //don't include lmaudit.h, has name conflicts\n#include <lm.h> //LAN management, NetServerEnum\n#include <Wlanapi.h> //WiFi\n#include <adhoc.h> //WiFi\n#include <Pla.h> //performance logs and alerts\n#include <werapi.h> //error reporting\n#include <physicalmonitorenumerationapi.h>\n#include <propvarutil.h>\n\n//core audio API\n#include <Audioclient.h>\n#include <Audiopolicy.h>\n#include <Mmdeviceapi.h>\n#include <Devicetopology.h>\n#include <Endpointvolume.h>\n\n#if 0\n//temporary. Used in the library, but don't need to include in the public database.\n//#include <sapi.h> //note: will be several errors when converting. Edit the preprocessed files in several places.\n////#include <sphelper.h> //C++ code, impossible to convert\n\n//these are little tested and probably not useful\n#include <iphlpapi.h> //IP helper, eg list network adapters. Use System.Net.NetworkInformation namespace.\n#include <icmpapi.h> //ICMP, eg to implement a Ping(). Use System.Net.NetworkInformation namespace.\n#include <GL/gl.h> //openGL\n#include <GL/glu.h> //openGL util\n\n//these are never tested but seem interesting\n#include <RestartManager.h> //restart manager\n#include <Clfsw32.h> //common log file system. .NET has something similar in System.Diagnostics.\n#include <Clfsmgmtw32.h> //common log file management. .NET has something similar in System.Diagnostics.\n#include <WinEvt.h> //event log. .NET has something similar in System.Diagnostics.\n#include <Winsxs.h> //several interfaces for side-by-side assembly management\n\n//these probably should not be added\n#include <shldisp.h> //shell IDispatch interfaces\n#include <scrnsave.h> //screen saver\n#include <dbghelp.h> //debug help library\n#include <imagehlp.h> //many the same as dbghelp.h. Includes wintrust.h -> wincrypt.h which is not welcome.\n#include <setupapi.h> //obsolete\n#include <mapi.h> //only for MS Outlook. Can instead use Outlook COM.\n#include <snmp.h> //can instead download a SNMP .NET library\n#include Sspi.h //authentication. Includes Security.h. Use .NET classes.\n//these would be included if no WIN32_LEAN_AND_MEAN\n#include <winefs.h> //encrypted file system. .NET can encrypt/decrypt a file, but maybe this also can be useful.\n#include <winspool.h> //printer API. Big. Use System.Drawing.Printing namespace. See also System.Printing namespace.\n#include <wincrypt.h> //cryptography. Large. Use System.Security.Cryptography namespace.\n#include <Esent.h> //Windows native database API. Use ESENT.NET instead; tested, slooow, better use SQLite.\n#include <wininet.h> //internet. We can instead use System.Net.Http namespace and System.Net.FtpWebRequest classes, but maybe this still can be useful.\n#include <uiribbon.h>\n//#include <mscoree.h> //unmanaged .NET API. Somehow this file disappeared.\n#endif\n"
  },
  {
    "path": "Scripts/old/Windows SDK to C#/Scripts/SDK preprocessor.cs",
    "content": "/// Preprocesses Windows SDK headers.\n/// The output files then can be used by the converter project.\n/// Temporary and output files are in folder \"C:\\code\\au\\Other\\Api\". Links to some of them added in this folder.\n/// Uses Clang compiler from LLVM 3.9.1 64-bit.\n\nprint.clear();\n\nstring clangPath = @\"C:\\Program Files\\LLVM\\bin\\clang.exe\";\nstring SDK_Include = @\"C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.26100.0\\um\";\nstring Shared_Include = @\"C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.26100.0\\shared\";\n//string CRT_Include = @\"C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.26100.0\\ucrt\";\n//string VS_CRT_Include=@\"C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\include\" //vcruntime.h etc missing in SDK\n\n_Preprocess(true, @\"C:\\code\\au\\Other\\Api\\Api-preprocessed-64.cpp\");\n_Preprocess(false, @\"C:\\code\\au\\Other\\Api\\Api-preprocessed-32.cpp\");\n\n\n// Preprocesses C++ headers from file \"SDK headers.h\" and saves in file outFile.\nvoid _Preprocess(bool is64bit, string outFile) {\n\t//temp files\n\tstring cppFile = @\"C:\\code\\au\\Other\\Api\\Api-headers.cpp\";//saved macro \"SDK headers\" text\n\tstring intermFile = @\"C:\\code\\au\\Other\\Api\\Api-interm.cpp\";//first clang invocation output\n\t\n\t//save \"SDK headers.h\" text to cppFile, with some modifications, etc\n\t_CreateCppFile(cppFile, is64bit);\n\t\n\t//run clang to preprocess cppFile\n\tstring cl = $\"\"\"\n-E -dD -undef\n-fms-compatibility -fms-extensions\n-fdeclspec -fdollars-in-identifiers\n-nobuiltininc -nostdinc++\n-Wno-extra-tokens -Wno-comment -Wno-nonportable-include-path -Wno-expansion-to-defined\n-I \"{SDK_Include}\" -I \"{Shared_Include}\"\n-o \"{intermFile}\"\n\"{cppFile}\"\n\"\"\";\n\tcl = cl.Replace(\"\\r\\n\", \" \");\n\tif (0 != run.console(o => print.it(o), clangPath, cl)) throw new AuException();\n\t\n\t//In old version was used this. Now errors. Now no errors without the CRT. When compared output, everything is almost the same, with few non-important changes.\n\t//-I \"{SDK_Include}\" -I \"{CRT_Include}\" -idirafter \"{VS_CRT_Include}\"\n\t\n\t/* some clang command line options\n -emit-ast\n -fdiagnostics-parseable-fixits\n                          Print fix-its in machine parseable form\n -fdiagnostics-print-source-range-info\n                          Print source range spans in numeric form\n -fdiagnostics-show-note-include-stack\n                          Display include stacks for diagnostic notes\n  -femit-all-decls        Emit all declarations, even if unused\n  -ffreestanding          Assert that the compilation takes place in a freestanding environment\n  -fgnu-keywords          Allow GNU-extension keywords regardless of language standard\n  -fgnu89-inline          Use the gnu89 inline semantics\n  -fms-compatibility-version=<value>\n                          Dot-separated value representing the Microsoft compiler version number to report in _MSC_VER (0 = don't define it (default))\n  -fmsc-version=<value>   Microsoft compiler version number to report in _MSC_VER (0 = don't define it (default))\n  -fno-merge-all-constants\n                          Disallow merging of constants\n  -fno-show-column        Do not include column number on diagnostics\n  -fno-show-source-location\n                          Do not include source location information with diagnostics\n  -fno-signed-char        Char is unsigned\n  -fpack-struct=<value>   Specify the default maximum struct packing alignment\n  -H                      Show header includes and nesting depth\n  -idirafter <value>      Add directory to AFTER include search path\n  -nobuiltininc           Disable builtin #include directories\n  -nostdinc++             Disable standard #include directories for the C++ standard library\n  -print-search-dirs      Print the paths used for finding libraries and programs\n  -P                      Disable linemarker output in -E mode\n  -Qunused-arguments      Don't emit warning for unused driver arguments\n  -std=<value>            Language standard to compile for\n  -stdlib=<value>         C++ standard library to use\n  -undef                  undef all system defines\n  -W<warning>             Enable the specified warning\n  -w                      Suppress all warnings\n  -x <language>           Treat subsequent input files as having type <language>\n\t*/\n\t\n\tstring s = File.ReadAllText(intermFile);\n\t\n\t/* tested:\n clang removes space/tab before and after # in directives.\n clang joins lines ending with \\.\n clang uses \\n for newlines, except in raw strings.\n clang replaces '#pragma  pack' to '#pragma pack', but does not change spaces after it.\n\t*/\n\t\n\t//remove some #define etc that are added by clang or added here\n\tif (0 == s.RxReplace(@\"(?s)^.+?\\R#define AU *\\R\", \"\", out s, 1)) throw new AuException();\n\t\n\t//remove SAL, CRT etc (use '# n \"include file\"' added by clang when not using -P)\n\ts = s.RxReplace(\"\"\"(?mi)(?>^# \\d+ \".+[/\\\\](?:specstrings\\w*|\\w*sal|\\w+specs|winapifamily|winpackagefamily|stralign|ucrt\\\\[^\\.\"]+|VC\\\\\\\\include\\\\\\\\(?!excpt\\.h)[^\\.\"]+)\\.h\")(?s).+?(?=^# \\d)\"\"\", \"\");\n\t\n\t//remove '# n \"include file\"' etc that clang adds when not using -P\n\tif (0 == s.RxReplace(@\"(?m)^# \\d.+\\R\", \"\", out s)) throw new AuException();\n\t\n\t//escape raw strings\n\ts = s.RxReplace(@\"R\"\"(.*)\\(((?s).*?)\\)\\1\"\"\", m => {\n\t\tvar s = m[2].Value;\n\t\tprint.it($\"<>info: raw string. Please review because extracting raw strings is unreliable:\\r\\n<c orange>{s}<>\");\n\t\treturn s.Escape(quote: true);\n\t});\n\t\n\t//remove empty lines to make easier to debug-read\n\tif (0 == s.RxReplace(@\"(?m)^\\s*\\R\", \"\", out s)) throw new AuException();\n\t\n\t//make #pragma pack easier to parse\n\tif (0 == s.RxReplace(@\"(?m)^#pragma[ \\t]+pack[ \\t]*\\(\", \"`(\", out s)) throw new AuException();\n\t\n\t//remove #pragma that we don't need\n\tif (0 == s.RxReplace(@\"(?m)^#pragma.+\\R\", \"\", out s)) throw new AuException();\n\t\n\t//remove thousand separator ' from number literals\n\ts = s.RxReplace(@\"(?<=\\d)'(?=\\d)\", \"\");\n\t\n\t//replace double-word types, to make easier to convert\n\ts = s.RxReplace(@\"\\blong\\s+long\\b\", \"__int64\");\n\ts = s.RxReplace(@\"\\blong\\s+double\\b\", \"double\");\n\ts = s.RxReplace(@\"\\bsigned\\s+\\b(char|short|int|long|__int64|__int8|__int16|__int32)\\b\", \"$1\");\n\ts = s.RxReplace(@\"\\bunsigned\\s+\\b(char|short|int|long|__int64|__int8|__int16|__int32)\\b\", \"u$$$1\");\n\t\n\t//convert DECLARE_HANDLE(X) to IntPtr\n\ts = s.RxReplace(@\"\\bstruct\\s+(\\w+)__\\{int\\s+unused;\\};\\s*typedef\\s+struct \\1__\\s*\\*\\1;\", \"typedef IntPtr $1;\");\n\t//for HANDLE use IntPtr, not void*\n\tif (0 == s.RxReplace(@\"\\btypedef void ?\\* ?HANDLE;\", \"typedef IntPtr HANDLE;\", out s, 1)) throw new AuException();\n\ts = s.RxReplace(@\"\\btypedef void ?\\* ?(\\w+_HANDLE);\", \"typedef IntPtr $1;\");\n\ts = s.RxReplace(@\"\\btypedef void ?\\* ?(H\\w+);\", \"typedef IntPtr $1;\");\n\t//remove HWND typedef, we add it in converter\n\tif (0 == s.RxReplace(@\"\\btypedef IntPtr HWND;\", \" \", out s, 1)) throw new AuException();\n\t\n\t//replace inline functions LongToHandle/LongToPtr to IntPtr cast\n\ts = s.RxReplace(@\"(?ms)^__inline\\s+void\\s*\\*\\s*(U?LongTo(?:Handle|Ptr))\\s*\\([^{]+\\{[^}]+\\}\", \"#define $1(h) (IntPtr)(h)\");\n\t\n\t//expand HRESULT_FROM_WIN32\n\ts = s.RxReplace(@\"\\bHRESULT_FROM_WIN32\\((\\w+)\\)\", \"(0x80070000|$1)\");\n\t\n\t//remove unused SAL annotations (defined as '^\"text\"; most removed by our #undef/#define)\n\ts = s.RxReplace(@\"\\^\"\"\\w+\"\"\", m => {\n\t\tvar s = m.Value;\n\t\tif (0 == s.Like(false, \"^\\\"_In_*\", \"^\\\"_Inout_*\", \"^\\\"_Out_*\", \"^\\\"_Outptr_*\")) {\n\t\t\tif (s.Like(\"^\\\"__RPC_*\")) {\n\t\t\t\tvar ss = s[8..];\n\t\t\t\tif (0 != ss.Like(false, \"*_in\\\"\", \"*_in_*\")) return s.Insert(2, \"_In_\");\n\t\t\t\tif (0 != ss.Like(false, \"*_inout\\\"\", \"*_inout_*\")) return s.Insert(2, \"_Inout_\");\n\t\t\t\tif (0 != ss.Like(false, \"*_deref_out\\\"\", \"*_deref_out_*\")) return s.Insert(2, \"_Outptr_\");\n\t\t\t\tif (0 != ss.Like(false, \"*_out\\\"\", \"*_out_*\")) return s.Insert(2, \"_Out_\");\n\t\t\t\tif (0 != ss.Like(false, \"unique_pointer*\", \"string*\")) return null; //filter garbage to make easier to debug\n\t\t\t\tgoto ge;\n\t\t\t} else if (s.Like(\"^\\\"__*\")) {\n\t\t\t\tvar ss = s[4..];\n\t\t\t\tif (ss.Like(\"out_data_source*\")) return null;\n\t\t\t\tif (0 != ss.Like(false, \"in\\\"\", \"in_*\")) return s.Insert(2, \"_In_\");\n\t\t\t\tif (0 != ss.Like(false, \"inout\\\"\", \"inout_*\")) return s.Insert(2, \"_Inout_\");\n\t\t\t\tif (0 != ss.Like(false, \"out\\\"\", \"out_*\")) return s.Insert(2, \"_Out_\");\n\t\t\t\tif (0 != ss.Like(false, \"field_*\", \"drv_*\", \"kernel_entry*\", \"control_entrypoint*\")) return null; //filter garbage to make easier to debug\n\t\t\t\tgoto ge;\n\t\t\t} else if (s.Like(\"^\\\"_COM_Outptr_*\")) {\n\t\t\t\treturn s.Insert(2, \"_Outptr_\");\n\t\t\t} else if (s.Like(\"^\\\"_Deref_*_range_\\\"\")) {\n\t\t\t\t//used only after _Out_ etc, therefore useless and would be removed later anyway\n\t\t\t\treturn null;\n\t\t\t} else if (0 != s.Like(false, \"^\\\"_Null*\", \"^\\\"_Ret*\", \"^\\\"_Field_*\", \"^\\\"_Reserved_*\", \"^\\\"_Success_*\", \"^\\\"_Must_*\", \"^\\\"_Post_*\", \"^\\\"_Pre_*\", \"^\\\"_Printf_*\", \"^\\\"_Struct_*\", \"^\\\"_Check_return_*\", \"^\\\"_Function_class_*\", \"^\\\"_Frees_ptr_*\", \"^\\\"_Analysis_noreturn_*\", \"^\\\"_IRQL_requires_*\", \"^\\\"_Strict_type_match*\", \"^\\\"_Translates_Win32_to_HRESULT_*\")) {\n\t\t\t\t//filter garbage to make easier to debug\n\t\t\t\treturn null;\n\t\t\t} else goto ge;\n\t\t}\n\t\treturn s;\n\t\tge:\n\t\tprint.it(s);\n\t\treturn null;\n\t});\n\tif (0 == s.RxReplace(@\"(\\^\"\"\\w+\"\")(?:\\s*\\^\"\"\\w+\"\")+\", \"$1\", out s)) throw new AuException(); //multiple-to-one (usually the first is what we need)\n\t\n\t//manage #define/#undef:\n\t//\tExtract all #define/#undef, remove from s, and add to sa.\n\t//\tAlso append to sa '`d$$$NAME VALUE'. The second clang call will unexpand C macros in values.\n\tvar sa = _DefineUndef();\n\tstring _DefineUndef() {\n\t\tvar rx = new regexp(@\"(?m)^#(d|u)(?:efine|ndef)[ \\t]+([\\w\\$]+\\b)(.*\\R)\");\n\t\tif (!rx.FindAll(s, out var a)) throw new AuException();\n\t\ts = rx.Replace(s);\n\t\tvar b = new StringBuilder();\n\t\tforeach (var m in a) b.Append(m.Value);\n\t\tforeach (var m in a) b.AppendFormat(\"`{0}$$$_{1}{2}\", m[1].Value, m[2].Value, m[3].Value);\n\t\treturn b.ToString();\n\t}\n\t\n\t//remove __declspec and other keywords that we don't need\n\ts = s.RxReplace(@\"\\b(?:__unaligned|__w64|_w64|static|volatile|explicit|friend|mutable|__ptr32|__ptr64|constexpr|thread_local|__restrict|restrict|tile_static|__clrcall|__vectorcall)\\b\", \"\");\n\ts = s.RxReplace(@\"\\balignas\\s*\\(\\s*\\w+\\s*\\)\", \"\");\n\tint n1, n2;\n\tn1 = s.RxFindAll(@\"\\b__declspec\\s*\\(\\s*\"\"\").Count(); //print.it(n1); //295584 with SAL, 0 without\n\tif (n1 > 0 && n1 != s.RxReplace(@\"(?s)\\b__declspec\\s*\\(\\s*\"\".*?\"\"\\s*\\)\", \"\", out s)) throw new AuException();\n\tn1 = s.RxFindAll(@\"\\b__declspec\\s*\\(\\s*uuid\\b\").Count(); //print.it(n1); //713\n\tif (n1 > 0) { n2 = s.RxReplace(@\"\\b__declspec\\s*\\(\\s*uuid\\s*\\(\\s*(\"\"[^\"\"]+\"\")\\s*\\)\\s*\\)\", \"uuid($1)\", out s); if (n2 != n1) throw new AuException(); }\n\tn1 = s.RxFindAll(@\"\\b__declspec\").Count(); //print.it(n1); //5574\n\tif (n1 > 0) { n2 = s.RxReplace(@\"(?s)\\b__declspec\\s*\\(\\s*\\w+\\s*(?:\\(\\s*(?:\\w+|\"\"[^\"\"]*\"\")\\s*\\))?\\)\", \"\", out s); if (n2 != n1) throw new AuException(); }\n\t\n\t//make sure that uuid is not before struct, and remove from others\n\ts = s.RxReplace(@\"\\btypedef\\s+(uuid\\s*\\(.+?\\))\\s*struct\\b\", \"typedef struct $1 \");\n\ts = s.RxReplace(@\"\\btypedef\\s+uuid\\s*\\(.+?\\)\", \"typedef \");\n\t\n\t//replace 'typedef TYPE [callConv] FUNCTYPE(' to 'typedef TYPE ([callConv]*FUNCTYPE)('\n\ts = s.RxReplace(@\"(\\btypedef\\s+\\w+[*&\\s]+)(__?(?:stdcall|cdecl|fastcall|thiscall)\\s+)?(\\w+)\\s*\\(\", \"$1 ($2*$3)(\");\n\t\n\t//convert 'using X = Y' to 'typedef Y X', also for function typedefs. 0 in SDK.\n\t//s = s.RxReplace(@\"\\busing\\s+(\\w+)\\s*=([\\s\\w,*&]+?);\", \"typedef $2 $1;\");\n\t//s = s.RxReplace(@\"\\busing\\s+(\\w+)\\s*=\\s*([\\s\\w,*&]+?)\\(([\\s\\w]*\\*\\s*)\\)\\s*\\(\", \"typedef $2($3$1)(\");\n\t\n\t//add * to 'typedef T(__stdcall X)(...);' etc\n\tvar aFN = new List<string>();\n\ts = s.RxReplace(@\"\\btypedef\\s+\\w+[\\s*&]*\\((?:_?_stdcall|_?_cdecl)\\s+(\\w+)\\s*\\)\\s*\\(\", m => {\n\t\tint offs = m.Start;\n\t\tint i = m[1].Start - offs, j = m[1].End - offs;\n\t\tvar s = m.Value;\n\t\taFN.Add(s[i..j]);\n\t\treturn s.ReplaceAt(i - 1, 1, \"*\");\n\t});\n\tforeach (var v in aFN) s = s.RxReplace(@\"(\\btypedef\\s+\" + v + @\")\\s*\\*\", \"$1 \"); //remove * from 'typedef FUNC *LPFUNC;'\n\t\n\t//remove 'void' from 'Func(void)'\n\ts = s.RxReplace(@\"\\(\\s*void\\s*\\)(?=\\s*[;=])\", \"()\");\n\t\n\t//convert 'X const *' to 'const X *' etc\n\ts = s.RxReplace(@\"\\b(\\w+)\\s+const\\s*(?=\\*|\\w+\\s*\\[)\", \"const $1 \");\n\ts = s.RxReplace(@\"\\*\\s*const\\b\", \"**\");\n\t\n\t//remove 'public' from base (used for interface struct)\n\ts = s.RxReplace(@\":\\s*public\\s+(?=\\w)\", \":\");\n\t\n\t//somehow one '_VARIANT_BOOL bool;' not removed\n\ts = s.RxReplace(@\"\\b_VARIANT_BOOL bool;\", \" \");\n\t\n\t//remove other converter-unsuported noise\n\ts = s.RxReplace(@\"\\btypedef\\s+char\\s+__C_ASSERT__\\b[^;]*;\", \" \");\n\ts = s.RxReplace(@\"\\Rnoexcept\\R;\", \";\");\n\ts = s.RxReplace(@\"\\btypedef struct\\K const\\b\", \"\");\n\t\n\t//replace 'inline namespace' to 'namespace inline'\n\ts = s.RxReplace(@\"\\binline\\s+namespace\\b\", \"namespace inline\"); //0 in SDK\n\t\n\t//replace 2 __stdcall to 1\n\ts = s.RxReplace(@\"\\b__stdcall\\s+__stdcall\\b\", \"__stdcall\"); //4 in SDK\n\t\n\t//resolve identifier conflicts where the same name is defined in two header files\n\ts = s.RxReplace(@\"(?m)^typedef PDH_HLOG HLOG;\", \"\");\n\t\n\t//replace 'TYPE __forceinline' to '__forceinline TYPE'\n\ts = s.RxReplace(@\"(?m)LONGLONG\\s+__forceinline\\b\", \"__forceinline LONGLONG\");\n\t\n\t//replace some code that cannot be converted\n\ts = s.RxReplace(@\" rgbIndexId[sizeof(JET_API_PTR)+sizeof(u$long)+sizeof(u$long)]\", \" rgbIndexId[ 24 ]\"); //last member in struct that is only used as pointer parameter\n\t\n\t//remove classes, because converter does not convert or skip them\n\ts = s.RxReplace(@\"(?ms)^class PMSIHANDLE\\s*\\{.+?^\\};\", \"\");\n\t\n\t//convert property keys\n\tsa = sa.RxReplace(@\"(?m)^`d\\$\\$\\$_INIT_(PKEY_\\w+) +\\{ *\\{ *([^\\}]+) *\\} *, *(\\w+) *\\} *$\", @\"`cp \"\"internal static PROPERTYKEY $1 = new PROPERTYKEY() { fmtid = new Guid($2), pid = $3 };\"\"\");\n\ts = s.RxReplace(@\"(?m)^extern \"\"C\"\" const PROPERTYKEY PKEY_\\w+;$\", \"\");\n\t\n\t//-----------------------\n\t\n\t//run clang again on #define/#undef to expand macros in their values\n\t\n\tstring defFileIn = folders.ThisAppTemp + \"sdk def in.cpp\";\n\tstring defFileOut = folders.ThisAppTemp + \"sdk def out.cpp\";\n\tfilesystem.saveText(defFileIn, sa);\n\t\n\tcl = $\"\"\"\n-E -P -undef\n-fms-compatibility -fms-extensions\n-fdeclspec -fdollars-in-identifiers\n-nobuiltininc -nostdinc++\n-o \"{defFileOut}\"\n\"{defFileIn}\"\n\"\"\";\n\tcl = cl.Replace(\"\\r\\n\", \" \");\n\tif (0 != run.console(o => print.it(o), clangPath, cl)) throw new AuException();\n\t\n\tsa = File.ReadAllText(defFileOut);\n\ts += sa;\n\t\n\t//-----------------------\n\t\n\t//replace known sizeof(string)\n\ts = s.RxReplace(@\"\\b\\Qsizeof(\"\"://\"\")\\E\", \"4\");\n\ts = s.RxReplace(@\"\\b\\Qsizeof(L\"\"\\\\TransactionManager\\\\\"\")\\E\", \"42\");\n\ts = s.RxReplace(@\"\\b\\Qsizeof(L\"\"\\\\Transaction\\\\\"\")\\E\", \"28\");\n\ts = s.RxReplace(@\"\\b\" + Regex.Escape(@\"sizeof(L\"\"\\\\Enlistment\\\\\"\")\"), \"26\");\n\ts = s.RxReplace(@\"\\b\\Qsizeof(L\"\"\\\\ResourceManager\\\\\"\")\\E\", \"36\");\n\t\n\t//replace known ?: operators like '((1 != 0) ? 0x00010000 : 0)'\n\ts = s.RxReplace(@\"\\(\\(0 != 0\\) \\? \\w+ : (\\w+)\\)\", \"($1)\");\n\ts = s.RxReplace(@\"\\(\\(1 != 0\\) \\? (\\w+) : \\w+\\)\", \"($1)\");\n\t\n\t//remove rarely used things not handled by the SDK converter\n\ts = s.RxReplace(@\"        DWORD64 Reserved : 64 - 8;\\R\", \"\");\n\ts = s.RxReplace(@\"\\b\\Qtypedef PVOID (ENCLAVE_TARGET_FUNCTION)(PVOID);[10]typedef ENCLAVE_TARGET_FUNCTION (*PENCLAVE_TARGET_FUNCTION);[10]typedef PENCLAVE_TARGET_FUNCTION LPENCLAVE_TARGET_FUNCTION;\\E\\R\", \"\");\n\t\n\t//fails to convert\n\ts = s.Replace(\"typedef PVOID (ENCLAVE_TARGET_FUNCTION)(PVOID);\\ntypedef ENCLAVE_TARGET_FUNCTION (*PENCLAVE_TARGET_FUNCTION);\\ntypedef PENCLAVE_TARGET_FUNCTION LPENCLAVE_TARGET_FUNCTION;\\n\", \"\");\n\t\n\tfilesystem.saveText(outFile, s);\n\t\n\tprint.it($\"PREPROCESSED {(is64bit ? \"64\" : \"32\")}-bit\");\n}\n\nvoid _CreateCppFile(string cppFile, bool is64bit) {\n\tvar s = File.ReadAllText(folders.sourceCode() + \"SDK headers.h\");\n\t\n\tif (is64bit) s = \"#define USE64BIT\\r\\n\" + s;\n\t\n\tvar b = new StringBuilder();\n\tb.AppendLine(s).AppendLine();\n\t\n\t//finally undef C macros defined here\n\tif (!s.RxFindAll(@\"(?m)^[ \\t]*#[ \\t]*define\\s+(\\w+).*\\R\", 1, out var a)) throw new AuException();\n\tforeach (var v in a) b.Append(\"#undef \").AppendLine(v);\n\t\n\tfilesystem.saveText(cppFile, b.ToString());\n\t\n\t//SAL\n\t\n\tstring catsal = pathname.getDirectory(cppFile) + @\"\\Au-sal.h\";\n\ts = \"\"\"\n#define __SPECSTRINGS_STRICT_LEVEL 0 //prevent redefining _Outptr_ etc to something useless\n#undef _SA_annotes3\n#define _SA_annotes3(n,pp1,pp2,pp3) ^pp1\n//only _SA_annotes3 is used\n//#undef _SA_annotes1\n//#define _SA_annotes1(n,pp1) ^pp1\n//#undef _SA_annotes2\n//#define _SA_annotes2(n,pp1,pp2) ^pp1\n\n\"\"\";\n\tfilesystem.saveText(catsal, s);\n\t\n\tstring sal = Shared_Include + @\"\\sal.h\";\n\ts = File.ReadAllText(sal);\n\tif (!s.Contains(\"AU\")) {\n\t\tstring incl = $\"#ifdef AU\\r\\n#include \\\"{catsal}\\\"\\r\\n#endif\\r\\n\";\n\t\tif (0 == s.RxReplace(@\"(?m)^#define _SA_annotes3\\(n,pp1,pp2,pp3\\)\\R\", \"$0\" + incl, out s, 1)) throw new AuException();\n\t\tfilesystem.saveText(sal, s, backup: true);\n\t}\n\t\n\tstring appmodel = SDK_Include + @\"\\appmodel.h\";\n\tif (is64bit && !filesystem.exists(appmodel)) {\n\t\tfilesystem.copy(SDK_Include + @\"\\..\\winrt\\appmodel.h\", appmodel);\n\t\tfilesystem.copy(SDK_Include + @\"\\..\\winrt\\minappmodel.h\", SDK_Include + @\"\\minappmodel.h\");\n\t}\n}\n"
  },
  {
    "path": "Scripts/old/Windows SDK to C#/Scripts/SdkUtil.cs",
    "content": "static class SdkUtil {\n\t\n\tpublic static string GetVisualStudioToolsFolderPath(bool preview) {\n\t\t//string vswherePath = @\"C:\\Program Files (x86)\\Microsoft Visual Studio\\Installer\\vswhere.exe\";\n\t\t//if (0 != run.console(out string s, vswherePath, $\"-latest {(preview ? \"-prerelease\" : \"\")} -property installationPath\")) throw null;\n\t\t//s = s.Trim() + @\"\\VC\\Tools\\MSVC\\\";\n\t\t\n\t\tvar s = $@\"C:\\Program Files\\Microsoft Visual Studio\\2022\\{(preview ? \"Preview\" : \"Community\")}\\VC\\Tools\\MSVC\\\";\n\t\tstring ver = Directory.GetDirectories(s).OrderByDescending(d => d).First();\n\t\treturn ver + @\"\\bin\\Hostx64\\x64\";\n\t}\n}\n"
  },
  {
    "path": "Scripts/old/Windows SDK to C#/Scripts/once/SDK get GUID.cs",
    "content": "/// Gets GUID names/data from .lib files.\n/// Saves in \"C:\\code\\au\\Other\\Api\\GuidMap.txt\".\n\n/*/ c ..\\SdkUtil.cs; /*/\n\nprint.clear();\n\nstring libDir = @\"C:\\Program Files (x86)\\Windows Kits\\10\\Lib\\10.0.26100.0\\um\\x64\";\n(string, string)[] libs = {\n\t(libDir, \"*.lib\"),\n};\n\nstring r = _CreateGuidMap(0);\n//print.it(r);\nfilesystem.saveText(@\"C:\\code\\au\\Other\\Api\\GuidMap.txt\", r);\n\n\n//Extracts GUID names/data from .lib files.\n//Returns the list of extracted GUID names/data.\n//\n//flags: 1 include subfolders\nstring _CreateGuidMap(int flags) {\n\tstring dumpbin = SdkUtil.GetVisualStudioToolsFolderPath(true) + @\"\\dumpbin.exe\";\n\t\n\tvar files = new List<FEFile>();\n\tforeach (var (dir, fn) in libs) {\n\t\tfiles.AddRange(filesystem.enumFiles(dir, fn, 0 != (flags & 1) ? FEFlags.AllDescendants : 0).Where(f => 0 == f.Name.Starts(false, \"api-ms-\", \"ntstc_\")));\n\t}\n\t\n\tvar bag = new ConcurrentBag<string>();\n\tParallel.ForEach(files, f => {\n\t\tvar fn = f.Name;\n\t\tint e = run.console(out var s, dumpbin, $\"/HEADERS /RAWDATA \\\"{f.FullPath}\\\"\");\n\t\tif (e != 0) {\n\t\t\tif (e == -1073741515) {\n\t\t\t\trun.it(dumpbin); //show \"mspdb80.dll is missing\"\n\t\t\t\tthrow new AuException($\"dumpbin.exe failed, {e}\");\n\t\t\t}\n\t\t\tprint.warning($\"dumpbin.exe failed, {e}, {fn}\");\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tif (!s.Contains(\"COMDAT; sym=\")) return;\n\t\t//print.it($\"<><lc green>{fn}<>\");\n\t\t//print.it(s);\n\t\t\n\t\tvar rx = @\"(?m) +COMDAT; sym= *((?:[A-Z]|guid)\\w+)\\R(?: .+\\R)+\\RRAW DATA #\\w+\\R  \\w+: (\\w\\w) (\\w\\w) (\\w\\w) (\\w\\w) (\\w\\w) (\\w\\w) (\\w\\w) (\\w\\w) (\\w\\w) (\\w\\w) (\\w\\w) (\\w\\w) (\\w\\w) (\\w\\w) (\\w\\w) (\\w\\w)  .*\\R\\R\";\n\t\tforeach (var m in s.RxFindAll(rx)) {\n\t\t\tvar sym = m[1].Value;\n\t\t\tif (sym.Starts(\"IID___\")) continue;\n\t\t\t//if(sym.Length>50) print.it(sym);\n\t\t\tbag.Add($\"{sym} 0x{m[5]}{m[4]}{m[3]}{m[2]}, 0x{m[7]}{m[6]}, 0x{m[9]}{m[8]}, 0x{m[10]}, 0x{m[11]}, 0x{m[12]}, 0x{m[13]}, 0x{m[14]}, 0x{m[15]}, 0x{m[16]}, 0x{m[17]}\");\n\t\t}\n\t});\n\t\n\tprint.it($\"Found {bag.Count} GUIDs.\"); //28081\n\t\n\treturn string.Join(\"\\r\\n\", bag.OrderBy(o => o).Append(\"\")); //OrderBy because Parallel makes random\n}\n"
  },
  {
    "path": "Scripts/old/Windows SDK to C#/Scripts/once/SDK get dll names.cs",
    "content": "/// Gets dll function names and their dll file names from .lib and .dll files. Also gets ordinal or alias if need.\n/// Saves in \"C:\\code\\au\\Other\\Api\\DllMap.txt\". Format:\n/// FuncX dllX.dll\n/// FuncZ dllX.dll|#ordinal\n/// FuncY dllX.dll|FuncNameInDll\n\n/*/ c ..\\SdkUtil.cs; /*/\n\nprint.clear();\n\nstring libDir = @\"C:\\Program Files (x86)\\Windows Kits\\10\\Lib\\10.0.26100.0\\um\\x64\";\nstring sysDir = (string)folders.System + \"\\\\\";\nobject[] libs = {\n\t(libDir, \"*.lib\"),\n\tsysDir+\"hhctrl.ocx\",\n\tsysDir+\"ieframe.dll\",\n\tsysDir+\"pla.dll\",\n\tsysDir+\"msvcrt.dll\",\n\tsysDir+\"ntdll.dll\",\n\tsysDir+\"kernelbase.dll\",\n};\n\nstring r = _CreateDllMap(2 | 0);\nfilesystem.saveText(@\"C:\\code\\au\\Other\\Api\\DllMap.txt\", r);\n\n\n//Extracts dll function names and their dll file names from lib or/and dll file(s).\n//Returns the list of extracted functions-dlls.\n//Note: Shows warnings \"no exports\" etc. It's normal when processing all lib or dll in a folder.\n//\n//flags: 1 include subfolders, 2 include system dlls for libs, 4 use cached dumpbin results if exists\nstring _CreateDllMap(int flags) {\n\tstring dumpbin = SdkUtil.GetVisualStudioToolsFolderPath(true) + @\"\\dumpbin.exe\";\n\tstring sLib = null, sDll = null;\n\t\n\t//cache dumpbin results when developing post-dumpbin code\n\tstring cacheLib = folders.ThisAppTemp + \"_CreateDllMap lib.txt\";\n\tstring cacheDll = folders.ThisAppTemp + \"_CreateDllMap dll.txt\";\n\tif (0 != (flags & 4) && filesystem.exists(cacheLib) && filesystem.exists(cacheDll)) {\n\t\tsLib = File.ReadAllText(cacheLib);\n\t\tsDll = File.ReadAllText(cacheDll);\n\t\tprint.it(\"NOTE: using cached dumpbin results (flag 4).\");\n\t} else {\n\t\tvar files = new List<string>();\n\t\tforeach (var o in libs) {\n\t\t\tif (o is (string dir, string fn)) {\n\t\t\t\tfiles.AddRange(filesystem.enumFiles(dir, fn, 0 != (flags & 1) ? FEFlags.AllDescendants : 0).Where(f => 0 == f.Name.Starts(false, \"api-ms-\", \"ntstc_\")).Select(o => o.FullPath));\n\t\t\t} else {\n\t\t\t\tfiles.Add(o as string);\n\t\t\t}\n\t\t}\n\t\t\n\t\tvar dDllOfLib = new ConcurrentDictionary<string, string>(); //at first process lib, and add dll to this\n\t\tConcurrentBag<string> bLib = new(), bDll = new();\n\t\t\n\t\tParallel.ForEach(files, path => {\n\t\t\tpath = path.Lower();\n\t\t\tvar fn = pathname.getName(path);\n\t\t\tif (fn.Starts(\"api-ms-\")) return;\n\t\t\tif (!fn.Ends(\".lib\")) { dDllOfLib.TryAdd(path, fn); return; }\n\t\t\tif (0 != fn.Like(false, \"ntstc_*\", \"mspbase.lib\", \"strmbase.lib\", \"uuid.lib\", \"dnslib.lib\", \"netlib.lib\", \"dnsrpc.lib\", \"clfsmgmt.lib\", \"icu*.lib\", \"lz32.lib\")) return; //large, no dll func, or no public/useful dll func\n\t\t\t//print.it(fn);\n\t\t\t\n\t\t\t/*\n\t\t\t/HEADERS - functions and dll names; slow.\n\t\t\t/EXPORTS - just function names.\n\t\t\t*/\n\t\t\tint e = run.console(out var s, dumpbin, $\"/HEADERS \\\"{path}\\\"\");\n\t\t\tif (e != 0) {\n\t\t\t\tif (e == -1073741515) {\n\t\t\t\t\trun.it(dumpbin); //show \"mspdb80.dll is missing\"\n\t\t\t\t\tthrow new AuException($\"dumpbin.exe failed, {e}\");\n\t\t\t\t}\n\t\t\t\tprint.warning($\"dumpbin.exe failed, {e}, {fn}\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\tif (!s.RxFindAll(@\"(?m)^  DLL name *: (.+)\\R(?s).+?\\R\\R\", out var a)) {\n\t\t\t\tprint.it($\"no exports in {fn}\");\n\t\t\t} else {\n\t\t\t\tstring prev = null;\n\t\t\t\tforeach (var m in a) {\n\t\t\t\t\tvar dll = m[1].Value;\n\t\t\t\t\tif (dll.Contains(\"-ms-\")) {\n\t\t\t\t\t\t//print.it(dll);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tbLib.Add(m.Value);\n\t\t\t\t\t_SearchAdd(dll.Lower());\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (0 != (flags & 2)) _SearchAdd(fn.ReplaceAt(^3.., \"dll\"));\n\t\t\t\t\n\t\t\t\tvoid _SearchAdd(string fn) {\n\t\t\t\t\tif (fn == prev) return; prev = fn;\n\t\t\t\t\t//print.it(fn);\n\t\t\t\t\tvar s = filesystem.searchPath(fn);\n\t\t\t\t\tif (s != null) dDllOfLib.TryAdd(s.Lower(), fn);\n\t\t\t\t\t//else print.it(fn);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\t\n\t\tParallel.ForEach(dDllOfLib, kv => {\n\t\t\tvar (path, fn) = kv;\n\t\t\tint e = run.console(out var s, dumpbin, $\"/EXPORTS \\\"{path}\\\"\");\n\t\t\tif (e != 0) {\n\t\t\t\tprint.warning($\"dumpbin.exe failed, {e}, {fn}\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\tif (!s.RxMatch(@\"\\bordinal hint RVA.+\\R(?s).+?\\R\\R\", 0, out string s1)) print.it($\"no exports in {fn}\");\n\t\t\tbDll.Add($\"DLL: {fn}\\r\\n{s1}\");\n\t\t});\n\t\t\n\t\tsLib = string.Join(\"\\r\\n\", bLib.OrderBy(o => o, StringComparer.OrdinalIgnoreCase)); //OrderBy because Parallel makes random\n\t\tsDll = string.Join(\"\\r\\n\", bDll.OrderBy(o => o, StringComparer.OrdinalIgnoreCase));\n\t\tfilesystem.saveText(cacheLib, sLib);\n\t\tfilesystem.saveText(cacheDll, sDll);\n\t}\n\t\n\t/* list item formats:\n\tsymbol dll\n\tsymbol dll|#ordinal\n\tsymbol dll|funcNameInDll\n\t*/\n\t\n\tDictionary<string, string> d = new(), dDll = new();\n\tvar _cd = _CreateChooseDllMap();\n\t\n\t//LIB\n\t\n\tvar rx = \"\"\"\n(?m)^  DLL name *: (.+)\n  Symbol name *: (\\w.+)\n  Type *: code\n  Name type *: (.+)\n  (?:Ordinal *: (.+)|Hint .+\\R  Name *: (.+))\\R\\R\n\"\"\";\n\tif (!sLib.RxFindAll(rx, out var a)) throw new AuException();\n\tforeach (var m in a) {\n\t\tstring DLL = m[1].Value.Lower(), sym = m[2].Value, name = m[5].Value, ord = m[4].Value;\n\t\tif (sym[0] == '_') sym = sym.RxReplace(@\"^_([^@]+?)\", \"$1\");\n\t\tstring v;\n\t\tif (!ord.NE()) {\n\t\t\tif (DLL == \"mapi32.dll\") v = DLL; //two ord, one of which incorrect\n\t\t\telse v = $\"{DLL}|#{ord.ToInt()}\";\n\t\t} else if (!name.NE()) {\n\t\t\tif (name == sym) v = DLL;\n\t\t\telse v = $\"{DLL}|{name}\";\n\t\t} else throw new AuException();\n\t\t\n\t\tif (!d.TryGetValue(sym, out var vv)) d.Add(sym, v);\n\t\telse if (_ChooseDll(vv, v, sym)) d[sym] = v;\n\t}\n\t\n\t//DLL\n\t\n\trx = @\"(?m)(?>^DLL: (.+)\\Rordinal hint RVA .+\\R\\R)(?s)(.+?)\\R\\R\";\n\tif (!sDll.RxFindAll(rx, out a)) throw new AuException();\n\tforeach (var m in a) {\n\t\tstring DLL = m[1].Value.Lower();\n\t\tif (DLL == \"hhsetup.dll\") continue; //all func \"?...\"\n\t\tif (DLL == \"profapi.dll\") continue; //fails\n\t\tif (!m[2].Value.RxFindAll(@\"(?m)^      (.{20})(\\w+)\", out var b)) { print.warning($\"failed, {DLL}\"); continue; }\n\t\tforeach (var k in b) {\n\t\t\tvar sym = k[2].Value;\n\t\t\tif (d.ContainsKey(sym)) continue;\n\t\t\tif (sym.Starts(\"Dll\")) continue; //DllGetClassObject etc\n\t\t\tif (!dDll.TryGetValue(sym, out var vv)) dDll.Add(sym, DLL);\n\t\t\telse if (_ChooseDll(vv, DLL, sym)) dDll[sym] = DLL;\n\t\t}\n\t}\n\t\n\tprint.it($\"Found {d.Count + dDll.Count} functions.\"); //29934\n\t\n\treturn string.Join(\"\\r\\n\", d.Select(o => o.Key + \" \" + o.Value).Concat(dDll.Select(o => o.Key + \" \" + o.Value)).OrderBy(o => o).Append(\"\"));\n\t\n\tbool _ChooseDll(string dllOld, string dllNew, string sym) {\n\t\tif (dllOld != dllNew) {\n\t\t\tforeach (var (s1, s2) in _cd) {\n\t\t\t\tif(dllOld.Like(s1) && dllNew.Like(s2)) return true;\n\t\t\t\tif(dllOld.Like(s2) && dllNew.Like(s1)) return false;\n\t\t\t}\n\t\t\t\n\t\t\t//print.it($\"{sym,-40}:  (\\\"{dllNew}\\\", \\\"{dllOld}\\\"),\");\n\t\t}\n\t\treturn false;\n\t}\n\t\n\tstatic (string s1, string s2)[] _CreateChooseDllMap() {\n\t\treturn new (string, string)[] {\n\t\t\t(\"bluetoothapis.dll\", \"bthprops.cpl\"),\n\t\t\t(\"chakra.dll\", \"jscript9.dll\"),\n\t\t\t(\"d3d11.dll\", \"gdi32.dll\"),\n\t\t\t(\"dsparse.dll\", \"ntdsapi.dll\"),\n\t\t\t(\"evr.dll\", \"mfplat.dll\"),\n\t\t\t(\"iprop.dll\", \"ole32.dll\"),\n\t\t\t(\"kernel32.dll\", \"normaliz.dll\"),\n\t\t\t(\"kernelbase.dll\", \"kernel32.dll\"),\n\t\t\t(\"kernelbase.dll\", \"msvcrt.dll\"),\n\t\t\t(\"kernelbase.dll\", \"version.dll\"),\n\t\t\t(\"kernelbase.dll\", \"ntdll.dll\"),\n\t\t\t(\"kernel32.dll\", \"ntdll.dll\"),\n\t\t\t(\"*\", \"netapi32.dll\"),\n\t\t\t(\"ncrypt.dll\", \"bcrypt.dll\"),\n\t\t\t(\"ntdll.dll\", \"msvcrt.dll\"),\n\t\t\t(\"*\", \"secur32.dll\"),\n\t\t\t(\"shlwapi.dll|#*\", \"shlwapi.dll\"),\n\t\t\t(\"spoolss.dll\", \"winspool.drv*\"),\n\t\t\t(\"winsta.dll\", \"wtsapi32.dll\"),\n\t\t\t(\"irprops.cpl\", \"bthprops.cpl\"),\n\t\t\t(\"wintrust.dll\", \"crypt32.dll\"),\n\t\t\t(\"imagehlp.dll\", \"dbghelp.dll\"),\n\t\t\t(\"mfcore.dll\", \"mf.dll\"),\n\t\t\t(\"mf.dll\", \"mfplat.dll\"),\n\t\t\t(\"mscorsn.dll\", \"mscoree.dll\"),\n\t\t\t(\"mstask.dll\", \"mstask.dll|#*\"),\n\t\t\t(\"olepro32.dll*\", \"oleaut32.dll*\"),\n\t\t\t(\"olecli32*\", \"ole32.dll\"),\n\t\t\t(\"shfolder.dll\", \"shell32.dll\"),\n\t\t\t(\"wsock32.dll*\", \"*\"),\n\t\t\t(\"setupapi.dll\", \"cfgmgr32.dll\"),\n\t\t\t(\"wmi.dll\", \"advapi32.dll\"),\n\t\t\t(\"certcli.dll\", \"certca.dll\"),\n\t\t\t(\"vertdll.dll\", \"*\"),\n\t\t\t(\"comctl32.dll\", \"comctl32.dll|#*\"),\n\t\t\t(\"msi.dll\", \"msi.dll|#*\"),\n\t\t\t(\"oledlg.dll\", \"oledlg.dll|#*\"),\n\t\t\t(\"shell32.dll|#*\", \"shell32.dll\"),\n\t\t\t(\"schannel.dll\", \"secur32.dll\"),\n\t\t\t(\"secur32.dll\", \"sspicli.dll\"),\n\t\t\t(\"uxtheme.dll|#*\", \"uxtheme.dll\"),\n\t\t\t(\"wer.dll\", \"advapi32.dll\"),\n\t\t\t(\"cryptbase.dll\", \"advapi32.dll\"),\n\t\t\t(\"comctl32.dll|_TrackMouseEvent\", \"user32.dll\"),\n\t\t\t(\"sporder.dll\", \"ws2_32.dll\"),\n\t\t\t(\"*\", \"xaudio*\"),\n\t\t\t(\"*\", \"xinput*\"),\n\t\t\t(\"*\", \"d3d*\"),\n\t\t};\n\t}\n}\n"
  },
  {
    "path": "_/default.exe.manifest",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\" manifestVersion=\"1.0\" xmlns:asmv3=\"urn:schemas-microsoft-com:asm.v3\">\n<assemblyIdentity\n    version=\"1.0.0.0\"\n    processorArchitecture=\"*\"\n    name=\"Company.Product.Filename\"\n    type=\"win32\"\n/>\n<description>File Description</description>\n<dependency>\n    <dependentAssembly>\n        <assemblyIdentity\n            type=\"win32\"\n            name=\"Microsoft.Windows.Common-Controls\"\n            version=\"6.0.0.0\"\n            processorArchitecture=\"*\"\n            publicKeyToken=\"6595b64144ccf1df\"\n            language=\"*\"\n        />\n    </dependentAssembly>\n</dependency>\n<trustInfo xmlns=\"urn:schemas-microsoft-com:asm.v2\">\n    <security>\n        <requestedPrivileges>\n            <requestedExecutionLevel level=\"asInvoker\" uiAccess=\"false\"/>\n        </requestedPrivileges>\n    </security>\n</trustInfo>\n<compatibility xmlns=\"urn:schemas-microsoft-com:compatibility.v1\">\n    <application>\n      <!-- Windows 7 -->\n      <supportedOS Id=\"{35138b9a-5d96-4fbd-8e2d-a2440225f93a}\"/>\n      <!-- Windows 8 -->\n      <supportedOS Id=\"{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}\"/>\n      <!-- Windows 8.1 -->\n      <supportedOS Id=\"{1f676c76-80e1-4239-95bb-83d0f6d0da78}\"/>\n      <!-- Windows 10 -->\n      <supportedOS Id=\"{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}\"/>\n    </application>\n</compatibility>\n<asmv3:application>\n\t<asmv3:windowsSettings xmlns=\"http://schemas.microsoft.com/SMI/2005/WindowsSettings\">\n\t\t<dpiAwareness xmlns=\"http://schemas.microsoft.com/SMI/2016/WindowsSettings\">PerMonitorV2</dpiAwareness>\n\t\t<dpiAware>True/PM</dpiAware>\n\t</asmv3:windowsSettings>\n\t<asmv3:windowsSettings xmlns=\"http://schemas.microsoft.com/SMI/2011/WindowsSettings\">\n\t\t<disableWindowFiltering>true</disableWindowFiltering>\n\t</asmv3:windowsSettings>\n</asmv3:application>\n</assembly>\n"
  },
  {
    "path": "_/dotnet_ref_editor.txt",
    "content": "*d|Accessibility|DirectWriteForwarder|Microsoft.VisualBasic|Microsoft.VisualBasic.Forms|Microsoft.Win32.Registry.AccessControl|Microsoft.Win32.SystemEvents|PresentationCore|PresentationFramework|PresentationFramework-SystemCore|PresentationFramework-SystemData|PresentationFramework-SystemDrawing|PresentationFramework-SystemXml|PresentationFramework-SystemXmlLinq|PresentationFramework.Aero|PresentationFramework.Aero2|PresentationFramework.AeroLite|PresentationFramework.Classic|PresentationFramework.Fluent|PresentationFramework.Luna|PresentationFramework.Royale|PresentationUI|ReachFramework|System.CodeDom|System.Configuration.ConfigurationManager|System.Design|System.Diagnostics.EventLog|System.Diagnostics.EventLog.Messages|System.Diagnostics.PerformanceCounter|System.DirectoryServices|System.Drawing|System.Drawing.Common|System.Drawing.Design|System.Formats.Nrbf|System.IO.Packaging|System.Printing|System.Private.Windows.Core|System.Private.Windows.GdiPlus|System.Resources.Extensions|System.Security.Cryptography.Pkcs|System.Security.Cryptography.ProtectedData|System.Security.Cryptography.Xml|System.Security.Permissions|System.Windows.Controls.Ribbon|System.Windows.Extensions|System.Windows.Forms|System.Windows.Forms.Design|System.Windows.Forms.Design.Editors|System.Windows.Forms.Primitives|System.Windows.Input.Manipulations|System.Windows.Presentation|System.Windows.Primitives|System.Xaml|UIAutomationClient|UIAutomationClientSideProviders|UIAutomationProvider|UIAutomationTypes|WindowsBase|WindowsFormsIntegration|*c|Microsoft.CSharp|Microsoft.VisualBasic.Core|Microsoft.Win32.Primitives|Microsoft.Win32.Registry|mscorlib|netstandard|System|System.AppContext|System.Buffers|System.Collections|System.Collections.Concurrent|System.Collections.Immutable|System.Collections.NonGeneric|System.Collections.Specialized|System.ComponentModel|System.ComponentModel.Annotations|System.ComponentModel.DataAnnotations|System.ComponentModel.EventBasedAsync|System.ComponentModel.Primitives|System.ComponentModel.TypeConverter|System.Configuration|System.Console|System.Core|System.Data|System.Data.Common|System.Data.DataSetExtensions|System.Diagnostics.Contracts|System.Diagnostics.Debug|System.Diagnostics.DiagnosticSource|System.Diagnostics.FileVersionInfo|System.Diagnostics.Process|System.Diagnostics.StackTrace|System.Diagnostics.TextWriterTraceListener|System.Diagnostics.Tools|System.Diagnostics.TraceSource|System.Diagnostics.Tracing|System.Drawing.Primitives|System.Dynamic.Runtime|System.Formats.Asn1|System.Formats.Tar|System.Globalization|System.Globalization.Calendars|System.Globalization.Extensions|System.IO|System.IO.Compression|System.IO.Compression.Brotli|System.IO.Compression.FileSystem|System.IO.Compression.ZipFile|System.IO.FileSystem|System.IO.FileSystem.AccessControl|System.IO.FileSystem.DriveInfo|System.IO.FileSystem.Primitives|System.IO.FileSystem.Watcher|System.IO.IsolatedStorage|System.IO.MemoryMappedFiles|System.IO.Pipelines|System.IO.Pipes|System.IO.Pipes.AccessControl|System.IO.UnmanagedMemoryStream|System.Linq|System.Linq.AsyncEnumerable|System.Linq.Expressions|System.Linq.Parallel|System.Linq.Queryable|System.Memory|System.Net|System.Net.Http|System.Net.Http.Json|System.Net.HttpListener|System.Net.Mail|System.Net.NameResolution|System.Net.NetworkInformation|System.Net.Ping|System.Net.Primitives|System.Net.Quic|System.Net.Requests|System.Net.Security|System.Net.ServerSentEvents|System.Net.ServicePoint|System.Net.Sockets|System.Net.WebClient|System.Net.WebHeaderCollection|System.Net.WebProxy|System.Net.WebSockets|System.Net.WebSockets.Client|System.Numerics|System.Numerics.Vectors|System.ObjectModel|System.Private.CoreLib|System.Private.DataContractSerialization|System.Private.Uri|System.Private.Xml|System.Private.Xml.Linq|System.Reflection|System.Reflection.DispatchProxy|System.Reflection.Emit|System.Reflection.Emit.ILGeneration|System.Reflection.Emit.Lightweight|System.Reflection.Extensions|System.Reflection.Metadata|System.Reflection.Primitives|System.Reflection.TypeExtensions|System.Resources.Reader|System.Resources.ResourceManager|System.Resources.Writer|System.Runtime|System.Runtime.CompilerServices.Unsafe|System.Runtime.CompilerServices.VisualC|System.Runtime.Extensions|System.Runtime.Handles|System.Runtime.InteropServices|System.Runtime.InteropServices.JavaScript|System.Runtime.InteropServices.RuntimeInformation|System.Runtime.Intrinsics|System.Runtime.Loader|System.Runtime.Numerics|System.Runtime.Serialization|System.Runtime.Serialization.Formatters|System.Runtime.Serialization.Json|System.Runtime.Serialization.Primitives|System.Runtime.Serialization.Xml|System.Security|System.Security.AccessControl|System.Security.Claims|System.Security.Cryptography|System.Security.Cryptography.Algorithms|System.Security.Cryptography.Cng|System.Security.Cryptography.Csp|System.Security.Cryptography.Encoding|System.Security.Cryptography.OpenSsl|System.Security.Cryptography.Primitives|System.Security.Cryptography.X509Certificates|System.Security.Principal|System.Security.Principal.Windows|System.Security.SecureString|System.ServiceModel.Web|System.ServiceProcess|System.Text.Encoding|System.Text.Encoding.CodePages|System.Text.Encoding.Extensions|System.Text.Encodings.Web|System.Text.Json|System.Text.RegularExpressions|System.Threading|System.Threading.AccessControl|System.Threading.Channels|System.Threading.Overlapped|System.Threading.Tasks|System.Threading.Tasks.Dataflow|System.Threading.Tasks.Extensions|System.Threading.Tasks.Parallel|System.Threading.Thread|System.Threading.ThreadPool|System.Threading.Timer|System.Transactions|System.Transactions.Local|System.ValueTuple|System.Web|System.Web.HttpUtility|System.Windows|System.Xml|System.Xml.Linq|System.Xml.ReaderWriter|System.Xml.Serialization|System.Xml.XDocument|System.Xml.XmlDocument|System.Xml.XmlSerializer|System.Xml.XPath|System.Xml.XPath.XDocument|*a|Au|Au.Controls"
  },
  {
    "path": "_/dotnet_ref_task.txt",
    "content": "*d|Accessibility|DirectWriteForwarder|Microsoft.VisualBasic|Microsoft.VisualBasic.Forms|Microsoft.Win32.Registry.AccessControl|Microsoft.Win32.SystemEvents|PresentationCore|PresentationFramework|PresentationFramework-SystemCore|PresentationFramework-SystemData|PresentationFramework-SystemDrawing|PresentationFramework-SystemXml|PresentationFramework-SystemXmlLinq|PresentationFramework.Aero|PresentationFramework.Aero2|PresentationFramework.AeroLite|PresentationFramework.Classic|PresentationFramework.Fluent|PresentationFramework.Luna|PresentationFramework.Royale|PresentationUI|ReachFramework|System.CodeDom|System.Configuration.ConfigurationManager|System.Design|System.Diagnostics.EventLog|System.Diagnostics.EventLog.Messages|System.Diagnostics.PerformanceCounter|System.DirectoryServices|System.Drawing|System.Drawing.Common|System.Drawing.Design|System.Formats.Nrbf|System.IO.Packaging|System.Printing|System.Private.Windows.Core|System.Private.Windows.GdiPlus|System.Resources.Extensions|System.Security.Cryptography.Pkcs|System.Security.Cryptography.ProtectedData|System.Security.Cryptography.Xml|System.Security.Permissions|System.Windows.Controls.Ribbon|System.Windows.Extensions|System.Windows.Forms|System.Windows.Forms.Design|System.Windows.Forms.Design.Editors|System.Windows.Forms.Primitives|System.Windows.Input.Manipulations|System.Windows.Presentation|System.Windows.Primitives|System.Xaml|UIAutomationClient|UIAutomationClientSideProviders|UIAutomationProvider|UIAutomationTypes|WindowsBase|WindowsFormsIntegration|*c|Microsoft.CSharp|Microsoft.VisualBasic.Core|Microsoft.Win32.Primitives|Microsoft.Win32.Registry|mscorlib|netstandard|System|System.AppContext|System.Buffers|System.Collections|System.Collections.Concurrent|System.Collections.Immutable|System.Collections.NonGeneric|System.Collections.Specialized|System.ComponentModel|System.ComponentModel.Annotations|System.ComponentModel.DataAnnotations|System.ComponentModel.EventBasedAsync|System.ComponentModel.Primitives|System.ComponentModel.TypeConverter|System.Configuration|System.Console|System.Core|System.Data|System.Data.Common|System.Data.DataSetExtensions|System.Diagnostics.Contracts|System.Diagnostics.Debug|System.Diagnostics.DiagnosticSource|System.Diagnostics.FileVersionInfo|System.Diagnostics.Process|System.Diagnostics.StackTrace|System.Diagnostics.TextWriterTraceListener|System.Diagnostics.Tools|System.Diagnostics.TraceSource|System.Diagnostics.Tracing|System.Drawing.Primitives|System.Dynamic.Runtime|System.Formats.Asn1|System.Formats.Tar|System.Globalization|System.Globalization.Calendars|System.Globalization.Extensions|System.IO|System.IO.Compression|System.IO.Compression.Brotli|System.IO.Compression.FileSystem|System.IO.Compression.ZipFile|System.IO.FileSystem|System.IO.FileSystem.AccessControl|System.IO.FileSystem.DriveInfo|System.IO.FileSystem.Primitives|System.IO.FileSystem.Watcher|System.IO.IsolatedStorage|System.IO.MemoryMappedFiles|System.IO.Pipelines|System.IO.Pipes|System.IO.Pipes.AccessControl|System.IO.UnmanagedMemoryStream|System.Linq|System.Linq.AsyncEnumerable|System.Linq.Expressions|System.Linq.Parallel|System.Linq.Queryable|System.Memory|System.Net|System.Net.Http|System.Net.Http.Json|System.Net.HttpListener|System.Net.Mail|System.Net.NameResolution|System.Net.NetworkInformation|System.Net.Ping|System.Net.Primitives|System.Net.Quic|System.Net.Requests|System.Net.Security|System.Net.ServerSentEvents|System.Net.ServicePoint|System.Net.Sockets|System.Net.WebClient|System.Net.WebHeaderCollection|System.Net.WebProxy|System.Net.WebSockets|System.Net.WebSockets.Client|System.Numerics|System.Numerics.Vectors|System.ObjectModel|System.Private.CoreLib|System.Private.DataContractSerialization|System.Private.Uri|System.Private.Xml|System.Private.Xml.Linq|System.Reflection|System.Reflection.DispatchProxy|System.Reflection.Emit|System.Reflection.Emit.ILGeneration|System.Reflection.Emit.Lightweight|System.Reflection.Extensions|System.Reflection.Metadata|System.Reflection.Primitives|System.Reflection.TypeExtensions|System.Resources.Reader|System.Resources.ResourceManager|System.Resources.Writer|System.Runtime|System.Runtime.CompilerServices.Unsafe|System.Runtime.CompilerServices.VisualC|System.Runtime.Extensions|System.Runtime.Handles|System.Runtime.InteropServices|System.Runtime.InteropServices.JavaScript|System.Runtime.InteropServices.RuntimeInformation|System.Runtime.Intrinsics|System.Runtime.Loader|System.Runtime.Numerics|System.Runtime.Serialization|System.Runtime.Serialization.Formatters|System.Runtime.Serialization.Json|System.Runtime.Serialization.Primitives|System.Runtime.Serialization.Xml|System.Security|System.Security.AccessControl|System.Security.Claims|System.Security.Cryptography|System.Security.Cryptography.Algorithms|System.Security.Cryptography.Cng|System.Security.Cryptography.Csp|System.Security.Cryptography.Encoding|System.Security.Cryptography.OpenSsl|System.Security.Cryptography.Primitives|System.Security.Cryptography.X509Certificates|System.Security.Principal|System.Security.Principal.Windows|System.Security.SecureString|System.ServiceModel.Web|System.ServiceProcess|System.Text.Encoding|System.Text.Encoding.CodePages|System.Text.Encoding.Extensions|System.Text.Encodings.Web|System.Text.Json|System.Text.RegularExpressions|System.Threading|System.Threading.AccessControl|System.Threading.Channels|System.Threading.Overlapped|System.Threading.Tasks|System.Threading.Tasks.Dataflow|System.Threading.Tasks.Extensions|System.Threading.Tasks.Parallel|System.Threading.Thread|System.Threading.ThreadPool|System.Threading.Timer|System.Transactions|System.Transactions.Local|System.ValueTuple|System.Web|System.Web.HttpUtility|System.Windows|System.Xml|System.Xml.Linq|System.Xml.ReaderWriter|System.Xml.Serialization|System.Xml.XDocument|System.Xml.XmlDocument|System.Xml.XmlSerializer|System.Xml.XPath|System.Xml.XPath.XDocument|*a|Au"
  },
  {
    "path": "_/gitBinaryRestore.csv",
    "content": "restore"
  },
  {
    "path": "global.json",
    "content": "{\n  \"//sdk\": {\n    \"version\": \"10.0.100\"\n  }\n}"
  }
]